@dxos/react-ui-table 0.8.4-main.fcfe5033a5 → 0.8.4-staging.60fe92afc8

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 (115) hide show
  1. package/LICENSE +102 -5
  2. package/dist/lib/browser/{chunk-F7Y374KL.mjs → chunk-4KOQTPFC.mjs} +62 -111
  3. package/dist/lib/browser/chunk-4KOQTPFC.mjs.map +7 -0
  4. package/dist/lib/browser/{chunk-BF54SSNL.mjs → chunk-62PYOJPY.mjs} +13 -16
  5. package/dist/lib/browser/chunk-62PYOJPY.mjs.map +7 -0
  6. package/dist/lib/browser/chunk-J5LGTIGS.mjs +10 -0
  7. package/dist/lib/browser/chunk-J5LGTIGS.mjs.map +7 -0
  8. package/dist/lib/browser/index.mjs +57 -131
  9. package/dist/lib/browser/index.mjs.map +4 -4
  10. package/dist/lib/browser/meta.json +1 -1
  11. package/dist/lib/browser/testing/index.mjs +9 -8
  12. package/dist/lib/browser/testing/index.mjs.map +3 -3
  13. package/dist/lib/browser/translations.mjs +36 -0
  14. package/dist/lib/browser/translations.mjs.map +7 -0
  15. package/dist/lib/browser/types/index.mjs +2 -1
  16. package/dist/lib/node-esm/{chunk-SIRDIPHZ.mjs → chunk-7LMOZUTS.mjs} +13 -16
  17. package/dist/lib/node-esm/chunk-7LMOZUTS.mjs.map +7 -0
  18. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +11 -0
  19. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs.map +7 -0
  20. package/dist/lib/node-esm/{chunk-CCJ4LOVM.mjs → chunk-KOSV2J5N.mjs} +62 -111
  21. package/dist/lib/node-esm/chunk-KOSV2J5N.mjs.map +7 -0
  22. package/dist/lib/node-esm/index.mjs +57 -131
  23. package/dist/lib/node-esm/index.mjs.map +4 -4
  24. package/dist/lib/node-esm/meta.json +1 -1
  25. package/dist/lib/node-esm/testing/index.mjs +9 -8
  26. package/dist/lib/node-esm/testing/index.mjs.map +3 -3
  27. package/dist/lib/node-esm/translations.mjs +37 -0
  28. package/dist/lib/node-esm/translations.mjs.map +7 -0
  29. package/dist/lib/node-esm/types/index.mjs +2 -1
  30. package/dist/types/src/components/Table/ColumnActionsMenu.d.ts.map +1 -1
  31. package/dist/types/src/components/Table/ColumnSettings.d.ts +2 -2
  32. package/dist/types/src/components/Table/ColumnSettings.d.ts.map +1 -1
  33. package/dist/types/src/components/Table/DynamicTable.d.ts +3 -4
  34. package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
  35. package/dist/types/src/components/Table/DynamicTable.stories.d.ts +21 -20
  36. package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
  37. package/dist/types/src/components/Table/Relations.stories.d.ts +87 -81
  38. package/dist/types/src/components/Table/Relations.stories.d.ts.map +1 -1
  39. package/dist/types/src/components/Table/RowActionsMenu.d.ts.map +1 -1
  40. package/dist/types/src/components/Table/Table.d.ts +2 -2
  41. package/dist/types/src/components/Table/Table.d.ts.map +1 -1
  42. package/dist/types/src/components/Table/Table.stories.d.ts +88 -81
  43. package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
  44. package/dist/types/src/components/Table/TableContent.d.ts +2 -1
  45. package/dist/types/src/components/Table/TableContent.d.ts.map +1 -1
  46. package/dist/types/src/components/Table/TableToolbar.d.ts +1 -1
  47. package/dist/types/src/components/Table/TableToolbar.d.ts.map +1 -1
  48. package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts.map +1 -1
  49. package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +1 -2
  50. package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
  51. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
  52. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts +19 -19
  53. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
  54. package/dist/types/src/components/testing.d.ts.map +1 -1
  55. package/dist/types/src/hooks/useAddRow.d.ts.map +1 -1
  56. package/dist/types/src/hooks/useProjectionModel.d.ts.map +1 -1
  57. package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
  58. package/dist/types/src/index.d.ts +0 -2
  59. package/dist/types/src/index.d.ts.map +1 -1
  60. package/dist/types/src/model/modal-controller.d.ts.map +1 -1
  61. package/dist/types/src/model/selection-model.d.ts +1 -1
  62. package/dist/types/src/model/selection-model.d.ts.map +1 -1
  63. package/dist/types/src/model/table-model.d.ts +7 -7
  64. package/dist/types/src/model/table-model.d.ts.map +1 -1
  65. package/dist/types/src/model/table-presentation.d.ts.map +1 -1
  66. package/dist/types/src/playwright/TableManager.d.ts.map +1 -1
  67. package/dist/types/src/playwright/playwright.config.d.ts.map +1 -1
  68. package/dist/types/src/testing/useTestTableModel.d.ts +1 -1
  69. package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -1
  70. package/dist/types/src/translations.d.ts +19 -19
  71. package/dist/types/src/translations.d.ts.map +1 -1
  72. package/dist/types/src/types/Table.d.ts +12 -6
  73. package/dist/types/src/types/Table.d.ts.map +1 -1
  74. package/dist/types/src/util/dynamic-table.d.ts +6 -6
  75. package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
  76. package/dist/types/src/util/query-ast.d.ts.map +1 -1
  77. package/dist/types/src/util/schema.d.ts.map +1 -1
  78. package/dist/types/src/util/signals.d.ts.map +1 -1
  79. package/dist/types/src/util/sort.d.ts.map +1 -1
  80. package/dist/types/src/util/table-buttons.d.ts +20 -20
  81. package/dist/types/src/util/table-buttons.d.ts.map +1 -1
  82. package/dist/types/src/util/table-controls.d.ts +4 -4
  83. package/dist/types/src/util/table-controls.d.ts.map +1 -1
  84. package/dist/types/src/util/tag.d.ts.map +1 -1
  85. package/dist/types/tsconfig.tsbuildinfo +1 -1
  86. package/package.json +53 -63
  87. package/src/components/Table/ColumnActionsMenu.tsx +2 -1
  88. package/src/components/Table/ColumnSettings.tsx +2 -2
  89. package/src/components/Table/DynamicTable.stories.tsx +9 -8
  90. package/src/components/Table/DynamicTable.tsx +11 -12
  91. package/src/components/Table/Relations.stories.tsx +46 -9
  92. package/src/components/Table/RowActionsMenu.tsx +3 -2
  93. package/src/components/Table/Table.stories.tsx +86 -33
  94. package/src/components/Table/TableContent.tsx +9 -4
  95. package/src/components/Table/TableToolbar.tsx +2 -2
  96. package/src/components/TableCellEditor/CellValidationMessage.tsx +1 -2
  97. package/src/components/TableCellEditor/FormCellEditor.tsx +8 -7
  98. package/src/components/TableCellEditor/TableCellEditor.stories.tsx +7 -4
  99. package/src/components/TableCellEditor/TableCellEditor.tsx +8 -6
  100. package/src/components/testing.ts +7 -12
  101. package/src/hooks/useProjectionModel.ts +5 -6
  102. package/src/hooks/useTableModel.ts +1 -1
  103. package/src/index.ts +0 -4
  104. package/src/model/table-model.test.ts +4 -9
  105. package/src/model/table-model.ts +43 -36
  106. package/src/model/table-presentation.test.ts +4 -9
  107. package/src/model/table-presentation.ts +15 -15
  108. package/src/testing/useTestTableModel.ts +6 -7
  109. package/src/types/Table.ts +16 -15
  110. package/src/util/dynamic-table.test.ts +13 -20
  111. package/src/util/dynamic-table.ts +19 -18
  112. package/dist/lib/browser/chunk-BF54SSNL.mjs.map +0 -7
  113. package/dist/lib/browser/chunk-F7Y374KL.mjs.map +0 -7
  114. package/dist/lib/node-esm/chunk-CCJ4LOVM.mjs.map +0 -7
  115. package/dist/lib/node-esm/chunk-SIRDIPHZ.mjs.map +0 -7
