@dxos/react-ui-table 0.8.4-main.5ea62a8 → 0.8.4-main.60689f5b1c
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/LICENSE +102 -5
- package/dist/lib/browser/chunk-4URYOFDL.mjs +69 -0
- package/dist/lib/browser/chunk-4URYOFDL.mjs.map +7 -0
- package/dist/lib/browser/chunk-J5LGTIGS.mjs +10 -0
- package/dist/lib/browser/chunk-J5LGTIGS.mjs.map +7 -0
- package/dist/lib/browser/chunk-YDXPC7HA.mjs +1695 -0
- package/dist/lib/browser/chunk-YDXPC7HA.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +1139 -1198
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +35 -42
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/browser/translations.mjs +36 -0
- package/dist/lib/browser/translations.mjs.map +7 -0
- package/dist/lib/browser/types/index.mjs +4 -3
- package/dist/lib/node-esm/chunk-2AN3QQ3D.mjs +1697 -0
- package/dist/lib/node-esm/chunk-2AN3QQ3D.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-B2SZYAEC.mjs +70 -0
- package/dist/lib/node-esm/chunk-B2SZYAEC.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +11 -0
- package/dist/lib/node-esm/chunk-HSLMI22Q.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +1139 -1198
- 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 +35 -42
- package/dist/lib/node-esm/testing/index.mjs.map +3 -3
- package/dist/lib/node-esm/translations.mjs +37 -0
- package/dist/lib/node-esm/translations.mjs.map +7 -0
- package/dist/lib/node-esm/types/index.mjs +4 -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 +6 -7
- package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts +23 -39
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/Relations.stories.d.ts +96 -29
- 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 +20 -18
- package/dist/types/src/components/Table/Table.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.stories.d.ts +96 -31
- package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/TableContent.d.ts +18 -0
- package/dist/types/src/components/Table/TableContent.d.ts.map +1 -0
- package/dist/types/src/components/Table/TableToolbar.d.ts +21 -0
- package/dist/types/src/components/Table/TableToolbar.d.ts.map +1 -0
- package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts +1 -1
- package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +12 -12
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +7 -7
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts +25 -28
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/index.d.ts +1 -0
- package/dist/types/src/components/TableCellEditor/index.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +0 -1
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +23 -16
- 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/index.d.ts +0 -2
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/model/modal-controller.d.ts +5 -8
- package/dist/types/src/model/modal-controller.d.ts.map +1 -1
- package/dist/types/src/model/selection-model.d.ts +18 -9
- package/dist/types/src/model/selection-model.d.ts.map +1 -1
- package/dist/types/src/model/table-model.d.ts +105 -26
- 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/playwright/playwright.config.d.ts.map +1 -1
- package/dist/types/src/testing/useTestTableModel.d.ts +18 -29
- package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +21 -23
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/Table.d.ts +25 -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 -12
- 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/schema.d.ts +1 -1
- package/dist/types/src/util/schema.d.ts.map +1 -1
- package/dist/types/src/util/signals.d.ts.map +1 -1
- package/dist/types/src/util/sort.d.ts +12 -0
- package/dist/types/src/util/sort.d.ts.map +1 -0
- package/dist/types/src/util/table-buttons.d.ts +20 -20
- package/dist/types/src/util/table-buttons.d.ts.map +1 -1
- package/dist/types/src/util/table-controls.d.ts +4 -4
- package/dist/types/src/util/table-controls.d.ts.map +1 -1
- package/dist/types/src/util/tag.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +69 -78
- package/src/components/Table/ColumnActionsMenu.tsx +17 -12
- package/src/components/Table/ColumnSettings.tsx +8 -9
- package/src/components/Table/DynamicTable.stories.tsx +35 -34
- package/src/components/Table/DynamicTable.tsx +32 -36
- package/src/components/Table/Relations.stories.tsx +195 -48
- package/src/components/Table/RowActionsMenu.tsx +27 -17
- package/src/components/Table/Table.stories.tsx +145 -129
- package/src/components/Table/Table.tsx +43 -403
- package/src/components/Table/TableContent.tsx +425 -0
- package/src/components/Table/TableToolbar.tsx +111 -0
- package/src/components/TableCellEditor/CellValidationMessage.tsx +5 -6
- package/src/components/TableCellEditor/FormCellEditor.tsx +125 -131
- package/src/components/TableCellEditor/TableCellEditor.stories.tsx +32 -56
- package/src/components/TableCellEditor/TableCellEditor.tsx +47 -140
- package/src/components/TableCellEditor/index.ts +1 -0
- package/src/components/index.ts +0 -1
- package/src/components/testing.ts +44 -18
- 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/index.ts +0 -4
- package/src/model/modal-controller.ts +23 -40
- package/src/model/selection-model.ts +75 -44
- package/src/model/table-model.test.ts +93 -56
- package/src/model/table-model.ts +435 -163
- package/src/model/table-presentation.test.ts +67 -56
- package/src/model/table-presentation.ts +45 -42
- package/src/playwright/TableManager.ts +13 -6
- package/src/playwright/playwright.config.ts +1 -1
- package/src/playwright/smoke.spec.ts +59 -13
- package/src/testing/useTestTableModel.ts +49 -39
- package/src/translations.ts +20 -22
- package/src/types/Table.ts +78 -0
- package/src/types/index.ts +1 -1
- package/src/util/dynamic-table.test.ts +70 -0
- package/src/util/dynamic-table.ts +42 -30
- package/src/util/index.ts +2 -0
- package/src/util/query-ast.ts +22 -0
- package/src/util/schema.ts +4 -4
- 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-63UUF4IG.mjs +0 -66
- package/dist/lib/browser/chunk-63UUF4IG.mjs.map +0 -7
- package/dist/lib/browser/chunk-RQYXNWWN.mjs +0 -1822
- package/dist/lib/browser/chunk-RQYXNWWN.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-K734DJB7.mjs +0 -67
- package/dist/lib/node-esm/chunk-K734DJB7.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-QNDUGIS7.mjs +0 -1823
- package/dist/lib/node-esm/chunk-QNDUGIS7.mjs.map +0 -7
- package/dist/types/src/components/Table/CreateRefPanel.d.ts +0 -9
- package/dist/types/src/components/Table/CreateRefPanel.d.ts.map +0 -1
- package/dist/types/src/components/TableToolbar/TableToolbar.d.ts +0 -21
- package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +0 -1
- package/dist/types/src/components/TableToolbar/index.d.ts +0 -2
- package/dist/types/src/components/TableToolbar/index.d.ts.map +0 -1
- package/dist/types/src/model/table-sorting.d.ts +0 -53
- 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/components/Table/CreateRefPanel.tsx +0 -103
- package/src/components/TableToolbar/TableToolbar.tsx +0 -102
- package/src/components/TableToolbar/index.ts +0 -5
- package/src/model/table-sorting.ts +0 -216
- package/src/types/schema.ts +0 -73
|
@@ -4,16 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
import { type Completion } from '@codemirror/autocomplete';
|
|
6
6
|
import { EditorView } from '@codemirror/view';
|
|
7
|
-
import { type Schema } from 'effect/Schema';
|
|
8
7
|
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
|
9
8
|
|
|
10
9
|
import { debounce } from '@dxos/async';
|
|
11
|
-
import type
|
|
12
|
-
import {
|
|
10
|
+
import { type Type } from '@dxos/echo';
|
|
11
|
+
import { Format, TypeEnum } from '@dxos/echo/internal';
|
|
13
12
|
import { invariant } from '@dxos/invariant';
|
|
14
13
|
import { type DxGridAxis, type DxGridPosition } from '@dxos/lit-grid';
|
|
15
|
-
import { useThemeContext } from '@dxos/react-ui';
|
|
16
|
-
import { createMarkdownExtensions } from '@dxos/react-ui-editor';
|
|
17
14
|
import {
|
|
18
15
|
type EditorBlurHandler,
|
|
19
16
|
type EditorKeyEvent,
|
|
@@ -25,40 +22,37 @@ import {
|
|
|
25
22
|
parseCellIndex,
|
|
26
23
|
useGridContext,
|
|
27
24
|
} from '@dxos/react-ui-grid';
|
|
28
|
-
import { createLinks, tagPicker } from '@dxos/react-ui-tag-picker';
|
|
29
25
|
import { type FieldProjection } from '@dxos/schema';
|
|
30
26
|
|
|
31
27
|
import { type ModalController, type TableModel } from '../../model';
|
|
32
|
-
|
|
33
28
|
import { CellValidationMessage } from './CellValidationMessage';
|
|
34
|
-
import { FormCellEditor } from './FormCellEditor';
|
|
29
|
+
import { FormCellEditor, type OnCreateHandler } from './FormCellEditor';
|
|
35
30
|
|
|
36
31
|
/**
|
|
37
32
|
* Option to create new object/value.
|
|
38
33
|
*/
|
|
39
|
-
export type QueryResult = Pick<Completion, 'label'> & {
|
|
34
|
+
export type QueryResult = Pick<Completion, 'label'> & {
|
|
35
|
+
data: any;
|
|
36
|
+
};
|
|
40
37
|
|
|
41
|
-
export type TableCellEditorProps = {
|
|
38
|
+
export type TableCellEditorProps<T extends Type.AnyEntity = Type.AnyEntity> = {
|
|
39
|
+
schema?: T;
|
|
42
40
|
model?: TableModel;
|
|
43
41
|
modals?: ModalController;
|
|
44
|
-
schema?: Schema.AnyNoContext;
|
|
45
42
|
onFocus?: (axis?: DxGridAxis, delta?: -1 | 0 | 1, cell?: DxGridPosition) => void;
|
|
43
|
+
onCreate?: OnCreateHandler;
|
|
46
44
|
onSave?: () => void;
|
|
47
|
-
client?: Client;
|
|
48
45
|
};
|
|
49
46
|
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
export const TableValueEditor = ({
|
|
47
|
+
export const TableValueEditor = <T extends Type.AnyEntity = Type.AnyEntity>({
|
|
48
|
+
__gridScope,
|
|
49
|
+
schema,
|
|
54
50
|
model,
|
|
55
51
|
modals,
|
|
56
|
-
schema,
|
|
57
52
|
onFocus,
|
|
58
53
|
onSave,
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}: GridScopedProps<TableCellEditorProps>) => {
|
|
54
|
+
onCreate,
|
|
55
|
+
}: GridScopedProps<TableCellEditorProps<T>>) => {
|
|
62
56
|
const { editing } = useGridContext('TableValueEditor', __gridScope);
|
|
63
57
|
|
|
64
58
|
const fieldProjection = useMemo<FieldProjection | undefined>(() => {
|
|
@@ -67,7 +61,7 @@ export const TableValueEditor = ({
|
|
|
67
61
|
}
|
|
68
62
|
|
|
69
63
|
const { col } = parseCellIndex(editing.index);
|
|
70
|
-
const field = model.projection.
|
|
64
|
+
const field = model.projection.getFields()[col];
|
|
71
65
|
const fieldProjection = model.projection.getFieldProjection(field.id);
|
|
72
66
|
invariant(fieldProjection);
|
|
73
67
|
return fieldProjection;
|
|
@@ -75,39 +69,36 @@ export const TableValueEditor = ({
|
|
|
75
69
|
|
|
76
70
|
if (
|
|
77
71
|
fieldProjection?.props.type === TypeEnum.Array ||
|
|
78
|
-
fieldProjection?.props.format ===
|
|
79
|
-
fieldProjection?.props.format ===
|
|
80
|
-
// TODO(thure): Support `
|
|
72
|
+
fieldProjection?.props.format === Format.TypeFormat.SingleSelect ||
|
|
73
|
+
fieldProjection?.props.format === Format.TypeFormat.Ref
|
|
74
|
+
// TODO(thure): Support `Format.TypeFormat.MultiSelect`
|
|
81
75
|
) {
|
|
82
76
|
return (
|
|
83
|
-
<FormCellEditor
|
|
84
|
-
fieldProjection={fieldProjection}
|
|
85
|
-
model={model}
|
|
86
|
-
schema={schema}
|
|
77
|
+
<FormCellEditor<T>
|
|
87
78
|
__gridScope={__gridScope}
|
|
88
|
-
|
|
89
|
-
|
|
79
|
+
schema={schema}
|
|
80
|
+
model={model}
|
|
81
|
+
fieldProjection={fieldProjection}
|
|
90
82
|
modals={modals}
|
|
83
|
+
onSave={onSave}
|
|
84
|
+
onCreate={onCreate}
|
|
91
85
|
/>
|
|
92
86
|
);
|
|
93
87
|
}
|
|
94
88
|
|
|
95
|
-
// For all other types, use the existing cell editor
|
|
89
|
+
// For all other types, use the existing cell editor.
|
|
96
90
|
return <TableCellEditor model={model} modals={modals} onFocus={onFocus} onSave={onSave} __gridScope={__gridScope} />;
|
|
97
91
|
};
|
|
98
92
|
|
|
99
|
-
const editorSlots = { scroller: { className: '!plb-[--dx-grid-cell-editor-padding-block]' } };
|
|
100
|
-
|
|
101
93
|
export const TableCellEditor = ({
|
|
94
|
+
__gridScope,
|
|
102
95
|
model,
|
|
103
96
|
modals,
|
|
104
97
|
onFocus,
|
|
105
98
|
onSave,
|
|
106
|
-
__gridScope,
|
|
107
99
|
}: GridScopedProps<TableCellEditorProps>) => {
|
|
108
|
-
const {
|
|
100
|
+
const { editing, setEditing } = useGridContext('TableCellEditor', __gridScope);
|
|
109
101
|
const suppressNextBlur = useRef(false);
|
|
110
|
-
const { themeMode } = useThemeContext();
|
|
111
102
|
const [validationError, setValidationError] = useState<string | null>(null);
|
|
112
103
|
const [validationVariant, setValidationVariant] = useState<'error' | 'warning'>('error');
|
|
113
104
|
|
|
@@ -117,12 +108,13 @@ export const TableCellEditor = ({
|
|
|
117
108
|
}
|
|
118
109
|
|
|
119
110
|
const { col } = parseCellIndex(editing.index);
|
|
120
|
-
const field = model.projection.
|
|
111
|
+
const field = model.projection.getFields()[col];
|
|
121
112
|
const fieldProjection = model.projection.getFieldProjection(field.id);
|
|
122
113
|
invariant(fieldProjection);
|
|
123
114
|
return fieldProjection;
|
|
124
115
|
}, [model, editing]);
|
|
125
116
|
|
|
117
|
+
// TOOD(burdon): Attach to event handler?
|
|
126
118
|
const handleEnter = useCallback(
|
|
127
119
|
async (value: any) => {
|
|
128
120
|
if (!model || !editing) {
|
|
@@ -139,7 +131,7 @@ export const TableCellEditor = ({
|
|
|
139
131
|
setEditing(null);
|
|
140
132
|
onSave?.();
|
|
141
133
|
} else {
|
|
142
|
-
setValidationError(
|
|
134
|
+
setValidationError(validationResult.error);
|
|
143
135
|
setValidationVariant('error');
|
|
144
136
|
}
|
|
145
137
|
},
|
|
@@ -174,7 +166,6 @@ export const TableCellEditor = ({
|
|
|
174
166
|
}
|
|
175
167
|
|
|
176
168
|
const cell = parseCellIndex(editing.index);
|
|
177
|
-
|
|
178
169
|
if (value !== undefined) {
|
|
179
170
|
// Pre-commit validation check.
|
|
180
171
|
const result = await model.validateCellData(cell, value);
|
|
@@ -205,71 +196,26 @@ export const TableCellEditor = ({
|
|
|
205
196
|
[model, editing, onFocus, fieldProjection, setEditing, onSave],
|
|
206
197
|
);
|
|
207
198
|
|
|
208
|
-
const
|
|
199
|
+
const extensions = useMemo(() => {
|
|
209
200
|
if (!fieldProjection) {
|
|
210
201
|
return [];
|
|
211
202
|
}
|
|
212
203
|
|
|
213
|
-
const
|
|
204
|
+
const extensions = [
|
|
214
205
|
editorKeys({
|
|
215
206
|
onClose: handleClose,
|
|
216
207
|
...(editing?.initialContent && { onNav: handleClose }),
|
|
217
208
|
}),
|
|
218
209
|
];
|
|
219
|
-
|
|
220
|
-
const format = fieldProjection.props.format;
|
|
221
|
-
|
|
222
|
-
if (format === FormatEnum.SingleSelect || format === FormatEnum.MultiSelect) {
|
|
223
|
-
// TODO(ZaymonFC): Reconcile this with the TagPicker component?
|
|
224
|
-
// Add markdown extensions needed by tag picker.
|
|
225
|
-
extension.push(createMarkdownExtensions({ themeMode }));
|
|
226
|
-
|
|
227
|
-
const options = fieldProjection.props.options || [];
|
|
228
|
-
|
|
229
|
-
const mode = format === FormatEnum.SingleSelect ? ('single-select' as const) : ('multi-select' as const);
|
|
230
|
-
|
|
231
|
-
extension.push(
|
|
232
|
-
tagPicker({
|
|
233
|
-
mode,
|
|
234
|
-
keymap: false,
|
|
235
|
-
onSearch: (text, selectedIds) => {
|
|
236
|
-
return options
|
|
237
|
-
.filter(
|
|
238
|
-
(option) =>
|
|
239
|
-
selectedIds.indexOf(option.id) === -1 &&
|
|
240
|
-
(text.length === 0 || option.title.toLowerCase().includes(text.toLowerCase())),
|
|
241
|
-
)
|
|
242
|
-
.map((option) => ({
|
|
243
|
-
id: option.id,
|
|
244
|
-
label: option.title,
|
|
245
|
-
hue: option.color as any,
|
|
246
|
-
}));
|
|
247
|
-
},
|
|
248
|
-
onUpdate: (ids) => {
|
|
249
|
-
if (model && editing) {
|
|
250
|
-
if (ids.length === 0) {
|
|
251
|
-
return;
|
|
252
|
-
}
|
|
253
|
-
if (mode === 'single-select') {
|
|
254
|
-
void handleEnter(ids[0]);
|
|
255
|
-
} else {
|
|
256
|
-
void handleEnter(ids);
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
},
|
|
260
|
-
}),
|
|
261
|
-
);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// Add validation extension to handle content changes
|
|
210
|
+
// Add validation extension to handle content changes.
|
|
265
211
|
if (model && editing) {
|
|
266
|
-
|
|
212
|
+
extensions.push(
|
|
267
213
|
EditorView.updateListener.of(
|
|
268
214
|
debounce((update) => {
|
|
269
215
|
const content = update.state.doc.toString();
|
|
270
216
|
const cell = parseCellIndex(editing.index);
|
|
271
217
|
|
|
272
|
-
// Perform validation on content change
|
|
218
|
+
// Perform validation on content change.
|
|
273
219
|
void model.validateCellData(cell, content).then((result) => {
|
|
274
220
|
if (result.valid) {
|
|
275
221
|
setValidationError(null);
|
|
@@ -283,60 +229,12 @@ export const TableCellEditor = ({
|
|
|
283
229
|
);
|
|
284
230
|
}
|
|
285
231
|
|
|
286
|
-
return
|
|
287
|
-
}, [model, modals, editing, fieldProjection, handleClose
|
|
232
|
+
return extensions;
|
|
233
|
+
}, [model, modals, editing, fieldProjection, handleClose]);
|
|
288
234
|
|
|
289
235
|
const getCellContent = useCallback<GridCellEditorProps['getCellContent']>(() => {
|
|
290
236
|
if (model && editing) {
|
|
291
237
|
const cell = parseCellIndex(editing.index);
|
|
292
|
-
const { col } = cell;
|
|
293
|
-
const field = model.projection.fields[col];
|
|
294
|
-
const fieldProjection = model.projection.getFieldProjection(field.id);
|
|
295
|
-
|
|
296
|
-
if (
|
|
297
|
-
fieldProjection?.props.format === FormatEnum.SingleSelect ||
|
|
298
|
-
fieldProjection?.props.format === FormatEnum.MultiSelect
|
|
299
|
-
) {
|
|
300
|
-
const value = model.getCellData(cell);
|
|
301
|
-
|
|
302
|
-
if (value !== undefined) {
|
|
303
|
-
const options = fieldProjection.props.options || [];
|
|
304
|
-
|
|
305
|
-
if (fieldProjection.props.format === FormatEnum.MultiSelect) {
|
|
306
|
-
const tagItems = value
|
|
307
|
-
.split(',')
|
|
308
|
-
.map((id: string) => {
|
|
309
|
-
const option = options.find((o) => o.id === id);
|
|
310
|
-
if (option) {
|
|
311
|
-
return {
|
|
312
|
-
id,
|
|
313
|
-
label: option.title,
|
|
314
|
-
hue: option.color as any,
|
|
315
|
-
};
|
|
316
|
-
}
|
|
317
|
-
return undefined;
|
|
318
|
-
})
|
|
319
|
-
.filter((item: any): item is { id: any; label: string; hue: any } => item !== undefined);
|
|
320
|
-
|
|
321
|
-
return createLinks(tagItems);
|
|
322
|
-
} else {
|
|
323
|
-
const option = options.find((o) => o.id === value);
|
|
324
|
-
|
|
325
|
-
if (option) {
|
|
326
|
-
const tagItem = {
|
|
327
|
-
id: value,
|
|
328
|
-
label: option.title,
|
|
329
|
-
hue: option.color as any,
|
|
330
|
-
};
|
|
331
|
-
|
|
332
|
-
return createLinks([tagItem]);
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
return '';
|
|
338
|
-
}
|
|
339
|
-
|
|
340
238
|
const value = model.getCellData(cell);
|
|
341
239
|
return value !== undefined ? String(value) : '';
|
|
342
240
|
}
|
|
@@ -345,7 +243,16 @@ export const TableCellEditor = ({
|
|
|
345
243
|
return (
|
|
346
244
|
<>
|
|
347
245
|
<CellValidationMessage validationError={validationError} variant={validationVariant} __gridScope={__gridScope} />
|
|
348
|
-
<GridCellEditor
|
|
246
|
+
<GridCellEditor
|
|
247
|
+
extensions={extensions}
|
|
248
|
+
slots={{
|
|
249
|
+
content: {
|
|
250
|
+
className: '!py-(--dx-grid-cell-editor-padding-block)',
|
|
251
|
+
},
|
|
252
|
+
}}
|
|
253
|
+
getCellContent={getCellContent}
|
|
254
|
+
onBlur={handleBlur}
|
|
255
|
+
/>
|
|
349
256
|
</>
|
|
350
257
|
);
|
|
351
258
|
};
|
package/src/components/index.ts
CHANGED
|
@@ -2,33 +2,59 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { Atom } from '@effect-atom/atom-react';
|
|
6
|
+
import * as Schema from 'effect/Schema';
|
|
6
7
|
import { useEffect } from 'react';
|
|
7
8
|
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
9
|
+
import { Format, Type } from '@dxos/echo';
|
|
10
|
+
import { TypeEnum } from '@dxos/echo/internal';
|
|
11
|
+
import { setValue } from '@dxos/effect';
|
|
12
|
+
import { random } 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: 'com.example.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
|
-
name:
|
|
25
|
-
age:
|
|
26
|
-
active:
|
|
27
|
-
netWorth:
|
|
40
|
+
name: random.person.fullName(),
|
|
41
|
+
age: random.number.int({ min: 20, max: 70 }),
|
|
42
|
+
active: random.datatype.boolean(),
|
|
43
|
+
netWorth: random.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: random.person.fullName(),
|
|
54
|
+
age: random.number.int({ min: 20, max: 70 }),
|
|
55
|
+
active: random.datatype.boolean(),
|
|
56
|
+
netWorth: random.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 Schema } from 'effect';
|
|
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
|
};
|