@dxos/react-ui-table 0.8.4-main.5ea62a8 → 0.8.4-main.72ec0f3

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 (97) hide show
  1. package/dist/lib/browser/{chunk-63UUF4IG.mjs → chunk-2PFAPQL4.mjs} +7 -7
  2. package/dist/lib/browser/chunk-2PFAPQL4.mjs.map +7 -0
  3. package/dist/lib/browser/{chunk-RQYXNWWN.mjs → chunk-GS2UNGJK.mjs} +451 -498
  4. package/dist/lib/browser/chunk-GS2UNGJK.mjs.map +7 -0
  5. package/dist/lib/browser/index.mjs +170 -317
  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 +10 -7
  9. package/dist/lib/browser/testing/index.mjs.map +3 -3
  10. package/dist/lib/browser/types/index.mjs +1 -1
  11. package/dist/lib/node-esm/{chunk-K734DJB7.mjs → chunk-6POOFFYF.mjs} +7 -7
  12. package/dist/lib/node-esm/chunk-6POOFFYF.mjs.map +7 -0
  13. package/dist/lib/node-esm/{chunk-QNDUGIS7.mjs → chunk-TRR3C5NX.mjs} +451 -498
  14. package/dist/lib/node-esm/chunk-TRR3C5NX.mjs.map +7 -0
  15. package/dist/lib/node-esm/index.mjs +170 -317
  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 +10 -7
  19. package/dist/lib/node-esm/testing/index.mjs.map +3 -3
  20. package/dist/lib/node-esm/types/index.mjs +1 -1
  21. package/dist/types/src/components/Table/DynamicTable.d.ts +2 -2
  22. package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
  23. package/dist/types/src/components/Table/DynamicTable.stories.d.ts +3 -3
  24. package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
  25. package/dist/types/src/components/Table/Relations.stories.d.ts +70 -7
  26. package/dist/types/src/components/Table/Relations.stories.d.ts.map +1 -1
  27. package/dist/types/src/components/Table/RowActionsMenu.d.ts.map +1 -1
  28. package/dist/types/src/components/Table/Table.d.ts +5 -2
  29. package/dist/types/src/components/Table/Table.d.ts.map +1 -1
  30. package/dist/types/src/components/Table/Table.stories.d.ts +5 -6
  31. package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
  32. package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +7 -5
  33. package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
  34. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +5 -3
  35. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
  36. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts +1 -2
  37. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
  38. package/dist/types/src/components/TableCellEditor/index.d.ts +1 -0
  39. package/dist/types/src/components/TableCellEditor/index.d.ts.map +1 -1
  40. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts +2 -2
  41. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +1 -1
  42. package/dist/types/src/components/testing.d.ts +3 -3
  43. package/dist/types/src/components/testing.d.ts.map +1 -1
  44. package/dist/types/src/hooks/useAddRow.d.ts +1 -1
  45. package/dist/types/src/hooks/useAddRow.d.ts.map +1 -1
  46. package/dist/types/src/hooks/useTableModel.d.ts +3 -3
  47. package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
  48. package/dist/types/src/model/modal-controller.d.ts +0 -7
  49. package/dist/types/src/model/modal-controller.d.ts.map +1 -1
  50. package/dist/types/src/model/table-model.d.ts +4 -4
  51. package/dist/types/src/model/table-model.d.ts.map +1 -1
  52. package/dist/types/src/model/table-presentation.d.ts.map +1 -1
  53. package/dist/types/src/model/table-sorting.d.ts +2 -3
  54. package/dist/types/src/model/table-sorting.d.ts.map +1 -1
  55. package/dist/types/src/testing/useTestTableModel.d.ts +3 -17
  56. package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -1
  57. package/dist/types/src/types/schema.d.ts +6 -6
  58. package/dist/types/src/types/schema.d.ts.map +1 -1
  59. package/dist/types/src/util/dynamic-table.d.ts +5 -6
  60. package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
  61. package/dist/types/src/util/schema.d.ts +1 -1
  62. package/dist/types/src/util/schema.d.ts.map +1 -1
  63. package/dist/types/tsconfig.tsbuildinfo +1 -1
  64. package/package.json +53 -52
  65. package/src/components/Table/DynamicTable.stories.tsx +11 -10
  66. package/src/components/Table/DynamicTable.tsx +4 -7
  67. package/src/components/Table/Relations.stories.tsx +127 -18
  68. package/src/components/Table/RowActionsMenu.tsx +21 -13
  69. package/src/components/Table/Table.stories.tsx +47 -46
  70. package/src/components/Table/Table.tsx +13 -9
  71. package/src/components/TableCellEditor/FormCellEditor.tsx +51 -41
  72. package/src/components/TableCellEditor/TableCellEditor.stories.tsx +19 -46
  73. package/src/components/TableCellEditor/TableCellEditor.tsx +17 -112
  74. package/src/components/TableCellEditor/index.ts +1 -0
  75. package/src/components/TableToolbar/TableToolbar.tsx +32 -22
  76. package/src/components/testing.ts +2 -2
  77. package/src/hooks/useAddRow.ts +1 -1
  78. package/src/hooks/useTableModel.ts +5 -5
  79. package/src/model/modal-controller.ts +0 -28
  80. package/src/model/table-model.test.ts +17 -7
  81. package/src/model/table-model.ts +19 -9
  82. package/src/model/table-presentation.test.ts +20 -18
  83. package/src/model/table-presentation.ts +4 -5
  84. package/src/model/table-sorting.ts +16 -7
  85. package/src/playwright/TableManager.ts +1 -1
  86. package/src/playwright/smoke.spec.ts +7 -5
  87. package/src/testing/useTestTableModel.ts +8 -5
  88. package/src/types/schema.ts +8 -8
  89. package/src/util/dynamic-table.ts +12 -13
  90. package/src/util/schema.ts +4 -4
  91. package/dist/lib/browser/chunk-63UUF4IG.mjs.map +0 -7
  92. package/dist/lib/browser/chunk-RQYXNWWN.mjs.map +0 -7
  93. package/dist/lib/node-esm/chunk-K734DJB7.mjs.map +0 -7
  94. package/dist/lib/node-esm/chunk-QNDUGIS7.mjs.map +0 -7
  95. package/dist/types/src/components/Table/CreateRefPanel.d.ts +0 -9
  96. package/dist/types/src/components/Table/CreateRefPanel.d.ts.map +0 -1
  97. package/src/components/Table/CreateRefPanel.tsx +0 -103
@@ -4,16 +4,14 @@
4
4
 
5
5
  import { type Completion } from '@codemirror/autocomplete';
6
6
  import { EditorView } from '@codemirror/view';
7
- import { type Schema } from 'effect/Schema';
7
+ import type * as Schema from 'effect/Schema';
8
8
  import React, { useCallback, useMemo, useRef, useState } from 'react';
9
9
 
10
10
  import { debounce } from '@dxos/async';
11
11
  import type { Client } from '@dxos/client';
12
- import { FormatEnum, TypeEnum } from '@dxos/echo-schema';
12
+ import { FormatEnum, TypeEnum } from '@dxos/echo/internal';
13
13
  import { invariant } from '@dxos/invariant';
14
14
  import { type DxGridAxis, type DxGridPosition } from '@dxos/lit-grid';
15
- import { useThemeContext } from '@dxos/react-ui';
16
- import { createMarkdownExtensions } from '@dxos/react-ui-editor';
17
15
  import {
18
16
  type EditorBlurHandler,
19
17
  type EditorKeyEvent,
@@ -25,13 +23,12 @@ import {
25
23
  parseCellIndex,
26
24
  useGridContext,
27
25
  } from '@dxos/react-ui-grid';
28
- import { createLinks, tagPicker } from '@dxos/react-ui-tag-picker';
29
26
  import { type FieldProjection } from '@dxos/schema';
30
27
 
31
28
  import { type ModalController, type TableModel } from '../../model';
32
29
 
33
30
  import { CellValidationMessage } from './CellValidationMessage';
34
- import { FormCellEditor } from './FormCellEditor';
31
+ import { FormCellEditor, type OnCreateHandler } from './FormCellEditor';
35
32
 
36
33
  /**
37
34
  * Option to create new object/value.
@@ -41,8 +38,9 @@ export type QueryResult = Pick<Completion, 'label'> & { data: any };
41
38
  export type TableCellEditorProps = {
42
39
  model?: TableModel;
43
40
  modals?: ModalController;
44
- schema?: Schema.AnyNoContext;
41
+ schema?: Schema.Schema.AnyNoContext;
45
42
  onFocus?: (axis?: DxGridAxis, delta?: -1 | 0 | 1, cell?: DxGridPosition) => void;
43
+ onCreate?: OnCreateHandler;
46
44
  onSave?: () => void;
47
45
  client?: Client;
48
46
  };
@@ -56,6 +54,7 @@ export const TableValueEditor = ({
56
54
  schema,
57
55
  onFocus,
58
56
  onSave,
57
+ onCreate,
59
58
  client,
60
59
  __gridScope,
61
60
  }: GridScopedProps<TableCellEditorProps>) => {
@@ -86,6 +85,7 @@ export const TableValueEditor = ({
86
85
  schema={schema}
87
86
  __gridScope={__gridScope}
88
87
  onSave={onSave}
88
+ onCreate={onCreate}
89
89
  client={client}
90
90
  modals={modals}
91
91
  />
@@ -96,7 +96,7 @@ export const TableValueEditor = ({
96
96
  return <TableCellEditor model={model} modals={modals} onFocus={onFocus} onSave={onSave} __gridScope={__gridScope} />;
97
97
  };
98
98
 
99
- const editorSlots = { scroller: { className: '!plb-[--dx-grid-cell-editor-padding-block]' } };
99
+ const editorSlots = { scroll: { className: '!plb-[--dx-grid-cell-editor-padding-block]' } };
100
100
 
101
101
  export const TableCellEditor = ({
102
102
  model,
@@ -105,9 +105,8 @@ export const TableCellEditor = ({
105
105
  onSave,
106
106
  __gridScope,
107
107
  }: GridScopedProps<TableCellEditorProps>) => {
108
- const { id: gridId, editing, setEditing } = useGridContext('TableCellEditor', __gridScope);
108
+ const { editing, setEditing } = useGridContext('TableCellEditor', __gridScope);
109
109
  const suppressNextBlur = useRef(false);
110
- const { themeMode } = useThemeContext();
111
110
  const [validationError, setValidationError] = useState<string | null>(null);
112
111
  const [validationVariant, setValidationVariant] = useState<'error' | 'warning'>('error');
113
112
 
@@ -174,7 +173,6 @@ export const TableCellEditor = ({
174
173
  }
175
174
 
176
175
  const cell = parseCellIndex(editing.index);
177
-
178
176
  if (value !== undefined) {
179
177
  // Pre-commit validation check.
180
178
  const result = await model.validateCellData(cell, value);
@@ -205,71 +203,26 @@ export const TableCellEditor = ({
205
203
  [model, editing, onFocus, fieldProjection, setEditing, onSave],
206
204
  );
207
205
 
208
- const extension = useMemo(() => {
206
+ const extensions = useMemo(() => {
209
207
  if (!fieldProjection) {
210
208
  return [];
211
209
  }
212
210
 
213
- const extension = [
211
+ const extensions = [
214
212
  editorKeys({
215
213
  onClose: handleClose,
216
214
  ...(editing?.initialContent && { onNav: handleClose }),
217
215
  }),
218
216
  ];
219
-
220
- const format = fieldProjection.props.format;
221
-
222
- if (format === FormatEnum.SingleSelect || format === FormatEnum.MultiSelect) {
223
- // TODO(ZaymonFC): Reconcile this with the TagPicker component?
224
- // Add markdown extensions needed by tag picker.
225
- extension.push(createMarkdownExtensions({ themeMode }));
226
-
227
- const options = fieldProjection.props.options || [];
228
-
229
- const mode = format === FormatEnum.SingleSelect ? ('single-select' as const) : ('multi-select' as const);
230
-
231
- extension.push(
232
- tagPicker({
233
- mode,
234
- keymap: false,
235
- onSearch: (text, selectedIds) => {
236
- return options
237
- .filter(
238
- (option) =>
239
- selectedIds.indexOf(option.id) === -1 &&
240
- (text.length === 0 || option.title.toLowerCase().includes(text.toLowerCase())),
241
- )
242
- .map((option) => ({
243
- id: option.id,
244
- label: option.title,
245
- hue: option.color as any,
246
- }));
247
- },
248
- onUpdate: (ids) => {
249
- if (model && editing) {
250
- if (ids.length === 0) {
251
- return;
252
- }
253
- if (mode === 'single-select') {
254
- void handleEnter(ids[0]);
255
- } else {
256
- void handleEnter(ids);
257
- }
258
- }
259
- },
260
- }),
261
- );
262
- }
263
-
264
- // Add validation extension to handle content changes
217
+ // Add validation extension to handle content changes.
265
218
  if (model && editing) {
266
- extension.push(
219
+ extensions.push(
267
220
  EditorView.updateListener.of(
268
221
  debounce((update) => {
269
222
  const content = update.state.doc.toString();
270
223
  const cell = parseCellIndex(editing.index);
271
224
 
272
- // Perform validation on content change
225
+ // Perform validation on content change.
273
226
  void model.validateCellData(cell, content).then((result) => {
274
227
  if (result.valid) {
275
228
  setValidationError(null);
@@ -283,60 +236,12 @@ export const TableCellEditor = ({
283
236
  );
284
237
  }
285
238
 
286
- return extension;
287
- }, [model, modals, editing, fieldProjection, handleClose, themeMode]);
239
+ return extensions;
240
+ }, [model, modals, editing, fieldProjection, handleClose]);
288
241
 
289
242
  const getCellContent = useCallback<GridCellEditorProps['getCellContent']>(() => {
290
243
  if (model && editing) {
291
244
  const cell = parseCellIndex(editing.index);
292
- const { col } = cell;
293
- const field = model.projection.fields[col];
294
- const fieldProjection = model.projection.getFieldProjection(field.id);
295
-
296
- if (
297
- fieldProjection?.props.format === FormatEnum.SingleSelect ||
298
- fieldProjection?.props.format === FormatEnum.MultiSelect
299
- ) {
300
- const value = model.getCellData(cell);
301
-
302
- if (value !== undefined) {
303
- const options = fieldProjection.props.options || [];
304
-
305
- if (fieldProjection.props.format === FormatEnum.MultiSelect) {
306
- const tagItems = value
307
- .split(',')
308
- .map((id: string) => {
309
- const option = options.find((o) => o.id === id);
310
- if (option) {
311
- return {
312
- id,
313
- label: option.title,
314
- hue: option.color as any,
315
- };
316
- }
317
- return undefined;
318
- })
319
- .filter((item: any): item is { id: any; label: string; hue: any } => item !== undefined);
320
-
321
- return createLinks(tagItems);
322
- } else {
323
- const option = options.find((o) => o.id === value);
324
-
325
- if (option) {
326
- const tagItem = {
327
- id: value,
328
- label: option.title,
329
- hue: option.color as any,
330
- };
331
-
332
- return createLinks([tagItem]);
333
- }
334
- }
335
- }
336
-
337
- return '';
338
- }
339
-
340
245
  const value = model.getCellData(cell);
341
246
  return value !== undefined ? String(value) : '';
342
247
  }
@@ -345,7 +250,7 @@ export const TableCellEditor = ({
345
250
  return (
346
251
  <>
347
252
  <CellValidationMessage validationError={validationError} variant={validationVariant} __gridScope={__gridScope} />
348
- <GridCellEditor extension={extension} getCellContent={getCellContent} onBlur={handleBlur} slots={editorSlots} />
253
+ <GridCellEditor extensions={extensions} getCellContent={getCellContent} onBlur={handleBlur} slots={editorSlots} />
349
254
  </>
350
255
  );
351
256
  };
@@ -3,3 +3,4 @@
3
3
  //
4
4
 
5
5
  export * from './TableCellEditor';
6
+ export { type OnCreateHandler } from './FormCellEditor';
@@ -2,7 +2,7 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { Rx } from '@effect-rx/rx-react';
5
+ import { Atom } from '@effect-atom/atom-react';
6
6
  import React, { useMemo } from 'react';
7
7
 
8
8
  import { live } from '@dxos/live-object';
@@ -18,7 +18,7 @@ import {
18
18
  createMenuAction,
19
19
  useMenuActions,
20
20
  } from '@dxos/react-ui-menu';
21
- import { rxFromSignal } from '@dxos/react-ui-menu';
21
+ import { atomFromSignal } from '@dxos/react-ui-menu';
22
22
 
23
23
  import { translationKey } from '../../translations';
24
24
 
@@ -30,12 +30,14 @@ export type TableToolbarActionType = 'add-row' | 'comment' | 'save-view';
30
30
 
31
31
  type TableToolbarState = Partial<{ viewDirty: boolean }>;
32
32
 
33
+ // TODO(burdon): Radix style layout.
34
+
33
35
  export type TableToolbarProps = ThemedClassName<
34
36
  TableToolbarState & {
35
37
  onAdd: () => void;
36
38
  onSave: () => void;
37
39
  attendableId?: string;
38
- customActions?: Rx.Rx<ActionGraphProps>;
40
+ customActions?: Atom.Atom<ActionGraphProps>;
39
41
  }
40
42
  >;
41
43
 
@@ -46,33 +48,41 @@ const createTableToolbarActions = ({
46
48
  customActions,
47
49
  }: {
48
50
  state: TableToolbarState;
49
- onAdd: () => void;
50
- onSave: () => void;
51
- customActions?: Rx.Rx<ActionGraphProps>;
51
+ onAdd?: () => void;
52
+ onSave?: () => void;
53
+ customActions?: Atom.Atom<ActionGraphProps>;
52
54
  }) =>
53
- Rx.make((get) => {
54
- const add = createMenuAction<TableToolbarActionProperties>('add-row', onAdd, {
55
- type: 'add-row' as const,
56
- icon: 'ph--plus--regular',
57
- label: ['add row', { ns: translationKey }],
58
- testId: 'table.toolbar.add-row',
59
- });
60
- const save = createMenuAction<TableToolbarActionProperties>('save-view', onSave, {
61
- type: 'save-view' as const,
62
- icon: 'ph--floppy-disk--regular',
63
- label: ['save view label', { ns: translationKey }],
64
- testId: 'table.toolbar.save-view',
65
- iconOnly: false,
66
- hidden: get(rxFromSignal(() => !state.viewDirty)),
67
- });
55
+ Atom.make((get) => {
56
+ const nodes: ActionGraphNodes = [];
57
+ if (onAdd) {
58
+ const add = createMenuAction<TableToolbarActionProperties>('add-row', onAdd, {
59
+ type: 'add-row' as const,
60
+ icon: 'ph--plus--regular',
61
+ label: ['add row', { ns: translationKey }],
62
+ testId: 'table.toolbar.add-row',
63
+ });
64
+ nodes.push(add);
65
+ }
66
+ if (onSave) {
67
+ const save = createMenuAction<TableToolbarActionProperties>('save-view', onSave, {
68
+ type: 'save-view' as const,
69
+ icon: 'ph--floppy-disk--regular',
70
+ label: ['save view label', { ns: translationKey }],
71
+ testId: 'table.toolbar.save-view',
72
+ iconOnly: false,
73
+ hidden: get(atomFromSignal(() => !state.viewDirty)),
74
+ });
75
+ nodes.push(save);
76
+ }
68
77
  const gap = createGapSeparator();
69
- const nodes: ActionGraphNodes = [add, save, ...gap.nodes];
78
+ nodes.push(...gap.nodes);
70
79
  const edges: ActionGraphEdges = nodes.map(({ id: target }) => ({ source: 'root', target }));
71
80
  if (customActions) {
72
81
  const custom = get(customActions);
73
82
  nodes.push(...custom.nodes);
74
83
  edges.push(...custom.edges);
75
84
  }
85
+
76
86
  return {
77
87
  nodes,
78
88
  edges,
@@ -2,10 +2,10 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { Schema } from 'effect';
5
+ import * as Schema from 'effect/Schema';
6
6
  import { useEffect } from 'react';
7
7
 
8
- import { FormatEnum, ObjectId, TypeEnum, TypedObject, setValue } from '@dxos/echo-schema';
8
+ import { FormatEnum, ObjectId, TypeEnum, TypedObject, setValue } from '@dxos/echo/internal';
9
9
  import { live } from '@dxos/live-object';
10
10
  import { faker } from '@dxos/random';
11
11
  import { type ProjectionModel } from '@dxos/schema';
@@ -2,7 +2,7 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { type Schema } from 'effect';
5
+ import type * as Schema from 'effect/Schema';
6
6
  import { useCallback } from 'react';
7
7
 
8
8
  import { type Space } from '@dxos/client/echo';
@@ -6,17 +6,17 @@ import { effect } from '@preact/signals-core';
6
6
  import orderBy from 'lodash.orderby';
7
7
  import { useEffect, useMemo, useState } from 'react';
8
8
 
9
- import { type JsonSchemaType } from '@dxos/echo-schema';
9
+ import { Obj } from '@dxos/echo';
10
+ import { type JsonSchemaType } from '@dxos/echo/internal';
10
11
  import { type Live } from '@dxos/live-object';
11
- import { fullyQualifiedId } from '@dxos/react-client/echo';
12
12
  import { useSelected, useSelectionActions } from '@dxos/react-ui-attention';
13
- import { type DataType, type ProjectionModel } from '@dxos/schema';
13
+ import { type ProjectionModel, type View } from '@dxos/schema';
14
14
  import { isNonNullable } from '@dxos/util';
15
15
 
16
16
  import { TableModel, type TableModelProps, type TableRow, type TableRowAction } from '../model';
17
17
 
18
18
  export type UseTableModelParams<T extends TableRow = TableRow> = {
19
- view?: DataType.View;
19
+ view?: View.View;
20
20
  schema?: JsonSchemaType;
21
21
  projection?: ProjectionModel;
22
22
  rows?: Live<T>[];
@@ -39,7 +39,7 @@ export const useTableModel = <T extends TableRow = TableRow>({
39
39
  onRowAction,
40
40
  ...props
41
41
  }: UseTableModelParams<T>): TableModel<T> | undefined => {
42
- const selected = useSelected(view && fullyQualifiedId(view), 'multi');
42
+ const selected = useSelected(view && Obj.getDXN(view).toString(), 'multi');
43
43
  const initialSelection = useMemo(() => selected, [view]);
44
44
 
45
45
  const [model, setModel] = useState<TableModel<T>>();
@@ -5,21 +5,12 @@
5
5
  import { signal } from '@preact/signals-core';
6
6
  import { type MutableRefObject, type RefObject } from 'react';
7
7
 
8
- import { type Live } from '@dxos/live-object';
9
- import { log } from '@dxos/log';
10
-
11
8
  export type ColumnSettingsMode = { type: 'create' } | { type: 'edit'; fieldId: string };
12
9
 
13
10
  export type ModalState =
14
11
  | { type: 'row'; rowIndex: number }
15
12
  | { type: 'column'; fieldId: string }
16
13
  | { type: 'refPanel'; targetId: string; typename: string }
17
- | {
18
- type: 'createRefPanel';
19
- typename: string;
20
- initialValues?: Record<string, string>;
21
- onCreate?: (obj: Live<any>) => void;
22
- }
23
14
  | { type: 'columnSettings'; mode: ColumnSettingsMode }
24
15
  | { type: 'closed' };
25
16
 
@@ -68,25 +59,6 @@ export class ModalController {
68
59
  }
69
60
  };
70
61
 
71
- public openCreateRef = (
72
- typename: string,
73
- anchorCell: Element | null,
74
- initialValues?: Record<string, string>,
75
- onCreate?: (obj: Live<any>) => void,
76
- ) => {
77
- if (anchorCell) {
78
- this._triggerRef.current = anchorCell as HTMLElement;
79
- this._state.value = {
80
- type: 'createRefPanel',
81
- typename,
82
- initialValues,
83
- onCreate,
84
- };
85
- } else {
86
- log.warn('anchor cell not found while creating new ref');
87
- }
88
- };
89
-
90
62
  public close = () => {
91
63
  this._state.value = { type: 'closed' };
92
64
  };
@@ -3,15 +3,16 @@
3
3
  //
4
4
 
5
5
  import { computed } from '@preact/signals-core';
6
- import { Schema } from 'effect';
6
+ import * as Schema from 'effect/Schema';
7
7
  import { afterEach, beforeEach, describe, expect, it } from 'vitest';
8
8
 
9
- import { TypedObject } from '@dxos/echo-schema';
10
- import { updateCounter } from '@dxos/echo-schema/testing';
9
+ import { Filter, Query } from '@dxos/echo';
10
+ import { TypedObject } from '@dxos/echo/internal';
11
+ import { updateCounter } from '@dxos/echo/testing';
12
+ import { createEchoSchema } from '@dxos/echo/testing';
11
13
  import { registerSignalsRuntime } from '@dxos/echo-signals';
12
14
  import { live } from '@dxos/live-object';
13
- import { createEchoSchema } from '@dxos/live-object/testing';
14
- import { createView } from '@dxos/schema';
15
+ import { View } from '@dxos/schema';
15
16
 
16
17
  import { Table } from '../types';
17
18
 
@@ -24,7 +25,7 @@ registerSignalsRuntime();
24
25
 
25
26
  describe('TableModel', () => {
26
27
  let updateCount = 0;
27
- let model: TableModel;
28
+ let model: any;
28
29
 
29
30
  beforeEach(async () => {
30
31
  updateCount = 0;
@@ -32,6 +33,11 @@ describe('TableModel', () => {
32
33
  onCellUpdate: () => updateCount++,
33
34
  });
34
35
  await model.open();
36
+ model.setRows([
37
+ { id: '1', title: 'Test', completed: false },
38
+ { id: '2', title: 'Test 2', completed: true },
39
+ { id: '3', title: 'Test 3', completed: false },
40
+ ]);
35
41
  });
36
42
 
37
43
  afterEach(async () => {
@@ -107,6 +113,10 @@ class Test extends TypedObject({ typename: 'example.com/type/Test', version: '0.
107
113
  const createTableModel = (props: Partial<TableModelProps> = {}): TableModel => {
108
114
  const schema = createEchoSchema(Test);
109
115
  const table = Table.make();
110
- const view = createView({ typename: schema.typename, jsonSchema: schema.jsonSchema, presentation: table });
116
+ const view = View.make({
117
+ query: Query.select(Filter.type(schema)),
118
+ jsonSchema: schema.jsonSchema,
119
+ presentation: table,
120
+ });
111
121
  return new TableModel({ view, schema: schema.jsonSchema, ...props });
112
122
  };
@@ -7,7 +7,6 @@ import { type ReadonlySignal, computed, effect, signal } from '@preact/signals-c
7
7
  import { Resource } from '@dxos/context';
8
8
  import { Obj, Ref } from '@dxos/echo';
9
9
  import {
10
- type FieldSortType,
11
10
  FormatEnum,
12
11
  type JsonProp,
13
12
  type JsonSchemaType,
@@ -15,11 +14,10 @@ import {
15
14
  getValue,
16
15
  setValue,
17
16
  toEffectSchema,
18
- } from '@dxos/echo-schema';
17
+ } from '@dxos/echo/internal';
19
18
  import { invariant } from '@dxos/invariant';
20
19
  import { ObjectId } from '@dxos/keys';
21
20
  import { getSnapshot, isLiveObject } from '@dxos/live-object';
22
- import { fullyQualifiedId } from '@dxos/react-client/echo';
23
21
  import { type Label } from '@dxos/react-ui';
24
22
  import { formatForEditing, parseValue } from '@dxos/react-ui-form';
25
23
  import {
@@ -28,7 +26,14 @@ import {
28
26
  type DxGridPlaneRange,
29
27
  type DxGridPosition,
30
28
  } from '@dxos/react-ui-grid';
31
- import { type DataType, ProjectionModel, type PropertyType, type ValidationError, validateSchema } from '@dxos/schema';
29
+ import {
30
+ type FieldSortType,
31
+ ProjectionModel,
32
+ type PropertyType,
33
+ type ValidationError,
34
+ type View,
35
+ validateSchema,
36
+ } from '@dxos/schema';
32
37
 
33
38
  import { Table } from '../types';
34
39
  import { touch } from '../util';
@@ -72,7 +77,7 @@ const defaultFeatures: TableFeatures = {
72
77
  export type InsertRowResult = 'draft' | 'final';
73
78
 
74
79
  export type TableModelProps<T extends TableRow = TableRow> = {
75
- view: DataType.View;
80
+ view: View.View;
76
81
  schema: JsonSchemaType;
77
82
  projection?: ProjectionModel;
78
83
  features?: Partial<TableFeatures>;
@@ -90,7 +95,7 @@ export type TableModelProps<T extends TableRow = TableRow> = {
90
95
  };
91
96
 
92
97
  export class TableModel<T extends TableRow = TableRow> extends Resource {
93
- private readonly _view: DataType.View;
98
+ private readonly _view: View.View;
94
99
  private readonly _projection: ProjectionModel;
95
100
  private _table?: Table.Table;
96
101
 
@@ -135,6 +140,7 @@ export class TableModel<T extends TableRow = TableRow> extends Resource {
135
140
  super();
136
141
  this._view = view;
137
142
  this._projection = projection ?? new ProjectionModel(schema, view.projection);
143
+ this._projection.normalizeView();
138
144
 
139
145
  // TODO(ZaymonFC): Use our more robust config merging module?
140
146
  this._features = { ...defaultFeatures, ...features };
@@ -169,10 +175,10 @@ export class TableModel<T extends TableRow = TableRow> extends Resource {
169
175
  }
170
176
 
171
177
  public get id(): string {
172
- return fullyQualifiedId(this._view);
178
+ return Obj.getDXN(this._view).toString();
173
179
  }
174
180
 
175
- public get view(): DataType.View {
181
+ public get view(): View.View {
176
182
  return this._view;
177
183
  }
178
184
 
@@ -473,7 +479,11 @@ export class TableModel<T extends TableRow = TableRow> extends Resource {
473
479
  }
474
480
 
475
481
  default: {
476
- return formatForEditing({ type: props.type, format: props.format, value });
482
+ return formatForEditing({
483
+ type: props.type,
484
+ format: props.format,
485
+ value,
486
+ });
477
487
  }
478
488
  }
479
489
  };
@@ -2,13 +2,14 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { Schema } from 'effect';
5
+ import * as Schema from 'effect/Schema';
6
6
  import { beforeEach, describe, expect, it } from 'vitest';
7
7
 
8
- import { TypedObject } from '@dxos/echo-schema';
8
+ import { Filter, Query } from '@dxos/echo';
9
+ import { TypedObject } from '@dxos/echo/internal';
10
+ import { createEchoSchema } from '@dxos/echo/testing';
9
11
  import { live } from '@dxos/live-object';
10
- import { createEchoSchema } from '@dxos/live-object/testing';
11
- import { createView } from '@dxos/schema';
12
+ import { View } from '@dxos/schema';
12
13
 
13
14
  import { Table } from '../types';
14
15
 
@@ -26,8 +27,8 @@ describe('TablePresentation', () => {
26
27
  updateCount = 0;
27
28
  ({ data } = live({
28
29
  data: [
29
- { col1: 'A', col2: 1, col3: true },
30
- { col1: 'B', col2: 2, col3: false },
30
+ { title: 'A', count: 1 },
31
+ { title: 'B', count: 2 },
31
32
  ],
32
33
  }));
33
34
 
@@ -44,8 +45,8 @@ describe('TablePresentation', () => {
44
45
  presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 1, col: 2 } }, 'grid');
45
46
 
46
47
  // Trigger a row update.
47
- data[0].col1 = 'New Value';
48
- expect(updateCount).toBe(1);
48
+ data[0].title = 'New Value';
49
+ expect(updateCount).toBe(2);
49
50
 
50
51
  // Verify the new value through getCells.
51
52
  const cells = presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 0, col: 0 } }, 'grid');
@@ -56,15 +57,14 @@ describe('TablePresentation', () => {
56
57
  // Set up visible range to include our test data.
57
58
  presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 2, col: 2 } }, 'grid');
58
59
 
59
- data.push({ col1: 'C', col2: 3, col3: true });
60
- expect(updateCount).toBe(1);
60
+ data.push({ title: 'C', count: 3 });
61
+ expect(updateCount).toBe(2);
61
62
 
62
63
  const cells = presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 2, col: 2 } }, 'grid');
63
64
 
64
65
  // Verify the new row's data.
65
66
  expect(cells['0,2']?.value).toBe('C');
66
67
  expect(cells['1,2']?.value).toBe('3');
67
- expect(cells['2,2']?.value).toBe('true');
68
68
  });
69
69
 
70
70
  it('should handle combined operations reactively', () => {
@@ -72,32 +72,34 @@ describe('TablePresentation', () => {
72
72
  presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 2, col: 2 } }, 'grid');
73
73
 
74
74
  // Multiple operations.
75
- data[0].col1 = 'Updated A';
76
- data.push({ col1: 'C', col2: 3, col3: true });
75
+ data[0].title = 'Updated A';
76
+ data.push({ title: 'C', count: 3, completed: true });
77
77
  data.splice(1, 1);
78
78
 
79
79
  // We expect one update per operation affecting visible rows.
80
- expect(updateCount).toBe(3);
80
+ expect(updateCount).toBe(4);
81
81
 
82
82
  const cells = presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 1, col: 2 } }, 'grid');
83
83
 
84
84
  // Verify final state.
85
85
  expect(cells['0,0']?.value).toBe('Updated A');
86
86
  expect(cells['0,1']?.value).toBe('C');
87
- expect(cells['1,1']?.value).toBe('3');
88
- expect(cells['2,1']?.value).toBe('true');
89
87
  });
90
88
  });
91
89
  });
92
90
 
93
91
  class Test extends TypedObject({ typename: 'example.com/type/Test', version: '0.1.0' })({
94
92
  title: Schema.String,
95
- completed: Schema.Boolean,
93
+ count: Schema.Number,
96
94
  }) {}
97
95
 
98
96
  const createTableModel = (props: Partial<TableModelProps> = {}): TableModel => {
99
97
  const schema = createEchoSchema(Test);
100
98
  const table = Table.make();
101
- const view = createView({ typename: schema.typename, jsonSchema: schema.jsonSchema, presentation: table });
99
+ const view = View.make({
100
+ query: Query.select(Filter.type(schema)),
101
+ jsonSchema: schema.jsonSchema,
102
+ presentation: table,
103
+ });
102
104
  return new TableModel({ view, schema: schema.jsonSchema, ...props });
103
105
  };