@dxos/react-ui-table 0.8.3 → 0.8.4-main.03d5cd7b56

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 (194) 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 +1162 -2554
  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 +115 -0
  11. package/dist/lib/browser/testing/index.mjs.map +7 -0
  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 -5
  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 +1162 -2554
  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 +116 -0
  25. package/dist/lib/node-esm/testing/index.mjs.map +7 -0
  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 -5
  29. package/dist/types/src/components/Table/ColumnActionsMenu.d.ts +2 -3
  30. package/dist/types/src/components/Table/ColumnActionsMenu.d.ts.map +1 -1
  31. package/dist/types/src/components/Table/ColumnSettings.d.ts +4 -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 +35 -3
  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 +111 -0
  38. package/dist/types/src/components/Table/Relations.stories.d.ts.map +1 -0
  39. package/dist/types/src/components/Table/RowActionsMenu.d.ts +1 -1
  40. package/dist/types/src/components/Table/RowActionsMenu.d.ts.map +1 -1
  41. package/dist/types/src/components/Table/Table.d.ts +21 -18
  42. package/dist/types/src/components/Table/Table.d.ts.map +1 -1
  43. package/dist/types/src/components/Table/Table.stories.d.ts +110 -6
  44. package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
  45. package/dist/types/src/components/Table/TableContent.d.ts +17 -0
  46. package/dist/types/src/components/Table/TableContent.d.ts.map +1 -0
  47. package/dist/types/src/components/Table/TableToolbar.d.ts +21 -0
  48. package/dist/types/src/components/Table/TableToolbar.d.ts.map +1 -0
  49. package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts +2 -1
  50. package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts.map +1 -1
  51. package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +14 -9
  52. package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
  53. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +12 -18
  54. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
  55. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts +38 -5
  56. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
  57. package/dist/types/src/components/TableCellEditor/index.d.ts +1 -0
  58. package/dist/types/src/components/TableCellEditor/index.d.ts.map +1 -1
  59. package/dist/types/src/components/index.d.ts +0 -1
  60. package/dist/types/src/components/index.d.ts.map +1 -1
  61. package/dist/types/src/components/testing.d.ts +26 -56
  62. package/dist/types/src/components/testing.d.ts.map +1 -1
  63. package/dist/types/src/hooks/index.d.ts +2 -0
  64. package/dist/types/src/hooks/index.d.ts.map +1 -1
  65. package/dist/types/src/hooks/useAddRow.d.ts +13 -0
  66. package/dist/types/src/hooks/useAddRow.d.ts.map +1 -0
  67. package/dist/types/src/hooks/useProjectionModel.d.ts +6 -0
  68. package/dist/types/src/hooks/useProjectionModel.d.ts.map +1 -0
  69. package/dist/types/src/hooks/useTableModel.d.ts +11 -10
  70. package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
  71. package/dist/types/src/index.d.ts +0 -3
  72. package/dist/types/src/index.d.ts.map +1 -1
  73. package/dist/types/src/model/modal-controller.d.ts +5 -8
  74. package/dist/types/src/model/modal-controller.d.ts.map +1 -1
  75. package/dist/types/src/model/selection-model.d.ts +18 -8
  76. package/dist/types/src/model/selection-model.d.ts.map +1 -1
  77. package/dist/types/src/model/table-model.d.ts +150 -32
  78. package/dist/types/src/model/table-model.d.ts.map +1 -1
  79. package/dist/types/src/model/table-presentation.d.ts +13 -2
  80. package/dist/types/src/model/table-presentation.d.ts.map +1 -1
  81. package/dist/types/src/playwright/TableManager.d.ts +2 -1
  82. package/dist/types/src/playwright/TableManager.d.ts.map +1 -1
  83. package/dist/types/src/playwright/playwright.config.d.ts +3 -0
  84. package/dist/types/src/playwright/playwright.config.d.ts.map +1 -0
  85. package/dist/types/src/testing/index.d.ts +2 -0
  86. package/dist/types/src/testing/index.d.ts.map +1 -0
  87. package/dist/types/src/testing/useTestTableModel.d.ts +25 -0
  88. package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -0
  89. package/dist/types/src/translations.d.ts +22 -25
  90. package/dist/types/src/translations.d.ts.map +1 -1
  91. package/dist/types/src/types/Table.d.ts +26 -0
  92. package/dist/types/src/types/Table.d.ts.map +1 -0
  93. package/dist/types/src/types/index.d.ts +1 -1
  94. package/dist/types/src/types/index.d.ts.map +1 -1
  95. package/dist/types/src/util/dynamic-table.d.ts +15 -14
  96. package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
  97. package/dist/types/src/util/dynamic-table.test.d.ts +2 -0
  98. package/dist/types/src/util/dynamic-table.test.d.ts.map +1 -0
  99. package/dist/types/src/util/index.d.ts +2 -1
  100. package/dist/types/src/util/index.d.ts.map +1 -1
  101. package/dist/types/src/util/query-ast.d.ts +7 -0
  102. package/dist/types/src/util/query-ast.d.ts.map +1 -0
  103. package/dist/types/src/util/schema.d.ts +1 -1
  104. package/dist/types/src/util/schema.d.ts.map +1 -1
  105. package/dist/types/src/util/signals.d.ts.map +1 -1
  106. package/dist/types/src/util/sort.d.ts +12 -0
  107. package/dist/types/src/util/sort.d.ts.map +1 -0
  108. package/dist/types/src/util/table-buttons.d.ts +31 -17
  109. package/dist/types/src/util/table-buttons.d.ts.map +1 -1
  110. package/dist/types/src/util/table-controls.d.ts +4 -4
  111. package/dist/types/src/util/table-controls.d.ts.map +1 -1
  112. package/dist/types/src/util/tag.d.ts.map +1 -1
  113. package/dist/types/tsconfig.tsbuildinfo +1 -1
  114. package/package.json +76 -72
  115. package/src/components/Table/ColumnActionsMenu.tsx +18 -13
  116. package/src/components/Table/ColumnSettings.tsx +18 -15
  117. package/src/components/Table/DynamicTable.stories.tsx +40 -36
  118. package/src/components/Table/DynamicTable.tsx +38 -34
  119. package/src/components/Table/Relations.stories.tsx +281 -0
  120. package/src/components/Table/RowActionsMenu.tsx +29 -19
  121. package/src/components/Table/Table.stories.tsx +198 -214
  122. package/src/components/Table/Table.tsx +47 -363
  123. package/src/components/Table/TableContent.tsx +421 -0
  124. package/src/components/Table/TableToolbar.tsx +111 -0
  125. package/src/components/TableCellEditor/CellValidationMessage.tsx +9 -5
  126. package/src/components/TableCellEditor/FormCellEditor.tsx +153 -49
  127. package/src/components/TableCellEditor/TableCellEditor.stories.tsx +40 -79
  128. package/src/components/TableCellEditor/TableCellEditor.tsx +109 -199
  129. package/src/components/TableCellEditor/index.ts +1 -0
  130. package/src/components/index.ts +0 -1
  131. package/src/components/testing.ts +46 -37
  132. package/src/hooks/index.ts +2 -0
  133. package/src/hooks/useAddRow.ts +36 -0
  134. package/src/hooks/useProjectionModel.ts +39 -0
  135. package/src/hooks/useTableModel.ts +41 -35
  136. package/src/index.ts +0 -5
  137. package/src/model/modal-controller.ts +24 -41
  138. package/src/model/selection-model.ts +75 -41
  139. package/src/model/table-model.test.ts +91 -56
  140. package/src/model/table-model.ts +629 -162
  141. package/src/model/table-presentation.test.ts +69 -59
  142. package/src/model/table-presentation.ts +283 -154
  143. package/src/playwright/TableManager.ts +13 -6
  144. package/src/playwright/playwright.config.ts +17 -0
  145. package/src/playwright/smoke.spec.ts +181 -42
  146. package/src/testing/index.ts +5 -0
  147. package/src/testing/useTestTableModel.ts +133 -0
  148. package/src/translations.ts +24 -24
  149. package/src/types/Table.ts +79 -0
  150. package/src/types/index.ts +2 -2
  151. package/src/util/dynamic-table.test.ts +70 -0
  152. package/src/util/dynamic-table.ts +51 -36
  153. package/src/util/index.ts +2 -1
  154. package/src/util/query-ast.ts +22 -0
  155. package/src/util/schema.ts +4 -4
  156. package/src/util/sort.ts +90 -0
  157. package/src/util/table-buttons.ts +28 -4
  158. package/src/util/table-controls.ts +6 -3
  159. package/dist/lib/browser/chunk-TL7CKQQV.mjs +0 -27
  160. package/dist/lib/browser/chunk-TL7CKQQV.mjs.map +0 -7
  161. package/dist/lib/node/chunk-O2VPBNS5.cjs +0 -50
  162. package/dist/lib/node/chunk-O2VPBNS5.cjs.map +0 -7
  163. package/dist/lib/node/index.cjs +0 -2739
  164. package/dist/lib/node/index.cjs.map +0 -7
  165. package/dist/lib/node/meta.json +0 -1
  166. package/dist/lib/node/types/index.cjs +0 -31
  167. package/dist/lib/node/types/index.cjs.map +0 -7
  168. package/dist/lib/node-esm/chunk-GBT6GD4G.mjs +0 -29
  169. package/dist/lib/node-esm/chunk-GBT6GD4G.mjs.map +0 -7
  170. package/dist/types/src/components/Table/CreateRefPanel.d.ts +0 -9
  171. package/dist/types/src/components/Table/CreateRefPanel.d.ts.map +0 -1
  172. package/dist/types/src/components/Table/relations.stories.d.ts +0 -9
  173. package/dist/types/src/components/Table/relations.stories.d.ts.map +0 -1
  174. package/dist/types/src/components/TableCellEditor/extension.d.ts +0 -13
  175. package/dist/types/src/components/TableCellEditor/extension.d.ts.map +0 -1
  176. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts +0 -21
  177. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +0 -1
  178. package/dist/types/src/components/TableToolbar/index.d.ts +0 -2
  179. package/dist/types/src/components/TableToolbar/index.d.ts.map +0 -1
  180. package/dist/types/src/model/table-sorting.d.ts +0 -53
  181. package/dist/types/src/model/table-sorting.d.ts.map +0 -1
  182. package/dist/types/src/types/table.d.ts +0 -56
  183. package/dist/types/src/types/table.d.ts.map +0 -1
  184. package/dist/types/src/util/table.d.ts +0 -14
  185. package/dist/types/src/util/table.d.ts.map +0 -1
  186. package/src/components/Table/CreateRefPanel.tsx +0 -84
  187. package/src/components/Table/relations.stories.tsx +0 -124
  188. package/src/components/TableCellEditor/extension.ts +0 -61
  189. package/src/components/TableToolbar/TableToolbar.tsx +0 -102
  190. package/src/components/TableToolbar/index.ts +0 -5
  191. package/src/model/table-sorting.ts +0 -216
  192. package/src/playwright/playwright.config.cts +0 -18
  193. package/src/types/table.ts +0 -27
  194. package/src/util/table.ts +0 -127
