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

continue typescript & chakra v1 migrations [skip ci]

This commit is contained in:
checktheroads
2020-12-12 00:49:03 -07:00
parent be601e4aef
commit 53dd6fada4
19 changed files with 142 additions and 287 deletions

View File

@@ -1,30 +1,29 @@
import { Button } from '@chakra-ui/react';
import { AnimatedDiv } from '~/components';
import { Button, Menu, MenuButton, MenuList } from '@chakra-ui/react';
import { Markdown } from '~/components';
import type { TFooterButton } from './types';
export const FooterButton = (props: TFooterButton) => {
const { side, href, ...rest } = props;
let buttonProps = Object();
if (typeof href !== 'undefined') {
buttonProps = { href, as: 'a', target: '_blank', rel: 'noopener noreferrer' };
}
const { content, title, side, ...rest } = props;
const placement = side === 'left' ? 'top-end' : side === 'right' ? 'top-start' : undefined;
return (
<AnimatedDiv
p={0}
w="auto"
d="flex"
flexGrow={0}
float={side}
flexShrink={0}
maxWidth="100%"
flexBasis="auto"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.6 }}>
<Button size="xs" variant="ghost" {...buttonProps} {...rest} />
</AnimatedDiv>
<Menu placement={placement}>
<MenuButton
as={Button}
size="xs"
variant="ghost"
aria-label={typeof title === 'string' ? title : undefined}>
{title}
</MenuButton>
<MenuList
px={6}
py={4}
textAlign={side}
mx={{ base: 1, lg: 2 }}
maxW={{ base: '100vw', lg: '50vw' }}
{...rest}>
<Markdown content={content} />
</MenuList>
</Menu>
);
};

View File

@@ -1,28 +0,0 @@
import { Drawer, DrawerBody, DrawerOverlay, DrawerContent } from '@chakra-ui/react';
import { Markdown } from '~/components';
import type { TFooterContent } from './types';
export const FooterContent = (props: TFooterContent) => {
const { isOpen, onClose, content, side = 'left', ...rest } = props;
return (
<Drawer placement="bottom" isOpen={isOpen} onClose={onClose}>
<DrawerOverlay>
<DrawerContent
px={6}
py={4}
w="auto"
borderBottom="1px"
display="flex"
maxWidth="100%"
flexBasis="auto"
justifyContent={side === 'left' ? 'flex-start' : 'flex-end'}
{...rest}>
<DrawerBody textAlign={side}>
<Markdown content={content} />
</DrawerBody>
</DrawerContent>
</DrawerOverlay>
</Drawer>
);
};

View File

