mirror of
https://github.com/checktheroads/hyperglass
synced 2024-05-11 05:55:08 +00:00
implement dynamic icon component & migrate back to react-icons
This commit is contained in:
@@ -12,11 +12,8 @@ import {
|
||||
useDisclosure,
|
||||
ModalCloseButton,
|
||||
} from '@chakra-ui/react';
|
||||
import { HiOutlineDownload as RefreshIcon } from '@meronex/icons/hi';
|
||||
import { IosColorPalette as ThemeIcon } from '@meronex/icons/ios';
|
||||
import { MdcCodeJson as ConfigIcon } from '@meronex/icons/mdc';
|
||||
import { useConfig, useColorValue, useBreakpointValue } from '~/context';
|
||||
import { CodeBlock } from '~/components';
|
||||
import { CodeBlock, DynamicIcon } from '~/components';
|
||||
import { useHyperglassConfig } from '~/hooks';
|
||||
|
||||
import type { UseDisclosureReturn } from '@chakra-ui/react';
|
||||
@@ -75,16 +72,26 @@ export const Debugger: React.FC = () => {
|
||||
<Tag size={tagSize} colorScheme="gray">
|
||||
{colorMode.toUpperCase()}
|
||||
</Tag>
|
||||
<Button size={btnSize} leftIcon={<ConfigIcon />} colorScheme="cyan" onClick={onConfigOpen}>
|
||||
<Button
|
||||
size={btnSize}
|
||||
colorScheme="cyan"
|
||||
onClick={onConfigOpen}
|
||||
leftIcon={<DynamicIcon icon={{ bs: 'BsBraces' }} />}
|
||||
>
|
||||
View Config
|
||||
</Button>
|
||||
<Button size={btnSize} leftIcon={<ThemeIcon />} colorScheme="blue" onClick={onThemeOpen}>
|
||||
<Button
|
||||
size={btnSize}
|
||||
leftIcon={<DynamicIcon icon={{ io: 'IoIosColorPalette' }} />}
|
||||
colorScheme="blue"
|
||||
onClick={onThemeOpen}
|
||||
>
|
||||
View Theme
|
||||
</Button>
|
||||
<Button
|
||||
size={btnSize}
|
||||
colorScheme="purple"
|
||||
leftIcon={<RefreshIcon />}
|
||||
leftIcon={<DynamicIcon icon={{ hi: 'HiOutlineDownload' }} />}
|
||||
onClick={() => refetch()}
|
||||
>
|
||||
Reload Config
|
||||
|
||||
@@ -40,6 +40,7 @@ export const FooterButton: React.FC<TFooterButton> = (props: TFooterButton) => {
|
||||
as={Button}
|
||||
size={size}
|
||||
variant="ghost"
|
||||
lineHeight={0}
|
||||
aria-label={typeof title === 'string' ? title : undefined}
|
||||
>
|
||||
{title}
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import { forwardRef } from 'react';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { Button, Icon, Tooltip } from '@chakra-ui/react';
|
||||
import { If } from '~/components';
|
||||
import { Button, Tooltip } from '@chakra-ui/react';
|
||||
import { DynamicIcon, If } from '~/components';
|
||||
import { useColorMode, useColorValue, useBreakpointValue } from '~/context';
|
||||
import { useOpposingColor } from '~/hooks';
|
||||
|
||||
import type { TColorModeToggle } from './types';
|
||||
|
||||
const Sun = dynamic<MeronexIcon>(() => import('@meronex/icons/hi').then(i => i.HiSun));
|
||||
const Moon = dynamic<MeronexIcon>(() => import('@meronex/icons/hi').then(i => i.HiMoon));
|
||||
|
||||
export const ColorModeToggle = forwardRef<HTMLButtonElement, TColorModeToggle>(
|
||||
(props: TColorModeToggle, ref) => {
|
||||
const { size = '1.5rem', ...rest } = props;
|
||||
@@ -34,10 +30,10 @@ export const ColorModeToggle = forwardRef<HTMLButtonElement, TColorModeToggle>(
|
||||
{...rest}
|
||||
>
|
||||
<If c={colorMode === 'light'}>
|
||||
<Icon as={Moon} boxSize={size} />
|
||||
<DynamicIcon icon={{ hi: 'HiMoon' }} boxSize={size} />
|
||||
</If>
|
||||
<If c={colorMode === 'dark'}>
|
||||
<Icon as={Sun} boxSize={size} />
|
||||
<DynamicIcon icon={{ hi: 'HiSun' }} boxSize={size} />
|
||||
</If>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useMemo } from 'react';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { Flex, Icon, HStack, useToken } from '@chakra-ui/react';
|
||||
import { If } from '~/components';
|
||||
import { Flex, HStack, useToken } from '@chakra-ui/react';
|
||||
import { DynamicIcon, If } from '~/components';
|
||||
import { useConfig, useMobile, useColorValue, useBreakpointValue } from '~/context';
|
||||
import { useStrf } from '~/hooks';
|
||||
import { FooterButton } from './button';
|
||||
@@ -12,9 +11,6 @@ import { isLink, isMenu } from './types';
|
||||
import type { ButtonProps, LinkProps } from '@chakra-ui/react';
|
||||
import type { Link, Menu } from '~/types';
|
||||
|
||||
const CodeIcon = dynamic<MeronexIcon>(() => import('@meronex/icons/fi').then(i => i.FiCode));
|
||||
const ExtIcon = dynamic<MeronexIcon>(() => import('@meronex/icons/go').then(i => i.GoLinkExternal));
|
||||
|
||||
function buildItems(links: Link[], menus: Menu[]): [(Link | Menu)[], (Link | Menu)[]] {
|
||||
const leftLinks = links.filter(link => link.side === 'left');
|
||||
const leftMenus = menus.filter(menu => menu.side === 'left');
|
||||
@@ -61,7 +57,7 @@ export const Footer: React.FC = () => {
|
||||
const icon: Partial<ButtonProps & LinkProps> = {};
|
||||
|
||||
if (item.showIcon) {
|
||||
icon.rightIcon = <ExtIcon />;
|
||||
icon.rightIcon = <DynamicIcon icon={{ go: 'GoLinkExternal' }} />;
|
||||
}
|
||||
return <FooterLink key={item.title} href={url} title={item.title} {...icon} />;
|
||||
} else if (isMenu(item)) {
|
||||
@@ -77,7 +73,7 @@ export const Footer: React.FC = () => {
|
||||
const icon: Partial<ButtonProps & LinkProps> = {};
|
||||
|
||||
if (item.showIcon) {
|
||||
icon.rightIcon = <ExtIcon />;
|
||||
icon.rightIcon = <DynamicIcon icon={{ go: 'GoLinkExternal' }} />;
|
||||
}
|
||||
return <FooterLink key={item.title} href={url} title={item.title} {...icon} />;
|
||||
} else if (isMenu(item)) {
|
||||
@@ -91,7 +87,7 @@ export const Footer: React.FC = () => {
|
||||
key="credit"
|
||||
side="right"
|
||||
content={content.credit}
|
||||
title={<Icon as={CodeIcon} boxSize={size} />}
|
||||
title={<DynamicIcon icon={{ fi: 'FiCode' }} boxSize={size} />}
|
||||
/>
|
||||
</If>
|
||||
<ColorModeToggle size={size} />
|
||||
|
||||
@@ -1,20 +1,12 @@
|
||||
import { useMemo } from 'react';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { Button, chakra, Stack, Text, VStack } from '@chakra-ui/react';
|
||||
import { Button, Stack, Text, VStack } from '@chakra-ui/react';
|
||||
import { DynamicIcon } from '~/components';
|
||||
import { useConfig, useColorValue } from '~/context';
|
||||
import { useStrf, useDNSQuery, useFormState } from '~/hooks';
|
||||
|
||||
import type { DnsOverHttps } from '~/types';
|
||||
import type { ResolvedTargetProps } from './types';
|
||||
|
||||
const RightArrow = chakra(
|
||||
dynamic<MeronexIcon>(() => import('@meronex/icons/fa').then(i => i.FaArrowCircleRight)),
|
||||
);
|
||||
|
||||
const LeftArrow = chakra(
|
||||
dynamic<MeronexIcon>(() => import('@meronex/icons/fa').then(i => i.FaArrowCircleLeft)),
|
||||
);
|
||||
|
||||
function findAnswer(data: DnsOverHttps.Response | undefined): string {
|
||||
let answer = '';
|
||||
if (typeof data !== 'undefined') {
|
||||
@@ -68,16 +60,12 @@ export const ResolvedTarget = (props: ResolvedTargetProps): JSX.Element => {
|
||||
}
|
||||
|
||||
const hasAnswer = useMemo(
|
||||
() => (!isError4 || !isError6) && (answer4 !== '' || answer6 !== ''),
|
||||
() => (!isError4 || !isError6) && (answer4 || answer6),
|
||||
[answer4, answer6, isError4, isError6],
|
||||
);
|
||||
const showA = useMemo(
|
||||
() => !isLoading4 && !isError4 && answer4 !== '',
|
||||
[isLoading4, isError4, answer4],
|
||||
);
|
||||
|
||||
const showA = useMemo(() => !isLoading4 && !isError4 && answer4, [isLoading4, isError4, answer4]);
|
||||
const showAAAA = useMemo(
|
||||
() => !isLoading6 && !isError6 && answer6 !== '',
|
||||
() => !isLoading6 && !isError6 && answer6,
|
||||
[isLoading6, isError6, answer6],
|
||||
);
|
||||
|
||||
@@ -102,7 +90,7 @@ export const ResolvedTarget = (props: ResolvedTargetProps): JSX.Element => {
|
||||
colorScheme="primary"
|
||||
justifyContent="space-between"
|
||||
onClick={() => selectTarget(answer4)}
|
||||
rightIcon={<RightArrow boxSize="18px" />}
|
||||
rightIcon={<DynamicIcon icon={{ fa: 'FaArrowCircleRight' }} boxSize="18px" />}
|
||||
>
|
||||
{answer4}
|
||||
</Button>
|
||||
@@ -116,7 +104,7 @@ export const ResolvedTarget = (props: ResolvedTargetProps): JSX.Element => {
|
||||
colorScheme="secondary"
|
||||
justifyContent="space-between"
|
||||
onClick={() => selectTarget(answer6)}
|
||||
rightIcon={<RightArrow boxSize="18px" />}
|
||||
rightIcon={<DynamicIcon icon={{ fa: 'FaArrowCircleRight' }} boxSize="18px" />}
|
||||
>
|
||||
{answer6}
|
||||
</Button>
|
||||
@@ -135,7 +123,7 @@ export const ResolvedTarget = (props: ResolvedTargetProps): JSX.Element => {
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={errorClose}
|
||||
leftIcon={<LeftArrow />}
|
||||
leftIcon={<DynamicIcon icon={{ fa: 'FaArrowCircleLeft' }} />}
|
||||
>
|
||||
{web.text.fqdnErrorButton}
|
||||
</Button>
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
import { useMemo } from 'react';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { Button, chakra, Stack, Text, VStack, useDisclosure } from '@chakra-ui/react';
|
||||
import { Prompt } from '~/components';
|
||||
import { Button, Stack, Text, VStack, useDisclosure } from '@chakra-ui/react';
|
||||
import { DynamicIcon, Prompt } from '~/components';
|
||||
import { useConfig, useColorValue } from '~/context';
|
||||
import { useStrf, useWtf } from '~/hooks';
|
||||
|
||||
import type { UserIPProps } from './types';
|
||||
|
||||
const RightArrow = chakra(
|
||||
dynamic<MeronexIcon>(() => import('@meronex/icons/fa').then(i => i.FaArrowCircleRight)),
|
||||
);
|
||||
|
||||
export const UserIP = (props: UserIPProps): JSX.Element => {
|
||||
const { setTarget } = props;
|
||||
const { onOpen, ...disclosure } = useDisclosure();
|
||||
@@ -67,7 +62,7 @@ export const UserIP = (props: UserIPProps): JSX.Element => {
|
||||
ipv4?.data?.ip && setTarget(ipv4.data.ip);
|
||||
disclosure.onClose();
|
||||
}}
|
||||
rightIcon={<RightArrow boxSize="18px" />}
|
||||
rightIcon={<DynamicIcon icon={{ fa: 'FaArrowCircleRight' }} boxSize="18px" />}
|
||||
>
|
||||
{ipv4?.data?.ip ?? noIPv4}
|
||||
</Button>
|
||||
@@ -85,7 +80,7 @@ export const UserIP = (props: UserIPProps): JSX.Element => {
|
||||
ipv6?.data?.ip && setTarget(ipv6.data.ip);
|
||||
disclosure.onClose();
|
||||
}}
|
||||
rightIcon={<RightArrow boxSize="18px" />}
|
||||
rightIcon={<DynamicIcon icon={{ fa: 'FaArrowCircleRight' }} boxSize="18px" />}
|
||||
>
|
||||
{ipv6?.data?.ip ?? noIPv6}
|
||||
</Button>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import dynamic from 'next/dynamic';
|
||||
import {
|
||||
Modal,
|
||||
ScaleFade,
|
||||
@@ -10,14 +9,12 @@ import {
|
||||
useDisclosure,
|
||||
ModalCloseButton,
|
||||
} from '@chakra-ui/react';
|
||||
import { Markdown } from '~/components';
|
||||
import { DynamicIcon, Markdown } from '~/components';
|
||||
import { useColorValue } from '~/context';
|
||||
import { isQueryContent } from '~/types';
|
||||
|
||||
import type { THelpModal } from './types';
|
||||
|
||||
const Info = dynamic<MeronexIcon>(() => import('@meronex/icons/fi').then(i => i.FiInfo));
|
||||
|
||||
export const HelpModal: React.FC<THelpModal> = (props: THelpModal) => {
|
||||
const { visible, item, name, ...rest } = props;
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
@@ -36,7 +33,7 @@ export const HelpModal: React.FC<THelpModal> = (props: THelpModal) => {
|
||||
minW={3}
|
||||
size="md"
|
||||
variant="link"
|
||||
icon={<Info />}
|
||||
icon={<DynamicIcon icon={{ fi: 'FiInfo' }} />}
|
||||
onClick={onOpen}
|
||||
colorScheme="blue"
|
||||
aria-label={`${name}_help`}
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
import dynamic from 'next/dynamic';
|
||||
import { Flex, Icon, IconButton } from '@chakra-ui/react';
|
||||
import { Flex, IconButton } from '@chakra-ui/react';
|
||||
import { AnimatePresence } from 'framer-motion';
|
||||
import { AnimatedDiv } from '~/components';
|
||||
import { AnimatedDiv, DynamicIcon } from '~/components';
|
||||
import { useColorValue } from '~/context';
|
||||
import { useOpposingColor, useFormState } from '~/hooks';
|
||||
|
||||
import type { TResetButton } from './types';
|
||||
|
||||
const LeftArrow = dynamic<MeronexIcon>(() => import('@meronex/icons/fa').then(i => i.FaAngleLeft));
|
||||
|
||||
export const ResetButton = (props: TResetButton): JSX.Element => {
|
||||
const { developerMode, resetForm, ...rest } = props;
|
||||
const status = useFormState(s => s.status);
|
||||
@@ -34,11 +31,12 @@ export const ResetButton = (props: TResetButton): JSX.Element => {
|
||||
>
|
||||
<Flex boxSize="100%" justifyContent="center" alignItems="center" {...rest}>
|
||||
<IconButton
|
||||
variant="unstyled"
|
||||
lineHeight={0}
|
||||
color="current"
|
||||
variant="unstyled"
|
||||
aria-label="Reset"
|
||||
onClick={resetForm}
|
||||
icon={<Icon as={LeftArrow} boxSize={8} />}
|
||||
icon={<DynamicIcon icon={{ fa: 'FaAngleLeft' }} boxSize={8} />}
|
||||
/>
|
||||
</Flex>
|
||||
</AnimatedDiv>
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
import { forwardRef } from 'react';
|
||||
import { Icon, Text, Box, Tooltip, Menu, MenuButton, MenuList, Link } from '@chakra-ui/react';
|
||||
import { CgMoreO as More } from '@meronex/icons/cg';
|
||||
import { BisError as Warning } from '@meronex/icons/bi';
|
||||
import { MdCancel as NotAllowed } from '@meronex/icons/md';
|
||||
import { RiHome2Fill as End } from '@meronex/icons/ri';
|
||||
import { BsQuestionCircleFill as Question } from '@meronex/icons/bs';
|
||||
import { FaCheckCircle as Check, FaChevronRight as ChevronRight } from '@meronex/icons/fa';
|
||||
import { Text, Box, Tooltip, Menu, MenuButton, MenuList, Link } from '@chakra-ui/react';
|
||||
import dayjs from 'dayjs';
|
||||
import relativeTimePlugin from 'dayjs/plugin/relativeTime';
|
||||
import utcPlugin from 'dayjs/plugin/utc';
|
||||
import { If } from '~/components';
|
||||
import { If, DynamicIcon } from '~/components';
|
||||
import { useConfig, useColorValue } from '~/context';
|
||||
import { useOpposingColor } from '~/hooks';
|
||||
|
||||
@@ -41,10 +35,10 @@ export const Active: React.FC<TActive> = (props: TActive) => {
|
||||
return (
|
||||
<>
|
||||
<If c={isActive}>
|
||||
<Icon color={color[+isActive]} as={Check} />
|
||||
<DynamicIcon color={color[+isActive]} icon={{ fa: 'FaCheckCircle' }} />
|
||||
</If>
|
||||
<If c={!isActive}>
|
||||
<Icon color={color[+isActive]} as={NotAllowed} />
|
||||
<DynamicIcon color={color[+isActive]} icon={{ md: 'MdCancel' }} />
|
||||
</If>
|
||||
</>
|
||||
);
|
||||
@@ -86,7 +80,7 @@ export const ASPath: React.FC<TASPath> = (props: TASPath) => {
|
||||
);
|
||||
|
||||
if (path.length === 0) {
|
||||
return <Icon as={End} />;
|
||||
return <DynamicIcon icon={{ ri: 'RiHome2Fill' }} />;
|
||||
}
|
||||
|
||||
const paths = [] as JSX.Element[];
|
||||
@@ -95,7 +89,13 @@ export const ASPath: React.FC<TASPath> = (props: TASPath) => {
|
||||
const asnStr = String(asn);
|
||||
i !== 0 &&
|
||||
paths.push(
|
||||
<Icon as={ChevronRight} key={`separator-${i}`} color={color[+active]} boxSize={5} px={2} />,
|
||||
<DynamicIcon
|
||||
icon={{ fa: 'FaChevronRight' }}
|
||||
key={`separator-${i}`}
|
||||
color={color[+active]}
|
||||
boxSize={5}
|
||||
px={2}
|
||||
/>,
|
||||
);
|
||||
paths.push(
|
||||
<Text fontSize="sm" as="span" whiteSpace="pre" fontFamily="mono" key={`as-${asnStr}-${i}`}>
|
||||
@@ -117,14 +117,14 @@ export const Communities: React.FC<TCommunities> = (props: TCommunities) => {
|
||||
<If c={communities.length === 0}>
|
||||
<Tooltip placement="right" hasArrow label={web.text.noCommunities}>
|
||||
<Link>
|
||||
<Icon as={Question} />
|
||||
<DynamicIcon icon={{ bs: 'BsQuestionCircleFill' }} />
|
||||
</Link>
|
||||
</Tooltip>
|
||||
</If>
|
||||
<If c={communities.length !== 0}>
|
||||
<Menu preventOverflow>
|
||||
<MenuButton>
|
||||
<Icon as={More} />
|
||||
<DynamicIcon icon={{ cg: 'CgMoreO' }} />
|
||||
</MenuButton>
|
||||
<MenuList
|
||||
p={3}
|
||||
@@ -163,7 +163,13 @@ const _RPKIState: React.ForwardRefRenderFunction<HTMLDivElement, TRPKIState> = (
|
||||
],
|
||||
);
|
||||
const color = useOpposingColor(bg[+active][state]);
|
||||
const icon = [NotAllowed, Check, Warning, Question];
|
||||
|
||||
const icon = [
|
||||
{ md: 'MdCancel' },
|
||||
{ fa: 'FaCheckCircle' },
|
||||
{ bi: 'BisError' },
|
||||
{ bs: 'BsQuestionCircleFill' },
|
||||
] as Record<string, string>[];
|
||||
|
||||
const text = [
|
||||
web.text.rpkiInvalid,
|
||||
@@ -181,7 +187,7 @@ const _RPKIState: React.ForwardRefRenderFunction<HTMLDivElement, TRPKIState> = (
|
||||
color={color}
|
||||
>
|
||||
<Box ref={ref} boxSize={5}>
|
||||
<Box as={icon[state]} color={bg[+active][state]} />
|
||||
<DynamicIcon icon={icon[state]} color={bg[+active][state]} />
|
||||
</Box>
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
import dynamic from 'next/dynamic';
|
||||
import { Button, Icon, Tooltip } from '@chakra-ui/react';
|
||||
import { Button, Tooltip } from '@chakra-ui/react';
|
||||
import { DynamicIcon } from '~/components';
|
||||
|
||||
import type { TPathButton } from './types';
|
||||
|
||||
const PathIcon = dynamic<MeronexIcon>(() =>
|
||||
import('@meronex/icons/bi').then(i => i.BisNetworkChart),
|
||||
);
|
||||
|
||||
export const PathButton: React.FC<TPathButton> = (props: TPathButton) => {
|
||||
export const PathButton = (props: TPathButton): JSX.Element => {
|
||||
const { onOpen } = props;
|
||||
return (
|
||||
<Tooltip hasArrow label="View AS Path" placement="top">
|
||||
<Button as="a" mx={1} size="sm" variant="ghost" onClick={onOpen} colorScheme="secondary">
|
||||
<Icon as={PathIcon} boxSize="16px" />
|
||||
<DynamicIcon icon={{ bi: 'BiNetworkChart' }} boxSize="16px" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import dynamic from 'next/dynamic';
|
||||
import { ButtonGroup, IconButton } from '@chakra-ui/react';
|
||||
import { useZoomPanHelper } from 'react-flow-renderer';
|
||||
|
||||
const Plus = dynamic<MeronexIcon>(() => import('@meronex/icons/fi').then(i => i.FiPlus));
|
||||
const Minus = dynamic<MeronexIcon>(() => import('@meronex/icons/fi').then(i => i.FiMinus));
|
||||
const Square = dynamic<MeronexIcon>(() => import('@meronex/icons/fi').then(i => i.FiSquare));
|
||||
import { DynamicIcon } from '~/components';
|
||||
|
||||
export const Controls: React.FC = () => {
|
||||
const { fitView, zoomIn, zoomOut } = useZoomPanHelper();
|
||||
@@ -20,9 +16,21 @@ export const Controls: React.FC = () => {
|
||||
variant="solid"
|
||||
colorScheme="secondary"
|
||||
>
|
||||
<IconButton icon={<Plus />} onClick={() => zoomIn()} aria-label="Zoom In" />
|
||||
<IconButton icon={<Minus />} onClick={() => zoomOut()} aria-label="Zoom Out" />
|
||||
<IconButton icon={<Square />} onClick={() => fitView()} aria-label="Fit Nodes" />
|
||||
<IconButton
|
||||
icon={<DynamicIcon icon={{ fi: 'FiPlus' }} />}
|
||||
onClick={() => zoomIn()}
|
||||
aria-label="Zoom In"
|
||||
/>
|
||||
<IconButton
|
||||
icon={<DynamicIcon icon={{ fi: 'FiMinus' }} />}
|
||||
onClick={() => zoomOut()}
|
||||
aria-label="Zoom Out"
|
||||
/>
|
||||
<IconButton
|
||||
icon={<DynamicIcon icon={{ fi: 'FiSquare' }} />}
|
||||
onClick={() => fitView()}
|
||||
aria-label="Fit Nodes"
|
||||
/>
|
||||
</ButtonGroup>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import dynamic from 'next/dynamic';
|
||||
import { Button, Icon, Tooltip, useClipboard } from '@chakra-ui/react';
|
||||
|
||||
const Copy = dynamic<MeronexIcon>(() => import('@meronex/icons/fi').then(i => i.FiCopy));
|
||||
const Check = dynamic<MeronexIcon>(() => import('@meronex/icons/fi').then(i => i.FiCheck));
|
||||
import { Button, Tooltip, useClipboard } from '@chakra-ui/react';
|
||||
import { DynamicIcon } from '~/components';
|
||||
|
||||
import type { TCopyButton } from './types';
|
||||
|
||||
export const CopyButton: React.FC<TCopyButton> = (props: TCopyButton) => {
|
||||
export const CopyButton = (props: TCopyButton): JSX.Element => {
|
||||
const { copyValue, ...rest } = props;
|
||||
const { onCopy, hasCopied } = useClipboard(copyValue);
|
||||
return (
|
||||
@@ -20,7 +17,7 @@ export const CopyButton: React.FC<TCopyButton> = (props: TCopyButton) => {
|
||||
colorScheme="secondary"
|
||||
{...rest}
|
||||
>
|
||||
<Icon as={hasCopied ? Check : Copy} boxSize="16px" />
|
||||
<DynamicIcon icon={{ fi: hasCopied ? 'FiCheck' : 'FiCopy' }} boxSize="16px" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useMemo } from 'react';
|
||||
import { AccordionIcon, Box, Spinner, HStack, Text, Tooltip } from '@chakra-ui/react';
|
||||
import { BisError as Warning } from '@meronex/icons/bi';
|
||||
import { FaCheckCircle as Check } from '@meronex/icons/fa';
|
||||
import { DynamicIcon } from '~/components';
|
||||
import { useConfig, useColorValue } from '~/context';
|
||||
import { useOpposingColor, useStrf } from '~/hooks';
|
||||
|
||||
@@ -43,8 +42,8 @@ export const ResultHeader: React.FC<TResultHeader> = (props: TResultHeader) => {
|
||||
{loading ? (
|
||||
<Spinner size="sm" mr={4} color={status} />
|
||||
) : (
|
||||
<Box
|
||||
as={isError ? Warning : Check}
|
||||
<DynamicIcon
|
||||
icon={isError ? { bi: 'BisError' } : { fa: 'FaCheckCircle' }}
|
||||
color={isError ? warning : defaultStatus}
|
||||
mr={4}
|
||||
boxSize="100%"
|
||||
|
||||
@@ -2,7 +2,6 @@ import { forwardRef, memo, useEffect, useMemo } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Flex,
|
||||
Icon,
|
||||
Alert,
|
||||
chakra,
|
||||
HStack,
|
||||
@@ -14,10 +13,9 @@ import {
|
||||
useAccordionContext,
|
||||
} from '@chakra-ui/react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { BsLightningFill } from '@meronex/icons/bs';
|
||||
import { startCase } from 'lodash';
|
||||
import startCase from 'lodash/startCase';
|
||||
import isEqual from 'react-fast-compare';
|
||||
import { BGPTable, Countdown, TextOutput, If, Path } from '~/components';
|
||||
import { BGPTable, Countdown, DynamicIcon, If, Path, TextOutput } from '~/components';
|
||||
import { useColorValue, useConfig, useMobile } from '~/context';
|
||||
import { useStrf, useLGQuery, useTableToString, useFormState, useDevice } from '~/hooks';
|
||||
import { isStructuredOutput, isStringOutput } from '~/types';
|
||||
@@ -262,7 +260,7 @@ const _Result: React.ForwardRefRenderFunction<HTMLDivElement, ResultProps> = (
|
||||
</If>
|
||||
<Tooltip hasArrow label={cacheLabel} placement="top">
|
||||
<Box>
|
||||
<Icon as={BsLightningFill} color={color} />
|
||||
<DynamicIcon icon={{ bs: 'BsLightningFill' }} color={color} />
|
||||
</Box>
|
||||
</Tooltip>
|
||||
<If c={isMobile}>
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import { forwardRef } from 'react';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { Button, Icon, Tooltip } from '@chakra-ui/react';
|
||||
import { Button, Tooltip } from '@chakra-ui/react';
|
||||
import { DynamicIcon } from '~/components';
|
||||
|
||||
import type { TRequeryButton } from './types';
|
||||
|
||||
const Repeat = dynamic<MeronexIcon>(() => import('@meronex/icons/fi').then(i => i.FiRepeat));
|
||||
|
||||
const _RequeryButton: React.ForwardRefRenderFunction<HTMLButtonElement, TRequeryButton> = (
|
||||
props: TRequeryButton,
|
||||
ref,
|
||||
@@ -25,7 +23,7 @@ const _RequeryButton: React.ForwardRefRenderFunction<HTMLButtonElement, TRequery
|
||||
colorScheme="secondary"
|
||||
{...rest}
|
||||
>
|
||||
<Icon as={Repeat} boxSize="16px" />
|
||||
<DynamicIcon icon={{ fi: 'FiRepeat' }} boxSize="16px" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
@@ -13,9 +13,8 @@ import {
|
||||
ModalCloseButton,
|
||||
PopoverCloseButton,
|
||||
} from '@chakra-ui/react';
|
||||
import { FiSearch } from '@meronex/icons/fi';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import { If, ResolvedTarget } from '~/components';
|
||||
import { DynamicIcon, If, ResolvedTarget } from '~/components';
|
||||
import { useMobile, useColorValue } from '~/context';
|
||||
import { useFormState } from '~/hooks';
|
||||
|
||||
@@ -33,7 +32,7 @@ const _SubmitIcon: React.ForwardRefRenderFunction<
|
||||
size="lg"
|
||||
width={16}
|
||||
type="submit"
|
||||
icon={<FiSearch />}
|
||||
icon={<DynamicIcon icon={{ fi: 'FiSearch' }} />}
|
||||
title="Submit Query"
|
||||
colorScheme="primary"
|
||||
isLoading={isLoading}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
// This rule isn't needed because react-table does this for us, for better or worse.
|
||||
/* eslint react/jsx-key: 0 */
|
||||
|
||||
import dynamic from 'next/dynamic';
|
||||
import { Flex, Icon, Text } from '@chakra-ui/react';
|
||||
import { Flex, Text } from '@chakra-ui/react';
|
||||
import { usePagination, useSortBy, useTable } from 'react-table';
|
||||
import { useMobile } from '~/context';
|
||||
import { CardBody, CardFooter, CardHeader, If } from '~/components';
|
||||
import { CardBody, CardFooter, CardHeader, DynamicIcon, If } from '~/components';
|
||||
import { TableMain } from './table';
|
||||
import { TableCell } from './cell';
|
||||
import { TableHead } from './head';
|
||||
@@ -18,25 +16,6 @@ import type { TableOptions, PluginHook } from 'react-table';
|
||||
import type { TCellRender } from '~/types';
|
||||
import type { TTable } from './types';
|
||||
|
||||
const ChevronRight = dynamic<MeronexIcon>(() =>
|
||||
import('@meronex/icons/fa').then(i => i.FaChevronRight),
|
||||
);
|
||||
|
||||
const ChevronLeft = dynamic<MeronexIcon>(() =>
|
||||
import('@meronex/icons/fa').then(i => i.FaChevronLeft),
|
||||
);
|
||||
|
||||
const ChevronDown = dynamic<MeronexIcon>(() =>
|
||||
import('@meronex/icons/fa').then(i => i.FaChevronDown),
|
||||
);
|
||||
|
||||
const DoubleChevronRight = dynamic<MeronexIcon>(() =>
|
||||
import('@meronex/icons/fi').then(i => i.FiChevronsRight),
|
||||
);
|
||||
const DoubleChevronLeft = dynamic<MeronexIcon>(() =>
|
||||
import('@meronex/icons/fi').then(i => i.FiChevronsLeft),
|
||||
);
|
||||
|
||||
export const Table: React.FC<TTable> = (props: TTable) => {
|
||||
const {
|
||||
data,
|
||||
@@ -112,10 +91,10 @@ export const Table: React.FC<TTable> = (props: TTable) => {
|
||||
</Text>
|
||||
<If c={column.isSorted}>
|
||||
<If c={typeof column.isSortedDesc !== 'undefined'}>
|
||||
<Icon as={ChevronDown} boxSize={4} ml={1} />
|
||||
<DynamicIcon icon={{ fa: 'FaChevronDown' }} boxSize={4} ml={1} />
|
||||
</If>
|
||||
<If c={!column.isSortedDesc}>
|
||||
<Icon as={ChevronRight} boxSize={4} ml={1} />
|
||||
<DynamicIcon icon={{ fa: 'FaChevronRight' }} boxSize={4} ml={1} />
|
||||
</If>
|
||||
</If>
|
||||
<If c={!column.isSorted}>{''}</If>
|
||||
@@ -163,13 +142,13 @@ export const Table: React.FC<TTable> = (props: TTable) => {
|
||||
mr={2}
|
||||
onClick={() => gotoPage(0)}
|
||||
isDisabled={!canPreviousPage}
|
||||
icon={<Icon as={DoubleChevronLeft} boxSize={4} />}
|
||||
icon={<DynamicIcon icon={{ fi: 'FiChevronsLeft' }} boxSize={4} />}
|
||||
/>
|
||||
<TableIconButton
|
||||
mr={2}
|
||||
onClick={() => previousPage()}
|
||||
isDisabled={!canPreviousPage}
|
||||
icon={<Icon as={ChevronLeft} boxSize={3} />}
|
||||
icon={<DynamicIcon icon={{ fa: 'FaChevronLeft' }} boxSize={3} />}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex justifyContent="center" alignItems="center">
|
||||
@@ -193,12 +172,12 @@ export const Table: React.FC<TTable> = (props: TTable) => {
|
||||
ml={2}
|
||||
onClick={nextPage}
|
||||
isDisabled={!canNextPage}
|
||||
icon={<Icon as={ChevronRight} boxSize={3} />}
|
||||
icon={<DynamicIcon icon={{ fa: 'FaChevronRight' }} boxSize={3} />}
|
||||
/>
|
||||
<TableIconButton
|
||||
ml={2}
|
||||
isDisabled={!canNextPage}
|
||||
icon={<Icon as={DoubleChevronRight} boxSize={4} />}
|
||||
icon={<DynamicIcon icon={{ fi: 'FiChevronsRight' }} boxSize={4} />}
|
||||
onClick={() => gotoPage(pageCount ? pageCount - 1 : 1)}
|
||||
/>
|
||||
</Flex>
|
||||
|
||||
181
hyperglass/ui/components/util/dynamic-icon.tsx
Normal file
181
hyperglass/ui/components/util/dynamic-icon.tsx
Normal file
@@ -0,0 +1,181 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
/* eslint-disable react-hooks/rules-of-hooks */
|
||||
import { memo, useMemo } from 'react';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { chakra, Icon as ChakraIcon } from '@chakra-ui/react';
|
||||
import isEqual from 'react-fast-compare';
|
||||
|
||||
import type { IconProps as ChakraIconProps, TooltipProps } from '@chakra-ui/react';
|
||||
|
||||
interface IconMap {
|
||||
[library: string]: string;
|
||||
}
|
||||
|
||||
interface DynamicIconProps extends Omit<ChakraIconProps, 'icon'> {
|
||||
icon: IconMap;
|
||||
}
|
||||
|
||||
interface ErrorIconProps {
|
||||
message: string;
|
||||
}
|
||||
|
||||
interface IconErrorConstructor {
|
||||
original: IconMap;
|
||||
library: string;
|
||||
iconName: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend builtin `Error` for easier handling of icon rendering errors.
|
||||
*/
|
||||
class IconError extends Error {
|
||||
/**
|
||||
* Original family → icon mapping object.
|
||||
*/
|
||||
original: IconMap;
|
||||
/**
|
||||
* Determined family/icon library.
|
||||
*/
|
||||
library: string;
|
||||
/**
|
||||
* Determined icon name.
|
||||
*/
|
||||
iconName: string;
|
||||
|
||||
constructor({ original, library, iconName }: IconErrorConstructor) {
|
||||
super();
|
||||
this.original = original;
|
||||
this.library = library;
|
||||
this.iconName = iconName;
|
||||
this.stack = this.stack + `\nOriginal object: '${JSON.stringify(this.original)}'`;
|
||||
}
|
||||
|
||||
get message(): string {
|
||||
return `No icon matches 'react-icons/${this.library}/${this.iconName}'`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive `react-icons` icon family → icon name mapping with proper capitalization. Also handles
|
||||
* existence (or not) of the family prefix.
|
||||
* @param iconObj Family to icon name mapping.
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* iconPath({ fa: 'FaPlus' });
|
||||
* iconPath({ fa: 'faplus' });
|
||||
* iconPath({ fa: 'plus' });
|
||||
* // all return → ['fa', 'FaPlus']
|
||||
* ```
|
||||
* @returns
|
||||
*/
|
||||
function iconPath(iconObj: IconMap): [string, string] {
|
||||
// Capitalize the first character of a string.
|
||||
const capitalizeFirst = (s: string) => s.charAt(0).toUpperCase() + s.slice(1);
|
||||
|
||||
// Get the first object key.
|
||||
const [familyKey] = Object.keys(iconObj);
|
||||
// Capitalize the family name.
|
||||
const family = capitalizeFirst(familyKey!);
|
||||
// Get the icon name.
|
||||
const initialName = iconObj[familyKey!];
|
||||
// Capitalize the icon name. If `faplus` is provided, it will now be `Faplus`.
|
||||
let name = capitalizeFirst(initialName!);
|
||||
// Create a regex pattern to determine if the family name is in the icon name. If `name` is
|
||||
// `Faplus`, this will be true.
|
||||
const familyPattern = new RegExp(`^${family}`, 'g');
|
||||
|
||||
if (name.match(familyPattern)) {
|
||||
// If the icon name contains the family name, remove it and capitalize the result. If `name`
|
||||
// was `Faplus`, it is now `Plus`.
|
||||
name = capitalizeFirst(name.replace(familyPattern, ''));
|
||||
}
|
||||
// Return a tuple of [family, icon name], i.e. [fa, FaPlus].
|
||||
return [family.toLowerCase(), `${family}${name}`];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic error icon to indicate that there was a problem dynamically importing or otherwise
|
||||
* rendering the dynamic icon. Wraps generic icon in a tooltip that provides more detail. This
|
||||
* is dynamically imported at render time in an effort to reduce load times.
|
||||
*
|
||||
* @param props Error message to be displayed.
|
||||
*/
|
||||
const ErrorIcon = (props: ErrorIconProps): JSX.Element => {
|
||||
const Tooltip = dynamic<TooltipProps>(() => import('@chakra-ui/react').then(m => m.Tooltip));
|
||||
return (
|
||||
<Tooltip hasArrow bg="red.500" label={props.message}>
|
||||
<chakra.span boxSize={8} color="red.500" p={1} textAlign="center">
|
||||
⚠
|
||||
</chakra.span>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
const _DynamicIcon = (props: DynamicIconProps): JSX.Element => {
|
||||
const { icon: iconObj, ...rest } = props;
|
||||
// Create a string representation of the icon family and name mapping for memoization.
|
||||
const key = Object.entries(iconObj).flat().join('--');
|
||||
try {
|
||||
const [library, iconName] = useMemo(() => {
|
||||
return iconPath(iconObj);
|
||||
}, [key]);
|
||||
|
||||
if (!library || !iconName) {
|
||||
// If either the library or icon name are falsy, error out.
|
||||
throw new IconError({ original: iconObj, iconName, library });
|
||||
}
|
||||
// Create a memoized version of the imported component, to update only when the computed
|
||||
// family/icon names are changed. Attempt to dynamically import icon from formatted
|
||||
// library/icon name.
|
||||
|
||||
const Component = useMemo(
|
||||
() =>
|
||||
dynamic(() =>
|
||||
import(`react-icons/${library}/index.js`)
|
||||
.then(i => {
|
||||
if (!(iconName in i)) {
|
||||
// If the icon name doesn't exist in the module, error out.
|
||||
throw new IconError({ original: iconObj, iconName, library });
|
||||
}
|
||||
// Otherwise, return the imported icon.
|
||||
return i[iconName as keyof typeof i];
|
||||
})
|
||||
.catch(error => {
|
||||
// Handle any error that occurs during dynamic import.
|
||||
console.error(error);
|
||||
const CaughtError = (): JSX.Element => <ErrorIcon message={String(error)} />;
|
||||
return CaughtError;
|
||||
}),
|
||||
),
|
||||
[library, iconName],
|
||||
);
|
||||
|
||||
// Return a Chakra-UI icon instance with the imported icon.
|
||||
return <ChakraIcon as={Component} {...rest} />;
|
||||
} catch (error) {
|
||||
// Handle any other uncaught errors.
|
||||
console.error(error);
|
||||
return <ErrorIcon message={String(error)} />;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Dynamically import a `react-icons` icon by name and wrap it in a Chakra-UI icon component.
|
||||
*
|
||||
* @param props Icon family to icon name mapping.
|
||||
*
|
||||
* @throws An error icon is produced if there is any error during the dynamic import process. A
|
||||
* console message is also displayed with additional details.
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* <>
|
||||
* <Icon icon={{ fa: 'FaPlus' }} />
|
||||
* // This also works:
|
||||
* <Icon icon={{ fa: 'plus' }} />
|
||||
* </>
|
||||
* ```
|
||||
*/
|
||||
export const DynamicIcon = memo(_DynamicIcon, isEqual);
|
||||
export default DynamicIcon;
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from './animated';
|
||||
export * from './dynamic-icon';
|
||||
export * from './if';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useMemo } from 'react';
|
||||
import create from 'zustand';
|
||||
import { intersectionWith } from 'lodash';
|
||||
import intersectionWith from 'lodash/intersectionWith';
|
||||
import plur from 'plur';
|
||||
import isEqual from 'react-fast-compare';
|
||||
import { all, andJoin, dedupObjectArray, withDev } from '~/util';
|
||||
|
||||
5
hyperglass/ui/package.json
vendored
5
hyperglass/ui/package.json
vendored
@@ -21,7 +21,6 @@
|
||||
"@emotion/styled": "^11.6.0",
|
||||
"@hookform/devtools": "^4.0.1",
|
||||
"@hookform/resolvers": "^2.8.4",
|
||||
"@meronex/icons": "^4.0.0",
|
||||
"dagre": "^0.8.5",
|
||||
"dayjs": "^1.10.4",
|
||||
"framer-motion": "^5.4.1",
|
||||
@@ -38,6 +37,7 @@
|
||||
"react-flow-renderer": "^9.6.0",
|
||||
"react-ga": "^3.3.0",
|
||||
"react-hook-form": "^7.21.0",
|
||||
"react-icons": "^4.3.1",
|
||||
"react-markdown": "^5.0.3",
|
||||
"react-query": "^3.16.0",
|
||||
"react-select": "^5.2.1",
|
||||
@@ -45,13 +45,14 @@
|
||||
"remark-gfm": "^1.0.0",
|
||||
"string-format": "^2.0.0",
|
||||
"vest": "^3.2.8",
|
||||
"zustand": "^3.5.10"
|
||||
"zustand": "^3.6.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/jest-dom": "^5.14.1",
|
||||
"@testing-library/react": "^12.1.0",
|
||||
"@types/dagre": "^0.7.44",
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/lodash": "^4.14.177",
|
||||
"@types/node": "^14.14.41",
|
||||
"@types/react": "^17.0.3",
|
||||
"@types/react-table": "^7.7.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { devtools } from 'zustand/middleware';
|
||||
|
||||
import type { StateCreator } from 'zustand';
|
||||
import type { StateCreator, SetState, GetState, StoreApi } from 'zustand';
|
||||
|
||||
/**
|
||||
* Wrap a zustand state function with devtools, if applicable.
|
||||
@@ -14,7 +14,7 @@ export function withDev<T extends object = {}>(
|
||||
name: string,
|
||||
): StateCreator<T> {
|
||||
if (typeof window !== 'undefined' && process.env.NODE_ENV === 'development') {
|
||||
return devtools<T>(store, { name });
|
||||
return devtools<T, SetState<T>, GetState<T>, StoreApi<T>>(store, { name });
|
||||
}
|
||||
return store;
|
||||
}
|
||||
|
||||
33
hyperglass/ui/yarn.lock
vendored
33
hyperglass/ui/yarn.lock
vendored
@@ -1514,14 +1514,6 @@
|
||||
"@types/yargs" "^16.0.0"
|
||||
chalk "^4.0.0"
|
||||
|
||||
"@meronex/icons@^4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@meronex/icons/-/icons-4.0.0.tgz#26e089a8a4ec176a5b6778fd54fcdd25b4746c67"
|
||||
integrity sha512-WnoxUT02qawZSvsoPSwe7YOqOk0APysIHugiD3dYdc/QNeoigN4PD8mmmtmZFKlv8/Z7eERub0BmPkWcJ1BI+w==
|
||||
dependencies:
|
||||
camelcase "^5.0.0"
|
||||
ncp "^2.0.0"
|
||||
|
||||
"@napi-rs/triples@1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@napi-rs/triples/-/triples-1.0.3.tgz#76d6d0c3f4d16013c61e45dfca5ff1e6c31ae53c"
|
||||
@@ -2094,6 +2086,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.170.tgz#0d67711d4bf7f4ca5147e9091b847479b87925d6"
|
||||
integrity sha512-bpcvu/MKHHeYX+qeEN8GE7DIravODWdACVA1ctevD8CN24RhPZIKMn9ntfAsrvLfSX3cR5RrBKAbYm9bGs0A+Q==
|
||||
|
||||
"@types/lodash@^4.14.177":
|
||||
version "4.14.177"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.177.tgz#f70c0d19c30fab101cad46b52be60363c43c4578"
|
||||
integrity sha512-0fDwydE2clKe9MNfvXHBHF9WEahRuj+msTuQqOmAApNORFvhMYZKNGGJdCzuhheVjMps/ti0Ak/iJPACMaevvw==
|
||||
|
||||
"@types/mdast@^3.0.0", "@types/mdast@^3.0.3":
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.3.tgz#2d7d671b1cd1ea3deb306ea75036c2a0407d2deb"
|
||||
@@ -2944,7 +2941,7 @@ callsites@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
|
||||
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
|
||||
|
||||
camelcase@^5.0.0, camelcase@^5.3.1:
|
||||
camelcase@^5.3.1:
|
||||
version "5.3.1"
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
|
||||
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
|
||||
@@ -6175,11 +6172,6 @@ natural-compare@^1.4.0:
|
||||
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
|
||||
integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
|
||||
|
||||
ncp@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3"
|
||||
integrity sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=
|
||||
|
||||
negotiator@0.6.2:
|
||||
version "0.6.2"
|
||||
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
|
||||
@@ -6957,6 +6949,11 @@ react-hook-form@^7.21.0:
|
||||
resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.21.0.tgz#f6f0311295b83ca768873ca9f2a787ff1525f591"
|
||||
integrity sha512-aekCf+dedYFIg+7nCK2acMvZ+s6Ohw2I7UNQ+zNIadBl1SoXow2Tl6c3F49xF8GFCdn5jeK43JHH26rmtdRyLQ==
|
||||
|
||||
react-icons@^4.3.1:
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.3.1.tgz#2fa92aebbbc71f43d2db2ed1aed07361124e91ca"
|
||||
integrity sha512-cB10MXLTs3gVuXimblAdI71jrJx8njrJZmNMEMC+sQu5B/BIOmlsAjskdqpn81y8UBVEGuHODd7/ci5DvoSzTQ==
|
||||
|
||||
react-is@17.0.2, "react-is@^16.12.0 || ^17.0.0", react-is@^17.0.1, react-is@^17.0.2:
|
||||
version "17.0.2"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
|
||||
@@ -8433,10 +8430,10 @@ yocto-queue@^0.1.0:
|
||||
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
|
||||
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
|
||||
|
||||
zustand@^3.5.10:
|
||||
version "3.5.10"
|
||||
resolved "https://registry.yarnpkg.com/zustand/-/zustand-3.5.10.tgz#d2622efd64530ffda285ee5b13ff645b68ab0faf"
|
||||
integrity sha512-upluvSRWrlCiExu2UbkuMIPJ9AigyjRFoO7O9eUossIj7rPPq7pcJ0NKk6t2P7KF80tg/UdPX6/pNKOSbs9DEg==
|
||||
zustand@^3.6.6:
|
||||
version "3.6.6"
|
||||
resolved "https://registry.yarnpkg.com/zustand/-/zustand-3.6.6.tgz#3b7473a15813f7af9784233abd052c3b4560bbcc"
|
||||
integrity sha512-y4755cIzJHQFEHgTQ5cHrlHdmXMxm5N3DU05Q27yT6rK4lKs2336t5IsAz5q9/GRaoEz6o8SiCOPDhZd5BnneA==
|
||||
|
||||
zwitch@^1.0.0:
|
||||
version "1.0.5"
|
||||
|
||||
Reference in New Issue
Block a user