@dxos/react-ui-table 0.8.4-main.28f8d3d → 0.8.4-main.2c6827d

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 (114) hide show
  1. package/dist/lib/browser/chunk-2PFAPQL4.mjs +66 -0
  2. package/dist/lib/browser/chunk-2PFAPQL4.mjs.map +7 -0
  3. package/dist/lib/browser/{chunk-ZAOSIG3L.mjs → chunk-GS2UNGJK.mjs} +174 -213
  4. package/dist/lib/browser/chunk-GS2UNGJK.mjs.map +7 -0
  5. package/dist/lib/browser/index.mjs +411 -515
  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 +12 -9
  9. package/dist/lib/browser/testing/index.mjs.map +3 -3
  10. package/dist/lib/browser/types/index.mjs +3 -3
  11. package/dist/lib/node-esm/chunk-6POOFFYF.mjs +67 -0
  12. package/dist/lib/node-esm/chunk-6POOFFYF.mjs.map +7 -0
  13. package/dist/lib/node-esm/{chunk-ZOFFI3JJ.mjs → chunk-TRR3C5NX.mjs} +174 -213
  14. package/dist/lib/node-esm/chunk-TRR3C5NX.mjs.map +7 -0
  15. package/dist/lib/node-esm/index.mjs +411 -515
  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 +12 -9
  19. package/dist/lib/node-esm/testing/index.mjs.map +3 -3
  20. package/dist/lib/node-esm/types/index.mjs +3 -3
  21. package/dist/types/src/components/Table/DynamicTable.d.ts +2 -2
  22. package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
  23. package/dist/types/src/components/Table/DynamicTable.stories.d.ts +51 -3
  24. package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
  25. package/dist/types/src/components/Table/Relations.stories.d.ts +103 -5
  26. package/dist/types/src/components/Table/Relations.stories.d.ts.map +1 -1
  27. package/dist/types/src/components/Table/RowActionsMenu.d.ts.map +1 -1
  28. package/dist/types/src/components/Table/Table.d.ts +8 -5
  29. package/dist/types/src/components/Table/Table.d.ts.map +1 -1
  30. package/dist/types/src/components/Table/Table.stories.d.ts +41 -3
  31. package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
  32. package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +12 -6
  33. package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
  34. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +8 -13
  35. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
  36. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts +39 -4
  37. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
  38. package/dist/types/src/components/TableCellEditor/index.d.ts +1 -0
  39. package/dist/types/src/components/TableCellEditor/index.d.ts.map +1 -1
  40. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts +2 -2
  41. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +1 -1
  42. package/dist/types/src/components/testing.d.ts +3 -3
  43. package/dist/types/src/components/testing.d.ts.map +1 -1
  44. package/dist/types/src/hooks/useAddRow.d.ts +3 -2
  45. package/dist/types/src/hooks/useAddRow.d.ts.map +1 -1
  46. package/dist/types/src/hooks/useTableModel.d.ts +3 -3
  47. package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
  48. package/dist/types/src/model/modal-controller.d.ts +0 -7
  49. package/dist/types/src/model/modal-controller.d.ts.map +1 -1
  50. package/dist/types/src/model/table-model.d.ts +9 -8
  51. package/dist/types/src/model/table-model.d.ts.map +1 -1
  52. package/dist/types/src/model/table-presentation.d.ts +1 -1
  53. package/dist/types/src/model/table-presentation.d.ts.map +1 -1
  54. package/dist/types/src/model/table-sorting.d.ts +2 -3
  55. package/dist/types/src/model/table-sorting.d.ts.map +1 -1
  56. package/dist/types/src/testing/useTestTableModel.d.ts +3 -22
  57. package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -1
  58. package/dist/types/src/types/index.d.ts +1 -1
  59. package/dist/types/src/types/index.d.ts.map +1 -1
  60. package/dist/types/src/types/schema.d.ts +25 -5
  61. package/dist/types/src/types/schema.d.ts.map +1 -1
  62. package/dist/types/src/util/dynamic-table.d.ts +5 -6
  63. package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
  64. package/dist/types/src/util/index.d.ts +0 -1
  65. package/dist/types/src/util/index.d.ts.map +1 -1
  66. package/dist/types/src/util/schema.d.ts +1 -1
  67. package/dist/types/src/util/schema.d.ts.map +1 -1
  68. package/dist/types/src/util/table-buttons.d.ts.map +1 -1
  69. package/dist/types/tsconfig.tsbuildinfo +1 -1
  70. package/package.json +53 -51
  71. package/src/components/Table/DynamicTable.stories.tsx +15 -12
  72. package/src/components/Table/DynamicTable.tsx +4 -7
  73. package/src/components/Table/Relations.stories.tsx +160 -42
  74. package/src/components/Table/RowActionsMenu.tsx +21 -13
  75. package/src/components/Table/Table.stories.tsx +63 -56
  76. package/src/components/Table/Table.tsx +109 -90
  77. package/src/components/TableCellEditor/FormCellEditor.tsx +153 -42
  78. package/src/components/TableCellEditor/TableCellEditor.stories.tsx +24 -64
  79. package/src/components/TableCellEditor/TableCellEditor.tsx +62 -208
  80. package/src/components/TableCellEditor/index.ts +1 -0
  81. package/src/components/TableToolbar/TableToolbar.tsx +32 -22
  82. package/src/components/testing.ts +2 -2
  83. package/src/hooks/useAddRow.ts +7 -5
  84. package/src/hooks/useTableModel.ts +5 -5
  85. package/src/model/modal-controller.ts +0 -28
  86. package/src/model/table-model.test.ts +19 -11
  87. package/src/model/table-model.ts +36 -24
  88. package/src/model/table-presentation.test.ts +22 -21
  89. package/src/model/table-presentation.ts +58 -39
  90. package/src/model/table-sorting.ts +16 -7
  91. package/src/playwright/TableManager.ts +1 -1
  92. package/src/playwright/smoke.spec.ts +51 -49
  93. package/src/testing/useTestTableModel.ts +10 -7
  94. package/src/types/index.ts +1 -1
  95. package/src/types/schema.ts +63 -6
  96. package/src/util/dynamic-table.ts +15 -16
  97. package/src/util/index.ts +0 -1
  98. package/src/util/schema.ts +4 -4
  99. package/src/util/table-buttons.ts +3 -3
  100. package/dist/lib/browser/chunk-L2LSB6KV.mjs +0 -18
  101. package/dist/lib/browser/chunk-L2LSB6KV.mjs.map +0 -7
  102. package/dist/lib/browser/chunk-ZAOSIG3L.mjs.map +0 -7
  103. package/dist/lib/node-esm/chunk-NXZ4CJYX.mjs +0 -20
  104. package/dist/lib/node-esm/chunk-NXZ4CJYX.mjs.map +0 -7
  105. package/dist/lib/node-esm/chunk-ZOFFI3JJ.mjs.map +0 -7
  106. package/dist/types/src/components/Table/CreateRefPanel.d.ts +0 -9
  107. package/dist/types/src/components/Table/CreateRefPanel.d.ts.map +0 -1
  108. package/dist/types/src/components/TableCellEditor/extension.d.ts +0 -13
  109. package/dist/types/src/components/TableCellEditor/extension.d.ts.map +0 -1
  110. package/dist/types/src/util/table.d.ts +0 -12
  111. package/dist/types/src/util/table.d.ts.map +0 -1
  112. package/src/components/Table/CreateRefPanel.tsx +0 -86
  113. package/src/components/TableCellEditor/extension.ts +0 -61
  114. package/src/util/table.ts +0 -53
@@ -8,7 +8,6 @@ import {
8
8
  TableModel,
9
9
  TablePresentation,
10
10
  cellClassesForRowSelection,
11
- createTable,
12
11
  getBaseSchema,
13
12
  makeDynamicTable,
14
13
  narrowSchema,
@@ -17,8 +16,8 @@ import {
17
16
  touch,
18
17
  useAddRow,
19
18
  useTableModel
20
- } from "./chunk-ZOFFI3JJ.mjs";
21
- import "./chunk-NXZ4CJYX.mjs";
19
+ } from "./chunk-TRR3C5NX.mjs";
20
+ import "./chunk-6POOFFYF.mjs";
22
21
 
23
22
  // src/translations.ts
24
23
  var translationKey = "react-ui-table";
@@ -53,27 +52,22 @@ var translations = [
53
52
  ];
54
53
 
55
54
  // src/components/Table/Table.tsx
