1
0
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:
checktheroads
2021-01-01 11:32:35 -07:00
parent 6a5c73c4d4
commit d7f721af01
29 changed files with 842 additions and 0 deletions

View 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}
/>
);
};

View 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}
/>
);

View 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>
);
};

View File

@@ -0,0 +1,3 @@
export * from './body';
export * from './footer';
export * from './header';

View 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 {}

View 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>
);
};

View 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>
);
});

View 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>
);
};

View File

@@ -0,0 +1 @@
export * from './footer';

View 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;
}

View 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} />;

View File

@@ -0,0 +1 @@
export * from './markdown';

View 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} />
);

View 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}
/>
);
};

View 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;
}

View 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}
/>
);

View 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" />
);

View 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}
/>
);
};

View 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} />;
};

View 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';

View 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>
);
}

View 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>
);
};

View 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}
/>
);
};

View 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}
/>
);
};

View 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'>;

View 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);

View 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;
};

View File

@@ -0,0 +1,2 @@
export * from './animated';
export * from './if';

View File

@@ -0,0 +1,5 @@
export interface TIf {
c: boolean;
render?: (rest: any) => JSX.Element;
[k: string]: any;
}