@dxos/react-ui-table 0.8.4-main.3eb6e50203 → 0.8.4-main.4a85c3132b

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 (80) hide show
  1. package/dist/lib/browser/{chunk-OZBLY7XM.mjs → chunk-DSR2MRZB.mjs} +10 -6
  2. package/dist/lib/browser/chunk-DSR2MRZB.mjs.map +7 -0
  3. package/dist/lib/browser/{chunk-GTGYBRJA.mjs → chunk-PTHMYAAM.mjs} +25 -22
  4. package/dist/lib/browser/chunk-PTHMYAAM.mjs.map +7 -0
  5. package/dist/lib/browser/index.mjs +179 -175
  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 +2 -2
  9. package/dist/lib/browser/testing/index.mjs.map +1 -1
  10. package/dist/lib/browser/types/index.mjs +1 -1
  11. package/dist/lib/node-esm/{chunk-DPNSZHU5.mjs → chunk-GI72MQSG.mjs} +25 -22
  12. package/dist/lib/node-esm/chunk-GI72MQSG.mjs.map +7 -0
  13. package/dist/lib/node-esm/{chunk-WNZSKIRF.mjs → chunk-HTZJSULI.mjs} +10 -6
  14. package/dist/lib/node-esm/chunk-HTZJSULI.mjs.map +7 -0
  15. package/dist/lib/node-esm/index.mjs +179 -175
  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 +2 -2
  19. package/dist/lib/node-esm/testing/index.mjs.map +1 -1
  20. package/dist/lib/node-esm/types/index.mjs +1 -1
  21. package/dist/types/src/components/Table/ColumnSettings.d.ts.map +1 -1
  22. package/dist/types/src/components/Table/DynamicTable.d.ts +2 -2
  23. package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
  24. package/dist/types/src/components/Table/DynamicTable.stories.d.ts +1 -1
  25. package/dist/types/src/components/Table/Relations.stories.d.ts.map +1 -1
  26. package/dist/types/src/components/Table/Table.d.ts +7 -7
  27. package/dist/types/src/components/Table/Table.d.ts.map +1 -1
  28. package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
  29. package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +3 -3
  30. package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
  31. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +2 -2
  32. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
  33. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
  34. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts +1 -1
  35. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +1 -1
  36. package/dist/types/src/hooks/useAddRow.d.ts +1 -1
  37. package/dist/types/src/hooks/useAddRow.d.ts.map +1 -1
  38. package/dist/types/src/hooks/useProjectionModel.d.ts +1 -1
  39. package/dist/types/src/hooks/useProjectionModel.d.ts.map +1 -1
  40. package/dist/types/src/model/table-model.d.ts +2 -1
  41. package/dist/types/src/model/table-model.d.ts.map +1 -1
  42. package/dist/types/src/model/table-presentation.d.ts.map +1 -1
  43. package/dist/types/src/testing/useTestTableModel.d.ts +2 -2
  44. package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -1
  45. package/dist/types/src/types/Table.d.ts +1 -1
  46. package/dist/types/src/types/Table.d.ts.map +1 -1
  47. package/dist/types/src/util/dynamic-table.d.ts +2 -2
  48. package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
  49. package/dist/types/src/util/query-ast.d.ts.map +1 -1
  50. package/dist/types/tsconfig.tsbuildinfo +1 -1
  51. package/package.json +36 -36
  52. package/src/components/Table/ColumnSettings.tsx +3 -4
  53. package/src/components/Table/DynamicTable.stories.tsx +2 -2
  54. package/src/components/Table/DynamicTable.tsx +7 -7
  55. package/src/components/Table/Relations.stories.tsx +12 -9
  56. package/src/components/Table/Table.stories.tsx +34 -25
  57. package/src/components/Table/Table.tsx +53 -57
  58. package/src/components/TableCellEditor/CellValidationMessage.tsx +4 -4
  59. package/src/components/TableCellEditor/FormCellEditor.tsx +11 -5
  60. package/src/components/TableCellEditor/TableCellEditor.stories.tsx +3 -2
  61. package/src/components/TableCellEditor/TableCellEditor.tsx +3 -3
  62. package/src/components/TableToolbar/TableToolbar.tsx +6 -6
  63. package/src/hooks/useAddRow.ts +1 -1
  64. package/src/hooks/useProjectionModel.ts +3 -3
  65. package/src/model/table-model.test.ts +4 -4
  66. package/src/model/table-model.ts +2 -1
  67. package/src/model/table-presentation.test.ts +4 -4
  68. package/src/model/table-presentation.ts +12 -11
  69. package/src/playwright/playwright.config.ts +1 -1
  70. package/src/testing/useTestTableModel.ts +2 -2
  71. package/src/types/Table.ts +9 -4
  72. package/src/util/dynamic-table.test.ts +2 -2
  73. package/src/util/dynamic-table.ts +7 -6
  74. package/src/util/query-ast.ts +3 -0
  75. package/src/util/table-buttons.ts +1 -1
  76. package/src/util/table-controls.ts +2 -2
  77. package/dist/lib/browser/chunk-GTGYBRJA.mjs.map +0 -7
  78. package/dist/lib/browser/chunk-OZBLY7XM.mjs.map +0 -7
  79. package/dist/lib/node-esm/chunk-DPNSZHU5.mjs.map +0 -7
  80. package/dist/lib/node-esm/chunk-WNZSKIRF.mjs.map +0 -7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/react-ui-table",
