@svgrid/enterprise 3.1.8 → 3.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (268) hide show
  1. package/README.md +1 -0
  2. package/dist/SvAlertRuleEditor.svelte +16 -5
  3. package/dist/SvGridAlerts.svelte +8 -0
  4. package/dist/SvGridAlerts.svelte.d.ts +4 -0
  5. package/dist/SvGridBoard.svelte +69 -10
  6. package/dist/SvGridBulkEditDrawer.svelte +13 -5
  7. package/dist/SvGridGallery.svelte +286 -0
  8. package/dist/SvGridGallery.svelte.d.ts +30 -0
  9. package/dist/SvGridScheduler.svelte +33 -11
  10. package/dist/SvGridSelectionBar.svelte +47 -0
  11. package/dist/advanced-filter/expr-columns-from-grid.d.ts +1 -0
  12. package/dist/advanced-filter/expr-columns-from-grid.js +11 -7
  13. package/dist/advanced-filter-enable.d.ts +6 -0
  14. package/dist/advanced-filter-enable.js +8 -0
  15. package/dist/alerts/alert-engine.d.ts +7 -0
  16. package/dist/alerts/alert-engine.js +5 -2
  17. package/dist/alerts/condition-editor.d.ts +26 -0
  18. package/dist/alerts/condition-editor.js +17 -0
  19. package/dist/base/index.d.ts +20 -0
  20. package/dist/base/index.js +20 -0
  21. package/dist/bulk-edit.d.ts +11 -0
  22. package/dist/bulk-edit.js +18 -1
  23. package/dist/cdn/svgrid-enterprise.svelte-external.js +11197 -10639
  24. package/dist/designer/assets/GridFooter-Cu8_yBlQ.js +2 -0
  25. package/dist/designer/assets/GridMenus-BUjkkAU2.js +6 -0
  26. package/dist/designer/assets/PinSplitter-BgTpgh1d.css +1 -0
  27. package/dist/designer/assets/PinSplitter-C5AwA1xV.js +1 -0
  28. package/dist/designer/assets/SvGrid.helpers-CIrmIo9U.js +1 -0
  29. package/dist/designer/assets/SvGridCellEditor-CJxThKL_.js +4 -0
  30. package/dist/designer/assets/SvListBox-CS7oUAiX.js +2 -0
  31. package/dist/designer/assets/conditional-formatting-MDWbF8_Y.js +1 -0
  32. package/dist/designer/assets/grid-extension.svelte-BZ7AqyJI.js +1 -0
  33. package/dist/designer/assets/{index-Obk8sI77.js → index-BPU2KNc5.js} +307 -307
  34. package/dist/designer/assets/index-BmvG99kR.css +1 -0
  35. package/dist/designer/assets/sparkline-C-NcnB2i.js +1 -0
  36. package/dist/designer/assets/src-C_U1NKOY.js +58 -0
  37. package/dist/designer/assets/sv-grid-scrollbar-B9N6Wuj0.js +109 -0
  38. package/dist/designer/assets/validate-H5u2HEQh.js +1 -0
  39. package/dist/designer/index.html +11 -7
  40. package/dist/expressions/compile.js +10 -0
  41. package/dist/expressions/evaluate.js +18 -0
  42. package/dist/expressions/expression-types.d.ts +10 -0
  43. package/dist/expressions/kinds.d.ts +28 -0
  44. package/dist/expressions/kinds.js +19 -0
  45. package/dist/expressions/parse.js +9 -1
  46. package/dist/fields/AttachmentCell.svelte +242 -0
  47. package/dist/fields/AttachmentCell.svelte.d.ts +5 -0
  48. package/dist/fields/AttachmentDialog.svelte +357 -0
  49. package/dist/fields/AttachmentDialog.svelte.d.ts +13 -0
  50. package/dist/fields/AttachmentEditor.svelte +41 -0
  51. package/dist/fields/AttachmentEditor.svelte.d.ts +11 -0
  52. package/dist/fields/AttachmentPreview.svelte +72 -0
  53. package/dist/fields/AttachmentPreview.svelte.d.ts +8 -0
  54. package/dist/fields/BarcodeCell.svelte +20 -0
  55. package/dist/fields/BarcodeCell.svelte.d.ts +5 -0
  56. package/dist/fields/BaseAutomations.svelte +558 -0
  57. package/dist/fields/BaseAutomations.svelte.d.ts +18 -0
  58. package/dist/fields/BaseChartView.svelte +47 -0
  59. package/dist/fields/BaseChartView.svelte.d.ts +8 -0
  60. package/dist/fields/BaseTableView.svelte +185 -0
  61. package/dist/fields/BaseTableView.svelte.d.ts +37 -0
  62. package/dist/fields/BaseTabs.svelte +308 -0
  63. package/dist/fields/BaseTabs.svelte.d.ts +21 -0
  64. package/dist/fields/ButtonCell.svelte +47 -0
  65. package/dist/fields/ButtonCell.svelte.d.ts +20 -0
  66. package/dist/fields/CheckboxCell.svelte +46 -0
  67. package/dist/fields/CheckboxCell.svelte.d.ts +14 -0
  68. package/dist/fields/DeleteFieldConfirm.svelte +80 -0
  69. package/dist/fields/DeleteFieldConfirm.svelte.d.ts +8 -0
  70. package/dist/fields/DeleteRecordConfirm.svelte +48 -0
  71. package/dist/fields/DeleteRecordConfirm.svelte.d.ts +9 -0
  72. package/dist/fields/FieldCell.svelte +80 -0
  73. package/dist/fields/FieldCell.svelte.d.ts +9 -0
  74. package/dist/fields/FieldEditor.svelte +321 -0
  75. package/dist/fields/FieldEditor.svelte.d.ts +10 -0
  76. package/dist/fields/FieldInput.svelte +188 -0
  77. package/dist/fields/FieldInput.svelte.d.ts +29 -0
  78. package/dist/fields/FieldsHeaderEnd.svelte +146 -0
  79. package/dist/fields/FieldsHeaderEnd.svelte.d.ts +6 -0
  80. package/dist/fields/FootRowAdder.svelte +8 -0
  81. package/dist/fields/FootRowAdder.svelte.d.ts +6 -0
  82. package/dist/fields/GroupHeader.svelte +127 -0
  83. package/dist/fields/GroupHeader.svelte.d.ts +24 -0
  84. package/dist/fields/LinkCell.svelte +46 -0
  85. package/dist/fields/LinkCell.svelte.d.ts +9 -0
  86. package/dist/fields/LinkPickerEditor.svelte +172 -0
  87. package/dist/fields/LinkPickerEditor.svelte.d.ts +9 -0
  88. package/dist/fields/LinkPreview.svelte +60 -0
  89. package/dist/fields/LinkPreview.svelte.d.ts +7 -0
  90. package/dist/fields/LinkedRecordView.svelte +94 -0
  91. package/dist/fields/LinkedRecordView.svelte.d.ts +9 -0
  92. package/dist/fields/LinkedRecordsCell.svelte +59 -0
  93. package/dist/fields/LinkedRecordsCell.svelte.d.ts +11 -0
  94. package/dist/fields/LongTextEditor.svelte +157 -0
  95. package/dist/fields/LongTextEditor.svelte.d.ts +11 -0
  96. package/dist/fields/NumberEditor.svelte +309 -0
  97. package/dist/fields/NumberEditor.svelte.d.ts +12 -0
  98. package/dist/fields/OptionsEditor.svelte +117 -0
  99. package/dist/fields/OptionsEditor.svelte.d.ts +11 -0
  100. package/dist/fields/PercentCell.svelte +21 -0
  101. package/dist/fields/PercentCell.svelte.d.ts +8 -0
  102. package/dist/fields/PropertyInput.svelte +204 -0
  103. package/dist/fields/PropertyInput.svelte.d.ts +11 -0
  104. package/dist/fields/RatingCell.svelte +75 -0
  105. package/dist/fields/RatingCell.svelte.d.ts +16 -0
  106. package/dist/fields/RatingEditor.svelte +82 -0
  107. package/dist/fields/RatingEditor.svelte.d.ts +9 -0
  108. package/dist/fields/RecordView.svelte +310 -0
  109. package/dist/fields/RecordView.svelte.d.ts +11 -0
  110. package/dist/fields/RichTextCell.svelte +30 -0
  111. package/dist/fields/RichTextCell.svelte.d.ts +5 -0
  112. package/dist/fields/RowAdder.svelte +93 -0
  113. package/dist/fields/RowAdder.svelte.d.ts +7 -0
  114. package/dist/fields/RowHeader.svelte +99 -0
  115. package/dist/fields/RowHeader.svelte.d.ts +11 -0
  116. package/dist/fields/SelectCell.svelte +35 -0
  117. package/dist/fields/SelectCell.svelte.d.ts +15 -0
  118. package/dist/fields/SelectEditor.svelte +350 -0
  119. package/dist/fields/SelectEditor.svelte.d.ts +17 -0
  120. package/dist/fields/SummaryCell.svelte +160 -0
  121. package/dist/fields/SummaryCell.svelte.d.ts +11 -0
  122. package/dist/fields/SvBase.svelte +545 -0
  123. package/dist/fields/SvBase.svelte.d.ts +30 -0
  124. package/dist/fields/SvBaseForm.svelte +295 -0
  125. package/dist/fields/SvBaseForm.svelte.d.ts +22 -0
  126. package/dist/fields/SvBaseViewBar.svelte +644 -0
  127. package/dist/fields/SvBaseViewBar.svelte.d.ts +26 -0
  128. package/dist/fields/UserCell.svelte +39 -0
  129. package/dist/fields/UserCell.svelte.d.ts +9 -0
  130. package/dist/fields/UserFace.svelte +30 -0
  131. package/dist/fields/UserFace.svelte.d.ts +12 -0
  132. package/dist/fields/activity.svelte.d.ts +50 -0
  133. package/dist/fields/activity.svelte.js +110 -0
  134. package/dist/fields/attachments.d.ts +95 -0
  135. package/dist/fields/attachments.js +145 -0
  136. package/dist/fields/automation-outbound.d.ts +46 -0
  137. package/dist/fields/automation-outbound.js +61 -0
  138. package/dist/fields/automation-server.d.ts +53 -0
  139. package/dist/fields/automation-server.js +333 -0
  140. package/dist/fields/automations.svelte.d.ts +208 -0
  141. package/dist/fields/automations.svelte.js +437 -0
  142. package/dist/fields/bar-request.svelte.d.ts +16 -0
  143. package/dist/fields/bar-request.svelte.js +15 -0
  144. package/dist/fields/base-alerts.d.ts +24 -0
  145. package/dist/fields/base-alerts.js +64 -0
  146. package/dist/fields/base-connect.d.ts +82 -0
  147. package/dist/fields/base-connect.js +271 -0
  148. package/dist/fields/base-schema.d.ts +104 -0
  149. package/dist/fields/base-schema.js +266 -0
  150. package/dist/fields/base-sql.d.ts +62 -0
  151. package/dist/fields/base-sql.js +251 -0
  152. package/dist/fields/base-sync.d.ts +142 -0
  153. package/dist/fields/base-sync.js +499 -0
  154. package/dist/fields/base-tables.d.ts +89 -0
  155. package/dist/fields/base-tables.js +349 -0
  156. package/dist/fields/builtin-types.d.ts +13 -0
  157. package/dist/fields/builtin-types.js +773 -0
  158. package/dist/fields/button.d.ts +2 -0
  159. package/dist/fields/button.js +17 -0
  160. package/dist/fields/chart-view.d.ts +35 -0
  161. package/dist/fields/chart-view.js +154 -0
  162. package/dist/fields/column-order.d.ts +6 -0
  163. package/dist/fields/column-order.js +12 -0
  164. package/dist/fields/convert.d.ts +16 -0
  165. package/dist/fields/convert.js +176 -0
  166. package/dist/fields/copy-text.d.ts +13 -0
  167. package/dist/fields/copy-text.js +45 -0
  168. package/dist/fields/editor-request.svelte.d.ts +71 -0
  169. package/dist/fields/editor-request.svelte.js +52 -0
  170. package/dist/fields/field-ops.d.ts +72 -0
  171. package/dist/fields/field-ops.js +334 -0
  172. package/dist/fields/filter-model.d.ts +84 -0
  173. package/dist/fields/filter-model.js +446 -0
  174. package/dist/fields/format.d.ts +109 -0
  175. package/dist/fields/format.js +288 -0
  176. package/dist/fields/formula.d.ts +50 -0
  177. package/dist/fields/formula.js +530 -0
  178. package/dist/fields/grid-access.d.ts +60 -0
  179. package/dist/fields/grid-access.js +120 -0
  180. package/dist/fields/index.d.ts +34 -0
  181. package/dist/fields/index.js +312 -0
  182. package/dist/fields/linked-fields.d.ts +8 -0
  183. package/dist/fields/linked-fields.js +40 -0
  184. package/dist/fields/links.d.ts +10 -0
  185. package/dist/fields/links.js +46 -0
  186. package/dist/fields/name-list.d.ts +16 -0
  187. package/dist/fields/name-list.js +49 -0
  188. package/dist/fields/palette.d.ts +47 -0
  189. package/dist/fields/palette.js +69 -0
  190. package/dist/fields/record-delete.svelte.d.ts +12 -0
  191. package/dist/fields/record-delete.svelte.js +5 -0
  192. package/dist/fields/record-menu.d.ts +9 -0
  193. package/dist/fields/record-menu.js +78 -0
  194. package/dist/fields/registry.svelte.d.ts +176 -0
  195. package/dist/fields/registry.svelte.js +51 -0
  196. package/dist/fields/rich-text.d.ts +23 -0
  197. package/dist/fields/rich-text.js +48 -0
  198. package/dist/fields/row-color-choice.svelte.d.ts +31 -0
  199. package/dist/fields/row-color-choice.svelte.js +25 -0
  200. package/dist/fields/row-colors.d.ts +36 -0
  201. package/dist/fields/row-colors.js +90 -0
  202. package/dist/fields/row-height.d.ts +28 -0
  203. package/dist/fields/row-height.js +50 -0
  204. package/dist/fields/sources.svelte.d.ts +80 -0
  205. package/dist/fields/sources.svelte.js +131 -0
  206. package/dist/fields/summaries.d.ts +18 -0
  207. package/dist/fields/summaries.js +138 -0
  208. package/dist/fields/view-model.d.ts +129 -0
  209. package/dist/fields/view-model.js +492 -0
  210. package/dist/fields/viewbar/ChartPanel.svelte +61 -0
  211. package/dist/fields/viewbar/ChartPanel.svelte.d.ts +9 -0
  212. package/dist/fields/viewbar/ColorPanel.svelte +112 -0
  213. package/dist/fields/viewbar/ColorPanel.svelte.d.ts +9 -0
  214. package/dist/fields/viewbar/ConditionValue.svelte +153 -0
  215. package/dist/fields/viewbar/ConditionValue.svelte.d.ts +15 -0
  216. package/dist/fields/viewbar/FieldIcon.svelte +18 -0
  217. package/dist/fields/viewbar/FieldIcon.svelte.d.ts +9 -0
  218. package/dist/fields/viewbar/FilterPanel.svelte +192 -0
  219. package/dist/fields/viewbar/FilterPanel.svelte.d.ts +13 -0
  220. package/dist/fields/viewbar/GanttPanel.svelte +60 -0
  221. package/dist/fields/viewbar/GanttPanel.svelte.d.ts +10 -0
  222. package/dist/fields/viewbar/HideFieldsPanel.svelte +104 -0
  223. package/dist/fields/viewbar/HideFieldsPanel.svelte.d.ts +7 -0
  224. package/dist/fields/viewbar/SortPanel.svelte +85 -0
  225. package/dist/fields/viewbar/SortPanel.svelte.d.ts +8 -0
  226. package/dist/fields/viewbar/ViewsMenu.svelte +250 -0
  227. package/dist/fields/viewbar/ViewsMenu.svelte.d.ts +15 -0
  228. package/dist/fields/viewbar/panel-focus.d.ts +3 -0
  229. package/dist/fields/viewbar/panel-focus.js +22 -0
  230. package/dist/fields/viewbar/sort-state.d.ts +20 -0
  231. package/dist/fields/viewbar/sort-state.js +24 -0
  232. package/dist/gallery.d.ts +7 -0
  233. package/dist/gallery.js +36 -0
  234. package/dist/gantt/SvGridGantt.svelte +88 -16
  235. package/dist/gantt/gantt-config.d.ts +28 -0
  236. package/dist/index.d.ts +3 -0
  237. package/dist/index.js +6 -0
  238. package/dist/install.js +3 -0
  239. package/dist/license-core.d.ts +3 -3
  240. package/dist/license.js +1 -0
  241. package/dist/node/studio.js +105 -4
  242. package/dist/scheduler-config.d.ts +11 -0
  243. package/dist/sources/introspect-supabase.js +4 -1
  244. package/dist/sources/rest.js +6 -2
  245. package/dist/studio/ui-components.generated.js +93 -1
  246. package/dist/upgrade-prompt.d.ts +1 -1
  247. package/dist/version.d.ts +2 -2
  248. package/dist/version.js +2 -2
  249. package/dist/wc/GridFooter-f5K0aLe_.js +145 -0
  250. package/dist/wc/GridMenus-FtSN_8t9.js +1369 -0
  251. package/dist/wc/PinSplitter-D-qtDhSM.js +132 -0
  252. package/dist/wc/{SvGrid.helpers-zBkDTryR.js → SvGrid.helpers-B2GEKrxS.js} +84 -36
  253. package/dist/wc/{SvGridCellEditor-CzUKEbkI.js → SvGridCellEditor-B_cLJrsp.js} +20 -9
  254. package/dist/wc/conditional-formatting-BXis_oP1.js +189 -0
  255. package/dist/wc/grid-extension.svelte-Bufq4B4C.js +20 -0
  256. package/dist/wc/sv-sheet-element.js +7556 -7617
  257. package/dist/wc/svelte-virtualizer.svelte-ehwOupl-.js +360 -0
  258. package/dist/wc/{validate-43C03DLP.js → validate-BaMO8UPE.js} +1 -1
  259. package/package.json +14 -3
  260. package/dist/designer/assets/GridMenus-CvcASlKv.js +0 -8
  261. package/dist/designer/assets/SvGrid.helpers-DE5mmORW.js +0 -1
  262. package/dist/designer/assets/SvGridCellEditor-C7NWcnNC.js +0 -4
  263. package/dist/designer/assets/SvListBox-BlmmzYcU.js +0 -110
  264. package/dist/designer/assets/index-KmSEY7Lb.css +0 -1
  265. package/dist/designer/assets/src-DqHCqori.js +0 -58
  266. package/dist/designer/assets/validate-B71dE1Cs.js +0 -1
  267. package/dist/wc/GridMenus-fWbBUiRH.js +0 -1088
  268. package/dist/wc/svelte-virtualizer.svelte-Dntdh8Ms.js +0 -581
