@mendylanda/ui 0.3.0-alpha.0 → 0.3.0-alpha.10

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 (55) hide show
  1. package/README.md +20 -3
  2. package/dist/class-names.js +1 -1
  3. package/dist/filters/filter-bar.d.ts +1 -1
  4. package/dist/filters/filter-bar.js +53 -12
  5. package/dist/filters/filter-menu-focus.d.ts +1 -1
  6. package/dist/filters/filter-menu-panel.d.ts +1 -1
  7. package/dist/filters/use-menu-placement.d.ts +1 -1
  8. package/dist/primitives/popover.d.ts +5 -0
  9. package/dist/primitives/popover.js +10 -0
  10. package/dist/styles.css +153 -27
  11. package/dist/styles.tailwind3.css +153 -27
  12. package/dist/table/clipboard.d.ts +1 -1
  13. package/dist/table/clipboard.js +35 -22
  14. package/dist/table/column-widths.d.ts +9 -0
  15. package/dist/table/column-widths.js +23 -0
  16. package/dist/table/columns.d.ts +6 -0
  17. package/dist/table/columns.js +4 -0
  18. package/dist/table/data-table.d.ts +3 -0
  19. package/dist/table/data-table.js +6 -3
  20. package/dist/table/index.d.ts +1 -0
  21. package/dist/table/index.js +1 -0
  22. package/dist/table/table-column-settings.d.ts +6 -0
  23. package/dist/table/table-column-settings.js +55 -0
  24. package/dist/table/table-controls.d.ts +1 -3
  25. package/dist/table/table-controls.js +4 -41
  26. package/dist/table/table-feedback.d.ts +5 -1
  27. package/dist/table/table-feedback.js +5 -4
  28. package/dist/table/table-filters.d.ts +8 -0
  29. package/dist/table/table-filters.js +9 -0
  30. package/dist/table/table-frame.d.ts +10 -0
  31. package/dist/table/table-frame.js +10 -0
  32. package/dist/table/table-layout.d.ts +44 -0
  33. package/dist/table/table-layout.js +68 -0
  34. package/dist/table/table-loading.d.ts +12 -0
  35. package/dist/table/table-loading.js +6 -0
  36. package/dist/table/table-parts.d.ts +20 -2
  37. package/dist/table/table-parts.js +91 -37
  38. package/dist/table/table-view.d.ts +19 -2
  39. package/dist/table/table-view.js +60 -53
  40. package/dist/table/use-column-window.d.ts +42 -0
  41. package/dist/table/use-column-window.js +65 -0
  42. package/dist/table/use-data-table.d.ts +2 -2
  43. package/dist/table/use-data-table.js +15 -2
  44. package/dist/table/use-fill-layout.d.ts +2 -0
  45. package/dist/table/use-fill-layout.js +67 -0
  46. package/dist/table/use-inline-row-selection.d.ts +10 -0
  47. package/dist/table/use-inline-row-selection.js +33 -0
  48. package/dist/table/use-stable-array.d.ts +3 -0
  49. package/dist/table/use-stable-array.js +16 -0
  50. package/dist/table/use-table-interaction.d.ts +1 -1
  51. package/dist/table/use-table-interaction.js +32 -3
  52. package/dist/table/use-table-results.d.ts +8 -0
  53. package/dist/table/use-table-results.js +63 -0
  54. package/dist/utils.js +13 -2
  55. package/package.json +9 -8
@@ -0,0 +1,55 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useId } from "react";
4
+ import { DragDropContext, Draggable, Droppable } from "@hello-pangea/dnd";
5
+ import { GripVertical, Pin, RotateCcw, SlidersHorizontal } from "lucide-react";
6
+ import { useMendyUI } from "../customization.js";
7
+ import { Button } from "../primitives/button.js";
8
+ import { Checkbox } from "../primitives/checkbox.js";
9
+ import { Popover, PopoverContent, PopoverTrigger } from "../primitives/popover.js";
10
+ import { cn } from "../utils.js";
11
+ import { applyTablePreferences } from "./use-data-table.js";
12
+ export function TableColumnSettings({ table, children, }) {
13
+ const id = useId();
14
+ const { portalContainer } = useMendyUI();
15
+ const columns = table.getAllLeafColumns();
16
+ const byId = new Map(columns.map((column) => [column.id, column]));
17
+ const order = [
18
+ ...new Set([...table.state.columnOrder, ...columns.map((column) => column.id)]),
19
+ ].filter((key) => byId.has(key));
20
+ const ordered = order.map((key) => byId.get(key));
21
+ const hidden = columns.filter((column) => !column.getIsVisible()).length;
22
+ const start = columns.filter((column) => column.getIsPinned() === "start").length;
23
+ const end = columns.filter((column) => column.getIsPinned() === "end").length;
24
+ const summary = [
25
+ hidden && `${hidden} hidden`,
26
+ start && `${start} pinned left`,
27
+ end && `${end} pinned right`,
28
+ ]
29
+ .filter(Boolean)
30
+ .join(", ");
31
+ function reorder(result) {
32
+ if (!result.destination || result.destination.index === result.source.index)
33
+ return;
34
+ const next = [...order];
35
+ const [moved] = next.splice(result.source.index, 1);
36
+ next.splice(result.destination.index, 0, moved);
37
+ table.setColumnOrder(next);
38
+ }
39
+ return (_jsxs(Popover, { children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx(Button, { variant: "outline", size: "icon-sm", "aria-label": "Column settings", title: "Column settings", children: _jsx(SlidersHorizontal, { className: "mui-100c22d5776b" }) }) }), _jsxs(PopoverContent, { align: "end", "aria-label": "Column settings", className: "mui-222f930b8752 mui-a4cce7869436 mui-302c0d124a94 mui-04760bcd507f", onCloseAutoFocus: (event) => {
40
+ if (document.activeElement?.closest('[role="grid"]'))
41
+ event.preventDefault();
42
+ }, children: [_jsxs("div", { className: "mui-27ead27a81df mui-bbe39cfb5cc6 mui-9513b3968002 mui-d3cea061f2f3", children: [_jsx("h4", { className: "mui-c74ab393b96d mui-daaac3fbf55e", children: "Column settings" }), _jsx("p", { className: "mui-3992de70b033 mui-35f35c41d134", children: "Drag to reorder, toggle visibility, and pin columns." })] }), children, _jsx(DragDropContext, { onDragEnd: reorder, children: _jsx(Droppable, { droppableId: id, getContainerForClone: () => portalContainer ?? document.body, renderClone: (provided, snapshot, rubric) => (_jsx(ColumnSettingsRow, { column: ordered[rubric.source.index], provided: provided, dragging: snapshot.isDragging })), children: (provided) => (_jsxs("div", { ref: provided.innerRef, ...provided.droppableProps, className: "mui-410da8dfa8ac mui-da0beef015c9 mui-2368e909f3a5 mui-b97db4a9f432", children: [ordered.map((column, index) => (_jsx(Draggable, { draggableId: `${id}-${column.id}`, disableInteractiveElementBlocking: true, index: index, children: (provided, snapshot) => (_jsx(ColumnSettingsRow, { column: column, provided: provided, dragging: snapshot.isDragging })) }, column.id))), provided.placeholder] })) }) }), _jsxs("div", { className: "mui-222f930b8752 mui-27ead27a81df mui-71556df3b421 mui-32c92bb1fde4 mui-074569488cca mui-894c3815a57f mui-9513b3968002 mui-b5edc3ea7c91", children: [_jsx("p", { className: "mui-3992de70b033 mui-35f35c41d134", "aria-live": "polite", children: summary || "No changes" }), _jsxs(Button, { variant: "ghost", size: "sm", className: "mui-c8e50836f8a1 mui-e7e01cc7f4df mui-3992de70b033", "aria-label": "Reset columns", onClick: () => applyTablePreferences(table, { version: 1, ...table.initialState }), children: [_jsx(RotateCcw, { className: "mui-5e34f5313859" }), "Reset"] })] })] })] }));
43
+ }
44
+ function ColumnSettingsRow({ column, provided, dragging, }) {
45
+ const definition = column.columnDef;
46
+ const label = definition.label ?? (typeof definition.header === "string" ? definition.header : column.id);
47
+ const pin = column.getIsPinned();
48
+ const next = pin === "start" ? "end" : pin === "end" ? false : "start";
49
+ const pinLabel = next === "start"
50
+ ? `Pin ${label} to left`
51
+ : next === "end"
52
+ ? `Pin ${label} to right`
53
+ : `Unpin ${label}`;
54
+ return (_jsxs("div", { ref: provided.innerRef, ...provided.draggableProps, "data-mendy-ui": "", "data-column-setting": column.id, "data-dragging": dragging, style: provided.draggableProps.style, className: cn("mui-222f930b8752 mui-71556df3b421 mui-074569488cca mui-3fa8c572949b mui-5b272f3c5076 mui-e7e01cc7f4df mui-a8c1dfb1d4ef mui-c74ab393b96d mui-957a109f887f", dragging ? "mui-3eef1ce97dba mui-0d4ca75de41c mui-853be6c11609 mui-fd9d0b696aac" : "mui-5cbdd1b32f1a"), children: [_jsx("button", { type: "button", ...provided.dragHandleProps, "aria-label": `Reorder ${label}`, title: "Drag to reorder. Press Space to move with arrow keys.", className: "mui-222f930b8752 mui-2bd43fb57913 mui-27ead27a81df mui-f2e47d9c52ad mui-71556df3b421 mui-a503dd374cca mui-02e603944040 mui-35f35c41d134 mui-b30fc56058b6 mui-355efe22aedb mui-db549d38ff19 mui-71998ea7a2d0", children: _jsx(GripVertical, { className: "mui-61c000c8a98a" }) }), _jsxs("label", { className: "mui-222f930b8752 mui-184ddc11e5f9 mui-7bd5bab6d7f4 mui-d2fa8848968d mui-71556df3b421 mui-074569488cca", children: [_jsx(Checkbox, { checked: column.getIsVisible(), disabled: !column.getCanHide(), onCheckedChange: (value) => column.toggleVisibility(value === true), "aria-label": `Show ${label}` }), _jsx("span", { className: "mui-5a3a4ee420c2", title: label, children: label })] }), _jsx(Button, { variant: "ghost", size: "icon-sm", disabled: !column.getCanPin(), "aria-label": pinLabel, title: pinLabel, className: cn("mui-0712570dcc9f mui-27ead27a81df", pin && "mui-87ecb59f2d2d"), onClick: () => column.pin(next), children: _jsx(Pin, { className: cn("mui-61c000c8a98a", pin === "start" && "mui-4e3f08588318", pin === "end" && "mui-c80411b7591f", !pin && "mui-35f35c41d134") }) })] }));
55
+ }
@@ -2,9 +2,7 @@ import type { ReactNode } from "react";
2
2
  import type { DataTableInstance } from "./use-data-table.js";
3
3
  import type { TableColumn } from "./columns.js";
4
4
  import type { SavedTableView } from "./state.js";
5
- export declare function TableColumnSettings<T extends object>({ table }: {
6
- table: DataTableInstance<T>;
7
- }): import("react/jsx-runtime").JSX.Element;
5
+ export { TableColumnSettings } from "./table-column-settings.js";
8
6
  export declare function TablePagination<T extends object>({ table, loading, }: {
9
7
  table: DataTableInstance<T>;
10
8
  loading?: boolean;
@@ -1,49 +1,12 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
3
3
  import { useState } from "react";
4
- import { ChevronLeft, ChevronRight, SlidersHorizontal } from "lucide-react";
4
+ import { ChevronLeft, ChevronRight } from "lucide-react";
5
5
  import { Button } from "../primitives/button.js";
6
6
  import { Checkbox } from "../primitives/checkbox.js";
7
7
  import { Input } from "../primitives/input.js";
8
- import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuCheckboxItem, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent, } from "../primitives/dropdown-menu.js";
9
- import { applyTablePreferences } from "./use-data-table.js";
10
- export function TableColumnSettings({ table }) {
11
- const columns = table.getAllLeafColumns();
12
- const byId = new Map(columns.map((column) => [column.id, column]));
13
- const orderedIds = new Set(table.state.columnOrder);
14
- const order = [
15
- ...table.state.columnOrder,
16
- ...columns.map((column) => column.id).filter((id) => !orderedIds.has(id)),
17
- ];
18
- function move(id, delta) {
19
- const next = [...order], index = next.indexOf(id), destination = index + delta;
20
- if (index < 0 || destination < 0 || destination >= next.length)
21
- return;
22
- next.splice(index, 1);
23
- next.splice(destination, 0, id);
24
- table.setColumnOrder(next);
25
- }
26
- return (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "outline", size: "sm", "aria-label": "Column settings", children: [_jsx(SlidersHorizontal, { className: "mui-100c22d5776b" }), _jsx("span", { children: "Columns" })] }) }), _jsxs(DropdownMenuContent, { align: "end", className: "mui-b057efba3283 mui-72b37c1a040d mui-1a26a0d28420", "aria-label": "Column settings", children: [order.map((id, index) => {
27
- const column = byId.get(id);
28
- if (!column)
29
- return null;
30
- const definition = column.columnDef;
31
- const label = definition.label ?? (typeof definition.header === "string" ? definition.header : id);
32
- return (_jsxs(DropdownMenuSub, { children: [_jsx(DropdownMenuSubTrigger, { children: label }), _jsxs(DropdownMenuSubContent, { children: [_jsx(DropdownMenuCheckboxItem, { checked: column.getIsVisible(), disabled: !column.getCanHide(), onSelect: (event) => event.preventDefault(), onCheckedChange: (value) => column.toggleVisibility(value), children: "Visible" }), _jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { disabled: !column.getCanPin(), onSelect: (event) => {
33
- event.preventDefault();
34
- column.pin(column.getIsPinned() === "start" ? false : "start");
35
- }, children: [" ", column.getIsPinned() === "start" ? "Unpin" : "Pin to start"] }), _jsx(DropdownMenuItem, { disabled: !column.getCanPin(), onSelect: (event) => {
36
- event.preventDefault();
37
- column.pin(column.getIsPinned() === "end" ? false : "end");
38
- }, children: column.getIsPinned() === "end" ? "Unpin" : "Pin to end" }), _jsx(DropdownMenuItem, { disabled: index === 0, onSelect: (event) => {
39
- event.preventDefault();
40
- move(id, -1);
41
- }, children: "Move earlier" }), _jsx(DropdownMenuItem, { disabled: index === order.length - 1, onSelect: (event) => {
42
- event.preventDefault();
43
- move(id, 1);
44
- }, children: "Move later" })] })] }, id));
45
- }), _jsx(DropdownMenuSeparator, {}), _jsx(DropdownMenuItem, { onSelect: () => applyTablePreferences(table, { version: 1, ...table.initialState }), children: "Reset columns" })] })] }));
46
- }
8
+ import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent, } from "../primitives/dropdown-menu.js";
9
+ export { TableColumnSettings } from "./table-column-settings.js";
47
10
  export function TablePagination({ table, loading, }) {
48
11
  return (_jsxs("nav", { "data-mendy-ui": "", "aria-label": "Table pagination", className: "mui-222f930b8752 mui-faa8a23c68f6 mui-71556df3b421 mui-307644c34db6 mui-d7db3451d154 mui-b5edc3ea7c91 mui-c74ab393b96d", children: [_jsxs("span", { children: ["Page ", table.state.pagination.pageIndex + 1, table.getPageCount() >= 0 ? ` of ${Math.max(1, table.getPageCount())}` : ""] }), _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "outline", size: "sm", children: [table.state.pagination.pageSize, " rows"] }) }), _jsx(DropdownMenuContent, { children: [10, 20, 50, 100].map((size) => (_jsxs(DropdownMenuItem, { onSelect: () => table.setPageSize(size), children: [size, " rows"] }, size))) })] }), _jsx(Button, { variant: "outline", size: "icon-sm", disabled: loading || !table.getCanPreviousPage(), "aria-label": "Previous page", onClick: () => table.previousPage(), children: _jsx(ChevronLeft, { className: "mui-100c22d5776b" }) }), _jsx(Button, { variant: "outline", size: "icon-sm", disabled: loading || !table.getCanNextPage(), "aria-label": "Next page", onClick: () => table.nextPage(), children: _jsx(ChevronRight, { className: "mui-100c22d5776b" }) })] }));