@@ -2,80 +2,161 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { type Schema } from 'effect';
6
- import React, { useCallback, useEffect, useMemo, useRef } from 'react';
5
+ import type * as Schema from 'effect/Schema';
6
+ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
7
7
 
8
- import { getSnapshot } from '@dxos/echo-schema';
8
+ import { Entity, type Type } from '@dxos/echo';
9
+ import { Ref, getValue } from '@dxos/echo/internal';
9
10
  import { invariant } from '@dxos/invariant';
10
- import { Popover } from '@dxos/react-ui';
11
- import { Form } from '@dxos/react-ui-form';
11
+ import { type Label, Popover } from '@dxos/react-ui';
12
+ import { Form, type FormRootProps, type RefFieldProps } from '@dxos/react-ui-form';
12
13
  import { parseCellIndex, useGridContext } from '@dxos/react-ui-grid';
13
14
  import { type FieldProjection } from '@dxos/schema';
14
- import { getDeep, setDeep } from '@dxos/util';
15
+ import { getDeep, isTruthy, setDeep } from '@dxos/util';
15
16
 
16
- import { type TableModel } from '../../model';
17
+ import { translationKey } from '#translations';
18
+
19
+ import { type ModalController, type TableModel, type TableRow } from '../../model';
17
20
  import { narrowSchema } from '../../util';
