@dxos/react-ui-table 0.6.14-staging.e15392e → 0.7.0

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 (242) hide show
  1. package/dist/lib/browser/_deprecated/index.mjs +1483 -0
  2. package/dist/lib/browser/_deprecated/index.mjs.map +7 -0
  3. package/dist/lib/browser/chunk-A5W4XWPD.mjs +18 -0
  4. package/dist/lib/browser/chunk-A5W4XWPD.mjs.map +7 -0
  5. package/dist/lib/browser/index.mjs +1275 -1356
  6. package/dist/lib/browser/index.mjs.map +4 -4
  7. package/dist/lib/browser/meta.json +1 -1
  8. package/dist/lib/browser/types/index.mjs +7 -0
  9. package/dist/lib/browser/types/index.mjs.map +7 -0
  10. package/dist/lib/node/_deprecated/index.cjs +1511 -0
  11. package/dist/lib/node/_deprecated/index.cjs.map +7 -0
  12. package/dist/lib/node/chunk-32IGMRME.cjs +40 -0
  13. package/dist/lib/node/chunk-32IGMRME.cjs.map +7 -0
  14. package/dist/lib/node/index.cjs +1381 -0
  15. package/dist/lib/node/index.cjs.map +7 -0
  16. package/dist/lib/node/meta.json +1 -0
  17. package/dist/lib/node/types/index.cjs +29 -0
  18. package/dist/lib/node/types/index.cjs.map +7 -0
  19. package/dist/lib/node-esm/_deprecated/index.mjs +1485 -0
  20. package/dist/lib/node-esm/_deprecated/index.mjs.map +7 -0
  21. package/dist/lib/node-esm/chunk-DWQ4KGLD.mjs +20 -0
  22. package/dist/lib/node-esm/chunk-DWQ4KGLD.mjs.map +7 -0
  23. package/dist/lib/node-esm/index.mjs +1400 -0
  24. package/dist/lib/node-esm/index.mjs.map +7 -0
  25. package/dist/lib/node-esm/meta.json +1 -0
  26. package/dist/lib/node-esm/types/index.mjs +8 -0
  27. package/dist/lib/node-esm/types/index.mjs.map +7 -0
  28. package/dist/types/src/_deprecated/components/Cell/Cell.d.ts.map +1 -0
  29. package/dist/types/src/_deprecated/components/Cell/CellCombobox.d.ts.map +1 -0
  30. package/dist/types/src/_deprecated/components/Cell/index.d.ts.map +1 -0
  31. package/dist/types/src/_deprecated/components/ColumnMenu/ColumnMenu.d.ts.map +1 -0
  32. package/dist/types/src/_deprecated/components/ColumnMenu/ColumnMenu.stories.d.ts.map +1 -0
  33. package/dist/types/src/_deprecated/components/ColumnMenu/ColumnSettings.d.ts.map +1 -0
  34. package/dist/types/src/{components → _deprecated/components}/ColumnMenu/ColumnSettings.stories.d.ts +4 -4
  35. package/dist/types/src/_deprecated/components/ColumnMenu/ColumnSettings.stories.d.ts.map +1 -0
  36. package/dist/types/src/_deprecated/components/ColumnMenu/index.d.ts.map +1 -0
  37. package/dist/types/src/_deprecated/components/Table/Table.d.ts +20 -0
  38. package/dist/types/src/_deprecated/components/Table/Table.d.ts.map +1 -0
  39. package/dist/types/src/_deprecated/components/Table/Table.stories.d.ts +116 -0
  40. package/dist/types/src/_deprecated/components/Table/Table.stories.d.ts.map +1 -0
  41. package/dist/types/src/_deprecated/components/Table/TableBody.d.ts.map +1 -0
  42. package/dist/types/src/_deprecated/components/Table/TableContext.d.ts.map +1 -0
  43. package/dist/types/src/_deprecated/components/Table/TableHead.d.ts.map +1 -0
  44. package/dist/types/src/_deprecated/components/Table/TableRootContext.d.ts.map +1 -0
  45. package/dist/types/src/_deprecated/components/Table/index.d.ts +5 -0
  46. package/dist/types/src/_deprecated/components/Table/index.d.ts.map +1 -0
  47. package/dist/types/src/_deprecated/components/Table/props.d.ts.map +1 -0
  48. package/dist/types/src/_deprecated/components/index.d.ts +4 -0
  49. package/dist/types/src/_deprecated/components/index.d.ts.map +1 -0
  50. package/dist/types/src/_deprecated/helpers.d.ts.map +1 -0
  51. package/dist/types/src/_deprecated/hooks/index.d.ts +5 -0
  52. package/dist/types/src/_deprecated/hooks/index.d.ts.map +1 -0
  53. package/dist/types/src/_deprecated/hooks/useColumnSizing.d.ts.map +1 -0
  54. package/dist/types/src/{hooks → _deprecated/hooks}/useColumnSorting.d.ts +1 -1
  55. package/dist/types/src/_deprecated/hooks/useColumnSorting.d.ts.map +1 -0
  56. package/dist/types/src/_deprecated/hooks/usePinLastRow.d.ts.map +1 -0
  57. package/dist/types/src/_deprecated/hooks/useRowSelection.d.ts.map +1 -0
  58. package/dist/types/src/_deprecated/index.d.ts +8 -0
  59. package/dist/types/src/_deprecated/index.d.ts.map +1 -0
  60. package/dist/types/src/_deprecated/schema/Schema.stories.d.ts.map +1 -0
  61. package/dist/types/src/{schema → _deprecated/schema}/column-utils.d.ts +3 -0
  62. package/dist/types/src/_deprecated/schema/column-utils.d.ts.map +1 -0
  63. package/dist/types/src/_deprecated/schema/index.d.ts.map +1 -0
  64. package/dist/types/src/{schema → _deprecated/schema}/types.d.ts +3 -2
  65. package/dist/types/src/_deprecated/schema/types.d.ts.map +1 -0
  66. package/dist/types/src/_deprecated/theme.d.ts.map +1 -0
  67. package/dist/types/src/_deprecated/translations.d.ts +21 -0
  68. package/dist/types/src/_deprecated/translations.d.ts.map +1 -0
  69. package/dist/types/src/_deprecated/types.d.ts.map +1 -0
  70. package/dist/types/src/components/Table/ColumnActionsMenu.d.ts +8 -0
  71. package/dist/types/src/components/Table/ColumnActionsMenu.d.ts.map +1 -0
  72. package/dist/types/src/components/Table/ColumnSettings.d.ts +8 -0
  73. package/dist/types/src/components/Table/ColumnSettings.d.ts.map +1 -0
  74. package/dist/types/src/components/Table/RowActionsMenu.d.ts +8 -0
  75. package/dist/types/src/components/Table/RowActionsMenu.d.ts.map +1 -0
  76. package/dist/types/src/components/Table/Table.d.ts +11 -14
  77. package/dist/types/src/components/Table/Table.d.ts.map +1 -1
  78. package/dist/types/src/components/Table/Table.stories.d.ts +10 -113
  79. package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
  80. package/dist/types/src/components/Table/index.d.ts +0 -3
  81. package/dist/types/src/components/Table/index.d.ts.map +1 -1
  82. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +17 -0
  83. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -0
  84. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts +11 -0
  85. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -0
  86. package/dist/types/src/components/TableCellEditor/extension.d.ts +12 -0
  87. package/dist/types/src/components/TableCellEditor/extension.d.ts.map +1 -0
  88. package/dist/types/src/components/TableCellEditor/index.d.ts +2 -0
  89. package/dist/types/src/components/TableCellEditor/index.d.ts.map +1 -0
  90. package/dist/types/src/components/Toolbar/Toolbar.d.ts +21 -0
  91. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -0
  92. package/dist/types/src/components/Toolbar/common.d.ts +20 -0
  93. package/dist/types/src/components/Toolbar/common.d.ts.map +1 -0
  94. package/dist/types/src/components/Toolbar/index.d.ts +2 -0
  95. package/dist/types/src/components/Toolbar/index.d.ts.map +1 -0
  96. package/dist/types/src/components/index.d.ts +1 -2
  97. package/dist/types/src/components/index.d.ts.map +1 -1
  98. package/dist/types/src/components/testing.d.ts +97 -0
  99. package/dist/types/src/components/testing.d.ts.map +1 -0
  100. package/dist/types/src/hooks/index.d.ts +1 -4
  101. package/dist/types/src/hooks/index.d.ts.map +1 -1
  102. package/dist/types/src/hooks/useTableModel.d.ts +15 -0
  103. package/dist/types/src/hooks/useTableModel.d.ts.map +1 -0
  104. package/dist/types/src/index.d.ts +3 -4
  105. package/dist/types/src/index.d.ts.map +1 -1
  106. package/dist/types/src/model/index.d.ts +3 -0
  107. package/dist/types/src/model/index.d.ts.map +1 -0
  108. package/dist/types/src/model/modal-controller.d.ts +29 -0
  109. package/dist/types/src/model/modal-controller.d.ts.map +1 -0
  110. package/dist/types/src/model/selection-model.d.ts +22 -0
  111. package/dist/types/src/model/selection-model.d.ts.map +1 -0
  112. package/dist/types/src/model/table-model.d.ts +93 -0
  113. package/dist/types/src/model/table-model.d.ts.map +1 -0
  114. package/dist/types/src/model/table-model.test.d.ts +2 -0
  115. package/dist/types/src/model/table-model.test.d.ts.map +1 -0
  116. package/dist/types/src/translations.d.ts +23 -16
  117. package/dist/types/src/translations.d.ts.map +1 -1
  118. package/dist/types/src/types/index.d.ts +2 -0
  119. package/dist/types/src/types/index.d.ts.map +1 -0
  120. package/dist/types/src/types/table.d.ts +106 -0
  121. package/dist/types/src/types/table.d.ts.map +1 -0
  122. package/dist/types/src/util/grid.d.ts +14 -0
  123. package/dist/types/src/util/grid.d.ts.map +1 -0
  124. package/dist/types/src/util/index.d.ts +5 -0
  125. package/dist/types/src/util/index.d.ts.map +1 -0
  126. package/dist/types/src/util/signals.d.ts +6 -0
  127. package/dist/types/src/util/signals.d.ts.map +1 -0
  128. package/dist/types/src/util/table-buttons.d.ts +24 -0
  129. package/dist/types/src/util/table-buttons.d.ts.map +1 -0
  130. package/dist/types/src/util/table-controls.d.ts +21 -0
  131. package/dist/types/src/util/table-controls.d.ts.map +1 -0
  132. package/dist/types/src/util/table.d.ts +8 -0
  133. package/dist/types/src/util/table.d.ts.map +1 -0
  134. package/package.json +56 -22
  135. package/src/{components → _deprecated/components}/ColumnMenu/ColumnMenu.stories.tsx +4 -4
  136. package/src/{components → _deprecated/components}/ColumnMenu/ColumnSettings.stories.tsx +9 -9
  137. package/src/{components → _deprecated/components}/ColumnMenu/ColumnSettings.tsx +3 -3
  138. package/src/_deprecated/components/Table/Table.stories.tsx +508 -0
  139. package/src/_deprecated/components/Table/Table.tsx +301 -0
  140. package/src/{components → _deprecated/components}/Table/TableHead.tsx +3 -1
  141. package/src/_deprecated/components/Table/index.ts +8 -0
  142. package/src/_deprecated/components/index.ts +7 -0
  143. package/src/{helpers.tsx → _deprecated/helpers.tsx} +4 -3
  144. package/src/_deprecated/hooks/index.ts +8 -0
  145. package/src/_deprecated/index.ts +12 -0
  146. package/src/_deprecated/schema/column-utils.ts +54 -0
  147. package/src/{schema → _deprecated/schema}/types.ts +3 -2
  148. package/src/_deprecated/translations.ts +26 -0
  149. package/src/components/Table/ColumnActionsMenu.tsx +60 -0
  150. package/src/components/Table/ColumnSettings.tsx +76 -0
  151. package/src/components/Table/RowActionsMenu.tsx +36 -0
  152. package/src/components/Table/Table.stories.tsx +197 -467
  153. package/src/components/Table/Table.tsx +172 -256
  154. package/src/components/Table/index.ts +1 -4
  155. package/src/components/TableCellEditor/TableCellEditor.stories.tsx +114 -0
  156. package/src/components/TableCellEditor/TableCellEditor.tsx +155 -0
  157. package/src/components/TableCellEditor/extension.ts +62 -0
  158. package/src/components/TableCellEditor/index.ts +5 -0
  159. package/src/components/Toolbar/Toolbar.tsx +86 -0
  160. package/src/components/Toolbar/common.tsx +72 -0
  161. package/src/components/Toolbar/index.ts +5 -0
  162. package/src/components/index.ts +2 -3
  163. package/src/components/testing.ts +114 -0
  164. package/src/hooks/index.ts +1 -4
  165. package/src/hooks/useTableModel.ts +52 -0
  166. package/src/index.ts +5 -6
  167. package/src/model/index.ts +6 -0
  168. package/src/model/modal-controller.ts +83 -0
  169. package/src/model/selection-model.ts +99 -0
  170. package/src/model/table-model.test.ts +182 -0
  171. package/src/model/table-model.ts +565 -0
  172. package/src/translations.ts +20 -13
  173. package/src/types/index.ts +5 -0
  174. package/src/types/table.ts +13 -0
  175. package/src/util/grid.ts +23 -0
  176. package/src/util/index.ts +8 -0
  177. package/src/util/signals.ts +9 -0
  178. package/src/util/table-buttons.ts +48 -0
  179. package/src/util/table-controls.ts +27 -0
  180. package/src/util/table.ts +77 -0
  181. package/dist/types/src/components/Cell/Cell.d.ts.map +0 -1
  182. package/dist/types/src/components/Cell/CellCombobox.d.ts.map +0 -1
  183. package/dist/types/src/components/Cell/index.d.ts.map +0 -1
  184. package/dist/types/src/components/ColumnMenu/ColumnMenu.d.ts.map +0 -1
  185. package/dist/types/src/components/ColumnMenu/ColumnMenu.stories.d.ts.map +0 -1
  186. package/dist/types/src/components/ColumnMenu/ColumnSettings.d.ts.map +0 -1
  187. package/dist/types/src/components/ColumnMenu/ColumnSettings.stories.d.ts.map +0 -1
  188. package/dist/types/src/components/ColumnMenu/index.d.ts.map +0 -1
  189. package/dist/types/src/components/Table/TableBody.d.ts.map +0 -1
  190. package/dist/types/src/components/Table/TableContext.d.ts.map +0 -1
  191. package/dist/types/src/components/Table/TableHead.d.ts.map +0 -1
  192. package/dist/types/src/components/Table/TableRootContext.d.ts.map +0 -1
  193. package/dist/types/src/components/Table/props.d.ts.map +0 -1
  194. package/dist/types/src/helpers.d.ts.map +0 -1
  195. package/dist/types/src/hooks/useColumnSizing.d.ts.map +0 -1
  196. package/dist/types/src/hooks/useColumnSorting.d.ts.map +0 -1
  197. package/dist/types/src/hooks/usePinLastRow.d.ts.map +0 -1
  198. package/dist/types/src/hooks/useRowSelection.d.ts.map +0 -1
  199. package/dist/types/src/schema/Schema.stories.d.ts.map +0 -1
  200. package/dist/types/src/schema/column-utils.d.ts.map +0 -1
  201. package/dist/types/src/schema/index.d.ts.map +0 -1
  202. package/dist/types/src/schema/types.d.ts.map +0 -1
  203. package/dist/types/src/theme.d.ts.map +0 -1
  204. package/dist/types/src/types.d.ts.map +0 -1
  205. package/src/schema/column-utils.ts +0 -48
  206. /package/dist/types/src/{components → _deprecated/components}/Cell/Cell.d.ts +0 -0
  207. /package/dist/types/src/{components → _deprecated/components}/Cell/CellCombobox.d.ts +0 -0
  208. /package/dist/types/src/{components → _deprecated/components}/Cell/index.d.ts +0 -0
  209. /package/dist/types/src/{components → _deprecated/components}/ColumnMenu/ColumnMenu.d.ts +0 -0
  210. /package/dist/types/src/{components → _deprecated/components}/ColumnMenu/ColumnMenu.stories.d.ts +0 -0
  211. /package/dist/types/src/{components → _deprecated/components}/ColumnMenu/ColumnSettings.d.ts +0 -0
  212. /package/dist/types/src/{components → _deprecated/components}/ColumnMenu/index.d.ts +0 -0
  213. /package/dist/types/src/{components → _deprecated/components}/Table/TableBody.d.ts +0 -0
  214. /package/dist/types/src/{components → _deprecated/components}/Table/TableContext.d.ts +0 -0
  215. /package/dist/types/src/{components → _deprecated/components}/Table/TableHead.d.ts +0 -0
  216. /package/dist/types/src/{components → _deprecated/components}/Table/TableRootContext.d.ts +0 -0
  217. /package/dist/types/src/{components → _deprecated/components}/Table/props.d.ts +0 -0
  218. /package/dist/types/src/{helpers.d.ts → _deprecated/helpers.d.ts} +0 -0
  219. /package/dist/types/src/{hooks → _deprecated/hooks}/useColumnSizing.d.ts +0 -0
  220. /package/dist/types/src/{hooks → _deprecated/hooks}/usePinLastRow.d.ts +0 -0
  221. /package/dist/types/src/{hooks → _deprecated/hooks}/useRowSelection.d.ts +0 -0
  222. /package/dist/types/src/{schema → _deprecated/schema}/Schema.stories.d.ts +0 -0
  223. /package/dist/types/src/{schema → _deprecated/schema}/index.d.ts +0 -0
  224. /package/dist/types/src/{theme.d.ts → _deprecated/theme.d.ts} +0 -0
  225. /package/dist/types/src/{types.d.ts → _deprecated/types.d.ts} +0 -0
  226. /package/src/{components → _deprecated/components}/Cell/Cell.tsx +0 -0
  227. /package/src/{components → _deprecated/components}/Cell/CellCombobox.tsx +0 -0
  228. /package/src/{components → _deprecated/components}/Cell/index.ts +0 -0
  229. /package/src/{components → _deprecated/components}/ColumnMenu/ColumnMenu.tsx +0 -0
  230. /package/src/{components → _deprecated/components}/ColumnMenu/index.ts +0 -0
  231. /package/src/{components → _deprecated/components}/Table/TableBody.tsx +0 -0
  232. /package/src/{components → _deprecated/components}/Table/TableContext.tsx +0 -0
  233. /package/src/{components → _deprecated/components}/Table/TableRootContext.ts +0 -0
  234. /package/src/{components → _deprecated/components}/Table/props.ts +0 -0
  235. /package/src/{hooks → _deprecated/hooks}/useColumnSizing.ts +0 -0
  236. /package/src/{hooks → _deprecated/hooks}/useColumnSorting.ts +0 -0
  237. /package/src/{hooks → _deprecated/hooks}/usePinLastRow.ts +0 -0
  238. /package/src/{hooks → _deprecated/hooks}/useRowSelection.ts +0 -0
  239. /package/src/{schema → _deprecated/schema}/Schema.stories.tsx +0 -0
  240. /package/src/{schema → _deprecated/schema}/index.ts +0 -0
  241. /package/src/{theme.ts → _deprecated/theme.ts} +0 -0
  242. /package/src/{types.ts → _deprecated/types.ts} +0 -0
