@dxos/react-ui-table 0.3.8-next.f4e0086 → 0.3.9-main.27aafd0

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