From 71cf3d656ed765d72551ca90e7e1d6a474c4294b Mon Sep 17 00:00:00 2001 From: aleidk Date: Sat, 23 Sep 2023 18:51:45 -0300 Subject: [PATCH] update filters for table --- src/components/Table.tsx | 89 +++++++++++++++++++++++++++++++++++-- src/pages/games/index.astro | 7 ++- 2 files changed, 91 insertions(+), 5 deletions(-) diff --git a/src/components/Table.tsx b/src/components/Table.tsx index c0e82cb..f5fbdf6 100644 --- a/src/components/Table.tsx +++ b/src/components/Table.tsx @@ -1,12 +1,19 @@ -import React from 'react'; +import React, { useMemo, useState } from 'react'; import usePagination, { Offset } from 'src/hooks/usePagination'; export type DataItem = Record; +export enum HeaderType { + Index, + String, + Number, +} + export interface Header { key: string; header: string; formatter?: (data: DataItem) => string; + type: HeaderType; } interface Props { @@ -14,8 +21,50 @@ interface Props { headers: Header[]; } +interface Filter { + type: HeaderType, + value: string, +} + + export default function Table({ data, headers }: Props): JSX.Element { - const { items, changeOffset } = usePagination({ items: data }); + const [filters, setFilters] = useState>({}); + + const resolveFilterByType = (filter: Filter, data: any): boolean => { + switch (filter.type) { + case HeaderType.String: + return data.search(filter.value) !== -1; + case HeaderType.Number: + return data === filter.value; + } + + return true; + } + + const filteredItems = useMemo(() => { + return data.filter((item) => { + return Object.entries(filters).every( + ([key, filter]) => resolveFilterByType(filter, item[key]), + ); + }); + }, [data, filters]); + + const { items, changeOffset } = usePagination({ + items: filteredItems, + }); + + const handleUpdateFilters = (name: string, type: HeaderType, value: any): 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) @@ -31,13 +80,45 @@ export default function Table({ data, headers }: Props): JSX.Element { return data[header.key]; } + function formatFilter(header: Header): JSX.Element { + switch (header.type) { + case HeaderType.String: + return ( + { + handleUpdateFilters(header.key, header.type, e.target.value ?? null); + }} + /> + ); + case HeaderType.Number: + return ( + { + handleUpdateFilters(header.key, header.type, e.target.value !== '' ? parseInt(e.target.value) : null); + }} + /> + ); + + default: + break; + } + + return <>; + } + return ( <> - +
{headers.map((item, idx) => ( - + ))} diff --git a/src/pages/games/index.astro b/src/pages/games/index.astro index 79f8822..3779e53 100644 --- a/src/pages/games/index.astro +++ b/src/pages/games/index.astro @@ -1,7 +1,7 @@ --- import { getCollection } from 'astro:content'; import Layout from '@layouts/Layout.astro'; -import Table, { type Header } from '@components/Table.jsx'; +import Table, { HeaderType, type Header } from '@components/Table.jsx'; const rawGames = await getCollection('games'); @@ -15,23 +15,28 @@ const headers: Header[] = [ { key: 'id', header: 'index', + type: HeaderType.Index, }, { key: 'title', header: 'Title', formatter: (data) => `${data.title}`, + type: HeaderType.String, }, { key: 'status', header: 'Status', + type: HeaderType.String, }, { key: 'times_played', header: 'Times Played', + type: HeaderType.Number, }, { key: 'registered_hours', header: 'Registered Hours', + type: HeaderType.Number, }, ]; ---
{item.header} +
+ {item.header} + {formatFilter(item)} +
+