@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.
Files changed (119) hide show
  1. package/dist/lib/browser/chunk-63UUF4IG.mjs +66 -0
  2. package/dist/lib/browser/chunk-63UUF4IG.mjs.map +7 -0
  3. package/dist/lib/browser/{chunk-5XLSHLOG.mjs → chunk-RQYXNWWN.mjs} +531 -460
  4. package/dist/lib/browser/chunk-RQYXNWWN.mjs.map +7 -0
  5. package/dist/lib/browser/index.mjs +691 -625
  6. package/dist/lib/browser/index.mjs.map +4 -4
  7. package/dist/lib/browser/meta.json +1 -1
  8. package/dist/lib/browser/testing/index.mjs +6 -4
  9. package/dist/lib/browser/testing/index.mjs.map +3 -3
  10. package/dist/lib/browser/types/index.mjs +3 -3
  11. package/dist/lib/node-esm/chunk-K734DJB7.mjs +67 -0
  12. package/dist/lib/node-esm/chunk-K734DJB7.mjs.map +7 -0
  13. package/dist/lib/node-esm/{chunk-Y66YLSTC.mjs → chunk-QNDUGIS7.mjs} +531 -460
  14. package/dist/lib/node-esm/chunk-QNDUGIS7.mjs.map +7 -0
  15. package/dist/lib/node-esm/index.mjs +691 -625
  16. package/dist/lib/node-esm/index.mjs.map +4 -4
  17. package/dist/lib/node-esm/meta.json +1 -1
  18. package/dist/lib/node-esm/testing/index.mjs +6 -4
  19. package/dist/lib/node-esm/testing/index.mjs.map +3 -3
  20. package/dist/lib/node-esm/types/index.mjs +3 -3
  21. package/dist/types/src/components/Table/ColumnActionsMenu.d.ts +1 -1
  22. package/dist/types/src/components/Table/ColumnActionsMenu.d.ts.map +1 -1
  23. package/dist/types/src/components/Table/ColumnSettings.d.ts +1 -1
  24. package/dist/types/src/components/Table/ColumnSettings.d.ts.map +1 -1
  25. package/dist/types/src/components/Table/CreateRefPanel.d.ts +1 -1
  26. package/dist/types/src/components/Table/CreateRefPanel.d.ts.map +1 -1
  27. package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
  28. package/dist/types/src/components/Table/DynamicTable.stories.d.ts +50 -2
  29. package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
  30. package/dist/types/src/components/Table/Relations.stories.d.ts +44 -0
  31. package/dist/types/src/components/Table/Relations.stories.d.ts.map +1 -0
  32. package/dist/types/src/components/Table/RowActionsMenu.d.ts +1 -1
  33. package/dist/types/src/components/Table/RowActionsMenu.d.ts.map +1 -1
  34. package/dist/types/src/components/Table/Table.d.ts +3 -2
  35. package/dist/types/src/components/Table/Table.d.ts.map +1 -1
  36. package/dist/types/src/components/Table/Table.stories.d.ts +42 -3
  37. package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
  38. package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts.map +1 -1
  39. package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +7 -3
  40. package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
  41. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +8 -15
  42. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
  43. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts +39 -3
  44. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
  45. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +1 -1
  46. package/dist/types/src/hooks/useAddRow.d.ts +2 -1
  47. package/dist/types/src/hooks/useAddRow.d.ts.map +1 -1
  48. package/dist/types/src/hooks/useTableModel.d.ts +1 -1
  49. package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
  50. package/dist/types/src/model/modal-controller.d.ts.map +1 -1
  51. package/dist/types/src/model/selection-model.d.ts.map +1 -1
  52. package/dist/types/src/model/table-model.d.ts +7 -5
  53. package/dist/types/src/model/table-model.d.ts.map +1 -1
  54. package/dist/types/src/model/table-presentation.d.ts +2 -1
  55. package/dist/types/src/model/table-presentation.d.ts.map +1 -1
  56. package/dist/types/src/model/table-sorting.d.ts +2 -2
  57. package/dist/types/src/model/table-sorting.d.ts.map +1 -1
  58. package/dist/types/src/testing/useTestTableModel.d.ts +4 -9
  59. package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -1
  60. package/dist/types/src/translations.d.ts +2 -2
  61. package/dist/types/src/types/index.d.ts +1 -1
  62. package/dist/types/src/types/index.d.ts.map +1 -1
  63. package/dist/types/src/types/schema.d.ts +24 -4
  64. package/dist/types/src/types/schema.d.ts.map +1 -1
  65. package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
  66. package/dist/types/src/util/index.d.ts +0 -1
  67. package/dist/types/src/util/index.d.ts.map +1 -1
  68. package/dist/types/src/util/table-buttons.d.ts.map +1 -1
  69. package/dist/types/src/util/table-controls.d.ts.map +1 -1
  70. package/dist/types/tsconfig.tsbuildinfo +1 -1
  71. package/package.json +44 -39
  72. package/src/components/Table/ColumnActionsMenu.tsx +1 -1
  73. package/src/components/Table/ColumnSettings.tsx +1 -1
  74. package/src/components/Table/CreateRefPanel.tsx +25 -8
  75. package/src/components/Table/DynamicTable.stories.tsx +7 -4
  76. package/src/components/Table/DynamicTable.tsx +4 -3
  77. package/src/components/Table/{relations.stories.tsx → Relations.stories.tsx} +40 -23
  78. package/src/components/Table/RowActionsMenu.tsx +3 -3
  79. package/src/components/Table/Table.stories.tsx +34 -23
  80. package/src/components/Table/Table.tsx +138 -112
  81. package/src/components/TableCellEditor/CellValidationMessage.tsx +1 -1
  82. package/src/components/TableCellEditor/FormCellEditor.tsx +143 -42
  83. package/src/components/TableCellEditor/TableCellEditor.stories.tsx +11 -23
  84. package/src/components/TableCellEditor/TableCellEditor.tsx +64 -119
  85. package/src/components/TableToolbar/TableToolbar.tsx +2 -2
  86. package/src/components/testing.ts +1 -1
  87. package/src/hooks/useAddRow.ts +6 -4
  88. package/src/hooks/useTableModel.ts +1 -1
  89. package/src/model/modal-controller.ts +1 -1
  90. package/src/model/selection-model.ts +3 -2
  91. package/src/model/table-model.test.ts +3 -4
  92. package/src/model/table-model.ts +28 -23
  93. package/src/model/table-presentation.test.ts +4 -4
  94. package/src/model/table-presentation.ts +72 -30
  95. package/src/model/table-sorting.ts +3 -3
  96. package/src/playwright/smoke.spec.ts +129 -37
  97. package/src/testing/useTestTableModel.ts +4 -3
  98. package/src/translations.ts +2 -2
  99. package/src/types/index.ts +1 -1
  100. package/src/types/schema.ts +62 -5
  101. package/src/util/dynamic-table.ts +5 -5
  102. package/src/util/index.ts +0 -1
  103. package/src/util/schema.ts +1 -1
  104. package/src/util/table-buttons.ts +4 -3
  105. package/src/util/table-controls.ts +4 -1
  106. package/dist/lib/browser/chunk-5XLSHLOG.mjs.map +0 -7
  107. package/dist/lib/browser/chunk-L2LSB6KV.mjs +0 -18
  108. package/dist/lib/browser/chunk-L2LSB6KV.mjs.map +0 -7
  109. package/dist/lib/node-esm/chunk-NXZ4CJYX.mjs +0 -20
  110. package/dist/lib/node-esm/chunk-NXZ4CJYX.mjs.map +0 -7
  111. package/dist/lib/node-esm/chunk-Y66YLSTC.mjs.map +0 -7
  112. package/dist/types/src/components/Table/relations.stories.d.ts +0 -9
  113. package/dist/types/src/components/Table/relations.stories.d.ts.map +0 -1
  114. package/dist/types/src/components/TableCellEditor/extension.d.ts +0 -13
  115. package/dist/types/src/components/TableCellEditor/extension.d.ts.map +0 -1
  116. package/dist/types/src/util/table.d.ts +0 -11
  117. package/dist/types/src/util/table.d.ts.map +0 -1
  118. package/src/components/TableCellEditor/extension.ts +0 -61
  119. 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 { defaultRowSize, Grid, type GridEditing } from '@dxos/react-ui-grid';
