@bmsuisse/datagrid 0.34.1 → 0.35.1

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.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/edit/widget-types.ts","../src/edit/EditFieldError.tsx","../src/edit/BooleanEditor.tsx","../src/edit/DateEditor.tsx","../src/edit/EnumEditor.tsx","../src/edit/MultilineStringEditor.tsx","../src/components/ui/textarea.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/cell-editing/rangeUtils.ts","../src/cell-editing/useCellSelection.ts","../src/cell-editing/useCellEditingState.ts","../src/cell-editing/renderCellModeCell.tsx","../src/cell-editing/SelectionOverlay.tsx","../src/cell-editing/coerce.ts","../src/cell-editing/clipboard.ts","../src/cell-editing/fillHandle.ts","../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/cell-editing/renderAlwaysEditCell.tsx","../src/hooks/useGroupExpansion.ts","../src/hooks/useStickyGroupHeaderTop.ts","../src/hooks/useVisibleColumns.ts","../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\n// Swiss-locale numbers group thousands with an apostrophe (straight `'` or\n// the typographic `’` some editors/autocorrect substitute) and still use a\n// plain period for the decimal point — e.g. \"1'234'567.89\", or \"CHF\n// 1'234.50\"/\"1'234.50 Fr.\" for a currency cell copied from a Swiss-locale\n// Excel/Sheets. `Number(...)` can't parse either on its own. Deliberately\n// scoped to just this one locale's conventions (this is a Swiss company's\n// own grid) — \"1,234.56\"-style US/UK grouping or \"1.234,56\"-style German\n// grouping isn't attempted here.\nconst SWISS_CURRENCY_MARKER = /^(?:chf|s?fr\\.?)\\s+|\\s+(?:chf|s?fr\\.?)$/gi;\nconst SWISS_THOUSANDS_SEPARATOR = /(\\d)['’](\\d{3})/;\n\n/** Strips a Swiss franc marker and thousands-grouping apostrophes so the result is plain `Number()`-parseable — see `SWISS_CURRENCY_MARKER`'s own doc. Looped (not a global regex) because removing one grouping apostrophe can expose the next one to the left, e.g. \"1'234'567\" needs two passes. */\nfunction stripSwissNumberFormatting(raw: string): string {\n const withoutCurrency = raw.replace(SWISS_CURRENCY_MARKER, \"\");\n let stripped = withoutCurrency;\n while (SWISS_THOUSANDS_SEPARATOR.test(stripped)) {\n stripped = stripped.replace(SWISS_THOUSANDS_SEPARATOR, \"$1$2\");\n }\n return stripped;\n}\n\n/**\n * Parses an arbitrary raw cell value into a `number`, or `null` if it isn't\n * one. Exported (unlike before) for the same reason `toDate` above is:\n * `cell-editing/coerce.ts` needs the exact same numeric parsing this module\n * already uses for display, to back paste/fill coercion without a second\n * copy that could drift from it.\n */\nexport function 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 trimmed = value.trim();\n const parsed = Number(trimmed);\n if (!Number.isNaN(parsed)) return parsed;\n // Only reached for a string `Number()` itself can't parse — a plain\n // \"1234.5\" (or any other already-valid numeric string) never touches\n // Swiss-specific stripping at all.\n const swiss = Number(stripSwissNumberFormatting(trimmed));\n return Number.isNaN(swiss) ? null : swiss;\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 * Overrides the plain-text form of this column's value used when copying a\n * `cellEditing`-mode selection to the clipboard. Defaults to the same\n * `defaultFormat` display text every other rendering path already uses —\n * only needed for a column with a custom `cell` whose displayed content\n * isn't a faithful plain-text rendering of the underlying value (e.g. a\n * badge/icon), the same situation `renderFilter`/`renderEditCell` exist to\n * override for their own concerns.\n */\n formatForClipboard?: (value: unknown, row: TRow) => string;\n /**\n * Computes this column's aggregate cell content from a set of rows — the\n * same function feeds both `DataGridProps`/`TreeDataGridProps`\n * `groupBy`'s optional per-group summary row (called once per bucket, with\n * that bucket's own rows) and `showTotals`'s grand-total row (called once,\n * with every currently-rendered row). No calculation happens by default:\n * omit this to leave the column's cell blank in both rows. There's\n * deliberately no separate \"group subtotal\" vs. \"grand total\" variant —\n * both are just \"reduce this set of rows to a value for this column\", so\n * one function covers both.\n *\n * `showTotals`'s grand-total row calls this with EVERY currently-rendered\n * row, which can be an empty array (e.g. a filter narrows the current page\n * to zero rows) — a bare `rows.reduce((a, b) => ...)` with no seed throws\n * on an empty array, so seed your reducer (`rows.reduce((a, b) => ..., 0)`)\n * rather than assuming at least one row. A per-group summary row never\n * has this problem: a bucket only exists because at least one row produced\n * its key.\n */\n summary?: (rows: TRow[]) => ReactNode;\n}\n\nexport type StringColumn<TRow> = BaseColumn<TRow> & {\n type: \"string\";\n /** Opt-in: defaults to false. Selects `MultilineStringEditor` (an auto-growing `<textarea>`) instead of `StringEditor`'s single-line `<Input>` for this column's default edit widget — same variant-flag idiom as `NumberColumn.currency`, not a separate `type`. Has no effect on static (non-editing) display, which stays whatever `column.cell`/`defaultFormat` already renders. */\n multiline?: boolean;\n};\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 { useEffect, useId, useState } 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 // Tracked as local state, not derived from `value?.operator` on every render: `emit`\n // below intentionally clears the filter entirely (`onChange(undefined)`) whenever\n // `nextText` is empty, since an operator with no value isn't a filter yet -- but that\n // means picking an operator *before* typing anything (a natural \"choose how to\n // search, then type\" flow) would otherwise have nowhere to persist the choice: the\n // very next keystroke would derive `operator` from `value?.operator`, which is still\n // `undefined`, silently reverting to \"Contains\". Local state survives across that gap;\n // the effect below only resyncs it when the filter changes from *outside* this widget\n // (cleared via \"reset filters\", restored from a saved filter, column filters reset).\n const [operator, setOperator] = useState<FilterOperator>(value?.operator ?? DEFAULT_OPERATOR);\n const text = typeof value?.value === \"string\" ? value.value : \"\";\n const inputId = useId();\n const isFiltered = text !== \"\";\n\n useEffect(() => {\n setOperator(value?.operator ?? DEFAULT_OPERATOR);\n }, [value?.operator]);\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) return;\n setOperator(nextOperator);\n 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 open,\n onOpenChange,\n}: EditWidgetProps<EnumColumn<TRow>> & {\n /** Controls the dropdown's own open state — omit to let `<Select>` manage it itself (the default, used everywhere except `AlwaysEditCell`; see that file's own doc for why it needs control here). */\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n}): ReactElement {\n const errorId = editErrorId(rowId, column.id);\n return (\n <div>\n <Select\n value={value === null || value === undefined ? \"\" : String(value)}\n onValueChange={onChange}\n open={open}\n onOpenChange={onOpenChange}\n >\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 { useLayoutEffect, useRef } from \"react\";\nimport type { StringColumn } from \"../column/types\";\nimport { Textarea } from \"../components/ui/textarea\";\nimport { EditFieldError } from \"./EditFieldError\";\nimport { editErrorId, type EditWidgetProps } from \"./widget-types\";\n\n/**\n * Default inline editor for a `type: \"string\"` column with `multiline: true`:\n * an auto-growing `<textarea>` in place of `StringEditor`'s single-line\n * `<Input>`. Dispatched by `registry.tsx`'s existing `case \"string\":` branch\n * — a rendering choice, not a new column `type` — so it works identically\n * under both `DataGridProps.editing` (row-batch) and `.cellEditing`\n * (immediate-apply) modes with zero extra wiring in either.\n */\nexport function MultilineStringEditor<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 const textareaRef = useRef<HTMLTextAreaElement>(null);\n // Captures the cursor position right after each native edit (in the\n // `onChange` handler below, before React re-renders) so it can be\n // restored in the layout effect afterward — this element can otherwise\n // lose focus and reset its caret to 0 on a re-render that changes OTHER\n // props alongside `value` (e.g. `aria-invalid`/`aria-describedby`\n // toggling because a `column.validateEdit` result flipped on the very\n // keystroke that made the field valid) even though the value change\n // itself gave no reason to move it. Restoring proactively — not only when\n // the element happens to still be focused — is what actually fixes this:\n // by the time this effect runs, focus has typically already moved away.\n const selectionRef = useRef<{ start: number; end: number } | null>(null);\n const text = value === null || value === undefined ? \"\" : String(value);\n\n // Grows the textarea to fit its content instead of showing an internal\n // scrollbar for a few extra lines — resetting to \"auto\" first (rather than\n // reading `scrollHeight` against whatever height was already set) is what\n // lets this shrink back down after deleting lines, not just grow. Runs as\n // a layout effect (not a plain effect) so the refocus/selection-restore\n // below lands before the browser paints, avoiding a visible caret jump.\n useLayoutEffect(() => {\n const el = textareaRef.current;\n if (!el) return;\n el.style.height = \"auto\";\n el.style.height = `${el.scrollHeight}px`;\n if (selectionRef.current) {\n if (document.activeElement !== el) el.focus();\n el.setSelectionRange(selectionRef.current.start, selectionRef.current.end);\n }\n }, [text]);\n\n return (\n <div>\n <Textarea\n ref={textareaRef}\n className=\"resize-none overflow-hidden\"\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 selectionRef.current = { start: event.target.selectionStart ?? 0, end: event.target.selectionEnd ?? 0 };\n onChange(event.target.value);\n }}\n />\n {error && <EditFieldError id={errorId} message={error} />}\n </div>\n );\n}\n","import type { TextareaHTMLAttributes } from \"react\";\nimport { forwardRef } from \"react\";\nimport { cn } from \"../../lib/utils\";\n\nexport type TextareaProps = TextareaHTMLAttributes<HTMLTextAreaElement>;\n\nexport const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(({ className, ...props }, ref) => {\n return (\n <textarea\n className={cn(\n \"flex min-h-16 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});\nTextarea.displayName = \"Textarea\";\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 { MultilineStringEditor } from \"./MultilineStringEditor\";\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 column.multiline ? (\n <MultilineStringEditor column={column} rowId={rowId} value={value} onChange={onChange} error={error} autoFocus={autoFocus} />\n ) : (\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 type { CellAddress, CellRange } from \"./types\";\n\n/** Maps each id to its position in `ids` — built once per (rowIds|columnIds) identity change, then reused for every lookup during a drag, rather than an O(n) `indexOf` per cell per mousemove. */\nexport function buildIndexMap(ids: string[]): Map<string, number> {\n const map = new Map<string, number>();\n ids.forEach((id, index) => map.set(id, index));\n return map;\n}\n\n/** A `CellRange`'s two corners resolved to ordinal row/column indices, with `start <= end` on both axes regardless of which corner `anchor`/`focus` was. */\nexport interface NormalizedRange {\n rowStart: number;\n rowEnd: number;\n colStart: number;\n colEnd: number;\n}\n\n/**\n * Resolves a `CellRange` to ordinal indices via `rowIndex`/`columnIndex`\n * (from `buildIndexMap`), or `undefined` if either endpoint's row or column\n * id no longer exists — e.g. the row was removed from `data` since the\n * selection was made.\n */\nexport function normalizeRange(\n range: CellRange,\n rowIndex: Map<string, number>,\n columnIndex: Map<string, number>,\n): NormalizedRange | undefined {\n const anchorRow = rowIndex.get(range.anchor.rowId);\n const focusRow = rowIndex.get(range.focus.rowId);\n const anchorCol = columnIndex.get(range.anchor.columnId);\n const focusCol = columnIndex.get(range.focus.columnId);\n if (anchorRow === undefined || focusRow === undefined || anchorCol === undefined || focusCol === undefined) {\n return undefined;\n }\n return {\n rowStart: Math.min(anchorRow, focusRow),\n rowEnd: Math.max(anchorRow, focusRow),\n colStart: Math.min(anchorCol, focusCol),\n colEnd: Math.max(anchorCol, focusCol),\n };\n}\n\n/** True if `cell` falls inside `range` (inclusive on all sides). */\nexport function isCellInRange(\n cell: CellAddress,\n range: CellRange,\n rowIndex: Map<string, number>,\n columnIndex: Map<string, number>,\n): boolean {\n const normalized = normalizeRange(range, rowIndex, columnIndex);\n if (!normalized) return false;\n const row = rowIndex.get(cell.rowId);\n const col = columnIndex.get(cell.columnId);\n if (row === undefined || col === undefined) return false;\n return row >= normalized.rowStart && row <= normalized.rowEnd && col >= normalized.colStart && col <= normalized.colEnd;\n}\n\n/** Every cell address inside `range`, in row-major order (top-to-bottom, left-to-right), skipping the row-major walk entirely once `normalizeRange` can't resolve either endpoint. */\nexport function cellsInRange(\n range: CellRange,\n rowIds: string[],\n columnIds: string[],\n rowIndex: Map<string, number>,\n columnIndex: Map<string, number>,\n): CellAddress[] {\n const normalized = normalizeRange(range, rowIndex, columnIndex);\n if (!normalized) return [];\n const cells: CellAddress[] = [];\n for (let r = normalized.rowStart; r <= normalized.rowEnd; r++) {\n const rowId = rowIds[r];\n if (rowId === undefined) continue;\n for (let c = normalized.colStart; c <= normalized.colEnd; c++) {\n const columnId = columnIds[c];\n if (columnId === undefined) continue;\n cells.push({ rowId, columnId });\n }\n }\n return cells;\n}\n\n/**\n * Extends a fill-handle drag's `base` range (the source selection, already\n * normalized) toward `target`, locked to whichever single axis — row or\n * column — has the larger overshoot past `base`'s own edge on that axis.\n * Matches Excel/Sheets' own autofill behavior: dragging the fill handle\n * never extends diagonally, only straight down/up/left/right from the\n * source range. Returns `base` unchanged if `target` is inside or on its\n * border (no overshoot on either axis).\n */\nexport function extendRangeForFill(base: NormalizedRange, target: { row: number; col: number }): NormalizedRange {\n const rowOvershoot = Math.max(0, base.rowStart - target.row, target.row - base.rowEnd);\n const colOvershoot = Math.max(0, base.colStart - target.col, target.col - base.colEnd);\n if (rowOvershoot === 0 && colOvershoot === 0) return base;\n if (rowOvershoot >= colOvershoot) {\n return {\n rowStart: Math.min(base.rowStart, target.row),\n rowEnd: Math.max(base.rowEnd, target.row),\n colStart: base.colStart,\n colEnd: base.colEnd,\n };\n }\n return {\n rowStart: base.rowStart,\n rowEnd: base.rowEnd,\n colStart: Math.min(base.colStart, target.col),\n colEnd: Math.max(base.colEnd, target.col),\n };\n}\n\n/**\n * Combines `normalizeRange` + `extendRangeForFill`, converting the result\n * back to row-id/column-id addressing — the fill-handle drag's per-frame\n * preview and its final commit both need exactly this, so it's factored out\n * once rather than duplicated between `useCellSelection`'s live preview memo\n * and its drag-end commit. Returns `undefined` if `base`, `target`, or any\n * resolved endpoint's id no longer exists.\n */\nexport function extendRangeForFillToCellRange(\n base: CellRange,\n target: CellAddress,\n rowIds: string[],\n columnIds: string[],\n rowIndex: Map<string, number>,\n columnIndex: Map<string, number>,\n): CellRange | undefined {\n const normalizedBase = normalizeRange(base, rowIndex, columnIndex);\n if (!normalizedBase) return undefined;\n const targetRow = rowIndex.get(target.rowId);\n const targetCol = columnIndex.get(target.columnId);\n if (targetRow === undefined || targetCol === undefined) return undefined;\n const extended = extendRangeForFill(normalizedBase, { row: targetRow, col: targetCol });\n const startRowId = rowIds[extended.rowStart];\n const endRowId = rowIds[extended.rowEnd];\n const startColumnId = columnIds[extended.colStart];\n const endColumnId = columnIds[extended.colEnd];\n if (startRowId === undefined || endRowId === undefined || startColumnId === undefined || endColumnId === undefined) {\n return undefined;\n }\n return { anchor: { rowId: startRowId, columnId: startColumnId }, focus: { rowId: endRowId, columnId: endColumnId } };\n}\n","import { useCallback, useMemo, useRef, useState } from \"react\";\nimport { buildIndexMap, extendRangeForFillToCellRange } from \"./rangeUtils\";\nimport type { CellAddress, CellRange } from \"./types\";\n\nexport type NavigationDirection = \"up\" | \"down\" | \"left\" | \"right\";\n\nexport interface CellSelectionOptions {\n /** Every row id, in on-screen order — the FULL logical row list, not just currently-mounted virtual items, so navigation and range math work correctly regardless of virtualization. */\n rowIds: string[];\n /** Every editable-surface column id, in on-screen order. */\n columnIds: string[];\n /** Called when navigation lands on a row id that (per the caller) may not currently be mounted — e.g. to `virtualizer.scrollToIndex` it into view. Not called for a move that stays on the same row (left/right). */\n onNavigateToRow?: (rowId: string) => void;\n}\n\nexport interface CellSelectionController {\n /** The committed selection (mouseup already happened, or a keyboard move), or `undefined` before any cell has ever been selected. */\n selection: CellRange | undefined;\n /** True while a mouse-drag range-select is in progress (between `startSelection` and `endDrag`). */\n isDragging: boolean;\n /** `selection` extended live to the drag's current cell while `isDragging`; otherwise identical to `selection`. This is the range a renderer should actually paint. */\n effectiveRange: CellRange | undefined;\n /** Mousedown on `cell`: starts a fresh single-cell selection, or (`extend: true`, e.g. shift+mousedown) extends the existing selection's focus corner to `cell` while keeping its anchor. Either way, also opens a drag (see `endDrag`) — a plain click-no-move sequence still round-trips through `startSelection`/`endDrag` and ends up exactly the same as if `updateDrag` were never called. */\n startSelection: (cell: CellAddress, options?: { extend?: boolean }) => void;\n /** Mousemove while dragging: updates the live drag-focus cell. Callers should coalesce rapid calls themselves (e.g. one `requestAnimationFrame`-scheduled flush per animation frame) — this hook applies whatever it's given without its own throttling. No-op if not currently dragging. */\n updateDrag: (cell: CellAddress) => void;\n /** Mouseup: commits the in-progress drag's live focus cell as the real selection. No-op if not currently dragging. */\n endDrag: () => void;\n /** Keyboard navigation: moves the focus corner by one row/column in `direction`, clamped to grid bounds. Collapses the selection to the new single cell unless `extend` (shift+arrow), which instead moves only the focus corner, keeping the existing anchor. */\n moveSelection: (direction: NavigationDirection, options?: { extend?: boolean }) => void;\n /** Directly replaces the selection, e.g. to leave a specific cell selected after a paste/fill commits. */\n setSelection: (range: CellRange) => void;\n clearSelection: () => void;\n\n /** True while a fill-handle drag is in progress (between `startFillDrag` and `endFillDrag`). Mutually exclusive with `isDragging` — starting one doesn't check for the other, but a caller should never let both begin at once (the fill handle and the rest of a cell are different mousedown targets). */\n isFillDragging: boolean;\n /** The selection extended toward the fill-drag's current target cell, axis-locked (see `extendRangeForFill`'s own doc) — the range a renderer should paint as the live fill preview. `undefined` when not fill-dragging, or before the first `updateFillDrag` call of a given drag. */\n fillPreviewRange: CellRange | undefined;\n /** Starts a fill-drag from the current `selection` (mousedown on the fill handle). No-op if there's no selection yet. */\n startFillDrag: () => void;\n /** Updates the fill-drag's live target cell (mousemove while fill-dragging). Callers should coalesce rapid calls themselves, same as `updateDrag`. No-op if not currently fill-dragging. */\n updateFillDrag: (cell: CellAddress) => void;\n /**\n * Commits the fill-drag (mouseup): sets the extended range as the new\n * `selection` and returns both the original source range and the final\n * extended one, so the caller can compute the actual fill values (which\n * source cell each newly-covered cell copies from) — that data-dependent\n * computation doesn't belong in this hook, which only knows row/column\n * ids, not row data. Returns `undefined` if not fill-dragging, there was\n * no base selection, or no target cell was ever reported (a drag that\n * never moved) — in the last case, nothing to commit, current selection\n * unchanged.\n */\n endFillDrag: () => { sourceRange: CellRange; finalRange: CellRange } | undefined;\n}\n\n/**\n * Owns the rectangular range-selection cursor for `cellEditing` mode: the\n * committed selection, an in-progress mouse-drag's live extent, and\n * keyboard navigation — all keyed by row-id/column-id (via `rowIds`/\n * `columnIds`'s ordinal position), never by DOM node or virtualized index,\n * so a selection is meaningful even for a row that isn't currently mounted.\n */\nexport function useCellSelection({ rowIds, columnIds, onNavigateToRow }: CellSelectionOptions): CellSelectionController {\n const [selection, setSelectionState] = useState<CellRange | undefined>(undefined);\n const [isDragging, setIsDragging] = useState(false);\n const [dragFocus, setDragFocus] = useState<CellAddress | undefined>(undefined);\n // Mirrors `isDragging`/`dragFocus` synchronously for `updateDrag`/`endDrag`'s\n // own logic — those are plain callbacks (not effects), so they need a value\n // that's already correct on the very next call after `startSelection`/\n // `updateDrag`, not one that only catches up after React's next render.\n const draggingRef = useRef(false);\n const dragFocusRef = useRef<CellAddress | undefined>(undefined);\n\n const rowIndex = useMemo(() => buildIndexMap(rowIds), [rowIds]);\n const columnIndex = useMemo(() => buildIndexMap(columnIds), [columnIds]);\n\n const startSelection = useCallback((cell: CellAddress, options?: { extend?: boolean }) => {\n setSelectionState((prev) => (options?.extend && prev ? { anchor: prev.anchor, focus: cell } : { anchor: cell, focus: cell }));\n draggingRef.current = true;\n setIsDragging(true);\n dragFocusRef.current = undefined;\n setDragFocus(undefined);\n }, []);\n\n const updateDrag = useCallback((cell: CellAddress) => {\n if (!draggingRef.current) return;\n dragFocusRef.current = cell;\n setDragFocus(cell);\n }, []);\n\n const endDrag = useCallback(() => {\n if (!draggingRef.current) return;\n draggingRef.current = false;\n setIsDragging(false);\n const finalFocus = dragFocusRef.current;\n dragFocusRef.current = undefined;\n setDragFocus(undefined);\n if (finalFocus) {\n setSelectionState((prev) => (prev ? { anchor: prev.anchor, focus: finalFocus } : prev));\n }\n }, []);\n\n const moveSelection = useCallback(\n (direction: NavigationDirection, options?: { extend?: boolean }) => {\n // Reads `selection` directly (a dependency below) rather than via\n // `setSelectionState`'s own updater-callback form — that updater runs\n // as part of React's commit bookkeeping and can run more than once for\n // the same update (e.g. Strict Mode's intentional double-invocation),\n // which `onNavigateToRow`'s side effect (`virtualizer.scrollToIndex`)\n // must not do.\n const from = selection?.focus ?? { rowId: rowIds[0] ?? \"\", columnId: columnIds[0] ?? \"\" };\n const rowIdx = rowIndex.get(from.rowId);\n const colIdx = columnIndex.get(from.columnId);\n if (rowIdx === undefined || colIdx === undefined || rowIds.length === 0 || columnIds.length === 0) return;\n let nextRowIdx = rowIdx;\n let nextColIdx = colIdx;\n if (direction === \"up\") nextRowIdx = Math.max(0, rowIdx - 1);\n else if (direction === \"down\") nextRowIdx = Math.min(rowIds.length - 1, rowIdx + 1);\n else if (direction === \"left\") nextColIdx = Math.max(0, colIdx - 1);\n else nextColIdx = Math.min(columnIds.length - 1, colIdx + 1);\n const nextRowId = rowIds[nextRowIdx]!;\n const nextColumnId = columnIds[nextColIdx]!;\n if (nextRowId !== from.rowId) onNavigateToRow?.(nextRowId);\n const nextCell: CellAddress = { rowId: nextRowId, columnId: nextColumnId };\n const anchor = options?.extend ? (selection?.anchor ?? nextCell) : nextCell;\n setSelectionState({ anchor, focus: nextCell });\n },\n [selection, rowIds, columnIds, rowIndex, columnIndex, onNavigateToRow],\n );\n\n const setSelection = useCallback((range: CellRange) => {\n setSelectionState(range);\n }, []);\n\n const clearSelection = useCallback(() => {\n draggingRef.current = false;\n setIsDragging(false);\n setDragFocus(undefined);\n setSelectionState(undefined);\n }, []);\n\n const effectiveRange = useMemo<CellRange | undefined>(() => {\n if (!selection) return undefined;\n if (isDragging && dragFocus) return { anchor: selection.anchor, focus: dragFocus };\n return selection;\n }, [selection, isDragging, dragFocus]);\n\n const [isFillDragging, setIsFillDragging] = useState(false);\n const [fillTarget, setFillTarget] = useState<CellAddress | undefined>(undefined);\n // Same synchronous-read rationale as `draggingRef`/`dragFocusRef` above.\n const fillDraggingRef = useRef(false);\n const fillTargetRef = useRef<CellAddress | undefined>(undefined);\n const fillBaseRef = useRef<CellRange | undefined>(undefined);\n\n const startFillDrag = useCallback(() => {\n if (!selection) return;\n fillBaseRef.current = selection;\n fillDraggingRef.current = true;\n setIsFillDragging(true);\n }, [selection]);\n\n const updateFillDrag = useCallback((cell: CellAddress) => {\n if (!fillDraggingRef.current) return;\n fillTargetRef.current = cell;\n setFillTarget(cell);\n }, []);\n\n const endFillDrag = useCallback((): { sourceRange: CellRange; finalRange: CellRange } | undefined => {\n if (!fillDraggingRef.current) return undefined;\n fillDraggingRef.current = false;\n setIsFillDragging(false);\n const sourceRange = fillBaseRef.current;\n const target = fillTargetRef.current;\n fillBaseRef.current = undefined;\n fillTargetRef.current = undefined;\n setFillTarget(undefined);\n if (!sourceRange || !target) return undefined;\n const finalRange = extendRangeForFillToCellRange(sourceRange, target, rowIds, columnIds, rowIndex, columnIndex);\n if (!finalRange) return undefined;\n setSelectionState(finalRange);\n return { sourceRange, finalRange };\n }, [rowIds, columnIds, rowIndex, columnIndex]);\n\n const fillPreviewRange = useMemo<CellRange | undefined>(() => {\n if (!isFillDragging || !selection || !fillTarget) return undefined;\n return extendRangeForFillToCellRange(selection, fillTarget, rowIds, columnIds, rowIndex, columnIndex);\n }, [isFillDragging, selection, fillTarget, rowIds, columnIds, rowIndex, columnIndex]);\n\n return {\n selection,\n isDragging,\n effectiveRange,\n startSelection,\n updateDrag,\n endDrag,\n moveSelection,\n setSelection,\n clearSelection,\n isFillDragging,\n fillPreviewRange,\n startFillDrag,\n updateFillDrag,\n endFillDrag,\n };\n}\n","import { useRef, useState } from \"react\";\nimport type { CellAddress, CellChange, CellEditingOptions } from \"./types\";\n\nfunction cellKey(rowId: string, columnId: string): string {\n return `${rowId}:${columnId}`;\n}\n\n/**\n * One cell's locally-echoed value after a commit, kept only until the row's\n * own real data (`rawValue` at render time) stops matching `previousValue` —\n * see `resolveValue`'s own doc for why that's the right invalidation\n * condition for a fire-and-forget, no-rollback model.\n */\ninterface CellOverride {\n value: unknown;\n previousValue: unknown;\n}\n\n/**\n * Everything `renderCellModeCell` needs to render one cell under\n * `cellEditing` mode. Bundled into one object handed through a `useRef` for\n * the identical reason `EditingCellContext` is (see that type's own doc):\n * `<DataGrid>`'s `tanstackColumns` memo must not depend on anything that\n * changes per-keystroke, or typing into one cell would remount the whole\n * table body.\n */\nexport interface CellEditingCellContext<TRow> {\n /** Mirrors `CellEditingOptions.alwaysEdit` — every editable cell renders `AlwaysEditCell` instead of going through `editingCell`/`onBeginEdit` at all when this is set. */\n alwaysEdit: boolean;\n /** Mirrors `CellEditingOptions.disabled`. Click-to-edit mode blocks interaction by simply never opening an editor while this is set (see `onBeginEdit`'s own gate) — `AlwaysEditCell`'s editors are always open regardless, so it reads this directly to disable its rendered form control instead. */\n disabled: boolean;\n /** The one cell currently showing its editor, or `undefined` — at most one at a time, the same convention `EditingCellContext.activeRowId` uses at row granularity. Meaningless (always `undefined`) under `alwaysEdit`, where every editable cell already has its own editor. */\n editingCell: CellAddress | undefined;\n /** Replaces (doesn't append to) the editor's starting value when editing began by typing a printable character directly (Excel's \"just start typing\" gesture) rather than double-click/Enter/F2, which start from the cell's current value. `undefined` means \"start from the current value.\" */\n initialText: string | undefined;\n /** Whether at least one `onChangeDraft` has fired this edit session — distinguishes \"no keystrokes yet, show the starting value\" from \"the user explicitly typed something,\" including an empty string. */\n hasDraft: boolean;\n /** The in-progress typed value for `editingCell`, meaningful only when `hasDraft` is true. */\n draftValue: unknown;\n getRowId: (row: TRow) => string;\n getError: (rowId: string, columnId: string) => string | undefined;\n /**\n * The value to actually display for one cell: `rawValue` (the row's real\n * data) unless a still-\"fresh\" local override exists. An override is\n * fresh only while `rawValue` still equals what it was at the moment of\n * the edit (`previousValue`) — the instant the row's real data changes to\n * ANYTHING else (the consumer's `onCellsChange` handler updated its own\n * `data` to reflect the accepted edit, or a refetch landed with something\n * different entirely, e.g. server-side normalization), real data is\n * trusted again instead of the stale local echo. This is what lets a\n * fire-and-forget, no-rollback model (see `CellEditingOptions.onCellsChange`'s\n * own doc) still self-correct if the consumer's eventual real data\n * diverges from what was optimistically shown.\n */\n resolveValue: (rowId: string, columnId: string, rawValue: unknown) => unknown;\n /** Starts editing `cell`. */\n onBeginEdit: (cell: CellAddress, initialText?: string) => void;\n /** Reverts without committing — Escape, or a blur while the current draft is invalid. */\n onCancelEdit: () => void;\n /** Records a keystroke/selection's new value for the cell currently being edited — for an \"atomic\" editor (enum/boolean/date, where a single selection IS the complete gesture) the caller follows this immediately with `onCommitEdit`; for a free-text editor (string/number) this only buffers, and the caller commits separately on blur/Enter/Tab. */\n onChangeDraft: (value: unknown) => void;\n /** Commits the current draft (or, if `hasDraft` is false, effectively a no-op close) for the cell currently being edited, as one `CellChange`, and closes its editor. `previousValue` is the row's real value before this edit, for the resulting `CellChange.previousValue`. */\n onCommitEdit: (row: TRow, previousValue: unknown) => void;\n /**\n * Commits an explicit `value` directly, bypassing the draft state\n * entirely — for an \"atomic\" editor (enum/boolean/date) whose `onChange`\n * fires once with the complete new value and wants to commit in that same\n * call. `onCommitEdit` above can't be reused for this: it reads `hasDraft`/\n * `draftValue` from React state, which a same-tick `onChangeDraft` call\n * immediately before it would NOT yet reflect (state updates don't apply\n * until the next render, even within the same event handler).\n */\n onCommitValue: (row: TRow, previousValue: unknown, value: unknown) => void;\n onSetError: (rowId: string, columnId: string, message: string | undefined) => void;\n /** Commits one independently-managed cell's change directly — for `AlwaysEditCell`, which owns its own draft state locally rather than through `editingCell`/`onChangeDraft`/`onCommitEdit`'s single shared slot (every editable cell needs its own simultaneous editor under `alwaysEdit`, not just one at a time). */\n applyChange: (change: CellChange<TRow>) => void;\n /**\n * Reads (and clears) whether `onCommitEdit`/`onCommitValue` committed\n * synchronously during the current call stack. `<DataGrid>`'s own keydown\n * handler needs this: `renderCellModeCell`'s Enter/Tab commit deliberately\n * doesn't stop the event from bubbling, so that handler can advance the\n * selection afterward — but `editingCell` itself is React state, which\n * doesn't reflect `closeEditor()`'s update until the next render, so\n * reading it directly during that same bubbled dispatch always still\n * shows the row that JUST closed. This ref-backed flag is the one piece of\n * \"did a commit just happen in this exact gesture\" state that's readable\n * synchronously, in the same event, immediately after the commit.\n */\n consumeJustCommitted: () => boolean;\n}\n\nexport interface CellEditingState<TRow> {\n /** The live context, read directly by a render path with no remount-avoidance concerns of its own. */\n ctx: CellEditingCellContext<TRow> | undefined;\n /** Same context, mirrored into a ref every render — see `CellEditingCellContext`'s own doc for why `<DataGrid>`'s per-cell closures must read through this instead of closing over `ctx` directly. */\n ctxRef: { current: CellEditingCellContext<TRow> | undefined };\n /** Commits a whole gesture's changes at once (paste, fill-drag) — no `editingCell` involved, unlike `onCommitEdit` above. */\n applyChanges: (changes: CellChange<TRow>[]) => void;\n}\n\n/**\n * Owns the \"true spreadsheet\" per-cell edit state: which single cell (if\n * any) is showing its editor, that cell's in-progress typed draft, per-cell\n * validation errors, and the local-override echo every committed change\n * leaves behind until the consumer's own real data catches up. Every\n * mutation — a typed commit, a future paste, a future fill-drag — funnels\n * through `applyChanges`, so a later undo/redo can be built on top of that\n * one function without a rearchitecture.\n */\nexport function useCellEditingState<TRow>(\n cellEditing: CellEditingOptions<TRow> | undefined,\n getRowId: (row: TRow) => string,\n): CellEditingState<TRow> {\n const [editingCell, setEditingCell] = useState<CellAddress | undefined>(undefined);\n const [initialText, setInitialText] = useState<string | undefined>(undefined);\n const [hasDraft, setHasDraft] = useState(false);\n const [draftValue, setDraftValue] = useState<unknown>(undefined);\n const [overrides, setOverrides] = useState<Map<string, CellOverride>>(new Map());\n const [errors, setErrors] = useState<Map<string, string>>(new Map());\n const justCommittedRef = useRef(false);\n\n function closeEditor(): void {\n setEditingCell(undefined);\n setInitialText(undefined);\n setHasDraft(false);\n setDraftValue(undefined);\n }\n\n function applyChanges(changes: CellChange<TRow>[]): void {\n if (!cellEditing || cellEditing.disabled || changes.length === 0) return;\n setOverrides((prev) => {\n const next = new Map(prev);\n for (const change of changes) {\n next.set(cellKey(change.rowId, change.columnId), { value: change.value, previousValue: change.previousValue });\n }\n return next;\n });\n setErrors((prev) => {\n if (prev.size === 0) return prev;\n const next = new Map(prev);\n for (const change of changes) next.delete(cellKey(change.rowId, change.columnId));\n return next.size === prev.size ? prev : next;\n });\n void cellEditing.onCellsChange(changes);\n }\n\n function onBeginEdit(cell: CellAddress, text?: string): void {\n // Matches `applyChanges`'s own `disabled` gate — a consumer setting\n // `cellEditing.disabled` (e.g. while a previous gesture's `onCellsChange`\n // is still in flight) expects ALL interaction blocked, not just the\n // eventual commit silently discarded while the editor still visibly\n // opens and appears to accept input.\n if (!cellEditing || cellEditing.disabled) return;\n setEditingCell(cell);\n setInitialText(text);\n setHasDraft(text !== undefined);\n setDraftValue(text);\n }\n\n function onChangeDraft(value: unknown): void {\n setHasDraft(true);\n setDraftValue(value);\n }\n\n function onCommitEdit(row: TRow, previousValue: unknown): void {\n const cell = editingCell;\n const shouldCommit = hasDraft;\n const value = draftValue;\n closeEditor();\n if (!cell || !shouldCommit) return;\n justCommittedRef.current = true;\n applyChanges([{ rowId: cell.rowId, row, columnId: cell.columnId, previousValue, value }]);\n }\n\n function onCommitValue(row: TRow, previousValue: unknown, value: unknown): void {\n const cell = editingCell;\n closeEditor();\n if (!cell) return;\n justCommittedRef.current = true;\n applyChanges([{ rowId: cell.rowId, row, columnId: cell.columnId, previousValue, value }]);\n }\n\n function consumeJustCommitted(): boolean {\n const value = justCommittedRef.current;\n justCommittedRef.current = false;\n return value;\n }\n\n function onSetError(rowId: string, columnId: string, message: string | undefined): void {\n setErrors((prev) => {\n const key = cellKey(rowId, columnId);\n if (message === undefined) {\n if (!prev.has(key)) return prev;\n const next = new Map(prev);\n next.delete(key);\n return next;\n }\n const next = new Map(prev);\n next.set(key, message);\n return next;\n });\n }\n\n function getError(rowId: string, columnId: string): string | undefined {\n return errors.get(cellKey(rowId, columnId));\n }\n\n function resolveValue(rowId: string, columnId: string, rawValue: unknown): unknown {\n const override = overrides.get(cellKey(rowId, columnId));\n if (!override) return rawValue;\n if (rawValue !== override.previousValue) return rawValue;\n return override.value;\n }\n\n const ctx: CellEditingCellContext<TRow> | undefined = cellEditing\n ? {\n alwaysEdit: cellEditing.alwaysEdit ?? false,\n disabled: cellEditing.disabled ?? false,\n editingCell,\n initialText,\n hasDraft,\n draftValue,\n getRowId,\n getError,\n resolveValue,\n onBeginEdit,\n onCancelEdit: closeEditor,\n onChangeDraft,\n onCommitEdit,\n onCommitValue,\n onSetError,\n applyChange: (change) => applyChanges([change]),\n consumeJustCommitted,\n }\n : undefined;\n\n // Reassigned every render (a plain statement, not an effect) — see\n // `CellEditingCellContext`'s own doc for why.\n const ctxRef = useRef<CellEditingCellContext<TRow> | undefined>(undefined);\n ctxRef.current = ctx;\n\n return { ctx, ctxRef, applyChanges };\n}\n","import type { KeyboardEvent as ReactKeyboardEvent, ReactNode } from \"react\";\nimport { defaultFormat } from \"../column/format\";\nimport type { ColumnDef } from \"../column/types\";\nimport { isEditable } from \"../column/types\";\nimport { renderDefaultEditWidget } from \"../edit/registry\";\nimport type { CellEditingCellContext } from \"./useCellEditingState\";\n\n/**\n * Column types whose default editor's `onChange` fires once per COMPLETE\n * selection (choosing an enum option, toggling a boolean, picking a date) —\n * for these, that single `onChange` call commits immediately, matching\n * Excel's own behavior for a dropdown/checkbox cell. `string`/`number`/\n * `currency` fire `onChange` once per KEYSTROKE instead, so committing on\n * every one of those would mean one `onCellsChange` call per character\n * typed — those buffer locally via `onChangeDraft` and commit separately, on\n * blur or Enter/Tab (see `renderCellModeCell`'s own logic below).\n */\nexport type ColumnTypeLiteral = \"string\" | \"number\" | \"currency\" | \"date\" | \"datetime\" | \"boolean\" | \"enum\";\n\n/** Shared with `renderAlwaysEditCell.tsx` — same commit-immediately-vs-buffer split applies whether a cell's editor opened via a click or is always open. */\nexport function isAtomicEditorType(type: ColumnTypeLiteral): boolean {\n return type === \"enum\" || type === \"boolean\" || type === \"date\" || type === \"datetime\";\n}\n\n/**\n * Renders one cell under `cellEditing` mode — the immediate-apply\n * counterpart to `edit/renderEditableCell.tsx`'s accumulate-then-save one.\n * Reuses the exact same per-type editor widgets via `registry.tsx`'s\n * `renderDefaultEditWidget` (and the same `column.renderEditCell`/\n * `column.validateEdit` escape hatches) — only the commit semantics differ.\n *\n * Not responsible for range-selection visuals — that's `SelectionOverlay`,\n * one overlay for the whole grid — this only decides whether THIS cell is\n * the one currently showing its editor.\n */\nexport function renderCellModeCell<TRow>(\n column: ColumnDef<TRow>,\n row: TRow,\n rawValue: unknown,\n ctx: CellEditingCellContext<TRow> | undefined,\n): ReactNode {\n const staticContent = (value: unknown): ReactNode => (column.cell ? column.cell(value, row) : defaultFormat(column, value));\n if (!ctx) return staticContent(rawValue);\n\n const rowId = ctx.getRowId(row);\n const value = ctx.resolveValue(rowId, column.id, rawValue);\n const isThisCellEditing = ctx.editingCell?.rowId === rowId && ctx.editingCell.columnId === column.id && isEditable(column, row);\n\n if (!isThisCellEditing) {\n return (\n <span data-testid={`cell-${rowId}-${column.id}`} className=\"block w-full\">\n {staticContent(value)}\n </span>\n );\n }\n\n const isAtomic = isAtomicEditorType(column.type);\n const error = ctx.getError(rowId, column.id);\n const startValue = ctx.initialText !== undefined ? ctx.initialText : value;\n const currentValue = ctx.hasDraft ? ctx.draftValue : startValue;\n\n function validate(candidate: unknown): string | undefined {\n return column.validateEdit?.(candidate, row);\n }\n\n const onChange = (next: unknown): void => {\n const message = validate(next);\n ctx!.onSetError(rowId, column.id, message);\n // `onCommitValue` (not `onChangeDraft` + `onCommitEdit`) for the atomic\n // path: `onCommitEdit` reads `hasDraft`/`draftValue` from React state,\n // which a same-tick `onChangeDraft` call immediately before it would NOT\n // yet reflect — state updates apply on the next render, not before the\n // current handler returns. `onCommitValue` takes the new value directly,\n // sidestepping that entirely.\n if (isAtomic && !message) {\n // `value` (resolveValue's result), not `rawValue` — a still-fresh\n // override from an immediately-prior edit this session must be the\n // reported `previousValue`, not the underlying row data it's\n // temporarily masking (see `resolveValue`'s own doc).\n ctx!.onCommitValue(row, value, next);\n return;\n }\n // Buffered types always land here; an atomic type only does when its\n // selection was invalid — shown (with its error) but not committed, via\n // the same draft slot buffered types use, so the invalid choice stays\n // visible instead of snapping back to the pre-edit value.\n ctx!.onChangeDraft(next);\n };\n\n const editor = column.renderEditCell\n ? column.renderEditCell(currentValue, row, onChange, error, true)\n : renderDefaultEditWidget(column, rowId, currentValue, onChange, error, true);\n\n // Buffered (string/number/currency) editors only: Enter/Tab attempts a\n // commit-and-continue; a validation failure blocks it (stops the event\n // from reaching <DataGrid>'s own Enter/Tab-navigates handler) so the user\n // stays on the broken cell instead of silently losing what they typed.\n // Escape always reverts, for every editor type — same one-cell-only\n // revert semantics `renderEditableCell.tsx` documents for row mode.\n function handleKeyDown(event: ReactKeyboardEvent): void {\n if (event.key === \"Escape\") {\n event.stopPropagation();\n ctx!.onCancelEdit();\n return;\n }\n // A multiline column's own Shift+Enter is left alone entirely — the\n // textarea's native default inserts a literal newline, matching real\n // spreadsheet multiline-cell editing (plain Enter still commits and\n // moves down, same as every other buffered editor).\n const isMultilineNewline = column.type === \"string\" && column.multiline && event.key === \"Enter\" && event.shiftKey;\n if (isMultilineNewline) return;\n if (isAtomic || (event.key !== \"Enter\" && event.key !== \"Tab\")) return;\n const message = validate(currentValue);\n if (message) {\n event.stopPropagation();\n return;\n }\n ctx!.onCommitEdit(row, value);\n // Deliberately NOT stopped: <DataGrid>'s own keydown handler on the\n // scroll container sees this same Enter/Tab afterward and moves the\n // selection cursor to the next cell — this cell is no longer \"editing\"\n // by the time that happens (onCommitEdit just closed it), so the two\n // handlers compose into exactly \"commit, then move,\" not a conflict.\n }\n\n // Buffered editors only: clicking away commits a valid draft, or reverts\n // an invalid one — there's no way to keep the editor open once focus has\n // already left it, so \"block the blur\" (Enter/Tab's option) isn't\n // available here.\n function handleBlur(): void {\n if (isAtomic) return;\n if (validate(currentValue)) ctx!.onCancelEdit();\n else ctx!.onCommitEdit(row, value);\n }\n\n return (\n <div onKeyDown={handleKeyDown} onBlur={handleBlur}>\n {editor}\n </div>\n );\n}\n","import type { MouseEvent as ReactMouseEvent, ReactElement } from \"react\";\nimport { useLayoutEffect, useRef, useState } from \"react\";\nimport type { CellRange } from \"./types\";\n\ninterface OverlayRect {\n top: number;\n left: number;\n width: number;\n height: number;\n}\n\n/** Matches a `[data-cell-row][data-cell-col]` `<td>` exactly — `CSS.escape` handles any id containing quotes/backslashes/etc. so the attribute selector below can't be broken out of. */\nfunction cellSelector(rowId: string, columnId: string): string {\n const escape = (value: string): string => (typeof CSS !== \"undefined\" && CSS.escape ? CSS.escape(value) : value.replace(/[\"\\\\]/g, \"\\\\$&\"));\n return `[data-cell-row=\"${escape(rowId)}\"][data-cell-col=\"${escape(columnId)}\"]`;\n}\n\n/** Resolves `range`'s bounding rect within `container`'s own unscrolled content-space — `undefined` if either corner isn't currently mounted (e.g. scrolled out under virtualization). Shared between the selection rect and the fill-preview rect below; see the component doc for why content-space (not viewport-space) is what makes this scroll correctly with zero listeners of its own. */\nfunction computeRangeRect(container: HTMLDivElement, range: CellRange): OverlayRect | undefined {\n const anchorEl = container.querySelector<HTMLElement>(cellSelector(range.anchor.rowId, range.anchor.columnId));\n const focusEl = container.querySelector<HTMLElement>(cellSelector(range.focus.rowId, range.focus.columnId));\n if (!anchorEl || !focusEl) return undefined;\n const containerRect = container.getBoundingClientRect();\n const a = anchorEl.getBoundingClientRect();\n const f = focusEl.getBoundingClientRect();\n const left = Math.min(a.left, f.left) - containerRect.left + container.scrollLeft;\n const top = Math.min(a.top, f.top) - containerRect.top + container.scrollTop;\n const right = Math.max(a.right, f.right) - containerRect.left + container.scrollLeft;\n const bottom = Math.max(a.bottom, f.bottom) - containerRect.top + container.scrollTop;\n return { top, left, width: right - left, height: bottom - top };\n}\n\nfunction rectsEqual(a: OverlayRect | undefined, b: OverlayRect | undefined): boolean {\n if (a === b) return true;\n if (!a || !b) return false;\n return a.top === b.top && a.left === b.left && a.width === b.width && a.height === b.height;\n}\n\nconst HANDLE_SIZE = 8;\n\n/**\n * Renders the current range selection (and, while a fill-handle drag is in\n * progress, a dashed preview of the range it would extend to) as absolutely-\n * positioned overlay divs, geometry read live from the DOM rects of the\n * relevant cells' `data-cell-row`/`data-cell-col` nodes — deliberately not\n * derived from `columnSizing`/row-height state, since reading the live DOM\n * always matches exactly what's rendered (including natural, non-resizing\n * column widths `columnSizing`'s own math doesn't account for).\n *\n * Must render as a CHILD of the scrollable container (not a sibling outside\n * it), with that container given `position: relative` — position/left/top\n * are computed in the container's own unscrolled content-space (rect minus\n * the container's rect, plus the container's current scroll offset), so once\n * set, the overlay scrolls together with the table content via ordinary\n * browser scrolling with no scroll listener of its own. A virtualized grid's\n * scroll-driven re-renders (the virtualizer's own React state) naturally\n * recompute this on every such render; a non-virtualized grid never needs to\n * (native scroll changes nothing in content-space).\n *\n * Deliberately isolated from `visibleColumns`/`tanstackColumns` — it reads\n * nothing from either, only `range`/`fillPreviewRange` and the DOM — so\n * painting/updating a selection or a fill-drag (including during a fast\n * mouse-drag) never touches the column-definition memo that would otherwise\n * remount the whole table body.\n */\nexport function SelectionOverlay({\n containerRef,\n range,\n fillPreviewRange,\n onFillHandleMouseDown,\n}: {\n containerRef: { current: HTMLDivElement | null };\n range: CellRange | undefined;\n /** The live preview range during a fill-handle drag, or `undefined` when not fill-dragging. */\n fillPreviewRange?: CellRange | undefined;\n /** Mousedown on the fill handle — starts a fill-drag. Omit to render no handle at all (e.g. a read-only or non-editable selection). */\n onFillHandleMouseDown?: (event: ReactMouseEvent<HTMLDivElement>) => void;\n}): ReactElement | null {\n const [rect, setRect] = useState<OverlayRect | undefined>(undefined);\n const [previewRect, setPreviewRect] = useState<OverlayRect | undefined>(undefined);\n\n // Read fresh inside the effect below via a ref, not as a dependency of the\n // scroll-listener effect — see that effect's own doc for why it must not\n // resubscribe on every `range`/`fillPreviewRange` change.\n const recomputeRef = useRef<() => void>(() => {});\n recomputeRef.current = () => {\n const container = containerRef.current;\n const nextRect = range && container ? computeRangeRect(container, range) : undefined;\n setRect((prev) => (rectsEqual(prev, nextRect) ? prev : nextRect));\n const nextPreviewRect = fillPreviewRange && container ? computeRangeRect(container, fillPreviewRange) : undefined;\n setPreviewRect((prev) => (rectsEqual(prev, nextPreviewRect) ? prev : nextPreviewRect));\n };\n\n // No dependency array: geometry can change for reasons other than `range`/\n // `fillPreviewRange` themselves (a column resize, a virtualized row\n // mounting/unmounting) — this is deliberately cheap enough (at most four\n // `getBoundingClientRect` reads) to redo on every render of this small,\n // isolated component rather than trying to enumerate every input that\n // could move a cell.\n useLayoutEffect(() => {\n recomputeRef.current();\n });\n\n // A `pinned` cell's `getBoundingClientRect()` is reported in VIEWPORT\n // space (fixed by `position: sticky`) rather than scrolling with the\n // content the way every other cell's does — `computeRangeRect`'s own\n // \"unscrolled content-space\" formula (rect minus the container's rect,\n // plus the container's current scroll offset) only cancels out correctly\n // for a cell that actually moves with `scrollLeft`/`scrollTop`. A plain\n // horizontal/vertical scroll of the container is otherwise invisible to\n // this component — nothing about it changes React state anywhere in\n // `<DataGrid>`, so the `useLayoutEffect` above (which only reruns when\n // THIS component re-renders) would leave a selection anchored on a pinned\n // column silently drifting away from the real cell as the container keeps\n // scrolling. A plain native `scroll` listener (passive; this only ever\n // reads geometry, never calls `preventDefault`) is what makes a pinned\n // corner track correctly; recomputing here is a no-op for the common case\n // of an all-unpinned selection, since that math already cancels out scroll\n // on its own.\n useLayoutEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n function handleScroll(): void {\n recomputeRef.current();\n }\n container.addEventListener(\"scroll\", handleScroll, { passive: true });\n return () => container.removeEventListener(\"scroll\", handleScroll);\n // eslint-disable-next-line react-hooks/exhaustive-deps -- deliberately only resubscribes when the container itself changes identity, not on every range/fillPreviewRange change; `recomputeRef.current` is reassigned above on every render and read fresh from inside the listener.\n }, [containerRef.current]);\n\n if (!rect) return null;\n return (\n <>\n <div\n data-testid=\"cell-selection-overlay\"\n aria-hidden\n className=\"pointer-events-none absolute z-20 border-2 border-primary bg-primary/10\"\n style={{ top: rect.top, left: rect.left, width: rect.width, height: rect.height }}\n />\n {previewRect && (\n <div\n data-testid=\"cell-fill-preview-overlay\"\n aria-hidden\n className=\"pointer-events-none absolute z-20 border-2 border-dashed border-primary\"\n style={{ top: previewRect.top, left: previewRect.left, width: previewRect.width, height: previewRect.height }}\n />\n )}\n {onFillHandleMouseDown && (\n <div\n data-testid=\"cell-fill-handle\"\n role=\"button\"\n aria-label=\"Fill handle: drag to fill adjacent cells\"\n className=\"absolute z-30 cursor-crosshair border border-background bg-primary\"\n style={{\n top: rect.top + rect.height - HANDLE_SIZE / 2,\n left: rect.left + rect.width - HANDLE_SIZE / 2,\n width: HANDLE_SIZE,\n height: HANDLE_SIZE,\n }}\n onMouseDown={(event) => {\n // Not this component's own selection-rect mousedown — starting a\n // fill-drag must not ALSO start (or extend) a range-select drag\n // on the cell underneath the handle.\n event.stopPropagation();\n onFillHandleMouseDown(event);\n }}\n />\n )}\n </>\n );\n}\n","import { toDate, toNumber } from \"../column/format\";\nimport type { ColumnDef, EnumOption } from \"../column/types\";\n\nfunction parseBoolean(raw: string): boolean | undefined {\n const normalized = raw.trim().toLowerCase();\n if (normalized === \"true\" || normalized === \"yes\" || normalized === \"1\") return true;\n if (normalized === \"false\" || normalized === \"no\" || normalized === \"0\") return false;\n return undefined;\n}\n\n/** Matches a pasted/filled string against `options` by value first (exact), then by label (case-insensitive) — a paste from a real spreadsheet is far more likely to carry the human-readable label than the underlying value code. */\nfunction matchEnumOption(options: EnumOption[], raw: string): string | undefined {\n const trimmed = raw.trim();\n const byValue = options.find((option) => option.value === trimmed);\n if (byValue) return byValue.value;\n const lowered = trimmed.toLowerCase();\n return options.find((option) => option.label.toLowerCase() === lowered)?.value;\n}\n\n// The literal text Excel/Sheets puts in a cell showing a formula error.\n// Pasting one of these into any non-`string` column skips that cell, same as\n// any other value that doesn't coerce — `toNumber`/`toDate`/`parseBoolean`/\n// `matchEnumOption` already happen to reject all of these today (none of\n// them looks like a number/date/boolean/option), but checking explicitly\n// means that stays true even if one of those helpers' own \"not a valid X\"\n// fallback changes independently later.\nconst EXCEL_ERROR_STRINGS = new Set([\"#N/A\", \"#DIV/0!\", \"#REF!\", \"#VALUE!\", \"#NAME?\", \"#NULL!\", \"#NUM!\", \"#SPILL!\", \"#CALC!\"]);\n\nfunction isExcelErrorString(raw: string): boolean {\n return EXCEL_ERROR_STRINGS.has(raw.trim());\n}\n\n/**\n * Coerces one pasted/filled raw TSV cell string into the typed value a\n * column's editor/validator expects — the paste/fill equivalent of\n * `edit/registry.tsx`'s per-type dispatch, reusing the exact same `toDate`/\n * `toNumber` parsing `column/format.ts` uses for display so paste coercion\n * can't drift from what the grid already shows.\n *\n * Returns `undefined` for a value that can't be coerced at all (e.g. \"abc\"\n * into a number column) — the caller's job to skip that cell rather than\n * hand `onCellsChange` a `NaN`/`Invalid Date`. An empty string coerces to\n * `null` for `number`/`currency` (pasting a blank cell over a numeric one\n * clears it, matching `NumberEditor`'s own null-for-cleared convention) —\n * `string` columns instead treat an empty string as itself a valid value.\n * An Excel/Sheets error string (`#N/A`, `#DIV/0!`, etc.) pasted into any\n * column but `string` is likewise skipped rather than coerced.\n */\nexport function coerceValueForColumn<TRow>(column: ColumnDef<TRow>, raw: string): { value: unknown } | undefined {\n if (column.type !== \"string\" && isExcelErrorString(raw)) return undefined;\n switch (column.type) {\n case \"string\":\n return { value: raw };\n case \"number\":\n case \"currency\": {\n if (raw.trim() === \"\") return { value: null };\n const num = toNumber(raw);\n return num === null ? undefined : { value: num };\n }\n case \"boolean\": {\n const bool = parseBoolean(raw);\n return bool === undefined ? undefined : { value: bool };\n }\n case \"date\":\n case \"datetime\": {\n const date = toDate(raw);\n return date === null ? undefined : { value: date };\n }\n case \"enum\": {\n const matched = matchEnumOption(column.options, raw);\n return matched === undefined ? undefined : { value: matched };\n }\n }\n}\n","import { defaultFormat } from \"../column/format\";\nimport type { ColumnDef } from \"../column/types\";\nimport { getColumnValue } from \"../column/types\";\nimport { buildIndexMap, normalizeRange } from \"./rangeUtils\";\nimport type { CellRange } from \"./types\";\n\n/** Wraps `field` in double quotes (doubling any internal quotes) only if it contains a tab, newline, or quote — the same quoting convention real spreadsheet apps (Excel, Google Sheets) use for their own TSV clipboard payloads, so a value round-trips correctly through copy/paste with them. */\nfunction quoteTsvField(field: string): string {\n if (!/[\\t\\n\\r\"]/.test(field)) return field;\n return `\"${field.replace(/\"/g, '\"\"')}\"`;\n}\n\n/**\n * Serializes the cells inside `range` to Excel-compatible TSV text: tab-\n * separated columns, newline-separated rows, walking `rowIds`/`columnIds` in\n * their on-screen order (not `range`'s own anchor/focus order). Each cell's\n * text comes from `column.formatForClipboard` if the column supplies one,\n * else the same `defaultFormat` display text the grid itself renders —\n * deliberately not a second \"raw value to string\" path.\n */\nexport function rangeToTsv<TRow>(\n range: CellRange,\n rows: TRow[],\n rowIds: string[],\n columns: ColumnDef<TRow>[],\n columnIds: string[],\n getRowId: (row: TRow) => string,\n): string {\n const rowIndex = buildIndexMap(rowIds);\n const columnIndex = buildIndexMap(columnIds);\n const normalized = normalizeRange(range, rowIndex, columnIndex);\n if (!normalized) return \"\";\n\n const columnById = new Map(columns.map((column) => [column.id, column]));\n const rowById = new Map(rows.map((row) => [getRowId(row), row]));\n\n const lines: string[] = [];\n for (let r = normalized.rowStart; r <= normalized.rowEnd; r++) {\n const rowId = rowIds[r];\n const row = rowId !== undefined ? rowById.get(rowId) : undefined;\n const fields: string[] = [];\n for (let c = normalized.colStart; c <= normalized.colEnd; c++) {\n const columnId = columnIds[c];\n const column = columnId !== undefined ? columnById.get(columnId) : undefined;\n if (!column || row === undefined) {\n fields.push(\"\");\n continue;\n }\n const value = getColumnValue(column, row);\n const text = column.formatForClipboard ? column.formatForClipboard(value, row) : defaultFormat(column, value);\n fields.push(quoteTsvField(text));\n }\n lines.push(fields.join(\"\\t\"));\n }\n return lines.join(\"\\n\");\n}\n\n/**\n * Parses Excel-compatible TSV/tab-delimited clipboard text into a 2D array\n * of raw string cells. A small character-by-character state machine (not a\n * naive split-by-newline-then-split-by-tab) so a quoted field's own embedded\n * newline — a genuine multi-line cell copied from Excel/Sheets — isn't\n * mistaken for a row boundary, and a doubled `\"\"` inside a quoted field\n * unescapes to one literal `\"`.\n */\nexport function parseTsv(text: string): string[][] {\n // A leading byte-order mark shows up on some Windows clipboard sources\n // (and would otherwise become part of the very first cell's own text).\n const withoutBom = text.startsWith(\"\") ? text.slice(1) : text;\n const normalized = withoutBom.replace(/\\r\\n/g, \"\\n\");\n const rows: string[][] = [];\n let row: string[] = [];\n let field = \"\";\n let inQuotes = false;\n let i = 0;\n while (i < normalized.length) {\n const ch = normalized[i]!;\n if (inQuotes) {\n if (ch === '\"') {\n if (normalized[i + 1] === '\"') {\n field += '\"';\n i += 2;\n continue;\n }\n inQuotes = false;\n i++;\n continue;\n }\n field += ch;\n i++;\n continue;\n }\n if (ch === '\"' && field === \"\") {\n inQuotes = true;\n i++;\n continue;\n }\n if (ch === \"\\t\") {\n row.push(field);\n field = \"\";\n i++;\n continue;\n }\n if (ch === \"\\n\") {\n row.push(field);\n rows.push(row);\n row = [];\n field = \"\";\n i++;\n continue;\n }\n field += ch;\n i++;\n }\n row.push(field);\n rows.push(row);\n\n // A trailing newline (common when copying a full range from Excel/Sheets)\n // would otherwise parse as one extra, bogus all-empty trailing row.\n const last = rows[rows.length - 1];\n if (rows.length > 1 && normalized.endsWith(\"\\n\") && last?.length === 1 && last[0] === \"\") {\n rows.pop();\n }\n return rows;\n}\n","import type { ColumnDef } from \"../column/types\";\nimport { getColumnValue, isEditable } from \"../column/types\";\nimport { buildIndexMap, normalizeRange } from \"./rangeUtils\";\nimport type { CellChange, CellRange } from \"./types\";\n\n/**\n * Computes the `CellChange`s a fill-handle drag produces: every cell newly\n * covered by `finalRange` but not already inside `sourceRange` copies its\n * value from the corresponding source cell, TILING the source pattern across\n * the fill direction when the extension is longer than the source itself —\n * the same repeat-the-pattern behavior Excel's own fill-handle uses for a\n * multi-cell source (a single-cell source is just the degenerate case: every\n * new cell copies that one cell). Value-copy only, no numeric/date\n * auto-increment — MVP scope, matching the akeneo hand-rolled\n * implementation this feature replaces, which also only ever copies a\n * single value uniformly.\n *\n * Cells already inside `sourceRange` are left out of the result entirely —\n * they're untouched by a fill, only the newly-covered ones change.\n * A target cell that's missing, not editable, or whose source cell is\n * missing is silently skipped.\n */\nexport function computeFillChanges<TRow>(\n sourceRange: CellRange,\n finalRange: CellRange,\n rowIds: string[],\n columnIds: string[],\n columns: ColumnDef<TRow>[],\n getRow: (rowId: string) => TRow | undefined,\n): CellChange<TRow>[] {\n const rowIndex = buildIndexMap(rowIds);\n const columnIndex = buildIndexMap(columnIds);\n const source = normalizeRange(sourceRange, rowIndex, columnIndex);\n const final = normalizeRange(finalRange, rowIndex, columnIndex);\n if (!source || !final) return [];\n\n const columnById = new Map(columns.map((column) => [column.id, column]));\n const sourceRowSpan = source.rowEnd - source.rowStart + 1;\n const sourceColSpan = source.colEnd - source.colStart + 1;\n\n /** Maps any row index in `final`'s range to the source row it tiles from — identity for a row already inside `source`, otherwise wrapping through `source`'s own rows in order, continuing in the same direction past its far edge. */\n function sourceRowFor(targetRow: number): number {\n if (targetRow >= source!.rowStart && targetRow <= source!.rowEnd) return targetRow;\n if (targetRow > source!.rowEnd) return source!.rowStart + ((targetRow - source!.rowEnd - 1) % sourceRowSpan);\n return source!.rowEnd - ((source!.rowStart - targetRow - 1) % sourceRowSpan);\n }\n function sourceColFor(targetCol: number): number {\n if (targetCol >= source!.colStart && targetCol <= source!.colEnd) return targetCol;\n if (targetCol > source!.colEnd) return source!.colStart + ((targetCol - source!.colEnd - 1) % sourceColSpan);\n return source!.colEnd - ((source!.colStart - targetCol - 1) % sourceColSpan);\n }\n\n const changes: CellChange<TRow>[] = [];\n for (let r = final.rowStart; r <= final.rowEnd; r++) {\n const isNewRow = r < source.rowStart || r > source.rowEnd;\n for (let c = final.colStart; c <= final.colEnd; c++) {\n const isNewCol = c < source.colStart || c > source.colEnd;\n if (!isNewRow && !isNewCol) continue; // inside the original source rectangle — untouched by the fill\n\n const targetRowId = rowIds[r];\n const targetColumnId = columnIds[c];\n if (targetRowId === undefined || targetColumnId === undefined) continue;\n const column = columnById.get(targetColumnId);\n const targetRow = getRow(targetRowId);\n if (!column || targetRow === undefined || !isEditable(column, targetRow)) continue;\n\n // Both axes matter here, not just the row one: a vertical fill (rows\n // extended, columns unchanged) has `sourceColFor(c) === c`, so\n // `sourceColumn === column` and this degenerates to \"same column,\n // cycled source rows\" — but a horizontal fill (columns extended, rows\n // unchanged) needs the SOURCE column's own value, not the target\n // column's, or every new column would just copy whatever (nothing)\n // already sat in its own cells instead of the pattern being extended.\n const sourceRowId = rowIds[sourceRowFor(r)];\n const sourceColumnId = columnIds[sourceColFor(c)];\n const sourceRow = sourceRowId !== undefined ? getRow(sourceRowId) : undefined;\n const sourceColumn = sourceColumnId !== undefined ? columnById.get(sourceColumnId) : undefined;\n if (sourceRow === undefined || !sourceColumn) continue;\n\n changes.push({\n rowId: targetRowId,\n row: targetRow,\n columnId: targetColumnId,\n previousValue: getColumnValue(column, targetRow),\n value: getColumnValue(sourceColumn, sourceRow),\n });\n }\n }\n return changes;\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 {\n CellContext,\n ColumnDef as TanstackColumnDef,\n ColumnSizingState,\n Row as TanstackRow,\n RowData,\n} 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 {\n ClipboardEvent as ReactClipboardEvent,\n CSSProperties,\n KeyboardEvent as ReactKeyboardEvent,\n MouseEvent as ReactMouseEvent,\n ReactElement,\n ReactNode,\n} from \"react\";\nimport { Fragment, useEffect, useMemo, useRef, useState } from \"react\";\nimport { parseTsv, rangeToTsv } from \"../cell-editing/clipboard\";\nimport { coerceValueForColumn } from \"../cell-editing/coerce\";\nimport { computeFillChanges } from \"../cell-editing/fillHandle\";\nimport { buildIndexMap, normalizeRange } from \"../cell-editing/rangeUtils\";\nimport { AlwaysEditCell, type AtomicGestureContext } from \"../cell-editing/renderAlwaysEditCell\";\nimport { renderCellModeCell } from \"../cell-editing/renderCellModeCell\";\nimport { SelectionOverlay } from \"../cell-editing/SelectionOverlay\";\nimport type { CellAddress, CellChange } from \"../cell-editing/types\";\nimport type { CellEditingCellContext } from \"../cell-editing/useCellEditingState\";\nimport { useCellEditingState } from \"../cell-editing/useCellEditingState\";\nimport { useCellSelection } from \"../cell-editing/useCellSelection\";\nimport { alignClassName } from \"../column/format\";\nimport type { ColumnDef } from \"../column/types\";\nimport { getColumnValue, isEditable, isFilterable, isSortable } from \"../column/types\";\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 { useGroupExpansion } from \"../hooks/useGroupExpansion\";\nimport { useStickyGroupHeaderTop } from \"../hooks/useStickyGroupHeaderTop\";\nimport { useVisibleColumns } from \"../hooks/useVisibleColumns\";\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// Floor for keyboard-driven resize (ArrowLeft on the resize handle) — mouse\n// drag has no equivalent floor today, but a keyboard user has no visual\n// feedback while resizing, so this keeps a column from being shrunk to\n// (or past) zero-width by holding the key down.\nconst MIN_COLUMN_SIZE = 40;\n\n// `aria-sort` belongs on the `<th>` itself, not the toggle `<button>` inside\n// it (WAI-ARIA \"sort\" property — see the table sort pattern in APG): screen\n// readers announce a header's sort state from the cell, not from focusing a\n// child control. `\"none\"` (rather than omitting the attribute) tells\n// assistive tech this header *is* sortable but not the current sort key,\n// distinct from a non-sortable column, which gets no `aria-sort` at all.\nfunction sortDirToAriaSort(dir: \"asc\" | \"desc\" | undefined): \"ascending\" | \"descending\" | \"none\" {\n if (dir === \"asc\") return \"ascending\";\n if (dir === \"desc\") return \"descending\";\n return \"none\";\n}\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// Stable empty-array identity for `useCellSelection`'s `rowIds`/`columnIds`\n// when `cellEditing` isn't set — a fresh `[]` literal every render would\n// still be a *different* array each time, defeating `buildIndexMap`'s own\n// memoization for no reason (selection is inert either way, but the memo\n// should stay cheap regardless).\nconst EMPTY_ID_ARRAY: string[] = [];\n\n/**\n * Whether a clipboard event's target is a genuine native text field — an\n * `<input>` (covers `StringEditor`/`NumberEditor`/`DateEditor`, the last via\n * `<input type=\"date\">`) or a `<textarea>` (`MultilineStringEditor`) —\n * deliberately NOT true for `EnumEditor`/`BooleanEditor`'s Radix-based\n * button/listbox widgets, which have no free text of their own to copy/paste\n * natively. Used by `handleCopy`/`handlePaste` to defer to the browser under\n * `cellEditing.alwaysEdit`, where every editable cell's own editor is always\n * mounted, so there's no single `editingCell` left to check the way\n * click-to-edit mode does.\n */\nfunction isNativeTextFieldTarget(event: { target: EventTarget | null }): boolean {\n const tag = (event.target as HTMLElement | null)?.tagName;\n return tag === \"INPUT\" || tag === \"TEXTAREA\";\n}\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 * One entry in the flattened, virtualizable render list `<DataGrid>` windows\n * over below (see `flatItems`'s own doc) — a synthetic group-header, or one\n * real table row. Kept as its own module-scope generic (rather than inlined\n * where `flatItems` is computed) so `renderFlatItem`/`renderGroupHeaderTbody`\n * can share the exact same shape without either side re-deriving it.\n */\ntype FlatItem<TRow extends RowData> =\n | { kind: \"header\"; key: string; rows: TRow[] }\n | { kind: \"row\"; row: TanstackRow<GridTableFeatures, TRow> };\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 *\n * `cellEditingCtxRef` follows the identical recipe for `cellEditing` mode's\n * own per-keystroke state (`CellEditingCellContext`). At most one of\n * `editingCtxRef.current`/`cellEditingCtxRef.current` is ever set (the two\n * modes are mutually exclusive — see `DataGridProps.cellEditing`'s own doc),\n * so which renderer a cell uses is decided fresh on every actual render,\n * with neither ref itself ever appearing in this function's own deps.\n *\n * `atomicGestureRef` is the same recipe again, for `AlwaysEditCell`'s own\n * atomic-widget gesture handling (`AtomicGestureContext`) — see its own doc.\n */\nfunction toTanstackColumns<TRow extends RowData>(\n columns: ColumnDef<TRow>[],\n editingCtxRef: { current: EditingCellContext<TRow> | undefined },\n cellEditingCtxRef: { current: CellEditingCellContext<TRow> | undefined },\n atomicGestureRef: { current: AtomicGestureContext | 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 const cellEditingCtx = cellEditingCtxRef.current;\n if (!cellEditingCtx) return renderEditableCell(column, info.row.original, info.getValue(), editingCtxRef.current);\n return cellEditingCtx.alwaysEdit ? (\n <AlwaysEditCell\n column={column}\n row={info.row.original}\n rawValue={info.getValue()}\n ctx={cellEditingCtx}\n atomicGesture={atomicGestureRef.current}\n />\n ) : (\n renderCellModeCell(column, info.row.original, info.getValue(), cellEditingCtx)\n );\n },\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 onGridStateChange,\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 showTotals = false,\n editing,\n cellEditing,\n}: DataGridProps<TRow>): ReactElement {\n // Not supported together with `groupBy` yet — `cellEditingRowIds` below is\n // built from the flat `tableRows` list, but rendering only shows expanded-\n // group rows via `groupedBuckets`'s own bucketed order, so a keyboard-\n // navigated selection could land on a row hidden inside a collapsed group.\n // Same \"one silently stands down\" precedent as `groupBy`+`virtualize` (see\n // `virtualize`'s own doc) rather than a hard error.\n const hasCellEditing = Boolean(cellEditing) && !groupBy;\n const { state, filtersByColumn, setColumnFilter, toggleSort, setPage } = useGridState(\n dataSource,\n initialState,\n gridState,\n showPagination,\n onGridStateChange,\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. Shared with `<TreeDataGrid>`'s own `groupBy` support via\n // `useGroupExpansion`.\n const { isGroupExpanded, toggleGroupExpanded } = useGroupExpansion(\n defaultGroupsExpanded,\n controlledExpandedGroups,\n onExpandedGroupsChange,\n );\n\n const { pendingEdits, editErrors, ctxRef: editingCtxRef, handleSaveEdits, handleDiscardEdits } = useEditingState(\n editing,\n getRowId,\n );\n // Gated on `hasCellEditing` (not `cellEditing` directly) so the groupBy\n // stand-down above (see that flag's own doc) also disables the per-cell\n // renderer/editor state, not just selection.\n const { ctx: cellEditingCtx, ctxRef: cellEditingCtxRef, applyChanges: applyCellChanges } = useCellEditingState(\n hasCellEditing ? cellEditing : undefined,\n getRowId,\n );\n // Same \"stable ref identity, reassigned every render\" recipe as\n // `editingCtxRef`/`cellEditingCtxRef` above — assigned further down, once\n // `cellSelection`/`pendingOpenEnumCell` (which its callbacks close over)\n // exist, but declared here so `tanstackColumns` below can take it without\n // depending on either.\n const atomicGestureRef = useRef<AtomicGestureContext | undefined>(undefined);\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. Shared with `<TreeDataGrid>`'s own\n // `columnVisibility` support via `useVisibleColumns`.\n const visibleColumns = useVisibleColumns(columns, columnVisibility);\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(\n () => toTanstackColumns(visibleColumns, editingCtxRef, cellEditingCtxRef, atomicGestureRef),\n [visibleColumns],\n );\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 // `table.getRow(id)` THROWS for an id no longer in the row model (verified\n // against @tanstack/table-core's coreRowsFeature.utils), unlike the\n // `?.original`-chained call sites below that were written assuming it\n // degrades to `undefined` — a real risk here specifically, since a\n // `cellEditing` selection/edit can reference a row id that's since been\n // removed from `data` (a consumer's own `onCellsChange` handler, or a\n // refetch). `getRowModel().rowsById` is a plain object lookup, so a\n // missing id just reads as `undefined`.\n function findRowById(rowId: string): TRow | undefined {\n return table.getRowModel().rowsById[rowId]?.original;\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 // Same \"entirely opt-in\" convention as `hasFilterRow`/`hasHeaderGroups`\n // above — the per-group summary row inside `renderGroupHeaderTbody` and\n // the `showTotals` `<tfoot>` both only render once at least one visible\n // column sets `summary` (see `BaseColumn.summary`'s own doc); a `groupBy`\n // or `showTotals` grid with no column opting in renders exactly as it did\n // before either feature existed.\n const hasColumnSummary = visibleColumns.some((column) => column.summary);\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\n // Dev-only guardrail: `showPagination={false}` with no explicit `pageSize`\n // now resolves to an effectively unbounded page (see `useGridState`'s own\n // doc), so this only fires for what that fix can't cover -- a controlled\n // `gridState` prop whose `pageSize` doesn't (or no longer) cover every\n // row, or a `dataSource: {mode: \"server\"}` response smaller than the\n // `rowCount` it reported. An explicit, uncontrolled `initialState.pageSize`\n // is the deliberate fixed-size-chunking use case and must never warn.\n const truncationWarnedRef = useRef(false);\n useEffect(() => {\n if (process.env.NODE_ENV === \"production\") return;\n if (showPagination) return;\n if (truncationWarnedRef.current) return;\n if (!gridState && initialState?.pageSize !== undefined) return;\n if (tableRows.length >= rowCount) return;\n truncationWarnedRef.current = true;\n console.warn(\n `[DataGrid] showPagination={false} but only ${tableRows.length} of ${rowCount} row(s) are rendered, with no pagination UI to reach the rest. Pass an explicit pageSize (via initialState or gridState) that covers every row you want visible, or a dataSource that returns all of them.`,\n );\n }, [showPagination, gridState, initialState?.pageSize, tableRows.length, rowCount]);\n\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. Shared with\n // `<TreeDataGrid>`'s own `groupBy` support via `useStickyGroupHeaderTop`.\n const { theadRef, groupHeaderTop } = useStickyGroupHeaderTop(Boolean(groupBy));\n\n // `threshold` is documented (see `DataGridVirtualizeOptions.threshold`) as\n // a REAL row count -- keyed off `tableRows.length`, not the flattened\n // render list below, so a grouped grid's synthetic header entries never\n // push a caller's actual row count over a threshold they didn't cross.\n // Identical to `flatItems.length` whenever `groupBy` is unset (no headers\n // to inflate the count), so this is a no-op change for every non-grouped\n // grid.\n const shouldVirtualize = Boolean(virtualize) && tableRows.length > (virtualize?.threshold ?? 100);\n\n // The single flattened, index-addressable render list `shouldVirtualize`\n // above gates windowing over -- a plain `{kind: \"row\"}` per table row when\n // `groupBy` is unset, or one `{kind: \"header\"}` entry per bucket\n // (contributing its OWN member rows right after it, only while that\n // bucket is expanded -- a collapsed bucket contributes just its header)\n // when it is. Flattening headers and rows into one array, instead of\n // virtualizing `tableRows` directly and rendering group headers as a\n // separate non-virtualized wrapper around them, is what makes `groupBy`\n // and `virtualize` composable at all: `useVirtualizer` needs one\n // contiguous, positionally-addressable list to window over, and a\n // header's height needs measuring/positioning exactly like a data row's.\n // Only actually built while virtualizing -- neither the plain `tableRows`\n // path nor `renderGroupedBucket`'s own non-virtualized grouped path below\n // ever reads it, and building it unconditionally would re-walk every\n // bucket (recomputing the exact same `bucket.items.map((row) =>\n // row.original)` `renderGroupedBucket` already does) purely to throw the\n // result away on every render of a grid that never virtualizes at all.\n const flatItems = useMemo<FlatItem<TRow>[]>(() => {\n if (!shouldVirtualize) return [];\n if (!groupedBuckets) return tableRows.map((row) => ({ kind: \"row\", row }));\n const items: FlatItem<TRow>[] = [];\n for (const bucket of groupedBuckets) {\n const originalRows = bucket.items.map((row) => row.original);\n items.push({ kind: \"header\", key: bucket.key, rows: originalRows });\n if (isGroupExpanded(bucket.key)) {\n for (const row of bucket.items) items.push({ kind: \"row\", row });\n }\n }\n return items;\n // eslint-disable-next-line react-hooks/exhaustive-deps -- `isGroupExpanded`'s identity is the only one of these deps that actually changes on an expand/collapse toggle (both the controlled-`expandedGroups`-object and uncontrolled-internal-Set forms get a fresh identity from their own setter); `groupedBuckets`/`tableRows` don't change from expand/collapse alone, they're listed because the memo also needs to re-run when the underlying data/grouping itself changes.\n }, [shouldVirtualize, groupedBuckets, tableRows, isGroupExpanded]);\n\n const scrollRef = useRef<HTMLDivElement>(null);\n const virtualizer = useVirtualizer({\n count: flatItems.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` -- the real\n // loaded-data count, deliberately NOT `flatItems.length`: the latter also\n // shifts on a pure expand/collapse toggle (no data changed at all), which\n // would defeat the \"won't fire again for the same data\" contract\n // (`DataGridVirtualizeOptions.onEndReached`'s own doc) the instant a\n // caller collapses a group while already scrolled to the bottom. WHERE\n // \"the end\" currently is, though, is still a `flatItems`/`virtualItems`\n // question (collapsing the last bucket really does move the bottom of the\n // rendered content) -- only the dedup fingerprint changes here. Guarded by\n // a ref (not state) since it's bookkeeping for an effect, not something\n // that should itself trigger a render.\n const lastVisibleIndex = virtualItems.length > 0 ? virtualItems[virtualItems.length - 1]!.index : -1;\n const notifiedForLengthRef = useRef<number>(-1);\n // A sort/filter change means the caller's `onStateChange` handler is about\n // to replace `dataSource.data` with an entirely new loaded window (a fresh\n // first page for the new sort/filter), not grow the existing one — reset\n // the dedup guard so a same-length replacement (e.g. the new window also\n // happens to be exactly one page long, the common case) doesn't get\n // silently mistaken for \"already notified for this length\" and swallowed.\n // Keyed off `state.filter`/`state.sort` identity, not `state.page`: this\n // mode doesn't own pagination (see `virtualize.onEndReached`'s own doc),\n // so a `page` change here would only come from some other caller-driven\n // reset, which already goes through a fresh `dataSource.data` (and thus a\n // real `tableRows.length` change) on its own.\n useEffect(() => {\n notifiedForLengthRef.current = -1;\n }, [state.filter, state.sort]);\n useEffect(() => {\n if (!virtualize?.onEndReached) return;\n if (lastVisibleIndex < 0 || lastVisibleIndex < flatItems.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, flatItems.length, tableRows.length, virtualize]);\n\n // Full logical row/column id lists (not just currently-mounted virtual\n // items) for `useCellSelection`'s range math and keyboard navigation — see\n // that hook's own doc for why id-keying (rather than DOM/virtualized\n // index) is what makes a selection survive virtualized mount/unmount.\n // Depends on `hasCellEditing` (not `cellEditing` itself) for the same\n // reason `serverRowCount`/`dataSource` above do: a caller inlining\n // `cellEditing={{ onCellsChange: fn }}` as a fresh object every render\n // must not defeat this memo just because that wrapper's identity changed.\n const cellEditingRowIds = useMemo(\n () => (hasCellEditing ? tableRows.map((row) => row.id) : EMPTY_ID_ARRAY),\n [hasCellEditing, tableRows],\n );\n const cellEditingColumnIds = useMemo(\n () => (hasCellEditing ? visibleColumns.map((column) => column.id) : EMPTY_ID_ARRAY),\n [hasCellEditing, visibleColumns],\n );\n const cellEditingRowIndex = useMemo(() => buildIndexMap(cellEditingRowIds), [cellEditingRowIds]);\n const cellSelection = useCellSelection({\n rowIds: cellEditingRowIds,\n columnIds: cellEditingColumnIds,\n onNavigateToRow: (rowId) => {\n if (!shouldVirtualize) return;\n const index = cellEditingRowIndex.get(rowId);\n if (index !== undefined) virtualizer.scrollToIndex(index);\n },\n });\n\n // Tracks the cell a mousedown started on — whether it was a shift-extend,\n // whether the pointer ever moved to a *different* cell before mouseup, and\n // whether this cell was ALREADY the one being edited at mousedown time —\n // the three things `handleMouseUp` below needs to tell a plain click (open\n // that cell's editor, Excel's single-click-to-edit convention) apart from\n // a real drag-select, and from a click that landed inside an\n // already-open editor (e.g. to reposition its text cursor). That last\n // check can't be redone at mouseup time by just reading\n // `cellEditingCtx.editingCell` then: `handleCellMouseDown`'s own\n // `scrollRef.current?.focus()` call blurs (closes/commits) whatever was\n // being edited as an ordinary side effect of moving focus, including this\n // very cell if the click landed inside it — by mouseup, `editingCell` is\n // already `undefined` either way, so only capturing the answer up front\n // (before that focus call) can tell \"was already editing this cell, don't\n // reopen it\" apart from \"was editing elsewhere, now free to open this one\".\n // Refs, not state: read-then-cleared synchronously inside a native event\n // handler, never rendered.\n const clickCellRef = useRef<{ rowId: string; columnId: string; extend: boolean; wasEditing: boolean } | null>(null);\n const dragMovedRef = useRef(false);\n // The one built-in enum (`<Select>`) cell (if any) that should open its\n // dropdown right now — see `AtomicGestureContext.shouldOpenEnum`'s own\n // doc for why `AlwaysEditCell` needs this instead of just letting Radix's\n // native open-on-click happen. Set (once) by `handleMouseUp` below;\n // deliberately never cleared back to `undefined` afterward — once set,\n // `AlwaysEditCell` opens itself and takes over its own open/close state\n // via `onOpenChange` from then on, so leaving this pointed at the same\n // cell is inert, not a standing \"force reopen.\"\n const [pendingOpenEnumCell, setPendingOpenEnumCell] = useState<CellAddress | undefined>(undefined);\n\n atomicGestureRef.current = {\n shouldOpenEnum: (rowId, columnId) =>\n pendingOpenEnumCell?.rowId === rowId && pendingOpenEnumCell?.columnId === columnId,\n // Mirrors exactly what `handleCellMouseDown` below does for every other\n // cell, minus the click-to-edit-only tail end (irrelevant here — always\n // invoked under `alwaysEdit`, same as that function's own early return\n // for it) — see `AtomicGestureContext.registerSelection`'s own doc for\n // why `AlwaysEditCell` can't just rely on that function running on its\n // own for a gesture it suppresses.\n registerSelection: (rowId, columnId, extend) => {\n cellSelection.startSelection({ rowId, columnId }, { extend });\n clickCellRef.current = { rowId, columnId, extend, wasEditing: false };\n dragMovedRef.current = false;\n },\n };\n\n /** Opens `cell`'s editor for a plain (non-extending, non-dragged) click — shared by the mouseup-commits-a-click path below and `handleCellDoubleClick`. No-ops if the cell isn't editable, or is (STILL — this check runs after `handleCellMouseDown`'s own blur-on-focus already had its chance to close it) the one being edited: `handleCellDoubleClick` has no `wasEditing` snapshot of its own to rely on, so this is what stops its second click's `dblclick` from clobbering a draft the reopen already produced. */\n function beginEditFromClick(cell: { rowId: string; columnId: string }): void {\n if (!cellEditingCtx) return;\n if (cellEditingCtx.editingCell?.rowId === cell.rowId && cellEditingCtx.editingCell.columnId === cell.columnId) return;\n const column = columnById.get(cell.columnId);\n const row = findRowById(cell.rowId);\n if (!column || row === undefined || !isEditable(column, row)) return;\n cellEditingCtx.onBeginEdit(cell);\n }\n\n // Mousemove/mouseup are attached to `window` (not the scroll container)\n // while a drag is in progress — the pointer commonly moves outside the\n // container's own bounds mid-drag (e.g. past its edge), and a container-\n // scoped listener alone would silently stop tracking the drag at that\n // point. Coalesced through one `requestAnimationFrame` per frame — never a\n // raw `updateDrag` call per native `mousemove` event — which is the fix\n // for the exact perf bug this whole feature exists to replace (see\n // `useCellSelection.updateDrag`'s own doc: a hand-rolled excel-grid\n // implementation elsewhere in this org rebuilt its entire column-def array\n // on every unthrottled mousemove pixel during a drag-fill).\n const pendingDragCellRef = useRef<{ rowId: string; columnId: string } | null>(null);\n const dragRafRef = useRef<number | null>(null);\n useEffect(() => {\n if (!cellSelection.isDragging) return;\n function handleMouseMove(event: MouseEvent): void {\n const cellEl = (event.target as HTMLElement | null)?.closest<HTMLElement>(\"[data-cell-row]\");\n const rowId = cellEl?.dataset.cellRow;\n const columnId = cellEl?.dataset.cellCol;\n if (rowId === undefined || columnId === undefined) return;\n const anchor = clickCellRef.current;\n if (anchor && (rowId !== anchor.rowId || columnId !== anchor.columnId)) dragMovedRef.current = true;\n pendingDragCellRef.current = { rowId, columnId };\n if (dragRafRef.current !== null) return;\n dragRafRef.current = requestAnimationFrame(() => {\n dragRafRef.current = null;\n if (pendingDragCellRef.current) cellSelection.updateDrag(pendingDragCellRef.current);\n });\n }\n function handleMouseUp(): void {\n cellSelection.endDrag();\n const clickCell = clickCellRef.current;\n clickCellRef.current = null;\n if (!clickCell || clickCell.extend || clickCell.wasEditing || dragMovedRef.current) return;\n // Under `alwaysEdit`, `beginEditFromClick` has nothing to do (see\n // `editingCell`'s own doc: meaningless there) — the one thing a plain\n // click still needs to trigger explicitly is opening a built-in enum\n // widget's dropdown, since `AlwaysEditCell` unconditionally suppresses\n // its native open-on-click while closed (see `AtomicGestureContext`'s\n // own doc for why). Every other atomic type there already reacts to a\n // plain click as normal (only a modifier-held one is suppressed), so\n // there's nothing further to do for them here.\n if (cellEditingCtx?.alwaysEdit) {\n const column = columnById.get(clickCell.columnId);\n if (column?.type === \"enum\" && !column.renderEditCell) {\n setPendingOpenEnumCell({ rowId: clickCell.rowId, columnId: clickCell.columnId });\n }\n return;\n }\n beginEditFromClick(clickCell);\n }\n window.addEventListener(\"mousemove\", handleMouseMove);\n window.addEventListener(\"mouseup\", handleMouseUp);\n return () => {\n window.removeEventListener(\"mousemove\", handleMouseMove);\n window.removeEventListener(\"mouseup\", handleMouseUp);\n if (dragRafRef.current !== null) cancelAnimationFrame(dragRafRef.current);\n dragRafRef.current = null;\n };\n }, [cellSelection.isDragging, cellSelection.updateDrag, cellSelection.endDrag]);\n\n // Same rAF-coalesced window-listener recipe as the range-select drag\n // above, for the fill-handle's own separate drag mode — mutually\n // exclusive with it (the handle's own `onMouseDown` in `SelectionOverlay`\n // stops propagation specifically so this doesn't also start a range-select\n // drag on the cell underneath it).\n const pendingFillCellRef = useRef<{ rowId: string; columnId: string } | null>(null);\n const fillRafRef = useRef<number | null>(null);\n useEffect(() => {\n if (!cellSelection.isFillDragging) return;\n function handleMouseMove(event: MouseEvent): void {\n const cellEl = (event.target as HTMLElement | null)?.closest<HTMLElement>(\"[data-cell-row]\");\n const rowId = cellEl?.dataset.cellRow;\n const columnId = cellEl?.dataset.cellCol;\n if (rowId === undefined || columnId === undefined) return;\n pendingFillCellRef.current = { rowId, columnId };\n if (fillRafRef.current !== null) return;\n fillRafRef.current = requestAnimationFrame(() => {\n fillRafRef.current = null;\n if (pendingFillCellRef.current) cellSelection.updateFillDrag(pendingFillCellRef.current);\n });\n }\n function handleMouseUp(): void {\n const result = cellSelection.endFillDrag();\n if (!result || !cellEditingCtx) return;\n const changes = computeFillChanges(\n result.sourceRange,\n result.finalRange,\n cellEditingRowIds,\n cellEditingColumnIds,\n visibleColumns,\n findRowById,\n );\n if (changes.length > 0) applyCellChanges(changes);\n }\n window.addEventListener(\"mousemove\", handleMouseMove);\n window.addEventListener(\"mouseup\", handleMouseUp);\n return () => {\n window.removeEventListener(\"mousemove\", handleMouseMove);\n window.removeEventListener(\"mouseup\", handleMouseUp);\n if (fillRafRef.current !== null) cancelAnimationFrame(fillRafRef.current);\n fillRafRef.current = null;\n };\n // Deliberately NOT depending on `cellEditingCtx`/`applyCellChanges`/\n // `findRowById`/etc. — none of those have a stable identity across\n // renders (they're plain functions, not memoized), so including them\n // would resubscribe this effect's window listeners on every render\n // while a drag is in progress, not just when the drag itself starts or\n // stops. `handleMouseUp` still reads the current values of each via\n // closure at the moment it actually fires (mouseup), which is fresh\n // enough for everything except one narrow edge case: toggling\n // `cellEditing.disabled` mid-drag doesn't abort an already-started\n // fill-drag's eventual commit. Accepted as a known limitation rather\n // than reintroducing per-render effect churn to close it.\n }, [cellSelection.isFillDragging, cellSelection.updateFillDrag, cellSelection.endFillDrag]);\n\n // A cell's editor is a real DOM node (an `<input>`/`<textarea>`/etc.) that\n // unmounts the instant `editingCell` clears — commit, cancel, or a blur\n // that reverted an invalid draft. Removing a FOCUSED node doesn't move\n // focus anywhere in particular (typically `document.body`), so without\n // this, every one of those endings would silently strand keyboard focus\n // outside the grid: F2/arrow-key navigation right after committing a cell\n // would have nothing to bubble through, since the scroll container\n // (`handleCellKeyDown`'s listener) is no longer in the event's path at\n // all. `justEndedEditingRef` tracks the transition (defined -> undefined)\n // rather than firing on mount, when nothing was ever focused here yet.\n const wasEditingCellRef = useRef<CellAddress | undefined>(undefined);\n useEffect(() => {\n const wasEditing = wasEditingCellRef.current !== undefined;\n wasEditingCellRef.current = cellEditingCtx?.editingCell;\n if (!wasEditing || cellEditingCtx?.editingCell) return;\n // Only when focus landed nowhere in particular (the browser's default\n // once a focused node is removed) — a blur caused by clicking some OTHER\n // focusable element on the page (not this cell) already sent focus\n // there before this effect runs; grabbing it back here would undo that\n // deliberate navigation away from the grid.\n if (document.activeElement === document.body) scrollRef.current?.focus();\n });\n\n function handleFillHandleMouseDown(): void {\n cellSelection.startFillDrag();\n }\n\n function handleCellMouseDown(event: ReactMouseEvent<HTMLDivElement>): void {\n if (!hasCellEditing) return;\n const cellEl = (event.target as HTMLElement).closest<HTMLElement>(\"[data-cell-row]\");\n const rowId = cellEl?.dataset.cellRow;\n const columnId = cellEl?.dataset.cellCol;\n if (rowId === undefined || columnId === undefined) return;\n cellSelection.startSelection({ rowId, columnId }, { extend: event.shiftKey });\n // Recorded for the window `mouseup` handler above: a plain click (this\n // exact cell, not shift-extended, never dragged elsewhere) either opens\n // its editor (click-to-edit mode's Excel convention) or, under\n // `alwaysEdit`, opens a built-in enum widget's own dropdown (see\n // `AtomicGestureContext`'s own doc for why that needs a signal from\n // here rather than just Radix's native click handling). Reset per\n // mousedown, not per click, so a genuine drag never leaves a stale\n // \"moved\" flag around to falsely block the *next* plain click.\n // `wasEditing` must be read NOW, before `scrollRef.current?.focus()`\n // below has a chance to blur (and thus close) this very cell — see\n // `clickCellRef`'s own doc for why that makes this the only correct\n // moment to snapshot it.\n const wasEditing = cellEditingCtx?.editingCell?.rowId === rowId && cellEditingCtx.editingCell.columnId === columnId;\n clickCellRef.current = { rowId, columnId, extend: event.shiftKey, wasEditing };\n dragMovedRef.current = false;\n // Under `alwaysEdit`, every editable cell already has its own always-\n // mounted editor (`AlwaysEditCell`) — there's no click-to-edit gesture to\n // open via `scrollRef.current?.focus()` below, which would only fight\n // the click's own natural job of focusing whichever control it landed\n // on. Selection (for copy/paste/fill-handle) still works exactly the\n // same either way, via `startSelection` above; `clickCellRef` set just\n // above is still needed here, for `handleMouseUp`'s own atomic-open\n // decision.\n if (cellEditingCtx?.alwaysEdit) return;\n // A click landing INSIDE the cell that's already being edited (e.g. to\n // reposition the text cursor, or drag-select some of its own text) must\n // be left alone entirely — moving focus below would blur-and-close it\n // via `renderCellModeCell`'s own `onBlur`, which is exactly the \"commit\n // this and move on\" gesture a genuinely different cell's click means,\n // not what a click *within* the same still-open editor means.\n if (wasEditing) return;\n // The scroll container itself (not any cell) holds keyboard focus for\n // arrow/Tab/Enter navigation — cells are plain `<td>`s, not focusable\n // controls, so a click needs to explicitly move focus here. Also what\n // closes/commits a currently-open editor elsewhere on the grid: moving\n // focus here blurs that editor's `<input>`, synchronously (before this\n // handler returns) triggering `renderCellModeCell`'s own `onBlur`\n // commit/cancel.\n scrollRef.current?.focus();\n }\n\n const CELL_NAV_KEYS: Record<string, \"up\" | \"down\" | \"left\" | \"right\"> = {\n ArrowUp: \"up\",\n ArrowDown: \"down\",\n ArrowLeft: \"left\",\n ArrowRight: \"right\",\n };\n\n /** Looks up the actual row/column for a selected cell address, for the begin-edit triggers below — `undefined` if either no longer exists (e.g. the row was removed from `data` since the selection was made). */\n function resolveSelectedCell(): { column: ColumnDef<TRow>; row: TRow } | undefined {\n const focus = cellSelection.selection?.focus;\n if (!focus) return undefined;\n const column = columnById.get(focus.columnId);\n const row = findRowById(focus.rowId);\n if (!column || row === undefined) return undefined;\n return { column, row };\n }\n\n function handleCellKeyDown(event: ReactKeyboardEvent<HTMLDivElement>): void {\n if (!hasCellEditing || !cellEditingCtx) return;\n // Under `alwaysEdit`, focus normally lives inside one of the always-\n // mounted per-cell `<input>`s, not the scroll container — this whole\n // custom arrow/Tab/Enter-moves-the-selection model would fight native\n // text-cursor movement and native Tab order between real form controls.\n // `AlwaysEditCell` owns its own Escape-to-revert locally; there's nothing\n // left here for it to do.\n if (cellEditingCtx.alwaysEdit) return;\n // While a cell is being edited, its own wrapper (`renderCellModeCell`)\n // owns Escape/Enter/Tab — this handler only ever sees them AFTER that\n // one has already committed (or reverted) and let the event bubble; see\n // that function's own doc on how the two compose. `editingCell` alone\n // can't tell the two \"still editing\" cases apart here: it's React state,\n // so a commit that just fired via `closeEditor()` hasn't cleared it yet\n // in THIS same synchronous dispatch — `consumeJustCommitted()` is the\n // one signal that's already up to date at this point.\n if (cellEditingCtx.editingCell && !cellEditingCtx.consumeJustCommitted()) return;\n\n const direction = CELL_NAV_KEYS[event.key];\n if (direction) {\n event.preventDefault();\n cellSelection.moveSelection(direction, { extend: event.shiftKey });\n return;\n }\n if (event.key === \"Tab\") {\n event.preventDefault();\n cellSelection.moveSelection(event.shiftKey ? \"left\" : \"right\");\n return;\n }\n if (event.key === \"Enter\") {\n event.preventDefault();\n cellSelection.moveSelection(\"down\");\n return;\n }\n\n // Begin-edit triggers: F2 (start from the current value), or typing any\n // single printable character directly — Excel's own \"just start typing\"\n // gesture — which REPLACES the cell's current value with what was typed\n // rather than appending to it.\n const isPrintableChar = event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey;\n if (event.key !== \"F2\" && !isPrintableChar) return;\n const target = resolveSelectedCell();\n if (!target || !isEditable(target.column, target.row)) return;\n event.preventDefault();\n cellEditingCtx.onBeginEdit(\n { rowId: cellEditingCtx.getRowId(target.row), columnId: target.column.id },\n isPrintableChar ? event.key : undefined,\n );\n }\n\n // Mostly a backstop now that a single plain click already opens a cell's\n // editor (see `handleCellMouseDown`'s own doc): still needed for the case\n // where the first click of the pair landed as a drag/extend (so its own\n // mouseup didn't open anything) but the second one didn't move — reuses\n // `beginEditFromClick`'s own \"already editing this exact cell\" guard so it\n // can't clobber a draft the first click already opened.\n function handleCellDoubleClick(event: ReactMouseEvent<HTMLDivElement>): void {\n if (!hasCellEditing || !cellEditingCtx || cellEditingCtx.alwaysEdit) return;\n const cellEl = (event.target as HTMLElement).closest<HTMLElement>(\"[data-cell-row]\");\n const rowId = cellEl?.dataset.cellRow;\n const columnId = cellEl?.dataset.cellCol;\n if (rowId === undefined || columnId === undefined) return;\n beginEditFromClick({ rowId, columnId });\n }\n\n function handleCopy(event: ReactClipboardEvent<HTMLDivElement>): void {\n // While a cell is actively being edited, a copy targets whatever text is\n // selected inside its own `<input>` — ordinary browser behavior, not a\n // range-copy of the whole selection. `isNativeTextFieldTarget` covers the\n // same case under `alwaysEdit`, where every editable cell's `<input>` is\n // always mounted (so there's no single `editingCell` to check) — an\n // atomic editor (enum/boolean/date) is a button/listbox, not a real text\n // field, so it's untouched by that check and still gets the range-copy.\n if (!hasCellEditing || !cellSelection.selection || cellEditingCtx?.editingCell || isNativeTextFieldTarget(event)) return;\n event.preventDefault();\n const text = rangeToTsv(cellSelection.selection, rows, cellEditingRowIds, visibleColumns, cellEditingColumnIds, getRowId);\n event.clipboardData.setData(\"text/plain\", text);\n }\n\n /** Builds one `CellChange` for `(row, col)` from `raw` pasted/filled text, or `undefined` to skip that cell — not editable, no longer exists, or `raw` doesn't coerce to that column's type (see `coerceValueForColumn`'s own doc). */\n function buildCellChange(row: number, col: number, raw: string): CellChange<TRow> | undefined {\n const rowId = cellEditingRowIds[row];\n const columnId = cellEditingColumnIds[col];\n if (rowId === undefined || columnId === undefined) return undefined;\n const column = columnById.get(columnId);\n const rowData = findRowById(rowId);\n if (!column || rowData === undefined || !isEditable(column, rowData)) return undefined;\n const coerced = coerceValueForColumn(column, raw);\n if (!coerced) return undefined;\n return { rowId, row: rowData, columnId, previousValue: getColumnValue(column, rowData), value: coerced.value };\n }\n\n function handlePaste(event: ReactClipboardEvent<HTMLDivElement>): void {\n // While a cell is actively being edited, a paste goes into its own\n // `<input>` — ordinary browser behavior, not a range-paste. Same\n // `alwaysEdit` nuance as `handleCopy` above.\n if (!hasCellEditing || !cellSelection.selection || cellEditingCtx?.editingCell || isNativeTextFieldTarget(event)) return;\n // Not gated on `text` being non-empty: the Clipboard API can't\n // distinguish \"nothing on the clipboard\" from \"an empty string was\n // copied\" (`getData` returns `\"\"` either way) — treating it as the\n // latter and clearing the selection matches how pasting a copied blank\n // cell behaves in a real spreadsheet, rather than silently doing nothing.\n const text = event.clipboardData.getData(\"text/plain\");\n event.preventDefault();\n const normalized = normalizeRange(cellSelection.selection, cellEditingRowIndex, buildIndexMap(cellEditingColumnIds));\n if (!normalized) return;\n const parsed = parseTsv(text);\n const changes: CellChange<TRow>[] = [];\n // A single copied value fills every selected cell (Excel's own\n // behavior for pasting one value onto a multi-cell selection); a real\n // multi-cell block instead anchors at the selection's top-left corner\n // and extends to match the pasted shape, clipped to grid bounds.\n if (parsed.length === 1 && parsed[0]?.length === 1) {\n const raw = parsed[0]![0]!;\n for (let r = normalized.rowStart; r <= normalized.rowEnd; r++) {\n for (let c = normalized.colStart; c <= normalized.colEnd; c++) {\n const change = buildCellChange(r, c, raw);\n if (change) changes.push(change);\n }\n }\n } else {\n for (let ri = 0; ri < parsed.length; ri++) {\n const line = parsed[ri]!;\n for (let ci = 0; ci < line.length; ci++) {\n const change = buildCellChange(normalized.rowStart + ri, normalized.colStart + ci, line[ci]!);\n if (change) changes.push(change);\n }\n }\n }\n if (changes.length > 0) applyCellChanges(changes);\n }\n\n function renderRow(\n row: (typeof tableRows)[number],\n measureRef?: (el: Element | null) => void,\n // Position within the flattened, virtualized render list (`flatItems`) —\n // distinct from `row.index` (this row's position within `tableRows`\n // alone) once `groupBy` interleaves header entries: `@tanstack/react-\n // virtual`'s `measureElement` reads the rendered node's `data-index`\n // attribute back to know which measurement slot it just measured (see\n // `<TreeDataGrid>`'s identical `data-index` doc for the same\n // requirement), so this MUST match the row's actual `flatItems` index\n // whenever virtualized. Defaults to `row.index`, which is already\n // correct for every non-grouped call site (virtualized or not) and for\n // every non-virtualized call site (the attribute is simply inert then).\n dataIndex: number = row.index,\n ): 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={dataIndex}>\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\n key={cell.id}\n style={cellProps?.style}\n className={cellProps?.className ?? BODY_TD_BASE_CLASS}\n // Only present under `cellEditing` — the anchor `handleCellMouseDown`/\n // the window drag listener/`SelectionOverlay` all look these up by\n // attribute selector; see `useCellSelection`'s own doc for why\n // row-id/column-id (not DOM index) is the addressing scheme.\n data-cell-row={hasCellEditing ? row.id : undefined}\n data-cell-col={hasCellEditing ? cell.column.id : undefined}\n >\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 `<td>` per visible column for the (non-sticky) per-group summary\n // row, each running that column's own `summary(rows)` (blank for a column\n // with none). `bgClassName` matches the label row above it\n // (`zebra ? \"bg-muted\" : \"bg-background\"`) so the two rows read as one\n // block. `showTotals`'s own `<tfoot>` needs different per-cell treatment\n // (sticky-to-bottom, always `bg-muted`) and builds its cells directly in\n // `renderTotalsFooter` instead of through this helper. Reuses\n // `bodyCellPropsByColumn` (pinning/resizing) rather than recomputing it,\n // same as every actual data-row cell.\n function renderGroupSummaryCells(rows: TRow[], bgClassName: string): ReactNode {\n return visibleColumns.map((column) => {\n const cellProps = bodyCellPropsByColumn.get(column.id);\n return (\n <td\n key={column.id}\n className={cn(cellProps?.className, bgClassName, \"font-medium\", alignClassName(column))}\n style={cellProps?.style}\n >\n {column.summary?.(rows)}\n </td>\n );\n });\n }\n\n // The leading detail/selection and trailing row-actions columns get one\n // blank filler cell each (matching `totalColumnCount`) rather than their\n // own pinned/interactive treatment -- there's no per-row checkbox/expand\n // affordance to show in a summary row. Unlike a real body row's own\n // pinned structural cells, these fillers aren't independently sticky-left/\n // right themselves; harmless while blank (nothing to misalign), but a\n // grid combining `enableColumnResizing`-style side pinning with a\n // `summary` column AND horizontal scroll could see this filler scroll out\n // ahead of a still-pinned data column. Documented as a known gap rather\n // than solved here, same tolerance the module's `groupBy`/`zebra` striping\n // gap already has (see AGENTS.md's \"Known limitations\").\n function summaryLeadingCellSpan(): number {\n return (showDetailColumn ? 1 : 0) + (showSelectionColumn ? 1 : 0);\n }\n\n function renderGroupSummaryRow(rows: TRow[]): ReactNode {\n const leadingSpan = summaryLeadingCellSpan();\n const bgClassName = zebra ? \"bg-muted\" : \"bg-background\";\n const fillerClass = cn(\"border-b border-border p-2\", bgClassName);\n return (\n <tr data-testid=\"group-summary-row\" className=\"divide-x divide-border\">\n {leadingSpan > 0 && <td colSpan={leadingSpan} className={fillerClass} />}\n {renderGroupSummaryCells(rows, bgClassName)}\n {showRowActionsColumn && <td className={fillerClass} aria-hidden />}\n </tr>\n );\n }\n\n // `showTotals`'s grand-total row -- sticky to the BOTTOM of the scroll\n // container (`bottom-0`, mirroring the header's own `top-0` stickiness),\n // so it stays visible while scrolling a tall grid instead of requiring a\n // scroll all the way down to see it, exactly the UX a \"totals row\"\n // exists for. `<tfoot>` renders after every `<tbody>` in source order\n // here, but HTML/CSS table layout always places a `<tfoot>` visually at\n // the bottom of the table regardless of where it appears in markup.\n function renderTotalsFooter(): ReactNode {\n if (!showTotals || !hasColumnSummary) return null;\n const summaryRows = tableRows.map((row) => row.original);\n const leadingSpan = summaryLeadingCellSpan();\n const fillerClass = \"sticky bottom-0 z-20 border-t border-border bg-muted p-2\";\n return (\n <tfoot>\n <tr data-testid=\"totals-row\" className=\"divide-x divide-border\">\n {leadingSpan > 0 && <td colSpan={leadingSpan} className={fillerClass} />}\n {visibleColumns.map((column) => {\n const pinnedProps = pinnedCellProps(column, \"header\");\n return (\n <td\n key={column.id}\n className={cn(\n \"border-t border-border p-2 font-medium\",\n alignClassName(column),\n pinnedProps.className,\n \"sticky bottom-0 z-20 bg-muted\",\n )}\n style={pinnedProps.style}\n >\n {column.summary?.(summaryRows)}\n </td>\n );\n })}\n {showRowActionsColumn && <td className={fillerClass} aria-hidden />}\n </tr>\n </tfoot>\n );\n }\n\n // Its own `<tbody>` (not nested inside anything else, matching every\n // per-row `<tbody>` from `renderRow`) so `measureRef`/`data-index` work the\n // exact same way a data row's does once virtualized -- shared by the\n // non-virtualized grouped path (`renderGroupedBucket`) and the virtualized\n // one (`renderFlatItem`), so a header's rendered markup can't drift\n // between the two.\n function renderGroupHeaderTbody(\n key: string,\n rows: TRow[],\n measureRef?: (el: Element | null) => void,\n dataIndex?: number,\n ): ReactNode {\n const expanded = isGroupExpanded(key);\n return (\n <tbody key={`group-${key}`} ref={measureRef} data-index={dataIndex}>\n <tr data-testid={`group-header-${key}`}>\n <td\n colSpan={totalColumnCount}\n className={cn(\"sticky z-20 border-b p-2 font-medium\", zebra ? \"bg-muted\" : \"bg-background\")}\n style={{ top: groupHeaderTop }}\n >\n <button\n type=\"button\"\n className=\"flex w-full items-center gap-1 text-left\"\n onClick={() => toggleGroupExpanded(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 ? renderGroupHeader(key, rows, expanded) : `${key} (${rows.length})`}\n </button>\n </td>\n </tr>\n {/* Not sticky, unlike the label row above -- it scrolls away with\n this group's own member rows once its `<tbody>` scrolls past the\n real `<thead>`, same as any other non-pinned content underneath a\n sticky element. Rendered regardless of `expanded`, which is the\n entire point: a collapsed group's per-column subtotal is often\n the only reason to collapse it in the first place. */}\n {hasColumnSummary && renderGroupSummaryRow(rows)}\n </tbody>\n );\n }\n\n // One `<tbody>` for the group-header row (via `renderGroupHeaderTbody`),\n // then each member row as its own sibling `<tbody>` (via `renderRow`) — a\n // `<tbody>` can't nest inside another, and a `<table>` happily holds any\n // number of them. Used only in the non-virtualized grouped path;\n // `renderFlatItem` below covers the same two shapes one flattened item at\n // a time once virtualized.\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 {renderGroupHeaderTbody(bucket.key, originalRows)}\n {expanded && bucket.items.map((row) => renderRow(row))}\n </Fragment>\n );\n }\n\n // Dispatches one `flatItems` entry to whichever of `renderGroupHeaderTbody`\n // / `renderRow` its `kind` needs — the virtualized render loop's per-item\n // callback, so it can stay a plain one-line `.map()` below.\n function renderFlatItem(\n item: FlatItem<TRow>,\n index: number,\n measureRef?: (el: Element | null) => void,\n ): ReactNode {\n if (item.kind === \"header\") return renderGroupHeaderTbody(item.key, item.rows, measureRef, index);\n return renderRow(item.row, measureRef, index);\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={cn(\"h-full overflow-auto rounded-md border\", hasCellEditing && \"relative\")}\n style={shouldVirtualize ? { maxHeight: virtualize?.maxBodyHeight ?? 480 } : undefined}\n // `tabIndex`/`onKeyDown` only under `cellEditing` — the scroll\n // container itself (not any individual cell, which is a plain\n // `<td>`, not a focusable control) holds keyboard focus for\n // arrow/Tab/Enter navigation between cells.\n tabIndex={hasCellEditing ? 0 : undefined}\n onMouseDown={hasCellEditing ? handleCellMouseDown : undefined}\n onDoubleClick={hasCellEditing ? handleCellDoubleClick : undefined}\n onKeyDown={hasCellEditing ? handleCellKeyDown : undefined}\n onCopy={hasCellEditing ? handleCopy : undefined}\n onPaste={hasCellEditing ? handlePaste : 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 const headerLabelId = `${header.id}-label`;\n return (\n <th\n key={header.id}\n rowSpan={rowSpan}\n style={cellProps?.style}\n className={cellProps?.className}\n aria-sort={sortable ? sortDirToAriaSort(sortEntry?.dir) : undefined}\n // Named via aria-labelledby pointing at the headerLabelId element below,\n // not name-from-content on the <th> itself: without it, the filter\n // trigger's own aria-label and the resize handle's `aria-label` (added\n // for keyboard support below) would otherwise get concatenated into this\n // header cell's computed accessible name too. Deliberately NOT a plain\n // `aria-label={column.header}` override, though — `column.header` is a\n // plain string required for things like width-estimation heuristics, but\n // a column can render genuinely richer content via `renderHeader` (e.g. a\n // two-line combined label for two related fields sharing one column) whose\n // full text belongs in the accessible name too, not just `column.header`.\n // aria-labelledby lets the labelled element's own name-from-content do\n // that correctly while still excluding the filter/resize controls, which\n // live outside it as siblings.\n aria-labelledby={headerLabelId}\n >\n <div className=\"flex items-center gap-1\">\n {sortable ? (\n <button\n type=\"button\"\n id={headerLabelId}\n data-testid={`sort-button-${column.id}`}\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 id={headerLabelId} className=\"flex items-center gap-1 px-1\">\n {headerContent}\n </div>\n )}\n {filterable && (\n <Popover>\n <PopoverTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n data-testid={`filter-trigger-${column.id}`}\n aria-label={`Filter ${column.header}`}\n >\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 role=\"separator\"\n aria-orientation=\"vertical\"\n aria-label={`Resize ${column.header} column`}\n tabIndex={0}\n onMouseDown={header.getResizeHandler()}\n onTouchStart={header.getResizeHandler()}\n onKeyDown={(event) => {\n if (event.key !== \"ArrowLeft\" && event.key !== \"ArrowRight\") return;\n event.preventDefault();\n const delta = event.key === \"ArrowRight\" ? 10 : -10;\n const nextSize = Math.max(MIN_COLUMN_SIZE, columnSize(column.id) + delta);\n updateColumnSizing({ ...columnSizing, [column.id]: nextSize });\n }}\n className=\"absolute right-0 top-0 h-full w-1 cursor-col-resize touch-none select-none hover:bg-accent focus-visible:bg-accent focus-visible:outline-none\"\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 renderFlatItem(flatItems[virtualItem.index]!, virtualItem.index, (el) =>\n virtualizer.measureElement(el),\n ),\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 {renderTotalsFooter()}\n </table>\n {hasCellEditing && (\n <SelectionOverlay\n containerRef={scrollRef}\n range={cellSelection.effectiveRange}\n fillPreviewRange={cellSelection.fillPreviewRange}\n onFillHandleMouseDown={\n cellSelection.selection && !cellSelection.isDragging && !cellEditingCtx?.editingCell\n ? handleFillHandleMouseDown\n : undefined\n }\n />\n )}\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","import {\n useEffect,\n useState,\n type KeyboardEvent as ReactKeyboardEvent,\n type MouseEvent as ReactMouseEvent,\n type PointerEvent as ReactPointerEvent,\n type ReactNode,\n} from \"react\";\nimport { defaultFormat } from \"../column/format\";\nimport type { ColumnDef } from \"../column/types\";\nimport { isEditable } from \"../column/types\";\nimport { EnumEditor } from \"../edit/EnumEditor\";\nimport { renderDefaultEditWidget } from \"../edit/registry\";\nimport { isAtomicEditorType } from \"./renderCellModeCell\";\nimport type { CellEditingCellContext } from \"./useCellEditingState\";\n\n/**\n * Bridges an atomic (enum/boolean/date/datetime) `AlwaysEditCell`'s own\n * gesture handling back into `<DataGrid>`'s shared cell-selection state —\n * needed only because these cells' widgets are permanently-mounted, LIVE\n * controls (a Radix `<Select>` trigger, a checkbox), unlike a non-atomic\n * editor's plain `<input>`, so a mouse gesture that's supposed to just\n * select the cell (shift+click, or a drag) can otherwise be swallowed or\n * misinterpreted by the widget's own native mouse handling before\n * `<DataGrid>`'s own delegated `onMouseDown` ever sees it. See\n * `AlwaysEditCell`'s own capture-phase handlers for the mechanics.\n */\nexport interface AtomicGestureContext {\n /**\n * True for exactly the one built-in enum (`<Select>`) cell that should\n * open its dropdown now. Set by `<DataGrid>`'s own mouseup handler the\n * instant a mousedown/mouseup pair on it resolves to a plain,\n * non-extended, non-dragged click — the only case its native\n * open-on-pointerdown/click is supposed to happen at all.\n *\n * This exists because `AlwaysEditCell` unconditionally suppresses that\n * native behavior while a built-in enum widget is closed (see its own\n * `onPointerDownCapture` doc): unlike the other atomic types, a `<Select>`\n * also supports a native \"press, drag to an option, release to pick it\"\n * gesture — letting it open immediately on ANY mousedown (as Radix does\n * by default) means a drag that merely passes through its now-open\n * popover can silently commit the wrong option as this cell's value,\n * not just steal the range-select gesture. A plain click needs this\n * signal as its own way back in once that's blocked.\n */\n shouldOpenEnum: (rowId: string, columnId: string) => boolean;\n /**\n * Registers this gesture's selection directly. Required because\n * `AlwaysEditCell` suppresses a widget's native pointerdown handling by\n * calling `preventDefault()` on it — and per the Pointer Events spec,\n * that ALSO cancels the browser's own compatibility `mousedown` event for\n * this same gesture, so `<DataGrid>`'s own delegated `onMouseDown`\n * handler (which is what normally calls this) never runs for it at all.\n */\n registerSelection: (rowId: string, columnId: string, extend: boolean) => void;\n}\n\n/**\n * One in-progress, uncommitted edit this component started itself.\n * `baseline` is `resolvedValue` at the moment the draft was last updated —\n * the same freshness convention `useCellEditingState`'s own `CellOverride`\n * uses, applied locally: as long as `resolvedValue` still equals `baseline`,\n * nothing else has touched this cell since, so `value` is still the right\n * thing to show/commit. The instant `resolvedValue` diverges from\n * `baseline` (a fill-drag or paste landed on this exact cell while a draft\n * was in progress), the draft is stale and must lose to that newer value —\n * committing it anyway would silently revert whatever just landed.\n */\ninterface LocalDraft {\n value: unknown;\n baseline: unknown;\n}\n\n/**\n * Renders one cell under `cellEditing.alwaysEdit` mode: every editable cell\n * gets its own permanently-mounted editor, all simultaneously — unlike\n * `renderCellModeCell`'s single shared `editingCell` slot (at most one editor\n * open at a time), this component owns its OWN local draft state, since with\n * every cell independently editable there's no single \"the\" cell to hang a\n * shared draft off of. A buffered (string/number/currency) editor still only\n * commits on blur/Enter/Tab, same as click-to-edit mode — this only changes\n * WHEN the editor first appears (always, not on click/double-click/F2/typed\n * character), not how a buffered edit gets committed.\n */\nexport function AlwaysEditCell<TRow>(props: {\n column: ColumnDef<TRow>;\n row: TRow;\n rawValue: unknown;\n ctx: CellEditingCellContext<TRow>;\n /** Only meaningful for an atomic column type — see `AtomicGestureContext`'s own doc. */\n atomicGesture?: AtomicGestureContext;\n}): ReactNode {\n const { column, row, rawValue, ctx, atomicGesture } = props;\n const rowId = ctx.getRowId(row);\n const resolvedValue = ctx.resolveValue(rowId, column.id, rawValue);\n // Called unconditionally, before the `isEditable` early return below —\n // `column.editable` can be a per-row predicate (see `isEditable` in\n // `column/types.ts`), so whether this cell renders an editor at all can\n // differ between renders of the very same component instance; a hook\n // can't sit after a conditional return that's conditional on that.\n const [draft, setDraft] = useState<LocalDraft | undefined>(undefined);\n // The built-in enum widget's own open/closed state, owned here (not by\n // `<Select>` itself) specifically so a mousedown/click can be\n // unconditionally suppressed while it's closed — see `AtomicGestureContext`'s\n // own doc for why. Irrelevant (never read) for every other column type.\n const [enumOpen, setEnumOpen] = useState(false);\n const isBuiltinEnum = column.type === \"enum\" && !column.renderEditCell;\n const shouldOpenEnumNow = isBuiltinEnum && (atomicGesture?.shouldOpenEnum(rowId, column.id) ?? false);\n // Opens it exactly once per `<DataGrid>`-confirmed plain click — this\n // effect only re-runs when `shouldOpenEnumNow` itself flips, not on every\n // render, so it doesn't fight a subsequent manual close (clicking the\n // trigger again, picking an option, Escape, clicking away all update\n // `enumOpen` via `onOpenChange` below, unaffected by this).\n useEffect(() => {\n if (shouldOpenEnumNow) setEnumOpen(true);\n }, [shouldOpenEnumNow]);\n\n if (!isEditable(column, row)) {\n return (\n <span data-testid={`cell-${rowId}-${column.id}`} className=\"block w-full\">\n {column.cell ? column.cell(resolvedValue, row) : defaultFormat(column, resolvedValue)}\n </span>\n );\n }\n\n const isAtomic = isAtomicEditorType(column.type);\n const error = ctx.getError(rowId, column.id);\n // Fresh only while `resolvedValue` still matches what it was when `draft`\n // was last updated — see `LocalDraft`'s own doc. A stale draft is treated\n // as if there were none: `currentValue` falls through to `resolvedValue`,\n // and the commit paths below refuse to commit it. Left in state rather\n // than cleared outright — recomputing this each render is enough to keep\n // display/commit correct without an extra render just to null it out.\n const draftIsFresh = draft !== undefined && resolvedValue === draft.baseline;\n const currentValue = draftIsFresh ? draft!.value : resolvedValue;\n\n function validate(candidate: unknown): string | undefined {\n return column.validateEdit?.(candidate, row);\n }\n\n function commit(value: unknown): void {\n ctx.applyChange({ rowId, row, columnId: column.id, previousValue: resolvedValue, value });\n setDraft(undefined);\n }\n\n const onChange = (next: unknown): void => {\n const message = validate(next);\n ctx.onSetError(rowId, column.id, message);\n if (isAtomic && !message) {\n commit(next);\n return;\n }\n setDraft({ value: next, baseline: resolvedValue });\n };\n\n function handleKeyDown(event: ReactKeyboardEvent): void {\n if (event.key === \"Escape\") {\n event.stopPropagation();\n ctx.onSetError(rowId, column.id, undefined);\n setDraft(undefined);\n return;\n }\n const isMultilineNewline = column.type === \"string\" && column.multiline && event.key === \"Enter\" && event.shiftKey;\n if (isMultilineNewline) return;\n if (isAtomic || (event.key !== \"Enter\" && event.key !== \"Tab\")) return;\n if (!draftIsFresh) return; // nothing (still) pending — no-op close, matches renderCellModeCell's own onCommitEdit convention\n const message = validate(draft!.value);\n if (message) {\n event.stopPropagation();\n return;\n }\n commit(draft!.value);\n }\n\n function handleBlur(): void {\n if (isAtomic || !draftIsFresh) return;\n if (validate(draft!.value)) setDraft(undefined);\n else commit(draft!.value);\n }\n\n // `stopPropagation()` alone does NOT stop Radix's own handlers on the\n // widget from still firing here — React dispatches capture- and\n // bubble-phase handlers off separate root-level native listeners, so a\n // synthetic `stopPropagation()` called from a CAPTURE handler only halts\n // React's own capture-side traversal, not the independent bubble-side one\n // that would still reach the widget's own bubble-phase prop. Only\n // stopping the underlying native event itself reliably prevents that\n // (verified against a live Radix `<Select>` trigger). Shared by both\n // capture handlers below. Returns `true` when it actually suppressed\n // anything, so callers know whether to also call `registerSelection`.\n function suppressNative(event: ReactPointerEvent | ReactMouseEvent): boolean {\n event.stopPropagation();\n event.nativeEvent.stopImmediatePropagation();\n return true;\n }\n\n // Modifier-only interception, for atomic widgets OTHER than the built-in\n // enum editor (which gets the stronger, always-while-closed interception\n // below instead): a shift/ctrl/meta-held click meant to just select the\n // cell would otherwise still reach the widget's own native click handling\n // (a checkbox's toggle, or focusing a native date input) — Radix/native\n // controls don't know to treat those modifiers as \"don't act, just\n // select\" the way this grid does. These don't cover nearby rows with a\n // draggable popover the way `<Select>` does, so a PLAIN drag starting on\n // one of them already resolves correctly with no interception needed at\n // all — nothing here ever eclipses `event.target` during it.\n function handleModifierCapture(event: ReactPointerEvent | ReactMouseEvent): boolean {\n if (!isAtomic || isBuiltinEnum) return false;\n if (!(event.shiftKey || event.ctrlKey || event.metaKey)) return false;\n return suppressNative(event);\n }\n\n // Unconditional (any gesture, not just modifier-held) interception for a\n // CLOSED built-in enum editor — see `AtomicGestureContext.shouldOpenEnum`'s\n // own doc for why a plain click can't just be let through immediately the\n // way it can for the other atomic types above: a `<Select>` that's\n // allowed to open on ANY plain mousedown also lets a drag starting on it\n // silently commit whichever option the drag happens to end up over.\n // Once open, this stops intercepting entirely — closing it again (click\n // the trigger, pick an option, click away, Escape) is left to Radix's own\n // normal behavior, via `onOpenChange={setEnumOpen}` below.\n function handleEnumOpenCapture(event: ReactPointerEvent | ReactMouseEvent): boolean {\n if (!isBuiltinEnum || enumOpen) return false;\n return suppressNative(event);\n }\n\n // Pointerdown, not just click: this is also where a suppressed gesture's\n // selection gets registered (see `AtomicGestureContext.registerSelection`'s\n // own doc for why `<DataGrid>`'s own mousedown handler can't be trusted to\n // still run on its own here) — pointerdown, rather than the later click,\n // since a real drag never produces a `click` at all (mouseup lands on a\n // different cell than mousedown), but still needs its start cell\n // registered as the selection's anchor.\n function handlePointerDownCapture(event: ReactPointerEvent): void {\n const suppressed = handleModifierCapture(event) || handleEnumOpenCapture(event);\n if (suppressed) atomicGesture?.registerSelection(rowId, column.id, event.shiftKey);\n }\n\n // A SEPARATE interception from the pointerdown one above, and just as\n // required: Radix's `<Select>` trigger also opens from its own `onClick`\n // handler, but only when `pointerType !== \"mouse\"` (its own touch\n // fallback) — and that internal pointer-type tracking is itself only\n // ever updated inside the very `onPointerDown` handler this file\n // suppresses, so once that's blocked, the trigger's `pointerType` gets\n // stuck at Radix's own initial default (\"touch\"), and its `onClick`\n // fallback opens the dropdown anyway on the ensuing native `click`\n // (mousedown+mouseup on the same target) if this isn't ALSO suppressed.\n // No `registerSelection` here — `handlePointerDownCapture` above already\n // covers it, and click fires (if at all) strictly after pointerdown for\n // the same gesture.\n function handleClickCapture(event: ReactMouseEvent): void {\n handleModifierCapture(event);\n handleEnumOpenCapture(event);\n }\n\n return (\n <fieldset\n disabled={ctx.disabled}\n onKeyDown={handleKeyDown}\n onBlur={handleBlur}\n onPointerDownCapture={handlePointerDownCapture}\n onClickCapture={handleClickCapture}\n className=\"contents\"\n >\n {column.type === \"enum\" && !column.renderEditCell ? (\n <EnumEditor\n column={column}\n rowId={rowId}\n value={currentValue}\n onChange={onChange}\n error={error}\n autoFocus={false}\n open={enumOpen}\n onOpenChange={setEnumOpen}\n />\n ) : column.renderEditCell ? (\n column.renderEditCell(currentValue, row, onChange, error, false)\n ) : (\n renderDefaultEditWidget(column, rowId, currentValue, onChange, error, false)\n )}\n </fieldset>\n );\n}\n","import { useCallback, useState } from \"react\";\n\nexport interface GroupExpansionController {\n /** The resolved (controlled-or-internal) expand state, keyed by group key. */\n expandedGroups: Record<string, boolean>;\n /** A key absent from `expandedGroups` (a group never toggled since it was first seen) falls back to `defaultGroupsExpanded`, not `false`. */\n isGroupExpanded: (key: string) => boolean;\n toggleGroupExpanded: (key: string) => void;\n}\n\n/**\n * Same controlled/uncontrolled pattern as `<DataGrid>`'s/`<TreeDataGrid>`'s\n * own `selectedIds`/`columnSizing`: both `controlledExpandedGroups` and\n * `onExpandedGroupsChange` must be supplied together to actually control\n * this state — omit both to let this hook own it internally. Shared between\n * the two grids' `groupBy` support so a fix to the fallback/toggle logic\n * never has to be made twice.\n *\n * `isGroupExpanded`/`toggleGroupExpanded` are stable (`useCallback`-wrapped)\n * across renders as long as `expandedGroups`/`defaultGroupsExpanded`/\n * `onExpandedGroupsChange` don't change — callers can safely list them in a\n * `useMemo`/`useEffect` dependency array without defeating memoization.\n */\nexport function useGroupExpansion(\n defaultGroupsExpanded: boolean,\n controlledExpandedGroups: Record<string, boolean> | undefined,\n onExpandedGroupsChange: ((expanded: Record<string, boolean>) => void) | undefined,\n): GroupExpansionController {\n const [internalExpandedGroups, setInternalExpandedGroups] = useState<Record<string, boolean>>({});\n const expandedGroups = controlledExpandedGroups ?? internalExpandedGroups;\n\n const updateExpandedGroups = useCallback(\n (next: Record<string, boolean>): void => {\n setInternalExpandedGroups(next);\n onExpandedGroupsChange?.(next);\n },\n [onExpandedGroupsChange],\n );\n\n const isGroupExpanded = useCallback(\n (key: string): boolean => expandedGroups[key] ?? defaultGroupsExpanded,\n [expandedGroups, defaultGroupsExpanded],\n );\n\n const toggleGroupExpanded = useCallback(\n (key: string): void => {\n updateExpandedGroups({ ...expandedGroups, [key]: !isGroupExpanded(key) });\n },\n [expandedGroups, isGroupExpanded, updateExpandedGroups],\n );\n\n return { expandedGroups, isGroupExpanded, toggleGroupExpanded };\n}\n","import type { RefObject } from \"react\";\nimport { useLayoutEffect, useRef, useState } from \"react\";\n\nexport interface StickyGroupHeaderTop {\n /** Attach to the grid's own `<thead>`. */\n theadRef: RefObject<HTMLTableSectionElement>;\n /** The measured `<thead>` height in pixels — `0` until first measured, or whenever `enabled` is false. */\n groupHeaderTop: number;\n}\n\n/**\n * Measures the real header height so a sticky group-header row can sit\n * exactly below it (its own `top` style) while member rows scroll past\n * underneath. `ResizeObserver` rather than a one-off measurement since\n * header height can change after mount (e.g. a column's header content\n * wrapping to a second line, or a resizable column changing the header\n * row's layout). Shared between `<DataGrid>` and `<TreeDataGrid>`'s own\n * `groupBy` support.\n */\nexport function useStickyGroupHeaderTop(enabled: boolean): StickyGroupHeaderTop {\n const theadRef = useRef<HTMLTableSectionElement>(null);\n const [groupHeaderTop, setGroupHeaderTop] = useState(0);\n useLayoutEffect(() => {\n if (!enabled) 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 }, [enabled]);\n return { theadRef, groupHeaderTop };\n}\n","import { useMemo } from \"react\";\nimport type { ColumnDef } from \"../column/types\";\nimport type { ColumnVisibility } from \"../column-selector/types\";\nimport { isColumnVisible } from \"../column-selector/visibility\";\n\n/**\n * Single source of truth for \"which columns actually render\" — filters\n * `columns` down to whatever `columnVisibility` marks visible, or returns\n * `columns` unchanged when `columnVisibility` is omitted. Shared between\n * `<DataGrid>` and `<TreeDataGrid>` so both read `columnVisibility` (e.g.\n * driven by a `<ColumnSelector>`) the same way.\n */\nexport function useVisibleColumns<TRow>(\n columns: ColumnDef<TRow>[],\n columnVisibility: ColumnVisibility | undefined,\n): ColumnDef<TRow>[] {\n return useMemo(\n () =>\n columnVisibility ? columns.filter((column) => isColumnVisible(columnVisibility, column.id)) : columns,\n [columns, columnVisibility],\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 `dataSource.onStateChange` doesn't exist and\n * is never called; the grid re-filters/re-sorts the in-memory array itself\n * instead. `onGridStateChange` (see its own doc on `DataGridProps`) gets the\n * same notifications regardless of mode, for callers that just want to\n * observe — e.g. a `\"client\"`-mode grid with no other way to know its\n * current filter/sort/page from outside.\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 *\n * `showPagination`, when `false` and the caller did NOT explicitly provide\n * `initialState.pageSize`, seeds `pageSize` as effectively unbounded instead\n * of `DEFAULT_STATE`'s 20 — \"hide the pagination controls\" means \"no\n * pagination\" by default, matching `<DataGrid>`'s `showPagination` doc. A\n * caller who explicitly sets `initialState.pageSize` (deliberate fixed-size\n * chunking with no UI, alongside `showPagination: false`) still gets exactly\n * that value; this only changes what happens when they didn't ask for a\n * particular page size at all. Has no effect once `externalState` is in\n * play — a controlled `GridState` is always fully caller-specified.\n */\nexport function useGridState<TRow>(\n dataSource: DataSource<TRow>,\n initialState?: Partial<GridState>,\n externalState?: GridState,\n showPagination = true,\n onGridStateChange?: (state: GridState) => void,\n): GridStateController {\n const [state, setState] = useState<GridState>(() => {\n if (externalState) return externalState;\n const pageSize =\n initialState?.pageSize ?? (showPagination ? DEFAULT_STATE.pageSize : Number.MAX_SAFE_INTEGER);\n return { ...DEFAULT_STATE, ...initialState, pageSize };\n });\n\n const notifyRaw = useCallback(\n (next: GridState) => {\n if (dataSource.mode === \"server\") dataSource.onStateChange(next);\n onGridStateChange?.(next);\n },\n [dataSource, onGridStateChange],\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 { Fragment, 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 { groupRows } from \"../grid/groupRows\";\nimport { useGroupExpansion } from \"../hooks/useGroupExpansion\";\nimport { useStickyGroupHeaderTop } from \"../hooks/useStickyGroupHeaderTop\";\nimport { useVisibleColumns } from \"../hooks/useVisibleColumns\";\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 loading = false,\n treeColumnId,\n columnVisibility,\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 testId,\n selectedIds: controlledSelectedIds,\n onSelectedIdsChange,\n getRowSelectionState,\n isRowSelectionDisabled,\n groupBy,\n renderGroupHeader,\n defaultGroupsExpanded = true,\n expandedGroups: controlledExpandedGroups,\n onExpandedGroupsChange,\n zebra = true,\n showTotals = false,\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 // Single source of truth for \"which columns actually render\", same\n // convention as `<DataGrid>`'s own `visibleColumns` — every other\n // representation below (the header/body column loops, `bodyTdClassByColumn`,\n // `totalColumnCount`) derives from this, not from `columns` directly.\n const visibleColumns = useVisibleColumns(columns, columnVisibility);\n\n // Needed before `groupedRender` below — which buckets count as \"currently\n // visible\" depends on which groups are expanded. Shared with `<DataGrid>`'s\n // own `groupBy` support via `useGroupExpansion`.\n const { isGroupExpanded, toggleGroupExpanded } = useGroupExpansion(\n defaultGroupsExpanded,\n controlledExpandedGroups,\n onExpandedGroupsChange,\n );\n\n // Grouping only ever buckets *root*-level rows (see `groupBy`'s own doc) —\n // each bucket's own rows are still flattened depth-first via the same\n // `flattenTree` every ungrouped render uses, so expand/collapse and lazy\n // loading behave identically either way. `flatRows` itself is skipped\n // entirely when grouped (nothing reads it in that mode, and flattening the\n // whole tree in original — not bucketed — order would be wasted work).\n const flatRows = useMemo(\n () => (groupBy ? [] : flattenTree(data, accessors, expanded, childrenMap)),\n [groupBy, data, accessors, expanded, childrenMap],\n );\n const groupedBuckets = useMemo(\n () =>\n groupBy\n ? groupRows(data, groupBy).map((bucket) => ({\n key: bucket.key,\n roots: bucket.items,\n flatRows: flattenTree(bucket.items, accessors, expanded, childrenMap),\n }))\n : undefined,\n [groupBy, data, accessors, expanded, childrenMap],\n );\n // Rows belonging to a *collapsed* group are never rendered, so — unlike\n // `<DataGrid>`'s page-scoped \"select all on this page\" (which has no\n // per-row hide/show mechanic besides pagination) — they must not count as\n // \"currently visible\" either: `<TreeDataGrid>`'s own selection already\n // excludes a collapsed *tree node*'s descendants from `flatRows`, so a\n // collapsed *group* follows the same rule for consistency. This also gives\n // each rendered row a global (not per-bucket-reset) index via\n // `startIndexByBucket` below, matching what `getRowProps`'s own `index`\n // param documents (\"position among the currently visible rows\").\n const groupedRender = useMemo(() => {\n if (!groupedBuckets) return undefined;\n const startIndexByBucket = new Map<string, number>();\n const rows: FlatTreeRow<TRow>[] = [];\n for (const bucket of groupedBuckets) {\n if (!isGroupExpanded(bucket.key)) continue;\n startIndexByBucket.set(bucket.key, rows.length);\n rows.push(...bucket.flatRows);\n }\n return { startIndexByBucket, rows };\n }, [groupedBuckets, isGroupExpanded]);\n // Flattened across every *expanded* group when grouped — selection/\"select\n // all visible\" operate over the whole currently-rendered set regardless of\n // which bucket a row falls in.\n const allFlatRows = groupedRender ? groupedRender.rows : flatRows;\n\n const treeColId = treeColumnId ?? columns[0]?.id;\n const showRowActionsColumn = Boolean(rowActions?.length);\n const showSelectionColumn = controlledSelectedIds !== undefined;\n const totalColumnCount =\n visibleColumns.length + (showRowActionsColumn ? 1 : 0) + (showSelectionColumn ? 1 : 0);\n\n // A group-header row sticks right below the real `<thead>` while its\n // members scroll past. Shared with `<DataGrid>`'s own `groupBy` support\n // via `useStickyGroupHeaderTop`.\n const { theadRef, groupHeaderTop } = useStickyGroupHeaderTop(Boolean(groupBy));\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 () => allFlatRows.filter((flatRow) => !(isRowSelectionDisabled?.(flatRow.row) ?? false)),\n [allFlatRows, 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 visibleColumns) map.set(column.id, `p-2 ${alignClassName(column)}`);\n return map;\n }, [visibleColumns]);\n\n // Same \"entirely opt-in\" convention `<DataGrid>`'s own `hasColumnSummary`\n // has — the per-group summary row below and the `showTotals` `<tfoot>`\n // both only render once at least one visible column sets `summary` (see\n // `BaseColumn.summary`'s own doc).\n const hasColumnSummary = visibleColumns.some((column) => column.summary);\n\n // One `<td>` per visible column for the per-group summary row, each\n // running that column's own `summary(rows)` — `rows` here is always a\n // bucket's own *root* rows (`bucket.roots`), never the flattened tree (see\n // `groupBy`'s own doc for why: a child's value is usually already\n // reflected in its parent's).\n function renderGroupSummaryCells(rows: TRow[]): ReactNode {\n return visibleColumns.map((column) => (\n <td key={column.id} className={cn(bodyTdClassByColumn.get(column.id), \"font-medium\")}>\n {column.summary?.(rows)}\n </td>\n ));\n }\n\n const scrollRef = useRef<HTMLDivElement>(null);\n // Interleaving synthetic group-header rows needs a flattened index space\n // to virtualize correctly — out of scope for this pass, so `groupBy`\n // forces virtualization off rather than silently mis-rendering, same as\n // `<DataGrid>`'s own grouped mode.\n const shouldVirtualize = !groupBy && 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 {visibleColumns.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 data-testid={testId}\n // Scrollability must not depend on whether the virtualizer is\n // active: `shouldVirtualize` only decides whether rows are windowed\n // (an unrelated performance optimization gated on `virtualizeThreshold`,\n // and always off for `groupBy`, see its own comment above) -- a tree\n // whose row count stays under that threshold, or that groups its\n // rows, can still overflow `maxBodyHeight` and needs the exact same\n // scroll box to reach the rest of it. Previously this div only got\n // its overflow-y-auto/maxHeight styling once virtualization kicked\n // in, so a non-virtualized (or grouped) tree that outgrew its box\n // just rendered past it with no way to scroll to what was clipped.\n className=\"overflow-y-auto\"\n style={{ maxHeight: maxBodyHeight }}\n >\n <table className=\"w-full border-collapse text-sm\">\n <thead ref={theadRef}>\n <tr className=\"divide-x divide-border\">\n {showSelectionColumn && (\n <th\n className=\"sticky top-0 z-20 border-b border-border bg-muted p-1\"\n style={{ width: SELECTION_COLUMN_WIDTH }}\n >\n <Checkbox\n aria-label=\"Select all visible rows\"\n checked={allVisibleSelected}\n onCheckedChange={toggleAllVisibleSelected}\n />\n </th>\n )}\n {visibleColumns.map((column) => (\n <th\n key={column.id}\n style={column.width ? { width: column.width } : undefined}\n className={cn(\n \"sticky top-0 z-20 border-b border-border bg-muted p-2 font-medium\",\n alignClassName(column),\n )}\n >\n {column.header}\n </th>\n ))}\n {showRowActionsColumn && (\n <th className=\"sticky top-0 z-20 border-b border-border bg-muted p-2\" aria-label=\"Row actions\" />\n )}\n </tr>\n </thead>\n <tbody>\n {data.length === 0 ? (\n <tr>\n <td colSpan={totalColumnCount} className=\"p-4 text-center text-muted-foreground\">\n {loading ? (\n <span className=\"inline-flex items-center gap-2\">\n <Loader2 className=\"h-4 w-4 animate-spin\" aria-hidden=\"true\" />\n Loading...\n </span>\n ) : (\n \"No results.\"\n )}\n </td>\n </tr>\n ) : groupedBuckets ? (\n groupedBuckets.map((bucket) => {\n const groupExpanded = isGroupExpanded(bucket.key);\n return (\n <Fragment key={`group-${bucket.key}`}>\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={groupExpanded}\n >\n <ChevronRight\n className={`h-4 w-4 shrink-0 transition-transform${groupExpanded ? \" rotate-90\" : \"\"}`}\n aria-hidden\n />\n {renderGroupHeader\n ? renderGroupHeader(bucket.key, bucket.roots, groupExpanded)\n : `${bucket.key} (${bucket.roots.length})`}\n </button>\n </td>\n </tr>\n {/* Not sticky, unlike the label row above — scrolls away\n with this group's own rows. Rendered regardless of\n `groupExpanded`, same as `<DataGrid>`'s own group\n summary row: a collapsed group's subtotal is often\n the whole reason to collapse it. */}\n {hasColumnSummary && (\n <tr\n data-testid={`group-summary-${bucket.key}`}\n className=\"border-b border-border divide-x divide-border\"\n >\n {showSelectionColumn && (\n <td\n className={cn(\"p-2\", zebra ? \"bg-muted\" : \"bg-background\")}\n style={{ width: SELECTION_COLUMN_WIDTH }}\n />\n )}\n {renderGroupSummaryCells(bucket.roots)}\n {showRowActionsColumn && (\n <td className={cn(\"p-2\", zebra ? \"bg-muted\" : \"bg-background\")} aria-hidden />\n )}\n </tr>\n )}\n {groupExpanded &&\n (() => {\n const startIndex = groupedRender?.startIndexByBucket.get(bucket.key) ?? 0;\n return bucket.flatRows.map((flatRow, i) => renderRow(flatRow, startIndex + i));\n })()}\n </Fragment>\n );\n })\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 {showTotals && hasColumnSummary && (\n // Sticky to the BOTTOM of the scroll container, mirroring the\n // `<thead>`'s own `top-0` stickiness — see `<DataGrid>`'s own\n // `renderTotalsFooter` for the full reasoning. Summarizes `data`\n // (the root-level rows), not `flatRows`/`allFlatRows` — same\n // \"roots only\" scope `groupBy` itself uses, and for the same\n // reason (a child's value is usually already folded into its\n // parent's).\n <tfoot>\n <tr data-testid=\"totals-row\" className=\"divide-x divide-border\">\n {showSelectionColumn && (\n <td\n className=\"sticky bottom-0 z-20 border-t border-border bg-muted p-2\"\n style={{ width: SELECTION_COLUMN_WIDTH }}\n />\n )}\n {visibleColumns.map((column) => (\n <td\n key={column.id}\n style={column.width ? { width: column.width } : undefined}\n className={cn(\n \"sticky bottom-0 z-20 border-t border-border bg-muted p-2 font-medium\",\n alignClassName(column),\n )}\n >\n {column.summary?.(data)}\n </td>\n ))}\n {showRowActionsColumn && (\n <td className=\"sticky bottom-0 z-20 border-t border-border bg-muted p-2\" aria-hidden />\n )}\n </tr>\n </tfoot>\n )}\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","useEffect","useId","useState","jsx","jsxs","DEFAULT_OPERATOR","useState","useId","useEffect","FunnelIcon","jsx","jsx","jsx","jsxs","parseISO","jsx","jsxs","parseISO","jsx","jsxs","useRef","forwardRef","jsx","forwardRef","jsx","jsxs","useRef","useEffect","useRef","useState","jsx","jsxs","useState","useRef","useEffect","jsx","jsxs","jsx","useEffect","useRef","useState","useState","useEffect","useRef","jsx","value","jsx","jsxs","useCallback","useMemo","useRef","useState","useState","useRef","useMemo","useCallback","useRef","useState","next","jsx","value","useLayoutEffect","useRef","useState","Fragment","jsx","jsxs","useEffect","forwardRef","jsx","jsxs","forwardRef","jsx","jsxs","useEffect","useMemo","forwardRef","jsx","forwardRef","jsx","jsx","jsxs","useMemo","FunnelIcon","Fragment","useEffect","useMemo","useRef","useState","useEffect","useState","jsx","useState","useEffect","useCallback","useState","useLayoutEffect","useRef","useState","useMemo","useMemo","useCallback","useEffect","useMemo","useState","useEffect","useMemo","useRef","useState","useCallback","useEffect","useMemo","withoutColumn","Fragment","jsx","jsxs","useState","useRef","useMemo","useEffect","rows","FunnelIcon","useVirtualizer","ChevronDown","ChevronRight","Loader2","Fragment","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/MultilineStringEditor.tsx","../src/components/ui/textarea.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/cell-editing/rangeUtils.ts","../src/cell-editing/useCellSelection.ts","../src/cell-editing/useCellEditingState.ts","../src/cell-editing/renderCellModeCell.tsx","../src/cell-editing/SelectionOverlay.tsx","../src/cell-editing/coerce.ts","../src/cell-editing/clipboard.ts","../src/cell-editing/fillHandle.ts","../src/column-selector/ColumnSelector.tsx","../src/components/ui/dialog.tsx","../src/column-selector/ordering.ts","../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/cell-editing/renderAlwaysEditCell.tsx","../src/hooks/useGroupExpansion.ts","../src/hooks/useStickyGroupHeaderTop.ts","../src/hooks/useVisibleColumns.ts","../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\n// Swiss-locale numbers group thousands with an apostrophe (straight `'` or\n// the typographic `’` some editors/autocorrect substitute) and still use a\n// plain period for the decimal point — e.g. \"1'234'567.89\", or \"CHF\n// 1'234.50\"/\"1'234.50 Fr.\" for a currency cell copied from a Swiss-locale\n// Excel/Sheets. `Number(...)` can't parse either on its own. Deliberately\n// scoped to just this one locale's conventions (this is a Swiss company's\n// own grid) — \"1,234.56\"-style US/UK grouping or \"1.234,56\"-style German\n// grouping isn't attempted here.\nconst SWISS_CURRENCY_MARKER = /^(?:chf|s?fr\\.?)\\s+|\\s+(?:chf|s?fr\\.?)$/gi;\nconst SWISS_THOUSANDS_SEPARATOR = /(\\d)['’](\\d{3})/;\n\n/** Strips a Swiss franc marker and thousands-grouping apostrophes so the result is plain `Number()`-parseable — see `SWISS_CURRENCY_MARKER`'s own doc. Looped (not a global regex) because removing one grouping apostrophe can expose the next one to the left, e.g. \"1'234'567\" needs two passes. */\nfunction stripSwissNumberFormatting(raw: string): string {\n const withoutCurrency = raw.replace(SWISS_CURRENCY_MARKER, \"\");\n let stripped = withoutCurrency;\n while (SWISS_THOUSANDS_SEPARATOR.test(stripped)) {\n stripped = stripped.replace(SWISS_THOUSANDS_SEPARATOR, \"$1$2\");\n }\n return stripped;\n}\n\n/**\n * Parses an arbitrary raw cell value into a `number`, or `null` if it isn't\n * one. Exported (unlike before) for the same reason `toDate` above is:\n * `cell-editing/coerce.ts` needs the exact same numeric parsing this module\n * already uses for display, to back paste/fill coercion without a second\n * copy that could drift from it.\n */\nexport function 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 trimmed = value.trim();\n const parsed = Number(trimmed);\n if (!Number.isNaN(parsed)) return parsed;\n // Only reached for a string `Number()` itself can't parse — a plain\n // \"1234.5\" (or any other already-valid numeric string) never touches\n // Swiss-specific stripping at all.\n const swiss = Number(stripSwissNumberFormatting(trimmed));\n return Number.isNaN(swiss) ? null : swiss;\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 * Overrides the plain-text form of this column's value used when copying a\n * `cellEditing`-mode selection to the clipboard. Defaults to the same\n * `defaultFormat` display text every other rendering path already uses —\n * only needed for a column with a custom `cell` whose displayed content\n * isn't a faithful plain-text rendering of the underlying value (e.g. a\n * badge/icon), the same situation `renderFilter`/`renderEditCell` exist to\n * override for their own concerns.\n */\n formatForClipboard?: (value: unknown, row: TRow) => string;\n /**\n * Computes this column's aggregate cell content from a set of rows — the\n * same function feeds both `DataGridProps`/`TreeDataGridProps`\n * `groupBy`'s optional per-group summary row (called once per bucket, with\n * that bucket's own rows) and `showTotals`'s grand-total row (called once,\n * with every currently-rendered row). No calculation happens by default:\n * omit this to leave the column's cell blank in both rows. There's\n * deliberately no separate \"group subtotal\" vs. \"grand total\" variant —\n * both are just \"reduce this set of rows to a value for this column\", so\n * one function covers both.\n *\n * `showTotals`'s grand-total row calls this with EVERY currently-rendered\n * row, which can be an empty array (e.g. a filter narrows the current page\n * to zero rows) — a bare `rows.reduce((a, b) => ...)` with no seed throws\n * on an empty array, so seed your reducer (`rows.reduce((a, b) => ..., 0)`)\n * rather than assuming at least one row. A per-group summary row never\n * has this problem: a bucket only exists because at least one row produced\n * its key.\n */\n summary?: (rows: TRow[]) => ReactNode;\n}\n\nexport type StringColumn<TRow> = BaseColumn<TRow> & {\n type: \"string\";\n /** Opt-in: defaults to false. Selects `MultilineStringEditor` (an auto-growing `<textarea>`) instead of `StringEditor`'s single-line `<Input>` for this column's default edit widget — same variant-flag idiom as `NumberColumn.currency`, not a separate `type`. Has no effect on static (non-editing) display, which stays whatever `column.cell`/`defaultFormat` already renders. */\n multiline?: boolean;\n};\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 { useEffect, useId, useState } 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 // Tracked as local state, not derived from `value?.operator` on every render: `emit`\n // below intentionally clears the filter entirely (`onChange(undefined)`) whenever\n // `nextText` is empty, since an operator with no value isn't a filter yet -- but that\n // means picking an operator *before* typing anything (a natural \"choose how to\n // search, then type\" flow) would otherwise have nowhere to persist the choice: the\n // very next keystroke would derive `operator` from `value?.operator`, which is still\n // `undefined`, silently reverting to \"Contains\". Local state survives across that gap;\n // the effect below only resyncs it when the filter changes from *outside* this widget\n // (cleared via \"reset filters\", restored from a saved filter, column filters reset).\n const [operator, setOperator] = useState<FilterOperator>(value?.operator ?? DEFAULT_OPERATOR);\n const text = typeof value?.value === \"string\" ? value.value : \"\";\n const inputId = useId();\n const isFiltered = text !== \"\";\n\n useEffect(() => {\n setOperator(value?.operator ?? DEFAULT_OPERATOR);\n }, [value?.operator]);\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) return;\n setOperator(nextOperator);\n 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 open,\n onOpenChange,\n}: EditWidgetProps<EnumColumn<TRow>> & {\n /** Controls the dropdown's own open state — omit to let `<Select>` manage it itself (the default, used everywhere except `AlwaysEditCell`; see that file's own doc for why it needs control here). */\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n}): ReactElement {\n const errorId = editErrorId(rowId, column.id);\n return (\n <div>\n <Select\n value={value === null || value === undefined ? \"\" : String(value)}\n onValueChange={onChange}\n open={open}\n onOpenChange={onOpenChange}\n >\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 { useLayoutEffect, useRef } from \"react\";\nimport type { StringColumn } from \"../column/types\";\nimport { Textarea } from \"../components/ui/textarea\";\nimport { EditFieldError } from \"./EditFieldError\";\nimport { editErrorId, type EditWidgetProps } from \"./widget-types\";\n\n/**\n * Default inline editor for a `type: \"string\"` column with `multiline: true`:\n * an auto-growing `<textarea>` in place of `StringEditor`'s single-line\n * `<Input>`. Dispatched by `registry.tsx`'s existing `case \"string\":` branch\n * — a rendering choice, not a new column `type` — so it works identically\n * under both `DataGridProps.editing` (row-batch) and `.cellEditing`\n * (immediate-apply) modes with zero extra wiring in either.\n */\nexport function MultilineStringEditor<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 const textareaRef = useRef<HTMLTextAreaElement>(null);\n // Captures the cursor position right after each native edit (in the\n // `onChange` handler below, before React re-renders) so it can be\n // restored in the layout effect afterward — this element can otherwise\n // lose focus and reset its caret to 0 on a re-render that changes OTHER\n // props alongside `value` (e.g. `aria-invalid`/`aria-describedby`\n // toggling because a `column.validateEdit` result flipped on the very\n // keystroke that made the field valid) even though the value change\n // itself gave no reason to move it. Restoring proactively — not only when\n // the element happens to still be focused — is what actually fixes this:\n // by the time this effect runs, focus has typically already moved away.\n const selectionRef = useRef<{ start: number; end: number } | null>(null);\n const text = value === null || value === undefined ? \"\" : String(value);\n\n // Grows the textarea to fit its content instead of showing an internal\n // scrollbar for a few extra lines — resetting to \"auto\" first (rather than\n // reading `scrollHeight` against whatever height was already set) is what\n // lets this shrink back down after deleting lines, not just grow. Runs as\n // a layout effect (not a plain effect) so the refocus/selection-restore\n // below lands before the browser paints, avoiding a visible caret jump.\n useLayoutEffect(() => {\n const el = textareaRef.current;\n if (!el) return;\n el.style.height = \"auto\";\n el.style.height = `${el.scrollHeight}px`;\n if (selectionRef.current) {\n if (document.activeElement !== el) el.focus();\n el.setSelectionRange(selectionRef.current.start, selectionRef.current.end);\n }\n }, [text]);\n\n return (\n <div>\n <Textarea\n ref={textareaRef}\n className=\"resize-none overflow-hidden\"\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 selectionRef.current = { start: event.target.selectionStart ?? 0, end: event.target.selectionEnd ?? 0 };\n onChange(event.target.value);\n }}\n />\n {error && <EditFieldError id={errorId} message={error} />}\n </div>\n );\n}\n","import type { TextareaHTMLAttributes } from \"react\";\nimport { forwardRef } from \"react\";\nimport { cn } from \"../../lib/utils\";\n\nexport type TextareaProps = TextareaHTMLAttributes<HTMLTextAreaElement>;\n\nexport const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(({ className, ...props }, ref) => {\n return (\n <textarea\n className={cn(\n \"flex min-h-16 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});\nTextarea.displayName = \"Textarea\";\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 { MultilineStringEditor } from \"./MultilineStringEditor\";\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 column.multiline ? (\n <MultilineStringEditor column={column} rowId={rowId} value={value} onChange={onChange} error={error} autoFocus={autoFocus} />\n ) : (\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 type { CellAddress, CellRange } from \"./types\";\n\n/** Maps each id to its position in `ids` — built once per (rowIds|columnIds) identity change, then reused for every lookup during a drag, rather than an O(n) `indexOf` per cell per mousemove. */\nexport function buildIndexMap(ids: string[]): Map<string, number> {\n const map = new Map<string, number>();\n ids.forEach((id, index) => map.set(id, index));\n return map;\n}\n\n/** A `CellRange`'s two corners resolved to ordinal row/column indices, with `start <= end` on both axes regardless of which corner `anchor`/`focus` was. */\nexport interface NormalizedRange {\n rowStart: number;\n rowEnd: number;\n colStart: number;\n colEnd: number;\n}\n\n/**\n * Resolves a `CellRange` to ordinal indices via `rowIndex`/`columnIndex`\n * (from `buildIndexMap`), or `undefined` if either endpoint's row or column\n * id no longer exists — e.g. the row was removed from `data` since the\n * selection was made.\n */\nexport function normalizeRange(\n range: CellRange,\n rowIndex: Map<string, number>,\n columnIndex: Map<string, number>,\n): NormalizedRange | undefined {\n const anchorRow = rowIndex.get(range.anchor.rowId);\n const focusRow = rowIndex.get(range.focus.rowId);\n const anchorCol = columnIndex.get(range.anchor.columnId);\n const focusCol = columnIndex.get(range.focus.columnId);\n if (anchorRow === undefined || focusRow === undefined || anchorCol === undefined || focusCol === undefined) {\n return undefined;\n }\n return {\n rowStart: Math.min(anchorRow, focusRow),\n rowEnd: Math.max(anchorRow, focusRow),\n colStart: Math.min(anchorCol, focusCol),\n colEnd: Math.max(anchorCol, focusCol),\n };\n}\n\n/** True if `cell` falls inside `range` (inclusive on all sides). */\nexport function isCellInRange(\n cell: CellAddress,\n range: CellRange,\n rowIndex: Map<string, number>,\n columnIndex: Map<string, number>,\n): boolean {\n const normalized = normalizeRange(range, rowIndex, columnIndex);\n if (!normalized) return false;\n const row = rowIndex.get(cell.rowId);\n const col = columnIndex.get(cell.columnId);\n if (row === undefined || col === undefined) return false;\n return row >= normalized.rowStart && row <= normalized.rowEnd && col >= normalized.colStart && col <= normalized.colEnd;\n}\n\n/** Every cell address inside `range`, in row-major order (top-to-bottom, left-to-right), skipping the row-major walk entirely once `normalizeRange` can't resolve either endpoint. */\nexport function cellsInRange(\n range: CellRange,\n rowIds: string[],\n columnIds: string[],\n rowIndex: Map<string, number>,\n columnIndex: Map<string, number>,\n): CellAddress[] {\n const normalized = normalizeRange(range, rowIndex, columnIndex);\n if (!normalized) return [];\n const cells: CellAddress[] = [];\n for (let r = normalized.rowStart; r <= normalized.rowEnd; r++) {\n const rowId = rowIds[r];\n if (rowId === undefined) continue;\n for (let c = normalized.colStart; c <= normalized.colEnd; c++) {\n const columnId = columnIds[c];\n if (columnId === undefined) continue;\n cells.push({ rowId, columnId });\n }\n }\n return cells;\n}\n\n/**\n * Extends a fill-handle drag's `base` range (the source selection, already\n * normalized) toward `target`, locked to whichever single axis — row or\n * column — has the larger overshoot past `base`'s own edge on that axis.\n * Matches Excel/Sheets' own autofill behavior: dragging the fill handle\n * never extends diagonally, only straight down/up/left/right from the\n * source range. Returns `base` unchanged if `target` is inside or on its\n * border (no overshoot on either axis).\n */\nexport function extendRangeForFill(base: NormalizedRange, target: { row: number; col: number }): NormalizedRange {\n const rowOvershoot = Math.max(0, base.rowStart - target.row, target.row - base.rowEnd);\n const colOvershoot = Math.max(0, base.colStart - target.col, target.col - base.colEnd);\n if (rowOvershoot === 0 && colOvershoot === 0) return base;\n if (rowOvershoot >= colOvershoot) {\n return {\n rowStart: Math.min(base.rowStart, target.row),\n rowEnd: Math.max(base.rowEnd, target.row),\n colStart: base.colStart,\n colEnd: base.colEnd,\n };\n }\n return {\n rowStart: base.rowStart,\n rowEnd: base.rowEnd,\n colStart: Math.min(base.colStart, target.col),\n colEnd: Math.max(base.colEnd, target.col),\n };\n}\n\n/**\n * Combines `normalizeRange` + `extendRangeForFill`, converting the result\n * back to row-id/column-id addressing — the fill-handle drag's per-frame\n * preview and its final commit both need exactly this, so it's factored out\n * once rather than duplicated between `useCellSelection`'s live preview memo\n * and its drag-end commit. Returns `undefined` if `base`, `target`, or any\n * resolved endpoint's id no longer exists.\n */\nexport function extendRangeForFillToCellRange(\n base: CellRange,\n target: CellAddress,\n rowIds: string[],\n columnIds: string[],\n rowIndex: Map<string, number>,\n columnIndex: Map<string, number>,\n): CellRange | undefined {\n const normalizedBase = normalizeRange(base, rowIndex, columnIndex);\n if (!normalizedBase) return undefined;\n const targetRow = rowIndex.get(target.rowId);\n const targetCol = columnIndex.get(target.columnId);\n if (targetRow === undefined || targetCol === undefined) return undefined;\n const extended = extendRangeForFill(normalizedBase, { row: targetRow, col: targetCol });\n const startRowId = rowIds[extended.rowStart];\n const endRowId = rowIds[extended.rowEnd];\n const startColumnId = columnIds[extended.colStart];\n const endColumnId = columnIds[extended.colEnd];\n if (startRowId === undefined || endRowId === undefined || startColumnId === undefined || endColumnId === undefined) {\n return undefined;\n }\n return { anchor: { rowId: startRowId, columnId: startColumnId }, focus: { rowId: endRowId, columnId: endColumnId } };\n}\n","import { useCallback, useMemo, useRef, useState } from \"react\";\nimport { buildIndexMap, extendRangeForFillToCellRange } from \"./rangeUtils\";\nimport type { CellAddress, CellRange } from \"./types\";\n\nexport type NavigationDirection = \"up\" | \"down\" | \"left\" | \"right\";\n\nexport interface CellSelectionOptions {\n /** Every row id, in on-screen order — the FULL logical row list, not just currently-mounted virtual items, so navigation and range math work correctly regardless of virtualization. */\n rowIds: string[];\n /** Every editable-surface column id, in on-screen order. */\n columnIds: string[];\n /** Called when navigation lands on a row id that (per the caller) may not currently be mounted — e.g. to `virtualizer.scrollToIndex` it into view. Not called for a move that stays on the same row (left/right). */\n onNavigateToRow?: (rowId: string) => void;\n}\n\nexport interface CellSelectionController {\n /** The committed selection (mouseup already happened, or a keyboard move), or `undefined` before any cell has ever been selected. */\n selection: CellRange | undefined;\n /** True while a mouse-drag range-select is in progress (between `startSelection` and `endDrag`). */\n isDragging: boolean;\n /** `selection` extended live to the drag's current cell while `isDragging`; otherwise identical to `selection`. This is the range a renderer should actually paint. */\n effectiveRange: CellRange | undefined;\n /** Mousedown on `cell`: starts a fresh single-cell selection, or (`extend: true`, e.g. shift+mousedown) extends the existing selection's focus corner to `cell` while keeping its anchor. Either way, also opens a drag (see `endDrag`) — a plain click-no-move sequence still round-trips through `startSelection`/`endDrag` and ends up exactly the same as if `updateDrag` were never called. */\n startSelection: (cell: CellAddress, options?: { extend?: boolean }) => void;\n /** Mousemove while dragging: updates the live drag-focus cell. Callers should coalesce rapid calls themselves (e.g. one `requestAnimationFrame`-scheduled flush per animation frame) — this hook applies whatever it's given without its own throttling. No-op if not currently dragging. */\n updateDrag: (cell: CellAddress) => void;\n /** Mouseup: commits the in-progress drag's live focus cell as the real selection. No-op if not currently dragging. */\n endDrag: () => void;\n /** Keyboard navigation: moves the focus corner by one row/column in `direction`, clamped to grid bounds. Collapses the selection to the new single cell unless `extend` (shift+arrow), which instead moves only the focus corner, keeping the existing anchor. */\n moveSelection: (direction: NavigationDirection, options?: { extend?: boolean }) => void;\n /** Directly replaces the selection, e.g. to leave a specific cell selected after a paste/fill commits. */\n setSelection: (range: CellRange) => void;\n clearSelection: () => void;\n\n /** True while a fill-handle drag is in progress (between `startFillDrag` and `endFillDrag`). Mutually exclusive with `isDragging` — starting one doesn't check for the other, but a caller should never let both begin at once (the fill handle and the rest of a cell are different mousedown targets). */\n isFillDragging: boolean;\n /** The selection extended toward the fill-drag's current target cell, axis-locked (see `extendRangeForFill`'s own doc) — the range a renderer should paint as the live fill preview. `undefined` when not fill-dragging, or before the first `updateFillDrag` call of a given drag. */\n fillPreviewRange: CellRange | undefined;\n /** Starts a fill-drag from the current `selection` (mousedown on the fill handle). No-op if there's no selection yet. */\n startFillDrag: () => void;\n /** Updates the fill-drag's live target cell (mousemove while fill-dragging). Callers should coalesce rapid calls themselves, same as `updateDrag`. No-op if not currently fill-dragging. */\n updateFillDrag: (cell: CellAddress) => void;\n /**\n * Commits the fill-drag (mouseup): sets the extended range as the new\n * `selection` and returns both the original source range and the final\n * extended one, so the caller can compute the actual fill values (which\n * source cell each newly-covered cell copies from) — that data-dependent\n * computation doesn't belong in this hook, which only knows row/column\n * ids, not row data. Returns `undefined` if not fill-dragging, there was\n * no base selection, or no target cell was ever reported (a drag that\n * never moved) — in the last case, nothing to commit, current selection\n * unchanged.\n */\n endFillDrag: () => { sourceRange: CellRange; finalRange: CellRange } | undefined;\n}\n\n/**\n * Owns the rectangular range-selection cursor for `cellEditing` mode: the\n * committed selection, an in-progress mouse-drag's live extent, and\n * keyboard navigation — all keyed by row-id/column-id (via `rowIds`/\n * `columnIds`'s ordinal position), never by DOM node or virtualized index,\n * so a selection is meaningful even for a row that isn't currently mounted.\n */\nexport function useCellSelection({ rowIds, columnIds, onNavigateToRow }: CellSelectionOptions): CellSelectionController {\n const [selection, setSelectionState] = useState<CellRange | undefined>(undefined);\n const [isDragging, setIsDragging] = useState(false);\n const [dragFocus, setDragFocus] = useState<CellAddress | undefined>(undefined);\n // Mirrors `isDragging`/`dragFocus` synchronously for `updateDrag`/`endDrag`'s\n // own logic — those are plain callbacks (not effects), so they need a value\n // that's already correct on the very next call after `startSelection`/\n // `updateDrag`, not one that only catches up after React's next render.\n const draggingRef = useRef(false);\n const dragFocusRef = useRef<CellAddress | undefined>(undefined);\n\n const rowIndex = useMemo(() => buildIndexMap(rowIds), [rowIds]);\n const columnIndex = useMemo(() => buildIndexMap(columnIds), [columnIds]);\n\n const startSelection = useCallback((cell: CellAddress, options?: { extend?: boolean }) => {\n setSelectionState((prev) => (options?.extend && prev ? { anchor: prev.anchor, focus: cell } : { anchor: cell, focus: cell }));\n draggingRef.current = true;\n setIsDragging(true);\n dragFocusRef.current = undefined;\n setDragFocus(undefined);\n }, []);\n\n const updateDrag = useCallback((cell: CellAddress) => {\n if (!draggingRef.current) return;\n dragFocusRef.current = cell;\n setDragFocus(cell);\n }, []);\n\n const endDrag = useCallback(() => {\n if (!draggingRef.current) return;\n draggingRef.current = false;\n setIsDragging(false);\n const finalFocus = dragFocusRef.current;\n dragFocusRef.current = undefined;\n setDragFocus(undefined);\n if (finalFocus) {\n setSelectionState((prev) => (prev ? { anchor: prev.anchor, focus: finalFocus } : prev));\n }\n }, []);\n\n const moveSelection = useCallback(\n (direction: NavigationDirection, options?: { extend?: boolean }) => {\n // Reads `selection` directly (a dependency below) rather than via\n // `setSelectionState`'s own updater-callback form — that updater runs\n // as part of React's commit bookkeeping and can run more than once for\n // the same update (e.g. Strict Mode's intentional double-invocation),\n // which `onNavigateToRow`'s side effect (`virtualizer.scrollToIndex`)\n // must not do.\n const from = selection?.focus ?? { rowId: rowIds[0] ?? \"\", columnId: columnIds[0] ?? \"\" };\n const rowIdx = rowIndex.get(from.rowId);\n const colIdx = columnIndex.get(from.columnId);\n if (rowIdx === undefined || colIdx === undefined || rowIds.length === 0 || columnIds.length === 0) return;\n let nextRowIdx = rowIdx;\n let nextColIdx = colIdx;\n if (direction === \"up\") nextRowIdx = Math.max(0, rowIdx - 1);\n else if (direction === \"down\") nextRowIdx = Math.min(rowIds.length - 1, rowIdx + 1);\n else if (direction === \"left\") nextColIdx = Math.max(0, colIdx - 1);\n else nextColIdx = Math.min(columnIds.length - 1, colIdx + 1);\n const nextRowId = rowIds[nextRowIdx]!;\n const nextColumnId = columnIds[nextColIdx]!;\n if (nextRowId !== from.rowId) onNavigateToRow?.(nextRowId);\n const nextCell: CellAddress = { rowId: nextRowId, columnId: nextColumnId };\n const anchor = options?.extend ? (selection?.anchor ?? nextCell) : nextCell;\n setSelectionState({ anchor, focus: nextCell });\n },\n [selection, rowIds, columnIds, rowIndex, columnIndex, onNavigateToRow],\n );\n\n const setSelection = useCallback((range: CellRange) => {\n setSelectionState(range);\n }, []);\n\n const clearSelection = useCallback(() => {\n draggingRef.current = false;\n setIsDragging(false);\n setDragFocus(undefined);\n setSelectionState(undefined);\n }, []);\n\n const effectiveRange = useMemo<CellRange | undefined>(() => {\n if (!selection) return undefined;\n if (isDragging && dragFocus) return { anchor: selection.anchor, focus: dragFocus };\n return selection;\n }, [selection, isDragging, dragFocus]);\n\n const [isFillDragging, setIsFillDragging] = useState(false);\n const [fillTarget, setFillTarget] = useState<CellAddress | undefined>(undefined);\n // Same synchronous-read rationale as `draggingRef`/`dragFocusRef` above.\n const fillDraggingRef = useRef(false);\n const fillTargetRef = useRef<CellAddress | undefined>(undefined);\n const fillBaseRef = useRef<CellRange | undefined>(undefined);\n\n const startFillDrag = useCallback(() => {\n if (!selection) return;\n fillBaseRef.current = selection;\n fillDraggingRef.current = true;\n setIsFillDragging(true);\n }, [selection]);\n\n const updateFillDrag = useCallback((cell: CellAddress) => {\n if (!fillDraggingRef.current) return;\n fillTargetRef.current = cell;\n setFillTarget(cell);\n }, []);\n\n const endFillDrag = useCallback((): { sourceRange: CellRange; finalRange: CellRange } | undefined => {\n if (!fillDraggingRef.current) return undefined;\n fillDraggingRef.current = false;\n setIsFillDragging(false);\n const sourceRange = fillBaseRef.current;\n const target = fillTargetRef.current;\n fillBaseRef.current = undefined;\n fillTargetRef.current = undefined;\n setFillTarget(undefined);\n if (!sourceRange || !target) return undefined;\n const finalRange = extendRangeForFillToCellRange(sourceRange, target, rowIds, columnIds, rowIndex, columnIndex);\n if (!finalRange) return undefined;\n setSelectionState(finalRange);\n return { sourceRange, finalRange };\n }, [rowIds, columnIds, rowIndex, columnIndex]);\n\n const fillPreviewRange = useMemo<CellRange | undefined>(() => {\n if (!isFillDragging || !selection || !fillTarget) return undefined;\n return extendRangeForFillToCellRange(selection, fillTarget, rowIds, columnIds, rowIndex, columnIndex);\n }, [isFillDragging, selection, fillTarget, rowIds, columnIds, rowIndex, columnIndex]);\n\n return {\n selection,\n isDragging,\n effectiveRange,\n startSelection,\n updateDrag,\n endDrag,\n moveSelection,\n setSelection,\n clearSelection,\n isFillDragging,\n fillPreviewRange,\n startFillDrag,\n updateFillDrag,\n endFillDrag,\n };\n}\n","import { useRef, useState } from \"react\";\nimport type { CellAddress, CellChange, CellEditingOptions } from \"./types\";\n\nfunction cellKey(rowId: string, columnId: string): string {\n return `${rowId}:${columnId}`;\n}\n\n/**\n * One cell's locally-echoed value after a commit, kept only until the row's\n * own real data (`rawValue` at render time) stops matching `previousValue` —\n * see `resolveValue`'s own doc for why that's the right invalidation\n * condition for a fire-and-forget, no-rollback model.\n */\ninterface CellOverride {\n value: unknown;\n previousValue: unknown;\n}\n\n/**\n * Everything `renderCellModeCell` needs to render one cell under\n * `cellEditing` mode. Bundled into one object handed through a `useRef` for\n * the identical reason `EditingCellContext` is (see that type's own doc):\n * `<DataGrid>`'s `tanstackColumns` memo must not depend on anything that\n * changes per-keystroke, or typing into one cell would remount the whole\n * table body.\n */\nexport interface CellEditingCellContext<TRow> {\n /** Mirrors `CellEditingOptions.alwaysEdit` — every editable cell renders `AlwaysEditCell` instead of going through `editingCell`/`onBeginEdit` at all when this is set. */\n alwaysEdit: boolean;\n /** Mirrors `CellEditingOptions.disabled`. Click-to-edit mode blocks interaction by simply never opening an editor while this is set (see `onBeginEdit`'s own gate) — `AlwaysEditCell`'s editors are always open regardless, so it reads this directly to disable its rendered form control instead. */\n disabled: boolean;\n /** The one cell currently showing its editor, or `undefined` — at most one at a time, the same convention `EditingCellContext.activeRowId` uses at row granularity. Meaningless (always `undefined`) under `alwaysEdit`, where every editable cell already has its own editor. */\n editingCell: CellAddress | undefined;\n /** Replaces (doesn't append to) the editor's starting value when editing began by typing a printable character directly (Excel's \"just start typing\" gesture) rather than double-click/Enter/F2, which start from the cell's current value. `undefined` means \"start from the current value.\" */\n initialText: string | undefined;\n /** Whether at least one `onChangeDraft` has fired this edit session — distinguishes \"no keystrokes yet, show the starting value\" from \"the user explicitly typed something,\" including an empty string. */\n hasDraft: boolean;\n /** The in-progress typed value for `editingCell`, meaningful only when `hasDraft` is true. */\n draftValue: unknown;\n getRowId: (row: TRow) => string;\n getError: (rowId: string, columnId: string) => string | undefined;\n /**\n * The value to actually display for one cell: `rawValue` (the row's real\n * data) unless a still-\"fresh\" local override exists. An override is\n * fresh only while `rawValue` still equals what it was at the moment of\n * the edit (`previousValue`) — the instant the row's real data changes to\n * ANYTHING else (the consumer's `onCellsChange` handler updated its own\n * `data` to reflect the accepted edit, or a refetch landed with something\n * different entirely, e.g. server-side normalization), real data is\n * trusted again instead of the stale local echo. This is what lets a\n * fire-and-forget, no-rollback model (see `CellEditingOptions.onCellsChange`'s\n * own doc) still self-correct if the consumer's eventual real data\n * diverges from what was optimistically shown.\n */\n resolveValue: (rowId: string, columnId: string, rawValue: unknown) => unknown;\n /** Starts editing `cell`. */\n onBeginEdit: (cell: CellAddress, initialText?: string) => void;\n /** Reverts without committing — Escape, or a blur while the current draft is invalid. */\n onCancelEdit: () => void;\n /** Records a keystroke/selection's new value for the cell currently being edited — for an \"atomic\" editor (enum/boolean/date, where a single selection IS the complete gesture) the caller follows this immediately with `onCommitEdit`; for a free-text editor (string/number) this only buffers, and the caller commits separately on blur/Enter/Tab. */\n onChangeDraft: (value: unknown) => void;\n /** Commits the current draft (or, if `hasDraft` is false, effectively a no-op close) for the cell currently being edited, as one `CellChange`, and closes its editor. `previousValue` is the row's real value before this edit, for the resulting `CellChange.previousValue`. */\n onCommitEdit: (row: TRow, previousValue: unknown) => void;\n /**\n * Commits an explicit `value` directly, bypassing the draft state\n * entirely — for an \"atomic\" editor (enum/boolean/date) whose `onChange`\n * fires once with the complete new value and wants to commit in that same\n * call. `onCommitEdit` above can't be reused for this: it reads `hasDraft`/\n * `draftValue` from React state, which a same-tick `onChangeDraft` call\n * immediately before it would NOT yet reflect (state updates don't apply\n * until the next render, even within the same event handler).\n */\n onCommitValue: (row: TRow, previousValue: unknown, value: unknown) => void;\n onSetError: (rowId: string, columnId: string, message: string | undefined) => void;\n /** Commits one independently-managed cell's change directly — for `AlwaysEditCell`, which owns its own draft state locally rather than through `editingCell`/`onChangeDraft`/`onCommitEdit`'s single shared slot (every editable cell needs its own simultaneous editor under `alwaysEdit`, not just one at a time). */\n applyChange: (change: CellChange<TRow>) => void;\n /**\n * Reads (and clears) whether `onCommitEdit`/`onCommitValue` committed\n * synchronously during the current call stack. `<DataGrid>`'s own keydown\n * handler needs this: `renderCellModeCell`'s Enter/Tab commit deliberately\n * doesn't stop the event from bubbling, so that handler can advance the\n * selection afterward — but `editingCell` itself is React state, which\n * doesn't reflect `closeEditor()`'s update until the next render, so\n * reading it directly during that same bubbled dispatch always still\n * shows the row that JUST closed. This ref-backed flag is the one piece of\n * \"did a commit just happen in this exact gesture\" state that's readable\n * synchronously, in the same event, immediately after the commit.\n */\n consumeJustCommitted: () => boolean;\n}\n\nexport interface CellEditingState<TRow> {\n /** The live context, read directly by a render path with no remount-avoidance concerns of its own. */\n ctx: CellEditingCellContext<TRow> | undefined;\n /** Same context, mirrored into a ref every render — see `CellEditingCellContext`'s own doc for why `<DataGrid>`'s per-cell closures must read through this instead of closing over `ctx` directly. */\n ctxRef: { current: CellEditingCellContext<TRow> | undefined };\n /** Commits a whole gesture's changes at once (paste, fill-drag) — no `editingCell` involved, unlike `onCommitEdit` above. */\n applyChanges: (changes: CellChange<TRow>[]) => void;\n}\n\n/**\n * Owns the \"true spreadsheet\" per-cell edit state: which single cell (if\n * any) is showing its editor, that cell's in-progress typed draft, per-cell\n * validation errors, and the local-override echo every committed change\n * leaves behind until the consumer's own real data catches up. Every\n * mutation — a typed commit, a future paste, a future fill-drag — funnels\n * through `applyChanges`, so a later undo/redo can be built on top of that\n * one function without a rearchitecture.\n */\nexport function useCellEditingState<TRow>(\n cellEditing: CellEditingOptions<TRow> | undefined,\n getRowId: (row: TRow) => string,\n): CellEditingState<TRow> {\n const [editingCell, setEditingCell] = useState<CellAddress | undefined>(undefined);\n const [initialText, setInitialText] = useState<string | undefined>(undefined);\n const [hasDraft, setHasDraft] = useState(false);\n const [draftValue, setDraftValue] = useState<unknown>(undefined);\n const [overrides, setOverrides] = useState<Map<string, CellOverride>>(new Map());\n const [errors, setErrors] = useState<Map<string, string>>(new Map());\n const justCommittedRef = useRef(false);\n\n function closeEditor(): void {\n setEditingCell(undefined);\n setInitialText(undefined);\n setHasDraft(false);\n setDraftValue(undefined);\n }\n\n function applyChanges(changes: CellChange<TRow>[]): void {\n if (!cellEditing || cellEditing.disabled || changes.length === 0) return;\n setOverrides((prev) => {\n const next = new Map(prev);\n for (const change of changes) {\n next.set(cellKey(change.rowId, change.columnId), { value: change.value, previousValue: change.previousValue });\n }\n return next;\n });\n setErrors((prev) => {\n if (prev.size === 0) return prev;\n const next = new Map(prev);\n for (const change of changes) next.delete(cellKey(change.rowId, change.columnId));\n return next.size === prev.size ? prev : next;\n });\n void cellEditing.onCellsChange(changes);\n }\n\n function onBeginEdit(cell: CellAddress, text?: string): void {\n // Matches `applyChanges`'s own `disabled` gate — a consumer setting\n // `cellEditing.disabled` (e.g. while a previous gesture's `onCellsChange`\n // is still in flight) expects ALL interaction blocked, not just the\n // eventual commit silently discarded while the editor still visibly\n // opens and appears to accept input.\n if (!cellEditing || cellEditing.disabled) return;\n setEditingCell(cell);\n setInitialText(text);\n setHasDraft(text !== undefined);\n setDraftValue(text);\n }\n\n function onChangeDraft(value: unknown): void {\n setHasDraft(true);\n setDraftValue(value);\n }\n\n function onCommitEdit(row: TRow, previousValue: unknown): void {\n const cell = editingCell;\n const shouldCommit = hasDraft;\n const value = draftValue;\n closeEditor();\n if (!cell || !shouldCommit) return;\n justCommittedRef.current = true;\n applyChanges([{ rowId: cell.rowId, row, columnId: cell.columnId, previousValue, value }]);\n }\n\n function onCommitValue(row: TRow, previousValue: unknown, value: unknown): void {\n const cell = editingCell;\n closeEditor();\n if (!cell) return;\n justCommittedRef.current = true;\n applyChanges([{ rowId: cell.rowId, row, columnId: cell.columnId, previousValue, value }]);\n }\n\n function consumeJustCommitted(): boolean {\n const value = justCommittedRef.current;\n justCommittedRef.current = false;\n return value;\n }\n\n function onSetError(rowId: string, columnId: string, message: string | undefined): void {\n setErrors((prev) => {\n const key = cellKey(rowId, columnId);\n if (message === undefined) {\n if (!prev.has(key)) return prev;\n const next = new Map(prev);\n next.delete(key);\n return next;\n }\n const next = new Map(prev);\n next.set(key, message);\n return next;\n });\n }\n\n function getError(rowId: string, columnId: string): string | undefined {\n return errors.get(cellKey(rowId, columnId));\n }\n\n function resolveValue(rowId: string, columnId: string, rawValue: unknown): unknown {\n const override = overrides.get(cellKey(rowId, columnId));\n if (!override) return rawValue;\n if (rawValue !== override.previousValue) return rawValue;\n return override.value;\n }\n\n const ctx: CellEditingCellContext<TRow> | undefined = cellEditing\n ? {\n alwaysEdit: cellEditing.alwaysEdit ?? false,\n disabled: cellEditing.disabled ?? false,\n editingCell,\n initialText,\n hasDraft,\n draftValue,\n getRowId,\n getError,\n resolveValue,\n onBeginEdit,\n onCancelEdit: closeEditor,\n onChangeDraft,\n onCommitEdit,\n onCommitValue,\n onSetError,\n applyChange: (change) => applyChanges([change]),\n consumeJustCommitted,\n }\n : undefined;\n\n // Reassigned every render (a plain statement, not an effect) — see\n // `CellEditingCellContext`'s own doc for why.\n const ctxRef = useRef<CellEditingCellContext<TRow> | undefined>(undefined);\n ctxRef.current = ctx;\n\n return { ctx, ctxRef, applyChanges };\n}\n","import type { KeyboardEvent as ReactKeyboardEvent, ReactNode } from \"react\";\nimport { defaultFormat } from \"../column/format\";\nimport type { ColumnDef } from \"../column/types\";\nimport { isEditable } from \"../column/types\";\nimport { renderDefaultEditWidget } from \"../edit/registry\";\nimport type { CellEditingCellContext } from \"./useCellEditingState\";\n\n/**\n * Column types whose default editor's `onChange` fires once per COMPLETE\n * selection (choosing an enum option, toggling a boolean, picking a date) —\n * for these, that single `onChange` call commits immediately, matching\n * Excel's own behavior for a dropdown/checkbox cell. `string`/`number`/\n * `currency` fire `onChange` once per KEYSTROKE instead, so committing on\n * every one of those would mean one `onCellsChange` call per character\n * typed — those buffer locally via `onChangeDraft` and commit separately, on\n * blur or Enter/Tab (see `renderCellModeCell`'s own logic below).\n */\nexport type ColumnTypeLiteral = \"string\" | \"number\" | \"currency\" | \"date\" | \"datetime\" | \"boolean\" | \"enum\";\n\n/** Shared with `renderAlwaysEditCell.tsx` — same commit-immediately-vs-buffer split applies whether a cell's editor opened via a click or is always open. */\nexport function isAtomicEditorType(type: ColumnTypeLiteral): boolean {\n return type === \"enum\" || type === \"boolean\" || type === \"date\" || type === \"datetime\";\n}\n\n/**\n * Renders one cell under `cellEditing` mode — the immediate-apply\n * counterpart to `edit/renderEditableCell.tsx`'s accumulate-then-save one.\n * Reuses the exact same per-type editor widgets via `registry.tsx`'s\n * `renderDefaultEditWidget` (and the same `column.renderEditCell`/\n * `column.validateEdit` escape hatches) — only the commit semantics differ.\n *\n * Not responsible for range-selection visuals — that's `SelectionOverlay`,\n * one overlay for the whole grid — this only decides whether THIS cell is\n * the one currently showing its editor.\n */\nexport function renderCellModeCell<TRow>(\n column: ColumnDef<TRow>,\n row: TRow,\n rawValue: unknown,\n ctx: CellEditingCellContext<TRow> | undefined,\n): ReactNode {\n const staticContent = (value: unknown): ReactNode => (column.cell ? column.cell(value, row) : defaultFormat(column, value));\n if (!ctx) return staticContent(rawValue);\n\n const rowId = ctx.getRowId(row);\n const value = ctx.resolveValue(rowId, column.id, rawValue);\n const isThisCellEditing = ctx.editingCell?.rowId === rowId && ctx.editingCell.columnId === column.id && isEditable(column, row);\n\n if (!isThisCellEditing) {\n return (\n <span data-testid={`cell-${rowId}-${column.id}`} className=\"block w-full\">\n {staticContent(value)}\n </span>\n );\n }\n\n const isAtomic = isAtomicEditorType(column.type);\n const error = ctx.getError(rowId, column.id);\n const startValue = ctx.initialText !== undefined ? ctx.initialText : value;\n const currentValue = ctx.hasDraft ? ctx.draftValue : startValue;\n\n function validate(candidate: unknown): string | undefined {\n return column.validateEdit?.(candidate, row);\n }\n\n const onChange = (next: unknown): void => {\n const message = validate(next);\n ctx!.onSetError(rowId, column.id, message);\n // `onCommitValue` (not `onChangeDraft` + `onCommitEdit`) for the atomic\n // path: `onCommitEdit` reads `hasDraft`/`draftValue` from React state,\n // which a same-tick `onChangeDraft` call immediately before it would NOT\n // yet reflect — state updates apply on the next render, not before the\n // current handler returns. `onCommitValue` takes the new value directly,\n // sidestepping that entirely.\n if (isAtomic && !message) {\n // `value` (resolveValue's result), not `rawValue` — a still-fresh\n // override from an immediately-prior edit this session must be the\n // reported `previousValue`, not the underlying row data it's\n // temporarily masking (see `resolveValue`'s own doc).\n ctx!.onCommitValue(row, value, next);\n return;\n }\n // Buffered types always land here; an atomic type only does when its\n // selection was invalid — shown (with its error) but not committed, via\n // the same draft slot buffered types use, so the invalid choice stays\n // visible instead of snapping back to the pre-edit value.\n ctx!.onChangeDraft(next);\n };\n\n const editor = column.renderEditCell\n ? column.renderEditCell(currentValue, row, onChange, error, true)\n : renderDefaultEditWidget(column, rowId, currentValue, onChange, error, true);\n\n // Buffered (string/number/currency) editors only: Enter/Tab attempts a\n // commit-and-continue; a validation failure blocks it (stops the event\n // from reaching <DataGrid>'s own Enter/Tab-navigates handler) so the user\n // stays on the broken cell instead of silently losing what they typed.\n // Escape always reverts, for every editor type — same one-cell-only\n // revert semantics `renderEditableCell.tsx` documents for row mode.\n function handleKeyDown(event: ReactKeyboardEvent): void {\n if (event.key === \"Escape\") {\n event.stopPropagation();\n ctx!.onCancelEdit();\n return;\n }\n // A multiline column's own Shift+Enter is left alone entirely — the\n // textarea's native default inserts a literal newline, matching real\n // spreadsheet multiline-cell editing (plain Enter still commits and\n // moves down, same as every other buffered editor).\n const isMultilineNewline = column.type === \"string\" && column.multiline && event.key === \"Enter\" && event.shiftKey;\n if (isMultilineNewline) return;\n if (isAtomic || (event.key !== \"Enter\" && event.key !== \"Tab\")) return;\n const message = validate(currentValue);\n if (message) {\n event.stopPropagation();\n return;\n }\n ctx!.onCommitEdit(row, value);\n // Deliberately NOT stopped: <DataGrid>'s own keydown handler on the\n // scroll container sees this same Enter/Tab afterward and moves the\n // selection cursor to the next cell — this cell is no longer \"editing\"\n // by the time that happens (onCommitEdit just closed it), so the two\n // handlers compose into exactly \"commit, then move,\" not a conflict.\n }\n\n // Buffered editors only: clicking away commits a valid draft, or reverts\n // an invalid one — there's no way to keep the editor open once focus has\n // already left it, so \"block the blur\" (Enter/Tab's option) isn't\n // available here.\n function handleBlur(): void {\n if (isAtomic) return;\n if (validate(currentValue)) ctx!.onCancelEdit();\n else ctx!.onCommitEdit(row, value);\n }\n\n return (\n <div onKeyDown={handleKeyDown} onBlur={handleBlur}>\n {editor}\n </div>\n );\n}\n","import type { MouseEvent as ReactMouseEvent, ReactElement } from \"react\";\nimport { useLayoutEffect, useRef, useState } from \"react\";\nimport type { CellRange } from \"./types\";\n\ninterface OverlayRect {\n top: number;\n left: number;\n width: number;\n height: number;\n}\n\n/** Matches a `[data-cell-row][data-cell-col]` `<td>` exactly — `CSS.escape` handles any id containing quotes/backslashes/etc. so the attribute selector below can't be broken out of. */\nfunction cellSelector(rowId: string, columnId: string): string {\n const escape = (value: string): string => (typeof CSS !== \"undefined\" && CSS.escape ? CSS.escape(value) : value.replace(/[\"\\\\]/g, \"\\\\$&\"));\n return `[data-cell-row=\"${escape(rowId)}\"][data-cell-col=\"${escape(columnId)}\"]`;\n}\n\n/** Resolves `range`'s bounding rect within `container`'s own unscrolled content-space — `undefined` if either corner isn't currently mounted (e.g. scrolled out under virtualization). Shared between the selection rect and the fill-preview rect below; see the component doc for why content-space (not viewport-space) is what makes this scroll correctly with zero listeners of its own. */\nfunction computeRangeRect(container: HTMLDivElement, range: CellRange): OverlayRect | undefined {\n const anchorEl = container.querySelector<HTMLElement>(cellSelector(range.anchor.rowId, range.anchor.columnId));\n const focusEl = container.querySelector<HTMLElement>(cellSelector(range.focus.rowId, range.focus.columnId));\n if (!anchorEl || !focusEl) return undefined;\n const containerRect = container.getBoundingClientRect();\n const a = anchorEl.getBoundingClientRect();\n const f = focusEl.getBoundingClientRect();\n const left = Math.min(a.left, f.left) - containerRect.left + container.scrollLeft;\n const top = Math.min(a.top, f.top) - containerRect.top + container.scrollTop;\n const right = Math.max(a.right, f.right) - containerRect.left + container.scrollLeft;\n const bottom = Math.max(a.bottom, f.bottom) - containerRect.top + container.scrollTop;\n return { top, left, width: right - left, height: bottom - top };\n}\n\nfunction rectsEqual(a: OverlayRect | undefined, b: OverlayRect | undefined): boolean {\n if (a === b) return true;\n if (!a || !b) return false;\n return a.top === b.top && a.left === b.left && a.width === b.width && a.height === b.height;\n}\n\nconst HANDLE_SIZE = 8;\n\n/**\n * Renders the current range selection (and, while a fill-handle drag is in\n * progress, a dashed preview of the range it would extend to) as absolutely-\n * positioned overlay divs, geometry read live from the DOM rects of the\n * relevant cells' `data-cell-row`/`data-cell-col` nodes — deliberately not\n * derived from `columnSizing`/row-height state, since reading the live DOM\n * always matches exactly what's rendered (including natural, non-resizing\n * column widths `columnSizing`'s own math doesn't account for).\n *\n * Must render as a CHILD of the scrollable container (not a sibling outside\n * it), with that container given `position: relative` — position/left/top\n * are computed in the container's own unscrolled content-space (rect minus\n * the container's rect, plus the container's current scroll offset), so once\n * set, the overlay scrolls together with the table content via ordinary\n * browser scrolling with no scroll listener of its own. A virtualized grid's\n * scroll-driven re-renders (the virtualizer's own React state) naturally\n * recompute this on every such render; a non-virtualized grid never needs to\n * (native scroll changes nothing in content-space).\n *\n * Deliberately isolated from `visibleColumns`/`tanstackColumns` — it reads\n * nothing from either, only `range`/`fillPreviewRange` and the DOM — so\n * painting/updating a selection or a fill-drag (including during a fast\n * mouse-drag) never touches the column-definition memo that would otherwise\n * remount the whole table body.\n */\nexport function SelectionOverlay({\n containerRef,\n range,\n fillPreviewRange,\n onFillHandleMouseDown,\n}: {\n containerRef: { current: HTMLDivElement | null };\n range: CellRange | undefined;\n /** The live preview range during a fill-handle drag, or `undefined` when not fill-dragging. */\n fillPreviewRange?: CellRange | undefined;\n /** Mousedown on the fill handle — starts a fill-drag. Omit to render no handle at all (e.g. a read-only or non-editable selection). */\n onFillHandleMouseDown?: (event: ReactMouseEvent<HTMLDivElement>) => void;\n}): ReactElement | null {\n const [rect, setRect] = useState<OverlayRect | undefined>(undefined);\n const [previewRect, setPreviewRect] = useState<OverlayRect | undefined>(undefined);\n\n // Read fresh inside the effect below via a ref, not as a dependency of the\n // scroll-listener effect — see that effect's own doc for why it must not\n // resubscribe on every `range`/`fillPreviewRange` change.\n const recomputeRef = useRef<() => void>(() => {});\n recomputeRef.current = () => {\n const container = containerRef.current;\n const nextRect = range && container ? computeRangeRect(container, range) : undefined;\n setRect((prev) => (rectsEqual(prev, nextRect) ? prev : nextRect));\n const nextPreviewRect = fillPreviewRange && container ? computeRangeRect(container, fillPreviewRange) : undefined;\n setPreviewRect((prev) => (rectsEqual(prev, nextPreviewRect) ? prev : nextPreviewRect));\n };\n\n // No dependency array: geometry can change for reasons other than `range`/\n // `fillPreviewRange` themselves (a column resize, a virtualized row\n // mounting/unmounting) — this is deliberately cheap enough (at most four\n // `getBoundingClientRect` reads) to redo on every render of this small,\n // isolated component rather than trying to enumerate every input that\n // could move a cell.\n useLayoutEffect(() => {\n recomputeRef.current();\n });\n\n // A `pinned` cell's `getBoundingClientRect()` is reported in VIEWPORT\n // space (fixed by `position: sticky`) rather than scrolling with the\n // content the way every other cell's does — `computeRangeRect`'s own\n // \"unscrolled content-space\" formula (rect minus the container's rect,\n // plus the container's current scroll offset) only cancels out correctly\n // for a cell that actually moves with `scrollLeft`/`scrollTop`. A plain\n // horizontal/vertical scroll of the container is otherwise invisible to\n // this component — nothing about it changes React state anywhere in\n // `<DataGrid>`, so the `useLayoutEffect` above (which only reruns when\n // THIS component re-renders) would leave a selection anchored on a pinned\n // column silently drifting away from the real cell as the container keeps\n // scrolling. A plain native `scroll` listener (passive; this only ever\n // reads geometry, never calls `preventDefault`) is what makes a pinned\n // corner track correctly; recomputing here is a no-op for the common case\n // of an all-unpinned selection, since that math already cancels out scroll\n // on its own.\n useLayoutEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n function handleScroll(): void {\n recomputeRef.current();\n }\n container.addEventListener(\"scroll\", handleScroll, { passive: true });\n return () => container.removeEventListener(\"scroll\", handleScroll);\n // eslint-disable-next-line react-hooks/exhaustive-deps -- deliberately only resubscribes when the container itself changes identity, not on every range/fillPreviewRange change; `recomputeRef.current` is reassigned above on every render and read fresh from inside the listener.\n }, [containerRef.current]);\n\n if (!rect) return null;\n return (\n <>\n <div\n data-testid=\"cell-selection-overlay\"\n aria-hidden\n className=\"pointer-events-none absolute z-20 border-2 border-primary bg-primary/10\"\n style={{ top: rect.top, left: rect.left, width: rect.width, height: rect.height }}\n />\n {previewRect && (\n <div\n data-testid=\"cell-fill-preview-overlay\"\n aria-hidden\n className=\"pointer-events-none absolute z-20 border-2 border-dashed border-primary\"\n style={{ top: previewRect.top, left: previewRect.left, width: previewRect.width, height: previewRect.height }}\n />\n )}\n {onFillHandleMouseDown && (\n <div\n data-testid=\"cell-fill-handle\"\n role=\"button\"\n aria-label=\"Fill handle: drag to fill adjacent cells\"\n className=\"absolute z-30 cursor-crosshair border border-background bg-primary\"\n style={{\n top: rect.top + rect.height - HANDLE_SIZE / 2,\n left: rect.left + rect.width - HANDLE_SIZE / 2,\n width: HANDLE_SIZE,\n height: HANDLE_SIZE,\n }}\n onMouseDown={(event) => {\n // Not this component's own selection-rect mousedown — starting a\n // fill-drag must not ALSO start (or extend) a range-select drag\n // on the cell underneath the handle.\n event.stopPropagation();\n onFillHandleMouseDown(event);\n }}\n />\n )}\n </>\n );\n}\n","import { toDate, toNumber } from \"../column/format\";\nimport type { ColumnDef, EnumOption } from \"../column/types\";\n\nfunction parseBoolean(raw: string): boolean | undefined {\n const normalized = raw.trim().toLowerCase();\n if (normalized === \"true\" || normalized === \"yes\" || normalized === \"1\") return true;\n if (normalized === \"false\" || normalized === \"no\" || normalized === \"0\") return false;\n return undefined;\n}\n\n/** Matches a pasted/filled string against `options` by value first (exact), then by label (case-insensitive) — a paste from a real spreadsheet is far more likely to carry the human-readable label than the underlying value code. */\nfunction matchEnumOption(options: EnumOption[], raw: string): string | undefined {\n const trimmed = raw.trim();\n const byValue = options.find((option) => option.value === trimmed);\n if (byValue) return byValue.value;\n const lowered = trimmed.toLowerCase();\n return options.find((option) => option.label.toLowerCase() === lowered)?.value;\n}\n\n// The literal text Excel/Sheets puts in a cell showing a formula error.\n// Pasting one of these into any non-`string` column skips that cell, same as\n// any other value that doesn't coerce — `toNumber`/`toDate`/`parseBoolean`/\n// `matchEnumOption` already happen to reject all of these today (none of\n// them looks like a number/date/boolean/option), but checking explicitly\n// means that stays true even if one of those helpers' own \"not a valid X\"\n// fallback changes independently later.\nconst EXCEL_ERROR_STRINGS = new Set([\"#N/A\", \"#DIV/0!\", \"#REF!\", \"#VALUE!\", \"#NAME?\", \"#NULL!\", \"#NUM!\", \"#SPILL!\", \"#CALC!\"]);\n\nfunction isExcelErrorString(raw: string): boolean {\n return EXCEL_ERROR_STRINGS.has(raw.trim());\n}\n\n/**\n * Coerces one pasted/filled raw TSV cell string into the typed value a\n * column's editor/validator expects — the paste/fill equivalent of\n * `edit/registry.tsx`'s per-type dispatch, reusing the exact same `toDate`/\n * `toNumber` parsing `column/format.ts` uses for display so paste coercion\n * can't drift from what the grid already shows.\n *\n * Returns `undefined` for a value that can't be coerced at all (e.g. \"abc\"\n * into a number column) — the caller's job to skip that cell rather than\n * hand `onCellsChange` a `NaN`/`Invalid Date`. An empty string coerces to\n * `null` for `number`/`currency` (pasting a blank cell over a numeric one\n * clears it, matching `NumberEditor`'s own null-for-cleared convention) —\n * `string` columns instead treat an empty string as itself a valid value.\n * An Excel/Sheets error string (`#N/A`, `#DIV/0!`, etc.) pasted into any\n * column but `string` is likewise skipped rather than coerced.\n */\nexport function coerceValueForColumn<TRow>(column: ColumnDef<TRow>, raw: string): { value: unknown } | undefined {\n if (column.type !== \"string\" && isExcelErrorString(raw)) return undefined;\n switch (column.type) {\n case \"string\":\n return { value: raw };\n case \"number\":\n case \"currency\": {\n if (raw.trim() === \"\") return { value: null };\n const num = toNumber(raw);\n return num === null ? undefined : { value: num };\n }\n case \"boolean\": {\n const bool = parseBoolean(raw);\n return bool === undefined ? undefined : { value: bool };\n }\n case \"date\":\n case \"datetime\": {\n const date = toDate(raw);\n return date === null ? undefined : { value: date };\n }\n case \"enum\": {\n const matched = matchEnumOption(column.options, raw);\n return matched === undefined ? undefined : { value: matched };\n }\n }\n}\n","import { defaultFormat } from \"../column/format\";\nimport type { ColumnDef } from \"../column/types\";\nimport { getColumnValue } from \"../column/types\";\nimport { buildIndexMap, normalizeRange } from \"./rangeUtils\";\nimport type { CellRange } from \"./types\";\n\n/** Wraps `field` in double quotes (doubling any internal quotes) only if it contains a tab, newline, or quote — the same quoting convention real spreadsheet apps (Excel, Google Sheets) use for their own TSV clipboard payloads, so a value round-trips correctly through copy/paste with them. */\nfunction quoteTsvField(field: string): string {\n if (!/[\\t\\n\\r\"]/.test(field)) return field;\n return `\"${field.replace(/\"/g, '\"\"')}\"`;\n}\n\n/**\n * Serializes the cells inside `range` to Excel-compatible TSV text: tab-\n * separated columns, newline-separated rows, walking `rowIds`/`columnIds` in\n * their on-screen order (not `range`'s own anchor/focus order). Each cell's\n * text comes from `column.formatForClipboard` if the column supplies one,\n * else the same `defaultFormat` display text the grid itself renders —\n * deliberately not a second \"raw value to string\" path.\n */\nexport function rangeToTsv<TRow>(\n range: CellRange,\n rows: TRow[],\n rowIds: string[],\n columns: ColumnDef<TRow>[],\n columnIds: string[],\n getRowId: (row: TRow) => string,\n): string {\n const rowIndex = buildIndexMap(rowIds);\n const columnIndex = buildIndexMap(columnIds);\n const normalized = normalizeRange(range, rowIndex, columnIndex);\n if (!normalized) return \"\";\n\n const columnById = new Map(columns.map((column) => [column.id, column]));\n const rowById = new Map(rows.map((row) => [getRowId(row), row]));\n\n const lines: string[] = [];\n for (let r = normalized.rowStart; r <= normalized.rowEnd; r++) {\n const rowId = rowIds[r];\n const row = rowId !== undefined ? rowById.get(rowId) : undefined;\n const fields: string[] = [];\n for (let c = normalized.colStart; c <= normalized.colEnd; c++) {\n const columnId = columnIds[c];\n const column = columnId !== undefined ? columnById.get(columnId) : undefined;\n if (!column || row === undefined) {\n fields.push(\"\");\n continue;\n }\n const value = getColumnValue(column, row);\n const text = column.formatForClipboard ? column.formatForClipboard(value, row) : defaultFormat(column, value);\n fields.push(quoteTsvField(text));\n }\n lines.push(fields.join(\"\\t\"));\n }\n return lines.join(\"\\n\");\n}\n\n/**\n * Parses Excel-compatible TSV/tab-delimited clipboard text into a 2D array\n * of raw string cells. A small character-by-character state machine (not a\n * naive split-by-newline-then-split-by-tab) so a quoted field's own embedded\n * newline — a genuine multi-line cell copied from Excel/Sheets — isn't\n * mistaken for a row boundary, and a doubled `\"\"` inside a quoted field\n * unescapes to one literal `\"`.\n */\nexport function parseTsv(text: string): string[][] {\n // A leading byte-order mark shows up on some Windows clipboard sources\n // (and would otherwise become part of the very first cell's own text).\n const withoutBom = text.startsWith(\"\") ? text.slice(1) : text;\n const normalized = withoutBom.replace(/\\r\\n/g, \"\\n\");\n const rows: string[][] = [];\n let row: string[] = [];\n let field = \"\";\n let inQuotes = false;\n let i = 0;\n while (i < normalized.length) {\n const ch = normalized[i]!;\n if (inQuotes) {\n if (ch === '\"') {\n if (normalized[i + 1] === '\"') {\n field += '\"';\n i += 2;\n continue;\n }\n inQuotes = false;\n i++;\n continue;\n }\n field += ch;\n i++;\n continue;\n }\n if (ch === '\"' && field === \"\") {\n inQuotes = true;\n i++;\n continue;\n }\n if (ch === \"\\t\") {\n row.push(field);\n field = \"\";\n i++;\n continue;\n }\n if (ch === \"\\n\") {\n row.push(field);\n rows.push(row);\n row = [];\n field = \"\";\n i++;\n continue;\n }\n field += ch;\n i++;\n }\n row.push(field);\n rows.push(row);\n\n // A trailing newline (common when copying a full range from Excel/Sheets)\n // would otherwise parse as one extra, bogus all-empty trailing row.\n const last = rows[rows.length - 1];\n if (rows.length > 1 && normalized.endsWith(\"\\n\") && last?.length === 1 && last[0] === \"\") {\n rows.pop();\n }\n return rows;\n}\n","import type { ColumnDef } from \"../column/types\";\nimport { getColumnValue, isEditable } from \"../column/types\";\nimport { buildIndexMap, normalizeRange } from \"./rangeUtils\";\nimport type { CellChange, CellRange } from \"./types\";\n\n/**\n * Computes the `CellChange`s a fill-handle drag produces: every cell newly\n * covered by `finalRange` but not already inside `sourceRange` copies its\n * value from the corresponding source cell, TILING the source pattern across\n * the fill direction when the extension is longer than the source itself —\n * the same repeat-the-pattern behavior Excel's own fill-handle uses for a\n * multi-cell source (a single-cell source is just the degenerate case: every\n * new cell copies that one cell). Value-copy only, no numeric/date\n * auto-increment — MVP scope, matching the akeneo hand-rolled\n * implementation this feature replaces, which also only ever copies a\n * single value uniformly.\n *\n * Cells already inside `sourceRange` are left out of the result entirely —\n * they're untouched by a fill, only the newly-covered ones change.\n * A target cell that's missing, not editable, or whose source cell is\n * missing is silently skipped.\n */\nexport function computeFillChanges<TRow>(\n sourceRange: CellRange,\n finalRange: CellRange,\n rowIds: string[],\n columnIds: string[],\n columns: ColumnDef<TRow>[],\n getRow: (rowId: string) => TRow | undefined,\n): CellChange<TRow>[] {\n const rowIndex = buildIndexMap(rowIds);\n const columnIndex = buildIndexMap(columnIds);\n const source = normalizeRange(sourceRange, rowIndex, columnIndex);\n const final = normalizeRange(finalRange, rowIndex, columnIndex);\n if (!source || !final) return [];\n\n const columnById = new Map(columns.map((column) => [column.id, column]));\n const sourceRowSpan = source.rowEnd - source.rowStart + 1;\n const sourceColSpan = source.colEnd - source.colStart + 1;\n\n /** Maps any row index in `final`'s range to the source row it tiles from — identity for a row already inside `source`, otherwise wrapping through `source`'s own rows in order, continuing in the same direction past its far edge. */\n function sourceRowFor(targetRow: number): number {\n if (targetRow >= source!.rowStart && targetRow <= source!.rowEnd) return targetRow;\n if (targetRow > source!.rowEnd) return source!.rowStart + ((targetRow - source!.rowEnd - 1) % sourceRowSpan);\n return source!.rowEnd - ((source!.rowStart - targetRow - 1) % sourceRowSpan);\n }\n function sourceColFor(targetCol: number): number {\n if (targetCol >= source!.colStart && targetCol <= source!.colEnd) return targetCol;\n if (targetCol > source!.colEnd) return source!.colStart + ((targetCol - source!.colEnd - 1) % sourceColSpan);\n return source!.colEnd - ((source!.colStart - targetCol - 1) % sourceColSpan);\n }\n\n const changes: CellChange<TRow>[] = [];\n for (let r = final.rowStart; r <= final.rowEnd; r++) {\n const isNewRow = r < source.rowStart || r > source.rowEnd;\n for (let c = final.colStart; c <= final.colEnd; c++) {\n const isNewCol = c < source.colStart || c > source.colEnd;\n if (!isNewRow && !isNewCol) continue; // inside the original source rectangle — untouched by the fill\n\n const targetRowId = rowIds[r];\n const targetColumnId = columnIds[c];\n if (targetRowId === undefined || targetColumnId === undefined) continue;\n const column = columnById.get(targetColumnId);\n const targetRow = getRow(targetRowId);\n if (!column || targetRow === undefined || !isEditable(column, targetRow)) continue;\n\n // Both axes matter here, not just the row one: a vertical fill (rows\n // extended, columns unchanged) has `sourceColFor(c) === c`, so\n // `sourceColumn === column` and this degenerates to \"same column,\n // cycled source rows\" — but a horizontal fill (columns extended, rows\n // unchanged) needs the SOURCE column's own value, not the target\n // column's, or every new column would just copy whatever (nothing)\n // already sat in its own cells instead of the pattern being extended.\n const sourceRowId = rowIds[sourceRowFor(r)];\n const sourceColumnId = columnIds[sourceColFor(c)];\n const sourceRow = sourceRowId !== undefined ? getRow(sourceRowId) : undefined;\n const sourceColumn = sourceColumnId !== undefined ? columnById.get(sourceColumnId) : undefined;\n if (sourceRow === undefined || !sourceColumn) continue;\n\n changes.push({\n rowId: targetRowId,\n row: targetRow,\n columnId: targetColumnId,\n previousValue: getColumnValue(column, targetRow),\n value: getColumnValue(sourceColumn, sourceRow),\n });\n }\n }\n return changes;\n}\n","import { Columns3, GripVertical } from \"lucide-react\";\nimport type { ReactElement, ReactNode } from \"react\";\nimport { useEffect, useState } 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 { cn } from \"../lib/utils\";\nimport { applyColumnOrder, moveColumnBefore } from \"./ordering\";\nimport {\n readPersistedColumnOrder,\n readPersistedVisibility,\n writePersistedColumnOrder,\n writePersistedVisibility,\n} 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 * Column ids in display order (typically the same order already fed to\n * `<DataGrid columns={...}>`). When given together with\n * `onColumnOrderChange`, each row gets a drag handle so a column can be\n * dragged to a new position -- purely within its own group (or the\n * ungrouped section): a column's `group` is fixed metadata from its\n * `ColumnDef`, not something dragging can change, so a row only ever\n * visibly moves among its own group's other rows. Omit both props for the\n * original visibility-only behavior (no drag handles rendered at all).\n *\n * `onColumnOrderChange` is the only thing that ever fires on a drop --\n * exactly one call, with the fully-computed new order, so a caller can\n * persist it (to its own state store, a backend, wherever) without also\n * reaching into this component's internals. `persistKey`, if set,\n * additionally layers the same kind of localStorage restore-on-mount/write-\n * on-change sync `visibility` already gets (under a separate storage key --\n * see `persistence.ts` -- so it can't collide with or migrate previously\n * stored visibility data).\n */\n columnOrder?: string[];\n onColumnOrderChange?: (order: string[]) => 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. `columnOrder`/`onColumnOrderChange` (both optional,\n * opt-in) layer the same kind of controlled sync on top for drag-to-reorder\n * — see their own doc comments above.\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 columnOrder,\n onColumnOrderChange,\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 // Same restore-on-mount for column order, gated on `onColumnOrderChange`\n // too (nothing to feed a restored order into otherwise). A separate effect\n // from the one above rather than one combined effect, since the two are\n // independent opt-ins (a caller can use persistKey for visibility only,\n // reordering only, or both) with no shared state between them.\n useEffect(() => {\n if (!persistKey || !onColumnOrderChange) return;\n const restored = readPersistedColumnOrder(persistKey);\n if (restored) onColumnOrderChange(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 canReorder = Boolean(onColumnOrderChange);\n const effectiveOrder = columnOrder ?? columns.map((column) => column.id);\n const orderedColumns = columnOrder ? applyColumnOrder(columns, columnOrder) : columns;\n const groups = groupColumns(orderedColumns);\n const groupById = new Map(columns.map((column) => [column.id, column.group] as const));\n\n // Which row is mid-drag, and which row it's currently hovering over --\n // local UI state only, never escapes this component. The committed result\n // (a new `columnOrder`) is all `onColumnOrderChange` ever sees.\n const [draggedId, setDraggedId] = useState<string | null>(null);\n const [overId, setOverId] = useState<string | null>(null);\n\n function handleDrop(targetId: string): void {\n if (!draggedId || draggedId === targetId) return;\n // A cross-group drop would still only reorder `effectiveOrder`'s flat\n // array correctly, but `groups` re-derives each *group section's own*\n // display order from that array's first-seen order too (see\n // `groupColumns`) -- so moving a column across a group boundary would\n // silently reshuffle which group's section renders first, not just\n // reorder within a group as documented above. Reject it outright rather\n // than let the column's own group visibly \"win\" a section-order fight\n // its drag was never meant to affect.\n if (groupById.get(draggedId) !== groupById.get(targetId)) return;\n const next = moveColumnBefore(effectiveOrder, draggedId, targetId);\n onColumnOrderChange?.(next);\n if (persistKey) writePersistedColumnOrder(persistKey, next);\n }\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 draggable={canReorder}\n onDragStart={canReorder ? () => setDraggedId(column.id) : undefined}\n onDragOver={\n canReorder\n ? (event) => {\n event.preventDefault();\n setOverId(column.id);\n }\n : undefined\n }\n onDragLeave={\n canReorder\n ? () => setOverId((current) => (current === column.id ? null : current))\n : undefined\n }\n onDrop={\n canReorder\n ? (event) => {\n event.preventDefault();\n handleDrop(column.id);\n setDraggedId(null);\n setOverId(null);\n }\n : undefined\n }\n onDragEnd={\n canReorder\n ? () => {\n setDraggedId(null);\n setOverId(null);\n }\n : undefined\n }\n className={cn(\n \"flex items-center gap-2 rounded-md px-1.5 py-1 text-sm hover:bg-accent/50 focus-within:bg-accent/50\",\n canReorder && \"cursor-grab\",\n draggedId === column.id && \"opacity-40\",\n overId === column.id &&\n draggedId !== null &&\n draggedId !== column.id &&\n \"outline -outline-offset-2 outline-2 outline-primary\",\n )}\n >\n {canReorder && (\n <GripVertical\n className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground/50\"\n aria-hidden\n />\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 { ColumnDef } from \"../column/types\";\n\n/**\n * Reorders `columns` to match `order` (an array of column ids in display\n * order), then hands the result to `groupColumns` -- since that function\n * preserves each column's position within its own group, pre-sorting the\n * input array this way is enough to get correctly-ordered groups with no\n * change to `groupColumns` itself.\n *\n * An id in `order` that no longer matches a real column is ignored (e.g. a\n * column removed from the grid since the order was persisted); a column\n * present in `columns` but missing from `order` (e.g. one newly added since)\n * is appended at the end, in its original `columns` position -- the same\n * \"graceful fallback for a stale persisted list\" shape `readPersistedVisibility`\n * already uses for visibility.\n */\nexport function applyColumnOrder<TRow>(\n columns: readonly ColumnDef<TRow>[],\n order: readonly string[],\n): ColumnDef<TRow>[] {\n const byId = new Map(columns.map((column) => [column.id, column] as const));\n const seen = new Set<string>();\n const ordered: ColumnDef<TRow>[] = [];\n\n for (const id of order) {\n const column = byId.get(id);\n if (column && !seen.has(id)) {\n ordered.push(column);\n seen.add(id);\n }\n }\n for (const column of columns) {\n if (!seen.has(column.id)) ordered.push(column);\n }\n return ordered;\n}\n\n/**\n * Moves `columnId` to sit immediately before `beforeColumnId` in `order` (or\n * to the end when `beforeColumnId` is undefined or no longer present) --\n * the pure reorder-array-splice logic behind `<ColumnSelector>`'s\n * drag-and-drop, kept separate from the DOM drag wiring so it's unit-testable\n * without simulating real drag events (jsdom has no working `DataTransfer`).\n * Recomputing `beforeColumnId`'s index AFTER removing `columnId` (rather than\n * the caller pre-computing an insert index) makes this correct regardless of\n * whether the drag moved the column earlier or later in the list.\n */\nexport function moveColumnBefore(\n order: readonly string[],\n columnId: string,\n beforeColumnId: string | undefined,\n): string[] {\n const next = order.filter((id) => id !== columnId);\n const insertAt = beforeColumnId ? next.indexOf(beforeColumnId) : -1;\n if (insertAt === -1) next.push(columnId);\n else next.splice(insertAt, 0, columnId);\n return next;\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\n// A different top-level namespace segment (\"column-order\", not \"columns\")\n// -- not just a \":order\" suffix appended to storageKeyFor's own key -- so the\n// two key families can never collide for ANY persistKey value. A suffix-only\n// scheme is only \"usually\" distinct: storageKeyFor(\"foo:order\") and\n// orderStorageKeyFor(\"foo\") would both resolve to\n// \"bmsui-datagrid:columns:foo:order\", silently clobbering each other's data\n// the moment two ColumnSelector instances in the same app pick persistKeys\n// that differ by exactly that literal suffix. Diverging at the namespace\n// segment itself rules that out structurally, not by convention.\nexport function orderStorageKeyFor(persistKey: string): string {\n return `bmsui-datagrid:column-order:${persistKey}`;\n}\n\n/**\n * Reads and JSON-parses a persisted column order (array of column ids) from\n * localStorage. Returns `null` if there's nothing stored, the JSON is\n * malformed, or the parsed value isn't an array of strings -- same\n * fall-back-don't-throw contract as `readPersistedVisibility`.\n */\nexport function readPersistedColumnOrder(persistKey: string): string[] | null {\n const raw = window.localStorage.getItem(orderStorageKeyFor(persistKey));\n if (raw === null) return null;\n try {\n const parsed = JSON.parse(raw);\n return Array.isArray(parsed) && parsed.every((id) => typeof id === \"string\") ? parsed : null;\n } catch {\n return null;\n }\n}\n\nexport function writePersistedColumnOrder(persistKey: string, order: readonly string[]): void {\n window.localStorage.setItem(orderStorageKeyFor(persistKey), JSON.stringify(order));\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 {\n CellContext,\n ColumnDef as TanstackColumnDef,\n ColumnSizingState,\n Row as TanstackRow,\n RowData,\n} 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 {\n ClipboardEvent as ReactClipboardEvent,\n CSSProperties,\n KeyboardEvent as ReactKeyboardEvent,\n MouseEvent as ReactMouseEvent,\n ReactElement,\n ReactNode,\n} from \"react\";\nimport { Fragment, useEffect, useMemo, useRef, useState } from \"react\";\nimport { parseTsv, rangeToTsv } from \"../cell-editing/clipboard\";\nimport { coerceValueForColumn } from \"../cell-editing/coerce\";\nimport { computeFillChanges } from \"../cell-editing/fillHandle\";\nimport { buildIndexMap, normalizeRange } from \"../cell-editing/rangeUtils\";\nimport { AlwaysEditCell, type AtomicGestureContext } from \"../cell-editing/renderAlwaysEditCell\";\nimport { renderCellModeCell } from \"../cell-editing/renderCellModeCell\";\nimport { SelectionOverlay } from \"../cell-editing/SelectionOverlay\";\nimport type { CellAddress, CellChange } from \"../cell-editing/types\";\nimport type { CellEditingCellContext } from \"../cell-editing/useCellEditingState\";\nimport { useCellEditingState } from \"../cell-editing/useCellEditingState\";\nimport { useCellSelection } from \"../cell-editing/useCellSelection\";\nimport { alignClassName } from \"../column/format\";\nimport type { ColumnDef } from \"../column/types\";\nimport { getColumnValue, isEditable, isFilterable, isSortable } from \"../column/types\";\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 { useGroupExpansion } from \"../hooks/useGroupExpansion\";\nimport { useStickyGroupHeaderTop } from \"../hooks/useStickyGroupHeaderTop\";\nimport { useVisibleColumns } from \"../hooks/useVisibleColumns\";\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// Floor for keyboard-driven resize (ArrowLeft on the resize handle) — mouse\n// drag has no equivalent floor today, but a keyboard user has no visual\n// feedback while resizing, so this keeps a column from being shrunk to\n// (or past) zero-width by holding the key down.\nconst MIN_COLUMN_SIZE = 40;\n\n// `aria-sort` belongs on the `<th>` itself, not the toggle `<button>` inside\n// it (WAI-ARIA \"sort\" property — see the table sort pattern in APG): screen\n// readers announce a header's sort state from the cell, not from focusing a\n// child control. `\"none\"` (rather than omitting the attribute) tells\n// assistive tech this header *is* sortable but not the current sort key,\n// distinct from a non-sortable column, which gets no `aria-sort` at all.\nfunction sortDirToAriaSort(dir: \"asc\" | \"desc\" | undefined): \"ascending\" | \"descending\" | \"none\" {\n if (dir === \"asc\") return \"ascending\";\n if (dir === \"desc\") return \"descending\";\n return \"none\";\n}\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// Stable empty-array identity for `useCellSelection`'s `rowIds`/`columnIds`\n// when `cellEditing` isn't set — a fresh `[]` literal every render would\n// still be a *different* array each time, defeating `buildIndexMap`'s own\n// memoization for no reason (selection is inert either way, but the memo\n// should stay cheap regardless).\nconst EMPTY_ID_ARRAY: string[] = [];\n\n/**\n * Whether a clipboard event's target is a genuine native text field — an\n * `<input>` (covers `StringEditor`/`NumberEditor`/`DateEditor`, the last via\n * `<input type=\"date\">`) or a `<textarea>` (`MultilineStringEditor`) —\n * deliberately NOT true for `EnumEditor`/`BooleanEditor`'s Radix-based\n * button/listbox widgets, which have no free text of their own to copy/paste\n * natively. Used by `handleCopy`/`handlePaste` to defer to the browser under\n * `cellEditing.alwaysEdit`, where every editable cell's own editor is always\n * mounted, so there's no single `editingCell` left to check the way\n * click-to-edit mode does.\n */\nfunction isNativeTextFieldTarget(event: { target: EventTarget | null }): boolean {\n const tag = (event.target as HTMLElement | null)?.tagName;\n return tag === \"INPUT\" || tag === \"TEXTAREA\";\n}\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 * One entry in the flattened, virtualizable render list `<DataGrid>` windows\n * over below (see `flatItems`'s own doc) — a synthetic group-header, or one\n * real table row. Kept as its own module-scope generic (rather than inlined\n * where `flatItems` is computed) so `renderFlatItem`/`renderGroupHeaderTbody`\n * can share the exact same shape without either side re-deriving it.\n */\ntype FlatItem<TRow extends RowData> =\n | { kind: \"header\"; key: string; rows: TRow[] }\n | { kind: \"row\"; row: TanstackRow<GridTableFeatures, TRow> };\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 *\n * `cellEditingCtxRef` follows the identical recipe for `cellEditing` mode's\n * own per-keystroke state (`CellEditingCellContext`). At most one of\n * `editingCtxRef.current`/`cellEditingCtxRef.current` is ever set (the two\n * modes are mutually exclusive — see `DataGridProps.cellEditing`'s own doc),\n * so which renderer a cell uses is decided fresh on every actual render,\n * with neither ref itself ever appearing in this function's own deps.\n *\n * `atomicGestureRef` is the same recipe again, for `AlwaysEditCell`'s own\n * atomic-widget gesture handling (`AtomicGestureContext`) — see its own doc.\n */\nfunction toTanstackColumns<TRow extends RowData>(\n columns: ColumnDef<TRow>[],\n editingCtxRef: { current: EditingCellContext<TRow> | undefined },\n cellEditingCtxRef: { current: CellEditingCellContext<TRow> | undefined },\n atomicGestureRef: { current: AtomicGestureContext | 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 const cellEditingCtx = cellEditingCtxRef.current;\n if (!cellEditingCtx) return renderEditableCell(column, info.row.original, info.getValue(), editingCtxRef.current);\n return cellEditingCtx.alwaysEdit ? (\n <AlwaysEditCell\n column={column}\n row={info.row.original}\n rawValue={info.getValue()}\n ctx={cellEditingCtx}\n atomicGesture={atomicGestureRef.current}\n />\n ) : (\n renderCellModeCell(column, info.row.original, info.getValue(), cellEditingCtx)\n );\n },\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 onGridStateChange,\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 showTotals = false,\n editing,\n cellEditing,\n}: DataGridProps<TRow>): ReactElement {\n // Not supported together with `groupBy` yet — `cellEditingRowIds` below is\n // built from the flat `tableRows` list, but rendering only shows expanded-\n // group rows via `groupedBuckets`'s own bucketed order, so a keyboard-\n // navigated selection could land on a row hidden inside a collapsed group.\n // Same \"one silently stands down\" precedent as `groupBy`+`virtualize` (see\n // `virtualize`'s own doc) rather than a hard error.\n const hasCellEditing = Boolean(cellEditing) && !groupBy;\n const { state, filtersByColumn, setColumnFilter, toggleSort, setPage } = useGridState(\n dataSource,\n initialState,\n gridState,\n showPagination,\n onGridStateChange,\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. Shared with `<TreeDataGrid>`'s own `groupBy` support via\n // `useGroupExpansion`.\n const { isGroupExpanded, toggleGroupExpanded } = useGroupExpansion(\n defaultGroupsExpanded,\n controlledExpandedGroups,\n onExpandedGroupsChange,\n );\n\n const { pendingEdits, editErrors, ctxRef: editingCtxRef, handleSaveEdits, handleDiscardEdits } = useEditingState(\n editing,\n getRowId,\n );\n // Gated on `hasCellEditing` (not `cellEditing` directly) so the groupBy\n // stand-down above (see that flag's own doc) also disables the per-cell\n // renderer/editor state, not just selection.\n const { ctx: cellEditingCtx, ctxRef: cellEditingCtxRef, applyChanges: applyCellChanges } = useCellEditingState(\n hasCellEditing ? cellEditing : undefined,\n getRowId,\n );\n // Same \"stable ref identity, reassigned every render\" recipe as\n // `editingCtxRef`/`cellEditingCtxRef` above — assigned further down, once\n // `cellSelection`/`pendingOpenEnumCell` (which its callbacks close over)\n // exist, but declared here so `tanstackColumns` below can take it without\n // depending on either.\n const atomicGestureRef = useRef<AtomicGestureContext | undefined>(undefined);\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. Shared with `<TreeDataGrid>`'s own\n // `columnVisibility` support via `useVisibleColumns`.\n const visibleColumns = useVisibleColumns(columns, columnVisibility);\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(\n () => toTanstackColumns(visibleColumns, editingCtxRef, cellEditingCtxRef, atomicGestureRef),\n [visibleColumns],\n );\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 // `table.getRow(id)` THROWS for an id no longer in the row model (verified\n // against @tanstack/table-core's coreRowsFeature.utils), unlike the\n // `?.original`-chained call sites below that were written assuming it\n // degrades to `undefined` — a real risk here specifically, since a\n // `cellEditing` selection/edit can reference a row id that's since been\n // removed from `data` (a consumer's own `onCellsChange` handler, or a\n // refetch). `getRowModel().rowsById` is a plain object lookup, so a\n // missing id just reads as `undefined`.\n function findRowById(rowId: string): TRow | undefined {\n return table.getRowModel().rowsById[rowId]?.original;\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 // Same \"entirely opt-in\" convention as `hasFilterRow`/`hasHeaderGroups`\n // above — the per-group summary row inside `renderGroupHeaderTbody` and\n // the `showTotals` `<tfoot>` both only render once at least one visible\n // column sets `summary` (see `BaseColumn.summary`'s own doc); a `groupBy`\n // or `showTotals` grid with no column opting in renders exactly as it did\n // before either feature existed.\n const hasColumnSummary = visibleColumns.some((column) => column.summary);\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\n // Dev-only guardrail: `showPagination={false}` with no explicit `pageSize`\n // now resolves to an effectively unbounded page (see `useGridState`'s own\n // doc), so this only fires for what that fix can't cover -- a controlled\n // `gridState` prop whose `pageSize` doesn't (or no longer) cover every\n // row, or a `dataSource: {mode: \"server\"}` response smaller than the\n // `rowCount` it reported. An explicit, uncontrolled `initialState.pageSize`\n // is the deliberate fixed-size-chunking use case and must never warn.\n const truncationWarnedRef = useRef(false);\n useEffect(() => {\n if (process.env.NODE_ENV === \"production\") return;\n if (showPagination) return;\n if (truncationWarnedRef.current) return;\n if (!gridState && initialState?.pageSize !== undefined) return;\n if (tableRows.length >= rowCount) return;\n truncationWarnedRef.current = true;\n console.warn(\n `[DataGrid] showPagination={false} but only ${tableRows.length} of ${rowCount} row(s) are rendered, with no pagination UI to reach the rest. Pass an explicit pageSize (via initialState or gridState) that covers every row you want visible, or a dataSource that returns all of them.`,\n );\n }, [showPagination, gridState, initialState?.pageSize, tableRows.length, rowCount]);\n\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. Shared with\n // `<TreeDataGrid>`'s own `groupBy` support via `useStickyGroupHeaderTop`.\n const { theadRef, groupHeaderTop } = useStickyGroupHeaderTop(Boolean(groupBy));\n\n // `threshold` is documented (see `DataGridVirtualizeOptions.threshold`) as\n // a REAL row count -- keyed off `tableRows.length`, not the flattened\n // render list below, so a grouped grid's synthetic header entries never\n // push a caller's actual row count over a threshold they didn't cross.\n // Identical to `flatItems.length` whenever `groupBy` is unset (no headers\n // to inflate the count), so this is a no-op change for every non-grouped\n // grid.\n const shouldVirtualize = Boolean(virtualize) && tableRows.length > (virtualize?.threshold ?? 100);\n\n // The single flattened, index-addressable render list `shouldVirtualize`\n // above gates windowing over -- a plain `{kind: \"row\"}` per table row when\n // `groupBy` is unset, or one `{kind: \"header\"}` entry per bucket\n // (contributing its OWN member rows right after it, only while that\n // bucket is expanded -- a collapsed bucket contributes just its header)\n // when it is. Flattening headers and rows into one array, instead of\n // virtualizing `tableRows` directly and rendering group headers as a\n // separate non-virtualized wrapper around them, is what makes `groupBy`\n // and `virtualize` composable at all: `useVirtualizer` needs one\n // contiguous, positionally-addressable list to window over, and a\n // header's height needs measuring/positioning exactly like a data row's.\n // Only actually built while virtualizing -- neither the plain `tableRows`\n // path nor `renderGroupedBucket`'s own non-virtualized grouped path below\n // ever reads it, and building it unconditionally would re-walk every\n // bucket (recomputing the exact same `bucket.items.map((row) =>\n // row.original)` `renderGroupedBucket` already does) purely to throw the\n // result away on every render of a grid that never virtualizes at all.\n const flatItems = useMemo<FlatItem<TRow>[]>(() => {\n if (!shouldVirtualize) return [];\n if (!groupedBuckets) return tableRows.map((row) => ({ kind: \"row\", row }));\n const items: FlatItem<TRow>[] = [];\n for (const bucket of groupedBuckets) {\n const originalRows = bucket.items.map((row) => row.original);\n items.push({ kind: \"header\", key: bucket.key, rows: originalRows });\n if (isGroupExpanded(bucket.key)) {\n for (const row of bucket.items) items.push({ kind: \"row\", row });\n }\n }\n return items;\n // eslint-disable-next-line react-hooks/exhaustive-deps -- `isGroupExpanded`'s identity is the only one of these deps that actually changes on an expand/collapse toggle (both the controlled-`expandedGroups`-object and uncontrolled-internal-Set forms get a fresh identity from their own setter); `groupedBuckets`/`tableRows` don't change from expand/collapse alone, they're listed because the memo also needs to re-run when the underlying data/grouping itself changes.\n }, [shouldVirtualize, groupedBuckets, tableRows, isGroupExpanded]);\n\n const scrollRef = useRef<HTMLDivElement>(null);\n const virtualizer = useVirtualizer({\n count: flatItems.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` -- the real\n // loaded-data count, deliberately NOT `flatItems.length`: the latter also\n // shifts on a pure expand/collapse toggle (no data changed at all), which\n // would defeat the \"won't fire again for the same data\" contract\n // (`DataGridVirtualizeOptions.onEndReached`'s own doc) the instant a\n // caller collapses a group while already scrolled to the bottom. WHERE\n // \"the end\" currently is, though, is still a `flatItems`/`virtualItems`\n // question (collapsing the last bucket really does move the bottom of the\n // rendered content) -- only the dedup fingerprint changes here. Guarded by\n // a ref (not state) since it's bookkeeping for an effect, not something\n // that should itself trigger a render.\n const lastVisibleIndex = virtualItems.length > 0 ? virtualItems[virtualItems.length - 1]!.index : -1;\n const notifiedForLengthRef = useRef<number>(-1);\n // A sort/filter change means the caller's `onStateChange` handler is about\n // to replace `dataSource.data` with an entirely new loaded window (a fresh\n // first page for the new sort/filter), not grow the existing one — reset\n // the dedup guard so a same-length replacement (e.g. the new window also\n // happens to be exactly one page long, the common case) doesn't get\n // silently mistaken for \"already notified for this length\" and swallowed.\n // Keyed off `state.filter`/`state.sort` identity, not `state.page`: this\n // mode doesn't own pagination (see `virtualize.onEndReached`'s own doc),\n // so a `page` change here would only come from some other caller-driven\n // reset, which already goes through a fresh `dataSource.data` (and thus a\n // real `tableRows.length` change) on its own.\n useEffect(() => {\n notifiedForLengthRef.current = -1;\n }, [state.filter, state.sort]);\n useEffect(() => {\n if (!virtualize?.onEndReached) return;\n if (lastVisibleIndex < 0 || lastVisibleIndex < flatItems.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, flatItems.length, tableRows.length, virtualize]);\n\n // Full logical row/column id lists (not just currently-mounted virtual\n // items) for `useCellSelection`'s range math and keyboard navigation — see\n // that hook's own doc for why id-keying (rather than DOM/virtualized\n // index) is what makes a selection survive virtualized mount/unmount.\n // Depends on `hasCellEditing` (not `cellEditing` itself) for the same\n // reason `serverRowCount`/`dataSource` above do: a caller inlining\n // `cellEditing={{ onCellsChange: fn }}` as a fresh object every render\n // must not defeat this memo just because that wrapper's identity changed.\n const cellEditingRowIds = useMemo(\n () => (hasCellEditing ? tableRows.map((row) => row.id) : EMPTY_ID_ARRAY),\n [hasCellEditing, tableRows],\n );\n const cellEditingColumnIds = useMemo(\n () => (hasCellEditing ? visibleColumns.map((column) => column.id) : EMPTY_ID_ARRAY),\n [hasCellEditing, visibleColumns],\n );\n const cellEditingRowIndex = useMemo(() => buildIndexMap(cellEditingRowIds), [cellEditingRowIds]);\n const cellSelection = useCellSelection({\n rowIds: cellEditingRowIds,\n columnIds: cellEditingColumnIds,\n onNavigateToRow: (rowId) => {\n if (!shouldVirtualize) return;\n const index = cellEditingRowIndex.get(rowId);\n if (index !== undefined) virtualizer.scrollToIndex(index);\n },\n });\n\n // Tracks the cell a mousedown started on — whether it was a shift-extend,\n // whether the pointer ever moved to a *different* cell before mouseup, and\n // whether this cell was ALREADY the one being edited at mousedown time —\n // the three things `handleMouseUp` below needs to tell a plain click (open\n // that cell's editor, Excel's single-click-to-edit convention) apart from\n // a real drag-select, and from a click that landed inside an\n // already-open editor (e.g. to reposition its text cursor). That last\n // check can't be redone at mouseup time by just reading\n // `cellEditingCtx.editingCell` then: `handleCellMouseDown`'s own\n // `scrollRef.current?.focus()` call blurs (closes/commits) whatever was\n // being edited as an ordinary side effect of moving focus, including this\n // very cell if the click landed inside it — by mouseup, `editingCell` is\n // already `undefined` either way, so only capturing the answer up front\n // (before that focus call) can tell \"was already editing this cell, don't\n // reopen it\" apart from \"was editing elsewhere, now free to open this one\".\n // Refs, not state: read-then-cleared synchronously inside a native event\n // handler, never rendered.\n const clickCellRef = useRef<{ rowId: string; columnId: string; extend: boolean; wasEditing: boolean } | null>(null);\n const dragMovedRef = useRef(false);\n // The one built-in enum (`<Select>`) cell (if any) that should open its\n // dropdown right now — see `AtomicGestureContext.shouldOpenEnum`'s own\n // doc for why `AlwaysEditCell` needs this instead of just letting Radix's\n // native open-on-click happen. Set (once) by `handleMouseUp` below;\n // deliberately never cleared back to `undefined` afterward — once set,\n // `AlwaysEditCell` opens itself and takes over its own open/close state\n // via `onOpenChange` from then on, so leaving this pointed at the same\n // cell is inert, not a standing \"force reopen.\"\n const [pendingOpenEnumCell, setPendingOpenEnumCell] = useState<CellAddress | undefined>(undefined);\n\n atomicGestureRef.current = {\n shouldOpenEnum: (rowId, columnId) =>\n pendingOpenEnumCell?.rowId === rowId && pendingOpenEnumCell?.columnId === columnId,\n // Mirrors exactly what `handleCellMouseDown` below does for every other\n // cell, minus the click-to-edit-only tail end (irrelevant here — always\n // invoked under `alwaysEdit`, same as that function's own early return\n // for it) — see `AtomicGestureContext.registerSelection`'s own doc for\n // why `AlwaysEditCell` can't just rely on that function running on its\n // own for a gesture it suppresses.\n registerSelection: (rowId, columnId, extend) => {\n cellSelection.startSelection({ rowId, columnId }, { extend });\n clickCellRef.current = { rowId, columnId, extend, wasEditing: false };\n dragMovedRef.current = false;\n },\n };\n\n /** Opens `cell`'s editor for a plain (non-extending, non-dragged) click — shared by the mouseup-commits-a-click path below and `handleCellDoubleClick`. No-ops if the cell isn't editable, or is (STILL — this check runs after `handleCellMouseDown`'s own blur-on-focus already had its chance to close it) the one being edited: `handleCellDoubleClick` has no `wasEditing` snapshot of its own to rely on, so this is what stops its second click's `dblclick` from clobbering a draft the reopen already produced. */\n function beginEditFromClick(cell: { rowId: string; columnId: string }): void {\n if (!cellEditingCtx) return;\n if (cellEditingCtx.editingCell?.rowId === cell.rowId && cellEditingCtx.editingCell.columnId === cell.columnId) return;\n const column = columnById.get(cell.columnId);\n const row = findRowById(cell.rowId);\n if (!column || row === undefined || !isEditable(column, row)) return;\n cellEditingCtx.onBeginEdit(cell);\n }\n\n // Mousemove/mouseup are attached to `window` (not the scroll container)\n // while a drag is in progress — the pointer commonly moves outside the\n // container's own bounds mid-drag (e.g. past its edge), and a container-\n // scoped listener alone would silently stop tracking the drag at that\n // point. Coalesced through one `requestAnimationFrame` per frame — never a\n // raw `updateDrag` call per native `mousemove` event — which is the fix\n // for the exact perf bug this whole feature exists to replace (see\n // `useCellSelection.updateDrag`'s own doc: a hand-rolled excel-grid\n // implementation elsewhere in this org rebuilt its entire column-def array\n // on every unthrottled mousemove pixel during a drag-fill).\n const pendingDragCellRef = useRef<{ rowId: string; columnId: string } | null>(null);\n const dragRafRef = useRef<number | null>(null);\n useEffect(() => {\n if (!cellSelection.isDragging) return;\n function handleMouseMove(event: MouseEvent): void {\n const cellEl = (event.target as HTMLElement | null)?.closest<HTMLElement>(\"[data-cell-row]\");\n const rowId = cellEl?.dataset.cellRow;\n const columnId = cellEl?.dataset.cellCol;\n if (rowId === undefined || columnId === undefined) return;\n const anchor = clickCellRef.current;\n if (anchor && (rowId !== anchor.rowId || columnId !== anchor.columnId)) dragMovedRef.current = true;\n pendingDragCellRef.current = { rowId, columnId };\n if (dragRafRef.current !== null) return;\n dragRafRef.current = requestAnimationFrame(() => {\n dragRafRef.current = null;\n if (pendingDragCellRef.current) cellSelection.updateDrag(pendingDragCellRef.current);\n });\n }\n function handleMouseUp(): void {\n cellSelection.endDrag();\n const clickCell = clickCellRef.current;\n clickCellRef.current = null;\n if (!clickCell || clickCell.extend || clickCell.wasEditing || dragMovedRef.current) return;\n // Under `alwaysEdit`, `beginEditFromClick` has nothing to do (see\n // `editingCell`'s own doc: meaningless there) — the one thing a plain\n // click still needs to trigger explicitly is opening a built-in enum\n // widget's dropdown, since `AlwaysEditCell` unconditionally suppresses\n // its native open-on-click while closed (see `AtomicGestureContext`'s\n // own doc for why). Every other atomic type there already reacts to a\n // plain click as normal (only a modifier-held one is suppressed), so\n // there's nothing further to do for them here.\n if (cellEditingCtx?.alwaysEdit) {\n const column = columnById.get(clickCell.columnId);\n if (column?.type === \"enum\" && !column.renderEditCell) {\n setPendingOpenEnumCell({ rowId: clickCell.rowId, columnId: clickCell.columnId });\n }\n return;\n }\n beginEditFromClick(clickCell);\n }\n window.addEventListener(\"mousemove\", handleMouseMove);\n window.addEventListener(\"mouseup\", handleMouseUp);\n return () => {\n window.removeEventListener(\"mousemove\", handleMouseMove);\n window.removeEventListener(\"mouseup\", handleMouseUp);\n if (dragRafRef.current !== null) cancelAnimationFrame(dragRafRef.current);\n dragRafRef.current = null;\n };\n }, [cellSelection.isDragging, cellSelection.updateDrag, cellSelection.endDrag]);\n\n // Same rAF-coalesced window-listener recipe as the range-select drag\n // above, for the fill-handle's own separate drag mode — mutually\n // exclusive with it (the handle's own `onMouseDown` in `SelectionOverlay`\n // stops propagation specifically so this doesn't also start a range-select\n // drag on the cell underneath it).\n const pendingFillCellRef = useRef<{ rowId: string; columnId: string } | null>(null);\n const fillRafRef = useRef<number | null>(null);\n useEffect(() => {\n if (!cellSelection.isFillDragging) return;\n function handleMouseMove(event: MouseEvent): void {\n const cellEl = (event.target as HTMLElement | null)?.closest<HTMLElement>(\"[data-cell-row]\");\n const rowId = cellEl?.dataset.cellRow;\n const columnId = cellEl?.dataset.cellCol;\n if (rowId === undefined || columnId === undefined) return;\n pendingFillCellRef.current = { rowId, columnId };\n if (fillRafRef.current !== null) return;\n fillRafRef.current = requestAnimationFrame(() => {\n fillRafRef.current = null;\n if (pendingFillCellRef.current) cellSelection.updateFillDrag(pendingFillCellRef.current);\n });\n }\n function handleMouseUp(): void {\n const result = cellSelection.endFillDrag();\n if (!result || !cellEditingCtx) return;\n const changes = computeFillChanges(\n result.sourceRange,\n result.finalRange,\n cellEditingRowIds,\n cellEditingColumnIds,\n visibleColumns,\n findRowById,\n );\n if (changes.length > 0) applyCellChanges(changes);\n }\n window.addEventListener(\"mousemove\", handleMouseMove);\n window.addEventListener(\"mouseup\", handleMouseUp);\n return () => {\n window.removeEventListener(\"mousemove\", handleMouseMove);\n window.removeEventListener(\"mouseup\", handleMouseUp);\n if (fillRafRef.current !== null) cancelAnimationFrame(fillRafRef.current);\n fillRafRef.current = null;\n };\n // Deliberately NOT depending on `cellEditingCtx`/`applyCellChanges`/\n // `findRowById`/etc. — none of those have a stable identity across\n // renders (they're plain functions, not memoized), so including them\n // would resubscribe this effect's window listeners on every render\n // while a drag is in progress, not just when the drag itself starts or\n // stops. `handleMouseUp` still reads the current values of each via\n // closure at the moment it actually fires (mouseup), which is fresh\n // enough for everything except one narrow edge case: toggling\n // `cellEditing.disabled` mid-drag doesn't abort an already-started\n // fill-drag's eventual commit. Accepted as a known limitation rather\n // than reintroducing per-render effect churn to close it.\n }, [cellSelection.isFillDragging, cellSelection.updateFillDrag, cellSelection.endFillDrag]);\n\n // A cell's editor is a real DOM node (an `<input>`/`<textarea>`/etc.) that\n // unmounts the instant `editingCell` clears — commit, cancel, or a blur\n // that reverted an invalid draft. Removing a FOCUSED node doesn't move\n // focus anywhere in particular (typically `document.body`), so without\n // this, every one of those endings would silently strand keyboard focus\n // outside the grid: F2/arrow-key navigation right after committing a cell\n // would have nothing to bubble through, since the scroll container\n // (`handleCellKeyDown`'s listener) is no longer in the event's path at\n // all. `justEndedEditingRef` tracks the transition (defined -> undefined)\n // rather than firing on mount, when nothing was ever focused here yet.\n const wasEditingCellRef = useRef<CellAddress | undefined>(undefined);\n useEffect(() => {\n const wasEditing = wasEditingCellRef.current !== undefined;\n wasEditingCellRef.current = cellEditingCtx?.editingCell;\n if (!wasEditing || cellEditingCtx?.editingCell) return;\n // Only when focus landed nowhere in particular (the browser's default\n // once a focused node is removed) — a blur caused by clicking some OTHER\n // focusable element on the page (not this cell) already sent focus\n // there before this effect runs; grabbing it back here would undo that\n // deliberate navigation away from the grid.\n if (document.activeElement === document.body) scrollRef.current?.focus();\n });\n\n function handleFillHandleMouseDown(): void {\n cellSelection.startFillDrag();\n }\n\n function handleCellMouseDown(event: ReactMouseEvent<HTMLDivElement>): void {\n if (!hasCellEditing) return;\n const cellEl = (event.target as HTMLElement).closest<HTMLElement>(\"[data-cell-row]\");\n const rowId = cellEl?.dataset.cellRow;\n const columnId = cellEl?.dataset.cellCol;\n if (rowId === undefined || columnId === undefined) return;\n cellSelection.startSelection({ rowId, columnId }, { extend: event.shiftKey });\n // Recorded for the window `mouseup` handler above: a plain click (this\n // exact cell, not shift-extended, never dragged elsewhere) either opens\n // its editor (click-to-edit mode's Excel convention) or, under\n // `alwaysEdit`, opens a built-in enum widget's own dropdown (see\n // `AtomicGestureContext`'s own doc for why that needs a signal from\n // here rather than just Radix's native click handling). Reset per\n // mousedown, not per click, so a genuine drag never leaves a stale\n // \"moved\" flag around to falsely block the *next* plain click.\n // `wasEditing` must be read NOW, before `scrollRef.current?.focus()`\n // below has a chance to blur (and thus close) this very cell — see\n // `clickCellRef`'s own doc for why that makes this the only correct\n // moment to snapshot it.\n const wasEditing = cellEditingCtx?.editingCell?.rowId === rowId && cellEditingCtx.editingCell.columnId === columnId;\n clickCellRef.current = { rowId, columnId, extend: event.shiftKey, wasEditing };\n dragMovedRef.current = false;\n // Under `alwaysEdit`, every editable cell already has its own always-\n // mounted editor (`AlwaysEditCell`) — there's no click-to-edit gesture to\n // open via `scrollRef.current?.focus()` below, which would only fight\n // the click's own natural job of focusing whichever control it landed\n // on. Selection (for copy/paste/fill-handle) still works exactly the\n // same either way, via `startSelection` above; `clickCellRef` set just\n // above is still needed here, for `handleMouseUp`'s own atomic-open\n // decision.\n if (cellEditingCtx?.alwaysEdit) return;\n // A click landing INSIDE the cell that's already being edited (e.g. to\n // reposition the text cursor, or drag-select some of its own text) must\n // be left alone entirely — moving focus below would blur-and-close it\n // via `renderCellModeCell`'s own `onBlur`, which is exactly the \"commit\n // this and move on\" gesture a genuinely different cell's click means,\n // not what a click *within* the same still-open editor means.\n if (wasEditing) return;\n // The scroll container itself (not any cell) holds keyboard focus for\n // arrow/Tab/Enter navigation — cells are plain `<td>`s, not focusable\n // controls, so a click needs to explicitly move focus here. Also what\n // closes/commits a currently-open editor elsewhere on the grid: moving\n // focus here blurs that editor's `<input>`, synchronously (before this\n // handler returns) triggering `renderCellModeCell`'s own `onBlur`\n // commit/cancel.\n scrollRef.current?.focus();\n }\n\n const CELL_NAV_KEYS: Record<string, \"up\" | \"down\" | \"left\" | \"right\"> = {\n ArrowUp: \"up\",\n ArrowDown: \"down\",\n ArrowLeft: \"left\",\n ArrowRight: \"right\",\n };\n\n /** Looks up the actual row/column for a selected cell address, for the begin-edit triggers below — `undefined` if either no longer exists (e.g. the row was removed from `data` since the selection was made). */\n function resolveSelectedCell(): { column: ColumnDef<TRow>; row: TRow } | undefined {\n const focus = cellSelection.selection?.focus;\n if (!focus) return undefined;\n const column = columnById.get(focus.columnId);\n const row = findRowById(focus.rowId);\n if (!column || row === undefined) return undefined;\n return { column, row };\n }\n\n function handleCellKeyDown(event: ReactKeyboardEvent<HTMLDivElement>): void {\n if (!hasCellEditing || !cellEditingCtx) return;\n // Under `alwaysEdit`, focus normally lives inside one of the always-\n // mounted per-cell `<input>`s, not the scroll container — this whole\n // custom arrow/Tab/Enter-moves-the-selection model would fight native\n // text-cursor movement and native Tab order between real form controls.\n // `AlwaysEditCell` owns its own Escape-to-revert locally; there's nothing\n // left here for it to do.\n if (cellEditingCtx.alwaysEdit) return;\n // While a cell is being edited, its own wrapper (`renderCellModeCell`)\n // owns Escape/Enter/Tab — this handler only ever sees them AFTER that\n // one has already committed (or reverted) and let the event bubble; see\n // that function's own doc on how the two compose. `editingCell` alone\n // can't tell the two \"still editing\" cases apart here: it's React state,\n // so a commit that just fired via `closeEditor()` hasn't cleared it yet\n // in THIS same synchronous dispatch — `consumeJustCommitted()` is the\n // one signal that's already up to date at this point.\n if (cellEditingCtx.editingCell && !cellEditingCtx.consumeJustCommitted()) return;\n\n const direction = CELL_NAV_KEYS[event.key];\n if (direction) {\n event.preventDefault();\n cellSelection.moveSelection(direction, { extend: event.shiftKey });\n return;\n }\n if (event.key === \"Tab\") {\n event.preventDefault();\n cellSelection.moveSelection(event.shiftKey ? \"left\" : \"right\");\n return;\n }\n if (event.key === \"Enter\") {\n event.preventDefault();\n cellSelection.moveSelection(\"down\");\n return;\n }\n\n // Begin-edit triggers: F2 (start from the current value), or typing any\n // single printable character directly — Excel's own \"just start typing\"\n // gesture — which REPLACES the cell's current value with what was typed\n // rather than appending to it.\n const isPrintableChar = event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey;\n if (event.key !== \"F2\" && !isPrintableChar) return;\n const target = resolveSelectedCell();\n if (!target || !isEditable(target.column, target.row)) return;\n event.preventDefault();\n cellEditingCtx.onBeginEdit(\n { rowId: cellEditingCtx.getRowId(target.row), columnId: target.column.id },\n isPrintableChar ? event.key : undefined,\n );\n }\n\n // Mostly a backstop now that a single plain click already opens a cell's\n // editor (see `handleCellMouseDown`'s own doc): still needed for the case\n // where the first click of the pair landed as a drag/extend (so its own\n // mouseup didn't open anything) but the second one didn't move — reuses\n // `beginEditFromClick`'s own \"already editing this exact cell\" guard so it\n // can't clobber a draft the first click already opened.\n function handleCellDoubleClick(event: ReactMouseEvent<HTMLDivElement>): void {\n if (!hasCellEditing || !cellEditingCtx || cellEditingCtx.alwaysEdit) return;\n const cellEl = (event.target as HTMLElement).closest<HTMLElement>(\"[data-cell-row]\");\n const rowId = cellEl?.dataset.cellRow;\n const columnId = cellEl?.dataset.cellCol;\n if (rowId === undefined || columnId === undefined) return;\n beginEditFromClick({ rowId, columnId });\n }\n\n function handleCopy(event: ReactClipboardEvent<HTMLDivElement>): void {\n // While a cell is actively being edited, a copy targets whatever text is\n // selected inside its own `<input>` — ordinary browser behavior, not a\n // range-copy of the whole selection. `isNativeTextFieldTarget` covers the\n // same case under `alwaysEdit`, where every editable cell's `<input>` is\n // always mounted (so there's no single `editingCell` to check) — an\n // atomic editor (enum/boolean/date) is a button/listbox, not a real text\n // field, so it's untouched by that check and still gets the range-copy.\n if (!hasCellEditing || !cellSelection.selection || cellEditingCtx?.editingCell || isNativeTextFieldTarget(event)) return;\n event.preventDefault();\n const text = rangeToTsv(cellSelection.selection, rows, cellEditingRowIds, visibleColumns, cellEditingColumnIds, getRowId);\n event.clipboardData.setData(\"text/plain\", text);\n }\n\n /** Builds one `CellChange` for `(row, col)` from `raw` pasted/filled text, or `undefined` to skip that cell — not editable, no longer exists, or `raw` doesn't coerce to that column's type (see `coerceValueForColumn`'s own doc). */\n function buildCellChange(row: number, col: number, raw: string): CellChange<TRow> | undefined {\n const rowId = cellEditingRowIds[row];\n const columnId = cellEditingColumnIds[col];\n if (rowId === undefined || columnId === undefined) return undefined;\n const column = columnById.get(columnId);\n const rowData = findRowById(rowId);\n if (!column || rowData === undefined || !isEditable(column, rowData)) return undefined;\n const coerced = coerceValueForColumn(column, raw);\n if (!coerced) return undefined;\n return { rowId, row: rowData, columnId, previousValue: getColumnValue(column, rowData), value: coerced.value };\n }\n\n function handlePaste(event: ReactClipboardEvent<HTMLDivElement>): void {\n // While a cell is actively being edited, a paste goes into its own\n // `<input>` — ordinary browser behavior, not a range-paste. Same\n // `alwaysEdit` nuance as `handleCopy` above.\n if (!hasCellEditing || !cellSelection.selection || cellEditingCtx?.editingCell || isNativeTextFieldTarget(event)) return;\n // Not gated on `text` being non-empty: the Clipboard API can't\n // distinguish \"nothing on the clipboard\" from \"an empty string was\n // copied\" (`getData` returns `\"\"` either way) — treating it as the\n // latter and clearing the selection matches how pasting a copied blank\n // cell behaves in a real spreadsheet, rather than silently doing nothing.\n const text = event.clipboardData.getData(\"text/plain\");\n event.preventDefault();\n const normalized = normalizeRange(cellSelection.selection, cellEditingRowIndex, buildIndexMap(cellEditingColumnIds));\n if (!normalized) return;\n const parsed = parseTsv(text);\n const changes: CellChange<TRow>[] = [];\n // A single copied value fills every selected cell (Excel's own\n // behavior for pasting one value onto a multi-cell selection); a real\n // multi-cell block instead anchors at the selection's top-left corner\n // and extends to match the pasted shape, clipped to grid bounds.\n if (parsed.length === 1 && parsed[0]?.length === 1) {\n const raw = parsed[0]![0]!;\n for (let r = normalized.rowStart; r <= normalized.rowEnd; r++) {\n for (let c = normalized.colStart; c <= normalized.colEnd; c++) {\n const change = buildCellChange(r, c, raw);\n if (change) changes.push(change);\n }\n }\n } else {\n for (let ri = 0; ri < parsed.length; ri++) {\n const line = parsed[ri]!;\n for (let ci = 0; ci < line.length; ci++) {\n const change = buildCellChange(normalized.rowStart + ri, normalized.colStart + ci, line[ci]!);\n if (change) changes.push(change);\n }\n }\n }\n if (changes.length > 0) applyCellChanges(changes);\n }\n\n function renderRow(\n row: (typeof tableRows)[number],\n measureRef?: (el: Element | null) => void,\n // Position within the flattened, virtualized render list (`flatItems`) —\n // distinct from `row.index` (this row's position within `tableRows`\n // alone) once `groupBy` interleaves header entries: `@tanstack/react-\n // virtual`'s `measureElement` reads the rendered node's `data-index`\n // attribute back to know which measurement slot it just measured (see\n // `<TreeDataGrid>`'s identical `data-index` doc for the same\n // requirement), so this MUST match the row's actual `flatItems` index\n // whenever virtualized. Defaults to `row.index`, which is already\n // correct for every non-grouped call site (virtualized or not) and for\n // every non-virtualized call site (the attribute is simply inert then).\n dataIndex: number = row.index,\n ): 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={dataIndex}>\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\n key={cell.id}\n style={cellProps?.style}\n className={cellProps?.className ?? BODY_TD_BASE_CLASS}\n // Only present under `cellEditing` — the anchor `handleCellMouseDown`/\n // the window drag listener/`SelectionOverlay` all look these up by\n // attribute selector; see `useCellSelection`'s own doc for why\n // row-id/column-id (not DOM index) is the addressing scheme.\n data-cell-row={hasCellEditing ? row.id : undefined}\n data-cell-col={hasCellEditing ? cell.column.id : undefined}\n >\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 `<td>` per visible column for the (non-sticky) per-group summary\n // row, each running that column's own `summary(rows)` (blank for a column\n // with none). `bgClassName` matches the label row above it\n // (`zebra ? \"bg-muted\" : \"bg-background\"`) so the two rows read as one\n // block. `showTotals`'s own `<tfoot>` needs different per-cell treatment\n // (sticky-to-bottom, always `bg-muted`) and builds its cells directly in\n // `renderTotalsFooter` instead of through this helper. Reuses\n // `bodyCellPropsByColumn` (pinning/resizing) rather than recomputing it,\n // same as every actual data-row cell.\n function renderGroupSummaryCells(rows: TRow[], bgClassName: string): ReactNode {\n return visibleColumns.map((column) => {\n const cellProps = bodyCellPropsByColumn.get(column.id);\n return (\n <td\n key={column.id}\n className={cn(cellProps?.className, bgClassName, \"font-medium\", alignClassName(column))}\n style={cellProps?.style}\n >\n {column.summary?.(rows)}\n </td>\n );\n });\n }\n\n // The leading detail/selection and trailing row-actions columns get one\n // blank filler cell each (matching `totalColumnCount`) rather than their\n // own pinned/interactive treatment -- there's no per-row checkbox/expand\n // affordance to show in a summary row. Unlike a real body row's own\n // pinned structural cells, these fillers aren't independently sticky-left/\n // right themselves; harmless while blank (nothing to misalign), but a\n // grid combining `enableColumnResizing`-style side pinning with a\n // `summary` column AND horizontal scroll could see this filler scroll out\n // ahead of a still-pinned data column. Documented as a known gap rather\n // than solved here, same tolerance the module's `groupBy`/`zebra` striping\n // gap already has (see AGENTS.md's \"Known limitations\").\n function summaryLeadingCellSpan(): number {\n return (showDetailColumn ? 1 : 0) + (showSelectionColumn ? 1 : 0);\n }\n\n function renderGroupSummaryRow(rows: TRow[]): ReactNode {\n const leadingSpan = summaryLeadingCellSpan();\n const bgClassName = zebra ? \"bg-muted\" : \"bg-background\";\n const fillerClass = cn(\"border-b border-border p-2\", bgClassName);\n return (\n <tr data-testid=\"group-summary-row\" className=\"divide-x divide-border\">\n {leadingSpan > 0 && <td colSpan={leadingSpan} className={fillerClass} />}\n {renderGroupSummaryCells(rows, bgClassName)}\n {showRowActionsColumn && <td className={fillerClass} aria-hidden />}\n </tr>\n );\n }\n\n // `showTotals`'s grand-total row -- sticky to the BOTTOM of the scroll\n // container (`bottom-0`, mirroring the header's own `top-0` stickiness),\n // so it stays visible while scrolling a tall grid instead of requiring a\n // scroll all the way down to see it, exactly the UX a \"totals row\"\n // exists for. `<tfoot>` renders after every `<tbody>` in source order\n // here, but HTML/CSS table layout always places a `<tfoot>` visually at\n // the bottom of the table regardless of where it appears in markup.\n function renderTotalsFooter(): ReactNode {\n if (!showTotals || !hasColumnSummary) return null;\n const summaryRows = tableRows.map((row) => row.original);\n const leadingSpan = summaryLeadingCellSpan();\n const fillerClass = \"sticky bottom-0 z-20 border-t border-border bg-muted p-2\";\n return (\n <tfoot>\n <tr data-testid=\"totals-row\" className=\"divide-x divide-border\">\n {leadingSpan > 0 && <td colSpan={leadingSpan} className={fillerClass} />}\n {visibleColumns.map((column) => {\n const pinnedProps = pinnedCellProps(column, \"header\");\n return (\n <td\n key={column.id}\n className={cn(\n \"border-t border-border p-2 font-medium\",\n alignClassName(column),\n pinnedProps.className,\n \"sticky bottom-0 z-20 bg-muted\",\n )}\n style={pinnedProps.style}\n >\n {column.summary?.(summaryRows)}\n </td>\n );\n })}\n {showRowActionsColumn && <td className={fillerClass} aria-hidden />}\n </tr>\n </tfoot>\n );\n }\n\n // Its own `<tbody>` (not nested inside anything else, matching every\n // per-row `<tbody>` from `renderRow`) so `measureRef`/`data-index` work the\n // exact same way a data row's does once virtualized -- shared by the\n // non-virtualized grouped path (`renderGroupedBucket`) and the virtualized\n // one (`renderFlatItem`), so a header's rendered markup can't drift\n // between the two.\n function renderGroupHeaderTbody(\n key: string,\n rows: TRow[],\n measureRef?: (el: Element | null) => void,\n dataIndex?: number,\n ): ReactNode {\n const expanded = isGroupExpanded(key);\n return (\n <tbody key={`group-${key}`} ref={measureRef} data-index={dataIndex}>\n <tr data-testid={`group-header-${key}`}>\n <td\n colSpan={totalColumnCount}\n className={cn(\"sticky z-20 border-b p-2 font-medium\", zebra ? \"bg-muted\" : \"bg-background\")}\n style={{ top: groupHeaderTop }}\n >\n <button\n type=\"button\"\n className=\"flex w-full items-center gap-1 text-left\"\n onClick={() => toggleGroupExpanded(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 ? renderGroupHeader(key, rows, expanded) : `${key} (${rows.length})`}\n </button>\n </td>\n </tr>\n {/* Not sticky, unlike the label row above -- it scrolls away with\n this group's own member rows once its `<tbody>` scrolls past the\n real `<thead>`, same as any other non-pinned content underneath a\n sticky element. Rendered regardless of `expanded`, which is the\n entire point: a collapsed group's per-column subtotal is often\n the only reason to collapse it in the first place. */}\n {hasColumnSummary && renderGroupSummaryRow(rows)}\n </tbody>\n );\n }\n\n // One `<tbody>` for the group-header row (via `renderGroupHeaderTbody`),\n // then each member row as its own sibling `<tbody>` (via `renderRow`) — a\n // `<tbody>` can't nest inside another, and a `<table>` happily holds any\n // number of them. Used only in the non-virtualized grouped path;\n // `renderFlatItem` below covers the same two shapes one flattened item at\n // a time once virtualized.\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 {renderGroupHeaderTbody(bucket.key, originalRows)}\n {expanded && bucket.items.map((row) => renderRow(row))}\n </Fragment>\n );\n }\n\n // Dispatches one `flatItems` entry to whichever of `renderGroupHeaderTbody`\n // / `renderRow` its `kind` needs — the virtualized render loop's per-item\n // callback, so it can stay a plain one-line `.map()` below.\n function renderFlatItem(\n item: FlatItem<TRow>,\n index: number,\n measureRef?: (el: Element | null) => void,\n ): ReactNode {\n if (item.kind === \"header\") return renderGroupHeaderTbody(item.key, item.rows, measureRef, index);\n return renderRow(item.row, measureRef, index);\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={cn(\"h-full overflow-auto rounded-md border\", hasCellEditing && \"relative\")}\n style={shouldVirtualize ? { maxHeight: virtualize?.maxBodyHeight ?? 480 } : undefined}\n // `tabIndex`/`onKeyDown` only under `cellEditing` — the scroll\n // container itself (not any individual cell, which is a plain\n // `<td>`, not a focusable control) holds keyboard focus for\n // arrow/Tab/Enter navigation between cells.\n tabIndex={hasCellEditing ? 0 : undefined}\n onMouseDown={hasCellEditing ? handleCellMouseDown : undefined}\n onDoubleClick={hasCellEditing ? handleCellDoubleClick : undefined}\n onKeyDown={hasCellEditing ? handleCellKeyDown : undefined}\n onCopy={hasCellEditing ? handleCopy : undefined}\n onPaste={hasCellEditing ? handlePaste : 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 const headerLabelId = `${header.id}-label`;\n return (\n <th\n key={header.id}\n rowSpan={rowSpan}\n style={cellProps?.style}\n className={cellProps?.className}\n aria-sort={sortable ? sortDirToAriaSort(sortEntry?.dir) : undefined}\n // Named via aria-labelledby pointing at the headerLabelId element below,\n // not name-from-content on the <th> itself: without it, the filter\n // trigger's own aria-label and the resize handle's `aria-label` (added\n // for keyboard support below) would otherwise get concatenated into this\n // header cell's computed accessible name too. Deliberately NOT a plain\n // `aria-label={column.header}` override, though — `column.header` is a\n // plain string required for things like width-estimation heuristics, but\n // a column can render genuinely richer content via `renderHeader` (e.g. a\n // two-line combined label for two related fields sharing one column) whose\n // full text belongs in the accessible name too, not just `column.header`.\n // aria-labelledby lets the labelled element's own name-from-content do\n // that correctly while still excluding the filter/resize controls, which\n // live outside it as siblings.\n aria-labelledby={headerLabelId}\n >\n <div className=\"flex items-center gap-1\">\n {sortable ? (\n <button\n type=\"button\"\n id={headerLabelId}\n data-testid={`sort-button-${column.id}`}\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 id={headerLabelId} className=\"flex items-center gap-1 px-1\">\n {headerContent}\n </div>\n )}\n {filterable && (\n <Popover>\n <PopoverTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n data-testid={`filter-trigger-${column.id}`}\n aria-label={`Filter ${column.header}`}\n >\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 role=\"separator\"\n aria-orientation=\"vertical\"\n aria-label={`Resize ${column.header} column`}\n tabIndex={0}\n onMouseDown={header.getResizeHandler()}\n onTouchStart={header.getResizeHandler()}\n onKeyDown={(event) => {\n if (event.key !== \"ArrowLeft\" && event.key !== \"ArrowRight\") return;\n event.preventDefault();\n const delta = event.key === \"ArrowRight\" ? 10 : -10;\n const nextSize = Math.max(MIN_COLUMN_SIZE, columnSize(column.id) + delta);\n updateColumnSizing({ ...columnSizing, [column.id]: nextSize });\n }}\n className=\"absolute right-0 top-0 h-full w-1 cursor-col-resize touch-none select-none hover:bg-accent focus-visible:bg-accent focus-visible:outline-none\"\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 renderFlatItem(flatItems[virtualItem.index]!, virtualItem.index, (el) =>\n virtualizer.measureElement(el),\n ),\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 {renderTotalsFooter()}\n </table>\n {hasCellEditing && (\n <SelectionOverlay\n containerRef={scrollRef}\n range={cellSelection.effectiveRange}\n fillPreviewRange={cellSelection.fillPreviewRange}\n onFillHandleMouseDown={\n cellSelection.selection && !cellSelection.isDragging && !cellEditingCtx?.editingCell\n ? handleFillHandleMouseDown\n : undefined\n }\n />\n )}\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","import {\n useEffect,\n useState,\n type KeyboardEvent as ReactKeyboardEvent,\n type MouseEvent as ReactMouseEvent,\n type PointerEvent as ReactPointerEvent,\n type ReactNode,\n} from \"react\";\nimport { defaultFormat } from \"../column/format\";\nimport type { ColumnDef } from \"../column/types\";\nimport { isEditable } from \"../column/types\";\nimport { EnumEditor } from \"../edit/EnumEditor\";\nimport { renderDefaultEditWidget } from \"../edit/registry\";\nimport { isAtomicEditorType } from \"./renderCellModeCell\";\nimport type { CellEditingCellContext } from \"./useCellEditingState\";\n\n/**\n * Bridges an atomic (enum/boolean/date/datetime) `AlwaysEditCell`'s own\n * gesture handling back into `<DataGrid>`'s shared cell-selection state —\n * needed only because these cells' widgets are permanently-mounted, LIVE\n * controls (a Radix `<Select>` trigger, a checkbox), unlike a non-atomic\n * editor's plain `<input>`, so a mouse gesture that's supposed to just\n * select the cell (shift+click, or a drag) can otherwise be swallowed or\n * misinterpreted by the widget's own native mouse handling before\n * `<DataGrid>`'s own delegated `onMouseDown` ever sees it. See\n * `AlwaysEditCell`'s own capture-phase handlers for the mechanics.\n */\nexport interface AtomicGestureContext {\n /**\n * True for exactly the one built-in enum (`<Select>`) cell that should\n * open its dropdown now. Set by `<DataGrid>`'s own mouseup handler the\n * instant a mousedown/mouseup pair on it resolves to a plain,\n * non-extended, non-dragged click — the only case its native\n * open-on-pointerdown/click is supposed to happen at all.\n *\n * This exists because `AlwaysEditCell` unconditionally suppresses that\n * native behavior while a built-in enum widget is closed (see its own\n * `onPointerDownCapture` doc): unlike the other atomic types, a `<Select>`\n * also supports a native \"press, drag to an option, release to pick it\"\n * gesture — letting it open immediately on ANY mousedown (as Radix does\n * by default) means a drag that merely passes through its now-open\n * popover can silently commit the wrong option as this cell's value,\n * not just steal the range-select gesture. A plain click needs this\n * signal as its own way back in once that's blocked.\n */\n shouldOpenEnum: (rowId: string, columnId: string) => boolean;\n /**\n * Registers this gesture's selection directly. Required because\n * `AlwaysEditCell` suppresses a widget's native pointerdown handling by\n * calling `preventDefault()` on it — and per the Pointer Events spec,\n * that ALSO cancels the browser's own compatibility `mousedown` event for\n * this same gesture, so `<DataGrid>`'s own delegated `onMouseDown`\n * handler (which is what normally calls this) never runs for it at all.\n */\n registerSelection: (rowId: string, columnId: string, extend: boolean) => void;\n}\n\n/**\n * One in-progress, uncommitted edit this component started itself.\n * `baseline` is `resolvedValue` at the moment the draft was last updated —\n * the same freshness convention `useCellEditingState`'s own `CellOverride`\n * uses, applied locally: as long as `resolvedValue` still equals `baseline`,\n * nothing else has touched this cell since, so `value` is still the right\n * thing to show/commit. The instant `resolvedValue` diverges from\n * `baseline` (a fill-drag or paste landed on this exact cell while a draft\n * was in progress), the draft is stale and must lose to that newer value —\n * committing it anyway would silently revert whatever just landed.\n */\ninterface LocalDraft {\n value: unknown;\n baseline: unknown;\n}\n\n/**\n * Renders one cell under `cellEditing.alwaysEdit` mode: every editable cell\n * gets its own permanently-mounted editor, all simultaneously — unlike\n * `renderCellModeCell`'s single shared `editingCell` slot (at most one editor\n * open at a time), this component owns its OWN local draft state, since with\n * every cell independently editable there's no single \"the\" cell to hang a\n * shared draft off of. A buffered (string/number/currency) editor still only\n * commits on blur/Enter/Tab, same as click-to-edit mode — this only changes\n * WHEN the editor first appears (always, not on click/double-click/F2/typed\n * character), not how a buffered edit gets committed.\n */\nexport function AlwaysEditCell<TRow>(props: {\n column: ColumnDef<TRow>;\n row: TRow;\n rawValue: unknown;\n ctx: CellEditingCellContext<TRow>;\n /** Only meaningful for an atomic column type — see `AtomicGestureContext`'s own doc. */\n atomicGesture?: AtomicGestureContext;\n}): ReactNode {\n const { column, row, rawValue, ctx, atomicGesture } = props;\n const rowId = ctx.getRowId(row);\n const resolvedValue = ctx.resolveValue(rowId, column.id, rawValue);\n // Called unconditionally, before the `isEditable` early return below —\n // `column.editable` can be a per-row predicate (see `isEditable` in\n // `column/types.ts`), so whether this cell renders an editor at all can\n // differ between renders of the very same component instance; a hook\n // can't sit after a conditional return that's conditional on that.\n const [draft, setDraft] = useState<LocalDraft | undefined>(undefined);\n // The built-in enum widget's own open/closed state, owned here (not by\n // `<Select>` itself) specifically so a mousedown/click can be\n // unconditionally suppressed while it's closed — see `AtomicGestureContext`'s\n // own doc for why. Irrelevant (never read) for every other column type.\n const [enumOpen, setEnumOpen] = useState(false);\n const isBuiltinEnum = column.type === \"enum\" && !column.renderEditCell;\n const shouldOpenEnumNow = isBuiltinEnum && (atomicGesture?.shouldOpenEnum(rowId, column.id) ?? false);\n // Opens it exactly once per `<DataGrid>`-confirmed plain click — this\n // effect only re-runs when `shouldOpenEnumNow` itself flips, not on every\n // render, so it doesn't fight a subsequent manual close (clicking the\n // trigger again, picking an option, Escape, clicking away all update\n // `enumOpen` via `onOpenChange` below, unaffected by this).\n useEffect(() => {\n if (shouldOpenEnumNow) setEnumOpen(true);\n }, [shouldOpenEnumNow]);\n\n if (!isEditable(column, row)) {\n return (\n <span data-testid={`cell-${rowId}-${column.id}`} className=\"block w-full\">\n {column.cell ? column.cell(resolvedValue, row) : defaultFormat(column, resolvedValue)}\n </span>\n );\n }\n\n const isAtomic = isAtomicEditorType(column.type);\n const error = ctx.getError(rowId, column.id);\n // Fresh only while `resolvedValue` still matches what it was when `draft`\n // was last updated — see `LocalDraft`'s own doc. A stale draft is treated\n // as if there were none: `currentValue` falls through to `resolvedValue`,\n // and the commit paths below refuse to commit it. Left in state rather\n // than cleared outright — recomputing this each render is enough to keep\n // display/commit correct without an extra render just to null it out.\n const draftIsFresh = draft !== undefined && resolvedValue === draft.baseline;\n const currentValue = draftIsFresh ? draft!.value : resolvedValue;\n\n function validate(candidate: unknown): string | undefined {\n return column.validateEdit?.(candidate, row);\n }\n\n function commit(value: unknown): void {\n ctx.applyChange({ rowId, row, columnId: column.id, previousValue: resolvedValue, value });\n setDraft(undefined);\n }\n\n const onChange = (next: unknown): void => {\n const message = validate(next);\n ctx.onSetError(rowId, column.id, message);\n if (isAtomic && !message) {\n commit(next);\n return;\n }\n setDraft({ value: next, baseline: resolvedValue });\n };\n\n function handleKeyDown(event: ReactKeyboardEvent): void {\n if (event.key === \"Escape\") {\n event.stopPropagation();\n ctx.onSetError(rowId, column.id, undefined);\n setDraft(undefined);\n return;\n }\n const isMultilineNewline = column.type === \"string\" && column.multiline && event.key === \"Enter\" && event.shiftKey;\n if (isMultilineNewline) return;\n if (isAtomic || (event.key !== \"Enter\" && event.key !== \"Tab\")) return;\n if (!draftIsFresh) return; // nothing (still) pending — no-op close, matches renderCellModeCell's own onCommitEdit convention\n const message = validate(draft!.value);\n if (message) {\n event.stopPropagation();\n return;\n }\n commit(draft!.value);\n }\n\n function handleBlur(): void {\n if (isAtomic || !draftIsFresh) return;\n if (validate(draft!.value)) setDraft(undefined);\n else commit(draft!.value);\n }\n\n // `stopPropagation()` alone does NOT stop Radix's own handlers on the\n // widget from still firing here — React dispatches capture- and\n // bubble-phase handlers off separate root-level native listeners, so a\n // synthetic `stopPropagation()` called from a CAPTURE handler only halts\n // React's own capture-side traversal, not the independent bubble-side one\n // that would still reach the widget's own bubble-phase prop. Only\n // stopping the underlying native event itself reliably prevents that\n // (verified against a live Radix `<Select>` trigger). Shared by both\n // capture handlers below. Returns `true` when it actually suppressed\n // anything, so callers know whether to also call `registerSelection`.\n function suppressNative(event: ReactPointerEvent | ReactMouseEvent): boolean {\n event.stopPropagation();\n event.nativeEvent.stopImmediatePropagation();\n return true;\n }\n\n // Modifier-only interception, for atomic widgets OTHER than the built-in\n // enum editor (which gets the stronger, always-while-closed interception\n // below instead): a shift/ctrl/meta-held click meant to just select the\n // cell would otherwise still reach the widget's own native click handling\n // (a checkbox's toggle, or focusing a native date input) — Radix/native\n // controls don't know to treat those modifiers as \"don't act, just\n // select\" the way this grid does. These don't cover nearby rows with a\n // draggable popover the way `<Select>` does, so a PLAIN drag starting on\n // one of them already resolves correctly with no interception needed at\n // all — nothing here ever eclipses `event.target` during it.\n function handleModifierCapture(event: ReactPointerEvent | ReactMouseEvent): boolean {\n if (!isAtomic || isBuiltinEnum) return false;\n if (!(event.shiftKey || event.ctrlKey || event.metaKey)) return false;\n return suppressNative(event);\n }\n\n // Unconditional (any gesture, not just modifier-held) interception for a\n // CLOSED built-in enum editor — see `AtomicGestureContext.shouldOpenEnum`'s\n // own doc for why a plain click can't just be let through immediately the\n // way it can for the other atomic types above: a `<Select>` that's\n // allowed to open on ANY plain mousedown also lets a drag starting on it\n // silently commit whichever option the drag happens to end up over.\n // Once open, this stops intercepting entirely — closing it again (click\n // the trigger, pick an option, click away, Escape) is left to Radix's own\n // normal behavior, via `onOpenChange={setEnumOpen}` below.\n function handleEnumOpenCapture(event: ReactPointerEvent | ReactMouseEvent): boolean {\n if (!isBuiltinEnum || enumOpen) return false;\n return suppressNative(event);\n }\n\n // Pointerdown, not just click: this is also where a suppressed gesture's\n // selection gets registered (see `AtomicGestureContext.registerSelection`'s\n // own doc for why `<DataGrid>`'s own mousedown handler can't be trusted to\n // still run on its own here) — pointerdown, rather than the later click,\n // since a real drag never produces a `click` at all (mouseup lands on a\n // different cell than mousedown), but still needs its start cell\n // registered as the selection's anchor.\n function handlePointerDownCapture(event: ReactPointerEvent): void {\n const suppressed = handleModifierCapture(event) || handleEnumOpenCapture(event);\n if (suppressed) atomicGesture?.registerSelection(rowId, column.id, event.shiftKey);\n }\n\n // A SEPARATE interception from the pointerdown one above, and just as\n // required: Radix's `<Select>` trigger also opens from its own `onClick`\n // handler, but only when `pointerType !== \"mouse\"` (its own touch\n // fallback) — and that internal pointer-type tracking is itself only\n // ever updated inside the very `onPointerDown` handler this file\n // suppresses, so once that's blocked, the trigger's `pointerType` gets\n // stuck at Radix's own initial default (\"touch\"), and its `onClick`\n // fallback opens the dropdown anyway on the ensuing native `click`\n // (mousedown+mouseup on the same target) if this isn't ALSO suppressed.\n // No `registerSelection` here — `handlePointerDownCapture` above already\n // covers it, and click fires (if at all) strictly after pointerdown for\n // the same gesture.\n function handleClickCapture(event: ReactMouseEvent): void {\n handleModifierCapture(event);\n handleEnumOpenCapture(event);\n }\n\n return (\n <fieldset\n disabled={ctx.disabled}\n onKeyDown={handleKeyDown}\n onBlur={handleBlur}\n onPointerDownCapture={handlePointerDownCapture}\n onClickCapture={handleClickCapture}\n className=\"contents\"\n >\n {column.type === \"enum\" && !column.renderEditCell ? (\n <EnumEditor\n column={column}\n rowId={rowId}\n value={currentValue}\n onChange={onChange}\n error={error}\n autoFocus={false}\n open={enumOpen}\n onOpenChange={setEnumOpen}\n />\n ) : column.renderEditCell ? (\n column.renderEditCell(currentValue, row, onChange, error, false)\n ) : (\n renderDefaultEditWidget(column, rowId, currentValue, onChange, error, false)\n )}\n </fieldset>\n );\n}\n","import { useCallback, useState } from \"react\";\n\nexport interface GroupExpansionController {\n /** The resolved (controlled-or-internal) expand state, keyed by group key. */\n expandedGroups: Record<string, boolean>;\n /** A key absent from `expandedGroups` (a group never toggled since it was first seen) falls back to `defaultGroupsExpanded`, not `false`. */\n isGroupExpanded: (key: string) => boolean;\n toggleGroupExpanded: (key: string) => void;\n}\n\n/**\n * Same controlled/uncontrolled pattern as `<DataGrid>`'s/`<TreeDataGrid>`'s\n * own `selectedIds`/`columnSizing`: both `controlledExpandedGroups` and\n * `onExpandedGroupsChange` must be supplied together to actually control\n * this state — omit both to let this hook own it internally. Shared between\n * the two grids' `groupBy` support so a fix to the fallback/toggle logic\n * never has to be made twice.\n *\n * `isGroupExpanded`/`toggleGroupExpanded` are stable (`useCallback`-wrapped)\n * across renders as long as `expandedGroups`/`defaultGroupsExpanded`/\n * `onExpandedGroupsChange` don't change — callers can safely list them in a\n * `useMemo`/`useEffect` dependency array without defeating memoization.\n */\nexport function useGroupExpansion(\n defaultGroupsExpanded: boolean,\n controlledExpandedGroups: Record<string, boolean> | undefined,\n onExpandedGroupsChange: ((expanded: Record<string, boolean>) => void) | undefined,\n): GroupExpansionController {\n const [internalExpandedGroups, setInternalExpandedGroups] = useState<Record<string, boolean>>({});\n const expandedGroups = controlledExpandedGroups ?? internalExpandedGroups;\n\n const updateExpandedGroups = useCallback(\n (next: Record<string, boolean>): void => {\n setInternalExpandedGroups(next);\n onExpandedGroupsChange?.(next);\n },\n [onExpandedGroupsChange],\n );\n\n const isGroupExpanded = useCallback(\n (key: string): boolean => expandedGroups[key] ?? defaultGroupsExpanded,\n [expandedGroups, defaultGroupsExpanded],\n );\n\n const toggleGroupExpanded = useCallback(\n (key: string): void => {\n updateExpandedGroups({ ...expandedGroups, [key]: !isGroupExpanded(key) });\n },\n [expandedGroups, isGroupExpanded, updateExpandedGroups],\n );\n\n return { expandedGroups, isGroupExpanded, toggleGroupExpanded };\n}\n","import type { RefObject } from \"react\";\nimport { useLayoutEffect, useRef, useState } from \"react\";\n\nexport interface StickyGroupHeaderTop {\n /** Attach to the grid's own `<thead>`. */\n theadRef: RefObject<HTMLTableSectionElement>;\n /** The measured `<thead>` height in pixels — `0` until first measured, or whenever `enabled` is false. */\n groupHeaderTop: number;\n}\n\n/**\n * Measures the real header height so a sticky group-header row can sit\n * exactly below it (its own `top` style) while member rows scroll past\n * underneath. `ResizeObserver` rather than a one-off measurement since\n * header height can change after mount (e.g. a column's header content\n * wrapping to a second line, or a resizable column changing the header\n * row's layout). Shared between `<DataGrid>` and `<TreeDataGrid>`'s own\n * `groupBy` support.\n */\nexport function useStickyGroupHeaderTop(enabled: boolean): StickyGroupHeaderTop {\n const theadRef = useRef<HTMLTableSectionElement>(null);\n const [groupHeaderTop, setGroupHeaderTop] = useState(0);\n useLayoutEffect(() => {\n if (!enabled) 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 }, [enabled]);\n return { theadRef, groupHeaderTop };\n}\n","import { useMemo } from \"react\";\nimport type { ColumnDef } from \"../column/types\";\nimport type { ColumnVisibility } from \"../column-selector/types\";\nimport { isColumnVisible } from \"../column-selector/visibility\";\n\n/**\n * Single source of truth for \"which columns actually render\" — filters\n * `columns` down to whatever `columnVisibility` marks visible, or returns\n * `columns` unchanged when `columnVisibility` is omitted. Shared between\n * `<DataGrid>` and `<TreeDataGrid>` so both read `columnVisibility` (e.g.\n * driven by a `<ColumnSelector>`) the same way.\n */\nexport function useVisibleColumns<TRow>(\n columns: ColumnDef<TRow>[],\n columnVisibility: ColumnVisibility | undefined,\n): ColumnDef<TRow>[] {\n return useMemo(\n () =>\n columnVisibility ? columns.filter((column) => isColumnVisible(columnVisibility, column.id)) : columns,\n [columns, columnVisibility],\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 `dataSource.onStateChange` doesn't exist and\n * is never called; the grid re-filters/re-sorts the in-memory array itself\n * instead. `onGridStateChange` (see its own doc on `DataGridProps`) gets the\n * same notifications regardless of mode, for callers that just want to\n * observe — e.g. a `\"client\"`-mode grid with no other way to know its\n * current filter/sort/page from outside.\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 *\n * `showPagination`, when `false` and the caller did NOT explicitly provide\n * `initialState.pageSize`, seeds `pageSize` as effectively unbounded instead\n * of `DEFAULT_STATE`'s 20 — \"hide the pagination controls\" means \"no\n * pagination\" by default, matching `<DataGrid>`'s `showPagination` doc. A\n * caller who explicitly sets `initialState.pageSize` (deliberate fixed-size\n * chunking with no UI, alongside `showPagination: false`) still gets exactly\n * that value; this only changes what happens when they didn't ask for a\n * particular page size at all. Has no effect once `externalState` is in\n * play — a controlled `GridState` is always fully caller-specified.\n */\nexport function useGridState<TRow>(\n dataSource: DataSource<TRow>,\n initialState?: Partial<GridState>,\n externalState?: GridState,\n showPagination = true,\n onGridStateChange?: (state: GridState) => void,\n): GridStateController {\n const [state, setState] = useState<GridState>(() => {\n if (externalState) return externalState;\n const pageSize =\n initialState?.pageSize ?? (showPagination ? DEFAULT_STATE.pageSize : Number.MAX_SAFE_INTEGER);\n return { ...DEFAULT_STATE, ...initialState, pageSize };\n });\n\n const notifyRaw = useCallback(\n (next: GridState) => {\n if (dataSource.mode === \"server\") dataSource.onStateChange(next);\n onGridStateChange?.(next);\n },\n [dataSource, onGridStateChange],\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 { Fragment, 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 { groupRows } from \"../grid/groupRows\";\nimport { useGroupExpansion } from \"../hooks/useGroupExpansion\";\nimport { useStickyGroupHeaderTop } from \"../hooks/useStickyGroupHeaderTop\";\nimport { useVisibleColumns } from \"../hooks/useVisibleColumns\";\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 loading = false,\n treeColumnId,\n columnVisibility,\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 testId,\n selectedIds: controlledSelectedIds,\n onSelectedIdsChange,\n getRowSelectionState,\n isRowSelectionDisabled,\n groupBy,\n renderGroupHeader,\n defaultGroupsExpanded = true,\n expandedGroups: controlledExpandedGroups,\n onExpandedGroupsChange,\n zebra = true,\n showTotals = false,\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 // Single source of truth for \"which columns actually render\", same\n // convention as `<DataGrid>`'s own `visibleColumns` — every other\n // representation below (the header/body column loops, `bodyTdClassByColumn`,\n // `totalColumnCount`) derives from this, not from `columns` directly.\n const visibleColumns = useVisibleColumns(columns, columnVisibility);\n\n // Needed before `groupedRender` below — which buckets count as \"currently\n // visible\" depends on which groups are expanded. Shared with `<DataGrid>`'s\n // own `groupBy` support via `useGroupExpansion`.\n const { isGroupExpanded, toggleGroupExpanded } = useGroupExpansion(\n defaultGroupsExpanded,\n controlledExpandedGroups,\n onExpandedGroupsChange,\n );\n\n // Grouping only ever buckets *root*-level rows (see `groupBy`'s own doc) —\n // each bucket's own rows are still flattened depth-first via the same\n // `flattenTree` every ungrouped render uses, so expand/collapse and lazy\n // loading behave identically either way. `flatRows` itself is skipped\n // entirely when grouped (nothing reads it in that mode, and flattening the\n // whole tree in original — not bucketed — order would be wasted work).\n const flatRows = useMemo(\n () => (groupBy ? [] : flattenTree(data, accessors, expanded, childrenMap)),\n [groupBy, data, accessors, expanded, childrenMap],\n );\n const groupedBuckets = useMemo(\n () =>\n groupBy\n ? groupRows(data, groupBy).map((bucket) => ({\n key: bucket.key,\n roots: bucket.items,\n flatRows: flattenTree(bucket.items, accessors, expanded, childrenMap),\n }))\n : undefined,\n [groupBy, data, accessors, expanded, childrenMap],\n );\n // Rows belonging to a *collapsed* group are never rendered, so — unlike\n // `<DataGrid>`'s page-scoped \"select all on this page\" (which has no\n // per-row hide/show mechanic besides pagination) — they must not count as\n // \"currently visible\" either: `<TreeDataGrid>`'s own selection already\n // excludes a collapsed *tree node*'s descendants from `flatRows`, so a\n // collapsed *group* follows the same rule for consistency. This also gives\n // each rendered row a global (not per-bucket-reset) index via\n // `startIndexByBucket` below, matching what `getRowProps`'s own `index`\n // param documents (\"position among the currently visible rows\").\n const groupedRender = useMemo(() => {\n if (!groupedBuckets) return undefined;\n const startIndexByBucket = new Map<string, number>();\n const rows: FlatTreeRow<TRow>[] = [];\n for (const bucket of groupedBuckets) {\n if (!isGroupExpanded(bucket.key)) continue;\n startIndexByBucket.set(bucket.key, rows.length);\n rows.push(...bucket.flatRows);\n }\n return { startIndexByBucket, rows };\n }, [groupedBuckets, isGroupExpanded]);\n // Flattened across every *expanded* group when grouped — selection/\"select\n // all visible\" operate over the whole currently-rendered set regardless of\n // which bucket a row falls in.\n const allFlatRows = groupedRender ? groupedRender.rows : flatRows;\n\n const treeColId = treeColumnId ?? columns[0]?.id;\n const showRowActionsColumn = Boolean(rowActions?.length);\n const showSelectionColumn = controlledSelectedIds !== undefined;\n const totalColumnCount =\n visibleColumns.length + (showRowActionsColumn ? 1 : 0) + (showSelectionColumn ? 1 : 0);\n\n // A group-header row sticks right below the real `<thead>` while its\n // members scroll past. Shared with `<DataGrid>`'s own `groupBy` support\n // via `useStickyGroupHeaderTop`.\n const { theadRef, groupHeaderTop } = useStickyGroupHeaderTop(Boolean(groupBy));\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 () => allFlatRows.filter((flatRow) => !(isRowSelectionDisabled?.(flatRow.row) ?? false)),\n [allFlatRows, 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 visibleColumns) map.set(column.id, `p-2 ${alignClassName(column)}`);\n return map;\n }, [visibleColumns]);\n\n // Same \"entirely opt-in\" convention `<DataGrid>`'s own `hasColumnSummary`\n // has — the per-group summary row below and the `showTotals` `<tfoot>`\n // both only render once at least one visible column sets `summary` (see\n // `BaseColumn.summary`'s own doc).\n const hasColumnSummary = visibleColumns.some((column) => column.summary);\n\n // One `<td>` per visible column for the per-group summary row, each\n // running that column's own `summary(rows)` — `rows` here is always a\n // bucket's own *root* rows (`bucket.roots`), never the flattened tree (see\n // `groupBy`'s own doc for why: a child's value is usually already\n // reflected in its parent's).\n function renderGroupSummaryCells(rows: TRow[]): ReactNode {\n return visibleColumns.map((column) => (\n <td key={column.id} className={cn(bodyTdClassByColumn.get(column.id), \"font-medium\")}>\n {column.summary?.(rows)}\n </td>\n ));\n }\n\n const scrollRef = useRef<HTMLDivElement>(null);\n // Interleaving synthetic group-header rows needs a flattened index space\n // to virtualize correctly — out of scope for this pass, so `groupBy`\n // forces virtualization off rather than silently mis-rendering, same as\n // `<DataGrid>`'s own grouped mode.\n const shouldVirtualize = !groupBy && 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 {visibleColumns.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 data-testid={testId}\n // Scrollability must not depend on whether the virtualizer is\n // active: `shouldVirtualize` only decides whether rows are windowed\n // (an unrelated performance optimization gated on `virtualizeThreshold`,\n // and always off for `groupBy`, see its own comment above) -- a tree\n // whose row count stays under that threshold, or that groups its\n // rows, can still overflow `maxBodyHeight` and needs the exact same\n // scroll box to reach the rest of it. Previously this div only got\n // its overflow-y-auto/maxHeight styling once virtualization kicked\n // in, so a non-virtualized (or grouped) tree that outgrew its box\n // just rendered past it with no way to scroll to what was clipped.\n className=\"overflow-y-auto\"\n style={{ maxHeight: maxBodyHeight }}\n >\n <table className=\"w-full border-collapse text-sm\">\n <thead ref={theadRef}>\n <tr className=\"divide-x divide-border\">\n {showSelectionColumn && (\n <th\n className=\"sticky top-0 z-20 border-b border-border bg-muted p-1\"\n style={{ width: SELECTION_COLUMN_WIDTH }}\n >\n <Checkbox\n aria-label=\"Select all visible rows\"\n checked={allVisibleSelected}\n onCheckedChange={toggleAllVisibleSelected}\n />\n </th>\n )}\n {visibleColumns.map((column) => (\n <th\n key={column.id}\n style={column.width ? { width: column.width } : undefined}\n className={cn(\n \"sticky top-0 z-20 border-b border-border bg-muted p-2 font-medium\",\n alignClassName(column),\n )}\n >\n {column.header}\n </th>\n ))}\n {showRowActionsColumn && (\n <th className=\"sticky top-0 z-20 border-b border-border bg-muted p-2\" aria-label=\"Row actions\" />\n )}\n </tr>\n </thead>\n <tbody>\n {data.length === 0 ? (\n <tr>\n <td colSpan={totalColumnCount} className=\"p-4 text-center text-muted-foreground\">\n {loading ? (\n <span className=\"inline-flex items-center gap-2\">\n <Loader2 className=\"h-4 w-4 animate-spin\" aria-hidden=\"true\" />\n Loading...\n </span>\n ) : (\n \"No results.\"\n )}\n </td>\n </tr>\n ) : groupedBuckets ? (\n groupedBuckets.map((bucket) => {\n const groupExpanded = isGroupExpanded(bucket.key);\n return (\n <Fragment key={`group-${bucket.key}`}>\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={groupExpanded}\n >\n <ChevronRight\n className={`h-4 w-4 shrink-0 transition-transform${groupExpanded ? \" rotate-90\" : \"\"}`}\n aria-hidden\n />\n {renderGroupHeader\n ? renderGroupHeader(bucket.key, bucket.roots, groupExpanded)\n : `${bucket.key} (${bucket.roots.length})`}\n </button>\n </td>\n </tr>\n {/* Not sticky, unlike the label row above — scrolls away\n with this group's own rows. Rendered regardless of\n `groupExpanded`, same as `<DataGrid>`'s own group\n summary row: a collapsed group's subtotal is often\n the whole reason to collapse it. */}\n {hasColumnSummary && (\n <tr\n data-testid={`group-summary-${bucket.key}`}\n className=\"border-b border-border divide-x divide-border\"\n >\n {showSelectionColumn && (\n <td\n className={cn(\"p-2\", zebra ? \"bg-muted\" : \"bg-background\")}\n style={{ width: SELECTION_COLUMN_WIDTH }}\n />\n )}\n {renderGroupSummaryCells(bucket.roots)}\n {showRowActionsColumn && (\n <td className={cn(\"p-2\", zebra ? \"bg-muted\" : \"bg-background\")} aria-hidden />\n )}\n </tr>\n )}\n {groupExpanded &&\n (() => {\n const startIndex = groupedRender?.startIndexByBucket.get(bucket.key) ?? 0;\n return bucket.flatRows.map((flatRow, i) => renderRow(flatRow, startIndex + i));\n })()}\n </Fragment>\n );\n })\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 {showTotals && hasColumnSummary && (\n // Sticky to the BOTTOM of the scroll container, mirroring the\n // `<thead>`'s own `top-0` stickiness — see `<DataGrid>`'s own\n // `renderTotalsFooter` for the full reasoning. Summarizes `data`\n // (the root-level rows), not `flatRows`/`allFlatRows` — same\n // \"roots only\" scope `groupBy` itself uses, and for the same\n // reason (a child's value is usually already folded into its\n // parent's).\n <tfoot>\n <tr data-testid=\"totals-row\" className=\"divide-x divide-border\">\n {showSelectionColumn && (\n <td\n className=\"sticky bottom-0 z-20 border-t border-border bg-muted p-2\"\n style={{ width: SELECTION_COLUMN_WIDTH }}\n />\n )}\n {visibleColumns.map((column) => (\n <td\n key={column.id}\n style={column.width ? { width: column.width } : undefined}\n className={cn(\n \"sticky bottom-0 z-20 border-t border-border bg-muted p-2 font-medium\",\n alignClassName(column),\n )}\n >\n {column.summary?.(data)}\n </td>\n ))}\n {showRowActionsColumn && (\n <td className=\"sticky bottom-0 z-20 border-t border-border bg-muted p-2\" aria-hidden />\n )}\n </tr>\n </tfoot>\n )}\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","useEffect","useId","useState","jsx","jsxs","DEFAULT_OPERATOR","useState","useId","useEffect","FunnelIcon","jsx","jsx","jsx","jsxs","parseISO","jsx","jsxs","parseISO","jsx","jsxs","useRef","forwardRef","jsx","forwardRef","jsx","jsxs","useRef","useEffect","useRef","useState","jsx","jsxs","useState","useRef","useEffect","jsx","jsxs","jsx","useEffect","useRef","useState","useState","useEffect","useRef","jsx","value","jsx","jsxs","useCallback","useMemo","useRef","useState","useState","useRef","useMemo","useCallback","useRef","useState","next","jsx","value","useLayoutEffect","useRef","useState","Fragment","jsx","jsxs","useEffect","useState","forwardRef","jsx","jsxs","forwardRef","jsx","jsxs","useEffect","useState","useMemo","forwardRef","jsx","forwardRef","jsx","jsx","jsxs","useMemo","FunnelIcon","Fragment","useEffect","useMemo","useRef","useState","useEffect","useState","jsx","useState","useEffect","useCallback","useState","useLayoutEffect","useRef","useState","useMemo","useMemo","useCallback","useEffect","useMemo","useState","useEffect","useMemo","useRef","useState","useCallback","useEffect","useMemo","withoutColumn","Fragment","jsx","jsxs","useState","useRef","useMemo","useEffect","rows","FunnelIcon","useVirtualizer","ChevronDown","ChevronRight","Loader2","Fragment","useEffect","useMemo","useRef","state","useCallback","useMemo","useState","Fragment","jsx","jsxs","Loader2","ChevronDown","ChevronRight","useMemo","useRef","useEffect","useVirtualizer"]}