UNPKG

@moontra/moonui-pro

Version:

Premium React components for MoonUI - Advanced UI library with 50+ pro components including performance, interactive, and gesture components

183 lines (158 loc) 5.17 kB
"use client" import React from 'react' import { ColumnDef } from '@tanstack/react-table' export interface UseDataTableOptions<TData> { data: TData[] columns: ColumnDef<TData, any>[] searchable?: boolean filterable?: boolean sortable?: boolean pagination?: boolean pageSize?: number enableRowSelection?: boolean enableMultiRowSelection?: boolean } export interface UseDataTableReturn<TData> { // Table state filteredData: TData[] selectedRows: TData[] searchQuery: string currentPage: number totalPages: number // Actions setSearchQuery: (query: string) => void setCurrentPage: (page: number) => void selectRow: (row: TData) => void selectAllRows: () => void clearSelection: () => void exportData: (format?: 'csv' | 'json') => void // Utilities getRowCount: () => number getSelectedCount: () => number isRowSelected: (row: TData) => boolean } export function useDataTable<TData extends Record<string, any>>( options: UseDataTableOptions<TData> ): UseDataTableReturn<TData> { const { data, searchable = true, pageSize = 10, enableRowSelection = false, enableMultiRowSelection = true, } = options const [searchQuery, setSearchQuery] = React.useState('') const [currentPage, setCurrentPage] = React.useState(1) const [selectedRows, setSelectedRows] = React.useState<TData[]>([]) // Filter data based on search query const filteredData = React.useMemo(() => { if (!searchable || !searchQuery.trim()) { return data } return data.filter((row) => { return Object.values(row).some((value) => { if (value == null) return false return String(value).toLowerCase().includes(searchQuery.toLowerCase()) }) }) }, [data, searchQuery, searchable]) // Calculate pagination const totalPages = Math.ceil(filteredData.length / pageSize) const paginatedData = React.useMemo(() => { const startIndex = (currentPage - 1) * pageSize const endIndex = startIndex + pageSize return filteredData.slice(startIndex, endIndex) }, [filteredData, currentPage, pageSize]) // Row selection handlers const selectRow = React.useCallback((row: TData) => { if (!enableRowSelection) return setSelectedRows((prev) => { if (!enableMultiRowSelection) { return [row] } const isSelected = prev.some((selectedRow) => JSON.stringify(selectedRow) === JSON.stringify(row) ) if (isSelected) { return prev.filter((selectedRow) => JSON.stringify(selectedRow) !== JSON.stringify(row) ) } else { return [...prev, row] } }) }, [enableRowSelection, enableMultiRowSelection]) const selectAllRows = React.useCallback(() => { if (!enableRowSelection) return setSelectedRows((prev) => { if (prev.length === filteredData.length) { return [] } else { return [...filteredData] } }) }, [enableRowSelection, filteredData]) const clearSelection = React.useCallback(() => { setSelectedRows([]) }, []) const isRowSelected = React.useCallback((row: TData) => { return selectedRows.some((selectedRow) => JSON.stringify(selectedRow) === JSON.stringify(row) ) }, [selectedRows]) // Export functionality const exportData = React.useCallback((format: 'csv' | 'json' = 'csv') => { const dataToExport = selectedRows.length > 0 ? selectedRows : filteredData if (format === 'csv') { const headers = Object.keys(dataToExport[0] || {}) const csvContent = [ headers.join(','), ...dataToExport.map(row => headers.map(header => { const value = row[header] // Escape commas and quotes in CSV if (typeof value === 'string' && (value.includes(',') || value.includes('"'))) { return `"${value.replace(/"/g, '""')}"` } return value }).join(',') ) ].join('\n') const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }) const link = document.createElement('a') link.href = URL.createObjectURL(blob) link.download = 'data.csv' link.click() } else if (format === 'json') { const jsonContent = JSON.stringify(dataToExport, null, 2) const blob = new Blob([jsonContent], { type: 'application/json;charset=utf-8;' }) const link = document.createElement('a') link.href = URL.createObjectURL(blob) link.download = 'data.json' link.click() } }, [selectedRows, filteredData]) // Utility functions const getRowCount = React.useCallback(() => filteredData.length, [filteredData]) const getSelectedCount = React.useCallback(() => selectedRows.length, [selectedRows]) // Reset page when search changes React.useEffect(() => { setCurrentPage(1) }, [searchQuery]) return { filteredData: paginatedData, selectedRows, searchQuery, currentPage, totalPages, setSearchQuery, setCurrentPage, selectRow, selectAllRows, clearSelection, exportData, getRowCount, getSelectedCount, isRowSelected, } }