@dxos/react-ui-table 0.8.2-staging.7ac8446 → 0.8.2

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-U6LL2AVU.mjs +26 -0
  2. package/dist/lib/browser/chunk-U6LL2AVU.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +1115 -905
  4. package/dist/lib/browser/index.mjs.map +4 -4
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/types/index.mjs +1 -1
  7. package/dist/lib/node/{chunk-TGG6OZ54.cjs → chunk-CXDGXPM6.cjs} +12 -12
  8. package/dist/lib/node/chunk-CXDGXPM6.cjs.map +7 -0
  9. package/dist/lib/node/index.cjs +1113 -906
  10. package/dist/lib/node/index.cjs.map +4 -4
  11. package/dist/lib/node/meta.json +1 -1
  12. package/dist/lib/node/types/index.cjs +3 -3
  13. package/dist/lib/node/types/index.cjs.map +1 -1
  14. package/dist/lib/node-esm/{chunk-KKLP54L3.mjs → chunk-EFSM52KQ.mjs} +10 -10
  15. package/dist/lib/node-esm/chunk-EFSM52KQ.mjs.map +7 -0
  16. package/dist/lib/node-esm/index.mjs +1115 -905
  17. package/dist/lib/node-esm/index.mjs.map +4 -4
  18. package/dist/lib/node-esm/meta.json +1 -1
  19. package/dist/lib/node-esm/types/index.mjs +1 -1
  20. package/dist/types/src/components/Table/ColumnActionsMenu.d.ts.map +1 -1
  21. package/dist/types/src/components/Table/ColumnSettings.d.ts.map +1 -1
  22. package/dist/types/src/components/Table/CreateRefPanel.d.ts.map +1 -1
  23. package/dist/types/src/components/Table/DynamicTable.d.ts +3 -4
  24. package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
  25. package/dist/types/src/components/Table/RowActionsMenu.d.ts.map +1 -1
  26. package/dist/types/src/components/Table/Table.d.ts +2 -0
  27. package/dist/types/src/components/Table/Table.d.ts.map +1 -1
  28. package/dist/types/src/components/Table/Table.stories.d.ts +1 -0
  29. package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
  30. package/dist/types/src/components/Table/relations.stories.d.ts.map +1 -1
  31. package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +13 -0
  32. package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -0
  33. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +3 -0
  34. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
  35. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
  36. package/dist/types/src/components/TableCellEditor/extension.d.ts.map +1 -1
  37. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts +6 -3
  38. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +1 -1
  39. package/dist/types/src/components/testing.d.ts +40 -40
  40. package/dist/types/src/components/testing.d.ts.map +1 -1
  41. package/dist/types/src/hooks/useTableModel.d.ts +2 -2
  42. package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
  43. package/dist/types/src/model/modal-controller.d.ts +3 -3
  44. package/dist/types/src/model/modal-controller.d.ts.map +1 -1
  45. package/dist/types/src/model/selection-model.d.ts.map +1 -1
  46. package/dist/types/src/model/table-model.d.ts.map +1 -1
  47. package/dist/types/src/model/table-presentation.d.ts.map +1 -1
  48. package/dist/types/src/types/table.d.ts +46 -45
  49. package/dist/types/src/types/table.d.ts.map +1 -1
  50. package/dist/types/src/util/dynamic-table.d.ts +4 -3
  51. package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
  52. package/dist/types/src/util/index.d.ts +1 -0
  53. package/dist/types/src/util/index.d.ts.map +1 -1
  54. package/dist/types/src/util/schema.d.ts +12 -0
  55. package/dist/types/src/util/schema.d.ts.map +1 -0
  56. package/dist/types/src/util/signals.d.ts.map +1 -1
  57. package/dist/types/src/util/table-buttons.d.ts +0 -14
  58. package/dist/types/src/util/table-buttons.d.ts.map +1 -1
  59. package/dist/types/src/util/table-controls.d.ts.map +1 -1
  60. package/dist/types/src/util/table.d.ts +2 -2
  61. package/dist/types/src/util/table.d.ts.map +1 -1
  62. package/dist/types/src/util/tag.d.ts +11 -0
  63. package/dist/types/src/util/tag.d.ts.map +1 -0
  64. package/dist/types/tsconfig.tsbuildinfo +1 -1
  65. package/package.json +41 -34
  66. package/src/components/Table/CreateRefPanel.tsx +5 -5
  67. package/src/components/Table/DynamicTable.stories.tsx +4 -4
  68. package/src/components/Table/DynamicTable.tsx +4 -5
  69. package/src/components/Table/Table.stories.tsx +84 -50
  70. package/src/components/Table/Table.tsx +25 -28
  71. package/src/components/Table/relations.stories.tsx +38 -32
  72. package/src/components/TableCellEditor/FormCellEditor.tsx +94 -0
  73. package/src/components/TableCellEditor/TableCellEditor.stories.tsx +13 -11
  74. package/src/components/TableCellEditor/TableCellEditor.tsx +45 -4
  75. package/src/components/TableToolbar/TableToolbar.tsx +65 -35
  76. package/src/components/testing.ts +10 -9
  77. package/src/hooks/useTableModel.ts +6 -4
  78. package/src/model/modal-controller.ts +3 -3
  79. package/src/model/selection-model.ts +1 -1
  80. package/src/model/table-model.test.ts +7 -6
  81. package/src/model/table-model.ts +20 -3
  82. package/src/model/table-presentation.test.ts +7 -6
  83. package/src/model/table-presentation.ts +54 -10
  84. package/src/types/table.ts +16 -11
  85. package/src/util/dynamic-table.ts +9 -6
  86. package/src/util/index.ts +1 -0
  87. package/src/util/schema.ts +36 -0
  88. package/src/util/table-buttons.ts +1 -24
  89. package/src/util/table.ts +13 -16
  90. package/src/util/tag.ts +33 -0
  91. package/dist/lib/browser/chunk-YWNRUF4J.mjs +0 -26
  92. package/dist/lib/browser/chunk-YWNRUF4J.mjs.map +0 -7
  93. package/dist/lib/node/chunk-TGG6OZ54.cjs.map +0 -7
  94. package/dist/lib/node-esm/chunk-KKLP54L3.mjs.map +0 -7
  95. package/dist/types/src/components/Table/RefPanel.d.ts +0 -9
  96. package/dist/types/src/components/Table/RefPanel.d.ts.map +0 -1
  97. package/src/components/Table/RefPanel.tsx +0 -52