3
- "version": "0.8.4-main.3eb6e50203",
3
+ "version": "0.8.4-main.4a85c3132b",
4
4
  "description": "A table component.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -68,31 +68,31 @@
68
68
  "@radix-ui/react-use-controllable-state": "1.1.0",
69
69
  "date-fns": "^3.3.1",
70
70
  "lodash.orderby": "^4.6.0",
71
- "@dxos/async": "0.8.4-main.3eb6e50203",
72
- "@dxos/context": "0.8.4-main.3eb6e50203",
73
- "@dxos/echo-atom": "0.8.4-main.3eb6e50203",
74
- "@dxos/echo": "0.8.4-main.3eb6e50203",
75
- "@dxos/effect": "0.8.4-main.3eb6e50203",
76
- "@dxos/keys": "0.8.4-main.3eb6e50203",
77
- "@dxos/log": "0.8.4-main.3eb6e50203",
78
- "@dxos/random": "0.8.4-main.3eb6e50203",
79
- "@dxos/invariant": "0.8.4-main.3eb6e50203",
80
- "@dxos/react-client": "0.8.4-main.3eb6e50203",
81
- "@dxos/lit-grid": "0.8.4-main.3eb6e50203",
82
- "@dxos/react-ui-attention": "0.8.4-main.3eb6e50203",
83
- "@dxos/react-ui-editor": "0.8.4-main.3eb6e50203",
84
- "@dxos/react-ui-components": "0.8.4-main.3eb6e50203",
85
- "@dxos/react-ui-form": "0.8.4-main.3eb6e50203",
86
- "@dxos/react-ui-grid": "0.8.4-main.3eb6e50203",
87
- "@dxos/react-ui-menu": "0.8.4-main.3eb6e50203",
88
- "@dxos/react-ui-stack": "0.8.4-main.3eb6e50203",
89
- "@dxos/debug": "0.8.4-main.3eb6e50203",
90
- "@dxos/react-ui-searchlist": "0.8.4-main.3eb6e50203",
91
- "@dxos/react-ui-syntax-highlighter": "0.8.4-main.3eb6e50203",
92
- "@dxos/types": "0.8.4-main.3eb6e50203",
93
- "@dxos/ui-types": "0.8.4-main.3eb6e50203",
94
- "@dxos/schema": "0.8.4-main.3eb6e50203",
95
- "@dxos/util": "0.8.4-main.3eb6e50203"
71
+ "@dxos/async": "0.8.4-main.4a85c3132b",
72
+ "@dxos/context": "0.8.4-main.4a85c3132b",
73
+ "@dxos/debug": "0.8.4-main.4a85c3132b",
74
+ "@dxos/echo": "0.8.4-main.4a85c3132b",
75
+ "@dxos/echo-atom": "0.8.4-main.4a85c3132b",
76
+ "@dxos/effect": "0.8.4-main.4a85c3132b",
77
+ "@dxos/invariant": "0.8.4-main.4a85c3132b",
78
+ "@dxos/random": "0.8.4-main.4a85c3132b",
79
+ "@dxos/log": "0.8.4-main.4a85c3132b",
80
+ "@dxos/react-client": "0.8.4-main.4a85c3132b",
81
+ "@dxos/keys": "0.8.4-main.4a85c3132b",
82
+ "@dxos/react-ui-attention": "0.8.4-main.4a85c3132b",
83
+ "@dxos/react-ui-components": "0.8.4-main.4a85c3132b",
84
+ "@dxos/react-ui-editor": "0.8.4-main.4a85c3132b",
85
+ "@dxos/react-ui-form": "0.8.4-main.4a85c3132b",
86
+ "@dxos/react-ui-menu": "0.8.4-main.4a85c3132b",
87
+ "@dxos/react-ui-searchlist": "0.8.4-main.4a85c3132b",
88
+ "@dxos/react-ui-grid": "0.8.4-main.4a85c3132b",
89
+ "@dxos/react-ui-stack": "0.8.4-main.4a85c3132b",
90
+ "@dxos/schema": "0.8.4-main.4a85c3132b",
91
+ "@dxos/types": "0.8.4-main.4a85c3132b",
92
+ "@dxos/react-ui-syntax-highlighter": "0.8.4-main.4a85c3132b",
93
+ "@dxos/ui-types": "0.8.4-main.4a85c3132b",
94
+ "@dxos/util": "0.8.4-main.4a85c3132b",
95
+ "@dxos/lit-grid": "0.8.4-main.4a85c3132b"
96
96
  },
