@svgrid/enterprise 3.1.8 → 3.1.10
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.
- package/README.md +1 -0
- package/dist/SvAlertRuleEditor.svelte +16 -5
- package/dist/SvGridAlerts.svelte +8 -0
- package/dist/SvGridAlerts.svelte.d.ts +4 -0
- package/dist/SvGridBoard.svelte +69 -10
- package/dist/SvGridBulkEditDrawer.svelte +13 -5
- package/dist/SvGridGallery.svelte +286 -0
- package/dist/SvGridGallery.svelte.d.ts +30 -0
- package/dist/SvGridScheduler.svelte +33 -11
- package/dist/SvGridSelectionBar.svelte +47 -0
- package/dist/advanced-filter/expr-columns-from-grid.d.ts +1 -0
- package/dist/advanced-filter/expr-columns-from-grid.js +11 -7
- package/dist/advanced-filter-enable.d.ts +6 -0
- package/dist/advanced-filter-enable.js +8 -0
- package/dist/alerts/alert-engine.d.ts +7 -0
- package/dist/alerts/alert-engine.js +5 -2
- package/dist/alerts/condition-editor.d.ts +26 -0
- package/dist/alerts/condition-editor.js +17 -0
- package/dist/base/index.d.ts +20 -0
- package/dist/base/index.js +20 -0
- package/dist/bulk-edit.d.ts +11 -0
- package/dist/bulk-edit.js +18 -1
- package/dist/cdn/svgrid-enterprise.svelte-external.js +11197 -10639
- package/dist/designer/assets/GridFooter-Cu8_yBlQ.js +2 -0
- package/dist/designer/assets/GridMenus-BUjkkAU2.js +6 -0
- package/dist/designer/assets/PinSplitter-BgTpgh1d.css +1 -0
- package/dist/designer/assets/PinSplitter-C5AwA1xV.js +1 -0
- package/dist/designer/assets/SvGrid.helpers-CIrmIo9U.js +1 -0
- package/dist/designer/assets/SvGridCellEditor-CJxThKL_.js +4 -0
- package/dist/designer/assets/SvListBox-CS7oUAiX.js +2 -0
- package/dist/designer/assets/conditional-formatting-MDWbF8_Y.js +1 -0
- package/dist/designer/assets/grid-extension.svelte-BZ7AqyJI.js +1 -0
- package/dist/designer/assets/index-BmvG99kR.css +1 -0
- package/dist/designer/assets/{index-Obk8sI77.js → index-CJodU3tU.js} +307 -307
- package/dist/designer/assets/sparkline-C-NcnB2i.js +1 -0
- package/dist/designer/assets/src-C_U1NKOY.js +58 -0
- package/dist/designer/assets/sv-grid-scrollbar-B9N6Wuj0.js +109 -0
- package/dist/designer/assets/validate-H5u2HEQh.js +1 -0
- package/dist/designer/index.html +11 -7
- package/dist/expressions/compile.js +10 -0
- package/dist/expressions/evaluate.js +18 -0
- package/dist/expressions/expression-types.d.ts +10 -0
- package/dist/expressions/kinds.d.ts +28 -0
- package/dist/expressions/kinds.js +19 -0
- package/dist/expressions/parse.js +9 -1
- package/dist/fields/AttachmentCell.svelte +242 -0
- package/dist/fields/AttachmentCell.svelte.d.ts +5 -0
- package/dist/fields/AttachmentDialog.svelte +357 -0
- package/dist/fields/AttachmentDialog.svelte.d.ts +13 -0
- package/dist/fields/AttachmentEditor.svelte +41 -0
- package/dist/fields/AttachmentEditor.svelte.d.ts +11 -0
- package/dist/fields/AttachmentPreview.svelte +72 -0
- package/dist/fields/AttachmentPreview.svelte.d.ts +8 -0
- package/dist/fields/BarcodeCell.svelte +20 -0
- package/dist/fields/BarcodeCell.svelte.d.ts +5 -0
- package/dist/fields/BaseAutomations.svelte +558 -0
- package/dist/fields/BaseAutomations.svelte.d.ts +18 -0
- package/dist/fields/BaseChartView.svelte +47 -0
- package/dist/fields/BaseChartView.svelte.d.ts +8 -0
- package/dist/fields/BaseTableView.svelte +185 -0
- package/dist/fields/BaseTableView.svelte.d.ts +37 -0
- package/dist/fields/BaseTabs.svelte +308 -0
- package/dist/fields/BaseTabs.svelte.d.ts +21 -0
- package/dist/fields/ButtonCell.svelte +47 -0
- package/dist/fields/ButtonCell.svelte.d.ts +20 -0
- package/dist/fields/CheckboxCell.svelte +46 -0
- package/dist/fields/CheckboxCell.svelte.d.ts +14 -0
- package/dist/fields/DeleteFieldConfirm.svelte +80 -0
- package/dist/fields/DeleteFieldConfirm.svelte.d.ts +8 -0
- package/dist/fields/DeleteRecordConfirm.svelte +48 -0
- package/dist/fields/DeleteRecordConfirm.svelte.d.ts +9 -0
- package/dist/fields/FieldCell.svelte +80 -0
- package/dist/fields/FieldCell.svelte.d.ts +9 -0
- package/dist/fields/FieldEditor.svelte +321 -0
- package/dist/fields/FieldEditor.svelte.d.ts +10 -0
- package/dist/fields/FieldInput.svelte +188 -0
- package/dist/fields/FieldInput.svelte.d.ts +29 -0
- package/dist/fields/FieldsHeaderEnd.svelte +146 -0
- package/dist/fields/FieldsHeaderEnd.svelte.d.ts +6 -0
- package/dist/fields/FootRowAdder.svelte +8 -0
- package/dist/fields/FootRowAdder.svelte.d.ts +6 -0
- package/dist/fields/GroupHeader.svelte +127 -0
- package/dist/fields/GroupHeader.svelte.d.ts +24 -0
- package/dist/fields/LinkCell.svelte +46 -0
- package/dist/fields/LinkCell.svelte.d.ts +9 -0
- package/dist/fields/LinkPickerEditor.svelte +172 -0
- package/dist/fields/LinkPickerEditor.svelte.d.ts +9 -0
- package/dist/fields/LinkPreview.svelte +60 -0
- package/dist/fields/LinkPreview.svelte.d.ts +7 -0
- package/dist/fields/LinkedRecordView.svelte +94 -0
- package/dist/fields/LinkedRecordView.svelte.d.ts +9 -0
- package/dist/fields/LinkedRecordsCell.svelte +59 -0
- package/dist/fields/LinkedRecordsCell.svelte.d.ts +11 -0
- package/dist/fields/LongTextEditor.svelte +157 -0
- package/dist/fields/LongTextEditor.svelte.d.ts +11 -0
- package/dist/fields/NumberEditor.svelte +309 -0
- package/dist/fields/NumberEditor.svelte.d.ts +12 -0
- package/dist/fields/OptionsEditor.svelte +117 -0
- package/dist/fields/OptionsEditor.svelte.d.ts +11 -0
- package/dist/fields/PercentCell.svelte +21 -0
- package/dist/fields/PercentCell.svelte.d.ts +8 -0
- package/dist/fields/PropertyInput.svelte +204 -0
- package/dist/fields/PropertyInput.svelte.d.ts +11 -0
- package/dist/fields/RatingCell.svelte +75 -0
- package/dist/fields/RatingCell.svelte.d.ts +16 -0
- package/dist/fields/RatingEditor.svelte +82 -0
- package/dist/fields/RatingEditor.svelte.d.ts +9 -0
- package/dist/fields/RecordView.svelte +310 -0
- package/dist/fields/RecordView.svelte.d.ts +11 -0
- package/dist/fields/RichTextCell.svelte +30 -0
- package/dist/fields/RichTextCell.svelte.d.ts +5 -0
- package/dist/fields/RowAdder.svelte +93 -0
- package/dist/fields/RowAdder.svelte.d.ts +7 -0
- package/dist/fields/RowHeader.svelte +99 -0
- package/dist/fields/RowHeader.svelte.d.ts +11 -0
- package/dist/fields/SelectCell.svelte +35 -0
- package/dist/fields/SelectCell.svelte.d.ts +15 -0
- package/dist/fields/SelectEditor.svelte +350 -0
- package/dist/fields/SelectEditor.svelte.d.ts +17 -0
- package/dist/fields/SummaryCell.svelte +160 -0
- package/dist/fields/SummaryCell.svelte.d.ts +11 -0
- package/dist/fields/SvBase.svelte +545 -0
- package/dist/fields/SvBase.svelte.d.ts +30 -0
- package/dist/fields/SvBaseForm.svelte +295 -0
- package/dist/fields/SvBaseForm.svelte.d.ts +22 -0
- package/dist/fields/SvBaseViewBar.svelte +644 -0
- package/dist/fields/SvBaseViewBar.svelte.d.ts +26 -0
- package/dist/fields/UserCell.svelte +39 -0
- package/dist/fields/UserCell.svelte.d.ts +9 -0
- package/dist/fields/UserFace.svelte +30 -0
- package/dist/fields/UserFace.svelte.d.ts +12 -0
- package/dist/fields/activity.svelte.d.ts +50 -0
- package/dist/fields/activity.svelte.js +110 -0
- package/dist/fields/attachments.d.ts +95 -0
- package/dist/fields/attachments.js +145 -0
- package/dist/fields/automation-outbound.d.ts +46 -0
- package/dist/fields/automation-outbound.js +61 -0
- package/dist/fields/automation-server.d.ts +53 -0
- package/dist/fields/automation-server.js +333 -0
- package/dist/fields/automations.svelte.d.ts +208 -0
- package/dist/fields/automations.svelte.js +437 -0
- package/dist/fields/bar-request.svelte.d.ts +16 -0
- package/dist/fields/bar-request.svelte.js +15 -0
- package/dist/fields/base-alerts.d.ts +24 -0
- package/dist/fields/base-alerts.js +64 -0
- package/dist/fields/base-connect.d.ts +82 -0
- package/dist/fields/base-connect.js +271 -0
- package/dist/fields/base-schema.d.ts +104 -0
- package/dist/fields/base-schema.js +266 -0
- package/dist/fields/base-sql.d.ts +62 -0
- package/dist/fields/base-sql.js +251 -0
- package/dist/fields/base-sync.d.ts +142 -0
- package/dist/fields/base-sync.js +499 -0
- package/dist/fields/base-tables.d.ts +89 -0
- package/dist/fields/base-tables.js +349 -0
- package/dist/fields/builtin-types.d.ts +13 -0
- package/dist/fields/builtin-types.js +773 -0
- package/dist/fields/button.d.ts +2 -0
- package/dist/fields/button.js +17 -0
- package/dist/fields/chart-view.d.ts +35 -0
- package/dist/fields/chart-view.js +154 -0
- package/dist/fields/column-order.d.ts +6 -0
- package/dist/fields/column-order.js +12 -0
- package/dist/fields/convert.d.ts +16 -0
- package/dist/fields/convert.js +176 -0
- package/dist/fields/copy-text.d.ts +13 -0
- package/dist/fields/copy-text.js +45 -0
- package/dist/fields/editor-request.svelte.d.ts +71 -0
- package/dist/fields/editor-request.svelte.js +52 -0
- package/dist/fields/field-ops.d.ts +72 -0
- package/dist/fields/field-ops.js +334 -0
- package/dist/fields/filter-model.d.ts +84 -0
- package/dist/fields/filter-model.js +446 -0
- package/dist/fields/format.d.ts +109 -0
- package/dist/fields/format.js +288 -0
- package/dist/fields/formula.d.ts +50 -0
- package/dist/fields/formula.js +530 -0
- package/dist/fields/grid-access.d.ts +60 -0
- package/dist/fields/grid-access.js +120 -0
- package/dist/fields/index.d.ts +34 -0
- package/dist/fields/index.js +312 -0
- package/dist/fields/linked-fields.d.ts +8 -0
- package/dist/fields/linked-fields.js +40 -0
- package/dist/fields/links.d.ts +10 -0
- package/dist/fields/links.js +46 -0
- package/dist/fields/name-list.d.ts +16 -0
- package/dist/fields/name-list.js +49 -0
- package/dist/fields/palette.d.ts +47 -0
- package/dist/fields/palette.js +69 -0
- package/dist/fields/record-delete.svelte.d.ts +12 -0
- package/dist/fields/record-delete.svelte.js +5 -0
- package/dist/fields/record-menu.d.ts +9 -0
- package/dist/fields/record-menu.js +78 -0
- package/dist/fields/registry.svelte.d.ts +176 -0
- package/dist/fields/registry.svelte.js +51 -0
- package/dist/fields/rich-text.d.ts +23 -0
- package/dist/fields/rich-text.js +48 -0
- package/dist/fields/row-color-choice.svelte.d.ts +31 -0
- package/dist/fields/row-color-choice.svelte.js +25 -0
- package/dist/fields/row-colors.d.ts +36 -0
- package/dist/fields/row-colors.js +90 -0
- package/dist/fields/row-height.d.ts +28 -0
- package/dist/fields/row-height.js +50 -0
- package/dist/fields/sources.svelte.d.ts +80 -0
- package/dist/fields/sources.svelte.js +131 -0
- package/dist/fields/summaries.d.ts +18 -0
- package/dist/fields/summaries.js +138 -0
- package/dist/fields/view-model.d.ts +129 -0
- package/dist/fields/view-model.js +492 -0
- package/dist/fields/viewbar/ChartPanel.svelte +61 -0
- package/dist/fields/viewbar/ChartPanel.svelte.d.ts +9 -0
- package/dist/fields/viewbar/ColorPanel.svelte +112 -0
- package/dist/fields/viewbar/ColorPanel.svelte.d.ts +9 -0
- package/dist/fields/viewbar/ConditionValue.svelte +153 -0
- package/dist/fields/viewbar/ConditionValue.svelte.d.ts +15 -0
- package/dist/fields/viewbar/FieldIcon.svelte +18 -0
- package/dist/fields/viewbar/FieldIcon.svelte.d.ts +9 -0
- package/dist/fields/viewbar/FilterPanel.svelte +192 -0
- package/dist/fields/viewbar/FilterPanel.svelte.d.ts +13 -0
- package/dist/fields/viewbar/GanttPanel.svelte +60 -0
- package/dist/fields/viewbar/GanttPanel.svelte.d.ts +10 -0
- package/dist/fields/viewbar/HideFieldsPanel.svelte +104 -0
- package/dist/fields/viewbar/HideFieldsPanel.svelte.d.ts +7 -0
- package/dist/fields/viewbar/SortPanel.svelte +85 -0
- package/dist/fields/viewbar/SortPanel.svelte.d.ts +8 -0
- package/dist/fields/viewbar/ViewsMenu.svelte +250 -0
- package/dist/fields/viewbar/ViewsMenu.svelte.d.ts +15 -0
- package/dist/fields/viewbar/panel-focus.d.ts +3 -0
- package/dist/fields/viewbar/panel-focus.js +22 -0
- package/dist/fields/viewbar/sort-state.d.ts +20 -0
- package/dist/fields/viewbar/sort-state.js +24 -0
- package/dist/gallery.d.ts +7 -0
- package/dist/gallery.js +36 -0
- package/dist/gantt/SvGridGantt.svelte +88 -16
- package/dist/gantt/gantt-config.d.ts +28 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +6 -0
- package/dist/install.js +3 -0
- package/dist/license-core.d.ts +3 -3
- package/dist/license.js +1 -0
- package/dist/node/studio.js +105 -4
- package/dist/scheduler-config.d.ts +11 -0
- package/dist/sources/introspect-supabase.js +4 -1
- package/dist/sources/rest.js +6 -2
- package/dist/studio/ui-components.generated.js +93 -1
- package/dist/upgrade-prompt.d.ts +1 -1
- package/dist/version.d.ts +2 -2
- package/dist/version.js +2 -2
- package/dist/wc/GridFooter-f5K0aLe_.js +145 -0
- package/dist/wc/GridMenus-FtSN_8t9.js +1369 -0
- package/dist/wc/PinSplitter-D-qtDhSM.js +132 -0
- package/dist/wc/{SvGrid.helpers-zBkDTryR.js → SvGrid.helpers-B2GEKrxS.js} +84 -36
- package/dist/wc/{SvGridCellEditor-CzUKEbkI.js → SvGridCellEditor-B_cLJrsp.js} +20 -9
- package/dist/wc/conditional-formatting-BXis_oP1.js +189 -0
- package/dist/wc/grid-extension.svelte-Bufq4B4C.js +20 -0
- package/dist/wc/sv-sheet-element.js +7556 -7617
- package/dist/wc/svelte-virtualizer.svelte-ehwOupl-.js +360 -0
- package/dist/wc/{validate-43C03DLP.js → validate-BaMO8UPE.js} +1 -1
- package/package.json +14 -3
- package/dist/designer/assets/GridMenus-CvcASlKv.js +0 -8
- package/dist/designer/assets/SvGrid.helpers-DE5mmORW.js +0 -1
- package/dist/designer/assets/SvGridCellEditor-C7NWcnNC.js +0 -4
- package/dist/designer/assets/SvListBox-BlmmzYcU.js +0 -110
- package/dist/designer/assets/index-KmSEY7Lb.css +0 -1
- package/dist/designer/assets/src-DqHCqori.js +0 -58
- package/dist/designer/assets/validate-B71dE1Cs.js +0 -1
- package/dist/wc/GridMenus-fWbBUiRH.js +0 -1088
- 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>[];
|