@dxos/react-ui-table 0.4.10-main.3e35a2f → 0.4.10-main.3f5e2d2

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 (49) hide show
  1. package/dist/lib/browser/index.mjs +303 -176
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/types/src/components/ColumnMenu/ColumnMenu.d.ts.map +1 -1
  5. package/dist/types/src/components/ColumnMenu/ColumnMenu.stories.d.ts +5 -1
  6. package/dist/types/src/components/ColumnMenu/ColumnMenu.stories.d.ts.map +1 -1
  7. package/dist/types/src/components/ColumnMenu/ColumnSettingsForm.d.ts.map +1 -1
  8. package/dist/types/src/components/Table/Table.d.ts +17 -4
  9. package/dist/types/src/components/Table/Table.d.ts.map +1 -1
  10. package/dist/types/src/components/Table/Table.stories.d.ts +32 -1
  11. package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
  12. package/dist/types/src/components/Table/TableBody.d.ts.map +1 -1
  13. package/dist/types/src/components/Table/TableRootContext.d.ts +17 -0
  14. package/dist/types/src/components/Table/TableRootContext.d.ts.map +1 -0
  15. package/dist/types/src/components/Table/index.d.ts +1 -0
  16. package/dist/types/src/components/Table/index.d.ts.map +1 -1
  17. package/dist/types/src/components/Table/props.d.ts +2 -3
  18. package/dist/types/src/components/Table/props.d.ts.map +1 -1
  19. package/dist/types/src/helpers.d.ts.map +1 -1
  20. package/dist/types/src/hooks/index.d.ts +2 -0
  21. package/dist/types/src/hooks/index.d.ts.map +1 -1
  22. package/dist/types/src/hooks/useColumnSizing.d.ts +12 -0
  23. package/dist/types/src/hooks/useColumnSizing.d.ts.map +1 -0
  24. package/dist/types/src/hooks/usePinLastRow.d.ts +1 -1
  25. package/dist/types/src/hooks/usePinLastRow.d.ts.map +1 -1
  26. package/dist/types/src/hooks/useRowSelection.d.ts +7 -0
  27. package/dist/types/src/hooks/useRowSelection.d.ts.map +1 -0
  28. package/dist/types/src/schema/SchemaToTable.stories.d.ts.map +1 -1
  29. package/dist/types/src/schema/schemaToColumns.d.ts +1 -1
  30. package/dist/types/src/schema/schemaToColumns.d.ts.map +1 -1
  31. package/package.json +20 -17
  32. package/src/components/ColumnMenu/ColumnMenu.stories.tsx +30 -0
  33. package/src/components/ColumnMenu/ColumnMenu.tsx +34 -32
  34. package/src/components/ColumnMenu/ColumnSettingsForm.tsx +69 -63
  35. package/src/components/Table/Table.stories.tsx +244 -74
  36. package/src/components/Table/Table.tsx +81 -97
  37. package/src/components/Table/TableBody.tsx +6 -1
  38. package/src/components/Table/TableRootContext.ts +39 -0
  39. package/src/components/Table/index.ts +1 -0
  40. package/src/components/Table/props.ts +17 -24
  41. package/src/helpers.tsx +9 -7
  42. package/src/hooks/index.ts +2 -0
  43. package/src/hooks/useColumnSizing.ts +45 -0
  44. package/src/hooks/usePinLastRow.ts +47 -26
  45. package/src/hooks/useRowSelection.ts +39 -0
  46. package/src/schema/SchemaToTable.stories.tsx +16 -17
  47. package/src/schema/schema.test.ts +1 -1
  48. package/src/schema/schema.ts +1 -1
  49. package/src/schema/schemaToColumns.ts +2 -1
@@ -92,7 +92,7 @@ var CellCombobox = ({ model, value, onValueChange }) => {
92
92
 
93
93
  // packages/ui/react-ui-table/src/components/ColumnMenu/ColumnMenu.tsx
94
94
  import { X as X2, GearSix, CaretDown as CaretDown2, ArrowDown, ArrowUp } from "@phosphor-icons/react";
95
- import React3, { useRef as useRef2, useState as useState5, useCallback as useCallback2 } from "react";
95
+ import React3, { useRef as useRef2, useState as useState6, useCallback as useCallback5 } from "react";
96
96
  import { Button as Button2, DensityProvider, Popover as Popover2, DropdownMenu } from "@dxos/react-ui";
97
97
  import { getSize as getSize3, mx as mx3 } from "@dxos/react-ui-theme";
98
98
 
@@ -107,19 +107,21 @@ var Section = ({ children, className }) => /* @__PURE__ */ React2.createElement(
107
107
  className: mx2("p-2", className)
108
108
  }, children);