97
97
  "devDependencies": {
98
98
  "@effect/platform": "0.94.4",
@@ -103,14 +103,14 @@
103
103
  "fast-check": "^3.19.0",
104
104
  "react": "~19.2.3",
105
105
  "react-dom": "~19.2.3",
106
- "vite": "7.1.9",
107
- "@dxos/random": "0.8.4-main.3eb6e50203",
108
- "@dxos/lit-ui": "0.8.4-main.3eb6e50203",
109
- "@dxos/react-client": "0.8.4-main.3eb6e50203",
110
- "@dxos/react-ui": "0.8.4-main.3eb6e50203",
111
- "@dxos/storybook-utils": "0.8.4-main.3eb6e50203",
112
- "@dxos/test-utils": "0.8.4-main.3eb6e50203",
113
- "@dxos/ui-theme": "0.8.4-main.3eb6e50203"
106
+ "vite": "^7.1.11",
107
+ "@dxos/lit-ui": "0.8.4-main.4a85c3132b",
108
+ "@dxos/random": "0.8.4-main.4a85c3132b",
109
+ "@dxos/react-client": "0.8.4-main.4a85c3132b",
110
+ "@dxos/react-ui": "0.8.4-main.4a85c3132b",
111
+ "@dxos/storybook-utils": "0.8.4-main.4a85c3132b",
112
+ "@dxos/test-utils": "0.8.4-main.4a85c3132b",
113
+ "@dxos/ui-theme": "0.8.4-main.4a85c3132b"
114
114
  },
115
115
  "peerDependencies": {
116
116
  "@effect-atom/atom-react": "^0.5.0",
@@ -118,8 +118,8 @@
118
118
  "effect": "3.19.16",
119
119
  "react": "~19.2.3",
120
120
  "react-dom": "~19.2.3",
121
- "@dxos/react-ui": "0.8.4-main.3eb6e50203",
122
- "@dxos/ui-theme": "0.8.4-main.3eb6e50203"
121
+ "@dxos/react-ui": "0.8.4-main.4a85c3132b",
122
+ "@dxos/ui-theme": "0.8.4-main.4a85c3132b"
123
123
  },