@@ -7,12 +7,12 @@ import '@dxos-theme';
7
7
  import { type Meta, type StoryObj } from '@storybook/react';
8
8
  import React, { useEffect, useMemo, useState } from 'react';
9
9
 
10
- import { ImmutableSchema, type EchoSchema } from '@dxos/echo-schema';
10
+ import { type EchoSchema, isMutable } from '@dxos/echo-schema';
11
11
  import { invariant } from '@dxos/invariant';
12
12
  import { faker } from '@dxos/random';
13
- import { Filter, useQuery, create } from '@dxos/react-client/echo';
13
+ import { Filter, useQuery, live } from '@dxos/react-client/echo';
14
14
  import { useClientProvider, withClientProvider } from '@dxos/react-client/testing';
15
- import { defaultSizeRow, Grid, type GridEditing } from '@dxos/react-ui-grid';
15
+ import { defaultRowSize, Grid, type GridEditing } from '@dxos/react-ui-grid';
16
16
  import { ViewProjection, ViewType } from '@dxos/schema';
17
17
  import { withLayout, withTheme } from '@dxos/storybook-utils';
18
18
 
@@ -31,7 +31,7 @@ const DefaultStory = ({ editing }: StoryProps) => {
31
31
  const { space } = useClientProvider();
32
32
  invariant(space);
33
33
 
34
- const tables = useQuery(space, Filter.schema(TableType));
34
+ const tables = useQuery(space, Filter.type(TableType));
35
35
  const [table, setTable] = useState<TableType>();
36
36
  const [schema, setSchema] = useState<EchoSchema>();
37
37
  useEffect(() => {
@@ -53,15 +53,16 @@ const DefaultStory = ({ editing }: StoryProps) => {
53
53
  () => ({
54
54
  selection: { enabled: true, mode: 'multiple' },
55
55
  dataEditable: true,
56
- schemaEditable: !(schema instanceof ImmutableSchema),
56
+ schemaEditable: schema && isMutable(schema),
57
57
  }),
58
- [],
58
+ [schema],
59
59
  );
60
60
 
61
61
  const model = useTableModel({ table, projection, features });
62
62
 
63
63
  const handleQuery: TableCellEditorProps['onQuery'] = async ({ field }) => {
64
- const { objects } = await space.db.query(schema).run();
64
+ // TODO(dmaretskyi): If no schema query nothing
65
+ const { objects } = await space.db.query(schema ? Filter.type(schema) : Filter.everything()).run();
65
66
  return objects.map((obj) => {
66
67
  const label = obj[field.referencePath ?? 'id'];
67
68
  return {
@@ -76,7 +77,7 @@ const DefaultStory = ({ editing }: StoryProps) => {
76
77
  }
77
78
 
78
79
  return (
79
- <div className='flex w-[300px] border border-separator' style={{ height: defaultSizeRow }}>
80
+ <div className='flex w-[300px] border border-separator' style={{ height: defaultRowSize }}>
80
81
  <Grid.Root id='test' editing={editing}>
81
82
  <TableCellEditor model={model} onQuery={handleQuery} />
82
83
  </Grid.Root>
@@ -95,11 +96,11 @@ const meta: Meta<StoryProps> = {
95
96
  createIdentity: true,
96
97
  createSpace: true,
97
98
  onSpaceCreated: async ({ client, space }) => {
98
- const table = space.db.add(create(TableType, {}));
99
+ const table = space.db.add(live(TableType, {}));
99
100
  const schema = await initializeTable({ client, space, table });
100
101
  Array.from({ length: 10 }).forEach(() => {
101
102
  space.db.add(
102
- create(schema, {
103
+ live(schema, {
103
104
  name: faker.person.fullName(),
104
105
  }),
105
106
  );
@@ -107,7 +108,7 @@ const meta: Meta<StoryProps> = {
107
108
  },
108
109
  }),
109
110
  withTheme,
110
- withLayout({ tooltips: true }),
111
+ withLayout(),
111
112
  ],
112
113
  };
113
114
 
@@ -120,6 +121,7 @@ export const Default: Story = {
120
121
  editing: {
121
122
  index: 'grid,0,3',
122
123
  initialContent: 'Test',
124
+ cellElement: null,
123
125
  },
124
126
  },
125
127
  };
@@ -1,11 +1,12 @@
1
1
  //
2
- // Copyright 2024 DXOS.org
2
+ // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
5
  import { type Completion } from '@codemirror/autocomplete';
6
+ import { type Schema } from 'effect/Schema';
6
7
  import React, { useCallback, useMemo, useRef } from 'react';
7
8
 
8
- import { FormatEnum } from '@dxos/echo-schema';
9
+ import { FormatEnum, TypeEnum } from '@dxos/echo-schema';
9
10
  import { invariant } from '@dxos/invariant';
10
11
  import { type DxGrid } from '@dxos/lit-grid';
11
12
  import { useThemeContext } from '@dxos/react-ui';
@@ -26,6 +27,7 @@ import {
26
27
  import { tagPickerExtension, createLinks } from '@dxos/react-ui-tag-picker';
27
28
  import { type FieldProjection } from '@dxos/schema';
28
29
 
30
+ import { FormCellEditor } from './FormCellEditor';
29
31
  import { completion } from './extension';
30
32
  import { type TableModel, type ModalController } from '../../model';
31
33
 
@@ -43,11 +45,52 @@ export type QueryResult = Pick<Completion, 'label'> & { data: any };
43
45
  export type TableCellEditorProps = {
44
46
  model?: TableModel;
45
47
  modals?: ModalController;
48
+ schema?: Schema.AnyNoContext;
46
49
  onEnter?: (cell: DxGridPlanePosition) => void;
47
50
  onFocus?: DxGrid['refocus'];
48
51
  onQuery?: (field: FieldProjection, text: string) => Promise<QueryResult[]>;
49
52
  };
50
53
 
54
+ export const TableValueEditor = ({
55
+ model,
56
+ modals,
57
+ schema,
58
+ onEnter,
59
+ onFocus,
60
+ onQuery,
61
+ __gridScope,
62
+ }: GridScopedProps<TableCellEditorProps>) => {
63
+ const { editing } = useGridContext('TableValueEditor', __gridScope);
64
+
65
+ const fieldProjection = useMemo<FieldProjection | undefined>(() => {
66
+ if (!model || !editing) {
67
+ return;
68
+ }
69
+
70
+ const { col } = parseCellIndex(editing.index);
71
+ const field = model.projection.view.fields[col];
72
+ const fieldProjection = model.projection.getFieldProjection(field.id);
73
+ invariant(fieldProjection);
74
+ return fieldProjection;
75
+ }, [model, editing]);
76
+
77
+ if (fieldProjection?.props.type === TypeEnum.Array) {
78
+ return <FormCellEditor fieldProjection={fieldProjection} model={model} schema={schema} __gridScope={__gridScope} />;
79
+ }
80
+
81
+ // For all other types, use the existing cell editor
82
+ return (
83
+ <TableCellEditor
84
+ model={model}
85
+ modals={modals}
86
+ onEnter={onEnter}
87
+ onFocus={onFocus}
88
+ onQuery={onQuery}
89
+ __gridScope={__gridScope}
90
+ />
91
+ );
92
+ };
93
+
51
94
  export const TableCellEditor = ({
52
95
  model,
53
96
  modals,
@@ -261,11 +304,9 @@ export const TableCellEditor = ({
261
304
  }
262
305
  }
263
306
 
264
- // Return empty string if no value
265
307
  return '';
266
308
  }
267
309
 
268
- // Default behavior for other formats
269
310
  const value = model.getCellData(cell);
270
311
  return value !== undefined ? String(value) : '';
271
312
  }
@@ -2,19 +2,23 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import React, { useCallback, useMemo } from 'react';
5
+ import { Rx } from '@effect-rx/rx-react';
6
+ import React, { useMemo } from 'react';
6
7
 
7
- import { create } from '@dxos/live-object';
8
+ import { live } from '@dxos/live-object';
8
9
  import { type ThemedClassName } from '@dxos/react-ui';
9
10
  import {
11
+ type ActionGraphEdges,
12
+ type ActionGraphNodes,
13
+ type ActionGraphProps,
10
14
  createGapSeparator,
11
15
  createMenuAction,
12
16
  type MenuAction,
13
- type MenuActionHandler,
14
17
  MenuProvider,
15
18
  ToolbarMenu,
16
19
  useMenuActions,
17
20
  } from '@dxos/react-ui-menu';
21
+ import { rxFromSignal } from '@dxos/react-ui-menu';
18
22
 
19
23
  import { translationKey } from '../../translations';
20
24
 
@@ -27,45 +31,71 @@ export type TableToolbarActionType = 'add-row' | 'comment' | 'save-view';
27
31
  type TableToolbarState = Partial<{ viewDirty: boolean }>;
28
32
 
29
33
  export type TableToolbarProps = ThemedClassName<
30
- TableToolbarState & { onAction?: MenuActionHandler<TableToolbarAction>; attendableId?: string }
34
+ TableToolbarState & {
35
+ onAdd: () => void;
36
+ onSave: () => void;
37
+ attendableId?: string;
38
+ customActions?: Rx.Rx<ActionGraphProps>;
39
+ }
31
40
  >;
32
41
 
33
- const createTableToolbarActions = (state: TableToolbarState) => {
34
- const add = createMenuAction<TableToolbarActionProperties>('add-row', {
35
- type: 'add-row',
36
- icon: 'ph--plus--regular',
37
- label: ['add row', { ns: translationKey }],
38
- testId: 'table.toolbar.add-row',
42
+ const createTableToolbarActions = ({
43
+ state,
44
+ onAdd,
45
+ onSave,
46
+ customActions,
47
+ }: {
48
+ state: TableToolbarState;
49
+ onAdd: () => void;
50
+ onSave: () => void;
51
+ customActions?: Rx.Rx<ActionGraphProps>;
52
+ }) =>
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
+ });
68
+ const gap = createGapSeparator();
69
+ const nodes: ActionGraphNodes = [add, save, ...gap.nodes];
70
+ const edges: ActionGraphEdges = nodes.map(({ id: target }) => ({ source: 'root', target }));
71
+ if (customActions) {
72
+ const custom = get(customActions);
73
+ nodes.push(...custom.nodes);
74
+ edges.push(...custom.edges);
75
+ }
76
+ return {
77
+ nodes,
78
+ edges,
79
+ };
39
80
  });
40
- const save = createMenuAction<TableToolbarActionProperties>('save-view', {
41
- type: 'save-view',
42
- icon: 'ph--floppy-disk--regular',
43
- label: ['save view label', { ns: translationKey }],
44
- testId: 'table.toolbar.save-view',
45
- iconOnly: false,
46
- hidden: !state.viewDirty,
47
- });
48
- const gap = createGapSeparator();
49
- const comment = createMenuAction('comment', {
50
- type: 'comment',
51
- icon: 'ph--chat-text--regular',
52
- label: ['create comment', { ns: translationKey }],
53
- testId: 'table.toolbar.comment',
54
- });
55
- const nodes = [add, save, ...gap.nodes, comment];
56
- return {
57
- nodes,
58
- edges: nodes.map(({ id: target }) => ({ source: 'root', target })),
59
- };
60
- };
61
81
 
62
- export const TableToolbar = ({ classNames, viewDirty, attendableId, onAction }: TableToolbarProps) => {
63
- const state = useMemo(() => create<TableToolbarState>({ viewDirty }), []);
64
- const actionsCreator = useCallback(() => createTableToolbarActions(state), [state]);
82
+ export const TableToolbar = ({
83
+ classNames,
84
+ viewDirty,
85
+ attendableId,
86
+ onAdd,
87
+ onSave,
88
+ customActions,
89
+ }: TableToolbarProps) => {
90
+ const state = useMemo(() => live<TableToolbarState>({ viewDirty }), []);
91
+ const actionsCreator = useMemo(
92
+ () => createTableToolbarActions({ state, onAdd, onSave, customActions }),
93
+ [state, onAdd, onSave, customActions],
94
+ );
65
95
  const menu = useMenuActions(actionsCreator);
66
96
 
67
97
  return (
68
- <MenuProvider {...menu} attendableId={attendableId} onAction={onAction as MenuActionHandler}>
98
+ <MenuProvider {...menu} attendableId={attendableId}>
69
99
  <ToolbarMenu classNames={classNames} />
70
100
  </MenuProvider>
71
101
  );
@@ -2,25 +2,26 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
+ import { Schema } from 'effect';
5
6
  import { useEffect } from 'react';
6
7
 
7
- import { setValue, toJsonSchema, S, TypeEnum, TypedObject, FormatEnum } from '@dxos/echo-schema';
8
+ import { setValue, toJsonSchema, TypeEnum, TypedObject, FormatEnum, ObjectId } from '@dxos/echo-schema';
8
9
  import { faker } from '@dxos/random';
9
- import { create, makeRef } from '@dxos/react-client/echo';
10
+ import { live, makeRef } from '@dxos/react-client/echo';
10
11
  import { createView, type ViewProjection } from '@dxos/schema';
11
12
 
12
13
  import { TableType } from '../types';
13
14
 
14
15
  export const TestSchema = TypedObject({ typename: 'example.com/type/Test', version: '0.1.0' })({
15
- id: S.String,
16
- name: S.optional(S.String),
17
- age: S.optional(S.Number),
18
- active: S.optional(S.Boolean),
19
- netWorth: S.optional(S.Number),
16
+ id: ObjectId,
17
+ name: Schema.optional(Schema.String),
18
+ age: Schema.optional(Schema.Number),
19
+ active: Schema.optional(Schema.Boolean),
20
+ netWorth: Schema.optional(Schema.Number),
20
21
  });
21
22
 
22
23
  export const createTable = (schema = TestSchema) => {
23
- return create(TableType, {
24
+ return live(TableType, {
24
25
  view: makeRef(
25
26
  createView({
26
27
  name: 'Test',
@@ -32,7 +33,7 @@ export const createTable = (schema = TestSchema) => {
32
33
  };
33
34
 
34
35
  export const createItems = (n: number) => {
35
- const { data } = create({
36
+ const { data } = live({
36
37
  data: Array.from({ length: n }, () => ({
37
38
  name: faker.person.fullName(),
38
39
  age: faker.number.int({ min: 20, max: 70 }),
@@ -5,7 +5,7 @@
5
5
  import { effect } from '@preact/signals-core';
6
6
  import { useEffect, useState } from 'react';
7
7
 
8
- import { fullyQualifiedId, getSpace, type ReactiveObject } from '@dxos/react-client/echo';
8
+ import { fullyQualifiedId, getSpace, type Live } from '@dxos/react-client/echo';
9
9
  import { useSelectionActions } from '@dxos/react-ui-attention';
10
10
  import { type ViewProjection } from '@dxos/schema';
11
11
  import { isNonNullable } from '@dxos/util';
@@ -16,7 +16,7 @@ import { type TableType } from '../types';
16
16
  export type UseTableModelParams<T extends TableRow = TableRow> = {
17
17
  table?: TableType;
18
18
  projection?: ViewProjection;
19
- rows?: ReactiveObject<T>[];
19
+ rows?: Live<T>[];
20
20
  rowActions?: TableRowAction[];
21
21
  onSelectionChanged?: (selection: string[]) => void;
22
22
  onRowAction?: (actionId: string, data: T) => void;
@@ -70,7 +70,9 @@ export const useTableModel = <T extends TableRow = TableRow>({
70
70
  }
71
71
  }, [model, rows]);
72
72
 
73
- const { select, clear } = useSelectionActions([table?.id, table?.view?.target?.query.typename].filter(isNonNullable));
73
+ const { multiSelect, clear } = useSelectionActions(
74
+ [table?.id, table?.view?.target?.query.typename].filter(isNonNullable),
75
+ );
74
76
 
75
77
  useEffect(() => {
76
78
  if (!model) {
@@ -79,7 +81,7 @@ export const useTableModel = <T extends TableRow = TableRow>({
79
81
 
80
82
  const unsubscribe = effect(() => {
81
83
  const selectedItems = [...model.selection.selection.value];
82
- select(selectedItems);
84
+ multiSelect(selectedItems);
83
85
  onSelectionChanged?.(selectedItems);
84
86
  });
85
87
 
@@ -5,7 +5,7 @@
5
5
  import { signal } from '@preact/signals-core';
6
6
  import { type RefObject, type MutableRefObject } from 'react';
7
7
 
8
- import { type ReactiveObject } from '@dxos/live-object';
8
+ import { type Live } from '@dxos/live-object';
9
9
  import { log } from '@dxos/log';
10
10
 
11
11
  export type ColumnSettingsMode = { type: 'create' } | { type: 'edit'; fieldId: string };
@@ -18,7 +18,7 @@ export type ModalState =
18
18
  type: 'createRefPanel';
19
19
  typename: string;
20
20
  initialValues?: Record<string, string>;
21
- onCreate?: (obj: ReactiveObject<any>) => void;
21
+ onCreate?: (obj: Live<any>) => void;
22
22
  }
23
23
  | { type: 'columnSettings'; mode: ColumnSettingsMode }
24
24
  | { type: 'closed' };
@@ -72,7 +72,7 @@ export class ModalController {
72
72
  typename: string,
73
73
  anchorCell: Element | null,
74
74
  initialValues?: Record<string, string>,
75
- onCreate?: (obj: ReactiveObject<any>) => void,
75
+ onCreate?: (obj: Live<any>) => void,
76
76
  ) => {
77
77
  if (anchorCell) {
78
78
  this._triggerRef.current = anchorCell as HTMLElement;
@@ -43,7 +43,7 @@ export class SelectionModel<T extends TableRow> extends Resource {
43
43
  super();
44
44
  }
45
45
 
46
- protected override async _open() {
46
+ protected override async _open(): Promise<void> {
47
47
  const selectionWatcher = effect(() => {
48
48
  touch(this._selection.value);
49
49
  this._onSelectionChanged?.();
@@ -3,13 +3,14 @@
3
3
  //
4
4
 
5
5
  import { computed } from '@preact/signals-core';
6
+ import { Schema } from 'effect';
6
7
  import { afterEach, beforeEach, describe, expect, it } from 'vitest';
7
8
 
8
- import { S, TypedObject } from '@dxos/echo-schema';
9
+ import { TypedObject } from '@dxos/echo-schema';
9
10
  import { updateCounter } from '@dxos/echo-schema/testing';
10
11
  import { registerSignalsRuntime } from '@dxos/echo-signals';
11
12
  import { createEchoSchema } from '@dxos/live-object/testing';
12
- import { create, makeRef } from '@dxos/react-client/echo';
13
+ import { live, makeRef } from '@dxos/react-client/echo';
13
14
  import { createView, ViewProjection } from '@dxos/schema';
14
15
 
15
16
  import { TableModel, type TableModelProps } from './table-model';
@@ -69,7 +70,7 @@ describe('TableModel', () => {
69
70
 
70
71
  describe('reactivity', () => {
71
72
  it('pure signals should nest', () => {
72
- const signal$ = create({ arr: [{ thingInside: 1 }, { thingInside: 2 }] });
73
+ const signal$ = live({ arr: [{ thingInside: 1 }, { thingInside: 2 }] });
73
74
 
74
75
  const computed$ = computed(() => {
75
76
  return signal$.arr.map((row) =>
@@ -99,14 +100,14 @@ describe('TableModel', () => {
99
100
  });
100
101
 
101
102
  class Test extends TypedObject({ typename: 'example.com/type/Test', version: '0.1.0' })({
102
- title: S.String,
103
- completed: S.Boolean,
103
+ title: Schema.String,
104
+ completed: Schema.Boolean,
104
105
  }) {}
105
106
 
106
107
  const createTableModel = (props: Partial<TableModelProps> = {}): TableModel => {
107
108
  const schema = createEchoSchema(Test);
108
109
  const view = createView({ name: 'Test', typename: schema.typename, jsonSchema: schema.jsonSchema });
109
110
  const projection = new ViewProjection(schema.jsonSchema, view);
110
- const table = create(TableType, { view: makeRef(view) });
111
+ const table = live(TableType, { view: makeRef(view) });
111
112
  return new TableModel({ id: table.id, space: undefined, view, projection, ...props });
112
113
  };
@@ -8,7 +8,7 @@ import { type Space } from '@dxos/client/echo';
8
8
  import { Resource } from '@dxos/context';
9
9
  import { type FieldSortType, FormatEnum, getValue, setValue, type JsonProp } from '@dxos/echo-schema';
10
10
  import { invariant } from '@dxos/invariant';
11
- import { isReactiveObject, makeRef } from '@dxos/live-object';
11
+ import { isLiveObject, makeRef } from '@dxos/live-object';
12
12
  import { formatForEditing, parseValue } from '@dxos/react-ui-form';
13
13
  import {
14
14
  type DxGridAxisMeta,
@@ -21,6 +21,7 @@ import { type ViewType, type ViewProjection } from '@dxos/schema';
21
21
  import { type SelectionMode, SelectionModel } from './selection-model';
22
22
  import { TableSorting } from './table-sorting';
23
23
  import { touch } from '../util';
24
+ import { extractTagIds } from '../util/tag';
24
25
 
25
26
  // TODO(burdon): Use schema types.
26
27
  export type TableRow = Record<JsonProp, any> & { id: string };
@@ -201,7 +202,7 @@ export class TableModel<T extends TableRow = TableRow> extends Resource {
201
202
  // Initialisation
202
203
  //
203
204
 
204
- protected override async _open() {
205
+ protected override async _open(): Promise<void> {
205
206
  this.initializeColumnMeta();
206
207
  this.initializeEffects();
207
208
  this._selection = new SelectionModel(this._sorting.sortedRows, this._features.selection.mode ?? 'multiple', () =>
@@ -342,12 +343,28 @@ export class TableModel<T extends TableRow = TableRow> extends Resource {
342
343
  case FormatEnum.Ref: {
343
344
  // TODO(ZaymonFC): This get's called an additional time by the cell editor onBlur, but with the cell editors
344
345
  // plain string value. Maybe onBlur should be called with the actual value?
345
- if (isReactiveObject(value)) {
346
+ if (isLiveObject(value)) {
346
347
  setValue(this._rows.value[rowIdx], field.path, makeRef(value));
347
348
  }
348
349
  break;
349
350
  }
350
351
 
352
+ case FormatEnum.SingleSelect: {
353
+ const ids = extractTagIds(value);
354
+ if (ids && ids.length > 0) {
355
+ setValue(this._rows.value[rowIdx], field.path, ids[0]);
356
+ }
357
+ break;
358
+ }
359
+
360
+ case FormatEnum.MultiSelect: {
361
+ const ids = extractTagIds(value);
362
+ if (ids) {
363
+ setValue(this._rows.value[rowIdx], field.path, ids);
364
+ }
365
+ break;
366
+ }
367
+
351
368
  default: {
352
369
  setValue(
353
370
  this._rows.value[rowIdx],
@@ -2,10 +2,11 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
+ import { Schema } from 'effect';
5
6
  import { describe, expect, it, beforeEach } from 'vitest';
6
7
 
7
- import { S, TypedObject } from '@dxos/echo-schema';
8
- import { create, makeRef } from '@dxos/live-object';
8
+ import { TypedObject } from '@dxos/echo-schema';
9
+ import { live, makeRef } from '@dxos/live-object';
9
10
  import { createEchoSchema } from '@dxos/live-object/testing';
10
11
  import { createView, ViewProjection } from '@dxos/schema';
11
12
 
@@ -22,7 +23,7 @@ describe('TablePresentation', () => {
22
23
 
23
24
  beforeEach(async () => {
24
25
  updateCount = 0;
25
- ({ data } = create({
26
+ ({ data } = live({
26
27
  data: [
27
28
  { col1: 'A', col2: 1, col3: true },
28
29
  { col1: 'B', col2: 2, col3: false },
@@ -89,14 +90,14 @@ describe('TablePresentation', () => {
89
90
  });
90
91
 
91
92
  class Test extends TypedObject({ typename: 'example.com/type/Test', version: '0.1.0' })({
92
- title: S.String,
93
- completed: S.Boolean,
93
+ title: Schema.String,
94
+ completed: Schema.Boolean,
94
95
  }) {}
95
96
 
96
97
  const createTableModel = (props: Partial<TableModelProps> = {}): TableModel => {
97
98
  const schema = createEchoSchema(Test);
98
99
  const view = createView({ name: 'Test', typename: schema.typename, jsonSchema: schema.jsonSchema });
99
100
  const projection = new ViewProjection(schema.jsonSchema, view);
100
- const table = create(TableType, { view: makeRef(view) });
101
+ const table = live(TableType, { view: makeRef(view) });
101
102
  return new TableModel({ id: table.id, view, projection, ...props });
102
103
  };