add select type to table
This commit is contained in:
parent
71cf3d656e
commit
739ee87879
4 changed files with 104 additions and 12 deletions
47
src/components/Table/Inputs/Select.tsx
Normal file
47
src/components/Table/Inputs/Select.tsx
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import React, { useMemo, type ChangeEventHandler } from 'react';
|
||||
|
||||
export type DataItem = Record<string, any>;
|
||||
|
||||
interface Props {
|
||||
onChange: (value: string | null) => void;
|
||||
data: DataItem[];
|
||||
keyData: string;
|
||||
}
|
||||
|
||||
export default function Select({
|
||||
data,
|
||||
keyData,
|
||||
onChange,
|
||||
}: Props): JSX.Element {
|
||||
const options = useMemo(() => {
|
||||
let options = data.map((item) => item[keyData]);
|
||||
|
||||
options = [...new Set(options)];
|
||||
|
||||
options = options.map((item, idx) => (
|
||||
<option key={idx} value={item}>
|
||||
{item}
|
||||
</option>
|
||||
));
|
||||
|
||||
options.unshift(
|
||||
<option key={-1} value="">
|
||||
Select...
|
||||
</option>,
|
||||
);
|
||||
|
||||
return options;
|
||||
}, [data, keyData]);
|
||||
|
||||
return (
|
||||
<select
|
||||
name="fooe"
|
||||
id="foo"
|
||||
onChange={({ target }) =>
|
||||
{ onChange(target.value !== '' ? target.value : null); }
|
||||
}
|
||||
>
|
||||
{options}
|
||||
</select>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import React, { useMemo, useState } from 'react';
|
||||
import usePagination, { Offset } from 'src/hooks/usePagination';
|
||||
import Select from './Inputs/Select';
|
||||
|
||||
export type DataItem = Record<string, any>;
|
||||
|
||||
|
|
@ -7,6 +8,8 @@ export enum HeaderType {
|
|||
Index,
|
||||
String,
|
||||
Number,
|
||||
Select,
|
||||
Multiple,
|
||||
}
|
||||
|
||||
export interface Header {
|
||||
|
|
@ -22,11 +25,10 @@ interface Props {
|
|||
}
|
||||
|
||||
interface Filter {
|
||||
type: HeaderType,
|
||||
value: string,
|
||||
type: HeaderType;
|
||||
value: string;
|
||||
}
|
||||
|
||||
|
||||
export default function Table({ data, headers }: Props): JSX.Element {
|
||||
const [filters, setFilters] = useState<Record<string, Filter>>({});
|
||||
|
||||
|
|
@ -36,15 +38,17 @@ export default function Table({ data, headers }: Props): JSX.Element {
|
|||
return data.search(filter.value) !== -1;
|
||||
case HeaderType.Number:
|
||||
return data === filter.value;
|
||||
case HeaderType.Select:
|
||||
return data === filter.value;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
const filteredItems = useMemo(() => {
|
||||
return data.filter((item) => {
|
||||
return Object.entries(filters).every(
|
||||
([key, filter]) => resolveFilterByType(filter, item[key]),
|
||||
return Object.entries(filters).every(([key, filter]) =>
|
||||
resolveFilterByType(filter, item[key]),
|
||||
);
|
||||
});
|
||||
}, [data, filters]);
|
||||
|
|
@ -53,7 +57,11 @@ export default function Table({ data, headers }: Props): JSX.Element {
|
|||
items: filteredItems,
|
||||
});
|
||||
|
||||
const handleUpdateFilters = (name: string, type: HeaderType, value: any): void => {
|
||||
const handleUpdateFilters = (
|
||||
name: string,
|
||||
type: HeaderType,
|
||||
value: any,
|
||||
): void => {
|
||||
setFilters((prev) => {
|
||||
if (value === null) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||
|
|
@ -63,7 +71,6 @@ export default function Table({ data, headers }: Props): JSX.Element {
|
|||
|
||||
return { ...prev, [name]: { value, type } };
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
function formatCell(data: DataItem, header: Header): JSX.Element {
|
||||
|
|
@ -77,6 +84,16 @@ export default function Table({ data, headers }: Props): JSX.Element {
|
|||
);
|
||||
}
|
||||
|
||||
if (header.type === HeaderType.Multiple) {
|
||||
return (
|
||||
<ul className="text-start">
|
||||
{data[header.key].map((item: JSX.Element, idx: number) => (
|
||||
<li key={idx}>{item}</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
return data[header.key];
|
||||
}
|
||||
|
||||
|
|
@ -86,7 +103,11 @@ export default function Table({ data, headers }: Props): JSX.Element {
|
|||
return (
|
||||
<input
|
||||
onChange={(e) => {
|
||||
handleUpdateFilters(header.key, header.type, e.target.value ?? null);
|
||||
handleUpdateFilters(
|
||||
header.key,
|
||||
header.type,
|
||||
e.target.value ?? null,
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
|
@ -95,7 +116,21 @@ export default function Table({ data, headers }: Props): JSX.Element {
|
|||
<input
|
||||
type="number"
|
||||
onChange={(e) => {
|
||||
handleUpdateFilters(header.key, header.type, e.target.value !== '' ? parseInt(e.target.value) : null);
|
||||
handleUpdateFilters(
|
||||
header.key,
|
||||
header.type,
|
||||
e.target.value !== '' ? parseInt(e.target.value) : null,
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
case HeaderType.Select:
|
||||
return (
|
||||
<Select
|
||||
data={data}
|
||||
keyData={header.key}
|
||||
onChange={(value) => {
|
||||
handleUpdateFilters(header.key, header.type, value);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
5
src/components/Table/index.ts
Normal file
5
src/components/Table/index.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import Table, { HeaderType, type Header } from './Table';
|
||||
|
||||
export default Table;
|
||||
|
||||
export { HeaderType, type Header };
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
import { getCollection } from 'astro:content';
|
||||
import Layout from '@layouts/Layout.astro';
|
||||
import Table, { HeaderType, type Header } from '@components/Table.jsx';
|
||||
import Table, { HeaderType, type Header } from '@components/Table';
|
||||
|
||||
const rawGames = await getCollection('games');
|
||||
|
||||
|
|
@ -26,7 +26,12 @@ const headers: Header[] = [
|
|||
{
|
||||
key: 'status',
|
||||
header: 'Status',
|
||||
type: HeaderType.String,
|
||||
type: HeaderType.Select,
|
||||
},
|
||||
{
|
||||
key: 'genres',
|
||||
header: 'Genres',
|
||||
type: HeaderType.Multiple,
|
||||
},
|
||||
{
|
||||
key: 'times_played',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue