@dxos/react-ui-table 0.8.4-main.dedc0f3 → 0.8.4-main.dfabb4ec29

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 (176) hide show
  1. package/dist/lib/browser/chunk-FBURZBPH.mjs +70 -0
  2. package/dist/lib/browser/chunk-FBURZBPH.mjs.map +7 -0
  3. package/dist/lib/browser/chunk-J5LGTIGS.mjs +10 -0
  4. package/dist/lib/browser/chunk-J5LGTIGS.mjs.map +7 -0
  5. package/dist/lib/browser/chunk-YGC7LZJN.mjs +1695 -0
  6. package/dist/lib/browser/chunk-YGC7LZJN.mjs.map +7 -0
  7. package/dist/lib/browser/index.mjs +1139 -1193
  8. package/dist/lib/browser/index.mjs.map +4 -4
  9. package/dist/lib/browser/meta.json +1 -1
  10. package/dist/lib/browser/testing/index.mjs +33 -42
  11. package/dist/lib/browser/testing/index.mjs.map +3 -3
  12. package/dist/lib/browser/translations.mjs +36 -0
  13. package/dist/lib/browser/translations.mjs.map +7 -0
  14. package/dist/lib/browser/types/index.mjs +4 -3
  15. package/dist/lib/node-esm/chunk-DKKMSVKP.mjs +71 -0
  16. package/dist/lib/node-esm/chunk-DKKMSVKP.mjs.map +7 -0
  17. package/dist/lib/node-esm/chunk-GKIWDEZ7.mjs +1697 -0
  18. package/dist/lib/node-esm/chunk-GKIWDEZ7.mjs.map +7 -0
  19. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +11 -0
  20. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs.map +7 -0
  21. package/dist/lib/node-esm/index.mjs +1139 -1193
  22. package/dist/lib/node-esm/index.mjs.map +4 -4
  23. package/dist/lib/node-esm/meta.json +1 -1
  24. package/dist/lib/node-esm/testing/index.mjs +33 -42
  25. package/dist/lib/node-esm/testing/index.mjs.map +3 -3
  26. package/dist/lib/node-esm/translations.mjs +37 -0
  27. package/dist/lib/node-esm/translations.mjs.map +7 -0
  28. package/dist/lib/node-esm/types/index.mjs +4 -3
  29. package/dist/types/src/components/Table/ColumnActionsMenu.d.ts +1 -2
  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 +7 -7
  34. package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
  35. package/dist/types/src/components/Table/DynamicTable.stories.d.ts +23 -39
  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 +96 -29
  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 +20 -18
  41. package/dist/types/src/components/Table/Table.d.ts.map +1 -1
  42. package/dist/types/src/components/Table/Table.stories.d.ts +96 -31
  43. package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
  44. package/dist/types/src/components/Table/TableContent.d.ts +17 -0
  45. package/dist/types/src/components/Table/TableContent.d.ts.map +1 -0
  46. package/dist/types/src/components/Table/TableToolbar.d.ts +21 -0
  47. package/dist/types/src/components/Table/TableToolbar.d.ts.map +1 -0
  48. package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts +1 -1
  49. package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts.map +1 -1
  50. package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +12 -12
  51. package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
  52. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +7 -7
  53. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
  54. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts +25 -28
  55. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
  56. package/dist/types/src/components/TableCellEditor/index.d.ts +1 -0
  57. package/dist/types/src/components/TableCellEditor/index.d.ts.map +1 -1
  58. package/dist/types/src/components/index.d.ts +0 -1
  59. package/dist/types/src/components/index.d.ts.map +1 -1
  60. package/dist/types/src/components/testing.d.ts +23 -16
  61. package/dist/types/src/components/testing.d.ts.map +1 -1
  62. package/dist/types/src/hooks/index.d.ts +2 -1
  63. package/dist/types/src/hooks/index.d.ts.map +1 -1
  64. package/dist/types/src/hooks/useAddRow.d.ts +5 -6
  65. package/dist/types/src/hooks/useAddRow.d.ts.map +1 -1
  66. package/dist/types/src/hooks/useProjectionModel.d.ts +6 -0
  67. package/dist/types/src/hooks/useProjectionModel.d.ts.map +1 -0
  68. package/dist/types/src/hooks/useTableModel.d.ts +9 -9
  69. package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
  70. package/dist/types/src/index.d.ts +0 -2
  71. package/dist/types/src/index.d.ts.map +1 -1
  72. package/dist/types/src/model/modal-controller.d.ts +5 -8
  73. package/dist/types/src/model/modal-controller.d.ts.map +1 -1
  74. package/dist/types/src/model/selection-model.d.ts +18 -9
  75. package/dist/types/src/model/selection-model.d.ts.map +1 -1
  76. package/dist/types/src/model/table-model.d.ts +106 -26
  77. package/dist/types/src/model/table-model.d.ts.map +1 -1
  78. package/dist/types/src/model/table-presentation.d.ts +3 -1
  79. package/dist/types/src/model/table-presentation.d.ts.map +1 -1
  80. package/dist/types/src/playwright/TableManager.d.ts +2 -1
  81. package/dist/types/src/playwright/TableManager.d.ts.map +1 -1
  82. package/dist/types/src/playwright/playwright.config.d.ts.map +1 -1
  83. package/dist/types/src/testing/useTestTableModel.d.ts +18 -15
  84. package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -1
  85. package/dist/types/src/translations.d.ts +21 -23
  86. package/dist/types/src/translations.d.ts.map +1 -1
  87. package/dist/types/src/types/Table.d.ts +26 -0
  88. package/dist/types/src/types/Table.d.ts.map +1 -0
  89. package/dist/types/src/types/index.d.ts +1 -1
  90. package/dist/types/src/types/index.d.ts.map +1 -1
  91. package/dist/types/src/util/dynamic-table.d.ts +14 -12
  92. package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
  93. package/dist/types/src/util/dynamic-table.test.d.ts +2 -0
  94. package/dist/types/src/util/dynamic-table.test.d.ts.map +1 -0
  95. package/dist/types/src/util/index.d.ts +2 -0
  96. package/dist/types/src/util/index.d.ts.map +1 -1
  97. package/dist/types/src/util/query-ast.d.ts +7 -0
  98. package/dist/types/src/util/query-ast.d.ts.map +1 -0
  99. package/dist/types/src/util/schema.d.ts +1 -1
  100. package/dist/types/src/util/schema.d.ts.map +1 -1
  101. package/dist/types/src/util/signals.d.ts.map +1 -1
  102. package/dist/types/src/util/sort.d.ts +12 -0
  103. package/dist/types/src/util/sort.d.ts.map +1 -0
  104. package/dist/types/src/util/table-buttons.d.ts +20 -20
  105. package/dist/types/src/util/table-buttons.d.ts.map +1 -1
  106. package/dist/types/src/util/table-controls.d.ts +4 -4
  107. package/dist/types/src/util/table-controls.d.ts.map +1 -1
  108. package/dist/types/src/util/tag.d.ts.map +1 -1
  109. package/dist/types/tsconfig.tsbuildinfo +1 -1
  110. package/package.json +67 -76
  111. package/src/components/Table/ColumnActionsMenu.tsx +17 -12
  112. package/src/components/Table/ColumnSettings.tsx +8 -9
  113. package/src/components/Table/DynamicTable.stories.tsx +34 -33
  114. package/src/components/Table/DynamicTable.tsx +33 -36
  115. package/src/components/Table/Relations.stories.tsx +196 -48
  116. package/src/components/Table/RowActionsMenu.tsx +27 -17
  117. package/src/components/Table/Table.stories.tsx +144 -127
  118. package/src/components/Table/Table.tsx +43 -403
  119. package/src/components/Table/TableContent.tsx +421 -0
  120. package/src/components/Table/TableToolbar.tsx +111 -0
  121. package/src/components/TableCellEditor/CellValidationMessage.tsx +5 -6
  122. package/src/components/TableCellEditor/FormCellEditor.tsx +125 -131
  123. package/src/components/TableCellEditor/TableCellEditor.stories.tsx +32 -55
  124. package/src/components/TableCellEditor/TableCellEditor.tsx +39 -124
  125. package/src/components/TableCellEditor/index.ts +1 -0
  126. package/src/components/index.ts +0 -1
  127. package/src/components/testing.ts +44 -18
  128. package/src/hooks/index.ts +2 -1
  129. package/src/hooks/useAddRow.ts +8 -10
  130. package/src/hooks/useProjectionModel.ts +39 -0
  131. package/src/hooks/useTableModel.ts +36 -34
  132. package/src/index.ts +0 -4
  133. package/src/model/modal-controller.ts +23 -40
  134. package/src/model/selection-model.ts +75 -44
  135. package/src/model/table-model.test.ts +93 -56
  136. package/src/model/table-model.ts +437 -170
  137. package/src/model/table-presentation.test.ts +67 -56
  138. package/src/model/table-presentation.ts +45 -41
  139. package/src/playwright/TableManager.ts +13 -6
  140. package/src/playwright/playwright.config.ts +1 -1
  141. package/src/playwright/smoke.spec.ts +59 -13
  142. package/src/testing/useTestTableModel.ts +48 -39
  143. package/src/translations.ts +20 -22
  144. package/src/types/Table.ts +79 -0
  145. package/src/types/index.ts +1 -1
  146. package/src/util/dynamic-table.test.ts +70 -0
  147. package/src/util/dynamic-table.ts +43 -31
  148. package/src/util/index.ts +2 -0
  149. package/src/util/query-ast.ts +22 -0
  150. package/src/util/schema.ts +4 -4
  151. package/src/util/sort.ts +90 -0
  152. package/src/util/table-buttons.ts +1 -1
  153. package/src/util/table-controls.ts +2 -2
  154. package/dist/lib/browser/chunk-63UUF4IG.mjs +0 -66
  155. package/dist/lib/browser/chunk-63UUF4IG.mjs.map +0 -7
  156. package/dist/lib/browser/chunk-GPZSXPSA.mjs +0 -1822
  157. package/dist/lib/browser/chunk-GPZSXPSA.mjs.map +0 -7
  158. package/dist/lib/node-esm/chunk-K734DJB7.mjs +0 -67
  159. package/dist/lib/node-esm/chunk-K734DJB7.mjs.map +0 -7
  160. package/dist/lib/node-esm/chunk-XUD6W42C.mjs +0 -1823
  161. package/dist/lib/node-esm/chunk-XUD6W42C.mjs.map +0 -7
  162. package/dist/types/src/components/Table/CreateRefPanel.d.ts +0 -9
  163. package/dist/types/src/components/Table/CreateRefPanel.d.ts.map +0 -1
  164. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts +0 -21
  165. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +0 -1
  166. package/dist/types/src/components/TableToolbar/index.d.ts +0 -2
  167. package/dist/types/src/components/TableToolbar/index.d.ts.map +0 -1
  168. package/dist/types/src/model/table-sorting.d.ts +0 -52
  169. package/dist/types/src/model/table-sorting.d.ts.map +0 -1
  170. package/dist/types/src/types/schema.d.ts +0 -28
  171. package/dist/types/src/types/schema.d.ts.map +0 -1
  172. package/src/components/Table/CreateRefPanel.tsx +0 -103
  173. package/src/components/TableToolbar/TableToolbar.tsx +0 -102
  174. package/src/components/TableToolbar/index.ts +0 -5
  175. package/src/model/table-sorting.ts +0 -225
  176. package/src/types/schema.ts +0 -73
