import React, { useMemo, useRef, useState } from 'react'; import usePagination, { Offset } from 'src/hooks/usePagination'; import { SelectFilter, NumberFilter, resolveFilterByType } from './Filters'; import type { DataItem, Header, Value, Filter } from './types'; import { HeaderType } from './types'; import styles from './Table.module.css'; interface Props { data: DataItem[]; headers: Header[]; } export default function Table({ data, headers }: Props): JSX.Element { const [filters, setFilters] = useState>({}); const filtersId = useRef(crypto.randomUUID()); const filteredItems = useMemo(() => { return data.filter((item) => { return Object.entries(filters).every(([key, filter]) => resolveFilterByType(filter, item[key]), ); }); }, [data, filters]); const { items, changeOffset, getPaginationRange } = usePagination({ items: filteredItems, limit: 10, }); const handleUpdateFilters = ( name: string, type: HeaderType, value: Value, ): void => { setFilters((prev) => { if (value === null) { // eslint-disable-next-line @typescript-eslint/no-dynamic-delete delete prev[name]; return { ...prev }; } return { ...prev, [name]: { value, type } }; }); }; function formatCell(data: DataItem, header: Header): JSX.Element { // This formatting is only used because the source is trusted (private markdown files manage only by me) // and because Astro don't allow me to pass JSX from an Astro file to a TSX file, // so I have to pass the formatted row as a string. // DON'T use this method on a public API if (header.hasCustomCell && header.formatter) { return (
); } if (header.type === HeaderType.Multiple) { return (
    {data[header.key].map((item: JSX.Element, idx: number) => (
  • {item}
  • ))}
); } return data[header.key]; } function formatFilter(header: Header): JSX.Element { const baseProps = { key: header.key + filtersId.current, keyData: header.key, value: filters[header.key]?.value, onChange: (value: Value) => { handleUpdateFilters(header.key, header.type, value); }, }; switch (header.type) { case HeaderType.String: return ( { baseProps.onChange(e.target.value); }} key={baseProps.key} /> ); case HeaderType.Number: return ( { handleUpdateFilters(header.key, header.type, value as Value); }} /> ); case HeaderType.Select: return ; case HeaderType.Multiple: return ; default: break; } return <>; } return ( <>
{headers.map((item, idx) => ( ))} {items.map((item, idx) => ( {headers.map((header, hidx) => ( ))} ))}
{item.header} {formatFilter(item)}
{formatCell(item, header)}
{getPaginationRange().map((item) => ( ))}
); }