@dxos/react-ui-table 0.8.4-main.2e9d522 → 0.8.4-main.3c1ae3b

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