@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,499 @@
|
|
|
1
|
+
import { colId, rowIdOf, syncInverse } from './base-tables.js';
|
|
2
|
+
const isJunction = (l) => 'through' in l;
|
|
3
|
+
const ids = (v) => (Array.isArray(v) ? v.map(String) : v == null || v === '' ? [] : [String(v)]);
|
|
4
|
+
const request = (endRow, filter = {}) => ({
|
|
5
|
+
startRow: 0,
|
|
6
|
+
endRow,
|
|
7
|
+
pageIndex: 0,
|
|
8
|
+
pageSize: endRow,
|
|
9
|
+
sortModel: [],
|
|
10
|
+
filterModel: filter,
|
|
11
|
+
});
|
|
12
|
+
/**
|
|
13
|
+
* A value as the page keeps it: a date column read as a JS Date (PGlite,
|
|
14
|
+
* node-postgres) becomes 'YYYY-MM-DD', or a full ISO time when it has one.
|
|
15
|
+
*/
|
|
16
|
+
function fromDb(v) {
|
|
17
|
+
if (v instanceof Date) {
|
|
18
|
+
const iso = v.toISOString();
|
|
19
|
+
return iso.endsWith('T00:00:00.000Z') ? iso.slice(0, 10) : iso;
|
|
20
|
+
}
|
|
21
|
+
return v;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* A base table's source from any data source with the grid's contract
|
|
25
|
+
* (`createSupabaseDataSource`, `createSqlDataSource`, `createRestDataSource`,
|
|
26
|
+
* `createInMemoryDataSource`, or your own).
|
|
27
|
+
*
|
|
28
|
+
* ```ts
|
|
29
|
+
* source: baseSource(createSupabaseDataSource({ client, table: 'tasks', schema }), {
|
|
30
|
+
* links: { project: { column: 'project_id' } },
|
|
31
|
+
* realtime: (onChange) => createSupabaseRealtime({ client, table: 'tasks', onChange }).unsubscribe,
|
|
32
|
+
* })
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export function baseSource(ds, options = {}) {
|
|
36
|
+
const limit = options.limit ?? 10000;
|
|
37
|
+
const idField = options.idField ?? 'id';
|
|
38
|
+
const links = options.links ?? {};
|
|
39
|
+
const rename = options.columns ?? {};
|
|
40
|
+
const back = new Map(Object.entries(rename).map(([f, c]) => [c, f]));
|
|
41
|
+
const fks = new Map(Object.entries(links).filter((e) => !isJunction(e[1])));
|
|
42
|
+
const junctions = Object.entries(links).filter((e) => isJunction(e[1]));
|
|
43
|
+
const fkColumns = new Map([...fks].map(([field, l]) => [l.column, field]));
|
|
44
|
+
/** A row as the database has it, as the page keeps it (junction links aside). */
|
|
45
|
+
function toPage(r) {
|
|
46
|
+
const row = {};
|
|
47
|
+
for (const [k, v] of Object.entries(r ?? {})) {
|
|
48
|
+
const fk = fkColumns.get(k);
|
|
49
|
+
if (fk)
|
|
50
|
+
row[fk] = v == null ? [] : [String(v)];
|
|
51
|
+
else
|
|
52
|
+
row[back.get(k) ?? k] = fromDb(v);
|
|
53
|
+
}
|
|
54
|
+
row.id = String(r?.[idField] ?? row.id ?? '');
|
|
55
|
+
return row;
|
|
56
|
+
}
|
|
57
|
+
// Junction row ids by "from|to", to delete a pair.
|
|
58
|
+
const pairs = new Map();
|
|
59
|
+
const pairKey = (a, b) => `${a}|${b}`;
|
|
60
|
+
const src = {
|
|
61
|
+
limit,
|
|
62
|
+
links,
|
|
63
|
+
skip: new Set(options.skip ?? []),
|
|
64
|
+
toPage,
|
|
65
|
+
async load() {
|
|
66
|
+
const res = await ds.getRows(request(limit));
|
|
67
|
+
const out = res.rows.map((r) => toPage(r));
|
|
68
|
+
for (const [field, j] of junctions) {
|
|
69
|
+
const seen = new Map();
|
|
70
|
+
pairs.set(field, seen);
|
|
71
|
+
const jr = await j.through.getRows(request(limit * 20));
|
|
72
|
+
const byFrom = new Map();
|
|
73
|
+
for (const p of jr.rows) {
|
|
74
|
+
const a = String(p[j.from]);
|
|
75
|
+
const b = String(p[j.to]);
|
|
76
|
+
seen.set(pairKey(a, b), String(p[j.idField ?? 'id']));
|
|
77
|
+
const list = byFrom.get(a);
|
|
78
|
+
if (list)
|
|
79
|
+
list.push(b);
|
|
80
|
+
else
|
|
81
|
+
byFrom.set(a, [b]);
|
|
82
|
+
}
|
|
83
|
+
for (const row of out)
|
|
84
|
+
row[field] = byFrom.get(String(row.id)) ?? [];
|
|
85
|
+
}
|
|
86
|
+
return out;
|
|
87
|
+
},
|
|
88
|
+
async insert(row) {
|
|
89
|
+
const { stored, later } = split(row);
|
|
90
|
+
const created = (await ds.createRow(stored));
|
|
91
|
+
const id = String(created?.[idField] ?? row.id);
|
|
92
|
+
for (const [field, to] of later)
|
|
93
|
+
await addPairs(field, id, to);
|
|
94
|
+
return { ...row, id };
|
|
95
|
+
},
|
|
96
|
+
async update(id, patch, before) {
|
|
97
|
+
const { stored, later } = split(patch);
|
|
98
|
+
if (Object.keys(stored).length)
|
|
99
|
+
await ds.updateRow(id, stored);
|
|
100
|
+
for (const [field, to] of later) {
|
|
101
|
+
const was = new Set(ids(before[field]));
|
|
102
|
+
const now = new Set(to);
|
|
103
|
+
await addPairs(field, id, to.filter((x) => !was.has(x)));
|
|
104
|
+
await dropPairs(field, id, [...was].filter((x) => !now.has(x)));
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
async remove(id, before) {
|
|
108
|
+
for (const [field] of junctions)
|
|
109
|
+
await dropPairs(field, id, ids(before[field]));
|
|
110
|
+
await ds.deleteRow(id);
|
|
111
|
+
},
|
|
112
|
+
...(options.realtime
|
|
113
|
+
? {
|
|
114
|
+
subscribe: (onChange) => {
|
|
115
|
+
const stop = options.realtime(onChange);
|
|
116
|
+
return typeof stop === 'function' ? stop : () => stop.unsubscribe();
|
|
117
|
+
},
|
|
118
|
+
}
|
|
119
|
+
: {}),
|
|
120
|
+
};
|
|
121
|
+
/** A record's fields as columns, and its junction links to write after it. */
|
|
122
|
+
function split(row) {
|
|
123
|
+
const stored = {};
|
|
124
|
+
const later = [];
|
|
125
|
+
for (const [k, v] of Object.entries(row)) {
|
|
126
|
+
const link = links[k];
|
|
127
|
+
if (link && isJunction(link))
|
|
128
|
+
later.push([k, ids(v)]);
|
|
129
|
+
else if (link) {
|
|
130
|
+
const first = ids(v)[0];
|
|
131
|
+
stored[link.column] = first == null ? null : linkedId(k, first);
|
|
132
|
+
}
|
|
133
|
+
else if (k === 'id') {
|
|
134
|
+
if (!options.serverIds)
|
|
135
|
+
stored[idField] = v;
|
|
136
|
+
}
|
|
137
|
+
else
|
|
138
|
+
stored[rename[k] ?? k] = v;
|
|
139
|
+
}
|
|
140
|
+
return { stored, later };
|
|
141
|
+
}
|
|
142
|
+
// A linked record's database id. The writer sets `ids` and knows the table.
|
|
143
|
+
const linkedTable = new Map();
|
|
144
|
+
function linkedId(field, id) {
|
|
145
|
+
const table = linkedTable.get(field);
|
|
146
|
+
return table && src.ids ? src.ids.toServer(table, id) : id;
|
|
147
|
+
}
|
|
148
|
+
async function addPairs(field, from, to) {
|
|
149
|
+
const j = links[field];
|
|
150
|
+
const seen = pairs.get(field) ?? new Map();
|
|
151
|
+
pairs.set(field, seen);
|
|
152
|
+
for (const x of to) {
|
|
153
|
+
const b = linkedId(field, x);
|
|
154
|
+
const made = (await j.through.createRow({ [j.from]: from, [j.to]: b }));
|
|
155
|
+
seen.set(pairKey(from, b), String(made?.[j.idField ?? 'id'] ?? ''));
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
async function dropPairs(field, from, to) {
|
|
159
|
+
const j = links[field];
|
|
160
|
+
const seen = pairs.get(field);
|
|
161
|
+
for (const x of to) {
|
|
162
|
+
const b = linkedId(field, x);
|
|
163
|
+
const pid = seen?.get(pairKey(from, b));
|
|
164
|
+
if (!pid)
|
|
165
|
+
continue;
|
|
166
|
+
await j.through.deleteRow(pid);
|
|
167
|
+
seen.delete(pairKey(from, b));
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
// The writer tells the source which table each link field points at.
|
|
171
|
+
;
|
|
172
|
+
src.linkTables = linkedTable;
|
|
173
|
+
return src;
|
|
174
|
+
}
|
|
175
|
+
// ---- The writer -------------------------------------------------------------
|
|
176
|
+
/** Field types worked out in the page: never written. */
|
|
177
|
+
const COMPUTED = new Set(['formula', 'lookup', 'rollup', 'count', 'button']);
|
|
178
|
+
const clone = (r) => {
|
|
179
|
+
const out = {};
|
|
180
|
+
for (const [k, v] of Object.entries(r))
|
|
181
|
+
out[k] = Array.isArray(v) ? [...v] : v && typeof v === 'object' ? JSON.parse(JSON.stringify(v)) : v;
|
|
182
|
+
return out;
|
|
183
|
+
};
|
|
184
|
+
const same = (a, b) => {
|
|
185
|
+
if (a === b)
|
|
186
|
+
return true;
|
|
187
|
+
if ((a == null || a === '') && (b == null || b === ''))
|
|
188
|
+
return true;
|
|
189
|
+
if (Array.isArray(a) || Array.isArray(b) || (a && typeof a === 'object') || (b && typeof b === 'object'))
|
|
190
|
+
return JSON.stringify(a ?? []) === JSON.stringify(b ?? []);
|
|
191
|
+
return false;
|
|
192
|
+
};
|
|
193
|
+
/** Whether a link field is the one worked out from the other side (that side stores the pairs). */
|
|
194
|
+
function derivedLink(tables, def) {
|
|
195
|
+
const inverse = def.fieldProps?.inverse;
|
|
196
|
+
if (!inverse)
|
|
197
|
+
return false;
|
|
198
|
+
const other = tables.find((t) => t.id === def.fieldProps?.table);
|
|
199
|
+
const otherDef = other?.columns.find((d) => colId(d) === inverse);
|
|
200
|
+
const otherField = otherDef ? String(otherDef.field ?? colId(otherDef)) : '';
|
|
201
|
+
return !!other?.source?.links?.[otherField];
|
|
202
|
+
}
|
|
203
|
+
/** The fields of a table its source writes. */
|
|
204
|
+
function storedFields(tables, t) {
|
|
205
|
+
const src = t.source;
|
|
206
|
+
return t.columns
|
|
207
|
+
.filter((d) => d.field != null && !COMPUTED.has(d.fieldType) && !src.skip.has(String(d.field)))
|
|
208
|
+
.filter((d) => !(d.fieldType === 'link' && !src.links[String(d.field)] && derivedLink(tables, d)))
|
|
209
|
+
.map((d) => String(d.field));
|
|
210
|
+
}
|
|
211
|
+
export function createBaseSync(options) {
|
|
212
|
+
// What the database holds, per table, by the record's page id.
|
|
213
|
+
const synced = new Map();
|
|
214
|
+
// Page id <-> database id, per table, for records the database gave its own id.
|
|
215
|
+
const toServerIds = new Map();
|
|
216
|
+
const toLocalIds = new Map();
|
|
217
|
+
const pendingInsert = new Set();
|
|
218
|
+
const stops = [];
|
|
219
|
+
let queue = Promise.resolve();
|
|
220
|
+
let pending = 0;
|
|
221
|
+
// The last failed write: the status keeps saying so until a write goes through.
|
|
222
|
+
let failure = null;
|
|
223
|
+
let disposed = false;
|
|
224
|
+
const status = (s) => options.onStatus?.(s);
|
|
225
|
+
const ids = {
|
|
226
|
+
toServer: (table, id) => toServerIds.get(table)?.get(id) ?? id,
|
|
227
|
+
toLocal: (table, id) => toLocalIds.get(table)?.get(id) ?? id,
|
|
228
|
+
};
|
|
229
|
+
const key = (table, id) => `${table}:${id}`;
|
|
230
|
+
function wire(t) {
|
|
231
|
+
const src = t.source;
|
|
232
|
+
src.ids = ids;
|
|
233
|
+
const linkTables = src.linkTables;
|
|
234
|
+
if (linkTables)
|
|
235
|
+
for (const d of t.columns)
|
|
236
|
+
if (d.fieldType === 'link' && d.fieldProps?.table)
|
|
237
|
+
linkTables.set(String(d.field), String(d.fieldProps.table));
|
|
238
|
+
}
|
|
239
|
+
/** Records read from a source, with database ids turned into page ids. */
|
|
240
|
+
async function read(t) {
|
|
241
|
+
const rows = await t.source.load();
|
|
242
|
+
const linkTo = new Map(t.columns.filter((d) => d.fieldType === 'link').map((d) => [String(d.field), String(d.fieldProps?.table ?? '')]));
|
|
243
|
+
return rows.map((r) => {
|
|
244
|
+
const row = { ...r, id: ids.toLocal(t.id, String(r.id)) };
|
|
245
|
+
for (const [field, table] of linkTo)
|
|
246
|
+
if (field in row && Array.isArray(row[field]))
|
|
247
|
+
row[field] = row[field].map((x) => ids.toLocal(table, x));
|
|
248
|
+
return row;
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
/** Fill in the fields that link back to a table from its rows. */
|
|
252
|
+
function linkBack(list, tableId) {
|
|
253
|
+
let next = list;
|
|
254
|
+
const t = next.find((x) => x.id === tableId);
|
|
255
|
+
for (const d of t?.columns ?? [])
|
|
256
|
+
if (d.fieldType === 'link' && d.fieldProps?.inverse && t?.source?.links[String(d.field)])
|
|
257
|
+
next = syncInverse(next, tableId, colId(d));
|
|
258
|
+
return next;
|
|
259
|
+
}
|
|
260
|
+
/** Load every table that has a source, then fill in the fields that link back. */
|
|
261
|
+
async function load() {
|
|
262
|
+
const list = options.getTables() ?? [];
|
|
263
|
+
const withSource = list.filter((t) => t.source);
|
|
264
|
+
if (!withSource.length)
|
|
265
|
+
return;
|
|
266
|
+
status({ state: 'loading' });
|
|
267
|
+
for (const t of withSource)
|
|
268
|
+
wire(t);
|
|
269
|
+
const loaded = new Map();
|
|
270
|
+
for (const t of withSource) {
|
|
271
|
+
try {
|
|
272
|
+
const rows = await read(t);
|
|
273
|
+
loaded.set(t.id, rows);
|
|
274
|
+
if (rows.length >= t.source.limit)
|
|
275
|
+
status({ state: 'limited', table: t.name, limit: t.source.limit });
|
|
276
|
+
}
|
|
277
|
+
catch (e) {
|
|
278
|
+
failure = report(t, 'load', e);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
if (disposed)
|
|
282
|
+
return;
|
|
283
|
+
let next = (options.getTables() ?? []).map((t) => (loaded.has(t.id) ? { ...t, data: loaded.get(t.id) } : t));
|
|
284
|
+
for (const t of withSource)
|
|
285
|
+
next = linkBack(next, t.id);
|
|
286
|
+
for (const t of next)
|
|
287
|
+
if (loaded.has(t.id))
|
|
288
|
+
remember(next, t);
|
|
289
|
+
options.apply(next);
|
|
290
|
+
for (const t of withSource) {
|
|
291
|
+
const stop = t.source.subscribe?.(() => refresh(t.id));
|
|
292
|
+
if (stop)
|
|
293
|
+
stops.push(stop);
|
|
294
|
+
}
|
|
295
|
+
if (pending === 0)
|
|
296
|
+
status(failure ?? { state: 'idle' });
|
|
297
|
+
}
|
|
298
|
+
function remember(list, t) {
|
|
299
|
+
const fields = storedFields(list, t);
|
|
300
|
+
const map = new Map();
|
|
301
|
+
for (const r of t.data)
|
|
302
|
+
if (r)
|
|
303
|
+
map.set(rowIdOf(t, r), pick(r, fields));
|
|
304
|
+
synced.set(t.id, map);
|
|
305
|
+
}
|
|
306
|
+
const pick = (r, fields) => {
|
|
307
|
+
const out = {};
|
|
308
|
+
for (const f of fields)
|
|
309
|
+
if (f in r)
|
|
310
|
+
out[f] = Array.isArray(r[f]) ? [...r[f]] : r[f] && typeof r[f] === 'object' ? clone({ v: r[f] }).v : r[f];
|
|
311
|
+
return out;
|
|
312
|
+
};
|
|
313
|
+
/** Compare the tables with what the database holds, and queue what differs. */
|
|
314
|
+
function push(list) {
|
|
315
|
+
if (!list || disposed)
|
|
316
|
+
return;
|
|
317
|
+
for (const t of list) {
|
|
318
|
+
const before = synced.get(t.id);
|
|
319
|
+
if (!t.source || !before)
|
|
320
|
+
continue;
|
|
321
|
+
const fields = storedFields(list, t);
|
|
322
|
+
const seen = new Set();
|
|
323
|
+
for (const r of t.data) {
|
|
324
|
+
if (!r)
|
|
325
|
+
continue;
|
|
326
|
+
const id = rowIdOf(t, r);
|
|
327
|
+
seen.add(id);
|
|
328
|
+
const was = before.get(id);
|
|
329
|
+
if (!was) {
|
|
330
|
+
if (pendingInsert.has(key(t.id, id)))
|
|
331
|
+
continue;
|
|
332
|
+
const row = pick(r, fields);
|
|
333
|
+
before.set(id, row);
|
|
334
|
+
pendingInsert.add(key(t.id, id));
|
|
335
|
+
enqueue(t, 'insert', id, async () => {
|
|
336
|
+
const stored = await t.source.insert({ ...row, id });
|
|
337
|
+
const dbId = String(stored.id ?? id);
|
|
338
|
+
if (dbId !== id) {
|
|
339
|
+
const out = toServerIds.get(t.id) ?? new Map();
|
|
340
|
+
out.set(id, dbId);
|
|
341
|
+
toServerIds.set(t.id, out);
|
|
342
|
+
const inn = toLocalIds.get(t.id) ?? new Map();
|
|
343
|
+
inn.set(dbId, id);
|
|
344
|
+
toLocalIds.set(t.id, inn);
|
|
345
|
+
}
|
|
346
|
+
pendingInsert.delete(key(t.id, id));
|
|
347
|
+
}, () => {
|
|
348
|
+
pendingInsert.delete(key(t.id, id));
|
|
349
|
+
before.delete(id);
|
|
350
|
+
undo(t.id, (rows) => rows.filter((x) => x && rowIdOf(t, x) !== id));
|
|
351
|
+
});
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
const patch = {};
|
|
355
|
+
for (const f of fields)
|
|
356
|
+
if (!same(r[f], was[f]))
|
|
357
|
+
patch[f] = Array.isArray(r[f]) ? [...r[f]] : r[f];
|
|
358
|
+
if (!Object.keys(patch).length)
|
|
359
|
+
continue;
|
|
360
|
+
const prev = { ...was };
|
|
361
|
+
Object.assign(was, pick(patch, Object.keys(patch)));
|
|
362
|
+
enqueue(t, 'update', id, () => t.source.update(ids.toServer(t.id, id), patch, prev), () => {
|
|
363
|
+
Object.assign(was, prev);
|
|
364
|
+
undo(t.id, (rows) => rows.map((x) => (x && rowIdOf(t, x) === id ? { ...x, ...pick(prev, Object.keys(patch)) } : x)));
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
for (const [id, was] of [...before]) {
|
|
368
|
+
if (seen.has(id))
|
|
369
|
+
continue;
|
|
370
|
+
before.delete(id);
|
|
371
|
+
enqueue(t, 'delete', id, () => t.source.remove(ids.toServer(t.id, id), was), () => {
|
|
372
|
+
before.set(id, was);
|
|
373
|
+
undo(t.id, (rows) => [...rows, { ...was, id }]);
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
function enqueue(t, action, _id, run, rollback) {
|
|
379
|
+
pending += 1;
|
|
380
|
+
status({ state: 'saving', pending });
|
|
381
|
+
queue = queue.then(async () => {
|
|
382
|
+
if (disposed)
|
|
383
|
+
return;
|
|
384
|
+
try {
|
|
385
|
+
await run();
|
|
386
|
+
failure = null;
|
|
387
|
+
}
|
|
388
|
+
catch (e) {
|
|
389
|
+
rollback();
|
|
390
|
+
failure = report(t, action, e);
|
|
391
|
+
}
|
|
392
|
+
finally {
|
|
393
|
+
pending -= 1;
|
|
394
|
+
if (pending === 0 && !disposed)
|
|
395
|
+
status(failure ?? { state: 'idle' });
|
|
396
|
+
else if (!disposed)
|
|
397
|
+
status({ state: 'saving', pending });
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
function report(t, action, e) {
|
|
402
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
403
|
+
const s = { state: 'error', message, table: t.name };
|
|
404
|
+
status(s);
|
|
405
|
+
options.onError?.({ table: t.name, action, message });
|
|
406
|
+
return s;
|
|
407
|
+
}
|
|
408
|
+
/** Put a table's rows back as the database has them, without writing. */
|
|
409
|
+
function undo(tableId, change) {
|
|
410
|
+
const list = options.getTables();
|
|
411
|
+
if (!list)
|
|
412
|
+
return;
|
|
413
|
+
let next = list.map((t) => (t.id === tableId ? { ...t, data: change(t.data) } : t));
|
|
414
|
+
next = linkBack(next, tableId);
|
|
415
|
+
options.apply(next);
|
|
416
|
+
}
|
|
417
|
+
// Changes made elsewhere: read the table again and take what moved.
|
|
418
|
+
const timers = new Map();
|
|
419
|
+
function refresh(tableId) {
|
|
420
|
+
clearTimeout(timers.get(tableId));
|
|
421
|
+
timers.set(tableId, setTimeout(() => void reload(tableId), 120));
|
|
422
|
+
}
|
|
423
|
+
async function reload(tableId) {
|
|
424
|
+
const t0 = options.getTables()?.find((t) => t.id === tableId);
|
|
425
|
+
if (!t0?.source || disposed)
|
|
426
|
+
return;
|
|
427
|
+
// Our own writes first: what they changed is already in the page.
|
|
428
|
+
await queue;
|
|
429
|
+
let rows;
|
|
430
|
+
try {
|
|
431
|
+
rows = await read(t0);
|
|
432
|
+
}
|
|
433
|
+
catch (e) {
|
|
434
|
+
report(t0, 'load', e);
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
const list = options.getTables();
|
|
438
|
+
const t = list?.find((x) => x.id === tableId);
|
|
439
|
+
const before = synced.get(tableId);
|
|
440
|
+
if (!list || !t || !before || disposed)
|
|
441
|
+
return;
|
|
442
|
+
const fields = storedFields(list, t);
|
|
443
|
+
const fresh = new Map(rows.map((r) => [String(r.id), r]));
|
|
444
|
+
let changed = false;
|
|
445
|
+
const data = [];
|
|
446
|
+
for (const r of t.data) {
|
|
447
|
+
if (!r)
|
|
448
|
+
continue;
|
|
449
|
+
const id = rowIdOf(t, r);
|
|
450
|
+
const server = fresh.get(id);
|
|
451
|
+
if (!server) {
|
|
452
|
+
if (pendingInsert.has(key(tableId, id)))
|
|
453
|
+
data.push(r);
|
|
454
|
+
else {
|
|
455
|
+
before.delete(id);
|
|
456
|
+
changed = true;
|
|
457
|
+
}
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
fresh.delete(id);
|
|
461
|
+
const moved = fields.filter((f) => f in server && !same(server[f], before.get(id)?.[f]));
|
|
462
|
+
if (moved.length) {
|
|
463
|
+
changed = true;
|
|
464
|
+
const next = { ...r };
|
|
465
|
+
for (const f of moved)
|
|
466
|
+
next[f] = server[f];
|
|
467
|
+
before.set(id, { ...before.get(id), ...pick(server, moved) });
|
|
468
|
+
data.push(next);
|
|
469
|
+
}
|
|
470
|
+
else
|
|
471
|
+
data.push(r);
|
|
472
|
+
}
|
|
473
|
+
for (const [id, r] of fresh) {
|
|
474
|
+
changed = true;
|
|
475
|
+
before.set(id, pick(r, fields));
|
|
476
|
+
data.push(r);
|
|
477
|
+
}
|
|
478
|
+
if (!changed)
|
|
479
|
+
return;
|
|
480
|
+
let next = list.map((x) => (x.id === tableId ? { ...x, data } : x));
|
|
481
|
+
next = linkBack(next, tableId);
|
|
482
|
+
options.apply(next);
|
|
483
|
+
}
|
|
484
|
+
return {
|
|
485
|
+
load,
|
|
486
|
+
push,
|
|
487
|
+
/** Read a table again now (after a change you know of). */
|
|
488
|
+
refresh,
|
|
489
|
+
/** Resolves when every queued write has finished. */
|
|
490
|
+
settled: () => queue,
|
|
491
|
+
dispose() {
|
|
492
|
+
disposed = true;
|
|
493
|
+
for (const stop of stops)
|
|
494
|
+
stop();
|
|
495
|
+
for (const t of timers.values())
|
|
496
|
+
clearTimeout(t);
|
|
497
|
+
},
|
|
498
|
+
};
|
|
499
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { BaseAutomation } from './automations.svelte.js';
|
|
2
|
+
import type { BaseSource } from './base-sync.js';
|
|
3
|
+
import type { FieldView } from './view-model.js';
|
|
4
|
+
type Row = Record<string, unknown>;
|
|
5
|
+
type Def = Record<string, any>;
|
|
6
|
+
/** One table of a base. */
|
|
7
|
+
export type BaseTable = {
|
|
8
|
+
/** Unique across the app: link fields name it in `fieldProps.table`. */
|
|
9
|
+
id: string;
|
|
10
|
+
name: string;
|
|
11
|
+
data: Row[];
|
|
12
|
+
/** Columns declared by field type (`fieldType`, `fieldProps`). */
|
|
13
|
+
columns: Def[];
|
|
14
|
+
views?: FieldView[];
|
|
15
|
+
/** The field that names a record. Default: the first column. */
|
|
16
|
+
primaryField?: string;
|
|
17
|
+
/** A record's id. Default: its `id` property. */
|
|
18
|
+
getRowId?: (row: Row) => string;
|
|
19
|
+
/**
|
|
20
|
+
* Where the records live: a database or an API (`baseSource`). The table
|
|
21
|
+
* loads from it and writes every change back.
|
|
22
|
+
*/
|
|
23
|
+
source?: BaseSource;
|
|
24
|
+
/** What happens when records are added, changed or match a condition (the view bar's Automations). */
|
|
25
|
+
automations?: BaseAutomation[];
|
|
26
|
+
/** Grid props for this table only (they win over the ones on `<SvBase>`). */
|
|
27
|
+
props?: Record<string, unknown>;
|
|
28
|
+
};
|
|
29
|
+
export declare const colId: (d: Def) => string;
|
|
30
|
+
export declare const rowIdOf: (t: BaseTable, r: Row) => string;
|
|
31
|
+
export declare const primaryOf: (t: BaseTable) => string;
|
|
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 declare function addInverse(tables: BaseTable[], tableId: string, fieldId: string): BaseTable[];
|
|
38
|
+
/**
|
|
39
|
+
* Make the field that links back agree with `fieldId`'s values: a record of
|
|
40
|
+
* the other table lists the records that link to it. Ids it already lists
|
|
41
|
+
* keep their place, new ones go at the end. When the field back holds one
|
|
42
|
+
* record (`multiple: false`), it keeps the one that linked last, and the
|
|
43
|
+
* records it was linked from before let go of it, so both sides agree.
|
|
44
|
+
*/
|
|
45
|
+
export declare function syncInverse(tables: BaseTable[], tableId: string, fieldId: string): BaseTable[];
|
|
46
|
+
/**
|
|
47
|
+
* What is wrong with the links of the tables as loaded: an `inverse` that
|
|
48
|
+
* names no link field, and two sides of a link that disagree. The base does
|
|
49
|
+
* not rewrite loaded data, so it reports instead (once per message, in the
|
|
50
|
+
* console; SvBase calls this when it mounts).
|
|
51
|
+
*/
|
|
52
|
+
export declare function checkLinks(tables: BaseTable[], warn?: (message: string) => void): string[];
|
|
53
|
+
/** Every link field of a table brings its other side up to date (after records are added or deleted). */
|
|
54
|
+
export declare function syncAllInverses(tables: BaseTable[], tableId: string): BaseTable[];
|
|
55
|
+
/** Remove the field that links back from `def` (its column and its values). */
|
|
56
|
+
export declare function dropInverse(tables: BaseTable[], def: Def | undefined): BaseTable[];
|
|
57
|
+
/**
|
|
58
|
+
* A field of `tableId` changed from `prev` to its current column. A link that
|
|
59
|
+
* now points at another table loses the field back in the old one, its values
|
|
60
|
+
* (ids of the old table's records) and gets a field back in the new one; a
|
|
61
|
+
* link turned into another type loses the field back.
|
|
62
|
+
*/
|
|
63
|
+
export declare function fieldChanged(tables: BaseTable[], tableId: string, prev: Def | undefined, fieldId: string): BaseTable[];
|
|
64
|
+
/** The lookups, rollups and counts of a table (or a grid's columns) that read its link field `link`. */
|
|
65
|
+
export declare function linkReaders(t: {
|
|
66
|
+
columns: ReadonlyArray<Def>;
|
|
67
|
+
}, link: Def): Def[];
|
|
68
|
+
/**
|
|
69
|
+
* Link field `def` of `tableId` was deleted (it is gone from the columns
|
|
70
|
+
* already): the lookups, rollups and counts that read it go too, and so do
|
|
71
|
+
* its field back, with its values, and the ones that read that.
|
|
72
|
+
*/
|
|
73
|
+
export declare function removeLinkField(tables: BaseTable[], tableId: string, def: Def | undefined): BaseTable[];
|
|
74
|
+
/**
|
|
75
|
+
* Delete a table, and every link field of another table that points at it
|
|
76
|
+
* (with its values), with the lookups, rollups and counts that read those.
|
|
77
|
+
*/
|
|
78
|
+
export declare function removeTable(tables: BaseTable[], tableId: string): BaseTable[];
|
|
79
|
+
export { newRecordId } from './field-ops.js';
|
|
80
|
+
/** A table name not used yet: "Table 2", "Table 3". */
|
|
81
|
+
export declare function freeTableName(tables: ReadonlyArray<BaseTable>, base?: string): string;
|
|
82
|
+
/** A new table as a base starts one: Name, Notes and Status, three empty records, a grid view. */
|
|
83
|
+
export declare function blankTable(tables: ReadonlyArray<BaseTable>, name?: string): BaseTable;
|
|
84
|
+
/**
|
|
85
|
+
* A copy of a table: its fields, records and views, under a new name. Its
|
|
86
|
+
* link fields point at the same records; addInverse gives each one a field
|
|
87
|
+
* back once the copy is in the base.
|
|
88
|
+
*/
|
|
89
|
+
export declare function copyTable(tables: ReadonlyArray<BaseTable>, source: BaseTable): BaseTable;
|