Skip to content

Commit

Permalink
fix: new react table (#538)
Browse files Browse the repository at this point in the history
* chore: move from old table library to tanstack react-table

* fix: remove logs

* feat: implement column sorting

---------

Co-authored-by: secondl1ght <[email protected]>
  • Loading branch information
AmbossKeegan and secondl1ght authored Jun 13, 2023
1 parent ffb955c commit edc7785
Show file tree
Hide file tree
Showing 4 changed files with 320 additions and 44 deletions.
45 changes: 45 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"@nestjs/schedule": "^2.2.2",
"@nestjs/throttler": "^4.0.0",
"@nestjs/websockets": "^9.4.2",
"@tanstack/react-table": "^8.9.1",
"@visx/axis": "^2.12.2",
"@visx/chord": "^2.10.0",
"@visx/event": "^2.6.0",
Expand Down
215 changes: 215 additions & 0 deletions src/client/src/components/table-v2/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
import { useEffect, useState } from 'react';
import styled, { css } from 'styled-components';
import {
useReactTable,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
flexRender,
ColumnDef,
SortingState,
} from '@tanstack/react-table';
import { Input } from '../input';
import { separationColor } from '../../../src/styles/Themes';

interface TableV2Props {
columns: ColumnDef<any, any>[];
data: any;
filterPlaceholder: string;
withBorder?: boolean;
alignCenter?: boolean;
fontSize?: string;
}

const FilterLine = styled.div`
margin-bottom: 24px;
`;

type StyledTableProps = {
withBorder?: boolean;
alignCenter?: boolean;
fontSize?: string;
};

const Styles = styled.div`
overflow-x: auto;
table {
border-spacing: 0;
tr {
:last-child {
td {
border-bottom: 0;
}
}
}
.cursor {
cursor: pointer;
}
,
th,
td {
font-size: ${({ fontSize }: StyledTableProps) => fontSize || '14px'};
text-align: left;
margin: 0;
padding: 8px;
${({ withBorder }: StyledTableProps) =>
withBorder &&
css`
border-bottom: 1px solid ${separationColor};
`}
${({ alignCenter }: StyledTableProps) =>
alignCenter &&
css`
text-align: center;
padding: 8px;
`}
:last-child {
border-right: 0;
}
}
}
`;

export default function TableV2({
columns,
data,
filterPlaceholder,
withBorder,
alignCenter,
fontSize,
}: TableV2Props) {
const [globalFilter, setGlobalFilter] = useState('');
const [sorting, setSorting] = useState<SortingState>([]);

const table = useReactTable({
data,
columns,
state: {
globalFilter,
sorting,
},
enableSorting: true,
onGlobalFilterChange: setGlobalFilter,
onSortingChange: setSorting,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
getPaginationRowModel: getPaginationRowModel(),
});

return (
<>
<FilterLine>
<DebouncedInput
value={globalFilter ?? ''}
onChange={value => setGlobalFilter(String(value))}
placeholder={filterPlaceholder}
count={table.getFilteredRowModel().rows.length}
/>
</FilterLine>

<Styles
withBorder={withBorder}
fontSize={fontSize}
alignCenter={alignCenter}
>
<table>
<thead>
{table.getHeaderGroups().map(headerGroup => (
<tr key={headerGroup.id}>
{headerGroup.headers.map(header => {
return (
<th
key={header.id}
colSpan={header.colSpan}
style={{ whiteSpace: 'nowrap' }}
>
{header.isPlaceholder ? null : (
<>
<div
{...{
className: header.column.getCanSort()
? 'cursor'
: '',
onClick: header.column.getToggleSortingHandler(),
}}
>
{flexRender(
header.column.columnDef.header,
header.getContext()
)}
{{
asc: ' ⬆',
desc: ' ⬇',
}[header.column.getIsSorted() as string] ?? null}
</div>
</>
)}
</th>
);
})}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map(row => {
return (
<tr key={row.id}>
{row.getVisibleCells().map(cell => {
return (
<td key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</td>
);
})}
</tr>
);
})}
</tbody>
</table>
</Styles>
</>
);
}

// A debounced input react component
function DebouncedInput({
value: initialValue,
onChange,
debounce = 500,
placeholder,
count,
}: {
value: string | number;
onChange: (value: string | number) => void;
count: number;
debounce?: number;
placeholder?: string;
} & Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onChange'>) {
const [value, setValue] = useState(initialValue);

useEffect(() => {
setValue(initialValue);
}, [initialValue]);

useEffect(() => {
const timeout = setTimeout(() => {
onChange(value);
}, debounce);

return () => clearTimeout(timeout);
}, [value]);

return (
<Input
maxWidth={'300px'}
value={value || ''}
onChange={e => setValue(e.target.value)}
placeholder={`Search ${count} ${placeholder || ''}`}
/>
);
}
Loading

0 comments on commit edc7785

Please sign in to comment.