109
109
  var ColumnSettingsForm = ({ column, tableDef, tableDefs, onUpdate, onDelete, onClose }) => {
110
- const [prop, setProp] = useState2(column.id);
111
- const [refTable, setRefTable] = useState2(column.refTable);
112
- const [refProp, setRefProp] = useState2(column.refProp);
113
- const [type, setType] = useState2(String(column.type));
114
- const [label, setLabel] = useState2(column.label);
115
- const [digits, setDigits] = useState2(String(column.digits ?? "0"));
110
+ const [formState, setFormState] = useState2({
111
+ prop: column.id,
112
+ refTable: column.refTable,
113
+ refProp: column.refProp,
114
+ type: String(column.type),
115
+ label: column.label,
116
+ digits: String(column.digits ?? "0")
117
+ });
116
118
  const propRef = useRef(null);
117
119
  const { t } = useTranslation2(translationKey);
118
120
  const handleCancel = () => {
119
- setProp(column.id);
120
121
  onClose?.();
121
122
  };
122
123
  const handleSave = () => {
124
+ const { prop, refTable, refProp, type, label, digits } = formState;
123
125
  if (!prop.length || !prop.match(/^[a-zA-Z_].+/i)) {
124
126
  propRef.current?.focus();
125
127
  return;
@@ -131,7 +133,7 @@ var ColumnSettingsForm = ({ column, tableDef, tableDefs, onUpdate, onDelete, onC
131
133
  return;
132
134
  }
133
135
  }
134
- onUpdate?.(prop, {
136
+ onUpdate?.(column.id, {
135
137
  id: prop,
136
138
  prop,
137
139
  label,
@@ -148,22 +150,30 @@ var ColumnSettingsForm = ({ column, tableDef, tableDefs, onUpdate, onDelete, onC
148
150
  classNames: "mbe-1"
149
151
  }, t("column label label")), /* @__PURE__ */ React2.createElement(Input.TextInput, {
150
152
  placeholder: "Column label",
151
- value: label ?? "",
152
- onChange: (event) => setLabel(event.target.value),
153
+ value: formState.label ?? "",
154
+ onChange: (event) => setFormState((prevState) => ({
155
+ ...prevState,
156
+ label: event.target.value
157
+ })),
153
158
  autoFocus: true
154
159
  })), /* @__PURE__ */ React2.createElement(Input.Root, null, /* @__PURE__ */ React2.createElement(Input.Label, {
155
160
  classNames: "mbe-1 mbs-3"
156
161
  }, t("property key label")), /* @__PURE__ */ React2.createElement(Input.TextInput, {
157
162
  ref: propRef,
158
163
  placeholder: "Property key",
159
- // TODO(burdon): Provide hooks for value normalization, ENTER, ESC, etc.
160
- value: prop ?? "",
161
- onChange: (event) => setProp(event.target.value.replace(/[^\w_]/g, ""))
164
+ value: formState.prop ?? "",
165
+ onChange: (event) => setFormState((prevState) => ({
166
+ ...prevState,
167
+ prop: event.target.value.replace(/[^\w_]/g, "")
168
+ }))
162
169
  })), /* @__PURE__ */ React2.createElement(Input.Root, null, /* @__PURE__ */ React2.createElement(Input.Label, {
163
170
  classNames: "mbe-1 mbs-3"
164
171
  }, t("column type label")), /* @__PURE__ */ React2.createElement(Select.Root, {
165
- value: type,
166
- onValueChange: setType
172
+ value: formState.type,
173
+ onValueChange: (value) => setFormState((prevState) => ({
174
+ ...prevState,
175
+ type: value
176
+ }))
167
177
  }, /* @__PURE__ */ React2.createElement(Select.TriggerButton, {
168
178
  placeholder: "Type",
169
179
  classNames: "is-full"
@@ -172,37 +182,46 @@ var ColumnSettingsForm = ({ column, tableDef, tableDefs, onUpdate, onDelete, onC
172
182
  "boolean",
173
183
  "string",
174
184
  "ref"
175
- ].map((type2) => /* @__PURE__ */ React2.createElement(Select.Option, {
176
- key: type2,
177
- value: type2
178
- }, t(`${type2} column type label`))))))))), type === "number" && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Section, null, /* @__PURE__ */ React2.createElement(Input.Root, null, /* @__PURE__ */ React2.createElement(Input.Label, {
185
+ ].map((type) => /* @__PURE__ */ React2.createElement(Select.Option, {
186
+ key: type,
187
+ value: type
188
+ }, t(`${type} column type label`))))))))), formState.type === "number" && /* @__PURE__ */ React2.createElement(Section, null, /* @__PURE__ */ React2.createElement(Input.Root, null, /* @__PURE__ */ React2.createElement(Input.Label, {
179
189
  classNames: "mbe-1 mbs-3"
180
190
  }, t("digits label")), /* @__PURE__ */ React2.createElement(Input.TextInput, {
181
- value: digits ?? "",
182
- onChange: (event) => setDigits(event.target.value)
183
- })))), type === "ref" && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Section, null, /* @__PURE__ */ React2.createElement(Input.Root, null, /* @__PURE__ */ React2.createElement(Input.Label, {
191
+ value: formState.digits ?? "",
192
+ onChange: (event) => setFormState((prevState) => ({
193
+ ...prevState,
194
+ digits: event.target.value
195
+ }))
196
+ }))), formState.type === "ref" && /* @__PURE__ */ React2.createElement(Section, null, /* @__PURE__ */ React2.createElement(Input.Root, null, /* @__PURE__ */ React2.createElement(Input.Label, {
184
197
  classNames: "mbe-1 mbs-3"
185
198
  }, "Table"), /* @__PURE__ */ React2.createElement(Select.Root, {
186
- value: refTable,
187
- onValueChange: setRefTable
199
+ value: formState.refTable,
200
+ onValueChange: (value) => setFormState((prevState) => ({
201
+ ...prevState,
202
+ refTable: value
203
+ }))
188
204
  }, /* @__PURE__ */ React2.createElement(Select.TriggerButton, {
189
205
  placeholder: "Table",
190
206
  classNames: "is-full"
191
207
  }), /* @__PURE__ */ React2.createElement(Select.Portal, null, /* @__PURE__ */ React2.createElement(Select.Content, null, /* @__PURE__ */ React2.createElement(Select.Viewport, null, tableDefs.filter((t2) => t2.id !== tableDef.id).map(({ id, name }) => /* @__PURE__ */ React2.createElement(Select.Option, {
192
208
  key: id,
193
209
  value: id
194
- }, name ?? id))))))), refTable && /* @__PURE__ */ React2.createElement(Input.Root, null, /* @__PURE__ */ React2.createElement(Input.Label, {
210
+ }, name ?? id))))))), formState.refTable && /* @__PURE__ */ React2.createElement(Input.Root, null, /* @__PURE__ */ React2.createElement(Input.Label, {
195
211
  classNames: "mbe-1 mbs-3"
196
212
  }, "Table property"), /* @__PURE__ */ React2.createElement(Select.Root, {
197
- value: refProp,
198
- onValueChange: setRefProp
213
+ value: formState.refProp,
214
+ onValueChange: (value) => setFormState((prevState) => ({
215
+ ...prevState,
216
+ refProp: value
217
+ }))
199
218
  }, /* @__PURE__ */ React2.createElement(Select.TriggerButton, {
200
219
  placeholder: "Property",
201
220
  classNames: "is-full"
202
- }), /* @__PURE__ */ React2.createElement(Select.Portal, null, /* @__PURE__ */ React2.createElement(Select.Content, null, /* @__PURE__ */ React2.createElement(Select.Viewport, null, tableDefs.find((tableDef2) => tableDef2.id === refTable)?.columns.map(({ id, label: label2 }) => /* @__PURE__ */ React2.createElement(Select.Option, {
221
+ }), /* @__PURE__ */ React2.createElement(Select.Portal, null, /* @__PURE__ */ React2.createElement(Select.Content, null, /* @__PURE__ */ React2.createElement(Select.Viewport, null, tableDefs.find((tableDef2) => tableDef2.id === formState.refTable)?.columns.map(({ id, label }) => /* @__PURE__ */ React2.createElement(Select.Option, {
203
222
  key: id,
204
223
  value: id
205
- }, label2 ?? id))))))))), /* @__PURE__ */ React2.createElement(Separator, {
224
+ }, label ?? id)))))))), /* @__PURE__ */ React2.createElement(Separator, {
206
225
  classNames: "mli-2"
207
226
  }), /* @__PURE__ */ React2.createElement(Section, {
208
227
  className: "space-b-1.5"
@@ -233,11 +252,44 @@ var ColumnSettingsForm = ({ column, tableDef, tableDefs, onUpdate, onDelete, onC
233
252
  }))));