@@ -5,24 +5,26 @@
5
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
6
6
  import * as Schema from 'effect/Schema';
7
7
  import React, { useCallback } from 'react';
8
+ import { expect, userEvent, waitFor, within } from 'storybook/test';
8
9
 
9
- import { Annotation, type Database, Format, Obj, type QueryAST, Ref, Type } from '@dxos/echo';
10
- import { View } from '@dxos/echo';
10
+ import { DXN, Annotation, type Database, Format, Obj, type QueryAST, Ref, Type, View } from '@dxos/echo';
11
11
  import { type Mutable, PropertyMetaAnnotationId } from '@dxos/echo/internal';
12
12
  import { invariant } from '@dxos/invariant';
13
13
  import { random } from '@dxos/random';
14
14
  import { PublicKey } from '@dxos/react-client';
15
15
  import { withClientProvider } from '@dxos/react-client/testing';
16
16
  import { Panel, ScrollArea } from '@dxos/react-ui';
17
- import { ViewEditor, translations as formTranslations } from '@dxos/react-ui-form';
18
- import { SyntaxHighlighter } from '@dxos/react-ui-syntax-highlighter';
17
+ import { ViewEditor } from '@dxos/react-ui-form';
18
+ import { translations as formTranslations } from '@dxos/react-ui-form/translations';
19
+ import { JsonHighlighter } from '@dxos/react-ui-syntax-highlighter';
19
20
  import { withLayout, withTheme } from '@dxos/react-ui/testing';
