@kadconsulting/dry
Version:
KAD Reusable Component Library
200 lines • 11.1 kB
JavaScript
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