diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..55712c1 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "typescript.tsdk": "node_modules/typescript/lib" +} \ No newline at end of file diff --git a/package.json b/package.json index 4c1ea94..d1fd572 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "@synthetixio/transaction-notifier": "^2.70.1", "clsx": "^1.1.1", "lodash": "^4.17.21", + "react-table": "^7.8.0", "react-transition-group": "^4.4.2" }, "devDependencies": { @@ -52,6 +53,7 @@ "@types/node": "^16.11.13", "@types/react": "^18.0.0", "@types/react-dom": "^18.0.0", + "@types/react-table": "^7.7.12", "@types/react-transition-group": "^4.4.4", "@typescript-eslint/eslint-plugin": "^5.7.0", "@typescript-eslint/parser": "^5.7.0", @@ -71,8 +73,8 @@ "lint-staged": "^12.1.2", "postcss": "^8.4.5", "prettier": "^2.5.1", - "react": "^18.0.0", - "react-dom": "^18.0.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", "react-scripts": "5.0.0", "rollup": "^2.61.1", "rollup-plugin-copy": "^3.4.0", @@ -83,7 +85,7 @@ "semantic-release": "19.0.2", "storybook-dark-mode": "^1.1.0", "tailwindcss": "^3.0.3", - "typescript": "^4.5.4", + "typescript": "^4.7.4", "web-vitals": "^2.1.2", "webpack": "5" }, diff --git a/src/components/Pagination/Pagination.tsx b/src/components/Pagination/Pagination.tsx index 026baaf..3430bcb 100644 --- a/src/components/Pagination/Pagination.tsx +++ b/src/components/Pagination/Pagination.tsx @@ -22,7 +22,7 @@ export const Pagination: React.FC = ({ localization = { of: 'of' }, - className + className = 'ui-max-w-[350px]' }) => { const pageCount = Math.ceil(length / pageSize); @@ -35,25 +35,27 @@ export const Pagination: React.FC = ({ return (
- gotoPage(0)} - /> +
+ gotoPage(0)} + /> - gotoPage(Math.max(pageIndex - 1, 0))} - /> + gotoPage(Math.max(pageIndex - 1, 0))} + /> +
{startIndex + 1}-{endIndex} @@ -62,21 +64,23 @@ export const Pagination: React.FC = ({ {length}
- canNextPage && gotoPage(pageIndex + 1)} - /> +
+ canNextPage && gotoPage(pageIndex + 1)} + /> - gotoPage(pageCount - 1)} - /> + gotoPage(pageCount - 1)} + /> +
); }; diff --git a/src/components/Table/Table.stories.tsx b/src/components/Table/Table.stories.tsx new file mode 100644 index 0000000..2829d9f --- /dev/null +++ b/src/components/Table/Table.stories.tsx @@ -0,0 +1,95 @@ +import { ComponentMeta, ComponentStory } from '@storybook/react'; +import { Card } from 'components/Card/Card'; +import { Column } from 'react-table'; + +import { Table } from './Table'; + +export default { + title: 'Table', + component: Table, + decorators: [(Story) => ] +} as ComponentMeta; + +const Template: ComponentStory = (args) => ( + + + +); + +export const Primary = Template.bind({}); + +type Crypto = { + coin: string; + symbol: string; + price: number; + percent1H: number; + percent24H: number; + percent7D: number; + last7Days: string; +}; + +const data: Crypto[] = [ + { + coin: 'Synthetix', + symbol: 'sUSD', + price: 11, + percent1H: -1.3, + percent24H: -3.5, + percent7D: -8.6, + last7Days: 'https://picsum.photos/50' + }, + { + coin: 'Synthetix', + symbol: 'SNX', + price: 2.71, + percent1H: -5.3, + percent24H: 9.5, + percent7D: 2.6, + last7Days: 'https://picsum.photos/50' + } +]; + +const columns: Column[] = [ + { + disableSortBy: true, + Header: 'Coin', + accessor: 'symbol', + columnClass: 'ui-text-left', + cellClass: 'ui-text-left', + Cell: ({ row }) => ( +
+ + {row.original.symbol} + {row.original.coin} +
+ ) + }, + { + Header: 'Price', + accessor: (row) => row.price, + sortType: (a, b) => a.original.price - b.original.price + }, + { + Header: '1h', + accessor: 'percent1H', + Cell: (row) => row.value, + sortType: (a, b) => a.original.percent1H - b.original.percent1H + }, + { + Header: '7h', + accessor: 'percent7D', + Cell: (row) => row.value, + sortType: (a, b) => a.original.percent7D - b.original.percent7D + } +]; + +Primary.args = { + className: '', + data: [...data, ...data], + columns: columns, + initialState: { pageSize: 3 } +}; diff --git a/src/components/Table/Table.tsx b/src/components/Table/Table.tsx new file mode 100644 index 0000000..979afc7 --- /dev/null +++ b/src/components/Table/Table.tsx @@ -0,0 +1,139 @@ +/* eslint-disable react/jsx-key */ +import clsx from 'clsx'; +import { Icon } from 'components/Icon/Icon'; +import { Pagination, PaginationLocalization } from 'components/Pagination/Pagination'; +import React, { ReactElement, ReactNode, useMemo } from 'react'; +import { + Row, + TableHeaderProps, + TableOptions, + useFlexLayout, + usePagination, + useSortBy, + useTable +} from 'react-table'; + +export interface TableProps> extends TableOptions { + className?: string; + paginationLocalization?: PaginationLocalization; + onClick?: (row: Row) => void; +} + +export const Table = >(props: TableProps): ReactElement => { + const { className, paginationLocalization, onClick, ...rest } = props; + + const defaultColumn = useMemo( + () => ({ + width: 150, + minWidth: 100 + }), + [] + ); + + const tableInstance = useTable( + { ...rest, defaultColumn }, + useSortBy, + useFlexLayout, + usePagination + ); + + const { + headerGroups, + prepareRow, + getTableProps, + getTableBodyProps, + page, + state, + gotoPage, + rows + } = tableInstance; + + const { pageIndex, pageSize } = state; + + return ( + <> +
+
+ + {headerGroups.map((headerGroup) => ( + + {headerGroup.headers.map((column) => ( + + <> + {column.render('Header')} + + {column.isSorted && ( + + )} + + + + ))} + + ))} + + + + {page.map((row) => { + prepareRow(row); + return ( + onClick?.(row)} + {...row.getRowProps()} + className={clsx( + 'ui-text-white ui-border-t last:ui-border-b ui-border-solid ui-border-gray-700', + { + 'ui-cursor-pointer': !!onClick + } + )} + > + {row.cells.map((cell) => ( + + ))} + + ); + })} + +
+ + {cell.render('Cell') as ReactNode} + +
+ + + + ); +}; + +interface HeaderProps extends TableHeaderProps { + children: React.ReactNode; +} + +const TableHeader: React.FC = ({ children, ...props }) => { + return {children}; +}; diff --git a/src/types/react-table-config.d.ts b/src/types/react-table-config.d.ts new file mode 100644 index 0000000..81c1acf --- /dev/null +++ b/src/types/react-table-config.d.ts @@ -0,0 +1,124 @@ +/* eslint-disable */ +import { + UseColumnOrderInstanceProps, + UseColumnOrderState, + UseExpandedHooks, + UseExpandedInstanceProps, + UseExpandedOptions, + UseExpandedRowProps, + UseExpandedState, + UseFiltersColumnOptions, + UseFiltersColumnProps, + UseFiltersInstanceProps, + UseFiltersOptions, + UseFiltersState, + UseGlobalFiltersColumnOptions, + UseGlobalFiltersInstanceProps, + UseGlobalFiltersOptions, + UseGlobalFiltersState, + UseGroupByCellProps, + UseGroupByColumnOptions, + UseGroupByColumnProps, + UseGroupByHooks, + UseGroupByInstanceProps, + UseGroupByOptions, + UseGroupByRowProps, + UseGroupByState, + UsePaginationInstanceProps, + UsePaginationOptions, + UsePaginationState, + UseResizeColumnsColumnOptions, + UseResizeColumnsColumnProps, + UseResizeColumnsOptions, + UseResizeColumnsState, + UseRowSelectHooks, + UseRowSelectInstanceProps, + UseRowSelectOptions, + UseRowSelectRowProps, + UseRowSelectState, + UseRowStateCellProps, + UseRowStateInstanceProps, + UseRowStateOptions, + UseRowStateRowProps, + UseRowStateState, + UseSortByColumnOptions, + UseSortByColumnProps, + UseSortByHooks, + UseSortByInstanceProps, + UseSortByOptions, + UseSortByState +} from "react-table"; + +declare module "react-table" { + // take this file as-is, or comment out the sections that don't apply to your plugin configuration + + export interface TableOptions> + extends UseExpandedOptions, + UseFiltersOptions, + UseGlobalFiltersOptions, + UseGroupByOptions, + UsePaginationOptions, + UseResizeColumnsOptions, + UseRowSelectOptions, + UseRowStateOptions, + UseSortByOptions, + // note that having Record here allows you to add anything to the options, this matches the spirit of the + // underlying js library, but might be cleaner if it's replaced by a more specific type that matches your + // feature set, this is a safe default. + Record {} + + export interface Hooks = Record> + extends UseExpandedHooks, + UseGroupByHooks, + UseRowSelectHooks, + UseSortByHooks {} + + export interface TableInstance = Record> + extends UseColumnOrderInstanceProps, + UseExpandedInstanceProps, + UseFiltersInstanceProps, + UseGlobalFiltersInstanceProps, + UseGroupByInstanceProps, + UsePaginationInstanceProps, + UseRowSelectInstanceProps, + UseRowStateInstanceProps, + UseSortByInstanceProps {} + + export interface TableState = Record> + extends UseColumnOrderState, + UseExpandedState, + UseFiltersState, + UseGlobalFiltersState, + UseGroupByState, + UsePaginationState, + UseResizeColumnsState, + UseRowSelectState, + UseRowStateState, + UseSortByState {} + + export interface ColumnInterface = Record> + extends UseFiltersColumnOptions, + UseGlobalFiltersColumnOptions, + UseGroupByColumnOptions, + UseResizeColumnsColumnOptions, + UseSortByColumnOptions { + cellClass?: string; + columnClass?: string; + } + + export interface ColumnInstance = Record> + extends UseFiltersColumnProps, + UseGroupByColumnProps, + UseResizeColumnsColumnProps, + UseSortByColumnProps {} + + export interface Cell = Record, V = any> + extends UseGroupByCellProps, + UseRowStateCellProps {} + + export interface Row = Record> + extends UseExpandedRowProps, + UseGroupByRowProps, + UseRowSelectRowProps, + UseRowStateRowProps {} +} diff --git a/tailwind.config.js b/tailwind.config.js index 927d3f4..c0405bb 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -98,7 +98,7 @@ module.exports = { 'gray-400': 'var(--color-gray-400)', 'gray-500': 'var(--color-gray-500)', 'gray-600': 'var(--color-gray-600)', - 'gray-650': 'rgba(var(--color-gray-650), )', + 'gray-650': 'var(--color-gray-650)', 'gray-700': 'var(--color-gray-700)', 'gray-800': 'var(--color-gray-800)', 'gray-900': 'var(--color-gray-900)', diff --git a/yarn.lock b/yarn.lock index fae3323..354b0ba 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4160,6 +4160,13 @@ dependencies: "@types/react" "*" +"@types/react-table@^7.7.12": + version "7.7.12" + resolved "https://registry.yarnpkg.com/@types/react-table/-/react-table-7.7.12.tgz#628011d3cb695b07c678704a61f2f1d5b8e567fd" + integrity sha512-bRUent+NR/WwtDGwI/BqhZ8XnHghwHw0HUKeohzB5xN3K2qKWYE5w19e7GCuOkL1CXD9Gi1HFy7TIm2AvgWUHg== + dependencies: + "@types/react" "*" + "@types/react-transition-group@^4.4.4": version "4.4.4" resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.4.tgz#acd4cceaa2be6b757db61ed7b432e103242d163e" @@ -14473,7 +14480,7 @@ react-docgen@^5.0.0: node-dir "^0.1.10" strip-indent "^3.0.0" -react-dom@^18.0.0: +react-dom@^18.2.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== @@ -14590,6 +14597,11 @@ react-syntax-highlighter@^15.4.5: prismjs "^1.27.0" refractor "^3.6.0" +react-table@^7.8.0: + version "7.8.0" + resolved "https://registry.yarnpkg.com/react-table/-/react-table-7.8.0.tgz#07858c01c1718c09f7f1aed7034fcfd7bda907d2" + integrity sha512-hNaz4ygkZO4bESeFfnfOft73iBUj8K5oKi1EcSHPAibEydfsX2MyU6Z8KCr3mv3C9Kqqh71U+DhZkFvibbnPbA== + react-transition-group@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.2.tgz#8b59a56f09ced7b55cbd53c36768b922890d5470" @@ -14600,7 +14612,7 @@ react-transition-group@^4.4.2: loose-envify "^1.4.0" prop-types "^15.6.2" -react@^18.0.0: +react@^18.2.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== @@ -16884,7 +16896,7 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== -typescript@^4.4.3, typescript@^4.5.4, typescript@^4.6.4: +typescript@^4.4.3, typescript@^4.6.4, typescript@^4.7.4: version "4.7.4" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235" integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==