@dxos/react-ui-table 0.8.3 → 0.8.4-main.1c7ec43d41

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