@@ -2,95 +2,76 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { type Schema } from 'effect';
5
+ import type * as Schema from 'effect/Schema';
6
6
  import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
7
7
 
8
- import { Filter, Obj } from '@dxos/echo';
9
- import { Ref, type TypeAnnotation, getValue } from '@dxos/echo-schema';
8
+ import { Entity, type Type } from '@dxos/echo';
9
+ import { Ref, getValue } from '@dxos/echo/internal';
10
10
  import { invariant } from '@dxos/invariant';
11
- import { getSnapshot } from '@dxos/live-object';
12
- import { type Client } from '@dxos/react-client';
13
- import { getSpace } from '@dxos/react-client/echo';
14
- import { Popover } from '@dxos/react-ui';
15
- import { Form, type FormProps } from '@dxos/react-ui-form';
16
- import { cellQuery, parseCellIndex, useGridContext } from '@dxos/react-ui-grid';
11
+ import { type Label, Popover } from '@dxos/react-ui';
12
+ import { Form, type FormRootProps, type RefFieldProps } from '@dxos/react-ui-form';
13
+ import { parseCellIndex, useGridContext } from '@dxos/react-ui-grid';
17
14
  import { type FieldProjection } from '@dxos/schema';
18
- import { getDeep, isNotFalsy, setDeep } from '@dxos/util';
15
+ import { getDeep, isTruthy, setDeep } from '@dxos/util';
19
16
 
