@dxos/react-ui-table 0.4.10-main.0302e13 → 0.4.10-main.05dbd64

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 (88) hide show
  1. package/dist/lib/browser/index.mjs +775 -384
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/types/src/components/ColumnMenu/ColumnMenu.d.ts +19 -0
  5. package/dist/types/src/components/ColumnMenu/ColumnMenu.d.ts.map +1 -0
  6. package/dist/types/src/components/{ColumnMenu.stories.d.ts → ColumnMenu/ColumnMenu.stories.d.ts} +10 -2
  7. package/dist/types/src/components/ColumnMenu/ColumnMenu.stories.d.ts.map +1 -0
  8. package/dist/types/src/components/ColumnMenu/ColumnSettingsForm.d.ts +11 -0
  9. package/dist/types/src/components/ColumnMenu/ColumnSettingsForm.d.ts.map +1 -0
  10. package/dist/types/src/components/ColumnMenu/index.d.ts +3 -0
  11. package/dist/types/src/components/ColumnMenu/index.d.ts.map +1 -0
  12. package/dist/types/src/components/Table/Table.d.ts +17 -4
  13. package/dist/types/src/components/Table/Table.d.ts.map +1 -1
  14. package/dist/types/src/components/Table/Table.stories.d.ts +32 -1
  15. package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
  16. package/dist/types/src/components/Table/TableBody.d.ts.map +1 -1
  17. package/dist/types/src/components/Table/TableRootContext.d.ts +17 -0
  18. package/dist/types/src/components/Table/TableRootContext.d.ts.map +1 -0
  19. package/dist/types/src/components/Table/index.d.ts +1 -0
  20. package/dist/types/src/components/Table/index.d.ts.map +1 -1
  21. package/dist/types/src/components/Table/props.d.ts +2 -3
  22. package/dist/types/src/components/Table/props.d.ts.map +1 -1
  23. package/dist/types/src/components/index.d.ts +1 -1
  24. package/dist/types/src/components/index.d.ts.map +1 -1
  25. package/dist/types/src/helpers.d.ts +4 -0
  26. package/dist/types/src/helpers.d.ts.map +1 -1
  27. package/dist/types/src/hooks/index.d.ts +5 -0
  28. package/dist/types/src/hooks/index.d.ts.map +1 -0
  29. package/dist/types/src/hooks/useColumnSizing.d.ts +12 -0
  30. package/dist/types/src/hooks/useColumnSizing.d.ts.map +1 -0
  31. package/dist/types/src/hooks/useColumnSorting.d.ts +9 -0
  32. package/dist/types/src/hooks/useColumnSorting.d.ts.map +1 -0
  33. package/dist/types/src/hooks/usePinLastRow.d.ts +3 -0
  34. package/dist/types/src/hooks/usePinLastRow.d.ts.map +1 -0
  35. package/dist/types/src/hooks/useRowSelection.d.ts +7 -0
  36. package/dist/types/src/hooks/useRowSelection.d.ts.map +1 -0
  37. package/dist/types/src/index.d.ts +2 -1
  38. package/dist/types/src/index.d.ts.map +1 -1
  39. package/dist/types/src/schema/SchemaToTable.stories.d.ts +11 -0
  40. package/dist/types/src/schema/SchemaToTable.stories.d.ts.map +1 -0
  41. package/dist/types/src/schema/index.d.ts +3 -0
  42. package/dist/types/src/schema/index.d.ts.map +1 -0
  43. package/dist/types/src/schema/schema.d.ts +5 -0
  44. package/dist/types/src/schema/schema.d.ts.map +1 -0
  45. package/dist/types/src/schema/schema.test.d.ts +2 -0
  46. package/dist/types/src/schema/schema.test.d.ts.map +1 -0
  47. package/dist/types/src/schema/schemaToColumns.d.ts +4 -0
  48. package/dist/types/src/schema/schemaToColumns.d.ts.map +1 -0
  49. package/dist/types/src/{schema.d.ts → schema/types.d.ts} +1 -1
  50. package/dist/types/src/schema/types.d.ts.map +1 -0
  51. package/package.json +22 -16
  52. package/src/components/ColumnMenu/ColumnMenu.stories.tsx +127 -0
  53. package/src/components/ColumnMenu/ColumnMenu.tsx +168 -0
  54. package/src/components/ColumnMenu/ColumnSettingsForm.tsx +214 -0
  55. package/src/components/ColumnMenu/index.ts +6 -0
  56. package/src/components/Table/Table.stories.tsx +244 -74
  57. package/src/components/Table/Table.tsx +79 -96
  58. package/src/components/Table/TableBody.tsx +6 -1
  59. package/src/components/Table/TableRootContext.ts +39 -0
  60. package/src/components/Table/index.ts +1 -0
  61. package/src/components/Table/props.ts +17 -24
  62. package/src/components/index.ts +1 -1
  63. package/src/helpers.tsx +32 -8
  64. package/src/hooks/index.ts +8 -0
  65. package/src/hooks/useColumnSizing.ts +45 -0
  66. package/src/hooks/useColumnSorting.ts +54 -0
  67. package/src/hooks/usePinLastRow.ts +64 -0
  68. package/src/hooks/useRowSelection.ts +39 -0
  69. package/src/index.ts +3 -1
  70. package/src/schema/SchemaToTable.stories.tsx +71 -0
  71. package/src/schema/index.ts +6 -0
  72. package/src/schema/schema.test.ts +148 -0
  73. package/src/schema/schema.ts +92 -0
  74. package/src/schema/schemaToColumns.ts +48 -0
  75. package/src/theme.ts +1 -1
  76. package/dist/types/src/components/ColumnMenu.d.ts +0 -13
  77. package/dist/types/src/components/ColumnMenu.d.ts.map +0 -1
  78. package/dist/types/src/components/ColumnMenu.stories.d.ts.map +0 -1
  79. package/dist/types/src/components/SortButton.d.ts +0 -8
  80. package/dist/types/src/components/SortButton.d.ts.map +0 -1
  81. package/dist/types/src/components/hooks/usePinLastRow.d.ts +0 -3
  82. package/dist/types/src/components/hooks/usePinLastRow.d.ts.map +0 -1
  83. package/dist/types/src/schema.d.ts.map +0 -1
  84. package/src/components/ColumnMenu.stories.tsx +0 -46
  85. package/src/components/ColumnMenu.tsx +0 -248
  86. package/src/components/SortButton.tsx +0 -34
  87. package/src/components/hooks/usePinLastRow.ts +0 -43
  88. /package/src/{schema.ts → schema/types.ts} +0 -0
@@ -1,40 +1,3 @@
1
- // packages/ui/react-ui-table/src/components/ColumnMenu.tsx
2
- import { Check, Trash, X, GearSix } from "@phosphor-icons/react";
3
- import React2, { useRef, useState } from "react";
4
- import { Button as Button2, DensityProvider, Input, Popover, Select, Separator, useTranslation } from "@dxos/react-ui";
5
- import { getSize as getSize2, mx } from "@dxos/react-ui-theme";
6
- import { safeParseInt } from "@dxos/util";
7
-
8
- // packages/ui/react-ui-table/src/components/SortButton.tsx
9
- import { CaretUpDown, SortAscending, SortDescending } from "@phosphor-icons/react";
10
- import React from "react";
11
- import { Button } from "@dxos/react-ui";
12
- import { getSize } from "@dxos/react-ui-theme";
13
- var SortButton = ({ sortDirection, onClick }) => {
14
- let icon;
15
- const size = getSize(4);
16
- switch (sortDirection) {
17
- case "asc":
18
- icon = /* @__PURE__ */ React.createElement(SortAscending, {
19
- className: size
20
- });
21
- break;
22
- case "desc":
23
- icon = /* @__PURE__ */ React.createElement(SortDescending, {
24
- className: size
25
- });
26
- break;
27
- default:
28
- icon = /* @__PURE__ */ React.createElement(CaretUpDown, {
29
- className: size
30
- });
31
- }
32
- return /* @__PURE__ */ React.createElement(Button, {
33
- variant: "ghost",
34
- onClick
35
- }, icon);
36
- };
37
-
38
1
  // packages/ui/react-ui-table/src/translations.ts
39
2
  var translationKey = "table";
40
3
  var translations_default = [
@@ -58,47 +21,107 @@ var translations_default = [
58
21
  }
59
22
  ];
60
23
 
61
- // packages/ui/react-ui-table/src/components/ColumnMenu.tsx
62
- var ColumnMenu = ({ column, ...props }) => {
63
- const title = column.label?.length ? column.label : column.id;
64
- const header = props.context.header;
65
- const canSort = header.column.getCanSort();
66
- const sortDirection = header.column.getIsSorted();
67
- const toggleSort = header.column.getToggleSortingHandler();
68
- return /* @__PURE__ */ React2.createElement("div", {
69
- className: "flex items-center gap-2"
70
- }, /* @__PURE__ */ React2.createElement("div", {
71
- className: "flex-1 min-is-0 truncate",
72
- title
73
- }, title), /* @__PURE__ */ React2.createElement("div", {
74
- className: "flex gap-0"
75
- }, canSort && toggleSort && /* @__PURE__ */ React2.createElement(SortButton, {
76
- sortDirection,
77
- onClick: toggleSort
78
- }), /* @__PURE__ */ React2.createElement(ColumnPanel, {
79
- ...props,
80
- column
81
- })));
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-base 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)));
82
91
  };
92
+
93
+ // packages/ui/react-ui-table/src/components/ColumnMenu/ColumnMenu.tsx
94
+ import { X as X2, GearSix, CaretDown as CaretDown2, ArrowDown, ArrowUp } from "@phosphor-icons/react";
95
+ import React3, { useRef as useRef2, useState as useState6, useCallback as useCallback5 } from "react";
96
+ import { Button as Button2, DensityProvider, Popover as Popover2, DropdownMenu } from "@dxos/react-ui";
97
+ import { getSize as getSize3, mx as mx3 } from "@dxos/react-ui-theme";
98
+
99
+ // packages/ui/react-ui-table/src/components/ColumnMenu/ColumnSettingsForm.tsx
100
+ import { Check, Trash, X } from "@phosphor-icons/react";
101
+ import React2, { useRef, useState as useState2 } from "react";
102
+ import { Button, Input, Select, Separator, useTranslation as useTranslation2 } from "@dxos/react-ui";
103
+ import { getSize as getSize2, mx as mx2 } from "@dxos/react-ui-theme";
104
+ import { safeParseInt } from "@dxos/util";
83
105
  var Section = ({ children, className }) => /* @__PURE__ */ React2.createElement("div", {
84
106
  role: "none",
85
- className: mx("p-2", className)
107
+ className: mx2("p-2", className)
86
108
  }, children);