@@ -27,6 +27,7 @@
27
27
  portalToBody,
28
28
  popIn,
29
29
  createDismissableLayer,
30
+ getGridExtension,
30
31
  onScrollOutside,
31
32
  SvDrawer,
32
33
  SvForm,
@@ -161,7 +162,16 @@
161
162
  }
162
163
 
163
164
  // --- columns lookup + default title field ---
164
- const fieldColumns = $derived(columns.filter((c) => typeof c.field === "string"));
165
+ // Columns declared by field type are filled in as the grid fills them
166
+ // (formatter, computed value), so the task table shows what the grid shows.
167
+ const fieldColumns = $derived.by(() => {
168
+ const own = columns.filter((c) => typeof c.field === "string");
169
+ const fill = getGridExtension()?.column;
170
+ if (!fill) return own;
171
+ type Def = Record<string, unknown>;
172
+ const defs = own as unknown as Def[];
173
+ return defs.map((c) => (c.fieldType ? fill(c, defs) : c)) as unknown as typeof own;
174
+ });
165
175
  const titleField = $derived(
166
176
  gantt.titleField ?? (fieldColumns[0]?.field as string | undefined),
167
177
  );
@@ -178,7 +188,11 @@
178
188
  getTitle: (r) => (titleField ? String(fieldValue(r, titleField) ?? "") : ""),
