@dxos/react-ui-table 0.8.4-main.c4373fc → 0.8.4-main.c85a9c8dae
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-DSR2MRZB.mjs +80 -0
- package/dist/lib/browser/chunk-DSR2MRZB.mjs.map +7 -0
- package/dist/lib/browser/chunk-PTHMYAAM.mjs +1751 -0
- package/dist/lib/browser/chunk-PTHMYAAM.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +1160 -1024
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +30 -42
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/browser/types/index.mjs +3 -3
- package/dist/lib/node-esm/chunk-GI72MQSG.mjs +1753 -0
- package/dist/lib/node-esm/chunk-GI72MQSG.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-HTZJSULI.mjs +81 -0
- package/dist/lib/node-esm/chunk-HTZJSULI.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +1160 -1024
- 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 +30 -42
- package/dist/lib/node-esm/testing/index.mjs.map +3 -3
- package/dist/lib/node-esm/types/index.mjs +3 -3
- 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 +7 -7
- package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts +4 -18
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/Relations.stories.d.ts +12 -9
- 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 +12 -12
- package/dist/types/src/components/Table/Table.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.stories.d.ts +71 -5
- 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 +4 -4
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TableToolbar/TableToolbar.d.ts +5 -5
- package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +22 -15
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/hooks/index.d.ts +2 -1
- package/dist/types/src/hooks/index.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 +6 -0
- package/dist/types/src/hooks/useProjectionModel.d.ts.map +1 -0
- package/dist/types/src/hooks/useTableModel.d.ts +9 -9
- 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 +105 -25
- 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/playwright/TableManager.d.ts +2 -1
- package/dist/types/src/playwright/TableManager.d.ts.map +1 -1
- package/dist/types/src/testing/useTestTableModel.d.ts +18 -15
- package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +4 -4
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/Table.d.ts +31 -0
- package/dist/types/src/types/Table.d.ts.map +1 -0
- package/dist/types/src/types/index.d.ts +1 -1
- package/dist/types/src/types/index.d.ts.map +1 -1
- package/dist/types/src/util/dynamic-table.d.ts +14 -11
- package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
- package/dist/types/src/util/dynamic-table.test.d.ts +2 -0
- package/dist/types/src/util/dynamic-table.test.d.ts.map +1 -0
- 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 +54 -54
- package/src/components/Table/ColumnActionsMenu.tsx +10 -6
- package/src/components/Table/ColumnSettings.tsx +8 -9
- package/src/components/Table/DynamicTable.stories.tsx +16 -16
- package/src/components/Table/DynamicTable.tsx +29 -21
- package/src/components/Table/Relations.stories.tsx +52 -60
- package/src/components/Table/RowActionsMenu.tsx +24 -15
- package/src/components/Table/Table.stories.tsx +114 -96
- package/src/components/Table/Table.tsx +326 -300
- package/src/components/TableCellEditor/CellValidationMessage.tsx +5 -5
- package/src/components/TableCellEditor/FormCellEditor.tsx +117 -134
- package/src/components/TableCellEditor/TableCellEditor.stories.tsx +18 -13
- package/src/components/TableCellEditor/TableCellEditor.tsx +31 -31
- package/src/components/TableToolbar/TableToolbar.tsx +53 -37
- package/src/components/testing.ts +38 -12
- package/src/hooks/index.ts +2 -1
- package/src/hooks/useAddRow.ts +8 -10
- package/src/hooks/useProjectionModel.ts +39 -0
- package/src/hooks/useTableModel.ts +36 -34
- package/src/model/modal-controller.ts +23 -12
- package/src/model/selection-model.ts +75 -44
- package/src/model/table-model.test.ts +82 -54
- package/src/model/table-model.ts +436 -170
- package/src/model/table-presentation.test.ts +59 -49
- package/src/model/table-presentation.ts +43 -37
- package/src/playwright/TableManager.ts +13 -6
- package/src/playwright/playwright.config.ts +1 -1
- package/src/playwright/smoke.spec.ts +56 -10
- package/src/testing/useTestTableModel.ts +45 -38
- package/src/translations.ts +3 -3
- package/src/types/Table.ts +95 -0
- package/src/types/index.ts +1 -1
- package/src/util/dynamic-table.test.ts +70 -0
- package/src/util/dynamic-table.ts +41 -28
- package/src/util/index.ts +2 -0
- package/src/util/query-ast.ts +22 -0
- package/src/util/sort.ts +90 -0
- package/src/util/table-buttons.ts +1 -1
- package/src/util/table-controls.ts +2 -2
- package/dist/lib/browser/chunk-4K7JPALT.mjs +0 -66
- package/dist/lib/browser/chunk-4K7JPALT.mjs.map +0 -7
- package/dist/lib/browser/chunk-OZTXLZGM.mjs +0 -1804
- package/dist/lib/browser/chunk-OZTXLZGM.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-MK6OVUF6.mjs +0 -67
- package/dist/lib/node-esm/chunk-MK6OVUF6.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-S56Z4V4X.mjs +0 -1805
- package/dist/lib/node-esm/chunk-S56Z4V4X.mjs.map +0 -7
- package/dist/types/src/model/table-sorting.d.ts +0 -52
- package/dist/types/src/model/table-sorting.d.ts.map +0 -1
- package/dist/types/src/types/schema.d.ts +0 -28
- package/dist/types/src/types/schema.d.ts.map +0 -1
- package/src/model/table-sorting.ts +0 -225
- package/src/types/schema.ts +0 -73
|
@@ -2,23 +2,20 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
import React, { useMemo } from 'react';
|
|
5
|
+
import { Atom, RegistryContext } from '@effect-atom/atom-react';
|
|
6
|
+
import React, { useContext, useEffect, useMemo } from 'react';
|
|
7
7
|
|
|
8
|
-
import { live } from '@dxos/live-object';
|
|
9
8
|
import { type ThemedClassName } from '@dxos/react-ui';
|
|
10
9
|
import {
|
|
11
10
|
type ActionGraphEdges,
|
|
12
11
|
type ActionGraphNodes,
|
|
13
12
|
type ActionGraphProps,
|
|
13
|
+
Menu,
|
|
14
14
|
type MenuAction,
|
|
15
|
-
MenuProvider,
|
|
16
|
-
ToolbarMenu,
|
|
17
15
|
createGapSeparator,
|
|
18
16
|
createMenuAction,
|
|
19
17
|
useMenuActions,
|
|
20
18
|
} from '@dxos/react-ui-menu';
|
|
21
|
-
import { rxFromSignal } from '@dxos/react-ui-menu';
|
|
22
19
|
|
|
23
20
|
import { translationKey } from '../../translations';
|
|
24
21
|
|
|
@@ -30,49 +27,60 @@ export type TableToolbarActionType = 'add-row' | 'comment' | 'save-view';
|
|
|
30
27
|
|
|
31
28
|
type TableToolbarState = Partial<{ viewDirty: boolean }>;
|
|
32
29
|
|
|
30
|
+
// TODO(burdon): Radix style layout.
|
|
31
|
+
|
|
33
32
|
export type TableToolbarProps = ThemedClassName<
|
|
34
33
|
TableToolbarState & {
|
|
35
|
-
onAdd
|
|
36
|
-
onSave
|
|
34
|
+
onAdd?: () => void;
|
|
35
|
+
onSave?: () => void;
|
|
37
36
|
attendableId?: string;
|
|
38
|
-
customActions?:
|
|
37
|
+
customActions?: Atom.Atom<ActionGraphProps>;
|
|
39
38
|
}
|
|
40
39
|
>;
|
|
41
40
|
|
|
42
41
|
const createTableToolbarActions = ({
|
|
43
|
-
|
|
42
|
+
stateAtom,
|
|
44
43
|
onAdd,
|
|
45
44
|
onSave,
|
|
46
45
|
customActions,
|
|
47
46
|
}: {
|
|
48
|
-
|
|
49
|
-
onAdd
|
|
50
|
-
onSave
|
|
51
|
-
customActions?:
|
|
47
|
+
stateAtom: Atom.Atom<TableToolbarState>;
|
|
48
|
+
onAdd?: () => void;
|
|
49
|
+
onSave?: () => void;
|
|
50
|
+
customActions?: Atom.Atom<ActionGraphProps>;
|
|
52
51
|
}) =>
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
52
|
+
Atom.make((get) => {
|
|
53
|
+
const nodes: ActionGraphNodes = [];
|
|
54
|
+
if (onAdd) {
|
|
55
|
+
const add = createMenuAction<TableToolbarActionProperties>('add-row', onAdd, {
|
|
56
|
+
type: 'add-row' as const,
|
|
57
|
+
icon: 'ph--plus--regular',
|
|
58
|
+
label: ['add row label', { ns: translationKey }],
|
|
59
|
+
testId: 'table.toolbar.add-row',
|
|
60
|
+
});
|
|
61
|
+
nodes.push(add);
|
|
62
|
+
}
|
|
63
|
+
if (onSave) {
|
|
64
|
+
const state = get(stateAtom);
|
|
65
|
+
const save = createMenuAction<TableToolbarActionProperties>('save-view', onSave, {
|
|
66
|
+
type: 'save-view' as const,
|
|
67
|
+
icon: 'ph--floppy-disk--regular',
|
|
68
|
+
label: ['save view label', { ns: translationKey }],
|
|
69
|
+
testId: 'table.toolbar.save-view',
|
|
70
|
+
iconOnly: false,
|
|
71
|
+
hidden: !state.viewDirty,
|
|
72
|
+
});
|
|
73
|
+
nodes.push(save);
|
|
74
|
+
}
|
|
68
75
|
const gap = createGapSeparator();
|
|
69
|
-
|
|
70
|
-
const edges: ActionGraphEdges = nodes.map(({ id: target }) => ({ source: 'root', target }));
|
|
76
|
+
nodes.push(...gap.nodes);
|
|
77
|
+
const edges: ActionGraphEdges = nodes.map(({ id: target }) => ({ source: 'root', target, relation: 'child' }));
|
|
71
78
|
if (customActions) {
|
|
72
79
|
const custom = get(customActions);
|
|
73
80
|
nodes.push(...custom.nodes);
|
|
74
81
|
edges.push(...custom.edges);
|
|
75
82
|
}
|
|
83
|
+
|
|
76
84
|
return {
|
|
77
85
|
nodes,
|
|
78
86
|
edges,
|
|
@@ -86,17 +94,25 @@ export const TableToolbar = ({
|
|
|
86
94
|
onAdd,
|
|
87
95
|
onSave,
|
|
88
96
|
customActions,
|
|
97
|
+
...props
|
|
89
98
|
}: TableToolbarProps) => {
|
|
90
|
-
const
|
|
99
|
+
const registry = useContext(RegistryContext);
|
|
100
|
+
const stateAtom = useMemo(() => Atom.make<TableToolbarState>({ viewDirty }), []);
|
|
101
|
+
|
|
102
|
+
// Update state.viewDirty when the prop changes.
|
|
103
|
+
useEffect(() => {
|
|
104
|
+
registry.set(stateAtom, { viewDirty });
|
|
105
|
+
}, [registry, stateAtom, viewDirty]);
|
|
106
|
+
|
|
91
107
|
const actionsCreator = useMemo(
|
|
92
|
-
() => createTableToolbarActions({
|
|
93
|
-
[
|
|
108
|
+
() => createTableToolbarActions({ stateAtom, onAdd, onSave, customActions }),
|
|
109
|
+
[stateAtom, onAdd, onSave, customActions],
|
|
94
110
|
);
|
|
95
111
|
const menu = useMenuActions(actionsCreator);
|
|
96
112
|
|
|
97
113
|
return (
|
|
98
|
-
<
|
|
99
|
-
<
|
|
100
|
-
</
|
|
114
|
+
<Menu.Root {...menu} attendableId={attendableId}>
|
|
115
|
+
<Menu.Toolbar {...props} classNames={classNames} />
|
|
116
|
+
</Menu.Root>
|
|
101
117
|
);
|
|
102
118
|
};
|
|
@@ -2,33 +2,59 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { Atom } from '@effect-atom/atom-react';
|
|
5
6
|
import * as Schema from 'effect/Schema';
|
|
6
7
|
import { useEffect } from 'react';
|
|
7
8
|
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
9
|
+
import { Format, Type } from '@dxos/echo';
|
|
10
|
+
import { TypeEnum } from '@dxos/echo/internal';
|
|
11
|
+
import { setValue } from '@dxos/effect';
|
|
10
12
|
import { faker } from '@dxos/random';
|
|
11
13
|
import { type ProjectionModel } from '@dxos/schema';
|
|
12
14
|
|
|
13
|
-
export const TestSchema =
|
|
14
|
-
id: ObjectId,
|
|
15
|
+
export const TestSchema = Schema.Struct({
|
|
15
16
|
name: Schema.optional(Schema.String),
|
|
16
17
|
age: Schema.optional(Schema.Number),
|
|
17
18
|
active: Schema.optional(Schema.Boolean),
|
|
18
19
|
netWorth: Schema.optional(Schema.Number),
|
|
19
|
-
})
|
|
20
|
+
}).pipe(
|
|
21
|
+
Type.object({
|
|
22
|
+
typename: 'example.com/type/Test',
|
|
23
|
+
version: '0.1.0',
|
|
24
|
+
}),
|
|
25
|
+
);
|
|
20
26
|
|
|
21
|
-
export
|
|
22
|
-
|
|
27
|
+
export type TestItem = {
|
|
28
|
+
name: string;
|
|
29
|
+
age: number;
|
|
30
|
+
active: boolean;
|
|
31
|
+
netWorth: number;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Creates an Atom containing test items data.
|
|
36
|
+
*/
|
|
37
|
+
export const createItemsAtom = (n: number) => {
|
|
38
|
+
return Atom.make<{ data: TestItem[] }>({
|
|
23
39
|
data: Array.from({ length: n }, () => ({
|
|
24
40
|
name: faker.person.fullName(),
|
|
25
41
|
age: faker.number.int({ min: 20, max: 70 }),
|
|
26
42
|
active: faker.datatype.boolean(),
|
|
27
43
|
netWorth: faker.number.int(),
|
|
28
44
|
})),
|
|
29
|
-
});
|
|
45
|
+
}).pipe(Atom.keepAlive);
|
|
46
|
+
};
|
|
30
47
|
|
|
31
|
-
|
|
48
|
+
/**
|
|
49
|
+
* Creates plain test items (non-reactive).
|
|
50
|
+
*/
|
|
51
|
+
export const createItems = (n: number): TestItem[] => {
|
|
52
|
+
return Array.from({ length: n }, () => ({
|
|
53
|
+
name: faker.person.fullName(),
|
|
54
|
+
age: faker.number.int({ min: 20, max: 70 }),
|
|
55
|
+
active: faker.datatype.boolean(),
|
|
56
|
+
netWorth: faker.number.int(),
|
|
57
|
+
}));
|
|
32
58
|
};
|
|
33
59
|
|
|
34
60
|
export type SimulatorProps = {
|
|
@@ -59,7 +85,7 @@ export const useSimulator = ({ items, projection, insertInterval, updateInterval
|
|
|
59
85
|
|
|
60
86
|
const i = setInterval(() => {
|
|
61
87
|
const rowIdx = Math.floor(Math.random() * items.length);
|
|
62
|
-
const fields = projection.
|
|
88
|
+
const fields = projection.getFields() ?? [];
|
|
63
89
|
const columnIdx = Math.floor(Math.random() * fields.length);
|
|
64
90
|
const field = fields[columnIdx];
|
|
65
91
|
const item = items[rowIdx];
|
|
@@ -88,7 +114,7 @@ export const useSimulator = ({ items, projection, insertInterval, updateInterval
|
|
|
88
114
|
|
|
89
115
|
if (format) {
|
|
90
116
|
switch (format) {
|
|
91
|
-
case
|
|
117
|
+
case Format.TypeFormat.Currency: {
|
|
92
118
|
item[path] = Math.floor(Math.random() * 1000);
|
|
93
119
|
break;
|
|
94
120
|
}
|
|
@@ -98,5 +124,5 @@ export const useSimulator = ({ items, projection, insertInterval, updateInterval
|
|
|
98
124
|
}, updateInterval);
|
|
99
125
|
|
|
100
126
|
return () => clearInterval(i);
|
|
101
|
-
}, [items, projection.
|
|
127
|
+
}, [items, projection.getFields(), updateInterval]);
|
|
102
128
|
};
|
package/src/hooks/index.ts
CHANGED
package/src/hooks/useAddRow.ts
CHANGED
|
@@ -2,17 +2,15 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import type * as Schema from 'effect/Schema';
|
|
6
5
|
import { useCallback } from 'react';
|
|
7
6
|
|
|
8
|
-
import { type
|
|
9
|
-
import { Obj } from '@dxos/echo';
|
|
7
|
+
import { type Database, Obj, type Type } from '@dxos/echo';
|
|
10
8
|
|
|
11
9
|
import { type InsertRowResult } from '../model';
|
|
12
10
|
|
|
13
|
-
export type
|
|
14
|
-
|
|
15
|
-
schema?:
|
|
11
|
+
export type UseAddRowProps = {
|
|
12
|
+
db?: Database.Database;
|
|
13
|
+
schema?: Type.AnyObj;
|
|
16
14
|
};
|
|
17
15
|
|
|
18
16
|
/**
|
|
@@ -20,12 +18,12 @@ export type UseAddRowParams = {
|
|
|
20
18
|
* Returns a callback that attempts to create an object and returns boolean success/failure.
|
|
21
19
|
* Can accept optional data to create objects with specific content (used for committing draft rows).
|
|
22
20
|
*/
|
|
23
|
-
export const useAddRow = ({
|
|
21
|
+
export const useAddRow = ({ db, schema }: UseAddRowProps) => {
|
|
24
22
|
return useCallback(
|
|
25
23
|
(data?: any): InsertRowResult => {
|
|
26
|
-
if (
|
|
24
|
+
if (db && schema) {
|
|
27
25
|
try {
|
|
28
|
-
|
|
26
|
+
db.add(Obj.make(schema, data ?? {}));
|
|
29
27
|
return 'final';
|
|
30
28
|
} catch (error) {
|
|
31
29
|
return 'draft';
|
|
@@ -33,6 +31,6 @@ export const useAddRow = ({ space, schema }: UseAddRowParams) => {
|
|
|
33
31
|
}
|
|
34
32
|
return 'final';
|
|
35
33
|
},
|
|
36
|
-
[
|
|
34
|
+
[db, schema],
|
|
37
35
|
);
|
|
38
36
|
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type Registry } from '@effect-atom/atom-react';
|
|
6
|
+
import { useState } from 'react';
|
|
7
|
+
|
|
8
|
+
import { JsonSchema, Type } from '@dxos/echo';
|
|
9
|
+
import { useAsyncEffect } from '@dxos/react-ui';
|
|
10
|
+
import { ProjectionModel, createEchoChangeCallback } from '@dxos/schema';
|
|
11
|
+
|
|
12
|
+
import { type Table } from '../types';
|
|
13
|
+
|
|
14
|
+
export const useProjectionModel = <S extends Type.AnyEntity>(
|
|
15
|
+
schema: S | undefined,
|
|
16
|
+
table: Table.Table | undefined,
|
|
17
|
+
registry: Registry.Registry,
|
|
18
|
+
) => {
|
|
19
|
+
const [projection, setProjection] = useState<ProjectionModel | undefined>();
|
|
20
|
+
|
|
21
|
+
useAsyncEffect(async () => {
|
|
22
|
+
if (schema && table) {
|
|
23
|
+
const view = await table.view.load();
|
|
24
|
+
// For mutable schemas (EchoSchema), use the live jsonSchema reference for reactivity.
|
|
25
|
+
// For immutable schemas, create a snapshot.
|
|
26
|
+
const jsonSchema = Type.isMutable(schema) ? schema.jsonSchema : JsonSchema.toJsonSchema(schema);
|
|
27
|
+
|
|
28
|
+
// Always use createEchoChangeCallback since the view is ECHO-backed.
|
|
29
|
+
// Pass schema only when mutable to allow schema mutations.
|
|
30
|
+
const change = createEchoChangeCallback(view, Type.isMutable(schema) ? schema : undefined);
|
|
31
|
+
|
|
32
|
+
const projection = new ProjectionModel({ registry, view, baseSchema: jsonSchema, change });
|
|
33
|
+
projection.normalizeView();
|
|
34
|
+
setProjection(projection);
|
|
35
|
+
}
|
|
36
|
+
}, [schema, table, registry]);
|
|
37
|
+
|
|
38
|
+
return projection;
|
|
39
|
+
};
|
|
@@ -2,58 +2,65 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
import { useEffect, useMemo, useState } from 'react';
|
|
5
|
+
import { RegistryContext } from '@effect-atom/atom-react';
|
|
6
|
+
import { useContext, useEffect, useMemo, useState } from 'react';
|
|
8
7
|
|
|
9
|
-
import { type
|
|
10
|
-
import { type Live } from '@dxos/live-object';
|
|
11
|
-
import { fullyQualifiedId } from '@dxos/react-client/echo';
|
|
8
|
+
import { type Database, Obj } from '@dxos/echo';
|
|
12
9
|
import { useSelected, useSelectionActions } from '@dxos/react-ui-attention';
|
|
13
|
-
import { type
|
|
10
|
+
import { type ProjectionModel } from '@dxos/schema';
|
|
14
11
|
import { isNonNullable } from '@dxos/util';
|
|
15
12
|
|
|
16
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
TableModel,
|
|
15
|
+
type TableModelProps,
|
|
16
|
+
type TableRow,
|
|
17
|
+
type TableRowAction,
|
|
18
|
+
createEchoChangeCallback,
|
|
19
|
+
} from '../model';
|
|
20
|
+
import { type Table } from '../types';
|
|
17
21
|
|
|
18
|
-
export type
|
|
19
|
-
|
|
20
|
-
schema?: JsonSchemaType;
|
|
22
|
+
export type UseTableModelProps<T extends TableRow = TableRow> = {
|
|
23
|
+
object?: Table.Table;
|
|
21
24
|
projection?: ProjectionModel;
|
|
22
|
-
|
|
25
|
+
db?: Database.Database;
|
|
26
|
+
rows?: T[];
|
|
23
27
|
rowActions?: TableRowAction[];
|
|
24
28
|
onSelectionChanged?: (selection: string[]) => void;
|
|
25
29
|
onRowAction?: (actionId: string, data: T) => void;
|
|
26
30
|
} & Pick<
|
|
27
31
|
TableModelProps<T>,
|
|
28
|
-
'features' | 'onInsertRow' | 'onDeleteRows' | '
|
|
32
|
+
'features' | 'onInsertRow' | 'onDeleteRows' | 'onColumnDelete' | 'onCellUpdate' | 'onRowOrderChange'
|
|
29
33
|
>;
|
|
30
34
|
|
|
31
35
|
export const useTableModel = <T extends TableRow = TableRow>({
|
|
32
|
-
|
|
33
|
-
schema,
|
|
36
|
+
object,
|
|
34
37
|
projection,
|
|
38
|
+
db,
|
|
35
39
|
rows,
|
|
36
40
|
rowActions,
|
|
37
41
|
features,
|
|
38
42
|
onSelectionChanged,
|
|
39
43
|
onRowAction,
|
|
40
44
|
...props
|
|
41
|
-
}:
|
|
42
|
-
const
|
|
43
|
-
const
|
|
45
|
+
}: UseTableModelProps<T>): TableModel<T> | undefined => {
|
|
46
|
+
const registry = useContext(RegistryContext);
|
|
47
|
+
const selected = useSelected(object && Obj.getDXN(object).toString(), 'multi');
|
|
48
|
+
const initialSelection = useMemo(() => selected, [object]);
|
|
44
49
|
|
|
45
50
|
const [model, setModel] = useState<TableModel<T>>();
|
|
46
51
|
useEffect(() => {
|
|
47
|
-
if (!
|
|
52
|
+
if (!object || !projection) {
|
|
48
53
|
return;
|
|
49
54
|
}
|
|
50
55
|
|
|
51
56
|
let model: TableModel<T> | undefined;
|
|
52
57
|
const t = setTimeout(async () => {
|
|
53
58
|
model = new TableModel<T>({
|
|
54
|
-
|
|
55
|
-
|
|
59
|
+
registry,
|
|
60
|
+
object,
|
|
56
61
|
projection,
|
|
62
|
+
db,
|
|
63
|
+
change: createEchoChangeCallback<T>(object),
|
|
57
64
|
features,
|
|
58
65
|
rowActions,
|
|
59
66
|
initialSelection,
|
|
@@ -68,18 +75,13 @@ export const useTableModel = <T extends TableRow = TableRow>({
|
|
|
68
75
|
clearTimeout(t);
|
|
69
76
|
void model?.close();
|
|
70
77
|
};
|
|
71
|
-
|
|
78
|
+
// TODO(burdon): Trigger if callbacks change?
|
|
79
|
+
}, [registry, object, projection, features, rowActions, initialSelection]);
|
|
72
80
|
|
|
73
|
-
// Update data.
|
|
81
|
+
// Update data when rows change.
|
|
74
82
|
useEffect(() => {
|
|
75
|
-
if (rows) {
|
|
76
|
-
|
|
77
|
-
/*
|
|
78
|
-
* Sort all objects by string id field as a temporary workaround for query ordering issues
|
|
79
|
-
* Reference: https://github.com/dxos/dxos/pull/9409
|
|
80
|
-
*/
|
|
81
|
-
const sortedRows = orderBy(rows, [(row) => String(row.id)], ['asc']);
|
|
82
|
-
model?.setRows(sortedRows);
|
|
83
|
+
if (rows && model) {
|
|
84
|
+
model.setRows(rows);
|
|
83
85
|
}
|
|
84
86
|
}, [model, rows]);
|
|
85
87
|
|
|
@@ -90,8 +92,8 @@ export const useTableModel = <T extends TableRow = TableRow>({
|
|
|
90
92
|
return;
|
|
91
93
|
}
|
|
92
94
|
|
|
93
|
-
const unsubscribe =
|
|
94
|
-
const selectedItems = [...model.selection.selection
|
|
95
|
+
const unsubscribe = registry.subscribe(model.selection.selectionAtom, () => {
|
|
96
|
+
const selectedItems = [...model.selection.selection];
|
|
95
97
|
multiSelect(selectedItems);
|
|
96
98
|
onSelectionChanged?.(selectedItems);
|
|
97
99
|
});
|
|
@@ -101,7 +103,7 @@ export const useTableModel = <T extends TableRow = TableRow>({
|
|
|
101
103
|
clear();
|
|
102
104
|
unsubscribe();
|
|
103
105
|
};
|
|
104
|
-
}, [model, onSelectionChanged]);
|
|
106
|
+
}, [registry, model, onSelectionChanged]);
|
|
105
107
|
|
|
106
108
|
return model;
|
|
107
109
|
};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { Atom, type Registry } from '@effect-atom/atom-react';
|
|
6
6
|
import { type MutableRefObject, type RefObject } from 'react';
|
|
7
7
|
|
|
8
8
|
export type ColumnSettingsMode = { type: 'create' } | { type: 'edit'; fieldId: string };
|
|
@@ -15,13 +15,23 @@ export type ModalState =
|
|
|
15
15
|
| { type: 'closed' };
|
|
16
16
|
|
|
17
17
|
export class ModalController {
|
|
18
|
-
private readonly
|
|
18
|
+
private readonly _registry: Registry.Registry;
|
|
19
|
+
private readonly _state: Atom.Writable<ModalState>;
|
|
19
20
|
private readonly _triggerRef: MutableRefObject<HTMLElement> = { current: null as unknown as HTMLElement };
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
constructor(registry: Registry.Registry) {
|
|
23
|
+
this._registry = registry;
|
|
24
|
+
this._state = Atom.make<ModalState>({ type: 'closed' });
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public get state(): Atom.Atom<ModalState> {
|
|
22
28
|
return this._state;
|
|
23
29
|
}
|
|
24
30
|
|
|
31
|
+
public get stateValue(): ModalState {
|
|
32
|
+
return this._registry.get(this._state);
|
|
33
|
+
}
|
|
34
|
+
|
|
25
35
|
public get trigger() {
|
|
26
36
|
return this._triggerRef as RefObject<HTMLButtonElement>;
|
|
27
37
|
}
|
|
@@ -31,35 +41,36 @@ export class ModalController {
|
|
|
31
41
|
};
|
|
32
42
|
|
|
33
43
|
public showRowMenu = (rowIndex: number) => {
|
|
34
|
-
this._state
|
|
44
|
+
this._registry.set(this._state, { type: 'row', rowIndex });
|
|
35
45
|
};
|
|
36
46
|
|
|
37
47
|
public showColumnMenu = (fieldId: string) => {
|
|
38
|
-
this._state
|
|
48
|
+
this._registry.set(this._state, { type: 'column', fieldId });
|
|
39
49
|
};
|
|
40
50
|
|
|
41
51
|
public showColumnCreator = () => {
|
|
42
|
-
this._state
|
|
52
|
+
this._registry.set(this._state, { type: 'columnSettings', mode: { type: 'create' } });
|
|
43
53
|
};
|
|
44
54
|
|
|
45
55
|
public showReferencePanel = (targetId: string, schemaId: string) => {
|
|
46
|
-
this._state
|
|
56
|
+
this._registry.set(this._state, { type: 'refPanel', targetId, typename: schemaId });
|
|
47
57
|
};
|
|
48
58
|
|
|
49
59
|
public openColumnSettings = () => {
|
|
50
|
-
|
|
51
|
-
|
|
60
|
+
const currentState = this._registry.get(this._state);
|
|
61
|
+
if (currentState.type === 'column') {
|
|
62
|
+
const fieldId = currentState.fieldId;
|
|
52
63
|
// Queue next render to let the column action modal close completely.
|
|
53
64
|
requestAnimationFrame(() => {
|
|
54
|
-
this._state
|
|
65
|
+
this._registry.set(this._state, {
|
|
55
66
|
type: 'columnSettings',
|
|
56
67
|
mode: { type: 'edit', fieldId },
|
|
57
|
-
};
|
|
68
|
+
});
|
|
58
69
|
});
|
|
59
70
|
}
|
|
60
71
|
};
|
|
61
72
|
|
|
62
73
|
public close = () => {
|
|
63
|
-
this._state
|
|
74
|
+
this._registry.set(this._state, { type: 'closed' });
|
|
64
75
|
};
|
|
65
76
|
}
|