Refactor types organization for table component

This commit is contained in:
Alexander Navarro 2023-11-11 11:24:51 -03:00
parent deb95b355f
commit c0683dafb3
12 changed files with 1490 additions and 1195 deletions

View file

@ -20,17 +20,17 @@
"react-dom": "^18.0.0" "react-dom": "^18.0.0"
}, },
"devDependencies": { "devDependencies": {
"@typescript-eslint/eslint-plugin": "^5.50.0", "@typescript-eslint/eslint-plugin": "^5.62.0",
"eslint": "^8.0.1", "eslint": "^8.53.0",
"eslint-config-prettier": "^8.8.0", "eslint-config-prettier": "^8.10.0",
"eslint-config-standard-with-typescript": "^35.0.0", "eslint-config-standard-with-typescript": "^35.0.0",
"eslint-plugin-import": "^2.25.2", "eslint-plugin-import": "^2.29.0",
"eslint-plugin-n": "^15.0.0", "eslint-plugin-n": "^15.7.0",
"eslint-plugin-promise": "^6.0.0", "eslint-plugin-promise": "^6.1.1",
"eslint-plugin-react": "^7.32.2", "eslint-plugin-react": "^7.33.2",
"gh-pages": "^5.0.0", "gh-pages": "^5.0.0",
"prettier": "^2.8.8", "prettier": "^2.8.8",
"prettier-plugin-astro": "^0.10.0", "prettier-plugin-astro": "^0.10.0",
"typescript": "*" "typescript": "^5.2.2"
} }
} }

2529
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff

View file

@ -1,15 +1,14 @@
import React, { import React, { useState, useRef, useEffect } from 'react';
useState,
type ChangeEventHandler,
useRef,
useEffect,
} from 'react';
import styles from './SelectInput.module.css'; import styles from './SelectInput.module.css';
interface Option {
label: string;
value: any;
}
interface Props { interface Props {
onChange: (value: string | string[] | null) => void; onChange: (value: string | string[] | null) => void;
options: [{ label: string; value: any }]; options: Option[];
keyData: string;
isMultiple?: boolean; isMultiple?: boolean;
value?: string | string[]; value?: string | string[];
} }

View file

View file

