From d263fb92036a0cc271c44415b1b9765bc4431ee7 Mon Sep 17 00:00:00 2001 From: checktheroads Date: Tue, 22 Dec 2020 02:00:36 -0700 Subject: [PATCH] continue typescript & chakra v1 migrations [skip ci] --- hyperglass/ui/components/buttons/copy.tsx | 2 +- hyperglass/ui/components/buttons/index.ts | 1 + hyperglass/ui/components/buttons/path.tsx | 17 ++ hyperglass/ui/components/buttons/types.ts | 4 + hyperglass/ui/components/index.ts | 1 + hyperglass/ui/components/path/chart.tsx | 60 ++++++ hyperglass/ui/components/path/index.ts | 1 + hyperglass/ui/components/path/path.tsx | 41 ++++ hyperglass/ui/components/path/types.ts | 25 +++ hyperglass/ui/components/path/util.ts | 68 +++++++ .../ui/components/results/individual.tsx | 26 ++- hyperglass/ui/context/HyperglassProvider.tsx | 2 +- hyperglass/ui/hooks/index.ts | 3 +- hyperglass/ui/hooks/useASNDetail.ts | 11 ++ hyperglass/ui/hooks/useLGState.ts | 21 +- hyperglass/ui/package.json | 2 + hyperglass/ui/types/bgpview.ts | 26 +++ hyperglass/ui/types/guards.ts | 4 +- hyperglass/ui/types/index.ts | 2 + hyperglass/ui/types/util.ts | 4 + hyperglass/ui/util/common.ts | 61 ++++++ hyperglass/ui/yarn.lock | 182 +++++++++++++++++- 22 files changed, 547 insertions(+), 17 deletions(-) create mode 100644 hyperglass/ui/components/buttons/path.tsx create mode 100644 hyperglass/ui/components/path/chart.tsx create mode 100644 hyperglass/ui/components/path/index.ts create mode 100644 hyperglass/ui/components/path/path.tsx create mode 100644 hyperglass/ui/components/path/types.ts create mode 100644 hyperglass/ui/components/path/util.ts create mode 100644 hyperglass/ui/hooks/useASNDetail.ts create mode 100644 hyperglass/ui/types/bgpview.ts create mode 100644 hyperglass/ui/types/util.ts diff --git a/hyperglass/ui/components/buttons/copy.tsx b/hyperglass/ui/components/buttons/copy.tsx index 6afb4e2..4f84ee0 100644 --- a/hyperglass/ui/components/buttons/copy.tsx +++ b/hyperglass/ui/components/buttons/copy.tsx @@ -17,7 +17,7 @@ export const CopyButton = (props: TCopyButton) => { size="sm" variant="ghost" onClick={onCopy} - colorScheme="primary" + colorScheme="secondary" {...rest}> diff --git a/hyperglass/ui/components/buttons/index.ts b/hyperglass/ui/components/buttons/index.ts index 134ee4b..460b183 100644 --- a/hyperglass/ui/components/buttons/index.ts +++ b/hyperglass/ui/components/buttons/index.ts @@ -1,5 +1,6 @@ export * from './colorMode'; export * from './copy'; +export * from './path'; export * from './requery'; export * from './reset'; export * from './submit'; diff --git a/hyperglass/ui/components/buttons/path.tsx b/hyperglass/ui/components/buttons/path.tsx new file mode 100644 index 0000000..7c5762c --- /dev/null +++ b/hyperglass/ui/components/buttons/path.tsx @@ -0,0 +1,17 @@ +import dynamic from 'next/dynamic'; +import { Button, Icon, Tooltip } from '@chakra-ui/react'; + +import type { TPathButton } from './types'; + +const PathIcon = dynamic(() => import('@meronex/icons/gr').then(i => i.GrNetwork)); + +export const PathButton = (props: TPathButton) => { + const { onOpen } = props; + return ( + + + + ); +}; diff --git a/hyperglass/ui/components/buttons/types.ts b/hyperglass/ui/components/buttons/types.ts index 91e930b..50b12d9 100644 --- a/hyperglass/ui/components/buttons/types.ts +++ b/hyperglass/ui/components/buttons/types.ts @@ -23,3 +23,7 @@ export interface TRSubmitButton { onChange(e: OnChangeArgs): void; children: React.ReactNode; } + +export interface TPathButton { + onOpen(): void; +} diff --git a/hyperglass/ui/components/index.ts b/hyperglass/ui/components/index.ts index 9f682a1..2221434 100644 --- a/hyperglass/ui/components/index.ts +++ b/hyperglass/ui/components/index.ts @@ -15,6 +15,7 @@ export * from './lookingGlass'; export * from './markdown'; export * from './meta'; export * from './output'; +export * from './path'; export * from './results'; export * from './select'; export * from './table'; diff --git a/hyperglass/ui/components/path/chart.tsx b/hyperglass/ui/components/path/chart.tsx new file mode 100644 index 0000000..85995cb --- /dev/null +++ b/hyperglass/ui/components/path/chart.tsx @@ -0,0 +1,60 @@ +import { useMemo } from 'react'; +import { Box, Flex, Icon, Badge, VStack } from '@chakra-ui/react'; +import { BeatLoader } from 'react-spinners'; +import ReactFlow from 'react-flow-renderer'; +import { Background, Controls } from 'react-flow-renderer'; +import { Handle, Position } from 'react-flow-renderer'; +import { useConfig, useColorValue, useColorToken } from '~/context'; +import { useASNDetail } from '~/hooks'; +import { buildElements } from './util'; + +import type { TChart, TNode, TNodeData } from './types'; + +export const Chart = (props: TChart) => { + const { data } = props; + const { primary_asn, org_name } = useConfig(); + // const elements = useMemo(() => [...buildElements({ asn: primary_asn, name: org_name }, data)], [ + // data, + // ]); + + const elements = [...buildElements({ asn: primary_asn, name: org_name }, data)]; + const dots = useColorToken('blackAlpha.500', 'whiteAlpha.400'); + return ( + + + + + + + ); +}; + +const TestNode = (props: TNode) => { + const { data } = props; + const { asn, name, hasChildren, hasParents } = data; + const color = useColorValue('black', 'white'); + const bg = useColorValue('white', 'blackAlpha.800'); + const { data: asnData, isError, isLoading } = useASNDetail(asn); + return ( + <> + {hasChildren && } + + + + {isLoading ? ( + + ) : !isError && asnData?.data?.description_short ? ( + asnData.data.description_short + ) : ( + name + )} + + + {asn} + + + + {hasParents && } + + ); +}; diff --git a/hyperglass/ui/components/path/index.ts b/hyperglass/ui/components/path/index.ts new file mode 100644 index 0000000..44bb0aa --- /dev/null +++ b/hyperglass/ui/components/path/index.ts @@ -0,0 +1 @@ +export * from './path'; diff --git a/hyperglass/ui/components/path/path.tsx b/hyperglass/ui/components/path/path.tsx new file mode 100644 index 0000000..3e5b6f9 --- /dev/null +++ b/hyperglass/ui/components/path/path.tsx @@ -0,0 +1,41 @@ +import { + Modal, + ModalBody, + ModalHeader, + ModalOverlay, + ModalContent, + useDisclosure, + Skeleton, + ModalCloseButton, +} from '@chakra-ui/react'; +import { PathButton } from '~/components'; +import { useColorValue } from '~/context'; +import { useLGState } from '~/hooks'; +import { Chart } from './chart'; + +import type { TPath } from './types'; + +export const Path = (props: TPath) => { + const { device } = props; + const { getResponse } = useLGState(); + const { isOpen, onClose, onOpen } = useDisclosure(); + const { displayTarget } = useLGState(); + const response = getResponse(device); + const output = response?.output as TStructuredResponse; + const bg = useColorValue('white', 'blackFaded.800'); + return ( + <> + + + + + {`Path to ${displayTarget.value}`} + + + {response !== null ? : } + + + + + ); +}; diff --git a/hyperglass/ui/components/path/types.ts b/hyperglass/ui/components/path/types.ts new file mode 100644 index 0000000..975614c --- /dev/null +++ b/hyperglass/ui/components/path/types.ts @@ -0,0 +1,25 @@ +import type { NodeProps } from 'react-flow-renderer'; + +export interface TChart { + data: TStructuredResponse; +} + +export interface TPath { + device: string; +} + +export interface TNode extends Omit { + data: D; +} + +export interface TNodeData { + asn: string; + name: string; + hasChildren: boolean; + hasParents?: boolean; +} + +export interface BasePath { + asn: string; + name: string; +} diff --git a/hyperglass/ui/components/path/util.ts b/hyperglass/ui/components/path/util.ts new file mode 100644 index 0000000..34155f8 --- /dev/null +++ b/hyperglass/ui/components/path/util.ts @@ -0,0 +1,68 @@ +import { arrangeIntoTree } from '~/util'; + +import type { FlowElement, Elements } from 'react-flow-renderer'; +import type { PathPart } from '~/types'; +import type { BasePath } from './types'; + +function treeToElement(part: PathPart, len: number, index: number): FlowElement[] { + const x = index * 250; + const y = -(len * 10); + let elements = [ + { + id: String(part.base), + type: 'TestNode', + position: { x, y }, + data: { + asn: part.base, + name: `AS${part.base}`, + hasChildren: part.children.length !== 0, + hasParents: true, + }, + }, + ] as Elements; + + for (const child of part.children) { + let xc = index; + if (part.children.length !== 0) { + elements.push({ + id: `e${part.base}-${child.base}`, + source: String(part.base), + target: String(child.base), + }); + } else { + xc = x; + } + elements.push(...treeToElement(child, part.children.length * 12 + len, xc)); + } + return elements; +} + +export function* buildElements(base: BasePath, data: TStructuredResponse): Generator { + const { routes } = data; + // Eliminate empty AS paths & deduplicate non-empty AS paths. Length should be same as count minus empty paths. + const asPaths = routes.filter(r => r.as_path.length !== 0).map(r => [...new Set(r.as_path)]); + const asTree = arrangeIntoTree(asPaths); + const numHops = asPaths.flat().length; + const childPaths = asTree.map((a, i) => { + return treeToElement(a, asTree.length, i); + }); + + // Add the first hop at the base. + yield { + id: base.asn, + type: 'TestNode', + position: { x: 150, y: numHops * 10 }, + data: { asn: base.asn, name: base.name, hasChildren: true, hasParents: false }, + }; + + for (const [i, path] of childPaths.entries()) { + // path = Each unique path from origin + const first = path[0]; + yield { id: `e${base.asn}-${first.id}`, source: base.asn, target: first.id }; + // Add link from base to each first hop. + yield { id: `e${base.asn}-${first.id}`, source: base.asn, target: first.id }; + for (const hop of path) { + yield hop; + } + } +} diff --git a/hyperglass/ui/components/results/individual.tsx b/hyperglass/ui/components/results/individual.tsx index 326c092..cdadab6 100644 --- a/hyperglass/ui/components/results/individual.tsx +++ b/hyperglass/ui/components/results/individual.tsx @@ -12,9 +12,9 @@ import { import { motion } from 'framer-motion'; import { BsLightningFill } from '@meronex/icons/bs'; import { startCase } from 'lodash'; -import { BGPTable, Countdown, CopyButton, RequeryButton, TextOutput, If } from '~/components'; +import { BGPTable, Countdown, CopyButton, RequeryButton, TextOutput, If, Path } from '~/components'; import { useColorValue, useConfig, useMobile } from '~/context'; -import { useStrf, useLGQuery, useTableToString } from '~/hooks'; +import { useStrf, useLGQuery, useLGState, useTableToString } from '~/hooks'; import { isStructuredOutput, isStringOutput } from '~/types'; import { FormattedError } from './error'; import { ResultHeader } from './header'; @@ -49,12 +49,15 @@ export const Result = forwardRef((props, ref) => { } = props; const { web, cache, messages } = useConfig(); + const isMobile = useMobile(); const color = useColorValue('black', 'white'); const scrollbar = useColorValue('blackAlpha.300', 'whiteAlpha.300'); const scrollbarHover = useColorValue('blackAlpha.400', 'whiteAlpha.400'); const scrollbarBg = useColorValue('blackAlpha.50', 'whiteAlpha.50'); + const { responses } = useLGState(); + const { data, error, isError, isLoading, refetch } = useLGQuery({ queryLocation, queryTarget, @@ -62,6 +65,10 @@ export const Result = forwardRef((props, ref) => { queryVrf, }); + if (typeof data !== 'undefined') { + responses.merge({ [device.name]: data }); + } + const cacheLabel = useStrf(web.text.cache_icon, { time: data?.timestamp }, [data?.timestamp]); const [isOpen, setOpen] = useState(false); @@ -175,6 +182,9 @@ export const Result = forwardRef((props, ref) => { /> + {isStructuredOutput(data) && data.level === 'success' && tableComponent && ( + + )} @@ -199,7 +209,7 @@ export const Result = forwardRef((props, ref) => { }}> - {!isError && typeof data !== 'undefined' && ( + {!isError && typeof data !== 'undefined' ? ( <> {isStructuredOutput(data) && data.level === 'success' && tableComponent ? ( {data.output} @@ -209,8 +219,16 @@ export const Result = forwardRef((props, ref) => { - ) : null} + ) : ( + + + + )} + ) : ( + + + )} diff --git a/hyperglass/ui/context/HyperglassProvider.tsx b/hyperglass/ui/context/HyperglassProvider.tsx index f9ee4bf..6efbe13 100644 --- a/hyperglass/ui/context/HyperglassProvider.tsx +++ b/hyperglass/ui/context/HyperglassProvider.tsx @@ -31,7 +31,7 @@ export const useTheme = (): ITheme => useChakraTheme(); export const useMobile = (): boolean => useBreakpointValue({ base: true, md: true, lg: false, xl: false }) ?? true; -export const useColorToken = (light: string, dark: string): ValueOf => +export const useColorToken = (light: string, dark: string): string => useColorModeValue(useToken('colors', light), useToken('colors', dark)); export { diff --git a/hyperglass/ui/hooks/index.ts b/hyperglass/ui/hooks/index.ts index 40439e2..67337ed 100644 --- a/hyperglass/ui/hooks/index.ts +++ b/hyperglass/ui/hooks/index.ts @@ -1,9 +1,10 @@ +export * from './useASNDetail'; export * from './useBooleanValue'; export * from './useDevice'; export * from './useGreeting'; export * from './useLGQuery'; +export * from './useLGState'; export * from './useOpposingColor'; export * from './useSessionStorage'; export * from './useStrf'; export * from './useTableToString'; -export * from './useLGState'; diff --git a/hyperglass/ui/hooks/useASNDetail.ts b/hyperglass/ui/hooks/useASNDetail.ts new file mode 100644 index 0000000..2d1dd2a --- /dev/null +++ b/hyperglass/ui/hooks/useASNDetail.ts @@ -0,0 +1,11 @@ +import { useQuery } from 'react-query'; +import type { TASNDetails } from '~/types'; + +async function query(asn: string): Promise { + const res = await fetch(`https://api.bgpview.io/asn/${asn}`, { mode: 'cors' }); + return await res.json(); +} + +export function useASNDetail(asn: string) { + return useQuery(asn, query); +} diff --git a/hyperglass/ui/hooks/useLGState.ts b/hyperglass/ui/hooks/useLGState.ts index 2d65414..018f456 100644 --- a/hyperglass/ui/hooks/useLGState.ts +++ b/hyperglass/ui/hooks/useLGState.ts @@ -4,29 +4,32 @@ import type { State } from '@hookstate/core'; import type { Families, TDeviceVrf, TQueryTypes, TFormData } from '~/types'; type TLGState = { - isSubmitting: boolean; queryVrf: string; families: Families; queryTarget: string; btnLoading: boolean; + formData: TFormData; + isSubmitting: boolean; displayTarget: string; queryType: TQueryTypes; queryLocation: string[]; availVrfs: TDeviceVrf[]; resolvedIsOpen: boolean; fqdnTarget: string | null; - formData: TFormData; + responses: { [d: string]: TQueryResponse }; }; type TLGStateHandlers = { resolvedOpen(): void; resolvedClose(): void; resetForm(): void; + getResponse(d: string): TQueryResponse | null; }; const LGState = createState({ - isSubmitting: false, + formData: { query_location: [], query_target: '', query_type: '', query_vrf: '' }, resolvedIsOpen: false, + isSubmitting: false, displayTarget: '', queryLocation: [], btnLoading: false, @@ -34,9 +37,9 @@ const LGState = createState({ queryTarget: '', queryType: '', availVrfs: [], + responses: {}, queryVrf: '', families: [], - formData: { query_location: [], query_target: '', query_type: '', query_vrf: '' }, }); export function useLGState(): State & TLGStateHandlers { @@ -59,8 +62,16 @@ export function useLGState(): State & TLGStateHandlers { resolvedIsOpen: false, btnLoading: false, formData: { query_location: [], query_target: '', query_type: '', query_vrf: '' }, + responses: {}, }); } + function getResponse(device: string): TQueryResponse | null { + if (device in state.responses) { + return state.responses[device].value; + } else { + return null; + } + } - return { resetForm, resolvedOpen, resolvedClose, ...state }; + return { resetForm, resolvedOpen, resolvedClose, getResponse, ...state }; } diff --git a/hyperglass/ui/package.json b/hyperglass/ui/package.json index 623e42f..fe7e1a5 100644 --- a/hyperglass/ui/package.json +++ b/hyperglass/ui/package.json @@ -35,10 +35,12 @@ "react-countdown": "^2.2.1", "react-dom": "^17.0.1", "react-fast-compare": "^3.2.0", + "react-flow-renderer": "^8.2.3", "react-hook-form": "^6.13.1", "react-markdown": "^4.3.1", "react-query": "^2.26.4", "react-select": "^3.1.1", + "react-spinners": "^0.9.0", "react-string-replace": "^0.4.4", "react-table": "^7.6.2", "string-format": "^2.0.0", diff --git a/hyperglass/ui/types/bgpview.ts b/hyperglass/ui/types/bgpview.ts new file mode 100644 index 0000000..effc2f3 --- /dev/null +++ b/hyperglass/ui/types/bgpview.ts @@ -0,0 +1,26 @@ +interface TASNRIRAllocation { + rir_name: string | null; + country_code: string | null; + date_allocated: string | null; +} +interface TASNData { + asn: number; + name: string | null; + description_short: string | null; + description_full: string[]; + country_code: string; + website: string | null; + email_contacts: string[]; + abuse_contacts: string[]; + looking_glass: string | null; + traffic_estimation: string | null; + traffic_ratio: string | null; + owner_address: string[]; + rir_allocation: TASNRIRAllocation; + date_updated: string | null; +} +export interface TASNDetails { + status: string; + status_message: string; + data: TASNData; +} diff --git a/hyperglass/ui/types/guards.ts b/hyperglass/ui/types/guards.ts index 59055ea..d02092c 100644 --- a/hyperglass/ui/types/guards.ts +++ b/hyperglass/ui/types/guards.ts @@ -16,9 +16,9 @@ export function isString(a: any): a is string { } export function isStructuredOutput(data: any): data is TStringTableData { - return typeof data.output !== 'string'; + return typeof data !== 'undefined' && 'output' in data && typeof data.output !== 'string'; } export function isStringOutput(data: any): data is TQueryResponseString { - return typeof data.output === 'string'; + return typeof data !== 'undefined' && 'output' in data && typeof data.output === 'string'; } diff --git a/hyperglass/ui/types/index.ts b/hyperglass/ui/types/index.ts index 50b030d..deb4caf 100644 --- a/hyperglass/ui/types/index.ts +++ b/hyperglass/ui/types/index.ts @@ -1,3 +1,4 @@ +export * from './bgpview'; export * from './common'; export * from './config'; export * from './data'; @@ -5,3 +6,4 @@ export * from './dns-over-https'; export * from './guards'; export * from './table'; export * from './theme'; +export * from './util'; diff --git a/hyperglass/ui/types/util.ts b/hyperglass/ui/types/util.ts new file mode 100644 index 0000000..0d909cc --- /dev/null +++ b/hyperglass/ui/types/util.ts @@ -0,0 +1,4 @@ +export interface PathPart { + base: number; + children: PathPart[]; +} diff --git a/hyperglass/ui/util/common.ts b/hyperglass/ui/util/common.ts index afbf345..2c210ea 100644 --- a/hyperglass/ui/util/common.ts +++ b/hyperglass/ui/util/common.ts @@ -12,3 +12,64 @@ export function flatten(arr: any[][]): T[] { return flat.concat(Array.isArray(toFlatten) ? flatten(toFlatten) : toFlatten); }, []); } + +export function chunkArray(array: A[], size: number): A[][] { + let result = [] as A[][]; + for (let i = 0; i < array.length; i += size) { + let chunk = array.slice(i, i + size); + result.push(chunk); + } + return result; +} + +interface PathPart { + base: number; + children: PathPart[]; +} + +/** + * Arrange an array of arrays into a tree of nodes. + * + * Blatantly stolen from: + * @see https://gist.github.com/stephanbogner/4b590f992ead470658a5ebf09167b03d + */ +export function arrangeIntoTree

(paths: P[][]): PathPart[] { + let tree = [] as PathPart[]; + + for (let i = 0; i < paths.length; i++) { + let path = paths[i]; + let currentLevel = tree; + + for (let j = 0; j < path.length; j++) { + let part = path[j]; + + const existingPath = findWhere(currentLevel, 'base', part); + + if (existingPath !== false) { + currentLevel = existingPath.children; + } else { + const newPart = { + base: part, + children: [], + } as PathPart; + + currentLevel.push(newPart); + currentLevel = newPart.children; + } + } + } + return tree; + + function findWhere(array: any[], idx: string, value: V): PathPart | false { + let t = 0; + while (t < array.length && array[t][idx] !== value) { + t++; + } + + if (t < array.length) { + return array[t]; + } else { + return false; + } + } +} diff --git a/hyperglass/ui/yarn.lock b/hyperglass/ui/yarn.lock index 24b202e..43598b0 100644 --- a/hyperglass/ui/yarn.lock +++ b/hyperglass/ui/yarn.lock @@ -160,7 +160,7 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/runtime@7.12.5", "@babel/runtime@^7.0.0", "@babel/runtime@^7.10.5": +"@babel/runtime@7.12.5", "@babel/runtime@^7.0.0", "@babel/runtime@^7.10.5", "@babel/runtime@^7.12.5": version "7.12.5" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e" integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg== @@ -760,6 +760,18 @@ "@emotion/weak-memoize" "^0.2.5" stylis "^4.0.3" +"@emotion/core@^10.0.15": + version "10.1.1" + resolved "https://registry.yarnpkg.com/@emotion/core/-/core-10.1.1.tgz#c956c1365f2f2481960064bcb8c4732e5fb612c3" + integrity sha512-ZMLG6qpXR8x031NXD8HJqugy/AZSkAuMxxqB46pmAR7ze47MhNJ56cdoX243QPZdGctrdfo+s08yZTiwaUcRKA== + dependencies: + "@babel/runtime" "^7.5.5" + "@emotion/cache" "^10.0.27" + "@emotion/css" "^10.0.27" + "@emotion/serialize" "^0.11.15" + "@emotion/sheet" "0.9.4" + "@emotion/utils" "0.11.3" + "@emotion/core@^10.0.9": version "10.0.35" resolved "https://registry.yarnpkg.com/@emotion/core/-/core-10.0.35.tgz#513fcf2e22cd4dfe9d3894ed138c9d7a859af9b3" @@ -2096,7 +2108,12 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -classnames@2.2.6: +classcat@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/classcat/-/classcat-4.1.0.tgz#e8fd8623e5625187b58adf49bb669a13b6c520f4" + integrity sha512-RA8O5oCi1I1CF6rR4cRBROh8MtZzM4w7xKLm0jd+S6UN2G4FIto+9DVOeFc46JEZFN5PVe/EZWLQO1VU/AUH4A== + +classnames@2.2.6, classnames@^2.2.5: version "2.2.6" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce" integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q== @@ -2508,6 +2525,68 @@ cyclist@^1.0.1: resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= +"d3-color@1 - 2": + version "2.0.0" + resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-2.0.0.tgz#8d625cab42ed9b8f601a1760a389f7ea9189d62e" + integrity sha512-SPXi0TSKPD4g9tw0NMZFnR95XVgUZiBH+uUTqQuDu1OsE2zomHU7ho0FISciaPvosimixwHFl3WHLGabv6dDgQ== + +"d3-dispatch@1 - 2": + version "2.0.0" + resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-2.0.0.tgz#8a18e16f76dd3fcaef42163c97b926aa9b55e7cf" + integrity sha512-S/m2VsXI7gAti2pBoLClFFTMOO1HTtT0j99AuXLoGFKO6deHDdnv6ZGTxSTTUTgO1zVcv82fCOtDjYK4EECmWA== + +d3-drag@2: + version "2.0.0" + resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-2.0.0.tgz#9eaf046ce9ed1c25c88661911c1d5a4d8eb7ea6d" + integrity sha512-g9y9WbMnF5uqB9qKqwIIa/921RYWzlUDv9Jl1/yONQwxbOfszAWTCm8u7HOTgJgRDXiRZN56cHT9pd24dmXs8w== + dependencies: + d3-dispatch "1 - 2" + d3-selection "2" + +"d3-ease@1 - 2": + version "2.0.0" + resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-2.0.0.tgz#fd1762bfca00dae4bacea504b1d628ff290ac563" + integrity sha512-68/n9JWarxXkOWMshcT5IcjbB+agblQUaIsbnXmrzejn2O82n3p2A9R2zEB9HIEFWKFwPAEDDN8gR0VdSAyyAQ== + +"d3-interpolate@1 - 2": + version "2.0.1" + resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-2.0.1.tgz#98be499cfb8a3b94d4ff616900501a64abc91163" + integrity sha512-c5UhwwTs/yybcmTpAVqwSFl6vrQ8JZJoT5F7xNFK9pymv5C0Ymcc9/LIJHtYIggg/yS9YHw8i8O8tgb9pupjeQ== + dependencies: + d3-color "1 - 2" + +d3-selection@2, d3-selection@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-2.0.0.tgz#94a11638ea2141b7565f883780dabc7ef6a61066" + integrity sha512-XoGGqhLUN/W14NmaqcO/bb1nqjDAw5WtSYb2X8wiuQWvSZUsUVYsOSkOybUrNvcBjaywBdYPy03eXHMXjk9nZA== + +"d3-timer@1 - 2": + version "2.0.0" + resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-2.0.0.tgz#055edb1d170cfe31ab2da8968deee940b56623e6" + integrity sha512-TO4VLh0/420Y/9dO3+f9abDEFYeCUr2WZRlxJvbp4HPTQcSylXNiL6yZa9FIUvV1yRiFufl1bszTCLDqv9PWNA== + +d3-transition@2: + version "2.0.0" + resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-2.0.0.tgz#366ef70c22ef88d1e34105f507516991a291c94c" + integrity sha512-42ltAGgJesfQE3u9LuuBHNbGrI/AJjNL2OAUdclE70UE6Vy239GCBEYD38uBPoLeNsOhFStGpPI0BAOV+HMxog== + dependencies: + d3-color "1 - 2" + d3-dispatch "1 - 2" + d3-ease "1 - 2" + d3-interpolate "1 - 2" + d3-timer "1 - 2" + +d3-zoom@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/d3-zoom/-/d3-zoom-2.0.0.tgz#f04d0afd05518becce879d04709c47ecd93fba54" + integrity sha512-fFg7aoaEm9/jf+qfstak0IYpnesZLiMX6GZvXtUSdv8RH2o4E2qeelgdU09eKS6wGuiGMfcnMI0nTIqWzRHGpw== + dependencies: + d3-dispatch "1 - 2" + d3-drag "2" + d3-interpolate "1 - 2" + d3-selection "2" + d3-transition "2" + d@1, d@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" @@ -2759,6 +2838,20 @@ duplexify@^3.4.2, duplexify@^3.6.0: readable-stream "^2.0.0" stream-shift "^1.0.0" +easy-peasy@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/easy-peasy/-/easy-peasy-4.0.1.tgz#8b3ab1ebb43509a62dc2c37b4269a9141e33d918" + integrity sha512-aTvB48M2ej6dM/wllUm1F7CTWGnYOYh82SHBkvJtOZhJ/9L8Gmg/nIVqDPwJeojOWZe+gbLtpyi8DhN6fPNBYg== + dependencies: + immer "7.0.9" + is-plain-object "^5.0.0" + memoizerific "^1.11.3" + redux "^4.0.5" + redux-thunk "^2.3.0" + symbol-observable "^2.0.3" + ts-toolbelt "^8.0.7" + use-memo-one "^1.1.1" + ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -3247,7 +3340,7 @@ extglob@^2.0.4: snapdragon "^0.8.1" to-regex "^3.0.1" -fast-deep-equal@^3.1.1: +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== @@ -3783,6 +3876,11 @@ ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== +immer@7.0.9: + version "7.0.9" + resolved "https://registry.yarnpkg.com/immer/-/immer-7.0.9.tgz#28e7552c21d39dd76feccd2b800b7bc86ee4a62e" + integrity sha512-Vs/gxoM4DqNAYR7pugIxi0Xc8XAun/uy7AQu4fLLqaTBHxjOP9pJ266Q9MWA/ly4z6rAFZbvViOtihxUZ7O28A== + import-fresh@^3.0.0, import-fresh@^3.1.0: version "3.2.1" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66" @@ -4059,6 +4157,11 @@ is-plain-object@^2.0.3, is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" +is-plain-object@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" + integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== + is-regex@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9" @@ -4376,6 +4479,11 @@ map-cache@^0.2.2: resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= +map-or-similar@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/map-or-similar/-/map-or-similar-1.5.0.tgz#6de2653174adfb5d9edc33c69d3e92a1b76faf08" + integrity sha1-beJlMXSt+12e3DPGnT6Sobdvrwg= + map-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" @@ -4414,6 +4522,13 @@ memoize-one@^5.0.0: resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.1.1.tgz#047b6e3199b508eaec03504de71229b8eb1d75c0" integrity sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA== +memoizerific@^1.11.3: + version "1.11.3" + resolved "https://registry.yarnpkg.com/memoizerific/-/memoizerific-1.11.3.tgz#7c87a4646444c32d75438570905f2dbd1b1a805a" + integrity sha1-fIekZGREwy11Q4VwkF8tvRsagFo= + dependencies: + map-or-similar "^1.5.0" + memory-fs@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" @@ -5475,11 +5590,32 @@ react-dom@^17.0.1: object-assign "^4.1.1" scheduler "^0.20.1" +react-draggable@^4.4.3: + version "4.4.3" + resolved "https://registry.yarnpkg.com/react-draggable/-/react-draggable-4.4.3.tgz#0727f2cae5813e36b0e4962bf11b2f9ef2b406f3" + integrity sha512-jV4TE59MBuWm7gb6Ns3Q1mxX8Azffb7oTtDtBgFkxRvhDp38YAARmRplrj0+XGkhOJB5XziArX+4HUUABtyZ0w== + dependencies: + classnames "^2.2.5" + prop-types "^15.6.0" + react-fast-compare@3.2.0, react-fast-compare@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb" integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA== +react-flow-renderer@^8.2.3: + version "8.2.3" + resolved "https://registry.yarnpkg.com/react-flow-renderer/-/react-flow-renderer-8.2.3.tgz#00df643d66c4414037eb5ebbdeb7aaa819860649" + integrity sha512-Kez92PpeU1xmXNMa1pFiWYvLaLaFSmBX+9CUTSmXMO3eqpTD48nMJsax4lu/qv/muxJyBjuftSXb9hQM7iHJVQ== + dependencies: + "@babel/runtime" "^7.12.5" + classcat "^4.1.0" + d3-selection "^2.0.0" + d3-zoom "^2.0.0" + easy-peasy "^4.0.1" + fast-deep-equal "^3.1.3" + react-draggable "^4.4.3" + react-focus-lock@2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/react-focus-lock/-/react-focus-lock-2.4.1.tgz#e842cc93da736b5c5d331799012544295cbcee4f" @@ -5568,6 +5704,13 @@ react-select@^3.1.1: react-input-autosize "^2.2.2" react-transition-group "^4.3.0" +react-spinners@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/react-spinners/-/react-spinners-0.9.0.tgz#b22c38acbfce580cd6f1b04a4649e812370b1fb8" + integrity sha512-+x6eD8tn/aYLdxZjNW7fSR1uoAXLb9qq6TFYZR1dFweJvckcf/HfP8Pa/cy5HOvB/cvI4JgrYXTjh2Me3S6Now== + dependencies: + "@emotion/core" "^10.0.15" + react-string-replace@^0.4.4: version "0.4.4" resolved "https://registry.yarnpkg.com/react-string-replace/-/react-string-replace-0.4.4.tgz#24006fbe0db573d5be583133df38b1a735cb4225" @@ -5645,6 +5788,19 @@ readdirp@~3.5.0: dependencies: picomatch "^2.2.1" +redux-thunk@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622" + integrity sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw== + +redux@^4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f" + integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w== + dependencies: + loose-envify "^1.4.0" + symbol-observable "^1.2.0" + regenerator-runtime@^0.13.4: version "0.13.7" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" @@ -6436,6 +6592,16 @@ supports-color@^7.1.0: dependencies: has-flag "^4.0.0" +symbol-observable@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" + integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== + +symbol-observable@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-2.0.3.tgz#5b521d3d07a43c351055fa43b8355b62d33fd16a" + integrity sha512-sQV7phh2WCYAn81oAkakC5qjq2Ml0g8ozqz03wOGnx9dDlG1de6yrF+0RAzSJD8fPUow3PTSMf2SAbOGxb93BA== + table@^5.2.3: version "5.4.6" resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" @@ -6651,6 +6817,11 @@ ts-pnp@^1.1.6: resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92" integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw== +ts-toolbelt@^8.0.7: + version "8.0.7" + resolved "https://registry.yarnpkg.com/ts-toolbelt/-/ts-toolbelt-8.0.7.tgz#4dad2928831a811ee17dbdab6eb1919fc0a295bf" + integrity sha512-KICHyKxc5Nu34kyoODrEe2+zvuQQaubTJz7pnC5RQ19TH/Jged1xv+h8LBrouaSD310m75oAljYs59LNHkLDkQ== + tsconfig-paths@^3.9.0: version "3.9.0" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b" @@ -6880,6 +7051,11 @@ use-callback-ref@^1.2.1, use-callback-ref@^1.2.3: resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.2.4.tgz#d86d1577bfd0b955b6e04aaf5971025f406bea3c" integrity sha512-rXpsyvOnqdScyied4Uglsp14qzag1JIemLeTWGKbwpotWht57hbP78aNT+Q4wdFKQfQibbUX4fb6Qb4y11aVOQ== +use-memo-one@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.1.tgz#39e6f08fe27e422a7d7b234b5f9056af313bd22c" + integrity sha512-oFfsyun+bP7RX8X2AskHNTxu+R3QdE/RC5IefMbqptmACAA/gfol1KDD5KRzPsGMa62sWxGZw+Ui43u6x4ddoQ== + use-sidecar@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/use-sidecar/-/use-sidecar-1.0.3.tgz#17a4e567d4830c0c0ee100040e85a7fe68611e0f"