{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "data-table-8",
  "type": "registry:component",
  "title": "DataTable 8",
  "description": "DataTable 8. A component for displaying and managing tabular data.",
  "dependencies": [
    "@dnd-kit/core",
    "@dnd-kit/modifiers",
    "@dnd-kit/sortable",
    "@dnd-kit/utilities",
    "lucide-react"
  ],
  "registryDependencies": [
    "button",
    "table"
  ],
  "files": [
    {
      "path": "components/watermelon/data-table-8.tsx",
      "type": "registry:component",
      "content": "'use client'\n\nimport { useMemo, useState } from 'react'\n\nimport { ChevronDownIcon, ChevronUpIcon, GripVerticalIcon } from 'lucide-react'\n\nimport {\n  closestCenter,\n  DndContext,\n  KeyboardSensor,\n  MouseSensor,\n  TouchSensor,\n  useSensor,\n  useSensors,\n  type DragEndEvent\n} from '@dnd-kit/core'\nimport { restrictToHorizontalAxis } from '@dnd-kit/modifiers'\nimport { arrayMove, horizontalListSortingStrategy, SortableContext, useSortable } from '@dnd-kit/sortable'\nimport { CSS } from '@dnd-kit/utilities'\n\nimport { Button } from '@/components/base-ui/button'\nimport { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/base-ui/table'\n\ntype Employee = {\n  department: string\n  dob: string\n  employeeId: number\n  firstName: string\n  hireDate: string\n  jobTitle: string\n  lastName: string\n  salary: number\n}\n\ntype ColumnKey = 'department' | 'dob' | 'firstName' | 'hireDate' | 'jobTitle' | 'lastName' | 'salary'\n\ntype SortDirection = 'asc' | 'desc'\n\ntype SortConfig = {\n  column: ColumnKey\n  direction: SortDirection\n}\n\ntype ColumnConfig = {\n  key: ColumnKey\n  label: string\n}\n\nconst data: readonly Employee[] = [\n  {\n    employeeId: 1,\n    firstName: 'John',\n    lastName: 'Doe',\n    jobTitle: 'Software Engineer',\n    department: 'Engineering',\n    dob: '1990-01-01',\n    hireDate: '2020-01-15',\n    salary: 80000\n  },\n  {\n    employeeId: 2,\n    firstName: 'Jane',\n    lastName: 'Smith',\n    jobTitle: 'Product Manager',\n    department: 'Product',\n    dob: '1985-05-20',\n    hireDate: '2019-03-10',\n    salary: 95000\n  },\n  {\n    employeeId: 3,\n    firstName: 'Alice',\n    lastName: 'Johnson',\n    jobTitle: 'UX Designer',\n    department: 'Design',\n    dob: '1992-07-30',\n    hireDate: '2021-06-01',\n    salary: 70000\n  },\n  {\n    employeeId: 4,\n    firstName: 'Bob',\n    lastName: 'Brown',\n    jobTitle: 'Data Analyst',\n    department: 'Analytics',\n    dob: '1988-11-15',\n    hireDate: '2018-09-20',\n    salary: 75000\n  }\n] as const\n\nconst defaultColumns: readonly ColumnConfig[] = [\n  { key: 'firstName', label: 'First Name' },\n  { key: 'lastName', label: 'Last Name' },\n  { key: 'jobTitle', label: 'Job Title' },\n  { key: 'department', label: 'Department' },\n  { key: 'dob', label: 'Date of Birth' },\n  { key: 'hireDate', label: 'Hire Date' },\n  { key: 'salary', label: 'Salary' }\n] as const\n\nconst formatCurrency = (salary: number) =>\n  new Intl.NumberFormat('en-US', {\n    style: 'currency',\n    currency: 'USD'\n  }).format(salary)\n\nconst renderValue = (employee: Employee, key: ColumnKey) => {\n  switch (key) {\n    case 'firstName':\n      return <div className='font-medium'>{employee.firstName}</div>\n    case 'lastName':\n      return employee.lastName\n    case 'jobTitle':\n      return employee.jobTitle\n    case 'department':\n      return employee.department\n    case 'dob':\n      return employee.dob\n    case 'hireDate':\n      return employee.hireDate\n    case 'salary':\n      return formatCurrency(employee.salary)\n  }\n}\n\nconst DraggableHeader = ({\n  column,\n  direction,\n  onToggleSort\n}: {\n  column: ColumnConfig\n  direction?: SortDirection\n  onToggleSort: (column: ColumnKey) => void\n}) => {\n  const { attributes, isDragging, listeners, setNodeRef, transform, transition } = useSortable({\n    id: column.key\n  })\n\n  return (\n    <TableHead\n      ref={setNodeRef}\n      className='relative h-12 bg-muted/20 text-[13px] font-medium tracking-[0.08em] text-muted-foreground'\n      style={{\n        opacity: isDragging ? 0.85 : 1,\n        transform: CSS.Transform.toString(transform),\n        transition\n      }}\n      aria-sort={direction === 'asc' ? 'ascending' : direction === 'desc' ? 'descending' : 'none'}\n    >\n      <div className='flex items-center justify-between gap-2'>\n        <div className='flex items-center gap-1'>\n          <Button\n            size='icon'\n            variant='ghost'\n            className='-ml-2 size-7 text-muted-foreground hover:bg-background hover:text-foreground'\n            {...attributes}\n            {...listeners}\n            aria-label='Drag to reorder'\n          >\n            <GripVerticalIcon className='size-4 opacity-60' aria-hidden='true' />\n          </Button>\n          <span className='truncate'>{column.label}</span>\n        </div>\n\n        <Button\n          size='icon'\n          variant='ghost'\n          className='group -mr-1 size-7 text-muted-foreground hover:bg-background hover:text-foreground'\n          onClick={() => onToggleSort(column.key)}\n          aria-label={`Sort by ${column.label}`}\n        >\n          {direction === 'asc' ? (\n            <ChevronUpIcon className='size-4 opacity-60' aria-hidden='true' />\n          ) : direction === 'desc' ? (\n            <ChevronDownIcon className='size-4 opacity-60' aria-hidden='true' />\n          ) : (\n            <ChevronUpIcon className='size-4 opacity-0 group-hover:opacity-60' aria-hidden='true' />\n          )}\n        </Button>\n      </div>\n    </TableHead>\n  )\n}\n\nconst DataTable8 = () => {\n  const [columnOrder, setColumnOrder] = useState<ColumnKey[]>(defaultColumns.map((column) => column.key))\n  const [sortConfig, setSortConfig] = useState<SortConfig>({\n    column: 'firstName',\n    direction: 'asc'\n  })\n\n  const sensors = useSensors(useSensor(MouseSensor), useSensor(TouchSensor), useSensor(KeyboardSensor))\n\n  const orderedColumns = useMemo(\n    () =>\n      columnOrder\n        .map((key) => defaultColumns.find((column) => column.key === key))\n        .filter((column): column is ColumnConfig => Boolean(column)),\n    [columnOrder]\n  )\n\n  const sortedData = useMemo(() => {\n    const sorted = [...data]\n\n    sorted.sort((left, right) => {\n      const leftValue = left[sortConfig.column]\n      const rightValue = right[sortConfig.column]\n\n      const comparison =\n        typeof leftValue === 'number' && typeof rightValue === 'number'\n          ? leftValue - rightValue\n          : String(leftValue).localeCompare(String(rightValue))\n\n      return sortConfig.direction === 'asc' ? comparison : -comparison\n    })\n\n    return sorted\n  }, [sortConfig])\n\n  const handleDragEnd = (event: DragEndEvent) => {\n    const { active, over } = event\n\n    if (!over || active.id === over.id) {\n      return\n    }\n\n    setColumnOrder((current) => {\n      const oldIndex = current.indexOf(active.id as ColumnKey)\n      const newIndex = current.indexOf(over.id as ColumnKey)\n\n      return arrayMove(current, oldIndex, newIndex)\n    })\n  }\n\n  const toggleSort = (column: ColumnKey) => {\n    setSortConfig((current) => {\n      if (current.column === column) {\n        return {\n          column,\n          direction: current.direction === 'asc' ? 'desc' : 'asc'\n        }\n      }\n\n      return {\n        column,\n        direction: 'asc'\n      }\n    })\n  }\n\n  return (\n    <div className='w-60 sm:w-100 md:w-120 lg:w-160 2xl:w-220 mx-auto'>\n      <div className='overflow-hidden rounded-xl border border-border/60 bg-background shadow-sm'>\n        <DndContext\n          collisionDetection={closestCenter}\n          modifiers={[restrictToHorizontalAxis]}\n          onDragEnd={handleDragEnd}\n          sensors={sensors}\n        >\n          <Table>\n            <TableHeader>\n              <TableRow className='bg-muted/20 [&>th]:border-t-0'>\n                <SortableContext items={columnOrder} strategy={horizontalListSortingStrategy}>\n                  {orderedColumns.map((column) => (\n                    <DraggableHeader\n                      key={column.key}\n                      column={column}\n                      direction={sortConfig.column === column.key ? sortConfig.direction : undefined}\n                      onToggleSort={toggleSort}\n                    />\n                  ))}\n                </SortableContext>\n              </TableRow>\n            </TableHeader>\n            <TableBody>\n              {sortedData.map((employee) => (\n                <TableRow key={employee.employeeId} className='hover:bg-muted/10'>\n                  {orderedColumns.map((column) => (\n                    <TableCell key={column.key} className='truncate py-3.5'>\n                      {renderValue(employee, column.key)}\n                    </TableCell>\n                  ))}\n                </TableRow>\n              ))}\n            </TableBody>\n          </Table>\n        </DndContext>\n      </div>\n    </div>\n  )\n}\n\nexport default DataTable8\n"
    }
  ]
}