@@ -1,101 +1,56 @@
import { Box, Flex, useDisclosure } from '@chakra-ui/react';
import { FiCode } from '@meronex/icons/fi';
import { GoLinkExternal } from '@meronex/icons/go';
import stringFormat from 'string-format';
import dynamic from 'next/dynamic';
import { Button, Flex, Link } from '@chakra-ui/react';
import { If } from '~/components';
import { useConfig, useColorValue } from '~/context';
import { useStrf } from '~/hooks';
import { FooterButton } from './button';
import { FooterContent } from './content';
const CodeIcon = dynamic<MeronexIcon>(() => import('@meronex/icons/fi').then(i => i.FiCode));
const ExtIcon = dynamic<MeronexIcon>(() => import('@meronex/icons/go').then(i => i.GoLinkExternal));
export const Footer = () => {
const { web, content, primary_asn } = useConfig();
const help = useDisclosure();
const terms = useDisclosure();
const credit = useDisclosure();
const footerBg = useColorValue('blackAlpha.50', 'whiteAlpha.100');
const footerColor = useColorValue('black', 'white');
const contentBorder = useColorValue('blackAlpha.100', 'whiteAlpha.200');
const extUrl = web.external_link.url.includes('{primary_asn}')
? stringFormat(web.external_link.url, { primary_asn })
: web.external_link.url ?? '/';
const extUrl = useStrf(web.external_link.url, { primary_asn }) ?? '/';
return (
<>
{web.help_menu.enable && (
<FooterContent
content={content.help_menu}
borderColor={contentBorder}
bg={footerBg}
side="left"
{...help}
/>
)}
{web.terms.enable && (
<FooterContent
content={content.terms}
borderColor={contentBorder}
bg={footerBg}
side="left"
{...terms}
/>
)}
{web.credit.enable && (
<FooterContent
borderColor={contentBorder}
content={content.credit}
bg={footerBg}
side="right"
{...credit}
/>
)}
<Flex
px={6}
w="100%"
as="footer"
bg={footerBg}
flexWrap="wrap"
textAlign="center"
alignItems="center"
color={footerColor}
py={{ base: 4, lg: 2 }}
justifyContent="space-between">
<If c={web.terms.enable}>
<FooterButton side="left" onClick={terms.onToggle} aria-label={web.terms.title}>
{web.terms.title}
</FooterButton>
</If>
<If c={web.help_menu.enable}>
<FooterButton side="left" onClick={help.onToggle} aria-label={web.help_menu.title}>
{web.help_menu.title}
</FooterButton>
</If>
<Flex
p={0}
flexGrow={0}
flexShrink={0}
maxWidth="100%"
flexBasis="auto"
marginRight="auto"
/>
<If c={web.credit.enable}>
<FooterButton side="right" onClick={credit.onToggle} aria-label="Powered by hyperglass">
<FiCode />
</FooterButton>
</If>
<If c={web.external_link.enable}>
<FooterButton
size="xs"
side="right"
href={extUrl}
variant="ghost"
aria-label={web.external_link.title}
rightIcon={<Box as={GoLinkExternal} />}>
{web.external_link.title}
</FooterButton>
</If>
</Flex>
</>
<Flex
px={6}
w="100%"
zIndex={1}
as="footer"
bg={footerBg}
flexWrap="wrap"
textAlign="center"
alignItems="center"
color={footerColor}
py={{ base: 4, lg: 2 }}
justifyContent="space-between">
<If c={web.terms.enable}>
<FooterButton side="left" content={content.terms} title={web.terms.title} />
</If>
<If c={web.help_menu.enable}>
<FooterButton side="left" content={content.help_menu} title={web.help_menu.title} />
</If>
<Flex p={0} flexGrow={0} flexShrink={0} maxWidth="100%" flexBasis="auto" marginRight="auto" />
<If c={web.credit.enable}>
<FooterButton side="right" content={content.credit} title={<CodeIcon />} />
</If>
<If c={web.external_link.enable}>
<Button
size="xs"
as={Link}
isExternal
href={extUrl}
variant="ghost"
rightIcon={<ExtIcon />}
aria-label={web.external_link.title}>
{web.external_link.title}
</Button>
</If>
</Flex>
);
};

View File

