import { DndContext, KeyboardSensor, PointerSensor, useSensor, useSensors } from '@dnd-kit/core'; import type { DragEndEvent } from '@dnd-kit/core'; import { SortableContext, useSortable, verticalListSortingStrategy, } from '@dnd-kit/sortable'; import { CSS } from '@dnd-kit/utilities'; import { flexRender, getCoreRowModel, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, useReactTable } from '@tanstack/react-table'; import type { ColumnDef, ColumnFiltersState, SortingState } from '@tanstack/react-table'; import { GripVertical } from 'lucide-react'; import { ChevronLeft, ChevronRight } from 'lucide-react'; import * as React from 'react'; import { Button } from '@/components/ui/button'; import { Card, CardContent } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table'; interface DataTableProps { columns: ColumnDef[]; data: TData[]; searchKey?: string; searchPlaceholder?: string; emptyText?: string; onReorder?: (items: TData[]) => void; getRowId?: (item: TData) => string | number; } const DragHandleContext = React.createContext<{ listeners?: Record; attributes?: Record; }>({}); export function DragHandleTrigger() { const { listeners, attributes } = React.useContext(DragHandleContext); return ( ); } function SortableTableRow({ id, children, }: { id: string; children: React.ReactNode; }) { const { attributes, listeners, setNodeRef, transform, transition, isDragging, } = useSortable({ id }); const style = { transform: CSS.Transform.toString(transform), transition, opacity: isDragging ? 0.4 : undefined, }; return ( {children} ); } export function DataTable({ columns, data, searchKey, searchPlaceholder = 'Cari...', emptyText = 'Tidak ada data.', onReorder, getRowId, }: DataTableProps) { const [sorting, setSorting] = React.useState([]); const [columnFilters, setColumnFilters] = React.useState([]); const isSortable = !!onReorder && !!getRowId; const visibleColumns = isSortable ? [ { id: 'drag', header: '', cell: () => , meta: { className: 'w-[40px]', headerClassName: 'w-[40px]', }, } as ColumnDef, ...columns, ] : columns; const sensors = useSensors( useSensor(PointerSensor, { activationConstraint: { distance: 8 }, }), useSensor(KeyboardSensor), ); const table = useReactTable({ data, columns: visibleColumns, getCoreRowModel: getCoreRowModel(), getPaginationRowModel: getPaginationRowModel(), onSortingChange: setSorting, getSortedRowModel: getSortedRowModel(), onColumnFiltersChange: setColumnFilters, getFilteredRowModel: getFilteredRowModel(), state: { sorting, columnFilters, }, }); const itemIds = React.useMemo( () => data.map((item) => String(getRowId ? getRowId(item) : '')), [data, getRowId], ); function handleDragEnd(event: DragEndEvent) { const { active, over } = event; if (!over || active.id === over.id) { return; } const oldIndex = itemIds.indexOf(String(active.id)); const newIndex = itemIds.indexOf(String(over.id)); if (oldIndex === -1 || newIndex === -1) { return; } const reordered = [...data]; const [moved] = reordered.splice(oldIndex, 1); reordered.splice(newIndex, 0, moved); onReorder?.(reordered); } return ( <> {searchKey && ( table .getColumn(searchKey) ?.setFilterValue(event.target.value) } className="max-w-sm" /> )} {table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => ( {header.isPlaceholder ? null : flexRender( header.column.columnDef .header, header.getContext(), )} ))} ))} {table.getRowModel().rows?.length ? ( isSortable ? ( {table .getRowModel() .rows.map((row) => ( {row .getVisibleCells() .map((cell) => ( {flexRender( cell .column .columnDef .cell, cell.getContext(), )} ))} ))} ) : ( table .getRowModel() .rows.map((row) => ( {row .getVisibleCells() .map((cell) => ( {flexRender( cell.column .columnDef .cell, cell.getContext(), )} ))} )) ) ) : ( {emptyText} )}
Halaman {table.getState().pagination.pageIndex + 1} dari{' '} {table.getPageCount()}
); }