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