@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
text/typescript
"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,
}
}