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

JS style updates [skip ci]

This commit is contained in:
checktheroads
2020-10-07 09:41:58 -07:00
parent 3743f1a4c5
commit 5e61e097a2
73 changed files with 995 additions and 1385 deletions

9
hyperglass/ui/.eslintrc Normal file
View File

@@ -0,0 +1,9 @@
{
"extends": "@upstatement/eslint-config/react",
"plugins": ["react-hooks"],
"settings": { "import/resolver": { "typescript": {} } },
"rules": {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": ["warn"]
}
}

View File

@@ -1,4 +1,4 @@
import * as React from "react"; import * as React from 'react';
import { import {
Flex, Flex,
Icon, Icon,
@@ -8,39 +8,39 @@ import {
PopoverTrigger, PopoverTrigger,
Text, Text,
Tooltip, Tooltip,
useColorMode useColorMode,
} from "@chakra-ui/core"; } from '@chakra-ui/core';
import { MdLastPage } from "@meronex/icons/md"; import { MdLastPage } from '@meronex/icons/md';
import dayjs from "dayjs"; import dayjs from 'dayjs';
import relativeTimePlugin from "dayjs/plugin/relativeTime"; import relativeTimePlugin from 'dayjs/plugin/relativeTime';
import utcPlugin from "dayjs/plugin/utc"; import utcPlugin from 'dayjs/plugin/utc';
import { useConfig } from "app/context"; import { useConfig } from 'app/context';
import { Table } from "app/components"; import { Table } from 'app/components';
dayjs.extend(relativeTimePlugin); dayjs.extend(relativeTimePlugin);
dayjs.extend(utcPlugin); dayjs.extend(utcPlugin);
const isActiveColor = { const isActiveColor = {
true: { dark: "green.300", light: "green.500" }, true: { dark: 'green.300', light: 'green.500' },
false: { dark: "gray.300", light: "gray.500" } false: { dark: 'gray.300', light: 'gray.500' },
}; };
const arrowColor = { const arrowColor = {
true: { dark: "blackAlpha.500", light: "blackAlpha.500" }, true: { dark: 'blackAlpha.500', light: 'blackAlpha.500' },
false: { dark: "whiteAlpha.300", light: "blackAlpha.500" } false: { dark: 'whiteAlpha.300', light: 'blackAlpha.500' },
}; };
const rpkiIcon = ["not-allowed", "check-circle", "warning", "question"]; const rpkiIcon = ['not-allowed', 'check-circle', 'warning', 'question'];
const rpkiColor = { const rpkiColor = {
true: { true: {
dark: ["red.500", "green.600", "yellow.500", "gray.800"], dark: ['red.500', 'green.600', 'yellow.500', 'gray.800'],
light: ["red.500", "green.500", "yellow.500", "gray.600"] light: ['red.500', 'green.500', 'yellow.500', 'gray.600'],
}, },
false: { false: {
dark: ["red.300", "green.300", "yellow.300", "gray.300"], dark: ['red.300', 'green.300', 'yellow.300', 'gray.300'],
light: ["red.400", "green.500", "yellow.400", "gray.500"] light: ['red.400', 'green.500', 'yellow.400', 'gray.500'],
} },
}; };
const makeColumns = fields => { const makeColumns = fields => {
@@ -50,7 +50,7 @@ const makeColumns = fields => {
Header: header, Header: header,
accessor: accessor, accessor: accessor,
align: align, align: align,
hidden: false hidden: false,
}; };
if (align === null) { if (align === null) {
columnConfig.hidden = true; columnConfig.hidden = true;
@@ -68,22 +68,15 @@ const MonoField = ({ v, ...props }) => (
const Active = ({ isActive }) => { const Active = ({ isActive }) => {
const { colorMode } = useColorMode(); const { colorMode } = useColorMode();
return ( return (
<Icon <Icon name={isActive ? 'check-circle' : 'warning'} color={isActiveColor[isActive][colorMode]} />
name={isActive ? "check-circle" : "warning"}
color={isActiveColor[isActive][colorMode]}
/>
); );
}; };
const Age = ({ inSeconds }) => { const Age = ({ inSeconds }) => {
const now = dayjs.utc(); const now = dayjs.utc();
const then = now.subtract(inSeconds, "seconds"); const then = now.subtract(inSeconds, 'seconds');
return ( return (
<Tooltip <Tooltip hasArrow label={then.toString().replace('GMT', 'UTC')} placement="right">
hasArrow
label={then.toString().replace("GMT", "UTC")}
placement="right"
>
<Text fontSize="sm">{now.to(then, true)}</Text> <Text fontSize="sm">{now.to(then, true)}</Text>
</Tooltip> </Tooltip>
); );
@@ -91,9 +84,7 @@ const Age = ({ inSeconds }) => {
const Weight = ({ weight, winningWeight }) => { const Weight = ({ weight, winningWeight }) => {
const fixMeText = const fixMeText =
winningWeight === "low" winningWeight === 'low' ? 'Lower Weight is Preferred' : 'Higher Weight is Preferred';
? "Lower Weight is Preferred"
: "Higher Weight is Preferred";
return ( return (
<Tooltip hasArrow label={fixMeText} placement="right"> <Tooltip hasArrow label={fixMeText} placement="right">
<Text fontSize="sm" fontFamily="mono"> <Text fontSize="sm" fontFamily="mono">
@@ -113,22 +104,12 @@ const ASPath = ({ path, active }) => {
const asnStr = String(asn); const asnStr = String(asn);
i !== 0 && i !== 0 &&
paths.push( paths.push(
<Icon <Icon name="chevron-right" key={`separator-${i}`} color={arrowColor[active][colorMode]} />,
name="chevron-right"
key={`separator-${i}`}
color={arrowColor[active][colorMode]}
/>
); );
paths.push( paths.push(
<Text <Text fontSize="sm" as="span" whiteSpace="pre" fontFamily="mono" key={`as-${asnStr}-${i}`}>
fontSize="sm"
as="span"
whiteSpace="pre"
fontFamily="mono"
key={`as-${asnStr}-${i}`}
>
{asnStr} {asnStr}
</Text> </Text>,
); );
}); });
return paths; return paths;
@@ -152,13 +133,12 @@ const Communities = ({ communities }) => {
textAlign="left" textAlign="left"
p={4} p={4}
width="unset" width="unset"
color={colorMode === "dark" ? "white" : "black"} color={colorMode === 'dark' ? 'white' : 'black'}
fontFamily="mono" fontFamily="mono"
fontWeight="normal" fontWeight="normal"
whiteSpace="pre-wrap" whiteSpace="pre-wrap">
>
<PopoverArrow /> <PopoverArrow />
{communities.join("\n")} {communities.join('\n')}
</PopoverContent> </PopoverContent>
</Popover> </Popover>
)); ));
@@ -172,18 +152,11 @@ const RPKIState = ({ state, active }) => {
web.text.rpki_invalid, web.text.rpki_invalid,
web.text.rpki_valid, web.text.rpki_valid,
web.text.rpki_unknown, web.text.rpki_unknown,
web.text.rpki_unverified web.text.rpki_unverified,
]; ];
return ( return (
<Tooltip <Tooltip hasArrow placement="right" label={stateText[state] ?? stateText[3]}>
hasArrow <Icon name={rpkiIcon[state]} color={rpkiColor[active][colorMode][state]} />
placement="right"
label={stateText[state] ?? stateText[3]}
>
<Icon
name={rpkiIcon[state]}
color={rpkiColor[active][colorMode][state]}
/>
</Tooltip> </Tooltip>
); );
}; };
@@ -193,24 +166,16 @@ const Cell = ({ data, rawData, longestASN }) => {
prefix: <MonoField v={data.value} />, prefix: <MonoField v={data.value} />,
active: <Active isActive={data.value} />, active: <Active isActive={data.value} />,
age: <Age inSeconds={data.value} />, age: <Age inSeconds={data.value} />,
weight: ( weight: <Weight weight={data.value} winningWeight={rawData.winning_weight} />,
<Weight weight={data.value} winningWeight={rawData.winning_weight} />
),
med: <MonoField v={data.value} />, med: <MonoField v={data.value} />,
local_preference: <MonoField v={data.value} />, local_preference: <MonoField v={data.value} />,
as_path: ( as_path: <ASPath path={data.value} active={data.row.values.active} longestASN={longestASN} />,
<ASPath
path={data.value}
active={data.row.values.active}
longestASN={longestASN}
/>
),
communities: <Communities communities={data.value} />, communities: <Communities communities={data.value} />,
next_hop: <MonoField v={data.value} />, next_hop: <MonoField v={data.value} />,
source_as: <MonoField v={data.value} />, source_as: <MonoField v={data.value} />,
source_rid: <MonoField v={data.value} />, source_rid: <MonoField v={data.value} />,
peer_rid: <MonoField v={data.value} />, peer_rid: <MonoField v={data.value} />,
rpki_state: <RPKIState state={data.value} active={data.row.values.active} /> rpki_state: <RPKIState state={data.value} active={data.row.values.active} />,
}; };
return component[data.column.id] ?? <> </>; return component[data.column.id] ?? <> </>;
}; };
@@ -220,7 +185,7 @@ export const BGPTable = ({ children: data, ...props }) => {
const columns = makeColumns(config.parsed_data_fields); const columns = makeColumns(config.parsed_data_fields);
return ( return (
<Flex my={8} maxW={["100%", "100%", "100%", "100%"]} w="100%" {...props}> <Flex my={8} maxW={['100%', '100%', '100%', '100%']} w="100%" {...props}>
<Table <Table
columns={columns} columns={columns}
data={data.routes} data={data.routes}

View File

@@ -1,8 +1,8 @@
import * as React from "react"; import * as React from 'react';
import Countdown, { zeroPad } from "react-countdown"; import Countdown, { zeroPad } from 'react-countdown';
import { Text, useColorMode } from "@chakra-ui/core"; import { Text, useColorMode } from '@chakra-ui/core';
const bg = { dark: "white", light: "black" }; const bg = { dark: 'white', light: 'black' };
const Renderer = ({ hours, minutes, seconds, completed, props }) => { const Renderer = ({ hours, minutes, seconds, completed, props }) => {
if (completed) { if (completed) {
@@ -15,7 +15,7 @@ const Renderer = ({ hours, minutes, seconds, completed, props }) => {
<Text fontSize="xs" color="gray.500"> <Text fontSize="xs" color="gray.500">
{props.text} {props.text}
<Text as="span" fontSize="xs" color={bg[props.colorMode]}> <Text as="span" fontSize="xs" color={bg[props.colorMode]}>
{time.join(":")} {time.join(':')}
</Text> </Text>
</Text> </Text>
); );

View File

@@ -1,8 +1,8 @@
import * as React from "react"; import * as React from 'react';
import { Flex, useColorMode } from "@chakra-ui/core"; import { Flex, useColorMode } from '@chakra-ui/core';
const bg = { light: "white", dark: "original.dark" }; const bg = { light: 'white', dark: 'original.dark' };
const color = { light: "original.dark", dark: "white" }; const color = { light: 'original.dark', dark: 'white' };
export const CardBody = ({ onClick = () => false, children, ...props }) => { export const CardBody = ({ onClick = () => false, children, ...props }) => {
const { colorMode } = useColorMode(); const { colorMode } = useColorMode();
@@ -17,8 +17,7 @@ export const CardBody = ({ onClick = () => false, children, ...props }) => {
bg={bg[colorMode]} bg={bg[colorMode]}
color={color[colorMode]} color={color[colorMode]}
overflow="hidden" overflow="hidden"
{...props} {...props}>
>
{children} {children}
</Flex> </Flex>
); );

View File

@@ -1,5 +1,5 @@
import * as React from "react"; import * as React from 'react';
import { Flex } from "@chakra-ui/core"; import { Flex } from '@chakra-ui/core';
export const CardFooter = ({ children, ...props }) => ( export const CardFooter = ({ children, ...props }) => (
<Flex <Flex
@@ -12,8 +12,7 @@ export const CardFooter = ({ children, ...props }) => (
overflowY="hidden" overflowY="hidden"
flexDirection="row" flexDirection="row"
justifyContent="space-between" justifyContent="space-between"
{...props} {...props}>
>
{children} {children}
</Flex> </Flex>
); );

View File

@@ -1,7 +1,7 @@
import * as React from "react"; import * as React from 'react';
import { Flex, Text, useColorMode } from "@chakra-ui/core"; import { Flex, Text, useColorMode } from '@chakra-ui/core';
const bg = { light: "blackAlpha.50", dark: "whiteAlpha.100" }; const bg = { light: 'blackAlpha.50', dark: 'whiteAlpha.100' };
export const CardHeader = ({ children, ...props }) => { export const CardHeader = ({ children, ...props }) => {
const { colorMode } = useColorMode(); const { colorMode } = useColorMode();
@@ -13,8 +13,7 @@ export const CardHeader = ({ children, ...props }) => {
roundedTopLeft={4} roundedTopLeft={4}
roundedTopRight={4} roundedTopRight={4}
borderBottomWidth="1px" borderBottomWidth="1px"
{...props} {...props}>
>
<Text fontWeight="bold">{children}</Text> <Text fontWeight="bold">{children}</Text>
</Flex> </Flex>
); );

View File

@@ -1,3 +1,3 @@
export * from "./CardBody"; export * from './CardBody';
export * from "./CardFooter"; export * from './CardFooter';
export * from "./CardHeader"; export * from './CardHeader';

View File

@@ -1,85 +1,76 @@
import * as React from "react"; import * as React from 'react';
import { Text, useColorMode, useTheme } from "@chakra-ui/core"; import { Text, useColorMode, useTheme } from '@chakra-ui/core';
import Select from "react-select"; import Select from 'react-select';
import { opposingColor } from "app/util"; import { opposingColor } from 'app/util';
export const ChakraSelect = React.forwardRef( export const ChakraSelect = React.forwardRef(
( ({ placeholder = 'Select...', isFullWidth, size, children, ...props }, ref) => {
{ placeholder = "Select...", isFullWidth, size, children, ...props },
ref
) => {
const theme = useTheme(); const theme = useTheme();
const { colorMode } = useColorMode(); const { colorMode } = useColorMode();
const sizeMap = { const sizeMap = {
lg: { height: theme.space[12] }, lg: { height: theme.space[12] },
md: { height: theme.space[10] }, md: { height: theme.space[10] },
sm: { height: theme.space[8] } sm: { height: theme.space[8] },
}; };
const colorSetPrimaryBg = { const colorSetPrimaryBg = {
dark: theme.colors.primary[300], dark: theme.colors.primary[300],
light: theme.colors.primary[500] light: theme.colors.primary[500],
}; };
const colorSetPrimaryColor = opposingColor( const colorSetPrimaryColor = opposingColor(theme, colorSetPrimaryBg[colorMode]);
theme,
colorSetPrimaryBg[colorMode]
);
const bg = { const bg = {
dark: theme.colors.whiteAlpha[100], dark: theme.colors.whiteAlpha[100],
light: theme.colors.white light: theme.colors.white,
}; };
const color = { const color = {
dark: theme.colors.whiteAlpha[800], dark: theme.colors.whiteAlpha[800],
light: theme.colors.black light: theme.colors.black,
}; };
const borderFocused = theme.colors.secondary[500]; const borderFocused = theme.colors.secondary[500];
const borderDisabled = theme.colors.whiteAlpha[100]; const borderDisabled = theme.colors.whiteAlpha[100];
const border = { const border = {
dark: theme.colors.whiteAlpha[50], dark: theme.colors.whiteAlpha[50],
light: theme.colors.gray[100] light: theme.colors.gray[100],
}; };
const borderRadius = theme.space[1]; const borderRadius = theme.space[1];
const hoverColor = { const hoverColor = {
dark: theme.colors.whiteAlpha[200], dark: theme.colors.whiteAlpha[200],
light: theme.colors.gray[300] light: theme.colors.gray[300],
}; };
const { height } = sizeMap[size]; const { height } = sizeMap[size];
const optionBgActive = { const optionBgActive = {
dark: theme.colors.primary[400], dark: theme.colors.primary[400],
light: theme.colors.primary[600] light: theme.colors.primary[600],
}; };
const optionBgColor = opposingColor(theme, optionBgActive[colorMode]); const optionBgColor = opposingColor(theme, optionBgActive[colorMode]);
const optionSelectedBg = { const optionSelectedBg = {
dark: theme.colors.whiteAlpha[400], dark: theme.colors.whiteAlpha[400],
light: theme.colors.blackAlpha[400] light: theme.colors.blackAlpha[400],
}; };
const optionSelectedColor = opposingColor( const optionSelectedColor = opposingColor(theme, optionSelectedBg[colorMode]);
theme,
optionSelectedBg[colorMode]
);
const selectedDisabled = theme.colors.whiteAlpha[400]; const selectedDisabled = theme.colors.whiteAlpha[400];
const placeholderColor = { const placeholderColor = {
dark: theme.colors.whiteAlpha[700], dark: theme.colors.whiteAlpha[700],
light: theme.colors.gray[600] light: theme.colors.gray[600],
}; };
const menuBg = { const menuBg = {
dark: theme.colors.blackFaded[800], dark: theme.colors.blackFaded[800],
light: theme.colors.whiteFaded[50] light: theme.colors.whiteFaded[50],
}; };
const menuColor = { const menuColor = {
dark: theme.colors.white, dark: theme.colors.white,
light: theme.colors.blackAlpha[800] light: theme.colors.blackAlpha[800],
}; };
const scrollbar = { const scrollbar = {
dark: theme.colors.whiteAlpha[300], dark: theme.colors.whiteAlpha[300],
light: theme.colors.blackAlpha[300] light: theme.colors.blackAlpha[300],
}; };
const scrollbarHover = { const scrollbarHover = {
dark: theme.colors.whiteAlpha[400], dark: theme.colors.whiteAlpha[400],
light: theme.colors.blackAlpha[400] light: theme.colors.blackAlpha[400],
}; };
const scrollbarBg = { const scrollbarBg = {
dark: theme.colors.whiteAlpha[50], dark: theme.colors.whiteAlpha[50],
light: theme.colors.blackAlpha[50] light: theme.colors.blackAlpha[50],
}; };
return ( return (
<Select <Select
@@ -89,7 +80,7 @@ export const ChakraSelect = React.forwardRef(
...base, ...base,
minHeight: height, minHeight: height,
borderRadius: borderRadius, borderRadius: borderRadius,
width: "100%" width: '100%',
}), }),
control: (base, state) => ({ control: (base, state) => ({
...base, ...base,
@@ -102,29 +93,29 @@ export const ChakraSelect = React.forwardRef(
? borderFocused ? borderFocused
: border[colorMode], : border[colorMode],
borderRadius: borderRadius, borderRadius: borderRadius,
"&:hover": { '&:hover': {
borderColor: hoverColor[colorMode] borderColor: hoverColor[colorMode],
} },
}), }),
menu: base => ({ menu: base => ({
...base, ...base,
backgroundColor: menuBg[colorMode], backgroundColor: menuBg[colorMode],
borderRadius: borderRadius borderRadius: borderRadius,
}), }),
menuList: base => ({ menuList: base => ({
...base, ...base,
"&::-webkit-scrollbar": { width: "5px" }, '&::-webkit-scrollbar': { width: '5px' },
"&::-webkit-scrollbar-track": { '&::-webkit-scrollbar-track': {
backgroundColor: scrollbarBg[colorMode] backgroundColor: scrollbarBg[colorMode],
}, },
"&::-webkit-scrollbar-thumb": { '&::-webkit-scrollbar-thumb': {
backgroundColor: scrollbar[colorMode] backgroundColor: scrollbar[colorMode],
}, },
"&::-webkit-scrollbar-thumb:hover": { '&::-webkit-scrollbar-thumb:hover': {
backgroundColor: scrollbarHover[colorMode] backgroundColor: scrollbarHover[colorMode],
}, },
"-ms-overflow-style": { display: "none" } '-ms-overflow-style': { display: 'none' },
}), }),
option: (base, state) => ({ option: (base, state) => ({
...base, ...base,
@@ -134,7 +125,7 @@ export const ChakraSelect = React.forwardRef(
? optionSelectedBg[colorMode] ? optionSelectedBg[colorMode]
: state.isFocused : state.isFocused
? colorSetPrimaryBg[colorMode] ? colorSetPrimaryBg[colorMode]
: "transparent", : 'transparent',
color: state.isDisabled color: state.isDisabled
? selectedDisabled ? selectedDisabled
: state.isFocused : state.isFocused
@@ -143,62 +134,57 @@ export const ChakraSelect = React.forwardRef(
? optionSelectedColor ? optionSelectedColor
: menuColor[colorMode], : menuColor[colorMode],
fontSize: theme.fontSizes[size], fontSize: theme.fontSizes[size],
"&:active": { '&:active': {
backgroundColor: optionBgActive[colorMode], backgroundColor: optionBgActive[colorMode],
color: optionBgColor color: optionBgColor,
} },
}), }),
indicatorSeparator: base => ({ indicatorSeparator: base => ({
...base, ...base,
backgroundColor: placeholderColor[colorMode] backgroundColor: placeholderColor[colorMode],
}), }),
dropdownIndicator: base => ({ dropdownIndicator: base => ({
...base, ...base,
color: placeholderColor[colorMode], color: placeholderColor[colorMode],
"&:hover": { '&:hover': {
color: color[colorMode] color: color[colorMode],
} },
}), }),
valueContainer: base => ({ valueContainer: base => ({
...base, ...base,
paddingLeft: theme.space[4], paddingLeft: theme.space[4],
paddingRight: theme.space[4] paddingRight: theme.space[4],
}), }),
multiValue: base => ({ multiValue: base => ({
...base, ...base,
backgroundColor: colorSetPrimaryBg[colorMode] backgroundColor: colorSetPrimaryBg[colorMode],
}), }),
multiValueLabel: base => ({ multiValueLabel: base => ({
...base, ...base,
color: colorSetPrimaryColor color: colorSetPrimaryColor,
}), }),
multiValueRemove: base => ({ multiValueRemove: base => ({
...base, ...base,
color: colorSetPrimaryColor, color: colorSetPrimaryColor,
"&:hover": { '&:hover': {
color: colorSetPrimaryColor, color: colorSetPrimaryColor,
backgroundColor: "inherit" backgroundColor: 'inherit',
} },
}), }),
singleValue: base => ({ singleValue: base => ({
...base, ...base,
color: color[colorMode], color: color[colorMode],
fontSize: theme.fontSizes[size] fontSize: theme.fontSizes[size],
}) }),
}} }}
placeholder={ placeholder={
<Text <Text color={placeholderColor[colorMode]} fontSize={size} fontFamily={theme.fonts.body}>
color={placeholderColor[colorMode]}
fontSize={size}
fontFamily={theme.fonts.body}
>
{placeholder} {placeholder}
</Text> </Text>
} }
{...props} {...props}>
>
{children} {children}
</Select> </Select>
); );
} },
); );

View File

@@ -1,10 +1,10 @@
import * as React from "react"; import * as React from 'react';
import { Box, useColorMode } from "@chakra-ui/core"; import { Box, useColorMode } from '@chakra-ui/core';
export const CodeBlock = ({ children }) => { export const CodeBlock = ({ children }) => {
const { colorMode } = useColorMode(); const { colorMode } = useColorMode();
const bg = { dark: "gray.800", light: "blackAlpha.100" }; const bg = { dark: 'gray.800', light: 'blackAlpha.100' };
const color = { dark: "white", light: "black" }; const color = { dark: 'white', light: 'black' };
return ( return (
<Box <Box
fontFamily="mono" fontFamily="mono"
@@ -17,8 +17,7 @@ export const CodeBlock = ({ children }) => {
color={color[colorMode]} color={color[colorMode]}
fontSize="sm" fontSize="sm"
whiteSpace="pre-wrap" whiteSpace="pre-wrap"
as="pre" as="pre">
>
{children} {children}
</Box> </Box>
); );

View File

@@ -1,43 +1,41 @@
import * as React from "react"; import * as React from 'react';
import { forwardRef } from "react"; import { forwardRef } from 'react';
import { Button, useColorMode } from "@chakra-ui/core"; import { Button, useColorMode } from '@chakra-ui/core';
const Sun = ({ color, size = "1.5rem", ...props }) => ( const Sun = ({ color, size = '1.5rem', ...props }) => (
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512" viewBox="0 0 512 512"
style={{ style={{
height: size, height: size,
width: size width: size,
}} }}
strokeWidth={0} strokeWidth={0}
stroke="currentColor" stroke="currentColor"
fill="currentColor" fill="currentColor"
{...props} {...props}>
>
<path <path
d="M256 32a224 224 0 00-161.393 69.035h323.045A224 224 0 00256 32zM79.148 118.965a224 224 0 00-16.976 25.16H449.74a224 224 0 00-16.699-25.16H79.148zm-27.222 45.16A224 224 0 0043.3 186.25h425.271a224 224 0 00-8.586-22.125H51.926zM36.783 210.25a224 224 0 00-3.02 19.125h444.368a224 224 0 00-3.113-19.125H36.783zm-4.752 45.125A224 224 0 0032 256a224 224 0 00.64 16.5h446.534A224 224 0 00480 256a224 224 0 00-.021-.625H32.03zm4.67 45.125a224 224 0 003.395 15.125h431.578a224 224 0 003.861-15.125H36.701zm14.307 45.125a224 224 0 006.017 13.125H454.82a224 224 0 006.342-13.125H51.008zm26.316 45.125a224 224 0 009.04 11.125H425.86a224 224 0 008.727-11.125H77.324zm45.62 45.125A224 224 0 00136.247 445h239.89a224 224 0 0012.936-9.125h-266.13z" d="M256 32a224 224 0 00-161.393 69.035h323.045A224 224 0 00256 32zM79.148 118.965a224 224 0 00-16.976 25.16H449.74a224 224 0 00-16.699-25.16H79.148zm-27.222 45.16A224 224 0 0043.3 186.25h425.271a224 224 0 00-8.586-22.125H51.926zM36.783 210.25a224 224 0 00-3.02 19.125h444.368a224 224 0 00-3.113-19.125H36.783zm-4.752 45.125A224 224 0 0032 256a224 224 0 00.64 16.5h446.534A224 224 0 00480 256a224 224 0 00-.021-.625H32.03zm4.67 45.125a224 224 0 003.395 15.125h431.578a224 224 0 003.861-15.125H36.701zm14.307 45.125a224 224 0 006.017 13.125H454.82a224 224 0 006.342-13.125H51.008zm26.316 45.125a224 224 0 009.04 11.125H425.86a224 224 0 008.727-11.125H77.324zm45.62 45.125A224 224 0 00136.247 445h239.89a224 224 0 0012.936-9.125h-266.13z"
fill={color || "currentColor"} fill={color || 'currentColor'}
/> />
</svg> </svg>
); );
const Moon = ({ color, size = "1.5rem", ...props }) => ( const Moon = ({ color, size = '1.5rem', ...props }) => (
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16" viewBox="0 0 16 16"
style={{ style={{
height: size, height: size,
width: size width: size,
}} }}
strokeWidth={0} strokeWidth={0}
stroke="currentColor" stroke="currentColor"
fill="currentColor" fill="currentColor"
{...props} {...props}>
>
<path <path
d="M14.53 10.53a7 7 0 01-9.058-9.058A7.003 7.003 0 008 15a7.002 7.002 0 006.53-4.47z" d="M14.53 10.53a7 7 0 01-9.058-9.058A7.003 7.003 0 008 15a7.002 7.002 0 006.53-4.47z"
fill={color || "currentColor"} fill={color || 'currentColor'}
fillRule="evenodd" fillRule="evenodd"
clipRule="evenodd" clipRule="evenodd"
/> />
@@ -45,13 +43,13 @@ const Moon = ({ color, size = "1.5rem", ...props }) => (
); );
const iconMap = { dark: Moon, light: Sun }; const iconMap = { dark: Moon, light: Sun };
const outlineColor = { dark: "primary.300", light: "primary.600" }; const outlineColor = { dark: 'primary.300', light: 'primary.600' };
export const ColorModeToggle = forwardRef((props, ref) => { export const ColorModeToggle = forwardRef((props, ref) => {
const { colorMode, toggleColorMode } = useColorMode(); const { colorMode, toggleColorMode } = useColorMode();
const Icon = iconMap[colorMode]; const Icon = iconMap[colorMode];
const label = `Switch to ${colorMode === "light" ? "dark" : "light"} mode`; const label = `Switch to ${colorMode === 'light' ? 'dark' : 'light'} mode`;
return ( return (
<Button <Button
@@ -63,13 +61,12 @@ export const ColorModeToggle = forwardRef((props, ref) => {
borderWidth="1px" borderWidth="1px"
borderColor="transparent" borderColor="transparent"
_hover={{ _hover={{
backgroundColor: "unset", backgroundColor: 'unset',
borderColor: outlineColor[colorMode] borderColor: outlineColor[colorMode],
}} }}
color="current" color="current"
px={4} px={4}
{...props} {...props}>
>
<Icon /> <Icon />
</Button> </Button>
); );

View File

@@ -1,21 +1,15 @@
import * as React from "react"; import * as React from 'react';
import { useEffect } from "react"; import { useEffect } from 'react';
import { Text } from "@chakra-ui/core"; import { Text } from '@chakra-ui/core';
import { components } from "react-select"; import { components } from 'react-select';
import { ChakraSelect } from "app/components"; import { ChakraSelect } from 'app/components';
export const CommunitySelect = ({ export const CommunitySelect = ({ name, communities, onChange, register, unregister }) => {
name,
communities,
onChange,
register,
unregister
}) => {
const communitySelections = communities.map(c => { const communitySelections = communities.map(c => {
return { return {
value: c.community, value: c.community,
label: c.display_name, label: c.display_name,
description: c.description description: c.description,
}; };
}); });
const Option = ({ label, data, ...props }) => { const Option = ({ label, data, ...props }) => {
@@ -38,7 +32,7 @@ export const CommunitySelect = ({
size="lg" size="lg"
name={name} name={name}
onChange={e => { onChange={e => {
onChange({ field: name, value: e.value || "" }); onChange({ field: name, value: e.value || '' });
}} }}
options={communitySelections} options={communitySelections}
components={{ Option }} components={{ Option }}

View File

@@ -1,7 +1,7 @@
import * as React from "react"; import * as React from 'react';
import { Button, Icon, Tooltip, useClipboard } from "@chakra-ui/core"; import { Button, Icon, Tooltip, useClipboard } from '@chakra-ui/core';
export const CopyButton = ({ bg = "secondary", copyValue, ...props }) => { export const CopyButton = ({ bg = 'secondary', copyValue, ...props }) => {
const { onCopy, hasCopied } = useClipboard(copyValue); const { onCopy, hasCopied } = useClipboard(copyValue);
return ( return (
<Tooltip hasArrow label="Copy Output" placement="top"> <Tooltip hasArrow label="Copy Output" placement="top">
@@ -12,13 +12,8 @@ export const CopyButton = ({ bg = "secondary", copyValue, ...props }) => {
zIndex="dropdown" zIndex="dropdown"
onClick={onCopy} onClick={onCopy}
mx={1} mx={1}
{...props} {...props}>
> {hasCopied ? <Icon name="check" size="16px" /> : <Icon name="copy" size="16px" />}
{hasCopied ? (
<Icon name="check" size="16px" />
) : (
<Icon name="copy" size="16px" />
)}
</Button> </Button>
</Tooltip> </Tooltip>
); );

View File

@@ -1,4 +1,4 @@
import * as React from "react"; import * as React from 'react';
import { import {
Button, Button,
Modal, Modal,
@@ -11,36 +11,28 @@ import {
Tag, Tag,
useDisclosure, useDisclosure,
useColorMode, useColorMode,
useTheme useTheme,
} from "@chakra-ui/core"; } from '@chakra-ui/core';
import { useConfig, useMedia } from "app/context"; import { useConfig, useMedia } from 'app/context';
import { CodeBlock } from "app/components"; import { CodeBlock } from 'app/components';
const prettyMediaSize = { const prettyMediaSize = {
sm: "SMALL", sm: 'SMALL',
md: "MEDIUM", md: 'MEDIUM',
lg: "LARGE", lg: 'LARGE',
xl: "X-LARGE" xl: 'X-LARGE',
}; };
export const Debugger = () => { export const Debugger = () => {
const { const { isOpen: configOpen, onOpen: onConfigOpen, onClose: configClose } = useDisclosure();
isOpen: configOpen, const { isOpen: themeOpen, onOpen: onThemeOpen, onClose: themeClose } = useDisclosure();
onOpen: onConfigOpen,
onClose: configClose
} = useDisclosure();
const {
isOpen: themeOpen,
onOpen: onThemeOpen,
onClose: themeClose
} = useDisclosure();
const config = useConfig(); const config = useConfig();
const theme = useTheme(); const theme = useTheme();
const bg = { light: "white", dark: "black" }; const bg = { light: 'white', dark: 'black' };
const color = { light: "black", dark: "white" }; const color = { light: 'black', dark: 'white' };
const { colorMode } = useColorMode(); const { colorMode } = useColorMode();
const { mediaSize } = useMedia(); const { mediaSize } = useMedia();
const borderColor = { light: "gray.100", dark: "gray.600" }; const borderColor = { light: 'gray.100', dark: 'gray.600' };
return ( return (
<> <>
<Stack <Stack
@@ -55,8 +47,7 @@ export const Debugger = () => {
bottom={0} bottom={0}
justifyContent="center" justifyContent="center"
zIndex={1000} zIndex={1000}
maxW="100%" maxW="100%">
>
<Tag variantColor="gray">{colorMode.toUpperCase()}</Tag> <Tag variantColor="gray">{colorMode.toUpperCase()}</Tag>
<Tag variantColor="teal">{prettyMediaSize[mediaSize]}</Tag> <Tag variantColor="teal">{prettyMediaSize[mediaSize]}</Tag>
<Button size="sm" variantColor="cyan" onClick={onConfigOpen}> <Button size="sm" variantColor="cyan" onClick={onConfigOpen}>
@@ -73,8 +64,7 @@ export const Debugger = () => {
color={color[colorMode]} color={color[colorMode]}
py={4} py={4}
borderRadius="md" borderRadius="md"
maxW="90%" maxW="90%">
>
<ModalHeader>Loaded Configuration</ModalHeader> <ModalHeader>Loaded Configuration</ModalHeader>
<ModalCloseButton /> <ModalCloseButton />
<ModalBody> <ModalBody>
@@ -89,8 +79,7 @@ export const Debugger = () => {
color={color[colorMode]} color={color[colorMode]}
py={4} py={4}
borderRadius="md" borderRadius="md"
maxW="90%" maxW="90%">
>
<ModalHeader>Loaded Theme</ModalHeader> <ModalHeader>Loaded Theme</ModalHeader>
<ModalCloseButton /> <ModalCloseButton />
<ModalBody> <ModalBody>

View File

@@ -1,18 +1,18 @@
import * as React from "react"; import * as React from 'react';
import { useState } from "react"; import { useState } from 'react';
import { Flex, useColorMode } from "@chakra-ui/core"; import { Flex, useColorMode } from '@chakra-ui/core';
import { FiCode } from "@meronex/icons/fi"; import { FiCode } from '@meronex/icons/fi';
import { GoLinkExternal } from "@meronex/icons/go"; import { GoLinkExternal } from '@meronex/icons/go';
import format from "string-format"; import format from 'string-format';
import { useConfig } from "app/context"; import { useConfig } from 'app/context';
import { FooterButton } from "./FooterButton"; import { FooterButton } from './FooterButton';
import { FooterContent } from "./FooterContent"; import { FooterContent } from './FooterContent';
format.extend(String.prototype, {}); format.extend(String.prototype, {});
const footerBg = { light: "blackAlpha.50", dark: "whiteAlpha.100" }; const footerBg = { light: 'blackAlpha.50', dark: 'whiteAlpha.100' };
const footerColor = { light: "black", dark: "white" }; const footerColor = { light: 'black', dark: 'white' };
const contentBorder = { light: "blackAlpha.100", dark: "whiteAlpha.200" }; const contentBorder = { light: 'blackAlpha.100', dark: 'whiteAlpha.200' };
export const Footer = () => { export const Footer = () => {
const config = useConfig(); const config = useConfig();
@@ -21,23 +21,23 @@ export const Footer = () => {
const [termsVisible, showTerms] = useState(false); const [termsVisible, showTerms] = useState(false);
const [creditVisible, showCredit] = useState(false); const [creditVisible, showCredit] = useState(false);
const handleCollapse = i => { const handleCollapse = i => {
if (i === "help") { if (i === 'help') {
showTerms(false); showTerms(false);
showCredit(false); showCredit(false);
showHelp(!helpVisible); showHelp(!helpVisible);
} else if (i === "credit") { } else if (i === 'credit') {
showTerms(false); showTerms(false);
showHelp(false); showHelp(false);
showCredit(!creditVisible); showCredit(!creditVisible);
} else if (i === "terms") { } else if (i === 'terms') {
showHelp(false); showHelp(false);
showCredit(false); showCredit(false);
showTerms(!termsVisible); showTerms(!termsVisible);
} }
}; };
const extUrl = config.web.external_link.url.includes("{primary_asn}") const extUrl = config.web.external_link.url.includes('{primary_asn}')
? config.web.external_link.url.format({ primary_asn: config.primary_asn }) ? config.web.external_link.url.format({ primary_asn: config.primary_asn })
: config.web.external_link.url || "/"; : config.web.external_link.url || '/';
return ( return (
<> <>
{config.web.help_menu.enable && ( {config.web.help_menu.enable && (
@@ -80,23 +80,20 @@ export const Footer = () => {
alignItems="center" alignItems="center"
bg={footerBg[colorMode]} bg={footerBg[colorMode]}
color={footerColor[colorMode]} color={footerColor[colorMode]}
justifyContent="space-between" justifyContent="space-between">
>
{config.web.terms.enable && ( {config.web.terms.enable && (
<FooterButton <FooterButton
side="left" side="left"
onClick={() => handleCollapse("terms")} onClick={() => handleCollapse('terms')}
aria-label={config.web.terms.title} aria-label={config.web.terms.title}>
>
{config.web.terms.title} {config.web.terms.title}
</FooterButton> </FooterButton>
)} )}
{config.web.help_menu.enable && ( {config.web.help_menu.enable && (
<FooterButton <FooterButton
side="left" side="left"
onClick={() => handleCollapse("help")} onClick={() => handleCollapse('help')}
aria-label={config.web.help_menu.title} aria-label={config.web.help_menu.title}>
>
{config.web.help_menu.title} {config.web.help_menu.title}
</FooterButton> </FooterButton>
)} )}
@@ -111,9 +108,8 @@ export const Footer = () => {
{config.web.credit.enable && ( {config.web.credit.enable && (
<FooterButton <FooterButton
side="right" side="right"
onClick={() => handleCollapse("credit")} onClick={() => handleCollapse('credit')}
aria-label="Powered by hyperglass" aria-label="Powered by hyperglass">
>
<FiCode /> <FiCode />
</FooterButton> </FooterButton>
)} )}
@@ -126,8 +122,7 @@ export const Footer = () => {
rel="noopener noreferrer" rel="noopener noreferrer"
variant="ghost" variant="ghost"
rightIcon={GoLinkExternal} rightIcon={GoLinkExternal}
size="xs" size="xs">
>
{config.web.external_link.title} {config.web.external_link.title}
</FooterButton> </FooterButton>
)} )}

View File

@@ -1,29 +1,26 @@
import * as React from "react"; import * as React from 'react';
import { Button, Flex } from "@chakra-ui/core"; import { Button, Flex } from '@chakra-ui/core';
import { motion } from "framer-motion"; import { motion } from 'framer-motion';
const AnimatedFlex = motion.custom(Flex); const AnimatedFlex = motion.custom(Flex);
export const FooterButton = React.forwardRef( export const FooterButton = React.forwardRef(({ onClick, side, children, ...props }, ref) => {
({ onClick, side, children, ...props }, ref) => { return (
return ( <AnimatedFlex
<AnimatedFlex p={0}
p={0} w="auto"
w="auto" ref={ref}
ref={ref} flexGrow={0}
flexGrow={0} float={side}
float={side} flexShrink={0}
flexShrink={0} maxWidth="100%"
maxWidth="100%" flexBasis="auto"
flexBasis="auto" initial={{ opacity: 0 }}
initial={{ opacity: 0 }} animate={{ opacity: 1 }}
animate={{ opacity: 1 }} transition={{ duration: 0.6 }}>
transition={{ duration: 0.6 }} <Button size="xs" variant="ghost" onClick={onClick} {...props}>
> {children}
<Button size="xs" variant="ghost" onClick={onClick} {...props}> </Button>
{children} </AnimatedFlex>
</Button> );
</AnimatedFlex> });
);
}
);

View File

@@ -1,10 +1,10 @@
import * as React from "react"; import * as React from 'react';
import { forwardRef } from "react"; import { forwardRef } from 'react';
import { Box, Collapse } from "@chakra-ui/core"; import { Box, Collapse } from '@chakra-ui/core';
import { Markdown } from "app/components/Markdown"; import { Markdown } from 'app/components/Markdown';
export const FooterContent = forwardRef( export const FooterContent = forwardRef(
({ isOpen = false, content, side = "left", title, ...props }, ref) => { ({ isOpen = false, content, side = 'left', title, ...props }, ref) => {
return ( return (
<Collapse <Collapse
px={6} px={6}
@@ -16,13 +16,12 @@ export const FooterContent = forwardRef(
maxWidth="100%" maxWidth="100%"
isOpen={isOpen} isOpen={isOpen}
flexBasis="auto" flexBasis="auto"
justifyContent={side === "left" ? "flex-start" : "flex-end"} justifyContent={side === 'left' ? 'flex-start' : 'flex-end'}
{...props} {...props}>
>
<Box textAlign={side}> <Box textAlign={side}>
<Markdown content={content} /> <Markdown content={content} />
</Box> </Box>
</Collapse> </Collapse>
); );
} },
); );

View File

@@ -1 +1 @@
export * from "./Footer"; export * from './Footer';

View File

@@ -1,13 +1,7 @@
import * as React from "react"; import * as React from 'react';
import { import { Flex, FormControl, FormLabel, FormErrorMessage, useColorMode } from '@chakra-ui/core';
Flex,
FormControl,
FormLabel,
FormErrorMessage,
useColorMode
} from "@chakra-ui/core";
const labelColor = { dark: "whiteAlpha.700", light: "blackAlpha.700" }; const labelColor = { dark: 'whiteAlpha.700', light: 'blackAlpha.700' };
export const FormField = ({ export const FormField = ({
label, label,
@@ -28,14 +22,13 @@ export const FormField = ({
<FormControl <FormControl
as={Flex} as={Flex}
flexDirection="column" flexDirection="column"
flex={["1 0 100%", "1 0 100%", "1 0 33.33%", "1 0 33.33%"]} flex={['1 0 100%', '1 0 100%', '1 0 33.33%', '1 0 33.33%']}
w="100%" w="100%"
maxW="100%" maxW="100%"
mx={2} mx={2}
my={[2, 2, 4, 4]} my={[2, 2, 4, 4]}
isInvalid={error && error.message} isInvalid={error && error.message}
{...props} {...props}>
>
<FormLabel <FormLabel
htmlFor={name} htmlFor={name}
color={labelColor[colorMode]} color={labelColor[colorMode]}
@@ -44,8 +37,7 @@ export const FormField = ({
display="flex" display="flex"
alignItems="center" alignItems="center"
justifyContent="space-between" justifyContent="space-between"
pr={0} pr={0}>
>
{label} {label}
{labelAddOn || null} {labelAddOn || null}
</FormLabel> </FormLabel>

View File

@@ -1,4 +1,4 @@
import * as React from "react"; import * as React from 'react';
import { import {
Button, Button,
Modal, Modal,
@@ -9,13 +9,13 @@ import {
ModalBody, ModalBody,
ModalCloseButton, ModalCloseButton,
useColorMode, useColorMode,
useDisclosure useDisclosure,
} from "@chakra-ui/core"; } from '@chakra-ui/core';
import { Markdown } from "app/components"; import { Markdown } from 'app/components';
import { motion } from "framer-motion"; import { motion } from 'framer-motion';
const bg = { light: "white", dark: "black" }; const bg = { light: 'white', dark: 'black' };
const color = { light: "black", dark: "white" }; const color = { light: 'black', dark: 'white' };
const AnimatedModalContent = motion.custom(ModalContent); const AnimatedModalContent = motion.custom(ModalContent);
const AnimatedModalOverlay = motion.custom(ModalOverlay); const AnimatedModalOverlay = motion.custom(ModalOverlay);
@@ -36,8 +36,7 @@ export const Greeting = ({ greetingConfig, content, onClickThrough }) => {
size="full" size="full"
isCentered isCentered
closeOnOverlayClick={!greetingConfig.required} closeOnOverlayClick={!greetingConfig.required}
closeOnEsc={!greetingConfig.required} closeOnEsc={!greetingConfig.required}>
>
<AnimatedModalOverlay <AnimatedModalOverlay
initial={{ opacity: 0 }} initial={{ opacity: 0 }}
animate={{ opacity: 1 }} animate={{ opacity: 1 }}
@@ -51,8 +50,7 @@ export const Greeting = ({ greetingConfig, content, onClickThrough }) => {
color={color[colorMode]} color={color[colorMode]}
py={4} py={4}
borderRadius="md" borderRadius="md"
maxW={["95%", "75%", "75%", "75%"]} maxW={['95%', '75%', '75%', '75%']}>
>
<ModalHeader>{greetingConfig.title}</ModalHeader> <ModalHeader>{greetingConfig.title}</ModalHeader>
{!greetingConfig.required && <ModalCloseButton />} {!greetingConfig.required && <ModalCloseButton />}
<ModalBody> <ModalBody>

View File

@@ -1,54 +1,54 @@
import * as React from "react"; import * as React from 'react';
import { Flex, useColorMode } from "@chakra-ui/core"; import { Flex, useColorMode } from '@chakra-ui/core';
import { motion, AnimatePresence } from "framer-motion"; import { motion, AnimatePresence } from 'framer-motion';
import { useConfig, useHyperglassState, useMedia } from "app/context"; import { useConfig, useHyperglassState, useMedia } from 'app/context';
import { Title, ResetButton, ColorModeToggle } from "app/components"; import { Title, ResetButton, ColorModeToggle } from 'app/components';
const titleVariants = { const titleVariants = {
sm: { sm: {
fullSize: { scale: 1, marginLeft: 0 }, fullSize: { scale: 1, marginLeft: 0 },
smallLogo: { marginLeft: "auto" }, smallLogo: { marginLeft: 'auto' },
smallText: { marginLeft: "auto" } smallText: { marginLeft: 'auto' },
}, },
md: { md: {
fullSize: { scale: 1 }, fullSize: { scale: 1 },
smallLogo: { scale: 0.5 }, smallLogo: { scale: 0.5 },
smallText: { scale: 0.8 } smallText: { scale: 0.8 },
}, },
lg: { lg: {
fullSize: { scale: 1 }, fullSize: { scale: 1 },
smallLogo: { scale: 0.5 }, smallLogo: { scale: 0.5 },
smallText: { scale: 0.8 } smallText: { scale: 0.8 },
}, },
xl: { xl: {
fullSize: { scale: 1 }, fullSize: { scale: 1 },
smallLogo: { scale: 0.5 }, smallLogo: { scale: 0.5 },
smallText: { scale: 0.8 } smallText: { scale: 0.8 },
} },
}; };
const bg = { light: "white", dark: "black" }; const bg = { light: 'white', dark: 'black' };
const headerTransition = { const headerTransition = {
type: "spring", type: 'spring',
ease: "anticipate", ease: 'anticipate',
damping: 15, damping: 15,
stiffness: 100 stiffness: 100,
}; };
const titleJustify = { const titleJustify = {
true: ["flex-end", "flex-end", "center", "center"], true: ['flex-end', 'flex-end', 'center', 'center'],
false: ["flex-start", "flex-start", "center", "center"] false: ['flex-start', 'flex-start', 'center', 'center'],
}; };
const titleHeight = { const titleHeight = {
true: null, true: null,
false: [null, "20vh", "20vh", "20vh"] false: [null, '20vh', '20vh', '20vh'],
}; };
const resetButtonMl = { true: [null, 2, 2, 2], false: null }; const resetButtonMl = { true: [null, 2, 2, 2], false: null };
const widthMap = { const widthMap = {
text_only: "100%", text_only: '100%',
logo_only: ["90%", "90%", "50%", "50%"], logo_only: ['90%', '90%', '50%', '50%'],
logo_subtitle: ["90%", "90%", "50%", "50%"], logo_subtitle: ['90%', '90%', '50%', '50%'],
all: ["90%", "90%", "50%", "50%"] all: ['90%', '90%', '50%', '50%'],
}; };
export const Header = ({ layoutRef, ...props }) => { export const Header = ({ layoutRef, ...props }) => {
@@ -66,17 +66,13 @@ export const Header = ({ layoutRef, ...props }) => {
<AnimatedFlex <AnimatedFlex
layoutTransition={headerTransition} layoutTransition={headerTransition}
initial={{ opacity: 0, x: -50 }} initial={{ opacity: 0, x: -50 }}
animate={{ opacity: 1, x: 0, width: "unset" }} animate={{ opacity: 1, x: 0, width: 'unset' }}
exit={{ opacity: 0, x: -50 }} exit={{ opacity: 0, x: -50 }}
alignItems="center" alignItems="center"
mb={[null, "auto"]} mb={[null, 'auto']}
ml={resetButtonMl[isSubmitting]} ml={resetButtonMl[isSubmitting]}
display={isSubmitting ? "flex" : "none"} display={isSubmitting ? 'flex' : 'none'}>
> <AnimatedResetButton isSubmitting={isSubmitting} onClick={handleFormReset} />
<AnimatedResetButton
isSubmitting={isSubmitting}
onClick={handleFormReset}
/>
</AnimatedFlex> </AnimatedFlex>
</AnimatePresence> </AnimatePresence>
); );
@@ -84,25 +80,22 @@ export const Header = ({ layoutRef, ...props }) => {
<AnimatedFlex <AnimatedFlex
key="title" key="title"
px={1} px={1}
alignItems={ alignItems={isSubmitting ? 'center' : ['center', 'center', 'flex-end', 'flex-end']}
isSubmitting ? "center" : ["center", "center", "flex-end", "flex-end"]
}
positionTransition={headerTransition} positionTransition={headerTransition}
initial={{ scale: 0.5 }} initial={{ scale: 0.5 }}
animate={ animate={
isSubmitting && web.text.title_mode === "text_only" isSubmitting && web.text.title_mode === 'text_only'
? "smallText" ? 'smallText'
: isSubmitting && web.text.title_mode !== "text_only" : isSubmitting && web.text.title_mode !== 'text_only'
? "smallLogo" ? 'smallLogo'
: "fullSize" : 'fullSize'
} }
variants={titleVariants[mediaSize]} variants={titleVariants[mediaSize]}
justifyContent={titleJustify[isSubmitting]} justifyContent={titleJustify[isSubmitting]}
mt={[null, isSubmitting ? null : "auto"]} mt={[null, isSubmitting ? null : 'auto']}
maxW={widthMap[web.text.title_mode]} maxW={widthMap[web.text.title_mode]}
flex="1 0 0" flex="1 0 0"
minH={titleHeight[isSubmitting]} minH={titleHeight[isSubmitting]}>
>
<Title isSubmitting={isSubmitting} onClick={handleFormReset} /> <Title isSubmitting={isSubmitting} onClick={handleFormReset} />
</AnimatedFlex> </AnimatedFlex>
); );
@@ -113,9 +106,8 @@ export const Header = ({ layoutRef, ...props }) => {
alignItems="center" alignItems="center"
initial={{ opacity: 0 }} initial={{ opacity: 0 }}
animate={{ opacity: 1 }} animate={{ opacity: 1 }}
mb={[null, "auto"]} mb={[null, 'auto']}
mr={isSubmitting ? null : 2} mr={isSubmitting ? null : 2}>
>
<ColorModeToggle /> <ColorModeToggle />
</AnimatedFlex> </AnimatedFlex>
); );
@@ -124,14 +116,14 @@ export const Header = ({ layoutRef, ...props }) => {
sm: [title, resetButton, colorModeToggle], sm: [title, resetButton, colorModeToggle],
md: [resetButton, title, colorModeToggle], md: [resetButton, title, colorModeToggle],
lg: [resetButton, title, colorModeToggle], lg: [resetButton, title, colorModeToggle],
xl: [resetButton, title, colorModeToggle] xl: [resetButton, title, colorModeToggle],
}, },
true: { true: {
sm: [resetButton, colorModeToggle, title], sm: [resetButton, colorModeToggle, title],
md: [resetButton, title, colorModeToggle], md: [resetButton, title, colorModeToggle],
lg: [resetButton, title, colorModeToggle], lg: [resetButton, title, colorModeToggle],
xl: [resetButton, title, colorModeToggle] xl: [resetButton, title, colorModeToggle],
} },
}; };
return ( return (
<Flex <Flex
@@ -142,16 +134,14 @@ export const Header = ({ layoutRef, ...props }) => {
flex="0 1 auto" flex="0 1 auto"
bg={bg[colorMode]} bg={bg[colorMode]}
color="gray.500" color="gray.500"
{...props} {...props}>
>
<Flex <Flex
w="100%" w="100%"
mx="auto" mx="auto"
pt={6} pt={6}
justify="space-between" justify="space-between"
flex="1 0 auto" flex="1 0 auto"
alignItems={isSubmitting ? "center" : "flex-start"} alignItems={isSubmitting ? 'center' : 'flex-start'}>
>
{layout[isSubmitting][mediaSize]} {layout[isSubmitting][mediaSize]}
</Flex> </Flex>
</Flex> </Flex>

View File

@@ -1,4 +1,4 @@
import * as React from "react"; import * as React from 'react';
import { import {
IconButton, IconButton,
Modal, Modal,
@@ -9,10 +9,10 @@ import {
ModalCloseButton, ModalCloseButton,
useDisclosure, useDisclosure,
useColorMode, useColorMode,
useTheme useTheme,
} from "@chakra-ui/core"; } from '@chakra-ui/core';
import { motion, AnimatePresence } from "framer-motion"; import { motion, AnimatePresence } from 'framer-motion';
import { Markdown } from "app/components"; import { Markdown } from 'app/components';
const AnimatedIcon = motion.custom(IconButton); const AnimatedIcon = motion.custom(IconButton);
@@ -20,11 +20,11 @@ export const HelpModal = ({ item, name }) => {
const { isOpen, onOpen, onClose } = useDisclosure(); const { isOpen, onOpen, onClose } = useDisclosure();
const { colors } = useTheme(); const { colors } = useTheme();
const { colorMode } = useColorMode(); const { colorMode } = useColorMode();
const bg = { light: "whiteFaded.50", dark: "blackFaded.800" }; const bg = { light: 'whiteFaded.50', dark: 'blackFaded.800' };
const color = { light: "black", dark: "white" }; const color = { light: 'black', dark: 'white' };
const iconColor = { const iconColor = {
light: colors.primary[500], light: colors.primary[500],
dark: colors.primary[300] dark: colors.primary[300],
}; };
return ( return (
<> <>
@@ -53,12 +53,7 @@ export const HelpModal = ({ item, name }) => {
</AnimatePresence> </AnimatePresence>
<Modal isOpen={isOpen} onClose={onClose} size="xl"> <Modal isOpen={isOpen} onClose={onClose} size="xl">
<ModalOverlay /> <ModalOverlay />
<ModalContent <ModalContent bg={bg[colorMode]} color={color[colorMode]} py={4} borderRadius="md">
bg={bg[colorMode]}
color={color[colorMode]}
py={4}
borderRadius="md"
>
<ModalHeader>{item.params.title}</ModalHeader> <ModalHeader>{item.params.title}</ModalHeader>
<ModalCloseButton /> <ModalCloseButton />
<ModalBody> <ModalBody>

View File

@@ -1,10 +1,10 @@
import * as React from "react"; import * as React from 'react';
import { forwardRef, useState, useEffect } from "react"; import { forwardRef, useState, useEffect } from 'react';
import { Box, Flex } from "@chakra-ui/core"; import { Box, Flex } from '@chakra-ui/core';
import { useForm } from "react-hook-form"; import { useForm } from 'react-hook-form';
import { intersectionWith, isEqual } from "lodash"; import { intersectionWith, isEqual } from 'lodash';
import * as yup from "yup"; import * as yup from 'yup';
import format from "string-format"; import format from 'string-format';
import { import {
FormField, FormField,
HelpModal, HelpModal,
@@ -14,9 +14,9 @@ import {
CommunitySelect, CommunitySelect,
QueryVrf, QueryVrf,
ResolvedTarget, ResolvedTarget,
SubmitButton SubmitButton,
} from "app/components"; } from 'app/components';
import { useConfig } from "app/context"; import { useConfig } from 'app/context';
format.extend(String.prototype, {}); format.extend(String.prototype, {});
@@ -27,20 +27,16 @@ const formSchema = config =>
.of(yup.string()) .of(yup.string())
.required( .required(
config.messages.no_input.format({ config.messages.no_input.format({
field: config.web.text.query_location field: config.web.text.query_location,
}) }),
), ),
query_type: yup query_type: yup
.string() .string()
.required( .required(config.messages.no_input.format({ field: config.web.text.query_type })),
config.messages.no_input.format({ field: config.web.text.query_type })
),
query_vrf: yup.string(), query_vrf: yup.string(),
query_target: yup query_target: yup
.string() .string()
.required( .required(config.messages.no_input.format({ field: config.web.text.query_target })),
config.messages.no_input.format({ field: config.web.text.query_target })
)
}); });
const FormRow = ({ children, ...props }) => ( const FormRow = ({ children, ...props }) => (
@@ -48,38 +44,27 @@ const FormRow = ({ children, ...props }) => (
flexDirection="row" flexDirection="row"
flexWrap="wrap" flexWrap="wrap"
w="100%" w="100%"
justifyContent={["center", "center", "space-between", "space-between"]} justifyContent={['center', 'center', 'space-between', 'space-between']}
{...props} {...props}>
>
{children} {children}
</Flex> </Flex>
); );
export const HyperglassForm = forwardRef( export const HyperglassForm = forwardRef(
( ({ isSubmitting, setSubmitting, setFormData, greetingAck, setGreetingAck, ...props }, ref) => {
{
isSubmitting,
setSubmitting,
setFormData,
greetingAck,
setGreetingAck,
...props
},
ref
) => {
const config = useConfig(); const config = useConfig();
const { handleSubmit, register, unregister, setValue, errors } = useForm({ const { handleSubmit, register, unregister, setValue, errors } = useForm({
validationSchema: formSchema(config), validationSchema: formSchema(config),
defaultValues: { query_vrf: "default", query_target: "" } defaultValues: { query_vrf: 'default', query_target: '' },
}); });
const [queryLocation, setQueryLocation] = useState([]); const [queryLocation, setQueryLocation] = useState([]);
const [queryType, setQueryType] = useState(""); const [queryType, setQueryType] = useState('');
const [queryVrf, setQueryVrf] = useState(""); const [queryVrf, setQueryVrf] = useState('');
const [queryTarget, setQueryTarget] = useState(""); const [queryTarget, setQueryTarget] = useState('');
const [availVrfs, setAvailVrfs] = useState([]); const [availVrfs, setAvailVrfs] = useState([]);
const [fqdnTarget, setFqdnTarget] = useState(""); const [fqdnTarget, setFqdnTarget] = useState('');
const [displayTarget, setDisplayTarget] = useState(""); const [displayTarget, setDisplayTarget] = useState('');
const [families, setFamilies] = useState([]); const [families, setFamilies] = useState([]);
const onSubmit = values => { const onSubmit = values => {
if (!greetingAck && config.web.greeting.required) { if (!greetingAck && config.web.greeting.required) {
@@ -100,7 +85,7 @@ export const HyperglassForm = forwardRef(
config.devices[loc].vrfs.map(vrf => { config.devices[loc].vrfs.map(vrf => {
locVrfs.push({ locVrfs.push({
label: vrf.display_name, label: vrf.display_name,
value: vrf.id value: vrf.id,
}); });
deviceVrfs.push([{ id: vrf.id, ipv4: vrf.ipv4, ipv6: vrf.ipv6 }]); deviceVrfs.push([{ id: vrf.id, ipv4: vrf.ipv4, ipv6: vrf.ipv6 }]);
}); });
@@ -109,9 +94,7 @@ export const HyperglassForm = forwardRef(
const intersecting = intersectionWith(...allVrfs, isEqual); const intersecting = intersectionWith(...allVrfs, isEqual);
setAvailVrfs(intersecting); setAvailVrfs(intersecting);
!intersecting.includes(queryVrf) && !intersecting.includes(queryVrf) && queryVrf !== 'default' && setQueryVrf('default');
queryVrf !== "default" &&
setQueryVrf("default");
let ipv4 = 0; let ipv4 = 0;
let ipv6 = 0; let ipv6 = 0;
@@ -120,7 +103,7 @@ export const HyperglassForm = forwardRef(
deviceVrfs deviceVrfs
.filter(v => intersecting.every(i => i.id === v.id)) .filter(v => intersecting.every(i => i.id === v.id))
.reduce((a, b) => a.concat(b)) .reduce((a, b) => a.concat(b))
.filter(v => v.id === "default") .filter(v => v.id === 'default')
.map(v => { .map(v => {
v.ipv4 === true && ipv4++; v.ipv4 === true && ipv4++;
v.ipv6 === true && ipv6++; v.ipv6 === true && ipv6++;
@@ -138,13 +121,13 @@ export const HyperglassForm = forwardRef(
const handleChange = e => { const handleChange = e => {
setValue(e.field, e.value); setValue(e.field, e.value);
e.field === "query_location" e.field === 'query_location'
? handleLocChange(e) ? handleLocChange(e)
: e.field === "query_type" : e.field === 'query_type'
? setQueryType(e.value) ? setQueryType(e.value)
: e.field === "query_vrf" : e.field === 'query_vrf'
? setQueryVrf(e.value) ? setQueryVrf(e.value)
: e.field === "query_target" : e.field === 'query_target'
? setQueryTarget(e.value) ? setQueryTarget(e.value)
: null; : null;
}; };
@@ -152,37 +135,35 @@ export const HyperglassForm = forwardRef(
const vrfContent = config.content.vrf[queryVrf]?.[queryType]; const vrfContent = config.content.vrf[queryVrf]?.[queryType];
const validFqdnQueryType = const validFqdnQueryType =
["ping", "traceroute", "bgp_route"].includes(queryType) && ['ping', 'traceroute', 'bgp_route'].includes(queryType) &&
fqdnTarget && fqdnTarget &&
queryVrf === "default" queryVrf === 'default'
? fqdnTarget ? fqdnTarget
: null; : null;
useEffect(() => { useEffect(() => {
register({ name: "query_location" }); register({ name: 'query_location' });
register({ name: "query_type" }); register({ name: 'query_type' });
register({ name: "query_vrf" }); register({ name: 'query_vrf' });
}, [register]); }, [register]);
Object.keys(errors).length >= 1 && console.error(errors); Object.keys(errors).length >= 1 && console.error(errors);
return ( return (
<Box <Box
as="form" as="form"
onSubmit={handleSubmit(onSubmit)} onSubmit={handleSubmit(onSubmit)}
maxW={["100%", "100%", "75%", "75%"]} maxW={['100%', '100%', '75%', '75%']}
w="100%" w="100%"
p={0} p={0}
mx="auto" mx="auto"
my={4} my={4}
textAlign="left" textAlign="left"
ref={ref} ref={ref}
{...props} {...props}>
>
<FormRow> <FormRow>
<FormField <FormField
label={config.web.text.query_location} label={config.web.text.query_location}
name="query_location" name="query_location"
error={errors.query_location} error={errors.query_location}>
>
<QueryLocation <QueryLocation
onChange={handleChange} onChange={handleChange}
locations={config.networks} locations={config.networks}
@@ -193,10 +174,7 @@ export const HyperglassForm = forwardRef(
label={config.web.text.query_type} label={config.web.text.query_type}
name="query_type" name="query_type"
error={errors.query_type} error={errors.query_type}
labelAddOn={ labelAddOn={vrfContent && <HelpModal item={vrfContent} name="query_type" />}>
vrfContent && <HelpModal item={vrfContent} name="query_type" />
}
>
<QueryType <QueryType
onChange={handleChange} onChange={handleChange}
queryTypes={config.queries.list} queryTypes={config.queries.list}
@@ -206,11 +184,7 @@ export const HyperglassForm = forwardRef(
</FormRow> </FormRow>
<FormRow> <FormRow>
{availVrfs.length > 1 && ( {availVrfs.length > 1 && (
<FormField <FormField label={config.web.text.query_vrf} name="query_vrf" error={errors.query_vrf}>
label={config.web.text.query_vrf}
name="query_vrf"
error={errors.query_vrf}
>
<QueryVrf <QueryVrf
label={config.web.text.query_vrf} label={config.web.text.query_vrf}
vrfs={availVrfs} vrfs={availVrfs}
@@ -233,10 +207,8 @@ export const HyperglassForm = forwardRef(
availVrfs={availVrfs} availVrfs={availVrfs}
/> />
) )
} }>
> {queryType === 'bgp_community' && config.queries.bgp_community.mode === 'select' ? (
{queryType === "bgp_community" &&
config.queries.bgp_community.mode === "select" ? (
<CommunitySelect <CommunitySelect
label={config.queries.bgp_community.display_name} label={config.queries.bgp_community.display_name}
name="query_target" name="query_target"
@@ -251,9 +223,7 @@ export const HyperglassForm = forwardRef(
placeholder={config.web.text.query_target} placeholder={config.web.text.query_target}
register={register} register={register}
unregister={unregister} unregister={unregister}
resolveTarget={["ping", "traceroute", "bgp_route"].includes( resolveTarget={['ping', 'traceroute', 'bgp_route'].includes(queryType)}
queryType
)}
value={queryTarget} value={queryTarget}
setFqdn={setFqdnTarget} setFqdn={setFqdnTarget}
setTarget={handleChange} setTarget={handleChange}
@@ -271,12 +241,11 @@ export const HyperglassForm = forwardRef(
my={2} my={2}
mr={[0, 0, 2, 2]} mr={[0, 0, 2, 2]}
flexDirection="column" flexDirection="column"
flex="0 0 0" flex="0 0 0">
>
<SubmitButton isLoading={isSubmitting} /> <SubmitButton isLoading={isSubmitting} />
</Flex> </Flex>
</FormRow> </FormRow>
</Box> </Box>
); );
} },
); );

View File

@@ -1,11 +1,11 @@
import * as React from "react"; import * as React from 'react';
import { forwardRef } from "react"; import { forwardRef } from 'react';
import { Flex, useColorMode } from "@chakra-ui/core"; import { Flex, useColorMode } from '@chakra-ui/core';
export const Label = forwardRef( export const Label = forwardRef(
({ value, label, labelColor, valueBg, valueColor, ...props }, ref) => { ({ value, label, labelColor, valueBg, valueColor, ...props }, ref) => {
const { colorMode } = useColorMode(); const { colorMode } = useColorMode();
const _labelColor = { dark: "whiteAlpha.700", light: "blackAlpha.700" }; const _labelColor = { dark: 'whiteAlpha.700', light: 'blackAlpha.700' };
return ( return (
<Flex <Flex
ref={ref} ref={ref}
@@ -14,8 +14,7 @@ export const Label = forwardRef(
justifyContent="flex-start" justifyContent="flex-start"
mx={[1, 2, 2, 2]} mx={[1, 2, 2, 2]}
my={2} my={2}
{...props} {...props}>
>
<Flex <Flex
display="inline-flex" display="inline-flex"
justifyContent="center" justifyContent="center"
@@ -24,15 +23,14 @@ export const Label = forwardRef(
whiteSpace="nowrap" whiteSpace="nowrap"
mb={2} mb={2}
mr={0} mr={0}
bg={valueBg || "primary.600"} bg={valueBg || 'primary.600'}
color={valueColor || "white"} color={valueColor || 'white'}
borderBottomLeftRadius={4} borderBottomLeftRadius={4}
borderTopLeftRadius={4} borderTopLeftRadius={4}
borderBottomRightRadius={0} borderBottomRightRadius={0}
borderTopRightRadius={0} borderTopRightRadius={0}
fontWeight="bold" fontWeight="bold"
fontSize={["xs", "sm", "sm", "sm"]} fontSize={['xs', 'sm', 'sm', 'sm']}>
>
{value} {value}
</Flex> </Flex>
<Flex <Flex
@@ -44,17 +42,16 @@ export const Label = forwardRef(
mb={2} mb={2}
ml={0} ml={0}
mr={0} mr={0}
boxShadow={`inset 0px 0px 0px 1px ${valueBg || "primary.600"}`} boxShadow={`inset 0px 0px 0px 1px ${valueBg || 'primary.600'}`}
color={labelColor || _labelColor[colorMode]} color={labelColor || _labelColor[colorMode]}
borderBottomRightRadius={4} borderBottomRightRadius={4}
borderTopRightRadius={4} borderTopRightRadius={4}
borderBottomLeftRadius={0} borderBottomLeftRadius={0}
borderTopLeftRadius={0} borderTopLeftRadius={0}
fontSize={["xs", "sm", "sm", "sm"]} fontSize={['xs', 'sm', 'sm', 'sm']}>
>
{label} {label}
</Flex> </Flex>
</Flex> </Flex>
); );
} },
); );

View File

@@ -1,11 +1,11 @@
import * as React from "react"; import * as React from 'react';
import { useRef } from "react"; import { useRef } from 'react';
import { Flex, useColorMode } from "@chakra-ui/core"; import { Flex, useColorMode } from '@chakra-ui/core';
import { useConfig, useHyperglassState } from "app/context"; import { useConfig, useHyperglassState } from 'app/context';
import { Debugger, Greeting, Footer, Header } from "app/components"; import { Debugger, Greeting, Footer, Header } from 'app/components';
const bg = { light: "white", dark: "black" }; const bg = { light: 'white', dark: 'black' };
const color = { light: "black", dark: "white" }; const color = { light: 'black', dark: 'white' };
export const Layout = ({ children }) => { export const Layout = ({ children }) => {
const config = useConfig(); const config = useConfig();
@@ -21,8 +21,7 @@ export const Layout = ({ children }) => {
minHeight="100vh" minHeight="100vh"
bg={bg[colorMode]} bg={bg[colorMode]}
flexDirection="column" flexDirection="column"
color={color[colorMode]} color={color[colorMode]}>
>
<Flex px={2} flex="0 1 auto" flexDirection="column"> <Flex px={2} flex="0 1 auto" flexDirection="column">
<Header layoutRef={containerRef} /> <Header layoutRef={containerRef} />
</Flex> </Flex>
@@ -35,8 +34,7 @@ export const Layout = ({ children }) => {
textAlign="center" textAlign="center"
alignItems="center" alignItems="center"
justifyContent="start" justifyContent="start"
flexDirection="column" flexDirection="column">
>
{children} {children}
</Flex> </Flex>
<Footer /> <Footer />

View File

@@ -1,18 +1,17 @@
import * as React from "react"; import * as React from 'react';
import { Flex, Spinner, useColorMode } from "@chakra-ui/core"; import { Flex, Spinner, useColorMode } from '@chakra-ui/core';
export const Loading = () => { export const Loading = () => {
const { colorMode } = useColorMode(); const { colorMode } = useColorMode();
const bg = { light: "white", dark: "black" }; const bg = { light: 'white', dark: 'black' };
const color = { light: "black", dark: "white" }; const color = { light: 'black', dark: 'white' };
return ( return (
<Flex <Flex
flexDirection="column" flexDirection="column"
minHeight="100vh" minHeight="100vh"
w="100%" w="100%"
bg={bg[colorMode]} bg={bg[colorMode]}
color={color[colorMode]} color={color[colorMode]}>
>
<Flex <Flex
as="main" as="main"
w="100%" w="100%"
@@ -25,8 +24,7 @@ export const Loading = () => {
flexDirection="column" flexDirection="column"
px={2} px={2}
py={0} py={0}
mt={["50%", "50%", "50%", "25%"]} mt={['50%', '50%', '50%', '25%']}>
>
<Spinner color="primary.500" w="6rem" h="6rem" /> <Spinner color="primary.500" w="6rem" h="6rem" />
</Flex> </Flex>
</Flex> </Flex>

View File

@@ -1,7 +1,7 @@
import * as React from "react"; import * as React from 'react';
import { motion, AnimatePresence } from "framer-motion"; import { motion, AnimatePresence } from 'framer-motion';
import { Layout, HyperglassForm, Results } from "app/components"; import { Layout, HyperglassForm, Results } from 'app/components';
import { useHyperglassState } from "app/context"; import { useHyperglassState } from 'app/context';
const AnimatedForm = motion.custom(HyperglassForm); const AnimatedForm = motion.custom(HyperglassForm);
@@ -12,7 +12,7 @@ export const LookingGlass = () => {
formData, formData,
setFormData, setFormData,
greetingAck, greetingAck,
setGreetingAck setGreetingAck,
} = useHyperglassState(); } = useHyperglassState();
return ( return (

View File

@@ -1,4 +1,4 @@
import * as React from "react"; import * as React from 'react';
import { import {
Checkbox as ChakraCheckbox, Checkbox as ChakraCheckbox,
Divider as ChakraDivider, Divider as ChakraDivider,
@@ -7,19 +7,19 @@ import {
Link as ChakraLink, Link as ChakraLink,
List as ChakraList, List as ChakraList,
ListItem as ChakraListItem, ListItem as ChakraListItem,
Text as ChakraText Text as ChakraText,
} from "@chakra-ui/core"; } from '@chakra-ui/core';
import { TableCell, TableHeader, Table as ChakraTable } from "./MDTable"; import { TableCell, TableHeader, Table as ChakraTable } from './MDTable';
import { CodeBlock as CustomCodeBlock } from "app/components"; import { CodeBlock as CustomCodeBlock } from 'app/components';
export const Checkbox = ({ checked, children }) => ( export const Checkbox = ({ checked, children }) => (
<ChakraCheckbox isChecked={checked}>{children}</ChakraCheckbox> <ChakraCheckbox isChecked={checked}>{children}</ChakraCheckbox>
); );
export const List = ({ ordered, children }) => ( export const List = ({ ordered, children }) => (
<ChakraList as={ordered ? "ol" : "ul"}>{children}</ChakraList> <ChakraList as={ordered ? 'ol' : 'ul'}>{children}</ChakraList>
); );
export const ListItem = ({ checked, children }) => export const ListItem = ({ checked, children }) =>
@@ -31,12 +31,12 @@ export const ListItem = ({ checked, children }) =>
export const Heading = ({ level, children }) => { export const Heading = ({ level, children }) => {
const levelMap = { const levelMap = {
1: { as: "h1", size: "lg", fontWeight: "bold" }, 1: { as: 'h1', size: 'lg', fontWeight: 'bold' },
2: { as: "h2", size: "lg", fontWeight: "normal" }, 2: { as: 'h2', size: 'lg', fontWeight: 'normal' },
3: { as: "h3", size: "lg", fontWeight: "bold" }, 3: { as: 'h3', size: 'lg', fontWeight: 'bold' },
4: { as: "h4", size: "md", fontWeight: "normal" }, 4: { as: 'h4', size: 'md', fontWeight: 'normal' },
5: { as: "h5", size: "md", fontWeight: "bold" }, 5: { as: 'h5', size: 'md', fontWeight: 'bold' },
6: { as: "h6", size: "sm", fontWeight: "bold" } 6: { as: 'h6', size: 'sm', fontWeight: 'bold' },
}; };
return <ChakraHeading {...levelMap[level]}>{children}</ChakraHeading>; return <ChakraHeading {...levelMap[level]}>{children}</ChakraHeading>;
}; };
@@ -47,9 +47,7 @@ export const Link = ({ children, ...props }) => (
</ChakraLink> </ChakraLink>
); );
export const CodeBlock = ({ value }) => ( export const CodeBlock = ({ value }) => <CustomCodeBlock>{value}</CustomCodeBlock>;
<CustomCodeBlock>{value}</CustomCodeBlock>
);
export const TableData = ({ isHeader, children, ...props }) => { export const TableData = ({ isHeader, children, ...props }) => {
const Component = isHeader ? TableHeader : TableCell; const Component = isHeader ? TableHeader : TableCell;

View File

@@ -1,30 +1,19 @@
import * as React from "react"; import * as React from 'react';
import { Box, useColorMode } from "@chakra-ui/core"; import { Box, useColorMode } from '@chakra-ui/core';
export const Table = props => ( export const Table = props => <Box as="table" textAlign="left" mt={4} width="full" {...props} />;
<Box as="table" textAlign="left" mt={4} width="full" {...props} />
);
const bg = { light: "blackAlpha.50", dark: "whiteAlpha.50" }; const bg = { light: 'blackAlpha.50', dark: 'whiteAlpha.50' };
export const TableHeader = props => { export const TableHeader = props => {
const { colorMode } = useColorMode(); const { colorMode } = useColorMode();
return ( return <Box as="th" bg={bg[colorMode]} fontWeight="semibold" p={2} fontSize="sm" {...props} />;
<Box
as="th"
bg={bg[colorMode]}
fontWeight="semibold"
p={2}
fontSize="sm"
{...props}
/>
);
}; };
export const TableCell = ({ isHeader = false, ...props }) => ( export const TableCell = ({ isHeader = false, ...props }) => (
<Box <Box
as={isHeader ? "th" : "td"} as={isHeader ? 'th' : 'td'}
p={2} p={2}
borderTopWidth="1px" borderTopWidth="1px"
borderColor="inherit" borderColor="inherit"

View File

@@ -1,6 +1,6 @@
import * as React from "react"; import * as React from 'react';
import { forwardRef } from "react"; import { forwardRef } from 'react';
import ReactMarkdown from "react-markdown"; import ReactMarkdown from 'react-markdown';
import { import {
List, List,
ListItem, ListItem,
@@ -11,8 +11,8 @@ import {
Paragraph, Paragraph,
InlineCode, InlineCode,
Divider, Divider,
Table Table,
} from "./MDComponents"; } from './MDComponents';
const mdComponents = { const mdComponents = {
paragraph: Paragraph, paragraph: Paragraph,
@@ -24,7 +24,7 @@ const mdComponents = {
thematicBreak: Divider, thematicBreak: Divider,
code: CodeBlock, code: CodeBlock,
table: Table, table: Table,
tableCell: TableData tableCell: TableData,
}; };
export const Markdown = forwardRef(({ content }, ref) => ( export const Markdown = forwardRef(({ content }, ref) => (

View File

@@ -1 +1 @@
export * from "./Markdown"; export * from './Markdown';

View File

@@ -1,38 +1,38 @@
import * as React from "react"; import * as React from 'react';
import { useEffect, useState } from "react"; import { useEffect, useState } from 'react';
import Head from "next/head"; import Head from 'next/head';
import { useTheme } from "@chakra-ui/core"; import { useTheme } from '@chakra-ui/core';
import { useConfig } from "app/context"; import { useConfig } from 'app/context';
import { googleFontUrl } from "app/util"; import { googleFontUrl } from 'app/util';
export const Meta = () => { export const Meta = () => {
const config = useConfig(); const config = useConfig();
const theme = useTheme(); const theme = useTheme();
const [location, setLocation] = useState({}); const [location, setLocation] = useState({});
const title = config?.site_title || "hyperglass"; const title = config?.site_title || 'hyperglass';
const description = config?.site_description || "Network Looking Glass"; const description = config?.site_description || 'Network Looking Glass';
const siteName = `${title} - ${description}`; const siteName = `${title} - ${description}`;
const keywords = config?.site_keywords || [ const keywords = config?.site_keywords || [
"hyperglass", 'hyperglass',
"looking glass", 'looking glass',
"lg", 'lg',
"peer", 'peer',
"peering", 'peering',
"ipv4", 'ipv4',
"ipv6", 'ipv6',
"transit", 'transit',
"community", 'community',
"communities", 'communities',
"bgp", 'bgp',
"routing", 'routing',
"network", 'network',
"isp" 'isp',
]; ];
const language = config?.language ?? "en"; const language = config?.language ?? 'en';
const primaryFont = googleFontUrl(theme.fonts.body); const primaryFont = googleFontUrl(theme.fonts.body);
const monoFont = googleFontUrl(theme.fonts.mono); const monoFont = googleFontUrl(theme.fonts.mono);
useEffect(() => { useEffect(() => {
if (typeof window !== "undefined" && location === {}) { if (typeof window !== 'undefined' && location === {}) {
setLocation(window.location); setLocation(window.location);
} }
}, [location]); }, [location]);
@@ -41,7 +41,7 @@ export const Meta = () => {
<title>{title}</title> <title>{title}</title>
<meta name="hg-version" content={config.hyperglass_version} /> <meta name="hg-version" content={config.hyperglass_version} />
<meta name="description" content={description} /> <meta name="description" content={description} />
<meta name="keywords" content={keywords.join(", ")} /> <meta name="keywords" content={keywords.join(', ')} />
<meta name="language" content={language} /> <meta name="language" content={language} />
<meta name="url" content={location.href} /> <meta name="url" content={location.href} />
<meta name="og:title" content={title} /> <meta name="og:title" content={title} />

View File

@@ -1,5 +1,5 @@
import * as React from "react"; import * as React from 'react';
import { ChakraSelect } from "app/components"; import { ChakraSelect } from 'app/components';
const buildLocations = networks => { const buildLocations = networks => {
const locations = []; const locations = [];
@@ -9,7 +9,7 @@ const buildLocations = networks => {
netLocations.push({ netLocations.push({
label: loc.display_name, label: loc.display_name,
value: loc.name, value: loc.name,
group: net.display_name group: net.display_name,
}); });
}); });
locations.push({ label: net.display_name, options: netLocations }); locations.push({ label: net.display_name, options: netLocations });
@@ -25,7 +25,7 @@ export const QueryLocation = ({ locations, onChange, label }) => {
e.map(sel => { e.map(sel => {
selected.push(sel.value); selected.push(sel.value);
}); });
onChange({ field: "query_location", value: selected }); onChange({ field: 'query_location', value: selected });
}; };
return ( return (
<ChakraSelect <ChakraSelect

View File

@@ -1,13 +1,13 @@
import * as React from "react"; import * as React from 'react';
import { useEffect } from "react"; import { useEffect } from 'react';
import { Input, useColorMode } from "@chakra-ui/core"; import { Input, useColorMode } from '@chakra-ui/core';
const fqdnPattern = /^(?!:\/\/)([a-zA-Z0-9-]+\.)?[a-zA-Z0-9-][a-zA-Z0-9-]+\.[a-zA-Z-]{2,6}?$/gim; const fqdnPattern = /^(?!:\/\/)([a-zA-Z0-9-]+\.)?[a-zA-Z0-9-][a-zA-Z0-9-]+\.[a-zA-Z-]{2,6}?$/gim;
const bg = { dark: "whiteAlpha.100", light: "white" }; const bg = { dark: 'whiteAlpha.100', light: 'white' };
const color = { dark: "whiteAlpha.800", light: "gray.400" }; const color = { dark: 'whiteAlpha.800', light: 'gray.400' };
const border = { dark: "whiteAlpha.50", light: "gray.100" }; const border = { dark: 'whiteAlpha.50', light: 'gray.100' };
const placeholderColor = { dark: "whiteAlpha.700", light: "gray.600" }; const placeholderColor = { dark: 'whiteAlpha.700', light: 'gray.600' };
export const QueryTarget = ({ export const QueryTarget = ({
placeholder, placeholder,
@@ -19,7 +19,7 @@ export const QueryTarget = ({
setTarget, setTarget,
resolveTarget, resolveTarget,
displayValue, displayValue,
setDisplayValue setDisplayValue,
}) => { }) => {
const { colorMode } = useColorMode(); const { colorMode } = useColorMode();
@@ -61,7 +61,7 @@ export const QueryTarget = ({
placeholder={placeholder} placeholder={placeholder}
borderColor={border[colorMode]} borderColor={border[colorMode]}
_placeholder={{ _placeholder={{
color: placeholderColor[colorMode] color: placeholderColor[colorMode],
}} }}
/> />
</> </>

View File

@@ -1,5 +1,5 @@
import * as React from "react"; import * as React from 'react';
import { ChakraSelect } from "app/components"; import { ChakraSelect } from 'app/components';
export const QueryType = ({ queryTypes, onChange, label }) => { export const QueryType = ({ queryTypes, onChange, label }) => {
const queries = queryTypes const queries = queryTypes
@@ -11,7 +11,7 @@ export const QueryType = ({ queryTypes, onChange, label }) => {
<ChakraSelect <ChakraSelect
size="lg" size="lg"
name="query_type" name="query_type"
onChange={e => onChange({ field: "query_type", value: e.value })} onChange={e => onChange({ field: 'query_type', value: e.value })}
options={queries} options={queries}
aria-label={label} aria-label={label}
/> />

View File

@@ -1,5 +1,5 @@
import * as React from "react"; import * as React from 'react';
import { ChakraSelect } from "app/components"; import { ChakraSelect } from 'app/components';
export const QueryVrf = ({ vrfs, onChange, label }) => ( export const QueryVrf = ({ vrfs, onChange, label }) => (
<ChakraSelect <ChakraSelect
@@ -7,6 +7,6 @@ export const QueryVrf = ({ vrfs, onChange, label }) => (
options={vrfs} options={vrfs}
name="query_vrf" name="query_vrf"
aria-label={label} aria-label={label}
onChange={e => onChange({ field: "query_vrf", value: e.value })} onChange={e => onChange({ field: 'query_vrf', value: e.value })}
/> />
); );

View File

@@ -1,17 +1,9 @@
import * as React from "react"; import * as React from 'react';
import { Button, Icon, Tooltip } from "@chakra-ui/core"; import { Button, Icon, Tooltip } from '@chakra-ui/core';
export const RequeryButton = ({ requery, bg = "secondary", ...props }) => ( export const RequeryButton = ({ requery, bg = 'secondary', ...props }) => (
<Tooltip hasArrow label="Reload Query" placement="top"> <Tooltip hasArrow label="Reload Query" placement="top">
<Button <Button mx={1} as="a" size="sm" zIndex="1" variantColor={bg} onClick={requery} {...props}>
mx={1}
as="a"
size="sm"
zIndex="1"
variantColor={bg}
onClick={requery}
{...props}
>
<Icon size="16px" name="repeat" /> <Icon size="16px" name="repeat" />
</Button> </Button>
</Tooltip> </Tooltip>

View File

@@ -1,18 +1,15 @@
import * as React from "react"; import * as React from 'react';
import { Button } from "@chakra-ui/core"; import { Button } from '@chakra-ui/core';
import { FiChevronLeft } from "@meronex/icons/fi"; import { FiChevronLeft } from '@meronex/icons/fi';
export const ResetButton = React.forwardRef( export const ResetButton = React.forwardRef(({ isSubmitting, onClick }, ref) => (
({ isSubmitting, onClick }, ref) => ( <Button
<Button ref={ref}
ref={ref} color="current"
color="current" variant="ghost"
variant="ghost" onClick={onClick}
onClick={onClick} aria-label="Reset Form"
aria-label="Reset Form" opacity={isSubmitting ? 1 : 0}>
opacity={isSubmitting ? 1 : 0} <FiChevronLeft size={24} />
> </Button>
<FiChevronLeft size={24} /> ));
</Button>
)
);

View File

@@ -1,23 +1,14 @@
import * as React from "react"; import * as React from 'react';
import { forwardRef, useEffect } from "react"; import { forwardRef, useEffect } from 'react';
import { import { Button, Icon, Spinner, Stack, Tag, Text, Tooltip, useColorMode } from '@chakra-ui/core';
Button, import useAxios from 'axios-hooks';
Icon, import format from 'string-format';
Spinner, import { useConfig } from 'app/context';
Stack,
Tag,
Text,
Tooltip,
useColorMode
} from "@chakra-ui/core";
import useAxios from "axios-hooks";
import format from "string-format";
import { useConfig } from "app/context";
format.extend(String.prototype, {}); format.extend(String.prototype, {});
const labelBg = { dark: "secondary", light: "secondary" }; const labelBg = { dark: 'secondary', light: 'secondary' };
const labelBgSuccess = { dark: "success", light: "success" }; const labelBgSuccess = { dark: 'success', light: 'success' };
export const ResolvedTarget = forwardRef( export const ResolvedTarget = forwardRef(
({ fqdnTarget, setTarget, queryTarget, families, availVrfs }, ref) => { ({ fqdnTarget, setTarget, queryTarget, families, availVrfs }, ref) => {
@@ -25,7 +16,7 @@ export const ResolvedTarget = forwardRef(
const config = useConfig(); const config = useConfig();
const labelBgStatus = { const labelBgStatus = {
true: labelBgSuccess[colorMode], true: labelBgSuccess[colorMode],
false: labelBg[colorMode] false: labelBg[colorMode],
}; };
const dnsUrl = config.web.dns_provider.url; const dnsUrl = config.web.dns_provider.url;
const query4 = families.includes(4); const query4 = families.includes(4);
@@ -33,30 +24,26 @@ export const ResolvedTarget = forwardRef(
const params = { const params = {
4: { 4: {
url: dnsUrl, url: dnsUrl,
params: { name: fqdnTarget, type: "A" }, params: { name: fqdnTarget, type: 'A' },
headers: { accept: "application/dns-json" }, headers: { accept: 'application/dns-json' },
crossdomain: true, crossdomain: true,
timeout: 1000 timeout: 1000,
}, },
6: { 6: {
url: dnsUrl, url: dnsUrl,
params: { name: fqdnTarget, type: "AAAA" }, params: { name: fqdnTarget, type: 'AAAA' },
headers: { accept: "application/dns-json" }, headers: { accept: 'application/dns-json' },
crossdomain: true, crossdomain: true,
timeout: 1000 timeout: 1000,
} },
}; };
const [{ data: data4, loading: loading4, error: error4 }] = useAxios( const [{ data: data4, loading: loading4, error: error4 }] = useAxios(params[4]);
params[4]
);
const [{ data: data6, loading: loading6, error: error6 }] = useAxios( const [{ data: data6, loading: loading6, error: error6 }] = useAxios(params[6]);
params[6]
);
const handleOverride = overridden => { const handleOverride = overridden => {
setTarget({ field: "query_target", value: overridden }); setTarget({ field: 'query_target', value: overridden });
}; };
const isSelected = value => { const isSelected = value => {
@@ -64,9 +51,8 @@ export const ResolvedTarget = forwardRef(
}; };
const findAnswer = data => { const findAnswer = data => {
return data?.Answer?.filter( return data?.Answer?.filter(answerData => answerData.type === data?.Question[0]?.type)[0]
answerData => answerData.type === data?.Question[0]?.type ?.data;
)[0]?.data;
}; };
useEffect(() => { useEffect(() => {
@@ -84,16 +70,11 @@ export const ResolvedTarget = forwardRef(
isInline isInline
w="100%" w="100%"
justifyContent={ justifyContent={
query4 && query4 && data4?.Answer && query6 && data6?.Answer && availVrfs.length > 1
data4?.Answer && ? 'space-between'
query6 && : 'flex-end'
data6?.Answer &&
availVrfs.length > 1
? "space-between"
: "flex-end"
} }
flexWrap="wrap" flexWrap="wrap">
>
{loading4 || {loading4 ||
error4 || error4 ||
(query4 && findAnswer(data4) && ( (query4 && findAnswer(data4) && (
@@ -101,10 +82,9 @@ export const ResolvedTarget = forwardRef(
<Tooltip <Tooltip
hasArrow hasArrow
label={config.web.text.fqdn_tooltip.format({ label={config.web.text.fqdn_tooltip.format({
protocol: "IPv4" protocol: 'IPv4',
})} })}
placement="bottom" placement="bottom">
>
<Button <Button
height="unset" height="unset"
minW="unset" minW="unset"
@@ -112,24 +92,16 @@ export const ResolvedTarget = forwardRef(
py="0.1rem" py="0.1rem"
px={2} px={2}
mr={2} mr={2}
variantColor={ variantColor={labelBgStatus[findAnswer(data4) === queryTarget]}
labelBgStatus[findAnswer(data4) === queryTarget]
}
borderRadius="md" borderRadius="md"
onClick={() => handleOverride(findAnswer(data4))} onClick={() => handleOverride(findAnswer(data4))}>
>
IPv4 IPv4
</Button> </Button>
</Tooltip> </Tooltip>
{loading4 && <Spinner />} {loading4 && <Spinner />}
{error4 && <Icon name="warning" />} {error4 && <Icon name="warning" />}
{findAnswer(data4) && ( {findAnswer(data4) && (
<Text <Text fontSize="xs" fontFamily="mono" as="span" fontWeight={400}>
fontSize="xs"
fontFamily="mono"
as="span"
fontWeight={400}
>
{findAnswer(data4)} {findAnswer(data4)}
</Text> </Text>
)} )}
@@ -142,10 +114,9 @@ export const ResolvedTarget = forwardRef(
<Tooltip <Tooltip
hasArrow hasArrow
label={config.web.text.fqdn_tooltip.format({ label={config.web.text.fqdn_tooltip.format({
protocol: "IPv6" protocol: 'IPv6',
})} })}
placement="bottom" placement="bottom">
>
<Button <Button
height="unset" height="unset"
minW="unset" minW="unset"
@@ -155,20 +126,14 @@ export const ResolvedTarget = forwardRef(
mr={2} mr={2}
variantColor={isSelected(findAnswer(data6))} variantColor={isSelected(findAnswer(data6))}
borderRadius="md" borderRadius="md"
onClick={() => handleOverride(findAnswer(data6))} onClick={() => handleOverride(findAnswer(data6))}>
>
IPv6 IPv6
</Button> </Button>
</Tooltip> </Tooltip>
{loading6 && <Spinner />} {loading6 && <Spinner />}
{error6 && <Icon name="warning" />} {error6 && <Icon name="warning" />}
{findAnswer(data6) && ( {findAnswer(data6) && (
<Text <Text fontSize="xs" fontFamily="mono" as="span" fontWeight={400}>
fontSize="xs"
fontFamily="mono"
as="span"
fontWeight={400}
>
{findAnswer(data6)} {findAnswer(data6)}
</Text> </Text>
)} )}
@@ -176,5 +141,5 @@ export const ResolvedTarget = forwardRef(
))} ))}
</Stack> </Stack>
); );
} },
); );

View File

@@ -1,6 +1,6 @@
/** @jsx jsx */ /** @jsx jsx */
import { jsx } from "@emotion/core"; import { jsx } from '@emotion/core';
import { forwardRef, useEffect, useState } from "react"; import { forwardRef, useEffect, useState } from 'react';
import { import {
AccordionItem, AccordionItem,
AccordionHeader, AccordionHeader,
@@ -13,30 +13,30 @@ import {
Tooltip, Tooltip,
Text, Text,
useColorMode, useColorMode,
useTheme useTheme,
} from "@chakra-ui/core"; } from '@chakra-ui/core';
import { BsLightningFill } from "@meronex/icons/bs"; import { BsLightningFill } from '@meronex/icons/bs';
import styled from "@emotion/styled"; import styled from '@emotion/styled';
import useAxios from "axios-hooks"; import useAxios from 'axios-hooks';
import strReplace from "react-string-replace"; import strReplace from 'react-string-replace';
import format from "string-format"; import format from 'string-format';
import { startCase } from "lodash"; import { startCase } from 'lodash';
import { useConfig, useMedia } from "app/context"; import { useConfig, useMedia } from 'app/context';
import { import {
BGPTable, BGPTable,
CacheTimeout, CacheTimeout,
CopyButton, CopyButton,
RequeryButton, RequeryButton,
ResultHeader, ResultHeader,
TextOutput TextOutput,
} from "app/components"; } from 'app/components';
import { tableToString } from "app/util"; import { tableToString } from 'app/util';
format.extend(String.prototype, {}); format.extend(String.prototype, {});
const FormattedError = ({ keywords, message }) => { const FormattedError = ({ keywords, message }) => {
const patternStr = keywords.map(kw => `(${kw})`).join("|"); const patternStr = keywords.map(kw => `(${kw})`).join('|');
const pattern = new RegExp(patternStr, "gi"); const pattern = new RegExp(patternStr, 'gi');
let errorFmt; let errorFmt;
try { try {
errorFmt = strReplace(message, pattern, match => ( errorFmt = strReplace(message, pattern, match => (
@@ -56,21 +56,21 @@ const AccordionHeaderWrapper = styled(Flex)`
background-color: ${props => props.hoverBg}; background-color: ${props => props.hoverBg};
} }
&:focus { &:focus {
box-shadow: "outline"; box-shadow: 'outline';
} }
`; `;
const statusMap = { const statusMap = {
success: "success", success: 'success',
warning: "warning", warning: 'warning',
error: "warning", error: 'warning',
danger: "error" danger: 'error',
}; };
const color = { dark: "white", light: "black" }; const color = { dark: 'white', light: 'black' };
const scrollbar = { dark: "whiteAlpha.300", light: "blackAlpha.300" }; const scrollbar = { dark: 'whiteAlpha.300', light: 'blackAlpha.300' };
const scrollbarHover = { dark: "whiteAlpha.400", light: "blackAlpha.400" }; const scrollbarHover = { dark: 'whiteAlpha.400', light: 'blackAlpha.400' };
const scrollbarBg = { dark: "whiteAlpha.50", light: "blackAlpha.50" }; const scrollbarBg = { dark: 'whiteAlpha.50', light: 'blackAlpha.50' };
export const Result = forwardRef( export const Result = forwardRef(
( (
@@ -83,25 +83,25 @@ export const Result = forwardRef(
queryTarget, queryTarget,
index, index,
resultsComplete, resultsComplete,
setComplete setComplete,
}, },
ref ref,
) => { ) => {
const config = useConfig(); const config = useConfig();
const theme = useTheme(); const theme = useTheme();
const { isSm } = useMedia(); const { isSm } = useMedia();
const { colorMode } = useColorMode(); const { colorMode } = useColorMode();
let [{ data, loading, error }, refetch] = useAxios({ let [{ data, loading, error }, refetch] = useAxios({
url: "/api/query/", url: '/api/query/',
method: "post", method: 'post',
data: { data: {
query_location: queryLocation, query_location: queryLocation,
query_type: queryType, query_type: queryType,
query_vrf: queryVrf, query_vrf: queryVrf,
query_target: queryTarget query_target: queryTarget,
}, },
timeout: timeout, timeout: timeout,
useCache: false useCache: false,
}); });
const [isOpen, setOpen] = useState(false); const [isOpen, setOpen] = useState(false);
@@ -117,7 +117,7 @@ export const Result = forwardRef(
let errorMsg; let errorMsg;
if (error && error.response?.data?.output) { if (error && error.response?.data?.output) {
errorMsg = error.response.data.output; errorMsg = error.response.data.output;
} else if (error && error.message.startsWith("timeout")) { } else if (error && error.message.startsWith('timeout')) {
errorMsg = config.messages.request_timeout; errorMsg = config.messages.request_timeout;
} else if (error?.response?.statusText) { } else if (error?.response?.statusText) {
errorMsg = startCase(error.response.statusText); errorMsg = startCase(error.response.statusText);
@@ -130,22 +130,20 @@ export const Result = forwardRef(
error && console.error(error); error && console.error(error);
const errorLevel = const errorLevel =
(error?.response?.data?.level && (error?.response?.data?.level && statusMap[error.response?.data?.level]) ?? 'error';
statusMap[error.response?.data?.level]) ??
"error";
const structuredDataComponent = { const structuredDataComponent = {
bgp_route: BGPTable, bgp_route: BGPTable,
bgp_aspath: BGPTable, bgp_aspath: BGPTable,
bgp_community: BGPTable, bgp_community: BGPTable,
ping: TextOutput, ping: TextOutput,
traceroute: TextOutput traceroute: TextOutput,
}; };
let Output = TextOutput; let Output = TextOutput;
let copyValue = data?.output; let copyValue = data?.output;
if (data?.format === "application/json") { if (data?.format === 'application/json') {
Output = structuredDataComponent[queryType]; Output = structuredDataComponent[queryType];
copyValue = tableToString(queryTarget, data, config); copyValue = tableToString(queryTarget, data, config);
} }
@@ -168,10 +166,9 @@ export const Result = forwardRef(
isDisabled={loading} isDisabled={loading}
ref={ref} ref={ref}
css={css({ css={css({
"&:last-of-type": { borderBottom: "none" }, '&:last-of-type': { borderBottom: 'none' },
"&:first-of-type": { borderTop: "none" } '&:first-of-type': { borderTop: 'none' },
})(theme)} })(theme)}>
>
<AccordionHeaderWrapper hoverBg="blackAlpha.50"> <AccordionHeaderWrapper hoverBg="blackAlpha.50">
<AccordionHeader <AccordionHeader
flex="1 0 auto" flex="1 0 auto"
@@ -179,8 +176,7 @@ export const Result = forwardRef(
_hover={{}} _hover={{}}
_focus={{}} _focus={{}}
w="unset" w="unset"
onClick={handleToggle} onClick={handleToggle}>
>
<ResultHeader <ResultHeader
title={device.display_name} title={device.display_name}
loading={loading} loading={loading}
@@ -191,43 +187,30 @@ export const Result = forwardRef(
/> />
</AccordionHeader> </AccordionHeader>
<ButtonGroup px={[1, 1, 3, 3]} py={2}> <ButtonGroup px={[1, 1, 3, 3]} py={2}>
<CopyButton <CopyButton copyValue={copyValue} variant="ghost" isDisabled={loading} />
copyValue={copyValue} <RequeryButton requery={refetch} variant="ghost" isDisabled={loading} />
variant="ghost"
isDisabled={loading}
/>
<RequeryButton
requery={refetch}
variant="ghost"
isDisabled={loading}
/>
</ButtonGroup> </ButtonGroup>
</AccordionHeaderWrapper> </AccordionHeaderWrapper>
<AccordionPanel <AccordionPanel
pb={4} pb={4}
overflowX="auto" overflowX="auto"
css={css({ css={css({
WebkitOverflowScrolling: "touch", WebkitOverflowScrolling: 'touch',
"&::-webkit-scrollbar": { height: "5px" }, '&::-webkit-scrollbar': { height: '5px' },
"&::-webkit-scrollbar-track": { '&::-webkit-scrollbar-track': {
backgroundColor: scrollbarBg[colorMode] backgroundColor: scrollbarBg[colorMode],
}, },
"&::-webkit-scrollbar-thumb": { '&::-webkit-scrollbar-thumb': {
backgroundColor: scrollbar[colorMode] backgroundColor: scrollbar[colorMode],
}, },
"&::-webkit-scrollbar-thumb:hover": { '&::-webkit-scrollbar-thumb:hover': {
backgroundColor: scrollbarHover[colorMode] backgroundColor: scrollbarHover[colorMode],
}, },
"-ms-overflow-style": { display: "none" } '-ms-overflow-style': { display: 'none' },
})(theme)} })(theme)}>
>
<Flex direction="column" flexWrap="wrap"> <Flex direction="column" flexWrap="wrap">
<Flex <Flex direction="column" flex="1 0 auto" maxW={error ? '100%' : null}>
direction="column"
flex="1 0 auto"
maxW={error ? "100%" : null}
>
{!error && data && <Output>{data?.output}</Output>} {!error && data && <Output>{data?.output}</Output>}
{error && ( {error && (
<Alert rounded="lg" my={2} py={4} status={errorLevel}> <Alert rounded="lg" my={2} py={4} status={errorLevel}>
@@ -241,14 +224,8 @@ export const Result = forwardRef(
<Flex <Flex
px={3} px={3}
mt={2} mt={2}
justifyContent={[ justifyContent={['flex-start', 'flex-start', 'flex-end', 'flex-end']}
"flex-start", flex="1 0 auto">
"flex-start",
"flex-end",
"flex-end"
]}
flex="1 0 auto"
>
{config.cache.show_text && data && !error && ( {config.cache.show_text && data && !error && (
<> <>
{!isSm && ( {!isSm && (
@@ -258,14 +235,13 @@ export const Result = forwardRef(
/> />
)} )}
<Tooltip <Tooltip
display={data?.cached ? null : "none"} display={data?.cached ? null : 'none'}
hasArrow hasArrow
label={config.web.text.cache_icon.format({ label={config.web.text.cache_icon.format({
time: data?.timestamp time: data?.timestamp,
})} })}
placement="top" placement="top">
> <Box ml={1} display={data?.cached ? 'block' : 'none'}>
<Box ml={1} display={data?.cached ? "block" : "none"}>
<BsLightningFill color={color[colorMode]} /> <BsLightningFill color={color[colorMode]} />
</Box> </Box>
</Tooltip> </Tooltip>
@@ -282,5 +258,5 @@ export const Result = forwardRef(
</AccordionPanel> </AccordionPanel>
</AccordionItem> </AccordionItem>
); );
} },
); );

View File

@@ -1,35 +1,27 @@
import * as React from "react"; import * as React from 'react';
import { forwardRef } from "react"; import { forwardRef } from 'react';
import { import { AccordionIcon, Icon, Spinner, Stack, Text, Tooltip, useColorMode } from '@chakra-ui/core';
AccordionIcon, import format from 'string-format';
Icon, import { useConfig } from 'app/context';
Spinner,
Stack,
Text,
Tooltip,
useColorMode
} from "@chakra-ui/core";
import format from "string-format";
import { useConfig } from "app/context";
format.extend(String.prototype, {}); format.extend(String.prototype, {});
const runtimeText = (runtime, text) => { const runtimeText = (runtime, text) => {
let unit; let unit;
if (runtime === 1) { if (runtime === 1) {
unit = "second"; unit = 'second';
} else { } else {
unit = "seconds"; unit = 'seconds';
} }
const fmt = text.format({ seconds: runtime }); const fmt = text.format({ seconds: runtime });
return `${fmt} ${unit}`; return `${fmt} ${unit}`;
}; };
const statusColor = { dark: "primary.300", light: "primary.500" }; const statusColor = { dark: 'primary.300', light: 'primary.500' };
const warningColor = { dark: 300, light: 500 }; const warningColor = { dark: 300, light: 500 };
const defaultStatusColor = { const defaultStatusColor = {
dark: "success.300", dark: 'success.300',
light: "success.500" light: 'success.500',
}; };
export const ResultHeader = forwardRef( export const ResultHeader = forwardRef(
@@ -53,19 +45,13 @@ export const ResultHeader = forwardRef(
<Tooltip <Tooltip
hasArrow hasArrow
label={runtimeText(runtime, config.web.text.complete_time)} label={runtimeText(runtime, config.web.text.complete_time)}
placement="top" placement="top">
> <Icon name="check" color={defaultStatusColor[colorMode]} mr={4} size={6} />
<Icon
name="check"
color={defaultStatusColor[colorMode]}
mr={4}
size={6}
/>
</Tooltip> </Tooltip>
)} )}
<Text fontSize="lg">{title}</Text> <Text fontSize="lg">{title}</Text>
<AccordionIcon ml="auto" /> <AccordionIcon ml="auto" />
</Stack> </Stack>
); );
} },
); );

View File

@@ -1,9 +1,9 @@
import * as React from "react"; import * as React from 'react';
import { useState } from "react"; import { useState } from 'react';
import { Accordion, Box, Stack, useTheme } from "@chakra-ui/core"; import { Accordion, Box, Stack, useTheme } from '@chakra-ui/core';
import { motion, AnimatePresence } from "framer-motion"; import { motion, AnimatePresence } from 'framer-motion';
import { Label, Result } from "app/components"; import { Label, Result } from 'app/components';
import { useConfig, useMedia } from "app/context"; import { useConfig, useMedia } from 'app/context';
const AnimatedResult = motion.custom(Result); const AnimatedResult = motion.custom(Result);
const AnimatedLabel = motion.custom(Label); const AnimatedLabel = motion.custom(Label);
@@ -13,40 +13,40 @@ const labelInitial = {
sm: { opacity: 0, x: -100 }, sm: { opacity: 0, x: -100 },
md: { opacity: 0, x: -100 }, md: { opacity: 0, x: -100 },
lg: { opacity: 0, x: -100 }, lg: { opacity: 0, x: -100 },
xl: { opacity: 0, x: -100 } xl: { opacity: 0, x: -100 },
}, },
center: { center: {
sm: { opacity: 0 }, sm: { opacity: 0 },
md: { opacity: 0 }, md: { opacity: 0 },
lg: { opacity: 0 }, lg: { opacity: 0 },
xl: { opacity: 0 } xl: { opacity: 0 },
}, },
right: { right: {
sm: { opacity: 0, x: 100 }, sm: { opacity: 0, x: 100 },
md: { opacity: 0, x: 100 }, md: { opacity: 0, x: 100 },
lg: { opacity: 0, x: 100 }, lg: { opacity: 0, x: 100 },
xl: { opacity: 0, x: 100 } xl: { opacity: 0, x: 100 },
} },
}; };
const labelAnimate = { const labelAnimate = {
left: { left: {
sm: { opacity: 1, x: 0 }, sm: { opacity: 1, x: 0 },
md: { opacity: 1, x: 0 }, md: { opacity: 1, x: 0 },
lg: { opacity: 1, x: 0 }, lg: { opacity: 1, x: 0 },
xl: { opacity: 1, x: 0 } xl: { opacity: 1, x: 0 },
}, },
center: { center: {
sm: { opacity: 1 }, sm: { opacity: 1 },
md: { opacity: 1 }, md: { opacity: 1 },
lg: { opacity: 1 }, lg: { opacity: 1 },
xl: { opacity: 1 } xl: { opacity: 1 },
}, },
right: { right: {
sm: { opacity: 1, x: 0 }, sm: { opacity: 1, x: 0 },
md: { opacity: 1, x: 0 }, md: { opacity: 1, x: 0 },
lg: { opacity: 1, x: 0 }, lg: { opacity: 1, x: 0 },
xl: { opacity: 1, x: 0 } xl: { opacity: 1, x: 0 },
} },
}; };
export const Results = ({ export const Results = ({
@@ -61,20 +61,18 @@ export const Results = ({
const theme = useTheme(); const theme = useTheme();
const { mediaSize } = useMedia(); const { mediaSize } = useMedia();
const matchedVrf = const matchedVrf =
config.vrfs.filter(v => v.id === queryVrf)[0] ?? config.vrfs.filter(v => v.id === queryVrf)[0] ?? config.vrfs.filter(v => v.id === 'default')[0];
config.vrfs.filter(v => v.id === "default")[0];
const [resultsComplete, setComplete] = useState(null); const [resultsComplete, setComplete] = useState(null);
return ( return (
<> <>
<Box <Box
maxW={["100%", "100%", "75%", "50%"]} maxW={['100%', '100%', '75%', '50%']}
w="100%" w="100%"
p={0} p={0}
mx="auto" mx="auto"
my={4} my={4}
textAlign="left" textAlign="left"
{...props} {...props}>
>
<Stack isInline align="center" justify="center" mt={4} flexWrap="wrap"> <Stack isInline align="center" justify="center" mt={4} flexWrap="wrap">
<AnimatePresence> <AnimatePresence>
{queryLocation && ( {queryLocation && (
@@ -87,7 +85,7 @@ export const Results = ({
label={config.web.text.query_type} label={config.web.text.query_type}
value={config.queries[queryType].display_name} value={config.queries[queryType].display_name}
valueBg={theme.colors.cyan[500]} valueBg={theme.colors.cyan[500]}
fontSize={["xs", "sm", "sm", "sm"]} fontSize={['xs', 'sm', 'sm', 'sm']}
/> />
<AnimatedLabel <AnimatedLabel
initial={labelInitial.center[mediaSize]} initial={labelInitial.center[mediaSize]}
@@ -97,7 +95,7 @@ export const Results = ({
label={config.web.text.query_target} label={config.web.text.query_target}
value={queryTarget} value={queryTarget}
valueBg={theme.colors.teal[600]} valueBg={theme.colors.teal[600]}
fontSize={["xs", "sm", "sm", "sm"]} fontSize={['xs', 'sm', 'sm', 'sm']}
/> />
<AnimatedLabel <AnimatedLabel
initial={labelInitial.right[mediaSize]} initial={labelInitial.right[mediaSize]}
@@ -107,7 +105,7 @@ export const Results = ({
label={config.web.text.query_vrf} label={config.web.text.query_vrf}
value={matchedVrf.display_name} value={matchedVrf.display_name}
valueBg={theme.colors.blue[500]} valueBg={theme.colors.blue[500]}
fontSize={["xs", "sm", "sm", "sm"]} fontSize={['xs', 'sm', 'sm', 'sm']}
/> />
</> </>
)} )}
@@ -115,7 +113,7 @@ export const Results = ({
</Stack> </Stack>
</Box> </Box>
<Box <Box
maxW={["100%", "100%", "75%", "75%"]} maxW={['100%', '100%', '75%', '75%']}
w="100%" w="100%"
p={0} p={0}
mx="auto" mx="auto"
@@ -123,15 +121,13 @@ export const Results = ({
textAlign="left" textAlign="left"
borderWidth="1px" borderWidth="1px"
rounded="lg" rounded="lg"
overflow="hidden" overflow="hidden">
>
<Accordion <Accordion
allowMultiple allowMultiple
initial={{ opacity: 1 }} initial={{ opacity: 1 }}
transition={{ duration: 0.3 }} transition={{ duration: 0.3 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 300 }} exit={{ opacity: 0, y: 300 }}>
>
<AnimatePresence> <AnimatePresence>
{queryLocation && {queryLocation &&
queryLocation.map((loc, i) => ( queryLocation.map((loc, i) => (

View File

@@ -1,63 +1,57 @@
import * as React from "react"; import * as React from 'react';
import { forwardRef } from "react"; import { forwardRef } from 'react';
import { import { Box, PseudoBox, Spinner, useColorMode, useTheme } from '@chakra-ui/core';
Box, import { FiSearch } from '@meronex/icons/fi';
PseudoBox, import { opposingColor } from 'app/util';
Spinner,
useColorMode,
useTheme
} from "@chakra-ui/core";
import { FiSearch } from "@meronex/icons/fi";
import { opposingColor } from "app/util";
const btnProps = { const btnProps = {
display: "inline-flex", display: 'inline-flex',
appearance: "none", appearance: 'none',
alignItems: "center", alignItems: 'center',
justifyContent: "center", justifyContent: 'center',
transition: "all 250ms", transition: 'all 250ms',
userSelect: "none", userSelect: 'none',
position: "relative", position: 'relative',
whiteSpace: "nowrap", whiteSpace: 'nowrap',
verticalAlign: "middle", verticalAlign: 'middle',
lineHeight: "1.2", lineHeight: '1.2',
outline: "none", outline: 'none',
as: "button", as: 'button',
type: "submit", type: 'submit',
borderRadius: "md", borderRadius: 'md',
fontWeight: "semibold" fontWeight: 'semibold',
}; };
const btnSizeMap = { const btnSizeMap = {
lg: { lg: {
height: 12, height: 12,
minWidth: 12, minWidth: 12,
fontSize: "lg", fontSize: 'lg',
px: 6 px: 6,
}, },
md: { md: {
height: 10, height: 10,
minWidth: 10, minWidth: 10,
fontSize: "md", fontSize: 'md',
px: 4 px: 4,
}, },
sm: { sm: {
height: 8, height: 8,
minWidth: 8, minWidth: 8,
fontSize: "sm", fontSize: 'sm',
px: 3 px: 3,
}, },
xs: { xs: {
height: 6, height: 6,
minWidth: 6, minWidth: 6,
fontSize: "xs", fontSize: 'xs',
px: 2 px: 2,
} },
}; };
const btnBg = { dark: "primary.300", light: "primary.500" }; const btnBg = { dark: 'primary.300', light: 'primary.500' };
const btnBgActive = { dark: "primary.400", light: "primary.600" }; const btnBgActive = { dark: 'primary.400', light: 'primary.600' };
const btnBgHover = { dark: "primary.200", light: "primary.400" }; const btnBgHover = { dark: 'primary.200', light: 'primary.400' };
export const SubmitButton = forwardRef( export const SubmitButton = forwardRef(
( (
@@ -66,12 +60,12 @@ export const SubmitButton = forwardRef(
isDisabled = false, isDisabled = false,
isActive = false, isActive = false,
isFullWidth = false, isFullWidth = false,
size = "lg", size = 'lg',
loadingText, loadingText,
children, children,
...props ...props
}, },
ref ref,
) => { ) => {
const _isDisabled = isDisabled || isLoading; const _isDisabled = isDisabled || isLoading;
const { colorMode } = useColorMode(); const { colorMode } = useColorMode();
@@ -86,8 +80,8 @@ export const SubmitButton = forwardRef(
disabled={_isDisabled} disabled={_isDisabled}
aria-disabled={_isDisabled} aria-disabled={_isDisabled}
aria-label="Submit Query" aria-label="Submit Query"
width={isFullWidth ? "full" : undefined} width={isFullWidth ? 'full' : undefined}
data-active={isActive ? "true" : undefined} data-active={isActive ? 'true' : undefined}
bg={btnBg[colorMode]} bg={btnBg[colorMode]}
color={btnColor} color={btnColor}
_active={{ bg: btnBgActive[colorMode], color: btnColorActive }} _active={{ bg: btnBgActive[colorMode], color: btnColorActive }}
@@ -95,11 +89,10 @@ export const SubmitButton = forwardRef(
_focus={{ boxShadow: theme.shadows.outline }} _focus={{ boxShadow: theme.shadows.outline }}
{...btnProps} {...btnProps}
{...btnSize} {...btnSize}
{...props} {...props}>
>
{isLoading ? ( {isLoading ? (
<Spinner <Spinner
position={loadingText ? "relative" : "absolute"} position={loadingText ? 'relative' : 'absolute'}
mr={loadingText ? 2 : 0} mr={loadingText ? 2 : 0}
color="currentColor" color="currentColor"
size="1em" size="1em"
@@ -116,5 +109,5 @@ export const SubmitButton = forwardRef(
: children} : children}
</PseudoBox> </PseudoBox>
); );
} },
); );

View File

@@ -1,16 +1,16 @@
import * as React from "react"; import * as React from 'react';
import { useMemo } from "react"; import { useMemo } from 'react';
import { Flex, Icon, Text } from "@chakra-ui/core"; import { Flex, Icon, Text } from '@chakra-ui/core';
import { usePagination, useSortBy, useTable } from "react-table"; import { usePagination, useSortBy, useTable } from 'react-table';
import { useMedia } from "app/context"; import { useMedia } from 'app/context';
import { CardBody, CardFooter, CardHeader } from "app/components"; import { CardBody, CardFooter, CardHeader } from 'app/components';
import { TableMain } from "./TableMain"; import { TableMain } from './TableMain';
import { TableCell } from "./TableCell"; import { TableCell } from './TableCell';
import { TableHead } from "./TableHead"; import { TableHead } from './TableHead';
import { TableRow } from "./TableRow"; import { TableRow } from './TableRow';
import { TableBody } from "./TableBody"; import { TableBody } from './TableBody';
import { TableIconButton } from "./TableIconButton"; import { TableIconButton } from './TableIconButton';
import { TableSelectShow } from "./TableSelectShow"; import { TableSelectShow } from './TableSelectShow';
export const Table = ({ export const Table = ({
columns, columns,
@@ -24,7 +24,7 @@ export const Table = ({
cellRender = null, cellRender = null,
rowHighlightProp, rowHighlightProp,
rowHighlightBg, rowHighlightBg,
rowHighlightColor rowHighlightColor,
}) => { }) => {
const tableColumns = useMemo(() => columns, [columns]); const tableColumns = useMemo(() => columns, [columns]);
@@ -34,9 +34,9 @@ export const Table = ({
() => ({ () => ({
minWidth: 100, minWidth: 100,
width: 150, width: 150,
maxWidth: 300 maxWidth: 300,
}), }),
[] [],
); );
let hiddenColumns = []; let hiddenColumns = [];
@@ -60,7 +60,7 @@ export const Table = ({
nextPage, nextPage,
previousPage, previousPage,
setPageSize, setPageSize,
state: { pageIndex, pageSize } state: { pageIndex, pageSize },
} = useTable( } = useTable(
{ {
columns: tableColumns, columns: tableColumns,
@@ -69,11 +69,11 @@ export const Table = ({
initialState: { initialState: {
pageIndex: 0, pageIndex: 0,
pageSize: initialPageSize, pageSize: initialPageSize,
hiddenColumns: hiddenColumns hiddenColumns: hiddenColumns,
} },
}, },
useSortBy, useSortBy,
usePagination usePagination,
); );
return ( return (
@@ -82,20 +82,16 @@ export const Table = ({
<TableMain {...getTableProps()}> <TableMain {...getTableProps()}>
<TableHead> <TableHead>
{headerGroups.map(headerGroup => ( {headerGroups.map(headerGroup => (
<TableRow <TableRow key={headerGroup.id} {...headerGroup.getHeaderGroupProps()}>
key={headerGroup.id}
{...headerGroup.getHeaderGroupProps()}
>
{headerGroup.headers.map(column => ( {headerGroup.headers.map(column => (
<TableCell <TableCell
as="th" as="th"
align={column.align} align={column.align}
key={column.id} key={column.id}
{...column.getHeaderProps()} {...column.getHeaderProps()}
{...column.getSortByToggleProps()} {...column.getSortByToggleProps()}>
>
<Text fontSize="sm" fontWeight="bold" display="inline-block"> <Text fontSize="sm" fontWeight="bold" display="inline-block">
{column.render("Header")} {column.render('Header')}
</Text> </Text>
{column.isSorted ? ( {column.isSorted ? (
column.isSortedDesc ? ( column.isSortedDesc ? (
@@ -104,7 +100,7 @@ export const Table = ({
<Icon name="chevron-up" size={4} ml={1} /> <Icon name="chevron-up" size={4} ml={1} />
) )
) : ( ) : (
"" ''
)} )}
</TableCell> </TableCell>
))} ))}
@@ -124,8 +120,7 @@ export const Table = ({
highlight={row.values[rowHighlightProp] ?? false} highlight={row.values[rowHighlightProp] ?? false}
highlightBg={rowHighlightBg} highlightBg={rowHighlightBg}
highlightColor={rowHighlightColor} highlightColor={rowHighlightColor}
{...row.getRowProps()} {...row.getRowProps()}>
>
{row.cells.map((cell, i) => { {row.cells.map((cell, i) => {
return ( return (
<TableCell <TableCell
@@ -133,14 +128,13 @@ export const Table = ({
cell={cell} cell={cell}
bordersVertical={[bordersVertical, i]} bordersVertical={[bordersVertical, i]}
key={cell.row.index} key={cell.row.index}
{...cell.getCellProps()} {...cell.getCellProps()}>
> {cell.render(cellRender ?? 'Cell')}
{cell.render(cellRender ?? "Cell")}
</TableCell> </TableCell>
); );
})} })}
</TableRow> </TableRow>
) ),
)} )}
</TableBody> </TableBody>
</TableMain> </TableMain>
@@ -161,10 +155,10 @@ export const Table = ({
</Flex> </Flex>
<Flex justifyContent="center" alignItems="center"> <Flex justifyContent="center" alignItems="center">
<Text fontSize="sm" mr={4} whiteSpace="nowrap"> <Text fontSize="sm" mr={4} whiteSpace="nowrap">
Page{" "} Page{' '}
<strong> <strong>
{pageIndex + 1} of {pageOptions.length} {pageIndex + 1} of {pageOptions.length}
</strong>{" "} </strong>{' '}
</Text> </Text>
{!(isSm || isMd) && ( {!(isSm || isMd) && (
<TableSelectShow <TableSelectShow

View File

@@ -1,18 +1,17 @@
/** @jsx jsx */ /** @jsx jsx */
import { jsx } from "@emotion/core"; import { jsx } from '@emotion/core';
import { Box, css } from "@chakra-ui/core"; import { Box, css } from '@chakra-ui/core';
export const TableBody = ({ children, ...props }) => ( export const TableBody = ({ children, ...props }) => (
<Box <Box
as="tbody" as="tbody"
overflowY="scroll" overflowY="scroll"
css={css({ css={css({
"&::-webkit-scrollbar": { display: "none" }, '&::-webkit-scrollbar': { display: 'none' },
"&": { msOverflowStyle: "none" } '&': { msOverflowStyle: 'none' },
})} })}
overflowX="hidden" overflowX="hidden"
{...props} {...props}>
>
{children} {children}
</Box> </Box>
); );

View File

@@ -1,18 +1,12 @@
import * as React from "react"; import * as React from 'react';
import { Box, useColorMode } from "@chakra-ui/core"; import { Box, useColorMode } from '@chakra-ui/core';
const cellBorder = { const cellBorder = {
dark: { borderLeft: "1px", borderLeftColor: "whiteAlpha.100" }, dark: { borderLeft: '1px', borderLeftColor: 'whiteAlpha.100' },
light: { borderLeft: "1px", borderLeftColor: "blackAlpha.100" } light: { borderLeft: '1px', borderLeftColor: 'blackAlpha.100' },
}; };
export const TableCell = ({ export const TableCell = ({ bordersVertical = [false, 0, 0], align, cell, children, ...props }) => {
bordersVertical = [false, 0, 0],
align,
cell,
children,
...props
}) => {
const { colorMode } = useColorMode(); const { colorMode } = useColorMode();
const [doVerticalBorders, index] = bordersVertical; const [doVerticalBorders, index] = bordersVertical;
let borderProps = {}; let borderProps = {};
@@ -28,8 +22,7 @@ export const TableCell = ({
whiteSpace="nowrap" whiteSpace="nowrap"
textAlign={align} textAlign={align}
{...borderProps} {...borderProps}
{...props} {...props}>
>
{children} {children}
</Box> </Box>
); );

View File

@@ -1,18 +1,12 @@
import * as React from "react"; import * as React from 'react';
import { Box, useColorMode } from "@chakra-ui/core"; import { Box, useColorMode } from '@chakra-ui/core';
const bg = { dark: "whiteAlpha.100", light: "blackAlpha.100" }; const bg = { dark: 'whiteAlpha.100', light: 'blackAlpha.100' };
export const TableHead = ({ children, ...props }) => { export const TableHead = ({ children, ...props }) => {
const { colorMode } = useColorMode(); const { colorMode } = useColorMode();
return ( return (
<Box <Box as="thead" overflowX="hidden" overflowY="auto" bg={bg[colorMode]} {...props}>
as="thead"
overflowX="hidden"
overflowY="auto"
bg={bg[colorMode]}
{...props}
>
{children} {children}
</Box> </Box>
); );

View File

@@ -1,14 +1,7 @@
import * as React from "react"; import * as React from 'react';
import { IconButton } from "@chakra-ui/core"; import { IconButton } from '@chakra-ui/core';
export const TableIconButton = ({ export const TableIconButton = ({ icon, onClick, isDisabled, color, children, ...props }) => (
icon,
onClick,
isDisabled,
color,
children,
...props
}) => (
<IconButton <IconButton
size="sm" size="sm"
icon={icon} icon={icon}
@@ -17,8 +10,7 @@ export const TableIconButton = ({
variantColor={color} variantColor={color}
isDisabled={isDisabled} isDisabled={isDisabled}
aria-label="Table Icon Button" aria-label="Table Icon Button"
{...props} {...props}>
>
{children} {children}
</IconButton> </IconButton>
); );

View File

@@ -1,10 +1,10 @@
/** @jsx jsx */ /** @jsx jsx */
import { jsx } from "@emotion/core"; import { jsx } from '@emotion/core';
import { Box, css, useTheme, useColorMode } from "@chakra-ui/core"; import { Box, css, useTheme, useColorMode } from '@chakra-ui/core';
const scrollbar = { dark: "whiteAlpha.300", light: "blackAlpha.300" }; const scrollbar = { dark: 'whiteAlpha.300', light: 'blackAlpha.300' };
const scrollbarHover = { dark: "whiteAlpha.400", light: "blackAlpha.400" }; const scrollbarHover = { dark: 'whiteAlpha.400', light: 'blackAlpha.400' };
const scrollbarBg = { dark: "whiteAlpha.50", light: "blackAlpha.50" }; const scrollbarBg = { dark: 'whiteAlpha.50', light: 'blackAlpha.50' };
export const TableMain = ({ children, ...props }) => { export const TableMain = ({ children, ...props }) => {
const theme = useTheme(); const theme = useTheme();
@@ -14,24 +14,23 @@ export const TableMain = ({ children, ...props }) => {
as="table" as="table"
display="block" display="block"
css={css({ css={css({
"&::-webkit-scrollbar": { height: "5px" }, '&::-webkit-scrollbar': { height: '5px' },
"&::-webkit-scrollbar-track": { '&::-webkit-scrollbar-track': {
backgroundColor: scrollbarBg[colorMode] backgroundColor: scrollbarBg[colorMode],
}, },
"&::-webkit-scrollbar-thumb": { '&::-webkit-scrollbar-thumb': {
backgroundColor: scrollbar[colorMode] backgroundColor: scrollbar[colorMode],
}, },
"&::-webkit-scrollbar-thumb:hover": { '&::-webkit-scrollbar-thumb:hover': {
backgroundColor: scrollbarHover[colorMode] backgroundColor: scrollbarHover[colorMode],
}, },
"-ms-overflow-style": { display: "none" } '-ms-overflow-style': { display: 'none' },
})(theme)} })(theme)}
overflowX="auto" overflowX="auto"
borderRadius="md" borderRadius="md"
boxSizing="border-box" boxSizing="border-box"
{...props} {...props}>
>
{children} {children}
</Box> </Box>
); );

View File

@@ -1,19 +1,19 @@
import * as React from "react"; import * as React from 'react';
import { PseudoBox, useColorMode, useTheme } from "@chakra-ui/core"; import { PseudoBox, useColorMode, useTheme } from '@chakra-ui/core';
import { opposingColor } from "app/util"; import { opposingColor } from 'app/util';
const hoverBg = { dark: "whiteAlpha.50", light: "blackAlpha.50" }; const hoverBg = { dark: 'whiteAlpha.50', light: 'blackAlpha.50' };
const bgStripe = { dark: "whiteAlpha.50", light: "blackAlpha.50" }; const bgStripe = { dark: 'whiteAlpha.50', light: 'blackAlpha.50' };
const rowBorder = { const rowBorder = {
dark: { borderTop: "1px", borderTopColor: "whiteAlpha.100" }, dark: { borderTop: '1px', borderTopColor: 'whiteAlpha.100' },
light: { borderTop: "1px", borderTopColor: "blackAlpha.100" } light: { borderTop: '1px', borderTopColor: 'blackAlpha.100' },
}; };
const alphaMap = { dark: "200", light: "100" }; const alphaMap = { dark: '200', light: '100' };
const alphaMapHover = { dark: "100", light: "200" }; const alphaMapHover = { dark: '100', light: '200' };
export const TableRow = ({ export const TableRow = ({
highlight = false, highlight = false,
highlightBg = "primary", highlightBg = 'primary',
doStripe = false, doStripe = false,
doHorizontalBorders = false, doHorizontalBorders = false,
index = 0, index = 0,
@@ -31,23 +31,21 @@ export const TableRow = ({
} }
const color = highlight ? opposingColor(theme, bg) : null; const color = highlight ? opposingColor(theme, bg) : null;
const borderProps = const borderProps = doHorizontalBorders && index !== 0 ? rowBorder[colorMode] : {};
doHorizontalBorders && index !== 0 ? rowBorder[colorMode] : {};
return ( return (
<PseudoBox <PseudoBox
as="tr" as="tr"
_hover={{ _hover={{
cursor: "pointer", cursor: 'pointer',
backgroundColor: highlight backgroundColor: highlight
? `${highlightBg}.${alphaMapHover[colorMode]}` ? `${highlightBg}.${alphaMapHover[colorMode]}`
: hoverBg[colorMode] : hoverBg[colorMode],
}} }}
bg={bg} bg={bg}
color={color} color={color}
fontWeight={highlight ? "bold" : null} fontWeight={highlight ? 'bold' : null}
{...borderProps} {...borderProps}
{...props} {...props}>
>
{children} {children}
</PseudoBox> </PseudoBox>
); );

View File

@@ -1,5 +1,5 @@
import * as React from "react"; import * as React from 'react';
import { Select } from "@chakra-ui/core"; import { Select } from '@chakra-ui/core';
export const TableSelectShow = ({ value, onChange, children, ...props }) => ( export const TableSelectShow = ({ value, onChange, children, ...props }) => (
<Select size="sm" onChange={onChange} {...props}> <Select size="sm" onChange={onChange} {...props}>

View File

@@ -1,8 +1,8 @@
export * from "./Table"; export * from './Table';
export * from "./TableBody"; export * from './TableBody';
export * from "./TableCell"; export * from './TableCell';
export * from "./TableHead"; export * from './TableHead';
export * from "./TableIconButton"; export * from './TableIconButton';
export * from "./TableMain"; export * from './TableMain';
export * from "./TableRow"; export * from './TableRow';
export * from "./TableSelectShow"; export * from './TableSelectShow';

View File

@@ -1,11 +1,11 @@
/** @jsx jsx */ /** @jsx jsx */
import { jsx } from "@emotion/core"; import { jsx } from '@emotion/core';
import { Box, css, useColorMode } from "@chakra-ui/core"; import { Box, css, useColorMode } from '@chakra-ui/core';
const bg = { dark: "gray.800", light: "blackAlpha.100" }; const bg = { dark: 'gray.800', light: 'blackAlpha.100' };
const color = { dark: "white", light: "black" }; const color = { dark: 'white', light: 'black' };
const selectionBg = { dark: "white", light: "black" }; const selectionBg = { dark: 'white', light: 'black' };
const selectionColor = { dark: "black", light: "white" }; const selectionColor = { dark: 'black', light: 'white' };
export const TextOutput = ({ children, ...props }) => { export const TextOutput = ({ children, ...props }) => {
const { colorMode } = useColorMode(); const { colorMode } = useColorMode();
@@ -25,17 +25,16 @@ export const TextOutput = ({ children, ...props }) => {
whiteSpace="pre-wrap" whiteSpace="pre-wrap"
as="pre" as="pre"
css={css({ css={css({
"&::selection": { '&::selection': {
backgroundColor: selectionBg[colorMode], backgroundColor: selectionBg[colorMode],
color: selectionColor[colorMode] color: selectionColor[colorMode],
} },
})} })}
{...props} {...props}>
>
{children {children
.split("\\n") .split('\\n')
.join("\n") .join('\n')
.replace(/\n\n/g, "\n")} .replace(/\n\n/g, '\n')}
</Box> </Box>
); );
}; };

View File

@@ -1,31 +1,25 @@
/** @jsx jsx */ /** @jsx jsx */
import { jsx } from "@emotion/core"; import { jsx } from '@emotion/core';
import { forwardRef } from "react"; import { forwardRef } from 'react';
import { Button, Heading, Image, Stack, useColorMode } from "@chakra-ui/core"; import { Button, Heading, Image, Stack, useColorMode } from '@chakra-ui/core';
import { useConfig, useMedia } from "app/context"; import { useConfig, useMedia } from 'app/context';
const titleSize = { true: ["2xl", "2xl", "5xl", "5xl"], false: "2xl" }; const titleSize = { true: ['2xl', '2xl', '5xl', '5xl'], false: '2xl' };
const titleMargin = { true: 2, false: 0 }; const titleMargin = { true: 2, false: 0 };
const textAlignment = { false: ["right", "center"], true: ["left", "center"] }; const textAlignment = { false: ['right', 'center'], true: ['left', 'center'] };
const logoName = { light: "dark", dark: "light" }; const logoName = { light: 'dark', dark: 'light' };
const justifyMap = { const justifyMap = {
true: ["flex-end", "center", "center", "center"], true: ['flex-end', 'center', 'center', 'center'],
false: ["flex-start", "center", "center", "center"] false: ['flex-start', 'center', 'center', 'center'],
}; };
const logoFallback = { const logoFallback = {
light: light: 'https://res.cloudinary.com/hyperglass/image/upload/v1593916013/logo-dark.svg',
"https://res.cloudinary.com/hyperglass/image/upload/v1593916013/logo-dark.svg", dark: 'https://res.cloudinary.com/hyperglass/image/upload/v1593916013/logo-light.svg',
dark:
"https://res.cloudinary.com/hyperglass/image/upload/v1593916013/logo-light.svg"
}; };
const TitleOnly = ({ text, showSubtitle }) => ( const TitleOnly = ({ text, showSubtitle }) => (
<Heading <Heading as="h1" mb={titleMargin[showSubtitle]} fontSize={titleSize[showSubtitle]}>
as="h1"
mb={titleMargin[showSubtitle]}
fontSize={titleSize[showSubtitle]}
>
{text} {text}
</Heading> </Heading>
); );
@@ -33,26 +27,18 @@ const TitleOnly = ({ text, showSubtitle }) => (
const SubtitleOnly = ({ text, mediaSize, ...props }) => ( const SubtitleOnly = ({ text, mediaSize, ...props }) => (
<Heading <Heading
as="h3" as="h3"
fontSize={["md", "md", "xl", "xl"]} fontSize={['md', 'md', 'xl', 'xl']}
whiteSpace="break-spaces" whiteSpace="break-spaces"
textAlign={["left", "left", "center", "center"]} textAlign={['left', 'left', 'center', 'center']}
{...props} {...props}>
>
{text} {text}
</Heading> </Heading>
); );
const TextOnly = ({ text, mediaSize, showSubtitle, ...props }) => ( const TextOnly = ({ text, mediaSize, showSubtitle, ...props }) => (
<Stack <Stack spacing={2} maxW="100%" textAlign={textAlignment[showSubtitle]} {...props}>
spacing={2}
maxW="100%"
textAlign={textAlignment[showSubtitle]}
{...props}
>
<TitleOnly text={text.title} showSubtitle={showSubtitle} /> <TitleOnly text={text.title} showSubtitle={showSubtitle} />
{showSubtitle && ( {showSubtitle && <SubtitleOnly text={text.subtitle} mediaSize={mediaSize} />}
<SubtitleOnly text={text.subtitle} mediaSize={mediaSize} />
)}
</Stack> </Stack>
); );
@@ -63,15 +49,15 @@ const Logo = ({ text, logo }) => {
return ( return (
<Image <Image
css={{ css={{
userDrag: "none", userDrag: 'none',
userSelect: "none", userSelect: 'none',
msUserSelect: "none", msUserSelect: 'none',
MozUserSelect: "none", MozUserSelect: 'none',
WebkitUserDrag: "none", WebkitUserDrag: 'none',
WebkitUserSelect: "none" WebkitUserSelect: 'none',
}} }}
alt={text.title} alt={text.title}
width={width ?? "auto"} width={width ?? 'auto'}
fallbackSrc={logoFallback[colorMode]} fallbackSrc={logoFallback[colorMode]}
src={`/images/${logoName[colorMode]}${logoExt[colorMode]}`} src={`/images/${logoName[colorMode]}${logoExt[colorMode]}`}
/> />
@@ -88,12 +74,7 @@ const LogoSubtitle = ({ text, logo, mediaSize }) => (
const All = ({ text, logo, mediaSize, showSubtitle }) => ( const All = ({ text, logo, mediaSize, showSubtitle }) => (
<> <>
<Logo text={text} logo={logo} /> <Logo text={text} logo={logo} />
<TextOnly <TextOnly mediaSize={mediaSize} showSubtitle={showSubtitle} mt={2} text={text} />
mediaSize={mediaSize}
showSubtitle={showSubtitle}
mt={2}
text={text}
/>
</> </>
); );
@@ -101,7 +82,7 @@ const modeMap = {
text_only: TextOnly, text_only: TextOnly,
logo_only: Logo, logo_only: Logo,
logo_subtitle: LogoSubtitle, logo_subtitle: LogoSubtitle,
all: All all: All,
}; };
export const Title = forwardRef(({ onClick, isSubmitting, ...props }, ref) => { export const Title = forwardRef(({ onClick, isSubmitting, ...props }, ref) => {
@@ -118,12 +99,11 @@ export const Title = forwardRef(({ onClick, isSubmitting, ...props }, ref) => {
flexWrap="wrap" flexWrap="wrap"
flexDir="column" flexDir="column"
onClick={onClick} onClick={onClick}
_focus={{ boxShadow: "none" }} _focus={{ boxShadow: 'none' }}
_hover={{ textDecoration: "none" }} _hover={{ textDecoration: 'none' }}
justifyContent={justifyMap[isSubmitting]} justifyContent={justifyMap[isSubmitting]}
alignItems={["flex-start", "flex-start", "center"]} alignItems={['flex-start', 'flex-start', 'center']}
{...props} {...props}>
>
<MatchedMode <MatchedMode
mediaSize={mediaSize} mediaSize={mediaSize}
showSubtitle={!isSubmitting} showSubtitle={!isSubmitting}

View File

@@ -1,35 +1,35 @@
export * from "./BGPTable"; export * from './BGPTable';
export * from "./CacheTimeout"; export * from './CacheTimeout';
export * from "./Card"; export * from './Card';
export * from "./ChakraSelect"; export * from './ChakraSelect';
export * from "./CodeBlock"; export * from './CodeBlock';
export * from "./ColorModeToggle"; export * from './ColorModeToggle';
export * from "./CommunitySelect"; export * from './CommunitySelect';
export * from "./CopyButton"; export * from './CopyButton';
export * from "./Debugger"; export * from './Debugger';
export * from "./Footer"; export * from './Footer';
export * from "./FormField"; export * from './FormField';
export * from "./Greeting"; export * from './Greeting';
export * from "./Header"; export * from './Header';
export * from "./HelpModal"; export * from './HelpModal';
export * from "./HyperglassForm"; export * from './HyperglassForm';
export * from "./Label"; export * from './Label';
export * from "./Layout"; export * from './Layout';
export * from "./Loading"; export * from './Loading';
export * from "./LookingGlass"; export * from './LookingGlass';
export * from "./Markdown"; export * from './Markdown';
export * from "./Meta"; export * from './Meta';
export * from "./QueryLocation"; export * from './QueryLocation';
export * from "./QueryTarget"; export * from './QueryTarget';
export * from "./QueryType"; export * from './QueryType';
export * from "./QueryVrf"; export * from './QueryVrf';
export * from "./RequeryButton"; export * from './RequeryButton';
export * from "./ResetButton"; export * from './ResetButton';
export * from "./ResolvedTarget"; export * from './ResolvedTarget';
export * from "./Result"; export * from './Result';
export * from "./ResultHeader"; export * from './ResultHeader';
export * from "./Results"; export * from './Results';
export * from "./SubmitButton"; export * from './SubmitButton';
export * from "./Table"; export * from './Table';
export * from "./TextOutput"; export * from './TextOutput';
export * from "./Title"; export * from './Title';

View File

@@ -1,15 +1,15 @@
import * as React from "react"; import * as React from 'react';
import { createContext, useContext, useMemo } from "react"; import { createContext, useContext, useMemo } from 'react';
import dynamic from "next/dynamic"; import dynamic from 'next/dynamic';
import { CSSReset, ThemeProvider } from "@chakra-ui/core"; import { CSSReset, ThemeProvider } from '@chakra-ui/core';
import { MediaProvider } from "./MediaProvider"; import { MediaProvider } from './MediaProvider';
import { StateProvider } from "./StateProvider"; import { StateProvider } from './StateProvider';
import { makeTheme, defaultTheme } from "app/util"; import { makeTheme, defaultTheme } from 'app/util';
// Disable SSR for ColorModeProvider // Disable SSR for ColorModeProvider
const ColorModeProvider = dynamic( const ColorModeProvider = dynamic(
() => import("@chakra-ui/core").then(mod => mod.ColorModeProvider), () => import('@chakra-ui/core').then(mod => mod.ColorModeProvider),
{ ssr: false } { ssr: false },
); );
const HyperglassContext = createContext(null); const HyperglassContext = createContext(null);

View File

@@ -1,6 +1,6 @@
import * as React from "react"; import * as React from 'react';
import { createContext, useContext, useMemo } from "react"; import { createContext, useContext, useMemo } from 'react';
import { useMediaLayout } from "use-media"; import { useMediaLayout } from 'use-media';
const MediaContext = createContext(null); const MediaContext = createContext(null);
@@ -13,16 +13,16 @@ export const MediaProvider = ({ theme, children }) => {
let mediaSize = false; let mediaSize = false;
switch (true) { switch (true) {
case isSm: case isSm:
mediaSize = "sm"; mediaSize = 'sm';
break; break;
case isMd: case isMd:
mediaSize = "md"; mediaSize = 'md';
break; break;
case isLg: case isLg:
mediaSize = "lg"; mediaSize = 'lg';
break; break;
case isXl: case isXl:
mediaSize = "xl"; mediaSize = 'xl';
break; break;
} }
const value = useMemo( const value = useMemo(
@@ -31,13 +31,11 @@ export const MediaProvider = ({ theme, children }) => {
isMd: isMd, isMd: isMd,
isLg: isLg, isLg: isLg,
isXl: isXl, isXl: isXl,
mediaSize: mediaSize mediaSize: mediaSize,
}), }),
[isSm, isMd, isLg, isXl, mediaSize] [isSm, isMd, isLg, isXl, mediaSize],
);
return (
<MediaContext.Provider value={value}>{children}</MediaContext.Provider>
); );
return <MediaContext.Provider value={value}>{children}</MediaContext.Provider>;
}; };
export const useMedia = () => useContext(MediaContext); export const useMedia = () => useContext(MediaContext);

View File

@@ -1,18 +1,15 @@
import * as React from "react"; import * as React from 'react';
import { createContext, useContext, useMemo, useState } from "react"; import { createContext, useContext, useMemo, useState } from 'react';
import { useSessionStorage } from "app/hooks"; import { useSessionStorage } from 'app/hooks';
const StateContext = createContext(null); const StateContext = createContext(null);
export const StateProvider = ({ children }) => { export const StateProvider = ({ children }) => {
const [isSubmitting, setSubmitting] = useState(false); const [isSubmitting, setSubmitting] = useState(false);
const [formData, setFormData] = useState({}); const [formData, setFormData] = useState({});
const [greetingAck, setGreetingAck] = useSessionStorage( const [greetingAck, setGreetingAck] = useSessionStorage('hyperglass-greeting-ack', false);
"hyperglass-greeting-ack",
false
);
const resetForm = layoutRef => { const resetForm = layoutRef => {
layoutRef.current.scrollIntoView({ behavior: "smooth", block: "start" }); layoutRef.current.scrollIntoView({ behavior: 'smooth', block: 'start' });
setSubmitting(false); setSubmitting(false);
setFormData({}); setFormData({});
}; };
@@ -23,11 +20,9 @@ export const StateProvider = ({ children }) => {
setFormData, setFormData,
greetingAck, greetingAck,
setGreetingAck, setGreetingAck,
resetForm resetForm,
})); }));
return ( return <StateContext.Provider value={value}>{children}</StateContext.Provider>;
<StateContext.Provider value={value}>{children}</StateContext.Provider>
);
}; };
export const useHyperglassState = () => useContext(StateContext); export const useHyperglassState = () => useContext(StateContext);

View File

@@ -1,3 +1,3 @@
export * from "./HyperglassProvider"; export * from './HyperglassProvider';
export * from "./MediaProvider"; export * from './MediaProvider';
export * from "./StateProvider"; export * from './StateProvider';

View File

@@ -1 +1 @@
export * from "./useSessionStorage"; export * from './useSessionStorage';

View File

@@ -3,10 +3,10 @@ react-use: useSessionStorage
https://github.com/streamich/react-use/blob/master/src/useSessionStorage.ts https://github.com/streamich/react-use/blob/master/src/useSessionStorage.ts
*/ */
import { useEffect, useState } from "react"; import { useEffect, useState } from 'react';
export const useSessionStorage = (key, initialValue, raw) => { export const useSessionStorage = (key, initialValue, raw) => {
const isClient = typeof window === "object"; const isClient = typeof window === 'object';
if (!isClient) { if (!isClient) {
return [initialValue, () => {}]; return [initialValue, () => {}];
} }
@@ -14,16 +14,11 @@ export const useSessionStorage = (key, initialValue, raw) => {
const [state, setState] = useState(() => { const [state, setState] = useState(() => {
try { try {
const sessionStorageValue = sessionStorage.getItem(key); const sessionStorageValue = sessionStorage.getItem(key);
if (typeof sessionStorageValue !== "string") { if (typeof sessionStorageValue !== 'string') {
sessionStorage.setItem( sessionStorage.setItem(key, raw ? String(initialValue) : JSON.stringify(initialValue));
key,
raw ? String(initialValue) : JSON.stringify(initialValue)
);
return initialValue; return initialValue;
} else { } else {
return raw return raw ? sessionStorageValue : JSON.parse(sessionStorageValue || 'null');
? sessionStorageValue
: JSON.parse(sessionStorageValue || "null");
} }
} catch { } catch {
// If user is in private mode or has storage restriction // If user is in private mode or has storage restriction

View File

@@ -1,5 +1,5 @@
// const aliases = require("./.alias"); // const aliases = require("./.alias");
const envVars = require("/tmp/hyperglass.env.json"); const envVars = require('/tmp/hyperglass.env.json');
const { configFile } = envVars; const { configFile } = envVars;
const config = require(String(configFile)); const config = require(String(configFile));
@@ -18,6 +18,6 @@ module.exports = {
_NODE_ENV_: config.NODE_ENV, _NODE_ENV_: config.NODE_ENV,
_HYPERGLASS_URL_: config._HYPERGLASS_URL_, _HYPERGLASS_URL_: config._HYPERGLASS_URL_,
_HYPERGLASS_CONFIG_: config._HYPERGLASS_CONFIG_, _HYPERGLASS_CONFIG_: config._HYPERGLASS_CONFIG_,
_HYPERGLASS_FAVICONS_: config._HYPERGLASS_FAVICONS_ _HYPERGLASS_FAVICONS_: config._HYPERGLASS_FAVICONS_,
} },
}; };

View File

@@ -1,51 +1,52 @@
/* eslint-disable no-console */ /* eslint-disable no-console */
const express = require("express"); const express = require('express');
const proxyMiddleware = require("http-proxy-middleware"); const proxyMiddleware = require('http-proxy-middleware');
const next = require("next"); const next = require('next');
const envVars = require("/tmp/hyperglass.env.json"); const envVars = require('/tmp/hyperglass.env.json');
const { configFile } = envVars; const { configFile } = envVars;
const config = require(String(configFile)); const config = require(String(configFile));
const { NODE_ENV: env, _HYPERGLASS_URL_: envUrl } = config; const { NODE_ENV: env, _HYPERGLASS_URL_: envUrl } = config;
const devProxy = { const devProxy = {
"/api/query/": { target: envUrl + "api/query/", pathRewrite: { "^/api/query/": "" } }, '/api/query/': { target: envUrl + 'api/query/', pathRewrite: { '^/api/query/': '' } },
"/images": { target: envUrl + "images", pathRewrite: { "^/images": "" } }, '/images': { target: envUrl + 'images', pathRewrite: { '^/images': '' } },
"/custom": { target: envUrl + "custom", pathRewrite: { "^/custom": "" } } '/custom': { target: envUrl + 'custom', pathRewrite: { '^/custom': '' } },
}; };
const port = parseInt(process.env.PORT, 10) || 3000; const port = parseInt(process.env.PORT, 10) || 3000;
const dev = env !== "production"; const dev = env !== 'production';
const app = next({ const app = next({
dir: ".", // base directory where everything is, could move to src later dir: '.', // base directory where everything is, could move to src later
dev dev,
}); });
const handle = app.getRequestHandler(); const handle = app.getRequestHandler();
let server; let server;
app.prepare() app
.then(() => { .prepare()
server = express(); .then(() => {
server = express();
// Set up the proxy. // Set up the proxy.
if (dev && devProxy) { if (dev && devProxy) {
Object.keys(devProxy).forEach(function(context) { Object.keys(devProxy).forEach(function(context) {
server.use(proxyMiddleware(context, devProxy[context])); server.use(proxyMiddleware(context, devProxy[context]));
}); });
} }
// Default catch-all handler to allow Next.js to handle all other routes // Default catch-all handler to allow Next.js to handle all other routes
server.all("*", (req, res) => handle(req, res)); server.all('*', (req, res) => handle(req, res));
server.listen(port, err => { server.listen(port, err => {
if (err) { if (err) {
throw err; throw err;
} }
console.log(`> Ready on port ${port} [${env}]`); console.log(`> Ready on port ${port} [${env}]`);
});
})
.catch(err => {
console.log("An error occurred, unable to start the server");
console.log(err);
}); });
})
.catch(err => {
console.log('An error occurred, unable to start the server');
console.log(err);
});

View File

@@ -45,17 +45,15 @@
"devDependencies": { "devDependencies": {
"@typescript-eslint/eslint-plugin": "^2.24.0", "@typescript-eslint/eslint-plugin": "^2.24.0",
"@typescript-eslint/parser": "^2.24.0", "@typescript-eslint/parser": "^2.24.0",
"@upstatement/eslint-config": "^0.4.3",
"@upstatement/prettier-config": "^0.3.0",
"babel-eslint": "^10.1.0", "babel-eslint": "^10.1.0",
"es-check": "^5.1.0", "es-check": "^5.1.0",
"eslint": "^6.8.0", "eslint": "^6.8.0",
"eslint-config-airbnb": "^18.1.0",
"eslint-config-prettier": "^6.10.0",
"eslint-config-react-app": "^5.2.0", "eslint-config-react-app": "^5.2.0",
"eslint-import-resolver-webpack": "^0.13.0", "eslint-import-resolver-webpack": "^0.13.0",
"eslint-plugin-flowtype": "^4.6.0",
"eslint-plugin-import": "^2.20.1", "eslint-plugin-import": "^2.20.1",
"eslint-plugin-jsx-a11y": "^6.2.3", "eslint-plugin-jsx-a11y": "^6.2.3",
"eslint-plugin-prettier": "^3.1.2",
"eslint-plugin-react": "^7.19.0", "eslint-plugin-react": "^7.19.0",
"eslint-plugin-react-hooks": "^2.3.0", "eslint-plugin-react-hooks": "^2.3.0",
"express": "^4.17.1", "express": "^4.17.1",

View File

@@ -1,7 +1,7 @@
import * as React from "react"; import * as React from 'react';
import Head from "next/head"; import Head from 'next/head';
// import { useRouter } from "next/router"; // import { useRouter } from "next/router";
import { HyperglassProvider } from "app/context"; import { HyperglassProvider } from 'app/context';
// import Error from "./_error"; // import Error from "./_error";
const config = process.env._HYPERGLASS_CONFIG_; const config = process.env._HYPERGLASS_CONFIG_;

View File

@@ -1,5 +1,5 @@
import React from "react"; import React from 'react';
import Document, { Html, Head, Main, NextScript } from "next/document"; import Document, { Html, Head, Main, NextScript } from 'next/document';
class MyDocument extends Document { class MyDocument extends Document {
static async getInitialProps(ctx) { static async getInitialProps(ctx) {
@@ -13,11 +13,7 @@ class MyDocument extends Document {
<Head> <Head>
<link rel="dns-prefetch" href="//fonts.googleapis.com" /> <link rel="dns-prefetch" href="//fonts.googleapis.com" />
<link rel="dns-prefetch" href="//fonts.gstatic.com" /> <link rel="dns-prefetch" href="//fonts.gstatic.com" />
<link <link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="true" />
rel="preconnect"
href="https://fonts.gstatic.com"
crossOrigin="true"
/>
<link rel="preconnect" href="https://www.google-analytics.com" /> <link rel="preconnect" href="https://www.google-analytics.com" />
</Head> </Head>
<body> <body>

View File

@@ -1,6 +1,6 @@
import React from "react"; import React from 'react';
import dynamic from "next/dynamic"; import dynamic from 'next/dynamic';
import { useRouter } from "next/router"; import { useRouter } from 'next/router';
import { import {
Button, Button,
CSSReset, CSSReset,
@@ -9,42 +9,37 @@ import {
Text, Text,
ThemeProvider, ThemeProvider,
useColorMode, useColorMode,
theme as defaultTheme theme as defaultTheme,
} from "@chakra-ui/core"; } from '@chakra-ui/core';
import { inRange } from "lodash"; import { inRange } from 'lodash';
const ColorModeProvider = dynamic( const ColorModeProvider = dynamic(
() => import("@chakra-ui/core").then(mod => mod.ColorModeProvider), () => import('@chakra-ui/core').then(mod => mod.ColorModeProvider),
{ ssr: false } { ssr: false },
); );
const ErrorContent = ({ msg, statusCode }) => { const ErrorContent = ({ msg, statusCode }) => {
const { colorMode } = useColorMode(); const { colorMode } = useColorMode();
const bg = { light: "white", dark: "black" }; const bg = { light: 'white', dark: 'black' };
const baseCode = inRange(statusCode, 400, 500) const baseCode = inRange(statusCode, 400, 500) ? 400 : inRange(statusCode, 500, 600) ? 500 : 400;
? 400
: inRange(statusCode, 500, 600)
? 500
: 400;
const errorColor = { const errorColor = {
400: { light: "error.500", dark: "error.300" }, 400: { light: 'error.500', dark: 'error.300' },
500: { light: "danger.500", dark: "danger.300" } 500: { light: 'danger.500', dark: 'danger.300' },
}; };
const variantColor = { const variantColor = {
400: "error", 400: 'error',
500: "danger" 500: 'danger',
}; };
const color = { light: "black", dark: "white" }; const color = { light: 'black', dark: 'white' };
const { push } = useRouter(); const { push } = useRouter();
const handleClick = () => push("/"); const handleClick = () => push('/');
return ( return (
<Flex <Flex
w="100%" w="100%"
minHeight="100vh" minHeight="100vh"
bg={bg[colorMode]} bg={bg[colorMode]}
flexDirection="column" flexDirection="column"
color={color[colorMode]} color={color[colorMode]}>
>
<Flex <Flex
px={2} px={2}
py={0} py={0}
@@ -57,8 +52,7 @@ const ErrorContent = ({ msg, statusCode }) => {
alignItems="center" alignItems="center"
flexDirection="column" flexDirection="column"
justifyContent="start" justifyContent="start"
mt={["50%", "50%", "50%", "25%"]} mt={['50%', '50%', '50%', '25%']}>
>
<Heading mb={4} as="h1" fontSize="2xl"> <Heading mb={4} as="h1" fontSize="2xl">
<Text as="span" color={errorColor[baseCode][colorMode]}> <Text as="span" color={errorColor[baseCode][colorMode]}>
{msg} {msg}
@@ -66,11 +60,7 @@ const ErrorContent = ({ msg, statusCode }) => {
{statusCode === 404 && <Text as="span"> isn't a thing...</Text>} {statusCode === 404 && <Text as="span"> isn't a thing...</Text>}
</Heading> </Heading>
<Button <Button variant="outline" onClick={handleClick} variantColor={variantColor[baseCode]}>
variant="outline"
onClick={handleClick}
variantColor={variantColor[baseCode]}
>
Home Home
</Button> </Button>
</Flex> </Flex>
@@ -91,7 +81,7 @@ const ErrorPage = ({ msg, statusCode }) => {
ErrorPage.getInitialProps = ({ res, err }) => { ErrorPage.getInitialProps = ({ res, err }) => {
const statusCode = res ? res.statusCode : err ? err.statusCode : 404; const statusCode = res ? res.statusCode : err ? err.statusCode : 404;
const msg = err ? err.message : res.req?.url || "Error"; const msg = err ? err.message : res.req?.url || 'Error';
return { msg, statusCode }; return { msg, statusCode };
}; };

View File

@@ -1,12 +1,12 @@
import * as React from "react"; import * as React from 'react';
import Head from "next/head"; import Head from 'next/head';
import dynamic from "next/dynamic"; import dynamic from 'next/dynamic';
import { Meta, Loading } from "app/components"; import { Meta, Loading } from 'app/components';
const LookingGlass = dynamic( const LookingGlass = dynamic(
() => import("app/components/LookingGlass").then(i => i.LookingGlass), () => import('app/components/LookingGlass').then(i => i.LookingGlass),
{ {
loading: Loading loading: Loading,
} },
); );
const Index = ({ faviconComponents }) => { const Index = ({ faviconComponents }) => {
@@ -31,8 +31,8 @@ export async function getStaticProps(context) {
}); });
return { return {
props: { props: {
faviconComponents: components faviconComponents: components,
} },
}; };
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
import { theme as chakraTheme } from "@chakra-ui/core"; import { theme as chakraTheme } from '@chakra-ui/core';
import chroma from "chroma-js"; import chroma from 'chroma-js';
const alphaColors = color => ({ const alphaColors = color => ({
900: chroma(color) 900: chroma(color)
@@ -31,41 +31,26 @@ const alphaColors = color => ({
.css(), .css(),
50: chroma(color) 50: chroma(color)
.alpha(0.04) .alpha(0.04)
.css() .css(),
}); });
const generateColors = colorInput => { const generateColors = colorInput => {
const colorMap = {}; const colorMap = {};
const lightnessMap = [ const lightnessMap = [0.95, 0.85, 0.75, 0.65, 0.55, 0.45, 0.35, 0.25, 0.15, 0.05];
0.95,
0.85,
0.75,
0.65,
0.55,
0.45,
0.35,
0.25,
0.15,
0.05
];
const saturationMap = [0.32, 0.16, 0.08, 0.04, 0, 0, 0.04, 0.08, 0.16, 0.32]; const saturationMap = [0.32, 0.16, 0.08, 0.04, 0, 0, 0.04, 0.08, 0.16, 0.32];
const validColor = chroma.valid(colorInput.trim()) const validColor = chroma.valid(colorInput.trim()) ? chroma(colorInput.trim()) : chroma('#000');
? chroma(colorInput.trim())
: chroma("#000");
const lightnessGoal = validColor.get("hsl.l"); const lightnessGoal = validColor.get('hsl.l');
const closestLightness = lightnessMap.reduce((prev, curr) => const closestLightness = lightnessMap.reduce((prev, curr) =>
Math.abs(curr - lightnessGoal) < Math.abs(prev - lightnessGoal) Math.abs(curr - lightnessGoal) < Math.abs(prev - lightnessGoal) ? curr : prev,
? curr
: prev
); );
const baseColorIndex = lightnessMap.findIndex(l => l === closestLightness); const baseColorIndex = lightnessMap.findIndex(l => l === closestLightness);
const colors = lightnessMap const colors = lightnessMap
.map(l => validColor.set("hsl.l", l)) .map(l => validColor.set('hsl.l', l))
.map(color => chroma(color)) .map(color => chroma(color))
.map((color, i) => { .map((color, i) => {
const saturationDelta = saturationMap[i] - saturationMap[baseColorIndex]; const saturationDelta = saturationMap[i] - saturationMap[baseColorIndex];
@@ -84,31 +69,31 @@ const generateColors = colorInput => {
}; };
const defaultBodyFonts = [ const defaultBodyFonts = [
"-apple-system", '-apple-system',
"BlinkMacSystemFont", 'BlinkMacSystemFont',
'"Segoe UI"', '"Segoe UI"',
"Helvetica", 'Helvetica',
"Arial", 'Arial',
"sans-serif", 'sans-serif',
'"Apple Color Emoji"', '"Apple Color Emoji"',
'"Segoe UI Emoji"', '"Segoe UI Emoji"',
'"Segoe UI Symbol"' '"Segoe UI Symbol"',
]; ];
const defaultMonoFonts = [ const defaultMonoFonts = [
"SFMono-Regular", 'SFMono-Regular',
"Melno", 'Melno',
"Monaco", 'Monaco',
"Consolas", 'Consolas',
'"Liberation Mono"', '"Liberation Mono"',
'"Courier New"', '"Courier New"',
"monospace" 'monospace',
]; ];
const generatePalette = palette => { const generatePalette = palette => {
const generatedPalette = {}; const generatedPalette = {};
Object.keys(palette).map(color => { Object.keys(palette).map(color => {
if (!["black", "white"].includes(color)) { if (!['black', 'white'].includes(color)) {
generatedPalette[color] = generateColors(palette[color]); generatedPalette[color] = generateColors(palette[color]);
} else { } else {
generatedPalette[color] = palette[color]; generatedPalette[color] = palette[color];
@@ -120,9 +105,8 @@ const generatePalette = palette => {
}; };
const formatFont = font => { const formatFont = font => {
const fontList = font.split(" "); const fontList = font.split(' ');
const fontFmt = const fontFmt = fontList.length >= 2 ? `'${fontList.join(' ')}'` : fontList.join(' ');
fontList.length >= 2 ? `'${fontList.join(" ")}'` : fontList.join(" ");
return fontFmt; return fontFmt;
}; };
@@ -137,25 +121,25 @@ const importFonts = userFonts => {
mono.unshift(monoFmt); mono.unshift(monoFmt);
} }
return { return {
body: body.join(", "), body: body.join(', '),
heading: body.join(", "), heading: body.join(', '),
mono: mono.join(", ") mono: mono.join(', '),
}; };
}; };
const importColors = (userColors = {}) => { const importColors = (userColors = {}) => {
const generatedColors = generatePalette(userColors); const generatedColors = generatePalette(userColors);
return { return {
transparent: "transparent", transparent: 'transparent',
current: "currentColor", current: 'currentColor',
...generatedColors ...generatedColors,
}; };
}; };
export const makeTheme = userTheme => ({ export const makeTheme = userTheme => ({
...chakraTheme, ...chakraTheme,
colors: importColors(userTheme.colors), colors: importColors(userTheme.colors),
fonts: importFonts(userTheme.fonts) fonts: importFonts(userTheme.fonts),
}); });
export const isDark = color => { export const isDark = color => {
@@ -169,7 +153,7 @@ export const isLight = color => isDark(color);
export const opposingColor = (theme, color) => { export const opposingColor = (theme, color) => {
if (color.match(/^\w+\.\d+$/m)) { if (color.match(/^\w+\.\d+$/m)) {
const colorParts = color.split("."); const colorParts = color.split('.');
if (colorParts.length !== 2) { if (colorParts.length !== 2) {
throw Error(`Color is improperly formatted. Got '${color}'`); throw Error(`Color is improperly formatted. Got '${color}'`);
} }
@@ -181,14 +165,14 @@ export const opposingColor = (theme, color) => {
}; };
export const googleFontUrl = (fontFamily, weights = [300, 400, 700]) => { export const googleFontUrl = (fontFamily, weights = [300, 400, 700]) => {
const urlWeights = weights.join(","); const urlWeights = weights.join(',');
const fontName = fontFamily const fontName = fontFamily
.split(/, /)[0] .split(/, /)[0]
.trim() .trim()
.replace(/'|"/g, ""); .replace(/'|"/g, '');
const urlFont = fontName.split(/ /).join("+"); const urlFont = fontName.split(/ /).join('+');
const urlBase = `https://fonts.googleapis.com/css?family=${urlFont}:${urlWeights}&display=swap`; const urlBase = `https://fonts.googleapis.com/css?family=${urlFont}:${urlWeights}&display=swap`;
return urlBase; return urlBase;
}; };
export { theme as defaultTheme } from "@chakra-ui/core"; export { theme as defaultTheme } from '@chakra-ui/core';

View File

@@ -1432,6 +1432,18 @@
semver "^7.3.2" semver "^7.3.2"
tsutils "^3.17.1" tsutils "^3.17.1"
"@upstatement/eslint-config@^0.4.3":
version "0.4.3"
resolved "https://registry.yarnpkg.com/@upstatement/eslint-config/-/eslint-config-0.4.3.tgz#9cfbe22d15c0fee2f7154e9f61455c4e53718027"
integrity sha512-I5wYURRsCUpYyTkyq/AOF+aNglJAde2Kzlq297pQ2r61AatUgn84Jw4CsYR6rgQNmTinpLtb8uwi12mOIaM8zg==
"@upstatement/prettier-config@^0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@upstatement/prettier-config/-/prettier-config-0.3.0.tgz#cf34332dfba90c7586cd0bde134b7fc771163323"
integrity sha512-/DCKpd5tsgZ7Mshb7MSLdFkPRXHxZhzEKJP9ZW58ix0ARw1nbrcdbyiovHEutiQqmh4OjdLJjIAJ2MVUiEaB+Q==
dependencies:
prettier "^1.x.x"
"@use-it/event-listener@^0.1.2": "@use-it/event-listener@^0.1.2":
version "0.1.5" version "0.1.5"
resolved "https://registry.yarnpkg.com/@use-it/event-listener/-/event-listener-0.1.5.tgz#870456241bfef66acea6395c69b66fe516bee3cd" resolved "https://registry.yarnpkg.com/@use-it/event-listener/-/event-listener-0.1.5.tgz#870456241bfef66acea6395c69b66fe516bee3cd"
@@ -3336,31 +3348,6 @@ escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
eslint-config-airbnb-base@^14.2.0:
version "14.2.0"
resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.0.tgz#fe89c24b3f9dc8008c9c0d0d88c28f95ed65e9c4"
integrity sha512-Snswd5oC6nJaevs3nZoLSTvGJBvzTfnBqOIArkf3cbyTyq9UD79wOk8s+RiL6bhca0p/eRO6veczhf6A/7Jy8Q==
dependencies:
confusing-browser-globals "^1.0.9"
object.assign "^4.1.0"
object.entries "^1.1.2"
eslint-config-airbnb@^18.1.0:
version "18.2.0"
resolved "https://registry.yarnpkg.com/eslint-config-airbnb/-/eslint-config-airbnb-18.2.0.tgz#8a82168713effce8fc08e10896a63f1235499dcd"
integrity sha512-Fz4JIUKkrhO0du2cg5opdyPKQXOI2MvF8KUvN2710nJMT6jaRUpRE2swrJftAjVGL7T1otLM5ieo5RqS1v9Udg==
dependencies:
eslint-config-airbnb-base "^14.2.0"
object.assign "^4.1.0"
object.entries "^1.1.2"
eslint-config-prettier@^6.10.0:
version "6.12.0"
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.12.0.tgz#9eb2bccff727db1c52104f0b49e87ea46605a0d2"
integrity sha512-9jWPlFlgNwRUYVoujvWTQ1aMO8o6648r+K7qU7K5Jmkbyqav1fuEZC0COYpGBxyiAJb65Ra9hrmFx19xRGwXWw==
dependencies:
get-stdin "^6.0.0"
eslint-config-react-app@^5.2.0: eslint-config-react-app@^5.2.0:
version "5.2.1" version "5.2.1"
resolved "https://registry.yarnpkg.com/eslint-config-react-app/-/eslint-config-react-app-5.2.1.tgz#698bf7aeee27f0cea0139eaef261c7bf7dd623df" resolved "https://registry.yarnpkg.com/eslint-config-react-app/-/eslint-config-react-app-5.2.1.tgz#698bf7aeee27f0cea0139eaef261c7bf7dd623df"
@@ -3400,13 +3387,6 @@ eslint-module-utils@^2.6.0:
debug "^2.6.9" debug "^2.6.9"
pkg-dir "^2.0.0" pkg-dir "^2.0.0"
eslint-plugin-flowtype@^4.6.0:
version "4.7.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-4.7.0.tgz#903a6ea3eb5cbf4c7ba7fa73cc43fc39ab7e4a70"
integrity sha512-M+hxhSCk5QBEValO5/UqrS4UunT+MgplIJK5wA1sCtXjzBcZkpTGRwxmLHhGpbHcrmQecgt6ZL/KDdXWqGB7VA==
dependencies:
lodash "^4.17.15"
eslint-plugin-import@^2.20.1: eslint-plugin-import@^2.20.1:
version "2.22.1" version "2.22.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz#0896c7e6a0cf44109a2d97b95903c2bb689d7702" resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz#0896c7e6a0cf44109a2d97b95903c2bb689d7702"
@@ -3443,13 +3423,6 @@ eslint-plugin-jsx-a11y@^6.2.3:
jsx-ast-utils "^2.4.1" jsx-ast-utils "^2.4.1"
language-tags "^1.0.5" language-tags "^1.0.5"
eslint-plugin-prettier@^3.1.2:
version "3.1.4"
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.4.tgz#168ab43154e2ea57db992a2cd097c828171f75c2"
integrity sha512-jZDa8z76klRqo+TdGDTFJSavwbnWK2ZpqGKNZ+VvweMW516pDUMmQ2koXvxEE4JhzNvTv+radye/bWGBmA6jmg==
dependencies:
prettier-linter-helpers "^1.0.0"
eslint-plugin-react-hooks@^2.3.0: eslint-plugin-react-hooks@^2.3.0:
version "2.5.1" version "2.5.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-2.5.1.tgz#4ef5930592588ce171abeb26f400c7fbcbc23cd0" resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-2.5.1.tgz#4ef5930592588ce171abeb26f400c7fbcbc23cd0"
@@ -3749,11 +3722,6 @@ fast-deep-equal@^3.1.1:
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
fast-diff@^1.1.2:
version "1.2.0"
resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03"
integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==
fast-json-stable-stringify@^2.0.0: fast-json-stable-stringify@^2.0.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
@@ -4024,11 +3992,6 @@ gauge@~1.2.5:
lodash.padend "^4.1.0" lodash.padend "^4.1.0"
lodash.padstart "^4.1.0" lodash.padstart "^4.1.0"
get-stdin@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b"
integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==
get-value@^2.0.3, get-value@^2.0.6: get-value@^2.0.3, get-value@^2.0.6:
version "2.0.6" version "2.0.6"
resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28"
@@ -5935,14 +5898,7 @@ prelude-ls@~1.1.2:
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=
prettier-linter-helpers@^1.0.0: prettier@^1.19.1, prettier@^1.x.x:
version "1.0.0"
resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b"
integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==
dependencies:
fast-diff "^1.1.2"
prettier@^1.19.1:
version "1.19.1" version "1.19.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb"
integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==