-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added table component and some glob constants
- Loading branch information
1 parent
c606cfd
commit 16f8078
Showing
13 changed files
with
487 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import React from "react"; | ||
import Svg from "../Svg"; | ||
import { SvgProps } from "../types"; | ||
|
||
const Icon: React.FC<React.PropsWithChildren<SvgProps>> = (props) => ( | ||
<Svg viewBox="0 0 12 12" {...props} fill="none"> | ||
<path d="M5.25 9L3.75 10.5L2.25 9" strokeLinecap="round" strokeLinejoin="round" /> | ||
<path d="M6.75 3L8.25 1.5L9.75 3" strokeLinecap="round" strokeLinejoin="round" /> | ||
<path d="M8.25 10.5V1.5" strokeLinecap="round" strokeLinejoin="round" /> | ||
<path d="M3.75 1.5V10.5" strokeLinecap="round" strokeLinejoin="round" /> | ||
</Svg> | ||
); | ||
|
||
export default Icon; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
import TableComponent from "./index"; | ||
import React, { useState } from "react"; | ||
import { Box, Flex, Grid } from "../Box"; | ||
import Button from "../Button"; | ||
import Text from "../Text"; | ||
import Dropdown from "../Dropdown"; | ||
import Checkbox from "../Checkbox"; | ||
import Tooltip from "../Tooltip"; | ||
|
||
export default { | ||
title: "Components/Table", | ||
component: TableComponent, | ||
argTypes: {}, | ||
}; | ||
|
||
export const Table = () => { | ||
const [page, setPage] = useState(1); | ||
const [perPage, setPerPage] = useState(3); | ||
const [withMinHeight, setWithMinHeight] = useState(true); | ||
const [showCustomHeader, setShowCustomHeader] = useState(true); | ||
const [loading, setLoading] = useState(false); | ||
const [empty, setEmpty] = useState(false); | ||
|
||
const items = [ | ||
{ id: 0, price: 12, name: "H Name", someField: "Some Field Value", sortField: "123" }, | ||
{ id: 1, price: 54, name: "D Name", someField: "Some Field Value", sortField: "Some String" }, | ||
{ id: 2, price: 1, name: "I Name", someField: "Some Field Value", sortField: "Some String" }, | ||
{ | ||
id: 3, | ||
price: 31, | ||
name: "G Name", | ||
someField: "Some Field Value", | ||
sortField: "123", | ||
}, | ||
{ id: 4, price: 82, name: "J Name", someField: "Some Field Value", sortField: "Some String" }, | ||
{ id: 5, price: 51, name: "F Name", someField: "Some Field Value", sortField: "123" }, | ||
{ id: 6, price: 66, name: "C Name", someField: "Some Field Value", sortField: "Some String" }, | ||
{ id: 7, price: 89, name: "A Name", someField: "Some Field Value", sortField: "Some String" }, | ||
{ id: 8, price: 5, name: "E Name", someField: "Some Field Value", sortField: "123" }, | ||
{ id: 9, price: 57, name: "B Name", someField: "Some Field Value", sortField: "Some String" }, | ||
]; | ||
|
||
type ItemType = (typeof items)[number]; | ||
|
||
return ( | ||
<Box> | ||
<Grid gridTemplateColumns={"repeat(2, auto)"} gridGap={"16px"} alignItems="center" mb={"24px"}> | ||
<Flex alignItems="center"> | ||
<Text mr={"8px"}>Per page:</Text> | ||
<Dropdown | ||
value={perPage} | ||
onChange={setPerPage} | ||
items={[ | ||
{ value: 3, title: "3 Items" }, | ||
{ value: 5, title: "5 Items" }, | ||
{ value: 10, title: "10 Items" }, | ||
]} | ||
/> | ||
</Flex> | ||
<label style={{ cursor: "pointer", display: "flex" }}> | ||
<Text mr={"8px"}>Min height (347px)</Text> | ||
<Checkbox value={withMinHeight} onChange={setWithMinHeight} /> | ||
</label> | ||
<label style={{ cursor: "pointer", display: "flex" }}> | ||
<Text mr={"8px"}>Custom header</Text> | ||
<Checkbox value={showCustomHeader} onChange={setShowCustomHeader} /> | ||
</label> | ||
<label style={{ cursor: "pointer", display: "flex" }}> | ||
<Text mr={"8px"}>Loading</Text> | ||
<Checkbox value={loading} onChange={setLoading} /> | ||
</label> | ||
<label style={{ cursor: "pointer", display: "flex" }}> | ||
<Tooltip content={"Empty List card can be customized"}> | ||
<Text mr={"8px"}>Empty list</Text> | ||
</Tooltip> | ||
<Checkbox value={empty} onChange={setEmpty} /> | ||
</label> | ||
</Grid> | ||
<TableComponent<ItemType> | ||
headers={[ | ||
{ key: "name", title: "Name", sortable: true }, | ||
{ | ||
key: "sortField", | ||
title: "Special Field", | ||
tooltip: "Values are sorted based on convertibility to number with custom sorting method", | ||
sortFunc: (a, b, reverseOrder) => | ||
(Number(isNaN(Number(a.sortField))) - Number(isNaN(Number(b.sortField)))) * (reverseOrder ? -1 : 1), | ||
}, | ||
{ | ||
key: "someField", | ||
title: "Some Field", | ||
}, | ||
|
||
{ | ||
key: "price", | ||
title: "Price $", | ||
width: "100px", | ||
sortable: true, | ||
tooltip: "Custom width === 100px", | ||
renderFunc: ({ price }) => `$${price}`, | ||
}, | ||
{ | ||
width: "77px", | ||
renderFunc: (item) => ( | ||
<Button | ||
onClick={() => { | ||
console.log("Click table item", item); | ||
}} | ||
scale={"small"} | ||
variant={"outlined"} | ||
> | ||
Click | ||
</Button> | ||
), | ||
}, //actions | ||
]} | ||
items={!empty && !loading ? items : []} | ||
page={page} | ||
perPage={perPage} | ||
changePage={setPage} | ||
header={ | ||
showCustomHeader ? ( | ||
<Text pb={"24px"} variant="h5"> | ||
Custom header | ||
</Text> | ||
) : undefined | ||
} | ||
minHeight={withMinHeight ? "347px" : undefined} | ||
loading={loading} | ||
/> | ||
</Box> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
import { | ||
TableContent, | ||
TableContentWrap, | ||
TableHeader, | ||
TableHeaderTitleBtn, | ||
TableItem, | ||
TablePagBtn, | ||
TablePagWrap, | ||
TableWrap, | ||
} from "./styles"; | ||
import { ITableProps, SortData } from "./types"; | ||
import Text from "../Text"; | ||
import { ArrowLeftIcon, QuestionIcon } from "../Svg"; | ||
import Tooltip from "../Tooltip"; | ||
import { Box, Flex } from "../Box"; | ||
import React, { useEffect, useMemo, useState } from "react"; | ||
import { useTheme } from "styled-components"; | ||
import Svg from "../Svg/Svg"; | ||
import { defaultSortCallback } from "./utils"; | ||
import { LoadingSpinner } from "../Loaders"; | ||
|
||
function SortBtn({ data, active }: { data: SortData<any> | undefined; active: boolean }) { | ||
const theme = useTheme(); | ||
const directColor = active && data && !data.reverseOrder ? theme.colors.primaryDefault : theme.colors.darkGray; | ||
const reverseColor = active && data?.reverseOrder ? theme.colors.primaryDefault : theme.colors.darkGray; | ||
return ( | ||
<Svg viewBox="0 0 12 12" size={"12px"} mr={"4px"} fill="none"> | ||
<path d="M5.25 9L3.75 10.5L2.25 9" stroke={directColor} strokeLinecap="round" strokeLinejoin="round" /> | ||
<path d="M6.75 3L8.25 1.5L9.75 3" stroke={reverseColor} strokeLinecap="round" strokeLinejoin="round" /> | ||
<path d="M8.25 10.5V1.5" stroke={reverseColor} strokeLinecap="round" strokeLinejoin="round" /> | ||
<path d="M3.75 1.5V10.5" stroke={directColor} strokeLinecap="round" strokeLinejoin="round" /> | ||
</Svg> | ||
); | ||
} | ||
|
||
export default function Table<T = any>({ | ||
headers, | ||
items, | ||
page, | ||
perPage = 10, | ||
minHeight, | ||
header, | ||
changePage = (page: number) => {}, | ||
loading, | ||
emptyCard, | ||
}: ITableProps<T>) { | ||
const maxPage = useMemo(() => Math.ceil(items.length / perPage), [items, perPage]); | ||
|
||
const theme = useTheme(); | ||
|
||
const [sortData, setSortData] = useState<SortData<T>>(); | ||
|
||
const sortedItems = useMemo(() => { | ||
const newItems = [...items]; | ||
if (sortData?.sortBy) { | ||
const sortedHeader = headers.find((header) => header.key === sortData.sortBy); | ||
if (sortedHeader?.sortFunc) { | ||
newItems.sort((a, b) => sortedHeader.sortFunc(a, b, sortData.reverseOrder)); | ||
} else { | ||
newItems.sort((itemA, itemB) => defaultSortCallback(itemA, itemB, sortData)); | ||
} | ||
} | ||
return newItems; | ||
}, [items, sortData]); | ||
|
||
const paginatedItems = useMemo(() => { | ||
const start = (page - 1) * perPage; | ||
const end = start + perPage; | ||
return sortedItems.slice(start, end); | ||
}, [sortedItems, perPage]); | ||
|
||
useEffect(() => { | ||
changePage(1); | ||
}, [sortData, perPage]); | ||
|
||
return ( | ||
<TableWrap minHeight={minHeight}> | ||
<Box width={"100%"}>{header}</Box> | ||
<TableContentWrap> | ||
{items.length && !loading ? ( | ||
<TableContent cols={headers}> | ||
{headers.map((header, i) => { | ||
const isSortable = !!(header.sortable || header.sortFunc); | ||
return ( | ||
<TableHeader key={i}> | ||
{header.key && header.title && ( | ||
<TableHeaderTitleBtn | ||
disabled={!isSortable} | ||
onClick={() => { | ||
const sortBy = header.key; | ||
if (sortBy) { | ||
if (sortData?.sortBy === sortBy && sortData.reverseOrder) setSortData(undefined); | ||
else { | ||
const reverseOrder = sortData?.sortBy !== sortBy ? false : !sortData?.reverseOrder; | ||
setSortData({ sortBy, reverseOrder }); | ||
} | ||
} | ||
}} | ||
active={sortData?.sortBy === header.key} | ||
> | ||
{isSortable && <SortBtn data={sortData} active={sortData?.sortBy === header.key} />} | ||
<span>{header.title}</span> | ||
</TableHeaderTitleBtn> | ||
)} | ||
{header.tooltip && ( | ||
<Box ml={"4px"}> | ||
<Tooltip content={header.tooltip}> | ||
<QuestionIcon size={"13px"} color="darkGray" /> | ||
</Tooltip> | ||
</Box> | ||
)} | ||
</TableHeader> | ||
); | ||
})} | ||
{paginatedItems.map((item) => | ||
headers.map((header, j) => { | ||
const customContent = header.renderFunc && header.renderFunc(item); | ||
const nativeContent = String(header.key ? item[header.key] : ""); | ||
const content = customContent || nativeContent; | ||
return <TableItem key={j}>{content}</TableItem>; | ||
}) | ||
)} | ||
</TableContent> | ||
) : loading ? ( | ||
<Flex alignItems={"center"} justifyContent={"center"} height={"100%"}> | ||
<LoadingSpinner /> | ||
</Flex> | ||
) : ( | ||
<Flex alignItems={"center"} justifyContent={"center"} height={"100%"}> | ||
{emptyCard || <Text color="darkGray">No data</Text>} | ||
</Flex> | ||
)} | ||
</TableContentWrap> | ||
{maxPage > 1 && ( | ||
<TablePagWrap> | ||
<TablePagBtn | ||
disabled={!(page > 1)} | ||
onClick={() => { | ||
changePage(page - 1); | ||
}} | ||
> | ||
<ArrowLeftIcon size={"16px"} color="primaryDefault" /> | ||
</TablePagBtn> | ||
<Flex> | ||
<Box mr={"0.4em"}>Page</Box> | ||
<Box color={theme.colors.primaryDefault} minWidth={"10px"}> | ||
{page} | ||
</Box> | ||
<Box mx={"0.4em"}>of</Box> | ||
<Box>{maxPage}</Box> | ||
</Flex> | ||
<TablePagBtn | ||
disabled={!(page < maxPage)} | ||
onClick={() => { | ||
changePage(page + 1); | ||
}} | ||
> | ||
<ArrowLeftIcon size={"16px"} style={{ transform: "scaleX(-1)" }} color="primaryDefault" /> | ||
</TablePagBtn> | ||
</TablePagWrap> | ||
)} | ||
</TableWrap> | ||
); | ||
} |
Oops, something went wrong.