@dxos/react-ui-table 0.8.2-staging.7ac8446 → 0.8.3-main.672df60
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-U6LL2AVU.mjs +26 -0
- package/dist/lib/browser/chunk-U6LL2AVU.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +1115 -905
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/types/index.mjs +1 -1
- package/dist/lib/node/{chunk-TGG6OZ54.cjs → chunk-CXDGXPM6.cjs} +12 -12
- package/dist/lib/node/chunk-CXDGXPM6.cjs.map +7 -0
- package/dist/lib/node/index.cjs +1113 -906
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/types/index.cjs +3 -3
- package/dist/lib/node/types/index.cjs.map +1 -1
- package/dist/lib/node-esm/{chunk-KKLP54L3.mjs → chunk-EFSM52KQ.mjs} +10 -10
- package/dist/lib/node-esm/chunk-EFSM52KQ.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +1115 -905
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/types/index.mjs +1 -1
- package/dist/types/src/components/Table/ColumnActionsMenu.d.ts.map +1 -1
- package/dist/types/src/components/Table/ColumnSettings.d.ts.map +1 -1
- package/dist/types/src/components/Table/CreateRefPanel.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.d.ts +3 -4
- package/dist/types/src/components/Table/DynamicTable.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 +2 -0
- package/dist/types/src/components/Table/Table.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.stories.d.ts +1 -0
- package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/relations.stories.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +13 -0
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -0
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +3 -0
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/extension.d.ts.map +1 -1
- package/dist/types/src/components/TableToolbar/TableToolbar.d.ts +6 -3
- package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +40 -40
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/hooks/useTableModel.d.ts +2 -2
- package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
- package/dist/types/src/model/modal-controller.d.ts +3 -3
- package/dist/types/src/model/modal-controller.d.ts.map +1 -1
- package/dist/types/src/model/selection-model.d.ts.map +1 -1
- package/dist/types/src/model/table-model.d.ts.map +1 -1
- package/dist/types/src/model/table-presentation.d.ts.map +1 -1
- package/dist/types/src/types/table.d.ts +46 -45
- package/dist/types/src/types/table.d.ts.map +1 -1
- package/dist/types/src/util/dynamic-table.d.ts +4 -3
- package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
- package/dist/types/src/util/index.d.ts +1 -0
- package/dist/types/src/util/index.d.ts.map +1 -1
- package/dist/types/src/util/schema.d.ts +12 -0
- package/dist/types/src/util/schema.d.ts.map +1 -0
- package/dist/types/src/util/signals.d.ts.map +1 -1
- package/dist/types/src/util/table-buttons.d.ts +0 -14
- package/dist/types/src/util/table-buttons.d.ts.map +1 -1
- package/dist/types/src/util/table-controls.d.ts.map +1 -1
- package/dist/types/src/util/table.d.ts +2 -2
- package/dist/types/src/util/table.d.ts.map +1 -1
- package/dist/types/src/util/tag.d.ts +11 -0
- package/dist/types/src/util/tag.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +41 -34
- package/src/components/Table/CreateRefPanel.tsx +5 -5
- package/src/components/Table/DynamicTable.stories.tsx +4 -4
- package/src/components/Table/DynamicTable.tsx +4 -5
- package/src/components/Table/Table.stories.tsx +85 -51
- package/src/components/Table/Table.tsx +25 -28
- package/src/components/Table/relations.stories.tsx +39 -33
- package/src/components/TableCellEditor/FormCellEditor.tsx +94 -0
- package/src/components/TableCellEditor/TableCellEditor.stories.tsx +13 -11
- package/src/components/TableCellEditor/TableCellEditor.tsx +45 -4
- package/src/components/TableToolbar/TableToolbar.tsx +65 -35
- package/src/components/testing.ts +10 -9
- package/src/hooks/useTableModel.ts +6 -4
- package/src/model/modal-controller.ts +3 -3
- package/src/model/selection-model.ts +1 -1
- package/src/model/table-model.test.ts +7 -6
- package/src/model/table-model.ts +20 -3
- package/src/model/table-presentation.test.ts +7 -6
- package/src/model/table-presentation.ts +54 -10
- package/src/types/table.ts +16 -11
- package/src/util/dynamic-table.ts +9 -6
- package/src/util/index.ts +1 -0
- package/src/util/schema.ts +36 -0
- package/src/util/table-buttons.ts +1 -24
- package/src/util/table.ts +13 -16
- package/src/util/tag.ts +33 -0
- package/dist/lib/browser/chunk-YWNRUF4J.mjs +0 -26
- package/dist/lib/browser/chunk-YWNRUF4J.mjs.map +0 -7
- package/dist/lib/node/chunk-TGG6OZ54.cjs.map +0 -7
- package/dist/lib/node-esm/chunk-KKLP54L3.mjs.map +0 -7
- package/dist/types/src/components/Table/RefPanel.d.ts +0 -9
- package/dist/types/src/components/Table/RefPanel.d.ts.map +0 -1
- package/src/components/Table/RefPanel.tsx +0 -52
|
@@ -7,12 +7,12 @@ import '@dxos-theme';
|
|
|
7
7
|
import { type Meta, type StoryObj } from '@storybook/react';
|
|
8
8
|
import React, { useEffect, useMemo, useState } from 'react';
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import { type EchoSchema, isMutable } from '@dxos/echo-schema';
|
|
11
11
|
import { invariant } from '@dxos/invariant';
|
|
12
12
|
import { faker } from '@dxos/random';
|
|
13
|
-
import { Filter, useQuery,
|
|
13
|
+
import { Filter, useQuery, live } from '@dxos/react-client/echo';
|
|
14
14
|
import { useClientProvider, withClientProvider } from '@dxos/react-client/testing';
|
|
15
|
-
import {
|
|
15
|
+
import { defaultRowSize, Grid, type GridEditing } from '@dxos/react-ui-grid';
|
|
16
16
|
import { ViewProjection, ViewType } from '@dxos/schema';
|
|
17
17
|
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
18
18
|
|
|
@@ -31,7 +31,7 @@ const DefaultStory = ({ editing }: StoryProps) => {
|
|
|
31
31
|
const { space } = useClientProvider();
|
|
32
32
|
invariant(space);
|
|
33
33
|
|
|
34
|
-
const tables = useQuery(space, Filter.
|
|
34
|
+
const tables = useQuery(space, Filter.type(TableType));
|
|
35
35
|
const [table, setTable] = useState<TableType>();
|
|
36
36
|
const [schema, setSchema] = useState<EchoSchema>();
|
|
37
37
|
useEffect(() => {
|
|
@@ -53,15 +53,16 @@ const DefaultStory = ({ editing }: StoryProps) => {
|
|
|
53
53
|
() => ({
|
|
54
54
|
selection: { enabled: true, mode: 'multiple' },
|
|
55
55
|
dataEditable: true,
|
|
56
|
-
schemaEditable:
|
|
56
|
+
schemaEditable: schema && isMutable(schema),
|
|
57
57
|
}),
|
|
58
|
-
[],
|
|
58
|
+
[schema],
|
|
59
59
|
);
|
|
60
60
|
|
|
61
61
|
const model = useTableModel({ table, projection, features });
|
|
62
62
|
|
|
63
63
|
const handleQuery: TableCellEditorProps['onQuery'] = async ({ field }) => {
|
|
64
|
-
|
|
64
|
+
// TODO(dmaretskyi): If no schema query nothing
|
|
65
|
+
const { objects } = await space.db.query(schema ? Filter.type(schema) : Filter.everything()).run();
|
|
65
66
|
return objects.map((obj) => {
|
|
66
67
|
const label = obj[field.referencePath ?? 'id'];
|
|
67
68
|
return {
|
|
@@ -76,7 +77,7 @@ const DefaultStory = ({ editing }: StoryProps) => {
|
|
|
76
77
|
}
|
|
77
78
|
|
|
78
79
|
return (
|
|
79
|
-
<div className='flex w-[300px] border border-separator' style={{ height:
|
|
80
|
+
<div className='flex w-[300px] border border-separator' style={{ height: defaultRowSize }}>
|
|
80
81
|
<Grid.Root id='test' editing={editing}>
|
|
81
82
|
<TableCellEditor model={model} onQuery={handleQuery} />
|
|
82
83
|
</Grid.Root>
|
|
@@ -95,11 +96,11 @@ const meta: Meta<StoryProps> = {
|
|
|
95
96
|
createIdentity: true,
|
|
96
97
|
createSpace: true,
|
|
97
98
|
onSpaceCreated: async ({ client, space }) => {
|
|
98
|
-
const table = space.db.add(
|
|
99
|
+
const table = space.db.add(live(TableType, {}));
|
|
99
100
|
const schema = await initializeTable({ client, space, table });
|
|
100
101
|
Array.from({ length: 10 }).forEach(() => {
|
|
101
102
|
space.db.add(
|
|
102
|
-
|
|
103
|
+
live(schema, {
|
|
103
104
|
name: faker.person.fullName(),
|
|
104
105
|
}),
|
|
105
106
|
);
|
|
@@ -107,7 +108,7 @@ const meta: Meta<StoryProps> = {
|
|
|
107
108
|
},
|
|
108
109
|
}),
|
|
109
110
|
withTheme,
|
|
110
|
-
withLayout(
|
|
111
|
+
withLayout(),
|
|
111
112
|
],
|
|
112
113
|
};
|
|
113
114
|
|
|
@@ -120,6 +121,7 @@ export const Default: Story = {
|
|
|
120
121
|
editing: {
|
|
121
122
|
index: 'grid,0,3',
|
|
122
123
|
initialContent: 'Test',
|
|
124
|
+
cellElement: null,
|
|
123
125
|
},
|
|
124
126
|
},
|
|
125
127
|
};
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
//
|
|
2
|
-
// Copyright
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { type Completion } from '@codemirror/autocomplete';
|
|
6
|
+
import { type Schema } from 'effect/Schema';
|
|
6
7
|
import React, { useCallback, useMemo, useRef } from 'react';
|
|
7
8
|
|
|
8
|
-
import { FormatEnum } from '@dxos/echo-schema';
|
|
9
|
+
import { FormatEnum, TypeEnum } from '@dxos/echo-schema';
|
|
9
10
|
import { invariant } from '@dxos/invariant';
|
|
10
11
|
import { type DxGrid } from '@dxos/lit-grid';
|
|
11
12
|
import { useThemeContext } from '@dxos/react-ui';
|
|
@@ -26,6 +27,7 @@ import {
|
|
|
26
27
|
import { tagPickerExtension, createLinks } from '@dxos/react-ui-tag-picker';
|
|
27
28
|
import { type FieldProjection } from '@dxos/schema';
|
|
28
29
|
|
|
30
|
+
import { FormCellEditor } from './FormCellEditor';
|
|
29
31
|
import { completion } from './extension';
|
|
30
32
|
import { type TableModel, type ModalController } from '../../model';
|
|
31
33
|
|
|
@@ -43,11 +45,52 @@ export type QueryResult = Pick<Completion, 'label'> & { data: any };
|
|
|
43
45
|
export type TableCellEditorProps = {
|
|
44
46
|
model?: TableModel;
|
|
45
47
|
modals?: ModalController;
|
|
48
|
+
schema?: Schema.AnyNoContext;
|
|
46
49
|
onEnter?: (cell: DxGridPlanePosition) => void;
|
|
47
50
|
onFocus?: DxGrid['refocus'];
|
|
48
51
|
onQuery?: (field: FieldProjection, text: string) => Promise<QueryResult[]>;
|
|
49
52
|
};
|
|
50
53
|
|
|
54
|
+
export const TableValueEditor = ({
|
|
55
|
+
model,
|
|
56
|
+
modals,
|
|
57
|
+
schema,
|
|
58
|
+
onEnter,
|
|
59
|
+
onFocus,
|
|
60
|
+
onQuery,
|
|
61
|
+
__gridScope,
|
|
62
|
+
}: GridScopedProps<TableCellEditorProps>) => {
|
|
63
|
+
const { editing } = useGridContext('TableValueEditor', __gridScope);
|
|
64
|
+
|
|
65
|
+
const fieldProjection = useMemo<FieldProjection | undefined>(() => {
|
|
66
|
+
if (!model || !editing) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const { col } = parseCellIndex(editing.index);
|
|
71
|
+
const field = model.projection.view.fields[col];
|
|
72
|
+
const fieldProjection = model.projection.getFieldProjection(field.id);
|
|
73
|
+
invariant(fieldProjection);
|
|
74
|
+
return fieldProjection;
|
|
75
|
+
}, [model, editing]);
|
|
76
|
+
|
|
77
|
+
if (fieldProjection?.props.type === TypeEnum.Array) {
|
|
78
|
+
return <FormCellEditor fieldProjection={fieldProjection} model={model} schema={schema} __gridScope={__gridScope} />;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// For all other types, use the existing cell editor
|
|
82
|
+
return (
|
|
83
|
+
<TableCellEditor
|
|
84
|
+
model={model}
|
|
85
|
+
modals={modals}
|
|
86
|
+
onEnter={onEnter}
|
|
87
|
+
onFocus={onFocus}
|
|
88
|
+
onQuery={onQuery}
|
|
89
|
+
__gridScope={__gridScope}
|
|
90
|
+
/>
|
|
91
|
+
);
|
|
92
|
+
};
|
|
93
|
+
|
|
51
94
|
export const TableCellEditor = ({
|
|
52
95
|
model,
|
|
53
96
|
modals,
|
|
@@ -261,11 +304,9 @@ export const TableCellEditor = ({
|
|
|
261
304
|
}
|
|
262
305
|
}
|
|
263
306
|
|
|
264
|
-
// Return empty string if no value
|
|
265
307
|
return '';
|
|
266
308
|
}
|
|
267
309
|
|
|
268
|
-
// Default behavior for other formats
|
|
269
310
|
const value = model.getCellData(cell);
|
|
270
311
|
return value !== undefined ? String(value) : '';
|
|
271
312
|
}
|
|
@@ -2,19 +2,23 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import { Rx } from '@effect-rx/rx-react';
|
|
6
|
+
import React, { useMemo } from 'react';
|
|
6
7
|
|
|
7
|
-
import {
|
|
8
|
+
import { live } from '@dxos/live-object';
|
|
8
9
|
import { type ThemedClassName } from '@dxos/react-ui';
|
|
9
10
|
import {
|
|
11
|
+
type ActionGraphEdges,
|
|
12
|
+
type ActionGraphNodes,
|
|
13
|
+
type ActionGraphProps,
|
|
10
14
|
createGapSeparator,
|
|
11
15
|
createMenuAction,
|
|
12
16
|
type MenuAction,
|
|
13
|
-
type MenuActionHandler,
|
|
14
17
|
MenuProvider,
|
|
15
18
|
ToolbarMenu,
|
|
16
19
|
useMenuActions,
|
|
17
20
|
} from '@dxos/react-ui-menu';
|
|
21
|
+
import { rxFromSignal } from '@dxos/react-ui-menu';
|
|
18
22
|
|
|
19
23
|
import { translationKey } from '../../translations';
|
|
20
24
|
|
|
@@ -27,45 +31,71 @@ export type TableToolbarActionType = 'add-row' | 'comment' | 'save-view';
|
|
|
27
31
|
type TableToolbarState = Partial<{ viewDirty: boolean }>;
|
|
28
32
|
|
|
29
33
|
export type TableToolbarProps = ThemedClassName<
|
|
30
|
-
TableToolbarState & {
|
|
34
|
+
TableToolbarState & {
|
|
35
|
+
onAdd: () => void;
|
|
36
|
+
onSave: () => void;
|
|
37
|
+
attendableId?: string;
|
|
38
|
+
customActions?: Rx.Rx<ActionGraphProps>;
|
|
39
|
+
}
|
|
31
40
|
>;
|
|
32
41
|
|
|
33
|
-
const createTableToolbarActions = (
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
42
|
+
const createTableToolbarActions = ({
|
|
43
|
+
state,
|
|
44
|
+
onAdd,
|
|
45
|
+
onSave,
|
|
46
|
+
customActions,
|
|
47
|
+
}: {
|
|
48
|
+
state: TableToolbarState;
|
|
49
|
+
onAdd: () => void;
|
|
50
|
+
onSave: () => void;
|
|
51
|
+
customActions?: Rx.Rx<ActionGraphProps>;
|
|
52
|
+
}) =>
|
|
53
|
+
Rx.make((get) => {
|
|
54
|
+
const add = createMenuAction<TableToolbarActionProperties>('add-row', onAdd, {
|
|
55
|
+
type: 'add-row' as const,
|
|
56
|
+
icon: 'ph--plus--regular',
|
|
57
|
+
label: ['add row', { ns: translationKey }],
|
|
58
|
+
testId: 'table.toolbar.add-row',
|
|
59
|
+
});
|
|
60
|
+
const save = createMenuAction<TableToolbarActionProperties>('save-view', onSave, {
|
|
61
|
+
type: 'save-view' as const,
|
|
62
|
+
icon: 'ph--floppy-disk--regular',
|
|
63
|
+
label: ['save view label', { ns: translationKey }],
|
|
64
|
+
testId: 'table.toolbar.save-view',
|
|
65
|
+
iconOnly: false,
|
|
66
|
+
hidden: get(rxFromSignal(() => !state.viewDirty)),
|
|
67
|
+
});
|
|
68
|
+
const gap = createGapSeparator();
|
|
69
|
+
const nodes: ActionGraphNodes = [add, save, ...gap.nodes];
|
|
70
|
+
const edges: ActionGraphEdges = nodes.map(({ id: target }) => ({ source: 'root', target }));
|
|
71
|
+
if (customActions) {
|
|
72
|
+
const custom = get(customActions);
|
|
73
|
+
nodes.push(...custom.nodes);
|
|
74
|
+
edges.push(...custom.edges);
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
nodes,
|
|
78
|
+
edges,
|
|
79
|
+
};
|
|
39
80
|
});
|
|
40
|
-
const save = createMenuAction<TableToolbarActionProperties>('save-view', {
|
|
41
|
-
type: 'save-view',
|
|
42
|
-
icon: 'ph--floppy-disk--regular',
|
|
43
|
-
label: ['save view label', { ns: translationKey }],
|
|
44
|
-
testId: 'table.toolbar.save-view',
|
|
45
|
-
iconOnly: false,
|
|
46
|
-
hidden: !state.viewDirty,
|
|
47
|
-
});
|
|
48
|
-
const gap = createGapSeparator();
|
|
49
|
-
const comment = createMenuAction('comment', {
|
|
50
|
-
type: 'comment',
|
|
51
|
-
icon: 'ph--chat-text--regular',
|
|
52
|
-
label: ['create comment', { ns: translationKey }],
|
|
53
|
-
testId: 'table.toolbar.comment',
|
|
54
|
-
});
|
|
55
|
-
const nodes = [add, save, ...gap.nodes, comment];
|
|
56
|
-
return {
|
|
57
|
-
nodes,
|
|
58
|
-
edges: nodes.map(({ id: target }) => ({ source: 'root', target })),
|
|
59
|
-
};
|
|
60
|
-
};
|
|
61
81
|
|
|
62
|
-
export const TableToolbar = ({
|
|
63
|
-
|
|
64
|
-
|
|
82
|
+
export const TableToolbar = ({
|
|
83
|
+
classNames,
|
|
84
|
+
viewDirty,
|
|
85
|
+
attendableId,
|
|
86
|
+
onAdd,
|
|
87
|
+
onSave,
|
|
88
|
+
customActions,
|
|
89
|
+
}: TableToolbarProps) => {
|
|
90
|
+
const state = useMemo(() => live<TableToolbarState>({ viewDirty }), []);
|
|
91
|
+
const actionsCreator = useMemo(
|
|
92
|
+
() => createTableToolbarActions({ state, onAdd, onSave, customActions }),
|
|
93
|
+
[state, onAdd, onSave, customActions],
|
|
94
|
+
);
|
|
65
95
|
const menu = useMenuActions(actionsCreator);
|
|
66
96
|
|
|
67
97
|
return (
|
|
68
|
-
<MenuProvider {...menu} attendableId={attendableId}
|
|
98
|
+
<MenuProvider {...menu} attendableId={attendableId}>
|
|
69
99
|
<ToolbarMenu classNames={classNames} />
|
|
70
100
|
</MenuProvider>
|
|
71
101
|
);
|
|
@@ -2,25 +2,26 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { Schema } from 'effect';
|
|
5
6
|
import { useEffect } from 'react';
|
|
6
7
|
|
|
7
|
-
import { setValue, toJsonSchema,
|
|
8
|
+
import { setValue, toJsonSchema, TypeEnum, TypedObject, FormatEnum, ObjectId } from '@dxos/echo-schema';
|
|
8
9
|
import { faker } from '@dxos/random';
|
|
9
|
-
import {
|
|
10
|
+
import { live, makeRef } from '@dxos/react-client/echo';
|
|
10
11
|
import { createView, type ViewProjection } from '@dxos/schema';
|
|
11
12
|
|
|
12
13
|
import { TableType } from '../types';
|
|
13
14
|
|
|
14
15
|
export const TestSchema = TypedObject({ typename: 'example.com/type/Test', version: '0.1.0' })({
|
|
15
|
-
id:
|
|
16
|
-
name:
|
|
17
|
-
age:
|
|
18
|
-
active:
|
|
19
|
-
netWorth:
|
|
16
|
+
id: ObjectId,
|
|
17
|
+
name: Schema.optional(Schema.String),
|
|
18
|
+
age: Schema.optional(Schema.Number),
|
|
19
|
+
active: Schema.optional(Schema.Boolean),
|
|
20
|
+
netWorth: Schema.optional(Schema.Number),
|
|
20
21
|
});
|
|
21
22
|
|
|
22
23
|
export const createTable = (schema = TestSchema) => {
|
|
23
|
-
return
|
|
24
|
+
return live(TableType, {
|
|
24
25
|
view: makeRef(
|
|
25
26
|
createView({
|
|
26
27
|
name: 'Test',
|
|
@@ -32,7 +33,7 @@ export const createTable = (schema = TestSchema) => {
|
|
|
32
33
|
};
|
|
33
34
|
|
|
34
35
|
export const createItems = (n: number) => {
|
|
35
|
-
const { data } =
|
|
36
|
+
const { data } = live({
|
|
36
37
|
data: Array.from({ length: n }, () => ({
|
|
37
38
|
name: faker.person.fullName(),
|
|
38
39
|
age: faker.number.int({ min: 20, max: 70 }),
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { effect } from '@preact/signals-core';
|
|
6
6
|
import { useEffect, useState } from 'react';
|
|
7
7
|
|
|
8
|
-
import { fullyQualifiedId, getSpace, type
|
|
8
|
+
import { fullyQualifiedId, getSpace, type Live } from '@dxos/react-client/echo';
|
|
9
9
|
import { useSelectionActions } from '@dxos/react-ui-attention';
|
|
10
10
|
import { type ViewProjection } from '@dxos/schema';
|
|
11
11
|
import { isNonNullable } from '@dxos/util';
|
|
@@ -16,7 +16,7 @@ import { type TableType } from '../types';
|
|
|
16
16
|
export type UseTableModelParams<T extends TableRow = TableRow> = {
|
|
17
17
|
table?: TableType;
|
|
18
18
|
projection?: ViewProjection;
|
|
19
|
-
rows?:
|
|
19
|
+
rows?: Live<T>[];
|
|
20
20
|
rowActions?: TableRowAction[];
|
|
21
21
|
onSelectionChanged?: (selection: string[]) => void;
|
|
22
22
|
onRowAction?: (actionId: string, data: T) => void;
|
|
@@ -70,7 +70,9 @@ export const useTableModel = <T extends TableRow = TableRow>({
|
|
|
70
70
|
}
|
|
71
71
|
}, [model, rows]);
|
|
72
72
|
|
|
73
|
-
const {
|
|
73
|
+
const { multiSelect, clear } = useSelectionActions(
|
|
74
|
+
[table?.id, table?.view?.target?.query.typename].filter(isNonNullable),
|
|
75
|
+
);
|
|
74
76
|
|
|
75
77
|
useEffect(() => {
|
|
76
78
|
if (!model) {
|
|
@@ -79,7 +81,7 @@ export const useTableModel = <T extends TableRow = TableRow>({
|
|
|
79
81
|
|
|
80
82
|
const unsubscribe = effect(() => {
|
|
81
83
|
const selectedItems = [...model.selection.selection.value];
|
|
82
|
-
|
|
84
|
+
multiSelect(selectedItems);
|
|
83
85
|
onSelectionChanged?.(selectedItems);
|
|
84
86
|
});
|
|
85
87
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { signal } from '@preact/signals-core';
|
|
6
6
|
import { type RefObject, type MutableRefObject } from 'react';
|
|
7
7
|
|
|
8
|
-
import { type
|
|
8
|
+
import { type Live } from '@dxos/live-object';
|
|
9
9
|
import { log } from '@dxos/log';
|
|
10
10
|
|
|
11
11
|
export type ColumnSettingsMode = { type: 'create' } | { type: 'edit'; fieldId: string };
|
|
@@ -18,7 +18,7 @@ export type ModalState =
|
|
|
18
18
|
type: 'createRefPanel';
|
|
19
19
|
typename: string;
|
|
20
20
|
initialValues?: Record<string, string>;
|
|
21
|
-
onCreate?: (obj:
|
|
21
|
+
onCreate?: (obj: Live<any>) => void;
|
|
22
22
|
}
|
|
23
23
|
| { type: 'columnSettings'; mode: ColumnSettingsMode }
|
|
24
24
|
| { type: 'closed' };
|
|
@@ -72,7 +72,7 @@ export class ModalController {
|
|
|
72
72
|
typename: string,
|
|
73
73
|
anchorCell: Element | null,
|
|
74
74
|
initialValues?: Record<string, string>,
|
|
75
|
-
onCreate?: (obj:
|
|
75
|
+
onCreate?: (obj: Live<any>) => void,
|
|
76
76
|
) => {
|
|
77
77
|
if (anchorCell) {
|
|
78
78
|
this._triggerRef.current = anchorCell as HTMLElement;
|
|
@@ -43,7 +43,7 @@ export class SelectionModel<T extends TableRow> extends Resource {
|
|
|
43
43
|
super();
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
protected override async _open() {
|
|
46
|
+
protected override async _open(): Promise<void> {
|
|
47
47
|
const selectionWatcher = effect(() => {
|
|
48
48
|
touch(this._selection.value);
|
|
49
49
|
this._onSelectionChanged?.();
|
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { computed } from '@preact/signals-core';
|
|
6
|
+
import { Schema } from 'effect';
|
|
6
7
|
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
7
8
|
|
|
8
|
-
import {
|
|
9
|
+
import { TypedObject } from '@dxos/echo-schema';
|
|
9
10
|
import { updateCounter } from '@dxos/echo-schema/testing';
|
|
10
11
|
import { registerSignalsRuntime } from '@dxos/echo-signals';
|
|
11
12
|
import { createEchoSchema } from '@dxos/live-object/testing';
|
|
12
|
-
import {
|
|
13
|
+
import { live, makeRef } from '@dxos/react-client/echo';
|
|
13
14
|
import { createView, ViewProjection } from '@dxos/schema';
|
|
14
15
|
|
|
15
16
|
import { TableModel, type TableModelProps } from './table-model';
|
|
@@ -69,7 +70,7 @@ describe('TableModel', () => {
|
|
|
69
70
|
|
|
70
71
|
describe('reactivity', () => {
|
|
71
72
|
it('pure signals should nest', () => {
|
|
72
|
-
const signal$ =
|
|
73
|
+
const signal$ = live({ arr: [{ thingInside: 1 }, { thingInside: 2 }] });
|
|
73
74
|
|
|
74
75
|
const computed$ = computed(() => {
|
|
75
76
|
return signal$.arr.map((row) =>
|
|
@@ -99,14 +100,14 @@ describe('TableModel', () => {
|
|
|
99
100
|
});
|
|
100
101
|
|
|
101
102
|
class Test extends TypedObject({ typename: 'example.com/type/Test', version: '0.1.0' })({
|
|
102
|
-
title:
|
|
103
|
-
completed:
|
|
103
|
+
title: Schema.String,
|
|
104
|
+
completed: Schema.Boolean,
|
|
104
105
|
}) {}
|
|
105
106
|
|
|
106
107
|
const createTableModel = (props: Partial<TableModelProps> = {}): TableModel => {
|
|
107
108
|
const schema = createEchoSchema(Test);
|
|
108
109
|
const view = createView({ name: 'Test', typename: schema.typename, jsonSchema: schema.jsonSchema });
|
|
109
110
|
const projection = new ViewProjection(schema.jsonSchema, view);
|
|
110
|
-
const table =
|
|
111
|
+
const table = live(TableType, { view: makeRef(view) });
|
|
111
112
|
return new TableModel({ id: table.id, space: undefined, view, projection, ...props });
|
|
112
113
|
};
|
package/src/model/table-model.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { type Space } from '@dxos/client/echo';
|
|
|
8
8
|
import { Resource } from '@dxos/context';
|
|
9
9
|
import { type FieldSortType, FormatEnum, getValue, setValue, type JsonProp } from '@dxos/echo-schema';
|
|
10
10
|
import { invariant } from '@dxos/invariant';
|
|
11
|
-
import {
|
|
11
|
+
import { isLiveObject, makeRef } from '@dxos/live-object';
|
|
12
12
|
import { formatForEditing, parseValue } from '@dxos/react-ui-form';
|
|
13
13
|
import {
|
|
14
14
|
type DxGridAxisMeta,
|
|
@@ -21,6 +21,7 @@ import { type ViewType, type ViewProjection } from '@dxos/schema';
|
|
|
21
21
|
import { type SelectionMode, SelectionModel } from './selection-model';
|
|
22
22
|
import { TableSorting } from './table-sorting';
|
|
23
23
|
import { touch } from '../util';
|
|
24
|
+
import { extractTagIds } from '../util/tag';
|
|
24
25
|
|
|
25
26
|
// TODO(burdon): Use schema types.
|
|
26
27
|
export type TableRow = Record<JsonProp, any> & { id: string };
|
|
@@ -201,7 +202,7 @@ export class TableModel<T extends TableRow = TableRow> extends Resource {
|
|
|
201
202
|
// Initialisation
|
|
202
203
|
//
|
|
203
204
|
|
|
204
|
-
protected override async _open() {
|
|
205
|
+
protected override async _open(): Promise<void> {
|
|
205
206
|
this.initializeColumnMeta();
|
|
206
207
|
this.initializeEffects();
|
|
207
208
|
this._selection = new SelectionModel(this._sorting.sortedRows, this._features.selection.mode ?? 'multiple', () =>
|
|
@@ -342,12 +343,28 @@ export class TableModel<T extends TableRow = TableRow> extends Resource {
|
|
|
342
343
|
case FormatEnum.Ref: {
|
|
343
344
|
// TODO(ZaymonFC): This get's called an additional time by the cell editor onBlur, but with the cell editors
|
|
344
345
|
// plain string value. Maybe onBlur should be called with the actual value?
|
|
345
|
-
if (
|
|
346
|
+
if (isLiveObject(value)) {
|
|
346
347
|
setValue(this._rows.value[rowIdx], field.path, makeRef(value));
|
|
347
348
|
}
|
|
348
349
|
break;
|
|
349
350
|
}
|
|
350
351
|
|
|
352
|
+
case FormatEnum.SingleSelect: {
|
|
353
|
+
const ids = extractTagIds(value);
|
|
354
|
+
if (ids && ids.length > 0) {
|
|
355
|
+
setValue(this._rows.value[rowIdx], field.path, ids[0]);
|
|
356
|
+
}
|
|
357
|
+
break;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
case FormatEnum.MultiSelect: {
|
|
361
|
+
const ids = extractTagIds(value);
|
|
362
|
+
if (ids) {
|
|
363
|
+
setValue(this._rows.value[rowIdx], field.path, ids);
|
|
364
|
+
}
|
|
365
|
+
break;
|
|
366
|
+
}
|
|
367
|
+
|
|
351
368
|
default: {
|
|
352
369
|
setValue(
|
|
353
370
|
this._rows.value[rowIdx],
|
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { Schema } from 'effect';
|
|
5
6
|
import { describe, expect, it, beforeEach } from 'vitest';
|
|
6
7
|
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
8
|
+
import { TypedObject } from '@dxos/echo-schema';
|
|
9
|
+
import { live, makeRef } from '@dxos/live-object';
|
|
9
10
|
import { createEchoSchema } from '@dxos/live-object/testing';
|
|
10
11
|
import { createView, ViewProjection } from '@dxos/schema';
|
|
11
12
|
|
|
@@ -22,7 +23,7 @@ describe('TablePresentation', () => {
|
|
|
22
23
|
|
|
23
24
|
beforeEach(async () => {
|
|
24
25
|
updateCount = 0;
|
|
25
|
-
({ data } =
|
|
26
|
+
({ data } = live({
|
|
26
27
|
data: [
|
|
27
28
|
{ col1: 'A', col2: 1, col3: true },
|
|
28
29
|
{ col1: 'B', col2: 2, col3: false },
|
|
@@ -89,14 +90,14 @@ describe('TablePresentation', () => {
|
|
|
89
90
|
});
|
|
90
91
|
|
|
91
92
|
class Test extends TypedObject({ typename: 'example.com/type/Test', version: '0.1.0' })({
|
|
92
|
-
title:
|
|
93
|
-
completed:
|
|
93
|
+
title: Schema.String,
|
|
94
|
+
completed: Schema.Boolean,
|
|
94
95
|
}) {}
|
|
95
96
|
|
|
96
97
|
const createTableModel = (props: Partial<TableModelProps> = {}): TableModel => {
|
|
97
98
|
const schema = createEchoSchema(Test);
|
|
98
99
|
const view = createView({ name: 'Test', typename: schema.typename, jsonSchema: schema.jsonSchema });
|
|
99
100
|
const projection = new ViewProjection(schema.jsonSchema, view);
|
|
100
|
-
const table =
|
|
101
|
+
const table = live(TableType, { view: makeRef(view) });
|
|
101
102
|
return new TableModel({ id: table.id, view, projection, ...props });
|
|
102
103
|
};
|