{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "data-table",
  "type": "registry:block",
  "title": "Data Table",
  "description": "An MRT-complete data table for shadcn/ui (TanStack Table v8): sorting, filtering, search, grouping, editing, pinning, virtualization, export and more.",
  "meta": {
    "version": "0.4.1"
  },
  "dependencies": [
    "@tanstack/react-table",
    "@tanstack/match-sorter-utils",
    "@tanstack/react-virtual",
    "@dnd-kit/core",
    "@dnd-kit/sortable",
    "@dnd-kit/utilities",
    "date-fns",
    "lucide-react"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "badge",
    "button",
    "calendar",
    "checkbox",
    "command",
    "context-menu",
    "dialog",
    "dropdown-menu",
    "input",
    "label",
    "popover",
    "select",
    "skeleton",
    "slider",
    "table",
    "tooltip"
  ],
  "files": [
    {
      "path": "ui/data-table/components/body/click-to-copy.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/body/click-to-copy.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\n\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipTrigger,\n} from \"@/components/ui/tooltip\"\nimport { cn } from \"@/lib/utils\"\n\n/**\n * Wraps cell content in a click-to-copy affordance with a transient \"Copied\"\n * tooltip. Uses `navigator.clipboard` (secure-context only) with a\n * `document.execCommand` fallback, both guarded for SSR.\n */\nexport function ClickToCopy({\n  value,\n  copyLabel,\n  copiedLabel,\n  children,\n}: {\n  value: string\n  copyLabel: string\n  copiedLabel: string\n  children: React.ReactNode\n}) {\n  const [copied, setCopied] = React.useState(false)\n  const timer = React.useRef<ReturnType<typeof setTimeout> | undefined>(\n    undefined\n  )\n\n  React.useEffect(() => () => clearTimeout(timer.current), [])\n\n  const copy = async () => {\n    const ok = await copyText(value)\n    if (!ok) return\n    setCopied(true)\n    clearTimeout(timer.current)\n    timer.current = setTimeout(() => setCopied(false), 1200)\n  }\n\n  return (\n    <Tooltip open={copied || undefined}>\n      <TooltipTrigger asChild>\n        <button\n          type=\"button\"\n          onClick={copy}\n          aria-label={copyLabel}\n          className={cn(\n            \"-mx-1 rounded-sm px-1 text-left transition-colors outline-none hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring/40\"\n          )}\n        >\n          {children}\n        </button>\n      </TooltipTrigger>\n      <TooltipContent>{copied ? copiedLabel : copyLabel}</TooltipContent>\n    </Tooltip>\n  )\n}\n\nasync function copyText(text: string): Promise<boolean> {\n  if (typeof navigator !== \"undefined\" && navigator.clipboard?.writeText) {\n    try {\n      await navigator.clipboard.writeText(text)\n      return true\n    } catch {\n      // fall through to legacy path\n    }\n  }\n  if (typeof document === \"undefined\") return false\n  try {\n    const el = document.createElement(\"textarea\")\n    el.value = text\n    el.style.position = \"fixed\"\n    el.style.opacity = \"0\"\n    document.body.appendChild(el)\n    el.select()\n    const ok = document.execCommand(\"copy\")\n    document.body.removeChild(el)\n    return ok\n  } catch {\n    return false\n  }\n}\n"
    },
    {
      "path": "ui/data-table/components/body/data-table-body.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/body/data-table-body.tsx",
      "content": "\"use client\"\n\nimport { SortableContext, verticalListSortingStrategy } from \"@dnd-kit/sortable\"\nimport { type Cell, type Row, type RowData } from \"@tanstack/react-table\"\nimport type { Virtualizer } from \"@tanstack/react-virtual\"\nimport * as React from \"react\"\n\nimport { TableBody, TableCell, TableRow } from \"@/components/ui/table\"\nimport { cn } from \"@/lib/utils\"\n\nimport {\n  ALIGN_CELL,\n  DENSITY_CELL_PADDING,\n  NON_DATA_COLUMN_IDS,\n  SELECTED_ROW_CLASS,\n} from \"../../core/constants\"\nimport type { DataTableInstance } from \"../../core/types\"\nimport { resolveRowHeight } from \"../../helpers/resolve-row-height\"\nimport type {\n  VirtualRowItem,\n  WithColumnSpacers,\n} from \"../../hooks/use-table-virtualizers\"\nimport {\n  getColumnPinningClass,\n  getColumnPinningStyle,\n  getWidthStyle,\n} from \"../../utils/column-styles\"\nimport { DataTableCreateRow } from \"../editing/data-table-create-row\"\nimport { DataTableBodyRow } from \"./dnd\"\nimport { renderBodyCell } from \"./render-body-cell\"\nimport { SkeletonRows } from \"./skeleton-rows\"\n\ninterface DataTableBodyProps<TData extends RowData> {\n  table: DataTableInstance<TData>\n  rowVirtualizer: Virtualizer<HTMLDivElement, HTMLTableRowElement>\n  virtualItems: VirtualRowItem<TData>[]\n  virtualColumns: { index: number }[]\n  withColumnSpacers: WithColumnSpacers\n}\n\n/**\n * The table `<tbody>`: the optional inline create-row, then either skeleton\n * rows (initial load), the virtualized window, the normal (sortable) rows, or\n * the empty-state row. Owns per-row / per-cell rendering, tree indentation, and\n * expanded detail panels.\n */\nexport function DataTableBody<TData extends RowData>({\n  table,\n  rowVirtualizer,\n  virtualItems,\n  virtualColumns,\n  withColumnSpacers,\n}: DataTableBodyProps<TData>) {\n  const {\n    density,\n    enableKeyboardNavigation,\n    enableFilterMatchHighlighting,\n    columnsWithCustomCell,\n    enableColumnResizing,\n    enableColumnVirtualization,\n    enableRowVirtualization,\n    enableRowOrdering,\n    renderDetailPanel,\n    onRowClick,\n    onRowDoubleClick,\n    onCellClick,\n    onCellDoubleClick,\n    renderEmpty,\n    enablePagination,\n    showSkeletons,\n    localization,\n    rowHeight,\n    getRowHeight,\n  } = table.tableInstance\n\n  const padding = DENSITY_CELL_PADDING[density]\n  const visibleColumnCount = table.getVisibleLeafColumns().length\n  const topRows = table.getTopRows()\n  const bottomRows = table.getBottomRows()\n  const centerRows = table.getCenterRows()\n  const hasRows = topRows.length + centerRows.length + bottomRows.length > 0\n  const centerRowIds = centerRows.map((r) => r.id)\n\n  // Tree data (getSubRows) indents the first real data column by row depth so\n  // the hierarchy is visible (grouped rows self-indent via the group cell).\n  const isTreeData = table.options.getSubRows != null\n  const firstDataColumnId = table\n    .getVisibleLeafColumns()\n    .find((c) => !NON_DATA_COLUMN_IDS.has(c.id))?.id\n\n  const renderCell = (\n    cell: Cell<TData, unknown>,\n    row: Row<TData>,\n    rowIndex: number,\n    colIndex: number,\n    rowHeightValue: number | \"auto\" | undefined\n  ) => {\n    const align = cell.column.columnDef.meta?.align ?? \"left\"\n    const isAutoHeight = rowHeightValue === \"auto\"\n    const isFixedHeight = typeof rowHeightValue === \"number\"\n    const treeIndent =\n      isTreeData &&\n      cell.column.id === firstDataColumnId &&\n      !cell.getIsGrouped() &&\n      row.depth > 0\n        ? row.depth\n        : 0\n    const content = renderBodyCell(\n      cell,\n      table,\n      enableFilterMatchHighlighting,\n      columnsWithCustomCell,\n      localization\n    )\n    const indented =\n      treeIndent > 0 ? (\n        <span\n          className=\"flex items-center\"\n          style={{ paddingInlineStart: `${treeIndent}rem` }}\n        >\n          {content}\n        </span>\n      ) : (\n        content\n      )\n    return (\n      <TableCell\n        key={cell.id}\n        data-cell-row={rowIndex}\n        data-cell-col={colIndex}\n        data-pinned={cell.column.getIsPinned() || undefined}\n        tabIndex={\n          enableKeyboardNavigation\n            ? rowIndex === 0 && colIndex === 0\n              ? 0\n              : -1\n            : undefined\n        }\n        style={{\n          ...getWidthStyle(cell.column, table),\n          ...getColumnPinningStyle(cell.column),\n        }}\n        onClick={\n          onCellClick\n            ? (event) => onCellClick({ cell, row, table, event })\n            : undefined\n        }\n        onDoubleClick={\n          onCellDoubleClick\n            ? (event) => onCellDoubleClick({ cell, row, table, event })\n            : undefined\n        }\n        className={cn(\n          \"relative bg-background group-data-[state=selected]:bg-transparent\",\n          padding,\n          ALIGN_CELL[align],\n          // Fixed layout (resizing on) clips overflowing content with an\n          // ellipsis instead of letting it bleed into the next column. An\n          // \"auto\" row opts out so its content wraps and the row grows.\n          enableColumnResizing && !isAutoHeight && \"overflow-hidden text-ellipsis\",\n          isAutoHeight && \"align-top whitespace-normal wrap-break-word\",\n          getColumnPinningClass(cell.column),\n          enableKeyboardNavigation &&\n            \"focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:-outline-offset-2 focus-visible:outline-none\"\n        )}\n      >\n        {isFixedHeight ? (\n          // Pin the content box to the requested height and clip overflow.\n          <div className=\"overflow-hidden\" style={{ height: rowHeightValue }}>\n            {indented}\n          </div>\n        ) : (\n          indented\n        )}\n      </TableCell>\n    )\n  }\n\n  const renderCells = (row: Row<TData>, rowIndex: number): React.ReactNode => {\n    const rowHeightValue = resolveRowHeight(row, { rowHeight, getRowHeight })\n    const cells = row.getVisibleCells()\n    if (!enableColumnVirtualization) {\n      return cells.map((cell, colIndex) =>\n        renderCell(cell, row, rowIndex, colIndex, rowHeightValue)\n      )\n    }\n    return withColumnSpacers(\n      virtualColumns\n        .map((vc) => {\n          const cell = cells[vc.index]\n          return cell\n            ? renderCell(cell, row, rowIndex, vc.index, rowHeightValue)\n            : null\n        })\n        .filter(Boolean) as React.ReactNode[],\n      `row-${row.id}`\n    )\n  }\n\n  const detailRow = (row: Row<TData>) => (\n    <TableRow className=\"hover:bg-transparent\">\n      <TableCell colSpan={visibleColumnCount} className=\"bg-muted/20 p-0\">\n        <div className=\"p-3\">{renderDetailPanel?.({ row, table })}</div>\n      </TableCell>\n    </TableRow>\n  )\n\n  let runningRowIndex = 0\n  const renderRow = (row: Row<TData>) => {\n    const rowIndex = runningRowIndex++\n    const isGrouped = row.getIsGrouped()\n    const showDetail = !!renderDetailPanel && row.getIsExpanded() && !isGrouped\n\n    return (\n      <React.Fragment key={row.id}>\n        <DataTableBodyRow\n          row={row}\n          draggable={\n            enableRowOrdering &&\n            !enableRowVirtualization &&\n            !row.getIsPinned() &&\n            !isGrouped\n          }\n          onClick={\n            onRowClick\n              ? (event) => onRowClick({ row, table, event })\n              : undefined\n          }\n          onDoubleClick={\n            onRowDoubleClick\n              ? (event) => onRowDoubleClick({ row, table, event })\n              : undefined\n          }\n        >\n          {renderCells(row, rowIndex)}\n        </DataTableBodyRow>\n        {showDetail && detailRow(row)}\n      </React.Fragment>\n    )\n  }\n\n  return (\n    <TableBody>\n      {table.tableInstance.enableEditing &&\n        table.tableInstance.isCreating &&\n        table.tableInstance.createDisplayMode === \"row\" && (\n          <DataTableCreateRow table={table} />\n        )}\n      {showSkeletons && !hasRows ? (\n        <SkeletonRows\n          rowCount={enablePagination ? table.getState().pagination.pageSize : 8}\n          columnCount={visibleColumnCount}\n          padding={padding}\n        />\n      ) : hasRows && enableRowVirtualization ? (\n        <>\n          {topRows.map(renderRow)}\n          {(() => {\n            const vRows = rowVirtualizer.getVirtualItems()\n            const padTop = vRows.length ? (vRows[0]?.start ?? 0) : 0\n            const padBottom = vRows.length\n              ? rowVirtualizer.getTotalSize() -\n                (vRows[vRows.length - 1]?.end ?? 0)\n              : 0\n            return (\n              <>\n                {padTop > 0 && (\n                  <tr aria-hidden>\n                    <td\n                      colSpan={visibleColumnCount}\n                      style={{ height: padTop, padding: 0, border: 0 }}\n                    />\n                  </tr>\n                )}\n                {vRows.map((vRow) => {\n                  const item = virtualItems[vRow.index]\n                  if (!item) return null\n                  if (item.detail) {\n                    return (\n                      <TableRow\n                        key={`${item.row.id}-detail`}\n                        data-index={vRow.index}\n                        ref={rowVirtualizer.measureElement}\n                        className=\"hover:bg-transparent\"\n                      >\n                        <TableCell\n                          colSpan={visibleColumnCount}\n                          className=\"bg-muted/20 p-0\"\n                        >\n                          <div className=\"p-3\">\n                            {renderDetailPanel?.({ row: item.row, table })}\n                          </div>\n                        </TableCell>\n                      </TableRow>\n                    )\n                  }\n                  return (\n                    <TableRow\n                      key={item.row.id}\n                      data-index={vRow.index}\n                      ref={rowVirtualizer.measureElement}\n                      data-state={\n                        item.row.getIsSelected() ? \"selected\" : undefined\n                      }\n                      onClick={\n                        onRowClick\n                          ? (event) =>\n                              onRowClick({ row: item.row, table, event })\n                          : undefined\n                      }\n                      onDoubleClick={\n                        onRowDoubleClick\n                          ? (event) =>\n                              onRowDoubleClick({ row: item.row, table, event })\n                          : undefined\n                      }\n                      className={cn(\n                        SELECTED_ROW_CLASS,\n                        (onRowClick || onRowDoubleClick) && \"cursor-pointer\"\n                      )}\n                    >\n                      {renderCells(item.row, vRow.index)}\n                    </TableRow>\n                  )\n                })}\n                {padBottom > 0 && (\n                  <tr aria-hidden>\n                    <td\n                      colSpan={visibleColumnCount}\n                      style={{ height: padBottom, padding: 0, border: 0 }}\n                    />\n                  </tr>\n                )}\n              </>\n            )\n          })()}\n          {bottomRows.map(renderRow)}\n        </>\n      ) : hasRows ? (\n        <>\n          {topRows.map(renderRow)}\n          <SortableContext\n            items={centerRowIds}\n            strategy={verticalListSortingStrategy}\n          >\n            {centerRows.map(renderRow)}\n          </SortableContext>\n          {bottomRows.map(renderRow)}\n        </>\n      ) : (\n        <TableRow className=\"hover:bg-transparent\">\n          <TableCell\n            colSpan={visibleColumnCount}\n            className=\"h-32 text-center text-sm text-muted-foreground\"\n          >\n            {renderEmpty?.({ table }) ?? localization.noRecordsToDisplay}\n          </TableCell>\n        </TableRow>\n      )}\n    </TableBody>\n  )\n}\n"
    },
    {
      "path": "ui/data-table/components/body/data-table-footer.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/body/data-table-footer.tsx",
      "content": "\"use client\"\n\nimport { flexRender, type Header, type RowData } from \"@tanstack/react-table\"\nimport type { VirtualItem } from \"@tanstack/react-virtual\"\n\nimport {\n  TableCell,\n  TableFooter,\n  TableRow,\n} from \"@/components/ui/table\"\nimport { cn } from \"@/lib/utils\"\n\nimport { DENSITY_CELL_PADDING } from \"../../core/constants\"\nimport type { DataTableInstance } from \"../../core/types\"\nimport type { WithColumnSpacers } from \"../../hooks/use-table-virtualizers\"\nimport {\n  getColumnPinningClass,\n  getColumnPinningStyle,\n  getWidthStyle,\n} from \"../../utils/column-styles\"\n\ninterface DataTableFooterProps<TData extends RowData> {\n  table: DataTableInstance<TData>\n  virtualColumns: VirtualItem[]\n  withColumnSpacers: WithColumnSpacers\n}\n\n/** Whether any leaf column defines a footer (controls whether a `<tfoot>` renders). */\nexport function hasFooter<TData extends RowData>(\n  table: DataTableInstance<TData>\n): boolean {\n  return table.getAllLeafColumns().some((c) => c.columnDef.footer != null)\n}\n\n/**\n * The table `<tfoot>` (aggregation / footer cells). Stickiness is controlled by\n * `enableStickyFooter` (on by default), independent of whether a footer exists.\n */\nexport function DataTableFooter<TData extends RowData>({\n  table,\n  virtualColumns,\n  withColumnSpacers,\n}: DataTableFooterProps<TData>) {\n  const { density, enableColumnVirtualization, enableStickyFooter, refs } =\n    table.tableInstance\n  const padding = DENSITY_CELL_PADDING[density]\n\n  return (\n    <TableFooter\n      // Forwarding the exposed DOM ref object as a JSX ref (not reading\n      // .current during render).\n      // eslint-disable-next-line react-hooks/refs\n      ref={refs.tableFooterRef}\n      className={cn(enableStickyFooter && \"sticky bottom-0 z-20\")}\n    >\n      {table.getFooterGroups().map((footerGroup) => {\n        const headers = enableColumnVirtualization\n          ? (virtualColumns\n              .map((vc) => footerGroup.headers[vc.index])\n              .filter(Boolean) as Header<TData, unknown>[])\n          : footerGroup.headers\n        const cells = headers.map((header) => (\n          <TableCell\n            key={header.id}\n            colSpan={header.colSpan}\n            style={{\n              ...getWidthStyle(header.column, table),\n              ...getColumnPinningStyle(header.column),\n            }}\n            className={cn(padding, getColumnPinningClass(header.column))}\n          >\n            {header.isPlaceholder\n              ? null\n              : flexRender(header.column.columnDef.footer, header.getContext())}\n          </TableCell>\n        ))\n        return (\n          <TableRow key={footerGroup.id} className=\"hover:bg-transparent\">\n            {withColumnSpacers(cells, `footer-${footerGroup.id}`)}\n          </TableRow>\n        )\n      })}\n    </TableFooter>\n  )\n}\n"
    },
    {
      "path": "ui/data-table/components/body/dnd/body-row.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/body/dnd/body-row.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport type { Row, RowData } from \"@tanstack/react-table\"\nimport { useSortable } from \"@dnd-kit/sortable\"\nimport { CSS } from \"@dnd-kit/utilities\"\n\nimport { TableRow } from \"@/components/ui/table\"\nimport { cn } from \"@/lib/utils\"\n\nimport { SELECTED_ROW_CLASS } from \"../../../core/constants\"\nimport { RowDragContext } from \"../../../injected-columns/injected-columns\"\n\n/**\n * Body row. `useSortable` is always called (disabled when row ordering is off).\n * Exposes its drag-handle props through {@link RowDragContext} so the drag\n * column's handle can activate it. Applies the selected-row accent.\n */\nexport function DataTableBodyRow<TData extends RowData>({\n  row,\n  draggable,\n  children,\n  className,\n  onClick,\n  onDoubleClick,\n}: {\n  row: Row<TData>\n  draggable: boolean\n  children: React.ReactNode\n  className?: string\n  onClick?: React.MouseEventHandler<HTMLTableRowElement>\n  onDoubleClick?: React.MouseEventHandler<HTMLTableRowElement>\n}) {\n  const {\n    attributes,\n    listeners,\n    setNodeRef,\n    setActivatorNodeRef,\n    transform,\n    transition,\n    isDragging,\n  } = useSortable({ id: row.id, disabled: !draggable, data: { type: \"row\" } })\n\n  const style: React.CSSProperties = {\n    transform: CSS.Transform.toString(transform),\n    transition,\n    position: isDragging ? \"relative\" : undefined,\n    zIndex: isDragging ? 1 : undefined,\n  }\n\n  const dragProps = React.useMemo(\n    () => ({\n      attributes: attributes as unknown as Record<string, unknown>,\n      listeners: listeners as Record<string, unknown> | undefined,\n      setActivatorNodeRef,\n    }),\n    [attributes, listeners, setActivatorNodeRef]\n  )\n\n  return (\n    <RowDragContext.Provider value={dragProps}>\n      <TableRow\n        ref={setNodeRef}\n        style={style}\n        data-state={row.getIsSelected() ? \"selected\" : undefined}\n        onClick={onClick}\n        onDoubleClick={onDoubleClick}\n        className={cn(\n          SELECTED_ROW_CLASS,\n          isDragging && \"bg-muted\",\n          (onClick || onDoubleClick) && \"cursor-pointer\",\n          className\n        )}\n      >\n        {children}\n      </TableRow>\n    </RowDragContext.Provider>\n  )\n}\n"
    },
    {
      "path": "ui/data-table/components/body/dnd/column-resize-handle.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/body/dnd/column-resize-handle.tsx",
      "content": "\"use client\"\n\nimport type { Header, RowData } from \"@tanstack/react-table\"\n\nimport { cn } from \"@/lib/utils\"\n\nimport type { DataTableInstance } from \"../../../core/types\"\n\n/** Drag handle to grab the resize edge of a column header. */\nexport function ColumnResizeHandle<TData extends RowData, TValue>({\n  header,\n  table,\n}: {\n  header: Header<TData, TValue>\n  table: DataTableInstance<TData>\n}) {\n  if (!header.column.getCanResize()) return null\n  return (\n    <span\n      role=\"separator\"\n      aria-label={table.tableInstance.localization.resizeColumn}\n      onMouseDown={header.getResizeHandler()}\n      onTouchStart={header.getResizeHandler()}\n      onDoubleClick={() =>\n        table.tableInstance.enableColumnAutosize\n          ? table.tableInstance.autoSizeColumn(header.column.id)\n          : header.column.resetSize()\n      }\n      className={cn(\n        // Hidden at rest; a faint divider appears while the cursor is anywhere\n        // in the header row (group/th), strengthens to primary on direct hover,\n        // and stays primary while actively resizing.\n        \"absolute top-0 right-0 z-10 h-full w-1 cursor-col-resize touch-none bg-transparent transition-colors select-none group-hover/th:bg-border/60 hover:bg-primary\",\n        // Touch devices can't hover, so the grip would be invisible: on a coarse\n        // pointer reveal it at rest and widen the hit area for a finger.\n        \"pointer-coarse:w-1.5\",\n        header.column.getIsResizing()\n          ? \"bg-primary\"\n          : \"pointer-coarse:bg-border/60\"\n      )}\n    />\n  )\n}\n"
    },
    {
      "path": "ui/data-table/components/body/dnd/head-cell.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/body/dnd/head-cell.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport type { Header, RowData } from \"@tanstack/react-table\"\nimport { useSortable } from \"@dnd-kit/sortable\"\nimport { CSS } from \"@dnd-kit/utilities\"\n\nimport { Button } from \"@/components/ui/button\"\nimport { TableHead } from \"@/components/ui/table\"\nimport { cn } from \"@/lib/utils\"\n\nimport {\n  getColumnPinningClass,\n  getColumnPinningStyle,\n} from \"../../../utils/column-styles\"\nimport type { IconComponent } from \"../../../core/icons\"\nimport type { DataTableInstance } from \"../../../core/types\"\nimport { ColumnResizeHandle } from \"./column-resize-handle\"\n\n/** dnd-kit activator props for the current column's drag handle. */\nexport interface ColumnDragHandleProps {\n  attributes: Record<string, unknown>\n  listeners: Record<string, unknown> | undefined\n  setActivatorNodeRef: (el: HTMLElement | null) => void\n}\n\n/** Provided by the sortable header cell, consumed by {@link ColumnDragHandle}\n *  so the grip can live inside the column header (beside the actions menu)\n *  while the dnd-kit wiring stays on the cell. Null when the column isn't\n *  draggable. */\nexport const ColumnDragContext =\n  React.createContext<ColumnDragHandleProps | null>(null)\n\n/**\n * Header cell. `useSortable` is always called (disabled when ordering is off or\n * for display columns) to keep hook order stable. Applies pinning + width\n * styles, an optional drag grip, and the resize handle.\n */\nexport function DataTableHeadCell<TData extends RowData, TValue>({\n  header,\n  table,\n  draggable,\n  resizable,\n  widthStyle,\n  padding,\n  children,\n}: {\n  header: Header<TData, TValue>\n  table: DataTableInstance<TData>\n  draggable: boolean\n  resizable: boolean\n  widthStyle: React.CSSProperties\n  padding: string\n  children: React.ReactNode\n}) {\n  const column = header.column\n  const {\n    attributes,\n    listeners,\n    setNodeRef,\n    setActivatorNodeRef,\n    transform,\n    isDragging,\n  } = useSortable({\n    id: column.id,\n    disabled: !draggable,\n    data: { type: \"column\" },\n  })\n\n  const style: React.CSSProperties = {\n    ...widthStyle,\n    ...getColumnPinningStyle(column),\n    transform: CSS.Translate.toString(transform),\n    // Animate width for programmatic changes (autosize, pinning) but not during\n    // an active drag, where the easing makes the header lag behind the cursor.\n    transition: column.getIsResizing() ? undefined : \"width 0.15s ease\",\n    opacity: isDragging ? 0.7 : undefined,\n    zIndex: isDragging ? 30 : undefined,\n  }\n\n  // Hand the drag-activator props to the header (via context) so the grip can\n  // render next to the column-actions menu instead of crowding the left edge.\n  const dragProps = React.useMemo<ColumnDragHandleProps>(\n    () => ({\n      attributes: attributes as unknown as Record<string, unknown>,\n      listeners: listeners as Record<string, unknown> | undefined,\n      setActivatorNodeRef,\n    }),\n    [attributes, listeners, setActivatorNodeRef]\n  )\n\n  return (\n    <TableHead\n      ref={setNodeRef}\n      colSpan={header.colSpan}\n      style={style}\n      data-pinned={column.getIsPinned() || undefined}\n      aria-sort={ariaSort(column.getIsSorted())}\n      className={cn(\n        \"relative bg-background\",\n        padding,\n        // Match the body: under fixed layout, keep long header labels from\n        // bleeding past the (resizable) column edge.\n        resizable && \"overflow-hidden\",\n        getColumnPinningClass(column)\n      )}\n    >\n      <ColumnDragContext.Provider value={draggable ? dragProps : null}>\n        {children}\n      </ColumnDragContext.Provider>\n      {resizable && <ColumnResizeHandle header={header} table={table} />}\n    </TableHead>\n  )\n}\n\n/** Column reorder grip. Reads dnd-kit props from {@link ColumnDragContext} and\n *  renders nothing when the column isn't draggable. Lives inside the column\n *  header, just before the column-actions menu. */\nexport function ColumnDragHandle({\n  label,\n  Icon,\n}: {\n  label: string\n  Icon: IconComponent\n}) {\n  const ctx = React.useContext(ColumnDragContext)\n  if (!ctx) return null\n  const { attributes, listeners, setActivatorNodeRef } = ctx\n  return (\n    <Button\n      type=\"button\"\n      variant=\"ghost\"\n      size=\"icon\"\n      aria-label={label}\n      ref={setActivatorNodeRef}\n      // dnd-kit injects an aria-describedby id that can differ between the\n      // server and client render; suppress that benign attribute mismatch.\n      suppressHydrationWarning\n      {...attributes}\n      {...listeners}\n      // touch-none: let dnd-kit's pointer sensor own the touch gesture\n      // (otherwise the browser scrolls/selects text before a drag can start on\n      // a phone). size-7 matches the actions menu for a finger-friendly target.\n      className=\"size-7 shrink-0 cursor-grab touch-none text-muted-foreground opacity-70 transition-opacity group-hover/th:opacity-100 focus-visible:opacity-100 active:cursor-grabbing\"\n    >\n      <Icon className=\"size-3.5\" />\n    </Button>\n  )\n}\n\nfunction ariaSort(\n  sorted: false | \"asc\" | \"desc\"\n): React.AriaAttributes[\"aria-sort\"] {\n  if (sorted === \"asc\") return \"ascending\"\n  if (sorted === \"desc\") return \"descending\"\n  return \"none\"\n}\n"
    },
    {
      "path": "ui/data-table/components/body/dnd/index.ts",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/body/dnd/index.ts",
      "content": "\"use client\"\n\n// dnd-kit sortable primitives for column/row drag + column resize.\n\nexport { ColumnResizeHandle } from \"./column-resize-handle\"\nexport { DataTableHeadCell, ColumnDragHandle } from \"./head-cell\"\nexport { DataTableBodyRow } from \"./body-row\"\n"
    },
    {
      "path": "ui/data-table/components/body/highlight.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/body/highlight.tsx",
      "content": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst MAX_HIGHLIGHT_LENGTH = 2000\n\nfunction escapeRegExp(input: string): string {\n  return input.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")\n}\n\n/**\n * Wraps occurrences of `query` within `text` in a themeable `<mark>`. Work is\n * skipped when there's no query, the query is escaped, and very long strings\n * are left unhighlighted to avoid regex blowups on huge cells.\n */\nexport const Highlight = React.memo(function Highlight({\n  text,\n  query,\n  className,\n}: {\n  text: string\n  query?: string | null\n  className?: string\n}) {\n  const trimmed = query?.trim()\n  if (!trimmed || text.length === 0 || text.length > MAX_HIGHLIGHT_LENGTH) {\n    return <>{text}</>\n  }\n\n  const parts = text.split(new RegExp(`(${escapeRegExp(trimmed)})`, \"gi\"))\n  if (parts.length === 1) return <>{text}</>\n\n  const needle = trimmed.toLowerCase()\n  return (\n    <>\n      {parts.map((part, index) =>\n        part.toLowerCase() === needle ? (\n          <mark\n            key={index}\n            className={cn(\n              \"rounded-xs bg-highlight px-0.5 text-highlight-foreground\",\n              className\n            )}\n          >\n            {part}\n          </mark>\n        ) : (\n          <React.Fragment key={index}>{part}</React.Fragment>\n        )\n      )}\n    </>\n  )\n})\n"
    },
    {
      "path": "ui/data-table/components/body/render-body-cell.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/body/render-body-cell.tsx",
      "content": "\"use client\"\n\nimport { flexRender, type Cell, type RowData } from \"@tanstack/react-table\"\nimport * as React from \"react\"\n\nimport type { DataTableInstance } from \"../../core/types\"\nimport { SUBSTRING_MODES } from \"../../fns/filter-fns\"\nimport { getEffectiveMode } from \"../../helpers/effective-filter-mode\"\nimport { DataTableBodyCellContent } from \"../editing/data-table-edit-cell\"\nimport { Highlight } from \"./highlight\"\n\n/**\n * Resolves a body cell's content, handling grouped / aggregated / placeholder\n * cells and falling back to the highlight-aware value renderer.\n */\nexport function renderBodyCell<TData extends RowData>(\n  cell: Cell<TData, unknown>,\n  table: DataTableInstance<TData>,\n  enableHighlight: boolean,\n  columnsWithCustomCell: ReadonlySet<string>,\n  localization: DataTableInstance<TData>[\"tableInstance\"][\"localization\"]\n): React.ReactNode {\n  const { row, column } = cell\n  const icons = table.tableInstance.icons\n  const meta = column.columnDef.meta\n\n  // Grouped/aggregated/placeholder cells are a grouping concept. Tree data\n  // (getSubRows) also marks parent rows' cells as \"aggregated\", which would\n  // bypass normal cell rendering (e.g. the expand chevron). Only take these\n  // branches when grouping is actually active.\n  const isGrouping = table.getState().grouping.length > 0\n\n  if (isGrouping && cell.getIsGrouped()) {\n    return (\n      <button\n        type=\"button\"\n        aria-label={localization.toggleRowExpanded}\n        aria-expanded={row.getIsExpanded()}\n        onClick={row.getToggleExpandedHandler()}\n        style={{ paddingInlineStart: `${row.depth * 1}rem` }}\n        className=\"flex items-center gap-1.5 text-left outline-none focus-visible:ring-2 focus-visible:ring-ring/40\"\n      >\n        {row.getIsExpanded() ? (\n          <icons.expanded className=\"size-4 shrink-0 text-muted-foreground\" />\n        ) : (\n          <icons.collapsed className=\"size-4 shrink-0 text-muted-foreground\" />\n        )}\n        <span className=\"font-medium\">\n          {meta?.renderGroupedCell\n            ? meta.renderGroupedCell({ cell, row, column, table })\n            : flexRender(column.columnDef.cell, cell.getContext())}\n        </span>\n        <span className=\"text-xs text-muted-foreground\">\n          ({row.subRows.length})\n        </span>\n      </button>\n    )\n  }\n\n  if (isGrouping && cell.getIsAggregated()) {\n    if (meta?.renderAggregatedCell) {\n      return meta.renderAggregatedCell({ cell, row, column, table })\n    }\n    return flexRender(\n      column.columnDef.aggregatedCell ?? column.columnDef.cell,\n      cell.getContext()\n    )\n  }\n\n  if (isGrouping && cell.getIsPlaceholder()) {\n    return meta?.renderPlaceholderCell\n      ? meta.renderPlaceholderCell({ cell, row, column, table })\n      : null\n  }\n\n  return (\n    <DataTableBodyCellContent\n      cell={cell}\n      table={table}\n      fallback={renderCellContent(\n        cell,\n        table,\n        enableHighlight,\n        columnsWithCustomCell\n      )}\n    />\n  )\n}\n\n/**\n * Renders a cell's value, auto-highlighting matched substrings for columns with\n * no custom cell renderer and an active string substring filter / global query.\n */\nfunction renderCellContent<TData extends RowData>(\n  cell: Cell<TData, unknown>,\n  table: DataTableInstance<TData>,\n  enableHighlight: boolean,\n  columnsWithCustomCell: ReadonlySet<string>\n): React.ReactNode {\n  const { column } = cell\n  const value = cell.getValue()\n  const canHighlight =\n    enableHighlight &&\n    !column.columnDef.meta?.disableHighlight &&\n    !columnsWithCustomCell.has(column.id) &&\n    (typeof value === \"string\" || typeof value === \"number\")\n\n  if (canHighlight) {\n    const query = resolveHighlightQuery(cell, table)\n    if (query) {\n      return <Highlight text={String(value)} query={query} />\n    }\n  }\n\n  return flexRender(column.columnDef.cell, cell.getContext())\n}\n\n/** The active highlight query for a cell: its column filter, else global search. */\nfunction resolveHighlightQuery<TData extends RowData>(\n  cell: Cell<TData, unknown>,\n  table: DataTableInstance<TData>\n): string | null {\n  const filterValue = cell.column.getFilterValue()\n  if (\n    typeof filterValue === \"string\" &&\n    filterValue.length > 0 &&\n    SUBSTRING_MODES.has(getEffectiveMode(cell.column, table))\n  ) {\n    return filterValue\n  }\n  const globalFilter = table.getState().globalFilter\n  if (\n    typeof globalFilter === \"string\" &&\n    globalFilter.length > 0 &&\n    SUBSTRING_MODES.has(table.tableInstance.globalFilterMode)\n  ) {\n    return globalFilter\n  }\n  return null\n}\n"
    },
    {
      "path": "ui/data-table/components/body/selection-checkbox.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/body/selection-checkbox.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\ninterface SelectionCheckboxProps extends Omit<\n  React.ComponentProps<\"button\">,\n  \"onChange\" | \"type\" | \"role\" | \"aria-checked\"\n> {\n  checked?: boolean\n  indeterminate?: boolean\n  onCheckedChange?: (checked: boolean) => void\n}\n\n/**\n * Selection checkbox that renders a minus glyph for the indeterminate\n * (some-but-not-all) state — the visual MRT uses for partial select-all.\n *\n * Implemented as a styled `role=\"checkbox\"` button (no headless primitive) so\n * the data table carries no direct dependency on a specific primitive library\n * and renders identically whether the consumer's shadcn setup is Radix- or\n * Base-UI-based. Mirrors the shadcn `Checkbox` styling so it blends in.\n *\n * The check / indeterminate glyphs are inline SVG (not the swappable `icons`\n * system, nor an icon-library import) so a selection checkbox never depends on\n * a particular icon package — it renders the same even if a consumer overrides\n * every table icon or doesn't install Lucide.\n */\nfunction SelectionCheckbox({\n  className,\n  indeterminate = false,\n  checked = false,\n  onCheckedChange,\n  ...props\n}: SelectionCheckboxProps) {\n  const active = checked || indeterminate\n  return (\n    <button\n      type=\"button\"\n      role=\"checkbox\"\n      data-slot=\"checkbox\"\n      aria-checked={indeterminate ? \"mixed\" : checked}\n      onClick={() => onCheckedChange?.(!checked)}\n      className={cn(\n        \"peer relative flex size-4.5 shrink-0 items-center justify-center rounded-none border bg-transparent transition-shadow outline-none focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/30 disabled:cursor-not-allowed disabled:opacity-50\",\n        active\n          ? \"border-primary bg-primary text-primary-foreground\"\n          : \"border-input\",\n        className\n      )}\n      {...props}\n    >\n      {active && (\n        <svg\n          viewBox=\"0 0 24 24\"\n          fill=\"none\"\n          stroke=\"currentColor\"\n          strokeWidth={3}\n          strokeLinecap=\"round\"\n          strokeLinejoin=\"round\"\n          className=\"size-3.5\"\n          aria-hidden=\"true\"\n        >\n          {indeterminate ? <path d=\"M5 12h14\" /> : <path d=\"M20 6 9 17l-5-5\" />}\n        </svg>\n      )}\n    </button>\n  )\n}\n\nexport { SelectionCheckbox }\n"
    },
    {
      "path": "ui/data-table/components/body/skeleton-rows.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/body/skeleton-rows.tsx",
      "content": "\"use client\"\n\nimport { Skeleton } from \"@/components/ui/skeleton\"\nimport { TableCell, TableRow } from \"@/components/ui/table\"\n\n/** Placeholder skeleton rows shown while the initial data load is in flight. */\nexport function SkeletonRows({\n  rowCount,\n  columnCount,\n  padding,\n}: {\n  rowCount: number\n  columnCount: number\n  padding: string\n}) {\n  return (\n    <>\n      {Array.from({ length: Math.max(1, rowCount) }).map((_, rowIndex) => (\n        <TableRow key={rowIndex} className=\"hover:bg-transparent\">\n          {Array.from({ length: Math.max(1, columnCount) }).map(\n            (__, colIndex) => (\n              <TableCell key={colIndex} className={padding}>\n                <Skeleton className=\"h-4 w-full max-w-48\" />\n              </TableCell>\n            )\n          )}\n        </TableRow>\n      ))}\n    </>\n  )\n}\n"
    },
    {
      "path": "ui/data-table/components/editing/data-table-create-row.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/editing/data-table-create-row.tsx",
      "content": "\"use client\"\n\nimport type { Column, RowData } from \"@tanstack/react-table\"\n\nimport { Button } from \"@/components/ui/button\"\nimport { TableCell, TableRow } from \"@/components/ui/table\"\n\nimport type { DataTableInstance } from \"../../core/types\"\nimport { getColumnLabel } from \"../../helpers/column-label\"\nimport { isColumnEditable } from \"../../helpers/is-column-editable\"\nimport { DataTableEditField } from \"./data-table-edit-field\"\n\n/**\n * Inline create row for `createDisplayMode: \"row\"`. Renders an editor per\n * editable column bound to the shared `rowDraft`, with Save/Cancel in a\n * full-width action strip beneath it. Save commits via `onCreateRow`.\n */\nexport function DataTableCreateRow<TData extends RowData>({\n  table,\n}: {\n  table: DataTableInstance<TData>\n}) {\n  const { enableEditing, rowDraft, onCreateRow, cancelEdit, localization } =\n    table.tableInstance\n\n  const leafColumns = table.getVisibleLeafColumns()\n  const editableColumns = leafColumns.filter(\n    (column) => enableEditing && isColumnEditable(column)\n  )\n  const hasErrors = editableColumns.some(\n    (column) => column.columnDef.meta?.validate?.(rowDraft[column.id]) != null\n  )\n\n  const submit = () => {\n    if (hasErrors) return\n    onCreateRow?.({ values: rowDraft, table, exit: cancelEdit })\n  }\n\n  return (\n    <>\n      <TableRow\n        data-slot=\"data-table-create-row\"\n        className=\"bg-muted/30 align-top hover:bg-muted/30\"\n      >\n        {leafColumns.map((column) => {\n          const editable = enableEditing && isColumnEditable(column)\n          return (\n            <TableCell key={column.id} className=\"p-2 align-top\">\n              {editable ? <CreateField column={column} table={table} /> : null}\n            </TableCell>\n          )\n        })}\n      </TableRow>\n      <TableRow className=\"border-b-2 bg-muted/30 hover:bg-muted/30\">\n        <TableCell colSpan={leafColumns.length} className=\"p-2\">\n          <div className=\"flex items-center justify-end gap-2\">\n            <Button variant=\"outline\" size=\"sm\" onClick={cancelEdit}>\n              {localization.cancel}\n            </Button>\n            <Button size=\"sm\" onClick={submit} disabled={hasErrors}>\n              {localization.save}\n            </Button>\n          </div>\n        </TableCell>\n      </TableRow>\n    </>\n  )\n}\n\n/** One editor in the create row, bound to the shared row draft. */\nfunction CreateField<TData extends RowData>({\n  column,\n  table,\n}: {\n  column: Column<TData, unknown>\n  table: DataTableInstance<TData>\n}) {\n  const cn = table.tableInstance\n  const meta = column.columnDef.meta\n  const value = cn.rowDraft[column.id]\n  return (\n    <DataTableEditField\n      value={value}\n      variant={meta?.editVariant}\n      options={meta?.editSelectOptions ?? meta?.options}\n      error={meta?.validate?.(value)}\n      ariaLabel={getColumnLabel(column)}\n      onChange={(next) => cn.setRowDraftValue(column.id, next)}\n    />\n  )\n}\n"
    },
    {
      "path": "ui/data-table/components/editing/data-table-edit-cell.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/editing/data-table-edit-cell.tsx",
      "content": "\"use client\"\n\nimport type { Cell, RowData } from \"@tanstack/react-table\"\nimport * as React from \"react\"\n\nimport {\n  ContextMenu,\n  ContextMenuContent,\n  ContextMenuTrigger,\n} from \"@/components/ui/context-menu\"\n\nimport type { DataTableInstance } from \"../../core/types\"\nimport { getColumnLabel } from \"../../helpers/column-label\"\nimport { isColumnEditable } from \"../../helpers/is-column-editable\"\nimport { ClickToCopy } from \"../body/click-to-copy\"\nimport { DataTableEditField } from \"./data-table-edit-field\"\n\n/**\n * Resolves a leaf data cell's interactive content: an inline editor when this\n * cell/row is being edited (cell/row/table modes), otherwise the value wrapped\n * with click-to-copy, click-to-edit (cell mode), and/or a cell-actions context\n * menu as configured. `fallback` is the normal rendered value.\n */\nexport function DataTableBodyCellContent<TData extends RowData>({\n  cell,\n  table,\n  fallback,\n}: {\n  cell: Cell<TData, unknown>\n  table: DataTableInstance<TData>\n  fallback: React.ReactNode\n}) {\n  const cn = table.tableInstance\n  const { row, column } = cell\n  const editable = cn.enableEditing && isColumnEditable(column)\n  const mode = cn.editDisplayMode\n\n  const isCellEditing =\n    mode === \"cell\" &&\n    cn.editingCell?.rowId === row.id &&\n    cn.editingCell?.columnId === column.id\n  const isRowEditing = mode === \"row\" && cn.editingRowId === row.id\n  const isTableEditing = mode === \"table\"\n\n  if (editable && (isCellEditing || isRowEditing || isTableEditing)) {\n    const renderEditCell = column.columnDef.meta?.renderEditCell\n    if (renderEditCell) {\n      return renderEditCell({ cell, row, column, table })\n    }\n    return mode === \"row\" ? (\n      <RowDraftEditor cell={cell} table={table} />\n    ) : (\n      <LocalDraftEditor\n        cell={cell}\n        table={table}\n        exitOnCommit={mode === \"cell\"}\n      />\n    )\n  }\n\n  let node: React.ReactNode = fallback\n\n  const copyEnabled =\n    column.columnDef.meta?.enableClickToCopy ?? cn.enableClickToCopy\n  if (copyEnabled) {\n    node = (\n      <ClickToCopy\n        value={String(cell.getValue() ?? \"\")}\n        copyLabel={cn.localization.copy}\n        copiedLabel={cn.localization.copied}\n      >\n        {node}\n      </ClickToCopy>\n    )\n  } else if (editable && mode === \"cell\") {\n    node = (\n      <button\n        type=\"button\"\n        onClick={() =>\n          cn.setEditingCell({ rowId: row.id, columnId: column.id })\n        }\n        className=\"-mx-1 w-full rounded-sm px-1 text-left transition-colors outline-none hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring/40\"\n      >\n        {node}\n      </button>\n    )\n  }\n\n  if (cn.renderCellActionMenuItems) {\n    node = (\n      <ContextMenu>\n        <ContextMenuTrigger asChild>\n          <span className=\"block\">{node}</span>\n        </ContextMenuTrigger>\n        <ContextMenuContent>\n          {cn.renderCellActionMenuItems({ cell, row, table })}\n        </ContextMenuContent>\n      </ContextMenu>\n    )\n  }\n\n  return node\n}\n\n/** Editor bound to the shared row draft (row/modal editing). */\nfunction RowDraftEditor<TData extends RowData>({\n  cell,\n  table,\n}: {\n  cell: Cell<TData, unknown>\n  table: DataTableInstance<TData>\n}) {\n  const cn = table.tableInstance\n  const { column } = cell\n  const meta = column.columnDef.meta\n  const value =\n    column.id in cn.rowDraft ? cn.rowDraft[column.id] : cell.getValue()\n  const error = meta?.validate?.(value)\n  return (\n    <DataTableEditField\n      value={value}\n      variant={meta?.editVariant}\n      options={meta?.editSelectOptions ?? meta?.options}\n      error={error}\n      ariaLabel={getColumnLabel(column)}\n      onChange={(next) => cn.setRowDraftValue(column.id, next)}\n    />\n  )\n}\n\n/** Editor with local draft state (cell + table editing). */\nfunction LocalDraftEditor<TData extends RowData>({\n  cell,\n  table,\n  exitOnCommit,\n}: {\n  cell: Cell<TData, unknown>\n  table: DataTableInstance<TData>\n  exitOnCommit: boolean\n}) {\n  const cn = table.tableInstance\n  const { row, column } = cell\n  const meta = column.columnDef.meta\n  const [draft, setDraft] = React.useState<unknown>(() => cell.getValue())\n  const error = meta?.validate?.(draft)\n\n  const commit = () => {\n    if (error) return\n    if (draft !== cell.getValue()) {\n      cn.onEditCellSave?.({ row, column, value: draft, table })\n    }\n    if (exitOnCommit) cn.setEditingCell(null)\n  }\n\n  const cancel = () => {\n    setDraft(cell.getValue())\n    if (exitOnCommit) cn.setEditingCell(null)\n  }\n\n  return (\n    <DataTableEditField\n      value={draft}\n      variant={meta?.editVariant}\n      options={meta?.editSelectOptions ?? meta?.options}\n      error={error}\n      ariaLabel={getColumnLabel(column)}\n      autoFocus={exitOnCommit}\n      onChange={setDraft}\n      onCommit={commit}\n      onCancel={cancel}\n    />\n  )\n}\n"
    },
    {
      "path": "ui/data-table/components/editing/data-table-edit-field.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/editing/data-table-edit-field.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\n\nimport { Input } from \"@/components/ui/input\"\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from \"@/components/ui/select\"\nimport { cn } from \"@/lib/utils\"\n\nimport type { DataTableFilterOption, EditVariant } from \"../../core/types\"\n\nconst FIELD_CLASS =\n  \"h-8 rounded-sm text-xs font-normal tracking-normal normal-case\"\n\n/**\n * A controlled inline edit field (text / number / select). The parent owns the\n * draft value; this renders the input, surfaces validation errors, and wires\n * Enter (commit) / Escape (cancel) / blur (commit) for text inputs.\n */\nexport function DataTableEditField({\n  value,\n  onChange,\n  onCommit,\n  onCancel,\n  variant = \"text\",\n  options,\n  error,\n  ariaLabel,\n  autoFocus,\n}: {\n  value: unknown\n  onChange: (value: unknown) => void\n  onCommit?: () => void\n  onCancel?: () => void\n  variant?: EditVariant\n  options?: DataTableFilterOption[]\n  error?: string\n  ariaLabel: string\n  autoFocus?: boolean\n}) {\n  if (variant === \"select\") {\n    return (\n      <div className=\"flex flex-col gap-0.5\">\n        <Select\n          // Always controlled: \"\" shows the placeholder in both Radix and\n          // Base UI, while `undefined` would flip to uncontrolled.\n          value={value == null ? \"\" : String(value)}\n          onValueChange={(next) => {\n            onChange(next)\n            onCommit?.()\n          }}\n        >\n          <SelectTrigger\n            size=\"sm\"\n            className={cn(FIELD_CLASS, \"w-full px-2\")}\n            aria-label={ariaLabel}\n            aria-invalid={!!error}\n          >\n            <SelectValue placeholder=\"—\" />\n          </SelectTrigger>\n          <SelectContent>\n            {(options ?? []).map((option) => (\n              <SelectItem key={option.value} value={option.value}>\n                {option.label}\n              </SelectItem>\n            ))}\n          </SelectContent>\n        </Select>\n        {error && <FieldError>{error}</FieldError>}\n      </div>\n    )\n  }\n\n  return (\n    <div className=\"flex flex-col gap-0.5\">\n      <Input\n        autoFocus={autoFocus}\n        type={variant === \"number\" ? \"number\" : \"text\"}\n        inputMode={variant === \"number\" ? \"decimal\" : undefined}\n        value={value == null ? \"\" : String(value)}\n        aria-label={ariaLabel}\n        aria-invalid={!!error}\n        onChange={(e) =>\n          onChange(\n            variant === \"number\"\n              ? e.target.value === \"\"\n                ? \"\"\n                : Number(e.target.value)\n              : e.target.value\n          )\n        }\n        onKeyDown={(e) => {\n          if (e.key === \"Enter\") {\n            e.preventDefault()\n            onCommit?.()\n          } else if (e.key === \"Escape\") {\n            e.preventDefault()\n            onCancel?.()\n          }\n        }}\n        onBlur={() => onCommit?.()}\n        className={FIELD_CLASS}\n      />\n      {error && <FieldError>{error}</FieldError>}\n    </div>\n  )\n}\n\nfunction FieldError({ children }: { children: React.ReactNode }) {\n  return <span className=\"text-[10px] text-destructive\">{children}</span>\n}\n"
    },
    {
      "path": "ui/data-table/components/editing/data-table-edit-modal.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/editing/data-table-edit-modal.tsx",
      "content": "\"use client\"\n\nimport type { RowData } from \"@tanstack/react-table\"\n\nimport { Button } from \"@/components/ui/button\"\nimport {\n  Dialog,\n  DialogContent,\n  DialogFooter,\n  DialogHeader,\n  DialogTitle,\n} from \"@/components/ui/dialog\"\nimport { Label } from \"@/components/ui/label\"\n\nimport type { DataTableInstance } from \"../../core/types\"\nimport { getColumnLabel } from \"../../helpers/column-label\"\nimport { isColumnEditable } from \"../../helpers/is-column-editable\"\nimport { DataTableEditField } from \"./data-table-edit-field\"\n\n/**\n * Edit/create dialog for `editDisplayMode: \"modal\"` (and the create form in any\n * mode). Renders an editor per editable column bound to the shared row draft;\n * Save commits via `onSaveRow` / `onCreateRow`.\n */\nexport function DataTableEditModal<TData extends RowData>({\n  table,\n}: {\n  table: DataTableInstance<TData>\n}) {\n  const {\n    localization,\n    editDisplayMode,\n    createDisplayMode,\n    editingRowId,\n    isCreating,\n    rowDraft,\n    setRowDraftValue,\n    cancelEdit,\n    onSaveRow,\n    onCreateRow,\n  } = table.tableInstance\n\n  const editingRow =\n    editingRowId != null ? table.getRow(editingRowId) : undefined\n  const open =\n    (isCreating && createDisplayMode === \"modal\") ||\n    (editDisplayMode === \"modal\" && editingRowId != null)\n  if (!open) return null\n\n  const editableColumns = table\n    .getAllLeafColumns()\n    .filter((column) => isColumnEditable(column))\n\n  const hasErrors = editableColumns.some((column) => {\n    const value =\n      column.id in rowDraft\n        ? rowDraft[column.id]\n        : editingRow?.getValue(column.id)\n    return column.columnDef.meta?.validate?.(value) != null\n  })\n\n  const submit = () => {\n    if (hasErrors) return\n    if (isCreating) {\n      onCreateRow?.({ values: rowDraft, table, exit: cancelEdit })\n    } else if (editingRow) {\n      onSaveRow?.({\n        row: editingRow,\n        values: rowDraft,\n        table,\n        exit: cancelEdit,\n      })\n    }\n  }\n\n  return (\n    <Dialog open onOpenChange={(next) => !next && cancelEdit()}>\n      <DialogContent className=\"sm:max-w-lg\">\n        <DialogHeader>\n          <DialogTitle>\n            {isCreating ? localization.createNewRow : localization.editRow}\n          </DialogTitle>\n        </DialogHeader>\n        <div className=\"flex flex-col gap-3 py-2\">\n          {editableColumns.map((column) => {\n            const meta = column.columnDef.meta\n            const value =\n              column.id in rowDraft\n                ? rowDraft[column.id]\n                : editingRow?.getValue(column.id)\n            return (\n              <div key={column.id} className=\"flex flex-col gap-1.5\">\n                <Label className=\"text-xs\">{getColumnLabel(column)}</Label>\n                <DataTableEditField\n                  value={value}\n                  variant={meta?.editVariant}\n                  options={meta?.editSelectOptions ?? meta?.options}\n                  error={meta?.validate?.(value)}\n                  ariaLabel={getColumnLabel(column)}\n                  onChange={(next) => setRowDraftValue(column.id, next)}\n                />\n              </div>\n            )\n          })}\n        </div>\n        <DialogFooter>\n          <Button variant=\"outline\" size=\"sm\" onClick={cancelEdit}>\n            {localization.cancel}\n          </Button>\n          <Button size=\"sm\" onClick={submit} disabled={hasErrors}>\n            {localization.save}\n          </Button>\n        </DialogFooter>\n      </DialogContent>\n    </Dialog>\n  )\n}\n"
    },
    {
      "path": "ui/data-table/components/head/data-table-column-filter.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/head/data-table-column-filter.tsx",
      "content": "\"use client\"\n\nimport type { Header, RowData } from \"@tanstack/react-table\"\n\nimport { DataTableFilterModeMenu } from \"../menus/data-table-filter-mode-menu\"\nimport {\n  CheckboxFilterField,\n  DateFilterField,\n  DateRangeFilterField,\n  MultiSelectFilterField,\n  NumberFilterField,\n  RangeSliderFilterField,\n  SelectFilterField,\n  TextFilterField,\n  type FilterFieldProps,\n} from \"./filter-variants\"\nimport type { DataTableInstance } from \"../../core/types\"\n\ninterface DataTableColumnFilterProps<TData extends RowData, TValue> {\n  header: Header<TData, TValue>\n  table: DataTableInstance<TData>\n}\n\n/**\n * One filter-row field. Reads `column.meta.variant` and renders the matching\n * control, preceded by the filter-mode menu (where the variant supports modes).\n * `meta.renderColumnFilter` is an escape hatch for fully custom UI. Returns an\n * empty placeholder for non-filterable columns so the grid stays aligned.\n */\nexport function DataTableColumnFilter<TData extends RowData, TValue>({\n  header,\n  table,\n}: DataTableColumnFilterProps<TData, TValue>) {\n  const { column } = header\n\n  if (header.isPlaceholder || !column.getCanFilter()) {\n    return <div className=\"h-8\" />\n  }\n\n  const custom = column.columnDef.meta?.renderColumnFilter\n  if (custom) {\n    return <>{custom({ column, table })}</>\n  }\n\n  return (\n    <div className=\"flex items-center gap-0.5\">\n      <DataTableFilterModeMenu column={column} table={table} />\n      <div className=\"min-w-0 flex-1\">\n        <FilterField column={column} table={table} />\n      </div>\n    </div>\n  )\n}\n\nfunction FilterField<TData extends RowData, TValue>({\n  column,\n  table,\n}: FilterFieldProps<TData, TValue>) {\n  const variant = column.columnDef.meta?.variant ?? \"text\"\n  switch (variant) {\n    case \"select\":\n      return <SelectFilterField column={column} table={table} />\n    case \"multi-select\":\n      return <MultiSelectFilterField column={column} table={table} />\n    case \"checkbox\":\n      return <CheckboxFilterField column={column} table={table} />\n    case \"range\":\n      return <NumberFilterField column={column} table={table} />\n    case \"range-slider\":\n      return <RangeSliderFilterField column={column} table={table} />\n    case \"date\":\n      return <DateFilterField column={column} table={table} />\n    case \"date-range\":\n      return <DateRangeFilterField column={column} table={table} />\n    default:\n      return <TextFilterField column={column} table={table} />\n  }\n}\n"
    },
    {
      "path": "ui/data-table/components/head/data-table-column-header.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/head/data-table-column-header.tsx",
      "content": "\"use client\"\n\nimport { flexRender, type Header, type RowData } from \"@tanstack/react-table\"\n\nimport { Badge } from \"@/components/ui/badge\"\nimport { Button } from \"@/components/ui/button\"\nimport {\n  Popover,\n  PopoverContent,\n  PopoverTrigger,\n} from \"@/components/ui/popover\"\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipTrigger,\n} from \"@/components/ui/tooltip\"\nimport { cn } from \"@/lib/utils\"\n\nimport type { DataTableInstance } from \"../../core/types\"\nimport { getColumnLabel } from \"../../helpers/column-label\"\nimport {\n  headerControlsOptionsFromTable,\n  shouldShowColumnActions,\n  shouldShowColumnFilterButton,\n} from \"../../helpers/header-controls\"\nimport { ColumnDragHandle } from \"../body/dnd\"\nimport { DataTableColumnActions } from \"../menus/data-table-column-actions\"\nimport { DataTableColumnFilter } from \"./data-table-column-filter\"\n\ninterface DataTableColumnHeaderProps<TData extends RowData, TValue> {\n  header: Header<TData, TValue>\n  table: DataTableInstance<TData>\n}\n\nconst ALIGN_CLASS = {\n  left: \"justify-start text-left\",\n  center: \"justify-center text-center\",\n  right: \"justify-end text-right\",\n} as const\n\n/**\n * Wraps every header cell with the MRT layout: label (sort trigger) · sort\n * indicator · multi-sort order badge · column-actions menu, left-aligned by\n * default. Clicking the label cycles asc → desc → none; shift-click multi-sorts\n * (both via TanStack's `getToggleSortingHandler`).\n */\nexport function DataTableColumnHeader<TData extends RowData, TValue>({\n  header,\n  table,\n}: DataTableColumnHeaderProps<TData, TValue>) {\n  const { column } = header\n  const { localization, icons } = table.tableInstance\n  const controls = headerControlsOptionsFromTable(table)\n  const align = column.columnDef.meta?.align ?? \"left\"\n\n  const showFilterPopover = shouldShowColumnFilterButton(column, controls)\n  const hasFilter = column.getFilterValue() != null\n  const filterPopover = showFilterPopover ? (\n    <Popover>\n      <Tooltip>\n        <TooltipTrigger asChild>\n          <PopoverTrigger asChild>\n            <Button\n              variant=\"ghost\"\n              size=\"icon\"\n              aria-label={localization.filterByColumn(getColumnLabel(column))}\n              className={cn(\n                \"size-7 shrink-0 opacity-70 transition-opacity group-hover/th:opacity-100 focus-visible:opacity-100 data-[state=open]:opacity-100\",\n                hasFilter && \"text-primary opacity-100\"\n              )}\n            >\n              {hasFilter ? <icons.filter /> : <icons.filterOff />}\n            </Button>\n          </PopoverTrigger>\n        </TooltipTrigger>\n        <TooltipContent>\n          {localization.filterByColumn(getColumnLabel(column))}\n        </TooltipContent>\n      </Tooltip>\n      <PopoverContent align=\"start\" className=\"w-64 gap-2\">\n        <span className=\"text-xs font-semibold tracking-wide text-muted-foreground uppercase\">\n          {localization.filterByColumn(getColumnLabel(column))}\n        </span>\n        <DataTableColumnFilter header={header} table={table} />\n      </PopoverContent>\n    </Popover>\n  ) : null\n\n  const labelNode = header.isPlaceholder\n    ? null\n    : flexRender(column.columnDef.header, header.getContext())\n\n  const canSort = column.getCanSort()\n  const sorted = column.getIsSorted() // false | \"asc\" | \"desc\"\n  const sortIndex = column.getSortIndex()\n  const isMultiSort = table.getState().sorting.length > 1 && sortIndex >= 0\n\n  const showActions = shouldShowColumnActions(column, controls)\n\n  const dragHandle = (\n    <ColumnDragHandle\n      label={localization.reorderColumn}\n      Icon={icons.dragHandle}\n    />\n  )\n\n  // Non-interactive header (e.g. the selection column): render content plainly.\n  if (!canSort && !showActions) {\n    return (\n      <div\n        className={cn(\"group/th flex items-center gap-1\", ALIGN_CLASS[align])}\n      >\n        {labelNode}\n        {dragHandle}\n        {filterPopover}\n      </div>\n    )\n  }\n\n  const sortTooltip = !sorted\n    ? localization.sortByColumnAsc(getColumnLabel(column))\n    : sorted === \"asc\"\n      ? localization.sortByColumnDesc(getColumnLabel(column))\n      : localization.clearSort\n\n  return (\n    <div\n      className={cn(\n        \"group/th flex items-center gap-0.5\",\n        align === \"right\" && \"flex-row-reverse\",\n        ALIGN_CLASS[align]\n      )}\n    >\n      {canSort ? (\n        <Tooltip>\n          <TooltipTrigger asChild>\n            <button\n              type=\"button\"\n              onClick={column.getToggleSortingHandler()}\n              aria-label={sortTooltip}\n              className=\"-mx-1.5 flex min-w-0 items-center gap-1 rounded-sm px-1.5 py-1 text-xs font-medium tracking-wider text-muted-foreground uppercase transition-colors outline-none hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring/40 data-[sorted=true]:text-foreground\"\n              data-sorted={!!sorted}\n            >\n              <span className=\"min-w-0 truncate\">{labelNode}</span>\n              <SortIndicator sorted={sorted} icons={icons} />\n              {isMultiSort && (\n                <Badge\n                  variant=\"secondary\"\n                  className=\"ml-0.5 h-4 min-w-4 shrink-0 justify-center rounded-sm px-1 text-[10px] leading-none tabular-nums\"\n                >\n                  {sortIndex + 1}\n                </Badge>\n              )}\n            </button>\n          </TooltipTrigger>\n          <TooltipContent>{sortTooltip}</TooltipContent>\n        </Tooltip>\n      ) : (\n        <span className=\"min-w-0 truncate px-0 text-xs font-medium tracking-wider uppercase\">\n          {labelNode}\n        </span>\n      )}\n\n      {dragHandle}\n      {showActions && (\n        <DataTableColumnActions\n          column={column}\n          table={table}\n          className=\"-my-1\"\n        />\n      )}\n      {filterPopover}\n    </div>\n  )\n}\n\nfunction SortIndicator({\n  sorted,\n  icons,\n}: {\n  sorted: false | \"asc\" | \"desc\"\n  icons: DataTableInstance[\"tableInstance\"][\"icons\"]\n}) {\n  if (sorted === \"asc\")\n    return <icons.sortAscending className=\"size-3.5 shrink-0\" />\n  if (sorted === \"desc\")\n    return <icons.sortDescending className=\"size-3.5 shrink-0\" />\n  return (\n    <icons.sortUnsorted className=\"size-3.5 shrink-0 opacity-50 transition-opacity group-hover/th:opacity-70\" />\n  )\n}\n"
    },
    {
      "path": "ui/data-table/components/head/data-table-header.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/head/data-table-header.tsx",
      "content": "\"use client\"\n\nimport {\n  SortableContext,\n  horizontalListSortingStrategy,\n} from \"@dnd-kit/sortable\"\nimport type { Header, RowData } from \"@tanstack/react-table\"\nimport type { VirtualItem } from \"@tanstack/react-virtual\"\nimport * as React from \"react\"\n\nimport {\n  TableHead,\n  TableHeader,\n  TableRow,\n} from \"@/components/ui/table\"\nimport { cn } from \"@/lib/utils\"\n\nimport { DENSITY_CELL_PADDING } from \"../../core/constants\"\nimport type { DataTableInstance } from \"../../core/types\"\nimport {\n  headerControlsOptionsFromTable,\n  shouldShowColumnDragGrip,\n} from \"../../helpers/header-controls\"\nimport type { WithColumnSpacers } from \"../../hooks/use-table-virtualizers\"\nimport {\n  getColumnPinningClass,\n  getColumnPinningStyle,\n  getWidthStyle,\n} from \"../../utils/column-styles\"\nimport { DataTableHeadCell } from \"../body/dnd\"\nimport { DataTableColumnFilter } from \"./data-table-column-filter\"\nimport { DataTableColumnHeader } from \"./data-table-column-header\"\n\ninterface DataTableHeaderProps<TData extends RowData> {\n  table: DataTableInstance<TData>\n  virtualColumns: VirtualItem[]\n  withColumnSpacers: WithColumnSpacers\n}\n\n/**\n * The table `<thead>`: the (optionally sticky) header-group rows and, when\n * enabled, the per-column filter subheader row. Honors column virtualization,\n * ordering (drag handles), pinning, and resizing.\n */\nexport function DataTableHeader<TData extends RowData>({\n  table,\n  virtualColumns,\n  withColumnSpacers,\n}: DataTableHeaderProps<TData>) {\n  const {\n    density,\n    enableColumnResizing,\n    enableColumnVirtualization,\n    enableColumnFilters,\n    showColumnFilters,\n    columnFilterDisplayMode,\n    enableStickyHeader,\n    refs,\n  } = table.tableInstance\n\n  const controls = headerControlsOptionsFromTable(table)\n  const padding = DENSITY_CELL_PADDING[density]\n  const leafColumnIds = table.getVisibleLeafColumns().map((c) => c.id)\n\n  const anyFilterable = table\n    .getAllColumns()\n    .some((column) => column.getCanFilter())\n  const filterRowVisible =\n    enableColumnFilters &&\n    showColumnFilters &&\n    anyFilterable &&\n    columnFilterDisplayMode === \"subheader\"\n\n  const renderHeadCell = (header: Header<TData, unknown>) => (\n    <DataTableHeadCell\n      key={header.id}\n      header={header}\n      table={table}\n      // Draggable for reordering (column ordering) or to drag onto the group\n      // zone (grouping) — the drag-end handler routes by the drop target. The\n      // grip's presence drives the column's reserved width, so both read the\n      // same predicate (see helpers/header-controls).\n      draggable={shouldShowColumnDragGrip(header.column, controls)}\n      resizable={enableColumnResizing}\n      widthStyle={getWidthStyle(header.column, table)}\n      padding={padding}\n    >\n      {header.isPlaceholder ? null : (\n        <DataTableColumnHeader header={header} table={table} />\n      )}\n    </DataTableHeadCell>\n  )\n\n  const renderFilterCell = (header: Header<TData, unknown>) => (\n    <TableHead\n      key={header.id}\n      colSpan={header.colSpan}\n      style={{\n        ...getWidthStyle(header.column, table),\n        ...getColumnPinningStyle(header.column),\n      }}\n      className={cn(\"bg-background\", getColumnPinningClass(header.column))}\n    >\n      <DataTableColumnFilter header={header} table={table} />\n    </TableHead>\n  )\n\n  return (\n    <TableHeader\n      // Forwarding the exposed DOM ref object as a JSX ref (not reading\n      // .current during render).\n      // eslint-disable-next-line react-hooks/refs\n      ref={refs.tableHeadRef}\n      className={cn(enableStickyHeader && \"sticky top-0 z-20 bg-background\")}\n    >\n      {table.getHeaderGroups().map((headerGroup) => (\n        <TableRow\n          key={headerGroup.id}\n          className=\"group/th hover:bg-transparent\"\n        >\n          {enableColumnVirtualization ? (\n            withColumnSpacers(\n              virtualColumns\n                .map((vc) => {\n                  const header = headerGroup.headers[vc.index]\n                  return header ? renderHeadCell(header) : null\n                })\n                .filter(Boolean) as React.ReactNode[],\n              `head-${headerGroup.id}`\n            )\n          ) : (\n            <SortableContext\n              items={leafColumnIds}\n              strategy={horizontalListSortingStrategy}\n            >\n              {headerGroup.headers.map(renderHeadCell)}\n            </SortableContext>\n          )}\n        </TableRow>\n      ))}\n\n      {filterRowVisible &&\n        table.getHeaderGroups().map((headerGroup) => (\n          <TableRow\n            key={`${headerGroup.id}-filters`}\n            className=\"hover:bg-transparent\"\n          >\n            {enableColumnVirtualization\n              ? withColumnSpacers(\n                  virtualColumns\n                    .map((vc) => {\n                      const header = headerGroup.headers[vc.index]\n                      return header ? renderFilterCell(header) : null\n                    })\n                    .filter(Boolean) as React.ReactNode[],\n                  `filter-${headerGroup.id}`\n                )\n              : headerGroup.headers.map(renderFilterCell)}\n          </TableRow>\n        ))}\n    </TableHeader>\n  )\n}\n"
    },
    {
      "path": "ui/data-table/components/head/filter-variants/checkbox.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/head/filter-variants/checkbox.tsx",
      "content": "\"use client\"\n\nimport type { RowData } from \"@tanstack/react-table\"\n\nimport { Checkbox } from \"@/components/ui/checkbox\"\n\nimport { getColumnLabel } from \"../../../helpers/column-label\"\nimport type { FilterFieldProps } from \"./shared\"\n\nexport function CheckboxFilterField<TData extends RowData, TValue>({\n  column,\n  table,\n}: FilterFieldProps<TData, TValue>) {\n  const { localization } = table.tableInstance\n  const value = column.getFilterValue()\n  const checked = value === true\n  return (\n    <label className=\"flex h-8 items-center gap-2 text-xs text-muted-foreground\">\n      <Checkbox\n        checked={checked}\n        onCheckedChange={(next) =>\n          column.setFilterValue(next === true ? true : undefined)\n        }\n        aria-label={localization.filterByColumn(getColumnLabel(column))}\n      />\n      {getColumnLabel(column)}\n    </label>\n  )\n}\n"
    },
    {
      "path": "ui/data-table/components/head/filter-variants/date-range.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/head/filter-variants/date-range.tsx",
      "content": "\"use client\"\n\nimport type { RowData } from \"@tanstack/react-table\"\nimport { format } from \"date-fns\"\n\nimport { Button } from \"@/components/ui/button\"\nimport { Calendar } from \"@/components/ui/calendar\"\nimport {\n  Popover,\n  PopoverContent,\n  PopoverTrigger,\n} from \"@/components/ui/popover\"\nimport { cn } from \"@/lib/utils\"\n\nimport { getColumnLabel } from \"../../../helpers/column-label\"\nimport {\n  CALENDAR_NAV_PROPS,\n  FIELD_CLASS,\n  type FilterFieldProps,\n} from \"./shared\"\n\nexport function DateRangeFilterField<TData extends RowData, TValue>({\n  column,\n  table,\n}: FilterFieldProps<TData, TValue>) {\n  const { localization, icons } = table.tableInstance\n  const value = (column.getFilterValue() as [Date?, Date?]) ?? [\n    undefined,\n    undefined,\n  ]\n  const from = value[0]\n  const to = value[1]\n  return (\n    <Popover>\n      <PopoverTrigger asChild>\n        <Button\n          variant=\"outline\"\n          size=\"sm\"\n          className={cn(\n            FIELD_CLASS,\n            \"w-full justify-start gap-2 px-2 font-normal\"\n          )}\n          aria-label={localization.filterByColumn(getColumnLabel(column))}\n        >\n          <icons.calendar className=\"text-muted-foreground\" />\n          {from || to ? (\n            <span className=\"truncate\">\n              {from ? format(from, \"PP\") : \"…\"} – {to ? format(to, \"PP\") : \"…\"}\n            </span>\n          ) : (\n            <span className=\"truncate text-muted-foreground\">\n              {localization.pickDateRange}\n            </span>\n          )}\n        </Button>\n      </PopoverTrigger>\n      <PopoverContent className=\"w-auto p-0\" align=\"start\">\n        <Calendar\n          mode=\"range\"\n          selected={{ from, to }}\n          onSelect={(range) =>\n            column.setFilterValue(\n              range?.from || range?.to ? [range?.from, range?.to] : undefined\n            )\n          }\n          autoFocus\n          {...CALENDAR_NAV_PROPS}\n        />\n      </PopoverContent>\n    </Popover>\n  )\n}\n"
    },
    {
      "path": "ui/data-table/components/head/filter-variants/date.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/head/filter-variants/date.tsx",
      "content": "\"use client\"\n\nimport type { RowData } from \"@tanstack/react-table\"\nimport { format } from \"date-fns\"\n\nimport { Button } from \"@/components/ui/button\"\nimport { Calendar } from \"@/components/ui/calendar\"\nimport {\n  Popover,\n  PopoverContent,\n  PopoverTrigger,\n} from \"@/components/ui/popover\"\nimport { cn } from \"@/lib/utils\"\n\nimport { VALUELESS_MODES } from \"../../../fns/filter-fns\"\nimport { getColumnLabel } from \"../../../helpers/column-label\"\nimport { getEffectiveMode } from \"../../../helpers/effective-filter-mode\"\nimport { DateRangeFilterField } from \"./date-range\"\nimport {\n  CALENDAR_NAV_PROPS,\n  FIELD_CLASS,\n  ValuelessLabel,\n  type FilterFieldProps,\n} from \"./shared\"\n\nexport function DateFilterField<TData extends RowData, TValue>({\n  column,\n  table,\n}: FilterFieldProps<TData, TValue>) {\n  const { localization, icons } = table.tableInstance\n  const mode = getEffectiveMode(column, table)\n\n  if (VALUELESS_MODES.has(mode)) {\n    return <ValuelessLabel label={localization.filterModes[mode] ?? mode} />\n  }\n\n  // The `betweenDates` mode (and the date-range variant) selects a range.\n  if (mode === \"betweenDates\") {\n    return <DateRangeFilterField column={column} table={table} />\n  }\n\n  const value = column.getFilterValue() as Date | undefined\n  return (\n    <Popover>\n      <PopoverTrigger asChild>\n        <Button\n          variant=\"outline\"\n          size=\"sm\"\n          className={cn(\n            FIELD_CLASS,\n            \"w-full justify-start gap-2 px-2 font-normal\"\n          )}\n          aria-label={localization.filterByColumn(getColumnLabel(column))}\n        >\n          <icons.calendar className=\"text-muted-foreground\" />\n          {value ? (\n            <span className=\"truncate\">{format(value, \"PP\")}</span>\n          ) : (\n            <span className=\"truncate text-muted-foreground\">\n              {localization.pickDate}\n            </span>\n          )}\n        </Button>\n      </PopoverTrigger>\n      <PopoverContent className=\"w-auto p-0\" align=\"start\">\n        <Calendar\n          mode=\"single\"\n          selected={value}\n          onSelect={(date) => column.setFilterValue(date ?? undefined)}\n          autoFocus\n          {...CALENDAR_NAV_PROPS}\n        />\n      </PopoverContent>\n    </Popover>\n  )\n}\n"
    },
    {
      "path": "ui/data-table/components/head/filter-variants/index.ts",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/head/filter-variants/index.ts",
      "content": "\"use client\"\n\n// Filter-row field components, one per `meta.variant`. Selected by\n// `DataTableColumnFilter`. Shared helpers live in `./shared`.\n\nexport type { FilterFieldProps } from \"./shared\"\nexport { TextFilterField } from \"./text\"\nexport { NumberFilterField } from \"./number\"\nexport { RangeSliderFilterField } from \"./range-slider\"\nexport { SelectFilterField } from \"./select\"\nexport { MultiSelectFilterField } from \"./multi-select\"\nexport { CheckboxFilterField } from \"./checkbox\"\nexport { DateFilterField } from \"./date\"\nexport { DateRangeFilterField } from \"./date-range\"\n"
    },
    {
      "path": "ui/data-table/components/head/filter-variants/multi-select.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/head/filter-variants/multi-select.tsx",
      "content": "\"use client\"\n\nimport type { RowData } from \"@tanstack/react-table\"\n\nimport { Badge } from \"@/components/ui/badge\"\nimport { Button } from \"@/components/ui/button\"\nimport { Checkbox } from \"@/components/ui/checkbox\"\nimport {\n  Command,\n  CommandEmpty,\n  CommandGroup,\n  CommandInput,\n  CommandItem,\n  CommandList,\n} from \"@/components/ui/command\"\nimport {\n  Popover,\n  PopoverContent,\n  PopoverTrigger,\n} from \"@/components/ui/popover\"\nimport { cn } from \"@/lib/utils\"\n\nimport { getColumnLabel } from \"../../../helpers/column-label\"\nimport { FIELD_CLASS, useSelectOptions, type FilterFieldProps } from \"./shared\"\n\nexport function MultiSelectFilterField<TData extends RowData, TValue>({\n  column,\n  table,\n}: FilterFieldProps<TData, TValue>) {\n  const { localization } = table.tableInstance\n  const { options, counts } = useSelectOptions(column)\n  const selected = (column.getFilterValue() as string[]) ?? []\n\n  const toggle = (value: string) => {\n    const next = selected.includes(value)\n      ? selected.filter((v) => v !== value)\n      : [...selected, value]\n    column.setFilterValue(next.length > 0 ? next : undefined)\n  }\n\n  return (\n    <Popover>\n      <PopoverTrigger asChild>\n        <Button\n          variant=\"outline\"\n          size=\"sm\"\n          className={cn(FIELD_CLASS, \"w-full justify-between px-2 font-normal\")}\n          aria-label={localization.filterByColumn(getColumnLabel(column))}\n        >\n          {selected.length > 0 ? (\n            <span className=\"flex min-w-0 items-center gap-1\">\n              <Badge variant=\"secondary\" className=\"rounded-sm px-1\">\n                {selected.length}\n              </Badge>\n              <span className=\"truncate\">{selected.join(\", \")}</span>\n            </span>\n          ) : (\n            <span className=\"truncate text-muted-foreground\">\n              {localization.filterPlaceholder(getColumnLabel(column))}\n            </span>\n          )}\n        </Button>\n      </PopoverTrigger>\n      <PopoverContent className=\"w-56 p-0\" align=\"start\">\n        <Command>\n          <CommandInput placeholder={localization.search} className=\"h-8\" />\n          <CommandList>\n            <CommandEmpty>{localization.noRecordsToDisplay}</CommandEmpty>\n            <CommandGroup>\n              {options.map((option) => {\n                const isSelected = selected.includes(option.value)\n                return (\n                  <CommandItem\n                    key={option.value}\n                    value={option.value}\n                    onSelect={() => toggle(option.value)}\n                    className=\"gap-2\"\n                  >\n                    <Checkbox\n                      checked={isSelected}\n                      className=\"pointer-events-none\"\n                    />\n                    <span className=\"flex-1 truncate\">{option.label}</span>\n                    <span className=\"text-xs text-muted-foreground tabular-nums\">\n                      {counts.get(option.value) ?? 0}\n                    </span>\n                  </CommandItem>\n                )\n              })}\n            </CommandGroup>\n          </CommandList>\n        </Command>\n      </PopoverContent>\n    </Popover>\n  )\n}\n"
    },
    {
      "path": "ui/data-table/components/head/filter-variants/number.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/head/filter-variants/number.tsx",
      "content": "\"use client\"\n\nimport type { RowData } from \"@tanstack/react-table\"\n\nimport { Input } from \"@/components/ui/input\"\n\nimport { VALUELESS_MODES } from \"../../../fns/filter-fns\"\nimport { getColumnLabel } from \"../../../helpers/column-label\"\nimport { getEffectiveMode } from \"../../../helpers/effective-filter-mode\"\nimport {\n  BETWEEN_MODES,\n  FIELD_CLASS,\n  ValuelessLabel,\n  type FilterFieldProps,\n} from \"./shared\"\n\nexport function NumberFilterField<TData extends RowData, TValue>({\n  column,\n  table,\n}: FilterFieldProps<TData, TValue>) {\n  const { localization } = table.tableInstance\n  const mode = getEffectiveMode(column, table)\n\n  if (VALUELESS_MODES.has(mode)) {\n    return <ValuelessLabel label={localization.filterModes[mode] ?? mode} />\n  }\n\n  if (BETWEEN_MODES.has(mode)) {\n    const value = (column.getFilterValue() ?? [\"\", \"\"]) as [\n      string | number,\n      string | number,\n    ]\n    const setBound = (index: 0 | 1, next: string) => {\n      const draft: [string | number, string | number] = [\n        value[0] ?? \"\",\n        value[1] ?? \"\",\n      ]\n      draft[index] = next\n      column.setFilterValue(\n        draft[0] === \"\" && draft[1] === \"\" ? undefined : draft\n      )\n    }\n    return (\n      <div className=\"flex items-center gap-1\">\n        <Input\n          type=\"number\"\n          inputMode=\"decimal\"\n          value={String(value[0] ?? \"\")}\n          onChange={(e) => setBound(0, e.target.value)}\n          placeholder={localization.min}\n          aria-label={localization.min}\n          className={FIELD_CLASS}\n        />\n        <span className=\"text-muted-foreground\">–</span>\n        <Input\n          type=\"number\"\n          inputMode=\"decimal\"\n          value={String(value[1] ?? \"\")}\n          onChange={(e) => setBound(1, e.target.value)}\n          placeholder={localization.max}\n          aria-label={localization.max}\n          className={FIELD_CLASS}\n        />\n      </div>\n    )\n  }\n\n  const value = (column.getFilterValue() ?? \"\") as string\n  return (\n    <Input\n      type=\"number\"\n      inputMode=\"decimal\"\n      value={value}\n      onChange={(e) => column.setFilterValue(e.target.value || undefined)}\n      placeholder={localization.filterPlaceholder(getColumnLabel(column))}\n      aria-label={localization.filterByColumn(getColumnLabel(column))}\n      className={FIELD_CLASS}\n    />\n  )\n}\n"
    },
    {
      "path": "ui/data-table/components/head/filter-variants/range-slider.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/head/filter-variants/range-slider.tsx",
      "content": "\"use client\"\n\nimport type { RowData } from \"@tanstack/react-table\"\n\nimport { Slider } from \"@/components/ui/slider\"\n\nimport { getColumnLabel } from \"../../../helpers/column-label\"\nimport type { FilterFieldProps } from \"./shared\"\n\nexport function RangeSliderFilterField<TData extends RowData, TValue>({\n  column,\n}: FilterFieldProps<TData, TValue>) {\n  const facetedMinMax = column.getFacetedMinMaxValues()\n  const min = Math.floor(facetedMinMax?.[0] ?? 0)\n  const max = Math.ceil(facetedMinMax?.[1] ?? 100)\n  const value = (column.getFilterValue() ?? [min, max]) as [number, number]\n  const current: [number, number] = [\n    value[0] === (\"\" as unknown) || value[0] == null ? min : Number(value[0]),\n    value[1] === (\"\" as unknown) || value[1] == null ? max : Number(value[1]),\n  ]\n  return (\n    <div className=\"flex flex-col gap-1.5 px-1 pt-1\">\n      <Slider\n        min={min}\n        max={max}\n        step={1}\n        value={current}\n        onValueChange={(next) => {\n          // Radix always emits `number[]`; Base UI emits `number` for a\n          // single-thumb slider. Normalize to a pair so both flavors index\n          // safely.\n          const pair = Array.isArray(next) ? next : [next, next]\n          column.setFilterValue(\n            pair[0] === min && pair[1] === max ? undefined : pair\n          )\n        }}\n        aria-label={getColumnLabel(column)}\n      />\n      <div className=\"flex justify-between text-[10px] text-muted-foreground tabular-nums\">\n        <span>{current[0]}</span>\n        <span>{current[1]}</span>\n      </div>\n    </div>\n  )\n}\n"
    },
    {
      "path": "ui/data-table/components/head/filter-variants/select.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/head/filter-variants/select.tsx",
      "content": "\"use client\"\n\nimport type { RowData } from \"@tanstack/react-table\"\n\nimport { Button } from \"@/components/ui/button\"\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from \"@/components/ui/select\"\nimport { cn } from \"@/lib/utils\"\n\nimport { getColumnLabel } from \"../../../helpers/column-label\"\nimport { FIELD_CLASS, useSelectOptions, type FilterFieldProps } from \"./shared\"\n\nexport function SelectFilterField<TData extends RowData, TValue>({\n  column,\n  table,\n}: FilterFieldProps<TData, TValue>) {\n  const { localization, icons } = table.tableInstance\n  const { options } = useSelectOptions(column)\n  const value = (column.getFilterValue() as string) || \"\"\n  return (\n    <div className=\"flex items-center gap-1\">\n      <Select\n        // Always controlled: \"\" shows the placeholder in both Radix and Base\n        // UI, while `undefined` would flip to uncontrolled and go stale.\n        value={value}\n        onValueChange={(next) => column.setFilterValue(next || undefined)}\n      >\n        <SelectTrigger\n          size=\"sm\"\n          className={cn(FIELD_CLASS, \"w-full min-w-0 flex-1 px-2\")}\n          aria-label={localization.filterByColumn(getColumnLabel(column))}\n        >\n          <SelectValue\n            placeholder={localization.filterPlaceholder(getColumnLabel(column))}\n          />\n        </SelectTrigger>\n        <SelectContent>\n          {options.map((option) => (\n            <SelectItem key={option.value} value={option.value}>\n              {option.label}\n            </SelectItem>\n          ))}\n        </SelectContent>\n      </Select>\n      {value && (\n        <Button\n          variant=\"ghost\"\n          size=\"icon\"\n          aria-label={localization.clearFilter}\n          onClick={() => column.setFilterValue(undefined)}\n          className=\"size-7\"\n        >\n          <icons.clear />\n        </Button>\n      )}\n    </div>\n  )\n}\n"
    },
    {
      "path": "ui/data-table/components/head/filter-variants/shared.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/head/filter-variants/shared.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport type { Column, RowData } from \"@tanstack/react-table\"\n\nimport { Input } from \"@/components/ui/input\"\nimport { cn } from \"@/lib/utils\"\n\nimport type { IconComponent } from \"../../../core/icons\"\nimport type {\n  DataTableFilterOption,\n  DataTableInstance,\n} from \"../../../core/types\"\n\nexport interface FilterFieldProps<TData extends RowData, TValue> {\n  column: Column<TData, TValue>\n  table: DataTableInstance<TData>\n}\n\nexport const FIELD_CLASS =\n  \"h-8 rounded-sm text-xs font-normal tracking-normal normal-case\"\n\nexport const BETWEEN_MODES = new Set([\"between\", \"betweenInclusive\"])\n\n/**\n * Shared Calendar props so the month/year dropdowns are usable regardless of\n * which base Calendar component the consumer ships. The year dropdown derives\n * its options from the startMonth/endMonth range.\n */\nexport const CALENDAR_NAV_PROPS = {\n  captionLayout: \"dropdown\",\n  startMonth: new Date(new Date().getFullYear() - 100, 0),\n  endMonth: new Date(new Date().getFullYear() + 10, 11),\n} as const\n\n/** A muted pill shown for valueless modes (empty / not empty). */\nexport function ValuelessLabel({ label }: { label: string }) {\n  return (\n    <div className=\"flex h-8 items-center rounded-sm border border-dashed px-2 text-xs text-muted-foreground\">\n      {label}\n    </div>\n  )\n}\n\n/** Options for select-style variants: explicit `meta.options` or faceted values. */\nexport function useSelectOptions<TData extends RowData, TValue>(\n  column: Column<TData, TValue>\n): { options: DataTableFilterOption[]; counts: Map<string, number> } {\n  const facets = column.getFacetedUniqueValues()\n  return React.useMemo(() => {\n    const counts = new Map<string, number>()\n    for (const [value, count] of facets) {\n      if (value == null) continue\n      counts.set(String(value), count)\n    }\n    const explicit = column.columnDef.meta?.options\n    if (explicit && explicit.length > 0) {\n      return { options: explicit, counts }\n    }\n    const options = Array.from(counts.keys())\n      .sort((a, b) => a.localeCompare(b))\n      .map((value) => ({ label: value, value }))\n    return { options, counts }\n  }, [facets, column.columnDef.meta?.options])\n}\n\n/** A text input with a trailing clear affordance. */\nexport function ClearableInput({\n  value,\n  onChange,\n  placeholder,\n  ariaLabel,\n  clearLabel,\n  ClearIcon,\n}: {\n  value: string\n  onChange: (next: string) => void\n  placeholder: string\n  ariaLabel: string\n  clearLabel: string\n  ClearIcon: IconComponent\n}) {\n  return (\n    <div className=\"relative flex-1\">\n      <Input\n        value={value}\n        onChange={(e) => onChange(e.target.value)}\n        placeholder={placeholder}\n        aria-label={ariaLabel}\n        className={cn(FIELD_CLASS, value && \"pr-7\")}\n      />\n      {value && (\n        <button\n          type=\"button\"\n          aria-label={clearLabel}\n          onClick={() => onChange(\"\")}\n          className=\"absolute inset-y-0 right-1.5 flex items-center text-muted-foreground transition-colors hover:text-foreground\"\n        >\n          <ClearIcon className=\"size-3.5\" />\n        </button>\n      )}\n    </div>\n  )\n}\n"
    },
    {
      "path": "ui/data-table/components/head/filter-variants/text.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/head/filter-variants/text.tsx",
      "content": "\"use client\"\n\nimport type { RowData } from \"@tanstack/react-table\"\n\nimport { VALUELESS_MODES } from \"../../../fns/filter-fns\"\nimport { getColumnLabel } from \"../../../helpers/column-label\"\nimport { getEffectiveMode } from \"../../../helpers/effective-filter-mode\"\nimport { ClearableInput, ValuelessLabel, type FilterFieldProps } from \"./shared\"\n\nexport function TextFilterField<TData extends RowData, TValue>({\n  column,\n  table,\n}: FilterFieldProps<TData, TValue>) {\n  const { localization, icons } = table.tableInstance\n  const mode = getEffectiveMode(column, table)\n  if (VALUELESS_MODES.has(mode)) {\n    return <ValuelessLabel label={localization.filterModes[mode] ?? mode} />\n  }\n  const value = (column.getFilterValue() ?? \"\") as string\n  return (\n    <ClearableInput\n      value={value}\n      onChange={(next) => column.setFilterValue(next || undefined)}\n      placeholder={localization.filterPlaceholder(getColumnLabel(column))}\n      ariaLabel={localization.filterByColumn(getColumnLabel(column))}\n      clearLabel={localization.clearFilter}\n      ClearIcon={icons.clear}\n    />\n  )\n}\n"
    },
    {
      "path": "ui/data-table/components/menus/data-table-column-actions.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/menus/data-table-column-actions.tsx",
      "content": "\"use client\"\n\nimport type { Column, RowData } from \"@tanstack/react-table\"\n\nimport { Button } from \"@/components/ui/button\"\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuSeparator,\n  DropdownMenuTrigger,\n} from \"@/components/ui/dropdown-menu\"\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipTrigger,\n} from \"@/components/ui/tooltip\"\nimport { cn } from \"@/lib/utils\"\n\nimport type { DataTableInstance } from \"../../core/types\"\nimport { getColumnLabel } from \"../../helpers/column-label\"\n\ninterface DataTableColumnActionsProps<TData extends RowData, TValue> {\n  column: Column<TData, TValue>\n  table: DataTableInstance<TData>\n  className?: string\n}\n\n/**\n * The MRT-signature per-column menu: a vertical-dots ghost button after the\n * header label that opens sort / hide / filter actions. Items render only when\n * the column supports them, so an ID column with sorting/filtering off shows\n * just the hide controls.\n */\nexport function DataTableColumnActions<TData extends RowData, TValue>({\n  column,\n  table,\n  className,\n}: DataTableColumnActionsProps<TData, TValue>) {\n  const {\n    localization,\n    icons,\n    setShowColumnFilters,\n    columnFilterDisplayMode,\n    enableColumnPinning,\n    enableGrouping,\n    renderColumnActionsMenuItems,\n  } = table.tableInstance\n  const canSort = column.getCanSort()\n  const canHide = column.getCanHide()\n  const canFilter = column.getCanFilter()\n  const canPin = enableColumnPinning && column.getCanPin()\n  const canGroup = enableGrouping && column.getCanGroup()\n  const isGrouped = column.getIsGrouped()\n  const sorted = column.getIsSorted()\n  const pinned = column.getIsPinned()\n  const hasFilter = column.getFilterValue() != null\n\n  return (\n    <DropdownMenu>\n      <Tooltip>\n        <TooltipTrigger asChild>\n          <DropdownMenuTrigger asChild>\n            <Button\n              variant=\"ghost\"\n              size=\"icon\"\n              aria-label={localization.columnActions}\n              className={cn(\n                \"size-7 shrink-0 opacity-70 transition-opacity group-hover/th:opacity-100 focus-visible:opacity-100 data-[state=open]:opacity-100\",\n                className\n              )}\n            >\n              <icons.columnActions />\n            </Button>\n          </DropdownMenuTrigger>\n        </TooltipTrigger>\n        <TooltipContent>{localization.columnActions}</TooltipContent>\n      </Tooltip>\n      <DropdownMenuContent align=\"start\" className=\"w-48\">\n        {canSort && (\n          <>\n            <DropdownMenuItem\n              onClick={() => column.toggleSorting(false)}\n              disabled={sorted === \"asc\"}\n            >\n              <icons.sortAscending />\n              {localization.sortAscending}\n            </DropdownMenuItem>\n            <DropdownMenuItem\n              onClick={() => column.toggleSorting(true)}\n              disabled={sorted === \"desc\"}\n            >\n              <icons.sortDescending />\n              {localization.sortDescending}\n            </DropdownMenuItem>\n            <DropdownMenuItem\n              onClick={() => column.clearSorting()}\n              disabled={!sorted}\n            >\n              <icons.clearAll />\n              {localization.clearSort}\n            </DropdownMenuItem>\n          </>\n        )}\n\n        {canFilter && (\n          <>\n            {canSort && <DropdownMenuSeparator />}\n            {columnFilterDisplayMode === \"subheader\" && (\n              <DropdownMenuItem onClick={() => setShowColumnFilters(true)}>\n                <icons.filter />\n                {localization.filterByColumn(getColumnLabel(column))}\n              </DropdownMenuItem>\n            )}\n            <DropdownMenuItem\n              onClick={() => column.setFilterValue(undefined)}\n              disabled={!hasFilter}\n            >\n              <icons.filterOff />\n              {localization.clearFilter}\n            </DropdownMenuItem>\n          </>\n        )}\n\n        {canHide && (\n          <>\n            {(canSort || canFilter) && <DropdownMenuSeparator />}\n            <DropdownMenuItem onClick={() => column.toggleVisibility(false)}>\n              <icons.hide />\n              {localization.hideColumn}\n            </DropdownMenuItem>\n            <DropdownMenuItem\n              onClick={() => table.toggleAllColumnsVisible(true)}\n            >\n              <icons.clearAll />\n              {localization.showAllColumns}\n            </DropdownMenuItem>\n          </>\n        )}\n\n        {canGroup && (\n          <>\n            {(canSort || canFilter || canHide) && <DropdownMenuSeparator />}\n            <DropdownMenuItem onClick={() => column.toggleGrouping()}>\n              <icons.group />\n              {isGrouped\n                ? localization.ungroupByColumn(getColumnLabel(column))\n                : localization.groupByColumn(getColumnLabel(column))}\n            </DropdownMenuItem>\n          </>\n        )}\n\n        {canPin && (\n          <>\n            {(canSort || canFilter || canHide || canGroup) && (\n              <DropdownMenuSeparator />\n            )}\n            <DropdownMenuItem\n              onClick={() => column.pin(\"left\")}\n              disabled={pinned === \"left\"}\n            >\n              <icons.pin />\n              {localization.pinToLeft}\n            </DropdownMenuItem>\n            <DropdownMenuItem\n              onClick={() => column.pin(\"right\")}\n              disabled={pinned === \"right\"}\n            >\n              <icons.pin />\n              {localization.pinToRight}\n            </DropdownMenuItem>\n            <DropdownMenuItem\n              onClick={() => column.pin(false)}\n              disabled={!pinned}\n            >\n              <icons.unpin />\n              {localization.unpin}\n            </DropdownMenuItem>\n          </>\n        )}\n\n        {renderColumnActionsMenuItems && (\n          <>\n            {(canSort || canFilter || canHide || canGroup || canPin) && (\n              <DropdownMenuSeparator />\n            )}\n            {renderColumnActionsMenuItems({\n              column: column as Column<TData, unknown>,\n              table,\n            })}\n          </>\n        )}\n      </DropdownMenuContent>\n    </DropdownMenu>\n  )\n}\n"
    },
    {
      "path": "ui/data-table/components/menus/data-table-filter-mode-menu.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/menus/data-table-filter-mode-menu.tsx",
      "content": "\"use client\"\n\nimport type { Column, RowData } from \"@tanstack/react-table\"\n\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuGroup,\n  DropdownMenuLabel,\n  DropdownMenuRadioGroup,\n  DropdownMenuRadioItem,\n  DropdownMenuSeparator,\n  DropdownMenuTrigger,\n} from \"@/components/ui/dropdown-menu\"\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipTrigger,\n} from \"@/components/ui/tooltip\"\nimport { cn } from \"@/lib/utils\"\n\nimport type { DataTableInstance } from \"../../core/types\"\nimport { modeOptionsForVariant, type FilterMode } from \"../../fns/filter-fns\"\nimport { getEffectiveMode } from \"../../helpers/effective-filter-mode\"\n\n/**\n * The `Filter` adornment that opens a mode menu (contains/equals/…; numeric and\n * date variants get their own sets). Swapping the mode changes the column's\n * `filterFn` and resets the stale value. Hidden when the variant has a single\n * fixed mode or modes are disabled.\n */\nexport function DataTableFilterModeMenu<TData extends RowData, TValue>({\n  column,\n  table,\n}: {\n  column: Column<TData, TValue>\n  table: DataTableInstance<TData>\n}) {\n  const {\n    localization,\n    icons,\n    setColumnFilterMode,\n    enableColumnFilterModes,\n    renderColumnFilterModeMenuItems,\n  } = table.tableInstance\n  const variant = column.columnDef.meta?.variant ?? \"text\"\n  const perColumn = column.columnDef.meta?.enableColumnFilterModes\n  const enabled = perColumn ?? enableColumnFilterModes\n  // Restrict (and order) to the column's allowed subset when provided.\n  const allowed = column.columnDef.meta?.columnFilterModeOptions\n  const variantModes = modeOptionsForVariant(variant)\n  const modes = allowed\n    ? allowed.filter((mode) => variantModes.includes(mode))\n    : variantModes\n\n  if (!enabled || modes.length === 0) return null\n\n  const current = getEffectiveMode(column, table)\n\n  return (\n    <DropdownMenu>\n      <Tooltip>\n        <TooltipTrigger asChild>\n          <DropdownMenuTrigger asChild>\n            <button\n              type=\"button\"\n              aria-label={localization.changeFilterMode}\n              className={cn(\n                \"flex size-8 shrink-0 items-center justify-center rounded-sm text-muted-foreground transition-colors outline-none hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring/40 aria-expanded:text-foreground\"\n              )}\n            >\n              <icons.filter className=\"size-3.5\" />\n            </button>\n          </DropdownMenuTrigger>\n        </TooltipTrigger>\n        <TooltipContent>{localization.filterMode}</TooltipContent>\n      </Tooltip>\n      <DropdownMenuContent align=\"start\" className=\"w-52\">\n        {/* Base UI's GroupLabel requires a Group ancestor; Radix renders the\n            group as an inert wrapper. */}\n        <DropdownMenuGroup>\n          <DropdownMenuLabel>{localization.filterMode}</DropdownMenuLabel>\n        </DropdownMenuGroup>\n        <DropdownMenuSeparator />\n        {renderColumnFilterModeMenuItems ? (\n          renderColumnFilterModeMenuItems({\n            column: column as Column<TData, unknown>,\n            modes,\n            currentMode: current,\n            onSelect: (mode) => setColumnFilterMode(column.id, mode),\n            table,\n          })\n        ) : (\n          <DropdownMenuRadioGroup\n            value={current}\n            onValueChange={(value) =>\n              setColumnFilterMode(column.id, value as FilterMode)\n            }\n          >\n            {modes.map((mode) => (\n              <DropdownMenuRadioItem key={mode} value={mode}>\n                {localization.filterModes[mode] ?? mode}\n              </DropdownMenuRadioItem>\n            ))}\n          </DropdownMenuRadioGroup>\n        )}\n      </DropdownMenuContent>\n    </DropdownMenu>\n  )\n}\n"
    },
    {
      "path": "ui/data-table/components/menus/data-table-filter-panel.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/menus/data-table-filter-panel.tsx",
      "content": "\"use client\"\n\nimport type { Column, RowData } from \"@tanstack/react-table\"\nimport * as React from \"react\"\n\nimport { Button } from \"@/components/ui/button\"\nimport {\n  Dialog,\n  DialogContent,\n  DialogDescription,\n  DialogFooter,\n  DialogHeader,\n  DialogTitle,\n} from \"@/components/ui/dialog\"\nimport { Input } from \"@/components/ui/input\"\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from \"@/components/ui/select\"\n\nimport type {\n  AdvancedFilterGroup,\n  AdvancedFilterOperator,\n  AdvancedFilterRule,\n  DataTableInstance,\n  FilterVariant,\n} from \"../../core/types\"\nimport {\n  getOperatorsForVariant,\n  isValuelessOperator,\n} from \"../../fns/advanced-filter\"\nimport { getColumnLabel } from \"../../helpers/column-label\"\n\n// Monotonic ids for new rules. Panel is client-only, so a module counter is\n// stable enough (and avoids crypto/uuid deps).\nlet ruleSeq = 0\nconst nextRuleId = () => `cn-adv-rule-${++ruleSeq}`\n\nfunction columnVariant<TData extends RowData>(\n  column: Column<TData, unknown> | undefined\n): FilterVariant {\n  return column?.columnDef.meta?.variant ?? \"text\"\n}\n\nfunction valueInputType(variant: FilterVariant): \"text\" | \"number\" | \"date\" {\n  if (variant === \"range\" || variant === \"range-slider\") return \"number\"\n  if (variant === \"date\" || variant === \"date-range\") return \"date\"\n  return \"text\"\n}\n\ntype Localization<TData extends RowData> =\n  DataTableInstance<TData>[\"tableInstance\"][\"localization\"]\n\n/**\n * Modal dialog for building compound AND/OR filter rules. Edits accumulate in a\n * local draft and are only committed when \"Apply\" is clicked — closing or\n * cancelling discards them. The body is mounted only while open, so the draft\n * is freshly seeded from the active filter each time the dialog opens.\n */\nexport function DataTableFilterPanel<TData extends RowData>({\n  table,\n}: {\n  table: DataTableInstance<TData>\n}) {\n  const { showAdvancedFilterPanel, setShowAdvancedFilterPanel } =\n    table.tableInstance\n  return (\n    <Dialog\n      open={showAdvancedFilterPanel}\n      onOpenChange={setShowAdvancedFilterPanel}\n    >\n      {showAdvancedFilterPanel && <FilterPanelContent table={table} />}\n    </Dialog>\n  )\n}\n\nfunction FilterPanelContent<TData extends RowData>({\n  table,\n}: {\n  table: DataTableInstance<TData>\n}) {\n  const {\n    localization,\n    icons,\n    advancedFilter,\n    setAdvancedFilter,\n    setShowAdvancedFilterPanel,\n  } = table.tableInstance\n\n  // Seeded once on mount (the dialog body remounts each open).\n  const [draft, setDraft] = React.useState<AdvancedFilterGroup>(advancedFilter)\n\n  const { logic, rules } = draft\n  const columns = table.getAllLeafColumns().filter((c) => c.getCanFilter())\n\n  const updateRule = (id: string, patch: Partial<AdvancedFilterRule>) =>\n    setDraft((group) => ({\n      ...group,\n      rules: group.rules.map((r) => (r.id === id ? { ...r, ...patch } : r)),\n    }))\n\n  const addRule = () => {\n    const column = columns[0]\n    if (!column) return\n    const operator =\n      getOperatorsForVariant(columnVariant(column))[0] ?? \"contains\"\n    setDraft((group) => ({\n      ...group,\n      rules: [\n        ...group.rules,\n        { id: nextRuleId(), columnId: column.id, operator, value: undefined },\n      ],\n    }))\n  }\n\n  const removeRule = (id: string) =>\n    setDraft((group) => ({\n      ...group,\n      rules: group.rules.filter((r) => r.id !== id),\n    }))\n\n  const clearAll = () => setDraft((group) => ({ ...group, rules: [] }))\n\n  const apply = () => {\n    setAdvancedFilter(draft)\n    setShowAdvancedFilterPanel(false)\n  }\n\n  // Changing the column may invalidate the operator/value, so reconcile both.\n  const changeColumn = (rule: AdvancedFilterRule, columnId: string) => {\n    const operators = getOperatorsForVariant(\n      columnVariant(table.getColumn(columnId))\n    )\n    const operator = operators.includes(rule.operator)\n      ? rule.operator\n      : (operators[0] ?? \"contains\")\n    updateRule(rule.id, {\n      columnId,\n      operator,\n      value: undefined,\n      value2: undefined,\n    })\n  }\n\n  const changeOperator = (\n    rule: AdvancedFilterRule,\n    operator: AdvancedFilterOperator\n  ) =>\n    updateRule(\n      rule.id,\n      isValuelessOperator(operator)\n        ? { operator, value: undefined, value2: undefined }\n        : { operator }\n    )\n\n  return (\n    <DialogContent className=\"gap-3 sm:max-w-lg\">\n      <DialogHeader>\n        <DialogTitle>{localization.advancedFilters}</DialogTitle>\n        <DialogDescription className=\"sr-only\">\n          {localization.advancedFilters}\n        </DialogDescription>\n      </DialogHeader>\n\n      <div className=\"flex max-h-[50vh] flex-col gap-2 overflow-y-auto\">\n        <div className=\"flex flex-wrap items-center gap-2 text-sm\">\n          <span className=\"text-muted-foreground\">\n            {localization.advancedFiltersMatchLabel}\n          </span>\n          <Select\n            value={logic === \"or\" ? \"any\" : \"all\"}\n            onValueChange={(v) =>\n              setDraft((group) => ({\n                ...group,\n                logic: v === \"any\" ? \"or\" : \"and\",\n              }))\n            }\n          >\n            <SelectTrigger className=\"h-8 w-20\">\n              <SelectValue />\n            </SelectTrigger>\n            <SelectContent>\n              <SelectItem value=\"all\">\n                {localization.advancedFiltersMatchAll}\n              </SelectItem>\n              <SelectItem value=\"any\">\n                {localization.advancedFiltersMatchAny}\n              </SelectItem>\n            </SelectContent>\n          </Select>\n          <span className=\"text-muted-foreground\">\n            {localization.advancedFiltersOf}\n          </span>\n        </div>\n\n        {rules.length === 0 ? (\n          <p className=\"text-sm text-muted-foreground\">\n            {localization.advancedFiltersEmpty}\n          </p>\n        ) : (\n          <div className=\"flex flex-col gap-2\">\n            {rules.map((rule) => {\n              const column = table.getColumn(rule.columnId)\n              const variant = columnVariant(column)\n              const operators = getOperatorsForVariant(variant)\n              return (\n                <div\n                  key={rule.id}\n                  className=\"flex flex-col gap-2 rounded-md border p-2\"\n                >\n                  <div className=\"flex items-center gap-2\">\n                    <Select\n                      value={rule.columnId}\n                      // Base UI's Select can emit null (cleared value); Radix\n                      // never does. Guard so both flavors type-check.\n                      onValueChange={(v) => {\n                        if (v != null) changeColumn(rule, v)\n                      }}\n                    >\n                      <SelectTrigger className=\"h-8 flex-1\">\n                        <SelectValue\n                          placeholder={localization.advancedFiltersColumn}\n                        />\n                      </SelectTrigger>\n                      <SelectContent>\n                        {columns.map((c) => (\n                          <SelectItem key={c.id} value={c.id}>\n                            {getColumnLabel(c)}\n                          </SelectItem>\n                        ))}\n                      </SelectContent>\n                    </Select>\n                    <Button\n                      variant=\"ghost\"\n                      size=\"icon\"\n                      className=\"size-8 shrink-0 text-muted-foreground\"\n                      aria-label={localization.removeFilterRule}\n                      onClick={() => removeRule(rule.id)}\n                    >\n                      <icons.clear />\n                    </Button>\n                  </div>\n                  <div className=\"flex items-center gap-2\">\n                    <Select\n                      value={rule.operator}\n                      onValueChange={(v) =>\n                        changeOperator(rule, v as AdvancedFilterOperator)\n                      }\n                    >\n                      <SelectTrigger className=\"h-8 w-40 shrink-0\">\n                        <SelectValue />\n                      </SelectTrigger>\n                      <SelectContent>\n                        {operators.map((op) => (\n                          <SelectItem key={op} value={op}>\n                            {localization.advancedFilterOperators[op] ?? op}\n                          </SelectItem>\n                        ))}\n                      </SelectContent>\n                    </Select>\n                    <RuleValueInput\n                      rule={rule}\n                      variant={variant}\n                      column={column}\n                      localization={localization}\n                      onChange={(patch) => updateRule(rule.id, patch)}\n                    />\n                  </div>\n                </div>\n              )\n            })}\n          </div>\n        )}\n\n        <Button\n          variant=\"outline\"\n          size=\"sm\"\n          className=\"self-start\"\n          onClick={addRule}\n          disabled={columns.length === 0}\n        >\n          {localization.advancedFiltersAddRule}\n        </Button>\n      </div>\n\n      <DialogFooter className=\"sm:justify-between\">\n        <Button\n          variant=\"ghost\"\n          size=\"sm\"\n          onClick={clearAll}\n          disabled={rules.length === 0}\n        >\n          {localization.advancedFiltersClearAll}\n        </Button>\n        <div className=\"flex items-center gap-2\">\n          <Button\n            variant=\"outline\"\n            size=\"sm\"\n            onClick={() => setShowAdvancedFilterPanel(false)}\n          >\n            {localization.cancel}\n          </Button>\n          <Button size=\"sm\" onClick={apply}>\n            {localization.advancedFiltersApply}\n          </Button>\n        </div>\n      </DialogFooter>\n    </DialogContent>\n  )\n}\n\nfunction RuleValueInput<TData extends RowData>({\n  rule,\n  variant,\n  column,\n  localization,\n  onChange,\n}: {\n  rule: AdvancedFilterRule\n  variant: FilterVariant\n  column: Column<TData, unknown> | undefined\n  localization: Localization<TData>\n  onChange: (patch: Partial<AdvancedFilterRule>) => void\n}) {\n  if (isValuelessOperator(rule.operator)) return null\n\n  const options = column?.columnDef.meta?.options\n  if ((variant === \"select\" || variant === \"multi-select\") && options?.length) {\n    return (\n      <Select\n        value={(rule.value as string | undefined) ?? \"\"}\n        onValueChange={(v) => onChange({ value: v })}\n      >\n        <SelectTrigger className=\"h-8 flex-1\">\n          <SelectValue placeholder={localization.advancedFiltersValue} />\n        </SelectTrigger>\n        <SelectContent>\n          {options.map((o) => (\n            <SelectItem key={o.value} value={o.value}>\n              {o.label}\n            </SelectItem>\n          ))}\n        </SelectContent>\n      </Select>\n    )\n  }\n\n  if (variant === \"checkbox\") {\n    return (\n      <Select\n        value={rule.value === undefined ? \"\" : String(rule.value)}\n        onValueChange={(v) => onChange({ value: v === \"true\" })}\n      >\n        <SelectTrigger className=\"h-8 flex-1\">\n          <SelectValue placeholder={localization.advancedFiltersValue} />\n        </SelectTrigger>\n        <SelectContent>\n          <SelectItem value=\"true\">True</SelectItem>\n          <SelectItem value=\"false\">False</SelectItem>\n        </SelectContent>\n      </Select>\n    )\n  }\n\n  const type = valueInputType(variant)\n  if (rule.operator === \"between\") {\n    return (\n      <div className=\"flex flex-1 items-center gap-1\">\n        <Input\n          type={type}\n          className=\"h-8\"\n          value={(rule.value as string | undefined) ?? \"\"}\n          onChange={(e) => onChange({ value: e.target.value })}\n          placeholder={localization.min}\n        />\n        <span className=\"text-muted-foreground\">–</span>\n        <Input\n          type={type}\n          className=\"h-8\"\n          value={(rule.value2 as string | undefined) ?? \"\"}\n          onChange={(e) => onChange({ value2: e.target.value })}\n          placeholder={localization.max}\n        />\n      </div>\n    )\n  }\n\n  return (\n    <Input\n      type={type}\n      className=\"h-8 flex-1\"\n      value={(rule.value as string | undefined) ?? \"\"}\n      onChange={(e) => onChange({ value: e.target.value })}\n      placeholder={localization.advancedFiltersValue}\n    />\n  )\n}\n"
    },
    {
      "path": "ui/data-table/components/toolbar/controls/advanced-filter-toggle.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/toolbar/controls/advanced-filter-toggle.tsx",
      "content": "\"use client\"\n\nimport type { RowData } from \"@tanstack/react-table\"\n\nimport { Badge } from \"@/components/ui/badge\"\nimport { Button } from \"@/components/ui/button\"\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipTrigger,\n} from \"@/components/ui/tooltip\"\nimport { cn } from \"@/lib/utils\"\n\nimport type { DataTableInstance } from \"../../../core/types\"\n\n/** Toolbar button that opens the advanced filter panel; badges the active-rule\n *  count when any rules are set. */\nexport function DataTableAdvancedFilterToggle<TData extends RowData>({\n  table,\n}: {\n  table: DataTableInstance<TData>\n}) {\n  const {\n    localization,\n    icons,\n    advancedFilter,\n    showAdvancedFilterPanel,\n    setShowAdvancedFilterPanel,\n  } = table.tableInstance\n  const count = advancedFilter.rules.length\n\n  return (\n    <Tooltip>\n      <TooltipTrigger asChild>\n        <Button\n          variant=\"outline\"\n          size=\"icon\"\n          aria-label={localization.advancedFilters}\n          aria-pressed={showAdvancedFilterPanel}\n          onClick={() => setShowAdvancedFilterPanel((prev) => !prev)}\n          className={cn(\n            \"relative size-8\",\n            (showAdvancedFilterPanel || count > 0) && \"bg-muted text-foreground\"\n          )}\n        >\n          <icons.advancedFilter />\n          {count > 0 && (\n            <Badge className=\"absolute -top-1.5 -right-1.5 size-4 justify-center rounded-full p-0 text-[10px] tabular-nums\">\n              {count}\n            </Badge>\n          )}\n        </Button>\n      </TooltipTrigger>\n      <TooltipContent>{localization.advancedFilters}</TooltipContent>\n    </Tooltip>\n  )\n}\n"
    },
    {
      "path": "ui/data-table/components/toolbar/controls/density-toggle.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/toolbar/controls/density-toggle.tsx",
      "content": "\"use client\"\n\nimport type { RowData } from \"@tanstack/react-table\"\n\nimport { Button } from \"@/components/ui/button\"\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipTrigger,\n} from \"@/components/ui/tooltip\"\n\nimport { DENSITY_ORDER } from \"../../../core/constants\"\nimport type { DataTableInstance } from \"../../../core/types\"\n\nconst DENSITY_LABEL_KEYS = {\n  comfortable: \"densityComfortable\",\n  compact: \"densityCompact\",\n  spacious: \"densitySpacious\",\n} as const\n\n/** Single button that cycles comfortable → compact → spacious. */\nexport function DataTableDensityToggle<TData extends RowData>({\n  table,\n}: {\n  table: DataTableInstance<TData>\n}) {\n  const { localization, icons, density, setDensity } = table.tableInstance\n  const currentLabel = localization[DENSITY_LABEL_KEYS[density]]\n  const label = `${localization.toggleDensity} (${currentLabel})`\n  return (\n    <Tooltip>\n      <TooltipTrigger asChild>\n        <Button\n          variant=\"outline\"\n          size=\"icon\"\n          aria-label={label}\n          className=\"size-8\"\n          onClick={() =>\n            setDensity((prev) => {\n              const next =\n                DENSITY_ORDER[\n                  (DENSITY_ORDER.indexOf(prev) + 1) % DENSITY_ORDER.length\n                ]\n              return next ?? \"comfortable\"\n            })\n          }\n        >\n          <icons.density />\n        </Button>\n      </TooltipTrigger>\n      <TooltipContent>{label}</TooltipContent>\n    </Tooltip>\n  )\n}\n"
    },
    {
      "path": "ui/data-table/components/toolbar/controls/filter-toggle.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/toolbar/controls/filter-toggle.tsx",
      "content": "\"use client\"\n\nimport type { RowData } from \"@tanstack/react-table\"\n\nimport { Button } from \"@/components/ui/button\"\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipTrigger,\n} from \"@/components/ui/tooltip\"\nimport { cn } from \"@/lib/utils\"\n\nimport type { DataTableInstance } from \"../../../core/types\"\n\n/** Funnel toggle that shows/hides the filter row. */\nexport function DataTableFilterToggle<TData extends RowData>({\n  table,\n}: {\n  table: DataTableInstance<TData>\n}) {\n  const { localization, icons, showColumnFilters, setShowColumnFilters } =\n    table.tableInstance\n  const label = showColumnFilters\n    ? localization.hideColumnFilters\n    : localization.showColumnFilters\n  return (\n    <Tooltip>\n      <TooltipTrigger asChild>\n        <Button\n          variant=\"outline\"\n          size=\"icon\"\n          aria-label={label}\n          aria-pressed={showColumnFilters}\n          onClick={() => setShowColumnFilters((prev) => !prev)}\n          className={cn(\n            \"size-8\",\n            showColumnFilters && \"bg-muted text-foreground\"\n          )}\n        >\n          <icons.filter />\n        </Button>\n      </TooltipTrigger>\n      <TooltipContent>{label}</TooltipContent>\n    </Tooltip>\n  )\n}\n"
    },
    {
      "path": "ui/data-table/components/toolbar/controls/fullscreen-toggle.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/toolbar/controls/fullscreen-toggle.tsx",
      "content": "\"use client\"\n\nimport type { RowData } from \"@tanstack/react-table\"\n\nimport { Button } from \"@/components/ui/button\"\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipTrigger,\n} from \"@/components/ui/tooltip\"\n\nimport type { DataTableInstance } from \"../../../core/types\"\n\n/** Full-screen toggle (state-driven; the surface fixes itself to the viewport). */\nexport function DataTableFullscreenToggle<TData extends RowData>({\n  table,\n}: {\n  table: DataTableInstance<TData>\n}) {\n  const { localization, icons, isFullscreen, setIsFullscreen } =\n    table.tableInstance\n  const label = isFullscreen\n    ? localization.exitFullscreen\n    : localization.enterFullscreen\n  return (\n    <Tooltip>\n      <TooltipTrigger asChild>\n        <Button\n          variant=\"outline\"\n          size=\"icon\"\n          aria-label={label}\n          aria-pressed={isFullscreen}\n          onClick={() => setIsFullscreen((prev) => !prev)}\n          className=\"size-8\"\n        >\n          {isFullscreen ? <icons.fullscreenExit /> : <icons.fullscreenEnter />}\n        </Button>\n      </TooltipTrigger>\n      <TooltipContent>{label}</TooltipContent>\n    </Tooltip>\n  )\n}\n"
    },
    {
      "path": "ui/data-table/components/toolbar/controls/index.ts",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/toolbar/controls/index.ts",
      "content": "\"use client\"\n\n// Toolbar internal-action toggles (filter row, density, full screen).\n\nexport { DataTableFilterToggle } from \"./filter-toggle\"\nexport { DataTableAdvancedFilterToggle } from \"./advanced-filter-toggle\"\nexport { DataTableDensityToggle } from \"./density-toggle\"\nexport { DataTableFullscreenToggle } from \"./fullscreen-toggle\"\n"
    },
    {
      "path": "ui/data-table/components/toolbar/data-table-alert-banner.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/toolbar/data-table-alert-banner.tsx",
      "content": "\"use client\"\n\nimport type { RowData } from \"@tanstack/react-table\"\n\nimport { cn } from \"@/lib/utils\"\n\nimport type { DataTableInstance } from \"../../core/types\"\n\n/**\n * Selection alert banner shown between the toolbar and the table when any rows\n * are selected: a muted strip with the localized count and a Clear action.\n */\nexport function DataTableAlertBanner<TData extends RowData>({\n  table,\n}: {\n  table: DataTableInstance<TData>\n}) {\n  const { localization, enableRowSelection } = table.tableInstance\n  if (!enableRowSelection) return null\n\n  const selectedCount = table.getSelectedRowModel().rows.length\n  if (selectedCount === 0) return null\n\n  const totalCount = table.getPrePaginationRowModel().rows.length\n\n  return (\n    <div\n      data-slot=\"data-table-alert-banner\"\n      className={cn(\n        \"flex items-center justify-between gap-3 rounded-md border bg-muted px-3 py-2 text-xs font-medium text-muted-foreground\"\n      )}\n      role=\"status\"\n    >\n      <span>{localization.rowsSelected(selectedCount, totalCount)}</span>\n      <button\n        type=\"button\"\n        onClick={() => table.resetRowSelection()}\n        className=\"font-semibold tracking-wide text-foreground underline-offset-4 hover:underline\"\n      >\n        {localization.clearSelection}\n      </button>\n    </div>\n  )\n}\n"
    },
    {
      "path": "ui/data-table/components/toolbar/data-table-bottom-toolbar.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/toolbar/data-table-bottom-toolbar.tsx",
      "content": "\"use client\"\n\nimport type { RowData } from \"@tanstack/react-table\"\n\nimport { cn } from \"@/lib/utils\"\n\nimport type { DataTableInstance } from \"../../core/types\"\nimport { DataTablePagination } from \"./data-table-pagination\"\n\ninterface DataTableBottomToolbarProps<TData extends RowData> {\n  table: DataTableInstance<TData>\n  pageSizeOptions?: number[]\n}\n\n/**\n * The bottom toolbar region. Honors a `renderBottomToolbar` override; otherwise\n * lays out optional custom actions alongside the bottom pagination control,\n * rendering nothing when neither is present.\n */\nexport function DataTableBottomToolbar<TData extends RowData>({\n  table,\n  pageSizeOptions,\n}: DataTableBottomToolbarProps<TData>) {\n  const {\n    enableBottomToolbar,\n    enablePagination,\n    positionPagination,\n    renderBottomToolbar,\n    renderBottomToolbarCustomActions,\n    refs,\n  } = table.tableInstance\n\n  if (renderBottomToolbar) return <>{renderBottomToolbar({ table })}</>\n  if (!enableBottomToolbar) return null\n\n  const customActions = renderBottomToolbarCustomActions?.({ table })\n  const showBottomPagination =\n    enablePagination &&\n    (positionPagination === \"bottom\" || positionPagination === \"both\")\n  const pagination = showBottomPagination ? (\n    <DataTablePagination table={table} pageSizeOptions={pageSizeOptions} />\n  ) : null\n\n  if (customActions == null && pagination == null) return null\n\n  return (\n    <div\n      // Forwarding the exposed DOM ref object as a JSX ref (not reading\n      // .current during render).\n      // eslint-disable-next-line react-hooks/refs\n      ref={refs.bottomToolbarRef}\n      data-slot=\"data-table-bottom-toolbar\"\n      className={cn(\n        customActions != null &&\n          \"flex flex-wrap items-center justify-between gap-4\"\n      )}\n    >\n      {customActions != null ? (\n        <>\n          <div className=\"flex items-center gap-2\">{customActions}</div>\n          {pagination && <div className=\"flex-1\">{pagination}</div>}\n        </>\n      ) : (\n        pagination\n      )}\n    </div>\n  )\n}\n"
    },
    {
      "path": "ui/data-table/components/toolbar/data-table-global-filter.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/toolbar/data-table-global-filter.tsx",
      "content": "\"use client\"\n\nimport type { RowData } from \"@tanstack/react-table\"\nimport * as React from \"react\"\n\nimport { Button } from \"@/components/ui/button\"\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuGroup,\n  DropdownMenuLabel,\n  DropdownMenuRadioGroup,\n  DropdownMenuRadioItem,\n  DropdownMenuSeparator,\n  DropdownMenuTrigger,\n} from \"@/components/ui/dropdown-menu\"\nimport { Input } from \"@/components/ui/input\"\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipTrigger,\n} from \"@/components/ui/tooltip\"\nimport { cn } from \"@/lib/utils\"\n\nimport type { DataTableInstance } from \"../../core/types\"\nimport type { GlobalFilterMode } from \"../../fns/filter-fns\"\n\nconst GLOBAL_MODES: GlobalFilterMode[] = [\n  \"fuzzy\",\n  \"contains\",\n  \"startsWith\",\n  \"endsWith\",\n  \"equals\",\n]\n\n/**\n * Expandable global search (MRT order: first in the toolbar icon cluster). The\n * search button expands into an input with a leading icon, an optional\n * search-mode menu, and a clear affordance. Input is debounced in manual\n * (server) mode so each keystroke doesn't fire a request.\n */\nexport function DataTableGlobalFilter<TData extends RowData>({\n  table,\n  searchInputRef,\n}: {\n  table: DataTableInstance<TData>\n  /** Optional ref forwarded to the search input. Defaults to the instance's\n   *  `searchInputRef` when rendered by `DataTable`. */\n  searchInputRef?: React.RefObject<HTMLInputElement | null>\n}) {\n  const {\n    localization,\n    icons,\n    enableGlobalFilter,\n    enableGlobalFilterModes,\n    globalFilterMode,\n    setGlobalFilterMode,\n    renderGlobalFilterModeMenuItems,\n  } = table.tableInstance\n\n  const external = (table.getState().globalFilter ?? \"\") as string\n  const [expanded, setExpanded] = React.useState(external.length > 0)\n  const [value, setValue] = React.useState(external)\n  // Drive focus-on-expand and let consumers focus the box. Use the forwarded\n  // ref when provided (so `DataTable` shares its `searchInputRef`), else a\n  // local one. React assigns it directly — no manual `.current` mutation.\n  const localRef = React.useRef<HTMLInputElement>(null)\n  const inputRef = searchInputRef ?? localRef\n\n  const debounceMs = table.options.manualFiltering ? 300 : 0\n\n  React.useEffect(() => {\n    if (value === (table.getState().globalFilter ?? \"\")) return\n    const id = setTimeout(\n      () => table.setGlobalFilter(value || undefined),\n      debounceMs\n    )\n    return () => clearTimeout(id)\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [value, debounceMs])\n\n  if (!enableGlobalFilter) return null\n\n  if (!expanded) {\n    return (\n      <Tooltip>\n        <TooltipTrigger asChild>\n          <Button\n            variant=\"outline\"\n            size=\"icon\"\n            aria-label={localization.search}\n            className=\"size-8\"\n            onClick={() => {\n              setExpanded(true)\n              requestAnimationFrame(() => inputRef.current?.focus())\n            }}\n          >\n            <icons.search />\n          </Button>\n        </TooltipTrigger>\n        <TooltipContent>{localization.search}</TooltipContent>\n      </Tooltip>\n    )\n  }\n\n  const clear = () => {\n    setValue(\"\")\n    table.setGlobalFilter(undefined)\n    setExpanded(false)\n  }\n\n  return (\n    <div className=\"flex h-9 items-center gap-0.5 rounded-md border bg-background pr-1 pl-2 focus-within:border-ring\">\n      <icons.search className=\"size-3.5 shrink-0 text-muted-foreground\" />\n      <Input\n        ref={inputRef}\n        value={value}\n        onChange={(e) => setValue(e.target.value)}\n        onBlur={() => {\n          if (!value) setExpanded(false)\n        }}\n        placeholder={localization.searchPlaceholder}\n        aria-label={localization.search}\n        className={cn(\n          \"h-7 w-40 border-0 px-1 text-xs font-normal tracking-normal normal-case shadow-none focus-visible:ring-0 sm:w-56\"\n        )}\n      />\n      {value && (\n        <Button\n          variant=\"ghost\"\n          size=\"icon\"\n          aria-label={localization.clearSearch}\n          onClick={clear}\n          className=\"size-7\"\n        >\n          <icons.clear />\n        </Button>\n      )}\n      {enableGlobalFilterModes && (\n        <DropdownMenu>\n          <Tooltip>\n            <TooltipTrigger asChild>\n              <DropdownMenuTrigger asChild>\n                <Button\n                  variant=\"ghost\"\n                  size=\"icon\"\n                  aria-label={localization.globalFilterMode}\n                  className=\"size-7\"\n                >\n                  <icons.search className=\"size-3\" />\n                </Button>\n              </DropdownMenuTrigger>\n            </TooltipTrigger>\n            <TooltipContent>{localization.globalFilterMode}</TooltipContent>\n          </Tooltip>\n          <DropdownMenuContent align=\"end\" className=\"w-48\">\n            {/* Base UI's GroupLabel requires a Group ancestor; Radix renders\n                the group as an inert wrapper. */}\n            <DropdownMenuGroup>\n              <DropdownMenuLabel>\n                {localization.globalFilterMode}\n              </DropdownMenuLabel>\n            </DropdownMenuGroup>\n            <DropdownMenuSeparator />\n            {renderGlobalFilterModeMenuItems ? (\n              renderGlobalFilterModeMenuItems({\n                modes: GLOBAL_MODES,\n                currentMode: globalFilterMode,\n                onSelect: setGlobalFilterMode,\n                table,\n              })\n            ) : (\n              <DropdownMenuRadioGroup\n                value={globalFilterMode}\n                onValueChange={(mode) =>\n                  setGlobalFilterMode(mode as GlobalFilterMode)\n                }\n              >\n                {GLOBAL_MODES.map((mode) => (\n                  <DropdownMenuRadioItem key={mode} value={mode}>\n                    {localization.filterModes[mode] ?? mode}\n                  </DropdownMenuRadioItem>\n                ))}\n              </DropdownMenuRadioGroup>\n            )}\n          </DropdownMenuContent>\n        </DropdownMenu>\n      )}\n    </div>\n  )\n}\n"
    },
    {
      "path": "ui/data-table/components/toolbar/data-table-grouping.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/toolbar/data-table-grouping.tsx",
      "content": "\"use client\"\n\nimport { useDroppable } from \"@dnd-kit/core\"\nimport type { RowData } from \"@tanstack/react-table\"\n\nimport { Badge } from \"@/components/ui/badge\"\nimport { cn } from \"@/lib/utils\"\n\nimport type { DataTableInstance } from \"../../core/types\"\nimport { getColumnLabel } from \"../../helpers/column-label\"\n\nexport const GROUP_DROPZONE_ID = \"cn-group-dropzone\"\n\n/**\n * The MRT \"drop to group by\" zone: a dashed strip below the toolbar showing\n * chips for the active grouping columns (each removable) and acting as a drop\n * target for column drags (handled by the column DnD context in `<DataTable>`).\n */\nexport function DataTableDropToGroupZone<TData extends RowData>({\n  table,\n}: {\n  table: DataTableInstance<TData>\n}) {\n  const { localization, icons } = table.tableInstance\n  const grouping = table.getState().grouping\n  const { setNodeRef, isOver } = useDroppable({ id: GROUP_DROPZONE_ID })\n\n  return (\n    <div\n      ref={setNodeRef}\n      className={cn(\n        \"flex min-h-10 flex-wrap items-center gap-2 rounded-md border border-dashed px-3 py-2 text-xs text-muted-foreground transition-colors\",\n        isOver && \"border-primary bg-muted text-foreground\"\n      )}\n    >\n      <icons.group className=\"size-3.5 shrink-0\" />\n      {grouping.length === 0 ? (\n        <span>{localization.dropToGroupBy}</span>\n      ) : (\n        grouping.map((columnId) => {\n          const column = table.getColumn(columnId)\n          if (!column) return null\n          return (\n            <Badge\n              key={columnId}\n              variant=\"secondary\"\n              className=\"gap-1 rounded-sm pr-1 normal-case\"\n            >\n              {getColumnLabel(column)}\n              <button\n                type=\"button\"\n                aria-label={localization.ungroupByColumn(\n                  getColumnLabel(column)\n                )}\n                onClick={() => column.toggleGrouping()}\n                className=\"rounded-sm text-muted-foreground transition-colors hover:text-foreground\"\n              >\n                <icons.clear className=\"size-3\" />\n              </button>\n            </Badge>\n          )\n        })\n      )}\n    </div>\n  )\n}\n"
    },
    {
      "path": "ui/data-table/components/toolbar/data-table-pagination.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/toolbar/data-table-pagination.tsx",
      "content": "\"use client\"\n\nimport type { RowData } from \"@tanstack/react-table\"\nimport type * as React from \"react\"\n\nimport { Button } from \"@/components/ui/button\"\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from \"@/components/ui/select\"\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipTrigger,\n} from \"@/components/ui/tooltip\"\n\nimport type { DataTableInstance } from \"../../core/types\"\n\ninterface DataTablePaginationProps<TData extends RowData> {\n  table: DataTableInstance<TData>\n  pageSizeOptions?: number[]\n}\n\n/**\n * Bottom toolbar. Left: rows-per-page select. Right: MRT-style \"start–end of\n * total\" range label + first/prev/next/last icon buttons. Works in client and\n * manual/server pagination (counts come from the table instance).\n */\nexport function DataTablePagination<TData extends RowData>({\n  table,\n  pageSizeOptions = [5, 10, 25, 50, 100],\n}: DataTablePaginationProps<TData>) {\n  const { localization, icons, paginationDisplayMode } = table.tableInstance\n  if (paginationDisplayMode === \"custom\") return null\n\n  const { pageIndex, pageSize } = table.getState().pagination\n  const totalRows = table.getRowCount()\n  const start = totalRows === 0 ? 0 : pageIndex * pageSize + 1\n  const end = Math.min((pageIndex + 1) * pageSize, totalRows)\n\n  return (\n    <div\n      data-slot=\"data-table-pagination\"\n      className=\"flex flex-wrap items-center justify-between gap-4 py-1\"\n    >\n      <div className=\"flex items-center gap-2\">\n        <span className=\"text-xs font-medium tracking-wide text-muted-foreground\">\n          {localization.rowsPerPage}\n        </span>\n        <Select\n          value={`${pageSize}`}\n          onValueChange={(value) => table.setPageSize(Number(value))}\n        >\n          <SelectTrigger\n            size=\"sm\"\n            className=\"h-8 w-18\"\n            aria-label={localization.rowsPerPage}\n          >\n            <SelectValue placeholder={`${pageSize}`} />\n          </SelectTrigger>\n          <SelectContent>\n            {pageSizeOptions.map((size) => (\n              <SelectItem key={size} value={`${size}`}>\n                {size}\n              </SelectItem>\n            ))}\n          </SelectContent>\n        </Select>\n      </div>\n\n      {paginationDisplayMode === \"pages\" ? (\n        <div className=\"flex items-center gap-1\">\n          <PaginationButton\n            label={localization.goToPreviousPage}\n            onClick={() => table.previousPage()}\n            disabled={!table.getCanPreviousPage()}\n          >\n            <icons.pagePrev />\n          </PaginationButton>\n          {getPageList(pageIndex + 1, table.getPageCount()).map((item, i) =>\n            item === \"ellipsis\" ? (\n              <span\n                key={`ellipsis-${i}`}\n                className=\"px-1 text-xs text-muted-foreground\"\n                aria-hidden\n              >\n                …\n              </span>\n            ) : (\n              <Button\n                key={item}\n                variant={item === pageIndex + 1 ? \"default\" : \"outline\"}\n                size=\"icon\"\n                aria-label={localization.goToPage(item)}\n                aria-current={item === pageIndex + 1 ? \"page\" : undefined}\n                onClick={() => table.setPageIndex(item - 1)}\n                className=\"size-8 text-xs tabular-nums\"\n              >\n                {item}\n              </Button>\n            )\n          )}\n          <PaginationButton\n            label={localization.goToNextPage}\n            onClick={() => table.nextPage()}\n            disabled={!table.getCanNextPage()}\n          >\n            <icons.pageNext />\n          </PaginationButton>\n        </div>\n      ) : (\n        <div className=\"flex items-center gap-3\">\n          <span className=\"text-xs font-medium tracking-wide text-muted-foreground tabular-nums\">\n            {localization.paginationRange(start, end, totalRows)}\n          </span>\n          <div className=\"flex items-center gap-1\">\n            <PaginationButton\n              label={localization.goToFirstPage}\n              onClick={() => table.setPageIndex(0)}\n              disabled={!table.getCanPreviousPage()}\n            >\n              <icons.pageFirst />\n            </PaginationButton>\n            <PaginationButton\n              label={localization.goToPreviousPage}\n              onClick={() => table.previousPage()}\n              disabled={!table.getCanPreviousPage()}\n            >\n              <icons.pagePrev />\n            </PaginationButton>\n            <PaginationButton\n              label={localization.goToNextPage}\n              onClick={() => table.nextPage()}\n              disabled={!table.getCanNextPage()}\n            >\n              <icons.pageNext />\n            </PaginationButton>\n            <PaginationButton\n              label={localization.goToLastPage}\n              onClick={() => table.setPageIndex(table.getPageCount() - 1)}\n              disabled={!table.getCanNextPage()}\n            >\n              <icons.pageLast />\n            </PaginationButton>\n          </div>\n        </div>\n      )}\n    </div>\n  )\n}\n\n/**\n * Windowed page list for \"pages\" mode: always shows the first and last page and\n * the current page ± 1, collapsing the gaps to an `\"ellipsis\"` marker. Lists\n * every page when there are 7 or fewer. Page numbers are 1-based.\n */\nfunction getPageList(current: number, total: number): (number | \"ellipsis\")[] {\n  if (total <= 1) return total === 1 ? [1] : []\n  if (total <= 7) return Array.from({ length: total }, (_, i) => i + 1)\n  const wanted = [1, total, current, current - 1, current + 1].filter(\n    (p) => p >= 1 && p <= total\n  )\n  const sorted = [...new Set(wanted)].sort((a, b) => a - b)\n  const result: (number | \"ellipsis\")[] = []\n  let prev = 0\n  for (const page of sorted) {\n    if (page - prev > 1) result.push(\"ellipsis\")\n    result.push(page)\n    prev = page\n  }\n  return result\n}\n\nfunction PaginationButton({\n  label,\n  onClick,\n  disabled,\n  children,\n}: {\n  label: string\n  onClick: () => void\n  disabled: boolean\n  children: React.ReactNode\n}) {\n  return (\n    <Tooltip>\n      <TooltipTrigger asChild>\n        <Button\n          variant=\"outline\"\n          size=\"icon\"\n          aria-label={label}\n          onClick={onClick}\n          disabled={disabled}\n          className=\"size-8\"\n        >\n          {children}\n        </Button>\n      </TooltipTrigger>\n      <TooltipContent>{label}</TooltipContent>\n    </Tooltip>\n  )\n}\n"
    },
    {
      "path": "ui/data-table/components/toolbar/data-table-toolbar.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/toolbar/data-table-toolbar.tsx",
      "content": "\"use client\"\n\nimport type { RowData } from \"@tanstack/react-table\"\nimport type * as React from \"react\"\n\nimport type { DataTableInstance } from \"../../core/types\"\nimport { DataTableFilterPanel } from \"../menus/data-table-filter-panel\"\nimport {\n  DataTableAdvancedFilterToggle,\n  DataTableDensityToggle,\n  DataTableFilterToggle,\n  DataTableFullscreenToggle,\n} from \"./controls\"\nimport { DataTableGlobalFilter } from \"./data-table-global-filter\"\nimport { DataTableViewOptions } from \"./data-table-view-options\"\n\n/**\n * Top toolbar. Left region: title slot + consumer toolbar actions. Right\n * region: the MRT-ordered icon cluster (global search → filters funnel →\n * column visibility → density → full screen).\n */\nexport function DataTableToolbar<TData extends RowData>({\n  table,\n  toolbarRef,\n  searchInputRef,\n}: {\n  table: DataTableInstance<TData>\n  /** Optional ref forwarded to the toolbar root. Defaults to the instance's\n   *  `topToolbarRef` when rendered by `DataTable`. */\n  toolbarRef?: React.Ref<HTMLDivElement>\n  /** Optional ref forwarded to the global-search input. Defaults to the\n   *  instance's `searchInputRef` when rendered by `DataTable`. */\n  searchInputRef?: React.RefObject<HTMLInputElement | null>\n}) {\n  const {\n    title,\n    renderToolbarActions,\n    renderToolbarInternalActions,\n    enableToolbarInternalActions,\n    enableGlobalFilter,\n    positionGlobalFilter,\n    enableColumnFilters,\n    columnFilterDisplayMode,\n    enableAdvancedFilter,\n    enableColumnActions,\n    enableDensityToggle,\n    enableFullscreenToggle,\n  } = table.tableInstance\n\n  const anyFilterable = table\n    .getAllColumns()\n    .some((column) => column.getCanFilter())\n\n  const showGlobalFilter = enableGlobalFilter && positionGlobalFilter !== \"none\"\n\n  return (\n    <>\n      <div\n        ref={toolbarRef}\n        data-slot=\"data-table-toolbar\"\n        className=\"flex items-start justify-between gap-3 py-1\"\n      >\n        <div className=\"flex min-h-9 flex-1 flex-wrap items-center gap-2\">\n          {showGlobalFilter && positionGlobalFilter === \"left\" && (\n            <DataTableGlobalFilter\n              table={table}\n              searchInputRef={searchInputRef}\n            />\n          )}\n          {title != null && (\n            <div className=\"text-sm font-semibold tracking-wide\">{title}</div>\n          )}\n          {renderToolbarActions?.({ table })}\n        </div>\n\n        {enableToolbarInternalActions && (\n          <div\n            data-slot=\"data-table-toolbar-actions\"\n            className=\"flex shrink-0 items-center gap-1.5\"\n          >\n            {renderToolbarInternalActions ? (\n              renderToolbarInternalActions({ table })\n            ) : (\n              <>\n                {showGlobalFilter && positionGlobalFilter === \"right\" && (\n                  <DataTableGlobalFilter\n                    table={table}\n                    searchInputRef={searchInputRef}\n                  />\n                )}\n                {enableColumnFilters &&\n                  anyFilterable &&\n                  columnFilterDisplayMode === \"subheader\" && (\n                    <DataTableFilterToggle table={table} />\n                  )}\n                {enableAdvancedFilter && (\n                  <DataTableAdvancedFilterToggle table={table} />\n                )}\n                {enableColumnActions && <DataTableViewOptions table={table} />}\n                {enableDensityToggle && (\n                  <DataTableDensityToggle table={table} />\n                )}\n                {enableFullscreenToggle && (\n                  <DataTableFullscreenToggle table={table} />\n                )}\n              </>\n            )}\n          </div>\n        )}\n      </div>\n      {enableAdvancedFilter && <DataTableFilterPanel table={table} />}\n    </>\n  )\n}\n"
    },
    {
      "path": "ui/data-table/components/toolbar/data-table-view-options.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/components/toolbar/data-table-view-options.tsx",
      "content": "\"use client\"\n\nimport type { RowData } from \"@tanstack/react-table\"\n\nimport { Button } from \"@/components/ui/button\"\nimport {\n  DropdownMenu,\n  DropdownMenuCheckboxItem,\n  DropdownMenuContent,\n  DropdownMenuGroup,\n  DropdownMenuLabel,\n  DropdownMenuSeparator,\n  DropdownMenuTrigger,\n} from \"@/components/ui/dropdown-menu\"\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipTrigger,\n} from \"@/components/ui/tooltip\"\n\nimport type { DataTableInstance } from \"../../core/types\"\nimport { getColumnLabel } from \"../../helpers/column-label\"\n\n/**\n * Column visibility menu (toolbar icon cluster). Lists every hideable column\n * as a checkbox item; the header `label`/string is used for the menu text.\n */\nexport function DataTableViewOptions<TData extends RowData>({\n  table,\n}: {\n  table: DataTableInstance<TData>\n}) {\n  const { localization, icons } = table.tableInstance\n  const hideableColumns = table\n    .getAllColumns()\n    .filter((column) => column.getCanHide())\n\n  if (hideableColumns.length === 0) return null\n\n  return (\n    <DropdownMenu>\n      <Tooltip>\n        <TooltipTrigger asChild>\n          <DropdownMenuTrigger asChild>\n            <Button\n              variant=\"outline\"\n              size=\"icon\"\n              aria-label={localization.columnVisibility}\n              className=\"size-8\"\n            >\n              <icons.columnVisibility />\n            </Button>\n          </DropdownMenuTrigger>\n        </TooltipTrigger>\n        <TooltipContent>{localization.columnVisibility}</TooltipContent>\n      </Tooltip>\n      <DropdownMenuContent align=\"end\" className=\"w-52\">\n        {/* Base UI's GroupLabel requires a Group ancestor; Radix renders the\n            group as an inert wrapper. */}\n        <DropdownMenuGroup>\n          <DropdownMenuLabel>{localization.columnVisibility}</DropdownMenuLabel>\n        </DropdownMenuGroup>\n        <DropdownMenuSeparator />\n        {hideableColumns.map((column) => (\n          <DropdownMenuCheckboxItem\n            key={column.id}\n            className=\"capitalize\"\n            checked={column.getIsVisible()}\n            onCheckedChange={(value) => column.toggleVisibility(!!value)}\n            onSelect={(e) => e.preventDefault()}\n          >\n            {getColumnLabel(column)}\n          </DropdownMenuCheckboxItem>\n        ))}\n      </DropdownMenuContent>\n    </DropdownMenu>\n  )\n}\n"
    },
    {
      "path": "ui/data-table/core/config-context.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/core/config-context.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\n\nimport type { DataTableIcons } from \"./icons\"\nimport type { DataTableLocalization } from \"./localization\"\n\n/**\n * App-wide defaults for every table below it. `useDataTable` merges these\n * between the built-in defaults and per-call options:\n *   defaults  <  provider  <  useDataTable({ icons, localization })\n * Handy for setting one icon library or locale across a whole app without\n * passing it to each table.\n */\nexport interface DataTableConfigContextValue {\n  icons?: Partial<DataTableIcons>\n  localization?: Partial<DataTableLocalization>\n}\n\nconst DataTableConfigContext = React.createContext<DataTableConfigContextValue>(\n  {}\n)\n\nexport function DataTableConfigProvider({\n  icons,\n  localization,\n  children,\n}: DataTableConfigContextValue & { children: React.ReactNode }) {\n  const value = React.useMemo(\n    () => ({ icons, localization }),\n    [icons, localization]\n  )\n  return (\n    <DataTableConfigContext.Provider value={value}>\n      {children}\n    </DataTableConfigContext.Provider>\n  )\n}\n\nexport function useDataTableConfigContext(): DataTableConfigContextValue {\n  return React.useContext(DataTableConfigContext)\n}\n"
    },
    {
      "path": "ui/data-table/core/constants.ts",
      "type": "registry:ui",
      "target": "components/ui/data-table/core/constants.ts",
      "content": "import {\n  EXPAND_COLUMN_ID,\n  ROW_DRAG_COLUMN_ID,\n  ROW_NUMBER_COLUMN_ID,\n} from \"../injected-columns/injected-columns\"\nimport { ROW_ACTIONS_COLUMN_ID } from \"../injected-columns/data-table-row-actions\"\nimport { SELECTION_COLUMN_ID } from \"../injected-columns/selection-column\"\nimport type { Density } from \"./types\"\n\nexport const DENSITY_ORDER: Density[] = [\"comfortable\", \"compact\", \"spacious\"]\n\n/** Vertical padding utility per density level, applied to header + body cells. */\nexport const DENSITY_CELL_PADDING: Record<Density, string> = {\n  compact: \"py-1\",\n  comfortable: \"py-2.5\",\n  spacious: \"py-4\",\n}\n\n/** Horizontal alignment → text-align utility, applied to body cells. */\nexport const ALIGN_CELL = {\n  left: \"text-left\",\n  center: \"text-center\",\n  right: \"text-right\",\n} as const\n\n/** Injected columns that should never be draggable in the header. */\nexport const DISPLAY_COLUMN_IDS = new Set([\n  SELECTION_COLUMN_ID,\n  ROW_NUMBER_COLUMN_ID,\n  ROW_DRAG_COLUMN_ID,\n])\n\n// All injected (non-user) columns, used to find the first real data column so\n// tree (sub-row) rows can be indented by depth there.\nexport const NON_DATA_COLUMN_IDS = new Set([\n  SELECTION_COLUMN_ID,\n  ROW_NUMBER_COLUMN_ID,\n  ROW_DRAG_COLUMN_ID,\n  EXPAND_COLUMN_ID,\n  ROW_ACTIONS_COLUMN_ID,\n])\n\n/**\n * Selected-row styling shared by the virtualized row and the DnD/normal row\n * so both paths stay in sync: a primary-tinted background (hover variants\n * keep it stable under the base row hover) plus the 2px inset accent bar.\n * `group` lets cells clear their opaque background via\n * `group-data-[state=selected]` so the tint shows through.\n */\nexport const SELECTED_ROW_CLASS =\n  \"group data-[state=selected]:bg-primary/20 data-[state=selected]:hover:bg-primary/20 data-[state=selected]:shadow-[inset_2px_0_0_0_var(--primary)]\"\n"
    },
    {
      "path": "ui/data-table/core/data-table.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/core/data-table.tsx",
      "content": "\"use client\"\n\nimport { DndContext } from \"@dnd-kit/core\"\nimport { type RowData } from \"@tanstack/react-table\"\nimport * as React from \"react\"\n\nimport { Table, TableCaption } from \"@/components/ui/table\"\nimport { TooltipProvider } from \"@/components/ui/tooltip\"\nimport { cn } from \"@/lib/utils\"\n\nimport { DataTableBody } from \"../components/body/data-table-body\"\nimport {\n  DataTableFooter,\n  hasFooter,\n} from \"../components/body/data-table-footer\"\nimport { DataTableEditModal } from \"../components/editing/data-table-edit-modal\"\nimport { DataTableHeader } from \"../components/head/data-table-header\"\nimport { DataTableAlertBanner } from \"../components/toolbar/data-table-alert-banner\"\nimport { DataTableBottomToolbar } from \"../components/toolbar/data-table-bottom-toolbar\"\nimport { DataTableDropToGroupZone } from \"../components/toolbar/data-table-grouping\"\nimport { DataTablePagination } from \"../components/toolbar/data-table-pagination\"\nimport { DataTableToolbar } from \"../components/toolbar/data-table-toolbar\"\nimport { useGridNavigation } from \"../hooks/use-grid-navigation\"\nimport { useInfiniteScroll } from \"../hooks/use-infinite-scroll\"\nimport { useTableDnd } from \"../hooks/use-table-dnd\"\nimport { useTableVirtualizers } from \"../hooks/use-table-virtualizers\"\nimport { getColumnSizeVars } from \"../utils/column-styles\"\nimport { type DataTableInstance } from \"./types\"\n\n/**\n * Body wrapper that freezes during an active column resize. With\n * `columnResizeMode: \"onChange\"` the table re-renders on every mousemove;\n * skipping the body's re-render keeps drags smooth, since column widths come\n * from the CSS size vars on `<table>` and update without React. Outside of a\n * resize the comparator returns false, so normal re-rendering is unchanged.\n */\nconst MemoizedDataTableBody = React.memo(\n  DataTableBody,\n  (_prev, next) =>\n    next.table.getState().columnSizingInfo.isResizingColumn !== false\n) as typeof DataTableBody\n\ninterface DataTableProps<\n  TData extends RowData,\n> extends React.ComponentProps<\"div\"> {\n  table: DataTableInstance<TData>\n  /** Page-size options for the bottom pagination control. */\n  pageSizeOptions?: number[]\n  /** Extra classes for the scrollable table surface (e.g. a max-height that\n   *  overrides the default sticky-header/virtualization bound). */\n  surfaceClassName?: string\n}\n\n/**\n * Renders a data table from an instance produced by {@link useDataTable}.\n * Vertical stack: top toolbar → alert banner → drop-to-group zone → bordered\n * surface (sticky header, optional filter row, body, optional sticky footer) →\n * pagination. Wires DnD column/row ordering, pinning, resizing, grouping,\n * expansion, and detail panels.\n */\nexport function DataTable<TData extends RowData>({\n  table,\n  pageSizeOptions,\n  surfaceClassName,\n  className,\n  ...props\n}: DataTableProps<TData>) {\n  const {\n    density,\n    isFullscreen,\n    showProgressBars,\n    showLoadingOverlay,\n    enableColumnResizing,\n    enableGrouping,\n    enableRowVirtualization,\n    enableStickyHeader,\n    enablePagination,\n    enableInfiniteScroll,\n    onLoadMore,\n    hasNextPage,\n    isFetchingNextPage,\n    infiniteScrollThreshold,\n    positionPagination,\n    positionToolbarAlertBanner,\n    positionToolbarDropZone,\n    enableTopToolbar,\n    enableKeyboardNavigation,\n    localization,\n    renderCaption,\n    renderTopToolbar,\n    refs,\n  } = table.tableInstance\n\n  const { ref: gridRef, onKeyDown } = useGridNavigation<HTMLDivElement>(\n    enableKeyboardNavigation\n  )\n  const { sensors, collisionDetection, handleDragEnd } = useTableDnd(table)\n  const { rowVirtualizer, virtualItems, virtualColumns, withColumnSpacers } =\n    useTableVirtualizers(table, gridRef)\n\n  // Infinite scroll: observe a bottom sentinel against the scroll surface and\n  // ask the consumer to append the next chunk when it nears the viewport.\n  const sentinelRef = React.useRef<HTMLDivElement>(null)\n  useInfiniteScroll({\n    enabled: enableInfiniteScroll,\n    hasNextPage,\n    isFetchingNextPage,\n    onLoadMore,\n    threshold: infiniteScrollThreshold,\n    scrollRef: gridRef,\n    sentinelRef,\n  })\n\n  const hasRows =\n    table.getTopRows().length +\n      table.getCenterRows().length +\n      table.getBottomRows().length >\n    0\n  const showFooter = hasFooter(table)\n\n  const columnSizing = table.getState().columnSizing\n  const columnSizingInfo = table.getState().columnSizingInfo\n  const columnSizeVars = React.useMemo(\n    () => (enableColumnResizing ? getColumnSizeVars(table) : {}),\n    // columnSizing/Info are intentional triggers: recompute vars on resize.\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n    [enableColumnResizing, table, columnSizing, columnSizingInfo]\n  )\n\n  return (\n    // Radix names the open delay `delayDuration`; Base UI's provider names it\n    // `delay`. Both are context-only components that ignore foreign props, so\n    // passing both (via an assertion, since each flavor types only its own)\n    // keeps this file flavor-neutral for the registry's install-time transform.\n    <TooltipProvider\n      {...({\n        delayDuration: 300,\n        delay: 300,\n      } as Partial<React.ComponentProps<typeof TooltipProvider>>)}\n    >\n      <div\n        // Forwarding the exposed DOM ref object as a JSX ref (not reading\n        // .current during render).\n        // eslint-disable-next-line react-hooks/refs\n        ref={refs.tablePaperRef}\n        data-slot=\"data-table\"\n        className={cn(\n          \"flex w-full flex-col gap-2\",\n          isFullscreen &&\n            \"fixed inset-0 z-50 gap-2 overflow-auto bg-background p-4\",\n          className\n        )}\n        data-density={density}\n        {...props}\n      >\n        {renderTopToolbar\n          ? renderTopToolbar({ table })\n          : enableTopToolbar && (\n              <DataTableToolbar\n                table={table}\n                // Forwarding the exposed ref objects as props (not reading\n                // .current during render).\n                // eslint-disable-next-line react-hooks/refs\n                toolbarRef={refs.topToolbarRef}\n                // eslint-disable-next-line react-hooks/refs\n                searchInputRef={refs.searchInputRef}\n              />\n            )}\n        {positionToolbarAlertBanner === \"top\" && (\n          <DataTableAlertBanner table={table} />\n        )}\n\n        {enablePagination &&\n          (positionPagination === \"top\" || positionPagination === \"both\") && (\n            <DataTablePagination\n              table={table}\n              pageSizeOptions={pageSizeOptions}\n            />\n          )}\n\n        <DndContext\n          sensors={sensors}\n          collisionDetection={collisionDetection}\n          onDragEnd={handleDragEnd}\n        >\n          {enableGrouping &&\n            (positionToolbarDropZone === \"top\" ||\n              positionToolbarDropZone === \"both\") && (\n              <DataTableDropToGroupZone table={table} />\n            )}\n\n          <div\n            // Callback refs run after render; assigning .current there is\n            // legal React.\n            // eslint-disable-next-line react-hooks/immutability\n            ref={(node) => {\n              gridRef.current = node\n              // The exposed container ref is a RefObject, mutable by contract.\n              // eslint-disable-next-line react-hooks/immutability\n              refs.tableContainerRef.current = node\n            }}\n            onKeyDown={onKeyDown}\n            data-slot=\"data-table-surface\"\n            className={cn(\n              // This surface is the single scroll container for both axes, so\n              // the sticky header/footer engage and the horizontal scrollbar\n              // stays pinned to the visible bottom. Neutralize the shadcn\n              // <Table> wrapper's own overflow so it doesn't become a second\n              // (unbounded) scroll container that breaks sticky positioning.\n              \"relative overflow-auto rounded-md border *:data-[slot=table-container]:overflow-visible\",\n              // MRT-parity default bound: with a sticky header the surface\n              // caps near the viewport height, so tall content (long groups,\n              // trees, detail panels) scrolls internally under the pinned\n              // header instead of stretching the page. `surfaceClassName`\n              // (via tailwind-merge) or `enableStickyHeader: false` opts out.\n              enableStickyHeader &&\n                \"max-h-[clamp(350px,calc(100dvh-200px),9999px)]\",\n              enableRowVirtualization && \"max-h-150\",\n              surfaceClassName\n            )}\n          >\n            {showProgressBars && (\n              <div\n                data-slot=\"data-table-progress\"\n                className=\"absolute inset-x-0 top-0 z-30 h-0.5 overflow-hidden bg-primary/20\"\n                role=\"presentation\"\n              >\n                {/* @keyframes can't go in an inline style attribute, so the rule\n                    lives in this co-located <style> — keeping the table\n                    self-contained (no globals.css / registry CSS needed). The\n                    animation itself is applied inline; reduced motion stops it. */}\n                <style>\n                  {\n                    \"@keyframes data-table-progress{from{transform:translateX(-100%)}to{transform:translateX(400%)}}@media (prefers-reduced-motion:reduce){[data-slot=data-table-progress-bar]{animation:none!important}}\"\n                  }\n                </style>\n                <div\n                  data-slot=\"data-table-progress-bar\"\n                  className=\"h-full w-1/3 bg-primary\"\n                  style={{\n                    animation: \"data-table-progress 1.1s ease-in-out infinite\",\n                  }}\n                />\n              </div>\n            )}\n\n            <Table\n              style={{\n                ...columnSizeVars,\n                // Fixed layout makes per-column widths authoritative (auto\n                // layout would stretch/redistribute them and ignore a resize).\n                // `max(100%, totalSize)` keeps the table at least as wide as the\n                // surface: when the columns are narrower than the surface, fixed\n                // layout distributes the slack proportionally so they fill it\n                // (no trailing empty space); when they outgrow the surface, the\n                // table exceeds 100% and scrolls horizontally.\n                ...(enableColumnResizing\n                  ? { width: `max(100%, ${table.getTotalSize()}px)` }\n                  : null),\n              }}\n              className={cn(enableColumnResizing && \"table-fixed\")}\n            >\n              {renderCaption && (\n                <TableCaption>{renderCaption({ table })}</TableCaption>\n              )}\n              <DataTableHeader\n                table={table}\n                virtualColumns={virtualColumns}\n                withColumnSpacers={withColumnSpacers}\n              />\n              <MemoizedDataTableBody\n                table={table}\n                rowVirtualizer={rowVirtualizer}\n                virtualItems={virtualItems}\n                virtualColumns={virtualColumns}\n                withColumnSpacers={withColumnSpacers}\n              />\n              {showFooter && (\n                <DataTableFooter\n                  table={table}\n                  virtualColumns={virtualColumns}\n                  withColumnSpacers={withColumnSpacers}\n                />\n              )}\n            </Table>\n\n            {enableInfiniteScroll && hasRows && (\n              <>\n                {/* Bottom sentinel: when it scrolls near the viewport (within\n                    `infiniteScrollThreshold`px) the table asks for the next\n                    chunk. Sits after the rendered window, so it works with or\n                    without row virtualization. `sticky left-0` keeps it within\n                    the horizontal viewport so a wide (horizontally scrolled)\n                    table still triggers — the IntersectionObserver checks both\n                    axes, and a left-anchored sentinel would otherwise scroll\n                    out of the root rect. */}\n                <div\n                  ref={sentinelRef}\n                  data-slot=\"data-table-infinite-sentinel\"\n                  aria-hidden\n                  className=\"sticky left-0 h-px w-full\"\n                />\n                {isFetchingNextPage && (\n                  <div\n                    data-slot=\"data-table-infinite-loader\"\n                    role=\"status\"\n                    className=\"sticky left-0 flex items-center justify-center gap-2 py-3 text-sm text-muted-foreground\"\n                  >\n                    <span\n                      aria-hidden\n                      className=\"size-4 animate-spin rounded-full border-2 border-current border-t-transparent\"\n                    />\n                    {localization.loadingMore}\n                  </div>\n                )}\n              </>\n            )}\n\n            {showLoadingOverlay && hasRows && (\n              <div\n                className=\"absolute inset-0 z-10 bg-background/40\"\n                aria-hidden\n              />\n            )}\n          </div>\n\n          {enableGrouping &&\n            (positionToolbarDropZone === \"bottom\" ||\n              positionToolbarDropZone === \"both\") && (\n              <DataTableDropToGroupZone table={table} />\n            )}\n        </DndContext>\n\n        {positionToolbarAlertBanner === \"bottom\" && (\n          <DataTableAlertBanner table={table} />\n        )}\n\n        <DataTableBottomToolbar\n          table={table}\n          pageSizeOptions={pageSizeOptions}\n        />\n\n        <DataTableEditModal table={table} />\n      </div>\n    </TooltipProvider>\n  )\n}\n"
    },
    {
      "path": "ui/data-table/core/icons.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/core/icons.tsx",
      "content": "import {\n  ArrowDown,\n  ArrowUp,\n  Calendar,\n  Check,\n  ChevronDown,\n  ChevronLeft,\n  ChevronRight,\n  ChevronsLeft,\n  ChevronsRight,\n  ChevronsUpDown,\n  Columns3,\n  EyeOff,\n  Filter,\n  FilterX,\n  GripVertical,\n  Group,\n  ListFilter,\n  Maximize2,\n  Minimize2,\n  MoreVertical,\n  Pencil,\n  Pin,\n  PinOff,\n  RotateCcw,\n  Rows3,\n  Search,\n  X,\n} from \"lucide-react\"\n\n/** Any component that renders an icon and accepts a `className`. */\nexport type IconComponent = React.ComponentType<{ className?: string }>\n\n/**\n * Every icon the data table renders, by semantic name. Defaults are Lucide\n * (shadcn's default icon library). Override any subset via\n * `useDataTable({ icons: { … } })` to swap icon libraries or individual glyphs\n * (MRT's `icons` prop equivalent). Selection checkboxes and calendar internals\n * come from the shadcn primitives and are out of scope here.\n */\nexport interface DataTableIcons {\n  sortAscending: IconComponent\n  sortDescending: IconComponent\n  sortUnsorted: IconComponent\n  columnActions: IconComponent\n  filter: IconComponent\n  filterOff: IconComponent\n  /** Advanced filter panel toggle. */\n  advancedFilter: IconComponent\n  /** Clear-sort / show-all reset action. */\n  clearAll: IconComponent\n  hide: IconComponent\n  pin: IconComponent\n  pinnedRow: IconComponent\n  unpin: IconComponent\n  group: IconComponent\n  columnVisibility: IconComponent\n  density: IconComponent\n  fullscreenEnter: IconComponent\n  fullscreenExit: IconComponent\n  search: IconComponent\n  clear: IconComponent\n  pageFirst: IconComponent\n  pagePrev: IconComponent\n  pageNext: IconComponent\n  pageLast: IconComponent\n  expanded: IconComponent\n  collapsed: IconComponent\n  dragHandle: IconComponent\n  edit: IconComponent\n  save: IconComponent\n  cancel: IconComponent\n  calendar: IconComponent\n}\n\nexport const defaultIcons: DataTableIcons = {\n  sortAscending: ArrowUp,\n  sortDescending: ArrowDown,\n  sortUnsorted: ChevronsUpDown,\n  columnActions: MoreVertical,\n  filter: Filter,\n  filterOff: FilterX,\n  advancedFilter: ListFilter,\n  clearAll: RotateCcw,\n  hide: EyeOff,\n  pin: Pin,\n  pinnedRow: Pin,\n  unpin: PinOff,\n  group: Group,\n  columnVisibility: Columns3,\n  density: Rows3,\n  fullscreenEnter: Maximize2,\n  fullscreenExit: Minimize2,\n  search: Search,\n  clear: X,\n  pageFirst: ChevronsLeft,\n  pagePrev: ChevronLeft,\n  pageNext: ChevronRight,\n  pageLast: ChevronsRight,\n  expanded: ChevronDown,\n  collapsed: ChevronRight,\n  dragHandle: GripVertical,\n  edit: Pencil,\n  save: Check,\n  cancel: X,\n  calendar: Calendar,\n}\n"
    },
    {
      "path": "ui/data-table/core/localization.ts",
      "type": "registry:ui",
      "target": "components/ui/data-table/core/localization.ts",
      "content": "/**\n * Localization string table for the data table. Every user-facing string\n * (labels, aria text, pagination range) flows through here so the table can be\n * fully translated. Function-valued entries handle interpolation/plurals.\n *\n * Default is English. Override per instance via `useDataTable({ localization })`.\n */\nexport interface DataTableLocalization {\n  // Selection\n  selectAll: string\n  selectRow: string\n  clearSelection: string\n  rowsSelected: (selected: number, total: number) => string\n\n  // Sorting\n  sortByColumnAsc: (column: string) => string\n  sortByColumnDesc: (column: string) => string\n  sortAscending: string\n  sortDescending: string\n  clearSort: string\n  sortedAscending: string\n  sortedDescending: string\n\n  // Column actions\n  columnActions: string\n  hideColumn: string\n  showAllColumns: string\n  pinToLeft: string\n  pinToRight: string\n  unpin: string\n  reorderColumn: string\n  reorderRow: string\n  pinRow: string\n  unpinRow: string\n  resizeColumn: string\n\n  // Grouping / expansion\n  groupByColumn: (column: string) => string\n  ungroupByColumn: (column: string) => string\n  groupedBy: string\n  dropToGroupBy: string\n  expand: string\n  collapse: string\n  expandAll: string\n  collapseAll: string\n  toggleRowExpanded: string\n\n  // Column visibility\n  columnVisibility: string\n  toggleColumnVisibility: string\n\n  // Filtering\n  filterByColumn: (column: string) => string\n  clearFilter: string\n  filterMode: string\n  changeFilterMode: string\n  filterPlaceholder: (column: string) => string\n  showColumnFilters: string\n  hideColumnFilters: string\n  min: string\n  max: string\n  pickDate: string\n  pickDateRange: string\n  /** Labels for each filter mode, keyed by the `FilterMode` string. */\n  filterModes: Record<string, string>\n\n  // Advanced filter panel\n  advancedFilters: string\n  advancedFiltersMatchLabel: string\n  advancedFiltersMatchAll: string\n  advancedFiltersMatchAny: string\n  advancedFiltersOf: string\n  advancedFiltersAddRule: string\n  advancedFiltersApply: string\n  advancedFiltersClearAll: string\n  advancedFiltersColumn: string\n  advancedFiltersOperator: string\n  advancedFiltersValue: string\n  advancedFiltersEmpty: string\n  removeFilterRule: string\n  /** Operator labels, keyed by `AdvancedFilterOperator`. */\n  advancedFilterOperators: Record<string, string>\n\n  // Global search\n  search: string\n  searchPlaceholder: string\n  clearSearch: string\n  globalFilterMode: string\n\n  // Density\n  toggleDensity: string\n  densityComfortable: string\n  densityCompact: string\n  densitySpacious: string\n\n  // Full screen\n  enterFullscreen: string\n  exitFullscreen: string\n\n  // Pagination\n  rowsPerPage: string\n  paginationRange: (start: number, end: number, total: number) => string\n  goToFirstPage: string\n  goToPreviousPage: string\n  goToNextPage: string\n  goToLastPage: string\n  goToPage: (page: number) => string\n\n  // Editing / actions\n  rowActions: string\n  edit: string\n  save: string\n  cancel: string\n  delete: string\n  create: string\n  createNewRow: string\n  editRow: string\n  required: string\n  copy: string\n  copied: string\n  cellActions: string\n\n  // Empty / loading\n  noRecordsToDisplay: string\n  loading: string\n  loadingMore: string\n}\n\nexport const defaultLocalization: DataTableLocalization = {\n  selectAll: \"Select all\",\n  selectRow: \"Select row\",\n  clearSelection: \"Clear selection\",\n  rowsSelected: (selected, total) =>\n    `${selected} of ${total} row${total === 1 ? \"\" : \"s\"} selected`,\n\n  sortByColumnAsc: (column) => `Sort by ${column} ascending`,\n  sortByColumnDesc: (column) => `Sort by ${column} descending`,\n  sortAscending: \"Sort ascending\",\n  sortDescending: \"Sort descending\",\n  clearSort: \"Clear sort\",\n  sortedAscending: \"Sorted ascending\",\n  sortedDescending: \"Sorted descending\",\n\n  columnActions: \"Column actions\",\n  hideColumn: \"Hide column\",\n  showAllColumns: \"Show all columns\",\n  pinToLeft: \"Pin to left\",\n  pinToRight: \"Pin to right\",\n  unpin: \"Unpin\",\n  reorderColumn: \"Reorder column\",\n  reorderRow: \"Reorder row\",\n  pinRow: \"Pin row\",\n  unpinRow: \"Unpin row\",\n  resizeColumn: \"Resize column\",\n\n  groupByColumn: (column) => `Group by ${column}`,\n  ungroupByColumn: (column) => `Ungroup by ${column}`,\n  groupedBy: \"Grouped by\",\n  dropToGroupBy: \"Drag a column here to group by it\",\n  expand: \"Expand\",\n  collapse: \"Collapse\",\n  expandAll: \"Expand all\",\n  collapseAll: \"Collapse all\",\n  toggleRowExpanded: \"Toggle row expanded\",\n\n  columnVisibility: \"Column visibility\",\n  toggleColumnVisibility: \"Toggle column visibility\",\n\n  filterByColumn: (column) => `Filter by ${column}`,\n  clearFilter: \"Clear filter\",\n  filterMode: \"Filter mode\",\n  changeFilterMode: \"Change filter mode\",\n  filterPlaceholder: (column) => `Filter ${column}…`,\n  showColumnFilters: \"Show filters\",\n  hideColumnFilters: \"Hide filters\",\n  min: \"Min\",\n  max: \"Max\",\n  pickDate: \"Pick a date\",\n  pickDateRange: \"Pick a date range\",\n  filterModes: {\n    fuzzy: \"Fuzzy\",\n    contains: \"Contains\",\n    startsWith: \"Starts with\",\n    endsWith: \"Ends with\",\n    equals: \"Equals\",\n    notEquals: \"Not equals\",\n    empty: \"Empty\",\n    notEmpty: \"Not empty\",\n    between: \"Between (exclusive)\",\n    betweenInclusive: \"Between (inclusive)\",\n    greaterThan: \"Greater than\",\n    greaterThanOrEqualTo: \"Greater than or equal to\",\n    lessThan: \"Less than\",\n    lessThanOrEqualTo: \"Less than or equal to\",\n    before: \"Before\",\n    after: \"After\",\n    betweenDates: \"Between\",\n    equalsString: \"Equals\",\n    arrIncludesSome: \"Includes\",\n    equalsBool: \"Equals\",\n  },\n\n  advancedFilters: \"Advanced filters\",\n  advancedFiltersMatchLabel: \"Match\",\n  advancedFiltersMatchAll: \"All\",\n  advancedFiltersMatchAny: \"Any\",\n  advancedFiltersOf: \"of the following rules\",\n  advancedFiltersAddRule: \"Add filter\",\n  advancedFiltersApply: \"Apply\",\n  advancedFiltersClearAll: \"Clear all\",\n  advancedFiltersColumn: \"Column\",\n  advancedFiltersOperator: \"Operator\",\n  advancedFiltersValue: \"Value\",\n  advancedFiltersEmpty: \"No filters yet. Add one to get started.\",\n  removeFilterRule: \"Remove filter\",\n  advancedFilterOperators: {\n    contains: \"contains\",\n    notContains: \"does not contain\",\n    startsWith: \"starts with\",\n    endsWith: \"ends with\",\n    equals: \"equals\",\n    notEquals: \"does not equal\",\n    isEmpty: \"is empty\",\n    isNotEmpty: \"is not empty\",\n    greaterThan: \"greater than\",\n    greaterThanOrEqual: \"greater than or equal\",\n    lessThan: \"less than\",\n    lessThanOrEqual: \"less than or equal\",\n    between: \"is between\",\n  },\n\n  search: \"Search\",\n  searchPlaceholder: \"Search…\",\n  clearSearch: \"Clear search\",\n  globalFilterMode: \"Search mode\",\n\n  toggleDensity: \"Toggle density\",\n  densityComfortable: \"Comfortable\",\n  densityCompact: \"Compact\",\n  densitySpacious: \"Spacious\",\n\n  enterFullscreen: \"Enter full screen\",\n  exitFullscreen: \"Exit full screen\",\n\n  rowsPerPage: \"Rows per page\",\n  paginationRange: (start, end, total) => `${start}–${end} of ${total}`,\n  goToFirstPage: \"Go to first page\",\n  goToPreviousPage: \"Go to previous page\",\n  goToNextPage: \"Go to next page\",\n  goToLastPage: \"Go to last page\",\n  goToPage: (page) => `Go to page ${page}`,\n\n  rowActions: \"Row actions\",\n  edit: \"Edit\",\n  save: \"Save\",\n  cancel: \"Cancel\",\n  delete: \"Delete\",\n  create: \"Create\",\n  createNewRow: \"Create new row\",\n  editRow: \"Edit row\",\n  required: \"Required\",\n  copy: \"Copy\",\n  copied: \"Copied\",\n  cellActions: \"Cell actions\",\n\n  noRecordsToDisplay: \"No records to display\",\n  loading: \"Loading…\",\n  loadingMore: \"Loading more…\",\n}\n"
    },
    {
      "path": "ui/data-table/core/types.ts",
      "type": "registry:ui",
      "target": "components/ui/data-table/core/types.ts",
      "content": "import type {\n  Cell,\n  Column,\n  Row,\n  RowData,\n  Table,\n  TableOptions,\n} from \"@tanstack/react-table\"\nimport type * as React from \"react\"\n\nimport type { Virtualizer, VirtualizerOptions } from \"@tanstack/react-virtual\"\n\nimport type { FilterMode, GlobalFilterMode } from \"../fns/filter-fns\"\nimport type { DataTableIcons } from \"./icons\"\nimport type { DataTableLocalization } from \"./localization\"\n\n// Re-exported as part of the public type surface: these describe column-`meta`\n// filter config, while the runtime filter fns stay internal.\nexport type { FilterMode, GlobalFilterMode } from \"../fns/filter-fns\"\n\nexport type Density = \"compact\" | \"comfortable\" | \"spacious\"\n\n/** The `@tanstack/react-virtual` instance powering body-row virtualization. */\nexport type DataTableRowVirtualizer = Virtualizer<\n  HTMLDivElement,\n  HTMLTableRowElement\n>\n/** The `@tanstack/react-virtual` instance powering column virtualization. */\nexport type DataTableColumnVirtualizer = Virtualizer<\n  HTMLDivElement,\n  HTMLTableCellElement\n>\n\n/** A value, or a function of the table instance returning that value. */\ntype ValueOrFunc<TData extends RowData, TValue> =\n  TValue | ((props: { table: DataTableInstance<TData> }) => TValue)\n\n/** Partial passthrough merged into the row `useVirtualizer` call. */\nexport type RowVirtualizerOptions<TData extends RowData> = ValueOrFunc<\n  TData,\n  Partial<VirtualizerOptions<HTMLDivElement, HTMLTableRowElement>>\n>\n/** Partial passthrough merged into the column `useVirtualizer` call. */\nexport type ColumnVirtualizerOptions<TData extends RowData> = ValueOrFunc<\n  TData,\n  Partial<VirtualizerOptions<HTMLDivElement, HTMLTableCellElement>>\n>\n\n/**\n * DOM refs to the table's structural elements, exposed on\n * `table.tableInstance.refs` for imperative access (focus, measure, scroll). Each is\n * populated after mount and may be `null` when its element isn't rendered\n * (e.g. `bottomToolbarRef` with no bottom toolbar, `tableFooterRef` with no\n * footer, `searchInputRef` before the search box is expanded).\n */\nexport interface DataTableRefs {\n  /** The outermost `data-slot=\"data-table\"` wrapper. Always present. */\n  tablePaperRef: React.RefObject<HTMLDivElement | null>\n  /** The scroll container (`data-slot=\"data-table-surface\"`) — the single\n   *  scroll container for both axes. Always present. */\n  tableContainerRef: React.RefObject<HTMLDivElement | null>\n  /** The top toolbar root (`data-slot=\"data-table-toolbar\"`). `null` when the\n   *  top toolbar is disabled or replaced via `renderTopToolbar`. */\n  topToolbarRef: React.RefObject<HTMLDivElement | null>\n  /** The bottom toolbar root (`data-slot=\"data-table-bottom-toolbar\"`). `null`\n   *  when there is no bottom toolbar, or it is replaced via\n   *  `renderBottomToolbar`. */\n  bottomToolbarRef: React.RefObject<HTMLDivElement | null>\n  /** The `<thead>` element. Always present. */\n  tableHeadRef: React.RefObject<HTMLTableSectionElement | null>\n  /** The `<tfoot>` element. `null` unless a column defines a `footer`. */\n  tableFooterRef: React.RefObject<HTMLTableSectionElement | null>\n  /** The global-search `<input>`. `null` until the search box is expanded. */\n  searchInputRef: React.RefObject<HTMLInputElement | null>\n}\n\n/** Filter UI variants (full set wired in Phase 2; Phase 1 ships \"text\"). */\nexport type FilterVariant =\n  | \"text\"\n  | \"select\"\n  | \"multi-select\"\n  | \"checkbox\"\n  | \"range\"\n  | \"range-slider\"\n  | \"date\"\n  | \"date-range\"\n\nexport interface DataTableFilterOption {\n  label: string\n  value: string\n}\n\n/** Operators for the advanced filter panel's compound rules. Availability per\n *  column is gated by its `meta.variant` (see `getOperatorsForVariant`). */\nexport type AdvancedFilterOperator =\n  | \"isEmpty\"\n  | \"isNotEmpty\"\n  | \"equals\"\n  | \"notEquals\"\n  | \"contains\"\n  | \"notContains\"\n  | \"startsWith\"\n  | \"endsWith\"\n  | \"greaterThan\"\n  | \"greaterThanOrEqual\"\n  | \"lessThan\"\n  | \"lessThanOrEqual\"\n  | \"between\"\n\n/** A single advanced-filter condition: column + operator + value(s). */\nexport interface AdvancedFilterRule {\n  /** Stable key for React + edits. */\n  id: string\n  columnId: string\n  operator: AdvancedFilterOperator\n  /** Comparison value (string / number / Date / undefined). */\n  value: unknown\n  /** Upper bound, only used by the `between` operator. */\n  value2?: unknown\n}\n\n/** The full advanced filter: a flat list of rules joined by one logic mode. */\nexport interface AdvancedFilterGroup {\n  logic: \"and\" | \"or\"\n  rules: AdvancedFilterRule[]\n}\n\nexport type EditDisplayMode = \"cell\" | \"row\" | \"table\" | \"modal\" | \"custom\"\n\n/** How the create form is surfaced (decoupled from {@link EditDisplayMode}). */\nexport type CreateDisplayMode = \"modal\" | \"row\" | \"custom\"\n\n/** How the pagination controls render. `\"pages\"` = numbered page buttons. */\nexport type PaginationDisplayMode = \"default\" | \"pages\" | \"custom\"\n\n/** Where column filter inputs live: the filter subheader row or per-column\n *  popovers opened from the column header. */\nexport type ColumnFilterDisplayMode = \"subheader\" | \"popover\" | \"custom\"\n\n/** Which cell is being edited (cell mode). */\nexport interface EditingCell {\n  rowId: string\n  columnId: string\n}\n\n/** Edit-field variants for the inline editors. */\nexport type EditVariant = \"text\" | \"number\" | \"select\"\n\n// Per-column configuration carried on `columnDef.meta`. Augments the TanStack\n// `ColumnMeta` interface so it is strongly typed everywhere `meta` is read.\ndeclare module \"@tanstack/react-table\" {\n  interface ColumnMeta<TData extends RowData, TValue> {\n    /** Filter UI variant rendered in the filter row. Defaults to \"text\". */\n    variant?: FilterVariant\n    /** Options for `select` / `multi-select` filter variants. If omitted for a\n     *  select-style variant, options are derived from faceted unique values. */\n    options?: DataTableFilterOption[]\n    /** Default filter mode for this column (overrides the per-variant default). */\n    filterMode?: FilterMode\n    /** Per-column override for the filter-mode menu (defaults to the table). */\n    enableColumnFilterModes?: boolean\n    /** Custom filter UI for this column (escape hatch). Replaces the variant. */\n    renderColumnFilter?: (props: {\n      column: Column<TData, TValue>\n      table: DataTableInstance<TData>\n    }) => React.ReactNode\n    /** Restrict (and order) the filter-mode menu for this column to this subset\n     *  of modes. Include the column's default mode. */\n    columnFilterModeOptions?: FilterMode[]\n    /** Allow editing this column (defaults to true when table editing is on). */\n    enableEditing?: boolean\n    /** Inline editor variant. Defaults to \"text\". */\n    editVariant?: EditVariant\n    /** Options for the \"select\" edit variant. */\n    editSelectOptions?: DataTableFilterOption[]\n    /** Custom inline editor for this column (escape hatch). Replaces the built-in\n     *  editor while the cell/row is editing; drive the value via `table.tableInstance`\n     *  (`rowDraft`/`setRowDraftValue` or `onEditCellSave`). */\n    renderEditCell?: (props: CellRenderProps<TData, TValue>) => React.ReactNode\n    /** Custom render for this column's group header cell (when grouped). */\n    renderGroupedCell?: (\n      props: CellRenderProps<TData, TValue>\n    ) => React.ReactNode\n    /** Custom render for this column's aggregated cell (when grouped). Overrides\n     *  the TanStack `columnDef.aggregatedCell`. */\n    renderAggregatedCell?: (\n      props: CellRenderProps<TData, TValue>\n    ) => React.ReactNode\n    /** Custom render for this column's placeholder cells in grouped rows (cells\n     *  with no value because another column owns the group). Default: empty. */\n    renderPlaceholderCell?: (\n      props: CellRenderProps<TData, TValue>\n    ) => React.ReactNode\n    /** Validate an edited value; return an error message or undefined if valid. */\n    validate?: (value: unknown) => string | undefined\n    /** Show a click-to-copy affordance on this column's cells. */\n    enableClickToCopy?: boolean\n    /** Horizontal alignment applied to the header label and body cells. */\n    align?: \"left\" | \"center\" | \"right\"\n    /** Opt this column out of match highlighting. */\n    disableHighlight?: boolean\n    /** Hide the column-actions menu for this column. */\n    disableColumnActions?: boolean\n    /** Human-readable label for menus when the header is not a plain string. */\n    label?: string\n  }\n}\n\nexport interface DataTableSlotProps<TData extends RowData> {\n  table: DataTableInstance<TData>\n}\n\nexport interface RowEvent<TData extends RowData> {\n  row: Row<TData>\n  table: DataTableInstance<TData>\n  event: React.MouseEvent<HTMLTableRowElement>\n}\n\nexport interface CellEvent<TData extends RowData> {\n  cell: Cell<TData, unknown>\n  row: Row<TData>\n  table: DataTableInstance<TData>\n  event: React.MouseEvent<HTMLTableCellElement>\n}\n\n/** Props passed to per-column cell render hooks on `columnDef.meta`. */\nexport interface CellRenderProps<TData extends RowData, TValue = unknown> {\n  cell: Cell<TData, TValue>\n  row: Row<TData>\n  column: Column<TData, TValue>\n  table: DataTableInstance<TData>\n}\n\n/**\n * Our configuration + UI state, attached to the TanStack table instance under\n * `table.tableInstance`. Sub-components read it from the instance rather than via\n * prop drilling (mirrors Material React Table's `table.options` pattern).\n */\nexport interface DataTableConfig<TData extends RowData> {\n  localization: DataTableLocalization\n  icons: DataTableIcons\n  density: Density\n  setDensity: React.Dispatch<React.SetStateAction<Density>>\n  isFullscreen: boolean\n  setIsFullscreen: React.Dispatch<React.SetStateAction<boolean>>\n  showColumnFilters: boolean\n  setShowColumnFilters: React.Dispatch<React.SetStateAction<boolean>>\n  /** Active filter mode per column id. */\n  columnFilterModes: Record<string, FilterMode>\n  /** Switch a column's filter mode (resets the value when it becomes invalid). */\n  setColumnFilterMode: (columnId: string, mode: FilterMode) => void\n  globalFilterMode: GlobalFilterMode\n  setGlobalFilterMode: (mode: GlobalFilterMode) => void\n  enableGlobalFilter: boolean\n  enableGlobalFilterModes: boolean\n  /** Compound AND/OR filter panel (independent of, and additive to, the\n   *  per-column filters and global search). */\n  enableAdvancedFilter: boolean\n  advancedFilter: AdvancedFilterGroup\n  setAdvancedFilter: React.Dispatch<React.SetStateAction<AdvancedFilterGroup>>\n  showAdvancedFilterPanel: boolean\n  setShowAdvancedFilterPanel: React.Dispatch<React.SetStateAction<boolean>>\n  isLoading: boolean\n  isSaving: boolean\n  showProgressBars: boolean\n  showSkeletons: boolean\n  showLoadingOverlay: boolean\n  enableFacetedValues: boolean\n  enableColumnActions: boolean\n  enableColumnFilters: boolean\n  enableColumnFilterModes: boolean\n  enableFilterMatchHighlighting: boolean\n  /** Column ids that supply a custom cell renderer (skipped by auto-highlight). */\n  columnsWithCustomCell: ReadonlySet<string>\n  enableColumnOrdering: boolean\n  enableColumnPinning: boolean\n  enableColumnResizing: boolean\n  enableColumnAutosize: boolean\n  /** Resize a single column to fit its widest visible value (header + data). */\n  autoSizeColumn: (columnId: string) => void\n  /** Auto-size every resizable visible column. */\n  autoSizeAllColumns: () => void\n  enableRowOrdering: boolean\n  enableRowPinning: boolean\n  enableRowNumbers: boolean\n  rowNumberMode: \"static\" | \"original\"\n  /** Called on a row drag-and-drop reorder; the consumer reorders its data. */\n  onRowOrderChange?: (activeRowId: string, overRowId: string) => void\n  enableGrouping: boolean\n  enableExpanding: boolean\n  enableStickyFooter: boolean\n  renderDetailPanel?: (props: {\n    row: Row<TData>\n    table: DataTableInstance<TData>\n  }) => React.ReactNode\n\n  // Editing / actions\n  enableEditing: boolean\n  editDisplayMode: EditDisplayMode\n  createDisplayMode: CreateDisplayMode\n  editingCell: EditingCell | null\n  setEditingCell: React.Dispatch<React.SetStateAction<EditingCell | null>>\n  editingRowId: string | null\n  isCreating: boolean\n  /** Draft values for the row/modal editor + create form, keyed by column id. */\n  rowDraft: Record<string, unknown>\n  setRowDraftValue: (columnId: string, value: unknown) => void\n  /** Enter row/modal editing for a row, seeding the draft from its values. */\n  beginRowEdit: (row: Row<TData>) => void\n  /** Open the create form, seeding the draft from `createRowDefaults`. */\n  beginCreate: () => void\n  /** Exit any editing/creating state, discarding the draft. */\n  cancelEdit: () => void\n  enableClickToCopy: boolean\n  onEditCellSave?: (props: {\n    row: Row<TData>\n    column: Column<TData, unknown>\n    value: unknown\n    table: DataTableInstance<TData>\n  }) => void\n  onSaveRow?: (props: {\n    row: Row<TData>\n    values: Record<string, unknown>\n    table: DataTableInstance<TData>\n    exit: () => void\n  }) => void\n  onCreateRow?: (props: {\n    values: Record<string, unknown>\n    table: DataTableInstance<TData>\n    exit: () => void\n  }) => void\n  renderRowActions?: (props: {\n    row: Row<TData>\n    table: DataTableInstance<TData>\n  }) => React.ReactNode\n  renderCellActionMenuItems?: (props: {\n    cell: Cell<TData, unknown>\n    row: Row<TData>\n    table: DataTableInstance<TData>\n  }) => React.ReactNode\n  renderRowActionMenuItems?: (props: {\n    row: Row<TData>\n    table: DataTableInstance<TData>\n  }) => React.ReactNode\n  renderColumnActionsMenuItems?: (props: {\n    column: Column<TData, unknown>\n    table: DataTableInstance<TData>\n  }) => React.ReactNode\n  renderColumnFilterModeMenuItems?: (props: {\n    column: Column<TData, unknown>\n    modes: FilterMode[]\n    currentMode: FilterMode\n    onSelect: (mode: FilterMode) => void\n    table: DataTableInstance<TData>\n  }) => React.ReactNode\n  renderGlobalFilterModeMenuItems?: (props: {\n    modes: GlobalFilterMode[]\n    currentMode: GlobalFilterMode\n    onSelect: (mode: GlobalFilterMode) => void\n    table: DataTableInstance<TData>\n  }) => React.ReactNode\n\n  // Event listeners\n  onRowClick?: (props: RowEvent<TData>) => void\n  onRowDoubleClick?: (props: RowEvent<TData>) => void\n  onCellClick?: (props: CellEvent<TData>) => void\n  onCellDoubleClick?: (props: CellEvent<TData>) => void\n\n  /** DOM refs to the table's structural elements (populated after mount). */\n  refs: DataTableRefs\n  enableRowVirtualization: boolean\n  enableColumnVirtualization: boolean\n  estimateRowHeight: number\n  virtualOverscan: number\n  /** Flat height (px) applied to every row. */\n  rowHeight?: number\n  /** Per-row height: a px number, `\"auto\"` (wrap + grow), or `null` for default. */\n  getRowHeight?: (row: Row<TData>) => number | \"auto\" | null\n  rowVirtualizerOptions?: RowVirtualizerOptions<TData>\n  columnVirtualizerOptions?: ColumnVirtualizerOptions<TData>\n  rowVirtualizerInstanceRef?: React.RefObject<DataTableRowVirtualizer | null>\n  columnVirtualizerInstanceRef?: React.RefObject<DataTableColumnVirtualizer | null>\n  enableStickyHeader: boolean\n  enablePagination: boolean\n  /** Load more rows as the user scrolls near the bottom (auto-hides the pager). */\n  enableInfiniteScroll: boolean\n  /** Called when the sentinel nears the viewport and more rows can be loaded. */\n  onLoadMore?: () => void\n  /** Consumer flag: more rows exist to load. */\n  hasNextPage: boolean\n  /** Consumer flag: a load is currently in flight. */\n  isFetchingNextPage: boolean\n  /** Distance (px) from the bottom at which to prefetch. */\n  infiniteScrollThreshold: number\n  positionPagination: \"top\" | \"bottom\" | \"both\" | \"none\"\n  paginationDisplayMode: PaginationDisplayMode\n  columnFilterDisplayMode: ColumnFilterDisplayMode\n  positionGlobalFilter: \"left\" | \"right\" | \"none\"\n  positionToolbarAlertBanner: \"top\" | \"bottom\" | \"none\"\n  positionToolbarDropZone: \"top\" | \"bottom\" | \"both\" | \"none\"\n  enableRowSelection: boolean\n  enableTopToolbar: boolean\n  enableBottomToolbar: boolean\n  enableDensityToggle: boolean\n  enableFullscreenToggle: boolean\n  enableToolbarInternalActions: boolean\n  enableKeyboardNavigation: boolean\n  title?: React.ReactNode\n  renderToolbarActions?: (props: DataTableSlotProps<TData>) => React.ReactNode\n  renderTopToolbar?: (props: DataTableSlotProps<TData>) => React.ReactNode\n  renderBottomToolbar?: (props: DataTableSlotProps<TData>) => React.ReactNode\n  renderToolbarInternalActions?: (\n    props: DataTableSlotProps<TData>\n  ) => React.ReactNode\n  renderBottomToolbarCustomActions?: (\n    props: DataTableSlotProps<TData>\n  ) => React.ReactNode\n  renderCaption?: (props: DataTableSlotProps<TData>) => React.ReactNode\n  renderEmpty?: (props: DataTableSlotProps<TData>) => React.ReactNode\n}\n\n/** A TanStack table instance enriched with our `tableInstance` config. */\nexport type DataTableInstance<TData extends RowData = unknown> =\n  Table<TData> & {\n    tableInstance: DataTableConfig<TData>\n  }\n\n/**\n * Options for {@link useDataTable}. Extends the full TanStack `TableOptions`\n * (so controlled state, `manual*` flags, `getRowId`, etc. all pass through)\n * and adds our presentation/feature options. `getCoreRowModel` and the other\n * row models are supplied with sensible defaults but can be overridden.\n */\nexport interface UseDataTableOptions<TData extends RowData> extends Omit<\n  TableOptions<TData>,\n  \"getCoreRowModel\"\n> {\n  getCoreRowModel?: TableOptions<TData>[\"getCoreRowModel\"]\n  localization?: Partial<DataTableLocalization>\n  /** Override any subset of the table's icons. */\n  icons?: Partial<DataTableIcons>\n  /** Initial density. Uncontrolled. */\n  defaultDensity?: Density\n  /** Initially show the filter row. Uncontrolled. */\n  defaultShowColumnFilters?: boolean\n  /** Show the loading affordances (progress bar; skeletons when empty; a dimming\n   *  overlay over existing rows). Toggle each independently below. */\n  isLoading?: boolean\n  /** Show the progress bar for an in-flight save/mutation. Defaults the progress\n   *  bar on without replacing rows with skeletons. */\n  isSaving?: boolean\n  /** Show the top progress bar. Default: `isLoading || isSaving`. */\n  showProgressBars?: boolean\n  /** Replace the body with skeleton rows while empty. Default: `isLoading`. */\n  showSkeletons?: boolean\n  /** Dim existing rows with an overlay while loading. Default: `isLoading`. */\n  showLoadingOverlay?: boolean\n  /** Compute faceted unique values / min-max (auto select options + range\n   *  bounds). Default true; disable to skip the faceted row models. */\n  enableFacetedValues?: boolean\n  enableColumnActions?: boolean\n  /** Show the filter-mode menu adornment on filter fields. Default true. */\n  enableColumnFilterModes?: boolean\n  /** Highlight matched substrings in cells. Default true. */\n  enableFilterMatchHighlighting?: boolean\n  /** Show the expandable global search in the toolbar. Default true. */\n  enableGlobalFilter?: boolean\n  /** Show the global search mode menu (fuzzy/contains/…). Default true. */\n  enableGlobalFilterModes?: boolean\n  /** While the global search is in fuzzy mode, order rows by best match (most\n   *  relevant first) until the user applies their own sort. Default false —\n   *  MRT defaults this on, but the data table keeps it off so searching never silently\n   *  reorders rows unless opted in. Ignored for non-fuzzy modes, when grouping\n   *  or expanded, or under manual sorting/filtering. */\n  enableGlobalFilterRankedResults?: boolean\n  /** Initial global search mode. Default \"fuzzy\". */\n  defaultGlobalFilterMode?: GlobalFilterMode\n  /** Controlled density. Pair with `onDensityChange`; omit for uncontrolled\n   *  (seed the initial value with `defaultDensity`). */\n  density?: Density\n  /** Called whenever the density changes (toolbar toggle or programmatic). */\n  onDensityChange?: (density: Density) => void\n  /** Controlled full-screen state. Pair with `onIsFullscreenChange`. */\n  isFullscreen?: boolean\n  /** Called whenever the full-screen state is toggled. */\n  onIsFullscreenChange?: (isFullscreen: boolean) => void\n  /** Controlled filter-row visibility. Pair with `onShowColumnFiltersChange`;\n   *  omit for uncontrolled (seed with `defaultShowColumnFilters`). */\n  showColumnFilters?: boolean\n  /** Called whenever the filter row is shown or hidden. */\n  onShowColumnFiltersChange?: (showColumnFilters: boolean) => void\n  /** Enable the advanced filter panel: compound rules joined by AND/OR, applied\n   *  on top of the per-column filters. A toolbar button opens the panel and\n   *  shows the active-rule count. Default false. */\n  enableAdvancedFilter?: boolean\n  /** Controlled advanced filter. Pair with `onAdvancedFilterChange`; omit for\n   *  uncontrolled (seed with `defaultAdvancedFilter`). */\n  advancedFilter?: AdvancedFilterGroup\n  /** Initial advanced filter for uncontrolled usage. */\n  defaultAdvancedFilter?: AdvancedFilterGroup\n  /** Called whenever the advanced filter changes. */\n  onAdvancedFilterChange?: (filter: AdvancedFilterGroup) => void\n  /** Controlled global search mode. Pair with `onGlobalFilterModeChange`;\n   *  omit for uncontrolled (seed with `defaultGlobalFilterMode`). */\n  globalFilterMode?: GlobalFilterMode\n  /** Called whenever the global search mode changes. */\n  onGlobalFilterModeChange?: (mode: GlobalFilterMode) => void\n  /** Drag-and-drop column reordering (adds a grip to each header). */\n  enableColumnOrdering?: boolean\n  /** Column pinning (left/right) via the column-actions menu + sticky columns. */\n  enableColumnPinning?: boolean\n  /** Column resizing via an edge drag handle. */\n  enableColumnResizing?: boolean\n  /**\n   * Double-clicking a column's resize handle auto-sizes it to fit its widest\n   * value. Defaults to `enableColumnResizing`. When off, double-click resets\n   * the column to its default size instead.\n   */\n  enableColumnAutosize?: boolean\n  /** Drag-and-drop row reordering (adds a drag-handle column). */\n  enableRowOrdering?: boolean\n  /** Row pinning (top) via a pin toggle in the row-number column. */\n  enableRowPinning?: boolean\n  /** Adds a leading row-number column. */\n  enableRowNumbers?: boolean\n  /** \"static\" tracks the current view (page-aware); \"original\" uses source index. */\n  rowNumberMode?: \"static\" | \"original\"\n  /** Called on a row drag-and-drop reorder; the consumer reorders its data. */\n  onRowOrderChange?: (activeRowId: string, overRowId: string) => void\n  /** Row grouping (group-by menu + drop-to-group zone + aggregated group rows). */\n  enableGrouping?: boolean\n  /** Row expansion (tree sub-rows and/or detail panels). Auto-on with grouping\n   *  or when `renderDetailPanel`/`getSubRows` is provided. */\n  enableExpanding?: boolean\n  /** Pin the footer (aggregation/footer cells) to the bottom of the surface.\n   *  Default false. */\n  enableStickyFooter?: boolean\n  /** Render an expanding detail panel for each row. */\n  renderDetailPanel?: (props: {\n    row: Row<TData>\n    table: DataTableInstance<TData>\n  }) => React.ReactNode\n\n  // Editing / actions\n  /** Enable inline editing. */\n  enableEditing?: boolean\n  /** How edits are surfaced. Default \"cell\". */\n  editDisplayMode?: EditDisplayMode\n  /** How the create form is surfaced, independent of `editDisplayMode`:\n   *  `\"modal\"` (dialog, default), `\"row\"` (an inline editable row at the top of\n   *  the table), or `\"custom\"` (you render it from `isCreating` + `rowDraft`). */\n  createDisplayMode?: CreateDisplayMode\n  /** Default values for the create form, keyed by column id. */\n  createRowDefaults?: Record<string, unknown>\n  /** Show a click-to-copy affordance on all cells (per-column override via meta). */\n  enableClickToCopy?: boolean\n  onEditCellSave?: (props: {\n    row: Row<TData>\n    column: Column<TData, unknown>\n    value: unknown\n    table: DataTableInstance<TData>\n  }) => void\n  onSaveRow?: (props: {\n    row: Row<TData>\n    values: Record<string, unknown>\n    table: DataTableInstance<TData>\n    exit: () => void\n  }) => void\n  onCreateRow?: (props: {\n    values: Record<string, unknown>\n    table: DataTableInstance<TData>\n    exit: () => void\n  }) => void\n  renderRowActions?: (props: {\n    row: Row<TData>\n    table: DataTableInstance<TData>\n  }) => React.ReactNode\n  renderCellActionMenuItems?: (props: {\n    cell: Cell<TData, unknown>\n    row: Row<TData>\n    table: DataTableInstance<TData>\n  }) => React.ReactNode\n  /** Render a kebab menu in the row-actions column. Returns the menu items\n   *  (e.g. `<DropdownMenuItem>`); injects the actions column automatically. */\n  renderRowActionMenuItems?: (props: {\n    row: Row<TData>\n    table: DataTableInstance<TData>\n  }) => React.ReactNode\n  /** Append custom items to the bottom of every column-actions menu. Returns the\n   *  items (e.g. `<DropdownMenuItem>`); a separator is added before them. */\n  renderColumnActionsMenuItems?: (props: {\n    column: Column<TData, unknown>\n    table: DataTableInstance<TData>\n  }) => React.ReactNode\n  /** Replace the radio items in a column's filter-mode menu. Render your own\n   *  items and call `onSelect(mode)` to switch; `modes` is the allowed set. */\n  renderColumnFilterModeMenuItems?: (props: {\n    column: Column<TData, unknown>\n    modes: FilterMode[]\n    currentMode: FilterMode\n    onSelect: (mode: FilterMode) => void\n    table: DataTableInstance<TData>\n  }) => React.ReactNode\n  /** Replace the radio items in the global-search mode menu. Render your own\n   *  items and call `onSelect(mode)` to switch; `modes` is the allowed set. */\n  renderGlobalFilterModeMenuItems?: (props: {\n    modes: GlobalFilterMode[]\n    currentMode: GlobalFilterMode\n    onSelect: (mode: GlobalFilterMode) => void\n    table: DataTableInstance<TData>\n  }) => React.ReactNode\n\n  /** Fired when a body row is clicked / double-clicked. */\n  onRowClick?: (props: RowEvent<TData>) => void\n  onRowDoubleClick?: (props: RowEvent<TData>) => void\n  /** Fired when a body cell is clicked / double-clicked. */\n  onCellClick?: (props: CellEvent<TData>) => void\n  onCellDoubleClick?: (props: CellEvent<TData>) => void\n\n  /** Virtualize body rows for large datasets (recommended past ~100 rows).\n   *  The table surface becomes the scroll container — give it a bounded height\n   *  via `className`/`style` (defaults to `max-h-[600px]`). Disables row DnD. */\n  enableRowVirtualization?: boolean\n  /** Virtualize columns for very wide tables. Applies fixed column widths and\n   *  is not combined with column pinning/ordering. */\n  enableColumnVirtualization?: boolean\n  /** Estimated row height (px) for the virtualizer. Default 52. */\n  estimateRowHeight?: number\n  /** Extra rows rendered above/below the viewport. Default 8. */\n  virtualOverscan?: number\n  /** Flat height (px) applied to every row. Overridden per-row by\n   *  `getRowHeight`. Also seeds the virtualizer estimate. */\n  rowHeight?: number\n  /** Per-row height. Return a px number to pin the row, `\"auto\"` to let it wrap\n   *  and grow to fit its content (even with column resizing on), or `null` to\n   *  fall back to `rowHeight` / the density default. Applies to data rows. */\n  getRowHeight?: (row: Row<TData>) => number | \"auto\" | null\n  /** Partial `@tanstack/react-virtual` options merged into the row virtualizer\n   *  (overrides the built-in `count`/`estimateSize`/`overscan`/`measureElement`).\n   *  Accepts an object or a `({ table }) => options` function. */\n  rowVirtualizerOptions?: RowVirtualizerOptions<TData>\n  /** Partial `@tanstack/react-virtual` options merged into the column\n   *  virtualizer. Accepts an object or a `({ table }) => options` function. */\n  columnVirtualizerOptions?: ColumnVirtualizerOptions<TData>\n  /** Ref populated with the row `Virtualizer` instance for imperative control\n   *  (e.g. `scrollToIndex`). Only set when `enableRowVirtualization`. */\n  rowVirtualizerInstanceRef?: React.RefObject<DataTableRowVirtualizer | null>\n  /** Ref populated with the column `Virtualizer` instance. Only set when\n   *  `enableColumnVirtualization`. */\n  columnVirtualizerInstanceRef?: React.RefObject<DataTableColumnVirtualizer | null>\n  /** Pin the header rows to the top of the scrollable surface. Also gives the\n   *  surface a default viewport-bound max-height (`clamp(350px, 100dvh -\n   *  200px, 9999px)`) so tall content scrolls internally; override the bound\n   *  with `surfaceClassName`. Default true. */\n  enableStickyHeader?: boolean\n  enablePagination?: boolean\n  /** Load more rows as the user scrolls near the bottom (infinite append). When\n   *  on, the pager is hidden and pagination is forced to a single growing page,\n   *  so all appended rows render. Fully controlled: the table calls `onLoadMore`\n   *  only when the bottom sentinel nears the viewport AND `hasNextPage` AND\n   *  `!isFetchingNextPage`. Pairs directly with TanStack Query's\n   *  `useInfiniteQuery` (`fetchNextPage`/`hasNextPage`/`isFetchingNextPage`).\n   *  Default false. */\n  enableInfiniteScroll?: boolean\n  /** Called to request the next chunk. Should be stable (memoized) so the table\n   *  doesn't re-trigger on every render. Append the result to `data`. */\n  onLoadMore?: () => void\n  /** Whether more rows remain to be loaded. Default false. */\n  hasNextPage?: boolean\n  /** Whether a load is currently in flight (blocks re-triggering). Default false. */\n  isFetchingNextPage?: boolean\n  /** Distance in px from the bottom at which to prefetch. Default 200. */\n  infiniteScrollThreshold?: number\n  /** Where the pagination controls render. Default \"bottom\". \"none\" keeps\n   *  pagination active but hides the controls. */\n  positionPagination?: \"top\" | \"bottom\" | \"both\" | \"none\"\n  /** Pagination control style: `\"default\"` (range label + first/prev/next/last\n   *  buttons), `\"pages\"` (numbered page buttons), or `\"custom\"` (render your own\n   *  via `renderBottomToolbarCustomActions`). Default \"default\". */\n  paginationDisplayMode?: PaginationDisplayMode\n  /** Where column filter inputs live: `\"subheader\"` (a filter row under the\n   *  header, default), `\"popover\"` (per-column popovers opened from the column\n   *  header), or `\"custom\"` (you render them). */\n  columnFilterDisplayMode?: ColumnFilterDisplayMode\n  /** Which toolbar region the global search renders in. Default \"right\" (the\n   *  internal-actions cluster). \"left\" places it next to the title/actions;\n   *  \"none\" hides it (same as `enableGlobalFilter: false`). */\n  positionGlobalFilter?: \"left\" | \"right\" | \"none\"\n  /** Where the row-selection alert banner renders. Default \"top\". */\n  positionToolbarAlertBanner?: \"top\" | \"bottom\" | \"none\"\n  /** Where the group-by drop zone renders (grouping only). Default \"top\". */\n  positionToolbarDropZone?: \"top\" | \"bottom\" | \"both\" | \"none\"\n  /** Position of the auto-injected row-actions column. Default \"last\". */\n  positionActionsColumn?: \"first\" | \"last\"\n  /** Position of the auto-injected expand column (tree / detail panel).\n   *  Default \"first\". */\n  positionExpandColumn?: \"first\" | \"last\"\n  /** Select-all scope for the header checkbox: the current page (\"page\",\n   *  default) or every row (\"all\"). */\n  selectAllMode?: \"page\" | \"all\"\n  /** Show the select-all checkbox in the selection column header. Default true. */\n  enableSelectAll?: boolean\n  enableTopToolbar?: boolean\n  enableBottomToolbar?: boolean\n  /** Show the density toggle in the toolbar. Default true. */\n  enableDensityToggle?: boolean\n  /** Show the full-screen toggle in the toolbar. Default true. */\n  enableFullscreenToggle?: boolean\n  /** Show the toolbar's internal icon-action cluster (search, filters, column\n   *  visibility, export, density, full screen). Default true. Hides the whole\n   *  cluster at once; use the per-item flags for finer control. */\n  enableToolbarInternalActions?: boolean\n  enableKeyboardNavigation?: boolean\n  title?: React.ReactNode\n  /** Custom content rendered in the top toolbar's left region (next to the\n   *  title), e.g. bulk-action buttons. */\n  renderToolbarActions?: (props: DataTableSlotProps<TData>) => React.ReactNode\n  /** Replace the entire top toolbar with custom content. */\n  renderTopToolbar?: (props: DataTableSlotProps<TData>) => React.ReactNode\n  /** Replace the entire bottom toolbar (pagination region) with custom content. */\n  renderBottomToolbar?: (props: DataTableSlotProps<TData>) => React.ReactNode\n  /** Replace the top toolbar's internal icon-action cluster with custom content. */\n  renderToolbarInternalActions?: (\n    props: DataTableSlotProps<TData>\n  ) => React.ReactNode\n  /** Custom content rendered in the bottom toolbar's left region (next to\n   *  pagination), e.g. summary text or actions. */\n  renderBottomToolbarCustomActions?: (\n    props: DataTableSlotProps<TData>\n  ) => React.ReactNode\n  /** Render a `<caption>` for the table (e.g. an accessible summary). */\n  renderCaption?: (props: DataTableSlotProps<TData>) => React.ReactNode\n  renderEmpty?: (props: DataTableSlotProps<TData>) => React.ReactNode\n}\n"
    },
    {
      "path": "ui/data-table/core/use-data-table.ts",
      "type": "registry:ui",
      "target": "components/ui/data-table/core/use-data-table.ts",
      "content": "\"use client\"\n\nimport {\n  getCoreRowModel,\n  getExpandedRowModel,\n  getFacetedMinMaxValues,\n  getFacetedRowModel,\n  getFacetedUniqueValues,\n  getFilteredRowModel,\n  getGroupedRowModel,\n  getPaginationRowModel,\n  useReactTable,\n  type RowData,\n} from \"@tanstack/react-table\"\nimport * as React from \"react\"\n\nimport { VALUELESS_MODES, type FilterMode } from \"../fns/filter-fns\"\nimport { columnKey } from \"../helpers/column-key\"\nimport { getColumnLabel } from \"../helpers/column-label\"\nimport {\n  getColumnDefMinWidth,\n  getColumnDefPreferredWidth,\n  getHeaderControlsWidth,\n  isDataColumnDef,\n  type HeaderControlsOptions,\n} from \"../helpers/header-controls\"\nimport { measureColumnWidth } from \"../helpers/measure-column-width\"\nimport { useAdvancedFilter } from \"../hooks/use-advanced-filter\"\nimport { useColumnFilterModes } from \"../hooks/use-column-filter-modes\"\nimport { useControllableState } from \"../hooks/use-controllable-state\"\nimport { useEditingState } from \"../hooks/use-editing-state\"\nimport { useGlobalFilterMode } from \"../hooks/use-global-filter-mode\"\nimport { usePageResetOnFilterChange } from \"../hooks/use-page-reset-on-filter-change\"\nimport { useResolvedColumns } from \"../hooks/use-resolved-columns\"\nimport { useDataTableConfigContext } from \"./config-context\"\nimport { defaultIcons } from \"./icons\"\nimport { defaultLocalization } from \"./localization\"\nimport type {\n  DataTableConfig,\n  DataTableInstance,\n  Density,\n  UseDataTableOptions,\n} from \"./types\"\n\n/**\n * Core hook. Wraps `useReactTable` with MRT-flavoured defaults (row models,\n * auto-injected selection column, localization) and attaches our presentation\n * state + feature flags to the instance as `table.tableInstance`. Returns the\n * enriched instance to hand to `<DataTable table={table} />`.\n *\n * Controlled data state (sorting/filtering/pagination/selection/visibility),\n * `manual*` server-side flags, and `getRowId` all pass straight through to\n * TanStack via the spread options. The presentation state is split into focused\n * hooks (`use-editing-state`, `use-column-filter-modes`, `use-global-filter-mode`,\n * `use-resolved-columns`, `use-page-reset-on-filter-change`); this hook wires\n * them together and assembles the `tableInstance` config.\n */\nexport function useDataTable<TData extends RowData>(\n  options: UseDataTableOptions<TData>\n): DataTableInstance<TData> {\n  const {\n    localization: localizationProp,\n    icons: iconsProp,\n    defaultDensity = \"comfortable\",\n    defaultShowColumnFilters = false,\n    isLoading = false,\n    isSaving = false,\n    showProgressBars: showProgressBarsProp,\n    showSkeletons: showSkeletonsProp,\n    showLoadingOverlay: showLoadingOverlayProp,\n    enableFacetedValues = true,\n    enableColumnActions = true,\n    enableStickyHeader = true,\n    enablePagination: enablePaginationProp = true,\n    enableInfiniteScroll = false,\n    onLoadMore,\n    hasNextPage = false,\n    isFetchingNextPage = false,\n    infiniteScrollThreshold = 200,\n    positionPagination = \"bottom\",\n    paginationDisplayMode = \"default\",\n    columnFilterDisplayMode = \"subheader\",\n    positionGlobalFilter = \"right\",\n    positionToolbarAlertBanner = \"top\",\n    positionToolbarDropZone = \"top\",\n    positionActionsColumn = \"last\",\n    positionExpandColumn = \"first\",\n    selectAllMode = \"page\",\n    enableSelectAll = true,\n    enableTopToolbar = true,\n    enableBottomToolbar = true,\n    enableDensityToggle = true,\n    enableFullscreenToggle = true,\n    enableKeyboardNavigation = true,\n    enableColumnFilterModes = true,\n    enableFilterMatchHighlighting = true,\n    enableGlobalFilter = true,\n    enableGlobalFilterModes = true,\n    enableGlobalFilterRankedResults = false,\n    defaultGlobalFilterMode = \"fuzzy\",\n    density: densityProp,\n    onDensityChange,\n    isFullscreen: isFullscreenProp,\n    onIsFullscreenChange,\n    showColumnFilters: showColumnFiltersProp,\n    onShowColumnFiltersChange,\n    enableAdvancedFilter = false,\n    advancedFilter: advancedFilterProp,\n    defaultAdvancedFilter,\n    onAdvancedFilterChange,\n    globalFilterMode: globalFilterModeProp,\n    onGlobalFilterModeChange,\n    enableColumnOrdering = false,\n    enableColumnPinning = false,\n    enableColumnResizing = false,\n    enableColumnAutosize: enableColumnAutosizeProp,\n    enableRowOrdering = false,\n    enableRowPinning = false,\n    enableRowNumbers = false,\n    rowNumberMode = \"static\",\n    onRowOrderChange,\n    enableGrouping = false,\n    enableExpanding: enableExpandingProp,\n    enableStickyFooter = false,\n    renderDetailPanel,\n    enableEditing = false,\n    editDisplayMode = \"cell\",\n    createDisplayMode = \"modal\",\n    createRowDefaults,\n    enableClickToCopy = false,\n    onEditCellSave,\n    onSaveRow,\n    onCreateRow,\n    renderRowActions,\n    renderCellActionMenuItems,\n    renderRowActionMenuItems,\n    renderColumnActionsMenuItems,\n    renderColumnFilterModeMenuItems,\n    renderGlobalFilterModeMenuItems,\n    renderCaption,\n    onRowClick,\n    onRowDoubleClick,\n    onCellClick,\n    onCellDoubleClick,\n    enableRowVirtualization = false,\n    enableColumnVirtualization = false,\n    estimateRowHeight = 52,\n    virtualOverscan = 8,\n    rowHeight,\n    getRowHeight,\n    rowVirtualizerOptions,\n    columnVirtualizerOptions,\n    rowVirtualizerInstanceRef,\n    columnVirtualizerInstanceRef,\n    enableToolbarInternalActions = true,\n    title,\n    renderToolbarActions,\n    renderTopToolbar,\n    renderBottomToolbar,\n    renderToolbarInternalActions,\n    renderBottomToolbarCustomActions,\n    renderEmpty,\n    columns,\n    ...tableOptions\n  } = options\n\n  // Infinite scroll implies a single growing page: hide the pager and drop the\n  // pagination row model so every appended row renders.\n  const enablePagination = enableInfiniteScroll ? false : enablePaginationProp\n\n  // App-wide defaults from a surrounding DataTableConfigProvider (if any) sit\n  // between the built-in defaults and per-call options.\n  const configCtx = useDataTableConfigContext()\n  const localization = React.useMemo(\n    () => ({\n      ...defaultLocalization,\n      ...configCtx.localization,\n      ...localizationProp,\n    }),\n    [configCtx.localization, localizationProp]\n  )\n  const icons = React.useMemo(\n    () => ({ ...defaultIcons, ...configCtx.icons, ...iconsProp }),\n    [configCtx.icons, iconsProp]\n  )\n\n  // Expansion turns on for tree data (getSubRows), detail panels, or grouping.\n  const enableExpanding =\n    enableExpandingProp ??\n    (!!renderDetailPanel || !!tableOptions.getSubRows || enableGrouping)\n  // An expand column is needed for tree sub-rows or detail panels (grouped\n  // rows carry their own chevron in the grouping cell).\n  const needsExpandColumn = !!renderDetailPanel || !!tableOptions.getSubRows\n\n  // Loading affordances: each can be forced on/off, else derived from the\n  // loading/saving flags (progress bar for either; skeletons + overlay only\n  // for the initial data load).\n  const showProgressBars = showProgressBarsProp ?? (isLoading || isSaving)\n  const showSkeletons = showSkeletonsProp ?? isLoading\n  const showLoadingOverlay = showLoadingOverlayProp ?? isLoading\n\n  const [density, setDensity] = useControllableState<Density>(\n    densityProp,\n    defaultDensity,\n    onDensityChange\n  )\n  const [isFullscreen, setIsFullscreen] = useControllableState(\n    isFullscreenProp,\n    false,\n    onIsFullscreenChange\n  )\n  const [showColumnFilters, setShowColumnFilters] = useControllableState(\n    showColumnFiltersProp,\n    defaultShowColumnFilters,\n    onShowColumnFiltersChange\n  )\n\n  const {\n    editingCell,\n    setEditingCell,\n    editingRowId,\n    isCreating,\n    rowDraft,\n    setRowDraftValue,\n    beginRowEdit,\n    beginCreate,\n    cancelEdit,\n  } = useEditingState<TData>(createRowDefaults)\n\n  const { columnFilterModes, setColumnFilterModes, dynamicFilterFn } =\n    useColumnFilterModes<TData>(columns)\n\n  const isManualFiltering = !!tableOptions.manualFiltering\n\n  const {\n    globalFilterMode,\n    setGlobalFilterMode,\n    dynamicGlobalFilterFn,\n    rankedSortedRowModel,\n  } = useGlobalFilterMode<TData>({\n    globalFilterMode: globalFilterModeProp,\n    defaultGlobalFilterMode,\n    onGlobalFilterModeChange,\n    enableGlobalFilterRankedResults,\n    manualSorting: !!tableOptions.manualSorting,\n    manualFiltering: isManualFiltering,\n    enableGrouping,\n  })\n\n  const {\n    advancedFilter,\n    setAdvancedFilter,\n    showAdvancedFilterPanel,\n    setShowAdvancedFilterPanel,\n    advancedFilteredRowModel,\n  } = useAdvancedFilter<TData>({\n    enableAdvancedFilter,\n    advancedFilter: advancedFilterProp,\n    defaultAdvancedFilter,\n    onAdvancedFilterChange,\n  })\n\n  const enableRowSelection =\n    tableOptions.enableRowSelection != null\n      ? !!tableOptions.enableRowSelection\n      : false\n\n  // Columns with a consumer-provided cell renderer are left untouched by\n  // auto-highlighting (the consumer owns their markup).\n  const columnsWithCustomCell = React.useMemo(() => {\n    const set = new Set<string>()\n    for (const def of columns) {\n      const key = columnKey(def as { id?: string; accessorKey?: unknown })\n      if (key && \"cell\" in def && def.cell != null) set.add(key)\n    }\n    return set\n  }, [columns])\n\n  const resolvedColumns = useResolvedColumns<TData>({\n    columns,\n    enableRowOrdering,\n    enableRowSelection,\n    selectAllMode,\n    enableSelectAll,\n    needsExpandColumn,\n    positionExpandColumn,\n    enableRowNumbers,\n    rowNumberMode,\n    enableRowPinning,\n    renderRowActions,\n    renderRowActionMenuItems,\n    positionActionsColumn,\n    enableEditing,\n    editDisplayMode,\n    localization,\n    icons,\n  })\n\n  // Which header affordances render — shared by the column sizing below, the\n  // autosize measurement, and the header components, so they can't diverge.\n  const headerControlsOptions = React.useMemo<HeaderControlsOptions>(\n    () => ({\n      enableColumnActions,\n      enableColumnOrdering,\n      enableGrouping,\n      enableColumnPinning,\n      enableColumnVirtualization,\n      columnFilterDisplayMode,\n    }),\n    [\n      enableColumnActions,\n      enableColumnOrdering,\n      enableGrouping,\n      enableColumnPinning,\n      enableColumnVirtualization,\n      columnFilterDisplayMode,\n    ]\n  )\n\n  // Size each data column so its header controls are never clipped. Under\n  // `table-layout: fixed` a `<th>` is pinned to `getSize()` and clips overflow,\n  // and CSS `min-width` is ignored — so the drag grip / actions / filter button\n  // (all shrink-0) would be hidden behind a long label or a small size.\n  //  • minSize — a hard floor (controls + a sliver of label) that raises\n  //    `getSize()` and stops the resize drag; the label truncates past it.\n  //  • size — the at-rest default, widened to fit the full header so nothing is\n  //    truncated until the user drags narrower; only when no size was pinned.\n  // Done here as a pure transform (not a post-build mutation) so React Compiler\n  // keeps it and the SSR and client size vars match — a mutation in a memo/effect\n  // is dead-code-eliminated on the client and desyncs hydration.\n  const baseColumnSize = tableOptions.defaultColumn?.size ?? 150\n  const sizedColumns = React.useMemo(\n    () =>\n      resolvedColumns.map((def) => {\n        if (!isDataColumnDef(def)) return def\n        const minSize = Math.max(\n          def.minSize ?? 0,\n          getColumnDefMinWidth(def, headerControlsOptions)\n        )\n        const size =\n          def.size != null\n            ? def.size\n            : Math.max(\n                baseColumnSize,\n                getColumnDefPreferredWidth(def, headerControlsOptions)\n              )\n        return { ...def, minSize, size }\n      }),\n    [resolvedColumns, headerControlsOptions, baseColumnSize]\n  )\n\n  // The React Compiler bails on TanStack Table's mutable instance; expected.\n  // eslint-disable-next-line react-hooks/incompatible-library\n  const table = useReactTable<TData>({\n    ...tableOptions,\n    columns: sizedColumns,\n    // Default page-reset off: TanStack's auto-reset runs a state update during\n    // render (warns in React 19 dev). We reset on filter changes via an effect\n    // below instead. Consumers can re-enable by passing the option explicitly.\n    autoResetPageIndex: tableOptions.autoResetPageIndex ?? false,\n    defaultColumn: {\n      filterFn: dynamicFilterFn,\n      ...tableOptions.defaultColumn,\n    },\n    enableGlobalFilter,\n    globalFilterFn: tableOptions.globalFilterFn ?? dynamicGlobalFilterFn,\n    enableColumnPinning,\n    enableColumnResizing,\n    columnResizeMode: tableOptions.columnResizeMode ?? \"onChange\",\n    enableRowPinning,\n    keepPinnedRows: tableOptions.keepPinnedRows ?? true,\n    enableGrouping,\n    enableExpanding,\n    // Detail panels expand arbitrary rows; tree data uses getSubRows' own logic.\n    getRowCanExpand:\n      tableOptions.getRowCanExpand ??\n      (renderDetailPanel ? () => true : undefined),\n    getGroupedRowModel: enableGrouping\n      ? (tableOptions.getGroupedRowModel ?? getGroupedRowModel())\n      : tableOptions.getGroupedRowModel,\n    getExpandedRowModel: enableExpanding\n      ? (tableOptions.getExpandedRowModel ?? getExpandedRowModel())\n      : tableOptions.getExpandedRowModel,\n    getCoreRowModel: tableOptions.getCoreRowModel ?? getCoreRowModel(),\n    getSortedRowModel: tableOptions.manualSorting\n      ? tableOptions.getSortedRowModel\n      : (tableOptions.getSortedRowModel ?? rankedSortedRowModel),\n    getFilteredRowModel: isManualFiltering\n      ? tableOptions.getFilteredRowModel\n      : (tableOptions.getFilteredRowModel ??\n        (enableAdvancedFilter\n          ? advancedFilteredRowModel\n          : getFilteredRowModel())),\n    // Client-side faceting powers select/multi-select option lists + counts and\n    // range-slider bounds. Skipped in manual mode (server supplies facets) or\n    // when `enableFacetedValues` is off.\n    getFacetedRowModel:\n      isManualFiltering || !enableFacetedValues\n        ? tableOptions.getFacetedRowModel\n        : (tableOptions.getFacetedRowModel ?? getFacetedRowModel()),\n    getFacetedUniqueValues:\n      isManualFiltering || !enableFacetedValues\n        ? tableOptions.getFacetedUniqueValues\n        : (tableOptions.getFacetedUniqueValues ?? getFacetedUniqueValues()),\n    getFacetedMinMaxValues:\n      isManualFiltering || !enableFacetedValues\n        ? tableOptions.getFacetedMinMaxValues\n        : (tableOptions.getFacetedMinMaxValues ?? getFacetedMinMaxValues()),\n    getPaginationRowModel:\n      !enablePagination || tableOptions.manualPagination\n        ? tableOptions.getPaginationRowModel\n        : (tableOptions.getPaginationRowModel ?? getPaginationRowModel()),\n  }) as DataTableInstance<TData>\n\n  const enableColumnFilters = tableOptions.enableColumnFilters !== false\n\n  usePageResetOnFilterChange(table, {\n    enablePagination,\n    manualPagination: tableOptions.manualPagination,\n    autoResetPageIndex: tableOptions.autoResetPageIndex,\n  })\n\n  // Advanced filter edits can shrink the result set; jump back to the first\n  // page so the user isn't stranded on an out-of-range page (mirrors the\n  // column-filter reset above).\n  const advancedFilterResetRef = React.useRef(advancedFilter)\n  React.useEffect(() => {\n    if (advancedFilterResetRef.current === advancedFilter) return\n    advancedFilterResetRef.current = advancedFilter\n    if (\n      enableAdvancedFilter &&\n      enablePagination &&\n      !tableOptions.manualPagination\n    ) {\n      table.setPageIndex(0)\n    }\n  }, [\n    advancedFilter,\n    enableAdvancedFilter,\n    enablePagination,\n    tableOptions.manualPagination,\n    table,\n  ])\n\n  // Switching a column's mode resets its value so a stale value (e.g. a\n  // numeric range left over from \"between\") can't break the new mode. Valueless\n  // modes (empty/notEmpty) get a truthy sentinel so they stay active. Lives here\n  // (not in useColumnFilterModes) because it needs the table instance.\n  const setColumnFilterMode = React.useCallback(\n    (columnId: string, mode: FilterMode) => {\n      setColumnFilterModes((prev) => ({ ...prev, [columnId]: mode }))\n      const column = table.getColumn(columnId)\n      if (!column) return\n      column.setFilterValue(VALUELESS_MODES.has(mode) ? mode : undefined)\n    },\n    [table, setColumnFilterModes]\n  )\n\n  // Structural DOM refs, exposed on `table.tableInstance.refs` and attached to the\n  // corresponding elements in DataTable / toolbar / global-filter.\n  const tablePaperRef = React.useRef<HTMLDivElement>(null)\n  const tableContainerRef = React.useRef<HTMLDivElement>(null)\n  const topToolbarRef = React.useRef<HTMLDivElement>(null)\n  const bottomToolbarRef = React.useRef<HTMLDivElement>(null)\n  const tableHeadRef = React.useRef<HTMLTableSectionElement>(null)\n  const tableFooterRef = React.useRef<HTMLTableSectionElement>(null)\n  const searchInputRef = React.useRef<HTMLInputElement>(null)\n\n  // Double-clicking a resize handle (or calling this imperatively) fits a column\n  // to its widest value. Defaults on when resizing is enabled.\n  const enableColumnAutosize = enableColumnAutosizeProp ?? enableColumnResizing\n\n  const autoSizeColumn = React.useCallback(\n    (columnId: string) => {\n      const column = table.getColumn(columnId)\n      if (!column || !column.getCanResize()) return\n\n      // Font + horizontal padding come from a real rendered cell so measurement\n      // matches the actual type scale and density; fall back to sane defaults.\n      const containerEl = tableContainerRef.current\n      const sampleCell =\n        containerEl?.querySelector<HTMLElement>(\"tbody td\") ??\n        containerEl?.querySelector<HTMLElement>(\"thead th\") ??\n        null\n      let font = \"14px sans-serif\"\n      let padding = 24\n      if (sampleCell) {\n        const cs = getComputedStyle(sampleCell)\n        if (cs.font) font = cs.font\n        padding = parseFloat(cs.paddingLeft) + parseFloat(cs.paddingRight)\n      }\n\n      // Measure the full dataset (not just virtualized-visible rows) from raw\n      // values. Headers render uppercase, so match that for measurement.\n      const values = table.getRowModel().rows.map((row) => {\n        const value = row.getValue(columnId)\n        return value == null ? \"\" : String(value)\n      })\n      const headerText = getColumnLabel(column).toUpperCase()\n\n      // Reserve room beside the label for the header affordances that actually\n      // render (sort indicator, drag grip, column-actions trigger, popover\n      // filter button). Shared with the per-column minSize floor below so\n      // measurement and the floor can never disagree — see helpers/header-controls.\n      const extraWidth = getHeaderControlsWidth(column, headerControlsOptions)\n\n      const width = measureColumnWidth(values, headerText, {\n        font,\n        padding,\n        extraWidth,\n      })\n\n      // Respect any per-column size bounds from the column def.\n      const { minSize, maxSize } = column.columnDef\n      let clamped = width\n      if (typeof minSize === \"number\") clamped = Math.max(clamped, minSize)\n      if (typeof maxSize === \"number\") clamped = Math.min(clamped, maxSize)\n\n      table.setColumnSizing((prev) => ({ ...prev, [columnId]: clamped }))\n    },\n    [table, headerControlsOptions]\n  )\n\n  const autoSizeAllColumns = React.useCallback(() => {\n    for (const column of table.getVisibleLeafColumns()) {\n      if (column.getCanResize()) autoSizeColumn(column.id)\n    }\n  }, [table, autoSizeColumn])\n\n  const config: DataTableConfig<TData> = {\n    localization,\n    icons,\n    refs: {\n      tablePaperRef,\n      tableContainerRef,\n      topToolbarRef,\n      bottomToolbarRef,\n      tableHeadRef,\n      tableFooterRef,\n      searchInputRef,\n    },\n    density,\n    setDensity,\n    isFullscreen,\n    setIsFullscreen,\n    showColumnFilters,\n    setShowColumnFilters,\n    columnFilterModes,\n    setColumnFilterMode,\n    globalFilterMode,\n    setGlobalFilterMode,\n    enableGlobalFilter,\n    enableGlobalFilterModes,\n    enableAdvancedFilter,\n    advancedFilter,\n    setAdvancedFilter,\n    showAdvancedFilterPanel,\n    setShowAdvancedFilterPanel,\n    isLoading,\n    isSaving,\n    showProgressBars,\n    showSkeletons,\n    showLoadingOverlay,\n    enableFacetedValues,\n    enableColumnActions,\n    enableColumnFilters,\n    enableColumnFilterModes,\n    enableFilterMatchHighlighting,\n    columnsWithCustomCell,\n    enableColumnOrdering,\n    enableColumnPinning,\n    enableColumnResizing,\n    enableColumnAutosize,\n    autoSizeColumn,\n    autoSizeAllColumns,\n    enableRowOrdering,\n    enableRowPinning,\n    enableRowNumbers,\n    rowNumberMode,\n    onRowOrderChange,\n    enableGrouping,\n    enableExpanding,\n    enableStickyFooter,\n    renderDetailPanel,\n    enableEditing,\n    editDisplayMode,\n    createDisplayMode,\n    editingCell,\n    setEditingCell,\n    editingRowId,\n    isCreating,\n    rowDraft,\n    setRowDraftValue,\n    beginRowEdit,\n    beginCreate,\n    cancelEdit,\n    enableClickToCopy,\n    onEditCellSave,\n    onSaveRow,\n    onCreateRow,\n    renderRowActions,\n    renderCellActionMenuItems,\n    onRowClick,\n    onRowDoubleClick,\n    onCellClick,\n    onCellDoubleClick,\n    enableRowVirtualization,\n    enableColumnVirtualization,\n    estimateRowHeight,\n    virtualOverscan,\n    rowHeight,\n    getRowHeight,\n    rowVirtualizerOptions,\n    columnVirtualizerOptions,\n    rowVirtualizerInstanceRef,\n    columnVirtualizerInstanceRef,\n    enableStickyHeader,\n    enablePagination,\n    enableInfiniteScroll,\n    onLoadMore,\n    hasNextPage,\n    isFetchingNextPage,\n    infiniteScrollThreshold,\n    positionPagination,\n    paginationDisplayMode,\n    columnFilterDisplayMode,\n    positionGlobalFilter,\n    positionToolbarAlertBanner,\n    positionToolbarDropZone,\n    enableRowSelection,\n    enableTopToolbar,\n    enableBottomToolbar,\n    enableDensityToggle,\n    enableFullscreenToggle,\n    enableToolbarInternalActions,\n    enableKeyboardNavigation,\n    title,\n    renderToolbarActions,\n    renderTopToolbar,\n    renderBottomToolbar,\n    renderToolbarInternalActions,\n    renderBottomToolbarCustomActions,\n    renderRowActionMenuItems,\n    renderColumnActionsMenuItems,\n    renderColumnFilterModeMenuItems,\n    renderGlobalFilterModeMenuItems,\n    renderCaption,\n    renderEmpty,\n  }\n\n  table.tableInstance = config\n\n  return table\n}\n"
    },
    {
      "path": "ui/data-table/fns/advanced-filter.ts",
      "type": "registry:ui",
      "target": "components/ui/data-table/fns/advanced-filter.ts",
      "content": "import {\n  getFilteredRowModel,\n  type Row,\n  type RowData,\n  type RowModel,\n  type Table,\n} from \"@tanstack/react-table\"\n\nimport type {\n  AdvancedFilterGroup,\n  AdvancedFilterOperator,\n  AdvancedFilterRule,\n  FilterVariant,\n} from \"../core/types\"\n\n/** A value that contributes nothing to a filter (no cell value / no input). */\nfunction isEmptyValue(value: unknown): boolean {\n  return (\n    value == null ||\n    value === \"\" ||\n    (Array.isArray(value) && value.length === 0)\n  )\n}\n\n/** Parse a number from a value, or null if it isn't numeric. */\nfunction asNumber(value: unknown): number | null {\n  if (typeof value === \"number\") return Number.isNaN(value) ? null : value\n  if (typeof value === \"string\" && value.trim() !== \"\") {\n    const n = Number(value)\n    return Number.isNaN(n) ? null : n\n  }\n  return null\n}\n\n/** Parse an epoch time from a Date or date-ish string, or null. */\nfunction asTime(value: unknown): number | null {\n  if (value instanceof Date) return value.getTime()\n  if (typeof value === \"string\" && value.trim() !== \"\") {\n    const t = Date.parse(value)\n    return Number.isNaN(t) ? null : t\n  }\n  return null\n}\n\n/** Order two cell-ish values: numeric first, then date, then string. */\nfunction compareValues(a: unknown, b: unknown): number {\n  const na = asNumber(a)\n  const nb = asNumber(b)\n  if (na != null && nb != null) return na - nb\n  const ta = asTime(a)\n  const tb = asTime(b)\n  if (ta != null && tb != null) return ta - tb\n  return String(a).localeCompare(String(b))\n}\n\n/** Loose equality: numeric when both sides parse as numbers, else case- and\n *  whitespace-insensitive string compare (so \"Active\" matches a \"active\" rule). */\nfunction looseEquals(cell: unknown, value: unknown): boolean {\n  const nc = asNumber(cell)\n  const nv = asNumber(value)\n  if (nc != null && nv != null) return nc === nv\n  if (typeof cell === \"boolean\" || typeof value === \"boolean\") {\n    return String(cell) === String(value)\n  }\n  return (\n    String(cell).trim().toLowerCase() === String(value).trim().toLowerCase()\n  )\n}\n\nconst VALUELESS_OPERATORS: ReadonlySet<AdvancedFilterOperator> = new Set([\n  \"isEmpty\",\n  \"isNotEmpty\",\n])\n\n/**\n * Evaluate one operator against a cell value. Rules whose value input is\n * incomplete are treated as inactive (return true) so a half-typed rule doesn't\n * blank the table. An empty cell only matches `isEmpty` / `notEquals` /\n * `notContains`; every other operator needs a value to compare against.\n */\nexport function applyOperator(\n  cellValue: unknown,\n  operator: AdvancedFilterOperator,\n  value: unknown,\n  value2: unknown\n): boolean {\n  if (operator === \"isEmpty\") return isEmptyValue(cellValue)\n  if (operator === \"isNotEmpty\") return !isEmptyValue(cellValue)\n\n  const cellEmpty = isEmptyValue(cellValue)\n\n  if (operator === \"between\") {\n    if (isEmptyValue(value) || isEmptyValue(value2)) return true\n    if (cellEmpty) return false\n    const lo = compareValues(cellValue, value)\n    const hi = compareValues(cellValue, value2)\n    return lo >= 0 && hi <= 0\n  }\n\n  // A value-requiring operator with no value is inactive.\n  if (isEmptyValue(value)) return true\n\n  if (operator === \"notEquals\")\n    return cellEmpty || !looseEquals(cellValue, value)\n  if (operator === \"notContains\") {\n    return (\n      cellEmpty ||\n      !String(cellValue).toLowerCase().includes(String(value).toLowerCase())\n    )\n  }\n\n  // Remaining operators can't match an empty cell.\n  if (cellEmpty) return false\n\n  switch (operator) {\n    case \"equals\":\n      return looseEquals(cellValue, value)\n    case \"contains\":\n      return String(cellValue)\n        .toLowerCase()\n        .includes(String(value).toLowerCase())\n    case \"startsWith\":\n      return String(cellValue)\n        .toLowerCase()\n        .startsWith(String(value).toLowerCase())\n    case \"endsWith\":\n      return String(cellValue)\n        .toLowerCase()\n        .endsWith(String(value).toLowerCase())\n    case \"greaterThan\":\n      return compareValues(cellValue, value) > 0\n    case \"greaterThanOrEqual\":\n      return compareValues(cellValue, value) >= 0\n    case \"lessThan\":\n      return compareValues(cellValue, value) < 0\n    case \"lessThanOrEqual\":\n      return compareValues(cellValue, value) <= 0\n    default:\n      return true\n  }\n}\n\n/** Evaluate a single rule against a row. */\nexport function evaluateRule<TData extends RowData>(\n  row: Row<TData>,\n  rule: AdvancedFilterRule\n): boolean {\n  return applyOperator(\n    row.getValue(rule.columnId),\n    rule.operator,\n    rule.value,\n    rule.value2\n  )\n}\n\n/** Evaluate the whole group against a row (AND/OR over its rules). */\nexport function evaluateAdvancedFilterGroup<TData extends RowData>(\n  row: Row<TData>,\n  group: AdvancedFilterGroup\n): boolean {\n  if (group.rules.length === 0) return true\n  return group.logic === \"and\"\n    ? group.rules.every((rule) => evaluateRule(row, rule))\n    : group.rules.some((rule) => evaluateRule(row, rule))\n}\n\n/** Operators offered for a column, by its filter variant. */\nexport function getOperatorsForVariant(\n  variant: FilterVariant\n): AdvancedFilterOperator[] {\n  switch (variant) {\n    case \"select\":\n    case \"multi-select\":\n      return [\"equals\", \"notEquals\", \"isEmpty\", \"isNotEmpty\"]\n    case \"range\":\n    case \"range-slider\":\n      return [\n        \"equals\",\n        \"notEquals\",\n        \"greaterThan\",\n        \"greaterThanOrEqual\",\n        \"lessThan\",\n        \"lessThanOrEqual\",\n        \"between\",\n        \"isEmpty\",\n        \"isNotEmpty\",\n      ]\n    case \"date\":\n    case \"date-range\":\n      return [\n        \"equals\",\n        \"notEquals\",\n        \"greaterThan\",\n        \"lessThan\",\n        \"between\",\n        \"isEmpty\",\n        \"isNotEmpty\",\n      ]\n    case \"checkbox\":\n      return [\"equals\"]\n    default:\n      return [\n        \"contains\",\n        \"notContains\",\n        \"startsWith\",\n        \"endsWith\",\n        \"equals\",\n        \"notEquals\",\n        \"isEmpty\",\n        \"isNotEmpty\",\n      ]\n  }\n}\n\n/** Whether an operator hides its value input(s). */\nexport function isValuelessOperator(operator: AdvancedFilterOperator): boolean {\n  return VALUELESS_OPERATORS.has(operator)\n}\n\n/** Rebuild a row model from a filtered set of top-level rows, recomputing the\n *  flat list + id map from the kept rows and their descendants. */\nfunction filterRowModel<TData extends RowData>(\n  model: RowModel<TData>,\n  predicate: (row: Row<TData>) => boolean\n): RowModel<TData> {\n  const rows = model.rows.filter(predicate)\n  const flatRows: Row<TData>[] = []\n  const rowsById: Record<string, Row<TData>> = {}\n  const collect = (rs: Row<TData>[]) => {\n    for (const row of rs) {\n      flatRows.push(row)\n      rowsById[row.id] = row\n      if (row.subRows.length) collect(row.subRows)\n    }\n  }\n  collect(rows)\n  return { rows, flatRows, rowsById }\n}\n\n/**\n * Wraps TanStack's filtered row model and additionally applies the advanced\n * filter group on top, so both filter systems are active simultaneously. The\n * group is read through `getGroup` at evaluation time (the factory keeps a\n * stable identity to preserve memoization), and results are cached per\n * underlying model + group so they only recompute when something changes.\n */\nexport function createAdvancedFilteredRowModel<TData extends RowData>(\n  getGroup: () => AdvancedFilterGroup\n): (table: Table<TData>) => () => RowModel<TData> {\n  const base = getFilteredRowModel<TData>()\n  return (table) => {\n    const delegate = base(table)\n    let cachedFor: RowModel<TData> | null = null\n    let cachedGroup: AdvancedFilterGroup | null = null\n    let cached: RowModel<TData> | null = null\n    return () => {\n      const model = delegate()\n      const group = getGroup()\n      if (group.rules.length === 0) return model\n      if (cached && cachedFor === model && cachedGroup === group) return cached\n      cached = filterRowModel(model, (row) =>\n        evaluateAdvancedFilterGroup(row, group)\n      )\n      cachedFor = model\n      cachedGroup = group\n      return cached\n    }\n  }\n}\n"
    },
    {
      "path": "ui/data-table/fns/filter-factories.ts",
      "type": "registry:ui",
      "target": "components/ui/data-table/fns/filter-factories.ts",
      "content": "import type { FilterFn, RowData } from \"@tanstack/react-table\"\nimport { rankItem } from \"@tanstack/match-sorter-utils\"\n\nimport {\n  MODE_FNS,\n  isInactive,\n  type FilterMode,\n  type GlobalFilterMode,\n} from \"./filter-modes\"\n\n/**\n * Single dynamic filter function assigned to every column via `defaultColumn`.\n * It reads the column's current mode from the provided lookup and dispatches.\n * Mode state lives in the hook; this closure reads it through `getMode` (a ref\n * getter) so the function identity stays stable across renders.\n */\nexport function createDynamicFilterFn<TData extends RowData>(\n  getMode: (columnId: string) => FilterMode\n): FilterFn<TData> {\n  const fn: FilterFn<TData> = (row, columnId, filterValue) => {\n    const mode = getMode(columnId)\n    const modeFn = MODE_FNS[mode] ?? MODE_FNS.contains\n    return modeFn(row.getValue(columnId), filterValue)\n  }\n  // Keep valueless modes active even with a blank value; otherwise drop blanks.\n  fn.autoRemove = (value) => isInactive(value)\n  return fn\n}\n\n/**\n * Mode-aware global search. `fuzzy` uses match-sorter's `rankItem` (MRT's own\n * choice); other modes reuse the column mode functions. Matches across every\n * searchable column (TanStack runs this per column and ORs the results).\n */\nexport function createGlobalFilterFn<TData extends RowData>(\n  getMode: () => GlobalFilterMode\n): FilterFn<TData> {\n  const fn: FilterFn<TData> = (row, columnId, filterValue, addMeta) => {\n    const value = String(filterValue ?? \"\")\n    if (value === \"\") return true\n    const mode = getMode()\n    if (mode === \"fuzzy\") {\n      // Stash the rank so `enableGlobalFilterRankedResults` can order rows by\n      // match quality; `rankGlobalFuzzy` reads it back off columnFiltersMeta.\n      const itemRank = rankItem(row.getValue(columnId), value)\n      addMeta(itemRank)\n      return itemRank.passed\n    }\n    const modeFn = MODE_FNS[mode] ?? MODE_FNS.contains\n    return modeFn(row.getValue(columnId), value)\n  }\n  fn.autoRemove = (value) => value == null || value === \"\"\n  return fn\n}\n"
    },
    {
      "path": "ui/data-table/fns/filter-fns.ts",
      "type": "registry:ui",
      "target": "components/ui/data-table/fns/filter-fns.ts",
      "content": "// Barrel for the filtering engine. Internal modules:\n//   filter-modes.ts     — mode predicates (MODE_FNS), value coercers, mode sets\n//   filter-factories.ts — the per-column / global dynamic FilterFn factories\n//   ranked-row-model.ts — fuzzy-rank sorted row model\n//   variant-modes.ts    — variant → default mode + mode-menu options\n// Most of the module imports from here (\"../fns/filter-fns\"); keep this surface\n// stable when adding to the sub-modules.\n\nexport {\n  MODE_FNS,\n  VALUELESS_MODES,\n  SUBSTRING_MODES,\n  isInactive,\n  type FilterMode,\n  type GlobalFilterMode,\n} from \"./filter-modes\"\nexport { createDynamicFilterFn, createGlobalFilterFn } from \"./filter-factories\"\nexport { rankGlobalFuzzy, createRankedSortedRowModel } from \"./ranked-row-model\"\nexport { defaultModeForVariant, modeOptionsForVariant } from \"./variant-modes\"\n"
    },
    {
      "path": "ui/data-table/fns/filter-modes.ts",
      "type": "registry:ui",
      "target": "components/ui/data-table/fns/filter-modes.ts",
      "content": "/** Global-search modes offered in the global filter-mode menu. */\nexport type GlobalFilterMode =\n  \"fuzzy\" | \"contains\" | \"startsWith\" | \"endsWith\" | \"equals\"\n\n/** All supported filter modes across variants. */\nexport type FilterMode =\n  // text\n  | \"contains\"\n  | \"equals\"\n  | \"notEquals\"\n  | \"startsWith\"\n  | \"endsWith\"\n  | \"fuzzy\"\n  // shared\n  | \"empty\"\n  | \"notEmpty\"\n  // numeric\n  | \"between\"\n  | \"betweenInclusive\"\n  | \"greaterThan\"\n  | \"greaterThanOrEqualTo\"\n  | \"lessThan\"\n  | \"lessThanOrEqualTo\"\n  // date\n  | \"before\"\n  | \"after\"\n  | \"betweenDates\"\n  // fixed (no mode menu)\n  | \"equalsString\"\n  | \"arrIncludesSome\"\n  | \"equalsBool\"\n\nimport { isAfter, isBefore, startOfDay } from \"date-fns\"\n\nconst str = (v: unknown): string => (v == null ? \"\" : String(v))\nconst lower = (v: unknown): string => str(v).toLowerCase()\nconst num = (v: unknown): number =>\n  typeof v === \"number\" ? v : parseFloat(str(v))\nconst isBlank = (v: unknown): boolean =>\n  v == null || v === \"\" || (Array.isArray(v) && v.length === 0)\nconst toDate = (v: unknown): Date | null => {\n  if (v == null || v === \"\") return null\n  const d = v instanceof Date ? v : new Date(v as string)\n  return Number.isNaN(d.getTime()) ? null : d\n}\n\n// Each entry: (row value already resolved) decides inclusion. The dynamic\n// `filterFn` resolves `row.getValue(columnId)` and dispatches by mode.\ntype ModeFn = (cellValue: unknown, filterValue: unknown) => boolean\n\nexport const MODE_FNS: Record<FilterMode, ModeFn> = {\n  contains: (cell, val) => lower(cell).includes(lower(val)),\n  equals: (cell, val) => lower(cell) === lower(val),\n  notEquals: (cell, val) => lower(cell) !== lower(val),\n  startsWith: (cell, val) => lower(cell).startsWith(lower(val)),\n  endsWith: (cell, val) => lower(cell).endsWith(lower(val)),\n  fuzzy: (cell, val) => lower(cell).includes(lower(val)),\n\n  empty: (cell) => isBlank(cell),\n  notEmpty: (cell) => !isBlank(cell),\n\n  greaterThan: (cell, val) => num(cell) > num(val),\n  greaterThanOrEqualTo: (cell, val) => num(cell) >= num(val),\n  lessThan: (cell, val) => num(cell) < num(val),\n  lessThanOrEqualTo: (cell, val) => num(cell) <= num(val),\n  between: (cell, val) => betweenNum(cell, val, false),\n  betweenInclusive: (cell, val) => betweenNum(cell, val, true),\n\n  before: (cell, val) => {\n    const c = toDate(cell)\n    const v = toDate(val)\n    return c != null && v != null && isBefore(c, startOfDay(v))\n  },\n  after: (cell, val) => {\n    const c = toDate(cell)\n    const v = toDate(val)\n    return c != null && v != null && isAfter(c, startOfDay(v))\n  },\n  betweenDates: (cell, val) => {\n    const c = toDate(cell)\n    const range = (Array.isArray(val) ? val : []) as unknown[]\n    const from = toDate(range[0])\n    const to = toDate(range[1])\n    if (c == null) return false\n    if (from != null && isBefore(c, startOfDay(from))) return false\n    if (to != null && isAfter(c, startOfDay(to))) return false\n    return from != null || to != null\n  },\n\n  equalsString: (cell, val) => str(cell) === str(val),\n  arrIncludesSome: (cell, val) => {\n    const arr = (Array.isArray(val) ? val : []) as unknown[]\n    if (arr.length === 0) return true\n    return arr.map(str).includes(str(cell))\n  },\n  equalsBool: (cell, val) => Boolean(cell) === Boolean(val),\n}\n\nfunction betweenNum(cell: unknown, val: unknown, inclusive: boolean): boolean {\n  const arr = (Array.isArray(val) ? val : []) as unknown[]\n  const n = num(cell)\n  if (Number.isNaN(n)) return false\n  const min = arr[0] === \"\" || arr[0] == null ? null : num(arr[0])\n  const max = arr[1] === \"\" || arr[1] == null ? null : num(arr[1])\n  if (min != null && (inclusive ? n < min : n <= min)) return false\n  if (max != null && (inclusive ? n > max : n >= max)) return false\n  return min != null || max != null\n}\n\n/** Modes that activate without a typed value (they test the cell only). */\nexport const VALUELESS_MODES: ReadonlySet<FilterMode> = new Set([\n  \"empty\",\n  \"notEmpty\",\n])\n\n/** Modes whose string value should drive match highlighting in cells. */\nexport const SUBSTRING_MODES: ReadonlySet<FilterMode> = new Set([\n  \"contains\",\n  \"startsWith\",\n  \"endsWith\",\n  \"equals\",\n  \"fuzzy\",\n])\n\n/**\n * A value is \"inactive\" (filter should auto-remove) when blank — unless the\n * mode is valueless, where any truthy sentinel keeps it active.\n */\nexport function isInactive(value: unknown): boolean {\n  if (Array.isArray(value)) return value.every((v) => v == null || v === \"\")\n  return value == null || value === \"\"\n}\n"
    },
    {
      "path": "ui/data-table/fns/ranked-row-model.ts",
      "type": "registry:ui",
      "target": "components/ui/data-table/fns/ranked-row-model.ts",
      "content": "import {\n  getSortedRowModel,\n  type Row,\n  type RowData,\n  type RowModel,\n  type Table,\n} from \"@tanstack/react-table\"\nimport type { RankingInfo } from \"@tanstack/match-sorter-utils\"\n\n/**\n * Descending comparator over the best fuzzy rank a row earned across the\n * columns the global search ran on. Drives `enableGlobalFilterRankedResults`.\n * Ranks are written to `row.columnFiltersMeta` by the fuzzy branch of\n * `createGlobalFilterFn`; only that branch writes meta, so this never picks up\n * per-column filter state.\n */\nexport function rankGlobalFuzzy<TData extends RowData>(\n  rowA: Row<TData>,\n  rowB: Row<TData>\n): number {\n  const best = (row: Row<TData>): number => {\n    let max = -Infinity\n    for (const meta of Object.values(row.columnFiltersMeta)) {\n      const rank = (meta as RankingInfo | undefined)?.rank\n      if (rank != null && rank > max) max = rank\n    }\n    return max === -Infinity ? 0 : max\n  }\n  return best(rowB) - best(rowA)\n}\n\n/**\n * Wraps TanStack's sorted row model. When `isActive(table)` is true (the caller\n * decides: fuzzy global search on, no user sort, no grouping/expansion), the\n * top-level rows are re-ordered by best fuzzy rank; otherwise the normal sorted\n * model passes through untouched. Re-ordering at the sorted-model layer means\n * pagination, pinning, and the body all observe the ranked order with no extra\n * wiring. The result is cached per underlying model object, so the sort only\n * re-runs when filtering/sorting/data actually change.\n */\nexport function createRankedSortedRowModel<TData extends RowData>(\n  isActive: (table: Table<TData>) => boolean\n): (table: Table<TData>) => () => RowModel<TData> {\n  const base = getSortedRowModel<TData>()\n  return (table) => {\n    const delegate = base(table)\n    let cachedFor: RowModel<TData> | null = null\n    let cached: RowModel<TData> | null = null\n    return () => {\n      const model = delegate()\n      if (!isActive(table)) return model\n      if (cached && cachedFor === model) return cached\n      cached = { ...model, rows: [...model.rows].sort(rankGlobalFuzzy) }\n      cachedFor = model\n      return cached\n    }\n  }\n}\n"
    },
    {
      "path": "ui/data-table/fns/variant-modes.ts",
      "type": "registry:ui",
      "target": "components/ui/data-table/fns/variant-modes.ts",
      "content": "import type { FilterVariant } from \"../core/types\"\nimport type { FilterMode } from \"./filter-modes\"\n\n/** Default mode for a variant when the consumer hasn't chosen one. */\nexport function defaultModeForVariant(variant: FilterVariant): FilterMode {\n  switch (variant) {\n    case \"range\":\n    case \"range-slider\":\n      return \"between\"\n    case \"date\":\n      return \"equals\"\n    case \"date-range\":\n      return \"betweenDates\"\n    case \"select\":\n      return \"equalsString\"\n    case \"multi-select\":\n      return \"arrIncludesSome\"\n    case \"checkbox\":\n      return \"equalsBool\"\n    default:\n      return \"contains\"\n  }\n}\n\n/** Modes offered in the mode menu per variant. Empty array → no mode menu. */\nexport function modeOptionsForVariant(variant: FilterVariant): FilterMode[] {\n  switch (variant) {\n    case \"text\":\n      return [\n        \"fuzzy\",\n        \"contains\",\n        \"startsWith\",\n        \"endsWith\",\n        \"equals\",\n        \"notEquals\",\n        \"empty\",\n        \"notEmpty\",\n      ]\n    case \"range\":\n    case \"range-slider\":\n      return [\n        \"between\",\n        \"betweenInclusive\",\n        \"equals\",\n        \"notEquals\",\n        \"greaterThan\",\n        \"greaterThanOrEqualTo\",\n        \"lessThan\",\n        \"lessThanOrEqualTo\",\n        \"empty\",\n        \"notEmpty\",\n      ]\n    case \"date\":\n    case \"date-range\":\n      return [\n        \"equals\",\n        \"notEquals\",\n        \"before\",\n        \"after\",\n        \"betweenDates\",\n        \"empty\",\n        \"notEmpty\",\n      ]\n    default:\n      // select / multi-select / checkbox have a single fixed mode\n      return []\n  }\n}\n"
    },
    {
      "path": "ui/data-table/helpers/column-key.ts",
      "type": "registry:ui",
      "target": "components/ui/data-table/helpers/column-key.ts",
      "content": "/** Best-effort column id used to key per-column state (e.g. filter modes). */\nexport function columnKey(def: {\n  id?: string\n  accessorKey?: unknown\n}): string | null {\n  if (def.id) return def.id\n  if (typeof def.accessorKey === \"string\") return def.accessorKey\n  return null\n}\n"
    },
    {
      "path": "ui/data-table/helpers/column-label.ts",
      "type": "registry:ui",
      "target": "components/ui/data-table/helpers/column-label.ts",
      "content": "import type { Column, RowData } from \"@tanstack/react-table\"\n\n/** Best-effort human label for a column: explicit meta.label, else its\n *  string header, else its id. */\nexport function getColumnLabel<TData extends RowData, TValue>(\n  column: Column<TData, TValue>\n): string {\n  const meta = column.columnDef.meta\n  if (meta?.label) return meta.label\n  const header = column.columnDef.header\n  if (typeof header === \"string\" && header.length > 0) return header\n  return column.id\n}\n"
    },
    {
      "path": "ui/data-table/helpers/effective-filter-mode.ts",
      "type": "registry:ui",
      "target": "components/ui/data-table/helpers/effective-filter-mode.ts",
      "content": "import type { Column, RowData } from \"@tanstack/react-table\"\n\nimport type { DataTableInstance } from \"../core/types\"\nimport { defaultModeForVariant, type FilterMode } from \"../fns/filter-fns\"\n\n/** Effective filter mode for a column: explicit selection → meta → variant default. */\nexport function getEffectiveMode<TData extends RowData, TValue>(\n  column: Column<TData, TValue>,\n  table: DataTableInstance<TData>\n): FilterMode {\n  const variant = column.columnDef.meta?.variant ?? \"text\"\n  return (\n    table.tableInstance.columnFilterModes[column.id] ??\n    column.columnDef.meta?.filterMode ??\n    defaultModeForVariant(variant)\n  )\n}\n"
    },
    {
      "path": "ui/data-table/helpers/header-controls.ts",
      "type": "registry:ui",
      "target": "components/ui/data-table/helpers/header-controls.ts",
      "content": "import type { Column, ColumnDef, RowData } from \"@tanstack/react-table\"\n\nimport { DISPLAY_COLUMN_IDS } from \"../core/constants\"\nimport type { ColumnFilterDisplayMode, DataTableInstance } from \"../core/types\"\n\n/**\n * Feature flags that decide which affordances a column header renders. Single\n * source of truth so the autosize measurement, the per-column minimum width,\n * and the header components themselves can never disagree about what shows (the\n * disagreement that let controls get clipped under the fixed table layout).\n */\nexport interface HeaderControlsOptions {\n  enableColumnActions: boolean\n  enableColumnOrdering: boolean\n  enableGrouping: boolean\n  enableColumnPinning: boolean\n  enableColumnVirtualization: boolean\n  columnFilterDisplayMode: ColumnFilterDisplayMode\n}\n\n// size-7 (28px) icon button + the header's gap-0.5 (2px) between affordances.\nconst ICON_BUTTON_WIDTH = 30\n// The sort glyph + its gap, rendered inside the label button.\nconst SORT_INDICATOR_WIDTH = 20\n// Horizontal cell padding + a sliver of label, so a column never collapses to\n// controls-only. A coarse constant: the exact (density-dependent) padding isn't\n// knowable without the DOM, and over-reserving a few px is harmless.\nconst LABEL_AND_PADDING_MIN = 56\n// Rough width of one uppercase, letter-spaced header character at the header's\n// text-xs scale. Deterministic (no canvas) so the SSR and client size vars\n// agree — a canvas measurement would differ between them and warn on hydration.\nconst HEADER_CHAR_WIDTH = 8.5\n// Horizontal cell padding reserved around the header label.\nconst HEADER_PADDING = 24\n// Cap so a very long header can't create an enormous default column.\nconst HEADER_LABEL_MAX = 260\n\n/** Build {@link HeaderControlsOptions} from a live table's resolved config. */\nexport function headerControlsOptionsFromTable<TData extends RowData>(\n  table: DataTableInstance<TData>\n): HeaderControlsOptions {\n  const config = table.tableInstance\n  return {\n    enableColumnActions: config.enableColumnActions,\n    enableColumnOrdering: config.enableColumnOrdering,\n    enableGrouping: config.enableGrouping,\n    enableColumnPinning: config.enableColumnPinning,\n    enableColumnVirtualization: config.enableColumnVirtualization,\n    columnFilterDisplayMode: config.columnFilterDisplayMode,\n  }\n}\n\n/** Whether the column-reorder drag grip renders in this column's header. */\nexport function shouldShowColumnDragGrip<TData extends RowData, TValue>(\n  column: Column<TData, TValue>,\n  opts: HeaderControlsOptions\n): boolean {\n  if (DISPLAY_COLUMN_IDS.has(column.id)) return false\n  return (\n    (opts.enableColumnOrdering ||\n      (opts.enableGrouping && column.getCanGroup())) &&\n    !opts.enableColumnVirtualization &&\n    !column.getIsPinned()\n  )\n}\n\n/** Whether the column-actions (⋮) trigger renders in this column's header. */\nexport function shouldShowColumnActions<TData extends RowData, TValue>(\n  column: Column<TData, TValue>,\n  opts: HeaderControlsOptions\n): boolean {\n  return (\n    opts.enableColumnActions &&\n    !column.columnDef.meta?.disableColumnActions &&\n    (column.getCanSort() ||\n      column.getCanHide() ||\n      column.getCanFilter() ||\n      (opts.enableColumnPinning && column.getCanPin()) ||\n      (opts.enableGrouping && column.getCanGroup()))\n  )\n}\n\n/** Whether the popover filter button renders in this column's header. */\nexport function shouldShowColumnFilterButton<TData extends RowData, TValue>(\n  column: Column<TData, TValue>,\n  opts: HeaderControlsOptions\n): boolean {\n  return opts.columnFilterDisplayMode === \"popover\" && column.getCanFilter()\n}\n\n/**\n * Width (px) to reserve beside the label for the header affordances that will\n * actually render for this column — the sort indicator plus any of the drag\n * grip, actions trigger, and filter button. Used as `extraWidth` when\n * autosizing and as the controls portion of {@link getHeaderControlsMinWidth}.\n */\nexport function getHeaderControlsWidth<TData extends RowData, TValue>(\n  column: Column<TData, TValue>,\n  opts: HeaderControlsOptions\n): number {\n  return (\n    (column.getCanSort() ? SORT_INDICATOR_WIDTH : 0) +\n    (shouldShowColumnDragGrip(column, opts) ? ICON_BUTTON_WIDTH : 0) +\n    (shouldShowColumnActions(column, opts) ? ICON_BUTTON_WIDTH : 0) +\n    (shouldShowColumnFilterButton(column, opts) ? ICON_BUTTON_WIDTH : 0)\n  )\n}\n\n// ----------------------------------------------------------------------------\n// ColumnDef-based reservation\n//\n// The functions above read a *built* column (capabilities via getCan*). The\n// size/minSize floors, however, must be baked into the column defs *before*\n// `useReactTable` — a pure transform, so React Compiler keeps it and SSR/client\n// agree (mutating a built column in an effect/memo gets dead-code-eliminated on\n// the client and desyncs hydration). These mirror the predicates above but\n// derive capabilities from the def + flags. They err toward reserving (a column\n// counts as sortable/filterable unless explicitly disabled) so the reservation\n// is always ≥ what actually renders — controls can never end up clipped.\n// ----------------------------------------------------------------------------\n\ntype AnyColumnDef<TData extends RowData> = ColumnDef<TData, unknown> & {\n  accessorKey?: unknown\n  accessorFn?: unknown\n  columns?: unknown\n  enableSorting?: boolean\n  enableHiding?: boolean\n  enableColumnFilter?: boolean\n  enableGrouping?: boolean\n  enablePinning?: boolean\n  minSize?: number\n  size?: number\n}\n\nfunction hasAccessor<TData extends RowData>(def: AnyColumnDef<TData>): boolean {\n  return def.accessorKey != null || def.accessorFn != null\n}\n\nfunction defLabel<TData extends RowData>(def: AnyColumnDef<TData>): string {\n  if (def.meta?.label) return def.meta.label\n  if (typeof def.header === \"string\" && def.header.length > 0) return def.header\n  if (typeof def.accessorKey === \"string\") return def.accessorKey\n  return \"\"\n}\n\nfunction getColumnDefControlsWidth<TData extends RowData>(\n  def: AnyColumnDef<TData>,\n  opts: HeaderControlsOptions\n): number {\n  const canSort = def.enableSorting !== false && hasAccessor(def)\n  const canHide = def.enableHiding !== false\n  const canFilter = def.enableColumnFilter !== false && hasAccessor(def)\n  const canGroup = def.enableGrouping !== false && hasAccessor(def)\n  const canPin = def.enablePinning !== false\n\n  const showGrip =\n    (opts.enableColumnOrdering || (opts.enableGrouping && canGroup)) &&\n    !opts.enableColumnVirtualization\n  const showActions =\n    opts.enableColumnActions &&\n    !def.meta?.disableColumnActions &&\n    (canSort ||\n      canHide ||\n      canFilter ||\n      (opts.enableColumnPinning && canPin) ||\n      (opts.enableGrouping && canGroup))\n  const showFilter = opts.columnFilterDisplayMode === \"popover\" && canFilter\n\n  return (\n    (canSort ? SORT_INDICATOR_WIDTH : 0) +\n    (showGrip ? ICON_BUTTON_WIDTH : 0) +\n    (showActions ? ICON_BUTTON_WIDTH : 0) +\n    (showFilter ? ICON_BUTTON_WIDTH : 0)\n  )\n}\n\n/** {@link getHeaderControlsMinWidth} computed from a column def (pre-build). */\nexport function getColumnDefMinWidth<TData extends RowData>(\n  def: ColumnDef<TData, unknown>,\n  opts: HeaderControlsOptions\n): number {\n  return LABEL_AND_PADDING_MIN + getColumnDefControlsWidth(def, opts)\n}\n\n/** {@link getHeaderPreferredWidth} computed from a column def (pre-build). */\nexport function getColumnDefPreferredWidth<TData extends RowData>(\n  def: ColumnDef<TData, unknown>,\n  opts: HeaderControlsOptions\n): number {\n  const labelWidth = Math.min(\n    HEADER_LABEL_MAX,\n    defLabel(def).length * HEADER_CHAR_WIDTH\n  )\n  return Math.ceil(\n    HEADER_PADDING + labelWidth + getColumnDefControlsWidth(def, opts)\n  )\n}\n\n/** True for a leaf user/data column def (not an injected display column or a\n *  header group) — the only defs that get a control-aware size/minSize. */\nexport function isDataColumnDef<TData extends RowData>(\n  def: ColumnDef<TData, unknown>\n): boolean {\n  const anyDef = def as AnyColumnDef<TData>\n  if (Array.isArray(anyDef.columns)) return false\n  if (def.id != null && DISPLAY_COLUMN_IDS.has(def.id)) return false\n  return hasAccessor(anyDef)\n}\n"
    },
    {
      "path": "ui/data-table/helpers/is-column-editable.ts",
      "type": "registry:ui",
      "target": "components/ui/data-table/helpers/is-column-editable.ts",
      "content": "import type { Column, RowData } from \"@tanstack/react-table\"\n\n/** A column is editable when it has an accessor and isn't opted out via meta. */\nexport function isColumnEditable<TData extends RowData, TValue>(\n  column: Column<TData, TValue>\n): boolean {\n  return (\n    column.accessorFn != null && column.columnDef.meta?.enableEditing !== false\n  )\n}\n"
    },
    {
      "path": "ui/data-table/helpers/measure-column-width.ts",
      "type": "registry:ui",
      "target": "components/ui/data-table/helpers/measure-column-width.ts",
      "content": "export interface MeasureColumnWidthOptions {\n  /** CSS `font` shorthand read from a rendered cell (e.g. `getComputedStyle(cell).font`). */\n  font: string\n  /** Total horizontal cell padding (left + right) in px. */\n  padding: number\n  /** Extra space for header affordances (sort icon, actions trigger). Default 0. */\n  extraWidth?: number\n  /** Lower bound for the result. Default 0. */\n  minWidth?: number\n  /** Upper bound, to stop one long value producing an absurd column. Default 400. */\n  maxWidth?: number\n}\n\n// A single reused canvas keeps text measurement off the DOM (no reflow) and\n// avoids allocating a canvas per call.\nlet sharedCtx: CanvasRenderingContext2D | null | undefined\n\nfunction getContext(): CanvasRenderingContext2D | null {\n  if (sharedCtx !== undefined) return sharedCtx\n  if (typeof document === \"undefined\") {\n    sharedCtx = null\n    return null\n  }\n  sharedCtx = document.createElement(\"canvas\").getContext(\"2d\")\n  return sharedCtx\n}\n\n/**\n * Measure the width a column needs to fit its widest value without wrapping.\n *\n * Pure + framework-free: canvas text measurement means it covers the full\n * dataset (not just the rows currently rendered under virtualization), at the\n * cost of not accounting for custom cell markup — it measures the raw string\n * values the caller passes in. Falls back to a rough character estimate when no\n * canvas is available (non-browser environments).\n */\nexport function measureColumnWidth(\n  values: string[],\n  headerText: string,\n  options: MeasureColumnWidthOptions\n): number {\n  const {\n    font,\n    padding,\n    extraWidth = 0,\n    minWidth = 0,\n    maxWidth = 400,\n  } = options\n  const ctx = getContext()\n\n  let contentWidth: number\n  if (ctx) {\n    ctx.font = font\n    contentWidth = ctx.measureText(headerText).width\n    for (const value of values) {\n      const width = ctx.measureText(value).width\n      if (width > contentWidth) contentWidth = width\n    }\n  } else {\n    // ~8px per character is a coarse fallback for non-browser environments.\n    const longest = values.reduce(\n      (max, v) => (v.length > max ? v.length : max),\n      headerText.length\n    )\n    contentWidth = longest * 8\n  }\n\n  const total = Math.ceil(contentWidth + padding + extraWidth)\n  return Math.max(minWidth, Math.min(maxWidth, total))\n}\n"
    },
    {
      "path": "ui/data-table/helpers/resolve-row-height.ts",
      "type": "registry:ui",
      "target": "components/ui/data-table/helpers/resolve-row-height.ts",
      "content": "import type { Row, RowData } from \"@tanstack/react-table\"\n\nexport interface RowHeightOptions<TData extends RowData> {\n  /** Flat height (px) applied to every row. */\n  rowHeight?: number\n  /** Per-row height: a px number, `\"auto\"` (wrap and grow), or `null` to fall\n   *  back to `rowHeight` / the density default. */\n  getRowHeight?: (row: Row<TData>) => number | \"auto\" | null\n}\n\n/**\n * Resolves a row's height from the `getRowHeight` / `rowHeight` options.\n * Precedence: `getRowHeight(row)` (unless it returns null/undefined) →\n * `rowHeight` → `undefined` (meaning \"use the density default\").\n */\nexport function resolveRowHeight<TData extends RowData>(\n  row: Row<TData>,\n  { getRowHeight, rowHeight }: RowHeightOptions<TData>\n): number | \"auto\" | undefined {\n  const resolved = getRowHeight?.(row)\n  if (resolved != null) return resolved\n  return rowHeight\n}\n"
    },
    {
      "path": "ui/data-table/hooks/use-advanced-filter.ts",
      "type": "registry:ui",
      "target": "components/ui/data-table/hooks/use-advanced-filter.ts",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport type { RowData, RowModel, Table } from \"@tanstack/react-table\"\n\nimport { createAdvancedFilteredRowModel } from \"../fns/advanced-filter\"\nimport type { AdvancedFilterGroup } from \"../core/types\"\nimport { useControllableState } from \"./use-controllable-state\"\n\nconst EMPTY_GROUP: AdvancedFilterGroup = { logic: \"and\", rules: [] }\n\ninterface UseAdvancedFilterParams {\n  enableAdvancedFilter: boolean\n  advancedFilter?: AdvancedFilterGroup\n  defaultAdvancedFilter?: AdvancedFilterGroup\n  onAdvancedFilterChange?: (filter: AdvancedFilterGroup) => void\n}\n\nexport interface AdvancedFilterState<TData extends RowData> {\n  advancedFilter: AdvancedFilterGroup\n  setAdvancedFilter: React.Dispatch<React.SetStateAction<AdvancedFilterGroup>>\n  showAdvancedFilterPanel: boolean\n  setShowAdvancedFilterPanel: React.Dispatch<React.SetStateAction<boolean>>\n  /** Filtered row model that applies the advanced group on top of the normal\n   *  column/global filters. Stable identity (preserves TanStack memoization). */\n  advancedFilteredRowModel: (table: Table<TData>) => () => RowModel<TData>\n}\n\n/**\n * Advanced filter state (controllable group + panel visibility) plus the custom\n * filtered row model it drives. The active group is read through a ref so the\n * row-model factory keeps a stable identity; when the feature is off the ref\n * holds the empty group, so the model is a pass-through.\n */\nexport function useAdvancedFilter<TData extends RowData>({\n  enableAdvancedFilter,\n  advancedFilter: advancedFilterProp,\n  defaultAdvancedFilter,\n  onAdvancedFilterChange,\n}: UseAdvancedFilterParams): AdvancedFilterState<TData> {\n  const [advancedFilter, setAdvancedFilter] =\n    useControllableState<AdvancedFilterGroup>(\n      advancedFilterProp,\n      defaultAdvancedFilter ?? EMPTY_GROUP,\n      onAdvancedFilterChange\n    )\n  const [showAdvancedFilterPanel, setShowAdvancedFilterPanel] =\n    React.useState(false)\n\n  const groupRef = React.useRef<AdvancedFilterGroup>(EMPTY_GROUP)\n  // Render-phase latest-ref write is load-bearing: TanStack computes row\n  // models during render, and the stable factory below must see this render's\n  // group (an effect write would filter with a stale group for a full frame).\n  // eslint-disable-next-line react-hooks/refs\n  groupRef.current = enableAdvancedFilter ? advancedFilter : EMPTY_GROUP\n\n  const advancedFilteredRowModel = React.useMemo(\n    // The getter runs inside TanStack's row-model computation, not during\n    // this hook's render.\n    // eslint-disable-next-line react-hooks/refs\n    () => createAdvancedFilteredRowModel<TData>(() => groupRef.current),\n    []\n  )\n\n  return {\n    advancedFilter,\n    setAdvancedFilter,\n    showAdvancedFilterPanel,\n    setShowAdvancedFilterPanel,\n    advancedFilteredRowModel,\n  }\n}\n"
    },
    {
      "path": "ui/data-table/hooks/use-column-filter-modes.ts",
      "type": "registry:ui",
      "target": "components/ui/data-table/hooks/use-column-filter-modes.ts",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport type { ColumnDef, FilterFn, RowData } from \"@tanstack/react-table\"\n\nimport {\n  createDynamicFilterFn,\n  defaultModeForVariant,\n  type FilterMode,\n} from \"../fns/filter-fns\"\nimport { columnKey } from \"../helpers/column-key\"\n\nexport interface ColumnFilterModes<TData extends RowData> {\n  columnFilterModes: Record<string, FilterMode>\n  setColumnFilterModes: React.Dispatch<\n    React.SetStateAction<Record<string, FilterMode>>\n  >\n  /** Resolve the active mode for a column (active → default → \"contains\"). */\n  getColumnMode: (columnId: string) => FilterMode\n  /** Single dynamic filter fn assigned to every column via `defaultColumn`. */\n  dynamicFilterFn: FilterFn<TData>\n}\n\n/**\n * Per-column filter-mode state and the dynamic filter function that reads it.\n * Default modes are derived from each column's `meta.filterMode` / `meta.variant`.\n * Modes are read through refs so the filter fn keeps a stable identity (changing\n * it would thrash the filtered row model every render).\n *\n * The value-resetting `setColumnFilterMode` action lives in `useDataTable`\n * because it needs the table instance, which can't exist before this hook\n * supplies `dynamicFilterFn`.\n */\nexport function useColumnFilterModes<TData extends RowData>(\n  columns: ColumnDef<TData, unknown>[]\n): ColumnFilterModes<TData> {\n  const [columnFilterModes, setColumnFilterModes] = React.useState<\n    Record<string, FilterMode>\n  >({})\n\n  // Per-column default mode derived from `meta.filterMode` / `meta.variant`.\n  const defaultModes = React.useMemo(() => {\n    const map: Record<string, FilterMode> = {}\n    for (const def of columns) {\n      const key = columnKey(def as { id?: string; accessorKey?: unknown })\n      if (!key) continue\n      const meta = def.meta\n      map[key] =\n        meta?.filterMode ?? defaultModeForVariant(meta?.variant ?? \"text\")\n    }\n    return map\n  }, [columns])\n\n  // Refs let the dynamic filterFn read current modes without re-creating its\n  // identity (which would thrash the filtered row model on every render).\n  // The render-phase writes are load-bearing: TanStack filters during render,\n  // so the filterFn must see this render's modes (an effect write would\n  // filter with stale modes for a full frame).\n  const modesRef = React.useRef(columnFilterModes)\n  // eslint-disable-next-line react-hooks/refs\n  modesRef.current = columnFilterModes\n  const defaultModesRef = React.useRef(defaultModes)\n  // eslint-disable-next-line react-hooks/refs\n  defaultModesRef.current = defaultModes\n\n  const getColumnMode = React.useCallback(\n    (columnId: string): FilterMode =>\n      modesRef.current[columnId] ??\n      defaultModesRef.current[columnId] ??\n      \"contains\",\n    []\n  )\n\n  const dynamicFilterFn = React.useMemo(\n    // getColumnMode reads refs inside TanStack's filtering pass, not during\n    // this hook's render.\n    // eslint-disable-next-line react-hooks/refs\n    () => createDynamicFilterFn<TData>(getColumnMode),\n    [getColumnMode]\n  )\n\n  return {\n    columnFilterModes,\n    setColumnFilterModes,\n    getColumnMode,\n    dynamicFilterFn,\n  }\n}\n"
    },
    {
      "path": "ui/data-table/hooks/use-controllable-state.ts",
      "type": "registry:ui",
      "target": "components/ui/data-table/hooks/use-controllable-state.ts",
      "content": "\"use client\"\n\nimport * as React from \"react\"\n\n/**\n * State that is uncontrolled by default but becomes controlled when a value is\n * supplied. Either way `onChange` fires, so consumers can observe a change\n * without taking over ownership. Returns the same tuple shape as `useState` so\n * existing `Dispatch<SetStateAction<T>>` consumers keep working.\n */\nexport function useControllableState<T>(\n  controlled: T | undefined,\n  defaultValue: T,\n  onChange?: (value: T) => void\n): [T, React.Dispatch<React.SetStateAction<T>>] {\n  const [uncontrolled, setUncontrolled] = React.useState(defaultValue)\n  const isControlled = controlled !== undefined\n  const value = isControlled ? controlled : uncontrolled\n\n  // Functional updaters must chain within a single React batch (useState\n  // semantics), so resolve them against the latest dispatched value rather\n  // than the render-captured one. The effect re-syncs after the controlling\n  // parent applies (or rejects) the change.\n  const latest = React.useRef(value)\n  React.useEffect(() => {\n    latest.current = value\n  })\n\n  const setValue = React.useCallback<React.Dispatch<React.SetStateAction<T>>>(\n    (next) => {\n      const resolved =\n        typeof next === \"function\"\n          ? (next as (prev: T) => T)(latest.current)\n          : next\n      latest.current = resolved\n      if (!isControlled) setUncontrolled(resolved)\n      onChange?.(resolved)\n    },\n    [isControlled, onChange]\n  )\n\n  return [value, setValue]\n}\n"
    },
    {
      "path": "ui/data-table/hooks/use-editing-state.ts",
      "type": "registry:ui",
      "target": "components/ui/data-table/hooks/use-editing-state.ts",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport type { Row, RowData } from \"@tanstack/react-table\"\n\nimport type { EditingCell } from \"../core/types\"\n\nexport interface EditingState<TData extends RowData> {\n  editingCell: EditingCell | null\n  setEditingCell: React.Dispatch<React.SetStateAction<EditingCell | null>>\n  editingRowId: string | null\n  isCreating: boolean\n  rowDraft: Record<string, unknown>\n  setRowDraftValue: (columnId: string, value: unknown) => void\n  beginRowEdit: (row: Row<TData>) => void\n  beginCreate: () => void\n  cancelEdit: () => void\n}\n\n/**\n * Inline editing / create-form state machine. Tracks which cell or row is being\n * edited (and whether the create form is open) plus the draft values, and\n * exposes the transitions to enter/leave those states. The draft is seeded from\n * the row's accessor cells on edit, or from `createRowDefaults` on create.\n */\nexport function useEditingState<TData extends RowData>(\n  createRowDefaults?: Record<string, unknown>\n): EditingState<TData> {\n  const [editingCell, setEditingCell] = React.useState<EditingCell | null>(null)\n  const [editingRowId, setEditingRowId] = React.useState<string | null>(null)\n  const [isCreating, setIsCreating] = React.useState(false)\n  const [rowDraft, setRowDraft] = React.useState<Record<string, unknown>>({})\n\n  const setRowDraftValue = React.useCallback(\n    (columnId: string, value: unknown) =>\n      setRowDraft((prev) => ({ ...prev, [columnId]: value })),\n    []\n  )\n\n  const beginRowEdit = React.useCallback((row: Row<TData>) => {\n    const draft: Record<string, unknown> = {}\n    for (const cell of row.getAllCells()) {\n      if (cell.column.accessorFn != null) {\n        draft[cell.column.id] = cell.getValue()\n      }\n    }\n    setRowDraft(draft)\n    setEditingRowId(row.id)\n    setIsCreating(false)\n    setEditingCell(null)\n  }, [])\n\n  const beginCreate = React.useCallback(() => {\n    setRowDraft(createRowDefaults ? { ...createRowDefaults } : {})\n    setIsCreating(true)\n    setEditingRowId(null)\n    setEditingCell(null)\n  }, [createRowDefaults])\n\n  const cancelEdit = React.useCallback(() => {\n    setEditingCell(null)\n    setEditingRowId(null)\n    setIsCreating(false)\n    setRowDraft({})\n  }, [])\n\n  return {\n    editingCell,\n    setEditingCell,\n    editingRowId,\n    isCreating,\n    rowDraft,\n    setRowDraftValue,\n    beginRowEdit,\n    beginCreate,\n    cancelEdit,\n  }\n}\n"
    },
    {
      "path": "ui/data-table/hooks/use-global-filter-mode.ts",
      "type": "registry:ui",
      "target": "components/ui/data-table/hooks/use-global-filter-mode.ts",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport type { FilterFn, RowData, RowModel, Table } from \"@tanstack/react-table\"\n\nimport {\n  createGlobalFilterFn,\n  createRankedSortedRowModel,\n  type GlobalFilterMode,\n} from \"../fns/filter-fns\"\nimport { useControllableState } from \"./use-controllable-state\"\n\ninterface UseGlobalFilterModeParams {\n  globalFilterMode?: GlobalFilterMode\n  defaultGlobalFilterMode: GlobalFilterMode\n  onGlobalFilterModeChange?: (mode: GlobalFilterMode) => void\n  enableGlobalFilterRankedResults: boolean\n  manualSorting: boolean\n  manualFiltering: boolean\n  enableGrouping: boolean\n}\n\nexport interface GlobalFilterModeState<TData extends RowData> {\n  globalFilterMode: GlobalFilterMode\n  setGlobalFilterMode: (mode: GlobalFilterMode) => void\n  /** Mode-aware global filter fn (new identity per mode → re-runs filtering). */\n  dynamicGlobalFilterFn: FilterFn<TData>\n  /** Sorted row model that re-orders by fuzzy rank when ranking is active. */\n  rankedSortedRowModel: (table: Table<TData>) => () => RowModel<TData>\n}\n\n/**\n * Global-search mode state plus the two row-model pieces it drives: the\n * mode-aware global filter fn and the fuzzy-ranked sorted row model. While a\n * fuzzy global search is active and the user hasn't sorted, rows are ordered by\n * best match; the current config is read through a ref so the model factory\n * keeps a stable identity (recreating it would defeat TanStack's memoization).\n */\nexport function useGlobalFilterMode<TData extends RowData>({\n  globalFilterMode: globalFilterModeProp,\n  defaultGlobalFilterMode,\n  onGlobalFilterModeChange,\n  enableGlobalFilterRankedResults,\n  manualSorting,\n  manualFiltering,\n  enableGrouping,\n}: UseGlobalFilterModeParams): GlobalFilterModeState<TData> {\n  const [globalFilterMode, setGlobalFilterMode] =\n    useControllableState<GlobalFilterMode>(\n      globalFilterModeProp,\n      defaultGlobalFilterMode,\n      onGlobalFilterModeChange\n    )\n\n  // Recreating the fn when the mode changes gives it a new identity, which\n  // makes TanStack re-run global filtering with the new mode immediately.\n  const dynamicGlobalFilterFn = React.useMemo(\n    () => createGlobalFilterFn<TData>(() => globalFilterMode),\n    [globalFilterMode]\n  )\n\n  const rankingRef = React.useRef<{\n    enabled: boolean\n    mode: GlobalFilterMode\n    manualSorting: boolean\n    manualFiltering: boolean\n    grouping: boolean\n  }>(null!)\n  // Render-phase latest-ref write is load-bearing: TanStack computes row\n  // models during render, and the stable factory below must see this render's\n  // config (an effect write would rank with stale config for a full frame).\n  // eslint-disable-next-line react-hooks/refs\n  rankingRef.current = {\n    enabled: enableGlobalFilterRankedResults,\n    mode: globalFilterMode,\n    manualSorting,\n    manualFiltering,\n    grouping: enableGrouping,\n  }\n  const rankedSortedRowModel = React.useMemo(\n    () =>\n      // The predicate runs inside TanStack's row-model computation, not\n      // during this hook's render.\n      // eslint-disable-next-line react-hooks/refs\n      createRankedSortedRowModel<TData>((t) => {\n        const c = rankingRef.current\n        if (!c.enabled || c.mode !== \"fuzzy\") return false\n        if (c.manualSorting || c.manualFiltering) return false\n        const s = t.getState()\n        if (!s.globalFilter) return false\n        if (s.sorting.some(Boolean)) return false\n        if (c.grouping && s.grouping.length > 0) return false\n        if (s.expanded === true || Object.values(s.expanded).some(Boolean))\n          return false\n        return true\n      }),\n    []\n  )\n\n  return {\n    globalFilterMode,\n    setGlobalFilterMode,\n    dynamicGlobalFilterFn,\n    rankedSortedRowModel,\n  }\n}\n"
    },
    {
      "path": "ui/data-table/hooks/use-grid-navigation.ts",
      "type": "registry:ui",
      "target": "components/ui/data-table/hooks/use-grid-navigation.ts",
      "content": "\"use client\"\n\nimport * as React from \"react\"\n\nconst NAV_KEYS = new Set([\n  \"ArrowUp\",\n  \"ArrowDown\",\n  \"ArrowLeft\",\n  \"ArrowRight\",\n  \"Home\",\n  \"End\",\n])\n\nconst EDITABLE = new Set([\"INPUT\", \"TEXTAREA\", \"SELECT\"])\n\n/**\n * Roving-tabindex keyboard navigation for body cells (MRT V3 parity). Cells\n * carry `data-cell-row` / `data-cell-col`; the grid is a single tab stop and\n * arrow keys / Home / End move focus between cells. Skipped while focus is in\n * an editable control so typing in a filter/edit field isn't hijacked.\n */\nexport function useGridNavigation<T extends HTMLElement>(enabled: boolean) {\n  const ref = React.useRef<T>(null)\n\n  const onKeyDown = React.useCallback(\n    (event: React.KeyboardEvent<T>) => {\n      if (!enabled || !NAV_KEYS.has(event.key)) return\n      const root = ref.current\n      if (!root) return\n\n      const active = document.activeElement as HTMLElement | null\n      if (active && EDITABLE.has(active.tagName)) return\n\n      const current = active?.closest<HTMLElement>(\"[data-cell-row]\")\n      if (!current || !root.contains(current)) return\n\n      const cells = Array.from(\n        root.querySelectorAll<HTMLElement>(\"[data-cell-row]\")\n      )\n      if (cells.length === 0) return\n\n      let maxRow = 0\n      let maxCol = 0\n      for (const cell of cells) {\n        maxRow = Math.max(maxRow, Number(cell.dataset.cellRow))\n        maxCol = Math.max(maxCol, Number(cell.dataset.cellCol))\n      }\n\n      let row = Number(current.dataset.cellRow)\n      let col = Number(current.dataset.cellCol)\n\n      switch (event.key) {\n        case \"ArrowUp\":\n          row = Math.max(0, row - 1)\n          break\n        case \"ArrowDown\":\n          row = Math.min(maxRow, row + 1)\n          break\n        case \"ArrowLeft\":\n          col = Math.max(0, col - 1)\n          break\n        case \"ArrowRight\":\n          col = Math.min(maxCol, col + 1)\n          break\n        case \"Home\":\n          col = 0\n          if (event.ctrlKey) row = 0\n          break\n        case \"End\":\n          col = maxCol\n          if (event.ctrlKey) row = maxRow\n          break\n      }\n\n      const next = cells.find(\n        (cell) =>\n          Number(cell.dataset.cellRow) === row &&\n          Number(cell.dataset.cellCol) === col\n      )\n      if (!next || next === current) return\n\n      event.preventDefault()\n      for (const cell of cells) cell.tabIndex = -1\n      next.tabIndex = 0\n      next.focus()\n    },\n    [enabled]\n  )\n\n  return { ref, onKeyDown }\n}\n"
    },
    {
      "path": "ui/data-table/hooks/use-infinite-scroll.ts",
      "type": "registry:ui",
      "target": "components/ui/data-table/hooks/use-infinite-scroll.ts",
      "content": "\"use client\"\n\nimport * as React from \"react\"\n\nexport interface UseInfiniteScrollOptions {\n  /** Master switch. When false, no observer is created. */\n  enabled: boolean\n  /** Whether more rows exist to load. */\n  hasNextPage: boolean\n  /** Whether a load is currently in flight (blocks re-triggering). */\n  isFetchingNextPage: boolean\n  /** Called (once per satisfied condition) to request the next chunk. Should be\n   *  stable across renders (e.g. `useCallback`) to avoid extra calls. */\n  onLoadMore?: () => void\n  /** Distance in px from the bottom at which to prefetch. Default 200. */\n  threshold?: number\n  /** The scroll container the sentinel lives in (IntersectionObserver root). */\n  scrollRef: React.RefObject<HTMLElement | null>\n  /** The sentinel element rendered after the last row. */\n  sentinelRef: React.RefObject<HTMLElement | null>\n}\n\n/**\n * Fires `onLoadMore` when a bottom sentinel scrolls near the viewport, gated by\n * the controlled `hasNextPage` / `isFetchingNextPage` flags. Works whether or\n * not row virtualization is on, since the sentinel sits after the rendered\n * window. Re-checks on flag changes so a finished fetch that leaves the sentinel\n * still in view loads the following chunk.\n */\nexport function useInfiniteScroll({\n  enabled,\n  hasNextPage,\n  isFetchingNextPage,\n  onLoadMore,\n  threshold = 200,\n  scrollRef,\n  sentinelRef,\n}: UseInfiniteScrollOptions): void {\n  const [isIntersecting, setIsIntersecting] = React.useState(false)\n\n  React.useEffect(() => {\n    const sentinel = sentinelRef.current\n    if (!enabled || !sentinel) {\n      setIsIntersecting(false)\n      return\n    }\n    const observer = new IntersectionObserver(\n      (entries) => {\n        const entry = entries[0]\n        if (entry) setIsIntersecting(entry.isIntersecting)\n      },\n      {\n        root: scrollRef.current ?? null,\n        rootMargin: `0px 0px ${threshold}px 0px`,\n      }\n    )\n    observer.observe(sentinel)\n    return () => observer.disconnect()\n  }, [enabled, threshold, scrollRef, sentinelRef])\n\n  React.useEffect(() => {\n    if (enabled && isIntersecting && hasNextPage && !isFetchingNextPage) {\n      onLoadMore?.()\n    }\n  }, [enabled, isIntersecting, hasNextPage, isFetchingNextPage, onLoadMore])\n}\n"
    },
    {
      "path": "ui/data-table/hooks/use-page-reset-on-filter-change.ts",
      "type": "registry:ui",
      "target": "components/ui/data-table/hooks/use-page-reset-on-filter-change.ts",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport type { RowData } from \"@tanstack/react-table\"\n\nimport type { DataTableInstance } from \"../core/types\"\n\ninterface PageResetParams {\n  enablePagination: boolean\n  manualPagination?: boolean\n  autoResetPageIndex?: boolean\n}\n\n/**\n * Clamps to the first page when the filter set changes (MRT behaviour),\n * replacing TanStack's render-phase auto-reset (which warns in React 19 dev).\n * Runs after mount so there is no state update during render. Skipped under\n * manual pagination (server owns it) and when the consumer opted into the\n * native auto-reset. Guarded by comparing the previous filters key — not by\n * counting effect runs — so StrictMode's double-invoke on mount is a no-op.\n */\nexport function usePageResetOnFilterChange<TData extends RowData>(\n  table: DataTableInstance<TData>,\n  { enablePagination, manualPagination, autoResetPageIndex }: PageResetParams\n): void {\n  const filtersKey = JSON.stringify([\n    table.getState().columnFilters,\n    table.getState().globalFilter,\n  ])\n  const prevFiltersKeyRef = React.useRef<string | undefined>(undefined)\n  React.useEffect(() => {\n    const prevFiltersKey = prevFiltersKeyRef.current\n    prevFiltersKeyRef.current = filtersKey\n    if (prevFiltersKey === undefined || prevFiltersKey === filtersKey) return\n    if (enablePagination && !manualPagination && autoResetPageIndex == null) {\n      table.setPageIndex(0)\n    }\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [filtersKey])\n}\n"
    },
    {
      "path": "ui/data-table/hooks/use-resolved-columns.ts",
      "type": "registry:ui",
      "target": "components/ui/data-table/hooks/use-resolved-columns.ts",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport type { ColumnDef, RowData } from \"@tanstack/react-table\"\n\nimport {\n  createExpandColumn,\n  createRowDragHandleColumn,\n  createRowNumberColumn,\n} from \"../injected-columns/injected-columns\"\nimport { createRowActionsColumn } from \"../injected-columns/data-table-row-actions\"\nimport { createSelectionColumn } from \"../injected-columns/selection-column\"\nimport type { DataTableIcons } from \"../core/icons\"\nimport type { DataTableLocalization } from \"../core/localization\"\nimport type { EditDisplayMode, UseDataTableOptions } from \"../core/types\"\n\ninterface UseResolvedColumnsParams<TData extends RowData> {\n  columns: ColumnDef<TData, unknown>[]\n  enableRowOrdering: boolean\n  enableRowSelection: boolean\n  selectAllMode: \"page\" | \"all\"\n  enableSelectAll: boolean\n  needsExpandColumn: boolean\n  positionExpandColumn: \"first\" | \"last\"\n  enableRowNumbers: boolean\n  rowNumberMode: \"static\" | \"original\"\n  enableRowPinning: boolean\n  renderRowActions: UseDataTableOptions<TData>[\"renderRowActions\"]\n  renderRowActionMenuItems: UseDataTableOptions<TData>[\"renderRowActionMenuItems\"]\n  positionActionsColumn: \"first\" | \"last\"\n  enableEditing: boolean\n  editDisplayMode: EditDisplayMode\n  localization: DataTableLocalization\n  icons: DataTableIcons\n}\n\n/**\n * Injects the display columns around the user's columns, memoized so TanStack\n * never receives a new `columns` identity per render (a classic infinite-loop /\n * lost-state trap). Leading order: drag handle → selection → expand → row\n * number → user columns. The expand and row-actions columns can move to the\n * trailing/leading edge via `positionExpandColumn` / `positionActionsColumn`.\n */\nexport function useResolvedColumns<TData extends RowData>({\n  columns,\n  enableRowOrdering,\n  enableRowSelection,\n  selectAllMode,\n  enableSelectAll,\n  needsExpandColumn,\n  positionExpandColumn,\n  enableRowNumbers,\n  rowNumberMode,\n  enableRowPinning,\n  renderRowActions,\n  renderRowActionMenuItems,\n  positionActionsColumn,\n  enableEditing,\n  editDisplayMode,\n  localization,\n  icons,\n}: UseResolvedColumnsParams<TData>): ColumnDef<TData, unknown>[] {\n  return React.useMemo(() => {\n    const leading = []\n    const trailing = []\n    if (enableRowOrdering) {\n      leading.push(createRowDragHandleColumn<TData>(localization, icons))\n    }\n    if (enableRowSelection) {\n      leading.push(\n        createSelectionColumn<TData>(\n          localization,\n          selectAllMode,\n          enableSelectAll\n        )\n      )\n    }\n    if (needsExpandColumn) {\n      const expand = createExpandColumn<TData>(localization, icons)\n      if (positionExpandColumn === \"last\") trailing.push(expand)\n      else leading.push(expand)\n    }\n    if (enableRowNumbers) {\n      leading.push(\n        createRowNumberColumn<TData>(\n          localization,\n          rowNumberMode,\n          enableRowPinning,\n          icons\n        )\n      )\n    }\n    const showRowActions =\n      !!renderRowActions ||\n      !!renderRowActionMenuItems ||\n      (enableEditing &&\n        (editDisplayMode === \"row\" || editDisplayMode === \"modal\"))\n    if (showRowActions) {\n      const actions = createRowActionsColumn<TData>(positionActionsColumn)\n      if (positionActionsColumn === \"first\") leading.unshift(actions)\n      else trailing.push(actions)\n    }\n    return leading.length > 0 || trailing.length > 0\n      ? [...leading, ...columns, ...trailing]\n      : columns\n  }, [\n    columns,\n    enableRowOrdering,\n    enableRowSelection,\n    selectAllMode,\n    enableSelectAll,\n    needsExpandColumn,\n    positionExpandColumn,\n    enableRowNumbers,\n    rowNumberMode,\n    enableRowPinning,\n    renderRowActions,\n    renderRowActionMenuItems,\n    positionActionsColumn,\n    enableEditing,\n    editDisplayMode,\n    localization,\n    icons,\n  ])\n}\n"
    },
    {
      "path": "ui/data-table/hooks/use-table-dnd.ts",
      "type": "registry:ui",
      "target": "components/ui/data-table/hooks/use-table-dnd.ts",
      "content": "\"use client\"\n\nimport {\n  KeyboardSensor,\n  PointerSensor,\n  closestCenter,\n  pointerWithin,\n  useSensor,\n  useSensors,\n  type CollisionDetection,\n  type DragEndEvent,\n} from \"@dnd-kit/core\"\nimport { arrayMove, sortableKeyboardCoordinates } from \"@dnd-kit/sortable\"\nimport type { RowData } from \"@tanstack/react-table\"\n\nimport { GROUP_DROPZONE_ID } from \"../components/toolbar/data-table-grouping\"\nimport type { DataTableInstance } from \"../core/types\"\n\n/**\n * Sensors + drag-end handler for the single `DndContext` that drives both\n * column and row reordering. The active item's `data.type` (set in useSortable)\n * routes to the right handler — two nested contexts can't be used: dnd-kit\n * renders an a11y `<div>` that is invalid inside `<tbody>`, and a body-wrapping\n * context would swallow header drags.\n */\nexport function useTableDnd<TData extends RowData>(\n  table: DataTableInstance<TData>\n) {\n  const sensors = useSensors(\n    useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),\n    useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })\n  )\n\n  // The group drop zone should accept a drop anywhere inside its bounds, not\n  // only near its center. Prefer a pointer-within hit on the zone; otherwise\n  // fall back to closestCenter for column/row reordering (and keyboard dnd,\n  // where pointerWithin yields nothing).\n  const collisionDetection: CollisionDetection = (args) => {\n    const groupHit = pointerWithin(args).find((c) => c.id === GROUP_DROPZONE_ID)\n    return groupHit ? [groupHit] : closestCenter(args)\n  }\n\n  const onRowOrderChange = table.tableInstance.onRowOrderChange\n\n  const handleDragEnd = (event: DragEndEvent) => {\n    const { active, over } = event\n    if (!over) return\n    const type = active.data.current?.type\n\n    if (type === \"row\") {\n      if (active.id !== over.id) {\n        onRowOrderChange?.(active.id as string, over.id as string)\n      }\n      return\n    }\n\n    // column drag → drop on the group zone groups by that column\n    if (over.id === GROUP_DROPZONE_ID) {\n      const column = table.getColumn(active.id as string)\n      if (column && !column.getIsGrouped()) column.toggleGrouping()\n      return\n    }\n    // Otherwise it's a reorder, which only applies when ordering is enabled\n    // (a column may be draggable solely to support drag-to-group).\n    if (!table.tableInstance.enableColumnOrdering) return\n    if (active.id === over.id) return\n    const base =\n      table.getState().columnOrder.length > 0\n        ? table.getState().columnOrder\n        : table.getAllLeafColumns().map((c) => c.id)\n    const oldIndex = base.indexOf(active.id as string)\n    const newIndex = base.indexOf(over.id as string)\n    if (oldIndex === -1 || newIndex === -1) return\n    table.setColumnOrder(arrayMove(base, oldIndex, newIndex))\n  }\n\n  return { sensors, collisionDetection, handleDragEnd }\n}\n"
    },
    {
      "path": "ui/data-table/hooks/use-table-virtualizers.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/hooks/use-table-virtualizers.tsx",
      "content": "\"use client\"\n\nimport type { Row, RowData } from \"@tanstack/react-table\"\nimport { useVirtualizer } from \"@tanstack/react-virtual\"\nimport * as React from \"react\"\n\nimport type { DataTableInstance } from \"../core/types\"\nimport { resolveRowHeight } from \"../helpers/resolve-row-height\"\n\nexport interface VirtualRowItem<TData extends RowData> {\n  row: Row<TData>\n  detail: boolean\n}\n\n/** Wraps a window of cells with left/right spacers when virtualizing columns. */\nexport type WithColumnSpacers = (\n  cells: React.ReactNode[],\n  keyPrefix: string\n) => React.ReactNode\n\n/**\n * Row + column virtualization for {@link DataTable}. Builds the flattened\n * virtualization list (center rows + their expanded detail panels), wires both\n * `@tanstack/react-virtual` instances, exposes them via the optional instance\n * refs, and returns the helpers the header/body/footer need to render only the\n * visible window with left/right column spacers.\n */\nexport function useTableVirtualizers<TData extends RowData>(\n  table: DataTableInstance<TData>,\n  gridRef: React.RefObject<HTMLDivElement | null>\n) {\n  const {\n    enableRowVirtualization,\n    enableColumnVirtualization,\n    renderDetailPanel,\n    estimateRowHeight,\n    rowHeight,\n    getRowHeight,\n    virtualOverscan,\n    rowVirtualizerOptions,\n    columnVirtualizerOptions,\n    rowVirtualizerInstanceRef,\n    columnVirtualizerInstanceRef,\n  } = table.tableInstance\n\n  // Flatten center rows (+ expanded detail panels) into a virtualization list.\n  const virtualItems: VirtualRowItem<TData>[] = []\n  if (enableRowVirtualization) {\n    for (const row of table.getCenterRows()) {\n      virtualItems.push({ row, detail: false })\n      if (renderDetailPanel && row.getIsExpanded() && !row.getIsGrouped()) {\n        virtualItems.push({ row, detail: true })\n      }\n    }\n  }\n\n  // User-supplied passthrough options, resolved from their value-or-function form.\n  const rowVOptions =\n    typeof rowVirtualizerOptions === \"function\"\n      ? rowVirtualizerOptions({ table })\n      : rowVirtualizerOptions\n  const columnVOptions =\n    typeof columnVirtualizerOptions === \"function\"\n      ? columnVirtualizerOptions({ table })\n      : columnVirtualizerOptions\n\n  // The React Compiler bails on TanStack Virtual's mutable instance; expected.\n  // eslint-disable-next-line react-hooks/incompatible-library\n  const rowVirtualizer = useVirtualizer({\n    count: virtualItems.length,\n    getScrollElement: () => gridRef.current,\n    // Per-row estimate: an exact px number pins the row; \"auto\" (or no override)\n    // uses the flat estimate and lets `measureElement` correct to the real\n    // height. Detail-panel rows always fall back to the flat estimate.\n    estimateSize: (index) => {\n      const item = virtualItems[index]\n      if (!item || item.detail) return estimateRowHeight\n      const resolved = resolveRowHeight(item.row, { rowHeight, getRowHeight })\n      return typeof resolved === \"number\" ? resolved : estimateRowHeight\n    },\n    overscan: virtualOverscan,\n    measureElement:\n      typeof window !== \"undefined\"\n        ? (el) => el?.getBoundingClientRect().height ?? 0\n        : undefined,\n    ...rowVOptions,\n  })\n\n  // Horizontal virtualizer for wide tables. When off, count is 0 and the\n  // helpers below fall through to rendering all columns.\n  const leafColumns = table.getVisibleLeafColumns()\n  const columnVirtualizer = useVirtualizer({\n    horizontal: true,\n    count: enableColumnVirtualization ? leafColumns.length : 0,\n    getScrollElement: () => gridRef.current,\n    estimateSize: (index) => leafColumns[index]?.getSize() ?? 150,\n    overscan: virtualOverscan,\n    ...columnVOptions,\n  })\n\n  // Expose the virtualizer instances for imperative control (e.g. scrollToIndex).\n  React.useEffect(() => {\n    if (rowVirtualizerInstanceRef)\n      rowVirtualizerInstanceRef.current = rowVirtualizer\n  })\n  React.useEffect(() => {\n    if (columnVirtualizerInstanceRef)\n      columnVirtualizerInstanceRef.current = columnVirtualizer\n  })\n\n  const virtualColumns = enableColumnVirtualization\n    ? columnVirtualizer.getVirtualItems()\n    : []\n  const colSpacerLeft = virtualColumns.length\n    ? (virtualColumns[0]?.start ?? 0)\n    : 0\n  const colSpacerRight = virtualColumns.length\n    ? columnVirtualizer.getTotalSize() -\n      (virtualColumns[virtualColumns.length - 1]?.end ?? 0)\n    : 0\n\n  /** Wraps a row of cells with left/right spacers when virtualizing columns. */\n  const withColumnSpacers = (\n    cells: React.ReactNode[],\n    keyPrefix: string\n  ): React.ReactNode => {\n    if (!enableColumnVirtualization) return cells\n    return (\n      <>\n        {colSpacerLeft > 0 && (\n          <td\n            key={`${keyPrefix}-spacer-l`}\n            aria-hidden\n            style={{ width: colSpacerLeft }}\n          />\n        )}\n        {cells}\n        {colSpacerRight > 0 && (\n          <td\n            key={`${keyPrefix}-spacer-r`}\n            aria-hidden\n            style={{ width: colSpacerRight }}\n          />\n        )}\n      </>\n    )\n  }\n\n  return {\n    rowVirtualizer,\n    columnVirtualizer,\n    virtualItems,\n    virtualColumns,\n    withColumnSpacers,\n  }\n}\n"
    },
    {
      "path": "ui/data-table/index.ts",
      "type": "registry:ui",
      "target": "components/ui/data-table/index.ts",
      "content": "\"use client\"\n\n// Public API — the data table, its hook, config plumbing, documented helpers,\n// and the types describing them. The engine and shared definitions live under\n// ./core; the supporting building blocks live under ./components, ./hooks,\n// ./fns, and ./utils and are intentionally not all re-exported here.\n\nexport { DataTable } from \"./core/data-table\"\nexport { useDataTable } from \"./core/use-data-table\"\n\nexport {\n  DataTableConfigProvider,\n  useDataTableConfigContext,\n} from \"./core/config-context\"\nexport type { DataTableConfigContextValue } from \"./core/config-context\"\n\nexport { defaultIcons } from \"./core/icons\"\nexport type { DataTableIcons, IconComponent } from \"./core/icons\"\n\nexport { defaultLocalization } from \"./core/localization\"\nexport type { DataTableLocalization } from \"./core/localization\"\n\nexport type {\n  Density,\n  FilterVariant,\n  FilterMode,\n  GlobalFilterMode,\n  DataTableFilterOption,\n  AdvancedFilterOperator,\n  AdvancedFilterRule,\n  AdvancedFilterGroup,\n  DataTableConfig,\n  DataTableInstance,\n  DataTableSlotProps,\n  UseDataTableOptions,\n  EditDisplayMode,\n  EditVariant,\n  EditingCell,\n  RowEvent,\n  CellEvent,\n  DataTableRefs,\n  DataTableRowVirtualizer,\n  DataTableColumnVirtualizer,\n  RowVirtualizerOptions,\n  ColumnVirtualizerOptions,\n} from \"./core/types\"\n"
    },
    {
      "path": "ui/data-table/injected-columns/data-table-row-actions.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/injected-columns/data-table-row-actions.tsx",
      "content": "\"use client\"\n\nimport type { ColumnDef, Row, RowData } from \"@tanstack/react-table\"\n\nimport { Button } from \"@/components/ui/button\"\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuTrigger,\n} from \"@/components/ui/dropdown-menu\"\n\nimport type { DataTableInstance } from \"../core/types\"\n\nexport const ROW_ACTIONS_COLUMN_ID = \"cn-row-actions\"\n\n/** Actions column: edit/save/cancel controls + the consumer's\n *  `renderRowActions` slot. When positioned `\"first\"` the controls align to the\n *  left edge; when `\"last\"` (default) they align to the right. */\nexport function createRowActionsColumn<TData extends RowData>(\n  position: \"first\" | \"last\" = \"last\"\n): ColumnDef<TData> {\n  const align = position === \"first\" ? \"left\" : \"right\"\n  return {\n    id: ROW_ACTIONS_COLUMN_ID,\n    enableSorting: false,\n    enableHiding: false,\n    enableColumnFilter: false,\n    enableResizing: false,\n    enableGrouping: false,\n    size: 90,\n    minSize: 80,\n    meta: { disableColumnActions: true, align },\n    header: () => null,\n    cell: ({ row, table }) => (\n      <RowActionsCell\n        row={row}\n        table={table as DataTableInstance<TData>}\n        align={align}\n      />\n    ),\n  }\n}\n\nfunction RowActionsCell<TData extends RowData>({\n  row,\n  table,\n  align,\n}: {\n  row: Row<TData>\n  table: DataTableInstance<TData>\n  align: \"left\" | \"right\"\n}) {\n  const justify = align === \"left\" ? \"justify-start\" : \"justify-end\"\n  const {\n    localization,\n    icons,\n    enableEditing,\n    editDisplayMode,\n    editingRowId,\n    rowDraft,\n    onSaveRow,\n    beginRowEdit,\n    cancelEdit,\n    renderRowActions,\n    renderRowActionMenuItems,\n  } = table.tableInstance\n\n  const isEditingThisRow = editDisplayMode === \"row\" && editingRowId === row.id\n\n  if (isEditingThisRow) {\n    return (\n      <div className={`flex items-center gap-1 ${justify}`}>\n        <Button\n          variant=\"ghost\"\n          size=\"icon\"\n          aria-label={localization.save}\n          className=\"size-7\"\n          onClick={() =>\n            onSaveRow?.({ row, values: rowDraft, table, exit: cancelEdit })\n          }\n        >\n          <icons.save />\n        </Button>\n        <Button\n          variant=\"ghost\"\n          size=\"icon\"\n          aria-label={localization.cancel}\n          className=\"size-7\"\n          onClick={cancelEdit}\n        >\n          <icons.cancel />\n        </Button>\n      </div>\n    )\n  }\n\n  const canInlineEdit =\n    enableEditing && (editDisplayMode === \"row\" || editDisplayMode === \"modal\")\n\n  return (\n    <div className={`flex items-center gap-1 ${justify}`}>\n      {canInlineEdit && (\n        <Button\n          variant=\"ghost\"\n          size=\"icon\"\n          aria-label={localization.edit}\n          className=\"size-7\"\n          onClick={() => beginRowEdit(row)}\n        >\n          <icons.edit />\n        </Button>\n      )}\n      {renderRowActions?.({ row, table })}\n      {renderRowActionMenuItems && (\n        <DropdownMenu>\n          <DropdownMenuTrigger asChild>\n            <Button\n              variant=\"ghost\"\n              size=\"icon\"\n              aria-label={localization.rowActions}\n              className=\"size-7\"\n            >\n              <icons.columnActions />\n            </Button>\n          </DropdownMenuTrigger>\n          <DropdownMenuContent align=\"end\">\n            {renderRowActionMenuItems({ row, table })}\n          </DropdownMenuContent>\n        </DropdownMenu>\n      )}\n    </div>\n  )\n}\n"
    },
    {
      "path": "ui/data-table/injected-columns/expand-column.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/injected-columns/expand-column.tsx",
      "content": "\"use client\"\n\nimport type { ColumnDef, RowData } from \"@tanstack/react-table\"\n\nimport type { DataTableIcons } from \"../core/icons\"\nimport type { DataTableLocalization } from \"../core/localization\"\n\nexport const EXPAND_COLUMN_ID = \"cn-expand\"\n\n/** Expand/collapse column for detail panels and tree (sub-row) expansion. */\nexport function createExpandColumn<TData extends RowData>(\n  localization: DataTableLocalization,\n  icons: DataTableIcons\n): ColumnDef<TData> {\n  return {\n    id: EXPAND_COLUMN_ID,\n    enableSorting: false,\n    enableHiding: false,\n    enableColumnFilter: false,\n    enableResizing: false,\n    size: 44,\n    minSize: 44,\n    meta: { disableColumnActions: true },\n    header: ({ table }) =>\n      table.getCanSomeRowsExpand() ? (\n        <button\n          type=\"button\"\n          aria-label={localization.expandAll}\n          onClick={table.getToggleAllRowsExpandedHandler()}\n          className=\"flex items-center justify-center text-muted-foreground transition-colors outline-none hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring/40\"\n        >\n          {table.getIsAllRowsExpanded() ? (\n            <icons.expanded className=\"size-4\" />\n          ) : (\n            <icons.collapsed className=\"size-4\" />\n          )}\n        </button>\n      ) : null,\n    cell: ({ row }) => {\n      if (!row.getCanExpand()) return null\n      return (\n        <button\n          type=\"button\"\n          aria-label={localization.toggleRowExpanded}\n          aria-expanded={row.getIsExpanded()}\n          onClick={row.getToggleExpandedHandler()}\n          className=\"flex items-center justify-center text-muted-foreground transition-colors outline-none hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring/40\"\n        >\n          {row.getIsExpanded() ? (\n            <icons.expanded className=\"size-4\" />\n          ) : (\n            <icons.collapsed className=\"size-4\" />\n          )}\n        </button>\n      )\n    },\n  }\n}\n"
    },
    {
      "path": "ui/data-table/injected-columns/injected-columns.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/injected-columns/injected-columns.tsx",
      "content": "\"use client\"\n\n// Barrel for the injected leading/trailing column factories (TanStack \"display\"\n// columns — no data accessor). Each lives in its own file; selection and\n// row-actions columns are siblings in this folder. Many modules import the\n// column-id constants from here.\n\nexport { EXPAND_COLUMN_ID, createExpandColumn } from \"./expand-column\"\nexport {\n  ROW_DRAG_COLUMN_ID,\n  RowDragContext,\n  createRowDragHandleColumn,\n  type RowDragHandleProps,\n} from \"./row-drag-column\"\nexport {\n  ROW_NUMBER_COLUMN_ID,\n  createRowNumberColumn,\n} from \"./row-number-column\"\n"
    },
    {
      "path": "ui/data-table/injected-columns/row-drag-column.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/injected-columns/row-drag-column.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport type { ColumnDef, RowData } from \"@tanstack/react-table\"\n\nimport { Button } from \"@/components/ui/button\"\n\nimport type { DataTableIcons, IconComponent } from \"../core/icons\"\nimport type { DataTableLocalization } from \"../core/localization\"\n\nexport const ROW_DRAG_COLUMN_ID = \"cn-row-drag\"\n\n/** dnd-kit activator props for the current row's drag handle. */\nexport interface RowDragHandleProps {\n  attributes: Record<string, unknown>\n  listeners: Record<string, unknown> | undefined\n  setActivatorNodeRef: (el: HTMLElement | null) => void\n}\n\nexport const RowDragContext = React.createContext<RowDragHandleProps | null>(\n  null\n)\n\n/** Drag-handle column for row ordering. The handle reads dnd-kit props from\n *  {@link RowDragContext}, set by the sortable row wrapper. */\nexport function createRowDragHandleColumn<TData extends RowData>(\n  localization: DataTableLocalization,\n  icons: DataTableIcons\n): ColumnDef<TData> {\n  return {\n    id: ROW_DRAG_COLUMN_ID,\n    enableSorting: false,\n    enableHiding: false,\n    enableColumnFilter: false,\n    enableResizing: false,\n    size: 40,\n    minSize: 40,\n    meta: { disableColumnActions: true, align: \"center\" },\n    header: () => null,\n    cell: () => (\n      <RowDragHandle label={localization.reorderRow} Icon={icons.dragHandle} />\n    ),\n  }\n}\n\nfunction RowDragHandle({\n  label,\n  Icon,\n}: {\n  label: string\n  Icon: IconComponent\n}) {\n  const ctx = React.useContext(RowDragContext)\n  return (\n    <Button\n      type=\"button\"\n      variant=\"ghost\"\n      size=\"icon\"\n      aria-label={label}\n      ref={ctx?.setActivatorNodeRef}\n      suppressHydrationWarning\n      {...(ctx?.attributes ?? {})}\n      {...(ctx?.listeners ?? {})}\n      // touch-none: let dnd-kit's pointer sensor own the touch gesture\n      // (otherwise the browser scrolls/selects text before a drag can start on\n      // a phone). size-7 gives a finger-friendly tap target.\n      className=\"size-7 cursor-grab touch-none text-muted-foreground active:cursor-grabbing\"\n    >\n      <Icon className=\"size-4\" />\n    </Button>\n  )\n}\n"
    },
    {
      "path": "ui/data-table/injected-columns/row-number-column.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/injected-columns/row-number-column.tsx",
      "content": "\"use client\"\n\nimport type { ColumnDef, RowData } from \"@tanstack/react-table\"\n\nimport { cn } from \"@/lib/utils\"\n\nimport type { DataTableIcons } from \"../core/icons\"\nimport type { DataTableLocalization } from \"../core/localization\"\n\nexport const ROW_NUMBER_COLUMN_ID = \"cn-row-number\"\n\n/** Row-number column. `static` numbers track the current view (page-aware);\n *  `original` uses the stable source index. Optionally shows a pin toggle. */\nexport function createRowNumberColumn<TData extends RowData>(\n  localization: DataTableLocalization,\n  mode: \"static\" | \"original\",\n  enableRowPinning: boolean,\n  icons: DataTableIcons\n): ColumnDef<TData> {\n  return {\n    id: ROW_NUMBER_COLUMN_ID,\n    enableSorting: false,\n    enableHiding: false,\n    enableColumnFilter: false,\n    enableResizing: false,\n    size: 56,\n    minSize: 48,\n    meta: { disableColumnActions: true, align: \"center\", label: \"#\" },\n    header: () => <span className=\"text-muted-foreground\">#</span>,\n    cell: ({ row, table }) => {\n      const number =\n        mode === \"original\"\n          ? row.index + 1\n          : table.getRowModel().rows.indexOf(row) +\n            1 +\n            table.getState().pagination.pageIndex *\n              table.getState().pagination.pageSize\n\n      if (!enableRowPinning) {\n        return (\n          <span className=\"text-xs text-muted-foreground tabular-nums\">\n            {number}\n          </span>\n        )\n      }\n\n      const pinned = row.getIsPinned()\n      return (\n        <span className=\"group/rownum relative flex items-center justify-center\">\n          <span\n            className={cn(\n              \"text-xs text-muted-foreground tabular-nums\",\n              \"group-hover/rownum:opacity-0\"\n            )}\n          >\n            {number}\n          </span>\n          <button\n            type=\"button\"\n            aria-label={pinned ? localization.unpinRow : localization.pinRow}\n            onClick={() => row.pin(pinned ? false : \"top\")}\n            className={cn(\n              \"absolute inset-0 flex items-center justify-center text-muted-foreground opacity-0 transition-opacity group-hover/rownum:opacity-100 hover:text-foreground focus-visible:opacity-100\",\n              pinned && \"text-primary opacity-100\"\n            )}\n          >\n            {pinned ? (\n              <icons.pinnedRow className=\"size-3.5\" />\n            ) : (\n              <icons.pin className=\"size-3.5\" />\n            )}\n          </button>\n        </span>\n      )\n    },\n  }\n}\n"
    },
    {
      "path": "ui/data-table/injected-columns/selection-column.tsx",
      "type": "registry:ui",
      "target": "components/ui/data-table/injected-columns/selection-column.tsx",
      "content": "\"use client\"\n\nimport type { ColumnDef, RowData } from \"@tanstack/react-table\"\n\nimport { SelectionCheckbox } from \"../components/body/selection-checkbox\"\nimport type { DataTableLocalization } from \"../core/localization\"\n\nexport const SELECTION_COLUMN_ID = \"cn-select\"\n\n/**\n * Builds the auto-injected selection column. The header carries the\n * select-all checkbox (with indeterminate state) for multi-select tables; for\n * single-select tables the header is empty. `selectAllMode` selects the current\n * page (\"page\", default) or every row (\"all\"); `enableSelectAll: false` hides\n * the header checkbox. Clicks are isolated from row click handlers via\n * `stopPropagation`.\n */\nexport function createSelectionColumn<TData extends RowData>(\n  localization: DataTableLocalization,\n  selectAllMode: \"page\" | \"all\" = \"page\",\n  enableSelectAll = true\n): ColumnDef<TData> {\n  return {\n    id: SELECTION_COLUMN_ID,\n    enableSorting: false,\n    enableHiding: false,\n    enableColumnFilter: false,\n    enableResizing: false,\n    size: 44,\n    minSize: 44,\n    meta: { disableColumnActions: true, align: \"center\" },\n    header: ({ table }) => {\n      // No select-all affordance for single-select tables or when disabled.\n      if (!enableSelectAll) return null\n      if (table.options.enableMultiRowSelection === false) return null\n      const allMode = selectAllMode === \"all\"\n      const allSelected = allMode\n        ? table.getIsAllRowsSelected()\n        : table.getIsAllPageRowsSelected()\n      const someSelected = allMode\n        ? table.getIsSomeRowsSelected()\n        : table.getIsSomePageRowsSelected()\n      return (\n        <div\n          className=\"flex items-center justify-center\"\n          onClick={(e) => e.stopPropagation()}\n        >\n          <SelectionCheckbox\n            aria-label={localization.selectAll}\n            checked={allSelected}\n            indeterminate={someSelected && !allSelected}\n            onCheckedChange={(value) =>\n              allMode\n                ? table.toggleAllRowsSelected(!!value)\n                : table.toggleAllPageRowsSelected(!!value)\n            }\n          />\n        </div>\n      )\n    },\n    cell: ({ row }) => (\n      <div\n        className=\"flex items-center justify-center\"\n        onClick={(e) => e.stopPropagation()}\n      >\n        <SelectionCheckbox\n          aria-label={localization.selectRow}\n          checked={row.getIsSelected()}\n          disabled={!row.getCanSelect()}\n          onCheckedChange={(value) => row.toggleSelected(!!value)}\n        />\n      </div>\n    ),\n  }\n}\n"
    },
    {
      "path": "ui/data-table/utils/column-styles.ts",
      "type": "registry:ui",
      "target": "components/ui/data-table/utils/column-styles.ts",
      "content": "import type { Column, RowData, Table } from \"@tanstack/react-table\"\nimport type { CSSProperties } from \"react\"\n\nimport type { DataTableInstance } from \"../core/types\"\n\n/**\n * Sticky positioning for a pinned column. Offsets come from TanStack\n * (`getStart`/`getAfter`) so multiple pinned columns stack correctly.\n */\nexport function getColumnPinningStyle<TData extends RowData, TValue>(\n  column: Column<TData, TValue>\n): CSSProperties {\n  const pinned = column.getIsPinned()\n  if (!pinned) return {}\n  return {\n    position: \"sticky\",\n    zIndex: 2,\n    ...(pinned === \"left\"\n      ? { left: column.getStart(\"left\") }\n      : { right: column.getAfter(\"right\") }),\n  }\n}\n\n/**\n * Edge-shadow class for the boundary pinned columns (last left / first right),\n * built from `--border` (via a utility) so it reads in light and dark.\n */\nexport function getColumnPinningClass<TData extends RowData, TValue>(\n  column: Column<TData, TValue>\n): string {\n  const pinned = column.getIsPinned()\n  if (!pinned) return \"\"\n  const isLastLeft = pinned === \"left\" && column.getIsLastColumn(\"left\")\n  const isFirstRight = pinned === \"right\" && column.getIsFirstColumn(\"right\")\n  if (isLastLeft) {\n    return \"after:pointer-events-none after:absolute after:inset-y-0 after:-right-px after:w-2 after:translate-x-full after:bg-gradient-to-r after:from-border/60 after:to-transparent\"\n  }\n  if (isFirstRight) {\n    return \"before:pointer-events-none before:absolute before:inset-y-0 before:-left-px before:w-2 before:-translate-x-full before:bg-gradient-to-l before:from-border/60 before:to-transparent\"\n  }\n  return \"\"\n}\n\n/**\n * CSS custom properties holding each column's size, set once on the table\n * element. Cells read them via `width: calc(var(--col-…-size) * 1px)` so a\n * resize drag updates a variable instead of re-rendering every cell.\n */\nexport function getColumnSizeVars<TData extends RowData>(\n  table: Table<TData>\n): Record<string, string> {\n  const headers = table.getFlatHeaders()\n  const vars: Record<string, string> = {}\n  for (const header of headers) {\n    vars[`--header-${header.id}-size`] = `${header.getSize()}`\n    vars[`--col-${header.column.id}-size`] = `${header.column.getSize()}`\n  }\n  return vars\n}\n\nexport function getColumnWidthStyle(columnId: string): CSSProperties {\n  return { width: `calc(var(--col-${columnId}-size) * 1px)` }\n}\n\n/**\n * Resolves a column's width style. While resizing, widths come from CSS vars.\n * Otherwise honor an explicitly defined `columnDef.size` (MRT behaviour) but\n * leave unsized columns to the browser's auto layout so the table still fills\n * its container. Column virtualization needs every column to have a concrete\n * width.\n */\nexport function getWidthStyle<TData extends RowData>(\n  column: Column<TData, unknown>,\n  table: DataTableInstance<TData>\n): CSSProperties {\n  const { enableColumnResizing, enableColumnVirtualization } =\n    table.tableInstance\n  if (enableColumnResizing) return getColumnWidthStyle(column.id)\n  if (enableColumnVirtualization || column.columnDef.size != null) {\n    const size = column.getSize()\n    return { width: size, minWidth: size }\n  }\n  return {}\n}\n"
    }
  ],
  "cssVars": {
    "theme": {
      "color-highlight": "var(--highlight, var(--accent))",
      "color-highlight-foreground": "var(--highlight-foreground, var(--accent-foreground))"
    },
    "light": {
      "highlight": "oklch(0.905 0.158 96.5)",
      "highlight-foreground": "oklch(0.35 0.07 72)"
    },
    "dark": {
      "highlight": "oklch(0.85 0.16 96.5)",
      "highlight-foreground": "oklch(0.26 0.05 72)"
    }
  }
}