@dxos/react-ui-table 0.8.4-main.f9ba587 → 0.8.4-main.fcc0d83b33

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 (185) hide show
  1. package/dist/lib/browser/chunk-FBURZBPH.mjs +70 -0
  2. package/dist/lib/browser/chunk-FBURZBPH.mjs.map +7 -0
  3. package/dist/lib/browser/chunk-J5LGTIGS.mjs +10 -0
  4. package/dist/lib/browser/chunk-J5LGTIGS.mjs.map +7 -0
  5. package/dist/lib/browser/chunk-YGC7LZJN.mjs +1695 -0
  6. package/dist/lib/browser/chunk-YGC7LZJN.mjs.map +7 -0
  7. package/dist/lib/browser/index.mjs +1158 -2872
  8. package/dist/lib/browser/index.mjs.map +4 -4
  9. package/dist/lib/browser/meta.json +1 -1
  10. package/dist/lib/browser/testing/index.mjs +115 -0
  11. package/dist/lib/browser/testing/index.mjs.map +7 -0
  12. package/dist/lib/browser/translations.mjs +36 -0
  13. package/dist/lib/browser/translations.mjs.map +7 -0
  14. package/dist/lib/browser/types/index.mjs +4 -5
  15. package/dist/lib/node-esm/chunk-DKKMSVKP.mjs +71 -0
  16. package/dist/lib/node-esm/chunk-DKKMSVKP.mjs.map +7 -0
  17. package/dist/lib/node-esm/chunk-GKIWDEZ7.mjs +1697 -0
  18. package/dist/lib/node-esm/chunk-GKIWDEZ7.mjs.map +7 -0
  19. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +11 -0
  20. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs.map +7 -0
  21. package/dist/lib/node-esm/index.mjs +1158 -2872
  22. package/dist/lib/node-esm/index.mjs.map +4 -4
  23. package/dist/lib/node-esm/meta.json +1 -1
  24. package/dist/lib/node-esm/testing/index.mjs +116 -0
  25. package/dist/lib/node-esm/testing/index.mjs.map +7 -0
  26. package/dist/lib/node-esm/translations.mjs +37 -0
  27. package/dist/lib/node-esm/translations.mjs.map +7 -0
  28. package/dist/lib/node-esm/types/index.mjs +4 -5
  29. package/dist/types/src/components/Table/ColumnActionsMenu.d.ts +2 -3
  30. package/dist/types/src/components/Table/ColumnActionsMenu.d.ts.map +1 -1
  31. package/dist/types/src/components/Table/ColumnSettings.d.ts +4 -2
  32. package/dist/types/src/components/Table/ColumnSettings.d.ts.map +1 -1
  33. package/dist/types/src/components/Table/DynamicTable.d.ts +7 -7
  34. package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
  35. package/dist/types/src/components/Table/DynamicTable.stories.d.ts +35 -3
  36. package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
  37. package/dist/types/src/components/Table/Relations.stories.d.ts +111 -0
  38. package/dist/types/src/components/Table/Relations.stories.d.ts.map +1 -0
  39. package/dist/types/src/components/Table/RowActionsMenu.d.ts +1 -1
  40. package/dist/types/src/components/Table/RowActionsMenu.d.ts.map +1 -1
  41. package/dist/types/src/components/Table/Table.d.ts +21 -18
  42. package/dist/types/src/components/Table/Table.d.ts.map +1 -1
  43. package/dist/types/src/components/Table/Table.stories.d.ts +110 -6
  44. package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
  45. package/dist/types/src/components/Table/TableContent.d.ts +17 -0
  46. package/dist/types/src/components/Table/TableContent.d.ts.map +1 -0
  47. package/dist/types/src/components/Table/TableToolbar.d.ts +21 -0
  48. package/dist/types/src/components/Table/TableToolbar.d.ts.map +1 -0
  49. package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts +1 -1
  50. package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts.map +1 -1
  51. package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +14 -9
  52. package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
  53. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +12 -18
  54. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
  55. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts +38 -5
  56. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
  57. package/dist/types/src/components/TableCellEditor/index.d.ts +1 -0
  58. package/dist/types/src/components/TableCellEditor/index.d.ts.map +1 -1
  59. package/dist/types/src/components/index.d.ts +0 -1
  60. package/dist/types/src/components/index.d.ts.map +1 -1
  61. package/dist/types/src/components/testing.d.ts +26 -56
  62. package/dist/types/src/components/testing.d.ts.map +1 -1
  63. package/dist/types/src/hooks/index.d.ts +2 -1
  64. package/dist/types/src/hooks/index.d.ts.map +1 -1
  65. package/dist/types/src/hooks/useAddRow.d.ts +6 -6
  66. package/dist/types/src/hooks/useAddRow.d.ts.map +1 -1
  67. package/dist/types/src/hooks/useProjectionModel.d.ts +6 -0
  68. package/dist/types/src/hooks/useProjectionModel.d.ts.map +1 -0
  69. package/dist/types/src/hooks/useTableModel.d.ts +11 -10
  70. package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
  71. package/dist/types/src/index.d.ts +0 -3
  72. package/dist/types/src/index.d.ts.map +1 -1
  73. package/dist/types/src/model/modal-controller.d.ts +5 -8
  74. package/dist/types/src/model/modal-controller.d.ts.map +1 -1
  75. package/dist/types/src/model/selection-model.d.ts +18 -8
  76. package/dist/types/src/model/selection-model.d.ts.map +1 -1
  77. package/dist/types/src/model/table-model.d.ts +116 -34
  78. package/dist/types/src/model/table-model.d.ts.map +1 -1
  79. package/dist/types/src/model/table-presentation.d.ts +5 -2
  80. package/dist/types/src/model/table-presentation.d.ts.map +1 -1
  81. package/dist/types/src/playwright/TableManager.d.ts +2 -1
  82. package/dist/types/src/playwright/TableManager.d.ts.map +1 -1
  83. package/dist/types/src/playwright/playwright.config.d.ts.map +1 -1
  84. package/dist/types/src/testing/index.d.ts +2 -0
  85. package/dist/types/src/testing/index.d.ts.map +1 -0
  86. package/dist/types/src/testing/useTestTableModel.d.ts +25 -0
  87. package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -0
  88. package/dist/types/src/translations.d.ts +21 -23
  89. package/dist/types/src/translations.d.ts.map +1 -1
  90. package/dist/types/src/types/Table.d.ts +26 -0
  91. package/dist/types/src/types/Table.d.ts.map +1 -0
  92. package/dist/types/src/types/index.d.ts +1 -1
  93. package/dist/types/src/types/index.d.ts.map +1 -1
  94. package/dist/types/src/util/dynamic-table.d.ts +15 -14
  95. package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
  96. package/dist/types/src/util/dynamic-table.test.d.ts +2 -0
  97. package/dist/types/src/util/dynamic-table.test.d.ts.map +1 -0
  98. package/dist/types/src/util/index.d.ts +2 -1
  99. package/dist/types/src/util/index.d.ts.map +1 -1
  100. package/dist/types/src/util/query-ast.d.ts +7 -0
  101. package/dist/types/src/util/query-ast.d.ts.map +1 -0
  102. package/dist/types/src/util/schema.d.ts +1 -1
  103. package/dist/types/src/util/schema.d.ts.map +1 -1
  104. package/dist/types/src/util/signals.d.ts.map +1 -1
  105. package/dist/types/src/util/sort.d.ts +12 -0
  106. package/dist/types/src/util/sort.d.ts.map +1 -0
  107. package/dist/types/src/util/table-buttons.d.ts +20 -20
  108. package/dist/types/src/util/table-buttons.d.ts.map +1 -1
  109. package/dist/types/src/util/table-controls.d.ts +4 -4
  110. package/dist/types/src/util/table-controls.d.ts.map +1 -1
  111. package/dist/types/src/util/tag.d.ts.map +1 -1
  112. package/dist/types/tsconfig.tsbuildinfo +1 -1
  113. package/package.json +80 -69
  114. package/src/components/Table/ColumnActionsMenu.tsx +18 -13
  115. package/src/components/Table/ColumnSettings.tsx +18 -15
  116. package/src/components/Table/DynamicTable.stories.tsx +40 -36
  117. package/src/components/Table/DynamicTable.tsx +38 -34
  118. package/src/components/Table/Relations.stories.tsx +281 -0
  119. package/src/components/Table/RowActionsMenu.tsx +28 -18
  120. package/src/components/Table/Table.stories.tsx +198 -220
  121. package/src/components/Table/Table.tsx +47 -376
  122. package/src/components/Table/TableContent.tsx +421 -0
  123. package/src/components/Table/TableToolbar.tsx +111 -0
  124. package/src/components/TableCellEditor/CellValidationMessage.tsx +6 -6
  125. package/src/components/TableCellEditor/FormCellEditor.tsx +153 -49
  126. package/src/components/TableCellEditor/TableCellEditor.stories.tsx +38 -77
  127. package/src/components/TableCellEditor/TableCellEditor.tsx +104 -233
  128. package/src/components/TableCellEditor/index.ts +1 -0
  129. package/src/components/index.ts +0 -1
  130. package/src/components/testing.ts +46 -37
  131. package/src/hooks/index.ts +2 -1
  132. package/src/hooks/useAddRow.ts +14 -14
  133. package/src/hooks/useProjectionModel.ts +39 -0
  134. package/src/hooks/useTableModel.ts +41 -35
  135. package/src/index.ts +0 -5
  136. package/src/model/modal-controller.ts +24 -41
  137. package/src/model/selection-model.ts +75 -41
  138. package/src/model/table-model.test.ts +91 -56
  139. package/src/model/table-model.ts +468 -187
  140. package/src/model/table-presentation.test.ts +77 -60
  141. package/src/model/table-presentation.ts +112 -66
  142. package/src/playwright/TableManager.ts +13 -6
  143. package/src/playwright/playwright.config.ts +1 -1
  144. package/src/playwright/smoke.spec.ts +180 -43
  145. package/src/testing/index.ts +5 -0
  146. package/src/testing/useTestTableModel.ts +133 -0
  147. package/src/translations.ts +20 -22
  148. package/src/types/Table.ts +79 -0
  149. package/src/types/index.ts +2 -2
  150. package/src/util/dynamic-table.test.ts +77 -0
  151. package/src/util/dynamic-table.ts +51 -36
  152. package/src/util/index.ts +2 -1
  153. package/src/util/query-ast.ts +22 -0
  154. package/src/util/schema.ts +4 -4
  155. package/src/util/sort.ts +90 -0
  156. package/src/util/table-buttons.ts +4 -3
  157. package/src/util/table-controls.ts +6 -3
  158. package/dist/lib/browser/chunk-Q5QQXJUO.mjs +0 -27
  159. package/dist/lib/browser/chunk-Q5QQXJUO.mjs.map +0 -7
  160. package/dist/lib/node-esm/chunk-ZNOU6LHG.mjs +0 -29
  161. package/dist/lib/node-esm/chunk-ZNOU6LHG.mjs.map +0 -7
  162. package/dist/types/src/components/Table/CreateRefPanel.d.ts +0 -9
  163. package/dist/types/src/components/Table/CreateRefPanel.d.ts.map +0 -1
  164. package/dist/types/src/components/Table/relations.stories.d.ts +0 -9
  165. package/dist/types/src/components/Table/relations.stories.d.ts.map +0 -1
  166. package/dist/types/src/components/TableCellEditor/extension.d.ts +0 -13
  167. package/dist/types/src/components/TableCellEditor/extension.d.ts.map +0 -1
  168. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts +0 -21
  169. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +0 -1
  170. package/dist/types/src/components/TableToolbar/index.d.ts +0 -2
  171. package/dist/types/src/components/TableToolbar/index.d.ts.map +0 -1
  172. package/dist/types/src/model/table-sorting.d.ts +0 -53
  173. package/dist/types/src/model/table-sorting.d.ts.map +0 -1
  174. package/dist/types/src/types/table.d.ts +0 -56
  175. package/dist/types/src/types/table.d.ts.map +0 -1
  176. package/dist/types/src/util/table.d.ts +0 -14
  177. package/dist/types/src/util/table.d.ts.map +0 -1
  178. package/src/components/Table/CreateRefPanel.tsx +0 -84
  179. package/src/components/Table/relations.stories.tsx +0 -123
  180. package/src/components/TableCellEditor/extension.ts +0 -61
  181. package/src/components/TableToolbar/TableToolbar.tsx +0 -102
  182. package/src/components/TableToolbar/index.ts +0 -5
  183. package/src/model/table-sorting.ts +0 -216
  184. package/src/types/table.ts +0 -27
  185. package/src/util/table.ts +0 -127
@@ -1,312 +1,56 @@
1
1
  import {
2
- TableSchema,
3
- TableType
4
- } from "./chunk-Q5QQXJUO.mjs";
5
-
6
- // src/translations.ts
7
- var translationKey = "react-ui-table";
8
- var translations = [
9
- {
10
- "en-US": {
11
- [translationKey]: {
12
- "object placeholder": "New table",
13
- "create object label": "Create table",
14
- "table name placeholder": "Table name",
15
- "settings title": "Table settings",
16
- "table schema label": "Schema",
17
- "new schema": "New schema",
18
- "continue label": "Continue",
19
- "create stack section label": "Create table",
20
- "add row label": "Add row",
21
- "create comment": "Create thread",
22
- "column action sort descending": "Sort Descending",
23
- "column action sort ascending": "Sort Ascending",
24
- "column action clear sorting": "Clear Sorting",
25
- "column action settings": "Column Settings",
26
- "column action delete": "Delete Column",
27
- "delete row label": "Delete row",
28
- "bulk delete row label": "Delete selected rows",
29
- "column deleted label": "Column deleted",
30
- "new column button label": "Create column",
31
- "create new object label": "Create new object \u201C{{text}}\u201D",
32
- "save view label": "Save changes to view"
33
- }
34
- }
35
- }
36
- ];
2
+ BUTTON_IDENTIFIERS,
3
+ CONTROL_IDENTIFIERS,
4
+ CheckboxStory,
5
+ SwitchStory,
6
+ TableModel,
7
+ TablePresentation,
8
+ cellClassesForRowSelection,
9
+ compareValues,
10
+ createDirectChangeCallback,
11
+ createEchoChangeCallback,
12
+ extractOrder,
13
+ getSortValue,
14
+ tableButtons,
15
+ tableControls,
16
+ useAddRow,
17
+ useProjectionModel,
18
+ useTableModel
19
+ } from "./chunk-YGC7LZJN.mjs";
20
+ import {
21
+ Table_exports
22
+ } from "./chunk-FBURZBPH.mjs";
23
+ import "./chunk-J5LGTIGS.mjs";
37
24
 
38
25
  // src/components/Table/Table.tsx
39
- import { useSignals as _useSignals8 } from "@preact-signals/safe-react/tracking";
40
- import React8, { forwardRef, useCallback as useCallback5, useImperativeHandle, useState as useState3, useMemo as useMemo5, useEffect as useEffect4 } from "react";
41
- import { getValue as getValue4 } from "@dxos/echo-schema";
42
- import { invariant as invariant4 } from "@dxos/invariant";
43
- import { Filter } from "@dxos/react-client/echo";
44
- import { useTranslation as useTranslation3 } from "@dxos/react-ui";
45
- import { useAttention } from "@dxos/react-ui-attention";
46
- import { closestCell, Grid, gridSeparatorInlineEnd, gridSeparatorBlockEnd } from "@dxos/react-ui-grid";
47
- import { mx as mx2 } from "@dxos/react-ui-theme";
48
- import { isNotFalsy, safeParseInt } from "@dxos/util";
49
-
50
- // src/components/Table/ColumnActionsMenu.tsx
51
- import { useSignals as _useSignals } from "@preact-signals/safe-react/tracking";
52
- import React from "react";
53
- import { DropdownMenu, useTranslation } from "@dxos/react-ui";
54
- var ColumnActionsMenu = ({ model, modals }) => {
55
- var _effect = _useSignals();
56
- try {
57
- const { t } = useTranslation(translationKey);
58
- const state = modals.state.value;
59
- if (state?.type !== "column") {
60
- return null;
61
- }
62
- const currentSort = model.sorting?.sorting;
63
- const isCurrentColumnSorted = currentSort?.fieldId === state.fieldId;
64
- return /* @__PURE__ */ React.createElement(DropdownMenu.Root, {
65
- modal: false,
66
- open: true,
67
- onOpenChange: modals.close
68
- }, /* @__PURE__ */ React.createElement(DropdownMenu.VirtualTrigger, {
69
- virtualRef: modals.trigger
70
- }), /* @__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, {
71
- "data-testid": "column-sort-descending",
72
- onClick: () => model.sorting?.setSort(state.fieldId, "desc")
73
- }, t("column action sort descending")), (!isCurrentColumnSorted || currentSort?.direction === "desc") && /* @__PURE__ */ React.createElement(DropdownMenu.Item, {
74
- "data-testid": "column-sort-ascending",
75
- onClick: () => model.sorting?.setSort(state.fieldId, "asc")
76
- }, t("column action sort ascending")), isCurrentColumnSorted && /* @__PURE__ */ React.createElement(DropdownMenu.Item, {
77
- "data-testid": "column-clear-sort",
78
- onClick: () => model.sorting?.clearSort()
79
- }, t("column action clear sorting")), model.getColumnCount() > 1 && model.features.schemaEditable && /* @__PURE__ */ React.createElement(DropdownMenu.Item, {
80
- "data-testid": "column-delete",
81
- onClick: () => model.deleteColumn(state.fieldId)
82
- }, t("column action delete")), model.features.schemaEditable && /* @__PURE__ */ React.createElement(DropdownMenu.Item, {
83
- "data-testid": "column-settings",
84
- onClick: () => modals.openColumnSettings()
85
- }, t("column action settings"))), /* @__PURE__ */ React.createElement(DropdownMenu.Arrow, null))));
86
- } finally {
87
- _effect.f();
88
- }
89
- };
90
-
91
- // src/components/Table/ColumnSettings.tsx
92
- import { useSignals as _useSignals2 } from "@preact-signals/safe-react/tracking";
93
- import React2, { useCallback, useEffect, useMemo, useState } from "react";
94
- import { Popover } from "@dxos/react-ui";
95
- import { FieldEditor } from "@dxos/react-ui-form";
96
- var ColumnSettings = ({ model, modals, onNewColumn }) => {
97
- var _effect = _useSignals2();
98
- try {
99
- const [newField, setNewField] = useState();
100
- const state = modals.state.value;
101
- const space = model?.space;
102
- useEffect(() => {
103
- if (state?.type === "columnSettings" && state.mode.type === "create" && model?.projection) {
104
- setNewField(model.projection.createFieldProjection());
105
- requestAnimationFrame(() => {
106
- onNewColumn();
107
- });
108
- } else {
109
- setNewField(void 0);
110
- }
111
- }, [
112
- model?.projection,
113
- state
114
- ]);
115
- const existingField = useMemo(() => {
116
- if (state?.type === "columnSettings") {
117
- const { mode } = state;
118
- if (mode.type === "edit") {
119
- return model?.view?.fields.find((f) => f.id === mode.fieldId);
120
- }
121
- }
122
- return void 0;
123
- }, [
124
- model?.view?.fields,
125
- state
126
- ]);
127
- const field = existingField ?? newField;
128
- const handleSave = useCallback(() => {
129
- modals.close();
130
- }, [
131
- modals
132
- ]);
133
- const handleCancel = useCallback(() => {
134
- if (state?.type === "columnSettings" && state.mode.type === "create" && newField) {
135
- model?.projection?.deleteFieldProjection(newField.id);
136
- }
137
- }, [
138
- model?.projection,
139
- state,
140
- newField
141
- ]);
142
- if (!model?.view || !model.projection || !field) {
143
- return null;
144
- }
145
- return /* @__PURE__ */ React2.createElement(Popover.Root, {
146
- modal: false,
147
- open: state?.type === "columnSettings"
148
- }, /* @__PURE__ */ React2.createElement(Popover.VirtualTrigger, {
149
- virtualRef: modals.trigger
150
- }), /* @__PURE__ */ React2.createElement(Popover.Portal, null, /* @__PURE__ */ React2.createElement(Popover.Content, {
151
- classNames: "md:is-64"
152
- }, /* @__PURE__ */ React2.createElement(Popover.Viewport, null, /* @__PURE__ */ React2.createElement(FieldEditor, {
153
- view: model.view,
154
- projection: model.projection,
155
- field,
156
- registry: space?.db.schemaRegistry,
157
- onSave: handleSave,
158
- onCancel: handleCancel
159
- })), /* @__PURE__ */ React2.createElement(Popover.Arrow, null))));
160
- } finally {
161
- _effect.f();
162
- }
163
- };
164
-
165
- // src/components/Table/CreateRefPanel.tsx
166
- import { useSignals as _useSignals3 } from "@preact-signals/safe-react/tracking";
167
- import { Schema } from "effect";
168
- import React3, { useCallback as useCallback2, useMemo as useMemo2 } from "react";
169
- import { live } from "@dxos/react-client/echo";
170
- import { Popover as Popover2 } from "@dxos/react-ui";
171
- import { Form } from "@dxos/react-ui-form";
172
- import { useGridContext } from "@dxos/react-ui-grid";
173
- var CreateRefPanel = ({ model, modals, __gridScope }) => {
174
- var _effect = _useSignals3();
175
- try {
176
- const { id: gridId } = useGridContext("TableCellEditor", __gridScope);
177
- const space = model?.space;
178
- const state = modals.state.value;
179
- const schema = useMemo2(() => {
180
- if (!space || state?.type !== "createRefPanel") {
181
- return;
182
- }
183
- const [schema2] = space.db.schemaRegistry.query({
184
- typename: state.typename
185
- }).runSync();
186
- if (schema2) {
187
- const omit = Schema.omit("id");
188
- return omit(schema2);
189
- }
190
- }, [
191
- space,
192
- state
193
- ]);
194
- const handleSave = useCallback2((values) => {
195
- if (!model || !space || state?.type !== "createRefPanel") {
196
- return;
197
- }
198
- const [schema2] = space.db.schemaRegistry.query({
199
- typename: state.typename
200
- }).runSync();
201
- if (schema2) {
202
- const obj = space.db.add(live(schema2, values));
203
- state.onCreate?.(obj);
204
- }
205
- void modals.close();
206
- }, [
207
- modals,
208
- space,
209
- state
210
- ]);
211
- const handleCancel = useCallback2(() => {
212
- if (model) {
213
- modals.close();
214
- }
215
- }, [
216
- modals
217
- ]);
218
- if (!model?.view || !model.projection) {
219
- return null;
220
- }
221
- return /* @__PURE__ */ React3.createElement(Popover2.Root, {
222
- modal: false,
223
- open: state?.type === "createRefPanel" && !!schema,
224
- onOpenChange: (nextOpen) => {
225
- if (model && !nextOpen) {
226
- return modals.close();
227
- }
228
- }
229
- }, /* @__PURE__ */ React3.createElement(Popover2.VirtualTrigger, {
230
- virtualRef: modals.trigger
231
- }), /* @__PURE__ */ React3.createElement(Popover2.Portal, null, /* @__PURE__ */ React3.createElement(Popover2.Content, {
232
- classNames: "md:is-64",
233
- "data-grid": gridId
234
- }, /* @__PURE__ */ React3.createElement(Popover2.Viewport, null, state?.type === "createRefPanel" && schema && /* @__PURE__ */ React3.createElement(Form, {
235
- schema,
236
- values: state.initialValues ?? {},
237
- onSave: handleSave,
238
- onCancel: handleCancel
239
- })), /* @__PURE__ */ React3.createElement(Popover2.Arrow, null))));
240
- } finally {
241
- _effect.f();
242
- }
243
- };
244
-
245
- // src/components/Table/RowActionsMenu.tsx
246
- import { useSignals as _useSignals4 } from "@preact-signals/safe-react/tracking";
247
- import React4 from "react";
248
- import { DropdownMenu as DropdownMenu2, useTranslation as useTranslation2 } from "@dxos/react-ui";
249
- var RowActionsMenu = ({ model, modals }) => {
250
- var _effect = _useSignals4();
251
- try {
252
- const { t } = useTranslation2(translationKey);
253
- const hasSelection = model.selection.hasSelection.value;
254
- const state = modals.state.value;
255
- if (state?.type !== "row") {
256
- return null;
257
- }
258
- return /* @__PURE__ */ React4.createElement(DropdownMenu2.Root, {
259
- modal: false,
260
- open: true,
261
- onOpenChange: modals.close
262
- }, /* @__PURE__ */ React4.createElement(DropdownMenu2.VirtualTrigger, {
263
- virtualRef: modals.trigger
264
- }), /* @__PURE__ */ React4.createElement(DropdownMenu2.Content, null, /* @__PURE__ */ React4.createElement(DropdownMenu2.Viewport, null, model.features.dataEditable !== false && /* @__PURE__ */ React4.createElement(DropdownMenu2.Item, {
265
- "data-testid": "row-menu-delete",
266
- onClick: () => model.deleteRow(state.rowIndex)
267
- }, t(hasSelection ? "bulk delete row label" : "delete row label")), model.rowActions?.map((action) => /* @__PURE__ */ React4.createElement(DropdownMenu2.Item, {
268
- key: action.id,
269
- "data-testid": `row-action-${action.id}`,
270
- onClick: () => {
271
- modals.close();
272
- model.handleRowAction(action.id, state.rowIndex);
273
- }
274
- }, t(action.translationKey)))), /* @__PURE__ */ React4.createElement(DropdownMenu2.Arrow, null)));
275
- } finally {
276
- _effect.f();
277
- }
278
- };
279
-
280
- // src/model/table-model.ts
281
- import { computed as computed3, effect as effect2, signal as signal3 } from "@preact/signals-core";
282
- import { Resource as Resource2 } from "@dxos/context";
283
- import { Ref as Ref3 } from "@dxos/echo";
284
- import { FormatEnum as FormatEnum3, getValue as getValue2, setValue, getSnapshot, getSchema, toEffectSchema } from "@dxos/echo-schema";
285
- import { invariant } from "@dxos/invariant";
286
- import { ObjectId } from "@dxos/keys";
287
- import { isLiveObject } from "@dxos/live-object";
288
- import { formatForEditing, parseValue } from "@dxos/react-ui-form";
289
- import { validateSchema } from "@dxos/schema";
26
+ import { Atom as Atom4 } from "@effect-atom/atom-react";
27
+ import { createContext } from "@radix-ui/react-context";
28
+ import React9, { forwardRef, useImperativeHandle, useRef as useRef3 } from "react";
290
29
 
291
- // src/model/selection-model.ts
292
- import { computed, effect, signal } from "@preact/signals-core";
293
- import { Resource } from "@dxos/context";
30
+ // src/components/Table/TableContent.tsx
31
+ import { Atom as Atom2, RegistryContext, useAtomValue as useAtomValue4 } from "@effect-atom/atom-react";
32
+ import * as String2 from "effect/String";
33
+ import React7, { useCallback as useCallback4, useContext, useEffect as useEffect3, useMemo as useMemo4, useState as useState4 } from "react";
34
+ import { log } from "@dxos/log";
35
+ import { useAttention } from "@dxos/react-ui-attention";
36
+ import { Grid, closestCell, gridSeparatorBlockEnd, gridSeparatorInlineEnd } from "@dxos/react-ui-grid";
37
+ import { DxEditRequest } from "@dxos/react-ui-grid";
38
+ import { composable, composableProps, mx } from "@dxos/ui-theme";
294
39
 
295
40
  // src/util/dynamic-table.ts
296
- import { Obj, Ref } from "@dxos/echo";
297
- import { getTypename, toJsonSchema } from "@dxos/echo-schema";
298
- import { createView, getSchemaFromPropertyDefinitions, ViewProjection } from "@dxos/schema";
299
- var getBaseSchema = ({ typename, properties, jsonSchema, schema }) => {
41
+ import { Filter, JsonSchema, Obj, Order, Query, Ref, Type } from "@dxos/echo";
42
+ import { ProjectionModel, ViewModel, createEchoChangeCallback as createEchoChangeCallback2, getSchemaFromPropertyDefinitions } from "@dxos/schema";
43
+ var getBaseSchema = ({ schema, typename, properties, jsonSchema }) => {
300
44
  if (typename && properties) {
301
45
  const schema2 = getSchemaFromPropertyDefinitions(typename, properties);
302
46
  return {
303
47
  typename: schema2.typename,
304
- jsonSchema: schema2.jsonSchema
48
+ jsonSchema: JsonSchema.toJsonSchema(schema2)
305
49
  };
306
50
  } else if (schema) {
307
51
  return {
308
- typename: getTypename(schema),
309
- jsonSchema: toJsonSchema(schema)
52
+ typename: Type.getTypename(schema),
53
+ jsonSchema: JsonSchema.toJsonSchema(schema)
310
54
  };
311
55
  } else if (typename && jsonSchema) {
312
56
  return {
@@ -317,34 +61,41 @@ var getBaseSchema = ({ typename, properties, jsonSchema, schema }) => {
317
61
  throw new Error("invalid properties");
318
62
  }
319
63
  };
320
- var makeDynamicTable = ({ typename, jsonSchema, properties }) => {
321
- const view = createView({
322
- name: "dynamic-table",
323
- typename,
64
+ var makeDynamicTable = ({ registry, jsonSchema, properties }) => {
65
+ const view = ViewModel.make({
66
+ query: Query.select(Filter.everything()),
324
67
  jsonSchema,
325
68
  ...properties && {
326
69
  fields: properties.map((property) => property.name)
327
70
  }
328
71
  });
329
- const table = Obj.make(TableType, {
330
- name: "dynamic-table",
331
- view: Ref.make(view)
72
+ const object = Obj.make(Table_exports.Table, {
73
+ view: Ref.make(view),
74
+ sizes: {}
75
+ });
76
+ const projection = new ProjectionModel({
77
+ registry,
78
+ view,
79
+ baseSchema: jsonSchema,
80
+ change: createEchoChangeCallback2(view, jsonSchema)
332
81
  });
333
- const projection = new ViewProjection(jsonSchema, view);
334
- if (properties && view.fields) {
335
- setProperties(view, projection, properties);
82
+ projection.normalizeView();
83
+ if (properties && projection.getFields()) {
84
+ setProperties(view, projection, object, properties);
336
85
  }
337
86
  return {
338
- table,
339
- projection
87
+ projection,
88
+ object
340
89
  };
341
90
  };
342
- var setProperties = (view, projection, properties) => {
91
+ var setProperties = (view, projection, table, properties) => {
343
92
  for (const property of properties) {
344
- const field = view.fields.find((field2) => field2.path === property.name);
93
+ const field = projection.getFields().find((field2) => field2.path === property.name);
345
94
  if (field) {
346
95
  if (property.size !== void 0) {
347
- field.size = property.size;
96
+ Obj.update(table, (table2) => {
97
+ table2.sizes[field.path] = property.size;
98
+ });
348
99
  }
349
100
  if (property.title !== void 0) {
350
101
  const fieldProjection = projection.getFieldProjection(field.id);
@@ -357,13 +108,11 @@ var setProperties = (view, projection, properties) => {
357
108
  });
358
109
  }
359
110
  if (property.sort) {
360
- const fieldId = field.id;
361
- view.query.sort = [
362
- {
363
- fieldId,
364
- direction: property.sort
365
- }
366
- ];
111
+ const currentQuery = Query.fromAst(Obj.getSnapshot(view).query.ast);
112
+ const newQuery = currentQuery.orderBy(Order.property(field.path, property.sort));
113
+ Obj.update(view, (view2) => {
114
+ view2.query.ast = newQuery.ast;
115
+ });
367
116
  }
368
117
  }
369
118
  }
@@ -372,1910 +121,379 @@ var setProperties = (view, projection, properties) => {
372
121
  // src/util/signals.ts
373
122
  var touch = (value) => value;
374
123
 
375
- // src/util/table.ts
376
- import { Schema as Schema2 } from "effect";
377
- import { Format, FormatEnum, Ref as Ref2, TypedObject, TypeEnum } from "@dxos/echo-schema";
378
- import { PublicKey } from "@dxos/react-client";
379
- import { live as live2 } from "@dxos/react-client/echo";
380
- import { createFieldId, createView as createView2, getSchemaProperties, ViewProjection as ViewProjection2 } from "@dxos/schema";
381
- var initializeTable = async ({ client, space, table, typename, initialRow = true }) => {
382
- if (typename) {
383
- const schema = await space.db.graph.getSchemaByTypename(typename, space.db);
384
- if (!schema) {
385
- throw new Error(`Schema not found: ${typename}`);
386
- }
387
- const fields = getSchemaProperties(schema.ast).map((prop) => prop.name);
388
- table.view = Ref2.make(createView2({
389
- // TODO(ZaymonFC): Don't hardcode name?
390
- name: "View",
391
- typename: schema.typename,
392
- jsonSchema: schema.jsonSchema,
393
- fields
394
- }));
395
- return schema;
396
- } else {
397
- const [schema] = await space.db.schemaRegistry.register([
398
- createContactSchema()
399
- ]);
400
- const fields = ContactFields;
401
- table.view = Ref2.make(createView2({
402
- name: "View",
403
- typename: schema.typename,
404
- jsonSchema: schema.jsonSchema,
405
- fields
406
- }));
407
- createProjection(schema, table.view.target);
408
- if (initialRow) {
409
- space.db.add(live2(schema, {}));
410
- }
411
- return schema;
412
- }
413
- };
414
- var createContactSchema = () => TypedObject({
415
- typename: `example.com/type/${PublicKey.random().truncate()}`,
416
- version: "0.1.0"
417
- })({
418
- name: Schema2.optional(Schema2.String).annotations({
419
- title: "Name"
420
- }),
421
- active: Schema2.optional(Schema2.Boolean).annotations({
422
- title: "Active"
423
- }),
424
- email: Schema2.optional(Format.Email),
425
- salary: Schema2.optional(Format.Currency()).annotations({
426
- title: "Salary"
427
- })
428
- });
429
- var ContactFields = [
430
- "name",
431
- "email",
432
- "salary",
433
- "active"
434
- ];
435
- var createProjection = (schema, view) => {
436
- const projection = new ViewProjection2(schema.jsonSchema, view);
437
- projection.setFieldProjection({
438
- field: {
439
- id: view.fields.find((f) => f.path === "salary").id,
440
- path: "salary",
441
- size: 150
442
- }
443
- });
444
- projection.setFieldProjection({
445
- field: {
446
- id: view.fields.find((f) => f.path === "active").id,
447
- path: "active",
448
- size: 100
449
- }
450
- });
451
- projection.setFieldProjection({
452
- field: {
453
- id: createFieldId(),
454
- path: "manager",
455
- referencePath: "name"
456
- },
457
- props: {
458
- property: "manager",
459
- type: TypeEnum.Ref,
460
- format: FormatEnum.Ref,
461
- referenceSchema: schema.typename,
462
- title: "Manager"
463
- }
464
- });
465
- return projection;
466
- };
467
-
468
- // src/util/table-buttons.ts
469
- var BUTTON_IDENTIFIERS = {
470
- columnSettings: "data-table-column-settings-button",
471
- newColumn: "data-table-new-column-button",
472
- referencedCell: "data-table-ref-cell-button",
473
- rowMenu: "data-table-row-menu-button",
474
- saveDraftRow: "data-table-save-draft-row-button",
475
- sort: "data-table-sort-button"
476
- };
477
- var createButton = ({ attr, data, disabled = false, icon, testId, type = "primary" }) => {
478
- const dataAttrs = Object.entries(data).map(([k, v]) => `${k}="${v}"`).join(" ");
479
- const positionClass = type === "primary" ? "inline-end-2" : "inline-end-9";
480
- return `<button ${attr} data-testid="${testId}" class="dx-button is-6 pli-0.5 min-bs-0 absolute inset-block-1 ${positionClass}" ${dataAttrs} ${disabled ? "disabled" : ""}><svg data-size="4"><use href="/icons.svg#${icon}"/></svg></button>`;
481
- };
482
- var addColumnButton = {
483
- attr: BUTTON_IDENTIFIERS.newColumn,
484
- icon: "ph--plus--regular",
485
- render: ({ disabled }) => {
486
- return createButton({
487
- attr: BUTTON_IDENTIFIERS.newColumn,
488
- icon: addColumnButton.icon,
489
- disabled,
490
- data: {},
491
- testId: "table-new-column-button"
492
- });
493
- },
494
- getData: (_el) => ({
495
- type: "newColumn"
496
- })
497
- };
498
- var columnSettingsButton = {
499
- attr: BUTTON_IDENTIFIERS.columnSettings,
500
- icon: "ph--caret-down--regular",
501
- render: ({ fieldId }) => {
502
- return createButton({
503
- attr: BUTTON_IDENTIFIERS.columnSettings,
504
- icon: columnSettingsButton.icon,
505
- data: {
506
- "data-field-id": fieldId
507
- },
508
- testId: "table-column-settings-button"
509
- });
510
- },
511
- getData: (el) => ({
512
- type: "columnSettings",
513
- fieldId: el.getAttribute("data-field-id")
514
- })
515
- };
516
- var rowMenuButton = {
517
- attr: BUTTON_IDENTIFIERS.rowMenu,
518
- icon: "ph--dots-three--regular",
519
- render: ({ rowIndex }) => {
520
- return createButton({
521
- attr: BUTTON_IDENTIFIERS.rowMenu,
522
- icon: rowMenuButton.icon,
523
- data: {
524
- "data-row-index": rowIndex.toString()
525
- },
526
- testId: "table-row-menu-button"
527
- });
528
- },
529
- getData: (el) => ({
530
- type: "rowMenu",
531
- rowIndex: Number(el.getAttribute("data-row-index"))
532
- })
533
- };
534
- var sortButton = {
535
- attr: BUTTON_IDENTIFIERS.sort,
536
- icon: "ph--sort-ascending--regular",
537
- render: ({ fieldId, direction }) => {
538
- return createButton({
539
- attr: BUTTON_IDENTIFIERS.sort,
540
- icon: direction === "desc" ? "ph--sort-descending--regular" : "ph--sort-ascending--regular",
541
- data: {
542
- "data-field-id": fieldId,
543
- "data-direction": direction ?? ""
544
- },
545
- testId: "table-sort-button",
546
- // TODO(ZaymonFC): All buttons should be rendered into an absolutely positioned flex container.
547
- // This is a bit hacky.
548
- type: "secondary"
549
- });
550
- },
551
- getData: (el) => ({
552
- type: "sort",
553
- fieldId: el.getAttribute("data-field-id"),
554
- direction: el.getAttribute("data-direction")
555
- })
556
- };
557
- var saveDraftRowButton = {
558
- attr: BUTTON_IDENTIFIERS.saveDraftRow,
559
- icon: "ph--floppy-disk--regular",
560
- render: ({ rowIndex, disabled }) => {
561
- return createButton({
562
- attr: BUTTON_IDENTIFIERS.saveDraftRow,
563
- icon: saveDraftRowButton.icon,
564
- disabled,
565
- data: {
566
- "data-row-index": rowIndex.toString()
567
- },
568
- testId: "table-save-draft-row-button"
569
- });
570
- },
571
- getData: (el) => ({
572
- type: "saveDraftRow",
573
- rowIndex: Number(el.getAttribute("data-row-index"))
574
- })
575
- };
576
- var tableButtons = {
577
- addColumn: addColumnButton,
578
- columnSettings: columnSettingsButton,
579
- rowMenu: rowMenuButton,
580
- saveDraftRow: saveDraftRowButton,
581
- sort: sortButton
582
- };
583
-
584
- // src/util/table-controls.ts
585
- var CONTROL_IDENTIFIERS = {
586
- checkbox: "data-table-checkbox",
587
- switch: "data-table-switch"
588
- };
589
- var BASE_CLASSES = {
590
- checkbox: "absolute inset-block-[.375rem] inline-end-[.375rem] dx-checkbox",
591
- switch: "absolute inset-block-[.25rem] inline-end-[.25rem] dx-checkbox--switch"
592
- };
593
- var renderAttributes = (data) => {
594
- return Object.entries(data).map(([k, v]) => `${k}="${v}"`).join(" ");
595
- };
596
- var renderInput = (baseClass, attrs, checked = false, disabled = false, preventToggle = false, testId) => {
597
- return `<input type="checkbox" class="${baseClass}" ${renderAttributes(attrs)} ${checked ? "checked" : ""} ${preventToggle ? 'onclick="return false"' : ""} ${disabled ? "disabled" : ""} data-testid="${testId}"/>`;
598
- };
599
- var CheckboxStory = ({ rowIndex, header = false, checked = false, disabled = false }) => {
600
- const attrs = {
601
- [CONTROL_IDENTIFIERS.checkbox]: "",
602
- "data-row-index": rowIndex.toString(),
603
- ...header && {
604
- "data-header": ""
605
- }
606
- };
607
- return renderInput(BASE_CLASSES.checkbox, attrs, checked, disabled, true, "table-selection");
608
- };
609
- var SwitchStory = ({ colIndex, rowIndex, checked = false, disabled = false }) => {
610
- const attrs = {
611
- [CONTROL_IDENTIFIERS.switch]: "",
612
- "data-row-index": rowIndex.toString(),
613
- "data-col-index": colIndex.toString()
614
- };
615
- return renderInput(BASE_CLASSES.switch, attrs, checked, disabled, true, "table-switch");
616
- };
617
- var tableControls = {
618
- checkbox: {
619
- attr: CONTROL_IDENTIFIERS.checkbox,
620
- render: CheckboxStory,
621
- getData: (el) => ({
622
- type: "checkbox",
623
- rowIndex: Number(el.getAttribute("data-row-index")),
624
- header: el.hasAttribute("data-header")
625
- })
626
- },
627
- switch: {
628
- attr: CONTROL_IDENTIFIERS.switch,
629
- render: SwitchStory,
630
- getData: (el) => ({
631
- type: "switch",
632
- rowIndex: Number(el.getAttribute("data-row-index")),
633
- colIndex: Number(el.getAttribute("data-col-index"))
634
- })
635
- }
636
- };
637
-
638
124
  // src/util/schema.ts
639
- import { Schema as Schema3 } from "effect";
640
- import { isTypeLiteral, TypeLiteral } from "effect/SchemaAST";
125
+ import * as Schema from "effect/Schema";
126
+ import * as SchemaAST from "effect/SchemaAST";
641
127
  var narrowSchema = (schema, paths) => {
642
128
  const ast = schema?.ast;
643
- if (isTypeLiteral(ast)) {
129
+ if (SchemaAST.isTypeLiteral(ast)) {
644
130
  const propertySignatures = ast.propertySignatures.filter((signature) => paths.includes(signature.name.toString()));
645
131
  if (propertySignatures.length > 0) {
646
- const narrowType = new TypeLiteral(propertySignatures, []);
647
- return Schema3.make(narrowType);
132
+ const narrowType = new SchemaAST.TypeLiteral(propertySignatures, []);
133
+ return Schema.make(narrowType);
648
134
  }
649
135
  }
650
136
  return void 0;
651
137
  };
652
138
 
653
- // src/model/selection-model.ts
654
- var SelectionModel = class extends Resource {
655
- constructor(_rows, _selectionMode, _onSelectionChanged) {
656
- super(), this._rows = _rows, this._selectionMode = _selectionMode, this._onSelectionChanged = _onSelectionChanged, this._selection = signal(/* @__PURE__ */ new Set()), this._validSelectedIds = computed(() => {
657
- const rows = this._rows.value;
658
- if (!rows) {
659
- return /* @__PURE__ */ new Set();
660
- }
661
- const validIds = new Set(rows.map((row) => row.id));
662
- return new Set([
663
- ...this._selection.value
664
- ].filter((id) => validIds.has(id)));
665
- }), this._hasSelection = computed(() => this._validSelectedIds.value.size > 0), this._allSelected = computed(() => {
666
- const rows = this._rows.value;
667
- if (rows.length === 0) {
668
- return false;
669
- }
670
- return rows.every((row) => this._selection.value.has(row.id));
671
- }), this.getSelectedRows = () => {
672
- const selectedIds = this._validSelectedIds.value;
673
- return this._rows.value.filter((row) => selectedIds.has(row.id));
674
- }, this.isRowIndexSelected = (rowIndex) => {
675
- const row = this._rows.value[rowIndex];
676
- return this._selection.value.has(row.id);
677
- }, this.isObjectSelected = (object) => {
678
- return this._selection.value.has(object.id);
679
- }, this.toggleSelectionForRowIndex = (rowIndex) => {
680
- const row = this._rows.value[rowIndex];
681
- const newSelection = new Set(this._selection.value);
682
- if (newSelection.has(row.id)) {
683
- newSelection.delete(row.id);
684
- } else {
685
- if (this._selectionMode === "single") {
686
- newSelection.clear();
687
- }
688
- newSelection.add(row.id);
689
- }
690
- this._selection.value = newSelection;
691
- }, this.setSelection = (mode) => {
692
- switch (mode) {
693
- case "all": {
694
- this._selection.value = new Set(this._rows.value.map((row) => row.id));
695
- break;
696
- }
697
- case "none": {
698
- this._selection.value = /* @__PURE__ */ new Set();
699
- break;
700
- }
701
- }
702
- };
703
- }
704
- async _open() {
705
- const selectionWatcher = effect(() => {
706
- touch(this._selection.value);
707
- this._onSelectionChanged?.();
139
+ // src/model/modal-controller.ts
140
+ import { Atom } from "@effect-atom/atom-react";
141
+ var ModalController = class {
142
+ _registry;
143
+ _state;
144
+ _triggerRef = {
145
+ current: null
146
+ };
147
+ constructor(registry) {
148
+ this._registry = registry;
149
+ this._state = Atom.make({
150
+ type: "closed"
708
151
  });
709
- this._ctx.onDispose(selectionWatcher);
710
- }
711
- //
712
- // Getters
713
- //
714
- get selection() {
715
- return this._selection;
716
152
  }
717
- get hasSelection() {
718
- return this._hasSelection;
153
+ get state() {
154
+ return this._state;
719
155
  }
720
- get allRowsSeleted() {
721
- return this._allSelected;
156
+ get stateValue() {
157
+ return this._registry.get(this._state);
722
158
  }
723
- get selectionMode() {
724
- return this._selectionMode;
159
+ get trigger() {
160
+ return this._triggerRef;
725
161
  }
726
- };
727
-
728
- // src/model/table-sorting.ts
729
- import { computed as computed2, signal as signal2 } from "@preact/signals-core";
730
- import orderBy from "lodash.orderby";
731
- import { getValue, FormatEnum as FormatEnum2, TypeEnum as TypeEnum2 } from "@dxos/echo-schema";
732
- import { formatForDisplay } from "@dxos/react-ui-form";
733
- var TableSorting = class {
734
- constructor(rows, _view, _projection) {
735
- this._view = _view;
736
- this._projection = _projection;
737
- this._displayToDataIndex = /* @__PURE__ */ new Map();
738
- this._localSort = signal2(void 0);
739
- this._rows = rows;
740
- this._isDirty = computed2(() => {
741
- const local = this._localSort.value;
742
- const viewSort = this._view?.query.sort?.[0];
743
- if (local?.type === "cleared") {
744
- return viewSort !== void 0;
745
- }
746
- if (local?.type === "active") {
747
- if (!viewSort) {
748
- return true;
749
- }
750
- return local.sort.fieldId !== viewSort.fieldId || local.sort.direction !== viewSort.direction;
162
+ setTrigger = (trigger) => {
163
+ this._triggerRef.current = trigger;
164
+ };
165
+ showRowMenu = (rowIndex) => {
166
+ this._registry.set(this._state, {
167
+ type: "row",
168
+ rowIndex
169
+ });
170
+ };
171
+ showColumnMenu = (fieldId) => {
172
+ this._registry.set(this._state, {
173
+ type: "column",
174
+ fieldId
175
+ });
176
+ };
177
+ showColumnCreator = () => {
178
+ this._registry.set(this._state, {
179
+ type: "columnSettings",
180
+ mode: {
181
+ type: "create"
751
182
  }
752
- return false;
753
183
  });
754
- this.sortedRows = this.initialiseSortedRows();
755
- }
756
- /**
757
- * @reactive
758
- * @returns Local sort if present, falls back to view sort.
759
- */
760
- get sorting() {
761
- const local = this._localSort.value;
762
- if (local?.type === "cleared") {
763
- return void 0;
184
+ };
185
+ showReferencePanel = (targetId, schemaId) => {
186
+ this._registry.set(this._state, {
187
+ type: "refPanel",
188
+ targetId,
189
+ typename: schemaId
190
+ });
191
+ };
192
+ openColumnSettings = () => {
193
+ const currentState = this._registry.get(this._state);
194
+ if (currentState.type === "column") {
195
+ const fieldId = currentState.fieldId;
196
+ requestAnimationFrame(() => {
197
+ this._registry.set(this._state, {
198
+ type: "columnSettings",
199
+ mode: {
200
+ type: "edit",
201
+ fieldId
202
+ }
203
+ });
204
+ });
764
205
  }
765
- if (local?.type === "active") {
766
- return local.sort;
206
+ };
207
+ close = () => {
208
+ this._registry.set(this._state, {
209
+ type: "closed"
210
+ });
211
+ };
212
+ };
213
+
214
+ // src/components/TableCellEditor/TableCellEditor.tsx
215
+ import { EditorView } from "@codemirror/view";
216
+ import React3, { useCallback as useCallback2, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
217
+ import { debounce } from "@dxos/async";
218
+ import { Format, TypeEnum } from "@dxos/echo/internal";
219
+ import { invariant as invariant2 } from "@dxos/invariant";
220
+ import { GridCellEditor, editorKeys, parseCellIndex as parseCellIndex2, useGridContext as useGridContext3 } from "@dxos/react-ui-grid";
221
+
222
+ // src/components/TableCellEditor/CellValidationMessage.tsx
223
+ import React from "react";
224
+ import { useGridContext } from "@dxos/react-ui-grid";
225
+ var CellValidationMessage = ({ __gridScope, validationError, variant = "error" }) => {
226
+ const { editing, editBox: box } = useGridContext("GridSheetCellEditor", __gridScope);
227
+ if (!editing || !validationError) {
228
+ return null;
229
+ }
230
+ const bgClass = variant === "error" ? "bg-error-surface" : "bg-warning-surface";
231
+ const textClass = variant === "error" ? "text-error-surface-text" : "text-warning-surface-text";
232
+ return /* @__PURE__ */ React.createElement("div", {
233
+ role: "none",
234
+ className: `absolute ${bgClass} ${textClass} rounded-h-sm text-xs p-1`,
235
+ style: {
236
+ ...{
237
+ "--dx-grid-cell-width": `${box?.inlineSize ?? 200}px`
238
+ },
239
+ zIndex: 10,
240
+ insetBlockEnd: `calc(100% - ${box.insetBlockStart}px)`,
241
+ insetInlineStart: box.insetInlineStart,
242
+ inlineSize: box.inlineSize
767
243
  }
768
- return this._view?.query.sort?.[0];
769
- }
770
- /**
771
- * @reactive
772
- * @returns Whether local sort differs from view sort.
773
- */
774
- get isDirty() {
775
- return this._isDirty.value;
776
- }
777
- getDataIndex(displayIndex) {
778
- return this._displayToDataIndex.get(displayIndex) ?? displayIndex;
779
- }
780
- setSort(fieldId, direction) {
781
- this._localSort.value = {
782
- type: "active",
783
- sort: {
784
- fieldId,
785
- direction
786
- }
787
- };
244
+ }, validationError);
245
+ };
246
+
247
+ // src/components/TableCellEditor/FormCellEditor.tsx
248
+ import React2, { useCallback, useEffect, useMemo, useRef, useState } from "react";
249
+ import { Entity } from "@dxos/echo";
250
+ import { Ref as Ref2, getValue } from "@dxos/echo/internal";
251
+ import { invariant } from "@dxos/invariant";
252
+ import { Popover } from "@dxos/react-ui";
253
+ import { Form } from "@dxos/react-ui-form";
254
+ import { parseCellIndex, useGridContext as useGridContext2 } from "@dxos/react-ui-grid";
255
+ import { getDeep, isTruthy, setDeep } from "@dxos/util";
256
+ import { translationKey } from "#translations";
257
+ var __dxlog_file = "/__w/dxos/dxos/packages/ui/react-ui-table/src/components/TableCellEditor/FormCellEditor.tsx";
258
+ var createOptionLabel = [
259
+ "create-new-object.label",
260
+ {
261
+ ns: translationKey
788
262
  }
789
- toggleSort(fieldId) {
790
- if (!this._view || !this.sorting || this.sorting.fieldId !== fieldId) {
791
- return;
263
+ ];
264
+ var FormCellEditor = ({ __gridScope, schema, model, fieldProjection, onSave, onCreate, ...formProps }) => {
265
+ const anchorRef = useRef(null);
266
+ const { id: _gridId, editing: contextEditing, setEditing } = useGridContext2("ArrayEditor", __gridScope);
267
+ const [editing, setLocalEditing] = useState(false);
268
+ useEffect(() => {
269
+ if (contextEditing && contextEditing.cellElement) {
270
+ anchorRef.current = contextEditing.cellElement.querySelector(".dx-grid__cell__content");
271
+ setLocalEditing(true);
272
+ } else {
273
+ anchorRef.current = null;
274
+ setLocalEditing(false);
792
275
  }
793
- switch (this.sorting.direction) {
794
- case "asc": {
795
- this.setSort(fieldId, "desc");
796
- break;
797
- }
798
- case "desc": {
799
- this.setSort(fieldId, "asc");
800
- break;
801
- }
276
+ }, [
277
+ contextEditing
278
+ ]);
279
+ const narrowedSchema = useMemo(() => {
280
+ if (!schema) {
281
+ return void 0;
802
282
  }
803
- }
804
- clearSort() {
805
- this._localSort.value = {
806
- type: "cleared"
807
- };
808
- }
809
- save() {
810
- if (this._view && this._localSort.value !== void 0) {
811
- if (this._localSort.value.type === "active") {
812
- this._view.query.sort = [
813
- this._localSort.value.sort
814
- ];
283
+ return narrowSchema(schema, [
284
+ fieldProjection.field.path
285
+ ]);
286
+ }, [
287
+ JSON.stringify(schema),
288
+ fieldProjection.field.path
289
+ ]);
290
+ const originalRow = useMemo(() => {
291
+ if (model && contextEditing) {
292
+ const cell = parseCellIndex(contextEditing.index);
293
+ if (model.isDraftCell(cell)) {
294
+ const draftRow = model.getDraftRows()[cell.row];
295
+ invariant(draftRow, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 52, S: void 0, A: ["draftRow", ""] });
296
+ return draftRow.data;
815
297
  } else {
816
- this._view.query.sort = [];
298
+ const row = model.getRowAt(cell.row);
299
+ invariant(row, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 56, S: void 0, A: ["row", ""] });
300
+ return row;
817
301
  }
818
- this._localSort.value = void 0;
819
302
  }
820
- }
821
- //
822
- // Initialisation.
823
- //
824
- initialiseSortedRows() {
825
- return computed2(() => {
826
- this._displayToDataIndex.clear();
827
- const sort = this.sorting;
828
- if (!sort || !this._view) {
829
- return this._rows.value;
830
- }
831
- const field = this._view.fields.find((f) => f.id === sort.fieldId);
832
- if (!field) {
833
- return this._rows.value;
834
- }
835
- const { props } = this._projection.getFieldProjection(field.id);
836
- const dataWithIndices = this._rows.value.map((item, index) => {
837
- const sortValue = this.getSortValue(props, field, item);
838
- const isEmpty = !(props.type === TypeEnum2.Boolean) && (sortValue == null || sortValue === "");
839
- return {
840
- item,
841
- index,
842
- value: sortValue,
843
- isEmpty
844
- };
845
- });
846
- const ordered = orderBy(dataWithIndices, [
847
- "isEmpty",
848
- "value"
849
- ], [
850
- "asc",
851
- sort.direction
852
- ]);
853
- ordered.forEach(({ index: dataIndex }, displayIndex) => {
854
- if (displayIndex !== dataIndex) {
855
- this._displayToDataIndex.set(displayIndex, dataIndex);
856
- }
857
- });
858
- return ordered.map(({ item }) => item);
859
- });
860
- }
861
- getSortValue(props, field, item) {
862
- const { type, format } = props;
863
- const value = getValue(item, field.path);
864
- if (type === TypeEnum2.Boolean) {
865
- return !!value;
303
+ return void 0;
304
+ }, [
305
+ model,
306
+ contextEditing
307
+ ]);
308
+ const initialFormValues = useMemo(() => originalRow ? JSON.parse(JSON.stringify(originalRow)) : {}, [
309
+ originalRow
310
+ ]);
311
+ const [formValues, setFormValues] = useState(initialFormValues);
312
+ const handleValuesChanged = useCallback((values) => {
313
+ setFormValues(values);
314
+ }, []);
315
+ const handleOpenChange = useCallback((nextOpen) => {
316
+ if (nextOpen === false) {
317
+ setEditing(null);
318
+ onSave?.();
866
319
  }
867
- if (type === TypeEnum2.Number) {
868
- return value;
320
+ setLocalEditing(nextOpen);
321
+ }, []);
322
+ const handleSave = useCallback((values) => {
323
+ if (!originalRow) {
324
+ return;
869
325
  }
870
- if (value == null) {
871
- return value;
326
+ const path = fieldProjection.field.path;
327
+ const value = getDeep(values, [
328
+ path
329
+ ]);
330
+ if (model) {
331
+ model.changeRow(originalRow, (mutableRow) => {
332
+ setDeep(mutableRow, [
333
+ path
334
+ ], value);
335
+ });
336
+ } else {
337
+ setDeep(originalRow, [
338
+ path
339
+ ], value);
872
340
  }
873
- if (format === FormatEnum2.SingleSelect) {
874
- const options = props.options;
875
- if (!options) {
876
- return void 0;
341
+ contextEditing?.cellElement?.focus();
342
+ setEditing(null);
343
+ setLocalEditing(false);
344
+ onSave?.();
345
+ }, [
346
+ fieldProjection.field.path,
347
+ onSave,
348
+ contextEditing,
349
+ originalRow,
350
+ model
351
+ ]);
352
+ const handleCreate = useCallback((schema2, values) => {
353
+ const objectWithId = onCreate?.(schema2, values);
354
+ if (objectWithId && originalRow) {
355
+ const ref = Ref2.make(objectWithId);
356
+ const path = fieldProjection.field.path;
357
+ if (model) {
358
+ model.changeRow(originalRow, (mutableRow) => {
359
+ setDeep(mutableRow, [
360
+ path
361
+ ], ref);
362
+ });
363
+ } else {
364
+ setDeep(originalRow, [
365
+ path
366
+ ], ref);
877
367
  }
878
- const option = options.find((option2) => option2.id === value);
879
- return option ? options.indexOf(option) : void 0;
880
368
  }
881
- if (format === FormatEnum2.Ref && field.referencePath) {
882
- const refValue = getValue(value.target, field.referencePath);
883
- return refValue;
884
- }
885
- return formatForDisplay({
886
- type,
887
- format,
888
- value
889
- });
890
- }
369
+ contextEditing?.cellElement?.focus();
370
+ setEditing(null);
371
+ setLocalEditing(false);
372
+ onSave?.();
373
+ }, [
374
+ fieldProjection.field.path,
375
+ onSave,
376
+ contextEditing,
377
+ originalRow,
378
+ onCreate,
379
+ model
380
+ ]);
381
+ const getOptions = useCallback((results) => results.map((obj) => {
382
+ return {
383
+ id: Entity.getDXN(obj).toString(),
384
+ label: getValue(obj, fieldProjection.field.referencePath) || obj.id.toString()
385
+ };
386
+ }).filter(isTruthy), [
387
+ fieldProjection
388
+ ]);
389
+ if (!editing) {
390
+ return null;
391
+ }
392
+ return /* @__PURE__ */ React2.createElement(Popover.Root, {
393
+ open: editing,
394
+ onOpenChange: handleOpenChange
395
+ }, /* @__PURE__ */ React2.createElement(Popover.VirtualTrigger, {
396
+ virtualRef: anchorRef
397
+ }), /* @__PURE__ */ React2.createElement(Popover.Portal, null, /* @__PURE__ */ React2.createElement(Popover.Content, {
398
+ tabIndex: -1,
399
+ classNames: "dx-card-popover-width dx-density-fine"
400
+ }, /* @__PURE__ */ React2.createElement(Popover.Arrow, null), /* @__PURE__ */ React2.createElement(Popover.Viewport, null, /* @__PURE__ */ React2.createElement(Form.Root, {
401
+ ...formProps,
402
+ autoFocus: true,
403
+ schema: narrowedSchema,
404
+ values: formValues,
405
+ onValuesChanged: handleValuesChanged,
406
+ projection: model?.projection,
407
+ createInitialValuePath: fieldProjection.field.referencePath,
408
+ createOptionIcon: "ph--plus--regular",
409
+ createOptionLabel,
410
+ db: model?.db,
411
+ getOptions,
412
+ onCreate: handleCreate,
413
+ onSave: handleSave
414
+ }, /* @__PURE__ */ React2.createElement(Form.Viewport, null, /* @__PURE__ */ React2.createElement(Form.Content, null, /* @__PURE__ */ React2.createElement(Form.FieldSet, null), /* @__PURE__ */ React2.createElement(Form.Actions, null))))))));
891
415
  };
892
416
 
893
- // src/util/tag.ts
894
- var TAG_PATTERN_REGEX = /\[([^\]]+)\]\(([^)]+)\)/g;
895
- var extractTagIds = (value) => {
896
- if (typeof value !== "string") {
897
- return void 0;
898
- }
899
- const ids = [];
900
- let match;
901
- while ((match = TAG_PATTERN_REGEX.exec(value)) !== null) {
902
- if (match[2]) {
903
- ids.push(match[2]);
904
- }
905
- }
906
- return ids.length > 0 ? ids : void 0;
907
- };
908
-
909
- // src/model/table-model.ts
910
- var __dxlog_file = "/__w/dxos/dxos/packages/ui/react-ui-table/src/model/table-model.ts";
911
- var defaultFeatures = {
912
- selection: {
913
- enabled: true,
914
- mode: "multiple"
915
- },
916
- dataEditable: false,
917
- schemaEditable: false
918
- };
919
- var TableModel = class extends Resource2 {
920
- constructor({ id, space, view, projection, features = {}, sorting = [], pinnedRows = {
921
- top: [],
922
- bottom: []
923
- }, rowActions = [], onCellUpdate, onDeleteColumn, onDeleteRows, onInsertRow, onRowOrderChange, onRowAction }) {
924
- super();
925
- this._visibleRange = signal3({
926
- start: {
927
- row: 0,
928
- col: 0
929
- },
930
- end: {
931
- row: 0,
932
- col: 0
933
- }
934
- });
935
- this._rows = signal3([]);
936
- this._draftRows = signal3([]);
937
- //
938
- // Data
939
- //
940
- this.setRows = (obj) => {
941
- this._rows.value = obj;
942
- };
943
- this.getRowCount = () => this._rows.value.length;
944
- /**
945
- * @reactive
946
- */
947
- this.getDraftRowCount = () => this._draftRows.value.length;
948
- this.getColumnCount = () => this._view?.fields.length ?? 0;
949
- this.insertRow = () => {
950
- const result = this._onInsertRow?.();
951
- if (result === false && this._draftRows.value.length === 0) {
952
- this.createDraftRow();
953
- }
954
- };
955
- this.commitDraftRow = (draftRowIndex) => {
956
- if (draftRowIndex < 0 || draftRowIndex >= this._draftRows.value.length) {
957
- return false;
958
- }
959
- const draftRow = this._draftRows.value[draftRowIndex];
960
- if (!draftRow.valid) {
961
- return false;
962
- }
963
- const success = this._onInsertRow?.(draftRow.data);
964
- if (success) {
965
- const newDraftRows = this._draftRows.value.filter((_, index) => index !== draftRowIndex);
966
- this._draftRows.value = newDraftRows;
967
- }
968
- return success ?? false;
969
- };
970
- this.deleteRow = (rowIndex) => {
971
- const row = this._sorting.getDataIndex(rowIndex);
972
- const obj = this._rows.value[row];
973
- const objectsToDelete = [];
974
- if (this.features.selection.enabled && this._selection.hasSelection.value) {
975
- const selectedRows = this._selection.getSelectedRows();
976
- objectsToDelete.push(...selectedRows);
977
- } else {
978
- objectsToDelete.push(obj);
979
- }
980
- this._onDeleteRows?.(row, objectsToDelete);
981
- };
982
- this.handleRowAction = (actionId, rowIndex) => {
983
- if (!this._onRowAction) {
984
- return;
985
- }
986
- const row = this._sorting.getDataIndex(rowIndex);
987
- const data = this._rows.value[row];
988
- if (data) {
989
- this._onRowAction(actionId, data);
990
- }
991
- };
992
- this.getCellData = (cell) => {
993
- const { col, row, plane } = cell;
994
- const fields = this._view?.fields ?? [];
995
- if (col < 0 || col >= fields.length) {
996
- return void 0;
997
- }
998
- const field = fields[col];
999
- let value;
1000
- if (plane === "frozenRowsEnd") {
1001
- if (row >= 0 && row < this._draftRows.value.length) {
1002
- value = getValue2(this._draftRows.value[row].data, field.path);
1003
- } else {
1004
- return void 0;
1005
- }
1006
- } else {
1007
- const dataIndex = this._sorting.getDataIndex(row);
1008
- value = getValue2(this._rows.value[dataIndex], field.path);
1009
- }
1010
- if (value == null) {
1011
- return "";
1012
- }
1013
- const { props } = this._projection.getFieldProjection(field.id);
1014
- switch (props.format) {
1015
- case FormatEnum3.Ref: {
1016
- if (!field.referencePath) {
1017
- return "";
1018
- }
1019
- return getValue2(value.target, field.referencePath);
1020
- }
1021
- default: {
1022
- return formatForEditing({
1023
- type: props.type,
1024
- format: props.format,
1025
- value
1026
- });
1027
- }
1028
- }
1029
- };
1030
- this.validateCellData = async ({ col, row, plane }, value) => {
1031
- const fields = this._view?.fields ?? [];
1032
- if (col < 0 || col >= fields.length) {
1033
- return {
1034
- valid: false,
1035
- error: "Invalid column index"
1036
- };
1037
- }
1038
- const field = fields[col];
1039
- const { props } = this._projection.getFieldProjection(field.id);
1040
- const transformedValue = editorTextToCellValue(props, value);
1041
- const isDraftRow = plane === "frozenRowsEnd";
1042
- if (isDraftRow) {
1043
- const isInvalidDraftRowIndex = row < 0 || row >= this._draftRows.value.length;
1044
- if (isInvalidDraftRowIndex) {
1045
- return {
1046
- valid: false,
1047
- error: "Invalid draft row index"
1048
- };
1049
- }
1050
- const draftRow = this._draftRows.value[row];
1051
- const snapshot = {
1052
- ...draftRow.data
1053
- };
1054
- setValue(snapshot, field.path, transformedValue);
1055
- const validationErrors = this.validateDraftRowData(snapshot);
1056
- if (validationErrors.length > 0) {
1057
- const error = validationErrors.find((err) => err.path === field.path);
1058
- if (error) {
1059
- return {
1060
- valid: false,
1061
- error: error.message
1062
- };
1063
- }
1064
- }
1065
- return {
1066
- valid: true
1067
- };
1068
- } else {
1069
- const rowIdx = this._sorting.getDataIndex(row);
1070
- const currentRow = this._rows.value[rowIdx];
1071
- invariant(currentRow, "Invalid row index", {
1072
- F: __dxlog_file,
1073
- L: 507,
1074
- S: this,
1075
- A: [
1076
- "currentRow",
1077
- "'Invalid row index'"
1078
- ]
1079
- });
1080
- const snapshot = getSnapshot(currentRow);
1081
- setValue(snapshot, field.path, transformedValue);
1082
- const schema = getSchema(currentRow);
1083
- invariant(schema, void 0, {
1084
- F: __dxlog_file,
1085
- L: 513,
1086
- S: this,
1087
- A: [
1088
- "schema",
1089
- ""
1090
- ]
1091
- });
1092
- const validationResult = validateSchema(schema, snapshot);
1093
- if (validationResult && validationResult.length > 0) {
1094
- const error = validationResult[0];
1095
- invariant(error.path === field.path, void 0, {
1096
- F: __dxlog_file,
1097
- L: 518,
1098
- S: this,
1099
- A: [
1100
- "error.path === field.path",
1101
- ""
1102
- ]
1103
- });
1104
- return {
1105
- valid: false,
1106
- error: error.message
1107
- };
1108
- }
1109
- return {
1110
- valid: true
1111
- };
1112
- }
1113
- };
1114
- this.setCellData = (cell, value) => {
1115
- const { col, row, plane } = cell;
1116
- const fields = this._view?.fields ?? [];
1117
- if (col < 0 || col >= fields.length) {
1118
- return;
1119
- }
1120
- const field = fields[col];
1121
- const { props } = this._projection.getFieldProjection(field.id);
1122
- const transformedValue = editorTextToCellValue(props, value);
1123
- if (props.format === FormatEnum3.Ref && !isLiveObject(value)) {
1124
- return;
1125
- }
1126
- if (plane === "frozenRowsEnd") {
1127
- if (row >= 0 && row < this._draftRows.value.length) {
1128
- setValue(this._draftRows.value[row].data, field.path, transformedValue);
1129
- this.validateDraftRow(row);
1130
- }
1131
- } else {
1132
- const rowIdx = this._sorting.getDataIndex(row);
1133
- setValue(this._rows.value[rowIdx], field.path, transformedValue);
1134
- }
1135
- };
1136
- this._id = id;
1137
- this._space = space;
1138
- this._view = view;
1139
- this._projection = projection;
1140
- this._features = {
1141
- ...defaultFeatures,
1142
- ...features
1143
- };
1144
- invariant(!(this._features.dataEditable && this._features.selection.enabled && this._features.selection.mode === "single"), "Single selection is not compatible with editable tables.", {
1145
- F: __dxlog_file,
1146
- L: 145,
1147
- S: this,
1148
- A: [
1149
- "!(this._features.dataEditable && this._features.selection.enabled && this._features.selection.mode === 'single')",
1150
- "'Single selection is not compatible with editable tables.'"
1151
- ]
1152
- });
1153
- this._sorting = new TableSorting(this._rows, this._view, projection);
1154
- if (sorting.length > 0) {
1155
- const [sort] = sorting;
1156
- this._sorting.setSort(sort.fieldId, sort.direction);
1157
- }
1158
- this._pinnedRows = pinnedRows;
1159
- this._rowActions = rowActions;
1160
- this._onInsertRow = onInsertRow;
1161
- this._onDeleteRows = onDeleteRows;
1162
- this._onDeleteColumn = onDeleteColumn;
1163
- this._onCellUpdate = onCellUpdate;
1164
- this._onRowOrderChange = onRowOrderChange;
1165
- this._onRowAction = onRowAction;
1166
- }
1167
- get id() {
1168
- return this._id;
1169
- }
1170
- get space() {
1171
- return this._space;
1172
- }
1173
- get view() {
1174
- return this._view;
1175
- }
1176
- get projection() {
1177
- return this._projection;
1178
- }
1179
- get rows() {
1180
- return this._sorting.sortedRows;
1181
- }
1182
- get features() {
1183
- return this._features;
1184
- }
1185
- /**
1186
- * @reactive
1187
- */
1188
- get sorting() {
1189
- return this._sorting;
1190
- }
1191
- /**
1192
- * Gets the row data at the specified display index.
1193
- */
1194
- getRowAt(displayIndex) {
1195
- if (displayIndex < 0 || displayIndex >= this._sorting.sortedRows.value.length) {
1196
- return void 0;
1197
- }
1198
- return this._sorting.sortedRows.value[displayIndex];
1199
- }
1200
- get pinnedRows() {
1201
- return this._pinnedRows;
1202
- }
1203
- get columnMeta() {
1204
- invariant(this._columnMeta, void 0, {
1205
- F: __dxlog_file,
1206
- L: 214,
1207
- S: this,
1208
- A: [
1209
- "this._columnMeta",
1210
- ""
1211
- ]
1212
- });
1213
- return this._columnMeta;
1214
- }
1215
- get selection() {
1216
- return this._selection;
1217
- }
1218
- /** @reactive */
1219
- get isViewDirty() {
1220
- return this._sorting.isDirty;
1221
- }
1222
- get rowActions() {
1223
- return this._rowActions;
1224
- }
1225
- //
1226
- // Initialisation
1227
- //
1228
- async _open() {
1229
- this.initializeColumnMeta();
1230
- this.initializeEffects();
1231
- this._selection = new SelectionModel(this._sorting.sortedRows, this._features.selection.mode ?? "multiple", () => this._onRowOrderChange?.());
1232
- await this._selection.open(this._ctx);
1233
- }
1234
- initializeColumnMeta() {
1235
- this._columnMeta = computed3(() => {
1236
- const fields = this._view?.fields ?? [];
1237
- const meta = Object.fromEntries(fields.map((field, index) => [
1238
- index,
1239
- {
1240
- size: field?.size ?? 256,
1241
- resizeable: true
1242
- }
1243
- ]));
1244
- return {
1245
- grid: meta,
1246
- frozenColsStart: {
1247
- 0: {
1248
- size: 30,
1249
- resizeable: false
1250
- }
1251
- },
1252
- frozenColsEnd: {
1253
- 0: {
1254
- size: 40,
1255
- resizeable: false
1256
- }
1257
- }
1258
- };
1259
- });
1260
- }
1261
- initializeEffects() {
1262
- const rowOrderWatcher = effect2(() => {
1263
- touch(this._sorting.sortedRows.value);
1264
- this._onRowOrderChange?.();
1265
- });
1266
- this._ctx.onDispose(rowOrderWatcher);
1267
- const rowEffectManager = effect2(() => {
1268
- const { start, end } = touch(this._visibleRange.value);
1269
- const rowEffects = [];
1270
- for (let row = start.row; row <= end.row; row++) {
1271
- rowEffects.push(effect2(() => {
1272
- const obj = this._sorting.sortedRows.value[row];
1273
- this._view?.fields.forEach((field) => touch(getValue2(obj, field.path)));
1274
- this._onCellUpdate?.({
1275
- row,
1276
- col: start.col,
1277
- plane: "grid"
1278
- });
1279
- }));
1280
- }
1281
- return () => rowEffects.forEach((cleanup) => cleanup());
1282
- });
1283
- this._ctx.onDispose(rowEffectManager);
1284
- const draftRowsWatcher = effect2(() => {
1285
- touch(this._draftRows.value);
1286
- this._onRowOrderChange?.();
1287
- });
1288
- this._ctx.onDispose(draftRowsWatcher);
1289
- }
1290
- // TODO(ZaymonFC): Return .value instead of exposing the signal.
1291
- get draftRows() {
1292
- return this._draftRows;
1293
- }
1294
- /**
1295
- * Checks if a specific field path has validation errors for a given draft row.
1296
- * @param draftRowIndex - The index of the draft row to check
1297
- * @param fieldPath - The path of the field to check for validation errors
1298
- * @returns true if the field has validation errors, false otherwise
1299
- */
1300
- hasDraftRowValidationError(draftRowIndex, fieldPath) {
1301
- if (draftRowIndex < 0 || draftRowIndex >= this._draftRows.value.length) {
1302
- return false;
1303
- }
1304
- const draftRow = this._draftRows.value[draftRowIndex];
1305
- const validationErrors = draftRow.validationErrors || [];
1306
- return validationErrors.some((error) => error.path === fieldPath);
1307
- }
1308
- createDraftRow() {
1309
- if (!this._view) {
1310
- return;
1311
- }
1312
- const draftData = {
1313
- id: ObjectId.random()
1314
- };
1315
- const validationErrors = this.validateDraftRowData(draftData);
1316
- const draftRow = {
1317
- data: draftData,
1318
- valid: validationErrors.length === 0,
1319
- validationErrors
1320
- };
1321
- this._draftRows.value = [
1322
- ...this._draftRows.value,
1323
- draftRow
1324
- ];
1325
- }
1326
- validateDraftRow(draftRowIndex) {
1327
- if (draftRowIndex < 0 || draftRowIndex >= this._draftRows.value.length) {
1328
- return;
1329
- }
1330
- const draftRow = this._draftRows.value[draftRowIndex];
1331
- const validationErrors = this.validateDraftRowData(draftRow.data);
1332
- const updatedDraftRow = {
1333
- ...draftRow,
1334
- valid: validationErrors.length === 0,
1335
- validationErrors
1336
- };
1337
- const newDraftRows = [
1338
- ...this._draftRows.value
1339
- ];
1340
- newDraftRows[draftRowIndex] = updatedDraftRow;
1341
- this._draftRows.value = newDraftRows;
1342
- }
1343
- validateDraftRowData(data) {
1344
- const schema = toEffectSchema(this._projection.schema);
1345
- return validateSchema(schema, data) || [];
1346
- }
1347
- /**
1348
- * Updates a cell's value using a transform function.
1349
- * @param {DxGridPlanePosition} position - The position of the cell to update.
1350
- * @param {(value: any) => any} update - A function that takes the current value and returns the updated value.
1351
- */
1352
- updateCellData({ col, row }, update) {
1353
- const dataRow = this._sorting.getDataIndex(row);
1354
- const fields = this._view?.fields ?? [];
1355
- const field = fields[col];
1356
- const value = getValue2(this._rows.value[dataRow], field.path);
1357
- const updatedValue = update(value);
1358
- setValue(this._rows.value[dataRow], field.path, updatedValue);
1359
- }
1360
- /**
1361
- * Gets the domain-appropriate cell type for a given cell position.
1362
- * This abstracts away the low-level grid plane concepts and provides domain-specific types.
1363
- * @param cell - The cell position to classify
1364
- * @returns The domain-appropriate cell type
1365
- */
1366
- getCellType(cell) {
1367
- switch (cell.plane) {
1368
- case "frozenRowsEnd":
1369
- return "draft";
1370
- case "frozenRowsStart":
1371
- return "header";
1372
- case "grid":
1373
- default:
1374
- return "standard";
1375
- }
1376
- }
1377
- /**
1378
- * Convenience method to check if a cell is a draft cell.
1379
- * @param cell - The cell position to check
1380
- * @returns true if the cell is a draft cell, false otherwise
1381
- */
1382
- isDraftCell(cell) {
1383
- return this.getCellType(cell) === "draft";
1384
- }
1385
- //
1386
- // Column Operations
1387
- //
1388
- deleteColumn(fieldId) {
1389
- if (!this._view) {
417
+ // src/components/TableCellEditor/TableCellEditor.tsx
418
+ var __dxlog_file2 = "/__w/dxos/dxos/packages/ui/react-ui-table/src/components/TableCellEditor/TableCellEditor.tsx";
419
+ var TableValueEditor = ({ __gridScope, schema, model, modals, onFocus, onSave, onCreate }) => {
420
+ const { editing } = useGridContext3("TableValueEditor", __gridScope);
421
+ const fieldProjection = useMemo2(() => {
422
+ if (!model || !editing) {
1390
423
  return;
1391
424
  }
1392
- const field = this._view?.fields.find((field2) => field2.id === fieldId);
1393
- if (field && this._onDeleteColumn) {
1394
- this._onDeleteColumn(field.id);
1395
- }
1396
- }
1397
- //
1398
- // Resize
1399
- //
1400
- setColumnWidth(columnIndex, width) {
1401
- const fields = this._view?.fields ?? [];
1402
- if (columnIndex < fields.length) {
1403
- const newWidth = Math.max(0, width);
1404
- const field = fields[columnIndex];
1405
- if (field) {
1406
- field.size = newWidth;
1407
- }
1408
- }
1409
- }
1410
- //
1411
- // Pinning
1412
- //
1413
- // TODO(burdon): Change to setPinned(on/off).
1414
- pinRow(rowIndex, side) {
1415
- this._pinnedRows[side].push(rowIndex);
1416
- }
1417
- unpinRow(rowIndex) {
1418
- this._pinnedRows.top = this._pinnedRows.top.filter((index) => index !== rowIndex);
1419
- this._pinnedRows.bottom = this._pinnedRows.bottom.filter((index) => index !== rowIndex);
1420
- }
1421
- //
1422
- // View operations
1423
- //
1424
- saveView() {
1425
- this._sorting.save();
1426
- }
1427
- };
1428
- var editorTextToCellValue = (props, value) => {
1429
- switch (props.format) {
1430
- case FormatEnum3.Ref: {
1431
- if (isLiveObject(value)) {
1432
- return Ref3.make(value);
1433
- } else {
1434
- return value;
1435
- }
1436
- }
1437
- case FormatEnum3.SingleSelect: {
1438
- const ids = extractTagIds(value);
1439
- if (ids && ids.length > 0) {
1440
- return ids[0];
1441
- } else {
1442
- return value;
1443
- }
1444
- }
1445
- case FormatEnum3.MultiSelect: {
1446
- const ids = extractTagIds(value);
1447
- return ids || value;
1448
- }
1449
- default: {
1450
- return parseValue({
1451
- type: props.type,
1452
- format: props.format,
1453
- value
1454
- });
1455
- }
1456
- }
1457
- };
1458
-
1459
- // src/model/modal-controller.ts
1460
- import { signal as signal4 } from "@preact/signals-core";
1461
- import { log } from "@dxos/log";
1462
- var __dxlog_file2 = "/__w/dxos/dxos/packages/ui/react-ui-table/src/model/modal-controller.ts";
1463
- var ModalController = class {
1464
- constructor() {
1465
- this._state = signal4({
1466
- type: "closed"
1467
- });
1468
- this._triggerRef = {
1469
- current: null
1470
- };
1471
- this.setTrigger = (trigger) => {
1472
- this._triggerRef.current = trigger;
1473
- };
1474
- this.showRowMenu = (rowIndex) => {
1475
- this._state.value = {
1476
- type: "row",
1477
- rowIndex
1478
- };
1479
- };
1480
- this.showColumnMenu = (fieldId) => {
1481
- this._state.value = {
1482
- type: "column",
1483
- fieldId
1484
- };
1485
- };
1486
- this.showColumnCreator = () => {
1487
- this._state.value = {
1488
- type: "columnSettings",
1489
- mode: {
1490
- type: "create"
1491
- }
1492
- };
1493
- };
1494
- this.showReferencePanel = (targetId, schemaId) => {
1495
- this._state.value = {
1496
- type: "refPanel",
1497
- targetId,
1498
- typename: schemaId
1499
- };
1500
- };
1501
- this.openColumnSettings = () => {
1502
- if (this._state.value.type === "column") {
1503
- const fieldId = this._state.value.fieldId;
1504
- requestAnimationFrame(() => {
1505
- this._state.value = {
1506
- type: "columnSettings",
1507
- mode: {
1508
- type: "edit",
1509
- fieldId
1510
- }
1511
- };
1512
- });
1513
- }
1514
- };
1515
- this.openCreateRef = (typename, anchorCell, initialValues, onCreate) => {
1516
- if (anchorCell) {
1517
- this._triggerRef.current = anchorCell;
1518
- this._state.value = {
1519
- type: "createRefPanel",
1520
- typename,
1521
- initialValues,
1522
- onCreate
1523
- };
1524
- } else {
1525
- log.warn("anchor cell not found while creating new ref", void 0, {
1526
- F: __dxlog_file2,
1527
- L: 86,
1528
- S: this,
1529
- C: (f, a) => f(...a)
1530
- });
1531
- }
1532
- };
1533
- this.close = () => {
1534
- this._state.value = {
1535
- type: "closed"
1536
- };
1537
- };
1538
- }
1539
- get state() {
1540
- return this._state;
1541
- }
1542
- get trigger() {
1543
- return this._triggerRef;
1544
- }
1545
- };
1546
-
1547
- // src/model/table-presentation.ts
1548
- import { signal as signal5 } from "@preact/signals-core";
1549
- import { isNotNullable } from "effect/Predicate";
1550
- import { Obj as Obj2 } from "@dxos/echo";
1551
- import { FormatEnum as FormatEnum4, getValue as getValue3, TypeEnum as TypeEnum3 } from "@dxos/echo-schema";
1552
- import { cellClassesForFieldType, formatForDisplay as formatForDisplay2 } from "@dxos/react-ui-form";
1553
- import { toPlaneCellIndex } from "@dxos/react-ui-grid";
1554
- import { mx } from "@dxos/react-ui-theme";
1555
- import { VIEW_FIELD_LIMIT } from "@dxos/schema";
1556
- var TablePresentation = class {
1557
- constructor(model, _visibleRange = signal5({
1558
- start: {
1559
- row: 0,
1560
- col: 0
1561
- },
1562
- end: {
1563
- row: 0,
1564
- col: 0
1565
- }
1566
- })) {
1567
- this.model = model;
1568
- this._visibleRange = _visibleRange;
1569
- this.fieldProjectionCache = /* @__PURE__ */ new Map();
1570
- }
1571
- getCells(range, plane) {
1572
- this.fieldProjectionCache.clear();
1573
- let cells;
1574
- switch (plane) {
1575
- case "grid":
1576
- this._visibleRange.value = range;
1577
- cells = this.getMainGridCells(range);
1578
- break;
1579
- case "frozenRowsStart":
1580
- cells = this.getHeaderCells(range);
1581
- break;
1582
- case "frozenColsStart":
1583
- cells = this.getSelectionColumnCells(range);
1584
- break;
1585
- case "frozenColsEnd":
1586
- cells = this.getActionColumnCells(range);
1587
- break;
1588
- case "fixedStartStart":
1589
- cells = this.getSelectAllCell();
1590
- break;
1591
- case "fixedStartEnd":
1592
- cells = this.getNewColumnCell();
1593
- break;
1594
- case "frozenRowsEnd":
1595
- cells = this.getDraftRowCells(range);
1596
- break;
1597
- case "fixedEndEnd":
1598
- cells = this.getDraftActionCells(range);
1599
- break;
1600
- default:
1601
- cells = {};
1602
- }
1603
- if (plane === "grid" && this.model.features.dataEditable === false) {
1604
- Object.values(cells).forEach((cell) => {
1605
- cell.readonly = "text-select";
1606
- });
1607
- }
1608
- return cells;
1609
- }
1610
- /**
1611
- * Gets a field projection, using cache if available within the current render pass.
1612
- */
1613
- getFieldProjection(fieldId) {
1614
- let projection = this.fieldProjectionCache.get(fieldId);
1615
- if (!projection) {
1616
- projection = this.model.projection.getFieldProjection(fieldId);
1617
- this.fieldProjectionCache.set(fieldId, projection);
1618
- }
1619
- return projection;
1620
- }
1621
- createDataCell(cells, obj, field, colIndex, displayIndex) {
1622
- const { props } = this.getFieldProjection(field.id);
1623
- const cell = {
1624
- get value() {
1625
- const value = getValue3(obj, field.path);
1626
- if (value == null) {
1627
- return "";
1628
- }
1629
- if (props.type === "array") {
1630
- return "";
1631
- }
1632
- switch (props.format) {
1633
- case FormatEnum4.Boolean:
1634
- case FormatEnum4.SingleSelect:
1635
- case FormatEnum4.MultiSelect: {
1636
- return "";
1637
- }
1638
- case FormatEnum4.Ref: {
1639
- if (!field.referencePath) {
1640
- return "";
1641
- }
1642
- return getValue3(value.target, field.referencePath);
1643
- }
1644
- default: {
1645
- return formatForDisplay2({
1646
- type: props.type,
1647
- format: props.format,
1648
- value
1649
- });
1650
- }
1651
- }
1652
- }
1653
- };
1654
- const classes = [];
1655
- const formatClasses = cellClassesForFieldType({
1656
- type: props.type,
1657
- format: props.format
1658
- });
1659
- if (formatClasses) {
1660
- classes.push(formatClasses);
1661
- }
1662
- const rowSelectionClasses = cellClassesForRowSelection(this.model.selection.isObjectSelected(obj), this.model.selection.selectionMode);
1663
- if (rowSelectionClasses) {
1664
- classes.push(rowSelectionClasses);
1665
- }
1666
- if (classes.length > 0) {
1667
- cell.className = mx(classes.flat());
1668
- }
1669
- if (props.type === TypeEnum3.Array) {
1670
- const targetArray = getValue3(obj, field.path);
1671
- if (targetArray && Array.isArray(targetArray)) {
1672
- const getLabel = (value) => {
1673
- if (!value) {
1674
- return void 0;
1675
- }
1676
- if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
1677
- return String(value);
1678
- }
1679
- if (Array.isArray(value)) {
1680
- return "Array";
1681
- }
1682
- if (typeof value === "object" && value.name) {
1683
- return value.name;
1684
- }
1685
- if (typeof value === "object" && value.value) {
1686
- return `${value.value}`;
1687
- }
1688
- return "Object";
1689
- };
1690
- const tags = targetArray.map(getLabel).filter(isNotNullable).map((title) => {
1691
- return `<span class="dx-tag rounded-xl" data-hue="neutral">${title}</span>`;
1692
- }).join("");
1693
- cell.accessoryHtml = `<div role='none' class="flex flex-row gap-1 overflow-x-auto">${tags}</div>`;
1694
- }
1695
- }
1696
- if (props.format === FormatEnum4.Ref && props.referenceSchema) {
1697
- const targetObj = getValue3(obj, field.path)?.target;
1698
- if (targetObj) {
1699
- const dxn = Obj2.getDXN(targetObj)?.toString();
1700
- cell.accessoryHtml = `<dx-ref-tag refId=${dxn} class="dx-button is-6 pli-[3px] pbe-[2px] min-bs-0 absolute inline-end-1"><dx-icon icon="ph--link-simple--regular"/></dx-ref-tag>`;
1701
- }
1702
- }
1703
- if (props.format === FormatEnum4.Boolean) {
1704
- const value = getValue3(obj, field.path);
1705
- cell.accessoryHtml = tableControls.switch.render({
1706
- colIndex,
1707
- rowIndex: displayIndex,
1708
- checked: value ?? false
1709
- });
1710
- cell.readonly = "no-text-select";
1711
- }
1712
- if (props.format === FormatEnum4.SingleSelect) {
1713
- const value = getValue3(obj, field.path);
1714
- const options = this.model.projection.getFieldProjection(field.id).props.options;
1715
- if (options) {
1716
- const option = options.find((o) => o.id === value);
1717
- if (option) {
1718
- cell.accessoryHtml = `<span class="dx-tag" data-hue="${option.color}">${option.title}</span>`;
1719
- }
1720
- }
1721
- }
1722
- if (props.format === FormatEnum4.MultiSelect) {
1723
- const values = getValue3(obj, field.path);
1724
- const options = this.model.projection.getFieldProjection(field.id).props.options;
1725
- if (options && values && values.length > 0) {
1726
- const tags = values.map((value) => {
1727
- const option = options.find((o) => o.id === value);
1728
- if (option) {
1729
- return `<span class="dx-tag" data-hue="${option.color}">${option.title}</span>`;
1730
- }
1731
- return null;
1732
- }).filter(Boolean).join("");
1733
- if (tags) {
1734
- cell.accessoryHtml = tags;
1735
- }
1736
- }
1737
- }
1738
- const idx = toPlaneCellIndex({
1739
- col: colIndex,
1740
- row: displayIndex
425
+ const { col } = parseCellIndex2(editing.index);
426
+ const field = model.projection.getFields()[col];
427
+ const fieldProjection2 = model.projection.getFieldProjection(field.id);
428
+ invariant2(fieldProjection2, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 21, S: void 0, A: ["fieldProjection", ""] });
429
+ return fieldProjection2;
430
+ }, [
431
+ model,
432
+ editing
433
+ ]);
434
+ if (fieldProjection?.props.type === TypeEnum.Array || fieldProjection?.props.format === Format.TypeFormat.SingleSelect || fieldProjection?.props.format === Format.TypeFormat.Ref) {
435
+ return /* @__PURE__ */ React3.createElement(FormCellEditor, {
436
+ __gridScope,
437
+ schema,
438
+ model,
439
+ fieldProjection,
440
+ modals,
441
+ onSave,
442
+ onCreate
1741
443
  });
1742
- cells[idx] = cell;
1743
- }
1744
- getMainGridCells(range) {
1745
- const cells = {};
1746
- const fields = this.model.view?.fields ?? [];
1747
- for (let row = range.start.row; row <= range.end.row && row < this.model.getRowCount(); row++) {
1748
- for (let col = range.start.col; col <= range.end.col && col < fields.length; col++) {
1749
- const field = fields[col];
1750
- if (!field) {
1751
- continue;
1752
- }
1753
- this.createDataCell(cells, this.model.rows.value[row], field, col, row);
1754
- }
1755
- }
1756
- return cells;
1757
- }
1758
- getDraftRowCells(range) {
1759
- const cells = {};
1760
- const fields = this.model.view?.fields ?? [];
1761
- const draftRows = this.model.draftRows.value;
1762
- for (let row = range.start.row; row <= range.end.row && row < draftRows.length; row++) {
1763
- const draftRow = draftRows[row];
1764
- for (let col = range.start.col; col <= range.end.col && col < fields.length; col++) {
1765
- const field = fields[col];
1766
- if (!field) {
1767
- continue;
1768
- }
1769
- this.createDataCell(cells, draftRow.data, field, col, row);
1770
- if (this.model.hasDraftRowValidationError(row, field.path)) {
1771
- const cellIndex = toPlaneCellIndex({
1772
- col,
1773
- row
1774
- });
1775
- const cellValue = cells[cellIndex];
1776
- if (cellValue) {
1777
- const existingClasses = cellValue.className || "";
1778
- const draftClasses = "dx-grid__cell--flagged";
1779
- cellValue.className = existingClasses ? `${existingClasses} ${draftClasses}` : draftClasses;
1780
- }
1781
- }
1782
- }
1783
- }
1784
- return cells;
1785
- }
1786
- getHeaderCells(range) {
1787
- const cells = {};
1788
- const fields = this.model.view?.fields ?? [];
1789
- for (let col = range.start.col; col <= range.end.col && col < fields.length; col++) {
1790
- const { field, props } = this.getFieldProjection(fields[col].id);
1791
- const sorting = this.model.sorting?.sorting;
1792
- const direction = sorting?.fieldId === field.id ? sorting.direction : void 0;
1793
- cells[toPlaneCellIndex({
1794
- col,
1795
- row: 0
1796
- })] = {
1797
- // TODO(burdon): Use same logic as form for fallback title.
1798
- value: props.title ?? field.path,
1799
- readonly: true,
1800
- resizeHandle: "col",
1801
- accessoryHtml: `
1802
- ${direction !== void 0 ? tableButtons.sort.render({
1803
- fieldId: field.id,
1804
- direction
1805
- }) : ""}
1806
- ${tableButtons.columnSettings.render({
1807
- fieldId: field.id
1808
- })}
1809
- `,
1810
- className: "!bg-toolbarSurface"
1811
- };
1812
- }
1813
- return cells;
1814
- }
1815
- getSelectionColumnCells(range) {
1816
- const cells = {};
1817
- for (let row = range.start.row; row <= range.end.row && row < this.model.getRowCount(); row++) {
1818
- const isSelected = this.model.selection.isRowIndexSelected(row);
1819
- const classes = cellClassesForRowSelection(isSelected, this.model.selection.selectionMode);
1820
- cells[toPlaneCellIndex({
1821
- col: 0,
1822
- row
1823
- })] = {
1824
- value: "",
1825
- readonly: "no-text-select",
1826
- className: classes ? mx(classes) : void 0,
1827
- accessoryHtml: tableControls.checkbox.render({
1828
- rowIndex: row,
1829
- checked: isSelected
1830
- })
1831
- };
1832
- }
1833
- return cells;
1834
- }
1835
- getActionColumnCells(range) {
1836
- const cells = {};
1837
- for (let row = range.start.row; row <= range.end.row && row < this.model.getRowCount(); row++) {
1838
- const isSelected = this.model.selection.isRowIndexSelected(row);
1839
- const classes = cellClassesForRowSelection(isSelected, this.model.selection.selectionMode);
1840
- cells[toPlaneCellIndex({
1841
- col: 0,
1842
- row
1843
- })] = {
1844
- value: "",
1845
- readonly: true,
1846
- className: classes ? mx(classes) : void 0,
1847
- accessoryHtml: tableButtons.rowMenu.render({
1848
- rowIndex: row
1849
- })
1850
- };
1851
- }
1852
- return cells;
1853
- }
1854
- getSelectAllCell() {
1855
- if (!this.model.features.selection.enabled || this.model.selection.selectionMode === "single") {
1856
- return {
1857
- [toPlaneCellIndex({
1858
- col: 0,
1859
- row: 0
1860
- })]: {
1861
- className: "!bg-toolbarSurface",
1862
- readonly: true,
1863
- value: ""
1864
- }
1865
- };
1866
- }
1867
- return {
1868
- [toPlaneCellIndex({
1869
- col: 0,
1870
- row: 0
1871
- })]: {
1872
- accessoryHtml: tableControls.checkbox.render({
1873
- rowIndex: 0,
1874
- header: true,
1875
- checked: this.model.selection.allRowsSeleted.value
1876
- }),
1877
- className: "!bg-toolbarSurface",
1878
- readonly: true,
1879
- value: ""
1880
- }
1881
- };
1882
- }
1883
- getNewColumnCell() {
1884
- return {
1885
- [toPlaneCellIndex({
1886
- col: 0,
1887
- row: 0
1888
- })]: {
1889
- accessoryHtml: this.model.features.schemaEditable ? tableButtons.addColumn.render({
1890
- disabled: (this.model.view?.fields?.length ?? 0) >= VIEW_FIELD_LIMIT
1891
- }) : void 0,
1892
- className: "!bg-toolbarSurface",
1893
- readonly: true,
1894
- value: ""
1895
- }
1896
- };
1897
- }
1898
- getDraftActionCells(range) {
1899
- const cells = {};
1900
- const draftRows = this.model.draftRows.value;
1901
- for (let row = range.start.row; row <= range.end.row && row < draftRows.length; row++) {
1902
- const draftRow = draftRows[row];
1903
- const disabled = !draftRow.valid;
1904
- cells[toPlaneCellIndex({
1905
- col: 0,
1906
- row
1907
- })] = {
1908
- value: "",
1909
- readonly: true,
1910
- accessoryHtml: tableButtons.saveDraftRow.render({
1911
- rowIndex: row,
1912
- disabled
1913
- })
1914
- };
1915
- }
1916
- return cells;
1917
444
  }
445
+ return /* @__PURE__ */ React3.createElement(TableCellEditor, {
446
+ model,
447
+ modals,
448
+ onFocus,
449
+ onSave,
450
+ __gridScope
451
+ });
1918
452
  };
1919
- var cellClassesForRowSelection = (selected, selectionMode) => {
1920
- if (!selected) {
1921
- if (selectionMode === "single") {
1922
- return [
1923
- "!cursor-pointer"
1924
- ];
1925
- } else {
1926
- return void 0;
453
+ var TableCellEditor = ({ __gridScope, model, modals, onFocus, onSave }) => {
454
+ const { editing, setEditing } = useGridContext3("TableCellEditor", __gridScope);
455
+ const suppressNextBlur = useRef2(false);
456
+ const [validationError, setValidationError] = useState2(null);
457
+ const [validationVariant, setValidationVariant] = useState2("error");
458
+ const fieldProjection = useMemo2(() => {
459
+ if (!model || !editing) {
460
+ return;
1927
461
  }
1928
- }
1929
- switch (selectionMode) {
1930
- case "single":
1931
- return [
1932
- "!bg-currentRelated hover:bg-hoverSurface",
1933
- "!cursor-pointer"
1934
- ];
1935
- case "multiple":
1936
- return [
1937
- "!bg-gridCellSelected"
1938
- ];
1939
- }
1940
- };
1941
-
1942
- // src/components/TableCellEditor/TableCellEditor.tsx
1943
- import { useSignals as _useSignals7 } from "@preact-signals/safe-react/tracking";
1944
- import React7, { useCallback as useCallback4, useEffect as useEffect3, useMemo as useMemo4, useRef as useRef2, useState as useState2 } from "react";
1945
- import { FormatEnum as FormatEnum5, TypeEnum as TypeEnum4 } from "@dxos/echo-schema";
1946
- import { invariant as invariant3 } from "@dxos/invariant";
1947
- import { useThemeContext } from "@dxos/react-ui";
1948
- import { createMarkdownExtensions } from "@dxos/react-ui-editor";
1949
- import { cellQuery, editorKeys, parseCellIndex as parseCellIndex2, useGridContext as useGridContext4, GridCellEditor } from "@dxos/react-ui-grid";
1950
- import { tagPicker, createLinks } from "@dxos/react-ui-tag-picker";
1951
-
1952
- // src/components/TableCellEditor/CellValidationMessage.tsx
1953
- import { useSignals as _useSignals5 } from "@preact-signals/safe-react/tracking";
1954
- import React5 from "react";
1955
- import { useGridContext as useGridContext2 } from "@dxos/react-ui-grid";
1956
- var CellValidationMessage = ({ validationError, variant = "error", __gridScope }) => {
1957
- var _effect = _useSignals5();
1958
- try {
1959
- const { editing, editBox: box } = useGridContext2("GridSheetCellEditor", __gridScope);
1960
- if (!editing || !validationError) {
1961
- return null;
462
+ const { col } = parseCellIndex2(editing.index);
463
+ const field = model.projection.getFields()[col];
464
+ const fieldProjection2 = model.projection.getFieldProjection(field.id);
465
+ invariant2(fieldProjection2, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 59, S: void 0, A: ["fieldProjection", ""] });
466
+ return fieldProjection2;
467
+ }, [
468
+ model,
469
+ editing
470
+ ]);
471
+ const handleEnter = useCallback2(async (value) => {
472
+ if (!model || !editing) {
473
+ return;
1962
474
  }
1963
- const bgClass = variant === "error" ? "bg-errorSurface" : "bg-warningSurface";
1964
- const textClass = variant === "error" ? "text-errorSurfaceText" : "text-warningSurfaceText";
1965
- return /* @__PURE__ */ React5.createElement("div", {
1966
- role: "none",
1967
- className: `absolute ${bgClass} ${textClass} rounded-bs-sm text-xs p-1`,
1968
- style: {
1969
- ...{
1970
- "--dx-gridCellWidth": `${box?.inlineSize ?? 200}px`
1971
- },
1972
- zIndex: 10,
1973
- insetBlockEnd: `calc(100% - ${box.insetBlockStart}px)`,
1974
- insetInlineStart: box.insetInlineStart,
1975
- inlineSize: box.inlineSize
1976
- }
1977
- }, validationError);
1978
- } finally {
1979
- _effect.f();
1980
- }
1981
- };
1982
-
1983
- // src/components/TableCellEditor/FormCellEditor.tsx
1984
- import { useSignals as _useSignals6 } from "@preact-signals/safe-react/tracking";
1985
- import React6, { useCallback as useCallback3, useEffect as useEffect2, useMemo as useMemo3, useRef } from "react";
1986
- import { getSnapshot as getSnapshot2 } from "@dxos/echo-schema";
1987
- import { invariant as invariant2 } from "@dxos/invariant";
1988
- import { Popover as Popover3 } from "@dxos/react-ui";
1989
- import { Form as Form2 } from "@dxos/react-ui-form";
1990
- import { parseCellIndex, useGridContext as useGridContext3 } from "@dxos/react-ui-grid";
1991
- import { getDeep, setDeep } from "@dxos/util";
1992
- var __dxlog_file3 = "/__w/dxos/dxos/packages/ui/react-ui-table/src/components/TableCellEditor/FormCellEditor.tsx";
1993
- var FormCellEditor = ({ fieldProjection, model, schema, __gridScope }) => {
1994
- var _effect = _useSignals6();
1995
- try {
1996
- const { editing, setEditing } = useGridContext3("ArrayEditor", __gridScope);
1997
- const cellRef = useRef(null);
1998
- useEffect2(() => {
1999
- if (editing && editing.cellElement) {
2000
- cellRef.current = editing.cellElement;
2001
- }
2002
- }, [
2003
- editing?.cellElement
2004
- ]);
2005
- const narrowedSchema = useMemo3(() => {
2006
- if (!schema) {
2007
- return void 0;
2008
- }
2009
- return narrowSchema(schema, [
2010
- fieldProjection.field.path
2011
- ]);
2012
- }, [
2013
- JSON.stringify(schema),
2014
- fieldProjection.field.path
2015
- ]);
2016
- const originalRow = useMemo3(() => {
2017
- if (model && editing) {
2018
- const cell = parseCellIndex(editing.index);
2019
- const row = model.getRowAt(cell.row);
2020
- invariant2(row, void 0, {
2021
- F: __dxlog_file3,
2022
- L: 47,
2023
- S: void 0,
2024
- A: [
2025
- "row",
2026
- ""
2027
- ]
2028
- });
2029
- return row;
2030
- }
2031
- return void 0;
2032
- }, [
2033
- model,
2034
- editing
2035
- ]);
2036
- const formValues = useMemo3(() => {
2037
- if (originalRow) {
2038
- return getSnapshot2(originalRow);
2039
- }
2040
- }, [
2041
- originalRow
2042
- ]);
2043
- const handleSave = useCallback3((values) => {
2044
- const path = fieldProjection.field.path;
2045
- const value = getDeep(values, [
2046
- path
2047
- ]);
2048
- setDeep(originalRow, [
2049
- path
2050
- ], value);
475
+ const cell = parseCellIndex2(editing.index);
476
+ const validationResult = await model.validateCellData(cell, value);
477
+ if (validationResult.valid) {
478
+ setValidationError(null);
479
+ model.setCellData(cell, value);
480
+ onFocus?.();
2051
481
  setEditing(null);
2052
- }, [
2053
- fieldProjection.field.path,
2054
- originalRow
2055
- ]);
2056
- const handleOpenChange = useCallback3((nextOpen) => {
2057
- if (nextOpen === false) {
2058
- setEditing(null);
2059
- }
2060
- }, [
2061
- setEditing
2062
- ]);
2063
- if (!editing) {
2064
- return null;
2065
- }
2066
- return /* @__PURE__ */ React6.createElement(Popover3.Root, {
2067
- open: editing !== null,
2068
- onOpenChange: handleOpenChange
2069
- }, /* @__PURE__ */ React6.createElement(Popover3.VirtualTrigger, {
2070
- virtualRef: cellRef
2071
- }), /* @__PURE__ */ React6.createElement(Popover3.Content, {
2072
- tabIndex: -1,
2073
- classNames: "popover-card-width"
2074
- }, /* @__PURE__ */ React6.createElement(Popover3.Arrow, null), /* @__PURE__ */ React6.createElement(Popover3.Viewport, null, /* @__PURE__ */ React6.createElement(Form2, {
2075
- values: formValues,
2076
- schema: narrowedSchema,
2077
- onSave: handleSave
2078
- }))));
2079
- } finally {
2080
- _effect.f();
2081
- }
2082
- };
2083
-
2084
- // src/components/TableCellEditor/extension.ts
2085
- import { autocompletion, pickedCompletion } from "@codemirror/autocomplete";
2086
- import { EditorView } from "@codemirror/view";
2087
- var completion = ({ debug, onQuery, onMatch }) => {
2088
- return [
2089
- EditorView.theme({
2090
- ".cm-completionDialog": {
2091
- width: "var(--dx-gridCellWidth)"
2092
- }
2093
- }),
2094
- autocompletion({
2095
- icons: false,
2096
- activateOnTyping: true,
2097
- closeOnBlur: !debug,
2098
- override: [
2099
- async (context) => {
2100
- const text = context.state.doc.toString();
2101
- return {
2102
- from: 0,
2103
- options: await onQuery(text)
2104
- };
2105
- }
2106
- ],
2107
- // TODO(burdon): Consts from grid (to line-up with grid).
2108
- tooltipClass: () => "cm-completionDialog !mt-[8px] !-ml-[4px] [&>ul]:!max-h-[264px]",
2109
- optionClass: () => "flex h-[33px] items-center"
2110
- }),
2111
- // Check for accepted completion metadata in the transaction.
2112
- EditorView.updateListener.of((update) => {
2113
- update.transactions.forEach((tr) => {
2114
- const completion2 = tr.annotation(pickedCompletion);
2115
- if (completion2) {
2116
- onMatch(completion2.data);
2117
- }
2118
- });
2119
- })
2120
- ];
2121
- };
2122
-
2123
- // src/components/TableCellEditor/TableCellEditor.tsx
2124
- var __dxlog_file4 = "/__w/dxos/dxos/packages/ui/react-ui-table/src/components/TableCellEditor/TableCellEditor.tsx";
2125
- var newValue = Symbol.for("newValue");
2126
- var createOption = (text) => ({
2127
- [newValue]: true,
2128
- text
2129
- });
2130
- var isCreateOption = (data) => typeof data === "object" && data[newValue];
2131
- var TableValueEditor = ({ model, modals, schema, onEnter, onFocus, onQuery, __gridScope }) => {
2132
- var _effect = _useSignals7();
2133
- try {
2134
- const { editing } = useGridContext4("TableValueEditor", __gridScope);
2135
- const fieldProjection = useMemo4(() => {
2136
- if (!model || !editing) {
2137
- return;
2138
- }
2139
- const { col } = parseCellIndex2(editing.index);
2140
- const field = model.projection.view.fields[col];
2141
- const fieldProjection2 = model.projection.getFieldProjection(field.id);
2142
- invariant3(fieldProjection2, void 0, {
2143
- F: __dxlog_file4,
2144
- L: 74,
2145
- S: void 0,
2146
- A: [
2147
- "fieldProjection",
2148
- ""
2149
- ]
2150
- });
2151
- return fieldProjection2;
2152
- }, [
2153
- model,
2154
- editing
2155
- ]);
2156
- if (fieldProjection?.props.type === TypeEnum4.Array) {
2157
- return /* @__PURE__ */ React7.createElement(FormCellEditor, {
2158
- fieldProjection,
2159
- model,
2160
- schema,
2161
- __gridScope
2162
- });
482
+ onSave?.();
483
+ } else {
484
+ setValidationError(validationResult.error);
485
+ setValidationVariant("error");
2163
486
  }
2164
- return /* @__PURE__ */ React7.createElement(TableCellEditor, {
2165
- model,
2166
- modals,
2167
- onEnter,
2168
- onFocus,
2169
- onQuery,
2170
- __gridScope
2171
- });
2172
- } finally {
2173
- _effect.f();
2174
- }
2175
- };
2176
- var editorSlots = {
2177
- scroller: {
2178
- className: "!plb-[--dx-grid-cell-editor-padding-block]"
2179
- }
2180
- };
2181
- var TableCellEditor = ({ model, modals, onEnter, onFocus, onQuery, __gridScope }) => {
2182
- var _effect = _useSignals7();
2183
- try {
2184
- const { id: gridId, editing, setEditing } = useGridContext4("TableCellEditor", __gridScope);
2185
- const suppressNextBlur = useRef2(false);
2186
- const { themeMode } = useThemeContext();
2187
- const [_validationError, setValidationError] = useState2(null);
2188
- const [_validationVariant, setValidationVariant] = useState2("error");
2189
- const fieldProjection = useMemo4(() => {
2190
- if (!model || !editing) {
2191
- return;
2192
- }
2193
- const { col } = parseCellIndex2(editing.index);
2194
- const field = model.projection.view.fields[col];
2195
- const fieldProjection2 = model.projection.getFieldProjection(field.id);
2196
- invariant3(fieldProjection2, void 0, {
2197
- F: __dxlog_file4,
2198
- L: 119,
2199
- S: void 0,
2200
- A: [
2201
- "fieldProjection",
2202
- ""
2203
- ]
2204
- });
2205
- return fieldProjection2;
2206
- }, [
2207
- model,
2208
- editing
2209
- ]);
2210
- useEffect3(() => {
2211
- if (!model || !editing || !fieldProjection) {
2212
- setValidationError(null);
2213
- return;
2214
- }
2215
- const cell = parseCellIndex2(editing.index);
2216
- const { row, col } = cell;
2217
- if (model.isDraftCell(cell)) {
2218
- const field = model.projection.view.fields[col];
2219
- const hasValidationError = model.hasDraftRowValidationError(row, field.path);
2220
- if (hasValidationError) {
2221
- const draftRows = model.draftRows.value;
2222
- if (row >= 0 && row < draftRows.length) {
2223
- const draftRow = draftRows[row];
2224
- const validationError = draftRow.validationErrors?.find((error) => error.path === field.path);
2225
- if (validationError) {
2226
- setValidationError(validationError.message);
2227
- setValidationVariant("warning");
2228
- }
2229
- }
2230
- } else {
2231
- setValidationError(null);
2232
- }
2233
- } else {
2234
- setValidationError(null);
2235
- }
2236
- }, [
2237
- model,
2238
- editing,
2239
- fieldProjection
2240
- ]);
2241
- const handleEnter = useCallback4(async (value) => {
2242
- if (!model || !editing) {
2243
- return;
2244
- }
2245
- const cell = parseCellIndex2(editing.index);
2246
- const validationResult = await model.validateCellData(cell, value);
2247
- if (validationResult.valid) {
2248
- setValidationError(null);
2249
- model.setCellData(cell, value);
2250
- onEnter?.(cell);
2251
- onFocus?.();
2252
- setEditing(null);
2253
- } else {
2254
- setValidationError(validationResult.error);
2255
- setValidationVariant("error");
2256
- }
2257
- }, [
2258
- model,
2259
- editing,
2260
- onEnter,
2261
- onFocus,
2262
- setEditing
2263
- ]);
2264
- const handleBlur = useCallback4(async (value) => {
2265
- if (!model || !editing) {
2266
- return;
2267
- }
2268
- if (suppressNextBlur.current) {
2269
- suppressNextBlur.current = false;
2270
- }
2271
- }, [
2272
- model,
2273
- editing
2274
- ]);
2275
- const handleClose = useCallback4(async (value, event) => {
2276
- if (!model || !editing || !fieldProjection) {
2277
- return;
2278
- }
487
+ }, [
488
+ model,
489
+ editing,
490
+ onFocus,
491
+ setEditing
492
+ ]);
493
+ const handleBlur = useCallback2(async (value) => {
494
+ if (suppressNextBlur.current) {
495
+ suppressNextBlur.current = false;
496
+ } else if (model && editing) {
2279
497
  const cell = parseCellIndex2(editing.index);
2280
498
  if (value !== void 0) {
2281
499
  const result = await model.validateCellData(cell, value);
@@ -2283,169 +501,109 @@ var TableCellEditor = ({ model, modals, onEnter, onFocus, onQuery, __gridScope }
2283
501
  setValidationError(null);
2284
502
  model.setCellData(cell, value);
2285
503
  setEditing(null);
2286
- onEnter?.(cell);
2287
- if (event && onFocus) {
2288
- onFocus(determineNavigationAxis(event), determineNavigationDelta(event));
2289
- }
2290
- } else {
2291
- setValidationError(result.error);
2292
- setValidationVariant("error");
504
+ onSave?.();
2293
505
  }
2294
- } else {
506
+ }
507
+ }
508
+ }, [
509
+ model,
510
+ editing
511
+ ]);
512
+ const handleClose = useCallback2(async (value, event) => {
513
+ if (!model || !editing || !fieldProjection) {
514
+ return;
515
+ }
516
+ const cell = parseCellIndex2(editing.index);
517
+ if (value !== void 0) {
518
+ const result = await model.validateCellData(cell, value);
519
+ if (result.valid) {
520
+ suppressNextBlur.current = true;
2295
521
  setValidationError(null);
522
+ model.setCellData(cell, value);
2296
523
  setEditing(null);
524
+ onSave?.();
2297
525
  if (event && onFocus) {
2298
- onFocus(determineNavigationAxis(event), determineNavigationDelta(event));
526
+ onFocus(determineNavigationAxis(event), determineNavigationDelta(event), cell);
2299
527
  }
528
+ } else {
529
+ setValidationError(result.error);
530
+ setValidationVariant("error");
2300
531
  }
2301
- }, [
2302
- model,
2303
- editing,
2304
- onFocus,
2305
- onEnter,
2306
- fieldProjection,
2307
- setEditing
2308
- ]);
2309
- const extension = useMemo4(() => {
2310
- if (!fieldProjection) {
2311
- return [];
2312
- }
2313
- const extension2 = [
2314
- editorKeys({
2315
- onClose: handleClose,
2316
- ...editing?.initialContent && {
2317
- onNav: handleClose
2318
- }
2319
- })
2320
- ];
2321
- const format = fieldProjection.props.format;
2322
- if (format === FormatEnum5.SingleSelect || format === FormatEnum5.MultiSelect) {
2323
- extension2.push(createMarkdownExtensions({
2324
- themeMode
2325
- }));
2326
- const options = fieldProjection.props.options || [];
2327
- const mode = format === FormatEnum5.SingleSelect ? "single-select" : "multi-select";
2328
- extension2.push(tagPicker({
2329
- mode,
2330
- keymap: false,
2331
- onSearch: (text, selectedIds) => {
2332
- return options.filter((option) => selectedIds.indexOf(option.id) === -1 && (text.length === 0 || option.title.toLowerCase().includes(text.toLowerCase()))).map((option) => ({
2333
- id: option.id,
2334
- label: option.title,
2335
- hue: option.color
2336
- }));
2337
- },
2338
- onUpdate: (ids) => {
2339
- if (model && editing) {
2340
- if (ids.length === 0) {
2341
- return;
2342
- }
2343
- if (mode === "single-select") {
2344
- void handleEnter(ids[0]);
2345
- } else {
2346
- void handleEnter(ids);
2347
- }
2348
- }
2349
- }
2350
- }));
532
+ } else {
533
+ suppressNextBlur.current = true;
534
+ setValidationError(null);
535
+ setEditing(null);
536
+ onSave?.();
537
+ if (event && onFocus) {
538
+ onFocus(determineNavigationAxis(event), determineNavigationDelta(event));
2351
539
  }
2352
- if (onQuery) {
2353
- switch (fieldProjection.props.format) {
2354
- case FormatEnum5.Ref: {
2355
- extension2.push([
2356
- completion({
2357
- onQuery: (text) => onQuery(fieldProjection, text),
2358
- onMatch: (data) => {
2359
- if (model && editing && modals) {
2360
- if (isCreateOption(data)) {
2361
- const { field, props } = fieldProjection;
2362
- if (props.referenceSchema) {
2363
- suppressNextBlur.current = true;
2364
- modals.openCreateRef(props.referenceSchema, document.querySelector(cellQuery(editing.index, gridId)), {
2365
- [field.referencePath]: data.text
2366
- }, (data2) => {
2367
- void handleEnter(data2);
2368
- });
2369
- }
2370
- } else {
2371
- void handleEnter(data);
2372
- }
2373
- }
2374
- }
2375
- })
2376
- ]);
2377
- break;
2378
- }
540
+ }
541
+ }, [
542
+ model,
543
+ editing,
544
+ onFocus,
545
+ fieldProjection,
546
+ setEditing,
547
+ onSave
548
+ ]);
549
+ const extensions = useMemo2(() => {
550
+ if (!fieldProjection) {
551
+ return [];
552
+ }
553
+ const extensions2 = [
554
+ editorKeys({
555
+ onClose: handleClose,
556
+ ...editing?.initialContent && {
557
+ onNav: handleClose
2379
558
  }
2380
- }
2381
- return extension2;
2382
- }, [
2383
- model,
2384
- modals,
2385
- editing,
2386
- fieldProjection,
2387
- handleClose,
2388
- themeMode
2389
- ]);
2390
- const getCellContent = useCallback4(() => {
2391
- if (model && editing) {
559
+ })
560
+ ];
561
+ if (model && editing) {
562
+ extensions2.push(EditorView.updateListener.of(debounce((update) => {
563
+ const content = update.state.doc.toString();
2392
564
  const cell = parseCellIndex2(editing.index);
2393
- const { col } = cell;
2394
- const field = model.projection.view.fields[col];
2395
- const fieldProjection2 = model.projection.getFieldProjection(field.id);
2396
- if (fieldProjection2?.props.format === FormatEnum5.SingleSelect || fieldProjection2?.props.format === FormatEnum5.MultiSelect) {
2397
- const value2 = model.getCellData(cell);
2398
- if (value2 !== void 0) {
2399
- const options = fieldProjection2.props.options || [];
2400
- if (fieldProjection2.props.format === FormatEnum5.MultiSelect) {
2401
- const tagItems = value2.split(",").map((id) => {
2402
- const option = options.find((o) => o.id === id);
2403
- if (option) {
2404
- return {
2405
- id,
2406
- label: option.title,
2407
- hue: option.color
2408
- };
2409
- }
2410
- return void 0;
2411
- }).filter((item) => item !== void 0);
2412
- return createLinks(tagItems);
2413
- } else {
2414
- const option = options.find((o) => o.id === value2);
2415
- if (option) {
2416
- const tagItem = {
2417
- id: value2,
2418
- label: option.title,
2419
- hue: option.color
2420
- };
2421
- return createLinks([
2422
- tagItem
2423
- ]);
2424
- }
2425
- }
565
+ void model.validateCellData(cell, content).then((result) => {
566
+ if (result.valid) {
567
+ setValidationError(null);
568
+ } else {
569
+ setValidationError(result.error);
570
+ setValidationVariant("error");
2426
571
  }
2427
- return "";
2428
- }
2429
- const value = model.getCellData(cell);
2430
- return value !== void 0 ? String(value) : "";
572
+ });
573
+ }, 10)));
574
+ }
575
+ return extensions2;
576
+ }, [
577
+ model,
578
+ modals,
579
+ editing,
580
+ fieldProjection,
581
+ handleClose
582
+ ]);
583
+ const getCellContent = useCallback2(() => {
584
+ if (model && editing) {
585
+ const cell = parseCellIndex2(editing.index);
586
+ const value = model.getCellData(cell);
587
+ return value !== void 0 ? String(value) : "";
588
+ }
589
+ }, [
590
+ model,
591
+ editing
592
+ ]);
593
+ return /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement(CellValidationMessage, {
594
+ validationError,
595
+ variant: validationVariant,
596
+ __gridScope
597
+ }), /* @__PURE__ */ React3.createElement(GridCellEditor, {
598
+ extensions,
599
+ slots: {
600
+ content: {
601
+ className: "!py-(--dx-grid-cell-editor-padding-block)"
2431
602
  }
2432
- }, [
2433
- model,
2434
- editing
2435
- ]);
2436
- return /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement(CellValidationMessage, {
2437
- validationError: _validationError,
2438
- variant: _validationVariant,
2439
- __gridScope
2440
- }), /* @__PURE__ */ React7.createElement(GridCellEditor, {
2441
- extension,
2442
- getCellContent,
2443
- onBlur: handleBlur,
2444
- slots: editorSlots
2445
- }));
2446
- } finally {
2447
- _effect.f();
2448
- }
603
+ },
604
+ getCellContent,
605
+ onBlur: handleBlur
606
+ }));
2449
607
  };
2450
608
  var determineNavigationAxis = ({ key }) => {
2451
609
  switch (key) {
@@ -2460,554 +618,573 @@ var determineNavigationAxis = ({ key }) => {
2460
618
  return "col";
2461
619
  }
2462
620
  };
2463
- var determineNavigationDelta = ({ key, shift }) => {
2464
- switch (key) {
2465
- case "ArrowUp":
2466
- case "ArrowLeft":
2467
- return -1;
2468
- case "ArrowDown":
2469
- case "ArrowRight":
2470
- return 1;
621
+ var determineNavigationDelta = ({ key, shift }) => {
622
+ switch (key) {
623
+ case "ArrowUp":
624
+ case "ArrowLeft":
625
+ return -1;
626
+ case "ArrowDown":
627
+ case "ArrowRight":
628
+ return 1;
629
+ }
630
+ return shift ? -1 : 1;
631
+ };
632
+
633
+ // src/components/Table/ColumnActionsMenu.tsx
634
+ import { useAtomValue } from "@effect-atom/atom-react";
635
+ import React4 from "react";
636
+ import { DropdownMenu, useTranslation } from "@dxos/react-ui";
637
+ import { translationKey as translationKey2 } from "#translations";
638
+ var ColumnActionsMenu = ({ model, modals }) => {
639
+ const { t } = useTranslation(translationKey2);
640
+ const state = useAtomValue(modals.state);
641
+ if (state?.type !== "column") {
642
+ return null;
643
+ }
644
+ const currentSort = model.getSorting();
645
+ const isCurrentColumnSorted = currentSort?.fieldId === state.fieldId;
646
+ return /* @__PURE__ */ React4.createElement(DropdownMenu.Root, {
647
+ modal: false,
648
+ open: true,
649
+ onOpenChange: modals.close
650
+ }, /* @__PURE__ */ React4.createElement(DropdownMenu.VirtualTrigger, {
651
+ virtualRef: modals.trigger
652
+ }), /* @__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, {
653
+ "data-testid": "column-sort-descending",
654
+ onClick: () => model.setSort(state.fieldId, "desc")
655
+ }, t("column-action-sort-descending.menu")), (!isCurrentColumnSorted || currentSort?.direction === "desc") && /* @__PURE__ */ React4.createElement(DropdownMenu.Item, {
656
+ "data-testid": "column-sort-ascending",
657
+ onClick: () => model.setSort(state.fieldId, "asc")
658
+ }, t("column-action-sort-ascending.menu")), isCurrentColumnSorted && /* @__PURE__ */ React4.createElement(DropdownMenu.Item, {
659
+ "data-testid": "column-clear-sort",
660
+ onClick: () => model.clearSort()
661
+ }, t("column-action-clear-sorting.menu")), model.getColumnCount() > 1 && model.features.schemaEditable && /* @__PURE__ */ React4.createElement(DropdownMenu.Item, {
662
+ "data-testid": "column-delete",
663
+ onClick: () => model.deleteColumn(state.fieldId)
664
+ }, t("column-action-delete.menu")), model.features.schemaEditable && /* @__PURE__ */ React4.createElement(DropdownMenu.Item, {
665
+ "data-testid": "column-settings",
666
+ onClick: () => modals.openColumnSettings()
667
+ }, t("column-action-settings.menu"))), /* @__PURE__ */ React4.createElement(DropdownMenu.Arrow, null))));
668
+ };
669
+
670
+ // src/components/Table/ColumnSettings.tsx
671
+ import { useAtomValue as useAtomValue2 } from "@effect-atom/atom-react";
672
+ import React5, { useCallback as useCallback3, useEffect as useEffect2, useMemo as useMemo3, useState as useState3 } from "react";
673
+ import { Popover as Popover2 } from "@dxos/react-ui";
674
+ import { FieldEditor } from "@dxos/react-ui-form";
675
+ var ColumnSettings = ({ registry, model, modals, onNewColumn }) => {
676
+ const [newField, setNewField] = useState3();
677
+ const state = useAtomValue2(modals.state);
678
+ useEffect2(() => {
679
+ if (state?.type === "columnSettings" && state.mode.type === "create" && model?.projection) {
680
+ setNewField(model.projection.createFieldProjection());
681
+ requestAnimationFrame(() => {
682
+ onNewColumn();
683
+ });
684
+ } else {
685
+ setNewField(void 0);
686
+ }
687
+ }, [
688
+ model?.projection,
689
+ state
690
+ ]);
691
+ const existingField = useMemo3(() => {
692
+ if (state?.type === "columnSettings") {
693
+ const { mode } = state;
694
+ if (mode.type === "edit") {
695
+ return model?.projection?.getFields().find((f) => f.id === mode.fieldId);
696
+ }
697
+ }
698
+ return void 0;
699
+ }, [
700
+ model?.projection,
701
+ state
702
+ ]);
703
+ const field = existingField ?? newField;
704
+ const handleSave = useCallback3(() => {
705
+ modals.close();
706
+ }, [
707
+ modals
708
+ ]);
709
+ const handleCancel = useCallback3(() => {
710
+ if (state?.type === "columnSettings" && state.mode.type === "create" && newField) {
711
+ model?.projection.deleteFieldProjection(newField.id);
712
+ }
713
+ }, [
714
+ model?.projection,
715
+ state,
716
+ newField
717
+ ]);
718
+ if (!model?.projection || !field) {
719
+ return null;
720
+ }
721
+ return /* @__PURE__ */ React5.createElement(Popover2.Root, {
722
+ modal: false,
723
+ open: state?.type === "columnSettings"
724
+ }, /* @__PURE__ */ React5.createElement(Popover2.VirtualTrigger, {
725
+ virtualRef: modals.trigger
726
+ }), /* @__PURE__ */ React5.createElement(Popover2.Portal, null, /* @__PURE__ */ React5.createElement(Popover2.Content, {
727
+ classNames: "md:w-64"
728
+ }, /* @__PURE__ */ React5.createElement(Popover2.Viewport, null, /* @__PURE__ */ React5.createElement(FieldEditor, {
729
+ projection: model.projection,
730
+ field,
731
+ registry,
732
+ onSave: handleSave,
733
+ onCancel: handleCancel
734
+ })), /* @__PURE__ */ React5.createElement(Popover2.Arrow, null))));
735
+ };
736
+
737
+ // src/components/Table/RowActionsMenu.tsx
738
+ import { useAtomValue as useAtomValue3 } from "@effect-atom/atom-react";
739
+ import React6 from "react";
740
+ import { DropdownMenu as DropdownMenu2, toLocalizedString, useTranslation as useTranslation2 } from "@dxos/react-ui";
741
+ import { translationKey as translationKey3 } from "#translations";
742
+ var RowActionsMenu = ({ model, modals }) => {
743
+ const { t } = useTranslation2(translationKey3);
744
+ const hasSelection = model.selection.hasSelection;
745
+ const state = useAtomValue3(modals.state);
746
+ if (state?.type !== "row") {
747
+ return null;
748
+ }
749
+ return /* @__PURE__ */ React6.createElement(DropdownMenu2.Root, {
750
+ modal: false,
751
+ open: true,
752
+ onOpenChange: modals.close
753
+ }, /* @__PURE__ */ React6.createElement(DropdownMenu2.VirtualTrigger, {
754
+ virtualRef: modals.trigger
755
+ }), /* @__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, {
756
+ key: action.id,
757
+ "data-testid": `row-action-${action.id}`,
758
+ onClick: () => {
759
+ modals.close();
760
+ model.handleRowAction(action.id, state.rowIndex);
761
+ }
762
+ }, toLocalizedString(action.label, t)))), /* @__PURE__ */ React6.createElement(DropdownMenu2.Separator, null)), model.features.dataEditable !== false && /* @__PURE__ */ React6.createElement(DropdownMenu2.Item, {
763
+ "data-testid": "row-menu-delete",
764
+ onClick: () => model.deleteRow(state.rowIndex)
765
+ }, t(hasSelection ? "bulk delete row label" : "delete row label"))), /* @__PURE__ */ React6.createElement(DropdownMenu2.Arrow, null)));
766
+ };
767
+
768
+ // src/components/Table/TableContent.tsx
769
+ var __dxlog_file3 = "/__w/dxos/dxos/packages/ui/react-ui-table/src/components/Table/TableContent.tsx";
770
+ var columnDefault = {
771
+ grid: {
772
+ minSize: 80,
773
+ maxSize: 640
2471
774
  }
2472
- return shift ? -1 : 1;
2473
775
  };
2474
-
2475
- // src/components/Table/Table.tsx
2476
- var __dxlog_file5 = "/__w/dxos/dxos/packages/ui/react-ui-table/src/components/Table/Table.tsx";
2477
- var TableRoot = ({ children, role }) => {
2478
- var _effect = _useSignals8();
2479
- try {
2480
- return /* @__PURE__ */ React8.createElement("div", {
2481
- role: "none",
2482
- className: mx2("relative !border-separator", role === "section" ? "attention-surface overflow-hidden [&_.dx-grid]:max-is-[--dx-grid-content-inline-size]" : "flex flex-col [&_.dx-grid]:grow [&_.dx-grid]:max-is-[--dx-grid-content-inline-size] [&_.dx-grid]:bs-0 [&_.dx-grid]:max-bs-[--dx-grid-content-block-size]")
2483
- }, children);
2484
- } finally {
2485
- _effect.f();
776
+ var rowDefault = {
777
+ frozenRowsStart: {
778
+ readonly: true,
779
+ focusUnfurl: false
2486
780
  }
2487
781
  };
2488
- var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttention, schema, client, onRowClick }, forwardedRef) => {
2489
- var _effect = _useSignals8();
2490
- try {
2491
- const [dxGrid, setDxGrid] = useState3(null);
2492
- const { hasAttention } = useAttention(model?.id ?? "table");
2493
- const { t } = useTranslation3(translationKey);
2494
- const modals = useMemo5(() => new ModalController(), []);
2495
- const draftRowCount = model?.getDraftRowCount() ?? 0;
2496
- const frozen = useMemo5(() => {
2497
- const noActionColumn = model?.features.dataEditable === false && model?.features.schemaEditable === false && model.rowActions.length === 0;
2498
- return {
2499
- frozenRowsStart: 1,
2500
- frozenRowsEnd: draftRowCount,
2501
- frozenColsStart: model?.features.selection.enabled ? 1 : 0,
2502
- frozenColsEnd: noActionColumn ? 0 : 1
2503
- };
2504
- }, [
2505
- model,
2506
- draftRowCount
2507
- ]);
2508
- const getCells = useCallback5((range, plane) => presentation?.getCells(range, plane) ?? {}, [
2509
- presentation
2510
- ]);
2511
- useEffect4(() => {
2512
- if (!presentation || !dxGrid) {
2513
- return;
2514
- }
2515
- dxGrid.getCells = getCells;
2516
- }, [
2517
- presentation,
2518
- dxGrid,
2519
- getCells
2520
- ]);
2521
- useImperativeHandle(forwardedRef, () => {
2522
- if (!presentation || !dxGrid) {
2523
- return {};
782
+ var emptyColumnMeta = Atom2.make({
783
+ grid: {}
784
+ });
785
+ var emptyRowsAtom = Atom2.make([]);
786
+ var emptyCellUpdateAtom = Atom2.make(0);
787
+ var TABLE_CONTENT_NAME = "Table.Content";
788
+ var TableContent = composable(({ schema, model, presentation, ignoreAttention, onCreate, onRowClick, testId, ...props }, forwardedRef) => {
789
+ const registry = useContext(RegistryContext);
790
+ const [dxGrid, setDxGrid] = useState4(null);
791
+ const { hasAttention } = useAttention(model?.id ?? "table");
792
+ const modals = useMemo4(() => new ModalController(registry), [
793
+ registry
794
+ ]);
795
+ const columnMeta = useAtomValue4(model?.columnMeta ?? emptyColumnMeta);
796
+ const rows = useAtomValue4(model?.rows ?? emptyRowsAtom);
797
+ const cellUpdateCounter = useAtomValue4(model?.cellUpdate ?? emptyCellUpdateAtom);
798
+ const columnCount = Object.keys(columnMeta.grid).length || model?.projection.getFields().length || 0;
799
+ const draftRowCount = model?.getDraftRowCount() ?? 0;
800
+ const frozen = useMemo4(() => {
801
+ const noActionColumn = model?.features.dataEditable === false && model?.features.schemaEditable === false && model.rowActions.length === 0;
802
+ return {
803
+ frozenRowsStart: 1,
804
+ frozenRowsEnd: Math.max(1, draftRowCount),
805
+ frozenColsStart: model?.features.selection.enabled ? 1 : 0,
806
+ frozenColsEnd: noActionColumn ? 0 : 1
807
+ };
808
+ }, [
809
+ model,
810
+ draftRowCount
811
+ ]);
812
+ const getCells = useCallback4((range, plane) => presentation?.getCells(range, plane) ?? {}, [
813
+ presentation
814
+ ]);
815
+ useEffect3(() => {
816
+ if (!dxGrid || !presentation) {
817
+ return;
818
+ }
819
+ dxGrid.getCells = getCells;
820
+ }, [
821
+ dxGrid,
822
+ presentation,
823
+ getCells
824
+ ]);
825
+ useEffect3(() => {
826
+ if (!dxGrid) {
827
+ return;
828
+ }
829
+ dxGrid.updateCells(true);
830
+ dxGrid.requestUpdate();
831
+ }, [
832
+ dxGrid,
833
+ rows,
834
+ cellUpdateCounter
835
+ ]);
836
+ const handleInsertRowResult = useCallback4((insertResult) => {
837
+ if (insertResult === "draft") {
838
+ requestAnimationFrame(() => {
839
+ dxGrid?.setFocus({
840
+ plane: "frozenRowsEnd",
841
+ col: 0,
842
+ row: 0
843
+ });
844
+ dxGrid?.refocus();
845
+ });
846
+ } else {
847
+ requestAnimationFrame(() => {
848
+ dxGrid?.setFocus({
849
+ plane: "grid",
850
+ col: 0,
851
+ row: model ? model.getRowCount() - 1 : 0
852
+ });
853
+ dxGrid?.refocus();
854
+ });
855
+ }
856
+ }, [
857
+ model,
858
+ dxGrid
859
+ ]);
860
+ const handleSave = useCallback4(() => {
861
+ dxGrid?.updateCells(true);
862
+ dxGrid?.requestUpdate();
863
+ }, [
864
+ dxGrid
865
+ ]);
866
+ const handleSaveDraftRow = useCallback4((rowIndex = 0) => {
867
+ if (model && dxGrid) {
868
+ const didCommitSuccessfully = model.commitDraftRow(rowIndex);
869
+ if (didCommitSuccessfully) {
870
+ requestAnimationFrame(() => {
871
+ dxGrid.scrollToEndRow();
872
+ });
2524
873
  }
2525
- return {
2526
- update: (cell) => {
2527
- if (cell) {
2528
- dxGrid.updateIfWithinBounds(cell);
2529
- } else {
2530
- dxGrid.updateCells(true);
2531
- dxGrid.requestUpdate();
2532
- }
2533
- }
2534
- };
2535
- }, [
2536
- presentation,
2537
- dxGrid
2538
- ]);
2539
- const handleGridClick = useCallback5((event) => {
2540
- const rowIndex = safeParseInt(event.target.ariaRowIndex ?? "");
2541
- if (rowIndex != null) {
2542
- if (onRowClick) {
874
+ }
875
+ }, [
876
+ model,
877
+ dxGrid
878
+ ]);
879
+ const handleGridClick = useCallback4((event) => {
880
+ const cell = closestCell(event.target);
881
+ if (cell) {
882
+ const { row: rowIndex, plane } = cell;
883
+ if (onRowClick) {
884
+ if (plane === "grid") {
2543
885
  const row = model?.getRowAt(rowIndex);
2544
886
  row && onRowClick(row);
2545
- }
2546
- if (model?.features.selection.enabled && model?.selection.selectionMode === "single") {
2547
- model.selection.toggleSelectionForRowIndex(rowIndex);
887
+ } else {
888
+ onRowClick(cell);
2548
889
  }
2549
890
  }
2550
- if (!modals) {
891
+ if (model?.features.selection.enabled && model?.selection.selectionMode === "single") {
892
+ model.selection.toggleSelectionForRowIndex(rowIndex);
893
+ }
894
+ }
895
+ if (!modals) {
896
+ return;
897
+ }
898
+ const buttonSelector = Object.values(tableButtons).map((button) => `button[${button.attr}]`).join(",");
899
+ const tableButtonElement = event.target.closest(buttonSelector);
900
+ if (tableButtonElement) {
901
+ const button = Object.values(tableButtons).find((btn) => tableButtonElement.hasAttribute(btn.attr));
902
+ if (!button) {
2551
903
  return;
2552
904
  }
2553
- const buttonSelector = Object.values(tableButtons).map((button) => `button[${button.attr}]`).join(",");
2554
- const tableButtonElement = event.target.closest(buttonSelector);
2555
- if (tableButtonElement) {
2556
- const button = Object.values(tableButtons).find((btn) => tableButtonElement.hasAttribute(btn.attr));
2557
- if (!button) {
2558
- return;
905
+ modals.setTrigger(tableButtonElement);
906
+ const data = button.getData(tableButtonElement);
907
+ switch (data.type) {
908
+ case "rowMenu": {
909
+ modals.showRowMenu(data.rowIndex);
910
+ break;
2559
911
  }
2560
- modals.setTrigger(tableButtonElement);
2561
- const data = button.getData(tableButtonElement);
2562
- switch (data.type) {
2563
- case "rowMenu": {
2564
- modals.showRowMenu(data.rowIndex);
2565
- break;
2566
- }
2567
- case "columnSettings": {
2568
- modals.showColumnMenu(data.fieldId);
2569
- break;
2570
- }
2571
- case "newColumn": {
2572
- modals.showColumnCreator();
2573
- break;
2574
- }
2575
- case "sort": {
2576
- model?.sorting?.toggleSort(data.fieldId);
2577
- break;
2578
- }
2579
- case "saveDraftRow": {
2580
- if (model) {
2581
- const didCommitSuccessfully = model.commitDraftRow(data.rowIndex);
2582
- if (dxGrid && didCommitSuccessfully) {
2583
- requestAnimationFrame(() => {
2584
- dxGrid.scrollToEndRow();
2585
- });
2586
- }
2587
- }
2588
- break;
2589
- }
912
+ case "columnSettings": {
913
+ modals.showColumnMenu(data.fieldId);
914
+ break;
2590
915
  }
2591
- return;
2592
- }
2593
- const controlSelector = Object.values(tableControls).map((control) => `input[${control.attr}]`).join(",");
2594
- const controlElement = event.target.closest(controlSelector);
2595
- if (controlElement) {
2596
- const control = Object.values(tableControls).find((ctrl) => controlElement.hasAttribute(ctrl.attr));
2597
- if (!control) {
2598
- return;
916
+ case "newColumn": {
917
+ modals.showColumnCreator();
918
+ break;
2599
919
  }
2600
- const data = control.getData(controlElement);
2601
- switch (data.type) {
2602
- case "checkbox": {
2603
- if (data.header) {
2604
- model?.selection.setSelection(model.selection.allRowsSeleted.value ? "none" : "all");
2605
- } else {
2606
- model?.selection.toggleSelectionForRowIndex(data.rowIndex);
2607
- }
2608
- break;
2609
- }
2610
- case "switch": {
2611
- if (model) {
2612
- model.updateCellData({
2613
- row: data.rowIndex,
2614
- col: data.colIndex
2615
- }, (value) => !value);
2616
- }
2617
- break;
2618
- }
920
+ case "sort": {
921
+ model?.toggleSort(data.fieldId);
922
+ break;
2619
923
  }
2620
- }
2621
- }, [
2622
- model,
2623
- modals,
2624
- dxGrid
2625
- ]);
2626
- const handleFocus = useCallback5((increment, delta) => {
2627
- if (dxGrid) {
2628
- dxGrid.refocus(increment, delta);
2629
- }
2630
- }, [
2631
- dxGrid
2632
- ]);
2633
- const handleEnter = useCallback5((cell) => {
2634
- if (!model?.features.dataEditable) {
2635
- return;
2636
- }
2637
- if (model && cell.row === model.getRowCount() - 1) {
2638
- model.insertRow();
2639
- if (dxGrid) {
2640
- requestAnimationFrame(() => {
2641
- dxGrid?.scrollToRow(cell.row + 1);
2642
- dxGrid?.refocus("row", 1);
2643
- });
924
+ case "saveDraftRow": {
925
+ handleSaveDraftRow();
926
+ break;
2644
927
  }
2645
928
  }
2646
- }, [
2647
- model,
2648
- dxGrid
2649
- ]);
2650
- const handleKeyDown = useCallback5((event) => {
2651
- if (!model?.features.dataEditable) {
2652
- return;
2653
- }
2654
- const cell = closestCell(event.target);
2655
- if (!model || !cell) {
929
+ return;
930
+ }
931
+ const controlSelector = Object.values(tableControls).map((control) => `input[${control.attr}]`).join(",");
932
+ const controlElement = event.target.closest(controlSelector);
933
+ if (controlElement) {
934
+ const control = Object.values(tableControls).find((ctrl) => controlElement.hasAttribute(ctrl.attr));
935
+ if (!control) {
2656
936
  return;
2657
937
  }
2658
- switch (event.key) {
2659
- case "Backspace":
2660
- case "Delete": {
2661
- model.setCellData(cell, void 0);
938
+ const data = control.getData(controlElement);
939
+ switch (data.type) {
940
+ case "checkbox": {
941
+ if (data.header) {
942
+ model?.selection.setSelection(model.selection.allRowsSelected ? "none" : "all");
943
+ } else {
944
+ model?.selection.toggleSelectionForRowIndex(data.rowIndex);
945
+ }
946
+ break;
947
+ }
948
+ case "switch": {
949
+ if (model) {
950
+ model.updateCellData({
951
+ row: data.rowIndex,
952
+ col: data.colIndex
953
+ }, (value) => !value);
954
+ }
2662
955
  break;
2663
956
  }
2664
957
  }
2665
- }, [
2666
- model
2667
- ]);
2668
- const handleAxisResize = useCallback5((event) => {
2669
- if (event.axis === "col") {
2670
- const columnIndex = parseInt(event.index, 10);
2671
- model?.setColumnWidth(columnIndex, event.size);
2672
- }
2673
- }, [
2674
- model
2675
- ]);
2676
- const handleWheel = useCallback5((event) => {
2677
- if (!ignoreAttention && !hasAttention) {
2678
- event.stopPropagation();
2679
- }
2680
- }, [
2681
- hasAttention,
2682
- ignoreAttention
2683
- ]);
2684
- const handleNewColumn = useCallback5(() => {
2685
- const columns = model?.getColumnCount();
2686
- if (dxGrid && columns) {
2687
- dxGrid.scrollToColumn(columns - 1);
958
+ }
959
+ }, [
960
+ model,
961
+ modals,
962
+ dxGrid
963
+ ]);
964
+ const handleFocus = useCallback4((increment, delta, cell) => {
965
+ if (dxGrid && model) {
966
+ if (cell?.plane === "grid" && cell?.row >= model.getRowCount() - 1 && increment !== "col") {
967
+ handleInsertRowResult(draftRowCount < 1 ? model.insertRow() : "final");
968
+ } else if (cell?.plane === "frozenRowsEnd" && increment === "row") {
969
+ handleSaveDraftRow(cell.row);
970
+ handleInsertRowResult(model.insertRow());
971
+ } else {
972
+ dxGrid.refocus(increment, delta);
2688
973
  }
2689
- }, [
2690
- model,
2691
- dxGrid
2692
- ]);
2693
- const handleQuery = useCallback5(async ({ field, props }, text) => {
2694
- if (model && props.referenceSchema && field.referencePath) {
2695
- const space = model.space;
2696
- invariant4(space, void 0, {
2697
- F: __dxlog_file5,
2698
- L: 325,
2699
- S: void 0,
2700
- A: [
2701
- "space",
2702
- ""
2703
- ]
2704
- });
2705
- let schema2;
2706
- if (client) {
2707
- schema2 = client.graph.schemaRegistry.getSchema(props.referenceSchema);
2708
- }
2709
- if (!schema2) {
2710
- schema2 = space.db.schemaRegistry.getSchema(props.referenceSchema);
974
+ }
975
+ }, [
976
+ dxGrid,
977
+ model
978
+ ]);
979
+ const handleKeyDown = useCallback4((event) => {
980
+ if (!model?.features.dataEditable) {
981
+ return;
982
+ }
983
+ const cell = closestCell(event.target);
984
+ if (!model || !cell) {
985
+ return;
986
+ }
987
+ if (event.metaKey || event.ctrlKey) {
988
+ switch (event.key) {
989
+ case "c": {
990
+ try {
991
+ const cellData = model.getCellData(cell);
992
+ const textContent = cellData?.toString() ?? "";
993
+ void navigator.clipboard.writeText(textContent);
994
+ event.preventDefault();
995
+ } catch (error) {
996
+ log.catch(error, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 262, S: void 0 });
997
+ }
998
+ break;
2711
999
  }
2712
- if (schema2) {
2713
- const { objects } = await space.db.query(Filter.type(schema2)).run();
2714
- const options = objects.map((obj) => {
2715
- const value = getValue4(obj, field.referencePath);
2716
- if (!value || typeof value !== "string") {
2717
- return void 0;
2718
- }
2719
- return {
2720
- label: value,
2721
- data: obj
2722
- };
2723
- }).filter(isNotFalsy);
2724
- return [
2725
- ...options,
2726
- {
2727
- label: t("create new object label", {
2728
- text
2729
- }),
2730
- data: createOption(text)
1000
+ case "v": {
1001
+ event.preventDefault();
1002
+ void navigator.clipboard.readText().then((clipboardText) => {
1003
+ try {
1004
+ model.setCellData(cell, String2.trim(clipboardText).replace(/[\n\r]+/, " "));
1005
+ handleSave();
1006
+ } catch {
1007
+ const cellElement = event.target.closest('[data-dx-grid-action="cell"]');
1008
+ if (cellElement) {
1009
+ const rect = cellElement.getBoundingClientRect();
1010
+ const editRequest = new DxEditRequest({
1011
+ cellIndex: `${cell.plane},${cell.col},${cell.row}`,
1012
+ cellBox: {
1013
+ insetInlineStart: rect.left,
1014
+ insetBlockStart: rect.top,
1015
+ inlineSize: rect.width,
1016
+ blockSize: rect.height
1017
+ },
1018
+ cellElement,
1019
+ initialContent: clipboardText
1020
+ });
1021
+ cellElement.dispatchEvent(editRequest);
1022
+ }
2731
1023
  }
2732
- ];
1024
+ });
1025
+ break;
2733
1026
  }
2734
1027
  }
2735
- return [];
2736
- }, [
2737
- model,
2738
- client,
2739
- t
2740
- ]);
2741
- if (!model || !modals) {
2742
- return /* @__PURE__ */ React8.createElement("span", {
2743
- role: "none",
2744
- className: "attention-surface"
2745
- });
2746
1028
  }
2747
- return /* @__PURE__ */ React8.createElement(Grid.Root, {
2748
- id: model.id ?? "table-grid"
2749
- }, /* @__PURE__ */ React8.createElement(TableValueEditor, {
2750
- model,
2751
- modals,
2752
- schema,
2753
- onEnter: handleEnter,
2754
- onFocus: handleFocus,
2755
- onQuery: handleQuery
2756
- }), /* @__PURE__ */ React8.createElement(Grid.Content, {
2757
- className: mx2("[--dx-grid-base:var(--baseSurface)]", gridSeparatorInlineEnd, gridSeparatorBlockEnd),
2758
- frozen,
2759
- columns: model.columnMeta.value,
2760
- limitRows: model.getRowCount() ?? 0,
2761
- limitColumns: model.view?.fields?.length ?? 0,
2762
- overscroll: "trap",
2763
- onAxisResize: handleAxisResize,
2764
- onClick: handleGridClick,
2765
- onKeyDown: handleKeyDown,
2766
- onWheelCapture: handleWheel,
2767
- ref: setDxGrid
2768
- }), /* @__PURE__ */ React8.createElement(RowActionsMenu, {
2769
- model,
2770
- modals
2771
- }), /* @__PURE__ */ React8.createElement(ColumnActionsMenu, {
2772
- model,
2773
- modals
2774
- }), /* @__PURE__ */ React8.createElement(ColumnSettings, {
2775
- model,
2776
- modals,
2777
- onNewColumn: handleNewColumn
2778
- }), /* @__PURE__ */ React8.createElement(CreateRefPanel, {
2779
- model,
2780
- modals
2781
- }));
2782
- } finally {
2783
- _effect.f();
2784
- }
2785
- });
2786
- var Table = {
2787
- Root: TableRoot,
2788
- Main: TableMain
2789
- };
2790
-
2791
- // src/components/Table/DynamicTable.tsx
2792
- import { useSignals as _useSignals10 } from "@preact-signals/safe-react/tracking";
2793
- import React9, { useRef as useRef3, useMemo as useMemo6, useCallback as useCallback7 } from "react";
2794
- import { useDefaultValue } from "@dxos/react-ui";
2795
- import { mx as mx3 } from "@dxos/react-ui-theme";
2796
-
2797
- // src/hooks/useTableModel.ts
2798
- import { useSignals as _useSignals9 } from "@preact-signals/safe-react/tracking";
2799
- import { effect as effect3 } from "@preact/signals-core";
2800
- import orderBy2 from "lodash.orderby";
2801
- import { useEffect as useEffect5, useState as useState4 } from "react";
2802
- import { fullyQualifiedId, getSpace } from "@dxos/react-client/echo";
2803
- import { useSelectionActions } from "@dxos/react-ui-attention";
2804
- import { isNonNullable } from "@dxos/util";
2805
- var useTableModel = ({ table, projection, rows, rowActions, features, onSelectionChanged, onRowAction, ...props }) => {
2806
- var _effect = _useSignals9();
2807
- try {
2808
- const [model, setModel] = useState4();
2809
- useEffect5(() => {
2810
- if (!table || !projection) {
2811
- return;
2812
- }
2813
- let model2;
2814
- const t = setTimeout(async () => {
2815
- model2 = new TableModel({
2816
- id: fullyQualifiedId(table),
2817
- space: getSpace(table),
2818
- view: table.view?.target,
2819
- projection,
2820
- features,
2821
- rowActions,
2822
- onRowAction,
2823
- ...props
2824
- });
2825
- await model2.open();
2826
- setModel(model2);
2827
- });
2828
- return () => {
2829
- clearTimeout(t);
2830
- void model2?.close();
2831
- };
2832
- }, [
2833
- table,
2834
- projection,
2835
- table?.view?.target,
2836
- features,
2837
- rowActions
2838
- ]);
2839
- useEffect5(() => {
2840
- if (rows) {
2841
- const sortedRows = orderBy2(rows, [
2842
- (row) => String(row.id)
2843
- ], [
2844
- "asc"
2845
- ]);
2846
- model?.setRows(sortedRows);
2847
- }
2848
- }, [
2849
- model,
2850
- rows
2851
- ]);
2852
- const { multiSelect, clear } = useSelectionActions([
2853
- table?.id,
2854
- table?.view?.target?.query.typename
2855
- ].filter(isNonNullable));
2856
- useEffect5(() => {
2857
- if (!model) {
2858
- return;
2859
- }
2860
- const unsubscribe = effect3(() => {
2861
- const selectedItems = [
2862
- ...model.selection.selection.value
2863
- ];
2864
- multiSelect(selectedItems);
2865
- onSelectionChanged?.(selectedItems);
2866
- });
2867
- return () => {
2868
- clear();
2869
- unsubscribe();
2870
- };
2871
- }, [
2872
- model,
2873
- onSelectionChanged
2874
- ]);
2875
- return model;
2876
- } finally {
2877
- _effect.f();
2878
- }
2879
- };
2880
-
2881
- // src/hooks/useAddRow.ts
2882
- import { useCallback as useCallback6 } from "react";
2883
- import { Obj as Obj3 } from "@dxos/echo";
2884
- var useAddRow = ({ space, schema }) => {
2885
- return useCallback6((data) => {
2886
- if (space && schema) {
2887
- try {
2888
- space.db.add(Obj3.make(schema, data ?? {}));
2889
- return true;
2890
- } catch (error) {
2891
- return false;
1029
+ switch (event.key) {
1030
+ case "Backspace":
1031
+ case "Delete": {
1032
+ try {
1033
+ model.setCellData(cell, void 0);
1034
+ event.preventDefault();
1035
+ handleSave();
1036
+ } catch {
1037
+ }
1038
+ break;
2892
1039
  }
2893
1040
  }
2894
- return false;
2895
1041
  }, [
2896
- space,
2897
- schema
1042
+ model,
1043
+ dxGrid,
1044
+ handleSave
2898
1045
  ]);
2899
- };
2900
-
2901
- // src/components/Table/DynamicTable.tsx
2902
- var DynamicTable = ({ classNames, name = "example.com/dynamic-table", rows, properties, jsonSchema, schema, rowActions, onRowClick, onRowAction, ...props }) => {
2903
- var _effect = _useSignals10();
2904
- try {
2905
- const { table, projection } = useMemo6(() => {
2906
- const props2 = getBaseSchema({
2907
- typename: name,
2908
- properties,
2909
- jsonSchema,
2910
- schema
2911
- });
2912
- return makeDynamicTable({
2913
- ...props2,
2914
- properties
2915
- });
2916
- }, [
2917
- name,
2918
- properties,
2919
- schema,
2920
- jsonSchema
2921
- ]);
2922
- const tableRef = useRef3(null);
2923
- const handleCellUpdate = useCallback7((cell) => {
2924
- tableRef.current?.update?.(cell);
2925
- }, []);
2926
- const handleRowOrderChange = useCallback7(() => {
2927
- tableRef.current?.update?.();
2928
- }, []);
2929
- const features = useDefaultValue(props.features, () => ({
2930
- selection: {
2931
- enabled: false
1046
+ const handleAxisResize = useCallback4((event) => {
1047
+ if (event.axis === "col") {
1048
+ const columnIndex = parseInt(event.index, 10);
1049
+ model?.setColumnWidth(columnIndex, event.size);
1050
+ }
1051
+ }, [
1052
+ model
1053
+ ]);
1054
+ const handleWheel = useCallback4((event) => {
1055
+ if (!ignoreAttention && !hasAttention) {
1056
+ event.stopPropagation();
1057
+ }
1058
+ }, [
1059
+ hasAttention,
1060
+ ignoreAttention
1061
+ ]);
1062
+ const handleNewColumn = useCallback4(() => {
1063
+ const columns = model?.getColumnCount();
1064
+ if (dxGrid && columns) {
1065
+ dxGrid.scrollToColumn(columns - 1);
1066
+ }
1067
+ }, [
1068
+ model,
1069
+ dxGrid
1070
+ ]);
1071
+ const { controllerRef } = useTableContext(TABLE_CONTENT_NAME);
1072
+ controllerRef.current = useMemo4(() => {
1073
+ if (!dxGrid || !presentation) {
1074
+ return {};
1075
+ }
1076
+ return {
1077
+ update: (cell) => {
1078
+ if (cell) {
1079
+ dxGrid.updateIfWithinBounds(cell, true);
1080
+ } else {
1081
+ dxGrid.updateCells(true);
1082
+ dxGrid.requestUpdate();
1083
+ }
2932
1084
  },
2933
- dataEditable: false
2934
- }));
2935
- const model = useTableModel({
2936
- table,
2937
- rows,
2938
- projection,
2939
- features,
2940
- rowActions,
2941
- onCellUpdate: handleCellUpdate,
2942
- onRowOrderChange: handleRowOrderChange,
2943
- onRowAction
2944
- });
2945
- const presentation = useMemo6(() => {
2946
- if (model) {
2947
- return new TablePresentation(model);
2948
- }
2949
- }, [
2950
- model
2951
- ]);
2952
- return /* @__PURE__ */ React9.createElement("div", {
2953
- role: "none",
2954
- className: mx3("is-full bs-full grow grid", classNames)
2955
- }, /* @__PURE__ */ React9.createElement("div", {
1085
+ handleInsertRowResult
1086
+ };
1087
+ }, [
1088
+ dxGrid,
1089
+ model,
1090
+ presentation
1091
+ ]);
1092
+ if (!model || !modals) {
1093
+ return /* @__PURE__ */ React7.createElement("span", {
2956
1094
  role: "none",
2957
- className: "grid min-bs-0 overflow-hidden"
2958
- }, /* @__PURE__ */ React9.createElement(Table.Root, null, /* @__PURE__ */ React9.createElement(Table.Main, {
2959
- ref: tableRef,
2960
- model,
2961
- presentation,
2962
- ignoreAttention: true,
2963
- onRowClick
2964
- }))));
2965
- } finally {
2966
- _effect.f();
1095
+ className: "dx-attention-surface"
1096
+ });
2967
1097
  }
2968
- };
1098
+ return /* @__PURE__ */ React7.createElement("div", {
1099
+ ...composableProps(props, {
1100
+ classNames: "dx-container relative"
1101
+ }),
1102
+ ref: forwardedRef
1103
+ }, /* @__PURE__ */ React7.createElement(Grid.Root, {
1104
+ id: model.id ?? "table-grid"
1105
+ }, /* @__PURE__ */ React7.createElement(TableValueEditor, {
1106
+ model,
1107
+ modals,
1108
+ schema,
1109
+ onFocus: handleFocus,
1110
+ onSave: handleSave,
1111
+ onCreate
1112
+ }), /* @__PURE__ */ React7.createElement(Grid.Content, {
1113
+ className: mx("[--dx-grid-base:var(--base-surface)]", gridSeparatorInlineEnd, gridSeparatorBlockEnd),
1114
+ frozen,
1115
+ columns: columnMeta,
1116
+ columnDefault,
1117
+ rowDefault,
1118
+ limitRows: rows.length,
1119
+ limitColumns: columnCount,
1120
+ overscroll: "trap",
1121
+ onAxisResize: handleAxisResize,
1122
+ onClick: handleGridClick,
1123
+ onKeyDownCapture: handleKeyDown,
1124
+ onWheelCapture: handleWheel,
1125
+ ...testId && {
1126
+ "data-testid": testId
1127
+ },
1128
+ ref: setDxGrid
1129
+ }), /* @__PURE__ */ React7.createElement(RowActionsMenu, {
1130
+ model,
1131
+ modals
1132
+ }), /* @__PURE__ */ React7.createElement(ColumnActionsMenu, {
1133
+ model,
1134
+ modals
1135
+ }), /* @__PURE__ */ React7.createElement(ColumnSettings, {
1136
+ model,
1137
+ modals,
1138
+ onNewColumn: handleNewColumn
1139
+ })));
1140
+ });
1141
+ TableContent.displayName = TABLE_CONTENT_NAME;
2969
1142
 
2970
- // src/components/TableToolbar/TableToolbar.tsx
2971
- import { useSignals as _useSignals11 } from "@preact-signals/safe-react/tracking";
2972
- import { Rx } from "@effect-rx/rx-react";
2973
- import React10, { useMemo as useMemo7 } from "react";
2974
- import { live as live3 } from "@dxos/live-object";
2975
- import { createGapSeparator, createMenuAction, MenuProvider, ToolbarMenu, useMenuActions } from "@dxos/react-ui-menu";
2976
- import { rxFromSignal } from "@dxos/react-ui-menu";
2977
- var createTableToolbarActions = ({ state, onAdd, onSave, customActions }) => Rx.make((get) => {
2978
- const add = createMenuAction("add-row", onAdd, {
2979
- type: "add-row",
2980
- icon: "ph--plus--regular",
2981
- label: [
2982
- "add row",
2983
- {
2984
- ns: translationKey
2985
- }
2986
- ],
2987
- testId: "table.toolbar.add-row"
2988
- });
2989
- const save = createMenuAction("save-view", onSave, {
2990
- type: "save-view",
2991
- icon: "ph--floppy-disk--regular",
2992
- label: [
2993
- "save view label",
2994
- {
2995
- ns: translationKey
2996
- }
2997
- ],
2998
- testId: "table.toolbar.save-view",
2999
- iconOnly: false,
3000
- hidden: get(rxFromSignal(() => !state.viewDirty))
3001
- });
1143
+ // src/components/Table/TableToolbar.tsx
1144
+ import { Atom as Atom3, RegistryContext as RegistryContext2 } from "@effect-atom/atom-react";
1145
+ import React8, { useContext as useContext2, useEffect as useEffect4, useMemo as useMemo5 } from "react";
1146
+ import { Menu, createGapSeparator, createMenuAction, useMenuActions } from "@dxos/react-ui-menu";
1147
+ import { composable as composable2, composableProps as composableProps2 } from "@dxos/ui-theme";
1148
+ import { translationKey as translationKey4 } from "#translations";
1149
+ var createTableToolbarActions = ({ stateAtom, onAdd, onSave, customActions }) => Atom3.make((get) => {
1150
+ const nodes = [];
1151
+ if (onAdd) {
1152
+ const add = createMenuAction("add-row", onAdd, {
1153
+ type: "add-row",
1154
+ icon: "ph--plus--regular",
1155
+ label: [
1156
+ "add-row.label",
1157
+ {
1158
+ ns: translationKey4
1159
+ }
1160
+ ],
1161
+ testId: "table.toolbar.add-row"
1162
+ });
1163
+ nodes.push(add);
1164
+ }
1165
+ if (onSave) {
1166
+ const state = get(stateAtom);
1167
+ const save = createMenuAction("save-view", onSave, {
1168
+ type: "save-view",
1169
+ icon: "ph--floppy-disk--regular",
1170
+ label: [
1171
+ "save-view.label",
1172
+ {
1173
+ ns: translationKey4
1174
+ }
1175
+ ],
1176
+ testId: "table.toolbar.save-view",
1177
+ iconOnly: false,
1178
+ hidden: !state.viewDirty
1179
+ });
1180
+ nodes.push(save);
1181
+ }
3002
1182
  const gap = createGapSeparator();
3003
- const nodes = [
3004
- add,
3005
- save,
3006
- ...gap.nodes
3007
- ];
1183
+ nodes.push(...gap.nodes);
3008
1184
  const edges = nodes.map(({ id: target }) => ({
3009
1185
  source: "root",
3010
- target
1186
+ target,
1187
+ relation: "child"
3011
1188
  }));
3012
1189
  if (customActions) {
3013
1190
  const custom = get(customActions);
@@ -3019,33 +1196,140 @@ var createTableToolbarActions = ({ state, onAdd, onSave, customActions }) => Rx.
3019
1196
  edges
3020
1197
  };
3021
1198
  });
3022
- var TableToolbar = ({ classNames, viewDirty, attendableId, onAdd, onSave, customActions }) => {
3023
- var _effect = _useSignals11();
3024
- try {
3025
- const state = useMemo7(() => live3({
1199
+ var TableToolbar = composable2(({ attendableId, viewDirty, onAdd, onSave, customActions, ...props }, forwardedRef) => {
1200
+ const registry = useContext2(RegistryContext2);
1201
+ const stateAtom = useMemo5(() => Atom3.make({
1202
+ viewDirty
1203
+ }), []);
1204
+ useEffect4(() => {
1205
+ registry.set(stateAtom, {
3026
1206
  viewDirty
3027
- }), []);
3028
- const actionsCreator = useMemo7(() => createTableToolbarActions({
3029
- state,
3030
- onAdd,
3031
- onSave,
3032
- customActions
3033
- }), [
3034
- state,
3035
- onAdd,
3036
- onSave,
3037
- customActions
3038
- ]);
3039
- const menu = useMenuActions(actionsCreator);
3040
- return /* @__PURE__ */ React10.createElement(MenuProvider, {
3041
- ...menu,
3042
- attendableId
3043
- }, /* @__PURE__ */ React10.createElement(ToolbarMenu, {
3044
- classNames
1207
+ });
1208
+ }, [
1209
+ registry,
1210
+ stateAtom,
1211
+ viewDirty
1212
+ ]);
1213
+ const actionsCreator = useMemo5(() => createTableToolbarActions({
1214
+ stateAtom,
1215
+ onAdd,
1216
+ onSave,
1217
+ customActions
1218
+ }), [
1219
+ stateAtom,
1220
+ onAdd,
1221
+ onSave,
1222
+ customActions
1223
+ ]);
1224
+ const menuActions = useMenuActions(actionsCreator);
1225
+ return /* @__PURE__ */ React8.createElement(Menu.Root, {
1226
+ ...menuActions,
1227
+ attendableId
1228
+ }, /* @__PURE__ */ React8.createElement(Menu.Toolbar, {
1229
+ ...composableProps2(props),
1230
+ ref: forwardedRef
1231
+ }));
1232
+ });
1233
+ TableToolbar.displayName = "TableToolbar";
1234
+
1235
+ // src/components/Table/Table.tsx
1236
+ var emptyColumnMeta2 = Atom4.make({
1237
+ grid: {}
1238
+ });
1239
+ var [TableContextProvider, useTableContext] = createContext("Table");
1240
+ var TABLE_ROOT_NAME = "Table.Root";
1241
+ var TableRoot = /* @__PURE__ */ forwardRef(({ children }, forwardedRef) => {
1242
+ const controllerRef = useRef3({});
1243
+ useImperativeHandle(forwardedRef, () => ({
1244
+ update: (cell) => controllerRef.current?.update?.(cell),
1245
+ handleInsertRowResult: (result) => controllerRef.current?.handleInsertRowResult?.(result)
1246
+ }), []);
1247
+ return /* @__PURE__ */ React9.createElement(TableContextProvider, {
1248
+ controllerRef
1249
+ }, children);
1250
+ });
1251
+ TableRoot.displayName = TABLE_ROOT_NAME;
1252
+ var Table = {
1253
+ Root: TableRoot,
1254
+ Toolbar: TableToolbar,
1255
+ Content: TableContent
1256
+ };
1257
+
1258
+ // src/components/Table/DynamicTable.tsx
1259
+ import { RegistryContext as RegistryContext3 } from "@effect-atom/atom-react";
1260
+ import React10, { useCallback as useCallback5, useContext as useContext3, useEffect as useEffect5, useMemo as useMemo6, useRef as useRef4, useState as useState5 } from "react";
1261
+ import { useDefaultValue } from "@dxos/react-ui";
1262
+ import { mx as mx2 } from "@dxos/ui-theme";
1263
+ var DynamicTable = ({ classNames, schema, name = "com.example.dynamicTable", rows, properties, jsonSchema: jsonSchemaProp, rowActions, onRowClick, onRowAction, ...props }) => {
1264
+ const registry = useContext3(RegistryContext3);
1265
+ const [dynamicTable, setDynamicTable] = useState5();
1266
+ const { jsonSchema } = useMemo6(() => getBaseSchema({
1267
+ typename: name,
1268
+ properties,
1269
+ jsonSchema: jsonSchemaProp,
1270
+ schema
1271
+ }), [
1272
+ name,
1273
+ properties,
1274
+ jsonSchemaProp,
1275
+ schema
1276
+ ]);
1277
+ useEffect5(() => {
1278
+ setDynamicTable(makeDynamicTable({
1279
+ registry,
1280
+ jsonSchema,
1281
+ properties
3045
1282
  }));
3046
- } finally {
3047
- _effect.f();
3048
- }
1283
+ }, [
1284
+ registry,
1285
+ jsonSchema,
1286
+ properties
1287
+ ]);
1288
+ const tableRef = useRef4(null);
1289
+ const handleCellUpdate = useCallback5((cell) => {
1290
+ tableRef.current?.update?.(cell);
1291
+ }, []);
1292
+ const handleRowOrderChange = useCallback5(() => {
1293
+ tableRef.current?.update?.();
1294
+ }, []);
1295
+ const features = useDefaultValue(props.features, () => ({
1296
+ selection: {
1297
+ enabled: false
1298
+ },
1299
+ dataEditable: false
1300
+ }));
1301
+ const model = useTableModel({
1302
+ rows,
1303
+ object: dynamicTable?.object,
1304
+ projection: dynamicTable?.projection,
1305
+ features,
1306
+ rowActions,
1307
+ onCellUpdate: handleCellUpdate,
1308
+ onRowOrderChange: handleRowOrderChange,
1309
+ onRowAction
1310
+ });
1311
+ const presentation = useMemo6(() => {
1312
+ if (model) {
1313
+ return new TablePresentation(registry, model);
1314
+ }
1315
+ }, [
1316
+ registry,
1317
+ model
1318
+ ]);
1319
+ return /* @__PURE__ */ React10.createElement("div", {
1320
+ role: "none",
1321
+ className: mx2("dx-expander grid", classNames)
1322
+ }, /* @__PURE__ */ React10.createElement("div", {
1323
+ role: "none",
1324
+ className: "grid min-h-0 overflow-hidden"
1325
+ }, /* @__PURE__ */ React10.createElement(Table.Root, {
1326
+ ref: tableRef
1327
+ }, /* @__PURE__ */ React10.createElement(Table.Content, {
1328
+ model,
1329
+ presentation,
1330
+ ignoreAttention: true,
1331
+ onRowClick
1332
+ }))));
3049
1333
  };
3050
1334
  export {
3051
1335
  BUTTON_IDENTIFIERS,
@@ -3057,19 +1341,21 @@ export {
3057
1341
  Table,
3058
1342
  TableModel,
3059
1343
  TablePresentation,
3060
- TableSchema,
3061
- TableToolbar,
3062
- TableType,
3063
1344
  cellClassesForRowSelection,
1345
+ compareValues,
1346
+ createDirectChangeCallback,
1347
+ createEchoChangeCallback,
1348
+ extractOrder,
3064
1349
  getBaseSchema,
3065
- initializeTable,
1350
+ getSortValue,
3066
1351
  makeDynamicTable,
3067
1352
  narrowSchema,
3068
1353
  tableButtons,
3069
1354
  tableControls,
3070
1355
  touch,
3071
- translations,
3072
1356
  useAddRow,
1357
+ useProjectionModel,
1358
+ useTableContext,
3073
1359
  useTableModel
3074
1360
  };
3075
1361
  //# sourceMappingURL=index.mjs.map