@dxos/react-ui-table 0.8.3 → 0.8.4-main.03d5cd7b56

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