@inui/design-system 0.0.0-stage → 1.22.0
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.
- package/README.md +360 -2
- package/dist/components/ui/accordion.d.ts +44 -0
- package/dist/components/ui/accordion.d.ts.map +1 -0
- package/dist/components/ui/accordion.js +86 -0
- package/dist/components/ui/accordion.js.map +1 -0
- package/dist/components/ui/ai-avatar.d.ts +46 -0
- package/dist/components/ui/ai-avatar.d.ts.map +1 -0
- package/dist/components/ui/ai-avatar.js +43 -0
- package/dist/components/ui/ai-avatar.js.map +1 -0
- package/dist/components/ui/ai-label.d.ts +118 -0
- package/dist/components/ui/ai-label.d.ts.map +1 -0
- package/dist/components/ui/ai-label.js +184 -0
- package/dist/components/ui/ai-label.js.map +1 -0
- package/dist/components/ui/ai-sparkle.d.ts +91 -0
- package/dist/components/ui/ai-sparkle.d.ts.map +1 -0
- package/dist/components/ui/ai-sparkle.js +200 -0
- package/dist/components/ui/ai-sparkle.js.map +1 -0
- package/dist/components/ui/alert-dialog.d.ts +79 -0
- package/dist/components/ui/alert-dialog.d.ts.map +1 -0
- package/dist/components/ui/alert-dialog.js +147 -0
- package/dist/components/ui/alert-dialog.js.map +1 -0
- package/dist/components/ui/alert.d.ts +71 -0
- package/dist/components/ui/alert.d.ts.map +1 -0
- package/dist/components/ui/alert.js +108 -0
- package/dist/components/ui/alert.js.map +1 -0
- package/dist/components/ui/aspect-ratio.d.ts +30 -0
- package/dist/components/ui/aspect-ratio.d.ts.map +1 -0
- package/dist/components/ui/aspect-ratio.js +40 -0
- package/dist/components/ui/aspect-ratio.js.map +1 -0
- package/dist/components/ui/attachment.d.ts +74 -0
- package/dist/components/ui/attachment.d.ts.map +1 -0
- package/dist/components/ui/attachment.js +142 -0
- package/dist/components/ui/attachment.js.map +1 -0
- package/dist/components/ui/avatar.d.ts +82 -0
- package/dist/components/ui/avatar.d.ts.map +1 -0
- package/dist/components/ui/avatar.js +123 -0
- package/dist/components/ui/avatar.js.map +1 -0
- package/dist/components/ui/badge.d.ts +130 -0
- package/dist/components/ui/badge.d.ts.map +1 -0
- package/dist/components/ui/badge.js +226 -0
- package/dist/components/ui/badge.js.map +1 -0
- package/dist/components/ui/breadcrumb.d.ts +43 -0
- package/dist/components/ui/breadcrumb.d.ts.map +1 -0
- package/dist/components/ui/breadcrumb.js +98 -0
- package/dist/components/ui/breadcrumb.js.map +1 -0
- package/dist/components/ui/browser-tabs.d.ts +218 -0
- package/dist/components/ui/browser-tabs.d.ts.map +1 -0
- package/dist/components/ui/browser-tabs.js +528 -0
- package/dist/components/ui/browser-tabs.js.map +1 -0
- package/dist/components/ui/bubble.d.ts +80 -0
- package/dist/components/ui/bubble.d.ts.map +1 -0
- package/dist/components/ui/bubble.js +235 -0
- package/dist/components/ui/bubble.js.map +1 -0
- package/dist/components/ui/button-group.d.ts +60 -0
- package/dist/components/ui/button-group.d.ts.map +1 -0
- package/dist/components/ui/button-group.js +89 -0
- package/dist/components/ui/button-group.js.map +1 -0
- package/dist/components/ui/button.d.ts +157 -0
- package/dist/components/ui/button.d.ts.map +1 -0
- package/dist/components/ui/button.js +144 -0
- package/dist/components/ui/button.js.map +1 -0
- package/dist/components/ui/calendar.d.ts +57 -0
- package/dist/components/ui/calendar.d.ts.map +1 -0
- package/dist/components/ui/calendar.js +163 -0
- package/dist/components/ui/calendar.js.map +1 -0
- package/dist/components/ui/card.d.ts +96 -0
- package/dist/components/ui/card.d.ts.map +1 -0
- package/dist/components/ui/card.js +131 -0
- package/dist/components/ui/card.js.map +1 -0
- package/dist/components/ui/carousel.d.ts +86 -0
- package/dist/components/ui/carousel.d.ts.map +1 -0
- package/dist/components/ui/carousel.js +207 -0
- package/dist/components/ui/carousel.js.map +1 -0
- package/dist/components/ui/checkbox.d.ts +54 -0
- package/dist/components/ui/checkbox.d.ts.map +1 -0
- package/dist/components/ui/checkbox.js +78 -0
- package/dist/components/ui/checkbox.js.map +1 -0
- package/dist/components/ui/collapsible.d.ts +45 -0
- package/dist/components/ui/collapsible.d.ts.map +1 -0
- package/dist/components/ui/collapsible.js +65 -0
- package/dist/components/ui/collapsible.js.map +1 -0
- package/dist/components/ui/combobox.d.ts +111 -0
- package/dist/components/ui/combobox.d.ts.map +1 -0
- package/dist/components/ui/combobox.js +264 -0
- package/dist/components/ui/combobox.js.map +1 -0
- package/dist/components/ui/command.d.ts +71 -0
- package/dist/components/ui/command.d.ts.map +1 -0
- package/dist/components/ui/command.js +133 -0
- package/dist/components/ui/command.js.map +1 -0
- package/dist/components/ui/context-menu.d.ts +70 -0
- package/dist/components/ui/context-menu.d.ts.map +1 -0
- package/dist/components/ui/context-menu.js +163 -0
- package/dist/components/ui/context-menu.js.map +1 -0
- package/dist/components/ui/count-badge.d.ts +40 -0
- package/dist/components/ui/count-badge.d.ts.map +1 -0
- package/dist/components/ui/count-badge.js +43 -0
- package/dist/components/ui/count-badge.js.map +1 -0
- package/dist/components/ui/data-table.d.ts +1026 -0
- package/dist/components/ui/data-table.d.ts.map +1 -0
- package/dist/components/ui/data-table.js +3423 -0
- package/dist/components/ui/data-table.js.map +1 -0
- package/dist/components/ui/dialog.d.ts +73 -0
- package/dist/components/ui/dialog.d.ts.map +1 -0
- package/dist/components/ui/dialog.js +145 -0
- package/dist/components/ui/dialog.js.map +1 -0
- package/dist/components/ui/direction.d.ts +35 -0
- package/dist/components/ui/direction.d.ts.map +1 -0
- package/dist/components/ui/direction.js +35 -0
- package/dist/components/ui/direction.js.map +1 -0
- package/dist/components/ui/drawer.d.ts +53 -0
- package/dist/components/ui/drawer.d.ts.map +1 -0
- package/dist/components/ui/drawer.js +115 -0
- package/dist/components/ui/drawer.js.map +1 -0
- package/dist/components/ui/dropdown-menu.d.ts +75 -0
- package/dist/components/ui/dropdown-menu.d.ts.map +1 -0
- package/dist/components/ui/dropdown-menu.js +173 -0
- package/dist/components/ui/dropdown-menu.js.map +1 -0
- package/dist/components/ui/dropzone.d.ts +141 -0
- package/dist/components/ui/dropzone.d.ts.map +1 -0
- package/dist/components/ui/dropzone.js +257 -0
- package/dist/components/ui/dropzone.js.map +1 -0
- package/dist/components/ui/empty.d.ts +94 -0
- package/dist/components/ui/empty.d.ts.map +1 -0
- package/dist/components/ui/empty.js +149 -0
- package/dist/components/ui/empty.js.map +1 -0
- package/dist/components/ui/field.d.ts +122 -0
- package/dist/components/ui/field.d.ts.map +1 -0
- package/dist/components/ui/field.js +254 -0
- package/dist/components/ui/field.js.map +1 -0
- package/dist/components/ui/hover-card.d.ts +51 -0
- package/dist/components/ui/hover-card.d.ts.map +1 -0
- package/dist/components/ui/hover-card.js +101 -0
- package/dist/components/ui/hover-card.js.map +1 -0
- package/dist/components/ui/input-group.d.ts +62 -0
- package/dist/components/ui/input-group.d.ts.map +1 -0
- package/dist/components/ui/input-group.js +115 -0
- package/dist/components/ui/input-group.js.map +1 -0
- package/dist/components/ui/input-otp.d.ts +52 -0
- package/dist/components/ui/input-otp.d.ts.map +1 -0
- package/dist/components/ui/input-otp.js +89 -0
- package/dist/components/ui/input-otp.js.map +1 -0
- package/dist/components/ui/input.d.ts +47 -0
- package/dist/components/ui/input.d.ts.map +1 -0
- package/dist/components/ui/input.js +47 -0
- package/dist/components/ui/input.js.map +1 -0
- package/dist/components/ui/item.d.ts +74 -0
- package/dist/components/ui/item.d.ts.map +1 -0
- package/dist/components/ui/item.js +176 -0
- package/dist/components/ui/item.js.map +1 -0
- package/dist/components/ui/kbd.d.ts +41 -0
- package/dist/components/ui/kbd.d.ts.map +1 -0
- package/dist/components/ui/kbd.js +58 -0
- package/dist/components/ui/kbd.js.map +1 -0
- package/dist/components/ui/key-value-pair.d.ts +56 -0
- package/dist/components/ui/key-value-pair.d.ts.map +1 -0
- package/dist/components/ui/key-value-pair.js +110 -0
- package/dist/components/ui/key-value-pair.js.map +1 -0
- package/dist/components/ui/label.d.ts +83 -0
- package/dist/components/ui/label.d.ts.map +1 -0
- package/dist/components/ui/label.js +85 -0
- package/dist/components/ui/label.js.map +1 -0
- package/dist/components/ui/marker.d.ts +54 -0
- package/dist/components/ui/marker.d.ts.map +1 -0
- package/dist/components/ui/marker.js +79 -0
- package/dist/components/ui/marker.js.map +1 -0
- package/dist/components/ui/menubar.d.ts +72 -0
- package/dist/components/ui/menubar.d.ts.map +1 -0
- package/dist/components/ui/menubar.js +174 -0
- package/dist/components/ui/menubar.js.map +1 -0
- package/dist/components/ui/message-scroller.d.ts +53 -0
- package/dist/components/ui/message-scroller.d.ts.map +1 -0
- package/dist/components/ui/message-scroller.js +95 -0
- package/dist/components/ui/message-scroller.js.map +1 -0
- package/dist/components/ui/message.d.ts +63 -0
- package/dist/components/ui/message.d.ts.map +1 -0
- package/dist/components/ui/message.js +92 -0
- package/dist/components/ui/message.js.map +1 -0
- package/dist/components/ui/meta-list.d.ts +55 -0
- package/dist/components/ui/meta-list.d.ts.map +1 -0
- package/dist/components/ui/meta-list.js +75 -0
- package/dist/components/ui/meta-list.js.map +1 -0
- package/dist/components/ui/native-select.d.ts +51 -0
- package/dist/components/ui/native-select.d.ts.map +1 -0
- package/dist/components/ui/native-select.js +75 -0
- package/dist/components/ui/native-select.js.map +1 -0
- package/dist/components/ui/navigation-menu.d.ts +56 -0
- package/dist/components/ui/navigation-menu.d.ts.map +1 -0
- package/dist/components/ui/navigation-menu.js +116 -0
- package/dist/components/ui/navigation-menu.js.map +1 -0
- package/dist/components/ui/page-header.d.ts +223 -0
- package/dist/components/ui/page-header.d.ts.map +1 -0
- package/dist/components/ui/page-header.js +233 -0
- package/dist/components/ui/page-header.js.map +1 -0
- package/dist/components/ui/pagination.d.ts +68 -0
- package/dist/components/ui/pagination.d.ts.map +1 -0
- package/dist/components/ui/pagination.js +145 -0
- package/dist/components/ui/pagination.js.map +1 -0
- package/dist/components/ui/popover.d.ts +61 -0
- package/dist/components/ui/popover.d.ts.map +1 -0
- package/dist/components/ui/popover.js +144 -0
- package/dist/components/ui/popover.js.map +1 -0
- package/dist/components/ui/progress.d.ts +35 -0
- package/dist/components/ui/progress.d.ts.map +1 -0
- package/dist/components/ui/progress.js +46 -0
- package/dist/components/ui/progress.js.map +1 -0
- package/dist/components/ui/questionnaire.d.ts +92 -0
- package/dist/components/ui/questionnaire.d.ts.map +1 -0
- package/dist/components/ui/questionnaire.js +277 -0
- package/dist/components/ui/questionnaire.js.map +1 -0
- package/dist/components/ui/radio-group.d.ts +50 -0
- package/dist/components/ui/radio-group.d.ts.map +1 -0
- package/dist/components/ui/radio-group.js +65 -0
- package/dist/components/ui/radio-group.js.map +1 -0
- package/dist/components/ui/resizable.d.ts +44 -0
- package/dist/components/ui/resizable.d.ts.map +1 -0
- package/dist/components/ui/resizable.js +73 -0
- package/dist/components/ui/resizable.js.map +1 -0
- package/dist/components/ui/scroll-area.d.ts +55 -0
- package/dist/components/ui/scroll-area.d.ts.map +1 -0
- package/dist/components/ui/scroll-area.js +72 -0
- package/dist/components/ui/scroll-area.js.map +1 -0
- package/dist/components/ui/select.d.ts +274 -0
- package/dist/components/ui/select.d.ts.map +1 -0
- package/dist/components/ui/select.js +609 -0
- package/dist/components/ui/select.js.map +1 -0
- package/dist/components/ui/separator.d.ts +31 -0
- package/dist/components/ui/separator.d.ts.map +1 -0
- package/dist/components/ui/separator.js +38 -0
- package/dist/components/ui/separator.js.map +1 -0
- package/dist/components/ui/sheet.d.ts +59 -0
- package/dist/components/ui/sheet.d.ts.map +1 -0
- package/dist/components/ui/sheet.js +128 -0
- package/dist/components/ui/sheet.js.map +1 -0
- package/dist/components/ui/sidebar.d.ts +166 -0
- package/dist/components/ui/sidebar.d.ts.map +1 -0
- package/dist/components/ui/sidebar.js +512 -0
- package/dist/components/ui/sidebar.js.map +1 -0
- package/dist/components/ui/skeleton.d.ts +35 -0
- package/dist/components/ui/skeleton.d.ts.map +1 -0
- package/dist/components/ui/skeleton.js +46 -0
- package/dist/components/ui/skeleton.js.map +1 -0
- package/dist/components/ui/slider.d.ts +39 -0
- package/dist/components/ui/slider.d.ts.map +1 -0
- package/dist/components/ui/slider.js +75 -0
- package/dist/components/ui/slider.js.map +1 -0
- package/dist/components/ui/sonner.d.ts +56 -0
- package/dist/components/ui/sonner.d.ts.map +1 -0
- package/dist/components/ui/sonner.js +96 -0
- package/dist/components/ui/sonner.js.map +1 -0
- package/dist/components/ui/spinner.d.ts +37 -0
- package/dist/components/ui/spinner.d.ts.map +1 -0
- package/dist/components/ui/spinner.js +51 -0
- package/dist/components/ui/spinner.js.map +1 -0
- package/dist/components/ui/stepper.d.ts +84 -0
- package/dist/components/ui/stepper.d.ts.map +1 -0
- package/dist/components/ui/stepper.js +145 -0
- package/dist/components/ui/stepper.js.map +1 -0
- package/dist/components/ui/switch.d.ts +36 -0
- package/dist/components/ui/switch.d.ts.map +1 -0
- package/dist/components/ui/switch.js +44 -0
- package/dist/components/ui/switch.js.map +1 -0
- package/dist/components/ui/table.d.ts +80 -0
- package/dist/components/ui/table.d.ts.map +1 -0
- package/dist/components/ui/table.js +130 -0
- package/dist/components/ui/table.js.map +1 -0
- package/dist/components/ui/tabs.d.ts +87 -0
- package/dist/components/ui/tabs.d.ts.map +1 -0
- package/dist/components/ui/tabs.js +128 -0
- package/dist/components/ui/tabs.js.map +1 -0
- package/dist/components/ui/textarea.d.ts +35 -0
- package/dist/components/ui/textarea.d.ts.map +1 -0
- package/dist/components/ui/textarea.js +38 -0
- package/dist/components/ui/textarea.js.map +1 -0
- package/dist/components/ui/time-picker.d.ts +58 -0
- package/dist/components/ui/time-picker.d.ts.map +1 -0
- package/dist/components/ui/time-picker.js +188 -0
- package/dist/components/ui/time-picker.js.map +1 -0
- package/dist/components/ui/toggle-group.d.ts +52 -0
- package/dist/components/ui/toggle-group.d.ts.map +1 -0
- package/dist/components/ui/toggle-group.js +94 -0
- package/dist/components/ui/toggle-group.js.map +1 -0
- package/dist/components/ui/toggle.d.ts +50 -0
- package/dist/components/ui/toggle.d.ts.map +1 -0
- package/dist/components/ui/toggle.js +70 -0
- package/dist/components/ui/toggle.js.map +1 -0
- package/dist/components/ui/tooltip.d.ts +51 -0
- package/dist/components/ui/tooltip.d.ts.map +1 -0
- package/dist/components/ui/tooltip.js +78 -0
- package/dist/components/ui/tooltip.js.map +1 -0
- package/dist/hooks/use-dark-mode.d.ts +2 -0
- package/dist/hooks/use-dark-mode.d.ts.map +1 -0
- package/dist/hooks/use-dark-mode.js +36 -0
- package/dist/hooks/use-dark-mode.js.map +1 -0
- package/dist/hooks/use-mobile.d.ts +2 -0
- package/dist/hooks/use-mobile.d.ts.map +1 -0
- package/dist/hooks/use-mobile.js +20 -0
- package/dist/hooks/use-mobile.js.map +1 -0
- package/dist/index.d.ts +88 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +77 -0
- package/dist/lib/scope.d.ts +64 -0
- package/dist/lib/scope.d.ts.map +1 -0
- package/dist/lib/scope.js +74 -0
- package/dist/lib/scope.js.map +1 -0
- package/dist/lib/utils.d.ts +3 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +11 -0
- package/dist/lib/utils.js.map +1 -0
- package/dist/styles.css +2 -0
- package/dist/theme-tokens.css +537 -0
- package/dist/theme.css +417 -0
- package/package.json +103 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-table.js","names":[],"sources":["../../../src/components/ui/data-table.tsx"],"sourcesContent":["\"use client\"\n\n/**\n * DataTable — Table plus sorting, filtering, search, row selection,\n * row actions, expandable rows, AI affordances, column management, and\n * pagination.\n *\n * A composite over the existing `Table` primitive (unmodified) — state is\n * owned by `@tanstack/react-table`, a headless logic-only library with no\n * rendered markup of its own (same \"headless engine, MMDS renders every\n * pixel\" shape as RichTextEditor's use of draft-js), not hand-rolled here.\n * Every visible piece (checkboxes, buttons, the search field, filter\n * popovers, the column menu, pagination, loading/empty states) is an\n * existing MMDS primitive — Checkbox, Button, InputGroup, Input, Select,\n * Popover, DropdownMenu, Skeleton, Empty, AISparkle.\n */\n\nimport * as React from \"react\"\nimport {\n useReactTable,\n getCoreRowModel,\n getSortedRowModel,\n getFilteredRowModel,\n getPaginationRowModel,\n getFacetedRowModel,\n getFacetedUniqueValues,\n getFacetedMinMaxValues,\n getExpandedRowModel,\n flexRender,\n type ColumnDef,\n type Column,\n type Row,\n type FilterFn,\n type SortingState,\n type VisibilityState,\n type RowSelectionState,\n type ColumnFiltersState,\n type ColumnOrderState,\n type ColumnPinningState,\n type ColumnSizingState,\n type ExpandedState,\n type PaginationState,\n type Table as TanstackTable,\n} from \"@tanstack/react-table\"\nimport {\n ArrowLeftIcon,\n ArrowLeftToLineIcon,\n ArrowRightIcon,\n ArrowRightToLineIcon,\n ArrowUpIcon,\n ArrowDownIcon,\n CheckCheckIcon,\n ChevronsUpDownIcon,\n ChevronDownIcon,\n ChevronRightIcon,\n ChevronLeftIcon,\n Columns3Icon,\n EllipsisIcon,\n EyeOffIcon,\n ListFilterIcon,\n PinIcon,\n PinOffIcon,\n RotateCcwIcon,\n SearchIcon,\n SearchXIcon,\n UnfoldHorizontalIcon,\n XIcon,\n CircleAlertIcon,\n} from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport {\n Table,\n TableCaption,\n TableHeader,\n TableBody,\n TableRow,\n TableHead,\n TableCell,\n} from \"@/components/ui/table\"\nimport { Checkbox } from \"@/components/ui/checkbox\"\nimport { Button } from \"@/components/ui/button\"\nimport { Input } from \"@/components/ui/input\"\nimport { Label } from \"@/components/ui/label\"\nimport {\n Pagination,\n PaginationContent,\n PaginationEllipsis,\n PaginationItem,\n PaginationLink,\n PaginationNext,\n PaginationPrevious,\n} from \"@/components/ui/pagination\"\nimport { Skeleton } from \"@/components/ui/skeleton\"\nimport {\n AILabel,\n AILabelContent,\n AILabelTrigger,\n} from \"@/components/ui/ai-label\"\nimport { AISparkle } from \"@/components/ui/ai-sparkle\"\nimport {\n Empty,\n EmptyHeader,\n EmptyMedia,\n EmptyTitle,\n EmptyDescription,\n} from \"@/components/ui/empty\"\nimport {\n InputGroup,\n InputGroupAddon,\n InputGroupInput,\n} from \"@/components/ui/input-group\"\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from \"@/components/ui/dropdown-menu\"\nimport {\n Popover,\n PopoverContent,\n PopoverDescription,\n PopoverHeader,\n PopoverTitle,\n PopoverTrigger,\n} from \"@/components/ui/popover\"\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n SelectMultiple,\n SelectMultipleContent,\n SelectMultipleFooter,\n SelectMultipleGroup,\n SelectMultipleItem,\n SelectMultipleLabel,\n SelectMultipleTrigger,\n} from \"@/components/ui/select\"\nimport {\n Tooltip,\n TooltipContent,\n TooltipTrigger,\n} from \"@/components/ui/tooltip\"\n\n/** One resolved option for a `select`/`singleSelect`/`range` filter — see `meta.filterOptions`. */\ntype FilterOption = {\n value: string\n label?: React.ReactNode\n /** Plain-text stand-in for `label` — read by the trigger's comma-list and the search recipe below when `label` isn't already a plain string (an icon, a Badge, anything non-text). */\n textValue?: string\n icon?: React.ComponentType<{ className?: string }>\n /** A second, muted line under the label — a code, a subtitle, whatever extra context makes one option distinguishable from another that isn't worth its own column. Plain text only (unlike `label`), since it renders inside the same comma-list/search-text machinery `textValue` does. */\n description?: string\n /** Options sharing a `group` render together under a `SelectMultipleLabel`/muted heading, in whatever order groups first appear in the resolved list. Ungrouped options (no `group` on any option in the list) render as one flat list — grouping only activates once at least one option opts in. */\n group?: string\n disabled?: boolean\n /** Set internally when `showFilterCounts` is on — not meant to be supplied on a `filterOptions` entry yourself, since it's overwritten from `getFacetedUniqueValues()` regardless. */\n count?: number\n}\n\ndeclare module \"@tanstack/react-table\" {\n interface ColumnMeta<TData, TValue> {\n /**\n * \"text\" (default): substring filter — pass a custom `filterFn` directly\n * on the `ColumnDef` (checked first, ahead of everything here) for\n * anything other than a case-insensitive substring match, e.g. TanStack's\n * own `\"equalsString\"`/`\"includesStringSensitive\"` presets, or your own.\n * \"select\": multi-select checklist of the column's unique values (via getFacetedUniqueValues).\n * \"singleSelect\": same source, but only one value at a time.\n * \"bucket\" (`\"range\"` also still accepted — see `rangeBucketSize`/`bucketSize`, deprecated\n * in favor of it): multi-select checklist of fixed-size numeric buckets (\"1-3\", \"4-6\", ...),\n * computed from the trailing number in each unique value — for id-like columns (invoice\n * numbers, ticket numbers) where an exact substring match isn't the point.\n */\n filterVariant?: \"text\" | \"select\" | \"singleSelect\" | \"range\" | \"bucket\"\n /** Bucket size for `filterVariant: \"bucket\"` — how many consecutive numeric values group into one bucket. Default 3. Deprecated alias: `rangeBucketSize`. */\n bucketSize?: number\n /** @deprecated Use `bucketSize` — same option, `filterVariant: \"bucket\"`'s original name back when the variant itself was still called `\"range\"`. */\n rangeBucketSize?: number\n /**\n * Explicit options for a `select`/`singleSelect`/`range` column instead\n * of deriving them from the column's own unique values — labels, icons,\n * groups, and a fixed order none of that can express on its own. A\n * function receives `column` (e.g. to read `getFacetedUniqueValues()`\n * counts yourself, or vary options off something outside the table\n * entirely). Every option's own `value` must still match what actually\n * appears in the data for the filter to match anything.\n */\n filterOptions?:\n FilterOption[] | ((column: Column<TData, TValue>) => FilterOption[])\n /** Renders `Skeleton` rows in place of the option list while an async `filterOptions` source is still loading — DataTable never fetches anything itself, this only controls what renders meanwhile. */\n filterOptionsLoading?: boolean\n /** How the resolved option list orders itself — default `\"auto\"`: a numeric-aware collator (so \"2\" sorts before \"10\"), applied whether the options came from facets or your own `filterOptions`. `\"none\"` keeps `filterOptions`' own array order (meaningless for facet-derived options, which have no other order). A comparator gets full control. */\n filterOptionsSort?:\n \"auto\" | \"none\" | ((a: FilterOption, b: FilterOption) => number)\n /**\n * Per-column override of `DataTable`'s own `filterApplyMode` — e.g. one\n * text column that should defer to an explicit Apply while the rest of\n * the table applies immediately, or vice versa. Doesn't affect\n * `select`/`range`: those are always deferred regardless (see\n * `isMultiSelectFilterVariant`) — this can't force them immediate. Also\n * meaningless for `singleSelect`, which closes on pick before an Apply\n * step could ever matter.\n */\n filterApplyMode?: \"immediate\" | \"deferred\"\n /**\n * Escape hatch for a filter UI none of the built-in `filterVariant`s\n * cover — a date-range picker, a numeric slider, a three-state\n * toggle, a multi-field filter, anything a specific table's own\n * use case calls for. This is the deliberate answer for those, not a\n * gap: MMDS ships the primitives (`Calendar`, `Slider`, `ToggleGroup`,\n * `Input`, ...) a designer composes into whatever shape that use case\n * actually needs — a number-range Slider, a date-range Calendar, a\n * Yes/No ToggleGroup — rather than DataTable guessing at one fixed\n * shape/labeling/styling for everyone. Checked first, ahead of\n * `filterVariant`, in every filter surface (header-icon popover,\n * inline toolbar button, and the \"All filters\" pane) — same\n * `value`/`onValueChange` shape those surfaces already pass to the\n * built-in variants, and `value`/`onValueChange` carry a pending draft\n * under `filterApplyMode=\"deferred\"` in all three the same way, not\n * just two of them: the inline toolbar button wraps a deferred custom\n * render in a Popover + the shared Cancel/Apply footer of its own (it\n * has no such popover otherwise, unlike `select`/`range`/text, which\n * always get one); the pane — which has no popover for *any* variant,\n * custom or built-in — gets an inline Cancel/Apply row directly under\n * the rendered content instead. Immediate (the default), every surface\n * renders it directly with no extra chrome, same as always. Read and\n * write the real filter state yourself via `column` (e.g.\n * `column.getFacetedUniqueValues()`, `column.getFacetedMinMaxValues()`\n * — that one needs `getFacetedMinMaxValues` passed to the table's own\n * options too, since DataTable only wires it up automatically for its\n * own built-in variants) if the custom UI needs it beyond the one\n * value being edited.\n */\n filterRender?: (props: {\n column: Column<TData, TValue>\n value: unknown\n onValueChange: (value: unknown) => void\n }) => React.ReactNode\n /**\n * Defines a column purely to filter on a data point that isn't shown\n * anywhere in the table — the column itself (its `header`/`cell`) never\n * renders, permanently, with no user-facing toggle to show it: it's\n * excluded from the Columns menu entirely (not just its checkbox — from\n * the reorder/pin list too, since there's no header cell to reorder or\n * pin) rather than merely defaulting hidden the way a plain hideable\n * column can. It still needs a real `accessorKey`/`accessorFn` (that's\n * what its filter reads) and still shows up as a normal filterable\n * column in the \"All filters\" pane — but never inline in the toolbar\n * (no user-facing pin toggle for it there either, same reasoning as\n * the Columns menu exclusion — there's no header cell for a toolbar\n * chip to point back at), and its presence alone forces the pane to\n * exist at all even if 3 or fewer *other* columns are filterable,\n * since the pane is its only access point. Give it a `header` anyway\n * (used as its filter's label) even though it's never rendered.\n */\n filterOnly?: boolean\n /**\n * Merges other columns' filter fields into *this* column's own\n * header-icon filter popover instead of each getting its own separate\n * entry in the toolbar/pane — for a cell that already displays more\n * than one filterable value at once (e.g. a \"Patient\" column whose\n * custom `cell` renders a name plus an MRN below it), so filtering\n * either reads as one decision made from one icon, not two unrelated\n * ones. Each id listed here is excluded from `filterableColumns`\n * (so it never *also* shows up on its own) and instead renders as its\n * own labeled section inside this column's popover, stacked under a\n * divider — typically `meta: { filterOnly: true }` columns, since the\n * value they filter is usually already folded into this column's own\n * `cell` rather than shown in a column of its own, but that's a\n * convention, not a requirement: any filterable column id works. This\n * column's own filter field always renders first, unlabeled; merged\n * ones below it are labeled with their own `header` so it's clear\n * which value each field filters. The header icon itself shows filled\n * (and the Cancel/Apply footer under `filterApplyMode=\"deferred\"`\n * commits/discards) if *any* of them — this column or a merged one —\n * has an active filter, not just this column's own.\n */\n filterMergeWith?: string[]\n /**\n * Overrides this column's position in the filter order — both the\n * toolbar's pinned chips and the \"All filters\" pane render filterable\n * columns sorted by this (ascending; ties broken by column order, then\n * lower first), independent of the table's own column order. Omit to\n * keep the default (this column's own position in column order, as\n * today) — only set it on columns that actually need to jump out of\n * that order.\n */\n filterOrder?: number\n /**\n * Aligns this column's header and cell content — default `\"left\"`,\n * the right fit for text. Set `\"right\"` for quantitative content\n * (amounts, counts, percentages, anything meant to be scanned/summed\n * down the column) — numbers read fastest when their digits line up\n * on a shared right edge, which is why this is a per-column opt-in\n * rather than something DataTable guesses from the data: it has no\n * reliable way to tell \"123 Main St\" apart from \"123\" apart from a\n * custom cell rendering a styled duration, so the convention is\n * yours to apply, not DataTable's to infer. `\"right\"` also mirrors\n * the header itself — the sort button's label right-aligns and the\n * filter icon/`headerActions` cluster moves to the *left* of it,\n * instead of both sitting to its right — so the controls stay off to\n * the side the numbers don't occupy.\n */\n align?: \"left\" | \"right\"\n /**\n * This column's name wherever one is needed that isn't the rendered\n * `header` itself — the Columns menu, the \"All filters\" pane, header\n * tooltips/`aria-label`s, applied-filter copy. Only matters when\n * `header` isn't a plain string (an icon-only header, a custom\n * component) — DataTable has no reliable way to derive a name from\n * arbitrary JSX, and falls back to the column's raw `id` (a dev-only\n * console warning, once per column) rather than guessing.\n */\n label?: string\n /**\n * Collapses this column's cell into one `rowSpan`-merged cell across\n * however many consecutive rendered rows (post-sort/filter/page — the\n * rows actually adjacent on screen, not the raw data) share the same\n * `column.getValue()` — a spreadsheet-style vertical merge instead of\n * repeating the value on every row. Only meaningful when those rows\n * are actually adjacent because the table is sorted by (or the data is\n * pre-grouped by) this column; otherwise every run is length 1 and\n * nothing visibly merges. Equality is plain `===` — no custom\n * comparator. Content is top-aligned inside the merged cell so it\n * reads like a label for the rows below it, not vertically centered;\n * override via the column's own `cell` if centered fits better.\n */\n mergeRepeatedValues?: boolean\n /** Sticks this column to the left/right edge during horizontal scroll. Seeds the *initial* pinned side only — the user can toggle it live from there (e.g. via `columnMenuActions` in `headerActions`), unless `pinLocked` is set. */\n pin?: \"left\" | \"right\"\n /** Suppresses `columnMenuActions`' Pin left/Pin right for this column, so pinning stays fully dev-controlled with no user-facing toggle. Doesn't stop a custom `headerActions` entry from calling `column.pin(...)` itself. */\n pinLocked?: boolean\n /**\n * Custom actions rendered in this column's header cell, next to the\n * sort button and filter icon — hidden until the header cell is\n * hovered or keyboard-focused, collapsing into an overflow menu past\n * 2 actions (same `RowActions` component row actions use). A\n * function, not a static array, so labels/icons can reflect live\n * per-column state (e.g. \"Pin left\" flipping to \"Unpin\"). Takes the\n * table too, since some actions (`columnMenuActions`' \"Fit to\n * content\") need it. See `columnMenuActions`.\n */\n headerActions?: (\n column: Column<TData, unknown>,\n table: TanstackTable<TData>\n ) => RowAction[]\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars -- must match the upstream interface's type parameter exactly to merge\n interface TableMeta<TData> {\n /** Internal — backs `columnMenuActions`' \"Fit to content\" measurement. Not meant to be set by consumers. */\n getColumnCellElements?: (columnId: string) => HTMLElement[]\n }\n}\n\n// Interactive box is `size-6` (24px, WCAG 2.5.8's Target Size floor) but the\n// visible checkbox stays the usual `size-4` (16px) look: `border-0` plus an\n// `outline` pulled inward by a negative `outline-offset` draws the \"empty\n// box\" cue 4px in from the real (bigger) edge instead of tracing it, and\n// `bg-clip-content` + matching padding confines the checked-state fill to\n// that same inner square instead of painting the full 24px box. A plain\n// wrapper `<span>` around the normal `size-4` Checkbox doesn't satisfy\n// axe's target-size check here — it measures the interactive element's own\n// box, not a non-interactive ancestor's — so the element that IS the\n// checkbox has to actually be 24px; only what's *painted* can stay\n// smaller. (`inset-shadow`/`box-shadow` were tried first instead of\n// `outline` — a box-shadow, even inset, is always flush with the\n// border-box edge and ignores padding entirely, so that drew the ring at\n// the full 24px edge instead of the intended 16px inset; verified by\n// scaling the rendered checkbox up 4x in Storybook and looking at it\n// directly. `outline-offset` is the only one of the three that's actually\n// padding-aware.)\n//\n// The `!` (important) on the outline-style override is load-bearing, not\n// decorative: Checkbox's own base class carries an unconditional\n// `outline-none`, which sets `outline-style: none` directly (not just\n// through Tailwind's shared `--tw-outline-style` variable) to suppress the\n// native focus outline in favor of its own `ring-*` styling. Since\n// `outline-1` here only *reads* that same variable rather than setting\n// `outline-style` on its own, whichever of the two rules for the literal\n// `outline-style` property happens to land later in the compiled\n// stylesheet wins — unrelated to this class string's own order, and not\n// something to rely on. Forcing this one declaration `!important` is what\n// actually guarantees it wins, confirmed by rendering both ways.\nconst SELECTION_CHECKBOX_CLASS =\n \"size-6 rounded-[6px] border-0 p-1 bg-clip-content [outline-style:solid]! outline-1 -outline-offset-4 outline-input data-[state=checked]:[outline-style:none]! data-[state=indeterminate]:[outline-style:none]!\"\n\nconst SELECTION_COLUMN_ID = \"__select__\"\nconst EXPAND_COLUMN_ID = \"__expand__\"\nconst AI_COLUMN_ID = \"__ai__\"\nconst INJECTED_COLUMN_IDS = new Set([\n SELECTION_COLUMN_ID,\n EXPAND_COLUMN_ID,\n AI_COLUMN_ID,\n])\n\n// Maps DataTable's density prop onto AILabelTrigger's `size`: \"default\"\n// passes straight through, \"sm\" and \"xs\" density both get \"sm\" — and never\n// AILabel's `iconOnly`. Icon-only is opt-in only, for when a designer\n// explicitly asks for it; a component picking it on its own is exactly\n// what the design rule forbids, so the visible \"AI\" text stays at every\n// density here.\nfunction AIMarkerTrigger({\n size,\n ariaLabel,\n}: {\n size: \"default\" | \"sm\" | \"xs\"\n ariaLabel: string\n}) {\n return (\n <AILabelTrigger\n size={size === \"default\" ? \"default\" : \"sm\"}\n aria-label={ariaLabel}\n />\n )\n}\n\nfunction getColumnId<TData, TValue>(\n column: ColumnDef<TData, TValue>\n): string | undefined {\n if (column.id) return column.id\n if (\"accessorKey\" in column && typeof column.accessorKey === \"string\") {\n return column.accessorKey\n }\n return undefined\n}\n\n/** The dev-configured default pinning — every column's own `meta.pin`, collected into TanStack's `{ left, right }` shape. Seeds `columnPinning`'s initial state and is what \"Reset to defaults\" restores it to. */\nfunction derivePinningFromMeta<TData, TValue>(\n columns: ColumnDef<TData, TValue>[]\n): ColumnPinningState {\n const left: string[] = []\n const right: string[] = []\n columns.forEach((col) => {\n const id = getColumnId(col)\n if (!id) return\n if (col.meta?.pin === \"left\") left.push(id)\n if (col.meta?.pin === \"right\") right.push(id)\n })\n return { left, right }\n}\n\nfunction idListsEqual(a: string[] | undefined, b: string[] | undefined) {\n const left = a ?? []\n const right = b ?? []\n return (\n left.length === right.length &&\n left.every((value, index) => value === right[index])\n )\n}\n\n// A single offscreen canvas, reused across every measurement call —\n// cheap to keep around, and avoids touching the real DOM just to measure\n// text.\nlet measureCanvasContext: CanvasRenderingContext2D | null | undefined\nfunction getMeasureContext(): CanvasRenderingContext2D | null {\n if (measureCanvasContext !== undefined) return measureCanvasContext\n measureCanvasContext =\n typeof document === \"undefined\"\n ? null\n : (document.createElement(\"canvas\").getContext(\"2d\") ?? null)\n return measureCanvasContext\n}\n\nconst MIN_SIZE_FALLBACK_CHAR_WIDTH = 8\n\nfunction measureTextWidth(text: string, font: string): number {\n const ctx = getMeasureContext()\n if (!ctx) return text.length * MIN_SIZE_FALLBACK_CHAR_WIDTH\n ctx.font = font\n return ctx.measureText(text).width\n}\n\n// A header-cell icon-only control (the filter trigger, or the\n// headerActions cluster once it collapses to its own single overflow\n// button — both `size=\"xs\" iconOnly`, `size-6` = 24px) plus the `gap-0.5`\n// (2px) separating it from whatever's before it in the row. These sit in\n// the *same* flex row as the sort button and — critically — still take\n// up real layout space even at rest (`opacity-0` only hides paint, not\n// layout), so a column with either of them needs room for them too, not\n// just its own label.\nconst HEADER_ICON_CONTROL_ALLOWANCE = 24 + 2\n\n/**\n * The narrowest a column should ever resize to — just enough for its\n * header's first character, an ellipsis, the sort icon (if sortable),\n * and the filter icon/headerActions cluster (if this column has them —\n * both occupy real layout space in the header row regardless of their\n * own hover-reveal opacity), so a squeezed column still reads as \"this\n * column, truncated\" rather than disappearing into an unreadable sliver\n * or squeezing its own label to nothing to make room for its sibling\n * controls. Measured per column via an offscreen canvas (the \"W\" in\n * \"Website\" needs more room than the \"I\" in \"Invoice\") rather than one\n * flat constant for every column — that's what makes this \"dynamic.\"\n * Only works out the real first character when `header` is a plain\n * string; a render-function header falls back to\n * `MIN_SIZE_FALLBACK_CHAR_WIDTH` since there's no text to introspect\n * without actually mounting it.\n */\nfunction computeColumnMinSize<TData, TValue>(\n col: ColumnDef<TData, TValue>,\n tableSize: \"default\" | \"sm\" | \"xs\"\n): number {\n const font =\n tableSize === \"xs\"\n ? \"500 12px system-ui, sans-serif\"\n : \"500 14px system-ui, sans-serif\"\n // `meta.label` stands in for a non-string `header` here too — an\n // icon-only header still reads roughly as wide as its own name, a far\n // better guess than the flat fallback below.\n const label = (\n col.meta?.label ?? (typeof col.header === \"string\" ? col.header : \"\")\n ).trim()\n const charWidth = label\n ? measureTextWidth(label.charAt(0), font)\n : MIN_SIZE_FALLBACK_CHAR_WIDTH\n const ellipsisWidth = measureTextWidth(\"…\", font)\n const canSort = col.enableSorting !== false\n // gap-1 (4px) + the sort icon itself (size-3.5, 14px) — only reserved\n // when the column can actually sort, since that's the only case the\n // header renders an icon at all.\n const sortIconAllowance = canSort ? 4 + 14 : 0\n const canFilter = col.enableColumnFilter !== false\n const hasHeaderActions = Boolean(col.meta?.headerActions)\n const siblingControlsAllowance =\n (canFilter ? HEADER_ICON_CONTROL_ALLOWANCE : 0) +\n (hasHeaderActions ? HEADER_ICON_CONTROL_ALLOWANCE : 0)\n // TableHead's own `px-2` inset (16px) — separate from, and in addition\n // to, the sort button's own padding below, since both really are\n // there at once: the cell's padding around the whole header-cell flex\n // row, and the button's own padding inside just the label slot.\n const cellPadding = 16\n // The sort button's own `px-2` (16px) minus the `-ml-2`/`-mr-2` that\n // cancels it on whichever side faces the cell's true edge — net 8px,\n // only present when there's a real button there at all (a\n // non-sortable header is a plain `<div>` with no padding of its own).\n const buttonPadding = canSort ? 8 : 0\n return Math.ceil(\n charWidth +\n ellipsisWidth +\n sortIconAllowance +\n siblingControlsAllowance +\n cellPadding +\n buttonPadding\n )\n}\n\n/** A column absent from either map is implicitly visible (`true`) — comparing raw keys would treat \"never touched\" and \"explicitly shown again\" as different when they're not. */\nfunction visibilityEqual(a: VisibilityState, b: VisibilityState) {\n const keys = new Set([...Object.keys(a), ...Object.keys(b)])\n return [...keys].every((key) => (a[key] ?? true) === (b[key] ?? true))\n}\n\n/** A column absent from either map has no explicit width set — same \"missing means untouched\" comparison as `visibilityEqual`, just with no shared implicit value to fall back to (there's no one universal default width), so a column present in only one side always counts as a difference. */\nfunction sizingEqual(a: ColumnSizingState, b: ColumnSizingState) {\n const keys = new Set([...Object.keys(a), ...Object.keys(b)])\n return [...keys].every((key) => a[key] === b[key])\n}\n\nconst selectFilterFn: FilterFn<unknown> = (\n row,\n columnId,\n filterValue: string[]\n) => {\n if (!Array.isArray(filterValue) || filterValue.length === 0) return true\n return filterValue.includes(String(row.getValue(columnId)))\n}\n\nconst singleSelectFilterFn: FilterFn<unknown> = (\n row,\n columnId,\n filterValue: string\n) => {\n if (!filterValue) return true\n return String(row.getValue(columnId)) === filterValue\n}\n\nconst DEFAULT_RANGE_BUCKET_SIZE = 3\n// Reserved sentinel for a \"singleSelect\" all-values/clear item — Radix Select's own value\n// prop treats \"\" as \"nothing selected\" (so the trigger falls back to its placeholder), but that\n// exact string is disallowed on an Item, hence a real, non-empty stand-in value here.\nconst ALL_VALUE = \"__all__\"\n\n/** The last run of digits in a string — \"INV003\" -> 3. Powers `filterVariant: \"range\"`. */\nfunction extractTrailingNumber(value: unknown): number | null {\n const match = String(value ?? \"\").match(/(\\d+)(?!.*\\d)/)\n return match ? Number(match[0]) : null\n}\n\n// Keyed on the `getFacetedUniqueValues()` Map's own identity (TanStack Table\n// memoizes that per faceting recompute, not per render) so re-renders that\n// don't change the underlying data reuse the bucketing work instead of\n// re-sorting/re-bucketing every value on every keystroke elsewhere in the table.\nconst rangeBucketCache = new WeakMap<\n Map<unknown, number>,\n Map<number, string[]>\n>()\n\n/** Buckets a column's sorted unique values (by trailing number) into fixed-size groups, each rendered as a \"min-max\" (or just \"min\" for a single-value bucket) label. */\nfunction computeRangeBuckets<TData>(\n column: Column<TData, unknown>,\n bucketSize: number\n): string[] {\n const uniqueValues = column.getFacetedUniqueValues()\n const cached = rangeBucketCache.get(uniqueValues)?.get(bucketSize)\n if (cached) return cached\n\n const numbers = Array.from(uniqueValues.keys())\n .map(extractTrailingNumber)\n .filter((value): value is number => value !== null)\n .sort((a, b) => a - b)\n const buckets: string[] = []\n for (let index = 0; index < numbers.length; index += bucketSize) {\n const slice = numbers.slice(index, index + bucketSize)\n const min = slice[0]\n const max = slice[slice.length - 1]\n buckets.push(min === max ? `${min}` : `${min}-${max}`)\n }\n\n const byBucketSize = rangeBucketCache.get(uniqueValues) ?? new Map()\n byBucketSize.set(bucketSize, buckets)\n rangeBucketCache.set(uniqueValues, byBucketSize)\n\n return buckets\n}\n\n/** A \"min-max\" (or single \"n\") bucket label back into its numeric bounds. */\nfunction parseRangeBucket(label: string): [number, number] {\n const [min, max] = label.split(\"-\").map(Number)\n return max === undefined ? [min, min] : [min, max]\n}\n\nconst rangeFilterFn: FilterFn<unknown> = (\n row,\n columnId,\n filterValue: string[]\n) => {\n if (!Array.isArray(filterValue) || filterValue.length === 0) return true\n const value = extractTrailingNumber(row.getValue(columnId))\n if (value === null) return false\n return filterValue.some((bucket) => {\n const [min, max] = parseRangeBucket(bucket)\n return value >= min && value <= max\n })\n}\n\n/** Matches `query` only where a word starts — at the beginning of the text or right after anything that isn't a letter or digit. The one pattern behind both the `searchMatch=\"startsWith\"` filter and its highlight, so they can never disagree. Capture group 1 is the (possibly empty) boundary character, so the highlight can skip it; a consuming prefix instead of a lookbehind keeps this working on Safari before 16.4. */\nfunction wordStartPattern(query: string, flags: string) {\n return new RegExp(\n `(^|[^\\\\p{L}\\\\p{N}])${query.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")}`,\n flags\n )\n}\n\nconst startsWithWordFilterFn: FilterFn<unknown> = (\n row,\n columnId,\n filterValue\n) => {\n const query = String(filterValue ?? \"\").trim()\n if (!query) return true\n return wordStartPattern(query, \"iu\").test(\n String(row.getValue(columnId) ?? \"\")\n )\n}\n\n/**\n * Wraps the substring of `text` matching `query` in a `<mark>`. Exported so a\n * column with a custom `cell` can highlight matches itself — read the live\n * query off `info.table.getState().globalFilter` inside that `cell`. Pass\n * `startsWith` when the table's `searchMatch` is `\"startsWith\"`, so only\n * word-start matches are marked.\n */\nfunction HighlightMatch({\n text,\n query,\n startsWith = false,\n}: {\n text: string\n query: string\n startsWith?: boolean\n}) {\n const trimmed = query.trim()\n if (!trimmed) return <>{text}</>\n const mark = (part: string, index: number) => (\n <mark key={index} className=\"rounded-[2px] bg-primary/15 text-inherit\">\n {part}\n </mark>\n )\n if (startsWith) {\n const parts: React.ReactNode[] = []\n let last = 0\n for (const match of text.matchAll(wordStartPattern(trimmed, \"giu\"))) {\n const start = match.index + match[1].length\n if (start > last) parts.push(text.slice(last, start))\n parts.push(mark(match[0].slice(match[1].length), parts.length))\n last = match.index + match[0].length\n }\n if (last < text.length) parts.push(text.slice(last))\n return <>{parts}</>\n }\n const pattern = new RegExp(\n `(${trimmed.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")})`,\n \"gi\"\n )\n const parts = text.split(pattern)\n return (\n <>\n {parts.map((part, index) =>\n part.toLowerCase() === trimmed.toLowerCase() ? mark(part, index) : part\n )}\n </>\n )\n}\n\n/** The committed (not live-dragging/previewing) column layout `onColumnLayoutChange` hands back — pair with `defaultColumnVisibility`/`Order`/`Pinning`/`Sizing` to persist and restore it. */\ntype DataTableColumnLayout = {\n visibility: VisibilityState\n order: ColumnOrderState\n pinning: ColumnPinningState\n sizing: ColumnSizingState\n}\n\ntype RowAction = {\n label: string\n icon?: React.ComponentType<{ className?: string }>\n onClick: () => void\n variant?: \"default\" | \"destructive\"\n /**\n * Passed straight through to the rendered control (an inline icon Button\n * or, past the overflow threshold, a DropdownMenuItem) — both already\n * forward arbitrary DOM props, so a hover-preview, focus-driven tooltip,\n * or any other non-click interaction works the same regardless of which\n * one ends up rendering this action.\n */\n onMouseEnter?: React.MouseEventHandler\n onMouseLeave?: React.MouseEventHandler\n onFocus?: React.FocusEventHandler\n onBlur?: React.FocusEventHandler\n}\n\n/**\n * Renders `actions` as inline icon buttons (≤2 actions) or an overflow\n * menu (>2) — `mode` overrides the auto pick. Stops its own click from\n * bubbling, so it composes safely inside a row that also has `onRowClick`.\n */\nfunction RowActions({\n actions,\n mode = \"auto\",\n revealClassName,\n onOpenChange,\n}: {\n actions: RowAction[]\n mode?: \"inline\" | \"overflow\" | \"auto\"\n /**\n * Hover/keyboard-focus-reveal classes for this cluster's own visibility\n * (e.g. `revealColumnControlsOnHover`'s computed class) — applied here\n * rather than by a wrapping `<div>` at the call site, so the overflow\n * branch can force itself visible (via real React state, not a CSS\n * selector) while its own menu is open, however the pointer got there.\n * A modal Radix menu sets `pointer-events: none` on the rest of the\n * page while open, which breaks *any* CSS-only `:hover` tracking for\n * this cluster regardless of where the cursor physically is — hence\n * `open` state, not `group-hover`, driving the override. Omit to\n * always render visible (no hover-gating at all).\n */\n revealClassName?: string\n /** Fires when the overflow menu opens/closes — a sibling control in the\n * same header cell (the filter icon) needs to know too, since it can't\n * detect this itself: this menu is modal, so it sets `pointer-events:\n * none` on the rest of the header while open, breaking that sibling's\n * own `:hover` tracking regardless of where the pointer actually is. */\n onOpenChange?: (open: boolean) => void\n}) {\n const [overflowOpen, setOverflowOpenState] = React.useState(false)\n const setOverflowOpen = (open: boolean) => {\n setOverflowOpenState(open)\n onOpenChange?.(open)\n }\n const resolved =\n mode === \"auto\" ? (actions.length <= 2 ? \"inline\" : \"overflow\") : mode\n\n if (resolved === \"inline\") {\n return (\n <div\n data-fit-exclude\n draggable={false}\n className={cn(\"flex items-center justify-end gap-1\", revealClassName)}\n onClick={(event) => event.stopPropagation()}\n >\n {actions.map((action) => (\n <Tooltip key={action.label}>\n <TooltipTrigger asChild>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"xs\"\n iconOnly\n aria-label={action.label}\n className={cn(\n action.variant === \"destructive\" &&\n \"text-destructive hover:text-destructive\"\n )}\n onClick={action.onClick}\n onMouseEnter={action.onMouseEnter}\n onMouseLeave={action.onMouseLeave}\n onFocus={action.onFocus}\n onBlur={action.onBlur}\n >\n {action.icon ? <action.icon /> : null}\n </Button>\n </TooltipTrigger>\n <TooltipContent>{action.label}</TooltipContent>\n </Tooltip>\n ))}\n </div>\n )\n }\n\n return (\n <div\n data-fit-exclude\n draggable={false}\n className={cn(\n \"flex justify-end\",\n overflowOpen ? \"opacity-100\" : revealClassName\n )}\n onClick={(event) => event.stopPropagation()}\n >\n <DropdownMenu open={overflowOpen} onOpenChange={setOverflowOpen}>\n <Tooltip>\n <TooltipTrigger asChild>\n <DropdownMenuTrigger asChild>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"xs\"\n iconOnly\n aria-label=\"Row actions\"\n >\n <EllipsisIcon />\n </Button>\n </DropdownMenuTrigger>\n </TooltipTrigger>\n <TooltipContent>Row actions</TooltipContent>\n </Tooltip>\n <DropdownMenuContent align=\"end\">\n {actions.map((action) => (\n <DropdownMenuItem\n key={action.label}\n variant={action.variant}\n onClick={action.onClick}\n onMouseEnter={action.onMouseEnter}\n onMouseLeave={action.onMouseLeave}\n onFocus={action.onFocus}\n onBlur={action.onBlur}\n >\n {action.icon ? <action.icon data-icon=\"inline-start\" /> : null}\n {action.label}\n </DropdownMenuItem>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n )\n}\n\n// Sub-pixel rounding buffer on top of the measured natural width, so a\n// fit-to-content column doesn't come back a hair too narrow and\n// immediately re-clip its own content.\nconst FIT_TO_CONTENT_PADDING = 2\n\n// Default drag-resize ceiling for a column with no `maxSize` opinion of its\n// own — without one, `enableColumnResizing` lets a column be dragged\n// arbitrarily wide, which is rarely what's wanted (a name/description\n// column swallowing the whole table). Only caps *drag* resizing, not a\n// column's initial `size`, and a column whose own `size` is already past\n// this (see `processedUserColumns`) keeps that as its ceiling instead.\nconst DEFAULT_MAX_COLUMN_SIZE = 1200\n\n// Past this many pages, the compact page-jump control swaps from a\n// `Select` (every page as a separate `SelectItem`, which past this point\n// means thousands of DOM nodes just to open the popover) to a plain number\n// `Input` — the same page count, a fraction of the render cost.\nconst PAGE_SELECT_THRESHOLD = 50\n\n/**\n * The page numbers (and `\"ellipsis\"` gaps) a numbered `Pagination` control\n * actually renders for `current` (1-indexed) out of `total` pages — always\n * page 1 and `total`, always `current`'s own immediate neighbors, an\n * ellipsis standing in for whatever's skipped between those two clusters.\n */\nfunction getPaginationWindow(\n current: number,\n total: number\n): (number | \"ellipsis\")[] {\n const pages = new Set<number>([1, total])\n for (let page = current - 1; page <= current + 1; page++) {\n if (page >= 1 && page <= total) pages.add(page)\n }\n const sorted = [...pages].sort((a, b) => a - b)\n const result: (number | \"ellipsis\")[] = []\n let previous = 0\n for (const page of sorted) {\n if (page - previous > 1) result.push(\"ellipsis\")\n result.push(page)\n previous = page\n }\n return result\n}\n\n/**\n * Clones every element in `elements` into one shared, unconstrained, hidden\n * container appended to `document.body` exactly once, to measure each\n * clone's natural (unwrapped, unclipped) content width independent of\n * whatever width the live layout currently gives it — one batch of\n * append/measure/remove instead of one full reflow-triggering cycle per\n * cell, which is what a header plus a full page of body rows would\n * otherwise cost on every \"Fit to content\" click. Measuring the live\n * elements' own `scrollWidth` directly doesn't work here: once a column\n * has already been fit once, its cells are exactly as wide as their\n * content, so `scrollWidth` on those *live* cells just reports back their\n * own current box width (there's nothing left to overflow) rather than the\n * true minimum — clicking \"Fit to content\" again would then add\n * `FIT_TO_CONTENT_PADDING` on top of that every time, growing the column a\n * couple pixels on every click forever. The clones sidestep that by never\n * being constrained by the table's own column width to begin with. Strips\n * anything marked `data-fit-exclude` first — hover-only header actions\n * shouldn't force permanent extra width onto a column just because they're\n * in the DOM.\n */\nfunction measureNaturalWidths(elements: HTMLElement[]): number[] {\n const container = document.createElement(\"div\")\n Object.assign(container.style, {\n position: \"absolute\",\n visibility: \"hidden\",\n left: \"-9999px\",\n top: \"-9999px\",\n })\n const clones = elements.map((element) => {\n const clone = element.cloneNode(true) as HTMLElement\n clone\n .querySelectorAll(\"[data-fit-exclude]\")\n .forEach((node) => node.remove())\n Object.assign(clone.style, {\n display: \"inline-block\",\n width: \"auto\",\n maxWidth: \"none\",\n whiteSpace: \"nowrap\",\n })\n container.appendChild(clone)\n return clone\n })\n document.body.appendChild(container)\n const widths = clones.map((clone) => clone.scrollWidth)\n container.remove()\n return widths\n}\n\n/**\n * Measures every currently-rendered cell in `column` — its header plus\n * this page's body rows, via `measureNaturalWidths` — and sets the\n * column's size to the widest one found, via `table.setColumnSizing`.\n * Only rows rendered on the *current* page are measured — a deliberate,\n * cheaper scope than the whole dataset. Requires `enableColumnResizing`\n * on `DataTable` (a column that can't resize can't be fit either — see\n * `columnMenuActions`). The measuring pass runs one frame later\n * (`requestAnimationFrame`) so it never shares a frame with whatever\n * triggered it (a click, a double-click) — it's a batch of forced\n * layout reads either way, just not fighting that frame's own paint too.\n */\nfunction fitColumnToContent<TData>(\n column: Column<TData, unknown>,\n table: TanstackTable<TData>\n) {\n const elements = table.options.meta?.getColumnCellElements?.(column.id) ?? []\n if (elements.length === 0) return\n requestAnimationFrame(() => {\n const widest = Math.max(...measureNaturalWidths(elements))\n table.setColumnSizing((old) => ({\n ...old,\n [column.id]: widest + FIT_TO_CONTENT_PADDING,\n }))\n })\n}\n\n/**\n * Pin left/Pin right `RowAction`s for `column` — each becomes \"Unpin\"\n * instead once the column's already pinned to that side. Empty when the\n * column can't pin (`getCanPin()`) or `meta.pinLocked` is set. Shared by\n * `columnMenuActions` (the header-cell menu) and the Columns-menu popover\n * row, which renders these same two actions as plain inline icon buttons\n * instead of inside a dropdown.\n */\nfunction pinActions<TData>(column: Column<TData, unknown>): RowAction[] {\n if (!column.getCanPin() || column.columnDef.meta?.pinLocked) return []\n const pinned = column.getIsPinned()\n return [\n pinned === \"left\"\n ? { label: \"Unpin\", icon: PinOffIcon, onClick: () => column.pin(false) }\n : {\n label: \"Pin left\",\n icon: ArrowLeftToLineIcon,\n onClick: () => column.pin(\"left\"),\n },\n pinned === \"right\"\n ? { label: \"Unpin\", icon: PinOffIcon, onClick: () => column.pin(false) }\n : {\n label: \"Pin right\",\n icon: ArrowRightToLineIcon,\n onClick: () => column.pin(\"right\"),\n },\n ]\n}\n\n/**\n * A ready-made column-menu bundle for `headerActions` — Pin left, Pin\n * right (via `pinActions`), Hide column, and Fit to content — each\n * omitted when the column can't do it (`getCanPin()`/`getCanHide()`/\n * `getCanResize()`, the last only true once `enableColumnResizing` is\n * set). Typically 3-4 actions, so `RowActions`' own auto mode already\n * renders it as a single overflow \"more\" menu — no need to force\n * `mode=\"overflow\"`.\n */\nfunction columnMenuActions<TData>(\n column: Column<TData, unknown>,\n table: TanstackTable<TData>\n): RowAction[] {\n const actions: RowAction[] = [...pinActions(column)]\n if (column.getCanHide()) {\n actions.push({\n label: \"Hide column\",\n icon: EyeOffIcon,\n onClick: () => column.toggleVisibility(false),\n })\n }\n if (column.getCanResize()) {\n actions.push({\n label: \"Fit to content\",\n icon: UnfoldHorizontalIcon,\n onClick: () => fitColumnToContent(column, table),\n })\n }\n return actions\n}\n\nconst numericAwareCollator = new Intl.Collator(undefined, { numeric: true })\n\n/** `\"bucket\"` is the canonical name; `\"range\"` is still accepted (with a dev warning — see the deprecation checks near the component's other dev warnings) since it originally had no more specific name to disambiguate against. */\nfunction isBucketFilterVariant<TData>(column: Column<TData, unknown>): boolean {\n const variant = column.columnDef.meta?.filterVariant\n return variant === \"bucket\" || variant === \"range\"\n}\n\n/** `bucketSize` is the canonical name; `rangeBucketSize` is still accepted (same deprecation as `\"range\"` above). */\nfunction resolveBucketSize<TData>(column: Column<TData, unknown>): number {\n const meta = column.columnDef.meta\n return meta?.bucketSize ?? meta?.rangeBucketSize ?? DEFAULT_RANGE_BUCKET_SIZE\n}\n\n/** The resolved option list for an enumerable filter (`select`/`singleSelect`/`bucket`) — `meta.filterOptions` if given, else raw unique values or computed range buckets, sorted per `meta.filterOptionsSort` and decorated with a facet `count` when `showFilterCounts`. Shared by the header filter icon, the inline pinned filter, and the \"more filters\" pane — all three render the same resolved list the same way, see `renderFilterOptionContent`. */\nfunction resolveFilterOptions<TData>(\n column: Column<TData, unknown>,\n showFilterCounts: boolean\n): FilterOption[] {\n const meta = column.columnDef.meta\n const explicit = meta?.filterOptions\n let options: FilterOption[]\n if (explicit) {\n options = typeof explicit === \"function\" ? explicit(column) : explicit\n } else if (isBucketFilterVariant(column)) {\n options = computeRangeBuckets(column, resolveBucketSize(column)).map(\n (value) => ({ value })\n )\n } else {\n options = Array.from(column.getFacetedUniqueValues().keys()).map(\n (value) => ({ value: String(value) })\n )\n }\n\n const sortMode = meta?.filterOptionsSort ?? \"auto\"\n if (typeof sortMode === \"function\") {\n options = [...options].sort(sortMode)\n } else if (sortMode === \"auto\") {\n options = [...options].sort((a, b) =>\n numericAwareCollator.compare(\n a.textValue ?? (typeof a.label === \"string\" ? a.label : a.value),\n b.textValue ?? (typeof b.label === \"string\" ? b.label : b.value)\n )\n )\n }\n\n if (showFilterCounts) {\n const counts = column.getFacetedUniqueValues()\n options = options.map((option) => ({\n ...option,\n count: counts.get(option.value),\n }))\n }\n\n return options\n}\n\n/** Whether any option in the list opts into grouping — grouping only activates once at least one option sets `group`; a list with none renders flat. */\nfunction hasGroupedFilterOptions(options: FilterOption[]): boolean {\n return options.some((option) => option.group)\n}\n\n/** Stable-orders `options` into `{ group, options }` buckets, in whichever order each group first appears — used by every surface that renders grouped options the same way. */\nfunction groupFilterOptions(\n options: FilterOption[]\n): { group: string | undefined; options: FilterOption[] }[] {\n const order: (string | undefined)[] = []\n const byGroup = new Map<string | undefined, FilterOption[]>()\n for (const option of options) {\n if (!byGroup.has(option.group)) {\n order.push(option.group)\n byGroup.set(option.group, [])\n }\n byGroup.get(option.group)!.push(option)\n }\n return order.map((group) => ({ group, options: byGroup.get(group)! }))\n}\n\n/** The label content shared by every filter surface for one resolved option — icon, label (falling back to the raw value), a muted count when present, and a muted `description` line underneath when given. `size-4` is explicit, not left to each surface's own ambient `[&_svg:not([class*='size-'])]:size-4` (\"skip if the class already sets a size\") sizing rule — the checklist's plain `Label`+`Checkbox` row has no such rule the way `SelectItem`/`SelectMultipleItem` do, so an unsized icon there falls back to the browser's native (much larger) SVG default instead. */\nfunction FilterOptionContent({ option }: { option: FilterOption }) {\n const Icon = option.icon\n return (\n <span className=\"flex min-w-0 flex-1 items-center gap-1.5\">\n {Icon && <Icon className=\"size-4 shrink-0\" />}\n <span className=\"flex min-w-0 flex-col\">\n <span className=\"flex items-center gap-1 truncate\">\n {option.label ?? option.value}\n {option.count != null && (\n <span className=\"text-muted-foreground\">({option.count})</span>\n )}\n </span>\n {option.description && (\n <span className=\"truncate text-xs text-muted-foreground\">\n {option.description}\n </span>\n )}\n </span>\n </span>\n )\n}\n\n/** Stand-in for the option list while `meta.filterOptionsLoading` is set — same shape in every surface (checklist, `SelectMultipleContent`, plain `Select`'s content), since DataTable never fetches anything itself and has no real option count to size the skeleton to. */\nfunction FilterOptionsSkeleton() {\n return (\n <div className=\"flex flex-col gap-1.5 p-1\">\n <Skeleton className=\"h-4 w-full\" />\n <Skeleton className=\"h-4 w-4/5\" />\n <Skeleton className=\"h-4 w-3/5\" />\n </div>\n )\n}\n\n// Past this many options, a search input for the checklist/\n// `SelectMultipleContent` surfaces is worth the extra chrome — below it,\n// scanning the whole list directly is faster than typing. (The\n// select-all/clear row has its own, lower bar — see\n// `FILTER_SELECT_ALL_THRESHOLD` below.)\nconst FILTER_SEARCH_THRESHOLD = 8\n\n// \"Select all\" earns its own, much lower bar than the search box — it's\n// useful on a short list too (clearing 3-4 boxes by hand is exactly the\n// tedium it exists for), it doesn't need a long list to justify the row\n// the way a search input needs enough options to actually search through.\nconst FILTER_SELECT_ALL_THRESHOLD = 2\n\nfunction optionSearchText(option: FilterOption): string {\n return (\n option.textValue ??\n (typeof option.label === \"string\" ? option.label : option.value)\n )\n}\n\n/** The always-open checklist `ColumnFilterField`'s `select`/`range` branch renders — its own component so the search/select-all state doesn't leak into the parent's. */\nfunction FilterOptionChecklist({\n options,\n selected,\n onValueChange,\n stickySelectAll,\n}: {\n options: FilterOption[]\n selected: string[]\n onValueChange: (next: string[] | undefined) => void\n stickySelectAll: boolean\n}) {\n const [query, setQuery] = React.useState(\"\")\n const showSearch = options.length > FILTER_SEARCH_THRESHOLD\n const grouped = hasGroupedFilterOptions(options)\n const visibleOptions = showSearch\n ? options.filter((option) =>\n optionSearchText(option).toLowerCase().includes(query.toLowerCase())\n )\n : options\n const allVisibleSelected =\n visibleOptions.length > 0 &&\n visibleOptions.every((option) => selected.includes(option.value))\n const someVisibleSelected = visibleOptions.some((option) =>\n selected.includes(option.value)\n )\n\n function toggleAll() {\n if (allVisibleSelected) {\n const visibleValues = new Set(\n visibleOptions.map((option) => option.value)\n )\n const next = selected.filter((value) => !visibleValues.has(value))\n onValueChange(next.length ? next : undefined)\n } else {\n const next = Array.from(\n new Set([...selected, ...visibleOptions.map((option) => option.value)])\n )\n onValueChange(next)\n }\n }\n\n function renderOption(option: FilterOption) {\n return (\n <Label key={option.value} className=\"flex items-center gap-2 font-normal\">\n <Checkbox\n checked={selected.includes(option.value)}\n disabled={option.disabled}\n onCheckedChange={(checked) => {\n const next = checked\n ? [...selected, option.value]\n : selected.filter((value) => value !== option.value)\n onValueChange(next.length ? next : undefined)\n }}\n />\n <FilterOptionContent option={option} />\n </Label>\n )\n }\n\n const selectAllRow = options.length > FILTER_SELECT_ALL_THRESHOLD &&\n visibleOptions.length > 0 && (\n <Label className=\"flex items-center gap-2 pb-1.5 font-normal\">\n <Checkbox\n checked={\n allVisibleSelected\n ? true\n : someVisibleSelected\n ? \"indeterminate\"\n : false\n }\n onCheckedChange={toggleAll}\n />\n Select all\n </Label>\n )\n\n return (\n <div className=\"flex flex-col gap-1.5\">\n {showSearch && (\n <Input\n placeholder=\"Search...\"\n value={query}\n onChange={(event) => setQuery(event.target.value)}\n />\n )}\n {stickySelectAll && selectAllRow}\n <div className=\"flex max-h-64 flex-col gap-1.5 overflow-y-auto\">\n {!stickySelectAll && selectAllRow}\n {visibleOptions.length === 0 && (\n <p className=\"text-sm text-muted-foreground\">No values</p>\n )}\n {grouped\n ? groupFilterOptions(visibleOptions).map(({ group, options }) => (\n <div key={group ?? \"__ungrouped__\"}>\n {group && (\n <Label className=\"mb-1 block text-xs text-muted-foreground\">\n {group}\n </Label>\n )}\n <div className=\"flex flex-col gap-1.5\">\n {options.map(renderOption)}\n </div>\n </div>\n ))\n : visibleOptions.map(renderOption)}\n </div>\n </div>\n )\n}\n\n/**\n * `value`/`onValueChange` are handed in rather than read straight off\n * `column` so this same field can drive either the real, immediately-applied\n * filter state or a popover's own pending draft — see `ColumnFilterTrigger`'s\n * `deferred` prop for the latter. Both still read `column` itself for\n * `filterVariant`/unique values; only the value being edited is decoupled.\n */\n/** A text filter input whose own displayed value updates every keystroke, while the real filter it drives only catches up 200ms after typing stops (flushed immediately on blur or Enter) — same \"instant to type into, debounced to act on\" split as the toolbar's global search. */\nfunction DebouncedFilterInput({\n value,\n onValueChange,\n placeholder = \"Filter...\",\n}: {\n value: string\n onValueChange: (value: string | undefined) => void\n placeholder?: string\n}) {\n const [inputValue, setInputValue] = React.useState(value)\n const debouncedOnValueChange = useDebouncedCallback(onValueChange, 200)\n // Resyncs when the real value changes for a reason other than this\n // input's own typing — \"Clear filters\", another surface's copy of this\n // same column applying a different value.\n React.useEffect(() => {\n setInputValue(value)\n }, [value])\n return (\n <Input\n placeholder={placeholder}\n value={inputValue}\n onChange={(event) => {\n setInputValue(event.target.value)\n debouncedOnValueChange(event.target.value || undefined)\n }}\n onBlur={() => onValueChange(inputValue || undefined)}\n onKeyDown={(event) => {\n if (event.key === \"Enter\") onValueChange(inputValue || undefined)\n }}\n />\n )\n}\n\nfunction ColumnFilterField<TData>({\n column,\n value,\n onValueChange,\n showFilterCounts,\n stickyFilterSelectAll,\n}: {\n column: Column<TData, unknown>\n value: unknown\n onValueChange: (value: unknown) => void\n showFilterCounts: boolean\n stickyFilterSelectAll: boolean\n}) {\n const meta = column.columnDef.meta\n if (meta?.filterRender) {\n return meta.filterRender({ column, value, onValueChange })\n }\n if (meta?.filterOptionsLoading) {\n return <FilterOptionsSkeleton />\n }\n if (meta?.filterVariant === \"select\" || isBucketFilterVariant(column)) {\n const options = resolveFilterOptions(column, showFilterCounts)\n const selected = (value as string[] | undefined) ?? []\n return (\n <FilterOptionChecklist\n options={options}\n selected={selected}\n onValueChange={onValueChange}\n stickySelectAll={stickyFilterSelectAll}\n />\n )\n }\n\n if (meta?.filterVariant === \"singleSelect\") {\n const options = resolveFilterOptions(column, showFilterCounts)\n const selected = (value as string | undefined) ?? ALL_VALUE\n return (\n <Select\n value={selected}\n onValueChange={(next) =>\n onValueChange(next === ALL_VALUE ? undefined : next)\n }\n >\n <SelectTrigger className=\"w-full\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={ALL_VALUE}>All</SelectItem>\n {options.map((option) => (\n <SelectItem\n key={option.value}\n value={option.value}\n disabled={option.disabled}\n >\n <FilterOptionContent option={option} />\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )\n }\n\n return (\n <DebouncedFilterInput\n value={(value as string) ?? \"\"}\n onValueChange={onValueChange}\n />\n )\n}\n\n/** The search input + select-all/clear row + grouped-or-flat `SelectMultipleItem`s shared by every `SelectMultiple`-based filter surface (inline toolbar chip and pane, for `select`/`range`/`singleSelect` alike) — matches `select.tsx`'s own documented search recipe: hidden via `className`, never unmounted, so a selected-but-filtered-out item keeps its label registered for the trigger's own comma-list. `showSelectAll` is only meaningful for true multi-select (`select`/`range`) — a `singleSelect` list has nothing for \"select all\" to mean. */\nfunction SelectMultipleOptionsContent({\n options,\n value,\n onValueChangeAll,\n showSelectAll = true,\n stickySelectAll,\n}: {\n options: FilterOption[]\n value: string[]\n onValueChangeAll: (next: string[] | undefined) => void\n showSelectAll?: boolean\n stickySelectAll: boolean\n}) {\n const [query, setQuery] = React.useState(\"\")\n const showSearch = options.length > FILTER_SEARCH_THRESHOLD\n const grouped = hasGroupedFilterOptions(options)\n const matches = (option: FilterOption) =>\n !showSearch ||\n !query ||\n optionSearchText(option).toLowerCase().includes(query.toLowerCase())\n const visibleOptions = options.filter(matches)\n const allVisibleSelected =\n visibleOptions.length > 0 &&\n visibleOptions.every((option) => value.includes(option.value))\n const someVisibleSelected = visibleOptions.some((option) =>\n value.includes(option.value)\n )\n\n function toggleAll() {\n if (allVisibleSelected) {\n const visibleValues = new Set(\n visibleOptions.map((option) => option.value)\n )\n const next = value.filter((v) => !visibleValues.has(v))\n onValueChangeAll(next.length ? next : undefined)\n } else {\n const next = Array.from(\n new Set([...value, ...visibleOptions.map((option) => option.value)])\n )\n onValueChangeAll(next)\n }\n }\n\n function renderItem(option: FilterOption) {\n return (\n <SelectMultipleItem\n key={option.value}\n value={option.value}\n disabled={option.disabled}\n textValue={\n option.textValue ??\n (typeof option.label === \"string\" ? option.label : undefined)\n }\n className={cn(!matches(option) && \"hidden\")}\n >\n <FilterOptionContent option={option} />\n </SelectMultipleItem>\n )\n }\n\n const selectAllRow = showSelectAll &&\n options.length > FILTER_SELECT_ALL_THRESHOLD &&\n options.length > 0 && (\n <Label className=\"flex items-center gap-2 p-1.5 font-normal\">\n <Checkbox\n checked={\n allVisibleSelected\n ? true\n : someVisibleSelected\n ? \"indeterminate\"\n : false\n }\n onCheckedChange={toggleAll}\n />\n Select all\n </Label>\n )\n\n return (\n <>\n {showSearch && (\n <div className=\"p-1 pb-0\">\n <Input\n placeholder=\"Search...\"\n value={query}\n onChange={(event) => setQuery(event.target.value)}\n />\n </div>\n )}\n {stickySelectAll && selectAllRow}\n {/* A fixed height regardless of option count, not one that keeps\n growing until it hits the viewport edge — `SelectMultipleContent`\n itself already caps at `--radix-popover-content-available-height`,\n which on a long list (`LongOptionLists`) means a popover as tall\n as the screen; this keeps every list, short or long, the same\n predictable size. `stickySelectAll` (default `false`) puts\n \"Select all\" inside this scroll area instead, as the first row —\n the common expectation for a plain checkbox list, where nothing\n else stays fixed while scrolling either. */}\n <div className=\"max-h-64 overflow-y-auto\">\n {!stickySelectAll && selectAllRow}\n {grouped\n ? groupFilterOptions(options).map(\n ({ group, options: groupOptions }) => (\n <SelectMultipleGroup key={group ?? \"__ungrouped__\"}>\n {group && <SelectMultipleLabel>{group}</SelectMultipleLabel>}\n {groupOptions.map(renderItem)}\n </SelectMultipleGroup>\n )\n )\n : options.map(renderItem)}\n </div>\n </>\n )\n}\n\n/** Same filtering as `ColumnFilterField` (including `meta.filterRender`, checked first), but the select/range/singleSelect variants stay closed by default (a `SelectMultiple`/`Select`) instead of listing every value upfront — the filters pane holds every filterable column at once, so an always-open checklist per column would be too much to scan. `value`/`onValueChange` are the pane's own shared draft under `filterApplyMode=\"deferred\"` (see the pane's footer), or the real filter directly otherwise — same dual-mode shape `ColumnFilterField` already uses. */\n/**\n * Same filter shapes as `ColumnFilterField`, but self-contained rather\n * than taking `value`/`onValueChange` from a caller — the pane has no\n * shared batch draft or footer of its own (just \"Clear filters\", which\n * clears the real, already-applied filters). A multiselect field\n * (`select`/`range`) is still always deferred, same as everywhere else,\n * but that's *this* field's own popover — its own draft, its own\n * Cancel/Apply footer inside it — exactly like the inline toolbar's\n * identical field, not a pane-wide batch.\n */\n/** The pane's `filterRender` field under a deferred apply mode — a local draft plus an inline Cancel/Apply row, since `filterRender` in the pane isn't wrapped in a popover of its own the way every other variant here is (it just renders directly, always visible), so there's no popover footer to hang Cancel/Apply off of the way `SelectMultipleFooter`/`FilterPopoverFooter` do elsewhere. */\nfunction DeferredPaneFilterRender<TData>({\n column,\n filterRender,\n}: {\n column: Column<TData, unknown>\n filterRender: (props: {\n column: Column<TData, unknown>\n value: unknown\n onValueChange: (value: unknown) => void\n }) => React.ReactNode\n}) {\n const committedValue = column.getFilterValue()\n const [pendingValue, setPendingValue] = React.useState(committedValue)\n React.useEffect(() => {\n setPendingValue(committedValue)\n }, [committedValue])\n return (\n <div className=\"flex flex-col gap-2\">\n {filterRender({\n column,\n value: pendingValue,\n onValueChange: setPendingValue,\n })}\n <div className=\"flex items-center justify-end gap-1\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"xs\"\n onClick={() => setPendingValue(committedValue)}\n >\n Cancel\n </Button>\n <Button\n type=\"button\"\n size=\"xs\"\n disabled={filterDraftUnchanged(committedValue, pendingValue)}\n onClick={() => column.setFilterValue(pendingValue)}\n >\n Apply\n </Button>\n </div>\n </div>\n )\n}\n\nfunction PaneColumnFilterField<TData>({\n column,\n showFilterCounts,\n tableDeferred,\n stickyFilterSelectAll,\n}: {\n column: Column<TData, unknown>\n showFilterCounts: boolean\n tableDeferred?: boolean\n stickyFilterSelectAll: boolean\n}) {\n const meta = column.columnDef.meta\n // Only read/written by the select/range branch below.\n const [open, setOpenState] = React.useState(false)\n const [pendingValue, setPendingValue] = React.useState<unknown>(undefined)\n const setOpen = (value: boolean) => {\n if (value) setPendingValue(column.getFilterValue())\n setOpenState(value)\n }\n\n if (meta?.filterRender) {\n if (isColumnDeferred(column, !!tableDeferred)) {\n return (\n <DeferredPaneFilterRender\n column={column}\n filterRender={meta.filterRender}\n />\n )\n }\n return meta.filterRender({\n column,\n value: column.getFilterValue(),\n onValueChange: (value) => column.setFilterValue(value),\n })\n }\n if (meta?.filterOptionsLoading) {\n return <FilterOptionsSkeleton />\n }\n if (meta?.filterVariant === \"select\" || isBucketFilterVariant(column)) {\n const options = resolveFilterOptions(column, showFilterCounts)\n // Committed while closed — so this reads correctly the moment the\n // pane opens, e.g. right after the toolbar's own copy of this same\n // column applied a filter, with no interaction of this field's own\n // popover yet to have seeded `pendingValue`. Live picks while open,\n // same as everywhere else this pattern's used.\n const selected = open\n ? ((pendingValue as string[] | undefined) ?? [])\n : ((column.getFilterValue() as string[] | undefined) ?? [])\n return (\n <SelectMultiple\n open={open}\n onOpenChange={setOpen}\n value={selected}\n onValueChange={(next) =>\n setPendingValue(next.length ? next : undefined)\n }\n >\n <SelectMultipleTrigger placeholder=\"All\" className=\"w-full\" />\n <SelectMultipleContent>\n <SelectMultipleOptionsContent\n options={options}\n value={selected}\n onValueChangeAll={setPendingValue}\n stickySelectAll={stickyFilterSelectAll}\n />\n <SelectMultipleFooter>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"xs\"\n onClick={() => setOpen(false)}\n >\n Cancel\n </Button>\n <Button\n type=\"button\"\n size=\"xs\"\n disabled={filterDraftUnchanged(\n column.getFilterValue(),\n pendingValue\n )}\n onClick={() => {\n column.setFilterValue(pendingValue)\n setOpen(false)\n }}\n >\n Apply\n </Button>\n </SelectMultipleFooter>\n </SelectMultipleContent>\n </SelectMultiple>\n )\n }\n\n if (meta?.filterVariant === \"singleSelect\") {\n // `SelectMultiple` capped at one value (`closeOnSelect`), not plain\n // `Select` — matches the inline toolbar's identical singleSelect\n // treatment (see that comment) rather than the header-icon popover's\n // own plain-`Select` one, for the same reason: a checkbox-turned-radio\n // list picked from directly, not a native-feeling dropdown. Always\n // live, same as text below — `closeOnSelect` means there's never a\n // moment an open draft would be visible to defer in the first place.\n const options = resolveFilterOptions(column, showFilterCounts)\n const selected = (column.getFilterValue() as string | undefined) ?? \"\"\n return (\n <SelectMultiple\n closeOnSelect\n value={selected ? [selected] : []}\n onValueChange={(next) => column.setFilterValue(next.at(-1))}\n >\n <SelectMultipleTrigger placeholder=\"All\" className=\"w-full\" />\n <SelectMultipleContent>\n <SelectMultipleOptionsContent\n options={options}\n value={selected ? [selected] : []}\n onValueChangeAll={() => {}}\n showSelectAll={false}\n stickySelectAll={stickyFilterSelectAll}\n />\n </SelectMultipleContent>\n </SelectMultiple>\n )\n }\n\n // Always live — a plain, always-visible Input in the pane has no\n // popover of its own to defer within the way every other variant above\n // either always has (select/range/singleSelect) or builds itself\n // (`filterRender`). Still debounced, same as everywhere else text\n // filters live — \"always live\" is about there being no Apply step, not\n // about refiltering on every keystroke.\n return (\n <DebouncedFilterInput\n value={(column.getFilterValue() as string) ?? \"\"}\n onValueChange={(value) => column.setFilterValue(value)}\n />\n )\n}\n\n// Dev-only, one warning per column id per session — `columnLabel` runs on\n// every render of everything that needs a column's name (menus, tooltips,\n// the filters pane), so warning there unconditionally would spam the\n// console on every keystroke instead of surfacing the misconfiguration once.\nconst warnedMissingLabelIds = new Set<string>()\n\nfunction columnLabel<TData>(column: Column<TData, unknown>) {\n if (column.columnDef.meta?.label) return column.columnDef.meta.label\n if (typeof column.columnDef.header === \"string\")\n return column.columnDef.header\n if (\n process.env.NODE_ENV !== \"production\" &&\n !warnedMissingLabelIds.has(column.id)\n ) {\n warnedMissingLabelIds.add(column.id)\n console.warn(\n `DataTable: column \"${column.id}\" has a non-string \\`header\\` (an icon, a custom component, ...) and no \\`meta.label\\` — every place that needs this column's *name* rather than its rendered header (the Columns menu, the \"All filters\" pane, tooltips, applied-filter/chip copy) falls back to its raw \\`id\\` instead. Set \\`meta.label\\` to fix this.`\n )\n }\n return column.id\n}\n\n/**\n * Bulk-actions bar shown while any row is selected — hovers over the\n * bottom edge of the table's own viewport, never the screen: `sticky`\n * inside `tableContainerRef`, which is unconditionally a genuine scroll\n * container (its own `overflow-auto`) by the time this renders. `sticky`\n * (not `absolute`) is what actually matters here:\n * `absolute` positions against the *content* box, so it scrolls away\n * with the rows above it instead of staying put — `sticky` is what\n * tracks the scroll *viewport* itself. Since `tableContainerRef` is the\n * nearest scrolling ancestor either way, this never hits the Card\n * `overflow-hidden` problem `sticky` usually has (that only happens when\n * `sticky` has to bubble past a non-scrolling container to find one).\n * Either way it's a child of `tableContainerRef`, not a portal, so it's\n * bounded by the table's own box, not the viewport.\n */\nconst BulkActionsBar = React.forwardRef<\n HTMLDivElement,\n {\n count: number\n hiddenByFilterCount?: number\n totalCount: number\n isAllSelected: boolean\n maxSelectedRows?: number\n atSelectionCap?: boolean\n onSelectAll: () => void\n onClear: () => void\n clearLabel?: string\n controlSize: \"sm\" | \"xs\"\n controlIconSize: \"sm\" | \"xs\"\n children?: React.ReactNode\n }\n>(function BulkActionsBar(\n {\n count,\n hiddenByFilterCount = 0,\n totalCount,\n isAllSelected,\n maxSelectedRows,\n atSelectionCap = false,\n onSelectAll,\n onClear,\n clearLabel = \"Clear selection\",\n controlSize,\n controlIconSize,\n children,\n },\n ref\n) {\n return (\n <div\n ref={ref}\n className=\"pointer-events-none sticky bottom-2 z-10 flex justify-center\"\n >\n <div className=\"pointer-events-auto flex items-center gap-1 rounded-full border border-border bg-popover p-1.5 shadow-popover\">\n <div className=\"flex items-center gap-1.5 px-2\">\n <span className=\"text-sm text-muted-foreground\">\n {isAllSelected ? (\n `All ${totalCount} selected`\n ) : (\n <>\n {count}{\" \"}\n {maxSelectedRows != null\n ? `of ${maxSelectedRows} max selected`\n : \"selected\"}\n {hiddenByFilterCount > 0 &&\n ` (${hiddenByFilterCount} hidden by filters)`}\n </>\n )}\n </span>\n </div>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size={controlSize}\n disabled={!isAllSelected && atSelectionCap}\n title={\n !isAllSelected && atSelectionCap\n ? `Up to ${maxSelectedRows} rows can be selected`\n : undefined\n }\n onClick={isAllSelected ? onClear : onSelectAll}\n >\n {isAllSelected ? (\n \"Clear selection\"\n ) : (\n <>\n <CheckCheckIcon />\n Select all {totalCount}\n </>\n )}\n </Button>\n {children && (\n <>\n <div className=\"h-5 w-px shrink-0 bg-border\" />\n <div className=\"flex items-center gap-1\">{children}</div>\n </>\n )}\n <div className=\"h-5 w-px shrink-0 bg-border\" />\n <Button\n type=\"button\"\n variant=\"ghost\"\n size={controlIconSize}\n iconOnly\n aria-label={clearLabel}\n onClick={onClear}\n >\n <XIcon />\n </Button>\n </div>\n </div>\n )\n})\n\n/** Sticky offset for a pinned column — `undefined` (no inline style) when the column isn't pinned. */\nfunction getPinnedStyle<TData>(\n column: Column<TData, unknown>\n): React.CSSProperties | undefined {\n const pinned = column.getIsPinned()\n if (!pinned) return undefined\n return pinned === \"left\"\n ? { position: \"sticky\", left: column.getStart(\"left\") }\n : { position: \"sticky\", right: column.getAfter(\"right\") }\n}\n\n/**\n * Solid `bg-card` (the table's own real white surface, not `bg-background`\n * — that token is the page canvas gray, which read as a mismatched \"grey\n * bg\" against the rest of the table) so scrolled content doesn't show\n * through underneath, plus a thicker boundary divider on the outermost\n * pinned column of each side — on its right edge for the left-pinned\n * group, its left edge for the right-pinned group, i.e. towards whichever\n * side actually borders the scrollable/center columns. `bg-card` is opaque\n * and painted directly on this cell, which would otherwise hide this row's\n * own hover/selected tint (both painted on the `<tr>`, a layer down) for\n * just this one column — `group-hover/row`/`group-data-[state=selected]/row`\n * (reacting to `TableRow`'s own `group/row`, see table.tsx) repaints the\n * same tint directly on this cell instead of relying on it showing through.\n */\nfunction getPinnedClassName<TData>(\n column: Column<TData, unknown>,\n table: TanstackTable<TData>\n) {\n const pinned = column.getIsPinned()\n if (!pinned) return \"\"\n const visibleLeft = table.getLeftLeafColumns().filter((c) => c.getIsVisible())\n const visibleRight = table\n .getRightLeafColumns()\n .filter((c) => c.getIsVisible())\n const isLastLeft = pinned === \"left\" && visibleLeft.at(-1)?.id === column.id\n const isFirstRight = pinned === \"right\" && visibleRight[0]?.id === column.id\n return cn(\n \"z-10 bg-card\",\n // `bg-muted/50`/`bg-muted` alone (the same classes `TableRow` itself\n // uses) are semi-transparent — correct on the `<tr>`, which has real\n // page/card background behind it, but wrong here: alpha-blended\n // against whatever's scrolled *underneath* this sticky pinned cell,\n // not against its own opaque `bg-card`, so real content bled through\n // during hover/selection instead of staying hidden. `color-mix`\n // pre-blends the same tint against `--card` into one flat, fully\n // opaque color instead, so the cell never stops fully covering\n // what's behind it.\n \"group-hover/row:bg-[color-mix(in_oklab,var(--color-muted)_50%,var(--color-card))]\",\n \"group-data-[state=selected]/row:bg-[color-mix(in_oklab,var(--color-muted)_100%,var(--color-card))]\",\n isLastLeft && \"border-r-2 border-border\",\n isFirstRight && \"border-l-2 border-border\"\n )\n}\n\n/**\n * The \"scroll fade\" cue — an inset shadow indicating there's scrollable\n * content sliding in underneath a pinned column — lives on the *scrolling*\n * side of the pinned boundary (the first/last center column next to it),\n * not on the pinned column itself: the fade represents content that's\n * about to disappear under something fixed, which is a property of the\n * content moving, not of the fixed thing it's moving under. Only rendered\n * once `canScroll` is true (the table actually overflows horizontally) —\n * a shadow implying \"more content behind this edge\" would be misleading\n * otherwise.\n */\nfunction getScrollFadeClassName<TData>(\n column: Column<TData, unknown>,\n table: TanstackTable<TData>,\n canScroll: boolean\n) {\n if (!canScroll || column.getIsPinned()) return \"\"\n const visibleLeft = table.getLeftLeafColumns().filter((c) => c.getIsVisible())\n const visibleRight = table\n .getRightLeafColumns()\n .filter((c) => c.getIsVisible())\n const visibleCenter = table\n .getCenterLeafColumns()\n .filter((c) => c.getIsVisible())\n const isFirstCenter =\n visibleLeft.length > 0 && visibleCenter[0]?.id === column.id\n const isLastCenter =\n visibleRight.length > 0 && visibleCenter.at(-1)?.id === column.id\n return cn(\n isFirstCenter && \"shadow-[inset_8px_0_8px_-8px_rgba(0,0,0,0.15)]\",\n isLastCenter && \"shadow-[inset_-8px_0_8px_-8px_rgba(0,0,0,0.15)]\"\n )\n}\n\n/**\n * Cancel/Apply footer for a plain-`Popover`-based filter field (see\n * `ColumnFilterTrigger` and `InlineColumnFilter`'s own fallback branch) —\n * picks edit a local draft only, never the real filter; Apply commits that\n * draft, Cancel just closes and discards it, leaving the real filter\n * exactly as it was before the popover opened.\n */\nfunction FilterPopoverFooter({\n onCancel,\n onApply,\n applyDisabled,\n}: {\n onCancel: () => void\n onApply: () => void\n /** Disables Apply when the draft(s) behind this footer haven't changed from what's already applied — see `filterDraftUnchanged`. */\n applyDisabled?: boolean\n}) {\n return (\n // Edge-to-edge divider against the popover's own default `p-2.5` (same\n // -bleed/pad-back-in trick as the Columns menu's footer) — only the\n // border reaches the popover's true left/right edges; the buttons\n // stay padded back in to that same 2.5 inset instead of sitting flush\n // against it too.\n <div className=\"-mx-2.5 mt-2 flex w-[calc(100%+--spacing(5))] items-center justify-end gap-1 border-t border-border px-2.5 pt-2\">\n <Button type=\"button\" variant=\"ghost\" size=\"xs\" onClick={onCancel}>\n Cancel\n </Button>\n <Button\n type=\"button\"\n size=\"xs\"\n disabled={applyDisabled}\n onClick={onApply}\n >\n Apply\n </Button>\n </div>\n )\n}\n\n/**\n * A multiselect filter (`filterVariant: \"select\"` or `\"range\"` — either\n * way, a checklist of several values at once) always needs a deliberate\n * Apply, in every surface, regardless of `filterApplyMode` — picking one\n * value out of a list of many isn't a complete thought the way a single\n * text edit or a `singleSelect` pick already is; the whole point of\n * checking several boxes is to review the set before it takes effect.\n * `filterApplyMode=\"deferred\"` still separately opts every *other* filter\n * shape into the same Cancel/Apply step.\n */\n/** True for every filter shape with more than one field/pick to review before it's a complete thought — `select`/`range`/`bucket` checklists. */\nfunction isMultiSelectFilterVariant<TData>(\n column: Column<TData, unknown>\n): boolean {\n const variant = column.columnDef.meta?.filterVariant\n return variant === \"select\" || variant === \"range\" || variant === \"bucket\"\n}\n\n/** `singleSelect` commits on a single click/pick — there's never a moment an open draft exists to defer in the first place, so it can never be put into deferred mode, the same way a multiselect variant can never be taken *out* of it (see `isMultiSelectFilterVariant`). */\nfunction isNeverDeferredFilterVariant<TData>(\n column: Column<TData, unknown>\n): boolean {\n return column.columnDef.meta?.filterVariant === \"singleSelect\"\n}\n\n/** Whether this column's filter should defer to an explicit Apply — `meta.filterApplyMode` overrides the table-wide `filterApplyMode` when set, but neither can turn a multiselect (`select`/`range`/`bucket`) *off* deferred, or a `singleSelect` *into* it. */\nfunction isColumnDeferred<TData>(\n column: Column<TData, unknown>,\n tableDeferred: boolean\n): boolean {\n const columnMode = column.columnDef.meta?.filterApplyMode\n // `filterRender` owns its own semantics entirely — `filterApplyMode`\n // (table-wide or per-column) is the only thing that can defer it.\n // `filterVariant`'s own always/never-deferred rules below don't apply\n // here, even if the column's `meta` happens to still carry a\n // `filterVariant` alongside `filterRender` (e.g. from spreading an\n // existing column's `meta` to keep its `headerActions` while adding a\n // custom render on top) — `filterRender` is checked first everywhere\n // else a column's filter actually renders, so it should win here too,\n // not leave a stale `filterVariant` quietly forcing Cancel/Apply behind\n // its back.\n if (column.columnDef.meta?.filterRender) {\n return columnMode === \"deferred\" || (!columnMode && tableDeferred)\n }\n if (isNeverDeferredFilterVariant(column)) return false\n const deferred = columnMode ? columnMode === \"deferred\" : tableDeferred\n return deferred || isMultiSelectFilterVariant(column)\n}\n\n/**\n * Whether a filter draft actually differs from what's currently applied —\n * gates every deferred filter surface's Apply button so it's disabled\n * (not just harmless) when clicking it would be a no-op. Filter values\n * here are always plain JSON-serializable data (strings, string arrays,\n * small plain objects from a custom `filterRender`), so a `JSON.stringify`\n * comparison is a legitimate deep-equality check, not a shortcut.\n */\nfunction filterDraftUnchanged(committed: unknown, pending: unknown): boolean {\n return JSON.stringify(committed ?? null) === JSON.stringify(pending ?? null)\n}\n\n/** Delays invoking `callback` until `delayMs` have passed with no new call — the returned function is stable across renders (safe in a dep array), always calling whatever `callback` currently is by the time the delay elapses, not whatever it was when the delay started. */\nfunction useDebouncedCallback<Args extends unknown[]>(\n callback: (...args: Args) => void,\n delayMs: number\n): (...args: Args) => void {\n const callbackRef = React.useRef(callback)\n React.useEffect(() => {\n callbackRef.current = callback\n })\n const timeoutRef = React.useRef<ReturnType<typeof setTimeout> | undefined>(\n undefined\n )\n React.useEffect(() => () => clearTimeout(timeoutRef.current), [])\n return React.useCallback(\n (...args: Args) => {\n clearTimeout(timeoutRef.current)\n timeoutRef.current = setTimeout(\n () => callbackRef.current(...args),\n delayMs\n )\n },\n [delayMs]\n )\n}\n\n/** Resolves `column`'s own `meta.filterMergeWith` ids against `table`, dropping any that don't resolve to a real column — the list `ColumnFilterTrigger` actually renders a field for, `column` itself always first. */\nfunction mergedFilterColumns<TData>(\n column: Column<TData, unknown>,\n table: TanstackTable<TData>\n): Column<TData, unknown>[] {\n const mergeIds = column.columnDef.meta?.filterMergeWith ?? []\n return [\n column,\n ...mergeIds\n .map((id) => table.getColumn(id))\n .filter((c): c is Column<TData, unknown> => !!c),\n ]\n}\n\nfunction ColumnFilterTrigger<TData>({\n column,\n table,\n revealClassName,\n onOpenChange,\n tableDeferred,\n showFilterCounts,\n stickyFilterSelectAll,\n}: {\n column: Column<TData, unknown>\n table: TanstackTable<TData>\n /**\n * Hover/keyboard-focus-reveal classes for this trigger's own wrapper —\n * see `RowActions`' identical prop for why this lives here (controlled\n * `open` state, not a CSS `:hover`/`data-state` selector) rather than a\n * wrapping `<div>` at the call site. Ignored while the column is\n * filtered or the popover itself is open — both force it visible.\n */\n revealClassName?: string\n /** Fires when the popover opens/closes — see `RowActions`' identical prop. */\n onOpenChange?: (open: boolean) => void\n /** `DataTable`'s own `filterApplyMode` — adds the Cancel/Apply footer for non-multiselect fields, unless a field's own `meta.filterApplyMode` overrides it. A multiselect field (`select`/`range`) gets it regardless — see `isColumnDeferred`. */\n tableDeferred?: boolean\n showFilterCounts: boolean\n stickyFilterSelectAll: boolean\n}) {\n const [open, setOpenState] = React.useState(false)\n // Only read/written while `deferred` — a draft that `ColumnFilterField`\n // edits instead of the real filter, seeded from whatever's actually\n // committed each time the popover opens, so picks don't touch the real\n // table until Apply. Keyed by column id since a merged popover (see\n // `meta.filterMergeWith`) drafts more than one column's filter at once.\n const [pendingValues, setPendingValues] = React.useState<\n Record<string, unknown>\n >({})\n const mergedColumns = mergedFilterColumns(column, table)\n // Deferred for this whole popover if the table-wide setting (or any\n // merged field's own override) says so, or if *any* field in it (the\n // host column or a merged one) is a multiselect — one shared footer\n // covers every field here, so one deferred field is enough to put the\n // whole popover in that mode.\n const deferred = mergedColumns.some((c) =>\n isColumnDeferred(c, !!tableDeferred)\n )\n const setOpen = (value: boolean) => {\n if (value) {\n setPendingValues(\n Object.fromEntries(mergedColumns.map((c) => [c.id, c.getFilterValue()]))\n )\n }\n setOpenState(value)\n onOpenChange?.(value)\n }\n const label = columnLabel(column)\n const filtered = mergedColumns.some((c) => c.getIsFiltered())\n return (\n <div\n data-fit-exclude\n draggable={false}\n className={cn(\n \"shrink-0\",\n filtered || open ? \"opacity-100\" : revealClassName\n )}\n >\n <Popover open={open} onOpenChange={setOpen}>\n <Tooltip>\n <TooltipTrigger asChild>\n <PopoverTrigger asChild>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"xs\"\n iconOnly\n aria-label={`Filter ${label}`}\n className={cn(\"shrink-0\", filtered && \"text-primary\")}\n >\n <ListFilterIcon />\n </Button>\n </PopoverTrigger>\n </TooltipTrigger>\n <TooltipContent>{`Filter ${label}`}</TooltipContent>\n </Tooltip>\n <PopoverContent\n align=\"start\"\n className=\"max-w-(--radix-popover-content-available-width) min-w-56\"\n >\n <div className=\"flex flex-col gap-3\">\n {mergedColumns.map((c, index) => (\n <div\n key={c.id}\n className={cn(index > 0 && \"border-t border-border pt-3\")}\n >\n {/* Only labeled past the first field — this column's own\n filter needs no label (the popover's already anchored\n to its header), but a merged one needs to say which\n value it filters, since it isn't its own column. */}\n {mergedColumns.length > 1 && (\n <Label className=\"mb-1.5 block text-xs text-muted-foreground\">\n {columnLabel(c)}\n </Label>\n )}\n <ColumnFilterField\n column={c}\n value={deferred ? pendingValues[c.id] : c.getFilterValue()}\n onValueChange={(value) =>\n deferred\n ? setPendingValues((prev) => ({ ...prev, [c.id]: value }))\n : c.setFilterValue(value)\n }\n showFilterCounts={showFilterCounts}\n stickyFilterSelectAll={stickyFilterSelectAll}\n />\n </div>\n ))}\n </div>\n {deferred && (\n <FilterPopoverFooter\n onCancel={() => setOpen(false)}\n onApply={() => {\n mergedColumns.forEach((c) =>\n c.setFilterValue(pendingValues[c.id])\n )\n setOpen(false)\n }}\n // Every field sharing this footer has to actually change\n // before Apply can do anything — one shared footer covers\n // the whole popover (see `mergedColumns` above), so it's\n // disabled only while *all* of them still match what's\n // already applied.\n applyDisabled={mergedColumns.every((c) =>\n filterDraftUnchanged(c.getFilterValue(), pendingValues[c.id])\n )}\n />\n )}\n </PopoverContent>\n </Popover>\n </div>\n )\n}\n\n/** A pinned/always-visible filter, shown as a labeled button in the toolbar next to search. */\nfunction InlineColumnFilter<TData>({\n column,\n size,\n tableDeferred,\n showFilterCounts,\n stickyFilterSelectAll,\n}: {\n column: Column<TData, unknown>\n size: \"default\" | \"sm\" | \"xs\"\n /** `DataTable`'s own `filterApplyMode` — adds the Cancel/Apply footer for a non-multiselect field, unless `meta.filterApplyMode` overrides it for this column. A multiselect field (`select`/`range`) gets it regardless — see `isColumnDeferred`. Neither matters for the `singleSelect` branch below, which already closes on pick (`closeOnSelect`) — there's no open popover left by the time a footer could. */\n tableDeferred?: boolean\n showFilterCounts: boolean\n stickyFilterSelectAll: boolean\n}) {\n const label = columnLabel(column)\n const filterVariant = column.columnDef.meta?.filterVariant\n const deferred = isColumnDeferred(column, !!tableDeferred)\n const [open, setOpenState] = React.useState(false)\n // Only read/written while `deferred` — a draft picks edit instead of the\n // real filter, seeded from whatever's actually committed each time the\n // popover opens, so picks don't touch the real table until Apply.\n const [pendingValue, setPendingValue] = React.useState<unknown>(undefined)\n const setOpen = (value: boolean) => {\n if (value) setPendingValue(column.getFilterValue())\n setOpenState(value)\n }\n\n // Immediate: rendered directly inline, no popover/button chrome — a\n // custom filter is free to build its own trigger/popover if it wants\n // one, same as any other fully custom control would, and most don't\n // need one at all for a quick always-live control. Deferred: falls\n // through to the plain-text branch's own Popover+Button+`ColumnFilterField`\n // below instead (which already checks `filterRender` first and feeds it\n // the same open/`pendingValue` draft `select`/`range`/text already get)\n // — that gives it a real trigger, a place for the draft to live while\n // the popover's open, and the shared `FilterPopoverFooter`, none of\n // which an inline, no-lifecycle render has anywhere to put.\n if (column.columnDef.meta?.filterRender && !deferred) {\n return column.columnDef.meta.filterRender({\n column,\n value: column.getFilterValue(),\n onValueChange: (value) => column.setFilterValue(value),\n })\n }\n\n if (column.columnDef.meta?.filterOptionsLoading) {\n return <FilterOptionsSkeleton />\n }\n\n // Select/range columns use the Select primitive itself (SelectMultiple) as\n // the toolbar trigger, not a Button-triggered Popover — its trigger\n // already speaks \"column label as placeholder, selected labels as value,\n // clear-all x\" natively, no extra tinting needed on top of it. It only\n // ever shows the first selected value's label, `maxLabelChars` ellipsis-\n // truncating that one label past 12 characters — applied even with a\n // single value selected, since one long value can blow out the toolbar's\n // width same as a long list would — plus \"+N\" once more than one is\n // selected. `inlineLabel` keeps the column label attached (\"Status: Paid\")\n // instead of the value reading standalone.\n if (filterVariant === \"select\" || isBucketFilterVariant(column)) {\n const options = resolveFilterOptions(column, showFilterCounts)\n const committed = (column.getFilterValue() as string[] | undefined) ?? []\n // Draft only while actually open — closed, this shows the committed\n // value (correct the moment it mounts, e.g. right after the pane\n // applied this same column's filter, with no open interaction of its\n // own yet to have seeded `pendingValue`); open, it's live picks, not\n // frozen at whatever was last applied — checking a box should show as\n // checked immediately, same as the pane's own copy of this field.\n // Only the real *filter* (the table's rows) waits for Apply; the\n // visible selection itself doesn't.\n const effectiveDeferred = deferred && open\n const selected = effectiveDeferred\n ? ((pendingValue as string[] | undefined) ?? [])\n : committed\n return (\n <SelectMultiple\n size={size}\n value={selected}\n onValueChange={(next) => {\n const value = next.length ? next : undefined\n if (effectiveDeferred) setPendingValue(value)\n else column.setFilterValue(value)\n }}\n {...(deferred ? { open, onOpenChange: setOpen } : {})}\n >\n <SelectMultipleTrigger\n placeholder={label}\n inlineLabel={label}\n maxLabelChars={12}\n />\n <SelectMultipleContent>\n <SelectMultipleOptionsContent\n options={options}\n value={selected}\n onValueChangeAll={(next) => {\n if (effectiveDeferred) setPendingValue(next)\n else column.setFilterValue(next)\n }}\n stickySelectAll={stickyFilterSelectAll}\n />\n {deferred && (\n <SelectMultipleFooter>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"xs\"\n onClick={() => setOpen(false)}\n >\n Cancel\n </Button>\n <Button\n type=\"button\"\n size=\"xs\"\n disabled={filterDraftUnchanged(\n column.getFilterValue(),\n pendingValue\n )}\n onClick={() => {\n column.setFilterValue(pendingValue)\n setOpen(false)\n }}\n >\n Apply\n </Button>\n </SelectMultipleFooter>\n )}\n </SelectMultipleContent>\n </SelectMultiple>\n )\n }\n\n // singleSelect uses SelectMultiple capped at one value (`closeOnSelect`),\n // not plain Select — Select is unconditionally modal (no `modal` prop\n // exists on Radix's Select.Root at all) and its default\n // `position=\"item-aligned\"` renders the open list directly over its own\n // trigger, hiding this filter's inline label the moment it's open.\n // Inline toolbar filters sit next to other controls (search, other\n // filters, the table itself) the user needs to keep using while one is\n // open — blocking that (and hiding which column is being filtered) is\n // exactly what plain Select would do here. Picking the already-selected\n // value again toggles it off (clears), same as the old \"All\" item did;\n // the trigger's own clear \"x\" (from SelectMultipleTrigger, once\n // non-empty) covers the same case more directly.\n if (filterVariant === \"singleSelect\") {\n const options = resolveFilterOptions(column, showFilterCounts)\n const selected = (column.getFilterValue() as string | undefined) ?? \"\"\n return (\n <SelectMultiple\n size={size}\n closeOnSelect\n value={selected ? [selected] : []}\n onValueChange={(next) => column.setFilterValue(next.at(-1))}\n >\n <SelectMultipleTrigger placeholder={label} inlineLabel={label} />\n <SelectMultipleContent>\n <SelectMultipleOptionsContent\n options={options}\n value={selected ? [selected] : []}\n onValueChangeAll={() => {}}\n showSelectAll={false}\n stickySelectAll={stickyFilterSelectAll}\n />\n </SelectMultipleContent>\n </SelectMultiple>\n )\n }\n\n return (\n <Popover\n open={deferred ? open : undefined}\n onOpenChange={deferred ? setOpen : undefined}\n >\n <PopoverTrigger asChild>\n <Button\n type=\"button\"\n variant=\"outline\"\n size={size}\n className={cn(\n column.getIsFiltered() && \"border-primary text-primary\"\n )}\n >\n {label}\n <ChevronDownIcon data-icon=\"inline-end\" className=\"opacity-50\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent\n align=\"start\"\n className=\"max-w-(--radix-popover-content-available-width) min-w-56\"\n >\n <ColumnFilterField\n column={column}\n value={deferred ? pendingValue : column.getFilterValue()}\n onValueChange={\n deferred ? setPendingValue : (value) => column.setFilterValue(value)\n }\n showFilterCounts={showFilterCounts}\n stickyFilterSelectAll={stickyFilterSelectAll}\n />\n {deferred && (\n <FilterPopoverFooter\n onCancel={() => setOpen(false)}\n onApply={() => {\n column.setFilterValue(pendingValue)\n setOpen(false)\n }}\n applyDisabled={filterDraftUnchanged(\n column.getFilterValue(),\n pendingValue\n )}\n />\n )}\n </PopoverContent>\n </Popover>\n )\n}\n\nfunction DataTable<TData, TValue = unknown>({\n columns: userColumns,\n data,\n loading = false,\n error,\n errorDescription,\n emptyTitle = \"No results\",\n emptyDescription = \"Try adjusting your search.\",\n searchPlaceholder = \"Search...\",\n searchableColumnIds,\n searchMatch = \"contains\",\n filterApplyMode = \"immediate\",\n defaultSorting,\n defaultColumnFilters,\n defaultGlobalFilter,\n defaultPageIndex,\n defaultColumnVisibility,\n defaultColumnOrder,\n defaultColumnPinning,\n defaultColumnSizing,\n defaultExpanded,\n onExpandedChange,\n tableRef,\n onColumnLayoutChange,\n defaultPinnedFilterIds,\n allowFilterPinning = true,\n showFilterCounts = false,\n stickyFilterSelectAll = false,\n clearFiltersOnHide = false,\n enableRowSelection = false,\n selectionScope = \"filtered\",\n maxSelectedRows,\n defaultRowSelection,\n selectionBarPlacement,\n onRowSelectionChange,\n onStateChange,\n renderBulkActions,\n onRowClick,\n renderExpandedRow,\n getRowId,\n lockedColumnIds = [],\n enableColumnResizing = false,\n enableMultiSort = false,\n revealColumnControlsOnHover = true,\n striped = false,\n cellDividers = false,\n aiAction,\n actionButtons,\n aiRowPredicate,\n aiColumnIds,\n aiCellPredicate,\n pageSize = 10,\n pageSizeOptions = [10, 20, 30],\n showPageSizeSelector = true,\n paginationStyle = \"compact\",\n onLoadMore,\n loadMoreOffset = 200,\n hasMore = false,\n showColumnToggle = true,\n loadingMore = false,\n maxHeight,\n stickyHeader = true,\n caption,\n captionHidden = true,\n size = \"default\",\n className,\n \"aria-label\": ariaLabel,\n}: {\n columns: ColumnDef<TData, TValue>[]\n data: TData[]\n /** Renders skeleton rows shaped like the real columns instead of `data`. */\n loading?: boolean\n /** True renders the failure state (an Empty with `role=\"alert\"`) instead of rows. Passing a string/node here still works but is deprecated — put the copy in `errorDescription` instead, so \"is errored\" and \"what to say\" are separate. */\n error?: boolean | React.ReactNode\n /** Description shown in the failure state; defaults to \"Something went wrong. Try again.\" Only read while `error` is truthy. */\n errorDescription?: React.ReactNode\n emptyTitle?: string\n emptyDescription?: string\n searchPlaceholder?: string\n /** Column ids the search box matches against. Defaults to the first two entries in `columns`. `\"all\"` matches every column with a real accessor (`accessorKey`/`accessorFn`) — an injected column (selection checkbox, expand toggle, AI marker) has neither, so this never accidentally includes those. */\n searchableColumnIds?: string[] | \"all\"\n /** How the search box matches — default `\"contains\"` (anywhere in the value). `\"startsWith\"` only matches where a word starts, so \"nair\" finds \"Priya Nair\" but not \"Bernair\". Matches are highlighted the same way. */\n searchMatch?: \"contains\" | \"startsWith\"\n /**\n * Only affects the header-icon popover and inline toolbar's plain-text\n * field — a multiselect filter (`\"select\"`/`\"range\"`, a checklist of\n * several values at once) always requires Apply regardless of this\n * prop, in every surface (see `isMultiSelectFilterVariant`), and\n * `\"singleSelect\"` never does anywhere (closes on pick, before a footer\n * could matter). `\"immediate\"` (default): a text pick applies to the\n * real table as it's made, no confirmation step. `\"deferred\"` adds a\n * Cancel/Apply step to that same text field instead — picks edit a\n * local draft only, with no effect on the real table until Apply\n * commits it; Cancel just closes and discards it. Table-wide, not\n * per-column — a mix of immediate and deferred text filters in one\n * toolbar would be confusing about which picks need a second step. Has\n * no effect on the \"All filters\" pane at all — every field there is\n * either always live (text, singleSelect) or always deferred with its\n * own individual popover Apply/Cancel (select/range), never driven by\n * this prop.\n */\n filterApplyMode?: \"immediate\" | \"deferred\"\n /** Sort applied on first render — same shape as `@tanstack/react-table`'s `SortingState`. Uncontrolled from there on; the user's own header clicks take over. */\n defaultSorting?: SortingState\n /** Column filters applied on first render — same shape as `@tanstack/react-table`'s `ColumnFiltersState` (`[{ id: \"status\", value: [\"Paid\"] }, ...]`, `value` shaped however that column's own filter variant expects it). Uncontrolled from there on; the user's own picks take over. Paired with `defaultGlobalFilter` and `defaultSorting`, this is what makes restoring table state after a round trip (e.g. from a URL query param, on the way back from a detail page) possible — `onStateChange` reads that same state back out live, but had nothing to feed a value back in with until these existed. */\n defaultColumnFilters?: ColumnFiltersState\n /** Global search value applied on first render. Uncontrolled from there on — the user's own typing into the search box takes over. See `defaultColumnFilters`. */\n defaultGlobalFilter?: string\n /** Page index applied on first render (0-based). Uncontrolled from there on — the user's own pagination clicks take over. Ignored with `onLoadMore` (infinite scroll has no page concept). */\n defaultPageIndex?: number\n /** Column visibility applied on first render — same shape as `@tanstack/react-table`'s `VisibilityState`. Uncontrolled from there on; the Columns menu's own checkboxes take over. Also becomes the baseline \"Reset to defaults\" restores and `hasColumnCustomization` compares against, in place of \"everything visible.\" */\n defaultColumnVisibility?: VisibilityState\n /** Column order applied on first render — same shape as `@tanstack/react-table`'s `ColumnOrderState`, center (unpinned) columns only. Uncontrolled from there on; drag-reordering in the Columns menu takes over. Also becomes the \"Reset to defaults\" baseline in place of `columns`' own order. */\n defaultColumnOrder?: ColumnOrderState\n /** Column pinning applied on first render — same shape as `@tanstack/react-table`'s `ColumnPinningState`. Wins over any per-column `meta.pin` when provided; uncontrolled from there on, same as `defaultColumnOrder`. Also becomes the \"Reset to defaults\" baseline in place of `meta.pin`-derived pinning. */\n defaultColumnPinning?: ColumnPinningState\n /** Column widths applied on first render — same shape as `@tanstack/react-table`'s `ColumnSizingState`. Uncontrolled from there on; the user's own drag-resizing takes over. Also becomes what \"Reset to defaults\" restores widths to, in place of each column's own `size`. */\n defaultColumnSizing?: ColumnSizingState\n /** Expanded rows applied on first render — same shape as `@tanstack/react-table`'s `ExpandedState`. Uncontrolled from there on; the row's own expand toggle takes over. Only meaningful alongside `renderExpandedRow` or `getSubRows`. */\n defaultExpanded?: ExpandedState\n /** Fires after a row's expanded state changes, live — the `renderExpandedRow`/tree-data counterpart to `onRowSelectionChange`. Not controlled: there's no live two-way binding, but paired with `defaultExpanded` it round-trips the same way `onStateChange`/`defaultColumnFilters` do. */\n onExpandedChange?: (expanded: ExpandedState) => void\n /** A read-mostly escape hatch onto the underlying `@tanstack/react-table` instance — for anything DataTable doesn't already expose a prop for (e.g. a CSV export reading `table.getFilteredRowModel()`, or `table.setColumnFilters(...)` from an external \"saved views\" picker). A callback, not a ref object — assign it to a ref of your own inside the callback (`tableRef={(table) => (myRef.current = table)}`) if you need to hold onto it; called after mount, and again with `null` on unmount. Treat table methods called from outside as one-off imperative actions, not a second source of truth to render from. */\n tableRef?: (table: TanstackTable<TData> | null) => void\n /** Fires whenever committed (not live-dragging/live-previewing) column visibility, order, pinning, or sizing changes — after a resize ends, or a Columns-menu Apply/Reset, not on every intermediate drag pixel or menu preview tick. Pair with `defaultColumnVisibility`/`Order`/`Pinning`/`Sizing` to persist a user's layout (e.g. to `localStorage`) and restore it next session. */\n onColumnLayoutChange?: (layout: DataTableColumnLayout) => void\n /** Which filterable columns start pinned/inline in the toolbar (with a pin toggle in the \"All filters\" pane to change it live, unless `allowFilterPinning` is off). Defaults to the first two filterable entries in `columns`, in column order — set this instead when the columns you want inline aren't already first. */\n defaultPinnedFilterIds?: string[]\n /** Whether the \"All filters\" pane's per-row pin/unpin toggle renders at all — default `true`. Set `false` for a designer-fixed toolbar: `defaultPinnedFilterIds` (or its first-two-columns default) becomes the permanent, non-user-changeable inline set, and every other filterable column lives only in the pane, with no way to move it out. The toggle itself is hover/keyboard-focus-revealed by default, same as the header cell's own controls — see `revealColumnControlsOnHover`. */\n allowFilterPinning?: boolean\n /** Shows each option's `getFacetedUniqueValues()` count next to its label (\"Paid (124)\") across every `select`/`singleSelect`/`range` filter surface — the header-icon popover, the inline toolbar chip, and the pane. Off by default: a count only means something once a consumer actually wants \"how many rows have this value\" surfaced, and it costs a facet lookup per option on every render otherwise skipped. */\n showFilterCounts?: boolean\n /** Whether \"Select all\" in a multi-select filter's option list (once past its own, lower option-count threshold — independent of the search box's) stays pinned above the list while it scrolls — default `false`: it's the first row inside the same scrolling area as the options themselves, same as a plain checkbox list where nothing else stays fixed either. Set `true` to keep it visible regardless of scroll position instead. */\n stickyFilterSelectAll?: boolean\n /** Clears a column's filter the moment it's hidden via the Columns menu — default `false` (a hidden column's filter stays active, still visible in the pane with a muted \"(hidden)\" suffix, since hiding a column isn't the same action as clearing what it's filtered to). Set `true` if hiding a column should mean \"I'm done filtering by this\" instead. */\n clearFiltersOnHide?: boolean\n /** Turns on the row-selection checkbox column. A function instead of a plain `true` makes specific rows unselectable — e.g. `(row) => row.original.status !== \"Completed\"` — the cell checkbox `disabled`s for those rows and neither the header nor \"Select all\" checkbox counts them. */\n enableRowSelection?: boolean | ((row: Row<TData>) => boolean)\n /** What \"selected\" means once a filter hides some of the checked rows — default `\"filtered\"`: selection state itself never loses those rows (clearing the filter shows them checked again), but the count, `renderBulkActions`, and `onRowSelectionChange` all act only on rows that currently pass every filter, so a bulk action can never silently reach past what's on screen. `\"all\"` restores pre-1.x behavior, where a bulk action acts on every checked row regardless of the active filter — opt into this only if that's a deliberate multi-pass selection workflow. */\n selectionScope?: \"filtered\" | \"all\"\n /** Caps how many rows can be checked at once — every unselected checkbox (row, header, \"Select all\") disables once the cap is hit, so a user has to uncheck something before adding another. Already-checked rows stay checkable (to uncheck) past the cap. */\n maxSelectedRows?: number\n /** Rows checked on first render — same shape as `@tanstack/react-table`'s `RowSelectionState` (`{ [rowId]: true }`). Uncontrolled from there on. Meaningless without `getRowId`: row identity otherwise defaults to render index, which this has no way to match against your data ahead of the first render. */\n defaultRowSelection?: RowSelectionState\n /** Where the selection UI (count, \"Select all\", `renderBulkActions`) lives in paginated mode — default `\"toolbar\"` swaps search/filters out for it there; `\"floating\"` keeps search/filters mounted and shows the same floating pill infinite-scroll tables always use instead. No effect with `onLoadMore` set — infinite scroll has no footer to swap into, so it's always floating. */\n selectionBarPlacement?: \"toolbar\" | \"floating\"\n /** `details.hiddenByFilterCount` is how many additional rows are checked but excluded from `selectedRows` by `selectionScope=\"filtered\"` (always `0` under `\"all\"`); `details.allSelectedRows` is the unfiltered full set regardless of scope. */\n onRowSelectionChange?: (\n selectedRows: TData[],\n details: { allSelectedRows: TData[]; hiddenByFilterCount: number }\n ) => void\n /** Fires after sorting, column filters, search, pagination, expansion, or pinned-filter changes, live — a read-only escape hatch for syncing this internal state elsewhere (URL params, analytics, a server-driven refetch). Not a controlled-mode prop — there's no live two-way binding — but paired with `defaultSorting`/`defaultColumnFilters`/`defaultGlobalFilter`/`defaultPageIndex`/`defaultExpanded`/`defaultPinnedFilterIds` it's enough to round-trip state out to a URL and back in on next mount. */\n onStateChange?: (state: {\n sorting: SortingState\n columnFilters: ColumnFiltersState\n globalFilter: string\n pagination: PaginationState\n expanded: ExpandedState\n pinnedFilterIds: string[]\n }) => void\n /** Product-configured actions shown while rows are selected — on an infinite-scroll table (`onLoadMore`), a pill that hovers over the table's own bottom edge (count + Select all on the left, this on the right of a divider, a close button after another); on a paginated table, search/filters in the toolbar are swapped out for the count, Select all, this, and a close button instead — no separate bar. Omit either way for just the count/Select all/close with no actions. */\n renderBulkActions?: (\n selectedRows: TData[],\n clearSelection: () => void\n ) => React.ReactNode\n /** Makes every row clickable/keyboard-activatable — e.g. navigating to a detail view. Combine with `enableRowSelection` freely; clicking the row checkbox never triggers this. */\n onRowClick?: (row: TData) => void\n /** Renders below a row when expanded — a detail card, or more rows of its own. Injects a leading expand/collapse toggle on every row. */\n renderExpandedRow?: (row: TData) => React.ReactNode\n /** A stable per-row id (e.g. `(row) => row.id`), passed straight through to `@tanstack/react-table`. Without it, a row's identity defaults to its index — fine for rendering, but meaningless across a sort/filter/pagination change, where identity is what row-selection/expansion state tracks against. */\n getRowId?: (originalRow: TData, index: number) => string\n /** Column ids the Columns menu won't let the user reorder. Injected columns are always locked. */\n lockedColumnIds?: string[]\n /** Adds a drag handle to every resizable column's right border (injected columns never resize). Also required for `columnMenuActions`' \"Fit to content\" to render at all — it checks `column.getCanResize()`, same as the handle. Set `enableResizing: false` on a `ColumnDef` to exempt a specific column. */\n enableColumnResizing?: boolean\n /** Shift-click a sortable header to add it to the existing sort instead of replacing it — `sorting` then holds more than one entry, in click order, and each sorted header shows a small 1/2/3 badge next to its arrow marking that order. `aria-sort` on any one header still only ever reflects that column's own direction (the attribute has no concept of rank), so the badge is the only place the *order* is actually conveyed. Default `false`. */\n enableMultiSort?: boolean\n /** Whether the header-cell filter icon, `headerActions`/`columnMenuActions` menu, and the Columns-menu popover's own pin/move controls stay hidden until hovered or keyboard-focused, or render always-visible. Default `true` — set `false` for a consumer that wants every control visible up front instead of discovered on hover. */\n revealColumnControlsOnHover?: boolean\n /** Tints every other body row for readability on wide/dense tables. */\n striped?: boolean\n /** Adds a vertical rule between columns, for tables where column boundaries need to read clearly. */\n cellDividers?: boolean\n /** Renders a `variant=\"ai\"` action in the toolbar — table-level AI presence (e.g. \"Summarize\", \"Ask AI\"). There's no `ai` boolean/whole-table glow prop — DataTable's own outer surface never takes a border/shadow of its own; wrap it in a Card instead and put the glow + an `AILabel` on that, same as any other AI-marked surface. See the \"WithAIWholeTable\" story. */\n aiAction?: { label: string; onClick: () => void }\n /** Product-configured toolbar actions, rendered in order right before the Columns menu — persistent, unlike `renderBulkActions` (selection-scoped) or `aiAction` (always `variant=\"ai\"`, rendered separately, and the one toolbar control that stays at `size` itself instead of the notch-below `controlSize`/`controlIconSize` every other toolbar control — including this one, every variant — renders at). Cap at 3: this is a designer call per table, not a user preference, and a toolbar reads as noise past that. `\"primary\"` maps to a filled `variant=\"default\"` Button (keep this the artifact's only filled button — never pair two), `\"secondary\"` to `variant=\"outline\"`, `\"icon\"` to an icon-only `variant=\"outline\"` Button (needs `icon` set; `label` still required, used as its `aria-label` since there's no visible text). */\n actionButtons?: {\n label: string\n onClick: () => void\n variant?: \"primary\" | \"secondary\" | \"icon\"\n icon?: React.ReactNode\n disabled?: boolean\n }[]\n /** Rows this returns true for get a colored left edge (an inset box-shadow on the row's first cell — the table is `border-separate`, so a `<tr>` itself can never paint a border) plus a soft inward AI glow bleeding in from that same edge (`bg-ai-glow`, not a solid fill — echoes `aiColumnIds`' colored top rule, just on the row's left edge instead) and a leading `AILabel` — the very first thing in the row, ahead of the expand toggle and selection checkbox — row-level AI presence. */\n aiRowPredicate?: (row: TData) => boolean\n /** Every cell in a column whose id is in this list gets the same left-glowing inward glow, tiling seamlessly down the whole column since every cell in it shares the same width; that column's header also gets a colored top rule and a leading `AILabel` (generic explanatory copy — compose `AILabel` directly into that column's own `header` instead of using this if you need column-specific text) — column-level AI presence. */\n aiColumnIds?: string[]\n /** Individual cells this returns true for get a trailing `AILabel` inline after the value — no glow at all, unlike row/column level — cell-level AI presence, for when only specific values (not a whole row or column) are AI-generated. */\n aiCellPredicate?: (row: TData, columnId: string) => boolean\n pageSize?: number\n /** Choices shown in the footer's \"items per page\" control. */\n pageSizeOptions?: number[]\n /** Whether the \"Items per page\" control renders in the footer at all — a designer's call per table, not a user preference. Default true. */\n showPageSizeSelector?: boolean\n /** How the footer's page indicator renders — default `\"compact\"`: a page-jump control (a `Select` past a plain click-through, or a number `Input` once page count passes `PAGE_SELECT_THRESHOLD`) plus \"of N pages\" text, next to Previous/Next icon buttons. `\"numbered\"`: `Pagination`'s own numbered links with ellipsis windowing around the current page, first/last page always reachable directly. */\n paginationStyle?: \"compact\" | \"numbered\"\n /** Switches to infinite scroll: every row in `data` renders (no client pagination, no footer at all), the table scrolls internally with a locked header (and locked pinned columns) instead of the page, and this is called when the user scrolls near the last row. Append the next page to `data` yourself; pair with `hasMore`/`loadingMore`. */\n onLoadMore?: () => void\n /** How far (in px) from the bottom of the scroll area the next page starts loading — default `200`. Raise it to prefetch earlier on slow endpoints, `0` to load only when the last row is actually reached. Only relevant with `onLoadMore`. */\n loadMoreOffset?: number\n /** Whether there's more to load — when false, the scroll trigger stops rendering (and observing) entirely. Only relevant with `onLoadMore`. */\n hasMore?: boolean\n /** Shows the toolbar's \"Columns\" button (visibility, order and pinning) — default `true`. Set `false` for tables whose column set is fixed. */\n showColumnToggle?: boolean\n /** Shows skeleton rows below the last real row while the next page is in flight, same shape as `loading`. Only relevant with `onLoadMore`. */\n loadingMore?: boolean\n /** Caps how tall the internal scroll container can grow (any CSS size). In infinite-scroll mode (`onLoadMore`) this is the container's real height, defaulting to `\"32rem\"` — it has no other height source. In paginated mode it's an upper bound only, layered on top of whatever height the surrounding layout already gives the rows area (e.g. a fixed-height Card) — omit it there and nothing changes. */\n maxHeight?: string\n /** Whether the header row sticks to the top of the scroll container instead of scrolling away with the body — default `true`. */\n stickyHeader?: boolean\n /** An accessible-name fallback rendered as a real `TableCaption` — set this (or `aria-label`) so the table has *some* accessible name; a dev-only console warning fires when neither is given. `captionHidden` (default `true`) keeps it visually `sr-only` while still counting as the table's name; set `false` to actually show it above the rows. */\n caption?: React.ReactNode\n captionHidden?: boolean\n size?: \"default\" | \"sm\" | \"xs\"\n className?: string\n \"aria-label\"?: string\n}) {\n React.useEffect(() => {\n if (process.env.NODE_ENV === \"production\") return\n if (actionButtons && actionButtons.length > 3) {\n console.warn(\n `DataTable: \\`actionButtons\\` has ${actionButtons.length} entries, but only the first 3 render in the toolbar — the rest are silently dropped. Move the overflow into a dropdown of your own, or trim the list.`\n )\n }\n if (typeof error !== \"boolean\" && error != null) {\n console.warn(\n \"DataTable: passing a string/node to `error` is deprecated — put that copy in `errorDescription` instead and pass `error={true}`. `error` will stop accepting anything but a boolean in a future version.\"\n )\n }\n if (!ariaLabel && !caption) {\n console.warn(\n \"DataTable: no `aria-label` or `caption` provided — the table has no accessible name, so assistive tech has nothing to announce it by. Pass one describing what the table shows.\"\n )\n }\n if (userColumns.some((col) => col.meta?.filterVariant === \"range\")) {\n console.warn(\n 'DataTable: `filterVariant: \"range\"` is deprecated — use `\"bucket\"` instead (same behavior, clearer name). `\"range\"` will stop being accepted in a future version.'\n )\n }\n if (userColumns.some((col) => col.meta?.rangeBucketSize != null)) {\n console.warn(\n 'DataTable: `meta.rangeBucketSize` is deprecated — use `meta.bucketSize` instead (same option, renamed to match `filterVariant: \"bucket\"`).'\n )\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- dev-only warnings, deliberately not re-checked on every userColumns identity change (only actionButtons/error/ariaLabel are expected to change often enough to matter)\n }, [actionButtons, error, ariaLabel, caption])\n\n const [sorting, setSorting] = React.useState<SortingState>(\n defaultSorting ?? []\n )\n const [columnVisibility, setColumnVisibility] =\n React.useState<VisibilityState>(defaultColumnVisibility ?? {})\n const [columnOrder, setColumnOrder] = React.useState<ColumnOrderState>(\n defaultColumnOrder ?? []\n )\n // `defaultColumnPinning` wins over `meta.pin` when given — `meta.pin`\n // remains the per-column seed for a consumer that hasn't bothered with\n // the table-level prop. Either way this is a lazy useState initializer,\n // not a live-recomputing useMemo, since pinning is user-togglable from\n // there on (via a `headerActions` Pin action, e.g. `columnMenuActions`,\n // or any custom `column.pin(...)` call).\n const [columnPinning, setColumnPinning] = React.useState<ColumnPinningState>(\n () => defaultColumnPinning ?? derivePinningFromMeta(userColumns)\n )\n const [columnSizing, setColumnSizing] = React.useState<ColumnSizingState>(\n defaultColumnSizing ?? {}\n )\n // Scopes `columnMenuActions`' \"Fit to content\" DOM measurement to just\n // this table instance (`getColumnCellElements`, wired into the table's\n // `meta` below) — see `fitColumnToContent`.\n const tableContainerRef = React.useRef<HTMLDivElement>(null)\n // Measures BulkActionsBar's real rendered height so the spacer right\n // after the last row can match it exactly — the bar overlays rows\n // rather than pushing them (see its own comment), so without this the\n // last row(s) would stay permanently hidden behind it once scrolled to\n // the bottom.\n const bulkActionsBarRef = React.useRef<HTMLDivElement>(null)\n const [bulkActionsBarHeight, setBulkActionsBarHeight] = React.useState(0)\n const mainColumnRef = React.useRef<HTMLDivElement>(null)\n const footerRef = React.useRef<HTMLDivElement>(null)\n const [columnSearchQuery, setColumnSearchQuery] = React.useState(\"\")\n const [filtersPaneOpen, setFiltersPaneOpen] = React.useState(false)\n const [columnsMenuOpen, setColumnsMenuOpen] = React.useState(false)\n // Column visibility/order/pinning changes apply live (so the menu's own\n // preview and the real table never disagree) — Cancel un-applies them by\n // restoring whatever was committed when the menu opened; Apply just\n // keeps what's already there. Refs, not state: nothing here needs a\n // re-render of its own.\n const columnsMenuSnapshotRef = React.useRef<{\n visibility: VisibilityState\n order: ColumnOrderState\n pinning: ColumnPinningState\n } | null>(null)\n const columnsMenuAppliedRef = React.useRef(false)\n const [paginationState, setPaginationState] = React.useState<PaginationState>(\n {\n pageSize,\n pageIndex: defaultPageIndex ?? 0,\n }\n )\n // A restored `defaultPageIndex` with data still loading (empty on mount)\n // would be wiped by TanStack's auto-reset the moment rows arrive — hold\n // the reset off until the first non-empty `data`.\n const [pageRestorePending, setPageRestorePending] = React.useState(\n Boolean(defaultPageIndex) && data.length === 0\n )\n React.useEffect(() => {\n if (pageRestorePending && data.length > 0) setPageRestorePending(false)\n }, [pageRestorePending, data.length])\n const [rowSelection, setRowSelection] = React.useState<RowSelectionState>(\n defaultRowSelection ?? {}\n )\n // Keyed on raw `rowSelection` state, not any filtered/scoped view of it —\n // the cap is a hard ceiling on how many rows are checked at all, so it\n // has to hold regardless of `selectionScope` or what's currently filtered\n // into view.\n const atSelectionCap =\n maxSelectedRows != null &&\n Object.keys(rowSelection).length >= maxSelectedRows\n const effectiveEnableRowSelection = React.useCallback(\n (row: Row<TData>) => {\n const allowed =\n typeof enableRowSelection === \"function\"\n ? enableRowSelection(row)\n : enableRowSelection\n if (!allowed) return false\n return !atSelectionCap || row.getIsSelected()\n },\n [enableRowSelection, atSelectionCap]\n )\n const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(\n defaultColumnFilters ?? []\n )\n const filtersPaneRef = React.useRef<HTMLDivElement>(null)\n const filtersPaneId = React.useId()\n const filtersPaneHeadingId = React.useId()\n const filtersPaneTriggerRef = React.useRef<HTMLButtonElement>(null)\n const filtersPaneCloseButtonRef = React.useRef<HTMLButtonElement>(null)\n // Focus moves in on open (the pane's own close button — the first real\n // focusable thing inside it) and back out on close (the trigger that\n // opened it) — otherwise focus just silently stays wherever it was\n // (the trigger, mid-DOM-removal, or nowhere at all once that trigger\n // itself unmounts while open), which is the number one way a docked\n // panel like this one strands a keyboard/AT user with no sense of\n // where they landed.\n const filtersPaneWasOpenRef = React.useRef(false)\n React.useEffect(() => {\n if (filtersPaneOpen && !filtersPaneWasOpenRef.current) {\n filtersPaneCloseButtonRef.current?.focus()\n } else if (!filtersPaneOpen && filtersPaneWasOpenRef.current) {\n filtersPaneTriggerRef.current?.focus()\n }\n filtersPaneWasOpenRef.current = filtersPaneOpen\n }, [filtersPaneOpen])\n // The pane sits outside DataTable's own box (a flex sibling of the main\n // column), so when DataTable is placed inside a Card that also renders a\n // CardHeader/CardFooter above/below it, the pane's top/bottom edges fall\n // short of the Card's true edges by however tall that surrounding content\n // is — not a fixed spacing token, so it's measured in JS (mirrors the\n // FLIP drag-reorder measurement below) rather than expressed in CSS.\n const [filtersPaneEdgeAdjust, setFiltersPaneEdgeAdjust] = React.useState<{\n marginTop: number\n height: number\n } | null>(null)\n const [expanded, setExpanded] = React.useState<ExpandedState>(\n defaultExpanded ?? {}\n )\n React.useEffect(() => {\n onExpandedChange?.(expanded)\n // eslint-disable-next-line react-hooks/exhaustive-deps -- fire when the state itself changes, not when the callback identity changes\n }, [expanded])\n const [globalFilter, setGlobalFilter] = React.useState(\n defaultGlobalFilter ?? \"\"\n )\n // The input's own value — updates every keystroke so typing feels\n // instant. `globalFilter` (the real filter driving row filtering, read\n // by `onStateChange`, everything else) only catches up 200ms after\n // typing stops, via `debouncedSetGlobalFilter` below — refiltering the\n // whole row model on every single keystroke is real, measurable cost on\n // a big table, and nothing about typing needs that to happen mid-word.\n const [searchInput, setSearchInput] = React.useState(\n defaultGlobalFilter ?? \"\"\n )\n const debouncedSetGlobalFilter = useDebouncedCallback(setGlobalFilter, 200)\n const [pinnedFilterIds, setPinnedFilterIds] = React.useState<string[]>(() => {\n if (defaultPinnedFilterIds) return defaultPinnedFilterIds\n const mergedAwayIds = new Set(\n userColumns.flatMap((col) => col.meta?.filterMergeWith ?? [])\n )\n return userColumns\n .filter(\n (col) =>\n col.enableColumnFilter !== false &&\n !col.meta?.filterOnly &&\n !mergedAwayIds.has(getColumnId(col) ?? \"\")\n )\n .slice(0, 2)\n .map(getColumnId)\n .filter((id): id is string => !!id)\n })\n\n function toggleFilterPin(columnId: string) {\n setPinnedFilterIds((prev) =>\n prev.includes(columnId)\n ? prev.filter((id) => id !== columnId)\n : [...prev, columnId]\n )\n }\n\n const searchableIds = React.useMemo(() => {\n if (searchableColumnIds === \"all\") {\n return userColumns\n .filter((col) => \"accessorKey\" in col || \"accessorFn\" in col)\n .map(getColumnId)\n .filter((id): id is string => !!id)\n }\n return (\n searchableColumnIds ??\n userColumns\n .slice(0, 2)\n .map(getColumnId)\n .filter((id): id is string => !!id)\n )\n }, [searchableColumnIds, userColumns])\n\n const columns = React.useMemo<ColumnDef<TData, unknown>[]>(() => {\n const processedUserColumns = userColumns.map((col) => {\n const id = getColumnId(col)\n const isSearchable = id !== undefined && searchableIds.includes(id)\n const isLocked = id !== undefined && lockedColumnIds.includes(id)\n const isFilterOnly = !!col.meta?.filterOnly\n const next = {\n ...col,\n enableGlobalFilter: col.enableGlobalFilter ?? isSearchable,\n enableColumnFilter: col.enableColumnFilter ?? true,\n // A locked column's position can't change — it also can't be hidden,\n // it always shows, regardless of what the column's own ColumnDef\n // says. A `filterOnly` column is the opposite: it can't be hidden\n // either, but because it's forced permanently *invisible* instead\n // (see `columnVisibility` below) — there's no checkbox for either\n // case, since `manageableColumns` excludes `filterOnly` columns\n // entirely (see there for why).\n enableHiding: isLocked || isFilterOnly ? false : col.enableHiding,\n minSize: col.minSize ?? computeColumnMinSize(col, size),\n // A column deliberately given a wide `size` (say 2000, for a long\n // free-text column) keeps that as its own ceiling instead of being\n // clamped straight back down to the default the moment a user\n // tries to *drag*-resize it — only a column with no opinion of its\n // own gets the flat default ceiling.\n maxSize:\n col.maxSize ??\n (col.size && col.size > DEFAULT_MAX_COLUMN_SIZE\n ? undefined\n : DEFAULT_MAX_COLUMN_SIZE),\n filterFn:\n col.filterFn ??\n (col.meta?.filterVariant === \"select\"\n ? (selectFilterFn as FilterFn<TData>)\n : col.meta?.filterVariant === \"range\" ||\n col.meta?.filterVariant === \"bucket\"\n ? (rangeFilterFn as FilterFn<TData>)\n : col.meta?.filterVariant === \"singleSelect\"\n ? (singleSelectFilterFn as FilterFn<TData>)\n : \"includesString\"),\n } as ColumnDef<TData, unknown>\n if (!col.cell && isSearchable) {\n // Reads the query straight off table state at render time instead\n // of closing over the `globalFilter` variable — closing over it\n // would need `globalFilter` in this memo's own deps, rebuilding\n // every `ColumnDef` (and so invalidating every one of TanStack's\n // row models) on every keystroke instead of just the one row\n // model search actually needs to touch.\n next.cell = (info) => (\n <HighlightMatch\n text={String(info.getValue() ?? \"\")}\n query={String(info.table.getState().globalFilter ?? \"\")}\n startsWith={searchMatch === \"startsWith\"}\n />\n )\n }\n return next\n }) as ColumnDef<TData, unknown>[]\n\n const leadingColumns: ColumnDef<TData, unknown>[] = []\n\n if (aiRowPredicate) {\n leadingColumns.push({\n id: AI_COLUMN_ID,\n // Visually empty header, but never an empty <th>: assistive tech\n // reads column headers, and an empty one is flagged (axe\n // empty-table-header).\n header: () => <span className=\"sr-only\">AI</span>,\n // First thing in the row, ahead of the expand toggle and the\n // selection checkbox — its own leading slot, not competing with\n // either for the same one. `size` follows the table's own density\n // prop (via AIMarkerTrigger — \"xs\" density still gets the \"sm\"\n // marker, never AILabel's opt-in `iconOnly`), so the marker\n // matches the density of the rest of the row while keeping its\n // visible \"AI\" text.\n cell: ({ row }) =>\n aiRowPredicate(row.original) ? (\n <AILabel>\n <AIMarkerTrigger size={size} ariaLabel=\"AI-suggested row\" />\n <AILabelContent>\n <PopoverHeader>\n <PopoverTitle>Why you're seeing this</PopoverTitle>\n </PopoverHeader>\n <PopoverDescription>\n This row's values are generated or suggested by AI.\n </PopoverDescription>\n </AILabelContent>\n </AILabel>\n ) : null,\n enableSorting: false,\n enableHiding: false,\n enableGlobalFilter: false,\n enableColumnFilter: false,\n enablePinning: false,\n enableResizing: false,\n size: 56,\n })\n }\n\n if (renderExpandedRow) {\n leadingColumns.push({\n id: EXPAND_COLUMN_ID,\n header: () => <span className=\"sr-only\">Expand</span>,\n cell: ({ row }) =>\n row.getCanExpand() ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"xs\"\n iconOnly\n aria-label={row.getIsExpanded() ? \"Collapse row\" : \"Expand row\"}\n // The label swap above is the only *visual* cue outside the\n // chevron rotation — this is the Disclosure pattern's actual\n // state attribute, the one AT actually keys off. Without it,\n // tabbing back onto an already-expanded toggle re-affirms\n // nothing beyond a label a screen reader already announced\n // once. aria-controls needs the expanded content row to\n // exist and carry this exact id — see the matching id below.\n aria-expanded={row.getIsExpanded()}\n aria-controls={`data-table-expanded-${row.id}`}\n onClick={(event) => {\n event.stopPropagation()\n row.toggleExpanded()\n }}\n >\n <ChevronRightIcon\n className={cn(\n \"transition-transform\",\n row.getIsExpanded() && \"rotate-90\"\n )}\n />\n </Button>\n ) : null,\n enableSorting: false,\n enableHiding: false,\n enableGlobalFilter: false,\n enableColumnFilter: false,\n enablePinning: false,\n enableResizing: false,\n size: 32,\n })\n }\n\n if (enableRowSelection) {\n leadingColumns.push({\n id: SELECTION_COLUMN_ID,\n // See SELECTION_CHECKBOX_CLASS above for why this isn't just\n // `size-6` (that satisfies axe's target-size check but visibly\n // enlarges the checkbox itself — the box stays the usual 16px\n // look, only the real interactive/hit area is 24px).\n header: ({ table }) => {\n // Judged against the consumer's own `enableRowSelection`, not the\n // cap-aware one wired into the table — once the cap is hit every\n // unchecked row reports `getCanSelect() === false`, which would\n // make TanStack's all-page-selected check vacuously true.\n const state = table.getState().rowSelection\n const pageRows = table\n .getRowModel()\n .rows.filter((row) =>\n typeof enableRowSelection === \"function\"\n ? enableRowSelection(row)\n : !!enableRowSelection\n )\n const selectedCount = pageRows.filter((row) => state[row.id]).length\n const allSelected =\n pageRows.length > 0 && selectedCount === pageRows.length\n const capReached =\n maxSelectedRows != null &&\n Object.keys(state).length >= maxSelectedRows\n return (\n <Checkbox\n className={SELECTION_CHECKBOX_CLASS}\n aria-label={\n onLoadMore ? \"Select all rows\" : \"Select all rows on this page\"\n }\n checked={\n allSelected ? true : selectedCount > 0 ? \"indeterminate\" : false\n }\n disabled={capReached && !allSelected && selectedCount === 0}\n title={\n capReached && !allSelected && selectedCount === 0\n ? `Up to ${maxSelectedRows} rows can be selected`\n : undefined\n }\n onCheckedChange={(value) => {\n const next = { ...state }\n // At the cap with a partial page, \"select all\" can't add\n // anything — treat the click as \"clear this page\" instead.\n if (!value || (capReached && !allSelected)) {\n pageRows.forEach((row) => delete next[row.id])\n } else {\n let remaining =\n maxSelectedRows != null\n ? Math.max(0, maxSelectedRows - Object.keys(next).length)\n : Infinity\n for (const row of pageRows) {\n if (next[row.id]) continue\n if (remaining <= 0) break\n next[row.id] = true\n remaining -= 1\n }\n }\n table.setRowSelection(next)\n }}\n />\n )\n },\n cell: ({ row }) => (\n <Checkbox\n className={SELECTION_CHECKBOX_CLASS}\n aria-label=\"Select row\"\n checked={row.getIsSelected()}\n disabled={!row.getCanSelect()}\n title={\n !row.getCanSelect() && atSelectionCap && !row.getIsSelected()\n ? `Up to ${maxSelectedRows} rows can be selected`\n : undefined\n }\n onCheckedChange={(value) => row.toggleSelected(!!value)}\n onClick={(event) => event.stopPropagation()}\n />\n ),\n enableSorting: false,\n enableHiding: false,\n enableGlobalFilter: false,\n enableColumnFilter: false,\n enablePinning: false,\n enableResizing: false,\n // 40, not the expand column's 32 — the now-`size-6` (24px)\n // checkbox plus the cell's own `p-2` (8px each side) needs that\n // much just to render unclipped, let alone with any real space\n // around it; at 32 axe's target-size check flagged it as\n // \"partially obscured\" even though the checkbox's own box really\n // is 24px, because there was nowhere for it to actually sit.\n size: 40,\n })\n }\n\n return [...leadingColumns, ...processedUserColumns]\n // `globalFilter` deliberately excluded — see the `HighlightMatch` cell\n // above, which reads it from table state at render time instead of\n // closing over it here, specifically so a search keystroke doesn't\n // also rebuild every `ColumnDef` (and invalidate every TanStack row\n // model wholesale) on top of the filtering that keystroke already\n // costs.\n }, [\n userColumns,\n enableRowSelection,\n atSelectionCap,\n maxSelectedRows,\n searchableIds,\n searchMatch,\n renderExpandedRow,\n aiRowPredicate,\n lockedColumnIds,\n size,\n onLoadMore,\n ])\n\n // `meta.filterOnly` columns are forced hidden here — not just excluded\n // from the Columns menu's checkboxes above — because visibility itself is\n // state, not something a `ColumnDef` can dictate; without this override\n // one could still end up rendered (e.g. `resetColumns` restoring whatever\n // `columnVisibility` looked like before).\n const effectiveColumnVisibility = React.useMemo(() => {\n const forcedHidden = userColumns.reduce<VisibilityState>((acc, col) => {\n const id = getColumnId(col)\n if (id && col.meta?.filterOnly) acc[id] = false\n return acc\n }, {})\n return { ...columnVisibility, ...forcedHidden }\n }, [columnVisibility, userColumns])\n\n // The injected AI/expand/selection columns document themselves as always\n // leading the row (\"first thing in the row, ahead of...\"), but that's\n // only true by accident of definition order — they're `enablePinning:\n // false` (no user toggle of their own) and not part of `columnPinning`,\n // so they render wherever they fall in the *center* group. The moment a\n // real column is pinned left, left-pinned columns render before the\n // center group entirely (`getVisibleCells()` is `[...left, ...center,\n // ...right]`), which pushes these behind it — breaking both the leading\n // `AILabel`'s claim to be first and the row/column AI glow fix above\n // (which depends on the *true* first cell being the one that's pinned).\n // Prepending them into the *effective* left-pinned group — only once\n // something is genuinely pinned left, never touching the raw state\n // Cancel/Apply/Reset compare against — makes them actually sticky and\n // first, restoring that guarantee without changing anything for a table\n // that never pins.\n const effectiveColumnPinning = React.useMemo(() => {\n const leadingIds = [\n aiRowPredicate && AI_COLUMN_ID,\n renderExpandedRow && EXPAND_COLUMN_ID,\n enableRowSelection && SELECTION_COLUMN_ID,\n ].filter((id): id is string => !!id)\n const left = columnPinning.left ?? []\n if (leadingIds.length === 0 || left.length === 0) return columnPinning\n return {\n ...columnPinning,\n left: [...leadingIds, ...left.filter((id) => !leadingIds.includes(id))],\n }\n }, [columnPinning, aiRowPredicate, renderExpandedRow, enableRowSelection])\n\n // Faceting (`getFacetedRowModel`/`getFacetedUniqueValues`) is a full\n // extra pass over the dataset on every filter change — worth it for the\n // variants that actually read `column.getFacetedUniqueValues()`\n // (`enumerableFilterOptions`, used by select/singleSelect/range) or a\n // custom `filterRender` that might (its own docs invite calling it), but\n // dead weight otherwise. Recomputed with the same cheap `.some()` every\n // render rather than memoized — `userColumns` is small and this is far\n // cheaper than the faceting itself.\n const needsFacetedFiltering = userColumns.some(\n (col) =>\n col.meta?.filterVariant === \"select\" ||\n col.meta?.filterVariant === \"singleSelect\" ||\n col.meta?.filterVariant === \"range\" ||\n col.meta?.filterVariant === \"bucket\" ||\n col.meta?.filterRender\n )\n // `getFacetedMinMaxValues` is its own separate pass (bounds, not the\n // full unique-value list `needsFacetedFiltering` above covers) — no\n // built-in variant reads it, but a custom `filterRender` might (a\n // number-range Slider built from MMDS's own `Slider` primitive, e.g.),\n // same reasoning as `filterRender` counting toward `needsFacetedFiltering`\n // above.\n const needsFacetedMinMax = userColumns.some((col) => col.meta?.filterRender)\n\n const table = useReactTable({\n data,\n columns,\n getRowId,\n state: {\n sorting,\n columnVisibility: effectiveColumnVisibility,\n columnOrder,\n columnPinning: effectiveColumnPinning,\n columnSizing,\n rowSelection,\n columnFilters,\n expanded,\n globalFilter,\n pagination: paginationState,\n },\n onSortingChange: setSorting,\n onColumnVisibilityChange: (updater) => {\n setColumnVisibility((old) => {\n const next = typeof updater === \"function\" ? updater(old) : updater\n if (clearFiltersOnHide) {\n const newlyHidden = Object.keys(next).filter(\n (id) => next[id] === false && old[id] !== false\n )\n if (newlyHidden.length > 0) {\n setColumnFilters((filters) =>\n filters.filter((filter) => !newlyHidden.includes(filter.id))\n )\n }\n }\n return next\n })\n },\n onColumnOrderChange: setColumnOrder,\n onColumnPinningChange: setColumnPinning,\n onColumnSizingChange: setColumnSizing,\n onRowSelectionChange: setRowSelection,\n onColumnFiltersChange: setColumnFilters,\n onExpandedChange: setExpanded,\n onGlobalFilterChange: setGlobalFilter,\n enableRowSelection: effectiveEnableRowSelection,\n getRowCanExpand: renderExpandedRow ? () => true : undefined,\n getCoreRowModel: getCoreRowModel(),\n getSortedRowModel: getSortedRowModel(),\n globalFilterFn:\n searchMatch === \"startsWith\"\n ? (startsWithWordFilterFn as FilterFn<TData>)\n : \"auto\",\n getFilteredRowModel: getFilteredRowModel(),\n // Infinite scroll (`onLoadMore`) shows every loaded row instead of a\n // single page — no pagination row model, no footer.\n getPaginationRowModel: onLoadMore ? undefined : getPaginationRowModel(),\n getFacetedRowModel:\n needsFacetedFiltering || needsFacetedMinMax\n ? getFacetedRowModel()\n : undefined,\n getFacetedUniqueValues: needsFacetedFiltering\n ? getFacetedUniqueValues()\n : undefined,\n getFacetedMinMaxValues: needsFacetedMinMax\n ? getFacetedMinMaxValues()\n : undefined,\n getExpandedRowModel: getExpandedRowModel(),\n // `pageIndex` lives in controlled state (seeded from `defaultPageIndex`)\n // rather than `initialState`: TanStack's auto-reset on sort/filter/data\n // changes returns to `initialState.pagination.pageIndex`, which would\n // pin every reset to the restored page instead of page 1.\n initialState: { pagination: { pageSize, pageIndex: 0 } },\n onPaginationChange: setPaginationState,\n autoResetPageIndex: pageRestorePending ? false : undefined,\n enableColumnResizing,\n columnResizeMode: \"onChange\",\n enableMultiSort,\n meta: {\n getColumnCellElements: (columnId) => {\n const root = tableContainerRef.current\n if (!root) return []\n return Array.from(\n root.querySelectorAll<HTMLElement>(`[data-column-id=\"${columnId}\"]`)\n )\n },\n },\n })\n\n // `useReactTable` returns the same instance across renders (it mutates in\n // place internally rather than recreating), so this only actually\n // re-invokes when `tableRef` itself changes identity, not on every\n // render.\n React.useEffect(() => {\n if (!tableRef) return\n tableRef(table)\n return () => tableRef(null)\n }, [tableRef, table])\n\n // An out-of-range page (an oversized `defaultPageIndex`, or rows removed\n // out from under the current page) clamps to the last real page rather\n // than rendering empty with an inverted range announcement.\n const clampPageCount = onLoadMore ? 0 : table.getPageCount()\n const clampPageIndex = paginationState.pageIndex\n React.useEffect(() => {\n if (clampPageCount > 0 && clampPageIndex >= clampPageCount) {\n setPaginationState((prev) => ({ ...prev, pageIndex: clampPageCount - 1 }))\n }\n }, [clampPageCount, clampPageIndex])\n\n // `pageSize` only seeds `initialState` above, so a prop change after mount\n // is otherwise silently ignored — this mirrors it into the live table\n // state whenever it actually changes, without touching the pageIndex a\n // user may have already paged to.\n const previousPageSizeRef = React.useRef(pageSize)\n React.useEffect(() => {\n if (previousPageSizeRef.current !== pageSize) {\n previousPageSizeRef.current = pageSize\n table.setPageSize(pageSize)\n }\n }, [pageSize, table])\n\n // Resizing fires its own `onColumnSizingChange` on every drag pixel — that's\n // too noisy to hand a consumer wiring this to `localStorage` on every\n // frame, so this only actually calls `onColumnLayoutChange` on the\n // transition from \"a column is resizing\" to \"no column is resizing\",\n // i.e. drag-end. The Columns menu's own Apply/Reset call it directly\n // instead (see their own call sites) since those are already discrete,\n // already-settled actions with no live-drag equivalent to debounce past.\n const isResizingColumn = table.getState().columnSizingInfo.isResizingColumn\n const previousIsResizingColumnRef = React.useRef(isResizingColumn)\n React.useEffect(() => {\n if (previousIsResizingColumnRef.current && !isResizingColumn) {\n onColumnLayoutChange?.({\n visibility: columnVisibility,\n order: columnOrder,\n pinning: columnPinning,\n sizing: columnSizing,\n })\n }\n previousIsResizingColumnRef.current = isResizingColumn\n }, [\n isResizingColumn,\n columnVisibility,\n columnOrder,\n columnPinning,\n columnSizing,\n onColumnLayoutChange,\n ])\n\n const pagination = table.getState().pagination\n React.useEffect(() => {\n onStateChange?.({\n sorting,\n columnFilters,\n globalFilter,\n pagination,\n expanded,\n pinnedFilterIds,\n })\n // eslint-disable-next-line react-hooks/exhaustive-deps -- fire when the state itself changes, not when the callback identity changes\n }, [\n sorting,\n columnFilters,\n globalFilter,\n pagination,\n expanded,\n pinnedFilterIds,\n ])\n\n const lockedIds = React.useMemo(\n () => new Set([...INJECTED_COLUMN_IDS, ...lockedColumnIds]),\n [lockedColumnIds]\n )\n\n const [draggedColumnId, setDraggedColumnId] = React.useState<string | null>(\n null\n )\n const [dragOverColumnId, setDragOverColumnId] = React.useState<string | null>(\n null\n )\n\n // Shared ordering base for both `manageableColumns` and `filterableColumns`\n // below — every real (non-injected) leaf column, in `columnOrder` once the\n // user has reordered anything, definition order otherwise.\n const orderedRealColumns = React.useMemo(() => {\n const leaf = table\n .getAllLeafColumns()\n .filter((column) => !INJECTED_COLUMN_IDS.has(column.id))\n if (!columnOrder.length) return leaf\n const rank = new Map(columnOrder.map((id, index) => [id, index]))\n return [...leaf].sort(\n (a, b) => (rank.get(a.id) ?? 0) - (rank.get(b.id) ?? 0)\n )\n }, [table, columnOrder])\n\n const manageableColumns = React.useMemo(\n () =>\n orderedRealColumns.filter((column) => !column.columnDef.meta?.filterOnly),\n [orderedRealColumns]\n )\n\n function columnSection(columnId: string): \"left\" | \"center\" | \"right\" {\n if (columnPinning.left?.includes(columnId)) return \"left\"\n if (columnPinning.right?.includes(columnId)) return \"right\"\n return \"center\"\n }\n\n // The Columns-menu popover groups rows into up to three sections —\n // pinned left, unpinned/center, pinned right — mirroring the exact\n // grouping `getHeaderGroups()` already renders the real header/body\n // cells in (see the `<colgroup>` comment above). Center keeps\n // `columnOrder`'s own relative order; each pinned side keeps its\n // `columnPinning` array's own order — the order those columns were\n // pinned in, which is also what positions them on screen.\n const manageableColumnsBySection = React.useMemo(() => {\n const byId = new Map(manageableColumns.map((column) => [column.id, column]))\n const toColumns = (ids: string[] | undefined) =>\n (ids ?? [])\n .map((id) => byId.get(id))\n .filter(\n (column): column is (typeof manageableColumns)[number] =>\n column !== undefined\n )\n const left = toColumns(columnPinning.left)\n const right = toColumns(columnPinning.right)\n const pinnedIds = new Set([\n ...(columnPinning.left ?? []),\n ...(columnPinning.right ?? []),\n ])\n const center = manageableColumns.filter(\n (column) => !pinnedIds.has(column.id)\n )\n return { left, center, right }\n }, [manageableColumns, columnPinning])\n\n // Live preview of where a drag would land — computed but not committed\n // until drop, so the rows can animate into place as you drag without\n // touching real state on every dragover. Never crosses a section\n // boundary: dragging a pinned column onto the center group (or vice\n // versa) is what the dedicated pin buttons are for instead — a plain\n // reorder confined to one section is a much narrower, more predictable\n // gesture. Shared by the live preview below (keyed off the debounced\n // dragOverColumnId) and onDrop (keyed off the actual row the browser\n // just fired a drop event on) — drop must never trust the debounced\n // state, since releasing the pointer within that debounce window would\n // otherwise commit whatever the *previous* hover target was, not the\n // one actually dropped on.\n function computeReorderedIds(draggedId: string, targetId: string) {\n if (!draggedId || !targetId || draggedId === targetId) return null\n if (lockedIds.has(draggedId) || lockedIds.has(targetId)) return null\n const section = columnSection(draggedId)\n if (section !== columnSection(targetId)) return null\n const order = manageableColumnsBySection[section].map((column) => column.id)\n const fromIndex = order.indexOf(draggedId)\n const toIndex = order.indexOf(targetId)\n if (fromIndex === -1 || toIndex === -1) return null\n const next = [...order]\n next.splice(fromIndex, 1)\n next.splice(toIndex, 0, draggedId)\n return { section, order: next }\n }\n\n // Which edge of the current drop target to draw the insertion line\n // against — dragging forward in the list (`fromIndex < toIndex`) ends\n // with the dragged column occupying the target's old slot and the\n // target shifting back by one, which reads most clearly as \"landing\n // just after\" the target; dragging backward reads as \"landing just\n // before\" it. Shared by the header cells (a vertical line, left/right)\n // and the Columns-menu popover rows (a horizontal line, top/bottom) —\n // same underlying reorder, two different axes to draw it on.\n function dropInsertSide(\n draggedId: string,\n targetId: string\n ): \"before\" | \"after\" | null {\n if (!draggedId || !targetId || draggedId === targetId) return null\n if (lockedIds.has(draggedId) || lockedIds.has(targetId)) return null\n const section = columnSection(draggedId)\n if (section !== columnSection(targetId)) return null\n const order = manageableColumnsBySection[section].map((column) => column.id)\n const fromIndex = order.indexOf(draggedId)\n const toIndex = order.indexOf(targetId)\n if (fromIndex === -1 || toIndex === -1) return null\n return fromIndex < toIndex ? \"after\" : \"before\"\n }\n\n const previewReorder = React.useMemo(() => {\n if (!draggedColumnId || !dragOverColumnId) return null\n return computeReorderedIds(draggedColumnId, dragOverColumnId)\n // eslint-disable-next-line react-hooks/exhaustive-deps -- computeReorderedIds closes over manageableColumnsBySection/lockedIds, both already listed\n }, [draggedColumnId, dragOverColumnId, manageableColumnsBySection, lockedIds])\n\n function displayedSection(section: \"left\" | \"center\" | \"right\") {\n const base = manageableColumnsBySection[section]\n if (!previewReorder || previewReorder.section !== section) return base\n const byId = new Map(base.map((column) => [column.id, column]))\n return previewReorder.order\n .map((id) => byId.get(id))\n .filter((column): column is (typeof base)[number] => column !== undefined)\n }\n\n const displayedLeft = displayedSection(\"left\")\n const displayedCenter = displayedSection(\"center\")\n const displayedRight = displayedSection(\"right\")\n // Flat concat, in render order — only used to key the FLIP-animation\n // effect below (it measures actual DOM position, so it doesn't care\n // about sections itself, just whether *some* row's Y position moved).\n const displayedColumns = [\n ...displayedLeft,\n ...displayedCenter,\n ...displayedRight,\n ]\n\n // The baseline \"Reset to defaults\" restores and `hasColumnCustomization`\n // compares against — the seeded `default*` prop when the consumer gave\n // one, else the same fallback each state initializer above uses (derived\n // from `meta.pin` for pinning, otherwise \"nothing set\"). This is a dev\n // default, not the user's own persisted layout — a consumer wiring up\n // `onColumnLayoutChange` to `localStorage` still sees \"Reset to defaults\"\n // reset to *this*, not to what was persisted last session.\n const derivedDefaultColumnPinning = React.useMemo(\n () => derivePinningFromMeta(userColumns),\n [userColumns]\n )\n const effectiveDefaultColumnPinning =\n defaultColumnPinning ?? derivedDefaultColumnPinning\n const effectiveDefaultColumnVisibility = defaultColumnVisibility ?? {}\n const effectiveDefaultColumnOrder = defaultColumnOrder ?? []\n const effectiveDefaultColumnSizing = defaultColumnSizing ?? {}\n const hasColumnCustomization =\n !idListsEqual(columnOrder, effectiveDefaultColumnOrder) ||\n !visibilityEqual(columnVisibility, effectiveDefaultColumnVisibility) ||\n !idListsEqual(columnPinning.left, effectiveDefaultColumnPinning.left) ||\n !idListsEqual(columnPinning.right, effectiveDefaultColumnPinning.right) ||\n !sizingEqual(columnSizing, effectiveDefaultColumnSizing)\n\n // Whether anything's actually changed since the menu opened — distinct\n // from `hasColumnCustomization` (which compares against the *default*,\n // for \"Reset to defaults\"). Apply only makes sense, and only enables,\n // once there's something in this session worth committing.\n const columnsMenuSnapshot = columnsMenuSnapshotRef.current\n const hasPendingColumnsMenuChanges = columnsMenuSnapshot\n ? !visibilityEqual(columnVisibility, columnsMenuSnapshot.visibility) ||\n !idListsEqual(columnOrder, columnsMenuSnapshot.order) ||\n !idListsEqual(columnPinning.left, columnsMenuSnapshot.pinning.left) ||\n !idListsEqual(columnPinning.right, columnsMenuSnapshot.pinning.right)\n : false\n\n // `centerOrder` only ever lists the unpinned \"manageable\" columns (the\n // drag list excludes injected ones) — @tanstack/react-table appends\n // anything missing from `columnOrder` at the very end, so committing it\n // alone would silently drag the selection/expand/AI columns from their\n // fixed leading position to the end of the table on every reorder.\n // Re-prepending them here keeps that position fixed no matter what the\n // drag list itself contains. Pinned columns' own position within\n // `columnOrder` doesn't affect rendering at all (that's driven by\n // `columnPinning` — see `columnSection` above), so appending them after\n // is harmless; they're only here so nothing just disappears from state.\n function commitColumnOrder(centerOrder: string[]) {\n const leadingIds = table\n .getAllLeafColumns()\n .filter((column) => INJECTED_COLUMN_IDS.has(column.id))\n .map((column) => column.id)\n const pinnedIds = [\n ...(columnPinning.left ?? []),\n ...(columnPinning.right ?? []),\n ]\n setColumnOrder([...leadingIds, ...centerOrder, ...pinnedIds])\n }\n\n function commitPinOrder(section: \"left\" | \"right\", order: string[]) {\n setColumnPinning((old) => ({ ...old, [section]: order }))\n }\n\n function commitSectionOrder(\n section: \"left\" | \"center\" | \"right\",\n order: string[]\n ) {\n if (section === \"center\") commitColumnOrder(order)\n else commitPinOrder(section, order)\n }\n\n // Shared by the row's Move left/right buttons (still \"up\"/\"down\"\n // internally — that's this list's own vertical order, which is what\n // actually gets spliced; the buttons just *display* it as left/right,\n // since that's what the move does to the real table): where `columnId`\n // would land\n // moving one step in `direction`, within its own section (see\n // `computeReorderedIds`) and skipping over locked neighbors rather than\n // swapping with one — `null` when there's nowhere valid to go (already\n // at that end of its section, or every remaining neighbor is locked).\n function findMoveTargetIndex(\n columnId: string,\n direction: \"up\" | \"down\"\n ): number | null {\n const section = columnSection(columnId)\n const order = manageableColumnsBySection[section].map((column) => column.id)\n const index = order.indexOf(columnId)\n if (index === -1) return null\n let targetIndex = direction === \"up\" ? index - 1 : index + 1\n while (\n targetIndex >= 0 &&\n targetIndex < order.length &&\n lockedIds.has(order[targetIndex])\n ) {\n targetIndex += direction === \"up\" ? -1 : 1\n }\n return targetIndex >= 0 && targetIndex < order.length ? targetIndex : null\n }\n\n function canMoveColumn(columnId: string, direction: \"up\" | \"down\") {\n return findMoveTargetIndex(columnId, direction) !== null\n }\n\n function moveColumn(columnId: string, direction: \"up\" | \"down\") {\n const section = columnSection(columnId)\n const order = manageableColumnsBySection[section].map((column) => column.id)\n const index = order.indexOf(columnId)\n const targetIndex = findMoveTargetIndex(columnId, direction)\n if (index === -1 || targetIndex === null) return\n const next = [...order]\n next.splice(index, 1)\n next.splice(targetIndex, 0, columnId)\n commitSectionOrder(section, next)\n // Overall position across every section, not just this one — \"up\"/\n // \"down\" is `moveColumn`'s own vocabulary (see the buttons that call\n // it), but the announcement uses the same left/right words their own\n // labels/tooltips do.\n const sectionsBefore =\n section === \"left\"\n ? []\n : section === \"center\"\n ? manageableColumnsBySection.left\n : [\n ...manageableColumnsBySection.left,\n ...manageableColumnsBySection.center,\n ]\n const newPosition = sectionsBefore.length + targetIndex + 1\n const column = manageableColumnsBySection[section].find(\n (c) => c.id === columnId\n )\n if (column) {\n setLiveAnnouncement(\n `${columnLabel(column)} moved ${direction === \"up\" ? \"left\" : \"right\"}, now column ${newPosition} of ${manageableColumns.length}`\n )\n }\n }\n\n function resetColumns() {\n setColumnOrder(effectiveDefaultColumnOrder)\n setColumnVisibility(effectiveDefaultColumnVisibility)\n setColumnPinning(effectiveDefaultColumnPinning)\n setColumnSizing(effectiveDefaultColumnSizing)\n onColumnLayoutChange?.({\n visibility: effectiveDefaultColumnVisibility,\n order: effectiveDefaultColumnOrder,\n pinning: effectiveDefaultColumnPinning,\n sizing: effectiveDefaultColumnSizing,\n })\n }\n\n function handleColumnsMenuOpenChange(open: boolean) {\n if (open) {\n columnsMenuSnapshotRef.current = {\n visibility: columnVisibility,\n order: columnOrder,\n pinning: columnPinning,\n }\n columnsMenuAppliedRef.current = false\n } else {\n if (!columnsMenuAppliedRef.current && columnsMenuSnapshotRef.current) {\n setColumnVisibility(columnsMenuSnapshotRef.current.visibility)\n setColumnOrder(columnsMenuSnapshotRef.current.order)\n setColumnPinning(columnsMenuSnapshotRef.current.pinning)\n }\n setColumnSearchQuery(\"\")\n }\n setColumnsMenuOpen(open)\n }\n\n // The pane's own box already matches the main column's height via flex\n // stretch — this only closes the gap left by ancestor content the pane's\n // DOM subtree doesn't include (e.g. a Card's header/footer above/below\n // DataTable itself). Re-measures on open only; a live ResizeObserver\n // wasn't worth the complexity for a surface that isn't resizing while a\n // filters pane is open.\n React.useLayoutEffect(() => {\n if (!filtersPaneOpen) {\n setFiltersPaneEdgeAdjust(null)\n return\n }\n const pane = filtersPaneRef.current\n const surface = pane?.closest('[data-slot=\"card\"]')\n if (!pane || !surface) {\n setFiltersPaneEdgeAdjust(null)\n return\n }\n const paneRect = pane.getBoundingClientRect()\n const surfaceRect = surface.getBoundingClientRect()\n const topDelta = Math.max(0, paneRect.top - surfaceRect.top)\n const bottomDelta = Math.max(0, surfaceRect.bottom - paneRect.bottom)\n if (topDelta === 0 && bottomDelta === 0) {\n setFiltersPaneEdgeAdjust(null)\n return\n }\n setFiltersPaneEdgeAdjust({\n marginTop: -topDelta,\n height: paneRect.height + topDelta + bottomDelta,\n })\n }, [filtersPaneOpen])\n\n // FLIP animation: measure each row before the order changes, then transform\n // from its old position back to the new one so the list visibly slides\n // instead of jumping. Ref callbacks are cached per column id (not recreated\n // inline in JSX) so React doesn't detach/reattach every row on every\n // render — that churn was making `columnRowRefs` unreliable mid-drag.\n const columnRowRefs = React.useRef(new Map<string, HTMLDivElement>())\n const columnRowRefCallbacks = React.useRef(\n new Map<string, (element: HTMLDivElement | null) => void>()\n )\n const columnRowRectsRef = React.useRef(new Map<string, DOMRect>())\n const pendingFrameRef = React.useRef<number | null>(null)\n // Native `dragover` fires on every pointer move — far faster than a\n // 180ms slide can visually settle during a fast real drag across many\n // rows. Without this, every single row crossed restarts the animation\n // immediately, which is what read as the whole list moving erratically\n // rather than sliding. Debouncing the *preview* update (not the required\n // per-event preventDefault(), which still runs on every dragover) means\n // the list only reorders once the pointer holds still on a row for a\n // moment, instead of chasing every row a fast sweep passes over.\n const dragOverDebounceRef = React.useRef<ReturnType<\n typeof setTimeout\n > | null>(null)\n const displayedOrderKey = displayedColumns\n .map((column) => column.id)\n .join(\",\")\n\n function getColumnRowRef(id: string) {\n let callback = columnRowRefCallbacks.current.get(id)\n if (!callback) {\n callback = (element) => {\n if (element) columnRowRefs.current.set(id, element)\n else columnRowRefs.current.delete(id)\n }\n columnRowRefCallbacks.current.set(id, callback)\n }\n return callback\n }\n\n React.useLayoutEffect(() => {\n const previousRects = columnRowRectsRef.current\n\n if (pendingFrameRef.current !== null) {\n cancelAnimationFrame(pendingFrameRef.current)\n pendingFrameRef.current = null\n }\n\n // Snap every row back to its untransformed layout position before\n // measuring. Dragging quickly across several rows fires this effect\n // faster than the previous run's 180ms transition finishes, and\n // `getBoundingClientRect()` reports the CURRENT painted position —\n // mid-transition, that's a moving target, not the row's real layout\n // slot. Measuring it anyway fed the next delta a wrong \"previous\"\n // position, which is what read as other rows jittering erratically\n // while dragging. Both writes land in the same synchronous\n // useLayoutEffect pass (before paint), so this doesn't itself flash —\n // it just replaces \"wherever mid-transition\" with \"the new FLIP\n // starting point\" in one uninterrupted frame.\n columnRowRefs.current.forEach((element) => {\n element.style.transition = \"none\"\n element.style.transform = \"\"\n })\n\n const nextRects = new Map<string, DOMRect>()\n columnRowRefs.current.forEach((element, id) => {\n nextRects.set(id, element.getBoundingClientRect())\n })\n\n const toAnimate: HTMLDivElement[] = []\n columnRowRefs.current.forEach((element, id) => {\n const previous = previousRects.get(id)\n const next = nextRects.get(id)\n if (!previous || !next) return\n const deltaY = previous.top - next.top\n if (Math.abs(deltaY) < 0.5) return\n element.style.transform = `translateY(${deltaY}px)`\n toAnimate.push(element)\n })\n\n if (toAnimate.length > 0) {\n // Force a layout flush so the \"jump\" above registers as a real paint\n // state before the transition-back below runs — otherwise both style\n // writes can coalesce into a single paint and no slide is visible.\n toAnimate.forEach((element) => void element.getBoundingClientRect())\n pendingFrameRef.current = requestAnimationFrame(() => {\n toAnimate.forEach((element) => {\n element.style.transition = \"transform 180ms ease\"\n element.style.transform = \"\"\n })\n pendingFrameRef.current = null\n })\n }\n\n columnRowRectsRef.current = nextRects\n }, [displayedOrderKey])\n\n const {\n filterableColumns,\n showAllFiltersInline,\n inlineFilterColumns,\n paneFilterCount,\n } = React.useMemo(() => {\n // Every id any *visible* column's `meta.filterMergeWith` references —\n // excluded below so a merged column never *also* shows up as its own\n // separate toolbar/pane entry on top of appearing inside its host's\n // popover. Gated on the host's own visibility: a hidden host renders\n // no header cell at all, so it has no popover for a merged child to\n // live inside — excluding the child there too would make its filter\n // completely unreachable rather than just relocated, so it falls back\n // to surfacing as its own ordinary pane entry instead.\n const mergedAwayFilterIds = new Set(\n userColumns.flatMap((col) => {\n const hostId = getColumnId(col)\n const hostVisible =\n !hostId || effectiveColumnVisibility[hostId] !== false\n return hostVisible ? (col.meta?.filterMergeWith ?? []) : []\n })\n )\n\n // Derived from `orderedRealColumns`, not `manageableColumns` — a\n // `meta.filterOnly` column is deliberately excluded from `manageableColumns`\n // (so it never appears in, or can be re-shown via, the Columns menu) but\n // still needs to surface here, since filtering it is the whole point of\n // defining it.\n const filterableColumnsBase = orderedRealColumns.filter(\n (column) => column.getCanFilter() && !mergedAwayFilterIds.has(column.id)\n )\n // Filter *display* order is independent of column order: a column's\n // `meta.filterOrder` (if set) repositions it, while every column that\n // omits it defaults to its own index in `filterableColumnsBase` — so it\n // stays exactly where column order already put it. Array.prototype.sort\n // is stable in every engine/runtime this project targets, so ties (two\n // defaulted columns, or two explicit equal `filterOrder`s) keep their\n // relative order instead of shuffling on every render.\n const withFilterOrderPositions = filterableColumnsBase.map(\n (column, index) => ({\n column,\n order: column.columnDef.meta?.filterOrder ?? index,\n })\n )\n withFilterOrderPositions.sort((a, b) => a.order - b.order)\n const filterableColumns = withFilterOrderPositions.map(\n (entry) => entry.column\n )\n // A `filterOnly` column is never pinnable/inline — no toolbar chip has\n // anywhere to point back at, since there's no real header cell for it.\n // The pane is its only access point, so once one exists the pane always\n // renders regardless of the total filterable count — \"3 or fewer, show\n // everything inline\" only holds when every one of them actually *can*\n // go inline.\n const hasFilterOnlyColumn = filterableColumns.some(\n (column) => !!column.columnDef.meta?.filterOnly\n )\n const showAllFiltersInline =\n !hasFilterOnlyColumn && filterableColumns.length <= 3\n const inlineFilterColumns = showAllFiltersInline\n ? filterableColumns\n : filterableColumns.filter(\n (column) =>\n !column.columnDef.meta?.filterOnly &&\n pinnedFilterIds.includes(column.id)\n )\n const paneFilterCount = showAllFiltersInline\n ? 0\n : filterableColumns.length - inlineFilterColumns.length\n\n return {\n filterableColumns,\n hasFilterOnlyColumn,\n showAllFiltersInline,\n inlineFilterColumns,\n paneFilterCount,\n }\n }, [\n orderedRealColumns,\n userColumns,\n pinnedFilterIds,\n effectiveColumnVisibility,\n ])\n\n // `getSelectedRowModel()` returns every checked row regardless of the\n // active filter — a row checked before a filter narrowed the table stays\n // in there even though it's no longer on screen. `getFilteredSelectedRowModel()`\n // intersects that with `getFilteredRowModel()`, so a filter-hidden\n // selection drops out of what bulk actions/the count see without losing\n // the underlying `rowSelection` state (clearing the filter shows it\n // checked again). `selectionScope=\"all\"` opts back into the unfiltered\n // set for a consumer that deliberately selects across separate filter\n // passes and wants a bulk action to reach all of them at once.\n const allSelectedRows = table.getSelectedRowModel().rows\n const filteredSelectedRows = table.getFilteredSelectedRowModel().rows\n const selectedRows =\n selectionScope === \"all\" ? allSelectedRows : filteredSelectedRows\n const hiddenByFilterCount =\n selectionScope === \"all\"\n ? 0\n : allSelectedRows.length - filteredSelectedRows.length\n const hasSelection = selectedRows.length > 0\n // Already filtered-scoped — see the TanStack source, it checks against\n // `getFilteredRowModel()`, not every row ever loaded — so this is exactly\n // \"every row the user could currently select is selected,\" regardless of\n // `selectionScope`.\n //\n // Computed against the consumer's own `enableRowSelection`, not the\n // cap-aware one: once `maxSelectedRows` is hit every unchecked row reports\n // `getCanSelect() === false`, which makes TanStack's `getIsAllRowsSelected`\n // vacuously true even though most rows are unchecked.\n const isRowSelectableIgnoringCap = (row: Row<TData>) =>\n typeof enableRowSelection === \"function\"\n ? enableRowSelection(row)\n : !!enableRowSelection\n const selectableFilteredRows = table\n .getFilteredRowModel()\n .rows.filter(isRowSelectableIgnoringCap)\n const isAllFilteredSelected =\n selectableFilteredRows.length > 0 &&\n selectableFilteredRows.every((row) => !!rowSelection[row.id])\n // Adds the selectable filtered rows to the existing selection (checked\n // rows hidden by the filter are kept) and stops at `maxSelectedRows`.\n const selectAllFiltered = () => {\n const next: RowSelectionState = { ...rowSelection }\n let remaining =\n maxSelectedRows != null\n ? Math.max(0, maxSelectedRows - Object.keys(next).length)\n : Infinity\n for (const row of selectableFilteredRows) {\n if (next[row.id]) continue\n if (remaining <= 0) break\n next[row.id] = true\n remaining -= 1\n }\n setRowSelection(next)\n }\n // Infinite scroll has no real footer to swap search/filters into (see the\n // toolbar's own comment below), so it's always the floating bar regardless\n // of `selectionBarPlacement` — the prop only has an actual choice to make\n // in paginated mode.\n const effectiveSelectionBarPlacement = onLoadMore\n ? \"floating\"\n : (selectionBarPlacement ?? \"toolbar\")\n // `BulkActionsBar` only ever mounts under this exact condition (see its\n // render site below) — keying the effect's own deps on it, instead of\n // running with no dep array at all, is what stops a `ResizeObserver`\n // from being torn down and recreated on every single render (including\n // every keystroke and hover-state flip that has nothing to do with the\n // bar). It still needs to watch `bulkActionsBarRef.current`'s *size*\n // after that — hence the observer at all, not just a static measurement\n // — but doesn't need to re-attach unless the element itself is about to\n // mount or unmount.\n const bulkActionsBarMounted =\n hasSelection && effectiveSelectionBarPlacement === \"floating\"\n React.useLayoutEffect(() => {\n const el = bulkActionsBarRef.current\n if (!el) {\n setBulkActionsBarHeight(0)\n return\n }\n const observer = new ResizeObserver(([entry]) =>\n setBulkActionsBarHeight(entry.contentRect.height)\n )\n observer.observe(el)\n return () => observer.disconnect()\n }, [bulkActionsBarMounted])\n\n // Whether the scroll container actually has anything to scroll\n // *horizontally* right now — a resized/narrow viewport, a lot of\n // columns, or none of the above once columns are hidden/resized back\n // down. Only true then does the container itself need to be reachable\n // by keyboard at all (`tabIndex`, below) — a table that already fits\n // has nothing extra to expose. Observes the `<table>` element itself,\n // not just the container: the container's own box never changes size\n // (it's what's fixed, everything scrolls *inside* it), only the\n // table's rendered width does, as columns resize/show/hide.\n const [canScrollHorizontally, setCanScrollHorizontally] =\n React.useState(false)\n React.useLayoutEffect(() => {\n const container = tableContainerRef.current\n const tableEl = container?.querySelector(\"table\")\n if (!container || !tableEl) return\n function measure() {\n if (!container) return\n setCanScrollHorizontally(container.scrollWidth > container.clientWidth)\n }\n measure()\n const observer = new ResizeObserver(measure)\n observer.observe(tableEl)\n observer.observe(container)\n return () => observer.disconnect()\n }, [])\n\n // A row with onRowClick gets a real tabIndex/keydown handler, but keeps\n // its native table role=\"row\" — AT announces \"row\" with no signal that\n // Enter/Space does anything, unlike the cursor-pointer cue sighted mouse\n // users get. This shared, visually-hidden hint is what aria-describedby\n // points every clickable row at instead.\n const rowClickHintId = React.useId()\n\n const selectedIdsKey = selectedRows.map((row) => row.id).join(\"\\u0000\")\n React.useEffect(() => {\n onRowSelectionChange?.(\n selectedRows.map((row) => row.original),\n {\n allSelectedRows: allSelectedRows.map((row) => row.original),\n hiddenByFilterCount,\n }\n )\n // `selectedIdsKey` re-fires it when `data` changes underneath the same\n // selection (a deleted/refetched row) — the ids it resolves to change.\n // eslint-disable-next-line react-hooks/exhaustive-deps -- fire when the selection or what it's scoped against changes, not when the callback identity changes\n }, [\n rowSelection,\n columnFilters,\n globalFilter,\n selectionScope,\n selectedIdsKey,\n ])\n const rows = table.getRowModel().rows\n // The real, currently-visible leaf columns — not the raw `columns` array,\n // which still includes `meta.filterOnly` columns (permanently force-hidden,\n // see `effectiveColumnVisibility` above) and doesn't shrink when the user\n // hides a column via the Columns menu. Loading/error/empty/expanded rows\n // all need to match whatever the real header/colgroup actually rendered,\n // or their `<td>` count silently disagrees with it.\n const visibleLeafColumns = table.getVisibleLeafColumns()\n\n // Roving tabindex across resize handles — visible order, resizable\n // columns only. Without this, a wide table with a dozen resizable\n // columns puts a dozen separate tab stops between the header and the\n // first row; only the \"active\" one (last focused, defaulting to the\n // first) is a real tab stop, and Up/Down moves between them instead\n // (Left/Right is already resize's own step, so it can't double as\n // \"move focus\" too).\n const resizableColumnIds = visibleLeafColumns\n .filter((column) => column.getCanResize())\n .map((column) => column.id)\n const [activeResizeColumnId, setActiveResizeColumnId] = React.useState<\n string | null\n >(null)\n const resizeHandleRefs = React.useRef(new Map<string, HTMLDivElement>())\n\n // No `rows.length > 0` clause — an empty *filtered* result (e.g. a search\n // that matches nothing) still keeps the footer around, both because the\n // page-size selector shouldn't disappear just because this particular\n // filter narrowed things to zero, and because the range text below\n // already renders \"0 items\" instead of a bogus \"1–10\" for that case.\n const showFooter = !loading && !error && !onLoadMore\n\n // One header row's fixed height in px, by `size` — 40/36/32, the same\n // literal values `TableHead`'s own h-10/h-9/h-8 use (see table.tsx). A\n // grouped header has more than one row stacked in the sticky header, and\n // each row past the first needs its own `top` offset equal to every\n // previous row's height summed — computed here rather than measured,\n // since these three heights are fixed per `size`, not data-dependent.\n const headerRowHeightPx = size === \"xs\" ? 32 : size === \"sm\" ? 36 : 40\n\n // `table.getHeaderGroups()` gives one row per header depth — depth 1\n // (a real author-defined group's leaf children) forces every *other*\n // top-level column to also get an entry at that shallower depth, even\n // when it was never grouped. TanStack reuses the exact same Column for\n // that entry (`header.column.columnDef.columns` stays `undefined`,\n // unlike a real group's synthetic parent column), so without special\n // handling it would render as a plain unstyled duplicate of its own\n // leaf header one row early, then again for real one row down.\n // `passthroughLeafIds` collects which columns those are — the render\n // below `rowSpan`s that column's row-0 entry down through every header\n // row instead, and skips its later, now-redundant entries entirely.\n const headerGroupsForRender = table.getHeaderGroups()\n const passthroughLeafIds = new Set(\n headerGroupsForRender[0]?.headers\n .filter(\n (header) =>\n header.subHeaders.length > 0 &&\n // `columns` only exists on TanStack's `GroupColumnDef` variant of\n // the `ColumnDef` union — the cast is just narrowing to check for\n // it, not asserting the shape is actually that variant.\n !Array.isArray(\n (header.column.columnDef as { columns?: unknown }).columns\n )\n )\n .map((header) => header.column.id) ?? []\n )\n\n // Which leaf columns sit at the left/right edge of a real header group's\n // span — a vertical divider there (below, in both the leaf header row and\n // every body cell) is what keeps a grouped/merged table from reading as\n // broken rather than intentional: a `meta.mergeRepeatedValues` column\n // already leaves horizontal row dividers *not* crossing it while its\n // neighbors' do (that's the merge working correctly, but with nothing\n // else marking the column's own edges, it looks like the grid just\n // stopped rendering there); a grouped column's own boundary needs the\n // same treatment for the same reason, one level up. Always on, not\n // gated behind `cellDividers` — that prop is about a uniform grid over\n // *every* column, a much heavier visual than just framing the\n // grouped/merged ones.\n const groupBoundaryColumnIds = React.useMemo(() => {\n const starts = new Set<string>()\n const ends = new Set<string>()\n for (const header of headerGroupsForRender[0]?.headers ?? []) {\n // A flat leaf column (no grouping anywhere in this table at all —\n // by far the common case) has no group to bound in the first\n // place; without this, `getLeafHeaders()` below still returns\n // *something* for it (itself), and every ordinary column in an\n // ungrouped table would wrongly get treated as its own one-column\n // \"group\".\n if (header.subHeaders.length === 0) continue\n if (passthroughLeafIds.has(header.column.id)) continue\n // `getLeafHeaders()` is misleadingly named — its own recursion\n // pushes every header it visits, group headers included, not just\n // true leaves (TanStack's `recurseHeader` appends `h` itself after\n // recursing into `h.subHeaders`) — filtering to `subHeaders.length\n // === 0` is what actually narrows it to real leaf headers.\n const leaves = header\n .getLeafHeaders()\n .filter((leaf) => leaf.subHeaders.length === 0)\n if (leaves.length === 0) continue\n starts.add(leaves[0].column.id)\n ends.add(leaves[leaves.length - 1].column.id)\n }\n return { starts, ends }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- `passthroughLeafIds` is a new Set every render (derived from `headerGroupsForRender`, itself from `table`, already a dep) — including it would defeat the memo every time instead of only when the header structure actually changes.\n }, [headerGroupsForRender])\n\n // Precomputed once per render: for each column with\n // `meta.mergeRepeatedValues`, which rendered rows start a run (get the\n // real `rowSpan`, `> 1` when merged) vs. are covered by a previous row's\n // span (rowSpan 0 — skip rendering that `<td>` entirely, the native HTML\n // rowSpan contract). Based on the rows actually adjacent on screen\n // (post-sort/filter/page, `rows` below), not the raw data array — a run\n // only forms when equal values are genuinely next to each other on screen.\n const mergedCellRowSpans = React.useMemo(() => {\n const spans = new Map<string, Map<string, number>>()\n for (const column of visibleLeafColumns) {\n if (!column.columnDef.meta?.mergeRepeatedValues) continue\n const columnSpans = new Map<string, number>()\n let runStart = 0\n for (let i = 1; i <= rows.length; i++) {\n const continuesRun =\n i < rows.length &&\n rows[i].getValue(column.id) === rows[runStart].getValue(column.id)\n if (continuesRun) continue\n columnSpans.set(rows[runStart].id, i - runStart)\n for (let covered = runStart + 1; covered < i; covered++) {\n columnSpans.set(rows[covered].id, 0)\n }\n runStart = i\n }\n spans.set(column.id, columnSpans)\n }\n return spans\n }, [visibleLeafColumns, rows])\n\n // Precisely how tall the scrollable rows area gets to be: whatever's left\n // in the main column after the toolbar and (if shown) the footer — same\n // intent `flex-1` used to express, but computed directly instead. `flex-1`\n // always claims 100% of the leftover space the instant it *can* grow,\n // regardless of how tall the rows themselves actually are, which is\n // consistent but doesn't leave room to special-case \"rows shorter than\n // that\" cleanly. Measuring lands on the exact same number when a fixed-\n // height surface (e.g. a Card) actually constrains the column — the rows\n // area still fills it and the footer still lands flush at that surface's\n // true bottom edge — and resolves to the rows' own natural height (a\n // no-op) when nothing constrains it, same as `flex-1` did in that case.\n const [rowsAreaHeight, setRowsAreaHeight] = React.useState<number | null>(\n null\n )\n React.useLayoutEffect(() => {\n const mainColumn = mainColumnRef.current\n const container = tableContainerRef.current\n if (!mainColumn || !container) return\n\n function measure() {\n if (!mainColumn || !container) return\n const mainRect = mainColumn.getBoundingClientRect()\n const containerRect = container.getBoundingClientRect()\n const footerEl = footerRef.current\n const footerHeight = footerEl\n ? footerEl.getBoundingClientRect().height\n : 0\n // The footer sits flush under the rows area (no column gap — see the\n // main column's own comment), so the footer's height is the only\n // thing to subtract. Any extra fixed offset here would make this\n // measurement smaller than the rows' natural height when nothing\n // constrains the column, and the ResizeObserver below would then\n // re-shrink it by that offset on every pass until it hit zero.\n const available = mainRect.bottom - containerRect.top - footerHeight\n setRowsAreaHeight(Math.max(available, 0))\n }\n\n measure()\n const observer = new ResizeObserver(measure)\n observer.observe(mainColumn)\n if (footerRef.current) observer.observe(footerRef.current)\n return () => observer.disconnect()\n }, [showFooter])\n\n const pageIndex = table.getState().pagination.pageIndex\n const filteredRowCount = table.getFilteredRowModel().rows.length\n const currentPageSize = table.getState().pagination.pageSize\n const rangeStart =\n filteredRowCount === 0 ? 0 : pageIndex * currentPageSize + 1\n const rangeEnd = Math.min((pageIndex + 1) * currentPageSize, filteredRowCount)\n\n // One shared live region for every \"state settled, tell AT about it\"\n // announcement — selection, result counts, sort, and column move/pin all\n // funnel into this single piece of state instead of each getting its\n // own `role=\"status\"` span. A selection changing on every single\n // checkbox click (or a result count changing on every keystroke of a\n // search box) would otherwise spam AT with every intermediate value\n // during a fast burst — each source below debounces on its own terms\n // before writing here, so only the settled value after a burst actually\n // gets read out.\n const [liveAnnouncement, setLiveAnnouncement] = React.useState(\"\")\n React.useEffect(() => {\n const timeout = setTimeout(() => {\n setLiveAnnouncement(\n !hasSelection\n ? \"\"\n : isAllFilteredSelected\n ? `All ${filteredRowCount} rows selected`\n : `${selectedRows.length} of ${filteredRowCount} rows selected`\n )\n }, 400)\n return () => clearTimeout(timeout)\n }, [\n hasSelection,\n isAllFilteredSelected,\n selectedRows.length,\n filteredRowCount,\n ])\n\n // Result count, after whatever combination of search/filter changes\n // settles — same 500ms-after-settled shape the search input's own\n // debounce already uses, so this doesn't fire mid-keystroke either.\n React.useEffect(() => {\n const timeout = setTimeout(() => {\n setLiveAnnouncement(\n filteredRowCount === 0\n ? \"No results\"\n : `Showing ${rangeStart}–${rangeEnd} of ${filteredRowCount} results`\n )\n }, 500)\n return () => clearTimeout(timeout)\n }, [columnFilters, globalFilter, filteredRowCount, rangeStart, rangeEnd])\n\n // Sort changes are already a single discrete action (one click), not a\n // burst — no debounce needed, just skip the announcement on mount so\n // opening the table doesn't immediately announce whatever `defaultSorting`\n // seeded it with.\n const sortingMountedRef = React.useRef(false)\n React.useEffect(() => {\n if (!sortingMountedRef.current) {\n sortingMountedRef.current = true\n return\n }\n if (sorting.length === 0) {\n setLiveAnnouncement(\"Sort cleared\")\n return\n }\n const labels = sorting.map(\n (sort) =>\n `${columnLabel(table.getColumn(sort.id)!)}, ${sort.desc ? \"descending\" : \"ascending\"}`\n )\n setLiveAnnouncement(`Sorted by ${labels.join(\", then \")}`)\n // eslint-disable-next-line react-hooks/exhaustive-deps -- `table` is stable across renders (see its own comment); re-running this for its identity would defeat the mount guard above\n }, [sorting])\n\n // Pinning changes from *any* trigger — the header dropdown's Pin left/\n // right, the Columns menu's own inline pin buttons, or a fully custom\n // `headerActions` calling `column.pin()` directly — all funnel through\n // this one `columnPinning` state, so diffing it here catches every\n // trigger at once instead of wrapping each `onClick` at every call site\n // that could ever change it.\n const previousColumnPinningRef = React.useRef(columnPinning)\n React.useEffect(() => {\n const previous = previousColumnPinningRef.current\n previousColumnPinningRef.current = columnPinning\n const newlyPinned = (side: \"left\" | \"right\") =>\n (columnPinning[side] ?? []).find(\n (id) => !(previous[side] ?? []).includes(id)\n )\n const newlyUnpinned = [\n ...(previous.left ?? []),\n ...(previous.right ?? []),\n ].find(\n (id) =>\n !(columnPinning.left ?? []).includes(id) &&\n !(columnPinning.right ?? []).includes(id)\n )\n const pinnedLeftId = newlyPinned(\"left\")\n const pinnedRightId = newlyPinned(\"right\")\n const columnId = pinnedLeftId ?? pinnedRightId ?? newlyUnpinned\n if (!columnId) return\n const column = table.getColumn(columnId)\n if (!column) return\n setLiveAnnouncement(\n pinnedLeftId\n ? `${columnLabel(column)} pinned left`\n : pinnedRightId\n ? `${columnLabel(column)} pinned right`\n : `${columnLabel(column)} unpinned`\n )\n // eslint-disable-next-line react-hooks/exhaustive-deps -- `table` is stable across renders (see its own comment)\n }, [columnPinning])\n\n const loadMoreSentinelRef = React.useRef<HTMLTableRowElement>(null)\n\n React.useEffect(() => {\n if (!onLoadMore || !hasMore) return\n const sentinel = loadMoreSentinelRef.current\n if (!sentinel) return\n const observer = new IntersectionObserver(\n (entries) => {\n if (entries[0]?.isIntersecting) onLoadMore()\n },\n { rootMargin: `${loadMoreOffset}px` }\n )\n observer.observe(sentinel)\n return () => observer.disconnect()\n }, [onLoadMore, hasMore, loadMoreOffset])\n\n // Toolbar/footer controls (search, filters, Columns, items-per-page,\n // page-jump, prev/next) render one size notch below the table's own\n // `size` — they're always-visible, repetitive chrome, not content that\n // should compete for the same visual weight as the row data. \"xs\" is\n // already the floor, so it stays put rather than dropping further.\n const controlSize: \"sm\" | \"xs\" = size === \"default\" ? \"sm\" : \"xs\"\n const controlIconSize: \"sm\" | \"xs\" = size === \"default\" ? \"sm\" : \"xs\"\n // Shared by the header cell's filter icon + column menu, and the Columns\n // popover's pin/move controls — one knob (`revealColumnControlsOnHover`)\n // for \"does this consumer want their final UX hover-discovered or\n // always-visible\" instead of deciding it per-control.\n // `(hover: none)` — a touch device has no hover state to reveal these\n // with at all, so a control gated purely on hover/focus-within would be\n // permanently unreachable there (no cursor ever \"arrives\" to trigger\n // it, and focus-within only helps once something inside is already\n // focused, which is circular for a control you need to *find* first).\n // Always visible instead on any such device, same as\n // `revealColumnControlsOnHover={false}` would give every device.\n const hoverRevealClass = revealColumnControlsOnHover\n ? \"opacity-0 transition-opacity group-hover/header-actions:opacity-100 group-focus-within/header-actions:opacity-100 [@media(hover:none)]:opacity-100\"\n : \"\"\n const hoverRevealRowClass = revealColumnControlsOnHover\n ? \"opacity-0 transition-opacity group-hover/column-row:opacity-100 group-focus-within/column-row:opacity-100 [@media(hover:none)]:opacity-100\"\n : \"\"\n // Same knob, for the \"All filters\" pane's own per-row pin/unpin toggle —\n // a separate named group (`filter-row`) since that row isn't the\n // Columns menu's `column-row`.\n const hoverRevealFilterRowClass = revealColumnControlsOnHover\n ? \"opacity-0 transition-opacity group-hover/filter-row:opacity-100 group-focus-within/filter-row:opacity-100 [@media(hover:none)]:opacity-100\"\n : \"\"\n // Which header cell has an open filter Popover or header-actions\n // DropdownMenu right now, if any — `ColumnFilterTrigger`/`RowActions`\n // report their own open/close via `onOpenChange`. Needed because a\n // *sibling* control's visibility can't detect this on its own: opening\n // either is a modal Radix overlay, which sets `pointer-events: none` on\n // the rest of the header cell while open — that breaks `:hover`\n // tracking (CSS can't tell hover from \"pointer happens to be there but\n // hit-testing is disabled\") for every other control in the same cell,\n // not just the one that opened, regardless of where the cursor\n // physically is. `group-focus-within` doesn't cover it either, since\n // focus can stay on the trigger that opened while a sibling's own\n // hover state is what breaks.\n const [openHeaderColumnId, setOpenHeaderColumnId] = React.useState<\n string | null\n >(null)\n\n // One row of the Columns-menu popover — checkbox/label, the same Pin\n // left/Pin right (or \"Unpin\") buttons `columnMenuActions` renders in the\n // header, and a move handle. Drag-and-drop and the arrow-key move both\n // stay confined to this column's own pin section (see\n // `computeReorderedIds`/`moveColumn`) — dragging across sections isn't\n // supported, the pin buttons are the dedicated way to move a column\n // into or out of a pinned section instead.\n function renderColumnMenuRow(column: (typeof manageableColumns)[number]) {\n const locked = lockedIds.has(column.id)\n const canHide = column.getCanHide()\n const label = columnLabel(column)\n const isDragging = draggedColumnId === column.id\n const isDropTarget =\n dragOverColumnId === column.id && draggedColumnId !== null && !isDragging\n const dropSide =\n isDropTarget && draggedColumnId\n ? dropInsertSide(draggedColumnId, column.id)\n : null\n const pins = pinActions(column)\n return (\n <div\n key={column.id}\n ref={getColumnRowRef(column.id)}\n draggable={!locked}\n onDragStart={() => {\n if (locked) return\n setDraggedColumnId(column.id)\n }}\n onDragEnd={() => {\n if (dragOverDebounceRef.current) {\n clearTimeout(dragOverDebounceRef.current)\n dragOverDebounceRef.current = null\n }\n setDraggedColumnId(null)\n setDragOverColumnId(null)\n }}\n onDragEnter={(event) => {\n if (locked || !draggedColumnId) return\n event.preventDefault()\n }}\n onDragOver={(event) => {\n if (locked || !draggedColumnId) return\n // Required on every dragover regardless of the debounce below,\n // or the browser refuses this as a valid drop target at all.\n event.preventDefault()\n if (dragOverColumnId === column.id) return\n if (dragOverDebounceRef.current)\n clearTimeout(dragOverDebounceRef.current)\n dragOverDebounceRef.current = setTimeout(() => {\n setDragOverColumnId(column.id)\n dragOverDebounceRef.current = null\n }, 80)\n }}\n // No onDragLeave: moving onto a child (the checkbox, the label\n // text) fires dragleave on this row even though the pointer is\n // still over it, which cleared dragOverColumnId and made the\n // preview flicker. dragover alone already keeps it correct — it\n // re-fires (bubbling from whichever child is under the pointer)\n // on every move, including onto/off of a different row.\n onDrop={(event) => {\n event.preventDefault()\n if (dragOverDebounceRef.current) {\n clearTimeout(dragOverDebounceRef.current)\n dragOverDebounceRef.current = null\n }\n // column.id here is the row actually under the pointer at drop\n // — not the debounced dragOverColumnId, which can still be\n // pointing at whatever row was hovered before this one if the\n // drop lands inside that 80ms window.\n if (draggedColumnId) {\n const finalReorder = computeReorderedIds(draggedColumnId, column.id)\n if (finalReorder) {\n commitSectionOrder(finalReorder.section, finalReorder.order)\n }\n }\n setDraggedColumnId(null)\n setDragOverColumnId(null)\n }}\n className={cn(\n \"group/column-row relative flex items-center gap-1 rounded-md px-1.5 py-1 hover:bg-accent/50\",\n !locked && \"cursor-grab active:cursor-grabbing\",\n isDragging && \"opacity-40\",\n isDropTarget && \"bg-accent\"\n )}\n >\n {/* Insertion line — see `dropInsertSide`; the horizontal-axis\n counterpart to the same indicator on table header cells. */}\n {dropSide && (\n <div\n aria-hidden=\"true\"\n className={cn(\n \"absolute inset-x-1.5 z-10 h-0.5 bg-primary\",\n dropSide === \"before\" ? \"top-0\" : \"bottom-0\"\n )}\n />\n )}\n {/* A non-hideable column (locked, or enableHiding: false in its\n own ColumnDef) gets the identical checkbox row too — always\n checked, just disabled — instead of a plain label with no\n checkbox at all. Same UI, same \"can't hide this\" story as the\n move button below. */}\n <Label className=\"flex flex-1 items-center gap-2 truncate font-normal\">\n <Checkbox\n checked={canHide ? column.getIsVisible() : true}\n disabled={!canHide}\n onCheckedChange={(value) => column.toggleVisibility(!!value)}\n />\n <span className=\"truncate\">{label}</span>\n </Label>\n {pins.length > 0 && (\n <div\n className={cn(\"flex shrink-0 items-center\", hoverRevealRowClass)}\n >\n {pins.map((action) => (\n <Tooltip key={action.label}>\n <TooltipTrigger asChild>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"xs\"\n iconOnly\n aria-label={action.label}\n onClick={action.onClick}\n >\n {action.icon ? <action.icon className=\"size-3.5\" /> : null}\n </Button>\n </TooltipTrigger>\n <TooltipContent>{action.label}</TooltipContent>\n </Tooltip>\n ))}\n </div>\n )}\n {/* Explicit Move left/right buttons, not a single drag-only\n handle — clicking either commits the move immediately, so a\n mouse user who doesn't want to drag (and a keyboard user, for\n whom a real <Button> is natively Enter/Space-operable with no\n custom keydown handling needed) both have a direct way to\n reorder. Labelled left/right, not up/down, even though this\n list is vertical — \"up\"/\"down\" only describes the popover's\n own incidental layout; what the click actually does is move\n the column left/right in the real table, which is the\n direction that should read from the label. The row itself\n stays draggable regardless — this is an additional path, not\n a replacement for drag-and-drop. */}\n <div className={cn(\"flex shrink-0 items-center\", hoverRevealRowClass)}>\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"xs\"\n iconOnly\n disabled={locked || !canMoveColumn(column.id, \"up\")}\n aria-label={`Move ${label} left`}\n onClick={() => moveColumn(column.id, \"up\")}\n >\n <ArrowLeftIcon className=\"size-3.5\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>Move left</TooltipContent>\n </Tooltip>\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"xs\"\n iconOnly\n disabled={locked || !canMoveColumn(column.id, \"down\")}\n aria-label={`Move ${label} right`}\n onClick={() => moveColumn(column.id, \"down\")}\n >\n <ArrowRightIcon className=\"size-3.5\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>Move right</TooltipContent>\n </Tooltip>\n </div>\n </div>\n )\n }\n\n function commitPageJump(value: string) {\n if (value.trim() === \"\") return\n const page = Number(value)\n if (!Number.isFinite(page)) return\n const clamped = Math.min(\n table.getPageCount(),\n Math.max(1, Math.round(page))\n )\n table.setPageIndex(clamped - 1)\n }\n\n // Past `PAGE_SELECT_THRESHOLD` pages, a `Select` means one `SelectItem`\n // per page rendered into the popover regardless of whether it's ever\n // opened — thousands of DOM nodes for a table that large. A plain number\n // `Input` costs the same one control either way. `key={pageIndex}`\n // remounts it (discarding any uncommitted keystroke) whenever the real\n // page changes from *outside* this input — Previous/Next, a jump from\n // `Pagination` in `\"numbered\"` mode, or a fresh `setPageIndex` call —\n // so it never reads stale once something else moves the page.\n const renderPageSelect = (triggerClassName: string) =>\n table.getPageCount() > PAGE_SELECT_THRESHOLD ? (\n <Input\n key={pageIndex}\n type=\"number\"\n min={1}\n max={table.getPageCount()}\n defaultValue={pageIndex + 1}\n aria-label=\"Current page\"\n className={cn(\"w-14 text-center\", triggerClassName)}\n onKeyDown={(event) => {\n if (event.key !== \"Enter\") return\n commitPageJump(event.currentTarget.value)\n }}\n onBlur={(event) => commitPageJump(event.currentTarget.value)}\n />\n ) : (\n <Select\n value={String(pageIndex + 1)}\n onValueChange={(value) => table.setPageIndex(Number(value) - 1)}\n >\n <SelectTrigger\n size={controlSize}\n aria-label=\"Current page\"\n className={triggerClassName}\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {Array.from({ length: table.getPageCount() }, (_, index) => (\n <SelectItem key={index} value={String(index + 1)}>\n {index + 1}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )\n\n const renderNumberedPagination = () => {\n const pageCount = table.getPageCount()\n const currentPage = pageIndex + 1\n return (\n <Pagination className=\"mx-0 w-auto justify-start\">\n <PaginationContent>\n <PaginationItem>\n <PaginationPrevious\n size={controlIconSize}\n disabled={!table.getCanPreviousPage()}\n onClick={() => table.previousPage()}\n />\n </PaginationItem>\n {getPaginationWindow(currentPage, pageCount).map((page, index) =>\n page === \"ellipsis\" ? (\n <PaginationItem key={`ellipsis-${index}`}>\n <PaginationEllipsis />\n </PaginationItem>\n ) : (\n <PaginationItem key={page}>\n <PaginationLink\n size={controlIconSize}\n isActive={page === currentPage}\n onClick={() => table.setPageIndex(page - 1)}\n >\n {page}\n </PaginationLink>\n </PaginationItem>\n )\n )}\n <PaginationItem>\n <PaginationNext\n size={controlIconSize}\n disabled={!table.getCanNextPage()}\n onClick={() => table.nextPage()}\n />\n </PaginationItem>\n </PaginationContent>\n </Pagination>\n )\n }\n\n return (\n <div\n data-slot=\"data-table\"\n className={cn(\n // Plain inheritance already carries a real ancestor Card's\n // --card-spacing down to every consumer below (toolbar, rows,\n // footer, pane) with no help needed — a custom property that\n // isn't overridden here just cascades normally. There used to be\n // a `--card-spacing: var(--card-spacing, <fallback>)` rule on\n // this root, meant to also give standalone (no real Card) tables\n // one consistent fallback instead of each consumer picking its\n // own. That rule was wrong: a custom property referencing itself\n // is a cycle per spec, and cycles are detected and invalidated\n // *before* the fallback is even considered — it doesn't \"try,\n // fail, then use the fallback,\" it's just permanently invalid,\n // which (since an inherited property with an invalid value does\n // NOT fall back to the inherited value either) broke\n // `--card-spacing` for this entire subtree, standalone or not.\n // Confirmed in an isolated test outside this file: the exact\n // same pattern (`--x: var(--x, fallback)`) on a plain div\n // computes to nothing, with or without an ancestor value. Fixed\n // instead by aligning each consumer's own local fallback below\n // to the same literal value, rather than trying to synthesize\n // one shared default via CSS.\n \"flex h-full\",\n className\n )}\n >\n <div\n ref={mainColumnRef}\n // No column `gap` here on purpose: the only spacing wanted is\n // between the toolbar and the table (the toolbar's own margin\n // below, now itself derived from --card-spacing instead of a flat\n // mb-3). A flex gap would also open a gap between the last row and\n // the footer, which — with the footer's own `border-t` already\n // acting as the divider — read as a stray empty band.\n className=\"flex min-h-0 min-w-0 flex-1 flex-col\"\n >\n <div\n // Same edge-to-edge trick as Table's own row dividers: pull out by\n // the surface's --card-spacing (inherited normally from a real\n // ancestor Card, same 1rem fallback as the footer/pane below\n // when standalone) and widen to match, then pad back in by the\n // same amount, so the toolbar's own outer box lines up with the\n // table's (which is now wider than its unadjusted siblings would\n // be) instead of looking narrower, while its content still sits\n // at the same inset as the table's cell content and never touches\n // the surface's true edge. Right side only reaches that edge\n // while the filters pane is closed — open, the main column's\n // right edge is the pane's own border, not the surface's, so only\n // the left side still pulls out (the width grows by one\n // card-spacing instead of two). The bottom margin is 0.75x\n // --card-spacing rather than a flat value so it scales with size\n // exactly like every other gap in the table.\n className={cn(\n \"@container/data-table-toolbar mb-[calc(var(--card-spacing,1rem)*0.75)] flex flex-wrap items-center justify-between gap-2 px-(--card-spacing,1rem) @max-sm/data-table-toolbar:flex-col @max-sm/data-table-toolbar:items-stretch\",\n filtersPaneOpen\n ? \"-ml-(--card-spacing,0px) w-[calc(100%+var(--card-spacing,0px))]\"\n : \"-mx-(--card-spacing,0px) w-[calc(100%+var(--card-spacing,0px)*2)]\"\n )}\n >\n <div\n className={cn(\n \"flex min-w-0 flex-1 flex-wrap items-center gap-2\",\n size === \"default\" && \"min-h-8\",\n size === \"sm\" && \"min-h-7\",\n size === \"xs\" && \"min-h-6\"\n )}\n >\n {hasSelection && effectiveSelectionBarPlacement === \"toolbar\" ? (\n // Paginated tables (a real footer, not infinite scroll) swap\n // search/filters out for the selection itself here instead of\n // a separate bar by default — search and filters act on rows\n // that may no longer even be on screen once a selection is\n // this loud about itself, so showing both at once reads as\n // two competing toolbars. Set `selectionBarPlacement=\"floating\"`\n // to keep search/filters live instead and get the same\n // floating bar infinite-scroll tables always use (they have\n // no footer to swap into, and search/filtering the\n // still-loading rows underneath a selection is exactly the\n // \"load more, then filter/search what's loaded\" flow that\n // mode exists for, so they never get a choice here).\n <div className=\"flex min-w-0 flex-1 items-center gap-1.5\">\n {isAllFilteredSelected ? (\n <>\n <span className=\"px-1 text-sm whitespace-nowrap text-muted-foreground\">\n All {table.getFilteredRowModel().rows.length} selected\n </span>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size={controlSize}\n onClick={() => setRowSelection({})}\n >\n Clear selection\n </Button>\n </>\n ) : (\n <>\n <span className=\"px-1 text-sm whitespace-nowrap text-muted-foreground\">\n {maxSelectedRows != null\n ? `${selectedRows.length} of ${maxSelectedRows} max selected`\n : `${selectedRows.length} selected`}\n {hiddenByFilterCount > 0 &&\n ` (${hiddenByFilterCount} hidden by filters)`}\n </span>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size={controlSize}\n disabled={atSelectionCap}\n title={\n atSelectionCap\n ? `Up to ${maxSelectedRows} rows can be selected`\n : undefined\n }\n onClick={selectAllFiltered}\n >\n <CheckCheckIcon />\n Select all {selectableFilteredRows.length}\n </Button>\n </>\n )}\n {renderBulkActions && (\n <div className=\"flex items-center gap-1\">\n {renderBulkActions(\n selectedRows.map((row) => row.original),\n () => setRowSelection({})\n )}\n </div>\n )}\n <Button\n type=\"button\"\n variant=\"ghost\"\n size={controlIconSize}\n iconOnly\n aria-label=\"Clear selection\"\n onClick={() => setRowSelection({})}\n >\n <XIcon />\n </Button>\n </div>\n ) : (\n <>\n <InputGroup\n className={cn(\n \"max-w-64\",\n controlSize === \"sm\" && \"h-7\",\n controlSize === \"xs\" && \"h-6\"\n )}\n >\n <InputGroupAddon>\n <SearchIcon />\n </InputGroupAddon>\n <InputGroupInput\n aria-label={searchPlaceholder}\n placeholder={searchPlaceholder}\n value={searchInput}\n onChange={(event) => {\n setSearchInput(event.target.value)\n debouncedSetGlobalFilter(event.target.value)\n }}\n onBlur={() => setGlobalFilter(searchInput)}\n onKeyDown={(event) => {\n if (event.key === \"Enter\") setGlobalFilter(searchInput)\n }}\n />\n </InputGroup>\n\n {(inlineFilterColumns.length > 0 ||\n (!showAllFiltersInline && paneFilterCount > 0)) && (\n // Tighter gap than the gap-2 separating this whole group\n // from search — these filter controls are one closely\n // related cluster, not independent toolbar items.\n // `flex-wrap` here, not just on the outer toolbar row\n // above — flex-wrap only wraps whole *items*, and this\n // whole cluster is one item as far as that outer row is\n // concerned, so without its own wrap, pinning enough\n // filters (no cap on `pinnedFilterIds`) just runs this\n // div past the toolbar's edge instead of onto a new line.\n <div className=\"flex flex-wrap items-center gap-1\">\n {/* While the pane is open, every filterable column lives\n there — the pinned inline chips would just be showing\n the same controls twice, so they're hidden. `hidden`\n (CSS), not a conditional unmount — each one owns its\n own open/pending-draft state, and unmounting on every\n pane open and remounting fresh on every close was\n exactly what let a just-applied pane value fail to\n show here: this stays mounted the whole time, so\n there's no remount for it to have raced. */}\n {inlineFilterColumns.map((column) => (\n <div\n key={column.id}\n className={cn(filtersPaneOpen && \"hidden\")}\n >\n <InlineColumnFilter\n column={column}\n size={controlSize}\n tableDeferred={filterApplyMode === \"deferred\"}\n showFilterCounts={showFilterCounts}\n stickyFilterSelectAll={stickyFilterSelectAll}\n />\n </div>\n ))}\n\n {/* Hidden while the pane is open — every filterable\n column already lives there, so the trigger would\n just be a redundant way to close it. Shown whenever\n there are more than 3 filterable columns at all,\n regardless of how many are currently pinned — even\n with every one of them pinned inline, the pane is\n still the only place to *unpin* one, so it can't be\n allowed to go unreachable. */}\n {/* A looser gap than the pinned filter chips' own\n gap-1 above — \"All filters\" and the applied-count\n text are two independent toolbar items, not one\n tightly related cluster the way the pinned chips\n are. The text and \"Clear filters\" get their own\n tighter gap-2 below — that button already carries\n its own internal padding, so gap-4 there read as\n too much on top of it. */}\n <div className=\"flex items-center gap-4\">\n {!showAllFiltersInline && (\n <Button\n ref={filtersPaneTriggerRef}\n type=\"button\"\n variant=\"outline\"\n size={controlSize}\n aria-expanded={filtersPaneOpen}\n aria-controls={filtersPaneId}\n onClick={() => setFiltersPaneOpen((open) => !open)}\n >\n All filters\n </Button>\n )}\n {/* Right after \"All filters\" (not with the pane's own\n footer button of the same name, though this one now\n does exactly what that one does) — a count-badged\n button read as \"some filters are set, tap to see\n which\"; a plain count is more scannable at a glance,\n and reads correctly regardless of how many of those\n filters are inline-pinned vs pane-only, which the\n badge never distinguished visually anyway. Clears\n every applied filter, inline pinned ones included —\n an unqualified \"Clear filters\" label reads as \"clear\n everything,\" and each inline filter's own per-field\n clear is still there for anyone who wants to clear\n just one instead. */}\n {!showAllFiltersInline &&\n !filtersPaneOpen &&\n columnFilters.length > 0 && (\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm text-muted-foreground\">\n {columnFilters.length} filter\n {columnFilters.length === 1 ? \"\" : \"s\"} applied\n </span>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size={controlSize}\n onClick={() => setColumnFilters([])}\n >\n Clear filters\n </Button>\n </div>\n )}\n </div>\n </div>\n )}\n </>\n )}\n </div>\n\n <div className=\"flex items-center gap-2\">\n {aiAction && (\n <Button\n type=\"button\"\n variant=\"ai\"\n size={size}\n onClick={aiAction.onClick}\n >\n <AISparkle size=\"sm\" data-icon=\"inline-start\" />\n {aiAction.label}\n </Button>\n )}\n\n {actionButtons?.slice(0, 3).map((action, index) =>\n action.variant === \"icon\" ? (\n <Button\n key={index}\n type=\"button\"\n variant=\"outline\"\n size={controlIconSize}\n iconOnly\n disabled={action.disabled}\n aria-label={action.label}\n onClick={action.onClick}\n >\n {action.icon}\n </Button>\n ) : (\n <Button\n key={index}\n type=\"button\"\n variant={\n action.variant === \"secondary\" ? \"outline\" : \"default\"\n }\n size={controlSize}\n disabled={action.disabled}\n onClick={action.onClick}\n >\n {action.icon && (\n <span data-icon=\"inline-start\">{action.icon}</span>\n )}\n {action.label}\n </Button>\n )\n )}\n\n {showColumnToggle && manageableColumns.length > 0 && (\n // A Popover, not a DropdownMenu — each row below has several\n // independently-focusable controls (a checkbox, pin\n // buttons, move buttons), not one action per row.\n // DropdownMenuContent wraps its content in a Radix\n // RovingFocusGroup that (a) intercepts Tab (preventDefault,\n // so it never reaches a plain child button/checkbox) and\n // (b) only moves arrow-key focus between registered\n // DropdownMenuItems — since these rows render plain\n // Checkbox/Button elements instead, a keyboard user who\n // opens the menu has no way to ever focus them. Popover\n // imposes neither restriction — normal Tab order works.\n <Popover\n open={columnsMenuOpen}\n onOpenChange={handleColumnsMenuOpenChange}\n >\n <PopoverTrigger asChild>\n <Button\n type=\"button\"\n variant=\"outline\"\n size={controlIconSize}\n iconOnly\n aria-label=\"Columns\"\n >\n <Columns3Icon />\n </Button>\n </PopoverTrigger>\n <PopoverContent align=\"end\" className=\"w-72 gap-0 p-1\">\n {manageableColumns.length > 10 && (\n <div className=\"px-1.5 pb-1\">\n <Input\n aria-label=\"Search columns\"\n placeholder=\"Search columns...\"\n value={columnSearchQuery}\n onChange={(event) =>\n setColumnSearchQuery(event.target.value)\n }\n className=\"h-7\"\n onKeyDown={(event) => event.stopPropagation()}\n />\n </div>\n )}\n <div className=\"max-h-72 overflow-y-auto\">\n {(() => {\n const matchesSearch = (\n column: (typeof manageableColumns)[number]\n ) =>\n columnLabel(column)\n .toLowerCase()\n .includes(columnSearchQuery.toLowerCase())\n const filteredLeft = displayedLeft.filter(matchesSearch)\n const filteredCenter =\n displayedCenter.filter(matchesSearch)\n const filteredRight = displayedRight.filter(matchesSearch)\n return (\n <>\n {filteredLeft.map(renderColumnMenuRow)}\n {filteredLeft.length > 0 &&\n (filteredCenter.length > 0 ||\n filteredRight.length > 0) && (\n <div className=\"my-1 border-t border-border\" />\n )}\n {filteredCenter.map(renderColumnMenuRow)}\n {filteredRight.length > 0 &&\n (filteredLeft.length > 0 ||\n filteredCenter.length > 0) && (\n <div className=\"my-1 border-t border-border\" />\n )}\n {filteredRight.map(renderColumnMenuRow)}\n </>\n )\n })()}\n </div>\n <div\n // Edge-to-edge divider (same -pull-out/pad-back-in trick\n // used elsewhere in this file) against PopoverContent's\n // own `p-1` (overridden from its own default `p-2.5`\n // above) — only the border reaches the popover's true\n // left/right edges; the buttons stay padded back in to\n // `px-1.5`, the same inset the rows above use. `pb-1.5`\n // matches `pt-2` (not identical — `pt-2` reads correctly\n // against the divider immediately above it, `pb-1.5`\n // against the popover's own ambient `p-1` below) so the\n // buttons sit centered in the footer strip either way,\n // instead of pinned flush to the bottom edge.\n className=\"-mx-1 flex w-[calc(100%+--spacing(2))] items-center justify-between gap-2 border-t border-border px-1.5 pt-2 pb-1.5\"\n >\n {hasColumnCustomization ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"xs\"\n onClick={resetColumns}\n >\n <RotateCcwIcon data-icon=\"inline-start\" />\n Reset to defaults\n </Button>\n ) : (\n <span />\n )}\n {/* gap-1, not gap-2 — Cancel/Apply resolve one\n decision, a tight cluster, not two independent\n footer items (same rule as the toolbar's filter\n controls and PageHeader's Level 1 actions). */}\n <div className=\"flex items-center gap-1\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"xs\"\n onClick={() => handleColumnsMenuOpenChange(false)}\n >\n Cancel\n </Button>\n <Button\n type=\"button\"\n size=\"xs\"\n disabled={!hasPendingColumnsMenuChanges}\n onClick={() => {\n columnsMenuAppliedRef.current = true\n handleColumnsMenuOpenChange(false)\n onColumnLayoutChange?.({\n visibility: columnVisibility,\n order: columnOrder,\n pinning: columnPinning,\n sizing: columnSizing,\n })\n }}\n >\n Apply\n </Button>\n </div>\n </div>\n </PopoverContent>\n </Popover>\n )}\n </div>\n </div>\n\n <div\n ref={tableContainerRef}\n tabIndex={canScrollHorizontally ? 0 : undefined}\n aria-label={\n canScrollHorizontally\n ? `${ariaLabel ?? \"Table\"} scroll area`\n : undefined\n }\n className={cn(\n canScrollHorizontally &&\n \"focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:outline-none\",\n // Height comes from the `rowsAreaHeight` measurement above, not\n // `flex-1` — same intent (fill whatever's left in a stretched\n // surface so the static footer below lands flush at that\n // surface's true bottom edge), just computed directly instead\n // of via flex-grow, which claims 100% of the leftover space\n // unconditionally the instant it's able to (see that state's\n // own comment). `min-h-0` still matters on first paint, before\n // the layout effect has measured anything yet. relative:\n // positions BulkActionsBar (see below) against this box, not\n // the viewport.\n \"relative min-h-0\",\n // This box, not Table's own inner wrapper, is what needs to\n // scroll — a sticky header/pinned column resolves against a\n // single nearest scrolling ancestor, and BulkActionsBar's own\n // `sticky bottom-2` (a sibling of `<Table>` right below) needs\n // that ancestor to be reachable one level up, not buried a\n // level down inside Table's own markup. But Table's inner\n // wrapper is also the thing that bleeds past its own parent\n // via a negative margin to reach the surface's true edges\n // (see table.tsx) — so scrolling here, one level further out\n // and *inset* from those edges by the surface's own padding,\n // would clip that bleed short. Fix: give this box the exact\n // same bleed Table's inner wrapper uses (both can't bleed at\n // once without overshooting, so the inner one is neutralized\n // back to plain 100%-width right below), so it can scroll all\n // the way to those edges instead of clipping them.\n // A visible, thin scrollbar — not `no-scrollbar` — so a\n // sighted user actually sees there's more to scroll to; the\n // `tabIndex`/focus-ring above already means a keyboard user\n // can reach it either way.\n \"-mx-(--card-spacing,0px) w-[calc(100%+var(--card-spacing,0px)*2)] [scrollbar-width:thin] [scrollbar-color:var(--color-border)_transparent] overflow-auto\",\n \"[&_[data-slot=table-container]]:mx-0 [&_[data-slot=table-container]]:w-full [&_[data-slot=table-container]]:overflow-visible\",\n // 2.4.11 — without this, tabbing to a row control near the top\n // of a scrolled, sticky-header table scrolls it exactly to the\n // viewport/scroll-container edge, right under that header. Match\n // scroll-margin-top to the sticky header's own height (h-10/h-9/\n // h-8, same values Table's own size variants already use) so\n // native focus-scroll clears it. Literal per-size strings, not a\n // template value — Tailwind's scanner needs the class name\n // present verbatim in source to generate it.\n size === \"xs\"\n ? \"[&_tbody_:focus-visible]:scroll-mt-8\"\n : size === \"sm\"\n ? \"[&_tbody_:focus-visible]:scroll-mt-9\"\n : \"[&_tbody_:focus-visible]:scroll-mt-10\",\n // While the filters pane is open, the right edge to reach\n // belongs to the pane instead of the surface — cap the bleed\n // back to the main column's own width on that side only.\n filtersPaneOpen && \"mr-0 w-[calc(100%+var(--card-spacing,0px))]\"\n )}\n style={\n onLoadMore\n ? { maxHeight: maxHeight ?? \"32rem\" }\n : {\n ...(rowsAreaHeight === null\n ? undefined\n : { height: rowsAreaHeight }),\n ...(maxHeight ? { maxHeight } : undefined),\n }\n }\n >\n {onRowClick && (\n <span id={rowClickHintId} className=\"sr-only\">\n Press Enter to view details\n </span>\n )}\n {/* Skeleton rows carry no text of their own to announce a\n loading transition — this is the only signal a screen reader\n user gets that a fetch started/finished. */}\n {(loading || loadingMore) && (\n <span role=\"status\" className=\"sr-only\">\n Loading…\n </span>\n )}\n <span role=\"status\" className=\"sr-only\">\n {liveAnnouncement}\n </span>\n <Table\n size={size}\n aria-label={ariaLabel}\n // The real position in the *full* filtered/paginated set, not\n // just what's currently in the DOM — same reasoning as\n // `aria-rowindex` on each row below. `headerGroupsForRender\n // .length` header rows count too (`aria-rowcount`/`rowindex`\n // both describe rows in the accessibility tree, headers\n // included, not just body rows).\n aria-rowcount={filteredRowCount + headerGroupsForRender.length}\n aria-colcount={visibleLeafColumns.length}\n className={cn(\n // `border-separate` (not the table default `border-collapse`)\n // is required for pinned columns: a collapsed border is\n // computed as part of the table's own border grid at layout\n // time and doesn't participate in a sticky cell's compositor\n // offset, so it stays behind at the column's pre-scroll\n // position instead of moving with the pinned divider/cell.\n \"table-fixed border-separate border-spacing-0\",\n striped && \"[&_tbody_tr:nth-child(even)]:bg-muted/20\",\n // Without this, TableRow's own `hover:bg-muted/50`\n // (table.tsx) ties in specificity with the rule right above\n // (one class + one pseudo-class each) — and that rule's\n // compiled order happens to win the tie, silently\n // swallowing the hover tint on every even/striped row (it\n // stays at 20% opacity instead of jumping to hover's 50%).\n // `!` forces `!important`, which always wins over a tied,\n // non-important specificity regardless of source order.\n striped && \"[&_tbody_tr:hover]:bg-muted/50!\",\n cellDividers &&\n \"[&_td]:border-r [&_td:last-child]:border-r-0 [&_th]:border-r [&_th:last-child]:border-r-0\"\n )}\n >\n {caption != null && (\n <TableCaption className={cn(captionHidden && \"sr-only\")}>\n {caption}\n </TableCaption>\n )}\n <colgroup>\n {/* `getHeaderGroups()`, not `getVisibleLeafColumns()` — the\n latter is `columnOrder`-only and doesn't regroup pinned\n columns to the edges the way header/body cells already do,\n so the two would disagree on column order the moment a\n pinned column isn't already first/last by definition\n order, silently mapping each `<col>`'s width to the wrong\n cell. The `filterOnly` check below is belt-and-suspenders on\n top of that column already being forced into\n `columnVisibility` — see that comment — so a `<col>` never\n renders for one even if the two ever disagree. `.at(-1)`,\n not `[0]` — with grouped headers (nested `columns`) the\n leaf row (real per-column widths) is the *last* header\n group, not the first; `[0]` there would be the group row,\n whose \"columns\" are each wider than any one `<col>`. */}\n {table\n .getHeaderGroups()\n .at(-1)\n ?.headers.filter(\n (header) => !header.column.columnDef.meta?.filterOnly\n )\n .map((header) => (\n <col\n key={header.column.id}\n style={{ width: header.column.getSize() }}\n />\n ))}\n </colgroup>\n <TableHeader>\n {headerGroupsForRender.map((headerGroup, headerGroupIndex) => (\n <TableRow key={headerGroup.id}>\n {headerGroup.headers\n .filter(\n (header) =>\n !header.column.columnDef.meta?.filterOnly &&\n // An ungrouped column's own header row is `rowSpan`'d\n // down from row 0 below (`passthroughLeafIds`) —\n // every row after that would otherwise render the\n // same leaf header a second time, right on top of\n // the cell that rowSpan already reaches.\n !(\n headerGroupIndex > 0 &&\n passthroughLeafIds.has(header.column.id)\n )\n )\n .map((header) => {\n const isPassthroughLeaf = passthroughLeafIds.has(\n header.column.id\n )\n // A real group header (this position's column has its\n // own author-defined `columns`, e.g. two leaf columns\n // sharing one label above them — not an ungrouped\n // column's passthrough entry, which is the *same* leaf\n // column, just appearing a row early) gets none of the\n // leaf header's controls — no sort button, resize\n // handle, filter trigger, headerActions, or AI marker,\n // all of which belong to one specific leaf column, not\n // a label spanning several.\n const isRealGroupHeader =\n header.subHeaders.length > 0 && !isPassthroughLeaf\n const isLeafHeader =\n !isRealGroupHeader &&\n (!header.isPlaceholder || isPassthroughLeaf)\n const canSort = isLeafHeader && header.column.getCanSort()\n const sortDirection = header.column.getIsSorted()\n const canResize =\n isLeafHeader && header.column.getCanResize()\n const align =\n header.column.columnDef.meta?.align ?? \"left\"\n const headerControlsReveal =\n openHeaderColumnId === header.column.id\n ? \"opacity-100\"\n : hoverRevealClass\n const setHeaderOpen = (open: boolean) =>\n setOpenHeaderColumnId(open ? header.column.id : null)\n const headerActions = !isLeafHeader\n ? []\n : (header.column.columnDef.meta?.headerActions?.(\n header.column,\n table\n ) ?? [])\n return (\n <TableHead\n key={header.id}\n data-column-id={header.column.id}\n colSpan={header.colSpan}\n rowSpan={\n isPassthroughLeaf && headerGroupIndex === 0\n ? headerGroupsForRender.length\n : undefined\n }\n aria-sort={\n !canSort\n ? undefined\n : sortDirection === \"asc\"\n ? \"ascending\"\n : sortDirection === \"desc\"\n ? \"descending\"\n : \"none\"\n }\n style={{\n ...(stickyHeader && {\n position: \"sticky\",\n // Row 0's own top is 0; each row after it sits\n // beneath every previous header row's fixed\n // height (see `headerRowHeightPx`) — with a\n // single flat header row (no grouping) this is\n // always just 0.\n top: headerGroupIndex * headerRowHeightPx,\n }),\n ...getPinnedStyle(header.column),\n }}\n className={cn(\n \"group/header-actions relative\",\n isRealGroupHeader &&\n // An explicit bottom border (the header row's\n // own default already renders one on the\n // `<tr>`, but stacking a second, explicit one\n // directly on this cell keeps it from ever\n // reading as accidental) — a group label\n // needs to read as its own band, clearly\n // distinct from the real, interactive leaf\n // header row directly beneath it.\n \"border-x border-b border-border text-center\",\n groupBoundaryColumnIds.starts.has(\n header.column.id\n ) && \"border-l border-border\",\n groupBoundaryColumnIds.ends.has(header.column.id) &&\n \"border-r border-border\",\n header.column.columnDef.meta?.mergeRepeatedValues &&\n \"border-x border-border\",\n getPinnedClassName(header.column, table),\n getScrollFadeClassName(\n header.column,\n table,\n canScrollHorizontally\n ),\n stickyHeader && \"z-20 bg-background\",\n // A pinned column's header cell must outrank a\n // plain sticky header cell too, not just body\n // rows — otherwise `getPinnedClassName`'s z-10\n // gets dropped by twMerge in favor of the z-20\n // above (same class group, last one wins), and\n // scrolling unpinned header cells (later in DOM\n // order, now tied at z-20) paint over it.\n stickyHeader &&\n header.column.getIsPinned() &&\n \"z-30\",\n // Column-level AI presence: a colored top rule\n // as the column's boundary marker, plus the same\n // left-edge `bg-ai-glow` glow the body cells\n // below carry, so the glow runs unbroken from\n // this header cell down the whole column instead\n // of starting a row late. A background-image, so\n // it layers over the sticky header's own\n // `bg-background` — `cn` registers the glow\n // classes as background-image utilities, else\n // tailwind-merge dropped `bg-background` as a\n // competing colour and the cell went white.\n isLeafHeader &&\n aiColumnIds?.includes(header.column.id) &&\n \"border-t-2 border-t-ai-wash bg-ai-glow\"\n )}\n >\n <div\n className={cn(\n \"flex items-center gap-0.5\",\n align === \"right\" && \"flex-row-reverse\"\n )}\n >\n {/* No `truncate`/`overflow-hidden` here — it clipped the\n sort button's own hover/rounded background on the side\n the button's `-ml-2`/`-mr-2` shifts it toward. Each\n branch below truncates its own content instead. */}\n <div\n className={cn(\n \"min-w-0 flex-1\",\n align === \"right\" && \"flex justify-end\"\n )}\n >\n {isRealGroupHeader ? (\n // Group headers get no sort/resize/filter\n // controls of their own — just the label,\n // centered over the leaf columns it spans.\n <div className=\"min-w-0 truncate text-center text-xs font-medium\">\n {flexRender(\n header.column.columnDef.header,\n header.getContext()\n )}\n </div>\n ) : header.isPlaceholder &&\n !isPassthroughLeaf ? null : canSort ? ( // of its own. // leaf, this position truly has no column // nothing to render — unlike a passthrough // levels on one branch than another) has // depth under a *real* group with fewer // A genuine placeholder (deeper, unfilled\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"xs\"\n className={cn(\n // `shrink!` overrides Button's own\n // base `shrink-0` — without it, this\n // button keeps its full natural\n // (untruncated) content width no\n // matter how little room a narrow,\n // resized column actually has,\n // pushing the label's rendered\n // width to 0 instead of showing a\n // truncated \"I…\".\n \"h-7 max-w-full min-w-0 shrink! gap-1 px-2 font-medium\",\n align === \"right\" ? \"-mr-2\" : \"-ml-2\"\n )}\n onClick={header.column.getToggleSortingHandler()}\n >\n <span className=\"min-w-0 truncate\">\n {flexRender(\n header.column.columnDef.header,\n header.getContext()\n )}\n </span>\n {enableMultiSort &&\n sorting.length > 1 &&\n sortDirection &&\n (() => {\n const sortRank =\n sorting.findIndex(\n (sort) =>\n sort.id === header.column.id\n ) + 1\n return (\n // `aria-sort` above only ever\n // carries this one column's own\n // direction — it has no concept\n // of rank among several sorted\n // columns at once, so this\n // badge is the only place that\n // order is conveyed at all,\n // visually only (a screen\n // reader gets the direction via\n // `aria-sort`, not this rank —\n // announcing \"sorted, 2\" with\n // no context of what it's the\n // 2nd *of* would be more\n // confusing than helpful).\n <span\n aria-hidden=\"true\"\n className=\"flex size-3.5 shrink-0 items-center justify-center rounded-full bg-muted text-[10px] font-medium text-muted-foreground\"\n >\n {sortRank}\n </span>\n )\n })()}\n {sortDirection === \"asc\" ? (\n <ArrowUpIcon className=\"size-3.5 shrink-0\" />\n ) : sortDirection === \"desc\" ? (\n <ArrowDownIcon className=\"size-3.5 shrink-0\" />\n ) : (\n <ChevronsUpDownIcon className=\"size-3.5 shrink-0 opacity-50\" />\n )}\n </Button>\n </TooltipTrigger>\n {/* Assumes the common ascending-first toggle\n cycle (none → asc → desc → none) — right\n for every story here, since none sets\n `sortDescFirst`. */}\n <TooltipContent>\n {sortDirection === \"asc\"\n ? \"Sort descending\"\n : sortDirection === \"desc\"\n ? \"Clear sort\"\n : \"Sort ascending\"}\n {enableMultiSort &&\n \" — Shift+click to sort by multiple columns\"}\n </TooltipContent>\n </Tooltip>\n ) : (\n // Matches the sort button's label size\n // (Button's `size=\"xs\"` hard-sets `text-xs`)\n // so a plain, non-sortable header doesn't\n // read a step larger than its sortable\n // neighbors at \"default\"/\"sm\" table density.\n <div className=\"min-w-0 truncate text-xs\">\n {flexRender(\n header.column.columnDef.header,\n header.getContext()\n )}\n </div>\n )}\n </div>\n {isLeafHeader &&\n mergedFilterColumns(header.column, table).some(\n (c) => c.getCanFilter()\n ) && (\n <ColumnFilterTrigger\n column={header.column}\n table={table}\n revealClassName={headerControlsReveal}\n onOpenChange={setHeaderOpen}\n tableDeferred={filterApplyMode === \"deferred\"}\n showFilterCounts={showFilterCounts}\n stickyFilterSelectAll={stickyFilterSelectAll}\n />\n )}\n {headerActions.length > 0 && (\n <RowActions\n actions={headerActions}\n revealClassName={headerControlsReveal}\n onOpenChange={setHeaderOpen}\n />\n )}\n {isLeafHeader &&\n aiColumnIds?.includes(header.column.id) && (\n // Generic explanatory copy — compose AILabel\n // directly into this column's own `header`\n // instead of using `aiColumnIds` if a\n // column-specific explanation is needed.\n <AILabel>\n <AIMarkerTrigger\n size={size}\n ariaLabel={`AI-generated column: ${columnLabel(\n header.column\n )}`}\n />\n <AILabelContent>\n <PopoverHeader>\n <PopoverTitle>\n Why you're seeing this\n </PopoverTitle>\n </PopoverHeader>\n <PopoverDescription>\n This column's values are generated or\n suggested by AI.\n </PopoverDescription>\n </AILabelContent>\n </AILabel>\n )}\n </div>\n {canResize && (\n <div\n ref={(element) => {\n if (element) {\n resizeHandleRefs.current.set(\n header.column.id,\n element\n )\n } else {\n resizeHandleRefs.current.delete(\n header.column.id\n )\n }\n }}\n onMouseDown={header.getResizeHandler()}\n onTouchStart={header.getResizeHandler()}\n // The same \"widest currently-rendered cell\"\n // recipe `columnMenuActions`' own \"Fit to\n // content\" uses — the handle's double-click\n // is just a faster path to it than opening\n // that menu.\n onDoubleClick={() =>\n fitColumnToContent(header.column, table)\n }\n // A `role=\"separator\"` this shape (resizable,\n // between two regions) implies a focusable\n // widget per the APG Window Splitter pattern —\n // without tabIndex/keydown/aria-valuenow, it was\n // 100% mouse/touch-only with no keyboard path\n // to resize at all. Roving, not a flat 0 —\n // see `resizableColumnIds`'s own comment.\n tabIndex={\n (activeResizeColumnId ??\n resizableColumnIds[0]) === header.column.id\n ? 0\n : -1\n }\n onFocus={() =>\n setActiveResizeColumnId(header.column.id)\n }\n onKeyDown={(event) => {\n if (\n event.key === \"ArrowUp\" ||\n event.key === \"ArrowDown\"\n ) {\n event.preventDefault()\n const index = resizableColumnIds.indexOf(\n header.column.id\n )\n const nextIndex =\n event.key === \"ArrowDown\"\n ? index + 1\n : index - 1\n const nextId = resizableColumnIds[nextIndex]\n if (!nextId) return\n resizeHandleRefs.current.get(nextId)?.focus()\n setActiveResizeColumnId(nextId)\n return\n }\n const minSize =\n header.column.columnDef.minSize ?? 20\n const maxSize =\n header.column.columnDef.maxSize ??\n Number.MAX_SAFE_INTEGER\n const clamp = (size: number) =>\n Math.min(maxSize, Math.max(minSize, size))\n let nextSize: number | undefined\n if (event.key === \"Home\") {\n nextSize = minSize\n } else if (event.key === \"End\") {\n // Same \"effectively unbounded\" guard the\n // `aria-valuemax` below already uses —\n // jumping to `Number.MAX_SAFE_INTEGER` for\n // a column with no real `maxSize` isn't a\n // \"max\", it's a runaway column.\n nextSize = maxSize < 1e6 ? maxSize : undefined\n } else if (\n event.key === \"ArrowLeft\" ||\n event.key === \"ArrowRight\"\n ) {\n const step = event.shiftKey ? 50 : 10\n const delta =\n event.key === \"ArrowRight\" ? step : -step\n nextSize = clamp(header.getSize() + delta)\n }\n if (nextSize === undefined) return\n event.preventDefault()\n setColumnSizing((prev) => ({\n ...prev,\n [header.column.id]: nextSize,\n }))\n }}\n role=\"separator\"\n aria-orientation=\"vertical\"\n aria-label={`Resize ${columnLabel(header.column)}`}\n aria-valuenow={header.getSize()}\n aria-valuemin={\n header.column.columnDef.minSize ?? 20\n }\n aria-valuemax={\n typeof header.column.columnDef.maxSize ===\n \"number\" &&\n header.column.columnDef.maxSize < 1e6\n ? header.column.columnDef.maxSize\n : undefined\n }\n className={cn(\n \"absolute top-0 right-0 h-full w-1 cursor-col-resize touch-none select-none hover:bg-primary/50 focus-visible:bg-primary focus-visible:outline-none\",\n header.column.getIsResizing() && \"bg-primary\"\n )}\n />\n )}\n </TableHead>\n )\n })}\n </TableRow>\n ))}\n </TableHeader>\n <TableBody>\n {loading ? (\n Array.from({\n length: table.getState().pagination.pageSize,\n }).map((_, rowIndex) => (\n <TableRow key={rowIndex}>\n {visibleLeafColumns.map((_, columnIndex) => (\n <TableCell key={columnIndex}>\n <Skeleton className=\"h-4 w-full\" />\n </TableCell>\n ))}\n </TableRow>\n ))\n ) : error ? (\n <TableRow>\n <TableCell\n colSpan={visibleLeafColumns.length}\n className=\"h-56 text-center\"\n >\n <Empty role=\"alert\">\n <EmptyHeader>\n <EmptyMedia variant=\"icon\">\n <CircleAlertIcon />\n </EmptyMedia>\n <EmptyTitle>Failed to load data</EmptyTitle>\n <EmptyDescription>\n {errorDescription ??\n (typeof error === \"boolean\"\n ? \"Something went wrong. Try again.\"\n : error)}\n </EmptyDescription>\n </EmptyHeader>\n </Empty>\n </TableCell>\n </TableRow>\n ) : rows.length === 0 ? (\n <TableRow>\n <TableCell\n colSpan={visibleLeafColumns.length}\n className=\"h-56 text-center\"\n >\n <Empty>\n <EmptyHeader>\n <EmptyMedia variant=\"icon\">\n <SearchXIcon />\n </EmptyMedia>\n <EmptyTitle>{emptyTitle}</EmptyTitle>\n <EmptyDescription>{emptyDescription}</EmptyDescription>\n </EmptyHeader>\n </Empty>\n </TableCell>\n </TableRow>\n ) : (\n rows.map((row, rowIndex) => (\n <React.Fragment key={row.id}>\n <TableRow\n // The real position in the full filtered set — for a\n // paginated table, `rowIndex` alone resets to 0 every\n // page, silently disagreeing with `aria-rowcount`\n // above the moment there's more than one page.\n // Infinite scroll has no \"page\" of its own (every\n // loaded row renders at once starting from the top),\n // so it skips the pageIndex/pageSize offset entirely.\n aria-rowindex={\n (onLoadMore ? 0 : pageIndex * currentPageSize) +\n rowIndex +\n headerGroupsForRender.length +\n 1\n }\n data-state={row.getIsSelected() ? \"selected\" : undefined}\n onClick={\n onRowClick ? () => onRowClick(row.original) : undefined\n }\n onKeyDown={\n onRowClick\n ? (event) => {\n if (event.key !== \"Enter\" && event.key !== \" \")\n return\n event.preventDefault()\n onRowClick(row.original)\n }\n : undefined\n }\n tabIndex={onRowClick ? 0 : undefined}\n aria-describedby={onRowClick ? rowClickHintId : undefined}\n className={cn(\n onRowClick &&\n \"cursor-pointer outline-none focus-visible:bg-muted/50 focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-inset\",\n // Row-level AI presence: a soft inward glow bleeding\n // in from the row's own left edge (`bg-ai-glow`, a\n // background-image, not box-shadow — box-shadow\n // doesn't paint reliably on `<tr>` in every\n // browser). Applied once here on the whole row,\n // not per-cell, since it needs to read as one\n // continuous glow from the row's true left edge.\n // The matching colored left edge is NOT here: the\n // table is `border-separate` (for pinned columns),\n // and in that model a `<tr>` can't paint borders at\n // all — it's an inset box-shadow on the row's first\n // cell instead, see below.\n aiRowPredicate?.(row.original) && \"bg-ai-glow\",\n // Expanded: the divider between this row and its\n // expanded content is the inset one drawn inside the\n // expanded row below (see its own comment) — this\n // row's own edge-to-edge border would otherwise sit\n // right on top of it, reading as two dividers at once.\n renderExpandedRow && row.getIsExpanded() && \"border-b-0\"\n )}\n >\n {row\n .getVisibleCells()\n .filter(\n (cell) =>\n !cell.column.columnDef.meta?.filterOnly &&\n // rowSpan 0 means a previous row's merged cell\n // already covers this position — the native\n // HTML rowSpan contract, not a real value to\n // ever render a `<td>` for.\n mergedCellRowSpans\n .get(cell.column.id)\n ?.get(row.id) !== 0\n )\n .map((cell, cellIndex) => {\n const alignRight =\n cell.column.columnDef.meta?.align === \"right\"\n const mergeRowSpan = mergedCellRowSpans\n .get(cell.column.id)\n ?.get(row.id)\n const cellContent = aiCellPredicate?.(\n row.original,\n cell.column.id\n ) ? (\n // Cell-level AI presence: just the label,\n // inline AFTER the value — no glow at all.\n // Trailing, not leading: the value stays where\n // the eye expects it in the column, and the\n // marker reads as an annotation on it.\n // Unlike row/column, a single AI-touched value\n // doesn't need its own boundary treatment, only\n // the explainability entry point.\n <span className=\"inline-flex items-center gap-1\">\n {flexRender(\n cell.column.columnDef.cell,\n cell.getContext()\n )}\n <AILabel>\n <AIMarkerTrigger\n size={size}\n ariaLabel=\"AI-suggested value\"\n />\n <AILabelContent>\n <PopoverHeader>\n <PopoverTitle>\n Why you're seeing this\n </PopoverTitle>\n </PopoverHeader>\n <PopoverDescription>\n This value is generated or suggested by AI.\n </PopoverDescription>\n </AILabelContent>\n </AILabel>\n </span>\n ) : (\n flexRender(\n cell.column.columnDef.cell,\n cell.getContext()\n )\n )\n\n // Whether this cell needs its own copy of the\n // row-level AI glow — see the `bg-ai-glow`/\n // `backgroundSize` comment below for why every\n // left-pinned cell needs one, not just the\n // leading one: with `effectiveColumnPinning`\n // above, the injected AI column and a genuinely\n // pinned column can both end up in the same\n // sticky left-pinned group, and each is its own\n // opaque `<td>` — glowing only the very first one\n // would leave the rest of that group as a flat,\n // glow-less gap before the `<tr>`'s own copy\n // reappears past it.\n const needsPinnedAiGlow =\n cell.column.getIsPinned() === \"left\" &&\n aiRowPredicate?.(row.original)\n\n return (\n <TableCell\n key={cell.id}\n data-column-id={cell.column.id}\n rowSpan={\n mergeRowSpan && mergeRowSpan > 1\n ? mergeRowSpan\n : undefined\n }\n style={{\n ...getPinnedStyle(cell.column),\n // `bg-ai-glow`'s 20% fade is relative to\n // whatever box paints it — on the `<tr>`\n // that's the full row, but any one pinned\n // `<td>` alone is almost always narrower.\n // Explicitly sizing the background to the\n // *row's* total width (not this cell's own)\n // reproduces the exact same fade distance as\n // the `<tr>`'s own copy; shifting it left by\n // this column's own cumulative pinned offset\n // (`getStart(\"left\")`) makes each pinned\n // cell show the exact slice of that same\n // gradient that would fall at its actual\n // position in the row — so a *group* of\n // pinned cells reads as one continuous glow\n // sliding underneath them, picking back up\n // exactly where the previous one left off,\n // instead of each restarting the fade from\n // its own left edge.\n ...(needsPinnedAiGlow && {\n backgroundSize: `${table.getTotalSize()}px 100%`,\n backgroundPositionX: `-${cell.column.getStart(\"left\")}px`,\n }),\n }}\n className={cn(\n \"overflow-hidden text-ellipsis\",\n // Reads like a label for the rows it spans,\n // not a spreadsheet-centered value — the\n // default `align-middle` every other cell\n // uses would otherwise float this one dead\n // center of the whole merged block.\n mergeRowSpan != null && \"align-top\",\n // Same reasoning as the header's own group/\n // merge borders above — a merged or grouped\n // column's horizontal row dividers don't\n // cross it the way its neighbors' do, so its\n // own vertical edges need to read as\n // deliberate framing, not a rendering gap.\n mergeRowSpan != null &&\n \"border-x border-border\",\n groupBoundaryColumnIds.starts.has(\n cell.column.id\n ) && \"border-l border-border\",\n groupBoundaryColumnIds.ends.has(\n cell.column.id\n ) && \"border-r border-border\",\n getPinnedClassName(cell.column, table),\n getScrollFadeClassName(\n cell.column,\n table,\n canScrollHorizontally\n ),\n // Column-level AI presence: the same\n // `bg-ai-glow` glow the row-level marker puts\n // on the whole `<tr>`, applied per-cell here\n // instead since a column isn't a single DOM\n // node — every cell in the column shares the\n // same width, so the identical gradient tiles\n // seamlessly into one continuous glow running\n // down the column's left edge. This relies on\n // the `<td>` staying a real table-cell (so its\n // box spans the full row height) — which is\n // why right alignment below is an inner\n // wrapper, never `display:flex` on the td\n // itself (that shrank the cell box to its\n // content height and left a gap in the glow\n // at every row boundary).\n aiColumnIds?.includes(cell.column.id) &&\n \"bg-ai-glow\",\n // Row-level AI presence's colored left edge —\n // an inset box-shadow on the row's first cell,\n // not `border-l` on the `<tr>`: the table is\n // `border-separate` (for pinned columns), and\n // in that model a `<tr>` never paints borders.\n // Inset shadow rather than a real cell border\n // so it costs no layout width and doesn't\n // shift this row's first-column content\n // against the rows around it. Echoes the\n // colored top rule `aiColumnIds` puts on its\n // header cell.\n cellIndex === 0 &&\n aiRowPredicate?.(row.original) &&\n \"shadow-[inset_2px_0_0_0_var(--color-ai-wash)]\",\n // A second copy of the `<tr>`'s own\n // `bg-ai-glow`, directly on every left-pinned\n // cell — needed because each is `position:\n // sticky` with its own opaque `bg-background`\n // once pinned (`getPinnedClassName` above),\n // so the `<tr>`'s copy (anchored to the row's\n // original, unscrolled left edge) would\n // otherwise end up hidden behind them the\n // moment the table scrolls horizontally. The\n // `backgroundSize`/`backgroundPositionX`\n // override above is what keeps this from\n // reading as a smaller glow than the\n // `<tr>`'s, or as several glows instead of\n // one continuous one — see that comment.\n needsPinnedAiGlow && \"bg-ai-glow\"\n )}\n >\n {alignRight ? (\n // An inner `flex justify-end` wrapper (not\n // just `text-right`) so this also right-aligns\n // a custom `cell` that renders its own\n // flex/block markup (a Badge, an icon+text\n // cluster) — `text-align` alone only moves\n // inline content, not a block/flex child's own\n // box. A wrapper rather than `display:flex` on\n // the td itself, so the td stays a real\n // table-cell (see the `bg-ai-glow` note above).\n <div className=\"flex items-center justify-end\">\n {cellContent}\n </div>\n ) : (\n cellContent\n )}\n </TableCell>\n )\n })}\n </TableRow>\n {renderExpandedRow && row.getIsExpanded() && (\n // Two different dividers, on purpose: the one between\n // this and the row above is inset (an inner wrapper's\n // own border-t, indented from the left) to read as\n // \"inside\" that row rather than a peer of it: the one\n // below — separating this expanded content from the\n // *next* top-level row — is this TableRow's own\n // default edge-to-edge border-b, left untouched.\n <TableRow\n data-slot=\"data-table-expanded-row\"\n id={`data-table-expanded-${row.id}`}\n >\n <TableCell\n colSpan={visibleLeafColumns.length}\n className=\"bg-muted/20 p-0\"\n >\n <div className=\"ml-8 border-t border-border p-4\">\n {renderExpandedRow(row.original)}\n </div>\n </TableCell>\n </TableRow>\n )}\n </React.Fragment>\n ))\n )}\n {onLoadMore && hasMore && rows.length > 0 && (\n <>\n <TableRow\n ref={loadMoreSentinelRef}\n aria-hidden=\"true\"\n data-slot=\"data-table-load-more-sentinel\"\n className=\"border-none\"\n >\n <TableCell\n colSpan={visibleLeafColumns.length}\n className=\"h-px p-0\"\n />\n </TableRow>\n {loadingMore &&\n Array.from({ length: 3 }).map((_, rowIndex) => (\n <TableRow key={`load-more-skeleton-${rowIndex}`}>\n {visibleLeafColumns.map((_, columnIndex) => (\n <TableCell key={columnIndex}>\n <Skeleton className=\"h-4 w-full\" />\n </TableCell>\n ))}\n </TableRow>\n ))}\n </>\n )}\n </TableBody>\n </Table>\n {bulkActionsBarMounted && (\n <>\n {/* Real scrollable space, not just visual — reserves room\n for the bar below so it never permanently covers the\n last row(s) once scrolled all the way down. Mounts under\n the same condition as the bar itself: always for infinite\n scroll (no footer to swap into, so the bar is the only\n option), or in paginated mode when `selectionBarPlacement=\"floating\"`\n opts in — see `effectiveSelectionBarPlacement`. Either\n way, the table's own bottom edge is the *scroll*\n container's edge, not a fixed one, so a bar anchored to\n the viewport would be the wrong fix for the wrong\n problem. */}\n <div\n aria-hidden=\"true\"\n style={{ height: bulkActionsBarHeight + 8 }}\n />\n <BulkActionsBar\n ref={bulkActionsBarRef}\n count={selectedRows.length}\n hiddenByFilterCount={hiddenByFilterCount}\n totalCount={selectableFilteredRows.length}\n isAllSelected={isAllFilteredSelected}\n maxSelectedRows={maxSelectedRows}\n atSelectionCap={atSelectionCap}\n onSelectAll={selectAllFiltered}\n onClear={() => setRowSelection({})}\n controlSize={controlSize}\n controlIconSize={controlIconSize}\n >\n {renderBulkActions?.(\n selectedRows.map((row) => row.original),\n () => setRowSelection({})\n )}\n </BulkActionsBar>\n </>\n )}\n </div>\n\n {showFooter && (\n <div\n ref={footerRef}\n // `data-slot=\"card-footer\"` isn't a real CardFooter — it's\n // borrowing Card's own `has-data-[slot=card-footer]:pb-0`\n // detection so a surrounding Card zeroes its bottom padding\n // instead of leaving a gap under this footer, the same way it\n // would for a real CardFooter. Combined with the edge-to-edge\n // treatment (same trick as Table's rows and the toolbar), the\n // footer touches the surface's left/right/bottom edges while its\n // own padding keeps content where a CardFooter's would sit.\n data-slot=\"card-footer\"\n // py is a fixed py-1.5, not --card-spacing-driven like px — the\n // footer's height tracks DataTable's own `size` density, not\n // whatever surface it happens to sit on. Right side only\n // reaches the surface's edge while the filters pane is closed\n // — see the toolbar's own comment above for why.\n className={cn(\n \"flex flex-wrap items-center gap-4 border-t border-border bg-muted/50 px-(--card-spacing,1rem) py-1.5\",\n filtersPaneOpen\n ? \"-ml-(--card-spacing,0px) w-[calc(100%+var(--card-spacing,0px))] rounded-bl-xl\"\n : \"-mx-(--card-spacing,0px) w-[calc(100%+var(--card-spacing,0px)*2)] rounded-b-xl\"\n )}\n >\n <p\n role=\"status\"\n className=\"text-sm whitespace-nowrap text-muted-foreground\"\n >\n {filteredRowCount === 0\n ? \"0 items\"\n : `${rangeStart}–${rangeEnd} of ${filteredRowCount} items`}\n </p>\n\n {showPageSizeSelector && (\n <div className=\"flex items-center gap-1.5 text-sm text-muted-foreground\">\n <span className=\"whitespace-nowrap\">Items per page:</span>\n <Select\n value={String(currentPageSize)}\n onValueChange={(value) => table.setPageSize(Number(value))}\n >\n <SelectTrigger\n size={controlSize}\n aria-label=\"Items per page\"\n className=\"h-auto border-none bg-transparent px-1 font-medium text-foreground\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {pageSizeOptions.map((option) => (\n <SelectItem key={option} value={String(option)}>\n {option}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n )}\n\n {table.getPageCount() > 1 &&\n (paginationStyle === \"numbered\" ? (\n <div className=\"ml-auto border-l border-border pl-4\">\n {renderNumberedPagination()}\n </div>\n ) : (\n <div className=\"ml-auto flex items-center gap-3 border-l border-border pl-4\">\n <div className=\"flex items-center gap-1.5 text-sm text-muted-foreground\">\n {renderPageSelect(\n \"h-auto border-none bg-transparent px-1 font-medium text-foreground\"\n )}\n <span className=\"whitespace-nowrap\">\n of {table.getPageCount()} pages\n </span>\n </div>\n <div className=\"flex items-center gap-0.5\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size={controlIconSize}\n iconOnly\n aria-label=\"Previous page\"\n disabled={!table.getCanPreviousPage()}\n onClick={() => table.previousPage()}\n >\n <ChevronLeftIcon />\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size={controlIconSize}\n iconOnly\n aria-label=\"Next page\"\n disabled={!table.getCanNextPage()}\n onClick={() => table.nextPage()}\n >\n <ChevronRightIcon />\n </Button>\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n\n {filtersPaneOpen && (\n // A docked pane, not a floating overlay — it takes real space from\n // the table (the main column above is `flex-1`, so it shrinks to\n // make room) instead of covering it. Right/bottom/top all reach the\n // true edge of whatever surface DataTable sits on: right via the\n // same `--card-spacing` negative-margin trick as everything else\n // here, top/bottom via `filtersPaneEdgeAdjust` (see the\n // measurement effect above) since a Card's header/footer height\n // isn't expressible as a fixed token.\n <div\n ref={filtersPaneRef}\n id={filtersPaneId}\n role=\"region\"\n aria-labelledby={filtersPaneHeadingId}\n // Escape closes the pane — except when a nested overlay (a\n // filter field's own popover, a select's listbox) is open\n // *inside* it, in which case that overlay should close first,\n // the same \"innermost first\" behavior every stacked overlay in\n // this app already gets. Radix marks the open one with this\n // data attribute regardless of which specific filter/control\n // it belongs to, so checking for any of them under the current\n // target is enough without needing to know which.\n onKeyDown={(event) => {\n if (event.key !== \"Escape\") return\n const target = event.target as HTMLElement\n if (target.closest(\"[data-slot=popover-content], [role=listbox]\"))\n return\n setFiltersPaneOpen(false)\n }}\n style={\n filtersPaneEdgeAdjust\n ? {\n marginTop: filtersPaneEdgeAdjust.marginTop,\n height: filtersPaneEdgeAdjust.height,\n }\n : undefined\n }\n className=\"-mr-(--card-spacing,0px) flex w-72 shrink-0 flex-col gap-4 border-l border-border bg-card p-(--card-spacing,1rem)\"\n >\n <div className=\"flex items-center justify-between gap-2\">\n <h2\n id={filtersPaneHeadingId}\n className=\"text-base font-medium text-foreground\"\n >\n All filters\n </h2>\n <Button\n ref={filtersPaneCloseButtonRef}\n type=\"button\"\n variant=\"ghost\"\n size=\"xs\"\n iconOnly\n aria-label=\"Close filters\"\n onClick={() => setFiltersPaneOpen(false)}\n >\n <XIcon />\n </Button>\n </div>\n {/* `min-h-0` overrides the flex item's default content-based\n min-height so it actually shrinks to the space the pane's\n fixed height leaves for it — without it, a long filter list\n pushes the pane taller than the surface instead of scrolling\n inside it. `-m-1`/`p-1` is breathing room, not a spacing\n choice — `overflow-y-auto` also clips horizontal *paint*\n (a focus ring is a box-shadow, not layout width, but this\n container's `overflow` still masks it at its own edge) even\n though nothing here actually needs to scroll sideways, so a\n focused filter control's ring was getting clipped flush\n against this box's edge. The negative margin cancels the\n padding's effect on layout/position (the pane's own 1rem\n padding around this box is more than enough slack to\n absorb it), it's purely there to push the actual clip\n boundary out past the ring's own spread. */}\n <div className=\"-m-1 flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto p-1\">\n {filterableColumns.map((column) => {\n const pinned = pinnedFilterIds.includes(column.id)\n const label = columnLabel(column)\n return (\n <div\n key={column.id}\n className=\"group/filter-row flex flex-col gap-1.5\"\n >\n <div className=\"flex items-center justify-between gap-2\">\n <Label>\n {label}\n {!column.getIsVisible() &&\n !column.columnDef.meta?.filterOnly && (\n <span className=\"ml-1 font-normal text-muted-foreground\">\n (hidden)\n </span>\n )}\n </Label>\n {allowFilterPinning &&\n !column.columnDef.meta?.filterOnly && (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"xs\"\n iconOnly\n aria-label={\n pinned ? `Unpin ${label}` : `Pin ${label}`\n }\n className={hoverRevealFilterRowClass}\n onClick={() => toggleFilterPin(column.id)}\n >\n {pinned ? (\n <PinOffIcon className=\"size-3.5\" />\n ) : (\n <PinIcon className=\"size-3.5\" />\n )}\n </Button>\n )}\n </div>\n <PaneColumnFilterField\n column={column}\n showFilterCounts={showFilterCounts}\n tableDeferred={filterApplyMode === \"deferred\"}\n stickyFilterSelectAll={stickyFilterSelectAll}\n />\n </div>\n )\n })}\n </div>\n <div\n // Edge-to-edge divider within the pane itself (same trick,\n // scaled to the pane's own padding instead of the surface's)\n // — only the border reaches the pane's true edges; the button\n // stays padded back in to line up with the fields above.\n // `py-1.5` (not the pane's own generous padding) matches the\n // table's own footer height exactly, so the two read as one\n // continuous bar height at the surface's bottom edge, not two\n // differently-sized strips stacked on top of each other. `-mb`\n // reaches past the pane's own ambient bottom padding down to\n // its true bottom edge (same trick as the horizontal edges).\n // Just \"Clear filters\", full width since it's the only action\n // in this footer — a multiselect field's own Cancel/Apply\n // lives inside that field's own popover instead (see\n // `PaneColumnFilterField`), not batched here.\n className=\"-mx-(--card-spacing,1rem) -mb-(--card-spacing,1rem) flex w-[calc(100%+var(--card-spacing,1rem)*2)] items-center justify-center border-t border-border px-(--card-spacing,1rem) py-1.5\"\n >\n <Button\n type=\"button\"\n variant=\"ghost\"\n size={controlSize}\n className=\"w-full\"\n disabled={columnFilters.length === 0}\n onClick={() => setColumnFilters([])}\n >\n Clear filters\n </Button>\n </div>\n </div>\n )}\n </div>\n )\n}\n\nexport { DataTable, HighlightMatch, RowActions, columnMenuActions }\nexport type { RowAction, DataTableColumnLayout, FilterOption }\n\n/**\n * WHEN TO USE\n * - Tabular data that needs sorting, search, column filters, selectable\n * rows with bulk actions, row actions, expandable detail rows, row-click\n * navigation, AI affordances, column management, or pagination handled\n * for you — an admin list, a resource picker, anything past \"just render\n * some rows.\"\n *\n * NOT WHEN\n * - A fixed, small table with no interaction beyond display — use Table\n * directly, it's lighter and this adds nothing over it.\n * - You need virtualization or server-driven async fetching — neither is\n * built in this pass. Virtualization has no existing MMDS primitive to\n * lean on and would need a further headless dependency (e.g.\n * `@tanstack/react-virtual`) — out of scope here. (Column resize is\n * already built — `enableColumnResizing` — this isn't that.)\n *\n * KEY RULES\n * - `columns`/`data` follow `@tanstack/react-table`'s `ColumnDef<TData>[]`\n * shape directly — this component doesn't wrap or reinvent that API, it\n * renders it. Read TanStack Table's own docs for `accessorKey`, custom\n * `cell` renderers, `enableSorting: false` per column, etc.\n * - Sorting, filtering, search, selection, expansion, column order/\n * visibility, and pagination state all live inside DataTable (via\n * `useReactTable`) — an exception to MMDS's usual fully-uncontrolled\n * convention, because the whole point of this component is to own that\n * state so you don't have to. `onRowSelectionChange` is one escape\n * hatch, for wiring selection to something outside (a bulk delete call, a\n * parent form); `onStateChange` is another, firing sorting/columnFilters/\n * globalFilter live for the same reason (a URL param, analytics, a\n * server-driven refetch) — read-only, no live two-way binding, but\n * paired with `defaultSorting`/`defaultColumnFilters`/`defaultGlobalFilter`\n * (seeding each on first render) it's enough to round-trip state out to\n * a URL and back in on next mount. `defaultPinnedFilterIds` seeds the\n * toolbar's inline-filter set the same way — all four still fully\n * uncontrolled after first render, same as everything else here.\n * - Columns render at a fixed width (`table-fixed` + a `<colgroup>` driven by\n * each column's `ColumnDef.size`, default 150) instead of auto-fitting to\n * content — set `size` per column for anything narrower/wider than that.\n * This trades content-driven auto-sizing for widths that stay put across\n * sorting, filtering, and pagination.\n * - The toolbar carries the same edge-to-edge treatment as `Table` itself\n * (see Table's own KEY RULES) — its outer box matches the table's wider\n * footprint instead of looking narrower next to it, and its own padding\n * keeps search/filter/Columns content at the same inset as the table's\n * cell content. Automatic, no prop for it.\n * - Toolbar controls (search, inline/\"All filters\" filters, Columns) and\n * footer controls (items-per-page, page-jump, Previous/Next) render one\n * size notch below `size` itself — \"default\" gets \"sm\" controls, \"sm\"\n * gets \"xs\", \"xs\" stays \"xs\" (already the floor). They're always-visible,\n * repetitive chrome, not row content competing for the same emphasis —\n * automatic, not a prop, and it scales with `size` rather than being\n * fixed at one density. The `aiAction` button is the deliberate exception\n * — it's meant to stand out, so it stays at `size` itself.\n * - `loading` swaps every cell for a `Skeleton` shaped to the real columns —\n * don't also filter `data` to empty while loading, pass the real (stale\n * or previous) data and let `loading` cover it, or column widths jump.\n * - `error` (a boolean) swaps the whole body for an Empty-based failure\n * state — same composition the error-state pattern uses elsewhere\n * (Patterns/UI States), just pre-wired here — and `errorDescription`\n * supplies its copy (default \"Something went wrong. Try again.\"). Two\n * props on purpose: whether it failed and what to say are different\n * things; passing the copy as `error` itself still works but is\n * deprecated. A plain empty `data` array\n * (not loading, no error) renders the \"no results\" Empty state instead.\n * - `enableRowSelection` injects a checkbox column automatically — don't\n * add your own selection column to `columns`.\n * - Row actions are just a normal column whose `cell` renders the exported\n * `RowActions` helper (inline icon buttons for ≤2 actions, an overflow\n * menu past that) — there's no dedicated `actions` prop, this is the same\n * \"put a Button in a cell\" technique any custom `cell` already uses.\n * `RowActions` stops its own clicks from bubbling, so it's safe inside a\n * row that also sets `onRowClick`.\n * - `renderExpandedRow` adds a leading expand/collapse toggle to every row;\n * expanding inserts a full-width row below it with whatever you return —\n * a detail card, or a nested table of its own. The toggle stops its click\n * from bubbling, so it composes with `onRowClick` too.\n * - `onRowClick` makes the whole row clickable and keyboard-activatable\n * (Enter/Space), for row→detail navigation. It composes with\n * `enableRowSelection` — the selection checkbox stops its own click from\n * bubbling up, so checking a row never also triggers `onRowClick`. Any\n * other interactive element you put inside a cell (a button, a link)\n * needs its own `stopPropagation` the same way if it shouldn't also\n * trigger the row click.\n * - `striped` and `cellDividers` are independent visual modifiers — zebra\n * row tinting and vertical column rules, respectively — for tables where\n * plain row/column boundaries are hard to scan.\n * - AI presence has a distinct answer at each level, but DataTable's own\n * outer surface never takes a border/shadow of its own — there's no `ai`\n * boolean prop. For whole-table AI presence, the table just sits inside\n * an AI Card — the exact same recipe AI/Card's \"BoundaryTreatments\" story uses (a\n * `bg-gradient-to-r from-ai-wash to-ai-grad-from p-px` wrapper div, not\n * a one-off DataTable-specific border) — plus a soft inward glow rising\n * from the Card's own bottom edge (`bg-ai-glow-bottom`, fading out at 15%\n * of the Card's own height) as a plain Card className — a background-image,\n * so it layers over Card's own `bg-card` (`cn` registers the glow classes\n * as background-image utilities so tailwind-merge keeps both).\n * `AILabel` sits in the\n * Card's top corner (a `CardAction`) — see the \"WithAIWholeTable\" story.\n * The whole-table treatment is a boundary (gradient outline), an inward\n * bottom glow and a corner marker — not the row/column glow used below.\n * Below that: `aiAction` (table level)\n * renders a `variant=\"ai\"` toolbar button; `aiRowPredicate` (row level)\n * puts a leading `AILabel` as the very first thing in the row — ahead\n * of the expand toggle and selection checkbox, its own leading slot —\n * plus a colored left edge (an inset box-shadow on the row's first\n * cell, not `border-l` on the `<tr>` — the table is `border-separate`\n * for pinned columns, and in that model a `<tr>` never paints borders)\n * and a soft inward glow (`bg-ai-glow`, fading out at 20% of the row's own\n * width, a background-image, not a solid fill) bleeding in from that\n * same left edge — plus a second copy of that same glow painted directly\n * on every left-pinned cell (however many — the injected AI column and a\n * genuinely pinned column can both land in that same sticky group, see\n * `effectiveColumnPinning` above), needed because pinning makes each\n * `position: sticky` with its own opaque background, so the `<tr>`'s copy\n * (anchored to the row's original, unscrolled left edge) would otherwise\n * end up hidden behind them once the table scrolls horizontally. Each\n * copy is sized via an explicit `backgroundSize` (the row's real total\n * width, from `table.getTotalSize()`, not that cell's own narrower one)\n * and shifted by `backgroundPositionX: -column.getStart(\"left\")` so it\n * shows the exact slice of that same gradient that falls at its actual\n * position in the row — the whole pinned group reads as one continuous\n * glow, pixel-identical to the unpinned case, rather than a smaller\n * version of it; `aiColumnIds` (column level)\n * adds the same left-edge glow down the whole column, header cell\n * included (every cell in a column shares the same width, so 20% of it\n * resolves to the same px value down the whole column, tiling into one\n * continuous glow with no visible seam), a colored top rule on that\n * column's header cell\n * (mirrored by the row's own left edge above), and a leading\n * `AILabel` (generic copy) automatically in that header;\n * `aiCellPredicate` (cell level) adds just a trailing `AILabel` inline\n * after the value — no glow, no border, since a single AI-touched value\n * doesn't need its own boundary treatment, only the explainability\n * entry point. Every `AILabelTrigger` here follows DataTable's own\n * density prop — \"default\" at default density, \"sm\" at both sm and xs\n * — never AILabel's `iconOnly`, which is opt-in only for when a\n * designer explicitly asks for it. Give an AI-marked column enough\n * width for its title plus the visible \"AI\" marker (the header keeps\n * both; the title truncates first). Row/column backgrounds use\n * `background-image` rather than `box-shadow` specifically for the row\n * case — `box-shadow` doesn't paint reliably on `<tr>` in every\n * browser, `background-image` does. Compose `AILabel` directly into a\n * column's own `header`/`cell` instead of `aiColumnIds` when a column\n * needs its own specific explanation text.\n * - `meta: { align: \"left\" | \"right\" }` (default `\"left\"`) aligns both a\n * column's header and its cell content — the guideline is quantitative\n * content (amounts, counts, percentages, anything meant to be compared\n * or summed down the column) gets `\"right\"`, text stays `\"left\"`. Cell\n * alignment uses `flex justify-end`, not just `text-align`, so it also\n * right-aligns a custom `cell` that renders its own Badge/icon cluster,\n * not only plain text. `\"right\"` mirrors the header too — the sort\n * button's label moves to that side and the filter icon/`headerActions`\n * cluster moves to the *other* side (`flex-row-reverse` on the same row,\n * not a second layout to maintain), so a numeric column's controls sit\n * off to the side its digits don't occupy.\n * - Search matches only `searchableColumnIds` (default: the first two\n * entries in `columns`) — set it explicitly to widen/narrow the scope.\n * Matches highlight automatically for plain columns (no custom `cell`);\n * for a column with a custom `cell`, highlight it yourself with the\n * exported `HighlightMatch`, reading the query off\n * `info.table.getState().globalFilter`.\n * - Every column gets a filter icon in its header cell. In the toolbar,\n * filters show inline right after search — all of them if there are 3 or\n * fewer filterable columns, otherwise just the pinned ones (the first two\n * by default, and however many the user pins from the pane after that —\n * uncapped, the row wraps onto more lines rather than overflowing) plus\n * an \"All filters\" button, shown whenever there are more than 3\n * filterable columns at all — regardless of how many are currently\n * pinned inline, even every one of them, since the pane is still the\n * only place to *unpin* one. That button opens a docked pane, not a\n * floating overlay — the table area (`flex-1`) shrinks to make room for\n * it on the surface DataTable sits on, instead of the pane covering the\n * table, and reaches that surface's true top/right/bottom edge (measured\n * in JS against the nearest Card, since a header/footer's height isn't a\n * fixed token) while never growing taller than that surface — its own\n * filter-list area is a `min-h-0` flex child so a long list scrolls\n * internally instead of pushing the pane past the surface's edge. While\n * the pane is open, the inline pinned filter buttons and the \"All\n * filters\" trigger both hide — every filterable column and its current\n * value already lives in the pane itself, each with a pin/unpin toggle\n * (hover/keyboard-focus-revealed by default, like the header cell's own\n * controls — see `revealColumnControlsOnHover`), so there's one place to\n * look, not two copies of the same control. `allowFilterPinning` (default\n * `true`) turns that toggle off entirely for a designer-fixed toolbar —\n * `defaultPinnedFilterIds` (or its first-two default) becomes the\n * permanent inline set, with no user-facing way to change which columns\n * those are; every other filterable column still lives in the pane, just\n * with nothing to move it out. Both the inline pinned chips and the pane\n * list order their filterable columns by the table's own column order by\n * default; set `meta: { filterOrder: N }` on a `ColumnDef` to decouple a\n * filter's display position from where its column currently sits — handy\n * when drag-and-drop/the Columns menu can reorder columns but a filter\n * should stay put regardless. Once\n * any filter is set, the toolbar shows \"N filters applied\" (every\n * applied filter, inline pinned ones included, not just pane-only ones)\n * next to a \"Clear filters\" button that clears all of them the same way\n * — the pane's own footer \"Clear filters\" does the same, just from\n * inside the pane. Default filter is text-contains; set\n * `meta: { filterVariant: \"select\" }` on a `ColumnDef` for a\n * multi-select filter instead — a checklist in the header icon's own\n * popover; a `SelectMultiple` for the inline pinned button (its\n * trigger's placeholder is the column label, its value only ever the\n * first selected value's label, `maxLabelChars={12}` ellipsis-truncating\n * that one past 12 characters even with just one selected, plus \"+N\"\n * once more than one is — so neither a single long value nor a long list\n * can run the toolbar out of room) and for the pane (which holds every\n * filterable column at once). A multiselect filter (`\"select\"` or\n * `\"range\"`) always requires a deliberate Apply — in every surface,\n * regardless of `filterApplyMode` — since checking several boxes is a\n * set to review before committing, not a single complete thought; picks\n * still show as checked/selected immediately, on the trigger and in the\n * list both — only the real filtering of the table's rows waits for\n * Apply, not the visible selection itself. Closed (nothing open to\n * preview into), the trigger always reflects whatever's actually\n * committed. Its Cancel/Apply footer always lives inside that one\n * field's own popover — the header icon's, the inline pinned button's,\n * or (in the pane) that field's own, not a pane-wide batch, even though\n * the pane holds every filterable column open at once; each field there\n * resolves independently, exactly like the toolbar's. `filterVariant:\n * \"singleSelect\"` picks exactly one value from that same kind of list —\n * `SelectMultiple` capped at one value (`closeOnSelect`), in every\n * surface including the pane, not a native-feeling dropdown — and,\n * since it closes the instant a pick is made, is never itself deferred\n * regardless of `filterApplyMode` (no open popover left by the time a\n * footer could matter). `meta: { filterRender: (props) => node }` is\n * the escape hatch past those four built-in shapes — a date-range\n * picker, a slider, a radio group, colored selection badges, anything a\n * designer's own filter needs — given the same `{ column, value,\n * onValueChange }` every built-in variant already receives. Deferred\n * under `filterApplyMode=\"deferred\"` in every surface, not just the\n * header-icon popover: the inline toolbar button wraps it in a Popover +\n * the same shared Cancel/Apply footer text filters get there; the pane —\n * which has no popover for any variant — adds an inline Cancel/Apply row\n * directly under the rendered content instead. Immediate, every surface\n * renders it with no extra chrome, same as always.\n * - `meta: { filterOnly: true }` defines a column purely to filter a data\n * point that isn't shown anywhere in the table — its `header`/`cell`\n * never render, and unlike a plain hidden column it has no checkbox\n * anywhere to show it (excluded from the Columns menu entirely, not just\n * defaulted off), while still showing up as a normal filterable column in\n * the \"All filters\" pane — never inline in the toolbar, and never\n * pinnable there either (no header cell for either to point back at).\n * Its presence alone forces the pane to exist even when 3 or fewer\n * *other* columns are filterable, since the pane is its only access\n * point.\n * - `meta: { filterMergeWith: [...ids] }` folds other columns' filter\n * fields into *this* column's own header-icon popover instead of each\n * getting a separate toolbar/pane entry — for a cell whose custom `cell`\n * already displays more than one filterable value at once (e.g. a name\n * plus an MRN below it), so filtering either reads as one decision from\n * one icon. Pairs naturally with `filterOnly` on the merged column(s),\n * since the value being merged in is usually already folded into this\n * column's own `cell` rather than shown in a column of its own — but\n * that's a convention, not a requirement. Each merged field renders\n * below this column's own (always first, unlabeled), under a divider,\n * labeled with its own `header`. The icon fills, and the\n * `filterApplyMode=\"deferred\"` footer commits/discards, based on any of\n * them having an active filter — not just this column's own.\n * - Column reordering lives only in the Columns menu, not the header\n * cells themselves — drag a row there (a thin primary-colored line\n * marks exactly where it'll land) or use its Move left/Move right\n * buttons — real, individually focusable/clickable buttons, so no\n * custom keyboard handling is needed for them to be keyboard-operable,\n * unlike the mouse-only drag. They say left/right, not up/down, even\n * though the list itself is vertical — that's what the button actually\n * does to the real table, which is what the label should describe, not\n * the popover's own incidental layout. Every one of these buttons —\n * Move left/right, and (once a column can pin) Pin left/Pin right or\n * \"Unpin\" using the same icons `columnMenuActions` renders in the\n * header — is hover/keyboard-focus-revealed by default (see\n * `revealColumnControlsOnHover`) and carries its own Tooltip labelling\n * it. Once any column is pinned, the list splits into up to three\n * sections — pinned left, unpinned/center, pinned right, each separated\n * by its own divider — mirroring the same grouping the real header/body\n * cells render in; reordering (drag or the Move buttons) stays confined\n * to one section at a time, matching what a plain visual drag could\n * mean anyway. Moving a column *into* or *out of* a pinned section is\n * what the Pin left/Pin right/Unpin buttons are for instead. Past 10\n * manageable columns the list grows a search box and scrolls instead of\n * pushing the menu taller. Visibility, order, and pinning changes apply\n * live as you make them (so the menu's own rows and the real table\n * never disagree), but the menu's footer still offers Cancel (reverts\n * all three to whatever was committed when the menu opened, including\n * if you dismiss it by clicking outside or Escape) and Apply (disabled\n * until at least one of the three has actually changed since the menu\n * opened) — so the whole session reads as one reviewable batch of\n * changes, not silent live edits. \"Reset to defaults\" sits in that same\n * footer, only rendered once order, hidden columns, or pinning have\n * actually changed from default — a different comparison than Apply's\n * (against the dev-configured default, not against this session's own\n * starting point).\n * - `lockedColumnIds` marks a column as fully static: the user can't\n * reorder it (shows a lock glyph instead of a drag handle, and isn't a\n * valid drop target) or hide it (the show/hide checkbox doesn't even\n * render — `enableHiding: false` is forced regardless of what the\n * column's own `ColumnDef` says). Every injected column (selection,\n * expand, AI) is always locked. Column-visibility control (the checkbox\n * on each row) also only lists columns that don't set\n * `enableHiding: false` themselves — non-hideable columns still show up\n * for reordering.\n * - Column pinning: set `meta: { pin: \"left\" | \"right\" }` on a `ColumnDef`\n * to seed a column's *initial* pinned side. From there it's live,\n * user-togglable `@tanstack/react-table` state — wire the exported\n * `columnMenuActions` into `meta.headerActions` for a ready-made Pin\n * left/Pin right toggle (see below), or call `column.pin(...)` from any\n * custom action of your own. Any number of columns can pin to the same\n * side at once — they render as a contiguous group at that edge, in the\n * order they were pinned (reorderable afterward from the Columns menu,\n * confined to that group — see above), with a divider towards whichever\n * edge actually borders the scrollable/center columns (the left group's\n * right edge, the right group's left edge) on top of the solid\n * background every pinned cell already gets so scrolled content doesn't\n * show through it. Set `meta: { pinLocked: true }` to keep a column's\n * pin fully dev-controlled with no user-facing toggle (`columnMenuActions`\n * omits both Pin actions for it — a custom `headerActions` entry could\n * still call `column.pin()` on it directly, since this only suppresses\n * the built-in affordance, not the underlying state). A pinned column\n * renders `position: sticky` at the correct cumulative offset, so it\n * stays in view while the rest of the table scrolls horizontally —\n * hover/selected/striped row tinting won't show through a pinned cell\n * either, a known trade-off of frozen columns. Pinning a real column left\n * also pulls the injected AI/expand/selection columns (whichever are\n * present) into that same left-pinned group ahead of it, live — so they\n * stay genuinely first and sticky too, matching their own \"leading slot\"\n * documentation, instead of being pushed behind a real pinned column the\n * moment one exists. Only kicks in once something's actually pinned left;\n * otherwise these render exactly as before (unpinned, first in the\n * unpinned/center group by definition order — no sticky, no divider).\n * Nothing to configure — this is automatic and not itself user-togglable.\n * - \"Sticky first column\" isn't its own prop — it's this same pinning\n * recipe, combined with `lockedColumnIds` so the user can't unpin or\n * reorder it away from that edge: `defaultColumnPinning={{ left: [\"id\"] }}`\n * plus `lockedColumnIds={[\"id\"]}` (see `ColumnManagement`).\n * - `meta.headerActions` adds custom actions to a column's header cell,\n * next to its sort button and filter icon — hidden until that header\n * cell is hovered or keyboard-focused (see `revealColumnControlsOnHover`\n * below), and rendered with the same exported `RowActions` helper row\n * actions use (inline for ≤2 actions, an overflow menu past that),\n * uniformly for every column including pinned ones. It's a function of\n * `(column, table)`, not a static array, so it can reflect live\n * per-column state. The exported `columnMenuActions` is the ready-made\n * bundle for this slot — Pin left/Pin right (each becomes \"Unpin\" once\n * pinned to that side, and both are omitted for a `pinLocked` column),\n * Hide column, and Fit to content, each omitted when the column can't\n * do it. Being 3-4 actions, it renders as a single overflow \"more\" menu\n * in practice — the same shape as a plain custom `headerActions`\n * function returning your own `RowAction[]` would, if you need\n * something `columnMenuActions` doesn't cover.\n * - `revealColumnControlsOnHover` (default `true`) is the one knob for\n * whether the filter icon, `headerActions`/`columnMenuActions`, and the\n * Columns menu's own pin/move controls stay hidden until hovered or\n * keyboard-focused, or render always-visible — set `false` for a\n * consumer whose final UX calls for every control visible up front.\n * - `enableColumnResizing` adds a drag handle to every resizable column's\n * right border (`ColumnDef.enableResizing: false` exempts one; injected\n * columns already are). It's also what unlocks `columnMenuActions`' \"Fit\n * to content\" — that action measures every currently-rendered cell in\n * the column (this page's rows, not the whole dataset) via `scrollWidth`\n * and sets the column to the widest one found.\n * - Bulk actions while any row is selected land in one of two places,\n * never both:\n * - `onLoadMore` (infinite scroll, no footer): the toolbar stays\n * mounted and interactive (search, filters) — searching/filtering\n * the rows loaded so far while some of them are selected is exactly\n * the \"load more, then narrow down what's loaded\" flow this mode is\n * for. Instead, a floating bar hovers over the table's own bottom\n * edge (never the raw viewport). Three sections divided by a line:\n * count + \"Select all\" (every filtered row across everything loaded\n * so far, not just what's rendered), `renderBulkActions`'s own\n * product-configured actions in the middle, and a close button that\n * clears the selection. Scrolling to the last row while any row is\n * selected reveals extra bottom padding sized to the bar's own\n * height — otherwise the last row(s) would stay hidden behind it\n * permanently.\n * - Paginated (a real footer): no floating bar, and the toolbar's own\n * search/filter controls are swapped out in place for the selection\n * itself — count, \"Select all\" (every filtered row across every\n * page, not just the current one — same as infinite scroll's own),\n * `renderBulkActions`'s actions, then a close button. Search/filters\n * act on rows that may not even be the ones selected once paging is\n * involved, so showing both the selection and a still-live search\n * box read as two competing toolbars — this is the table's original\n * selection behavior, kept for exactly that reason.\n * - `getRowId` (e.g. `(row) => row.id`) gives each row a stable identity\n * across a sort/filter/pagination change, passed straight through to\n * `@tanstack/react-table` — without it, a row's identity defaults to its\n * index, which is meaningless the moment the row set changes underneath\n * it (filtering 100 rows down to 30 shouldn't treat \"row 3\" before and\n * after as the same thing unless it actually is).\n * - Footer is a full-width bar with the current range/total first, then\n * \"Items per page\" (`pageSizeOptions`, default `[10, 20, 30]`; set\n * `showPageSizeSelector={false}` to drop it — a design call for tables\n * where the consumer shouldn't be choosing page size, not a user\n * preference), and — past one page — a jump-to-page select plus\n * Previous/Next. It carries the same\n * edge-to-edge treatment as the toolbar and `Table` itself, but on all\n * three sides it borders — left, right, *and* bottom — so on a Card it\n * reads as that Card's own footer (same border-t/bg-muted/rounded-b-xl\n * look as a real CardFooter, and it borrows CardFooter's own\n * `data-slot` so a surrounding Card zeroes its bottom padding for it\n * too).\n * - `onLoadMore` switches to infinite scroll instead of pagination: every\n * row in `data` renders (no client-side page slicing), there's no footer\n * at all, and the table scrolls inside its own `maxHeight`-bounded\n * container (default `32rem`) instead of the page — the header (and any\n * pinned columns) stay locked in place via `position: sticky` while the\n * body scrolls. `onLoadMore` fires once an invisible sentinel below the\n * last row scrolls near-into view. Fetch the next page and append it to\n * `data` yourself; `hasMore` stops rendering the sentinel once there's\n * nothing left, and `loadingMore` renders 3 skeleton rows below the last\n * real one — same shape as `loading`, not a spinner — while a fetch is\n * in flight. Sorting/filtering still run client-side over whatever's\n * currently loaded — an inherent limitation with partial data, not\n * something this mode solves.\n *\n * USAGE\n * const columns: ColumnDef<Invoice>[] = [\n * { accessorKey: \"id\", header: \"Invoice\" },\n * { accessorKey: \"amount\", header: \"Amount\" },\n * { accessorKey: \"status\", header: \"Status\", meta: { filterVariant: \"select\" } },\n * ]\n *\n * <DataTable columns={columns} data={invoices} aria-label=\"Invoices\" />\n *\n * // Selectable rows with a bulk-delete action — the count (swapped\n * // into the toolbar here, since this table paginates), \"Select all\",\n * // and the close/clear button are all built in; this only fills the\n * // actions themselves\n * <DataTable\n * columns={columns}\n * data={invoices}\n * enableRowSelection\n * renderBulkActions={(selected, clear) => (\n * <Button size=\"sm\" variant=\"ghost\" onClick={() => { onDelete(selected); clear() }}>\n * <TrashIcon />\n * Delete\n * </Button>\n * )}\n * />\n *\n * // Row actions (inline for ≤2, overflow menu for >2)\n * const withActions: ColumnDef<Invoice>[] = [\n * ...columns,\n * {\n * id: \"actions\",\n * header: \"\",\n * size: 72,\n * enableSorting: false,\n * enableHiding: false,\n * enableColumnFilter: false,\n * cell: ({ row }) => (\n * <RowActions\n * actions={[\n * { label: \"Edit\", icon: PencilIcon, onClick: () => onEdit(row.original) },\n * { label: \"Delete\", icon: TrashIcon, variant: \"destructive\", onClick: () => onDelete(row.original) },\n * ]}\n * />\n * ),\n * },\n * ]\n *\n * // Header-cell actions: a fully custom one, for anything columnMenuActions\n * // doesn't cover\n * const withHeaderActions: ColumnDef<Invoice>[] = columns.map((col) =>\n * col.id === \"status\"\n * ? {\n * ...col,\n * meta: {\n * ...col.meta,\n * headerActions: () => [\n * { label: \"Search\", icon: SearchIcon, onClick: () => openColumnSearch(\"status\") },\n * ],\n * },\n * }\n * : col\n * )\n *\n * // Column resizing (drag handles) plus the ready-made Pin left/Pin\n * // right/Hide/Fit-to-content menu on every column\n * const withColumnMenu: ColumnDef<Invoice>[] = columns.map((col) => ({\n * ...col,\n * meta: { ...col.meta, headerActions: columnMenuActions },\n * }))\n * <DataTable columns={withColumnMenu} data={invoices} enableColumnResizing />\n *\n * // Right-align a quantitative column — header and cells both flip\n * const withAlignedAmount: ColumnDef<Invoice>[] = columns.map((col) =>\n * col.id === \"amount\"\n * ? { ...col, meta: { ...col.meta, align: \"right\" } }\n * : col\n * )\n *\n * // Expandable detail row, striping, and column dividers\n * <DataTable\n * columns={columns}\n * data={invoices}\n * renderExpandedRow={(invoice) => <InvoiceDetailCard invoice={invoice} />}\n * striped\n * cellDividers\n * />\n *\n * // AI presence at every level (except whole-table — wrap in a Card for that, see below)\n * <DataTable\n * columns={columns}\n * data={invoices}\n * aiAction={{ label: \"Summarize\", onClick: () => runAISummary(invoices) }}\n * aiRowPredicate={(invoice) => invoice.flaggedByAI}\n * aiColumnIds={[\"amount\"]}\n * aiCellPredicate={(invoice, columnId) => columnId === \"customer\" && invoice.customerSuggestedByAI}\n * />\n *\n * // Whole-table AI presence: the table just sits inside an AI Card (the\n * // same recipe AI/Card's \"BoundaryTreatments\" story uses) plus a soft glow rising from\n * // the Card's own bottom edge (bg-ai-glow-bottom, a plain Card className) —\n * // AILabel in its top corner, never on DataTable's own surface\n * <div className=\"rounded-xl bg-gradient-to-r from-ai-wash to-ai-grad-from p-px\">\n * <Card className=\"w-full bg-ai-glow-bottom\">\n * <CardHeader>\n * <CardTitle>Invoices</CardTitle>\n * <CardAction>\n * <AILabel>\n * <AILabelTrigger />\n * <AILabelContent>...</AILabelContent>\n * </AILabel>\n * </CardAction>\n * </CardHeader>\n * <CardContent>\n * <DataTable columns={columns} data={invoices} />\n * </CardContent>\n * </Card>\n * </div>\n *\n * // Loading and error states\n * <DataTable columns={columns} data={[]} loading />\n * <DataTable columns={columns} data={[]} error errorDescription=\"Couldn't reach the server.\" />\n *\n * // Clickable rows (e.g. navigate to a detail view), a locked first column,\n * // and a wider items-per-page range\n * <DataTable\n * columns={columns}\n * data={invoices}\n * onRowClick={(invoice) => router.push(`/invoices/${invoice.id}`)}\n * lockedColumnIds={[\"id\"]}\n * pageSizeOptions={[10, 25, 50]}\n * />\n *\n * // Drop the \"Items per page\" control from the static footer — page size\n * // stays fixed at whatever `pageSize` is, not something the user picks\n * <DataTable columns={columns} data={invoices} showPageSizeSelector={false} />\n *\n * // Infinite scroll instead of pagination — no footer\n * const [invoices, setInvoices] = useState(firstPage)\n * const [loadingMore, setLoadingMore] = useState(false)\n * <DataTable\n * columns={columns}\n * data={invoices}\n * hasMore={invoices.length < total}\n * loadingMore={loadingMore}\n * onLoadMore={async () => {\n * setLoadingMore(true)\n * setInvoices((current) => [...current, ...await fetchNextPage(current.length)])\n * setLoadingMore(false)\n * }}\n * />\n */\n"],"mappings":"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"}
|