49
12
  }
@@ -1,7 +1,11 @@
1
1
  import type { ReactNode } from "react";
2
2
  import type { TableDataState } from "./table-view.js";
3
- export declare function TableInitialState({ status, hasRows, loadingState, emptyState, error, retry, }: Pick<TableDataState, "status" | "error" | "retry"> & {
3
+ export declare function TableInitialState({ status, hasRows, loadingState, emptyState, error, retry, hasFilters, pageIndex, clearFilters, firstPage, }: Pick<TableDataState, "status" | "error" | "retry"> & {
4
4
  hasRows: boolean;
5
5
  loadingState?: ReactNode;
6
6
  emptyState?: ReactNode;
7
+ hasFilters?: boolean;
8
+ pageIndex?: number;
9
+ clearFilters?: () => void;
10
+ firstPage?: () => void;
7
11
  }): string | number | bigint | boolean | import("react/jsx-runtime").JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | null;
@@ -1,11 +1,12 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { Button } from "../primitives/button.js";
3
- export function TableInitialState({ status, hasRows, loadingState, emptyState, error, retry, }) {
3
+ export function TableInitialState({ status, hasRows, loadingState, emptyState, error, retry, hasFilters, pageIndex = 0, clearFilters, firstPage, }) {
4
4
  if (hasRows)
5
5
  return null;
6
6
  if (status === "loading")
7
- return (loadingState ?? (_jsx("div", { role: "status", "aria-label": "Loading rows", className: "mui-b8ceaab118a4 mui-094f5333853b", children: Array.from({ length: 8 }, (_, index) => (_jsx("div", { className: "mui-1fb07b38c63b mui-7b3cd77eb999 mui-01024deec75e mui-87ecb59f2d2d mui-237747164b60" }, index))) })));
7
+ return loadingState ?? null;
8
8
  if (status === "error")
9
9
  return (_jsxs("div", { role: "alert", className: "mui-c432836760e3 mui-05ab12448317", children: [error ?? "Could not load rows.", retry && (_jsx(Button, { variant: "outline", size: "sm", onClick: retry, children: "Retry" }))] }));
10
- return (_jsx("div", { role: "status", className: "mui-c7902d77ad80 mui-05ab12448317 mui-35f35c41d134", children: emptyState ?? "No results." }));
10
+ return (_jsx("div", { role: "status", className: "mui-222f930b8752 mui-302c0d124a94 mui-71556df3b421 mui-074569488cca mui-c7902d77ad80 mui-05ab12448317 mui-35f35c41d134", children: emptyState ??
11
+ (pageIndex > 0 ? (_jsxs(_Fragment, { children: [_jsx("span", { children: "No results on this page." }), _jsx(Button, { variant: "outline", size: "sm", onClick: firstPage, children: "Go to first page" })] })) : hasFilters ? (_jsxs(_Fragment, { children: [_jsx("span", { children: "No results match your filters." }), clearFilters && (_jsx(Button, { variant: "outline", size: "sm", onClick: clearFilters, children: "Clear filters" }))] })) : ("No rows yet.")) }));
11
12
  }
@@ -0,0 +1,8 @@
1
+ import type { ComponentProps } from "react";
2
+ import { FilterBar } from "../filters/filter-bar.js";
3
+ export type TableFiltersProps = ComponentProps<typeof FilterBar> & {
4
+ /** Hide the free-text search when the query only supports defined filters. */
5
+ showSearch?: boolean;
6
+ };
7
+ /** Use the same controller here and on TableView when composing a custom toolbar. */
8
+ export declare function TableFilters({ showSearch, ...props }: TableFiltersProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,9 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { FilterBar, FilterRoot, FilterMenu, FilterList, FilterFeedback, } from "../filters/filter-bar.js";
4
+ /** Use the same controller here and on TableView when composing a custom toolbar. */
5
+ export function TableFilters({ showSearch = true, ...props }) {
6
+ if (showSearch)
7
+ return _jsx(FilterBar, { ...props });
8
+ return (_jsxs(FilterRoot, { ...props, children: [_jsx(FilterMenu, {}), _jsx(FilterList, { showClear: props.showClear }), _jsx(FilterFeedback, {})] }));
9
+ }
@@ -0,0 +1,10 @@
1
+ import type { ReactNode } from "react";
2
+ /** Shared sizing for the composed view and the table with built-in controls. */
3
+ export declare function TableFrame({ layout, stretch, slot, header, footer, children, }: {
4
+ layout: "content" | "fill";
5
+ stretch?: boolean;
6
+ slot?: string;
7
+ header?: ReactNode;
8
+ footer?: ReactNode;
9
+ children: ReactNode;
10
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,10 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { cn } from "../utils.js";
4
+ import { useFillLayout } from "./use-fill-layout.js";
5
+ /** Shared sizing for the composed view and the table with built-in controls. */
6
+ export function TableFrame({ layout, stretch = false, slot = "table-view", header, footer, children, }) {
7
+ const fill = layout === "fill";
8
+ const ref = useFillLayout(fill);
9
+ return (_jsxs("div", { ref: ref, "data-mendy-ui": "", "data-slot": slot, "data-layout": layout, className: cn("mui-184ddc11e5f9", fill ? "mui-222f930b8752 mui-410da8dfa8ac mui-302c0d124a94 mui-074569488cca" : "mui-267171770524"), style: !fill && stretch ? { height: "100%" } : undefined, children: [header && _jsx("div", { className: "mui-27ead27a81df", children: header }), _jsx("div", { className: fill ? "mui-0f2a693e93e2 mui-410da8dfa8ac mui-7bd5bab6d7f4 mui-b5985369ee35" : undefined, style: !fill && stretch ? { height: "100%" } : undefined, children: children }), footer && _jsx("div", { className: "mui-27ead27a81df mui-267171770524", children: footer })] }));
10
+ }
@@ -0,0 +1,44 @@
1
+ import type { CSSProperties } from "react";
2
+ import type { Column } from "@tanstack/react-table";
3
+ import type { DataTableFeatures } from "./features.js";
4
+ import type { DataTableInstance } from "./use-data-table.js";
5
+ /** Keep headers, skeletons, and loaded cells on the same column geometry. */
6
+ export declare function tableLayout<T extends object>(table: DataTableInstance<T>, viewportWidth: number | null, rowHeight: number): {
7
+ columns: Column<{
8
+ cellSelectionFeature: import("@tanstack/react-table").TableFeature;
9
+ columnResizingFeature: import("@tanstack/react-table").TableFeature;
10
+ columnOrderingFeature: import("@tanstack/react-table").TableFeature;
11
+ columnPinningFeature: import("@tanstack/react-table").TableFeature;
12
+ columnSizingFeature: import("@tanstack/react-table").TableFeature;
13
+ columnVisibilityFeature: import("@tanstack/react-table").TableFeature;
14
+ columnFilteringFeature: import("@tanstack/react-table").TableFeature;
15
+ columnFacetingFeature: import("@tanstack/react-table").TableFeature;
16
+ globalFilteringFeature: import("@tanstack/react-table").TableFeature;
17
+ rowPaginationFeature: import("@tanstack/react-table").TableFeature;
18
+ rowSelectionFeature: import("@tanstack/react-table").TableFeature;
19
+ rowSortingFeature: import("@tanstack/react-table").TableFeature;
20
+ filteredRowModel: (table: import("@tanstack/react-table").Table<any, any>) => () => import("@tanstack/react-table").RowModel<any, any>;
21
+ sortedRowModel: (table: import("@tanstack/react-table").Table<any, any>) => () => import("@tanstack/react-table").RowModel<any, any>;
22
+ paginatedRowModel: (table: import("@tanstack/react-table").Table<any, any>) => () => import("@tanstack/react-table").RowModel<any, any>;
23
+ facetedRowModel: (table: import("@tanstack/react-table").Table<any, any>, columnId: string) => () => import("@tanstack/react-table").RowModel<any, any>;
24
+ facetedUniqueValues: (table: import("@tanstack/react-table").Table<import("@tanstack/react-table").TableFeatures, any>, columnId: string) => () => Map<any, number>;
25
+ filterFns: {
26
+ includesString: import("@tanstack/react-table").CreatedFilterFn<any, any>;
27
+ inNumberRange: import("@tanstack/react-table").CreatedFilterFn<any, any>;
28
+ inDateRange: import("@tanstack/react-table").CreatedFilterFn<any, any>;
29
+ equals: import("@tanstack/react-table").CreatedFilterFn<any, any>;
30
+ arrIncludes: import("@tanstack/react-table").CreatedFilterFn<any, any>;
31
+ weakEquals: import("@tanstack/react-table").CreatedFilterFn<any, any>;
32
+ };
33
+ sortFns: {
34
+ alphanumeric: import("@tanstack/react-table").CreatedSortFn<any, any>;
35
+ basic: import("@tanstack/react-table").CreatedSortFn<any, any>;
36
+ datetime: import("@tanstack/react-table").CreatedSortFn<any, any>;
37
+ text: import("@tanstack/react-table").CreatedSortFn<any, any>;
38
+ };
39
+ }, T, unknown>[];
40
+ totalWidth: number;
41
+ pinningActive: boolean;
42
+ cellStyle: (column: Column<DataTableFeatures, T>) => CSSProperties;
43
+ offsets: number[];
44
+ };
@@ -0,0 +1,68 @@
1
+ import { distributeColumnWidths } from "./column-widths.js";
2
+ /** Keep headers, skeletons, and loaded cells on the same column geometry. */
3
+ export function tableLayout(table, viewportWidth, rowHeight) {
4
+ const columns = [
5
+ ...table.getStartVisibleLeafColumns(),
6
+ ...table.getCenterVisibleLeafColumns(),
7
+ ...table.getEndVisibleLeafColumns(),
8
+ ];
9
+ const widths = distributeColumnWidths(columns.map((column) => {
10
+ const definition = column.columnDef;
11
+ return {
12
+ id: column.id,
13
+ size: column.getSize(),
14
+ maxSize: definition.maxSize ?? 1200,
15
+ grow: Object.hasOwn(table.state.columnSizing, column.id)
16
+ ? false
17
+ : (definition.grow ?? (column.accessorFn ? 1 : false)),
18
+ };
19
+ }), viewportWidth);
20
+ const size = (column) => widths.get(column.id);
21
+ const totalWidth = columns.reduce((sum, column) => sum + size(column), 0);
22
+ const pinnedWidth = [
23
+ ...table.getStartVisibleLeafColumns(),
24
+ ...table.getEndVisibleLeafColumns(),
25
+ ].reduce((sum, column) => sum + size(column), 0);
26
+ // Preserve stored pins, but leave room to reach the middle columns on narrow screens.
27
+ const pinningActive = (viewportWidth ?? Infinity) >= Math.min(totalWidth, pinnedWidth + 120);
28
+ const positions = new Map();
29
+ let offset = 0;
30
+ for (const column of table.getStartVisibleLeafColumns()) {
31
+ positions.set(column.id, { side: "left", offset });
32
+ offset += size(column);
33
+ }
34
+ offset = 0;
35
+ for (const column of [...table.getEndVisibleLeafColumns()].reverse()) {
36
+ positions.set(column.id, { side: "right", offset });
37
+ offset += size(column);
38
+ }
39
+ const styles = new Map();
40
+ const offsets = [0];
41
+ for (const column of columns) {
42
+ offsets.push(offsets.at(-1) + size(column));
43
+ const pin = pinningActive ? positions.get(column.id) : undefined;
44
+ styles.set(column.id, {
45
+ width: size(column),
46
+ minWidth: size(column),
47
+ height: rowHeight,
48
+ ...(column.id === table.getEndVisibleLeafColumns()[0]?.id
49
+ ? { marginInlineStart: "auto" }
50
+ : {}),
51
+ ...(pin
52
+ ? {
53
+ position: "sticky",
54
+ [pin.side]: pin.offset,
55
+ zIndex: 2,
56
+ ...(column.id === table.getStartVisibleLeafColumns().at(-1)?.id
57
+ ? { borderRightWidth: 4 }
58
+ : {}),
59
+ ...(column.id === table.getEndVisibleLeafColumns()[0]?.id
60
+ ? { borderLeftWidth: 4 }
61
+ : {}),
62
+ }
63
+ : {}),
64
+ });
65
+ }
66
+ const cellStyle = (column) => styles.get(column.id);
67
+ return { columns, totalWidth, pinningActive, cellStyle, offsets };
68
+ }
@@ -0,0 +1,12 @@
1
+ import type { CSSProperties } from "react";
2
+ import type { Column } from "@tanstack/react-table";
3
+ import type { DataTableFeatures } from "./features.js";
4
+ /** Loading shares the live column geometry and its single scroll container. */
5
+ export declare function TableLoadingRows<T extends object>({ columns, columnGaps, count, width, rowHeight, cellStyle, }: {
6
+ columns: Column<DataTableFeatures, T>[];
7
+ columnGaps: Map<string, number>;
8
+ count: number;
9
+ width: number;
10
+ rowHeight: number;
11
+ cellStyle: (column: Column<DataTableFeatures, T>) => CSSProperties;
12
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Fragment } from "react";
3
+ /** Loading shares the live column geometry and its single scroll container. */
4
+ export function TableLoadingRows({ columns, columnGaps, count, width, rowHeight, cellStyle, }) {
5
+ return (_jsx("div", { role: "status", "aria-label": "Loading rows", className: "mui-2bf6510f1330", style: { width }, children: Array.from({ length: count }, (_, index) => (_jsx("div", { "aria-hidden": "true", "data-slot": "table-loading-row", className: "mui-222f930b8752 mui-2bf6510f1330", style: { height: rowHeight }, children: columns.map((column) => (_jsxs(Fragment, { children: [columnGaps.has(column.id) && (_jsx("div", { "aria-hidden": "true", className: "mui-27ead27a81df", style: { width: columnGaps.get(column.id) } })), _jsx("div", { "data-slot": "table-loading-cell", "data-column-id": column.id, className: "mui-222f930b8752 mui-27ead27a81df mui-71556df3b421 mui-bbe39cfb5cc6 mui-3b6d5fc7b061 mui-5b272f3c5076 mui-0f9e6672913a", style: cellStyle(column), children: _jsx("div", { className: "mui-2edce89ef182 mui-56bb14cbd1fe mui-7b3cd77eb999 mui-02e603944040 mui-87ecb59f2d2d mui-237747164b60" }) }, column.id)] }, column.id))) }, index))) }));
6
+ }
@@ -2,23 +2,41 @@ import type { CSSProperties, ReactNode } from "react";
2
2
  import type { Column, Header, Row } from "@tanstack/react-table";
3
3
  import type { DataTableFeatures } from "./features.js";
4
4
  import type { DataTableInstance } from "./use-data-table.js";
5
- export declare function TableHeaderCell<T extends object>({ table, header, index, style, renderHeader, contentClassName, }: {
5
+ export declare function TableHeaderCell<T extends object>({ table, header, index, style, renderHeader, contentClassName, selectLoaded, }: {
6
6
  table: DataTableInstance<T>;
7
7
  header: Header<DataTableFeatures, T, unknown>;
8
8
  index: number;
9
9
  style: CSSProperties;
10
10
  renderHeader?: (header: Header<DataTableFeatures, T, unknown>) => ReactNode;
11
11
  contentClassName?: string;
12
+ selectLoaded?: {
13
+ checked: boolean | "indeterminate";
14
+ disabled: boolean;
15
+ };
12
16
  }): import("react/jsx-runtime").JSX.Element;
13
- export declare function TableBodyRow<T extends object>({ row, rowIndex, start, rowHeight, cellStyle, focusedId, copied, contentClassName, onRowActivate, isRowHighlighted, }: {
17
+ declare function TableBodyRowImpl<T extends object>({ row, rowIndex, start, rowHeight, autoRowHeight, measureElement, rowClassName, renderRowDetail, detailWidth, cellStyle, columns, columnGaps, columnIndexes, contentVersion, contentState, focusedId, copied, contentClassName, onRowClick, onRowActivate, isRowHighlighted, inlineSelection, }: {
14
18
  row: Row<DataTableFeatures, T>;
15
19
  rowIndex: number;
16
20
  start: number;
17
21
  rowHeight: number;
22
+ autoRowHeight: boolean;
23
+ measureElement?: (element: HTMLDivElement | null) => void;
24
+ rowClassName?: (row: T) => string | undefined;
25
+ renderRowDetail?: (row: T) => ReactNode;
26
+ detailWidth: number;
18
27
  cellStyle: (column: Column<DataTableFeatures, T>) => CSSProperties;
28
+ columns: Column<DataTableFeatures, T>[];
29
+ columnGaps: Map<string, number>;
30
+ columnIndexes: Map<string, number>;
31
+ contentVersion: unknown;
32
+ contentState: unknown;
19
33
  focusedId?: string;
20
34
  copied: boolean;
21
35
  contentClassName?: string;
36
+ onRowClick?: (row: T) => void;
22
37
  onRowActivate?: (row: T) => void;
23
38
  isRowHighlighted?: (row: T) => boolean;
39
+ inlineSelection: boolean;
24
40
  }): import("react/jsx-runtime").JSX.Element;
41
+ export declare const TableBodyRow: typeof TableBodyRowImpl;
42
+ export {};
@@ -1,67 +1,121 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { flexRender } from "@tanstack/react-table";
3
+ import { Fragment, memo, useMemo } from "react";
4
+ import { constructCell, flexRender } from "@tanstack/react-table";
4
5
  import { ArrowDown, ArrowUp } from "lucide-react";
5
6
  import { Button } from "../primitives/button.js";
7
+ import { Checkbox } from "../primitives/checkbox.js";
6
8
  import { cn } from "../utils.js";
7
9
  import { interactiveSelector } from "./clipboard.js";
8
- export function TableHeaderCell({ table, header, index, style, renderHeader, contentClassName, }) {
10
+ export function TableHeaderCell({ table, header, index, style, renderHeader, contentClassName, selectLoaded, }) {
9
11
  const column = header.column;
10
12
  const definition = column.columnDef;
11
13
  const title = definition.label ?? (typeof definition.header === "string" ? definition.header : column.id);
12
14
  const sorted = column.getIsSorted();
13
- return (_jsxs("div", { role: "columnheader", "aria-colindex": index + 1, "aria-sort": sorted === "asc" ? "ascending" : sorted === "desc" ? "descending" : undefined, style: style, className: "mui-d2d9e1f13413 mui-222f930b8752 mui-27ead27a81df mui-71556df3b421 mui-19e97f3f40e6 mui-3b6d5fc7b061 mui-5b272f3c5076 mui-0f9e6672913a mui-daaac3fbf55e mui-35f35c41d134 mui-beeef6663906", children: [_jsx("div", { className: cn("mui-184ddc11e5f9 mui-7bd5bab6d7f4 mui-d5111d0e9f48", (renderHeader || typeof definition.header === "function") && contentClassName), children: _jsx(TableHeaderContent, { header: header, renderHeader: renderHeader, title: title }) }), column.getCanResize() && _jsx(TableResizeHandle, { table: table, header: header, title: title })] }, column.id));
15
+ return (_jsxs("div", { role: "columnheader", "data-column-id": column.id, "aria-colindex": index + 1, "aria-sort": sorted === "asc" ? "ascending" : sorted === "desc" ? "descending" : undefined, style: style, className: "mui-ad936fcbed63 mui-d2d9e1f13413 mui-222f930b8752 mui-27ead27a81df mui-71556df3b421 mui-19e97f3f40e6 mui-3b6d5fc7b061 mui-5b272f3c5076 mui-0f9e6672913a mui-daaac3fbf55e mui-35f35c41d134", children: [selectLoaded && index === 0 && (_jsx(Checkbox, { "aria-label": "Select loaded rows", disabled: selectLoaded.disabled, checked: selectLoaded.checked, onCheckedChange: (value) => table.toggleAllPageRowsSelected(value === true), className: "mui-477d8f4dfce9 mui-94f91922a10e mui-34a57d4b4103 mui-8da02faac748 mui-b8b1747fa763 mui-d2e4a66bb430 mui-978f422895e5 mui-d0c2ae8419e1 mui-e2ed522ed9b7 mui-fa8e1efead3c mui-eaedb91f8774" })), _jsx("div", { className: cn("mui-184ddc11e5f9 mui-7bd5bab6d7f4 mui-d5111d0e9f48", (renderHeader || typeof definition.header === "function") && contentClassName), children: _jsx(TableHeaderContent, { header: header, renderHeader: renderHeader, title: title }) }), column.getCanResize() && (_jsx(TableResizeHandle, { table: table, header: header, title: title, width: Number(style.width) }))] }, column.id));
14
16
  }
15
- export function TableBodyRow({ row, rowIndex, start, rowHeight, cellStyle, focusedId, copied, contentClassName, onRowActivate, isRowHighlighted, }) {
16
- const cells = [
17
- ...row.getStartVisibleCells(),
18
- ...row.getCenterVisibleCells(),
19
- ...row.getEndVisibleCells(),
20
- ];
21
- return (_jsx("div", { role: "row", "aria-rowindex": rowIndex + 2, "aria-selected": row.getIsSelected(), "data-highlighted": isRowHighlighted?.(row.original), "data-index": rowIndex, className: "mui-ad936fcbed63 mui-747355bdc2a2 mui-635702706586 mui-98599e4ee250 mui-222f930b8752 mui-2bf6510f1330 mui-63b29dace366 mui-9d7d1760cd8e", style: { height: rowHeight, transform: `translateY(${start}px)` }, children: cells.map((cell, index) => {
17
+ function TableBodyRowImpl({ row, rowIndex, start, rowHeight, autoRowHeight, measureElement, rowClassName, renderRowDetail, detailWidth, cellStyle, columns, columnGaps, columnIndexes, contentVersion, contentState, focusedId, copied, contentClassName, onRowClick, onRowActivate, isRowHighlighted, inlineSelection, }) {
18
+ // The engine's getAllCells API eagerly allocates every column. Build only the
19
+ // viewport cells with its public constructor, and release them with this row.
20
+ const cache = useMemo(() => new WeakMap(), [row]);
21
+ const cells = columns.map((column) => {
22
+ let cell = cache.get(column);
23
+ if (!cell) {
24
+ cell = constructCell(column, row, row.table);
25
+ cache.set(column, cell);
26
+ }
27
+ return cell;
28
+ });
29
+ const rowElement = (_jsx("div", { ref: renderRowDetail ? undefined : measureElement, role: "row", "aria-rowindex": renderRowDetail ? undefined : rowIndex + 2, "aria-selected": row.getIsSelected(), "data-highlighted": row.getIsSelected() || isRowHighlighted?.(row.original), "data-index": rowIndex, className: cn("mui-ad936fcbed63 mui-635702706586 mui-222f930b8752 mui-2bf6510f1330 mui-5b272f3c5076 mui-37ad15a95595 mui-9d7d1760cd8e", rowClassName?.(row.original)), style: {
30
+ height: autoRowHeight ? undefined : rowHeight,
31
+ minHeight: rowHeight,
32
+ top: renderRowDetail ? 0 : start,
33
+ position: renderRowDetail ? "relative" : "absolute",
34
+ }, children: cells.map((cell) => {
35
+ const index = columnIndexes.get(cell.column.id);
22
36
  const selected = cell.getIsSelected();
23
37
  const definition = cell.column.columnDef;
24
38
  const edge = selected ? cell.getSelectionEdges() : null;
25
- return (_jsx("div", { role: "gridcell", "aria-colindex": index + 1, "aria-selected": selected, "data-row-id": row.id, "data-column-id": cell.column.id, tabIndex: focusedId ? (focusedId === cell.id ? 0 : -1) : rowIndex === 0 && index === 0 ? 0 : -1, style: {
26
- ...cellStyle(cell.column),
27
- ...(selected
28
- ? {
29
- boxShadow: `${edge?.top ? "inset 0 2px var(--primary)," : ""}${edge?.bottom ? "inset 0 -2px var(--primary)," : ""}${edge?.left ? "inset 2px 0 var(--primary)," : ""}${edge?.right ? "inset -2px 0 var(--primary)," : ""} inset 0 0 0 0 transparent`,
30
- }
31
- : {}),
32
- }, className: cn("mui-222f930b8752 mui-27ead27a81df mui-71556df3b421 mui-d5111d0e9f48 mui-bbe39cfb5cc6 mui-3b6d5fc7b061 mui-5b272f3c5076 mui-0f9e6672913a mui-97b1c00ff005 mui-e6020023567a mui-a47d391255f3 mui-a8719ed8b095 mui-2b1c7d88acff", definition.align === "end" && "mui-307644c34db6 mui-10558dbb3a24", definition.align === "center" && "mui-a503dd374cca mui-05ab12448317", selected && "mui-292affc1f780", selected && copied && "mui-047624ec0c39 mui-d254c96aea74"), onMouseDown: (event) => {
33
- if (event.button !== 0 ||
34
- event.detail > 1 ||
35
- !cell.getCanSelect() ||
36
- event.target.closest(interactiveSelector) ||
37
- window.getSelection()?.toString())
38
- return;
39
- event.preventDefault();
40
- event.stopPropagation();
41
- event.currentTarget.focus({ preventScroll: true });
42
- cell.getSelectionStartHandler()(event);
43
- }, onMouseEnter: cell.getSelectionExtendHandler(), onDoubleClick: (event) => {
44
- if (!event.target.closest(interactiveSelector))
45
- onRowActivate?.(row.original);
46
- }, children: _jsx("div", { className: cn("mui-184ddc11e5f9 mui-81b198a2db84 mui-5a3a4ee420c2", contentClassName), children: flexRender(cell.column.columnDef.cell ?? (() => String(cell.getValue() ?? "")), cell.getContext()) }) }, cell.id));
39
+ return (_jsxs(Fragment, { children: [columnGaps.has(cell.column.id) && (_jsx("div", { "aria-hidden": "true", className: "mui-27ead27a81df", style: { width: columnGaps.get(cell.column.id) } })), _jsxs("div", { role: "gridcell", "aria-colindex": index + 1, "aria-selected": selected, "data-row-id": row.id, "data-column-id": cell.column.id, tabIndex: focusedId
40
+ ? focusedId === cell.id
41
+ ? 0
42
+ : -1
43
+ : rowIndex === 0 && index === 0
44
+ ? 0
45
+ : -1, style: autoRowHeight
46
+ ? { ...cellStyle(cell.column), height: undefined, minHeight: rowHeight }
47
+ : cellStyle(cell.column), className: cn("mui-d2d9e1f13413 mui-222f930b8752 mui-27ead27a81df mui-71556df3b421 mui-bbe39cfb5cc6 mui-3b6d5fc7b061 mui-6ba846e1630e mui-0f9e6672913a", !(selected && copied) && "mui-97b1c00ff005 mui-e6020023567a", !selected && "mui-a47d391255f3 mui-a8719ed8b095 mui-2b1c7d88acff", definition.align === "end" && "mui-307644c34db6 mui-10558dbb3a24", definition.align === "center" && "mui-a503dd374cca mui-05ab12448317", selected && "mui-292affc1f780 mui-b30fc56058b6", selected && copied && "mui-047624ec0c39 mui-d254c96aea74"), onMouseDown: (event) => {
48
+ if (event.button !== 0 ||
49
+ event.detail > 1 ||
50
+ !cell.getCanSelect() ||
51
+ event.target.closest(interactiveSelector) ||
52
+ window.getSelection()?.toString())
53
+ return;
54
+ event.preventDefault();
55
+ event.stopPropagation();
56
+ event.currentTarget.focus({ preventScroll: true });
57
+ cell.getSelectionStartHandler()(event);
58
+ }, onMouseEnter: cell.getSelectionExtendHandler(), onClick: (event) => {
59
+ if (event.detail <= 1 && !event.target.closest(interactiveSelector))
60
+ onRowClick?.(row.original);
61
+ }, onDoubleClick: (event) => {
62
+ if (!event.target.closest(interactiveSelector))
63
+ onRowActivate?.(row.original);
64
+ }, children: [selected && (_jsx("span", { "aria-hidden": "true", "data-selection-outline": "", className: "mui-33228ff7d583 mui-747355bdc2a2 mui-d7efd1b0b0f9 mui-515f9fdc6db7", style: {
65
+ top: edge?.top ? 0 : -1,
66
+ bottom: edge?.bottom ? -1 : -2,
67
+ left: -Number(cellStyle(cell.column).borderLeftWidth ?? 0),
68
+ right: -Number(cellStyle(cell.column).borderRightWidth ?? 1),
69
+ borderTopWidth: edge?.top ? 2 : 0,
70
+ borderBottomWidth: edge?.bottom ? 2 : 0,
71
+ borderLeftWidth: edge?.left ? 2 : 0,
72
+ borderRightWidth: edge?.right ? 2 : 0,
73
+ } })), inlineSelection && index === 0 && (_jsx(Checkbox, { "aria-label": `Select row ${rowIndex + 1}`, checked: row.getIsSelected(), disabled: !row.getCanSelect(), onCheckedChange: (value) => row.toggleSelected(value === true), onMouseDown: (event) => event.stopPropagation(), onClick: (event) => event.stopPropagation(), className: "mui-68d781378b03 mui-94f91922a10e mui-34a57d4b4103 mui-8da02faac748 mui-b8b1747fa763 mui-d2e4a66bb430 mui-978f422895e5 mui-d0c2ae8419e1 mui-9c02852bfc55 mui-e2ed522ed9b7 mui-cbd3aed703f7 mui-fa8e1efead3c mui-eaedb91f8774" })), _jsx("div", { className: cn("mui-184ddc11e5f9 mui-81b198a2db84", autoRowHeight ? "mui-726fca123972 mui-37cf9655f30a mui-b5edc3ea7c91" : "mui-7bd696ca6be3 mui-5a3a4ee420c2", contentClassName), children: _jsx(TableCellContent, { cell: cell, renderer: cell.column.columnDef.cell, version: contentVersion, state: contentState }) })] }, cell.id)] }, cell.id));
47
74
  }) }, row.id));
75
+ if (!renderRowDetail)
76
+ return rowElement;
77
+ const detail = renderRowDetail(row.original);
78
+ return (_jsxs("div", { ref: measureElement, "data-index": rowIndex, role: "rowgroup", className: "mui-747355bdc2a2 mui-635702706586 mui-2bf6510f1330", style: { top: start }, children: [rowElement, detail != null && (_jsx("div", { role: "row", className: "mui-bbe39cfb5cc6 mui-5b272f3c5076", children: _jsx("div", { role: "gridcell", "aria-colspan": columns.length, className: "mui-964a9431ff49 mui-184ddc11e5f9 mui-094f5333853b", style: { width: detailWidth, maxWidth: "100%", insetInlineStart: 0 }, children: detail }) }))] }));
48
79
  }
49
- function TableResizeHandle({ table, header, title, }) {
80
+ export const TableBodyRow = memo(TableBodyRowImpl);
81
+ function TableResizeHandle({ table, header, title, width, }) {
50
82
  const column = header.column;
51
- return (_jsx("div", { role: "separator", tabIndex: 0, "aria-label": `Resize ${title}`, "aria-orientation": "vertical", "aria-valuenow": Math.round(column.getSize()), "aria-valuemin": column.columnDef.minSize ?? 48, "aria-valuemax": column.columnDef.maxSize ?? 1200, onDoubleClick: () => column.resetSize(), onMouseDown: header.getResizeHandler(), onTouchStart: header.getResizeHandler(), onKeyDown: (event) => {
83
+ const beginResize = (event) => {
84
+ if ("touches" in event && event.touches.length > 1)
85
+ return;
86
+ if ("button" in event) {
87
+ if (event.button !== 0)
88
+ return;
89
+ event.preventDefault();
90
+ event.currentTarget.focus({ preventScroll: true });
91
+ }
92
+ // Start dragging from the visible width, not the smaller configured base width.
93
+ header.getResizeHandler()(event);
94
+ table.setColumnSizing((sizes) => ({ ...sizes, [column.id]: width }));
95
+ table.setColumnResizing((state) => ({
96
+ ...state,
97
+ startSize: width,
98
+ columnSizingStart: [[column.id, width]],
99
+ }));
100
+ };
101
+ return (_jsx("div", { role: "separator", tabIndex: 0, "aria-label": `Resize ${title}`, "aria-orientation": "vertical", "aria-valuenow": Math.round(width), "aria-valuemin": column.columnDef.minSize ?? 48, "aria-valuemax": column.columnDef.maxSize ?? 1200, onDoubleClick: () => column.resetSize(), onMouseDown: beginResize, onTouchStart: beginResize, onKeyDown: (event) => {
52
102
  if (event.key === "ArrowLeft" || event.key === "ArrowRight") {
53
103
  event.preventDefault();
54
104
  event.stopPropagation();
55
105
  table.setColumnSizing((sizes) => ({
56
106
  ...sizes,
57
- [column.id]: Math.min(column.columnDef.maxSize ?? 1200, Math.max(column.columnDef.minSize ?? 48, column.getSize() + (event.key === "ArrowLeft" ? -10 : 10))),
107
+ [column.id]: Math.min(column.columnDef.maxSize ?? 1200, Math.max(column.columnDef.minSize ?? 48, width + (event.key === "ArrowLeft" ? -10 : 10))),
58
108
  }));
59
109
  }
60
- }, className: "mui-747355bdc2a2 mui-eb9e48c5dda9 mui-a74c4477017c mui-418e44ad4f9e mui-d4ea834145ae mui-2194d094b585 mui-739a9a000791 mui-4273133a876a mui-2f669cfad5aa mui-2e357972fc10" }));
110
+ }, className: "mui-747355bdc2a2 mui-eb9e48c5dda9 mui-a74c4477017c mui-418e44ad4f9e mui-d4ea834145ae mui-2194d094b585 mui-739a9a000791 mui-78905c9e865c mui-4273133a876a mui-2f669cfad5aa mui-2e357972fc10" }));
61
111
  }
62
112
  function TableHeaderContent({ header, renderHeader, title, }) {
63
113
  const column = header.column;
64
114
  const definition = column.columnDef;
65
115
  const sorted = column.getIsSorted();
66
- return renderHeader ? (renderHeader(header)) : typeof definition.header === "function" ? (flexRender(definition.header, header.getContext())) : !column.getCanSort() ? (_jsx("span", { className: "mui-496aca80e4d8 mui-5a3a4ee420c2", title: title, children: title })) : (_jsxs(Button, { variant: "ghost", size: "sm", className: "mui-1fb07b38c63b mui-81b198a2db84 mui-c62ec162662e mui-19e97f3f40e6 mui-16e909e6f01d mui-daaac3fbf55e", onClick: column.getToggleSortingHandler(), title: title, children: [_jsx("span", { className: "mui-5a3a4ee420c2", children: title }), sorted === "asc" ? (_jsx(ArrowUp, { className: "mui-5e34f5313859 mui-27ead27a81df" })) : sorted === "desc" ? (_jsx(ArrowDown, { className: "mui-5e34f5313859 mui-27ead27a81df" })) : null] }));
116
+ return renderHeader ? (renderHeader(header)) : typeof definition.header === "function" ? (flexRender(definition.header, header.getContext())) : !column.getCanSort() ? (_jsx("span", { className: "mui-496aca80e4d8 mui-5a3a4ee420c2", title: title, children: title })) : (_jsxs(Button, { variant: "ghost", size: "sm", className: "mui-1fb07b38c63b mui-81b198a2db84 mui-c62ec162662e mui-19e97f3f40e6 mui-16e909e6f01d mui-daaac3fbf55e mui-35f35c41d134 mui-71863717995b mui-4d6720d47806", onClick: column.getToggleSortingHandler(), title: title, children: [_jsx("span", { className: "mui-5a3a4ee420c2", children: title }), sorted === "asc" ? (_jsx(ArrowUp, { className: "mui-5e34f5313859 mui-27ead27a81df" })) : sorted === "desc" ? (_jsx(ArrowDown, { className: "mui-5e34f5313859 mui-27ead27a81df" })) : null] }));
67
117
  }
118
+ // Geometry-only updates do not need to rerun application-owned cell renderers.
119
+ const TableCellContent = memo(function TableCellContent({ cell, renderer, }) {
120
+ return renderer ? flexRender(renderer, cell.getContext()) : String(cell.getValue() ?? "");
121
+ });
@@ -2,6 +2,7 @@ import type { ReactNode, Ref } from "react";
2
2
  import type { Header } from "@tanstack/react-table";
3
3
  import type { DataTableFeatures } from "./features.js";
4
4
  import type { DataTableInstance } from "./use-data-table.js";
5
+ import type { FilterController } from "../filters/use-filters.js";
5
6
  export interface TableDataState {
6
7
  status?: "loading" | "ready" | "error";
7
8
  error?: ReactNode;
@@ -17,8 +18,17 @@ export interface TableDataState {
17
18
  export interface TableViewProps<T extends object> extends TableDataState {
18
19
  table: DataTableInstance<T>;
19
20
  label?: string;
21
+ /** Defaults to "auto": fit the rows, capped at 65% of the viewport height. */
20
22
  height?: number | string;
21
- rowHeight?: number;
23
+ /** Fill the remaining page height, or fit content by default. Fill overrides height. */
24
+ layout?: "content" | "fill";
25
+ /** Controls below the grid, included in the available height when layout is fill. */
26
+ footer?: ReactNode;
27
+ /** Auto measures multiline rows and retains all columns to keep their height stable. */
28
+ rowHeight?: number | "auto";
29
+ rowClassName?: (row: T) => string | undefined;
30
+ /** Expanded content beneath a record. Return null for collapsed records. */
31
+ renderRowDetail?: (row: T) => ReactNode;
22
32
  className?: string;
23
33
  /** Theme adapter for application-owned renderers, not package controls. */
24
34
  contentClassName?: string;
@@ -27,9 +37,16 @@ export interface TableViewProps<T extends object> extends TableDataState {
27
37
  scrollRef?: Ref<HTMLDivElement>;
28
38
  /** Change when the result set changes, not when another page is appended. */
29
39
  queryKey?: string;
40
+ /** Shared filter state for query resets and empty-result recovery. */
41
+ filters?: FilterController;
30
42
  renderHeader?: (header: Header<DataTableFeatures, T, unknown>) => ReactNode;
43
+ /** Single-click action, also used by Enter unless onRowActivate is provided. */
44
+ onRowClick?: (row: T) => void;
45
+ /** Optional double-click and Enter action. Interactive controls keep their own behavior. */
31
46
  onRowActivate?: (row: T) => void;
32
47
  isRowHighlighted?: (row: T) => boolean;
48
+ /** Inline checkboxes for enabled row selection. Set false for custom selection controls. */
49
+ rowSelectionControls?: boolean;
33
50
  onCopyError?: (error: unknown) => void;
34
51
  }
35
- export declare function TableView<T extends object>({ table, label, height, rowHeight, className, contentClassName, emptyState, loadingState, scrollRef, status, refreshing, error, retry, loadMore, queryKey, renderHeader, onRowActivate, isRowHighlighted, onCopyError, }: TableViewProps<T>): import("react/jsx-runtime").JSX.Element;
52
+ export declare function TableView<T extends object>({ table, label, height, layout: layoutMode, footer, rowHeight: rowHeightOption, rowClassName, renderRowDetail, className, contentClassName, emptyState, loadingState, scrollRef, status, refreshing, error, retry, loadMore, queryKey, filters, renderHeader, onRowClick, onRowActivate, isRowHighlighted, rowSelectionControls, onCopyError, }: TableViewProps<T>): import("react/jsx-runtime").JSX.Element;