18
21
 
19
- type FormCellEditorProps = {
20
- fieldProjection: FieldProjection;
21
- model?: TableModel;
22
- schema?: Schema.Schema.AnyNoContext;
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> = {
23
27
  __gridScope: any;
24
- };
28
+ schema?: T;
29
+ model?: TableModel;
30
+ fieldProjection: FieldProjection;
31
+ modals?: ModalController; // TODO(burdon): Not used. Remove?
32
+ onSave?: () => void;
33
+ onCreate?: OnCreateHandler;
34
+ } & Omit<FormRootProps<any>, 'values' | 'schema' | 'onCreate'>;
25
35
 
26
- export const FormCellEditor = ({ fieldProjection, model, schema, __gridScope }: FormCellEditorProps) => {
27
- const { editing, setEditing } = useGridContext('ArrayEditor', __gridScope);
28
- const cellRef = useRef<HTMLButtonElement | null>(null);
36
+ export const FormCellEditor = <T extends Type.AnyEntity = Type.AnyEntity>({
37
+ __gridScope,
38
+ schema,
39
+ model,
40
+ fieldProjection,
41
+ onSave,
42
+ onCreate,
43
+ ...formProps
44
+ }: FormCellEditorProps<T>) => {
45
+ const anchorRef = useRef<HTMLButtonElement | null>(null);
46
+ const { id: _gridId, editing: contextEditing, setEditing } = useGridContext('ArrayEditor', __gridScope);
47
+ const [editing, setLocalEditing] = useState(false);
29
48
 
30
49
  useEffect(() => {
31
- if (editing && editing.cellElement) {
32
- cellRef.current = editing.cellElement as HTMLButtonElement;
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);
33
58
  }
34
- }, [editing?.cellElement]);
59
+ }, [contextEditing]);
35
60
 