179
189
  getProgress: (r) =>
180
190
  progressOf[key(r)] ??
181
- (gantt.progressField ? (fieldValue(r, gantt.progressField) as never) : undefined),
191
+ (pcfg.progressValue
192
+ ? (pcfg.progressValue(r) as never)
193
+ : gantt.progressField
194
+ ? (fieldValue(r, gantt.progressField) as never)
195
+ : undefined),
182
196
  getParent: (r) =>
183
197
  gantt.parentField
184
198
  ? (fieldValue(r, gantt.parentField) as string | null | undefined)
@@ -186,9 +200,10 @@
186
200
  getMilestone: (r) =>
187
201
  gantt.milestoneField ? !!fieldValue(r, gantt.milestoneField) : false,
188
202
  getColor: (r) =>
189
- gantt.colorField
203
+ pcfg.colorOf?.(r) ??
204
+ (gantt.colorField
190
205
  ? ((fieldValue(r, gantt.colorField) as string | undefined) ?? gantt.color)
191
- : gantt.color,
206
+ : gantt.color),
192
207
  }));
193
208
 
194
209
  const tasks = $derived(resolveTasks(data, spec, cal));
@@ -323,6 +338,9 @@
323
338
  (pct: number) => new Date(axis.start.getTime() + (pct / 100) * axis.totalMs),
