@dxos/react-ui-table 0.4.9 → 0.4.10-main.06ef97a

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 (71) hide show
  1. package/dist/lib/browser/index.mjs +558 -266
  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} +5 -1
  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.map +1 -1
  13. package/dist/types/src/components/Table/Table.stories.d.ts +3 -0
  14. package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
  15. package/dist/types/src/components/Table/TableBody.d.ts.map +1 -1
  16. package/dist/types/src/components/index.d.ts +1 -1
  17. package/dist/types/src/components/index.d.ts.map +1 -1
  18. package/dist/types/src/helpers.d.ts +4 -0
  19. package/dist/types/src/helpers.d.ts.map +1 -1
  20. package/dist/types/src/hooks/index.d.ts +3 -0
  21. package/dist/types/src/hooks/index.d.ts.map +1 -0
  22. package/dist/types/src/hooks/useColumnSorting.d.ts +9 -0
  23. package/dist/types/src/hooks/useColumnSorting.d.ts.map +1 -0
  24. package/dist/types/src/hooks/usePinLastRow.d.ts +3 -0
  25. package/dist/types/src/hooks/usePinLastRow.d.ts.map +1 -0
  26. package/dist/types/src/index.d.ts +2 -1
  27. package/dist/types/src/index.d.ts.map +1 -1
  28. package/dist/types/src/schema/SchemaToTable.stories.d.ts +11 -0
  29. package/dist/types/src/schema/SchemaToTable.stories.d.ts.map +1 -0
  30. package/dist/types/src/schema/index.d.ts +3 -0
  31. package/dist/types/src/schema/index.d.ts.map +1 -0
  32. package/dist/types/src/schema/schema.d.ts +5 -0
  33. package/dist/types/src/schema/schema.d.ts.map +1 -0
  34. package/dist/types/src/schema/schema.test.d.ts +2 -0
  35. package/dist/types/src/schema/schema.test.d.ts.map +1 -0
  36. package/dist/types/src/schema/schemaToColumns.d.ts +4 -0
  37. package/dist/types/src/schema/schemaToColumns.d.ts.map +1 -0
  38. package/dist/types/src/{schema.d.ts → schema/types.d.ts} +1 -1
  39. package/dist/types/src/schema/types.d.ts.map +1 -0
  40. package/dist/types/src/theme.d.ts +1 -0
  41. package/dist/types/src/theme.d.ts.map +1 -1
  42. package/package.json +18 -15
  43. package/src/components/ColumnMenu/ColumnMenu.stories.tsx +97 -0
  44. package/src/components/ColumnMenu/ColumnMenu.tsx +166 -0
  45. package/src/components/ColumnMenu/ColumnSettingsForm.tsx +208 -0
  46. package/src/components/ColumnMenu/index.ts +6 -0
  47. package/src/components/Table/Table.stories.tsx +31 -0
  48. package/src/components/Table/Table.tsx +6 -15
  49. package/src/components/Table/TableBody.tsx +6 -1
  50. package/src/components/index.ts +1 -1
  51. package/src/helpers.tsx +23 -1
  52. package/src/hooks/index.ts +6 -0
  53. package/src/hooks/useColumnSorting.ts +54 -0
  54. package/src/hooks/usePinLastRow.ts +43 -0
  55. package/src/index.ts +3 -1
  56. package/src/schema/SchemaToTable.stories.tsx +72 -0
  57. package/src/schema/index.ts +6 -0
  58. package/src/schema/schema.test.ts +148 -0
  59. package/src/schema/schema.ts +93 -0
  60. package/src/schema/schemaToColumns.ts +47 -0
  61. package/src/theme.ts +3 -2
  62. package/dist/types/src/components/ColumnMenu.d.ts +0 -13
  63. package/dist/types/src/components/ColumnMenu.d.ts.map +0 -1
  64. package/dist/types/src/components/ColumnMenu.stories.d.ts.map +0 -1
  65. package/dist/types/src/components/SortButton.d.ts +0 -8
  66. package/dist/types/src/components/SortButton.d.ts.map +0 -1
  67. package/dist/types/src/schema.d.ts.map +0 -1
  68. package/src/components/ColumnMenu.stories.tsx +0 -46
  69. package/src/components/ColumnMenu.tsx +0 -248
  70. package/src/components/SortButton.tsx +0 -34
  71. /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,45 +21,103 @@ 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 useState5, useCallback as useCallback2 } 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 [prop, setProp] = useState2(column.id);
111
+ const [refTable, setRefTable] = useState2(column.refTable);
112
+ const [refProp, setRefProp] = useState2(column.refProp);
113
+ const [type, setType] = useState2(String(column.type));
114
+ const [label, setLabel] = useState2(column.label);
115
+ const [digits, setDigits] = useState2(String(column.digits ?? "0"));
95
116
  const propRef = useRef(null);
96
- const { t } = useTranslation(translationKey);
117
+ const { t } = useTranslation2(translationKey);
97
118
  const handleCancel = () => {
98
119
  setProp(column.id);
99
- setOpen(false);
120
+ onClose?.();
100
121
  };
101
122
  const handleSave = () => {
102
123
  if (!prop.length || !prop.match(/^[a-zA-Z_].+/i)) {
@@ -119,22 +140,9 @@ var ColumnPanel = ({ context, tableDefs, tableDef, column, onUpdate, onDelete })
119
140
  refProp,
120
141
  digits: safeParseInt(digits)
121
142
  });