17
- import { createDefaultSchema, DataType } from '@dxos/schema';
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 { TableView } from '../../types';
25
- import { createTable } from '../../util';
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} onQuery={handleQuery} />
64
+ <TableCellEditor model={model} schema={schema} />
77
65
  </Grid.Root>
78
66
  </div>
79
67
  );
80
68
  };
81
69
 
82
- const meta: Meta<StoryProps> = {
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, TableView],
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 createTable({ client, space, typename: schema.typename });
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<StoryProps>;
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, useEffect, useMemo, useRef, useState } from 'react';
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 DxGrid } from '@dxos/lit-grid';
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
- cellQuery,
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 { tagPicker, createLinks } from '@dxos/react-ui-tag-picker';
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
- onEnter?: (cell: DxGridPlanePosition) => void;
51
- onFocus?: DxGrid['refocus'];
45
+ onFocus?: (axis?: DxGridAxis, delta?: -1 | 0 | 1, cell?: DxGridPosition) => void;
52
46
  onSave?: () => void;
53
- onQuery?: (field: FieldProjection, text: string) => Promise<QueryResult[]>;
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
- onQuery,
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 [_validationError, setValidationError] = useState<string | null>(null);
125
- const [_validationVariant, setValidationVariant] = useState<'error' | 'warning'>('error');
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, onEnter, onFocus, setEditing],
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, onEnter, fieldProjection, setEditing, onSave],
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
- if (onQuery) {
303
- switch (fieldProjection.props.format) {
304
- case FormatEnum.Ref: {
305
- extension.push([
306
- completion({
307
- onQuery: (text) => onQuery(fieldProjection, text),
308
- onMatch: (data) => {
309
- if (model && editing && modals) {
310
- if (isCreateOption(data)) {
311
- const { field, props } = fieldProjection;
312
- if (props.referenceSchema) {
313
- suppressNextBlur.current = true;
314
- modals.openCreateRef(
315
- props.referenceSchema,
316
- document.querySelector(cellQuery(editing.index, gridId)),
317
- {
318
- [field.referencePath!]: data.text,
319
- },
320
- (data) => {
321
- void handleEnter(data);
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, setValue, TypedObject, TypeEnum } from '@dxos/echo-schema';
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';
@@ -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 true;
29
+ return 'final';
28
30
  } catch (error) {
29
- return false;
31
+ return 'draft';
30
32
  }
31
33
  }
32
- return false;
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 { type TableRow, TableModel, type TableModelProps, type TableRowAction } from '../model';
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 RefObject, type MutableRefObject } from 'react';
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 { computed, effect, type Signal, signal, type ReadonlySignal } from '@preact/signals-core';
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 = Obj.make(TableView, { sizes: {} });
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
  };