From 17108bc560f6ff1b9c91b83309fbc718d5ea3c37 Mon Sep 17 00:00:00 2001 From: Rohit Malhotra Date: Fri, 23 Aug 2024 13:52:42 +0530 Subject: [PATCH] DApp-1799 blocks/table (#1824) * table in progress * in progress * in progress * table complete * fixed header route + reverted changes * added alignment support --- package.json | 3 +- src/blocks/index.ts | 3 +- src/blocks/table/Table.tsx | 175 ++++++++++++++++++ src/blocks/table/Table.types.ts | 16 ++ src/blocks/table/index.ts | 1 + src/blocks/theme/colors/colors.semantics.ts | 4 + src/blocks/theme/semantics/semantics.table.ts | 14 ++ src/structure/Header.tsx | 2 +- yarn.lock | 50 ++++- 9 files changed, 263 insertions(+), 5 deletions(-) create mode 100644 src/blocks/table/Table.tsx create mode 100644 src/blocks/table/Table.types.ts create mode 100644 src/blocks/table/index.ts create mode 100644 src/blocks/theme/semantics/semantics.table.ts diff --git a/package.json b/package.json index d702c2838e..17731aed2f 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ }, "dependencies": { "@babel/runtime": "^7.21.0", - "@emotion/react": "^11.8.2", + "@emotion/react": "^11.13.0", "@emotion/styled": "^11.8.1", "@ethersproject/abi": "^5.6.0", "@ethersproject/providers": "^5.6.0", @@ -44,6 +44,7 @@ "@reach/combobox": "^0.18.0", "@reach/tabs": "^0.18.0", "@reduxjs/toolkit": "^1.7.1", + "@table-library/react-table-library": "^4.1.7", "@tanstack/react-query": "^5.44.0", "@tanstack/react-query-devtools": "^5.44.0", "@testing-library/dom": "^9.0.1", diff --git a/src/blocks/index.ts b/src/blocks/index.ts index 90e32e913f..936b25aa9e 100644 --- a/src/blocks/index.ts +++ b/src/blocks/index.ts @@ -13,7 +13,8 @@ export { Separator, type SeparatorProps } from './separator'; export { Skeleton, type SkeletonProps } from './skeleton'; export { Select, type SelectProps } from './select'; export { Tabs, type TabsProps, type TabItem } from './tabs'; -export { Tag } from './tag'; +export { Table, type TableProps } from './table'; +export { Tag, type TagProps } from './tag'; export { Text, type TextProps } from './text'; export { Tooltip, type TooltipProps } from './tooltip'; export { TextArea, type TextAreaProps } from './textarea'; diff --git a/src/blocks/table/Table.tsx b/src/blocks/table/Table.tsx new file mode 100644 index 0000000000..63c8d8e978 --- /dev/null +++ b/src/blocks/table/Table.tsx @@ -0,0 +1,175 @@ +import { FC, useMemo } from 'react'; +import { + Table as ReactTable, + Header, + HeaderRow, + Body, + Row, + HeaderCell, + Cell, +} from '@table-library/react-table-library/table'; +import { useTheme } from '@table-library/react-table-library/theme'; +import styled from 'styled-components'; +import { textVariants } from '../text'; +import { SurfaceColors } from '../theme/Theme.types'; +import { Column, DataSource } from './Table.types'; + +export type TableProps = { + columns: Column[]; + dataSource: DataSource[]; + fixedHeader?: boolean; + backgroundColor?: SurfaceColors; +}; + +const StyledHeaderCell = styled(HeaderCell)<{ headerAlignment?: Column['headerAlignment'] }>` + ${({ headerAlignment }) => + headerAlignment + ? ` + div { + display: flex; + justify-content: ${headerAlignment}; + } + ` + : ''} +`; + +const StyledRowCell = styled(Cell)<{ cellAlignment?: Column['cellAlignment'] }>` + ${({ cellAlignment }) => + cellAlignment + ? ` + div { + display: flex; + justify-content: ${cellAlignment}; + } + ` + : ''} +`; + +const Table: FC = ({ backgroundColor = 'surface-secondary', columns, dataSource, fixedHeader = false }) => { + const columnData = useMemo(() => { + const columnWidths = columns.map((col) => col.width || `${100 / columns.length}%`); + + const leftRightPositionCSS = columns + .map((col, index) => { + if (col?.fixed == 'left') { + return ` + &:nth-of-type(${index + 1}) { + left: ${index === 0 ? '0px' : columnWidths[index]}; + }; + `; + } + if (col?.fixed == 'right') { + return ` + &:nth-of-type(${index + 1}) { + right: ${index + 1 === columns.length ? '0px' : columnWidths[index]}; + }; + `; + } + + return ''; + }) + .join(''); + + return { + columnWidthCSS: columnWidths.join(' '), + leftRightPositionCSS, + }; + }, [columns]); + + const theme = useTheme({ + Table: ` + --data-table-library_grid-template-columns: ${columnData.columnWidthCSS}; + `, + BaseCell: ` + ${columnData.leftRightPositionCSS} + `, + Cell: ` + align-items: center; + align-self: stretch; + border-bottom: var(--border-sm) solid var(--components-table-stroke-default); + color: var(--components-table-text-default); + display: flex; + flex: 1 0 0; + font-family: var(--font-family); + font-size: ${textVariants['bs-semibold'].fontSize}; + font-style: ${textVariants['bs-semibold'].fontStyle}; + font-weight: ${textVariants['bs-semibold'].fontWeight}; + line-height: ${textVariants['bs-semibold'].lineHeight}; + gap: var(--spacing-xxs); + height: 56px; + padding: var(--spacing-xxxs); + `, + Row: ` + background: var(--${backgroundColor}); + `, + HeaderRow: ` + background: var(--${backgroundColor}); + `, + HeaderCell: ` + align-items: center; + color: var(--components-table-text-heading); + display: flex; + font-family: var(--font-family); + font-size: ${textVariants['c-bold'].fontSize}; + font-style: ${textVariants['c-bold'].fontStyle}; + font-weight: ${textVariants['c-bold'].fontWeight}; + line-height: ${textVariants['c-bold'].lineHeight}; + padding: var(--spacing-xxs) var(--spacing-xxxs); + gap: 10px; + `, + }); + + return ( + + {(tableList: DataSource[]) => ( + <> +
+ + {columns.map((column, index) => ( + + {column.title} + + ))} + +
+ + + {tableList.map((record) => ( + + {columns.map((column) => { + const cellValue = `${record?.[column.dataIndex] || ''}`; + return ( + + {column.render ? column.render(cellValue, record) : cellValue} + + ); + })} + + ))} + + + )} +
+ ); +}; + +export { Table }; diff --git a/src/blocks/table/Table.types.ts b/src/blocks/table/Table.types.ts new file mode 100644 index 0000000000..c7641a938d --- /dev/null +++ b/src/blocks/table/Table.types.ts @@ -0,0 +1,16 @@ +import { CSSProperties, ReactNode } from 'react'; + +export type Column = { + title: string; + dataIndex: string; + cellAlignment?: CSSProperties['justifyContent']; + headerAlignment?: CSSProperties['justifyContent']; + render?: (text: string, record: any) => ReactNode; + width?: string; + fixed?: 'left' | 'right'; +}; + +export type DataSource = { + id: string; + [key: string]: any; +}; diff --git a/src/blocks/table/index.ts b/src/blocks/table/index.ts new file mode 100644 index 0000000000..75193adc33 --- /dev/null +++ b/src/blocks/table/index.ts @@ -0,0 +1 @@ +export * from './Table'; diff --git a/src/blocks/theme/colors/colors.semantics.ts b/src/blocks/theme/colors/colors.semantics.ts index 2f48d2010a..99888511d5 100644 --- a/src/blocks/theme/colors/colors.semantics.ts +++ b/src/blocks/theme/colors/colors.semantics.ts @@ -25,6 +25,7 @@ import { textAreaSemantics } from '../semantics/semantics.textarea'; import { toastSemantics } from '../semantics/semantics.toast'; import { tooltipSemantics } from '../semantics/semantics.tooltip'; import { spinnerSemantics } from '../semantics/semantics.spinner'; +import { tableSemantics } from '../semantics/semantics.table'; // TODO: find a better way to do this in future type SemanticKeys = { @@ -46,6 +47,7 @@ type SemanticKeys = { surface: 'surface'; stroke: 'stroke'; skeleton: 'components-skeleton-loader'; + table: 'components-table'; tag: 'components-tag'; text: 'text'; textArea: 'components-textarea'; @@ -74,6 +76,7 @@ export const semanticKeys: SemanticKeys = { surface: 'surface', stroke: 'stroke', skeleton: 'components-skeleton-loader', + table: 'components-table', tag: 'components-tag', text: 'text', textArea: 'components-textarea', @@ -102,6 +105,7 @@ export const colorSemantics = { [semanticKeys.surface]: surfaceSemantics, [semanticKeys.stroke]: strokeSemantics, [semanticKeys.skeleton]: skeletonSemantics, + [semanticKeys.table]: tableSemantics, [semanticKeys.tag]: tagSemantics, [semanticKeys.text]: textSemantics, [semanticKeys.textArea]: textAreaSemantics, diff --git a/src/blocks/theme/semantics/semantics.table.ts b/src/blocks/theme/semantics/semantics.table.ts new file mode 100644 index 0000000000..ccdaed49b6 --- /dev/null +++ b/src/blocks/theme/semantics/semantics.table.ts @@ -0,0 +1,14 @@ +import { iconSemantics } from './semantics.icon'; +import { strokeSemantics } from './semantics.stroke'; +import { textSemantics } from './semantics.text'; + +export const tableSemantics = { + 'stroke-default': { light: strokeSemantics['tertiary'].light, dark: strokeSemantics['tertiary'].dark }, + + 'text-default': { light: textSemantics['primary'].light, dark: textSemantics['primary'].dark }, + 'text-heading': { light: textSemantics['tertiary'].light, dark: textSemantics['tertiary'].dark }, + 'text-disabled': { light: textSemantics['state-disabled'].light, dark: textSemantics['state-disabled'].dark }, + + 'icon-default': { light: iconSemantics['primary'].light, dark: iconSemantics['primary'].dark }, + 'icon-disabled': { light: iconSemantics['state-disabled'].light, dark: iconSemantics['state-disabled'].dark }, +}; diff --git a/src/structure/Header.tsx b/src/structure/Header.tsx index 30ac133395..165f141068 100644 --- a/src/structure/Header.tsx +++ b/src/structure/Header.tsx @@ -204,7 +204,7 @@ function Header({ isDarkMode, darkModeToggle }) { justify="flex-start" flex="0" > - + diff --git a/yarn.lock b/yarn.lock index 7a1e3cee29..e769663ed3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -797,7 +797,7 @@ __metadata: languageName: node linkType: hard -"@emotion/react@npm:^11.8.2": +"@emotion/react@npm:^11.13.0": version: 11.13.0 resolution: "@emotion/react@npm:11.13.0" dependencies: @@ -6241,6 +6241,21 @@ __metadata: languageName: node linkType: hard +"@table-library/react-table-library@npm:^4.1.7": + version: 4.1.7 + resolution: "@table-library/react-table-library@npm:4.1.7" + dependencies: + clsx: "npm:1.1.1" + react-virtualized-auto-sizer: "npm:1.0.7" + react-window: "npm:1.8.7" + peerDependencies: + "@emotion/react": ">= 11" + react: ">=16.8.0" + react-dom: ">=16.8.0" + checksum: 10/14fba2fdfd09d25d6e8321874b934ce250814e7bdb8a5c5dc627dbcd61f5d2c5d1d5ae3a52ce4036a1f28fb883ecec88ed9bd4441b2756258fe05dfce2d4fd15 + languageName: node + linkType: hard + "@tanstack/query-async-storage-persister@npm:4.29.23": version: 4.29.23 resolution: "@tanstack/query-async-storage-persister@npm:4.29.23" @@ -9964,6 +9979,13 @@ __metadata: languageName: node linkType: hard +"clsx@npm:1.1.1": + version: 1.1.1 + resolution: "clsx@npm:1.1.1" + checksum: 10/ff052650329773b9b245177305fc4c4dc3129f7b2be84af4f58dc5defa99538c61d4207be7419405a5f8f3d92007c954f4daba5a7b74e563d5de71c28c830063 + languageName: node + linkType: hard + "clsx@npm:2.1.0": version: 2.1.0 resolution: "clsx@npm:2.1.0" @@ -18216,7 +18238,7 @@ __metadata: "@3id/connect": "npm:0.4.1" "@babel/runtime": "npm:^7.21.0" "@ceramicnetwork/ceramic-core": "npm:0.12.4" - "@emotion/react": "npm:^11.8.2" + "@emotion/react": "npm:^11.13.0" "@emotion/styled": "npm:^11.8.1" "@ethersproject/abi": "npm:^5.6.0" "@ethersproject/providers": "npm:^5.6.0" @@ -18236,6 +18258,7 @@ __metadata: "@reach/combobox": "npm:^0.18.0" "@reach/tabs": "npm:^0.18.0" "@reduxjs/toolkit": "npm:^1.7.1" + "@table-library/react-table-library": "npm:^4.1.7" "@tanstack/react-query": "npm:^5.44.0" "@tanstack/react-query-devtools": "npm:^5.44.0" "@testing-library/dom": "npm:^9.0.1" @@ -19195,6 +19218,16 @@ __metadata: languageName: node linkType: hard +"react-virtualized-auto-sizer@npm:1.0.7": + version: 1.0.7 + resolution: "react-virtualized-auto-sizer@npm:1.0.7" + peerDependencies: + react: ^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc + react-dom: ^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc + checksum: 10/a16d8ac11a0efa20d44f36a5cf3854027895b806363a409f2ddc07ff92bfc92aaf5b8eee21f3e1b153cee2273db5e5383b6334d4b530d934ed08808ecfb45b9f + languageName: node + linkType: hard + "react-virtualized-auto-sizer@npm:^1.0.2": version: 1.0.24 resolution: "react-virtualized-auto-sizer@npm:1.0.24" @@ -19218,6 +19251,19 @@ __metadata: languageName: node linkType: hard +"react-window@npm:1.8.7": + version: 1.8.7 + resolution: "react-window@npm:1.8.7" + dependencies: + "@babel/runtime": "npm:^7.0.0" + memoize-one: "npm:>=3.1.1 <6" + peerDependencies: + react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + checksum: 10/c838d60be2d2a6535e5a7db63da451aad6bc75f01de37ba3cdab13fee9acc7a75ff16f17772228faadfbfedfa51addc9496806fde92e7bec7bc5f35549b37450 + languageName: node + linkType: hard + "react-window@npm:^1.8.5": version: 1.8.10 resolution: "react-window@npm:1.8.10"