@dxos/react-ui-table 0.8.3-staging.0fa589b → 0.8.4-main.f9ba587
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-TL7CKQQV.mjs → chunk-Q5QQXJUO.mjs} +2 -2
- package/dist/lib/browser/index.mjs +552 -227
- 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-esm/{chunk-GBT6GD4G.mjs → chunk-ZNOU6LHG.mjs} +2 -2
- package/dist/lib/node-esm/index.mjs +552 -227
- 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/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/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 +2 -2
- 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/index.d.ts +1 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/model/table-model.d.ts +42 -6
- 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/playwright/playwright.config.d.ts +3 -0
- package/dist/types/src/playwright/playwright.config.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/table.d.ts +4 -4
- 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/tsconfig.tsbuildinfo +1 -1
- package/package.json +38 -40
- package/src/components/Table/DynamicTable.stories.tsx +2 -2
- package/src/components/Table/Table.stories.tsx +18 -12
- package/src/components/Table/Table.tsx +17 -4
- package/src/components/Table/relations.stories.tsx +4 -5
- package/src/components/TableCellEditor/CellValidationMessage.tsx +6 -1
- package/src/components/TableCellEditor/TableCellEditor.stories.tsx +3 -3
- package/src/components/TableCellEditor/TableCellEditor.tsx +54 -15
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useAddRow.ts +36 -0
- package/src/index.ts +1 -1
- package/src/model/table-model.ts +220 -34
- package/src/model/table-presentation.ts +213 -130
- package/src/playwright/playwright.config.ts +17 -0
- package/src/playwright/smoke.spec.ts +9 -7
- package/src/translations.ts +5 -3
- package/src/util/table-buttons.ts +24 -1
- 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/src/playwright/playwright.config.cts +0 -18
- /package/dist/lib/browser/{chunk-TL7CKQQV.mjs.map → chunk-Q5QQXJUO.mjs.map} +0 -0
- /package/dist/lib/node-esm/{chunk-GBT6GD4G.mjs.map → chunk-ZNOU6LHG.mjs.map} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/react-ui-table",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.4-main.f9ba587",
|
|
4
4
|
"description": "A table component.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -53,60 +53,58 @@
|
|
|
53
53
|
"@radix-ui/react-use-controllable-state": "1.1.0",
|
|
54
54
|
"date-fns": "^3.3.1",
|
|
55
55
|
"lodash.orderby": "^4.6.0",
|
|
56
|
-
"@dxos/context": "0.8.
|
|
57
|
-
"@dxos/debug": "0.8.
|
|
58
|
-
"@dxos/echo
|
|
59
|
-
"@dxos/
|
|
60
|
-
"@dxos/
|
|
61
|
-
"@dxos/invariant": "0.8.
|
|
62
|
-
"@dxos/
|
|
63
|
-
"@dxos/
|
|
64
|
-
"@dxos/
|
|
65
|
-
"@dxos/
|
|
66
|
-
"@dxos/plugin-search": "0.8.
|
|
67
|
-
"@dxos/plugin-space": "0.8.
|
|
68
|
-
"@dxos/react-client": "0.8.
|
|
69
|
-
"@dxos/react-ui-attention": "0.8.
|
|
70
|
-
"@dxos/react-ui-editor": "0.8.
|
|
71
|
-
"@dxos/react-ui-form": "0.8.
|
|
72
|
-
"@dxos/react-ui-
|
|
73
|
-
"@dxos/react-ui-
|
|
74
|
-
"@dxos/react-ui-searchlist": "0.8.
|
|
75
|
-
"@dxos/react-ui-stack": "0.8.
|
|
76
|
-
"@dxos/react-ui-syntax-highlighter": "0.8.
|
|
77
|
-
"@dxos/react-ui-
|
|
78
|
-
"@dxos/react-ui-
|
|
79
|
-
"@dxos/schema": "0.8.
|
|
80
|
-
"@dxos/util": "0.8.
|
|
56
|
+
"@dxos/context": "0.8.4-main.f9ba587",
|
|
57
|
+
"@dxos/debug": "0.8.4-main.f9ba587",
|
|
58
|
+
"@dxos/echo": "0.8.4-main.f9ba587",
|
|
59
|
+
"@dxos/echo-schema": "0.8.4-main.f9ba587",
|
|
60
|
+
"@dxos/effect": "0.8.4-main.f9ba587",
|
|
61
|
+
"@dxos/invariant": "0.8.4-main.f9ba587",
|
|
62
|
+
"@dxos/keys": "0.8.4-main.f9ba587",
|
|
63
|
+
"@dxos/lit-grid": "0.8.4-main.f9ba587",
|
|
64
|
+
"@dxos/log": "0.8.4-main.f9ba587",
|
|
65
|
+
"@dxos/live-object": "0.8.4-main.f9ba587",
|
|
66
|
+
"@dxos/plugin-search": "0.8.4-main.f9ba587",
|
|
67
|
+
"@dxos/plugin-space": "0.8.4-main.f9ba587",
|
|
68
|
+
"@dxos/react-client": "0.8.4-main.f9ba587",
|
|
69
|
+
"@dxos/react-ui-attention": "0.8.4-main.f9ba587",
|
|
70
|
+
"@dxos/react-ui-editor": "0.8.4-main.f9ba587",
|
|
71
|
+
"@dxos/react-ui-form": "0.8.4-main.f9ba587",
|
|
72
|
+
"@dxos/react-ui-grid": "0.8.4-main.f9ba587",
|
|
73
|
+
"@dxos/react-ui-menu": "0.8.4-main.f9ba587",
|
|
74
|
+
"@dxos/react-ui-searchlist": "0.8.4-main.f9ba587",
|
|
75
|
+
"@dxos/react-ui-stack": "0.8.4-main.f9ba587",
|
|
76
|
+
"@dxos/react-ui-syntax-highlighter": "0.8.4-main.f9ba587",
|
|
77
|
+
"@dxos/react-ui-tag-picker": "0.8.4-main.f9ba587",
|
|
78
|
+
"@dxos/react-ui-types": "0.8.4-main.f9ba587",
|
|
79
|
+
"@dxos/schema": "0.8.4-main.f9ba587",
|
|
80
|
+
"@dxos/util": "0.8.4-main.f9ba587"
|
|
81
81
|
},
|
|
82
82
|
"devDependencies": {
|
|
83
|
-
"@effect-rx/rx-react": "
|
|
84
|
-
"@effect/platform": "0.
|
|
85
|
-
"@phosphor-icons/react": "^2.1.5",
|
|
83
|
+
"@effect-rx/rx-react": "0.38.0",
|
|
84
|
+
"@effect/platform": "0.88.0",
|
|
86
85
|
"@types/lodash.orderby": "^4.6.9",
|
|
87
86
|
"@types/react": "~18.2.0",
|
|
88
87
|
"@types/react-dom": "~18.2.0",
|
|
89
|
-
"effect": "3.
|
|
88
|
+
"effect": "3.16.13",
|
|
90
89
|
"fast-check": "^3.19.0",
|
|
91
90
|
"react": "~18.2.0",
|
|
92
91
|
"react-dom": "~18.2.0",
|
|
93
92
|
"vite": "5.4.7",
|
|
94
|
-
"@dxos/echo-signals": "0.8.
|
|
95
|
-
"@dxos/random": "0.8.
|
|
96
|
-
"@dxos/react-ui": "0.8.
|
|
97
|
-
"@dxos/react-ui-theme": "0.8.
|
|
98
|
-
"@dxos/storybook-utils": "0.8.
|
|
99
|
-
"@dxos/test-utils": "0.8.
|
|
93
|
+
"@dxos/echo-signals": "0.8.4-main.f9ba587",
|
|
94
|
+
"@dxos/random": "0.8.4-main.f9ba587",
|
|
95
|
+
"@dxos/react-ui": "0.8.4-main.f9ba587",
|
|
96
|
+
"@dxos/react-ui-theme": "0.8.4-main.f9ba587",
|
|
97
|
+
"@dxos/storybook-utils": "0.8.4-main.f9ba587",
|
|
98
|
+
"@dxos/test-utils": "0.8.4-main.f9ba587"
|
|
100
99
|
},
|
|
101
100
|
"peerDependencies": {
|
|
102
101
|
"@effect-rx/rx-react": "^0.34.1",
|
|
103
|
-
"@effect/platform": "0.80.12",
|
|
104
|
-
"@phosphor-icons/react": "^2.1.5",
|
|
102
|
+
"@effect/platform": "^0.80.12",
|
|
105
103
|
"effect": "^3.13.3",
|
|
106
104
|
"react": "~18.2.0",
|
|
107
105
|
"react-dom": "~18.2.0",
|
|
108
|
-
"@dxos/react-ui": "0.8.
|
|
109
|
-
"@dxos/react-ui-theme": "0.8.
|
|
106
|
+
"@dxos/react-ui": "0.8.4-main.f9ba587",
|
|
107
|
+
"@dxos/react-ui-theme": "0.8.4-main.f9ba587"
|
|
110
108
|
},
|
|
111
109
|
"publishConfig": {
|
|
112
110
|
"access": "public"
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import '@dxos-theme';
|
|
6
6
|
|
|
7
|
-
import { type StoryObj, type Meta } from '@storybook/react';
|
|
7
|
+
import { type StoryObj, type Meta } from '@storybook/react-vite';
|
|
8
8
|
import React, { useMemo, useState } from 'react';
|
|
9
9
|
|
|
10
10
|
import { Obj } from '@dxos/echo';
|
|
@@ -19,7 +19,7 @@ import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
|
19
19
|
|
|
20
20
|
import { DynamicTable } from './DynamicTable';
|
|
21
21
|
import { type TableFeatures } from '../../model';
|
|
22
|
-
import translations from '../../translations';
|
|
22
|
+
import { translations } from '../../translations';
|
|
23
23
|
|
|
24
24
|
faker.seed(0);
|
|
25
25
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import '@dxos-theme';
|
|
6
6
|
|
|
7
|
-
import { type StoryObj, type Meta } from '@storybook/react';
|
|
7
|
+
import { type StoryObj, type Meta } from '@storybook/react-vite';
|
|
8
8
|
import { Schema } from 'effect';
|
|
9
9
|
import React, { useCallback, useMemo, useRef } from 'react';
|
|
10
10
|
|
|
@@ -23,9 +23,9 @@ import { Testing, createObjectFactory } from '@dxos/schema/testing';
|
|
|
23
23
|
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
24
24
|
|
|
25
25
|
import { Table, type TableController } from './Table';
|
|
26
|
-
import { useTableModel } from '../../hooks';
|
|
26
|
+
import { useTableModel, useAddRow } from '../../hooks';
|
|
27
27
|
import { TablePresentation } from '../../model';
|
|
28
|
-
import translations from '../../translations';
|
|
28
|
+
import { translations } from '../../translations';
|
|
29
29
|
import { TableType } from '../../types';
|
|
30
30
|
import { initializeTable } from '../../util';
|
|
31
31
|
import { TableToolbar } from '../TableToolbar';
|
|
@@ -72,11 +72,7 @@ const useTestTableModel = () => {
|
|
|
72
72
|
tableRef.current?.update?.();
|
|
73
73
|
}, []);
|
|
74
74
|
|
|
75
|
-
const
|
|
76
|
-
if (space && schema) {
|
|
77
|
-
space.db.add(live(schema, {}));
|
|
78
|
-
}
|
|
79
|
-
}, [space, schema]);
|
|
75
|
+
const addRow = useAddRow({ space, schema });
|
|
80
76
|
|
|
81
77
|
const handleDeleteRows = useCallback(
|
|
82
78
|
(_: number, objects: any[]) => {
|
|
@@ -101,13 +97,17 @@ const useTestTableModel = () => {
|
|
|
101
97
|
projection,
|
|
102
98
|
features,
|
|
103
99
|
rows: filteredObjects,
|
|
104
|
-
onInsertRow:
|
|
100
|
+
onInsertRow: addRow,
|
|
105
101
|
onDeleteRows: handleDeleteRows,
|
|
106
102
|
onDeleteColumn: handleDeleteColumn,
|
|
107
103
|
onCellUpdate: handleCellUpdate,
|
|
108
104
|
onRowOrderChange: handleRowOrderChange,
|
|
109
105
|
});
|
|
110
106
|
|
|
107
|
+
const handleInsertRow = useCallback(() => {
|
|
108
|
+
model?.insertRow();
|
|
109
|
+
}, [model]);
|
|
110
|
+
|
|
111
111
|
const handleSaveView = useCallback(() => {
|
|
112
112
|
model?.saveView();
|
|
113
113
|
}, [model]);
|
|
@@ -209,8 +209,16 @@ type StoryProps = { rows?: number };
|
|
|
209
209
|
const meta: Meta<StoryProps> = {
|
|
210
210
|
title: 'ui/react-ui-table/Table',
|
|
211
211
|
render: DefaultStory,
|
|
212
|
-
parameters: {
|
|
212
|
+
parameters: {
|
|
213
|
+
translations,
|
|
214
|
+
layout: 'fullscreen',
|
|
215
|
+
controls: {
|
|
216
|
+
disable: true,
|
|
217
|
+
},
|
|
218
|
+
},
|
|
213
219
|
decorators: [
|
|
220
|
+
withTheme,
|
|
221
|
+
withLayout({ fullscreen: true }),
|
|
214
222
|
withClientProvider({
|
|
215
223
|
types: [TableType, ViewType],
|
|
216
224
|
createIdentity: true,
|
|
@@ -227,8 +235,6 @@ const meta: Meta<StoryProps> = {
|
|
|
227
235
|
});
|
|
228
236
|
},
|
|
229
237
|
}),
|
|
230
|
-
withLayout({ fullscreen: true }),
|
|
231
|
-
withTheme,
|
|
232
238
|
],
|
|
233
239
|
};
|
|
234
240
|
|
|
@@ -91,6 +91,8 @@ const TableMain = forwardRef<TableController, TableMainProps>(
|
|
|
91
91
|
const { t } = useTranslation(translationKey);
|
|
92
92
|
const modals = useMemo(() => new ModalController(), []);
|
|
93
93
|
|
|
94
|
+
const draftRowCount = model?.getDraftRowCount() ?? 0;
|
|
95
|
+
|
|
94
96
|
const frozen = useMemo(() => {
|
|
95
97
|
const noActionColumn =
|
|
96
98
|
model?.features.dataEditable === false &&
|
|
@@ -99,10 +101,11 @@ const TableMain = forwardRef<TableController, TableMainProps>(
|
|
|
99
101
|
|
|
100
102
|
return {
|
|
101
103
|
frozenRowsStart: 1,
|
|
104
|
+
frozenRowsEnd: draftRowCount,
|
|
102
105
|
frozenColsStart: model?.features.selection.enabled ? 1 : 0,
|
|
103
106
|
frozenColsEnd: noActionColumn ? 0 : 1,
|
|
104
107
|
};
|
|
105
|
-
}, [model]);
|
|
108
|
+
}, [model, draftRowCount]);
|
|
106
109
|
|
|
107
110
|
const getCells = useCallback<NonNullable<GridContentProps['getCells']>>(
|
|
108
111
|
(range: DxGridPlaneRange, plane: DxGridPlane) => presentation?.getCells(range, plane) ?? {},
|
|
@@ -188,6 +191,17 @@ const TableMain = forwardRef<TableController, TableMainProps>(
|
|
|
188
191
|
model?.sorting?.toggleSort(data.fieldId);
|
|
189
192
|
break;
|
|
190
193
|
}
|
|
194
|
+
case 'saveDraftRow': {
|
|
195
|
+
if (model) {
|
|
196
|
+
const didCommitSuccessfully = model.commitDraftRow(data.rowIndex);
|
|
197
|
+
if (dxGrid && didCommitSuccessfully) {
|
|
198
|
+
requestAnimationFrame(() => {
|
|
199
|
+
dxGrid.scrollToEndRow();
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
191
205
|
}
|
|
192
206
|
return;
|
|
193
207
|
}
|
|
@@ -222,7 +236,7 @@ const TableMain = forwardRef<TableController, TableMainProps>(
|
|
|
222
236
|
}
|
|
223
237
|
}
|
|
224
238
|
},
|
|
225
|
-
[model, modals],
|
|
239
|
+
[model, modals, dxGrid],
|
|
226
240
|
);
|
|
227
241
|
|
|
228
242
|
const handleFocus = useCallback<NonNullable<TableCellEditorProps['onFocus']>>(
|
|
@@ -242,7 +256,7 @@ const TableMain = forwardRef<TableController, TableMainProps>(
|
|
|
242
256
|
|
|
243
257
|
// TODO(burdon): Insert row only if bottom row isn't completely blank already.
|
|
244
258
|
if (model && cell.row === model.getRowCount() - 1) {
|
|
245
|
-
model.insertRow(
|
|
259
|
+
model.insertRow();
|
|
246
260
|
if (dxGrid) {
|
|
247
261
|
requestAnimationFrame(() => {
|
|
248
262
|
dxGrid?.scrollToRow(cell.row + 1);
|
|
@@ -366,7 +380,6 @@ const TableMain = forwardRef<TableController, TableMainProps>(
|
|
|
366
380
|
<Grid.Content
|
|
367
381
|
className={mx('[--dx-grid-base:var(--baseSurface)]', gridSeparatorInlineEnd, gridSeparatorBlockEnd)}
|
|
368
382
|
frozen={frozen}
|
|
369
|
-
// getCells={getCells}
|
|
370
383
|
columns={model.columnMeta.value}
|
|
371
384
|
limitRows={model.getRowCount() ?? 0}
|
|
372
385
|
limitColumns={model.view?.fields?.length ?? 0}
|
|
@@ -4,17 +4,16 @@
|
|
|
4
4
|
|
|
5
5
|
import '@dxos-theme';
|
|
6
6
|
|
|
7
|
-
import { type StoryObj, type Meta } from '@storybook/react';
|
|
7
|
+
import { type StoryObj, type Meta } from '@storybook/react-vite';
|
|
8
8
|
import { SchemaAST } from 'effect';
|
|
9
9
|
import React, { useEffect, useMemo } from 'react';
|
|
10
10
|
|
|
11
|
-
import { Obj, type Type } from '@dxos/echo';
|
|
11
|
+
import { Obj, Ref, type Type } from '@dxos/echo';
|
|
12
12
|
import { getSchemaTypename, toJsonSchema } from '@dxos/echo-schema';
|
|
13
13
|
import { getAnnotation } from '@dxos/effect';
|
|
14
14
|
import { invariant } from '@dxos/invariant';
|
|
15
15
|
import { faker } from '@dxos/random';
|
|
16
16
|
import { useClient } from '@dxos/react-client';
|
|
17
|
-
import { makeRef } from '@dxos/react-client/echo';
|
|
18
17
|
import { useClientProvider, withClientProvider } from '@dxos/react-client/testing';
|
|
19
18
|
import { DataType, createView, ViewProjection, ViewType } from '@dxos/schema';
|
|
20
19
|
import { createAsyncGenerator, type ValueGenerator } from '@dxos/schema/testing';
|
|
@@ -23,7 +22,7 @@ import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
|
23
22
|
import { Table } from './Table';
|
|
24
23
|
import { useTableModel } from '../../hooks';
|
|
25
24
|
import { type TableFeatures, TablePresentation, type TableRow } from '../../model';
|
|
26
|
-
import translations from '../../translations';
|
|
25
|
+
import { translations } from '../../translations';
|
|
27
26
|
import { TableType } from '../../types';
|
|
28
27
|
|
|
29
28
|
faker.seed(1);
|
|
@@ -45,7 +44,7 @@ const useTestModel = <S extends Type.Obj.Any>(schema: S, count: number) => {
|
|
|
45
44
|
invariant(typename);
|
|
46
45
|
const name = getAnnotation<string>(SchemaAST.TitleAnnotationId)(schema.ast) ?? typename;
|
|
47
46
|
const view = createView({ name, typename, jsonSchema });
|
|
48
|
-
return space.db.add(Obj.make(TableType, { view:
|
|
47
|
+
return space.db.add(Obj.make(TableType, { view: Ref.make(view) }));
|
|
49
48
|
}, [schema, space, jsonSchema]);
|
|
50
49
|
|
|
51
50
|
const projection = useMemo(() => {
|
|
@@ -8,10 +8,12 @@ import { useGridContext, type GridScopedProps } from '@dxos/react-ui-grid';
|
|
|
8
8
|
|
|
9
9
|
export type CellValidationMessageProps = {
|
|
10
10
|
validationError: string | null;
|
|
11
|
+
variant?: 'error' | 'warning';
|
|
11
12
|
};
|
|
12
13
|
|
|
13
14
|
export const CellValidationMessage = ({
|
|
14
15
|
validationError,
|
|
16
|
+
variant = 'error',
|
|
15
17
|
__gridScope,
|
|
16
18
|
}: GridScopedProps<CellValidationMessageProps>) => {
|
|
17
19
|
const { editing, editBox: box } = useGridContext('GridSheetCellEditor', __gridScope);
|
|
@@ -20,10 +22,13 @@ export const CellValidationMessage = ({
|
|
|
20
22
|
return null;
|
|
21
23
|
}
|
|
22
24
|
|
|
25
|
+
const bgClass = variant === 'error' ? 'bg-errorSurface' : 'bg-warningSurface';
|
|
26
|
+
const textClass = variant === 'error' ? 'text-errorSurfaceText' : 'text-warningSurfaceText';
|
|
27
|
+
|
|
23
28
|
return (
|
|
24
29
|
<div
|
|
25
30
|
role='none'
|
|
26
|
-
className=
|
|
31
|
+
className={`absolute ${bgClass} ${textClass} rounded-bs-sm text-xs p-1`}
|
|
27
32
|
style={{
|
|
28
33
|
...{ '--dx-gridCellWidth': `${box?.inlineSize ?? 200}px` },
|
|
29
34
|
zIndex: 10,
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import '@dxos-theme';
|
|
6
6
|
|
|
7
|
-
import { type Meta, type StoryObj } from '@storybook/react';
|
|
7
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
8
8
|
import React, { useEffect, useMemo, useState } from 'react';
|
|
9
9
|
|
|
10
10
|
import { Obj } from '@dxos/echo';
|
|
@@ -20,7 +20,7 @@ import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
|
20
20
|
import { TableCellEditor, type TableCellEditorProps } from './TableCellEditor';
|
|
21
21
|
import { useTableModel } from '../../hooks';
|
|
22
22
|
import { type TableFeatures } from '../../model';
|
|
23
|
-
import translations from '../../translations';
|
|
23
|
+
import { translations } from '../../translations';
|
|
24
24
|
import { TableType } from '../../types';
|
|
25
25
|
import { initializeTable } from '../../util';
|
|
26
26
|
|
|
@@ -87,7 +87,7 @@ const DefaultStory = ({ editing }: StoryProps) => {
|
|
|
87
87
|
};
|
|
88
88
|
|
|
89
89
|
const meta: Meta<StoryProps> = {
|
|
90
|
-
title: '
|
|
90
|
+
title: 'ui/react-ui-table/TableCellEditor',
|
|
91
91
|
component: DefaultStory,
|
|
92
92
|
render: DefaultStory,
|
|
93
93
|
parameters: { translations, layout: 'centered' },
|
|
@@ -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';
|
|
@@ -92,6 +92,8 @@ export const TableValueEditor = ({
|
|
|
92
92
|
);
|
|
93
93
|
};
|
|
94
94
|
|
|
95
|
+
const editorSlots = { scroller: { className: '!plb-[--dx-grid-cell-editor-padding-block]' } };
|
|
96
|
+
|
|
95
97
|
export const TableCellEditor = ({
|
|
96
98
|
model,
|
|
97
99
|
modals,
|
|
@@ -104,6 +106,7 @@ export const TableCellEditor = ({
|
|
|
104
106
|
const suppressNextBlur = useRef(false);
|
|
105
107
|
const { themeMode } = useThemeContext();
|
|
106
108
|
const [_validationError, setValidationError] = useState<string | null>(null);
|
|
109
|
+
const [_validationVariant, setValidationVariant] = useState<'error' | 'warning'>('error');
|
|
107
110
|
|
|
108
111
|
const fieldProjection = useMemo<FieldProjection | undefined>(() => {
|
|
109
112
|
if (!model || !editing) {
|
|
@@ -117,6 +120,38 @@ export const TableCellEditor = ({
|
|
|
117
120
|
return fieldProjection;
|
|
118
121
|
}, [model, editing]);
|
|
119
122
|
|
|
123
|
+
useEffect(() => {
|
|
124
|
+
// Check for existing validation errors when editing starts (for draft rows).
|
|
125
|
+
if (!model || !editing || !fieldProjection) {
|
|
126
|
+
setValidationError(null);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const cell = parseCellIndex(editing.index);
|
|
131
|
+
const { row, col } = cell;
|
|
132
|
+
|
|
133
|
+
if (model.isDraftCell(cell)) {
|
|
134
|
+
const field = model.projection.view.fields[col];
|
|
135
|
+
const hasValidationError = model.hasDraftRowValidationError(row, field.path);
|
|
136
|
+
|
|
137
|
+
if (hasValidationError) {
|
|
138
|
+
const draftRows = model.draftRows.value;
|
|
139
|
+
if (row >= 0 && row < draftRows.length) {
|
|
140
|
+
const draftRow = draftRows[row];
|
|
141
|
+
const validationError = draftRow.validationErrors?.find((error) => error.path === field.path);
|
|
142
|
+
if (validationError) {
|
|
143
|
+
setValidationError(validationError.message);
|
|
144
|
+
setValidationVariant('warning');
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
} else {
|
|
148
|
+
setValidationError(null);
|
|
149
|
+
}
|
|
150
|
+
} else {
|
|
151
|
+
setValidationError(null);
|
|
152
|
+
}
|
|
153
|
+
}, [model, editing, fieldProjection]);
|
|
154
|
+
|
|
120
155
|
const handleEnter = useCallback(
|
|
121
156
|
async (value: any) => {
|
|
122
157
|
if (!model || !editing) {
|
|
@@ -124,19 +159,17 @@ export const TableCellEditor = ({
|
|
|
124
159
|
}
|
|
125
160
|
|
|
126
161
|
const cell = parseCellIndex(editing.index);
|
|
162
|
+
const validationResult = await model.validateCellData(cell, value);
|
|
127
163
|
|
|
128
|
-
|
|
129
|
-
const result = await model.validateCellData(cell, value);
|
|
130
|
-
|
|
131
|
-
if (result.valid) {
|
|
164
|
+
if (validationResult.valid) {
|
|
132
165
|
setValidationError(null);
|
|
133
166
|
model.setCellData(cell, value);
|
|
134
167
|
onEnter?.(cell);
|
|
135
168
|
onFocus?.();
|
|
136
169
|
setEditing(null);
|
|
137
170
|
} else {
|
|
138
|
-
setValidationError(
|
|
139
|
-
|
|
171
|
+
setValidationError(validationResult.error);
|
|
172
|
+
setValidationVariant('error');
|
|
140
173
|
}
|
|
141
174
|
},
|
|
142
175
|
[model, editing, onEnter, onFocus, setEditing],
|
|
@@ -178,6 +211,7 @@ export const TableCellEditor = ({
|
|
|
178
211
|
}
|
|
179
212
|
} else {
|
|
180
213
|
setValidationError(result.error);
|
|
214
|
+
setValidationVariant('error');
|
|
181
215
|
}
|
|
182
216
|
} else {
|
|
183
217
|
setValidationError(null);
|
|
@@ -214,9 +248,9 @@ export const TableCellEditor = ({
|
|
|
214
248
|
const mode = format === FormatEnum.SingleSelect ? ('single-select' as const) : ('multi-select' as const);
|
|
215
249
|
|
|
216
250
|
extension.push(
|
|
217
|
-
|
|
251
|
+
tagPicker({
|
|
218
252
|
mode,
|
|
219
|
-
|
|
253
|
+
keymap: false,
|
|
220
254
|
onSearch: (text, selectedIds) => {
|
|
221
255
|
return options
|
|
222
256
|
.filter(
|
|
@@ -300,9 +334,10 @@ export const TableCellEditor = ({
|
|
|
300
334
|
if (value !== undefined) {
|
|
301
335
|
const options = fieldProjection.props.options || [];
|
|
302
336
|
|
|
303
|
-
if (fieldProjection.props.format === FormatEnum.MultiSelect
|
|
337
|
+
if (fieldProjection.props.format === FormatEnum.MultiSelect) {
|
|
304
338
|
const tagItems = value
|
|
305
|
-
.
|
|
339
|
+
.split(',')
|
|
340
|
+
.map((id: string) => {
|
|
306
341
|
const option = options.find((o) => o.id === id);
|
|
307
342
|
if (option) {
|
|
308
343
|
return {
|
|
@@ -313,7 +348,7 @@ export const TableCellEditor = ({
|
|
|
313
348
|
}
|
|
314
349
|
return undefined;
|
|
315
350
|
})
|
|
316
|
-
.filter((item): item is { id: any; label: string; hue: any } => item !== undefined);
|
|
351
|
+
.filter((item: any): item is { id: any; label: string; hue: any } => item !== undefined);
|
|
317
352
|
|
|
318
353
|
return createLinks(tagItems);
|
|
319
354
|
} else {
|
|
@@ -341,8 +376,12 @@ export const TableCellEditor = ({
|
|
|
341
376
|
|
|
342
377
|
return (
|
|
343
378
|
<>
|
|
344
|
-
<CellValidationMessage
|
|
345
|
-
|
|
379
|
+
<CellValidationMessage
|
|
380
|
+
validationError={_validationError}
|
|
381
|
+
variant={_validationVariant}
|
|
382
|
+
__gridScope={__gridScope}
|
|
383
|
+
/>
|
|
384
|
+
<GridCellEditor extension={extension} getCellContent={getCellContent} onBlur={handleBlur} slots={editorSlots} />
|
|
346
385
|
</>
|
|
347
386
|
);
|
|
348
387
|
};
|
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
|
+
};
|