36
- const narrowedSchema = useMemo(() => {
61
+ const narrowedSchema = useMemo<Schema.Schema.AnyNoContext | undefined>(() => {
37
62
  if (!schema) {
38
63
  return undefined;
39
64
  }
65
+
40
66
  return narrowSchema(schema, [fieldProjection.field.path]);
41
67
  }, [JSON.stringify(schema), fieldProjection.field.path]); // TODO(burdon): Avoid stringify.
42
68
 
43
- const originalRow = useMemo(() => {
44
- if (model && editing) {
45
- const cell = parseCellIndex(editing.index);
46
- const row = model.getRowAt(cell.row);
47
- invariant(row);
48
-
49
- return row;
69
+ const originalRow = useMemo<TableRow | undefined>(() => {
70
+ if (model && contextEditing) {
71
+ // Check if this is a draft cell and get the appropriate row data
72
+ const cell = parseCellIndex(contextEditing.index);
73
+ if (model.isDraftCell(cell)) {
74
+ const draftRow = model.getDraftRows()[cell.row];
75
+ invariant(draftRow);
76
+ return draftRow.data;
77
+ } else {
78
+ const row = model.getRowAt(cell.row);
79
+ invariant(row);
80
+ return row;
81
+ }
50
82
  }
51
83
 
52
84
  return undefined;
53
- }, [model, editing]);
85
+ }, [model, contextEditing]);
86
+
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);
54
91
 
55
- const formValues = useMemo(() => {
56
- if (originalRow) {
57
- // NOTE(ZaymonFC): Important to get a snapshot to eject from the live object.
58
- return getSnapshot(originalRow);
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?.();
59
99
  }
60
- }, [originalRow]);
100
+ setLocalEditing(nextOpen);
101
+ }, []);
61
102
 
