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 React, { useMemo, useState } from 'react';
|
||||||
import usePagination, { Offset } from 'src/hooks/usePagination';
|
import usePagination, { Offset } from 'src/hooks/usePagination';
|
||||||
|
import Select from './Inputs/Select';
|
||||||
|
|
||||||
export type DataItem = Record<string, any>;
|
export type DataItem = Record<string, any>;
|
||||||
|
|
||||||
|
|
@ -7,6 +8,8 @@ export enum HeaderType {
|
||||||
Index,
|
Index,
|
||||||
String,
|
String,
|
||||||
Number,
|
Number,
|
||||||
|
Select,
|
||||||
|
Multiple,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Header {
|
export interface Header {
|
||||||
|
|
@ -22,11 +25,10 @@ interface Props {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Filter {
|
interface Filter {
|
||||||
type: HeaderType,
|
type: HeaderType;
|
||||||
value: string,
|
value: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export default function Table({ data, headers }: Props): JSX.Element {
|
export default function Table({ data, headers }: Props): JSX.Element {
|
||||||
const [filters, setFilters] = useState<Record<string, Filter>>({});
|
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;
|
return data.search(filter.value) !== -1;
|
||||||
case HeaderType.Number:
|
case HeaderType.Number:
|
||||||
return data === filter.value;
|
return data === filter.value;
|
||||||
|
case HeaderType.Select:
|
||||||
|
return data === filter.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
};
|
||||||
|
|
||||||
const filteredItems = useMemo(() => {
|
const filteredItems = useMemo(() => {
|
||||||
return data.filter((item) => {
|
return data.filter((item) => {
|
||||||
return Object.entries(filters).every(
|
return Object.entries(filters).every(([key, filter]) =>
|
||||||
([key, filter]) => resolveFilterByType(filter, item[key]),
|
resolveFilterByType(filter, item[key]),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}, [data, filters]);
|
}, [data, filters]);
|
||||||
|
|
@ -53,7 +57,11 @@ export default function Table({ data, headers }: Props): JSX.Element {
|
||||||
items: filteredItems,
|
items: filteredItems,
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleUpdateFilters = (name: string, type: HeaderType, value: any): void => {
|
const handleUpdateFilters = (
|
||||||
|
name: string,
|
||||||
|
type: HeaderType,
|
||||||
|
value: any,
|
||||||
|
): void => {
|
||||||
setFilters((prev) => {
|
setFilters((prev) => {
|
||||||
if (value === null) {
|
if (value === null) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
// 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 } };
|
return { ...prev, [name]: { value, type } };
|
||||||
});
|
});
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function formatCell(data: DataItem, header: Header): JSX.Element {
|
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];
|
return data[header.key];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -86,7 +103,11 @@ export default function Table({ data, headers }: Props): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<input
|
<input
|
||||||
onChange={(e) => {
|
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
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
onChange={(e) => {
|
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 { getCollection } from 'astro:content';
|
||||||
import Layout from '@layouts/Layout.astro';
|
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');
|
const rawGames = await getCollection('games');
|
||||||
|
|
||||||
|
|
@ -26,7 +26,12 @@ const headers: Header[] = [
|
||||||
{
|
{
|
||||||
key: 'status',
|
key: 'status',
|
||||||
header: 'Status',
|
header: 'Status',
|
||||||
type: HeaderType.String,
|
type: HeaderType.Select,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'genres',
|
||||||
|
header: 'Genres',
|
||||||
|
type: HeaderType.Multiple,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'times_played',
|
key: 'times_played',
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue