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