@dxos/react-ui-table 0.8.3-staging.0fa589b → 0.8.4-main.1068cf700f

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