@dxos/react-ui-table 0.8.3 → 0.8.4-main.c1de068
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-5XLSHLOG.mjs +1751 -0
- package/dist/lib/browser/chunk-5XLSHLOG.mjs.map +7 -0
- package/dist/lib/browser/chunk-L2LSB6KV.mjs +18 -0
- package/dist/lib/browser/chunk-L2LSB6KV.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +240 -1639
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +121 -0
- package/dist/lib/browser/testing/index.mjs.map +7 -0
- package/dist/lib/browser/types/index.mjs +3 -5
- package/dist/lib/node-esm/chunk-NXZ4CJYX.mjs +20 -0
- package/dist/lib/node-esm/chunk-NXZ4CJYX.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-Y66YLSTC.mjs +1752 -0
- package/dist/lib/node-esm/chunk-Y66YLSTC.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +240 -1639
- 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 +122 -0
- package/dist/lib/node-esm/testing/index.mjs.map +7 -0
- package/dist/lib/node-esm/types/index.mjs +3 -5
- package/dist/types/src/components/Table/ColumnSettings.d.ts +3 -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 +1 -1
- package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts +1 -1
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.stories.d.ts +1 -1
- package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/relations.stories.d.ts +1 -1
- package/dist/types/src/components/Table/relations.stories.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts +2 -1
- package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +2 -1
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +3 -2
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +3 -40
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/hooks/index.d.ts +1 -0
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useAddRow.d.ts +13 -0
- package/dist/types/src/hooks/useAddRow.d.ts.map +1 -0
- package/dist/types/src/hooks/useTableModel.d.ts +7 -6
- package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -2
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/model/selection-model.d.ts +2 -1
- package/dist/types/src/model/selection-model.d.ts.map +1 -1
- package/dist/types/src/model/table-model.d.ts +56 -20
- package/dist/types/src/model/table-model.d.ts.map +1 -1
- package/dist/types/src/model/table-presentation.d.ts +8 -0
- package/dist/types/src/model/table-presentation.d.ts.map +1 -1
- package/dist/types/src/model/table-sorting.d.ts +2 -2
- package/dist/types/src/model/table-sorting.d.ts.map +1 -1
- package/dist/types/src/playwright/playwright.config.d.ts +3 -0
- package/dist/types/src/playwright/playwright.config.d.ts.map +1 -0
- package/dist/types/src/testing/index.d.ts +2 -0
- package/dist/types/src/testing/index.d.ts.map +1 -0
- package/dist/types/src/testing/useTestTableModel.d.ts +41 -0
- package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -0
- package/dist/types/src/translations.d.ts +3 -4
- package/dist/types/src/translations.d.ts.map +1 -1
- 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/types/schema.d.ts +8 -0
- package/dist/types/src/types/schema.d.ts.map +1 -0
- package/dist/types/src/util/dynamic-table.d.ts +3 -4
- package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
- package/dist/types/src/util/table-buttons.d.ts +15 -1
- package/dist/types/src/util/table-buttons.d.ts.map +1 -1
- package/dist/types/src/util/table.d.ts +8 -11
- package/dist/types/src/util/table.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +48 -40
- package/src/components/Table/ColumnSettings.tsx +14 -10
- package/src/components/Table/CreateRefPanel.tsx +5 -3
- package/src/components/Table/DynamicTable.stories.tsx +2 -2
- package/src/components/Table/DynamicTable.tsx +13 -7
- package/src/components/Table/Table.stories.tsx +104 -148
- package/src/components/Table/Table.tsx +50 -32
- package/src/components/Table/relations.stories.tsx +27 -35
- package/src/components/TableCellEditor/CellValidationMessage.tsx +6 -1
- package/src/components/TableCellEditor/FormCellEditor.tsx +30 -21
- package/src/components/TableCellEditor/TableCellEditor.stories.tsx +24 -28
- package/src/components/TableCellEditor/TableCellEditor.tsx +79 -21
- package/src/components/testing.ts +7 -24
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useAddRow.ts +36 -0
- package/src/hooks/useTableModel.ts +20 -16
- package/src/index.ts +1 -2
- package/src/model/selection-model.ts +4 -2
- package/src/model/table-model.test.ts +7 -8
- package/src/model/table-model.ts +267 -78
- package/src/model/table-presentation.test.ts +6 -7
- package/src/model/table-presentation.ts +215 -132
- package/src/model/table-sorting.ts +9 -9
- package/src/playwright/playwright.config.ts +17 -0
- package/src/playwright/smoke.spec.ts +21 -20
- package/src/testing/index.ts +5 -0
- package/src/testing/useTestTableModel.ts +122 -0
- package/src/translations.ts +5 -3
- package/src/types/index.ts +2 -2
- package/src/types/schema.ts +16 -0
- package/src/util/dynamic-table.ts +19 -17
- package/src/util/table-buttons.ts +24 -1
- package/src/util/table.ts +33 -111
- package/dist/lib/browser/chunk-TL7CKQQV.mjs +0 -27
- package/dist/lib/browser/chunk-TL7CKQQV.mjs.map +0 -7
- package/dist/lib/node/chunk-O2VPBNS5.cjs +0 -50
- package/dist/lib/node/chunk-O2VPBNS5.cjs.map +0 -7
- package/dist/lib/node/index.cjs +0 -2739
- package/dist/lib/node/index.cjs.map +0 -7
- package/dist/lib/node/meta.json +0 -1
- package/dist/lib/node/types/index.cjs +0 -31
- package/dist/lib/node/types/index.cjs.map +0 -7
- package/dist/lib/node-esm/chunk-GBT6GD4G.mjs +0 -29
- package/dist/lib/node-esm/chunk-GBT6GD4G.mjs.map +0 -7
- package/dist/types/src/types/table.d.ts +0 -56
- package/dist/types/src/types/table.d.ts.map +0 -1
- package/src/playwright/playwright.config.cts +0 -18
- package/src/types/table.ts +0 -27
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { type Completion } from '@codemirror/autocomplete';
|
|
6
6
|
import { type Schema } from 'effect/Schema';
|
|
7
|
-
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
|
7
|
+
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
8
8
|
|
|
9
9
|
import { FormatEnum, TypeEnum } from '@dxos/echo-schema';
|
|
10
10
|
import { invariant } from '@dxos/invariant';
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
type GridCellEditorProps,
|
|
25
25
|
type GridScopedProps,
|
|
26
26
|
} from '@dxos/react-ui-grid';
|
|
27
|
-
import {
|
|
27
|
+
import { tagPicker, createLinks } from '@dxos/react-ui-tag-picker';
|
|
28
28
|
import { type FieldProjection } from '@dxos/schema';
|
|
29
29
|
|
|
30
30
|
import { CellValidationMessage } from './CellValidationMessage';
|
|
@@ -49,6 +49,7 @@ export type TableCellEditorProps = {
|
|
|
49
49
|
schema?: Schema.AnyNoContext;
|
|
50
50
|
onEnter?: (cell: DxGridPlanePosition) => void;
|
|
51
51
|
onFocus?: DxGrid['refocus'];
|
|
52
|
+
onSave?: () => void;
|
|
52
53
|
onQuery?: (field: FieldProjection, text: string) => Promise<QueryResult[]>;
|
|
53
54
|
};
|
|
54
55
|
|
|
@@ -58,6 +59,7 @@ export const TableValueEditor = ({
|
|
|
58
59
|
schema,
|
|
59
60
|
onEnter,
|
|
60
61
|
onFocus,
|
|
62
|
+
onSave,
|
|
61
63
|
onQuery,
|
|
62
64
|
__gridScope,
|
|
63
65
|
}: GridScopedProps<TableCellEditorProps>) => {
|
|
@@ -69,14 +71,26 @@ export const TableValueEditor = ({
|
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
const { col } = parseCellIndex(editing.index);
|
|
72
|
-
const field = model.projection.
|
|
74
|
+
const field = model.projection.fields[col];
|
|
73
75
|
const fieldProjection = model.projection.getFieldProjection(field.id);
|
|
74
76
|
invariant(fieldProjection);
|
|
75
77
|
return fieldProjection;
|
|
76
78
|
}, [model, editing]);
|
|
77
79
|
|
|
78
|
-
if (
|
|
79
|
-
|
|
80
|
+
if (
|
|
81
|
+
fieldProjection?.props.type === TypeEnum.Array ||
|
|
82
|
+
fieldProjection?.props.format === FormatEnum.SingleSelect
|
|
83
|
+
// TODO(thure): Support `FormatEnum.MultiSelect`
|
|
84
|
+
) {
|
|
85
|
+
return (
|
|
86
|
+
<FormCellEditor
|
|
87
|
+
fieldProjection={fieldProjection}
|
|
88
|
+
model={model}
|
|
89
|
+
schema={schema}
|
|
90
|
+
__gridScope={__gridScope}
|
|
91
|
+
onSave={onSave}
|
|
92
|
+
/>
|
|
93
|
+
);
|
|
80
94
|
}
|
|
81
95
|
|
|
82
96
|
// For all other types, use the existing cell editor
|
|
@@ -87,23 +101,28 @@ export const TableValueEditor = ({
|
|
|
87
101
|
onEnter={onEnter}
|
|
88
102
|
onFocus={onFocus}
|
|
89
103
|
onQuery={onQuery}
|
|
104
|
+
onSave={onSave}
|
|
90
105
|
__gridScope={__gridScope}
|
|
91
106
|
/>
|
|
92
107
|
);
|
|
93
108
|
};
|
|
94
109
|
|
|
110
|
+
const editorSlots = { scroller: { className: '!plb-[--dx-grid-cell-editor-padding-block]' } };
|
|
111
|
+
|
|
95
112
|
export const TableCellEditor = ({
|
|
96
113
|
model,
|
|
97
114
|
modals,
|
|
98
115
|
onEnter,
|
|
99
116
|
onFocus,
|
|
100
117
|
onQuery,
|
|
118
|
+
onSave,
|
|
101
119
|
__gridScope,
|
|
102
120
|
}: GridScopedProps<TableCellEditorProps>) => {
|
|
103
121
|
const { id: gridId, editing, setEditing } = useGridContext('TableCellEditor', __gridScope);
|
|
104
122
|
const suppressNextBlur = useRef(false);
|
|
105
123
|
const { themeMode } = useThemeContext();
|
|
106
124
|
const [_validationError, setValidationError] = useState<string | null>(null);
|
|
125
|
+
const [_validationVariant, setValidationVariant] = useState<'error' | 'warning'>('error');
|
|
107
126
|
|
|
108
127
|
const fieldProjection = useMemo<FieldProjection | undefined>(() => {
|
|
109
128
|
if (!model || !editing) {
|
|
@@ -111,12 +130,44 @@ export const TableCellEditor = ({
|
|
|
111
130
|
}
|
|
112
131
|
|
|
113
132
|
const { col } = parseCellIndex(editing.index);
|
|
114
|
-
const field = model.projection.
|
|
133
|
+
const field = model.projection.fields[col];
|
|
115
134
|
const fieldProjection = model.projection.getFieldProjection(field.id);
|
|
116
135
|
invariant(fieldProjection);
|
|
117
136
|
return fieldProjection;
|
|
118
137
|
}, [model, editing]);
|
|
119
138
|
|
|
139
|
+
useEffect(() => {
|
|
140
|
+
// Check for existing validation errors when editing starts (for draft rows).
|
|
141
|
+
if (!model || !editing || !fieldProjection) {
|
|
142
|
+
setValidationError(null);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const cell = parseCellIndex(editing.index);
|
|
147
|
+
const { row, col } = cell;
|
|
148
|
+
|
|
149
|
+
if (model.isDraftCell(cell)) {
|
|
150
|
+
const field = model.projection.fields[col];
|
|
151
|
+
const hasValidationError = model.hasDraftRowValidationError(row, field.path);
|
|
152
|
+
|
|
153
|
+
if (hasValidationError) {
|
|
154
|
+
const draftRows = model.draftRows.value;
|
|
155
|
+
if (row >= 0 && row < draftRows.length) {
|
|
156
|
+
const draftRow = draftRows[row];
|
|
157
|
+
const validationError = draftRow.validationErrors?.find((error) => error.path === field.path);
|
|
158
|
+
if (validationError) {
|
|
159
|
+
setValidationError(validationError.message);
|
|
160
|
+
setValidationVariant('warning');
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
} else {
|
|
164
|
+
setValidationError(null);
|
|
165
|
+
}
|
|
166
|
+
} else {
|
|
167
|
+
setValidationError(null);
|
|
168
|
+
}
|
|
169
|
+
}, [model, editing, fieldProjection]);
|
|
170
|
+
|
|
120
171
|
const handleEnter = useCallback(
|
|
121
172
|
async (value: any) => {
|
|
122
173
|
if (!model || !editing) {
|
|
@@ -124,19 +175,18 @@ export const TableCellEditor = ({
|
|
|
124
175
|
}
|
|
125
176
|
|
|
126
177
|
const cell = parseCellIndex(editing.index);
|
|
178
|
+
const validationResult = await model.validateCellData(cell, value);
|
|
127
179
|
|
|
128
|
-
|
|
129
|
-
const result = await model.validateCellData(cell, value);
|
|
130
|
-
|
|
131
|
-
if (result.valid) {
|
|
180
|
+
if (validationResult.valid) {
|
|
132
181
|
setValidationError(null);
|
|
133
182
|
model.setCellData(cell, value);
|
|
134
183
|
onEnter?.(cell);
|
|
135
184
|
onFocus?.();
|
|
136
185
|
setEditing(null);
|
|
186
|
+
onSave?.();
|
|
137
187
|
} else {
|
|
138
|
-
setValidationError(
|
|
139
|
-
|
|
188
|
+
setValidationError(validationResult.error);
|
|
189
|
+
setValidationVariant('error');
|
|
140
190
|
}
|
|
141
191
|
},
|
|
142
192
|
[model, editing, onEnter, onFocus, setEditing],
|
|
@@ -172,22 +222,25 @@ export const TableCellEditor = ({
|
|
|
172
222
|
setValidationError(null);
|
|
173
223
|
model.setCellData(cell, value);
|
|
174
224
|
setEditing(null);
|
|
225
|
+
onSave?.();
|
|
175
226
|
onEnter?.(cell);
|
|
176
227
|
if (event && onFocus) {
|
|
177
228
|
onFocus(determineNavigationAxis(event), determineNavigationDelta(event));
|
|
178
229
|
}
|
|
179
230
|
} else {
|
|
180
231
|
setValidationError(result.error);
|
|
232
|
+
setValidationVariant('error');
|
|
181
233
|
}
|
|
182
234
|
} else {
|
|
183
235
|
setValidationError(null);
|
|
184
236
|
setEditing(null);
|
|
237
|
+
onSave?.();
|
|
185
238
|
if (event && onFocus) {
|
|
186
239
|
onFocus(determineNavigationAxis(event), determineNavigationDelta(event));
|
|
187
240
|
}
|
|
188
241
|
}
|
|
189
242
|
},
|
|
190
|
-
[model, editing, onFocus, onEnter, fieldProjection, setEditing],
|
|
243
|
+
[model, editing, onFocus, onEnter, fieldProjection, setEditing, onSave],
|
|
191
244
|
);
|
|
192
245
|
|
|
193
246
|
const extension = useMemo(() => {
|
|
@@ -214,9 +267,9 @@ export const TableCellEditor = ({
|
|
|
214
267
|
const mode = format === FormatEnum.SingleSelect ? ('single-select' as const) : ('multi-select' as const);
|
|
215
268
|
|
|
216
269
|
extension.push(
|
|
217
|
-
|
|
270
|
+
tagPicker({
|
|
218
271
|
mode,
|
|
219
|
-
|
|
272
|
+
keymap: false,
|
|
220
273
|
onSearch: (text, selectedIds) => {
|
|
221
274
|
return options
|
|
222
275
|
.filter(
|
|
@@ -288,7 +341,7 @@ export const TableCellEditor = ({
|
|
|
288
341
|
if (model && editing) {
|
|
289
342
|
const cell = parseCellIndex(editing.index);
|
|
290
343
|
const { col } = cell;
|
|
291
|
-
const field = model.projection.
|
|
344
|
+
const field = model.projection.fields[col];
|
|
292
345
|
const fieldProjection = model.projection.getFieldProjection(field.id);
|
|
293
346
|
|
|
294
347
|
if (
|
|
@@ -300,9 +353,10 @@ export const TableCellEditor = ({
|
|
|
300
353
|
if (value !== undefined) {
|
|
301
354
|
const options = fieldProjection.props.options || [];
|
|
302
355
|
|
|
303
|
-
if (fieldProjection.props.format === FormatEnum.MultiSelect
|
|
356
|
+
if (fieldProjection.props.format === FormatEnum.MultiSelect) {
|
|
304
357
|
const tagItems = value
|
|
305
|
-
.
|
|
358
|
+
.split(',')
|
|
359
|
+
.map((id: string) => {
|
|
306
360
|
const option = options.find((o) => o.id === id);
|
|
307
361
|
if (option) {
|
|
308
362
|
return {
|
|
@@ -313,7 +367,7 @@ export const TableCellEditor = ({
|
|
|
313
367
|
}
|
|
314
368
|
return undefined;
|
|
315
369
|
})
|
|
316
|
-
.filter((item): item is { id: any; label: string; hue: any } => item !== undefined);
|
|
370
|
+
.filter((item: any): item is { id: any; label: string; hue: any } => item !== undefined);
|
|
317
371
|
|
|
318
372
|
return createLinks(tagItems);
|
|
319
373
|
} else {
|
|
@@ -341,8 +395,12 @@ export const TableCellEditor = ({
|
|
|
341
395
|
|
|
342
396
|
return (
|
|
343
397
|
<>
|
|
344
|
-
<CellValidationMessage
|
|
345
|
-
|
|
398
|
+
<CellValidationMessage
|
|
399
|
+
validationError={_validationError}
|
|
400
|
+
variant={_validationVariant}
|
|
401
|
+
__gridScope={__gridScope}
|
|
402
|
+
/>
|
|
403
|
+
<GridCellEditor extension={extension} getCellContent={getCellContent} onBlur={handleBlur} slots={editorSlots} />
|
|
346
404
|
</>
|
|
347
405
|
);
|
|
348
406
|
};
|
|
@@ -5,13 +5,10 @@
|
|
|
5
5
|
import { Schema } from 'effect';
|
|
6
6
|
import { useEffect } from 'react';
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
8
|
+
import { FormatEnum, ObjectId, setValue, TypedObject, TypeEnum } from '@dxos/echo-schema';
|
|
9
|
+
import { live } from '@dxos/live-object';
|
|
10
10
|
import { faker } from '@dxos/random';
|
|
11
|
-
import {
|
|
12
|
-
import { createView, type ViewProjection } from '@dxos/schema';
|
|
13
|
-
|
|
14
|
-
import { TableType } from '../types';
|
|
11
|
+
import { type ProjectionModel } from '@dxos/schema';
|
|
15
12
|
|
|
16
13
|
export const TestSchema = TypedObject({ typename: 'example.com/type/Test', version: '0.1.0' })({
|
|
17
14
|
id: ObjectId,
|
|
@@ -21,18 +18,6 @@ export const TestSchema = TypedObject({ typename: 'example.com/type/Test', versi
|
|
|
21
18
|
netWorth: Schema.optional(Schema.Number),
|
|
22
19
|
});
|
|
23
20
|
|
|
24
|
-
export const createTable = (schema = TestSchema) => {
|
|
25
|
-
return Obj.make(TableType, {
|
|
26
|
-
view: Ref.make(
|
|
27
|
-
createView({
|
|
28
|
-
name: 'Test',
|
|
29
|
-
typename: schema.typename,
|
|
30
|
-
jsonSchema: toJsonSchema(schema),
|
|
31
|
-
}),
|
|
32
|
-
),
|
|
33
|
-
});
|
|
34
|
-
};
|
|
35
|
-
|
|
36
21
|
export const createItems = (n: number) => {
|
|
37
22
|
const { data } = live({
|
|
38
23
|
data: Array.from({ length: n }, () => ({
|
|
@@ -47,13 +32,13 @@ export const createItems = (n: number) => {
|
|
|
47
32
|
};
|
|
48
33
|
|
|
49
34
|
export type SimulatorProps = {
|
|
50
|
-
|
|
35
|
+
projection: ProjectionModel;
|
|
51
36
|
items: any[];
|
|
52
37
|
insertInterval?: number;
|
|
53
38
|
updateInterval?: number;
|
|
54
39
|
};
|
|
55
40
|
|
|
56
|
-
export const useSimulator = ({ items,
|
|
41
|
+
export const useSimulator = ({ items, projection, insertInterval, updateInterval }: SimulatorProps) => {
|
|
57
42
|
useEffect(() => {
|
|
58
43
|
if (!insertInterval) {
|
|
59
44
|
return;
|
|
@@ -74,10 +59,8 @@ export const useSimulator = ({ items, table, insertInterval, updateInterval }: S
|
|
|
74
59
|
|
|
75
60
|
const i = setInterval(() => {
|
|
76
61
|
const rowIdx = Math.floor(Math.random() * items.length);
|
|
77
|
-
const fields =
|
|
62
|
+
const fields = projection.fields ?? [];
|
|
78
63
|
const columnIdx = Math.floor(Math.random() * fields.length);
|
|
79
|
-
// TODO(ZaymonFC): ... This is borked.
|
|
80
|
-
const projection: ViewProjection = (table as any)._projection;
|
|
81
64
|
const field = fields[columnIdx];
|
|
82
65
|
const item = items[rowIdx];
|
|
83
66
|
|
|
@@ -115,5 +98,5 @@ export const useSimulator = ({ items, table, insertInterval, updateInterval }: S
|
|
|
115
98
|
}, updateInterval);
|
|
116
99
|
|
|
117
100
|
return () => clearInterval(i);
|
|
118
|
-
}, [items,
|
|
101
|
+
}, [items, projection.fields, updateInterval]);
|
|
119
102
|
};
|
package/src/hooks/index.ts
CHANGED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type Schema } from 'effect';
|
|
6
|
+
import { useCallback } from 'react';
|
|
7
|
+
|
|
8
|
+
import { type Space } from '@dxos/client/echo';
|
|
9
|
+
import { Obj } from '@dxos/echo';
|
|
10
|
+
|
|
11
|
+
export type UseAddRowParams = {
|
|
12
|
+
space?: Space;
|
|
13
|
+
schema?: Schema.Schema.AnyNoContext;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Hook that provides a safe row creation function that handles schema validation failures.
|
|
18
|
+
* Returns a callback that attempts to create an object and returns boolean success/failure.
|
|
19
|
+
* Can accept optional data to create objects with specific content (used for committing draft rows).
|
|
20
|
+
*/
|
|
21
|
+
export const useAddRow = ({ space, schema }: UseAddRowParams) => {
|
|
22
|
+
return useCallback(
|
|
23
|
+
(data?: any) => {
|
|
24
|
+
if (space && schema) {
|
|
25
|
+
try {
|
|
26
|
+
space.db.add(Obj.make(schema, data ?? {}));
|
|
27
|
+
return true;
|
|
28
|
+
} catch (error) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return false;
|
|
33
|
+
},
|
|
34
|
+
[space, schema],
|
|
35
|
+
);
|
|
36
|
+
};
|
|
@@ -4,19 +4,21 @@
|
|
|
4
4
|
|
|
5
5
|
import { effect } from '@preact/signals-core';
|
|
6
6
|
import orderBy from 'lodash.orderby';
|
|
7
|
-
import { useEffect, useState } from 'react';
|
|
7
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
8
8
|
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
9
|
+
import { type JsonSchemaType } from '@dxos/echo-schema';
|
|
10
|
+
import { type Live } from '@dxos/live-object';
|
|
11
|
+
import { fullyQualifiedId } from '@dxos/react-client/echo';
|
|
12
|
+
import { useSelected, useSelectionActions } from '@dxos/react-ui-attention';
|
|
13
|
+
import { type DataType, type ProjectionModel } from '@dxos/schema';
|
|
12
14
|
import { isNonNullable } from '@dxos/util';
|
|
13
15
|
|
|
14
16
|
import { type TableRow, TableModel, type TableModelProps, type TableRowAction } from '../model';
|
|
15
|
-
import { type TableType } from '../types';
|
|
16
17
|
|
|
17
18
|
export type UseTableModelParams<T extends TableRow = TableRow> = {
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
view?: DataType.View;
|
|
20
|
+
schema?: JsonSchemaType;
|
|
21
|
+
projection?: ProjectionModel;
|
|
20
22
|
rows?: Live<T>[];
|
|
21
23
|
rowActions?: TableRowAction[];
|
|
22
24
|
onSelectionChanged?: (selection: string[]) => void;
|
|
@@ -27,7 +29,8 @@ export type UseTableModelParams<T extends TableRow = TableRow> = {
|
|
|
27
29
|
>;
|
|
28
30
|
|
|
29
31
|
export const useTableModel = <T extends TableRow = TableRow>({
|
|
30
|
-
|
|
32
|
+
view,
|
|
33
|
+
schema,
|
|
31
34
|
projection,
|
|
32
35
|
rows,
|
|
33
36
|
rowActions,
|
|
@@ -36,21 +39,24 @@ export const useTableModel = <T extends TableRow = TableRow>({
|
|
|
36
39
|
onRowAction,
|
|
37
40
|
...props
|
|
38
41
|
}: UseTableModelParams<T>): TableModel<T> | undefined => {
|
|
42
|
+
const selected = useSelected(view && fullyQualifiedId(view), 'multi');
|
|
43
|
+
const initialSelection = useMemo(() => selected, [view]);
|
|
44
|
+
|
|
39
45
|
const [model, setModel] = useState<TableModel<T>>();
|
|
40
46
|
useEffect(() => {
|
|
41
|
-
if (!
|
|
47
|
+
if (!view || !schema) {
|
|
42
48
|
return;
|
|
43
49
|
}
|
|
44
50
|
|
|
45
51
|
let model: TableModel<T> | undefined;
|
|
46
52
|
const t = setTimeout(async () => {
|
|
47
53
|
model = new TableModel<T>({
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
view: table.view?.target,
|
|
54
|
+
view,
|
|
55
|
+
schema,
|
|
51
56
|
projection,
|
|
52
57
|
features,
|
|
53
58
|
rowActions,
|
|
59
|
+
initialSelection,
|
|
54
60
|
onRowAction,
|
|
55
61
|
...props,
|
|
56
62
|
});
|
|
@@ -62,7 +68,7 @@ export const useTableModel = <T extends TableRow = TableRow>({
|
|
|
62
68
|
clearTimeout(t);
|
|
63
69
|
void model?.close();
|
|
64
70
|
};
|
|
65
|
-
}, [
|
|
71
|
+
}, [view, schema, projection, features, rowActions, initialSelection]); // TODO(burdon): Trigger if callbacks change?
|
|
66
72
|
|
|
67
73
|
// Update data.
|
|
68
74
|
useEffect(() => {
|
|
@@ -77,9 +83,7 @@ export const useTableModel = <T extends TableRow = TableRow>({
|
|
|
77
83
|
}
|
|
78
84
|
}, [model, rows]);
|
|
79
85
|
|
|
80
|
-
const { multiSelect, clear } = useSelectionActions(
|
|
81
|
-
[table?.id, table?.view?.target?.query.typename].filter(isNonNullable),
|
|
82
|
-
);
|
|
86
|
+
const { multiSelect, clear } = useSelectionActions([model?.id].filter(isNonNullable));
|
|
83
87
|
|
|
84
88
|
useEffect(() => {
|
|
85
89
|
if (!model) {
|
package/src/index.ts
CHANGED
|
@@ -2,12 +2,11 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import translations from './translations';
|
|
5
|
+
import { translations } from './translations';
|
|
6
6
|
|
|
7
7
|
export * from './components';
|
|
8
8
|
export * from './hooks';
|
|
9
9
|
export * from './model';
|
|
10
|
-
export * from './types';
|
|
11
10
|
export * from './util';
|
|
12
11
|
|
|
13
12
|
export { translations };
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { computed, effect, signal, type ReadonlySignal } from '@preact/signals-core';
|
|
5
|
+
import { computed, effect, type Signal, signal, type ReadonlySignal } from '@preact/signals-core';
|
|
6
6
|
|
|
7
7
|
import { Resource } from '@dxos/context';
|
|
8
8
|
|
|
@@ -12,7 +12,7 @@ import { touch } from '../util';
|
|
|
12
12
|
export type SelectionMode = 'single' | 'multiple';
|
|
13
13
|
|
|
14
14
|
export class SelectionModel<T extends TableRow> extends Resource {
|
|
15
|
-
private readonly _selection
|
|
15
|
+
private readonly _selection: Signal<Set<string>>;
|
|
16
16
|
|
|
17
17
|
private readonly _validSelectedIds = computed<Set<string>>(() => {
|
|
18
18
|
const rows = this._rows.value;
|
|
@@ -38,9 +38,11 @@ export class SelectionModel<T extends TableRow> extends Resource {
|
|
|
38
38
|
constructor(
|
|
39
39
|
private readonly _rows: ReadonlySignal<T[]>,
|
|
40
40
|
private readonly _selectionMode: SelectionMode,
|
|
41
|
+
private readonly _initialSelection: string[],
|
|
41
42
|
private readonly _onSelectionChanged?: () => void,
|
|
42
43
|
) {
|
|
43
44
|
super();
|
|
45
|
+
this._selection = signal(new Set(this._initialSelection));
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
protected override async _open(): Promise<void> {
|
|
@@ -6,16 +6,16 @@ import { computed } from '@preact/signals-core';
|
|
|
6
6
|
import { Schema } from 'effect';
|
|
7
7
|
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
8
8
|
|
|
9
|
-
import { Obj
|
|
9
|
+
import { Obj } from '@dxos/echo';
|
|
10
10
|
import { TypedObject } from '@dxos/echo-schema';
|
|
11
11
|
import { updateCounter } from '@dxos/echo-schema/testing';
|
|
12
12
|
import { registerSignalsRuntime } from '@dxos/echo-signals';
|
|
13
|
+
import { live } from '@dxos/live-object';
|
|
13
14
|
import { createEchoSchema } from '@dxos/live-object/testing';
|
|
14
|
-
import {
|
|
15
|
-
import { createView, ViewProjection } from '@dxos/schema';
|
|
15
|
+
import { createView } from '@dxos/schema';
|
|
16
16
|
|
|
17
17
|
import { TableModel, type TableModelProps } from './table-model';
|
|
18
|
-
import {
|
|
18
|
+
import { TableView } from '../types';
|
|
19
19
|
|
|
20
20
|
// TODO(burdon): Tests are disabled in project.json since they bring in plugin deps.
|
|
21
21
|
// Restore once factored out into react-ui-table.
|
|
@@ -107,8 +107,7 @@ class Test extends TypedObject({ typename: 'example.com/type/Test', version: '0.
|
|
|
107
107
|
|
|
108
108
|
const createTableModel = (props: Partial<TableModelProps> = {}): TableModel => {
|
|
109
109
|
const schema = createEchoSchema(Test);
|
|
110
|
-
const
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
return new TableModel({ id: table.id, space: undefined, view, projection, ...props });
|
|
110
|
+
const table = Obj.make(TableView, { sizes: {} });
|
|
111
|
+
const view = createView({ typename: schema.typename, jsonSchema: schema.jsonSchema, presentation: table });
|
|
112
|
+
return new TableModel({ view, schema: schema.jsonSchema, ...props });
|
|
114
113
|
};
|