UNPKG

@kadconsulting/dry

Version:
200 lines 11.1 kB
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { useState, useEffect, useMemo, forwardRef } from 'react'; import { useTable, usePagination } from 'react-table'; import TableBody from './TableBody/TableBody'; import TableFooter from './TableFooter/TableFooter'; import TableHeader from './TableHeader/TableHeader'; import { handleRowSelect, handleRowExpansion, handleSortChange, handleColumnOrderChange, handleColumnResize, handlePageSizeChange, handleClickNext, handleClickPrevious, handleGoToPage, } from './TableHandlers'; import { FilterType, } from './DataTableTypes'; import classnames from 'classnames'; import './DataTable.scss'; import useWindowSize from '../../hooks/useWindowSize'; const DataTable = forwardRef((props, ref) => { const { id, className, columns: initialColumns, data, pageIndex, pageSize: initialPageSize, onPageChange, columnOrder: initialColumnOrder, columnWidths: initialColumnWidths, visibleColumns, pinnedColumns, setColumnOrder: setColumnOrderProp, setColumnWidths: setColumnWidthsProp, loading, filters, loadingRowCount, setPageIndex, handleRowClick, hasPageSizeInput, hasGoToPageInput, hasResize, isDraggable, badgeColumnConfig, handleDataFilterChange, hasAccordion, } = props; const windowSize = useWindowSize(); // Initialize state variables const [columns, setColumns] = useState(initialColumns); const [localColumnOrder, setLocalColumnOrder] = useState(initialColumnOrder); const [localColumnWidths, setLocalColumnWidths] = useState(initialColumnWidths); useEffect(() => { if (localColumnOrder.length > 0) { const newColumns = localColumnOrder .map((colId) => { return initialColumns.find((col) => col.id === colId); }) .filter(Boolean); if (newColumns.length === initialColumns.length) { setColumns(newColumns); } } }, [localColumnOrder, localColumnWidths]); // Find the default sorted column or use the first column const defaultSortedColumn = useMemo(() => { return initialColumns.find((col) => col.defaultSort) || initialColumns[0]; }, [initialColumns]); const [sortedColumn, setSortedColumn] = useState({ id: defaultSortedColumn?.id || defaultSortedColumn?.accessor, direction: 'asc', }); const filteredData = useMemo(() => { const newData = data.filter((row) => { return Object.entries(filters).every(([key, value]) => { const column = columns.find((col) => col.accessor === key); if (!column) return true; switch (column.filterType) { case FilterType.Text: const newValue = value.map((data) => data.toString().toLowerCase()); const newTest = row[key]?.toString().toLowerCase(); if (!newTest) return false; // This is for the case when the filter is empty and we want to show all the data if (newValue.length === 0) return true; return newValue.find((data) => newTest.includes(data)); case FilterType.Number: return row[key] === value; case FilterType.DateRange: const startDate = new Date(value.startDate); const endDate = new Date(value.endDate); const dateToCheck = new Date(row[key]); return dateToCheck >= startDate && dateToCheck <= endDate; default: return true; } }); }); handleDataFilterChange && handleDataFilterChange(newData); return newData; }, [filters, data, columns]); // Helper function to parse the specific date format const parseCustomDate = (dateString) => { // First, try to create a Date object directly const date = new Date(dateString); if (!isNaN(date.getTime())) { return date; } // If that fails, try our custom parsing for the specific format const regex = /^(\w+)\s(\d{1,2}),\s(\d{4})\s-\s(\d{1,2}):(\d{2})\s(AM|PM)$/; const match = dateString.match(regex); if (match) { const [, month, day, year, hours, minutes, period] = match; const monthIndex = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December', ].indexOf(month); let parsedHours = parseInt(hours); if (period.toLowerCase() === 'pm' && parsedHours !== 12) { parsedHours += 12; } else if (period.toLowerCase() === 'am' && parsedHours === 12) { parsedHours = 0; } return new Date(parseInt(year), monthIndex, parseInt(day), parsedHours, parseInt(minutes)); } // If all parsing attempts fail, return null return null; }; // Placeholder function for other date formats const parseDateFallback = (dateString) => { // TODO: Implement additional date parsing logic here // For now, we'll use the built-in Date parser as a fallback const date = new Date(dateString); return isNaN(date.getTime()) ? null : date; }; const sortedData = useMemo(() => { if (!sortedColumn) return filteredData; return [...filteredData].sort((a, b) => { const key = sortedColumn.id; const direction = sortedColumn.direction; const column = columns.find((col) => col.accessor === key); if (!column) return 0; const aValue = a[key]; const bValue = b[key]; if (column.filterType === FilterType.DateRange) { let aDate = parseCustomDate(aValue); let bDate = parseCustomDate(bValue); // If custom parsing fails, try the fallback if (!aDate) aDate = parseDateFallback(aValue); if (!bDate) bDate = parseDateFallback(bValue); // If both dates are valid, compare them if (aDate && bDate) { if (aDate < bDate) { return direction === 'asc' ? -1 : 1; } if (aDate > bDate) { return direction === 'asc' ? 1 : -1; } return 0; } // If date parsing fails, fall back to string comparison console.warn('Date parsing failed, falling back to string comparison'); return direction === 'asc' ? String(aValue).localeCompare(String(bValue)) : String(bValue).localeCompare(String(aValue)); } if (typeof aValue === 'number' && typeof bValue === 'number') { if (aValue < bValue) { return direction === 'asc' ? -1 : 1; } if (aValue > bValue) { return direction === 'asc' ? 1 : -1; } } if (typeof aValue === 'string' && typeof bValue === 'string') { return direction === 'asc' ? aValue.localeCompare(bValue) : bValue.localeCompare(aValue); } return 0; }); }, [filteredData, sortedColumn, columns]); const isLoading = useMemo(() => { return loading && filteredData.length === 0; }, [loading, filteredData]); const [selectedRowIds, setSelectedRowIds] = useState({}); const [expandedRowIds, setExpandedRowIds] = useState({}); const [sortBy, setSortBy] = useState([]); const [pageSize, setPageSize] = useState(initialPageSize); const tableInstance = useTable({ columns, data: sortedData, // use sortedData here initialState: { pageIndex, pageSize, sortBy }, }, usePagination // TODO: this need to be made dynamic and we need to replace the any ); const { getTableBodyProps, prepareRow, rows, headerGroups, pageCount } = tableInstance; const paginatedRows = rows.slice(pageIndex * pageSize, (pageIndex + 1) * pageSize); const newHandleSortChange = (newSortBy) => { handleSortChange(setSortBy, newSortBy); const columnId = newSortBy.length > 0 ? newSortBy[0].id : defaultSortedColumn?.id || defaultSortedColumn?.accessor; const direction = newSortBy.length > 0 ? (newSortBy[0].desc ? 'desc' : 'asc') : 'asc'; setSortedColumn({ id: columnId, direction }); }; return (_jsxs("div", { id: id, ref: ref, className: classnames(className, 'dry-data-table'), children: [_jsxs("table", { children: [windowSize.width > 770 && (_jsx(TableHeader, { headerGroups: headerGroups, onSortChange: newHandleSortChange, sortedColumn: sortedColumn, columnOrder: localColumnOrder, columnWidths: localColumnWidths, onColumnOrderChange: (newOrder) => { handleColumnOrderChange(setColumnOrderProp, newOrder); setLocalColumnOrder(newOrder); }, onColumnResize: (newColumnWidths) => { handleColumnResize(setColumnWidthsProp, newColumnWidths); setLocalColumnWidths(newColumnWidths); }, hasResize: hasResize, isDraggable: isDraggable })), _jsx(TableBody, { hasAccordion: hasAccordion, loadingRowCount: loadingRowCount, loading: isLoading, columns: columns, data: sortedData, getTableBodyProps: getTableBodyProps, prepareRow: prepareRow, rows: paginatedRows, selectedRowIds: selectedRowIds, onRowSelect: (newSelectedRowIds) => handleRowSelect(setSelectedRowIds, newSelectedRowIds), expandedRowIds: expandedRowIds, onRowExpansion: (newExpandedRowIds) => handleRowExpansion(setExpandedRowIds, newExpandedRowIds), handleRowClick: handleRowClick, badgeColumnConfig: badgeColumnConfig })] }), _jsx(TableFooter, { pageIndex: pageIndex, pageSize: pageSize, totalRows: rows.length, hasPageSizeInput: hasPageSizeInput, hasGoToPageInput: hasGoToPageInput, onClickNext: () => handleClickNext(onPageChange, pageIndex, pageSize), onClickPrevious: () => handleClickPrevious(onPageChange, pageIndex, pageSize), onGoToPage: (page) => handleGoToPage(onPageChange, pageCount, pageSize, page), setPageSize: (newPageSize) => handlePageSizeChange(onPageChange, pageIndex, setPageSize, newPageSize, setPageIndex) })] })); }); export default DataTable; //# sourceMappingURL=DataTable.js.map