@@ -1,1480 +1,1399 @@
1
+ import {
2
+ TableType
3
+ } from "./chunk-A5W4XWPD.mjs";
4
+
1
5
  // packages/ui/react-ui-table/src/translations.ts
2
6
  var translationKey = "table";
3
7
  var translations_default = [
4
8
  {
5
9
  "en-US": {
6
10
  [translationKey]: {
7
- "no selection label": "\xA0",
8
- "number column type label": "Number",
9
- "boolean column type label": "Boolean",
10
- "date column type label": "Date",
11
- "string column type label": "Text",
12
- "ref column type label": "Reference",
13
- "digits label": "Decimal places",
14
- "save label": "Save",
15
- "delete label": "Delete",
16
- "column label label": "Label",
17
- "property key label": "Property",
18
- "column type label": "Type"
11
+ "object placeholder": "New table",
12
+ "create object label": "Create table",
13
+ "table name placeholder": "Table name",
14
+ "settings title": "Table settings",
15
+ "table schema label": "Schema",
16
+ "new schema": "New schema",
17
+ "continue label": "Continue",
18
+ "create stack section label": "Create table",
19
+ "add row": "Add row",
20
+ "create comment": "Create thread",
21
+ "column action sort descending": "Sort Descending",
22
+ "column action sort ascending": "Sort Ascending",
23
+ "column action clear sorting": "Clear Sorting",
24
+ "column action settings": "Column Settings",
25
+ "column action delete": "Delete Column",
26
+ "delete row label": "Delete row",
27
+ "bulk delete row label": "Delete selected rows",
28
+ "column deleted label": "Column deleted",
29
+ "new column button label": "Create column"
19
30
  }
20
31
  }
21
32
  }
22
33
  ];
23
34
 
24
- // packages/ui/react-ui-table/src/components/Cell/CellCombobox.tsx
25
- import { CaretDown } from "@phosphor-icons/react";
26
- import React, { useCallback, useState } from "react";
27
- import { Popover, useTranslation } from "@dxos/react-ui";
28
- import { PopoverCombobox } from "@dxos/react-ui-searchlist";
29
- import { descriptionText, getSize, mx, staticPlaceholderText } from "@dxos/react-ui-theme";
30
- var initialItems = (model, value) => value ? {
31
- [model.getId(value)]: {
32
- label: model.getText(value),
33
- data: value
34
- }
35
- } : {};
36
- var CellCombobox = ({ model, value, onValueChange }) => {
37
- const { t } = useTranslation(translationKey);
38
- const [items, setItems] = useState(initialItems(model, value));
39
- const [loading, setLoading] = useState(false);
40
- const [searchInputValue, setSearchInputValue] = useState("");
41
- const handleSearchInputValueChange = async (text) => {
42
- setSearchInputValue(text ?? "");
43
- setLoading(true);
44
- const items2 = await model.query(text);
45
- setItems(items2.reduce((acc, item) => {
46
- acc[model.getId(item)] = {
47
- label: model.getText(item),
48
- data: item
49
- };
50
- return acc;
51
- }, initialItems(model, value)));
52
- setLoading(false);
53
- };
54
- const handleValueChange = useCallback((id) => {
55
- onValueChange(items[id]?.data);
56
- }, [
57
- items,
58
- onValueChange
59
- ]);
60
- return /* @__PURE__ */ React.createElement(PopoverCombobox.Root, {
61
- placeholder: t("no selection label")
62
- }, /* @__PURE__ */ React.createElement(PopoverCombobox.Trigger, {
63
- variant: "ghost",
64
- classNames: "is-full rounded-none ring-inset"
65
- }, value ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", {
66
- className: mx("font-normal text-start text-baseText flex-1 min-is-0 truncate mie-2", !value && staticPlaceholderText)
67
- }, model.getText(value)), /* @__PURE__ */ React.createElement(CaretDown, {
68
- weight: "bold",
69
- className: getSize(3)
70
- })) : null), /* @__PURE__ */ React.createElement(PopoverCombobox.Content, {
71
- shouldFilter: false,
72
- classNames: "is-[--radix-popover-trigger-width]"
73
- }, /* @__PURE__ */ React.createElement(PopoverCombobox.Input, {
74
- value: searchInputValue,
75
- onValueChange: handleSearchInputValueChange
76
- }), /* @__PURE__ */ React.createElement(PopoverCombobox.List, null, searchInputValue.length < 1 ? /* @__PURE__ */ React.createElement("p", {
77
- className: mx("text-center", descriptionText)
78
- }, "Start typing to search") : loading ? /* @__PURE__ */ React.createElement("p", {
79
- className: mx("text-center", descriptionText)
80
- }, "Searching\u2026") : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(PopoverCombobox.Empty, {
81
- classNames: [
82
- "text-center",
83
- descriptionText
84
- ]
85
- }, "No results"), Object.entries(items).map(([id, { label }]) => /* @__PURE__ */ React.createElement(PopoverCombobox.Item, {
86
- key: id,
87
- value: id,
88
- onSelect: handleValueChange,
89
- classNames: "truncate"
90
- }, label)))), /* @__PURE__ */ React.createElement(Popover.Arrow, null)));
91
- };
92
-
93
- // packages/ui/react-ui-table/src/components/ColumnMenu/ColumnMenu.tsx
94
- import { X, GearSix, CaretDown as CaretDown2, ArrowDown, ArrowUp, ArrowRight, ArrowLeft } from "@phosphor-icons/react";
95
- import React4, { useRef as useRef2, useState as useState6, useCallback as useCallback5 } from "react";
96
- import { Button as Button2, Popover as Popover2, DropdownMenu } from "@dxos/react-ui";
97
- import { getSize as getSize2, mx as mx2 } from "@dxos/react-ui-theme";
35
+ // packages/ui/react-ui-table/src/components/Table/Table.tsx
36
+ import React5, { forwardRef, useCallback as useCallback3, useImperativeHandle, useState as useState2 } from "react";
37
+ import { getValue } from "@dxos/echo-schema";
38
+ import { invariant as invariant2 } from "@dxos/invariant";
39
+ import { Filter, getSpace as getSpace2, fullyQualifiedId } from "@dxos/react-client/echo";
40
+ import { useAttention } from "@dxos/react-ui-attention";
41
+ import { Grid, closestCell } from "@dxos/react-ui-grid";
42
+ import { mx } from "@dxos/react-ui-theme";
43
+ import { isNotFalsy } from "@dxos/util";
98
44
 
99
- // packages/ui/react-ui-table/src/components/ColumnMenu/ColumnSettings.tsx
100
- import React2, { useRef, useState as useState2 } from "react";
101
- import { Button, Input, Select, useTranslation as useTranslation2 } from "@dxos/react-ui";
102
- import { FieldValueTypes } from "@dxos/schema";
103
- import { safeParseInt } from "@dxos/util";
104
- var ColumnSettings = ({ column, tableDef, tablesToReference, onUpdate, onDelete, onClose }) => {
105
- const { t } = useTranslation2(translationKey);
106
- const propRef = useRef(null);
107
- const [formState, setFormState] = useState2({
108
- prop: column.id,
109
- refTable: column.refTable,
110
- refProp: column.refProp,
111
- type: String(column.type),
112
- label: column.label,
113
- digits: String(column.digits ?? "0")
114
- });
115
- const handleSave = () => {
116
- const { prop, refTable, refProp, type, label, digits } = formState;
117
- if (!prop.length || !prop.match(/^[a-zA-Z_].+/i)) {
118
- propRef.current?.focus();
119
- return;
120
- }
121
- const current = tableDef.columns.find((c) => c.id !== column.id && c.id === prop);
122
- if (current) {
123
- if (current.type !== "ref") {
124
- propRef.current?.focus();
125
- return;
126
- }
127
- }
128
- onUpdate?.(column.id, {
129
- id: prop,
130
- prop,
131
- label,
132
- type,
133
- refTable,
134
- refProp,
135
- digits: safeParseInt(digits)
136
- });
137
- onClose?.();
138
- };
139
- const handleDelete = () => {
140
- onDelete?.(column.id);
141
- onClose?.();
142
- };
143
- return /* @__PURE__ */ React2.createElement("div", {
144
- className: "flex flex-col w-full gap-4"
145
- }, /* @__PURE__ */ React2.createElement("div", {
146
- className: "flex flex-col gap-2"
147
- }, /* @__PURE__ */ React2.createElement(Input.Root, null, /* @__PURE__ */ React2.createElement(Input.Label, null, t("column label label")), /* @__PURE__ */ React2.createElement(Input.TextInput, {
148
- placeholder: "Column label",
149
- value: formState.label ?? "",
150
- onChange: (event) => setFormState((prevState) => ({
151
- ...prevState,
152
- label: event.target.value
153
- })),
154
- autoFocus: true,
155
- "data-testid": "table.column-settings.label"
156
- })), /* @__PURE__ */ React2.createElement(Input.Root, null, /* @__PURE__ */ React2.createElement(Input.Label, null, t("property key label")), /* @__PURE__ */ React2.createElement(Input.TextInput, {
157
- ref: propRef,
158
- placeholder: "Property key",
159
- value: formState.prop ?? "",
160
- onChange: (event) => setFormState((prevState) => ({
161
- ...prevState,
162
- prop: event.target.value.replace(/[^\w_]/g, "")
163
- }))
164
- })), /* @__PURE__ */ React2.createElement(Input.Root, null, /* @__PURE__ */ React2.createElement(Input.Label, null, t("column type label")), /* @__PURE__ */ React2.createElement(Select.Root, {
165
- value: formState.type,
166
- onValueChange: (value) => setFormState((prevState) => ({
167
- ...prevState,
168
- type: value
169
- }))
170
- }, /* @__PURE__ */ React2.createElement(Select.TriggerButton, {
171
- classNames: "is-full",
172
- placeholder: "Type"
173
- }), /* @__PURE__ */ React2.createElement(Select.Portal, null, /* @__PURE__ */ React2.createElement(Select.Content, null, /* @__PURE__ */ React2.createElement(Select.Viewport, null, FieldValueTypes.map((type) => /* @__PURE__ */ React2.createElement(Select.Option, {
174
- key: type,
175
- value: type
176
- }, t(`${type} column type label`)))))))), formState.type === "number" && /* @__PURE__ */ React2.createElement(Input.Root, null, /* @__PURE__ */ React2.createElement(Input.Label, null, t("digits label")), /* @__PURE__ */ React2.createElement(Input.TextInput, {
177
- value: formState.digits ?? "",
178
- onChange: (event) => setFormState((prevState) => ({
179
- ...prevState,
180
- digits: event.target.value
181
- }))
182
- })), formState.type === "ref" && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Input.Root, null, /* @__PURE__ */ React2.createElement(Input.Label, null, "Table"), /* @__PURE__ */ React2.createElement(Select.Root, {
183
- value: formState.refTable,
184
- onValueChange: (value) => setFormState((prevState) => ({
185
- ...prevState,
186
- refTable: value
187
- }))
188
- }, /* @__PURE__ */ React2.createElement(Select.TriggerButton, {
189
- placeholder: "Table",
190
- classNames: "is-full"
191
- }), /* @__PURE__ */ React2.createElement(Select.Portal, null, /* @__PURE__ */ React2.createElement(Select.Content, null, /* @__PURE__ */ React2.createElement(Select.Viewport, null, tablesToReference.filter((t2) => t2.id !== tableDef.id).map(({ id, name }) => /* @__PURE__ */ React2.createElement(Select.Option, {
192
- key: id,
193
- value: id
194
- }, name ?? id))))))), formState.refTable && /* @__PURE__ */ React2.createElement(Input.Root, null, /* @__PURE__ */ React2.createElement(Input.Label, null, "Table property"), /* @__PURE__ */ React2.createElement(Select.Root, {
195
- value: formState.refProp,
196
- onValueChange: (value) => setFormState((prevState) => ({
197
- ...prevState,
198
- refProp: value
199
- }))
200
- }, /* @__PURE__ */ React2.createElement(Select.TriggerButton, {
201
- placeholder: "Property"
202
- }), /* @__PURE__ */ React2.createElement(Select.Portal, null, /* @__PURE__ */ React2.createElement(Select.Content, null, /* @__PURE__ */ React2.createElement(Select.Viewport, null, tablesToReference.find((tableDef2) => tableDef2.id === formState.refTable)?.columns.map(({ id, label }) => /* @__PURE__ */ React2.createElement(Select.Option, {
203
- key: id,
204
- value: id
205
- }, label ?? id))))))))), /* @__PURE__ */ React2.createElement("div", null, /* @__PURE__ */ React2.createElement("div", {
206
- className: "flex flex-row justify-end gap-1"
207
- }, /* @__PURE__ */ React2.createElement(Button, {
208
- onClick: handleDelete,
209
- "data-testid": "table.column-settings.delete"
210
- }, t("delete label")), /* @__PURE__ */ React2.createElement(Button, {
211
- variant: "primary",
212
- onClick: handleSave,
213
- "data-testid": "table.column-settings.save"
214
- }, t("save label")))));
45
+ // packages/ui/react-ui-table/src/components/Table/ColumnActionsMenu.tsx
46
+ import React from "react";
47
+ import { DropdownMenu, useTranslation } from "@dxos/react-ui";
48
+ var ColumnActionsMenu = ({ model }) => {
49
+ const { t } = useTranslation(translationKey);
50
+ const state = model?.modalController.state.value;
51
+ if (!model || state?.type !== "column") {
52
+ return null;
53
+ }
54
+ const currentSort = model.sorting;
55
+ const isCurrentColumnSorted = currentSort?.fieldId === state.fieldId;
56
+ return /* @__PURE__ */ React.createElement(DropdownMenu.Root, {
57
+ modal: false,
58
+ open: state?.type === "column",
59
+ onOpenChange: model.modalController.close
60
+ }, /* @__PURE__ */ React.createElement(DropdownMenu.VirtualTrigger, {
61
+ virtualRef: model.modalController.trigger
62
+ }), /* @__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, {
63
+ onClick: () => model.setSort(state.fieldId, "desc")
64
+ }, t("column action sort descending")), (!isCurrentColumnSorted || currentSort?.direction === "desc") && /* @__PURE__ */ React.createElement(DropdownMenu.Item, {
65
+ onClick: () => model.setSort(state.fieldId, "asc")
66
+ }, t("column action sort ascending")), isCurrentColumnSorted && /* @__PURE__ */ React.createElement(DropdownMenu.Item, {
67
+ onClick: () => model.clearSort()
68
+ }, t("column action clear sorting")), model.getColumnCount() > 1 && /* @__PURE__ */ React.createElement(DropdownMenu.Item, {
69
+ onClick: () => model.deleteColumn(state.fieldId)
70
+ }, t("column action delete")), /* @__PURE__ */ React.createElement(DropdownMenu.Item, {
71
+ onClick: () => model.modalController.openColumnSettings()
72
+ }, t("column action settings"))), /* @__PURE__ */ React.createElement(DropdownMenu.Arrow, null))));
215
73
  };
216
74
 