20
- import { type ModalController, type TableModel } from '../../model';
21
- import { translationKey } from '../../translations';
17
+ import { translationKey } from '#translations';
18
+
19
+ import { type ModalController, type TableModel, type TableRow } from '../../model';
22
20
  import { narrowSchema } from '../../util';
23
21
 
24
- type FormCellEditorProps = {
25
- fieldProjection: FieldProjection;
22
+ const createOptionLabel: Label = ['create-new-object.label', { ns: translationKey }];
23
+
24
+ export type OnCreateHandler = (schema: Type.AnyEntity, values: any) => Parameters<typeof Ref.make>[0];
25
+
26
+ export type FormCellEditorProps<T extends Type.AnyEntity = Type.AnyEntity> = {
27
+ __gridScope: any;
28
+ schema?: T;
26
29
  model?: TableModel;
27
- schema?: Schema.Schema.AnyNoContext;
30
+ fieldProjection: FieldProjection;
31
+ modals?: ModalController; // TODO(burdon): Not used. Remove?
28
32
  onSave?: () => void;
29
- client?: Client;
30
- modals?: ModalController;
31
- __gridScope: any;
32
- } & Omit<FormProps<any>, 'values' | 'schema'>;
33
-
34
- const createOptionLabel = ['create new object label', { ns: translationKey }] as [string, { ns: string }];
33
+ onCreate?: OnCreateHandler;
34
+ } & Omit<FormRootProps<any>, 'values' | 'schema' | 'onCreate'>;
35
35
 
