@bmsuisse/datagrid 0.25.3 → 0.27.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/dist/index.d.ts +352 -3
- package/dist/index.js +674 -186
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/filter/types.ts","../src/filter/evaluate.ts","../src/filter/BooleanFilter.tsx","../src/components/ui/button.tsx","../src/lib/utils.ts","../src/components/ui/popover.tsx","../src/components/ui/select.tsx","../src/filter/DateRangeFilter.tsx","../src/components/ui/calendar.tsx","../src/filter/EnumFilter.tsx","../src/components/ui/checkbox.tsx","../src/components/ui/input.tsx","../src/filter/NumberComparisonFilter.tsx","../src/column/format.ts","../src/filter/numberRangeShared.ts","../src/filter/NumberRangeFilter.tsx","../src/filter/NumberHistogramFilter.tsx","../src/column/types.ts","../src/filter/facetedValues.ts","../src/filter/StringFilter.tsx","../src/filter/registry.tsx","../src/column-selector/ColumnSelector.tsx","../src/components/ui/dialog.tsx","../src/column-selector/persistence.ts","../src/column-selector/visibility.ts","../src/menu/resolveMenuItems.ts","../src/menu/ActionsMenu.tsx","../src/components/ui/dropdown-menu.tsx","../src/grid/DataGrid.tsx","../src/lib/structuralColumns.ts","../src/grid/groupRows.ts","../src/grid/processClientData.ts","../src/grid/useGridState.ts","../src/hooks/useDebouncedCallback.ts","../src/tree/TreeDataGrid.tsx","../src/tree/flattenTree.ts","../src/tree/selectionState.ts","../src/tree/useTreeState.ts"],"sourcesContent":["/**\n * Shared filter/sort contract for @bmsuisse/datagrid.\n *\n * This shape is intentionally mirrored byte-for-byte (field names and operator\n * set) by the Python package `bmsdna.datagrid` (see `bmsdna/datagrid/filters.py`)\n * so that a `GridState` produced by the grid in \"server\" mode can be serialized\n * as JSON and handed directly to a FastAPI endpoint that turns it into SQL or a\n * Meilisearch filter string.\n */\n\n/** The full set of operators supported by any FilterDescriptor. */\nexport type FilterOperator =\n | \"eq\"\n | \"neq\"\n | \"lt\"\n | \"lte\"\n | \"gt\"\n | \"gte\"\n | \"in\"\n | \"notIn\"\n | \"contains\"\n | \"doesNotContain\"\n | \"startsWith\"\n | \"endsWith\"\n | \"isEmpty\"\n | \"isNotEmpty\"\n | \"isNull\"\n | \"isNotNull\"\n | \"between\";\n\n/**\n * A single leaf filter condition.\n *\n * `field` is either a plain field name, or a pre-qualified path (e.g.\n * `[\"c\", \"customer_name\"]` for `c.customer_name` in a join) — the grid never\n * infers joins, callers pre-qualify ambiguous fields themselves.\n *\n * `value` is an array for `in`/`notIn`/`between`, and is omitted entirely for\n * unary operators like `isNull`/`isNotNull`/`isEmpty`/`isNotEmpty`.\n */\nexport interface FilterDescriptor {\n field: string | string[];\n operator: FilterOperator;\n value?: unknown;\n ignoreCase?: boolean;\n}\n\n/** Combines two or more filters (leaf or nested composite) with AND/OR logic. */\nexport interface CompositeFilterDescriptor {\n logic: \"and\" | \"or\";\n filters: (FilterDescriptor | CompositeFilterDescriptor)[];\n}\n\nexport interface SortDescriptor {\n field: string;\n dir: \"asc\" | \"desc\";\n}\n\n/** The full sort/filter/pagination state a <DataGrid> tracks and can emit. */\nexport interface GridState {\n filter: CompositeFilterDescriptor | null;\n sort: SortDescriptor[];\n page: number;\n pageSize: number;\n}\n\n/** True for a leaf `FilterDescriptor`, false for a `CompositeFilterDescriptor`. */\nexport function isFilterDescriptor(\n node: FilterDescriptor | CompositeFilterDescriptor,\n): node is FilterDescriptor {\n return \"operator\" in node;\n}\n\n/** True for a `CompositeFilterDescriptor`, false for a leaf `FilterDescriptor`. */\nexport function isCompositeFilterDescriptor(\n node: FilterDescriptor | CompositeFilterDescriptor,\n): node is CompositeFilterDescriptor {\n return \"logic\" in node;\n}\n\n/** Operators that never carry a `value` (unary presence/absence checks). */\nexport const UNARY_OPERATORS: ReadonlySet<FilterOperator> = new Set([\n \"isEmpty\",\n \"isNotEmpty\",\n \"isNull\",\n \"isNotNull\",\n]);\n\n/** Renders a `field` (string or pre-qualified path) as a single dotted key, for use as a stable id/key. */\nexport function fieldKey(field: string | string[]): string {\n return Array.isArray(field) ? field.join(\".\") : field;\n}\n","/**\n * Client-mode filter evaluator. This is the `\"client\"` DataSource's answer to\n * `sql.py`'s `build_select`/`build_count` and `meili.py`'s\n * `build_filter_expr` — same contract (CompositeFilterDescriptor /\n * FilterDescriptor), same operator semantics, applied in-memory instead of\n * pushed down to a query engine.\n *\n * The operator semantics implemented here MUST stay identical to the Python\n * side (see bmsdna/datagrid/sql.py and bmsdna/datagrid/meili.py) — that's the\n * whole point of \"client\" and \"server\" DataSource modes being interchangeable\n * from the caller's perspective. Key semantics, spelled out because they're\n * easy to get subtly wrong:\n * - `contains`/`startsWith`/`endsWith` are case-sensitive unless `ignoreCase`.\n * - `between` is inclusive on both ends.\n * - `isNull`/`isNotNull` treat both `null` and `undefined` as \"null\".\n * - `isEmpty`/`isNotEmpty` treat `null`/`undefined`/`\"\"`/`[]` as \"empty\".\n * - Every operator (including `eq`/`in`/`neq`/`notIn`/`doesNotContain`) is\n * false against a `null`/`undefined` row value, except the explicitly\n * null-aware operators themselves. This mirrors SQL's three-valued logic\n * collapsed into a WHERE clause: a comparison against NULL is UNKNOWN,\n * which always excludes the row — including for negated comparisons\n * like `<> `/`NOT IN`/`NOT LIKE`, which is easy to get backwards.\n */\nimport { parseISO } from \"date-fns\";\nimport type { CompositeFilterDescriptor, FilterDescriptor } from \"./types\";\nimport { fieldKey, isCompositeFilterDescriptor } from \"./types\";\n\n/**\n * Resolves a (possibly pre-qualified/nested) field path against a row.\n * Exported for reuse by <DataGrid>'s client-mode sort comparator, which\n * needs the exact same field resolution the filter evaluator uses.\n */\nexport function getFieldValue(row: unknown, field: string | string[]): unknown {\n const segments = fieldKey(field).split(\".\");\n let current: unknown = row;\n for (const segment of segments) {\n if (current === null || typeof current !== \"object\") return undefined;\n current = (current as Record<string, unknown>)[segment];\n }\n return current;\n}\n\nfunction isNullish(value: unknown): boolean {\n return value === null || value === undefined;\n}\n\nfunction isEmptyValue(value: unknown): boolean {\n if (isNullish(value)) return true;\n if (typeof value === \"string\") return value.length === 0;\n if (Array.isArray(value)) return value.length === 0;\n return false;\n}\n\n/**\n * Converts a Date, or a string/number parseable as one, to epoch milliseconds.\n *\n * Strings go through date-fns's `parseISO` rather than the bare `new\n * Date(value)` constructor: a `\"YYYY-MM-DD\"` string is parsed by the JS spec\n * as UTC midnight, not local midnight, but `DateRangeFilter` always\n * serializes date-only bounds in *local* time (via date-fns `format`) — so\n * naively parsing one back with `new Date(value)` would shift it by the\n * local UTC offset (e.g. a \"Today\" filter bound of `\"2024-06-15\"` would\n * parse to 2024-06-14T20:00 local in a timezone behind UTC, excluding rows\n * that are actually on the 15th). `parseISO` treats a date-only string as\n * local midnight, matching how it was serialized — the same fix\n * `DateRangeFilter`'s own `parseBound` applies for that widget's *display*,\n * shared here via the same date-fns function instead of a second hand-rolled\n * copy of the \"append a local-time marker\" logic.\n */\nfunction toEpochMs(value: unknown): number | null {\n if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value.getTime();\n if (typeof value === \"number\") return value;\n if (typeof value === \"string\") {\n const parsed = parseISO(value);\n return Number.isNaN(parsed.getTime()) ? null : parsed.getTime();\n }\n return null;\n}\n\n/** A string is \"numeric\" if it round-trips through Number() to a finite value (and isn't blank/whitespace). */\nfunction asFiniteNumber(value: number | string): number | null {\n if (typeof value === \"number\") return Number.isFinite(value) ? value : null;\n if (value.trim() === \"\") return null;\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : null;\n}\n\n/** Normalizes a non-Date value to something orderable: a number or a string. */\nfunction normalizeForCompare(value: unknown): number | string | null {\n if (isNullish(value)) return null;\n if (typeof value === \"number\") return value;\n if (typeof value === \"boolean\") return value ? 1 : 0;\n return String(value);\n}\n\n/**\n * Orders two values. Returns null when either side is nullish, or can't be\n * compared at all (callers treat that as \"the comparison doesn't hold\").\n *\n * When either side is an actual `Date` instance, both sides are parsed as\n * dates and compared chronologically (via epoch ms) — comparing a `Date` to\n * an ISO string by normalizing the `Date` to a number would compare a\n * stringified epoch-ms number against the ISO string lexicographically,\n * which is nonsensical. When neither side is a `Date`, ISO 8601 date/\n * datetime strings already sort correctly under plain string comparison, so\n * no date parsing is needed (or safe to assume) there. If exactly one side\n * normalizes to a number, the other is still tried as a numeric string (e.g.\n * a row value that arrived as `\"9\"` via JSON/CSV/query-string, compared\n * against a NumberRangeFilter's literal `10`) — without this, `\"9\" > \"10\"`\n * lexicographically, inverting every gt/lt/gte/lte/between verdict for a\n * column whose values aren't consistently typed.\n *\n * Exported for reuse by <DataGrid>'s client-mode sort comparator.\n */\nexport function compareValues(a: unknown, b: unknown): number | null {\n if (a instanceof Date || b instanceof Date) {\n const ta = toEpochMs(a);\n const tb = toEpochMs(b);\n return ta === null || tb === null ? null : ta - tb;\n }\n const na = normalizeForCompare(a);\n const nb = normalizeForCompare(b);\n if (na === null || nb === null) return null;\n const numA = asFiniteNumber(na);\n const numB = asFiniteNumber(nb);\n if (numA !== null && numB !== null) return numA - numB;\n const sa = String(na);\n const sb = String(nb);\n if (sa < sb) return -1;\n if (sa > sb) return 1;\n return 0;\n}\n\nfunction valuesEqual(rowValue: unknown, target: unknown, ignoreCase: boolean | undefined): boolean {\n if (ignoreCase && typeof rowValue === \"string\" && typeof target === \"string\") {\n return rowValue.toLowerCase() === target.toLowerCase();\n }\n if (rowValue === target) return true;\n // Falls back to the same numeric-string/Date coercion compareValues uses\n // for gt/lt/between — without this, a row value that arrived as the\n // string \"9\" would fail eq/in against the number 9 (`\"9\" === 9` is\n // false) while correctly satisfying gt/lt against the same pair, an\n // inconsistency between operators that are supposed to agree.\n return compareValues(rowValue, target) === 0;\n}\n\nfunction stringMatch(\n rowValue: unknown,\n needle: unknown,\n ignoreCase: boolean | undefined,\n test: (haystack: string, needle: string) => boolean,\n): boolean {\n if (typeof rowValue !== \"string\" || typeof needle !== \"string\") return false;\n const haystack = ignoreCase ? rowValue.toLowerCase() : rowValue;\n const target = ignoreCase ? needle.toLowerCase() : needle;\n return test(haystack, target);\n}\n\nfunction isTwoElementArray(value: unknown): value is [unknown, unknown] {\n return Array.isArray(value) && value.length === 2;\n}\n\nfunction evaluateLeaf<TRow>(row: TRow, filter: FilterDescriptor): boolean {\n const rowValue = getFieldValue(row, filter.field);\n const { operator, value, ignoreCase } = filter;\n\n switch (operator) {\n case \"isNull\":\n return isNullish(rowValue);\n case \"isNotNull\":\n return !isNullish(rowValue);\n case \"isEmpty\":\n return isEmptyValue(rowValue);\n case \"isNotEmpty\":\n return !isEmptyValue(rowValue);\n\n case \"eq\":\n return !isNullish(rowValue) && valuesEqual(rowValue, value, ignoreCase);\n case \"neq\":\n return !isNullish(rowValue) && !valuesEqual(rowValue, value, ignoreCase);\n\n case \"lt\":\n case \"lte\":\n case \"gt\":\n case \"gte\": {\n const cmp = compareValues(rowValue, value);\n if (cmp === null) return false;\n if (operator === \"lt\") return cmp < 0;\n if (operator === \"lte\") return cmp <= 0;\n if (operator === \"gt\") return cmp > 0;\n return cmp >= 0;\n }\n\n case \"between\": {\n if (isNullish(rowValue) || !isTwoElementArray(value)) return false;\n const [boundA, boundB] = value;\n const cmpA = compareValues(rowValue, boundA);\n const cmpB = compareValues(rowValue, boundB);\n if (cmpA === null || cmpB === null) return false;\n // Order-independent on purpose: a hand-built or saved FilterDescriptor\n // (anything other than NumberRangeFilter's own guarded emit path) may\n // carry its bounds as [larger, smaller]; treating that as \"matches\n // nothing\" rather than as the equivalent valid range is a real, easy\n // mistake to make (see NumberRangeFilter's descriptorFor for the UI\n // widget's side of this same guard).\n return (cmpA >= 0 && cmpB <= 0) || (cmpA <= 0 && cmpB >= 0);\n }\n\n case \"in\":\n return (\n !isNullish(rowValue) &&\n Array.isArray(value) &&\n value.some((candidate) => valuesEqual(rowValue, candidate, ignoreCase))\n );\n case \"notIn\":\n return (\n !isNullish(rowValue) &&\n Array.isArray(value) &&\n !value.some((candidate) => valuesEqual(rowValue, candidate, ignoreCase))\n );\n\n case \"contains\":\n return stringMatch(rowValue, value, ignoreCase, (h, n) => h.includes(n));\n case \"doesNotContain\":\n return !isNullish(rowValue) && !stringMatch(rowValue, value, ignoreCase, (h, n) => h.includes(n));\n case \"startsWith\":\n return stringMatch(rowValue, value, ignoreCase, (h, n) => h.startsWith(n));\n case \"endsWith\":\n return stringMatch(rowValue, value, ignoreCase, (h, n) => h.endsWith(n));\n }\n}\n\n/**\n * Evaluates a filter tree (leaf or composite, or `null`/`undefined` for \"no\n * filter\") against a single row. Used by `<DataGrid>` in `\"client\"` mode;\n * `\"server\"` mode never calls this — it trusts the caller already applied\n * the equivalent filter server-side.\n */\nexport function evaluateFilter<TRow>(\n row: TRow,\n filter: CompositeFilterDescriptor | FilterDescriptor | null | undefined,\n): boolean {\n if (!filter) return true;\n if (isCompositeFilterDescriptor(filter)) {\n return filter.logic === \"and\"\n ? filter.filters.every((child) => evaluateFilter(row, child))\n : filter.filters.some((child) => evaluateFilter(row, child));\n }\n return evaluateLeaf(row, filter);\n}\n","import { FunnelIcon } from \"@heroicons/react/24/outline\";\nimport type { ReactElement } from \"react\";\nimport type { BooleanColumn } from \"../column/types\";\nimport { Button } from \"../components/ui/button\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"../components/ui/popover\";\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"../components/ui/select\";\nimport { cn } from \"../lib/utils\";\nimport type { FilterDescriptor } from \"./types\";\nimport type { FilterWidgetProps } from \"./widget-types\";\n\ntype BooleanChoice = \"all\" | \"yes\" | \"no\";\n\nconst BOOLEAN_CHOICES: readonly { value: BooleanChoice; label: string }[] = [\n { value: \"all\", label: \"All\" },\n { value: \"yes\", label: \"Yes\" },\n { value: \"no\", label: \"No\" },\n];\n\n/**\n * Derives the displayed choice from a FilterDescriptor. This widget only ever\n * emits `eq`, but a `GridState` can round-trip through a URL/server and come\n * back with `neq` (e.g. a foreign caller expressing \"not true\") — `neq` must\n * invert the mapping, or the dropdown would display \"Yes\" for a filter that\n * actually means \"not true\" (i.e. false).\n *\n * `eq`/`neq` are the only operators this maps to a specific choice on\n * purpose: a 3-way All/Yes/No select has exactly three states to represent,\n * and `isNull`/`isNotNull`/`in`/`notIn` don't correspond to any of them\n * unambiguously (e.g. `in: [true]` and `eq: true` both \"mean\" Yes, but `in:\n * [true, false]` means neither Yes nor No nor really \"All\" either). Every\n * other operator falls through to \"All\" rather than guessing — a deliberate\n * scope limit of this specific widget's UI model, not a bug to keep chasing;\n * a caller who needs to represent those distinctly should render its own\n * filter UI for that column via `cell`/a custom filter render, not this one.\n */\nfunction choiceOf(value: FilterDescriptor | undefined): BooleanChoice {\n if (!value) return \"all\";\n if (value.operator === \"eq\") {\n if (value.value === true) return \"yes\";\n if (value.value === false) return \"no\";\n return \"all\";\n }\n if (value.operator === \"neq\") {\n if (value.value === true) return \"no\";\n if (value.value === false) return \"yes\";\n return \"all\";\n }\n return \"all\";\n}\n\n/**\n * Default filter widget for `type: \"boolean\"` columns: a 3-way All/Yes/No\n * select.\n *\n * `bare` (default `false`) skips this component's own Popover/trigger\n * Button, rendering just the select directly — see `EnumFilter`'s identical\n * `bare` doc for why (`renderDefaultFilterWidget` passes this automatically\n * outside `filterDisplay: \"row\"`).\n */\nexport function BooleanFilter<TRow>({\n column,\n value,\n onChange,\n bare = false,\n}: FilterWidgetProps<BooleanColumn<TRow>> & { bare?: boolean }): ReactElement {\n const choice = choiceOf(value);\n const isFiltered = choice !== \"all\";\n const summary = choice === \"yes\" ? \"Yes\" : choice === \"no\" ? \"No\" : \"\";\n\n function emit(next: BooleanChoice): void {\n if (next === \"all\") {\n onChange(undefined);\n return;\n }\n onChange({ field: column.id, operator: \"eq\", value: next === \"yes\" });\n }\n\n const panel = (\n <Select\n value={choice}\n onValueChange={(next) => {\n const match = BOOLEAN_CHOICES.find((option) => option.value === next)?.value;\n if (match) emit(match);\n }}\n >\n <SelectTrigger aria-label={`${column.header} filter`}>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {BOOLEAN_CHOICES.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n );\n\n if (bare) {\n return <div data-testid={`filter-${column.id}`}>{panel}</div>;\n }\n\n return (\n <Popover>\n <PopoverTrigger asChild>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className={cn(\"gap-1\", isFiltered ? \"max-w-[140px] px-2\" : \"w-8 justify-center px-0\")}\n aria-label={`Filter ${column.header}`}\n data-testid={`filter-${column.id}`}\n >\n <FunnelIcon\n className={cn(\"h-3.5 w-3.5 shrink-0\", isFiltered ? \"opacity-100 text-primary\" : \"opacity-40\")}\n aria-hidden\n />\n {isFiltered && <span className=\"truncate\">{summary}</span>}\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-40 p-2\">{panel}</PopoverContent>\n </Popover>\n );\n}\n","import { Slot } from \"@radix-ui/react-slot\";\nimport { type VariantProps, cva } from \"class-variance-authority\";\nimport type { ButtonHTMLAttributes } from \"react\";\nimport { forwardRef } from \"react\";\nimport { cn } from \"../../lib/utils\";\n\nexport const buttonVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n outline: \"border border-input bg-background hover:bg-accent hover:text-accent-foreground\",\n ghost: \"hover:bg-accent hover:text-accent-foreground\",\n destructive: \"bg-destructive text-destructive-foreground hover:bg-destructive/90\",\n },\n size: {\n default: \"h-9 px-3\",\n sm: \"h-8 px-2 text-xs\",\n icon: \"h-9 w-9\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n },\n);\n\nexport interface ButtonProps\n extends ButtonHTMLAttributes<HTMLButtonElement>,\n VariantProps<typeof buttonVariants> {\n asChild?: boolean;\n}\n\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(\n ({ className, variant, size, asChild = false, ...props }, ref) => {\n const Comp = asChild ? Slot : \"button\";\n // `buttonVariants` (via `cva`) already appends `className` itself —\n // through plain `clsx` concatenation, not `twMerge` — so it's already\n // present in `variantClass` below; the separate `cn()` call exists only\n // to resolve conflicts between it and the variant/size classes (e.g. a\n // caller overriding the default `h-9`). Skipping that resolution pass\n // entirely when there's no `className` to conflict with matters here\n // specifically because `<DataGrid>` renders a `<Button>` per visible row\n // (the expand toggle, the row-actions trigger) with no `className` of\n // its own — every scroll-triggered re-render was paying for a full\n // clsx+twMerge parse of an already-final, already-conflict-free string.\n const variantClass = buttonVariants({ variant, size });\n return (\n <Comp className={className ? cn(variantClass, className) : variantClass} ref={ref} {...props} />\n );\n },\n);\nButton.displayName = \"Button\";\n","import type { SyntheticEvent } from \"react\";\nimport { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/** Merges conditional class names, then dedupes conflicting Tailwind utility classes. */\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n\n/**\n * Every interactive control rendered *inside* a row (the expand chevron, the\n * selection checkbox, `rowActions`' kebab trigger) must stop a click from\n * bubbling up to the `<tr>` — otherwise a `getRowProps`-driven row-wide\n * `onClick` (e.g. \"click the row to navigate\") fires alongside whatever the\n * control itself was meant to do, since the control's own click event\n * bubbles to the row by default. Use this on every such control's `onClick`\n * rather than inlining `(event) => event.stopPropagation()` at each call\n * site — a repeated one-liner is easy to miss adding to the *next* per-row\n * control (see ActionsMenu's kebab trigger, which was missed here once\n * already).\n */\nexport function stopRowClick(event: SyntheticEvent): void {\n event.stopPropagation();\n}\n","import * as PopoverPrimitive from \"@radix-ui/react-popover\";\nimport type { ComponentPropsWithoutRef, ElementRef } from \"react\";\nimport { forwardRef } from \"react\";\nimport { cn } from \"../../lib/utils\";\n\nexport const Popover = PopoverPrimitive.Root;\nexport const PopoverTrigger = PopoverPrimitive.Trigger;\nexport const PopoverAnchor = PopoverPrimitive.Anchor;\n\nexport const PopoverContent = forwardRef<\n ElementRef<typeof PopoverPrimitive.Content>,\n ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>\n>(({ className, align = \"start\", sideOffset = 4, ...props }, ref) => (\n <PopoverPrimitive.Portal>\n <PopoverPrimitive.Content\n ref={ref}\n align={align}\n sideOffset={sideOffset}\n className={cn(\n \"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none\",\n className,\n )}\n {...props}\n />\n </PopoverPrimitive.Portal>\n));\nPopoverContent.displayName = \"PopoverContent\";\n","import * as SelectPrimitive from \"@radix-ui/react-select\";\nimport { Check, ChevronDown } from \"lucide-react\";\nimport type { ComponentPropsWithoutRef, ElementRef } from \"react\";\nimport { forwardRef } from \"react\";\nimport { cn } from \"../../lib/utils\";\n\nexport const Select = SelectPrimitive.Root;\nexport const SelectGroup = SelectPrimitive.Group;\nexport const SelectValue = SelectPrimitive.Value;\n\nexport const SelectTrigger = forwardRef<\n ElementRef<typeof SelectPrimitive.Trigger>,\n ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>\n>(({ className, children, ...props }, ref) => (\n <SelectPrimitive.Trigger\n ref={ref}\n className={cn(\n \"flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50\",\n className,\n )}\n {...props}\n >\n {children}\n <SelectPrimitive.Icon asChild>\n <ChevronDown className=\"h-4 w-4 opacity-50\" />\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n));\nSelectTrigger.displayName = \"SelectTrigger\";\n\nexport const SelectContent = forwardRef<\n ElementRef<typeof SelectPrimitive.Content>,\n ComponentPropsWithoutRef<typeof SelectPrimitive.Content>\n>(({ className, children, position = \"popper\", ...props }, ref) => (\n <SelectPrimitive.Portal>\n <SelectPrimitive.Content\n ref={ref}\n position={position}\n className={cn(\n \"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md\",\n position === \"popper\" && \"translate-y-1\",\n className,\n )}\n {...props}\n >\n <SelectPrimitive.Viewport className=\"p-1\">{children}</SelectPrimitive.Viewport>\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n));\nSelectContent.displayName = \"SelectContent\";\n\nexport const SelectItem = forwardRef<\n ElementRef<typeof SelectPrimitive.Item>,\n ComponentPropsWithoutRef<typeof SelectPrimitive.Item>\n>(({ className, children, ...props }, ref) => (\n <SelectPrimitive.Item\n ref={ref}\n className={cn(\n \"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n className,\n )}\n {...props}\n >\n <span className=\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\">\n <SelectPrimitive.ItemIndicator>\n <Check className=\"h-4 w-4\" />\n </SelectPrimitive.ItemIndicator>\n </span>\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n </SelectPrimitive.Item>\n));\nSelectItem.displayName = \"SelectItem\";\n","import { CalendarDaysIcon } from \"@heroicons/react/24/outline\";\nimport { endOfDay, endOfMonth, format, parseISO, startOfDay, startOfMonth, subDays } from \"date-fns\";\nimport type { ReactElement } from \"react\";\nimport type { DateRange } from \"react-day-picker\";\nimport type { DateColumn } from \"../column/types\";\nimport { Button } from \"../components/ui/button\";\nimport { Calendar } from \"../components/ui/calendar\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"../components/ui/popover\";\nimport { cn } from \"../lib/utils\";\nimport type { FilterDescriptor } from \"./types\";\nimport type { FilterWidgetProps } from \"./widget-types\";\n\ninterface Preset {\n key: string;\n label: string;\n range: (today: Date) => DateRange;\n}\n\nconst PRESETS: readonly Preset[] = [\n {\n key: \"today\",\n label: \"Today\",\n range: (today) => ({ from: startOfDay(today), to: endOfDay(today) }),\n },\n {\n key: \"last7\",\n label: \"Last 7 days\",\n range: (today) => ({ from: startOfDay(subDays(today, 6)), to: endOfDay(today) }),\n },\n {\n key: \"thisMonth\",\n label: \"This month\",\n range: (today) => ({ from: startOfMonth(today), to: endOfMonth(today) }),\n },\n];\n\nfunction isStringPair(value: unknown): value is [string, string] {\n return (\n Array.isArray(value) &&\n value.length === 2 &&\n typeof value[0] === \"string\" &&\n typeof value[1] === \"string\"\n );\n}\n\n/**\n * Parses a bound produced by `isoOf`, via date-fns's `parseISO` rather than\n * the bare `new Date(...)` constructor. A bare `\"YYYY-MM-DD\"` string is\n * parsed by the JS spec as UTC midnight, not local midnight — but `isoOf`\n * serialized it via date-fns `format()` in *local* time, so re-parsing it\n * with `new Date(...)` would shift the displayed day backwards in any\n * timezone behind UTC. `parseISO` treats a date-only string as local\n * midnight, matching how it was serialized (the same fix `evaluate.ts`'s\n * `toEpochMs` applies for filter *evaluation*, sharing this date-fns\n * function instead of a second hand-rolled copy of the same logic).\n */\nfunction parseBound(value: string): Date {\n return parseISO(value);\n}\n\nfunction rangeOf(value: FilterDescriptor | undefined): DateRange | undefined {\n if (!value || value.operator !== \"between\" || !isStringPair(value.value)) return undefined;\n const from = parseBound(value.value[0]);\n const to = parseBound(value.value[1]);\n if (Number.isNaN(from.getTime()) || Number.isNaN(to.getTime())) return undefined;\n return { from, to };\n}\n\n/** Serializes a Date as a date-only (`type: \"date\"`) or full ISO (`type: \"datetime\"`) string. */\nfunction isoOf(date: Date, columnType: \"date\" | \"datetime\"): string {\n return columnType === \"datetime\" ? date.toISOString() : format(date, \"yyyy-MM-dd\");\n}\n\n/**\n * Default filter widget for `type: \"date\"` / `type: \"datetime\"` columns: a\n * date-range picker with Today / Last 7 days / This month presets, plus a\n * calendar for a custom range. Always emits the `between` operator\n * (inclusive both ends, matching evaluateFilter/sql.py/meili.py semantics).\n *\n * `today` defaults to the real current date; it's exposed as a prop purely\n * so tests can pin preset computation to a fixed date without mocking the\n * system clock (and the timer machinery that comes with that).\n *\n * `bare` (default `false`) skips this component's own Popover/trigger\n * Button, rendering just the presets + calendar directly — see\n * `EnumFilter`'s identical `bare` doc for why (`renderDefaultFilterWidget`\n * passes this automatically outside `filterDisplay: \"row\"`).\n */\nexport function DateRangeFilter<TRow>({\n column,\n value,\n onChange,\n today = new Date(),\n bare = false,\n}: FilterWidgetProps<DateColumn<TRow>> & { today?: Date; bare?: boolean }): ReactElement {\n const range = rangeOf(value);\n\n function emit(next: DateRange | undefined): void {\n if (!next?.from || !next.to) {\n onChange(undefined);\n return;\n }\n // The presets above already end their range at endOfDay(today); a\n // custom range picked directly from the Calendar does not — clicking a\n // day just returns that day's local midnight, with no time component.\n // For a \"date\" column that's fine (isoOf drops the time entirely), but\n // for \"datetime\" it would serialize the end bound as midnight of the\n // selected day, excluding nearly the entire day from the `between`\n // range. Applying endOfDay here (idempotent for the presets, which are\n // already at end-of-day) makes \"through this day\" the one consistent\n // meaning of the upper bound, regardless of how the range was chosen.\n const to = column.type === \"datetime\" ? endOfDay(next.to) : next.to;\n onChange({\n field: column.id,\n operator: \"between\",\n value: [isoOf(next.from, column.type), isoOf(to, column.type)],\n });\n }\n\n const isFiltered = range?.from !== undefined;\n const summary = range?.from\n ? range.to\n ? `${format(range.from, \"PP\")} - ${format(range.to, \"PP\")}`\n : format(range.from, \"PP\")\n : \"\";\n\n const panel = (\n <div className=\"space-y-2\">\n <div className=\"flex flex-wrap gap-2\">\n {PRESETS.map((preset) => (\n <Button\n key={preset.key}\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => emit(preset.range(today))}\n >\n {preset.label}\n </Button>\n ))}\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => emit(undefined)}>\n Custom\n </Button>\n </div>\n <Calendar mode=\"range\" selected={range} onSelect={emit} numberOfMonths={1} />\n </div>\n );\n\n if (bare) {\n return <div data-testid={`filter-${column.id}`}>{panel}</div>;\n }\n\n return (\n <Popover>\n <PopoverTrigger asChild>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className={cn(\"gap-1\", isFiltered ? \"max-w-[220px] px-2\" : \"w-8 justify-center px-0\")}\n aria-label={`Filter ${column.header}`}\n data-testid={`filter-${column.id}`}\n >\n <CalendarDaysIcon className=\"h-3.5 w-3.5 shrink-0 opacity-70\" aria-hidden />\n {isFiltered && <span className=\"truncate\">{summary}</span>}\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-auto\">{panel}</PopoverContent>\n </Popover>\n );\n}\n","import type { ComponentProps } from \"react\";\nimport { DayPicker } from \"react-day-picker\";\nimport { cn } from \"../../lib/utils\";\n\n/**\n * Thin wrapper around react-day-picker's <DayPicker>, styled minimally via\n * Tailwind utility classes on the root element. Consumers must additionally\n * import \"react-day-picker/style.css\" (or supply their own equivalent day/\n * cell styling) — this package intentionally does not bundle that CSS so it\n * doesn't leak into consumers that style calendars differently.\n */\nexport type CalendarProps = ComponentProps<typeof DayPicker>;\n\nexport function Calendar({ className, ...props }: CalendarProps) {\n return <DayPicker className={cn(\"p-3\", className)} {...props} />;\n}\n","import { FunnelIcon } from \"@heroicons/react/24/outline\";\nimport type { ReactElement } from \"react\";\nimport { useMemo, useState } from \"react\";\nimport type { EnumColumn } from \"../column/types\";\nimport { Button } from \"../components/ui/button\";\nimport { Checkbox } from \"../components/ui/checkbox\";\nimport { Input } from \"../components/ui/input\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"../components/ui/popover\";\nimport { cn } from \"../lib/utils\";\nimport type { FilterDescriptor } from \"./types\";\nimport type { FilterWidgetProps } from \"./widget-types\";\n\nfunction selectedValuesOf(value: FilterDescriptor | undefined): string[] {\n if (!value || value.operator !== \"in\" || !Array.isArray(value.value)) return [];\n return value.value.filter((entry): entry is string => typeof entry === \"string\");\n}\n\nfunction toggle(selected: string[], optionValue: string): string[] {\n return selected.includes(optionValue)\n ? selected.filter((v) => v !== optionValue)\n : [...selected, optionValue];\n}\n\n/**\n * Default filter widget for `type: \"enum\"` columns: a single Excel-style\n * checkbox list, regardless of how many `column.options` there are (no\n * short-list/long-list branch) —\n *\n * - a search input filters which options *render* below via a plain\n * substring match against each option's label; it never touches the\n * committed filter on its own.\n * - a tri-state \"Select all\" checkbox acts on whatever subset is currently\n * visible after that search, not the full option set — matching Excel's\n * own column-filter behavior (\"select all of what's shown\").\n * - every visible option gets its own plain checkbox + label row (a real\n * shadcn `Checkbox`, not a menu item), inside a scrollable list so long\n * option lists don't blow up the popover's height.\n * - every toggle emits the updated `FilterDescriptor` immediately — there's\n * no OK/Cancel step, matching every other filter widget in this package.\n *\n * `bare` (default `false`) skips this component's own Popover/trigger\n * Button, rendering just the search input + option list directly — for a\n * caller that already provides its own popover/collapsible (this is what\n * `renderDefaultFilterWidget` passes automatically outside `filterDisplay:\n * \"row\"`, so `<DataGrid>`'s own header-icon popover isn't nested inside a\n * second one of this component's own).\n */\nexport function EnumFilter<TRow>({\n column,\n value,\n onChange,\n bare = false,\n}: FilterWidgetProps<EnumColumn<TRow>> & { bare?: boolean }): ReactElement {\n const [search, setSearch] = useState(\"\");\n const selected = selectedValuesOf(value);\n\n const visibleOptions = useMemo(() => {\n const term = search.trim().toLowerCase();\n if (!term) return column.options;\n return column.options.filter((option) => option.label.toLowerCase().includes(term));\n }, [column.options, search]);\n\n function emit(next: string[]): void {\n onChange(next.length === 0 ? undefined : { field: column.id, operator: \"in\", value: next });\n }\n\n const visibleValues = visibleOptions.map((option) => option.value);\n const visibleSelectedCount = visibleValues.filter((v) => selected.includes(v)).length;\n const allVisibleSelected = visibleValues.length > 0 && visibleSelectedCount === visibleValues.length;\n const someVisibleSelected = visibleSelectedCount > 0 && !allVisibleSelected;\n\n /** Selects/deselects every currently-*visible* option, leaving any selection outside the search term untouched. */\n function toggleSelectAllVisible(): void {\n if (allVisibleSelected) {\n emit(selected.filter((v) => !visibleValues.includes(v)));\n return;\n }\n const next = new Set(selected);\n for (const v of visibleValues) next.add(v);\n emit([...next]);\n }\n\n const isFiltered = selected.length > 0;\n const summary = `${selected.length} selected`;\n\n const panel = (\n <div className={bare ? \"flex w-full flex-col gap-2\" : \"flex w-64 flex-col gap-2 p-2\"}>\n <Input\n placeholder={`Search ${column.header.toLowerCase()}...`}\n value={search}\n onChange={(event) => setSearch(event.target.value)}\n />\n <label className=\"flex items-center gap-2 border-b px-1 pb-2 text-sm\">\n <Checkbox\n checked={someVisibleSelected ? \"indeterminate\" : allVisibleSelected}\n disabled={visibleValues.length === 0}\n onCheckedChange={toggleSelectAllVisible}\n aria-label=\"Select all\"\n />\n Select all\n </label>\n <div className=\"flex max-h-60 flex-col gap-1 overflow-y-auto\">\n {visibleOptions.length === 0 ? (\n <p className=\"py-2 text-center text-sm text-muted-foreground\">No matches.</p>\n ) : (\n visibleOptions.map((option) => (\n <label\n key={option.value}\n className=\"flex items-center gap-2 rounded-sm px-1 py-1 text-sm hover:bg-accent hover:text-accent-foreground\"\n >\n <Checkbox\n checked={selected.includes(option.value)}\n onCheckedChange={() => emit(toggle(selected, option.value))}\n />\n {option.label}\n </label>\n ))\n )}\n </div>\n </div>\n );\n\n if (bare) {\n return <div data-testid={`filter-${column.id}`}>{panel}</div>;\n }\n\n return (\n <Popover>\n <PopoverTrigger asChild>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className={cn(\"gap-1\", isFiltered ? \"max-w-[180px] px-2\" : \"w-8 justify-center px-0\")}\n aria-label={`Filter ${column.header}`}\n data-testid={`filter-${column.id}`}\n >\n <FunnelIcon\n className={cn(\"h-3.5 w-3.5 shrink-0\", isFiltered ? \"opacity-100 text-primary\" : \"opacity-40\")}\n aria-hidden\n />\n {isFiltered && <span className=\"truncate\">{summary}</span>}\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-64 p-2\">{panel}</PopoverContent>\n </Popover>\n );\n}\n","import * as CheckboxPrimitive from \"@radix-ui/react-checkbox\";\nimport { Check, Minus } from \"lucide-react\";\nimport type { ComponentPropsWithoutRef, ElementRef } from \"react\";\nimport { forwardRef } from \"react\";\nimport { cn } from \"../../lib/utils\";\n\n/**\n * Plain tri-state checkbox (checked / unchecked / `indeterminate`), backed\n * by Radix's own Checkbox primitive rather than a native `<input>` — unlike\n * the native element, Radix exposes `indeterminate` as a first-class\n * `checked` value (`\"indeterminate\"`) instead of an imperative DOM property\n * a `ref` effect has to poke, and it renders with `role=\"checkbox\"` +\n * `aria-checked=\"mixed\"` for free.\n */\nexport const Checkbox = forwardRef<\n ElementRef<typeof CheckboxPrimitive.Root>,\n ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>\n>(({ className, ...props }, ref) => (\n <CheckboxPrimitive.Root\n ref={ref}\n className={cn(\n // A fixed 3px radius, not the theme's scaled `rounded-sm` — that\n // token is meant for buttons/cards sized well above 16px, and on a\n // box this small it reads as a circle (radio button) instead of a\n // checkbox once an app's `--radius` grows past shadcn's default.\n \"peer h-4 w-4 shrink-0 rounded-[3px] border border-primary shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[state=indeterminate]:bg-primary data-[state=indeterminate]:text-primary-foreground\",\n className,\n )}\n {...props}\n >\n <CheckboxPrimitive.Indicator className=\"flex items-center justify-center text-current\">\n {props.checked === \"indeterminate\" ? (\n <Minus className=\"h-3 w-3\" />\n ) : (\n <Check className=\"h-3 w-3\" />\n )}\n </CheckboxPrimitive.Indicator>\n </CheckboxPrimitive.Root>\n));\nCheckbox.displayName = \"Checkbox\";\n","import type { InputHTMLAttributes } from \"react\";\nimport { forwardRef } from \"react\";\nimport { cn } from \"../../lib/utils\";\n\nexport type InputProps = InputHTMLAttributes<HTMLInputElement>;\n\nexport const Input = forwardRef<HTMLInputElement, InputProps>(({ className, type, ...props }, ref) => {\n return (\n <input\n type={type}\n className={cn(\n \"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50\",\n className,\n )}\n ref={ref}\n {...props}\n />\n );\n});\nInput.displayName = \"Input\";\n","import { FunnelIcon } from \"@heroicons/react/24/outline\";\nimport type { ReactElement } from \"react\";\nimport { useEffect, useId, useRef, useState } from \"react\";\nimport { defaultFormat } from \"../column/format\";\nimport type { NumberColumn } from \"../column/types\";\nimport { Button } from \"../components/ui/button\";\nimport { Input } from \"../components/ui/input\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"../components/ui/popover\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"../components/ui/select\";\nimport { cn } from \"../lib/utils\";\nimport { isNumberPair } from \"./numberRangeShared\";\nimport type { FilterDescriptor, FilterOperator } from \"./types\";\nimport type { FilterWidgetProps } from \"./widget-types\";\n\ntype NumberOperator = \"eq\" | \"neq\" | \"gt\" | \"gte\" | \"lt\" | \"lte\" | \"between\";\n\ninterface NumberOperatorOption {\n value: NumberOperator;\n label: string;\n}\n\nconst NUMBER_OPERATORS: readonly NumberOperatorOption[] = [\n { value: \"gt\", label: \"Greater than\" },\n { value: \"gte\", label: \"Greater than or equal to\" },\n { value: \"lt\", label: \"Less than\" },\n { value: \"lte\", label: \"Less than or equal to\" },\n { value: \"eq\", label: \"Equals\" },\n { value: \"neq\", label: \"Does not equal\" },\n { value: \"between\", label: \"Between\" },\n];\n\nconst OPERATOR_SYMBOL: Record<Exclude<NumberOperator, \"between\">, string> = {\n gt: \">\",\n gte: \"≥\",\n lt: \"<\",\n lte: \"≤\",\n eq: \"=\",\n neq: \"≠\",\n};\n\nconst DEFAULT_OPERATOR: NumberOperator = \"gt\";\n\nconst NUMBER_OPERATOR_VALUES: ReadonlySet<string> = new Set(NUMBER_OPERATORS.map((option) => option.value));\n\nfunction isNumberOperator(operator: FilterOperator | undefined): operator is NumberOperator {\n return operator !== undefined && NUMBER_OPERATOR_VALUES.has(operator);\n}\n\nfunction parseNumber(raw: string): number | undefined {\n if (raw.trim() === \"\") return undefined;\n const parsed = Number(raw);\n return Number.isNaN(parsed) ? undefined : parsed;\n}\n\ninterface ComparisonState {\n operator: NumberOperator;\n text: string;\n text2: string;\n}\n\n/** Derives the widget's own local state from the committed `FilterDescriptor` — see `emit`'s doc for why this isn't just read inline on every render. */\nfunction stateFromValue(value: FilterDescriptor | undefined): ComparisonState {\n if (!value) return { operator: DEFAULT_OPERATOR, text: \"\", text2: \"\" };\n if (value.operator === \"between\" && isNumberPair(value.value)) {\n return { operator: \"between\", text: String(value.value[0]), text2: String(value.value[1]) };\n }\n if (isNumberOperator(value.operator) && typeof value.value === \"number\") {\n return { operator: value.operator, text: value.value.toString(), text2: \"\" };\n }\n return { operator: DEFAULT_OPERATOR, text: \"\", text2: \"\" };\n}\n\n/**\n * An Excel-style alternative to the default `NumberRangeFilter`: an operator\n * dropdown (Greater than / Less than / Equals / ... / Between) plus one\n * number input (two for `Between`), instead of a plain always-visible\n * min/max range. Not wired up as any column type's automatic default —\n * `NumberRangeFilter` still is, so existing columns keep their current\n * behavior. Use this directly as a column's custom filter UI (see\n * `BaseColumn.renderFilter`) for a numeric column where users think in\n * terms of \"greater than X\" rather than \"between X and Y\".\n *\n * Local `operator`/text state (rather than deriving straight from `value`\n * on every render, the way `StringFilter` does) is what lets `Between` hold\n * a partially-typed pair — deriving directly would wipe whatever's in the\n * OTHER box the instant the first box's own edit clears `value` back to\n * `undefined` (no filter is emitted until both bounds parse), and would\n * reset the operator dropdown back to the default the same way. The effect\n * below only resyncs from `value` when it changed for some reason other\n * than this widget's own last `onChange` call (an external clear-all, or\n * restoring a saved view) — the same pattern `NumberRangeFilter` uses for\n * the same reason.\n *\n * `bare` (default `true`) skips this component's own Popover/trigger\n * Button, rendering just the operator + input(s) directly — correct under\n * the default `filterDisplay: \"popover\"`, where `<DataGrid>` already\n * supplies the header filter icon and popover; passing `false` there would\n * nest a second, redundant one inside it. Pass `bare={false}` only when the\n * column also sets `filterDisplay: \"row\"`, where nothing else provides a\n * trigger — see `NumberHistogramFilter`'s identical `bare` doc for why.\n */\nexport function NumberComparisonFilter<TRow>({\n column,\n value,\n onChange,\n bare = true,\n}: FilterWidgetProps<NumberColumn<TRow>> & { bare?: boolean }): ReactElement {\n const initial = stateFromValue(value);\n const [operator, setOperator] = useState<NumberOperator>(initial.operator);\n const [text, setText] = useState(initial.text);\n const [text2, setText2] = useState(initial.text2);\n const operatorId = useId();\n const valueId = useId();\n\n const lastEmittedRef = useRef<FilterDescriptor | undefined>(value);\n\n useEffect(() => {\n if (value === lastEmittedRef.current) return;\n lastEmittedRef.current = value;\n const next = stateFromValue(value);\n setOperator(next.operator);\n setText(next.text);\n setText2(next.text2);\n }, [value]);\n\n function emit(nextOperator: NumberOperator, nextText: string, nextText2: string): void {\n let next: FilterDescriptor | undefined;\n if (nextOperator === \"between\") {\n const min = parseNumber(nextText);\n const max = parseNumber(nextText2);\n next = min !== undefined && max !== undefined ? { field: column.id, operator: \"between\", value: [min, max] } : undefined;\n } else {\n const num = parseNumber(nextText);\n next = num !== undefined ? { field: column.id, operator: nextOperator, value: num } : undefined;\n }\n lastEmittedRef.current = next;\n onChange(next);\n }\n\n const isBetween = operator === \"between\";\n const isFiltered = value !== undefined;\n const summary = (() => {\n if (!value) return \"\";\n if (value.operator === \"between\" && isNumberPair(value.value)) {\n const [lo, hi] = value.value;\n return `${defaultFormat(column, lo)} – ${defaultFormat(column, hi)}`;\n }\n if (isNumberOperator(value.operator) && value.operator !== \"between\" && typeof value.value === \"number\") {\n return `${OPERATOR_SYMBOL[value.operator]} ${defaultFormat(column, value.value)}`;\n }\n return \"\";\n })();\n\n const panel = (\n <div className=\"flex w-full max-w-[220px] flex-col gap-2\">\n <Select\n value={operator}\n onValueChange={(next) => {\n const nextOperator = NUMBER_OPERATORS.find((option) => option.value === next)?.value;\n if (!nextOperator) return;\n setOperator(nextOperator);\n emit(nextOperator, text, text2);\n }}\n >\n <SelectTrigger id={operatorId} aria-label={`${column.header} filter operator`}>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {NUMBER_OPERATORS.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <div className=\"flex items-center gap-2\">\n <Input\n id={valueId}\n type=\"number\"\n className=\"min-w-0\"\n aria-label={isBetween ? `${column.header} minimum` : column.header}\n placeholder={isBetween ? \"Min\" : \"Value\"}\n value={text}\n onChange={(event) => {\n setText(event.target.value);\n emit(operator, event.target.value, text2);\n }}\n />\n {isBetween && (\n <>\n <span className=\"shrink-0 text-xs text-muted-foreground\">to</span>\n <Input\n type=\"number\"\n className=\"min-w-0\"\n aria-label={`${column.header} maximum`}\n placeholder=\"Max\"\n value={text2}\n onChange={(event) => {\n setText2(event.target.value);\n emit(operator, text, event.target.value);\n }}\n />\n </>\n )}\n </div>\n </div>\n );\n\n if (bare) {\n return <div data-testid={`filter-${column.id}`}>{panel}</div>;\n }\n\n return (\n <Popover>\n <PopoverTrigger asChild>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className={cn(\"gap-1\", isFiltered ? \"max-w-[180px] px-2\" : \"w-8 justify-center px-0\")}\n aria-label={`Filter ${column.header}`}\n data-testid={`filter-${column.id}`}\n >\n <FunnelIcon\n className={cn(\"h-3.5 w-3.5 shrink-0\", isFiltered ? \"opacity-100 text-primary\" : \"opacity-40\")}\n aria-hidden\n />\n {isFiltered && <span className=\"truncate\">{summary}</span>}\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-auto p-3\">{panel}</PopoverContent>\n </Popover>\n );\n}\n","import { parseISO } from \"date-fns\";\nimport type { ColumnAlign, ColumnDef } from \"./types\";\n\n/** The default horizontal alignment for a column, based purely on its `type`. */\nexport function defaultAlign<TRow>(column: ColumnDef<TRow>): ColumnAlign {\n switch (column.type) {\n case \"number\":\n case \"currency\":\n return \"right\";\n case \"boolean\":\n return \"center\";\n case \"string\":\n case \"enum\":\n case \"date\":\n case \"datetime\":\n return \"left\";\n }\n}\n\n/**\n * Maps a `ColumnAlign` to its literal Tailwind utility class. Written as a\n * static lookup table rather than a template literal (`text-${align}`)\n * because Tailwind's JIT content scanner only picks up class names that\n * appear as literal strings in source — `text-right` in particular never\n * shows up as a literal anywhere else in this package, so it would be\n * silently dropped from a consuming app's build.\n */\nconst ALIGN_CLASS_NAME: Record<ColumnAlign, string> = {\n left: \"text-left\",\n center: \"text-center\",\n right: \"text-right\",\n};\n\n/** The literal Tailwind class for a column's default alignment; see `defaultAlign`. */\nexport function alignClassName<TRow>(column: ColumnDef<TRow>): string {\n return ALIGN_CLASS_NAME[defaultAlign(column)];\n}\n\nfunction toDate(value: unknown): Date | null {\n if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value;\n if (typeof value === \"number\") {\n const date = new Date(value);\n return Number.isNaN(date.getTime()) ? null : date;\n }\n if (typeof value === \"string\") {\n // parseISO, not `new Date(value)`: a bare \"YYYY-MM-DD\" string (exactly\n // what a \"date\" column's value looks like coming out of e.g. SQLite —\n // see the demo's created_at) is parsed by the JS spec as UTC midnight,\n // not local midnight, which shifts the *displayed* day back one in any\n // timezone behind UTC. This is the same bug already fixed for filter\n // evaluation (evaluate.ts's toEpochMs) and the filter widget's own\n // display (DateRangeFilter's parseBound) — missed here initially\n // because this is a cell-rendering path, not a filtering one, but the\n // underlying string is the same shape and needs the same fix.\n const date = parseISO(value);\n return Number.isNaN(date.getTime()) ? null : date;\n }\n return null;\n}\n\nfunction toNumber(value: unknown): number | null {\n if (typeof value === \"number\") return Number.isNaN(value) ? null : value;\n if (typeof value === \"string\" && value.trim() !== \"\") {\n const parsed = Number(value);\n return Number.isNaN(parsed) ? null : parsed;\n }\n return null;\n}\n\n// `new Intl.NumberFormat(...)` does locale/ICU data lookup on construction\n// and is expensive enough that building one per cell, per row, per render\n// (this used to happen inline in the `\"currency\"` case below) shows up as\n// real GC/CPU pressure once a grid has more than a handful of currency\n// cells on screen. Formatters are immutable and keyed only by currency code\n// here (locale is always `undefined`, i.e. the runtime default), so caching\n// by currency code is exact — never stale.\nconst currencyFormatters = new Map<string, Intl.NumberFormat>();\nfunction currencyFormatter(currency: string): Intl.NumberFormat {\n let formatter = currencyFormatters.get(currency);\n if (!formatter) {\n formatter = new Intl.NumberFormat(undefined, { style: \"currency\", currency });\n currencyFormatters.set(currency, formatter);\n }\n return formatter;\n}\n\n/**\n * Renders a raw cell value as a display string, using the type-appropriate\n * default formatter for `column.type`. Columns with a custom `cell` render\n * function bypass this entirely — it only backs the built-in renderer.\n */\nexport function defaultFormat<TRow>(column: ColumnDef<TRow>, value: unknown): string {\n if (value === null || value === undefined) return \"\";\n\n switch (column.type) {\n case \"string\":\n return String(value);\n\n case \"number\": {\n const num = toNumber(value);\n return num === null ? String(value) : num.toLocaleString();\n }\n\n case \"currency\": {\n const num = toNumber(value);\n if (num === null) return String(value);\n return currencyFormatter(column.currency ?? \"USD\").format(num);\n }\n\n case \"date\": {\n const date = toDate(value);\n return date === null ? String(value) : date.toLocaleDateString();\n }\n\n case \"datetime\": {\n const date = toDate(value);\n return date === null ? String(value) : date.toLocaleString();\n }\n\n case \"boolean\": {\n // A truthy check here would render \"Yes\" for the *string* \"false\" (a\n // real shape for boolean data round-tripped through JSON/CSV/query\n // params) since any non-empty string is truthy — compare explicitly\n // instead of coercing.\n if (typeof value === \"boolean\") return value ? \"Yes\" : \"No\";\n if (typeof value === \"string\") {\n const normalized = value.trim().toLowerCase();\n if (normalized === \"true\") return \"Yes\";\n if (normalized === \"false\") return \"No\";\n }\n if (typeof value === \"number\") return value !== 0 ? \"Yes\" : \"No\";\n return String(value);\n }\n\n case \"enum\": {\n const match = column.options.find((option) => option.value === String(value));\n return match ? match.label : String(value);\n }\n }\n}\n","import type { FilterDescriptor } from \"./types\";\n\nexport interface NumberRange {\n min: number | undefined;\n max: number | undefined;\n}\n\nexport function isNumberPair(value: unknown): value is [number, number] {\n return (\n Array.isArray(value) &&\n value.length === 2 &&\n typeof value[0] === \"number\" &&\n typeof value[1] === \"number\"\n );\n}\n\n/** Reads the current `{min, max}` out of a column's FilterDescriptor, for whichever of `between`/`gte`/`lte` it currently holds. */\nexport function rangeOf(value: FilterDescriptor | undefined): NumberRange {\n if (!value) return { min: undefined, max: undefined };\n if (value.operator === \"between\" && isNumberPair(value.value)) {\n const [min, max] = value.value;\n return { min, max };\n }\n if (value.operator === \"gte\" && typeof value.value === \"number\") {\n return { min: value.value, max: undefined };\n }\n if (value.operator === \"lte\" && typeof value.value === \"number\") {\n return { min: undefined, max: value.value };\n }\n return { min: undefined, max: undefined };\n}\n\n/**\n * Builds the FilterDescriptor for a `{min, max}` pair — `between` when both\n * bounds are set (reordered so the lower bound is always <= the upper\n * bound), `gte`/`lte` when only one is, `undefined` when neither is.\n *\n * Shared by every numeric-range widget (`NumberRangeFilter`,\n * `NumberHistogramFilter`) rather than each re-deriving it, since the\n * bound-reordering guard is easy to get subtly wrong (an inverted pair\n * would otherwise produce a `between` that evaluateFilter/sql.py/meili.py\n * all correctly, but silently, treat as matching nothing).\n */\nexport function descriptorFor(field: string, range: NumberRange): FilterDescriptor | undefined {\n const { min, max } = range;\n if (min !== undefined && max !== undefined) {\n const [lower, upper] = min <= max ? [min, max] : [max, min];\n return { field, operator: \"between\", value: [lower, upper] };\n }\n if (min !== undefined) return { field, operator: \"gte\", value: min };\n if (max !== undefined) return { field, operator: \"lte\", value: max };\n return undefined;\n}\n","import { FunnelIcon } from \"@heroicons/react/24/outline\";\nimport type { ReactElement } from \"react\";\nimport { useEffect, useId, useRef, useState } from \"react\";\nimport { defaultFormat } from \"../column/format\";\nimport type { NumberColumn } from \"../column/types\";\nimport { Button } from \"../components/ui/button\";\nimport { Input } from \"../components/ui/input\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"../components/ui/popover\";\nimport { cn } from \"../lib/utils\";\nimport { descriptorFor, rangeOf } from \"./numberRangeShared\";\nimport type { FilterDescriptor } from \"./types\";\nimport type { FilterWidgetProps } from \"./widget-types\";\n\nfunction parseNumber(raw: string): number | undefined {\n if (raw.trim() === \"\") return undefined;\n const parsed = Number(raw);\n return Number.isNaN(parsed) ? undefined : parsed;\n}\n\n/**\n * Default filter widget for `type: \"number\"` / `type: \"currency\"` columns: a\n * min/max range. Emits `between` when both bounds are set (reordered so the\n * lower bound is always <= the upper bound), or `gte`/`lte` when only one\n * is — all three are evaluated identically by evaluateFilter and sql.py/\n * meili.py.\n *\n * The two input boxes track their own local text rather than deriving\n * straight from `value` on every render. If they derived from `value`\n * directly, reordering an inverted min/max (see above) would relocate\n * whatever the user just typed into the *other* box on the very next\n * render — so typing \"99\" into Min then \"9\" then \"9\" into Max (intending\n * \"99\") would see Max flip to \"99\" after the first \"9\" (since a swap makes\n * it the new max), and the second \"9\" would land in the now-relocated box,\n * producing \"909\" instead of \"99\". Keeping local text means what's on\n * screen always matches exactly what was typed, independent of how the\n * emitted FilterDescriptor's bounds get reordered.\n *\n * `bare` (default `false`) skips this component's own Popover/trigger\n * Button, rendering just the two inputs directly — see `EnumFilter`'s\n * identical `bare` doc for why (`renderDefaultFilterWidget` passes this\n * automatically outside `filterDisplay: \"row\"`). Under `filterDisplay:\n * \"row\"`, the min/max boxes are always visible at their natural width\n * regardless of a narrower column — wrapping them in a compact trigger\n * (bare: false) is what keeps a plain numeric column from forcing its whole\n * column wider than its data ever needs.\n */\nexport function NumberRangeFilter<TRow>({\n column,\n value,\n onChange,\n bare = false,\n}: FilterWidgetProps<NumberColumn<TRow>> & { bare?: boolean }): ReactElement {\n const initial = rangeOf(value);\n const [minText, setMinText] = useState(initial.min?.toString() ?? \"\");\n const [maxText, setMaxText] = useState(initial.max?.toString() ?? \"\");\n const minId = useId();\n const maxId = useId();\n\n // Tracks the exact descriptor object this widget itself last passed to\n // onChange, so the effect below can tell \"value changed because we edited\n // it\" (skip — local text is already right, and re-deriving it would\n // reintroduce the swap-corruption bug documented above) apart from\n // \"value changed for some other reason\" — a caller-driven clear-all-\n // filters, or restoring a different filter entirely (e.g. loading a saved\n // view), both of which this widget would otherwise never notice since it\n // normally drives `value` itself.\n const lastEmittedRef = useRef<FilterDescriptor | undefined>(value);\n\n useEffect(() => {\n if (value === lastEmittedRef.current) return;\n lastEmittedRef.current = value;\n const next = rangeOf(value);\n setMinText(next.min?.toString() ?? \"\");\n setMaxText(next.max?.toString() ?? \"\");\n }, [value]);\n\n function emit(nextMinText: string, nextMaxText: string): void {\n const next = descriptorFor(column.id, {\n min: parseNumber(nextMinText),\n max: parseNumber(nextMaxText),\n });\n lastEmittedRef.current = next;\n onChange(next);\n }\n\n const { min, max } = rangeOf(value);\n const isFiltered = min !== undefined || max !== undefined;\n const summary =\n min !== undefined && max !== undefined\n ? `${defaultFormat(column, min)} – ${defaultFormat(column, max)}`\n : min !== undefined\n ? `≥ ${defaultFormat(column, min)}`\n : max !== undefined\n ? `≤ ${defaultFormat(column, max)}`\n : \"\";\n\n const panel = (\n <div className=\"flex w-full max-w-[220px] items-center gap-2\">\n <Input\n id={minId}\n type=\"number\"\n aria-label={`${column.header} minimum`}\n placeholder=\"Min\"\n className=\"min-w-0\"\n value={minText}\n onChange={(event) => {\n setMinText(event.target.value);\n emit(event.target.value, maxText);\n }}\n />\n <span className=\"shrink-0 text-xs text-muted-foreground\">to</span>\n <Input\n id={maxId}\n type=\"number\"\n aria-label={`${column.header} maximum`}\n placeholder=\"Max\"\n className=\"min-w-0\"\n value={maxText}\n onChange={(event) => {\n setMaxText(event.target.value);\n emit(minText, event.target.value);\n }}\n />\n </div>\n );\n\n if (bare) {\n return <div data-testid={`filter-${column.id}`}>{panel}</div>;\n }\n\n return (\n <Popover>\n <PopoverTrigger asChild>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className={cn(\"gap-1\", isFiltered ? \"max-w-[180px] px-2\" : \"w-8 justify-center px-0\")}\n aria-label={`Filter ${column.header}`}\n data-testid={`filter-${column.id}`}\n >\n <FunnelIcon\n className={cn(\"h-3.5 w-3.5 shrink-0\", isFiltered ? \"opacity-100 text-primary\" : \"opacity-40\")}\n aria-hidden\n />\n {isFiltered && <span className=\"truncate\">{summary}</span>}\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-auto p-3\">{panel}</PopoverContent>\n </Popover>\n );\n}\n","import { FunnelIcon } from \"@heroicons/react/24/outline\";\nimport type { MouseEvent, ReactElement } from \"react\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport type { NumberColumn } from \"../column/types\";\nimport { Button } from \"../components/ui/button\";\nimport { Input } from \"../components/ui/input\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"../components/ui/popover\";\nimport { cn } from \"../lib/utils\";\nimport { descriptorFor, rangeOf } from \"./numberRangeShared\";\nimport type { FilterWidgetProps } from \"./widget-types\";\n\nconst BAR_HEIGHT_PX = 56;\nconst DEFAULT_BUCKETS = 24;\n\n// Signed log transform — spreads skewed financial data (a handful of huge\n// values, a long tail of small ones) evenly across the slider/histogram\n// instead of every bar but one being invisible; handles negative values.\nfunction tLog(v: number): number {\n return Math.sign(v) * Math.log10(Math.abs(v) + 1);\n}\nfunction tInv(t: number): number {\n return Math.sign(t) * (10 ** Math.abs(t) - 1);\n}\n\ntype NumberHistogramValuesProps =\n | {\n /**\n * The full set of values to compute the histogram and slider domain\n * from.\n *\n * MUST be computed excluding this column's OWN active filter (every\n * other active filter is fine, even expected — see\n * `facetedNumberValues`, which computes exactly this). Passing values\n * already narrowed by this same filter reproduces the bug this\n * component's design exists to prevent: the domain shrinks every time\n * the user adjusts the range, so they can never widen it back out, and\n * (if the same \"all rows\" array is reused across multiple numeric\n * columns) filtering one column warps every other numeric column's\n * histogram too.\n *\n * Use this (not `loadValues`) whenever the full, facet-safe row set is\n * already in memory — e.g. `<DataGrid>` in `\"client\"` mode.\n */\n allValues: (number | null | undefined)[];\n loadValues?: never;\n }\n | {\n allValues?: never;\n /**\n * Fetches the histogram/slider domain values on demand, called every\n * time the popover opens (not cached across opens, since every OTHER\n * active filter — which the fetch should itself respect — may have\n * changed since the last time it was open).\n *\n * Use this instead of `allValues` whenever the facet-safe row set\n * ISN'T already in memory — the standard case for `<DataGrid>` in\n * `\"server\"` mode, where the rows the grid holds are just the current\n * page, already narrowed by every active filter (including this\n * column's own), and therefore never safe to pass as `allValues`\n * directly. Typically this closes over a caller's own \"fetch facet\n * values for this column, given every other active filter\" endpoint.\n */\n loadValues: () => Promise<(number | null | undefined)[]>;\n };\n\nexport type NumberHistogramFilterProps<TRow> = FilterWidgetProps<NumberColumn<TRow>> &\n NumberHistogramValuesProps & {\n /** Formats a bound for display. Defaults to `toLocaleString()`. */\n format?: (value: number) => string;\n /** Number of histogram bars. Defaults to 24. */\n buckets?: number;\n /**\n * Set this to `false` only when the column also sets `filterDisplay:\n * \"row\"` — there, nothing else provides a trigger, so this needs its\n * own Popover + Button to avoid permanently expanding the histogram\n * inline in the filter row. Defaults to `true` (bare: no own Popover/\n * Button, just the histogram/slider/inputs/clear content) because a\n * custom `BaseColumn.renderFilter` is normally reached for under the\n * *default* `filterDisplay: \"popover\"`, where `<DataGrid>` already\n * renders a header filter icon that opens a popover around whatever\n * `renderFilter` returns — passing `false` there nests a second,\n * redundant Popover+Button inside the first, so opening the header\n * icon shows nothing but another filter icon to click, not the\n * histogram. There's no `open`/`onOpenChange` state at this level in\n * bare mode — the component simply renders as always-open; the\n * caller's own popover is what mounts/unmounts it.\n */\n bare?: boolean;\n };\n\n/**\n * A richer alternative to the default `NumberRangeFilter`: a log-scale\n * histogram behind a dual-thumb range slider, plus min/max text inputs.\n * Not wired up as any column type's automatic default (unlike\n * `NumberRangeFilter`) — it needs `allValues`/`loadValues` (see above),\n * which the grid itself doesn't have a way to supply automatically yet, so\n * use it directly as a column's custom filter UI (see `BaseColumn.renderFilter`).\n * By default (`bare` unset) this renders bare — just the histogram/slider/\n * inputs, no own trigger/popover — correct under the default `filterDisplay:\n * \"popover\"`, where `<DataGrid>` already supplies the header filter icon and\n * popover. Pass `bare={false}` when the column also sets `filterDisplay:\n * \"row\"`, where nothing else provides a trigger. Compute `allValues` via\n * `facetedNumberValues(data, column, filter)` in client mode, or supply\n * `loadValues` to fetch the facet from the server in server mode.\n */\nexport function NumberHistogramFilter<TRow>({\n column,\n value,\n onChange,\n allValues,\n loadValues,\n format = (v) => v.toLocaleString(),\n buckets = DEFAULT_BUCKETS,\n bare = true,\n}: NumberHistogramFilterProps<TRow>): ReactElement {\n const [openState, setOpen] = useState(false);\n // In bare mode there's no trigger of our own to toggle this -- the caller's\n // own popover controls whether this component is even mounted, so treat it\n // as permanently \"open\" internally (drives the value-computation/resync\n // effects below the same way a real open transition would).\n const open = bare || openState;\n const trackRef = useRef<HTMLDivElement>(null);\n const [minOnTop, setMinOnTop] = useState(false);\n const [loadedValues, setLoadedValues] = useState<(number | null | undefined)[]>([]);\n const [loading, setLoading] = useState(false);\n\n const { min, max } = rangeOf(value);\n\n // `loadValues` re-fetches on every open (not just the first) -- every\n // OTHER active filter it should itself respect may have changed since the\n // popover was last open, and it's cheap to only pay for this while the\n // control is actually visible.\n useEffect(() => {\n if (!open || !loadValues) return;\n let cancelled = false;\n setLoading(true);\n loadValues()\n .then((values) => {\n if (!cancelled) setLoadedValues(values);\n })\n .finally(() => {\n if (!cancelled) setLoading(false);\n });\n return () => {\n cancelled = true;\n };\n }, [open, loadValues]);\n\n const sourceValues = allValues ?? loadedValues;\n\n // O(n) histogram/domain scan only while the popover is actually open —\n // it's not visible (and its values aren't needed for anything else; the\n // closed trigger's label reads `min`/`max` directly, not the domain)\n // while closed, and this can run against a full unfiltered column of\n // however many rows the grid holds.\n const nums = useMemo(\n () => (open ? sourceValues.filter((v): v is number => v != null) : []),\n [sourceValues, open],\n );\n const dataMin = useMemo(() => nums.reduce((a, b) => Math.min(a, b), Infinity), [nums]);\n const dataMax = useMemo(() => nums.reduce((a, b) => Math.max(a, b), -Infinity), [nums]);\n\n const activeMin = min ?? dataMin;\n const activeMax = max ?? dataMax;\n\n // Local text state for the min/max boxes, resynced fresh each time the\n // popover opens (see the effect below) rather than derived straight from\n // `activeMin`/`activeMax` on every render. Deriving directly would mean\n // every keystroke round-trips through emit -> onChange -> the clamped/\n // rounded committed number -> back into the input's `value` — so typing\n // \"150\" character by character re-renders the box to \"1\", then \"15\", but\n // interleaved with whatever userEvent.clear() or cursor-position quirks\n // occur along the way corrupts the result (verified: this exact direct-\n // binding shape, ported faithfully from the original ad hoc version this\n // component replaces, produced \"499\" instead of \"150\" in a plain typing\n // test). `NumberRangeFilter` avoids the same class of bug the same way,\n // for the same reason — see its own docstring.\n const [minText, setMinText] = useState(\"\");\n const [maxText, setMaxText] = useState(\"\");\n\n useEffect(() => {\n if (!open) return;\n setMinText(Number.isFinite(activeMin) ? String(Math.round(activeMin)) : \"\");\n setMaxText(Number.isFinite(activeMax) ? String(Math.round(activeMax)) : \"\");\n // Intentionally resyncs on the open transition and on `loading`\n // flipping false (so a `loadValues` fetch's result populates the boxes\n // once it actually resolves — at the moment `open` first becomes true,\n // an in-flight fetch's `sourceValues` is still `[]`), but not on every\n // activeMin/activeMax change while open otherwise — typing itself\n // drives those via handleMinChange/handleMaxChange below, and\n // re-deriving text from the committed (rounded, clamped) number on\n // every keystroke is exactly the corruption local state exists to avoid.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [open, loading]);\n\n const logDataMin = Number.isFinite(dataMin) ? tLog(dataMin) : 0;\n const logDataMax = Number.isFinite(dataMax) ? tLog(dataMax) : 1;\n const logStep = (logDataMax - logDataMin) / 200;\n\n const logPct = useCallback(\n (v: number) => (logDataMax === logDataMin ? 0 : ((tLog(v) - logDataMin) / (logDataMax - logDataMin)) * 100),\n [logDataMin, logDataMax],\n );\n\n const histBars = useMemo(() => {\n if (!nums.length || dataMin === dataMax || !Number.isFinite(dataMin) || !Number.isFinite(dataMax)) return [];\n const step = (logDataMax - logDataMin) / buckets;\n const counts = Array<number>(buckets).fill(0);\n for (const v of nums) {\n const i = Math.min(Math.floor((tLog(v) - logDataMin) / step), buckets - 1);\n counts[i] = (counts[i] ?? 0) + 1;\n }\n const peak = Math.max(...counts);\n return counts.map((count, i) => ({\n count,\n heightPx: peak > 0 ? Math.max((count / peak) * BAR_HEIGHT_PX, 2) : 2,\n active: tInv(logDataMin + i * step) < activeMax && tInv(logDataMin + (i + 1) * step) > activeMin,\n }));\n }, [nums, dataMin, dataMax, logDataMin, logDataMax, buckets, activeMin, activeMax]);\n\n function emit(nextMin: number | undefined, nextMax: number | undefined): void {\n onChange(\n descriptorFor(column.id, {\n min: nextMin !== undefined && nextMin <= dataMin ? undefined : nextMin,\n max: nextMax !== undefined && nextMax >= dataMax ? undefined : nextMax,\n }),\n );\n }\n\n function handleMinChange(actual: number): void {\n emit(Math.min(actual, activeMax - 1), max);\n }\n\n function handleMaxChange(actual: number): void {\n emit(min, Math.max(actual, activeMin + 1));\n }\n\n function handleTrackHover(event: MouseEvent<HTMLDivElement>): void {\n if (!trackRef.current || !Number.isFinite(dataMin) || !Number.isFinite(dataMax)) return;\n const rect = trackRef.current.getBoundingClientRect();\n const ratio = (event.clientX - rect.left) / rect.width;\n const logHover = logDataMin + ratio * (logDataMax - logDataMin);\n setMinOnTop(Math.abs(logHover - tLog(activeMin)) <= Math.abs(logHover - tLog(activeMax)));\n }\n\n const isFiltered = value !== undefined;\n const summary = isFiltered ? `${format(activeMin)} – ${format(activeMax)}` : \"\";\n\n const panel = (\n <div className={bare ? \"flex w-full flex-col gap-3\" : \"flex w-80 flex-col gap-3\"}>\n {loading && (\n <p className=\"text-xs text-muted-foreground\" data-testid={`filter-${column.id}-loading`}>\n Loading…\n </p>\n )}\n <div className=\"flex items-end gap-px\" style={{ height: BAR_HEIGHT_PX }} data-testid={`filter-${column.id}-histogram`}>\n {histBars.map((bar, i) => (\n <div\n key={i}\n className={cn(\"flex-1 rounded-sm\", bar.active ? \"bg-primary/70\" : \"bg-muted-foreground/20\")}\n style={{ height: bar.heightPx }}\n />\n ))}\n </div>\n\n <div ref={trackRef} className=\"relative h-5\" onMouseMove={handleTrackHover}>\n <input\n type=\"range\"\n aria-label={`${column.header} minimum (slider)`}\n min={logDataMin}\n max={logDataMax}\n step={logStep}\n value={tLog(activeMin)}\n onChange={(event) => handleMinChange(tInv(Number.parseFloat(event.target.value)))}\n className=\"absolute inset-0 h-full w-full cursor-pointer opacity-0\"\n style={{ zIndex: minOnTop ? 5 : 3 }}\n />\n <input\n type=\"range\"\n aria-label={`${column.header} maximum (slider)`}\n min={logDataMin}\n max={logDataMax}\n step={logStep}\n value={tLog(activeMax)}\n onChange={(event) => handleMaxChange(tInv(Number.parseFloat(event.target.value)))}\n className=\"absolute inset-0 h-full w-full cursor-pointer opacity-0\"\n style={{ zIndex: minOnTop ? 3 : 5 }}\n />\n <div className=\"pointer-events-none absolute top-1/2 h-1.5 w-full -translate-y-1/2 rounded-full bg-muted\">\n <div\n className=\"absolute h-full rounded-full bg-primary\"\n style={{ left: `${logPct(activeMin)}%`, right: `${100 - logPct(activeMax)}%` }}\n />\n </div>\n {[activeMin, activeMax].map((v, i) => (\n <div\n key={i}\n className=\"pointer-events-none absolute top-1/2 h-4 w-4 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-primary bg-background shadow\"\n style={{ left: `${logPct(v)}%` }}\n />\n ))}\n </div>\n\n <div className=\"flex items-center gap-2\">\n <Input\n type=\"text\"\n inputMode=\"numeric\"\n aria-label={`${column.header} minimum`}\n value={minText}\n onChange={(event) => {\n setMinText(event.target.value);\n const parsed = Number.parseFloat(event.target.value.replace(/[^0-9.-]/g, \"\"));\n if (!Number.isNaN(parsed)) handleMinChange(parsed);\n }}\n />\n <span className=\"shrink-0 text-xs text-muted-foreground\">to</span>\n <Input\n type=\"text\"\n inputMode=\"numeric\"\n aria-label={`${column.header} maximum`}\n value={maxText}\n onChange={(event) => {\n setMaxText(event.target.value);\n const parsed = Number.parseFloat(event.target.value.replace(/[^0-9.-]/g, \"\"));\n if (!Number.isNaN(parsed)) handleMaxChange(parsed);\n }}\n />\n </div>\n\n {isFiltered && (\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => onChange(undefined)}>\n Clear\n </Button>\n )}\n </div>\n );\n\n if (bare) {\n return <div data-testid={`filter-${column.id}`}>{panel}</div>;\n }\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className={cn(\"gap-1\", isFiltered ? \"max-w-[200px] px-2\" : \"w-8 justify-center px-0\")}\n aria-label={`Filter ${column.header}`}\n data-testid={`filter-${column.id}`}\n >\n <FunnelIcon\n className={cn(\"h-3.5 w-3.5 shrink-0\", isFiltered ? \"opacity-100 text-primary\" : \"opacity-40\")}\n aria-hidden\n />\n {isFiltered && <span className=\"truncate\">{summary}</span>}\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-80\">{panel}</PopoverContent>\n </Popover>\n );\n}\n","import type { ReactNode } from \"react\";\nimport type { CompositeFilterDescriptor, FilterDescriptor } from \"../filter/types\";\n\n/** Horizontal alignment used for a column's cell content and header. */\nexport type ColumnAlign = \"left\" | \"center\" | \"right\";\n\n/** Fields shared by every column type, independent of `type`. */\nexport interface BaseColumn<TRow> {\n id: string;\n accessorKey?: keyof TRow & string;\n accessorFn?: (row: TRow) => unknown;\n header: string;\n width?: number;\n /** Opt-in: defaults to false — must be explicitly set to true to show a sort toggle in the header. */\n sortable?: boolean;\n /**\n * Opt-in: defaults to false, meaning a first click sorts ascending. Set true for a\n * column whose \"interesting\" direction is descending — revenue, risk scores, backlog —\n * so a first click surfaces the highest/most-urgent values first instead of the\n * lowest/safest ones. Cycles desc -> asc -> none instead of the default asc -> desc -> none.\n */\n sortDescFirst?: boolean;\n /** Opt-in: defaults to false — must be explicitly set to true to show a filter icon/widget in the header. */\n filterable?: boolean;\n pinned?: \"left\" | \"right\";\n /** Overrides the default type-based renderer. */\n cell?: (value: unknown, row: TRow) => ReactNode;\n /** Free-text group label for <ColumnSelector>; columns sharing a group are shown together. Ungrouped when omitted. */\n group?: string;\n /**\n * Free-text label for a spanning header cell in `<DataGrid>`'s own thead —\n * unlike `group` above (which only affects `<ColumnSelector>`), this\n * changes the grid's rendered markup. Contiguous *visible* columns sharing\n * the same `headerGroup` string get merged under one `colSpan`'d label\n * cell in an extra row above the normal per-column header row; a column\n * with no `headerGroup` instead spans both header rows itself (via\n * `rowSpan`), so it reads as a single ungrouped column rather than leaving\n * a blank cell above it. Two columns with the same `headerGroup` that\n * AREN'T adjacent in the visible column order (e.g. a differently-grouped\n * or ungrouped column sits between them) form two separate spanning cells\n * with the same label, rather than one — grouping is purely positional,\n * matching how a caller would read the columns left-to-right.\n */\n headerGroup?: string;\n /**\n * Overrides the default type-based filter widget (see\n * `renderDefaultFilterWidget`) for this column. Receives the same\n * `(value, onChange)` pair the default widget would, plus the grid's full\n * current `GridState.filter` (every column's combined filter) as a third\n * argument — needed for a widget like `NumberHistogramFilter`, whose\n * `allValues` prop must be computed via `facetedNumberValues(data, column,\n * filter)` to respect every OTHER active filter while excluding this\n * column's own (see `facetedNumberValues`'s own doc for why). Set this to\n * close over whatever extra data (e.g. the full unfiltered `data`) the\n * custom widget needs.\n */\n renderFilter?: (\n value: FilterDescriptor | undefined,\n onChange: (next: FilterDescriptor | undefined) => void,\n filter: CompositeFilterDescriptor | null,\n ) => ReactNode;\n /**\n * Where this column's filter control renders. Defaults to `\"popover\"`: a\n * filter-icon button in the header that opens the widget in a popover,\n * same as every column gets automatically today. `\"row\"` renders the\n * widget's content directly, inline, in an additional filter row under\n * the header instead — needed for a wide control (e.g.\n * `NumberHistogramFilter`'s slider/histogram) that doesn't fit well\n * collapsed into the header's compact trigger button. The filter row\n * itself is entirely opt-in: it only renders at all if at least one\n * visible, filterable column sets `filterDisplay: \"row\"`.\n */\n filterDisplay?: \"popover\" | \"row\";\n /**\n * Overrides the plain-text `header` rendering *inside* the header cell's\n * sort toggle `<button>` (the sort caret still renders alongside it, and\n * still inside that same button). Use this for a header that needs a\n * tooltip, an info icon, a loading spinner tied to a separate\n * batch-hydration fetch, or any other rich content — `header` itself\n * stays a plain string (used for the filter icon's `aria-label` and by\n * `<ColumnSelector>`, neither of which can work off a ReactNode).\n *\n * Whatever this returns is nested inside a real `<button>` — don't return\n * another focusable/interactive element (a nested `<button>`, a link, a\n * tooltip trigger that itself renders as a `<button>`) or the browser will\n * split the outer button apart, breaking click-to-sort for that column.\n * If the return value is non-textual (an icon with no visible label), the\n * button's accessible name is whatever text content survives inside it —\n * add your own `aria-label` inside the returned content if there isn't\n * any, since `<DataGrid>` has no way to tell your content lacks one.\n */\n renderHeader?: (column: BaseColumn<TRow>) => ReactNode;\n}\n\nexport type StringColumn<TRow> = BaseColumn<TRow> & { type: \"string\" };\n\nexport type NumberColumn<TRow> = BaseColumn<TRow> & {\n type: \"number\" | \"currency\";\n currency?: string;\n};\n\nexport type DateColumn<TRow> = BaseColumn<TRow> & { type: \"date\" | \"datetime\" };\n\nexport type BooleanColumn<TRow> = BaseColumn<TRow> & { type: \"boolean\" };\n\nexport interface EnumOption {\n value: string;\n label: string;\n}\n\nexport type EnumColumn<TRow> = BaseColumn<TRow> & {\n type: \"enum\";\n options: EnumOption[];\n};\n\n/** Discriminated union of every supported column shape, keyed on `type`. */\nexport type ColumnDef<TRow> =\n | StringColumn<TRow>\n | NumberColumn<TRow>\n | DateColumn<TRow>\n | BooleanColumn<TRow>\n | EnumColumn<TRow>;\n\n/** Resolves a column's raw cell value from a row, via `accessorFn` or `accessorKey`. */\nexport function getColumnValue<TRow>(column: ColumnDef<TRow>, row: TRow): unknown {\n if (column.accessorFn) return column.accessorFn(row);\n if (column.accessorKey) return row[column.accessorKey];\n return undefined;\n}\n\n/** True only if a column explicitly opts in (`sortable: true`) — the default is false. */\nexport function isSortable<TRow>(column: ColumnDef<TRow>): boolean {\n return column.sortable === true;\n}\n\n/** True only if a column explicitly opts in (`filterable: true`) — the default is false. */\nexport function isFilterable<TRow>(column: ColumnDef<TRow>): boolean {\n return column.filterable === true;\n}\n","import { getColumnValue } from \"../column/types\";\nimport type { ColumnDef } from \"../column/types\";\nimport { evaluateFilter } from \"./evaluate\";\nimport { fieldKey, isCompositeFilterDescriptor } from \"./types\";\nimport type { CompositeFilterDescriptor, FilterDescriptor } from \"./types\";\n\ntype FilterNode = FilterDescriptor | CompositeFilterDescriptor;\n\n/** Removes every leaf filter on `columnId` from a filter tree; returns `null` if nothing is left. */\nfunction withoutColumn(filter: FilterNode | null | undefined, columnId: string): FilterNode | null {\n if (!filter) return null;\n if (!isCompositeFilterDescriptor(filter)) {\n return fieldKey(filter.field) === columnId ? null : filter;\n }\n const kept = filter.filters\n .map((child) => withoutColumn(child, columnId))\n .filter((child): child is FilterNode => child !== null);\n if (kept.length === 0) return null;\n return { logic: filter.logic, filters: kept };\n}\n\n/**\n * Computes the correct `allValues` input for `NumberHistogramFilter` bound\n * to `column`: `data` filtered by every OTHER currently-active filter, with\n * `column`'s own filter excluded, then that column's values extracted.\n *\n * This exists to prevent a specific, easy-to-reach bug: computing a filter\n * widget's own histogram/slider domain from a row set that's already been\n * narrowed BY that same filter means the domain shrinks every time the user\n * adjusts the range — they can never widen it back out — and if one \"all\n * values\" computation is naively reused across multiple numeric columns\n * (e.g. from `table.getFilteredRowModel()`), setting a filter on column A\n * also warps column B's histogram, since B's domain was computed from rows\n * A already excluded. This is the standard \"faceted search\" fix: a filter's\n * own facet should reflect \"what could I select if I changed just this\n * filter,\" never a result set that filter has already narrowed.\n *\n * `filter` is the grid's full `GridState.filter` (every column's combined\n * filter) — pass it as-is; this function does the excluding.\n */\nexport function facetedNumberValues<TRow>(\n data: readonly TRow[],\n column: ColumnDef<TRow>,\n filter: FilterNode | null | undefined,\n): (number | null)[] {\n const withoutSelf = withoutColumn(filter, column.id);\n const values: (number | null)[] = [];\n for (const row of data) {\n if (!evaluateFilter(row, withoutSelf)) continue;\n const raw = getColumnValue(column, row);\n if (typeof raw === \"number\") {\n values.push(Number.isFinite(raw) ? raw : null);\n } else if (raw == null) {\n values.push(null);\n } else {\n const parsed = Number(raw);\n values.push(Number.isFinite(parsed) ? parsed : null);\n }\n }\n return values;\n}\n","import { FunnelIcon } from \"@heroicons/react/24/outline\";\nimport type { ReactElement } from \"react\";\nimport { useId } from \"react\";\nimport type { StringColumn } from \"../column/types\";\nimport { Button } from \"../components/ui/button\";\nimport { Input } from \"../components/ui/input\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"../components/ui/popover\";\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"../components/ui/select\";\nimport { cn } from \"../lib/utils\";\nimport type { FilterOperator } from \"./types\";\nimport type { FilterWidgetProps } from \"./widget-types\";\n\ninterface StringOperatorOption {\n value: \"contains\" | \"eq\" | \"startsWith\" | \"endsWith\";\n label: string;\n}\n\nconst STRING_OPERATORS: readonly StringOperatorOption[] = [\n { value: \"contains\", label: \"Contains\" },\n { value: \"eq\", label: \"Is\" },\n { value: \"startsWith\", label: \"Starts with\" },\n { value: \"endsWith\", label: \"Ends with\" },\n];\n\nconst DEFAULT_OPERATOR: FilterOperator = \"contains\";\n\n/**\n * Default filter widget for `type: \"string\"` columns: a text input plus an\n * operator dropdown.\n *\n * `bare` (default `false`) skips this component's own Popover/trigger\n * Button, rendering just the operator + input directly — see `EnumFilter`'s\n * identical `bare` doc for why (`renderDefaultFilterWidget` passes this\n * automatically outside `filterDisplay: \"row\"`).\n */\nexport function StringFilter<TRow>({\n column,\n value,\n onChange,\n bare = false,\n}: FilterWidgetProps<StringColumn<TRow>> & { bare?: boolean }): ReactElement {\n const operator = value?.operator ?? DEFAULT_OPERATOR;\n const text = typeof value?.value === \"string\" ? value.value : \"\";\n const inputId = useId();\n const isFiltered = text !== \"\";\n\n function emit(nextOperator: FilterOperator, nextText: string): void {\n if (nextText === \"\") {\n onChange(undefined);\n return;\n }\n onChange({ field: column.id, operator: nextOperator, value: nextText });\n }\n\n const panel = (\n <div className=\"flex w-full max-w-[220px] flex-col gap-2\">\n <Select\n value={operator}\n onValueChange={(next) => {\n // radix Select's onValueChange is typed as (value: string) => void, but the\n // value can only ever be one of the `value` props on the SelectItems below,\n // which are drawn from STRING_OPERATORS.\n const nextOperator = STRING_OPERATORS.find((option) => option.value === next)?.value;\n if (nextOperator) emit(nextOperator, text);\n }}\n >\n <SelectTrigger aria-label={`${column.header} filter operator`}>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {STRING_OPERATORS.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <Input\n id={inputId}\n value={text}\n placeholder={`Filter ${column.header.toLowerCase()}...`}\n onChange={(event) => emit(operator, event.target.value)}\n />\n </div>\n );\n\n if (bare) {\n return <div data-testid={`filter-${column.id}`}>{panel}</div>;\n }\n\n return (\n <Popover>\n <PopoverTrigger asChild>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className={cn(\"gap-1\", isFiltered ? \"max-w-[160px] px-2\" : \"w-8 justify-center px-0\")}\n aria-label={`Filter ${column.header}`}\n data-testid={`filter-${column.id}`}\n >\n <FunnelIcon\n className={cn(\"h-3.5 w-3.5 shrink-0\", isFiltered ? \"opacity-100 text-primary\" : \"opacity-40\")}\n aria-hidden\n />\n {isFiltered && <span className=\"truncate\">{text}</span>}\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-auto p-3\">{panel}</PopoverContent>\n </Popover>\n );\n}\n","import type { ReactElement } from \"react\";\nimport type { ColumnDef } from \"../column/types\";\nimport { BooleanFilter } from \"./BooleanFilter\";\nimport { DateRangeFilter } from \"./DateRangeFilter\";\nimport { EnumFilter } from \"./EnumFilter\";\nimport { NumberRangeFilter } from \"./NumberRangeFilter\";\nimport { StringFilter } from \"./StringFilter\";\nimport type { FilterDescriptor } from \"./types\";\n\n/**\n * Picks and renders the default filter widget for a column, based purely on\n * `column.type`. This is what makes filtering \"just work\" without a column\n * having to configure anything — the discriminated union on `type` lets each\n * branch narrow `column` to the exact widget it needs.\n *\n * Every default widget has its own self-contained Popover+trigger Button\n * (needed under `filterDisplay: \"row\"`, where nothing else provides one) —\n * everywhere else, `<DataGrid>`'s own header filter icon already opens a\n * popover around whatever this function returns, so passing `bare: true`\n * there (this function is `<DataGrid>`'s only caller, and always knows\n * `column.filterDisplay` already) is what keeps that from nesting a second\n * popover inside the first.\n */\nexport function renderDefaultFilterWidget<TRow>(\n column: ColumnDef<TRow>,\n value: FilterDescriptor | undefined,\n onChange: (next: FilterDescriptor | undefined) => void,\n): ReactElement {\n const bare = column.filterDisplay !== \"row\";\n switch (column.type) {\n case \"string\":\n return <StringFilter column={column} value={value} onChange={onChange} bare={bare} />;\n case \"enum\":\n return <EnumFilter column={column} value={value} onChange={onChange} bare={bare} />;\n case \"boolean\":\n return <BooleanFilter column={column} value={value} onChange={onChange} bare={bare} />;\n case \"number\":\n case \"currency\":\n return <NumberRangeFilter column={column} value={value} onChange={onChange} bare={bare} />;\n case \"date\":\n case \"datetime\":\n return <DateRangeFilter column={column} value={value} onChange={onChange} bare={bare} />;\n }\n}\n","import { Columns3 } from \"lucide-react\";\nimport type { ReactElement, ReactNode } from \"react\";\nimport { useEffect } from \"react\";\nimport type { ColumnDef } from \"../column/types\";\nimport { Button } from \"../components/ui/button\";\nimport { Checkbox } from \"../components/ui/checkbox\";\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n} from \"../components/ui/dialog\";\nimport { readPersistedVisibility, writePersistedVisibility } from \"./persistence\";\nimport type { ColumnVisibility } from \"./types\";\nimport { canHideColumn, countVisible, groupColumns, isColumnVisible } from \"./visibility\";\n\nexport interface ColumnSelectorProps<TRow> {\n columns: ColumnDef<TRow>[];\n visibility: ColumnVisibility;\n onVisibilityChange: (visibility: ColumnVisibility) => void;\n /** If set: restore from localStorage on mount, and persist on every subsequent change. */\n persistKey?: string;\n /** Optional custom trigger; defaults to a \"Columns\" icon button. */\n trigger?: ReactNode;\n /** Optional controlled open state, for callers that want their own trigger entirely. */\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n}\n\n/**\n * Controlled column-visibility selector: a modal listing every column,\n * grouped by `column.group` into side-by-side columns (ungrouped columns\n * first, unlabeled, matching `groupColumns`' own ordering), wrapping onto a\n * new row rather than growing the dialog past a reasonable width. Each named\n * group's header is a plain muted label — no bulk-select affordance of any\n * kind lives there (an earlier version had a tri-state checkbox, then an\n * \"All\"/\"None\" link pair, both dropped; see AGENTS.md for why). Purely\n * controlled by `visibility`/`onVisibilityChange` — `persistKey`\n * layers an optional localStorage sync on top (restore once on mount, write\n * on every change), it never becomes the source of truth in place of the\n * `visibility` prop.\n *\n * Not rendered inside `<DataGrid>` automatically — wire it up next to the\n * grid yourself, e.g.:\n *\n * ```tsx\n * const [visibility, setVisibility] = useState<ColumnVisibility>({});\n * <ColumnSelector columns={columns} visibility={visibility} onVisibilityChange={setVisibility} persistKey=\"orders\" />\n * <DataGrid columns={columns} columnVisibility={visibility} onColumnVisibilityChange={setVisibility} ... />\n * ```\n */\nexport function ColumnSelector<TRow>({\n columns,\n visibility,\n onVisibilityChange,\n persistKey,\n trigger,\n open,\n onOpenChange,\n}: ColumnSelectorProps<TRow>): ReactElement {\n // Restore from localStorage once on mount. Intentionally does not depend\n // on `visibility`/`onVisibilityChange` identity — this must run exactly\n // once regardless of how many times the parent re-renders.\n useEffect(() => {\n if (!persistKey) return;\n const restored = readPersistedVisibility(persistKey);\n if (restored) onVisibilityChange({ ...visibility, ...restored });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [persistKey]);\n\n function commit(next: ColumnVisibility): void {\n onVisibilityChange(next);\n if (persistKey) writePersistedVisibility(persistKey, next);\n }\n\n function toggleColumn(columnId: string): void {\n if (!canHideColumn(columns, visibility, columnId)) return;\n commit({ ...visibility, [columnId]: !isColumnVisible(visibility, columnId) });\n }\n\n const totalVisible = countVisible(columns, visibility);\n const groups = groupColumns(columns);\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogTrigger asChild>\n {trigger ?? (\n <Button variant=\"outline\" size=\"icon\" aria-label=\"Choose columns\">\n <Columns3 className=\"h-4 w-4\" aria-hidden />\n </Button>\n )}\n </DialogTrigger>\n <DialogContent className=\"w-fit max-w-[calc(100vw-2rem)] sm:max-w-2xl\">\n <DialogHeader>\n <DialogTitle>Columns</DialogTitle>\n </DialogHeader>\n {/* Groups lay out left-to-right, wrapping onto a new row instead of\n forcing the dialog wider than sm:max-w-2xl above — gap spacing\n alone separates columns (no vertical rule) so a wrapped row never\n ends up with a stray leading divider. */}\n <div className=\"flex flex-wrap gap-x-8 gap-y-5\">\n {groups.map(({ group, columns: groupCols }) => (\n <div key={group ?? \"__ungrouped__\"} className=\"flex min-w-32 flex-col gap-1.5\">\n {group && (\n <span className=\"border-b pb-1.5 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground\">\n {group}\n </span>\n )}\n {groupCols.map((column) => {\n const visible = isColumnVisible(visibility, column.id);\n return (\n <label\n key={column.id}\n className=\"flex items-center gap-2 rounded-md px-1.5 py-1 text-sm hover:bg-accent/50 focus-within:bg-accent/50\"\n >\n <Checkbox\n checked={visible}\n disabled={visible && totalVisible <= 1}\n onCheckedChange={() => toggleColumn(column.id)}\n />\n {column.header}\n </label>\n );\n })}\n </div>\n ))}\n </div>\n </DialogContent>\n </Dialog>\n );\n}\n","import * as DialogPrimitive from \"@radix-ui/react-dialog\";\nimport { X } from \"lucide-react\";\nimport type { ComponentPropsWithoutRef, ElementRef, HTMLAttributes, ReactElement } from \"react\";\nimport { forwardRef } from \"react\";\nimport { cn } from \"../../lib/utils\";\n\nexport const Dialog = DialogPrimitive.Root;\nexport const DialogTrigger = DialogPrimitive.Trigger;\n\nexport const DialogOverlay = forwardRef<\n ElementRef<typeof DialogPrimitive.Overlay>,\n ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Overlay\n ref={ref}\n className={cn(\"fixed inset-0 z-50 bg-black/50\", className)}\n {...props}\n />\n));\nDialogOverlay.displayName = \"DialogOverlay\";\n\nexport const DialogContent = forwardRef<\n ElementRef<typeof DialogPrimitive.Content>,\n ComponentPropsWithoutRef<typeof DialogPrimitive.Content>\n>(({ className, children, ...props }, ref) => (\n <DialogPrimitive.Portal>\n <DialogOverlay />\n <DialogPrimitive.Content\n ref={ref}\n className={cn(\n \"fixed left-1/2 top-1/2 z-50 w-full max-w-md -translate-x-1/2 -translate-y-1/2 rounded-lg border bg-background p-6 shadow-lg\",\n className,\n )}\n {...props}\n >\n {children}\n <DialogPrimitive.Close className=\"absolute right-4 top-4 rounded-sm opacity-70 hover:opacity-100\">\n <X className=\"h-4 w-4\" />\n <span className=\"sr-only\">Close</span>\n </DialogPrimitive.Close>\n </DialogPrimitive.Content>\n </DialogPrimitive.Portal>\n));\nDialogContent.displayName = \"DialogContent\";\n\nexport const DialogHeader = ({\n className,\n ...props\n}: HTMLAttributes<HTMLDivElement>): ReactElement => (\n <div className={cn(\"flex flex-col gap-1.5\", className)} {...props} />\n);\n\nexport const DialogTitle = forwardRef<\n ElementRef<typeof DialogPrimitive.Title>,\n ComponentPropsWithoutRef<typeof DialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Title ref={ref} className={cn(\"text-lg font-semibold\", className)} {...props} />\n));\nDialogTitle.displayName = \"DialogTitle\";\n\nexport const DialogDescription = forwardRef<\n ElementRef<typeof DialogPrimitive.Description>,\n ComponentPropsWithoutRef<typeof DialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Description\n ref={ref}\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n));\nDialogDescription.displayName = \"DialogDescription\";\n\nexport const DialogFooter = ({\n className,\n ...props\n}: HTMLAttributes<HTMLDivElement>): ReactElement => (\n <div className={cn(\"flex justify-end gap-2\", className)} {...props} />\n);\n","import type { ColumnVisibility } from \"./types\";\n\nexport function storageKeyFor(persistKey: string): string {\n return `bmsui-datagrid:columns:${persistKey}`;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * Reads and JSON-parses a persisted `ColumnVisibility` from localStorage.\n * Returns `null` if there's nothing stored, the JSON is malformed, or the\n * parsed value isn't a plain object — callers should fall back to whatever\n * visibility they already have rather than throwing on stale/corrupt data\n * (e.g. a previous version of this component persisting a different shape).\n */\nexport function readPersistedVisibility(persistKey: string): ColumnVisibility | null {\n const raw = window.localStorage.getItem(storageKeyFor(persistKey));\n if (raw === null) return null;\n try {\n const parsed = JSON.parse(raw);\n return isPlainObject(parsed) ? (parsed as ColumnVisibility) : null;\n } catch {\n return null;\n }\n}\n\nexport function writePersistedVisibility(persistKey: string, visibility: ColumnVisibility): void {\n window.localStorage.setItem(storageKeyFor(persistKey), JSON.stringify(visibility));\n}\n","import type { ColumnDef } from \"../column/types\";\nimport type { ColumnVisibility } from \"./types\";\n\n/** A missing key means visible, matching TanStack's own VisibilityState convention. */\nexport function isColumnVisible(visibility: ColumnVisibility, columnId: string): boolean {\n return visibility[columnId] !== false;\n}\n\n/** How many of `columns` are currently visible under `visibility`. */\nexport function countVisible<TRow>(\n columns: readonly ColumnDef<TRow>[],\n visibility: ColumnVisibility,\n): number {\n return columns.filter((column) => isColumnVisible(visibility, column.id)).length;\n}\n\nexport interface ColumnGroup<TRow> {\n /** undefined for the ungrouped section, which is always listed first. */\n group: string | undefined;\n columns: ColumnDef<TRow>[];\n}\n\n/**\n * Groups columns by `column.group`, preserving each group's first-seen order\n * and each column's original order within its group. The ungrouped section\n * (columns with no `group`) is always returned first, regardless of where\n * ungrouped columns actually fall in the input order.\n */\nexport function groupColumns<TRow>(columns: readonly ColumnDef<TRow>[]): ColumnGroup<TRow>[] {\n const ungrouped: ColumnDef<TRow>[] = [];\n const named = new Map<string, ColumnDef<TRow>[]>();\n\n for (const column of columns) {\n if (column.group === undefined) {\n ungrouped.push(column);\n continue;\n }\n const bucket = named.get(column.group);\n if (bucket) bucket.push(column);\n else named.set(column.group, [column]);\n }\n\n const result: ColumnGroup<TRow>[] = [];\n if (ungrouped.length > 0) result.push({ group: undefined, columns: ungrouped });\n for (const [group, groupedColumns] of named) result.push({ group, columns: groupedColumns });\n return result;\n}\n\n/** Whether toggling `columnId` off is currently allowed (must leave at least one column visible). */\nexport function canHideColumn<TRow>(\n columns: readonly ColumnDef<TRow>[],\n visibility: ColumnVisibility,\n columnId: string,\n): boolean {\n if (!isColumnVisible(visibility, columnId)) return true; // already hidden; \"hiding\" it again is a no-op\n return countVisible(columns, visibility) > 1;\n}\n\n/** Whether hiding every column in `groupColumnIds` at once is currently allowed. */\nexport function canHideGroup<TRow>(\n columns: readonly ColumnDef<TRow>[],\n visibility: ColumnVisibility,\n groupColumnIds: readonly string[],\n): boolean {\n const groupIds = new Set(groupColumnIds);\n const visibleOutsideGroup = columns.filter(\n (column) => !groupIds.has(column.id) && isColumnVisible(visibility, column.id),\n ).length;\n return visibleOutsideGroup > 0;\n}\n","import type { MenuItem, MenuItemContext } from \"./types\";\n\n/** A `MenuItem` with its `visible`/`disabled` predicates already evaluated against a context. */\nexport interface ResolvedMenuItem<TRow> extends MenuItem<TRow> {\n isDisabled: boolean;\n}\n\n/**\n * Evaluates each item's `visible`/`disabled` predicates against `ctx` (both\n * default to \"always\" when omitted), dropping items that aren't visible and\n * annotating the rest with `isDisabled`. Pure and UI-free on purpose, so the\n * gating logic itself — as opposed to the DropdownMenu rendering built on\n * top of it — can be unit-tested directly.\n */\nexport function resolveMenuItems<TRow>(\n items: readonly MenuItem<TRow>[],\n ctx: MenuItemContext<TRow>,\n): ResolvedMenuItem<TRow>[] {\n const resolved: ResolvedMenuItem<TRow>[] = [];\n for (const item of items) {\n const isVisible = item.visible ? item.visible(ctx) : true;\n if (!isVisible) continue;\n resolved.push({ ...item, isDisabled: item.disabled ? item.disabled(ctx) : false });\n }\n return resolved;\n}\n","import { MoreVertical } from \"lucide-react\";\nimport type { ReactElement } from \"react\";\nimport { useMemo } from \"react\";\nimport { Button } from \"../components/ui/button\";\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from \"../components/ui/dropdown-menu\";\nimport { stopRowClick } from \"../lib/utils\";\nimport { resolveMenuItems } from \"./resolveMenuItems\";\nimport type { MenuItem, MenuItemContext } from \"./types\";\n\ninterface ActionsMenuProps<TRow> {\n items: readonly MenuItem<TRow>[];\n ctx: MenuItemContext<TRow>;\n /** Accessible label for the trigger button, e.g. \"Row actions\" or \"Bulk actions\". */\n triggerLabel: string;\n}\n\n/**\n * Shared rendering for both `rowActions` (per-row kebab menu) and\n * `headerActions` (toolbar menu) — they differ only in what `ctx` they're\n * evaluated against (`{ row }` vs `{ selectedRows }`) and the trigger's\n * accessible label, so <DataGrid> passes those in rather than this module\n * having two near-identical components.\n */\nexport function ActionsMenu<TRow>({ items, ctx, triggerLabel }: ActionsMenuProps<TRow>): ReactElement | null {\n // `<DataGrid>` instantiates one `<ActionsMenu>` per visible row (for\n // `rowActions`) and re-renders every one of them on every scroll tick —\n // memoized on `ctx.row`/`ctx.selectedRows` (the parts of `ctx` any\n // `visible`/`disabled` predicate can actually read) rather than on `ctx`\n // itself, since the caller passes a fresh `{ row: row.original }` object\n // literal every render; that identity churn would otherwise defeat a\n // naive `useMemo([items, ctx])` even though the row itself hasn't changed.\n const resolved = useMemo(\n () => resolveMenuItems(items, ctx),\n [items, ctx.row, ctx.selectedRows],\n );\n if (resolved.length === 0) return null;\n\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"ghost\" size=\"icon\" aria-label={triggerLabel} onClick={stopRowClick}>\n <MoreVertical className=\"h-4 w-4\" aria-hidden />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n {resolved.map((item) => (\n <DropdownMenuItem\n key={item.id}\n danger={item.danger}\n disabled={item.isDisabled}\n onSelect={() => item.onSelect(ctx)}\n >\n {item.label}\n </DropdownMenuItem>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n","import * as DropdownMenuPrimitive from \"@radix-ui/react-dropdown-menu\";\nimport type { ComponentPropsWithoutRef, ElementRef } from \"react\";\nimport { forwardRef } from \"react\";\nimport { cn } from \"../../lib/utils\";\n\nexport const DropdownMenu = DropdownMenuPrimitive.Root;\nexport const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;\nexport const DropdownMenuGroup = DropdownMenuPrimitive.Group;\n\nexport const DropdownMenuContent = forwardRef<\n ElementRef<typeof DropdownMenuPrimitive.Content>,\n ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>\n>(({ className, sideOffset = 4, ...props }, ref) => (\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.Content\n ref={ref}\n sideOffset={sideOffset}\n className={cn(\n \"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md\",\n className,\n )}\n {...props}\n />\n </DropdownMenuPrimitive.Portal>\n));\nDropdownMenuContent.displayName = \"DropdownMenuContent\";\n\nexport const DropdownMenuItem = forwardRef<\n ElementRef<typeof DropdownMenuPrimitive.Item>,\n ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & { danger?: boolean }\n>(({ className, danger, ...props }, ref) => (\n <DropdownMenuPrimitive.Item\n ref={ref}\n className={cn(\n \"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n danger && \"text-destructive focus:bg-destructive/10 focus:text-destructive\",\n className,\n )}\n {...props}\n />\n));\nDropdownMenuItem.displayName = \"DropdownMenuItem\";\n\nexport const DropdownMenuSeparator = forwardRef<\n ElementRef<typeof DropdownMenuPrimitive.Separator>,\n ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n <DropdownMenuPrimitive.Separator\n ref={ref}\n className={cn(\"-mx-1 my-1 h-px bg-muted\", className)}\n {...props}\n />\n));\nDropdownMenuSeparator.displayName = \"DropdownMenuSeparator\";\n\nexport const DropdownMenuLabel = forwardRef<\n ElementRef<typeof DropdownMenuPrimitive.Label>,\n ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label>\n>(({ className, ...props }, ref) => (\n <DropdownMenuPrimitive.Label\n ref={ref}\n className={cn(\"px-2 py-1.5 text-xs font-medium text-muted-foreground\", className)}\n {...props}\n />\n));\nDropdownMenuLabel.displayName = \"DropdownMenuLabel\";\n","import { BarsArrowDownIcon, BarsArrowUpIcon, FunnelIcon } from \"@heroicons/react/24/outline\";\nimport type { CellContext, ColumnDef as TanstackColumnDef, ColumnSizingState, RowData } from \"@tanstack/react-table\";\nimport { columnResizingFeature, columnSizingFeature, flexRender, tableFeatures, useTable } from \"@tanstack/react-table\";\nimport { useVirtualizer } from \"@tanstack/react-virtual\";\nimport { ChevronRight, Loader2 } from \"lucide-react\";\nimport type { CSSProperties, ReactElement, ReactNode } from \"react\";\nimport { Fragment, useEffect, useLayoutEffect, useMemo, useRef, useState } from \"react\";\nimport { alignClassName, defaultFormat } from \"../column/format\";\nimport type { ColumnDef } from \"../column/types\";\nimport { getColumnValue, isFilterable, isSortable } from \"../column/types\";\nimport { isColumnVisible } from \"../column-selector/visibility\";\nimport { Button } from \"../components/ui/button\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"../components/ui/popover\";\nimport { renderDefaultFilterWidget } from \"../filter/registry\";\nimport type { FilterDescriptor } from \"../filter/types\";\nimport { cn, stopRowClick } from \"../lib/utils\";\nimport { SELECTION_COLUMN_WIDTH } from \"../lib/structuralColumns\";\nimport { ActionsMenu } from \"../menu/ActionsMenu\";\nimport { groupRows } from \"./groupRows\";\nimport { processClientData } from \"./processClientData\";\nimport type { DataGridProps } from \"./types\";\nimport { useGridState } from \"./useGridState\";\n\n// `<DataGrid>` never uses TanStack's own sorted/filtered/paginated row\n// models — `useGridState`/`processClientData` own all of that (see their\n// module docs) — so no stock row-model features are registered here, only\n// the automatic core row model. `columnSizingFeature`/`columnResizingFeature`\n// are the exception: `enableColumnResizing` (a table-level option, not a\n// stock row model) gates whether they actually do anything, so registering\n// them unconditionally costs nothing when a given <DataGrid> doesn't use\n// resize. Declared once at module scope, per TanStack's own guidance, rather\n// than recreated on every render.\nconst gridTableFeatures = tableFeatures({ columnSizingFeature, columnResizingFeature });\ntype GridTableFeatures = typeof gridTableFeatures;\n\n// TanStack's own default for any column without an explicit `size` (see\n// `getDefaultColumnSizingColumnDef` in `columnSizingFeature.utils`) — used\n// here only as the fallback a *pinned* column's offset math falls back to\n// before a `table` instance exists to ask `getSize()` directly.\nconst DEFAULT_COLUMN_SIZE = 150;\n\n// Fixed pixel width of each \"structural\" column DataGrid renders itself\n// (row-expand chevron, selection checkbox, per-row actions menu) rather than\n// deriving from `columns` — matched by the `width` style applied to their\n// own cells below. These always render pinned to the left/right edge of the\n// scroll container, regardless of whether any *data* column is `pinned`: a\n// data column's own pinned offset must reserve this space (see\n// `pinnedOffsets`'s `leadingOffset` param below), otherwise a pinned data\n// column sticks at the very edge and scrolls right over these non-sticky\n// columns, hiding them once the grid needs to scroll horizontally.\n// 36px (the shared `Button`'s `size=\"icon\"`) plus 4px of padding on each\n// side — big enough to comfortably hit (WCAG's 44px minimum touch target)\n// without the surrounding `p-1` cell padding eating into the button itself.\nconst DETAIL_COLUMN_WIDTH = 44;\nconst ROW_ACTIONS_COLUMN_WIDTH = 44;\n\n// Opaque equivalent of the body row's translucent `bg-foreground/5` zebra\n// tint, for the structural expand/selection/row-actions columns' `sticky`\n// cells — a translucent background there would let horizontally-scrolled\n// column content show through underneath on odd rows.\nconst STRUCTURAL_ZEBRA_BG_CLASS = \"bg-[color-mix(in_srgb,var(--color-foreground)_5%,var(--color-background))]\";\n\n/**\n * Cumulative sticky `left`/`right` pixel offset per pinned column id, in\n * visible-column order, starting from `leadingOffset` — the combined width\n * of whichever structural columns (expand/selection on the left,\n * row-actions on the right) render before the first data column on that\n * side, so a pinned data column's offset accounts for them instead of\n * assuming it's the first thing in the row.\n */\nfunction pinnedOffsets<TRow>(\n visibleColumns: ColumnDef<TRow>[],\n side: \"left\" | \"right\",\n getSize: (columnId: string) => number,\n leadingOffset: number,\n): Map<string, number> {\n const offsets = new Map<string, number>();\n let cursor = leadingOffset;\n const ordered = side === \"left\" ? visibleColumns : [...visibleColumns].reverse();\n for (const column of ordered) {\n if (column.pinned !== side) continue;\n offsets.set(column.id, cursor);\n cursor += getSize(column.id);\n }\n return offsets;\n}\n\n/** One run of contiguous visible columns for the optional spanning header-group row. */\ntype HeaderRun<TRow> =\n | { kind: \"group\"; label: string; columns: ColumnDef<TRow>[] }\n | { kind: \"single\"; column: ColumnDef<TRow> };\n\n/**\n * Groups contiguous visible columns sharing the same `headerGroup` label\n * into `\"group\"` runs; every other column becomes its own `\"single\"` run —\n * see `BaseColumn.headerGroup`'s doc for why this is positional rather than\n * collecting every same-labeled column regardless of adjacency.\n */\nfunction computeHeaderRuns<TRow>(columns: ColumnDef<TRow>[]): HeaderRun<TRow>[] {\n const runs: HeaderRun<TRow>[] = [];\n for (const column of columns) {\n const last = runs[runs.length - 1];\n if (column.headerGroup && last?.kind === \"group\" && last.label === column.headerGroup) {\n last.columns.push(column);\n } else if (column.headerGroup) {\n runs.push({ kind: \"group\", label: column.headerGroup, columns: [column] });\n } else {\n runs.push({ kind: \"single\", column });\n }\n }\n return runs;\n}\n\nfunction toTanstackColumns<TRow extends RowData>(\n columns: ColumnDef<TRow>[],\n): TanstackColumnDef<GridTableFeatures, TRow, unknown>[] {\n return columns.map((column) => ({\n id: column.id,\n header: column.header,\n size: column.width,\n accessorFn: (row: TRow) => getColumnValue(column, row),\n cell: (info: CellContext<GridTableFeatures, TRow, unknown>): ReactNode =>\n column.cell\n ? column.cell(info.getValue(), info.row.original)\n : defaultFormat(column, info.getValue()),\n }));\n}\n\n/**\n * A single React Table component covering both client- and server-driven\n * data: filtering, sorting, and pagination all flow through one `GridState`,\n * with `dataSource.mode` deciding whether the grid computes the visible page\n * itself (`\"client\"`) or trusts a page the caller already computed\n * (`\"server\"`). Column filter UI defaults to the type-appropriate widget\n * from `src/filter` unless a column supplies its own `cell` renderer.\n */\nexport function DataGrid<TRow extends RowData>({\n columns,\n dataSource,\n getRowId,\n rowActions,\n headerActions,\n enableMultiSort = false,\n initialState,\n columnVisibility,\n renderDetail,\n enableColumnResizing = false,\n columnResizeMode = \"onChange\",\n columnSizing: controlledColumnSizing,\n onColumnSizingChange,\n virtualize,\n showPagination = true,\n getRowProps,\n getRowTestId,\n gridState,\n selectedIds: controlledSelectedIds,\n onSelectedIdsChange,\n testId,\n loading: loadingProp = false,\n groupBy,\n renderGroupHeader,\n defaultGroupsExpanded = true,\n expandedGroups: controlledExpandedGroups,\n onExpandedGroupsChange,\n zebra = true,\n}: DataGridProps<TRow>): ReactElement {\n const { state, filtersByColumn, setColumnFilter, toggleSort, setPage } = useGridState(\n dataSource,\n initialState,\n gridState,\n );\n const [internalSelectedIds, setInternalSelectedIds] = useState<ReadonlySet<string>>(new Set());\n const selectedIds = controlledSelectedIds ?? internalSelectedIds;\n function updateSelectedIds(next: ReadonlySet<string>): void {\n setInternalSelectedIds(next);\n onSelectedIdsChange?.(next);\n }\n\n // Same controlled/uncontrolled pattern as `selectedIds` above — without\n // an internal fallback here, dragging a resize handle with no\n // `columnSizing`/`onColumnSizingChange` passed in (the common case; see\n // `enableColumnResizing`'s own doc) had nowhere to write the new width to\n // at all: `useTable` below was never given `state.columnSizing` nor\n // `onColumnSizingChange` in that case, so every drag was silently\n // discarded and no column ever actually resized.\n const [internalColumnSizing, setInternalColumnSizing] = useState<ColumnSizingState>({});\n const columnSizing = controlledColumnSizing ?? internalColumnSizing;\n function updateColumnSizing(next: ColumnSizingState): void {\n setInternalColumnSizing(next);\n onColumnSizingChange?.(next);\n }\n const [expandedIds, setExpandedIds] = useState<ReadonlySet<string>>(new Set());\n\n function toggleRowExpanded(id: string): void {\n setExpandedIds((prev) => {\n const next = new Set(prev);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n return next;\n });\n }\n\n // Same controlled/uncontrolled pattern as `selectedIds`/`columnSizing`\n // above. A key absent from this record (a group never toggled since it\n // was first seen) falls back to `defaultGroupsExpanded`, not `false` —\n // otherwise every group would render collapsed on first paint.\n const [internalExpandedGroups, setInternalExpandedGroups] = useState<Record<string, boolean>>({});\n const expandedGroupsState = controlledExpandedGroups ?? internalExpandedGroups;\n function updateExpandedGroups(next: Record<string, boolean>): void {\n setInternalExpandedGroups(next);\n onExpandedGroupsChange?.(next);\n }\n function isGroupExpanded(key: string): boolean {\n return expandedGroupsState[key] ?? defaultGroupsExpanded;\n }\n function toggleGroupExpanded(key: string): void {\n updateExpandedGroups({ ...expandedGroupsState, [key]: !isGroupExpanded(key) });\n }\n\n // Single source of truth for \"which columns actually render\": every other\n // representation below (the TanStack column defs, the header lookup map,\n // the \"No results\" colSpan) is derived from this, not from `columns`\n // directly — otherwise two derived views of the same visibility could\n // disagree with each other.\n const visibleColumns = useMemo(\n () =>\n columnVisibility ? columns.filter((column) => isColumnVisible(columnVisibility, column.id)) : columns,\n [columns, columnVisibility],\n );\n\n // Depend on dataSource.mode/.data (and .rowCount, only in server mode)\n // rather than on `dataSource` itself — a caller that inlines\n // `dataSource={{ mode: \"client\", data: rows }}` (a natural pattern, and\n // exactly what every test in this file does) creates a new wrapper object\n // on every render, which would defeat this memo entirely if it depended on\n // the wrapper's own identity instead of its actually-relevant fields.\n const serverRowCount = dataSource.mode === \"server\" ? dataSource.rowCount : undefined;\n const { rows, rowCount } = useMemo(() => {\n if (dataSource.mode === \"client\") return processClientData(dataSource.data, state);\n return { rows: dataSource.data, rowCount: serverRowCount ?? 0 };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [dataSource.mode, dataSource.data, serverRowCount, state]);\n\n const selectedRows = useMemo(\n () => rows.filter((row) => selectedIds.has(getRowId(row))),\n [rows, selectedIds, getRowId],\n );\n const allOnPageSelected = rows.length > 0 && rows.every((row) => selectedIds.has(getRowId(row)));\n\n function toggleRowSelected(id: string): void {\n const next = new Set(selectedIds);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n updateSelectedIds(next);\n }\n\n function toggleAllOnPageSelected(): void {\n const next = new Set(selectedIds);\n for (const row of rows) {\n if (allOnPageSelected) next.delete(getRowId(row));\n else next.add(getRowId(row));\n }\n updateSelectedIds(next);\n }\n\n const tanstackColumns = useMemo(() => toTanstackColumns(visibleColumns), [visibleColumns]);\n\n // A Map lookup instead of visibleColumns.find(...) inside the header\n // render loop below — the latter would make header rendering O(columns²)\n // (a linear scan per header, once per header) instead of O(columns).\n const columnById = useMemo(() => {\n const map = new Map<string, ColumnDef<TRow>>();\n for (const column of visibleColumns) map.set(column.id, column);\n return map;\n }, [visibleColumns]);\n\n const table = useTable({\n features: gridTableFeatures,\n data: rows,\n columns: tanstackColumns,\n getRowId,\n enableColumnResizing,\n columnResizeMode,\n state: { columnSizing },\n onColumnSizingChange: (updater) => {\n const next = typeof updater === \"function\" ? updater(columnSizing) : updater;\n updateColumnSizing(next);\n },\n });\n\n const pageCount = Math.max(1, Math.ceil(rowCount / state.pageSize));\n const loading = loadingProp || (dataSource.mode === \"server\" ? Boolean(dataSource.loading) : false);\n // The checkbox column itself is decoupled from `headerActions`: a caller\n // using controlled `selectedIds` (its own toolbar elsewhere on the page,\n // e.g. a bulk-export button) wants the checkboxes without <DataGrid>'s own\n // redundant \"{N} selected\" + bulk-actions bar, which stays gated on\n // `headerActions` specifically, below.\n const showSelectionColumn = Boolean(headerActions?.length) || controlledSelectedIds !== undefined;\n const showRowActionsColumn = Boolean(rowActions?.length);\n const showDetailColumn = Boolean(renderDetail);\n const totalColumnCount =\n visibleColumns.length +\n (showDetailColumn ? 1 : 0) +\n (showSelectionColumn ? 1 : 0) +\n (showRowActionsColumn ? 1 : 0);\n\n // The filter row is entirely opt-in — it only renders at all once at least\n // one visible, filterable column asks for it via `filterDisplay: \"row\"`\n // (see BaseColumn's doc for why: wide controls like NumberHistogramFilter\n // don't fit well collapsed into the header's compact popover trigger).\n const hasFilterRow = visibleColumns.some((column) => isFilterable(column) && column.filterDisplay === \"row\");\n\n // The extra spanning-label header row is entirely opt-in, same pattern as\n // `hasFilterRow` above — only renders at all once at least one visible\n // column sets `headerGroup` (see `BaseColumn.headerGroup`'s doc).\n const headerRuns = useMemo(() => computeHeaderRuns(visibleColumns), [visibleColumns]);\n const hasHeaderGroups = visibleColumns.some((column) => column.headerGroup);\n\n function renderFilterWidget(column: ColumnDef<TRow>): ReactNode {\n const value = filtersByColumn.get(column.id);\n const onChange = (next: FilterDescriptor | undefined): void => setColumnFilter(column.id, next);\n return column.renderFilter\n ? column.renderFilter(value, onChange, state.filter)\n : renderDefaultFilterWidget(column, value, onChange);\n }\n\n // `table.getColumn(id)?.getSize()` already resolves to exactly\n // `column.width ?? DEFAULT_COLUMN_SIZE` when resize has never touched that\n // column (TanStack's own resting value), and correctly reflects the live,\n // resized width once it has — one source of truth for both.\n const columnSize = (columnId: string): number => table.getColumn(columnId)?.getSize() ?? DEFAULT_COLUMN_SIZE;\n const leadingStructuralWidth =\n (showDetailColumn ? DETAIL_COLUMN_WIDTH : 0) + (showSelectionColumn ? SELECTION_COLUMN_WIDTH : 0);\n const trailingStructuralWidth = showRowActionsColumn ? ROW_ACTIONS_COLUMN_WIDTH : 0;\n\n // Memoized on `columnSizing` (rather than recomputed every render) because\n // every scroll tick re-renders this whole component (the virtualizer's own\n // state lives in React state) — without this, scrolling a wide/pinned grid\n // would reallocate both offset Maps (plus, for the right side, a full\n // `[...visibleColumns].reverse()` copy) on every single frame for no\n // reason: neither `visibleColumns` nor any column's resolved size changes\n // just because the scroll position did.\n // eslint-disable-next-line react-hooks/exhaustive-deps -- `columnSize` reads `columnSizing`/`table` by closure; both are covered by the explicit deps below.\n const leftPinnedOffsets = useMemo(\n () => pinnedOffsets(visibleColumns, \"left\", columnSize, leadingStructuralWidth),\n [visibleColumns, columnSizing, leadingStructuralWidth],\n );\n // eslint-disable-next-line react-hooks/exhaustive-deps -- see leftPinnedOffsets above.\n const rightPinnedOffsets = useMemo(\n () => pinnedOffsets(visibleColumns, \"right\", columnSize, trailingStructuralWidth),\n [visibleColumns, columnSizing, trailingStructuralWidth],\n );\n\n // Combines with (doesn't replace) the `width` style every cell already\n // gets — a pinned column's `left`/`right` offset only makes sense relative\n // to the scrolling `<table>`, and `bg-background` keeps whatever scrolls\n // underneath a pinned cell from showing through it. Only a pinned column,\n // or every column once `enableColumnResizing` is on, gets a concrete\n // width from `columnSize()` — an unpinned column in a non-resizable grid\n // keeps its original natural/content-driven sizing when it has no\n // explicit `width` of its own.\n function pinnedCellProps(\n column: ColumnDef<TRow>,\n area: \"header\" | \"body\" = \"body\",\n ): { className?: string; style?: CSSProperties } {\n // Pinned cells need an opaque background of their own so body/header\n // content scrolling underneath a sticky column doesn't show through —\n // matched to whichever section (header vs. body) they sit in, since the\n // header now carries `bg-muted` while the body stays `bg-background`.\n // Plain template strings rather than `cn()` throughout this function and\n // `structuralCellProps` below: every combination here is a fixed set of\n // internally-controlled utility classes touching unrelated CSS\n // properties (position/z-index vs. background), so there's never a\n // Tailwind conflict for `cn()`'s clsx+twMerge machinery to resolve —\n // called from the memoized per-column maps below, so it already runs\n // once per column per relevant state change, not once per cell.\n const bg = area === \"header\" ? \"bg-muted\" : \"bg-background\";\n if (column.pinned === \"left\") {\n return { className: `sticky z-10 ${bg}`, style: { left: leftPinnedOffsets.get(column.id), width: columnSize(column.id) } };\n }\n if (column.pinned === \"right\") {\n return { className: `sticky z-10 ${bg}`, style: { right: rightPinnedOffsets.get(column.id), width: columnSize(column.id) } };\n }\n if (enableColumnResizing) return { style: { width: columnSize(column.id) } };\n return { style: column.width ? { width: column.width } : undefined };\n }\n\n // Same sticky-pinning treatment as `pinnedCellProps`, for the structural\n // expand/selection/row-actions columns instead of a data column — these\n // always pin (they have no `pinned` prop of their own to opt out with),\n // so a pinned *data* column's reserved `leadingOffset`/its own offset\n // math actually lines up with a column that's really there, rather than a\n // gap left by content that scrolled away underneath it. The `border-b\n // p-1` base is baked in here (every call site below combined it with\n // exactly this className anyway) so callers use `.className`/`.classNameOdd`\n // directly instead of re-running `cn()` per row on an already-fixed string.\n function structuralCellProps(\n side: \"left\" | \"right\",\n offset: number,\n width: number,\n area: \"header\" | \"body\" = \"body\",\n ): { className: string; classNameOdd: string; style: CSSProperties } {\n // A header structural cell (select-all checkbox, expand-all chevron) also\n // sticks to the top of the scroll container -- z-20 so it stays above a\n // body row's own z-10 side-pinned cells once both are visible at once.\n const base = area === \"header\" ? \"border-b border-border p-1 sticky top-0 z-20\" : \"border-b border-border p-1 sticky z-10\";\n const bg = area === \"header\" ? \"bg-muted\" : \"bg-background\";\n const bgOdd = area === \"header\" ? \"bg-muted\" : STRUCTURAL_ZEBRA_BG_CLASS;\n return {\n className: `${base} ${bg}`,\n classNameOdd: `${base} ${bgOdd}`,\n style: { [side]: offset, width },\n };\n }\n\n // Computed once per render (cheap object literals) rather than inline at\n // each of the several header/body/filter-row call sites below, so the\n // selection column's offset — which depends on whether the detail column\n // renders before it — stays in exactly one place.\n const detailCellProps = structuralCellProps(\"left\", 0, DETAIL_COLUMN_WIDTH);\n const selectionCellProps = structuralCellProps(\n \"left\",\n showDetailColumn ? DETAIL_COLUMN_WIDTH : 0,\n SELECTION_COLUMN_WIDTH,\n );\n const rowActionsCellProps = structuralCellProps(\"right\", 0, ROW_ACTIONS_COLUMN_WIDTH);\n const detailHeaderCellProps = structuralCellProps(\"left\", 0, DETAIL_COLUMN_WIDTH, \"header\");\n const selectionHeaderCellProps = structuralCellProps(\n \"left\",\n showDetailColumn ? DETAIL_COLUMN_WIDTH : 0,\n SELECTION_COLUMN_WIDTH,\n \"header\",\n );\n const rowActionsHeaderCellProps = structuralCellProps(\"right\", 0, ROW_ACTIONS_COLUMN_WIDTH, \"header\");\n\n // Precomputed once per *column* (via the memoized maps below), not once\n // per column per *row* — the previous shape called `pinnedCellProps` (and\n // its `cn()` inside) from within the per-row cell-rendering loop, which\n // meant a virtualized 40-visible-row x 10-column grid ran that ~400 times\n // per render (and once per scroll-triggered re-render) instead of 10. Only\n // a pinned-or-resizing column needs a props object at all in the body;\n // every other column's `<td>` falls back to the plain base class below.\n const BODY_TD_BASE_CLASS = \"border-b border-border p-2\";\n function bodyCellClassAndStyle(column: ColumnDef<TRow>): { className: string; style?: CSSProperties } {\n const pinnedProps = column.pinned || enableColumnResizing ? pinnedCellProps(column) : undefined;\n return {\n className: pinnedProps?.className ? `${BODY_TD_BASE_CLASS} ${pinnedProps.className}` : BODY_TD_BASE_CLASS,\n style: pinnedProps?.style,\n };\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- `bodyCellClassAndStyle` closes over `enableColumnResizing`/`leftPinnedOffsets`/`rightPinnedOffsets`/`columnSizing`, all listed explicitly below.\n const bodyCellPropsByColumn = useMemo(() => {\n const map = new Map<string, { className: string; style?: CSSProperties }>();\n for (const column of visibleColumns) map.set(column.id, bodyCellClassAndStyle(column));\n return map;\n }, [visibleColumns, enableColumnResizing, leftPinnedOffsets, rightPinnedOffsets, columnSizing]);\n\n // Same idea for the two header contexts, each with their own fixed base\n // classes (the plain leaf header row vs. the opt-in filter row) — both\n // still only ever render once per <DataGrid> render (there's exactly one\n // header, not one per row), but memoizing keeps a pure-scroll re-render\n // from re-running `cn()` over every visible column for no reason either.\n function headerCellClassAndStyle(\n column: ColumnDef<TRow>,\n base: string,\n options: { sticky?: boolean } = {},\n ): { className: string; style?: CSSProperties } {\n const pinnedProps = pinnedCellProps(column, \"header\");\n const align = alignClassName(column);\n // `pinnedProps.className` (when set) comes first so `cn()`'s twMerge\n // resolves overlapping utilities (z-10/bg-muted from side-pinning) in\n // favor of whatever comes after -- the sticky-to-top treatment needs to\n // win: z-20 (not the side-pin's z-10) so a pinned column's header cell\n // still renders above a *body* row's own side-pinned cell once scrolled.\n const className = cn(\n base,\n align,\n pinnedProps.className,\n options.sticky && \"sticky top-0 z-20 bg-muted\",\n );\n return { className, style: pinnedProps.style };\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- see bodyCellPropsByColumn above.\n const leafHeaderCellPropsByColumn = useMemo(() => {\n const map = new Map<string, { className: string; style?: CSSProperties }>();\n for (const column of visibleColumns) {\n map.set(\n column.id,\n headerCellClassAndStyle(column, \"relative border-b border-border p-2 font-medium\", { sticky: true }),\n );\n }\n return map;\n }, [visibleColumns, enableColumnResizing, leftPinnedOffsets, rightPinnedOffsets, columnSizing]);\n // eslint-disable-next-line react-hooks/exhaustive-deps -- see bodyCellPropsByColumn above.\n const filterHeaderCellPropsByColumn = useMemo(() => {\n const map = new Map<string, { className: string; style?: CSSProperties }>();\n for (const column of visibleColumns) {\n map.set(column.id, headerCellClassAndStyle(column, \"border-b border-border p-2 align-top font-normal\"));\n }\n return map;\n }, [visibleColumns, enableColumnResizing, leftPinnedOffsets, rightPinnedOffsets, columnSizing]);\n\n const tableRows = table.getRowModel().rows;\n // Interleaving synthetic group-header rows (and hiding a collapsed\n // bucket's rows) needs a flattened index space to virtualize correctly —\n // out of scope for this pass, so `groupBy` forces virtualization off\n // rather than silently mis-rendering; see `groupBy`'s own doc.\n const shouldVirtualize = Boolean(virtualize) && !groupBy && tableRows.length > (virtualize?.threshold ?? 100);\n const groupedBuckets = useMemo(\n () => (groupBy ? groupRows(tableRows, (row) => groupBy(row.original)) : undefined),\n [tableRows, groupBy],\n );\n // A group-header row sticks right below the real `<thead>` while its\n // members scroll past -- `top` has to be the header's actual rendered\n // height (it varies with the optional filter/header-group rows and with\n // column-resize-driven wrapping), not a guessed constant. `ResizeObserver`\n // rather than a one-off measurement because header height can change after\n // mount (e.g. a column's header content wrapping to a second line).\n const theadRef = useRef<HTMLTableSectionElement>(null);\n const [groupHeaderTop, setGroupHeaderTop] = useState(0);\n useLayoutEffect(() => {\n if (!groupBy) return;\n const theadEl = theadRef.current;\n if (!theadEl) return;\n const updateHeight = (): void => setGroupHeaderTop(theadEl.getBoundingClientRect().height);\n updateHeight();\n const observer = new ResizeObserver(updateHeight);\n observer.observe(theadEl);\n return () => observer.disconnect();\n }, [groupBy]);\n const scrollRef = useRef<HTMLDivElement>(null);\n const virtualizer = useVirtualizer({\n count: tableRows.length,\n getScrollElement: () => scrollRef.current,\n estimateSize: () => virtualize?.estimatedRowHeight ?? 40,\n overscan: virtualize?.overscan ?? 10,\n enabled: shouldVirtualize,\n });\n const virtualItems = shouldVirtualize ? virtualizer.getVirtualItems() : [];\n const paddingTop = virtualItems.length > 0 ? virtualItems[0]!.start : 0;\n const paddingBottom =\n virtualItems.length > 0 ? virtualizer.getTotalSize() - virtualItems[virtualItems.length - 1]!.end : 0;\n\n // Fires `onEndReached` once per distinct `tableRows.length` — scrolling to\n // the last currently-loaded row while more rows might exist. Guarded by a\n // ref (not state) since it's bookkeeping for an effect, not something that\n // should itself trigger a render.\n const lastVisibleIndex = virtualItems.length > 0 ? virtualItems[virtualItems.length - 1]!.index : -1;\n const notifiedForLengthRef = useRef<number>(-1);\n useEffect(() => {\n if (!virtualize?.onEndReached) return;\n if (lastVisibleIndex < 0 || lastVisibleIndex < tableRows.length - 1) return;\n if (virtualize.hasMore === false) return;\n if (notifiedForLengthRef.current === tableRows.length) return;\n notifiedForLengthRef.current = tableRows.length;\n virtualize.onEndReached();\n }, [lastVisibleIndex, tableRows.length, virtualize]);\n\n function renderRow(row: (typeof tableRows)[number], measureRef?: (el: Element | null) => void): ReactNode {\n const isExpanded = expandedIds.has(row.id);\n // Each row gets its own `<tbody>` (a `<table>` can hold more than one)\n // instead of every row sharing one big one — a virtualizer's\n // `measureElement` observes exactly one DOM node per index, and a\n // `<tbody>`'s own bounding rect already reflects the combined height of\n // every `<tr>` inside it, INCLUDING an expanded detail row right below\n // it. Measuring the row's own `<tr>` alone (the alternative) would never\n // notice the detail panel's height at all once virtualized.\n const rowProps = getRowProps?.(row.original);\n const rowTestId = getRowTestId?.(row.original) ?? `row-${row.id}`;\n const isOddRow = zebra && row.index % 2 === 1;\n return (\n <tbody key={row.id} ref={measureRef} data-index={row.index}>\n <tr\n {...rowProps}\n data-testid={rowTestId}\n // `cn()` (clsx+twMerge) is only needed here when a caller-supplied\n // `getRowProps().className` might itself carry a conflicting\n // `bg-*` utility that needs resolving against the zebra stripe —\n // the overwhelmingly common case (no `getRowProps` at all) skips\n // straight to a plain string, avoiding that per-row allocation\n // for every grid that doesn't use the feature.\n className={\n rowProps?.className\n ? cn(\"divide-x divide-border\", rowProps.className as string, isOddRow && \"bg-foreground/5\")\n : `divide-x divide-border${isOddRow ? \" bg-foreground/5\" : \"\"}`\n }\n >\n {showDetailColumn && (\n <td\n className={isOddRow ? detailCellProps.classNameOdd : detailCellProps.className}\n style={detailCellProps.style}\n >\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n aria-label={`${isExpanded ? \"Collapse\" : \"Expand\"} row ${row.id}`}\n aria-expanded={isExpanded}\n onClick={(event) => {\n stopRowClick(event);\n toggleRowExpanded(row.id);\n }}\n >\n <ChevronRight\n className={`h-4 w-4 transition-transform${isExpanded ? \" rotate-90\" : \"\"}`}\n aria-hidden\n />\n </Button>\n </td>\n )}\n {showSelectionColumn && (\n <td\n className={isOddRow ? selectionCellProps.classNameOdd : selectionCellProps.className}\n style={selectionCellProps.style}\n >\n <input\n type=\"checkbox\"\n aria-label={`Select row ${row.id}`}\n checked={selectedIds.has(row.id)}\n onClick={stopRowClick}\n onChange={() => toggleRowSelected(row.id)}\n />\n </td>\n )}\n {/* getVisibleCells() moved behind columnVisibilityFeature in\n v9, which isn't registered here — visibility is already\n resolved above via `visibleColumns`, so every column\n reaching the table is meant to render; getAllCells() is\n the core-only equivalent for that already-filtered set. */}\n {row.getAllCells().map((cell) => {\n const cellProps = bodyCellPropsByColumn.get(cell.column.id);\n return (\n <td key={cell.id} style={cellProps?.style} className={cellProps?.className ?? BODY_TD_BASE_CLASS}>\n {flexRender(cell.column.columnDef.cell, cell.getContext())}\n </td>\n );\n })}\n {showRowActionsColumn && rowActions && (\n <td\n className={isOddRow ? rowActionsCellProps.classNameOdd : rowActionsCellProps.className}\n style={rowActionsCellProps.style}\n >\n <ActionsMenu items={rowActions} ctx={{ row: row.original }} triggerLabel={`Row actions for ${row.id}`} />\n </td>\n )}\n </tr>\n {showDetailColumn && isExpanded && renderDetail && (\n <tr data-testid={`${rowTestId}-detail`}>\n <td colSpan={totalColumnCount} className=\"border-b border-border p-2\">\n {renderDetail(row.original)}\n </td>\n </tr>\n )}\n </tbody>\n );\n }\n\n // One `<tbody>` for the group-header row, then each member row as its own\n // sibling `<tbody>` (via `renderRow`) — a `<tbody>` can't nest inside\n // another, and a `<table>` happily holds any number of them.\n function renderGroupedBucket(bucket: NonNullable<typeof groupedBuckets>[number]): ReactNode {\n const expanded = isGroupExpanded(bucket.key);\n const originalRows = bucket.items.map((row) => row.original);\n return (\n <Fragment key={`group-${bucket.key}`}>\n <tbody>\n <tr data-testid={`group-header-${bucket.key}`}>\n <td\n colSpan={totalColumnCount}\n className={cn(\n \"sticky z-20 border-b p-2 font-medium\",\n zebra ? \"bg-muted\" : \"bg-background\",\n )}\n style={{ top: groupHeaderTop }}\n >\n <button\n type=\"button\"\n className=\"flex w-full items-center gap-1 text-left\"\n onClick={() => toggleGroupExpanded(bucket.key)}\n aria-expanded={expanded}\n >\n <ChevronRight\n className={`h-4 w-4 shrink-0 transition-transform${expanded ? \" rotate-90\" : \"\"}`}\n aria-hidden\n />\n {renderGroupHeader\n ? renderGroupHeader(bucket.key, originalRows, expanded)\n : `${bucket.key} (${originalRows.length})`}\n </button>\n </td>\n </tr>\n </tbody>\n {expanded && bucket.items.map((row) => renderRow(row))}\n </Fragment>\n );\n }\n\n return (\n // `h-full`/`min-h-0` here and on the two wrappers below are no-ops unless\n // a consumer itself gives <DataGrid> a bounded height (e.g. wraps it in\n // its own `flex-1 min-h-0` container) -- percentage heights fall back to\n // `auto` with no definite ancestor height, so every existing natural-\n // height usage renders unchanged. Wrap it in a bounded container, though,\n // and this cascades down to the actual scrolling div below instead of\n // silently rendering at full unbounded content height, which used to\n // force any consumer wanting a bounded, page-filling grid to add its own\n // *second* `overflow-auto` wrapper around <DataGrid> -- two nested scroll\n // containers (that one scrolling vertically, this component's own\n // `overflow-x-auto` div scrolling horizontally) fighting over which one\n // a sticky header/pinned column is actually stuck relative to.\n <div className=\"flex h-full min-h-0 flex-col gap-2\" data-testid=\"datagrid\">\n {showSelectionColumn && headerActions && (\n <div className=\"flex shrink-0 items-center justify-between text-sm\">\n <span>{selectedRows.length} selected</span>\n <ActionsMenu items={headerActions} ctx={{ selectedRows }} triggerLabel=\"Bulk actions\" />\n </div>\n )}\n <div className=\"relative min-h-0 flex-1\">\n {loading && (\n <div\n data-testid=\"datagrid-loading-overlay\"\n className=\"absolute inset-0 z-30 flex items-center justify-center bg-background/60\"\n >\n <Loader2 className=\"h-6 w-6 animate-spin text-muted-foreground\" aria-hidden />\n </div>\n )}\n <div\n ref={scrollRef}\n data-testid={testId}\n className=\"h-full overflow-auto rounded-md border\"\n style={shouldVirtualize ? { maxHeight: virtualize?.maxBodyHeight ?? 480 } : undefined}\n >\n {/*\n `table-fixed` (only once every column has a concrete pixel width —\n see `enableColumnResizing`'s own doc) is required for that width to\n actually determine the rendered column, not just hint at it: the\n browser's default `table-layout: auto` algorithm treats a cell's\n `style.width` as one input among several (including cell content\n across every row) and can render a column far narrower than its\n specified width regardless of what `columnSize()` above computes —\n which silently broke both column resizing (the state updated\n correctly; the table just never reflected it) and pinned columns'\n sticky offset math (each depends on the *rendered* width matching\n `getSize()`). Omitted otherwise so a grid with no explicit widths\n keeps natural, content-driven column sizing.\n */}\n <table className={cn(\"w-full border-collapse text-sm\", enableColumnResizing && \"table-fixed\")}>\n <thead ref={theadRef} className=\"bg-muted\">\n {table.getHeaderGroups().map((headerGroup) => {\n // `headerById` backs both the optional spanning-label row below\n // and this leaf row — `table.getHeaderGroups()` always yields\n // exactly one entry (no TanStack column-grouping feature is\n // registered; `headerGroup?: string` is a rendering-only concern\n // handled entirely by `computeHeaderRuns`), so this map is built\n // fresh per (the single) headerGroup, not hoisted further up.\n const headerById = new Map(headerGroup.headers.map((header) => [header.id, header]));\n\n function renderLeafHeaderCell(column: ColumnDef<TRow>, rowSpan?: number): ReactNode {\n const header = headerById.get(column.id);\n if (!header) return null;\n const sortEntry = state.sort.find((s) => s.field === column.id);\n const sortable = isSortable(column);\n const filterable = isFilterable(column) && column.filterDisplay !== \"row\";\n const cellProps = leafHeaderCellPropsByColumn.get(column.id);\n const headerContent = column.renderHeader ? column.renderHeader(column) : column.header;\n return (\n <th key={header.id} rowSpan={rowSpan} style={cellProps?.style} className={cellProps?.className}>\n <div className=\"flex items-center gap-1\">\n {sortable ? (\n <button\n type=\"button\"\n className=\"flex items-center gap-1 rounded-sm px-1 hover:bg-accent hover:text-accent-foreground\"\n onClick={(event) =>\n toggleSort(column.id, enableMultiSort && event.shiftKey, column.sortDescFirst === true)\n }\n >\n {headerContent}\n {sortEntry?.dir === \"asc\" && <BarsArrowUpIcon className=\"h-3 w-3\" aria-hidden />}\n {sortEntry?.dir === \"desc\" && <BarsArrowDownIcon className=\"h-3 w-3\" aria-hidden />}\n </button>\n ) : (\n // Not wrapped in a `<button>` (unlike the sortable branch above): a\n // disabled `<button>` swallows pointer events for its entire subtree,\n // silently breaking any interactive content a column's own `renderHeader`\n // embeds directly (bare filter inputs, \"select/reject all\" buttons) —\n // clicks never reach them at all, even though nothing looks wrong visually.\n <div className=\"flex items-center gap-1 px-1\">{headerContent}</div>\n )}\n {filterable && (\n <Popover>\n <PopoverTrigger asChild>\n <Button variant=\"ghost\" size=\"icon\" aria-label={`Filter ${column.header}`}>\n <FunnelIcon\n className={cn(\n \"h-3 w-3\",\n filtersByColumn.get(column.id) !== undefined\n ? \"opacity-100 text-primary\"\n : \"opacity-40\",\n )}\n aria-hidden\n />\n </Button>\n </PopoverTrigger>\n <PopoverContent>{renderFilterWidget(column)}</PopoverContent>\n </Popover>\n )}\n </div>\n {enableColumnResizing && (\n <div\n data-testid={`resize-handle-${column.id}`}\n onMouseDown={header.getResizeHandler()}\n onTouchStart={header.getResizeHandler()}\n className=\"absolute right-0 top-0 h-full w-1 cursor-col-resize touch-none select-none hover:bg-accent\"\n />\n )}\n </th>\n );\n }\n\n return (\n <Fragment key={headerGroup.id}>\n {hasHeaderGroups && (\n <tr key={`${headerGroup.id}-groups`} data-testid=\"header-group-row\" className=\"divide-x divide-border\">\n {showDetailColumn && (\n <th\n rowSpan={2}\n className={detailHeaderCellProps.className}\n style={detailHeaderCellProps.style}\n aria-hidden\n />\n )}\n {showSelectionColumn && (\n <th\n rowSpan={2}\n className={selectionHeaderCellProps.className}\n style={selectionHeaderCellProps.style}\n >\n <input\n type=\"checkbox\"\n aria-label=\"Select all rows on this page\"\n checked={allOnPageSelected}\n onChange={toggleAllOnPageSelected}\n />\n </th>\n )}\n {headerRuns.map((run) =>\n run.kind === \"single\" ? (\n renderLeafHeaderCell(run.column, 2)\n ) : (\n <th\n key={`group-${run.label}-${run.columns[0]!.id}`}\n colSpan={run.columns.length}\n className=\"border-b border-border p-2 text-center font-medium\"\n >\n {run.label}\n </th>\n ),\n )}\n {showRowActionsColumn && (\n <th\n rowSpan={2}\n className={rowActionsHeaderCellProps.className}\n style={rowActionsHeaderCellProps.style}\n aria-label=\"Row actions\"\n />\n )}\n </tr>\n )}\n <tr key={headerGroup.id} className=\"divide-x divide-border\">\n {!hasHeaderGroups && showDetailColumn && (\n <th\n className={detailHeaderCellProps.className}\n style={detailHeaderCellProps.style}\n aria-hidden\n />\n )}\n {!hasHeaderGroups && showSelectionColumn && (\n <th\n className={selectionHeaderCellProps.className}\n style={selectionHeaderCellProps.style}\n >\n <input\n type=\"checkbox\"\n aria-label=\"Select all rows on this page\"\n checked={allOnPageSelected}\n onChange={toggleAllOnPageSelected}\n />\n </th>\n )}\n {headerGroup.headers.map((header) => {\n const column = columnById.get(header.id);\n // Ungrouped columns already rendered (with rowSpan={2})\n // in the spanning-label row above — only a column that's\n // part of an actual headerGroup run needs its own cell\n // in this leaf row.\n if (!column || (hasHeaderGroups && !column.headerGroup)) return null;\n return renderLeafHeaderCell(column);\n })}\n {!hasHeaderGroups && showRowActionsColumn && (\n <th\n className={rowActionsHeaderCellProps.className}\n style={rowActionsHeaderCellProps.style}\n aria-label=\"Row actions\"\n />\n )}\n </tr>\n </Fragment>\n );\n })}\n {hasFilterRow && (\n <tr data-testid=\"filter-row\" className=\"divide-x divide-border\">\n {showDetailColumn && (\n <th className={detailHeaderCellProps.className} style={detailHeaderCellProps.style} />\n )}\n {showSelectionColumn && (\n <th\n className={selectionHeaderCellProps.className}\n style={selectionHeaderCellProps.style}\n />\n )}\n {visibleColumns.map((column) => {\n const cellProps = filterHeaderCellPropsByColumn.get(column.id);\n return (\n <th key={column.id} style={cellProps?.style} className={cellProps?.className}>\n {isFilterable(column) && column.filterDisplay === \"row\" ? renderFilterWidget(column) : null}\n </th>\n );\n })}\n {showRowActionsColumn && (\n <th\n className={rowActionsHeaderCellProps.className}\n style={rowActionsHeaderCellProps.style}\n />\n )}\n </tr>\n )}\n </thead>\n {rows.length === 0 ? (\n <tbody>\n <tr>\n <td colSpan={totalColumnCount} className=\"p-4 text-center text-muted-foreground\">\n {loading ? \"Loading...\" : \"No results.\"}\n </td>\n </tr>\n </tbody>\n ) : shouldVirtualize ? (\n <>\n {paddingTop > 0 && (\n <tbody>\n <tr aria-hidden=\"true\">\n <td colSpan={totalColumnCount} style={{ padding: 0, border: \"none\", height: paddingTop }} />\n </tr>\n </tbody>\n )}\n {virtualItems.map((virtualItem) =>\n renderRow(tableRows[virtualItem.index]!, (el) => virtualizer.measureElement(el)),\n )}\n {paddingBottom > 0 && (\n <tbody>\n <tr aria-hidden=\"true\">\n <td colSpan={totalColumnCount} style={{ padding: 0, border: \"none\", height: paddingBottom }} />\n </tr>\n </tbody>\n )}\n </>\n ) : groupedBuckets ? (\n groupedBuckets.map((bucket) => renderGroupedBucket(bucket))\n ) : (\n tableRows.map((row) => renderRow(row))\n )}\n </table>\n </div>\n </div>\n {showPagination && (\n <div className=\"flex shrink-0 items-center justify-between text-sm\">\n <span>\n Page {state.page + 1} of {pageCount} ({rowCount} row{rowCount === 1 ? \"\" : \"s\"})\n </span>\n <div className=\"flex gap-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n disabled={state.page <= 0}\n onClick={() => setPage(state.page - 1)}\n >\n Previous\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n disabled={state.page >= pageCount - 1}\n onClick={() => setPage(state.page + 1)}\n >\n Next\n </Button>\n </div>\n </div>\n )}\n </div>\n );\n}\n","/**\n * Fixed pixel width of the selection-checkbox column both `<DataGrid>` and\n * `<TreeDataGrid>` render themselves, matched by the `width` style applied\n * to the column's own header/body cells. 36px (the shared `Button`'s\n * `size=\"icon\"`) plus 4px of padding on each side — big enough to comfortably\n * hit WCAG's 44px minimum touch target.\n */\nexport const SELECTION_COLUMN_WIDTH = 44;\n","export interface GroupBucket<T> {\n key: string;\n items: T[];\n}\n\n/**\n * Buckets `items` by `groupBy(item)`, preserving first-seen bucket order —\n * no re-sorting of either the buckets or the items within one. Framework-free\n * (no React/TanStack types) so it's independently testable.\n */\nexport function groupRows<T>(items: T[], groupBy: (item: T) => string): GroupBucket<T>[] {\n const buckets = new Map<string, T[]>();\n for (const item of items) {\n const key = groupBy(item);\n const bucket = buckets.get(key);\n if (bucket) bucket.push(item);\n else buckets.set(key, [item]);\n }\n return Array.from(buckets, ([key, groupItems]) => ({ key, items: groupItems }));\n}\n","import { compareValues, evaluateFilter, getFieldValue } from \"../filter/evaluate\";\nimport type { GridState, SortDescriptor } from \"../filter/types\";\n\n/** Multi-column comparator: earlier entries in `sort` take priority; nulls always sort last. */\nfunction makeComparator<TRow>(sort: readonly SortDescriptor[]): (a: TRow, b: TRow) => number {\n return (a, b) => {\n for (const { field, dir } of sort) {\n const av = getFieldValue(a, field);\n const bv = getFieldValue(b, field);\n const aNull = av === null || av === undefined;\n const bNull = bv === null || bv === undefined;\n if (aNull && bNull) continue;\n if (aNull) return 1;\n if (bNull) return -1;\n const cmp = compareValues(av, bv) ?? 0;\n if (cmp !== 0) return dir === \"asc\" ? cmp : -cmp;\n }\n return 0;\n };\n}\n\nexport interface ClientProcessingResult<TRow> {\n rows: TRow[];\n rowCount: number;\n}\n\n/**\n * Applies a `GridState` to an in-memory row array: filter (via\n * `evaluateFilter`, matching sql.py/meili.py semantics), then multi-column\n * sort, then pagination. This is the entire implementation of `\"client\"`\n * DataSource mode — `\"server\"` mode skips this and trusts the caller.\n */\nexport function processClientData<TRow>(\n data: readonly TRow[],\n state: GridState,\n): ClientProcessingResult<TRow> {\n const filtered = state.filter ? data.filter((row) => evaluateFilter(row, state.filter)) : data;\n const sorted = state.sort.length > 0 ? [...filtered].sort(makeComparator(state.sort)) : filtered;\n const start = state.page * state.pageSize;\n const rows = sorted.slice(start, start + state.pageSize);\n return { rows, rowCount: sorted.length };\n}\n","import { useCallback, useEffect, useMemo, useState } from \"react\";\nimport type { FilterDescriptor, GridState, SortDescriptor } from \"../filter/types\";\nimport { fieldKey } from \"../filter/types\";\nimport { useDebouncedCallback } from \"../hooks/useDebouncedCallback\";\nimport type { DataSource } from \"./types\";\n\nconst DEFAULT_STATE: GridState = { filter: null, sort: [], page: 0, pageSize: 20 };\nconst FILTER_CHANGE_DEBOUNCE_MS = 300;\n\nexport interface GridStateController {\n state: GridState;\n /** Per-column FilterDescriptor, keyed by column id. */\n filtersByColumn: ReadonlyMap<string, FilterDescriptor>;\n setColumnFilter: (columnId: string, next: FilterDescriptor | undefined) => void;\n /**\n * Cycles a column's sort: none -> asc -> desc -> none, or none -> desc -> asc -> none\n * when `descFirst` is true (see `BaseColumn.sortDescFirst`) — for a column whose\n * \"interesting\" direction is descending (revenue, risk scores, backlog), so a first\n * click surfaces the highest/most-urgent values instead of the lowest/safest ones.\n * Adds/replaces per `enableMultiSort`.\n */\n toggleSort: (columnId: string, additive: boolean, descFirst?: boolean) => void;\n setPage: (page: number) => void;\n setPageSize: (pageSize: number) => void;\n}\n\nfunction nextSortDir(\n current: SortDescriptor[\"dir\"] | undefined,\n descFirst: boolean,\n): SortDescriptor[\"dir\"] | undefined {\n const [first, second] = descFirst ? ([\"desc\", \"asc\"] as const) : ([\"asc\", \"desc\"] as const);\n if (current === undefined) return first;\n if (current === first) return second;\n return undefined;\n}\n\n/**\n * Owns `GridState` (filter/sort/page/pageSize) for `<DataGrid>` and, in\n * `\"server\"` mode, calls `dataSource.onStateChange` on every change —\n * debounced ~300ms for filter changes (typing), immediately for sort/page\n * changes. In `\"client\"` mode `onStateChange` doesn't exist and is never\n * called; the grid re-filters/re-sorts the in-memory array itself instead.\n *\n * Deliberately takes no `columns` and enforces no `sortable`/`filterable`\n * policy itself — `toggleSort`/`setColumnFilter` will happily act on any\n * column id passed to them. `<DataGrid>` is the only place that consults\n * `isSortable`/`isFilterable` (in its rendered header, gating the sort\n * button's `onClick` and whether a filter icon renders at all), because this\n * hook is a generic state container, not a per-column-policy enforcer. A\n * caller that imports this hook directly (bypassing <DataGrid>'s UI\n * entirely) is responsible for applying that same policy itself before\n * calling these setters, the same way it's responsible for its own markup.\n *\n * `externalState`, when given (from the very first render on), makes this\n * fully caller-controlled: whenever it changes identity, internal `state`\n * is overwritten to match (restoring a saved filter, clearing everything at\n * once, applying a URL-restored state — all without remounting\n * `<DataGrid>`). Pass the *same* value back in that a server-mode\n * `dataSource.onStateChange` call just handed you (mirror it into your own\n * state and pass that straight through) — a fresh object literal recreated\n * every render re-triggers this resync every render too. Like a controlled\n * `<input>`, don't switch between passing `externalState` and omitting it\n * across the component's lifetime — start controlled and stay controlled,\n * or start uncontrolled and stay uncontrolled; toggling to `undefined`\n * later does not hand ownership back, it just stops resyncing.\n */\nexport function useGridState<TRow>(\n dataSource: DataSource<TRow>,\n initialState?: Partial<GridState>,\n externalState?: GridState,\n): GridStateController {\n const [state, setState] = useState<GridState>(\n () => externalState ?? { ...DEFAULT_STATE, ...initialState },\n );\n\n const notifyRaw = useCallback(\n (next: GridState) => {\n if (dataSource.mode === \"server\") dataSource.onStateChange(next);\n },\n [dataSource],\n );\n const { run: notifyDebounced, cancel: cancelDebouncedNotify } = useDebouncedCallback(\n notifyRaw,\n FILTER_CHANGE_DEBOUNCE_MS,\n );\n\n // Sort/page changes must cancel any pending debounced filter notification\n // first — otherwise a filter edit made just before a sort/page click\n // schedules a notify that fires ~300ms later with its own (now stale)\n // captured state, silently overwriting the immediate sort/page change the\n // caller already received and acted on.\n const notifyNow = useCallback(\n (next: GridState) => {\n cancelDebouncedNotify();\n notifyRaw(next);\n },\n [notifyRaw, cancelDebouncedNotify],\n );\n\n // Pushing a new `externalState` must cancel any pending debounced filter\n // notification the same way notifyNow does for sort/page — otherwise a\n // filter edit made just before the push schedules a notify that fires\n // ~300ms later with its own (now stale) captured state. If the caller\n // mirrors that stale notify back into whatever it passes as\n // `externalState` (the exact two-way pattern this hook's own doc\n // recommends), it would silently resurrect the state this push just\n // overwrote.\n // eslint-disable-next-line react-hooks/exhaustive-deps -- see externalState's own doc above: intentionally only resyncs when its *identity* changes, not on every render.\n useEffect(() => {\n if (!externalState) return;\n cancelDebouncedNotify();\n setState(externalState);\n }, [externalState, cancelDebouncedNotify]);\n\n const filtersByColumn = useMemo(() => {\n const map = new Map<string, FilterDescriptor>();\n for (const child of state.filter?.filters ?? []) {\n if (\"operator\" in child) map.set(fieldKey(child.field), child);\n }\n return map;\n }, [state.filter]);\n\n /**\n * Replaces (or clears) one column's filter and combines every column's\n * current filter with AND — this is the entire filtering model the grid's\n * built-in per-column widgets implement, by design: one FilterDescriptor\n * per filterable column, ANDed together.\n *\n * `GridState.filter` is typed as a general `CompositeFilterDescriptor`\n * (supporting arbitrary AND/OR nesting) because the *type* is shared with\n * server-side consumers (sql.py/meili.py) and custom filter UIs that want\n * to build more elaborate queries. But if `initialState.filter` arrives\n * with `logic: \"or\"` (or any nesting beyond flat per-column leaves), the\n * very next edit through a built-in filter widget rewrites it flat as\n * `{ logic: \"and\", filters: [...] }`, silently discarding that structure.\n * Supporting arbitrary boolean composition in the built-in widgets would\n * require a genuinely different UI (a query builder), which is out of\n * scope here — this is a known, accepted limitation of the column-scoped\n * filtering model, not an oversight.\n */\n const setColumnFilter = useCallback(\n (columnId: string, next: FilterDescriptor | undefined) => {\n setState((prev) => {\n const remaining = (prev.filter?.filters ?? []).filter(\n (f) => !(\"operator\" in f) || fieldKey(f.field) !== columnId,\n );\n const filters = next ? [...remaining, next] : remaining;\n const nextState: GridState = {\n ...prev,\n filter: filters.length > 0 ? { logic: \"and\", filters } : null,\n page: 0,\n };\n notifyDebounced(nextState);\n return nextState;\n });\n },\n [notifyDebounced],\n );\n\n const toggleSort = useCallback(\n (columnId: string, additive: boolean, descFirst = false) => {\n setState((prev) => {\n const existing = prev.sort.find((s) => s.field === columnId);\n const dir = nextSortDir(existing?.dir, descFirst);\n const withoutColumn = prev.sort.filter((s) => s.field !== columnId);\n const base = additive ? withoutColumn : [];\n const sort = dir ? [...base, { field: columnId, dir }] : base;\n const nextState: GridState = { ...prev, sort };\n notifyNow(nextState);\n return nextState;\n });\n },\n [notifyNow],\n );\n\n const setPage = useCallback(\n (page: number) => {\n setState((prev) => {\n const nextState: GridState = { ...prev, page };\n notifyNow(nextState);\n return nextState;\n });\n },\n [notifyNow],\n );\n\n const setPageSize = useCallback(\n (pageSize: number) => {\n setState((prev) => {\n const nextState: GridState = { ...prev, pageSize, page: 0 };\n notifyNow(nextState);\n return nextState;\n });\n },\n [notifyNow],\n );\n\n return { state, filtersByColumn, setColumnFilter, toggleSort, setPage, setPageSize };\n}\n","import { useEffect, useMemo, useRef } from \"react\";\n\nexport interface DebouncedCallback<TArgs extends unknown[]> {\n /** Schedules `callback`, resetting the timer if one is already pending. */\n run: (...args: TArgs) => void;\n /**\n * Discards any pending scheduled call without invoking it. Callers that\n * also have an immediate, non-debounced path to the same effect (see\n * `<DataGrid>`'s `useGridState`, where sort/page changes call the\n * underlying callback immediately) need this to prevent an\n * already-in-flight debounced call from firing later with stale state and\n * overwriting the immediate one.\n */\n cancel: () => void;\n}\n\n/**\n * Returns a stable `{ run, cancel }` pair. `run` delays invoking `callback`\n * until `delayMs` has elapsed since the last call, resetting the timer on\n * every call in between (standard trailing-edge debounce). Used by\n * `<DataGrid>` in `\"server\"` mode to avoid firing `onStateChange` on every\n * keystroke while a filter widget is being typed into.\n */\nexport function useDebouncedCallback<TArgs extends unknown[]>(\n callback: (...args: TArgs) => void,\n delayMs: number,\n): DebouncedCallback<TArgs> {\n const callbackRef = useRef(callback);\n callbackRef.current = callback;\n\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n\n useEffect(() => {\n return () => {\n clearTimeout(timeoutRef.current);\n };\n }, []);\n\n return useMemo(() => {\n return {\n run: (...args: TArgs): void => {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = setTimeout(() => {\n callbackRef.current(...args);\n }, delayMs);\n },\n cancel: (): void => {\n clearTimeout(timeoutRef.current);\n },\n };\n }, [delayMs]);\n}\n","import { useVirtualizer } from \"@tanstack/react-virtual\";\nimport { AlertCircle, ChevronDown, ChevronRight, Loader2 } from \"lucide-react\";\nimport type { ReactElement, ReactNode } from \"react\";\nimport { useEffect, useMemo, useRef } from \"react\";\nimport { alignClassName, defaultFormat } from \"../column/format\";\nimport type { ColumnDef } from \"../column/types\";\nimport { getColumnValue } from \"../column/types\";\nimport { Checkbox } from \"../components/ui/checkbox\";\nimport { SELECTION_COLUMN_WIDTH } from \"../lib/structuralColumns\";\nimport { cn, stopRowClick } from \"../lib/utils\";\nimport { ActionsMenu } from \"../menu/ActionsMenu\";\nimport type { FlatTreeRow } from \"./flattenTree\";\nimport { flattenTree } from \"./flattenTree\";\nimport { computeSelectionStates } from \"./selectionState\";\nimport type { RowSelectionState } from \"./selectionState\";\nimport type { TreeAccessors, TreeDataGridProps } from \"./types\";\nimport { useTreeState } from \"./useTreeState\";\n\nconst EMPTY_SELECTED_IDS: ReadonlySet<string> = new Set();\nconst UNCHECKED_STATE: RowSelectionState = { checked: false, indeterminate: false };\n\nfunction renderCell<TRow>(column: ColumnDef<TRow>, row: TRow): ReactNode {\n const value = getColumnValue(column, row);\n return column.cell ? column.cell(value, row) : defaultFormat(column, value);\n}\n\ninterface TreeCellProps<TRow> {\n flatRow: FlatTreeRow<TRow>;\n column: ColumnDef<TRow>;\n indentSize: number;\n isLoading: boolean;\n error: string | undefined;\n onToggle: () => void;\n onRetry: () => void;\n}\n\nfunction TreeCell<TRow>({\n flatRow,\n column,\n indentSize,\n isLoading,\n error,\n onToggle,\n onRetry,\n}: TreeCellProps<TRow>): ReactElement {\n return (\n <div className=\"flex items-center gap-1\" style={{ paddingLeft: flatRow.depth * indentSize }}>\n {flatRow.hasChildren ? (\n <button\n type=\"button\"\n onClick={(event) => {\n stopRowClick(event);\n onToggle();\n }}\n disabled={isLoading}\n aria-label={flatRow.isExpanded ? \"Collapse\" : \"Expand\"}\n aria-expanded={flatRow.isExpanded}\n className=\"flex h-4 w-4 shrink-0 items-center justify-center text-muted-foreground hover:text-foreground disabled:opacity-50\"\n >\n {isLoading ? (\n <Loader2 className=\"h-3.5 w-3.5 animate-spin\" aria-hidden=\"true\" />\n ) : flatRow.isExpanded ? (\n <ChevronDown className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n ) : (\n <ChevronRight className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n )}\n </button>\n ) : (\n <span className=\"h-4 w-4 shrink-0\" aria-hidden=\"true\" />\n )}\n <span className=\"truncate\">{renderCell(column, flatRow.row)}</span>\n {/* Rendered on the parent row itself rather than as a synthetic extra\n row, so the flattened row count (and the virtualizer's count) never\n has to account for error state — see useTreeState's module doc for\n why a failed fetch surfaces via errorIds instead of throwing. */}\n {flatRow.isExpanded && error ? (\n <span className=\"ml-1 flex shrink-0 items-center gap-1 text-xs text-destructive\">\n <AlertCircle className=\"h-3 w-3\" aria-hidden=\"true\" />\n Failed to load.\n <button type=\"button\" className=\"underline underline-offset-2\" onClick={onRetry}>\n Retry\n </button>\n </span>\n ) : null}\n </div>\n );\n}\n\n/**\n * A lazy-loading tree grid: generalizes a consuming app's contract tree list\n * (a from-scratch hand-rolled hierarchy table with no shared abstraction)\n * into a reusable component on top of the same `ColumnDef<TRow>` system\n * `<DataGrid>` uses. Deliberately does NOT support sorting or client-side\n * filtering — that original hierarchy table has neither (hierarchy order\n * comes from the backend; \"filtering\" there means asking the server for a\n * differently-shaped tree, which is an app-level concern, not something\n * this component can generalize).\n *\n * Row indentation + the expand/collapse chevron are prepended to whichever\n * column's cell matches `treeColumnId` (defaults to `columns[0]`); every\n * other column renders exactly like it would in `<DataGrid>`.\n */\nexport function TreeDataGrid<TRow>({\n columns,\n data,\n treeColumnId,\n getRowId,\n getChildren,\n hasChildren,\n onLoadChildren,\n initialExpandedLevel = 0,\n expanded: controlledExpanded,\n onExpandedChange,\n childrenMap: controlledChildrenMap,\n onChildrenMapChange,\n indentSize = 20,\n getRowProps,\n getRowTestId,\n rowActions,\n virtualizeThreshold = 100,\n estimatedRowHeight = 40,\n maxBodyHeight = 480,\n selectedIds: controlledSelectedIds,\n onSelectedIdsChange,\n getRowSelectionState,\n isRowSelectionDisabled,\n zebra = true,\n}: TreeDataGridProps<TRow>): ReactElement {\n const accessors: TreeAccessors<TRow> = useMemo(\n () => ({ getRowId, getChildren, hasChildren }),\n [getRowId, getChildren, hasChildren],\n );\n\n const { expanded, childrenMap, loadingIds, errorIds, toggleExpand, retry, expandToLevel } = useTreeState({\n data,\n getRowId,\n getChildren,\n hasChildren,\n onLoadChildren,\n expanded: controlledExpanded,\n onExpandedChange,\n childrenMap: controlledChildrenMap,\n onChildrenMapChange,\n });\n\n // Runs exactly once, against the tree as it existed on first render — not\n // re-triggered by later `data`/`initialExpandedLevel` changes, matching\n // `useTreeState`'s \"no implicit reset\" stance (see its module doc): a\n // caller that wants a fresh auto-expand for genuinely different data\n // should remount via a `key` prop, not rely on this effect re-firing.\n const didAutoExpand = useRef(false);\n useEffect(() => {\n if (didAutoExpand.current) return;\n didAutoExpand.current = true;\n if (initialExpandedLevel > 0) void expandToLevel(initialExpandedLevel);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const flatRows = useMemo(\n () => flattenTree(data, accessors, expanded, childrenMap),\n [data, accessors, expanded, childrenMap],\n );\n\n const treeColId = treeColumnId ?? columns[0]?.id;\n const showRowActionsColumn = Boolean(rowActions?.length);\n const showSelectionColumn = controlledSelectedIds !== undefined;\n const totalColumnCount = columns.length + (showRowActionsColumn ? 1 : 0) + (showSelectionColumn ? 1 : 0);\n\n // Computed once (not per visible row) over the whole *loaded* tree, not\n // just `flatRows` — see `computeSelectionStates`'s own doc for why a\n // collapsed parent's indeterminate state still depends on children loaded\n // during an earlier expand. Skipped entirely when there's no selection\n // column to show.\n const selectionStates = useMemo(\n () =>\n showSelectionColumn\n ? computeSelectionStates(data, accessors, childrenMap, controlledSelectedIds ?? EMPTY_SELECTED_IDS)\n : undefined,\n [showSelectionColumn, data, accessors, childrenMap, controlledSelectedIds],\n );\n\n function resolveSelectionState(row: TRow, id: string): RowSelectionState {\n const override = getRowSelectionState?.(row);\n if (override) return { checked: override.checked, indeterminate: override.indeterminate ?? false };\n return selectionStates?.get(id) ?? UNCHECKED_STATE;\n }\n\n function toggleRowSelected(id: string): void {\n if (!controlledSelectedIds) return;\n const next = new Set(controlledSelectedIds);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n onSelectedIdsChange?.(next);\n }\n\n // \"Select all\" operates on the currently visible (flattened) rows, mirroring\n // <DataGrid>'s own \"select all on this page\" — and, like there, on raw\n // `selectedIds` membership specifically (not the derived indeterminate\n // view), since `checked` must always stay exactly `selectedIds.has(id)`.\n const selectableFlatRows = useMemo(\n () => flatRows.filter((flatRow) => !(isRowSelectionDisabled?.(flatRow.row) ?? false)),\n [flatRows, isRowSelectionDisabled],\n );\n const allVisibleSelected =\n showSelectionColumn &&\n selectableFlatRows.length > 0 &&\n selectableFlatRows.every((flatRow) => (controlledSelectedIds as ReadonlySet<string>).has(flatRow.id));\n\n function toggleAllVisibleSelected(): void {\n if (!controlledSelectedIds) return;\n const next = new Set(controlledSelectedIds);\n for (const flatRow of selectableFlatRows) {\n if (allVisibleSelected) next.delete(flatRow.id);\n else next.add(flatRow.id);\n }\n onSelectedIdsChange?.(next);\n }\n\n // `alignClassName(column)` only depends on the column, not the row, so\n // resolving it once per column here — instead of via `cn(\"p-2\",\n // alignClassName(column))` inside the per-row `<td>` loop below — turns an\n // O(rows x columns) `cn()`/twMerge call count into O(columns) per render.\n // `\"p-2\"` and `text-{align}` never conflict (distinct utility groups), so\n // a plain template string is exact, not an approximation.\n const bodyTdClassByColumn = useMemo(() => {\n const map = new Map<string, string>();\n for (const column of columns) map.set(column.id, `p-2 ${alignClassName(column)}`);\n return map;\n }, [columns]);\n\n const scrollRef = useRef<HTMLDivElement>(null);\n const shouldVirtualize = flatRows.length > virtualizeThreshold;\n const virtualizer = useVirtualizer({\n count: flatRows.length,\n getScrollElement: () => scrollRef.current,\n estimateSize: () => estimatedRowHeight,\n overscan: 10,\n enabled: shouldVirtualize,\n });\n\n function renderRow(\n flatRow: FlatTreeRow<TRow>,\n index: number,\n measureRef?: (el: Element | null) => void,\n ): ReactElement {\n const rowProps = getRowProps?.(flatRow.row, flatRow.depth, index);\n const rowTestId = getRowTestId?.(flatRow.row) ?? `tree-row-${flatRow.id}`;\n const isOddRow = zebra && index % 2 === 1;\n return (\n <tr\n key={flatRow.id}\n ref={measureRef}\n {...rowProps}\n // Required by `@tanstack/react-virtual`'s `measureElement` (wired\n // below via `measureRef`): it reads this attribute back off the\n // measured node to know which row it just measured. Without it,\n // `indexFromElement` falls back to -1 for every row (logging its own\n // \"Missing attribute name 'data-index={index}' on measured element\"\n // warning), which the virtualizer's `resizeItem` then discards\n // outright (`index < 0` bails before recording the size) — so a\n // row's real rendered height, e.g. this one growing well past\n // `estimatedRowHeight` once a caller renders an expanded editor\n // inside a cell, never overwrites the estimate. The virtualizer's\n // start/end offsets then drift from the real DOM layout as more\n // dynamically-sized rows accumulate, which can push a still-relevant\n // row out of the computed visible range — unmounting it (and\n // anything with local state/focus inside it) even though it's still\n // meant to be on screen.\n data-index={index}\n data-testid={rowTestId}\n // Same reasoning as <DataGrid>'s own row className: full `cn()`\n // conflict resolution is only needed when a caller-supplied\n // `getRowProps().className` might collide with the zebra stripe's\n // `bg-*` utility — skip straight to a plain string otherwise.\n className={\n rowProps?.className\n ? cn(\n \"border-b border-border divide-x divide-border\",\n rowProps.className as string,\n isOddRow && \"bg-foreground/5\",\n )\n : `border-b border-border divide-x divide-border${isOddRow ? \" bg-foreground/5\" : \"\"}`\n }\n >\n {showSelectionColumn && (\n <td className=\"p-1\" style={{ width: SELECTION_COLUMN_WIDTH }}>\n {(() => {\n const state = resolveSelectionState(flatRow.row, flatRow.id);\n return (\n <Checkbox\n aria-label={`Select row ${flatRow.id}`}\n checked={state.indeterminate ? \"indeterminate\" : state.checked}\n disabled={isRowSelectionDisabled?.(flatRow.row) ?? false}\n onCheckedChange={() => toggleRowSelected(flatRow.id)}\n onClick={stopRowClick}\n />\n );\n })()}\n </td>\n )}\n {columns.map((column) => (\n <td key={column.id} className={bodyTdClassByColumn.get(column.id)}>\n {column.id === treeColId ? (\n <TreeCell\n flatRow={flatRow}\n column={column}\n indentSize={indentSize}\n isLoading={loadingIds.has(flatRow.id)}\n error={errorIds.get(flatRow.id)}\n onToggle={() => toggleExpand(flatRow.row)}\n onRetry={() => retry(flatRow.row)}\n />\n ) : (\n renderCell(column, flatRow.row)\n )}\n </td>\n ))}\n {showRowActionsColumn && rowActions && (\n <td className=\"p-2\">\n <ActionsMenu\n items={rowActions}\n ctx={{ row: flatRow.row }}\n triggerLabel={`Row actions for ${flatRow.id}`}\n />\n </td>\n )}\n </tr>\n );\n }\n\n const virtualItems = shouldVirtualize ? virtualizer.getVirtualItems() : [];\n const paddingTop = virtualItems.length > 0 ? virtualItems[0]!.start : 0;\n const paddingBottom =\n virtualItems.length > 0 ? virtualizer.getTotalSize() - virtualItems[virtualItems.length - 1]!.end : 0;\n\n return (\n <div className=\"flex flex-col gap-2\" data-testid=\"tree-datagrid\">\n <div\n ref={scrollRef}\n className={shouldVirtualize ? \"overflow-y-auto\" : undefined}\n style={shouldVirtualize ? { maxHeight: maxBodyHeight } : undefined}\n >\n <table className=\"w-full border-collapse text-sm\">\n <thead>\n <tr className=\"divide-x divide-border\">\n {showSelectionColumn && (\n <th className=\"border-b border-border p-1\" style={{ width: SELECTION_COLUMN_WIDTH }}>\n <Checkbox\n aria-label=\"Select all visible rows\"\n checked={allVisibleSelected}\n onCheckedChange={toggleAllVisibleSelected}\n />\n </th>\n )}\n {columns.map((column) => (\n <th\n key={column.id}\n style={column.width ? { width: column.width } : undefined}\n className={`border-b border-border p-2 font-medium ${alignClassName(column)}`}\n >\n {column.header}\n </th>\n ))}\n {showRowActionsColumn && <th className=\"border-b border-border p-2\" aria-label=\"Row actions\" />}\n </tr>\n </thead>\n <tbody>\n {flatRows.length === 0 ? (\n <tr>\n <td colSpan={totalColumnCount} className=\"p-4 text-center text-muted-foreground\">\n No results.\n </td>\n </tr>\n ) : shouldVirtualize ? (\n <>\n {paddingTop > 0 && (\n <tr aria-hidden=\"true\">\n <td colSpan={totalColumnCount} style={{ padding: 0, border: \"none\", height: paddingTop }} />\n </tr>\n )}\n {virtualItems.map((virtualItem) =>\n renderRow(flatRows[virtualItem.index]!, virtualItem.index, (el) => virtualizer.measureElement(el)),\n )}\n {paddingBottom > 0 && (\n <tr aria-hidden=\"true\">\n <td\n colSpan={totalColumnCount}\n style={{ padding: 0, border: \"none\", height: paddingBottom }}\n />\n </tr>\n )}\n </>\n ) : (\n flatRows.map((flatRow, index) => renderRow(flatRow, index))\n )}\n </tbody>\n </table>\n </div>\n </div>\n );\n}\n","import type { TreeAccessors } from \"./types\";\n\nexport interface FlatTreeRow<TRow> {\n row: TRow;\n id: string;\n depth: number;\n hasChildren: boolean;\n isExpanded: boolean;\n}\n\n/**\n * Depth-first flatten of `roots` into the currently *visible* rows only —\n * a collapsed node's descendants never enter the output at all, matching\n * `ContractTreelist`'s `flattenContractTree` (the function this generalizes).\n *\n * `childrenOverride` (a node id -> loaded children map) takes priority over\n * `getChildren(row)` when both exist, since it reflects the most recently\n * fetched batch for a lazily-loaded node — `getChildren` may still return a\n * stale/partial eager batch for the same node.\n */\nexport function flattenTree<TRow>(\n roots: TRow[],\n accessors: TreeAccessors<TRow>,\n expanded: Record<string, boolean>,\n childrenOverride: ReadonlyMap<string, TRow[]>,\n): FlatTreeRow<TRow>[] {\n const { getRowId, getChildren } = accessors;\n const resolveHasChildren =\n accessors.hasChildren ?? ((row: TRow) => (getChildren(row)?.length ?? 0) > 0);\n const out: FlatTreeRow<TRow>[] = [];\n\n function visit(row: TRow, depth: number): void {\n const id = getRowId(row);\n const isExpanded = Boolean(expanded[id]);\n out.push({ row, id, depth, hasChildren: resolveHasChildren(row), isExpanded });\n if (!isExpanded) return;\n const children = childrenOverride.get(id) ?? getChildren(row) ?? [];\n for (const child of children) visit(child, depth + 1);\n }\n\n for (const root of roots) visit(root, 0);\n return out;\n}\n","import type { TreeAccessors } from \"./types\";\n\nexport interface RowSelectionState {\n checked: boolean;\n indeterminate: boolean;\n}\n\n/**\n * Bottom-up walk over the *whole loaded* tree (`roots` + `childrenOverride`),\n * not just the currently visible/expanded rows — a collapsed parent can\n * still be indeterminate based on children loaded during an earlier\n * expand-then-collapse (per `useTreeState`'s \"no implicit reset\" cache),\n * which `flattenTree`'s collapse-stopping walk deliberately never visits.\n *\n * `checked` is always exactly `selectedIds.has(id)` — never auto-derived,\n * so `selectedIds` stays the single source of truth for \"is this id\n * selected\" (see `TreeDataGridProps.getRowSelectionState`'s doc for why).\n * `indeterminate` is derived: true when the row itself isn't checked but at\n * least one loaded descendant is checked or itself indeterminate. An\n * unloaded/unexpanded subtree can't be inspected, so it reads as neither.\n */\nexport function computeSelectionStates<TRow>(\n roots: TRow[],\n accessors: TreeAccessors<TRow>,\n childrenOverride: ReadonlyMap<string, TRow[]>,\n selectedIds: ReadonlySet<string>,\n): Map<string, RowSelectionState> {\n const { getRowId, getChildren } = accessors;\n const out = new Map<string, RowSelectionState>();\n\n function visit(row: TRow): RowSelectionState {\n const id = getRowId(row);\n if (selectedIds.has(id)) {\n const state: RowSelectionState = { checked: true, indeterminate: false };\n out.set(id, state);\n return state;\n }\n const children = childrenOverride.get(id) ?? getChildren(row) ?? [];\n let anySelectedOrIndeterminate = false;\n for (const child of children) {\n const childState = visit(child);\n if (childState.checked || childState.indeterminate) anySelectedOrIndeterminate = true;\n }\n const state: RowSelectionState = { checked: false, indeterminate: anySelectedOrIndeterminate };\n out.set(id, state);\n return state;\n }\n\n for (const root of roots) visit(root);\n return out;\n}\n","import { useCallback, useMemo, useState } from \"react\";\nimport type { TreeAccessors } from \"./types\";\n\nexport interface TreeStateController<TRow> {\n expanded: Record<string, boolean>;\n childrenMap: ReadonlyMap<string, TRow[]>;\n loadingIds: ReadonlySet<string>;\n /** Node id -> the message from its most recent failed `onLoadChildren` call. */\n errorIds: ReadonlyMap<string, string>;\n toggleExpand: (row: TRow) => void;\n /** Re-attempts `onLoadChildren` for a node currently in `errorIds`. */\n retry: (row: TRow) => void;\n /** Expands every node from the roots down to (but not including) `level`, fetching lazy children along the way. */\n expandToLevel: (level: number) => Promise<void>;\n}\n\nexport interface UseTreeStateOptions<TRow> extends TreeAccessors<TRow> {\n data: TRow[];\n onLoadChildren?: (row: TRow) => Promise<TRow[]>;\n expanded?: Record<string, boolean>;\n onExpandedChange?: (expanded: Record<string, boolean>) => void;\n childrenMap?: ReadonlyMap<string, TRow[]>;\n onChildrenMapChange?: (childrenMap: ReadonlyMap<string, TRow[]>) => void;\n}\n\n/**\n * Owns everything `<TreeDataGrid>` needs beyond plain rendering: expand\n * state (controlled or internal, same pattern as `<ColumnSelector>`'s\n * `visibility`/`onVisibilityChange`), a per-node lazy-children cache, and\n * per-node loading/error tracking.\n *\n * Deliberately does NOT reset any of this when `data` changes identity —\n * unlike `ContractTreelist`, which resets `expanded`/`childrenMap` on every\n * filter change because filtering there reshapes the whole tree server-side.\n * This hook has no opinion on why `data` changed; a caller that wants a full\n * reset (e.g. switching to a different root entity entirely) should remount\n * `<TreeDataGrid>` via a `key` prop rather than rely on implicit magic here.\n */\nexport function useTreeState<TRow>({\n data,\n getRowId,\n getChildren,\n hasChildren,\n onLoadChildren,\n expanded: controlledExpanded,\n onExpandedChange,\n childrenMap: controlledChildrenMap,\n onChildrenMapChange,\n}: UseTreeStateOptions<TRow>): TreeStateController<TRow> {\n const [internalExpanded, setInternalExpanded] = useState<Record<string, boolean>>({});\n const expanded = controlledExpanded ?? internalExpanded;\n\n const setExpanded = useCallback(\n (updater: (prev: Record<string, boolean>) => Record<string, boolean>) => {\n if (controlledExpanded) {\n onExpandedChange?.(updater(controlledExpanded));\n } else {\n setInternalExpanded(updater);\n }\n },\n [controlledExpanded, onExpandedChange],\n );\n\n const [internalChildrenMap, setInternalChildrenMap] = useState<Map<string, TRow[]>>(new Map());\n const childrenMap = controlledChildrenMap ?? internalChildrenMap;\n\n const setChildrenMap = useCallback(\n (updater: (prev: ReadonlyMap<string, TRow[]>) => ReadonlyMap<string, TRow[]>) => {\n if (controlledChildrenMap) {\n onChildrenMapChange?.(updater(controlledChildrenMap));\n } else {\n setInternalChildrenMap((prev) => new Map(updater(prev)));\n }\n },\n [controlledChildrenMap, onChildrenMapChange],\n );\n\n const [loadingIds, setLoadingIds] = useState<Set<string>>(new Set());\n const [errorIds, setErrorIds] = useState<Map<string, string>>(new Map());\n\n const resolveHasChildren = useMemo(\n () => hasChildren ?? ((row: TRow) => (getChildren(row)?.length ?? 0) > 0),\n [hasChildren, getChildren],\n );\n\n const loadChildrenFor = useCallback(\n async (row: TRow): Promise<TRow[]> => {\n const id = getRowId(row);\n const cached = childrenMap.get(id);\n if (cached) return cached;\n\n const eager = getChildren(row);\n if (eager && eager.length > 0) return eager;\n if (!onLoadChildren) return eager ?? [];\n\n setLoadingIds((prev) => new Set(prev).add(id));\n setErrorIds((prev) => {\n if (!prev.has(id)) return prev;\n const next = new Map(prev);\n next.delete(id);\n return next;\n });\n try {\n const result = await onLoadChildren(row);\n setChildrenMap((prev) => new Map(prev).set(id, result));\n return result;\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Failed to load children.\";\n setErrorIds((prev) => new Map(prev).set(id, message));\n throw error;\n } finally {\n setLoadingIds((prev) => {\n const next = new Set(prev);\n next.delete(id);\n return next;\n });\n }\n },\n [childrenMap, getChildren, getRowId, onLoadChildren, setChildrenMap],\n );\n\n const toggleExpand = useCallback(\n (row: TRow) => {\n const id = getRowId(row);\n const willExpand = !expanded[id];\n setExpanded((prev) => ({ ...prev, [id]: willExpand }));\n // Fetch on the way open, never on the way closed — collapsing only\n // hides rows, it never drops the cache (see the module doc above).\n if (willExpand && resolveHasChildren(row)) {\n loadChildrenFor(row).catch(() => {\n // Deliberately swallowed: the failure is already surfaced via\n // `errorIds` (rendered inline with a retry affordance by\n // `<TreeDataGrid>`) — unlike `ContractTreelist`'s equivalent path\n // (`toggleExpand` -> `fetchChildrenFor` with no `.catch`), which\n // left an unhandled rejection and no visible error at all.\n });\n }\n },\n [expanded, getRowId, loadChildrenFor, resolveHasChildren, setExpanded],\n );\n\n const retry = useCallback(\n (row: TRow) => {\n loadChildrenFor(row).catch(() => {\n // Same reasoning as toggleExpand above.\n });\n },\n [loadChildrenFor],\n );\n\n const expandToLevel = useCallback(\n async (level: number): Promise<void> => {\n const nextExpanded: Record<string, boolean> = { ...expanded };\n\n async function visit(row: TRow, depth: number): Promise<void> {\n if (depth >= level || !resolveHasChildren(row)) return;\n nextExpanded[getRowId(row)] = true;\n let children: TRow[];\n try {\n children = await loadChildrenFor(row);\n } catch {\n // Leave this node expanded-but-empty; errorIds already has the\n // message and `<TreeDataGrid>` renders a retry affordance for it.\n return;\n }\n for (const child of children) await visit(child, depth + 1);\n }\n\n for (const root of data) await visit(root, 0);\n setExpanded(() => nextExpanded);\n },\n [data, expanded, getRowId, loadChildrenFor, resolveHasChildren, setExpanded],\n );\n\n return { expanded, childrenMap, loadingIds, errorIds, toggleExpand, retry, expandToLevel };\n}\n"],"mappings":"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","names":["forwardRef","jsx","forwardRef","jsx","forwardRef","jsx","forwardRef","jsx","jsx","jsxs","parseISO","jsx","jsx","jsxs","parseISO","FunnelIcon","Check","forwardRef","jsx","forwardRef","Check","forwardRef","jsx","forwardRef","jsx","jsxs","FunnelIcon","FunnelIcon","useState","parseISO","rangeOf","jsx","jsxs","useState","FunnelIcon","FunnelIcon","useEffect","useId","useRef","useState","jsx","jsxs","parseNumber","rangeOf","useState","useId","useRef","useEffect","FunnelIcon","FunnelIcon","useEffect","useMemo","useRef","useState","jsx","jsxs","format","useState","useRef","rangeOf","useEffect","useMemo","FunnelIcon","FunnelIcon","useId","jsx","jsxs","DEFAULT_OPERATOR","useId","FunnelIcon","jsx","useEffect","forwardRef","jsx","jsxs","forwardRef","jsx","jsxs","useEffect","useMemo","forwardRef","jsx","forwardRef","jsx","jsx","jsxs","useMemo","FunnelIcon","Fragment","useEffect","useMemo","useRef","useState","useCallback","useEffect","useMemo","useState","useEffect","useMemo","useRef","useState","useCallback","useEffect","useMemo","withoutColumn","Fragment","jsx","jsxs","useState","useMemo","useRef","useEffect","FunnelIcon","useVirtualizer","ChevronDown","ChevronRight","Loader2","useEffect","useMemo","useRef","state","useCallback","useMemo","useState","Fragment","jsx","jsxs","Loader2","ChevronDown","ChevronRight","useMemo","useRef","useEffect","useVirtualizer"]}
|
|
1
|
+
{"version":3,"sources":["../src/filter/types.ts","../src/filter/evaluate.ts","../src/filter/BooleanFilter.tsx","../src/components/ui/button.tsx","../src/lib/utils.ts","../src/components/ui/popover.tsx","../src/components/ui/select.tsx","../src/filter/DateRangeFilter.tsx","../src/components/ui/calendar.tsx","../src/filter/EnumFilter.tsx","../src/components/ui/checkbox.tsx","../src/components/ui/input.tsx","../src/filter/NumberComparisonFilter.tsx","../src/column/format.ts","../src/filter/numberRangeShared.ts","../src/filter/NumberRangeFilter.tsx","../src/filter/NumberHistogramFilter.tsx","../src/column/types.ts","../src/filter/facetedValues.ts","../src/filter/StringFilter.tsx","../src/filter/registry.tsx","../src/edit/widget-types.ts","../src/edit/EditFieldError.tsx","../src/edit/BooleanEditor.tsx","../src/edit/DateEditor.tsx","../src/edit/EnumEditor.tsx","../src/edit/NumberEditor.tsx","../src/edit/StringEditor.tsx","../src/edit/registry.tsx","../src/edit/editingState.ts","../src/edit/renderEditableCell.tsx","../src/edit/EditingBar.tsx","../src/column-selector/ColumnSelector.tsx","../src/components/ui/dialog.tsx","../src/column-selector/persistence.ts","../src/column-selector/visibility.ts","../src/menu/resolveMenuItems.ts","../src/menu/ActionsMenu.tsx","../src/components/ui/dropdown-menu.tsx","../src/grid/DataGrid.tsx","../src/lib/structuralColumns.ts","../src/grid/groupRows.ts","../src/grid/processClientData.ts","../src/grid/useGridState.ts","../src/hooks/useDebouncedCallback.ts","../src/tree/TreeDataGrid.tsx","../src/tree/flattenTree.ts","../src/tree/selectionState.ts","../src/tree/useTreeState.ts"],"sourcesContent":["/**\n * Shared filter/sort contract for @bmsuisse/datagrid.\n *\n * This shape is intentionally mirrored byte-for-byte (field names and operator\n * set) by the Python package `bmsdna.datagrid` (see `bmsdna/datagrid/filters.py`)\n * so that a `GridState` produced by the grid in \"server\" mode can be serialized\n * as JSON and handed directly to a FastAPI endpoint that turns it into SQL or a\n * Meilisearch filter string.\n */\n\n/** The full set of operators supported by any FilterDescriptor. */\nexport type FilterOperator =\n | \"eq\"\n | \"neq\"\n | \"lt\"\n | \"lte\"\n | \"gt\"\n | \"gte\"\n | \"in\"\n | \"notIn\"\n | \"contains\"\n | \"doesNotContain\"\n | \"startsWith\"\n | \"endsWith\"\n | \"isEmpty\"\n | \"isNotEmpty\"\n | \"isNull\"\n | \"isNotNull\"\n | \"between\";\n\n/**\n * A single leaf filter condition.\n *\n * `field` is either a plain field name, or a pre-qualified path (e.g.\n * `[\"c\", \"customer_name\"]` for `c.customer_name` in a join) — the grid never\n * infers joins, callers pre-qualify ambiguous fields themselves.\n *\n * `value` is an array for `in`/`notIn`/`between`, and is omitted entirely for\n * unary operators like `isNull`/`isNotNull`/`isEmpty`/`isNotEmpty`.\n */\nexport interface FilterDescriptor {\n field: string | string[];\n operator: FilterOperator;\n value?: unknown;\n ignoreCase?: boolean;\n}\n\n/** Combines two or more filters (leaf or nested composite) with AND/OR logic. */\nexport interface CompositeFilterDescriptor {\n logic: \"and\" | \"or\";\n filters: (FilterDescriptor | CompositeFilterDescriptor)[];\n}\n\nexport interface SortDescriptor {\n field: string;\n dir: \"asc\" | \"desc\";\n}\n\n/** The full sort/filter/pagination state a <DataGrid> tracks and can emit. */\nexport interface GridState {\n filter: CompositeFilterDescriptor | null;\n sort: SortDescriptor[];\n page: number;\n pageSize: number;\n}\n\n/** True for a leaf `FilterDescriptor`, false for a `CompositeFilterDescriptor`. */\nexport function isFilterDescriptor(\n node: FilterDescriptor | CompositeFilterDescriptor,\n): node is FilterDescriptor {\n return \"operator\" in node;\n}\n\n/** True for a `CompositeFilterDescriptor`, false for a leaf `FilterDescriptor`. */\nexport function isCompositeFilterDescriptor(\n node: FilterDescriptor | CompositeFilterDescriptor,\n): node is CompositeFilterDescriptor {\n return \"logic\" in node;\n}\n\n/** Operators that never carry a `value` (unary presence/absence checks). */\nexport const UNARY_OPERATORS: ReadonlySet<FilterOperator> = new Set([\n \"isEmpty\",\n \"isNotEmpty\",\n \"isNull\",\n \"isNotNull\",\n]);\n\n/** Renders a `field` (string or pre-qualified path) as a single dotted key, for use as a stable id/key. */\nexport function fieldKey(field: string | string[]): string {\n return Array.isArray(field) ? field.join(\".\") : field;\n}\n","/**\n * Client-mode filter evaluator. This is the `\"client\"` DataSource's answer to\n * `sql.py`'s `build_select`/`build_count` and `meili.py`'s\n * `build_filter_expr` — same contract (CompositeFilterDescriptor /\n * FilterDescriptor), same operator semantics, applied in-memory instead of\n * pushed down to a query engine.\n *\n * The operator semantics implemented here MUST stay identical to the Python\n * side (see bmsdna/datagrid/sql.py and bmsdna/datagrid/meili.py) — that's the\n * whole point of \"client\" and \"server\" DataSource modes being interchangeable\n * from the caller's perspective. Key semantics, spelled out because they're\n * easy to get subtly wrong:\n * - `contains`/`startsWith`/`endsWith` are case-sensitive unless `ignoreCase`.\n * - `between` is inclusive on both ends.\n * - `isNull`/`isNotNull` treat both `null` and `undefined` as \"null\".\n * - `isEmpty`/`isNotEmpty` treat `null`/`undefined`/`\"\"`/`[]` as \"empty\".\n * - Every operator (including `eq`/`in`/`neq`/`notIn`/`doesNotContain`) is\n * false against a `null`/`undefined` row value, except the explicitly\n * null-aware operators themselves. This mirrors SQL's three-valued logic\n * collapsed into a WHERE clause: a comparison against NULL is UNKNOWN,\n * which always excludes the row — including for negated comparisons\n * like `<> `/`NOT IN`/`NOT LIKE`, which is easy to get backwards.\n */\nimport { parseISO } from \"date-fns\";\nimport type { CompositeFilterDescriptor, FilterDescriptor } from \"./types\";\nimport { fieldKey, isCompositeFilterDescriptor } from \"./types\";\n\n/**\n * Resolves a (possibly pre-qualified/nested) field path against a row.\n * Exported for reuse by <DataGrid>'s client-mode sort comparator, which\n * needs the exact same field resolution the filter evaluator uses.\n */\nexport function getFieldValue(row: unknown, field: string | string[]): unknown {\n const segments = fieldKey(field).split(\".\");\n let current: unknown = row;\n for (const segment of segments) {\n if (current === null || typeof current !== \"object\") return undefined;\n current = (current as Record<string, unknown>)[segment];\n }\n return current;\n}\n\nfunction isNullish(value: unknown): boolean {\n return value === null || value === undefined;\n}\n\nfunction isEmptyValue(value: unknown): boolean {\n if (isNullish(value)) return true;\n if (typeof value === \"string\") return value.length === 0;\n if (Array.isArray(value)) return value.length === 0;\n return false;\n}\n\n/**\n * Converts a Date, or a string/number parseable as one, to epoch milliseconds.\n *\n * Strings go through date-fns's `parseISO` rather than the bare `new\n * Date(value)` constructor: a `\"YYYY-MM-DD\"` string is parsed by the JS spec\n * as UTC midnight, not local midnight, but `DateRangeFilter` always\n * serializes date-only bounds in *local* time (via date-fns `format`) — so\n * naively parsing one back with `new Date(value)` would shift it by the\n * local UTC offset (e.g. a \"Today\" filter bound of `\"2024-06-15\"` would\n * parse to 2024-06-14T20:00 local in a timezone behind UTC, excluding rows\n * that are actually on the 15th). `parseISO` treats a date-only string as\n * local midnight, matching how it was serialized — the same fix\n * `DateRangeFilter`'s own `parseBound` applies for that widget's *display*,\n * shared here via the same date-fns function instead of a second hand-rolled\n * copy of the \"append a local-time marker\" logic.\n */\nfunction toEpochMs(value: unknown): number | null {\n if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value.getTime();\n if (typeof value === \"number\") return value;\n if (typeof value === \"string\") {\n const parsed = parseISO(value);\n return Number.isNaN(parsed.getTime()) ? null : parsed.getTime();\n }\n return null;\n}\n\n/** A string is \"numeric\" if it round-trips through Number() to a finite value (and isn't blank/whitespace). */\nfunction asFiniteNumber(value: number | string): number | null {\n if (typeof value === \"number\") return Number.isFinite(value) ? value : null;\n if (value.trim() === \"\") return null;\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : null;\n}\n\n/** Normalizes a non-Date value to something orderable: a number or a string. */\nfunction normalizeForCompare(value: unknown): number | string | null {\n if (isNullish(value)) return null;\n if (typeof value === \"number\") return value;\n if (typeof value === \"boolean\") return value ? 1 : 0;\n return String(value);\n}\n\n/**\n * Orders two values. Returns null when either side is nullish, or can't be\n * compared at all (callers treat that as \"the comparison doesn't hold\").\n *\n * When either side is an actual `Date` instance, both sides are parsed as\n * dates and compared chronologically (via epoch ms) — comparing a `Date` to\n * an ISO string by normalizing the `Date` to a number would compare a\n * stringified epoch-ms number against the ISO string lexicographically,\n * which is nonsensical. When neither side is a `Date`, ISO 8601 date/\n * datetime strings already sort correctly under plain string comparison, so\n * no date parsing is needed (or safe to assume) there. If exactly one side\n * normalizes to a number, the other is still tried as a numeric string (e.g.\n * a row value that arrived as `\"9\"` via JSON/CSV/query-string, compared\n * against a NumberRangeFilter's literal `10`) — without this, `\"9\" > \"10\"`\n * lexicographically, inverting every gt/lt/gte/lte/between verdict for a\n * column whose values aren't consistently typed.\n *\n * Exported for reuse by <DataGrid>'s client-mode sort comparator.\n */\nexport function compareValues(a: unknown, b: unknown): number | null {\n if (a instanceof Date || b instanceof Date) {\n const ta = toEpochMs(a);\n const tb = toEpochMs(b);\n return ta === null || tb === null ? null : ta - tb;\n }\n const na = normalizeForCompare(a);\n const nb = normalizeForCompare(b);\n if (na === null || nb === null) return null;\n const numA = asFiniteNumber(na);\n const numB = asFiniteNumber(nb);\n if (numA !== null && numB !== null) return numA - numB;\n const sa = String(na);\n const sb = String(nb);\n if (sa < sb) return -1;\n if (sa > sb) return 1;\n return 0;\n}\n\nfunction valuesEqual(rowValue: unknown, target: unknown, ignoreCase: boolean | undefined): boolean {\n if (ignoreCase && typeof rowValue === \"string\" && typeof target === \"string\") {\n return rowValue.toLowerCase() === target.toLowerCase();\n }\n if (rowValue === target) return true;\n // Falls back to the same numeric-string/Date coercion compareValues uses\n // for gt/lt/between — without this, a row value that arrived as the\n // string \"9\" would fail eq/in against the number 9 (`\"9\" === 9` is\n // false) while correctly satisfying gt/lt against the same pair, an\n // inconsistency between operators that are supposed to agree.\n return compareValues(rowValue, target) === 0;\n}\n\nfunction stringMatch(\n rowValue: unknown,\n needle: unknown,\n ignoreCase: boolean | undefined,\n test: (haystack: string, needle: string) => boolean,\n): boolean {\n if (typeof rowValue !== \"string\" || typeof needle !== \"string\") return false;\n const haystack = ignoreCase ? rowValue.toLowerCase() : rowValue;\n const target = ignoreCase ? needle.toLowerCase() : needle;\n return test(haystack, target);\n}\n\nfunction isTwoElementArray(value: unknown): value is [unknown, unknown] {\n return Array.isArray(value) && value.length === 2;\n}\n\nfunction evaluateLeaf<TRow>(row: TRow, filter: FilterDescriptor): boolean {\n const rowValue = getFieldValue(row, filter.field);\n const { operator, value, ignoreCase } = filter;\n\n switch (operator) {\n case \"isNull\":\n return isNullish(rowValue);\n case \"isNotNull\":\n return !isNullish(rowValue);\n case \"isEmpty\":\n return isEmptyValue(rowValue);\n case \"isNotEmpty\":\n return !isEmptyValue(rowValue);\n\n case \"eq\":\n return !isNullish(rowValue) && valuesEqual(rowValue, value, ignoreCase);\n case \"neq\":\n return !isNullish(rowValue) && !valuesEqual(rowValue, value, ignoreCase);\n\n case \"lt\":\n case \"lte\":\n case \"gt\":\n case \"gte\": {\n const cmp = compareValues(rowValue, value);\n if (cmp === null) return false;\n if (operator === \"lt\") return cmp < 0;\n if (operator === \"lte\") return cmp <= 0;\n if (operator === \"gt\") return cmp > 0;\n return cmp >= 0;\n }\n\n case \"between\": {\n if (isNullish(rowValue) || !isTwoElementArray(value)) return false;\n const [boundA, boundB] = value;\n const cmpA = compareValues(rowValue, boundA);\n const cmpB = compareValues(rowValue, boundB);\n if (cmpA === null || cmpB === null) return false;\n // Order-independent on purpose: a hand-built or saved FilterDescriptor\n // (anything other than NumberRangeFilter's own guarded emit path) may\n // carry its bounds as [larger, smaller]; treating that as \"matches\n // nothing\" rather than as the equivalent valid range is a real, easy\n // mistake to make (see NumberRangeFilter's descriptorFor for the UI\n // widget's side of this same guard).\n return (cmpA >= 0 && cmpB <= 0) || (cmpA <= 0 && cmpB >= 0);\n }\n\n case \"in\":\n return (\n !isNullish(rowValue) &&\n Array.isArray(value) &&\n value.some((candidate) => valuesEqual(rowValue, candidate, ignoreCase))\n );\n case \"notIn\":\n return (\n !isNullish(rowValue) &&\n Array.isArray(value) &&\n !value.some((candidate) => valuesEqual(rowValue, candidate, ignoreCase))\n );\n\n case \"contains\":\n return stringMatch(rowValue, value, ignoreCase, (h, n) => h.includes(n));\n case \"doesNotContain\":\n return !isNullish(rowValue) && !stringMatch(rowValue, value, ignoreCase, (h, n) => h.includes(n));\n case \"startsWith\":\n return stringMatch(rowValue, value, ignoreCase, (h, n) => h.startsWith(n));\n case \"endsWith\":\n return stringMatch(rowValue, value, ignoreCase, (h, n) => h.endsWith(n));\n }\n}\n\n/**\n * Evaluates a filter tree (leaf or composite, or `null`/`undefined` for \"no\n * filter\") against a single row. Used by `<DataGrid>` in `\"client\"` mode;\n * `\"server\"` mode never calls this — it trusts the caller already applied\n * the equivalent filter server-side.\n */\nexport function evaluateFilter<TRow>(\n row: TRow,\n filter: CompositeFilterDescriptor | FilterDescriptor | null | undefined,\n): boolean {\n if (!filter) return true;\n if (isCompositeFilterDescriptor(filter)) {\n return filter.logic === \"and\"\n ? filter.filters.every((child) => evaluateFilter(row, child))\n : filter.filters.some((child) => evaluateFilter(row, child));\n }\n return evaluateLeaf(row, filter);\n}\n","import { FunnelIcon } from \"@heroicons/react/24/outline\";\nimport type { ReactElement } from \"react\";\nimport type { BooleanColumn } from \"../column/types\";\nimport { Button } from \"../components/ui/button\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"../components/ui/popover\";\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"../components/ui/select\";\nimport { cn } from \"../lib/utils\";\nimport type { FilterDescriptor } from \"./types\";\nimport type { FilterWidgetProps } from \"./widget-types\";\n\ntype BooleanChoice = \"all\" | \"yes\" | \"no\";\n\nconst BOOLEAN_CHOICES: readonly { value: BooleanChoice; label: string }[] = [\n { value: \"all\", label: \"All\" },\n { value: \"yes\", label: \"Yes\" },\n { value: \"no\", label: \"No\" },\n];\n\n/**\n * Derives the displayed choice from a FilterDescriptor. This widget only ever\n * emits `eq`, but a `GridState` can round-trip through a URL/server and come\n * back with `neq` (e.g. a foreign caller expressing \"not true\") — `neq` must\n * invert the mapping, or the dropdown would display \"Yes\" for a filter that\n * actually means \"not true\" (i.e. false).\n *\n * `eq`/`neq` are the only operators this maps to a specific choice on\n * purpose: a 3-way All/Yes/No select has exactly three states to represent,\n * and `isNull`/`isNotNull`/`in`/`notIn` don't correspond to any of them\n * unambiguously (e.g. `in: [true]` and `eq: true` both \"mean\" Yes, but `in:\n * [true, false]` means neither Yes nor No nor really \"All\" either). Every\n * other operator falls through to \"All\" rather than guessing — a deliberate\n * scope limit of this specific widget's UI model, not a bug to keep chasing;\n * a caller who needs to represent those distinctly should render its own\n * filter UI for that column via `cell`/a custom filter render, not this one.\n */\nfunction choiceOf(value: FilterDescriptor | undefined): BooleanChoice {\n if (!value) return \"all\";\n if (value.operator === \"eq\") {\n if (value.value === true) return \"yes\";\n if (value.value === false) return \"no\";\n return \"all\";\n }\n if (value.operator === \"neq\") {\n if (value.value === true) return \"no\";\n if (value.value === false) return \"yes\";\n return \"all\";\n }\n return \"all\";\n}\n\n/**\n * Default filter widget for `type: \"boolean\"` columns: a 3-way All/Yes/No\n * select.\n *\n * `bare` (default `false`) skips this component's own Popover/trigger\n * Button, rendering just the select directly — see `EnumFilter`'s identical\n * `bare` doc for why (`renderDefaultFilterWidget` passes this automatically\n * outside `filterDisplay: \"row\"`).\n */\nexport function BooleanFilter<TRow>({\n column,\n value,\n onChange,\n bare = false,\n}: FilterWidgetProps<BooleanColumn<TRow>> & { bare?: boolean }): ReactElement {\n const choice = choiceOf(value);\n const isFiltered = choice !== \"all\";\n const summary = choice === \"yes\" ? \"Yes\" : choice === \"no\" ? \"No\" : \"\";\n\n function emit(next: BooleanChoice): void {\n if (next === \"all\") {\n onChange(undefined);\n return;\n }\n onChange({ field: column.id, operator: \"eq\", value: next === \"yes\" });\n }\n\n const panel = (\n <Select\n value={choice}\n onValueChange={(next) => {\n const match = BOOLEAN_CHOICES.find((option) => option.value === next)?.value;\n if (match) emit(match);\n }}\n >\n <SelectTrigger aria-label={`${column.header} filter`}>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {BOOLEAN_CHOICES.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n );\n\n if (bare) {\n return <div data-testid={`filter-${column.id}`}>{panel}</div>;\n }\n\n return (\n <Popover>\n <PopoverTrigger asChild>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className={cn(\"gap-1\", isFiltered ? \"max-w-[140px] px-2\" : \"w-8 justify-center px-0\")}\n aria-label={`Filter ${column.header}`}\n data-testid={`filter-${column.id}`}\n >\n <FunnelIcon\n className={cn(\"h-3.5 w-3.5 shrink-0\", isFiltered ? \"opacity-100 text-primary\" : \"opacity-40\")}\n aria-hidden\n />\n {isFiltered && <span className=\"truncate\">{summary}</span>}\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-40 p-2\">{panel}</PopoverContent>\n </Popover>\n );\n}\n","import { Slot } from \"@radix-ui/react-slot\";\nimport { type VariantProps, cva } from \"class-variance-authority\";\nimport type { ButtonHTMLAttributes } from \"react\";\nimport { forwardRef } from \"react\";\nimport { cn } from \"../../lib/utils\";\n\nexport const buttonVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n outline: \"border border-input bg-background hover:bg-accent hover:text-accent-foreground\",\n ghost: \"hover:bg-accent hover:text-accent-foreground\",\n destructive: \"bg-destructive text-destructive-foreground hover:bg-destructive/90\",\n },\n size: {\n default: \"h-9 px-3\",\n sm: \"h-8 px-2 text-xs\",\n icon: \"h-9 w-9\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n },\n);\n\nexport interface ButtonProps\n extends ButtonHTMLAttributes<HTMLButtonElement>,\n VariantProps<typeof buttonVariants> {\n asChild?: boolean;\n}\n\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(\n ({ className, variant, size, asChild = false, ...props }, ref) => {\n const Comp = asChild ? Slot : \"button\";\n // `buttonVariants` (via `cva`) already appends `className` itself —\n // through plain `clsx` concatenation, not `twMerge` — so it's already\n // present in `variantClass` below; the separate `cn()` call exists only\n // to resolve conflicts between it and the variant/size classes (e.g. a\n // caller overriding the default `h-9`). Skipping that resolution pass\n // entirely when there's no `className` to conflict with matters here\n // specifically because `<DataGrid>` renders a `<Button>` per visible row\n // (the expand toggle, the row-actions trigger) with no `className` of\n // its own — every scroll-triggered re-render was paying for a full\n // clsx+twMerge parse of an already-final, already-conflict-free string.\n const variantClass = buttonVariants({ variant, size });\n return (\n <Comp className={className ? cn(variantClass, className) : variantClass} ref={ref} {...props} />\n );\n },\n);\nButton.displayName = \"Button\";\n","import type { SyntheticEvent } from \"react\";\nimport { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/** Merges conditional class names, then dedupes conflicting Tailwind utility classes. */\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n\n/**\n * Every interactive control rendered *inside* a row (the expand chevron, the\n * selection checkbox, `rowActions`' kebab trigger) must stop a click from\n * bubbling up to the `<tr>` — otherwise a `getRowProps`-driven row-wide\n * `onClick` (e.g. \"click the row to navigate\") fires alongside whatever the\n * control itself was meant to do, since the control's own click event\n * bubbles to the row by default. Use this on every such control's `onClick`\n * rather than inlining `(event) => event.stopPropagation()` at each call\n * site — a repeated one-liner is easy to miss adding to the *next* per-row\n * control (see ActionsMenu's kebab trigger, which was missed here once\n * already).\n */\nexport function stopRowClick(event: SyntheticEvent): void {\n event.stopPropagation();\n}\n","import * as PopoverPrimitive from \"@radix-ui/react-popover\";\nimport type { ComponentPropsWithoutRef, ElementRef } from \"react\";\nimport { forwardRef } from \"react\";\nimport { cn } from \"../../lib/utils\";\n\nexport const Popover = PopoverPrimitive.Root;\nexport const PopoverTrigger = PopoverPrimitive.Trigger;\nexport const PopoverAnchor = PopoverPrimitive.Anchor;\n\nexport const PopoverContent = forwardRef<\n ElementRef<typeof PopoverPrimitive.Content>,\n ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>\n>(({ className, align = \"start\", sideOffset = 4, ...props }, ref) => (\n <PopoverPrimitive.Portal>\n <PopoverPrimitive.Content\n ref={ref}\n align={align}\n sideOffset={sideOffset}\n className={cn(\n \"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none\",\n className,\n )}\n {...props}\n />\n </PopoverPrimitive.Portal>\n));\nPopoverContent.displayName = \"PopoverContent\";\n","import * as SelectPrimitive from \"@radix-ui/react-select\";\nimport { Check, ChevronDown } from \"lucide-react\";\nimport type { ComponentPropsWithoutRef, ElementRef } from \"react\";\nimport { forwardRef } from \"react\";\nimport { cn } from \"../../lib/utils\";\n\nexport const Select = SelectPrimitive.Root;\nexport const SelectGroup = SelectPrimitive.Group;\nexport const SelectValue = SelectPrimitive.Value;\n\nexport const SelectTrigger = forwardRef<\n ElementRef<typeof SelectPrimitive.Trigger>,\n ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>\n>(({ className, children, ...props }, ref) => (\n <SelectPrimitive.Trigger\n ref={ref}\n className={cn(\n \"flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50\",\n className,\n )}\n {...props}\n >\n {children}\n <SelectPrimitive.Icon asChild>\n <ChevronDown className=\"h-4 w-4 opacity-50\" />\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n));\nSelectTrigger.displayName = \"SelectTrigger\";\n\nexport const SelectContent = forwardRef<\n ElementRef<typeof SelectPrimitive.Content>,\n ComponentPropsWithoutRef<typeof SelectPrimitive.Content>\n>(({ className, children, position = \"popper\", ...props }, ref) => (\n <SelectPrimitive.Portal>\n <SelectPrimitive.Content\n ref={ref}\n position={position}\n className={cn(\n \"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md\",\n position === \"popper\" && \"translate-y-1\",\n className,\n )}\n {...props}\n >\n <SelectPrimitive.Viewport className=\"p-1\">{children}</SelectPrimitive.Viewport>\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n));\nSelectContent.displayName = \"SelectContent\";\n\nexport const SelectItem = forwardRef<\n ElementRef<typeof SelectPrimitive.Item>,\n ComponentPropsWithoutRef<typeof SelectPrimitive.Item>\n>(({ className, children, ...props }, ref) => (\n <SelectPrimitive.Item\n ref={ref}\n className={cn(\n \"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n className,\n )}\n {...props}\n >\n <span className=\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\">\n <SelectPrimitive.ItemIndicator>\n <Check className=\"h-4 w-4\" />\n </SelectPrimitive.ItemIndicator>\n </span>\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n </SelectPrimitive.Item>\n));\nSelectItem.displayName = \"SelectItem\";\n","import { CalendarDaysIcon } from \"@heroicons/react/24/outline\";\nimport { endOfDay, endOfMonth, format, parseISO, startOfDay, startOfMonth, subDays } from \"date-fns\";\nimport type { ReactElement } from \"react\";\nimport type { DateRange } from \"react-day-picker\";\nimport type { DateColumn } from \"../column/types\";\nimport { Button } from \"../components/ui/button\";\nimport { Calendar } from \"../components/ui/calendar\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"../components/ui/popover\";\nimport { cn } from \"../lib/utils\";\nimport type { FilterDescriptor } from \"./types\";\nimport type { FilterWidgetProps } from \"./widget-types\";\n\ninterface Preset {\n key: string;\n label: string;\n range: (today: Date) => DateRange;\n}\n\nconst PRESETS: readonly Preset[] = [\n {\n key: \"today\",\n label: \"Today\",\n range: (today) => ({ from: startOfDay(today), to: endOfDay(today) }),\n },\n {\n key: \"last7\",\n label: \"Last 7 days\",\n range: (today) => ({ from: startOfDay(subDays(today, 6)), to: endOfDay(today) }),\n },\n {\n key: \"thisMonth\",\n label: \"This month\",\n range: (today) => ({ from: startOfMonth(today), to: endOfMonth(today) }),\n },\n];\n\nfunction isStringPair(value: unknown): value is [string, string] {\n return (\n Array.isArray(value) &&\n value.length === 2 &&\n typeof value[0] === \"string\" &&\n typeof value[1] === \"string\"\n );\n}\n\n/**\n * Parses a bound produced by `isoOf`, via date-fns's `parseISO` rather than\n * the bare `new Date(...)` constructor. A bare `\"YYYY-MM-DD\"` string is\n * parsed by the JS spec as UTC midnight, not local midnight — but `isoOf`\n * serialized it via date-fns `format()` in *local* time, so re-parsing it\n * with `new Date(...)` would shift the displayed day backwards in any\n * timezone behind UTC. `parseISO` treats a date-only string as local\n * midnight, matching how it was serialized (the same fix `evaluate.ts`'s\n * `toEpochMs` applies for filter *evaluation*, sharing this date-fns\n * function instead of a second hand-rolled copy of the same logic).\n */\nfunction parseBound(value: string): Date {\n return parseISO(value);\n}\n\nfunction rangeOf(value: FilterDescriptor | undefined): DateRange | undefined {\n if (!value || value.operator !== \"between\" || !isStringPair(value.value)) return undefined;\n const from = parseBound(value.value[0]);\n const to = parseBound(value.value[1]);\n if (Number.isNaN(from.getTime()) || Number.isNaN(to.getTime())) return undefined;\n return { from, to };\n}\n\n/** Serializes a Date as a date-only (`type: \"date\"`) or full ISO (`type: \"datetime\"`) string. */\nfunction isoOf(date: Date, columnType: \"date\" | \"datetime\"): string {\n return columnType === \"datetime\" ? date.toISOString() : format(date, \"yyyy-MM-dd\");\n}\n\n/**\n * Default filter widget for `type: \"date\"` / `type: \"datetime\"` columns: a\n * date-range picker with Today / Last 7 days / This month presets, plus a\n * calendar for a custom range. Always emits the `between` operator\n * (inclusive both ends, matching evaluateFilter/sql.py/meili.py semantics).\n *\n * `today` defaults to the real current date; it's exposed as a prop purely\n * so tests can pin preset computation to a fixed date without mocking the\n * system clock (and the timer machinery that comes with that).\n *\n * `bare` (default `false`) skips this component's own Popover/trigger\n * Button, rendering just the presets + calendar directly — see\n * `EnumFilter`'s identical `bare` doc for why (`renderDefaultFilterWidget`\n * passes this automatically outside `filterDisplay: \"row\"`).\n */\nexport function DateRangeFilter<TRow>({\n column,\n value,\n onChange,\n today = new Date(),\n bare = false,\n}: FilterWidgetProps<DateColumn<TRow>> & { today?: Date; bare?: boolean }): ReactElement {\n const range = rangeOf(value);\n\n function emit(next: DateRange | undefined): void {\n if (!next?.from || !next.to) {\n onChange(undefined);\n return;\n }\n // The presets above already end their range at endOfDay(today); a\n // custom range picked directly from the Calendar does not — clicking a\n // day just returns that day's local midnight, with no time component.\n // For a \"date\" column that's fine (isoOf drops the time entirely), but\n // for \"datetime\" it would serialize the end bound as midnight of the\n // selected day, excluding nearly the entire day from the `between`\n // range. Applying endOfDay here (idempotent for the presets, which are\n // already at end-of-day) makes \"through this day\" the one consistent\n // meaning of the upper bound, regardless of how the range was chosen.\n const to = column.type === \"datetime\" ? endOfDay(next.to) : next.to;\n onChange({\n field: column.id,\n operator: \"between\",\n value: [isoOf(next.from, column.type), isoOf(to, column.type)],\n });\n }\n\n const isFiltered = range?.from !== undefined;\n const summary = range?.from\n ? range.to\n ? `${format(range.from, \"PP\")} - ${format(range.to, \"PP\")}`\n : format(range.from, \"PP\")\n : \"\";\n\n const panel = (\n <div className=\"space-y-2\">\n <div className=\"flex flex-wrap gap-2\">\n {PRESETS.map((preset) => (\n <Button\n key={preset.key}\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => emit(preset.range(today))}\n >\n {preset.label}\n </Button>\n ))}\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => emit(undefined)}>\n Custom\n </Button>\n </div>\n <Calendar mode=\"range\" selected={range} onSelect={emit} numberOfMonths={1} />\n </div>\n );\n\n if (bare) {\n return <div data-testid={`filter-${column.id}`}>{panel}</div>;\n }\n\n return (\n <Popover>\n <PopoverTrigger asChild>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className={cn(\"gap-1\", isFiltered ? \"max-w-[220px] px-2\" : \"w-8 justify-center px-0\")}\n aria-label={`Filter ${column.header}`}\n data-testid={`filter-${column.id}`}\n >\n <CalendarDaysIcon className=\"h-3.5 w-3.5 shrink-0 opacity-70\" aria-hidden />\n {isFiltered && <span className=\"truncate\">{summary}</span>}\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-auto\">{panel}</PopoverContent>\n </Popover>\n );\n}\n","import type { ComponentProps } from \"react\";\nimport { DayPicker } from \"react-day-picker\";\nimport { cn } from \"../../lib/utils\";\n\n/**\n * Thin wrapper around react-day-picker's <DayPicker>, styled minimally via\n * Tailwind utility classes on the root element. Consumers must additionally\n * import \"react-day-picker/style.css\" (or supply their own equivalent day/\n * cell styling) — this package intentionally does not bundle that CSS so it\n * doesn't leak into consumers that style calendars differently.\n */\nexport type CalendarProps = ComponentProps<typeof DayPicker>;\n\nexport function Calendar({ className, ...props }: CalendarProps) {\n return <DayPicker className={cn(\"p-3\", className)} {...props} />;\n}\n","import { FunnelIcon } from \"@heroicons/react/24/outline\";\nimport type { ReactElement } from \"react\";\nimport { useMemo, useState } from \"react\";\nimport type { EnumColumn } from \"../column/types\";\nimport { Button } from \"../components/ui/button\";\nimport { Checkbox } from \"../components/ui/checkbox\";\nimport { Input } from \"../components/ui/input\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"../components/ui/popover\";\nimport { cn } from \"../lib/utils\";\nimport type { FilterDescriptor } from \"./types\";\nimport type { FilterWidgetProps } from \"./widget-types\";\n\nfunction selectedValuesOf(value: FilterDescriptor | undefined): string[] {\n if (!value || value.operator !== \"in\" || !Array.isArray(value.value)) return [];\n return value.value.filter((entry): entry is string => typeof entry === \"string\");\n}\n\nfunction toggle(selected: string[], optionValue: string): string[] {\n return selected.includes(optionValue)\n ? selected.filter((v) => v !== optionValue)\n : [...selected, optionValue];\n}\n\n/**\n * Default filter widget for `type: \"enum\"` columns: a single Excel-style\n * checkbox list, regardless of how many `column.options` there are (no\n * short-list/long-list branch) —\n *\n * - a search input filters which options *render* below via a plain\n * substring match against each option's label; it never touches the\n * committed filter on its own.\n * - a tri-state \"Select all\" checkbox acts on whatever subset is currently\n * visible after that search, not the full option set — matching Excel's\n * own column-filter behavior (\"select all of what's shown\").\n * - every visible option gets its own plain checkbox + label row (a real\n * shadcn `Checkbox`, not a menu item), inside a scrollable list so long\n * option lists don't blow up the popover's height.\n * - every toggle emits the updated `FilterDescriptor` immediately — there's\n * no OK/Cancel step, matching every other filter widget in this package.\n *\n * `bare` (default `false`) skips this component's own Popover/trigger\n * Button, rendering just the search input + option list directly — for a\n * caller that already provides its own popover/collapsible (this is what\n * `renderDefaultFilterWidget` passes automatically outside `filterDisplay:\n * \"row\"`, so `<DataGrid>`'s own header-icon popover isn't nested inside a\n * second one of this component's own).\n */\nexport function EnumFilter<TRow>({\n column,\n value,\n onChange,\n bare = false,\n}: FilterWidgetProps<EnumColumn<TRow>> & { bare?: boolean }): ReactElement {\n const [search, setSearch] = useState(\"\");\n const selected = selectedValuesOf(value);\n\n const visibleOptions = useMemo(() => {\n const term = search.trim().toLowerCase();\n if (!term) return column.options;\n return column.options.filter((option) => option.label.toLowerCase().includes(term));\n }, [column.options, search]);\n\n function emit(next: string[]): void {\n onChange(next.length === 0 ? undefined : { field: column.id, operator: \"in\", value: next });\n }\n\n const visibleValues = visibleOptions.map((option) => option.value);\n const visibleSelectedCount = visibleValues.filter((v) => selected.includes(v)).length;\n const allVisibleSelected = visibleValues.length > 0 && visibleSelectedCount === visibleValues.length;\n const someVisibleSelected = visibleSelectedCount > 0 && !allVisibleSelected;\n\n /** Selects/deselects every currently-*visible* option, leaving any selection outside the search term untouched. */\n function toggleSelectAllVisible(): void {\n if (allVisibleSelected) {\n emit(selected.filter((v) => !visibleValues.includes(v)));\n return;\n }\n const next = new Set(selected);\n for (const v of visibleValues) next.add(v);\n emit([...next]);\n }\n\n const isFiltered = selected.length > 0;\n const summary = `${selected.length} selected`;\n\n const panel = (\n <div className={bare ? \"flex w-full flex-col gap-2\" : \"flex w-64 flex-col gap-2 p-2\"}>\n <Input\n placeholder={`Search ${column.header.toLowerCase()}...`}\n value={search}\n onChange={(event) => setSearch(event.target.value)}\n />\n <label className=\"flex items-center gap-2 border-b px-1 pb-2 text-sm\">\n <Checkbox\n checked={someVisibleSelected ? \"indeterminate\" : allVisibleSelected}\n disabled={visibleValues.length === 0}\n onCheckedChange={toggleSelectAllVisible}\n aria-label=\"Select all\"\n />\n Select all\n </label>\n <div className=\"flex max-h-60 flex-col gap-1 overflow-y-auto\">\n {visibleOptions.length === 0 ? (\n <p className=\"py-2 text-center text-sm text-muted-foreground\">No matches.</p>\n ) : (\n visibleOptions.map((option) => (\n <label\n key={option.value}\n className=\"flex items-center gap-2 rounded-sm px-1 py-1 text-sm hover:bg-accent hover:text-accent-foreground\"\n >\n <Checkbox\n checked={selected.includes(option.value)}\n onCheckedChange={() => emit(toggle(selected, option.value))}\n />\n {option.label}\n </label>\n ))\n )}\n </div>\n </div>\n );\n\n if (bare) {\n return <div data-testid={`filter-${column.id}`}>{panel}</div>;\n }\n\n return (\n <Popover>\n <PopoverTrigger asChild>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className={cn(\"gap-1\", isFiltered ? \"max-w-[180px] px-2\" : \"w-8 justify-center px-0\")}\n aria-label={`Filter ${column.header}`}\n data-testid={`filter-${column.id}`}\n >\n <FunnelIcon\n className={cn(\"h-3.5 w-3.5 shrink-0\", isFiltered ? \"opacity-100 text-primary\" : \"opacity-40\")}\n aria-hidden\n />\n {isFiltered && <span className=\"truncate\">{summary}</span>}\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-64 p-2\">{panel}</PopoverContent>\n </Popover>\n );\n}\n","import * as CheckboxPrimitive from \"@radix-ui/react-checkbox\";\nimport { Check, Minus } from \"lucide-react\";\nimport type { ComponentPropsWithoutRef, ElementRef } from \"react\";\nimport { forwardRef } from \"react\";\nimport { cn } from \"../../lib/utils\";\n\n/**\n * Plain tri-state checkbox (checked / unchecked / `indeterminate`), backed\n * by Radix's own Checkbox primitive rather than a native `<input>` — unlike\n * the native element, Radix exposes `indeterminate` as a first-class\n * `checked` value (`\"indeterminate\"`) instead of an imperative DOM property\n * a `ref` effect has to poke, and it renders with `role=\"checkbox\"` +\n * `aria-checked=\"mixed\"` for free.\n */\nexport const Checkbox = forwardRef<\n ElementRef<typeof CheckboxPrimitive.Root>,\n ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>\n>(({ className, ...props }, ref) => (\n <CheckboxPrimitive.Root\n ref={ref}\n className={cn(\n // A fixed 3px radius, not the theme's scaled `rounded-sm` — that\n // token is meant for buttons/cards sized well above 16px, and on a\n // box this small it reads as a circle (radio button) instead of a\n // checkbox once an app's `--radius` grows past shadcn's default.\n \"peer h-4 w-4 shrink-0 rounded-[3px] border border-primary shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[state=indeterminate]:bg-primary data-[state=indeterminate]:text-primary-foreground\",\n className,\n )}\n {...props}\n >\n <CheckboxPrimitive.Indicator className=\"flex items-center justify-center text-current\">\n {props.checked === \"indeterminate\" ? (\n <Minus className=\"h-3 w-3\" />\n ) : (\n <Check className=\"h-3 w-3\" />\n )}\n </CheckboxPrimitive.Indicator>\n </CheckboxPrimitive.Root>\n));\nCheckbox.displayName = \"Checkbox\";\n","import type { InputHTMLAttributes } from \"react\";\nimport { forwardRef } from \"react\";\nimport { cn } from \"../../lib/utils\";\n\nexport type InputProps = InputHTMLAttributes<HTMLInputElement>;\n\nexport const Input = forwardRef<HTMLInputElement, InputProps>(({ className, type, ...props }, ref) => {\n return (\n <input\n type={type}\n className={cn(\n \"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50\",\n className,\n )}\n ref={ref}\n {...props}\n />\n );\n});\nInput.displayName = \"Input\";\n","import { FunnelIcon } from \"@heroicons/react/24/outline\";\nimport type { ReactElement } from \"react\";\nimport { useEffect, useId, useRef, useState } from \"react\";\nimport { defaultFormat } from \"../column/format\";\nimport type { NumberColumn } from \"../column/types\";\nimport { Button } from \"../components/ui/button\";\nimport { Input } from \"../components/ui/input\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"../components/ui/popover\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"../components/ui/select\";\nimport { cn } from \"../lib/utils\";\nimport { isNumberPair } from \"./numberRangeShared\";\nimport type { FilterDescriptor, FilterOperator } from \"./types\";\nimport type { FilterWidgetProps } from \"./widget-types\";\n\ntype NumberOperator = \"eq\" | \"neq\" | \"gt\" | \"gte\" | \"lt\" | \"lte\" | \"between\";\n\ninterface NumberOperatorOption {\n value: NumberOperator;\n label: string;\n}\n\nconst NUMBER_OPERATORS: readonly NumberOperatorOption[] = [\n { value: \"gt\", label: \"Greater than\" },\n { value: \"gte\", label: \"Greater than or equal to\" },\n { value: \"lt\", label: \"Less than\" },\n { value: \"lte\", label: \"Less than or equal to\" },\n { value: \"eq\", label: \"Equals\" },\n { value: \"neq\", label: \"Does not equal\" },\n { value: \"between\", label: \"Between\" },\n];\n\nconst OPERATOR_SYMBOL: Record<Exclude<NumberOperator, \"between\">, string> = {\n gt: \">\",\n gte: \"≥\",\n lt: \"<\",\n lte: \"≤\",\n eq: \"=\",\n neq: \"≠\",\n};\n\nconst DEFAULT_OPERATOR: NumberOperator = \"gt\";\n\nconst NUMBER_OPERATOR_VALUES: ReadonlySet<string> = new Set(NUMBER_OPERATORS.map((option) => option.value));\n\nfunction isNumberOperator(operator: FilterOperator | undefined): operator is NumberOperator {\n return operator !== undefined && NUMBER_OPERATOR_VALUES.has(operator);\n}\n\nfunction parseNumber(raw: string): number | undefined {\n if (raw.trim() === \"\") return undefined;\n const parsed = Number(raw);\n return Number.isNaN(parsed) ? undefined : parsed;\n}\n\ninterface ComparisonState {\n operator: NumberOperator;\n text: string;\n text2: string;\n}\n\n/** Derives the widget's own local state from the committed `FilterDescriptor` — see `emit`'s doc for why this isn't just read inline on every render. */\nfunction stateFromValue(value: FilterDescriptor | undefined): ComparisonState {\n if (!value) return { operator: DEFAULT_OPERATOR, text: \"\", text2: \"\" };\n if (value.operator === \"between\" && isNumberPair(value.value)) {\n return { operator: \"between\", text: String(value.value[0]), text2: String(value.value[1]) };\n }\n if (isNumberOperator(value.operator) && typeof value.value === \"number\") {\n return { operator: value.operator, text: value.value.toString(), text2: \"\" };\n }\n return { operator: DEFAULT_OPERATOR, text: \"\", text2: \"\" };\n}\n\n/**\n * An Excel-style alternative to the default `NumberRangeFilter`: an operator\n * dropdown (Greater than / Less than / Equals / ... / Between) plus one\n * number input (two for `Between`), instead of a plain always-visible\n * min/max range. Not wired up as any column type's automatic default —\n * `NumberRangeFilter` still is, so existing columns keep their current\n * behavior. Use this directly as a column's custom filter UI (see\n * `BaseColumn.renderFilter`) for a numeric column where users think in\n * terms of \"greater than X\" rather than \"between X and Y\".\n *\n * Local `operator`/text state (rather than deriving straight from `value`\n * on every render, the way `StringFilter` does) is what lets `Between` hold\n * a partially-typed pair — deriving directly would wipe whatever's in the\n * OTHER box the instant the first box's own edit clears `value` back to\n * `undefined` (no filter is emitted until both bounds parse), and would\n * reset the operator dropdown back to the default the same way. The effect\n * below only resyncs from `value` when it changed for some reason other\n * than this widget's own last `onChange` call (an external clear-all, or\n * restoring a saved view) — the same pattern `NumberRangeFilter` uses for\n * the same reason.\n *\n * `bare` (default `true`) skips this component's own Popover/trigger\n * Button, rendering just the operator + input(s) directly — correct under\n * the default `filterDisplay: \"popover\"`, where `<DataGrid>` already\n * supplies the header filter icon and popover; passing `false` there would\n * nest a second, redundant one inside it. Pass `bare={false}` only when the\n * column also sets `filterDisplay: \"row\"`, where nothing else provides a\n * trigger — see `NumberHistogramFilter`'s identical `bare` doc for why.\n */\nexport function NumberComparisonFilter<TRow>({\n column,\n value,\n onChange,\n bare = true,\n}: FilterWidgetProps<NumberColumn<TRow>> & { bare?: boolean }): ReactElement {\n const initial = stateFromValue(value);\n const [operator, setOperator] = useState<NumberOperator>(initial.operator);\n const [text, setText] = useState(initial.text);\n const [text2, setText2] = useState(initial.text2);\n const operatorId = useId();\n const valueId = useId();\n\n const lastEmittedRef = useRef<FilterDescriptor | undefined>(value);\n\n useEffect(() => {\n if (value === lastEmittedRef.current) return;\n lastEmittedRef.current = value;\n const next = stateFromValue(value);\n setOperator(next.operator);\n setText(next.text);\n setText2(next.text2);\n }, [value]);\n\n function emit(nextOperator: NumberOperator, nextText: string, nextText2: string): void {\n let next: FilterDescriptor | undefined;\n if (nextOperator === \"between\") {\n const min = parseNumber(nextText);\n const max = parseNumber(nextText2);\n next = min !== undefined && max !== undefined ? { field: column.id, operator: \"between\", value: [min, max] } : undefined;\n } else {\n const num = parseNumber(nextText);\n next = num !== undefined ? { field: column.id, operator: nextOperator, value: num } : undefined;\n }\n lastEmittedRef.current = next;\n onChange(next);\n }\n\n const isBetween = operator === \"between\";\n const isFiltered = value !== undefined;\n const summary = (() => {\n if (!value) return \"\";\n if (value.operator === \"between\" && isNumberPair(value.value)) {\n const [lo, hi] = value.value;\n return `${defaultFormat(column, lo)} – ${defaultFormat(column, hi)}`;\n }\n if (isNumberOperator(value.operator) && value.operator !== \"between\" && typeof value.value === \"number\") {\n return `${OPERATOR_SYMBOL[value.operator]} ${defaultFormat(column, value.value)}`;\n }\n return \"\";\n })();\n\n const panel = (\n <div className=\"flex w-full max-w-[220px] flex-col gap-2\">\n <Select\n value={operator}\n onValueChange={(next) => {\n const nextOperator = NUMBER_OPERATORS.find((option) => option.value === next)?.value;\n if (!nextOperator) return;\n setOperator(nextOperator);\n emit(nextOperator, text, text2);\n }}\n >\n <SelectTrigger id={operatorId} aria-label={`${column.header} filter operator`}>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {NUMBER_OPERATORS.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <div className=\"flex items-center gap-2\">\n <Input\n id={valueId}\n type=\"number\"\n className=\"min-w-0\"\n aria-label={isBetween ? `${column.header} minimum` : column.header}\n placeholder={isBetween ? \"Min\" : \"Value\"}\n value={text}\n onChange={(event) => {\n setText(event.target.value);\n emit(operator, event.target.value, text2);\n }}\n />\n {isBetween && (\n <>\n <span className=\"shrink-0 text-xs text-muted-foreground\">to</span>\n <Input\n type=\"number\"\n className=\"min-w-0\"\n aria-label={`${column.header} maximum`}\n placeholder=\"Max\"\n value={text2}\n onChange={(event) => {\n setText2(event.target.value);\n emit(operator, text, event.target.value);\n }}\n />\n </>\n )}\n </div>\n </div>\n );\n\n if (bare) {\n return <div data-testid={`filter-${column.id}`}>{panel}</div>;\n }\n\n return (\n <Popover>\n <PopoverTrigger asChild>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className={cn(\"gap-1\", isFiltered ? \"max-w-[180px] px-2\" : \"w-8 justify-center px-0\")}\n aria-label={`Filter ${column.header}`}\n data-testid={`filter-${column.id}`}\n >\n <FunnelIcon\n className={cn(\"h-3.5 w-3.5 shrink-0\", isFiltered ? \"opacity-100 text-primary\" : \"opacity-40\")}\n aria-hidden\n />\n {isFiltered && <span className=\"truncate\">{summary}</span>}\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-auto p-3\">{panel}</PopoverContent>\n </Popover>\n );\n}\n","import { parseISO } from \"date-fns\";\nimport type { ColumnAlign, ColumnDef } from \"./types\";\n\n/** The default horizontal alignment for a column, based purely on its `type`. */\nexport function defaultAlign<TRow>(column: ColumnDef<TRow>): ColumnAlign {\n switch (column.type) {\n case \"number\":\n case \"currency\":\n return \"right\";\n case \"boolean\":\n return \"center\";\n case \"string\":\n case \"enum\":\n case \"date\":\n case \"datetime\":\n return \"left\";\n }\n}\n\n/**\n * Maps a `ColumnAlign` to its literal Tailwind utility class. Written as a\n * static lookup table rather than a template literal (`text-${align}`)\n * because Tailwind's JIT content scanner only picks up class names that\n * appear as literal strings in source — `text-right` in particular never\n * shows up as a literal anywhere else in this package, so it would be\n * silently dropped from a consuming app's build.\n */\nconst ALIGN_CLASS_NAME: Record<ColumnAlign, string> = {\n left: \"text-left\",\n center: \"text-center\",\n right: \"text-right\",\n};\n\n/** The literal Tailwind class for a column's default alignment; see `defaultAlign`. */\nexport function alignClassName<TRow>(column: ColumnDef<TRow>): string {\n return ALIGN_CLASS_NAME[defaultAlign(column)];\n}\n\n/**\n * Parses an arbitrary raw cell value into a `Date`, or `null` if it isn't\n * one. Exported (unlike `toNumber` below, which stays internal) because\n * `edit/DateEditor.tsx` needs the exact same date-only-vs-datetime parsing\n * this module already uses for display — a second copy would risk drifting\n * from the UTC-midnight fix documented below.\n */\nexport function toDate(value: unknown): Date | null {\n if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value;\n if (typeof value === \"number\") {\n const date = new Date(value);\n return Number.isNaN(date.getTime()) ? null : date;\n }\n if (typeof value === \"string\") {\n // parseISO, not `new Date(value)`: a bare \"YYYY-MM-DD\" string (exactly\n // what a \"date\" column's value looks like coming out of e.g. SQLite —\n // see the demo's created_at) is parsed by the JS spec as UTC midnight,\n // not local midnight, which shifts the *displayed* day back one in any\n // timezone behind UTC. This is the same bug already fixed for filter\n // evaluation (evaluate.ts's toEpochMs) and the filter widget's own\n // display (DateRangeFilter's parseBound) — missed here initially\n // because this is a cell-rendering path, not a filtering one, but the\n // underlying string is the same shape and needs the same fix.\n const date = parseISO(value);\n return Number.isNaN(date.getTime()) ? null : date;\n }\n return null;\n}\n\nfunction toNumber(value: unknown): number | null {\n if (typeof value === \"number\") return Number.isNaN(value) ? null : value;\n if (typeof value === \"string\" && value.trim() !== \"\") {\n const parsed = Number(value);\n return Number.isNaN(parsed) ? null : parsed;\n }\n return null;\n}\n\n// `new Intl.NumberFormat(...)` does locale/ICU data lookup on construction\n// and is expensive enough that building one per cell, per row, per render\n// (this used to happen inline in the `\"currency\"` case below) shows up as\n// real GC/CPU pressure once a grid has more than a handful of currency\n// cells on screen. Formatters are immutable and keyed only by currency code\n// here (locale is always `undefined`, i.e. the runtime default), so caching\n// by currency code is exact — never stale.\nconst currencyFormatters = new Map<string, Intl.NumberFormat>();\nfunction currencyFormatter(currency: string): Intl.NumberFormat {\n let formatter = currencyFormatters.get(currency);\n if (!formatter) {\n formatter = new Intl.NumberFormat(undefined, { style: \"currency\", currency });\n currencyFormatters.set(currency, formatter);\n }\n return formatter;\n}\n\n/**\n * Renders a raw cell value as a display string, using the type-appropriate\n * default formatter for `column.type`. Columns with a custom `cell` render\n * function bypass this entirely — it only backs the built-in renderer.\n */\nexport function defaultFormat<TRow>(column: ColumnDef<TRow>, value: unknown): string {\n if (value === null || value === undefined) return \"\";\n\n switch (column.type) {\n case \"string\":\n return String(value);\n\n case \"number\": {\n const num = toNumber(value);\n return num === null ? String(value) : num.toLocaleString();\n }\n\n case \"currency\": {\n const num = toNumber(value);\n if (num === null) return String(value);\n return currencyFormatter(column.currency ?? \"USD\").format(num);\n }\n\n case \"date\": {\n const date = toDate(value);\n return date === null ? String(value) : date.toLocaleDateString();\n }\n\n case \"datetime\": {\n const date = toDate(value);\n return date === null ? String(value) : date.toLocaleString();\n }\n\n case \"boolean\": {\n // A truthy check here would render \"Yes\" for the *string* \"false\" (a\n // real shape for boolean data round-tripped through JSON/CSV/query\n // params) since any non-empty string is truthy — compare explicitly\n // instead of coercing.\n if (typeof value === \"boolean\") return value ? \"Yes\" : \"No\";\n if (typeof value === \"string\") {\n const normalized = value.trim().toLowerCase();\n if (normalized === \"true\") return \"Yes\";\n if (normalized === \"false\") return \"No\";\n }\n if (typeof value === \"number\") return value !== 0 ? \"Yes\" : \"No\";\n return String(value);\n }\n\n case \"enum\": {\n const match = column.options.find((option) => option.value === String(value));\n return match ? match.label : String(value);\n }\n }\n}\n","import type { FilterDescriptor } from \"./types\";\n\nexport interface NumberRange {\n min: number | undefined;\n max: number | undefined;\n}\n\nexport function isNumberPair(value: unknown): value is [number, number] {\n return (\n Array.isArray(value) &&\n value.length === 2 &&\n typeof value[0] === \"number\" &&\n typeof value[1] === \"number\"\n );\n}\n\n/** Reads the current `{min, max}` out of a column's FilterDescriptor, for whichever of `between`/`gte`/`lte` it currently holds. */\nexport function rangeOf(value: FilterDescriptor | undefined): NumberRange {\n if (!value) return { min: undefined, max: undefined };\n if (value.operator === \"between\" && isNumberPair(value.value)) {\n const [min, max] = value.value;\n return { min, max };\n }\n if (value.operator === \"gte\" && typeof value.value === \"number\") {\n return { min: value.value, max: undefined };\n }\n if (value.operator === \"lte\" && typeof value.value === \"number\") {\n return { min: undefined, max: value.value };\n }\n return { min: undefined, max: undefined };\n}\n\n/**\n * Builds the FilterDescriptor for a `{min, max}` pair — `between` when both\n * bounds are set (reordered so the lower bound is always <= the upper\n * bound), `gte`/`lte` when only one is, `undefined` when neither is.\n *\n * Shared by every numeric-range widget (`NumberRangeFilter`,\n * `NumberHistogramFilter`) rather than each re-deriving it, since the\n * bound-reordering guard is easy to get subtly wrong (an inverted pair\n * would otherwise produce a `between` that evaluateFilter/sql.py/meili.py\n * all correctly, but silently, treat as matching nothing).\n */\nexport function descriptorFor(field: string, range: NumberRange): FilterDescriptor | undefined {\n const { min, max } = range;\n if (min !== undefined && max !== undefined) {\n const [lower, upper] = min <= max ? [min, max] : [max, min];\n return { field, operator: \"between\", value: [lower, upper] };\n }\n if (min !== undefined) return { field, operator: \"gte\", value: min };\n if (max !== undefined) return { field, operator: \"lte\", value: max };\n return undefined;\n}\n","import { FunnelIcon } from \"@heroicons/react/24/outline\";\nimport type { ReactElement } from \"react\";\nimport { useEffect, useId, useRef, useState } from \"react\";\nimport { defaultFormat } from \"../column/format\";\nimport type { NumberColumn } from \"../column/types\";\nimport { Button } from \"../components/ui/button\";\nimport { Input } from \"../components/ui/input\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"../components/ui/popover\";\nimport { cn } from \"../lib/utils\";\nimport { descriptorFor, rangeOf } from \"./numberRangeShared\";\nimport type { FilterDescriptor } from \"./types\";\nimport type { FilterWidgetProps } from \"./widget-types\";\n\nfunction parseNumber(raw: string): number | undefined {\n if (raw.trim() === \"\") return undefined;\n const parsed = Number(raw);\n return Number.isNaN(parsed) ? undefined : parsed;\n}\n\n/**\n * Default filter widget for `type: \"number\"` / `type: \"currency\"` columns: a\n * min/max range. Emits `between` when both bounds are set (reordered so the\n * lower bound is always <= the upper bound), or `gte`/`lte` when only one\n * is — all three are evaluated identically by evaluateFilter and sql.py/\n * meili.py.\n *\n * The two input boxes track their own local text rather than deriving\n * straight from `value` on every render. If they derived from `value`\n * directly, reordering an inverted min/max (see above) would relocate\n * whatever the user just typed into the *other* box on the very next\n * render — so typing \"99\" into Min then \"9\" then \"9\" into Max (intending\n * \"99\") would see Max flip to \"99\" after the first \"9\" (since a swap makes\n * it the new max), and the second \"9\" would land in the now-relocated box,\n * producing \"909\" instead of \"99\". Keeping local text means what's on\n * screen always matches exactly what was typed, independent of how the\n * emitted FilterDescriptor's bounds get reordered.\n *\n * `bare` (default `false`) skips this component's own Popover/trigger\n * Button, rendering just the two inputs directly — see `EnumFilter`'s\n * identical `bare` doc for why (`renderDefaultFilterWidget` passes this\n * automatically outside `filterDisplay: \"row\"`). Under `filterDisplay:\n * \"row\"`, the min/max boxes are always visible at their natural width\n * regardless of a narrower column — wrapping them in a compact trigger\n * (bare: false) is what keeps a plain numeric column from forcing its whole\n * column wider than its data ever needs.\n */\nexport function NumberRangeFilter<TRow>({\n column,\n value,\n onChange,\n bare = false,\n}: FilterWidgetProps<NumberColumn<TRow>> & { bare?: boolean }): ReactElement {\n const initial = rangeOf(value);\n const [minText, setMinText] = useState(initial.min?.toString() ?? \"\");\n const [maxText, setMaxText] = useState(initial.max?.toString() ?? \"\");\n const minId = useId();\n const maxId = useId();\n\n // Tracks the exact descriptor object this widget itself last passed to\n // onChange, so the effect below can tell \"value changed because we edited\n // it\" (skip — local text is already right, and re-deriving it would\n // reintroduce the swap-corruption bug documented above) apart from\n // \"value changed for some other reason\" — a caller-driven clear-all-\n // filters, or restoring a different filter entirely (e.g. loading a saved\n // view), both of which this widget would otherwise never notice since it\n // normally drives `value` itself.\n const lastEmittedRef = useRef<FilterDescriptor | undefined>(value);\n\n useEffect(() => {\n if (value === lastEmittedRef.current) return;\n lastEmittedRef.current = value;\n const next = rangeOf(value);\n setMinText(next.min?.toString() ?? \"\");\n setMaxText(next.max?.toString() ?? \"\");\n }, [value]);\n\n function emit(nextMinText: string, nextMaxText: string): void {\n const next = descriptorFor(column.id, {\n min: parseNumber(nextMinText),\n max: parseNumber(nextMaxText),\n });\n lastEmittedRef.current = next;\n onChange(next);\n }\n\n const { min, max } = rangeOf(value);\n const isFiltered = min !== undefined || max !== undefined;\n const summary =\n min !== undefined && max !== undefined\n ? `${defaultFormat(column, min)} – ${defaultFormat(column, max)}`\n : min !== undefined\n ? `≥ ${defaultFormat(column, min)}`\n : max !== undefined\n ? `≤ ${defaultFormat(column, max)}`\n : \"\";\n\n const panel = (\n <div className=\"flex w-full max-w-[220px] items-center gap-2\">\n <Input\n id={minId}\n type=\"number\"\n aria-label={`${column.header} minimum`}\n placeholder=\"Min\"\n className=\"min-w-0\"\n value={minText}\n onChange={(event) => {\n setMinText(event.target.value);\n emit(event.target.value, maxText);\n }}\n />\n <span className=\"shrink-0 text-xs text-muted-foreground\">to</span>\n <Input\n id={maxId}\n type=\"number\"\n aria-label={`${column.header} maximum`}\n placeholder=\"Max\"\n className=\"min-w-0\"\n value={maxText}\n onChange={(event) => {\n setMaxText(event.target.value);\n emit(minText, event.target.value);\n }}\n />\n </div>\n );\n\n if (bare) {\n return <div data-testid={`filter-${column.id}`}>{panel}</div>;\n }\n\n return (\n <Popover>\n <PopoverTrigger asChild>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className={cn(\"gap-1\", isFiltered ? \"max-w-[180px] px-2\" : \"w-8 justify-center px-0\")}\n aria-label={`Filter ${column.header}`}\n data-testid={`filter-${column.id}`}\n >\n <FunnelIcon\n className={cn(\"h-3.5 w-3.5 shrink-0\", isFiltered ? \"opacity-100 text-primary\" : \"opacity-40\")}\n aria-hidden\n />\n {isFiltered && <span className=\"truncate\">{summary}</span>}\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-auto p-3\">{panel}</PopoverContent>\n </Popover>\n );\n}\n","import { FunnelIcon } from \"@heroicons/react/24/outline\";\nimport type { MouseEvent, ReactElement } from \"react\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport type { NumberColumn } from \"../column/types\";\nimport { Button } from \"../components/ui/button\";\nimport { Input } from \"../components/ui/input\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"../components/ui/popover\";\nimport { cn } from \"../lib/utils\";\nimport { descriptorFor, rangeOf } from \"./numberRangeShared\";\nimport type { FilterWidgetProps } from \"./widget-types\";\n\nconst BAR_HEIGHT_PX = 56;\nconst DEFAULT_BUCKETS = 24;\n\n// Signed log transform — spreads skewed financial data (a handful of huge\n// values, a long tail of small ones) evenly across the slider/histogram\n// instead of every bar but one being invisible; handles negative values.\nfunction tLog(v: number): number {\n return Math.sign(v) * Math.log10(Math.abs(v) + 1);\n}\nfunction tInv(t: number): number {\n return Math.sign(t) * (10 ** Math.abs(t) - 1);\n}\n\ntype NumberHistogramValuesProps =\n | {\n /**\n * The full set of values to compute the histogram and slider domain\n * from.\n *\n * MUST be computed excluding this column's OWN active filter (every\n * other active filter is fine, even expected — see\n * `facetedNumberValues`, which computes exactly this). Passing values\n * already narrowed by this same filter reproduces the bug this\n * component's design exists to prevent: the domain shrinks every time\n * the user adjusts the range, so they can never widen it back out, and\n * (if the same \"all rows\" array is reused across multiple numeric\n * columns) filtering one column warps every other numeric column's\n * histogram too.\n *\n * Use this (not `loadValues`) whenever the full, facet-safe row set is\n * already in memory — e.g. `<DataGrid>` in `\"client\"` mode.\n */\n allValues: (number | null | undefined)[];\n loadValues?: never;\n }\n | {\n allValues?: never;\n /**\n * Fetches the histogram/slider domain values on demand, called every\n * time the popover opens (not cached across opens, since every OTHER\n * active filter — which the fetch should itself respect — may have\n * changed since the last time it was open).\n *\n * Use this instead of `allValues` whenever the facet-safe row set\n * ISN'T already in memory — the standard case for `<DataGrid>` in\n * `\"server\"` mode, where the rows the grid holds are just the current\n * page, already narrowed by every active filter (including this\n * column's own), and therefore never safe to pass as `allValues`\n * directly. Typically this closes over a caller's own \"fetch facet\n * values for this column, given every other active filter\" endpoint.\n */\n loadValues: () => Promise<(number | null | undefined)[]>;\n };\n\nexport type NumberHistogramFilterProps<TRow> = FilterWidgetProps<NumberColumn<TRow>> &\n NumberHistogramValuesProps & {\n /** Formats a bound for display. Defaults to `toLocaleString()`. */\n format?: (value: number) => string;\n /** Number of histogram bars. Defaults to 24. */\n buckets?: number;\n /**\n * Set this to `false` only when the column also sets `filterDisplay:\n * \"row\"` — there, nothing else provides a trigger, so this needs its\n * own Popover + Button to avoid permanently expanding the histogram\n * inline in the filter row. Defaults to `true` (bare: no own Popover/\n * Button, just the histogram/slider/inputs/clear content) because a\n * custom `BaseColumn.renderFilter` is normally reached for under the\n * *default* `filterDisplay: \"popover\"`, where `<DataGrid>` already\n * renders a header filter icon that opens a popover around whatever\n * `renderFilter` returns — passing `false` there nests a second,\n * redundant Popover+Button inside the first, so opening the header\n * icon shows nothing but another filter icon to click, not the\n * histogram. There's no `open`/`onOpenChange` state at this level in\n * bare mode — the component simply renders as always-open; the\n * caller's own popover is what mounts/unmounts it.\n */\n bare?: boolean;\n };\n\n/**\n * A richer alternative to the default `NumberRangeFilter`: a log-scale\n * histogram behind a dual-thumb range slider, plus min/max text inputs.\n * Not wired up as any column type's automatic default (unlike\n * `NumberRangeFilter`) — it needs `allValues`/`loadValues` (see above),\n * which the grid itself doesn't have a way to supply automatically yet, so\n * use it directly as a column's custom filter UI (see `BaseColumn.renderFilter`).\n * By default (`bare` unset) this renders bare — just the histogram/slider/\n * inputs, no own trigger/popover — correct under the default `filterDisplay:\n * \"popover\"`, where `<DataGrid>` already supplies the header filter icon and\n * popover. Pass `bare={false}` when the column also sets `filterDisplay:\n * \"row\"`, where nothing else provides a trigger. Compute `allValues` via\n * `facetedNumberValues(data, column, filter)` in client mode, or supply\n * `loadValues` to fetch the facet from the server in server mode.\n */\nexport function NumberHistogramFilter<TRow>({\n column,\n value,\n onChange,\n allValues,\n loadValues,\n format = (v) => v.toLocaleString(),\n buckets = DEFAULT_BUCKETS,\n bare = true,\n}: NumberHistogramFilterProps<TRow>): ReactElement {\n const [openState, setOpen] = useState(false);\n // In bare mode there's no trigger of our own to toggle this -- the caller's\n // own popover controls whether this component is even mounted, so treat it\n // as permanently \"open\" internally (drives the value-computation/resync\n // effects below the same way a real open transition would).\n const open = bare || openState;\n const trackRef = useRef<HTMLDivElement>(null);\n const [minOnTop, setMinOnTop] = useState(false);\n const [loadedValues, setLoadedValues] = useState<(number | null | undefined)[]>([]);\n const [loading, setLoading] = useState(false);\n\n const { min, max } = rangeOf(value);\n\n // `loadValues` re-fetches on every open (not just the first) -- every\n // OTHER active filter it should itself respect may have changed since the\n // popover was last open, and it's cheap to only pay for this while the\n // control is actually visible.\n useEffect(() => {\n if (!open || !loadValues) return;\n let cancelled = false;\n setLoading(true);\n loadValues()\n .then((values) => {\n if (!cancelled) setLoadedValues(values);\n })\n .finally(() => {\n if (!cancelled) setLoading(false);\n });\n return () => {\n cancelled = true;\n };\n }, [open, loadValues]);\n\n const sourceValues = allValues ?? loadedValues;\n\n // O(n) histogram/domain scan only while the popover is actually open —\n // it's not visible (and its values aren't needed for anything else; the\n // closed trigger's label reads `min`/`max` directly, not the domain)\n // while closed, and this can run against a full unfiltered column of\n // however many rows the grid holds.\n const nums = useMemo(\n () => (open ? sourceValues.filter((v): v is number => v != null) : []),\n [sourceValues, open],\n );\n const dataMin = useMemo(() => nums.reduce((a, b) => Math.min(a, b), Infinity), [nums]);\n const dataMax = useMemo(() => nums.reduce((a, b) => Math.max(a, b), -Infinity), [nums]);\n\n const activeMin = min ?? dataMin;\n const activeMax = max ?? dataMax;\n\n // Local text state for the min/max boxes, resynced fresh each time the\n // popover opens (see the effect below) rather than derived straight from\n // `activeMin`/`activeMax` on every render. Deriving directly would mean\n // every keystroke round-trips through emit -> onChange -> the clamped/\n // rounded committed number -> back into the input's `value` — so typing\n // \"150\" character by character re-renders the box to \"1\", then \"15\", but\n // interleaved with whatever userEvent.clear() or cursor-position quirks\n // occur along the way corrupts the result (verified: this exact direct-\n // binding shape, ported faithfully from the original ad hoc version this\n // component replaces, produced \"499\" instead of \"150\" in a plain typing\n // test). `NumberRangeFilter` avoids the same class of bug the same way,\n // for the same reason — see its own docstring.\n const [minText, setMinText] = useState(\"\");\n const [maxText, setMaxText] = useState(\"\");\n\n useEffect(() => {\n if (!open) return;\n setMinText(Number.isFinite(activeMin) ? String(Math.round(activeMin)) : \"\");\n setMaxText(Number.isFinite(activeMax) ? String(Math.round(activeMax)) : \"\");\n // Intentionally resyncs on the open transition and on `loading`\n // flipping false (so a `loadValues` fetch's result populates the boxes\n // once it actually resolves — at the moment `open` first becomes true,\n // an in-flight fetch's `sourceValues` is still `[]`), but not on every\n // activeMin/activeMax change while open otherwise — typing itself\n // drives those via handleMinChange/handleMaxChange below, and\n // re-deriving text from the committed (rounded, clamped) number on\n // every keystroke is exactly the corruption local state exists to avoid.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [open, loading]);\n\n const logDataMin = Number.isFinite(dataMin) ? tLog(dataMin) : 0;\n const logDataMax = Number.isFinite(dataMax) ? tLog(dataMax) : 1;\n const logStep = (logDataMax - logDataMin) / 200;\n\n const logPct = useCallback(\n (v: number) => (logDataMax === logDataMin ? 0 : ((tLog(v) - logDataMin) / (logDataMax - logDataMin)) * 100),\n [logDataMin, logDataMax],\n );\n\n const histBars = useMemo(() => {\n if (!nums.length || dataMin === dataMax || !Number.isFinite(dataMin) || !Number.isFinite(dataMax)) return [];\n const step = (logDataMax - logDataMin) / buckets;\n const counts = Array<number>(buckets).fill(0);\n for (const v of nums) {\n const i = Math.min(Math.floor((tLog(v) - logDataMin) / step), buckets - 1);\n counts[i] = (counts[i] ?? 0) + 1;\n }\n const peak = Math.max(...counts);\n return counts.map((count, i) => ({\n count,\n heightPx: peak > 0 ? Math.max((count / peak) * BAR_HEIGHT_PX, 2) : 2,\n active: tInv(logDataMin + i * step) < activeMax && tInv(logDataMin + (i + 1) * step) > activeMin,\n }));\n }, [nums, dataMin, dataMax, logDataMin, logDataMax, buckets, activeMin, activeMax]);\n\n function emit(nextMin: number | undefined, nextMax: number | undefined): void {\n onChange(\n descriptorFor(column.id, {\n min: nextMin !== undefined && nextMin <= dataMin ? undefined : nextMin,\n max: nextMax !== undefined && nextMax >= dataMax ? undefined : nextMax,\n }),\n );\n }\n\n function handleMinChange(actual: number): void {\n emit(Math.min(actual, activeMax - 1), max);\n }\n\n function handleMaxChange(actual: number): void {\n emit(min, Math.max(actual, activeMin + 1));\n }\n\n function handleTrackHover(event: MouseEvent<HTMLDivElement>): void {\n if (!trackRef.current || !Number.isFinite(dataMin) || !Number.isFinite(dataMax)) return;\n const rect = trackRef.current.getBoundingClientRect();\n const ratio = (event.clientX - rect.left) / rect.width;\n const logHover = logDataMin + ratio * (logDataMax - logDataMin);\n setMinOnTop(Math.abs(logHover - tLog(activeMin)) <= Math.abs(logHover - tLog(activeMax)));\n }\n\n const isFiltered = value !== undefined;\n const summary = isFiltered ? `${format(activeMin)} – ${format(activeMax)}` : \"\";\n\n const panel = (\n <div className={bare ? \"flex w-full flex-col gap-3\" : \"flex w-80 flex-col gap-3\"}>\n {loading && (\n <p className=\"text-xs text-muted-foreground\" data-testid={`filter-${column.id}-loading`}>\n Loading…\n </p>\n )}\n <div className=\"flex items-end gap-px\" style={{ height: BAR_HEIGHT_PX }} data-testid={`filter-${column.id}-histogram`}>\n {histBars.map((bar, i) => (\n <div\n key={i}\n className={cn(\"flex-1 rounded-sm\", bar.active ? \"bg-primary/70\" : \"bg-muted-foreground/20\")}\n style={{ height: bar.heightPx }}\n />\n ))}\n </div>\n\n <div ref={trackRef} className=\"relative h-5\" onMouseMove={handleTrackHover}>\n <input\n type=\"range\"\n aria-label={`${column.header} minimum (slider)`}\n min={logDataMin}\n max={logDataMax}\n step={logStep}\n value={tLog(activeMin)}\n onChange={(event) => handleMinChange(tInv(Number.parseFloat(event.target.value)))}\n className=\"absolute inset-0 h-full w-full cursor-pointer opacity-0\"\n style={{ zIndex: minOnTop ? 5 : 3 }}\n />\n <input\n type=\"range\"\n aria-label={`${column.header} maximum (slider)`}\n min={logDataMin}\n max={logDataMax}\n step={logStep}\n value={tLog(activeMax)}\n onChange={(event) => handleMaxChange(tInv(Number.parseFloat(event.target.value)))}\n className=\"absolute inset-0 h-full w-full cursor-pointer opacity-0\"\n style={{ zIndex: minOnTop ? 3 : 5 }}\n />\n <div className=\"pointer-events-none absolute top-1/2 h-1.5 w-full -translate-y-1/2 rounded-full bg-muted\">\n <div\n className=\"absolute h-full rounded-full bg-primary\"\n style={{ left: `${logPct(activeMin)}%`, right: `${100 - logPct(activeMax)}%` }}\n />\n </div>\n {[activeMin, activeMax].map((v, i) => (\n <div\n key={i}\n className=\"pointer-events-none absolute top-1/2 h-4 w-4 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-primary bg-background shadow\"\n style={{ left: `${logPct(v)}%` }}\n />\n ))}\n </div>\n\n <div className=\"flex items-center gap-2\">\n <Input\n type=\"text\"\n inputMode=\"numeric\"\n aria-label={`${column.header} minimum`}\n value={minText}\n onChange={(event) => {\n setMinText(event.target.value);\n const parsed = Number.parseFloat(event.target.value.replace(/[^0-9.-]/g, \"\"));\n if (!Number.isNaN(parsed)) handleMinChange(parsed);\n }}\n />\n <span className=\"shrink-0 text-xs text-muted-foreground\">to</span>\n <Input\n type=\"text\"\n inputMode=\"numeric\"\n aria-label={`${column.header} maximum`}\n value={maxText}\n onChange={(event) => {\n setMaxText(event.target.value);\n const parsed = Number.parseFloat(event.target.value.replace(/[^0-9.-]/g, \"\"));\n if (!Number.isNaN(parsed)) handleMaxChange(parsed);\n }}\n />\n </div>\n\n {isFiltered && (\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => onChange(undefined)}>\n Clear\n </Button>\n )}\n </div>\n );\n\n if (bare) {\n return <div data-testid={`filter-${column.id}`}>{panel}</div>;\n }\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className={cn(\"gap-1\", isFiltered ? \"max-w-[200px] px-2\" : \"w-8 justify-center px-0\")}\n aria-label={`Filter ${column.header}`}\n data-testid={`filter-${column.id}`}\n >\n <FunnelIcon\n className={cn(\"h-3.5 w-3.5 shrink-0\", isFiltered ? \"opacity-100 text-primary\" : \"opacity-40\")}\n aria-hidden\n />\n {isFiltered && <span className=\"truncate\">{summary}</span>}\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-80\">{panel}</PopoverContent>\n </Popover>\n );\n}\n","import type { ReactNode } from \"react\";\nimport type { CompositeFilterDescriptor, FilterDescriptor } from \"../filter/types\";\n\n/** Horizontal alignment used for a column's cell content and header. */\nexport type ColumnAlign = \"left\" | \"center\" | \"right\";\n\n/** Fields shared by every column type, independent of `type`. */\nexport interface BaseColumn<TRow> {\n id: string;\n accessorKey?: keyof TRow & string;\n accessorFn?: (row: TRow) => unknown;\n header: string;\n width?: number;\n /** Opt-in: defaults to false — must be explicitly set to true to show a sort toggle in the header. */\n sortable?: boolean;\n /**\n * Opt-in: defaults to false, meaning a first click sorts ascending. Set true for a\n * column whose \"interesting\" direction is descending — revenue, risk scores, backlog —\n * so a first click surfaces the highest/most-urgent values first instead of the\n * lowest/safest ones. Cycles desc -> asc -> none instead of the default asc -> desc -> none.\n */\n sortDescFirst?: boolean;\n /** Opt-in: defaults to false — must be explicitly set to true to show a filter icon/widget in the header. */\n filterable?: boolean;\n pinned?: \"left\" | \"right\";\n /** Overrides the default type-based renderer. */\n cell?: (value: unknown, row: TRow) => ReactNode;\n /** Free-text group label for <ColumnSelector>; columns sharing a group are shown together. Ungrouped when omitted. */\n group?: string;\n /**\n * Free-text label for a spanning header cell in `<DataGrid>`'s own thead —\n * unlike `group` above (which only affects `<ColumnSelector>`), this\n * changes the grid's rendered markup. Contiguous *visible* columns sharing\n * the same `headerGroup` string get merged under one `colSpan`'d label\n * cell in an extra row above the normal per-column header row; a column\n * with no `headerGroup` instead spans both header rows itself (via\n * `rowSpan`), so it reads as a single ungrouped column rather than leaving\n * a blank cell above it. Two columns with the same `headerGroup` that\n * AREN'T adjacent in the visible column order (e.g. a differently-grouped\n * or ungrouped column sits between them) form two separate spanning cells\n * with the same label, rather than one — grouping is purely positional,\n * matching how a caller would read the columns left-to-right.\n */\n headerGroup?: string;\n /**\n * Overrides the default type-based filter widget (see\n * `renderDefaultFilterWidget`) for this column. Receives the same\n * `(value, onChange)` pair the default widget would, plus the grid's full\n * current `GridState.filter` (every column's combined filter) as a third\n * argument — needed for a widget like `NumberHistogramFilter`, whose\n * `allValues` prop must be computed via `facetedNumberValues(data, column,\n * filter)` to respect every OTHER active filter while excluding this\n * column's own (see `facetedNumberValues`'s own doc for why). Set this to\n * close over whatever extra data (e.g. the full unfiltered `data`) the\n * custom widget needs.\n */\n renderFilter?: (\n value: FilterDescriptor | undefined,\n onChange: (next: FilterDescriptor | undefined) => void,\n filter: CompositeFilterDescriptor | null,\n ) => ReactNode;\n /**\n * Where this column's filter control renders. Defaults to `\"popover\"`: a\n * filter-icon button in the header that opens the widget in a popover,\n * same as every column gets automatically today. `\"row\"` renders the\n * widget's content directly, inline, in an additional filter row under\n * the header instead — needed for a wide control (e.g.\n * `NumberHistogramFilter`'s slider/histogram) that doesn't fit well\n * collapsed into the header's compact trigger button. The filter row\n * itself is entirely opt-in: it only renders at all if at least one\n * visible, filterable column sets `filterDisplay: \"row\"`.\n */\n filterDisplay?: \"popover\" | \"row\";\n /**\n * Overrides the plain-text `header` rendering *inside* the header cell's\n * sort toggle `<button>` (the sort caret still renders alongside it, and\n * still inside that same button). Use this for a header that needs a\n * tooltip, an info icon, a loading spinner tied to a separate\n * batch-hydration fetch, or any other rich content — `header` itself\n * stays a plain string (used for the filter icon's `aria-label` and by\n * `<ColumnSelector>`, neither of which can work off a ReactNode).\n *\n * Whatever this returns is nested inside a real `<button>` — don't return\n * another focusable/interactive element (a nested `<button>`, a link, a\n * tooltip trigger that itself renders as a `<button>`) or the browser will\n * split the outer button apart, breaking click-to-sort for that column.\n * If the return value is non-textual (an icon with no visible label), the\n * button's accessible name is whatever text content survives inside it —\n * add your own `aria-label` inside the returned content if there isn't\n * any, since `<DataGrid>` has no way to tell your content lacks one.\n */\n renderHeader?: (column: BaseColumn<TRow>) => ReactNode;\n /**\n * Opt-in: defaults to false — must be explicitly set (or return true) for\n * `<DataGrid>` to render this column's cells as inline editors. A function\n * makes it conditional per row (e.g. a \"locked\" row that can't be\n * edited). See `DataGridProps.editing` for the accumulate-then-save\n * workflow this feeds into — editable cells with no `editing` prop\n * supplied just render as static text, same as if `editable` were unset.\n *\n * A cell doesn't become an editor just because this is true, though:\n * clicking (or Enter/Space-ing) any editable cell in a row is what\n * activates editors for every editable column in THAT row at once — the\n * rest of the grid's rows stay static until clicked into themselves. At\n * most one row is active at a time: activating a row deactivates whatever\n * row was active before it (edit row by row, not several open at once) —\n * this does NOT discard the row you switch away from, its own pending\n * edits stay exactly as they were, just no longer shown as editors.\n *\n * Until a cell is activated, its content renders inside a clickable\n * `<span role=\"button\">` (this is what makes clicking it activate the\n * row) — a column that's both `editable` and has a custom `cell`\n * returning its own focusable content (a link, a button) produces nested\n * interactive elements once wrapped this way. Prefer a non-focusable\n * element (styled to look like a link, e.g.) in `cell` for a column\n * that's also `editable`, the same caution `renderHeader` needs under\n * `sortable`.\n */\n editable?: boolean | ((row: TRow) => boolean);\n /**\n * Overrides the default type-based editor (see `renderDefaultEditWidget`)\n * for this column's cells. Receives the cell's current value (the row's\n * original value, or a still-pending edit if the user already changed it\n * this session), the row itself, an `onChange` to record a new pending\n * value, this cell's current validation error message (from\n * `validateEdit` below) if any, and whether this specific cell is the one\n * whose click just activated the row (see `editable`'s own doc) — forward\n * it to your control's native `autoFocus` so the row-activating click also\n * focuses the field the user actually clicked, not just some other cell\n * in the same row.\n */\n renderEditCell?: (\n value: unknown,\n row: TRow,\n onChange: (next: unknown) => void,\n error: string | undefined,\n autoFocus: boolean,\n ) => ReactNode;\n /**\n * Validates a pending edit before it's allowed into `editing.onSave`'s\n * payload. Returning a message string blocks the Save button (and shows\n * the message under the cell) until the value is fixed or reverted to its\n * original; returning `undefined` means valid. Runs on every change to\n * this cell, including the default editors' own `onChange`.\n */\n validateEdit?: (value: unknown, row: TRow) => string | undefined;\n}\n\nexport type StringColumn<TRow> = BaseColumn<TRow> & { type: \"string\" };\n\nexport type NumberColumn<TRow> = BaseColumn<TRow> & {\n type: \"number\" | \"currency\";\n currency?: string;\n};\n\nexport type DateColumn<TRow> = BaseColumn<TRow> & { type: \"date\" | \"datetime\" };\n\nexport type BooleanColumn<TRow> = BaseColumn<TRow> & { type: \"boolean\" };\n\nexport interface EnumOption {\n value: string;\n label: string;\n}\n\nexport type EnumColumn<TRow> = BaseColumn<TRow> & {\n type: \"enum\";\n options: EnumOption[];\n};\n\n/** Discriminated union of every supported column shape, keyed on `type`. */\nexport type ColumnDef<TRow> =\n | StringColumn<TRow>\n | NumberColumn<TRow>\n | DateColumn<TRow>\n | BooleanColumn<TRow>\n | EnumColumn<TRow>;\n\n/** Resolves a column's raw cell value from a row, via `accessorFn` or `accessorKey`. */\nexport function getColumnValue<TRow>(column: ColumnDef<TRow>, row: TRow): unknown {\n if (column.accessorFn) return column.accessorFn(row);\n if (column.accessorKey) return row[column.accessorKey];\n return undefined;\n}\n\n/** True only if a column explicitly opts in (`sortable: true`) — the default is false. */\nexport function isSortable<TRow>(column: ColumnDef<TRow>): boolean {\n return column.sortable === true;\n}\n\n/** True only if a column explicitly opts in (`filterable: true`) — the default is false. */\nexport function isFilterable<TRow>(column: ColumnDef<TRow>): boolean {\n return column.filterable === true;\n}\n\n/** True only if a column explicitly opts in to `editable` — for this specific row, when `editable` is a predicate. Default is false. */\nexport function isEditable<TRow>(column: ColumnDef<TRow>, row: TRow): boolean {\n if (!column.editable) return false;\n return typeof column.editable === \"function\" ? column.editable(row) : true;\n}\n","import { getColumnValue } from \"../column/types\";\nimport type { ColumnDef } from \"../column/types\";\nimport { evaluateFilter } from \"./evaluate\";\nimport { fieldKey, isCompositeFilterDescriptor } from \"./types\";\nimport type { CompositeFilterDescriptor, FilterDescriptor } from \"./types\";\n\ntype FilterNode = FilterDescriptor | CompositeFilterDescriptor;\n\n/** Removes every leaf filter on `columnId` from a filter tree; returns `null` if nothing is left. */\nfunction withoutColumn(filter: FilterNode | null | undefined, columnId: string): FilterNode | null {\n if (!filter) return null;\n if (!isCompositeFilterDescriptor(filter)) {\n return fieldKey(filter.field) === columnId ? null : filter;\n }\n const kept = filter.filters\n .map((child) => withoutColumn(child, columnId))\n .filter((child): child is FilterNode => child !== null);\n if (kept.length === 0) return null;\n return { logic: filter.logic, filters: kept };\n}\n\n/**\n * Computes the correct `allValues` input for `NumberHistogramFilter` bound\n * to `column`: `data` filtered by every OTHER currently-active filter, with\n * `column`'s own filter excluded, then that column's values extracted.\n *\n * This exists to prevent a specific, easy-to-reach bug: computing a filter\n * widget's own histogram/slider domain from a row set that's already been\n * narrowed BY that same filter means the domain shrinks every time the user\n * adjusts the range — they can never widen it back out — and if one \"all\n * values\" computation is naively reused across multiple numeric columns\n * (e.g. from `table.getFilteredRowModel()`), setting a filter on column A\n * also warps column B's histogram, since B's domain was computed from rows\n * A already excluded. This is the standard \"faceted search\" fix: a filter's\n * own facet should reflect \"what could I select if I changed just this\n * filter,\" never a result set that filter has already narrowed.\n *\n * `filter` is the grid's full `GridState.filter` (every column's combined\n * filter) — pass it as-is; this function does the excluding.\n */\nexport function facetedNumberValues<TRow>(\n data: readonly TRow[],\n column: ColumnDef<TRow>,\n filter: FilterNode | null | undefined,\n): (number | null)[] {\n const withoutSelf = withoutColumn(filter, column.id);\n const values: (number | null)[] = [];\n for (const row of data) {\n if (!evaluateFilter(row, withoutSelf)) continue;\n const raw = getColumnValue(column, row);\n if (typeof raw === \"number\") {\n values.push(Number.isFinite(raw) ? raw : null);\n } else if (raw == null) {\n values.push(null);\n } else {\n const parsed = Number(raw);\n values.push(Number.isFinite(parsed) ? parsed : null);\n }\n }\n return values;\n}\n","import { FunnelIcon } from \"@heroicons/react/24/outline\";\nimport type { ReactElement } from \"react\";\nimport { useId } from \"react\";\nimport type { StringColumn } from \"../column/types\";\nimport { Button } from \"../components/ui/button\";\nimport { Input } from \"../components/ui/input\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"../components/ui/popover\";\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"../components/ui/select\";\nimport { cn } from \"../lib/utils\";\nimport type { FilterOperator } from \"./types\";\nimport type { FilterWidgetProps } from \"./widget-types\";\n\ninterface StringOperatorOption {\n value: \"contains\" | \"eq\" | \"startsWith\" | \"endsWith\";\n label: string;\n}\n\nconst STRING_OPERATORS: readonly StringOperatorOption[] = [\n { value: \"contains\", label: \"Contains\" },\n { value: \"eq\", label: \"Is\" },\n { value: \"startsWith\", label: \"Starts with\" },\n { value: \"endsWith\", label: \"Ends with\" },\n];\n\nconst DEFAULT_OPERATOR: FilterOperator = \"contains\";\n\n/**\n * Default filter widget for `type: \"string\"` columns: a text input plus an\n * operator dropdown.\n *\n * `bare` (default `false`) skips this component's own Popover/trigger\n * Button, rendering just the operator + input directly — see `EnumFilter`'s\n * identical `bare` doc for why (`renderDefaultFilterWidget` passes this\n * automatically outside `filterDisplay: \"row\"`).\n */\nexport function StringFilter<TRow>({\n column,\n value,\n onChange,\n bare = false,\n}: FilterWidgetProps<StringColumn<TRow>> & { bare?: boolean }): ReactElement {\n const operator = value?.operator ?? DEFAULT_OPERATOR;\n const text = typeof value?.value === \"string\" ? value.value : \"\";\n const inputId = useId();\n const isFiltered = text !== \"\";\n\n function emit(nextOperator: FilterOperator, nextText: string): void {\n if (nextText === \"\") {\n onChange(undefined);\n return;\n }\n onChange({ field: column.id, operator: nextOperator, value: nextText });\n }\n\n const panel = (\n <div className=\"flex w-full max-w-[220px] flex-col gap-2\">\n <Select\n value={operator}\n onValueChange={(next) => {\n // radix Select's onValueChange is typed as (value: string) => void, but the\n // value can only ever be one of the `value` props on the SelectItems below,\n // which are drawn from STRING_OPERATORS.\n const nextOperator = STRING_OPERATORS.find((option) => option.value === next)?.value;\n if (nextOperator) emit(nextOperator, text);\n }}\n >\n <SelectTrigger aria-label={`${column.header} filter operator`}>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {STRING_OPERATORS.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <Input\n id={inputId}\n value={text}\n placeholder={`Filter ${column.header.toLowerCase()}...`}\n onChange={(event) => emit(operator, event.target.value)}\n />\n </div>\n );\n\n if (bare) {\n return <div data-testid={`filter-${column.id}`}>{panel}</div>;\n }\n\n return (\n <Popover>\n <PopoverTrigger asChild>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className={cn(\"gap-1\", isFiltered ? \"max-w-[160px] px-2\" : \"w-8 justify-center px-0\")}\n aria-label={`Filter ${column.header}`}\n data-testid={`filter-${column.id}`}\n >\n <FunnelIcon\n className={cn(\"h-3.5 w-3.5 shrink-0\", isFiltered ? \"opacity-100 text-primary\" : \"opacity-40\")}\n aria-hidden\n />\n {isFiltered && <span className=\"truncate\">{text}</span>}\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-auto p-3\">{panel}</PopoverContent>\n </Popover>\n );\n}\n","import type { ReactElement } from \"react\";\nimport type { ColumnDef } from \"../column/types\";\nimport { BooleanFilter } from \"./BooleanFilter\";\nimport { DateRangeFilter } from \"./DateRangeFilter\";\nimport { EnumFilter } from \"./EnumFilter\";\nimport { NumberRangeFilter } from \"./NumberRangeFilter\";\nimport { StringFilter } from \"./StringFilter\";\nimport type { FilterDescriptor } from \"./types\";\n\n/**\n * Picks and renders the default filter widget for a column, based purely on\n * `column.type`. This is what makes filtering \"just work\" without a column\n * having to configure anything — the discriminated union on `type` lets each\n * branch narrow `column` to the exact widget it needs.\n *\n * Every default widget has its own self-contained Popover+trigger Button\n * (needed under `filterDisplay: \"row\"`, where nothing else provides one) —\n * everywhere else, `<DataGrid>`'s own header filter icon already opens a\n * popover around whatever this function returns, so passing `bare: true`\n * there (this function is `<DataGrid>`'s only caller, and always knows\n * `column.filterDisplay` already) is what keeps that from nesting a second\n * popover inside the first.\n */\nexport function renderDefaultFilterWidget<TRow>(\n column: ColumnDef<TRow>,\n value: FilterDescriptor | undefined,\n onChange: (next: FilterDescriptor | undefined) => void,\n): ReactElement {\n const bare = column.filterDisplay !== \"row\";\n switch (column.type) {\n case \"string\":\n return <StringFilter column={column} value={value} onChange={onChange} bare={bare} />;\n case \"enum\":\n return <EnumFilter column={column} value={value} onChange={onChange} bare={bare} />;\n case \"boolean\":\n return <BooleanFilter column={column} value={value} onChange={onChange} bare={bare} />;\n case \"number\":\n case \"currency\":\n return <NumberRangeFilter column={column} value={value} onChange={onChange} bare={bare} />;\n case \"date\":\n case \"datetime\":\n return <DateRangeFilter column={column} value={value} onChange={onChange} bare={bare} />;\n }\n}\n","/**\n * Contract every built-in edit widget implements — the `renderEditCell`\n * equivalent of `filter/widget-types.ts`'s `FilterWidgetProps`. `TColumn` is\n * narrowed to the specific column shape the widget knows how to render (e.g.\n * `EnumColumn` for `EnumEditor`) — `renderDefaultEditWidget` picks the right\n * widget by `column.type` before handing it a column, so the widget itself\n * never has to branch on `type`.\n *\n * `value` is the cell's current value: the row's original value, or a\n * still-pending edit if the user already changed it this session.\n * `onChange` records a new pending value — it does not itself validate or\n * persist anything. `error`, when set, is this cell's current\n * `column.validateEdit` message; every built-in widget shows it as small\n * text under its control and marks the control `aria-invalid`.\n *\n * `rowId` exists purely so each built-in widget's own `data-testid` (`edit-\n * ${rowId}-${column.id}`) stays unique per cell — unlike a filter widget,\n * which renders exactly once per column (in the header), an edit widget\n * renders once per (row, column) pair, so a column-id-only testid would\n * collide across every row the moment a grid has more than one.\n *\n * `autoFocus` is true for exactly one cell per row-activation: the specific\n * column whose static cell the user clicked (or Enter/Space'd) to turn the\n * whole row into editors — every other cell in that same row gets `false`.\n * Every built-in widget forwards it straight to its underlying control's own\n * native `autoFocus` prop, so the browser focuses that one control the\n * moment it mounts, without an extra click to actually start typing.\n */\nexport interface EditWidgetProps<TColumn> {\n column: TColumn;\n rowId: string;\n value: unknown;\n onChange: (next: unknown) => void;\n error?: string;\n autoFocus?: boolean;\n}\n\n/**\n * The `id` every built-in widget gives its own error `<span>`, so its\n * control's `aria-describedby` can point at it — a screen reader announces\n * `aria-invalid` alone with no indication of *why*, so the association\n * matters, not just the flag. Exported for a custom `renderEditCell` that\n * wants the same convention instead of inventing its own id scheme.\n */\nexport function editErrorId(rowId: string, columnId: string): string {\n return `edit-error-${rowId}-${columnId}`;\n}\n","import type { ReactElement } from \"react\";\n\n/**\n * The error text every built-in editor shows under its control, associated\n * with it via `id` + the control's own `aria-describedby={editErrorId(...)}`\n * — pulled out to one place so the a11y wiring/styling convention can't\n * drift between the 5 editors the way five independent copies of the same\n * `<span>` inevitably would.\n */\nexport function EditFieldError({ id, message }: { id: string; message: string }): ReactElement {\n return (\n <span id={id} className=\"mt-0.5 block text-xs text-destructive\">\n {message}\n </span>\n );\n}\n","import type { ReactElement } from \"react\";\nimport type { BooleanColumn } from \"../column/types\";\nimport { Checkbox } from \"../components/ui/checkbox\";\nimport { EditFieldError } from \"./EditFieldError\";\nimport { editErrorId, type EditWidgetProps } from \"./widget-types\";\n\n/**\n * Same explicit-comparison convention as `defaultFormat`'s boolean case\n * (not a truthy check) — a truthy check would treat the *string* `\"false\"`\n * as checked, a real shape for boolean data round-tripped through JSON/CSV.\n */\nfunction toBoolean(value: unknown): boolean {\n if (typeof value === \"boolean\") return value;\n if (typeof value === \"string\") return value.trim().toLowerCase() === \"true\";\n if (typeof value === \"number\") return value !== 0;\n return false;\n}\n\n/** Default inline editor for `type: \"boolean\"` columns: a single checkbox. */\nexport function BooleanEditor<TRow>({\n column,\n rowId,\n value,\n onChange,\n error,\n autoFocus,\n}: EditWidgetProps<BooleanColumn<TRow>>): ReactElement {\n const errorId = editErrorId(rowId, column.id);\n return (\n // No `gap-*` here (unlike a plain flex stack) — `EditFieldError`'s own\n // `mt-0.5` already provides the same spacing every other editor gets;\n // adding both would double it for this one editor only.\n <div className=\"flex flex-col items-center\">\n <Checkbox\n aria-label={`Edit ${column.header}`}\n aria-invalid={error ? true : undefined}\n aria-describedby={error ? errorId : undefined}\n data-testid={`edit-${rowId}-${column.id}`}\n autoFocus={autoFocus}\n checked={toBoolean(value)}\n onCheckedChange={(next) => onChange(next === true)}\n />\n {error && <EditFieldError id={errorId} message={error} />}\n </div>\n );\n}\n","import { format as formatDate, parseISO } from \"date-fns\";\nimport type { ReactElement } from \"react\";\nimport type { DateColumn } from \"../column/types\";\nimport { toDate } from \"../column/format\";\nimport { Input } from \"../components/ui/input\";\nimport { EditFieldError } from \"./EditFieldError\";\nimport { editErrorId, type EditWidgetProps } from \"./widget-types\";\n\n/**\n * Formats a raw cell value into the string a native date/datetime-local\n * input expects, or `\"\"` if it isn't a valid date. Includes seconds for\n * `\"datetime\"` (paired with the input's own `step={1}` below, so the\n * browser's picker UI shows a seconds field too) — omitting them here would\n * silently truncate any datetime value's seconds the moment a user edits\n * that cell at all, even if all they touched was the date portion, since\n * the input's own value string is what `onChange` below reconstructs a\n * `Date` from. Milliseconds are still dropped — native `datetime-local`\n * inputs support them via 3-decimal seconds, but sub-second precision on a\n * grid-edited datetime column is enough of an edge case to leave as a\n * documented scope cut rather than add here.\n */\nfunction inputValueOf(value: unknown, columnType: \"date\" | \"datetime\"): string {\n const date = toDate(value);\n if (!date) return \"\";\n return columnType === \"datetime\" ? formatDate(date, \"yyyy-MM-dd'T'HH:mm:ss\") : formatDate(date, \"yyyy-MM-dd\");\n}\n\n/**\n * Default inline editor for `type: \"date\"` / `type: \"datetime\"` columns: a\n * native `<input type=\"date\">`/`<input type=\"datetime-local\">`. Emits a\n * `Date` (or `null` for an emptied field), via `parseISO` rather than the\n * bare `new Date(...)` constructor — same convention `DateRangeFilter`'s\n * `parseBound` and `format.ts`'s `toDate` already use, since a native date\n * input's value is a date-only `\"YYYY-MM-DD\"` string for `type: \"date\"`,\n * which the JS spec would otherwise parse as UTC midnight instead of local\n * midnight.\n */\nexport function DateEditor<TRow>({\n column,\n rowId,\n value,\n onChange,\n error,\n autoFocus,\n}: EditWidgetProps<DateColumn<TRow>>): ReactElement {\n const errorId = editErrorId(rowId, column.id);\n return (\n <div>\n <Input\n type={column.type === \"datetime\" ? \"datetime-local\" : \"date\"}\n step={column.type === \"datetime\" ? 1 : undefined}\n className=\"h-8\"\n aria-label={`Edit ${column.header}`}\n aria-invalid={error ? true : undefined}\n aria-describedby={error ? errorId : undefined}\n data-testid={`edit-${rowId}-${column.id}`}\n autoFocus={autoFocus}\n value={inputValueOf(value, column.type)}\n onChange={(event) => onChange(event.target.value === \"\" ? null : parseISO(event.target.value))}\n />\n {error && <EditFieldError id={errorId} message={error} />}\n </div>\n );\n}\n","import type { ReactElement } from \"react\";\nimport type { EnumColumn } from \"../column/types\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"../components/ui/select\";\nimport { EditFieldError } from \"./EditFieldError\";\nimport { editErrorId, type EditWidgetProps } from \"./widget-types\";\n\n/** Default inline editor for `type: \"enum\"` columns: a single-value select over `column.options`. */\nexport function EnumEditor<TRow>({\n column,\n rowId,\n value,\n onChange,\n error,\n autoFocus,\n}: EditWidgetProps<EnumColumn<TRow>>): ReactElement {\n const errorId = editErrorId(rowId, column.id);\n return (\n <div>\n <Select value={value === null || value === undefined ? \"\" : String(value)} onValueChange={onChange}>\n <SelectTrigger\n className=\"h-8\"\n aria-label={`Edit ${column.header}`}\n aria-invalid={error ? true : undefined}\n aria-describedby={error ? errorId : undefined}\n data-testid={`edit-${rowId}-${column.id}`}\n autoFocus={autoFocus}\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {column.options.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n {error && <EditFieldError id={errorId} message={error} />}\n </div>\n );\n}\n","import type { ReactElement } from \"react\";\nimport { useEffect, useRef, useState } from \"react\";\nimport type { NumberColumn } from \"../column/types\";\nimport { Input } from \"../components/ui/input\";\nimport { EditFieldError } from \"./EditFieldError\";\nimport { editErrorId, type EditWidgetProps } from \"./widget-types\";\n\nfunction textFromValue(value: unknown): string {\n return value === null || value === undefined ? \"\" : String(value);\n}\n\n/**\n * Default inline editor for `type: \"number\"` / `type: \"currency\"` columns: a\n * native number input. Emits `null` for an emptied field (not `NaN` or `\"\"`)\n * so `validateEdit`/`onSave` see an explicit \"cleared\" value; otherwise emits\n * a real `number`, never the input's raw string.\n *\n * Keeps its own local `text` state instead of deriving the input's `value`\n * straight from the `value` prop on every render — a native\n * `type=\"number\"` input's own `.value` getter reads back as `\"\"` while the\n * user is mid-typing a string that isn't yet a complete valid float (\"12.\",\n * \"-\", \"1e\"), even though the field still visually shows what they typed.\n * Deriving the controlled `value` prop straight from a re-parsed number\n * would feed that `\"\"` back in on every such keystroke, wiping the decimal\n * point/minus sign before the user can finish typing it. `lastEmittedRef` +\n * the effect below only resync `text` from an EXTERNAL change to `value`\n * (Escape-revert, switching rows and back, a row's data refetched) — the\n * same pattern `NumberComparisonFilter` already uses for the identical\n * reason; see that component's own doc.\n */\nexport function NumberEditor<TRow>({\n column,\n rowId,\n value,\n onChange,\n error,\n autoFocus,\n}: EditWidgetProps<NumberColumn<TRow>>): ReactElement {\n const errorId = editErrorId(rowId, column.id);\n const [text, setText] = useState(() => textFromValue(value));\n const lastEmittedRef = useRef(value);\n\n useEffect(() => {\n if (value === lastEmittedRef.current) return;\n lastEmittedRef.current = value;\n setText(textFromValue(value));\n }, [value]);\n\n return (\n <div>\n <Input\n type=\"number\"\n className=\"h-8\"\n aria-label={`Edit ${column.header}`}\n aria-invalid={error ? true : undefined}\n aria-describedby={error ? errorId : undefined}\n data-testid={`edit-${rowId}-${column.id}`}\n autoFocus={autoFocus}\n value={text}\n onChange={(event) => {\n setText(event.target.value);\n const next = event.target.value === \"\" ? null : Number(event.target.value);\n lastEmittedRef.current = next;\n onChange(next);\n }}\n />\n {error && <EditFieldError id={errorId} message={error} />}\n </div>\n );\n}\n","import type { ReactElement } from \"react\";\nimport type { StringColumn } from \"../column/types\";\nimport { Input } from \"../components/ui/input\";\nimport { EditFieldError } from \"./EditFieldError\";\nimport { editErrorId, type EditWidgetProps } from \"./widget-types\";\n\n/** Default inline editor for `type: \"string\"` columns: a plain text input. */\nexport function StringEditor<TRow>({\n column,\n rowId,\n value,\n onChange,\n error,\n autoFocus,\n}: EditWidgetProps<StringColumn<TRow>>): ReactElement {\n const errorId = editErrorId(rowId, column.id);\n return (\n <div>\n <Input\n className=\"h-8\"\n aria-label={`Edit ${column.header}`}\n aria-invalid={error ? true : undefined}\n aria-describedby={error ? errorId : undefined}\n data-testid={`edit-${rowId}-${column.id}`}\n autoFocus={autoFocus}\n value={value === null || value === undefined ? \"\" : String(value)}\n onChange={(event) => onChange(event.target.value)}\n />\n {error && <EditFieldError id={errorId} message={error} />}\n </div>\n );\n}\n","import type { ReactElement } from \"react\";\nimport type { ColumnDef } from \"../column/types\";\nimport { BooleanEditor } from \"./BooleanEditor\";\nimport { DateEditor } from \"./DateEditor\";\nimport { EnumEditor } from \"./EnumEditor\";\nimport { NumberEditor } from \"./NumberEditor\";\nimport { StringEditor } from \"./StringEditor\";\n\n/**\n * Picks and renders the default inline-edit widget for a column, based\n * purely on `column.type` — the `renderEditCell` equivalent of\n * `filter/registry.tsx`'s `renderDefaultFilterWidget`. `<DataGrid>` calls\n * this itself for any editable column that doesn't set its own\n * `renderEditCell`; a consuming app only reaches for it directly to wrap or\n * extend one of these defaults rather than writing an editor from scratch.\n */\nexport function renderDefaultEditWidget<TRow>(\n column: ColumnDef<TRow>,\n rowId: string,\n value: unknown,\n onChange: (next: unknown) => void,\n error?: string,\n autoFocus?: boolean,\n): ReactElement {\n switch (column.type) {\n case \"string\":\n return (\n <StringEditor column={column} rowId={rowId} value={value} onChange={onChange} error={error} autoFocus={autoFocus} />\n );\n case \"enum\":\n return (\n <EnumEditor column={column} rowId={rowId} value={value} onChange={onChange} error={error} autoFocus={autoFocus} />\n );\n case \"boolean\":\n return (\n <BooleanEditor column={column} rowId={rowId} value={value} onChange={onChange} error={error} autoFocus={autoFocus} />\n );\n case \"number\":\n case \"currency\":\n return (\n <NumberEditor column={column} rowId={rowId} value={value} onChange={onChange} error={error} autoFocus={autoFocus} />\n );\n case \"date\":\n case \"datetime\":\n return (\n <DateEditor column={column} rowId={rowId} value={value} onChange={onChange} error={error} autoFocus={autoFocus} />\n );\n }\n}\n","import { useEffect, useRef, useState } from \"react\";\nimport type { ColumnDef } from \"../column/types\";\nimport { getColumnValue } from \"../column/types\";\nimport type { EditedRow, EditingOptions } from \"./types\";\n\n/** One row's accumulated pending edits. */\nexport interface PendingRowEdit<TRow> {\n /** The row as it looked when its first still-pending edit was made this session. */\n row: TRow;\n values: Map<string, unknown>;\n}\n\n/**\n * Everything a cell-rendering path (`renderEditableCell`) needs to decide\n * whether/how to render a given cell as an editor. Bundled into one object\n * so `<DataGrid>` can hand it through a `useRef` (see that component's own\n * doc on why — TanStack's `flexRender` treats `columnDef.cell` as a\n * component type, so a closure that changes identity on every edit would\n * remount the whole table body) while `<TreeDataGrid>`, which has no such\n * memoized column-def layer, can just read the hook's plain `ctx` value\n * directly every render.\n */\nexport interface EditingCellContext<TRow> {\n pendingEdits: Map<string, PendingRowEdit<TRow>>;\n editErrors: Map<string, Map<string, string>>;\n /**\n * The one row currently showing editors, or `null`. Deliberately NOT the\n * same thing as \"has a pending edit\": a row can be active with nothing\n * changed yet (just clicked into). At most one at a time — activating a\n * row deactivates whatever was active before it (edit row by row, not\n * several open at once). This does NOT discard the row you switch away\n * from: its own pending edits stay exactly as they were in `pendingEdits`,\n * just no longer shown as editors.\n */\n activeRowId: string | null;\n /** Set only by the click that activated a row — see `EditWidgetProps.autoFocus`'s own doc for why this exists. */\n autoFocusTarget: { rowId: string; columnId: string } | null;\n getRowId: (row: TRow) => string;\n onEdit: (column: ColumnDef<TRow>, row: TRow, value: unknown) => void;\n /** Activates `rowId` (deactivating whatever row was active before it) and requests autofocus on `columnId`'s editor specifically — the column whose static cell was actually clicked. */\n onActivateRow: (rowId: string, columnId: string) => void;\n}\n\nexport interface EditingState<TRow> {\n pendingEdits: Map<string, PendingRowEdit<TRow>>;\n editErrors: Map<string, Map<string, string>>;\n hasEditErrors: boolean;\n /** The live editing context — read directly during a render body that has no remount-avoidance concerns of its own (e.g. `<TreeDataGrid>`'s plain per-row render). */\n ctx: EditingCellContext<TRow> | undefined;\n /** Same context, mirrored into a ref every render — for a caller (`<DataGrid>`) whose cell-rendering closures must stay referentially stable and so can't close over `ctx` directly. */\n ctxRef: { current: EditingCellContext<TRow> | undefined };\n handleSaveEdits: () => Promise<void>;\n handleDiscardEdits: () => void;\n}\n\n/**\n * Same convention `defaultFormat`'s docs point to for other primitive\n * comparisons, extended to `Date`: the default `DateEditor` emits real `Date`\n * objects, and two distinct `Date` instances for the same instant are never\n * `===` to each other — without this, editing a date cell back to its\n * original value (a real thing to do while fixing a typo) would never clear\n * its own pending-edit entry, since the equality check below would always\n * see \"changed.\"\n */\nexport function editValuesEqual(a: unknown, b: unknown): boolean {\n if (a instanceof Date && b instanceof Date) return a.getTime() === b.getTime();\n return a === b;\n}\n\n/**\n * Owns the full accumulate-then-save inline-editing state machine: pending\n * edits, validation errors, which row is active, autofocus targeting, and\n * the Save/Discard commit logic — shared by `<DataGrid>` and\n * `<TreeDataGrid>` so the tricky parts (the async-save race around edits\n * made while `onSave` is in flight, the one-shot autoFocus consumption) only\n * exist, and only need fixing, in one place.\n */\nexport function useEditingState<TRow>(\n editing: EditingOptions<TRow> | undefined,\n getRowId: (row: TRow) => string,\n): EditingState<TRow> {\n // Uncontrolled-only — there's no scenario where a caller wants to seed or\n // drive pending edits from outside, only to read them back out via\n // `editing.onSave` once the user commits. Keyed by row id, not by (row\n // id, column id) pair: storing the row snapshot once per row (not once\n // per edited cell) is what lets `handleSaveEdits` hand back a real `TRow`\n // per edited row without a second id-keyed lookup across whatever page/\n // filter/expansion state the grid is in by the time Save is clicked.\n const [pendingEdits, setPendingEdits] = useState<Map<string, PendingRowEdit<TRow>>>(new Map());\n const [editErrors, setEditErrors] = useState<Map<string, Map<string, string>>>(new Map());\n const hasEditErrors = editErrors.size > 0;\n\n const [activeRowId, setActiveRowId] = useState<string | null>(null);\n const [autoFocusTarget, setAutoFocusTarget] = useState<{ rowId: string; columnId: string } | null>(null);\n\n function activateRow(rowId: string, columnId: string): void {\n setActiveRowId(rowId);\n setAutoFocusTarget({ rowId, columnId });\n }\n\n // Consumes `autoFocusTarget` exactly once, right after the render that\n // used it — NOT redundant with native `autoFocus` only firing on a DOM\n // node's *initial* mount. Under `virtualize`, a row's DOM can unmount\n // (scrolled out of the windowed range) and later remount (scrolled back\n // in); without clearing `autoFocusTarget` after its first use, that later\n // remount is ALSO an \"initial mount\" as far as the DOM is concerned,\n // silently stealing focus back to a cell the user scrolled away from.\n useEffect(() => {\n if (autoFocusTarget) setAutoFocusTarget(null);\n }, [autoFocusTarget]);\n\n function commitEdit(column: ColumnDef<TRow>, row: TRow, value: unknown): void {\n const rowId = getRowId(row);\n const baseline = getColumnValue(column, row);\n setPendingEdits((prev) => {\n const next = new Map(prev);\n const values = new Map(next.get(rowId)?.values);\n if (editValuesEqual(value, baseline)) values.delete(column.id);\n else values.set(column.id, value);\n if (values.size === 0) next.delete(rowId);\n else next.set(rowId, { row, values });\n return next;\n });\n const message = column.validateEdit?.(value, row);\n setEditErrors((prev) => {\n const next = new Map(prev);\n const rowErrors = new Map(next.get(rowId));\n if (message) rowErrors.set(column.id, message);\n else rowErrors.delete(column.id);\n if (rowErrors.size === 0) next.delete(rowId);\n else next.set(rowId, rowErrors);\n return next;\n });\n }\n\n async function handleSaveEdits(): Promise<void> {\n if (!editing || pendingEdits.size === 0) return;\n const snapshot = pendingEdits;\n const edited: EditedRow<TRow>[] = [...snapshot.entries()].map(([rowId, entry]) => ({\n rowId,\n row: entry.row,\n values: Object.fromEntries(entry.values),\n }));\n try {\n await editing.onSave(edited);\n } catch {\n // `onSave`'s own doc: rejecting means \"keep every pending edit exactly\n // as it was\" — the caller is responsible for its own error UI; this\n // catch exists only so that rejection doesn't also surface as an\n // unhandled promise rejection on top of whatever the caller already\n // shows.\n return;\n }\n // Clear only what THIS save actually covered, and only where it hasn't\n // changed since — nothing here disables a cell's inputs while `onSave`\n // is in flight (that's the caller's own `editing.saving` to act on;\n // this hook doesn't force it), so the user can keep typing into the\n // active row, or even switch to and edit a different one, during the\n // await. A blanket \"clear everything\" here would silently discard\n // whatever they typed in that window, even though it was never part of\n // `edited` at all. `survivedRowIds`/`clearedErrorKeys` are plain\n // synchronous side-channels from the `setPendingEdits` updater below to\n // the two updaters after it — safe because React runs queued updater\n // functions in order, synchronously, before committing.\n const snapshotRowIds = new Set(snapshot.keys());\n const survivedRowIds = new Set<string>();\n const clearedErrorKeys: { rowId: string; columnId: string }[] = [];\n setPendingEdits((prev) => {\n const next = new Map(prev);\n for (const [rowId, snapshotEntry] of snapshot) {\n const current = next.get(rowId);\n if (!current) continue;\n const values = new Map(current.values);\n for (const [columnId, snapshotValue] of snapshotEntry.values) {\n if (values.has(columnId) && editValuesEqual(values.get(columnId), snapshotValue)) {\n values.delete(columnId);\n clearedErrorKeys.push({ rowId, columnId });\n }\n }\n if (values.size === 0) {\n next.delete(rowId);\n } else {\n next.set(rowId, { row: current.row, values });\n survivedRowIds.add(rowId);\n }\n }\n return next;\n });\n setEditErrors((prev) => {\n if (clearedErrorKeys.length === 0) return prev;\n const next = new Map(prev);\n for (const { rowId, columnId } of clearedErrorKeys) {\n const rowErrors = next.get(rowId);\n if (!rowErrors) continue;\n const nextRowErrors = new Map(rowErrors);\n nextRowErrors.delete(columnId);\n if (nextRowErrors.size === 0) next.delete(rowId);\n else next.set(rowId, nextRowErrors);\n }\n return next;\n });\n // Deactivates the active row only if it was part of this save AND has\n // nothing left pending on it afterward — a row clicked into but never\n // actually changed (never part of `edited`) has nothing to save but\n // also nothing wrong with it, and one that got a NEW edit during the\n // await still has work in progress, so neither should be kicked out.\n setActiveRowId((prev) =>\n prev !== null && snapshotRowIds.has(prev) && !survivedRowIds.has(prev) ? null : prev,\n );\n }\n\n function handleDiscardEdits(): void {\n setPendingEdits(new Map());\n setEditErrors(new Map());\n setActiveRowId(null);\n editing?.onDiscard?.();\n }\n\n const ctx: EditingCellContext<TRow> | undefined = editing\n ? { pendingEdits, editErrors, activeRowId, autoFocusTarget, getRowId, onEdit: commitEdit, onActivateRow: activateRow }\n : undefined;\n\n // Reassigned every render (a plain statement, not an effect) so it's\n // already current by the time a `<DataGrid>`-style memoized cell closure\n // actually renders a cell in this same pass.\n const ctxRef = useRef<EditingCellContext<TRow> | undefined>(undefined);\n ctxRef.current = ctx;\n\n return { pendingEdits, editErrors, hasEditErrors, ctx, ctxRef, handleSaveEdits, handleDiscardEdits };\n}\n","import type { ReactNode } from \"react\";\nimport { defaultFormat } from \"../column/format\";\nimport type { ColumnDef } from \"../column/types\";\nimport { getColumnValue, isEditable } from \"../column/types\";\nimport { stopRowClick } from \"../lib/utils\";\nimport type { EditingCellContext } from \"./editingState\";\nimport { renderDefaultEditWidget } from \"./registry\";\n\n/**\n * Renders one cell for a column that may or may not be `editable`, given the\n * live editing context (or `undefined` when no `editing` prop was supplied\n * at all). Shared by `<DataGrid>` and `<TreeDataGrid>` so the click-to-\n * activate/Escape-to-revert/autoFocus behavior — and its bug fixes — only\n * exist in one place; see `useEditingState`'s own module doc.\n *\n * Not itself responsible for choosing WHICH raw value to start from\n * (`<DataGrid>` reads it off TanStack's `CellContext.getValue()`,\n * `<TreeDataGrid>` off a plain accessor) — callers pass `rawValue` in.\n */\nexport function renderEditableCell<TRow>(\n column: ColumnDef<TRow>,\n row: TRow,\n rawValue: unknown,\n ctx: EditingCellContext<TRow> | undefined,\n): ReactNode {\n const staticContent = (value: unknown): ReactNode =>\n column.cell ? column.cell(value, row) : defaultFormat(column, value);\n if (!ctx || !isEditable(column, row)) return staticContent(rawValue);\n\n const rowId = ctx.getRowId(row);\n // A pending edit on this cell wins over the row's real data regardless of\n // whether the row is currently active — switching to another row (only\n // one is ever active at a time) must not make an already-typed value look\n // reverted/lost just because its editor isn't on screen anymore; it's\n // still fully pending and will still be included in Save.\n const pendingRow = ctx.pendingEdits.get(rowId);\n const value = pendingRow?.values.has(column.id) ? pendingRow.values.get(column.id) : rawValue;\n\n if (ctx.activeRowId === rowId) {\n const error = ctx.editErrors.get(rowId)?.get(column.id);\n const autoFocus = ctx.autoFocusTarget?.rowId === rowId && ctx.autoFocusTarget.columnId === column.id;\n const onChange = (next: unknown): void => ctx.onEdit(column, row, next);\n const editor = column.renderEditCell\n ? column.renderEditCell(value, row, onChange, error, autoFocus)\n : renderDefaultEditWidget(column, rowId, value, onChange, error, autoFocus);\n // Escape reverts this ONE cell back to its original (never-edited)\n // value — not a multi-step undo back through every prior edit this\n // session, just \"give up on what I just typed here.\" A plain\n // `onKeyDown` here (rather than plumbing it through every default\n // editor + `renderEditCell`) works because the key event bubbles up\n // from whatever native control the editor renders; the row itself\n // stays active (only this cell's value changes) — deactivating the\n // whole row on Escape would also discard sibling cells' edits the user\n // had no intention of touching.\n return (\n <div\n onKeyDown={(event) => {\n if (event.key !== \"Escape\") return;\n // The default EnumEditor's Radix <Select> (or any custom\n // `renderEditCell` popover/dropdown) is a REACT descendant of\n // this `<div>` — so its synthetic events still bubble up to this\n // handler — but its actual DOM node lives in a portal OUTSIDE\n // this `<div>`. Radix's own Escape-closes-the-dropdown handling\n // doesn't stopPropagation, so without this real-DOM containment\n // check, pressing Escape just to close an open dropdown would\n // ALSO silently revert whatever this cell already had selected.\n if (!event.currentTarget.contains(event.target as Node)) return;\n event.stopPropagation();\n ctx.onEdit(column, row, getColumnValue(column, row));\n }}\n >\n {editor}\n </div>\n );\n }\n\n // Not yet activated: static content, but a click on it (any editable\n // column's cell) activates every editable column in this row at once —\n // see `EditingCellContext.activeRowId`'s own doc. `role`/`tabIndex` make\n // this keyboard-reachable (Enter/Space) too, not just clickable.\n return (\n <span\n role=\"button\"\n tabIndex={0}\n data-testid={`cell-${rowId}-${column.id}`}\n className=\"block w-full cursor-text\"\n onClick={(event) => {\n stopRowClick(event);\n ctx.onActivateRow(rowId, column.id);\n }}\n onKeyDown={(event) => {\n if (event.key !== \"Enter\" && event.key !== \" \") return;\n event.preventDefault();\n stopRowClick(event);\n ctx.onActivateRow(rowId, column.id);\n }}\n >\n {staticContent(value)}\n </span>\n );\n}\n","import type { ReactElement, ReactNode } from \"react\";\nimport { Button } from \"../components/ui/button\";\nimport type { EditingOptions } from \"./types\";\n\n/** Resolves a `saveLabel`/`discardLabel`-shaped prop to what actually renders — see `EditingOptions`'s own doc for why there's no placeholder substitution for the plain-string case. */\nfunction resolveEditLabel(\n label: string | ((changedRowCount: number) => ReactNode) | undefined,\n changedRowCount: number,\n fallback: (changedRowCount: number) => ReactNode,\n): ReactNode {\n if (label === undefined) return fallback(changedRowCount);\n return typeof label === \"function\" ? label(changedRowCount) : label;\n}\n\n// Bounded to 3 named ids, then \"and N more\" — a grid with dozens of\n// simultaneously-broken rows would otherwise turn this into an unreadable\n// wall of ids; there's no general \"human label for a row\" concept to fall\n// back on, so a row id (whatever the caller's own `getRowId` returns) is the\n// only identifying string available here.\nfunction describeErrorRows(editErrors: Map<string, Map<string, string>>): string {\n const rowIds = [...editErrors.keys()];\n const shown = rowIds.slice(0, 3);\n const suffix = rowIds.length > shown.length ? `, and ${rowIds.length - shown.length} more` : \"\";\n return `row${rowIds.length === 1 ? \"\" : \"s\"} ${shown.join(\", \")}${suffix}`;\n}\n\nexport interface EditingBarProps<TRow> {\n editing: EditingOptions<TRow>;\n pendingRowCount: number;\n editErrors: Map<string, Map<string, string>>;\n onSave: () => void;\n onDiscard: () => void;\n /** `data-testid` for the outer bar element. Defaults to `\"datagrid-edit-bar\"` (also the default for the Save/Discard buttons' own testids, unless overridden below). */\n testIdPrefix?: string;\n}\n\n/**\n * The Save/Discard bar `<DataGrid>` and `<TreeDataGrid>` both render above\n * themselves once at least one pending edit exists — shared so the two\n * grids' editing UI (and its `aria-live`/row-naming behavior) can't drift\n * apart from each other.\n */\nexport function EditingBar<TRow>({\n editing,\n pendingRowCount,\n editErrors,\n onSave,\n onDiscard,\n testIdPrefix = \"datagrid\",\n}: EditingBarProps<TRow>): ReactElement {\n const hasEditErrors = editErrors.size > 0;\n return (\n <div\n data-testid={`${testIdPrefix}-edit-bar`}\n className=\"flex shrink-0 flex-wrap items-center justify-between gap-2 rounded-md border bg-muted/50 px-3 py-2 text-sm\"\n >\n {/* `aria-live=\"polite\"` — a screen-reader user gets no other signal that this bar exists at all, let alone that\n its row count or error state just changed, since nothing here is itself focused when it appears. */}\n <span aria-live=\"polite\">\n {pendingRowCount} row{pendingRowCount === 1 ? \"\" : \"s\"} changed\n {hasEditErrors && (\n <span className=\"ml-2 text-destructive\">\n Fix the highlighted errors before saving ({describeErrorRows(editErrors)}).\n </span>\n )}\n </span>\n <div className=\"flex gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n data-testid={`${testIdPrefix}-discard-edits`}\n disabled={editing.saving}\n onClick={onDiscard}\n >\n {resolveEditLabel(editing.discardLabel, pendingRowCount, () => \"Discard\")}\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n data-testid={`${testIdPrefix}-save-edits`}\n disabled={editing.saving || hasEditErrors}\n onClick={onSave}\n >\n {resolveEditLabel(\n editing.saveLabel,\n pendingRowCount,\n (count) => `Save ${count} change${count === 1 ? \"\" : \"s\"}`,\n )}\n </Button>\n </div>\n </div>\n );\n}\n","import { Columns3 } from \"lucide-react\";\nimport type { ReactElement, ReactNode } from \"react\";\nimport { useEffect } from \"react\";\nimport type { ColumnDef } from \"../column/types\";\nimport { Button } from \"../components/ui/button\";\nimport { Checkbox } from \"../components/ui/checkbox\";\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n} from \"../components/ui/dialog\";\nimport { readPersistedVisibility, writePersistedVisibility } from \"./persistence\";\nimport type { ColumnVisibility } from \"./types\";\nimport { canHideColumn, countVisible, groupColumns, isColumnVisible } from \"./visibility\";\n\nexport interface ColumnSelectorProps<TRow> {\n columns: ColumnDef<TRow>[];\n visibility: ColumnVisibility;\n onVisibilityChange: (visibility: ColumnVisibility) => void;\n /** If set: restore from localStorage on mount, and persist on every subsequent change. */\n persistKey?: string;\n /** Optional custom trigger; defaults to a \"Columns\" icon button. */\n trigger?: ReactNode;\n /** Optional controlled open state, for callers that want their own trigger entirely. */\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n}\n\n/**\n * Controlled column-visibility selector: a modal listing every column,\n * grouped by `column.group` into side-by-side columns (ungrouped columns\n * first, unlabeled, matching `groupColumns`' own ordering), wrapping onto a\n * new row rather than growing the dialog past a reasonable width. Each named\n * group's header is a plain muted label — no bulk-select affordance of any\n * kind lives there (an earlier version had a tri-state checkbox, then an\n * \"All\"/\"None\" link pair, both dropped; see AGENTS.md for why). Purely\n * controlled by `visibility`/`onVisibilityChange` — `persistKey`\n * layers an optional localStorage sync on top (restore once on mount, write\n * on every change), it never becomes the source of truth in place of the\n * `visibility` prop.\n *\n * Not rendered inside `<DataGrid>` automatically — wire it up next to the\n * grid yourself, e.g.:\n *\n * ```tsx\n * const [visibility, setVisibility] = useState<ColumnVisibility>({});\n * <ColumnSelector columns={columns} visibility={visibility} onVisibilityChange={setVisibility} persistKey=\"orders\" />\n * <DataGrid columns={columns} columnVisibility={visibility} onColumnVisibilityChange={setVisibility} ... />\n * ```\n */\nexport function ColumnSelector<TRow>({\n columns,\n visibility,\n onVisibilityChange,\n persistKey,\n trigger,\n open,\n onOpenChange,\n}: ColumnSelectorProps<TRow>): ReactElement {\n // Restore from localStorage once on mount. Intentionally does not depend\n // on `visibility`/`onVisibilityChange` identity — this must run exactly\n // once regardless of how many times the parent re-renders.\n useEffect(() => {\n if (!persistKey) return;\n const restored = readPersistedVisibility(persistKey);\n if (restored) onVisibilityChange({ ...visibility, ...restored });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [persistKey]);\n\n function commit(next: ColumnVisibility): void {\n onVisibilityChange(next);\n if (persistKey) writePersistedVisibility(persistKey, next);\n }\n\n function toggleColumn(columnId: string): void {\n if (!canHideColumn(columns, visibility, columnId)) return;\n commit({ ...visibility, [columnId]: !isColumnVisible(visibility, columnId) });\n }\n\n const totalVisible = countVisible(columns, visibility);\n const groups = groupColumns(columns);\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogTrigger asChild>\n {trigger ?? (\n <Button variant=\"outline\" size=\"icon\" aria-label=\"Choose columns\">\n <Columns3 className=\"h-4 w-4\" aria-hidden />\n </Button>\n )}\n </DialogTrigger>\n <DialogContent className=\"w-fit max-w-[calc(100vw-2rem)] sm:max-w-2xl\">\n <DialogHeader>\n <DialogTitle>Columns</DialogTitle>\n </DialogHeader>\n {/* Groups lay out left-to-right, wrapping onto a new row instead of\n forcing the dialog wider than sm:max-w-2xl above — gap spacing\n alone separates columns (no vertical rule) so a wrapped row never\n ends up with a stray leading divider. */}\n <div className=\"flex flex-wrap gap-x-8 gap-y-5\">\n {groups.map(({ group, columns: groupCols }) => (\n <div key={group ?? \"__ungrouped__\"} className=\"flex min-w-32 flex-col gap-1.5\">\n {group && (\n <span className=\"border-b pb-1.5 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground\">\n {group}\n </span>\n )}\n {groupCols.map((column) => {\n const visible = isColumnVisible(visibility, column.id);\n return (\n <label\n key={column.id}\n className=\"flex items-center gap-2 rounded-md px-1.5 py-1 text-sm hover:bg-accent/50 focus-within:bg-accent/50\"\n >\n <Checkbox\n checked={visible}\n disabled={visible && totalVisible <= 1}\n onCheckedChange={() => toggleColumn(column.id)}\n />\n {column.header}\n </label>\n );\n })}\n </div>\n ))}\n </div>\n </DialogContent>\n </Dialog>\n );\n}\n","import * as DialogPrimitive from \"@radix-ui/react-dialog\";\nimport { X } from \"lucide-react\";\nimport type { ComponentPropsWithoutRef, ElementRef, HTMLAttributes, ReactElement } from \"react\";\nimport { forwardRef } from \"react\";\nimport { cn } from \"../../lib/utils\";\n\nexport const Dialog = DialogPrimitive.Root;\nexport const DialogTrigger = DialogPrimitive.Trigger;\n\nexport const DialogOverlay = forwardRef<\n ElementRef<typeof DialogPrimitive.Overlay>,\n ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Overlay\n ref={ref}\n className={cn(\"fixed inset-0 z-50 bg-black/50\", className)}\n {...props}\n />\n));\nDialogOverlay.displayName = \"DialogOverlay\";\n\nexport const DialogContent = forwardRef<\n ElementRef<typeof DialogPrimitive.Content>,\n ComponentPropsWithoutRef<typeof DialogPrimitive.Content>\n>(({ className, children, ...props }, ref) => (\n <DialogPrimitive.Portal>\n <DialogOverlay />\n <DialogPrimitive.Content\n ref={ref}\n className={cn(\n \"fixed left-1/2 top-1/2 z-50 w-full max-w-md -translate-x-1/2 -translate-y-1/2 rounded-lg border bg-background p-6 shadow-lg\",\n className,\n )}\n {...props}\n >\n {children}\n <DialogPrimitive.Close className=\"absolute right-4 top-4 rounded-sm opacity-70 hover:opacity-100\">\n <X className=\"h-4 w-4\" />\n <span className=\"sr-only\">Close</span>\n </DialogPrimitive.Close>\n </DialogPrimitive.Content>\n </DialogPrimitive.Portal>\n));\nDialogContent.displayName = \"DialogContent\";\n\nexport const DialogHeader = ({\n className,\n ...props\n}: HTMLAttributes<HTMLDivElement>): ReactElement => (\n <div className={cn(\"flex flex-col gap-1.5\", className)} {...props} />\n);\n\nexport const DialogTitle = forwardRef<\n ElementRef<typeof DialogPrimitive.Title>,\n ComponentPropsWithoutRef<typeof DialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Title ref={ref} className={cn(\"text-lg font-semibold\", className)} {...props} />\n));\nDialogTitle.displayName = \"DialogTitle\";\n\nexport const DialogDescription = forwardRef<\n ElementRef<typeof DialogPrimitive.Description>,\n ComponentPropsWithoutRef<typeof DialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Description\n ref={ref}\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n));\nDialogDescription.displayName = \"DialogDescription\";\n\nexport const DialogFooter = ({\n className,\n ...props\n}: HTMLAttributes<HTMLDivElement>): ReactElement => (\n <div className={cn(\"flex justify-end gap-2\", className)} {...props} />\n);\n","import type { ColumnVisibility } from \"./types\";\n\nexport function storageKeyFor(persistKey: string): string {\n return `bmsui-datagrid:columns:${persistKey}`;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * Reads and JSON-parses a persisted `ColumnVisibility` from localStorage.\n * Returns `null` if there's nothing stored, the JSON is malformed, or the\n * parsed value isn't a plain object — callers should fall back to whatever\n * visibility they already have rather than throwing on stale/corrupt data\n * (e.g. a previous version of this component persisting a different shape).\n */\nexport function readPersistedVisibility(persistKey: string): ColumnVisibility | null {\n const raw = window.localStorage.getItem(storageKeyFor(persistKey));\n if (raw === null) return null;\n try {\n const parsed = JSON.parse(raw);\n return isPlainObject(parsed) ? (parsed as ColumnVisibility) : null;\n } catch {\n return null;\n }\n}\n\nexport function writePersistedVisibility(persistKey: string, visibility: ColumnVisibility): void {\n window.localStorage.setItem(storageKeyFor(persistKey), JSON.stringify(visibility));\n}\n","import type { ColumnDef } from \"../column/types\";\nimport type { ColumnVisibility } from \"./types\";\n\n/** A missing key means visible, matching TanStack's own VisibilityState convention. */\nexport function isColumnVisible(visibility: ColumnVisibility, columnId: string): boolean {\n return visibility[columnId] !== false;\n}\n\n/** How many of `columns` are currently visible under `visibility`. */\nexport function countVisible<TRow>(\n columns: readonly ColumnDef<TRow>[],\n visibility: ColumnVisibility,\n): number {\n return columns.filter((column) => isColumnVisible(visibility, column.id)).length;\n}\n\nexport interface ColumnGroup<TRow> {\n /** undefined for the ungrouped section, which is always listed first. */\n group: string | undefined;\n columns: ColumnDef<TRow>[];\n}\n\n/**\n * Groups columns by `column.group`, preserving each group's first-seen order\n * and each column's original order within its group. The ungrouped section\n * (columns with no `group`) is always returned first, regardless of where\n * ungrouped columns actually fall in the input order.\n */\nexport function groupColumns<TRow>(columns: readonly ColumnDef<TRow>[]): ColumnGroup<TRow>[] {\n const ungrouped: ColumnDef<TRow>[] = [];\n const named = new Map<string, ColumnDef<TRow>[]>();\n\n for (const column of columns) {\n if (column.group === undefined) {\n ungrouped.push(column);\n continue;\n }\n const bucket = named.get(column.group);\n if (bucket) bucket.push(column);\n else named.set(column.group, [column]);\n }\n\n const result: ColumnGroup<TRow>[] = [];\n if (ungrouped.length > 0) result.push({ group: undefined, columns: ungrouped });\n for (const [group, groupedColumns] of named) result.push({ group, columns: groupedColumns });\n return result;\n}\n\n/** Whether toggling `columnId` off is currently allowed (must leave at least one column visible). */\nexport function canHideColumn<TRow>(\n columns: readonly ColumnDef<TRow>[],\n visibility: ColumnVisibility,\n columnId: string,\n): boolean {\n if (!isColumnVisible(visibility, columnId)) return true; // already hidden; \"hiding\" it again is a no-op\n return countVisible(columns, visibility) > 1;\n}\n\n/** Whether hiding every column in `groupColumnIds` at once is currently allowed. */\nexport function canHideGroup<TRow>(\n columns: readonly ColumnDef<TRow>[],\n visibility: ColumnVisibility,\n groupColumnIds: readonly string[],\n): boolean {\n const groupIds = new Set(groupColumnIds);\n const visibleOutsideGroup = columns.filter(\n (column) => !groupIds.has(column.id) && isColumnVisible(visibility, column.id),\n ).length;\n return visibleOutsideGroup > 0;\n}\n","import type { MenuItem, MenuItemContext } from \"./types\";\n\n/** A `MenuItem` with its `visible`/`disabled` predicates already evaluated against a context. */\nexport interface ResolvedMenuItem<TRow> extends MenuItem<TRow> {\n isDisabled: boolean;\n}\n\n/**\n * Evaluates each item's `visible`/`disabled` predicates against `ctx` (both\n * default to \"always\" when omitted), dropping items that aren't visible and\n * annotating the rest with `isDisabled`. Pure and UI-free on purpose, so the\n * gating logic itself — as opposed to the DropdownMenu rendering built on\n * top of it — can be unit-tested directly.\n */\nexport function resolveMenuItems<TRow>(\n items: readonly MenuItem<TRow>[],\n ctx: MenuItemContext<TRow>,\n): ResolvedMenuItem<TRow>[] {\n const resolved: ResolvedMenuItem<TRow>[] = [];\n for (const item of items) {\n const isVisible = item.visible ? item.visible(ctx) : true;\n if (!isVisible) continue;\n resolved.push({ ...item, isDisabled: item.disabled ? item.disabled(ctx) : false });\n }\n return resolved;\n}\n","import { MoreVertical } from \"lucide-react\";\nimport type { ReactElement } from \"react\";\nimport { useMemo } from \"react\";\nimport { Button } from \"../components/ui/button\";\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from \"../components/ui/dropdown-menu\";\nimport { stopRowClick } from \"../lib/utils\";\nimport { resolveMenuItems } from \"./resolveMenuItems\";\nimport type { MenuItem, MenuItemContext } from \"./types\";\n\ninterface ActionsMenuProps<TRow> {\n items: readonly MenuItem<TRow>[];\n ctx: MenuItemContext<TRow>;\n /** Accessible label for the trigger button, e.g. \"Row actions\" or \"Bulk actions\". */\n triggerLabel: string;\n}\n\n/**\n * Shared rendering for both `rowActions` (per-row kebab menu) and\n * `headerActions` (toolbar menu) — they differ only in what `ctx` they're\n * evaluated against (`{ row }` vs `{ selectedRows }`) and the trigger's\n * accessible label, so <DataGrid> passes those in rather than this module\n * having two near-identical components.\n */\nexport function ActionsMenu<TRow>({ items, ctx, triggerLabel }: ActionsMenuProps<TRow>): ReactElement | null {\n // `<DataGrid>` instantiates one `<ActionsMenu>` per visible row (for\n // `rowActions`) and re-renders every one of them on every scroll tick —\n // memoized on `ctx.row`/`ctx.selectedRows` (the parts of `ctx` any\n // `visible`/`disabled` predicate can actually read) rather than on `ctx`\n // itself, since the caller passes a fresh `{ row: row.original }` object\n // literal every render; that identity churn would otherwise defeat a\n // naive `useMemo([items, ctx])` even though the row itself hasn't changed.\n const resolved = useMemo(\n () => resolveMenuItems(items, ctx),\n [items, ctx.row, ctx.selectedRows],\n );\n if (resolved.length === 0) return null;\n\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"ghost\" size=\"icon\" aria-label={triggerLabel} onClick={stopRowClick}>\n <MoreVertical className=\"h-4 w-4\" aria-hidden />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n {resolved.map((item) => (\n <DropdownMenuItem\n key={item.id}\n danger={item.danger}\n disabled={item.isDisabled}\n onSelect={() => item.onSelect(ctx)}\n >\n {item.label}\n </DropdownMenuItem>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n","import * as DropdownMenuPrimitive from \"@radix-ui/react-dropdown-menu\";\nimport type { ComponentPropsWithoutRef, ElementRef } from \"react\";\nimport { forwardRef } from \"react\";\nimport { cn } from \"../../lib/utils\";\n\nexport const DropdownMenu = DropdownMenuPrimitive.Root;\nexport const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;\nexport const DropdownMenuGroup = DropdownMenuPrimitive.Group;\n\nexport const DropdownMenuContent = forwardRef<\n ElementRef<typeof DropdownMenuPrimitive.Content>,\n ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>\n>(({ className, sideOffset = 4, ...props }, ref) => (\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.Content\n ref={ref}\n sideOffset={sideOffset}\n className={cn(\n \"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md\",\n className,\n )}\n {...props}\n />\n </DropdownMenuPrimitive.Portal>\n));\nDropdownMenuContent.displayName = \"DropdownMenuContent\";\n\nexport const DropdownMenuItem = forwardRef<\n ElementRef<typeof DropdownMenuPrimitive.Item>,\n ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & { danger?: boolean }\n>(({ className, danger, ...props }, ref) => (\n <DropdownMenuPrimitive.Item\n ref={ref}\n className={cn(\n \"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n danger && \"text-destructive focus:bg-destructive/10 focus:text-destructive\",\n className,\n )}\n {...props}\n />\n));\nDropdownMenuItem.displayName = \"DropdownMenuItem\";\n\nexport const DropdownMenuSeparator = forwardRef<\n ElementRef<typeof DropdownMenuPrimitive.Separator>,\n ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n <DropdownMenuPrimitive.Separator\n ref={ref}\n className={cn(\"-mx-1 my-1 h-px bg-muted\", className)}\n {...props}\n />\n));\nDropdownMenuSeparator.displayName = \"DropdownMenuSeparator\";\n\nexport const DropdownMenuLabel = forwardRef<\n ElementRef<typeof DropdownMenuPrimitive.Label>,\n ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label>\n>(({ className, ...props }, ref) => (\n <DropdownMenuPrimitive.Label\n ref={ref}\n className={cn(\"px-2 py-1.5 text-xs font-medium text-muted-foreground\", className)}\n {...props}\n />\n));\nDropdownMenuLabel.displayName = \"DropdownMenuLabel\";\n","import { BarsArrowDownIcon, BarsArrowUpIcon, FunnelIcon } from \"@heroicons/react/24/outline\";\nimport type { CellContext, ColumnDef as TanstackColumnDef, ColumnSizingState, RowData } from \"@tanstack/react-table\";\nimport { columnResizingFeature, columnSizingFeature, flexRender, tableFeatures, useTable } from \"@tanstack/react-table\";\nimport { useVirtualizer } from \"@tanstack/react-virtual\";\nimport { ChevronRight, Loader2 } from \"lucide-react\";\nimport type { CSSProperties, ReactElement, ReactNode } from \"react\";\nimport { Fragment, useEffect, useLayoutEffect, useMemo, useRef, useState } from \"react\";\nimport { alignClassName } from \"../column/format\";\nimport type { ColumnDef } from \"../column/types\";\nimport { getColumnValue, isFilterable, isSortable } from \"../column/types\";\nimport { isColumnVisible } from \"../column-selector/visibility\";\nimport { Button } from \"../components/ui/button\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"../components/ui/popover\";\nimport { EditingBar } from \"../edit/EditingBar\";\nimport type { EditingCellContext } from \"../edit/editingState\";\nimport { useEditingState } from \"../edit/editingState\";\nimport { renderEditableCell } from \"../edit/renderEditableCell\";\nimport { renderDefaultFilterWidget } from \"../filter/registry\";\nimport type { FilterDescriptor } from \"../filter/types\";\nimport { cn, stopRowClick } from \"../lib/utils\";\nimport { SELECTION_COLUMN_WIDTH } from \"../lib/structuralColumns\";\nimport { ActionsMenu } from \"../menu/ActionsMenu\";\nimport { groupRows } from \"./groupRows\";\nimport { processClientData } from \"./processClientData\";\nimport type { DataGridProps } from \"./types\";\nimport { useGridState } from \"./useGridState\";\n\n// `<DataGrid>` never uses TanStack's own sorted/filtered/paginated row\n// models — `useGridState`/`processClientData` own all of that (see their\n// module docs) — so no stock row-model features are registered here, only\n// the automatic core row model. `columnSizingFeature`/`columnResizingFeature`\n// are the exception: `enableColumnResizing` (a table-level option, not a\n// stock row model) gates whether they actually do anything, so registering\n// them unconditionally costs nothing when a given <DataGrid> doesn't use\n// resize. Declared once at module scope, per TanStack's own guidance, rather\n// than recreated on every render.\nconst gridTableFeatures = tableFeatures({ columnSizingFeature, columnResizingFeature });\ntype GridTableFeatures = typeof gridTableFeatures;\n\n// TanStack's own default for any column without an explicit `size` (see\n// `getDefaultColumnSizingColumnDef` in `columnSizingFeature.utils`) — used\n// here only as the fallback a *pinned* column's offset math falls back to\n// before a `table` instance exists to ask `getSize()` directly.\nconst DEFAULT_COLUMN_SIZE = 150;\n\n// Fixed pixel width of each \"structural\" column DataGrid renders itself\n// (row-expand chevron, selection checkbox, per-row actions menu) rather than\n// deriving from `columns` — matched by the `width` style applied to their\n// own cells below. These always render pinned to the left/right edge of the\n// scroll container, regardless of whether any *data* column is `pinned`: a\n// data column's own pinned offset must reserve this space (see\n// `pinnedOffsets`'s `leadingOffset` param below), otherwise a pinned data\n// column sticks at the very edge and scrolls right over these non-sticky\n// columns, hiding them once the grid needs to scroll horizontally.\n// 36px (the shared `Button`'s `size=\"icon\"`) plus 4px of padding on each\n// side — big enough to comfortably hit (WCAG's 44px minimum touch target)\n// without the surrounding `p-1` cell padding eating into the button itself.\nconst DETAIL_COLUMN_WIDTH = 44;\nconst ROW_ACTIONS_COLUMN_WIDTH = 44;\n\n// Opaque equivalent of the body row's translucent `bg-foreground/5` zebra\n// tint, for the structural expand/selection/row-actions columns' `sticky`\n// cells — a translucent background there would let horizontally-scrolled\n// column content show through underneath on odd rows.\nconst STRUCTURAL_ZEBRA_BG_CLASS = \"bg-[color-mix(in_srgb,var(--color-foreground)_5%,var(--color-background))]\";\n\n/**\n * Cumulative sticky `left`/`right` pixel offset per pinned column id, in\n * visible-column order, starting from `leadingOffset` — the combined width\n * of whichever structural columns (expand/selection on the left,\n * row-actions on the right) render before the first data column on that\n * side, so a pinned data column's offset accounts for them instead of\n * assuming it's the first thing in the row.\n */\nfunction pinnedOffsets<TRow>(\n visibleColumns: ColumnDef<TRow>[],\n side: \"left\" | \"right\",\n getSize: (columnId: string) => number,\n leadingOffset: number,\n): Map<string, number> {\n const offsets = new Map<string, number>();\n let cursor = leadingOffset;\n const ordered = side === \"left\" ? visibleColumns : [...visibleColumns].reverse();\n for (const column of ordered) {\n if (column.pinned !== side) continue;\n offsets.set(column.id, cursor);\n cursor += getSize(column.id);\n }\n return offsets;\n}\n\n/** One run of contiguous visible columns for the optional spanning header-group row. */\ntype HeaderRun<TRow> =\n | { kind: \"group\"; label: string; columns: ColumnDef<TRow>[] }\n | { kind: \"single\"; column: ColumnDef<TRow> };\n\n/**\n * Groups contiguous visible columns sharing the same `headerGroup` label\n * into `\"group\"` runs; every other column becomes its own `\"single\"` run —\n * see `BaseColumn.headerGroup`'s doc for why this is positional rather than\n * collecting every same-labeled column regardless of adjacency.\n */\nfunction computeHeaderRuns<TRow>(columns: ColumnDef<TRow>[]): HeaderRun<TRow>[] {\n const runs: HeaderRun<TRow>[] = [];\n for (const column of columns) {\n const last = runs[runs.length - 1];\n if (column.headerGroup && last?.kind === \"group\" && last.label === column.headerGroup) {\n last.columns.push(column);\n } else if (column.headerGroup) {\n runs.push({ kind: \"group\", label: column.headerGroup, columns: [column] });\n } else {\n runs.push({ kind: \"single\", column });\n }\n }\n return runs;\n}\n\n/**\n * `toTanstackColumns` takes the editing-context REF (a stable object\n * identity for the whole component's lifetime), not the context value\n * directly — `flexRender` renders `columnDef.cell` as a component (its own\n * effective \"type\" for React's reconciliation, not just a value), so a\n * `cell` function that closes over `pendingEdits`/`editErrors` directly\n * would get a new identity every time either changes, i.e. on every\n * keystroke in any editor. React would then see a changed component type at\n * the same tree position and remount the ENTIRE table body under it — every\n * cell in every row, losing focus (and any other DOM/component state) out\n * from under the very input the user is typing into. Reading through\n * `useEditingState`'s `ctxRef` instead keeps every `cell` closure's identity\n * fixed to `visibleColumns` alone (exactly the `tanstackColumns` memo's\n * pre-editing dependency set below), while still reading whatever\n * `pendingEdits`/`editErrors`/`onEdit` actually is at the moment a cell\n * renders — the ref is reassigned every render, synchronously, before the\n * JSX returned below is processed. `<TreeDataGrid>` has no such memoized\n * column-def layer standing between it and every render, so it reads\n * `useEditingState`'s plain `ctx` value directly instead of needing this.\n */\nfunction toTanstackColumns<TRow extends RowData>(\n columns: ColumnDef<TRow>[],\n editingCtxRef: { current: EditingCellContext<TRow> | undefined },\n): TanstackColumnDef<GridTableFeatures, TRow, unknown>[] {\n return columns.map((column) => ({\n id: column.id,\n header: column.header,\n size: column.width,\n accessorFn: (row: TRow) => getColumnValue(column, row),\n cell: (info: CellContext<GridTableFeatures, TRow, unknown>): ReactNode =>\n renderEditableCell(column, info.row.original, info.getValue(), editingCtxRef.current),\n }));\n}\n\n/**\n * A single React Table component covering both client- and server-driven\n * data: filtering, sorting, and pagination all flow through one `GridState`,\n * with `dataSource.mode` deciding whether the grid computes the visible page\n * itself (`\"client\"`) or trusts a page the caller already computed\n * (`\"server\"`). Column filter UI defaults to the type-appropriate widget\n * from `src/filter` unless a column supplies its own `cell` renderer.\n */\nexport function DataGrid<TRow extends RowData>({\n columns,\n dataSource,\n getRowId,\n rowActions,\n headerActions,\n enableMultiSort = false,\n initialState,\n columnVisibility,\n renderDetail,\n enableColumnResizing = false,\n columnResizeMode = \"onChange\",\n columnSizing: controlledColumnSizing,\n onColumnSizingChange,\n virtualize,\n showPagination = true,\n getRowProps,\n getRowTestId,\n gridState,\n selectedIds: controlledSelectedIds,\n onSelectedIdsChange,\n testId,\n loading: loadingProp = false,\n groupBy,\n renderGroupHeader,\n defaultGroupsExpanded = true,\n expandedGroups: controlledExpandedGroups,\n onExpandedGroupsChange,\n zebra = true,\n editing,\n}: DataGridProps<TRow>): ReactElement {\n const { state, filtersByColumn, setColumnFilter, toggleSort, setPage } = useGridState(\n dataSource,\n initialState,\n gridState,\n );\n const [internalSelectedIds, setInternalSelectedIds] = useState<ReadonlySet<string>>(new Set());\n const selectedIds = controlledSelectedIds ?? internalSelectedIds;\n function updateSelectedIds(next: ReadonlySet<string>): void {\n setInternalSelectedIds(next);\n onSelectedIdsChange?.(next);\n }\n\n // Same controlled/uncontrolled pattern as `selectedIds` above — without\n // an internal fallback here, dragging a resize handle with no\n // `columnSizing`/`onColumnSizingChange` passed in (the common case; see\n // `enableColumnResizing`'s own doc) had nowhere to write the new width to\n // at all: `useTable` below was never given `state.columnSizing` nor\n // `onColumnSizingChange` in that case, so every drag was silently\n // discarded and no column ever actually resized.\n const [internalColumnSizing, setInternalColumnSizing] = useState<ColumnSizingState>({});\n const columnSizing = controlledColumnSizing ?? internalColumnSizing;\n function updateColumnSizing(next: ColumnSizingState): void {\n setInternalColumnSizing(next);\n onColumnSizingChange?.(next);\n }\n const [expandedIds, setExpandedIds] = useState<ReadonlySet<string>>(new Set());\n\n function toggleRowExpanded(id: string): void {\n setExpandedIds((prev) => {\n const next = new Set(prev);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n return next;\n });\n }\n\n // Same controlled/uncontrolled pattern as `selectedIds`/`columnSizing`\n // above. A key absent from this record (a group never toggled since it\n // was first seen) falls back to `defaultGroupsExpanded`, not `false` —\n // otherwise every group would render collapsed on first paint.\n const [internalExpandedGroups, setInternalExpandedGroups] = useState<Record<string, boolean>>({});\n const expandedGroupsState = controlledExpandedGroups ?? internalExpandedGroups;\n function updateExpandedGroups(next: Record<string, boolean>): void {\n setInternalExpandedGroups(next);\n onExpandedGroupsChange?.(next);\n }\n function isGroupExpanded(key: string): boolean {\n return expandedGroupsState[key] ?? defaultGroupsExpanded;\n }\n function toggleGroupExpanded(key: string): void {\n updateExpandedGroups({ ...expandedGroupsState, [key]: !isGroupExpanded(key) });\n }\n\n const { pendingEdits, editErrors, ctxRef: editingCtxRef, handleSaveEdits, handleDiscardEdits } = useEditingState(\n editing,\n getRowId,\n );\n\n // Single source of truth for \"which columns actually render\": every other\n // representation below (the TanStack column defs, the header lookup map,\n // the \"No results\" colSpan) is derived from this, not from `columns`\n // directly — otherwise two derived views of the same visibility could\n // disagree with each other.\n const visibleColumns = useMemo(\n () =>\n columnVisibility ? columns.filter((column) => isColumnVisible(columnVisibility, column.id)) : columns,\n [columns, columnVisibility],\n );\n\n // Depend on dataSource.mode/.data (and .rowCount, only in server mode)\n // rather than on `dataSource` itself — a caller that inlines\n // `dataSource={{ mode: \"client\", data: rows }}` (a natural pattern, and\n // exactly what every test in this file does) creates a new wrapper object\n // on every render, which would defeat this memo entirely if it depended on\n // the wrapper's own identity instead of its actually-relevant fields.\n const serverRowCount = dataSource.mode === \"server\" ? dataSource.rowCount : undefined;\n const { rows, rowCount } = useMemo(() => {\n if (dataSource.mode === \"client\") return processClientData(dataSource.data, state);\n return { rows: dataSource.data, rowCount: serverRowCount ?? 0 };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [dataSource.mode, dataSource.data, serverRowCount, state]);\n\n const selectedRows = useMemo(\n () => rows.filter((row) => selectedIds.has(getRowId(row))),\n [rows, selectedIds, getRowId],\n );\n const allOnPageSelected = rows.length > 0 && rows.every((row) => selectedIds.has(getRowId(row)));\n\n function toggleRowSelected(id: string): void {\n const next = new Set(selectedIds);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n updateSelectedIds(next);\n }\n\n function toggleAllOnPageSelected(): void {\n const next = new Set(selectedIds);\n for (const row of rows) {\n if (allOnPageSelected) next.delete(getRowId(row));\n else next.add(getRowId(row));\n }\n updateSelectedIds(next);\n }\n\n // Depends only on `visibleColumns` — exactly as before editing existed.\n // `editingCtxRef` is a `useRef` object, whose own identity never changes,\n // so it contributes nothing to this memo's invalidation; each column's\n // `cell` closure reads `editingCtxRef.current` fresh on every actual\n // render regardless (see `toTanstackColumns`'s doc for why that\n // indirection is what keeps typing in an editor from remounting the grid).\n const tanstackColumns = useMemo(() => toTanstackColumns(visibleColumns, editingCtxRef), [visibleColumns]);\n\n // A Map lookup instead of visibleColumns.find(...) inside the header\n // render loop below — the latter would make header rendering O(columns²)\n // (a linear scan per header, once per header) instead of O(columns).\n const columnById = useMemo(() => {\n const map = new Map<string, ColumnDef<TRow>>();\n for (const column of visibleColumns) map.set(column.id, column);\n return map;\n }, [visibleColumns]);\n\n const table = useTable({\n features: gridTableFeatures,\n data: rows,\n columns: tanstackColumns,\n getRowId,\n enableColumnResizing,\n columnResizeMode,\n state: { columnSizing },\n onColumnSizingChange: (updater) => {\n const next = typeof updater === \"function\" ? updater(columnSizing) : updater;\n updateColumnSizing(next);\n },\n });\n\n const pageCount = Math.max(1, Math.ceil(rowCount / state.pageSize));\n const loading = loadingProp || (dataSource.mode === \"server\" ? Boolean(dataSource.loading) : false);\n // The checkbox column itself is decoupled from `headerActions`: a caller\n // using controlled `selectedIds` (its own toolbar elsewhere on the page,\n // e.g. a bulk-export button) wants the checkboxes without <DataGrid>'s own\n // redundant \"{N} selected\" + bulk-actions bar, which stays gated on\n // `headerActions` specifically, below.\n const showSelectionColumn = Boolean(headerActions?.length) || controlledSelectedIds !== undefined;\n const showRowActionsColumn = Boolean(rowActions?.length);\n const showDetailColumn = Boolean(renderDetail);\n const totalColumnCount =\n visibleColumns.length +\n (showDetailColumn ? 1 : 0) +\n (showSelectionColumn ? 1 : 0) +\n (showRowActionsColumn ? 1 : 0);\n\n // The filter row is entirely opt-in — it only renders at all once at least\n // one visible, filterable column asks for it via `filterDisplay: \"row\"`\n // (see BaseColumn's doc for why: wide controls like NumberHistogramFilter\n // don't fit well collapsed into the header's compact popover trigger).\n const hasFilterRow = visibleColumns.some((column) => isFilterable(column) && column.filterDisplay === \"row\");\n\n // The extra spanning-label header row is entirely opt-in, same pattern as\n // `hasFilterRow` above — only renders at all once at least one visible\n // column sets `headerGroup` (see `BaseColumn.headerGroup`'s doc).\n const headerRuns = useMemo(() => computeHeaderRuns(visibleColumns), [visibleColumns]);\n const hasHeaderGroups = visibleColumns.some((column) => column.headerGroup);\n\n function renderFilterWidget(column: ColumnDef<TRow>): ReactNode {\n const value = filtersByColumn.get(column.id);\n const onChange = (next: FilterDescriptor | undefined): void => setColumnFilter(column.id, next);\n return column.renderFilter\n ? column.renderFilter(value, onChange, state.filter)\n : renderDefaultFilterWidget(column, value, onChange);\n }\n\n // `table.getColumn(id)?.getSize()` already resolves to exactly\n // `column.width ?? DEFAULT_COLUMN_SIZE` when resize has never touched that\n // column (TanStack's own resting value), and correctly reflects the live,\n // resized width once it has — one source of truth for both.\n const columnSize = (columnId: string): number => table.getColumn(columnId)?.getSize() ?? DEFAULT_COLUMN_SIZE;\n const leadingStructuralWidth =\n (showDetailColumn ? DETAIL_COLUMN_WIDTH : 0) + (showSelectionColumn ? SELECTION_COLUMN_WIDTH : 0);\n const trailingStructuralWidth = showRowActionsColumn ? ROW_ACTIONS_COLUMN_WIDTH : 0;\n\n // Memoized on `columnSizing` (rather than recomputed every render) because\n // every scroll tick re-renders this whole component (the virtualizer's own\n // state lives in React state) — without this, scrolling a wide/pinned grid\n // would reallocate both offset Maps (plus, for the right side, a full\n // `[...visibleColumns].reverse()` copy) on every single frame for no\n // reason: neither `visibleColumns` nor any column's resolved size changes\n // just because the scroll position did.\n // eslint-disable-next-line react-hooks/exhaustive-deps -- `columnSize` reads `columnSizing`/`table` by closure; both are covered by the explicit deps below.\n const leftPinnedOffsets = useMemo(\n () => pinnedOffsets(visibleColumns, \"left\", columnSize, leadingStructuralWidth),\n [visibleColumns, columnSizing, leadingStructuralWidth],\n );\n // eslint-disable-next-line react-hooks/exhaustive-deps -- see leftPinnedOffsets above.\n const rightPinnedOffsets = useMemo(\n () => pinnedOffsets(visibleColumns, \"right\", columnSize, trailingStructuralWidth),\n [visibleColumns, columnSizing, trailingStructuralWidth],\n );\n\n // Combines with (doesn't replace) the `width` style every cell already\n // gets — a pinned column's `left`/`right` offset only makes sense relative\n // to the scrolling `<table>`, and `bg-background` keeps whatever scrolls\n // underneath a pinned cell from showing through it. Only a pinned column,\n // or every column once `enableColumnResizing` is on, gets a concrete\n // width from `columnSize()` — an unpinned column in a non-resizable grid\n // keeps its original natural/content-driven sizing when it has no\n // explicit `width` of its own.\n function pinnedCellProps(\n column: ColumnDef<TRow>,\n area: \"header\" | \"body\" = \"body\",\n ): { className?: string; style?: CSSProperties } {\n // Pinned cells need an opaque background of their own so body/header\n // content scrolling underneath a sticky column doesn't show through —\n // matched to whichever section (header vs. body) they sit in, since the\n // header now carries `bg-muted` while the body stays `bg-background`.\n // Plain template strings rather than `cn()` throughout this function and\n // `structuralCellProps` below: every combination here is a fixed set of\n // internally-controlled utility classes touching unrelated CSS\n // properties (position/z-index vs. background), so there's never a\n // Tailwind conflict for `cn()`'s clsx+twMerge machinery to resolve —\n // called from the memoized per-column maps below, so it already runs\n // once per column per relevant state change, not once per cell.\n const bg = area === \"header\" ? \"bg-muted\" : \"bg-background\";\n if (column.pinned === \"left\") {\n return { className: `sticky z-10 ${bg}`, style: { left: leftPinnedOffsets.get(column.id), width: columnSize(column.id) } };\n }\n if (column.pinned === \"right\") {\n return { className: `sticky z-10 ${bg}`, style: { right: rightPinnedOffsets.get(column.id), width: columnSize(column.id) } };\n }\n if (enableColumnResizing) return { style: { width: columnSize(column.id) } };\n return { style: column.width ? { width: column.width } : undefined };\n }\n\n // Same sticky-pinning treatment as `pinnedCellProps`, for the structural\n // expand/selection/row-actions columns instead of a data column — these\n // always pin (they have no `pinned` prop of their own to opt out with),\n // so a pinned *data* column's reserved `leadingOffset`/its own offset\n // math actually lines up with a column that's really there, rather than a\n // gap left by content that scrolled away underneath it. The `border-b\n // p-1` base is baked in here (every call site below combined it with\n // exactly this className anyway) so callers use `.className`/`.classNameOdd`\n // directly instead of re-running `cn()` per row on an already-fixed string.\n function structuralCellProps(\n side: \"left\" | \"right\",\n offset: number,\n width: number,\n area: \"header\" | \"body\" = \"body\",\n ): { className: string; classNameOdd: string; style: CSSProperties } {\n // A header structural cell (select-all checkbox, expand-all chevron) also\n // sticks to the top of the scroll container -- z-20 so it stays above a\n // body row's own z-10 side-pinned cells once both are visible at once.\n const base = area === \"header\" ? \"border-b border-border p-1 sticky top-0 z-20\" : \"border-b border-border p-1 sticky z-10\";\n const bg = area === \"header\" ? \"bg-muted\" : \"bg-background\";\n const bgOdd = area === \"header\" ? \"bg-muted\" : STRUCTURAL_ZEBRA_BG_CLASS;\n return {\n className: `${base} ${bg}`,\n classNameOdd: `${base} ${bgOdd}`,\n style: { [side]: offset, width },\n };\n }\n\n // Computed once per render (cheap object literals) rather than inline at\n // each of the several header/body/filter-row call sites below, so the\n // selection column's offset — which depends on whether the detail column\n // renders before it — stays in exactly one place.\n const detailCellProps = structuralCellProps(\"left\", 0, DETAIL_COLUMN_WIDTH);\n const selectionCellProps = structuralCellProps(\n \"left\",\n showDetailColumn ? DETAIL_COLUMN_WIDTH : 0,\n SELECTION_COLUMN_WIDTH,\n );\n const rowActionsCellProps = structuralCellProps(\"right\", 0, ROW_ACTIONS_COLUMN_WIDTH);\n const detailHeaderCellProps = structuralCellProps(\"left\", 0, DETAIL_COLUMN_WIDTH, \"header\");\n const selectionHeaderCellProps = structuralCellProps(\n \"left\",\n showDetailColumn ? DETAIL_COLUMN_WIDTH : 0,\n SELECTION_COLUMN_WIDTH,\n \"header\",\n );\n const rowActionsHeaderCellProps = structuralCellProps(\"right\", 0, ROW_ACTIONS_COLUMN_WIDTH, \"header\");\n\n // Precomputed once per *column* (via the memoized maps below), not once\n // per column per *row* — the previous shape called `pinnedCellProps` (and\n // its `cn()` inside) from within the per-row cell-rendering loop, which\n // meant a virtualized 40-visible-row x 10-column grid ran that ~400 times\n // per render (and once per scroll-triggered re-render) instead of 10. Only\n // a pinned-or-resizing column needs a props object at all in the body;\n // every other column's `<td>` falls back to the plain base class below.\n const BODY_TD_BASE_CLASS = \"border-b border-border p-2\";\n function bodyCellClassAndStyle(column: ColumnDef<TRow>): { className: string; style?: CSSProperties } {\n const pinnedProps = column.pinned || enableColumnResizing ? pinnedCellProps(column) : undefined;\n return {\n className: pinnedProps?.className ? `${BODY_TD_BASE_CLASS} ${pinnedProps.className}` : BODY_TD_BASE_CLASS,\n style: pinnedProps?.style,\n };\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- `bodyCellClassAndStyle` closes over `enableColumnResizing`/`leftPinnedOffsets`/`rightPinnedOffsets`/`columnSizing`, all listed explicitly below.\n const bodyCellPropsByColumn = useMemo(() => {\n const map = new Map<string, { className: string; style?: CSSProperties }>();\n for (const column of visibleColumns) map.set(column.id, bodyCellClassAndStyle(column));\n return map;\n }, [visibleColumns, enableColumnResizing, leftPinnedOffsets, rightPinnedOffsets, columnSizing]);\n\n // Same idea for the two header contexts, each with their own fixed base\n // classes (the plain leaf header row vs. the opt-in filter row) — both\n // still only ever render once per <DataGrid> render (there's exactly one\n // header, not one per row), but memoizing keeps a pure-scroll re-render\n // from re-running `cn()` over every visible column for no reason either.\n function headerCellClassAndStyle(\n column: ColumnDef<TRow>,\n base: string,\n options: { sticky?: boolean } = {},\n ): { className: string; style?: CSSProperties } {\n const pinnedProps = pinnedCellProps(column, \"header\");\n const align = alignClassName(column);\n // `pinnedProps.className` (when set) comes first so `cn()`'s twMerge\n // resolves overlapping utilities (z-10/bg-muted from side-pinning) in\n // favor of whatever comes after -- the sticky-to-top treatment needs to\n // win: z-20 (not the side-pin's z-10) so a pinned column's header cell\n // still renders above a *body* row's own side-pinned cell once scrolled.\n const className = cn(\n base,\n align,\n pinnedProps.className,\n options.sticky && \"sticky top-0 z-20 bg-muted\",\n );\n return { className, style: pinnedProps.style };\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- see bodyCellPropsByColumn above.\n const leafHeaderCellPropsByColumn = useMemo(() => {\n const map = new Map<string, { className: string; style?: CSSProperties }>();\n for (const column of visibleColumns) {\n map.set(\n column.id,\n headerCellClassAndStyle(column, \"relative border-b border-border p-2 font-medium\", { sticky: true }),\n );\n }\n return map;\n }, [visibleColumns, enableColumnResizing, leftPinnedOffsets, rightPinnedOffsets, columnSizing]);\n // eslint-disable-next-line react-hooks/exhaustive-deps -- see bodyCellPropsByColumn above.\n const filterHeaderCellPropsByColumn = useMemo(() => {\n const map = new Map<string, { className: string; style?: CSSProperties }>();\n for (const column of visibleColumns) {\n map.set(column.id, headerCellClassAndStyle(column, \"border-b border-border p-2 align-top font-normal\"));\n }\n return map;\n }, [visibleColumns, enableColumnResizing, leftPinnedOffsets, rightPinnedOffsets, columnSizing]);\n\n const tableRows = table.getRowModel().rows;\n // Interleaving synthetic group-header rows (and hiding a collapsed\n // bucket's rows) needs a flattened index space to virtualize correctly —\n // out of scope for this pass, so `groupBy` forces virtualization off\n // rather than silently mis-rendering; see `groupBy`'s own doc.\n const shouldVirtualize = Boolean(virtualize) && !groupBy && tableRows.length > (virtualize?.threshold ?? 100);\n const groupedBuckets = useMemo(\n () => (groupBy ? groupRows(tableRows, (row) => groupBy(row.original)) : undefined),\n [tableRows, groupBy],\n );\n // A group-header row sticks right below the real `<thead>` while its\n // members scroll past -- `top` has to be the header's actual rendered\n // height (it varies with the optional filter/header-group rows and with\n // column-resize-driven wrapping), not a guessed constant. `ResizeObserver`\n // rather than a one-off measurement because header height can change after\n // mount (e.g. a column's header content wrapping to a second line).\n const theadRef = useRef<HTMLTableSectionElement>(null);\n const [groupHeaderTop, setGroupHeaderTop] = useState(0);\n useLayoutEffect(() => {\n if (!groupBy) return;\n const theadEl = theadRef.current;\n if (!theadEl) return;\n const updateHeight = (): void => setGroupHeaderTop(theadEl.getBoundingClientRect().height);\n updateHeight();\n const observer = new ResizeObserver(updateHeight);\n observer.observe(theadEl);\n return () => observer.disconnect();\n }, [groupBy]);\n const scrollRef = useRef<HTMLDivElement>(null);\n const virtualizer = useVirtualizer({\n count: tableRows.length,\n getScrollElement: () => scrollRef.current,\n estimateSize: () => virtualize?.estimatedRowHeight ?? 40,\n overscan: virtualize?.overscan ?? 10,\n enabled: shouldVirtualize,\n });\n const virtualItems = shouldVirtualize ? virtualizer.getVirtualItems() : [];\n const paddingTop = virtualItems.length > 0 ? virtualItems[0]!.start : 0;\n const paddingBottom =\n virtualItems.length > 0 ? virtualizer.getTotalSize() - virtualItems[virtualItems.length - 1]!.end : 0;\n\n // Fires `onEndReached` once per distinct `tableRows.length` — scrolling to\n // the last currently-loaded row while more rows might exist. Guarded by a\n // ref (not state) since it's bookkeeping for an effect, not something that\n // should itself trigger a render.\n const lastVisibleIndex = virtualItems.length > 0 ? virtualItems[virtualItems.length - 1]!.index : -1;\n const notifiedForLengthRef = useRef<number>(-1);\n useEffect(() => {\n if (!virtualize?.onEndReached) return;\n if (lastVisibleIndex < 0 || lastVisibleIndex < tableRows.length - 1) return;\n if (virtualize.hasMore === false) return;\n if (notifiedForLengthRef.current === tableRows.length) return;\n notifiedForLengthRef.current = tableRows.length;\n virtualize.onEndReached();\n }, [lastVisibleIndex, tableRows.length, virtualize]);\n\n function renderRow(row: (typeof tableRows)[number], measureRef?: (el: Element | null) => void): ReactNode {\n const isExpanded = expandedIds.has(row.id);\n // Each row gets its own `<tbody>` (a `<table>` can hold more than one)\n // instead of every row sharing one big one — a virtualizer's\n // `measureElement` observes exactly one DOM node per index, and a\n // `<tbody>`'s own bounding rect already reflects the combined height of\n // every `<tr>` inside it, INCLUDING an expanded detail row right below\n // it. Measuring the row's own `<tr>` alone (the alternative) would never\n // notice the detail panel's height at all once virtualized.\n const rowProps = getRowProps?.(row.original);\n const rowTestId = getRowTestId?.(row.original) ?? `row-${row.id}`;\n const isOddRow = zebra && row.index % 2 === 1;\n return (\n <tbody key={row.id} ref={measureRef} data-index={row.index}>\n <tr\n {...rowProps}\n data-testid={rowTestId}\n // `cn()` (clsx+twMerge) is only needed here when a caller-supplied\n // `getRowProps().className` might itself carry a conflicting\n // `bg-*` utility that needs resolving against the zebra stripe —\n // the overwhelmingly common case (no `getRowProps` at all) skips\n // straight to a plain string, avoiding that per-row allocation\n // for every grid that doesn't use the feature.\n className={\n rowProps?.className\n ? cn(\"divide-x divide-border\", rowProps.className as string, isOddRow && \"bg-foreground/5\")\n : `divide-x divide-border${isOddRow ? \" bg-foreground/5\" : \"\"}`\n }\n >\n {showDetailColumn && (\n <td\n className={isOddRow ? detailCellProps.classNameOdd : detailCellProps.className}\n style={detailCellProps.style}\n >\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n aria-label={`${isExpanded ? \"Collapse\" : \"Expand\"} row ${row.id}`}\n aria-expanded={isExpanded}\n onClick={(event) => {\n stopRowClick(event);\n toggleRowExpanded(row.id);\n }}\n >\n <ChevronRight\n className={`h-4 w-4 transition-transform${isExpanded ? \" rotate-90\" : \"\"}`}\n aria-hidden\n />\n </Button>\n </td>\n )}\n {showSelectionColumn && (\n <td\n className={isOddRow ? selectionCellProps.classNameOdd : selectionCellProps.className}\n style={selectionCellProps.style}\n >\n <input\n type=\"checkbox\"\n aria-label={`Select row ${row.id}`}\n checked={selectedIds.has(row.id)}\n onClick={stopRowClick}\n onChange={() => toggleRowSelected(row.id)}\n />\n </td>\n )}\n {/* getVisibleCells() moved behind columnVisibilityFeature in\n v9, which isn't registered here — visibility is already\n resolved above via `visibleColumns`, so every column\n reaching the table is meant to render; getAllCells() is\n the core-only equivalent for that already-filtered set. */}\n {row.getAllCells().map((cell) => {\n const cellProps = bodyCellPropsByColumn.get(cell.column.id);\n return (\n <td key={cell.id} style={cellProps?.style} className={cellProps?.className ?? BODY_TD_BASE_CLASS}>\n {flexRender(cell.column.columnDef.cell, cell.getContext())}\n </td>\n );\n })}\n {showRowActionsColumn && rowActions && (\n <td\n className={isOddRow ? rowActionsCellProps.classNameOdd : rowActionsCellProps.className}\n style={rowActionsCellProps.style}\n >\n <ActionsMenu items={rowActions} ctx={{ row: row.original }} triggerLabel={`Row actions for ${row.id}`} />\n </td>\n )}\n </tr>\n {showDetailColumn && isExpanded && renderDetail && (\n <tr data-testid={`${rowTestId}-detail`}>\n <td colSpan={totalColumnCount} className=\"border-b border-border p-2\">\n {renderDetail(row.original)}\n </td>\n </tr>\n )}\n </tbody>\n );\n }\n\n // One `<tbody>` for the group-header row, then each member row as its own\n // sibling `<tbody>` (via `renderRow`) — a `<tbody>` can't nest inside\n // another, and a `<table>` happily holds any number of them.\n function renderGroupedBucket(bucket: NonNullable<typeof groupedBuckets>[number]): ReactNode {\n const expanded = isGroupExpanded(bucket.key);\n const originalRows = bucket.items.map((row) => row.original);\n return (\n <Fragment key={`group-${bucket.key}`}>\n <tbody>\n <tr data-testid={`group-header-${bucket.key}`}>\n <td\n colSpan={totalColumnCount}\n className={cn(\n \"sticky z-20 border-b p-2 font-medium\",\n zebra ? \"bg-muted\" : \"bg-background\",\n )}\n style={{ top: groupHeaderTop }}\n >\n <button\n type=\"button\"\n className=\"flex w-full items-center gap-1 text-left\"\n onClick={() => toggleGroupExpanded(bucket.key)}\n aria-expanded={expanded}\n >\n <ChevronRight\n className={`h-4 w-4 shrink-0 transition-transform${expanded ? \" rotate-90\" : \"\"}`}\n aria-hidden\n />\n {renderGroupHeader\n ? renderGroupHeader(bucket.key, originalRows, expanded)\n : `${bucket.key} (${originalRows.length})`}\n </button>\n </td>\n </tr>\n </tbody>\n {expanded && bucket.items.map((row) => renderRow(row))}\n </Fragment>\n );\n }\n\n return (\n // `h-full`/`min-h-0` here and on the two wrappers below are no-ops unless\n // a consumer itself gives <DataGrid> a bounded height (e.g. wraps it in\n // its own `flex-1 min-h-0` container) -- percentage heights fall back to\n // `auto` with no definite ancestor height, so every existing natural-\n // height usage renders unchanged. Wrap it in a bounded container, though,\n // and this cascades down to the actual scrolling div below instead of\n // silently rendering at full unbounded content height, which used to\n // force any consumer wanting a bounded, page-filling grid to add its own\n // *second* `overflow-auto` wrapper around <DataGrid> -- two nested scroll\n // containers (that one scrolling vertically, this component's own\n // `overflow-x-auto` div scrolling horizontally) fighting over which one\n // a sticky header/pinned column is actually stuck relative to.\n <div className=\"flex h-full min-h-0 flex-col gap-2\" data-testid=\"datagrid\">\n {editing && pendingEdits.size > 0 && (\n <EditingBar\n editing={editing}\n pendingRowCount={pendingEdits.size}\n editErrors={editErrors}\n onSave={() => void handleSaveEdits()}\n onDiscard={handleDiscardEdits}\n />\n )}\n {showSelectionColumn && headerActions && (\n <div className=\"flex shrink-0 items-center justify-between text-sm\">\n <span>{selectedRows.length} selected</span>\n <ActionsMenu items={headerActions} ctx={{ selectedRows }} triggerLabel=\"Bulk actions\" />\n </div>\n )}\n <div className=\"relative min-h-0 flex-1\">\n {loading && (\n <div\n data-testid=\"datagrid-loading-overlay\"\n className=\"absolute inset-0 z-30 flex items-center justify-center bg-background/60\"\n >\n <Loader2 className=\"h-6 w-6 animate-spin text-muted-foreground\" aria-hidden />\n </div>\n )}\n <div\n ref={scrollRef}\n data-testid={testId}\n className=\"h-full overflow-auto rounded-md border\"\n style={shouldVirtualize ? { maxHeight: virtualize?.maxBodyHeight ?? 480 } : undefined}\n >\n {/*\n `table-fixed` (only once every column has a concrete pixel width —\n see `enableColumnResizing`'s own doc) is required for that width to\n actually determine the rendered column, not just hint at it: the\n browser's default `table-layout: auto` algorithm treats a cell's\n `style.width` as one input among several (including cell content\n across every row) and can render a column far narrower than its\n specified width regardless of what `columnSize()` above computes —\n which silently broke both column resizing (the state updated\n correctly; the table just never reflected it) and pinned columns'\n sticky offset math (each depends on the *rendered* width matching\n `getSize()`). Omitted otherwise so a grid with no explicit widths\n keeps natural, content-driven column sizing.\n */}\n <table className={cn(\"w-full border-collapse text-sm\", enableColumnResizing && \"table-fixed\")}>\n <thead ref={theadRef} className=\"bg-muted\">\n {table.getHeaderGroups().map((headerGroup) => {\n // `headerById` backs both the optional spanning-label row below\n // and this leaf row — `table.getHeaderGroups()` always yields\n // exactly one entry (no TanStack column-grouping feature is\n // registered; `headerGroup?: string` is a rendering-only concern\n // handled entirely by `computeHeaderRuns`), so this map is built\n // fresh per (the single) headerGroup, not hoisted further up.\n const headerById = new Map(headerGroup.headers.map((header) => [header.id, header]));\n\n function renderLeafHeaderCell(column: ColumnDef<TRow>, rowSpan?: number): ReactNode {\n const header = headerById.get(column.id);\n if (!header) return null;\n const sortEntry = state.sort.find((s) => s.field === column.id);\n const sortable = isSortable(column);\n const filterable = isFilterable(column) && column.filterDisplay !== \"row\";\n const cellProps = leafHeaderCellPropsByColumn.get(column.id);\n const headerContent = column.renderHeader ? column.renderHeader(column) : column.header;\n return (\n <th key={header.id} rowSpan={rowSpan} style={cellProps?.style} className={cellProps?.className}>\n <div className=\"flex items-center gap-1\">\n {sortable ? (\n <button\n type=\"button\"\n className=\"flex items-center gap-1 rounded-sm px-1 hover:bg-accent hover:text-accent-foreground\"\n onClick={(event) =>\n toggleSort(column.id, enableMultiSort && event.shiftKey, column.sortDescFirst === true)\n }\n >\n {headerContent}\n {sortEntry?.dir === \"asc\" && <BarsArrowUpIcon className=\"h-3 w-3\" aria-hidden />}\n {sortEntry?.dir === \"desc\" && <BarsArrowDownIcon className=\"h-3 w-3\" aria-hidden />}\n </button>\n ) : (\n // Not wrapped in a `<button>` (unlike the sortable branch above): a\n // disabled `<button>` swallows pointer events for its entire subtree,\n // silently breaking any interactive content a column's own `renderHeader`\n // embeds directly (bare filter inputs, \"select/reject all\" buttons) —\n // clicks never reach them at all, even though nothing looks wrong visually.\n <div className=\"flex items-center gap-1 px-1\">{headerContent}</div>\n )}\n {filterable && (\n <Popover>\n <PopoverTrigger asChild>\n <Button variant=\"ghost\" size=\"icon\" aria-label={`Filter ${column.header}`}>\n <FunnelIcon\n className={cn(\n \"h-3 w-3\",\n filtersByColumn.get(column.id) !== undefined\n ? \"opacity-100 text-primary\"\n : \"opacity-40\",\n )}\n aria-hidden\n />\n </Button>\n </PopoverTrigger>\n <PopoverContent>{renderFilterWidget(column)}</PopoverContent>\n </Popover>\n )}\n </div>\n {enableColumnResizing && (\n <div\n data-testid={`resize-handle-${column.id}`}\n onMouseDown={header.getResizeHandler()}\n onTouchStart={header.getResizeHandler()}\n className=\"absolute right-0 top-0 h-full w-1 cursor-col-resize touch-none select-none hover:bg-accent\"\n />\n )}\n </th>\n );\n }\n\n return (\n <Fragment key={headerGroup.id}>\n {hasHeaderGroups && (\n <tr key={`${headerGroup.id}-groups`} data-testid=\"header-group-row\" className=\"divide-x divide-border\">\n {showDetailColumn && (\n <th\n rowSpan={2}\n className={detailHeaderCellProps.className}\n style={detailHeaderCellProps.style}\n aria-hidden\n />\n )}\n {showSelectionColumn && (\n <th\n rowSpan={2}\n className={selectionHeaderCellProps.className}\n style={selectionHeaderCellProps.style}\n >\n <input\n type=\"checkbox\"\n aria-label=\"Select all rows on this page\"\n checked={allOnPageSelected}\n onChange={toggleAllOnPageSelected}\n />\n </th>\n )}\n {headerRuns.map((run) =>\n run.kind === \"single\" ? (\n renderLeafHeaderCell(run.column, 2)\n ) : (\n <th\n key={`group-${run.label}-${run.columns[0]!.id}`}\n colSpan={run.columns.length}\n className=\"border-b border-border p-2 text-center font-medium\"\n >\n {run.label}\n </th>\n ),\n )}\n {showRowActionsColumn && (\n <th\n rowSpan={2}\n className={rowActionsHeaderCellProps.className}\n style={rowActionsHeaderCellProps.style}\n aria-label=\"Row actions\"\n />\n )}\n </tr>\n )}\n <tr key={headerGroup.id} className=\"divide-x divide-border\">\n {!hasHeaderGroups && showDetailColumn && (\n <th\n className={detailHeaderCellProps.className}\n style={detailHeaderCellProps.style}\n aria-hidden\n />\n )}\n {!hasHeaderGroups && showSelectionColumn && (\n <th\n className={selectionHeaderCellProps.className}\n style={selectionHeaderCellProps.style}\n >\n <input\n type=\"checkbox\"\n aria-label=\"Select all rows on this page\"\n checked={allOnPageSelected}\n onChange={toggleAllOnPageSelected}\n />\n </th>\n )}\n {headerGroup.headers.map((header) => {\n const column = columnById.get(header.id);\n // Ungrouped columns already rendered (with rowSpan={2})\n // in the spanning-label row above — only a column that's\n // part of an actual headerGroup run needs its own cell\n // in this leaf row.\n if (!column || (hasHeaderGroups && !column.headerGroup)) return null;\n return renderLeafHeaderCell(column);\n })}\n {!hasHeaderGroups && showRowActionsColumn && (\n <th\n className={rowActionsHeaderCellProps.className}\n style={rowActionsHeaderCellProps.style}\n aria-label=\"Row actions\"\n />\n )}\n </tr>\n </Fragment>\n );\n })}\n {hasFilterRow && (\n <tr data-testid=\"filter-row\" className=\"divide-x divide-border\">\n {showDetailColumn && (\n <th className={detailHeaderCellProps.className} style={detailHeaderCellProps.style} />\n )}\n {showSelectionColumn && (\n <th\n className={selectionHeaderCellProps.className}\n style={selectionHeaderCellProps.style}\n />\n )}\n {visibleColumns.map((column) => {\n const cellProps = filterHeaderCellPropsByColumn.get(column.id);\n return (\n <th key={column.id} style={cellProps?.style} className={cellProps?.className}>\n {isFilterable(column) && column.filterDisplay === \"row\" ? renderFilterWidget(column) : null}\n </th>\n );\n })}\n {showRowActionsColumn && (\n <th\n className={rowActionsHeaderCellProps.className}\n style={rowActionsHeaderCellProps.style}\n />\n )}\n </tr>\n )}\n </thead>\n {rows.length === 0 ? (\n <tbody>\n <tr>\n <td colSpan={totalColumnCount} className=\"p-4 text-center text-muted-foreground\">\n {loading ? \"Loading...\" : \"No results.\"}\n </td>\n </tr>\n </tbody>\n ) : shouldVirtualize ? (\n <>\n {paddingTop > 0 && (\n <tbody>\n <tr aria-hidden=\"true\">\n <td colSpan={totalColumnCount} style={{ padding: 0, border: \"none\", height: paddingTop }} />\n </tr>\n </tbody>\n )}\n {virtualItems.map((virtualItem) =>\n renderRow(tableRows[virtualItem.index]!, (el) => virtualizer.measureElement(el)),\n )}\n {paddingBottom > 0 && (\n <tbody>\n <tr aria-hidden=\"true\">\n <td colSpan={totalColumnCount} style={{ padding: 0, border: \"none\", height: paddingBottom }} />\n </tr>\n </tbody>\n )}\n </>\n ) : groupedBuckets ? (\n groupedBuckets.map((bucket) => renderGroupedBucket(bucket))\n ) : (\n tableRows.map((row) => renderRow(row))\n )}\n </table>\n </div>\n </div>\n {showPagination && (\n <div className=\"flex shrink-0 items-center justify-between text-sm\">\n <span>\n Page {state.page + 1} of {pageCount} ({rowCount} row{rowCount === 1 ? \"\" : \"s\"})\n </span>\n <div className=\"flex gap-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n disabled={state.page <= 0}\n onClick={() => setPage(state.page - 1)}\n >\n Previous\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n disabled={state.page >= pageCount - 1}\n onClick={() => setPage(state.page + 1)}\n >\n Next\n </Button>\n </div>\n </div>\n )}\n </div>\n );\n}\n","/**\n * Fixed pixel width of the selection-checkbox column both `<DataGrid>` and\n * `<TreeDataGrid>` render themselves, matched by the `width` style applied\n * to the column's own header/body cells. 36px (the shared `Button`'s\n * `size=\"icon\"`) plus 4px of padding on each side — big enough to comfortably\n * hit WCAG's 44px minimum touch target.\n */\nexport const SELECTION_COLUMN_WIDTH = 44;\n","export interface GroupBucket<T> {\n key: string;\n items: T[];\n}\n\n/**\n * Buckets `items` by `groupBy(item)`, preserving first-seen bucket order —\n * no re-sorting of either the buckets or the items within one. Framework-free\n * (no React/TanStack types) so it's independently testable.\n */\nexport function groupRows<T>(items: T[], groupBy: (item: T) => string): GroupBucket<T>[] {\n const buckets = new Map<string, T[]>();\n for (const item of items) {\n const key = groupBy(item);\n const bucket = buckets.get(key);\n if (bucket) bucket.push(item);\n else buckets.set(key, [item]);\n }\n return Array.from(buckets, ([key, groupItems]) => ({ key, items: groupItems }));\n}\n","import { compareValues, evaluateFilter, getFieldValue } from \"../filter/evaluate\";\nimport type { GridState, SortDescriptor } from \"../filter/types\";\n\n/** Multi-column comparator: earlier entries in `sort` take priority; nulls always sort last. */\nfunction makeComparator<TRow>(sort: readonly SortDescriptor[]): (a: TRow, b: TRow) => number {\n return (a, b) => {\n for (const { field, dir } of sort) {\n const av = getFieldValue(a, field);\n const bv = getFieldValue(b, field);\n const aNull = av === null || av === undefined;\n const bNull = bv === null || bv === undefined;\n if (aNull && bNull) continue;\n if (aNull) return 1;\n if (bNull) return -1;\n const cmp = compareValues(av, bv) ?? 0;\n if (cmp !== 0) return dir === \"asc\" ? cmp : -cmp;\n }\n return 0;\n };\n}\n\nexport interface ClientProcessingResult<TRow> {\n rows: TRow[];\n rowCount: number;\n}\n\n/**\n * Applies a `GridState` to an in-memory row array: filter (via\n * `evaluateFilter`, matching sql.py/meili.py semantics), then multi-column\n * sort, then pagination. This is the entire implementation of `\"client\"`\n * DataSource mode — `\"server\"` mode skips this and trusts the caller.\n */\nexport function processClientData<TRow>(\n data: readonly TRow[],\n state: GridState,\n): ClientProcessingResult<TRow> {\n const filtered = state.filter ? data.filter((row) => evaluateFilter(row, state.filter)) : data;\n const sorted = state.sort.length > 0 ? [...filtered].sort(makeComparator(state.sort)) : filtered;\n const start = state.page * state.pageSize;\n const rows = sorted.slice(start, start + state.pageSize);\n return { rows, rowCount: sorted.length };\n}\n","import { useCallback, useEffect, useMemo, useState } from \"react\";\nimport type { FilterDescriptor, GridState, SortDescriptor } from \"../filter/types\";\nimport { fieldKey } from \"../filter/types\";\nimport { useDebouncedCallback } from \"../hooks/useDebouncedCallback\";\nimport type { DataSource } from \"./types\";\n\nconst DEFAULT_STATE: GridState = { filter: null, sort: [], page: 0, pageSize: 20 };\nconst FILTER_CHANGE_DEBOUNCE_MS = 300;\n\nexport interface GridStateController {\n state: GridState;\n /** Per-column FilterDescriptor, keyed by column id. */\n filtersByColumn: ReadonlyMap<string, FilterDescriptor>;\n setColumnFilter: (columnId: string, next: FilterDescriptor | undefined) => void;\n /**\n * Cycles a column's sort: none -> asc -> desc -> none, or none -> desc -> asc -> none\n * when `descFirst` is true (see `BaseColumn.sortDescFirst`) — for a column whose\n * \"interesting\" direction is descending (revenue, risk scores, backlog), so a first\n * click surfaces the highest/most-urgent values instead of the lowest/safest ones.\n * Adds/replaces per `enableMultiSort`.\n */\n toggleSort: (columnId: string, additive: boolean, descFirst?: boolean) => void;\n setPage: (page: number) => void;\n setPageSize: (pageSize: number) => void;\n}\n\nfunction nextSortDir(\n current: SortDescriptor[\"dir\"] | undefined,\n descFirst: boolean,\n): SortDescriptor[\"dir\"] | undefined {\n const [first, second] = descFirst ? ([\"desc\", \"asc\"] as const) : ([\"asc\", \"desc\"] as const);\n if (current === undefined) return first;\n if (current === first) return second;\n return undefined;\n}\n\n/**\n * Owns `GridState` (filter/sort/page/pageSize) for `<DataGrid>` and, in\n * `\"server\"` mode, calls `dataSource.onStateChange` on every change —\n * debounced ~300ms for filter changes (typing), immediately for sort/page\n * changes. In `\"client\"` mode `onStateChange` doesn't exist and is never\n * called; the grid re-filters/re-sorts the in-memory array itself instead.\n *\n * Deliberately takes no `columns` and enforces no `sortable`/`filterable`\n * policy itself — `toggleSort`/`setColumnFilter` will happily act on any\n * column id passed to them. `<DataGrid>` is the only place that consults\n * `isSortable`/`isFilterable` (in its rendered header, gating the sort\n * button's `onClick` and whether a filter icon renders at all), because this\n * hook is a generic state container, not a per-column-policy enforcer. A\n * caller that imports this hook directly (bypassing <DataGrid>'s UI\n * entirely) is responsible for applying that same policy itself before\n * calling these setters, the same way it's responsible for its own markup.\n *\n * `externalState`, when given (from the very first render on), makes this\n * fully caller-controlled: whenever it changes identity, internal `state`\n * is overwritten to match (restoring a saved filter, clearing everything at\n * once, applying a URL-restored state — all without remounting\n * `<DataGrid>`). Pass the *same* value back in that a server-mode\n * `dataSource.onStateChange` call just handed you (mirror it into your own\n * state and pass that straight through) — a fresh object literal recreated\n * every render re-triggers this resync every render too. Like a controlled\n * `<input>`, don't switch between passing `externalState` and omitting it\n * across the component's lifetime — start controlled and stay controlled,\n * or start uncontrolled and stay uncontrolled; toggling to `undefined`\n * later does not hand ownership back, it just stops resyncing.\n */\nexport function useGridState<TRow>(\n dataSource: DataSource<TRow>,\n initialState?: Partial<GridState>,\n externalState?: GridState,\n): GridStateController {\n const [state, setState] = useState<GridState>(\n () => externalState ?? { ...DEFAULT_STATE, ...initialState },\n );\n\n const notifyRaw = useCallback(\n (next: GridState) => {\n if (dataSource.mode === \"server\") dataSource.onStateChange(next);\n },\n [dataSource],\n );\n const { run: notifyDebounced, cancel: cancelDebouncedNotify } = useDebouncedCallback(\n notifyRaw,\n FILTER_CHANGE_DEBOUNCE_MS,\n );\n\n // Sort/page changes must cancel any pending debounced filter notification\n // first — otherwise a filter edit made just before a sort/page click\n // schedules a notify that fires ~300ms later with its own (now stale)\n // captured state, silently overwriting the immediate sort/page change the\n // caller already received and acted on.\n const notifyNow = useCallback(\n (next: GridState) => {\n cancelDebouncedNotify();\n notifyRaw(next);\n },\n [notifyRaw, cancelDebouncedNotify],\n );\n\n // Pushing a new `externalState` must cancel any pending debounced filter\n // notification the same way notifyNow does for sort/page — otherwise a\n // filter edit made just before the push schedules a notify that fires\n // ~300ms later with its own (now stale) captured state. If the caller\n // mirrors that stale notify back into whatever it passes as\n // `externalState` (the exact two-way pattern this hook's own doc\n // recommends), it would silently resurrect the state this push just\n // overwrote.\n // eslint-disable-next-line react-hooks/exhaustive-deps -- see externalState's own doc above: intentionally only resyncs when its *identity* changes, not on every render.\n useEffect(() => {\n if (!externalState) return;\n cancelDebouncedNotify();\n setState(externalState);\n }, [externalState, cancelDebouncedNotify]);\n\n const filtersByColumn = useMemo(() => {\n const map = new Map<string, FilterDescriptor>();\n for (const child of state.filter?.filters ?? []) {\n if (\"operator\" in child) map.set(fieldKey(child.field), child);\n }\n return map;\n }, [state.filter]);\n\n /**\n * Replaces (or clears) one column's filter and combines every column's\n * current filter with AND — this is the entire filtering model the grid's\n * built-in per-column widgets implement, by design: one FilterDescriptor\n * per filterable column, ANDed together.\n *\n * `GridState.filter` is typed as a general `CompositeFilterDescriptor`\n * (supporting arbitrary AND/OR nesting) because the *type* is shared with\n * server-side consumers (sql.py/meili.py) and custom filter UIs that want\n * to build more elaborate queries. But if `initialState.filter` arrives\n * with `logic: \"or\"` (or any nesting beyond flat per-column leaves), the\n * very next edit through a built-in filter widget rewrites it flat as\n * `{ logic: \"and\", filters: [...] }`, silently discarding that structure.\n * Supporting arbitrary boolean composition in the built-in widgets would\n * require a genuinely different UI (a query builder), which is out of\n * scope here — this is a known, accepted limitation of the column-scoped\n * filtering model, not an oversight.\n */\n const setColumnFilter = useCallback(\n (columnId: string, next: FilterDescriptor | undefined) => {\n setState((prev) => {\n const remaining = (prev.filter?.filters ?? []).filter(\n (f) => !(\"operator\" in f) || fieldKey(f.field) !== columnId,\n );\n const filters = next ? [...remaining, next] : remaining;\n const nextState: GridState = {\n ...prev,\n filter: filters.length > 0 ? { logic: \"and\", filters } : null,\n page: 0,\n };\n notifyDebounced(nextState);\n return nextState;\n });\n },\n [notifyDebounced],\n );\n\n const toggleSort = useCallback(\n (columnId: string, additive: boolean, descFirst = false) => {\n setState((prev) => {\n const existing = prev.sort.find((s) => s.field === columnId);\n const dir = nextSortDir(existing?.dir, descFirst);\n const withoutColumn = prev.sort.filter((s) => s.field !== columnId);\n const base = additive ? withoutColumn : [];\n const sort = dir ? [...base, { field: columnId, dir }] : base;\n const nextState: GridState = { ...prev, sort };\n notifyNow(nextState);\n return nextState;\n });\n },\n [notifyNow],\n );\n\n const setPage = useCallback(\n (page: number) => {\n setState((prev) => {\n const nextState: GridState = { ...prev, page };\n notifyNow(nextState);\n return nextState;\n });\n },\n [notifyNow],\n );\n\n const setPageSize = useCallback(\n (pageSize: number) => {\n setState((prev) => {\n const nextState: GridState = { ...prev, pageSize, page: 0 };\n notifyNow(nextState);\n return nextState;\n });\n },\n [notifyNow],\n );\n\n return { state, filtersByColumn, setColumnFilter, toggleSort, setPage, setPageSize };\n}\n","import { useEffect, useMemo, useRef } from \"react\";\n\nexport interface DebouncedCallback<TArgs extends unknown[]> {\n /** Schedules `callback`, resetting the timer if one is already pending. */\n run: (...args: TArgs) => void;\n /**\n * Discards any pending scheduled call without invoking it. Callers that\n * also have an immediate, non-debounced path to the same effect (see\n * `<DataGrid>`'s `useGridState`, where sort/page changes call the\n * underlying callback immediately) need this to prevent an\n * already-in-flight debounced call from firing later with stale state and\n * overwriting the immediate one.\n */\n cancel: () => void;\n}\n\n/**\n * Returns a stable `{ run, cancel }` pair. `run` delays invoking `callback`\n * until `delayMs` has elapsed since the last call, resetting the timer on\n * every call in between (standard trailing-edge debounce). Used by\n * `<DataGrid>` in `\"server\"` mode to avoid firing `onStateChange` on every\n * keystroke while a filter widget is being typed into.\n */\nexport function useDebouncedCallback<TArgs extends unknown[]>(\n callback: (...args: TArgs) => void,\n delayMs: number,\n): DebouncedCallback<TArgs> {\n const callbackRef = useRef(callback);\n callbackRef.current = callback;\n\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n\n useEffect(() => {\n return () => {\n clearTimeout(timeoutRef.current);\n };\n }, []);\n\n return useMemo(() => {\n return {\n run: (...args: TArgs): void => {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = setTimeout(() => {\n callbackRef.current(...args);\n }, delayMs);\n },\n cancel: (): void => {\n clearTimeout(timeoutRef.current);\n },\n };\n }, [delayMs]);\n}\n","import { useVirtualizer } from \"@tanstack/react-virtual\";\nimport { AlertCircle, ChevronDown, ChevronRight, Loader2 } from \"lucide-react\";\nimport type { ReactElement, ReactNode } from \"react\";\nimport { useEffect, useMemo, useRef } from \"react\";\nimport { alignClassName } from \"../column/format\";\nimport type { ColumnDef } from \"../column/types\";\nimport { getColumnValue } from \"../column/types\";\nimport { Checkbox } from \"../components/ui/checkbox\";\nimport { EditingBar } from \"../edit/EditingBar\";\nimport type { EditingCellContext } from \"../edit/editingState\";\nimport { useEditingState } from \"../edit/editingState\";\nimport { renderEditableCell } from \"../edit/renderEditableCell\";\nimport { SELECTION_COLUMN_WIDTH } from \"../lib/structuralColumns\";\nimport { cn, stopRowClick } from \"../lib/utils\";\nimport { ActionsMenu } from \"../menu/ActionsMenu\";\nimport type { FlatTreeRow } from \"./flattenTree\";\nimport { flattenTree } from \"./flattenTree\";\nimport { computeSelectionStates } from \"./selectionState\";\nimport type { RowSelectionState } from \"./selectionState\";\nimport type { TreeAccessors, TreeDataGridProps } from \"./types\";\nimport { useTreeState } from \"./useTreeState\";\n\nconst EMPTY_SELECTED_IDS: ReadonlySet<string> = new Set();\nconst UNCHECKED_STATE: RowSelectionState = { checked: false, indeterminate: false };\n\nfunction renderCell<TRow>(\n column: ColumnDef<TRow>,\n row: TRow,\n editingCtx: EditingCellContext<TRow> | undefined,\n): ReactNode {\n return renderEditableCell(column, row, getColumnValue(column, row), editingCtx);\n}\n\ninterface TreeCellProps<TRow> {\n flatRow: FlatTreeRow<TRow>;\n column: ColumnDef<TRow>;\n indentSize: number;\n isLoading: boolean;\n error: string | undefined;\n onToggle: () => void;\n onRetry: () => void;\n editingCtx: EditingCellContext<TRow> | undefined;\n}\n\nfunction TreeCell<TRow>({\n flatRow,\n column,\n indentSize,\n isLoading,\n error,\n onToggle,\n onRetry,\n editingCtx,\n}: TreeCellProps<TRow>): ReactElement {\n return (\n <div className=\"flex items-center gap-1\" style={{ paddingLeft: flatRow.depth * indentSize }}>\n {flatRow.hasChildren ? (\n <button\n type=\"button\"\n onClick={(event) => {\n stopRowClick(event);\n onToggle();\n }}\n disabled={isLoading}\n aria-label={flatRow.isExpanded ? \"Collapse\" : \"Expand\"}\n aria-expanded={flatRow.isExpanded}\n className=\"flex h-4 w-4 shrink-0 items-center justify-center text-muted-foreground hover:text-foreground disabled:opacity-50\"\n >\n {isLoading ? (\n <Loader2 className=\"h-3.5 w-3.5 animate-spin\" aria-hidden=\"true\" />\n ) : flatRow.isExpanded ? (\n <ChevronDown className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n ) : (\n <ChevronRight className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n )}\n </button>\n ) : (\n <span className=\"h-4 w-4 shrink-0\" aria-hidden=\"true\" />\n )}\n {/* `min-w-0 flex-1` (added alongside editing support) lets this span\n actually shrink/grow within the flex row instead of relying on its\n content's natural size — needed for the default editors' own\n `w-full` inputs to size correctly next to the indentation/chevron;\n `truncate` still applies to (and only visibly affects) static text\n content, same as before. */}\n <span className=\"min-w-0 flex-1 truncate\">{renderCell(column, flatRow.row, editingCtx)}</span>\n {/* Rendered on the parent row itself rather than as a synthetic extra\n row, so the flattened row count (and the virtualizer's count) never\n has to account for error state — see useTreeState's module doc for\n why a failed fetch surfaces via errorIds instead of throwing. */}\n {flatRow.isExpanded && error ? (\n <span className=\"ml-1 flex shrink-0 items-center gap-1 text-xs text-destructive\">\n <AlertCircle className=\"h-3 w-3\" aria-hidden=\"true\" />\n Failed to load.\n <button type=\"button\" className=\"underline underline-offset-2\" onClick={onRetry}>\n Retry\n </button>\n </span>\n ) : null}\n </div>\n );\n}\n\n/**\n * A lazy-loading tree grid: generalizes a consuming app's contract tree list\n * (a from-scratch hand-rolled hierarchy table with no shared abstraction)\n * into a reusable component on top of the same `ColumnDef<TRow>` system\n * `<DataGrid>` uses. Deliberately does NOT support sorting or client-side\n * filtering — that original hierarchy table has neither (hierarchy order\n * comes from the backend; \"filtering\" there means asking the server for a\n * differently-shaped tree, which is an app-level concern, not something\n * this component can generalize).\n *\n * Row indentation + the expand/collapse chevron are prepended to whichever\n * column's cell matches `treeColumnId` (defaults to `columns[0]`); every\n * other column renders exactly like it would in `<DataGrid>`.\n */\nexport function TreeDataGrid<TRow>({\n columns,\n data,\n treeColumnId,\n getRowId,\n getChildren,\n hasChildren,\n onLoadChildren,\n initialExpandedLevel = 0,\n expanded: controlledExpanded,\n onExpandedChange,\n childrenMap: controlledChildrenMap,\n onChildrenMapChange,\n indentSize = 20,\n getRowProps,\n getRowTestId,\n rowActions,\n virtualizeThreshold = 100,\n estimatedRowHeight = 40,\n maxBodyHeight = 480,\n selectedIds: controlledSelectedIds,\n onSelectedIdsChange,\n getRowSelectionState,\n isRowSelectionDisabled,\n zebra = true,\n editing,\n}: TreeDataGridProps<TRow>): ReactElement {\n const accessors: TreeAccessors<TRow> = useMemo(\n () => ({ getRowId, getChildren, hasChildren }),\n [getRowId, getChildren, hasChildren],\n );\n\n // No `flexRender`-as-component-type layer standing between this component\n // and every render (unlike `<DataGrid>`, which is why IT needs a ref\n // indirection — see `useEditingState`'s own doc), so `editingState.ctx`\n // (the plain, freshly-computed-each-render value) is read directly below,\n // not through `editingState.ctxRef`.\n const editingState = useEditingState(editing, getRowId);\n\n const { expanded, childrenMap, loadingIds, errorIds, toggleExpand, retry, expandToLevel } = useTreeState({\n data,\n getRowId,\n getChildren,\n hasChildren,\n onLoadChildren,\n expanded: controlledExpanded,\n onExpandedChange,\n childrenMap: controlledChildrenMap,\n onChildrenMapChange,\n });\n\n // Runs exactly once, against the tree as it existed on first render — not\n // re-triggered by later `data`/`initialExpandedLevel` changes, matching\n // `useTreeState`'s \"no implicit reset\" stance (see its module doc): a\n // caller that wants a fresh auto-expand for genuinely different data\n // should remount via a `key` prop, not rely on this effect re-firing.\n const didAutoExpand = useRef(false);\n useEffect(() => {\n if (didAutoExpand.current) return;\n didAutoExpand.current = true;\n if (initialExpandedLevel > 0) void expandToLevel(initialExpandedLevel);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const flatRows = useMemo(\n () => flattenTree(data, accessors, expanded, childrenMap),\n [data, accessors, expanded, childrenMap],\n );\n\n const treeColId = treeColumnId ?? columns[0]?.id;\n const showRowActionsColumn = Boolean(rowActions?.length);\n const showSelectionColumn = controlledSelectedIds !== undefined;\n const totalColumnCount = columns.length + (showRowActionsColumn ? 1 : 0) + (showSelectionColumn ? 1 : 0);\n\n // Computed once (not per visible row) over the whole *loaded* tree, not\n // just `flatRows` — see `computeSelectionStates`'s own doc for why a\n // collapsed parent's indeterminate state still depends on children loaded\n // during an earlier expand. Skipped entirely when there's no selection\n // column to show.\n const selectionStates = useMemo(\n () =>\n showSelectionColumn\n ? computeSelectionStates(data, accessors, childrenMap, controlledSelectedIds ?? EMPTY_SELECTED_IDS)\n : undefined,\n [showSelectionColumn, data, accessors, childrenMap, controlledSelectedIds],\n );\n\n function resolveSelectionState(row: TRow, id: string): RowSelectionState {\n const override = getRowSelectionState?.(row);\n if (override) return { checked: override.checked, indeterminate: override.indeterminate ?? false };\n return selectionStates?.get(id) ?? UNCHECKED_STATE;\n }\n\n function toggleRowSelected(id: string): void {\n if (!controlledSelectedIds) return;\n const next = new Set(controlledSelectedIds);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n onSelectedIdsChange?.(next);\n }\n\n // \"Select all\" operates on the currently visible (flattened) rows, mirroring\n // <DataGrid>'s own \"select all on this page\" — and, like there, on raw\n // `selectedIds` membership specifically (not the derived indeterminate\n // view), since `checked` must always stay exactly `selectedIds.has(id)`.\n const selectableFlatRows = useMemo(\n () => flatRows.filter((flatRow) => !(isRowSelectionDisabled?.(flatRow.row) ?? false)),\n [flatRows, isRowSelectionDisabled],\n );\n const allVisibleSelected =\n showSelectionColumn &&\n selectableFlatRows.length > 0 &&\n selectableFlatRows.every((flatRow) => (controlledSelectedIds as ReadonlySet<string>).has(flatRow.id));\n\n function toggleAllVisibleSelected(): void {\n if (!controlledSelectedIds) return;\n const next = new Set(controlledSelectedIds);\n for (const flatRow of selectableFlatRows) {\n if (allVisibleSelected) next.delete(flatRow.id);\n else next.add(flatRow.id);\n }\n onSelectedIdsChange?.(next);\n }\n\n // `alignClassName(column)` only depends on the column, not the row, so\n // resolving it once per column here — instead of via `cn(\"p-2\",\n // alignClassName(column))` inside the per-row `<td>` loop below — turns an\n // O(rows x columns) `cn()`/twMerge call count into O(columns) per render.\n // `\"p-2\"` and `text-{align}` never conflict (distinct utility groups), so\n // a plain template string is exact, not an approximation.\n const bodyTdClassByColumn = useMemo(() => {\n const map = new Map<string, string>();\n for (const column of columns) map.set(column.id, `p-2 ${alignClassName(column)}`);\n return map;\n }, [columns]);\n\n const scrollRef = useRef<HTMLDivElement>(null);\n const shouldVirtualize = flatRows.length > virtualizeThreshold;\n const virtualizer = useVirtualizer({\n count: flatRows.length,\n getScrollElement: () => scrollRef.current,\n estimateSize: () => estimatedRowHeight,\n overscan: 10,\n enabled: shouldVirtualize,\n });\n\n function renderRow(\n flatRow: FlatTreeRow<TRow>,\n index: number,\n measureRef?: (el: Element | null) => void,\n ): ReactElement {\n const rowProps = getRowProps?.(flatRow.row, flatRow.depth, index);\n const rowTestId = getRowTestId?.(flatRow.row) ?? `tree-row-${flatRow.id}`;\n const isOddRow = zebra && index % 2 === 1;\n return (\n <tr\n key={flatRow.id}\n ref={measureRef}\n {...rowProps}\n // Required by `@tanstack/react-virtual`'s `measureElement` (wired\n // below via `measureRef`): it reads this attribute back off the\n // measured node to know which row it just measured. Without it,\n // `indexFromElement` falls back to -1 for every row (logging its own\n // \"Missing attribute name 'data-index={index}' on measured element\"\n // warning), which the virtualizer's `resizeItem` then discards\n // outright (`index < 0` bails before recording the size) — so a\n // row's real rendered height, e.g. this one growing well past\n // `estimatedRowHeight` once a caller renders an expanded editor\n // inside a cell, never overwrites the estimate. The virtualizer's\n // start/end offsets then drift from the real DOM layout as more\n // dynamically-sized rows accumulate, which can push a still-relevant\n // row out of the computed visible range — unmounting it (and\n // anything with local state/focus inside it) even though it's still\n // meant to be on screen.\n data-index={index}\n data-testid={rowTestId}\n // Same reasoning as <DataGrid>'s own row className: full `cn()`\n // conflict resolution is only needed when a caller-supplied\n // `getRowProps().className` might collide with the zebra stripe's\n // `bg-*` utility — skip straight to a plain string otherwise.\n className={\n rowProps?.className\n ? cn(\n \"border-b border-border divide-x divide-border\",\n rowProps.className as string,\n isOddRow && \"bg-foreground/5\",\n )\n : `border-b border-border divide-x divide-border${isOddRow ? \" bg-foreground/5\" : \"\"}`\n }\n >\n {showSelectionColumn && (\n <td className=\"p-1\" style={{ width: SELECTION_COLUMN_WIDTH }}>\n {(() => {\n const state = resolveSelectionState(flatRow.row, flatRow.id);\n return (\n <Checkbox\n aria-label={`Select row ${flatRow.id}`}\n checked={state.indeterminate ? \"indeterminate\" : state.checked}\n disabled={isRowSelectionDisabled?.(flatRow.row) ?? false}\n onCheckedChange={() => toggleRowSelected(flatRow.id)}\n onClick={stopRowClick}\n />\n );\n })()}\n </td>\n )}\n {columns.map((column) => (\n <td key={column.id} className={bodyTdClassByColumn.get(column.id)}>\n {column.id === treeColId ? (\n <TreeCell\n flatRow={flatRow}\n column={column}\n indentSize={indentSize}\n isLoading={loadingIds.has(flatRow.id)}\n error={errorIds.get(flatRow.id)}\n onToggle={() => toggleExpand(flatRow.row)}\n onRetry={() => retry(flatRow.row)}\n editingCtx={editingState.ctx}\n />\n ) : (\n renderCell(column, flatRow.row, editingState.ctx)\n )}\n </td>\n ))}\n {showRowActionsColumn && rowActions && (\n <td className=\"p-2\">\n <ActionsMenu\n items={rowActions}\n ctx={{ row: flatRow.row }}\n triggerLabel={`Row actions for ${flatRow.id}`}\n />\n </td>\n )}\n </tr>\n );\n }\n\n const virtualItems = shouldVirtualize ? virtualizer.getVirtualItems() : [];\n const paddingTop = virtualItems.length > 0 ? virtualItems[0]!.start : 0;\n const paddingBottom =\n virtualItems.length > 0 ? virtualizer.getTotalSize() - virtualItems[virtualItems.length - 1]!.end : 0;\n\n return (\n <div className=\"flex flex-col gap-2\" data-testid=\"tree-datagrid\">\n {editing && editingState.pendingEdits.size > 0 && (\n <EditingBar\n editing={editing}\n pendingRowCount={editingState.pendingEdits.size}\n editErrors={editingState.editErrors}\n onSave={() => void editingState.handleSaveEdits()}\n onDiscard={editingState.handleDiscardEdits}\n testIdPrefix=\"tree-datagrid\"\n />\n )}\n <div\n ref={scrollRef}\n className={shouldVirtualize ? \"overflow-y-auto\" : undefined}\n style={shouldVirtualize ? { maxHeight: maxBodyHeight } : undefined}\n >\n <table className=\"w-full border-collapse text-sm\">\n <thead>\n <tr className=\"divide-x divide-border\">\n {showSelectionColumn && (\n <th className=\"border-b border-border p-1\" style={{ width: SELECTION_COLUMN_WIDTH }}>\n <Checkbox\n aria-label=\"Select all visible rows\"\n checked={allVisibleSelected}\n onCheckedChange={toggleAllVisibleSelected}\n />\n </th>\n )}\n {columns.map((column) => (\n <th\n key={column.id}\n style={column.width ? { width: column.width } : undefined}\n className={`border-b border-border p-2 font-medium ${alignClassName(column)}`}\n >\n {column.header}\n </th>\n ))}\n {showRowActionsColumn && <th className=\"border-b border-border p-2\" aria-label=\"Row actions\" />}\n </tr>\n </thead>\n <tbody>\n {flatRows.length === 0 ? (\n <tr>\n <td colSpan={totalColumnCount} className=\"p-4 text-center text-muted-foreground\">\n No results.\n </td>\n </tr>\n ) : shouldVirtualize ? (\n <>\n {paddingTop > 0 && (\n <tr aria-hidden=\"true\">\n <td colSpan={totalColumnCount} style={{ padding: 0, border: \"none\", height: paddingTop }} />\n </tr>\n )}\n {virtualItems.map((virtualItem) =>\n renderRow(flatRows[virtualItem.index]!, virtualItem.index, (el) => virtualizer.measureElement(el)),\n )}\n {paddingBottom > 0 && (\n <tr aria-hidden=\"true\">\n <td\n colSpan={totalColumnCount}\n style={{ padding: 0, border: \"none\", height: paddingBottom }}\n />\n </tr>\n )}\n </>\n ) : (\n flatRows.map((flatRow, index) => renderRow(flatRow, index))\n )}\n </tbody>\n </table>\n </div>\n </div>\n );\n}\n","import type { TreeAccessors } from \"./types\";\n\nexport interface FlatTreeRow<TRow> {\n row: TRow;\n id: string;\n depth: number;\n hasChildren: boolean;\n isExpanded: boolean;\n}\n\n/**\n * Depth-first flatten of `roots` into the currently *visible* rows only —\n * a collapsed node's descendants never enter the output at all, matching\n * `ContractTreelist`'s `flattenContractTree` (the function this generalizes).\n *\n * `childrenOverride` (a node id -> loaded children map) takes priority over\n * `getChildren(row)` when both exist, since it reflects the most recently\n * fetched batch for a lazily-loaded node — `getChildren` may still return a\n * stale/partial eager batch for the same node.\n */\nexport function flattenTree<TRow>(\n roots: TRow[],\n accessors: TreeAccessors<TRow>,\n expanded: Record<string, boolean>,\n childrenOverride: ReadonlyMap<string, TRow[]>,\n): FlatTreeRow<TRow>[] {\n const { getRowId, getChildren } = accessors;\n const resolveHasChildren =\n accessors.hasChildren ?? ((row: TRow) => (getChildren(row)?.length ?? 0) > 0);\n const out: FlatTreeRow<TRow>[] = [];\n\n function visit(row: TRow, depth: number): void {\n const id = getRowId(row);\n const isExpanded = Boolean(expanded[id]);\n out.push({ row, id, depth, hasChildren: resolveHasChildren(row), isExpanded });\n if (!isExpanded) return;\n const children = childrenOverride.get(id) ?? getChildren(row) ?? [];\n for (const child of children) visit(child, depth + 1);\n }\n\n for (const root of roots) visit(root, 0);\n return out;\n}\n","import type { TreeAccessors } from \"./types\";\n\nexport interface RowSelectionState {\n checked: boolean;\n indeterminate: boolean;\n}\n\n/**\n * Bottom-up walk over the *whole loaded* tree (`roots` + `childrenOverride`),\n * not just the currently visible/expanded rows — a collapsed parent can\n * still be indeterminate based on children loaded during an earlier\n * expand-then-collapse (per `useTreeState`'s \"no implicit reset\" cache),\n * which `flattenTree`'s collapse-stopping walk deliberately never visits.\n *\n * `checked` is always exactly `selectedIds.has(id)` — never auto-derived,\n * so `selectedIds` stays the single source of truth for \"is this id\n * selected\" (see `TreeDataGridProps.getRowSelectionState`'s doc for why).\n * `indeterminate` is derived: true when the row itself isn't checked but at\n * least one loaded descendant is checked or itself indeterminate. An\n * unloaded/unexpanded subtree can't be inspected, so it reads as neither.\n */\nexport function computeSelectionStates<TRow>(\n roots: TRow[],\n accessors: TreeAccessors<TRow>,\n childrenOverride: ReadonlyMap<string, TRow[]>,\n selectedIds: ReadonlySet<string>,\n): Map<string, RowSelectionState> {\n const { getRowId, getChildren } = accessors;\n const out = new Map<string, RowSelectionState>();\n\n function visit(row: TRow): RowSelectionState {\n const id = getRowId(row);\n if (selectedIds.has(id)) {\n const state: RowSelectionState = { checked: true, indeterminate: false };\n out.set(id, state);\n return state;\n }\n const children = childrenOverride.get(id) ?? getChildren(row) ?? [];\n let anySelectedOrIndeterminate = false;\n for (const child of children) {\n const childState = visit(child);\n if (childState.checked || childState.indeterminate) anySelectedOrIndeterminate = true;\n }\n const state: RowSelectionState = { checked: false, indeterminate: anySelectedOrIndeterminate };\n out.set(id, state);\n return state;\n }\n\n for (const root of roots) visit(root);\n return out;\n}\n","import { useCallback, useMemo, useState } from \"react\";\nimport type { TreeAccessors } from \"./types\";\n\nexport interface TreeStateController<TRow> {\n expanded: Record<string, boolean>;\n childrenMap: ReadonlyMap<string, TRow[]>;\n loadingIds: ReadonlySet<string>;\n /** Node id -> the message from its most recent failed `onLoadChildren` call. */\n errorIds: ReadonlyMap<string, string>;\n toggleExpand: (row: TRow) => void;\n /** Re-attempts `onLoadChildren` for a node currently in `errorIds`. */\n retry: (row: TRow) => void;\n /** Expands every node from the roots down to (but not including) `level`, fetching lazy children along the way. */\n expandToLevel: (level: number) => Promise<void>;\n}\n\nexport interface UseTreeStateOptions<TRow> extends TreeAccessors<TRow> {\n data: TRow[];\n onLoadChildren?: (row: TRow) => Promise<TRow[]>;\n expanded?: Record<string, boolean>;\n onExpandedChange?: (expanded: Record<string, boolean>) => void;\n childrenMap?: ReadonlyMap<string, TRow[]>;\n onChildrenMapChange?: (childrenMap: ReadonlyMap<string, TRow[]>) => void;\n}\n\n/**\n * Owns everything `<TreeDataGrid>` needs beyond plain rendering: expand\n * state (controlled or internal, same pattern as `<ColumnSelector>`'s\n * `visibility`/`onVisibilityChange`), a per-node lazy-children cache, and\n * per-node loading/error tracking.\n *\n * Deliberately does NOT reset any of this when `data` changes identity —\n * unlike `ContractTreelist`, which resets `expanded`/`childrenMap` on every\n * filter change because filtering there reshapes the whole tree server-side.\n * This hook has no opinion on why `data` changed; a caller that wants a full\n * reset (e.g. switching to a different root entity entirely) should remount\n * `<TreeDataGrid>` via a `key` prop rather than rely on implicit magic here.\n */\nexport function useTreeState<TRow>({\n data,\n getRowId,\n getChildren,\n hasChildren,\n onLoadChildren,\n expanded: controlledExpanded,\n onExpandedChange,\n childrenMap: controlledChildrenMap,\n onChildrenMapChange,\n}: UseTreeStateOptions<TRow>): TreeStateController<TRow> {\n const [internalExpanded, setInternalExpanded] = useState<Record<string, boolean>>({});\n const expanded = controlledExpanded ?? internalExpanded;\n\n const setExpanded = useCallback(\n (updater: (prev: Record<string, boolean>) => Record<string, boolean>) => {\n if (controlledExpanded) {\n onExpandedChange?.(updater(controlledExpanded));\n } else {\n setInternalExpanded(updater);\n }\n },\n [controlledExpanded, onExpandedChange],\n );\n\n const [internalChildrenMap, setInternalChildrenMap] = useState<Map<string, TRow[]>>(new Map());\n const childrenMap = controlledChildrenMap ?? internalChildrenMap;\n\n const setChildrenMap = useCallback(\n (updater: (prev: ReadonlyMap<string, TRow[]>) => ReadonlyMap<string, TRow[]>) => {\n if (controlledChildrenMap) {\n onChildrenMapChange?.(updater(controlledChildrenMap));\n } else {\n setInternalChildrenMap((prev) => new Map(updater(prev)));\n }\n },\n [controlledChildrenMap, onChildrenMapChange],\n );\n\n const [loadingIds, setLoadingIds] = useState<Set<string>>(new Set());\n const [errorIds, setErrorIds] = useState<Map<string, string>>(new Map());\n\n const resolveHasChildren = useMemo(\n () => hasChildren ?? ((row: TRow) => (getChildren(row)?.length ?? 0) > 0),\n [hasChildren, getChildren],\n );\n\n const loadChildrenFor = useCallback(\n async (row: TRow): Promise<TRow[]> => {\n const id = getRowId(row);\n const cached = childrenMap.get(id);\n if (cached) return cached;\n\n const eager = getChildren(row);\n if (eager && eager.length > 0) return eager;\n if (!onLoadChildren) return eager ?? [];\n\n setLoadingIds((prev) => new Set(prev).add(id));\n setErrorIds((prev) => {\n if (!prev.has(id)) return prev;\n const next = new Map(prev);\n next.delete(id);\n return next;\n });\n try {\n const result = await onLoadChildren(row);\n setChildrenMap((prev) => new Map(prev).set(id, result));\n return result;\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Failed to load children.\";\n setErrorIds((prev) => new Map(prev).set(id, message));\n throw error;\n } finally {\n setLoadingIds((prev) => {\n const next = new Set(prev);\n next.delete(id);\n return next;\n });\n }\n },\n [childrenMap, getChildren, getRowId, onLoadChildren, setChildrenMap],\n );\n\n const toggleExpand = useCallback(\n (row: TRow) => {\n const id = getRowId(row);\n const willExpand = !expanded[id];\n setExpanded((prev) => ({ ...prev, [id]: willExpand }));\n // Fetch on the way open, never on the way closed — collapsing only\n // hides rows, it never drops the cache (see the module doc above).\n if (willExpand && resolveHasChildren(row)) {\n loadChildrenFor(row).catch(() => {\n // Deliberately swallowed: the failure is already surfaced via\n // `errorIds` (rendered inline with a retry affordance by\n // `<TreeDataGrid>`) — unlike `ContractTreelist`'s equivalent path\n // (`toggleExpand` -> `fetchChildrenFor` with no `.catch`), which\n // left an unhandled rejection and no visible error at all.\n });\n }\n },\n [expanded, getRowId, loadChildrenFor, resolveHasChildren, setExpanded],\n );\n\n const retry = useCallback(\n (row: TRow) => {\n loadChildrenFor(row).catch(() => {\n // Same reasoning as toggleExpand above.\n });\n },\n [loadChildrenFor],\n );\n\n const expandToLevel = useCallback(\n async (level: number): Promise<void> => {\n const nextExpanded: Record<string, boolean> = { ...expanded };\n\n async function visit(row: TRow, depth: number): Promise<void> {\n if (depth >= level || !resolveHasChildren(row)) return;\n nextExpanded[getRowId(row)] = true;\n let children: TRow[];\n try {\n children = await loadChildrenFor(row);\n } catch {\n // Leave this node expanded-but-empty; errorIds already has the\n // message and `<TreeDataGrid>` renders a retry affordance for it.\n return;\n }\n for (const child of children) await visit(child, depth + 1);\n }\n\n for (const root of data) await visit(root, 0);\n setExpanded(() => nextExpanded);\n },\n [data, expanded, getRowId, loadChildrenFor, resolveHasChildren, setExpanded],\n );\n\n return { expanded, childrenMap, loadingIds, errorIds, toggleExpand, retry, expandToLevel };\n}\n"],"mappings":"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","names":["forwardRef","jsx","forwardRef","jsx","forwardRef","jsx","forwardRef","jsx","jsx","jsxs","parseISO","jsx","jsx","jsxs","parseISO","FunnelIcon","Check","forwardRef","jsx","forwardRef","Check","forwardRef","jsx","forwardRef","jsx","jsxs","FunnelIcon","FunnelIcon","useState","parseISO","rangeOf","jsx","jsxs","useState","FunnelIcon","FunnelIcon","useEffect","useId","useRef","useState","jsx","jsxs","parseNumber","rangeOf","useState","useId","useRef","useEffect","FunnelIcon","FunnelIcon","useEffect","useMemo","useRef","useState","jsx","jsxs","format","useState","useRef","rangeOf","useEffect","useMemo","FunnelIcon","FunnelIcon","useId","jsx","jsxs","DEFAULT_OPERATOR","useId","FunnelIcon","jsx","jsx","jsx","jsxs","parseISO","jsx","jsxs","parseISO","jsx","jsxs","useEffect","useRef","useState","jsx","jsxs","useState","useRef","useEffect","jsx","jsxs","jsx","useEffect","useRef","useState","useState","useEffect","useRef","jsx","value","jsx","jsxs","useEffect","forwardRef","jsx","jsxs","forwardRef","jsx","jsxs","useEffect","useMemo","forwardRef","jsx","forwardRef","jsx","jsx","jsxs","useMemo","FunnelIcon","Fragment","useEffect","useMemo","useRef","useState","useCallback","useEffect","useMemo","useState","useEffect","useMemo","useRef","useState","useCallback","useEffect","useMemo","withoutColumn","Fragment","jsx","jsxs","useState","useMemo","useRef","useEffect","FunnelIcon","useVirtualizer","ChevronDown","ChevronRight","Loader2","useEffect","useMemo","useRef","state","useCallback","useMemo","useState","Fragment","jsx","jsxs","Loader2","ChevronDown","ChevronRight","useMemo","useRef","useEffect","useVirtualizer"]}
|