update filters for table
This commit is contained in:
parent
c2a8f749b4
commit
71cf3d656e
2 changed files with 91 additions and 5 deletions
|
|
@ -1,12 +1,19 @@
|
||||||
import React from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
import usePagination, { Offset } from 'src/hooks/usePagination';
|
import usePagination, { Offset } from 'src/hooks/usePagination';
|
||||||
|
|
||||||
export type DataItem = Record<string, any>;
|
export type DataItem = Record<string, any>;
|
||||||
|
|
||||||
|
export enum HeaderType {
|
||||||
|
Index,
|
||||||
|
String,
|
||||||
|
Number,
|
||||||
|
}
|
||||||
|
|
||||||
export interface Header {
|
export interface Header {
|
||||||
key: string;
|
key: string;
|
||||||
header: string;
|
header: string;
|
||||||
formatter?: (data: DataItem) => string;
|
formatter?: (data: DataItem) => string;
|
||||||
|
type: HeaderType;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|
@ -14,8 +21,50 @@ interface Props {
|
||||||
headers: Header[];
|
headers: Header[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Filter {
|
||||||
|
type: HeaderType,
|
||||||
|
value: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export default function Table({ data, headers }: Props): JSX.Element {
|
export default function Table({ data, headers }: Props): JSX.Element {
|
||||||
const { items, changeOffset } = usePagination<DataItem>({ items: data });
|
const [filters, setFilters] = useState<Record<string, Filter>>({});
|
||||||
|
|
||||||
|
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<DataItem>({
|
||||||
|
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 {
|
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)
|
// 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];
|
return data[header.key];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatFilter(header: Header): JSX.Element {
|
||||||
|
switch (header.type) {
|
||||||
|
case HeaderType.String:
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
onChange={(e) => {
|
||||||
|
handleUpdateFilters(header.key, header.type, e.target.value ?? null);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
case HeaderType.Number:
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
onChange={(e) => {
|
||||||
|
handleUpdateFilters(header.key, header.type, e.target.value !== '' ? parseInt(e.target.value) : null);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<table>
|
<table style={{ width: '100%' }}>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
{headers.map((item, idx) => (
|
{headers.map((item, idx) => (
|
||||||
<th key={idx}>{item.header}</th>
|
<th key={idx}>
|
||||||
|
<div className="vstack">
|
||||||
|
{item.header}
|
||||||
|
{formatFilter(item)}
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
))}
|
))}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
import { getCollection } from 'astro:content';
|
import { getCollection } from 'astro:content';
|
||||||
import Layout from '@layouts/Layout.astro';
|
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');
|
const rawGames = await getCollection('games');
|
||||||
|
|
||||||
|
|
@ -15,23 +15,28 @@ const headers: Header[] = [
|
||||||
{
|
{
|
||||||
key: 'id',
|
key: 'id',
|
||||||
header: 'index',
|
header: 'index',
|
||||||
|
type: HeaderType.Index,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'title',
|
key: 'title',
|
||||||
header: 'Title',
|
header: 'Title',
|
||||||
formatter: (data) => `<a href="games/${data.slug}">${data.title}</a>`,
|
formatter: (data) => `<a href="games/${data.slug}">${data.title}</a>`,
|
||||||
|
type: HeaderType.String,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'status',
|
key: 'status',
|
||||||
header: 'Status',
|
header: 'Status',
|
||||||
|
type: HeaderType.String,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'times_played',
|
key: 'times_played',
|
||||||
header: 'Times Played',
|
header: 'Times Played',
|
||||||
|
type: HeaderType.Number,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'registered_hours',
|
key: 'registered_hours',
|
||||||
header: 'Registered Hours',
|
header: 'Registered Hours',
|
||||||
|
type: HeaderType.Number,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
---
|
---
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue