1
0
mirror of https://github.com/checktheroads/hyperglass synced 2024-05-11 05:55:08 +00:00

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",
"plugins": ["react-hooks"],
"settings": { "import/resolver": { "typescript": {} } },
"root": true,
"extends": [
"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": {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": ["warn"]
"@typescript-eslint/no-unused-vars": "off",
"@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 { 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 bg = useColorValue('white', 'dark.500');
const color = useColorValue('dark.500', 'white');
console.log('some shit');
return (
<Flex
bg={bg}

View File

@@ -1,8 +1,8 @@
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
p={4}
direction="column"

View File

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

View File

@@ -1,9 +1,9 @@
import type { FlexProps } from '@chakra-ui/react';
export interface ICardBody extends Omit<FlexProps, 'onClick'> {
export interface TCardBody extends Omit<FlexProps, 'onClick'> {
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';
export const CodeBlock = (props: BoxProps) => {
export const CodeBlock: React.FC<BoxProps> = (props: BoxProps) => {
const bg = useColorValue('blackAlpha.100', 'gray.800');
const color = useColorValue('black', 'white');
return (

View File

@@ -3,11 +3,11 @@ import ReactCountdown, { zeroPad } from 'react-countdown';
import { If } from '~/components';
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;
let time = [zeroPad(seconds)];
const time = [zeroPad(seconds)];
minutes !== 0 && time.unshift(zeroPad(minutes));
hours !== 0 && time.unshift(zeroPad(hours));
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 then = timeout * 1000;
return (

View File

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

View File

@@ -21,7 +21,7 @@ interface TViewer extends Pick<UseDisclosureReturn, 'isOpen' | 'onClose'> {
children: React.ReactNode;
}
const Viewer = (props: TViewer) => {
const Viewer: React.FC<TViewer> = (props: TViewer) => {
const { title, isOpen, onClose, children } = props;
const bg = useColorValue('white', 'black');
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: themeOpen, onOpen: onThemeOpen, onClose: themeClose } = useDisclosure();
const { colorMode } = useColorMode();
@@ -64,7 +64,8 @@ export const Debugger = () => {
position="relative"
justifyContent="center"
borderColor={borderColor}
spacing={{ base: 2, lg: 8 }}>
spacing={{ base: 2, lg: 8 }}
>
<Tag size={tagSize} colorScheme="gray">
{colorMode.toUpperCase()}
</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 { useColorValue, useBreakpointValue } from '~/context';
import { useOpposingColor } from '~/hooks';
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 placement = side === 'left' ? 'top' : side === 'right' ? 'top-end' : undefined;
const bg = useColorValue('white', 'gray.900');
@@ -17,7 +17,8 @@ export const FooterButton = (props: TFooterButton) => {
as={Button}
size={size}
variant="ghost"
aria-label={typeof title === 'string' ? title : undefined}>
aria-label={typeof title === 'string' ? title : undefined}
>
{title}
</MenuButton>
<MenuList
@@ -29,7 +30,8 @@ export const FooterButton = (props: TFooterButton) => {
textAlign="left"
mx={{ base: 1, lg: 2 }}
maxW={{ base: '100%', lg: '50vw' }}
{...rest}>
{...rest}
>
<Markdown content={content} />
</MenuList>
</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 Moon = dynamic<MeronexIcon>(() => import('@meronex/icons/hi').then(i => i.HiMoon));
export const ColorModeToggle = forwardRef<HTMLButtonElement, TColorModeToggle>((props, ref) => {
const { size = '1.5rem', ...rest } = props;
const { colorMode, toggleColorMode } = useColorMode();
export const ColorModeToggle = forwardRef<HTMLButtonElement, TColorModeToggle>(
(props: TColorModeToggle, ref) => {
const { size = '1.5rem', ...rest } = props;
const { colorMode, toggleColorMode } = useColorMode();
const bg = useColorValue('primary.500', 'yellow.300');
const color = useOpposingColor(bg);
const label = useColorValue('Switch to dark mode', 'Switch to light mode');
const btnSize = useBreakpointValue({ base: 'xs', lg: 'sm' });
const bg = useColorValue('primary.500', 'yellow.300');
const color = useOpposingColor(bg);
const label = useColorValue('Switch to dark mode', 'Switch to light mode');
const btnSize = useBreakpointValue({ base: 'xs', lg: 'sm' });
return (
<Tooltip hasArrow placement="top-end" label={label} bg={bg} color={color}>
<Button
ref={ref}
size={btnSize}
title={label}
variant="ghost"
aria-label={label}
_hover={{ color: bg }}
color="currentColor"
onClick={toggleColorMode}
{...rest}>
<If c={colorMode === 'light'}>
<Icon as={Moon} boxSize={size} />
</If>
<If c={colorMode === 'dark'}>
<Icon as={Sun} boxSize={size} />
</If>
</Button>
</Tooltip>
);
});
return (
<Tooltip hasArrow placement="top-end" label={label} bg={bg} color={color}>
<Button
ref={ref}
size={btnSize}
title={label}
variant="ghost"
aria-label={label}
_hover={{ color: bg }}
color="currentColor"
onClick={toggleColorMode}
{...rest}
>
<If c={colorMode === 'light'}>
<Icon as={Moon} boxSize={size} />
</If>
<If c={colorMode === 'dark'}>
<Icon as={Sun} boxSize={size} />
</If>
</Button>
</Tooltip>
);
},
);

View File

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

View File

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

View File

@@ -9,8 +9,6 @@ import type { OptionProps } from 'react-select';
import type { TBGPCommunity, TSelectOption } 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[] {
return communities.map(c => ({
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 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 }));
}
export const QueryType = (props: TQuerySelectField) => {
export const QueryType: React.FC<TQuerySelectField> = (props: TQuerySelectField) => {
const { onChange, label } = props;
const { queries } = useConfig();
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 }));
}
export const QueryVrf = (props: TQueryVrf) => {
export const QueryVrf: React.FC<TQueryVrf> = (props: TQueryVrf) => {
const { vrfs, onChange, label } = props;
const { selections } = useLGState();
const { exportState } = useLGMethods();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -14,9 +14,9 @@ import { useMobile } from '~/context';
// 5xl: 7
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 realSize = useToken('fontSizes', size);
const realSize = useToken('fontSizes', size) as string;
const isMobile = useMobile();
function getSize(l: number): void {
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));
export const HelpModal = (props: THelpModal) => {
export const HelpModal: React.FC<THelpModal> = (props: THelpModal) => {
const { visible, item, name, ...rest } = props;
const { isOpen, onOpen, onClose } = useDisclosure();
const bg = useColorValue('whiteSolid.50', 'blackSolid.800');

View File

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

View File

@@ -8,7 +8,7 @@ import { ResetButton } from './resetButton';
import type { TFrame } from './types';
export const Frame = (props: TFrame) => {
export const Frame: React.FC<TFrame> = (props: TFrame) => {
const { developer_mode } = useConfig();
const { isSubmitting } = useLGState();
const { resetForm } = useLGMethods();
@@ -39,7 +39,8 @@ export const Frame = (props: TFrame) => {
* 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/
*/
minHeight={isSafari ? '-webkit-fill-available' : '100vh'}>
minHeight={isSafari ? '-webkit-fill-available' : '100vh'}
>
<Header resetForm={handleReset} />
<Flex
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));
export const ResetButton = (props: TResetButton) => {
export const ResetButton: React.FC<TResetButton> = (props: TResetButton) => {
const { developerMode, resetForm, ...rest } = props;
const { isSubmitting } = useLGState();
const bg = useColorValue('primary.500', 'primary.300');
@@ -30,7 +30,8 @@ export const ResetButton = (props: TResetButton) => {
borderRightRadius="md"
initial={{ x: '-100%' }}
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}>
<IconButton
variant="unstyled"

View File

@@ -17,7 +17,8 @@ export const Loading: LoadableBaseOptions['loading'] = () => (
flexDirection="column"
css={{
'@media (prefers-color-scheme: dark)': { backgroundColor: 'black', color: 'white' },
}}>
}}
>
<Spinner color="primary.500" w="6rem" h="6rem" />
</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 { ack, greetingReady, isOpen: greetingIsOpen } = useGreeting();
const { ack, greetingReady } = useGreeting();
const getDevice = useDevice();
const noQueryType = useStrf(messages.no_input, { field: web.text.query_type });
@@ -243,7 +243,8 @@ export const LookingGlass = () => {
exit={{ opacity: 0, x: -300 }}
initial={{ opacity: 0, y: 300 }}
maxW={{ base: '100%', lg: '75%' }}
onSubmit={handleSubmit(submitHandler)}>
onSubmit={handleSubmit(submitHandler)}
>
<FormRow>
<FormField name="query_location" 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}
labelAddOn={
<HelpModal visible={isQueryContent(vrfContent)} item={vrfContent} name="query_type" />
}>
}
>
<QueryType onChange={handleChange} label={web.text.query_type} />
</FormField>
</FormRow>
@@ -280,7 +282,8 @@ export const LookingGlass = () => {
maxW="100%"
flex="0 0 0"
flexDir="column"
mr={{ base: 0, lg: 2 }}>
mr={{ base: 0, lg: 2 }}
>
<SubmitButton handleChange={handleChange} />
</Flex>
</FormRow>

View File

@@ -44,12 +44,12 @@ function clean<P extends ChakraProps>(props: P): P {
return props;
}
export const Checkbox = (props: TCheckbox & MDProps) => {
export const Checkbox: React.FC<TCheckbox & MDProps> = (props: TCheckbox & MDProps) => {
const { checked, node, ...rest } = props;
return <ChakraCheckbox isChecked={checked} {...rest} />;
};
export const List = (props: TList) => {
export const List: React.FC<TList> = (props: TList) => {
const { ordered, ...rest } = props;
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;
return checked ? (
<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 levelMap = {
@@ -87,14 +87,16 @@ export const Heading = (props: THeading) => {
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');
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;
return (
<>
@@ -108,7 +110,7 @@ export const TableData = (props: TTableData) => {
);
};
export const Paragraph = (props: TextProps) => (
export const Paragraph: React.FC<TextProps> = (props: TextProps) => (
<ChakraText
my={4}
css={{
@@ -118,11 +120,19 @@ export const Paragraph = (props: TextProps) => (
{...clean<TextProps>(props)}
/>
);
export const InlineCode = (props: CodeProps) => (
export const InlineCode: React.FC<CodeProps> = (props: CodeProps) => (
<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)} />
);
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,
} as ReactMarkdownProps['renderers'];
export const Markdown = (props: TMarkdown) => (
export const Markdown: React.FC<TMarkdown> = (props: TMarkdown) => (
<ReactMarkdown renderers={renderers} source={props.content} />
);

View File

@@ -3,16 +3,16 @@ import { useColorValue } from '~/context';
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} />
);
export const TH = (props: BoxProps) => {
export const TH: React.FC<BoxProps> = (props: BoxProps) => {
const bg = useColorValue('blackAlpha.50', 'whiteAlpha.50');
return <Box as="th" bg={bg} fontWeight="semibold" p={2} fontSize="sm" {...props} />;
};
export const TD = (props: BoxProps) => {
export const TD: React.FC<BoxProps> = (props: BoxProps) => {
return (
<Box
p={2}

View File

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

View File

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

View File

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

View File

@@ -26,7 +26,7 @@ import type {
dayjs.extend(relativeTimePlugin);
dayjs.extend(utcPlugin);
export const MonoField = (props: TMonoField) => {
export const MonoField: React.FC<TMonoField> = (props: TMonoField) => {
const { v, ...rest } = props;
return (
<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 color = useColorValue(['gray.500', 'green.500'], ['whiteAlpha.300', 'blackAlpha.500']);
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 now = dayjs.utc();
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 fixMeText =
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 color = useColorValue(
// light: inactive, active
@@ -89,7 +89,7 @@ export const ASPath = (props: TASPath) => {
return <Icon as={End} />;
}
let paths = [] as JSX.Element[];
const paths = [] as JSX.Element[];
path.map((asn, i) => {
const asnStr = String(asn);
@@ -107,7 +107,7 @@ export const ASPath = (props: TASPath) => {
return <>{paths}</>;
};
export const Communities = (props: TCommunities) => {
export const Communities: React.FC<TCommunities> = (props: TCommunities) => {
const { communities } = props;
const bg = useColorValue('white', 'gray.900');
const color = useOpposingColor(bg);
@@ -133,7 +133,8 @@ export const Communities = (props: TCommunities) => {
textAlign="left"
fontFamily="mono"
fontWeight="normal"
whiteSpace="pre-wrap">
whiteSpace="pre-wrap"
>
{communities.join('\n')}
</MenuList>
</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 { web } = useConfig();
const bg = useColorValue(
@@ -171,10 +175,13 @@ export const RPKIState = forwardRef<HTMLDivElement, TRPKIState>((props, ref) =>
placement="right"
label={text[state] ?? text[3]}
bg={bg[+active][state]}
color={color}>
color={color}
>
<Box ref={ref} boxSize={5}>
<Box as={icon[state]} color={bg[+active][state]} />
</Box>
</Tooltip>
);
});
};
export const RPKIState = forwardRef<HTMLDivElement, TRPKIState>(_RPKIState);

View File

@@ -10,7 +10,7 @@ function makeColumns(fields: TParsedDataField[]): TColumn[] {
return fields.map(pair => {
const [header, accessor, align] = pair;
let columnConfig = {
const columnConfig = {
align,
accessor,
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 { parsed_data_fields } = useConfig();
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}>
<Table
columns={columns}
bordersHorizontal
data={data.routes}
rowHighlightBg="green"
rowHighlightProp="active"
Cell={(d: TCellRender) => <Cell data={d} rawData={data} />}
bordersHorizontal
rowHighlightBg="green"
/>
</Flex>
);

View File

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

View File

@@ -7,7 +7,7 @@ const PathIcon = dynamic<MeronexIcon>(() =>
import('@meronex/icons/bi').then(i => i.BisNetworkChart),
);
export const PathButton = (props: TPathButton) => {
export const PathButton: React.FC<TPathButton> = (props: TPathButton) => {
const { onOpen } = props;
return (
<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 { TChart, TNode, TNodeData } from './types';
export const Chart = (props: TChart) => {
export const Chart: React.FC<TChart> = (props: TChart) => {
const { data } = props;
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 { 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 Square = dynamic<MeronexIcon>(() => import('@meronex/icons/fi').then(i => i.FiSquare));
export const Controls = () => {
export const Controls: React.FC = () => {
const { fitView, zoomIn, zoomOut } = useZoomPanHelper();
return (
<ButtonGroup
@@ -18,7 +18,8 @@ export const Controls = () => {
isAttached
pos="absolute"
variant="solid"
colorScheme="secondary">
colorScheme="secondary"
>
<IconButton icon={<Plus />} onClick={() => zoomIn()} aria-label="Zoom In" />
<IconButton icon={<Minus />} onClick={() => zoomOut()} aria-label="Zoom Out" />
<IconButton icon={<Square />} onClick={() => fitView()} aria-label="Fit Nodes" />

View File

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

View File

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

View File

@@ -7,7 +7,7 @@ import type { BasePath } from './types';
function treeToElement(part: PathPart, len: number, index: number): FlowElement[] {
const x = index * 250;
const y = -(len * 10);
let elements = [
const elements = [
{
id: String(part.base),
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 },
};
for (const [i, path] of childPaths.entries()) {
for (const path of childPaths) {
// path = Each unique path from origin
const first = path[0];
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';
export const CopyButton = (props: TCopyButton) => {
export const CopyButton: React.FC<TCopyButton> = (props: TCopyButton) => {
const { copyValue, ...rest } = props;
const { onCopy, hasCopied } = useClipboard(copyValue);
return (
@@ -18,7 +18,8 @@ export const CopyButton = (props: TCopyButton) => {
variant="ghost"
onClick={onCopy}
colorScheme="secondary"
{...rest}>
{...rest}
>
<Icon as={hasCopied ? Check : Copy} boxSize="16px" />
</Button>
</Tooltip>

View File

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

View File

@@ -7,7 +7,7 @@ import { useDevice, useLGState } from '~/hooks';
import { isQueryType } from '~/types';
import { Result } from './individual';
export const Results = () => {
export const Results: React.FC = () => {
const { queries, vrfs, web } = useConfig();
const { queryLocation, queryTarget, queryType, queryVrf } = useLGState();
@@ -83,7 +83,8 @@ export const Results = () => {
w="100%"
mx="auto"
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">
<AnimatePresence>
{queryLocation.value && (
@@ -92,7 +93,8 @@ export const Results = () => {
initial={initialLeft}
animate={animateLeft}
exit={{ opacity: 0, x: -100 }}
transition={{ duration: 0.3, delay: 0.3 }}>
transition={{ duration: 0.3, delay: 0.3 }}
>
<Label
bg={queryBg}
label={web.text.query_type}
@@ -104,7 +106,8 @@ export const Results = () => {
initial={initialCenter}
animate={animateCenter}
exit={{ opacity: 0, scale: 0.5 }}
transition={{ duration: 0.3, delay: 0.3 }}>
transition={{ duration: 0.3, delay: 0.3 }}
>
<Label
bg={targetBg}
value={queryTarget.value}
@@ -116,7 +119,8 @@ export const Results = () => {
initial={initialRight}
animate={animateRight}
exit={{ opacity: 0, x: 100 }}
transition={{ duration: 0.3, delay: 0.3 }}>
transition={{ duration: 0.3, delay: 0.3 }}
>
<Label
bg={vrfBg}
label={web.text.query_vrf}
@@ -142,7 +146,8 @@ export const Results = () => {
exit={{ opacity: 0, y: 300 }}
transition={{ duration: 0.3 }}
animate={{ opacity: 1, y: 0 }}
maxW={{ base: '100%', md: '75%' }}>
maxW={{ base: '100%', md: '75%' }}
>
<Accordion allowMultiple allowToggle index={resultsComplete}>
<AnimatePresence>
{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 {
return error !== null && 'message' in error;
return typeof error !== 'undefined' && error !== null && 'message' in error;
}
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 {

View File

@@ -15,7 +15,7 @@ const runtimeText = (runtime: number, text: string): string => {
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 status = useColorValue('primary.500', 'primary.300');
@@ -36,7 +36,8 @@ export const ResultHeader = (props: TResultHeader) => {
isDisabled={loading}
label={isError ? errorMsg : label}
bg={isError ? warning : defaultStatus}
color={color}>
color={color}
>
<Box boxSize={6}>
{loading ? (
<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 AccordionHeaderWrapper = (props: TAccordionHeaderWrapper) => {
const AccordionHeaderWrapper: React.FC<TAccordionHeaderWrapper> = (
props: TAccordionHeaderWrapper,
) => {
const { hoverBg, ...rest } = props;
return (
<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 {
index,
device,
@@ -170,7 +172,8 @@ export const Result = forwardRef<HTMLDivElement, TResult>((props, ref) => {
css={{
'&:last-of-type': { borderBottom: 'none' },
'&:first-of-type': { borderTop: 'none' },
}}>
}}
>
<AccordionHeaderWrapper hoverBg="blackAlpha.50">
<AccordionButton
py={2}
@@ -178,7 +181,8 @@ export const Result = forwardRef<HTMLDivElement, TResult>((props, ref) => {
_hover={{}}
_focus={{}}
flex="1 0 auto"
onClick={handleToggle}>
onClick={handleToggle}
>
<ResultHeader
isError={isError}
loading={isLoading}
@@ -213,7 +217,8 @@ export const Result = forwardRef<HTMLDivElement, TResult>((props, ref) => {
},
'-ms-overflow-style': { display: 'none' },
}}>
}}
>
<Box>
<Flex direction="column" flex="1 0 auto" maxW={error ? '100%' : undefined}>
{!isError && typeof data !== 'undefined' ? (
@@ -246,7 +251,8 @@ export const Result = forwardRef<HTMLDivElement, TResult>((props, ref) => {
mt={2}
spacing={1}
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={!isMobile}>
<Countdown timeout={cache.timeout} text={web.text.cache_prefix} />
@@ -265,4 +271,6 @@ export const Result = forwardRef<HTMLDivElement, TResult>((props, ref) => {
</AccordionPanel>
</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));
export const RequeryButton = forwardRef<HTMLButtonElement, TRequeryButton>((props, ref) => {
const _RequeryButton: React.ForwardRefRenderFunction<HTMLButtonElement, TRequeryButton> = (
props: TRequeryButton,
ref,
) => {
const { requery, ...rest } = props;
return (
@@ -20,9 +23,12 @@ export const RequeryButton = forwardRef<HTMLButtonElement, TRequeryButton>((prop
variant="ghost"
onClick={requery as TRequeryButton['onClick']}
colorScheme="secondary"
{...rest}>
{...rest}
>
<Icon as={Repeat} boxSize="16px" />
</Button>
</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';
const SelectContext = createContext<TSelectContext>(Object());
export const useSelectContext = () => useContext(SelectContext);
export const useSelectContext = (): TSelectContext => useContext(SelectContext);
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 { isOpen, onOpen, onClose } = useDisclosure();
@@ -49,8 +49,8 @@ export const Select = (props: TSelectBase) => {
const multiValue = useMultiValueStyle({ colorMode });
const multiValueLabel = useMultiValueLabelStyle({ colorMode });
const multiValueRemove = useMultiValueRemoveStyle({ colorMode });
const menuPortal = useMenuPortal({ colorMode });
const rsTheme = useRSTheme({ colorMode });
const menuPortal = useMenuPortal();
const rsTheme = useRSTheme();
return (
<SelectContext.Provider value={selectContext}>

View File

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

View File

@@ -1,3 +1,6 @@
/* eslint @typescript-eslint/no-explicit-any: 0 */
/* eslint @typescript-eslint/explicit-module-boundary-types: 0 */
import type {
Props as IReactSelect,
ControlProps,
@@ -8,6 +11,7 @@ import type {
IndicatorProps,
Theme as RSTheme,
PlaceholderProps,
Styles as RSStyles,
} from 'react-select';
import type { BoxProps } from '@chakra-ui/react';
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 TRSStyleCallback = (base: RSStyles) => RSStyles;
export type TRSThemeCallback = (theme: TRSTheme) => TRSTheme;
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 { TSubmitButton, TRSubmitButton } from './types';
const SubmitIcon = forwardRef<HTMLButtonElement, Omit<IconButtonProps, 'aria-label'>>(
(props, ref) => {
const { isLoading, ...rest } = props;
return (
<IconButton
ref={ref}
size="lg"
width={16}
type="submit"
icon={<FiSearch />}
title="Submit Query"
colorScheme="primary"
isLoading={isLoading}
aria-label="Submit Query"
{...rest}
/>
);
},
);
const _SubmitIcon: React.ForwardRefRenderFunction<
HTMLButtonElement,
Omit<IconButtonProps, 'aria-label'>
> = (props: Omit<IconButtonProps, 'aria-label'>, ref) => {
const { isLoading, ...rest } = props;
return (
<IconButton
ref={ref}
size="lg"
width={16}
type="submit"
icon={<FiSearch />}
title="Submit Query"
colorScheme="primary"
isLoading={isLoading}
aria-label="Submit Query"
{...rest}
/>
);
};
const SubmitIcon = forwardRef<HTMLButtonElement, Omit<IconButtonProps, 'aria-label'>>(_SubmitIcon);
/**
* Mobile Submit Button
*/
const MSubmitButton = (props: TRSubmitButton) => {
const MSubmitButton: React.FC<TRSubmitButton> = (props: TRSubmitButton) => {
const { children, isOpen, onClose, onChange } = props;
const bg = useColorValue('white', 'gray.900');
return (
@@ -58,7 +60,8 @@ const MSubmitButton = (props: TRSubmitButton) => {
onClose={onClose}
closeOnEsc={false}
closeOnOverlayClick={false}
motionPreset="slideInBottom">
motionPreset="slideInBottom"
>
<ModalOverlay />
<ModalContent bg={bg}>
<ModalCloseButton />
@@ -74,7 +77,7 @@ const MSubmitButton = (props: TRSubmitButton) => {
/**
* Desktop Submit Button
*/
const DSubmitButton = (props: TRSubmitButton) => {
const DSubmitButton: React.FC<TRSubmitButton> = (props: TRSubmitButton) => {
const { children, isOpen, onClose, onChange } = props;
const bg = useColorValue('white', 'gray.900');
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 isMobile = useMobile();
const { resolvedIsOpen, btnLoading } = useLGState();

View File

@@ -2,7 +2,7 @@ import { Box } 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
as="tbody"
overflowY="scroll"

View File

@@ -2,6 +2,6 @@ import { IconButton } from '@chakra-ui/react';
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" />
);

View File

@@ -3,7 +3,7 @@ import { useColorValue } from '~/context';
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 [doVerticalBorders, index] = bordersVertical;
const borderLeftColor = useColorValue('blackAlpha.100', 'whiteAlpha.100');

View File

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

View File

@@ -1,7 +1,7 @@
import { Select } 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;
return (
<Select size="sm" {...rest}>

View File

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

View File

@@ -3,7 +3,7 @@ import { useColorValue } from '~/context';
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 scrollbarHover = useColorValue('blackAlpha.400', 'whiteAlpha.400');
const scrollbarBg = useColorValue('blackAlpha.50', 'whiteAlpha.50');

View File

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

View File

@@ -1,5 +1,3 @@
export interface TIf {
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();
export const HyperglassProvider = (props: THyperglassProvider) => {
export const HyperglassProvider: React.FC<THyperglassProvider> = (props: THyperglassProvider) => {
const { config, children } = props;
const value = useMemo(() => config, []);
const userTheme = value && makeTheme(value.web.theme, value.web.theme.default_color_mode);

View File

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

View File

@@ -1,5 +1,6 @@
import { useQuery } from 'react-query';
import type { QueryObserverResult } from 'react-query';
import type { TASNQuery } from '~/types';
import type { TUseASNDetailFn } from './types';
@@ -9,6 +10,7 @@ async function query(ctx: TUseASNDetailFn): Promise<TASNQuery> {
mode: 'cors',
method: 'POST',
headers: { 'content-type': 'application/json' },
/* eslint no-useless-escape: 0 */
body: JSON.stringify({ query: `{ asn(asn:\"${asn}\"){ organization { orgName } } }` }),
});
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.
* @see https://api.asrank.caida.org/v2/docs
*/
export function useASNDetail(asn: string) {
export function useASNDetail(asn: string): QueryObserverResult<TASNQuery> {
return useQuery(asn, query, {
refetchOnWindowFocus: 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.
*/
export function useBooleanValue<T extends any, F extends any>(
export function useBooleanValue<T extends unknown, F extends unknown>(
status: boolean,
ifTrue: T,
ifFalse: F,

View File

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

View File

@@ -2,13 +2,14 @@ import { useQuery } from 'react-query';
import { useConfig } from '~/context';
import { fetchWithTimeout } from '~/util';
import type { QueryObserverResult } from 'react-query';
import type { TFormQuery } from '~/types';
import type { TUseLGQueryFn } from './types';
/**
* 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 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
* opposing color should be black or white.
*/
export function useIsDark(color: string) {
export function useIsDark(color: string): boolean {
const theme = useTheme();
if (typeof color === 'string' && color.match(/[a-zA-Z]+\.[a-zA-Z0-9]+/g)) {
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()
*/
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);
}

View File

@@ -43,7 +43,7 @@ function formatTime(val: number): string {
export function useTableToString(
target: string,
data: TQueryResponse | undefined,
...deps: any
...deps: unknown[]
): () => string {
const { web, parsed_data_fields, messages } = useConfig();
@@ -81,7 +81,7 @@ export function useTableToString(
let result = messages.no_output;
try {
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 field of parsed_data_fields) {
const [header, accessor, align] = field;

View File

@@ -50,20 +50,27 @@
"@types/react-table": "^7.0.25",
"@types/string-format": "^2.0.0",
"@types/yup": "^0.29.9",
"@typescript-eslint/eslint-plugin": "^2.24.0",
"@typescript-eslint/parser": "^2.24.0",
"@typescript-eslint/eslint-plugin": "^4.11.1",
"@typescript-eslint/parser": "^4.11.1",
"@upstatement/eslint-config": "^0.4.3",
"@upstatement/prettier-config": "^0.3.0",
"babel-eslint": "^10.1.0",
"eslint": "^6.8.0",
"eslint-config-prettier": "^7.1.0",
"eslint-config-react-app": "^5.2.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-react": "^7.19.0",
"eslint-plugin-react-hooks": "^2.3.0",
"eslint-plugin-prettier": "^3.3.0",
"eslint-plugin-react": "^7.22.0",
"eslint-plugin-react-hooks": "^4.2.0",
"express": "^4.17.1",
"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"
}
}

View File

@@ -2,21 +2,21 @@ import Head from 'next/head';
import { HyperglassProvider } from '~/context';
import { IConfig } from '~/types';
import type { AppProps, AppInitialProps, AppContext } from 'next/app';
if (process.env.NODE_ENV === 'development') {
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 {
appProps: { config: IConfig };
}
type Temp<IP> = React.FC<GetInitialPropsReturn<IP>> & {
getInitialProps(c?: AppContext): Promise<{ appProps: IP }>;
};
type TAppInitial = Pick<TApp, 'appProps'>;
const App = (props: TApp) => {
const App: Temp<TApp> = (props: GetInitialPropsReturn<TApp>) => {
const { Component, pageProps, appProps } = props;
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;
return { appProps: { config } };
};

View File

@@ -1,13 +1,13 @@
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 {
static async getInitialProps(ctx: DocumentContext) {
static async getInitialProps(ctx: DocumentContext): Promise<DocumentInitialProps> {
const initialProps = await Document.getInitialProps(ctx);
return { ...initialProps };
}
render() {
render(): JSX.Element {
return (
<Html lang="en">
<Head>

View File

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

View File

@@ -13,7 +13,7 @@ interface TIndex {
favicons: FaviconComponent[];
}
const Index = (props: TIndex) => {
const Index: React.FC<TIndex> = (props: TIndex) => {
const { favicons } = props;
return (
<>
@@ -32,7 +32,8 @@ const Index = (props: TIndex) => {
export const getStaticProps: GetStaticProps<TIndex> = async () => {
const faviconConfig = (process.env._HYPERGLASS_FAVICONS_ as unknown) as Favicon[];
const favicons = faviconConfig.map(icon => {
let { image_format, dimensions, prefix, rel } = icon;
const { image_format, dimensions, prefix } = icon;
let { rel } = icon;
if (rel === null) {
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,
"jsx": "preserve"
},
"exclude": ["node_modules"],
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "types/globals.d.ts"]
"exclude": ["node_modules", ".next"],
"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[]) {
for (let i of iter) {
export function all<I extends unknown>(...iter: I[]): boolean {
for (const i of iter) {
if (!i) {
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[][] {
let result = [] as A[][];
export function chunkArray<A extends unknown>(array: A[], size: number): A[][] {
const result = [] as A[][];
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);
}
return result;
}
interface PathPart {
type PathPart = {
base: number;
children: PathPart[];
}
};
/**
* Arrange an array of arrays into a tree of nodes.
@@ -33,17 +33,17 @@ interface PathPart {
* Blatantly stolen from:
* @see https://gist.github.com/stephanbogner/4b590f992ead470658a5ebf09167b03d
*/
export function arrangeIntoTree<P extends any>(paths: P[][]): PathPart[] {
let tree = [] as PathPart[];
export function arrangeIntoTree<P extends unknown>(paths: P[][]): PathPart[] {
const tree = [] as PathPart[];
for (let i = 0; i < paths.length; i++) {
let path = paths[i];
const path = paths[i];
let currentLevel = tree;
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) {
currentLevel = existingPath.children;
@@ -60,8 +60,13 @@ export function arrangeIntoTree<P extends any>(paths: P[][]): PathPart[] {
}
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;
while (t < array.length && array[t][idx] !== value) {
t++;
}

File diff suppressed because it is too large Load Diff