@svgrid/enterprise 3.1.7 → 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 (270) 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 +11375 -10805
  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-DtBIaQno.js → index-BPU2KNc5.js} +396 -395
  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 +141 -20
  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/emit-project.js +25 -6
  246. package/dist/studio/sveltekit3.js +13 -7
  247. package/dist/studio/ui-components.generated.js +94 -2
  248. package/dist/upgrade-prompt.d.ts +1 -1
  249. package/dist/version.d.ts +2 -2
  250. package/dist/version.js +2 -2
  251. package/dist/wc/GridFooter-f5K0aLe_.js +145 -0
  252. package/dist/wc/GridMenus-FtSN_8t9.js +1369 -0
  253. package/dist/wc/PinSplitter-D-qtDhSM.js +132 -0
  254. package/dist/wc/{SvGrid.helpers-zBkDTryR.js → SvGrid.helpers-B2GEKrxS.js} +84 -36
  255. package/dist/wc/{SvGridCellEditor-CzUKEbkI.js → SvGridCellEditor-B_cLJrsp.js} +20 -9
  256. package/dist/wc/conditional-formatting-BXis_oP1.js +189 -0
  257. package/dist/wc/grid-extension.svelte-Bufq4B4C.js +20 -0
  258. package/dist/wc/sv-sheet-element.js +7561 -7618
  259. package/dist/wc/svelte-virtualizer.svelte-ehwOupl-.js +360 -0
  260. package/dist/wc/{validate-43C03DLP.js → validate-BaMO8UPE.js} +1 -1
  261. package/package.json +14 -3
  262. package/dist/designer/assets/GridMenus-CvcASlKv.js +0 -8
  263. package/dist/designer/assets/SvGrid.helpers-DE5mmORW.js +0 -1
  264. package/dist/designer/assets/SvGridCellEditor-C7NWcnNC.js +0 -4
  265. package/dist/designer/assets/SvListBox-BlmmzYcU.js +0 -110
  266. package/dist/designer/assets/index-KmSEY7Lb.css +0 -1
  267. package/dist/designer/assets/src-DqHCqori.js +0 -58
  268. package/dist/designer/assets/validate-B71dE1Cs.js +0 -1
  269. package/dist/wc/GridMenus-fWbBUiRH.js +0 -1088
  270. package/dist/wc/svelte-virtualizer.svelte-Dntdh8Ms.js +0 -581
