@dxos/react-ui-table 0.3.8-next.598e316 → 0.3.8-next.9a32253

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