324
339
  );
325
340
  const bodyH = $derived(Math.max(rowH, nodes.length * rowH));
341
+ // The chart fills the pane below the last row too: the weekend shading runs
342
+ // to the bottom, and a double-click there adds a task.
343
+ let axisH = $state(0);
326
344
 
327
345
  // Non-working columns, as day bands. Under a day-granular axis these are
328
346
  // whole days; a tick coarser than a day (week / month) is never wholly
@@ -419,6 +437,16 @@
419
437
  });
420
438
  };
421
439
  for (const d of gantt.dependencies ?? []) push(d);
440
+ // Predecessors listed on the task that waits for them.
441
+ const pf = pcfg.predecessorField;
442
+ if (pf) {
443
+ for (const row of data) {
444
+ const raw = fieldValue(row, pf);
445
+ const ids = Array.isArray(raw) ? raw : raw == null || raw === "" ? [] : [raw];
446
+ const to = key(row);
447
+ for (const from of ids) push({ id: `${String(from)}->${to}`, from: String(from), to });
448
+ }
449
+ }
422
450
  const f = gantt.dependencyField;
423
451
  if (f) {
424
452
  for (const row of data) {
@@ -780,7 +808,7 @@
780
808
  // The hover card describes the bar as it was grabbed; after a move it
781
809
  // would go on showing those dates until the pointer left the bar.
782
810
  clearTimeout(tipTimer);
783
- tipBar = null;
811
+ tipKey = null;
784
812
  window.addEventListener("pointermove", onDragMove);
785
813
  window.addEventListener("pointerup", onDragEnd, { once: true });
786
814
  window.addEventListener("pointercancel", onDragCancel, { once: true });
@@ -1332,6 +1360,8 @@
1332
1360
  kind: "field" | "duration" | "progress" | "slack";
1333
1361
  field?: string;
1334
1362
  numeric?: boolean;
1363
+ /** The column itself, so a cell reads its value and shows it as the grid does. */
1364
+ def?: ColumnDef<TFeatures, TData>;
1335
1365
  };
1336
1366
  /** Two columns a consumer can name in `tableColumns` without defining them. */
1337
1367
  const BUILT_IN: Record<string, TableCol> = {
@@ -1346,9 +1376,10 @@
1346
1376
  const fromDef = (c: ColumnDef<TFeatures, TData>): TableCol => ({
1347
1377
  id: colId(c),
1348
1378
  label: typeof c.header === "string" ? c.header : ((c.field as string) ?? ""),
1349
- width: c.width ?? 140,
1379
+ width: pcfg.tableColumnWidths?.[colId(c)] ?? c.width ?? 140,
1350
1380
  kind: "field",
1351
1381
  field: c.field as string | undefined,
1382
+ def: c,
1352
1383
  });
1353
1384
  const want = gantt.tableColumns;
1354
1385
  if (!want) return fieldColumns.map(fromDef);
@@ -1397,7 +1428,9 @@
1397
1428
  if (tableWFitted || tableWTouched || gantt.tableWidth != null || viewportW <= 0) return;
1398
1429
  tableWFitted = true;
1399
1430
  const trailing = tableColsWidth - (tableCols[0]?.width ?? 0);
1400
- tableW = Math.max(180, trailing + 120, Math.min(tableW, Math.floor(viewportW * 0.4)));
1431
+ // Never more than 60% of the container either: with many columns the
1432
+ // trailing ones clip (the splitter shows them), the chart keeps its room.
1433
+ tableW = Math.min(Math.floor(viewportW * 0.6), Math.max(180, trailing + 120, Math.min(tableW, Math.floor(viewportW * 0.4))));
1401
1434
  });
1402
1435
  const showTable = $derived(tableCols.length > 0 && tableW > 0);
1403
1436
 
@@ -1428,10 +1461,29 @@
1428
1461
  }
1429
1462
  return String(slackDays(cpm, n.task.key));
1430
1463
  }
1431
- if (col.kind === "field" && col.field) return fmt(fieldValue(n.task.row, col.field));
1464
+ if (col.kind === "field" && col.field) return fieldText(col, n.task.row);
1432
1465
  return "";
1433
1466
  }
1434
1467
 
1468
+ /**
1469
+ * A field cell as the grid shows it: the column's computed value
1470
+ * (`fieldFn`) through its `formatter` (a select's label, a person's name, a
1471
+ * date in the column's format), else the plain value.
1472
+ */
1473
+ function fieldText(col: TableCol, row: TData): string {
1474
+ const def = col.def as { fieldFn?: (r: TData) => unknown; formatter?: (c: { value: unknown; row: TData; column: unknown }) => unknown } | undefined;
1475
+ const value = def?.fieldFn ? def.fieldFn(row) : fieldValue(row, col.field!);
1476
+ if (def?.formatter) {
1477
+ try {
1478
+ return String(def.formatter({ value, row, column: { id: col.id, columnDef: def } }) ?? "");
1479
+ } catch {
1480
+ return fmt(value);
1481
+ }
1482
+ }
1483
+ if (Array.isArray(value)) return value.map(fmt).join(", ");
1484
+ return fmt(value);
1485
+ }
1486
+
1435
1487
  // --- splitter -------------------------------------------------------------
1436
1488
  let splitDrag: { startX: number; startW: number } | null = null;