124
124
  "publishConfig": {
125
125
  "access": "public"
@@ -5,10 +5,9 @@
5
5
  import { useAtomValue } from '@effect-atom/atom-react';
6
6
  import React, { useCallback, useEffect, useMemo, useState } from 'react';
7
7
 
8
- import { type SchemaRegistry } from '@dxos/echo';
8
+ import { type SchemaRegistry, type View } from '@dxos/echo';
9
9
  import { Popover } from '@dxos/react-ui';
10
10
  import { FieldEditor } from '@dxos/react-ui-form';
11
- import { type FieldType } from '@dxos/schema';
12
11
 
13
12
  import { type ModalController, type TableModel } from '../../model';
14
13
 
@@ -20,7 +19,7 @@ type ColumnSettingsProps = {
20
19
  };
21
20
 
22
21
  export const ColumnSettings = ({ registry, model, modals, onNewColumn }: ColumnSettingsProps) => {
23
- const [newField, setNewField] = useState<FieldType>();
22
+ const [newField, setNewField] = useState<View.FieldType>();
24
23
  const state = useAtomValue(modals.state);
25
24
 
26
25
  useEffect(() => {
@@ -64,7 +63,7 @@ export const ColumnSettings = ({ registry, model, modals, onNewColumn }: ColumnS
64
63
  <Popover.Root modal={false} open={state?.type === 'columnSettings'}>
65
64
  <Popover.VirtualTrigger virtualRef={modals.trigger} />
66
65
  <Popover.Portal>
67
- <Popover.Content classNames='md:is-64'>
66
+ <Popover.Content classNames='md:w-64'>
68
67
  <Popover.Viewport>
69
68
  <FieldEditor
70
69
  projection={model.projection}
@@ -11,7 +11,7 @@ import { Format } from '@dxos/echo/internal';
11
11
  import { faker } from '@dxos/random';
12
12
  import { Filter, useQuery, useSchema } from '@dxos/react-client/echo';
13
13
  import { useClientStory, withClientProvider } from '@dxos/react-client/testing';
14
- import { withTheme } from '@dxos/react-ui/testing';
14
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
15
15
  import { type SchemaPropertyDefinition } from '@dxos/schema';
16
16
  import { TestSchema } from '@dxos/schema/testing';
17
17
  import { withRegistry } from '@dxos/storybook-utils';
@@ -59,7 +59,7 @@ const DynamicTableStory = () => {
59
59
  const meta = {
60
60
  title: 'ui/react-ui-table/DynamicTable',
61
61
  component: DynamicTable,
62
- decorators: [withTheme(), withRegistry],
62
+ decorators: [withTheme(), withLayout({ layout: 'fullscreen' }), withRegistry],
63
63
  parameters: {
64
64
  layout: 'fullscreen',
65
65
  translations,
@@ -19,7 +19,7 @@ import { type TablePropertyDefinition, getBaseSchema, makeDynamicTable } from '.
19
19
 
20
20
  import { Table, type TableController } from './Table';
21
21
 
22
- export type DynamicTableProps<T extends Type.Entity.Any = Type.Entity.Any> = ThemedClassName<{
22
+ export type DynamicTableProps<T extends Type.AnyEntity = Type.AnyEntity> = ThemedClassName<{
23
23
  schema?: T;
24
24
  name?: string; // TODO(burdon): Remove?
25
25
  rows: any[];
@@ -36,13 +36,13 @@ export type DynamicTableProps<T extends Type.Entity.Any = Type.Entity.Any> = The
36
36
  */
37
37
  // TODO(burdon): Instead of creating component variants, create helpers/hooks that normalize the props.
38
38
  // TODO(burdon): Warning: Cannot update a component (`DynamicTable`) while rendering a different component (`DynamicTable`).
39
- export const DynamicTable = <T extends Type.Entity.Any = Type.Entity.Any>({
39
+ export const DynamicTable = <T extends Type.AnyEntity = Type.AnyEntity>({
40
40
  classNames,
41
41
  schema,
42
42
  name = 'example.com/dynamic-table', // Rmove default or make random; this will lead to type collisions.
43
43
  rows,
44
44
  properties,
45
- jsonSchema: _jsonSchema,
45
+ jsonSchema: jsonSchemaProp,
46
46
  rowActions,
47
47
  onRowClick,
48
48
  onRowAction,
@@ -53,8 +53,8 @@ export const DynamicTable = <T extends Type.Entity.Any = Type.Entity.Any>({
53
53
 
54
54
  // TODO(burdon): Remove variance from the props (should be normalized externally; possibly via hooks).
55
55
  const { jsonSchema } = useMemo(
56
- () => getBaseSchema({ typename: name, properties, jsonSchema: _jsonSchema, schema }),
57
- [name, properties, _jsonSchema, schema],
56
+ () => getBaseSchema({ typename: name, properties, jsonSchema: jsonSchemaProp, schema }),
57
+ [name, properties, jsonSchemaProp, schema],
58
58
  );
59
59
 
60
60
  useEffect(() => {
@@ -98,8 +98,8 @@ export const DynamicTable = <T extends Type.Entity.Any = Type.Entity.Any>({
98
98
 
99
99
  // TODO(burdon): Do we need the outer divs?
100
100
  return (
101
- <div role='none' className={mx('is-full bs-full grow grid', classNames)}>
102
- <div role='none' className='grid min-bs-0 overflow-hidden'>
101
+ <div role='none' className={mx('w-full h-full grow grid', classNames)}>
102
+ <div role='none' className='grid min-h-0 overflow-hidden'>
103
103
  <Table.Root>
104
104
  <Table.Main
105
105
  ref={tableRef}
@@ -8,6 +8,7 @@ import React, { useCallback, useContext, useEffect, useMemo, useState } from 're
8
8
  import { expect, userEvent, within } from 'storybook/test';
9
9
 
10
10
  import { Obj, Type } from '@dxos/echo';
11
+ import { View } from '@dxos/echo';
11
12
  import { invariant } from '@dxos/invariant';
12
13
  import { type DxGrid } from '@dxos/lit-grid';
13
14
  import '@dxos/lit-ui/dx-tag-picker.pcss';
@@ -16,7 +17,7 @@ import { useClientStory, withClientProvider } from '@dxos/react-client/testing';
16
17
  import { useAsyncEffect } from '@dxos/react-ui';
17
18
  import { withLayout, withTheme } from '@dxos/react-ui/testing';
18
19
  import { translations as formTranslations } from '@dxos/react-ui-form';
19
- import { View } from '@dxos/schema';
20
+ import { ViewModel } from '@dxos/schema';
20
21
  import { type ValueGenerator, createAsyncGenerator } from '@dxos/schema/testing';
21
22
  import { withRegistry } from '@dxos/storybook-utils';
22
23
  import { Organization, Person } from '@dxos/types';
@@ -35,7 +36,7 @@ const generator: ValueGenerator = faker as any;
35
36
  // TODO(burdon): Mutable and immutable views.
36
37
  // TODO(burdon): Reconcile schemas types and utils (see API PR).
37
38
  // TODO(burdon): Base type for T (with id); see ECHO API PR?
38
- const useTestModel = <S extends Type.Obj.Any>(schema: S, count: number) => {
39
+ const useTestModel = <S extends Type.AnyObj>(schema: S, count: number) => {
39
40
  const registry = useContext(RegistryContext);
40
41
  const { space } = useClientStory();
41
42
  const [object, setObject] = useState<Table.Table>();
@@ -50,7 +51,7 @@ const useTestModel = <S extends Type.Obj.Any>(schema: S, count: number) => {
50
51
  return;
51
52
  }
52
53
 
53
- const { view, jsonSchema } = await View.makeFromDatabase({ db: space.db, typename: Type.getTypename(schema) });
54
+ const { view, jsonSchema } = await ViewModel.makeFromDatabase({ db: space.db, typename: Type.getTypename(schema) });
54
55
  const object = Table.make({ view, jsonSchema });
55
56
  setObject(object);
56
57
  space.db.add(object);
@@ -87,7 +88,7 @@ const DefaultStory = () => {
87
88
  const { space } = useClientStory();
88
89
 
89
90
  const handleCreate = useCallback(
90
- (schema: Type.Entity.Any, values: any) => {
91
+ (schema: Type.AnyEntity, values: any) => {
91
92
  invariant(Type.isObjectSchema(schema));
92
93
  invariant(space);
93
94
  return space.db.add(Obj.make(schema, values));
@@ -96,7 +97,7 @@ const DefaultStory = () => {
96
97
  );
97
98
 
98
99
  return (
99
- <div className='is-full bs-full grid grid-cols-2 divide-x divide-separator'>
100
+ <div className='w-full h-full grid grid-cols-2 divide-x divide-separator'>
100
101
  <TableComponent.Root>
101
102
  <TableComponent.Main
102
103
  model={orgModel}
@@ -195,8 +196,9 @@ export const Default: Story = {
195
196
  const saveButton = await body.findByTestId('save-button');
196
197
  await userEvent.click(saveButton);
197
198
 
198
- // Verify the relation was set (cell should now contain the org name)
199
- await expect(targetCell).toHaveTextContent(orgName.substring(0, 4));
199
+ // Verify the relation was set (cell should now contain the org name).
200
+ const updatedCell = within(secondGrid).getByTestId('grid.4.0');
201
+ await expect(updatedCell).toHaveTextContent(orgName.substring(0, 4));
200
202
 
201
203
  // Test object creation (new relations) - equivalent to "new relations work as expected" test
202
204
  // Find a different cell to test object creation (second row, relations column)
@@ -231,7 +233,8 @@ export const Default: Story = {
231
233
  const saveObjectButton = await within(createReferencedObjectForm).findByTestId('save-button');
232
234
  await userEvent.click(saveObjectButton);
233
235
 
234
- // Verify the new object was created and relation was set
235
- await expect(newTargetCell).toHaveTextContent(newOrgName);
236
+ // Verify the new object was created and relation was set.
237
+ const updatedNewCell = within(secondGrid).getByTestId('grid.4.1');
238
+ await expect(updatedNewCell).toHaveTextContent(newOrgName);
236
239
  },
237
240
  } as any;
@@ -6,24 +6,24 @@ 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
8
 
9
- import { Annotation, type Database, Format, Obj, type QueryAST, Type } from '@dxos/echo';
9
+ import { Annotation, type Database, Format, Obj, type QueryAST, Ref, Type } from '@dxos/echo';
10
+ import { View } from '@dxos/echo';
10
11
  import { type Mutable, PropertyMetaAnnotationId } from '@dxos/echo/internal';
11
12
  import { invariant } from '@dxos/invariant';
12
13
  import { faker } from '@dxos/random';
13
14
  import { PublicKey } from '@dxos/react-client';
14
15
  import { withClientProvider } from '@dxos/react-client/testing';
15
- import { ScrollArea } from '@dxos/react-ui';
16
- import { withTheme } from '@dxos/react-ui/testing';
16
+ import { Panel, ScrollArea } from '@dxos/react-ui';
17
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
17
18
  import { ViewEditor, translations as formTranslations } from '@dxos/react-ui-form';
18
19
  import { SyntaxHighlighter } from '@dxos/react-ui-syntax-highlighter';
19
- import { View, getSchemaFromPropertyDefinitions, getTypenameFromQuery } from '@dxos/schema';
20
+ import { ViewModel, getSchemaFromPropertyDefinitions, getTypenameFromQuery } from '@dxos/schema';
20
21
  import { TestSchema, createObjectFactory } from '@dxos/schema/testing';
21
22
  import { withRegistry } from '@dxos/storybook-utils';
22
23
 
23
24
  import { useTestTableModel } from '../../testing';
24
25
  import { translations } from '../../translations';
25
26
  import { Table } from '../../types';
26
- import { TableToolbar } from '../TableToolbar';
27
27
 
28
28
  import { Table as TableComponent } from './Table';
29
29
 
@@ -48,7 +48,7 @@ const Example = Schema.Struct({
48
48
  }),
49
49
  ),
50
50
  description: Schema.optional(Schema.String).annotations({ title: 'Description' }),
51
- parent: Schema.optional(Schema.suspend((): Type.Ref<Example> => Type.Ref(Example))).annotations({
51
+ parent: Schema.optional(Schema.suspend((): Ref.RefSchema<Example> => Ref.Ref(Example))).annotations({
52
52
  title: 'Parent',
53
53
  }),
54
54
  }).pipe(
@@ -119,20 +119,28 @@ const DefaultStory = () => {
119
119
 
120
120
  return (
121
121
  <div className='grow grid grid-cols-[1fr_350px]'>
122
- <div className='grid grid-rows-[min-content_1fr] min-bs-0 overflow-hidden'>
123
- <TableToolbar classNames='border-be border-subduedSeparator' onAdd={handleInsertRow} onSave={handleSaveView} />
124
- <TableComponent.Root>
125
- <TableComponent.Main
126
- ref={tableRef}
127
- schema={schema}
128
- model={model}
129
- presentation={presentation}
130
- onRowClick={handleRowClick}
131
- ignoreAttention
132
- />
133
- </TableComponent.Root>
134
- </div>
135
- <ScrollArea.Root orientation='vertical' classNames='bs-full border-l border-separator'>
122
+ <TableComponent.Root>
123
+ <Panel.Root>
124
+ <Panel.Toolbar asChild>
125
+ <TableComponent.Toolbar
126
+ classNames='border-b border-subdued-separator'
127
+ onAdd={handleInsertRow}
128
+ onSave={handleSaveView}
129
+ />
130
+ </Panel.Toolbar>
131
+ <Panel.Content asChild>
132
+ <TableComponent.Main
133
+ ref={tableRef}
134
+ schema={schema}
135
+ model={model}
136
+ presentation={presentation}
137
+ onRowClick={handleRowClick}
138
+ ignoreAttention
139
+ />
140
+ </Panel.Content>
141
+ </Panel.Root>
142
+ </TableComponent.Root>
143
+ <ScrollArea.Root orientation='vertical' classNames='border-l border-separator'>
136
144
  <ScrollArea.Viewport>
137
145
  <StoryViewEditor view={table.view.target} schema={schema} db={db} handleDeleteColumn={handleDeleteColumn} />
138
146
  <SyntaxHighlighter language='json' className='text-xs'>
@@ -150,13 +158,14 @@ type StoryProps = { rows?: number };
150
158
  // Story definitions.
151
159
  //
152
160
 
153
- // TODO(burdon): Need super simple story.
161
+ // TODO(burdon): Need simpler story.
154
162
 
155
163
  const meta = {
156
164
  title: 'ui/react-ui-table/Table',
157
165
  render: DefaultStory,
158
166
  decorators: [
159
167
  withTheme(),
168
+ withLayout({ layout: 'fullscreen' }),
160
169
  withRegistry,
161
170
  withClientProvider({
162
171
  types: [View.View, Table.Table],
@@ -164,7 +173,7 @@ const meta = {
164
173
  createSpace: true,
165
174
  onCreateSpace: async ({ space }) => {
166
175
  const [schema] = await space.db.schemaRegistry.register([Example]);
167
- const { view, jsonSchema } = await View.makeFromDatabase({ db: space.db, typename: schema.typename });
176
+ const { view, jsonSchema } = await ViewModel.makeFromDatabase({ db: space.db, typename: schema.typename });
168
177
  const table = Table.make({ view, jsonSchema });
169
178
  Obj.change(view, (v) => {
170
179
  v.projection.fields = [
@@ -210,7 +219,7 @@ export const StaticSchema: StoryObj = {
210
219
  createIdentity: true,
211
220
  createSpace: true,
212
221
  onCreateSpace: async ({ space }) => {
213
- const { view, jsonSchema } = await View.makeFromDatabase({
222
+ const { view, jsonSchema } = await ViewModel.makeFromDatabase({
214
223
  db: space.db,
215
224
  typename: TestSchema.Person.typename,
216
225
  });
@@ -256,7 +265,7 @@ export const ArrayOfObjects: StoryObj = {
256
265
  createIdentity: true,
257
266
  createSpace: true,
258
267
  onCreateSpace: async ({ space }) => {
259
- const { view, jsonSchema } = await View.makeFromDatabase({
268
+ const { view, jsonSchema } = await ViewModel.makeFromDatabase({
260
269
  db: space.db,
261
270
  typename: ContactWithArrayOfEmails.typename,
262
271
  });
@@ -314,7 +323,7 @@ export const Tags: Meta<StoryProps> = {
314
323
  const [storedSchema] = await space.db.schemaRegistry.register([schema]);
315
324
 
316
325
  // Initialize table.
317
- const { view, jsonSchema } = await View.makeFromDatabase({ db: space.db, typename });
326
+ const { view, jsonSchema } = await ViewModel.makeFromDatabase({ db: space.db, typename });
318
327
  const table = Table.make({ view, jsonSchema });
319
328
  space.db.add(table);
320
329
 
@@ -35,11 +35,13 @@ import {
35
35
  gridSeparatorInlineEnd,
36
36
  } from '@dxos/react-ui-grid';
37
37
  import { DxEditRequest } from '@dxos/react-ui-grid';
38
- import { mx } from '@dxos/ui-theme';
38
+ import { mx, useComposableProps } from '@dxos/ui-theme';
39
+ import { type ComposableProps } from '@dxos/ui-types';
39
40
 
40
41
  import { type InsertRowResult, ModalController, type TableModel, type TablePresentation } from '../../model';
41
42
  import { tableButtons, tableControls } from '../../util';
42
43
  import { type OnCreateHandler, type TableCellEditorProps, TableValueEditor } from '../TableCellEditor';
44
+ import { TableToolbar } from '../TableToolbar';
43
45
 
44
46
  import { ColumnActionsMenu } from './ColumnActionsMenu';
45
47
  import { ColumnSettings } from './ColumnSettings';
@@ -53,51 +55,37 @@ const emptyColumnMeta = Atom.make<DxGridAxisMeta>({ grid: {} });
53
55
  // Table.Root
54
56
  //
55
57
 
56
- export type TableRootProps = PropsWithChildren<{ role?: string }>;
58
+ export type TableRootProps = PropsWithChildren;
57
59
 
58
- const TableRoot = ({ children, role = 'article' }: TableRootProps) => {
59
- return (
60
- <div
61
- role='none'
62
- className={mx(
63
- 'relative !border-separator [&_.dx-grid]:max-is-[--dx-grid-content-inline-size] [&_.dx-grid]:max-bs-[--dx-grid-content-block-size]',
64
- role === 'card--popover' && 'popover-card-height',
65
- role === 'section' && 'attention-surface',
66
- role === 'card--intrinsic' && '[&_.dx-grid]:bs-[--dx-grid-content-block-size]',
67
- ['card--popover', 'section', 'card--extrinsic'].includes(role) && 'overflow-hidden',
68
- ['article', 'slide'].includes(role) && 'flex flex-col [&_.dx-grid]:grow [&_.dx-grid]:bs-0',
69
- )}
70
- >
71
- {children}
72
- </div>
73
- );
60
+ const TableRoot = ({ children }: TableRootProps) => {
61
+ return <>{children}</>;
74
62
  };
75
63
 
76
64
  //
77
65
  // Table.Main
78
66
  //
79
67
 
68
+ const emptyRowsAtom = Atom.make<unknown[]>([]);
69
+ const emptyCellUpdateAtom = Atom.make<number>(0);
70
+
80
71
  export type TableController = {
81
72
  update?: (cell?: DxGridPosition) => void;
82
73
  handleInsertRowResult?: (insertRowResult?: InsertRowResult) => void;
83
74
  };
84
75
 
85
- export type TableMainProps<T extends Type.Entity.Any = Type.Entity.Any> = {
76
+ export type TableMainProps<T extends Type.AnyEntity = Type.AnyEntity> = ComposableProps<{
86
77
  schema?: T;
87
78
  model?: TableModel;
88
79
  presentation?: TablePresentation;
89
- // TODO(burdon): Rename since attention isn't a useful concept here? Standardize across other components. Pass property into useAttention.
80
+ // TODO(burdon): Factor out attention.
90
81
  ignoreAttention?: boolean;
91
82
  onCreate?: OnCreateHandler;
92
83
  onRowClick?: (row: any) => void;
93
84
  testId?: string;
94
- };
85
+ }>;
95
86
 
96
- const emptyRowsAtom = Atom.make<unknown[]>([]);
97
- const emptyCellUpdateAtom = Atom.make<number>(0);
98
-
99
- const TableMainInner = <T extends Type.Entity.Any = Type.Entity.Any>(
100
- { schema, model, presentation, ignoreAttention, onCreate, onRowClick, testId }: TableMainProps<T>,
87
+ const TableMainInner = <T extends Type.AnyEntity = Type.AnyEntity>(
88
+ { schema, model, presentation, ignoreAttention, onCreate, onRowClick, testId, ...props }: TableMainProps<T>,
101
89
  forwardedRef: Ref<TableController>,
102
90
  ) => {
103
91
  const registry = useContext(RegistryContext);
@@ -424,47 +412,55 @@ const TableMainInner = <T extends Type.Entity.Any = Type.Entity.Any>(
424
412
  }, [dxGrid, model, presentation]);
425
413
 
426
414
  if (!model || !modals) {
427
- return <span role='none' className='attention-surface' />;
415
+ return <span role='none' className='dx-attention-surface' />;
428
416
  }
429
417
 
418
+ const { className, ...rest } = useComposableProps(props);
430
419
  return (
431
- <Grid.Root id={model.id ?? 'table-grid'}>
432
- <TableValueEditor<T>
433
- model={model}
434
- modals={modals}
435
- schema={schema}
436
- onFocus={handleFocus}
437
- onSave={handleSave}
438
- onCreate={onCreate}
439
- />
440
- <Grid.Content
441
- className={mx('[--dx-grid-base:var(--baseSurface)]', gridSeparatorInlineEnd, gridSeparatorBlockEnd)}
442
- frozen={frozen}
443
- columns={columnMeta}
444
- columnDefault={columnDefault}
445
- rowDefault={rowDefault}
446
- limitRows={rows.length}
447
- limitColumns={columnCount}
448
- overscroll='trap'
449
- onAxisResize={handleAxisResize}
450
- onClick={handleGridClick}
451
- onKeyDownCapture={handleKeyDown}
452
- onWheelCapture={handleWheel}
453
- {...(testId && { 'data-testid': testId })}
454
- ref={setDxGrid}
455
- />
456
- <RowActionsMenu model={model} modals={modals} />
457
- <ColumnActionsMenu model={model} modals={modals} />
458
- <ColumnSettings model={model} modals={modals} onNewColumn={handleNewColumn} />
459
- </Grid.Root>
420
+ <div {...rest} role='none' className={mx('dx-container relative', className)}>
421
+ <Grid.Root id={model.id ?? 'table-grid'}>
422
+ <TableValueEditor<T>
423
+ model={model}
424
+ modals={modals}
425
+ schema={schema}
426
+ onFocus={handleFocus}
427
+ onSave={handleSave}
428
+ onCreate={onCreate}
429
+ />
430
+ <Grid.Content
431
+ className={mx('[--dx-grid-base:var(--base-surface)]', gridSeparatorInlineEnd, gridSeparatorBlockEnd)}
432
+ frozen={frozen}
433
+ columns={columnMeta}
434
+ columnDefault={columnDefault}
435
+ rowDefault={rowDefault}
436
+ limitRows={rows.length}
437
+ limitColumns={columnCount}
438
+ overscroll='trap'
439
+ onAxisResize={handleAxisResize}
440
+ onClick={handleGridClick}
441
+ onKeyDownCapture={handleKeyDown}
442
+ onWheelCapture={handleWheel}
443
+ {...(testId && { 'data-testid': testId })}
444
+ ref={setDxGrid}
445
+ />
446
+ <RowActionsMenu model={model} modals={modals} />
447
+ <ColumnActionsMenu model={model} modals={modals} />
448
+ <ColumnSettings model={model} modals={modals} onNewColumn={handleNewColumn} />
449
+ </Grid.Root>
450
+ </div>
460
451
  );
461
452
  };
462
453
 
463
- const TableMain = forwardRef(TableMainInner) as <T extends Type.Entity.Any = Type.Entity.Any>(
454
+ const TableMain = forwardRef(TableMainInner) as <T extends Type.AnyEntity = Type.AnyEntity>(
464
455
  props: TableMainProps<T> & { ref?: Ref<TableController> },
465
456
  ) => JSX.Element;
466
457
 
458
+ //
459
+ // Table
460
+ //
461
+
467
462
  export const Table = {
468
463
  Root: TableRoot,
469
464
  Main: TableMain,
465
+ Toolbar: TableToolbar,
470
466
  };
@@ -22,15 +22,15 @@ export const CellValidationMessage = ({
22
22
  return null;
23
23
  }
24
24
 
25
- const bgClass = variant === 'error' ? 'bg-errorSurface' : 'bg-warningSurface';
26
- const textClass = variant === 'error' ? 'text-errorSurfaceText' : 'text-warningSurfaceText';
25
+ const bgClass = variant === 'error' ? 'bg-error-surface' : 'bg-warning-surface';
26
+ const textClass = variant === 'error' ? 'text-error-surface-text' : 'text-warning-surface-text';
27
27
 
28
28
  return (
29
29
  <div
30
30
  role='none'
31
- className={`absolute ${bgClass} ${textClass} rounded-bs-sm text-xs p-1`}
31
+ className={`absolute ${bgClass} ${textClass} rounded-h-sm text-xs p-1`}
32
32
  style={{
33
- ...{ '--dx-gridCellWidth': `${box?.inlineSize ?? 200}px` },
33
+ ...{ '--dx-grid-cell-width': `${box?.inlineSize ?? 200}px` },
34
34
  zIndex: 10,
35
35
  insetBlockEnd: `calc(100% - ${box.insetBlockStart}px)`,
36
36
  insetInlineStart: box.insetInlineStart,
@@ -20,9 +20,9 @@ import { narrowSchema } from '../../util';
20
20
 
21
21
  const createOptionLabel: Label = ['create new object label', { ns: translationKey }];
22
22
 
23
- export type OnCreateHandler = (schema: Type.Entity.Any, values: any) => Parameters<typeof Ref.make>[0];
23
+ export type OnCreateHandler = (schema: Type.AnyEntity, values: any) => Parameters<typeof Ref.make>[0];
24
24
 
25
- export type FormCellEditorProps<T extends Type.Entity.Any = Type.Entity.Any> = {
25
+ export type FormCellEditorProps<T extends Type.AnyEntity = Type.AnyEntity> = {
26
26
  __gridScope: any;
27
27
  schema?: T;
28
28
  model?: TableModel;
@@ -32,7 +32,7 @@ export type FormCellEditorProps<T extends Type.Entity.Any = Type.Entity.Any> = {
32
32
  onCreate?: OnCreateHandler;
33
33
  } & Omit<FormRootProps<any>, 'values' | 'schema' | 'onCreate'>;
34
34
 
35
- export const FormCellEditor = <T extends Type.Entity.Any = Type.Entity.Any>({
35
+ export const FormCellEditor = <T extends Type.AnyEntity = Type.AnyEntity>({
36
36
  __gridScope,
37
37
  schema,
38
38
  model,
@@ -85,8 +85,12 @@ export const FormCellEditor = <T extends Type.Entity.Any = Type.Entity.Any>({
85
85
 
86
86
  // NOTE: Important to get a mutable deep clone to eject from the echo object.
87
87
  // TODO(wittjosiah): Consider using something like Obj.clone for this use case.
88
- const formValues = useMemo(() => (originalRow ? JSON.parse(JSON.stringify(originalRow)) : {}), [originalRow]);
88
+ const initialFormValues = useMemo(() => (originalRow ? JSON.parse(JSON.stringify(originalRow)) : {}), [originalRow]);
89
+ const [formValues, setFormValues] = useState<any>(initialFormValues);
89
90
 
91
+ const handleValuesChanged = useCallback<NonNullable<FormRootProps<any>['onValuesChanged']>>((values) => {
92
+ setFormValues(values);
93
+ }, []);
90
94
  const handleOpenChange = useCallback((nextOpen: boolean) => {
91
95
  if (nextOpen === false) {
92
96
  setEditing(null);
@@ -162,7 +166,7 @@ export const FormCellEditor = <T extends Type.Entity.Any = Type.Entity.Any>({
162
166
  <Popover.Root open={editing} onOpenChange={handleOpenChange}>
163
167
  <Popover.VirtualTrigger virtualRef={anchorRef} />
164
168
  <Popover.Portal>
165
- <Popover.Content tabIndex={-1} classNames='popover-card-width density-fine'>
169
+ <Popover.Content tabIndex={-1} classNames='dx-card-popover-width dx-density-fine'>
166
170
  <Popover.Arrow />
167
171
  <Popover.Viewport>
168
172
  <Form.Root
@@ -170,6 +174,8 @@ export const FormCellEditor = <T extends Type.Entity.Any = Type.Entity.Any>({
170
174
  autoFocus
171
175
  schema={narrowedSchema}
172
176
  values={formValues}
177
+ onValuesChanged={handleValuesChanged}
178
+ projection={model?.projection}
173
179
  createInitialValuePath={fieldProjection.field.referencePath}
174
180
  createOptionIcon='ph--plus--regular'
175
181
  createOptionLabel={createOptionLabel}