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

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