122
- setOpen(false);
143
+ onClose?.();
123
144
  };
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", {
145
+ return /* @__PURE__ */ React2.createElement("div", {
138
146
  className: "flex flex-col gap-1"
139
147
  }, /* @__PURE__ */ React2.createElement(Section, null, /* @__PURE__ */ React2.createElement(Input.Root, null, /* @__PURE__ */ React2.createElement(Input.Label, {
140
148
  classNames: "mbe-1"
@@ -198,7 +206,7 @@ var ColumnPanel = ({ context, tableDefs, tableDef, column, onUpdate, onDelete })
198
206
  classNames: "mli-2"
199
207
  }), /* @__PURE__ */ React2.createElement(Section, {
200
208
  className: "space-b-1.5"
201
- }, /* @__PURE__ */ React2.createElement(Button2, {
209
+ }, /* @__PURE__ */ React2.createElement(Button, {
202
210
  variant: "primary",
203
211
  classNames: "is-full flex gap-2",
204
212
  onClick: handleSave
@@ -206,7 +214,7 @@ var ColumnPanel = ({ context, tableDefs, tableDef, column, onUpdate, onDelete })
206
214
  className: "grow"
207
215
  }), /* @__PURE__ */ React2.createElement(Check, {
208
216
  className: getSize2(5)
209
- })), /* @__PURE__ */ React2.createElement(Button2, {
217
+ })), /* @__PURE__ */ React2.createElement(Button, {
210
218
  classNames: "is-full flex gap-2",
211
219
  onClick: handleCancel
212
220
  }, /* @__PURE__ */ React2.createElement("span", null, t("cancel label", {
@@ -215,34 +223,241 @@ var ColumnPanel = ({ context, tableDefs, tableDef, column, onUpdate, onDelete })
215
223
  className: "grow"
216
224
  }), /* @__PURE__ */ React2.createElement(X, {
217
225
  className: getSize2(5)
218
- })), /* @__PURE__ */ React2.createElement(Button2, {
226
+ })), /* @__PURE__ */ React2.createElement(Button, {
219
227
  classNames: "is-full flex gap-2",
220
228
  onClick: () => onDelete?.(column.id)
221
229
  }, /* @__PURE__ */ React2.createElement("span", null, t("delete label")), /* @__PURE__ */ React2.createElement("div", {
222
230
  className: "grow"
223
231
  }), /* @__PURE__ */ React2.createElement(Trash, {
224
232
  className: getSize2(5)
225
- })))))), /* @__PURE__ */ React2.createElement(Popover.Arrow, null))));
233
+ }))));
234
+ };
235
+
236
+ // packages/ui/react-ui-table/src/hooks/useColumnSorting.ts
237
+ import { useState as useState3 } from "react";
238
+ var useColumnSorting = (column) => {
239
+ const [sortDirection, setSortDirection] = useState3(column.getIsSorted());
240
+ const [firstSort, setFirstSort] = useState3();
241
+ const canSort = column.getCanSort();
242
+ const updateColumnSort = (direction) => {
243
+ setSortDirection(direction);
244
+ switch (direction) {
245
+ case false:
246
+ column.clearSorting();
247
+ break;
248
+ case "asc":
249
+ column.toggleSorting(false);
250
+ break;
251
+ case "desc":
252
+ column.toggleSorting(true);
253
+ break;
254
+ }
255
+ };
256
+ const onToggleSort = () => {
257
+ if (sortDirection === false) {
258
+ return;
259
+ }
260
+ if (sortDirection === firstSort) {
261
+ updateColumnSort(firstSort === "asc" ? "desc" : "asc");
262
+ } else {
263
+ updateColumnSort(false);
264
+ }
265
+ };
266
+ const onSelectSort = (direction) => {
267
+ setFirstSort(direction);
268
+ updateColumnSort(direction);
269
+ };
270
+ const onClearSort = () => {
271
+ updateColumnSort(false);
272
+ };
273
+ return {
274
+ canSort,
275
+ sortDirection,
276
+ onSelectSort,
277
+ onToggleSort,
278
+ onClearSort
279
+ };
280
+ };
281
+
282
+ // packages/ui/react-ui-table/src/hooks/usePinLastRow.ts
283
+ import { useEffect, useState as useState4 } from "react";
284
+ var usePinLastRow = (pinLastRow, table, data, getScrollElement) => {
285
+ const [pinnedRowKey, setPinnedRowKey] = useState4();
286
+ useEffect(() => {
287
+ if (!pinLastRow) {
288
+ return;
289
+ }
290
+ const rows = table.getRowModel().rows;
291
+ const rowToPin = rows[rows.length - 1];
292
+ const rowId = rowToPin.id;
293
+ if (rowId === pinnedRowKey) {
294
+ return;
295
+ }
296
+ table.resetRowPinning();
297
+ rowToPin.pin("bottom");
298
+ const scrollElement = getScrollElement?.();
299
+ const isFirstRender = pinnedRowKey === void 0;
300
+ if (scrollElement && !isFirstRender) {
301
+ requestAnimationFrame(() => {
302
+ scrollElement.scrollTo({
303
+ top: scrollElement.scrollHeight
304
+ });
305
+ });
306
+ }
307
+ setPinnedRowKey(rowId);
308
+ }, [
309
+ pinLastRow,
310
+ table,
311
+ getScrollElement,
312
+ pinnedRowKey,
313
+ setPinnedRowKey,
314
+ data
315
+ ]);
316
+ };
317
+
318
+ // packages/ui/react-ui-table/src/components/ColumnMenu/ColumnMenu.tsx
319
+ var ColumnMenu = ({ column, ...props }) => {
320
+ const title = column.label?.length ? column.label : column.id;
321
+ const header = props.context.header;
322
+ const { canSort, sortDirection, onSelectSort, onToggleSort, onClearSort } = useColumnSorting(header.column);
323
+ const columnSettingsAnchorRef = useRef2(null);
324
+ const [isColumnSettingsOpen, setIsColumnSettingsOpen] = useState5(false);
325
+ const shouldPreventFocusOnClose = useRef2(false);
326
+ const onOpenColumnSettings = useCallback2(() => {
327
+ shouldPreventFocusOnClose.current = true;
328
+ setIsColumnSettingsOpen(true);
329
+ }, [
330
+ shouldPreventFocusOnClose,
331
+ setIsColumnSettingsOpen
332
+ ]);
333
+ const onDropdownCloseAutoFocus = useCallback2((event) => {
334
+ if (shouldPreventFocusOnClose.current) {
335
+ event.preventDefault();
336
+ shouldPreventFocusOnClose.current = false;
337
+ }
338
+ }, [
339
+ shouldPreventFocusOnClose
340
+ ]);
341
+ return /* @__PURE__ */ React3.createElement("div", {
342
+ className: "flex items-center justify-center justify-between"
343
+ }, /* @__PURE__ */ React3.createElement("div", {
344
+ className: "flex items-center gap-1 truncate",
345
+ title
346
+ }, /* @__PURE__ */ React3.createElement(SortIndicator, {
347
+ direction: sortDirection,
348
+ onClick: onToggleSort
349
+ }), /* @__PURE__ */ React3.createElement("div", {
350
+ className: "truncate"
351
+ }, title)), /* @__PURE__ */ React3.createElement(DropdownMenu.Root, {
352
+ modal: false
353
+ }, /* @__PURE__ */ React3.createElement(Popover2.Root, {
354
+ open: isColumnSettingsOpen,
355
+ onOpenChange: setIsColumnSettingsOpen,
356
+ modal: false
357
+ }, /* @__PURE__ */ React3.createElement(DropdownMenu.Trigger, {
358
+ ref: columnSettingsAnchorRef,
359
+ asChild: true
360
+ }, /* @__PURE__ */ React3.createElement(Popover2.Anchor, {
361
+ asChild: true
362
+ }, /* @__PURE__ */ React3.createElement(Button2, {
363
+ variant: "ghost"
364
+ }, /* @__PURE__ */ React3.createElement(CaretDown2, {
365
+ className: getSize3(4)
366
+ })))), /* @__PURE__ */ React3.createElement(DropdownMenu.Content, {
367
+ onCloseAutoFocus: onDropdownCloseAutoFocus,
368
+ sideOffset: 4,
369
+ collisionPadding: 8
370
+ }, /* @__PURE__ */ React3.createElement(DropdownMenu.Viewport, null, canSort && /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement(DropdownMenu.Item, {
371
+ onClick: () => onSelectSort("asc")
372
+ }, /* @__PURE__ */ React3.createElement("span", {
373
+ className: "grow"
374
+ }, "Sort ascending"), /* @__PURE__ */ React3.createElement("span", {
375
+ className: "opacity-50"
376
+ }, /* @__PURE__ */ React3.createElement(ArrowUp, {
377
+ className: getSize3(4)
378
+ }))), /* @__PURE__ */ React3.createElement(DropdownMenu.Item, {
379
+ onClick: () => onSelectSort("desc")
380
+ }, /* @__PURE__ */ React3.createElement("span", {
381
+ className: "grow"
382
+ }, "Sort descending"), /* @__PURE__ */ React3.createElement("span", {
383
+ className: "opacity-50"
384
+ }, /* @__PURE__ */ React3.createElement(ArrowDown, {
385
+ className: getSize3(4)
386
+ }))), sortDirection !== false && /* @__PURE__ */ React3.createElement(DropdownMenu.Item, {
387
+ onClick: onClearSort
388
+ }, /* @__PURE__ */ React3.createElement("span", {
389
+ className: "grow"
390
+ }, "Clear sort"), /* @__PURE__ */ React3.createElement("span", {
391
+ className: "opacity-50"
392
+ }, /* @__PURE__ */ React3.createElement(X2, {
393
+ className: getSize3(4)
394
+ })))), /* @__PURE__ */ React3.createElement(DropdownMenu.Item, {
395
+ onClick: onOpenColumnSettings
396
+ }, /* @__PURE__ */ React3.createElement("span", {
397
+ className: "grow"
398
+ }, "Column settings"), /* @__PURE__ */ React3.createElement("span", {
399
+ className: "opacity-50"
400
+ }, /* @__PURE__ */ React3.createElement(GearSix, {
401
+ className: mx3(getSize3(4), "rotate-90")
402
+ })))), /* @__PURE__ */ React3.createElement(DropdownMenu.Arrow, null)), /* @__PURE__ */ React3.createElement(ColumnSettingsPanel, {
403
+ ...props,
404
+ column,
405
+ onClose: () => setIsColumnSettingsOpen(false)
406
+ }))));
407
+ };
408
+ 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, {
409
+ classNames: "w-60"
410
+ }, /* @__PURE__ */ React3.createElement(DensityProvider, {
411
+ density: "fine"
412
+ }, /* @__PURE__ */ React3.createElement(ColumnSettingsForm, {
413
+ column,
414
+ tableDefs,
415
+ tableDef,
416
+ onUpdate,
417
+ onDelete,
418
+ onClose
419
+ }))), /* @__PURE__ */ React3.createElement(Popover2.Arrow, null)));
420
+ var SortIndicator = ({ direction, onClick }) => {
421
+ if (direction === false) {
422
+ return null;
423
+ }
424
+ let icon;
425
+ switch (direction) {
426
+ case "asc":
427
+ icon = /* @__PURE__ */ React3.createElement(ArrowUp, {
428
+ className: getSize3(3)
429
+ });
430
+ break;
431
+ case "desc":
432
+ icon = /* @__PURE__ */ React3.createElement(ArrowDown, {
433
+ className: getSize3(3)
434
+ });
435
+ break;
436
+ }
437
+ return /* @__PURE__ */ React3.createElement("div", {
438
+ onClick,
439
+ className: "flex items-center cursor-pointer"
440
+ }, icon);
226
441
  };
227
442
 
228
443
  // packages/ui/react-ui-table/src/components/Table/Table.tsx
229
444
  import { useControllableState } from "@radix-ui/react-use-controllable-state";
230
445
  import { getCoreRowModel, getSortedRowModel, useReactTable, getGroupedRowModel, getExpandedRowModel } from "@tanstack/react-table";
231
446
  import { useVirtualizer } from "@tanstack/react-virtual";
232
- import React8, { Fragment, useCallback, useEffect, useState as useState2 } from "react";
447
+ import React9, { Fragment, useCallback as useCallback3, useEffect as useEffect2, useState as useState6 } from "react";
233
448
  import { log } from "@dxos/log";
234
449
  import { useDefaultValue, useOnTransition } from "@dxos/react-ui";
235
450
 
236
451
  // packages/ui/react-ui-table/src/components/Table/TableBody.tsx
237
452
  import { useArrowNavigationGroup } from "@fluentui/react-tabster";
238
453
  import { flexRender } from "@tanstack/react-table";
239
- import React5 from "react";
454
+ import React6 from "react";
240
455
 
241
456
  // packages/ui/react-ui-table/src/components/Table/TableContext.tsx
242
- import React3, { useContext, createContext } from "react";
457
+ import React4, { useContext, createContext } from "react";
243
458
  var TableContext = /* @__PURE__ */ createContext({});
244
459
  var TableProvider = ({ children, ...value }) => {
245
- return /* @__PURE__ */ React3.createElement(TableContext.Provider, {
460
+ return /* @__PURE__ */ React4.createElement(TableContext.Provider, {
246
461
  value
247
462
  }, children);
248
463
  };
@@ -252,32 +467,32 @@ var useTableContext = () => {
252
467
  };
253
468
 
254
469
  // packages/ui/react-ui-table/src/theme.ts
255
- import { modalSurface, focusRing, ghostSelected, ghostSelectedCurrent, groupBorder, mx as mx2 } from "@dxos/react-ui-theme";
470
+ import { modalSurface, focusRing, ghostSelected, ghostSelectedCurrent, groupBorder, mx as mx4 } from "@dxos/react-ui-theme";
256
471
  var currentRow = "!bg-neutral-75 !dark:bg-neutral-850";
257
472
  var selectedRow = "!bg-primary-100 dark:!bg-primary-700";
258
473
  var flushPadding = "pli-0 plb-0";
259
474
  var textPadding = "pli-2 plb-0.5";
260
475
  var headPadding = "pli-2 plb-1";
261
476
  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);
477
+ var tableRoot = ({ fullWidth }, ...etc) => mx4("table-fixed", fullWidth && "is-full", ...etc);
478
+ var groupTh = (_props, ...etc) => mx4("text-start font-medium", flushPadding, ...etc);
479
+ var theadRoot = ({ header, stickyHeader }, ...etc) => mx4(header ? stickyHeader && "sticky block-start-[--sticky-top] z-[1]" : "collapse", header && "base-surface", "drop-shadow-sm", ...etc);
480
+ var theadTr = (_props, ...etc) => mx4("group", ...etc);
481
+ 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
482
  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");
483
+ 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
484
  };
270
- var tbodyRoot = (_props, ...etc) => mx2(...etc);
271
- var tbodyTr = ({ canBeCurrent }, ...etc) => mx2("group", canBeCurrent ? ghostSelectedCurrent : ghostSelected, canBeCurrent && focusRing, canBeCurrent && "cursor-pointer rounded", ...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);
485
+ var tbodyRoot = (_props, ...etc) => mx4(...etc);
486
+ 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);
487
+ var tdRoot = ({ border, isGrid }, ...etc) => mx4("relative", flushPadding, border && "border", border && groupBorder, isGrid && gridCellFocusRing, ...etc);
488
+ var tdContent = (_props, ...etc) => mx4(...etc);
489
+ var tfootRoot = (_props, ...etc) => mx4("sticky block-end-0 z-10", modalSurface, ...etc);
490
+ var tfootTr = (_props, ...etc) => mx4(...etc);
491
+ var tfootTh = ({ border }, ...etc) => mx4(textPadding, modalSurface, border && groupBorder, ...etc);
277
492
 
