Add a clear button that reset the filters values.
This commit is contained in:
parent
e6df894c85
commit
b1979d7351
5 changed files with 143 additions and 97 deletions
|
|
@ -120,110 +120,141 @@
|
||||||
text-align: end;
|
text-align: end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.overflow-scroll {
|
||||||
|
overflow: scroll;
|
||||||
|
}
|
||||||
|
.overflow-x-scroll {
|
||||||
|
overflow-x: scroll;
|
||||||
|
}
|
||||||
|
.overflow-y-scroll {
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
/* Start Margins */
|
/* Start Margins */
|
||||||
.m-1 {
|
.m-1 {
|
||||||
margin: var(--prj-spacing-1);
|
margin: var(--prj-spacing-1) !important;
|
||||||
}
|
}
|
||||||
.m-2 {
|
.m-2 {
|
||||||
margin: var(--prj-spacing-2);
|
margin: var(--prj-spacing-2) !important;
|
||||||
}
|
}
|
||||||
.m-3 {
|
.m-3 {
|
||||||
margin: var(--prj-spacing-3);
|
margin: var(--prj-spacing-3) !important;
|
||||||
}
|
}
|
||||||
.m-4 {
|
.m-4 {
|
||||||
margin: var(--prj-spacing-4);
|
margin: var(--prj-spacing-4) !important;
|
||||||
}
|
}
|
||||||
.m-5 {
|
.m-5 {
|
||||||
margin: var(--prj-spacing-5);
|
margin: var(--prj-spacing-5) !important;
|
||||||
|
}
|
||||||
|
.m-auto {
|
||||||
|
margin: auto !important;
|
||||||
}
|
}
|
||||||
.mx-1 {
|
.mx-1 {
|
||||||
margin-right: var(--prj-spacing-1) 0;
|
margin-right: var(--prj-spacing-1) 0 !important;
|
||||||
}
|
}
|
||||||
.mx-2 {
|
.mx-2 {
|
||||||
margin-right: var(--prj-spacing-2) 0;
|
margin-right: var(--prj-spacing-2) 0 !important;
|
||||||
}
|
}
|
||||||
.mx-3 {
|
.mx-3 {
|
||||||
margin-right: var(--prj-spacing-3) 0;
|
margin-right: var(--prj-spacing-3) 0 !important;
|
||||||
}
|
}
|
||||||
.mx-4 {
|
.mx-4 {
|
||||||
margin-right: var(--prj-spacing-4) 0;
|
margin-right: var(--prj-spacing-4) 0 !important;
|
||||||
}
|
}
|
||||||
.mx-5 {
|
.mx-5 {
|
||||||
margin-right: var(--prj-spacing-5) 0;
|
margin-right: var(--prj-spacing-5) 0 !important;
|
||||||
|
}
|
||||||
|
.mx-auto {
|
||||||
|
margin-right: auto 0 !important;
|
||||||
}
|
}
|
||||||
.my-1 {
|
.my-1 {
|
||||||
margin-right: 0 var(--prj-spacing-1);
|
margin-right: 0 var(--prj-spacing-1) !important;
|
||||||
}
|
}
|
||||||
.my-2 {
|
.my-2 {
|
||||||
margin-right: 0 var(--prj-spacing-2);
|
margin-right: 0 var(--prj-spacing-2) !important;
|
||||||
}
|
}
|
||||||
.my-3 {
|
.my-3 {
|
||||||
margin-right: 0 var(--prj-spacing-3);
|
margin-right: 0 var(--prj-spacing-3) !important;
|
||||||
}
|
}
|
||||||
.my-4 {
|
.my-4 {
|
||||||
margin-right: 0 var(--prj-spacing-4);
|
margin-right: 0 var(--prj-spacing-4) !important;
|
||||||
}
|
}
|
||||||
.my-5 {
|
.my-5 {
|
||||||
margin-right: 0 var(--prj-spacing-5);
|
margin-right: 0 var(--prj-spacing-5) !important;
|
||||||
|
}
|
||||||
|
.my-auto {
|
||||||
|
margin-right: 0 auto !important;
|
||||||
}
|
}
|
||||||
.mt-1 {
|
.mt-1 {
|
||||||
margin-top: var(--prj-spacing-1);
|
margin-top: var(--prj-spacing-1) !important;
|
||||||
}
|
}
|
||||||
.mt-2 {
|
.mt-2 {
|
||||||
margin-top: var(--prj-spacing-2);
|
margin-top: var(--prj-spacing-2) !important;
|
||||||
}
|
}
|
||||||
.mt-3 {
|
.mt-3 {
|
||||||
margin-top: var(--prj-spacing-3);
|
margin-top: var(--prj-spacing-3) !important;
|
||||||
}
|
}
|
||||||
.mt-4 {
|
.mt-4 {
|
||||||
margin-top: var(--prj-spacing-4);
|
margin-top: var(--prj-spacing-4) !important;
|
||||||
}
|
}
|
||||||
.mt-5 {
|
.mt-5 {
|
||||||
margin-top: var(--prj-spacing-5);
|
margin-top: var(--prj-spacing-5) !important;
|
||||||
|
}
|
||||||
|
.mt-auto {
|
||||||
|
margin-top: auto !important;
|
||||||
}
|
}
|
||||||
.mb-1 {
|
.mb-1 {
|
||||||
margin-bottom: var(--prj-spacing-1);
|
margin-bottom: var(--prj-spacing-1) !important;
|
||||||
}
|
}
|
||||||
.mb-2 {
|
.mb-2 {
|
||||||
margin-bottom: var(--prj-spacing-2);
|
margin-bottom: var(--prj-spacing-2) !important;
|
||||||
}
|
}
|
||||||
.mb-3 {
|
.mb-3 {
|
||||||
margin-bottom: var(--prj-spacing-3);
|
margin-bottom: var(--prj-spacing-3) !important;
|
||||||
}
|
}
|
||||||
.mb-4 {
|
.mb-4 {
|
||||||
margin-bottom: var(--prj-spacing-4);
|
margin-bottom: var(--prj-spacing-4) !important;
|
||||||
}
|
}
|
||||||
.mb-5 {
|
.mb-5 {
|
||||||
margin-bottom: var(--prj-spacing-5);
|
margin-bottom: var(--prj-spacing-5) !important;
|
||||||
|
}
|
||||||
|
.mb-auto {
|
||||||
|
margin-bottom: auto !important;
|
||||||
}
|
}
|
||||||
.ml-1 {
|
.ml-1 {
|
||||||
margin-left: var(--prj-spacing-1);
|
margin-left: var(--prj-spacing-1) !important;
|
||||||
}
|
}
|
||||||
.ml-2 {
|
.ml-2 {
|
||||||
margin-left: var(--prj-spacing-2);
|
margin-left: var(--prj-spacing-2) !important;
|
||||||
}
|
}
|
||||||
.ml-3 {
|
.ml-3 {
|
||||||
margin-left: var(--prj-spacing-3);
|
margin-left: var(--prj-spacing-3) !important;
|
||||||
}
|
}
|
||||||
.ml-4 {
|
.ml-4 {
|
||||||
margin-left: var(--prj-spacing-4);
|
margin-left: var(--prj-spacing-4) !important;
|
||||||
}
|
}
|
||||||
.ml-5 {
|
.ml-5 {
|
||||||
margin-left: var(--prj-spacing-5);
|
margin-left: var(--prj-spacing-5) !important;
|
||||||
|
}
|
||||||
|
.ml-auto {
|
||||||
|
margin-left: auto !important;
|
||||||
}
|
}
|
||||||
.mr-1 {
|
.mr-1 {
|
||||||
margin-right: var(--prj-spacing-1);
|
margin-right: var(--prj-spacing-1) !important;
|
||||||
}
|
}
|
||||||
.mr-2 {
|
.mr-2 {
|
||||||
margin-right: var(--prj-spacing-2);
|
margin-right: var(--prj-spacing-2) !important;
|
||||||
}
|
}
|
||||||
.mr-3 {
|
.mr-3 {
|
||||||
margin-right: var(--prj-spacing-3);
|
margin-right: var(--prj-spacing-3) !important;
|
||||||
}
|
}
|
||||||
.mr-4 {
|
.mr-4 {
|
||||||
margin-right: var(--prj-spacing-4);
|
margin-right: var(--prj-spacing-4) !important;
|
||||||
}
|
}
|
||||||
.mr-5 {
|
.mr-5 {
|
||||||
margin-right: var(--prj-spacing-5);
|
margin-right: var(--prj-spacing-5) !important;
|
||||||
|
}
|
||||||
|
.mr-auto {
|
||||||
|
margin-right: var(--prj-spacing-5) !important;
|
||||||
}
|
}
|
||||||
/* End Margins */
|
/* End Margins */
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,9 @@
|
||||||
padding: var(--prj-spacing-1);
|
padding: var(--prj-spacing-1);
|
||||||
background-color: var(--bg-color);
|
background-color: var(--bg-color);
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
gap: var(--prj-spacing-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.input {
|
.input {
|
||||||
|
|
@ -16,9 +19,6 @@
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.realInput {
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectedItem {
|
.selectedItem {
|
||||||
background-color: var(--prj-surface-3);
|
background-color: var(--prj-surface-3);
|
||||||
color: var(--prj-text);
|
color: var(--prj-text);
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,14 @@ interface Props {
|
||||||
options: [{ label: string; value: any }];
|
options: [{ label: string; value: any }];
|
||||||
keyData: string;
|
keyData: string;
|
||||||
isMultiple?: boolean;
|
isMultiple?: boolean;
|
||||||
|
value?: string | string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function SelectInput({
|
export default function SelectInput({
|
||||||
options,
|
options,
|
||||||
isMultiple = false,
|
isMultiple = false,
|
||||||
onChange,
|
onChange,
|
||||||
|
value = [],
|
||||||
}: Props): JSX.Element {
|
}: Props): JSX.Element {
|
||||||
const [selected, setSelected] = useState<string[]>([]);
|
const [selected, setSelected] = useState<string[]>([]);
|
||||||
const [filteredOptions, setFilteredOptions] = useState<Props['options']>([]);
|
const [filteredOptions, setFilteredOptions] = useState<Props['options']>([]);
|
||||||
|
|
@ -33,12 +35,12 @@ export default function SelectInput({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
onChange(isMultiple ? selected : selected.at(0));
|
onChange(isMultiple ? selected : selected[0]);
|
||||||
}, [selected]);
|
}, [selected]);
|
||||||
|
|
||||||
const handleFocusInput = (): void => {
|
const handleFocusInput = (): void => {
|
||||||
setIsOptionsOpen(true);
|
setIsOptionsOpen(true);
|
||||||
inputRef.current.focus();
|
inputRef.current?.focus();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAddElement = (item: any): void => {
|
const handleAddElement = (item: any): void => {
|
||||||
|
|
@ -49,6 +51,9 @@ export default function SelectInput({
|
||||||
return [item];
|
return [item];
|
||||||
});
|
});
|
||||||
setIsOptionsOpen(false);
|
setIsOptionsOpen(false);
|
||||||
|
|
||||||
|
if (inputRef.current === null) return;
|
||||||
|
|
||||||
inputRef.current.value = '';
|
inputRef.current.value = '';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -111,6 +116,14 @@ export default function SelectInput({
|
||||||
onChange={handleFilterOptions}
|
onChange={handleFilterOptions}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
onChange(null);
|
||||||
|
setSelected([]);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
X
|
||||||
|
</button>
|
||||||
<div className={styles.optionList} hidden={!isOptionsOpen}>
|
<div className={styles.optionList} hidden={!isOptionsOpen}>
|
||||||
{filteredOptions.map((item, idx) => (
|
{filteredOptions.map((item, idx) => (
|
||||||
<button
|
<button
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useMemo, useState } from 'react';
|
import React, { useMemo, useRef, useState } from 'react';
|
||||||
import usePagination, { Offset } from 'src/hooks/usePagination';
|
import usePagination, { Offset } from 'src/hooks/usePagination';
|
||||||
import {
|
import {
|
||||||
SelectFilter,
|
SelectFilter,
|
||||||
|
|
@ -29,8 +29,11 @@ interface Props {
|
||||||
headers: Header[];
|
headers: Header[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Value = string | string[] | number | number[] | null;
|
||||||
|
|
||||||
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>>({});
|
||||||
|
const filtersKey = useRef(crypto.randomUUID());
|
||||||
|
|
||||||
const filteredItems = useMemo(() => {
|
const filteredItems = useMemo(() => {
|
||||||
return data.filter((item) => {
|
return data.filter((item) => {
|
||||||
|
|
@ -86,49 +89,31 @@ export default function Table({ data, headers }: Props): JSX.Element {
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatFilter(header: Header): JSX.Element {
|
function formatFilter(header: Header): JSX.Element {
|
||||||
|
const baseProps = {
|
||||||
|
key: header.key + filtersKey.current,
|
||||||
|
keyData: header.key,
|
||||||
|
value: filters[header.key]?.value,
|
||||||
|
onChange: (value: Value) => {
|
||||||
|
handleUpdateFilters(header.key, header.type, value);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
switch (header.type) {
|
switch (header.type) {
|
||||||
case HeaderType.String:
|
case HeaderType.String:
|
||||||
return (
|
return (
|
||||||
<input
|
<input
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
handleUpdateFilters(
|
baseProps.onChange(e.target.value);
|
||||||
header.key,
|
|
||||||
header.type,
|
|
||||||
e.target.value ?? null,
|
|
||||||
);
|
|
||||||
}}
|
}}
|
||||||
|
key={baseProps.key}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case HeaderType.Number:
|
case HeaderType.Number:
|
||||||
return (
|
return <NumberInput {...baseProps} />;
|
||||||
<NumberInput
|
|
||||||
keyData={header.key}
|
|
||||||
onChange={(value) => {
|
|
||||||
handleUpdateFilters(header.key, header.type, value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
case HeaderType.Select:
|
case HeaderType.Select:
|
||||||
return (
|
return <SelectFilter data={data} {...baseProps} />;
|
||||||
<SelectFilter
|
|
||||||
data={data}
|
|
||||||
keyData={header.key}
|
|
||||||
onChange={(value) => {
|
|
||||||
handleUpdateFilters(header.key, header.type, value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
case HeaderType.Multiple:
|
case HeaderType.Multiple:
|
||||||
return (
|
return <SelectFilter {...baseProps} isMultiple data={data} />;
|
||||||
<SelectFilter
|
|
||||||
isMultiple
|
|
||||||
data={data}
|
|
||||||
keyData={header.key}
|
|
||||||
onChange={(value) => {
|
|
||||||
handleUpdateFilters(header.key, header.type, value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
@ -139,32 +124,47 @@ export default function Table({ data, headers }: Props): JSX.Element {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<table style={{ width: '100%' }}>
|
<section className="hstack">
|
||||||
<thead>
|
<button
|
||||||
<tr>
|
className="ml-auto"
|
||||||
{headers.map((item, idx) => (
|
onClick={() => {
|
||||||
<th key={idx}>
|
// FIXME: this clear the filters but not the value of the inputs
|
||||||
<div className="vstack">
|
setFilters({});
|
||||||
{item.header}
|
filtersKey.current = crypto.randomUUID();
|
||||||
{formatFilter(item)}
|
}}
|
||||||
</div>
|
>
|
||||||
</th>
|
Clear Filters
|
||||||
))}
|
</button>
|
||||||
</tr>
|
</section>
|
||||||
</thead>
|
|
||||||
|
|
||||||
<tbody>
|
<section className="mt-1 overflow-scroll">
|
||||||
{items.map((item, idx) => (
|
<table>
|
||||||
<tr key={idx}>
|
<thead>
|
||||||
{headers.map((header, hidx) => (
|
<tr>
|
||||||
<td key={hidx}>{formatCell(item, header)}</td>
|
{headers.map((item, idx) => (
|
||||||
|
<th key={idx}>
|
||||||
|
<div className="vstack">
|
||||||
|
{item.header}
|
||||||
|
{formatFilter(item)}
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
))}
|
))}
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
</thead>
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<section>
|
<tbody>
|
||||||
|
{items.map((item, idx) => (
|
||||||
|
<tr key={idx}>
|
||||||
|
{headers.map((header, hidx) => (
|
||||||
|
<td key={hidx}>{formatCell(item, header)}</td>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mt-1">
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
changeOffset(Offset.First);
|
changeOffset(Offset.First);
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,8 @@ const headers: Header[] = [
|
||||||
{
|
{
|
||||||
key: 'title',
|
key: 'title',
|
||||||
header: 'Title',
|
header: 'Title',
|
||||||
formatter: (data) => `<a href="games/${data.slug}">${data.title}</a>`,
|
// TODO: uncomment this when we are ready ton show a single page game.
|
||||||
|
// formatter: (data) => `<a href="games/${data.slug}">${data.title}</a>`,
|
||||||
type: HeaderType.String,
|
type: HeaderType.String,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -58,7 +59,8 @@ const headers: Header[] = [
|
||||||
{
|
{
|
||||||
games.map((item) => (
|
games.map((item) => (
|
||||||
<li>
|
<li>
|
||||||
<a href={`games/${item.slug}`}>{item.title}</a>
|
{/* <a href={`games/${item.slug}`}>{item.title}</a> */}
|
||||||
|
{item.title}
|
||||||
</li>
|
</li>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue