@dxos/react-ui-table 0.8.4-main.e098934 → 0.8.4-main.e8ec1fe

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-BBXDTWCX.mjs → chunk-GS2UNGJK.mjs} +443 -490
  4. package/dist/lib/browser/chunk-GS2UNGJK.mjs.map +7 -0
  5. package/dist/lib/browser/index.mjs +161 -303
  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 +9 -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-RNQDU7IQ.mjs → chunk-TRR3C5NX.mjs} +443 -490
  14. package/dist/lib/node-esm/chunk-TRR3C5NX.mjs.map +7 -0
  15. package/dist/lib/node-esm/index.mjs +161 -303
  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 +9 -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 -2
  54. package/dist/types/src/model/table-sorting.d.ts.map +1 -1
  55. package/dist/types/src/testing/useTestTableModel.d.ts +3 -3
  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 +52 -51
  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 +44 -43
  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 +7 -94
  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 +12 -7
  81. package/src/model/table-model.ts +12 -8
  82. package/src/model/table-presentation.test.ts +15 -18
  83. package/src/model/table-presentation.ts +4 -5
  84. package/src/model/table-sorting.ts +9 -9
  85. package/src/playwright/TableManager.ts +1 -1
  86. package/src/playwright/smoke.spec.ts +7 -5
  87. package/src/testing/useTestTableModel.ts +7 -5
  88. package/src/types/schema.ts +8 -8
  89. package/src/util/dynamic-table.ts +9 -11
  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-BBXDTWCX.mjs.map +0 -7
  93. package/dist/lib/node-esm/chunk-K734DJB7.mjs.map +0 -7
  94. package/dist/lib/node-esm/chunk-RNQDU7IQ.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,15 +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 { createMarkdownExtensions } from '@dxos/react-ui-editor';
