@dxos/react-ui-table 0.8.4-main.2e9d522 → 0.8.4-main.5ea62a8
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-63UUF4IG.mjs +66 -0
- package/dist/lib/browser/chunk-63UUF4IG.mjs.map +7 -0
- package/dist/lib/browser/{chunk-5XLSHLOG.mjs → chunk-RQYXNWWN.mjs} +531 -460
- package/dist/lib/browser/chunk-RQYXNWWN.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +691 -625
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +6 -4
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/browser/types/index.mjs +3 -3
- package/dist/lib/node-esm/chunk-K734DJB7.mjs +67 -0
- package/dist/lib/node-esm/chunk-K734DJB7.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-Y66YLSTC.mjs → chunk-QNDUGIS7.mjs} +531 -460
- package/dist/lib/node-esm/chunk-QNDUGIS7.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +691 -625
- 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 +6 -4
- package/dist/lib/node-esm/testing/index.mjs.map +3 -3
- package/dist/lib/node-esm/types/index.mjs +3 -3
- package/dist/types/src/components/Table/ColumnActionsMenu.d.ts +1 -1
- package/dist/types/src/components/Table/ColumnActionsMenu.d.ts.map +1 -1
- package/dist/types/src/components/Table/ColumnSettings.d.ts +1 -1
- package/dist/types/src/components/Table/ColumnSettings.d.ts.map +1 -1
- package/dist/types/src/components/Table/CreateRefPanel.d.ts +1 -1
- package/dist/types/src/components/Table/CreateRefPanel.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts +50 -2
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/Relations.stories.d.ts +44 -0
- package/dist/types/src/components/Table/Relations.stories.d.ts.map +1 -0
- package/dist/types/src/components/Table/RowActionsMenu.d.ts +1 -1
- package/dist/types/src/components/Table/RowActionsMenu.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.d.ts +3 -2
- package/dist/types/src/components/Table/Table.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.stories.d.ts +42 -3
- package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +7 -3
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +8 -15
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts +39 -3
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +1 -1
- package/dist/types/src/hooks/useAddRow.d.ts +2 -1
- package/dist/types/src/hooks/useAddRow.d.ts.map +1 -1
- package/dist/types/src/hooks/useTableModel.d.ts +1 -1
- package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
- 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 +7 -5
- package/dist/types/src/model/table-model.d.ts.map +1 -1
- package/dist/types/src/model/table-presentation.d.ts +2 -1
- 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/testing/useTestTableModel.d.ts +4 -9
- package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +2 -2
- 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 +24 -4
- package/dist/types/src/types/schema.d.ts.map +1 -1
- package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
- package/dist/types/src/util/index.d.ts +0 -1
- package/dist/types/src/util/index.d.ts.map +1 -1
- 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/tsconfig.tsbuildinfo +1 -1
- package/package.json +44 -39
- package/src/components/Table/ColumnActionsMenu.tsx +1 -1
- package/src/components/Table/ColumnSettings.tsx +1 -1
- package/src/components/Table/CreateRefPanel.tsx +25 -8
- package/src/components/Table/DynamicTable.stories.tsx +7 -4
- package/src/components/Table/DynamicTable.tsx +4 -3
- package/src/components/Table/{relations.stories.tsx → Relations.stories.tsx} +40 -23
- package/src/components/Table/RowActionsMenu.tsx +3 -3
- package/src/components/Table/Table.stories.tsx +34 -23
- package/src/components/Table/Table.tsx +138 -112
- package/src/components/TableCellEditor/CellValidationMessage.tsx +1 -1
- package/src/components/TableCellEditor/FormCellEditor.tsx +143 -42
- package/src/components/TableCellEditor/TableCellEditor.stories.tsx +11 -23
- package/src/components/TableCellEditor/TableCellEditor.tsx +64 -119
- package/src/components/TableToolbar/TableToolbar.tsx +2 -2
- package/src/components/testing.ts +1 -1
- package/src/hooks/useAddRow.ts +6 -4
- package/src/hooks/useTableModel.ts +1 -1
- package/src/model/modal-controller.ts +1 -1
- package/src/model/selection-model.ts +3 -2
- package/src/model/table-model.test.ts +3 -4
- package/src/model/table-model.ts +28 -23
- package/src/model/table-presentation.test.ts +4 -4
- package/src/model/table-presentation.ts +72 -30
- package/src/model/table-sorting.ts +3 -3
- package/src/playwright/smoke.spec.ts +129 -37
- package/src/testing/useTestTableModel.ts +4 -3
- package/src/translations.ts +2 -2
- package/src/types/index.ts +1 -1
- package/src/types/schema.ts +62 -5
- package/src/util/dynamic-table.ts +5 -5
- package/src/util/index.ts +0 -1
- package/src/util/schema.ts +1 -1
- package/src/util/table-buttons.ts +4 -3
- package/src/util/table-controls.ts +4 -1
- package/dist/lib/browser/chunk-5XLSHLOG.mjs.map +0 -7
- package/dist/lib/browser/chunk-L2LSB6KV.mjs +0 -18
- package/dist/lib/browser/chunk-L2LSB6KV.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-NXZ4CJYX.mjs +0 -20
- package/dist/lib/node-esm/chunk-NXZ4CJYX.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-Y66YLSTC.mjs.map +0 -7
- package/dist/types/src/components/Table/relations.stories.d.ts +0 -9
- package/dist/types/src/components/Table/relations.stories.d.ts.map +0 -1
- package/dist/types/src/components/TableCellEditor/extension.d.ts +0 -13
- package/dist/types/src/components/TableCellEditor/extension.d.ts.map +0 -1
- package/dist/types/src/util/table.d.ts +0 -11
- package/dist/types/src/util/table.d.ts.map +0 -1
- package/src/components/TableCellEditor/extension.ts +0 -61
- package/src/util/table.ts +0 -49
|
@@ -13,16 +13,16 @@ import { invariant } from '@dxos/invariant';
|
|
|
13
13
|
import { faker } from '@dxos/random';
|
|
14
14
|
import { Filter, useQuery } from '@dxos/react-client/echo';
|
|
15
15
|
import { useClientProvider, withClientProvider } from '@dxos/react-client/testing';
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
16
|
+
import { Grid, type GridEditing, defaultRowSize } from '@dxos/react-ui-grid';
|
|
17
|
+
import { DataType, createDefaultSchema } from '@dxos/schema';
|
|
18
18
|
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
19
19
|
|
|
20
|
-
import { TableCellEditor, type TableCellEditorProps } from './TableCellEditor';
|
|
21
20
|
import { useTableModel } from '../../hooks';
|
|
22
21
|
import { type TableFeatures } from '../../model';
|
|
23
22
|
import { translations } from '../../translations';
|
|
24
|
-
import {
|
|
25
|
-
|
|
23
|
+
import { Table } from '../../types';
|
|
24
|
+
|
|
25
|
+
import { TableCellEditor } from './TableCellEditor';
|
|
26
26
|
|
|
27
27
|
type StoryProps = {
|
|
28
28
|
editing: GridEditing;
|
|
@@ -54,18 +54,6 @@ const DefaultStory = ({ editing }: StoryProps) => {
|
|
|
54
54
|
|
|
55
55
|
const model = useTableModel({ view, schema: schema?.jsonSchema, features });
|
|
56
56
|
|
|
57
|
-
const handleQuery: TableCellEditorProps['onQuery'] = async ({ field }) => {
|
|
58
|
-
// TODO(dmaretskyi): If no schema query nothing
|
|
59
|
-
const { objects } = await space.db.query(schema ? Filter.type(schema) : Filter.everything()).run();
|
|
60
|
-
return objects.map((obj) => {
|
|
61
|
-
const label = obj[field.referencePath ?? 'id'];
|
|
62
|
-
return {
|
|
63
|
-
label,
|
|
64
|
-
data: obj,
|
|
65
|
-
};
|
|
66
|
-
});
|
|
67
|
-
};
|
|
68
|
-
|
|
69
57
|
if (!model || !schema || !view) {
|
|
70
58
|
return <div />;
|
|
71
59
|
}
|
|
@@ -73,25 +61,25 @@ const DefaultStory = ({ editing }: StoryProps) => {
|
|
|
73
61
|
return (
|
|
74
62
|
<div className='flex w-[300px] border border-separator' style={{ height: defaultRowSize }}>
|
|
75
63
|
<Grid.Root id='test' editing={editing}>
|
|
76
|
-
<TableCellEditor model={model}
|
|
64
|
+
<TableCellEditor model={model} schema={schema} />
|
|
77
65
|
</Grid.Root>
|
|
78
66
|
</div>
|
|
79
67
|
);
|
|
80
68
|
};
|
|
81
69
|
|
|
82
|
-
const meta
|
|
70
|
+
const meta = {
|
|
83
71
|
title: 'ui/react-ui-table/TableCellEditor',
|
|
84
72
|
component: DefaultStory,
|
|
85
73
|
render: DefaultStory,
|
|
86
74
|
parameters: { translations, layout: 'centered' },
|
|
87
75
|
decorators: [
|
|
88
76
|
withClientProvider({
|
|
89
|
-
types: [DataType.View,
|
|
77
|
+
types: [DataType.View, Table.Table],
|
|
90
78
|
createIdentity: true,
|
|
91
79
|
createSpace: true,
|
|
92
80
|
onSpaceCreated: async ({ client, space }) => {
|
|
93
81
|
const schema = createDefaultSchema();
|
|
94
|
-
const { view } = await
|
|
82
|
+
const { view } = await Table.makeView({ client, space, typename: schema.typename });
|
|
95
83
|
space.db.add(view);
|
|
96
84
|
Array.from({ length: 10 }).forEach(() => {
|
|
97
85
|
space.db.add(
|
|
@@ -107,11 +95,11 @@ const meta: Meta<StoryProps> = {
|
|
|
107
95
|
withTheme,
|
|
108
96
|
withLayout(),
|
|
109
97
|
],
|
|
110
|
-
}
|
|
98
|
+
} satisfies Meta<typeof DefaultStory>;
|
|
111
99
|
|
|
112
100
|
export default meta;
|
|
113
101
|
|
|
114
|
-
type Story = StoryObj<
|
|
102
|
+
type Story = StoryObj<typeof meta>;
|
|
115
103
|
|
|
116
104
|
export const Default: Story = {
|
|
117
105
|
args: {
|
|
@@ -3,64 +3,60 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { type Completion } from '@codemirror/autocomplete';
|
|
6
|
+
import { EditorView } from '@codemirror/view';
|
|
6
7
|
import { type Schema } from 'effect/Schema';
|
|
7
|
-
import React, { useCallback,
|
|
8
|
+
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
|
8
9
|
|
|
10
|
+
import { debounce } from '@dxos/async';
|
|
11
|
+
import type { Client } from '@dxos/client';
|
|
9
12
|
import { FormatEnum, TypeEnum } from '@dxos/echo-schema';
|
|
10
13
|
import { invariant } from '@dxos/invariant';
|
|
11
|
-
import { type
|
|
14
|
+
import { type DxGridAxis, type DxGridPosition } from '@dxos/lit-grid';
|
|
12
15
|
import { useThemeContext } from '@dxos/react-ui';
|
|
13
16
|
import { createMarkdownExtensions } from '@dxos/react-ui-editor';
|
|
14
17
|
import {
|
|
15
|
-
|
|
16
|
-
editorKeys,
|
|
17
|
-
parseCellIndex,
|
|
18
|
-
useGridContext,
|
|
19
|
-
type DxGridPlanePosition,
|
|
18
|
+
type EditorBlurHandler,
|
|
20
19
|
type EditorKeyEvent,
|
|
21
20
|
type EditorKeyOrBlurHandler,
|
|
22
|
-
type EditorBlurHandler,
|
|
23
21
|
GridCellEditor,
|
|
24
22
|
type GridCellEditorProps,
|
|
25
23
|
type GridScopedProps,
|
|
24
|
+
editorKeys,
|
|
25
|
+
parseCellIndex,
|
|
26
|
+
useGridContext,
|
|
26
27
|
} from '@dxos/react-ui-grid';
|
|
27
|
-
import {
|
|
28
|
+
import { createLinks, tagPicker } from '@dxos/react-ui-tag-picker';
|
|
28
29
|
import { type FieldProjection } from '@dxos/schema';
|
|
29
30
|
|
|
31
|
+
import { type ModalController, type TableModel } from '../../model';
|
|
32
|
+
|
|
30
33
|
import { CellValidationMessage } from './CellValidationMessage';
|
|
31
34
|
import { FormCellEditor } from './FormCellEditor';
|
|
32
|
-
import { completion } from './extension';
|
|
33
|
-
import { type TableModel, type ModalController } from '../../model';
|
|
34
|
-
|
|
35
|
-
const newValue = Symbol.for('newValue');
|
|
36
35
|
|
|
37
36
|
/**
|
|
38
37
|
* Option to create new object/value.
|
|
39
38
|
*/
|
|
40
|
-
export const createOption = (text: string) => ({ [newValue]: true, text });
|
|
41
|
-
|
|
42
|
-
const isCreateOption = (data: any) => typeof data === 'object' && data[newValue];
|
|
43
|
-
|
|
44
39
|
export type QueryResult = Pick<Completion, 'label'> & { data: any };
|
|
45
40
|
|
|
46
41
|
export type TableCellEditorProps = {
|
|
47
42
|
model?: TableModel;
|
|
48
43
|
modals?: ModalController;
|
|
49
44
|
schema?: Schema.AnyNoContext;
|
|
50
|
-
|
|
51
|
-
onFocus?: DxGrid['refocus'];
|
|
45
|
+
onFocus?: (axis?: DxGridAxis, delta?: -1 | 0 | 1, cell?: DxGridPosition) => void;
|
|
52
46
|
onSave?: () => void;
|
|
53
|
-
|
|
47
|
+
client?: Client;
|
|
54
48
|
};
|
|
55
49
|
|
|
50
|
+
const adaptValidationMessage = (message: string | null) =>
|
|
51
|
+
message ? (message.endsWith('is missing') ? 'Can’t be blank' : message) : null;
|
|
52
|
+
|
|
56
53
|
export const TableValueEditor = ({
|
|
57
54
|
model,
|
|
58
55
|
modals,
|
|
59
56
|
schema,
|
|
60
|
-
onEnter,
|
|
61
57
|
onFocus,
|
|
62
58
|
onSave,
|
|
63
|
-
|
|
59
|
+
client,
|
|
64
60
|
__gridScope,
|
|
65
61
|
}: GridScopedProps<TableCellEditorProps>) => {
|
|
66
62
|
const { editing } = useGridContext('TableValueEditor', __gridScope);
|
|
@@ -79,7 +75,8 @@ export const TableValueEditor = ({
|
|
|
79
75
|
|
|
80
76
|
if (
|
|
81
77
|
fieldProjection?.props.type === TypeEnum.Array ||
|
|
82
|
-
fieldProjection?.props.format === FormatEnum.SingleSelect
|
|
78
|
+
fieldProjection?.props.format === FormatEnum.SingleSelect ||
|
|
79
|
+
fieldProjection?.props.format === FormatEnum.Ref
|
|
83
80
|
// TODO(thure): Support `FormatEnum.MultiSelect`
|
|
84
81
|
) {
|
|
85
82
|
return (
|
|
@@ -89,22 +86,14 @@ export const TableValueEditor = ({
|
|
|
89
86
|
schema={schema}
|
|
90
87
|
__gridScope={__gridScope}
|
|
91
88
|
onSave={onSave}
|
|
89
|
+
client={client}
|
|
90
|
+
modals={modals}
|
|
92
91
|
/>
|
|
93
92
|
);
|
|
94
93
|
}
|
|
95
94
|
|
|
96
95
|
// For all other types, use the existing cell editor
|
|
97
|
-
return
|
|
98
|
-
<TableCellEditor
|
|
99
|
-
model={model}
|
|
100
|
-
modals={modals}
|
|
101
|
-
onEnter={onEnter}
|
|
102
|
-
onFocus={onFocus}
|
|
103
|
-
onQuery={onQuery}
|
|
104
|
-
onSave={onSave}
|
|
105
|
-
__gridScope={__gridScope}
|
|
106
|
-
/>
|
|
107
|
-
);
|
|
96
|
+
return <TableCellEditor model={model} modals={modals} onFocus={onFocus} onSave={onSave} __gridScope={__gridScope} />;
|
|
108
97
|
};
|
|
109
98
|
|
|
110
99
|
const editorSlots = { scroller: { className: '!plb-[--dx-grid-cell-editor-padding-block]' } };
|
|
@@ -112,17 +101,15 @@ const editorSlots = { scroller: { className: '!plb-[--dx-grid-cell-editor-paddin
|
|
|
112
101
|
export const TableCellEditor = ({
|
|
113
102
|
model,
|
|
114
103
|
modals,
|
|
115
|
-
onEnter,
|
|
116
104
|
onFocus,
|
|
117
|
-
onQuery,
|
|
118
105
|
onSave,
|
|
119
106
|
__gridScope,
|
|
120
107
|
}: GridScopedProps<TableCellEditorProps>) => {
|
|
121
108
|
const { id: gridId, editing, setEditing } = useGridContext('TableCellEditor', __gridScope);
|
|
122
109
|
const suppressNextBlur = useRef(false);
|
|
123
110
|
const { themeMode } = useThemeContext();
|
|
124
|
-
const [
|
|
125
|
-
const [
|
|
111
|
+
const [validationError, setValidationError] = useState<string | null>(null);
|
|
112
|
+
const [validationVariant, setValidationVariant] = useState<'error' | 'warning'>('error');
|
|
126
113
|
|
|
127
114
|
const fieldProjection = useMemo<FieldProjection | undefined>(() => {
|
|
128
115
|
if (!model || !editing) {
|
|
@@ -136,38 +123,6 @@ export const TableCellEditor = ({
|
|
|
136
123
|
return fieldProjection;
|
|
137
124
|
}, [model, editing]);
|
|
138
125
|
|
|
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
|
-
|
|
171
126
|
const handleEnter = useCallback(
|
|
172
127
|
async (value: any) => {
|
|
173
128
|
if (!model || !editing) {
|
|
@@ -180,28 +135,34 @@ export const TableCellEditor = ({
|
|
|
180
135
|
if (validationResult.valid) {
|
|
181
136
|
setValidationError(null);
|
|
182
137
|
model.setCellData(cell, value);
|
|
183
|
-
onEnter?.(cell);
|
|
184
138
|
onFocus?.();
|
|
185
139
|
setEditing(null);
|
|
186
140
|
onSave?.();
|
|
187
141
|
} else {
|
|
188
|
-
setValidationError(validationResult.error);
|
|
142
|
+
setValidationError(adaptValidationMessage(validationResult.error));
|
|
189
143
|
setValidationVariant('error');
|
|
190
144
|
}
|
|
191
145
|
},
|
|
192
|
-
[model, editing,
|
|
146
|
+
[model, editing, onFocus, setEditing],
|
|
193
147
|
);
|
|
194
148
|
|
|
195
149
|
const handleBlur = useCallback<EditorBlurHandler>(
|
|
196
150
|
async (value) => {
|
|
197
|
-
if (!model || !editing) {
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
151
|
if (suppressNextBlur.current) {
|
|
201
152
|
suppressNextBlur.current = false;
|
|
153
|
+
} else if (model && editing) {
|
|
154
|
+
// Save silently if validation passes
|
|
155
|
+
const cell = parseCellIndex(editing.index);
|
|
156
|
+
if (value !== undefined) {
|
|
157
|
+
const result = await model.validateCellData(cell, value);
|
|
158
|
+
if (result.valid) {
|
|
159
|
+
setValidationError(null);
|
|
160
|
+
model.setCellData(cell, value);
|
|
161
|
+
setEditing(null);
|
|
162
|
+
onSave?.();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
202
165
|
}
|
|
203
|
-
|
|
204
|
-
// Don't save on blur - let handleClose handle validation and saving
|
|
205
166
|
},
|
|
206
167
|
[model, editing],
|
|
207
168
|
);
|
|
@@ -219,19 +180,20 @@ export const TableCellEditor = ({
|
|
|
219
180
|
const result = await model.validateCellData(cell, value);
|
|
220
181
|
|
|
221
182
|
if (result.valid) {
|
|
183
|
+
suppressNextBlur.current = true;
|
|
222
184
|
setValidationError(null);
|
|
223
185
|
model.setCellData(cell, value);
|
|
224
186
|
setEditing(null);
|
|
225
187
|
onSave?.();
|
|
226
|
-
onEnter?.(cell);
|
|
227
188
|
if (event && onFocus) {
|
|
228
|
-
onFocus(determineNavigationAxis(event), determineNavigationDelta(event));
|
|
189
|
+
onFocus(determineNavigationAxis(event), determineNavigationDelta(event), cell);
|
|
229
190
|
}
|
|
230
191
|
} else {
|
|
231
192
|
setValidationError(result.error);
|
|
232
193
|
setValidationVariant('error');
|
|
233
194
|
}
|
|
234
195
|
} else {
|
|
196
|
+
suppressNextBlur.current = true;
|
|
235
197
|
setValidationError(null);
|
|
236
198
|
setEditing(null);
|
|
237
199
|
onSave?.();
|
|
@@ -240,7 +202,7 @@ export const TableCellEditor = ({
|
|
|
240
202
|
}
|
|
241
203
|
}
|
|
242
204
|
},
|
|
243
|
-
[model, editing, onFocus,
|
|
205
|
+
[model, editing, onFocus, fieldProjection, setEditing, onSave],
|
|
244
206
|
);
|
|
245
207
|
|
|
246
208
|
const extension = useMemo(() => {
|
|
@@ -299,39 +261,26 @@ export const TableCellEditor = ({
|
|
|
299
261
|
);
|
|
300
262
|
}
|
|
301
263
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
},
|
|
323
|
-
);
|
|
324
|
-
}
|
|
325
|
-
} else {
|
|
326
|
-
void handleEnter(data);
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
},
|
|
330
|
-
}),
|
|
331
|
-
]);
|
|
332
|
-
break;
|
|
333
|
-
}
|
|
334
|
-
}
|
|
264
|
+
// Add validation extension to handle content changes
|
|
265
|
+
if (model && editing) {
|
|
266
|
+
extension.push(
|
|
267
|
+
EditorView.updateListener.of(
|
|
268
|
+
debounce((update) => {
|
|
269
|
+
const content = update.state.doc.toString();
|
|
270
|
+
const cell = parseCellIndex(editing.index);
|
|
271
|
+
|
|
272
|
+
// Perform validation on content change
|
|
273
|
+
void model.validateCellData(cell, content).then((result) => {
|
|
274
|
+
if (result.valid) {
|
|
275
|
+
setValidationError(null);
|
|
276
|
+
} else {
|
|
277
|
+
setValidationError(result.error);
|
|
278
|
+
setValidationVariant('error');
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
}, 10),
|
|
282
|
+
),
|
|
283
|
+
);
|
|
335
284
|
}
|
|
336
285
|
|
|
337
286
|
return extension;
|
|
@@ -395,11 +344,7 @@ export const TableCellEditor = ({
|
|
|
395
344
|
|
|
396
345
|
return (
|
|
397
346
|
<>
|
|
398
|
-
<CellValidationMessage
|
|
399
|
-
validationError={_validationError}
|
|
400
|
-
variant={_validationVariant}
|
|
401
|
-
__gridScope={__gridScope}
|
|
402
|
-
/>
|
|
347
|
+
<CellValidationMessage validationError={validationError} variant={validationVariant} __gridScope={__gridScope} />
|
|
403
348
|
<GridCellEditor extension={extension} getCellContent={getCellContent} onBlur={handleBlur} slots={editorSlots} />
|
|
404
349
|
</>
|
|
405
350
|
);
|
|
@@ -11,11 +11,11 @@ import {
|
|
|
11
11
|
type ActionGraphEdges,
|
|
12
12
|
type ActionGraphNodes,
|
|
13
13
|
type ActionGraphProps,
|
|
14
|
-
createGapSeparator,
|
|
15
|
-
createMenuAction,
|
|
16
14
|
type MenuAction,
|
|
17
15
|
MenuProvider,
|
|
18
16
|
ToolbarMenu,
|
|
17
|
+
createGapSeparator,
|
|
18
|
+
createMenuAction,
|
|
19
19
|
useMenuActions,
|
|
20
20
|
} from '@dxos/react-ui-menu';
|
|
21
21
|
import { rxFromSignal } from '@dxos/react-ui-menu';
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { Schema } from 'effect';
|
|
6
6
|
import { useEffect } from 'react';
|
|
7
7
|
|
|
8
|
-
import { FormatEnum, ObjectId,
|
|
8
|
+
import { FormatEnum, ObjectId, TypeEnum, TypedObject, setValue } from '@dxos/echo-schema';
|
|
9
9
|
import { live } from '@dxos/live-object';
|
|
10
10
|
import { faker } from '@dxos/random';
|
|
11
11
|
import { type ProjectionModel } from '@dxos/schema';
|
package/src/hooks/useAddRow.ts
CHANGED
|
@@ -8,6 +8,8 @@ import { useCallback } from 'react';
|
|
|
8
8
|
import { type Space } from '@dxos/client/echo';
|
|
9
9
|
import { Obj } from '@dxos/echo';
|
|
10
10
|
|
|
11
|
+
import { type InsertRowResult } from '../model';
|
|
12
|
+
|
|
11
13
|
export type UseAddRowParams = {
|
|
12
14
|
space?: Space;
|
|
13
15
|
schema?: Schema.Schema.AnyNoContext;
|
|
@@ -20,16 +22,16 @@ export type UseAddRowParams = {
|
|
|
20
22
|
*/
|
|
21
23
|
export const useAddRow = ({ space, schema }: UseAddRowParams) => {
|
|
22
24
|
return useCallback(
|
|
23
|
-
(data?: any) => {
|
|
25
|
+
(data?: any): InsertRowResult => {
|
|
24
26
|
if (space && schema) {
|
|
25
27
|
try {
|
|
26
28
|
space.db.add(Obj.make(schema, data ?? {}));
|
|
27
|
-
return
|
|
29
|
+
return 'final';
|
|
28
30
|
} catch (error) {
|
|
29
|
-
return
|
|
31
|
+
return 'draft';
|
|
30
32
|
}
|
|
31
33
|
}
|
|
32
|
-
return
|
|
34
|
+
return 'final';
|
|
33
35
|
},
|
|
34
36
|
[space, schema],
|
|
35
37
|
);
|
|
@@ -13,7 +13,7 @@ import { useSelected, useSelectionActions } from '@dxos/react-ui-attention';
|
|
|
13
13
|
import { type DataType, type ProjectionModel } from '@dxos/schema';
|
|
14
14
|
import { isNonNullable } from '@dxos/util';
|
|
15
15
|
|
|
16
|
-
import {
|
|
16
|
+
import { TableModel, type TableModelProps, type TableRow, type TableRowAction } from '../model';
|
|
17
17
|
|
|
18
18
|
export type UseTableModelParams<T extends TableRow = TableRow> = {
|
|
19
19
|
view?: DataType.View;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { signal } from '@preact/signals-core';
|
|
6
|
-
import { type
|
|
6
|
+
import { type MutableRefObject, type RefObject } from 'react';
|
|
7
7
|
|
|
8
8
|
import { type Live } from '@dxos/live-object';
|
|
9
9
|
import { log } from '@dxos/log';
|
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { type ReadonlySignal, type Signal, computed, effect, signal } from '@preact/signals-core';
|
|
6
6
|
|
|
7
7
|
import { Resource } from '@dxos/context';
|
|
8
8
|
|
|
9
|
-
import { type TableRow } from './table-model';
|
|
10
9
|
import { touch } from '../util';
|
|
11
10
|
|
|
11
|
+
import { type TableRow } from './table-model';
|
|
12
|
+
|
|
12
13
|
export type SelectionMode = 'single' | 'multiple';
|
|
13
14
|
|
|
14
15
|
export class SelectionModel<T extends TableRow> extends Resource {
|
|
@@ -6,7 +6,6 @@ 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 } from '@dxos/echo';
|
|
10
9
|
import { TypedObject } from '@dxos/echo-schema';
|
|
11
10
|
import { updateCounter } from '@dxos/echo-schema/testing';
|
|
12
11
|
import { registerSignalsRuntime } from '@dxos/echo-signals';
|
|
@@ -14,8 +13,9 @@ import { live } from '@dxos/live-object';
|
|
|
14
13
|
import { createEchoSchema } from '@dxos/live-object/testing';
|
|
15
14
|
import { createView } from '@dxos/schema';
|
|
16
15
|
|
|
16
|
+
import { Table } from '../types';
|
|
17
|
+
|
|
17
18
|
import { TableModel, type TableModelProps } from './table-model';
|
|
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.
|
|
@@ -72,7 +72,6 @@ describe('TableModel', () => {
|
|
|
72
72
|
describe('reactivity', () => {
|
|
73
73
|
it('pure signals should nest', () => {
|
|
74
74
|
const signal$ = live({ arr: [{ thingInside: 1 }, { thingInside: 2 }] });
|
|
75
|
-
|
|
76
75
|
const computed$ = computed(() => {
|
|
77
76
|
return signal$.arr.map((row) =>
|
|
78
77
|
computed(() => {
|
|
@@ -107,7 +106,7 @@ class Test extends TypedObject({ typename: 'example.com/type/Test', version: '0.
|
|
|
107
106
|
|
|
108
107
|
const createTableModel = (props: Partial<TableModelProps> = {}): TableModel => {
|
|
109
108
|
const schema = createEchoSchema(Test);
|
|
110
|
-
const table =
|
|
109
|
+
const table = Table.make();
|
|
111
110
|
const view = createView({ typename: schema.typename, jsonSchema: schema.jsonSchema, presentation: table });
|
|
112
111
|
return new TableModel({ view, schema: schema.jsonSchema, ...props });
|
|
113
112
|
};
|