Refactor types organization for table component
This commit is contained in:
parent
deb95b355f
commit
c0683dafb3
12 changed files with 1490 additions and 1195 deletions
16
package.json
16
package.json
|
|
@ -20,17 +20,17 @@
|
|||
"react-dom": "^18.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^5.50.0",
|
||||
"eslint": "^8.0.1",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
||||
"eslint": "^8.53.0",
|
||||
"eslint-config-prettier": "^8.10.0",
|
||||
"eslint-config-standard-with-typescript": "^35.0.0",
|
||||
"eslint-plugin-import": "^2.25.2",
|
||||
"eslint-plugin-n": "^15.0.0",
|
||||
"eslint-plugin-promise": "^6.0.0",
|
||||
"eslint-plugin-react": "^7.32.2",
|
||||
"eslint-plugin-import": "^2.29.0",
|
||||
"eslint-plugin-n": "^15.7.0",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"gh-pages": "^5.0.0",
|
||||
"prettier": "^2.8.8",
|
||||
"prettier-plugin-astro": "^0.10.0",
|
||||
"typescript": "*"
|
||||
"typescript": "^5.2.2"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
2529
pnpm-lock.yaml
generated
2529
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,15 +1,14 @@
|
|||
import React, {
|
||||
useState,
|
||||
type ChangeEventHandler,
|
||||
useRef,
|
||||
useEffect,
|
||||
} from 'react';
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import styles from './SelectInput.module.css';
|
||||
|
||||
interface Option {
|
||||
label: string;
|
||||
value: any;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
onChange: (value: string | string[] | null) => void;
|
||||
options: [{ label: string; value: any }];
|
||||
keyData: string;
|
||||
options: Option[];
|
||||
isMultiple?: boolean;
|
||||
value?: string | string[];
|
||||
}
|
||||
|
|
|
|||
0
src/components/Inputs/Types.ts
Normal file
0
src/components/Inputs/Types.ts
Normal file
|
|
@ -1,7 +1,7 @@
|
|||
import React, { useState, type ChangeEventHandler, useEffect } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
interface Props {
|
||||
onChange: (value: string | string[] | number | number[] | null) => void;
|
||||
onChange: (value: [string, number | null]) => void;
|
||||
keyData: string;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import React, { useMemo, type ChangeEventHandler } from 'react';
|
||||
import type { DataItem } from '.';
|
||||
import React, { useMemo } from 'react';
|
||||
import type { DataItem, Value } from '../types';
|
||||
import SelectInput from '@components/Inputs/SelectInput';
|
||||
|
||||
interface Props {
|
||||
onChange: (value: string | string[] | null) => void;
|
||||
onChange: (value: Value) => void;
|
||||
data: DataItem[];
|
||||
keyData: string;
|
||||
isMultiple?: boolean;
|
||||
|
|
@ -31,28 +31,6 @@ export default function SelectFilter({
|
|||
return options;
|
||||
}, [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 (
|
||||
<SelectInput
|
||||
options={options}
|
||||
|
|
|
|||
|
|
@ -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 NumberInput } from './NumberInput.tsx';
|
||||
|
||||
export type DataItem = Record<string, any>;
|
||||
|
||||
export interface Filter {
|
||||
type: HeaderType;
|
||||
value: string | string[] | number | number[];
|
||||
}
|
||||
|
||||
const filterString = (value: Filter['value'], data: any): boolean => {
|
||||
const filterString = (value: Value, data: any): boolean => {
|
||||
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;
|
||||
|
||||
if (numberValue === null) return true;
|
||||
|
|
@ -34,14 +33,19 @@ const filterNumber = (value: Filter['value'], data: any): boolean => {
|
|||
return data === numberValue;
|
||||
}
|
||||
};
|
||||
const filterSelect = (value: Filter['value'], data: any): boolean => {
|
||||
const filterSelect = (value: Value, data: any): boolean => {
|
||||
return data === value;
|
||||
};
|
||||
const filterMultiple = (value: Filter['value'], data: any): boolean => {
|
||||
if (typeof value === 'string') {
|
||||
const filterMultiple = (value: Value, data: any): boolean => {
|
||||
if (value === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (typeof value === 'string' || typeof value === 'number') {
|
||||
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 => {
|
||||
|
|
|
|||
|
|
@ -6,34 +6,17 @@ import {
|
|||
resolveFilterByType,
|
||||
type Filter,
|
||||
} from './Filters';
|
||||
|
||||
export type DataItem = Record<string, any>;
|
||||
|
||||
export enum HeaderType {
|
||||
Index,
|
||||
String,
|
||||
Number,
|
||||
Select,
|
||||
Multiple,
|
||||
}
|
||||
|
||||
export interface Header {
|
||||
key: string;
|
||||
header: string;
|
||||
formatter?: (data: DataItem) => string;
|
||||
type: HeaderType;
|
||||
}
|
||||
import type { DataItem, Header, Value } from './types';
|
||||
import { HeaderType } from './types';
|
||||
|
||||
interface Props {
|
||||
data: DataItem[];
|
||||
headers: Header[];
|
||||
}
|
||||
|
||||
type Value = string | string[] | number | number[] | null;
|
||||
|
||||
export default function Table({ data, headers }: Props): JSX.Element {
|
||||
const [filters, setFilters] = useState<Record<string, Filter>>({});
|
||||
const filtersKey = useRef(crypto.randomUUID());
|
||||
const filtersId = useRef(crypto.randomUUID());
|
||||
|
||||
const filteredItems = useMemo(() => {
|
||||
return data.filter((item) => {
|
||||
|
|
@ -51,9 +34,8 @@ export default function Table({ data, headers }: Props): JSX.Element {
|
|||
const handleUpdateFilters = (
|
||||
name: string,
|
||||
type: HeaderType,
|
||||
value: any,
|
||||
value: Value,
|
||||
): void => {
|
||||
console.log(value);
|
||||
setFilters((prev) => {
|
||||
if (value === null) {
|
||||
// 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 {
|
||||
const baseProps = {
|
||||
key: header.key + filtersKey.current,
|
||||
key: header.key + filtersId.current,
|
||||
keyData: header.key,
|
||||
value: filters[header.key]?.value,
|
||||
onChange: (value: Value) => {
|
||||
|
|
@ -129,9 +111,8 @@ export default function Table({ data, headers }: Props): JSX.Element {
|
|||
<button
|
||||
className="ml-auto"
|
||||
onClick={() => {
|
||||
// FIXME: this clear the filters but not the value of the inputs
|
||||
setFilters({});
|
||||
filtersKey.current = crypto.randomUUID();
|
||||
filtersId.current = crypto.randomUUID();
|
||||
}}
|
||||
>
|
||||
Clear Filters
|
||||
|
|
|
|||
|
|
@ -1,5 +1 @@
|
|||
import Table, { HeaderType, type Header } from './Table';
|
||||
|
||||
export default Table;
|
||||
|
||||
export { HeaderType, type Header };
|
||||
export { default } from './Table';
|
||||
|
|
|
|||
23
src/components/Table/types.ts
Normal file
23
src/components/Table/types.ts
Normal 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;
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ interface Page {
|
|||
current: boolean;
|
||||
}
|
||||
|
||||
export interface IUsePagination<T> {
|
||||
interface IUsePagination<T> {
|
||||
items: T[];
|
||||
changeOffset: (offset: Offset, newValue?: number) => void;
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
---
|
||||
import { getCollection } from 'astro:content';
|
||||
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');
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue