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