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"
|
"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
2529
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
|
|
@ -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[];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
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 {
|
interface Props {
|
||||||
onChange: (value: string | string[] | number | number[] | null) => void;
|
onChange: (value: [string, number | null]) => void;
|
||||||
keyData: string;
|
keyData: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
|
|
|
||||||
|
|
@ -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 => {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1 @@
|
||||||
import Table, { HeaderType, type Header } from './Table';
|
export { default } from './Table';
|
||||||
|
|
||||||
export default Table;
|
|
||||||
|
|
||||||
export { HeaderType, type Header };
|
|
||||||
|
|
|
||||||
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;
|
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;
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -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');
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue