@dxos/react-ui-table 0.3.7 → 0.3.8-main.0694b57

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 (56) hide show
  1. package/dist/lib/browser/index.mjs +546 -475
  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/Cell/CellCombobox.d.ts +12 -0
  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 -0
  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/translations.d.ts +10 -0
  33. package/dist/types/src/translations.d.ts.map +1 -0
  34. package/dist/types/src/types.d.ts +13 -10
  35. package/dist/types/src/types.d.ts.map +1 -1
  36. package/package.json +12 -10
  37. package/src/components/Cell/Cell.tsx +43 -0
  38. package/src/components/Cell/CellCombobox.tsx +97 -0
  39. package/src/components/Cell/index.ts +5 -0
  40. package/src/components/{Table.stories.tsx → Table/Table.stories.tsx} +51 -50
  41. package/src/components/Table/Table.tsx +179 -0
  42. package/src/components/Table/TableBody.tsx +69 -0
  43. package/src/components/Table/TableContext.tsx +28 -0
  44. package/src/components/Table/TableFooter.tsx +42 -0
  45. package/src/components/Table/TableHead.tsx +79 -0
  46. package/src/components/Table/index.ts +6 -0
  47. package/src/components/Table/props.ts +53 -0
  48. package/src/components/index.ts +1 -0
  49. package/src/helpers.tsx +250 -282
  50. package/src/theme.ts +95 -63
  51. package/src/translations.ts +15 -0
  52. package/src/types.ts +15 -10
  53. package/dist/types/src/components/Table.d.ts +0 -29
  54. package/dist/types/src/components/Table.d.ts.map +0 -1
  55. package/dist/types/src/components/Table.stories.d.ts.map +0 -1
  56. 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);
