@dxos/react-ui-table 0.8.4-main.67995b8 → 0.8.4-main.72ec0f3

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 (132) 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-5XLSHLOG.mjs → chunk-GS2UNGJK.mjs} +493 -469
  4. package/dist/lib/browser/chunk-GS2UNGJK.mjs.map +7 -0
  5. package/dist/lib/browser/index.mjs +610 -695
  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 +14 -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-Y66YLSTC.mjs → chunk-TRR3C5NX.mjs} +493 -469
  14. package/dist/lib/node-esm/chunk-TRR3C5NX.mjs.map +7 -0
  15. package/dist/lib/node-esm/index.mjs +610 -695
  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 +14 -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/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/DynamicTable.d.ts +2 -2
  26. package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
  27. package/dist/types/src/components/Table/DynamicTable.stories.d.ts +51 -3
  28. package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
  29. package/dist/types/src/components/Table/Relations.stories.d.ts +107 -0
  30. package/dist/types/src/components/Table/Relations.stories.d.ts.map +1 -0
  31. package/dist/types/src/components/Table/RowActionsMenu.d.ts +1 -1
  32. package/dist/types/src/components/Table/RowActionsMenu.d.ts.map +1 -1
  33. package/dist/types/src/components/Table/Table.d.ts +8 -4
  34. package/dist/types/src/components/Table/Table.d.ts.map +1 -1
  35. package/dist/types/src/components/Table/Table.stories.d.ts +42 -4
  36. package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
  37. package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts.map +1 -1
  38. package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +12 -6
  39. package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
  40. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +12 -17
  41. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
  42. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts +39 -4
  43. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
  44. package/dist/types/src/components/TableCellEditor/index.d.ts +1 -0
  45. package/dist/types/src/components/TableCellEditor/index.d.ts.map +1 -1
  46. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts +2 -2
  47. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +1 -1
  48. package/dist/types/src/components/testing.d.ts +3 -3
  49. package/dist/types/src/components/testing.d.ts.map +1 -1
  50. package/dist/types/src/hooks/useAddRow.d.ts +3 -2
  51. package/dist/types/src/hooks/useAddRow.d.ts.map +1 -1
  52. package/dist/types/src/hooks/useTableModel.d.ts +4 -4
  53. package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
  54. package/dist/types/src/model/modal-controller.d.ts +0 -7
  55. package/dist/types/src/model/modal-controller.d.ts.map +1 -1
  56. package/dist/types/src/model/selection-model.d.ts.map +1 -1
  57. package/dist/types/src/model/table-model.d.ts +11 -9
  58. package/dist/types/src/model/table-model.d.ts.map +1 -1
  59. package/dist/types/src/model/table-presentation.d.ts +2 -1
  60. package/dist/types/src/model/table-presentation.d.ts.map +1 -1
  61. package/dist/types/src/model/table-sorting.d.ts +3 -4
  62. package/dist/types/src/model/table-sorting.d.ts.map +1 -1
  63. package/dist/types/src/testing/useTestTableModel.d.ts +3 -22
  64. package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -1
  65. package/dist/types/src/translations.d.ts +2 -2
  66. package/dist/types/src/types/index.d.ts +1 -1
  67. package/dist/types/src/types/index.d.ts.map +1 -1
  68. package/dist/types/src/types/schema.d.ts +25 -5
  69. package/dist/types/src/types/schema.d.ts.map +1 -1
  70. package/dist/types/src/util/dynamic-table.d.ts +5 -6
  71. package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
  72. package/dist/types/src/util/index.d.ts +0 -1
  73. package/dist/types/src/util/index.d.ts.map +1 -1
  74. package/dist/types/src/util/schema.d.ts +1 -1
  75. package/dist/types/src/util/schema.d.ts.map +1 -1
  76. package/dist/types/src/util/table-buttons.d.ts.map +1 -1
  77. package/dist/types/src/util/table-controls.d.ts.map +1 -1
  78. package/dist/types/tsconfig.tsbuildinfo +1 -1
  79. package/package.json +61 -59
  80. package/src/components/Table/ColumnActionsMenu.tsx +1 -1
  81. package/src/components/Table/ColumnSettings.tsx +1 -1
  82. package/src/components/Table/DynamicTable.stories.tsx +18 -14
  83. package/src/components/Table/DynamicTable.tsx +8 -10
  84. package/src/components/Table/Relations.stories.tsx +242 -0
  85. package/src/components/Table/RowActionsMenu.tsx +23 -15
  86. package/src/components/Table/Table.stories.tsx +72 -60
  87. package/src/components/Table/Table.tsx +140 -115
  88. package/src/components/TableCellEditor/CellValidationMessage.tsx +1 -1
  89. package/src/components/TableCellEditor/FormCellEditor.tsx +153 -42
  90. package/src/components/TableCellEditor/TableCellEditor.stories.tsx +26 -65
  91. package/src/components/TableCellEditor/TableCellEditor.tsx +74 -224
  92. package/src/components/TableCellEditor/index.ts +1 -0
  93. package/src/components/TableToolbar/TableToolbar.tsx +34 -24
  94. package/src/components/testing.ts +2 -2
  95. package/src/hooks/useAddRow.ts +7 -5
  96. package/src/hooks/useTableModel.ts +6 -6
  97. package/src/model/modal-controller.ts +1 -29
  98. package/src/model/selection-model.ts +3 -2
  99. package/src/model/table-model.test.ts +20 -11
  100. package/src/model/table-model.ts +46 -31
  101. package/src/model/table-presentation.test.ts +24 -22
  102. package/src/model/table-presentation.ts +74 -33
  103. package/src/model/table-sorting.ts +17 -8
  104. package/src/playwright/TableManager.ts +1 -1
  105. package/src/playwright/smoke.spec.ts +131 -37
  106. package/src/testing/useTestTableModel.ts +12 -8
  107. package/src/translations.ts +2 -2
  108. package/src/types/index.ts +1 -1
  109. package/src/types/schema.ts +63 -6
  110. package/src/util/dynamic-table.ts +16 -17
  111. package/src/util/index.ts +0 -1
  112. package/src/util/schema.ts +4 -4
  113. package/src/util/table-buttons.ts +4 -3
  114. package/src/util/table-controls.ts +4 -1
  115. package/dist/lib/browser/chunk-5XLSHLOG.mjs.map +0 -7
  116. package/dist/lib/browser/chunk-L2LSB6KV.mjs +0 -18
  117. package/dist/lib/browser/chunk-L2LSB6KV.mjs.map +0 -7
  118. package/dist/lib/node-esm/chunk-NXZ4CJYX.mjs +0 -20
  119. package/dist/lib/node-esm/chunk-NXZ4CJYX.mjs.map +0 -7
  120. package/dist/lib/node-esm/chunk-Y66YLSTC.mjs.map +0 -7
  121. package/dist/types/src/components/Table/CreateRefPanel.d.ts +0 -9
  122. package/dist/types/src/components/Table/CreateRefPanel.d.ts.map +0 -1
  123. package/dist/types/src/components/Table/relations.stories.d.ts +0 -9
  124. package/dist/types/src/components/Table/relations.stories.d.ts.map +0 -1
  125. package/dist/types/src/components/TableCellEditor/extension.d.ts +0 -13
  126. package/dist/types/src/components/TableCellEditor/extension.d.ts.map +0 -1
  127. package/dist/types/src/util/table.d.ts +0 -11
  128. package/dist/types/src/util/table.d.ts.map +0 -1
  129. package/src/components/Table/CreateRefPanel.tsx +0 -86
  130. package/src/components/Table/relations.stories.tsx +0 -116
  131. package/src/components/TableCellEditor/extension.ts +0 -61
  132. 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-GS2UNGJK.mjs";
19
+ import "./chunk-2PFAPQL4.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",
@@ -52,271 +51,37 @@ 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, 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";
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
- import { closestCell, Grid, gridSeparatorInlineEnd, gridSeparatorBlockEnd } from "@dxos/react-ui-grid";
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
-
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
61
 
296
62
  // 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";
299
- import { FormatEnum, TypeEnum } from "@dxos/echo-schema";
63
+ import { useSignals as _useSignals3 } from "@preact-signals/safe-react/tracking";
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";
300
68
  import { invariant as invariant2 } from "@dxos/invariant";
301
- import { useThemeContext } from "@dxos/react-ui";
302
- 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";
305
70
 
306
71
  // 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";
72
+ import { useSignals as _useSignals } from "@preact-signals/safe-react/tracking";
73
+ import React from "react";
74
+ import { useGridContext } from "@dxos/react-ui-grid";
310
75
  var CellValidationMessage = ({ validationError, variant = "error", __gridScope }) => {
311
- var _effect = _useSignals5();
76
+ var _effect = _useSignals();
312
77
  try {
313
- const { editing, editBox: box } = useGridContext2("GridSheetCellEditor", __gridScope);
78
+ const { editing, editBox: box } = useGridContext("GridSheetCellEditor", __gridScope);
314
79
  if (!editing || !validationError) {
315
80
  return null;
316
81
  }
317
82
  const bgClass = variant === "error" ? "bg-errorSurface" : "bg-warningSurface";
318
83
  const textClass = variant === "error" ? "text-errorSurfaceText" : "text-warningSurfaceText";
319
- return /* @__PURE__ */ React5.createElement("div", {
84
+ return /* @__PURE__ */ React.createElement("div", {
320
85
  role: "none",
321
86
  className: `absolute ${bgClass} ${textClass} rounded-bs-sm text-xs p-1`,
322
87
  style: {
@@ -335,71 +100,108 @@ var CellValidationMessage = ({ validationError, variant = "error", __gridScope }
335
100
  };
336
101
 
337
102
  // 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";
103
+ import { useSignals as _useSignals2 } from "@preact-signals/safe-react/tracking";
104
+ import * as Schema from "effect/Schema";
105
+ import React2, { useCallback, useEffect, useMemo, useRef, useState } from "react";
106
+ import { Filter, Obj } from "@dxos/echo";
107
+ import { FormatEnum, Ref, getValue } from "@dxos/echo/internal";
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 { parseCellIndex, useGridContext as useGridContext2 } from "@dxos/react-ui-grid";
114
+ import { getDeep, isTruthy, 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, onCreate, __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: 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);
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(isTruthy);
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: 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
+ }
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,93 +209,128 @@ 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
+ 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) : {}, [
415
245
  originalRow
416
246
  ]);
417
- const handleOpenChange = useCallback3((nextOpen) => {
247
+ const handleOpenChange = useCallback((nextOpen) => {
418
248
  if (nextOpen === false) {
419
249
  setEditing(null);
420
250
  onSave?.();
421
251
  }
422
252
  setLocalEditing(nextOpen);
423
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
+ ]);
424
295
  if (!editing) {
425
296
  return null;
426
297
  }
427
- return /* @__PURE__ */ React6.createElement(Popover3.Root, {
298
+ return /* @__PURE__ */ React2.createElement(Popover.Root, {
428
299
  open: editing,
429
300
  onOpenChange: handleOpenChange
430
- }, /* @__PURE__ */ React6.createElement(Popover3.VirtualTrigger, {
301
+ }, /* @__PURE__ */ React2.createElement(Popover.VirtualTrigger, {
431
302
  virtualRef: anchorRef
432
- }), /* @__PURE__ */ React6.createElement(Popover3.Content, {
303
+ }), /* @__PURE__ */ React2.createElement(Popover.Portal, null, /* @__PURE__ */ React2.createElement(Popover.Content, {
433
304
  tabIndex: -1,
434
305
  classNames: "popover-card-width density-fine"
435
- }, /* @__PURE__ */ React6.createElement(Popover3.Arrow, null), /* @__PURE__ */ React6.createElement(Popover3.Viewport, null, /* @__PURE__ */ React6.createElement(Form2, {
306
+ }, /* @__PURE__ */ React2.createElement(Popover.Arrow, null), /* @__PURE__ */ React2.createElement(Popover.Viewport, null, /* @__PURE__ */ React2.createElement(Form, {
307
+ autoFocus: true,
436
308
  values: formValues,
437
309
  schema: narrowedSchema,
438
- onSave: handleSave
439
- }))));
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
+ })))));
440
321
  } finally {
441
322
  _effect.f();
442
323
  }
443
324
  };
444
325
 
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
326
  // src/components/TableCellEditor/TableCellEditor.tsx
485
327
  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();
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 }) => {
330
+ var _effect = _useSignals3();
494
331
  try {
495
- const { editing } = useGridContext4("TableValueEditor", __gridScope);
496
- const fieldProjection = useMemo4(() => {
332
+ const { editing } = useGridContext3("TableValueEditor", __gridScope);
333
+ const fieldProjection = useMemo2(() => {
497
334
  if (!model || !editing) {
498
335
  return;
499
336
  }
@@ -502,7 +339,7 @@ var TableValueEditor = ({ model, modals, schema, onEnter, onFocus, onSave, onQue
502
339
  const fieldProjection2 = model.projection.getFieldProjection(field.id);
503
340
  invariant2(fieldProjection2, void 0, {
504
341
  F: __dxlog_file2,
505
- L: 76,
342
+ L: 71,
506
343
  S: void 0,
507
344
  A: [
508
345
  "fieldProjection",
@@ -514,21 +351,22 @@ var TableValueEditor = ({ model, modals, schema, onEnter, onFocus, onSave, onQue
514
351
  model,
515
352
  editing
516
353
  ]);
517
- if (fieldProjection?.props.type === TypeEnum.Array || fieldProjection?.props.format === FormatEnum.SingleSelect) {
518
- return /* @__PURE__ */ React7.createElement(FormCellEditor, {
354
+ if (fieldProjection?.props.type === TypeEnum.Array || fieldProjection?.props.format === FormatEnum2.SingleSelect || fieldProjection?.props.format === FormatEnum2.Ref) {
355
+ return /* @__PURE__ */ React3.createElement(FormCellEditor, {
519
356
  fieldProjection,
520
357
  model,
521
358
  schema,
522
359
  __gridScope,
523
- onSave
360
+ onSave,
361
+ onCreate,
362
+ client,
363
+ modals
524
364
  });
525
365
  }
526
- return /* @__PURE__ */ React7.createElement(TableCellEditor, {
366
+ return /* @__PURE__ */ React3.createElement(TableCellEditor, {
527
367
  model,
528
368
  modals,
529
- onEnter,
530
369
  onFocus,
531
- onQuery,
532
370
  onSave,
533
371
  __gridScope
534
372
  });
@@ -537,19 +375,18 @@ var TableValueEditor = ({ model, modals, schema, onEnter, onFocus, onSave, onQue
537
375
  }
538
376
  };
539
377
  var editorSlots = {
540
- scroller: {
378
+ scroll: {
541
379
  className: "!plb-[--dx-grid-cell-editor-padding-block]"
542
380
  }
543
381
  };
544
- var TableCellEditor = ({ model, modals, onEnter, onFocus, onQuery, onSave, __gridScope }) => {
545
- var _effect = _useSignals7();
382
+ var TableCellEditor = ({ model, modals, onFocus, onSave, __gridScope }) => {
383
+ var _effect = _useSignals3();
546
384
  try {
547
- const { id: gridId, editing, setEditing } = useGridContext4("TableCellEditor", __gridScope);
385
+ const { editing, setEditing } = useGridContext3("TableCellEditor", __gridScope);
548
386
  const suppressNextBlur = useRef2(false);
549
- const { themeMode } = useThemeContext();
550
- const [_validationError, setValidationError] = useState3(null);
551
- const [_validationVariant, setValidationVariant] = useState3("error");
552
- const fieldProjection = useMemo4(() => {
387
+ const [validationError, setValidationError] = useState2(null);
388
+ const [validationVariant, setValidationVariant] = useState2("error");
389
+ const fieldProjection = useMemo2(() => {
553
390
  if (!model || !editing) {
554
391
  return;
555
392
  }
@@ -558,7 +395,7 @@ var TableCellEditor = ({ model, modals, onEnter, onFocus, onQuery, onSave, __gri
558
395
  const fieldProjection2 = model.projection.getFieldProjection(field.id);
559
396
  invariant2(fieldProjection2, void 0, {
560
397
  F: __dxlog_file2,
561
- L: 135,
398
+ L: 121,
562
399
  S: void 0,
563
400
  A: [
564
401
  "fieldProjection",
@@ -568,40 +405,9 @@ var TableCellEditor = ({ model, modals, onEnter, onFocus, onQuery, onSave, __gri
568
405
  return fieldProjection2;
569
406
  }, [
570
407
  model,
571
- editing
572
- ]);
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
408
+ editing
603
409
  ]);
604
- const handleEnter = useCallback4(async (value) => {
410
+ const handleEnter = useCallback2(async (value) => {
605
411
  if (!model || !editing) {
606
412
  return;
607
413
  }
@@ -610,33 +416,39 @@ var TableCellEditor = ({ model, modals, onEnter, onFocus, onQuery, onSave, __gri
610
416
  if (validationResult.valid) {
611
417
  setValidationError(null);
612
418
  model.setCellData(cell, value);
613
- onEnter?.(cell);
614
419
  onFocus?.();
615
420
  setEditing(null);
616
421
  onSave?.();
617
422
  } else {
618
- setValidationError(validationResult.error);
423
+ setValidationError(adaptValidationMessage(validationResult.error));
619
424
  setValidationVariant("error");
620
425
  }
621
426
  }, [
622
427
  model,
623
428
  editing,
624
- onEnter,
625
429
  onFocus,
626
430
  setEditing
627
431
  ]);
628
- const handleBlur = useCallback4(async (value) => {
629
- if (!model || !editing) {
630
- return;
631
- }
432
+ const handleBlur = useCallback2(async (value) => {
632
433
  if (suppressNextBlur.current) {
633
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
+ }
634
446
  }
635
447
  }, [
636
448
  model,
637
449
  editing
638
450
  ]);
639
- const handleClose = useCallback4(async (value, event) => {
451
+ const handleClose = useCallback2(async (value, event) => {
640
452
  if (!model || !editing || !fieldProjection) {
641
453
  return;
642
454
  }
@@ -644,19 +456,20 @@ var TableCellEditor = ({ model, modals, onEnter, onFocus, onQuery, onSave, __gri
644
456
  if (value !== void 0) {
645
457
  const result = await model.validateCellData(cell, value);
646
458
  if (result.valid) {
459
+ suppressNextBlur.current = true;
647
460
  setValidationError(null);
648
461
  model.setCellData(cell, value);
649
462
  setEditing(null);
650
463
  onSave?.();
651
- onEnter?.(cell);
652
464
  if (event && onFocus) {
653
- onFocus(determineNavigationAxis(event), determineNavigationDelta(event));
465
+ onFocus(determineNavigationAxis(event), determineNavigationDelta(event), cell);
654
466
  }
655
467
  } else {
656
468
  setValidationError(result.error);
657
469
  setValidationVariant("error");
658
470
  }
659
471
  } else {
472
+ suppressNextBlur.current = true;
660
473
  setValidationError(null);
661
474
  setEditing(null);
662
475
  onSave?.();
@@ -668,16 +481,15 @@ var TableCellEditor = ({ model, modals, onEnter, onFocus, onQuery, onSave, __gri
668
481
  model,
669
482
  editing,
670
483
  onFocus,
671
- onEnter,
672
484
  fieldProjection,
673
485
  setEditing,
674
486
  onSave
675
487
  ]);
676
- const extension = useMemo4(() => {
488
+ const extensions = useMemo2(() => {
677
489
  if (!fieldProjection) {
678
490
  return [];
679
491
  }
680
- const extension2 = [
492
+ const extensions2 = [
681
493
  editorKeys({
682
494
  onClose: handleClose,
683
495
  ...editing?.initialContent && {
@@ -685,114 +497,31 @@ var TableCellEditor = ({ model, modals, onEnter, onFocus, onQuery, onSave, __gri
685
497
  }
686
498
  })
687
499
  ];
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
- }
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");
715
510
  }
716
- }
717
- }));
718
- }
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
- }
511
+ });
512
+ }, 10)));
747
513
  }
748
- return extension2;
514
+ return extensions2;
749
515
  }, [
750
516
  model,
751
517
  modals,
752
518
  editing,
753
519
  fieldProjection,
754
- handleClose,
755
- themeMode
520
+ handleClose
756
521
  ]);
757
- const getCellContent = useCallback4(() => {
522
+ const getCellContent = useCallback2(() => {
758
523
  if (model && editing) {
759
524
  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
525
  const value = model.getCellData(cell);
797
526
  return value !== void 0 ? String(value) : "";
798
527
  }
@@ -800,12 +529,12 @@ var TableCellEditor = ({ model, modals, onEnter, onFocus, onQuery, onSave, __gri
800
529
  model,
801
530
  editing
802
531
  ]);
803
- return /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement(CellValidationMessage, {
804
- validationError: _validationError,
805
- variant: _validationVariant,
532
+ return /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement(CellValidationMessage, {
533
+ validationError,
534
+ variant: validationVariant,
806
535
  __gridScope
807
- }), /* @__PURE__ */ React7.createElement(GridCellEditor, {
808
- extension,
536
+ }), /* @__PURE__ */ React3.createElement(GridCellEditor, {
537
+ extensions,
809
538
  getCellContent,
810
539
  onBlur: handleBlur,
811
540
  slots: editorSlots
@@ -839,12 +568,171 @@ var determineNavigationDelta = ({ key, shift }) => {
839
568
  return shift ? -1 : 1;
840
569
  };
841
570
 
571
+ // src/components/Table/ColumnActionsMenu.tsx
572
+ import { useSignals as _useSignals4 } from "@preact-signals/safe-react/tracking";
573
+ import React4 from "react";
574
+ import { DropdownMenu, useTranslation } from "@dxos/react-ui";
575
+ var ColumnActionsMenu = ({ model, modals }) => {
576
+ var _effect = _useSignals4();
577
+ try {
578
+ const { t } = useTranslation(translationKey);
579
+ const state = modals.state.value;
580
+ if (state?.type !== "column") {
581
+ return null;
582
+ }
583
+ const currentSort = model.sorting?.sorting;
584
+ const isCurrentColumnSorted = currentSort?.fieldId === state.fieldId;
585
+ return /* @__PURE__ */ React4.createElement(DropdownMenu.Root, {
586
+ modal: false,
587
+ open: true,
588
+ onOpenChange: modals.close
589
+ }, /* @__PURE__ */ React4.createElement(DropdownMenu.VirtualTrigger, {
590
+ virtualRef: modals.trigger
591
+ }), /* @__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, {
592
+ "data-testid": "column-sort-descending",
593
+ onClick: () => model.sorting?.setSort(state.fieldId, "desc")
594
+ }, t("column action sort descending")), (!isCurrentColumnSorted || currentSort?.direction === "desc") && /* @__PURE__ */ React4.createElement(DropdownMenu.Item, {
595
+ "data-testid": "column-sort-ascending",
596
+ onClick: () => model.sorting?.setSort(state.fieldId, "asc")
597
+ }, t("column action sort ascending")), isCurrentColumnSorted && /* @__PURE__ */ React4.createElement(DropdownMenu.Item, {
598
+ "data-testid": "column-clear-sort",
599
+ onClick: () => model.sorting?.clearSort()
600
+ }, t("column action clear sorting")), model.getColumnCount() > 1 && model.features.schemaEditable && /* @__PURE__ */ React4.createElement(DropdownMenu.Item, {
601
+ "data-testid": "column-delete",
602
+ onClick: () => model.deleteColumn(state.fieldId)
603
+ }, t("column action delete")), model.features.schemaEditable && /* @__PURE__ */ React4.createElement(DropdownMenu.Item, {
604
+ "data-testid": "column-settings",
605
+ onClick: () => modals.openColumnSettings()
606
+ }, t("column action settings"))), /* @__PURE__ */ React4.createElement(DropdownMenu.Arrow, null))));
607
+ } finally {
608
+ _effect.f();
609
+ }
610
+ };
611
+
612
+ // src/components/Table/ColumnSettings.tsx
613
+ import { useSignals as _useSignals5 } from "@preact-signals/safe-react/tracking";
614
+ import React5, { useCallback as useCallback3, useEffect as useEffect2, useMemo as useMemo3, useState as useState3 } from "react";
615
+ import { Popover as Popover2 } from "@dxos/react-ui";
616
+ import { FieldEditor } from "@dxos/react-ui-form";
617
+ var ColumnSettings = ({ registry, model, modals, onNewColumn }) => {
618
+ var _effect = _useSignals5();
619
+ try {
620
+ const [newField, setNewField] = useState3();
621
+ const state = modals.state.value;
622
+ useEffect2(() => {
623
+ if (state?.type === "columnSettings" && state.mode.type === "create" && model?.projection) {
624
+ setNewField(model.projection.createFieldProjection());
625
+ requestAnimationFrame(() => {
626
+ onNewColumn();
627
+ });
628
+ } else {
629
+ setNewField(void 0);
630
+ }
631
+ }, [
632
+ model?.projection,
633
+ state
634
+ ]);
635
+ const existingField = useMemo3(() => {
636
+ if (state?.type === "columnSettings") {
637
+ const { mode } = state;
638
+ if (mode.type === "edit") {
639
+ return model?.projection?.fields.find((f) => f.id === mode.fieldId);
640
+ }
641
+ }
642
+ return void 0;
643
+ }, [
644
+ model?.projection?.fields,
645
+ state
646
+ ]);
647
+ const field = existingField ?? newField;
648
+ const handleSave = useCallback3(() => {
649
+ modals.close();
650
+ }, [
651
+ modals
652
+ ]);
653
+ const handleCancel = useCallback3(() => {
654
+ if (state?.type === "columnSettings" && state.mode.type === "create" && newField) {
655
+ model?.projection.deleteFieldProjection(newField.id);
656
+ }
657
+ }, [
658
+ model?.projection,
659
+ state,
660
+ newField
661
+ ]);
662
+ if (!model?.projection || !field) {
663
+ return null;
664
+ }
665
+ return /* @__PURE__ */ React5.createElement(Popover2.Root, {
666
+ modal: false,
667
+ open: state?.type === "columnSettings"
668
+ }, /* @__PURE__ */ React5.createElement(Popover2.VirtualTrigger, {
669
+ virtualRef: modals.trigger
670
+ }), /* @__PURE__ */ React5.createElement(Popover2.Portal, null, /* @__PURE__ */ React5.createElement(Popover2.Content, {
671
+ classNames: "md:is-64"
672
+ }, /* @__PURE__ */ React5.createElement(Popover2.Viewport, null, /* @__PURE__ */ React5.createElement(FieldEditor, {
673
+ projection: model.projection,
674
+ field,
675
+ registry,
676
+ onSave: handleSave,
677
+ onCancel: handleCancel
678
+ })), /* @__PURE__ */ React5.createElement(Popover2.Arrow, null))));
679
+ } finally {
680
+ _effect.f();
681
+ }
682
+ };
683
+
684
+ // src/components/Table/RowActionsMenu.tsx
685
+ import { useSignals as _useSignals6 } from "@preact-signals/safe-react/tracking";
686
+ import React6 from "react";
687
+ import { DropdownMenu as DropdownMenu2, toLocalizedString, useTranslation as useTranslation2 } from "@dxos/react-ui";
688
+ var RowActionsMenu = ({ model, modals }) => {
689
+ var _effect = _useSignals6();
690
+ try {
691
+ const { t } = useTranslation2(translationKey);
692
+ const hasSelection = model.selection.hasSelection.value;
693
+ const state = modals.state.value;
694
+ if (state?.type !== "row") {
695
+ return null;
696
+ }
697
+ return /* @__PURE__ */ React6.createElement(DropdownMenu2.Root, {
698
+ modal: false,
699
+ open: true,
700
+ onOpenChange: modals.close
701
+ }, /* @__PURE__ */ React6.createElement(DropdownMenu2.VirtualTrigger, {
702
+ virtualRef: modals.trigger
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, {
704
+ key: action.id,
705
+ "data-testid": `row-action-${action.id}`,
706
+ onClick: () => {
707
+ modals.close();
708
+ model.handleRowAction(action.id, state.rowIndex);
709
+ }
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)));
714
+ } finally {
715
+ _effect.f();
716
+ }
717
+ };
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 {};
@@ -904,18 +821,38 @@ var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttenti
904
821
  dxGrid.updateCells(true);
905
822
  dxGrid.requestUpdate();
906
823
  }
907
- }
824
+ },
825
+ handleInsertRowResult
908
826
  };
909
827
  }, [
910
828
  presentation,
829
+ dxGrid,
830
+ model
831
+ ]);
832
+ const handleSaveDraftRow = useCallback4((rowIndex = 0) => {
833
+ if (model && dxGrid) {
834
+ const didCommitSuccessfully = model.commitDraftRow(rowIndex);
835
+ if (didCommitSuccessfully) {
836
+ requestAnimationFrame(() => {
837
+ dxGrid.scrollToEndRow();
838
+ });
839
+ }
840
+ }
841
+ }, [
842
+ model,
911
843
  dxGrid
912
844
  ]);
913
- const handleGridClick = useCallback5((event) => {
914
- const rowIndex = safeParseInt(event.target.closest("[aria-rowindex]")?.ariaRowIndex ?? "");
915
- if (rowIndex != null) {
845
+ const handleGridClick = useCallback4((event) => {
846
+ const cell = closestCell(event.target);
847
+ if (cell) {
848
+ const { row: rowIndex, plane } = cell;
916
849
  if (onRowClick) {
917
- const row = model?.getRowAt(rowIndex);
918
- row && onRowClick(row);
850
+ if (plane === "grid") {
851
+ const row = model?.getRowAt(rowIndex);
852
+ row && onRowClick(row);
853
+ } else {
854
+ onRowClick(cell);
855
+ }
919
856
  }
920
857
  if (model?.features.selection.enabled && model?.selection.selectionMode === "single") {
921
858
  model.selection.toggleSelectionForRowIndex(rowIndex);
@@ -951,14 +888,7 @@ var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttenti
951
888
  break;
952
889
  }
953
890
  case "saveDraftRow": {
954
- if (model) {
955
- const didCommitSuccessfully = model.commitDraftRow(data.rowIndex);
956
- if (dxGrid && didCommitSuccessfully) {
957
- requestAnimationFrame(() => {
958
- dxGrid.scrollToEndRow();
959
- });
960
- }
961
- }
891
+ handleSaveDraftRow();
962
892
  break;
963
893
  }
964
894
  }
@@ -997,31 +927,22 @@ var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttenti
997
927
  modals,
998
928
  dxGrid
999
929
  ]);
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
- });
930
+ const handleFocus = useCallback4((increment, delta, cell) => {
931
+ if (dxGrid && model) {
932
+ if (cell?.plane === "grid" && cell?.row >= model.getRowCount() - 1 && increment !== "col") {
933
+ handleInsertRowResult(draftRowCount < 1 ? model.insertRow() : "final");
934
+ } else if (cell?.plane === "frozenRowsEnd" && increment === "row") {
935
+ handleSaveDraftRow(cell.row);
936
+ handleInsertRowResult(model.insertRow());
937
+ } else {
938
+ dxGrid.refocus(increment, delta);
1018
939
  }
1019
940
  }
1020
941
  }, [
1021
- model,
1022
- dxGrid
942
+ dxGrid,
943
+ model
1023
944
  ]);
1024
- const handleKeyDown = useCallback5((event) => {
945
+ const handleKeyDown = useCallback4((event) => {
1025
946
  if (!model?.features.dataEditable) {
1026
947
  return;
1027
948
  }
@@ -1029,21 +950,66 @@ var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttenti
1029
950
  if (!model || !cell) {
1030
951
  return;
1031
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
+ }
1032
995
  switch (event.key) {
1033
996
  case "Backspace":
1034
997
  case "Delete": {
1035
998
  try {
1036
999
  model.setCellData(cell, void 0);
1037
1000
  event.preventDefault();
1001
+ handleSave();
1038
1002
  } catch {
1039
1003
  }
1040
1004
  break;
1041
1005
  }
1042
1006
  }
1043
1007
  }, [
1044
- model
1008
+ model,
1009
+ dxGrid,
1010
+ handleSave
1045
1011
  ]);
1046
- const handleAxisResize = useCallback5((event) => {
1012
+ const handleAxisResize = useCallback4((event) => {
1047
1013
  if (event.axis === "col") {
1048
1014
  const columnIndex = parseInt(event.index, 10);
1049
1015
  model?.setColumnWidth(columnIndex, event.size);
@@ -1051,7 +1017,7 @@ var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttenti
1051
1017
  }, [
1052
1018
  model
1053
1019
  ]);
1054
- const handleWheel = useCallback5((event) => {
1020
+ const handleWheel = useCallback4((event) => {
1055
1021
  if (!ignoreAttention && !hasAttention) {
1056
1022
  event.stopPropagation();
1057
1023
  }
@@ -1059,7 +1025,7 @@ var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttenti
1059
1025
  hasAttention,
1060
1026
  ignoreAttention
1061
1027
  ]);
1062
- const handleNewColumn = useCallback5(() => {
1028
+ const handleNewColumn = useCallback4(() => {
1063
1029
  const columns = model?.getColumnCount();
1064
1030
  if (dxGrid && columns) {
1065
1031
  dxGrid.scrollToColumn(columns - 1);
@@ -1068,79 +1034,28 @@ var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttenti
1068
1034
  model,
1069
1035
  dxGrid
1070
1036
  ]);
1071
- const handleQuery = useCallback5(async ({ field, props }, text) => {
1072
- if (model && props.referenceSchema && field.referencePath) {
1073
- const space = getSpace2(model.view);
1074
- invariant3(space, void 0, {
1075
- F: __dxlog_file3,
1076
- L: 330,
1077
- S: void 0,
1078
- A: [
1079
- "space",
1080
- ""
1081
- ]
1082
- });
1083
- let schema2;
1084
- if (client) {
1085
- schema2 = client.graph.schemaRegistry.getSchema(props.referenceSchema);
1086
- }
1087
- if (!schema2) {
1088
- schema2 = space.db.schemaRegistry.getSchema(props.referenceSchema);
1089
- }
1090
- if (schema2) {
1091
- const { objects } = await space.db.query(Filter.type(schema2)).run();
1092
- const options = objects.map((obj) => {
1093
- const value = getValue(obj, field.referencePath);
1094
- if (!value || typeof value !== "string") {
1095
- return void 0;
1096
- }
1097
- return {
1098
- label: value,
1099
- data: obj
1100
- };
1101
- }).filter(isNotFalsy);
1102
- return [
1103
- ...options,
1104
- {
1105
- label: t("create new object label", {
1106
- text
1107
- }),
1108
- data: createOption(text)
1109
- }
1110
- ];
1111
- }
1112
- }
1113
- return [];
1114
- }, [
1115
- model,
1116
- client,
1117
- t
1118
- ]);
1119
- const handleSave = useCallback5(() => {
1120
- dxGrid?.updateCells(true);
1121
- }, [
1122
- dxGrid
1123
- ]);
1124
1037
  if (!model || !modals) {
1125
- return /* @__PURE__ */ React8.createElement("span", {
1038
+ return /* @__PURE__ */ React7.createElement("span", {
1126
1039
  role: "none",
1127
1040
  className: "attention-surface"
1128
1041
  });
1129
1042
  }
1130
- return /* @__PURE__ */ React8.createElement(Grid.Root, {
1043
+ return /* @__PURE__ */ React7.createElement(Grid.Root, {
1131
1044
  id: model.id ?? "table-grid"
1132
- }, /* @__PURE__ */ React8.createElement(TableValueEditor, {
1045
+ }, /* @__PURE__ */ React7.createElement(TableValueEditor, {
1133
1046
  model,
1134
1047
  modals,
1135
1048
  schema,
1136
- onEnter: handleEnter,
1137
1049
  onFocus: handleFocus,
1138
- onQuery: handleQuery,
1139
- onSave: handleSave
1140
- }), /* @__PURE__ */ React8.createElement(Grid.Content, {
1050
+ onSave: handleSave,
1051
+ onCreate,
1052
+ client
1053
+ }), /* @__PURE__ */ React7.createElement(Grid.Content, {
1141
1054
  className: mx("[--dx-grid-base:var(--baseSurface)]", gridSeparatorInlineEnd, gridSeparatorBlockEnd),
1142
1055
  frozen,
1143
1056
  columns: model.columnMeta.value,
1057
+ columnDefault,
1058
+ rowDefault,
1144
1059
  limitRows: model.getRowCount() ?? 0,
1145
1060
  limitColumns: model.projection.fields.length,
1146
1061
  overscroll: "trap",
@@ -1148,20 +1063,20 @@ var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttenti
1148
1063
  onClick: handleGridClick,
1149
1064
  onKeyDownCapture: handleKeyDown,
1150
1065
  onWheelCapture: handleWheel,
1066
+ ...testId && {
1067
+ "data-testid": testId
1068
+ },
1151
1069
  ref: setDxGrid
1152
- }), /* @__PURE__ */ React8.createElement(RowActionsMenu, {
1070
+ }), /* @__PURE__ */ React7.createElement(RowActionsMenu, {
1153
1071
  model,
1154
1072
  modals
1155
- }), /* @__PURE__ */ React8.createElement(ColumnActionsMenu, {
1073
+ }), /* @__PURE__ */ React7.createElement(ColumnActionsMenu, {
1156
1074
  model,
1157
1075
  modals
1158
- }), /* @__PURE__ */ React8.createElement(ColumnSettings, {
1076
+ }), /* @__PURE__ */ React7.createElement(ColumnSettings, {
1159
1077
  model,
1160
1078
  modals,
1161
1079
  onNewColumn: handleNewColumn
1162
- }), /* @__PURE__ */ React8.createElement(CreateRefPanel, {
1163
- model,
1164
- modals
1165
1080
  }));
1166
1081
  } finally {
1167
1082
  _effect.f();
@@ -1173,14 +1088,14 @@ var Table = {
1173
1088
  };
1174
1089
 
1175
1090
  // src/components/Table/DynamicTable.tsx
1176
- import { useSignals as _useSignals9 } from "@preact-signals/safe-react/tracking";
1177
- import React9, { useRef as useRef3, useMemo as useMemo6, useCallback as useCallback6 } 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";
1178
1093
  import { useDefaultValue } from "@dxos/react-ui";
1179
1094
  import { mx as mx2 } from "@dxos/react-ui-theme";
1180
1095
  var DynamicTable = ({ classNames, name = "example.com/dynamic-table", rows, properties, jsonSchema: _jsonSchema, schema, rowActions, onRowClick, onRowAction, ...props }) => {
1181
- var _effect = _useSignals9();
1096
+ var _effect = _useSignals8();
1182
1097
  try {
1183
- const { typename, jsonSchema } = useMemo6(() => getBaseSchema({
1098
+ const { jsonSchema } = useMemo5(() => getBaseSchema({
1184
1099
  typename: name,
1185
1100
  properties,
1186
1101
  jsonSchema: _jsonSchema,
@@ -1191,20 +1106,18 @@ var DynamicTable = ({ classNames, name = "example.com/dynamic-table", rows, prop
1191
1106
  _jsonSchema,
1192
1107
  schema
1193
1108
  ]);
1194
- const { projection, view } = useMemo6(() => makeDynamicTable({
1195
- typename,
1109
+ const { projection, view } = useMemo5(() => makeDynamicTable({
1196
1110
  jsonSchema,
1197
1111
  properties
1198
1112
  }), [
1199
- typename,
1200
1113
  jsonSchema,
1201
1114
  properties
1202
1115
  ]);
1203
1116
  const tableRef = useRef3(null);
1204
- const handleCellUpdate = useCallback6((cell) => {
1117
+ const handleCellUpdate = useCallback5((cell) => {
1205
1118
  tableRef.current?.update?.(cell);
1206
1119
  }, []);
1207
- const handleRowOrderChange = useCallback6(() => {
1120
+ const handleRowOrderChange = useCallback5(() => {
1208
1121
  tableRef.current?.update?.();
1209
1122
  }, []);
1210
1123
  const features = useDefaultValue(props.features, () => ({
@@ -1224,20 +1137,20 @@ var DynamicTable = ({ classNames, name = "example.com/dynamic-table", rows, prop
1224
1137
  onRowOrderChange: handleRowOrderChange,
1225
1138
  onRowAction
1226
1139
  });
1227
- const presentation = useMemo6(() => {
1140
+ const presentation = useMemo5(() => {
1228
1141
  if (model) {
1229
1142
  return new TablePresentation(model);
1230
1143
  }
1231
1144
  }, [
1232
1145
  model
1233
1146
  ]);
1234
- return /* @__PURE__ */ React9.createElement("div", {
1147
+ return /* @__PURE__ */ React8.createElement("div", {
1235
1148
  role: "none",
1236
1149
  className: mx2("is-full bs-full grow grid", classNames)
1237
- }, /* @__PURE__ */ React9.createElement("div", {
1150
+ }, /* @__PURE__ */ React8.createElement("div", {
1238
1151
  role: "none",
1239
1152
  className: "grid min-bs-0 overflow-hidden"
1240
- }, /* @__PURE__ */ React9.createElement(Table.Root, null, /* @__PURE__ */ React9.createElement(Table.Main, {
1153
+ }, /* @__PURE__ */ React8.createElement(Table.Root, null, /* @__PURE__ */ React8.createElement(Table.Main, {
1241
1154
  ref: tableRef,
1242
1155
  model,
1243
1156
  presentation,
@@ -1250,43 +1163,46 @@ var DynamicTable = ({ classNames, name = "example.com/dynamic-table", rows, prop
1250
1163
  };
1251
1164
 
1252
1165
  // src/components/TableToolbar/TableToolbar.tsx
1253
- import { useSignals as _useSignals10 } from "@preact-signals/safe-react/tracking";
1254
- import { Rx } from "@effect-rx/rx-react";
1255
- import React10, { useMemo as useMemo7 } from "react";
1256
- import { live as live2 } from "@dxos/live-object";
1257
- import { createGapSeparator, createMenuAction, MenuProvider, ToolbarMenu, useMenuActions } from "@dxos/react-ui-menu";
1258
- import { rxFromSignal } from "@dxos/react-ui-menu";
1259
- var createTableToolbarActions = ({ state, onAdd, onSave, customActions }) => Rx.make((get) => {
1260
- const add = createMenuAction("add-row", onAdd, {
1261
- type: "add-row",
1262
- icon: "ph--plus--regular",
1263
- label: [
1264
- "add row",
1265
- {
1266
- ns: translationKey
1267
- }
1268
- ],
1269
- testId: "table.toolbar.add-row"
1270
- });
1271
- const save = createMenuAction("save-view", onSave, {
1272
- type: "save-view",
1273
- icon: "ph--floppy-disk--regular",
1274
- label: [
1275
- "save view label",
1276
- {
1277
- ns: translationKey
1278
- }
1279
- ],
1280
- testId: "table.toolbar.save-view",
1281
- iconOnly: false,
1282
- hidden: get(rxFromSignal(() => !state.viewDirty))
1283
- });
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";
1170
+ import { MenuProvider, ToolbarMenu, createGapSeparator, createMenuAction, useMenuActions } from "@dxos/react-ui-menu";
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
+ }
1284
1204
  const gap = createGapSeparator();
1285
- const nodes = [
1286
- add,
1287
- save,
1288
- ...gap.nodes
1289
- ];
1205
+ nodes.push(...gap.nodes);
1290
1206
  const edges = nodes.map(({ id: target }) => ({
1291
1207
  source: "root",
1292
1208
  target
@@ -1302,12 +1218,12 @@ var createTableToolbarActions = ({ state, onAdd, onSave, customActions }) => Rx.
1302
1218
  };
1303
1219
  });
1304
1220
  var TableToolbar = ({ classNames, viewDirty, attendableId, onAdd, onSave, customActions }) => {
1305
- var _effect = _useSignals10();
1221
+ var _effect = _useSignals9();
1306
1222
  try {
1307
- const state = useMemo7(() => live2({
1223
+ const state = useMemo6(() => live({
1308
1224
  viewDirty
1309
1225
  }), []);
1310
- const actionsCreator = useMemo7(() => createTableToolbarActions({
1226
+ const actionsCreator = useMemo6(() => createTableToolbarActions({
1311
1227
  state,
1312
1228
  onAdd,
1313
1229
  onSave,
@@ -1319,10 +1235,10 @@ var TableToolbar = ({ classNames, viewDirty, attendableId, onAdd, onSave, custom
1319
1235
  customActions
1320
1236
  ]);
1321
1237
  const menu = useMenuActions(actionsCreator);
1322
- return /* @__PURE__ */ React10.createElement(MenuProvider, {
1238
+ return /* @__PURE__ */ React9.createElement(MenuProvider, {
1323
1239
  ...menu,
1324
1240
  attendableId
1325
- }, /* @__PURE__ */ React10.createElement(ToolbarMenu, {
1241
+ }, /* @__PURE__ */ React9.createElement(ToolbarMenu, {
1326
1242
  classNames
1327
1243
  }));
1328
1244
  } finally {
@@ -1341,7 +1257,6 @@ export {
1341
1257
  TablePresentation,
1342
1258
  TableToolbar,
1343
1259
  cellClassesForRowSelection,
1344
- createTable,
1345
1260
  getBaseSchema,
1346
1261
  makeDynamicTable,
1347
1262
  narrowSchema,