87
- var ColumnPanel = ({ context, tableDefs, tableDef, column, onUpdate, onDelete }) => {
88
- const [open, setOpen] = useState(false);
89
- const [prop, setProp] = useState(column.id);
90
- const [refTable, setRefTable] = useState(column.refTable);
91
- const [refProp, setRefProp] = useState(column.refProp);
92
- const [type, setType] = useState(String(column.type));
93
- const [label, setLabel] = useState(column.label);
94
- const [digits, setDigits] = useState(String(column.digits ?? "0"));
109
+ var ColumnSettingsForm = ({ column, tableDef, tableDefs, onUpdate, onDelete, onClose }) => {
110
+ const [formState, setFormState] = useState2({
111
+ prop: column.id,
112
+ refTable: column.refTable,
113
+ refProp: column.refProp,
114
+ type: String(column.type),
115
+ label: column.label,
116
+ digits: String(column.digits ?? "0")
117
+ });
95
118
  const propRef = useRef(null);
96
- const { t } = useTranslation(translationKey);
119
+ const { t } = useTranslation2(translationKey);
97
120
  const handleCancel = () => {
98
- setProp(column.id);
99
- setOpen(false);
121
+ onClose?.();
100
122
  };
101
123
  const handleSave = () => {
124
+ const { prop, refTable, refProp, type, label, digits } = formState;
102
125
  if (!prop.length || !prop.match(/^[a-zA-Z_].+/i)) {
103
126
  propRef.current?.focus();
104
127
  return;
@@ -110,7 +133,7 @@ var ColumnPanel = ({ context, tableDefs, tableDef, column, onUpdate, onDelete })
110
133
  return;
111
134
  }
112
135
  }
113
- onUpdate?.(prop, {
136
+ onUpdate?.(column.id, {
114
137
  id: prop,
115
138
  prop,
116
139
  label,
@@ -119,43 +142,38 @@ var ColumnPanel = ({ context, tableDefs, tableDef, column, onUpdate, onDelete })
119
142
  refProp,
120
143
  digits: safeParseInt(digits)
121
144
  });
122
- setOpen(false);
145
+ onClose?.();
123
146
  };
124
- return /* @__PURE__ */ React2.createElement(Popover.Root, {
125
- open,
126
- onOpenChange: (nextOpen) => setOpen(nextOpen)
127
- }, /* @__PURE__ */ React2.createElement(Popover.Trigger, {
128
- asChild: true
129
- }, /* @__PURE__ */ React2.createElement(Button2, {
130
- variant: "ghost"
131
- }, /* @__PURE__ */ React2.createElement(GearSix, {
132
- className: mx(getSize2(4), "rotate-90")
133
- }))), /* @__PURE__ */ React2.createElement(Popover.Portal, null, /* @__PURE__ */ React2.createElement(Popover.Content, null, /* @__PURE__ */ React2.createElement(Popover.Viewport, {
134
- classNames: "w-60"
135
- }, /* @__PURE__ */ React2.createElement(DensityProvider, {
136
- density: "fine"
137
- }, /* @__PURE__ */ React2.createElement("div", {
147
+ return /* @__PURE__ */ React2.createElement("div", {
138
148
  className: "flex flex-col gap-1"
139
149
  }, /* @__PURE__ */ React2.createElement(Section, null, /* @__PURE__ */ React2.createElement(Input.Root, null, /* @__PURE__ */ React2.createElement(Input.Label, {
140
150
  classNames: "mbe-1"
141
151
  }, t("column label label")), /* @__PURE__ */ React2.createElement(Input.TextInput, {
142
152
  placeholder: "Column label",
143
- value: label ?? "",
144
- onChange: (event) => setLabel(event.target.value),
153
+ value: formState.label ?? "",
154
+ onChange: (event) => setFormState((prevState) => ({
155
+ ...prevState,
156
+ label: event.target.value
157
+ })),
145
158
  autoFocus: true
146
159
  })), /* @__PURE__ */ React2.createElement(Input.Root, null, /* @__PURE__ */ React2.createElement(Input.Label, {
147
160
  classNames: "mbe-1 mbs-3"
148
161
  }, t("property key label")), /* @__PURE__ */ React2.createElement(Input.TextInput, {
149
162
  ref: propRef,
150
163
  placeholder: "Property key",
151
- // TODO(burdon): Provide hooks for value normalization, ENTER, ESC, etc.
152
- value: prop ?? "",
153
- onChange: (event) => setProp(event.target.value.replace(/[^\w_]/g, ""))
164
+ value: formState.prop ?? "",
165
+ onChange: (event) => setFormState((prevState) => ({
166
+ ...prevState,
167
+ prop: event.target.value.replace(/[^\w_]/g, "")
168
+ }))
154
169
  })), /* @__PURE__ */ React2.createElement(Input.Root, null, /* @__PURE__ */ React2.createElement(Input.Label, {
155
170
  classNames: "mbe-1 mbs-3"
156
171
  }, t("column type label")), /* @__PURE__ */ React2.createElement(Select.Root, {
157
- value: type,
158
- onValueChange: setType
172
+ value: formState.type,
173
+ onValueChange: (value) => setFormState((prevState) => ({
174
+ ...prevState,
175
+ type: value
176
+ }))
159
177
  }, /* @__PURE__ */ React2.createElement(Select.TriggerButton, {
160
178
  placeholder: "Type",
161
179
  classNames: "is-full"
@@ -164,41 +182,50 @@ var ColumnPanel = ({ context, tableDefs, tableDef, column, onUpdate, onDelete })
164
182
  "boolean",
165
183
  "string",
166
184
  "ref"
167
- ].map((type2) => /* @__PURE__ */ React2.createElement(Select.Option, {
168
- key: type2,
169
- value: type2
170
- }, t(`${type2} column type label`))))))))), type === "number" && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Section, null, /* @__PURE__ */ React2.createElement(Input.Root, null, /* @__PURE__ */ React2.createElement(Input.Label, {
185
+ ].map((type) => /* @__PURE__ */ React2.createElement(Select.Option, {
186
+ key: type,
187
+ value: type
188
+ }, t(`${type} column type label`))))))))), formState.type === "number" && /* @__PURE__ */ React2.createElement(Section, null, /* @__PURE__ */ React2.createElement(Input.Root, null, /* @__PURE__ */ React2.createElement(Input.Label, {
171
189
  classNames: "mbe-1 mbs-3"
172
190
  }, t("digits label")), /* @__PURE__ */ React2.createElement(Input.TextInput, {
173
- value: digits ?? "",
174
- onChange: (event) => setDigits(event.target.value)
175
- })))), type === "ref" && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Section, null, /* @__PURE__ */ React2.createElement(Input.Root, null, /* @__PURE__ */ React2.createElement(Input.Label, {
191
+ value: formState.digits ?? "",
192
+ onChange: (event) => setFormState((prevState) => ({
193
+ ...prevState,
194
+ digits: event.target.value
195
+ }))
196
+ }))), formState.type === "ref" && /* @__PURE__ */ React2.createElement(Section, null, /* @__PURE__ */ React2.createElement(Input.Root, null, /* @__PURE__ */ React2.createElement(Input.Label, {
176
197
  classNames: "mbe-1 mbs-3"
177
198
  }, "Table"), /* @__PURE__ */ React2.createElement(Select.Root, {
178
- value: refTable,
179
- onValueChange: setRefTable
199
+ value: formState.refTable,
200
+ onValueChange: (value) => setFormState((prevState) => ({
201
+ ...prevState,
202
+ refTable: value
203
+ }))
180
204
  }, /* @__PURE__ */ React2.createElement(Select.TriggerButton, {
181
205
  placeholder: "Table",
182
206
  classNames: "is-full"
183
207
  }), /* @__PURE__ */ React2.createElement(Select.Portal, null, /* @__PURE__ */ React2.createElement(Select.Content, null, /* @__PURE__ */ React2.createElement(Select.Viewport, null, tableDefs.filter((t2) => t2.id !== tableDef.id).map(({ id, name }) => /* @__PURE__ */ React2.createElement(Select.Option, {
184
208
  key: id,
185
209
  value: id
186
- }, name ?? id))))))), refTable && /* @__PURE__ */ React2.createElement(Input.Root, null, /* @__PURE__ */ React2.createElement(Input.Label, {
210
+ }, name ?? id))))))), formState.refTable && /* @__PURE__ */ React2.createElement(Input.Root, null, /* @__PURE__ */ React2.createElement(Input.Label, {
187
211
  classNames: "mbe-1 mbs-3"
188
212
  }, "Table property"), /* @__PURE__ */ React2.createElement(Select.Root, {
189
- value: refProp,
190
- onValueChange: setRefProp
213
+ value: formState.refProp,
214
+ onValueChange: (value) => setFormState((prevState) => ({
215
+ ...prevState,
216
+ refProp: value
217
+ }))
191
218
  }, /* @__PURE__ */ React2.createElement(Select.TriggerButton, {
192
219
  placeholder: "Property",
193
220
  classNames: "is-full"
194
- }), /* @__PURE__ */ React2.createElement(Select.Portal, null, /* @__PURE__ */ React2.createElement(Select.Content, null, /* @__PURE__ */ React2.createElement(Select.Viewport, null, tableDefs.find((tableDef2) => tableDef2.id === refTable)?.columns.map(({ id, label: label2 }) => /* @__PURE__ */ React2.createElement(Select.Option, {
221
+ }), /* @__PURE__ */ React2.createElement(Select.Portal, null, /* @__PURE__ */ React2.createElement(Select.Content, null, /* @__PURE__ */ React2.createElement(Select.Viewport, null, tableDefs.find((tableDef2) => tableDef2.id === formState.refTable)?.columns.map(({ id, label }) => /* @__PURE__ */ React2.createElement(Select.Option, {
195
222
  key: id,
196
223
  value: id
197
- }, label2 ?? id))))))))), /* @__PURE__ */ React2.createElement(Separator, {
224
+ }, label ?? id)))))))), /* @__PURE__ */ React2.createElement(Separator, {
198
225
  classNames: "mli-2"
199
226
  }), /* @__PURE__ */ React2.createElement(Section, {
200
227
  className: "space-b-1.5"
201
- }, /* @__PURE__ */ React2.createElement(Button2, {
228
+ }, /* @__PURE__ */ React2.createElement(Button, {
202
229
  variant: "primary",
203
230
  classNames: "is-full flex gap-2",
204
231
  onClick: handleSave
@@ -206,7 +233,7 @@ var ColumnPanel = ({ context, tableDefs, tableDef, column, onUpdate, onDelete })
206
233
  className: "grow"
207
234
  }), /* @__PURE__ */ React2.createElement(Check, {
208
235
  className: getSize2(5)
209
- })), /* @__PURE__ */ React2.createElement(Button2, {
236
+ })), /* @__PURE__ */ React2.createElement(Button, {
210
237
  classNames: "is-full flex gap-2",
211
238
  onClick: handleCancel
212
239
  }, /* @__PURE__ */ React2.createElement("span", null, t("cancel label", {
@@ -215,34 +242,325 @@ var ColumnPanel = ({ context, tableDefs, tableDef, column, onUpdate, onDelete })
215
242
  className: "grow"
216
243
  }), /* @__PURE__ */ React2.createElement(X, {
217
244
  className: getSize2(5)
218
- })), /* @__PURE__ */ React2.createElement(Button2, {
245
+ })), /* @__PURE__ */ React2.createElement(Button, {
219
246
  classNames: "is-full flex gap-2",
220
247
  onClick: () => onDelete?.(column.id)
221
248
  }, /* @__PURE__ */ React2.createElement("span", null, t("delete label")), /* @__PURE__ */ React2.createElement("div", {
222
249
  className: "grow"
223
250
  }), /* @__PURE__ */ React2.createElement(Trash, {
224
251
  className: getSize2(5)
225
- })))))), /* @__PURE__ */ React2.createElement(Popover.Arrow, null))));
252
+ }))));
226
253
  };
