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:
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
8
hyperglass/ui/.prettierignore
Normal file
8
hyperglass/ui/.prettierignore
Normal 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
11
hyperglass/ui/.prettierrc
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"semi": true,
|
||||
"printWidth": 100,
|
||||
"tabWidth": 2,
|
||||
"singleQuote": true,
|
||||
"bracketSpacing": true,
|
||||
"jsxBracketSameLine": false,
|
||||
"useTabs": false,
|
||||
"arrowParens": "avoid",
|
||||
"trailingComma": "all"
|
||||
}
|
@@ -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}
|
||||
|
@@ -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"
|
||||
|
@@ -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>
|
||||
);
|
||||
|
@@ -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 {}
|
||||
|
@@ -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 (
|
||||
|
@@ -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 (
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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>
|
||||
|
@@ -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>
|
||||
|
@@ -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>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
@@ -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>
|
||||
|
@@ -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>
|
||||
|
@@ -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();
|
||||
|
@@ -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');
|
||||
|
@@ -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();
|
||||
|
@@ -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();
|
||||
|
@@ -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>
|
||||
</>
|
||||
|
@@ -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%"
|
||||
|
@@ -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 />
|
||||
|
@@ -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>
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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>
|
||||
);
|
||||
|
@@ -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>
|
||||
|
@@ -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();
|
||||
|
||||
|
@@ -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) {
|
||||
|
@@ -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');
|
||||
|
@@ -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);
|
||||
|
@@ -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}
|
||||
|
@@ -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"
|
||||
|
@@ -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>
|
||||
|
@@ -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>
|
||||
|
@@ -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)} />
|
||||
);
|
||||
|
@@ -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} />
|
||||
);
|
||||
|
@@ -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}
|
||||
|
@@ -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;
|
||||
|
@@ -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('/');
|
||||
|
@@ -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 = {
|
||||
|
@@ -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);
|
||||
|
@@ -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>
|
||||
);
|
||||
|
@@ -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>
|
||||
);
|
||||
|
@@ -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">
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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" />
|
||||
|
@@ -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>
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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 };
|
||||
|
@@ -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>
|
||||
|
@@ -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);
|
||||
|
@@ -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 &&
|
||||
|
@@ -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 {
|
||||
|
@@ -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} />
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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}>
|
||||
|
@@ -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]);
|
||||
};
|
||||
|
@@ -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';
|
||||
|
@@ -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();
|
||||
|
@@ -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"
|
||||
|
@@ -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" />
|
||||
);
|
||||
|
@@ -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');
|
||||
|
@@ -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} />;
|
||||
};
|
||||
|
@@ -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>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
@@ -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}>
|
||||
|
@@ -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,
|
||||
|
@@ -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');
|
||||
|
@@ -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;
|
||||
};
|
||||
|
@@ -1,5 +1,3 @@
|
||||
export interface TIf {
|
||||
c: boolean;
|
||||
render?: (rest: any) => JSX.Element;
|
||||
[k: string]: any;
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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']>;
|
||||
|
@@ -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,
|
||||
|
@@ -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,
|
||||
|
@@ -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,
|
||||
|
@@ -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();
|
||||
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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;
|
||||
|
17
hyperglass/ui/package.json
vendored
17
hyperglass/ui/package.json
vendored
@@ -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"
|
||||
}
|
||||
}
|
||||
|
@@ -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 } };
|
||||
};
|
||||
|
@@ -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>
|
||||
|
@@ -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}>
|
||||
|
@@ -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 = '';
|
||||
}
|
||||
|
@@ -1 +0,0 @@
|
||||
module.exports = require("@upstatement/prettier-config");
|
@@ -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));
|
||||
}
|
||||
})();
|
@@ -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"
|
||||
]
|
||||
}
|
||||
|
263
hyperglass/ui/util/common.js
Normal file
263
hyperglass/ui/util/common.js
Normal 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;
|
@@ -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++;
|
||||
}
|
||||
|
956
hyperglass/ui/yarn.lock
vendored
956
hyperglass/ui/yarn.lock
vendored
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user