{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "data-table",
  "type": "registry:ui",
  "title": "Data Table",
  "description": "Data Table is an interactive data-table pattern composing Table with header sorting and external Pagination — no columns-config prop; the consumer writes their own Table/TableHead/TableBody markup and drops in DataTableSortHeader for sortable columns.",
  "author": "Skrewww",
  "dependencies": [
    "@phosphor-icons/react"
  ],
  "registryDependencies": [
    "@skrewww/table",
    "@skrewww/foundation"
  ],
  "docs": "Host requirements (assumed already present, not installed by this command): react, react-dom.",
  "files": [
    {
      "path": "components/ui/DataTableSortHeader.tsx",
      "content": "/** @skrewww-component data-table */\n\"use client\";\n\nimport { CaretDown, CaretUp, CaretUpDown } from \"@phosphor-icons/react\";\nimport type { ReactNode } from \"react\";\nimport { cn } from \"@/lib/cn\";\nimport { TableHead, type TableHeadProps } from \"@/components/ui/Table\";\nimport type { DataTableSortColumnState } from \"@/lib/use-data-table-sort\";\nimport styles from \"@/components/ui/data-table-sort-header.module.css\";\n\nexport type DataTableSortHeaderProps = Omit<TableHeadProps, \"children\"> & {\n  /** This column's current sort state — \"none\" when a different column (or nothing) is sorted. */\n  sortDirection: DataTableSortColumnState;\n  /** Called when the header is activated via click, Enter, or Space. */\n  onSort: () => void;\n  /** Column label. */\n  children: ReactNode;\n  disabled?: boolean;\n};\n\nconst directionIcon = {\n  none: CaretUpDown,\n  ascending: CaretUp,\n  descending: CaretDown,\n} as const;\n\n/**\n * Sortable column header for the Data Table pattern — composes TableHead\n * with a real <button>, aria-sort, and a direction indicator. Data Table has\n * no columns-config prop: the consumer still writes their own Table markup\n * and drops this in place of a plain TableHead for sortable columns.\n */\nexport function DataTableSortHeader({\n  sortDirection,\n  onSort,\n  children,\n  disabled = false,\n  className,\n  ...tableHeadProps\n}: DataTableSortHeaderProps) {\n  const Icon = directionIcon[sortDirection];\n\n  return (\n    <TableHead aria-sort={sortDirection} className={className} {...tableHeadProps}>\n      <button\n        type=\"button\"\n        className={styles.sortButton}\n        onClick={onSort}\n        disabled={disabled}\n      >\n        <span>{children}</span>\n        <Icon\n          size={14}\n          weight=\"bold\"\n          aria-hidden=\"true\"\n          className={cn(styles.icon, sortDirection !== \"none\" && styles.iconActive)}\n        />\n      </button>\n    </TableHead>\n  );\n}\n",
      "type": "registry:ui",
      "target": "~/components/ui/DataTableSortHeader.tsx"
    },
    {
      "path": "components/ui/data-table-sort-header.module.css",
      "content": "/* TEMPORARY — React-first Data Table MVP; Figma verification pending */\n\n.sortButton {\n  display: inline-flex;\n  align-items: center;\n  gap: 0.25rem;\n  background: transparent;\n  border: 0;\n  padding: 0;\n  margin: 0;\n  cursor: pointer;\n  font: inherit;\n  font-weight: inherit;\n  color: inherit;\n  text-align: inherit;\n}\n\n.sortButton:disabled {\n  cursor: not-allowed;\n}\n\n.sortButton:focus-visible {\n  outline: 2px solid var(--semantic-focus-ring);\n  outline-offset: 2px;\n  border-radius: 2px;\n}\n\n.icon {\n  flex-shrink: 0;\n  color: var(--semantic-icon-muted);\n}\n\n.iconActive {\n  color: var(--table-header-text);\n}\n",
      "type": "registry:ui",
      "target": "~/components/ui/data-table-sort-header.module.css"
    },
    {
      "path": "lib/cn.ts",
      "content": "export function cn(...classes: Array<string | false | null | undefined>): string {\n  return classes.filter(Boolean).join(\" \");\n}\n",
      "type": "registry:lib",
      "target": "~/lib/cn.ts"
    },
    {
      "path": "lib/use-controllable.ts",
      "content": "import { useCallback, useEffect, useRef, useState } from \"react\";\n\nexport function useControllableState<T>({\n  value,\n  defaultValue,\n  onChange,\n  controlled: controlledProp,\n  valueProvided,\n}: {\n  value?: T;\n  defaultValue?: T;\n  onChange?: (next: T) => void;\n  /** Explicit controlled override — prefer `valueProvided` from the component root. */\n  controlled?: boolean;\n  /** True when the parent passed a `value` prop, including `undefined`. */\n  valueProvided?: boolean;\n}) {\n  const [uncontrolled, setUncontrolled] = useState(defaultValue as T);\n  const isControlled = controlledProp ?? valueProvided ?? value !== undefined;\n  const current = isControlled ? (value as T) : uncontrolled;\n  const wasControlledRef = useRef(isControlled);\n\n  useEffect(() => {\n    if (process.env.NODE_ENV !== \"development\") return;\n    if (wasControlledRef.current !== isControlled) {\n      console.warn(\n        \"[Skrewww] A component changed between controlled and uncontrolled modes. Keep `value` or `defaultValue` usage consistent across renders.\",\n      );\n    }\n    wasControlledRef.current = isControlled;\n  }, [isControlled]);\n\n  const setValue = useCallback(\n    (next: T) => {\n      if (!isControlled) setUncontrolled(next);\n      onChange?.(next);\n    },\n    [isControlled, onChange],\n  );\n\n  return [current, setValue] as const;\n}\n",
      "type": "registry:lib",
      "target": "~/lib/use-controllable.ts"
    },
    {
      "path": "lib/use-data-table-sort.ts",
      "content": "import { useCallback } from \"react\";\nimport { useControllableState } from \"@/lib/use-controllable\";\n\nexport type DataTableSortDirection = \"ascending\" | \"descending\";\n\n/** Per-column sort direction as exposed to a header cell — includes \"none\" for unsorted columns. */\nexport type DataTableSortColumnState = \"ascending\" | \"descending\" | \"none\";\n\nexport type DataTableSortState<TColumn extends string = string> = {\n  column: TColumn | null;\n  direction: DataTableSortDirection;\n};\n\nfunction defaultSortState<TColumn extends string>(): DataTableSortState<TColumn> {\n  return { column: null, direction: \"ascending\" };\n}\n\nexport type UseDataTableSortOptions<TColumn extends string> = {\n  /** Controlled sort state. Pass alongside onSortStateChange; omit for uncontrolled usage. */\n  sortState?: DataTableSortState<TColumn>;\n  /** Initial sort state for uncontrolled usage. Ignored when `sortState` is provided. */\n  defaultSortState?: DataTableSortState<TColumn>;\n  onSortStateChange?: (next: DataTableSortState<TColumn>) => void;\n};\n\n/**\n * Single-column sort state for Data Table, dual controlled/uncontrolled via\n * useControllableState (same pattern as Accordion/Dialog/Drawer/CalendarGrid\n * range mode). Cycle per column: none -> ascending -> descending -> none.\n * Activating a different column always resets it to ascending.\n */\nexport function useDataTableSort<TColumn extends string>(\n  options: UseDataTableSortOptions<TColumn> = {},\n) {\n  const { sortState, defaultSortState: defaultValue = defaultSortState<TColumn>(), onSortStateChange } =\n    options;\n  const valueProvided = \"sortState\" in options;\n\n  const [state, setState] = useControllableState<DataTableSortState<TColumn>>({\n    value: sortState,\n    defaultValue,\n    onChange: onSortStateChange,\n    valueProvided,\n  });\n\n  const getSortDirection = useCallback(\n    (column: TColumn): DataTableSortColumnState => {\n      if (state.column !== column) return \"none\";\n      return state.direction;\n    },\n    [state.column, state.direction],\n  );\n\n  const toggleSort = useCallback(\n    (column: TColumn) => {\n      if (state.column !== column) {\n        setState({ column, direction: \"ascending\" });\n        return;\n      }\n      if (state.direction === \"ascending\") {\n        setState({ column, direction: \"descending\" });\n        return;\n      }\n      setState({ column: null, direction: \"ascending\" });\n    },\n    [state.column, state.direction, setState],\n  );\n\n  return { sortState: state, getSortDirection, toggleSort } as const;\n}\n",
      "type": "registry:lib",
      "target": "~/lib/use-data-table-sort.ts"
    }
  ]
}