278
493
  // packages/ui/react-ui-table/src/components/Cell/Cell.tsx
279
494
  import { useFocusableGroup } from "@fluentui/react-tabster";
280
- import React4 from "react";
495
+ import React5 from "react";
281
496
  var CELL_NAME = "Cell";
282
497
  var FocusableCell = ({ cell, children }) => {
283
498
  const tableContext = useTableContext();
@@ -285,7 +500,7 @@ var FocusableCell = ({ cell, children }) => {
285
500
  tabBehavior: "limited"
286
501
  });
287
502
  const className = tdRoot(tableContext, cell.column.columnDef.meta?.cell?.classNames);
288
- return /* @__PURE__ */ React4.createElement("td", {
503
+ return /* @__PURE__ */ React5.createElement("td", {
289
504
  tabIndex: 0,
290
505
  ...domAttributes,
291
506
  className
@@ -293,14 +508,14 @@ var FocusableCell = ({ cell, children }) => {
293
508
  };
294
509
  var StaticCell = ({ cell, children }) => {
295
510
  const tableContext = useTableContext();
296
- return /* @__PURE__ */ React4.createElement("td", {
511
+ return /* @__PURE__ */ React5.createElement("td", {
297
512
  className: tdRoot(tableContext, cell.column.columnDef.meta?.cell?.classNames)
298
513
  }, children);
299
514
  };
300
515
  var Cell = ({ cell, children }) => {
301
516
  const { isGrid } = useTableContext();
302
517
  const Root = isGrid ? FocusableCell : StaticCell;
303
- return /* @__PURE__ */ React4.createElement(Root, {
518
+ return /* @__PURE__ */ React5.createElement(Root, {
304
519
  cell
305
520
  }, children);
306
521
  };
@@ -315,14 +530,17 @@ var TableBody = ({ rows }) => {
315
530
  axis: "grid"
316
531
  });
317
532
  const canBeCurrent = !isGrid && !!onDatumClick;
318
- return /* @__PURE__ */ React5.createElement("tbody", isGrid && domAttributes, rows.map((row) => {
533
+ return /* @__PURE__ */ React6.createElement("tbody", isGrid && domAttributes, rows.map((row) => {
319
534
  const isCurrent = currentDatum === row.original;
320
535
  const isSelected = rowSelection?.[row.id];
321
- return /* @__PURE__ */ React5.createElement("tr", {
536
+ const isPinned = row.getIsPinned();
537
+ const classNames = tbodyTr({
538
+ canBeCurrent,
539
+ isPinned: isPinned !== false
540
+ });
541
+ return /* @__PURE__ */ React6.createElement("tr", {
322
542
  key: keyAccessor ? keyAccessor(row.original) : row.id,
323
- className: tbodyTr({
324
- canBeCurrent
325
- }),
543
+ className: classNames,
326
544
  ...isCurrent && {
327
545
  "aria-current": "location"
328
546
  },
@@ -340,57 +558,57 @@ var TableBody = ({ rows }) => {
340
558
  }
341
559
  }
342
560
  }
343
- }, debug && /* @__PURE__ */ React5.createElement("td", null, row.id), row.getVisibleCells().map((cell) => {
344
- return /* @__PURE__ */ React5.createElement(Cell, {
561
+ }, debug && /* @__PURE__ */ React6.createElement("td", null, row.id), row.getVisibleCells().map((cell) => {
562
+ return /* @__PURE__ */ React6.createElement(Cell, {
345
563
  key: cell.id,
346
564
  cell
347
565
  }, flexRender(cell.column.columnDef.cell, {
348
566
  className: "pli-2",
349
567
  ...cell.getContext()
350
568
  }));
351
- }), expand && /* @__PURE__ */ React5.createElement("td", null));
569
+ }), expand && /* @__PURE__ */ React6.createElement("td", null));
352
570
  }));
353
571
  };
354
572
  TableBody.displayName = TABLE_BODY_NAME;
355
573
 
356
574
  // packages/ui/react-ui-table/src/components/Table/TableFooter.tsx
357
575
  import { flexRender as flexRender2 } from "@tanstack/react-table";
358
- import React6 from "react";
576
+ import React7 from "react";
359
577
  var TABLE_FOOT_NAME = "TableFooter";
360
578
  var TableFooter = (props) => {
361
579
  const tableContext = useTableContext();
362
580
  const { table, expand, debug } = tableContext;
363
581
  const footers = table.getFooterGroups();
364
- return /* @__PURE__ */ React6.createElement("tfoot", {
582
+ return /* @__PURE__ */ React7.createElement("tfoot", {
365
583
  className: tfootRoot(props)
366
- }, footers.map((footerGroup) => /* @__PURE__ */ React6.createElement("tr", {
584
+ }, footers.map((footerGroup) => /* @__PURE__ */ React7.createElement("tr", {
367
585
  key: footerGroup.id,
368
586
  className: tfootTr(tableContext)
369
- }, debug && /* @__PURE__ */ React6.createElement("th", null), footerGroup.headers.map((footer) => {
370
- return /* @__PURE__ */ React6.createElement("th", {
587
+ }, debug && /* @__PURE__ */ React7.createElement("th", null), footerGroup.headers.map((footer) => {
588
+ return /* @__PURE__ */ React7.createElement("th", {
371
589
  key: footer.id,
372
590
  className: tfootTh(tableContext, footer.column.columnDef.meta?.footer?.classNames)
373
591
  }, footer.isPlaceholder ? null : flexRender2(footer.column.columnDef.footer, footer.getContext()));
374
- }), expand && /* @__PURE__ */ React6.createElement("th", null))));
592
+ }), expand && /* @__PURE__ */ React7.createElement("th", null))));
375
593
  };
376
594
  TableFooter.displayName = TABLE_FOOT_NAME;
377
595
 
378
596
  // packages/ui/react-ui-table/src/components/Table/TableHead.tsx
379
597
  import { flexRender as flexRender3 } from "@tanstack/react-table";
380
- import React7 from "react";
598
+ import React8 from "react";
381
599
  var TABLE_HEAD_NAME = "TableHeader";
382
600
  var TableHead = (_props) => {
383
601
  const tableContext = useTableContext();
384
602
  const headerGroups = tableContext.table.getHeaderGroups();
385
- return /* @__PURE__ */ React7.createElement("thead", {
603
+ return /* @__PURE__ */ React8.createElement("thead", {
386
604
  className: theadRoot(tableContext)
387
605
  }, headerGroups.map((headerGroup) => {
388
606
  return (
389
607
  // Group element to hover resize handles.
390
- /* @__PURE__ */ React7.createElement("tr", {
608
+ /* @__PURE__ */ React8.createElement("tr", {
391
609
  key: headerGroup.id,
392
610
  className: theadTr(tableContext)
393
- }, tableContext.debug && /* @__PURE__ */ React7.createElement("th", {
611
+ }, tableContext.debug && /* @__PURE__ */ React8.createElement("th", {
394
612
  className: "font-mono",
395
613
  style: {
396
614
  width: 32
@@ -399,13 +617,13 @@ var TableHead = (_props) => {
399
617
  const isResizing = header.column.getIsResizing();
400
618
  const onResize = header.getResizeHandler();
401
619
  const resizable = header.column.columnDef.meta?.resizable;
402
- return /* @__PURE__ */ React7.createElement("th", {
620
+ return /* @__PURE__ */ React8.createElement("th", {
403
621
  key: header.id,
404
622
  style: {
405
623
  width: header.getSize()
406
624
  },
407
625
  className: theadTh(tableContext, header.column.columnDef.meta?.header?.classNames)
408
- }, !header || header.isPlaceholder ? null : flexRender3(header.column.columnDef.header, header.getContext()), resizable && /* @__PURE__ */ React7.createElement("div", {
626
+ }, !header || header.isPlaceholder ? null : flexRender3(header.column.columnDef.header, header.getContext()), resizable && /* @__PURE__ */ React8.createElement("div", {
409
627
  className: theadResizeRoot({
410
628
  isResizing
411
629
  }),
@@ -413,7 +631,7 @@ var TableHead = (_props) => {
413
631
  onMouseDown: onResize,
414
632
  onTouchStart: onResize
415
633
  }));
416
- }), tableContext.expand && /* @__PURE__ */ React7.createElement("th", null))
634
+ }), tableContext.expand && /* @__PURE__ */ React8.createElement("th", null))
417
635
  );
418
636
  }));
419
637
  };
@@ -426,9 +644,9 @@ var Table = (props) => {
426
644
  const columns = useDefaultValue(props.columns, []);
427
645
  const data = useDefaultValue(props.data, []);
428
646
  const TableProvider2 = TableProvider;
429
- const [columnsInitialised, setColumnsInitialised] = useState2(false);
430
- const [columnSizing, setColumnSizing] = useState2({});
431
- useEffect(() => {
647
+ const [columnsInitialised, setColumnsInitialised] = useState6(false);
648
+ const [columnSizing, setColumnSizing] = useState6({});
649
+ useEffect2(() => {
432
650
  if (columnsInitialised) {
433
651
  return;
434
652
  }
@@ -441,8 +659,8 @@ var Table = (props) => {
441
659
  columns,
442
660
  setColumnSizing
443
661
  ]);
444
- const [columnSizingInfo, setColumnSizingInfo] = useState2({});
445
- const notifyColumnResize = useCallback(() => onColumnResize?.(columnSizing), [
662
+ const [columnSizingInfo, setColumnSizingInfo] = useState6({});
663
+ const notifyColumnResize = useCallback3(() => onColumnResize?.(columnSizing), [
446
664
  onColumnResize,
447
665
  columnSizing
448
666
  ]);
@@ -452,7 +670,7 @@ var Table = (props) => {
452
670
  onChange: props.onRowSelectionChange,
453
671
  defaultProp: props.defaultRowSelection
454
672
  });
455
- const handleRowSelectionChange = useCallback((updaterOrValue) => {
673
+ const handleRowSelectionChange = useCallback3((updaterOrValue) => {
456
674
  const nextRowSelection = typeof updaterOrValue === "function" ? updaterOrValue(rowSelection) : updaterOrValue;
457
675
  if (rowsSelectable === "multi") {
458
676
  setRowSelection(nextRowSelection);
@@ -469,8 +687,8 @@ var Table = (props) => {
469
687
  setRowSelection,
470
688
  rowSelection
471
689
  ]);
472
- const [grouping, handleGroupingChange] = useState2(props.grouping ?? []);
473
- useEffect(() => handleGroupingChange(props.grouping ?? []), [
690
+ const [grouping, handleGroupingChange] = useState6(props.grouping ?? []);
691
+ useEffect2(() => handleGroupingChange(props.grouping ?? []), [
474
692
  handleGroupingChange,
475
693
  props.grouping
476
694
  ]);
@@ -512,41 +730,30 @@ var Table = (props) => {
512
730
  // Debug
513
731
  debugTable: debug
514
732
  });
515
- useEffect(() => {
733
+ useEffect2(() => {
516
734
  onDataSelectionChange?.(Object.keys(rowSelection).map((id) => table.getRowModel().rowsById[id].original));
517
735
  }, [
518
736
  onDataSelectionChange,
519
737
  rowSelection,
520
738
  table
521
739
  ]);
522
- useEffect(() => {
523
- if (!pinLastRow) {
524
- return;
525
- }
526
- table.resetRowPinning();
527
- const rows = table.getRowModel().rows;
528
- rows[rows.length - 1].pin("bottom");
529
- }, [
530
- pinLastRow,
531
- table,
532
- data
533
- ]);
740
+ usePinLastRow(pinLastRow, table, data, getScrollElement);
534
741
  const expand = false;
535
742
  if (!getScrollElement) {
536
743
  log.warn("Table: getScrollElement is not set. This is required for virtualized tables.", void 0, {
537
744
  F: __dxlog_file,
538
- L: 168,
745
+ L: 159,
539
746
  S: void 0,
540
747
  C: (f, a) => f(...a)
541
748
  });
542
749
  }
543
- return /* @__PURE__ */ React8.createElement(TableProvider2, {
750
+ return /* @__PURE__ */ React9.createElement(TableProvider2, {
544
751
  ...props,
545
752
  table,
546
753
  header,
547
754
  expand,
548
755
  isGrid: role === "grid" || role === "treegrid"
549
- }, /* @__PURE__ */ React8.createElement(TableImpl, {
756
+ }, /* @__PURE__ */ React9.createElement(TableImpl, {
550
757
  debug: false,
551
758
  getScrollElement,
552
759
  ...props
@@ -562,13 +769,13 @@ var TableImpl = (props) => {
562
769
  ...bottomRows
563
770
  ];
564
771
  if (debug) {
565
- return /* @__PURE__ */ React8.createElement("pre", {
772
+ return /* @__PURE__ */ React9.createElement("pre", {
566
773
  className: "font-mono text-xs text-neutral-500 m-1 p-2 ring"
567
- }, /* @__PURE__ */ React8.createElement("code", null, JSON.stringify(table.getState(), void 0, 2)));
774
+ }, /* @__PURE__ */ React9.createElement("code", null, JSON.stringify(table.getState(), void 0, 2)));
568
775
  }
569
776
  const virtualisable = getScrollElement !== void 0;
570
777
  const isResizingColumn = table.getState().columnSizingInfo.isResizingColumn;
571
- return /* @__PURE__ */ React8.createElement("table", {
778
+ return /* @__PURE__ */ React9.createElement("table", {
572
779
  role,
573
780
  className: tableRoot(props, classNames),
574
781
  ...!fullWidth && {
@@ -576,25 +783,21 @@ var TableImpl = (props) => {
576
783
  width: table.getTotalSize()
577
784
  }
578
785
  }
579
- }, /* @__PURE__ */ React8.createElement(TableHead, null), grouping?.length !== 0 ? virtualisable ? isResizingColumn ? /* @__PURE__ */ React8.createElement(MemoizedVirtualisedTableContent, {
786
+ }, /* @__PURE__ */ React9.createElement(TableHead, null), grouping?.length !== 0 ? virtualisable ? isResizingColumn ? /* @__PURE__ */ React9.createElement(MemoizedVirtualisedTableContent, {
580
787
  getScrollElement
581
- }) : /* @__PURE__ */ React8.createElement(VirtualizedTableContent, {
788
+ }) : /* @__PURE__ */ React9.createElement(VirtualizedTableContent, {
582
789
  getScrollElement
583
- }) : /* @__PURE__ */ React8.createElement(TableBody, {
790
+ }) : /* @__PURE__ */ React9.createElement(TableBody, {
584
791
  rows
585
- }) : /* @__PURE__ */ React8.createElement(GroupedTableContent, null), footer && /* @__PURE__ */ React8.createElement(TableFooter, null));
792
+ }) : /* @__PURE__ */ React9.createElement(GroupedTableContent, null), footer && /* @__PURE__ */ React9.createElement(TableFooter, null));
586
793
  };
587
794
  var VirtualizedTableContent = ({ getScrollElement }) => {
588
795
  const { table } = useTableContext();
589
796
  const centerRows = table.getCenterRows();
590
797
  const pinnedRows = table.getBottomRows();
591
- const rows = [
592
- ...centerRows,
593
- ...pinnedRows
594
- ];
595
798
  const { getTotalSize, getVirtualItems } = useVirtualizer({
596
799
  getScrollElement,
597
- count: rows.length,
800
+ count: centerRows.length,
598
801
  overscan: 8,
599
802
  estimateSize: () => 40
600
803
  });
@@ -602,123 +805,58 @@ var VirtualizedTableContent = ({ getScrollElement }) => {
602
805
  const totalSize = getTotalSize();
603
806
  const paddingTop = virtualRows.length > 0 ? virtualRows?.[0]?.start || 0 : 0;
604
807
  const paddingBottom = virtualRows.length > 0 ? totalSize - (virtualRows?.[virtualRows.length - 1]?.end || 0) : 0;
605
- return /* @__PURE__ */ React8.createElement(React8.Fragment, null, paddingTop > 0 && /* @__PURE__ */ React8.createElement("tbody", {
808
+ const rowsToRender = [
809
+ ...virtualRows.map((virtualRow) => centerRows[virtualRow.index]),
810
+ ...pinnedRows
811
+ ];
812
+ return /* @__PURE__ */ React9.createElement(React9.Fragment, null, paddingTop > 0 && /* @__PURE__ */ React9.createElement("tbody", {
606
813
  role: "none"
607
- }, /* @__PURE__ */ React8.createElement("tr", {
814
+ }, /* @__PURE__ */ React9.createElement("tr", {
608
815
  role: "none"
609
- }, /* @__PURE__ */ React8.createElement("td", {
816
+ }, /* @__PURE__ */ React9.createElement("td", {
610
817
  style: {
611
818
  height: `${paddingTop}px`
612
819
  },
613
820
  role: "none"
614
- }))), /* @__PURE__ */ React8.createElement(TableBody, {
615
- rows: virtualRows.map((virtualRow) => rows[virtualRow.index])
616
- }), paddingBottom > 0 && /* @__PURE__ */ React8.createElement("tbody", {
821
+ }))), /* @__PURE__ */ React9.createElement(TableBody, {
822
+ rows: rowsToRender
823
+ }), paddingBottom > 0 && /* @__PURE__ */ React9.createElement("tbody", {
617
824
  role: "none"
618
- }, /* @__PURE__ */ React8.createElement("tr", {
825
+ }, /* @__PURE__ */ React9.createElement("tr", {
619
826
  role: "none"
620
- }, /* @__PURE__ */ React8.createElement("td", {
827
+ }, /* @__PURE__ */ React9.createElement("td", {
621
828
  style: {
622
829
  height: `${paddingBottom}px`
623
830
  },
624
831
  role: "none"
625
832
  }))));
626
833
  };
627
- var MemoizedVirtualisedTableContent = /* @__PURE__ */ React8.memo(VirtualizedTableContent);
834
+ var MemoizedVirtualisedTableContent = /* @__PURE__ */ React9.memo(VirtualizedTableContent);
628
835
  var GroupedTableContent = () => {
629
836
  const { table: { getGroupedRowModel: getGroupedRowModel2, getHeaderGroups, getState }, debug } = useTableContext();
630
- return /* @__PURE__ */ React8.createElement(React8.Fragment, null, getGroupedRowModel2().rows.map((row, i) => {
631
- return /* @__PURE__ */ React8.createElement(Fragment, {
837
+ return /* @__PURE__ */ React9.createElement(React9.Fragment, null, getGroupedRowModel2().rows.map((row, i) => {
838
+ return /* @__PURE__ */ React9.createElement(Fragment, {
632
839
  key: i
633
- }, /* @__PURE__ */ React8.createElement("thead", null, /* @__PURE__ */ React8.createElement("tr", null, debug && /* @__PURE__ */ React8.createElement("th", null), /* @__PURE__ */ React8.createElement("th", {
840
+ }, /* @__PURE__ */ React9.createElement("thead", null, /* @__PURE__ */ React9.createElement("tr", null, debug && /* @__PURE__ */ React9.createElement("th", null), /* @__PURE__ */ React9.createElement("th", {
634
841
  // TODO(burdon): Calculate row span.
635
842
  colSpan: getHeaderGroups()[0].headers.length,
636
843
  className: groupTh({})
637
- }, getState().grouping[0], "[", String(row.getGroupingValue(getState().grouping[0])), "]"))), /* @__PURE__ */ React8.createElement(TableBody, {
844
+ }, getState().grouping[0], "[", String(row.getGroupingValue(getState().grouping[0])), "]"))), /* @__PURE__ */ React9.createElement(TableBody, {
638
845
  rows: row.subRows
639
846
  }));
640
847
  }));
641
848
  };
642
849
 
643
- // packages/ui/react-ui-table/src/components/Cell/CellCombobox.tsx
644
- import { CaretDown } from "@phosphor-icons/react";
645
- import React9, { useCallback as useCallback2, useState as useState3 } from "react";
646
- import { Popover as Popover2, useTranslation as useTranslation2 } from "@dxos/react-ui";
647
- import { PopoverCombobox } from "@dxos/react-ui-searchlist";
648
- import { descriptionText, getSize as getSize3, mx as mx3, staticPlaceholderText } from "@dxos/react-ui-theme";
649
- var initialItems = (model, value) => value ? {
650
- [model.getId(value)]: {
651
- label: model.getText(value),
652
- data: value
653
- }
654
- } : {};
655
- var CellCombobox = ({ model, value, onValueChange }) => {
656
- const { t } = useTranslation2(translationKey);
657
- const [items, setItems] = useState3(initialItems(model, value));
658
- const [loading, setLoading] = useState3(false);
659
- const [searchInputValue, setSearchInputValue] = useState3("");
660
- const handleSearchInputValueChange = async (text) => {
661
- setSearchInputValue(text ?? "");
662
- setLoading(true);
663
- const items2 = await model.query(text);
664
- setItems(items2.reduce((acc, item) => {
665
- acc[model.getId(item)] = {
666
- label: model.getText(item),
667
- data: item
668
- };
669
- return acc;
670
- }, initialItems(model, value)));
671
- setLoading(false);
672
- };
673
- const handleValueChange = useCallback2((id) => {
674
- onValueChange(items[id]?.data);
675
- }, [
676
- items,
677
- onValueChange
678
- ]);
679
- return /* @__PURE__ */ React9.createElement(PopoverCombobox.Root, {
680
- placeholder: t("no selection label")
681
- }, /* @__PURE__ */ React9.createElement(PopoverCombobox.Trigger, {
682
- variant: "ghost",
683
- classNames: "is-full rounded-none ring-inset"
684
- }, value ? /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement("span", {
685
- className: mx3("font-normal text-start text-base flex-1 min-is-0 truncate mie-2", !value && staticPlaceholderText)
686
- }, model.getText(value)), /* @__PURE__ */ React9.createElement(CaretDown, {
687
- weight: "bold",
688
- className: getSize3(3)
689
- })) : null), /* @__PURE__ */ React9.createElement(PopoverCombobox.Content, {
690
- shouldFilter: false,
691
- classNames: "is-[--radix-popover-trigger-width]"
692
- }, /* @__PURE__ */ React9.createElement(PopoverCombobox.Input, {
693
- value: searchInputValue,
694
- onValueChange: handleSearchInputValueChange
695
- }), /* @__PURE__ */ React9.createElement(PopoverCombobox.List, null, searchInputValue.length < 1 ? /* @__PURE__ */ React9.createElement("p", {
696
- className: mx3("text-center", descriptionText)
697
- }, "Start typing to search") : loading ? /* @__PURE__ */ React9.createElement("p", {
698
- className: mx3("text-center", descriptionText)
699
- }, "Searching\u2026") : /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(PopoverCombobox.Empty, {
700
- classNames: [
701
- "text-center",
702
- descriptionText
703
- ]
704
- }, "No results"), Object.entries(items).map(([id, { label }]) => /* @__PURE__ */ React9.createElement(PopoverCombobox.Item, {
705
- key: id,
706
- value: id,
707
- onSelect: handleValueChange,
708
- classNames: "truncate"
709
- }, label)))), /* @__PURE__ */ React9.createElement(Popover2.Arrow, null)));
710
- };
711
-
712
850
  // packages/ui/react-ui-table/src/helpers.tsx
713
851
  import { useFocusFinders } from "@fluentui/react-tabster";
714
- import { Check as Check2, ClipboardText, X as X2 } from "@phosphor-icons/react";
852
+ import { Check as Check2, ClipboardText, X as X3 } from "@phosphor-icons/react";
715
853
  import { createColumnHelper } from "@tanstack/react-table";
716
854
  import { format } from "date-fns/format";
717
855
  import { formatDistanceToNow } from "date-fns/formatDistanceToNow";
718
- import React10, { useEffect as useEffect2, useRef as useRef2, useState as useState4 } from "react";
856
+ import React10, { useEffect as useEffect3, useRef as useRef3, useState as useState7 } from "react";
719
857
  import { log as log2 } from "@dxos/log";
720
858
  import { Input as Input2, Tooltip } from "@dxos/react-ui";
721
- import { getSize as getSize4, mx as mx4 } from "@dxos/react-ui-theme";
859
+ import { getSize as getSize4, mx as mx5 } from "@dxos/react-ui-theme";
722
860
  var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/helpers.tsx";
723
861
  var createColumnBuilder = () => ({
724
862
  helper: createColumnHelper(),
@@ -733,11 +871,11 @@ var ComboboxBuilderCell = (cellContext) => {
733
871
  }) : null;
734
872
  };
735
873
  var StringBuilderCell = (cellContext) => {
736
- const ref = useRef2(null);
874
+ const ref = useRef3(null);
737
875
  const { findPrevFocusable } = useFocusFinders();
738
876
  const { onUpdate, cell } = cellContext.column.columnDef.meta;
739
877
  const initialValue = cellContext.getValue();
740
- const [value, setValue] = useState4(() => {
878
+ const [value, setValue] = useState7(() => {
741
879
  if (initialValue?.content) {
742
880
  return initialValue?.content;
743
881
  }
@@ -779,10 +917,10 @@ var displayNumber = (value, digits) => value || value === 0 ? value.toLocaleStri
779
917
  var NumberBuilderCell = (cellContext) => {
780
918
  const { onUpdate, digits } = cellContext.column.columnDef.meta;
781
919
  const value = cellContext.getValue();
782
- const [text, setText] = useState4(displayNumber(value, digits));
920
+ const [text, setText] = useState7(displayNumber(value, digits));
783
921
  const { findPrevFocusable } = useFocusFinders();
784
- const ref = useRef2(null);
785
- useEffect2(() => {
922
+ const ref = useRef3(null);
923
+ useEffect3(() => {
786
924
  (!document.activeElement || document.activeElement !== ref.current) && setText(displayNumber(value, digits));
787
925
  }, [
788
926
  digits,
@@ -869,7 +1007,7 @@ var ColumnBuilder = class {
869
1007
  cell: model && onUpdate ? ComboboxBuilderCell : (cellContext) => {
870
1008
  const cellValue = cellContext.getValue();
871
1009
  return /* @__PURE__ */ React10.createElement("div", {
872
- className: mx4("truncate", classNames)
1010
+ className: mx5("truncate", classNames)
873
1011
  }, cellValue ? model?.getText(cellValue) : "");
874
1012
  },
875
1013
  ...props,
@@ -900,7 +1038,7 @@ var ColumnBuilder = class {
900
1038
  value = value.content;
901
1039
  }
902
1040
  return /* @__PURE__ */ React10.createElement("div", {
903
- className: mx4("truncate", textPadding, classNames)
1041
+ className: mx5("truncate", textPadding, classNames)
904
1042
  }, value);
905
1043
  },
906
1044
  ...props,
@@ -929,7 +1067,6 @@ var ColumnBuilder = class {
929
1067
  onUpdate,
930
1068
  digits,
931
1069
  header: {
932
- classNames: "text-end",
933
1070
  ...props.meta?.header
934
1071
  },
935
1072
  cell: {
@@ -955,7 +1092,7 @@ var ColumnBuilder = class {
955
1092
  addSuffix: true
956
1093
  }) : value.toISOString() : void 0;
957
1094
  return /* @__PURE__ */ React10.createElement("div", {
958
- className: mx4(textPadding, classNames)
1095
+ className: mx5(textPadding, classNames)
959
1096
  }, str);
960
1097
  } catch (err) {
961
1098
  log2.info("formatError", {
@@ -1082,7 +1219,7 @@ var ColumnBuilder = class {
1082
1219
  */
1083
1220
  icon({ label, size, on, off, ...props } = {}) {
1084
1221
  const IconOn = on?.Icon ?? Check2;
1085
- const IconOff = off?.Icon ?? X2;
1222
+ const IconOff = off?.Icon ?? X3;
1086
1223
  return {
1087
1224
  size: size ?? 32,
1088
1225
  // TODO(Zan): Implement sort algorithm
@@ -1094,11 +1231,11 @@ var ColumnBuilder = class {
1094
1231
  const value = cell.getValue();
1095
1232
  if (value) {
1096
1233
  return /* @__PURE__ */ React10.createElement(IconOn, {
1097
- className: mx4("block mli-auto", getSize4(6), on?.className ?? "text-green-700")
1234
+ className: mx5("block mli-auto", getSize4(6), on?.className ?? "text-green-700")
1098
1235
  });
1099
1236
  } else if (value === false) {
1100
1237
  return /* @__PURE__ */ React10.createElement(IconOff, {
1101
- className: mx4("block mli-auto", getSize4(6), off?.className ?? "text-red-700")
1238
+ className: mx5("block mli-auto", getSize4(6), off?.className ?? "text-red-700")
1102
1239
  });
1103
1240
  } else {
1104
1241
  return null;
@@ -1107,13 +1244,165 @@ var ColumnBuilder = class {
1107
1244
  ...props
1108
1245
  };
1109
1246
  }
1247
+ /**
1248
+ * This display column type is for nested/unhandled data to appear as JSON.
1249
+ */
1250
+ display({ label, classNames, ...props } = {}) {
1251
+ return {
1252
+ size: 120,
1253
+ minSize: 120,
1254
+ header: (cell) => label ?? cell.header.id,
1255
+ cell: (cell) => {
1256
+ const value = cell.getValue();
1257
+ return /* @__PURE__ */ React10.createElement("div", {
1258
+ className: "overflow-scroll"
1259
+ }, /* @__PURE__ */ React10.createElement("code", {
1260
+ className: "text-xs"
1261
+ }, JSON.stringify(value)));
1262
+ },
1263
+ ...props,
1264
+ meta: {
1265
+ ...props.meta,
1266
+ cell: {
1267
+ ...props.meta?.cell,
1268
+ classNames: [
1269
+ "font-mono",
1270
+ textPadding,
1271
+ classNames
1272
+ ]
1273
+ }
1274
+ }
1275
+ };
1276
+ }
1277
+ };
1278
+
1279
+ // packages/ui/react-ui-table/src/schema/schema.ts
1280
+ import { AST } from "@effect/schema";
1281
+ var isOptionalUnion = (prop) => AST.isUnion(prop.type) && prop.isOptional;
1282
+ var unwrapOptionProperty = (prop) => {
1283
+ if (!isOptionalUnion(prop)) {
1284
+ throw new Error("Not an optional property");
1285
+ }
1286
+ if (!AST.isUnion(prop.type)) {
1287
+ throw new Error("Not a union type");
1288
+ }
1289
+ const [type, _undefinedCase] = prop.type.types;
1290
+ return type;
1291
+ };
1292
+ var typeToColumn = (type) => {
1293
+ if (AST.isStringKeyword(type)) {
1294
+ return "string";
1295
+ } else if (AST.isNumberKeyword(type)) {
1296
+ return "number";
1297
+ } else if (AST.isBooleanKeyword(type)) {
1298
+ return "boolean";
1299
+ }
1300
+ if (AST.isRefinement(type)) {
1301
+ return typeToColumn(type.from);
1302
+ }
1303
+ if (AST.isTransform(type)) {
1304
+ const identifier = AST.getIdentifierAnnotation(type);
1305
+ if (identifier._tag === "Some") {
1306
+ if (identifier.value === "DateFromString") {
1307
+ return "date";
1308
+ }
1309
+ }
1310
+ }
1311
+ return "display";
1312
+ };
1313
+ var propertyToColumn = (property) => {
1314
+ let type = property.type;
1315
+ if (property.isOptional) {
1316
+ type = unwrapOptionProperty(property);
1317
+ }
1318
+ return typeToColumn(type);
1319
+ };
1320
+ var isStruct = (node) => AST.isTypeLiteral(node);
1321
+ var classifySchemaProperties = (schema) => {
1322
+ const recurse = (node, path, acc) => {
1323
+ const properties = AST.getPropertySignatures(node);
1324
+ properties.forEach((prop) => {
1325
+ const propName = prop.name.toString();
1326
+ if (prop.isOptional) {
1327
+ const unwrappedAst = unwrapOptionProperty(prop);
1328
+ if (isStruct(unwrappedAst)) {
1329
+ return recurse(unwrappedAst, [
1330
+ ...path,
1331
+ propName
1332
+ ], acc);
1333
+ }
1334
+ }
1335
+ if (isStruct(prop.type)) {
1336
+ recurse(prop.type, [
1337
+ ...path,
1338
+ propName
1339
+ ], acc);
1340
+ } else {
1341
+ acc.push([
1342
+ path.concat(propName).join("."),
1343
+ propertyToColumn(prop)
1344
+ ]);
1345
+ }
1346
+ });
1347
+ return acc;
1348
+ };
1349
+ return recurse(schema.ast, [], []);
1350
+ };
1351
+
1352
+ // packages/ui/react-ui-table/src/schema/schemaToColumns.ts
1353
+ var schemaToColumnDefs = (schema) => {
1354
+ const { helper, builder } = createColumnBuilder();
1355
+ const classified = classifySchemaProperties(schema);
1356
+ return classified.map(([name, type]) => {
1357
+ const propertyKey = name.toString();
1358
+ let column;
1359
+ switch (type) {
1360
+ case "string": {
1361
+ column = builder.string({
1362
+ label: propertyKey
1363
+ });
1364
+ break;
1365
+ }
1366
+ case "number": {
1367
+ column = builder.number({
1368
+ label: propertyKey
1369
+ });
1370
+ break;
1371
+ }
1372
+ case "boolean": {
1373
+ column = builder.switch({
1374
+ label: propertyKey
1375
+ });
1376
+ break;
1377
+ }
1378
+ case "date": {
1379
+ column = builder.date({
1380
+ label: propertyKey
1381
+ });
1382
+ break;
1383
+ }
1384
+ case "display": {
1385
+ column = builder.display({
1386
+ label: propertyKey,
1387
+ id: propertyKey
1388
+ });
1389
+ break;
1390
+ }
1391
+ default: {
1392
+ throw new Error(`Unhandled column type: ${type}`);
1393
+ }
1394
+ }
1395
+ return helper.accessor(propertyKey, column);
1396
+ });
1110
1397
  };
1111
1398
  export {
1112
1399
  CellCombobox,
1113
1400
  ColumnBuilder,
1114
1401
  ColumnMenu,
1115
- ColumnPanel,
1402
+ ColumnSettingsForm,
1403
+ ColumnSettingsPanel,
1116
1404
  MemoizedVirtualisedTableContent,
1405
+ SortIndicator,
1117
1406
  Table,
1118
1407
  createColumnBuilder,
1119
1408
  currentRow,
@@ -1121,6 +1410,7 @@ export {
1121
1410
  gridCellFocusRing,
1122
1411
  groupTh,
1123
1412
  headPadding,
1413
+ schemaToColumnDefs,
1124
1414
  selectedRow,
1125
1415
  tableRoot,
1126
1416
  tbodyRoot,
@@ -1135,6 +1425,8 @@ export {
1135
1425
  theadRoot,
1136
1426
  theadTh,
1137
1427
  theadTr,
1138
- translations_default as translations
1428
+ translations_default as translations,
1429
+ useColumnSorting,
1430
+ usePinLastRow
1139
1431
  };
1140
1432
  //# sourceMappingURL=index.mjs.map