16
15
  import {
17
16
  type EditorBlurHandler,
18
17
  type EditorKeyEvent,
@@ -24,13 +23,12 @@ import {
24
23
  parseCellIndex,
25
24
  useGridContext,
26
25
  } from '@dxos/react-ui-grid';
27
- import { createLinks, tagPicker } from '@dxos/react-ui-tag-picker';
28
26
  import { type FieldProjection } from '@dxos/schema';
29
27
 
30
28
  import { type ModalController, type TableModel } from '../../model';
31
29
 
32
30
  import { CellValidationMessage } from './CellValidationMessage';
33
- import { FormCellEditor } from './FormCellEditor';
31
+ import { FormCellEditor, type OnCreateHandler } from './FormCellEditor';
34
32
 
35
33
  /**
36
34
  * Option to create new object/value.
@@ -40,8 +38,9 @@ export type QueryResult = Pick<Completion, 'label'> & { data: any };
40
38
  export type TableCellEditorProps = {
41
39
  model?: TableModel;
42
40
  modals?: ModalController;
43
- schema?: Schema.AnyNoContext;
41
+ schema?: Schema.Schema.AnyNoContext;
44
42
  onFocus?: (axis?: DxGridAxis, delta?: -1 | 0 | 1, cell?: DxGridPosition) => void;
43
+ onCreate?: OnCreateHandler;
45
44
  onSave?: () => void;
46
45
  client?: Client;
47
46
  };
@@ -55,6 +54,7 @@ export const TableValueEditor = ({
55
54
  schema,
56
55
  onFocus,
57
56
  onSave,
57
+ onCreate,
58
58
  client,
59
59
  __gridScope,
60
60
  }: GridScopedProps<TableCellEditorProps>) => {
@@ -85,6 +85,7 @@ export const TableValueEditor = ({
85
85
  schema={schema}
86
86
  __gridScope={__gridScope}
87
87
  onSave={onSave}
88
+ onCreate={onCreate}
88
89
  client={client}
89
90
  modals={modals}
90
91
  />
@@ -213,48 +214,6 @@ export const TableCellEditor = ({
213
214
  ...(editing?.initialContent && { onNav: handleClose }),
214
215
  }),
215
216
  ];
216
-
217
- const format = fieldProjection.props.format;
218
- if (format === FormatEnum.SingleSelect || format === FormatEnum.MultiSelect) {
219
- // TODO(ZaymonFC): Reconcile this with the TagPicker component?
220
- const options = fieldProjection.props.options || [];
221
- const mode = format === FormatEnum.SingleSelect ? ('single-select' as const) : ('multi-select' as const);
222
-
223
- // Add markdown extensions needed by tag picker.
224
- extensions.push(createMarkdownExtensions());
225
- extensions.push(
226
- tagPicker({
227
- mode,
228
- keymap: false,
229
- onSearch: (text, selectedIds) => {
230
- return options
231
- .filter(
232
- (option) =>
233
- selectedIds.indexOf(option.id) === -1 &&
234
- (text.length === 0 || option.title.toLowerCase().includes(text.toLowerCase())),
235
- )
236
- .map((option) => ({
237
- id: option.id,
238
- label: option.title,
239
- hue: option.color as any,
240
- }));
241
- },
242
- onUpdate: (ids) => {
243
- if (model && editing) {
244
- if (ids.length === 0) {
245
- return;
246
- }
247
- if (mode === 'single-select') {
248
- void handleEnter(ids[0]);
249
- } else {
250
- void handleEnter(ids);
251
- }
252
- }
253
- },
254
- }),
255
- );
256
- }
257
-
258
217
  // Add validation extension to handle content changes.
259
218
  if (model && editing) {
260
219
  extensions.push(
@@ -283,52 +242,6 @@ export const TableCellEditor = ({
283
242
  const getCellContent = useCallback<GridCellEditorProps['getCellContent']>(() => {
284
243
  if (model && editing) {
285
244
  const cell = parseCellIndex(editing.index);
286
- const { col } = cell;
287
- const field = model.projection.fields[col];
288
- const fieldProjection = model.projection.getFieldProjection(field.id);
289
-
290
- if (
291
- fieldProjection?.props.format === FormatEnum.SingleSelect ||
292
- fieldProjection?.props.format === FormatEnum.MultiSelect
293
- ) {
294
- const value = model.getCellData(cell);
295
- if (value !== undefined) {
296
- const options = fieldProjection.props.options || [];
297
- if (fieldProjection.props.format === FormatEnum.MultiSelect) {
298
- const tagItems = value
299
- .split(',')
300
- .map((id: string) => {
301
- const option = options.find((o) => o.id === id);
302
- if (option) {
303
- return {
304
- id,
305
- label: option.title,
306
- hue: option.color as any,
307
- };
308
- }
309
- return undefined;
310
- })
311
- .filter((item: any): item is { id: any; label: string; hue: any } => item !== undefined);
312
-
313
- return createLinks(tagItems);
314
- } else {
315
- const option = options.find((o) => o.id === value);
316
-
317
- if (option) {
318
- const tagItem = {
319
- id: value,
320
- label: option.title,
321
- hue: option.color as any,
322
- };
323
-
324
- return createLinks([tagItem]);
325
- }
326
- }
327
- }
328
-
329
- return '';
330
- }
331
-
332
245
  const value = model.getCellData(cell);
333
246
  return value !== undefined ? String(value) : '';
334
247
  }
@@ -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,16 +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
9
  import { Filter, Query } from '@dxos/echo';
10
- import { TypedObject } from '@dxos/echo-schema';
11
- import { updateCounter } from '@dxos/echo-schema/testing';
10
+ import { TypedObject } from '@dxos/echo/internal';
11
+ import { updateCounter } from '@dxos/echo/testing';
12
+ import { createEchoSchema } from '@dxos/echo/testing';
12
13
  import { registerSignalsRuntime } from '@dxos/echo-signals';
13
14
  import { live } from '@dxos/live-object';
14
- import { createEchoSchema } from '@dxos/live-object/testing';
15
- import { createView } from '@dxos/schema';
15
+ import { View } from '@dxos/schema';
16
16
 
17
17
  import { Table } from '../types';
18
18
 
@@ -25,7 +25,7 @@ registerSignalsRuntime();
25
25
 
26
26
  describe('TableModel', () => {
27
27
  let updateCount = 0;
28
- let model: TableModel;
28
+ let model: any;
29
29
 
30
30
  beforeEach(async () => {
31
31
  updateCount = 0;
@@ -33,6 +33,11 @@ describe('TableModel', () => {
33
33
  onCellUpdate: () => updateCount++,
34
34
  });
35
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
+ ]);
36
41
  });
37
42
 
38
43
  afterEach(async () => {
@@ -108,7 +113,7 @@ class Test extends TypedObject({ typename: 'example.com/type/Test', version: '0.
108
113
  const createTableModel = (props: Partial<TableModelProps> = {}): TableModel => {
109
114
  const schema = createEchoSchema(Test);
110
115
  const table = Table.make();
111
- const view = createView({
116
+ const view = View.make({
112
117
  query: Query.select(Filter.type(schema)),
113
118
  jsonSchema: schema.jsonSchema,
114
119
  presentation: table,
@@ -14,11 +14,10 @@ import {
14
14
  getValue,
15
15
  setValue,
16
16
  toEffectSchema,
17
- } from '@dxos/echo-schema';
17
+ } from '@dxos/echo/internal';
18
18
  import { invariant } from '@dxos/invariant';
19
19
  import { ObjectId } from '@dxos/keys';
20
20
  import { getSnapshot, isLiveObject } from '@dxos/live-object';
21
- import { fullyQualifiedId } from '@dxos/react-client/echo';
22
21
  import { type Label } from '@dxos/react-ui';
23
22
  import { formatForEditing, parseValue } from '@dxos/react-ui-form';
24
23
  import {
@@ -28,11 +27,11 @@ import {
28
27
  type DxGridPosition,
29
28
  } from '@dxos/react-ui-grid';
30
29
  import {
31
- type DataType,
32
30
  type FieldSortType,
33
31
  ProjectionModel,
34
32
  type PropertyType,
35
33
  type ValidationError,
34
+ type View,
36
35
  validateSchema,
37
36
  } from '@dxos/schema';
38
37
 
@@ -78,7 +77,7 @@ const defaultFeatures: TableFeatures = {
78
77
  export type InsertRowResult = 'draft' | 'final';
79
78
 
80
79
  export type TableModelProps<T extends TableRow = TableRow> = {
81
- view: DataType.View;
80
+ view: View.View;
82
81
  schema: JsonSchemaType;
83
82
  projection?: ProjectionModel;
84
83
  features?: Partial<TableFeatures>;
@@ -96,7 +95,7 @@ export type TableModelProps<T extends TableRow = TableRow> = {
96
95
  };
97
96
 
98
97
  export class TableModel<T extends TableRow = TableRow> extends Resource {
99
- private readonly _view: DataType.View;
98
+ private readonly _view: View.View;
100
99
  private readonly _projection: ProjectionModel;
101
100
  private _table?: Table.Table;
102
101
 
@@ -141,6 +140,7 @@ export class TableModel<T extends TableRow = TableRow> extends Resource {
141
140
  super();
142
141
  this._view = view;
143
142
  this._projection = projection ?? new ProjectionModel(schema, view.projection);
143
+ this._projection.normalizeView();
144
144
 
145
145
  // TODO(ZaymonFC): Use our more robust config merging module?
146
146
  this._features = { ...defaultFeatures, ...features };
@@ -175,10 +175,10 @@ export class TableModel<T extends TableRow = TableRow> extends Resource {
175
175
  }
176
176
 
177
177
  public get id(): string {
178
- return fullyQualifiedId(this._view);
178
+ return Obj.getDXN(this._view).toString();
179
179
  }
180
180
 
181
- public get view(): DataType.View {
181
+ public get view(): View.View {
182
182
  return this._view;
183
183
  }
184
184
 
@@ -479,7 +479,11 @@ export class TableModel<T extends TableRow = TableRow> extends Resource {
479
479
  }
480
480
 
481
481
  default: {
482
- return formatForEditing({ type: props.type, format: props.format, value });
482
+ return formatForEditing({
483
+ type: props.type,
484
+ format: props.format,
485
+ value,
486
+ });
483
487
  }
484
488
  }
485
489
  };
@@ -2,14 +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
8
  import { Filter, Query } from '@dxos/echo';
9
- import { TypedObject } from '@dxos/echo-schema';
9
+ import { TypedObject } from '@dxos/echo/internal';
10
+ import { createEchoSchema } from '@dxos/echo/testing';
10
11
  import { live } from '@dxos/live-object';
11
- import { createEchoSchema } from '@dxos/live-object/testing';
12
- import { createView } from '@dxos/schema';
12
+ import { View } from '@dxos/schema';
13
13
 
14
14
  import { Table } from '../types';
15
15
 
@@ -27,8 +27,8 @@ describe('TablePresentation', () => {
27
27
  updateCount = 0;
28
28
  ({ data } = live({
29
29
  data: [
30
- { col1: 'A', col2: 1, col3: true },
31
- { col1: 'B', col2: 2, col3: false },
30
+ { title: 'A', count: 1 },
31
+ { title: 'B', count: 2 },
32
32
  ],
33
33
  }));
34
34
 
@@ -45,8 +45,8 @@ describe('TablePresentation', () => {
45
45
  presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 1, col: 2 } }, 'grid');
46
46
 
47
47
  // Trigger a row update.
48
- data[0].col1 = 'New Value';
49
- expect(updateCount).toBe(1);
48
+ data[0].title = 'New Value';
49
+ expect(updateCount).toBe(2);
50
50
 
51
51
  // Verify the new value through getCells.
52
52
  const cells = presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 0, col: 0 } }, 'grid');
@@ -57,15 +57,14 @@ describe('TablePresentation', () => {
57
57
  // Set up visible range to include our test data.
58
58
  presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 2, col: 2 } }, 'grid');
59
59
 
60
- data.push({ col1: 'C', col2: 3, col3: true });
61
- expect(updateCount).toBe(1);
60
+ data.push({ title: 'C', count: 3 });
61
+ expect(updateCount).toBe(2);
62
62
 
63
63
  const cells = presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 2, col: 2 } }, 'grid');
64
64
 
65
65
  // Verify the new row's data.
66
66
  expect(cells['0,2']?.value).toBe('C');
67
67
  expect(cells['1,2']?.value).toBe('3');
68
- expect(cells['2,2']?.value).toBe('true');
69
68
  });
70
69
 
71
70
  it('should handle combined operations reactively', () => {
@@ -73,33 +72,31 @@ describe('TablePresentation', () => {
73
72
  presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 2, col: 2 } }, 'grid');
74
73
 
75
74
  // Multiple operations.
76
- data[0].col1 = 'Updated A';
77
- data.push({ col1: 'C', col2: 3, col3: true });
75
+ data[0].title = 'Updated A';
76
+ data.push({ title: 'C', count: 3, completed: true });
78
77
  data.splice(1, 1);
79
78
 
80
79
  // We expect one update per operation affecting visible rows.
81
- expect(updateCount).toBe(3);
80
+ expect(updateCount).toBe(4);
82
81
 
83
82
  const cells = presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 1, col: 2 } }, 'grid');
84
83
 
85
84
  // Verify final state.
86
85
  expect(cells['0,0']?.value).toBe('Updated A');
87
86
  expect(cells['0,1']?.value).toBe('C');
88
- expect(cells['1,1']?.value).toBe('3');
89
- expect(cells['2,1']?.value).toBe('true');
90
87
  });
91
88
  });
92
89
  });
93
90
 
94
91
  class Test extends TypedObject({ typename: 'example.com/type/Test', version: '0.1.0' })({
95
92
  title: Schema.String,
96
- completed: Schema.Boolean,
93
+ count: Schema.Number,
97
94
  }) {}
98
95
 
99
96
  const createTableModel = (props: Partial<TableModelProps> = {}): TableModel => {
100
97
  const schema = createEchoSchema(Test);
101
98
  const table = Table.make();
102
- const view = createView({
99
+ const view = View.make({
103
100
  query: Query.select(Filter.type(schema)),
104
101
  jsonSchema: schema.jsonSchema,
105
102
  presentation: table,
@@ -3,10 +3,10 @@
3
3
  //
4
4
 
5
5
  import { signal } from '@preact/signals-core';
6
- import { isNotNullable } from 'effect/Predicate';
6
+ import * as Predicate from 'effect/Predicate';
7
7
 
8
8
  import { Obj } from '@dxos/echo';
9
- import { FormatEnum, TypeEnum, getValue } from '@dxos/echo-schema';
9
+ import { FormatEnum, TypeEnum, getValue } from '@dxos/echo/internal';
10
10
  import { cellClassesForFieldType, formatForDisplay } from '@dxos/react-ui-form';
11
11
  import {
12
12
  type DxGridCellValue,
@@ -190,7 +190,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
190
190
 
191
191
  const tags = targetArray
192
192
  .map(getLabel)
193
- .filter(isNotNullable)
193
+ .filter(Predicate.isNotNullable)
194
194
  .map((title) => {
195
195
  return `<span class="dx-tag" data-hue="neutral">${title}</span>`;
196
196
  })
@@ -205,7 +205,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
205
205
  const targetObj = getValue(obj, field.path)?.target;
206
206
  if (targetObj) {
207
207
  const dxn = Obj.getDXN(targetObj)?.toString();
208
- cell.accessoryHtml = `<dx-anchor refId=${dxn} class="dx-button is-6 pli-[3px] pbe-[2px] min-bs-0 absolute inline-end-2 block-start-[.2rem]" data-dx-grid-action="accessory"><dx-icon icon="ph--link-simple--regular"/></dx-anchor>`;
208
+ cell.accessoryHtml = `<div role="none" class="absolute inline-end-0 inset-block-0 p-[--dx-grid-cell-content-padding-block]"><dx-anchor refId=${dxn} class="dx-button is-6 aspect-square min-bs-0" data-dx-grid-action="accessory"><dx-icon icon="ph--link-simple--regular"/></dx-anchor></div>`;
209
209
  }
210
210
  }
211
211
 
@@ -468,7 +468,6 @@ export const cellClassesForRowSelection = (selected: boolean, selectionMode: Sel
468
468
 
469
469
  switch (selectionMode) {
470
470
  case 'single':
471
- // TODO(ZaymonFC): @thure, do we need a grid version of 'currentRelated'?
472
471
  return ['!bg-currentRelated dx-grid__cell--no-focus-unfurl hover:bg-hoverSurface !cursor-pointer'];
473
472
  case 'multiple':
474
473
  return ['!bg-gridCellSelected'];
@@ -5,15 +5,15 @@
5
5
  import { type ReadonlySignal, type Signal, computed, signal } from '@preact/signals-core';
6
6
  import orderBy from 'lodash.orderby';
7
7
 
8
- import { FormatEnum, TypeEnum, getValue } from '@dxos/echo-schema';
8
+ import { FormatEnum, TypeEnum, getValue } from '@dxos/echo/internal';
9
9
  import { formatForDisplay } from '@dxos/react-ui-form';
10
- import type {
11
- DataType,
12
- FieldSortType,
13
- FieldType,
14
- ProjectionModel,
15
- PropertyType,
16
- SortDirectionType,
10
+ import {
11
+ type FieldSortType,
12
+ type FieldType,
13
+ type ProjectionModel,
14
+ type PropertyType,
15
+ type SortDirectionType,
16
+ type View,
17
17
  } from '@dxos/schema';
18
18
 
19
19
  import { type TableRow } from './table-model';
@@ -58,7 +58,7 @@ export class TableSorting<T extends TableRow> {
58
58
 
59
59
  constructor(
60
60
  rows: Signal<T[]>,
61
- private readonly _view: DataType.View,
61
+ private readonly _view: View.View,
62
62
  private readonly _projection: ProjectionModel,
63
63
  ) {
64
64
  this._rows = rows;
@@ -59,6 +59,6 @@ export class TableManager {
59
59
  await this.page.getByLabel(`format ${format}`).click();
60
60
  await this.page.getByPlaceholder('Property label.').click();
61
61
  await this.page.getByPlaceholder('Property label.').fill(label);
62
- await this.page.getByRole('button', { name: 'button save' }).click();
62
+ await this.page.getByRole('button', { name: 'save button' }).click();
63
63
  }
64
64
  }