56
- import { useSignals as _useSignals8 } from "@preact-signals/safe-react/tracking";
57
- import React8, { forwardRef, useCallback as useCallback5, useEffect as useEffect4, useImperativeHandle, useMemo as useMemo5, useState as useState4 } from "react";
58
- import { Filter } from "@dxos/echo";
59
- import { getValue } from "@dxos/echo-schema";
60
- import { invariant as invariant3 } from "@dxos/invariant";
61
- import { getSpace as getSpace2 } from "@dxos/react-client/echo";
62
- import { useTranslation as useTranslation3 } from "@dxos/react-ui";
55
+ import { useSignals as _useSignals7 } from "@preact-signals/safe-react/tracking";
56
+ import * as String2 from "effect/String";
57
+ import React7, { forwardRef, useCallback as useCallback4, useEffect as useEffect3, useImperativeHandle, useMemo as useMemo4, useState as useState4 } from "react";
63
58
  import { useAttention } from "@dxos/react-ui-attention";
64
59
  import { Grid, closestCell, gridSeparatorBlockEnd, gridSeparatorInlineEnd } from "@dxos/react-ui-grid";
60
+ import { DxEditRequest } from "@dxos/react-ui-grid";
65
61
  import { mx } from "@dxos/react-ui-theme";
66
- import { isNotFalsy, safeParseInt } from "@dxos/util";
67
62
 
68
63
  // src/components/TableCellEditor/TableCellEditor.tsx
69
64
  import { useSignals as _useSignals3 } from "@preact-signals/safe-react/tracking";