1437
1489
  function startSplit(e: PointerEvent) {
@@ -1460,6 +1512,8 @@
1460
1512
  let scrollEl = $state<HTMLElement | null>(null);
1461
1513
  let scrollTop = $state(0);
1462
1514
  let viewportH = $state(0);
1515
+ // Below viewportH, which it reads (see axisH above).
1516
+ const fillH = $derived(Math.max(bodyH, viewportH - axisH - 1));
1463
1517
  function onScroll() {
1464
1518
  if (scrollEl) scrollTop = scrollEl.scrollTop;
1465
1519
  }
@@ -1573,22 +1627,25 @@
1573
1627
  const tooltipCfg = $derived(gantt.tooltip);
1574
1628
  const hasTooltip = $derived(!!tooltipCfg);
1575
1629
  const tooltipSnippet = $derived(typeof tooltipCfg === "function" ? tooltipCfg : undefined);
1576
- let tipBar = $state<Bar | null>(null);
1630
+ // The hovered bar by key, read from the bars as drawn now: a bar object
1631
+ // kept from the hover would show the dates it had then (mid-drag, say).
1632
+ let tipKey = $state<string | null>(null);
1633
+ const tipBar = $derived(tipKey ? (bars.find((b) => b.key === tipKey) ?? null) : null);
1577
1634
  let tipPos = $state({ x: 0, y: 0 });
1578
1635
  let tipTimer: ReturnType<typeof setTimeout> | undefined;
1579
1636
  function onBarEnter(e: MouseEvent, bar: Bar) {
1580
- if (!hasTooltip) return;
1637
+ if (!hasTooltip || drag) return;
1581
1638
  clearTimeout(tipTimer);
1582
1639
  const target = e.currentTarget as HTMLElement;
1583
1640
  tipTimer = setTimeout(() => {
1584
1641
  const r = target.getBoundingClientRect();
1585
1642
  tipPos = { x: Math.round(r.left + r.width / 2), y: Math.round(r.top) };
1586
- tipBar = bar;
1643
+ tipKey = bar.key;
1587
1644
  }, gantt.tooltipDelay ?? 400);
1588
1645
  }
1589
1646
  function onBarLeave() {
1590
1647
  clearTimeout(tipTimer);
1591
- tipBar = null;
1648
+ tipKey = null;
1592
1649
  }
1593
1650
  $effect(() => () => clearTimeout(tipTimer));
1594
1651
 
@@ -1682,6 +1739,11 @@
1682
1739
  suppressClick = false;
1683
1740
  return;
1684
1741
  }
1742
+ // The app opens tasks itself (a record view): that wins over the drawer.
1743
+ if (pcfg.onTaskOpen) {
1744
+ pcfg.onTaskOpen(bar.row);
1745
+ return;
1746
+ }
1685
1747
  if (!hasDrawer) return;
