@dxos/react-ui-table 0.8.4-main.7ace549 → 0.8.4-main.937b3ca
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/dist/lib/browser/{chunk-KNC7XYT6.mjs → chunk-MNZKOFMA.mjs} +597 -634
- package/dist/lib/browser/chunk-MNZKOFMA.mjs.map +7 -0
- package/dist/lib/browser/{chunk-5XWSB3JP.mjs → chunk-OZBLY7XM.mjs} +18 -13
- package/dist/lib/browser/chunk-OZBLY7XM.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +1096 -980
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +19 -18
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/browser/types/index.mjs +1 -1
- package/dist/lib/node-esm/{chunk-CWSGS3Y2.mjs → chunk-2ATAIIAO.mjs} +597 -633
- package/dist/lib/node-esm/chunk-2ATAIIAO.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-MKTMOY46.mjs → chunk-WNZSKIRF.mjs} +18 -13
- package/dist/lib/node-esm/chunk-WNZSKIRF.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +1096 -980
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +19 -18
- package/dist/lib/node-esm/testing/index.mjs.map +3 -3
- package/dist/lib/node-esm/types/index.mjs +1 -1
- package/dist/types/src/components/Table/ColumnActionsMenu.d.ts +1 -2
- package/dist/types/src/components/Table/ColumnActionsMenu.d.ts.map +1 -1
- package/dist/types/src/components/Table/ColumnSettings.d.ts +2 -2
- package/dist/types/src/components/Table/ColumnSettings.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.d.ts +4 -5
- package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts +2 -16
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/Relations.stories.d.ts +6 -3
- package/dist/types/src/components/Table/Relations.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/RowActionsMenu.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.d.ts +8 -8
- package/dist/types/src/components/Table/Table.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.stories.d.ts +6 -3
- package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts +1 -1
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +10 -12
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +5 -7
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts +2 -2
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TableToolbar/TableToolbar.d.ts +2 -2
- package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +22 -13
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/hooks/useAddRow.d.ts +5 -6
- package/dist/types/src/hooks/useAddRow.d.ts.map +1 -1
- package/dist/types/src/hooks/useProjectionModel.d.ts +2 -1
- package/dist/types/src/hooks/useProjectionModel.d.ts.map +1 -1
- package/dist/types/src/hooks/useTableModel.d.ts +5 -4
- package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
- package/dist/types/src/model/modal-controller.d.ts +5 -1
- package/dist/types/src/model/modal-controller.d.ts.map +1 -1
- package/dist/types/src/model/selection-model.d.ts +17 -8
- package/dist/types/src/model/selection-model.d.ts.map +1 -1
- package/dist/types/src/model/table-model.d.ts +97 -16
- package/dist/types/src/model/table-model.d.ts.map +1 -1
- package/dist/types/src/model/table-presentation.d.ts +3 -1
- package/dist/types/src/model/table-presentation.d.ts.map +1 -1
- package/dist/types/src/testing/useTestTableModel.d.ts +5 -8
- package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +2 -2
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/Table.d.ts +17 -14
- package/dist/types/src/types/Table.d.ts.map +1 -1
- package/dist/types/src/util/dynamic-table.d.ts +8 -7
- package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
- package/dist/types/src/util/index.d.ts +2 -0
- package/dist/types/src/util/index.d.ts.map +1 -1
- package/dist/types/src/util/query-ast.d.ts +7 -0
- package/dist/types/src/util/query-ast.d.ts.map +1 -0
- package/dist/types/src/util/sort.d.ts +12 -0
- package/dist/types/src/util/sort.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +53 -54
- package/src/components/Table/ColumnActionsMenu.tsx +10 -6
- package/src/components/Table/ColumnSettings.tsx +6 -6
- package/src/components/Table/DynamicTable.stories.tsx +6 -7
- package/src/components/Table/DynamicTable.tsx +21 -22
- package/src/components/Table/Relations.stories.tsx +23 -23
- package/src/components/Table/RowActionsMenu.tsx +3 -2
- package/src/components/Table/Table.stories.tsx +34 -43
- package/src/components/Table/Table.tsx +335 -305
- package/src/components/TableCellEditor/CellValidationMessage.tsx +1 -1
- package/src/components/TableCellEditor/FormCellEditor.tsx +111 -134
- package/src/components/TableCellEditor/TableCellEditor.stories.tsx +7 -5
- package/src/components/TableCellEditor/TableCellEditor.tsx +27 -27
- package/src/components/TableToolbar/TableToolbar.tsx +18 -12
- package/src/components/testing.ts +37 -11
- package/src/hooks/useAddRow.ts +8 -10
- package/src/hooks/useProjectionModel.ts +29 -8
- package/src/hooks/useTableModel.ts +26 -22
- package/src/model/modal-controller.ts +23 -12
- package/src/model/selection-model.ts +75 -44
- package/src/model/table-model.test.ts +80 -52
- package/src/model/table-model.ts +402 -131
- package/src/model/table-presentation.test.ts +57 -47
- package/src/model/table-presentation.ts +22 -17
- package/src/playwright/TableManager.ts +2 -2
- package/src/playwright/playwright.config.ts +1 -1
- package/src/playwright/smoke.spec.ts +3 -2
- package/src/testing/useTestTableModel.ts +23 -24
- package/src/translations.ts +1 -1
- package/src/types/Table.ts +19 -18
- package/src/util/dynamic-table.ts +28 -12
- package/src/util/index.ts +2 -0
- package/src/util/query-ast.ts +19 -0
- package/src/util/sort.ts +90 -0
- package/dist/lib/browser/chunk-5XWSB3JP.mjs.map +0 -7
- package/dist/lib/browser/chunk-KNC7XYT6.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-CWSGS3Y2.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-MKTMOY46.mjs.map +0 -7
- package/dist/types/src/model/table-sorting.d.ts +0 -56
- package/dist/types/src/model/table-sorting.d.ts.map +0 -1
- package/src/model/table-sorting.ts +0 -234
|
@@ -1,86 +1,64 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';const require = createRequire(import.meta.url);
|
|
2
|
-
import {
|
|
3
|
-
Table_exports
|
|
4
|
-
} from "./chunk-MKTMOY46.mjs";
|
|
5
2
|
|
|
6
|
-
// src/util/
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
if (typename && properties) {
|
|
11
|
-
const schema2 = getSchemaFromPropertyDefinitions(typename, properties);
|
|
12
|
-
return {
|
|
13
|
-
typename: schema2.typename,
|
|
14
|
-
jsonSchema: schema2.jsonSchema
|
|
15
|
-
};
|
|
16
|
-
} else if (schema) {
|
|
17
|
-
return {
|
|
18
|
-
typename: Type.getTypename(schema),
|
|
19
|
-
jsonSchema: Type.toJsonSchema(schema)
|
|
20
|
-
};
|
|
21
|
-
} else if (typename && jsonSchema) {
|
|
22
|
-
return {
|
|
23
|
-
typename,
|
|
24
|
-
jsonSchema
|
|
25
|
-
};
|
|
26
|
-
} else {
|
|
27
|
-
throw new Error("invalid properties");
|
|
3
|
+
// src/util/query-ast.ts
|
|
4
|
+
var extractOrder = (queryAst) => {
|
|
5
|
+
if (queryAst.type === "order") {
|
|
6
|
+
return queryAst.order;
|
|
28
7
|
}
|
|
8
|
+
if (queryAst.type === "options") {
|
|
9
|
+
return extractOrder(queryAst.query);
|
|
10
|
+
}
|
|
11
|
+
return void 0;
|
|
29
12
|
};
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const projection = new ProjectionModel(jsonSchema, view.projection);
|
|
43
|
-
projection.normalizeView();
|
|
44
|
-
if (properties && projection.fields) {
|
|
45
|
-
setProperties(view, projection, object, properties);
|
|
46
|
-
}
|
|
47
|
-
return {
|
|
48
|
-
projection,
|
|
49
|
-
object
|
|
50
|
-
};
|
|
51
|
-
};
|
|
52
|
-
var setProperties = (view, projection, table, properties) => {
|
|
53
|
-
for (const property of properties) {
|
|
54
|
-
const field = projection.fields.find((field2) => field2.path === property.name);
|
|
55
|
-
if (field) {
|
|
56
|
-
if (property.size !== void 0) {
|
|
57
|
-
table.sizes[field.path] = property.size;
|
|
58
|
-
}
|
|
59
|
-
if (property.title !== void 0) {
|
|
60
|
-
const fieldProjection = projection.getFieldProjection(field.id);
|
|
61
|
-
projection.setFieldProjection({
|
|
62
|
-
...fieldProjection,
|
|
63
|
-
props: {
|
|
64
|
-
...fieldProjection.props,
|
|
65
|
-
title: property.title
|
|
66
|
-
}
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
if (property.sort) {
|
|
70
|
-
const fieldId = field.id;
|
|
71
|
-
view.sort = [
|
|
72
|
-
{
|
|
73
|
-
fieldId,
|
|
74
|
-
direction: property.sort
|
|
75
|
-
}
|
|
76
|
-
];
|
|
77
|
-
}
|
|
13
|
+
|
|
14
|
+
// src/util/sort.ts
|
|
15
|
+
import { Format } from "@dxos/echo";
|
|
16
|
+
var getSortValue = (value, format) => {
|
|
17
|
+
if (value === void 0 || value === null) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
const isDateFormat = format === Format.TypeFormat.DateTime || format === Format.TypeFormat.Date;
|
|
21
|
+
if (isDateFormat) {
|
|
22
|
+
const date = new Date(value);
|
|
23
|
+
if (!isNaN(date.getTime())) {
|
|
24
|
+
return date.getTime();
|
|
78
25
|
}
|
|
26
|
+
return String(value);
|
|
79
27
|
}
|
|
28
|
+
if (format === Format.TypeFormat.Number || format === Format.TypeFormat.Integer || format === Format.TypeFormat.Currency || format === Format.TypeFormat.Percent || format === Format.TypeFormat.Duration) {
|
|
29
|
+
const num = typeof value === "number" ? value : Number(value);
|
|
30
|
+
if (!isNaN(num)) {
|
|
31
|
+
return num;
|
|
32
|
+
}
|
|
33
|
+
return String(value);
|
|
34
|
+
}
|
|
35
|
+
if (typeof value === "string") {
|
|
36
|
+
return value;
|
|
37
|
+
}
|
|
38
|
+
if (typeof value === "number") {
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
return String(value);
|
|
42
|
+
};
|
|
43
|
+
var compareValues = (aValue, bValue, format, direction) => {
|
|
44
|
+
if (aValue === void 0 || aValue === null) {
|
|
45
|
+
return bValue === void 0 || bValue === null ? 0 : 1;
|
|
46
|
+
}
|
|
47
|
+
if (bValue === void 0 || bValue === null) {
|
|
48
|
+
return -1;
|
|
49
|
+
}
|
|
50
|
+
const aSortValue = getSortValue(aValue, format);
|
|
51
|
+
const bSortValue = getSortValue(bValue, format);
|
|
52
|
+
let comparison = 0;
|
|
53
|
+
if (typeof aSortValue === "number" && typeof bSortValue === "number") {
|
|
54
|
+
comparison = aSortValue - bSortValue;
|
|
55
|
+
} else if (typeof aSortValue === "string" && typeof bSortValue === "string") {
|
|
56
|
+
comparison = aSortValue.localeCompare(bSortValue);
|
|
57
|
+
} else {
|
|
58
|
+
comparison = String(aSortValue).localeCompare(String(bSortValue));
|
|
59
|
+
}
|
|
60
|
+
return direction === "desc" ? -comparison : comparison;
|
|
80
61
|
};
|
|
81
|
-
|
|
82
|
-
// src/util/signals.ts
|
|
83
|
-
var touch = (value) => value;
|
|
84
62
|
|
|
85
63
|
// src/util/table-buttons.ts
|
|
86
64
|
var BUTTON_IDENTIFIERS = {
|
|
@@ -255,29 +233,15 @@ var tableControls = {
|
|
|
255
233
|
}
|
|
256
234
|
};
|
|
257
235
|
|
|
258
|
-
// src/util/schema.ts
|
|
259
|
-
import * as Schema from "effect/Schema";
|
|
260
|
-
import * as SchemaAST from "effect/SchemaAST";
|
|
261
|
-
var narrowSchema = (schema, paths) => {
|
|
262
|
-
const ast = schema?.ast;
|
|
263
|
-
if (SchemaAST.isTypeLiteral(ast)) {
|
|
264
|
-
const propertySignatures = ast.propertySignatures.filter((signature) => paths.includes(signature.name.toString()));
|
|
265
|
-
if (propertySignatures.length > 0) {
|
|
266
|
-
const narrowType = new SchemaAST.TypeLiteral(propertySignatures, []);
|
|
267
|
-
return Schema.make(narrowType);
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
return void 0;
|
|
271
|
-
};
|
|
272
|
-
|
|
273
236
|
// src/model/table-model.ts
|
|
274
|
-
import {
|
|
237
|
+
import { Atom as Atom2 } from "@effect-atom/atom-react";
|
|
275
238
|
import { Resource as Resource2 } from "@dxos/context";
|
|
276
|
-
import { Format as Format2, Obj
|
|
277
|
-
import {
|
|
239
|
+
import { Format as Format2, Obj, Order, Query, Ref } from "@dxos/echo";
|
|
240
|
+
import { toEffectSchema } from "@dxos/echo/internal";
|
|
241
|
+
import { getSnapshot } from "@dxos/echo/internal";
|
|
242
|
+
import { getValue, setValue } from "@dxos/effect";
|
|
278
243
|
import { invariant } from "@dxos/invariant";
|
|
279
244
|
import { ObjectId } from "@dxos/keys";
|
|
280
|
-
import { getSnapshot, isLiveObject } from "@dxos/live-object";
|
|
281
245
|
import { formatForEditing, parseValue } from "@dxos/react-ui-form";
|
|
282
246
|
import { validateSchema } from "@dxos/schema";
|
|
283
247
|
|
|
@@ -298,75 +262,99 @@ var extractTagIds = (value) => {
|
|
|
298
262
|
};
|
|
299
263
|
|
|
300
264
|
// src/model/selection-model.ts
|
|
301
|
-
import {
|
|
265
|
+
import { Atom } from "@effect-atom/atom-react";
|
|
302
266
|
import { Resource } from "@dxos/context";
|
|
303
267
|
var SelectionModel = class extends Resource {
|
|
268
|
+
_registry;
|
|
304
269
|
_rows;
|
|
270
|
+
_selection;
|
|
271
|
+
_validSelectedIds;
|
|
272
|
+
_hasSelection;
|
|
273
|
+
_allSelected;
|
|
305
274
|
_selectionMode;
|
|
306
|
-
_initialSelection;
|
|
307
275
|
_onSelectionChanged;
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
276
|
+
constructor(registry, rows, selectionMode, initialSelection, onSelectionChanged) {
|
|
277
|
+
super();
|
|
278
|
+
this._registry = registry;
|
|
279
|
+
this._rows = rows;
|
|
280
|
+
this._selectionMode = selectionMode;
|
|
281
|
+
this._onSelectionChanged = onSelectionChanged;
|
|
282
|
+
this._selection = Atom.make(new Set(initialSelection));
|
|
283
|
+
this._validSelectedIds = Atom.make((get) => {
|
|
284
|
+
const rows2 = get(this._rows);
|
|
285
|
+
if (!rows2) {
|
|
286
|
+
return /* @__PURE__ */ new Set();
|
|
287
|
+
}
|
|
288
|
+
const validIds = new Set(rows2.map((row) => row.id));
|
|
289
|
+
const selection = get(this._selection);
|
|
290
|
+
return new Set([
|
|
291
|
+
...selection
|
|
292
|
+
].filter((id) => validIds.has(id)));
|
|
293
|
+
});
|
|
294
|
+
this._hasSelection = Atom.make((get) => get(this._validSelectedIds).size > 0);
|
|
295
|
+
this._allSelected = Atom.make((get) => {
|
|
296
|
+
const rows2 = get(this._rows);
|
|
297
|
+
if (rows2.length === 0) {
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
const selection = get(this._selection);
|
|
301
|
+
return rows2.every((row) => selection.has(row.id));
|
|
302
|
+
});
|
|
330
303
|
}
|
|
331
304
|
async _open() {
|
|
332
|
-
const
|
|
333
|
-
touch(this._selection.value);
|
|
305
|
+
const selectionUnsubscribe = this._registry.subscribe(this._selection, () => {
|
|
334
306
|
this._onSelectionChanged?.();
|
|
335
307
|
});
|
|
336
|
-
this._ctx.onDispose(
|
|
308
|
+
this._ctx.onDispose(selectionUnsubscribe);
|
|
337
309
|
}
|
|
338
310
|
//
|
|
339
311
|
// Getters
|
|
340
312
|
//
|
|
341
|
-
|
|
313
|
+
/** Get the selection atom for reactive access. */
|
|
314
|
+
get selectionAtom() {
|
|
342
315
|
return this._selection;
|
|
343
316
|
}
|
|
344
|
-
|
|
317
|
+
/** Get the current selection value. */
|
|
318
|
+
get selection() {
|
|
319
|
+
return this._registry.get(this._selection);
|
|
320
|
+
}
|
|
321
|
+
/** Get the hasSelection atom for reactive access. */
|
|
322
|
+
get hasSelectionAtom() {
|
|
345
323
|
return this._hasSelection;
|
|
346
324
|
}
|
|
347
|
-
|
|
325
|
+
/** Check if there is any selection. */
|
|
326
|
+
get hasSelection() {
|
|
327
|
+
return this._registry.get(this._hasSelection);
|
|
328
|
+
}
|
|
329
|
+
/** Get the allRowsSelected atom for reactive access. */
|
|
330
|
+
get allRowsSelectedAtom() {
|
|
348
331
|
return this._allSelected;
|
|
349
332
|
}
|
|
333
|
+
/** Check if all rows are selected. */
|
|
334
|
+
get allRowsSelected() {
|
|
335
|
+
return this._registry.get(this._allSelected);
|
|
336
|
+
}
|
|
350
337
|
get selectionMode() {
|
|
351
338
|
return this._selectionMode;
|
|
352
339
|
}
|
|
353
340
|
getSelectedRows = () => {
|
|
354
|
-
const selectedIds = this._validSelectedIds
|
|
355
|
-
return this._rows.
|
|
341
|
+
const selectedIds = this._registry.get(this._validSelectedIds);
|
|
342
|
+
return this._registry.get(this._rows).filter((row) => selectedIds.has(row.id));
|
|
356
343
|
};
|
|
357
344
|
isRowIndexSelected = (rowIndex) => {
|
|
358
|
-
const row = this._rows
|
|
359
|
-
return this._selection.
|
|
345
|
+
const row = this._registry.get(this._rows)[rowIndex];
|
|
346
|
+
return this._registry.get(this._selection).has(row.id);
|
|
360
347
|
};
|
|
361
348
|
isObjectSelected = (object) => {
|
|
362
|
-
return this._selection.
|
|
349
|
+
return this._registry.get(this._selection).has(object.id);
|
|
363
350
|
};
|
|
364
351
|
//
|
|
365
352
|
// Manipulation
|
|
366
353
|
//
|
|
367
354
|
toggleSelectionForRowIndex = (rowIndex) => {
|
|
368
|
-
const row = this._rows
|
|
369
|
-
const
|
|
355
|
+
const row = this._registry.get(this._rows)[rowIndex];
|
|
356
|
+
const currentSelection = this._registry.get(this._selection);
|
|
357
|
+
const newSelection = new Set(currentSelection);
|
|
370
358
|
if (newSelection.has(row.id)) {
|
|
371
359
|
newSelection.delete(row.id);
|
|
372
360
|
} else {
|
|
@@ -375,195 +363,38 @@ var SelectionModel = class extends Resource {
|
|
|
375
363
|
}
|
|
376
364
|
newSelection.add(row.id);
|
|
377
365
|
}
|
|
378
|
-
this._selection
|
|
366
|
+
this._registry.set(this._selection, newSelection);
|
|
379
367
|
};
|
|
380
368
|
setSelection = (mode) => {
|
|
381
369
|
switch (mode) {
|
|
382
370
|
case "all": {
|
|
383
|
-
this._selection
|
|
371
|
+
this._registry.set(this._selection, new Set(this._registry.get(this._rows).map((row) => row.id)));
|
|
384
372
|
break;
|
|
385
373
|
}
|
|
386
374
|
case "none": {
|
|
387
|
-
this._selection
|
|
375
|
+
this._registry.set(this._selection, /* @__PURE__ */ new Set());
|
|
388
376
|
break;
|
|
389
377
|
}
|
|
390
378
|
}
|
|
391
379
|
};
|
|
392
380
|
};
|
|
393
381
|
|
|
394
|
-
// src/model/table-sorting.ts
|
|
395
|
-
import { computed as computed2, signal as signal2 } from "@preact/signals-core";
|
|
396
|
-
import orderBy from "lodash.orderby";
|
|
397
|
-
import { Format, TypeEnum, getValue } from "@dxos/echo/internal";
|
|
398
|
-
import { formatForDisplay } from "@dxos/react-ui-form";
|
|
399
|
-
var TableSorting = class {
|
|
400
|
-
_displayToDataIndex = /* @__PURE__ */ new Map();
|
|
401
|
-
_rows;
|
|
402
|
-
_getView;
|
|
403
|
-
_projection;
|
|
404
|
-
_localSort = signal2(void 0);
|
|
405
|
-
_isDirty;
|
|
406
|
-
sortedRows;
|
|
407
|
-
constructor({ rows, projection, getView }) {
|
|
408
|
-
this._rows = rows;
|
|
409
|
-
this._getView = getView;
|
|
410
|
-
this._projection = projection;
|
|
411
|
-
this._isDirty = computed2(() => {
|
|
412
|
-
const local = this._localSort.value;
|
|
413
|
-
const viewSort = this._getView().sort?.[0];
|
|
414
|
-
if (local?.type === "cleared") {
|
|
415
|
-
return viewSort !== void 0;
|
|
416
|
-
}
|
|
417
|
-
if (local?.type === "active") {
|
|
418
|
-
if (!viewSort) {
|
|
419
|
-
return true;
|
|
420
|
-
}
|
|
421
|
-
return local.sort.fieldId !== viewSort.fieldId || local.sort.direction !== viewSort.direction;
|
|
422
|
-
}
|
|
423
|
-
return false;
|
|
424
|
-
});
|
|
425
|
-
this.sortedRows = this.initialiseSortedRows();
|
|
426
|
-
}
|
|
427
|
-
/**
|
|
428
|
-
* @reactive
|
|
429
|
-
* @returns Local sort if present, falls back to view sort.
|
|
430
|
-
*/
|
|
431
|
-
get sorting() {
|
|
432
|
-
const local = this._localSort.value;
|
|
433
|
-
if (local?.type === "cleared") {
|
|
434
|
-
return void 0;
|
|
435
|
-
}
|
|
436
|
-
if (local?.type === "active") {
|
|
437
|
-
return local.sort;
|
|
438
|
-
}
|
|
439
|
-
return this._getView().sort?.[0];
|
|
440
|
-
}
|
|
441
|
-
/**
|
|
442
|
-
* @reactive
|
|
443
|
-
* @returns Whether local sort differs from view sort.
|
|
444
|
-
*/
|
|
445
|
-
get isDirty() {
|
|
446
|
-
return this._isDirty.value;
|
|
447
|
-
}
|
|
448
|
-
getDataIndex(displayIndex) {
|
|
449
|
-
return this._displayToDataIndex.get(displayIndex) ?? displayIndex;
|
|
450
|
-
}
|
|
451
|
-
setSort(fieldId, direction) {
|
|
452
|
-
this._localSort.value = {
|
|
453
|
-
type: "active",
|
|
454
|
-
sort: {
|
|
455
|
-
fieldId,
|
|
456
|
-
direction
|
|
457
|
-
}
|
|
458
|
-
};
|
|
459
|
-
}
|
|
460
|
-
toggleSort(fieldId) {
|
|
461
|
-
if (!this.sorting || this.sorting.fieldId !== fieldId) {
|
|
462
|
-
return;
|
|
463
|
-
}
|
|
464
|
-
switch (this.sorting.direction) {
|
|
465
|
-
case "asc": {
|
|
466
|
-
this.setSort(fieldId, "desc");
|
|
467
|
-
break;
|
|
468
|
-
}
|
|
469
|
-
case "desc": {
|
|
470
|
-
this.setSort(fieldId, "asc");
|
|
471
|
-
break;
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
clearSort() {
|
|
476
|
-
this._localSort.value = {
|
|
477
|
-
type: "cleared"
|
|
478
|
-
};
|
|
479
|
-
}
|
|
480
|
-
save() {
|
|
481
|
-
if (this._localSort.value !== void 0) {
|
|
482
|
-
const view = this._getView();
|
|
483
|
-
if (this._localSort.value.type === "active") {
|
|
484
|
-
view.sort = [
|
|
485
|
-
this._localSort.value.sort
|
|
486
|
-
];
|
|
487
|
-
} else {
|
|
488
|
-
view.sort = [];
|
|
489
|
-
}
|
|
490
|
-
this._localSort.value = void 0;
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
//
|
|
494
|
-
// Initialisation.
|
|
495
|
-
//
|
|
496
|
-
initialiseSortedRows() {
|
|
497
|
-
return computed2(() => {
|
|
498
|
-
this._displayToDataIndex.clear();
|
|
499
|
-
const sort = this.sorting;
|
|
500
|
-
if (!sort) {
|
|
501
|
-
return this._rows.value;
|
|
502
|
-
}
|
|
503
|
-
const field = this._projection.fields.find((f) => f.id === sort.fieldId);
|
|
504
|
-
if (!field) {
|
|
505
|
-
return this._rows.value;
|
|
506
|
-
}
|
|
507
|
-
const { props } = this._projection.getFieldProjection(field.id);
|
|
508
|
-
const dataWithIndices = this._rows.value.map((item, index) => {
|
|
509
|
-
const sortValue = this.getSortValue(props, field, item);
|
|
510
|
-
const isEmpty = !(props.type === TypeEnum.Boolean) && (sortValue == null || sortValue === "");
|
|
511
|
-
return {
|
|
512
|
-
item,
|
|
513
|
-
index,
|
|
514
|
-
value: sortValue,
|
|
515
|
-
isEmpty
|
|
516
|
-
};
|
|
517
|
-
});
|
|
518
|
-
const ordered = orderBy(dataWithIndices, [
|
|
519
|
-
"isEmpty",
|
|
520
|
-
"value"
|
|
521
|
-
], [
|
|
522
|
-
"asc",
|
|
523
|
-
sort.direction
|
|
524
|
-
]);
|
|
525
|
-
ordered.forEach(({ index: dataIndex }, displayIndex) => {
|
|
526
|
-
if (displayIndex !== dataIndex) {
|
|
527
|
-
this._displayToDataIndex.set(displayIndex, dataIndex);
|
|
528
|
-
}
|
|
529
|
-
});
|
|
530
|
-
return ordered.map(({ item }) => item);
|
|
531
|
-
});
|
|
532
|
-
}
|
|
533
|
-
getSortValue(props, field, item) {
|
|
534
|
-
const { type, format } = props;
|
|
535
|
-
const value = getValue(item, field.path);
|
|
536
|
-
if (type === TypeEnum.Boolean) {
|
|
537
|
-
return !!value;
|
|
538
|
-
}
|
|
539
|
-
if (type === TypeEnum.Number) {
|
|
540
|
-
return value;
|
|
541
|
-
}
|
|
542
|
-
if (value == null) {
|
|
543
|
-
return value;
|
|
544
|
-
}
|
|
545
|
-
if (format === Format.TypeFormat.SingleSelect) {
|
|
546
|
-
const options = props.options;
|
|
547
|
-
if (!options) {
|
|
548
|
-
return void 0;
|
|
549
|
-
}
|
|
550
|
-
const option = options.find((option2) => option2.id === value);
|
|
551
|
-
return option ? options.indexOf(option) : void 0;
|
|
552
|
-
}
|
|
553
|
-
if (format === Format.TypeFormat.Ref && field.referencePath) {
|
|
554
|
-
const refValue = getValue(value.target, field.referencePath);
|
|
555
|
-
return refValue;
|
|
556
|
-
}
|
|
557
|
-
return formatForDisplay({
|
|
558
|
-
type,
|
|
559
|
-
format,
|
|
560
|
-
value
|
|
561
|
-
});
|
|
562
|
-
}
|
|
563
|
-
};
|
|
564
|
-
|
|
565
382
|
// src/model/table-model.ts
|
|
566
383
|
var __dxlog_file = "/__w/dxos/dxos/packages/ui/react-ui-table/src/model/table-model.ts";
|
|
384
|
+
var createEchoChangeCallback = (table) => ({
|
|
385
|
+
table: (mutate) => Obj.change(table, (mutableTable) => mutate(mutableTable)),
|
|
386
|
+
row: (row, mutate) => {
|
|
387
|
+
if (Obj.isObject(row)) {
|
|
388
|
+
Obj.change(row, (mutableRow) => mutate(mutableRow));
|
|
389
|
+
} else {
|
|
390
|
+
mutate(row);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
var createDirectChangeCallback = (table) => ({
|
|
395
|
+
table: (mutate) => mutate(table),
|
|
396
|
+
row: (row, mutate) => mutate(row)
|
|
397
|
+
});
|
|
567
398
|
var defaultFeatures = {
|
|
568
399
|
selection: {
|
|
569
400
|
enabled: true,
|
|
@@ -573,18 +404,12 @@ var defaultFeatures = {
|
|
|
573
404
|
schemaEditable: false
|
|
574
405
|
};
|
|
575
406
|
var TableModel = class extends Resource2 {
|
|
407
|
+
_registry;
|
|
576
408
|
_object;
|
|
577
409
|
_projection;
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
col: 0
|
|
582
|
-
},
|
|
583
|
-
end: {
|
|
584
|
-
row: 0,
|
|
585
|
-
col: 0
|
|
586
|
-
}
|
|
587
|
-
});
|
|
410
|
+
_db;
|
|
411
|
+
_change;
|
|
412
|
+
_visibleRange;
|
|
588
413
|
_onInsertRow;
|
|
589
414
|
_onDeleteRows;
|
|
590
415
|
_onDeleteColumn;
|
|
@@ -593,43 +418,115 @@ var TableModel = class extends Resource2 {
|
|
|
593
418
|
_onRowAction;
|
|
594
419
|
_rowActions;
|
|
595
420
|
_features;
|
|
596
|
-
_rows
|
|
597
|
-
_draftRows
|
|
421
|
+
_rows;
|
|
422
|
+
_draftRows;
|
|
423
|
+
// In-memory sort state - changes are local until saved.
|
|
424
|
+
_inMemorySort;
|
|
425
|
+
// Derived atom for persisted sort from view.query.ast.
|
|
426
|
+
_persistedSort;
|
|
427
|
+
// Derived atom for current sort (in-memory takes precedence over persisted).
|
|
598
428
|
_sorting;
|
|
429
|
+
// Derived atom for sorted rows (used by rows getter and SelectionModel).
|
|
430
|
+
_sortedRows;
|
|
431
|
+
// Derived atom for viewDirty state.
|
|
432
|
+
_viewDirty;
|
|
599
433
|
_pinnedRows;
|
|
600
434
|
_selection;
|
|
601
435
|
_columnMeta;
|
|
602
|
-
|
|
436
|
+
// Counter atom that increments when cells are updated - allows UI to react to cell changes.
|
|
437
|
+
_cellUpdateCounter;
|
|
438
|
+
constructor({ registry, object, projection, db, change, features = {}, initialSelection = [], pinnedRows = {
|
|
603
439
|
top: [],
|
|
604
440
|
bottom: []
|
|
605
441
|
}, rowActions = [], onCellUpdate, onDeleteColumn, onDeleteRows, onInsertRow, onRowOrderChange, onRowAction }) {
|
|
606
442
|
super();
|
|
443
|
+
this._registry = registry;
|
|
607
444
|
this._object = object;
|
|
608
445
|
this._projection = projection;
|
|
609
446
|
this._projection.normalizeView();
|
|
447
|
+
this._db = db;
|
|
448
|
+
this._change = change ?? createEchoChangeCallback(object);
|
|
610
449
|
this._features = {
|
|
611
450
|
...defaultFeatures,
|
|
612
451
|
...features
|
|
613
452
|
};
|
|
614
453
|
invariant(!(this._features.dataEditable && this._features.selection.enabled && this._features.selection.mode === "single"), "Single selection is not compatible with editable tables.", {
|
|
615
454
|
F: __dxlog_file,
|
|
616
|
-
L:
|
|
455
|
+
L: 196,
|
|
617
456
|
S: this,
|
|
618
457
|
A: [
|
|
619
458
|
"!(this._features.dataEditable && this._features.selection.enabled && this._features.selection.mode === 'single')",
|
|
620
459
|
"'Single selection is not compatible with editable tables.'"
|
|
621
460
|
]
|
|
622
461
|
});
|
|
623
|
-
this.
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
462
|
+
this._visibleRange = Atom2.make({
|
|
463
|
+
start: {
|
|
464
|
+
row: 0,
|
|
465
|
+
col: 0
|
|
466
|
+
},
|
|
467
|
+
end: {
|
|
468
|
+
row: 0,
|
|
469
|
+
col: 0
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
this._rows = Atom2.make([]);
|
|
473
|
+
this._draftRows = Atom2.make([]);
|
|
474
|
+
this._inMemorySort = Atom2.make(void 0);
|
|
475
|
+
this._cellUpdateCounter = Atom2.make(0);
|
|
476
|
+
this._persistedSort = Atom2.make((_get) => {
|
|
477
|
+
const viewSnapshot = Obj.getSnapshot(this.view);
|
|
478
|
+
const ast = viewSnapshot.query.ast;
|
|
479
|
+
const orders = extractOrder(ast);
|
|
480
|
+
if (orders && orders.length > 0) {
|
|
481
|
+
const firstOrder = orders[0];
|
|
482
|
+
if (firstOrder.kind === "property") {
|
|
483
|
+
const field = this._projection.getFields().find((f) => f.path === firstOrder.property);
|
|
484
|
+
if (field) {
|
|
485
|
+
return {
|
|
486
|
+
fieldId: field.id,
|
|
487
|
+
direction: firstOrder.direction
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
return void 0;
|
|
493
|
+
});
|
|
494
|
+
this._sorting = Atom2.make((get) => {
|
|
495
|
+
const inMemorySort = get(this._inMemorySort);
|
|
496
|
+
return inMemorySort ?? get(this._persistedSort);
|
|
497
|
+
});
|
|
498
|
+
this._sortedRows = Atom2.make((get) => {
|
|
499
|
+
const rows = get(this._rows);
|
|
500
|
+
const sort = get(this._sorting);
|
|
501
|
+
if (!sort || rows.length === 0) {
|
|
502
|
+
return rows;
|
|
503
|
+
}
|
|
504
|
+
const field = this._projection.getFields().find((f) => f.id === sort.fieldId);
|
|
505
|
+
if (!field) {
|
|
506
|
+
return rows;
|
|
507
|
+
}
|
|
508
|
+
const fieldProjection = this._projection.getFieldProjection(field.id);
|
|
509
|
+
const sorted = [
|
|
510
|
+
...rows
|
|
511
|
+
].sort((a, b) => {
|
|
512
|
+
const aValue = getValue(a, field.path);
|
|
513
|
+
const bValue = getValue(b, field.path);
|
|
514
|
+
return compareValues(aValue, bValue, fieldProjection.props.format, sort.direction);
|
|
515
|
+
});
|
|
516
|
+
return sorted;
|
|
517
|
+
});
|
|
518
|
+
this._selection = new SelectionModel(this._registry, this._sortedRows, this._features.selection.mode ?? "multiple", initialSelection, () => this._onRowOrderChange?.());
|
|
519
|
+
this._viewDirty = Atom2.make((get) => {
|
|
520
|
+
const inMemorySort = get(this._inMemorySort);
|
|
521
|
+
if (!inMemorySort) {
|
|
522
|
+
return false;
|
|
523
|
+
}
|
|
524
|
+
const persisted = get(this._persistedSort);
|
|
525
|
+
if (!persisted) {
|
|
526
|
+
return true;
|
|
527
|
+
}
|
|
528
|
+
return inMemorySort.fieldId !== persisted.fieldId || inMemorySort.direction !== persisted.direction;
|
|
627
529
|
});
|
|
628
|
-
if (sorting.length > 0) {
|
|
629
|
-
const [sort] = sorting;
|
|
630
|
-
this._sorting.setSort(sort.fieldId, sort.direction);
|
|
631
|
-
}
|
|
632
|
-
this._selection = new SelectionModel(this._sorting.sortedRows, this._features.selection.mode ?? "multiple", initialSelection, () => this._onRowOrderChange?.());
|
|
633
530
|
this._pinnedRows = pinnedRows;
|
|
634
531
|
this._rowActions = rowActions;
|
|
635
532
|
this._onInsertRow = onInsertRow;
|
|
@@ -640,13 +537,13 @@ var TableModel = class extends Resource2 {
|
|
|
640
537
|
this._onRowAction = onRowAction;
|
|
641
538
|
}
|
|
642
539
|
get id() {
|
|
643
|
-
return
|
|
540
|
+
return Obj.getDXN(this._object).toString();
|
|
644
541
|
}
|
|
645
542
|
get view() {
|
|
646
543
|
const view = this._object.view.target;
|
|
647
544
|
invariant(view, "Table model not initialized", {
|
|
648
545
|
F: __dxlog_file,
|
|
649
|
-
L:
|
|
546
|
+
L: 307,
|
|
650
547
|
S: this,
|
|
651
548
|
A: [
|
|
652
549
|
"view",
|
|
@@ -661,26 +558,52 @@ var TableModel = class extends Resource2 {
|
|
|
661
558
|
get projection() {
|
|
662
559
|
return this._projection;
|
|
663
560
|
}
|
|
561
|
+
get db() {
|
|
562
|
+
return this._db;
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Atom for reactive access to rows with in-memory sorting applied.
|
|
566
|
+
*/
|
|
664
567
|
get rows() {
|
|
665
|
-
return this.
|
|
568
|
+
return this._sortedRows;
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* Gets rows with in-memory sorting applied.
|
|
572
|
+
* In-memory sort is local to this instance until saved.
|
|
573
|
+
*/
|
|
574
|
+
getRows() {
|
|
575
|
+
return this._registry.get(this._sortedRows);
|
|
576
|
+
}
|
|
577
|
+
/** Atom for reactive access to cell changes. */
|
|
578
|
+
get cellUpdate() {
|
|
579
|
+
return this._cellUpdateCounter;
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* Change a row using the configured change callback.
|
|
583
|
+
* Use this instead of directly mutating to ensure consistent mutation handling.
|
|
584
|
+
*/
|
|
585
|
+
changeRow(row, mutate) {
|
|
586
|
+
this._change.row(row, mutate);
|
|
666
587
|
}
|
|
667
588
|
get features() {
|
|
668
589
|
return this._features;
|
|
669
590
|
}
|
|
670
591
|
/**
|
|
671
|
-
*
|
|
592
|
+
* Gets the current sort state.
|
|
593
|
+
* Priority: in-memory sort (local changes) > persisted sort (from query AST).
|
|
672
594
|
*/
|
|
673
|
-
|
|
674
|
-
return this._sorting;
|
|
595
|
+
getSorting() {
|
|
596
|
+
return this._registry.get(this._sorting);
|
|
675
597
|
}
|
|
676
598
|
/**
|
|
677
599
|
* Gets the row data at the specified display index.
|
|
678
600
|
*/
|
|
679
601
|
getRowAt(displayIndex) {
|
|
680
|
-
|
|
602
|
+
const sortedRows = this._registry.get(this._sortedRows);
|
|
603
|
+
if (displayIndex < 0 || displayIndex >= sortedRows.length) {
|
|
681
604
|
return void 0;
|
|
682
605
|
}
|
|
683
|
-
return
|
|
606
|
+
return sortedRows[displayIndex];
|
|
684
607
|
}
|
|
685
608
|
get pinnedRows() {
|
|
686
609
|
return this._pinnedRows;
|
|
@@ -688,7 +611,7 @@ var TableModel = class extends Resource2 {
|
|
|
688
611
|
get columnMeta() {
|
|
689
612
|
invariant(this._columnMeta, void 0, {
|
|
690
613
|
F: __dxlog_file,
|
|
691
|
-
L:
|
|
614
|
+
L: 380,
|
|
692
615
|
S: this,
|
|
693
616
|
A: [
|
|
694
617
|
"this._columnMeta",
|
|
@@ -700,10 +623,6 @@ var TableModel = class extends Resource2 {
|
|
|
700
623
|
get selection() {
|
|
701
624
|
return this._selection;
|
|
702
625
|
}
|
|
703
|
-
/** @reactive */
|
|
704
|
-
get isViewDirty() {
|
|
705
|
-
return this.sorting.isDirty;
|
|
706
|
-
}
|
|
707
626
|
get rowActions() {
|
|
708
627
|
return this._rowActions;
|
|
709
628
|
}
|
|
@@ -717,8 +636,17 @@ var TableModel = class extends Resource2 {
|
|
|
717
636
|
await this._selection.open(this._ctx);
|
|
718
637
|
}
|
|
719
638
|
initializeColumnMeta() {
|
|
720
|
-
this.
|
|
721
|
-
|
|
639
|
+
invariant(this._object.view.target, "View must be loaded before initializing column meta", {
|
|
640
|
+
F: __dxlog_file,
|
|
641
|
+
L: 404,
|
|
642
|
+
S: this,
|
|
643
|
+
A: [
|
|
644
|
+
"this._object.view.target",
|
|
645
|
+
"'View must be loaded before initializing column meta'"
|
|
646
|
+
]
|
|
647
|
+
});
|
|
648
|
+
this._columnMeta = Atom2.make((get) => {
|
|
649
|
+
const fields = get(this._projection.fields);
|
|
722
650
|
const meta = Object.fromEntries(fields.map((field, index) => [
|
|
723
651
|
index,
|
|
724
652
|
{
|
|
@@ -744,49 +672,62 @@ var TableModel = class extends Resource2 {
|
|
|
744
672
|
});
|
|
745
673
|
}
|
|
746
674
|
initializeEffects() {
|
|
747
|
-
const
|
|
748
|
-
touch(this.sorting.sortedRows.value);
|
|
675
|
+
const rowsUnsubscribe = this._registry.subscribe(this._rows, () => {
|
|
749
676
|
this._onRowOrderChange?.();
|
|
750
677
|
});
|
|
751
|
-
this._ctx.onDispose(
|
|
752
|
-
const
|
|
753
|
-
|
|
754
|
-
const
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
678
|
+
this._ctx.onDispose(rowsUnsubscribe);
|
|
679
|
+
const rowSubscriptions = /* @__PURE__ */ new Map();
|
|
680
|
+
const visibleRangeUnsubscribe = this._registry.subscribe(this._visibleRange, () => {
|
|
681
|
+
for (const [id, unsub] of rowSubscriptions) {
|
|
682
|
+
unsub();
|
|
683
|
+
rowSubscriptions.delete(id);
|
|
684
|
+
}
|
|
685
|
+
const { start, end } = this._registry.get(this._visibleRange);
|
|
686
|
+
const sortedRows = this._registry.get(this._sortedRows);
|
|
687
|
+
for (let row = start.row; row <= end.row && row < sortedRows.length; row++) {
|
|
688
|
+
const obj = sortedRows[row];
|
|
689
|
+
if (Obj.isObject(obj) && !rowSubscriptions.has(obj.id)) {
|
|
690
|
+
const unsub = Obj.subscribe(obj, () => {
|
|
691
|
+
this._registry.set(this._cellUpdateCounter, this._registry.get(this._cellUpdateCounter) + 1);
|
|
692
|
+
this._onCellUpdate?.({
|
|
693
|
+
row,
|
|
694
|
+
col: start.col,
|
|
695
|
+
plane: "grid"
|
|
696
|
+
});
|
|
763
697
|
});
|
|
764
|
-
|
|
698
|
+
rowSubscriptions.set(obj.id, unsub);
|
|
699
|
+
}
|
|
765
700
|
}
|
|
766
|
-
return () => rowEffects.forEach((cleanup) => cleanup());
|
|
767
701
|
});
|
|
768
|
-
this._ctx.onDispose(
|
|
769
|
-
|
|
770
|
-
|
|
702
|
+
this._ctx.onDispose(() => {
|
|
703
|
+
visibleRangeUnsubscribe();
|
|
704
|
+
for (const unsub of rowSubscriptions.values()) {
|
|
705
|
+
unsub();
|
|
706
|
+
}
|
|
707
|
+
});
|
|
708
|
+
const draftRowsUnsubscribe = this._registry.subscribe(this._draftRows, () => {
|
|
771
709
|
this._onRowOrderChange?.();
|
|
772
710
|
});
|
|
773
|
-
this._ctx.onDispose(
|
|
711
|
+
this._ctx.onDispose(draftRowsUnsubscribe);
|
|
774
712
|
}
|
|
775
713
|
//
|
|
776
714
|
// Data
|
|
777
715
|
//
|
|
778
716
|
setRows = (obj) => {
|
|
779
|
-
this._rows
|
|
717
|
+
this._registry.set(this._rows, obj);
|
|
780
718
|
};
|
|
781
|
-
getRowCount = () => this.
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
// TODO(ZaymonFC): Return .value instead of exposing the signal.
|
|
719
|
+
getRowCount = () => this._registry.get(this._sortedRows).length;
|
|
720
|
+
getDraftRowCount() {
|
|
721
|
+
return this._registry.get(this._draftRows).length;
|
|
722
|
+
}
|
|
723
|
+
/** Atom for reactive access to draft rows. */
|
|
787
724
|
get draftRows() {
|
|
788
725
|
return this._draftRows;
|
|
789
726
|
}
|
|
727
|
+
/** Gets the current draft rows. */
|
|
728
|
+
getDraftRows() {
|
|
729
|
+
return this._registry.get(this._draftRows);
|
|
730
|
+
}
|
|
790
731
|
/**
|
|
791
732
|
* Checks if a specific field path has validation errors for a given draft row.
|
|
792
733
|
* @param draftRowIndex - The index of the draft row to check
|
|
@@ -794,17 +735,17 @@ var TableModel = class extends Resource2 {
|
|
|
794
735
|
* @returns true if the field has validation errors, false otherwise
|
|
795
736
|
*/
|
|
796
737
|
hasDraftRowValidationError(draftRowIndex, fieldPath) {
|
|
797
|
-
if (draftRowIndex < 0 || draftRowIndex >= this._draftRows.
|
|
738
|
+
if (draftRowIndex < 0 || draftRowIndex >= this._registry.get(this._draftRows).length) {
|
|
798
739
|
return false;
|
|
799
740
|
}
|
|
800
|
-
const draftRow = this._draftRows
|
|
741
|
+
const draftRow = this._registry.get(this._draftRows)[draftRowIndex];
|
|
801
742
|
const validationErrors = draftRow.validationErrors || [];
|
|
802
743
|
return validationErrors.some((error) => error.path === fieldPath);
|
|
803
744
|
}
|
|
804
|
-
getColumnCount = () => this._projection?.
|
|
745
|
+
getColumnCount = () => this._projection?.getFields().length ?? 0;
|
|
805
746
|
insertRow = () => {
|
|
806
747
|
const result = this._onInsertRow?.();
|
|
807
|
-
if (result === "draft" && this._draftRows.
|
|
748
|
+
if (result === "draft" && this._registry.get(this._draftRows).length === 0) {
|
|
808
749
|
this.createDraftRow();
|
|
809
750
|
}
|
|
810
751
|
return result ?? "final";
|
|
@@ -822,16 +763,16 @@ var TableModel = class extends Resource2 {
|
|
|
822
763
|
valid: validationErrors.length === 0,
|
|
823
764
|
validationErrors
|
|
824
765
|
};
|
|
825
|
-
this._draftRows
|
|
826
|
-
...this._draftRows
|
|
766
|
+
this._registry.set(this._draftRows, [
|
|
767
|
+
...this._registry.get(this._draftRows),
|
|
827
768
|
draftRow
|
|
828
|
-
];
|
|
769
|
+
]);
|
|
829
770
|
}
|
|
830
771
|
validateDraftRow(draftRowIndex) {
|
|
831
|
-
if (draftRowIndex < 0 || draftRowIndex >= this._draftRows.
|
|
772
|
+
if (draftRowIndex < 0 || draftRowIndex >= this._registry.get(this._draftRows).length) {
|
|
832
773
|
return;
|
|
833
774
|
}
|
|
834
|
-
const draftRow = this._draftRows
|
|
775
|
+
const draftRow = this._registry.get(this._draftRows)[draftRowIndex];
|
|
835
776
|
const validationErrors = this.validateDraftRowData(draftRow.data);
|
|
836
777
|
const updatedDraftRow = {
|
|
837
778
|
...draftRow,
|
|
@@ -839,69 +780,71 @@ var TableModel = class extends Resource2 {
|
|
|
839
780
|
validationErrors
|
|
840
781
|
};
|
|
841
782
|
const newDraftRows = [
|
|
842
|
-
...this._draftRows
|
|
783
|
+
...this._registry.get(this._draftRows)
|
|
843
784
|
];
|
|
844
785
|
newDraftRows[draftRowIndex] = updatedDraftRow;
|
|
845
|
-
this._draftRows
|
|
786
|
+
this._registry.set(this._draftRows, newDraftRows);
|
|
846
787
|
}
|
|
847
788
|
validateDraftRowData(data) {
|
|
848
789
|
const schema = toEffectSchema(this._projection.baseSchema);
|
|
849
790
|
return validateSchema(schema, data) || [];
|
|
850
791
|
}
|
|
851
792
|
commitDraftRow = (draftRowIndex) => {
|
|
852
|
-
if (draftRowIndex < 0 || draftRowIndex >= this._draftRows.
|
|
793
|
+
if (draftRowIndex < 0 || draftRowIndex >= this._registry.get(this._draftRows).length) {
|
|
853
794
|
return false;
|
|
854
795
|
}
|
|
855
|
-
const draftRow = this._draftRows
|
|
796
|
+
const draftRow = this._registry.get(this._draftRows)[draftRowIndex];
|
|
856
797
|
if (!draftRow.valid) {
|
|
857
798
|
return false;
|
|
858
799
|
}
|
|
859
800
|
const insertRowResult = this._onInsertRow?.(draftRow.data);
|
|
860
801
|
if (insertRowResult === "final") {
|
|
861
|
-
const newDraftRows = this._draftRows.
|
|
862
|
-
this._draftRows
|
|
802
|
+
const newDraftRows = this._registry.get(this._draftRows).filter((_, index) => index !== draftRowIndex);
|
|
803
|
+
this._registry.set(this._draftRows, newDraftRows);
|
|
863
804
|
}
|
|
864
805
|
return insertRowResult === "final";
|
|
865
806
|
};
|
|
866
807
|
deleteRow = (rowIndex) => {
|
|
867
|
-
const
|
|
868
|
-
const obj =
|
|
808
|
+
const sortedRows = this._registry.get(this._sortedRows);
|
|
809
|
+
const obj = sortedRows[rowIndex];
|
|
869
810
|
const objectsToDelete = [];
|
|
870
|
-
if (this.features.selection.enabled && this._selection.hasSelection
|
|
811
|
+
if (this.features.selection.enabled && this._selection.hasSelection) {
|
|
871
812
|
const selectedRows = this._selection.getSelectedRows();
|
|
872
813
|
objectsToDelete.push(...selectedRows);
|
|
873
|
-
} else {
|
|
814
|
+
} else if (obj) {
|
|
874
815
|
objectsToDelete.push(obj);
|
|
875
816
|
}
|
|
876
|
-
|
|
817
|
+
if (objectsToDelete.length > 0) {
|
|
818
|
+
this._onDeleteRows?.(rowIndex, objectsToDelete);
|
|
819
|
+
}
|
|
877
820
|
};
|
|
878
821
|
handleRowAction = (actionId, rowIndex) => {
|
|
879
822
|
if (!this._onRowAction) {
|
|
880
823
|
return;
|
|
881
824
|
}
|
|
882
|
-
const
|
|
883
|
-
const data =
|
|
825
|
+
const sortedRows = this._registry.get(this._sortedRows);
|
|
826
|
+
const data = sortedRows[rowIndex];
|
|
884
827
|
if (data) {
|
|
885
828
|
this._onRowAction(actionId, data);
|
|
886
829
|
}
|
|
887
830
|
};
|
|
888
831
|
getCellData = (cell) => {
|
|
889
832
|
const { col, row, plane } = cell;
|
|
890
|
-
const fields = this._projection?.
|
|
833
|
+
const fields = this._projection?.getFields() ?? [];
|
|
891
834
|
if (col < 0 || col >= fields.length) {
|
|
892
835
|
return void 0;
|
|
893
836
|
}
|
|
894
837
|
const field = fields[col];
|
|
895
838
|
let value;
|
|
896
839
|
if (plane === "frozenRowsEnd") {
|
|
897
|
-
if (row >= 0 && row < this._draftRows.
|
|
898
|
-
value =
|
|
840
|
+
if (row >= 0 && row < this._registry.get(this._draftRows).length) {
|
|
841
|
+
value = getValue(this._registry.get(this._draftRows)[row].data, field.path);
|
|
899
842
|
} else {
|
|
900
843
|
return void 0;
|
|
901
844
|
}
|
|
902
845
|
} else {
|
|
903
|
-
const
|
|
904
|
-
value =
|
|
846
|
+
const sortedRows = this._registry.get(this._sortedRows);
|
|
847
|
+
value = getValue(sortedRows[row], field.path);
|
|
905
848
|
}
|
|
906
849
|
if (value == null) {
|
|
907
850
|
return "";
|
|
@@ -912,7 +855,7 @@ var TableModel = class extends Resource2 {
|
|
|
912
855
|
if (!field.referencePath) {
|
|
913
856
|
return "";
|
|
914
857
|
}
|
|
915
|
-
return
|
|
858
|
+
return getValue(value.target, field.referencePath);
|
|
916
859
|
}
|
|
917
860
|
default: {
|
|
918
861
|
return formatForEditing({
|
|
@@ -924,7 +867,7 @@ var TableModel = class extends Resource2 {
|
|
|
924
867
|
}
|
|
925
868
|
};
|
|
926
869
|
validateCellData = async ({ col, row, plane }, value) => {
|
|
927
|
-
const fields = this._projection?.
|
|
870
|
+
const fields = this._projection?.getFields() ?? [];
|
|
928
871
|
if (col < 0 || col >= fields.length) {
|
|
929
872
|
return {
|
|
930
873
|
valid: false,
|
|
@@ -936,14 +879,14 @@ var TableModel = class extends Resource2 {
|
|
|
936
879
|
const transformedValue = editorTextToCellValue(props, value);
|
|
937
880
|
const isDraftRow = plane === "frozenRowsEnd";
|
|
938
881
|
if (isDraftRow) {
|
|
939
|
-
const isInvalidDraftRowIndex = row < 0 || row >= this._draftRows.
|
|
882
|
+
const isInvalidDraftRowIndex = row < 0 || row >= this._registry.get(this._draftRows).length;
|
|
940
883
|
if (isInvalidDraftRowIndex) {
|
|
941
884
|
return {
|
|
942
885
|
valid: false,
|
|
943
886
|
error: "Invalid draft row index"
|
|
944
887
|
};
|
|
945
888
|
}
|
|
946
|
-
const draftRow = this._draftRows
|
|
889
|
+
const draftRow = this._registry.get(this._draftRows)[row];
|
|
947
890
|
const snapshot = {
|
|
948
891
|
...draftRow.data
|
|
949
892
|
};
|
|
@@ -962,11 +905,10 @@ var TableModel = class extends Resource2 {
|
|
|
962
905
|
valid: true
|
|
963
906
|
};
|
|
964
907
|
} else {
|
|
965
|
-
const
|
|
966
|
-
const currentRow = this._rows.value[rowIdx];
|
|
908
|
+
const currentRow = this._registry.get(this._rows)[row];
|
|
967
909
|
invariant(currentRow, "Invalid row index", {
|
|
968
910
|
F: __dxlog_file,
|
|
969
|
-
L:
|
|
911
|
+
L: 692,
|
|
970
912
|
S: this,
|
|
971
913
|
A: [
|
|
972
914
|
"currentRow",
|
|
@@ -975,10 +917,10 @@ var TableModel = class extends Resource2 {
|
|
|
975
917
|
});
|
|
976
918
|
const snapshot = getSnapshot(currentRow);
|
|
977
919
|
setValue(snapshot, field.path, transformedValue);
|
|
978
|
-
const schema =
|
|
920
|
+
const schema = Obj.getSchema(currentRow);
|
|
979
921
|
invariant(schema, void 0, {
|
|
980
922
|
F: __dxlog_file,
|
|
981
|
-
L:
|
|
923
|
+
L: 698,
|
|
982
924
|
S: this,
|
|
983
925
|
A: [
|
|
984
926
|
"schema",
|
|
@@ -990,7 +932,7 @@ var TableModel = class extends Resource2 {
|
|
|
990
932
|
const error = validationResult[0];
|
|
991
933
|
invariant(error.path === field.path, void 0, {
|
|
992
934
|
F: __dxlog_file,
|
|
993
|
-
L:
|
|
935
|
+
L: 703,
|
|
994
936
|
S: this,
|
|
995
937
|
A: [
|
|
996
938
|
"error.path === field.path",
|
|
@@ -1009,24 +951,27 @@ var TableModel = class extends Resource2 {
|
|
|
1009
951
|
};
|
|
1010
952
|
setCellData = (cell, value) => {
|
|
1011
953
|
const { col, row, plane } = cell;
|
|
1012
|
-
const fields = this._projection?.
|
|
954
|
+
const fields = this._projection?.getFields() ?? [];
|
|
1013
955
|
if (col < 0 || col >= fields.length) {
|
|
1014
956
|
return;
|
|
1015
957
|
}
|
|
1016
958
|
const field = fields[col];
|
|
1017
959
|
const { props } = this._projection.getFieldProjection(field.id);
|
|
1018
960
|
const transformedValue = editorTextToCellValue(props, value);
|
|
1019
|
-
if (props.format === Format2.TypeFormat.Ref && !
|
|
961
|
+
if (props.format === Format2.TypeFormat.Ref && !Obj.isObject(value)) {
|
|
1020
962
|
return;
|
|
1021
963
|
}
|
|
1022
964
|
if (plane === "frozenRowsEnd") {
|
|
1023
|
-
if (row >= 0 && row < this._draftRows.
|
|
1024
|
-
setValue(this._draftRows
|
|
965
|
+
if (row >= 0 && row < this._registry.get(this._draftRows).length) {
|
|
966
|
+
setValue(this._registry.get(this._draftRows)[row].data, field.path, transformedValue);
|
|
1025
967
|
this.validateDraftRow(row);
|
|
1026
968
|
}
|
|
1027
969
|
} else {
|
|
1028
|
-
const
|
|
1029
|
-
|
|
970
|
+
const sortedRows = this._registry.get(this._sortedRows);
|
|
971
|
+
const rowData = sortedRows[row];
|
|
972
|
+
this._change.row(rowData, (mutableRow) => {
|
|
973
|
+
setValue(mutableRow, field.path, transformedValue);
|
|
974
|
+
});
|
|
1030
975
|
}
|
|
1031
976
|
};
|
|
1032
977
|
/**
|
|
@@ -1035,12 +980,15 @@ var TableModel = class extends Resource2 {
|
|
|
1035
980
|
* @param {(value: any) => any} update - A function that takes the current value and returns the updated value.
|
|
1036
981
|
*/
|
|
1037
982
|
updateCellData({ col, row }, update) {
|
|
1038
|
-
const
|
|
1039
|
-
const fields = this._projection?.fields ?? [];
|
|
983
|
+
const fields = this._projection?.getFields() ?? [];
|
|
1040
984
|
const field = fields[col];
|
|
1041
|
-
const
|
|
985
|
+
const sortedRows = this._registry.get(this._sortedRows);
|
|
986
|
+
const rowData = sortedRows[row];
|
|
987
|
+
const value = getValue(rowData, field.path);
|
|
1042
988
|
const updatedValue = update(value);
|
|
1043
|
-
|
|
989
|
+
this._change.row(rowData, (mutableRow) => {
|
|
990
|
+
setValue(mutableRow, field.path, updatedValue);
|
|
991
|
+
});
|
|
1044
992
|
}
|
|
1045
993
|
/**
|
|
1046
994
|
* Gets the domain-appropriate cell type for a given cell position.
|
|
@@ -1074,7 +1022,7 @@ var TableModel = class extends Resource2 {
|
|
|
1074
1022
|
if (!this._projection) {
|
|
1075
1023
|
return;
|
|
1076
1024
|
}
|
|
1077
|
-
const field = this._projection?.
|
|
1025
|
+
const field = this._projection?.getFields().find((field2) => field2.id === fieldId);
|
|
1078
1026
|
if (field && this._onDeleteColumn) {
|
|
1079
1027
|
this._onDeleteColumn(field.id);
|
|
1080
1028
|
}
|
|
@@ -1083,12 +1031,14 @@ var TableModel = class extends Resource2 {
|
|
|
1083
1031
|
// Resize
|
|
1084
1032
|
//
|
|
1085
1033
|
setColumnWidth(columnIndex, width) {
|
|
1086
|
-
const fields = this._projection?.
|
|
1034
|
+
const fields = this._projection?.getFields() ?? [];
|
|
1087
1035
|
if (columnIndex < fields.length) {
|
|
1088
1036
|
const newWidth = Math.max(0, width);
|
|
1089
1037
|
const field = fields[columnIndex];
|
|
1090
1038
|
if (field) {
|
|
1091
|
-
this.table
|
|
1039
|
+
this._change.table((mutableTable) => {
|
|
1040
|
+
mutableTable.sizes[field.path] = newWidth;
|
|
1041
|
+
});
|
|
1092
1042
|
}
|
|
1093
1043
|
}
|
|
1094
1044
|
}
|
|
@@ -1104,17 +1054,87 @@ var TableModel = class extends Resource2 {
|
|
|
1104
1054
|
this._pinnedRows.bottom = this._pinnedRows.bottom.filter((index) => index !== rowIndex);
|
|
1105
1055
|
}
|
|
1106
1056
|
//
|
|
1107
|
-
//
|
|
1057
|
+
// Sort operations
|
|
1108
1058
|
//
|
|
1059
|
+
/**
|
|
1060
|
+
* Sets the sort order for a field in-memory (local to this instance).
|
|
1061
|
+
* Use saveView() to persist the sort to the view query.
|
|
1062
|
+
*/
|
|
1063
|
+
setSort(fieldId, direction) {
|
|
1064
|
+
const field = this._projection.getFields().find((f) => f.id === fieldId);
|
|
1065
|
+
if (!field) {
|
|
1066
|
+
return;
|
|
1067
|
+
}
|
|
1068
|
+
this._registry.set(this._inMemorySort, {
|
|
1069
|
+
fieldId,
|
|
1070
|
+
direction
|
|
1071
|
+
});
|
|
1072
|
+
}
|
|
1073
|
+
/**
|
|
1074
|
+
* Toggles the sort direction for a field in-memory.
|
|
1075
|
+
*/
|
|
1076
|
+
toggleSort(fieldId) {
|
|
1077
|
+
const currentSort = this.getSorting();
|
|
1078
|
+
if (!currentSort || currentSort.fieldId !== fieldId) {
|
|
1079
|
+
this.setSort(fieldId, "asc");
|
|
1080
|
+
return;
|
|
1081
|
+
}
|
|
1082
|
+
const newDirection = currentSort.direction === "asc" ? "desc" : "asc";
|
|
1083
|
+
this.setSort(fieldId, newDirection);
|
|
1084
|
+
}
|
|
1085
|
+
/**
|
|
1086
|
+
* Clears the in-memory sort order.
|
|
1087
|
+
*/
|
|
1088
|
+
clearSort() {
|
|
1089
|
+
this._registry.set(this._inMemorySort, void 0);
|
|
1090
|
+
}
|
|
1091
|
+
/**
|
|
1092
|
+
* Saves the current in-memory sort to the view query AST.
|
|
1093
|
+
* This persists the sort so it becomes the default for all viewers.
|
|
1094
|
+
*/
|
|
1109
1095
|
saveView() {
|
|
1110
|
-
this.
|
|
1096
|
+
const view = this.view;
|
|
1097
|
+
const inMemorySort = this._registry.get(this._inMemorySort);
|
|
1098
|
+
const viewSnapshot = Obj.getSnapshot(view);
|
|
1099
|
+
const currentQuery = Query.fromAst(viewSnapshot.query.ast);
|
|
1100
|
+
const baseQuery = this._getBaseQuery(currentQuery);
|
|
1101
|
+
if (inMemorySort) {
|
|
1102
|
+
const field = this._projection.getFields().find((f) => f.id === inMemorySort.fieldId);
|
|
1103
|
+
if (field) {
|
|
1104
|
+
const newQuery = baseQuery.orderBy(Order.property(field.path, inMemorySort.direction));
|
|
1105
|
+
Obj.change(view, (v) => {
|
|
1106
|
+
v.query.ast = newQuery.ast;
|
|
1107
|
+
});
|
|
1108
|
+
}
|
|
1109
|
+
} else {
|
|
1110
|
+
Obj.change(view, (v) => {
|
|
1111
|
+
v.query.ast = baseQuery.ast;
|
|
1112
|
+
});
|
|
1113
|
+
}
|
|
1114
|
+
this._registry.set(this._inMemorySort, void 0);
|
|
1115
|
+
}
|
|
1116
|
+
/**
|
|
1117
|
+
* Extracts the base query without order clauses.
|
|
1118
|
+
*/
|
|
1119
|
+
_getBaseQuery(query) {
|
|
1120
|
+
const ast = query.ast;
|
|
1121
|
+
if (ast.type === "order") {
|
|
1122
|
+
return Query.fromAst(ast.query);
|
|
1123
|
+
}
|
|
1124
|
+
return query;
|
|
1125
|
+
}
|
|
1126
|
+
/**
|
|
1127
|
+
* Checks if the view has unsaved changes (in-memory sort differs from persisted sort).
|
|
1128
|
+
*/
|
|
1129
|
+
getViewDirty() {
|
|
1130
|
+
return this._registry.get(this._viewDirty);
|
|
1111
1131
|
}
|
|
1112
1132
|
};
|
|
1113
1133
|
var editorTextToCellValue = (props, value) => {
|
|
1114
1134
|
switch (props.format) {
|
|
1115
1135
|
case Format2.TypeFormat.Ref: {
|
|
1116
|
-
if (
|
|
1117
|
-
return
|
|
1136
|
+
if (Obj.isObject(value)) {
|
|
1137
|
+
return Ref.make(value);
|
|
1118
1138
|
} else {
|
|
1119
1139
|
return value;
|
|
1120
1140
|
}
|
|
@@ -1141,104 +1161,40 @@ var editorTextToCellValue = (props, value) => {
|
|
|
1141
1161
|
}
|
|
1142
1162
|
};
|
|
1143
1163
|
|
|
1144
|
-
// src/model/modal-controller.ts
|
|
1145
|
-
import { signal as signal4 } from "@preact/signals-core";
|
|
1146
|
-
var ModalController = class {
|
|
1147
|
-
_state = signal4({
|
|
1148
|
-
type: "closed"
|
|
1149
|
-
});
|
|
1150
|
-
_triggerRef = {
|
|
1151
|
-
current: null
|
|
1152
|
-
};
|
|
1153
|
-
get state() {
|
|
1154
|
-
return this._state;
|
|
1155
|
-
}
|
|
1156
|
-
get trigger() {
|
|
1157
|
-
return this._triggerRef;
|
|
1158
|
-
}
|
|
1159
|
-
setTrigger = (trigger) => {
|
|
1160
|
-
this._triggerRef.current = trigger;
|
|
1161
|
-
};
|
|
1162
|
-
showRowMenu = (rowIndex) => {
|
|
1163
|
-
this._state.value = {
|
|
1164
|
-
type: "row",
|
|
1165
|
-
rowIndex
|
|
1166
|
-
};
|
|
1167
|
-
};
|
|
1168
|
-
showColumnMenu = (fieldId) => {
|
|
1169
|
-
this._state.value = {
|
|
1170
|
-
type: "column",
|
|
1171
|
-
fieldId
|
|
1172
|
-
};
|
|
1173
|
-
};
|
|
1174
|
-
showColumnCreator = () => {
|
|
1175
|
-
this._state.value = {
|
|
1176
|
-
type: "columnSettings",
|
|
1177
|
-
mode: {
|
|
1178
|
-
type: "create"
|
|
1179
|
-
}
|
|
1180
|
-
};
|
|
1181
|
-
};
|
|
1182
|
-
showReferencePanel = (targetId, schemaId) => {
|
|
1183
|
-
this._state.value = {
|
|
1184
|
-
type: "refPanel",
|
|
1185
|
-
targetId,
|
|
1186
|
-
typename: schemaId
|
|
1187
|
-
};
|
|
1188
|
-
};
|
|
1189
|
-
openColumnSettings = () => {
|
|
1190
|
-
if (this._state.value.type === "column") {
|
|
1191
|
-
const fieldId = this._state.value.fieldId;
|
|
1192
|
-
requestAnimationFrame(() => {
|
|
1193
|
-
this._state.value = {
|
|
1194
|
-
type: "columnSettings",
|
|
1195
|
-
mode: {
|
|
1196
|
-
type: "edit",
|
|
1197
|
-
fieldId
|
|
1198
|
-
}
|
|
1199
|
-
};
|
|
1200
|
-
});
|
|
1201
|
-
}
|
|
1202
|
-
};
|
|
1203
|
-
close = () => {
|
|
1204
|
-
this._state.value = {
|
|
1205
|
-
type: "closed"
|
|
1206
|
-
};
|
|
1207
|
-
};
|
|
1208
|
-
};
|
|
1209
|
-
|
|
1210
1164
|
// src/model/table-presentation.ts
|
|
1211
|
-
import {
|
|
1165
|
+
import { Atom as Atom3 } from "@effect-atom/atom-react";
|
|
1212
1166
|
import * as Predicate from "effect/Predicate";
|
|
1213
|
-
import { Obj as
|
|
1214
|
-
import { Format as Format3, TypeEnum
|
|
1215
|
-
import { cellClassesForFieldType, formatForDisplay
|
|
1167
|
+
import { Obj as Obj2 } from "@dxos/echo";
|
|
1168
|
+
import { Format as Format3, TypeEnum, getValue as getValue2 } from "@dxos/echo/internal";
|
|
1169
|
+
import { cellClassesForFieldType, formatForDisplay } from "@dxos/react-ui-form";
|
|
1216
1170
|
import { toPlaneCellIndex } from "@dxos/react-ui-grid";
|
|
1217
|
-
import { mx } from "@dxos/react-ui-theme";
|
|
1218
1171
|
import { VIEW_FIELD_LIMIT } from "@dxos/schema";
|
|
1172
|
+
import { mx } from "@dxos/ui-theme";
|
|
1219
1173
|
var TablePresentation = class {
|
|
1220
1174
|
model;
|
|
1175
|
+
_registry;
|
|
1221
1176
|
_visibleRange;
|
|
1222
1177
|
fieldProjectionCache = /* @__PURE__ */ new Map();
|
|
1223
|
-
constructor(
|
|
1224
|
-
start: {
|
|
1225
|
-
row: 0,
|
|
1226
|
-
col: 0
|
|
1227
|
-
},
|
|
1228
|
-
end: {
|
|
1229
|
-
row: 0,
|
|
1230
|
-
col: 0
|
|
1231
|
-
}
|
|
1232
|
-
})) {
|
|
1178
|
+
constructor(registry, model) {
|
|
1233
1179
|
this.model = model;
|
|
1234
|
-
this.
|
|
1180
|
+
this._registry = registry;
|
|
1181
|
+
this._visibleRange = Atom3.make({
|
|
1182
|
+
start: {
|
|
1183
|
+
row: 0,
|
|
1184
|
+
col: 0
|
|
1185
|
+
},
|
|
1186
|
+
end: {
|
|
1187
|
+
row: 0,
|
|
1188
|
+
col: 0
|
|
1189
|
+
}
|
|
1190
|
+
});
|
|
1235
1191
|
}
|
|
1236
1192
|
getCells(range, plane) {
|
|
1237
1193
|
this.fieldProjectionCache.clear();
|
|
1238
1194
|
let cells;
|
|
1239
1195
|
switch (plane) {
|
|
1240
1196
|
case "grid":
|
|
1241
|
-
this._visibleRange
|
|
1197
|
+
this._registry.set(this._visibleRange, range);
|
|
1242
1198
|
cells = this.getMainGridCells(range);
|
|
1243
1199
|
break;
|
|
1244
1200
|
case "frozenRowsStart":
|
|
@@ -1290,7 +1246,7 @@ var TablePresentation = class {
|
|
|
1290
1246
|
const { props } = this.getFieldProjection(field.id);
|
|
1291
1247
|
const cell = {
|
|
1292
1248
|
get value() {
|
|
1293
|
-
const value =
|
|
1249
|
+
const value = getValue2(obj, field.path);
|
|
1294
1250
|
if (value == null) {
|
|
1295
1251
|
return "";
|
|
1296
1252
|
}
|
|
@@ -1307,10 +1263,10 @@ var TablePresentation = class {
|
|
|
1307
1263
|
if (!field.referencePath) {
|
|
1308
1264
|
return "";
|
|
1309
1265
|
}
|
|
1310
|
-
return
|
|
1266
|
+
return getValue2(value.target, field.referencePath);
|
|
1311
1267
|
}
|
|
1312
1268
|
default: {
|
|
1313
|
-
return
|
|
1269
|
+
return formatForDisplay({
|
|
1314
1270
|
type: props.type,
|
|
1315
1271
|
format: props.format,
|
|
1316
1272
|
value
|
|
@@ -1329,8 +1285,8 @@ var TablePresentation = class {
|
|
|
1329
1285
|
classes.push(rowSelectionClasses);
|
|
1330
1286
|
}
|
|
1331
1287
|
cell.className = mx(classes);
|
|
1332
|
-
if (props.type ===
|
|
1333
|
-
const targetArray =
|
|
1288
|
+
if (props.type === TypeEnum.Array) {
|
|
1289
|
+
const targetArray = getValue2(obj, field.path);
|
|
1334
1290
|
if (targetArray && Array.isArray(targetArray)) {
|
|
1335
1291
|
const getLabel = (value) => {
|
|
1336
1292
|
if (!value) {
|
|
@@ -1357,14 +1313,14 @@ var TablePresentation = class {
|
|
|
1357
1313
|
}
|
|
1358
1314
|
}
|
|
1359
1315
|
if (props.format === Format3.TypeFormat.Ref && props.referenceSchema) {
|
|
1360
|
-
const targetObj =
|
|
1316
|
+
const targetObj = getValue2(obj, field.path)?.target;
|
|
1361
1317
|
if (targetObj) {
|
|
1362
|
-
const dxn =
|
|
1318
|
+
const dxn = Obj2.getDXN(targetObj)?.toString();
|
|
1363
1319
|
cell.accessoryHtml = `<div role="none" class="absolute inline-end-0 inset-block-0 p-[--dx-grid-cell-content-padding-block]"><dx-anchor refId=${dxn} class="dx-button is-6 aspect-square min-bs-0" data-dx-grid-action="accessory"><dx-icon icon="ph--link-simple--regular"/></dx-anchor></div>`;
|
|
1364
1320
|
}
|
|
1365
1321
|
}
|
|
1366
1322
|
if (props.format === Format3.TypeFormat.Boolean) {
|
|
1367
|
-
const value =
|
|
1323
|
+
const value = getValue2(obj, field.path);
|
|
1368
1324
|
cell.accessoryHtml = tableControls.switch.render({
|
|
1369
1325
|
colIndex,
|
|
1370
1326
|
rowIndex: displayIndex,
|
|
@@ -1373,7 +1329,7 @@ var TablePresentation = class {
|
|
|
1373
1329
|
cell.readonly = "no-text-select";
|
|
1374
1330
|
}
|
|
1375
1331
|
if (props.format === Format3.TypeFormat.SingleSelect) {
|
|
1376
|
-
const value =
|
|
1332
|
+
const value = getValue2(obj, field.path);
|
|
1377
1333
|
const options = this.model.projection.getFieldProjection(field.id).props.options;
|
|
1378
1334
|
if (options) {
|
|
1379
1335
|
const option = options.find((o) => o.id === value);
|
|
@@ -1383,7 +1339,7 @@ var TablePresentation = class {
|
|
|
1383
1339
|
}
|
|
1384
1340
|
}
|
|
1385
1341
|
if (props.format === Format3.TypeFormat.MultiSelect) {
|
|
1386
|
-
const values =
|
|
1342
|
+
const values = getValue2(obj, field.path);
|
|
1387
1343
|
const options = this.model.projection.getFieldProjection(field.id).props.options;
|
|
1388
1344
|
if (options && values && values.length > 0) {
|
|
1389
1345
|
const tags = values.map((value) => {
|
|
@@ -1406,22 +1362,22 @@ var TablePresentation = class {
|
|
|
1406
1362
|
}
|
|
1407
1363
|
getMainGridCells(range) {
|
|
1408
1364
|
const cells = {};
|
|
1409
|
-
const fields = this.model.projection?.
|
|
1365
|
+
const fields = this.model.projection?.getFields() ?? [];
|
|
1410
1366
|
for (let row = range.start.row; row <= range.end.row && row < this.model.getRowCount(); row++) {
|
|
1411
1367
|
for (let col = range.start.col; col <= range.end.col && col < fields.length; col++) {
|
|
1412
1368
|
const field = fields[col];
|
|
1413
1369
|
if (!field) {
|
|
1414
1370
|
continue;
|
|
1415
1371
|
}
|
|
1416
|
-
this.createDataCell(cells, this.model.
|
|
1372
|
+
this.createDataCell(cells, this.model.getRows()[row], field, col, row);
|
|
1417
1373
|
}
|
|
1418
1374
|
}
|
|
1419
1375
|
return cells;
|
|
1420
1376
|
}
|
|
1421
1377
|
getDraftRowCells(range) {
|
|
1422
1378
|
const cells = {};
|
|
1423
|
-
const fields = this.model.projection?.
|
|
1424
|
-
const draftRows = this.model.
|
|
1379
|
+
const fields = this.model.projection?.getFields() ?? [];
|
|
1380
|
+
const draftRows = this.model.getDraftRows();
|
|
1425
1381
|
if (draftRows.length === 0) {
|
|
1426
1382
|
for (let col = range.start.col; col <= range.end.col && col < fields.length; col++) {
|
|
1427
1383
|
cells[toPlaneCellIndex({
|
|
@@ -1461,11 +1417,11 @@ var TablePresentation = class {
|
|
|
1461
1417
|
}
|
|
1462
1418
|
getHeaderCells(range) {
|
|
1463
1419
|
const cells = {};
|
|
1464
|
-
const fields = this.model.projection?.
|
|
1420
|
+
const fields = this.model.projection?.getFields() ?? [];
|
|
1421
|
+
const currentSort = this.model.getSorting();
|
|
1465
1422
|
for (let col = range.start.col; col <= range.end.col && col < fields.length; col++) {
|
|
1466
1423
|
const { field, props } = this.model.projection.getFieldProjection(fields[col].id);
|
|
1467
|
-
const
|
|
1468
|
-
const direction = sorting?.fieldId === field.id ? sorting.direction : void 0;
|
|
1424
|
+
const direction = currentSort?.fieldId === field.id ? currentSort.direction : void 0;
|
|
1469
1425
|
cells[toPlaneCellIndex({
|
|
1470
1426
|
col,
|
|
1471
1427
|
row: 0
|
|
@@ -1548,7 +1504,7 @@ var TablePresentation = class {
|
|
|
1548
1504
|
accessoryHtml: tableControls.checkbox.render({
|
|
1549
1505
|
rowIndex: 0,
|
|
1550
1506
|
header: true,
|
|
1551
|
-
checked: this.model.selection.
|
|
1507
|
+
checked: this.model.selection.allRowsSelected
|
|
1552
1508
|
}),
|
|
1553
1509
|
className: "!bg-toolbarSurface",
|
|
1554
1510
|
readonly: true,
|
|
@@ -1563,7 +1519,7 @@ var TablePresentation = class {
|
|
|
1563
1519
|
row: 0
|
|
1564
1520
|
})]: {
|
|
1565
1521
|
accessoryHtml: this.model.features.schemaEditable ? tableButtons.addColumn.render({
|
|
1566
|
-
disabled: (this.model.projection?.
|
|
1522
|
+
disabled: (this.model.projection?.getFields()?.length ?? 0) >= VIEW_FIELD_LIMIT
|
|
1567
1523
|
}) : void 0,
|
|
1568
1524
|
className: "!bg-toolbarSurface",
|
|
1569
1525
|
readonly: true,
|
|
@@ -1573,7 +1529,7 @@ var TablePresentation = class {
|
|
|
1573
1529
|
}
|
|
1574
1530
|
getDraftActionCells(range) {
|
|
1575
1531
|
const cells = {};
|
|
1576
|
-
const draftRows = this.model.
|
|
1532
|
+
const draftRows = this.model.getDraftRows();
|
|
1577
1533
|
if (draftRows.length === 0) {
|
|
1578
1534
|
cells[toPlaneCellIndex({
|
|
1579
1535
|
col: 0,
|
|
@@ -1604,7 +1560,7 @@ var TablePresentation = class {
|
|
|
1604
1560
|
}
|
|
1605
1561
|
getDraftIconCells(range) {
|
|
1606
1562
|
const cells = {};
|
|
1607
|
-
const draftRows = this.model.
|
|
1563
|
+
const draftRows = this.model.getDraftRows();
|
|
1608
1564
|
for (let row = range.start.row; row <= range.end.row; row++) {
|
|
1609
1565
|
cells[toPlaneCellIndex({
|
|
1610
1566
|
col: 0,
|
|
@@ -1643,12 +1599,12 @@ var cellClassesForRowSelection = (selected, selectionMode) => {
|
|
|
1643
1599
|
|
|
1644
1600
|
// src/hooks/useAddRow.ts
|
|
1645
1601
|
import { useCallback } from "react";
|
|
1646
|
-
import { Obj as
|
|
1647
|
-
var useAddRow = ({
|
|
1602
|
+
import { Obj as Obj3 } from "@dxos/echo";
|
|
1603
|
+
var useAddRow = ({ db, schema }) => {
|
|
1648
1604
|
return useCallback((data) => {
|
|
1649
|
-
if (
|
|
1605
|
+
if (db && schema) {
|
|
1650
1606
|
try {
|
|
1651
|
-
|
|
1607
|
+
db.add(Obj3.make(schema, data ?? {}));
|
|
1652
1608
|
return "final";
|
|
1653
1609
|
} catch (error) {
|
|
1654
1610
|
return "draft";
|
|
@@ -1656,129 +1612,137 @@ var useAddRow = ({ space, schema }) => {
|
|
|
1656
1612
|
}
|
|
1657
1613
|
return "final";
|
|
1658
1614
|
}, [
|
|
1659
|
-
|
|
1615
|
+
db,
|
|
1660
1616
|
schema
|
|
1661
1617
|
]);
|
|
1662
1618
|
};
|
|
1663
1619
|
|
|
1664
1620
|
// src/hooks/useProjectionModel.ts
|
|
1665
|
-
import {
|
|
1666
|
-
import { Type
|
|
1667
|
-
import {
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1621
|
+
import { useState } from "react";
|
|
1622
|
+
import { Type } from "@dxos/echo";
|
|
1623
|
+
import { useAsyncEffect } from "@dxos/react-ui";
|
|
1624
|
+
import { ProjectionModel, createDirectChangeCallback as createDirectChangeCallback2, createEchoChangeCallback as createEchoChangeCallback2 } from "@dxos/schema";
|
|
1625
|
+
var useProjectionModel = (schema, table, registry) => {
|
|
1626
|
+
const [projection, setProjection] = useState();
|
|
1627
|
+
useAsyncEffect(async () => {
|
|
1628
|
+
if (schema && table) {
|
|
1629
|
+
const view = await table.view.load();
|
|
1630
|
+
const jsonSchema = Type.isMutable(schema) ? schema.jsonSchema : Type.toJsonSchema(schema);
|
|
1631
|
+
const change = Type.isMutable(schema) ? createEchoChangeCallback2(view, schema) : createDirectChangeCallback2(view.projection, jsonSchema);
|
|
1632
|
+
const projection2 = new ProjectionModel({
|
|
1633
|
+
registry,
|
|
1634
|
+
view,
|
|
1635
|
+
baseSchema: jsonSchema,
|
|
1636
|
+
change
|
|
1637
|
+
});
|
|
1638
|
+
projection2.normalizeView();
|
|
1639
|
+
setProjection(projection2);
|
|
1640
|
+
}
|
|
1641
|
+
}, [
|
|
1642
|
+
schema,
|
|
1643
|
+
table,
|
|
1644
|
+
registry
|
|
1645
|
+
]);
|
|
1646
|
+
return projection;
|
|
1647
|
+
};
|
|
1678
1648
|
|
|
1679
1649
|
// src/hooks/useTableModel.ts
|
|
1680
|
-
import {
|
|
1681
|
-
import {
|
|
1682
|
-
import
|
|
1683
|
-
import { useEffect, useMemo as useMemo2, useState } from "react";
|
|
1684
|
-
import { Obj as Obj5 } from "@dxos/echo";
|
|
1650
|
+
import { RegistryContext } from "@effect-atom/atom-react";
|
|
1651
|
+
import { useContext, useEffect, useMemo, useState as useState2 } from "react";
|
|
1652
|
+
import { Obj as Obj4 } from "@dxos/echo";
|
|
1685
1653
|
import { useSelected, useSelectionActions } from "@dxos/react-ui-attention";
|
|
1686
1654
|
import { isNonNullable } from "@dxos/util";
|
|
1687
|
-
var useTableModel = ({ object, projection, rows, rowActions, features, onSelectionChanged, onRowAction, ...props }) => {
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
setModel(model2);
|
|
1712
|
-
});
|
|
1713
|
-
return () => {
|
|
1714
|
-
clearTimeout(t);
|
|
1715
|
-
void model2?.close();
|
|
1716
|
-
};
|
|
1717
|
-
}, [
|
|
1718
|
-
object,
|
|
1719
|
-
projection,
|
|
1720
|
-
features,
|
|
1721
|
-
rowActions,
|
|
1722
|
-
initialSelection
|
|
1723
|
-
]);
|
|
1724
|
-
useEffect(() => {
|
|
1725
|
-
if (rows) {
|
|
1726
|
-
const sortedRows = orderBy2(rows, [
|
|
1727
|
-
(row) => String(row.id)
|
|
1728
|
-
], [
|
|
1729
|
-
"asc"
|
|
1730
|
-
]);
|
|
1731
|
-
model?.setRows(sortedRows);
|
|
1732
|
-
}
|
|
1733
|
-
}, [
|
|
1734
|
-
model,
|
|
1735
|
-
rows
|
|
1736
|
-
]);
|
|
1737
|
-
const { multiSelect, clear } = useSelectionActions([
|
|
1738
|
-
model?.id
|
|
1739
|
-
].filter(isNonNullable));
|
|
1740
|
-
useEffect(() => {
|
|
1741
|
-
if (!model) {
|
|
1742
|
-
return;
|
|
1743
|
-
}
|
|
1744
|
-
const unsubscribe = effect3(() => {
|
|
1745
|
-
const selectedItems = [
|
|
1746
|
-
...model.selection.selection.value
|
|
1747
|
-
];
|
|
1748
|
-
multiSelect(selectedItems);
|
|
1749
|
-
onSelectionChanged?.(selectedItems);
|
|
1655
|
+
var useTableModel = ({ object, projection, db, rows, rowActions, features, onSelectionChanged, onRowAction, ...props }) => {
|
|
1656
|
+
const registry = useContext(RegistryContext);
|
|
1657
|
+
const selected = useSelected(object && Obj4.getDXN(object).toString(), "multi");
|
|
1658
|
+
const initialSelection = useMemo(() => selected, [
|
|
1659
|
+
object
|
|
1660
|
+
]);
|
|
1661
|
+
const [model, setModel] = useState2();
|
|
1662
|
+
useEffect(() => {
|
|
1663
|
+
if (!object || !projection) {
|
|
1664
|
+
return;
|
|
1665
|
+
}
|
|
1666
|
+
let model2;
|
|
1667
|
+
const t = setTimeout(async () => {
|
|
1668
|
+
model2 = new TableModel({
|
|
1669
|
+
registry,
|
|
1670
|
+
object,
|
|
1671
|
+
projection,
|
|
1672
|
+
db,
|
|
1673
|
+
change: createEchoChangeCallback(object),
|
|
1674
|
+
features,
|
|
1675
|
+
rowActions,
|
|
1676
|
+
initialSelection,
|
|
1677
|
+
onRowAction,
|
|
1678
|
+
...props
|
|
1750
1679
|
});
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1680
|
+
await model2.open();
|
|
1681
|
+
setModel(model2);
|
|
1682
|
+
});
|
|
1683
|
+
return () => {
|
|
1684
|
+
clearTimeout(t);
|
|
1685
|
+
void model2?.close();
|
|
1686
|
+
};
|
|
1687
|
+
}, [
|
|
1688
|
+
registry,
|
|
1689
|
+
object,
|
|
1690
|
+
projection,
|
|
1691
|
+
features,
|
|
1692
|
+
rowActions,
|
|
1693
|
+
initialSelection
|
|
1694
|
+
]);
|
|
1695
|
+
useEffect(() => {
|
|
1696
|
+
if (rows && model) {
|
|
1697
|
+
model.setRows(rows);
|
|
1698
|
+
}
|
|
1699
|
+
}, [
|
|
1700
|
+
model,
|
|
1701
|
+
rows
|
|
1702
|
+
]);
|
|
1703
|
+
const { multiSelect, clear } = useSelectionActions([
|
|
1704
|
+
model?.id
|
|
1705
|
+
].filter(isNonNullable));
|
|
1706
|
+
useEffect(() => {
|
|
1707
|
+
if (!model) {
|
|
1708
|
+
return;
|
|
1709
|
+
}
|
|
1710
|
+
const unsubscribe = registry.subscribe(model.selection.selectionAtom, () => {
|
|
1711
|
+
const selectedItems = [
|
|
1712
|
+
...model.selection.selection
|
|
1713
|
+
];
|
|
1714
|
+
multiSelect(selectedItems);
|
|
1715
|
+
onSelectionChanged?.(selectedItems);
|
|
1716
|
+
});
|
|
1717
|
+
return () => {
|
|
1718
|
+
clear();
|
|
1719
|
+
unsubscribe();
|
|
1720
|
+
};
|
|
1721
|
+
}, [
|
|
1722
|
+
registry,
|
|
1723
|
+
model,
|
|
1724
|
+
onSelectionChanged
|
|
1725
|
+
]);
|
|
1726
|
+
return model;
|
|
1763
1727
|
};
|
|
1764
1728
|
|
|
1765
1729
|
export {
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1730
|
+
extractOrder,
|
|
1731
|
+
getSortValue,
|
|
1732
|
+
compareValues,
|
|
1769
1733
|
BUTTON_IDENTIFIERS,
|
|
1770
1734
|
tableButtons,
|
|
1771
1735
|
CONTROL_IDENTIFIERS,
|
|
1772
1736
|
CheckboxStory,
|
|
1773
1737
|
SwitchStory,
|
|
1774
1738
|
tableControls,
|
|
1775
|
-
|
|
1739
|
+
createEchoChangeCallback,
|
|
1740
|
+
createDirectChangeCallback,
|
|
1776
1741
|
TableModel,
|
|
1777
|
-
ModalController,
|
|
1778
1742
|
TablePresentation,
|
|
1779
1743
|
cellClassesForRowSelection,
|
|
1780
1744
|
useAddRow,
|
|
1781
1745
|
useProjectionModel,
|
|
1782
1746
|
useTableModel
|
|
1783
1747
|
};
|
|
1784
|
-
//# sourceMappingURL=chunk-
|
|
1748
|
+
//# sourceMappingURL=chunk-2ATAIIAO.mjs.map
|