@@ -0,0 +1,349 @@
1
+ /**
2
+ * The tables of a base and the links between them, as plain data: what
3
+ * `<SvBase tables>` keeps, and the rules that keep a link field and the field
4
+ * that links back from the other table in step. Every function here returns
5
+ * a new list of tables and never touches a table it does not change, so the
6
+ * component can hand the list to the app after each change.
7
+ *
8
+ * A link field names the table it points at (`fieldProps.table`) and, when
9
+ * that table is in the same base, the field there that links back
10
+ * (`fieldProps.inverse`). Its value is a list of record ids.
11
+ */
12
+ import { newRecordId, uniqueId } from './field-ops.js';
13
+ export const colId = (d) => String(d.id ?? d.field ?? '');
14
+ export const rowIdOf = (t, r) => (t.getRowId ? t.getRowId(r) : String(r?.id ?? ''));
15
+ export const primaryOf = (t) => t.primaryField ?? String(t.columns[0]?.field ?? colId(t.columns[0] ?? {}));
16
+ const isLink = (d) => d?.fieldType === 'link';
17
+ const ids = (v) => (Array.isArray(v) ? v.map(String) : v == null || v === '' ? [] : [String(v)]);
18
+ function replace(tables, id, change) {
19
+ return tables.map((t) => (t.id === id ? change(t) : t));
20
+ }
21
+ function setColumn(t, id, change) {
22
+ return { ...t, columns: t.columns.map((d) => (colId(d) === id ? change(d) : d)) };
23
+ }
24
+ /** A field name free in `t`: "Projects", else "Projects 2". */
25
+ function freeHeader(t, name) {
26
+ const taken = new Set(t.columns.map((d) => String(d.header ?? '').trim().toLowerCase()));
27
+ let n = name;
28
+ for (let i = 2; taken.has(n.toLowerCase()); i++)
29
+ n = `${name} ${i}`;
30
+ return n;
31
+ }
32
+ /**
33
+ * The field that links back from `def`'s table, made when `def` points at
34
+ * another table of the base and has none: a link field in that table named
35
+ * after this one, many records per record. Both fields name each other.
36
+ */
37
+ export function addInverse(tables, tableId, fieldId) {
38
+ const from = tables.find((t) => t.id === tableId);
39
+ const def = from?.columns.find((d) => colId(d) === fieldId);
40
+ const target = tables.find((t) => t.id === def?.fieldProps?.table);
41
+ if (!from || !def || !isLink(def) || !target || target.id === from.id)
42
+ return tables;
43
+ // Its field back, when that field names it in turn (a duplicated link field
44
+ // still names the original's, and gets its own).
45
+ const have = def.fieldProps?.inverse ? target.columns.find((d) => colId(d) === def.fieldProps.inverse) : undefined;
46
+ if (have?.fieldProps?.inverse === fieldId)
47
+ return syncInverse(tables, tableId, fieldId);
48
+ const id = uniqueId(from.name, target.columns.map(colId));
49
+ const back = { id, field: id, header: freeHeader(target, from.name), width: 200, fieldType: 'link', fieldProps: { table: from.id, multiple: true, inverse: fieldId } };
50
+ let next = replace(tables, target.id, (t) => ({ ...t, columns: [...t.columns, back] }));
51
+ next = replace(next, from.id, (t) => setColumn(t, fieldId, (d) => ({ ...d, fieldProps: { ...d.fieldProps, inverse: id } })));
52
+ return syncInverse(next, tableId, fieldId);
53
+ }
54
+ /**
55
+ * Make the field that links back agree with `fieldId`'s values: a record of
56
+ * the other table lists the records that link to it. Ids it already lists
57
+ * keep their place, new ones go at the end. When the field back holds one
58
+ * record (`multiple: false`), it keeps the one that linked last, and the
59
+ * records it was linked from before let go of it, so both sides agree.
60
+ */
61
+ export function syncInverse(tables, tableId, fieldId) {
62
+ const from = tables.find((t) => t.id === tableId);
63
+ const def = from?.columns.find((d) => colId(d) === fieldId);
64
+ const target = tables.find((t) => t.id === def?.fieldProps?.table);
65
+ const backId = def?.fieldProps?.inverse;
66
+ const back = target?.columns.find((d) => colId(d) === backId);
67
+ if (from && def && target && backId && !isLink(back))
68
+ warnOnce(missingInverse(from, def, target));
69
+ if (!from || !def || !target || !back || !isLink(back) || target.id === from.id)
70
+ return tables;
71
+ const field = String(def.field ?? fieldId);
72
+ const backField = String(back.field ?? backId);
73
+ const single = back.fieldProps?.multiple === false;
74
+ // Record of this table -> the target records it lets go of.
75
+ const letGo = new Map();
76
+ // Target record id -> the records of this table that link to it, in order.
77
+ const wanted = new Map();
78
+ for (const r of from.data) {
79
+ if (r == null)
80
+ continue;
81
+ const me = rowIdOf(from, r);
82
+ for (const to of ids(r[field])) {
83
+ const list = wanted.get(to);
84
+ if (list)
85
+ list.push(me);
86
+ else
87
+ wanted.set(to, [me]);
88
+ }
89
+ }
90
+ let changed = false;
91
+ const data = target.data.map((r) => {
92
+ if (r == null)
93
+ return r;
94
+ const me = rowIdOf(target, r);
95
+ const want = wanted.get(me) ?? [];
96
+ const prev = ids(r[backField]);
97
+ const keep = new Set(want);
98
+ let next = prev.filter((x) => keep.has(x));
99
+ for (const x of want)
100
+ if (!next.includes(x))
101
+ next.push(x);
102
+ if (single && next.length > 1) {
103
+ const fresh = want.filter((x) => !prev.includes(x));
104
+ const winner = fresh.length ? fresh[fresh.length - 1] : next[0];
105
+ for (const x of next) {
106
+ if (x === winner)
107
+ continue;
108
+ const lost = letGo.get(x);
109
+ if (lost)
110
+ lost.add(me);
111
+ else
112
+ letGo.set(x, new Set([me]));
113
+ }
114
+ next = [winner];
115
+ }
116
+ if (next.length === prev.length && next.every((x, i) => x === prev[i]))
117
+ return r;
118
+ changed = true;
119
+ return { ...r, [backField]: next };
120
+ });
121
+ let out = changed ? replace(tables, target.id, (t) => ({ ...t, data })) : tables;
122
+ if (letGo.size) {
123
+ out = replace(out, from.id, (t) => ({
124
+ ...t,
125
+ data: t.data.map((r) => {
126
+ const drop = r == null ? undefined : letGo.get(rowIdOf(t, r));
127
+ return drop ? { ...r, [field]: ids(r[field]).filter((x) => !drop.has(x)) } : r;
128
+ }),
129
+ }));
130
+ }
131
+ return out;
132
+ }
133
+ const warned = new Set();
134
+ function warnOnce(message) {
135
+ if (warned.has(message))
136
+ return;
137
+ warned.add(message);
138
+ console.warn(message);
139
+ }
140
+ const missingInverse = (from, def, target) => `[SvBase] Link field "${colId(def)}" of table "${from.id}" names "${def.fieldProps?.inverse}" as its field back (fieldProps.inverse), ` +
141
+ `but table "${target.id}" has no link field with that id. Links made on either side will not reach the other.`;
142
+ /**
143
+ * What is wrong with the links of the tables as loaded: an `inverse` that
144
+ * names no link field, and two sides of a link that disagree. The base does
145
+ * not rewrite loaded data, so it reports instead (once per message, in the
146
+ * console; SvBase calls this when it mounts).
147
+ */
148
+ export function checkLinks(tables, warn = warnOnce) {
149
+ const out = [];
150
+ const seen = new Set();
151
+ for (const t of tables) {
152
+ for (const d of t.columns) {
153
+ if (!isLink(d) || !d.fieldProps?.inverse)
154
+ continue;
155
+ const target = tables.find((x) => x.id === d.fieldProps.table);
156
+ if (!target || target.id === t.id)
157
+ continue;
158
+ const back = target.columns.find((x) => colId(x) === d.fieldProps.inverse);
159
+ if (!isLink(back)) {
160
+ out.push(missingInverse(t, d, target));
161
+ continue;
162
+ }
163
+ const pair = [`${t.id}.${colId(d)}`, `${target.id}.${colId(back)}`].sort().join('|');
164
+ if (seen.has(pair) || syncInverse(tables, t.id, colId(d)) === tables)
165
+ continue;
166
+ seen.add(pair);
167
+ out.push(`[SvBase] The two sides of the link "${colId(d)}" (table "${t.id}") and "${colId(back)}" (table "${target.id}") disagree in the loaded data. ` +
168
+ 'The base does not fix loaded data: the first link edit, or a record added or deleted, rebuilds the other side from the table where it happened.');
169
+ }
170
+ }
171
+ for (const m of out)
172
+ warn(m);
173
+ return out;
174
+ }
175
+ /** Every link field of a table brings its other side up to date (after records are added or deleted). */
176
+ export function syncAllInverses(tables, tableId) {
177
+ const from = tables.find((t) => t.id === tableId);
178
+ let next = tables;
179
+ for (const d of from?.columns ?? [])
180
+ if (isLink(d) && d.fieldProps?.inverse)
181
+ next = syncInverse(next, tableId, colId(d));
182
+ return next;
183
+ }
184
+ /** Remove the field that links back from `def` (its column and its values). */
185
+ export function dropInverse(tables, def) {
186
+ if (!isLink(def) || !def?.fieldProps?.inverse)
187
+ return tables;
188
+ const target = tables.find((t) => t.id === def.fieldProps.table);
189
+ const back = target?.columns.find((d) => colId(d) === def.fieldProps.inverse);
190
+ if (!target || !back || back.fieldProps?.table == null)
191
+ return tables;
192
+ const backField = String(back.field ?? colId(back));
193
+ return replace(tables, target.id, (t) => ({
194
+ ...t,
195
+ columns: t.columns.filter((d) => d !== back),
196
+ data: t.data.map((r) => {
197
+ if (r == null || !(backField in r))
198
+ return r;
199
+ const { [backField]: _drop, ...rest } = r;
200
+ return rest;
201
+ }),
202
+ }));
203
+ }
204
+ /**
205
+ * A field of `tableId` changed from `prev` to its current column. A link that
206
+ * now points at another table loses the field back in the old one, its values
207
+ * (ids of the old table's records) and gets a field back in the new one; a
208
+ * link turned into another type loses the field back.
209
+ */
210
+ export function fieldChanged(tables, tableId, prev, fieldId) {
211
+ const table = tables.find((t) => t.id === tableId);
212
+ const now = table?.columns.find((d) => colId(d) === fieldId);
213
+ if (!table || !now)
214
+ return tables;
215
+ const wasLink = isLink(prev);
216
+ const target = now.fieldProps?.table;
217
+ const moved = wasLink && isLink(now) && prev.fieldProps?.table !== target;
218
+ let next = tables;
219
+ if (wasLink && (!isLink(now) || moved)) {
220
+ next = dropInverse(next, prev);
221
+ next = replace(next, tableId, (t) => setColumn(t, fieldId, (d) => {
222
+ const { inverse: _gone, ...fieldProps } = d.fieldProps ?? {};
223
+ return { ...d, fieldProps };
224
+ }));
225
+ }
226
+ if (moved) {
227
+ const field = String(now.field ?? fieldId);
228
+ next = replace(next, tableId, (t) => ({ ...t, data: t.data.map((r) => (r == null ? r : { ...r, [field]: [] })) }));
229
+ }
230
+ // A link to one record: each record keeps the first id it holds, and the
231
+ // field back (synced below) lets go of the rest.
232
+ if (isLink(now) && now.fieldProps?.multiple === false) {
233
+ const field = String(now.field ?? fieldId);
234
+ next = replace(next, tableId, (t) => {
235
+ if (!t.data.some((r) => ids(r?.[field]).length > 1))
236
+ return t;
237
+ return { ...t, data: t.data.map((r) => (r != null && ids(r[field]).length > 1 ? { ...r, [field]: ids(r[field]).slice(0, 1) } : r)) };
238
+ });
239
+ }
240
+ if (isLink(now))
241
+ next = addInverse(next, tableId, fieldId);
242
+ return next;
243
+ }
244
+ const READERS = new Set(['lookup', 'rollup', 'count']);
245
+ /** The lookups, rollups and counts of a table (or a grid's columns) that read its link field `link`. */
246
+ export function linkReaders(t, link) {
247
+ const names = new Set([colId(link), String(link.field ?? '')].filter(Boolean));
248
+ return t.columns.filter((d) => READERS.has(d.fieldType) && names.has(String(d.fieldProps?.linkField ?? '')));
249
+ }
250
+ function dropColumns(t, gone) {
251
+ return gone.length ? { ...t, columns: t.columns.filter((d) => !gone.includes(d)) } : t;
252
+ }
253
+ /**
254
+ * Link field `def` of `tableId` was deleted (it is gone from the columns
255
+ * already): the lookups, rollups and counts that read it go too, and so do
256
+ * its field back, with its values, and the ones that read that.
257
+ */
258
+ export function removeLinkField(tables, tableId, def) {
259
+ if (!isLink(def))
260
+ return tables;
261
+ let next = replace(tables, tableId, (t) => dropColumns(t, linkReaders(t, def)));
262
+ const target = next.find((t) => t.id === def.fieldProps?.table);
263
+ const back = def.fieldProps?.inverse ? target?.columns.find((d) => colId(d) === def.fieldProps.inverse) : undefined;
264
+ if (target && isLink(back) && target.id !== tableId)
265
+ next = replace(next, target.id, (t) => dropColumns(t, linkReaders(t, back)));
266
+ return dropInverse(next, def);
267
+ }
268
+ /**
269
+ * Delete a table, and every link field of another table that points at it
270
+ * (with its values), with the lookups, rollups and counts that read those.
271
+ */
272
+ export function removeTable(tables, tableId) {
273
+ return tables
274
+ .filter((t) => t.id !== tableId)
275
+ .map((t) => {
276
+ const links = t.columns.filter((d) => isLink(d) && d.fieldProps?.table === tableId);
277
+ if (!links.length)
278
+ return t;
279
+ const gone = [...links, ...links.flatMap((d) => linkReaders(t, d))];
280
+ const fields = new Set(gone.map((d) => String(d.field ?? colId(d))));
281
+ return {
282
+ ...t,
283
+ columns: t.columns.filter((d) => !gone.includes(d)),
284
+ data: t.data.map((r) => {
285
+ if (r == null)
286
+ return r;
287
+ const out = {};
288
+ for (const [k, v] of Object.entries(r))
289
+ if (!fields.has(k))
290
+ out[k] = v;
291
+ return out;
292
+ }),
293
+ };
294
+ });
295
+ }
296
+ // A new record id; it lives with the other field operations, which give one to every new record.
297
+ export { newRecordId } from './field-ops.js';
298
+ /** A table name not used yet: "Table 2", "Table 3". */
299
+ export function freeTableName(tables, base = 'Table') {
300
+ const taken = new Set(tables.map((t) => t.name.trim().toLowerCase()));
301
+ let n = 1;
302
+ while (taken.has((n === 1 ? base : `${base} ${n}`).toLowerCase()))
303
+ n += 1;
304
+ return n === 1 ? base : `${base} ${n}`;
305
+ }
306
+ /** A new table as a base starts one: Name, Notes and Status, three empty records, a grid view. */
307
+ export function blankTable(tables, name = freeTableName(tables, 'Table')) {
308
+ const id = uniqueId(name, tables.map((t) => t.id));
309
+ return {
310
+ id,
311
+ name,
312
+ primaryField: 'name',
313
+ columns: [
314
+ { id: 'name', field: 'name', header: 'Name', width: 220, fieldType: 'text' },
315
+ { id: 'notes', field: 'notes', header: 'Notes', width: 260, fieldType: 'longText' },
316
+ {
317
+ id: 'status', field: 'status', header: 'Status', width: 140, fieldType: 'singleSelect',
318
+ fieldProps: { options: [{ value: 'Todo', color: '#94a3b8' }, { value: 'In progress', color: '#2d7ff9' }, { value: 'Done', color: '#20c933' }] },
319
+ },
320
+ ],
321
+ data: [0, 1, 2].map(() => ({ id: newRecordId() })),
322
+ views: [{ id: 'grid', name: 'Grid view', type: 'grid' }],
323
+ };
324
+ }
325
+ /**
326
+ * A copy of a table: its fields, records and views, under a new name. Its
327
+ * link fields point at the same records; addInverse gives each one a field
328
+ * back once the copy is in the base.
329
+ */
330
+ export function copyTable(tables, source) {
331
+ const name = freeTableName(tables, `${source.name} copy`);
332
+ const data = source.data.map((r) => ({ ...r, id: newRecordId() }));
333
+ return {
334
+ ...source,
335
+ id: uniqueId(name, tables.map((t) => t.id)),
336
+ name,
337
+ getRowId: undefined,
338
+ // The copy lives in the page: it is not the database table it came from.
339
+ source: undefined,
340
+ columns: source.columns.map((d) => {
341
+ if (!isLink(d) || !d.fieldProps?.inverse)
342
+ return d;
343
+ const { inverse: _theirs, ...fieldProps } = d.fieldProps;
344
+ return { ...d, fieldProps };
345
+ }),
346
+ data,
347
+ views: source.views?.map((v) => ({ ...v })),
348
+ };
349
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The field types `enableFields()` registers, with every option the field
3
+ * editor offers for each. Most map onto what the grid already draws and edits
4
+ * (an editor, a formatter, chips); some bring a cell of their own (checkbox
5
+ * icons, rating, percent bar, people, buttons, links). Every `fill` keeps what
6
+ * the column set itself, so `{ fieldType: 'number', align: 'left' }` stays left.
7
+ *
8
+ * Computed types (formula, lookup, rollup, count) give the column a `fieldFn`
9
+ * and make it read-only; automatic ones (autonumber, created / modified time
10
+ * and by) are written by the module and read-only too.
11
+ */
12
+ import type { FieldTypeDef } from './registry.svelte.js';
13
+ export declare const BUILTIN_FIELD_TYPES: FieldTypeDef<any>[];