217
- // packages/ui/react-ui-table/src/hooks/useColumnSizing.ts
218
- import { useCallback as useCallback2, useEffect, useState as useState3 } from "react";
219
- import { useOnTransition } from "@dxos/react-ui";
220
- var useColumnResizing = ({ columns, onColumnResize }) => {
221
- const [columnsInitialised, setColumnsInitialised] = useState3(false);
222
- const [columnSizing, setColumnSizing] = useState3({});
75
+ // packages/ui/react-ui-table/src/components/Table/ColumnSettings.tsx
76
+ import React2, { useCallback, useEffect, useMemo, useState } from "react";
77
+ import { getSpace } from "@dxos/react-client/echo";
78
+ import { DropdownMenu as DropdownMenu2 } from "@dxos/react-ui";
79
+ import { FieldEditor } from "@dxos/react-ui-data";
80
+ var ColumnSettings = ({ model }) => {
81
+ const [newField, setNewField] = useState();
82
+ const state = model?.modalController.state.value;
83
+ const space = getSpace(model?.table);
223
84
  useEffect(() => {
224
- if (columnsInitialised) {
225
- return;
226
- }
227
- setColumnSizing(columns.filter((column) => !!column.size && column.prop !== void 0).reduce((state, column) => {
228
- state[column.prop] = column.size;
229
- return state;
230
- }, {}));
231
- setColumnsInitialised(true);
232
- }, [
233
- columns,
234
- setColumnSizing
235
- ]);
236
- const [columnSizingInfo, setColumnSizingInfo] = useState3({});
237
- const notifyColumnResize = useCallback2(() => onColumnResize?.(columnSizing), [
238
- onColumnResize,
239
- columnSizing
240
- ]);
241
- useOnTransition(columnSizingInfo.isResizingColumn, (v) => typeof v === "string", false, notifyColumnResize);
242
- return {
243
- columnSizing,
244
- columnSizingInfo,
245
- setColumnSizing,
246
- setColumnSizingInfo
247
- };
248
- };
249
-
250
- // packages/ui/react-ui-table/src/hooks/useColumnSorting.ts
251
- import { useState as useState4 } from "react";
252
-
253
- // packages/ui/react-ui-table/src/components/Table/TableContext.tsx
254
- import React3, { useContext, createContext } from "react";
255
- import { raise } from "@dxos/debug";
256
- var TABLE_NAME = "Table";
257
- var TableContext = /* @__PURE__ */ createContext(void 0);
258
- var TableProvider = ({ children, ...value }) => {
259
- return /* @__PURE__ */ React3.createElement(TableContext.Provider, {
260
- value
261
- }, children);
262
- };
263
- var useTableContext = () => {
264
- return useContext(TableContext) ?? raise(new Error("Missing TableContext"));
265
- };
266
-
267
- // packages/ui/react-ui-table/src/hooks/useColumnSorting.ts
268
- var useColumnSorting = () => {
269
- const [columnSorting, setColumnSorting] = useState4([]);
270
- const sortColumn = (columnId, sortDirection) => {
271
- setColumnSorting([
272
- {
273
- id: columnId,
274
- desc: sortDirection === "desc"
275
- }
276
- ]);
277
- };
278
- const clearColumnSorting = () => {
279
- setColumnSorting([]);
280
- };
281
- return {
282
- sorting: columnSorting,
283
- sortColumn,
284
- clearColumnSorting
285
- };
286
- };
287
- var useSortColumn = (column) => {
288
- const { sorting, sortColumn, clearColumnSorting } = useTableContext();
289
- const [initialSortDirection, setInitialSortDirection] = useState4();
290
- const columnSort = sorting.find((s) => s.id === column.id);
291
- const sortDirection = columnSort ? columnSort.desc ? "desc" : "asc" : void 0;
292
- const canSort = column.getCanSort();
293
- const updateColumnSort = (direction) => {
294
- sortColumn(column.id, direction);
295
- };
296
- const onToggleSort = () => {
297
- if (sortDirection === void 0) {
298
- return;
299
- }
300
- if (sortDirection === initialSortDirection) {
301
- updateColumnSort(initialSortDirection === "asc" ? "desc" : "asc");
85
+ if (state?.type === "columnSettings" && state.mode.type === "create" && model?.projection) {
86
+ setNewField(model.projection.createFieldProjection());
302
87
  } else {
303
- clearColumnSorting();
88
+ setNewField(void 0);
304
89
  }
305
- };
306
- const onSelectSort = (direction) => {
307
- clearColumnSorting();
308
- setInitialSortDirection(direction);
309
- updateColumnSort(direction);
310
- };
311
- const onClearSort = () => {
312
- clearColumnSorting();
313
- };
314
- return {
315
- canSort,
316
- sortDirection,
317
- onSelectSort,
318
- onToggleSort,
319
- onClearSort
320
- };
321
- };
322
-
323
- // packages/ui/react-ui-table/src/hooks/usePinLastRow.ts
324
- import { useCallback as useCallback3, useEffect as useEffect2, useState as useState5 } from "react";
325
- var usePinLastRow = (pinLastRow, table, data) => {
326
- const [pinnedRowKey, setPinnedRowKey] = useState5();
327
- const pinRow = useCallback3((rowToPin) => {
328
- table.resetRowPinning();
329
- rowToPin.pin("bottom");
330
- setPinnedRowKey(rowToPin.id);
331
90
  }, [
332
- table
333
- ]);
334
- const clearRowPinning = useCallback3(() => {
335
- table.resetRowPinning();
336
- setPinnedRowKey(void 0);
337
- }, [
338
- table
91
+ model?.projection,
92
+ state
339
93
  ]);
340
- const pinLastRowCallback = useCallback3(() => {
341
- const rows = table.getRowModel().rows;
342
- if (rows.length === 0) {
343
- return clearRowPinning();
344
- }
345
- const lastRow = rows[rows.length - 1];
346
- if (lastRow.id === pinnedRowKey) {
347
- return;
94
+ const existingField = useMemo(() => {
95
+ if (state?.type === "columnSettings") {
96
+ const { mode } = state;
97
+ if (mode.type === "edit") {
98
+ return model?.table?.view?.fields.find((f) => f.id === mode.fieldId);
99
+ }
348
100
  }
349
- pinRow(lastRow);
101
+ return void 0;
350
102
  }, [
351
- table,
352
- pinnedRowKey
103
+ model?.table?.view?.fields,
104
+ state
353
105
  ]);
354
- useEffect2(() => {
355
- if (pinLastRow) {
356
- pinLastRowCallback();
357
- }
106
+ const field = existingField ?? newField;
107
+ const handleClose = useCallback(() => {
108
+ model?.modalController.close();
358
109
  }, [
359
- data,
360
- pinLastRow,
361
- pinLastRowCallback
110
+ model?.modalController
362
111
  ]);
363
- };
364
-
365
- // packages/ui/react-ui-table/src/hooks/useRowSelection.ts
366
- import { useControllableState } from "@radix-ui/react-use-controllable-state";
367
- import { useCallback as useCallback4 } from "react";
368
- var useRowSelection = (props) => {
369
- const [rowSelection = {}, setRowSelection] = useControllableState({
370
- prop: props.rowSelection,
371
- onChange: props.onRowSelectionChange,
372
- defaultProp: props.defaultRowSelection
373
- });
374
- const handleRowSelectionChange = useCallback4((updaterOrValue) => {
375
- const nextRowSelection = typeof updaterOrValue === "function" ? updaterOrValue(rowSelection) : updaterOrValue;
376
- if (props.rowsSelectable === "multi") {
377
- setRowSelection(nextRowSelection);
378
- } else if (props.rowsSelectable) {
379
- const nextRowSelectionKey = Object.keys(nextRowSelection).filter((id) => !rowSelection[id])[0];
380
- setRowSelection(nextRowSelectionKey ? {
381
- [nextRowSelectionKey]: true
382
- } : {});
383
- } else {
384
- setRowSelection({});
112
+ const handleCancel = useCallback(() => {
113
+ if (state?.type === "columnSettings" && state.mode.type === "create" && newField) {
114
+ model?.projection?.deleteFieldProjection(newField.id);
385
115
  }
386
116
  }, [
387
- props.rowsSelectable,
388
- setRowSelection,
389
- rowSelection
117
+ model?.projection,
118
+ state,
119
+ newField
390
120
  ]);
391
- return {
392
- rowSelection,
393
- handleRowSelectionChange
394
- };
121
+ if (!model?.table?.view || !model.projection || !field) {
122
+ return null;
123
+ }
124
+ return /* @__PURE__ */ React2.createElement(DropdownMenu2.Root, {
125
+ modal: false,
126
+ open: state?.type === "columnSettings"
127
+ }, /* @__PURE__ */ React2.createElement(DropdownMenu2.VirtualTrigger, {
128
+ virtualRef: model.modalController.trigger
129
+ }), /* @__PURE__ */ React2.createElement(DropdownMenu2.Portal, null, /* @__PURE__ */ React2.createElement(DropdownMenu2.Content, {
130
+ classNames: "md:is-64"
131
+ }, /* @__PURE__ */ React2.createElement(DropdownMenu2.Viewport, null, /* @__PURE__ */ React2.createElement(FieldEditor, {
132
+ view: model.table.view,
133
+ projection: model.projection,
134
+ field,
135
+ registry: space?.db.schemaRegistry,
136
+ onClose: handleClose,
137
+ onCancel: handleCancel
138
+ })), /* @__PURE__ */ React2.createElement(DropdownMenu2.Arrow, null))));
395
139
  };
396
140
 
397
- // packages/ui/react-ui-table/src/components/ColumnMenu/ColumnMenu.tsx
398
- var ColumnMenu = ({ column, columnOrderable, columnPosition, onColumnReorder, ...props }) => {
399
- const title = column.label?.length ? column.label : column.id;
400
- const header = props.context.header;
401
- const { canSort, sortDirection, onSelectSort, onToggleSort, onClearSort } = useSortColumn(header.column);
402
- const columnSettingsAnchorRef = useRef2(null);
403
- const [isColumnSettingsOpen, setIsColumnSettingsOpen] = useState6(false);
404
- const shouldPreventFocusOnClose = useRef2(false);
405
- const onOpenColumnSettings = useCallback5(() => {
406
- shouldPreventFocusOnClose.current = true;
407
- setIsColumnSettingsOpen(true);
408
- }, [
409
- shouldPreventFocusOnClose,
410
- setIsColumnSettingsOpen
411
- ]);
412
- const onDropdownCloseAutoFocus = useCallback5((event) => {
413
- if (shouldPreventFocusOnClose.current) {
414
- event.preventDefault();
415
- shouldPreventFocusOnClose.current = false;
416
- }
417
- }, [
418
- shouldPreventFocusOnClose
419
- ]);
420
- return /* @__PURE__ */ React4.createElement("div", {
421
- className: "flex items-center justify-between"
422
- }, /* @__PURE__ */ React4.createElement("div", {
423
- className: "flex items-center gap-1 truncate",
424
- title
425
- }, /* @__PURE__ */ React4.createElement(SortIndicator, {
426
- direction: sortDirection,
427
- onClick: onToggleSort
428
- }), /* @__PURE__ */ React4.createElement("div", {
429
- className: "truncate"
430
- }, title)), /* @__PURE__ */ React4.createElement(DropdownMenu.Root, {
431
- modal: false
432
- }, /* @__PURE__ */ React4.createElement(Popover2.Root, {
433
- open: isColumnSettingsOpen,
434
- onOpenChange: setIsColumnSettingsOpen,
435
- modal: false
436
- }, /* @__PURE__ */ React4.createElement(DropdownMenu.Trigger, {
437
- ref: columnSettingsAnchorRef,
438
- asChild: true
439
- }, /* @__PURE__ */ React4.createElement(Popover2.Anchor, {
440
- asChild: true
441
- }, /* @__PURE__ */ React4.createElement(Button2, {
442
- variant: "ghost",
443
- "data-testid": "table.column-menu"
444
- }, /* @__PURE__ */ React4.createElement(CaretDown2, {
445
- className: getSize2(4)
446
- })))), /* @__PURE__ */ React4.createElement(DropdownMenu.Portal, null, /* @__PURE__ */ React4.createElement(DropdownMenu.Content, {
447
- onCloseAutoFocus: onDropdownCloseAutoFocus,
448
- sideOffset: 4,
449
- collisionPadding: 8
450
- }, /* @__PURE__ */ React4.createElement(DropdownMenu.Viewport, null, canSort && /* @__PURE__ */ React4.createElement(React4.Fragment, null, /* @__PURE__ */ React4.createElement(DropdownMenu.Item, {
451
- onClick: () => onSelectSort("asc")
452
- }, /* @__PURE__ */ React4.createElement("span", {
453
- className: "grow"
454
- }, "Sort ascending"), /* @__PURE__ */ React4.createElement("span", {
455
- className: "opacity-50"
456
- }, /* @__PURE__ */ React4.createElement(ArrowUp, {
457
- className: getSize2(4)
458
- }))), /* @__PURE__ */ React4.createElement(DropdownMenu.Item, {
459
- onClick: () => onSelectSort("desc")
460
- }, /* @__PURE__ */ React4.createElement("span", {
461
- className: "grow"
462
- }, "Sort descending"), /* @__PURE__ */ React4.createElement("span", {
463
- className: "opacity-50"
464
- }, /* @__PURE__ */ React4.createElement(ArrowDown, {
465
- className: getSize2(4)
466
- }))), sortDirection && /* @__PURE__ */ React4.createElement(DropdownMenu.Item, {
467
- onClick: onClearSort
468
- }, /* @__PURE__ */ React4.createElement("span", {
469
- className: "grow"
470
- }, "Clear sort"), /* @__PURE__ */ React4.createElement("span", {
471
- className: "opacity-50"
472
- }, /* @__PURE__ */ React4.createElement(X, {
473
- className: getSize2(4)
474
- })))), columnOrderable && /* @__PURE__ */ React4.createElement(React4.Fragment, null, columnPosition !== "start" && /* @__PURE__ */ React4.createElement(DropdownMenu.Item, {
475
- onClick: () => onColumnReorder(header.column.id, "left")
476
- }, /* @__PURE__ */ React4.createElement("span", {
477
- className: "grow"
478
- }, "Move left"), /* @__PURE__ */ React4.createElement("span", {
479
- className: "opacity-50"
480
- }, /* @__PURE__ */ React4.createElement(ArrowLeft, {
481
- className: mx2(getSize2(4))
482
- }))), columnPosition !== "end" && /* @__PURE__ */ React4.createElement(DropdownMenu.Item, {
483
- onClick: () => onColumnReorder(header.column.id, "right")
484
- }, /* @__PURE__ */ React4.createElement("span", {
485
- className: "grow"
486
- }, "Move right"), /* @__PURE__ */ React4.createElement("span", {
487
- className: "opacity-50"
488
- }, /* @__PURE__ */ React4.createElement(ArrowRight, {
489
- className: mx2(getSize2(4))
490
- })))), /* @__PURE__ */ React4.createElement(DropdownMenu.Item, {
491
- onClick: onOpenColumnSettings,
492
- "data-testid": "table.open-column-settings"
493
- }, /* @__PURE__ */ React4.createElement("span", {
494
- className: "grow"
495
- }, "Column settings"), /* @__PURE__ */ React4.createElement("span", {
496
- className: "opacity-50"
497
- }, /* @__PURE__ */ React4.createElement(GearSix, {
498
- className: mx2(getSize2(4), "rotate-90")
499
- })))), /* @__PURE__ */ React4.createElement(DropdownMenu.Arrow, null))), /* @__PURE__ */ React4.createElement(Popover2.Portal, null, /* @__PURE__ */ React4.createElement(Popover2.Content, null, /* @__PURE__ */ React4.createElement(Popover2.Viewport, null, /* @__PURE__ */ React4.createElement(ColumnSettings, {
500
- ...props,
501
- column,
502
- onClose: () => setIsColumnSettingsOpen(false)
503
- })), /* @__PURE__ */ React4.createElement(Popover2.Arrow, null))))));
504
- };
505
- var SortIndicator = ({ direction, onClick }) => {
506
- if (direction === void 0) {
141
+ // packages/ui/react-ui-table/src/components/Table/RowActionsMenu.tsx
142
+ import React3 from "react";
143
+ import { DropdownMenu as DropdownMenu3, useTranslation as useTranslation2 } from "@dxos/react-ui";
144
+ var RowActionsMenu = ({ model }) => {
145
+ const { t } = useTranslation2(translationKey);
146
+ const state = model.modalController.state.value;
147
+ const hasSelection = model.selection.hasSelection.value;
148
+ if (state?.type !== "row") {
507
149
  return null;
508
150
  }
509
- let icon;
510
- switch (direction) {
511
- case "asc":
512
- icon = /* @__PURE__ */ React4.createElement(ArrowUp, {
513
- className: getSize2(3)
514
- });
515
- break;
516
- case "desc":
517
- icon = /* @__PURE__ */ React4.createElement(ArrowDown, {
518
- className: getSize2(3)
519
- });
520
- break;
521
- }
522
- return /* @__PURE__ */ React4.createElement("div", {
523
- onClick,
524
- className: "flex items-center cursor-pointer"
525
- }, icon);
151
+ return /* @__PURE__ */ React3.createElement(DropdownMenu3.Root, {
152
+ modal: false,
153
+ open: true,
154
+ onOpenChange: model.modalController.close
155
+ }, /* @__PURE__ */ React3.createElement(DropdownMenu3.VirtualTrigger, {
156
+ virtualRef: model.modalController.trigger
157
+ }), /* @__PURE__ */ React3.createElement(DropdownMenu3.Content, null, /* @__PURE__ */ React3.createElement(DropdownMenu3.Viewport, null, /* @__PURE__ */ React3.createElement(DropdownMenu3.Item, {
158
+ onClick: () => model.deleteRow(state.rowIndex)
159
+ }, t(hasSelection ? "bulk delete row label" : "delete row label"))), /* @__PURE__ */ React3.createElement(DropdownMenu3.Arrow, null)));
526
160
  };
527
161
 
528
- // packages/ui/react-ui-table/src/components/Table/Table.tsx
529
- import { Slot } from "@radix-ui/react-slot";
530
- import { getCoreRowModel, getExpandedRowModel, getGroupedRowModel, getSortedRowModel, useReactTable } from "@tanstack/react-table";
531
- import React8, { Fragment, memo, useCallback as useCallback7, useEffect as useEffect3, useState as useState8, useContext as useContext3 } from "react";
532
- import { useDefaultValue } from "@dxos/react-ui";
533
- import { mx as mx4 } from "@dxos/react-ui-theme";
162
+ // packages/ui/react-ui-table/src/components/TableCellEditor/TableCellEditor.tsx
163
+ import React4, { useCallback as useCallback2, useMemo as useMemo2 } from "react";
164
+ import { FormatEnum as FormatEnum2 } from "@dxos/echo-schema";
165
+ import { invariant } from "@dxos/invariant";
166
+ import { editorKeys, GridCellEditor, useGridContext } from "@dxos/react-ui-grid";
534
167
 
535
- // packages/ui/react-ui-table/src/components/Table/TableBody.tsx
536
- import { useArrowNavigationGroup } from "@fluentui/react-tabster";
537
- import { flexRender } from "@tanstack/react-table";
538
- import React6, { useContext as useContext2 } from "react";
168
+ // packages/ui/react-ui-table/src/components/TableCellEditor/extension.ts
169
+ import { autocompletion, pickedCompletion } from "@codemirror/autocomplete";
170
+ import { EditorView } from "@codemirror/view";
171
+ var completion = ({ onQuery, onMatch }) => {
172
+ return [
173
+ EditorView.theme({
174
+ ".cm-completionDialog": {
175
+ width: "var(--dx-gridCellWidth)"
176
+ }
177
+ }),
178
+ autocompletion({
179
+ icons: false,
180
+ activateOnTyping: true,
181
+ override: [
182
+ async (context) => {
183
+ const text = context.state.doc.toString();
184
+ return {
185
+ from: 0,
186
+ options: await onQuery(text)
187
+ };
188
+ }
189
+ ],
190
+ // TODO(burdon): Consts from grid (to line-up with grid).
191
+ tooltipClass: () => "cm-completionDialog !mt-[8px] !-ml-[4px] [&>ul]:!max-h-[264px]",
192
+ optionClass: () => "flex h-[33px] items-center"
193
+ }),
194
+ // Check for accepted completion metadata in the transaction.
195
+ EditorView.updateListener.of((update) => {
196
+ update.transactions.forEach((tr) => {
197
+ const completion2 = tr.annotation(pickedCompletion);
198
+ if (completion2) {
199
+ onMatch(completion2.data);
200
+ }
201
+ });
202
+ })
203
+ ];
204
+ };
539
205
 
540
- // packages/ui/react-ui-table/src/components/Table/TableRootContext.ts
541
- import { useVirtualizer } from "@tanstack/react-virtual";
542
- import { createContext as createContext2, useRef as useRef3, useState as useState7 } from "react";
543
- var useTableRootContext = () => {
544
- const scrollContextRef = useRef3(null);
545
- const [tableCount, setTableCount] = useState7(0);
546
- const virtualizer = useVirtualizer({
547
- getScrollElement: () => scrollContextRef?.current ?? null,
548
- count: tableCount,
549
- overscan: 8,
550
- estimateSize: () => 40
551
- });
552
- const dispatch = (action) => {
553
- switch (action.type) {
554
- case "updateTableCount":
555
- setTableCount(action.count);
556
- break;
557
- default:
558
- break;
559
- }
560
- };
206
+ // packages/ui/react-ui-table/src/util/grid.ts
207
+ var fromGridCell = ({ col, row }) => {
208
+ return `${col},${row}`;
209
+ };
210
+ var toGridCell = (cell) => {
211
+ const [col, row] = cell.split(",").map(Number);
561
212
  return {
562
- scrollContextRef,
563
- virtualizer,
564
- dispatch
213
+ row,
214
+ col
565
215
  };
566
216
  };
567
- var TableRootContext = createContext2({});
568
217
 
569
- // packages/ui/react-ui-table/src/theme.ts
570
- import { focusRing, ghostSelected, ghostSelectedCurrent, mx as mx3 } from "@dxos/react-ui-theme";
571
- var currentRow = "!bg-neutral-75 !dark:bg-neutral-850";
572
- var selectedRow = "!bg-primary-100 dark:!bg-primary-700";
573
- var flushPadding = "pli-0 plb-0";
574
- var textPadding = "pli-2 plb-0";
575
- var headPadding = "pli-2 plb-0";
576
- var gridCellFocusRing = "relative focus-within:outline focus-within:outline-1 outline-primary-500 outline-offset-[-1px]";
577
- var borderColors = "border-neutral-200 dark:border-neutral-700";
578
- var stickyRowColors = "bg-neutral-50 dark:bg-neutral-800";
579
- var tableRoot = ({ fullWidth }, ...etc) => mx3("table-fixed", "text-neutral-800 dark:text-neutral-200", fullWidth && "is-full", ...etc);
580
- var groupTh = (_props, ...etc) => mx3("text-start font-medium", flushPadding, ...etc);
581
- var theadRoot = ({ header, stickyHeader }, ...etc) => mx3(header ? stickyHeader && "sticky block-start-[--sticky-top] z-[1]" : "collapse", stickyRowColors, ...etc);
582
- var theadTr = (_props, ...etc) => mx3("group", ...etc);
583
- var theadTh = ({ border }, ...etc) => mx3("relative", "text-start text-xs select-none truncate", headPadding, border && borderColors, border && "border-t border-b border-l last:border-r", ...etc);
584
- var theadResizeRoot = ({ isResizing }) => {
585
- return mx3("absolute h-full w-[4px] top-0 right-0", "cursor-col-resize", "user-select-none", "touch-action-none", "pointer-coarse:bg-neutral-100 dark:pointer-coarse:bg-neutral-800", !isResizing && "group-hover:bg-neutral-100 dark:group-hover:bg-neutral-800", isResizing && "bg-primary-500");
586
- };
587
- var tbodyRoot = (_props, ...etc) => mx3(...etc);
588
- var tbodyTr = ({ canBeCurrent, isPinned }, ...etc) => mx3("group", canBeCurrent ? ghostSelectedCurrent : ghostSelected, canBeCurrent && focusRing, canBeCurrent && "cursor-pointer", isPinned && "sticky z-1 block-end-0", isPinned && stickyRowColors, ...etc);
589
- var tdRoot = ({ border, isGrid, pinned }, ...etc) => mx3("relative", "focus-within:!bg-neutral-50 dark:focus-within:!bg-neutral-900", flushPadding, border && "border-b border-l last:border-r border-neutral-200 dark:border-neutral-700", pinned && "border-t", isGrid && gridCellFocusRing, ...etc);
218
+ // packages/ui/react-ui-table/src/util/signals.ts
219
+ var touch = (value) => value;
590
220
 
591
- // packages/ui/react-ui-table/src/components/Cell/Cell.tsx
592
- import { useFocusableGroup } from "@fluentui/react-tabster";
593
- import React5, { useCallback as useCallback6 } from "react";
594
- var CELL_NAME = "Cell";
595
- var FocusableCell = ({ cell, children }) => {
596
- const tableContext = useTableContext();
597
- const domAttributes = useFocusableGroup({
598
- tabBehavior: "limited"
221
+ // packages/ui/react-ui-table/src/util/table.ts
222
+ import { AST, Format, FormatEnum, S, TypedObject, TypeEnum } from "@dxos/echo-schema";
223
+ import { log } from "@dxos/log";
224
+ import { PublicKey } from "@dxos/react-client";
225
+ import { create } from "@dxos/react-client/echo";
226
+ import { createFieldId, createView, ViewProjection } from "@dxos/schema";
227
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/util/table.ts";
228
+ var initializeTable = ({ space, table }) => {
229
+ log.info("initializeTable", {
230
+ table
231
+ }, {
232
+ F: __dxlog_file,
233
+ L: 26,
234
+ S: void 0,
235
+ C: (f, a) => f(...a)
599
236
  });
600
- const pinned = tableContext.table.getState().rowPinning?.bottom?.includes(cell.row.id);
601
- const className = tdRoot({
602
- ...tableContext,
603
- pinned
604
- }, cell.column.columnDef.meta?.cell?.classNames);
605
- const handleKeyDown = useCallback6(({ key, currentTarget }) => {
606
- if (key !== "Enter") {
607
- return;
608
- }
609
- const currentRow2 = currentTarget.closest("tr");
610
- if (!currentRow2) {
611
- return;
237
+ const ContactSchema = TypedObject({
238
+ typename: `example.com/type/${PublicKey.random().truncate()}`,
239
+ version: "0.1.0"
240
+ })({
241
+ name: S.optional(S.String).annotations({
242
+ [AST.TitleAnnotationId]: "Name"
243
+ }),
244
+ email: S.optional(Format.Email),
245
+ salary: S.optional(Format.Currency()).annotations({
246
+ [AST.TitleAnnotationId]: "Salary"
247
+ })
248
+ });
249
+ const contactSchema = space.db.schemaRegistry.addSchema(ContactSchema);
250
+ table.view = createView({
251
+ name: "Test",
252
+ typename: contactSchema.typename,
253
+ jsonSchema: contactSchema.jsonSchema,
254
+ fields: [
255
+ "name",
256
+ "email",
257
+ "salary"
258
+ ]
259
+ });
260
+ const projection = new ViewProjection(contactSchema, table.view);
261
+ projection.setFieldProjection({
262
+ field: {
263
+ id: table.view.fields[2].id,
264
+ path: "salary",
265
+ size: 150
612
266
  }
613
- const cellIndex = Array.from(currentRow2.cells || []).indexOf(currentTarget);
614
- const nextSibling = currentRow2.nextElementSibling;
615
- if (!(nextSibling instanceof HTMLTableRowElement)) {
616
- let depth = 0;
617
- const pollForNewRow = () => {
618
- const newNextSibling = currentRow2.nextElementSibling;
619
- if (newNextSibling instanceof HTMLTableRowElement) {
620
- const nextCell = newNextSibling.cells[cellIndex];
621
- const input = nextCell.querySelector("input");
622
- if (input) {
623
- input.focus();
624
- }
625
- currentRow2.scrollIntoView({
626
- behavior: "smooth",
627
- block: "center"
628
- });
629
- } else if (depth++ < 100) {
630
- requestAnimationFrame(pollForNewRow);
631
- }
632
- };
633
- requestAnimationFrame(pollForNewRow);
267
+ });
268
+ projection.setFieldProjection({
269
+ field: {
270
+ id: createFieldId(),
271
+ path: "manager",
272
+ referencePath: "name"
273
+ },
274
+ props: {
275
+ property: "manager",
276
+ type: TypeEnum.Ref,
277
+ format: FormatEnum.Ref,
278
+ referenceSchema: contactSchema.typename,
279
+ title: "Manager"
634
280
  }
635
- }, []);
636
- return /* @__PURE__ */ React5.createElement("td", {
637
- "data-testid": "table.cell",
638
- tabIndex: 0,
639
- ...domAttributes,
640
- className,
641
- onKeyDown: handleKeyDown
642
- }, children);
281
+ });
282
+ space.db.add(create(contactSchema, {}));
283
+ return contactSchema;
643
284
  };
644
- var StaticCell = ({ cell, children }) => {
645
- const tableContext = useTableContext();
646
- return /* @__PURE__ */ React5.createElement("td", {
647
- "data-testid": "table.cell",
648
- className: tdRoot(tableContext, cell.column.columnDef.meta?.cell?.classNames)
649
- }, children);
285
+
286
+ // packages/ui/react-ui-table/src/util/table-buttons.ts
287
+ var TABLE_ATTRS = {
288
+ columnSettings: "data-table-column-settings-button",
289
+ rowMenu: "data-table-row-menu-button",
290
+ newColumn: "data-table-new-column-button"
650
291
  };
651
- var Cell = ({ cell, children }) => {
652
- const { isGrid } = useTableContext();
653
- const Root = isGrid ? FocusableCell : StaticCell;
654
- return /* @__PURE__ */ React5.createElement(Root, {
655
- cell
656
- }, children);
292
+ var ICONS = {
293
+ columnSettings: "ph--caret-down--regular",
294
+ rowMenu: "ph--dots-three--regular",
295
+ newColumn: "ph--plus--regular"
296
+ };
297
+ var createButtonHtml = ({ button, value = "", disabled = false }) => {
298
+ const buttonClasses = "ch-button is-6 pli-0.5 min-bs-0 absolute inset-block-1 inline-end-2";
299
+ return `<button class="${buttonClasses}" ${TABLE_ATTRS[button]}="${value}" ${disabled ? "disabled" : ""}><svg><use href="/icons.svg#${ICONS[button]}"/></svg></button>`;
300
+ };
301
+ var tableButtons = {
302
+ columnSettings: {
303
+ attr: TABLE_ATTRS.columnSettings,
304
+ icon: ICONS.columnSettings,
305
+ render: ({ fieldId }) => createButtonHtml({
306
+ button: "columnSettings",
307
+ value: fieldId
308
+ })
309
+ },
310
+ rowMenu: {
311
+ attr: TABLE_ATTRS.rowMenu,
312
+ icon: ICONS.rowMenu,
313
+ render: ({ rowIndex }) => createButtonHtml({
314
+ button: "rowMenu",
315
+ value: rowIndex.toString()
316
+ })
317
+ },
318
+ addColumn: {
319
+ attr: TABLE_ATTRS.newColumn,
320
+ icon: ICONS.newColumn,
321
+ render: ({ disabled } = {}) => createButtonHtml({
322
+ button: "newColumn",
323
+ disabled
324
+ })
325
+ }
657
326
  };
658
- Cell.displayName = CELL_NAME;
659
327
 
660
- // packages/ui/react-ui-table/src/components/Table/TableBody.tsx
661
- var TABLE_BODY_NAME = "TableBody";
662
- var TableBody = ({ rows }) => {
663
- const { table, keyAccessor, currentDatum, debug, expand, isGrid, onDatumClick } = useTableContext();
664
- const rowSelection = table.getState().rowSelection;
665
- const domAttributes = useArrowNavigationGroup({
666
- axis: "grid"
667
- });
668
- const canBeCurrent = !isGrid && !!onDatumClick;
669
- const { virtualizer } = useContext2(TableRootContext);
670
- return /* @__PURE__ */ React6.createElement("tbody", isGrid && domAttributes, rows.map((row) => {
671
- const isCurrent = currentDatum === row.original;
672
- const isSelected = rowSelection?.[row.id];
673
- const isPinned = row.getIsPinned();
674
- const classNames = tbodyTr({
675
- canBeCurrent,
676
- isPinned: isPinned !== false
328
+ // packages/ui/react-ui-table/src/components/TableCellEditor/TableCellEditor.tsx
329
+ var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/components/TableCellEditor/TableCellEditor.tsx";
330
+ var TableCellEditor = ({ model, onEnter, onFocus, onQuery, __gridScope }) => {
331
+ const { editing } = useGridContext("TableCellEditor", __gridScope);
332
+ const fieldProjection = useMemo2(() => {
333
+ if (!model || !editing) {
334
+ return;
335
+ }
336
+ const { col } = toGridCell(editing.index);
337
+ const field = model.projection.view.fields[col];
338
+ const fieldProjection2 = model.projection.getFieldProjection(field.id);
339
+ invariant(fieldProjection2, void 0, {
340
+ F: __dxlog_file2,
341
+ L: 49,
342
+ S: void 0,
343
+ A: [
344
+ "fieldProjection",
345
+ ""
346
+ ]
677
347
  });
678
- return /* @__PURE__ */ React6.createElement("tr", {
679
- key: keyAccessor ? keyAccessor(row.original) : row.id,
680
- className: classNames,
681
- "data-index": row.index,
682
- "data-testid": "table.data-row",
683
- ref: virtualizer.measureElement,
684
- ...isCurrent && {
685
- "aria-current": "location"
686
- },
687
- ...isSelected && {
688
- "aria-selected": "true"
689
- },
690
- ...canBeCurrent && {
691
- tabIndex: 0,
692
- onClick: () => {
693
- onDatumClick(row.original);
694
- },
695
- onKeyUp: ({ key }) => {
696
- if (key === "Enter" || key === " ") {
697
- onDatumClick(row.original);
698
- }
348
+ return fieldProjection2;
349
+ }, [
350
+ model,
351
+ editing
352
+ ]);
353
+ const handleClose = useCallback2((value, event) => {
354
+ if (!model || !editing || !fieldProjection) {
355
+ return;
356
+ }
357
+ const cell = toGridCell(editing.index);
358
+ onEnter?.(cell);
359
+ if (event && onFocus) {
360
+ onFocus(determineNavigationAxis(event), determineNavigationDelta(event));
361
+ }
362
+ }, [
363
+ model,
364
+ editing,
365
+ onFocus,
366
+ onEnter,
367
+ fieldProjection,
368
+ determineNavigationAxis,
369
+ determineNavigationDelta
370
+ ]);
371
+ const handleBlur = useCallback2((value) => {
372
+ if (!model || !editing) {
373
+ return;
374
+ }
375
+ const cell = toGridCell(editing.index);
376
+ if (value !== void 0) {
377
+ model.setCellData(cell, value);
378
+ }
379
+ }, [
380
+ model,
381
+ editing
382
+ ]);
383
+ const extension = useMemo2(() => {
384
+ if (!fieldProjection) {
385
+ return [];
386
+ }
387
+ const extension2 = [
388
+ editorKeys({
389
+ onClose: handleClose,
390
+ ...editing?.initialContent && {
391
+ onNav: handleClose
392
+ }
393
+ })
394
+ ];
395
+ if (onQuery) {
396
+ switch (fieldProjection.props.format) {
397
+ case FormatEnum2.Ref: {
398
+ extension2.push([
399
+ completion({
400
+ onQuery: (text) => onQuery(fieldProjection, text),
401
+ onMatch: (data) => {
402
+ if (model && editing) {
403
+ const cell = toGridCell(editing.index);
404
+ model.setCellData(cell, data);
405
+ onEnter?.(cell);
406
+ onFocus?.();
407
+ }
408
+ }
409
+ })
410
+ ]);
411
+ break;
699
412
  }
700
413
  }
701
- }, debug && /* @__PURE__ */ React6.createElement("td", null, row.id), row.getVisibleCells().map((cell) => {
702
- return /* @__PURE__ */ React6.createElement(Cell, {
703
- key: cell.id,
704
- cell
705
- }, flexRender(cell.column.columnDef.cell, {
706
- ...cell.getContext()
707
- }));
708
- }), expand && /* @__PURE__ */ React6.createElement("td", null));
709
- }));
414
+ }
415
+ return extension2;
416
+ }, [
417
+ model,
418
+ editing,
419
+ fieldProjection,
420
+ handleClose
421
+ ]);
422
+ const getCellContent = useCallback2(() => {
423
+ if (model && editing) {
424
+ const value = model.getCellData(toGridCell(editing.index));
425
+ return value !== void 0 ? String(value) : "";
426
+ }
427
+ }, [
428
+ model,
429
+ editing
430
+ ]);
431
+ return /* @__PURE__ */ React4.createElement(GridCellEditor, {
432
+ extension,
433
+ getCellContent,
434
+ onBlur: handleBlur
435
+ });
710
436
  };
711
- TableBody.displayName = TABLE_BODY_NAME;
712
-
713
- // packages/ui/react-ui-table/src/components/Table/TableHead.tsx
714
- import { flexRender as flexRender2 } from "@tanstack/react-table";
715
- import React7 from "react";
716
- var TABLE_HEAD_NAME = "TableHeader";
717
- var TableHead = (_props) => {
718
- const tableContext = useTableContext();
719
- const headerGroups = tableContext.table.getHeaderGroups();
720
- return /* @__PURE__ */ React7.createElement("thead", {
721
- className: theadRoot(tableContext)
722
- }, headerGroups.map((headerGroup) => {
723
- return (
724
- // Group element to hover resize handles.
725
- /* @__PURE__ */ React7.createElement("tr", {
726
- key: headerGroup.id,
727
- className: theadTr(tableContext)
728
- }, tableContext.debug && /* @__PURE__ */ React7.createElement("th", {
729
- className: "font-mono",
730
- style: {
731
- width: 32
732
- }
733
- }, "#"), headerGroup.headers.map((header) => {
734
- const isResizing = header.column.getIsResizing();
735
- const onResize = header.getResizeHandler();
736
- const resizable = header.column.columnDef.meta?.resizable;
737
- return /* @__PURE__ */ React7.createElement("th", {
738
- key: header.id,
739
- style: {
740
- width: header.getSize()
741
- },
742
- className: theadTh(tableContext, header.column.columnDef.meta?.header?.classNames),
743
- "data-testid": "table.header-cell"
744
- }, !header || header.isPlaceholder ? null : flexRender2(header.column.columnDef.header, header.getContext()), resizable && /* @__PURE__ */ React7.createElement("div", {
745
- className: theadResizeRoot({
746
- isResizing
747
- }),
748
- onDoubleClick: header.column.resetSize,
749
- onMouseDown: onResize,
750
- onTouchStart: onResize
751
- }));
752
- }), tableContext.expand && /* @__PURE__ */ React7.createElement("th", null))
753
- );
754
- }));
437
+ var determineNavigationAxis = ({ key }) => {
438
+ switch (key) {
439
+ case "ArrowUp":
440
+ case "ArrowDown":
441
+ case "Enter": {
442
+ return "row";
443
+ }
444
+ case "ArrowLeft":
445
+ case "ArrowRight":
446
+ case "Tab":
447
+ return "col";
448
+ }
449
+ };
450
+ var determineNavigationDelta = ({ key, shift }) => {
451
+ switch (key) {
452
+ case "ArrowUp":
453
+ case "ArrowLeft":
454
+ return -1;
455
+ case "ArrowDown":
456
+ case "ArrowRight":
457
+ return 1;
458
+ }
459
+ return shift ? -1 : 1;
755
460
  };
756
- TableHead.displayName = TABLE_HEAD_NAME;
757
461
 
758
462
  // packages/ui/react-ui-table/src/components/Table/Table.tsx
759
- var TableRoot = ({ children }) => {
760
- const contextValue = useTableRootContext();
761
- return /* @__PURE__ */ React8.createElement(TableRootContext.Provider, {
762
- value: contextValue
763
- }, children);
463
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/components/Table/Table.tsx";
464
+ var inlineEndLine = "[&>.dx-grid]:relative [&>.dx-grid]:after:absolute [&>.dx-grid]:after:inset-block-0 [&>.dx-grid]:after:-inline-end-px [&>.dx-grid]:after:is-px [&>.dx-grid]:after:bg-separator";
465
+ var blockEndLine = "[&>.dx-grid]:before:absolute [&>.dx-grid]:before:inset-inline-0 [&>.dx-grid]:before:-block-end-px [&>.dx-grid]:before:bs-px [&>.dx-grid]:before:bg-separator";
466
+ var frozen = {
467
+ frozenRowsStart: 1,
468
+ frozenColsStart: 1,
469
+ frozenColsEnd: 1
764
470
  };
765
- var TableViewport = ({ children, classNames, asChild, ...props }) => {
766
- const { scrollContextRef } = useContext3(TableRootContext);
767
- const classes = mx4(classNames, "overflow-auto");
768
- return asChild ? /* @__PURE__ */ React8.createElement(Slot, {
769
- ref: scrollContextRef,
770
- className: classes,
771
- ...props
772
- }, children) : /* @__PURE__ */ React8.createElement("div", {
471
+ var TableRoot = ({ children }) => {
472
+ return /* @__PURE__ */ React5.createElement("div", {
773
473
  role: "none",
774
- className: classes,
775
- ref: scrollContextRef,
776
- ...props
474
+ className: "relative flex flex-col"
777
475
  }, children);
778
476
  };
779
- var TablePrimitive = (props) => {
780
- const { role, header = true, rowsSelectable, debug, pinLastRow, columnVisibility, onColumnResize, onDataSelectionChange } = props;
781
- const columns = useDefaultValue(props.columns, () => []);
782
- const incomingData = useDefaultValue(props.data, () => []);
783
- const [data, setData] = useState8([
784
- ...incomingData
477
+ var TableMain = /* @__PURE__ */ forwardRef(({ model }, forwardedRef) => {
478
+ const [dxGrid, setDxGrid] = useState2(null);
479
+ const { hasAttention } = useAttention(model?.table ? fullyQualifiedId(model.table) : "table");
480
+ useImperativeHandle(forwardedRef, () => {
481
+ if (!model || !dxGrid) {
482
+ return {};
483
+ }
484
+ dxGrid.getCells = model.getCells.bind(model);
485
+ return {
486
+ update: (cell) => {
487
+ if (cell) {
488
+ dxGrid.updateIfWithinBounds(cell);
489
+ } else {
490
+ dxGrid.updateCells(true);
491
+ }
492
+ }
493
+ };
494
+ }, [
495
+ model,
496
+ dxGrid
785
497
  ]);
786
- const forceUpdate = useCallback7(() => setData([
787
- ...incomingData
788
- ]), [
789
- incomingData
498
+ const handleFocus = (increment, delta) => {
499
+ dxGrid?.refocus(increment, delta);
500
+ };
501
+ const handleEnter = useCallback3((cell) => {
502
+ if (model && cell.row === model.getRowCount() - 1) {
503
+ model.insertRow(cell.row);
504
+ }
505
+ }, [
506
+ model
790
507
  ]);
791
- useEffect3(() => forceUpdate(), [
792
- JSON.stringify(incomingData),
793
- forceUpdate
508
+ const handleKeyDown = useCallback3((event) => {
509
+ const cell = closestCell(event.target);
510
+ if (!model || !cell) {
511
+ return;
512
+ }
513
+ switch (event.key) {
514
+ case "Backspace":
515
+ case "Delete": {
516
+ model.setCellData(cell, void 0);
517
+ break;
518
+ }
519
+ }
520
+ }, [
521
+ model
794
522
  ]);
795
- const TableProvider2 = TableProvider;
796
- const { columnSizing, setColumnSizing, columnSizingInfo, setColumnSizingInfo } = useColumnResizing({
797
- columns,
798
- onColumnResize
799
- });
800
- const { rowSelection, handleRowSelectionChange } = useRowSelection(props);
801
- const [grouping, handleGroupingChange] = useState8(props.grouping ?? []);
802
- useEffect3(() => handleGroupingChange(props.grouping ?? []), [
803
- handleGroupingChange,
804
- props.grouping
523
+ const handleAxisResize = useCallback3((event) => {
524
+ if (event.axis === "col") {
525
+ const columnIndex = parseInt(event.index, 10);
526
+ model?.setColumnWidth(columnIndex, event.size);
527
+ }
528
+ }, [
529
+ model
805
530
  ]);
806
- const columnSorting = useColumnSorting();
807
- const table = useReactTable({
808
- // Data
809
- meta: {},
810
- data,
811
- // Columns
812
- columns,
813
- defaultColumn: {
814
- size: 400,
815
- maxSize: 1024
816
- },
817
- // State
818
- state: {
819
- columnVisibility,
820
- columnSizing,
821
- columnSizingInfo,
822
- sorting: columnSorting.sorting,
823
- rowSelection,
824
- grouping
825
- },
826
- // Resize columns
827
- columnResizeMode: "onChange",
828
- enableColumnResizing: true,
829
- onColumnSizingChange: setColumnSizing,
830
- onColumnSizingInfoChange: setColumnSizingInfo,
831
- // Rows
832
- getCoreRowModel: getCoreRowModel(),
833
- getSortedRowModel: getSortedRowModel(),
834
- getExpandedRowModel: getExpandedRowModel(),
835
- // Grouping
836
- getGroupedRowModel: grouping.length > 1 ? getGroupedRowModel() : void 0,
837
- onGroupingChange: handleGroupingChange,
838
- enableMultiRowSelection: rowsSelectable === "multi" ? true : void 0,
839
- enableRowSelection: rowsSelectable === true ? true : void 0,
840
- onRowSelectionChange: handleRowSelectionChange,
841
- enableSorting: true,
842
- enableColumnPinning: true,
843
- // Debug
844
- debugTable: debug
845
- });
846
- useEffect3(() => {
847
- onDataSelectionChange?.(Object.keys(rowSelection).map((id) => table.getRowModel().rowsById[id].original));
531
+ const handleWheel = useCallback3((event) => {
532
+ if (!hasAttention) {
533
+ event.stopPropagation();
534
+ }
848
535
  }, [
849
- onDataSelectionChange,
850
- rowSelection,
851
- table
536
+ hasAttention
852
537
  ]);
853
- usePinLastRow(pinLastRow, table, data);
854
- const expand = false;
855
- return /* @__PURE__ */ React8.createElement(TableProvider2, {
856
- ...props,
857
- ...columnSorting,
858
- table,
859
- header,
860
- expand,
861
- isGrid: role === "grid" || role === "treegrid"
862
- }, /* @__PURE__ */ React8.createElement(TableImpl, {
863
- debug: false,
864
- ...props
865
- }));
866
- };
867
- var TableImpl = (props) => {
868
- const { debug, classNames, role, grouping, fullWidth } = props;
869
- const { table } = useTableContext();
870
- if (debug) {
871
- return /* @__PURE__ */ React8.createElement("pre", {
872
- className: "font-mono text-xs text-neutral-500 m-1 p-2 ring"
873
- }, /* @__PURE__ */ React8.createElement("code", null, JSON.stringify(table.getState(), void 0, 2)));
874
- }
875
- const isResizingColumn = table.getState().columnSizingInfo.isResizingColumn;
876
- const TableComponent = isResizingColumn ? MemoizedVirtualizedTableContent : VirtualizedTableContent;
877
- return /* @__PURE__ */ React8.createElement("table", {
878
- role,
879
- className: tableRoot(props, classNames),
880
- style: {
881
- // borderCollapse: 'separate',
882
- borderSpacing: 0,
883
- ...!fullWidth && {
884
- width: table.getTotalSize()
538
+ const handleQuery = useCallback3(async ({ field, props }, _text) => {
539
+ if (model && props.referenceSchema && field.referencePath) {
540
+ const space = getSpace2(model.table);
541
+ invariant2(space, void 0, {
542
+ F: __dxlog_file3,
543
+ L: 149,
544
+ S: void 0,
545
+ A: [
546
+ "space",
547
+ ""
548
+ ]
549
+ });
550
+ const schema = space.db.schemaRegistry.getSchema(props.referenceSchema);
551
+ if (schema) {
552
+ const { objects } = await space.db.query(Filter.schema(schema)).run();
553
+ return objects.map((obj) => {
554
+ const value = getValue(obj, field.referencePath);
555
+ if (!value || typeof value !== "string") {
556
+ return void 0;
557
+ }
558
+ return {
559
+ label: value,
560
+ data: obj
561
+ };
562
+ }).filter(isNotFalsy);
885
563
  }
886
564
  }
887
- }, /* @__PURE__ */ React8.createElement(TableHead, null), grouping?.length !== 0 ? /* @__PURE__ */ React8.createElement(TableComponent, null) : /* @__PURE__ */ React8.createElement(GroupedTableContent, null));
888
- };
889
- var VirtualizedTableContent = () => {
890
- const { table } = useTableContext();
891
- const { virtualizer, dispatch } = useContext3(TableRootContext);
892
- const centerRows = table.getCenterRows();
893
- let pinnedRows = [];
894
- useEffect3(() => {
895
- dispatch({
896
- type: "updateTableCount",
897
- count: centerRows.length
898
- });
565
+ return [];
899
566
  }, [
900
- centerRows.length
567
+ model
901
568
  ]);
902
- try {
903
- pinnedRows = table.getBottomRows();
904
- } catch (_) {
569
+ if (!model) {
570
+ return /* @__PURE__ */ React5.createElement("span", {
571
+ role: "none",
572
+ className: "attention-surface"
573
+ });
905
574
  }
906
- const { getTotalSize, getVirtualItems } = virtualizer;
907
- const virtualRows = getVirtualItems();
908
- const totalSize = getTotalSize();
909
- const paddingTop = virtualRows.length > 0 ? virtualRows?.[0]?.start || 0 : 0;
910
- const paddingBottom = virtualRows.length > 0 ? totalSize - (virtualRows?.[virtualRows.length - 1]?.end || 0) : 0;
911
- const rowsToRender = [
912
- ...virtualRows.map((virtualRow) => centerRows[virtualRow.index]),
913
- ...pinnedRows
914
- ].filter((r) => !!r);
915
- return /* @__PURE__ */ React8.createElement(Fragment, null, paddingTop > 0 && /* @__PURE__ */ React8.createElement("tbody", {
916
- role: "none"
917
- }, /* @__PURE__ */ React8.createElement("tr", {
918
- role: "none"
919
- }, /* @__PURE__ */ React8.createElement("td", {
920
- style: {
921
- height: `${paddingTop}px`
922
- },
923
- role: "none"
924
- }))), /* @__PURE__ */ React8.createElement(TableBody, {
925
- rows: rowsToRender
926
- }), paddingBottom > 0 && /* @__PURE__ */ React8.createElement("tbody", {
927
- role: "none"
928
- }, /* @__PURE__ */ React8.createElement("tr", {
929
- role: "none"
930
- }, /* @__PURE__ */ React8.createElement("td", {
931
- style: {
932
- height: `${paddingBottom}px`
933
- },
934
- role: "none"
935
- }))));
936
- };
937
- var MemoizedVirtualizedTableContent = /* @__PURE__ */ memo(VirtualizedTableContent);
938
- var GroupedTableContent = () => {
939
- const { table: { getGroupedRowModel: getGroupedRowModel2, getHeaderGroups, getState }, debug } = useTableContext();
940
- return /* @__PURE__ */ React8.createElement(React8.Fragment, null, getGroupedRowModel2().rows.map((row, i) => {
941
- return /* @__PURE__ */ React8.createElement(Fragment, {
942
- key: i
943
- }, /* @__PURE__ */ React8.createElement("thead", null, /* @__PURE__ */ React8.createElement("tr", null, debug && /* @__PURE__ */ React8.createElement("th", null), /* @__PURE__ */ React8.createElement("th", {
944
- // TODO(burdon): Calculate row span.
945
- colSpan: getHeaderGroups()[0].headers.length,
946
- className: groupTh({})
947
- }, getState().grouping[0], "[", String(row.getGroupingValue(getState().grouping[0])), "]"))), /* @__PURE__ */ React8.createElement(TableBody, {
948
- rows: row.subRows
949
- }));
575
+ return /* @__PURE__ */ React5.createElement(React5.Fragment, null, /* @__PURE__ */ React5.createElement(Grid.Root, {
576
+ id: model.table.id ?? "table-grid"
577
+ }, /* @__PURE__ */ React5.createElement(TableCellEditor, {
578
+ model,
579
+ onEnter: handleEnter,
580
+ onFocus: handleFocus,
581
+ onQuery: handleQuery
582
+ }), /* @__PURE__ */ React5.createElement(Grid.Content, {
583
+ onWheelCapture: handleWheel,
584
+ className: mx("[--dx-grid-base:var(--surface-bg)] [&_.dx-grid]:grow [&_.dx-grid]:max-is-max [&_.dx-grid]:bs-0 [&_.dx-grid]:max-bs-max", inlineEndLine, blockEndLine),
585
+ frozen,
586
+ columns: model.columnMeta.value,
587
+ limitRows: model.getRowCount() ?? 0,
588
+ limitColumns: model.table.view?.fields?.length ?? 0,
589
+ onAxisResize: handleAxisResize,
590
+ onClick: model?.handleGridClick,
591
+ onKeyDown: handleKeyDown,
592
+ overscroll: "trap",
593
+ ref: setDxGrid
594
+ })), /* @__PURE__ */ React5.createElement(RowActionsMenu, {
595
+ model
596
+ }), /* @__PURE__ */ React5.createElement(ColumnActionsMenu, {
597
+ model
598
+ }), /* @__PURE__ */ React5.createElement(ColumnSettings, {
599
+ model
950
600
  }));
951
- };
601
+ });
952
602
  var Table = {
953
603
  Root: TableRoot,
954
- Viewport: TableViewport,
955
- Main: TablePrimitive
604
+ Main: TableMain
956
605
  };
957
606
 
958
- // packages/ui/react-ui-table/src/helpers.tsx
959
- import { useFocusFinders } from "@fluentui/react-tabster";
960
- import { Check, ClipboardText, X as X2 } from "@phosphor-icons/react";
961
- import { createColumnHelper } from "@tanstack/react-table";
962
- import { format } from "date-fns/format";
963
- import { formatDistanceToNow } from "date-fns/formatDistanceToNow";
964
- import React9, { useCallback as useCallback8, useEffect as useEffect4, useRef as useRef4, useState as useState9 } from "react";
965
- import { log } from "@dxos/log";
966
- import { Input as Input2, Tooltip } from "@dxos/react-ui";
967
- import { getSize as getSize3, mx as mx5 } from "@dxos/react-ui-theme";
968
- var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/helpers.tsx";
969
- var createColumnBuilder = () => ({
970
- helper: createColumnHelper(),
971
- builder: new ColumnBuilder()
607
+ // packages/ui/react-ui-table/src/components/Toolbar/Toolbar.tsx
608
+ import { ChatText, RowsPlusBottom } from "@phosphor-icons/react";
609
+ import { createContext } from "@radix-ui/react-context";
610
+ import React7 from "react";
611
+ import { Toolbar as NaturalToolbar2, useTranslation as useTranslation3 } from "@dxos/react-ui";
612
+
613
+ // packages/ui/react-ui-table/src/components/Toolbar/common.tsx
614
+ import React6 from "react";
615
+ import { Toolbar as NaturalToolbar, Tooltip } from "@dxos/react-ui";
616
+ import { getSize } from "@dxos/react-ui-theme";
617
+ var iconStyles = getSize(5);
618
+ var buttonStyles = "min-bs-0 p-2";
619
+ var tooltipProps = {
620
+ side: "top",
621
+ classNames: "z-10"
622
+ };
623
+ var ToolbarSeparator = () => /* @__PURE__ */ React6.createElement("div", {
624
+ role: "separator",
625
+ className: "grow"
972
626
  });
973
- var ComboboxBuilderCell = (cellContext) => {
974
- const { model, onUpdate } = cellContext.column.columnDef.meta;
975
- return model ? /* @__PURE__ */ React9.createElement(CellCombobox, {
976
- model,
977
- value: cellContext.getValue(),
978
- onValueChange: (value) => onUpdate?.(cellContext.row.original, cellContext.column.id, value)
979
- }) : null;
627
+ var ToolbarButton = ({ Icon, children, ...props }) => {
628
+ return /* @__PURE__ */ React6.createElement(Tooltip.Root, null, /* @__PURE__ */ React6.createElement(Tooltip.Trigger, {
629
+ asChild: true
630
+ }, /* @__PURE__ */ React6.createElement(NaturalToolbar.Button, {
631
+ variant: "ghost",
632
+ ...props,
633
+ classNames: buttonStyles
634
+ }, /* @__PURE__ */ React6.createElement(Icon, {
635
+ className: iconStyles
636
+ }), /* @__PURE__ */ React6.createElement("span", {
637
+ className: "sr-only"
638
+ }, children))), /* @__PURE__ */ React6.createElement(Tooltip.Portal, null, /* @__PURE__ */ React6.createElement(Tooltip.Content, tooltipProps, children, /* @__PURE__ */ React6.createElement(Tooltip.Arrow, null))));
980
639
  };
981
- var StringBuilderCell = (cellContext) => {
982
- const ref = useRef4(null);
983
- const { findPrevFocusable } = useFocusFinders();
984
- const { onUpdate, cell } = cellContext.column.columnDef.meta;
985
- const initialValue = cellContext.getValue();
986
- const getInitialValue = useCallback8(() => {
987
- return initialValue?.content ?? initialValue;
988
- }, [
989
- initialValue
990
- ]);
991
- const [value, setValue] = useState9(getInitialValue);
992
- useEffect4(() => setValue(getInitialValue()), [
993
- getInitialValue
994
- ]);
995
- const handleSave = () => {
996
- ref.current && document.activeElement === ref.current && findPrevFocusable(ref.current)?.focus();
997
- if (value !== initialValue) {
998
- onUpdate?.(cellContext.row.original, cellContext.column.id, value);
999
- }
1000
- };
1001
- const handleCancel = () => setValue(initialValue);
1002
- let placeholder = false;
1003
- const columns = cellContext.table.getVisibleFlatColumns();
1004
- if (columns[0].id === cellContext.column.id) {
1005
- const rowCount = cellContext.table.getRowModel().rows.length;
1006
- placeholder = cellContext.row.index === rowCount - 1 && columns[0].id === cellContext.column.id;
1007
- }
1008
- return /* @__PURE__ */ React9.createElement(Input2.Root, null, /* @__PURE__ */ React9.createElement(Input2.TextInput, {
1009
- ref,
1010
- variant: "subdued",
1011
- placeholder: placeholder ? "Add row..." : void 0,
640
+
641
+ // packages/ui/react-ui-table/src/components/Toolbar/Toolbar.tsx
642
+ var [ToolbarContextProvider, useToolbarContext] = createContext("Toolbar");
643
+ var ToolbarRoot = ({ classNames, children, onAction }) => {
644
+ return /* @__PURE__ */ React7.createElement(ToolbarContextProvider, {
645
+ onAction
646
+ }, /* @__PURE__ */ React7.createElement(NaturalToolbar2.Root, {
1012
647
  classNames: [
1013
- "is-full",
1014
- textPadding,
1015
- cell?.classNames
1016
- ],
1017
- value: value ?? "",
1018
- onBlur: handleSave,
1019
- onChange: (event) => setValue(event.target.value),
1020
- onKeyDown: (event) => event.key === "Enter" && handleSave() || event.key === "Escape" && handleCancel(),
1021
- autoComplete: "off"
1022
- }));
648
+ "is-full shrink-0 overflow-x-auto overflow-y-hidden p-1",
649
+ classNames
650
+ ]
651
+ }, children));
1023
652
  };
1024
- var parseNumber = (text) => text?.trim().length ? Number(text) : NaN;
1025
- var displayNumber = (value, digits) => value || value === 0 ? value.toLocaleString(void 0, {
1026
- minimumFractionDigits: digits ?? 0,
1027
- maximumFractionDigits: digits ?? 2
1028
- }) : "";
1029
- var NumberBuilderCell = (cellContext) => {
1030
- const { onUpdate, digits } = cellContext.column.columnDef.meta;
1031
- const value = cellContext.getValue();
1032
- const [text, setText] = useState9(displayNumber(value, digits));
1033
- const { findPrevFocusable } = useFocusFinders();
1034
- const ref = useRef4(null);
1035
- useEffect4(() => {
1036
- (!document.activeElement || document.activeElement !== ref.current) && setText(displayNumber(value, digits));
1037
- }, [
1038
- digits,
1039
- value
1040
- ]);
1041
- const handleEdit = () => setText(value !== void 0 ? String(value) : "");
1042
- const handleSave = () => {
1043
- const nextValue = parseNumber(text);
1044
- if (!isNaN(nextValue)) {
1045
- onUpdate?.(cellContext.row.original, cellContext.column.id, nextValue);
1046
- setText(displayNumber(nextValue, digits));
1047
- } else {
1048
- setText(displayNumber(value, digits));
1049
- }
1050
- ref.current && document.activeElement === ref.current && findPrevFocusable(ref.current)?.focus();
1051
- };
1052
- const _handleCancel = () => {
1053
- setText(displayNumber(value, digits));
1054
- ref.current && findPrevFocusable(ref.current)?.focus();
1055
- };
1056
- return /* @__PURE__ */ React9.createElement(Input2.Root, null, /* @__PURE__ */ React9.createElement(Input2.TextInput, {
1057
- ref,
1058
- variant: "subdued",
1059
- value: text,
1060
- classNames: [
1061
- "is-full text-end font-mono",
1062
- textPadding
1063
- ],
1064
- onBlur: handleSave,
1065
- onFocus: handleEdit,
1066
- onChange: ({ target: { value: value2 } }) => setText(value2),
1067
- onKeyDown: (event) => {
1068
- return (
1069
- /* (event.key === 'Escape' && handleCancel()) || */
1070
- event.key === "Enter" && handleSave()
1071
- );
1072
- },
1073
- autoComplete: "off"
1074
- }));
653
+ var Editing = () => {
654
+ const { onAction } = useToolbarContext("Editing");
655
+ const { t } = useTranslation3(translationKey);
656
+ return /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement(ToolbarButton, {
657
+ value: "add-row",
658
+ Icon: RowsPlusBottom,
659
+ "data-testid": "table.toolbar.add-row",
660
+ onClick: () => onAction?.({
661
+ type: "add-row"
662
+ })
663
+ }, t("add row")));
1075
664
  };
1076
- var RowSelectorBuilderCell = (cellContext) => {
1077
- const { cell } = cellContext.column.columnDef.meta;
1078
- const { row } = cellContext;
1079
- const checked = row.getCanSelect() ? row.getIsSelected() : row.getCanSelectSubRows() && (row.getIsSomeSelected() ? "indeterminate" : row.getIsAllSubRowsSelected());
1080
- return /* @__PURE__ */ React9.createElement("div", {
1081
- className: "flex justify-center w-full h-full",
1082
- role: "presentation"
1083
- }, /* @__PURE__ */ React9.createElement(Input2.Root, null, /* @__PURE__ */ React9.createElement(Input2.Checkbox, {
1084
- size: 4,
1085
- classNames: [
1086
- cell?.classNames
1087
- ],
1088
- checked,
1089
- onCheckedChange: (event) => {
1090
- if (row.getCanSelect()) {
1091
- row.getToggleSelectedHandler()(event);
1092
- }
1093
- },
1094
- disabled: !(row.getCanSelect() || row.getCanSelectSubRows())
1095
- })));
665
+ var Actions = () => {
666
+ const { onAction } = useToolbarContext("Actions");
667
+ const { t } = useTranslation3(translationKey);
668
+ return /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement(ToolbarButton, {
669
+ value: "comment",
670
+ Icon: ChatText,
671
+ "data-testid": "table.toolbar.comment",
672
+ onClick: () => onAction?.({
673
+ type: "comment"
674
+ })
675
+ }, t("create comment")));
1096
676
  };
1097
- var SwitchBuilderCell = (cellContext) => {
1098
- const { onUpdate } = cellContext.column.columnDef.meta;
1099
- const value = cellContext.getValue();
1100
- return /* @__PURE__ */ React9.createElement(Input2.Root, null, /* @__PURE__ */ React9.createElement(Input2.Switch, {
1101
- classNames: "block mli-auto",
1102
- onClick: (event) => event.stopPropagation(),
1103
- disabled: !onUpdate,
1104
- checked: !!value,
1105
- onCheckedChange: (value2) => {
1106
- onUpdate?.(cellContext.row.original, cellContext.column.id, !!value2);
1107
- }
1108
- }));
677
+ var Toolbar = {
678
+ Root: ToolbarRoot,
679
+ Editing,
680
+ Actions,
681
+ Separator: ToolbarSeparator
1109
682
  };
1110
- var ColumnBuilder = class {
1111
- /**
1112
- * Combobox value
1113
- */
1114
- combobox({ label, classNames, model, onUpdate, ...props }) {
1115
- return {
1116
- minSize: 100,
1117
- header: (column) => {
1118
- return label ?? column.header.id;
1119
- },
1120
- cell: model && onUpdate ? ComboboxBuilderCell : (cellContext) => {
1121
- const cellValue = cellContext.getValue();
1122
- return /* @__PURE__ */ React9.createElement("div", {
1123
- className: mx5("truncate", classNames)
1124
- }, cellValue ? model?.getText(cellValue) : "");
1125
- },
1126
- ...props,
1127
- meta: {
1128
- ...props.meta,
1129
- model,
1130
- onUpdate,
1131
- cell: {
1132
- classNames,
1133
- ...props.meta?.cell
1134
- }
683
+
684
+ // packages/ui/react-ui-table/src/hooks/useTableModel.ts
685
+ import { useEffect as useEffect2, useState as useState3 } from "react";
686
+
687
+ // packages/ui/react-ui-table/src/model/table-model.ts
688
+ import { computed as computed2, effect as effect2, signal as signal3 } from "@preact/signals-core";
689
+ import orderBy from "lodash.orderby";
690
+ import { Resource as Resource2 } from "@dxos/context";
691
+ import { getValue as getValue2, setValue, FormatEnum as FormatEnum3 } from "@dxos/echo-schema";
692
+ import { invariant as invariant3 } from "@dxos/invariant";
693
+ import { PublicKey as PublicKey2 } from "@dxos/react-client";
694
+ import { cellClassesForFieldType, cellClassesForRowSelection, formatForDisplay, formatForEditing, parseValue } from "@dxos/react-ui-data";
695
+ import { mx as mx2 } from "@dxos/react-ui-theme";
696
+ import { VIEW_FIELD_LIMIT } from "@dxos/schema";
697
+
698
+ // packages/ui/react-ui-table/src/model/modal-controller.ts
699
+ import { signal } from "@preact/signals-core";
700
+ var ModalController = class {
701
+ constructor() {
702
+ this._state = signal({
703
+ type: "closed"
704
+ });
705
+ this._triggerRef = {
706
+ current: null
707
+ };
708
+ this.handleClick = (event) => {
709
+ const target = event.target;
710
+ const rowButton = target.closest(`button[${tableButtons.rowMenu.attr}]`);
711
+ if (rowButton) {
712
+ this._triggerRef.current = rowButton;
713
+ this._state.value = {
714
+ type: "row",
715
+ rowIndex: Number(rowButton.getAttribute(tableButtons.rowMenu.attr))
716
+ };
717
+ return true;
718
+ }
719
+ const columnButton = target.closest(`button[${tableButtons.columnSettings.attr}]`);
720
+ if (columnButton) {
721
+ const fieldId = columnButton.getAttribute(tableButtons.columnSettings.attr);
722
+ this._triggerRef.current = columnButton;
723
+ this._state.value = {
724
+ type: "column",
725
+ fieldId
726
+ };
727
+ return true;
1135
728
  }
729
+ const addColumnButton = target.closest(`button[${tableButtons.addColumn.attr}]`);
730
+ if (addColumnButton) {
731
+ this._triggerRef.current = addColumnButton;
732
+ this._state.value = {
733
+ type: "columnSettings",
734
+ mode: {
735
+ type: "create"
736
+ }
737
+ };
738
+ return true;
739
+ }
740
+ return false;
1136
741
  };
1137
- }
1138
- /**
1139
- * String formats.
1140
- */
1141
- string({ label, classNames, onUpdate, ...props } = {}) {
1142
- return {
1143
- minSize: 100,
1144
- // TODO(burdon): Default.
1145
- header: (column) => {
1146
- return label ?? column.header.id;
1147
- },
1148
- cell: onUpdate ? StringBuilderCell : (cell) => {
1149
- let value = cell.getValue();
1150
- if (typeof value === "object" && value?.__typename === "dxos.Text.v0") {
1151
- value = value.content;
1152
- }
1153
- return /* @__PURE__ */ React9.createElement("div", {
1154
- className: mx5("truncate", textPadding, classNames)
1155
- }, value);
1156
- },
1157
- ...props,
1158
- meta: {
1159
- ...props.meta,
1160
- onUpdate,
1161
- cell: {
1162
- ...props.meta?.cell,
1163
- classNames
1164
- }
742
+ this.openColumnSettings = () => {
743
+ if (this._state.value.type === "column") {
744
+ const fieldId = this._state.value.fieldId;
745
+ requestAnimationFrame(() => {
746
+ this._state.value = {
747
+ type: "columnSettings",
748
+ mode: {
749
+ type: "edit",
750
+ fieldId
751
+ }
752
+ };
753
+ });
1165
754
  }
1166
755
  };
756
+ this.close = () => {
757
+ this._state.value = {
758
+ type: "closed"
759
+ };
760
+ };
1167
761
  }
1168
- /**
1169
- * Number formats.
1170
- */
1171
- number({ label, minSize, classNames, digits, onUpdate, ...props } = {}) {
1172
- return {
1173
- size: 100,
1174
- minSize: 100,
1175
- header: (cell) => label ?? cell.header.id,
1176
- cell: NumberBuilderCell,
1177
- ...props,
1178
- meta: {
1179
- ...props.meta,
1180
- onUpdate,
1181
- digits,
1182
- header: {
1183
- ...props.meta?.header
1184
- },
1185
- cell: {
1186
- ...props.meta?.cell,
1187
- classNames
1188
- }
762
+ get state() {
763
+ return this._state;
764
+ }
765
+ get trigger() {
766
+ return this._triggerRef;
767
+ }
768
+ };
769
+
770
+ // packages/ui/react-ui-table/src/model/selection-model.ts
771
+ import { computed, effect, signal as signal2 } from "@preact/signals-core";
772
+ import { Resource } from "@dxos/context";
773
+ var SelectionModel = class extends Resource {
774
+ constructor(_rows, _onSelectionChanged) {
775
+ super();
776
+ this._rows = _rows;
777
+ this._onSelectionChanged = _onSelectionChanged;
778
+ this._selection = signal2(/* @__PURE__ */ new Set());
779
+ this._validSelectedIds = computed(() => {
780
+ const rows = this._rows.value;
781
+ if (!rows) {
782
+ return /* @__PURE__ */ new Set();
783
+ }
784
+ const validIds = new Set(rows.map((row) => row.id));
785
+ return new Set([
786
+ ...this._selection.value
787
+ ].filter((id) => validIds.has(id)));
788
+ });
789
+ this._hasSelection = computed(() => this._validSelectedIds.value.size > 0);
790
+ this._allSelected = computed(() => {
791
+ const rows = this._rows.value;
792
+ if (rows.length === 0) {
793
+ return false;
794
+ }
795
+ return rows.length !== 0 || rows.every((row) => this._selection.value.has(row.id));
796
+ });
797
+ this.getSelectedRows = () => {
798
+ const selectedIds = this._validSelectedIds.value;
799
+ return this._rows.value.filter((row) => selectedIds.has(row.id));
800
+ };
801
+ this.isRowIndexSelected = (rowIndex) => {
802
+ const row = this._rows.value[rowIndex];
803
+ return this._selection.value.has(row.id);
804
+ };
805
+ this.isObjectSelected = (object) => {
806
+ return this._selection.value.has(object.id);
807
+ };
808
+ this.toggleSelectionForRowIndex = (rowIndex) => {
809
+ const row = this._rows.value[rowIndex];
810
+ const newSelection = new Set(this._selection.value);
811
+ if (newSelection.has(row.id)) {
812
+ newSelection.delete(row.id);
813
+ } else {
814
+ newSelection.add(row.id);
1189
815
  }
816
+ this._selection.value = newSelection;
817
+ };
818
+ this.setSelection = (mode) => {
819
+ this._selection.value = mode === "all" ? new Set(this._rows.value.map((row) => row.id)) : /* @__PURE__ */ new Set();
1190
820
  };
1191
821
  }
1192
- /**
1193
- * Date formats.
1194
- */
1195
- // TODO(burdon): Date picker (pluggable renderers?)
1196
- date({ label, format: formatSpec, relative, classNames, ...props } = {}) {
1197
- return {
1198
- size: 220,
1199
- minSize: 100,
1200
- header: (cell) => label ?? cell.header.id,
1201
- cell: (cell) => {
1202
- const value = cell.getValue();
1203
- try {
1204
- const str = value ? formatSpec ? format(value, formatSpec) : relative ? formatDistanceToNow(value, {
1205
- addSuffix: true
1206
- }) : value.toISOString() : void 0;
1207
- return /* @__PURE__ */ React9.createElement("div", {
1208
- className: mx5(textPadding, classNames)
1209
- }, str);
1210
- } catch (err) {
1211
- log.info("formatError", {
1212
- value
1213
- }, {
1214
- F: __dxlog_file,
1215
- L: 377,
1216
- S: this,
1217
- C: (f, a) => f(...a)
1218
- });
1219
- return null;
1220
- }
822
+ async _open() {
823
+ const selectionWatcher = effect(() => {
824
+ touch(this._selection.value);
825
+ this._onSelectionChanged?.();
826
+ });
827
+ this._ctx.onDispose(selectionWatcher);
828
+ }
829
+ //
830
+ // Getters
831
+ //
832
+ get selection() {
833
+ return this._selection;
834
+ }
835
+ get hasSelection() {
836
+ return this._hasSelection;
837
+ }
838
+ get allRowsSeleted() {
839
+ return this._allSelected;
840
+ }
841
+ };
842
+
843
+ // packages/ui/react-ui-table/src/util/table-controls.ts
844
+ var checkboxAttributes = {
845
+ checkbox: "data-table-checkbox",
846
+ header: "data-table-header"
847
+ };
848
+ var renderCheckbox = ({ rowIndex, header = false, checked = false, disabled = false }) => {
849
+ const baseClasses = "absolute inset-block-[6px] inline-end-[6px]";
850
+ const { attributes } = tableControls.checkbox;
851
+ return `<input type="checkbox" class="${baseClasses}" ${attributes.checkbox}="${rowIndex}" ${header ? attributes.header : ""} ${checked ? "checked" : ""} ${disabled ? "disabled" : ""}/>`;
852
+ };
853
+ var tableControls = {
854
+ checkbox: {
855
+ attributes: checkboxAttributes,
856
+ render: renderCheckbox
857
+ }
858
+ };
859
+
860
+ // packages/ui/react-ui-table/src/model/table-model.ts
861
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/model/table-model.ts";
862
+ var TableModel = class extends Resource2 {
863
+ constructor({ table, projection, sorting = [], pinnedRows = {
864
+ top: [],
865
+ bottom: []
866
+ }, onCellUpdate, onDeleteColumn, onDeleteRows, onInsertRow, onRowOrderChanged }) {
867
+ super();
868
+ this.id = `table-model-${PublicKey2.random().truncate()}`;
869
+ this._visibleRange = signal3({
870
+ start: {
871
+ row: 0,
872
+ col: 0
1221
873
  },
1222
- ...props,
1223
- meta: {
1224
- ...props.meta,
1225
- cell: {
1226
- ...props.meta?.cell,
1227
- classNames
874
+ end: {
875
+ row: 0,
876
+ col: 0
877
+ }
878
+ });
879
+ /**
880
+ * Maps display indices to data indices.
881
+ * Used for translating between sorted/displayed order and original data order.
882
+ * Keys are display indices, values are corresponding data indices.
883
+ */
884
+ this._displayToDataIndex = /* @__PURE__ */ new Map();
885
+ this._sorting = signal3(void 0);
886
+ this._rows = signal3([]);
887
+ this._modalController = new ModalController();
888
+ //
889
+ // Get Cells
890
+ //
891
+ this.getCells = (range, plane) => {
892
+ switch (plane) {
893
+ case "grid": {
894
+ this._visibleRange.value = range;
895
+ return this.getMainGridCells(range);
896
+ }
897
+ case "frozenRowsStart": {
898
+ return this.getHeaderCells(range);
899
+ }
900
+ case "frozenColsStart": {
901
+ return this.getSelectionColumnCells(range);
902
+ }
903
+ case "frozenColsEnd": {
904
+ return this.getActionColumnCells(range);
905
+ }
906
+ case "fixedStartStart": {
907
+ return this.getSelectAllCell();
908
+ }
909
+ case "fixedStartEnd": {
910
+ return this.getNewColumnCell();
911
+ }
912
+ default: {
913
+ return {};
1228
914
  }
1229
915
  }
1230
916
  };
1231
- }
1232
- /**
1233
- * PublicKey with tooltip.
1234
- */
1235
- key({ label, tooltip, classNames, ...props } = {}) {
1236
- return {
1237
- size: 94,
1238
- minSize: 94,
1239
- header: (cell) => label ?? cell.header.id,
1240
- cell: (cell) => {
1241
- const value = cell.getValue();
1242
- if (!value) {
1243
- return;
917
+ this.getMainGridCells = (range) => {
918
+ const values = {};
919
+ const fields = this._table.view?.fields ?? [];
920
+ const addCell = (obj, field, colIndex, displayIndex) => {
921
+ const { props } = this._projection.getFieldProjection(field.id);
922
+ const value = {
923
+ get value() {
924
+ const value2 = getValue2(obj, field.path);
925
+ if (value2 == null) {
926
+ return "";
927
+ }
928
+ switch (props.format) {
929
+ case FormatEnum3.Ref: {
930
+ if (!field.referencePath) {
931
+ return "";
932
+ }
933
+ return getValue2(value2, field.referencePath);
934
+ }
935
+ default: {
936
+ return formatForDisplay({
937
+ type: props.type,
938
+ format: props.format,
939
+ value: value2
940
+ });
941
+ }
942
+ }
943
+ }
944
+ };
945
+ const classes = [];
946
+ const formatClasses = cellClassesForFieldType({
947
+ type: props.type,
948
+ format: props.format
949
+ });
950
+ if (formatClasses) {
951
+ classes.push(formatClasses);
952
+ }
953
+ const rowSelectionClasses = cellClassesForRowSelection(this._selection.isObjectSelected(obj));
954
+ if (rowSelectionClasses) {
955
+ classes.push(rowSelectionClasses);
1244
956
  }
1245
- const element = value.truncate();
1246
- if (!tooltip) {
1247
- return element;
957
+ if (classes.length > 0) {
958
+ value.className = mx2(classes.flat());
1248
959
  }
1249
- return /* @__PURE__ */ React9.createElement(Tooltip.Provider, null, /* @__PURE__ */ React9.createElement(Tooltip.Root, null, /* @__PURE__ */ React9.createElement(Tooltip.Trigger, null, element), /* @__PURE__ */ React9.createElement(Tooltip.Portal, null, /* @__PURE__ */ React9.createElement(Tooltip.Content, {
1250
- side: "right"
1251
- }, /* @__PURE__ */ React9.createElement(Tooltip.Arrow, null), /* @__PURE__ */ React9.createElement(ClipboardText, {
1252
- onClick: (ev) => {
1253
- ev.stopPropagation();
1254
- void navigator.clipboard.writeText(value.toHex());
960
+ const idx = fromGridCell({
961
+ col: colIndex,
962
+ row: displayIndex
963
+ });
964
+ values[idx] = value;
965
+ };
966
+ for (let row = range.start.row; row <= range.end.row && row < this._sortedRows.value.length; row++) {
967
+ for (let col = range.start.col; col <= range.end.col && col < fields.length; col++) {
968
+ const field = fields[col];
969
+ if (!field) {
970
+ continue;
1255
971
  }
1256
- })))));
1257
- },
1258
- ...props,
1259
- meta: {
1260
- ...props.meta,
1261
- cell: {
1262
- ...props.meta?.cell,
1263
- classNames: [
1264
- "font-mono",
1265
- textPadding,
1266
- classNames
1267
- ]
972
+ addCell(this._sortedRows.value[row], field, col, row);
1268
973
  }
1269
974
  }
975
+ return values;
1270
976
  };
1271
- }
1272
- /**
1273
- * Row selector
1274
- */
1275
- selectRow({ classNames, onUpdate, id = "selectRow" } = {}) {
1276
- return {
1277
- id,
1278
- size: 32,
1279
- enableResizing: false,
1280
- meta: {
1281
- onUpdate,
1282
- cell: {
1283
- classNames
977
+ this.getHeaderCells = (range) => {
978
+ const values = {};
979
+ const fields = this.table.view?.fields ?? [];
980
+ for (let col = range.start.col; col <= range.end.col && col < fields.length; col++) {
981
+ const { field, props } = this._projection.getFieldProjection(fields[col].id);
982
+ values[fromGridCell({
983
+ col,
984
+ row: 0
985
+ })] = {
986
+ value: props.title ?? field.path,
987
+ readonly: true,
988
+ resizeHandle: "col",
989
+ accessoryHtml: tableButtons.columnSettings.render({
990
+ fieldId: field.id
991
+ })
992
+ };
993
+ }
994
+ return values;
995
+ };
996
+ this.getSelectionColumnCells = (range) => {
997
+ const values = {};
998
+ for (let row = range.start.row; row <= range.end.row && row < this._rows.value.length; row++) {
999
+ const isSelected = this._selection.isRowIndexSelected(row);
1000
+ const classes = cellClassesForRowSelection(isSelected);
1001
+ values[fromGridCell({
1002
+ col: 0,
1003
+ row
1004
+ })] = {
1005
+ value: "",
1006
+ readonly: true,
1007
+ className: classes ? mx2(classes) : void 0,
1008
+ accessoryHtml: tableControls.checkbox.render({
1009
+ rowIndex: row,
1010
+ checked: isSelected
1011
+ })
1012
+ };
1013
+ }
1014
+ return values;
1015
+ };
1016
+ this.getActionColumnCells = (range) => {
1017
+ const values = {};
1018
+ for (let row = range.start.row; row <= range.end.row && row < this._rows.value.length; row++) {
1019
+ const isSelected = this._selection.isRowIndexSelected(row);
1020
+ const classes = cellClassesForRowSelection(isSelected);
1021
+ values[fromGridCell({
1022
+ col: 0,
1023
+ row
1024
+ })] = {
1025
+ value: "",
1026
+ readonly: true,
1027
+ className: classes ? mx2(classes) : void 0,
1028
+ accessoryHtml: tableButtons.rowMenu.render({
1029
+ rowIndex: row
1030
+ })
1031
+ };
1032
+ }
1033
+ return values;
1034
+ };
1035
+ this.getSelectAllCell = () => {
1036
+ return {
1037
+ [fromGridCell({
1038
+ col: 0,
1039
+ row: 0
1040
+ })]: {
1041
+ value: "",
1042
+ accessoryHtml: tableControls.checkbox.render({
1043
+ rowIndex: 0,
1044
+ header: true,
1045
+ checked: this._selection.allRowsSeleted.value
1046
+ }),
1047
+ readonly: true
1284
1048
  }
1285
- },
1286
- header: ({ table }) => {
1287
- const { rowsSelectable } = useTableContext();
1288
- const checked = table.getIsSomeRowsSelected() ? "indeterminate" : table.getIsAllRowsSelected();
1289
- return rowsSelectable === "multi" ? /* @__PURE__ */ React9.createElement("div", {
1290
- className: "flex justify-center w-full h-full",
1291
- role: "presentation"
1292
- }, /* @__PURE__ */ React9.createElement(Input2.Root, null, /* @__PURE__ */ React9.createElement(Input2.Checkbox, {
1293
- size: 4,
1294
- classNames: [
1295
- "mli-auto",
1296
- classNames
1297
- ],
1298
- checked,
1299
- onCheckedChange: () => table.toggleAllRowsSelected()
1300
- }))) : null;
1301
- },
1302
- cell: RowSelectorBuilderCell
1049
+ };
1303
1050
  };
1304
- }
1305
- /**
1306
- * Switch
1307
- */
1308
- switch({ label, classNames, onUpdate, ...props } = {}) {
1309
- return {
1310
- size: 50,
1311
- minSize: 50,
1312
- header: (column) => label ?? column.header.id,
1313
- // TODO(zan): Implement sort algorithm.
1314
- enableSorting: true,
1315
- cell: SwitchBuilderCell,
1316
- ...props,
1317
- meta: {
1318
- ...props.meta,
1319
- onUpdate,
1320
- cell: {
1321
- ...props.meta?.cell,
1322
- classNames: [
1323
- textPadding,
1324
- classNames
1325
- ]
1051
+ this.getNewColumnCell = () => {
1052
+ return {
1053
+ [fromGridCell({
1054
+ col: 0,
1055
+ row: 0
1056
+ })]: {
1057
+ value: "",
1058
+ accessoryHtml: tableButtons.addColumn.render({
1059
+ disabled: (this._table.view?.fields?.length ?? 0) >= VIEW_FIELD_LIMIT
1060
+ }),
1061
+ readonly: true
1326
1062
  }
1063
+ };
1064
+ };
1065
+ //
1066
+ // Data
1067
+ //
1068
+ this.setRows = (obj) => {
1069
+ this._rows.value = obj;
1070
+ };
1071
+ this.getRowCount = () => this._rows.value.length;
1072
+ this.getColumnCount = () => this.table.view?.fields.length ?? 0;
1073
+ this.insertRow = (rowIndex) => {
1074
+ const row = rowIndex !== void 0 ? this._displayToDataIndex.get(rowIndex) ?? rowIndex : this._rows.value.length;
1075
+ this._onInsertRow?.(row);
1076
+ };
1077
+ this.deleteRow = (rowIndex) => {
1078
+ const row = this._displayToDataIndex.get(rowIndex) ?? rowIndex;
1079
+ const obj = this._rows.value[row];
1080
+ const objectsToDelete = [];
1081
+ if (this._selection.hasSelection.value) {
1082
+ const selectedRows = this._selection.getSelectedRows();
1083
+ objectsToDelete.push(...selectedRows);
1084
+ } else {
1085
+ objectsToDelete.push(obj);
1327
1086
  }
1087
+ this._onDeleteRows?.(row, objectsToDelete);
1328
1088
  };
1329
- }
1330
- /**
1331
- * Icon based on boolean value.
1332
- */
1333
- icon({ label, size, on, off, ...props } = {}) {
1334
- const IconOn = on?.Icon ?? Check;
1335
- const IconOff = off?.Icon ?? X2;
1336
- return {
1337
- size: size ?? 32,
1338
- // TODO(zan): Implement sort algorithm.
1339
- enableSorting: false,
1340
- header: (column) => /* @__PURE__ */ React9.createElement("div", {
1341
- className: "justify-center"
1342
- }, label ?? column.header.id),
1343
- cell: (cell) => {
1344
- const value = cell.getValue();
1345
- if (value) {
1346
- return /* @__PURE__ */ React9.createElement(IconOn, {
1347
- className: mx5("block mli-auto", getSize3(6), on?.className ?? "text-green-700")
1348
- });
1349
- } else if (value === false) {
1350
- return /* @__PURE__ */ React9.createElement(IconOff, {
1351
- className: mx5("block mli-auto", getSize3(6), off?.className ?? "text-red-700")
1089
+ this.getCellData = ({ col, row }) => {
1090
+ const fields = this.table.view?.fields ?? [];
1091
+ if (col < 0 || col >= fields.length) {
1092
+ return void 0;
1093
+ }
1094
+ const field = fields[col];
1095
+ const dataIndex = this._displayToDataIndex.get(row) ?? row;
1096
+ const value = getValue2(this._rows.value[dataIndex], field.path);
1097
+ if (value == null) {
1098
+ return "";
1099
+ }
1100
+ const { props } = this._projection.getFieldProjection(field.id);
1101
+ switch (props.format) {
1102
+ case FormatEnum3.Ref: {
1103
+ if (!field.referencePath) {
1104
+ return "";
1105
+ }
1106
+ return getValue2(value, field.referencePath);
1107
+ }
1108
+ default: {
1109
+ return formatForEditing({
1110
+ type: props.type,
1111
+ format: props.format,
1112
+ value
1352
1113
  });
1353
- } else {
1354
- return null;
1355
1114
  }
1356
- },
1357
- ...props
1115
+ }
1358
1116
  };
1359
- }
1360
- /**
1361
- * This display column type is for nested/unhandled data to appear as JSON.
1362
- */
1363
- json({ label, classNames, ...props } = {}) {
1364
- return {
1365
- size: 120,
1366
- minSize: 120,
1367
- header: (cell) => label ?? cell.header.id,
1368
- cell: (cell) => {
1369
- const value = cell.getValue();
1370
- return /* @__PURE__ */ React9.createElement("div", {
1371
- className: "overflow-scroll"
1372
- }, /* @__PURE__ */ React9.createElement("code", {
1373
- className: "text-xs"
1374
- }, JSON.stringify(value)));
1375
- },
1376
- ...props,
1377
- meta: {
1378
- ...props.meta,
1379
- cell: {
1380
- ...props.meta?.cell,
1381
- classNames: [
1382
- "font-mono",
1383
- textPadding,
1384
- classNames
1385
- ]
1117
+ this.setCellData = ({ col, row }, value) => {
1118
+ const rowIdx = this._displayToDataIndex.get(row) ?? row;
1119
+ const fields = this.table.view?.fields ?? [];
1120
+ if (col < 0 || col >= fields.length) {
1121
+ return;
1122
+ }
1123
+ const field = fields[col];
1124
+ const { props } = this._projection.getFieldProjection(field.id);
1125
+ switch (props.format) {
1126
+ case FormatEnum3.Ref: {
1127
+ setValue(this._rows.value[rowIdx], field.path, value);
1128
+ break;
1129
+ }
1130
+ default: {
1131
+ setValue(this._rows.value[rowIdx], field.path, parseValue({
1132
+ type: props.type,
1133
+ format: props.format,
1134
+ value
1135
+ }));
1136
+ break;
1137
+ }
1138
+ }
1139
+ };
1140
+ //
1141
+ // Interactions
1142
+ //
1143
+ this.handleGridClick = (event) => {
1144
+ if (!this._modalController.handleClick(event)) {
1145
+ const target = event.target;
1146
+ const selectionCheckbox = target.closest(`input[${tableControls.checkbox.attributes.checkbox}]`);
1147
+ if (selectionCheckbox) {
1148
+ const isHeader = selectionCheckbox.hasAttribute(tableControls.checkbox.attributes.header);
1149
+ if (isHeader) {
1150
+ if (this._selection.allRowsSeleted.value) {
1151
+ this._selection.setSelection("none");
1152
+ } else {
1153
+ this._selection.setSelection("all");
1154
+ }
1155
+ } else {
1156
+ const rowIndex = Number(selectionCheckbox.getAttribute(tableControls.checkbox.attributes.checkbox));
1157
+ this._selection.toggleSelectionForRowIndex(rowIndex);
1158
+ }
1386
1159
  }
1387
1160
  }
1388
1161
  };
1162
+ this._table = table;
1163
+ this._projection = projection;
1164
+ this._sorting.value = sorting.at(0);
1165
+ this._pinnedRows = pinnedRows;
1166
+ this._onInsertRow = onInsertRow;
1167
+ this._onDeleteRows = onDeleteRows;
1168
+ this._onDeleteColumn = onDeleteColumn;
1169
+ this._onCellUpdate = onCellUpdate;
1170
+ this._onRowOrderChanged = onRowOrderChanged;
1389
1171
  }
1390
- };
1391
-
1392
- // packages/ui/react-ui-table/src/schema/column-utils.ts
1393
- import { FieldValueType, mapSchemaToFields } from "@dxos/schema";
1394
- var schemaToColumnDefs = (schema) => {
1395
- const { helper, builder } = createColumnBuilder();
1396
- const classified = mapSchemaToFields(schema);
1397
- return classified.map(([name, type]) => {
1398
- const propertyKey = name.toString();
1399
- let column;
1400
- switch (type) {
1401
- case FieldValueType.String: {
1402
- column = builder.string({
1403
- label: propertyKey,
1404
- classNames: [
1405
- name === "id" && "font-mono"
1406
- ]
1407
- });
1408
- break;
1172
+ get table() {
1173
+ return this._table;
1174
+ }
1175
+ get projection() {
1176
+ return this._projection;
1177
+ }
1178
+ get pinnedRows() {
1179
+ return this._pinnedRows;
1180
+ }
1181
+ get columnMeta() {
1182
+ invariant3(this._columnMeta, void 0, {
1183
+ F: __dxlog_file4,
1184
+ L: 124,
1185
+ S: this,
1186
+ A: [
1187
+ "this._columnMeta",
1188
+ ""
1189
+ ]
1190
+ });
1191
+ return this._columnMeta;
1192
+ }
1193
+ get sorting() {
1194
+ return this._sorting.value;
1195
+ }
1196
+ get modalController() {
1197
+ return this._modalController;
1198
+ }
1199
+ get selection() {
1200
+ return this._selection;
1201
+ }
1202
+ //
1203
+ // Initialisation
1204
+ //
1205
+ async _open() {
1206
+ this.initializeColumnMeta();
1207
+ this.initializeSorting();
1208
+ this.initializeEffects();
1209
+ this._selection = new SelectionModel(this._sortedRows, () => this._onRowOrderChanged?.());
1210
+ await this._selection.open(this._ctx);
1211
+ }
1212
+ initializeColumnMeta() {
1213
+ this._columnMeta = computed2(() => {
1214
+ const fields = this._table.view?.fields ?? [];
1215
+ const meta = Object.fromEntries(fields.map((field, index) => [
1216
+ index,
1217
+ {
1218
+ size: field?.size ?? 256,
1219
+ resizeable: true
1220
+ }
1221
+ ]));
1222
+ return {
1223
+ grid: meta,
1224
+ frozenColsStart: {
1225
+ 0: {
1226
+ size: 30,
1227
+ resizeable: false
1228
+ }
1229
+ },
1230
+ frozenColsEnd: {
1231
+ 0: {
1232
+ size: 40,
1233
+ resizeable: false
1234
+ }
1235
+ }
1236
+ };
1237
+ });
1238
+ }
1239
+ initializeSorting() {
1240
+ this._sortedRows = computed2(() => {
1241
+ this._displayToDataIndex.clear();
1242
+ const sort = this._sorting.value;
1243
+ if (!sort) {
1244
+ return this._rows.value;
1409
1245
  }
1410
- case FieldValueType.Number: {
1411
- column = builder.number({
1412
- label: propertyKey
1413
- });
1414
- break;
1246
+ const field = this._table.view?.fields.find((field2) => field2.id === sort.fieldId);
1247
+ if (!field) {
1248
+ return this._rows.value;
1415
1249
  }
1416
- case FieldValueType.Boolean: {
1417
- column = builder.switch({
1418
- label: propertyKey
1419
- });
1420
- break;
1250
+ const dataWithIndices = this._rows.value.map((item, index) => {
1251
+ const value = getValue2(item, field.path);
1252
+ return {
1253
+ item,
1254
+ index,
1255
+ value,
1256
+ isEmpty: value == null || value === ""
1257
+ };
1258
+ });
1259
+ const sorted = orderBy(dataWithIndices, [
1260
+ "isEmpty",
1261
+ "value"
1262
+ ], [
1263
+ "asc",
1264
+ sort.direction
1265
+ ]);
1266
+ for (let displayIndex = 0; displayIndex < sorted.length; displayIndex++) {
1267
+ const { index: dataIndex } = sorted[displayIndex];
1268
+ if (displayIndex !== dataIndex) {
1269
+ this._displayToDataIndex.set(displayIndex, dataIndex);
1270
+ }
1421
1271
  }
1422
- case FieldValueType.Date: {
1423
- column = builder.date({
1424
- label: propertyKey
1425
- });
1426
- break;
1272
+ return sorted.map(({ item }) => item);
1273
+ });
1274
+ }
1275
+ initializeEffects() {
1276
+ const rowOrderWatcher = effect2(() => {
1277
+ touch(this._sortedRows.value);
1278
+ this._onRowOrderChanged?.();
1279
+ });
1280
+ this._ctx.onDispose(rowOrderWatcher);
1281
+ const rowEffectManager = effect2(() => {
1282
+ const { start, end } = touch(this._visibleRange.value);
1283
+ const rowEffects = [];
1284
+ for (let row = start.row; row <= end.row; row++) {
1285
+ rowEffects.push(effect2(() => {
1286
+ const obj = this._sortedRows.value[row];
1287
+ this?._table?.view?.fields.forEach((field) => touch(getValue2(obj, field.path)));
1288
+ this._onCellUpdate?.({
1289
+ row,
1290
+ col: start.col
1291
+ });
1292
+ }));
1427
1293
  }
1428
- case FieldValueType.JSON: {
1429
- column = builder.json({
1430
- label: propertyKey,
1431
- id: propertyKey
1432
- });
1433
- break;
1294
+ return () => rowEffects.forEach((cleanup) => cleanup());
1295
+ });
1296
+ this._ctx.onDispose(rowEffectManager);
1297
+ }
1298
+ //
1299
+ // Column Operations
1300
+ //
1301
+ deleteColumn(fieldId) {
1302
+ if (!this.table.view) {
1303
+ return;
1304
+ }
1305
+ const field = this.table.view.fields.find((field2) => field2.id === fieldId);
1306
+ if (field && this._onDeleteColumn) {
1307
+ if (this._sorting.value?.fieldId === fieldId) {
1308
+ this.clearSort();
1434
1309
  }
1435
- default: {
1436
- throw new Error(`Unhandled column type: ${type}`);
1310
+ this._onDeleteColumn(field.id);
1311
+ }
1312
+ }
1313
+ //
1314
+ // Resize
1315
+ //
1316
+ setColumnWidth(columnIndex, width) {
1317
+ const fields = this.table.view?.fields ?? [];
1318
+ if (columnIndex < fields.length) {
1319
+ const newWidth = Math.max(0, width);
1320
+ const field = fields[columnIndex];
1321
+ if (field) {
1322
+ field.size = newWidth;
1437
1323
  }
1438
1324
  }
1439
- return helper.accessor(propertyKey, column);
1440
- });
1325
+ }
1326
+ //
1327
+ // Sorting
1328
+ //
1329
+ setSort(fieldId, direction) {
1330
+ this._sorting.value = {
1331
+ fieldId,
1332
+ direction
1333
+ };
1334
+ }
1335
+ clearSort() {
1336
+ this._sorting.value = void 0;
1337
+ }
1338
+ //
1339
+ // Pinning
1340
+ //
1341
+ // TODO(burdon): Change to setPinned(on/off).
1342
+ pinRow(rowIndex, side) {
1343
+ this._pinnedRows[side].push(rowIndex);
1344
+ }
1345
+ unpinRow(rowIndex) {
1346
+ this._pinnedRows.top = this._pinnedRows.top.filter((index) => index !== rowIndex);
1347
+ this._pinnedRows.bottom = this._pinnedRows.bottom.filter((index) => index !== rowIndex);
1348
+ }
1349
+ };
1350
+
1351
+ // packages/ui/react-ui-table/src/hooks/useTableModel.ts
1352
+ var useTableModel = ({ objects, table, projection, ...props }) => {
1353
+ const [model, setModel] = useState3();
1354
+ useEffect2(() => {
1355
+ if (!table || !projection) {
1356
+ return;
1357
+ }
1358
+ let model2;
1359
+ const t = setTimeout(async () => {
1360
+ model2 = new TableModel({
1361
+ table,
1362
+ projection,
1363
+ ...props
1364
+ });
1365
+ await model2.open();
1366
+ setModel(model2);
1367
+ });
1368
+ return () => {
1369
+ clearTimeout(t);
1370
+ void model2?.close();
1371
+ };
1372
+ }, [
1373
+ table,
1374
+ projection
1375
+ ]);
1376
+ useEffect2(() => {
1377
+ if (objects) {
1378
+ model?.setRows(objects);
1379
+ }
1380
+ }, [
1381
+ model,
1382
+ objects
1383
+ ]);
1384
+ return model;
1441
1385
  };
1442
1386
  export {
1443
- CellCombobox,
1444
- ColumnBuilder,
1445
- ColumnMenu,
1446
- ColumnSettings,
1447
- MemoizedVirtualizedTableContent,
1448
- SortIndicator,
1449
- TABLE_NAME,
1450
1387
  Table,
1451
- TablePrimitive,
1452
- TableProvider,
1453
- TableRootContext,
1454
- createColumnBuilder,
1455
- currentRow,
1456
- flushPadding,
1457
- gridCellFocusRing,
1458
- groupTh,
1459
- headPadding,
1460
- schemaToColumnDefs,
1461
- selectedRow,
1462
- tableRoot,
1463
- tbodyRoot,
1464
- tbodyTr,
1465
- tdRoot,
1466
- textPadding,
1467
- theadResizeRoot,
1468
- theadRoot,
1469
- theadTh,
1470
- theadTr,
1388
+ TableType,
1389
+ Toolbar,
1390
+ fromGridCell,
1391
+ initializeTable,
1392
+ tableButtons,
1393
+ toGridCell,
1394
+ touch,
1471
1395
  translations_default as translations,
1472
- useColumnResizing,
1473
- useColumnSorting,
1474
- usePinLastRow,
1475
- useRowSelection,
1476
- useSortColumn,
1477
- useTableContext,
1478
- useTableRootContext
1396
+ useTableModel,
1397
+ useToolbarContext
1479
1398
  };
1480
1399
  //# sourceMappingURL=index.mjs.map