62
- const handleSave = useCallback(
63
- (values: any) => {
103
+ const handleSave = useCallback<NonNullable<FormRootProps<any>['onSave']>>(
104
+ (values) => {
105
+ if (!originalRow) {
106
+ return;
107
+ }
64
108
  const path = fieldProjection.field.path;
65
109
  const value = getDeep(values, [path]);
66
- 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
+ }
118
+ contextEditing?.cellElement?.focus();
67
119
  setEditing(null);
120
+ setLocalEditing(false);
121
+ onSave?.();
68
122
  },
69
- [fieldProjection.field.path, originalRow],
123
+ [fieldProjection.field.path, onSave, contextEditing, originalRow, model],
70
124
  );
71
125
 
72
- const handleOpenChange = useCallback(
73
- (nextOpen: boolean) => {
74
- if (nextOpen === false) {
75
- setEditing(null);
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
+ }
76
140
  }
141
+ contextEditing?.cellElement?.focus();
142
+ setEditing(null);
143
+ setLocalEditing(false);
144
+ onSave?.();
77
145
  },
78
- [setEditing],
146
+ [fieldProjection.field.path, onSave, contextEditing, originalRow, onCreate, model],
147
+ );
148
+
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],
79
160
  );
80
161
 
81
162
  if (!editing) {
@@ -83,14 +164,37 @@ export const FormCellEditor = ({ fieldProjection, model, schema, __gridScope }:
83
164
  }
84
165
 
85
166
  return (
86
- <Popover.Root open={editing !== null} onOpenChange={handleOpenChange}>
87
- <Popover.VirtualTrigger virtualRef={cellRef} />
88
- <Popover.Content tabIndex={-1} classNames='popover-card-width'>
89
- <Popover.Arrow />
90
- <Popover.Viewport>
91
- <Form values={formValues} schema={narrowedSchema as any} onSave={handleSave} />
92
- </Popover.Viewport>
93
- </Popover.Content>
167
+ <Popover.Root open={editing} onOpenChange={handleOpenChange}>
168
+ <Popover.VirtualTrigger virtualRef={anchorRef} />
169
+ <Popover.Portal>
170
+ <Popover.Content tabIndex={-1} classNames='dx-card-popover-width dx-density-fine'>
171
+ <Popover.Arrow />
172
+ <Popover.Viewport>
173
+ <Form.Root
174
+ {...formProps}
175
+ autoFocus
176
+ schema={narrowedSchema}
177
+ values={formValues}
178
+ onValuesChanged={handleValuesChanged}
179
+ projection={model?.projection}
180
+ createInitialValuePath={fieldProjection.field.referencePath}
181
+ createOptionIcon='ph--plus--regular'
182
+ createOptionLabel={createOptionLabel}
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>
195
+ </Popover.Viewport>
196
+ </Popover.Content>
197
+ </Popover.Portal>
94
198
  </Popover.Root>
95
199
  );
96
200
  };
@@ -2,120 +2,81 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
- import { type Meta, type StoryObj } from '@storybook/react';
8
- import React, { useEffect, useMemo, useState } from 'react';
5
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
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, live } from '@dxos/react-client/echo';
15
- import { useClientProvider, withClientProvider } from '@dxos/react-client/testing';
16
- import { defaultRowSize, Grid, type GridEditing } from '@dxos/react-ui-grid';
17
- import { ViewProjection, ViewType } from '@dxos/schema';
18
- import { withLayout, withTheme } from '@dxos/storybook-utils';
9
+ import { View } from '@dxos/echo';
10
+ import { random } from '@dxos/random';
11
+ import { withClientProvider } from '@dxos/react-client/testing';
12
+ import { Grid, type GridEditing, defaultRowSize } from '@dxos/react-ui-grid';
13
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
14
+ import { ViewModel } from '@dxos/schema';
15
+ import { Task } from '@dxos/types';
16
+
17
+ import { translations } from '#translations';
19
18
 