70
- import React3, { useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
71
- import { FormatEnum, TypeEnum } from "@dxos/echo-schema";
65
+ import { EditorView } from "@codemirror/view";
66
+ import React3, { useCallback as useCallback2, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
67
+ import { debounce } from "@dxos/async";
68
+ import { FormatEnum as FormatEnum2, TypeEnum } from "@dxos/echo/internal";
72
69
  import { invariant as invariant2 } from "@dxos/invariant";
73
- import { useThemeContext } from "@dxos/react-ui";
74
- import { createMarkdownExtensions } from "@dxos/react-ui-editor";
75
- import { GridCellEditor, cellQuery, editorKeys, parseCellIndex as parseCellIndex2, useGridContext as useGridContext3 } from "@dxos/react-ui-grid";
76
- import { createLinks, tagPicker } from "@dxos/react-ui-tag-picker";
70
+ import { GridCellEditor, editorKeys, parseCellIndex as parseCellIndex2, useGridContext as useGridContext3 } from "@dxos/react-ui-grid";
77
71
 
78
72
  // src/components/TableCellEditor/CellValidationMessage.tsx
79
73
  import { useSignals as _useSignals } from "@preact-signals/safe-react/tracking";
@@ -106,110 +100,108 @@ var CellValidationMessage = ({ validationError, variant = "error", __gridScope }
106
100
  }
107
101
  };
108
102
 
109
- // src/components/TableCellEditor/extension.ts
110
- import { autocompletion, pickedCompletion } from "@codemirror/autocomplete";
111
- import { EditorView } from "@codemirror/view";
112
- var completion = ({ debug, onQuery, onMatch }) => {
113
- return [
114
- EditorView.theme({
115
- ".cm-completionDialog": {
116
- width: "var(--dx-gridCellWidth)"
117
- }
118
- }),
119
- autocompletion({
120
- icons: false,
121
- activateOnTyping: true,
122
- closeOnBlur: !debug,
123
- override: [
124
- async (context) => {
125
- const text = context.state.doc.toString();
126
- return {
127
- from: 0,
128
- options: await onQuery(text)
129
- };
130
- }
131
- ],
132
- // TODO(burdon): Consts from grid (to line-up with grid).
133
- tooltipClass: () => "cm-completionDialog !mt-[8px] !-ml-[4px] [&>ul]:!max-h-[264px]",
134
- optionClass: () => "flex h-[33px] items-center"
135
- }),
136
- // Check for accepted completion metadata in the transaction.
137
- EditorView.updateListener.of((update) => {
138
- update.transactions.forEach((tr) => {
139
- const completion2 = tr.annotation(pickedCompletion);
140
- if (completion2) {
141
- onMatch(completion2.data);
142
- }
143
- });
144
- })
145
- ];
146
- };
147
-
148
103
  // src/components/TableCellEditor/FormCellEditor.tsx
149
104
  import { useSignals as _useSignals2 } from "@preact-signals/safe-react/tracking";
105
+ import * as Schema from "effect/Schema";
150
106
  import React2, { useCallback, useEffect, useMemo, useRef, useState } from "react";
151
- import { getSnapshot } from "@dxos/echo-schema";
107
+ import { Filter, Obj } from "@dxos/echo";
108
+ import { FormatEnum, Ref, getValue } from "@dxos/echo/internal";
152
109
  import { invariant } from "@dxos/invariant";
110
+ import { getSnapshot } from "@dxos/live-object";
111
+ import { getSpace } from "@dxos/react-client/echo";
153
112
  import { Popover } from "@dxos/react-ui";
154
113
  import { Form } from "@dxos/react-ui-form";
155
114
  import { parseCellIndex, useGridContext as useGridContext2 } from "@dxos/react-ui-grid";
156
- import { getDeep, setDeep } from "@dxos/util";
115
+ import { getDeep, isTruthy, setDeep } from "@dxos/util";
157
116
  var __dxlog_file = "/__w/dxos/dxos/packages/ui/react-ui-table/src/components/TableCellEditor/FormCellEditor.tsx";
158
- var FormCellEditor = ({ fieldProjection, model, schema, onSave, __gridScope }) => {
117
+ var createOptionLabel = [
118
+ "create new object label",
119
+ {
120
+ ns: translationKey
121
+ }
122
+ ];
123
+ var FormCellEditor = ({ fieldProjection, model, schema, onSave, client, modals, onCreate, __gridScope, ...formProps }) => {
159
124
  var _effect = _useSignals2();
160
125
  try {
161
- const { editing: contextEditing, setEditing } = useGridContext2("ArrayEditor", __gridScope);
126
+ const { editing: contextEditing, setEditing, id: gridId } = useGridContext2("ArrayEditor", __gridScope);
162
127
  const [editing, setLocalEditing] = useState(false);
163
128
  const anchorRef = useRef(null);
164
- useEffect(() => {
165
- if (contextEditing && contextEditing.cellElement) {
166
- anchorRef.current = contextEditing.cellElement.querySelector(".dx-grid__cell__content");
167
- setLocalEditing(true);
168
- } else {
169
- anchorRef.current = null;
170
- setLocalEditing(false);
129
+ const getSchema = useCallback((typeAnnotation) => {
130
+ const space = getSpace(model.view);
131
+ invariant(space, void 0, {
132
+ F: __dxlog_file,
133
+ L: 57,
134
+ S: void 0,
135
+ A: [
136
+ "space",
137
+ ""
138
+ ]
139
+ });
140
+ let schema2;
141
+ if (client) {
142
+ schema2 = client.graph.schemaRegistry.getSchema(typeAnnotation.typename);
143
+ }
144
+ if (!schema2) {
145
+ schema2 = space.db.schemaRegistry.getSchema(typeAnnotation.typename);
171
146
  }
147
+ return {
148
+ space,
149
+ schema: schema2
150
+ };
172
151
  }, [
173
- contextEditing
152
+ client,
153
+ model
174
154
  ]);
175
- const narrowedSchema = useMemo(() => {
176
- if (!schema) {
177
- return void 0;
155
+ const handleQueryRefOptions = useCallback(async (typeAnnotation) => {
156
+ const { schema: schema2, space } = getSchema(typeAnnotation);
157
+ if (model && schema2 && space) {
158
+ const { objects } = await space.db.query(Filter.type(schema2)).run();
159
+ return objects.map((obj) => {
160
+ return {
161
+ dxn: Obj.getDXN(obj),
162
+ label: getValue(obj, fieldProjection.field.referencePath) || obj.id.toString()
163
+ };
164
+ }).filter(isTruthy);
178
165
  }
179
- return narrowSchema(schema, [
180
- fieldProjection.field.path
181
- ]);
166
+ return [];
182
167
  }, [
183
- JSON.stringify(schema),
184
- fieldProjection.field.path
168
+ client,
169
+ model
185
170
  ]);
186
171
  const originalRow = useMemo(() => {
187
172
  if (model && contextEditing) {
188
173
  const cell = parseCellIndex(contextEditing.index);
189
- const row = model.getRowAt(cell.row);
190
- invariant(row, void 0, {
191
- F: __dxlog_file,
192
- L: 55,
193
- S: void 0,
194
- A: [
195
- "row",
196
- ""
197
- ]
198
- });
199
- return row;
174
+ if (model.isDraftCell(cell)) {
175
+ const draftRow = model.draftRows.value[cell.row];
176
+ invariant(draftRow, void 0, {
177
+ F: __dxlog_file,
178
+ L: 98,
179
+ S: void 0,
180
+ A: [
181
+ "draftRow",
182
+ ""
183
+ ]
184
+ });
185
+ return draftRow.data;
186
+ } else {
187
+ const row = model.getRowAt(cell.row);
188
+ invariant(row, void 0, {
189
+ F: __dxlog_file,
190
+ L: 102,
191
+ S: void 0,
192
+ A: [
193
+ "row",
194
+ ""
195
+ ]
196
+ });
197
+ return row;
198
+ }
200
199
  }
201
200
  return void 0;
202
201
  }, [
203
202
  model,
204
203
  contextEditing
205
204
  ]);
206
- const formValues = useMemo(() => {
207
- if (originalRow) {
208
- return getSnapshot(originalRow);
209
- }
210
- }, [
211
- originalRow
212
- ]);
213
205
  const handleSave = useCallback((values) => {
214
206
  const path = fieldProjection.field.path;
215
207
  const value = getDeep(values, [
@@ -218,11 +210,39 @@ var FormCellEditor = ({ fieldProjection, model, schema, onSave, __gridScope }) =
218
210
  setDeep(originalRow, [
219
211
  path
220
212
  ], value);
213
+ contextEditing?.cellElement?.focus();
221
214
  setEditing(null);
222
215
  setLocalEditing(false);
223
216
  onSave?.();
224
217
  }, [
225
218
  fieldProjection.field.path,
219
+ onSave,
220
+ contextEditing,
221
+ originalRow
222
+ ]);
223
+ useEffect(() => {
224
+ if (contextEditing && contextEditing.cellElement) {
225
+ anchorRef.current = contextEditing.cellElement.querySelector(".dx-grid__cell__content");
226
+ setLocalEditing(true);
227
+ } else {
228
+ anchorRef.current = null;
229
+ setLocalEditing(false);
230
+ }
231
+ }, [
232
+ contextEditing
233
+ ]);
234
+ const narrowedSchema = useMemo(() => {
235
+ if (!schema) {
236
+ return void 0;
237
+ }
238
+ return narrowSchema(schema, [
239
+ fieldProjection.field.path
240
+ ]);
241
+ }, [
242
+ JSON.stringify(schema),
243
+ fieldProjection.field.path
244
+ ]);
245
+ const formValues = useMemo(() => originalRow ? getSnapshot(originalRow) : {}, [
226
246
  originalRow
227
247
  ]);
228
248
  const handleOpenChange = useCallback((nextOpen) => {
@@ -232,6 +252,47 @@ var FormCellEditor = ({ fieldProjection, model, schema, onSave, __gridScope }) =
232
252
  }
233
253
  setLocalEditing(nextOpen);
234
254
  }, []);
255
+ const refSchema = useMemo(() => {
256
+ if (fieldProjection.props.format === FormatEnum.Ref && fieldProjection.props.referenceSchema) {
257
+ const { schema: refSchema2 } = getSchema({
258
+ typename: fieldProjection.props.referenceSchema
259
+ });
260
+ return refSchema2;
261
+ }
262
+ return null;
263
+ }, [
264
+ fieldProjection.props.format,
265
+ fieldProjection.props.referenceSchema,
266
+ getSchema
267
+ ]);
268
+ const createSchema = useMemo(() => {
269
+ return refSchema ? Schema.omit("id")(refSchema) : null;
270
+ }, [
271
+ refSchema
272
+ ]);
273
+ const handleCreate = useCallback((values) => {
274
+ if (refSchema && onCreate) {
275
+ const objectWithId = onCreate(refSchema, values);
276
+ if (objectWithId) {
277
+ const ref = Ref.make(objectWithId);
278
+ const path = fieldProjection.field.path;
279
+ setDeep(originalRow, [
280
+ path
281
+ ], ref);
282
+ }
283
+ }
284
+ contextEditing?.cellElement?.focus();
285
+ setEditing(null);
286
+ setLocalEditing(false);
287
+ onSave?.();
288
+ }, [
289
+ fieldProjection.field.path,
290
+ onSave,
291
+ contextEditing,
292
+ originalRow,
293
+ refSchema,
294
+ onCreate
295
+ ]);
235
296
  if (!editing) {
236
297
  return null;
237
298
  }
@@ -240,14 +301,24 @@ var FormCellEditor = ({ fieldProjection, model, schema, onSave, __gridScope }) =
240
301
  onOpenChange: handleOpenChange
241
302
  }, /* @__PURE__ */ React2.createElement(Popover.VirtualTrigger, {
242
303
  virtualRef: anchorRef
243
- }), /* @__PURE__ */ React2.createElement(Popover.Content, {
304
+ }), /* @__PURE__ */ React2.createElement(Popover.Portal, null, /* @__PURE__ */ React2.createElement(Popover.Content, {
244
305
  tabIndex: -1,
245
306
  classNames: "popover-card-width density-fine"
246
307
  }, /* @__PURE__ */ React2.createElement(Popover.Arrow, null), /* @__PURE__ */ React2.createElement(Popover.Viewport, null, /* @__PURE__ */ React2.createElement(Form, {
308
+ autoFocus: true,
247
309
  values: formValues,
248
310
  schema: narrowedSchema,
249
- onSave: handleSave
250
- }))));
311
+ onSave: handleSave,
312
+ ...formProps,
313
+ onQueryRefOptions: handleQueryRefOptions,
314
+ ...createSchema && {
315
+ onCreate: handleCreate,
316
+ createSchema,
317
+ createInitialValuePath: fieldProjection.field.referencePath,
318
+ createOptionIcon: "ph--plus--regular",
319
+ createOptionLabel
320
+ }
321
+ })))));
251
322
  } finally {
252
323
  _effect.f();
253
324
  }
@@ -255,13 +326,8 @@ var FormCellEditor = ({ fieldProjection, model, schema, onSave, __gridScope }) =
255
326
 
256
327
  // src/components/TableCellEditor/TableCellEditor.tsx
257
328
  var __dxlog_file2 = "/__w/dxos/dxos/packages/ui/react-ui-table/src/components/TableCellEditor/TableCellEditor.tsx";
258
- var newValue = Symbol.for("newValue");
259
- var createOption = (text) => ({
260
- [newValue]: true,
261
- text
262
- });
263
- var isCreateOption = (data) => typeof data === "object" && data[newValue];
264
- var TableValueEditor = ({ model, modals, schema, onEnter, onFocus, onSave, onQuery, __gridScope }) => {
329
+ var adaptValidationMessage = (message) => message ? message.endsWith("is missing") ? "Can\u2019t be blank" : message : null;
330
+ var TableValueEditor = ({ model, modals, schema, onFocus, onSave, onCreate, client, __gridScope }) => {
265
331
  var _effect = _useSignals3();
266
332
  try {
267
333
  const { editing } = useGridContext3("TableValueEditor", __gridScope);
@@ -274,7 +340,7 @@ var TableValueEditor = ({ model, modals, schema, onEnter, onFocus, onSave, onQue
274
340
  const fieldProjection2 = model.projection.getFieldProjection(field.id);
275
341
  invariant2(fieldProjection2, void 0, {
276
342
  F: __dxlog_file2,
277
- L: 76,
343
+ L: 71,
278
344
  S: void 0,
279
345
  A: [
280
346
  "fieldProjection",
@@ -286,21 +352,22 @@ var TableValueEditor = ({ model, modals, schema, onEnter, onFocus, onSave, onQue
286
352
  model,
287
353
  editing
288
354
  ]);
289
- if (fieldProjection?.props.type === TypeEnum.Array || fieldProjection?.props.format === FormatEnum.SingleSelect) {
355
+ if (fieldProjection?.props.type === TypeEnum.Array || fieldProjection?.props.format === FormatEnum2.SingleSelect || fieldProjection?.props.format === FormatEnum2.Ref) {
290
356
  return /* @__PURE__ */ React3.createElement(FormCellEditor, {
291
357
  fieldProjection,
292
358
  model,
293
359
  schema,
294
360
  __gridScope,
295
- onSave
361
+ onSave,
362
+ onCreate,
363
+ client,
364
+ modals
296
365
  });
297
366
  }
298
367
  return /* @__PURE__ */ React3.createElement(TableCellEditor, {
299
368
  model,
300
369
  modals,
301
- onEnter,
302
370
  onFocus,
303
- onQuery,
304
371
  onSave,
305
372
  __gridScope
306
373
  });
@@ -309,16 +376,15 @@ var TableValueEditor = ({ model, modals, schema, onEnter, onFocus, onSave, onQue
309
376
  }
310
377
  };
311
378
  var editorSlots = {
312
- scroller: {
379
+ scroll: {
313
380
  className: "!plb-[--dx-grid-cell-editor-padding-block]"
314
381
  }
315
382
  };
316
- var TableCellEditor = ({ model, modals, onEnter, onFocus, onQuery, onSave, __gridScope }) => {
383
+ var TableCellEditor = ({ model, modals, onFocus, onSave, __gridScope }) => {
317
384
  var _effect = _useSignals3();
318
385
  try {
319
- const { id: gridId, editing, setEditing } = useGridContext3("TableCellEditor", __gridScope);
386
+ const { editing, setEditing } = useGridContext3("TableCellEditor", __gridScope);
320
387
  const suppressNextBlur = useRef2(false);
321
- const { themeMode } = useThemeContext();
322
388
  const [validationError, setValidationError] = useState2(null);
323
389
  const [validationVariant, setValidationVariant] = useState2("error");
324
390
  const fieldProjection = useMemo2(() => {
@@ -330,7 +396,7 @@ var TableCellEditor = ({ model, modals, onEnter, onFocus, onQuery, onSave, __gri
330
396
  const fieldProjection2 = model.projection.getFieldProjection(field.id);
331
397
  invariant2(fieldProjection2, void 0, {
332
398
  F: __dxlog_file2,
333
- L: 135,
399
+ L: 121,
334
400
  S: void 0,
335
401
  A: [
336
402
  "fieldProjection",
@@ -342,37 +408,6 @@ var TableCellEditor = ({ model, modals, onEnter, onFocus, onQuery, onSave, __gri
342
408
  model,
343
409
  editing
344
410
  ]);
345
- useEffect2(() => {
346
- if (!model || !editing || !fieldProjection) {
347
- setValidationError(null);
348
- return;
349
- }
350
- const cell = parseCellIndex2(editing.index);
351
- const { row, col } = cell;
352
- if (model.isDraftCell(cell)) {
353
- const field = model.projection.fields[col];
354
- const hasValidationError = model.hasDraftRowValidationError(row, field.path);
355
- if (hasValidationError) {
356
- const draftRows = model.draftRows.value;
357
- if (row >= 0 && row < draftRows.length) {
358
- const draftRow = draftRows[row];
359
- const validationError2 = draftRow.validationErrors?.find((error) => error.path === field.path);
360
- if (validationError2) {
361
- setValidationError(validationError2.message);
362
- setValidationVariant("warning");
363
- }
364
- }
365
- } else {
366
- setValidationError(null);
367
- }
368
- } else {
369
- setValidationError(null);
370
- }
371
- }, [
372
- model,
373
- editing,
374
- fieldProjection
375
- ]);
376
411
  const handleEnter = useCallback2(async (value) => {
377
412
  if (!model || !editing) {
378
413
  return;
@@ -382,27 +417,33 @@ var TableCellEditor = ({ model, modals, onEnter, onFocus, onQuery, onSave, __gri
382
417
  if (validationResult.valid) {
383
418
  setValidationError(null);
384
419
  model.setCellData(cell, value);
385
- onEnter?.(cell);
386
420
  onFocus?.();
387
421
  setEditing(null);
388
422
  onSave?.();
389
423
  } else {
390
- setValidationError(validationResult.error);
424
+ setValidationError(adaptValidationMessage(validationResult.error));
391
425
  setValidationVariant("error");
392
426
  }
393
427
  }, [
394
428
  model,
395
429
  editing,
396
- onEnter,
397
430
  onFocus,
398
431
  setEditing
399
432
  ]);
400
433
  const handleBlur = useCallback2(async (value) => {
401
- if (!model || !editing) {
402
- return;
403
- }
404
434
  if (suppressNextBlur.current) {
405
435
  suppressNextBlur.current = false;
436
+ } else if (model && editing) {
437
+ const cell = parseCellIndex2(editing.index);
438
+ if (value !== void 0) {
439
+ const result = await model.validateCellData(cell, value);
440
+ if (result.valid) {
441
+ setValidationError(null);
442
+ model.setCellData(cell, value);
443
+ setEditing(null);
444
+ onSave?.();
445
+ }
446
+ }
406
447
  }
407
448
  }, [
408
449
  model,
@@ -416,11 +457,11 @@ var TableCellEditor = ({ model, modals, onEnter, onFocus, onQuery, onSave, __gri
416
457
  if (value !== void 0) {
417
458
  const result = await model.validateCellData(cell, value);
418
459
  if (result.valid) {
460
+ suppressNextBlur.current = true;
419
461
  setValidationError(null);
420
462
  model.setCellData(cell, value);
421
463
  setEditing(null);
422
464
  onSave?.();
423
- onEnter?.(cell);
424
465
  if (event && onFocus) {
425
466
  onFocus(determineNavigationAxis(event), determineNavigationDelta(event), cell);
426
467
  }
@@ -429,6 +470,7 @@ var TableCellEditor = ({ model, modals, onEnter, onFocus, onQuery, onSave, __gri
429
470
  setValidationVariant("error");
430
471
  }
431
472
  } else {
473
+ suppressNextBlur.current = true;
432
474
  setValidationError(null);
433
475
  setEditing(null);
434
476
  onSave?.();
@@ -440,16 +482,15 @@ var TableCellEditor = ({ model, modals, onEnter, onFocus, onQuery, onSave, __gri
440
482
  model,
441
483
  editing,
442
484
  onFocus,
443
- onEnter,
444
485
  fieldProjection,
445
486
  setEditing,
446
487
  onSave
447
488
  ]);
448
- const extension = useMemo2(() => {
489
+ const extensions = useMemo2(() => {
449
490
  if (!fieldProjection) {
450
491
  return [];
451
492
  }
452
- const extension2 = [
493
+ const extensions2 = [
453
494
  editorKeys({
454
495
  onClose: handleClose,
455
496
  ...editing?.initialContent && {
@@ -457,114 +498,31 @@ var TableCellEditor = ({ model, modals, onEnter, onFocus, onQuery, onSave, __gri
457
498
  }
458
499
  })
459
500
  ];
460
- const format = fieldProjection.props.format;
461
- if (format === FormatEnum.SingleSelect || format === FormatEnum.MultiSelect) {
462
- extension2.push(createMarkdownExtensions({
463
- themeMode
464
- }));
465
- const options = fieldProjection.props.options || [];
466
- const mode = format === FormatEnum.SingleSelect ? "single-select" : "multi-select";
467
- extension2.push(tagPicker({
468
- mode,
469
- keymap: false,
470
- onSearch: (text, selectedIds) => {
471
- return options.filter((option) => selectedIds.indexOf(option.id) === -1 && (text.length === 0 || option.title.toLowerCase().includes(text.toLowerCase()))).map((option) => ({
472
- id: option.id,
473
- label: option.title,
474
- hue: option.color
475
- }));
476
- },
477
- onUpdate: (ids) => {
478
- if (model && editing) {
479
- if (ids.length === 0) {
480
- return;
481
- }
482
- if (mode === "single-select") {
483
- void handleEnter(ids[0]);
484
- } else {
485
- void handleEnter(ids);
486
- }
501
+ if (model && editing) {
502
+ extensions2.push(EditorView.updateListener.of(debounce((update) => {
503
+ const content = update.state.doc.toString();
504
+ const cell = parseCellIndex2(editing.index);
505
+ void model.validateCellData(cell, content).then((result) => {
506
+ if (result.valid) {
507
+ setValidationError(null);
508
+ } else {
509
+ setValidationError(result.error);
510
+ setValidationVariant("error");
487
511
  }
488
- }
489
- }));
490
- }
491
- if (onQuery) {
492
- switch (fieldProjection.props.format) {
493
- case FormatEnum.Ref: {
494
- extension2.push([
495
- completion({
496
- onQuery: (text) => onQuery(fieldProjection, text),
497
- onMatch: (data) => {
498
- if (model && editing && modals) {
499
- if (isCreateOption(data)) {
500
- const { field, props } = fieldProjection;
501
- if (props.referenceSchema) {
502
- suppressNextBlur.current = true;
503
- modals.openCreateRef(props.referenceSchema, document.querySelector(cellQuery(editing.index, gridId)), {
504
- [field.referencePath]: data.text
505
- }, (data2) => {
506
- void handleEnter(data2);
507
- });
508
- }
509
- } else {
510
- void handleEnter(data);
511
- }
512
- }
513
- }
514
- })
515
- ]);
516
- break;
517
- }
518
- }
512
+ });
513
+ }, 10)));
519
514
  }
520
- return extension2;
515
+ return extensions2;
521
516
  }, [
522
517
  model,
523
518
  modals,
524
519
  editing,
525
520
  fieldProjection,
526
- handleClose,
527
- themeMode
521
+ handleClose
528
522
  ]);
529
523
  const getCellContent = useCallback2(() => {
530
524
  if (model && editing) {
531
525
  const cell = parseCellIndex2(editing.index);
532
- const { col } = cell;
533
- const field = model.projection.fields[col];
534
- const fieldProjection2 = model.projection.getFieldProjection(field.id);
535
- if (fieldProjection2?.props.format === FormatEnum.SingleSelect || fieldProjection2?.props.format === FormatEnum.MultiSelect) {
536
- const value2 = model.getCellData(cell);
537
- if (value2 !== void 0) {
538
- const options = fieldProjection2.props.options || [];
539
- if (fieldProjection2.props.format === FormatEnum.MultiSelect) {
540
- const tagItems = value2.split(",").map((id) => {
541
- const option = options.find((o) => o.id === id);
542
- if (option) {
543
- return {
544
- id,
545
- label: option.title,
546
- hue: option.color
547
- };
548
- }
549
- return void 0;
550
- }).filter((item) => item !== void 0);
551
- return createLinks(tagItems);
552
- } else {
553
- const option = options.find((o) => o.id === value2);
554
- if (option) {
555
- const tagItem = {
556
- id: value2,
557
- label: option.title,
558
- hue: option.color
559
- };
560
- return createLinks([
561
- tagItem
562
- ]);
563
- }
564
- }
565
- }
566
- return "";
567
- }
568
526
  const value = model.getCellData(cell);
569
527
  return value !== void 0 ? String(value) : "";
570
528
  }
@@ -577,7 +535,7 @@ var TableCellEditor = ({ model, modals, onEnter, onFocus, onQuery, onSave, __gri
577
535
  variant: validationVariant,
578
536
  __gridScope
579
537
  }), /* @__PURE__ */ React3.createElement(GridCellEditor, {
580
- extension,
538
+ extensions,
581
539
  getCellContent,
582
540
  onBlur: handleBlur,
583
541
  slots: editorSlots
@@ -654,7 +612,7 @@ var ColumnActionsMenu = ({ model, modals }) => {
654
612
 
655
613
  // src/components/Table/ColumnSettings.tsx
656
614
  import { useSignals as _useSignals5 } from "@preact-signals/safe-react/tracking";
657
- import React5, { useCallback as useCallback3, useEffect as useEffect3, useMemo as useMemo3, useState as useState3 } from "react";
615
+ import React5, { useCallback as useCallback3, useEffect as useEffect2, useMemo as useMemo3, useState as useState3 } from "react";
658
616
  import { Popover as Popover2 } from "@dxos/react-ui";
659
617
  import { FieldEditor } from "@dxos/react-ui-form";
660
618
  var ColumnSettings = ({ registry, model, modals, onNewColumn }) => {
@@ -662,7 +620,7 @@ var ColumnSettings = ({ registry, model, modals, onNewColumn }) => {
662
620
  try {
663
621
  const [newField, setNewField] = useState3();
664
622
  const state = modals.state.value;
665
- useEffect3(() => {
623
+ useEffect2(() => {
666
624
  if (state?.type === "columnSettings" && state.mode.type === "create" && model?.projection) {
667
625
  setNewField(model.projection.createFieldProjection());
668
626
  requestAnimationFrame(() => {
@@ -724,93 +682,12 @@ var ColumnSettings = ({ registry, model, modals, onNewColumn }) => {
724
682
  }
725
683
  };
726
684
 
727
- // src/components/Table/CreateRefPanel.tsx
728
- import { useSignals as _useSignals6 } from "@preact-signals/safe-react/tracking";
729
- import { Schema } from "effect";
730
- import React6, { useCallback as useCallback4, useMemo as useMemo4 } from "react";
731
- import { live } from "@dxos/live-object";
732
- import { getSpace } from "@dxos/react-client/echo";
733
- import { Popover as Popover3 } from "@dxos/react-ui";
734
- import { Form as Form2 } from "@dxos/react-ui-form";
735
- import { useGridContext as useGridContext4 } from "@dxos/react-ui-grid";
736
- var CreateRefPanel = ({ model, modals, __gridScope }) => {
737
- var _effect = _useSignals6();
738
- try {
739
- const { id: gridId } = useGridContext4("TableCellEditor", __gridScope);
740
- const space = model && getSpace(model?.view);
741
- const state = modals.state.value;
742
- const schema = useMemo4(() => {
743
- if (!space || state?.type !== "createRefPanel") {
744
- return;
745
- }
746
- const [schema2] = space.db.schemaRegistry.query({
747
- typename: state.typename
748
- }).runSync();
749
- if (schema2) {
750
- const omit = Schema.omit("id");
751
- return omit(schema2);
752
- }
753
- }, [
754
- space,
755
- state
756
- ]);
757
- const handleSave = useCallback4((values) => {
758
- if (!model || !space || state?.type !== "createRefPanel") {
759
- return;
760
- }
761
- const [schema2] = space.db.schemaRegistry.query({
762
- typename: state.typename
763
- }).runSync();
764
- if (schema2) {
765
- const obj = space.db.add(live(schema2, values));
766
- state.onCreate?.(obj);
767
- }
768
- void modals.close();
769
- }, [
770
- modals,
771
- space,
772
- state
773
- ]);
774
- const handleCancel = useCallback4(() => {
775
- if (model) {
776
- modals.close();
777
- }
778
- }, [
779
- modals
780
- ]);
781
- if (!model?.projection) {
782
- return null;
783
- }
784
- return /* @__PURE__ */ React6.createElement(Popover3.Root, {
785
- modal: false,
786
- open: state?.type === "createRefPanel" && !!schema,
787
- onOpenChange: (nextOpen) => {
788
- if (model && !nextOpen) {
789
- return modals.close();
790
- }
791
- }
792
- }, /* @__PURE__ */ React6.createElement(Popover3.VirtualTrigger, {
793
- virtualRef: modals.trigger
794
- }), /* @__PURE__ */ React6.createElement(Popover3.Portal, null, /* @__PURE__ */ React6.createElement(Popover3.Content, {
795
- classNames: "md:is-64",
796
- "data-grid": gridId
797
- }, /* @__PURE__ */ React6.createElement(Popover3.Viewport, null, state?.type === "createRefPanel" && schema && /* @__PURE__ */ React6.createElement(Form2, {
798
- schema,
799
- values: state.initialValues ?? {},
800
- onSave: handleSave,
801
- onCancel: handleCancel
802
- })), /* @__PURE__ */ React6.createElement(Popover3.Arrow, null))));
803
- } finally {
804
- _effect.f();
805
- }
806
- };
807
-
808
685
  // src/components/Table/RowActionsMenu.tsx
809
- import { useSignals as _useSignals7 } from "@preact-signals/safe-react/tracking";
810
- import React7 from "react";
686
+ import { useSignals as _useSignals6 } from "@preact-signals/safe-react/tracking";
687
+ import React6 from "react";
811
688
  import { DropdownMenu as DropdownMenu2, toLocalizedString, useTranslation as useTranslation2 } from "@dxos/react-ui";
812
689
  var RowActionsMenu = ({ model, modals }) => {
813
- var _effect = _useSignals7();
690
+ var _effect = _useSignals6();
814
691
  try {
815
692
  const { t } = useTranslation2(translationKey);
816
693
  const hasSelection = model.selection.hasSelection.value;
@@ -818,34 +695,45 @@ var RowActionsMenu = ({ model, modals }) => {
818
695
  if (state?.type !== "row") {
819
696
  return null;
820
697
  }
821
- return /* @__PURE__ */ React7.createElement(DropdownMenu2.Root, {
698
+ return /* @__PURE__ */ React6.createElement(DropdownMenu2.Root, {
822
699
  modal: false,
823
700
  open: true,
824
701
  onOpenChange: modals.close
825
- }, /* @__PURE__ */ React7.createElement(DropdownMenu2.VirtualTrigger, {
702
+ }, /* @__PURE__ */ React6.createElement(DropdownMenu2.VirtualTrigger, {
826
703
  virtualRef: modals.trigger
827
- }), /* @__PURE__ */ React7.createElement(DropdownMenu2.Content, null, /* @__PURE__ */ React7.createElement(DropdownMenu2.Viewport, null, model.features.dataEditable !== false && /* @__PURE__ */ React7.createElement(DropdownMenu2.Item, {
828
- "data-testid": "row-menu-delete",
829
- onClick: () => model.deleteRow(state.rowIndex)
830
- }, t(hasSelection ? "bulk delete row label" : "delete row label")), model.rowActions?.map((action) => /* @__PURE__ */ React7.createElement(DropdownMenu2.Item, {
704
+ }), /* @__PURE__ */ React6.createElement(DropdownMenu2.Content, null, /* @__PURE__ */ React6.createElement(DropdownMenu2.Viewport, null, model.rowActions?.length > 0 && /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(DropdownMenu2.Group, null, model.rowActions?.map((action) => /* @__PURE__ */ React6.createElement(DropdownMenu2.Item, {
831
705
  key: action.id,
832
706
  "data-testid": `row-action-${action.id}`,
833
707
  onClick: () => {
834
708
  modals.close();
835
709
  model.handleRowAction(action.id, state.rowIndex);
836
710
  }
837
- }, toLocalizedString(action.label, t)))), /* @__PURE__ */ React7.createElement(DropdownMenu2.Arrow, null)));
711
+ }, toLocalizedString(action.label, t)))), /* @__PURE__ */ React6.createElement(DropdownMenu2.Separator, null)), model.features.dataEditable !== false && /* @__PURE__ */ React6.createElement(DropdownMenu2.Item, {
712
+ "data-testid": "row-menu-delete",
713
+ onClick: () => model.deleteRow(state.rowIndex)
714
+ }, t(hasSelection ? "bulk delete row label" : "delete row label"))), /* @__PURE__ */ React6.createElement(DropdownMenu2.Arrow, null)));
838
715
  } finally {
839
716
  _effect.f();
840
717
  }
841
718
  };
842
719
 
843
720
  // src/components/Table/Table.tsx
844
- var __dxlog_file3 = "/__w/dxos/dxos/packages/ui/react-ui-table/src/components/Table/Table.tsx";
721
+ var columnDefault = {
722
+ grid: {
723
+ minSize: 80,
724
+ maxSize: 640
725
+ }
726
+ };
727
+ var rowDefault = {
728
+ frozenRowsStart: {
729
+ readonly: true,
730
+ focusUnfurl: false
731
+ }
732
+ };
845
733
  var TableRoot = ({ children, role = "article" }) => {
846
- var _effect = _useSignals8();
734
+ var _effect = _useSignals7();
847
735
  try {
848
- return /* @__PURE__ */ React8.createElement("div", {
736
+ return /* @__PURE__ */ React7.createElement("div", {
849
737
  role: "none",
850
738
  className: mx("relative !border-separator [&_.dx-grid]:max-is-[--dx-grid-content-inline-size] [&_.dx-grid]:max-bs-[--dx-grid-content-block-size]", role === "card--popover" && "popover-card-height", role === "section" && "attention-surface", role === "card--intrinsic" && "[&_.dx-grid]:bs-[--dx-grid-content-block-size]", [
851
739
  "card--popover",
@@ -860,19 +748,24 @@ var TableRoot = ({ children, role = "article" }) => {
860
748
  _effect.f();
861
749
  }
862
750
  };
863
- var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttention, schema, client, onRowClick }, forwardedRef) => {
864
- var _effect = _useSignals8();
751
+ var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttention, schema, client, onCreate, onRowClick, testId }, forwardedRef) => {
752
+ var _effect = _useSignals7();
865
753
  try {
866
754
  const [dxGrid, setDxGrid] = useState4(null);
867
755
  const { hasAttention } = useAttention(model?.id ?? "table");
868
- const { t } = useTranslation3(translationKey);
869
- const modals = useMemo5(() => new ModalController(), []);
756
+ const modals = useMemo4(() => new ModalController(), []);
870
757
  const draftRowCount = model?.getDraftRowCount() ?? 0;
871
- const frozen = useMemo5(() => {
758
+ const handleSave = useCallback4(() => {
759
+ dxGrid?.updateCells(true);
760
+ dxGrid?.requestUpdate();
761
+ }, [
762
+ dxGrid
763
+ ]);
764
+ const frozen = useMemo4(() => {
872
765
  const noActionColumn = model?.features.dataEditable === false && model?.features.schemaEditable === false && model.rowActions.length === 0;
873
766
  return {
874
767
  frozenRowsStart: 1,
875
- frozenRowsEnd: draftRowCount,
768
+ frozenRowsEnd: Math.max(1, draftRowCount),
876
769
  frozenColsStart: model?.features.selection.enabled ? 1 : 0,
877
770
  frozenColsEnd: noActionColumn ? 0 : 1
878
771
  };
@@ -880,10 +773,10 @@ var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttenti
880
773
  model,
881
774
  draftRowCount
882
775
  ]);
883
- const getCells = useCallback5((range, plane) => presentation?.getCells(range, plane) ?? {}, [
776
+ const getCells = useCallback4((range, plane) => presentation?.getCells(range, plane) ?? {}, [
884
777
  presentation
885
778
  ]);
886
- useEffect4(() => {
779
+ useEffect3(() => {
887
780
  if (!presentation || !dxGrid) {
888
781
  return;
889
782
  }
@@ -893,6 +786,30 @@ var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttenti
893
786
  dxGrid,
894
787
  getCells
895
788
  ]);
789
+ const handleInsertRowResult = useCallback4((insertResult) => {
790
+ if (insertResult === "draft") {
791
+ requestAnimationFrame(() => {
792
+ dxGrid?.setFocus({
793
+ plane: "frozenRowsEnd",
794
+ col: 0,
795
+ row: 0
796
+ });
797
+ dxGrid?.refocus();
798
+ });
799
+ } else {
800
+ requestAnimationFrame(() => {
801
+ dxGrid?.setFocus({
802
+ plane: "grid",
803
+ col: 0,
804
+ row: model ? model.getRowCount() - 1 : 0
805
+ });
806
+ dxGrid?.refocus();
807
+ });
808
+ }
809
+ }, [
810
+ model,
811
+ dxGrid
812
+ ]);
896
813
  useImperativeHandle(forwardedRef, () => {
897
814
  if (!presentation || !dxGrid) {
898
815
  return {};
@@ -906,22 +823,14 @@ var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttenti
906
823
  dxGrid.requestUpdate();
907
824
  }
908
825
  },
909
- focusDraft: () => {
910
- requestAnimationFrame(() => {
911
- dxGrid.setFocus({
912
- plane: "frozenRowsEnd",
913
- col: 0,
914
- row: 0
915
- });
916
- dxGrid.refocus();
917
- });
918
- }
826
+ handleInsertRowResult
919
827
  };
920
828
  }, [
921
829
  presentation,
922
- dxGrid
830
+ dxGrid,
831
+ model
923
832
  ]);
924
- const handleSaveDraftRow = useCallback5((rowIndex = 0) => {
833
+ const handleSaveDraftRow = useCallback4((rowIndex = 0) => {
925
834
  if (model && dxGrid) {
926
835
  const didCommitSuccessfully = model.commitDraftRow(rowIndex);
927
836
  if (didCommitSuccessfully) {
@@ -934,12 +843,17 @@ var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttenti
934
843
  model,
935
844
  dxGrid
936
845
  ]);
937
- const handleGridClick = useCallback5((event) => {
938
- const rowIndex = safeParseInt(event.target.closest("[aria-rowindex]")?.ariaRowIndex ?? "");
939
- if (rowIndex != null) {
846
+ const handleGridClick = useCallback4((event) => {
847
+ const cell = closestCell(event.target);
848
+ if (cell) {
849
+ const { row: rowIndex, plane } = cell;
940
850
  if (onRowClick) {
941
- const row = model?.getRowAt(rowIndex);
942
- row && onRowClick(row);
851
+ if (plane === "grid") {
852
+ const row = model?.getRowAt(rowIndex);
853
+ row && onRowClick(row);
854
+ } else {
855
+ onRowClick(cell);
856
+ }
943
857
  }
944
858
  if (model?.features.selection.enabled && model?.selection.selectionMode === "single") {
945
859
  model.selection.toggleSelectionForRowIndex(rowIndex);
@@ -1014,25 +928,13 @@ var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttenti
1014
928
  modals,
1015
929
  dxGrid
1016
930
  ]);
1017
- const handleFocus = useCallback5((increment, delta, cell) => {
931
+ const handleFocus = useCallback4((increment, delta, cell) => {
1018
932
  if (dxGrid && model) {
1019
- if (cell?.plane === "grid" && cell?.row >= model.getRowCount() - 1) {
1020
- if (draftRowCount < 1) {
1021
- model.insertRow();
1022
- }
1023
- dxGrid.setFocus({
1024
- plane: "frozenRowsEnd",
1025
- col: 0,
1026
- row: 0
1027
- });
933
+ if (cell?.plane === "grid" && cell?.row >= model.getRowCount() - 1 && increment !== "col") {
934
+ handleInsertRowResult(draftRowCount < 1 ? model.insertRow() : "final");
1028
935
  } else if (cell?.plane === "frozenRowsEnd" && increment === "row") {
1029
936
  handleSaveDraftRow(cell.row);
1030
- model.insertRow();
1031
- dxGrid.setFocus({
1032
- plane: "frozenRowsEnd",
1033
- col: 0,
1034
- row: 0
1035
- });
937
+ handleInsertRowResult(model.insertRow());
1036
938
  } else {
1037
939
  dxGrid.refocus(increment, delta);
1038
940
  }
@@ -1041,7 +943,7 @@ var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttenti
1041
943
  dxGrid,
1042
944
  model
1043
945
  ]);
1044
- const handleKeyDown = useCallback5((event) => {
946
+ const handleKeyDown = useCallback4((event) => {
1045
947
  if (!model?.features.dataEditable) {
1046
948
  return;
1047
949
  }
@@ -1049,21 +951,66 @@ var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttenti
1049
951
  if (!model || !cell) {
1050
952
  return;
1051
953
  }
954
+ if (event.metaKey || event.ctrlKey) {
955
+ switch (event.key) {
956
+ case "c": {
957
+ try {
958
+ const cellData = model.getCellData(cell);
959
+ const textContent = cellData?.toString() ?? "";
960
+ void navigator.clipboard.writeText(textContent);
961
+ event.preventDefault();
962
+ } catch (error) {
963
+ console.warn("Failed to copy cell content:", error);
964
+ }
965
+ break;
966
+ }
967
+ case "v": {
968
+ event.preventDefault();
969
+ void navigator.clipboard.readText().then((clipboardText) => {
970
+ try {
971
+ model.setCellData(cell, String2.trim(clipboardText).replace(/[\n\r]+/, " "));
972
+ handleSave();
973
+ } catch {
974
+ const cellElement = event.target.closest('[data-dx-grid-action="cell"]');
975
+ if (cellElement) {
976
+ const rect = cellElement.getBoundingClientRect();
977
+ const editRequest = new DxEditRequest({
978
+ cellIndex: `${cell.plane},${cell.col},${cell.row}`,
979
+ cellBox: {
980
+ insetInlineStart: rect.left,
981
+ insetBlockStart: rect.top,
982
+ inlineSize: rect.width,
983
+ blockSize: rect.height
984
+ },
985
+ cellElement,
986
+ initialContent: clipboardText
987
+ });
988
+ cellElement.dispatchEvent(editRequest);
989
+ }
990
+ }
991
+ });
992
+ break;
993
+ }
994
+ }
995
+ }
1052
996
  switch (event.key) {
1053
997
  case "Backspace":
1054
998
  case "Delete": {
1055
999
  try {
1056
1000
  model.setCellData(cell, void 0);
1057
1001
  event.preventDefault();
1002
+ handleSave();
1058
1003
  } catch {
1059
1004
  }
1060
1005
  break;
1061
1006
  }
1062
1007
  }
1063
1008
  }, [
1064
- model
1009
+ model,
1010
+ dxGrid,
1011
+ handleSave
1065
1012
  ]);
1066
- const handleAxisResize = useCallback5((event) => {
1013
+ const handleAxisResize = useCallback4((event) => {
1067
1014
  if (event.axis === "col") {
1068
1015
  const columnIndex = parseInt(event.index, 10);
1069
1016
  model?.setColumnWidth(columnIndex, event.size);
@@ -1071,7 +1018,7 @@ var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttenti
1071
1018
  }, [
1072
1019
  model
1073
1020
  ]);
1074
- const handleWheel = useCallback5((event) => {
1021
+ const handleWheel = useCallback4((event) => {
1075
1022
  if (!ignoreAttention && !hasAttention) {
1076
1023
  event.stopPropagation();
1077
1024
  }
@@ -1079,7 +1026,7 @@ var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttenti
1079
1026
  hasAttention,
1080
1027
  ignoreAttention
1081
1028
  ]);
1082
- const handleNewColumn = useCallback5(() => {
1029
+ const handleNewColumn = useCallback4(() => {
1083
1030
  const columns = model?.getColumnCount();
1084
1031
  if (dxGrid && columns) {
1085
1032
  dxGrid.scrollToColumn(columns - 1);
@@ -1088,79 +1035,28 @@ var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttenti
1088
1035
  model,
1089
1036
  dxGrid
1090
1037
  ]);
1091
- const handleQuery = useCallback5(async ({ field, props }, text) => {
1092
- if (model && props.referenceSchema && field.referencePath) {
1093
- const space = getSpace2(model.view);
1094
- invariant3(space, void 0, {
1095
- F: __dxlog_file3,
1096
- L: 336,
1097
- S: void 0,
1098
- A: [
1099
- "space",
1100
- ""
1101
- ]
1102
- });
1103
- let schema2;
1104
- if (client) {
1105
- schema2 = client.graph.schemaRegistry.getSchema(props.referenceSchema);
1106
- }
1107
- if (!schema2) {
1108
- schema2 = space.db.schemaRegistry.getSchema(props.referenceSchema);
1109
- }
1110
- if (schema2) {
1111
- const { objects } = await space.db.query(Filter.type(schema2)).run();
1112
- const options = objects.map((obj) => {
1113
- const value = getValue(obj, field.referencePath);
1114
- if (!value || typeof value !== "string") {
1115
- return void 0;
1116
- }
1117
- return {
1118
- label: value,
1119
- data: obj
1120
- };
1121
- }).filter(isNotFalsy);
1122
- return [
1123
- ...options,
1124
- {
1125
- label: t("create new object label", {
1126
- text
1127
- }),
1128
- data: createOption(text)
1129
- }
1130
- ];
1131
- }
1132
- }
1133
- return [];
1134
- }, [
1135
- model,
1136
- client,
1137
- t
1138
- ]);
1139
- const handleSave = useCallback5(() => {
1140
- dxGrid?.updateCells(true);
1141
- dxGrid?.requestUpdate();
1142
- }, [
1143
- dxGrid
1144
- ]);
1145
1038
  if (!model || !modals) {
1146
- return /* @__PURE__ */ React8.createElement("span", {
1039
+ return /* @__PURE__ */ React7.createElement("span", {
1147
1040
  role: "none",
1148
1041
  className: "attention-surface"
1149
1042
  });
1150
1043
  }
1151
- return /* @__PURE__ */ React8.createElement(Grid.Root, {
1044
+ return /* @__PURE__ */ React7.createElement(Grid.Root, {
1152
1045
  id: model.id ?? "table-grid"
1153
- }, /* @__PURE__ */ React8.createElement(TableValueEditor, {
1046
+ }, /* @__PURE__ */ React7.createElement(TableValueEditor, {
1154
1047
  model,
1155
1048
  modals,
1156
1049
  schema,
1157
1050
  onFocus: handleFocus,
1158
- onQuery: handleQuery,
1159
- onSave: handleSave
1160
- }), /* @__PURE__ */ React8.createElement(Grid.Content, {
1051
+ onSave: handleSave,
1052
+ onCreate,
1053
+ client
1054
+ }), /* @__PURE__ */ React7.createElement(Grid.Content, {
1161
1055
  className: mx("[--dx-grid-base:var(--baseSurface)]", gridSeparatorInlineEnd, gridSeparatorBlockEnd),
1162
1056
  frozen,
1163
1057
  columns: model.columnMeta.value,
1058
+ columnDefault,
1059
+ rowDefault,
1164
1060
  limitRows: model.getRowCount() ?? 0,
1165
1061
  limitColumns: model.projection.fields.length,
1166
1062
  overscroll: "trap",
@@ -1168,20 +1064,20 @@ var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttenti
1168
1064
  onClick: handleGridClick,
1169
1065
  onKeyDownCapture: handleKeyDown,
1170
1066
  onWheelCapture: handleWheel,
1067
+ ...testId && {
1068
+ "data-testid": testId
1069
+ },
1171
1070
  ref: setDxGrid
1172
- }), /* @__PURE__ */ React8.createElement(RowActionsMenu, {
1071
+ }), /* @__PURE__ */ React7.createElement(RowActionsMenu, {
1173
1072
  model,
1174
1073
  modals
1175
- }), /* @__PURE__ */ React8.createElement(ColumnActionsMenu, {
1074
+ }), /* @__PURE__ */ React7.createElement(ColumnActionsMenu, {
1176
1075
  model,
1177
1076
  modals
1178
- }), /* @__PURE__ */ React8.createElement(ColumnSettings, {
1077
+ }), /* @__PURE__ */ React7.createElement(ColumnSettings, {
1179
1078
  model,
1180
1079
  modals,
1181
1080
  onNewColumn: handleNewColumn
1182
- }), /* @__PURE__ */ React8.createElement(CreateRefPanel, {
1183
- model,
1184
- modals
1185
1081
  }));
1186
1082
  } finally {
1187
1083
  _effect.f();
@@ -1193,14 +1089,14 @@ var Table = {
1193
1089
  };
1194
1090
 
1195
1091
  // src/components/Table/DynamicTable.tsx
1196
- import { useSignals as _useSignals9 } from "@preact-signals/safe-react/tracking";
1197
- import React9, { useCallback as useCallback6, useMemo as useMemo6, useRef as useRef3 } from "react";
1092
+ import { useSignals as _useSignals8 } from "@preact-signals/safe-react/tracking";
1093
+ import React8, { useCallback as useCallback5, useMemo as useMemo5, useRef as useRef3 } from "react";
1198
1094
  import { useDefaultValue } from "@dxos/react-ui";
1199
1095
  import { mx as mx2 } from "@dxos/react-ui-theme";
1200
1096
  var DynamicTable = ({ classNames, name = "example.com/dynamic-table", rows, properties, jsonSchema: _jsonSchema, schema, rowActions, onRowClick, onRowAction, ...props }) => {
1201
- var _effect = _useSignals9();
1097
+ var _effect = _useSignals8();
1202
1098
  try {
1203
- const { typename, jsonSchema } = useMemo6(() => getBaseSchema({
1099
+ const { jsonSchema } = useMemo5(() => getBaseSchema({
1204
1100
  typename: name,
1205
1101
  properties,
1206
1102
  jsonSchema: _jsonSchema,
@@ -1211,20 +1107,18 @@ var DynamicTable = ({ classNames, name = "example.com/dynamic-table", rows, prop
1211
1107
  _jsonSchema,
1212
1108
  schema
1213
1109
  ]);
1214
- const { projection, view } = useMemo6(() => makeDynamicTable({
1215
- typename,
1110
+ const { projection, view } = useMemo5(() => makeDynamicTable({
1216
1111
  jsonSchema,
1217
1112
  properties
1218
1113
  }), [
1219
- typename,
1220
1114
  jsonSchema,
1221
1115
  properties
1222
1116
  ]);
1223
1117
  const tableRef = useRef3(null);
1224
- const handleCellUpdate = useCallback6((cell) => {
1118
+ const handleCellUpdate = useCallback5((cell) => {
1225
1119
  tableRef.current?.update?.(cell);
1226
1120
  }, []);
1227
- const handleRowOrderChange = useCallback6(() => {
1121
+ const handleRowOrderChange = useCallback5(() => {
1228
1122
  tableRef.current?.update?.();
1229
1123
  }, []);
1230
1124
  const features = useDefaultValue(props.features, () => ({
@@ -1244,20 +1138,20 @@ var DynamicTable = ({ classNames, name = "example.com/dynamic-table", rows, prop
1244
1138
  onRowOrderChange: handleRowOrderChange,
1245
1139
  onRowAction
1246
1140
  });
1247
- const presentation = useMemo6(() => {
1141
+ const presentation = useMemo5(() => {
1248
1142
  if (model) {
1249
1143
  return new TablePresentation(model);
1250
1144
  }
1251
1145
  }, [
1252
1146
  model
1253
1147
  ]);
1254
- return /* @__PURE__ */ React9.createElement("div", {
1148
+ return /* @__PURE__ */ React8.createElement("div", {
1255
1149
  role: "none",
1256
1150
  className: mx2("is-full bs-full grow grid", classNames)
1257
- }, /* @__PURE__ */ React9.createElement("div", {
1151
+ }, /* @__PURE__ */ React8.createElement("div", {
1258
1152
  role: "none",
1259
1153
  className: "grid min-bs-0 overflow-hidden"
1260
- }, /* @__PURE__ */ React9.createElement(Table.Root, null, /* @__PURE__ */ React9.createElement(Table.Main, {
1154
+ }, /* @__PURE__ */ React8.createElement(Table.Root, null, /* @__PURE__ */ React8.createElement(Table.Main, {
1261
1155
  ref: tableRef,
1262
1156
  model,
1263
1157
  presentation,
@@ -1270,43 +1164,46 @@ var DynamicTable = ({ classNames, name = "example.com/dynamic-table", rows, prop
1270
1164
  };
1271
1165
 
1272
1166
  // src/components/TableToolbar/TableToolbar.tsx
1273
- import { useSignals as _useSignals10 } from "@preact-signals/safe-react/tracking";
1274
- import { Rx } from "@effect-rx/rx-react";
1275
- import React10, { useMemo as useMemo7 } from "react";
1276
- import { live as live2 } from "@dxos/live-object";
1167
+ import { useSignals as _useSignals9 } from "@preact-signals/safe-react/tracking";
1168
+ import { Atom } from "@effect-atom/atom-react";
1169
+ import React9, { useMemo as useMemo6 } from "react";
1170
+ import { live } from "@dxos/live-object";
1277
1171
  import { MenuProvider, ToolbarMenu, createGapSeparator, createMenuAction, useMenuActions } from "@dxos/react-ui-menu";
1278
- import { rxFromSignal } from "@dxos/react-ui-menu";
1279
- var createTableToolbarActions = ({ state, onAdd, onSave, customActions }) => Rx.make((get) => {
1280
- const add = createMenuAction("add-row", onAdd, {
1281
- type: "add-row",
1282
- icon: "ph--plus--regular",
1283
- label: [
1284
- "add row",
1285
- {
1286
- ns: translationKey
1287
- }
1288
- ],
1289
- testId: "table.toolbar.add-row"
1290
- });
1291
- const save = createMenuAction("save-view", onSave, {
1292
- type: "save-view",
1293
- icon: "ph--floppy-disk--regular",
1294
- label: [
1295
- "save view label",
1296
- {
1297
- ns: translationKey
1298
- }
1299
- ],
1300
- testId: "table.toolbar.save-view",
1301
- iconOnly: false,
1302
- hidden: get(rxFromSignal(() => !state.viewDirty))
1303
- });
1172
+ import { atomFromSignal } from "@dxos/react-ui-menu";
1173
+ var createTableToolbarActions = ({ state, onAdd, onSave, customActions }) => Atom.make((get) => {
1174
+ const nodes = [];
1175
+ if (onAdd) {
1176
+ const add = createMenuAction("add-row", onAdd, {
1177
+ type: "add-row",
1178
+ icon: "ph--plus--regular",
1179
+ label: [
1180
+ "add row",
1181
+ {
1182
+ ns: translationKey
1183
+ }
1184
+ ],
1185
+ testId: "table.toolbar.add-row"
1186
+ });
1187
+ nodes.push(add);
1188
+ }
1189
+ if (onSave) {
1190
+ const save = createMenuAction("save-view", onSave, {
1191
+ type: "save-view",
1192
+ icon: "ph--floppy-disk--regular",
1193
+ label: [
1194
+ "save view label",
1195
+ {
1196
+ ns: translationKey
1197
+ }
1198
+ ],
1199
+ testId: "table.toolbar.save-view",
1200
+ iconOnly: false,
1201
+ hidden: get(atomFromSignal(() => !state.viewDirty))
1202
+ });
1203
+ nodes.push(save);
1204
+ }
1304
1205
  const gap = createGapSeparator();
1305
- const nodes = [
1306
- add,
1307
- save,
1308
- ...gap.nodes
1309
- ];
1206
+ nodes.push(...gap.nodes);
1310
1207
  const edges = nodes.map(({ id: target }) => ({
1311
1208
  source: "root",
1312
1209
  target
@@ -1322,12 +1219,12 @@ var createTableToolbarActions = ({ state, onAdd, onSave, customActions }) => Rx.
1322
1219
  };
1323
1220
  });
1324
1221
  var TableToolbar = ({ classNames, viewDirty, attendableId, onAdd, onSave, customActions }) => {
1325
- var _effect = _useSignals10();
1222
+ var _effect = _useSignals9();
1326
1223
  try {
1327
- const state = useMemo7(() => live2({
1224
+ const state = useMemo6(() => live({
1328
1225
  viewDirty
1329
1226
  }), []);
1330
- const actionsCreator = useMemo7(() => createTableToolbarActions({
1227
+ const actionsCreator = useMemo6(() => createTableToolbarActions({
1331
1228
  state,
1332
1229
  onAdd,
1333
1230
  onSave,
@@ -1339,10 +1236,10 @@ var TableToolbar = ({ classNames, viewDirty, attendableId, onAdd, onSave, custom
1339
1236
  customActions
1340
1237
  ]);
1341
1238
  const menu = useMenuActions(actionsCreator);
1342
- return /* @__PURE__ */ React10.createElement(MenuProvider, {
1239
+ return /* @__PURE__ */ React9.createElement(MenuProvider, {
1343
1240
  ...menu,
1344
1241
  attendableId
1345
- }, /* @__PURE__ */ React10.createElement(ToolbarMenu, {
1242
+ }, /* @__PURE__ */ React9.createElement(ToolbarMenu, {
1346
1243
  classNames
1347
1244
  }));
1348
1245
  } finally {
@@ -1361,7 +1258,6 @@ export {
1361
1258
  TablePresentation,
1362
1259
  TableToolbar,
1363
1260
  cellClassesForRowSelection,
1364
- createTable,
1365
1261
  getBaseSchema,
1366
1262
  makeDynamicTable,
1367
1263
  narrowSchema,