20
- import { ViewModel, getSchemaFromPropertyDefinitions, getTypenameFromQuery } from '@dxos/schema';
21
+ import { ViewModel, getSchemaFromPropertyDefinitions } from '@dxos/schema';
21
22
  import { TestSchema, createObjectFactory } from '@dxos/schema/testing';
22
23
  import { withRegistry } from '@dxos/storybook-utils';
23
24
 
25
+ import { translations } from '#translations';
26
+
24
27
  import { useTestTableModel } from '../../testing';
25
- import { translations } from '../../translations';
26
28
  import { Table } from '../../types';
27
29
  import { Table as TableComponent } from './Table';
28
30
 
@@ -51,10 +53,11 @@ const Example = Schema.Struct({
51
53
  title: 'Parent',
52
54
  }),
53
55
  }).pipe(
54
- Type.object({ typename: `com.example.type.${PublicKey.random().truncate()}`, version: '0.1.0' }),
55
56
  Annotation.LabelAnnotation.set(['name']),
57
+ // NSID last segment must start with a letter (DXN spec), so prefix the random hex.
58
+ Type.makeObject(DXN.make(`com.example.type.example${PublicKey.random().truncate()}`, '0.1.0')),
56
59
  );
57
- interface Example extends Schema.Schema.Type<typeof Example> {}
60
+ interface Example extends Type.InstanceType<typeof Example> {}
58
61
 