234
253
  };
235
254
 
255
+ // packages/ui/react-ui-table/src/hooks/useColumnSizing.ts
256
+ import { useCallback as useCallback2, useEffect, useState as useState3 } from "react";
257
+ import { useOnTransition } from "@dxos/react-ui";
258
+ var useColumnResizing = ({ columns, onColumnResize }) => {
259
+ const [columnsInitialised, setColumnsInitialised] = useState3(false);
260
+ const [columnSizing, setColumnSizing] = useState3({});
261
+ useEffect(() => {
262
+ if (columnsInitialised) {
263
+ return;
264
+ }
265
+ setColumnSizing(columns.filter((column) => !!column.size && column.prop !== void 0).reduce((state, column) => {
266
+ state[column.prop] = column.size;
267
+ return state;
268
+ }, {}));
269
+ setColumnsInitialised(true);
270
+ }, [
271
+ columns,
272
+ setColumnSizing
273
+ ]);
274
+ const [columnSizingInfo, setColumnSizingInfo] = useState3({});
275
+ const notifyColumnResize = useCallback2(() => onColumnResize?.(columnSizing), [
276
+ onColumnResize,
277
+ columnSizing
278
+ ]);
279
+ useOnTransition(columnSizingInfo.isResizingColumn, (v) => typeof v === "string", false, notifyColumnResize);
280
+ return {
281
+ columnSizing,
282
+ columnSizingInfo,
283
+ setColumnSizing,
284
+ setColumnSizingInfo
285
+ };
286
+ };
287
+
236
288
  // packages/ui/react-ui-table/src/hooks/useColumnSorting.ts
237
- import { useState as useState3 } from "react";
289
+ import { useState as useState4 } from "react";
238
290
  var useColumnSorting = (column) => {
239
- const [sortDirection, setSortDirection] = useState3(column.getIsSorted());
240
- const [firstSort, setFirstSort] = useState3();
291
+ const [sortDirection, setSortDirection] = useState4(column.getIsSorted());
292
+ const [firstSort, setFirstSort] = useState4();
241
293
  const canSort = column.getCanSort();
242
294
  const updateColumnSort = (direction) => {
243
295
  setSortDirection(direction);
@@ -280,57 +332,109 @@ var useColumnSorting = (column) => {
280
332
  };
281
333
 
282
334
  // packages/ui/react-ui-table/src/hooks/usePinLastRow.ts
283
- import { useEffect, useState as useState4 } from "react";
284
- var usePinLastRow = (pinLastRow, table, data, getScrollElement) => {
285
- const [pinnedRowKey, setPinnedRowKey] = useState4();
286
- useEffect(() => {
287
- if (!pinLastRow) {
288
- return;
335
+ import { useCallback as useCallback3, useEffect as useEffect2, useState as useState5 } from "react";
336
+ var usePinLastRow = (pinLastRow, table, data) => {
337
+ const { scrollContextRef } = useTableRootContext();
338
+ const [pinnedRowKey, setPinnedRowKey] = useState5();
339
+ const scrollToBottom = useCallback3(() => {
340
+ const element = scrollContextRef.current;
341
+ const scrollHeight = element?.scrollHeight;
342
+ if (element && scrollHeight) {
343
+ requestAnimationFrame(() => element.scrollTo({
344
+ top: scrollHeight
345
+ }));
289
346
  }
347
+ }, []);
348
+ const pinRow = useCallback3((rowToPin) => {
349
+ table.resetRowPinning();
350
+ rowToPin.pin("bottom");
351
+ setPinnedRowKey(rowToPin.id);
352
+ }, [
353
+ table
354
+ ]);
355
+ const clearRowPinning = useCallback3(() => {
356
+ table.resetRowPinning();
357
+ setPinnedRowKey(void 0);
358
+ }, [
359
+ table
360
+ ]);
361
+ const pinLastRowCallback = useCallback3(() => {
290
362
  const rows = table.getRowModel().rows;
291
- const rowToPin = rows[rows.length - 1];
292
- const rowId = rowToPin.id;
293
- if (rowId === pinnedRowKey) {
363
+ if (rows.length === 0) {
364
+ return clearRowPinning();
365
+ }
366
+ const lastRow = rows[rows.length - 1];
367
+ if (lastRow.id === pinnedRowKey) {
294
368
  return;
295
369
  }
296
- table.resetRowPinning();
297
- rowToPin.pin("bottom");
298
- const scrollElement = getScrollElement?.();
299
- const isFirstRender = pinnedRowKey === void 0;
300
- if (scrollElement && !isFirstRender) {
301
- requestAnimationFrame(() => {
302
- scrollElement.scrollTo({
303
- top: scrollElement.scrollHeight
304
- });
305
- });
370
+ pinRow(lastRow);
371
+ if (pinnedRowKey !== void 0) {
372
+ scrollToBottom();
306
373
  }
307
- setPinnedRowKey(rowId);
308
374
  }, [
309
- pinLastRow,
310
375
  table,
311
- getScrollElement,
312
- pinnedRowKey,
313
- setPinnedRowKey,
314
- data
376
+ scrollToBottom,
377
+ pinnedRowKey
378
+ ]);
379
+ useEffect2(() => {
380
+ if (pinLastRow) {
381
+ pinLastRowCallback();
382
+ }
383
+ }, [
384
+ data,
385
+ pinLastRow,
386
+ pinLastRowCallback
315
387
  ]);
316
388
  };
317
389
 
390
+ // packages/ui/react-ui-table/src/hooks/useRowSelection.ts
391
+ import { useControllableState } from "@radix-ui/react-use-controllable-state";
392
+ import { useCallback as useCallback4 } from "react";
393
+ var useRowSelection = (props) => {
394
+ const [rowSelection = {}, setRowSelection] = useControllableState({
395
+ prop: props.rowSelection,
396
+ onChange: props.onRowSelectionChange,
397
+ defaultProp: props.defaultRowSelection
398
+ });
399
+ const handleRowSelectionChange = useCallback4((updaterOrValue) => {
400
+ const nextRowSelection = typeof updaterOrValue === "function" ? updaterOrValue(rowSelection) : updaterOrValue;
401
+ if (props.rowsSelectable === "multi") {
402
+ setRowSelection(nextRowSelection);
403
+ } else if (props.rowsSelectable) {
404
+ const nextRowSelectionKey = Object.keys(nextRowSelection).filter((id) => !rowSelection[id])[0];
405
+ setRowSelection(nextRowSelectionKey ? {
406
+ [nextRowSelectionKey]: true
407
+ } : {});
408
+ } else {
409
+ setRowSelection({});
410
+ }
411
+ }, [
412
+ props.rowsSelectable,
413
+ setRowSelection,
414
+ rowSelection
415
+ ]);
416
+ return {
417
+ rowSelection,
418
+ handleRowSelectionChange
419
+ };
420
+ };
421
+
318
422
  // packages/ui/react-ui-table/src/components/ColumnMenu/ColumnMenu.tsx
319
423
  var ColumnMenu = ({ column, ...props }) => {
320
424
  const title = column.label?.length ? column.label : column.id;
321
425
  const header = props.context.header;
322
426
  const { canSort, sortDirection, onSelectSort, onToggleSort, onClearSort } = useColumnSorting(header.column);
323
427
  const columnSettingsAnchorRef = useRef2(null);
324
- const [isColumnSettingsOpen, setIsColumnSettingsOpen] = useState5(false);
428
+ const [isColumnSettingsOpen, setIsColumnSettingsOpen] = useState6(false);
325
429
  const shouldPreventFocusOnClose = useRef2(false);
326
- const onOpenColumnSettings = useCallback2(() => {
430
+ const onOpenColumnSettings = useCallback5(() => {
327
431
  shouldPreventFocusOnClose.current = true;
328
432
  setIsColumnSettingsOpen(true);
329
433
  }, [
330
434
  shouldPreventFocusOnClose,
331
435
  setIsColumnSettingsOpen
332
436
  ]);
333
- const onDropdownCloseAutoFocus = useCallback2((event) => {
437
+ const onDropdownCloseAutoFocus = useCallback5((event) => {
334
438
  if (shouldPreventFocusOnClose.current) {
335
439
  event.preventDefault();
336
440
  shouldPreventFocusOnClose.current = false;
@@ -363,7 +467,7 @@ var ColumnMenu = ({ column, ...props }) => {
363
467
  variant: "ghost"
364
468
  }, /* @__PURE__ */ React3.createElement(CaretDown2, {
365
469
  className: getSize3(4)
366
- })))), /* @__PURE__ */ React3.createElement(DropdownMenu.Content, {
470
+ })))), /* @__PURE__ */ React3.createElement(DropdownMenu.Portal, null, /* @__PURE__ */ React3.createElement(DropdownMenu.Content, {
367
471
  onCloseAutoFocus: onDropdownCloseAutoFocus,
368
472
  sideOffset: 4,
369
473
  collisionPadding: 8
@@ -399,7 +503,7 @@ var ColumnMenu = ({ column, ...props }) => {
399
503
  className: "opacity-50"
400
504
  }, /* @__PURE__ */ React3.createElement(GearSix, {
401
505
  className: mx3(getSize3(4), "rotate-90")
402
- })))), /* @__PURE__ */ React3.createElement(DropdownMenu.Arrow, null)), /* @__PURE__ */ React3.createElement(ColumnSettingsPanel, {
506
+ })))), /* @__PURE__ */ React3.createElement(DropdownMenu.Arrow, null))), /* @__PURE__ */ React3.createElement(ColumnSettingsPanel, {
403
507
  ...props,
404
508
  column,
405
509
  onClose: () => setIsColumnSettingsOpen(false)
@@ -441,17 +545,16 @@ var SortIndicator = ({ direction, onClick }) => {
441
545
  };
442
546
 
443
547
  // packages/ui/react-ui-table/src/components/Table/Table.tsx
444
- import { useControllableState } from "@radix-ui/react-use-controllable-state";
548
+ import { Slot } from "@radix-ui/react-slot";
445
549
  import { getCoreRowModel, getSortedRowModel, useReactTable, getGroupedRowModel, getExpandedRowModel } from "@tanstack/react-table";
446
- import { useVirtualizer } from "@tanstack/react-virtual";
447
- import React9, { Fragment, useCallback as useCallback3, useEffect as useEffect2, useState as useState6 } from "react";
448
- import { log } from "@dxos/log";
449
- import { useDefaultValue, useOnTransition } from "@dxos/react-ui";
550
+ import React9, { Fragment, useCallback as useCallback6, useEffect as useEffect3, useState as useState8, useContext as useContext3 } from "react";
551
+ import { useDefaultValue } from "@dxos/react-ui";
552
+ import { mx as mx5 } from "@dxos/react-ui-theme";
450
553
 
451
554
  // packages/ui/react-ui-table/src/components/Table/TableBody.tsx
452
555
  import { useArrowNavigationGroup } from "@fluentui/react-tabster";
453
556
  import { flexRender } from "@tanstack/react-table";
454
- import React6 from "react";
557
+ import React6, { useContext as useContext2 } from "react";
455
558
 
456
559
  // packages/ui/react-ui-table/src/components/Table/TableContext.tsx
457
560
  import React4, { useContext, createContext } from "react";
@@ -466,6 +569,35 @@ var useTableContext = () => {
466
569
  return context;
467
570
  };
468
571
 
572
+ // packages/ui/react-ui-table/src/components/Table/TableRootContext.ts
573
+ import { useVirtualizer } from "@tanstack/react-virtual";
574
+ import { createContext as createContext2, useRef as useRef3, useState as useState7 } from "react";
575
+ var useTableRootContext = () => {
576
+ const scrollContextRef = useRef3(null);
577
+ const [tableCount, setTableCount] = useState7(0);
578
+ const virtualizer = useVirtualizer({
579
+ getScrollElement: () => scrollContextRef?.current ?? null,
580
+ count: tableCount,
581
+ overscan: 8,
582
+ estimateSize: () => 40
583
+ });
584
+ const dispatch = (action) => {
585
+ switch (action.type) {
586
+ case "updateTableCount":
587
+ setTableCount(action.count);
588
+ break;
589
+ default:
590
+ break;
591
+ }
592
+ };
593
+ return {
594
+ scrollContextRef,
595
+ virtualizer,
596
+ dispatch
597
+ };
598
+ };
599
+ var TableRootContext = createContext2({});
600
+
469
601
  // packages/ui/react-ui-table/src/theme.ts
470
602
  import { modalSurface, focusRing, ghostSelected, ghostSelectedCurrent, groupBorder, mx as mx4 } from "@dxos/react-ui-theme";
471
603
  var currentRow = "!bg-neutral-75 !dark:bg-neutral-850";
@@ -530,6 +662,7 @@ var TableBody = ({ rows }) => {
530
662
  axis: "grid"
531
663
  });
532
664
  const canBeCurrent = !isGrid && !!onDatumClick;
665
+ const { virtualizer } = useContext2(TableRootContext);
533
666
  return /* @__PURE__ */ React6.createElement("tbody", isGrid && domAttributes, rows.map((row) => {
534
667
  const isCurrent = currentDatum === row.original;
535
668
  const isSelected = rowSelection?.[row.id];
@@ -541,6 +674,8 @@ var TableBody = ({ rows }) => {
541
674
  return /* @__PURE__ */ React6.createElement("tr", {
542
675
  key: keyAccessor ? keyAccessor(row.original) : row.id,
543
676
  className: classNames,
677
+ "data-index": row.index,
678
+ ref: virtualizer.measureElement,
544
679
  ...isCurrent && {
545
680
  "aria-current": "location"
546
681
  },
@@ -638,57 +773,49 @@ var TableHead = (_props) => {
638
773
  TableHead.displayName = TABLE_HEAD_NAME;
639
774
 
640
775
  // packages/ui/react-ui-table/src/components/Table/Table.tsx
641
- var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/components/Table/Table.tsx";
642
- var Table = (props) => {
643
- const { role, onColumnResize, columnVisibility, header = true, rowsSelectable, debug, onDataSelectionChange, getScrollElement, pinLastRow } = props;
776
+ var TableRoot = ({ children }) => {
777
+ const contextValue = useTableRootContext();
778
+ return /* @__PURE__ */ React9.createElement(TableRootContext.Provider, {
779
+ value: contextValue
780
+ }, children);
781
+ };
782
+ var TableViewport = ({ children, classNames, asChild, ...props }) => {
783
+ const { scrollContextRef } = useContext3(TableRootContext);
784
+ const classes = mx5(classNames);
785
+ return asChild ? /* @__PURE__ */ React9.createElement(Slot, {
786
+ ref: scrollContextRef,
787
+ ...props
788
+ }, children) : /* @__PURE__ */ React9.createElement("div", {
789
+ role: "none",
790
+ className: classes,
791
+ ref: scrollContextRef,
792
+ ...props
793
+ }, children);
794
+ };
795
+ var TablePrimitive = (props) => {
796
+ const { role, onColumnResize, columnVisibility, header = true, rowsSelectable, debug, onDataSelectionChange, pinLastRow } = props;
644
797
  const columns = useDefaultValue(props.columns, []);
645
- const data = useDefaultValue(props.data, []);
646
- const TableProvider2 = TableProvider;
647
- const [columnsInitialised, setColumnsInitialised] = useState6(false);
648
- const [columnSizing, setColumnSizing] = useState6({});
649
- useEffect2(() => {
650
- if (columnsInitialised) {
651
- return;
652
- }
653
- setColumnSizing(columns.filter((column) => !!column.size && column.prop !== void 0).reduce((state, column) => {
654
- state[column.prop] = column.size;
655
- return state;
656
- }, {}));
657
- setColumnsInitialised(true);
658
- }, [
659
- columns,
660
- setColumnSizing
798
+ const incomingData = useDefaultValue(props.data, []);
799
+ const [data, setData] = useState8([
800
+ ...incomingData
661
801
  ]);
662
- const [columnSizingInfo, setColumnSizingInfo] = useState6({});
663
- const notifyColumnResize = useCallback3(() => onColumnResize?.(columnSizing), [
664
- onColumnResize,
665
- columnSizing
802
+ const forceUpdate = useCallback6(() => setData([
803
+ ...incomingData
804
+ ]), [
805
+ incomingData
666
806
  ]);
667
- useOnTransition(columnSizingInfo.isResizingColumn, (v) => typeof v === "string", false, notifyColumnResize);
668
- const [rowSelection = {}, setRowSelection] = useControllableState({
669
- prop: props.rowSelection,
670
- onChange: props.onRowSelectionChange,
671
- defaultProp: props.defaultRowSelection
672
- });
673
- const handleRowSelectionChange = useCallback3((updaterOrValue) => {
674
- const nextRowSelection = typeof updaterOrValue === "function" ? updaterOrValue(rowSelection) : updaterOrValue;
675
- if (rowsSelectable === "multi") {
676
- setRowSelection(nextRowSelection);
677
- } else if (rowsSelectable) {
678
- const nextRowSelectionKey = Object.keys(nextRowSelection).filter((id) => !rowSelection[id])[0];
679
- setRowSelection(nextRowSelectionKey ? {
680
- [nextRowSelectionKey]: true
681
- } : {});
682
- } else {
683
- setRowSelection({});
684
- }
685
- }, [
686
- rowsSelectable,
687
- setRowSelection,
688
- rowSelection
807
+ useEffect3(() => forceUpdate(), [
808
+ JSON.stringify(incomingData),
809
+ forceUpdate
689
810
  ]);
690
- const [grouping, handleGroupingChange] = useState6(props.grouping ?? []);
691
- useEffect2(() => handleGroupingChange(props.grouping ?? []), [
811
+ const TableProvider2 = TableProvider;
812
+ const { columnSizing, setColumnSizing, columnSizingInfo, setColumnSizingInfo } = useColumnResizing({
813
+ columns,
814
+ onColumnResize
815
+ });
816
+ const { rowSelection, handleRowSelectionChange } = useRowSelection(props);
817
+ const [grouping, handleGroupingChange] = useState8(props.grouping ?? []);
818
+ useEffect3(() => handleGroupingChange(props.grouping ?? []), [
692
819
  handleGroupingChange,
693
820
  props.grouping
694
821
  ]);
@@ -730,23 +857,15 @@ var Table = (props) => {
730
857
  // Debug
731
858
  debugTable: debug
732
859
  });
733
- useEffect2(() => {
860
+ useEffect3(() => {
734
861
  onDataSelectionChange?.(Object.keys(rowSelection).map((id) => table.getRowModel().rowsById[id].original));
735
862
  }, [
736
863
  onDataSelectionChange,
737
864
  rowSelection,
738
865
  table
739
866
  ]);
740
- usePinLastRow(pinLastRow, table, data, getScrollElement);
867
+ usePinLastRow(pinLastRow, table, data);
741
868
  const expand = false;
742
- if (!getScrollElement) {
743
- log.warn("Table: getScrollElement is not set. This is required for virtualized tables.", void 0, {
744
- F: __dxlog_file,
745
- L: 159,
746
- S: void 0,
747
- C: (f, a) => f(...a)
748
- });
749
- }
750
869
  return /* @__PURE__ */ React9.createElement(TableProvider2, {
751
870
  ...props,
752
871
  table,
@@ -755,26 +874,19 @@ var Table = (props) => {
755
874
  isGrid: role === "grid" || role === "treegrid"
756
875
  }, /* @__PURE__ */ React9.createElement(TableImpl, {
757
876
  debug: false,
758
- getScrollElement,
759
877
  ...props
760
878
  }));
761
879
  };
762
880
  var TableImpl = (props) => {
763
- const { debug, classNames, getScrollElement, role, footer, grouping, fullWidth } = props;
881
+ const { debug, classNames, role, footer, grouping, fullWidth } = props;
764
882
  const { table } = useTableContext();
765
- const centerRows = table.getCenterRows();
766
- const bottomRows = table.getBottomRows();
767
- const rows = [
768
- ...centerRows,
769
- ...bottomRows
770
- ];
771
883
  if (debug) {
772
884
  return /* @__PURE__ */ React9.createElement("pre", {
773
885
  className: "font-mono text-xs text-neutral-500 m-1 p-2 ring"
774
886
  }, /* @__PURE__ */ React9.createElement("code", null, JSON.stringify(table.getState(), void 0, 2)));
775
887
  }
776
- const virtualisable = getScrollElement !== void 0;
777
888
  const isResizingColumn = table.getState().columnSizingInfo.isResizingColumn;
889
+ const TableComponent = isResizingColumn ? MemoizedVirtualisedTableContent : VirtualizedTableContent;
778
890
  return /* @__PURE__ */ React9.createElement("table", {
779
891
  role,
780
892
  className: tableRoot(props, classNames),
@@ -783,24 +895,26 @@ var TableImpl = (props) => {
783
895
  width: table.getTotalSize()
784
896
  }
785
897
  }
786
- }, /* @__PURE__ */ React9.createElement(TableHead, null), grouping?.length !== 0 ? virtualisable ? isResizingColumn ? /* @__PURE__ */ React9.createElement(MemoizedVirtualisedTableContent, {
787
- getScrollElement
788
- }) : /* @__PURE__ */ React9.createElement(VirtualizedTableContent, {
789
- getScrollElement
790
- }) : /* @__PURE__ */ React9.createElement(TableBody, {
791
- rows
792
- }) : /* @__PURE__ */ React9.createElement(GroupedTableContent, null), footer && /* @__PURE__ */ React9.createElement(TableFooter, null));
898
+ }, /* @__PURE__ */ React9.createElement(TableHead, null), grouping?.length !== 0 ? /* @__PURE__ */ React9.createElement(TableComponent, null) : /* @__PURE__ */ React9.createElement(GroupedTableContent, null), footer && /* @__PURE__ */ React9.createElement(TableFooter, null));
793
899
  };
794
- var VirtualizedTableContent = ({ getScrollElement }) => {
900
+ var VirtualizedTableContent = () => {
795
901
  const { table } = useTableContext();
902
+ const { virtualizer, dispatch } = useContext3(TableRootContext);
796
903
  const centerRows = table.getCenterRows();
797
- const pinnedRows = table.getBottomRows();
798
- const { getTotalSize, getVirtualItems } = useVirtualizer({
799
- getScrollElement,
800
- count: centerRows.length,
801
- overscan: 8,
802
- estimateSize: () => 40
803
- });
904
+ let pinnedRows = [];
905
+ useEffect3(() => {
906
+ dispatch({
907
+ type: "updateTableCount",
908
+ count: centerRows.length
909
+ });
910
+ }, [
911
+ centerRows.length
912
+ ]);
913
+ try {
914
+ pinnedRows = table.getBottomRows();
915
+ } catch (_) {
916
+ }
917
+ const { getTotalSize, getVirtualItems } = virtualizer;
804
918
  const virtualRows = getVirtualItems();
805
919
  const totalSize = getTotalSize();
806
920
  const paddingTop = virtualRows.length > 0 ? virtualRows?.[0]?.start || 0 : 0;
@@ -808,8 +922,8 @@ var VirtualizedTableContent = ({ getScrollElement }) => {
808
922
  const rowsToRender = [
809
923
  ...virtualRows.map((virtualRow) => centerRows[virtualRow.index]),
810
924
  ...pinnedRows
811
- ];
812
- return /* @__PURE__ */ React9.createElement(React9.Fragment, null, paddingTop > 0 && /* @__PURE__ */ React9.createElement("tbody", {
925
+ ].filter((r) => !!r);
926
+ return /* @__PURE__ */ React9.createElement(Fragment, null, paddingTop > 0 && /* @__PURE__ */ React9.createElement("tbody", {
813
927
  role: "none"
814
928
  }, /* @__PURE__ */ React9.createElement("tr", {
815
929
  role: "none"
@@ -846,6 +960,11 @@ var GroupedTableContent = () => {
846
960
  }));
847
961
  }));
848
962
  };
963
+ var Table = {
964
+ Root: TableRoot,
965
+ Table: TablePrimitive,
966
+ Viewport: TableViewport
967
+ };
849
968
 
850
969
  // packages/ui/react-ui-table/src/helpers.tsx
851
970
  import { useFocusFinders } from "@fluentui/react-tabster";
@@ -853,11 +972,11 @@ import { Check as Check2, ClipboardText, X as X3 } from "@phosphor-icons/react";
853
972
  import { createColumnHelper } from "@tanstack/react-table";
854
973
  import { format } from "date-fns/format";
855
974
  import { formatDistanceToNow } from "date-fns/formatDistanceToNow";
856
- import React10, { useEffect as useEffect3, useRef as useRef3, useState as useState7 } from "react";
857
- import { log as log2 } from "@dxos/log";
975
+ import React10, { useCallback as useCallback7, useEffect as useEffect4, useRef as useRef4, useState as useState9 } from "react";
976
+ import { log } from "@dxos/log";
858
977
  import { Input as Input2, Tooltip } from "@dxos/react-ui";
859
- import { getSize as getSize4, mx as mx5 } from "@dxos/react-ui-theme";
860
- var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/helpers.tsx";
978
+ import { getSize as getSize4, mx as mx6 } from "@dxos/react-ui-theme";
979
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/helpers.tsx";
861
980
  var createColumnBuilder = () => ({
862
981
  helper: createColumnHelper(),
863
982
  builder: new ColumnBuilder()
@@ -871,16 +990,19 @@ var ComboboxBuilderCell = (cellContext) => {
871
990
  }) : null;
872
991
  };
873
992
  var StringBuilderCell = (cellContext) => {
874
- const ref = useRef3(null);
993
+ const ref = useRef4(null);
875
994
  const { findPrevFocusable } = useFocusFinders();
876
995
  const { onUpdate, cell } = cellContext.column.columnDef.meta;
877
996
  const initialValue = cellContext.getValue();
878
- const [value, setValue] = useState7(() => {
879
- if (initialValue?.content) {
880
- return initialValue?.content;
881
- }
882
- return initialValue;
883
- });
997
+ const getInitialValue = useCallback7(() => {
998
+ return initialValue?.content ?? initialValue;
999
+ }, [
1000
+ initialValue
1001
+ ]);
1002
+ const [value, setValue] = useState9(getInitialValue);
1003
+ useEffect4(() => setValue(getInitialValue()), [
1004
+ getInitialValue
1005
+ ]);
884
1006
  const handleSave = () => {
885
1007
  ref.current && document.activeElement === ref.current && findPrevFocusable(ref.current)?.focus();
886
1008
  if (value !== initialValue) {
@@ -917,10 +1039,10 @@ var displayNumber = (value, digits) => value || value === 0 ? value.toLocaleStri
917
1039
  var NumberBuilderCell = (cellContext) => {
918
1040
  const { onUpdate, digits } = cellContext.column.columnDef.meta;
919
1041
  const value = cellContext.getValue();
920
- const [text, setText] = useState7(displayNumber(value, digits));
1042
+ const [text, setText] = useState9(displayNumber(value, digits));
921
1043
  const { findPrevFocusable } = useFocusFinders();
922
- const ref = useRef3(null);
923
- useEffect3(() => {
1044
+ const ref = useRef4(null);
1045
+ useEffect4(() => {
924
1046
  (!document.activeElement || document.activeElement !== ref.current) && setText(displayNumber(value, digits));
925
1047
  }, [
926
1048
  digits,
@@ -1007,7 +1129,7 @@ var ColumnBuilder = class {
1007
1129
  cell: model && onUpdate ? ComboboxBuilderCell : (cellContext) => {
1008
1130
  const cellValue = cellContext.getValue();
1009
1131
  return /* @__PURE__ */ React10.createElement("div", {
1010
- className: mx5("truncate", classNames)
1132
+ className: mx6("truncate", classNames)
1011
1133
  }, cellValue ? model?.getText(cellValue) : "");
1012
1134
  },
1013
1135
  ...props,
@@ -1038,7 +1160,7 @@ var ColumnBuilder = class {
1038
1160
  value = value.content;
1039
1161
  }
1040
1162
  return /* @__PURE__ */ React10.createElement("div", {
1041
- className: mx5("truncate", textPadding, classNames)
1163
+ className: mx6("truncate", textPadding, classNames)
1042
1164
  }, value);
1043
1165
  },
1044
1166
  ...props,
@@ -1092,14 +1214,14 @@ var ColumnBuilder = class {
1092
1214
  addSuffix: true
1093
1215
  }) : value.toISOString() : void 0;
1094
1216
  return /* @__PURE__ */ React10.createElement("div", {
1095
- className: mx5(textPadding, classNames)
1217
+ className: mx6(textPadding, classNames)
1096
1218
  }, str);
1097
1219
  } catch (err) {
1098
- log2.info("formatError", {
1220
+ log.info("formatError", {
1099
1221
  value
1100
1222
  }, {
1101
- F: __dxlog_file2,
1102
- L: 374,
1223
+ F: __dxlog_file,
1224
+ L: 376,
1103
1225
  S: this,
1104
1226
  C: (f, a) => f(...a)
1105
1227
  });
@@ -1231,11 +1353,11 @@ var ColumnBuilder = class {
1231
1353
  const value = cell.getValue();
1232
1354
  if (value) {
1233
1355
  return /* @__PURE__ */ React10.createElement(IconOn, {
1234
- className: mx5("block mli-auto", getSize4(6), on?.className ?? "text-green-700")
1356
+ className: mx6("block mli-auto", getSize4(6), on?.className ?? "text-green-700")
1235
1357
  });
1236
1358
  } else if (value === false) {
1237
1359
  return /* @__PURE__ */ React10.createElement(IconOff, {
1238
- className: mx5("block mli-auto", getSize4(6), off?.className ?? "text-red-700")
1360
+ className: mx6("block mli-auto", getSize4(6), off?.className ?? "text-red-700")
1239
1361
  });
1240
1362
  } else {
1241
1363
  return null;
@@ -1277,7 +1399,7 @@ var ColumnBuilder = class {
1277
1399
  };
1278
1400
 
1279
1401
  // packages/ui/react-ui-table/src/schema/schema.ts
1280
- import { AST } from "@effect/schema";
1402
+ import * as AST from "@effect/schema/AST";
1281
1403
  var classifySchemaProperties = (schema) => {
1282
1404
  const recurse = (node, path, acc) => {
1283
1405
  const properties = AST.getPropertySignatures(node);
@@ -1404,6 +1526,8 @@ export {
1404
1526
  MemoizedVirtualisedTableContent,
1405
1527
  SortIndicator,
1406
1528
  Table,
1529
+ TablePrimitive,
1530
+ TableRootContext,
1407
1531
  createColumnBuilder,
1408
1532
  currentRow,
1409
1533
  flushPadding,
@@ -1426,7 +1550,10 @@ export {
1426
1550
  theadTh,
1427
1551
  theadTr,
1428
1552
  translations_default as translations,
1553
+ useColumnResizing,
1429
1554
  useColumnSorting,
1430
- usePinLastRow
1555
+ usePinLastRow,
1556
+ useRowSelection,
1557
+ useTableRootContext
1431
1558
  };
1432
1559
  //# sourceMappingURL=index.mjs.map