20
- import { TableCellEditor, type TableCellEditorProps } from './TableCellEditor';
21
- import { useTableModel } from '../../hooks';
22
- import { type TableFeatures } from '../../model';
23
- import translations from '../../translations';
24
- import { TableType } from '../../types';
25
- import { initializeTable } from '../../util';
19
+ import { useTestTableModel } from '../../testing';
20
+ import { Table } from '../../types';
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);
34
-
35
- const tables = useQuery(space, Filter.type(TableType));
36
- const [table, setTable] = useState<TableType>();
37
- const [schema, setSchema] = useState<EchoSchema>();
38
- useEffect(() => {
39
- if (space && tables.length && !table) {
40
- const table = tables[0];
41
- invariant(table.view);
42
- setTable(table);
43
- setSchema(space.db.schemaRegistry.getSchema(table.view.target!.query.typename!));
44
- }
45
- }, [space, tables]);
27
+ // TODO(burdon): Broken layout.
28
+ const DefaultStory = ({ editing }: DefaultStoryProps) => {
29
+ const { model, table } = useTestTableModel();
46
30
 
47
- const projection = useMemo(() => {
48
- if (schema && table?.view) {
49
- return new ViewProjection(schema.jsonSchema, table.view.target!);
50
- }
51
- }, [schema, table?.view]);
52
-
53
- const features: Partial<TableFeatures> = useMemo(
54
- () => ({
55
- selection: { enabled: true, mode: 'multiple' },
56
- dataEditable: true,
57
- schemaEditable: schema && isMutable(schema),
58
- }),
59
- [schema],
60
- );
61
-
62
- const model = useTableModel({ table, projection, features });
63
-
64
- const handleQuery: TableCellEditorProps['onQuery'] = async ({ field }) => {
65
- // TODO(dmaretskyi): If no schema query nothing
66
- const { objects } = await space.db.query(schema ? Filter.type(schema) : Filter.everything()).run();
67
- return objects.map((obj) => {
68
- const label = obj[field.referencePath ?? 'id'];
69
- return {
70
- label,
71
- data: obj,
72
- };
73
- });
74
- };
75
-
76
- if (!model || !schema || !table) {
31
+ if (!model || !table) {
77
32
  return <div />;
78
33
  }
79
34
 
80
35
  return (
81
- <div className='flex w-[300px] border border-separator' style={{ height: defaultRowSize }}>
36
+ <div className='border border-separator' style={{ height: defaultRowSize }}>
82
37
  <Grid.Root id='test' editing={editing}>
83
- <TableCellEditor model={model} onQuery={handleQuery} />
38
+ <TableCellEditor model={model} schema={Task.Task} />
84
39
  </Grid.Root>
85
40
  </div>
86
41
  );
87
42
  };
88
43
 
89
- const meta: Meta<StoryProps> = {
90
- title: 'plugins/plugin-table/TableCellEditor',
44
+ const meta = {
45
+ title: 'ui/react-ui-table/TableCellEditor',
91
46
  component: DefaultStory,
92
47
  render: DefaultStory,
93
- parameters: { translations, layout: 'centered' },
94
48
  decorators: [
49
+ withTheme(),
50
+ withLayout({ layout: 'column' }),
95
51
  withClientProvider({
96
- types: [TableType, ViewType],
52
+ types: [View.View, Task.Task, Table.Table],
97
53
  createIdentity: true,
98
54
  createSpace: true,
99
- onSpaceCreated: async ({ client, space }) => {
100
- const table = space.db.add(Obj.make(TableType, {}));
101
- const schema = await initializeTable({ client, space, table });
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);
102
59
  Array.from({ length: 10 }).forEach(() => {
103
60
  space.db.add(
104
- live(schema, {
105
- name: faker.person.fullName(),
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(),
106
65
  }),
107
66
  );
108
67
  });
109
68
  },
110
69
  }),
111
- withTheme,
112
- withLayout(),
113
70
  ],
114
- };
71
+ parameters: {
72
+ layout: 'fullscreen',
73
+ translations,
74
+ },
75
+ } satisfies Meta<typeof DefaultStory>;
115
76
 
116
77
  export default meta;
117
78
 
118
- type Story = StoryObj<StoryProps>;
79
+ type Story = StoryObj<typeof meta>;
119
80
 
120
81
  export const Default: Story = {
121
82
  args: {