mirror of
https://github.com/checktheroads/hyperglass
synced 2024-05-11 05:55:08 +00:00
fix case sensitive git config
This commit is contained in:
24
hyperglass/ui/components/card/body.tsx
Normal file
24
hyperglass/ui/components/card/body.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import { useColorValue } from '~/context';
|
||||
|
||||
import type { ICardBody } from './types';
|
||||
|
||||
export const CardBody = (props: ICardBody) => {
|
||||
const { onClick, ...rest } = props;
|
||||
const bg = useColorValue('white', 'dark.500');
|
||||
const color = useColorValue('dark.500', 'white');
|
||||
return (
|
||||
<Flex
|
||||
bg={bg}
|
||||
w="100%"
|
||||
maxW="95%"
|
||||
rounded="md"
|
||||
color={color}
|
||||
onClick={onClick}
|
||||
overflow="hidden"
|
||||
borderWidth="1px"
|
||||
direction="column"
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
};
|
18
hyperglass/ui/components/card/footer.tsx
Normal file
18
hyperglass/ui/components/card/footer.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
|
||||
import type { ICardFooter } from './types';
|
||||
|
||||
export const CardFooter = (props: ICardFooter) => (
|
||||
<Flex
|
||||
p={4}
|
||||
direction="column"
|
||||
overflowX="hidden"
|
||||
overflowY="hidden"
|
||||
flexDirection="row"
|
||||
borderTopWidth="1px"
|
||||
roundedBottomLeft={4}
|
||||
roundedBottomRight={4}
|
||||
justifyContent="space-between"
|
||||
{...props}
|
||||
/>
|
||||
);
|
21
hyperglass/ui/components/card/header.tsx
Normal file
21
hyperglass/ui/components/card/header.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import { Flex, Text } from '@chakra-ui/react';
|
||||
import { useColorValue } from '~/context';
|
||||
|
||||
import type { ICardHeader } from './types';
|
||||
|
||||
export const CardHeader = (props: ICardHeader) => {
|
||||
const { children, ...rest } = props;
|
||||
const bg = useColorValue('blackAlpha.50', 'whiteAlpha.100');
|
||||
return (
|
||||
<Flex
|
||||
p={4}
|
||||
bg={bg}
|
||||
direction="column"
|
||||
roundedTopLeft={4}
|
||||
roundedTopRight={4}
|
||||
borderBottomWidth="1px"
|
||||
{...rest}>
|
||||
<Text fontWeight="bold">{children}</Text>
|
||||
</Flex>
|
||||
);
|
||||
};
|
3
hyperglass/ui/components/card/index.ts
Normal file
3
hyperglass/ui/components/card/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './body';
|
||||
export * from './footer';
|
||||
export * from './header';
|
9
hyperglass/ui/components/card/types.ts
Normal file
9
hyperglass/ui/components/card/types.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import type { FlexProps } from '@chakra-ui/react';
|
||||
|
||||
export interface ICardBody extends Omit<FlexProps, 'onClick'> {
|
||||
onClick?: () => boolean;
|
||||
}
|
||||
|
||||
export interface ICardFooter extends FlexProps {}
|
||||
|
||||
export interface ICardHeader extends FlexProps {}
|
37
hyperglass/ui/components/footer/button.tsx
Normal file
37
hyperglass/ui/components/footer/button.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import { Button, Menu, MenuButton, MenuList } from '@chakra-ui/react';
|
||||
import { Markdown } from '~/components';
|
||||
import { useColorValue, useBreakpointValue } from '~/context';
|
||||
import { useOpposingColor } from '~/hooks';
|
||||
|
||||
import type { TFooterButton } from './types';
|
||||
|
||||
export const FooterButton = (props: TFooterButton) => {
|
||||
const { content, title, side, ...rest } = props;
|
||||
const placement = side === 'left' ? 'top' : side === 'right' ? 'top-start' : undefined;
|
||||
const bg = useColorValue('white', 'gray.900');
|
||||
const color = useOpposingColor(bg);
|
||||
const size = useBreakpointValue({ base: 'xs', lg: 'sm' });
|
||||
return (
|
||||
<Menu placement={placement}>
|
||||
<MenuButton
|
||||
as={Button}
|
||||
size={size}
|
||||
variant="ghost"
|
||||
aria-label={typeof title === 'string' ? title : undefined}>
|
||||
{title}
|
||||
</MenuButton>
|
||||
<MenuList
|
||||
bg={bg}
|
||||
boxShadow="2xl"
|
||||
color={color}
|
||||
px={6}
|
||||
py={4}
|
||||
textAlign="left"
|
||||
mx={{ base: 1, lg: 2 }}
|
||||
maxW={{ base: '100vw', lg: '50vw' }}
|
||||
{...rest}>
|
||||
<Markdown content={content} />
|
||||
</MenuList>
|
||||
</Menu>
|
||||
);
|
||||
};
|
43
hyperglass/ui/components/footer/colorMode.tsx
Normal file
43
hyperglass/ui/components/footer/colorMode.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import { forwardRef } from 'react';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { Button, Icon, Tooltip } from '@chakra-ui/react';
|
||||
import { If } from '~/components';
|
||||
import { useColorMode, useColorValue, useBreakpointValue } from '~/context';
|
||||
import { useOpposingColor } from '~/hooks';
|
||||
|
||||
import type { TColorModeToggle } from './types';
|
||||
|
||||
const Sun = dynamic<MeronexIcon>(() => import('@meronex/icons/hi').then(i => i.HiSun));
|
||||
const Moon = dynamic<MeronexIcon>(() => import('@meronex/icons/hi').then(i => i.HiMoon));
|
||||
|
||||
export const ColorModeToggle = forwardRef<HTMLButtonElement, TColorModeToggle>((props, ref) => {
|
||||
const { size = '1.5rem', ...rest } = props;
|
||||
const { colorMode, toggleColorMode } = useColorMode();
|
||||
|
||||
const bg = useColorValue('primary.500', 'yellow.300');
|
||||
const color = useOpposingColor(bg);
|
||||
const label = useColorValue('Switch to dark mode', 'Switch to light mode');
|
||||
const btnSize = useBreakpointValue({ base: 'xs', lg: 'sm' });
|
||||
|
||||
return (
|
||||
<Tooltip hasArrow placement="top-end" label={label} bg={bg} color={color}>
|
||||
<Button
|
||||
ref={ref}
|
||||
size={btnSize}
|
||||
title={label}
|
||||
variant="ghost"
|
||||
aria-label={label}
|
||||
_hover={{ color: bg }}
|
||||
color="currentColor"
|
||||
onClick={toggleColorMode}
|
||||
{...rest}>
|
||||
<If c={colorMode === 'light'}>
|
||||
<Icon as={Moon} boxSize={size} />
|
||||
</If>
|
||||
<If c={colorMode === 'dark'}>
|
||||
<Icon as={Sun} boxSize={size} />
|
||||
</If>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
);
|
||||
});
|
65
hyperglass/ui/components/footer/footer.tsx
Normal file
65
hyperglass/ui/components/footer/footer.tsx
Normal file
@@ -0,0 +1,65 @@
|
||||
import dynamic from 'next/dynamic';
|
||||
import { Button, Flex, Link, Icon, HStack, useToken } from '@chakra-ui/react';
|
||||
import { If } from '~/components';
|
||||
import { useConfig, useMobile, useColorValue, useBreakpointValue } from '~/context';
|
||||
import { useStrf } from '~/hooks';
|
||||
import { FooterButton } from './button';
|
||||
import { ColorModeToggle } from './colorMode';
|
||||
|
||||
const CodeIcon = dynamic<MeronexIcon>(() => import('@meronex/icons/fi').then(i => i.FiCode));
|
||||
const ExtIcon = dynamic<MeronexIcon>(() => import('@meronex/icons/go').then(i => i.GoLinkExternal));
|
||||
|
||||
export const Footer = () => {
|
||||
const { web, content, primary_asn } = useConfig();
|
||||
|
||||
const footerBg = useColorValue('blackAlpha.50', 'whiteAlpha.100');
|
||||
const footerColor = useColorValue('black', 'white');
|
||||
|
||||
const extUrl = useStrf(web.external_link.url, { primary_asn }) ?? '/';
|
||||
|
||||
const size = useBreakpointValue({ base: useToken('sizes', 4), lg: useToken('sizes', 6) });
|
||||
const btnSize = useBreakpointValue({ base: 'xs', lg: 'sm' });
|
||||
|
||||
const isMobile = useMobile();
|
||||
|
||||
return (
|
||||
<HStack
|
||||
px={6}
|
||||
py={4}
|
||||
w="100%"
|
||||
zIndex={1}
|
||||
as="footer"
|
||||
bg={footerBg}
|
||||
color={footerColor}
|
||||
spacing={{ base: 8, lg: 6 }}
|
||||
justifyContent={{ base: 'center', lg: 'space-between' }}>
|
||||
<If c={web.terms.enable}>
|
||||
<FooterButton side="left" content={content.terms} title={web.terms.title} />
|
||||
</If>
|
||||
<If c={web.help_menu.enable}>
|
||||
<FooterButton side="left" content={content.help_menu} title={web.help_menu.title} />
|
||||
</If>
|
||||
<If c={web.external_link.enable}>
|
||||
<Button
|
||||
as={Link}
|
||||
isExternal
|
||||
href={extUrl}
|
||||
size={btnSize}
|
||||
variant="ghost"
|
||||
rightIcon={<ExtIcon />}
|
||||
aria-label={web.external_link.title}>
|
||||
{web.external_link.title}
|
||||
</Button>
|
||||
</If>
|
||||
{!isMobile && <Flex p={0} flex="0 0 auto" maxWidth="100%" mr="auto" />}
|
||||
<If c={web.credit.enable}>
|
||||
<FooterButton
|
||||
side="right"
|
||||
content={content.credit}
|
||||
title={<Icon as={CodeIcon} boxSize={size} />}
|
||||
/>
|
||||
</If>
|
||||
<ColorModeToggle size={size} />
|
||||
</HStack>
|
||||
);
|
||||
};
|
1
hyperglass/ui/components/footer/index.ts
Normal file
1
hyperglass/ui/components/footer/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './footer';
|
15
hyperglass/ui/components/footer/types.ts
Normal file
15
hyperglass/ui/components/footer/types.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import type { ButtonProps, MenuListProps } from '@chakra-ui/react';
|
||||
|
||||
type TFooterSide = 'left' | 'right';
|
||||
|
||||
export interface TFooterButton extends Omit<MenuListProps, 'title'> {
|
||||
side: TFooterSide;
|
||||
title?: MenuListProps['children'];
|
||||
content: string;
|
||||
}
|
||||
|
||||
export type TFooterItems = 'help' | 'credit' | 'terms';
|
||||
|
||||
export interface TColorModeToggle extends ButtonProps {
|
||||
size?: string;
|
||||
}
|
87
hyperglass/ui/components/markdown/elements.tsx
Normal file
87
hyperglass/ui/components/markdown/elements.tsx
Normal file
@@ -0,0 +1,87 @@
|
||||
import {
|
||||
OrderedList,
|
||||
UnorderedList,
|
||||
Code as ChakraCode,
|
||||
Link as ChakraLink,
|
||||
Text as ChakraText,
|
||||
Divider as ChakraDivider,
|
||||
Heading as ChakraHeading,
|
||||
Checkbox as ChakraCheckbox,
|
||||
ListItem as ChakraListItem,
|
||||
} from '@chakra-ui/react';
|
||||
|
||||
import { TD, TH, Table as ChakraTable } from './table';
|
||||
|
||||
import { CodeBlock as CustomCodeBlock, If } from '~/components';
|
||||
|
||||
import type {
|
||||
BoxProps,
|
||||
TextProps,
|
||||
CodeProps,
|
||||
LinkProps,
|
||||
HeadingProps,
|
||||
DividerProps,
|
||||
} from '@chakra-ui/react';
|
||||
import type { TCheckbox, TList, THeading, TCodeBlock, TTableData, TListItem } from './types';
|
||||
|
||||
export const Checkbox = (props: TCheckbox) => {
|
||||
const { checked, ...rest } = props;
|
||||
return <ChakraCheckbox isChecked={checked} {...rest} />;
|
||||
};
|
||||
|
||||
export const List = (props: TList) => {
|
||||
const { ordered, ...rest } = props;
|
||||
return (
|
||||
<>
|
||||
<If c={ordered}>
|
||||
<OrderedList {...rest} />
|
||||
</If>
|
||||
<If c={!ordered}>
|
||||
<UnorderedList {...rest} />
|
||||
</If>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const ListItem = (props: TListItem) => {
|
||||
const { checked, ...rest } = props;
|
||||
return checked ? <Checkbox checked={checked} {...rest} /> : <ChakraListItem {...rest} />;
|
||||
};
|
||||
|
||||
export const Heading = (props: THeading) => {
|
||||
const { level, ...rest } = props;
|
||||
|
||||
const levelMap = {
|
||||
1: { as: 'h1', size: 'lg', fontWeight: 'bold' },
|
||||
2: { as: 'h2', size: 'lg', fontWeight: 'normal' },
|
||||
3: { as: 'h3', size: 'lg', fontWeight: 'bold' },
|
||||
4: { as: 'h4', size: 'md', fontWeight: 'normal' },
|
||||
5: { as: 'h5', size: 'md', fontWeight: 'bold' },
|
||||
6: { as: 'h6', size: 'sm', fontWeight: 'bold' },
|
||||
} as { [i: number]: HeadingProps };
|
||||
|
||||
return <ChakraHeading {...levelMap[level]} {...rest} />;
|
||||
};
|
||||
|
||||
export const Link = (props: LinkProps) => <ChakraLink isExternal {...props} />;
|
||||
|
||||
export const CodeBlock = (props: TCodeBlock) => <CustomCodeBlock>{props.value}</CustomCodeBlock>;
|
||||
|
||||
export const TableData = (props: TTableData) => {
|
||||
const { isHeader, ...rest } = props;
|
||||
return (
|
||||
<>
|
||||
<If c={isHeader}>
|
||||
<TH {...rest} />
|
||||
</If>
|
||||
<If c={!isHeader}>
|
||||
<TD {...rest} />
|
||||
</If>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const Paragraph = (props: TextProps) => <ChakraText {...props} />;
|
||||
export const InlineCode = (props: CodeProps) => <ChakraCode children={props.children} />;
|
||||
export const Divider = (props: DividerProps) => <ChakraDivider {...props} />;
|
||||
export const Table = (props: BoxProps) => <ChakraTable {...props} />;
|
1
hyperglass/ui/components/markdown/index.ts
Normal file
1
hyperglass/ui/components/markdown/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './markdown';
|
33
hyperglass/ui/components/markdown/markdown.tsx
Normal file
33
hyperglass/ui/components/markdown/markdown.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import {
|
||||
List,
|
||||
ListItem,
|
||||
Heading,
|
||||
Link,
|
||||
CodeBlock,
|
||||
TableData,
|
||||
Paragraph,
|
||||
InlineCode,
|
||||
Divider,
|
||||
Table,
|
||||
} from './elements';
|
||||
|
||||
import type { ReactMarkdownProps } from 'react-markdown';
|
||||
import type { TMarkdown } from './types';
|
||||
|
||||
const renderers = {
|
||||
paragraph: Paragraph,
|
||||
link: Link,
|
||||
heading: Heading,
|
||||
inlineCode: InlineCode,
|
||||
list: List,
|
||||
listItem: ListItem,
|
||||
thematicBreak: Divider,
|
||||
code: CodeBlock,
|
||||
table: Table,
|
||||
tableCell: TableData,
|
||||
} as ReactMarkdownProps['renderers'];
|
||||
|
||||
export const Markdown = (props: TMarkdown) => (
|
||||
<ReactMarkdown renderers={renderers} source={props.content} />
|
||||
);
|
27
hyperglass/ui/components/markdown/table.tsx
Normal file
27
hyperglass/ui/components/markdown/table.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { useColorValue } from '~/context';
|
||||
|
||||
import type { BoxProps } from '@chakra-ui/react';
|
||||
|
||||
export const Table = (props: BoxProps) => (
|
||||
<Box as="table" textAlign="left" mt={4} width="full" {...props} />
|
||||
);
|
||||
|
||||
export const TH = (props: BoxProps) => {
|
||||
const bg = useColorValue('blackAlpha.50', 'whiteAlpha.50');
|
||||
return <Box as="th" bg={bg} fontWeight="semibold" p={2} fontSize="sm" {...props} />;
|
||||
};
|
||||
|
||||
export const TD = (props: BoxProps) => {
|
||||
return (
|
||||
<Box
|
||||
p={2}
|
||||
as="td"
|
||||
fontSize="sm"
|
||||
whiteSpace="normal"
|
||||
borderTopWidth="1px"
|
||||
borderColor="inherit"
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
37
hyperglass/ui/components/markdown/types.ts
Normal file
37
hyperglass/ui/components/markdown/types.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import type {
|
||||
BoxProps,
|
||||
CheckboxProps,
|
||||
HeadingProps,
|
||||
ListProps,
|
||||
ListItemProps,
|
||||
} from '@chakra-ui/react';
|
||||
|
||||
export interface TMarkdown {
|
||||
content: string;
|
||||
}
|
||||
|
||||
export interface TCheckbox extends CheckboxProps {
|
||||
checked: boolean;
|
||||
}
|
||||
|
||||
export interface TListItem {
|
||||
checked: boolean;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
export interface TList extends ListProps {
|
||||
ordered: boolean;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
export interface THeading extends HeadingProps {
|
||||
level: 1 | 2 | 3 | 4 | 5 | 6;
|
||||
}
|
||||
|
||||
export interface TCodeBlock {
|
||||
value: React.ReactNode;
|
||||
}
|
||||
|
||||
export interface TTableData extends BoxProps {
|
||||
isHeader: boolean;
|
||||
}
|
16
hyperglass/ui/components/table/body.tsx
Normal file
16
hyperglass/ui/components/table/body.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Box } from '@chakra-ui/react';
|
||||
|
||||
import type { BoxProps } from '@chakra-ui/react';
|
||||
|
||||
export const TableBody = (props: BoxProps) => (
|
||||
<Box
|
||||
as="tbody"
|
||||
overflowY="scroll"
|
||||
css={{
|
||||
'&::-webkit-scrollbar': { display: 'none' },
|
||||
'&': { msOverflowStyle: 'none' },
|
||||
}}
|
||||
overflowX="hidden"
|
||||
{...props}
|
||||
/>
|
||||
);
|
7
hyperglass/ui/components/table/button.tsx
Normal file
7
hyperglass/ui/components/table/button.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
import { IconButton } from '@chakra-ui/react';
|
||||
|
||||
import type { TTableIconButton } from './types';
|
||||
|
||||
export const TableIconButton = (props: TTableIconButton) => (
|
||||
<IconButton size="sm" borderWidth={1} {...props} aria-label="Table Icon Button" />
|
||||
);
|
28
hyperglass/ui/components/table/cell.tsx
Normal file
28
hyperglass/ui/components/table/cell.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { useColorValue } from '~/context';
|
||||
|
||||
import type { TTableCell } from './types';
|
||||
|
||||
export const TableCell = (props: TTableCell) => {
|
||||
const { bordersVertical = [false, 0], align, ...rest } = props;
|
||||
const [doVerticalBorders, index] = bordersVertical;
|
||||
const borderLeftColor = useColorValue('blackAlpha.100', 'whiteAlpha.100');
|
||||
|
||||
let borderProps = {};
|
||||
if (doVerticalBorders && index !== 0) {
|
||||
borderProps = { borderLeft: '1px solid', borderLeftColor };
|
||||
}
|
||||
|
||||
return (
|
||||
<Box
|
||||
p={4}
|
||||
m={0}
|
||||
w="1%"
|
||||
as="td"
|
||||
textAlign={align}
|
||||
whiteSpace="nowrap"
|
||||
{...borderProps}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
};
|
9
hyperglass/ui/components/table/head.tsx
Normal file
9
hyperglass/ui/components/table/head.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { useColorValue } from '~/context';
|
||||
|
||||
import type { BoxProps } from '@chakra-ui/react';
|
||||
|
||||
export const TableHead = (props: BoxProps) => {
|
||||
const bg = useColorValue('blackAlpha.100', 'whiteAlpha.100');
|
||||
return <Box as="thead" overflowX="hidden" overflowY="auto" bg={bg} {...props} />;
|
||||
};
|
8
hyperglass/ui/components/table/index.ts
Normal file
8
hyperglass/ui/components/table/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export * from './body';
|
||||
export * from './button';
|
||||
export * from './cell';
|
||||
export * from './head';
|
||||
export * from './main';
|
||||
export * from './main';
|
||||
export * from './pageSelect';
|
||||
export * from './row';
|
202
hyperglass/ui/components/table/main.tsx
Normal file
202
hyperglass/ui/components/table/main.tsx
Normal file
@@ -0,0 +1,202 @@
|
||||
import dynamic from 'next/dynamic';
|
||||
import { Flex, Icon, Text } from '@chakra-ui/react';
|
||||
import { usePagination, useSortBy, useTable } from 'react-table';
|
||||
import { useMobile } from '~/context';
|
||||
import { CardBody, CardFooter, CardHeader, If } from '~/components';
|
||||
import { TableMain } from './table';
|
||||
import { TableCell } from './cell';
|
||||
import { TableHead } from './head';
|
||||
import { TableRow } from './row';
|
||||
import { TableBody } from './body';
|
||||
import { TableIconButton } from './button';
|
||||
import { TableSelectShow } from './pageSelect';
|
||||
|
||||
import type { TableOptions, PluginHook } from 'react-table';
|
||||
import type { TCellRender } from '~/types';
|
||||
import type { TTable } from './types';
|
||||
|
||||
const ChevronRight = dynamic<MeronexIcon>(() =>
|
||||
import('@meronex/icons/fa').then(i => i.FaChevronRight),
|
||||
);
|
||||
|
||||
const ChevronLeft = dynamic<MeronexIcon>(() =>
|
||||
import('@meronex/icons/fa').then(i => i.FaChevronLeft),
|
||||
);
|
||||
|
||||
const ChevronDown = dynamic<MeronexIcon>(() =>
|
||||
import('@meronex/icons/fa').then(i => i.FaChevronDown),
|
||||
);
|
||||
|
||||
const DoubleChevronRight = dynamic<MeronexIcon>(() =>
|
||||
import('@meronex/icons/fi').then(i => i.FiChevronsRight),
|
||||
);
|
||||
const DoubleChevronLeft = dynamic<MeronexIcon>(() =>
|
||||
import('@meronex/icons/fi').then(i => i.FiChevronsLeft),
|
||||
);
|
||||
|
||||
export function Table(props: TTable) {
|
||||
const {
|
||||
data,
|
||||
columns,
|
||||
heading,
|
||||
Cell,
|
||||
rowHighlightBg,
|
||||
striped = false,
|
||||
rowHighlightProp,
|
||||
bordersVertical = false,
|
||||
bordersHorizontal = false,
|
||||
} = props;
|
||||
|
||||
const isMobile = useMobile();
|
||||
|
||||
const defaultColumn = {
|
||||
minWidth: 100,
|
||||
width: 150,
|
||||
maxWidth: 300,
|
||||
};
|
||||
|
||||
let hiddenColumns = [] as string[];
|
||||
|
||||
for (const col of columns) {
|
||||
if (col.hidden) {
|
||||
hiddenColumns.push(col.accessor);
|
||||
}
|
||||
}
|
||||
|
||||
const options = {
|
||||
columns,
|
||||
defaultColumn,
|
||||
data,
|
||||
initialState: { hiddenColumns },
|
||||
} as TableOptions<TRoute>;
|
||||
|
||||
const plugins = [useSortBy, usePagination] as PluginHook<TRoute>[];
|
||||
|
||||
const instance = useTable<TRoute>(options, ...plugins);
|
||||
|
||||
const {
|
||||
page,
|
||||
gotoPage,
|
||||
nextPage,
|
||||
pageCount,
|
||||
prepareRow,
|
||||
canNextPage,
|
||||
pageOptions,
|
||||
setPageSize,
|
||||
headerGroups,
|
||||
previousPage,
|
||||
getTableProps,
|
||||
canPreviousPage,
|
||||
state: { pageIndex, pageSize },
|
||||
} = instance;
|
||||
|
||||
return (
|
||||
<CardBody>
|
||||
{heading && <CardHeader>{heading}</CardHeader>}
|
||||
<TableMain {...getTableProps()}>
|
||||
<TableHead>
|
||||
{headerGroups.map((headerGroup, i) => (
|
||||
<TableRow index={i} {...headerGroup.getHeaderGroupProps()}>
|
||||
{headerGroup.headers.map(column => (
|
||||
<TableCell
|
||||
as="th"
|
||||
align={column.align}
|
||||
{...column.getHeaderProps()}
|
||||
{...column.getSortByToggleProps()}>
|
||||
<Text fontSize="sm" fontWeight="bold" display="inline-block">
|
||||
{column.render('Header')}
|
||||
</Text>
|
||||
<If c={column.isSorted}>
|
||||
<If c={typeof column.isSortedDesc !== 'undefined'}>
|
||||
<Icon as={ChevronDown} boxSize={4} ml={1} />
|
||||
</If>
|
||||
<If c={!column.isSortedDesc}>
|
||||
<Icon as={ChevronRight} boxSize={4} ml={1} />
|
||||
</If>
|
||||
</If>
|
||||
<If c={!column.isSorted}>{''}</If>
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{page.map((row, key) => {
|
||||
prepareRow(row);
|
||||
return (
|
||||
<TableRow
|
||||
index={key}
|
||||
doStripe={striped}
|
||||
highlightBg={rowHighlightBg}
|
||||
doHorizontalBorders={bordersHorizontal}
|
||||
highlight={row.values[rowHighlightProp ?? ''] ?? false}
|
||||
{...row.getRowProps()}>
|
||||
{row.cells.map((cell, i) => {
|
||||
const { column, row, value } = cell as TCellRender;
|
||||
return (
|
||||
<TableCell
|
||||
align={cell.column.align}
|
||||
bordersVertical={[bordersVertical, i]}
|
||||
{...cell.getCellProps()}>
|
||||
{typeof Cell !== 'undefined' ? (
|
||||
<Cell column={column} row={row} value={value} />
|
||||
) : (
|
||||
cell.render('Cell')
|
||||
)}
|
||||
</TableCell>
|
||||
);
|
||||
})}
|
||||
</TableRow>
|
||||
);
|
||||
})}
|
||||
</TableBody>
|
||||
</TableMain>
|
||||
<CardFooter>
|
||||
<Flex direction="row">
|
||||
<TableIconButton
|
||||
mr={2}
|
||||
onClick={() => gotoPage(0)}
|
||||
isDisabled={!canPreviousPage}
|
||||
icon={<Icon as={DoubleChevronLeft} boxSize={4} />}
|
||||
/>
|
||||
<TableIconButton
|
||||
mr={2}
|
||||
onClick={() => previousPage()}
|
||||
isDisabled={!canPreviousPage}
|
||||
icon={<Icon as={ChevronLeft} boxSize={3} />}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex justifyContent="center" alignItems="center">
|
||||
<Text fontSize="sm" mr={4} whiteSpace="nowrap">
|
||||
Page{' '}
|
||||
<strong>
|
||||
{pageIndex + 1} of {pageOptions.length}
|
||||
</strong>{' '}
|
||||
</Text>
|
||||
{!isMobile && (
|
||||
<TableSelectShow
|
||||
value={pageSize}
|
||||
onChange={e => {
|
||||
setPageSize(Number(e.target.value));
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
<Flex direction="row">
|
||||
<TableIconButton
|
||||
ml={2}
|
||||
onClick={nextPage}
|
||||
isDisabled={!canNextPage}
|
||||
icon={<Icon as={ChevronRight} boxSize={3} />}
|
||||
/>
|
||||
<TableIconButton
|
||||
ml={2}
|
||||
isDisabled={!canNextPage}
|
||||
icon={<Icon as={DoubleChevronRight} boxSize={4} />}
|
||||
onClick={() => gotoPage(pageCount ? pageCount - 1 : 1)}
|
||||
/>
|
||||
</Flex>
|
||||
</CardFooter>
|
||||
</CardBody>
|
||||
);
|
||||
}
|
15
hyperglass/ui/components/table/pageSelect.tsx
Normal file
15
hyperglass/ui/components/table/pageSelect.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { Select } from '@chakra-ui/react';
|
||||
import { SelectProps } from '@chakra-ui/react';
|
||||
|
||||
export const TableSelectShow = (props: SelectProps) => {
|
||||
const { value, ...rest } = props;
|
||||
return (
|
||||
<Select size="sm" {...rest}>
|
||||
{[5, 10, 20, 30, 40, 50].map(value => (
|
||||
<option key={value} value={value}>
|
||||
Show {value}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
);
|
||||
};
|
51
hyperglass/ui/components/table/row.tsx
Normal file
51
hyperglass/ui/components/table/row.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { useColorValue } from '~/context';
|
||||
import { useOpposingColor } from '~/hooks';
|
||||
|
||||
import type { TTableRow } from './types';
|
||||
|
||||
export const TableRow = (props: TTableRow) => {
|
||||
const {
|
||||
index = 0,
|
||||
doStripe = false,
|
||||
highlight = false,
|
||||
highlightBg = 'primary',
|
||||
doHorizontalBorders = false,
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
const alpha = useColorValue('100', '200');
|
||||
const alphaHover = useColorValue('200', '100');
|
||||
const bgStripe = useColorValue('blackAlpha.50', 'whiteAlpha.50');
|
||||
let hoverBg = useColorValue('blackAlpha.50', 'whiteAlpha.50');
|
||||
const rowBorder = useColorValue(
|
||||
{ borderTop: '1px', borderTopColor: 'blackAlpha.100' },
|
||||
{ borderTop: '1px', borderTopColor: 'whiteAlpha.100' },
|
||||
);
|
||||
let bg;
|
||||
|
||||
if (highlight) {
|
||||
bg = `${String(highlightBg)}.${alpha}`;
|
||||
hoverBg = `${String(highlightBg)}.${alphaHover}`;
|
||||
} else if (doStripe && index % 2 !== 0) {
|
||||
bg = bgStripe;
|
||||
}
|
||||
const defaultBg = useColorValue('white', 'black');
|
||||
const color = useOpposingColor(bg ?? defaultBg);
|
||||
const borderProps = doHorizontalBorders && index !== 0 ? rowBorder : {};
|
||||
|
||||
return (
|
||||
<Box
|
||||
as="tr"
|
||||
bg={bg}
|
||||
css={{ '& > td': { color } }}
|
||||
fontWeight={highlight ? 'bold' : undefined}
|
||||
_hover={{
|
||||
cursor: 'pointer',
|
||||
backgroundColor: highlight ? `${String(highlightBg)}.${alphaHover}` : hoverBg,
|
||||
}}
|
||||
{...borderProps}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
};
|
34
hyperglass/ui/components/table/table.tsx
Normal file
34
hyperglass/ui/components/table/table.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { useColorValue } from '~/context';
|
||||
|
||||
import type { BoxProps } from '@chakra-ui/react';
|
||||
|
||||
export const TableMain = (props: BoxProps) => {
|
||||
const scrollbar = useColorValue('blackAlpha.300', 'whiteAlpha.300');
|
||||
const scrollbarHover = useColorValue('blackAlpha.400', 'whiteAlpha.400');
|
||||
const scrollbarBg = useColorValue('blackAlpha.50', 'whiteAlpha.50');
|
||||
return (
|
||||
<Box
|
||||
as="table"
|
||||
display="block"
|
||||
overflowX="auto"
|
||||
borderRadius="md"
|
||||
boxSizing="border-box"
|
||||
css={{
|
||||
'&::-webkit-scrollbar': { height: '5px' },
|
||||
'&::-webkit-scrollbar-track': {
|
||||
backgroundColor: scrollbarBg,
|
||||
},
|
||||
'&::-webkit-scrollbar-thumb': {
|
||||
backgroundColor: scrollbar,
|
||||
},
|
||||
'&::-webkit-scrollbar-thumb:hover': {
|
||||
backgroundColor: scrollbarHover,
|
||||
},
|
||||
|
||||
'-ms-overflow-style': { display: 'none' },
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
30
hyperglass/ui/components/table/types.ts
Normal file
30
hyperglass/ui/components/table/types.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import type { BoxProps, IconButtonProps } from '@chakra-ui/react';
|
||||
|
||||
import type { Theme, TColumn, TCellRender } from '~/types';
|
||||
|
||||
export interface TTable {
|
||||
data: TRoute[];
|
||||
striped?: boolean;
|
||||
columns: TColumn[];
|
||||
heading?: React.ReactNode;
|
||||
bordersVertical?: boolean;
|
||||
bordersHorizontal?: boolean;
|
||||
Cell?: React.FC<TCellRender>;
|
||||
rowHighlightProp?: keyof IRoute;
|
||||
rowHighlightBg?: Theme.ColorNames;
|
||||
}
|
||||
|
||||
export interface TTableCell extends Omit<BoxProps, 'align'> {
|
||||
bordersVertical?: [boolean, number];
|
||||
align?: 'left' | 'right' | 'center';
|
||||
}
|
||||
|
||||
export interface TTableRow extends BoxProps {
|
||||
highlightBg?: Theme.ColorNames;
|
||||
doHorizontalBorders?: boolean;
|
||||
highlight?: boolean;
|
||||
doStripe?: boolean;
|
||||
index: number;
|
||||
}
|
||||
|
||||
export type TTableIconButton = Omit<IconButtonProps, 'aria-label'>;
|
8
hyperglass/ui/components/util/animated.ts
Normal file
8
hyperglass/ui/components/util/animated.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { chakra } from '@chakra-ui/react';
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
export const AnimatedDiv = motion.custom(chakra.div);
|
||||
export const AnimatedForm = motion.custom(chakra.form);
|
||||
export const AnimatedH1 = motion.custom(chakra.h1);
|
||||
export const AnimatedH3 = motion.custom(chakra.h3);
|
||||
export const AnimatedButton = motion.custom(chakra.button);
|
6
hyperglass/ui/components/util/if.tsx
Normal file
6
hyperglass/ui/components/util/if.tsx
Normal file
@@ -0,0 +1,6 @@
|
||||
import type { TIf } from './types';
|
||||
|
||||
export const If = (props: TIf) => {
|
||||
const { c, render, children, ...rest } = props;
|
||||
return c ? (render ? render(rest) : children) : null;
|
||||
};
|
2
hyperglass/ui/components/util/index.ts
Normal file
2
hyperglass/ui/components/util/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './animated';
|
||||
export * from './if';
|
5
hyperglass/ui/components/util/types.ts
Normal file
5
hyperglass/ui/components/util/types.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export interface TIf {
|
||||
c: boolean;
|
||||
render?: (rest: any) => JSX.Element;
|
||||
[k: string]: any;
|
||||
}
|
Reference in New Issue
Block a user