227
254
 
228
- // packages/ui/react-ui-table/src/components/Table/Table.tsx
255
+ // packages/ui/react-ui-table/src/hooks/useColumnSizing.ts
256
+ import { useCallback as useCallback2, useEffect, useState as useState3 } from "react";
257
+ import { useOnTransition } from "@dxos/react-ui";
258
+ var useColumnResizing = ({ columns, onColumnResize }) => {
259
+ const [columnsInitialised, setColumnsInitialised] = useState3(false);
260
+ const [columnSizing, setColumnSizing] = useState3({});
261
+ useEffect(() => {
262
+ if (columnsInitialised) {
263
+ return;
264
+ }
265
+ setColumnSizing(columns.filter((column) => !!column.size && column.prop !== void 0).reduce((state, column) => {
266
+ state[column.prop] = column.size;
267
+ return state;
268
+ }, {}));
269
+ setColumnsInitialised(true);
270
+ }, [
271
+ columns,
272
+ setColumnSizing
273
+ ]);
274
+ const [columnSizingInfo, setColumnSizingInfo] = useState3({});
275
+ const notifyColumnResize = useCallback2(() => onColumnResize?.(columnSizing), [
276
+ onColumnResize,
277
+ columnSizing
278
+ ]);
279
+ useOnTransition(columnSizingInfo.isResizingColumn, (v) => typeof v === "string", false, notifyColumnResize);
280
+ return {
281
+ columnSizing,
282
+ columnSizingInfo,
283
+ setColumnSizing,
284
+ setColumnSizingInfo
285
+ };
286
+ };
287
+
288
+ // packages/ui/react-ui-table/src/hooks/useColumnSorting.ts
289
+ import { useState as useState4 } from "react";
290
+ var useColumnSorting = (column) => {
291
+ const [sortDirection, setSortDirection] = useState4(column.getIsSorted());
292
+ const [firstSort, setFirstSort] = useState4();
293
+ const canSort = column.getCanSort();
294
+ const updateColumnSort = (direction) => {
295
+ setSortDirection(direction);
296
+ switch (direction) {
297
+ case false:
298
+ column.clearSorting();
299
+ break;
300
+ case "asc":
301
+ column.toggleSorting(false);
302
+ break;
303
+ case "desc":
304
+ column.toggleSorting(true);
305
+ break;
306
+ }
307
+ };
308
+ const onToggleSort = () => {
309
+ if (sortDirection === false) {
310
+ return;
311
+ }
312
+ if (sortDirection === firstSort) {
313
+ updateColumnSort(firstSort === "asc" ? "desc" : "asc");
314
+ } else {
315
+ updateColumnSort(false);
316
+ }
317
+ };
318
+ const onSelectSort = (direction) => {
319
+ setFirstSort(direction);
320
+ updateColumnSort(direction);
321
+ };
322
+ const onClearSort = () => {
323
+ updateColumnSort(false);
324
+ };
325
+ return {
326
+ canSort,
327
+ sortDirection,
328
+ onSelectSort,
329
+ onToggleSort,
330
+ onClearSort
331
+ };
332
+ };
333
+
334
+ // packages/ui/react-ui-table/src/hooks/usePinLastRow.ts
335
+ import { useCallback as useCallback3, useEffect as useEffect2, useState as useState5 } from "react";
336
+ var usePinLastRow = (pinLastRow, table, data) => {
337
+ const { scrollContextRef } = useTableRootContext();
338
+ const [pinnedRowKey, setPinnedRowKey] = useState5();
339
+ const scrollToBottom = useCallback3(() => {
340
+ const element = scrollContextRef.current;
341
+ const scrollHeight = element?.scrollHeight;
342
+ if (element && scrollHeight) {
343
+ requestAnimationFrame(() => element.scrollTo({
344
+ top: scrollHeight
345
+ }));
346
+ }
347
+ }, []);
348
+ const pinRow = useCallback3((rowToPin) => {
349
+ table.resetRowPinning();
350
+ rowToPin.pin("bottom");
351
+ setPinnedRowKey(rowToPin.id);
352
+ }, [
353
+ table
354
+ ]);
355
+ const clearRowPinning = useCallback3(() => {
356
+ table.resetRowPinning();
357
+ setPinnedRowKey(void 0);
358
+ }, [
359
+ table
360
+ ]);
361
+ const pinLastRowCallback = useCallback3(() => {
362
+ const rows = table.getRowModel().rows;
363
+ if (rows.length === 0) {
364
+ return clearRowPinning();
365
+ }
366
+ const lastRow = rows[rows.length - 1];
367
+ if (lastRow.id === pinnedRowKey) {
368
+ return;
369
+ }
370
+ pinRow(lastRow);
371
+ if (pinnedRowKey !== void 0) {
372
+ scrollToBottom();
373
+ }
374
+ }, [
375
+ table,
376
+ scrollToBottom,
377
+ pinnedRowKey
378
+ ]);
379
+ useEffect2(() => {
380
+ if (pinLastRow) {
381
+ pinLastRowCallback();
382
+ }
383
+ }, [
384
+ data,
385
+ pinLastRow,
386
+ pinLastRowCallback
387
+ ]);
388
+ };
389
+
390
+ // packages/ui/react-ui-table/src/hooks/useRowSelection.ts
229
391
  import { useControllableState } from "@radix-ui/react-use-controllable-state";