1686
1748
  const next: Record<string, unknown> = {
1687
1749
  __start: dayInput(bar.start),
@@ -1743,8 +1805,11 @@
1743
1805
  let menuPanel = $state<HTMLElement | null>(null);
1744
1806
  function openMenu(e: MouseEvent, bar: Bar) {
1745
1807
  const items: MenuItem[] = [];
1746
- if (hasDrawer) items.push({ label: "Edit", onSelect: () => openDrawer(bar) });
1747
- if (editable && gantt.onTaskAdd) {
1808
+ if (pcfg.onTaskOpen) items.push({ label: "Open", onSelect: () => pcfg.onTaskOpen?.(bar.row) });
1809
+ else if (hasDrawer) items.push({ label: "Edit", onSelect: () => openDrawer(bar) });
1810
+ // A subtask needs a tree to hang in: without parentField it would be
1811
+ // just another task.
1812
+ if (editable && gantt.onTaskAdd && gantt.parentField) {
1748
1813
  items.push({
1749
1814
  label: "Add subtask",
1750
1815
  onSelect: () => gantt.onTaskAdd?.(bar.start, addDays(bar.start, 1), bar.key),
@@ -1937,6 +2002,8 @@
1937
2002
  ></span>
1938
2003
  </span>
1939
2004
  <span class="sv-gantt-meter-pct">{Math.round(drawnSpan(node).progress)}%</span>
2005
+ {:else if ci === 0 && pcfg.onTaskOpen}
2006
+ <button type="button" class="sv-gantt-cell-text sv-gantt-open" title={`Open ${node.task.title}`} onclick={() => pcfg.onTaskOpen?.(node.task.row)}>{cellText(col, node)}</button>
1940
2007
  {:else}
1941
2008
  <span class="sv-gantt-cell-text">{cellText(col, node)}</span>
1942
2009
  {/if}
@@ -1957,7 +2024,7 @@
1957
2024
  {/if}
1958
2025
 
1959
2026
  <div class="sv-gantt-chart" style={`width:${axisPx}px`}>
1960
- <div class="sv-gantt-axis" style={`width:${axisPx}px`}>
2027
+ <div class="sv-gantt-axis" style={`width:${axisPx}px`} bind:clientHeight={axisH}>
1961
2028
  <div class="sv-gantt-majors">
1962
2029
  {#each axis.majors as m (m.leftPct)}
1963
2030
  <div
@@ -1982,7 +2049,7 @@
1982
2049
  <div
1983
2050
  class="sv-gantt-body"
1984
2051
  bind:this={bodyEl}
1985
- style={`width:${axisPx}px; height:${bodyH}px`}
2052
+ style={`width:${axisPx}px; height:${fillH}px`}
1986
2053
  ondblclick={onBodyDblClick}
1987
2054
  oncontextmenu={onBodyContextMenu}
1988
2055
  >
@@ -2400,6 +2467,11 @@
2400
2467
  }
2401
2468
  .sv-gantt-parent { font-weight: 600; }
2402
2469
  .sv-gantt-cell-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2470
+ .sv-gantt-open {
2471
+ min-width: 0; padding: 0; border: 0; background: none; font: inherit; color: inherit; text-align: start; cursor: pointer;
2472
+ }
2473
+ .sv-gantt-open:hover { text-decoration: underline; text-underline-offset: 2px; }
2474
+ .sv-gantt-open:focus-visible { outline: 2px solid var(--sg-focus, var(--sg-accent, #2563eb)); outline-offset: 1px; border-radius: 3px; }
2403
2475
  .sv-gantt-chevron {
2404
2476
  flex: none;
2405
2477
  width: 16px;
@@ -18,6 +18,34 @@ import type { GanttConfig, RowData, SchedulerResource, TableFeatures } from '@sv
18
18
  */
19
19
  export type GanttConstraint = 'ASAP' | 'ALAP' | 'MSO' | 'MFO' | 'SNET' | 'SNLT' | 'FNET' | 'FNLT';
20
20
  export type GanttProConfig<TFeatures extends TableFeatures = TableFeatures, TData extends RowData = RowData> = GanttConfig<TFeatures, TData> & {
21
+ /**
22
+ * A bar's color from its row (a status option's color, say). Wins over
23
+ * `colorField` and `color`; return nothing to fall back to them.
24
+ */
25
+ colorOf?: (row: TData) => string | null | undefined;
26
+ /**
27
+ * Open a task where the app keeps it (a record view, a page) instead of
28
+ * the built-in drawer: a click or Enter on a bar, the task's name in the
29
+ * table, and "Open" in the bar's menu call it.
30
+ */
31
+ onTaskOpen?: (row: TData) => void;
32
+ /**
33
+ * Field holding the ids of the tasks this one DEPENDS ON (its predecessors),
34
+ * drawn as finish-to-start arrows into it. The other direction from
35
+ * `dependencyField`, which lists a task's successors.
36
+ */
37
+ predecessorField?: keyof TData & string;
38
+ /**
39
+ * A task's progress, 0 to 100, from its row: for a field that holds
40
+ * something else (a fraction, a status). Wins over `progressField` when
41
+ * drawing; the grip still reports 0 to 100 through `onProgressChange`.
42
+ */
43
+ progressValue?: (row: TData) => number | null | undefined;
44
+ /**
45
+ * Widths (px) of task-table columns by id, over the columns' own `width`:
46
+ * a grid column sized for a wide table can sit narrower beside the bars.
47
+ */
48
+ tableColumnWidths?: Record<string, number>;
21
49
  /**
22
50
  * Highlight the critical path: the chain of linked tasks with no slack,
23
51
  * where any slip moves the project's finish. Needs `dependencies`.
package/dist/index.d.ts CHANGED
@@ -9,6 +9,7 @@ export { default as SvImportDialog } from './SvImportDialog.svelte';
9
9
  export { installEnterprise, type EnterpriseGridApi, type EnterpriseAIApi, type EnterprisePivotApi } from './install.js';
10
10
  export { enableSchedulerView, SvGridScheduler } from './scheduler.js';
11
11
  export { enableBoardView, SvGridBoard } from './board.js';
12
+ export { enableGalleryView, SvGridGallery } from './gallery.js';
12
13
  export * from './gantt/index.js';
13
14
  export { enableSelectionBar, SvGridSelectionBar } from './selection-bar.js';
14
15
  export { applyBulkEdit, bulkEditableFields, bulkEditInitialValues, coerceBulkValue, type BulkEditField, type BulkEditResult } from './bulk-edit.js';
@@ -147,6 +148,8 @@ export { evaluatePredicate, evaluateScalar, evaluateChange, BUILTIN_FUNCTION_NAM
147
148
  export { parsePredicate, stringifyPredicate, stringifyScalar, validateExpression, collectColumnRefs, ExpressionParseError, } from './expressions/parse.js';
148
149
  export { OPERATORS, operatorMeta, operatorsForType, isValueless, isSetOperator, isRangeOperator, columnById, resolveColumnRef, columnLabel, columnRefText, type ExprColumn, type ExprColumnType, type OperatorMeta, } from './expressions/expression-columns.js';
149
150
  export type { ScalarExpr, PredicateExpr, ChangeExpr, ComparisonOp, ArithmeticOp, AggFn, EvalContext, ExprRow, } from './expressions/expression-types.js';
151
+ export { registerPredicateKind, getPredicateKind, type PredicateKind, type PredicateKindContext } from './expressions/kinds.js';
152
+ export { setAlertConditionEditor, type AlertConditionEditor } from './alerts/condition-editor.js';
150
153
  export { default as SvExpressionEditor } from './SvExpressionEditor.svelte';
151
154
  export { enableAlerts } from './alerts.js';
152
155
  export { createAlertEngine, renderTemplate, type AlertEngine, type AlertEngineOptions, type ValidateEditResult, } from './alerts/alert-engine.js';
package/dist/index.js CHANGED
@@ -20,6 +20,9 @@ export { enableSchedulerView, SvGridScheduler } from './scheduler.js';
20
20
  // Kanban board view (Pro). `enableBoardView()` registers the renderer so
21
21
  // `<SvGrid board={...}>` shows lanes of cards. Config types live in @svgrid/grid.
22
22
  export { enableBoardView, SvGridBoard } from './board.js';
23
+ // Gallery view (Pro). `enableGalleryView()` registers the renderer so
24
+ // `<SvGrid gallery={...}>` shows rows as cards. Config types live in @svgrid/grid.
25
+ export { enableGalleryView, SvGridGallery } from './gallery.js';
23
26
  // Gantt view (Pro). `enableGanttView()` registers the renderer so
24
27
  // `<SvGrid gantt={...}>` shows a task table beside a time chart. The `gantt`
25
28
  // prop and its config types live in @svgrid/grid; the layout model, the
@@ -188,6 +191,9 @@ export { pivotToChartSpec } from './pivot-chart.js';
188
191
  export { evaluatePredicate, evaluateScalar, evaluateChange, BUILTIN_FUNCTION_NAMES, isBuiltinFunction, } from './expressions/evaluate.js';
189
192
  export { parsePredicate, stringifyPredicate, stringifyScalar, validateExpression, collectColumnRefs, ExpressionParseError, } from './expressions/parse.js';
190
193
  export { OPERATORS, operatorMeta, operatorsForType, isValueless, isSetOperator, isRangeOperator, columnById, resolveColumnRef, columnLabel, columnRefText, } from './expressions/expression-columns.js';
194
+ // A module's own condition kind: the `ext` node (SvGrid Base's field filter).
195
+ export { registerPredicateKind, getPredicateKind } from './expressions/kinds.js';
196
+ export { setAlertConditionEditor } from './alerts/condition-editor.js';
191
197
  export { default as SvExpressionEditor } from './SvExpressionEditor.svelte';
192
198
  // Alert Rules engine (Pro). Declarative "when this holds / this moves -> toast /
193
199
  // badge / highlight / flash / prevent-edit / log". Authored at runtime,
package/dist/install.js CHANGED
@@ -9,6 +9,7 @@ import { aiFilter, aiSmartFill, aiSummarize, aiClassify, aiExport, aiFindAnomali
9
9
  import { createPivotModel, } from './pivot.js';
10
10
  import { enableSchedulerView } from './scheduler.js';
11
11
  import { enableBoardView } from './board.js';
12
+ import { enableGalleryView } from './gallery.js';
12
13
  import { enableGanttView } from './gantt/gantt.js';
13
14
  import { enableSelectionBar } from './selection-bar.js';
14
15
  import { enableAdvancedFilter } from './advanced-filter-enable.js';
@@ -46,6 +47,8 @@ export function installEnterprise(api) {
46
47
  enableSchedulerView();
47
48
  // Register the Kanban board view (no-op without the `board` prop).
48
49
  enableBoardView();
50
+ // Register the gallery view (no-op without the `gallery` prop).
51
+ enableGalleryView();
49
52
  // Register the Gantt view (no-op without the `gantt` prop).
50
53
  enableGanttView();
51
54
  enableSelectionBar();
@@ -2,13 +2,13 @@
2
2
  export declare const VALID_PREFIX = "SVENTERPRISE-";
3
3
  /**
4
4
  * What an order bought. `grid` is the enterprise grid on its own; `suite` adds
5
- * the spreadsheet and the Studio. Keys issued before editions existed carry no
5
+ * the spreadsheet, SvGrid Base (the table database) and the Studio. Keys issued before editions existed carry no
6
6
  * edition segment and read as `suite`, so nothing an existing customer already
7
7
  * ships changes when this lands.
8
8
  */
9
9
  export type LicenseEdition = 'grid' | 'suite';
10
- /** The three things an edition can gate. */
11
- export type LicensedProduct = 'grid' | 'spreadsheet' | 'studio';
10
+ /** The things an edition can gate. */
11
+ export type LicensedProduct = 'grid' | 'spreadsheet' | 'base' | 'studio';
12
12
  /** Does `edition` cover `product`? Pure, and the only place the answer lives. */
13
13
  export declare function editionCovers(edition: LicenseEdition, product: LicensedProduct): boolean;
14
14
  export type LicenseStatus = 'unset' | 'invalid' | 'revoked' | 'dev' | 'eval' | 'licensed';
package/dist/license.js CHANGED
@@ -139,6 +139,7 @@ export function nudgeEnterprise(feature) {
139
139
  const PRODUCT_LABEL = {
140
140
  grid: 'the enterprise grid',
141
141
  spreadsheet: 'the spreadsheet',
142
+ base: 'SvGrid Base',
142
143
  studio: 'the Studio',
143
144
  };
144
145
  /** One-time console notice for a feature the current edition does not cover.
@@ -2206,6 +2206,12 @@ export const variables = defineEnvVars({\n${names.map(([n, pub]) => ` ${n}: { $
2206
2206
  };
2207
2207
  }
2208
2208
  //#endregion
2209
+ //#region src/expressions/kinds.ts
2210
+ var kinds = /* @__PURE__ */ new Map();
2211
+ function getPredicateKind(type) {
2212
+ return kinds.get(type);
2213
+ }
2214
+ //#endregion
2209
2215
  //#region src/expressions/parse.ts
2210
2216
  /** Every column id referenced anywhere in a predicate (deduped, in order). */
2211
2217
  function collectColumnRefs(expr) {
@@ -2229,6 +2235,9 @@ function walkPredicate(expr, out) {
2229
2235
  walkScalar(expr.left, out);
2230
2236
  walkScalar(expr.right, out);
2231
2237
  return;
2238
+ case "ext":
2239
+ for (const c of getPredicateKind(expr.type)?.columns?.(expr.payload) ?? []) out.add(c);
2240
+ return;
2232
2241
  case "const": return;
2233
2242
  }
2234
2243
  }
@@ -8723,6 +8732,13 @@ var GENERATED_UI_SURFACE = {
8723
8732
  "description": "Kanban board mode. When set, the grid renders its rows as cards in horizontal lanes (bucketed by `board.groupBy`) instead of a table. See .",
8724
8733
  "group": "common"
8725
8734
  },
8735
+ {
8736
+ "key": "gallery",
8737
+ "label": "Gallery",
8738
+ "type": "json",
8739
+ "description": "Gallery mode. When set, the grid renders its rows as cards (a cover image, a title, a few fields) instead of a table. See .",
8740
+ "group": "common"
8741
+ },
8726
8742
  {
8727
8743
  "key": "scheduler",
8728
8744
  "label": "Scheduler",
@@ -8772,6 +8788,55 @@ var GENERATED_UI_SURFACE = {
8772
8788
  "description": "Floating bar that appears over the grid while rows are selected, showing the count and the actions that apply to the whole selection - the pattern an issue tracker uses for bulk edit. `true` gives the count and a Clear button. An array is the shorthand for `{ actions }`. Pass to also set `position` (`'bottom'` default, or `'top'`), `maxVisible` and `hideClear`. Omitted / `false` is off. Default **off**. This is chrome that floats over your content, not a gesture: it overlaps a row, and what belongs on it is specific to the app. **The prop and its types are free; the bar itself is an Enterprise feature.** Install `@svgrid/enterprise` and call `enableSelectionBar()` (or `installEnterprise`, which does it for you) to render it - without that the grid shows a short upsell note in its place, the same way the scheduler and board views do.",
8773
8789
  "group": "common"
8774
8790
  },
8791
+ {
8792
+ "key": "rowAdder",
8793
+ "label": "Row Adder",
8794
+ "type": "boolean",
8795
+ "description": "A \"+ Add row\" line under the last row. Clicking it (or Enter on it) appends a row, scrolls to it and starts editing its first editable cell. `true` uses the defaults; pass for the label and the new row's values. **The prop and its types are free; the row adder is drawn by the Enterprise `@svgrid/enterprise/fields` module** (`enableFields()`). Without it the prop does nothing.",
8796
+ "group": "common"
8797
+ },
8798
+ {
8799
+ "key": "rowColor",
8800
+ "label": "Row Color",
8801
+ "type": "json",
8802
+ "description": "Color rows by a status: by a select field (each row takes its option's `color`) or by rules (first match wins). The color shows on the row header by default (a 3px stripe at the start of the row-number cell); `target` moves it to a stripe in the first cell or a tint across the row. The row header also gets the status as its title, so the color is never the only signal. Computed for rendered rows only. **The prop and its types are free; the colors are drawn by the Enterprise `@svgrid/enterprise/fields` module** (`enableFields()`).",
8803
+ "group": "common"
8804
+ },
8805
+ {
8806
+ "key": "columnAdder",
8807
+ "label": "Column Adder",
8808
+ "type": "boolean",
8809
+ "description": "A \"+\" header cell after the last column that opens the field editor: a name, a field type and that type's properties. Creating adds the column and fires `onColumnAdd`; the grid never stores a schema, the app does. `{ types }` limits the types offered. **Drawn by the Enterprise `@svgrid/enterprise/fields` module.**",
8810
+ "group": "common"
8811
+ },
8812
+ {
8813
+ "key": "fieldMenu",
8814
+ "label": "Field Menu",
8815
+ "type": "boolean",
8816
+ "description": "Field actions in each column's menu: Edit field, Insert left / right, Duplicate, Delete, Hide. Edit can change the type; values are converted where they can be and the editor says how many cells would be cleared. **Drawn by the Enterprise `@svgrid/enterprise/fields` module.**",
8817
+ "group": "common"
8818
+ },
8819
+ {
8820
+ "key": "expandRows",
8821
+ "label": "Expand Rows",
8822
+ "type": "boolean",
8823
+ "description": "The row-number cell becomes the row's handle: on hover it shows a checkbox that selects the row and an expand button that opens the row as a record, every field with its label and its editor. With `showRowNumbers`. **Drawn by the Enterprise `@svgrid/enterprise/fields` module.**",
8824
+ "group": "common"
8825
+ },
8826
+ {
8827
+ "key": "pinSplitter",
8828
+ "label": "Pin Splitter",
8829
+ "type": "boolean",
8830
+ "description": "A divider at the edge of the left-pinned columns: drag it right to pin the columns it passes, left to unpin them. It snaps to column edges and keeps the columns where they are on screen; with the keyboard, focus it and use the arrow keys. Its words come from `localization.text` (`pinSplitterLabel`, `pinSplitterHint`, `pinSplitterNone`, `pinSplitterOne`, `pinSplitterMany`). Loaded only when set.",
8831
+ "group": "common"
8832
+ },
8833
+ {
8834
+ "key": "primaryField",
8835
+ "label": "Primary Field",
8836
+ "type": "string",
8837
+ "description": "The table's primary field (a column id or field): always the first column and pinned, never hidden or deleted, and the name of a record (the expanded record's title, a gallery card's title). **Kept by the Enterprise `@svgrid/enterprise/fields` module.**",
8838
+ "group": "common"
8839
+ },
8775
8840
  {
8776
8841
  "key": "features",
8777
8842
  "label": "Features",
@@ -9530,7 +9595,7 @@ var GENERATED_UI_SURFACE = {
9530
9595
  "key": "rowDragManaged",
9531
9596
  "label": "Row Drag Managed",
9532
9597
  "type": "boolean",
9533
- "description": "Enables managed row dragging. When `true`, every row becomes a drag source (grab cursor + a grip in the row-number cell) and a drop indicator paints between rows during a drag. On drop the grid mutates its own internal data - reordering within the grid, or moving the row across grids that share the same . Defaults to `false`.",
9598
+ "description": "Enables managed row dragging. When `true`, a row is dragged by its row-number cell (a grip shows while it is hovered), or by any cell when row numbers are off, and a drop indicator paints between rows during a drag. On drop the grid mutates its own internal data - reordering within the grid, or moving the row across grids that share the same . Defaults to `false`.",
9534
9599
  "group": "common"
9535
9600
  },
9536
9601
  {
@@ -9562,6 +9627,42 @@ var GENERATED_UI_SURFACE = {
9562
9627
  "prop": "onPivotModeChange",
9563
9628
  "description": "Fired when the in-grid Pivot toggle flips `pivotMode`."
9564
9629
  },
9630
+ {
9631
+ "key": "rowAdd",
9632
+ "label": "Row Add",
9633
+ "prop": "onRowAdd",
9634
+ "description": "Fires after the row adder appended a row, with that row. Persist it here."
9635
+ },
9636
+ {
9637
+ "key": "columnAdd",
9638
+ "label": "Column Add",
9639
+ "prop": "onColumnAdd",
9640
+ "description": "A field was added (column adder, Insert, Duplicate). `columns` is the whole column list after the change, as declared (store it as your `columns`); `data` is set when values were written too."
9641
+ },
9642
+ {
9643
+ "key": "columnChange",
9644
+ "label": "Column Change",
9645
+ "prop": "onColumnChange",
9646
+ "description": "A field was edited. `columns` as in `onColumnAdd`; `data` is set when a type change converted its values."
9647
+ },
9648
+ {
9649
+ "key": "columnRemove",
9650
+ "label": "Column Remove",
9651
+ "prop": "onColumnRemove",
9652
+ "description": "A field was deleted from the column menu (its values stay in the rows). `columns` as in `onColumnAdd`."
9653
+ },
9654
+ {
9655
+ "key": "rowsDelete",
9656
+ "label": "Rows Delete",
9657
+ "prop": "onRowsDelete",
9658
+ "description": "Rows were deleted from the selection bar's Delete (`deleteRows`): the rows and their ids, and the data without them, to store."
9659
+ },
9660
+ {
9661
+ "key": "rowColorChange",
9662
+ "label": "Row Color Change",
9663
+ "prop": "onRowColorChange",
9664
+ "description": "The rows were set to be colored by another field, or no longer colored, from a single select's column menu (\"Color rows by this field\" with `fieldMenu`). Store it and pass it back as `rowColor`."
9665
+ },
9565
9666
  {
9566
9667
  "key": "expandedChange",
9567
9668
  "label": "Expanded Change",
@@ -16345,7 +16446,7 @@ function hasFieldConditions(schema) {
16345
16446
  *
16346
16447
  * `version.test.ts` asserts this matches `package.json`, so the two cannot drift.
16347
16448
  */
16348
- var SVGRID_VERSION = "3.1.8";
16449
+ var SVGRID_VERSION = "3.1.9";
16349
16450
  /**
16350
16451
  * The version of `@svgrid/grid`, which releases on its own line: enterprise is
16351
16452
  * on 3.1.x while the grid is on 3.0.x.
@@ -16358,7 +16459,7 @@ var SVGRID_VERSION = "3.1.8";
16358
16459
  *
16359
16460
  * `version.test.ts` asserts this matches packages/grid/package.json.
16360
16461
  */
16361
- var GRID_VERSION = "3.0.11";
16462
+ var GRID_VERSION = "3.0.12";
16362
16463
  //#endregion
16363
16464
  //#region src/studio/emit-project.ts
16364
16465
  var has = (blocks, kind) => blocks.some((b) => b.config.kind === kind);
@@ -41337,7 +41438,7 @@ async function listSupabaseTables(url, key, fetchFn = globalThis.fetch) {
41337
41438
  /** Introspect a Supabase/PostgREST table into an EntitySchema, or null. */
41338
41439
  async function introspectSupabaseTable(options) {
41339
41440
  const o = {
41340
- fetch: globalThis.fetch,
41441
+ fetch: (...args) => globalThis.fetch(...args),
41341
41442
  ...options
41342
41443
  };
41343
41444
  const base = o.url.replace(/\/+$/, "");
@@ -108,6 +108,17 @@ export type SchedulerProConfig<TFeatures extends TableFeatures = TableFeatures,
108
108
  hidden?: boolean;
109
109
  }>;
110
110
  calendarField?: keyof TData & string;
111
+ /**
112
+ * Open an event where the app keeps it (a record view) instead of the
113
+ * built-in drawer: a click on the event calls it.
114
+ */
115
+ onEventOpen?: (row: TData) => void;
116
+ /**
117
+ * `false`: events move but keep their length - no edge grips. For records
118
+ * with one date (a start or a due date) there is no end to change.
119
+ * Default `true`.
120
+ */
121
+ resizable?: boolean;
111
122
  /**
112
123
  * External busy intervals to shade on a resource's timeline row - times it is
113
124
  * unavailable that aren't events in this scheduler (its own calendar). Combine
@@ -125,7 +125,10 @@ export async function listSupabaseTables(url, key, fetchFn = globalThis.fetch) {
125
125
  }
126
126
  /** Introspect a Supabase/PostgREST table into an EntitySchema, or null. */
127
127
  export async function introspectSupabaseTable(options) {
128
- const o = { fetch: globalThis.fetch, ...options };
128
+ // A wrapper, not `globalThis.fetch` itself: called as `o.fetch(...)` the
129
+ // browser's fetch gets `o` as its receiver and throws "Illegal invocation",
130
+ // which the paths below catch as "no answer", so no table was ever found.
131
+ const o = { fetch: (...args) => globalThis.fetch(...args), ...options };
129
132
  const base = o.url.replace(/\/+$/, '');
130
133
  const columns = (await viaOpenApi(o, base)) ?? (await viaSample(o, base)) ?? (await viaCsv(o, base));
131
134
  if (!columns || !columns.length)
@@ -110,8 +110,12 @@ export function createRestDataSource(config) {
110
110
  };
111
111
  const send = async (url, init) => {
112
112
  const res = await doFetch(url, init);
113
- if (!res.ok)
114
- throw new Error(`REST ${init?.method ?? 'GET'} ${url} -> ${res.status} ${res.statusText}`);
113
+ if (!res.ok) {
114
+ // The endpoint's own words, when it sent any (`{ error }` or `{ message }`): a
115
+ // validation message reads better to the user than the status line.
116
+ const said = await res.json().then((b) => b?.error ?? b?.message, () => undefined);
117
+ throw new Error(`REST ${init?.method ?? 'GET'} ${url} -> ${res.status} ${res.statusText}${typeof said === 'string' && said ? `: ${said}` : ''}`);
118
+ }
115
119
  const parsed = res.status === 204 ? undefined : await res.json().catch(() => undefined);
116
120
  return { body: parsed, res };
117
121
  };