221
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);
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,277 +408,110 @@ 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/helpers.tsx
524
- import { useFocusFinders } from "@fluentui/react-tabster";
525
- import { CaretDown as CaretDown2, Check as Check2, ClipboardText, X as X2 } from "@phosphor-icons/react";
526
- import { createColumnHelper } from "@tanstack/react-table";
527
- import format from "date-fns/format";
528
- import formatDistanceToNow from "date-fns/formatDistanceToNow";
529
- import defaultsDeep from "lodash.defaultsdeep";
530
- import React3, { useCallback, useRef as useRef3, useState as useState3 } from "react";
531
- import { Button as Button2, Input as Input2, Popover as Popover2, Tooltip } from "@dxos/react-ui";
532
- import { SearchList } from "@dxos/react-ui-searchlist";
533
- import { descriptionText, getSize as getSize2, mx as mx3 } from "@dxos/react-ui-theme";
534
- import { stripUndefinedValues } from "@dxos/util";
535
- var findNextFocusable = (findFirstFocusable, container) => {
536
- const next = findFirstFocusable(container);
537
- if (next) {
538
- if (next?.tagName === "INPUT") {
539
- return next;
540
- }
541
- if (container.nextElementSibling) {
542
- return findNextFocusable(findFirstFocusable, container.nextSibling);
487
+ // packages/ui/react-ui-table/src/components/Cell/CellCombobox.tsx
488
+ import { CaretDown as CaretDown2 } from "@phosphor-icons/react";
489
+ import React7, { useCallback as useCallback2, useState as useState3 } from "react";
490
+ import { Popover as Popover2, useTranslation } from "@dxos/react-ui";
491
+ import { PopoverCombobox } from "@dxos/react-ui-searchlist";
492
+ import { descriptionText, getSize as getSize2, mx as mx4, staticPlaceholderText } from "@dxos/react-ui-theme";
493
+
494
+ // packages/ui/react-ui-table/src/translations.ts
495
+ var translationKey = "table";
496
+ var translations_default = [
497
+ {
498
+ "en-US": {
499
+ [translationKey]: {
500
+ "no selection label": "\xA0"
501
+ }
543
502
  }
544
503
  }
545
- };
546
- var createColumnBuilder = () => ({
547
- helper: createColumnHelper(),
548
- builder: new ColumnBuilder()
549
- });
550
- var defaults = (options, ...sources) => {
551
- return stripUndefinedValues(defaultsDeep({}, options, ...sources));
552
- };
504
+ ];
505
+
506
+ // packages/ui/react-ui-table/src/components/Cell/CellCombobox.tsx
553
507
  var initialItems = (model, value) => value ? {
554
508
  [model.getId(value)]: {
555
509
  label: model.getText(value),
556
510
  data: value
557
511
  }
558
512
  } : {};
559
- var CellSelector = ({ model, value, onValueChange }) => {
513
+ var CellCombobox = ({ model, value, onValueChange }) => {
514
+ const { t } = useTranslation(translationKey);
560
515
  const [items, setItems] = useState3(initialItems(model, value));
561
516
  const [loading, setLoading] = useState3(false);
562
517
  const [searchInputValue, setSearchInputValue] = useState3("");
@@ -573,118 +528,214 @@ var CellSelector = ({ model, value, onValueChange }) => {
573
528
  }, initialItems(model, value)));
574
529
  setLoading(false);
575
530
  };
576
- const handleValueChange = useCallback((id) => {
531
+ const handleValueChange = useCallback2((id) => {
577
532
  onValueChange(items[id]?.data);
578
533
  }, [
579
534
  items,
580
535
  onValueChange
581
536
  ]);
582
- return /* @__PURE__ */ React3.createElement(Popover2.Root, null, /* @__PURE__ */ React3.createElement(Popover2.Trigger, {
583
- asChild: true
584
- }, /* @__PURE__ */ React3.createElement(Button2, {
537
+ return /* @__PURE__ */ React7.createElement(PopoverCombobox.Root, {
538
+ placeholder: t("no selection label")
539
+ }, /* @__PURE__ */ React7.createElement(PopoverCombobox.Trigger, {
585
540
  variant: "ghost",
586
- classNames: "is-full font-normal"
587
- }, /* @__PURE__ */ React3.createElement("span", {
588
- className: "flex-1 text-start truncate"
589
- }, value ? model.getText(value) : "\xA0"), /* @__PURE__ */ React3.createElement(CaretDown2, null))), /* @__PURE__ */ React3.createElement(Popover2.Portal, null, /* @__PURE__ */ React3.createElement(Popover2.Content, {
590
- side: "bottom",
591
- collisionPadding: 48,
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, {
545
+ weight: "bold",
546
+ className: getSize2(3)
547
+ })) : null), /* @__PURE__ */ React7.createElement(PopoverCombobox.Content, {
548
+ shouldFilter: false,
592
549
  classNames: "is-[--radix-popover-trigger-width]"
593
- }, /* @__PURE__ */ React3.createElement(SearchList.Root, {
594
- shouldFilter: false
595
- }, /* @__PURE__ */ React3.createElement(SearchList.Input, {
550
+ }, /* @__PURE__ */ React7.createElement(PopoverCombobox.Input, {
596
551
  value: searchInputValue,
597
552
  onValueChange: handleSearchInputValueChange
598
- }), /* @__PURE__ */ React3.createElement(Popover2.Viewport, null, /* @__PURE__ */ React3.createElement(SearchList.Content, null, searchInputValue.length < 1 ? /* @__PURE__ */ React3.createElement("p", {
599
- className: mx3("text-center", descriptionText)
600
- }, "Start typing to search") : loading ? /* @__PURE__ */ React3.createElement("p", {
601
- className: mx3("text-center", descriptionText)
602
- }, "Searching\u2026") : /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement(SearchList.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, {
603
558
  classNames: [
604
559
  "text-center",
605
560
  descriptionText
606
561
  ]
607
- }, "No results"), Object.entries(items).map(([id, { label }]) => /* @__PURE__ */ React3.createElement(SearchList.Item, {
562
+ }, "No results"), Object.entries(items).map(([id, { label }]) => /* @__PURE__ */ React7.createElement(PopoverCombobox.Item, {
608
563
  key: id,
609
564
  value: id,
610
565
  onSelect: handleValueChange,
611
566
  classNames: "truncate"
612
- }, label)))))), /* @__PURE__ */ React3.createElement(Popover2.Arrow, null))));
567
+ }, label)))), /* @__PURE__ */ React7.createElement(Popover2.Arrow, null)));
568
+ };
569
+
570
+ // packages/ui/react-ui-table/src/helpers.tsx
571
+ import { Check as Check2, ClipboardText, X as X2 } from "@phosphor-icons/react";
572
+ import { createColumnHelper } from "@tanstack/react-table";
573
+ import format from "date-fns/format";
574
+ import formatDistanceToNow from "date-fns/formatDistanceToNow";
575
+ import defaultsDeep from "lodash.defaultsdeep";
576
+ import React8, { useRef as useRef2, useState as useState4 } from "react";
577
+ import { Input as Input2, Tooltip } from "@dxos/react-ui";
578
+ import { getSize as getSize3, mx as mx5 } from "@dxos/react-ui-theme";
579
+ import { stripUndefinedValues } from "@dxos/util";
580
+ var createColumnBuilder = () => ({
581
+ helper: createColumnHelper(),
582
+ builder: new ColumnBuilder()
583
+ });
584
+ var defaults = (options, ...sources) => {
585
+ return stripUndefinedValues(defaultsDeep({}, options, ...sources));
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
+ }));
613
693
  };