392
+ import { useCallback as useCallback4 } from "react";
393
+ var useRowSelection = (props) => {
394
+ const [rowSelection = {}, setRowSelection] = useControllableState({
395
+ prop: props.rowSelection,
396
+ onChange: props.onRowSelectionChange,
397
+ defaultProp: props.defaultRowSelection
398
+ });
399
+ const handleRowSelectionChange = useCallback4((updaterOrValue) => {
400
+ const nextRowSelection = typeof updaterOrValue === "function" ? updaterOrValue(rowSelection) : updaterOrValue;
401
+ if (props.rowsSelectable === "multi") {
402
+ setRowSelection(nextRowSelection);
403
+ } else if (props.rowsSelectable) {
404
+ const nextRowSelectionKey = Object.keys(nextRowSelection).filter((id) => !rowSelection[id])[0];
405
+ setRowSelection(nextRowSelectionKey ? {
406
+ [nextRowSelectionKey]: true
407
+ } : {});
408
+ } else {
409
+ setRowSelection({});
410
+ }
411
+ }, [
412
+ props.rowsSelectable,
413
+ setRowSelection,
414
+ rowSelection
415
+ ]);
416
+ return {
417
+ rowSelection,
418
+ handleRowSelectionChange
419
+ };
420
+ };
421
+
422
+ // packages/ui/react-ui-table/src/components/ColumnMenu/ColumnMenu.tsx
423
+ var ColumnMenu = ({ column, ...props }) => {
424
+ const title = column.label?.length ? column.label : column.id;
425
+ const header = props.context.header;
426
+ const { canSort, sortDirection, onSelectSort, onToggleSort, onClearSort } = useColumnSorting(header.column);
427
+ const columnSettingsAnchorRef = useRef2(null);
428
+ const [isColumnSettingsOpen, setIsColumnSettingsOpen] = useState6(false);
429
+ const shouldPreventFocusOnClose = useRef2(false);
430
+ const onOpenColumnSettings = useCallback5(() => {
431
+ shouldPreventFocusOnClose.current = true;
432
+ setIsColumnSettingsOpen(true);
433
+ }, [
434
+ shouldPreventFocusOnClose,
435
+ setIsColumnSettingsOpen
436
+ ]);
437
+ const onDropdownCloseAutoFocus = useCallback5((event) => {
438
+ if (shouldPreventFocusOnClose.current) {
439
+ event.preventDefault();
440
+ shouldPreventFocusOnClose.current = false;
441
+ }
442
+ }, [
443
+ shouldPreventFocusOnClose
444
+ ]);
445
+ return /* @__PURE__ */ React3.createElement("div", {
446
+ className: "flex items-center justify-center justify-between"
447
+ }, /* @__PURE__ */ React3.createElement("div", {
448
+ className: "flex items-center gap-1 truncate",
449
+ title
450
+ }, /* @__PURE__ */ React3.createElement(SortIndicator, {
451
+ direction: sortDirection,
452
+ onClick: onToggleSort
453
+ }), /* @__PURE__ */ React3.createElement("div", {
454
+ className: "truncate"
455
+ }, title)), /* @__PURE__ */ React3.createElement(DropdownMenu.Root, {
456
+ modal: false
457
+ }, /* @__PURE__ */ React3.createElement(Popover2.Root, {
458
+ open: isColumnSettingsOpen,
459
+ onOpenChange: setIsColumnSettingsOpen,
460
+ modal: false
461
+ }, /* @__PURE__ */ React3.createElement(DropdownMenu.Trigger, {
462
+ ref: columnSettingsAnchorRef,
463
+ asChild: true
464
+ }, /* @__PURE__ */ React3.createElement(Popover2.Anchor, {
465
+ asChild: true
466
+ }, /* @__PURE__ */ React3.createElement(Button2, {
467
+ variant: "ghost"
468
+ }, /* @__PURE__ */ React3.createElement(CaretDown2, {
469
+ className: getSize3(4)
470
+ })))), /* @__PURE__ */ React3.createElement(DropdownMenu.Portal, null, /* @__PURE__ */ React3.createElement(DropdownMenu.Content, {
471
+ onCloseAutoFocus: onDropdownCloseAutoFocus,
472
+ sideOffset: 4,
473
+ collisionPadding: 8
474
+ }, /* @__PURE__ */ React3.createElement(DropdownMenu.Viewport, null, canSort && /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement(DropdownMenu.Item, {
475
+ onClick: () => onSelectSort("asc")
476
+ }, /* @__PURE__ */ React3.createElement("span", {
477
+ className: "grow"
478
+ }, "Sort ascending"), /* @__PURE__ */ React3.createElement("span", {
479
+ className: "opacity-50"
480
+ }, /* @__PURE__ */ React3.createElement(ArrowUp, {
481
+ className: getSize3(4)
482
+ }))), /* @__PURE__ */ React3.createElement(DropdownMenu.Item, {
483
+ onClick: () => onSelectSort("desc")
484
+ }, /* @__PURE__ */ React3.createElement("span", {
485
+ className: "grow"
486
+ }, "Sort descending"), /* @__PURE__ */ React3.createElement("span", {
487
+ className: "opacity-50"
488
+ }, /* @__PURE__ */ React3.createElement(ArrowDown, {
489
+ className: getSize3(4)
490
+ }))), sortDirection !== false && /* @__PURE__ */ React3.createElement(DropdownMenu.Item, {
491
+ onClick: onClearSort
492
+ }, /* @__PURE__ */ React3.createElement("span", {
493
+ className: "grow"
494
+ }, "Clear sort"), /* @__PURE__ */ React3.createElement("span", {
495
+ className: "opacity-50"
496
+ }, /* @__PURE__ */ React3.createElement(X2, {
497
+ className: getSize3(4)
498
+ })))), /* @__PURE__ */ React3.createElement(DropdownMenu.Item, {
499
+ onClick: onOpenColumnSettings
500
+ }, /* @__PURE__ */ React3.createElement("span", {
501
+ className: "grow"
502
+ }, "Column settings"), /* @__PURE__ */ React3.createElement("span", {
503
+ className: "opacity-50"
504
+ }, /* @__PURE__ */ React3.createElement(GearSix, {
505
+ className: mx3(getSize3(4), "rotate-90")
506
+ })))), /* @__PURE__ */ React3.createElement(DropdownMenu.Arrow, null))), /* @__PURE__ */ React3.createElement(ColumnSettingsPanel, {
507
+ ...props,
508
+ column,
509
+ onClose: () => setIsColumnSettingsOpen(false)
510
+ }))));
511
+ };
512
+ var ColumnSettingsPanel = ({ tableDefs, tableDef, column, onUpdate, onDelete, onClose }) => /* @__PURE__ */ React3.createElement(Popover2.Portal, null, /* @__PURE__ */ React3.createElement(Popover2.Content, null, /* @__PURE__ */ React3.createElement(Popover2.Viewport, {
513
+ classNames: "w-60"
514
+ }, /* @__PURE__ */ React3.createElement(DensityProvider, {
515
+ density: "fine"
516
+ }, /* @__PURE__ */ React3.createElement(ColumnSettingsForm, {
517
+ column,
518
+ tableDefs,
519
+ tableDef,
520
+ onUpdate,
521
+ onDelete,
522
+ onClose
523
+ }))), /* @__PURE__ */ React3.createElement(Popover2.Arrow, null)));
524
+ var SortIndicator = ({ direction, onClick }) => {
525
+ if (direction === false) {
526
+ return null;
527
+ }
528
+ let icon;
529
+ switch (direction) {
530
+ case "asc":
531
+ icon = /* @__PURE__ */ React3.createElement(ArrowUp, {
532
+ className: getSize3(3)
533
+ });
534
+ break;
535
+ case "desc":
536
+ icon = /* @__PURE__ */ React3.createElement(ArrowDown, {
537
+ className: getSize3(3)
538
+ });
539
+ break;
540
+ }
541
+ return /* @__PURE__ */ React3.createElement("div", {
542
+ onClick,
543
+ className: "flex items-center cursor-pointer"
544
+ }, icon);
545
+ };
546
+
547
+ // packages/ui/react-ui-table/src/components/Table/Table.tsx
548
+ import { Slot } from "@radix-ui/react-slot";
230
549
  import { getCoreRowModel, getSortedRowModel, useReactTable, getGroupedRowModel, getExpandedRowModel } from "@tanstack/react-table";
231
- import { useVirtualizer } from "@tanstack/react-virtual";
232
- import React8, { Fragment, useCallback, useEffect as useEffect2, useState as useState3 } from "react";
233
- import { log } from "@dxos/log";
234
- import { useDefaultValue, useOnTransition } from "@dxos/react-ui";
550
+ import React9, { Fragment, useCallback as useCallback6, useEffect as useEffect3, useState as useState8, useContext as useContext3 } from "react";
551
+ import { useDefaultValue } from "@dxos/react-ui";
552
+ import { mx as mx5 } from "@dxos/react-ui-theme";
235
553
 
236
554
  // packages/ui/react-ui-table/src/components/Table/TableBody.tsx
237
555
  import { useArrowNavigationGroup } from "@fluentui/react-tabster";
238
556
  import { flexRender } from "@tanstack/react-table";
239
- import React5 from "react";
557
+ import React6, { useContext as useContext2 } from "react";
240
558
 
241
559
  // packages/ui/react-ui-table/src/components/Table/TableContext.tsx
242
- import React3, { useContext, createContext } from "react";
560
+ import React4, { useContext, createContext } from "react";
243
561
  var TableContext = /* @__PURE__ */ createContext({});