@@ -1,16 +1,11 @@
import type { ButtonProps, DrawerProps, DrawerContentProps } from '@chakra-ui/react';
import type { MenuListProps } from '@chakra-ui/react';
type TFooterSide = 'left' | 'right';
export interface TFooterButton extends ButtonProps {
export interface TFooterButton extends Omit<MenuListProps, 'title'> {
side: TFooterSide;
href?: string;
}
export interface TFooterContent extends Omit<DrawerProps, 'children'>, DrawerContentProps {
isOpen: boolean;
title?: MenuListProps['children'];
content: string;
side: TFooterSide;
}
export type TFooterItems = 'help' | 'credit' | 'terms';

View File

@@ -1,4 +1,5 @@
import { chakra } from '@chakra-ui/react';
import { motion } from 'framer-motion';
export const AnimatedDiv = chakra(motion.div);
export const AnimatedDiv = motion.custom(chakra.div);
export const AnimatedForm = motion.custom(chakra.form);

View File

@@ -46,9 +46,9 @@ export const SubmitButton = forwardRef<HTMLDivElement, TSubmitButton>((props, re
} = props;
const _isDisabled = isDisabled || isLoading;
const bg = useColorValue('primary.500', 'primary.300');
const bgActive = useColorValue('primary.600', 'primary.400');
const bgHover = useColorValue('primary.400', 'primary.200');
const bg = useColorValue('primary.400', 'primary.500');
const bgActive = useColorValue('primary.500', 'primary.600');
const bgHover = useColorValue('primary.300', 'primary.400');
const color = useOpposingColor(bg);
const colorActive = useOpposingColor(bgActive);
const colorHover = useOpposingColor(bgHover);

View File

@@ -41,12 +41,12 @@ export const Debugger = () => {
position="relative"
justifyContent="center"
borderColor={borderColor}>
<Tag variantColor="gray">{colorMode.toUpperCase()}</Tag>
<Tag variantColor="teal">{mediaSize}</Tag>
<Button size="sm" variantColor="cyan" onClick={onConfigOpen}>
<Tag colorScheme="gray">{colorMode.toUpperCase()}</Tag>
<Tag colorScheme="teal">{mediaSize}</Tag>
<Button size="sm" colorScheme="cyan" onClick={onConfigOpen}>
View Config
</Button>
<Button size="sm" variantColor="purple" onClick={onThemeOpen}>
<Button size="sm" colorScheme="purple" onClick={onThemeOpen}>
View Theme
</Button>
</Stack>

View File

@@ -33,7 +33,7 @@ export const CommunitySelect = (props: TCommunitySelect) => {
const options = useMemo(() => buildOptions(communities), [communities.length]);
function handleChange(e: TSelectOption | TSelectOption[]): void {
if (!Array.isArray(e)) {
if (!Array.isArray(e) && e !== null) {
onChange({ field: name, value: e.value });
}
}

View File

@@ -24,7 +24,7 @@ export const QueryLocation = (props: TQuerySelectField) => {
const options = useMemo(() => buildOptions(networks), [networks.length]);
function handleChange(e: TSelectOption): void {
if (Array.isArray(e.value)) {
if (Array.isArray(e?.value) && e !== null) {
const value = e.value.map(sel => sel);
onChange({ field: 'query_location', value });
}

View File

@@ -56,11 +56,11 @@ export const QueryTarget = (props: TQueryTarget) => {
bg={bg}
size="lg"
color={color}
borderRadius="md"
onBlur={handleBlur}
onFocus={handleBlur}
value={displayValue}
borderColor={border}
borderRadius="0.25rem"
onChange={handleChange}
aria-label={placeholder}
onKeyDown={handleKeyDown}

View File

@@ -18,7 +18,9 @@ export const QueryType = (props: TQuerySelectField) => {
const options = useMemo(() => buildOptions(queries.list), [queries.list.length]);
function handleChange(e: TSelectOption): void {
onChange({ field: 'query_type', value: e.value });
if (e !== null) {
onChange({ field: 'query_type', value: e.value });
}
}
return (

View File

@@ -14,7 +14,9 @@ export const QueryVrf = (props: TQueryVrf) => {
const options = useMemo(() => buildOptions(vrfs), [vrfs.length]);
function handleChange(e: TSelectOption): void {
onChange({ field: 'query_vrf', value: e.value });
if (e !== null) {
onChange({ field: 'query_vrf', value: e.value });
}
}
return (

View File

@@ -25,14 +25,7 @@ export const Frame = (props: TFrame) => {
return (
<>
<Flex
bg={bg}
w="100%"
color={color}
flexDir="column"
minHeight="100vh"
ref={containerRef}
{...props}>
<Flex bg={bg} w="100%" color={color} flexDir="column" minHeight="100vh" ref={containerRef}>
<Flex px={2} flex="0 1 auto" flexDirection="column">
<Header resetForm={resetForm} />
</Flex>
@@ -49,9 +42,9 @@ export const Frame = (props: TFrame) => {
{...props}
/>
<Footer />
<If c={developer_mode}>
{/* <If c={developer_mode}>
<Debugger />
</If>
</If> */}
</Flex>
<If c={web.greeting.enable && !greetingAck}>
<Greeting onClickThrough={setGreetingAck} />

View File

@@ -1,4 +1,4 @@
import { motion, AnimatePresence } from 'framer-motion';
import { AnimatePresence } from 'framer-motion';
import { If, HyperglassForm, Results } from '~/components';
import { useGlobalState } from '~/context';
import { all } from '~/util';
@@ -28,13 +28,7 @@ export const Layout: React.FC = () => {
</If>
<AnimatePresence>
<If c={!isSubmitting.value}>
<motion.div
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3 }}
exit={{ opacity: 0, x: -300 }}
initial={{ opacity: 0, y: 300 }}>
<HyperglassForm />
</motion.div>
<HyperglassForm />
</If>
</AnimatePresence>
</Frame>

View File

@@ -1,9 +1,10 @@
import { useEffect, useMemo, useState } from 'react';
import { Box, Flex } from '@chakra-ui/react';
import { Flex } from '@chakra-ui/react';
import { useForm } from 'react-hook-form';
import { uniqWith } from 'lodash';
import * as yup from 'yup';
import {
AnimatedForm,
FormRow,
QueryVrf,
FormField,
@@ -248,13 +249,16 @@ export const HyperglassForm = () => {
Object.keys(errors).length >= 1 && console.error(errors);
return (
<Box
<AnimatedForm
p={0}
my={4}
w="100%"
as="form"
mx="auto"
textAlign="left"
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3 }}
exit={{ opacity: 0, x: -300 }}
initial={{ opacity: 0, y: 300 }}
onSubmit={handleSubmit(onSubmit)}
maxW={{ base: '100%', lg: '75%' }}>
<FormRow>
@@ -330,6 +334,6 @@ export const HyperglassForm = () => {
<SubmitButton isLoading={isSubmitting.value} />
</Flex>
</FormRow>
</Box>
</AnimatedForm>
);
};

View File

@@ -23,13 +23,13 @@ export const useControlStyle = (base: TStyles, state: TControl): TStyles => {
const { colorMode } = useSelectContext();
const borderHover = useColorValue(
useToken('colors', 'gray.300'),
useToken('colors', 'whiteAlpha.200'),
useToken('colors', 'whiteAlpha.400'),
);
const focusBorder = useToken('colors', 'secondary.500');
const focusBorder = useColorValue(useToken('colors', 'blue.500'), useToken('colors', 'blue.300'));
const invalidBorder = useColorValue(useToken('colors', 'red.500'), useToken('colors', 'red.300'));
const borderColor = useColorValue('inherit', useToken('colors', 'whiteAlpha.50'));
const borderRadius = useToken('radii', 'md');
const minHeight = useToken('sizes', 'lg');
const minHeight = useToken('space', 12);
const color = useColorValue(useToken('colors', 'black'), useToken('colors', 'whiteAlpha.800'));
const backgroundColor = useColorValue(
useToken('colors', 'white'),
@@ -40,9 +40,12 @@ export const useControlStyle = (base: TStyles, state: TControl): TStyles => {
borderRadius,
color,
minHeight,
transition: 'all 0.2s',
borderColor: isFocused ? focusBorder : borderColor,
boxShadow: isFocused ? `0 0 0 1px ${focusBorder}` : undefined,
'&:hover': { borderColor: isFocused ? focusBorder : borderHover },
'&:hover > div > span': { backgroundColor: borderHover },
'&:focus': { borderColor: focusBorder },
'&.invalid': { borderColor: invalidBorder, boxShadow: `0 0 0 1px ${invalidBorder}` },
};
return useMemo(() => mergeWith({}, base, styles), [colorMode, isFocused]);
@@ -52,7 +55,7 @@ export const useMenuStyle = (base: TStyles, state: TMenu): TStyles => {
const { colorMode, isOpen } = useSelectContext();
const backgroundColor = useColorValue(
useToken('colors', 'white'),
useToken('colors', 'whiteFaded.50'),
useToken('colors', 'blackFaded.800'),
);
const borderRadius = useToken('radii', 'md');
const styles = { borderRadius, backgroundColor };
@@ -87,7 +90,7 @@ export const useMenuListStyle = (base: TStyles, state: TMenuList): TStyles => {
};
export const useOptionStyle = (base: TStyles, state: TOption): TStyles => {
const { isFocused } = state;
const { isFocused, isSelected, isDisabled } = state;
const { colorMode, isOpen } = useSelectContext();
const fontSize = useToken('fontSizes', 'lg');
const disabled = useToken('colors', 'whiteAlpha.400');
@@ -103,31 +106,42 @@ export const useOptionStyle = (base: TStyles, state: TOption): TStyles => {
useToken('colors', 'primary.600'),
useToken('colors', 'primary.400'),
);
const disabledColor = useOpposingColor(disabled);
const selectedColor = useOpposingColor(selected);
const focusedColor = useOpposingColor(focused);
const activeColor = useOpposingColor(active);
const backgroundColor = useMemo(() => {
let bg = 'transparent';
switch (true) {
case isDisabled:
bg = disabled;
break;
case isSelected:
bg = selected;
break;
case isFocused:
bg = focused;
break;
}
return bg;
}, [isDisabled, isFocused, isSelected]);
const color = useOpposingColor(backgroundColor);
const styles = {
backgroundColor: state.isDisabled
? disabled
: state.isSelected
? selected
: state.isFocused
? focused
: 'transparent',
color: state.isDisabled
? disabledColor
: state.isSelected
? selectedColor
: state.isFocused
? focusedColor
: 'transparent',
backgroundColor,
color,
fontSize,
'&:focus': { backgroundColor: active, color: activeColor },
'&:active': { backgroundColor: active, color: activeColor },
};
return useMemo(() => mergeWith({}, base, styles), [colorMode, isFocused, isOpen]);
return useMemo(() => mergeWith({}, base, styles), [
isOpen,
colorMode,
isFocused,
isDisabled,
isSelected,
]);
};
export const useIndicatorSeparatorStyle = (base: TStyles, state: TIndicator): TStyles => {
@@ -142,8 +156,9 @@ export const useIndicatorSeparatorStyle = (base: TStyles, state: TIndicator): TS
export const usePlaceholderStyle = (base: TStyles, state: TPlaceholder): TStyles => {
const { colorMode } = useSelectContext();
const color = useColorValue(useToken('colors', 'whiteAlpha.700'), useToken('colors', 'gray.600'));
return useMemo(() => mergeWith({}, base, { color }), [colorMode]);
const color = useColorValue(useToken('colors', 'gray.600'), useToken('colors', 'whiteAlpha.700'));
const fontSize = useToken('fontSizes', 'lg');
return useMemo(() => mergeWith({}, base, { color, fontSize }), [colorMode]);
};
export const useSingleValueStyle = (props: TStyles) => {

View File

@@ -1,7 +1,8 @@
export type TSelectOption = {
label: string;
value: string | string[];
};
group?: string;
} | null;
export type TSelectOptionGroup = {
label: string;

View File

@@ -1,77 +0,0 @@
import dayjs from 'dayjs';
import relativeTimePlugin from 'dayjs/plugin/relativeTime';
import utcPlugin from 'dayjs/plugin/utc';
dayjs.extend(relativeTimePlugin);
dayjs.extend(utcPlugin);
const formatAsPath = path => {
return path.join(' → ');
};
const formatCommunities = comms => {
const commsStr = comms.map(c => ` - ${c}`);
return '\n' + commsStr.join('\n');
};
const formatBool = val => {
let fmt = '';
if (val === true) {
fmt = 'yes';
} else if (val === false) {
fmt = 'no';
}
return fmt;
};
const formatTime = val => {
const now = dayjs.utc();
const then = now.subtract(val, 'seconds');
const timestamp = then.toString().replace('GMT', 'UTC');
const relative = now.to(then, true);
return `${relative} (${timestamp})`;
};
export const tableToString = (target, data, config) => {
try {
const formatRpkiState = val => {
const rpkiStateNames = [
config.web.text.rpki_invalid,
config.web.text.rpki_valid,
config.web.text.rpki_unknown,
config.web.text.rpki_unverified,
];
return rpkiStateNames[val];
};
const tableFormatMap = {
age: formatTime,
active: formatBool,
as_path: formatAsPath,
communities: formatCommunities,
rpki_state: formatRpkiState,
};
let tableStringParts = [`Routes For: ${target}`, `Timestamp: ${data.timestamp} UTC`];
data.output.routes.map(route => {
config.parsed_data_fields.map(field => {
const [header, accessor, align] = field;
if (align !== null) {
let value = route[accessor];
const fmtFunc = tableFormatMap[accessor] ?? String;
value = fmtFunc(value);
if (accessor === 'prefix') {
tableStringParts.push(` - ${header}: ${value}`);
} else {
tableStringParts.push(` - ${header}: ${value}`);
}
}
});
});
return tableStringParts.join('\n');
} catch (err) {
console.error(err);
return `An error occurred while parsing the output: '${err.message}'`;
}
};

View File

@@ -1,3 +1,2 @@
export * from './common';
export * from './formatters';
export * from './theme';