@ -1,7 +1,7 @@
import React, { useState, type ChangeEventHandler, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
interface Props { interface Props {
onChange: (value: string | string[] | number | number[] | null) => void; onChange: (value: [string, number | null]) => void;
keyData: string; keyData: string;
} }

View file

@ -1,9 +1,9 @@
import React, { useMemo, type ChangeEventHandler } from 'react'; import React, { useMemo } from 'react';
import type { DataItem } from '.'; import type { DataItem, Value } from '../types';
import SelectInput from '@components/Inputs/SelectInput'; import SelectInput from '@components/Inputs/SelectInput';
interface Props { interface Props {
onChange: (value: string | string[] | null) => void; onChange: (value: Value) => void;
data: DataItem[]; data: DataItem[];
keyData: string; keyData: string;
isMultiple?: boolean; isMultiple?: boolean;
@ -31,28 +31,6 @@ export default function SelectFilter({
return options; return options;
}, [data, keyData]); }, [data, keyData]);
const onSelectChange: ChangeEventHandler<HTMLSelectElement> = ({
target,
}): void => {
if (!isMultiple) {
const value = target.value !== '' ? target.value : null;
onChange(value);
return;
}
const values = [];
// FIXME: This looks awfull, when updating the input use something better.
for (let index = 0; index < target.options.length; index++) {
const option = target.options[index];
if (!option.selected || option.value === '') continue;
values.push(option.value);
}
onChange(values);
};
return ( return (
<SelectInput <SelectInput
options={options} options={options}

View file

@ -1,19 +1,18 @@
import { HeaderType } from '../Table.tsx'; import { HeaderType, type Filter, type Value } from '../types.ts';
export { default as SelectFilter } from './SelectFilter.tsx'; export { default as SelectFilter } from './SelectFilter.tsx';
export { default as NumberInput } from './NumberInput.tsx'; export { default as NumberInput } from './NumberInput.tsx';
export type DataItem = Record<string, any>; export type DataItem = Record<string, any>;
export interface Filter { const filterString = (value: Value, data: any): boolean => {
type: HeaderType;
value: string | string[] | number | number[];
}
const filterString = (value: Filter['value'], data: any): boolean => {
return data.search(value) !== -1; return data.search(value) !== -1;
}; };
const filterNumber = (value: Filter['value'], data: any): boolean => { const filterNumber = (value: Value, data: any): boolean => {
if (!Array.isArray(value)) {
return true;
}
const [operator, numberValue] = value; const [operator, numberValue] = value;
if (numberValue === null) return true; if (numberValue === null) return true;
@ -34,14 +33,19 @@ const filterNumber = (value: Filter['value'], data: any): boolean => {
return data === numberValue; return data === numberValue;
} }
}; };
const filterSelect = (value: Filter['value'], data: any): boolean => { const filterSelect = (value: Value, data: any): boolean => {
return data === value; return data === value;
}; };
const filterMultiple = (value: Filter['value'], data: any): boolean => { const filterMultiple = (value: Value, data: any): boolean => {
if (typeof value === 'string') { if (value === null) {
return true;
}
if (typeof value === 'string' || typeof value === 'number') {
return data.includes(value); return data.includes(value);
} }
return value.every((filter: string) => data.includes(filter));
return value.every((filter: string | number) => data.includes(filter));
}; };
export const resolveFilterByType = (filter: Filter, data: any): boolean => { export const resolveFilterByType = (filter: Filter, data: any): boolean => {

View file

@ -6,34 +6,17 @@ import {
resolveFilterByType, resolveFilterByType,
type Filter, type Filter,
} from './Filters'; } from './Filters';
import type { DataItem, Header, Value } from './types';
export type DataItem = Record<string, any>; import { HeaderType } from './types';
export enum HeaderType {
Index,
String,
Number,
Select,
Multiple,
}
export interface Header {
key: string;
header: string;
formatter?: (data: DataItem) => string;
type: HeaderType;
}
interface Props { interface Props {
data: DataItem[]; data: DataItem[];
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 filtersId = useRef(crypto.randomUUID());
const filteredItems = useMemo(() => { const filteredItems = useMemo(() => {
return data.filter((item) => { return data.filter((item) => {
@ -51,9 +34,8 @@ export default function Table({ data, headers }: Props): JSX.Element {
const handleUpdateFilters = ( const handleUpdateFilters = (
name: string, name: string,
type: HeaderType, type: HeaderType,
value: any, value: Value,
): void => { ): void => {
console.log(value);
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
@ -91,7 +73,7 @@ export default function Table({ data, headers }: Props): JSX.Element {
function formatFilter(header: Header): JSX.Element { function formatFilter(header: Header): JSX.Element {
const baseProps = { const baseProps = {
key: header.key + filtersKey.current, key: header.key + filtersId.current,
keyData: header.key, keyData: header.key,
value: filters[header.key]?.value, value: filters[header.key]?.value,
onChange: (value: Value) => { onChange: (value: Value) => {
@ -129,9 +111,8 @@ export default function Table({ data, headers }: Props): JSX.Element {
<button <button
className="ml-auto" className="ml-auto"
onClick={() => { onClick={() => {
// FIXME: this clear the filters but not the value of the inputs
setFilters({}); setFilters({});
filtersKey.current = crypto.randomUUID(); filtersId.current = crypto.randomUUID();
}} }}
> >
Clear Filters Clear Filters

View file

@ -1,5 +1 @@
import Table, { HeaderType, type Header } from './Table'; export { default } from './Table';
export default Table;
export { HeaderType, type Header };

View file

@ -0,0 +1,23 @@
export type DataItem = Record<string, any>;
export type Value = string | string[] | number | number[] | null;
export enum HeaderType {
Index,
String,
Number,
Select,
Multiple,
}
export interface Header {
key: string;
header: string;
formatter?: (data: DataItem) => string;
type: HeaderType;
}
export interface Filter {
type: HeaderType;
value: Value;
}

View file

@ -5,7 +5,7 @@ interface Page {
current: boolean; current: boolean;
} }
export interface IUsePagination<T> { interface IUsePagination<T> {
items: T[]; items: T[];
changeOffset: (offset: Offset, newValue?: number) => void; changeOffset: (offset: Offset, newValue?: number) => void;
/** /**

View file

@ -1,7 +1,8 @@
--- ---
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'; import Table from '@components/Table';
import { HeaderType, type Header } from '@components/Table/types';
const rawGames = await getCollection('games'); const rawGames = await getCollection('games');