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