Add a clear button that reset the filters values.

This commit is contained in:
Alexander Navarro 2023-11-04 19:49:25 -03:00
parent e6df894c85
commit b1979d7351
5 changed files with 143 additions and 97 deletions

View file

@ -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 */

View file

@ -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);

View file

@ -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

View file

@ -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,7 +124,21 @@ export default function Table({ data, headers }: Props): JSX.Element {
return ( return (
<> <>
<table style={{ width: '100%' }}> <section className="hstack">
<button
className="ml-auto"
onClick={() => {
// FIXME: this clear the filters but not the value of the inputs
setFilters({});
filtersKey.current = crypto.randomUUID();
}}
>
Clear Filters
</button>
</section>
<section className="mt-1 overflow-scroll">
<table>
<thead> <thead>
<tr> <tr>
{headers.map((item, idx) => ( {headers.map((item, idx) => (
@ -163,8 +162,9 @@ export default function Table({ data, headers }: Props): JSX.Element {
))} ))}
</tbody> </tbody>
</table> </table>
</section>
<section> <section className="mt-1">
<button <button
onClick={() => { onClick={() => {
changeOffset(Offset.First); changeOffset(Offset.First);

View file

@ -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>
)) ))
} }