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

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