244
562
  var TableProvider = ({ children, ...value }) => {
245
- return /* @__PURE__ */ React3.createElement(TableContext.Provider, {
563
+ return /* @__PURE__ */ React4.createElement(TableContext.Provider, {
246
564
  value
247
565
  }, children);
248
566
  };
@@ -251,33 +569,62 @@ var useTableContext = () => {
251
569
  return context;
252
570
  };
253
571
 
572
+ // packages/ui/react-ui-table/src/components/Table/TableRootContext.ts
573
+ import { useVirtualizer } from "@tanstack/react-virtual";
574
+ import { createContext as createContext2, useRef as useRef3, useState as useState7 } from "react";
575
+ var useTableRootContext = () => {
576
+ const scrollContextRef = useRef3(null);
577
+ const [tableCount, setTableCount] = useState7(0);
578
+ const virtualizer = useVirtualizer({
579
+ getScrollElement: () => scrollContextRef?.current ?? null,
580
+ count: tableCount,
581
+ overscan: 8,
582
+ estimateSize: () => 40
583
+ });
584
+ const dispatch = (action) => {
585
+ switch (action.type) {
586
+ case "updateTableCount":
587
+ setTableCount(action.count);
588
+ break;
589
+ default:
590
+ break;
591
+ }
592
+ };
593
+ return {
594
+ scrollContextRef,
595
+ virtualizer,
596
+ dispatch
597
+ };
598
+ };
599
+ var TableRootContext = createContext2({});
600
+
254
601
  // packages/ui/react-ui-table/src/theme.ts
255
- import { modalSurface, focusRing, ghostSelected, ghostSelectedCurrent, groupBorder, mx as mx2 } from "@dxos/react-ui-theme";
602
+ import { modalSurface, focusRing, ghostSelected, ghostSelectedCurrent, groupBorder, mx as mx4 } from "@dxos/react-ui-theme";
256
603
  var currentRow = "!bg-neutral-75 !dark:bg-neutral-850";
257
604
  var selectedRow = "!bg-primary-100 dark:!bg-primary-700";
258
605
  var flushPadding = "pli-0 plb-0";
259
606
  var textPadding = "pli-2 plb-0.5";
260
607
  var headPadding = "pli-2 plb-1";
261
608
  var gridCellFocusRing = "focus-within:z-[11] focus-within:outline outline-2 outline-primary-500 dark:outline-primary-400";
262
- var tableRoot = ({ fullWidth }, ...etc) => mx2("table-fixed", fullWidth && "is-full", ...etc);
263
- var groupTh = (_props, ...etc) => mx2("text-start font-medium", flushPadding, ...etc);
264
- var theadRoot = ({ header, stickyHeader }, ...etc) => mx2(header ? stickyHeader && "sticky block-start-[--sticky-top] z-[1]" : "collapse", header && "base-surface", "drop-shadow-sm", ...etc);
265
- var theadTr = (_props, ...etc) => mx2("group", ...etc);
266
- var theadTh = ({ border }, ...etc) => mx2("relative", "text-start font-medium select-none truncate", headPadding, border && groupBorder, border && "border border-t-0 border-b-0 border-neutral-200", ...etc);
609
+ var tableRoot = ({ fullWidth }, ...etc) => mx4("table-fixed", fullWidth && "is-full", ...etc);
610
+ var groupTh = (_props, ...etc) => mx4("text-start font-medium", flushPadding, ...etc);
611
+ var theadRoot = ({ header, stickyHeader }, ...etc) => mx4(header ? stickyHeader && "sticky block-start-[--sticky-top] z-[1]" : "collapse", header && "base-surface", "drop-shadow-sm", ...etc);
612
+ var theadTr = (_props, ...etc) => mx4("group", ...etc);
613
+ var theadTh = ({ border }, ...etc) => mx4("relative", "text-start font-medium select-none truncate", headPadding, border && groupBorder, border && "border border-t-0 border-b-0 border-neutral-200", ...etc);
267
614
  var theadResizeRoot = ({ isResizing }) => {
268
- return mx2("absolute h-full w-[4px] top-0 right-0", "cursor-col-resize", "user-select-none", "touch-action-none", !isResizing && "group-hover:bg-neutral-100 dark:group-hover:bg-neutral-800", "pointer-coarse:bg-neutral-100 dark:pointer-coarse:bg-neutral-800", isResizing && "bg-primary-500 dark:bg-primary-400");
615
+ return mx4("absolute h-full w-[4px] top-0 right-0", "cursor-col-resize", "user-select-none", "touch-action-none", !isResizing && "group-hover:bg-neutral-100 dark:group-hover:bg-neutral-800", "pointer-coarse:bg-neutral-100 dark:pointer-coarse:bg-neutral-800", isResizing && "bg-primary-500 dark:bg-primary-400");
269
616
  };
270
- var tbodyRoot = (_props, ...etc) => mx2(...etc);
271
- var tbodyTr = ({ canBeCurrent, isPinned }, ...etc) => mx2("group", canBeCurrent ? ghostSelectedCurrent : ghostSelected, canBeCurrent && focusRing, canBeCurrent && "cursor-pointer rounded", isPinned && "sticky z-[20] bottom-[-1px] base-surface shadow-sm", ...etc);
272
- var tdRoot = ({ border, isGrid }, ...etc) => mx2("relative", flushPadding, border && "border", border && groupBorder, isGrid && gridCellFocusRing, ...etc);
273
- var tdContent = (_props, ...etc) => mx2(...etc);
274
- var tfootRoot = (_props, ...etc) => mx2("sticky block-end-0 z-10", modalSurface, ...etc);
275
- var tfootTr = (_props, ...etc) => mx2(...etc);
276
- var tfootTh = ({ border }, ...etc) => mx2(textPadding, modalSurface, border && groupBorder, ...etc);
617
+ var tbodyRoot = (_props, ...etc) => mx4(...etc);
618
+ var tbodyTr = ({ canBeCurrent, isPinned }, ...etc) => mx4("group", canBeCurrent ? ghostSelectedCurrent : ghostSelected, canBeCurrent && focusRing, canBeCurrent && "cursor-pointer rounded", isPinned && "sticky z-1 bottom-[-1px] base-surface", ...etc);
619
+ var tdRoot = ({ border, isGrid }, ...etc) => mx4("relative", flushPadding, border && "border", border && groupBorder, isGrid && gridCellFocusRing, ...etc);
620
+ var tdContent = (_props, ...etc) => mx4(...etc);
621
+ var tfootRoot = (_props, ...etc) => mx4("sticky block-end-0 z-10", modalSurface, ...etc);
622
+ var tfootTr = (_props, ...etc) => mx4(...etc);
623
+ var tfootTh = ({ border }, ...etc) => mx4(textPadding, modalSurface, border && groupBorder, ...etc);
277
624
 
278
625
  // packages/ui/react-ui-table/src/components/Cell/Cell.tsx
279
626
  import { useFocusableGroup } from "@fluentui/react-tabster";
280
- import React4 from "react";
627
+ import React5 from "react";
281
628
  var CELL_NAME = "Cell";
282
629
  var FocusableCell = ({ cell, children }) => {
283
630
  const tableContext = useTableContext();
@@ -285,7 +632,7 @@ var FocusableCell = ({ cell, children }) => {
285
632
  tabBehavior: "limited"
286
633
  });
287
634
  const className = tdRoot(tableContext, cell.column.columnDef.meta?.cell?.classNames);
288
- return /* @__PURE__ */ React4.createElement("td", {
635
+ return /* @__PURE__ */ React5.createElement("td", {
289
636
  tabIndex: 0,
290
637
  ...domAttributes,
291
638
  className
@@ -293,14 +640,14 @@ var FocusableCell = ({ cell, children }) => {
293
640
  };
294
641
  var StaticCell = ({ cell, children }) => {
295
642
  const tableContext = useTableContext();
296
- return /* @__PURE__ */ React4.createElement("td", {
643
+ return /* @__PURE__ */ React5.createElement("td", {
297
644
  className: tdRoot(tableContext, cell.column.columnDef.meta?.cell?.classNames)
298
645
  }, children);
299
646
  };
300
647
  var Cell = ({ cell, children }) => {
301
648
  const { isGrid } = useTableContext();
302
649
  const Root = isGrid ? FocusableCell : StaticCell;
303
- return /* @__PURE__ */ React4.createElement(Root, {
650
+ return /* @__PURE__ */ React5.createElement(Root, {
304
651
  cell
305
652
  }, children);
306
653
  };
@@ -315,7 +662,8 @@ var TableBody = ({ rows }) => {
315
662
  axis: "grid"
316
663
  });
317
664
  const canBeCurrent = !isGrid && !!onDatumClick;
318
- return /* @__PURE__ */ React5.createElement("tbody", isGrid && domAttributes, rows.map((row) => {
665
+ const { virtualizer } = useContext2(TableRootContext);
666
+ return /* @__PURE__ */ React6.createElement("tbody", isGrid && domAttributes, rows.map((row) => {
319
667
  const isCurrent = currentDatum === row.original;
320
668
  const isSelected = rowSelection?.[row.id];
321
669
  const isPinned = row.getIsPinned();
@@ -323,9 +671,11 @@ var TableBody = ({ rows }) => {
323
671
  canBeCurrent,
324
672
  isPinned: isPinned !== false
325
673
  });
326
- return /* @__PURE__ */ React5.createElement("tr", {
674
+ return /* @__PURE__ */ React6.createElement("tr", {
327
675
  key: keyAccessor ? keyAccessor(row.original) : row.id,
328
676
  className: classNames,
677
+ "data-index": row.index,
678
+ ref: virtualizer.measureElement,
329
679
  ...isCurrent && {
330
680
  "aria-current": "location"
331
681
  },
@@ -343,57 +693,57 @@ var TableBody = ({ rows }) => {
343
693
  }
344
694
  }
345
695
  }
346
- }, debug && /* @__PURE__ */ React5.createElement("td", null, row.id), row.getVisibleCells().map((cell) => {
347
- return /* @__PURE__ */ React5.createElement(Cell, {
696
+ }, debug && /* @__PURE__ */ React6.createElement("td", null, row.id), row.getVisibleCells().map((cell) => {
697
+ return /* @__PURE__ */ React6.createElement(Cell, {
348
698
  key: cell.id,
349
699
  cell
350
700
  }, flexRender(cell.column.columnDef.cell, {
351
701
  className: "pli-2",
352
702
  ...cell.getContext()
353
703
  }));
354
- }), expand && /* @__PURE__ */ React5.createElement("td", null));
704
+ }), expand && /* @__PURE__ */ React6.createElement("td", null));
355
705
  }));
356
706
  };
357
707
  TableBody.displayName = TABLE_BODY_NAME;
358
708
 
359
709
  // packages/ui/react-ui-table/src/components/Table/TableFooter.tsx
360
710
  import { flexRender as flexRender2 } from "@tanstack/react-table";
361
- import React6 from "react";
711
+ import React7 from "react";
362
712
  var TABLE_FOOT_NAME = "TableFooter";
363
713
  var TableFooter = (props) => {
364
714
  const tableContext = useTableContext();
365
715
  const { table, expand, debug } = tableContext;
366
716
  const footers = table.getFooterGroups();
367
- return /* @__PURE__ */ React6.createElement("tfoot", {
717
+ return /* @__PURE__ */ React7.createElement("tfoot", {
368
718
  className: tfootRoot(props)
369
- }, footers.map((footerGroup) => /* @__PURE__ */ React6.createElement("tr", {
719
+ }, footers.map((footerGroup) => /* @__PURE__ */ React7.createElement("tr", {
370
720
  key: footerGroup.id,
371
721
  className: tfootTr(tableContext)
372
- }, debug && /* @__PURE__ */ React6.createElement("th", null), footerGroup.headers.map((footer) => {
373
- return /* @__PURE__ */ React6.createElement("th", {
722
+ }, debug && /* @__PURE__ */ React7.createElement("th", null), footerGroup.headers.map((footer) => {
723
+ return /* @__PURE__ */ React7.createElement("th", {
374
724
  key: footer.id,
375
725
  className: tfootTh(tableContext, footer.column.columnDef.meta?.footer?.classNames)
376
726
  }, footer.isPlaceholder ? null : flexRender2(footer.column.columnDef.footer, footer.getContext()));
377
- }), expand && /* @__PURE__ */ React6.createElement("th", null))));
727
+ }), expand && /* @__PURE__ */ React7.createElement("th", null))));
378
728
  };
379
729
  TableFooter.displayName = TABLE_FOOT_NAME;
380
730
 
381
731
  // packages/ui/react-ui-table/src/components/Table/TableHead.tsx
382
732
  import { flexRender as flexRender3 } from "@tanstack/react-table";
383
- import React7 from "react";
733
+ import React8 from "react";
384
734
  var TABLE_HEAD_NAME = "TableHeader";
385
735
  var TableHead = (_props) => {
386
736
  const tableContext = useTableContext();
387
737
  const headerGroups = tableContext.table.getHeaderGroups();
388
- return /* @__PURE__ */ React7.createElement("thead", {
738
+ return /* @__PURE__ */ React8.createElement("thead", {
389
739
  className: theadRoot(tableContext)
390
740
  }, headerGroups.map((headerGroup) => {
391
741
  return (
392
742
  // Group element to hover resize handles.
393
- /* @__PURE__ */ React7.createElement("tr", {
743
+ /* @__PURE__ */ React8.createElement("tr", {
394
744
  key: headerGroup.id,
395
745
  className: theadTr(tableContext)
396
- }, tableContext.debug && /* @__PURE__ */ React7.createElement("th", {
746
+ }, tableContext.debug && /* @__PURE__ */ React8.createElement("th", {
397
747
  className: "font-mono",
398
748
  style: {
399
749
  width: 32
@@ -402,13 +752,13 @@ var TableHead = (_props) => {
402
752
  const isResizing = header.column.getIsResizing();
403
753
  const onResize = header.getResizeHandler();
404
754
  const resizable = header.column.columnDef.meta?.resizable;
405
- return /* @__PURE__ */ React7.createElement("th", {
755
+ return /* @__PURE__ */ React8.createElement("th", {
406
756
  key: header.id,
407
757
  style: {
408
758
  width: header.getSize()
409
759
  },
410
760
  className: theadTh(tableContext, header.column.columnDef.meta?.header?.classNames)
411
- }, !header || header.isPlaceholder ? null : flexRender3(header.column.columnDef.header, header.getContext()), resizable && /* @__PURE__ */ React7.createElement("div", {
761
+ }, !header || header.isPlaceholder ? null : flexRender3(header.column.columnDef.header, header.getContext()), resizable && /* @__PURE__ */ React8.createElement("div", {
412
762
  className: theadResizeRoot({
413
763
  isResizing
414
764
  }),
@@ -416,100 +766,56 @@ var TableHead = (_props) => {
416
766
  onMouseDown: onResize,
417
767
  onTouchStart: onResize
418
768
  }));
419
- }), tableContext.expand && /* @__PURE__ */ React7.createElement("th", null))
769
+ }), tableContext.expand && /* @__PURE__ */ React8.createElement("th", null))
420
770
  );
421
771
  }));
422
772
  };
423
773
  TableHead.displayName = TABLE_HEAD_NAME;
424
774
 
425
- // packages/ui/react-ui-table/src/components/hooks/usePinLastRow.ts
426
- import { useEffect, useState as useState2 } from "react";
427
- var usePinLastRow = (pinLastRow, table, data, getScrollElement) => {
428
- const [pinnedRowKey, setPinnedRowKey] = useState2();
429
- useEffect(() => {
430
- if (!pinLastRow) {
431
- return;
432
- }
433
- const rows = table.getRowModel().rows;
434
- const rowToPin = rows[rows.length - 1];
435
- const rowId = rowToPin.id;
436
- if (rowId === pinnedRowKey) {
437
- return;
438
- }
439
- table.resetRowPinning();
440
- rowToPin.pin("bottom");
441
- const scrollElement = getScrollElement?.();
442
- const isFirstRender = pinnedRowKey === void 0;
443
- if (scrollElement && !isFirstRender) {
444
- requestAnimationFrame(() => {
445
- scrollElement.scrollTo({
446
- top: scrollElement.scrollHeight
447
- });
448
- });
449
- }
450
- setPinnedRowKey(rowId);
451
- }, [
452
- pinLastRow,
453
- table,
454
- getScrollElement,
455
- pinnedRowKey,
456
- setPinnedRowKey,
457
- data
458
- ]);
459
- };
460
-
461
775
  // packages/ui/react-ui-table/src/components/Table/Table.tsx
462
- var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/components/Table/Table.tsx";
463
- var Table = (props) => {
464
- const { role, onColumnResize, columnVisibility, header = true, rowsSelectable, debug, onDataSelectionChange, getScrollElement, pinLastRow } = props;
776
+ var TableRoot = ({ children }) => {
777
+ const contextValue = useTableRootContext();
778
+ return /* @__PURE__ */ React9.createElement(TableRootContext.Provider, {
779
+ value: contextValue
780
+ }, children);
781
+ };
782
+ var TableViewport = ({ children, classNames, asChild, ...props }) => {
783
+ const { scrollContextRef } = useContext3(TableRootContext);
784
+ const classes = mx5(classNames);
785
+ return asChild ? /* @__PURE__ */ React9.createElement(Slot, {
786
+ ref: scrollContextRef,
787
+ ...props
788
+ }, children) : /* @__PURE__ */ React9.createElement("div", {
789
+ role: "none",
790
+ className: classes,
791
+ ref: scrollContextRef,
792
+ ...props
793
+ }, children);
794
+ };
795
+ var TablePrimitive = (props) => {
796
+ const { role, onColumnResize, columnVisibility, header = true, rowsSelectable, debug, onDataSelectionChange, pinLastRow } = props;
465
797
  const columns = useDefaultValue(props.columns, []);
466
- const data = useDefaultValue(props.data, []);
467
- const TableProvider2 = TableProvider;
468
- const [columnsInitialised, setColumnsInitialised] = useState3(false);
469
- const [columnSizing, setColumnSizing] = useState3({});
470
- useEffect2(() => {
471
- if (columnsInitialised) {
472
- return;
473
- }
474
- setColumnSizing(columns.filter((column) => !!column.size && column.prop !== void 0).reduce((state, column) => {
475
- state[column.prop] = column.size;
476
- return state;
477
- }, {}));
478
- setColumnsInitialised(true);
479
- }, [
480
- columns,
481
- setColumnSizing
798
+ const incomingData = useDefaultValue(props.data, []);
799
+ const [data, setData] = useState8([
800
+ ...incomingData
482
801
  ]);
483
- const [columnSizingInfo, setColumnSizingInfo] = useState3({});
484
- const notifyColumnResize = useCallback(() => onColumnResize?.(columnSizing), [
485
- onColumnResize,
486
- columnSizing
802
+ const forceUpdate = useCallback6(() => setData([
803
+ ...incomingData
804
+ ]), [
805
+ incomingData
487
806
  ]);
488
- useOnTransition(columnSizingInfo.isResizingColumn, (v) => typeof v === "string", false, notifyColumnResize);
489
- const [rowSelection = {}, setRowSelection] = useControllableState({
490
- prop: props.rowSelection,
491
- onChange: props.onRowSelectionChange,
492
- defaultProp: props.defaultRowSelection
493
- });
494
- const handleRowSelectionChange = useCallback((updaterOrValue) => {
495
- const nextRowSelection = typeof updaterOrValue === "function" ? updaterOrValue(rowSelection) : updaterOrValue;
496
- if (rowsSelectable === "multi") {
497
- setRowSelection(nextRowSelection);
498
- } else if (rowsSelectable) {
499
- const nextRowSelectionKey = Object.keys(nextRowSelection).filter((id) => !rowSelection[id])[0];
500
- setRowSelection(nextRowSelectionKey ? {
501
- [nextRowSelectionKey]: true
502
- } : {});
503
- } else {
504
- setRowSelection({});
505
- }
506
- }, [
507
- rowsSelectable,
508
- setRowSelection,
509
- rowSelection
807
+ useEffect3(() => forceUpdate(), [
808
+ JSON.stringify(incomingData),
809
+ forceUpdate
510
810
  ]);
511
- const [grouping, handleGroupingChange] = useState3(props.grouping ?? []);
512
- useEffect2(() => handleGroupingChange(props.grouping ?? []), [
811
+ const TableProvider2 = TableProvider;
812
+ const { columnSizing, setColumnSizing, columnSizingInfo, setColumnSizingInfo } = useColumnResizing({
813
+ columns,
814
+ onColumnResize
815
+ });
816
+ const { rowSelection, handleRowSelectionChange } = useRowSelection(props);
817
+ const [grouping, handleGroupingChange] = useState8(props.grouping ?? []);
818
+ useEffect3(() => handleGroupingChange(props.grouping ?? []), [
513
819
  handleGroupingChange,
514
820
  props.grouping
515
821
  ]);
@@ -551,52 +857,37 @@ var Table = (props) => {
551
857
  // Debug
552
858
  debugTable: debug
553
859
  });
554
- useEffect2(() => {
860
+ useEffect3(() => {
555
861
  onDataSelectionChange?.(Object.keys(rowSelection).map((id) => table.getRowModel().rowsById[id].original));
556
862
  }, [
557
863
  onDataSelectionChange,
558
864
  rowSelection,
559
865
  table
560
866
  ]);
561
- usePinLastRow(pinLastRow, table, data, getScrollElement);
867
+ usePinLastRow(pinLastRow, table, data);
562
868
  const expand = false;
563
- if (!getScrollElement) {
564
- log.warn("Table: getScrollElement is not set. This is required for virtualized tables.", void 0, {
565
- F: __dxlog_file,
566
- L: 159,
567
- S: void 0,
568
- C: (f, a) => f(...a)
569
- });
570
- }
571
- return /* @__PURE__ */ React8.createElement(TableProvider2, {
869
+ return /* @__PURE__ */ React9.createElement(TableProvider2, {
572
870
  ...props,
573
871
  table,
574
872
  header,
575
873
  expand,
576
874
  isGrid: role === "grid" || role === "treegrid"
577
- }, /* @__PURE__ */ React8.createElement(TableImpl, {
875
+ }, /* @__PURE__ */ React9.createElement(TableImpl, {
578
876
  debug: false,
579
- getScrollElement,
580
877
  ...props
581
878
  }));
582
879
  };
583
880
  var TableImpl = (props) => {
584
- const { debug, classNames, getScrollElement, role, footer, grouping, fullWidth } = props;
881
+ const { debug, classNames, role, footer, grouping, fullWidth } = props;
585
882
  const { table } = useTableContext();
586
- const centerRows = table.getCenterRows();
587
- const bottomRows = table.getBottomRows();
588
- const rows = [
589
- ...centerRows,
590
- ...bottomRows
591
- ];
592
883
  if (debug) {
593
- return /* @__PURE__ */ React8.createElement("pre", {
884
+ return /* @__PURE__ */ React9.createElement("pre", {
594
885
  className: "font-mono text-xs text-neutral-500 m-1 p-2 ring"
595
- }, /* @__PURE__ */ React8.createElement("code", null, JSON.stringify(table.getState(), void 0, 2)));
886
+ }, /* @__PURE__ */ React9.createElement("code", null, JSON.stringify(table.getState(), void 0, 2)));
596
887
  }
597
- const virtualisable = getScrollElement !== void 0;
598
888
  const isResizingColumn = table.getState().columnSizingInfo.isResizingColumn;
599
- return /* @__PURE__ */ React8.createElement("table", {
889
+ const TableComponent = isResizingColumn ? MemoizedVirtualisedTableContent : VirtualizedTableContent;
890
+ return /* @__PURE__ */ React9.createElement("table", {
600
891
  role,
601
892
  className: tableRoot(props, classNames),
602
893
  ...!fullWidth && {
@@ -604,24 +895,26 @@ var TableImpl = (props) => {
604
895
  width: table.getTotalSize()
605
896
  }
606
897
  }
607
- }, /* @__PURE__ */ React8.createElement(TableHead, null), grouping?.length !== 0 ? virtualisable ? isResizingColumn ? /* @__PURE__ */ React8.createElement(MemoizedVirtualisedTableContent, {
608
- getScrollElement
609
- }) : /* @__PURE__ */ React8.createElement(VirtualizedTableContent, {
610
- getScrollElement
611
- }) : /* @__PURE__ */ React8.createElement(TableBody, {
612
- rows
613
- }) : /* @__PURE__ */ React8.createElement(GroupedTableContent, null), footer && /* @__PURE__ */ React8.createElement(TableFooter, null));
898
+ }, /* @__PURE__ */ React9.createElement(TableHead, null), grouping?.length !== 0 ? /* @__PURE__ */ React9.createElement(TableComponent, null) : /* @__PURE__ */ React9.createElement(GroupedTableContent, null), footer && /* @__PURE__ */ React9.createElement(TableFooter, null));
614
899
  };
615
- var VirtualizedTableContent = ({ getScrollElement }) => {
900
+ var VirtualizedTableContent = () => {
616
901
  const { table } = useTableContext();
902
+ const { virtualizer, dispatch } = useContext3(TableRootContext);
617
903
  const centerRows = table.getCenterRows();
618
- const pinnedRows = table.getBottomRows();
619
- const { getTotalSize, getVirtualItems } = useVirtualizer({
620
- getScrollElement,
621
- count: centerRows.length,
622
- overscan: 8,
623
- estimateSize: () => 40
624
- });
904
+ useEffect3(() => {
905
+ dispatch({
906
+ type: "updateTableCount",
907
+ count: centerRows.length
908
+ });
909
+ }, [
910
+ centerRows.length
911
+ ]);
912
+ let pinnedRows = [];
913
+ try {
914
+ pinnedRows = table.getBottomRows();
915
+ } catch (_) {
916
+ }
917
+ const { getTotalSize, getVirtualItems } = virtualizer;
625
918
  const virtualRows = getVirtualItems();
626
919
  const totalSize = getTotalSize();
627
920
  const paddingTop = virtualRows.length > 0 ? virtualRows?.[0]?.start || 0 : 0;
@@ -630,124 +923,60 @@ var VirtualizedTableContent = ({ getScrollElement }) => {
630
923
  ...virtualRows.map((virtualRow) => centerRows[virtualRow.index]),
631
924
  ...pinnedRows
632
925
  ];
633
- return /* @__PURE__ */ React8.createElement(React8.Fragment, null, paddingTop > 0 && /* @__PURE__ */ React8.createElement("tbody", {
926
+ return /* @__PURE__ */ React9.createElement(Fragment, null, paddingTop > 0 && /* @__PURE__ */ React9.createElement("tbody", {
634
927
  role: "none"
635
- }, /* @__PURE__ */ React8.createElement("tr", {
928
+ }, /* @__PURE__ */ React9.createElement("tr", {
636
929
  role: "none"
637
- }, /* @__PURE__ */ React8.createElement("td", {
930
+ }, /* @__PURE__ */ React9.createElement("td", {
638
931
  style: {
639
932
  height: `${paddingTop}px`
640
933
  },
641
934
  role: "none"
642
- }))), /* @__PURE__ */ React8.createElement(TableBody, {
935
+ }))), /* @__PURE__ */ React9.createElement(TableBody, {
643
936
  rows: rowsToRender
644
- }), paddingBottom > 0 && /* @__PURE__ */ React8.createElement("tbody", {
937
+ }), paddingBottom > 0 && /* @__PURE__ */ React9.createElement("tbody", {
645
938
  role: "none"
646
- }, /* @__PURE__ */ React8.createElement("tr", {
939
+ }, /* @__PURE__ */ React9.createElement("tr", {
647
940
  role: "none"
648
- }, /* @__PURE__ */ React8.createElement("td", {
941
+ }, /* @__PURE__ */ React9.createElement("td", {
649
942
  style: {
650
943
  height: `${paddingBottom}px`
651
944
  },
652
945
  role: "none"
653
946
  }))));
654
947
  };
655
- var MemoizedVirtualisedTableContent = /* @__PURE__ */ React8.memo(VirtualizedTableContent);
948
+ var MemoizedVirtualisedTableContent = /* @__PURE__ */ React9.memo(VirtualizedTableContent);
656
949
  var GroupedTableContent = () => {
657
950
  const { table: { getGroupedRowModel: getGroupedRowModel2, getHeaderGroups, getState }, debug } = useTableContext();
658
- return /* @__PURE__ */ React8.createElement(React8.Fragment, null, getGroupedRowModel2().rows.map((row, i) => {
659
- return /* @__PURE__ */ React8.createElement(Fragment, {
951
+ return /* @__PURE__ */ React9.createElement(React9.Fragment, null, getGroupedRowModel2().rows.map((row, i) => {
952
+ return /* @__PURE__ */ React9.createElement(Fragment, {
660
953
  key: i
661
- }, /* @__PURE__ */ React8.createElement("thead", null, /* @__PURE__ */ React8.createElement("tr", null, debug && /* @__PURE__ */ React8.createElement("th", null), /* @__PURE__ */ React8.createElement("th", {
954
+ }, /* @__PURE__ */ React9.createElement("thead", null, /* @__PURE__ */ React9.createElement("tr", null, debug && /* @__PURE__ */ React9.createElement("th", null), /* @__PURE__ */ React9.createElement("th", {
662
955
  // TODO(burdon): Calculate row span.
663
956
  colSpan: getHeaderGroups()[0].headers.length,
664
957
  className: groupTh({})
665
- }, getState().grouping[0], "[", String(row.getGroupingValue(getState().grouping[0])), "]"))), /* @__PURE__ */ React8.createElement(TableBody, {
958
+ }, getState().grouping[0], "[", String(row.getGroupingValue(getState().grouping[0])), "]"))), /* @__PURE__ */ React9.createElement(TableBody, {
666
959
  rows: row.subRows
667
960
  }));
668
961
  }));
669
962
  };
670
-
671
- // packages/ui/react-ui-table/src/components/Cell/CellCombobox.tsx
672
- import { CaretDown } from "@phosphor-icons/react";
673
- import React9, { useCallback as useCallback2, useState as useState4 } from "react";
674
- import { Popover as Popover2, useTranslation as useTranslation2 } from "@dxos/react-ui";
675
- import { PopoverCombobox } from "@dxos/react-ui-searchlist";
676
- import { descriptionText, getSize as getSize3, mx as mx3, staticPlaceholderText } from "@dxos/react-ui-theme";
677
- var initialItems = (model, value) => value ? {
678
- [model.getId(value)]: {
679
- label: model.getText(value),
680
- data: value
681
- }
682
- } : {};
683
- var CellCombobox = ({ model, value, onValueChange }) => {
684
- const { t } = useTranslation2(translationKey);
685
- const [items, setItems] = useState4(initialItems(model, value));
686
- const [loading, setLoading] = useState4(false);
687
- const [searchInputValue, setSearchInputValue] = useState4("");
688
- const handleSearchInputValueChange = async (text) => {
689
- setSearchInputValue(text ?? "");
690
- setLoading(true);
691
- const items2 = await model.query(text);
692
- setItems(items2.reduce((acc, item) => {
693
- acc[model.getId(item)] = {
694
- label: model.getText(item),
695
- data: item
696
- };
697
- return acc;
698
- }, initialItems(model, value)));
699
- setLoading(false);
700
- };
701
- const handleValueChange = useCallback2((id) => {
702
- onValueChange(items[id]?.data);
703
- }, [
704
- items,
705
- onValueChange
706
- ]);
707
- return /* @__PURE__ */ React9.createElement(PopoverCombobox.Root, {
708
- placeholder: t("no selection label")
709
- }, /* @__PURE__ */ React9.createElement(PopoverCombobox.Trigger, {
710
- variant: "ghost",
711
- classNames: "is-full rounded-none ring-inset"
712
- }, value ? /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement("span", {
713
- className: mx3("font-normal text-start text-base flex-1 min-is-0 truncate mie-2", !value && staticPlaceholderText)
714
- }, model.getText(value)), /* @__PURE__ */ React9.createElement(CaretDown, {
715
- weight: "bold",
716
- className: getSize3(3)
717
- })) : null), /* @__PURE__ */ React9.createElement(PopoverCombobox.Content, {
718
- shouldFilter: false,
719
- classNames: "is-[--radix-popover-trigger-width]"
720
- }, /* @__PURE__ */ React9.createElement(PopoverCombobox.Input, {
721
- value: searchInputValue,
722
- onValueChange: handleSearchInputValueChange
723
- }), /* @__PURE__ */ React9.createElement(PopoverCombobox.List, null, searchInputValue.length < 1 ? /* @__PURE__ */ React9.createElement("p", {
724
- className: mx3("text-center", descriptionText)
725
- }, "Start typing to search") : loading ? /* @__PURE__ */ React9.createElement("p", {
726
- className: mx3("text-center", descriptionText)
727
- }, "Searching\u2026") : /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(PopoverCombobox.Empty, {
728
- classNames: [
729
- "text-center",
730
- descriptionText
731
- ]
732
- }, "No results"), Object.entries(items).map(([id, { label }]) => /* @__PURE__ */ React9.createElement(PopoverCombobox.Item, {
733
- key: id,
734
- value: id,
735
- onSelect: handleValueChange,
736
- classNames: "truncate"
737
- }, label)))), /* @__PURE__ */ React9.createElement(Popover2.Arrow, null)));
963
+ var Table = {
964
+ Root: TableRoot,
965
+ Table: TablePrimitive,
966
+ Viewport: TableViewport
738
967
  };
739
968
 
740
969
  // packages/ui/react-ui-table/src/helpers.tsx
741
970
  import { useFocusFinders } from "@fluentui/react-tabster";
742
- import { Check as Check2, ClipboardText, X as X2 } from "@phosphor-icons/react";
971
+ import { Check as Check2, ClipboardText, X as X3 } from "@phosphor-icons/react";
743
972
  import { createColumnHelper } from "@tanstack/react-table";
744
973
  import { format } from "date-fns/format";
745
974
  import { formatDistanceToNow } from "date-fns/formatDistanceToNow";
746
- import React10, { useEffect as useEffect3, useRef as useRef2, useState as useState5 } from "react";
747
- import { log as log2 } from "@dxos/log";
975
+ import React10, { useCallback as useCallback7, useEffect as useEffect4, useRef as useRef4, useState as useState9 } from "react";
976
+ import { log } from "@dxos/log";
748
977
  import { Input as Input2, Tooltip } from "@dxos/react-ui";
749
- import { getSize as getSize4, mx as mx4 } from "@dxos/react-ui-theme";
750
- var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/helpers.tsx";
978
+ import { getSize as getSize4, mx as mx6 } from "@dxos/react-ui-theme";
979
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/helpers.tsx";
751
980
  var createColumnBuilder = () => ({
752
981
  helper: createColumnHelper(),
753
982
  builder: new ColumnBuilder()
@@ -761,16 +990,19 @@ var ComboboxBuilderCell = (cellContext) => {
761
990
  }) : null;
762
991
  };
763
992
  var StringBuilderCell = (cellContext) => {
764
- const ref = useRef2(null);
993
+ const ref = useRef4(null);
765
994
  const { findPrevFocusable } = useFocusFinders();
766
995
  const { onUpdate, cell } = cellContext.column.columnDef.meta;
767
996
  const initialValue = cellContext.getValue();
768
- const [value, setValue] = useState5(() => {
769
- if (initialValue?.content) {
770
- return initialValue?.content;
771
- }
772
- return initialValue;
773
- });
997
+ const getInitialValue = useCallback7(() => {
998
+ return initialValue?.content ?? initialValue;
999
+ }, [
1000
+ initialValue
1001
+ ]);
1002
+ const [value, setValue] = useState9(getInitialValue);
1003
+ useEffect4(() => setValue(getInitialValue()), [
1004
+ getInitialValue
1005
+ ]);
774
1006
  const handleSave = () => {
775
1007
  ref.current && document.activeElement === ref.current && findPrevFocusable(ref.current)?.focus();
776
1008
  if (value !== initialValue) {
@@ -807,10 +1039,10 @@ var displayNumber = (value, digits) => value || value === 0 ? value.toLocaleStri
807
1039
  var NumberBuilderCell = (cellContext) => {
808
1040
  const { onUpdate, digits } = cellContext.column.columnDef.meta;
809
1041
  const value = cellContext.getValue();
810
- const [text, setText] = useState5(displayNumber(value, digits));
1042
+ const [text, setText] = useState9(displayNumber(value, digits));
811
1043
  const { findPrevFocusable } = useFocusFinders();
812
- const ref = useRef2(null);
813
- useEffect3(() => {
1044
+ const ref = useRef4(null);
1045
+ useEffect4(() => {
814
1046
  (!document.activeElement || document.activeElement !== ref.current) && setText(displayNumber(value, digits));
815
1047
  }, [
816
1048
  digits,
@@ -897,7 +1129,7 @@ var ColumnBuilder = class {
897
1129
  cell: model && onUpdate ? ComboboxBuilderCell : (cellContext) => {
898
1130
  const cellValue = cellContext.getValue();
899
1131
  return /* @__PURE__ */ React10.createElement("div", {
900
- className: mx4("truncate", classNames)
1132
+ className: mx6("truncate", classNames)
901
1133
  }, cellValue ? model?.getText(cellValue) : "");
902
1134
  },
903
1135
  ...props,
@@ -928,7 +1160,7 @@ var ColumnBuilder = class {
928
1160
  value = value.content;
929
1161
  }
930
1162
  return /* @__PURE__ */ React10.createElement("div", {
931
- className: mx4("truncate", textPadding, classNames)
1163
+ className: mx6("truncate", textPadding, classNames)
932
1164
  }, value);
933
1165
  },
934
1166
  ...props,
@@ -957,7 +1189,6 @@ var ColumnBuilder = class {
957
1189
  onUpdate,
958
1190
  digits,
959
1191
  header: {
960
- classNames: "text-end",
961
1192
  ...props.meta?.header
962
1193
  },
963
1194
  cell: {
@@ -983,14 +1214,14 @@ var ColumnBuilder = class {
983
1214
  addSuffix: true
984
1215
  }) : value.toISOString() : void 0;
985
1216
  return /* @__PURE__ */ React10.createElement("div", {
986
- className: mx4(textPadding, classNames)
1217
+ className: mx6(textPadding, classNames)
987
1218
  }, str);
988
1219
  } catch (err) {
989
- log2.info("formatError", {
1220
+ log.info("formatError", {
990
1221
  value
991
1222
  }, {
992
- F: __dxlog_file2,
993
- L: 374,
1223
+ F: __dxlog_file,
1224
+ L: 376,
994
1225
  S: this,
995
1226
  C: (f, a) => f(...a)
996
1227
  });
@@ -1110,7 +1341,7 @@ var ColumnBuilder = class {
1110
1341
  */
1111
1342
  icon({ label, size, on, off, ...props } = {}) {
1112
1343
  const IconOn = on?.Icon ?? Check2;
1113
- const IconOff = off?.Icon ?? X2;
1344
+ const IconOff = off?.Icon ?? X3;
1114
1345
  return {
1115
1346
  size: size ?? 32,
1116
1347
  // TODO(Zan): Implement sort algorithm
@@ -1122,11 +1353,11 @@ var ColumnBuilder = class {
1122
1353
  const value = cell.getValue();
1123
1354
  if (value) {
1124
1355
  return /* @__PURE__ */ React10.createElement(IconOn, {
1125
- className: mx4("block mli-auto", getSize4(6), on?.className ?? "text-green-700")
1356
+ className: mx6("block mli-auto", getSize4(6), on?.className ?? "text-green-700")
1126
1357
  });
1127
1358
  } else if (value === false) {
1128
1359
  return /* @__PURE__ */ React10.createElement(IconOff, {
1129
- className: mx4("block mli-auto", getSize4(6), off?.className ?? "text-red-700")
1360
+ className: mx6("block mli-auto", getSize4(6), off?.className ?? "text-red-700")
1130
1361
  });
1131
1362
  } else {
1132
1363
  return null;
@@ -1135,20 +1366,175 @@ var ColumnBuilder = class {
1135
1366
  ...props
1136
1367
  };
1137
1368
  }
1369
+ /**
1370
+ * This display column type is for nested/unhandled data to appear as JSON.
1371
+ */
1372
+ display({ label, classNames, ...props } = {}) {
1373
+ return {
1374
+ size: 120,
1375
+ minSize: 120,
1376
+ header: (cell) => label ?? cell.header.id,
1377
+ cell: (cell) => {
1378
+ const value = cell.getValue();
1379
+ return /* @__PURE__ */ React10.createElement("div", {
1380
+ className: "overflow-scroll"
1381
+ }, /* @__PURE__ */ React10.createElement("code", {
1382
+ className: "text-xs"
1383
+ }, JSON.stringify(value)));
1384
+ },
1385
+ ...props,
1386
+ meta: {
1387
+ ...props.meta,
1388
+ cell: {
1389
+ ...props.meta?.cell,
1390
+ classNames: [
1391
+ "font-mono",
1392
+ textPadding,
1393
+ classNames
1394
+ ]
1395
+ }
1396
+ }
1397
+ };
1398
+ }
1399
+ };
1400
+
1401
+ // packages/ui/react-ui-table/src/schema/schema.ts
1402
+ import * as AST from "@effect/schema/AST";
1403
+ var classifySchemaProperties = (schema) => {
1404
+ const recurse = (node, path, acc) => {
1405
+ const properties = AST.getPropertySignatures(node);
1406
+ properties.forEach((prop) => {
1407
+ const propName = prop.name.toString();
1408
+ if (prop.isOptional) {
1409
+ const unwrappedAst = unwrapOptionProperty(prop);
1410
+ if (isStruct(unwrappedAst)) {
1411
+ return recurse(unwrappedAst, [
1412
+ ...path,
1413
+ propName
1414
+ ], acc);
1415
+ }
1416
+ }
1417
+ if (isStruct(prop.type)) {
1418
+ recurse(prop.type, [
1419
+ ...path,
1420
+ propName
1421
+ ], acc);
1422
+ } else {
1423
+ acc.push([
1424
+ path.concat(propName).join("."),
1425
+ propertyToColumn(prop)
1426
+ ]);
1427
+ }
1428
+ });
1429
+ return acc;
1430
+ };
1431
+ return recurse(schema.ast, [], []);
1432
+ };
1433
+ var isStruct = (node) => AST.isTypeLiteral(node);
1434
+ var isOptionalUnion = (prop) => AST.isUnion(prop.type) && prop.isOptional;
1435
+ var unwrapOptionProperty = (prop) => {
1436
+ if (!isOptionalUnion(prop)) {
1437
+ throw new Error(`Not an optional property: ${String(prop.name)}`);
1438
+ }
1439
+ if (!AST.isUnion(prop.type)) {
1440
+ throw new Error(`Not a union type: ${String(prop.name)}`);
1441
+ }
1442
+ const [type, _undefinedCase] = prop.type.types;
1443
+ return type;
1444
+ };
1445
+ var typeToColumn = (type) => {
1446
+ if (AST.isStringKeyword(type)) {
1447
+ return "string";
1448
+ } else if (AST.isNumberKeyword(type)) {
1449
+ return "number";
1450
+ } else if (AST.isBooleanKeyword(type)) {
1451
+ return "boolean";
1452
+ }
1453
+ if (AST.isRefinement(type)) {
1454
+ return typeToColumn(type.from);
1455
+ }
1456
+ if (AST.isTransform(type)) {
1457
+ const identifier = AST.getIdentifierAnnotation(type);
1458
+ if (identifier._tag === "Some") {
1459
+ if (identifier.value === "DateFromString") {
1460
+ return "date";
1461
+ }
1462
+ }
1463
+ }
1464
+ return "display";
1465
+ };
1466
+ var propertyToColumn = (prop) => {
1467
+ let type = prop.type;
1468
+ if (prop.isOptional) {
1469
+ type = unwrapOptionProperty(prop);
1470
+ }
1471
+ return typeToColumn(type);
1472
+ };
1473
+
1474
+ // packages/ui/react-ui-table/src/schema/schemaToColumns.ts
1475
+ var schemaToColumnDefs = (schema) => {
1476
+ const { helper, builder } = createColumnBuilder();
1477
+ const classified = classifySchemaProperties(schema);
1478
+ return classified.map(([name, type]) => {
1479
+ const propertyKey = name.toString();
1480
+ let column;
1481
+ switch (type) {
1482
+ case "string": {
1483
+ column = builder.string({
1484
+ label: propertyKey
1485
+ });
1486
+ break;
1487
+ }
1488
+ case "number": {
1489
+ column = builder.number({
1490
+ label: propertyKey
1491
+ });
1492
+ break;
1493
+ }
1494
+ case "boolean": {
1495
+ column = builder.switch({
1496
+ label: propertyKey
1497
+ });
1498
+ break;
1499
+ }
1500
+ case "date": {
1501
+ column = builder.date({
1502
+ label: propertyKey
1503
+ });
1504
+ break;
1505
+ }
1506
+ case "display": {
1507
+ column = builder.display({
1508
+ label: propertyKey,
1509
+ id: propertyKey
1510
+ });
1511
+ break;
1512
+ }
1513
+ default: {
1514
+ throw new Error(`Unhandled column type: ${type}`);
1515
+ }
1516
+ }
1517
+ return helper.accessor(propertyKey, column);
1518
+ });
1138
1519
  };
1139
1520
  export {
1140
1521
  CellCombobox,
1141
1522
  ColumnBuilder,
1142
1523
  ColumnMenu,
1143
- ColumnPanel,
1524
+ ColumnSettingsForm,
1525
+ ColumnSettingsPanel,
1144
1526
  MemoizedVirtualisedTableContent,
1527
+ SortIndicator,
1145
1528
  Table,
1529
+ TablePrimitive,
1530
+ TableRootContext,
1146
1531
  createColumnBuilder,
1147
1532
  currentRow,
1148
1533
  flushPadding,
1149
1534
  gridCellFocusRing,
1150
1535
  groupTh,
1151
1536
  headPadding,
1537
+ schemaToColumnDefs,
1152
1538
  selectedRow,
1153
1539
  tableRoot,
1154
1540
  tbodyRoot,
@@ -1163,6 +1549,11 @@ export {
1163
1549
  theadRoot,
1164
1550
  theadTh,
1165
1551
  theadTr,
1166
- translations_default as translations
1552
+ translations_default as translations,
1553
+ useColumnResizing,
1554
+ useColumnSorting,
1555
+ usePinLastRow,
1556
+ useRowSelection,
1557
+ useTableRootContext
1167
1558
  };
1168
1559
  //# sourceMappingURL=index.mjs.map