36
- export const FormCellEditor = ({
37
- fieldProjection,
38
- model,
36
+ export const FormCellEditor = <T extends Type.AnyEntity = Type.AnyEntity>({
37
+ __gridScope,
39
38
  schema,
39
+ model,
40
+ fieldProjection,
40
41
  onSave,
41
- client,
42
- modals,
43
- __gridScope,
42
+ onCreate,
44
43
  ...formProps
45
- }: FormCellEditorProps) => {
46
- const { editing: contextEditing, setEditing, id: gridId } = useGridContext('ArrayEditor', __gridScope);
47
- const [editing, setLocalEditing] = useState(false);
44
+ }: FormCellEditorProps<T>) => {
48
45
  const anchorRef = useRef<HTMLButtonElement | null>(null);
46
+ const { id: _gridId, editing: contextEditing, setEditing } = useGridContext('ArrayEditor', __gridScope);
47
+ const [editing, setLocalEditing] = useState(false);
49
48
 
50
- const getSchema = useCallback(
51
- (typeAnnotation: TypeAnnotation) => {
52
- const space = getSpace(model!.view);
53
- invariant(space);
54
-
55
- let schema;
56
- if (client) {
57
- schema = client.graph.schemaRegistry.getSchema(typeAnnotation.typename);
58
- }
59
- if (!schema) {
60
- schema = space.db.schemaRegistry.getSchema(typeAnnotation.typename);
61
- }
62
- return { space, schema };
63
- },
64
- [client, model],
65
- );
49
+ useEffect(() => {
50
+ if (contextEditing && contextEditing.cellElement) {
51
+ anchorRef.current = (contextEditing.cellElement as HTMLElement).querySelector(
52
+ '.dx-grid__cell__content',
53
+ ) as HTMLButtonElement;
54
+ setLocalEditing(true);
55
+ } else {
56
+ anchorRef.current = null;
57
+ setLocalEditing(false);
58
+ }
59
+ }, [contextEditing]);
66
60
 
67
- const handleQueryRefOptions = useCallback(
68
- async (typeAnnotation: TypeAnnotation) => {
69
- const { schema, space } = getSchema(typeAnnotation);
70
- if (model && schema && space) {
71
- const { objects } = await space.db.query(Filter.type(schema)).run();
72
- return objects
73
- .map((obj) => {
74
- return {
75
- dxn: Obj.getDXN(obj),
76
- label: getValue(obj, fieldProjection.field.referencePath!) || obj.id.toString(),
77
- };
78
- })
79
- .filter(isNotFalsy);
80
- }
61
+ const narrowedSchema = useMemo<Schema.Schema.AnyNoContext | undefined>(() => {
62
+ if (!schema) {
63
+ return undefined;
64
+ }
81
65
 
82
- return [];
83
- },
84
- [client, model],
85
- );
66
+ return narrowSchema(schema, [fieldProjection.field.path]);
67
+ }, [JSON.stringify(schema), fieldProjection.field.path]); // TODO(burdon): Avoid stringify.
86
68
 
87
- const originalRow = useMemo(() => {
69
+ const originalRow = useMemo<TableRow | undefined>(() => {
88
70
  if (model && contextEditing) {
89
- const cell = parseCellIndex(contextEditing.index);
90
-
91
71
  // Check if this is a draft cell and get the appropriate row data
72
+ const cell = parseCellIndex(contextEditing.index);
92
73
  if (model.isDraftCell(cell)) {
93
- const draftRow = model.draftRows.value[cell.row];
74
+ const draftRow = model.getDraftRows()[cell.row];
94
75
  invariant(draftRow);
95
76
  return draftRow.data;
96
77
  } else {
@@ -103,79 +84,81 @@ export const FormCellEditor = ({
103
84
  return undefined;
104
85
  }, [model, contextEditing]);
105
86
 
106
- const handleSave = useCallback(
107
- (values: any) => {
87
+ // NOTE: Important to get a mutable deep clone to eject from the echo object.
88
+ // TODO(wittjosiah): Consider using something like Obj.clone for this use case.
89
+ const initialFormValues = useMemo(() => (originalRow ? JSON.parse(JSON.stringify(originalRow)) : {}), [originalRow]);
90
+ const [formValues, setFormValues] = useState<any>(initialFormValues);
91
+
92
+ const handleValuesChanged = useCallback<NonNullable<FormRootProps<any>['onValuesChanged']>>((values) => {
93
+ setFormValues(values);
94
+ }, []);
95
+ const handleOpenChange = useCallback((nextOpen: boolean) => {
96
+ if (nextOpen === false) {
97
+ setEditing(null);
98
+ onSave?.();
99
+ }
100
+ setLocalEditing(nextOpen);
101
+ }, []);
102
+
103
+ const handleSave = useCallback<NonNullable<FormRootProps<any>['onSave']>>(
104
+ (values) => {
105
+ if (!originalRow) {
106
+ return;
107
+ }
108
108
  const path = fieldProjection.field.path;
109
109
  const value = getDeep(values, [path]);
110
- setDeep(originalRow, [path], value);
110
+ // Use model's changeRow for consistent mutation handling.
111
+ if (model) {
112
+ model.changeRow(originalRow, (mutableRow) => {
113
+ setDeep(mutableRow, [path], value);
114
+ });
115
+ } else {
116
+ setDeep(originalRow, [path], value);
117
+ }
111
118
  contextEditing?.cellElement?.focus();
112
119
  setEditing(null);
113
120
  setLocalEditing(false);
114
121
  onSave?.();
115
122
  },
116
- [fieldProjection.field.path, onSave, contextEditing, originalRow],
123
+ [fieldProjection.field.path, onSave, contextEditing, originalRow, model],
117
124
  );
118
125
 
119
- const handleCreate = useCallback(
120
- (object: any) => {
121
- const ref = Ref.make(object);
122
- const path = fieldProjection.field.path;
123
- setDeep(originalRow, [path], ref);
126
+ const handleCreate = useCallback<NonNullable<FormRootProps<any>['onCreate']>>(
127
+ (schema, values) => {
128
+ const objectWithId = onCreate?.(schema, values);
129
+ if (objectWithId && originalRow) {
130
+ const ref = Ref.make(objectWithId);
131
+ const path = fieldProjection.field.path;
132
+ // Use model's changeRow for consistent mutation handling.
133
+ if (model) {
134
+ model.changeRow(originalRow, (mutableRow) => {
135
+ setDeep(mutableRow, [path], ref);
136
+ });
137
+ } else {
138
+ setDeep(originalRow, [path], ref);
139
+ }
140
+ }
124
141
  contextEditing?.cellElement?.focus();
125
142
  setEditing(null);
126
143
  setLocalEditing(false);
127
144
  onSave?.();
128
145
  },
129
- [fieldProjection.field.path, onSave, contextEditing, originalRow],
146
+ [fieldProjection.field.path, onSave, contextEditing, originalRow, onCreate, model],
130
147
  );
131
148
 
132
- const handleCreateFromQuery = useCallback(
133
- async (typeAnnotation: TypeAnnotation, query: string) => {
134
- if (model && modals && contextEditing?.index) {
135
- setLocalEditing(false);
136
- modals.openCreateRef(
137
- typeAnnotation.typename,
138
- document.querySelector(cellQuery(contextEditing.index, gridId)),
139
- {
140
- [fieldProjection.field.referencePath!]: query,
141
- },
142
- handleCreate,
143
- );
144
- }
145
- },
146
- [model, modals, client, contextEditing?.index, gridId, fieldProjection, handleSave],
149
+ const getOptions = useCallback<NonNullable<RefFieldProps['getOptions']>>(
150
+ (results) =>
151
+ results
152
+ .map((obj) => {
153
+ return {
154
+ id: Entity.getDXN(obj).toString(),
155
+ label: getValue(obj, fieldProjection.field.referencePath!) || obj.id.toString(),
156
+ };
157
+ })
158
+ .filter(isTruthy),
159
+ [fieldProjection],
147
160
  );
148
161
 
149
- useEffect(() => {
150
- if (contextEditing && contextEditing.cellElement) {
151
- anchorRef.current = (contextEditing.cellElement as HTMLElement).querySelector(
152
- '.dx-grid__cell__content',
153
- ) as HTMLButtonElement;
154
- setLocalEditing(true);
155
- } else {
156
- anchorRef.current = null;
157
- setLocalEditing(false);
158
- }
159
- }, [contextEditing]);
160
-
161
- const narrowedSchema = useMemo(() => {
162
- if (!schema) {
163
- return undefined;
164
- }
165
- return narrowSchema(schema, [fieldProjection.field.path]);
166
- }, [JSON.stringify(schema), fieldProjection.field.path]); // TODO(burdon): Avoid stringify.
167
-
168
- // NOTE: Important to get a snapshot to eject from the live object.
169
- const formValues = useMemo(() => (originalRow ? getSnapshot(originalRow) : {}), [originalRow]);
170
-
171
- const handleOpenChange = useCallback((nextOpen: boolean) => {
172
- if (nextOpen === false) {
173
- setEditing(null);
174
- onSave?.();
175
- }
176
- setLocalEditing(nextOpen);
177
- }, []);
178
-
179
162
  if (!editing) {
180
163
  return null;
181
164
  }
@@ -184,20 +167,31 @@ export const FormCellEditor = ({
184
167
  <Popover.Root open={editing} onOpenChange={handleOpenChange}>
185
168
  <Popover.VirtualTrigger virtualRef={anchorRef} />
186
169
  <Popover.Portal>
187
- <Popover.Content tabIndex={-1} classNames='popover-card-width density-fine'>
170
+ <Popover.Content tabIndex={-1} classNames='dx-card-popover-width dx-density-fine'>
188
171
  <Popover.Arrow />
189
172
  <Popover.Viewport>
190
- <Form
173
+ <Form.Root
174
+ {...formProps}
191
175
  autoFocus
176
+ schema={narrowedSchema}
192
177
  values={formValues}
193
- schema={narrowedSchema as any}
194
- onSave={handleSave}
195
- {...formProps}
196
- onQueryRefOptions={handleQueryRefOptions}
197
- onCreateFromQuery={handleCreateFromQuery}
178
+ onValuesChanged={handleValuesChanged}
179
+ projection={model?.projection}
180
+ createInitialValuePath={fieldProjection.field.referencePath}
198
181
  createOptionIcon='ph--plus--regular'
199
182
  createOptionLabel={createOptionLabel}
200
- />
183
+ db={model?.db}
184
+ getOptions={getOptions}
185
+ onCreate={handleCreate}
186
+ onSave={handleSave}
187
+ >
188
+ <Form.Viewport>
189
+ <Form.Content>
190
+ <Form.FieldSet />
191
+ <Form.Actions />
192
+ </Form.Content>
193
+ </Form.Viewport>
194
+ </Form.Root>
201
195
  </Popover.Viewport>
202
196
  </Popover.Content>
203
197
  </Popover.Portal>
@@ -2,66 +2,40 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
8
- import React, { useEffect, useMemo, useState } from 'react';
6
+ import React from 'react';
9
7
 
10
8
  import { Obj } from '@dxos/echo';
11
- import { type EchoSchema, isMutable } from '@dxos/echo-schema';
12
- import { invariant } from '@dxos/invariant';
13
- import { faker } from '@dxos/random';
14
- import { Filter, useQuery } from '@dxos/react-client/echo';
15
- import { useClientProvider, withClientProvider } from '@dxos/react-client/testing';
9
+ import { View } from '@dxos/echo';
10
+ import { random } from '@dxos/random';
11
+ import { withClientProvider } from '@dxos/react-client/testing';
16
12
  import { Grid, type GridEditing, defaultRowSize } from '@dxos/react-ui-grid';
17
- import { DataType, createDefaultSchema, typenameFromQuery } from '@dxos/schema';
18
- import { withLayout, withTheme } from '@dxos/storybook-utils';
13
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
14
+ import { ViewModel } from '@dxos/schema';
15
+ import { Task } from '@dxos/types';
19
16
 
20
- import { useTableModel } from '../../hooks';
21
- import { type TableFeatures } from '../../model';
22
- import { translations } from '../../translations';
23
- import { Table } from '../../types';
17
+ import { translations } from '#translations';
24
18
 
19
+ import { useTestTableModel } from '../../testing';
20
+ import { Table } from '../../types';
25
21
  import { TableCellEditor } from './TableCellEditor';
26
22
 
27
- type StoryProps = {
23
+ type DefaultStoryProps = {
28
24
  editing: GridEditing;
29
25
  };
30
26
 
31
- const DefaultStory = ({ editing }: StoryProps) => {
32
- const { space } = useClientProvider();
33
- invariant(space);
27
+ // TODO(burdon): Broken layout.
28
+ const DefaultStory = ({ editing }: DefaultStoryProps) => {
29
+ const { model, table } = useTestTableModel();
34
30
 
35
- const views = useQuery(space, Filter.type(DataType.View));
36
- const [view, setView] = useState<DataType.View>();
37
- const [schema, setSchema] = useState<EchoSchema>();
38
- useEffect(() => {
39
- if (space && views.length && !view) {
40
- const view = views[0];
41
- setView(view);
42
- setSchema(space.db.schemaRegistry.getSchema(typenameFromQuery(view.query)!));
43
- }
44
- }, [space, views, view]);
45
-
46
- const features: Partial<TableFeatures> = useMemo(
47
- () => ({
48
- selection: { enabled: true, mode: 'multiple' },
49
- dataEditable: true,
50
- schemaEditable: schema && isMutable(schema),
51
- }),
52
- [schema],
53
- );
54
-
55
- const model = useTableModel({ view, schema: schema?.jsonSchema, features });
56
-
57
- if (!model || !schema || !view) {
31
+ if (!model || !table) {
58
32
  return <div />;
59
33
  }
60
34
 
61
35
  return (
62
- <div className='flex w-[300px] border border-separator' style={{ height: defaultRowSize }}>
36
+ <div className='border border-separator' style={{ height: defaultRowSize }}>
63
37
  <Grid.Root id='test' editing={editing}>
64
- <TableCellEditor model={model} schema={schema} />
38
+ <TableCellEditor model={model} schema={Task.Task} />
65
39
  </Grid.Root>
66
40
  </div>
67
41
  );
@@ -71,30 +45,33 @@ const meta = {
71
45
  title: 'ui/react-ui-table/TableCellEditor',
72
46
  component: DefaultStory,
73
47
  render: DefaultStory,
74
- parameters: { translations, layout: 'centered' },
75
48
  decorators: [
49
+ withTheme(),
50
+ withLayout({ layout: 'column' }),
76
51
  withClientProvider({
77
- types: [DataType.View, Table.Table],
52
+ types: [View.View, Task.Task, Table.Table],
78
53
  createIdentity: true,
79
54
  createSpace: true,
80
- onSpaceCreated: async ({ client, space }) => {
81
- const schema = createDefaultSchema();
82
- const { view } = await Table.makeView({ client, space, typename: schema.typename });
83
- space.db.add(view);
55
+ onCreateSpace: async ({ space }) => {
56
+ const { view, jsonSchema } = await ViewModel.makeFromDatabase({ db: space.db, typename: Task.Task.typename });
57
+ const table = Table.make({ view, jsonSchema });
58
+ space.db.add(table);
84
59
  Array.from({ length: 10 }).forEach(() => {
85
60
  space.db.add(
86
- Obj.make(schema, {
87
- title: faker.person.fullName(),
88
- status: faker.helpers.arrayElement(['todo', 'in-progress', 'done'] as const),
89
- description: faker.lorem.sentence(),
61
+ Obj.make(Task.Task, {
62
+ title: random.person.fullName(),
63
+ status: random.helpers.arrayElement(['todo', 'in-progress', 'done'] as const),
64
+ description: random.lorem.sentence(),
90
65
  }),
91
66
  );
92
67
  });
93
68
  },
94
69
  }),
95
- withTheme,
96
- withLayout(),
97
70
  ],
71
+ parameters: {
72
+ layout: 'fullscreen',
73
+ translations,
74
+ },
98
75
  } satisfies Meta<typeof DefaultStory>;
99
76
 
100
77
  export default meta;