59
62
  const StoryViewEditor = ({
60
63
  view,
@@ -63,17 +66,16 @@ const StoryViewEditor = ({
63
66
  handleDeleteColumn,
64
67
  }: {
65
68
  view?: View.View;
66
- schema?: Schema.Schema.AnyNoContext;
69
+ schema?: Type.AnyEntity;
67
70
  db?: Database.Database;
68
71
  handleDeleteColumn: (fieldId: string) => void;
69
72
  }) => {
70
73
  const handleQueryChanged = useCallback(
71
74
  (newQuery: QueryAST.Query) => {
72
75
  invariant(schema);
73
- invariant(Type.isMutable(schema));
74
- schema.updateTypename(getTypenameFromQuery(newQuery));
76
+ invariant(Type.getDatabase(schema) != null);
75
77
  invariant(view);
76
- Obj.change(view, (view) => {
78
+ Obj.update(view, (view) => {
77
79
  view.query.ast = newQuery as Mutable<typeof newQuery>;
78
80
  });
79
81
  },
@@ -86,8 +88,8 @@ const StoryViewEditor = ({
86
88
 
87
89
  return (
88
90
  <ViewEditor
89
- registry={db?.schemaRegistry}
90
- schema={schema}
91
+ registry={db?.graph.registry}
92
+ type={schema}
91
93
  view={view}
92
94
  onQueryChanged={handleQueryChanged}
93
95
  onDelete={handleDeleteColumn}
@@ -141,9 +143,7 @@ const DefaultStory = () => {
141
143
  <ScrollArea.Root orientation='vertical' classNames='border-l border-separator'>
142
144
  <ScrollArea.Viewport>
143
145
  <StoryViewEditor view={table.view.target} schema={schema} db={db} handleDeleteColumn={handleDeleteColumn} />
144
- <SyntaxHighlighter language='json' className='text-xs'>
145
- {JSON.stringify({ view: table.view.target, schema }, null, 2)}
146
- </SyntaxHighlighter>
146
+ <JsonHighlighter data={{ view: table.view.target, schema }} classNames='text-xs' />
147
147
  </ScrollArea.Viewport>
148
148
  </ScrollArea.Root>
149
149
  </div>
@@ -170,10 +170,13 @@ const meta = {
170
170
  createIdentity: true,
171
171
  createSpace: true,
172
172
  onCreateSpace: async ({ space }) => {
173
- const [schema] = await space.db.schemaRegistry.register([Example]);
174
- const { view, jsonSchema } = await ViewModel.makeFromDatabase({ db: space.db, typename: schema.typename });
173
+ const type = await space.db.addType(Example);
174
+ const { view, jsonSchema } = await ViewModel.makeFromDatabase({
175
+ db: space.db,
176
+ typename: Type.getTypename(type),
177
+ });
175
178
  const table = Table.make({ view, jsonSchema });
176
- Obj.change(view, (view) => {
179
+ Obj.update(view, (view) => {
177
180
  view.projection.fields = [
178
181
  view.projection.fields.find((field) => field.path === 'name')!,
179
182
  ...view.projection.fields.filter((field) => field.path !== 'name'),
@@ -184,7 +187,7 @@ const meta = {
184
187
 
185
188
  Array.from({ length: 10 }).map(() => {
186
189
  return space.db.add(
187
- Obj.make(schema, {
190
+ Obj.make(type, {
188
191
  name: random.lorem.sentence(),
189
192
  status: random.helpers.arrayElement(['todo', 'in-progress', 'done'] as const),
190
193
  description: random.lorem.paragraph(),
@@ -219,7 +222,7 @@ export const StaticSchema: StoryObj = {
219
222
  onCreateSpace: async ({ space }) => {
220
223
  const { view, jsonSchema } = await ViewModel.makeFromDatabase({
221
224
  db: space.db,
222
- typename: TestSchema.Person.typename,
225
+ typename: Type.getTypename(TestSchema.Person),
223
226
  });
224
227
  const table = Table.make({ view, jsonSchema });
225
228
  space.db.add(table);
@@ -248,12 +251,7 @@ const ContactWithArrayOfEmails = Schema.Struct({
248
251
  }),
249
252
  ),
250
253
  ),
251
- }).pipe(
252
- Type.object({
253
- typename: 'org.dxos.type.contactWithArrayOfEmails',
254
- version: '0.1.0',
255
- }),
256
- );
254
+ }).pipe(Type.makeObject(DXN.make('org.dxos.type.contactWithArrayOfEmails', '0.1.0')));
257
255
 
258
256
  export const ArrayOfObjects: StoryObj = {
259
257
  render: DefaultStory,
@@ -265,7 +263,7 @@ export const ArrayOfObjects: StoryObj = {
265
263
  onCreateSpace: async ({ space }) => {
266
264
  const { view, jsonSchema } = await ViewModel.makeFromDatabase({
267
265
  db: space.db,
268
- typename: ContactWithArrayOfEmails.typename,
266
+ typename: Type.getTypename(ContactWithArrayOfEmails),
269
267
  });
270
268
  const table = Table.make({ view, jsonSchema });
271
269
  space.db.add(table);
@@ -285,6 +283,61 @@ export const ArrayOfObjects: StoryObj = {
285
283
  },
286
284
  };
287
285
 
286
+ export const RequiredSchema: StoryObj = {
287
+ render: DefaultStory,
288
+ decorators: [
289
+ withClientProvider({
290
+ types: [View.View, Table.Table, TestSchema.Person],
291
+ createIdentity: true,
292
+ createSpace: true,
293
+ onCreateSpace: async ({ space }) => {
294
+ // TestSchema.Person has a required `name: Schema.String` field.
295
+ // No pre-populated rows so the add-row flow is the first interaction.
296
+ const { view, jsonSchema } = await ViewModel.makeFromDatabase({
297
+ db: space.db,
298
+ typename: Type.getTypename(TestSchema.Person),
299
+ });
300
+ const table = Table.make({ view, jsonSchema });
301
+ space.db.add(table);
302
+ },
303
+ }),
304
+ ],
305
+ parameters: {
306
+ layout: 'fullscreen',
307
+ translations: [...translations, ...formTranslations],
308
+ },
309
+ play: async ({ canvasElement }) => {
310
+ const canvas = within(canvasElement);
311
+
312
+ // 30 s covers ECHO client + space + table creation (the only inherently slow step).
313
+ const addRowButton = await canvas.findByTestId('table.toolbar.add-row', undefined, { timeout: 30_000 });
314
+
315
+ // Person.name is required, so db.add throws → useAddRow returns 'draft' →
316
+ // a draft row appears in frozenRowsEnd and focus is set to its first cell.
317
+ await userEvent.click(addRowButton);
318
+
319
+ const draftCell = await canvas.findByTestId('frozenRowsEnd.0.0');
320
+ await userEvent.click(draftCell);
321
+
322
+ // Open the editor with Enter rather than typing to open — the latter routes the first
323
+ // character into dx-grid's `initialContent` and races the editor mount. The empty value
324
+ // fails validation; the editor must stay open so the value below can be entered.
325
+ await userEvent.keyboard('{Enter}');
326
+ await canvas.findByTestId('grid.cell-editor');
327
+
328
+ // The editor is focused (autoFocus); type the required value and commit.
329
+ await userEvent.keyboard('Alice');
330
+ await userEvent.keyboard('{Enter}');
331
+
332
+ // The draft row is committed and appears in the grid with the typed value.
333
+ await waitFor(async () => {
334
+ const cell = canvas.getByTestId('grid.0.0');
335
+ const text = cell.querySelector('.dx-grid__cell__content')?.textContent;
336
+ await expect(text).toBe('Alice');
337
+ });
338
+ },
339
+ };
340
+
288
341
  export const Tags: Meta<DefaultStoryProps> = {
289
342
  title: 'ui/react-ui-table/Table',
290
343
  render: DefaultStory,
@@ -295,7 +348,7 @@ export const Tags: Meta<DefaultStoryProps> = {
295
348
  createSpace: true,
296
349
  onCreateSpace: async ({ space }) => {
297
350
  // Configure schema.
298
- const typename = 'com.example.type.single-select';
351
+ const typename = 'com.example.type.singleSelect';
299
352
  const selectOptions = [
300
353
  { id: 'one', title: 'One', color: 'emerald' },
301
354
  { id: 'two', title: 'Two', color: 'blue' },
@@ -306,7 +359,7 @@ export const Tags: Meta<DefaultStoryProps> = {
306
359
 
307
360
  const selectOptionIds = selectOptions.map((o) => o.id);
308
361
 
309
- const schema = getSchemaFromPropertyDefinitions(typename, [
362
+ const type = getSchemaFromPropertyDefinitions(typename, [
310
363
  {
311
364
  name: 'single',
312
365
  format: Format.TypeFormat.SingleSelect,
@@ -318,7 +371,7 @@ export const Tags: Meta<DefaultStoryProps> = {
318
371
  config: { options: selectOptions },
319
372
  },
320
373
  ]);
321
- const [storedSchema] = await space.db.schemaRegistry.register([schema]);
374
+ const storedType = await space.db.addType(type);
322
375
 
323
376
  // Initialize table.
324
377
  const { view, jsonSchema } = await ViewModel.makeFromDatabase({ db: space.db, typename });
@@ -328,7 +381,7 @@ export const Tags: Meta<DefaultStoryProps> = {
328
381
  // Populate.
329
382
  Array.from({ length: 10 }).map(() => {
330
383
  return space.db.add(
331
- Obj.make(storedSchema, {
384
+ Obj.make(Type.assertObject(storedType), {
332
385
  single: random.helpers.arrayElement([...selectOptionIds, undefined]),
333
386
  multiple: random.helpers.randomSubset(selectOptionIds),
334
387
  }),
@@ -8,6 +8,7 @@ import React, { type MouseEvent, type WheelEvent, useCallback, useContext, useEf
8
8
 
9
9
  import { type Type } from '@dxos/echo';
10
10
  import { log } from '@dxos/log';
11
+ import { composable, composableProps } from '@dxos/react-ui';
11
12
  import { useAttention } from '@dxos/react-ui-attention';
12
13
  import {
13
14
  type DxGridAxisMeta,
@@ -21,7 +22,7 @@ import {
21
22
  gridSeparatorInlineEnd,
22
23
  } from '@dxos/react-ui-grid';
23
24
  import { DxEditRequest } from '@dxos/react-ui-grid';
24
- import { composable, composableProps, mx } from '@dxos/ui-theme';
25
+ import { mx } from '@dxos/ui-theme';
25
26
 
26
27
  import { type InsertRowResult, ModalController, type TableModel, type TablePresentation } from '../../model';
27
28
  import { tableButtons, tableControls } from '../../util';
@@ -49,16 +50,20 @@ export type TableContentProps = {
49
50
  presentation?: TablePresentation;
50
51
  // TODO(burdon): Factor out attention.
51
52
  ignoreAttention?: boolean;
53
+ attendableId?: string;
52
54
  onCreate?: OnCreateHandler;
53
55
  onRowClick?: (row: any) => void;
54
56
  testId?: string;
55
57
  };
56
58
 
57
59
  export const TableContent = composable<HTMLDivElement, TableContentProps>(
58
- ({ schema, model, presentation, ignoreAttention, onCreate, onRowClick, testId, ...props }, forwardedRef) => {
60
+ (
61
+ { schema, model, presentation, ignoreAttention, attendableId, onCreate, onRowClick, testId, ...props },
62
+ forwardedRef,
63
+ ) => {
59
64
  const registry = useContext(RegistryContext);
60
65
  const [dxGrid, setDxGrid] = useState<DxGridElement | null>(null);
61
- const { hasAttention } = useAttention(model?.id ?? 'table');
66
+ const { hasAttention } = useAttention(attendableId ?? model?.id ?? 'table');
62
67
  const modals = useMemo(() => new ModalController(registry), [registry]);
63
68
  const columnMeta = useAtomValue(model?.columnMeta ?? emptyColumnMeta);
64
69
  // Subscribe to rows atom to trigger re-render when rows change.
@@ -379,7 +384,7 @@ export const TableContent = composable<HTMLDivElement, TableContentProps>(
379
384
  }, [dxGrid, model, presentation]);
380
385
 
381
386
  if (!model || !modals) {
382
- return <span role='none' className='dx-attention-surface' />;
387
+ return <span className='dx-attention-surface' />;
383
388
  }
384
389
 
385
390
  return (
@@ -5,6 +5,7 @@
5
5
  import { Atom, RegistryContext } from '@effect-atom/atom-react';
6
6
  import React, { useContext, useEffect, useMemo } from 'react';
7
7
 
8
+ import { composable, composableProps } from '@dxos/react-ui';
8
9
  import {
9
10
  type ActionGraphEdges,
10
11
  type ActionGraphNodes,
@@ -16,9 +17,8 @@ import {
16
17
  createMenuAction,
17
18
  useMenuActions,
18
19
  } from '@dxos/react-ui-menu';
19
- import { composable, composableProps } from '@dxos/ui-theme';
20
20
 
21
- import { translationKey } from '../../translations';
21
+ import { translationKey } from '#translations';
22
22
 
23
23
  export type TableToolbarActionProperties = { type: TableToolbarActionType };
24
24
 
@@ -23,11 +23,10 @@ export const CellValidationMessage = ({
23
23
  }
24
24
 
25
25
  const bgClass = variant === 'error' ? 'bg-error-surface' : 'bg-warning-surface';
26
- const textClass = variant === 'error' ? 'text-error-surface-text' : 'text-warning-surface-text';
26
+ const textClass = variant === 'error' ? 'text-error-fg' : 'text-warning-fg';
27
27
 
28
28
  return (
29
29
  <div
30
- role='none'
31
30
  className={`absolute ${bgClass} ${textClass} rounded-h-sm text-xs p-1`}
32
31
  style={{
33
32
  ...{ '--dx-grid-cell-width': `${box?.inlineSize ?? 200}px` },
@@ -5,8 +5,8 @@
5
5
  import type * as Schema from 'effect/Schema';
6
6
  import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
7
7
 
8
- import { Entity, type Type } from '@dxos/echo';
9
- import { Ref, getValue } from '@dxos/echo/internal';
8
+ import { Entity, Ref, Type } from '@dxos/echo';
9
+ import { SchemaEx } from '@dxos/effect';
10
10
  import { invariant } from '@dxos/invariant';
11
11
  import { type Label, Popover } from '@dxos/react-ui';
12
12
  import { Form, type FormRootProps, type RefFieldProps } from '@dxos/react-ui-form';
@@ -14,8 +14,9 @@ import { parseCellIndex, useGridContext } from '@dxos/react-ui-grid';
14
14
  import { type FieldProjection } from '@dxos/schema';
15
15
  import { getDeep, isTruthy, setDeep } from '@dxos/util';
16
16
 
17
+ import { translationKey } from '#translations';
18
+
17
19
  import { type ModalController, type TableModel, type TableRow } from '../../model';
18
- import { translationKey } from '../../translations';
19
20
  import { narrowSchema } from '../../util';
20
21
 
21
22
  const createOptionLabel: Label = ['create-new-object.label', { ns: translationKey }];
@@ -62,7 +63,7 @@ export const FormCellEditor = <T extends Type.AnyEntity = Type.AnyEntity>({
62
63
  return undefined;
63
64
  }
64
65
 
65
- return narrowSchema(schema, [fieldProjection.field.path]);
66
+ return narrowSchema(Type.getSchema(schema), [fieldProjection.field.path]);
66
67
  }, [JSON.stringify(schema), fieldProjection.field.path]); // TODO(burdon): Avoid stringify.
67
68
 
68
69
  const originalRow = useMemo<TableRow | undefined>(() => {
@@ -150,8 +151,8 @@ export const FormCellEditor = <T extends Type.AnyEntity = Type.AnyEntity>({
150
151
  results
151
152
  .map((obj) => {
152
153
  return {
153
- id: Entity.getDXN(obj).toString(),
154
- label: getValue(obj, fieldProjection.field.referencePath!) || obj.id.toString(),
154
+ id: Entity.getURI(obj),
155
+ label: SchemaEx.getValue(obj, fieldProjection.field.referencePath!) || obj.id.toString(),
155
156
  };
156
157
  })
157
158
  .filter(isTruthy),
@@ -166,7 +167,7 @@ export const FormCellEditor = <T extends Type.AnyEntity = Type.AnyEntity>({
166
167
  <Popover.Root open={editing} onOpenChange={handleOpenChange}>
167
168
  <Popover.VirtualTrigger virtualRef={anchorRef} />
168
169
  <Popover.Portal>
169
- <Popover.Content tabIndex={-1} classNames='dx-card-popover-width dx-density-fine'>
170
+ <Popover.Content tabIndex={-1} classNames='dx-card-popover-width dx-density-md'>
170
171
  <Popover.Arrow />
171
172
  <Popover.Viewport>
172
173
  <Form.Root
@@ -5,8 +5,7 @@
5
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
6
6
  import React from 'react';
7
7
 
8
- import { Obj } from '@dxos/echo';
9
- import { View } from '@dxos/echo';
8
+ import { Obj, Type, View } from '@dxos/echo';
10
9
  import { random } from '@dxos/random';
11
10
  import { withClientProvider } from '@dxos/react-client/testing';
12
11
  import { Grid, type GridEditing, defaultRowSize } from '@dxos/react-ui-grid';
@@ -14,8 +13,9 @@ import { withLayout, withTheme } from '@dxos/react-ui/testing';
14
13
  import { ViewModel } from '@dxos/schema';
15
14
  import { Task } from '@dxos/types';
16
15
 
16
+ import { translations } from '#translations';
17
+
17
18
  import { useTestTableModel } from '../../testing';
18
- import { translations } from '../../translations';
19
19
  import { Table } from '../../types';
20
20
  import { TableCellEditor } from './TableCellEditor';
21
21
 
@@ -52,7 +52,10 @@ const meta = {
52
52
  createIdentity: true,
53
53
  createSpace: true,
54
54
  onCreateSpace: async ({ space }) => {
55
- const { view, jsonSchema } = await ViewModel.makeFromDatabase({ db: space.db, typename: Task.Task.typename });
55
+ const { view, jsonSchema } = await ViewModel.makeFromDatabase({
56
+ db: space.db,
57
+ typename: Type.getTypename(Task.Task),
58
+ });
56
59
  const table = Table.make({ view, jsonSchema });
57
60
  space.db.add(table);
58
61
  Array.from({ length: 10 }).forEach(() => {
@@ -8,7 +8,7 @@ import React, { useCallback, useMemo, useRef, useState } from 'react';
8
8
 
9
9
  import { debounce } from '@dxos/async';
10
10
  import { type Type } from '@dxos/echo';
11
- import { Format, TypeEnum } from '@dxos/echo/internal';
11
+ import { Format, TypeEnum } from '@dxos/echo/Format';
12
12
  import { invariant } from '@dxos/invariant';
13
13
  import { type DxGridAxis, type DxGridPosition } from '@dxos/lit-grid';
14
14
  import {
@@ -90,6 +90,12 @@ export const TableValueEditor = <T extends Type.AnyEntity = Type.AnyEntity>({
90
90
  return <TableCellEditor model={model} modals={modals} onFocus={onFocus} onSave={onSave} __gridScope={__gridScope} />;
91
91
  };
92
92
 
93
+ const cellEditorSlots: GridCellEditorProps['slots'] = {
94
+ content: {
95
+ className: '!py-(--dx-grid-cell-editor-padding-block)',
96
+ },
97
+ };
98
+
93
99
  export const TableCellEditor = ({
94
100
  __gridScope,
95
101
  model,
@@ -245,11 +251,7 @@ export const TableCellEditor = ({
245
251
  <CellValidationMessage validationError={validationError} variant={validationVariant} __gridScope={__gridScope} />
246
252
  <GridCellEditor
247
253
  extensions={extensions}
248
- slots={{
249
- content: {
250
- className: '!py-(--dx-grid-cell-editor-padding-block)',
251
- },
252
- }}
254
+ slots={cellEditorSlots}
253
255
  getCellContent={getCellContent}
254
256
  onBlur={handleBlur}
255
257
  />
@@ -6,9 +6,9 @@ import { Atom } from '@effect-atom/atom-react';
6
6
  import * as Schema from 'effect/Schema';
7
7
  import { useEffect } from 'react';
8
8
 
9
- import { Format, Type } from '@dxos/echo';
10
- import { TypeEnum } from '@dxos/echo/internal';
11
- import { setValue } from '@dxos/effect';
9
+ import { DXN, Format, Type } from '@dxos/echo';
10
+ import { TypeEnum } from '@dxos/echo/Format';
11
+ import { SchemaEx } from '@dxos/effect';
12
12
  import { random } from '@dxos/random';
13
13
  import { type ProjectionModel } from '@dxos/schema';
14
14
 
@@ -17,12 +17,7 @@ export const TestSchema = Schema.Struct({
17
17
  age: Schema.optional(Schema.Number),
18
18
  active: Schema.optional(Schema.Boolean),
19
19
  netWorth: Schema.optional(Schema.Number),
20
- }).pipe(
21
- Type.object({
22
- typename: 'com.example.type.test',
23
- version: '0.1.0',
24
- }),
25
- );
20
+ }).pipe(Type.makeObject(DXN.make('com.example.type.test', '0.1.0')));
26
21
 
27
22
  export type TestItem = {
28
23
  name: string;
@@ -99,15 +94,15 @@ export const useSimulator = ({ items, projection, insertInterval, updateInterval
99
94
  // TODO(ZaymonFC): Restore this once I know how to derive the type from the schema.
100
95
  switch (type) {
101
96
  case TypeEnum.String: {
102
- setValue(item, path, `Updated ${Date.now()}`);
97
+ SchemaEx.setValue(item, path, `Updated ${Date.now()}`);
103
98
  break;
104
99
  }
105
100
  case TypeEnum.Number: {
106
- setValue(item, path, Math.floor(Math.random() * 100));
101
+ SchemaEx.setValue(item, path, Math.floor(Math.random() * 100));
107
102
  break;
108
103
  }
109
104
  case TypeEnum.Boolean: {
110
- setValue(item, path, !item[path]);
105
+ SchemaEx.setValue(item, path, !item[path]);
111
106
  break;
112
107
  }
113
108
  }
@@ -5,7 +5,7 @@
5
5
  import { type Registry } from '@effect-atom/atom-react';
6
6
  import { useState } from 'react';
7
7
 
8
- import { JsonSchema, Type } from '@dxos/echo';
8
+ import { Type } from '@dxos/echo';
9
9
  import { useAsyncEffect } from '@dxos/react-ui';
10
10
  import { ProjectionModel, createEchoChangeCallback } from '@dxos/schema';
11
11
 
@@ -21,13 +21,12 @@ export const useProjectionModel = <S extends Type.AnyEntity>(
21
21
  useAsyncEffect(async () => {
22
22
  if (schema && table) {
23
23
  const view = await table.view.load();
24
- // For mutable schemas (EchoSchema), use the live jsonSchema reference for reactivity.
25
- // For immutable schemas, create a snapshot.
26
- const jsonSchema = Type.isMutable(schema) ? schema.jsonSchema : JsonSchema.toJsonSchema(schema);
24
+ // Use the live jsonSchema reference for reactivity.
25
+ const jsonSchema = schema.jsonSchema;
27
26
 
28
27
  // Always use createEchoChangeCallback since the view is ECHO-backed.
29
- // Pass schema only when mutable to allow schema mutations.
30
- const change = createEchoChangeCallback(view, Type.isMutable(schema) ? schema : undefined);
28
+ // Pass the type entity only when stored, to allow schema mutations.
29
+ const change = createEchoChangeCallback(view, Type.getDatabase(schema) != null ? schema : undefined);
31
30
 
32
31
  const projection = new ProjectionModel({ registry, view, baseSchema: jsonSchema, change });
33
32
  projection.normalizeView();
@@ -44,7 +44,7 @@ export const useTableModel = <T extends TableRow = TableRow>({
44
44
  ...props
45
45
  }: UseTableModelProps<T>): TableModel<T> | undefined => {
46
46
  const registry = useContext(RegistryContext);
47
- const selected = useSelected(object && Obj.getDXN(object).toString(), 'multi');
47
+ const selected = useSelected(object && Obj.getURI(object), 'multi');
48
48
  const initialSelection = useMemo(() => selected, [object]);
49
49
 
50
50
  const [model, setModel] = useState<TableModel<T>>();
package/src/index.ts CHANGED
@@ -2,11 +2,7 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { translations } from './translations';
6
-
7
5
  export * from './components';
8
6
  export * from './hooks';
9
7
  export * from './model';
10
8
  export * from './util';
11
-
12
- export { translations };
@@ -6,7 +6,7 @@ import { Registry } from '@effect-atom/atom-react';
6
6
  import * as Schema from 'effect/Schema';
7
7
  import { afterEach, beforeEach, describe, expect, test } from 'vitest';
8
8
 
9
- import { Filter, JsonSchema, Query, Type } from '@dxos/echo';
9
+ import { DXN, Filter, JsonSchema, Query, Type } from '@dxos/echo';
10
10
  import { createEchoSchema } from '@dxos/echo/testing';
11
11
  import { ProjectionModel, ViewModel, createDirectChangeCallback } from '@dxos/schema';
12
12
 
@@ -118,15 +118,10 @@ describe('TableModel', () => {
118
118
  const Test = Schema.Struct({
119
119
  title: Schema.String,
120
120
  completed: Schema.Boolean,
121
- }).pipe(
122
- Type.object({
123
- typename: 'com.example.type.test',
124
- version: '0.1.0',
125
- }),
126
- );
121
+ }).pipe(Type.makeObject(DXN.make('com.example.type.test', '0.1.0')));
127
122
 
128
123
  const createTableModel = (registry: Registry.Registry, props: Partial<TableModelProps> = {}): TableModel => {
129
- const schema = createEchoSchema(Test);
124
+ const schema = createEchoSchema(Type.getSchema(Test));
130
125
  const view = ViewModel.make({
131
126
  query: Query.select(Filter.type(schema)),
132
127
  jsonSchema: schema.jsonSchema,
@@ -136,7 +131,7 @@ const createTableModel = (registry: Registry.Registry, props: Partial<TableModel
136
131
  registry,
137
132
  view,
138
133
  baseSchema: schema.jsonSchema,
139
- change: createDirectChangeCallback(view.projection, JsonSchema.toJsonSchema(schema)),
134
+ change: createDirectChangeCallback(view.projection, JsonSchema.toJsonSchema(Type.getSchema(schema))),
140
135
  });
141
136
  projection.normalizeView();
142
137
  return new TableModel({