add typings for eslint

This commit is contained in:
checktheroads 2021-01-03 23:51:09 -07:00
parent 14b62bd8e1
commit 6ffbf95abd
90 changed files with 1637 additions and 416 deletions

View file

@ -1,9 +1,58 @@
{ {
"extends": "@upstatement/eslint-config/react", "root": true,
"plugins": ["react-hooks"], "extends": [
"settings": { "import/resolver": { "typescript": {} } }, "eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react/recommended",
"plugin:prettier/recommended",
"prettier/@typescript-eslint"
],
"parser": "@typescript-eslint/parser",
"env": {
"browser": true,
"es6": true,
"node": true
},
"parserOptions": {
"ecmaVersion": 2020,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true,
"arrowFunctions": true
},
"project": "./tsconfig.json"
},
"plugins": ["react", "@typescript-eslint", "prettier"],
"settings": {
"react": {
"version": "detect"
},
"import/resolver": {
"node": {
"extensions": [".js", ".jsx", ".ts", ".tsx"],
"paths": ["./src"]
}
}
},
"rules": { "rules": {
"react-hooks/rules-of-hooks": "error", "@typescript-eslint/no-unused-vars": "off",
"react-hooks/exhaustive-deps": ["warn"] "@typescript-eslint/no-unused-vars-experimental": "error",
"no-unused-vars": "off",
"react/jsx-uses-react": "off",
"react/react-in-jsx-scope": "off",
"comma-dangle": ["error", "always-multiline"],
"global-require": "off",
"import/no-dynamic-require": "off",
"import/prefer-default-export": "off",
"@typescript-eslint/no-inferrable-types": "off",
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-var-requires": "off",
"@typescript-eslint/no-empty-interface": [
"error",
{
"allowSingleExtends": true
}
]
} }
} }

View file

@ -0,0 +1,8 @@
node_modules
dist
package.json
yarn.lock
package-lock.json
.eslintrc
tsconfig.json
.next/

11
hyperglass/ui/.prettierrc Normal file
View file

@ -0,0 +1,11 @@
{
"semi": true,
"printWidth": 100,
"tabWidth": 2,
"singleQuote": true,
"bracketSpacing": true,
"jsxBracketSameLine": false,
"useTabs": false,
"arrowParens": "avoid",
"trailingComma": "all"
}

View file

@ -1,12 +1,13 @@
import { Flex } from '@chakra-ui/react'; import { Flex } from '@chakra-ui/react';
import { useColorValue } from '~/context'; import { useColorValue } from '~/context';
import type { ICardBody } from './types'; import type { TCardBody } from './types';
export const CardBody = (props: ICardBody) => { export const CardBody: React.FC<TCardBody> = (props: TCardBody) => {
const { onClick, ...rest } = props; const { onClick, ...rest } = props;
const bg = useColorValue('white', 'dark.500'); const bg = useColorValue('white', 'dark.500');
const color = useColorValue('dark.500', 'white'); const color = useColorValue('dark.500', 'white');
console.log('some shit');
return ( return (
<Flex <Flex
bg={bg} bg={bg}

View file

@ -1,8 +1,8 @@
import { Flex } from '@chakra-ui/react'; import { Flex } from '@chakra-ui/react';
import type { ICardFooter } from './types'; import type { TCardFooter } from './types';
export const CardFooter = (props: ICardFooter) => ( export const CardFooter: React.FC<TCardFooter> = (props: TCardFooter) => (
<Flex <Flex
p={4} p={4}
direction="column" direction="column"

View file

@ -1,9 +1,9 @@
import { Flex, Text } from '@chakra-ui/react'; import { Flex, Text } from '@chakra-ui/react';
import { useColorValue } from '~/context'; import { useColorValue } from '~/context';
import type { ICardHeader } from './types'; import type { TCardHeader } from './types';
export const CardHeader = (props: ICardHeader) => { export const CardHeader: React.FC<TCardHeader> = (props: TCardHeader) => {
const { children, ...rest } = props; const { children, ...rest } = props;
const bg = useColorValue('blackAlpha.50', 'whiteAlpha.100'); const bg = useColorValue('blackAlpha.50', 'whiteAlpha.100');
return ( return (
@ -14,7 +14,8 @@ export const CardHeader = (props: ICardHeader) => {
roundedTopLeft={4} roundedTopLeft={4}
roundedTopRight={4} roundedTopRight={4}
borderBottomWidth="1px" borderBottomWidth="1px"
{...rest}> {...rest}
>
<Text fontWeight="bold">{children}</Text> <Text fontWeight="bold">{children}</Text>
</Flex> </Flex>
); );

View file

@ -1,9 +1,9 @@
import type { FlexProps } from '@chakra-ui/react'; import type { FlexProps } from '@chakra-ui/react';
export interface ICardBody extends Omit<FlexProps, 'onClick'> { export interface TCardBody extends Omit<FlexProps, 'onClick'> {
onClick?: () => boolean; onClick?: () => boolean;
} }
export interface ICardFooter extends FlexProps {} export interface TCardFooter extends FlexProps {}
export interface ICardHeader extends FlexProps {} export interface TCardHeader extends FlexProps {}

View file

@ -3,7 +3,7 @@ import { useColorValue } from '~/context';
import type { BoxProps } from '@chakra-ui/react'; import type { BoxProps } from '@chakra-ui/react';
export const CodeBlock = (props: BoxProps) => { export const CodeBlock: React.FC<BoxProps> = (props: BoxProps) => {
const bg = useColorValue('blackAlpha.100', 'gray.800'); const bg = useColorValue('blackAlpha.100', 'gray.800');
const color = useColorValue('black', 'white'); const color = useColorValue('black', 'white');
return ( return (

View file

@ -3,11 +3,11 @@ import ReactCountdown, { zeroPad } from 'react-countdown';
import { If } from '~/components'; import { If } from '~/components';
import { useColorValue } from '~/context'; import { useColorValue } from '~/context';
import type { ICountdown, IRenderer } from './types'; import type { TCountdown, TRenderer } from './types';
const Renderer = (props: IRenderer) => { const Renderer: React.FC<TRenderer> = (props: TRenderer) => {
const { hours, minutes, seconds, completed, text } = props; const { hours, minutes, seconds, completed, text } = props;
let time = [zeroPad(seconds)]; const time = [zeroPad(seconds)];
minutes !== 0 && time.unshift(zeroPad(minutes)); minutes !== 0 && time.unshift(zeroPad(minutes));
hours !== 0 && time.unshift(zeroPad(hours)); hours !== 0 && time.unshift(zeroPad(hours));
const bg = useColorValue('black', 'white'); const bg = useColorValue('black', 'white');
@ -28,7 +28,7 @@ const Renderer = (props: IRenderer) => {
); );
}; };
export const Countdown = (props: ICountdown) => { export const Countdown: React.FC<TCountdown> = (props: TCountdown) => {
const { timeout, text } = props; const { timeout, text } = props;
const then = timeout * 1000; const then = timeout * 1000;
return ( return (

View file

@ -1,10 +1,10 @@
import type { CountdownRenderProps } from 'react-countdown'; import type { CountdownRenderProps } from 'react-countdown';
export interface IRenderer extends CountdownRenderProps { export interface TRenderer extends CountdownRenderProps {
text: string; text: string;
} }
export interface ICountdown { export interface TCountdown {
timeout: number; timeout: number;
text: string; text: string;
} }

View file

@ -21,7 +21,7 @@ interface TViewer extends Pick<UseDisclosureReturn, 'isOpen' | 'onClose'> {
children: React.ReactNode; children: React.ReactNode;
} }
const Viewer = (props: TViewer) => { const Viewer: React.FC<TViewer> = (props: TViewer) => {
const { title, isOpen, onClose, children } = props; const { title, isOpen, onClose, children } = props;
const bg = useColorValue('white', 'black'); const bg = useColorValue('white', 'black');
const color = useColorValue('black', 'white'); const color = useColorValue('black', 'white');
@ -39,7 +39,7 @@ const Viewer = (props: TViewer) => {
); );
}; };
export const Debugger = () => { export const Debugger: React.FC = () => {
const { isOpen: configOpen, onOpen: onConfigOpen, onClose: configClose } = useDisclosure(); const { isOpen: configOpen, onOpen: onConfigOpen, onClose: configClose } = useDisclosure();
const { isOpen: themeOpen, onOpen: onThemeOpen, onClose: themeClose } = useDisclosure(); const { isOpen: themeOpen, onOpen: onThemeOpen, onClose: themeClose } = useDisclosure();
const { colorMode } = useColorMode(); const { colorMode } = useColorMode();
@ -64,7 +64,8 @@ export const Debugger = () => {
position="relative" position="relative"
justifyContent="center" justifyContent="center"
borderColor={borderColor} borderColor={borderColor}
spacing={{ base: 2, lg: 8 }}> spacing={{ base: 2, lg: 8 }}
>
<Tag size={tagSize} colorScheme="gray"> <Tag size={tagSize} colorScheme="gray">
{colorMode.toUpperCase()} {colorMode.toUpperCase()}
</Tag> </Tag>

View file

@ -1,11 +1,11 @@
import { Box, Button, Menu, MenuButton, MenuList, MenuItem } from '@chakra-ui/react'; import { Button, Menu, MenuButton, MenuList } from '@chakra-ui/react';
import { Markdown } from '~/components'; import { Markdown } from '~/components';
import { useColorValue, useBreakpointValue } from '~/context'; import { useColorValue, useBreakpointValue } from '~/context';
import { useOpposingColor } from '~/hooks'; import { useOpposingColor } from '~/hooks';
import type { TFooterButton } from './types'; import type { TFooterButton } from './types';
export const FooterButton = (props: TFooterButton) => { export const FooterButton: React.FC<TFooterButton> = (props: TFooterButton) => {
const { content, title, side, ...rest } = props; const { content, title, side, ...rest } = props;
const placement = side === 'left' ? 'top' : side === 'right' ? 'top-end' : undefined; const placement = side === 'left' ? 'top' : side === 'right' ? 'top-end' : undefined;
const bg = useColorValue('white', 'gray.900'); const bg = useColorValue('white', 'gray.900');
@ -17,7 +17,8 @@ export const FooterButton = (props: TFooterButton) => {
as={Button} as={Button}
size={size} size={size}
variant="ghost" variant="ghost"
aria-label={typeof title === 'string' ? title : undefined}> aria-label={typeof title === 'string' ? title : undefined}
>
{title} {title}
</MenuButton> </MenuButton>
<MenuList <MenuList
@ -29,7 +30,8 @@ export const FooterButton = (props: TFooterButton) => {
textAlign="left" textAlign="left"
mx={{ base: 1, lg: 2 }} mx={{ base: 1, lg: 2 }}
maxW={{ base: '100%', lg: '50vw' }} maxW={{ base: '100%', lg: '50vw' }}
{...rest}> {...rest}
>
<Markdown content={content} /> <Markdown content={content} />
</MenuList> </MenuList>
</Menu> </Menu>

View file

@ -10,34 +10,37 @@ import type { TColorModeToggle } from './types';
const Sun = dynamic<MeronexIcon>(() => import('@meronex/icons/hi').then(i => i.HiSun)); const Sun = dynamic<MeronexIcon>(() => import('@meronex/icons/hi').then(i => i.HiSun));
const Moon = dynamic<MeronexIcon>(() => import('@meronex/icons/hi').then(i => i.HiMoon)); const Moon = dynamic<MeronexIcon>(() => import('@meronex/icons/hi').then(i => i.HiMoon));
export const ColorModeToggle = forwardRef<HTMLButtonElement, TColorModeToggle>((props, ref) => { export const ColorModeToggle = forwardRef<HTMLButtonElement, TColorModeToggle>(
const { size = '1.5rem', ...rest } = props; (props: TColorModeToggle, ref) => {
const { colorMode, toggleColorMode } = useColorMode(); const { size = '1.5rem', ...rest } = props;
const { colorMode, toggleColorMode } = useColorMode();
const bg = useColorValue('primary.500', 'yellow.300'); const bg = useColorValue('primary.500', 'yellow.300');
const color = useOpposingColor(bg); const color = useOpposingColor(bg);
const label = useColorValue('Switch to dark mode', 'Switch to light mode'); const label = useColorValue('Switch to dark mode', 'Switch to light mode');
const btnSize = useBreakpointValue({ base: 'xs', lg: 'sm' }); const btnSize = useBreakpointValue({ base: 'xs', lg: 'sm' });
return ( return (
<Tooltip hasArrow placement="top-end" label={label} bg={bg} color={color}> <Tooltip hasArrow placement="top-end" label={label} bg={bg} color={color}>
<Button <Button
ref={ref} ref={ref}
size={btnSize} size={btnSize}
title={label} title={label}
variant="ghost" variant="ghost"
aria-label={label} aria-label={label}
_hover={{ color: bg }} _hover={{ color: bg }}
color="currentColor" color="currentColor"
onClick={toggleColorMode} onClick={toggleColorMode}
{...rest}> {...rest}
<If c={colorMode === 'light'}> >
<Icon as={Moon} boxSize={size} /> <If c={colorMode === 'light'}>
</If> <Icon as={Moon} boxSize={size} />
<If c={colorMode === 'dark'}> </If>
<Icon as={Sun} boxSize={size} /> <If c={colorMode === 'dark'}>
</If> <Icon as={Sun} boxSize={size} />
</Button> </If>
</Tooltip> </Button>
); </Tooltip>
}); );
},
);

View file

@ -9,7 +9,7 @@ import { ColorModeToggle } from './colorMode';
const CodeIcon = dynamic<MeronexIcon>(() => import('@meronex/icons/fi').then(i => i.FiCode)); const CodeIcon = dynamic<MeronexIcon>(() => import('@meronex/icons/fi').then(i => i.FiCode));
const ExtIcon = dynamic<MeronexIcon>(() => import('@meronex/icons/go').then(i => i.GoLinkExternal)); const ExtIcon = dynamic<MeronexIcon>(() => import('@meronex/icons/go').then(i => i.GoLinkExternal));
export const Footer = () => { export const Footer: React.FC = () => {
const { web, content, primary_asn } = useConfig(); const { web, content, primary_asn } = useConfig();
const footerBg = useColorValue('blackAlpha.50', 'whiteAlpha.100'); const footerBg = useColorValue('blackAlpha.50', 'whiteAlpha.100');
@ -32,7 +32,8 @@ export const Footer = () => {
bg={footerBg} bg={footerBg}
color={footerColor} color={footerColor}
spacing={{ base: 8, lg: 6 }} spacing={{ base: 8, lg: 6 }}
justifyContent={{ base: 'center', lg: 'space-between' }}> justifyContent={{ base: 'center', lg: 'space-between' }}
>
<If c={web.terms.enable}> <If c={web.terms.enable}>
<FooterButton side="left" content={content.terms} title={web.terms.title} /> <FooterButton side="left" content={content.terms} title={web.terms.title} />
</If> </If>
@ -47,7 +48,8 @@ export const Footer = () => {
size={btnSize} size={btnSize}
variant="ghost" variant="ghost"
rightIcon={<ExtIcon />} rightIcon={<ExtIcon />}
aria-label={web.external_link.title}> aria-label={web.external_link.title}
>
{web.external_link.title} {web.external_link.title}
</Button> </Button>
</If> </If>

View file

@ -6,7 +6,7 @@ import { useBooleanValue } from '~/hooks';
import { TField, TFormError } from './types'; import { TField, TFormError } from './types';
export const FormField = (props: TField) => { export const FormField: React.FC<TField> = (props: TField) => {
const { name, label, children, labelAddOn, fieldAddOn, hiddenLabels = false, ...rest } = props; const { name, label, children, labelAddOn, fieldAddOn, hiddenLabels = false, ...rest } = props;
const labelColor = useColorValue('blackAlpha.700', 'whiteAlpha.700'); const labelColor = useColorValue('blackAlpha.700', 'whiteAlpha.700');
const errorColor = useColorValue('red.500', 'red.300'); const errorColor = useColorValue('red.500', 'red.300');
@ -30,7 +30,8 @@ export const FormField = (props: TField) => {
my={{ base: 2, lg: 4 }} my={{ base: 2, lg: 4 }}
isInvalid={error !== false} isInvalid={error !== false}
flex={{ base: '1 0 100%', lg: '1 0 33.33%' }} flex={{ base: '1 0 100%', lg: '1 0 33.33%' }}
{...rest}> {...rest}
>
<FormLabel <FormLabel
pl={1} pl={1}
pr={0} pr={0}
@ -39,7 +40,8 @@ export const FormField = (props: TField) => {
opacity={opacity} opacity={opacity}
alignItems="center" alignItems="center"
justifyContent="space-between" justifyContent="space-between"
color={error !== false ? errorColor : labelColor}> color={error !== false ? errorColor : labelColor}
>
{label} {label}
<If c={typeof labelAddOn !== 'undefined'}>{labelAddOn}</If> <If c={typeof labelAddOn !== 'undefined'}>{labelAddOn}</If>
</FormLabel> </FormLabel>

View file

@ -19,7 +19,7 @@ function buildOptions(networks: TNetwork[]) {
}); });
} }
export const QueryLocation = (props: TQuerySelectField) => { export const QueryLocation: React.FC<TQuerySelectField> = (props: TQuerySelectField) => {
const { onChange, label } = props; const { onChange, label } = props;
const { networks } = useConfig(); const { networks } = useConfig();

View file

@ -9,8 +9,6 @@ import type { OptionProps } from 'react-select';
import type { TBGPCommunity, TSelectOption } from '~/types'; import type { TBGPCommunity, TSelectOption } from '~/types';
import type { TQueryTarget } from './types'; import type { TQueryTarget } from './types';
const fqdnPattern = /^(?!:\/\/)([a-zA-Z0-9-]+\.)?[a-zA-Z0-9-][a-zA-Z0-9-]+\.[a-zA-Z-]{2,6}?$/gim;
function buildOptions(communities: TBGPCommunity[]): TSelectOption[] { function buildOptions(communities: TBGPCommunity[]): TSelectOption[] {
return communities.map(c => ({ return communities.map(c => ({
value: c.community, value: c.community,
@ -32,7 +30,7 @@ const Option = (props: OptionProps<Dict, false>) => {
); );
}; };
export const QueryTarget = (props: TQueryTarget) => { export const QueryTarget: React.FC<TQueryTarget> = (props: TQueryTarget) => {
const { name, register, onChange, placeholder } = props; const { name, register, onChange, placeholder } = props;
const bg = useColorValue('white', 'whiteAlpha.100'); const bg = useColorValue('white', 'whiteAlpha.100');

View file

@ -13,7 +13,7 @@ function buildOptions(queryTypes: TQuery[]): TSelectOption[] {
.map(q => ({ value: q.name, label: q.display_name })); .map(q => ({ value: q.name, label: q.display_name }));
} }
export const QueryType = (props: TQuerySelectField) => { export const QueryType: React.FC<TQuerySelectField> = (props: TQuerySelectField) => {
const { onChange, label } = props; const { onChange, label } = props;
const { queries } = useConfig(); const { queries } = useConfig();
const { errors } = useFormContext(); const { errors } = useFormContext();

View file

@ -9,7 +9,7 @@ function buildOptions(queryVrfs: TDeviceVrf[]): TSelectOption[] {
return queryVrfs.map(q => ({ value: q.id, label: q.display_name })); return queryVrfs.map(q => ({ value: q.id, label: q.display_name }));
} }
export const QueryVrf = (props: TQueryVrf) => { export const QueryVrf: React.FC<TQueryVrf> = (props: TQueryVrf) => {
const { vrfs, onChange, label } = props; const { vrfs, onChange, label } = props;
const { selections } = useLGState(); const { selections } = useLGState();
const { exportState } = useLGMethods(); const { exportState } = useLGMethods();

View file

@ -96,7 +96,8 @@ export const ResolvedTarget = (props: TResolvedTarget) => {
colorScheme="primary" colorScheme="primary"
justifyContent="space-between" justifyContent="space-between"
onClick={() => selectTarget(answer4)} onClick={() => selectTarget(answer4)}
rightIcon={<RightArrow boxSize="18px" />}> rightIcon={<RightArrow boxSize="18px" />}
>
{answer4} {answer4}
</Button> </Button>
)} )}
@ -109,7 +110,8 @@ export const ResolvedTarget = (props: TResolvedTarget) => {
colorScheme="secondary" colorScheme="secondary"
justifyContent="space-between" justifyContent="space-between"
onClick={() => selectTarget(answer6)} onClick={() => selectTarget(answer6)}
rightIcon={<RightArrow boxSize="18px" />}> rightIcon={<RightArrow boxSize="18px" />}
>
{answer6} {answer6}
</Button> </Button>
)} )}
@ -126,7 +128,8 @@ export const ResolvedTarget = (props: TResolvedTarget) => {
colorScheme="red" colorScheme="red"
variant="outline" variant="outline"
onClick={errorClose} onClick={errorClose}
leftIcon={<LeftArrow />}> leftIcon={<LeftArrow />}
>
{web.text.fqdn_error_button} {web.text.fqdn_error_button}
</Button> </Button>
</> </>

View file

@ -2,7 +2,7 @@ import { Flex } from '@chakra-ui/react';
import { FlexProps } from '@chakra-ui/react'; import { FlexProps } from '@chakra-ui/react';
export const FormRow = (props: FlexProps) => { export const FormRow: React.FC<FlexProps> = (props: FlexProps) => {
return ( return (
<Flex <Flex
w="100%" w="100%"

View file

@ -15,9 +15,9 @@ import { useGreeting, useOpposingColor } from '~/hooks';
import type { TGreeting } from './types'; import type { TGreeting } from './types';
export const Greeting = (props: TGreeting) => { export const Greeting: React.FC<TGreeting> = (props: TGreeting) => {
const { web, content } = useConfig(); const { web, content } = useConfig();
const { ack: greetingAck, isOpen, close, open } = useGreeting(); const { ack: greetingAck, isOpen, close } = useGreeting();
const bg = useColorValue('white', 'gray.800'); const bg = useColorValue('white', 'gray.800');
const color = useOpposingColor(bg); const color = useOpposingColor(bg);
@ -48,7 +48,8 @@ export const Greeting = (props: TGreeting) => {
isOpen={isOpen.value} isOpen={isOpen.value}
motionPreset="slideInBottom" motionPreset="slideInBottom"
closeOnEsc={web.greeting.required} closeOnEsc={web.greeting.required}
closeOnOverlayClick={web.greeting.required}> closeOnOverlayClick={web.greeting.required}
>
<ModalOverlay /> <ModalOverlay />
<ModalContent <ModalContent
py={4} py={4}
@ -56,7 +57,8 @@ export const Greeting = (props: TGreeting) => {
color={color} color={color}
borderRadius="md" borderRadius="md"
maxW={{ base: '95%', md: '75%' }} maxW={{ base: '95%', md: '75%' }}
{...props}> {...props}
>
<ModalHeader>{web.greeting.title}</ModalHeader> <ModalHeader>{web.greeting.title}</ModalHeader>
<If c={!web.greeting.required}> <If c={!web.greeting.required}>
<ModalCloseButton /> <ModalCloseButton />

View file

@ -7,7 +7,7 @@ import { Title } from './title';
import type { THeader } from './types'; import type { THeader } from './types';
export const Header = (props: THeader) => { export const Header: React.FC<THeader> = (props: THeader) => {
const { resetForm, ...rest } = props; const { resetForm, ...rest } = props;
const bg = useColorValue('white', 'black'); const bg = useColorValue('white', 'black');
@ -35,7 +35,8 @@ export const Header = (props: THeader) => {
width="full" width="full"
flex="0 1 auto" flex="0 1 auto"
color="gray.500" color="gray.500"
{...rest}> {...rest}
>
<ScaleFade in initialScale={0.5} style={{ width: '100%' }}> <ScaleFade in initialScale={0.5} style={{ width: '100%' }}>
<AnimatedDiv <AnimatedDiv
layout layout
@ -45,7 +46,8 @@ export const Header = (props: THeader) => {
maxW={titleWidth} maxW={titleWidth}
// This is here for the logo // This is here for the logo
justifyContent={justify} justifyContent={justify}
mx={{ base: isSubmitting.value ? 'auto' : 0, lg: 'auto' }}> mx={{ base: isSubmitting.value ? 'auto' : 0, lg: 'auto' }}
>
<Title /> <Title />
</AnimatedDiv> </AnimatedDiv>
</ScaleFade> </ScaleFade>

View file

@ -34,7 +34,7 @@ function useLogo(): [string, () => void] {
return useMemo(() => [fallback ?? src, setFallback], [colorMode]); return useMemo(() => [fallback ?? src, setFallback], [colorMode]);
} }
export const Logo = (props: TLogo) => { export const Logo: React.FC<TLogo> = (props: TLogo) => {
const { web } = useConfig(); const { web } = useConfig();
const { width } = web.logo; const { width } = web.logo;

View file

@ -2,7 +2,7 @@ import { Heading } from '@chakra-ui/react';
import { useConfig, useBreakpointValue } from '~/context'; import { useConfig, useBreakpointValue } from '~/context';
import { useTitleSize } from './useTitleSize'; import { useTitleSize } from './useTitleSize';
export const SubtitleOnly = () => { export const SubtitleOnly: React.FC = () => {
const { web } = useConfig(); const { web } = useConfig();
const sizeSm = useTitleSize(web.text.subtitle, 'sm'); const sizeSm = useTitleSize(web.text.subtitle, 'sm');
const fontSize = useBreakpointValue({ base: sizeSm, lg: 'xl' }); const fontSize = useBreakpointValue({ base: sizeSm, lg: 'xl' });
@ -13,7 +13,8 @@ export const SubtitleOnly = () => {
fontWeight="normal" fontWeight="normal"
fontSize={fontSize} fontSize={fontSize}
whiteSpace="break-spaces" whiteSpace="break-spaces"
textAlign={{ base: 'left', xl: 'center' }}> textAlign={{ base: 'left', xl: 'center' }}
>
{web.text.subtitle} {web.text.subtitle}
</Heading> </Heading>
); );

View file

@ -15,7 +15,7 @@ const AnimatedVStack = motion.custom(VStack);
/** /**
* Title wrapper for mobile devices, breakpoints sm & md. * Title wrapper for mobile devices, breakpoints sm & md.
*/ */
const MWrapper = (props: TMWrapper) => { const MWrapper: React.FC<TMWrapper> = (props: TMWrapper) => {
const { isSubmitting } = useLGState(); const { isSubmitting } = useLGState();
return ( return (
<AnimatedVStack <AnimatedVStack
@ -30,7 +30,7 @@ const MWrapper = (props: TMWrapper) => {
/** /**
* Title wrapper for desktop devices, breakpoints lg & xl. * Title wrapper for desktop devices, breakpoints lg & xl.
*/ */
const DWrapper = (props: TDWrapper) => { const DWrapper: React.FC<TDWrapper> = (props: TDWrapper) => {
const { isSubmitting } = useLGState(); const { isSubmitting } = useLGState();
return ( return (
<AnimatedVStack <AnimatedVStack
@ -49,7 +49,7 @@ const DWrapper = (props: TDWrapper) => {
* Universal wrapper for title sub-components, which will be different depending on the * Universal wrapper for title sub-components, which will be different depending on the
* `title_mode` configuration variable. * `title_mode` configuration variable.
*/ */
const TitleWrapper = (props: TDWrapper | TMWrapper) => { const TitleWrapper: React.FC<TDWrapper | TMWrapper> = (props: TDWrapper | TMWrapper) => {
const isMobile = useMobile(); const isMobile = useMobile();
return ( return (
<> <>
@ -61,7 +61,7 @@ const TitleWrapper = (props: TDWrapper | TMWrapper) => {
/** /**
* Title sub-component if `title_mode` is set to `text_only`. * Title sub-component if `title_mode` is set to `text_only`.
*/ */
const TextOnly = (props: TTitleWrapper) => { const TextOnly: React.FC<TTitleWrapper> = (props: TTitleWrapper) => {
return ( return (
<TitleWrapper {...props}> <TitleWrapper {...props}>
<TitleOnly /> <TitleOnly />
@ -73,7 +73,7 @@ const TextOnly = (props: TTitleWrapper) => {
/** /**
* Title sub-component if `title_mode` is set to `logo_only`. Renders only the logo. * Title sub-component if `title_mode` is set to `logo_only`. Renders only the logo.
*/ */
const LogoOnly = () => ( const LogoOnly: React.FC = () => (
<TitleWrapper> <TitleWrapper>
<Logo /> <Logo />
</TitleWrapper> </TitleWrapper>
@ -83,7 +83,7 @@ const LogoOnly = () => (
* Title sub-component if `title_mode` is set to `logo_subtitle`. Renders the logo with the * Title sub-component if `title_mode` is set to `logo_subtitle`. Renders the logo with the
* subtitle underneath. * subtitle underneath.
*/ */
const LogoSubtitle = () => ( const LogoSubtitle: React.FC = () => (
<TitleWrapper> <TitleWrapper>
<Logo /> <Logo />
<SubtitleOnly /> <SubtitleOnly />
@ -93,7 +93,7 @@ const LogoSubtitle = () => (
/** /**
* Title sub-component if `title_mode` is set to `all`. Renders the logo, title, and subtitle. * Title sub-component if `title_mode` is set to `all`. Renders the logo, title, and subtitle.
*/ */
const All = () => ( const All: React.FC = () => (
<TitleWrapper> <TitleWrapper>
<Logo /> <Logo />
<TextOnly mt={2} /> <TextOnly mt={2} />
@ -103,7 +103,7 @@ const All = () => (
/** /**
* Title component which renders sub-components based on the `title_mode` configuration variable. * Title component which renders sub-components based on the `title_mode` configuration variable.
*/ */
export const Title = (props: TTitle) => { export const Title: React.FC<TTitle> = (props: TTitle) => {
const { fontSize, ...rest } = props; const { fontSize, ...rest } = props;
const { web } = useConfig(); const { web } = useConfig();
const titleMode = web.text.title_mode; const titleMode = web.text.title_mode;
@ -133,7 +133,8 @@ export const Title = (props: TTitle) => {
*/ */
flexBasis={{ base: '100%', lg: isSafari ? '33%' : '100%' }} flexBasis={{ base: '100%', lg: isSafari ? '33%' : '100%' }}
mt={[null, isSubmitting.value ? null : 'auto']} mt={[null, isSubmitting.value ? null : 'auto']}
{...rest}> {...rest}
>
<Button <Button
px={0} px={0}
variant="link" variant="link"
@ -141,7 +142,8 @@ export const Title = (props: TTitle) => {
flexDir="column" flexDir="column"
onClick={handleClick} onClick={handleClick}
_focus={{ boxShadow: 'none' }} _focus={{ boxShadow: 'none' }}
_hover={{ textDecoration: 'none' }}> _hover={{ textDecoration: 'none' }}
>
<If c={titleMode === 'text_only'}> <If c={titleMode === 'text_only'}>
<TextOnly /> <TextOnly />
</If> </If>

View file

@ -3,7 +3,7 @@ import { useConfig } from '~/context';
import { useBooleanValue, useLGState } from '~/hooks'; import { useBooleanValue, useLGState } from '~/hooks';
import { useTitleSize } from './useTitleSize'; import { useTitleSize } from './useTitleSize';
export const TitleOnly = () => { export const TitleOnly: React.FC = () => {
const { web } = useConfig(); const { web } = useConfig();
const { isSubmitting } = useLGState(); const { isSubmitting } = useLGState();

View file

@ -14,9 +14,9 @@ import { useMobile } from '~/context';
// 5xl: 7 // 5xl: 7
type Sizes = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl' | '5xl'; type Sizes = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl' | '5xl';
export function useTitleSize(title: string, defaultSize: Sizes, deps: any[] = []) { export function useTitleSize(title: string, defaultSize: Sizes, deps: unknown[] = []): string {
const [size, setSize] = useState<Sizes>(defaultSize); const [size, setSize] = useState<Sizes>(defaultSize);
const realSize = useToken('fontSizes', size); const realSize = useToken('fontSizes', size) as string;
const isMobile = useMobile(); const isMobile = useMobile();
function getSize(l: number): void { function getSize(l: number): void {
switch (true) { switch (true) {

View file

@ -18,7 +18,7 @@ import type { THelpModal } from './types';
const Info = dynamic<MeronexIcon>(() => import('@meronex/icons/fi').then(i => i.FiInfo)); const Info = dynamic<MeronexIcon>(() => import('@meronex/icons/fi').then(i => i.FiInfo));
export const HelpModal = (props: THelpModal) => { export const HelpModal: React.FC<THelpModal> = (props: THelpModal) => {
const { visible, item, name, ...rest } = props; const { visible, item, name, ...rest } = props;
const { isOpen, onOpen, onClose } = useDisclosure(); const { isOpen, onOpen, onClose } = useDisclosure();
const bg = useColorValue('whiteSolid.50', 'blackSolid.800'); const bg = useColorValue('whiteSolid.50', 'blackSolid.800');

View file

@ -5,7 +5,7 @@ import { useOpposingColor } from '~/hooks';
import type { TLabel } from './types'; import type { TLabel } from './types';
export const Label = forwardRef<HTMLDivElement, TLabel>((props, ref) => { const _Label: React.ForwardRefRenderFunction<HTMLDivElement, TLabel> = (props: TLabel, ref) => {
const { value, label, labelColor, bg = 'primary.600', valueColor, ...rest } = props; const { value, label, labelColor, bg = 'primary.600', valueColor, ...rest } = props;
const valueColorAuto = useOpposingColor(bg); const valueColorAuto = useOpposingColor(bg);
@ -19,7 +19,8 @@ export const Label = forwardRef<HTMLDivElement, TLabel>((props, ref) => {
alignItems="center" alignItems="center"
mx={{ base: 1, md: 2 }} mx={{ base: 1, md: 2 }}
justifyContent="flex-start" justifyContent="flex-start"
{...rest}> {...rest}
>
<Flex <Flex
mb={2} mb={2}
mr={0} mr={0}
@ -35,7 +36,8 @@ export const Label = forwardRef<HTMLDivElement, TLabel>((props, ref) => {
borderBottomLeftRadius={4} borderBottomLeftRadius={4}
borderBottomRightRadius={0} borderBottomRightRadius={0}
fontSize={{ base: 'xs', md: 'sm' }} fontSize={{ base: 'xs', md: 'sm' }}
color={valueColor ?? valueColorAuto}> color={valueColor ?? valueColorAuto}
>
{value} {value}
</Flex> </Flex>
<Flex <Flex
@ -53,9 +55,12 @@ export const Label = forwardRef<HTMLDivElement, TLabel>((props, ref) => {
borderBottomRightRadius={4} borderBottomRightRadius={4}
fontSize={{ base: 'xs', md: 'sm' }} fontSize={{ base: 'xs', md: 'sm' }}
color={labelColor ?? defaultLabelColor} color={labelColor ?? defaultLabelColor}
boxShadow={`inset 0px 0px 0px 1px ${bg}`}> boxShadow={`inset 0px 0px 0px 1px ${bg}`}
>
{label} {label}
</Flex> </Flex>
</Flex> </Flex>
); );
}); };
export const Label = forwardRef(_Label);

View file

@ -8,7 +8,7 @@ import { ResetButton } from './resetButton';
import type { TFrame } from './types'; import type { TFrame } from './types';
export const Frame = (props: TFrame) => { export const Frame: React.FC<TFrame> = (props: TFrame) => {
const { developer_mode } = useConfig(); const { developer_mode } = useConfig();
const { isSubmitting } = useLGState(); const { isSubmitting } = useLGState();
const { resetForm } = useLGMethods(); const { resetForm } = useLGMethods();
@ -39,7 +39,8 @@ export const Frame = (props: TFrame) => {
* viewport. Safari needs `-webkit-fill-available`, but other browsers need `100vh`. * viewport. Safari needs `-webkit-fill-available`, but other browsers need `100vh`.
* @see https://allthingssmitty.com/2020/05/11/css-fix-for-100vh-in-mobile-webkit/ * @see https://allthingssmitty.com/2020/05/11/css-fix-for-100vh-in-mobile-webkit/
*/ */
minHeight={isSafari ? '-webkit-fill-available' : '100vh'}> minHeight={isSafari ? '-webkit-fill-available' : '100vh'}
>
<Header resetForm={handleReset} /> <Header resetForm={handleReset} />
<Flex <Flex
px={2} px={2}

View file

@ -9,7 +9,7 @@ import type { TResetButton } from './types';
const LeftArrow = dynamic<MeronexIcon>(() => import('@meronex/icons/fa').then(i => i.FaAngleLeft)); const LeftArrow = dynamic<MeronexIcon>(() => import('@meronex/icons/fa').then(i => i.FaAngleLeft));
export const ResetButton = (props: TResetButton) => { export const ResetButton: React.FC<TResetButton> = (props: TResetButton) => {
const { developerMode, resetForm, ...rest } = props; const { developerMode, resetForm, ...rest } = props;
const { isSubmitting } = useLGState(); const { isSubmitting } = useLGState();
const bg = useColorValue('primary.500', 'primary.300'); const bg = useColorValue('primary.500', 'primary.300');
@ -30,7 +30,8 @@ export const ResetButton = (props: TResetButton) => {
borderRightRadius="md" borderRightRadius="md"
initial={{ x: '-100%' }} initial={{ x: '-100%' }}
mb={developerMode ? { base: 0, lg: 14 } : undefined} mb={developerMode ? { base: 0, lg: 14 } : undefined}
transition={{ duration: 0.15, ease: [0.4, 0, 0.2, 1] }}> transition={{ duration: 0.15, ease: [0.4, 0, 0.2, 1] }}
>
<Flex boxSize="100%" justifyContent="center" alignItems="center" {...rest}> <Flex boxSize="100%" justifyContent="center" alignItems="center" {...rest}>
<IconButton <IconButton
variant="unstyled" variant="unstyled"

View file

@ -17,7 +17,8 @@ export const Loading: LoadableBaseOptions['loading'] = () => (
flexDirection="column" flexDirection="column"
css={{ css={{
'@media (prefers-color-scheme: dark)': { backgroundColor: 'black', color: 'white' }, '@media (prefers-color-scheme: dark)': { backgroundColor: 'black', color: 'white' },
}}> }}
>
<Spinner color="primary.500" w="6rem" h="6rem" /> <Spinner color="primary.500" w="6rem" h="6rem" />
</Flex> </Flex>
</Flex> </Flex>

View file

@ -41,10 +41,10 @@ function useIsFqdn(target: string, _type: string) {
); );
} }
export const LookingGlass = () => { export const LookingGlass: React.FC = () => {
const { web, content, messages } = useConfig(); const { web, content, messages } = useConfig();
const { ack, greetingReady, isOpen: greetingIsOpen } = useGreeting(); const { ack, greetingReady } = useGreeting();
const getDevice = useDevice(); const getDevice = useDevice();
const noQueryType = useStrf(messages.no_input, { field: web.text.query_type }); const noQueryType = useStrf(messages.no_input, { field: web.text.query_type });
@ -243,7 +243,8 @@ export const LookingGlass = () => {
exit={{ opacity: 0, x: -300 }} exit={{ opacity: 0, x: -300 }}
initial={{ opacity: 0, y: 300 }} initial={{ opacity: 0, y: 300 }}
maxW={{ base: '100%', lg: '75%' }} maxW={{ base: '100%', lg: '75%' }}
onSubmit={handleSubmit(submitHandler)}> onSubmit={handleSubmit(submitHandler)}
>
<FormRow> <FormRow>
<FormField name="query_location" label={web.text.query_location}> <FormField name="query_location" label={web.text.query_location}>
<QueryLocation onChange={handleChange} label={web.text.query_location} /> <QueryLocation onChange={handleChange} label={web.text.query_location} />
@ -253,7 +254,8 @@ export const LookingGlass = () => {
label={web.text.query_type} label={web.text.query_type}
labelAddOn={ labelAddOn={
<HelpModal visible={isQueryContent(vrfContent)} item={vrfContent} name="query_type" /> <HelpModal visible={isQueryContent(vrfContent)} item={vrfContent} name="query_type" />
}> }
>
<QueryType onChange={handleChange} label={web.text.query_type} /> <QueryType onChange={handleChange} label={web.text.query_type} />
</FormField> </FormField>
</FormRow> </FormRow>
@ -280,7 +282,8 @@ export const LookingGlass = () => {
maxW="100%" maxW="100%"
flex="0 0 0" flex="0 0 0"
flexDir="column" flexDir="column"
mr={{ base: 0, lg: 2 }}> mr={{ base: 0, lg: 2 }}
>
<SubmitButton handleChange={handleChange} /> <SubmitButton handleChange={handleChange} />
</Flex> </Flex>
</FormRow> </FormRow>

View file

@ -44,12 +44,12 @@ function clean<P extends ChakraProps>(props: P): P {
return props; return props;
} }
export const Checkbox = (props: TCheckbox & MDProps) => { export const Checkbox: React.FC<TCheckbox & MDProps> = (props: TCheckbox & MDProps) => {
const { checked, node, ...rest } = props; const { checked, node, ...rest } = props;
return <ChakraCheckbox isChecked={checked} {...rest} />; return <ChakraCheckbox isChecked={checked} {...rest} />;
}; };
export const List = (props: TList) => { export const List: React.FC<TList> = (props: TList) => {
const { ordered, ...rest } = props; const { ordered, ...rest } = props;
return ( return (
<> <>
@ -63,7 +63,7 @@ export const List = (props: TList) => {
); );
}; };
export const ListItem = (props: TListItem & MDProps) => { export const ListItem: React.FC<TListItem & MDProps> = (props: TListItem & MDProps) => {
const { checked, node, ...rest } = props; const { checked, node, ...rest } = props;
return checked ? ( return checked ? (
<Checkbox checked={checked} node={node} {...rest} /> <Checkbox checked={checked} node={node} {...rest} />
@ -72,7 +72,7 @@ export const ListItem = (props: TListItem & MDProps) => {
); );
}; };
export const Heading = (props: THeading) => { export const Heading: React.FC<THeading> = (props: THeading) => {
const { level, ...rest } = props; const { level, ...rest } = props;
const levelMap = { const levelMap = {
@ -87,14 +87,16 @@ export const Heading = (props: THeading) => {
return <ChakraHeading {...levelMap[level]} {...clean<Omit<THeading, 'level'>>(rest)} />; return <ChakraHeading {...levelMap[level]} {...clean<Omit<THeading, 'level'>>(rest)} />;
}; };
export const Link = (props: LinkProps) => { export const Link: React.FC<LinkProps> = (props: LinkProps) => {
const color = useColorValue('blue.500', 'blue.300'); const color = useColorValue('blue.500', 'blue.300');
return <ChakraLink isExternal color={color} {...clean<LinkProps>(props)} />; return <ChakraLink isExternal color={color} {...clean<LinkProps>(props)} />;
}; };
export const CodeBlock = (props: TCodeBlock) => <CustomCodeBlock>{props.value}</CustomCodeBlock>; export const CodeBlock: React.FC<TCodeBlock> = (props: TCodeBlock) => (
<CustomCodeBlock>{props.value}</CustomCodeBlock>
);
export const TableData = (props: TTableData) => { export const TableData: React.FC<TTableData> = (props: TTableData) => {
const { isHeader, ...rest } = props; const { isHeader, ...rest } = props;
return ( return (
<> <>
@ -108,7 +110,7 @@ export const TableData = (props: TTableData) => {
); );
}; };
export const Paragraph = (props: TextProps) => ( export const Paragraph: React.FC<TextProps> = (props: TextProps) => (
<ChakraText <ChakraText
my={4} my={4}
css={{ css={{
@ -118,11 +120,19 @@ export const Paragraph = (props: TextProps) => (
{...clean<TextProps>(props)} {...clean<TextProps>(props)}
/> />
); );
export const InlineCode = (props: CodeProps) => (
export const InlineCode: React.FC<CodeProps> = (props: CodeProps) => (
<ChakraCode borderRadius="md" px={1} {...clean<CodeProps>(props)} /> <ChakraCode borderRadius="md" px={1} {...clean<CodeProps>(props)} />
); );
export const Divider = (props: DividerProps) => (
export const Divider: React.FC<DividerProps> = (props: DividerProps) => (
<ChakraDivider my={2} {...clean<DividerProps>(props)} /> <ChakraDivider my={2} {...clean<DividerProps>(props)} />
); );
export const Table = (props: BoxProps) => <ChakraTable {...clean<BoxProps>(props)} />;
export const Br = (props: BoxProps) => <Box as="br" m={16} {...clean<BoxProps>(props)} />; export const Table: React.FC<BoxProps> = (props: BoxProps) => (
<ChakraTable {...clean<BoxProps>(props)} />
);
export const Br: React.FC<BoxProps> = (props: BoxProps) => (
<Box as="br" m={16} {...clean<BoxProps>(props)} />
);

View file

@ -30,6 +30,6 @@ const renderers = {
thematicBreak: Divider, thematicBreak: Divider,
} as ReactMarkdownProps['renderers']; } as ReactMarkdownProps['renderers'];
export const Markdown = (props: TMarkdown) => ( export const Markdown: React.FC<TMarkdown> = (props: TMarkdown) => (
<ReactMarkdown renderers={renderers} source={props.content} /> <ReactMarkdown renderers={renderers} source={props.content} />
); );

View file

@ -3,16 +3,16 @@ import { useColorValue } from '~/context';
import type { BoxProps } from '@chakra-ui/react'; import type { BoxProps } from '@chakra-ui/react';
export const Table = (props: BoxProps) => ( export const Table: React.FC<BoxProps> = (props: BoxProps) => (
<Box as="table" textAlign="left" mt={4} width="full" {...props} /> <Box as="table" textAlign="left" mt={4} width="full" {...props} />
); );
export const TH = (props: BoxProps) => { export const TH: React.FC<BoxProps> = (props: BoxProps) => {
const bg = useColorValue('blackAlpha.50', 'whiteAlpha.50'); const bg = useColorValue('blackAlpha.50', 'whiteAlpha.50');
return <Box as="th" bg={bg} fontWeight="semibold" p={2} fontSize="sm" {...props} />; return <Box as="th" bg={bg} fontWeight="semibold" p={2} fontSize="sm" {...props} />;
}; };
export const TD = (props: BoxProps) => { export const TD: React.FC<BoxProps> = (props: BoxProps) => {
return ( return (
<Box <Box
p={2} p={2}

View file

@ -1,10 +1,4 @@
import type { import type { BoxProps, CheckboxProps, HeadingProps, ListProps } from '@chakra-ui/react';
BoxProps,
CheckboxProps,
HeadingProps,
ListProps,
ListItemProps,
} from '@chakra-ui/react';
export interface TMarkdown { export interface TMarkdown {
content: string; content: string;

View file

@ -4,7 +4,7 @@ import { useTheme } from '@chakra-ui/react';
import { useConfig } from '~/context'; import { useConfig } from '~/context';
import { googleFontUrl } from '~/util'; import { googleFontUrl } from '~/util';
export const Meta = () => { export const Meta: React.FC = () => {
const config = useConfig(); const config = useConfig();
const { fonts } = useTheme(); const { fonts } = useTheme();
const [location, setLocation] = useState('/'); const [location, setLocation] = useState('/');

View file

@ -2,7 +2,7 @@ import { MonoField, Active, Weight, Age, Communities, RPKIState, ASPath } from '
import type { TCell } from './types'; import type { TCell } from './types';
export const Cell = (props: TCell) => { export const Cell: React.FC<TCell> = (props: TCell) => {
const { data, rawData } = props; const { data, rawData } = props;
const cellId = data.column.id as keyof TRoute; const cellId = data.column.id as keyof TRoute;
const component = { const component = {

View file

@ -26,7 +26,7 @@ import type {
dayjs.extend(relativeTimePlugin); dayjs.extend(relativeTimePlugin);
dayjs.extend(utcPlugin); dayjs.extend(utcPlugin);
export const MonoField = (props: TMonoField) => { export const MonoField: React.FC<TMonoField> = (props: TMonoField) => {
const { v, ...rest } = props; const { v, ...rest } = props;
return ( return (
<Text as="span" fontSize="sm" fontFamily="mono" {...rest}> <Text as="span" fontSize="sm" fontFamily="mono" {...rest}>
@ -35,7 +35,7 @@ export const MonoField = (props: TMonoField) => {
); );
}; };
export const Active = (props: TActive) => { export const Active: React.FC<TActive> = (props: TActive) => {
const { isActive } = props; const { isActive } = props;
const color = useColorValue(['gray.500', 'green.500'], ['whiteAlpha.300', 'blackAlpha.500']); const color = useColorValue(['gray.500', 'green.500'], ['whiteAlpha.300', 'blackAlpha.500']);
return ( return (
@ -50,7 +50,7 @@ export const Active = (props: TActive) => {
); );
}; };
export const Age = (props: TAge) => { export const Age: React.FC<TAge> = (props: TAge) => {
const { inSeconds, ...rest } = props; const { inSeconds, ...rest } = props;
const now = dayjs.utc(); const now = dayjs.utc();
const then = now.subtract(inSeconds, 'second'); const then = now.subtract(inSeconds, 'second');
@ -63,7 +63,7 @@ export const Age = (props: TAge) => {
); );
}; };
export const Weight = (props: TWeight) => { export const Weight: React.FC<TWeight> = (props: TWeight) => {
const { weight, winningWeight, ...rest } = props; const { weight, winningWeight, ...rest } = props;
const fixMeText = const fixMeText =
winningWeight === 'low' ? 'Lower Weight is Preferred' : 'Higher Weight is Preferred'; winningWeight === 'low' ? 'Lower Weight is Preferred' : 'Higher Weight is Preferred';
@ -76,7 +76,7 @@ export const Weight = (props: TWeight) => {
); );
}; };
export const ASPath = (props: TASPath) => { export const ASPath: React.FC<TASPath> = (props: TASPath) => {
const { path, active } = props; const { path, active } = props;
const color = useColorValue( const color = useColorValue(
// light: inactive, active // light: inactive, active
@ -89,7 +89,7 @@ export const ASPath = (props: TASPath) => {
return <Icon as={End} />; return <Icon as={End} />;
} }
let paths = [] as JSX.Element[]; const paths = [] as JSX.Element[];
path.map((asn, i) => { path.map((asn, i) => {
const asnStr = String(asn); const asnStr = String(asn);
@ -107,7 +107,7 @@ export const ASPath = (props: TASPath) => {
return <>{paths}</>; return <>{paths}</>;
}; };
export const Communities = (props: TCommunities) => { export const Communities: React.FC<TCommunities> = (props: TCommunities) => {
const { communities } = props; const { communities } = props;
const bg = useColorValue('white', 'gray.900'); const bg = useColorValue('white', 'gray.900');
const color = useOpposingColor(bg); const color = useOpposingColor(bg);
@ -133,7 +133,8 @@ export const Communities = (props: TCommunities) => {
textAlign="left" textAlign="left"
fontFamily="mono" fontFamily="mono"
fontWeight="normal" fontWeight="normal"
whiteSpace="pre-wrap"> whiteSpace="pre-wrap"
>
{communities.join('\n')} {communities.join('\n')}
</MenuList> </MenuList>
</Menu> </Menu>
@ -142,7 +143,10 @@ export const Communities = (props: TCommunities) => {
); );
}; };
export const RPKIState = forwardRef<HTMLDivElement, TRPKIState>((props, ref) => { const _RPKIState: React.ForwardRefRenderFunction<HTMLDivElement, TRPKIState> = (
props: TRPKIState,
ref,
) => {
const { state, active } = props; const { state, active } = props;
const { web } = useConfig(); const { web } = useConfig();
const bg = useColorValue( const bg = useColorValue(
@ -171,10 +175,13 @@ export const RPKIState = forwardRef<HTMLDivElement, TRPKIState>((props, ref) =>
placement="right" placement="right"
label={text[state] ?? text[3]} label={text[state] ?? text[3]}
bg={bg[+active][state]} bg={bg[+active][state]}
color={color}> color={color}
>
<Box ref={ref} boxSize={5}> <Box ref={ref} boxSize={5}>
<Box as={icon[state]} color={bg[+active][state]} /> <Box as={icon[state]} color={bg[+active][state]} />
</Box> </Box>
</Tooltip> </Tooltip>
); );
}); };
export const RPKIState = forwardRef<HTMLDivElement, TRPKIState>(_RPKIState);

View file

@ -10,7 +10,7 @@ function makeColumns(fields: TParsedDataField[]): TColumn[] {
return fields.map(pair => { return fields.map(pair => {
const [header, accessor, align] = pair; const [header, accessor, align] = pair;
let columnConfig = { const columnConfig = {
align, align,
accessor, accessor,
hidden: false, hidden: false,
@ -25,7 +25,7 @@ function makeColumns(fields: TParsedDataField[]): TColumn[] {
}); });
} }
export const BGPTable = (props: TBGPTable) => { export const BGPTable: React.FC<TBGPTable> = (props: TBGPTable) => {
const { children: data, ...rest } = props; const { children: data, ...rest } = props;
const { parsed_data_fields } = useConfig(); const { parsed_data_fields } = useConfig();
const columns = makeColumns(parsed_data_fields); const columns = makeColumns(parsed_data_fields);
@ -34,11 +34,11 @@ export const BGPTable = (props: TBGPTable) => {
<Flex my={8} justify="center" maxW="100%" w="100%" {...rest}> <Flex my={8} justify="center" maxW="100%" w="100%" {...rest}>
<Table <Table
columns={columns} columns={columns}
bordersHorizontal
data={data.routes} data={data.routes}
rowHighlightBg="green"
rowHighlightProp="active" rowHighlightProp="active"
Cell={(d: TCellRender) => <Cell data={d} rawData={data} />} Cell={(d: TCellRender) => <Cell data={d} rawData={data} />}
bordersHorizontal
rowHighlightBg="green"
/> />
</Flex> </Flex>
); );

View file

@ -3,7 +3,7 @@ import { useColorValue } from '~/context';
import type { TTextOutput } from './types'; import type { TTextOutput } from './types';
export const TextOutput = (props: TTextOutput) => { export const TextOutput: React.FC<TTextOutput> = (props: TTextOutput) => {
const { children, ...rest } = props; const { children, ...rest } = props;
const bg = useColorValue('blackAlpha.100', 'gray.800'); const bg = useColorValue('blackAlpha.100', 'gray.800');
@ -31,7 +31,8 @@ export const TextOutput = (props: TTextOutput) => {
color: selectionColor, color: selectionColor,
}, },
}} }}
{...rest}> {...rest}
>
{children.split('\\n').join('\n').replace(/\n\n/g, '\n')} {children.split('\\n').join('\n').replace(/\n\n/g, '\n')}
</Box> </Box>
); );

View file

@ -7,7 +7,7 @@ const PathIcon = dynamic<MeronexIcon>(() =>
import('@meronex/icons/bi').then(i => i.BisNetworkChart), import('@meronex/icons/bi').then(i => i.BisNetworkChart),
); );
export const PathButton = (props: TPathButton) => { export const PathButton: React.FC<TPathButton> = (props: TPathButton) => {
const { onOpen } = props; const { onOpen } = props;
return ( return (
<Tooltip hasArrow label="View AS Path" placement="top"> <Tooltip hasArrow label="View AS Path" placement="top">

View file

@ -11,7 +11,7 @@ import { buildElements } from './util';
import type { ReactFlowProps } from 'react-flow-renderer'; import type { ReactFlowProps } from 'react-flow-renderer';
import type { TChart, TNode, TNodeData } from './types'; import type { TChart, TNode, TNodeData } from './types';
export const Chart = (props: TChart) => { export const Chart: React.FC<TChart> = (props: TChart) => {
const { data } = props; const { data } = props;
const { primary_asn, org_name } = useConfig(); const { primary_asn, org_name } = useConfig();
@ -38,7 +38,7 @@ export const Chart = (props: TChart) => {
); );
}; };
const ASNode = (props: TNode<TNodeData>) => { const ASNode: React.FC<TNode<TNodeData>> = (props: TNode<TNodeData>) => {
const { data } = props; const { data } = props;
const { asn, name, hasChildren, hasParents } = data; const { asn, name, hasChildren, hasParents } = data;

View file

@ -6,7 +6,7 @@ const Plus = dynamic<MeronexIcon>(() => import('@meronex/icons/fi').then(i => i.
const Minus = dynamic<MeronexIcon>(() => import('@meronex/icons/fi').then(i => i.FiMinus)); const Minus = dynamic<MeronexIcon>(() => import('@meronex/icons/fi').then(i => i.FiMinus));
const Square = dynamic<MeronexIcon>(() => import('@meronex/icons/fi').then(i => i.FiSquare)); const Square = dynamic<MeronexIcon>(() => import('@meronex/icons/fi').then(i => i.FiSquare));
export const Controls = () => { export const Controls: React.FC = () => {
const { fitView, zoomIn, zoomOut } = useZoomPanHelper(); const { fitView, zoomIn, zoomOut } = useZoomPanHelper();
return ( return (
<ButtonGroup <ButtonGroup
@ -18,7 +18,8 @@ export const Controls = () => {
isAttached isAttached
pos="absolute" pos="absolute"
variant="solid" variant="solid"
colorScheme="secondary"> colorScheme="secondary"
>
<IconButton icon={<Plus />} onClick={() => zoomIn()} aria-label="Zoom In" /> <IconButton icon={<Plus />} onClick={() => zoomIn()} aria-label="Zoom In" />
<IconButton icon={<Minus />} onClick={() => zoomOut()} aria-label="Zoom Out" /> <IconButton icon={<Minus />} onClick={() => zoomOut()} aria-label="Zoom Out" />
<IconButton icon={<Square />} onClick={() => fitView()} aria-label="Fit Nodes" /> <IconButton icon={<Square />} onClick={() => fitView()} aria-label="Fit Nodes" />

View file

@ -15,7 +15,7 @@ import { Chart } from './chart';
import type { TPath } from './types'; import type { TPath } from './types';
export const Path = (props: TPath) => { export const Path: React.FC<TPath> = (props: TPath) => {
const { device } = props; const { device } = props;
const { displayTarget } = useLGState(); const { displayTarget } = useLGState();
const { getResponse } = useLGMethods(); const { getResponse } = useLGMethods();
@ -33,7 +33,8 @@ export const Path = (props: TPath) => {
bg={bg} bg={bg}
mt={{ base: 4, lg: '' }} mt={{ base: 4, lg: '' }}
maxH={{ base: '80%', lg: '60%' }} maxH={{ base: '80%', lg: '60%' }}
maxW={{ base: '100%', lg: '80%' }}> maxW={{ base: '100%', lg: '80%' }}
>
<ModalHeader>{`Path to ${displayTarget.value}`}</ModalHeader> <ModalHeader>{`Path to ${displayTarget.value}`}</ModalHeader>
<ModalCloseButton /> <ModalCloseButton />
<ModalBody> <ModalBody>

View file

@ -8,7 +8,7 @@ export interface TPath {
device: string; device: string;
} }
export interface TNode<D extends any> extends Omit<NodeProps, 'data'> { export interface TNode<D extends unknown> extends Omit<NodeProps, 'data'> {
data: D; data: D;
} }

View file

@ -7,7 +7,7 @@ import type { BasePath } from './types';
function treeToElement(part: PathPart, len: number, index: number): FlowElement[] { function treeToElement(part: PathPart, len: number, index: number): FlowElement[] {
const x = index * 250; const x = index * 250;
const y = -(len * 10); const y = -(len * 10);
let elements = [ const elements = [
{ {
id: String(part.base), id: String(part.base),
type: 'ASNode', type: 'ASNode',
@ -55,7 +55,7 @@ export function* buildElements(base: BasePath, data: TStructuredResponse): Gener
data: { asn: base.asn, name: base.name, hasChildren: true, hasParents: false }, data: { asn: base.asn, name: base.name, hasChildren: true, hasParents: false },
}; };
for (const [i, path] of childPaths.entries()) { for (const path of childPaths) {
// path = Each unique path from origin // path = Each unique path from origin
const first = path[0]; const first = path[0];
yield { id: `e${base.asn}-${first.id}`, source: base.asn, target: first.id }; yield { id: `e${base.asn}-${first.id}`, source: base.asn, target: first.id };

View file

@ -6,7 +6,7 @@ const Check = dynamic<MeronexIcon>(() => import('@meronex/icons/fi').then(i => i
import type { TCopyButton } from './types'; import type { TCopyButton } from './types';
export const CopyButton = (props: TCopyButton) => { export const CopyButton: React.FC<TCopyButton> = (props: TCopyButton) => {
const { copyValue, ...rest } = props; const { copyValue, ...rest } = props;
const { onCopy, hasCopied } = useClipboard(copyValue); const { onCopy, hasCopied } = useClipboard(copyValue);
return ( return (
@ -18,7 +18,8 @@ export const CopyButton = (props: TCopyButton) => {
variant="ghost" variant="ghost"
onClick={onCopy} onClick={onCopy}
colorScheme="secondary" colorScheme="secondary"
{...rest}> {...rest}
>
<Icon as={hasCopied ? Check : Copy} boxSize="16px" /> <Icon as={hasCopied ? Check : Copy} boxSize="16px" />
</Button> </Button>
</Tooltip> </Tooltip>

View file

@ -22,7 +22,7 @@ function formatError(text: string, values: string[], regex: RegExp): TFormatErro
}, [] as TFormatError[]); }, [] as TFormatError[]);
} }
export const FormattedError = (props: TFormattedError) => { export const FormattedError: React.FC<TFormattedError> = (props: TFormattedError) => {
const { keywords, message } = props; const { keywords, message } = props;
const pattern = new RegExp(keywords.map(kw => `(${kw})`).join('|'), 'gi'); const pattern = new RegExp(keywords.map(kw => `(${kw})`).join('|'), 'gi');
const things = formatError(message, keywords, pattern); const things = formatError(message, keywords, pattern);

View file

@ -7,7 +7,7 @@ import { useDevice, useLGState } from '~/hooks';
import { isQueryType } from '~/types'; import { isQueryType } from '~/types';
import { Result } from './individual'; import { Result } from './individual';
export const Results = () => { export const Results: React.FC = () => {
const { queries, vrfs, web } = useConfig(); const { queries, vrfs, web } = useConfig();
const { queryLocation, queryTarget, queryType, queryVrf } = useLGState(); const { queryLocation, queryTarget, queryType, queryVrf } = useLGState();
@ -83,7 +83,8 @@ export const Results = () => {
w="100%" w="100%"
mx="auto" mx="auto"
textAlign="left" textAlign="left"
maxW={{ base: '100%', lg: '75%', xl: '50%' }}> maxW={{ base: '100%', lg: '75%', xl: '50%' }}
>
<Stack isInline align="center" justify="center" mt={4} flexWrap="wrap"> <Stack isInline align="center" justify="center" mt={4} flexWrap="wrap">
<AnimatePresence> <AnimatePresence>
{queryLocation.value && ( {queryLocation.value && (
@ -92,7 +93,8 @@ export const Results = () => {
initial={initialLeft} initial={initialLeft}
animate={animateLeft} animate={animateLeft}
exit={{ opacity: 0, x: -100 }} exit={{ opacity: 0, x: -100 }}
transition={{ duration: 0.3, delay: 0.3 }}> transition={{ duration: 0.3, delay: 0.3 }}
>
<Label <Label
bg={queryBg} bg={queryBg}
label={web.text.query_type} label={web.text.query_type}
@ -104,7 +106,8 @@ export const Results = () => {
initial={initialCenter} initial={initialCenter}
animate={animateCenter} animate={animateCenter}
exit={{ opacity: 0, scale: 0.5 }} exit={{ opacity: 0, scale: 0.5 }}
transition={{ duration: 0.3, delay: 0.3 }}> transition={{ duration: 0.3, delay: 0.3 }}
>
<Label <Label
bg={targetBg} bg={targetBg}
value={queryTarget.value} value={queryTarget.value}
@ -116,7 +119,8 @@ export const Results = () => {
initial={initialRight} initial={initialRight}
animate={animateRight} animate={animateRight}
exit={{ opacity: 0, x: 100 }} exit={{ opacity: 0, x: 100 }}
transition={{ duration: 0.3, delay: 0.3 }}> transition={{ duration: 0.3, delay: 0.3 }}
>
<Label <Label
bg={vrfBg} bg={vrfBg}
label={web.text.query_vrf} label={web.text.query_vrf}
@ -142,7 +146,8 @@ export const Results = () => {
exit={{ opacity: 0, y: 300 }} exit={{ opacity: 0, y: 300 }}
transition={{ duration: 0.3 }} transition={{ duration: 0.3 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
maxW={{ base: '100%', md: '75%' }}> maxW={{ base: '100%', md: '75%' }}
>
<Accordion allowMultiple allowToggle index={resultsComplete}> <Accordion allowMultiple allowToggle index={resultsComplete}>
<AnimatePresence> <AnimatePresence>
{queryLocation.value && {queryLocation.value &&

View file

@ -1,9 +1,12 @@
/* eslint @typescript-eslint/no-explicit-any: 0 */
/* eslint @typescript-eslint/explicit-module-boundary-types: 0 */
export function isStackError(error: any): error is Error { export function isStackError(error: any): error is Error {
return error !== null && 'message' in error; return typeof error !== 'undefined' && error !== null && 'message' in error;
} }
export function isFetchError(error: any): error is Response { export function isFetchError(error: any): error is Response {
return error !== null && 'statusText' in error; return typeof error !== 'undefined' && error !== null && 'statusText' in error;
} }
export function isLGError(error: any): error is TQueryResponse { export function isLGError(error: any): error is TQueryResponse {

View file

@ -15,7 +15,7 @@ const runtimeText = (runtime: number, text: string): string => {
return `${text} ${unit}`; return `${text} ${unit}`;
}; };
export const ResultHeader = (props: TResultHeader) => { export const ResultHeader: React.FC<TResultHeader> = (props: TResultHeader) => {
const { title, loading, isError, errorMsg, errorLevel, runtime } = props; const { title, loading, isError, errorMsg, errorLevel, runtime } = props;
const status = useColorValue('primary.500', 'primary.300'); const status = useColorValue('primary.500', 'primary.300');
@ -36,7 +36,8 @@ export const ResultHeader = (props: TResultHeader) => {
isDisabled={loading} isDisabled={loading}
label={isError ? errorMsg : label} label={isError ? errorMsg : label}
bg={isError ? warning : defaultStatus} bg={isError ? warning : defaultStatus}
color={color}> color={color}
>
<Box boxSize={6}> <Box boxSize={6}>
{loading ? ( {loading ? (
<Spinner size="sm" mr={4} color={status} /> <Spinner size="sm" mr={4} color={status} />

View file

@ -27,7 +27,9 @@ import type { TAccordionHeaderWrapper, TResult, TErrorLevels } from './types';
const AnimatedAccordionItem = motion.custom(AccordionItem); const AnimatedAccordionItem = motion.custom(AccordionItem);
const AccordionHeaderWrapper = (props: TAccordionHeaderWrapper) => { const AccordionHeaderWrapper: React.FC<TAccordionHeaderWrapper> = (
props: TAccordionHeaderWrapper,
) => {
const { hoverBg, ...rest } = props; const { hoverBg, ...rest } = props;
return ( return (
<Flex <Flex
@ -39,7 +41,7 @@ const AccordionHeaderWrapper = (props: TAccordionHeaderWrapper) => {
); );
}; };
export const Result = forwardRef<HTMLDivElement, TResult>((props, ref) => { const _Result: React.ForwardRefRenderFunction<HTMLDivElement, TResult> = (props: TResult, ref) => {
const { const {
index, index,
device, device,
@ -170,7 +172,8 @@ export const Result = forwardRef<HTMLDivElement, TResult>((props, ref) => {
css={{ css={{
'&:last-of-type': { borderBottom: 'none' }, '&:last-of-type': { borderBottom: 'none' },
'&:first-of-type': { borderTop: 'none' }, '&:first-of-type': { borderTop: 'none' },
}}> }}
>
<AccordionHeaderWrapper hoverBg="blackAlpha.50"> <AccordionHeaderWrapper hoverBg="blackAlpha.50">
<AccordionButton <AccordionButton
py={2} py={2}
@ -178,7 +181,8 @@ export const Result = forwardRef<HTMLDivElement, TResult>((props, ref) => {
_hover={{}} _hover={{}}
_focus={{}} _focus={{}}
flex="1 0 auto" flex="1 0 auto"
onClick={handleToggle}> onClick={handleToggle}
>
<ResultHeader <ResultHeader
isError={isError} isError={isError}
loading={isLoading} loading={isLoading}
@ -213,7 +217,8 @@ export const Result = forwardRef<HTMLDivElement, TResult>((props, ref) => {
}, },
'-ms-overflow-style': { display: 'none' }, '-ms-overflow-style': { display: 'none' },
}}> }}
>
<Box> <Box>
<Flex direction="column" flex="1 0 auto" maxW={error ? '100%' : undefined}> <Flex direction="column" flex="1 0 auto" maxW={error ? '100%' : undefined}>
{!isError && typeof data !== 'undefined' ? ( {!isError && typeof data !== 'undefined' ? (
@ -246,7 +251,8 @@ export const Result = forwardRef<HTMLDivElement, TResult>((props, ref) => {
mt={2} mt={2}
spacing={1} spacing={1}
flex="1 0 auto" flex="1 0 auto"
justifyContent={{ base: 'flex-start', lg: 'flex-end' }}> justifyContent={{ base: 'flex-start', lg: 'flex-end' }}
>
<If c={cache.show_text && !isError && isCached}> <If c={cache.show_text && !isError && isCached}>
<If c={!isMobile}> <If c={!isMobile}>
<Countdown timeout={cache.timeout} text={web.text.cache_prefix} /> <Countdown timeout={cache.timeout} text={web.text.cache_prefix} />
@ -265,4 +271,6 @@ export const Result = forwardRef<HTMLDivElement, TResult>((props, ref) => {
</AccordionPanel> </AccordionPanel>
</AnimatedAccordionItem> </AnimatedAccordionItem>
); );
}); };
export const Result = forwardRef<HTMLDivElement, TResult>(_Result);

View file

@ -6,7 +6,10 @@ import type { TRequeryButton } from './types';
const Repeat = dynamic<MeronexIcon>(() => import('@meronex/icons/fi').then(i => i.FiRepeat)); const Repeat = dynamic<MeronexIcon>(() => import('@meronex/icons/fi').then(i => i.FiRepeat));
export const RequeryButton = forwardRef<HTMLButtonElement, TRequeryButton>((props, ref) => { const _RequeryButton: React.ForwardRefRenderFunction<HTMLButtonElement, TRequeryButton> = (
props: TRequeryButton,
ref,
) => {
const { requery, ...rest } = props; const { requery, ...rest } = props;
return ( return (
@ -20,9 +23,12 @@ export const RequeryButton = forwardRef<HTMLButtonElement, TRequeryButton>((prop
variant="ghost" variant="ghost"
onClick={requery as TRequeryButton['onClick']} onClick={requery as TRequeryButton['onClick']}
colorScheme="secondary" colorScheme="secondary"
{...rest}> {...rest}
>
<Icon as={Repeat} boxSize="16px" /> <Icon as={Repeat} boxSize="16px" />
</Button> </Button>
</Tooltip> </Tooltip>
); );
}); };
export const RequeryButton = forwardRef<HTMLButtonElement, TRequeryButton>(_RequeryButton);

View file

@ -21,11 +21,11 @@ import type { TSelectOption } from '~/types';
import type { TSelectBase, TSelectContext, TReactSelectChakra } from './types'; import type { TSelectBase, TSelectContext, TReactSelectChakra } from './types';
const SelectContext = createContext<TSelectContext>(Object()); const SelectContext = createContext<TSelectContext>(Object());
export const useSelectContext = () => useContext(SelectContext); export const useSelectContext = (): TSelectContext => useContext(SelectContext);
const ReactSelectChakra = chakra<typeof ReactSelect, TReactSelectChakra>(ReactSelect); const ReactSelectChakra = chakra<typeof ReactSelect, TReactSelectChakra>(ReactSelect);
export const Select = (props: TSelectBase) => { export const Select: React.FC<TSelectBase> = (props: TSelectBase) => {
const { options, multi, onSelect, isError = false, ...rest } = props; const { options, multi, onSelect, isError = false, ...rest } = props;
const { isOpen, onOpen, onClose } = useDisclosure(); const { isOpen, onOpen, onClose } = useDisclosure();
@ -49,8 +49,8 @@ export const Select = (props: TSelectBase) => {
const multiValue = useMultiValueStyle({ colorMode }); const multiValue = useMultiValueStyle({ colorMode });
const multiValueLabel = useMultiValueLabelStyle({ colorMode }); const multiValueLabel = useMultiValueLabelStyle({ colorMode });
const multiValueRemove = useMultiValueRemoveStyle({ colorMode }); const multiValueRemove = useMultiValueRemoveStyle({ colorMode });
const menuPortal = useMenuPortal({ colorMode }); const menuPortal = useMenuPortal();
const rsTheme = useRSTheme({ colorMode }); const rsTheme = useRSTheme();
return ( return (
<SelectContext.Provider value={selectContext}> <SelectContext.Provider value={selectContext}>

View file

@ -6,16 +6,14 @@ import { useColorValue, useColorToken, useMobile } from '~/context';
import { useSelectContext } from './select'; import { useSelectContext } from './select';
import type { import type {
TControl,
TIndicator,
TMenu, TMenu,
TMenuList,
TMultiValueState,
TOption, TOption,
TPlaceholder,
TStyles, TStyles,
TControl,
TRSTheme, TRSTheme,
TMultiValue, TMultiValue,
TRSThemeCallback,
TRSStyleCallback,
} from './types'; } from './types';
export const useControlStyle = (base: TStyles, state: TControl): TStyles => { export const useControlStyle = (base: TStyles, state: TControl): TStyles => {
@ -59,7 +57,7 @@ export const useMenuStyle = (base: TStyles, _: TMenu): TStyles => {
return useMemo(() => mergeWith({}, base, styles), [colorMode, isOpen]); return useMemo(() => mergeWith({}, base, styles), [colorMode, isOpen]);
}; };
export const useMenuListStyle = (base: TStyles, state: TMenuList): TStyles => { export const useMenuListStyle = (base: TStyles): TStyles => {
const { colorMode, isOpen } = useSelectContext(); const { colorMode, isOpen } = useSelectContext();
const scrollbarTrack = useColorToken('colors', 'blackAlpha.50', 'whiteAlpha.50'); const scrollbarTrack = useColorToken('colors', 'blackAlpha.50', 'whiteAlpha.50');
@ -123,59 +121,51 @@ export const useOptionStyle = (base: TStyles, state: TOption): TStyles => {
]); ]);
}; };
export const useIndicatorSeparatorStyle = (base: TStyles, state: TIndicator): TStyles => { export const useIndicatorSeparatorStyle = (base: TStyles): TStyles => {
const { colorMode } = useSelectContext(); const { colorMode } = useSelectContext();
const backgroundColor = useColorToken('colors', 'whiteAlpha.700', 'gray.600'); const backgroundColor = useColorToken('colors', 'whiteAlpha.700', 'gray.600');
const styles = { backgroundColor }; const styles = { backgroundColor };
return useMemo(() => mergeWith({}, base, styles), [colorMode]); return useMemo(() => mergeWith({}, base, styles), [colorMode]);
}; };
export const usePlaceholderStyle = (base: TStyles, state: TPlaceholder): TStyles => { export const usePlaceholderStyle = (base: TStyles): TStyles => {
const { colorMode } = useSelectContext(); const { colorMode } = useSelectContext();
const color = useColorToken('colors', 'gray.600', 'whiteAlpha.700'); const color = useColorToken('colors', 'gray.600', 'whiteAlpha.700');
const fontSize = useToken('fontSizes', 'lg'); const fontSize = useToken('fontSizes', 'lg');
return useMemo(() => mergeWith({}, base, { color, fontSize }), [colorMode]); return useMemo(() => mergeWith({}, base, { color, fontSize }), [colorMode]);
}; };
export const useSingleValueStyle = (props: TStyles) => { export const useSingleValueStyle = (): TRSStyleCallback => {
const { colorMode } = useSelectContext(); const { colorMode } = useSelectContext();
const color = useColorValue('black', 'whiteAlpha.800'); const color = useColorValue('black', 'whiteAlpha.800');
const fontSize = useToken('fontSizes', 'lg'); const fontSize = useToken('fontSizes', 'lg');
const styles = { color, fontSize }; const styles = { color, fontSize };
return useCallback((base: TStyles, state: TMultiValueState) => mergeWith({}, base, styles), [ return useCallback((base: TStyles) => mergeWith({}, base, styles), [color, colorMode]);
color,
colorMode,
]);
}; };
export const useMultiValueStyle = (props: TMultiValue) => { export const useMultiValueStyle = (props: TMultiValue): TRSStyleCallback => {
const { colorMode } = props; const { colorMode } = props;
const backgroundColor = useColorToken('colors', 'primary.500', 'primary.300'); const backgroundColor = useColorToken('colors', 'primary.500', 'primary.300');
const color = useOpposingColor(backgroundColor); const color = useOpposingColor(backgroundColor);
const styles = { backgroundColor, color }; const styles = { backgroundColor, color };
return useCallback((base: TStyles, state: TMultiValueState) => mergeWith({}, base, styles), [ return useCallback((base: TStyles) => mergeWith({}, base, styles), [backgroundColor, colorMode]);
backgroundColor,
colorMode,
]);
}; };
export const useMultiValueLabelStyle = (props: TMultiValue) => { export const useMultiValueLabelStyle = (props: TMultiValue): TRSStyleCallback => {
const { colorMode } = props; const { colorMode } = props;
const backgroundColor = useColorToken('colors', 'primary.500', 'primary.300'); const backgroundColor = useColorToken('colors', 'primary.500', 'primary.300');
const color = useOpposingColor(backgroundColor); const color = useOpposingColor(backgroundColor);
const styles = { color }; const styles = { color };
return useCallback((base: TStyles, state: TMultiValueState) => mergeWith({}, base, styles), [ return useCallback((base: TStyles) => mergeWith({}, base, styles), [colorMode]);
colorMode,
]);
}; };
export const useMultiValueRemoveStyle = (props: TMultiValue) => { export const useMultiValueRemoveStyle = (props: TMultiValue): TRSStyleCallback => {
const { colorMode } = props; const { colorMode } = props;
const backgroundColor = useColorToken('colors', 'primary.500', 'primary.300'); const backgroundColor = useColorToken('colors', 'primary.500', 'primary.300');
@ -185,22 +175,18 @@ export const useMultiValueRemoveStyle = (props: TMultiValue) => {
color, color,
'&:hover': { backgroundColor: 'inherit', color, opacity: 0.7 }, '&:hover': { backgroundColor: 'inherit', color, opacity: 0.7 },
}; };
return useCallback((base: TStyles, state: TMultiValueState) => mergeWith({}, base, styles), [ return useCallback((base: TStyles) => mergeWith({}, base, styles), [colorMode]);
colorMode,
]);
}; };
export const useRSTheme = (props: TMultiValue) => { export const useRSTheme = (): TRSThemeCallback => {
const borderRadius = useToken('radii', 'md'); const borderRadius = useToken('radii', 'md');
return useCallback((t: TRSTheme): TRSTheme => ({ ...t, borderRadius }), []); return useCallback((t: TRSTheme): TRSTheme => ({ ...t, borderRadius }), []);
}; };
export const useMenuPortal = (props: TMultiValue) => { export const useMenuPortal = (): TRSStyleCallback => {
const isMobile = useMobile(); const isMobile = useMobile();
const styles = { const styles = {
zIndex: isMobile ? 1500 : 1, zIndex: isMobile ? 1500 : 1,
}; };
return useCallback((base: TStyles, state: TMultiValueState) => mergeWith({}, base, styles), [ return useCallback((base: TStyles) => mergeWith({}, base, styles), [isMobile]);
isMobile,
]);
}; };

View file

@ -1,3 +1,6 @@
/* eslint @typescript-eslint/no-explicit-any: 0 */
/* eslint @typescript-eslint/explicit-module-boundary-types: 0 */
import type { import type {
Props as IReactSelect, Props as IReactSelect,
ControlProps, ControlProps,
@ -8,6 +11,7 @@ import type {
IndicatorProps, IndicatorProps,
Theme as RSTheme, Theme as RSTheme,
PlaceholderProps, PlaceholderProps,
Styles as RSStyles,
} from 'react-select'; } from 'react-select';
import type { BoxProps } from '@chakra-ui/react'; import type { BoxProps } from '@chakra-ui/react';
import type { Theme, TSelectOption, TSelectOptionMulti, TSelectOptionGroup } from '~/types'; import type { Theme, TSelectOption, TSelectOptionMulti, TSelectOptionGroup } from '~/types';
@ -70,4 +74,8 @@ export type TPlaceholder = PlaceholderProps<TOptions, false>;
export type TMultiValue = Pick<TSelectContext, 'colorMode'>; export type TMultiValue = Pick<TSelectContext, 'colorMode'>;
export type TRSStyleCallback = (base: RSStyles) => RSStyles;
export type TRSThemeCallback = (theme: TRSTheme) => TRSTheme;
export type { Styles as TStyles } from 'react-select'; export type { Styles as TStyles } from 'react-select';

View file

@ -22,30 +22,32 @@ import { useLGState, useLGMethods } from '~/hooks';
import type { IconButtonProps } from '@chakra-ui/react'; import type { IconButtonProps } from '@chakra-ui/react';
import type { TSubmitButton, TRSubmitButton } from './types'; import type { TSubmitButton, TRSubmitButton } from './types';
const SubmitIcon = forwardRef<HTMLButtonElement, Omit<IconButtonProps, 'aria-label'>>( const _SubmitIcon: React.ForwardRefRenderFunction<
(props, ref) => { HTMLButtonElement,
const { isLoading, ...rest } = props; Omit<IconButtonProps, 'aria-label'>
return ( > = (props: Omit<IconButtonProps, 'aria-label'>, ref) => {
<IconButton const { isLoading, ...rest } = props;
ref={ref} return (
size="lg" <IconButton
width={16} ref={ref}
type="submit" size="lg"
icon={<FiSearch />} width={16}
title="Submit Query" type="submit"
colorScheme="primary" icon={<FiSearch />}
isLoading={isLoading} title="Submit Query"
aria-label="Submit Query" colorScheme="primary"
{...rest} isLoading={isLoading}
/> aria-label="Submit Query"
); {...rest}
}, />
); );
};
const SubmitIcon = forwardRef<HTMLButtonElement, Omit<IconButtonProps, 'aria-label'>>(_SubmitIcon);
/** /**
* Mobile Submit Button * Mobile Submit Button
*/ */
const MSubmitButton = (props: TRSubmitButton) => { const MSubmitButton: React.FC<TRSubmitButton> = (props: TRSubmitButton) => {
const { children, isOpen, onClose, onChange } = props; const { children, isOpen, onClose, onChange } = props;
const bg = useColorValue('white', 'gray.900'); const bg = useColorValue('white', 'gray.900');
return ( return (
@ -58,7 +60,8 @@ const MSubmitButton = (props: TRSubmitButton) => {
onClose={onClose} onClose={onClose}
closeOnEsc={false} closeOnEsc={false}
closeOnOverlayClick={false} closeOnOverlayClick={false}
motionPreset="slideInBottom"> motionPreset="slideInBottom"
>
<ModalOverlay /> <ModalOverlay />
<ModalContent bg={bg}> <ModalContent bg={bg}>
<ModalCloseButton /> <ModalCloseButton />
@ -74,7 +77,7 @@ const MSubmitButton = (props: TRSubmitButton) => {
/** /**
* Desktop Submit Button * Desktop Submit Button
*/ */
const DSubmitButton = (props: TRSubmitButton) => { const DSubmitButton: React.FC<TRSubmitButton> = (props: TRSubmitButton) => {
const { children, isOpen, onClose, onChange } = props; const { children, isOpen, onClose, onChange } = props;
const bg = useColorValue('white', 'gray.900'); const bg = useColorValue('white', 'gray.900');
return ( return (
@ -91,7 +94,7 @@ const DSubmitButton = (props: TRSubmitButton) => {
); );
}; };
export const SubmitButton = (props: TSubmitButton) => { export const SubmitButton: React.FC<TSubmitButton> = (props: TSubmitButton) => {
const { handleChange } = props; const { handleChange } = props;
const isMobile = useMobile(); const isMobile = useMobile();
const { resolvedIsOpen, btnLoading } = useLGState(); const { resolvedIsOpen, btnLoading } = useLGState();

View file

@ -2,7 +2,7 @@ import { Box } from '@chakra-ui/react';
import type { BoxProps } from '@chakra-ui/react'; import type { BoxProps } from '@chakra-ui/react';
export const TableBody = (props: BoxProps) => ( export const TableBody: React.FC<BoxProps> = (props: BoxProps) => (
<Box <Box
as="tbody" as="tbody"
overflowY="scroll" overflowY="scroll"

View file

@ -2,6 +2,6 @@ import { IconButton } from '@chakra-ui/react';
import type { TTableIconButton } from './types'; import type { TTableIconButton } from './types';
export const TableIconButton = (props: TTableIconButton) => ( export const TableIconButton: React.FC<TTableIconButton> = (props: TTableIconButton) => (
<IconButton size="sm" borderWidth={1} {...props} aria-label="Table Icon Button" /> <IconButton size="sm" borderWidth={1} {...props} aria-label="Table Icon Button" />
); );

View file

@ -3,7 +3,7 @@ import { useColorValue } from '~/context';
import type { TTableCell } from './types'; import type { TTableCell } from './types';
export const TableCell = (props: TTableCell) => { export const TableCell: React.FC<TTableCell> = (props: TTableCell) => {
const { bordersVertical = [false, 0], align, ...rest } = props; const { bordersVertical = [false, 0], align, ...rest } = props;
const [doVerticalBorders, index] = bordersVertical; const [doVerticalBorders, index] = bordersVertical;
const borderLeftColor = useColorValue('blackAlpha.100', 'whiteAlpha.100'); const borderLeftColor = useColorValue('blackAlpha.100', 'whiteAlpha.100');

View file

@ -3,7 +3,7 @@ import { useColorValue } from '~/context';
import type { BoxProps } from '@chakra-ui/react'; import type { BoxProps } from '@chakra-ui/react';
export const TableHead = (props: BoxProps) => { export const TableHead: React.FC<BoxProps> = (props: BoxProps) => {
const bg = useColorValue('blackAlpha.100', 'whiteAlpha.100'); const bg = useColorValue('blackAlpha.100', 'whiteAlpha.100');
return <Box as="thead" overflowX="hidden" overflowY="auto" bg={bg} {...props} />; return <Box as="thead" overflowX="hidden" overflowY="auto" bg={bg} {...props} />;
}; };

View file

@ -1,3 +1,6 @@
// This rule isn't needed because react-table does this for us, for better or worse.
/* eslint react/jsx-key: 0 */
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import { Flex, Icon, Text } from '@chakra-ui/react'; import { Flex, Icon, Text } from '@chakra-ui/react';
import { usePagination, useSortBy, useTable } from 'react-table'; import { usePagination, useSortBy, useTable } from 'react-table';
@ -34,7 +37,7 @@ const DoubleChevronLeft = dynamic<MeronexIcon>(() =>
import('@meronex/icons/fi').then(i => i.FiChevronsLeft), import('@meronex/icons/fi').then(i => i.FiChevronsLeft),
); );
export function Table(props: TTable) { export const Table: React.FC<TTable> = (props: TTable) => {
const { const {
data, data,
columns, columns,
@ -55,7 +58,7 @@ export function Table(props: TTable) {
maxWidth: 300, maxWidth: 300,
}; };
let hiddenColumns = [] as string[]; const hiddenColumns = [] as string[];
for (const col of columns) { for (const col of columns) {
if (col.hidden) { if (col.hidden) {
@ -102,7 +105,8 @@ export function Table(props: TTable) {
as="th" as="th"
align={column.align} align={column.align}
{...column.getHeaderProps()} {...column.getHeaderProps()}
{...column.getSortByToggleProps()}> {...column.getSortByToggleProps()}
>
<Text fontSize="sm" fontWeight="bold" display="inline-block"> <Text fontSize="sm" fontWeight="bold" display="inline-block">
{column.render('Header')} {column.render('Header')}
</Text> </Text>
@ -130,14 +134,16 @@ export function Table(props: TTable) {
highlightBg={rowHighlightBg} highlightBg={rowHighlightBg}
doHorizontalBorders={bordersHorizontal} doHorizontalBorders={bordersHorizontal}
highlight={row.values[rowHighlightProp ?? ''] ?? false} highlight={row.values[rowHighlightProp ?? ''] ?? false}
{...row.getRowProps()}> {...row.getRowProps()}
>
{row.cells.map((cell, i) => { {row.cells.map((cell, i) => {
const { column, row, value } = cell as TCellRender; const { column, row, value } = cell as TCellRender;
return ( return (
<TableCell <TableCell
align={cell.column.align} align={cell.column.align}
bordersVertical={[bordersVertical, i]} bordersVertical={[bordersVertical, i]}
{...cell.getCellProps()}> {...cell.getCellProps()}
>
{typeof Cell !== 'undefined' ? ( {typeof Cell !== 'undefined' ? (
<Cell column={column} row={row} value={value} /> <Cell column={column} row={row} value={value} />
) : ( ) : (
@ -199,4 +205,4 @@ export function Table(props: TTable) {
</CardFooter> </CardFooter>
</CardBody> </CardBody>
); );
} };

View file

@ -1,7 +1,7 @@
import { Select } from '@chakra-ui/react'; import { Select } from '@chakra-ui/react';
import { SelectProps } from '@chakra-ui/react'; import { SelectProps } from '@chakra-ui/react';
export const TableSelectShow = (props: SelectProps) => { export const TableSelectShow: React.FC<SelectProps> = (props: SelectProps) => {
const { value, ...rest } = props; const { value, ...rest } = props;
return ( return (
<Select size="sm" {...rest}> <Select size="sm" {...rest}>

View file

@ -4,7 +4,7 @@ import { useOpposingColor } from '~/hooks';
import type { TTableRow } from './types'; import type { TTableRow } from './types';
export const TableRow = (props: TTableRow) => { export const TableRow: React.FC<TTableRow> = (props: TTableRow) => {
const { const {
index = 0, index = 0,
doStripe = false, doStripe = false,

View file

@ -3,7 +3,7 @@ import { useColorValue } from '~/context';
import type { BoxProps } from '@chakra-ui/react'; import type { BoxProps } from '@chakra-ui/react';
export const TableMain = (props: BoxProps) => { export const TableMain: React.FC<BoxProps> = (props: BoxProps) => {
const scrollbar = useColorValue('blackAlpha.300', 'whiteAlpha.300'); const scrollbar = useColorValue('blackAlpha.300', 'whiteAlpha.300');
const scrollbarHover = useColorValue('blackAlpha.400', 'whiteAlpha.400'); const scrollbarHover = useColorValue('blackAlpha.400', 'whiteAlpha.400');
const scrollbarBg = useColorValue('blackAlpha.50', 'whiteAlpha.50'); const scrollbarBg = useColorValue('blackAlpha.50', 'whiteAlpha.50');

View file

@ -1,6 +1,6 @@
import type { TIf } from './types'; import type { TIf } from './types';
export const If = (props: TIf) => { export const If = (props: React.PropsWithChildren<TIf>): React.ReactNode | null => {
const { c, render, children, ...rest } = props; const { c, children } = props;
return c ? (render ? render(rest) : children) : null; return c ? children : null;
}; };

View file

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

View file

@ -16,7 +16,7 @@ const HyperglassContext = createContext<IConfig>(Object());
const queryClient = new QueryClient(); const queryClient = new QueryClient();
export const HyperglassProvider = (props: THyperglassProvider) => { export const HyperglassProvider: React.FC<THyperglassProvider> = (props: THyperglassProvider) => {
const { config, children } = props; const { config, children } = props;
const value = useMemo(() => config, []); const value = useMemo(() => config, []);
const userTheme = value && makeTheme(value.web.theme, value.web.theme.default_color_mode); const userTheme = value && makeTheme(value.web.theme, value.web.theme.default_color_mode);

View file

@ -11,12 +11,6 @@ export interface TGlobalState {
formData: TFormData; formData: TFormData;
} }
interface TGlobalStateFunctions {
resetForm(): void;
}
// export type TUseGlobalState = State<TGlobalState> & TGlobalStateFunctions;
export interface TUseGlobalState { export interface TUseGlobalState {
isSubmitting: State<TGlobalState['isSubmitting']>; isSubmitting: State<TGlobalState['isSubmitting']>;
formData: State<TGlobalState['formData']>; formData: State<TGlobalState['formData']>;

View file

@ -1,5 +1,6 @@
import { useQuery } from 'react-query'; import { useQuery } from 'react-query';
import type { QueryObserverResult } from 'react-query';
import type { TASNQuery } from '~/types'; import type { TASNQuery } from '~/types';
import type { TUseASNDetailFn } from './types'; import type { TUseASNDetailFn } from './types';
@ -9,6 +10,7 @@ async function query(ctx: TUseASNDetailFn): Promise<TASNQuery> {
mode: 'cors', mode: 'cors',
method: 'POST', method: 'POST',
headers: { 'content-type': 'application/json' }, headers: { 'content-type': 'application/json' },
/* eslint no-useless-escape: 0 */
body: JSON.stringify({ query: `{ asn(asn:\"${asn}\"){ organization { orgName } } }` }), body: JSON.stringify({ query: `{ asn(asn:\"${asn}\"){ organization { orgName } } }` }),
}); });
return await res.json(); return await res.json();
@ -18,7 +20,7 @@ async function query(ctx: TUseASNDetailFn): Promise<TASNQuery> {
* Query the Caida AS Rank API to get an ASN's organization name for the AS Path component. * Query the Caida AS Rank API to get an ASN's organization name for the AS Path component.
* @see https://api.asrank.caida.org/v2/docs * @see https://api.asrank.caida.org/v2/docs
*/ */
export function useASNDetail(asn: string) { export function useASNDetail(asn: string): QueryObserverResult<TASNQuery> {
return useQuery(asn, query, { return useQuery(asn, query, {
refetchOnWindowFocus: false, refetchOnWindowFocus: false,
refetchInterval: false, refetchInterval: false,

View file

@ -3,7 +3,7 @@ import { useMemo } from 'react';
/** /**
* Track the state of a boolean and return values based on its state. * Track the state of a boolean and return values based on its state.
*/ */
export function useBooleanValue<T extends any, F extends any>( export function useBooleanValue<T extends unknown, F extends unknown>(
status: boolean, status: boolean,
ifTrue: T, ifTrue: T,
ifFalse: F, ifFalse: F,

View file

@ -2,6 +2,7 @@ import { useQuery } from 'react-query';
import { useConfig } from '~/context'; import { useConfig } from '~/context';
import { fetchWithTimeout } from '~/util'; import { fetchWithTimeout } from '~/util';
import type { QueryObserverResult } from 'react-query';
import type { DnsOverHttps } from '~/types'; import type { DnsOverHttps } from '~/types';
import type { TUseDNSQueryFn } from './types'; import type { TUseDNSQueryFn } from './types';
@ -46,7 +47,7 @@ export function useDNSQuery(
* Address family, e.g. IPv4 or IPv6. * Address family, e.g. IPv4 or IPv6.
*/ */
family: 4 | 6, family: 4 | 6,
) { ): QueryObserverResult<DnsOverHttps.Response> {
const { cache, web } = useConfig(); const { cache, web } = useConfig();
return useQuery([web.dns_provider.url, { target, family }], dnsQuery, { return useQuery([web.dns_provider.url, { target, family }], dnsQuery, {
cacheTime: cache.timeout * 1000, cacheTime: cache.timeout * 1000,

View file

@ -2,13 +2,14 @@ import { useQuery } from 'react-query';
import { useConfig } from '~/context'; import { useConfig } from '~/context';
import { fetchWithTimeout } from '~/util'; import { fetchWithTimeout } from '~/util';
import type { QueryObserverResult } from 'react-query';
import type { TFormQuery } from '~/types'; import type { TFormQuery } from '~/types';
import type { TUseLGQueryFn } from './types'; import type { TUseLGQueryFn } from './types';
/** /**
* Custom hook handle submission of a query to the hyperglass backend. * Custom hook handle submission of a query to the hyperglass backend.
*/ */
export function useLGQuery(query: TFormQuery) { export function useLGQuery(query: TFormQuery): QueryObserverResult<TQueryResponse> {
const { request_timeout, cache } = useConfig(); const { request_timeout, cache } = useConfig();
const controller = new AbortController(); const controller = new AbortController();

View file

@ -8,7 +8,7 @@ import type { TOpposingOptions } from './types';
* Parse the color string to determine if it's a Chakra UI theme key, and determine if the * Parse the color string to determine if it's a Chakra UI theme key, and determine if the
* opposing color should be black or white. * opposing color should be black or white.
*/ */
export function useIsDark(color: string) { export function useIsDark(color: string): boolean {
const theme = useTheme(); const theme = useTheme();
if (typeof color === 'string' && color.match(/[a-zA-Z]+\.[a-zA-Z0-9]+/g)) { if (typeof color === 'string' && color.match(/[a-zA-Z]+\.[a-zA-Z0-9]+/g)) {
color = getColor(theme, color, color); color = getColor(theme, color, color);

View file

@ -6,6 +6,6 @@ import type { UseStrfArgs } from './types';
/** /**
* Format a string with variables, like Python's string.format() * Format a string with variables, like Python's string.format()
*/ */
export function useStrf(str: string, fmt: UseStrfArgs, ...deps: any[]): string { export function useStrf(str: string, fmt: UseStrfArgs, ...deps: unknown[]): string {
return useMemo(() => format(str, fmt), deps); return useMemo(() => format(str, fmt), deps);
} }

View file

@ -43,7 +43,7 @@ function formatTime(val: number): string {
export function useTableToString( export function useTableToString(
target: string, target: string,
data: TQueryResponse | undefined, data: TQueryResponse | undefined,
...deps: any ...deps: unknown[]
): () => string { ): () => string {
const { web, parsed_data_fields, messages } = useConfig(); const { web, parsed_data_fields, messages } = useConfig();
@ -81,7 +81,7 @@ export function useTableToString(
let result = messages.no_output; let result = messages.no_output;
try { try {
if (typeof data !== 'undefined' && isStructuredOutput(data)) { if (typeof data !== 'undefined' && isStructuredOutput(data)) {
let tableStringParts = [`Routes For: ${target}`, `Timestamp: ${data.timestamp} UTC`]; const tableStringParts = [`Routes For: ${target}`, `Timestamp: ${data.timestamp} UTC`];
for (const route of data.output.routes) { for (const route of data.output.routes) {
for (const field of parsed_data_fields) { for (const field of parsed_data_fields) {
const [header, accessor, align] = field; const [header, accessor, align] = field;

View file

@ -50,20 +50,27 @@
"@types/react-table": "^7.0.25", "@types/react-table": "^7.0.25",
"@types/string-format": "^2.0.0", "@types/string-format": "^2.0.0",
"@types/yup": "^0.29.9", "@types/yup": "^0.29.9",
"@typescript-eslint/eslint-plugin": "^2.24.0", "@typescript-eslint/eslint-plugin": "^4.11.1",
"@typescript-eslint/parser": "^2.24.0", "@typescript-eslint/parser": "^4.11.1",
"@upstatement/eslint-config": "^0.4.3", "@upstatement/eslint-config": "^0.4.3",
"@upstatement/prettier-config": "^0.3.0", "@upstatement/prettier-config": "^0.3.0",
"babel-eslint": "^10.1.0", "babel-eslint": "^10.1.0",
"eslint": "^6.8.0", "eslint": "^6.8.0",
"eslint-config-prettier": "^7.1.0",
"eslint-config-react-app": "^5.2.0", "eslint-config-react-app": "^5.2.0",
"eslint-import-resolver-typescript": "^2.3.0", "eslint-import-resolver-typescript": "^2.3.0",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jest": "^24.1.3",
"eslint-plugin-json": "^2.1.2",
"eslint-plugin-jsx-a11y": "^6.2.3", "eslint-plugin-jsx-a11y": "^6.2.3",
"eslint-plugin-react": "^7.19.0", "eslint-plugin-prettier": "^3.3.0",
"eslint-plugin-react-hooks": "^2.3.0", "eslint-plugin-react": "^7.22.0",
"eslint-plugin-react-hooks": "^4.2.0",
"express": "^4.17.1", "express": "^4.17.1",
"http-proxy-middleware": "0.20.0", "http-proxy-middleware": "0.20.0",
"prettier": "2.0", "onchange": "^7.1.0",
"prettier": "^2.2.1",
"prettier-eslint": "^12.0.0",
"typescript": "^4.0.3" "typescript": "^4.0.3"
} }
} }

View file

@ -2,21 +2,21 @@ import Head from 'next/head';
import { HyperglassProvider } from '~/context'; import { HyperglassProvider } from '~/context';
import { IConfig } from '~/types'; import { IConfig } from '~/types';
import type { AppProps, AppInitialProps, AppContext } from 'next/app';
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === 'development') {
require('@hookstate/devtools'); require('@hookstate/devtools');
} }
import type { AppProps, AppInitialProps } from 'next/app'; type TApp = { config: IConfig };
type TAppProps = AppProps & AppInitialProps; type GetInitialPropsReturn<IP> = AppProps & AppInitialProps & { appProps: IP };
interface TApp extends TAppProps { type Temp<IP> = React.FC<GetInitialPropsReturn<IP>> & {
appProps: { config: IConfig }; getInitialProps(c?: AppContext): Promise<{ appProps: IP }>;
} };
type TAppInitial = Pick<TApp, 'appProps'>; const App: Temp<TApp> = (props: GetInitialPropsReturn<TApp>) => {
const App = (props: TApp) => {
const { Component, pageProps, appProps } = props; const { Component, pageProps, appProps } = props;
const { config } = appProps; const { config } = appProps;
@ -42,7 +42,7 @@ const App = (props: TApp) => {
); );
}; };
App.getInitialProps = async (): Promise<TAppInitial> => { App.getInitialProps = async function getInitialProps() {
const config = (process.env._HYPERGLASS_CONFIG_ as unknown) as IConfig; const config = (process.env._HYPERGLASS_CONFIG_ as unknown) as IConfig;
return { appProps: { config } }; return { appProps: { config } };
}; };

View file

@ -1,13 +1,13 @@
import Document, { Html, Head, Main, NextScript } from 'next/document'; import Document, { Html, Head, Main, NextScript } from 'next/document';
import type { DocumentContext } from 'next/document'; import type { DocumentContext, DocumentInitialProps } from 'next/document';
class MyDocument extends Document { class MyDocument extends Document {
static async getInitialProps(ctx: DocumentContext) { static async getInitialProps(ctx: DocumentContext): Promise<DocumentInitialProps> {
const initialProps = await Document.getInitialProps(ctx); const initialProps = await Document.getInitialProps(ctx);
return { ...initialProps }; return { ...initialProps };
} }
render() { render(): JSX.Element {
return ( return (
<Html lang="en"> <Html lang="en">
<Head> <Head>

View file

@ -12,14 +12,14 @@ import {
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { inRange } from 'lodash'; import { inRange } from 'lodash';
import type { NextPageContext } from 'next'; import type { NextPage, NextPageContext } from 'next';
interface TError { interface TError {
status: string; status: string;
code: number; code: number;
} }
const ErrorContent = (props: TError) => { const ErrorContent: React.FC<TError> = (props: TError) => {
const { status, code } = props; const { status, code } = props;
const router = useRouter(); const router = useRouter();
@ -52,12 +52,13 @@ const ErrorContent = (props: TError) => {
alignItems="center" alignItems="center"
flexDirection="column" flexDirection="column"
justifyContent="start" justifyContent="start"
mt={{ base: '50%', xl: '25%' }}> mt={{ base: '50%', xl: '25%' }}
>
<Heading mb={4} as="h1" fontSize="2xl"> <Heading mb={4} as="h1" fontSize="2xl">
<Text as="span" color={errorColor[baseCode]}> <Text as="span" color={errorColor[baseCode]}>
{status} {status}
</Text> </Text>
{code === 404 && <Text as="span"> isn't a thing...</Text>} {code === 404 && <Text as="span">{` isn't a thing...`}</Text>}
</Heading> </Heading>
<Button variant="outline" onClick={handleClick} colorScheme={colorScheme[baseCode]}> <Button variant="outline" onClick={handleClick} colorScheme={colorScheme[baseCode]}>
Home Home
@ -67,7 +68,7 @@ const ErrorContent = (props: TError) => {
); );
}; };
const ErrorPage = (props: TError) => { const ErrorPage: NextPage<TError> = (props: TError) => {
const { status, code } = props; const { status, code } = props;
return ( return (
<ThemeProvider theme={theme}> <ThemeProvider theme={theme}>

View file

@ -13,7 +13,7 @@ interface TIndex {
favicons: FaviconComponent[]; favicons: FaviconComponent[];
} }
const Index = (props: TIndex) => { const Index: React.FC<TIndex> = (props: TIndex) => {
const { favicons } = props; const { favicons } = props;
return ( return (
<> <>
@ -32,7 +32,8 @@ const Index = (props: TIndex) => {
export const getStaticProps: GetStaticProps<TIndex> = async () => { export const getStaticProps: GetStaticProps<TIndex> = async () => {
const faviconConfig = (process.env._HYPERGLASS_FAVICONS_ as unknown) as Favicon[]; const faviconConfig = (process.env._HYPERGLASS_FAVICONS_ as unknown) as Favicon[];
const favicons = faviconConfig.map(icon => { const favicons = faviconConfig.map(icon => {
let { image_format, dimensions, prefix, rel } = icon; const { image_format, dimensions, prefix } = icon;
let { rel } = icon;
if (rel === null) { if (rel === null) {
rel = ''; rel = '';
} }

View file

@ -1 +0,0 @@
module.exports = require("@upstatement/prettier-config");

View file

@ -1,40 +0,0 @@
// Insert this script in your index.html right after the <body> tag.
// This will help to prevent a flash if dark mode is the default.
(function() {
// Change these if you use something different in your hook.
var storageKey = 'darkMode';
var classNameDark = 'dark-mode';
var classNameLight = 'light-mode';
function setClassOnDocumentBody(darkMode) {
document.body.classList.add(darkMode ? classNameDark : classNameLight);
document.body.classList.remove(darkMode ? classNameLight : classNameDark);
}
var preferDarkQuery = '(prefers-color-scheme: dark)';
var mql = window.matchMedia(preferDarkQuery);
var supportsColorSchemeQuery = mql.media === preferDarkQuery;
var localStorageTheme = null;
try {
localStorageTheme = localStorage.getItem(storageKey);
} catch (err) {}
var localStorageExists = localStorageTheme !== null;
if (localStorageExists) {
localStorageTheme = JSON.parse(localStorageTheme);
}
// Determine the source of truth
if (localStorageExists) {
// source of truth from localStorage
setClassOnDocumentBody(localStorageTheme);
} else if (supportsColorSchemeQuery) {
// source of truth from system
setClassOnDocumentBody(mql.matches);
localStorage.setItem(storageKey, mql.matches);
} else {
// source of truth from document.body
var isDarkMode = document.body.classList.contains(classNameDark);
localStorage.setItem(storageKey, JSON.stringify(isDarkMode));
}
})();

View file

@ -30,6 +30,13 @@
"isolatedModules": true, "isolatedModules": true,
"jsx": "preserve" "jsx": "preserve"
}, },
"exclude": ["node_modules"], "exclude": ["node_modules", ".next"],
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "types/globals.d.ts"] "include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
"types/globals.d.ts",
"next.config.js",
"nextdev.js"
]
} }

View file

@ -0,0 +1,263 @@
'use strict';
var __assign =
(this && this.__assign) ||
function () {
__assign =
Object.assign ||
function (t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __awaiter =
(this && this.__awaiter) ||
function (thisArg, _arguments, P, generator) {
function adopt(value) {
return value instanceof P
? value
: new P(function (resolve) {
resolve(value);
});
}
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) {
try {
step(generator.next(value));
} catch (e) {
reject(e);
}
}
function rejected(value) {
try {
step(generator['throw'](value));
} catch (e) {
reject(e);
}
}
function step(result) {
result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
}
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator =
(this && this.__generator) ||
function (thisArg, body) {
var _ = {
label: 0,
sent: function () {
if (t[0] & 1) throw t[1];
return t[1];
},
trys: [],
ops: [],
},
f,
y,
t,
g;
return (
(g = { next: verb(0), throw: verb(1), return: verb(2) }),
typeof Symbol === 'function' &&
(g[Symbol.iterator] = function () {
return this;
}),
g
);
function verb(n) {
return function (v) {
return step([n, v]);
};
}
function step(op) {
if (f) throw new TypeError('Generator is already executing.');
while (_)
try {
if (
((f = 1),
y &&
(t =
op[0] & 2
? y['return']
: op[0]
? y['throw'] || ((t = y['return']) && t.call(y), 0)
: y.next) &&
!(t = t.call(y, op[1])).done)
)
return t;
if (((y = 0), t)) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0:
case 1:
t = op;
break;
case 4:
_.label++;
return { value: op[1], done: false };
case 5:
_.label++;
y = op[1];
op = [0];
continue;
case 7:
op = _.ops.pop();
_.trys.pop();
continue;
default:
if (
!((t = _.trys), (t = t.length > 0 && t[t.length - 1])) &&
(op[0] === 6 || op[0] === 2)
) {
_ = 0;
continue;
}
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) {
_.label = op[1];
break;
}
if (op[0] === 6 && _.label < t[1]) {
_.label = t[1];
t = op;
break;
}
if (t && _.label < t[2]) {
_.label = t[2];
_.ops.push(op);
break;
}
if (t[2]) _.ops.pop();
_.trys.pop();
continue;
}
op = body.call(thisArg, _);
} catch (e) {
op = [6, e];
y = 0;
} finally {
f = t = 0;
}
if (op[0] & 5) throw op[1];
return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __rest =
(this && this.__rest) ||
function (s, e) {
var t = {};
for (var p in s)
if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === 'function')
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
exports.__esModule = true;
exports.fetchWithTimeout = exports.arrangeIntoTree = exports.chunkArray = exports.flatten = exports.all = void 0;
function all() {
var iter = [];
for (var _i = 0; _i < arguments.length; _i++) {
iter[_i] = arguments[_i];
}
for (var _a = 0, iter_1 = iter; _a < iter_1.length; _a++) {
var i = iter_1[_a];
if (!i) {
return false;
}
}
return true;
}
exports.all = all;
function flatten(arr) {
return arr.reduce(function (flat, toFlatten) {
return flat.concat(Array.isArray(toFlatten) ? flatten(toFlatten) : toFlatten);
}, []);
}
exports.flatten = flatten;
function chunkArray(array, size) {
var result = [];
for (var i = 0; i < array.length; i += size) {
var chunk = array.slice(i, i + size);
result.push(chunk);
}
return result;
}
exports.chunkArray = chunkArray;
/**
* Arrange an array of arrays into a tree of nodes.
*
* Blatantly stolen from:
* @see https://gist.github.com/stephanbogner/4b590f992ead470658a5ebf09167b03d
*/
function arrangeIntoTree(paths) {
var tree = [];
for (var i = 0; i < paths.length; i++) {
var path = paths[i];
var currentLevel = tree;
for (var j = 0; j < path.length; j++) {
var part = path[j];
var existingPath = findWhere(currentLevel, 'base', part);
if (existingPath !== false) {
currentLevel = existingPath.children;
} else {
var newPart = {
base: part,
children: [],
};
currentLevel.push(newPart);
currentLevel = newPart.children;
}
}
}
return tree;
function findWhere(array, idx, value) {
var t = 0;
while (t < array.length && array[t][idx] !== value) {
t++;
}
if (t < array.length) {
return array[t];
} else {
return false;
}
}
}
exports.arrangeIntoTree = arrangeIntoTree;
/**
* Fetch Wrapper that incorporates a timeout via a passed AbortController instance.
*
* Adapted from: https://lowmess.com/blog/fetch-with-timeout
*/
function fetchWithTimeout(uri, options, timeout, controller) {
if (options === void 0) {
options = {};
}
return __awaiter(this, void 0, void 0, function () {
var _a, signal, allOptions, config;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
(_a = options.signal),
(signal = _a === void 0 ? new AbortController().signal : _a),
(allOptions = __rest(options, ['signal']));
config = __assign(__assign({}, allOptions), { signal: signal });
/**
* Set a timeout limit for the request using `setTimeout`. If the body of this timeout is
* reached before the request is completed, it will be cancelled.
*/
setTimeout(function () {
controller.abort();
}, timeout);
return [4 /*yield*/, fetch(uri, config)];
case 1:
return [2 /*return*/, _b.sent()];
}
});
});
}
exports.fetchWithTimeout = fetchWithTimeout;

View file

@ -1,5 +1,5 @@
export function all(...iter: any[]) { export function all<I extends unknown>(...iter: I[]): boolean {
for (let i of iter) { for (const i of iter) {
if (!i) { if (!i) {
return false; return false;
} }
@ -13,19 +13,19 @@ export function flatten<T extends unknown>(arr: any[][]): T[] {
}, []); }, []);
} }
export function chunkArray<A extends any>(array: A[], size: number): A[][] { export function chunkArray<A extends unknown>(array: A[], size: number): A[][] {
let result = [] as A[][]; const result = [] as A[][];
for (let i = 0; i < array.length; i += size) { for (let i = 0; i < array.length; i += size) {
let chunk = array.slice(i, i + size); const chunk = array.slice(i, i + size);
result.push(chunk); result.push(chunk);
} }
return result; return result;
} }
interface PathPart { type PathPart = {
base: number; base: number;
children: PathPart[]; children: PathPart[];
} };
/** /**
* Arrange an array of arrays into a tree of nodes. * Arrange an array of arrays into a tree of nodes.
@ -33,17 +33,17 @@ interface PathPart {
* Blatantly stolen from: * Blatantly stolen from:
* @see https://gist.github.com/stephanbogner/4b590f992ead470658a5ebf09167b03d * @see https://gist.github.com/stephanbogner/4b590f992ead470658a5ebf09167b03d
*/ */
export function arrangeIntoTree<P extends any>(paths: P[][]): PathPart[] { export function arrangeIntoTree<P extends unknown>(paths: P[][]): PathPart[] {
let tree = [] as PathPart[]; const tree = [] as PathPart[];
for (let i = 0; i < paths.length; i++) { for (let i = 0; i < paths.length; i++) {
let path = paths[i]; const path = paths[i];
let currentLevel = tree; let currentLevel = tree;
for (let j = 0; j < path.length; j++) { for (let j = 0; j < path.length; j++) {
let part = path[j]; const part = path[j];
const existingPath = findWhere(currentLevel, 'base', part); const existingPath = findWhere<PathPart, typeof part>(currentLevel, 'base', part);
if (existingPath !== false) { if (existingPath !== false) {
currentLevel = existingPath.children; currentLevel = existingPath.children;
@ -60,8 +60,13 @@ export function arrangeIntoTree<P extends any>(paths: P[][]): PathPart[] {
} }
return tree; return tree;
function findWhere<V extends any>(array: any[], idx: string, value: V): PathPart | false { function findWhere<A extends Record<string, unknown>, V extends unknown>(
array: A[],
idx: string,
value: V,
): A | false {
let t = 0; let t = 0;
while (t < array.length && array[t][idx] !== value) { while (t < array.length && array[t][idx] !== value) {
t++; t++;
} }

File diff suppressed because it is too large Load diff