@dxos/react-ui-table 0.3.8-next.f4e0086 → 0.3.9-main.03b62b6

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 (65) hide show
  1. package/dist/lib/browser/index.mjs +605 -532
  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/Cell/Cell.d.ts +12 -0
  5. package/dist/types/src/components/Cell/Cell.d.ts.map +1 -0
  6. package/dist/types/src/components/{ComboboxCell.d.ts → Cell/CellCombobox.d.ts} +4 -4
  7. package/dist/types/src/components/Cell/CellCombobox.d.ts.map +1 -0
  8. package/dist/types/src/components/Cell/index.d.ts +2 -0
  9. package/dist/types/src/components/Cell/index.d.ts.map +1 -0
  10. package/dist/types/src/components/ColumnMenu.d.ts.map +1 -1
  11. package/dist/types/src/components/Table/Table.d.ts +3 -0
  12. package/dist/types/src/components/Table/Table.d.ts.map +1 -0
  13. package/dist/types/src/components/{Table.stories.d.ts → Table/Table.stories.d.ts} +4 -7
  14. package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -0
  15. package/dist/types/src/components/Table/TableBody.d.ts +11 -0
  16. package/dist/types/src/components/Table/TableBody.d.ts.map +1 -0
  17. package/dist/types/src/components/Table/TableContext.d.ts +14 -0
  18. package/dist/types/src/components/Table/TableContext.d.ts.map +1 -0
  19. package/dist/types/src/components/Table/TableFooter.d.ts +7 -0
  20. package/dist/types/src/components/Table/TableFooter.d.ts.map +1 -0
  21. package/dist/types/src/components/Table/TableHead.d.ts +8 -0
  22. package/dist/types/src/components/Table/TableHead.d.ts.map +1 -0
  23. package/dist/types/src/components/Table/index.d.ts +3 -0
  24. package/dist/types/src/components/Table/index.d.ts.map +1 -0
  25. package/dist/types/src/components/Table/props.d.ts +40 -0
  26. package/dist/types/src/components/Table/props.d.ts.map +1 -0
  27. package/dist/types/src/components/index.d.ts +1 -1
  28. package/dist/types/src/components/index.d.ts.map +1 -1
  29. package/dist/types/src/helpers.d.ts +21 -15
  30. package/dist/types/src/helpers.d.ts.map +1 -1
  31. package/dist/types/src/index.d.ts +1 -0
  32. package/dist/types/src/index.d.ts.map +1 -1
  33. package/dist/types/src/schema.d.ts +0 -1
  34. package/dist/types/src/schema.d.ts.map +1 -1
  35. package/dist/types/src/theme.d.ts +30 -33
  36. package/dist/types/src/theme.d.ts.map +1 -1
  37. package/dist/types/src/translations.d.ts +11 -0
  38. package/dist/types/src/translations.d.ts.map +1 -1
  39. package/dist/types/src/types.d.ts +13 -10
  40. package/dist/types/src/types.d.ts.map +1 -1
  41. package/package.json +12 -10
  42. package/src/components/Cell/Cell.tsx +43 -0
  43. package/src/components/{ComboboxCell.tsx → Cell/CellCombobox.tsx} +6 -6
  44. package/src/components/Cell/index.ts +5 -0
  45. package/src/components/ColumnMenu.tsx +16 -16
  46. package/src/components/{Table.stories.tsx → Table/Table.stories.tsx} +51 -51
  47. package/src/components/Table/Table.tsx +184 -0
  48. package/src/components/Table/TableBody.tsx +70 -0
  49. package/src/components/Table/TableContext.tsx +28 -0
  50. package/src/components/Table/TableFooter.tsx +42 -0
  51. package/src/components/Table/TableHead.tsx +79 -0
  52. package/src/components/Table/index.ts +6 -0
  53. package/src/components/Table/props.ts +58 -0
  54. package/src/components/index.ts +1 -1
  55. package/src/helpers.tsx +289 -219
  56. package/src/index.ts +1 -0
  57. package/src/schema.ts +0 -8
  58. package/src/theme.ts +102 -63
  59. package/src/translations.ts +11 -0
  60. package/src/types.ts +15 -10
  61. package/dist/types/src/components/ComboboxCell.d.ts.map +0 -1
  62. package/dist/types/src/components/Table.d.ts +0 -29
  63. package/dist/types/src/components/Table.d.ts.map +0 -1
  64. package/dist/types/src/components/Table.stories.d.ts.map +0 -1
  65. package/src/components/Table.tsx +0 -551
@@ -1,45 +1,42 @@
1
1
  // packages/ui/react-ui-table/src/components/ColumnMenu.tsx
2
2
  import { Check, CaretDown, Trash, X } from "@phosphor-icons/react";
3
3
  import React, { useRef, useState } from "react";
4
- import { Button, DensityProvider, Input, Popover, Select, Separator } from "@dxos/react-ui";
4
+ import { Button, DensityProvider, Input, Popover, Select, Separator, useTranslation } from "@dxos/react-ui";
5
5
  import { getSize, mx } from "@dxos/react-ui-theme";
6
6
  import { safeParseInt } from "@dxos/util";
7
7
 
8
- // packages/ui/react-ui-table/src/schema.ts
9
- var columnTypes = /* @__PURE__ */ new Map([
10
- [
11
- "string",
12
- "Text"
13
- ],
14
- [
15
- "boolean",
16
- "Checkbox"
17
- ],
18
- [
19
- "number",
20
- "Number"
21
- ],
22
- [
23
- "date",
24
- "Date"
25
- ],
26
- [
27
- "ref",
28
- "Reference"
29
- ]
30
- ]);
8
+ // packages/ui/react-ui-table/src/translations.ts
9
+ var translationKey = "table";
10
+ var translations_default = [
11
+ {
12
+ "en-US": {
13
+ [translationKey]: {
14
+ "no selection label": "\xA0",
15
+ "number column type label": "Number",
16
+ "boolean column type label": "Toggle",
17
+ "date column type label": "Date",
18
+ "string column type label": "Text",
19
+ "ref column type label": "Reference",
20
+ "digits label": "Decimal places",
21
+ "save label": "Save",
22
+ "delete label": "Delete",
23
+ "column label label": "Label",
24
+ "property key label": "Property",
25
+ "column type label": "Type"
26
+ }
27
+ }
28
+ }
29
+ ];
31
30
 
32
31
  // packages/ui/react-ui-table/src/components/ColumnMenu.tsx
33
32
  var ColumnMenu = ({ column, ...props }) => {
34
33
  const title = column.label?.length ? column.label : column.id;
35
34
  return /* @__PURE__ */ React.createElement("div", {
36
- className: "flex grow items-center overflow-hidden"
35
+ className: "flex items-center gap-2"
37
36
  }, /* @__PURE__ */ React.createElement("div", {
38
- className: "truncate",
37
+ className: "flex-1 min-is-0 truncate",
39
38
  title
40
39
  }, title), /* @__PURE__ */ React.createElement("div", {
41
- className: "grow"
42
- }), /* @__PURE__ */ React.createElement("div", {
43
40
  className: "flex shrink-0"
44
41
  }, /* @__PURE__ */ React.createElement(ColumnPanel, {
45
42
  ...props,
@@ -59,6 +56,7 @@ var ColumnPanel = ({ context, tableDefs, tableDef, column, onUpdate, onDelete })
59
56
  const [label, setLabel] = useState(column.label);
60
57
  const [digits, setDigits] = useState(String(column.digits ?? "0"));
61
58
  const propRef = useRef(null);
59
+ const { t } = useTranslation(translationKey);
62
60
  const handleCancel = () => {
63
61
  setProp(column.id);
64
62
  setOpen(false);
@@ -93,7 +91,7 @@ var ColumnPanel = ({ context, tableDefs, tableDef, column, onUpdate, onDelete })
93
91
  asChild: true
94
92
  }, /* @__PURE__ */ React.createElement(Button, {
95
93
  variant: "ghost",
96
- classNames: "p-0"
94
+ classNames: "p-1"
97
95
  }, /* @__PURE__ */ React.createElement(CaretDown, {
98
96
  className: getSize(4)
99
97
  }))), /* @__PURE__ */ React.createElement(Popover.Portal, null, /* @__PURE__ */ React.createElement(Popover.Content, null, /* @__PURE__ */ React.createElement(Popover.Viewport, {
@@ -104,14 +102,14 @@ var ColumnPanel = ({ context, tableDefs, tableDef, column, onUpdate, onDelete })
104
102
  className: "flex flex-col gap-1"
105
103
  }, /* @__PURE__ */ React.createElement(Section, null, /* @__PURE__ */ React.createElement(Input.Root, null, /* @__PURE__ */ React.createElement(Input.Label, {
106
104
  classNames: "mbe-1"
107
- }, "Label"), /* @__PURE__ */ React.createElement(Input.TextInput, {
105
+ }, t("column label label")), /* @__PURE__ */ React.createElement(Input.TextInput, {
108
106
  placeholder: "Column label",
109
107
  value: label ?? "",
110
108
  onChange: (event) => setLabel(event.target.value),
111
109
  autoFocus: true
112
110
  })), /* @__PURE__ */ React.createElement(Input.Root, null, /* @__PURE__ */ React.createElement(Input.Label, {
113
111
  classNames: "mbe-1 mbs-3"
114
- }, "Property"), /* @__PURE__ */ React.createElement(Input.TextInput, {
112
+ }, t("property key label")), /* @__PURE__ */ React.createElement(Input.TextInput, {
115
113
  ref: propRef,
116
114
  placeholder: "Property key",
117
115
  // TODO(burdon): Provide hooks for value normalization, ENTER, ESC, etc.
@@ -119,18 +117,23 @@ var ColumnPanel = ({ context, tableDefs, tableDef, column, onUpdate, onDelete })
119
117
  onChange: (event) => setProp(event.target.value.replace(/[^\w_]/g, ""))
120
118
  })), /* @__PURE__ */ React.createElement(Input.Root, null, /* @__PURE__ */ React.createElement(Input.Label, {
121
119
  classNames: "mbe-1 mbs-3"
122
- }, "Type"), /* @__PURE__ */ React.createElement(Select.Root, {
120
+ }, t("column type label")), /* @__PURE__ */ React.createElement(Select.Root, {
123
121
  value: type,
124
122
  onValueChange: setType
125
123
  }, /* @__PURE__ */ React.createElement(Select.TriggerButton, {
126
124
  placeholder: "Type",
127
125
  classNames: "is-full"
128
- }), /* @__PURE__ */ React.createElement(Select.Portal, null, /* @__PURE__ */ React.createElement(Select.Content, null, /* @__PURE__ */ React.createElement(Select.Viewport, null, Array.from(columnTypes.entries()).map(([type2, label2]) => /* @__PURE__ */ React.createElement(Select.Option, {
126
+ }), /* @__PURE__ */ React.createElement(Select.Portal, null, /* @__PURE__ */ React.createElement(Select.Content, null, /* @__PURE__ */ React.createElement(Select.Viewport, null, [
127
+ "number",
128
+ "boolean",
129
+ "string",
130
+ "ref"
131
+ ].map((type2) => /* @__PURE__ */ React.createElement(Select.Option, {
129
132
  key: type2,
130
133
  value: type2
131
- }, label2)))))))), type === "number" && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Section, null, /* @__PURE__ */ React.createElement(Input.Root, null, /* @__PURE__ */ React.createElement(Input.Label, {
134
+ }, t(`${type2} column type label`))))))))), type === "number" && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Section, null, /* @__PURE__ */ React.createElement(Input.Root, null, /* @__PURE__ */ React.createElement(Input.Label, {
132
135
  classNames: "mbe-1 mbs-3"
133
- }, "Decimal places"), /* @__PURE__ */ React.createElement(Input.TextInput, {
136
+ }, t("digits label")), /* @__PURE__ */ React.createElement(Input.TextInput, {
134
137
  value: digits ?? "",
135
138
  onChange: (event) => setDigits(event.target.value)
136
139
  })))), type === "ref" && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Section, null, /* @__PURE__ */ React.createElement(Input.Root, null, /* @__PURE__ */ React.createElement(Input.Label, {
@@ -141,7 +144,7 @@ var ColumnPanel = ({ context, tableDefs, tableDef, column, onUpdate, onDelete })
141
144
  }, /* @__PURE__ */ React.createElement(Select.TriggerButton, {
142
145
  placeholder: "Table",
143
146
  classNames: "is-full"
144
- }), /* @__PURE__ */ React.createElement(Select.Portal, null, /* @__PURE__ */ React.createElement(Select.Content, null, /* @__PURE__ */ React.createElement(Select.Viewport, null, tableDefs.filter((t) => t.id !== tableDef.id).map(({ id, name }) => /* @__PURE__ */ React.createElement(Select.Option, {
147
+ }), /* @__PURE__ */ React.createElement(Select.Portal, null, /* @__PURE__ */ React.createElement(Select.Content, null, /* @__PURE__ */ React.createElement(Select.Viewport, null, tableDefs.filter((t2) => t2.id !== tableDef.id).map(({ id, name }) => /* @__PURE__ */ React.createElement(Select.Option, {
145
148
  key: id,
146
149
  value: id
147
150
  }, name ?? id))))))), refTable && /* @__PURE__ */ React.createElement(Input.Root, null, /* @__PURE__ */ React.createElement(Input.Label, {
@@ -163,114 +166,239 @@ var ColumnPanel = ({ context, tableDefs, tableDef, column, onUpdate, onDelete })
163
166
  variant: "primary",
164
167
  classNames: "is-full flex gap-2",
165
168
  onClick: handleSave
166
- }, /* @__PURE__ */ React.createElement("span", null, "Save"), /* @__PURE__ */ React.createElement("div", {
169
+ }, /* @__PURE__ */ React.createElement("span", null, t("save label")), /* @__PURE__ */ React.createElement("div", {
167
170
  className: "grow"
168
171
  }), /* @__PURE__ */ React.createElement(Check, {
169
172
  className: getSize(5)
170
173
  })), /* @__PURE__ */ React.createElement(Button, {
171
174
  classNames: "is-full flex gap-2",
172
175
  onClick: handleCancel
173
- }, /* @__PURE__ */ React.createElement("span", null, "Cancel"), /* @__PURE__ */ React.createElement("div", {
176
+ }, /* @__PURE__ */ React.createElement("span", null, t("cancel label", {
177
+ ns: "os"
178
+ })), /* @__PURE__ */ React.createElement("div", {
174
179
  className: "grow"
175
180
  }), /* @__PURE__ */ React.createElement(X, {
176
181
  className: getSize(5)
177
182
  })), /* @__PURE__ */ React.createElement(Button, {
178
183
  classNames: "is-full flex gap-2",
179
184
  onClick: () => onDelete?.(column.id)
180
- }, /* @__PURE__ */ React.createElement("span", null, "Delete"), /* @__PURE__ */ React.createElement("div", {
185
+ }, /* @__PURE__ */ React.createElement("span", null, t("delete label")), /* @__PURE__ */ React.createElement("div", {
181
186
  className: "grow"
182
187
  }), /* @__PURE__ */ React.createElement(Trash, {
183
188
  className: getSize(5)
184
189
  })))))), /* @__PURE__ */ React.createElement(Popover.Arrow, null))));
185
190
  };
186
191
 
187
- // packages/ui/react-ui-table/src/components/Table.tsx
188
- import { flexRender, getCoreRowModel, getGroupedRowModel, useReactTable } from "@tanstack/react-table";
189
- import React2, { Fragment, useEffect, useRef as useRef2, useState as useState2 } from "react";
192
+ // packages/ui/react-ui-table/src/components/Table/Table.tsx
193
+ import { useControllableState } from "@radix-ui/react-use-controllable-state";
194
+ import { getCoreRowModel, getGroupedRowModel, useReactTable } from "@tanstack/react-table";
195
+ import React6, { Fragment, useCallback, useEffect, useState as useState2 } from "react";
190
196
  import { debounce } from "@dxos/async";
191
- import { groupBorder, inputSurface, mx as mx2 } from "@dxos/react-ui-theme";
197
+
198
+ // packages/ui/react-ui-table/src/components/Table/TableBody.tsx
199
+ import { useArrowNavigationGroup } from "@fluentui/react-tabster";
200
+ import { flexRender } from "@tanstack/react-table";
201
+ import React3 from "react";
202
+
203
+ // packages/ui/react-ui-table/src/components/Table/TableContext.tsx
204
+ import { createContext } from "@radix-ui/react-context";
205
+ var TABLE_NAME = "Table";
206
+ var [TableProvider, useUntypedTableContext] = createContext(TABLE_NAME);
207
+ var useTableContext = (consumerName) => useUntypedTableContext(consumerName);
192
208
 
193
209
  // packages/ui/react-ui-table/src/theme.ts
194
- import { chromeSurface } from "@dxos/react-ui-theme";
195
- var defaultTableSlots = {
196
- // TODO(burdon): head/body/table rows.
197
- header: {
198
- className: [
199
- chromeSurface,
200
- "px-2 font-light select-none"
201
- ]
202
- },
203
- footer: {
204
- className: [
205
- chromeSurface,
206
- "px-2 font-light"
207
- ]
208
- },
209
- cell: {
210
- className: "px-2"
211
- },
212
- group: {
213
- className: "px-2 font-light text-xs text-left"
214
- },
215
- focus: {
216
- className: "ring ring-primary-600 ring-inset"
217
- },
218
- selected: {
219
- className: "!bg-teal-100 dark:!bg-teal-700"
220
- }
210
+ import { chromeSurface, fixedSurface, focusRing, ghostSelected, ghostSelectedCurrent, groupBorder, mx as mx2 } from "@dxos/react-ui-theme";
211
+ var currentRow = "!bg-neutral-75 !dark:bg-neutral-850";
212
+ var selectedRow = "!bg-primary-100 dark:!bg-primary-700";
213
+ var flushPadding = "pli-0 plb-0";
214
+ var textPadding = "pli-2 plb-0.5";
215
+ var headPadding = "pli-2 plb-1.5";
216
+ var gridCellFocusRing = "focus:z-[11] focus:outline outline-2 outline-primary-500 dark:outline-primary-400";
217
+ var tableRoot = ({ fullWidth }, ...etc) => mx2("table-fixed", fullWidth && "is-full", ...etc);
218
+ var groupTh = (_props, ...etc) => mx2("text-start font-medium", flushPadding, ...etc);
219
+ var theadRoot = ({ header, stickyHeader }, ...etc) => mx2(header ? stickyHeader && "sticky block-start-[--sticky-top] z-[1]" : "collapse", header && fixedSurface, ...etc);
220
+ var theadTr = (_props, ...etc) => mx2("group", ...etc);
221
+ var theadTh = ({ border }, ...etc) => mx2("relative text-start font-medium select-none truncate", headPadding, border && "border", border && groupBorder, ...etc);
222
+ var theadResizeRoot = (_props, ...etc) => mx2("absolute top-0 pis-1 h-full z-[10] w-[7px] -right-[5px] cursor-col-resize select-none touch-none opacity-20 hover:opacity-100", ...etc);
223
+ var theadResizeThumb = (_props, ...etc) => mx2("flex group-hover:bg-neutral-700 -ml-[2px] w-[1px] h-full", ...etc);
224
+ var tbodyRoot = (_props, ...etc) => mx2(...etc);
225
+ var tbodyTr = ({ canBeCurrent }, ...etc) => mx2("group", canBeCurrent ? ghostSelectedCurrent : ghostSelected, canBeCurrent && focusRing, canBeCurrent && "cursor-pointer rounded", ...etc);
226
+ var tdRoot = ({ border, isGrid }, ...etc) => mx2("relative", flushPadding, border && "border", border && groupBorder, isGrid && gridCellFocusRing, ...etc);
227
+ var tdContent = (_props, ...etc) => mx2(...etc);
228
+ var tfootRoot = (_props, ...etc) => mx2("sticky block-end-0 z-10", chromeSurface, ...etc);
229
+ var tfootTr = (_props, ...etc) => mx2(...etc);
230
+ var tfootTh = ({ border }, ...etc) => mx2(textPadding, chromeSurface, border && groupBorder, ...etc);
231
+
232
+ // packages/ui/react-ui-table/src/components/Cell/Cell.tsx
233
+ import { useFocusableGroup } from "@fluentui/react-tabster";
234
+ import React2 from "react";
235
+ var CELL_NAME = "Cell";
236
+ var FocusableCell = ({ cell, children }) => {
237
+ const tableContext = useTableContext(CELL_NAME);
238
+ const domAttributes = useFocusableGroup({
239
+ tabBehavior: "limited"
240
+ });
241
+ return /* @__PURE__ */ React2.createElement("td", {
242
+ tabIndex: 0,
243
+ ...domAttributes,
244
+ className: tdRoot(tableContext, cell.column.columnDef.meta?.cell?.classNames)
245
+ }, children);
246
+ };
247
+ var StaticCell = ({ cell, children }) => {
248
+ const tableContext = useTableContext(CELL_NAME);
249
+ return /* @__PURE__ */ React2.createElement("td", {
250
+ className: tdRoot(tableContext, cell.column.columnDef.meta?.cell?.classNames)
251
+ }, children);
221
252
  };
253
+ var Cell = ({ cell, children }) => {
254
+ const { isGrid } = useTableContext(CELL_NAME);
255
+ const Root = isGrid ? FocusableCell : StaticCell;
256
+ return /* @__PURE__ */ React2.createElement(Root, {
257
+ cell
258
+ }, children);
259
+ };
260
+ Cell.displayName = CELL_NAME;
222
261
 
223
- // packages/ui/react-ui-table/src/components/Table.tsx
224
- var updateSelection = (selectionState, id, selection) => {
225
- switch (selection) {
226
- case "single": {
227
- return {
228
- [id]: true
229
- };
230
- }
231
- case "single-toggle": {
232
- return selectionState[id] ? {} : {
233
- [id]: true
234
- };
235
- }
236
- case "multiple": {
237
- return {
238
- [id]: true,
239
- ...selectionState
240
- };
241
- }
242
- case "multiple-toggle": {
243
- if (selectionState[id]) {
244
- const newSelectionState = {
245
- ...selectionState
246
- };
247
- delete newSelectionState[id];
248
- return newSelectionState;
249
- } else {
250
- return {
251
- [id]: true,
252
- ...selectionState
253
- };
262
+ // packages/ui/react-ui-table/src/components/Table/TableBody.tsx
263
+ var TABLE_BODY_NAME = "TableBody";
264
+ var TableBody = ({ rows }) => {
265
+ const tableContext = useTableContext(TABLE_BODY_NAME);
266
+ const { table, keyAccessor, debug, expand, isGrid, onDatumClick, currentDatum } = tableContext;
267
+ const rowSelection = table.getState().rowSelection;
268
+ const domAttributes = useArrowNavigationGroup({
269
+ axis: "grid"
270
+ });
271
+ const canBeCurrent = !isGrid && !!onDatumClick;
272
+ return /* @__PURE__ */ React3.createElement("tbody", isGrid && domAttributes, rows.map((row) => {
273
+ const isCurrent = currentDatum === row.original;
274
+ const isSelected = rowSelection?.[row.id];
275
+ return /* @__PURE__ */ React3.createElement("tr", {
276
+ key: keyAccessor ? keyAccessor(row.original) : row.id,
277
+ className: tbodyTr({
278
+ canBeCurrent
279
+ }),
280
+ ...isCurrent && {
281
+ "aria-current": "location"
282
+ },
283
+ ...isSelected && {
284
+ "aria-selected": "true"
285
+ },
286
+ ...canBeCurrent && {
287
+ tabIndex: 0,
288
+ onClick: () => {
289
+ onDatumClick(row.original);
290
+ },
291
+ onKeyUp: ({ key }) => {
292
+ if (key === "Enter" || key === " ") {
293
+ onDatumClick(row.original);
294
+ }
295
+ }
254
296
  }
255
- }
256
- }
257
- return selectionState;
297
+ }, debug && /* @__PURE__ */ React3.createElement("td", null, row.id), row.getVisibleCells().map((cell) => {
298
+ return /* @__PURE__ */ React3.createElement(Cell, {
299
+ key: cell.id,
300
+ cell
301
+ }, flexRender(cell.column.columnDef.cell, {
302
+ className: "pli-2",
303
+ ...cell.getContext()
304
+ }));
305
+ }), expand && /* @__PURE__ */ React3.createElement("td", null));
306
+ }));
258
307
  };
259
- var Table = ({ slots = defaultTableSlots, ...props }) => {
260
- const { data = [], columns = [], onColumnResize, columnVisibility, header = true, footer, select, selected, onSelectedChange, fullWidth, pinToBottom, debug } = props;
261
- const [focus, setFocus] = useState2();
262
- const [rowSelection, setRowSelection] = useState2({});
263
- useEffect(() => {
264
- setRowSelection(selected?.reduce((selectionState, selected2) => {
265
- const row = table.getRowModel().rows.find((row2) => row2.original === selected2);
266
- if (row) {
267
- selectionState[row.id] = true;
268
- }
269
- return selectionState;
270
- }, {}) ?? {});
308
+ TableBody.displayName = TABLE_BODY_NAME;
309
+
310
+ // packages/ui/react-ui-table/src/components/Table/TableFooter.tsx
311
+ import { flexRender as flexRender2 } from "@tanstack/react-table";
312
+ import React4 from "react";
313
+ var TABLE_FOOTER_NAME = "TableFooter";
314
+ var TableFooter = (props) => {
315
+ const tableContext = useTableContext(TABLE_FOOTER_NAME);
316
+ const { table, expand, debug } = tableContext;
317
+ const footers = table.getFooterGroups();
318
+ return /* @__PURE__ */ React4.createElement("tfoot", {
319
+ className: tfootRoot(props)
320
+ }, footers.map((footerGroup) => /* @__PURE__ */ React4.createElement("tr", {
321
+ key: footerGroup.id,
322
+ className: tfootTr(tableContext)
323
+ }, debug && /* @__PURE__ */ React4.createElement("th", null), footerGroup.headers.map((footer) => {
324
+ return /* @__PURE__ */ React4.createElement("th", {
325
+ key: footer.id,
326
+ className: tfootTh(tableContext, footer.column.columnDef.meta?.footer?.classNames)
327
+ }, footer.isPlaceholder ? null : flexRender2(footer.column.columnDef.footer, footer.getContext()));
328
+ }), expand && /* @__PURE__ */ React4.createElement("th", null))));
329
+ };
330
+ TableFooter.displayName = TABLE_FOOTER_NAME;
331
+
332
+ // packages/ui/react-ui-table/src/components/Table/TableHead.tsx
333
+ import { flexRender as flexRender3 } from "@tanstack/react-table";
334
+ import React5 from "react";
335
+ import { mx as mx3 } from "@dxos/react-ui-theme";
336
+ var TABLE_HEAD_NAME = "TableHead";
337
+ var TableHead = (_props) => {
338
+ const tableContext = useTableContext(TABLE_HEAD_NAME);
339
+ const headerGroups = tableContext.table.getHeaderGroups();
340
+ const state = tableContext.table.getState();
341
+ return /* @__PURE__ */ React5.createElement("thead", {
342
+ className: theadRoot(tableContext)
343
+ }, headerGroups.map((headerGroup) => {
344
+ return (
345
+ // Group element to hover resize handles.
346
+ /* @__PURE__ */ React5.createElement("tr", {
347
+ key: headerGroup.id,
348
+ className: theadTr(tableContext)
349
+ }, tableContext.debug && /* @__PURE__ */ React5.createElement("th", {
350
+ className: "font-mono",
351
+ style: {
352
+ width: 32
353
+ }
354
+ }, "#"), headerGroup.headers.map((header) => {
355
+ const isResizing = header.column.getIsResizing();
356
+ return /* @__PURE__ */ React5.createElement("th", {
357
+ key: header.id,
358
+ style: {
359
+ width: header.getSize()
360
+ },
361
+ className: theadTh(tableContext, header.column.columnDef.meta?.header?.classNames)
362
+ }, !header || header.isPlaceholder ? null : flexRender3(header.column.columnDef.header, header.getContext()), header.column.columnDef.meta?.resizable && /* @__PURE__ */ React5.createElement("div", {
363
+ className: theadResizeRoot(tableContext, isResizing && "hidden"),
364
+ style: {
365
+ transform: `translateX(${isResizing ? state.columnSizingInfo.deltaOffset : 0}px)`
366
+ },
367
+ onMouseDown: header.getResizeHandler(),
368
+ onTouchStart: header.getResizeHandler()
369
+ }, /* @__PURE__ */ React5.createElement("div", {
370
+ className: mx3(theadResizeThumb(tableContext))
371
+ })));
372
+ }), tableContext.expand && /* @__PURE__ */ React5.createElement("th", null))
373
+ );
374
+ }));
375
+ };
376
+ TableHead.displayName = TABLE_HEAD_NAME;
377
+
378
+ // packages/ui/react-ui-table/src/components/Table/Table.tsx
379
+ var Table = (props) => {
380
+ const { role, data = [], columns = [], onColumnResize, columnVisibility, header = true, footer, rowsSelectable, fullWidth, debug, onDataSelectionChange, classNames } = props;
381
+ const TableProvider2 = TableProvider;
382
+ const [rowSelection = {}, directlySetRowSelection] = useControllableState({
383
+ prop: props.rowSelection,
384
+ onChange: props.onRowSelectionChange,
385
+ defaultProp: props.defaultRowSelection
386
+ });
387
+ const handleRowSelectionChange = useCallback((updaterOrValue) => {
388
+ const nextRowSelection = typeof updaterOrValue === "function" ? updaterOrValue(rowSelection) : updaterOrValue;
389
+ if (rowsSelectable === "multi") {
390
+ directlySetRowSelection(nextRowSelection);
391
+ } else if (rowsSelectable) {
392
+ const nextRowSelectionKey = Object.keys(nextRowSelection).filter((id) => !rowSelection[id])[0];
393
+ directlySetRowSelection(nextRowSelectionKey ? {
394
+ [nextRowSelectionKey]: true
395
+ } : {});
396
+ } else {
397
+ directlySetRowSelection({});
398
+ }
271
399
  }, [
272
- select,
273
- selected
400
+ rowSelection,
401
+ directlySetRowSelection
274
402
  ]);
275
403
  const [columnSizingInfo, setColumnSizingInfo] = useState2({});
276
404
  const onColumnResizeDebounced = debounce((info) => onColumnResize?.(info), 500);
@@ -286,268 +414,96 @@ var Table = ({ slots = defaultTableSlots, ...props }) => {
286
414
  props.grouping
287
415
  ]);
288
416
  const table = useReactTable({
417
+ // Data
418
+ meta: {},
289
419
  data,
420
+ // Columns
290
421
  columns,
291
422
  defaultColumn: {
292
- size: 400,
293
- maxSize: 800
423
+ size: 256,
424
+ maxSize: 1024
294
425
  },
426
+ // Rows
295
427
  getCoreRowModel: getCoreRowModel(),
296
- meta: {},
297
- // TODO(burdon): Pagination.
298
- // TODO(burdon): Sorting.
299
- // TODO(burdon): Filtering.
428
+ // State
300
429
  state: {
301
430
  columnVisibility,
302
431
  columnSizingInfo,
303
432
  rowSelection,
304
433
  grouping
305
434
  },
306
- // Grouping.
435
+ // Grouping
307
436
  getGroupedRowModel: getGroupedRowModel(),
308
437
  onGroupingChange: setGrouping,
309
- // Selection.
310
- enableRowSelection: select === "single" || select === "single-toggle",
311
- enableMultiRowSelection: select === "multiple" || select === "multiple-toggle",
312
- onRowSelectionChange: (rows) => {
313
- setRowSelection(rows);
314
- },
315
- // Resize columns.
316
- // TODO(burdon): Drag to re-order columns.
438
+ // Selection
439
+ ...rowsSelectable === "multi" ? {
440
+ enableMultiRowSelection: true
441
+ } : rowsSelectable ? {
442
+ enableRowSelection: true
443
+ } : {},
444
+ onRowSelectionChange: handleRowSelectionChange,
445
+ // Resize columns
317
446
  columnResizeMode: "onChange",
318
447
  enableColumnResizing: true,
319
448
  onColumnSizingInfoChange: setColumnSizingInfo,
449
+ // Debug
320
450
  debugTable: debug
321
451
  });
322
- const expand = false;
323
- const containerRef = usePinToBottom(data, pinToBottom);
324
- const handleSelect = (row) => {
325
- if (select) {
326
- setRowSelection((selectionState) => {
327
- const newSelectionState = updateSelection(selectionState, row.id, select);
328
- if (onSelectedChange) {
329
- const rows = Object.keys(newSelectionState).map((id) => table.getRowModel().rowsById[id].original);
330
- onSelectedChange(rows);
331
- }
332
- return newSelectionState;
333
- });
334
- } else {
335
- onSelectedChange?.([
336
- row.original
337
- ]);
338
- }
339
- };
340
- return /* @__PURE__ */ React2.createElement("div", {
341
- ref: containerRef,
342
- className: mx2("grow overflow-auto", slots?.root?.className)
343
- }, /* @__PURE__ */ React2.createElement("table", {
344
- // Styles:
345
- // table-fixed: Prevents fixed sized columns from shrinking.
346
- className: mx2(!fullWidth && "table-fixed", inputSurface, slots?.table?.className),
347
- style: {
348
- width: fullWidth ? "100%" : table.getTotalSize()
452
+ useEffect(() => {
453
+ if (onDataSelectionChange) {
454
+ onDataSelectionChange(Object.keys(rowSelection).map((id) => table.getRowModel().rowsById[id].original));
349
455
  }
350
- }, /* @__PURE__ */ React2.createElement(TableHead, {
456
+ }, [
457
+ onDataSelectionChange,
458
+ rowSelection,
459
+ table
460
+ ]);
461
+ const expand = false;
462
+ return /* @__PURE__ */ React6.createElement(TableProvider2, {
351
463
  ...props,
352
- slots,
353
464
  header,
354
- state: table.getState(),
355
- headers: table.getHeaderGroups()
356
- }), grouping.length === 0 && /* @__PURE__ */ React2.createElement(TableBody, {
357
- ...props,
358
- slots,
359
- rowSelection,
360
465
  expand,
361
- focus,
362
- onFocus: setFocus,
363
- onSelect: handleSelect,
466
+ table,
467
+ isGrid: role === "grid" || role === "treegrid"
468
+ }, /* @__PURE__ */ React6.createElement("table", {
469
+ role,
470
+ className: tableRoot(props, classNames),
471
+ ...!fullWidth && {
472
+ style: {
473
+ width: table.getTotalSize()
474
+ }
475
+ }
476
+ }, /* @__PURE__ */ React6.createElement(TableHead, null), grouping.length === 0 && /* @__PURE__ */ React6.createElement(TableBody, {
364
477
  rows: table.getRowModel().rows
365
478
  }), grouping.length !== 0 && table.getGroupedRowModel().rows.map((row, i) => {
366
- return /* @__PURE__ */ React2.createElement(Fragment, {
479
+ return /* @__PURE__ */ React6.createElement(Fragment, {
367
480
  key: i
368
- }, /* @__PURE__ */ React2.createElement("thead", null, /* @__PURE__ */ React2.createElement("tr", null, slots?.margin && /* @__PURE__ */ React2.createElement("th", {
369
- className: mx2(slots?.margin?.className)
370
- }), debug && /* @__PURE__ */ React2.createElement("th", null), /* @__PURE__ */ React2.createElement("th", {
481
+ }, /* @__PURE__ */ React6.createElement("thead", null, /* @__PURE__ */ React6.createElement("tr", null, debug && /* @__PURE__ */ React6.createElement("th", null), /* @__PURE__ */ React6.createElement("th", {
371
482
  // TODO(burdon): Calculate row span.
372
483
  colSpan: table.getHeaderGroups()[0].headers.length,
373
- className: mx2("text-left", slots?.group?.className)
374
- }, table.getState().grouping[0], "[", String(row.getGroupingValue(table.getState().grouping[0])), "]"))), /* @__PURE__ */ React2.createElement(TableBody, {
375
- ...props,
376
- slots,
377
- rowSelection,
378
- expand,
379
- focus,
380
- onFocus: setFocus,
381
- onSelect: handleSelect,
484
+ className: groupTh(props)
485
+ }, table.getState().grouping[0], "[", String(row.getGroupingValue(table.getState().grouping[0])), "]"))), /* @__PURE__ */ React6.createElement(TableBody, {
382
486
  rows: row.subRows
383
487
  }));
384
- }), footer && /* @__PURE__ */ React2.createElement(TableFoot, {
385
- ...props,
386
- slots,
387
- footers: table.getFooterGroups()
388
- })), debug && /* @__PURE__ */ React2.createElement("pre", {
488
+ }), footer && /* @__PURE__ */ React6.createElement(TableFooter, null)), debug && /* @__PURE__ */ React6.createElement("pre", {
389
489
  className: "font-mono text-xs text-neutral-500 my-4 p-2 ring"
390
- }, /* @__PURE__ */ React2.createElement("code", null, JSON.stringify(table.getState(), void 0, 2))));
391
- };
392
- var usePinToBottom = (data, pinToBottom) => {
393
- const containerRef = useRef2(null);
394
- const [stickyScrolling, setStickyScrolling] = useState2(true);
395
- useEffect(() => {
396
- const container = containerRef.current;
397
- if (!pinToBottom || !container) {
398
- return;
399
- }
400
- const handler = () => {
401
- const bottom = container.scrollHeight - container.scrollTop - container.clientHeight === 0;
402
- setStickyScrolling(bottom);
403
- };
404
- container.addEventListener("scroll", handler);
405
- return () => container.removeEventListener("scroll", handler);
406
- }, []);
407
- useEffect(() => {
408
- if (!pinToBottom || !containerRef.current || !stickyScrolling) {
409
- return;
410
- }
411
- containerRef.current?.scroll({
412
- top: containerRef.current.scrollHeight
413
- });
414
- }, [
415
- data
416
- ]);
417
- return containerRef;
418
- };
419
- var TableHead = ({ state, headers, expand, header, debug, fullWidth, border, slots }) => {
420
- return /* @__PURE__ */ React2.createElement("thead", {
421
- className: mx2(header ? [
422
- "sticky top-0 z-10"
423
- ] : "collapse")
424
- }, headers.map((headerGroup) => {
425
- return (
426
- // Group element to hover resize handles.
427
- /* @__PURE__ */ React2.createElement("tr", {
428
- key: headerGroup.id,
429
- className: "font-light group"
430
- }, slots?.margin && /* @__PURE__ */ React2.createElement("th", {
431
- className: mx2(slots?.margin?.className)
432
- }), debug && /* @__PURE__ */ React2.createElement("th", {
433
- className: "text-left",
434
- style: {
435
- width: 32
436
- }
437
- }, "#"), headerGroup.headers.map((header2) => {
438
- return /* @__PURE__ */ React2.createElement("th", {
439
- key: header2.id,
440
- style: {
441
- // Don't set width if fullWidth and no explicit size.
442
- width: fullWidth && header2.column.columnDef.meta?.expand ? void 0 : header2.getSize()
443
- },
444
- // Relative for resize handle.
445
- // TODO(burdon): Border scrolls with main content.
446
- // https://stackoverflow.com/questions/50361698/border-style-do-not-work-with-sticky-position-element
447
- className: mx2("relative text-left", border && groupBorder, slots?.header?.className, header2.column.columnDef.meta?.slots?.header?.className)
448
- }, !header2 || header2.isPlaceholder ? null : flexRender(header2.column.columnDef.header, header2.getContext()), header2.column.columnDef.meta?.resizable && /* @__PURE__ */ React2.createElement("div", {
449
- className: mx2("absolute top-0 pl-1 h-full z-[10] w-[7px] -right-[5px]", "cursor-col-resize select-none touch-none opacity-20 hover:opacity-100", header2.column.getIsResizing() && "hidden"),
450
- style: {
451
- transform: header2.column.getIsResizing() ? `translateX(${state.columnSizingInfo.deltaOffset}px)` : void 0
452
- },
453
- onMouseDown: header2.getResizeHandler(),
454
- onTouchStart: header2.getResizeHandler()
455
- }, /* @__PURE__ */ React2.createElement("div", {
456
- className: "flex group-hover:bg-neutral-700 -ml-[2px] w-[1px] h-full"
457
- })));
458
- }), expand && /* @__PURE__ */ React2.createElement("th", null), slots?.margin && /* @__PURE__ */ React2.createElement("th", {
459
- className: mx2(slots?.margin?.className)
460
- }))
461
- );
462
- }));
463
- };
464
- var TableBody = ({ keyAccessor, rows, rowSelection, focus, onFocus, onSelect, debug, border, expand, slots }) => {
465
- return /* @__PURE__ */ React2.createElement("tbody", null, rows.map((row) => {
466
- return /* @__PURE__ */ React2.createElement("tr", {
467
- key: keyAccessor ? keyAccessor(row.original) : row.id,
468
- onClick: () => onSelect?.(row),
469
- role: "button",
470
- className: mx2("group", rowSelection[row.id] && slots?.selected?.className, focus === row.id && slots?.focus?.className, slots?.row?.className)
471
- }, slots?.margin && /* @__PURE__ */ React2.createElement("td", {
472
- className: mx2(slots?.margin?.className)
473
- }, /* @__PURE__ */ React2.createElement("button", {
474
- role: "button",
475
- style: {
476
- width: 1,
477
- height: 1
478
- },
479
- className: "focus:outline-none",
480
- onFocus: () => onFocus?.(keyAccessor ? keyAccessor(row.original) : row.id),
481
- onBlur: () => onFocus?.(void 0),
482
- onKeyDown: (event) => {
483
- switch (event.key) {
484
- case "ArrowUp": {
485
- break;
486
- }
487
- case "ArrowDown": {
488
- break;
489
- }
490
- }
491
- }
492
- })), debug && /* @__PURE__ */ React2.createElement("td", null, row.id), row.getVisibleCells().map((cell) => {
493
- return /* @__PURE__ */ React2.createElement("td", {
494
- key: cell.id,
495
- className: mx2(border && groupBorder, slots?.cell?.className, cell.column.columnDef.meta?.slots?.cell?.className)
496
- }, flexRender(cell.column.columnDef.cell, {
497
- className: "px-2",
498
- ...cell.getContext()
499
- }));
500
- }), expand && /* @__PURE__ */ React2.createElement("td", null), slots?.margin && /* @__PURE__ */ React2.createElement("td", {
501
- className: mx2(slots?.margin?.className)
502
- }));
503
- }));
504
- };
505
- var TableFoot = ({ footers, expand, slots, debug, border }) => {
506
- return /* @__PURE__ */ React2.createElement("tfoot", {
507
- className: mx2("sticky bottom-0 z-[10]", slots?.footer?.className)
508
- }, footers.map((footerGroup) => /* @__PURE__ */ React2.createElement("tr", {
509
- key: footerGroup.id,
510
- className: "font-thin"
511
- }, slots?.margin && /* @__PURE__ */ React2.createElement("th", {
512
- className: mx2(slots?.margin?.className)
513
- }), debug && /* @__PURE__ */ React2.createElement("th", null), footerGroup.headers.map((footer) => {
514
- return /* @__PURE__ */ React2.createElement("th", {
515
- key: footer.id,
516
- className: mx2(border && groupBorder, "text-left", slots?.footer?.className, footer.column.columnDef.meta?.slots?.footer?.className)
517
- }, footer.isPlaceholder ? null : flexRender(footer.column.columnDef.footer, footer.getContext()));
518
- }), expand && /* @__PURE__ */ React2.createElement("th", null), slots?.margin && /* @__PURE__ */ React2.createElement("th", {
519
- className: mx2(slots?.margin?.className)
520
- }))));
490
+ }, /* @__PURE__ */ React6.createElement("code", null, JSON.stringify(table.getState(), void 0, 2))));
521
491
  };
522
492
 
523
- // packages/ui/react-ui-table/src/components/ComboboxCell.tsx
493
+ // packages/ui/react-ui-table/src/components/Cell/CellCombobox.tsx
524
494
  import { CaretDown as CaretDown2 } from "@phosphor-icons/react";
525
- import React3, { useCallback, useState as useState3 } from "react";
526
- import { Popover as Popover2, useTranslation } from "@dxos/react-ui";
495
+ import React7, { useCallback as useCallback2, useState as useState3 } from "react";
496
+ import { Popover as Popover2, useTranslation as useTranslation2 } from "@dxos/react-ui";
527
497
  import { PopoverCombobox } from "@dxos/react-ui-searchlist";
528
- import { descriptionText, getSize as getSize2, mx as mx3, staticPlaceholderText } from "@dxos/react-ui-theme";
529
-
530
- // packages/ui/react-ui-table/src/translations.ts
531
- var translationKey = "table";
532
- var translations_default = [
533
- {
534
- "en-US": {
535
- [translationKey]: {
536
- "no selection label": "\xA0"
537
- }
538
- }
539
- }
540
- ];
541
-
542
- // packages/ui/react-ui-table/src/components/ComboboxCell.tsx
498
+ import { descriptionText, getSize as getSize2, mx as mx4, staticPlaceholderText } from "@dxos/react-ui-theme";
543
499
  var initialItems = (model, value) => value ? {
544
500
  [model.getId(value)]: {
545
501
  label: model.getText(value),
546
502
  data: value
547
503
  }
548
504
  } : {};
549
- var ComboboxCell = ({ model, value, onValueChange }) => {
550
- const { t } = useTranslation(translationKey);
505
+ var CellCombobox = ({ model, value, onValueChange }) => {
506
+ const { t } = useTranslation2(translationKey);
551
507
  const [items, setItems] = useState3(initialItems(model, value));
552
508
  const [loading, setLoading] = useState3(false);
553
509
  const [searchInputValue, setSearchInputValue] = useState3("");
@@ -564,43 +520,43 @@ var ComboboxCell = ({ model, value, onValueChange }) => {
564
520
  }, initialItems(model, value)));
565
521
  setLoading(false);
566
522
  };
567
- const handleValueChange = useCallback((id) => {
523
+ const handleValueChange = useCallback2((id) => {
568
524
  onValueChange(items[id]?.data);
569
525
  }, [
570
526
  items,
571
527
  onValueChange
572
528
  ]);
573
- return /* @__PURE__ */ React3.createElement(PopoverCombobox.Root, {
529
+ return /* @__PURE__ */ React7.createElement(PopoverCombobox.Root, {
574
530
  placeholder: t("no selection label")
575
- }, /* @__PURE__ */ React3.createElement(PopoverCombobox.Trigger, {
531
+ }, /* @__PURE__ */ React7.createElement(PopoverCombobox.Trigger, {
576
532
  variant: "ghost",
577
- classNames: "is-full"
578
- }, value ? /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement("span", {
579
- className: mx3("font-normal text-start flex-1 min-is-0 truncate mie-2", !value && staticPlaceholderText)
580
- }, model.getText(value)), /* @__PURE__ */ React3.createElement(CaretDown2, {
533
+ classNames: "is-full rounded-none ring-inset"
534
+ }, value ? /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement("span", {
535
+ className: mx4("font-normal text-start flex-1 min-is-0 truncate mie-2", !value && staticPlaceholderText)
536
+ }, model.getText(value)), /* @__PURE__ */ React7.createElement(CaretDown2, {
581
537
  weight: "bold",
582
538
  className: getSize2(3)
583
- })) : null), /* @__PURE__ */ React3.createElement(PopoverCombobox.Content, {
539
+ })) : null), /* @__PURE__ */ React7.createElement(PopoverCombobox.Content, {
584
540
  shouldFilter: false,
585
541
  classNames: "is-[--radix-popover-trigger-width]"
586
- }, /* @__PURE__ */ React3.createElement(PopoverCombobox.Input, {
542
+ }, /* @__PURE__ */ React7.createElement(PopoverCombobox.Input, {
587
543
  value: searchInputValue,
588
544
  onValueChange: handleSearchInputValueChange
589
- }), /* @__PURE__ */ React3.createElement(PopoverCombobox.List, null, searchInputValue.length < 1 ? /* @__PURE__ */ React3.createElement("p", {
590
- className: mx3("text-center", descriptionText)
591
- }, "Start typing to search") : loading ? /* @__PURE__ */ React3.createElement("p", {
592
- className: mx3("text-center", descriptionText)
593
- }, "Searching\u2026") : /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement(PopoverCombobox.Empty, {
545
+ }), /* @__PURE__ */ React7.createElement(PopoverCombobox.List, null, searchInputValue.length < 1 ? /* @__PURE__ */ React7.createElement("p", {
546
+ className: mx4("text-center", descriptionText)
547
+ }, "Start typing to search") : loading ? /* @__PURE__ */ React7.createElement("p", {
548
+ className: mx4("text-center", descriptionText)
549
+ }, "Searching\u2026") : /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement(PopoverCombobox.Empty, {
594
550
  classNames: [
595
551
  "text-center",
596
552
  descriptionText
597
553
  ]
598
- }, "No results"), Object.entries(items).map(([id, { label }]) => /* @__PURE__ */ React3.createElement(PopoverCombobox.Item, {
554
+ }, "No results"), Object.entries(items).map(([id, { label }]) => /* @__PURE__ */ React7.createElement(PopoverCombobox.Item, {
599
555
  key: id,
600
556
  value: id,
601
557
  onSelect: handleValueChange,
602
558
  classNames: "truncate"
603
- }, label)))), /* @__PURE__ */ React3.createElement(Popover2.Arrow, null)));
559
+ }, label)))), /* @__PURE__ */ React7.createElement(Popover2.Arrow, null)));
604
560
  };
605
561
 
606
562
  // packages/ui/react-ui-table/src/helpers.tsx
@@ -609,239 +565,341 @@ import { Check as Check2, ClipboardText, X as X2 } from "@phosphor-icons/react";
609
565
  import { createColumnHelper } from "@tanstack/react-table";
610
566
  import format from "date-fns/format";
611
567
  import formatDistanceToNow from "date-fns/formatDistanceToNow";
612
- import defaultsDeep from "lodash.defaultsdeep";
613
- import React4, { useRef as useRef3, useState as useState4 } from "react";
568
+ import React8, { useEffect as useEffect2, useRef as useRef2, useState as useState4 } from "react";
614
569
  import { Input as Input2, Tooltip } from "@dxos/react-ui";
615
- import { getSize as getSize3, mx as mx4 } from "@dxos/react-ui-theme";
616
- import { stripUndefinedValues } from "@dxos/util";
617
- var findNextFocusable = (findFirstFocusable, container) => {
618
- const next = findFirstFocusable(container);
619
- if (next) {
620
- if (next?.tagName === "INPUT") {
621
- return next;
622
- }
623
- if (container.nextElementSibling) {
624
- return findNextFocusable(findFirstFocusable, container.nextSibling);
625
- }
626
- }
627
- };
570
+ import { getSize as getSize3, mx as mx5 } from "@dxos/react-ui-theme";
628
571
  var createColumnBuilder = () => ({
629
572
  helper: createColumnHelper(),
630
573
  builder: new ColumnBuilder()
631
574
  });
632
- var defaults = (options, ...sources) => {
633
- return stripUndefinedValues(defaultsDeep({}, options, ...sources));
575
+ var ComboboxBuilderCell = (cellContext) => {
576
+ const { model, onUpdate } = cellContext.column.columnDef.meta;
577
+ return model ? /* @__PURE__ */ React8.createElement(CellCombobox, {
578
+ model,
579
+ value: cellContext.getValue(),
580
+ onValueChange: (value) => onUpdate?.(cellContext.row.original, cellContext.column.id, value)
581
+ }) : null;
582
+ };
583
+ var StringBuilderCell = (cellContext) => {
584
+ const { onUpdate } = cellContext.column.columnDef.meta;
585
+ const initialValue = cellContext.getValue();
586
+ const [value, setValue] = useState4(initialValue);
587
+ const inputRef = useRef2(null);
588
+ const handleSave = () => {
589
+ if (value === initialValue) {
590
+ return;
591
+ }
592
+ onUpdate?.(cellContext.row.original, cellContext.column.id, value);
593
+ };
594
+ const handleCancel = () => {
595
+ setValue(initialValue);
596
+ };
597
+ const rows = cellContext.table.getRowModel().flatRows;
598
+ const columns = cellContext.table.getVisibleFlatColumns();
599
+ const placeholder = cellContext.row.index === rows.length - 1 && columns[0].id === cellContext.column.id;
600
+ return /* @__PURE__ */ React8.createElement(Input2.Root, null, /* @__PURE__ */ React8.createElement(Input2.TextInput, {
601
+ ref: inputRef,
602
+ variant: "subdued",
603
+ placeholder: placeholder ? "Add row..." : void 0,
604
+ classNames: [
605
+ "is-full",
606
+ textPadding
607
+ ],
608
+ value: value ?? "",
609
+ onBlur: () => handleSave(),
610
+ onChange: (event) => setValue(event.target.value),
611
+ onKeyDown: (event) => event.key === "Enter" && handleSave() || event.key === "Escape" && handleCancel()
612
+ }));
613
+ };
614
+ var parseNumber = (text) => text?.trim().length ? Number(text) : NaN;
615
+ var displayNumber = (value, digits) => value || value === 0 ? value.toLocaleString(void 0, {
616
+ minimumFractionDigits: digits ?? 0,
617
+ maximumFractionDigits: digits ?? 2
618
+ }) : "";
619
+ var NumberBuilderCell = (cellContext) => {
620
+ const { onUpdate, digits } = cellContext.column.columnDef.meta;
621
+ const value = cellContext.getValue();
622
+ const [text, setText] = useState4(displayNumber(value, digits));
623
+ const { findPrevFocusable } = useFocusFinders();
624
+ const ref = useRef2(null);
625
+ useEffect2(() => {
626
+ (!document.activeElement || document.activeElement !== ref.current) && setText(displayNumber(value, digits));
627
+ }, [
628
+ digits,
629
+ value
630
+ ]);
631
+ const handleEdit = () => {
632
+ if (onUpdate) {
633
+ setText(value !== void 0 ? String(value) : "");
634
+ }
635
+ };
636
+ const handleSave = () => {
637
+ const nextValue = parseNumber(text);
638
+ onUpdate?.(cellContext.row.original, cellContext.column.id, isNaN(nextValue) ? void 0 : nextValue);
639
+ setText(displayNumber(nextValue, digits));
640
+ ref.current && document.activeElement === ref.current && findPrevFocusable(ref.current)?.focus();
641
+ };
642
+ const _handleCancel = () => {
643
+ setText(displayNumber(value, digits));
644
+ ref.current && findPrevFocusable(ref.current)?.focus();
645
+ };
646
+ return /* @__PURE__ */ React8.createElement(Input2.Root, null, /* @__PURE__ */ React8.createElement(Input2.TextInput, {
647
+ ref,
648
+ variant: "subdued",
649
+ value: text,
650
+ classNames: [
651
+ "is-full text-end font-mono",
652
+ textPadding
653
+ ],
654
+ onBlur: handleSave,
655
+ onFocus: handleEdit,
656
+ onChange: ({ target: { value: value2 } }) => setText(value2),
657
+ onKeyDown: (event) => {
658
+ return (
659
+ /* (event.key === 'Escape' && handleCancel()) || */
660
+ event.key === "Enter" && handleSave()
661
+ );
662
+ }
663
+ }));
664
+ };
665
+ var RowSelectorBuilderCell = (cellContext) => {
666
+ const { cell } = cellContext.column.columnDef.meta;
667
+ const { row } = cellContext;
668
+ const checked = row.getCanSelect() ? row.getIsSelected() : row.getCanSelectSubRows() && (row.getIsSomeSelected() ? "indeterminate" : row.getIsAllSubRowsSelected());
669
+ return /* @__PURE__ */ React8.createElement(Input2.Root, null, /* @__PURE__ */ React8.createElement(Input2.Checkbox, {
670
+ size: 4,
671
+ classNames: [
672
+ "mli-auto",
673
+ cell?.classNames
674
+ ],
675
+ checked,
676
+ onCheckedChange: (event) => {
677
+ if (row.getCanSelect()) {
678
+ row.getToggleSelectedHandler()(event);
679
+ }
680
+ },
681
+ disabled: !(row.getCanSelect() || row.getCanSelectSubRows())
682
+ }));
683
+ };
684
+ var SwitchBuilderCell = (cellContext) => {
685
+ const { onUpdate } = cellContext.column.columnDef.meta;
686
+ const value = cellContext.getValue();
687
+ return /* @__PURE__ */ React8.createElement(Input2.Root, null, /* @__PURE__ */ React8.createElement(Input2.Switch, {
688
+ classNames: "block mli-auto",
689
+ onClick: (event) => event.stopPropagation(),
690
+ disabled: !onUpdate,
691
+ checked: !!value,
692
+ onCheckedChange: (value2) => {
693
+ onUpdate?.(cellContext.row.original, cellContext.column.id, !!value2);
694
+ }
695
+ }));
634
696
  };
635
697
  var ColumnBuilder = class {
636
698
  /**
637
- * Select value
699
+ * Combobox value
638
700
  */
639
- select({ label, className, model, onUpdate, ...props }) {
640
- return defaults(props, {
701
+ combobox({ label, classNames, model, onUpdate, ...props }) {
702
+ return {
641
703
  minSize: 100,
642
704
  header: (column) => {
643
- return /* @__PURE__ */ React4.createElement("div", {
644
- className: "truncate"
645
- }, label ?? column.header.id);
705
+ return label ?? column.header.id;
646
706
  },
647
- cell: model && onUpdate ? (cell) => /* @__PURE__ */ React4.createElement(ComboboxCell, {
707
+ cell: model && onUpdate ? ComboboxBuilderCell : (cellContext) => {
708
+ const cellValue = cellContext.getValue();
709
+ return /* @__PURE__ */ React8.createElement("div", {
710
+ className: mx5("truncate", classNames)
711
+ }, cellValue ? model?.getText(cellValue) : "");
712
+ },
713
+ ...props,
714
+ meta: {
715
+ ...props.meta,
648
716
  model,
649
- value: cell.getValue(),
650
- onValueChange: (value) => onUpdate?.(cell.row.original, cell.column.id, value)
651
- }) : (cell) => {
652
- const value = cell.getValue();
653
- return /* @__PURE__ */ React4.createElement("div", {
654
- className: mx4("truncate", className)
655
- }, value ? model.getText(value) : "");
717
+ onUpdate,
718
+ cell: {
719
+ classNames,
720
+ ...props.meta?.cell
721
+ }
656
722
  }
657
- });
723
+ };
658
724
  }
659
725
  /**
660
726
  * String formats.
661
727
  */
662
- string({ label, className, onUpdate, ...props } = {}) {
663
- return defaults(props, {
728
+ string({ label, classNames, onUpdate, ...props } = {}) {
729
+ return {
664
730
  minSize: 100,
665
731
  // TODO(burdon): Default.
666
732
  header: (column) => {
667
- return /* @__PURE__ */ React4.createElement("div", {
668
- className: "truncate"
669
- }, label ?? column.header.id);
733
+ return label ?? column.header.id;
670
734
  },
671
- cell: onUpdate ? (cell) => {
672
- const initialValue = cell.getValue();
673
- const [value, setValue] = useState4(initialValue);
674
- const inputRef = useRef3(null);
675
- const { findFirstFocusable } = useFocusFinders();
676
- const handleSave = (focusNext = false) => {
677
- if (value === initialValue) {
678
- return;
679
- }
680
- onUpdate?.(cell.row.original, cell.column.id, value);
681
- if (focusNext) {
682
- const rowElement = inputRef.current?.parentElement?.parentElement;
683
- setTimeout(() => {
684
- const next = findNextFocusable(findFirstFocusable, rowElement);
685
- next?.focus();
686
- });
687
- }
688
- };
689
- const handleCancel = () => {
690
- setValue(initialValue);
691
- };
692
- const rows = cell.table.getRowModel().flatRows;
693
- const columns = cell.table.getVisibleFlatColumns();
694
- const placeholder = cell.row.index === rows.length - 1 && columns[0].id === cell.column.id;
695
- return /* @__PURE__ */ React4.createElement(Input2.Root, null, /* @__PURE__ */ React4.createElement(Input2.TextInput, {
696
- ref: inputRef,
697
- variant: "subdued",
698
- placeholder: placeholder ? "Add row..." : void 0,
699
- classNames: "px-2",
700
- value: value ?? "",
701
- onBlur: () => handleSave(false),
702
- onChange: (event) => setValue(event.target.value),
703
- onKeyDown: (event) => event.key === "Enter" && handleSave(true) || event.key === "Escape" && handleCancel()
704
- }));
705
- } : (cell) => {
735
+ cell: onUpdate ? StringBuilderCell : (cell) => {
706
736
  const value = cell.getValue();
707
- return /* @__PURE__ */ React4.createElement("div", {
708
- className: mx4("truncate", className)
737
+ return /* @__PURE__ */ React8.createElement("div", {
738
+ className: mx5("truncate", textPadding, classNames)
709
739
  }, value);
740
+ },
741
+ ...props,
742
+ meta: {
743
+ ...props.meta,
744
+ onUpdate,
745
+ cell: {
746
+ ...props.meta?.cell,
747
+ classNames
748
+ }
710
749
  }
711
- });
750
+ };
712
751
  }
713
752
  /**
714
753
  * Number formats.
715
754
  */
716
- number({ label, minSize, className, digits, onUpdate, ...props } = {}) {
717
- return defaults(props, {
755
+ number({ label, minSize, classNames, digits, onUpdate, ...props } = {}) {
756
+ return {
718
757
  size: 100,
719
758
  minSize: 100,
720
- header: (cell) => /* @__PURE__ */ React4.createElement("div", {
721
- className: "w-full truncate text-right"
722
- }, label ?? cell.header.id),
723
- cell: (cell) => {
724
- const value = cell.getValue();
725
- const [text, setText] = useState4();
726
- const handleEdit = () => {
727
- if (onUpdate) {
728
- setText(value !== void 0 ? String(value) : "");
729
- }
730
- };
731
- const handleSave = () => {
732
- const value2 = text?.trim().length ? Number(text) : NaN;
733
- onUpdate?.(cell.row.original, cell.column.id, isNaN(value2) ? void 0 : value2);
734
- setText(void 0);
735
- };
736
- const handleCancel = () => {
737
- setText(void 0);
738
- };
739
- if (text !== void 0) {
740
- return /* @__PURE__ */ React4.createElement("div", {
741
- className: mx4("grow text-right font-mono", className)
742
- }, /* @__PURE__ */ React4.createElement(Input2.Root, null, /* @__PURE__ */ React4.createElement(Input2.TextInput, {
743
- autoFocus: true,
744
- value: text,
745
- classNames: "px-2",
746
- onBlur: handleSave,
747
- onChange: (event) => setText(event.target.value),
748
- onKeyDown: (event) => event.key === "Escape" && handleCancel() || event.key === "Enter" && handleSave()
749
- })));
759
+ header: (cell) => label ?? cell.header.id,
760
+ cell: NumberBuilderCell,
761
+ ...props,
762
+ meta: {
763
+ ...props.meta,
764
+ onUpdate,
765
+ digits,
766
+ header: {
767
+ classNames: "text-end",
768
+ ...props.meta?.header
769
+ },
770
+ cell: {
771
+ ...props.meta?.cell,
772
+ classNames
750
773
  }
751
- return /* @__PURE__ */ React4.createElement("div", {
752
- className: mx4('grow w-full text-right font-mono empty:after:content-["\xA0"]', className),
753
- onClick: handleEdit
754
- }, value?.toLocaleString(void 0, {
755
- minimumFractionDigits: digits ?? 0,
756
- maximumFractionDigits: digits ?? 2
757
- }));
758
774
  }
759
- });
775
+ };
760
776
  }
761
777
  /**
762
778
  * Date formats.
763
779
  */
764
780
  // TODO(burdon): Date picker (pluggable renderers?)
765
- date({ label, format: formatSpec, relative, className, ...props } = {}) {
766
- return defaults(props, {
781
+ date({ label, format: formatSpec, relative, classNames, ...props } = {}) {
782
+ return {
767
783
  size: 220,
768
784
  minSize: 100,
769
- header: (cell) => /* @__PURE__ */ React4.createElement("div", null, label ?? cell.header.id),
785
+ header: (cell) => label ?? cell.header.id,
770
786
  cell: (cell) => {
771
787
  const value = cell.getValue();
772
788
  try {
773
789
  const str = value ? formatSpec ? format(value, formatSpec) : relative ? formatDistanceToNow(value, {
774
790
  addSuffix: true
775
791
  }) : value.toISOString() : void 0;
776
- return /* @__PURE__ */ React4.createElement("div", {
777
- className: mx4("font-mono", className)
792
+ return /* @__PURE__ */ React8.createElement("div", {
793
+ className: mx5(textPadding, classNames)
778
794
  }, str);
779
795
  } catch (err) {
780
796
  console.log(value);
781
797
  return null;
782
798
  }
799
+ },
800
+ ...props,
801
+ meta: {
802
+ ...props.meta,
803
+ cell: {
804
+ ...props.meta?.cell,
805
+ classNames
806
+ }
783
807
  }
784
- });
808
+ };
785
809
  }
786
810
  /**
787
811
  * PublicKey with tooltip.
788
812
  */
789
- key({ label, tooltip, ...props } = {}) {
790
- return defaults(props, {
791
- size: 86,
792
- minSize: 86,
793
- header: (cell) => /* @__PURE__ */ React4.createElement("div", null, label ?? cell.header.id),
813
+ key({ label, tooltip, classNames, ...props } = {}) {
814
+ return {
815
+ size: 94,
816
+ minSize: 94,
817
+ header: (cell) => label ?? cell.header.id,
794
818
  cell: (cell) => {
795
819
  const value = cell.getValue();
796
820
  if (!value) {
797
821
  return;
798
822
  }
799
- const element = /* @__PURE__ */ React4.createElement("div", {
800
- className: "font-mono font-thin text-green-500 dark:text-green-300"
801
- }, value.truncate());
823
+ const element = value.truncate();
802
824
  if (!tooltip) {
803
825
  return element;
804
826
  }
805
- return /* @__PURE__ */ React4.createElement("div", {
806
- className: "group inline-flex gap-2 items-center"
807
- }, /* @__PURE__ */ React4.createElement(Tooltip.Provider, null, /* @__PURE__ */ React4.createElement(Tooltip.Root, null, /* @__PURE__ */ React4.createElement(Tooltip.Trigger, {
808
- asChild: true
809
- }, element), /* @__PURE__ */ React4.createElement(Tooltip.Content, {
827
+ return /* @__PURE__ */ React8.createElement(Tooltip.Provider, null, /* @__PURE__ */ React8.createElement(Tooltip.Root, null, /* @__PURE__ */ React8.createElement(Tooltip.Trigger, null, element), /* @__PURE__ */ React8.createElement(Tooltip.Portal, null, /* @__PURE__ */ React8.createElement(Tooltip.Content, {
810
828
  side: "right"
811
- }, /* @__PURE__ */ React4.createElement(Tooltip.Arrow, null), /* @__PURE__ */ React4.createElement(ClipboardText, {
829
+ }, /* @__PURE__ */ React8.createElement(Tooltip.Arrow, null), /* @__PURE__ */ React8.createElement(ClipboardText, {
812
830
  onClick: (ev) => {
813
831
  ev.stopPropagation();
814
832
  void navigator.clipboard.writeText(value.toHex());
815
833
  }
816
834
  })))));
835
+ },
836
+ ...props,
837
+ meta: {
838
+ ...props.meta,
839
+ cell: {
840
+ ...props.meta?.cell,
841
+ classNames: [
842
+ "font-mono",
843
+ textPadding,
844
+ classNames
845
+ ]
846
+ }
817
847
  }
818
- });
848
+ };
819
849
  }
820
850
  /**
821
- * Checkbox.
851
+ * Row selector
822
852
  */
823
- checkbox({ label, className, onUpdate, ...props } = {}) {
824
- return defaults(props, {
825
- size: 40,
826
- minSize: 40,
827
- header: (column) => /* @__PURE__ */ React4.createElement("div", {
828
- className: "flex grow justify-center"
829
- }, label ?? column.header.id),
830
- cell: (cell) => {
831
- const value = cell.getValue();
832
- return /* @__PURE__ */ React4.createElement("div", {
833
- className: "flex grow justify-center"
834
- }, /* @__PURE__ */ React4.createElement(Input2.Root, null, /* @__PURE__ */ React4.createElement(Input2.Checkbox, {
835
- onClick: (event) => event.stopPropagation(),
836
- classNames: className,
837
- disabled: !onUpdate,
838
- checked: !!value,
839
- onCheckedChange: (value2) => {
840
- onUpdate?.(cell.row.original, cell.column.id, !!value2);
841
- }
842
- })));
853
+ selectRow({ classNames, onUpdate, id = "selectRow" } = {}) {
854
+ return {
855
+ id,
856
+ size: 32,
857
+ minSize: 32,
858
+ maxSize: 32,
859
+ meta: {
860
+ onUpdate,
861
+ cell: {
862
+ classNames
863
+ }
864
+ },
865
+ header: ({ table }) => {
866
+ const { rowsSelectable } = useTableContext("HELPER_SELECT_ROW_HEADER_CELL");
867
+ const checked = table.getIsSomeRowsSelected() ? "indeterminate" : table.getIsAllRowsSelected();
868
+ return rowsSelectable === "multi" ? /* @__PURE__ */ React8.createElement(Input2.Root, null, /* @__PURE__ */ React8.createElement(Input2.Checkbox, {
869
+ size: 4,
870
+ classNames: [
871
+ "mli-auto",
872
+ classNames
873
+ ],
874
+ checked,
875
+ onCheckedChange: () => table.toggleAllRowsSelected()
876
+ })) : null;
877
+ },
878
+ cell: RowSelectorBuilderCell
879
+ };
880
+ }
881
+ /**
882
+ * Switch
883
+ */
884
+ switch({ label, classNames, onUpdate, ...props } = {}) {
885
+ return {
886
+ size: 50,
887
+ minSize: 50,
888
+ header: (column) => label ?? column.header.id,
889
+ cell: SwitchBuilderCell,
890
+ ...props,
891
+ meta: {
892
+ ...props.meta,
893
+ onUpdate,
894
+ cell: {
895
+ ...props.meta?.cell,
896
+ classNames: [
897
+ textPadding,
898
+ classNames
899
+ ]
900
+ }
843
901
  }
844
- });
902
+ };
845
903
  }
846
904
  /**
847
905
  * Icon based on boolean value.
@@ -849,41 +907,56 @@ var ColumnBuilder = class {
849
907
  icon({ label, size, on, off, ...props } = {}) {
850
908
  const IconOn = on?.Icon ?? Check2;
851
909
  const IconOff = off?.Icon ?? X2;
852
- return defaults(props, {
910
+ return {
853
911
  size: size ?? 32,
854
- header: (column) => /* @__PURE__ */ React4.createElement("div", {
912
+ header: (column) => /* @__PURE__ */ React8.createElement("div", {
855
913
  className: "justify-center"
856
914
  }, label ?? column.header.id),
857
915
  cell: (cell) => {
858
916
  const value = cell.getValue();
859
917
  if (value) {
860
- return /* @__PURE__ */ React4.createElement("div", {
861
- className: "flex grow justify-center"
862
- }, /* @__PURE__ */ React4.createElement(IconOn, {
863
- className: mx4(getSize3(6), on?.className ?? "text-green-700")
864
- }));
918
+ return /* @__PURE__ */ React8.createElement(IconOn, {
919
+ className: mx5("block mli-auto", getSize3(6), on?.className ?? "text-green-700")
920
+ });
865
921
  } else if (value === false) {
866
- return /* @__PURE__ */ React4.createElement("div", {
867
- className: "flex grow justify-center"
868
- }, /* @__PURE__ */ React4.createElement(IconOff, {
869
- className: mx4(getSize3(6), off?.className ?? "text-red-700")
870
- }));
922
+ return /* @__PURE__ */ React8.createElement(IconOff, {
923
+ className: mx5("block mli-auto", getSize3(6), off?.className ?? "text-red-700")
924
+ });
871
925
  } else {
872
926
  return null;
873
927
  }
874
- }
875
- });
928
+ },
929
+ ...props
930
+ };
876
931
  }
877
932
  };
878
933
  export {
934
+ CellCombobox,
879
935
  ColumnBuilder,
880
936
  ColumnMenu,
881
937
  ColumnPanel,
882
- ComboboxCell,
883
938
  Table,
884
- columnTypes,
885
939
  createColumnBuilder,
886
- defaultTableSlots,
887
- updateSelection
940
+ currentRow,
941
+ flushPadding,
942
+ gridCellFocusRing,
943
+ groupTh,
944
+ headPadding,
945
+ selectedRow,
946
+ tableRoot,
947
+ tbodyRoot,
948
+ tbodyTr,
949
+ tdContent,
950
+ tdRoot,
951
+ textPadding,
952
+ tfootRoot,
953
+ tfootTh,
954
+ tfootTr,
955
+ theadResizeRoot,
956
+ theadResizeThumb,
957
+ theadRoot,
958
+ theadTh,
959
+ theadTr,
960
+ translations_default as translations
888
961
  };
889
962
  //# sourceMappingURL=index.mjs.map