614
694
  var ColumnBuilder = class {
615
695
  /**
616
- * Select value
696
+ * Combobox value
617
697
  */
618
- select({ label, className, model, onUpdate, ...props }) {
698
+ combobox({ label, classNames, model, onUpdate, ...props }) {
619
699
  return defaults(props, {
620
700
  minSize: 100,
701
+ meta: {
702
+ model,
703
+ onUpdate,
704
+ cell: {
705
+ classNames
706
+ }
707
+ },
621
708
  header: (column) => {
622
- return /* @__PURE__ */ React3.createElement("div", {
623
- className: "truncate"
624
- }, label ?? column.header.id);
709
+ return label ?? column.header.id;
625
710
  },
626
- cell: model && onUpdate ? (cell) => /* @__PURE__ */ React3.createElement(CellSelector, {
627
- model,
628
- value: cell.getValue(),
629
- onValueChange: (value) => onUpdate?.(cell.row.original, cell.column.id, value)
630
- }) : (cell) => {
631
- const value = cell.getValue();
632
- return /* @__PURE__ */ React3.createElement("div", {
633
- className: mx3("truncate", className)
634
- }, 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) : "");
635
716
  }
636
717
  });
637
718
  }
638
719
  /**
639
720
  * String formats.
640
721
  */
641
- string({ label, className, onUpdate, ...props } = {}) {
722
+ string({ label, classNames, onUpdate, ...props } = {}) {
642
723
  return defaults(props, {
643
724
  minSize: 100,
725
+ meta: {
726
+ onUpdate,
727
+ cell: {
728
+ classNames
729
+ }
730
+ },
644
731
  // TODO(burdon): Default.
645
732
  header: (column) => {
646
- return /* @__PURE__ */ React3.createElement("div", {
647
- className: "truncate"
648
- }, label ?? column.header.id);
733
+ return label ?? column.header.id;
649
734
  },
650
- cell: onUpdate ? (cell) => {
651
- const initialValue = cell.getValue();
652
- const [value, setValue] = useState3(initialValue);
653
- const inputRef = useRef3(null);
654
- const { findFirstFocusable } = useFocusFinders();
655
- const handleSave = (focusNext = false) => {
656
- if (value === initialValue) {
657
- return;
658
- }
659
- onUpdate?.(cell.row.original, cell.column.id, value);
660
- if (focusNext) {
661
- const rowElement = inputRef.current?.parentElement?.parentElement;
662
- setTimeout(() => {
663
- const next = findNextFocusable(findFirstFocusable, rowElement);
664
- next?.focus();
665
- });
666
- }
667
- };
668
- const handleCancel = () => {
669
- setValue(initialValue);
670
- };
671
- const rows = cell.table.getRowModel().flatRows;
672
- const columns = cell.table.getVisibleFlatColumns();
673
- const placeholder = cell.row.index === rows.length - 1 && columns[0].id === cell.column.id;
674
- return /* @__PURE__ */ React3.createElement(Input2.Root, null, /* @__PURE__ */ React3.createElement(Input2.TextInput, {
675
- ref: inputRef,
676
- variant: "subdued",
677
- placeholder: placeholder ? "Add row..." : void 0,
678
- classNames: "px-2",
679
- value: value ?? "",
680
- onBlur: () => handleSave(false),
681
- onChange: (event) => setValue(event.target.value),
682
- onKeyDown: (event) => event.key === "Enter" && handleSave(true) || event.key === "Escape" && handleCancel()
683
- }));
684
- } : (cell) => {
735
+ cell: onUpdate ? StringBuilderCell : (cell) => {
685
736
  const value = cell.getValue();
686
- return /* @__PURE__ */ React3.createElement("div", {
687
- className: mx3("truncate", className)
737
+ return /* @__PURE__ */ React8.createElement("div", {
738
+ className: mx5("truncate", textPadding, classNames)
688
739
  }, value);
689
740
  }
690
741
  });
@@ -692,68 +743,46 @@ var ColumnBuilder = class {
692
743
  /**
693
744
  * Number formats.
694
745
  */
695
- number({ label, minSize, className, digits, onUpdate, ...props } = {}) {
746
+ number({ label, minSize, classNames, digits, onUpdate, ...props } = {}) {
696
747
  return defaults(props, {
697
748
  size: 100,
698
749
  minSize: 100,
699
- header: (cell) => /* @__PURE__ */ React3.createElement("div", {
700
- className: "w-full truncate text-right"
701
- }, label ?? cell.header.id),
702
- cell: (cell) => {
703
- const value = cell.getValue();
704
- const [text, setText] = useState3();
705
- const handleEdit = () => {
706
- if (onUpdate) {
707
- setText(value !== void 0 ? String(value) : "");
708
- }
709
- };
710
- const handleSave = () => {
711
- const value2 = text?.trim().length ? Number(text) : NaN;
712
- onUpdate?.(cell.row.original, cell.column.id, isNaN(value2) ? void 0 : value2);
713
- setText(void 0);
714
- };
715
- const handleCancel = () => {
716
- setText(void 0);
717
- };
718
- if (text !== void 0) {
719
- return /* @__PURE__ */ React3.createElement("div", {
720
- className: mx3("grow text-right font-mono", className)
721
- }, /* @__PURE__ */ React3.createElement(Input2.Root, null, /* @__PURE__ */ React3.createElement(Input2.TextInput, {
722
- autoFocus: true,
723
- value: text,
724
- classNames: "px-2",
725
- onBlur: handleSave,
726
- onChange: (event) => setText(event.target.value),
727
- onKeyDown: (event) => event.key === "Escape" && handleCancel() || event.key === "Enter" && handleSave()
728
- })));
750
+ meta: {
751
+ onUpdate,
752
+ digits,
753
+ header: {
754
+ classNames: "text-end"
755
+ },
756
+ cell: {
757
+ classNames
729
758
  }
730
- return /* @__PURE__ */ React3.createElement("div", {
731
- className: mx3('grow w-full text-right font-mono empty:after:content-["\xA0"]', className),
732
- onClick: handleEdit
733
- }, value?.toLocaleString(void 0, {
734
- minimumFractionDigits: digits ?? 0,
735
- maximumFractionDigits: digits ?? 2
736
- }));
737
- }
759
+ },
760
+ header: (cell) => label ?? cell.header.id,
761
+ cell: NumberBuilderCell
738
762
  });
739
763
  }
740
764
  /**
741
765
  * Date formats.
742
766
  */
743
767
  // TODO(burdon): Date picker (pluggable renderers?)
744
- date({ label, format: formatSpec, relative, className, ...props } = {}) {
768
+ date({ label, format: formatSpec, relative, classNames, ...props } = {}) {
745
769
  return defaults(props, {
746
770
  size: 220,
747
771
  minSize: 100,
748
- header: (cell) => /* @__PURE__ */ React3.createElement("div", null, label ?? cell.header.id),
772
+ meta: {
773
+ cell: {
774
+ classNames
775
+ }
776
+ },
777
+ header: (cell) => label ?? cell.header.id,
749
778
  cell: (cell) => {
750
779
  const value = cell.getValue();
751
780
  try {
752
781
  const str = value ? formatSpec ? format(value, formatSpec) : relative ? formatDistanceToNow(value, {
753
782
  addSuffix: true
754
783
  }) : value.toISOString() : void 0;
755
- return /* @__PURE__ */ React3.createElement("div", {
756
- className: mx3("font-mono", className)
784
+ return /* @__PURE__ */ React8.createElement("div", {
785
+ className: mx5(textPadding, classNames)
757
786
  }, str);
758
787
  } catch (err) {
759
788
  console.log(value);
@@ -767,59 +796,86 @@ var ColumnBuilder = class {
767
796
  */
768
797
  key({ label, tooltip, ...props } = {}) {
769
798
  return defaults(props, {
770
- size: 86,
771
- minSize: 86,
772
- header: (cell) => /* @__PURE__ */ React3.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,
773
810
  cell: (cell) => {
774
811
  const value = cell.getValue();
775
812
  if (!value) {
776
813
  return;
777
814
  }
778
- const element = /* @__PURE__ */ React3.createElement("div", {
779
- className: "font-mono font-thin text-green-500 dark:text-green-300"
780
- }, value.truncate());
815
+ const element = value.truncate();
781
816
  if (!tooltip) {
782
817
  return element;
783
818
  }
784
- return /* @__PURE__ */ React3.createElement("div", {
785
- className: "group inline-flex gap-2 items-center"
786
- }, /* @__PURE__ */ React3.createElement(Tooltip.Provider, null, /* @__PURE__ */ React3.createElement(Tooltip.Root, null, /* @__PURE__ */ React3.createElement(Tooltip.Trigger, {
787
- asChild: true
788
- }, element), /* @__PURE__ */ React3.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, {
789
820
  side: "right"
790
- }, /* @__PURE__ */ React3.createElement(Tooltip.Arrow, null), /* @__PURE__ */ React3.createElement(ClipboardText, {
821
+ }, /* @__PURE__ */ React8.createElement(Tooltip.Arrow, null), /* @__PURE__ */ React8.createElement(ClipboardText, {
791
822
  onClick: (ev) => {
792
823
  ev.stopPropagation();
793
824
  void navigator.clipboard.writeText(value.toHex());
794
825
  }
795
- })))));
826
+ }))));
796
827
  }
797
828
  });
798
829
  }
799
830
  /**
800
- * 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
801
863
  */
802
- checkbox({ label, className, onUpdate, ...props } = {}) {
864
+ switch({ label, classNames, onUpdate, ...props } = {}) {
803
865
  return defaults(props, {
804
- size: 40,
805
- minSize: 40,
806
- header: (column) => /* @__PURE__ */ React3.createElement("div", {
807
- className: "flex grow justify-center"
808
- }, label ?? column.header.id),
809
- cell: (cell) => {
810
- const value = cell.getValue();
811
- return /* @__PURE__ */ React3.createElement("div", {
812
- className: "flex grow justify-center"
813
- }, /* @__PURE__ */ React3.createElement(Input2.Root, null, /* @__PURE__ */ React3.createElement(Input2.Checkbox, {
814
- onClick: (event) => event.stopPropagation(),
815
- classNames: className,
816
- disabled: !onUpdate,
817
- checked: !!value,
818
- onCheckedChange: (value2) => {
819
- onUpdate?.(cell.row.original, cell.column.id, !!value2);
820
- }
821
- })));
822
- }
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
823
879
  });
824
880
  }
825
881
  /**
@@ -830,23 +886,19 @@ var ColumnBuilder = class {
830
886
  const IconOff = off?.Icon ?? X2;
831
887
  return defaults(props, {
832
888
  size: size ?? 32,
833
- header: (column) => /* @__PURE__ */ React3.createElement("div", {
889
+ header: (column) => /* @__PURE__ */ React8.createElement("div", {
834
890
  className: "justify-center"
835
891
  }, label ?? column.header.id),
836
892
  cell: (cell) => {
837
893
  const value = cell.getValue();
838
894
  if (value) {
839
- return /* @__PURE__ */ React3.createElement("div", {
840
- className: "flex grow justify-center"
841
- }, /* @__PURE__ */ React3.createElement(IconOn, {
842
- className: mx3(getSize2(6), on?.className ?? "text-green-700")
843
- }));
895
+ return /* @__PURE__ */ React8.createElement(IconOn, {
896
+ className: mx5("block mli-auto", getSize3(6), on?.className ?? "text-green-700")
897
+ });
844
898
  } else if (value === false) {
845
- return /* @__PURE__ */ React3.createElement("div", {
846
- className: "flex grow justify-center"
847
- }, /* @__PURE__ */ React3.createElement(IconOff, {
848
- className: mx3(getSize2(6), off?.className ?? "text-red-700")
849
- }));
899
+ return /* @__PURE__ */ React8.createElement(IconOff, {
900
+ className: mx5("block mli-auto", getSize3(6), off?.className ?? "text-red-700")
901
+ });
850
902
  } else {
851
903
  return null;
852
904
  }
@@ -855,13 +907,32 @@ var ColumnBuilder = class {
855
907
  }
856
908
  };
857
909
  export {
910
+ CellCombobox,
858
911
  ColumnBuilder,
859
912
  ColumnMenu,
860
913
  ColumnPanel,
861
914
  Table,
862
915
  columnTypes,
863
916
  createColumnBuilder,
864
- defaultTableSlots,
865
- 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
866
937
  };
867
938
  //# sourceMappingURL=index.mjs.map