Upgrade headless and tailwind

Migrate to widgets layout components
pull/114/head
J M Rossy 2 months ago
parent 8cdf93c7b5
commit 0bace4598d
  1. 9
      package.json
  2. 6
      src/AppLayout.tsx
  3. 2
      src/components/layout/Card.tsx
  4. 88
      src/components/layout/Dropdown.tsx
  5. 66
      src/components/layout/Modal.tsx
  6. 41
      src/components/nav/Header.tsx
  7. 227
      src/components/search/SearchFilterBar.tsx
  8. 9
      src/features/chains/ConfigureChains.tsx
  9. 33
      src/features/chains/queries/useScrapedChains.ts
  10. 7
      src/features/chains/utils.ts
  11. 9
      src/features/messages/cards/TransactionCard.tsx
  12. 2
      src/pages/_app.tsx
  13. 13
      src/styles/global.css
  14. 279
      yarn.lock

@ -4,12 +4,11 @@
"version": "5.3.0", "version": "5.3.0",
"author": "J M Rossy", "author": "J M Rossy",
"dependencies": { "dependencies": {
"@headlessui/react": "^1.7.17", "@headlessui/react": "^2.1.8",
"@hyperlane-xyz/registry": "4.4.1", "@hyperlane-xyz/registry": "4.4.1",
"@hyperlane-xyz/sdk": "5.3.0", "@hyperlane-xyz/sdk": "5.3.0",
"@hyperlane-xyz/utils": "5.3.0", "@hyperlane-xyz/utils": "5.3.0",
"@hyperlane-xyz/widgets": "5.3.0", "@hyperlane-xyz/widgets": "5.3.0",
"@metamask/jazzicon": "https://github.com/jmrossy/jazzicon#7a8df28974b4e81129bfbe3cab76308b889032a6",
"@tanstack/react-query": "^5.35.5", "@tanstack/react-query": "^5.35.5",
"bignumber.js": "^9.1.2", "bignumber.js": "^9.1.2",
"buffer": "^6.0.3", "buffer": "^6.0.3",
@ -23,9 +22,9 @@
"react-toastify": "^9.1.1", "react-toastify": "^9.1.1",
"react-tooltip": "^5.26.3", "react-tooltip": "^5.26.3",
"urql": "^3.0.3", "urql": "^3.0.3",
"yaml": "^2.4.2", "yaml": "^2.4.5",
"zod": "^3.21.2", "zod": "^3.21.2",
"zustand": "4.3.8" "zustand": "^4.5.5"
}, },
"devDependencies": { "devDependencies": {
"@trivago/prettier-plugin-sort-imports": "^4.1.1", "@trivago/prettier-plugin-sort-imports": "^4.1.1",
@ -42,7 +41,7 @@
"jest": "^29.6.3", "jest": "^29.6.3",
"postcss": "^8.4.21", "postcss": "^8.4.21",
"prettier": "^2.8.4", "prettier": "^2.8.4",
"tailwindcss": "^3.3.3", "tailwindcss": "^3.4.13",
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"typescript": "^5.5.4" "typescript": "^5.5.4"
}, },

@ -3,8 +3,8 @@ import { PropsWithChildren } from 'react';
import { toTitleCase } from '@hyperlane-xyz/utils'; import { toTitleCase } from '@hyperlane-xyz/utils';
import { Footer } from '../nav/Footer'; import { Footer } from './components/nav/Footer';
import { Header } from '../nav/Header'; import { Header } from './components/nav/Header';
interface Props { interface Props {
pathName: string; pathName: string;
@ -25,7 +25,7 @@ export function AppLayout({ pathName, children }: PropsWithChildren<Props>) {
{/* <InfoBanner /> */} {/* <InfoBanner /> */}
<Header pathName={pathName} /> <Header pathName={pathName} />
<div className="max-w-5xl mx-auto grow"> <div className="max-w-5xl mx-auto grow">
<main style={styles.main} className="relative min-h-full pt-3 z-20"> <main style={styles.main} className="relative min-h-full pt-3">
{children} {children}
</main> </main>
</div> </div>

@ -7,6 +7,6 @@ interface Props {
export function Card({ className, padding = 'p-4 sm:p-5', children }: PropsWithChildren<Props>) { export function Card({ className, padding = 'p-4 sm:p-5', children }: PropsWithChildren<Props>) {
return ( return (
<div className={`bg-white rounded-2xl overflow-auto ${padding} ${className}`}>{children}</div> <div className={`bg-white rounded-xl overflow-auto ${padding} ${className}`}>{children}</div>
); );
} }

@ -1,88 +0,0 @@
import { Menu, Popover, Transition } from '@headlessui/react';
import { Fragment, PropsWithChildren, ReactElement, ReactNode } from 'react';
interface MenuProps {
ButtonContent: (p: { isOpen: boolean }) => ReactElement;
buttonClasses?: string;
buttonTitle?: string;
menuItems: Array<(close: () => void) => ReactElement>;
menuClasses?: string;
isFullscreen?: boolean;
}
// Uses Headless menu, which auto-closes on any item click
export function DropdownMenu({
ButtonContent,
buttonClasses,
buttonTitle,
menuItems,
menuClasses,
isFullscreen,
}: MenuProps) {
const menuItemsClass = isFullscreen
? `z-50 fixed left-0 right-0 top-20 bottom-0 w-screen bg-blue-500 focus:outline-none ${menuClasses}`
: `z-50 absolute -right-1.5 mt-3 origin-top-right rounded-md bg-white shadow-md drop-shadow-md focus:outline-none ${menuClasses}`;
return (
<Menu as="div" className="relative">
<Menu.Button title={buttonTitle} className={`flex ${buttonClasses}`}>
{({ open }) => <ButtonContent isOpen={open} />}
</Menu.Button>
<DropdownTransition>
<Menu.Items className={menuItemsClass}>
{menuItems.map((mi, i) => (
<Menu.Item key={`menu-item-${i}`}>{({ close }) => mi(close)}</Menu.Item>
))}
</Menu.Items>
</DropdownTransition>
</Menu>
);
}
interface ModalProps {
buttonContent: ReactNode;
buttonClasses?: string;
buttonTitle?: string;
modalContent: (close: () => void) => ReactElement;
modalClasses?: string;
}
// Uses Headless Popover, which is a more general purpose dropdown box
export function DropdownModal({
buttonContent,
buttonClasses,
buttonTitle,
modalContent,
modalClasses,
}: ModalProps) {
return (
<Popover className="relative">
<Popover.Button title={buttonTitle} className={`flex ${buttonClasses}`}>
{buttonContent}
</Popover.Button>
<DropdownTransition>
<Popover.Panel
className={`z-50 absolute mt-3 origin-top-right rounded-md bg-white shadow-md drop-shadow-md focus:outline-none ${modalClasses}`}
>
{({ close }) => modalContent(close)}
</Popover.Panel>
</DropdownTransition>
</Popover>
);
}
function DropdownTransition({ children }: PropsWithChildren<unknown>) {
return (
<Transition
as={Fragment}
enter="transition ease-out duration-200"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-100"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
{children}
</Transition>
);
}

@ -1,66 +0,0 @@
import { Dialog, Transition } from '@headlessui/react';
import { Fragment, PropsWithChildren } from 'react';
import XCircle from '../../images/icons/x-circle.svg';
import { IconButton } from '../buttons/IconButton';
export function Modal({
isOpen,
title,
close,
maxWidth,
children,
}: PropsWithChildren<{ isOpen: boolean; title?: string; close: () => void; maxWidth?: string }>) {
return (
<Transition appear show={isOpen} as={Fragment}>
<Dialog as="div" className="relative z-30" onClose={close}>
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 bg-black bg-opacity-25" />
</Transition.Child>
<div className="fixed inset-0 overflow-y-auto">
<div className="flex min-h-full items-center justify-center p-4 text-center">
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"
>
<Dialog.Panel
className={`w-full ${
maxWidth || 'max-w-xs'
} max-h-[90vh] transform overflow-auto rounded-xl bg-white px-4 py-4 text-left shadow-lg transition-all`}
>
{!!title && (
<Dialog.Title as="h3" className="font-medium text-blue-500">
{title}
</Dialog.Title>
)}
{children}
<div className="absolute right-3 top-3">
<IconButton
imgSrc={XCircle}
onClick={close}
title="Close"
classes="hover:rotate-90"
/>
</div>
</Dialog.Panel>
</Transition.Child>
</div>
</div>
</Dialog>
</Transition>
);
}

@ -2,13 +2,14 @@ import Image from 'next/image';
import Link from 'next/link'; import Link from 'next/link';
import { PropsWithChildren, useEffect, useState } from 'react'; import { PropsWithChildren, useEffect, useState } from 'react';
import { DropdownMenu } from '@hyperlane-xyz/widgets';
import { docLinks, links } from '../../consts/links'; import { docLinks, links } from '../../consts/links';
import Explorer from '../../images/logos/hyperlane-explorer.svg'; import Explorer from '../../images/logos/hyperlane-explorer.svg';
import Logo from '../../images/logos/hyperlane-logo.svg'; import Logo from '../../images/logos/hyperlane-logo.svg';
import Name from '../../images/logos/hyperlane-name.svg'; import Name from '../../images/logos/hyperlane-name.svg';
import { Color } from '../../styles/Color'; import { Color } from '../../styles/Color';
import { HyperlaneWideChevron } from '../icons/Chevron'; import { HyperlaneWideChevron } from '../icons/Chevron';
import { DropdownMenu } from '../layout/Dropdown';
import { MiniSearchBar } from '../search/MiniSearchBar'; import { MiniSearchBar } from '../search/MiniSearchBar';
const PAGES_EXCLUDING_SEARCH = ['/', '/debugger']; const PAGES_EXCLUDING_SEARCH = ['/', '/debugger'];
@ -35,7 +36,7 @@ export function Header({ pathName }: { pathName: string }) {
return ( return (
<header <header
className={`z-30 sticky top-0 px-2 sm:px-6 lg:px-12 w-full bg-blue-500 transition-all ease-in-out duration-300 ${ className={`z-10 sticky top-0 px-2 sm:px-6 lg:px-12 w-full bg-blue-500 transition-all ease-in-out duration-300 ${
animateHeader ? 'py-1 border-b border-white' : 'py-4 sm:py-5' animateHeader ? 'py-1 border-b border-white' : 'py-4 sm:py-5'
}`} }`}
> >
@ -81,38 +82,36 @@ export function Header({ pathName }: { pathName: string }) {
{/* Dropdown menu, used on mobile */} {/* Dropdown menu, used on mobile */}
<div className="relative flex item-center sm:hidden mr-2"> <div className="relative flex item-center sm:hidden mr-2">
<DropdownMenu <DropdownMenu
ButtonContent={DropdownButton} button={<DropdownButton />}
buttonClasses="hover:opacity-80 active:opacity-70 transition-all" buttonClassname="hover:opacity-80 active:opacity-70 transition-all"
buttonTitle="Options"
menuItems={[ menuItems={[
(c: Fn) => ( ({ close }) => (
<MobileNavLink href="/" closeDropdown={c} key="Home"> <MobileNavLink href="/" closeDropdown={close} key="Home">
Home Home
</MobileNavLink> </MobileNavLink>
), ),
(c: Fn) => ( ({ close }) => (
<MobileNavLink href="/settings" closeDropdown={c} key="Settings"> <MobileNavLink href="/settings" closeDropdown={close} key="Settings">
Settings Settings
</MobileNavLink> </MobileNavLink>
), ),
// (c: Fn) => ( // ({ close }) => (
// <MobileNavLink href="/api" closeDropdown={c} key="API"> // <MobileNavLink href="/api" closeDropdown={c} key="API">
// API // API
// </MobileNavLink> // </MobileNavLink>
// ), // ),
(c: Fn) => ( ({ close }) => (
<MobileNavLink href={docLinks.home} closeDropdown={c} key="Docs"> <MobileNavLink href={docLinks.home} closeDropdown={close} key="Docs">
Docs Docs
</MobileNavLink> </MobileNavLink>
), ),
(c: Fn) => ( ({ close }) => (
<MobileNavLink href={links.home} closeDropdown={c} key="About"> <MobileNavLink href={links.home} closeDropdown={close} key="About">
About About
</MobileNavLink> </MobileNavLink>
), ),
]} ]}
menuClasses="pt-8 px-8" menuClassname="!left-0 !right-0 py-7 px-8 bg-blue-500"
isFullscreen={true}
/> />
</div> </div>
</div> </div>
@ -120,27 +119,27 @@ export function Header({ pathName }: { pathName: string }) {
); );
} }
function DropdownButton({ isOpen }: { isOpen: boolean }) { function DropdownButton() {
return ( return (
<div className="px-4 py-1 flex flex-col items-center border border-white bg-pink-500 rounded-lg"> <div className="px-4 py-1 flex flex-col items-center border border-white bg-pink-500 rounded-lg">
<HyperlaneWideChevron <HyperlaneWideChevron
width={10} width={10}
height={14} height={14}
direction={isOpen ? 'n' : 's'} direction="s"
color={Color.white} color={Color.white}
classes="transition-all" classes="transition-all"
/> />
<HyperlaneWideChevron <HyperlaneWideChevron
width={10} width={10}
height={14} height={14}
direction={isOpen ? 'n' : 's'} direction="s"
color={Color.white} color={Color.white}
classes="-mt-1 transition-all" classes="-mt-1 transition-all"
/> />
<HyperlaneWideChevron <HyperlaneWideChevron
width={10} width={10}
height={14} height={14}
direction={isOpen ? 'n' : 's'} direction="s"
color={Color.white} color={Color.white}
classes="-mt-1 transition-all" classes="-mt-1 transition-all"
/> />
@ -162,7 +161,7 @@ function MobileNavLink({
rel={isExternal ? 'noopener noreferrer' : undefined} rel={isExternal ? 'noopener noreferrer' : undefined}
target={isExternal ? '_blank' : undefined} target={isExternal ? '_blank' : undefined}
> >
<span className="text-2xl font-medium text-white capitalize">{children}</span> <span className="text-xl font-medium text-white capitalize">{children}</span>
</Link> </Link>
); );
} }

@ -1,27 +1,18 @@
import Image from 'next/image'; import Image from 'next/image';
import Link from 'next/link'; import Link from 'next/link';
import { useMemo, useState } from 'react'; import { useState } from 'react';
import { ChainMetadata } from '@hyperlane-xyz/sdk'; import { ChainMetadata } from '@hyperlane-xyz/sdk';
import { arrayToObject } from '@hyperlane-xyz/utils'; import { ChainSearchMenu, Modal, Popover } from '@hyperlane-xyz/widgets';
import { useScrapedChains } from '../../features/chains/queries/useScrapedChains'; import { useScrapedEvmChains } from '../../features/chains/queries/useScrapedChains';
import {
getChainDisplayName,
isEvmChain,
isPiChain,
isUnscrapedDbChain,
} from '../../features/chains/utils';
import GearIcon from '../../images/icons/gear.svg'; import GearIcon from '../../images/icons/gear.svg';
import { useMultiProvider } from '../../store'; import { useMultiProvider } from '../../store';
import { Color } from '../../styles/Color'; import { Color } from '../../styles/Color';
import { SolidButton } from '../buttons/SolidButton'; import { SolidButton } from '../buttons/SolidButton';
import { TextButton } from '../buttons/TextButton'; import { TextButton } from '../buttons/TextButton';
import { ChainLogo } from '../icons/ChainLogo';
import { ChevronIcon } from '../icons/Chevron'; import { ChevronIcon } from '../icons/Chevron';
import { CheckBox } from '../input/Checkbox';
import { DatetimeField } from '../input/DatetimeField'; import { DatetimeField } from '../input/DatetimeField';
import { DropdownModal } from '../layout/Dropdown';
interface Props { interface Props {
originChain: string | null; originChain: string | null;
@ -46,14 +37,14 @@ export function SearchFilterBar({
}: Props) { }: Props) {
return ( return (
<div className="flex items-center space-x-2 md:space-x-4"> <div className="flex items-center space-x-2 md:space-x-4">
<ChainMultiSelector <ChainSelector
text="Origin" text="Origin"
header="Origin Chains" header="Origin Chains"
value={originChain} value={originChain}
onChangeValue={onChangeOrigin} onChangeValue={onChangeOrigin}
position="-right-32" position="-right-32"
/> />
<ChainMultiSelector <ChainSelector
text="Destination" text="Destination"
header="Destination Chains" header="Destination Chains"
value={destinationChain} value={destinationChain}
@ -75,7 +66,7 @@ export function SearchFilterBar({
); );
} }
function ChainMultiSelector({ function ChainSelector({
text, text,
header, header,
value, value,
@ -88,173 +79,41 @@ function ChainMultiSelector({
onChangeValue: (value: string | null) => void; onChangeValue: (value: string | null) => void;
position?: string; position?: string;
}) { }) {
const { scrapedChains } = useScrapedChains();
const multiProvider = useMultiProvider(); const multiProvider = useMultiProvider();
const { chains, mainnets, testnets } = useMemo(() => { const { chains } = useScrapedEvmChains(multiProvider);
const chains = Object.values(multiProvider.metadata);
// Filtering to EVM is necessary to prevent errors until cosmos support is added
// https://github.com/hyperlane-xyz/hyperlane-explorer/issues/61
const scrapedEvmChains = chains.filter(
(c) =>
isEvmChain(multiProvider, c.chainId) &&
!isPiChain(multiProvider, scrapedChains, c.chainId) &&
!isUnscrapedDbChain(multiProvider, c.chainId),
);
const mainnets = scrapedEvmChains.filter((c) => !c.isTestnet);
const testnets = scrapedEvmChains.filter((c) => !!c.isTestnet);
// Return only evmChains because of graphql only accept query non-evm chains (with bigint type not string)
return { chains: scrapedEvmChains, mainnets, testnets };
}, [multiProvider, scrapedChains]);
// Need local state as buffer before user hits apply // const [checkedChain, setCheckedChain] = useState<ChainId|null>(value);
const [checkedChains, setCheckedChains] = useState(
value
? arrayToObject(value.split(','))
: arrayToObject(chains.map((c) => c.chainId.toString())),
);
const hasAnyUncheckedChain = (chains: ChainMetadata[]) => {
for (const c of chains) {
if (!checkedChains[c.chainId]) return true;
}
return false;
};
const onToggle = (chainId: string | number) => {
return (checked: boolean) => {
if (!hasAnyUncheckedChain(chains)) {
// If none are unchecked, uncheck all except this one
setCheckedChains({ [chainId]: true });
} else {
setCheckedChains({ ...checkedChains, [chainId]: checked });
}
};
};
const onToggleSection = (chains: ChainMetadata[]) => {
return () => {
const chainIds = chains.map((c) => c.chainId.toString());
if (hasAnyUncheckedChain(chains)) {
// If some are unchecked, check all
setCheckedChains({ ...checkedChains, ...arrayToObject(chainIds, true) });
} else {
// If none are unchecked, uncheck all
setCheckedChains({ ...checkedChains, ...arrayToObject(chainIds, false) });
}
};
};
const onToggleAll = () => {
setCheckedChains(arrayToObject(chains.map((c) => c.chainId.toString())));
};
const onToggleNone = () => { const onClickChain = (c: ChainMetadata) => {
setCheckedChains({}); // setCheckedChain(c.chainId);
onChangeValue(c.chainId.toString());
}; };
const onClickApply = (closeDropdown?: () => void) => { const [showModal, setShowModal] = useState(false);
const checkedList = Object.keys(checkedChains).filter((c) => !!checkedChains[c]); const closeModal = () => {
if (checkedList.length === 0 || checkedList.length === chains.length) { setShowModal(false);
// Use null value, indicating to filter needed
onChangeValue(null);
} else {
onChangeValue(checkedList.join(','));
}
if (closeDropdown) closeDropdown();
}; };
return ( return (
<DropdownModal <>
buttonContent={ <button
<> type="button"
<span className="text-white font-medium py-px">{text}</span> className="text-sm sm:min-w-[5.8rem] px-1 sm:px-2.5 py-0.5 flex items-center justify-center rounded-full bg-pink-500 hover:opacity-80 active:opacity-70 transition-all"
<ChevronIcon onClick={() => setShowModal(!showModal)}
direction="s" >
width={9} <span className="text-white font-medium py-px">{text}</span>
height={5} <ChevronIcon
classes="ml-2 opacity-80" direction="s"
color={Color.White} width={9}
/> height={5}
</> classes="ml-2 opacity-80"
} color={Color.white}
buttonClasses="text-sm sm:min-w-[5.8rem] px-1 sm:px-2.5 py-0.5 flex items-center justify-center rounded-full bg-pink-500 hover:opacity-80 active:opacity-70 transition-all" />
modalContent={(closeDropdown) => ( </button>
<div className="p-4"> <Modal isOpen={showModal} close={closeModal} panelClassname="max-w-lg p-4 sm:p-5">
<div className="flex items-center justify-between"> <ChainSearchMenu chainMetadata={chains} onClickChain={onClickChain} />
<h3 className="font-medium text-blue-500">{header}</h3> </Modal>
<div className="flex mr-4"> </>
<TextButton classes="text-sm font-medium text-pink-500" onClick={onToggleAll}>
All
</TextButton>
<TextButton classes="ml-3.5 text-sm font-medium text-pink-500" onClick={onToggleNone}>
None
</TextButton>
</div>
</div>
<div className="mt-2.5 flex space-x-2">
<div className="flex flex-col overflow-x-hidden overflow-y-auto max-h-100">
<div className="pb-1.5">
<CheckBox
checked={!hasAnyUncheckedChain(mainnets)}
onToggle={onToggleSection(mainnets)}
name="mainnet-chains"
>
<h4 className="ml-2 text-gray-800">Mainnet Chains</h4>
</CheckBox>
</div>
{mainnets.map((c) => (
<CheckBox
key={c.name}
checked={!!checkedChains[c.chainId]}
onToggle={onToggle(c.chainId)}
name={c.name}
>
<div className="py-0.5 ml-2 text-sm flex items-center">
<span className="mr-2 font-light">
{getChainDisplayName(multiProvider, c.chainId, true)}
</span>
<ChainLogo chainId={c.chainId} size={12} background={false} />
</div>
</CheckBox>
))}
</div>
<div className="flex flex-col overflow-x-hidden overflow-y-auto max-h-100">
<div className="pb-1.5">
<CheckBox
checked={!hasAnyUncheckedChain(testnets)}
onToggle={onToggleSection(testnets)}
name="testnet-chains"
>
<h4 className="ml-2 text-gray-800">Testnet Chains</h4>
</CheckBox>
</div>
{testnets.map((c) => (
<CheckBox
key={c.name}
checked={!!checkedChains[c.chainId]}
onToggle={onToggle(c.chainId)}
name={c.name}
>
<div className="py-0.5 ml-2 text-sm flex items-center">
<span className="mr-2 font-light">
{getChainDisplayName(multiProvider, c.chainId, true)}
</span>
<ChainLogo chainId={c.chainId} size={12} background={false} />
</div>
</CheckBox>
))}
</div>
</div>
<SolidButton
classes="mt-2.5 text-sm px-2 py-1 w-full"
onClick={() => onClickApply(closeDropdown)}
>
Apply
</SolidButton>
</div>
)}
modalClasses={`w-88 ${position || 'right-0'}`}
/>
); );
} }
@ -285,8 +144,8 @@ function DatetimeSelector({
}; };
return ( return (
<DropdownModal <Popover
buttonContent={ button={
<> <>
<span className="text-white font-medium py-px px-2">Time</span> <span className="text-white font-medium py-px px-2">Time</span>
<ChevronIcon <ChevronIcon
@ -294,12 +153,14 @@ function DatetimeSelector({
width={9} width={9}
height={5} height={5}
classes="ml-2 opacity-80" classes="ml-2 opacity-80"
color={Color.White} color={Color.white}
/> />
</> </>
} }
buttonClasses="text-sm px-1 sm:px-2.5 py-0.5 flex items-center justify-center rounded-full bg-pink-500 hover:opacity-80 active:opacity-70 transition-all" buttonClassname="text-sm px-1 sm:px-2.5 py-0.5 flex items-center justify-center rounded-full bg-pink-500 hover:opacity-80 active:opacity-70 transition-all"
modalContent={(closeDropdown) => ( panelClassname="w-60"
>
{({ close }) => (
<div className="p-4" key="date-time-selector"> <div className="p-4" key="date-time-selector">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<h3 className="text-blue-500 font-medium">Time Range</h3> <h3 className="text-blue-500 font-medium">Time Range</h3>
@ -315,15 +176,11 @@ function DatetimeSelector({
<h4 className="mt-3 mb-1 text-gray-500 text-sm font-medium">End Time</h4> <h4 className="mt-3 mb-1 text-gray-500 text-sm font-medium">End Time</h4>
<DatetimeField timestamp={endTime} onChange={setEndTime} /> <DatetimeField timestamp={endTime} onChange={setEndTime} />
</div> </div>
<SolidButton <SolidButton classes="mt-4 text-sm px-2 py-1 w-full" onClick={() => onClickApply(close)}>
classes="mt-4 text-sm px-2 py-1 w-full"
onClick={() => onClickApply(closeDropdown)}
>
Apply Apply
</SolidButton> </SolidButton>
</div> </div>
)} )}
modalClasses="w-60 -right-8" </Popover>
/>
); );
} }

@ -1,13 +1,13 @@
import { ChangeEventHandler, useState } from 'react'; import { ChangeEventHandler, useState } from 'react';
import { ChainName } from '@hyperlane-xyz/sdk'; import { ChainName } from '@hyperlane-xyz/sdk';
import { Modal } from '@hyperlane-xyz/widgets';
import { CopyButton } from '../../components/buttons/CopyButton'; import { CopyButton } from '../../components/buttons/CopyButton';
import { SolidButton } from '../../components/buttons/SolidButton'; import { SolidButton } from '../../components/buttons/SolidButton';
import { XIconButton } from '../../components/buttons/XIconButton'; import { XIconButton } from '../../components/buttons/XIconButton';
import { ChainLogo } from '../../components/icons/ChainLogo'; import { ChainLogo } from '../../components/icons/ChainLogo';
import { Card } from '../../components/layout/Card'; import { Card } from '../../components/layout/Card';
import { Modal } from '../../components/layout/Modal';
import { docLinks } from '../../consts/links'; import { docLinks } from '../../consts/links';
import { useMultiProvider } from '../../store'; import { useMultiProvider } from '../../store';
@ -123,12 +123,7 @@ export function ConfigureChains() {
<SolidButton classes="mt-4 mb-2 py-0.5 w-full" onClick={() => setShowAddChainModal(true)}> <SolidButton classes="mt-4 mb-2 py-0.5 w-full" onClick={() => setShowAddChainModal(true)}>
Add custom chain Add custom chain
</SolidButton> </SolidButton>
<Modal <Modal isOpen={showAddChainModal} close={closeModal} panelClassname="max-w-lg p-4 sm:p-5">
isOpen={showAddChainModal}
close={closeModal}
title="Add Custom Chain"
maxWidth="max-w-xl"
>
<p className="mt-2 font-light"> <p className="mt-2 font-light">
Input a chain metadata config including core contract addresses to enable exploration of Input a chain metadata config including core contract addresses to enable exploration of
that chain. See{' '} that chain. See{' '}

@ -1,7 +1,12 @@
import { useEffect } from 'react'; import { useEffect, useMemo } from 'react';
import { useQuery } from 'urql'; import { useQuery } from 'urql';
import { ChainMetadata, MultiProvider } from '@hyperlane-xyz/sdk';
import { objFilter } from '@hyperlane-xyz/utils';
import { unscrapedChainsInDb } from '../../../consts/config';
import { useStore } from '../../../store'; import { useStore } from '../../../store';
import { isEvmChain, isPiChain } from '../utils';
import { DOMAINS_QUERY, DomainsEntry } from './fragments'; import { DOMAINS_QUERY, DomainsEntry } from './fragments';
@ -28,3 +33,29 @@ export function useScrapedChains() {
isError: !!error, isError: !!error,
}; };
} }
export function useScrapedEvmChains(multiProvider: MultiProvider) {
const { scrapedChains, isFetching, isError } = useScrapedChains();
const { chains } = useMemo(() => {
// Filtering to EVM is necessary to prevent errors until cosmos support is added
// https://github.com/hyperlane-xyz/hyperlane-explorer/issues/61
const scrapedEvmChains = objFilter(
multiProvider.metadata,
(chainName, chainMetadata): chainMetadata is ChainMetadata =>
isEvmChain(multiProvider, chainMetadata.chainId) &&
!isPiChain(multiProvider, scrapedChains, chainMetadata.chainId) &&
!isUnscrapedDbChain(multiProvider, chainMetadata.chainId),
);
// Return only evmChains because of graphql only accept query non-evm chains (with bigint type not string)
return { chains: scrapedEvmChains };
}, [multiProvider, scrapedChains]);
return { chains, isFetching, isError };
}
// TODO: Remove once all chains in the DB are scraped
export function isUnscrapedDbChain(multiProvider: MultiProvider, chainIdOrName: number | string) {
const chainName = multiProvider.tryGetChainName(chainIdOrName);
return chainName && unscrapedChainsInDb.includes(chainName);
}

@ -2,7 +2,6 @@ import { IRegistry } from '@hyperlane-xyz/registry';
import { ChainMap, MultiProvider } from '@hyperlane-xyz/sdk'; import { ChainMap, MultiProvider } from '@hyperlane-xyz/sdk';
import { ProtocolType, toTitleCase } from '@hyperlane-xyz/utils'; import { ProtocolType, toTitleCase } from '@hyperlane-xyz/utils';
import { unscrapedChainsInDb } from '../../consts/config';
import { Environment } from '../../consts/environments'; import { Environment } from '../../consts/environments';
import { ChainConfig } from './chainConfig'; import { ChainConfig } from './chainConfig';
@ -51,9 +50,3 @@ export function isEvmChain(multiProvider: MultiProvider, chainIdOrName: number |
const protocol = multiProvider.tryGetProtocol(chainIdOrName); const protocol = multiProvider.tryGetProtocol(chainIdOrName);
return protocol === ProtocolType.Ethereum; return protocol === ProtocolType.Ethereum;
} }
// TODO: Remove once all chains in the DB are scraped
export function isUnscrapedDbChain(multiProvider: MultiProvider, chainIdOrName: number | string) {
const chainName = multiProvider.tryGetChainName(chainIdOrName);
return chainName && unscrapedChainsInDb.includes(chainName);
}

@ -4,12 +4,12 @@ import { PropsWithChildren, ReactNode, useState } from 'react';
import { MultiProvider } from '@hyperlane-xyz/sdk'; import { MultiProvider } from '@hyperlane-xyz/sdk';
import { isAddress, isZeroish } from '@hyperlane-xyz/utils'; import { isAddress, isZeroish } from '@hyperlane-xyz/utils';
import { Modal } from '@hyperlane-xyz/widgets';
import { Spinner } from '../../../components/animations/Spinner'; import { Spinner } from '../../../components/animations/Spinner';
import { ChainLogo } from '../../../components/icons/ChainLogo'; import { ChainLogo } from '../../../components/icons/ChainLogo';
import { HelpIcon } from '../../../components/icons/HelpIcon'; import { HelpIcon } from '../../../components/icons/HelpIcon';
import { Card } from '../../../components/layout/Card'; import { Card } from '../../../components/layout/Card';
import { Modal } from '../../../components/layout/Modal';
import { links } from '../../../consts/links'; import { links } from '../../../consts/links';
import { useMultiProvider } from '../../../store'; import { useMultiProvider } from '../../../store';
import { MessageStatus, MessageTx } from '../../../types'; import { MessageStatus, MessageTx } from '../../../types';
@ -305,12 +305,7 @@ function CallDataModal({ debugResult }: { debugResult?: MessageDebugResult }) {
<button onClick={() => setIsOpen(true)} className={`mt-5 ${styles.textLink}`}> <button onClick={() => setIsOpen(true)} className={`mt-5 ${styles.textLink}`}>
View calldata details View calldata details
</button> </button>
<Modal <Modal isOpen={isOpen} close={() => setIsOpen(false)} panelClassname="max-w-lg p-4 sm:p-5">
isOpen={isOpen}
title="Message Delivery Calldata"
close={() => setIsOpen(false)}
maxWidth="max-w-sm sm:max-w-md"
>
<div className="mt-2 flex flex-col space-y-3.5"> <div className="mt-2 flex flex-col space-y-3.5">
<p className="text-sm font-light"> <p className="text-sm font-light">
{`The last step of message delivery is the recipient contract's 'handle' function. If the handle is reverting, try debugging it with `} {`The last step of message delivery is the recipient contract's 'handle' function. If the handle is reverting, try debugging it with `}

@ -7,8 +7,8 @@ import { Provider as UrqlProvider, createClient as createUrqlClient } from 'urql
import '@hyperlane-xyz/widgets/styles.css'; import '@hyperlane-xyz/widgets/styles.css';
import { AppLayout } from '../AppLayout';
import { ErrorBoundary } from '../components/errors/ErrorBoundary'; import { ErrorBoundary } from '../components/errors/ErrorBoundary';
import { AppLayout } from '../components/layout/AppLayout';
import { config } from '../consts/config'; import { config } from '../consts/config';
import { ChainConfigSyncer } from '../features/chains/ChainConfigSyncer'; import { ChainConfigSyncer } from '../features/chains/ChainConfigSyncer';
import { MAIN_FONT } from '../styles/fonts'; import { MAIN_FONT } from '../styles/fonts';

@ -68,6 +68,19 @@ body {
} }
} }
/* Tailwind extension to hide scrollbar */
@layer utilities {
/* Chrome, Safari and Opera */
.no-scrollbar::-webkit-scrollbar {
display: none;
}
.no-scrollbar {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
}
/* /*
Input Overrides Input Overrides
=============== ===============

@ -2025,6 +2025,25 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@floating-ui/core@npm:^1.6.0":
version: 1.6.8
resolution: "@floating-ui/core@npm:1.6.8"
dependencies:
"@floating-ui/utils": "npm:^0.2.8"
checksum: 87d52989c3d2cc80373bc153b7a40814db3206ce7d0b2a2bdfb63e2ff39ffb8b999b1b0ccf28e548000ebf863bf16e2bed45eab4c4d287a5dbe974ef22368d82
languageName: node
linkType: hard
"@floating-ui/dom@npm:^1.0.0":
version: 1.6.11
resolution: "@floating-ui/dom@npm:1.6.11"
dependencies:
"@floating-ui/core": "npm:^1.6.0"
"@floating-ui/utils": "npm:^0.2.8"
checksum: 8579392ad10151474869e7640af169b0d7fc2df48d4da27b6dcb1a57202329147ed986b2972787d4b8cd550c87897271b2d9c4633c2ec7d0b3ad37ce1da636f1
languageName: node
linkType: hard
"@floating-ui/dom@npm:^1.6.1": "@floating-ui/dom@npm:^1.6.1":
version: 1.6.3 version: 1.6.3
resolution: "@floating-ui/dom@npm:1.6.3" resolution: "@floating-ui/dom@npm:1.6.3"
@ -2035,6 +2054,32 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@floating-ui/react-dom@npm:^2.1.2":
version: 2.1.2
resolution: "@floating-ui/react-dom@npm:2.1.2"
dependencies:
"@floating-ui/dom": "npm:^1.0.0"
peerDependencies:
react: ">=16.8.0"
react-dom: ">=16.8.0"
checksum: 2a67dc8499674e42ff32c7246bded185bb0fdd492150067caf9568569557ac4756a67787421d8604b0f241e5337de10762aee270d9aeef106d078a0ff13596c4
languageName: node
linkType: hard
"@floating-ui/react@npm:^0.26.16":
version: 0.26.24
resolution: "@floating-ui/react@npm:0.26.24"
dependencies:
"@floating-ui/react-dom": "npm:^2.1.2"
"@floating-ui/utils": "npm:^0.2.8"
tabbable: "npm:^6.0.0"
peerDependencies:
react: ">=16.8.0"
react-dom: ">=16.8.0"
checksum: 903ffbee2c6726d117086e2a83f43d6ad339970758ce7979fd16cc7cf8dc0f5b869bd72c2c8ee1bcd6c63b190bb0960effd4d403e63685fb5aeed6b185041b08
languageName: node
linkType: hard
"@floating-ui/utils@npm:^0.2.0, @floating-ui/utils@npm:^0.2.1": "@floating-ui/utils@npm:^0.2.0, @floating-ui/utils@npm:^0.2.1":
version: 0.2.1 version: 0.2.1
resolution: "@floating-ui/utils@npm:0.2.1" resolution: "@floating-ui/utils@npm:0.2.1"
@ -2042,6 +2087,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@floating-ui/utils@npm:^0.2.8":
version: 0.2.8
resolution: "@floating-ui/utils@npm:0.2.8"
checksum: 3e3ea3b2de06badc4baebdf358b3dbd77ccd9474a257a6ef237277895943db2acbae756477ec64de65a2a1436d94aea3107129a1feeef6370675bf2b161c1abc
languageName: node
linkType: hard
"@gar/promisify@npm:^1.1.3": "@gar/promisify@npm:^1.1.3":
version: 1.1.3 version: 1.1.3
resolution: "@gar/promisify@npm:1.1.3" resolution: "@gar/promisify@npm:1.1.3"
@ -2058,15 +2110,18 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@headlessui/react@npm:^1.7.17": "@headlessui/react@npm:^2.1.8":
version: 1.7.17 version: 2.1.8
resolution: "@headlessui/react@npm:1.7.17" resolution: "@headlessui/react@npm:2.1.8"
dependencies: dependencies:
client-only: "npm:^0.0.1" "@floating-ui/react": "npm:^0.26.16"
"@react-aria/focus": "npm:^3.17.1"
"@react-aria/interactions": "npm:^3.21.3"
"@tanstack/react-virtual": "npm:^3.8.1"
peerDependencies: peerDependencies:
react: ^16 || ^17 || ^18 react: ^18
react-dom: ^16 || ^17 || ^18 react-dom: ^18
checksum: 00ad7db43bc1904a149925693f9d99d000e237d6e7206d0ded6bf43089070e0cb5b7188bef6f2f7d0d9175039dc90838f1cb9d0cc2b7479d6139da40de637fb7 checksum: a82f115877dcc5e3d16a6b0502b6796a5bd3f38936835e241833a538c002d4ecfc3317868b0d1e9655e5de93201b0806f51bc10dbf32604e270cda4fc1636024
languageName: node languageName: node
linkType: hard linkType: hard
@ -2118,12 +2173,11 @@ __metadata:
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "@hyperlane-xyz/explorer@workspace:." resolution: "@hyperlane-xyz/explorer@workspace:."
dependencies: dependencies:
"@headlessui/react": "npm:^1.7.17" "@headlessui/react": "npm:^2.1.8"
"@hyperlane-xyz/registry": "npm:4.4.1" "@hyperlane-xyz/registry": "npm:4.4.1"
"@hyperlane-xyz/sdk": "npm:5.3.0" "@hyperlane-xyz/sdk": "npm:5.3.0"
"@hyperlane-xyz/utils": "npm:5.3.0" "@hyperlane-xyz/utils": "npm:5.3.0"
"@hyperlane-xyz/widgets": "npm:5.3.0" "@hyperlane-xyz/widgets": "npm:5.3.0"
"@metamask/jazzicon": "https://github.com/jmrossy/jazzicon#7a8df28974b4e81129bfbe3cab76308b889032a6"
"@tanstack/react-query": "npm:^5.35.5" "@tanstack/react-query": "npm:^5.35.5"
"@trivago/prettier-plugin-sort-imports": "npm:^4.1.1" "@trivago/prettier-plugin-sort-imports": "npm:^4.1.1"
"@types/jest": "npm:^29.5.3" "@types/jest": "npm:^29.5.3"
@ -2150,13 +2204,13 @@ __metadata:
react-dom: "npm:^18.2.0" react-dom: "npm:^18.2.0"
react-toastify: "npm:^9.1.1" react-toastify: "npm:^9.1.1"
react-tooltip: "npm:^5.26.3" react-tooltip: "npm:^5.26.3"
tailwindcss: "npm:^3.3.3" tailwindcss: "npm:^3.4.13"
ts-node: "npm:^10.9.1" ts-node: "npm:^10.9.1"
typescript: "npm:^5.5.4" typescript: "npm:^5.5.4"
urql: "npm:^3.0.3" urql: "npm:^3.0.3"
yaml: "npm:^2.4.2" yaml: "npm:^2.4.5"
zod: "npm:^3.21.2" zod: "npm:^3.21.2"
zustand: "npm:4.3.8" zustand: "npm:^4.5.5"
languageName: unknown languageName: unknown
linkType: soft linkType: soft
@ -2689,15 +2743,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@metamask/jazzicon@https://github.com/jmrossy/jazzicon#7a8df28974b4e81129bfbe3cab76308b889032a6":
version: 2.1.0
resolution: "@metamask/jazzicon@https://github.com/jmrossy/jazzicon.git#commit=7a8df28974b4e81129bfbe3cab76308b889032a6"
dependencies:
mersenne-twister: "npm:^1.1.0"
checksum: 5e56251b375eade58294334783fb37a15e8fd48d792f6dc93f7247b8897541324f9cf2d3f1d9b1cffdac1d932a8bc48a89dee7cdbd6e4a312ca2ff85df90131b
languageName: node
linkType: hard
"@next/env@npm:13.5.6": "@next/env@npm:13.5.6":
version: 13.5.6 version: 13.5.6
resolution: "@next/env@npm:13.5.6" resolution: "@next/env@npm:13.5.6"
@ -3009,6 +3054,81 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@react-aria/focus@npm:^3.17.1":
version: 3.18.3
resolution: "@react-aria/focus@npm:3.18.3"
dependencies:
"@react-aria/interactions": "npm:^3.22.3"
"@react-aria/utils": "npm:^3.25.3"
"@react-types/shared": "npm:^3.25.0"
"@swc/helpers": "npm:^0.5.0"
clsx: "npm:^2.0.0"
peerDependencies:
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
checksum: b11632e638de2f40ec12a4a8c818059b9bf7e90b288a93b46985350c887ae7ecdf037391537f86fbacb2a186dec7e7c41a8f2ff767fd232a8cac3189f03735b2
languageName: node
linkType: hard
"@react-aria/interactions@npm:^3.21.3, @react-aria/interactions@npm:^3.22.3":
version: 3.22.3
resolution: "@react-aria/interactions@npm:3.22.3"
dependencies:
"@react-aria/ssr": "npm:^3.9.6"
"@react-aria/utils": "npm:^3.25.3"
"@react-types/shared": "npm:^3.25.0"
"@swc/helpers": "npm:^0.5.0"
peerDependencies:
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
checksum: bc1e8381bda81c106d64bb6eebe06c244bcd6905d1be95fdc26bad1c5d83c48d1ec5159fb1cb8ea9ee7ebafc76595702e2d174f3c8394b766779c0d34bfa6de7
languageName: node
linkType: hard
"@react-aria/ssr@npm:^3.9.6":
version: 3.9.6
resolution: "@react-aria/ssr@npm:3.9.6"
dependencies:
"@swc/helpers": "npm:^0.5.0"
peerDependencies:
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
checksum: ea6b290346ce1e119ed9233fc0e34693d52ab9dc2509f07ab10710409b89484a544b7f26c1438802e97f3fb634844ae54638850cdd95caca0d1f5571781bf982
languageName: node
linkType: hard
"@react-aria/utils@npm:^3.25.3":
version: 3.25.3
resolution: "@react-aria/utils@npm:3.25.3"
dependencies:
"@react-aria/ssr": "npm:^3.9.6"
"@react-stately/utils": "npm:^3.10.4"
"@react-types/shared": "npm:^3.25.0"
"@swc/helpers": "npm:^0.5.0"
clsx: "npm:^2.0.0"
peerDependencies:
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
checksum: 86aed35da5cb0d48d949e40bf8226d5a6d6c92a8cdc60e3e12d524d1f3cc91ab6b54c5e1642823773cbb889fb61af7da22e89488b704b56fc5f4d8d59da7519b
languageName: node
linkType: hard
"@react-stately/utils@npm:^3.10.4":
version: 3.10.4
resolution: "@react-stately/utils@npm:3.10.4"
dependencies:
"@swc/helpers": "npm:^0.5.0"
peerDependencies:
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
checksum: 8a56b4d0cf8d5a7a692d6f94ffff63feac2d7078fbc5642b94b0afcaaf7c8f7f4682cfe546f98265034c52576c198be5502cff3f9b145137884e50eb9ffb96d5
languageName: node
linkType: hard
"@react-types/shared@npm:^3.25.0":
version: 3.25.0
resolution: "@react-types/shared@npm:3.25.0"
peerDependencies:
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
checksum: fa31eb6153c223210c2eee46934a63b922917bcde0ee583f2cfe59675db122c10e1cbae6549b1fea4284391fdbeca6888b36e9dc797231ad4a76def01490aea5
languageName: node
linkType: hard
"@rushstack/eslint-patch@npm:^1.1.3": "@rushstack/eslint-patch@npm:^1.1.3":
version: 1.1.4 version: 1.1.4
resolution: "@rushstack/eslint-patch@npm:1.1.4" resolution: "@rushstack/eslint-patch@npm:1.1.4"
@ -3824,6 +3944,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@swc/helpers@npm:^0.5.0":
version: 0.5.13
resolution: "@swc/helpers@npm:0.5.13"
dependencies:
tslib: "npm:^2.4.0"
checksum: 6ba2f7e215d32d71fce139e2cfc426b3ed7eaa709febdeb07b97260a4c9eea4784cf047cc1271be273990b08220b576b94a42b5780947c0b3be84973a847a24d
languageName: node
linkType: hard
"@szmarczak/http-timer@npm:^4.0.5": "@szmarczak/http-timer@npm:^4.0.5":
version: 4.0.6 version: 4.0.6
resolution: "@szmarczak/http-timer@npm:4.0.6" resolution: "@szmarczak/http-timer@npm:4.0.6"
@ -3860,6 +3989,25 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@tanstack/react-virtual@npm:^3.8.1":
version: 3.10.8
resolution: "@tanstack/react-virtual@npm:3.10.8"
dependencies:
"@tanstack/virtual-core": "npm:3.10.8"
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
checksum: 40a5d6089908096634fec2aa0cd646ca47c044c745e1b0d190ecbf9905ad2e6266ccd56c2550ed92f47349954dc11eb6930beac1354441ce7c98af81c5454d3f
languageName: node
linkType: hard
"@tanstack/virtual-core@npm:3.10.8":
version: 3.10.8
resolution: "@tanstack/virtual-core@npm:3.10.8"
checksum: 047e95fa72a0d341c0da8468799c176fd448481432f976a4780911bb4a2256aa4788d828f79fad78d127fe859b785189c13ca0fea10c560bf14d8ab8cb2c7790
languageName: node
linkType: hard
"@tootallnate/once@npm:2": "@tootallnate/once@npm:2":
version: 2.0.0 version: 2.0.0
resolution: "@tootallnate/once@npm:2.0.0" resolution: "@tootallnate/once@npm:2.0.0"
@ -5497,7 +5645,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"client-only@npm:0.0.1, client-only@npm:^0.0.1": "client-only@npm:0.0.1":
version: 0.0.1 version: 0.0.1
resolution: "client-only@npm:0.0.1" resolution: "client-only@npm:0.0.1"
checksum: 0c16bf660dadb90610553c1d8946a7fdfb81d624adea073b8440b7d795d5b5b08beb3c950c6a2cf16279365a3265158a236876d92bce16423c485c322d7dfaf8 checksum: 0c16bf660dadb90610553c1d8946a7fdfb81d624adea073b8440b7d795d5b5b08beb3c950c6a2cf16279365a3265158a236876d92bce16423c485c322d7dfaf8
@ -5531,6 +5679,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"clsx@npm:^2.0.0":
version: 2.1.1
resolution: "clsx@npm:2.1.1"
checksum: cdfb57fa6c7649bbff98d9028c2f0de2f91c86f551179541cf784b1cfdc1562dcb951955f46d54d930a3879931a980e32a46b598acaea274728dbe068deca919
languageName: node
linkType: hard
"co@npm:^4.6.0": "co@npm:^4.6.0":
version: 4.6.0 version: 4.6.0
resolution: "co@npm:4.6.0" resolution: "co@npm:4.6.0"
@ -6999,7 +7154,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"fast-glob@npm:^3.2.11, fast-glob@npm:^3.2.12": "fast-glob@npm:^3.2.11":
version: 3.2.12 version: 3.2.12
resolution: "fast-glob@npm:3.2.12" resolution: "fast-glob@npm:3.2.12"
dependencies: dependencies:
@ -7025,6 +7180,19 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"fast-glob@npm:^3.3.0":
version: 3.3.2
resolution: "fast-glob@npm:3.3.2"
dependencies:
"@nodelib/fs.stat": "npm:^2.0.2"
"@nodelib/fs.walk": "npm:^1.2.3"
glob-parent: "npm:^5.1.2"
merge2: "npm:^1.3.0"
micromatch: "npm:^4.0.4"
checksum: 222512e9315a0efca1276af9adb2127f02105d7288fa746145bf45e2716383fb79eb983c89601a72a399a56b7c18d38ce70457c5466218c5f13fad957cee16df
languageName: node
linkType: hard
"fast-json-stable-stringify@npm:^2.0.0, fast-json-stable-stringify@npm:^2.1.0": "fast-json-stable-stringify@npm:^2.0.0, fast-json-stable-stringify@npm:^2.1.0":
version: 2.1.0 version: 2.1.0
resolution: "fast-json-stable-stringify@npm:2.1.0" resolution: "fast-json-stable-stringify@npm:2.1.0"
@ -8962,12 +9130,12 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"jiti@npm:^1.18.2": "jiti@npm:^1.21.0":
version: 1.20.0 version: 1.21.6
resolution: "jiti@npm:1.20.0" resolution: "jiti@npm:1.21.6"
bin: bin:
jiti: bin/jiti.js jiti: bin/jiti.js
checksum: c4e59419dcf5599e599602c6c6bd0b3e19748c0bce886887cc91542ea085ef11f69a25dbda2b0ac7af8085afda34eef89ac6e9311949a01839c52a9af4352ec2 checksum: 289b124cea411c130a14ffe88e3d38376ab44b6695616dfa0a1f32176a8f20ec90cdd6d2b9d81450fc6467cfa4d865f04f49b98452bff0f812bc400fd0ae78d6
languageName: node languageName: node
linkType: hard linkType: hard
@ -9457,13 +9625,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"mersenne-twister@npm:^1.1.0":
version: 1.1.0
resolution: "mersenne-twister@npm:1.1.0"
checksum: 1123526199091097102f2f91639ad7d5b3df4b098de9a4a72c835920e11ef0ce08e25737d5af1d363325a60da8804365eae8a41e03b7a46a1acc22e18fa8f261
languageName: node
linkType: hard
"methods@npm:~1.1.2": "methods@npm:~1.1.2":
version: 1.1.2 version: 1.1.2
resolution: "methods@npm:1.1.2" resolution: "methods@npm:1.1.2"
@ -12004,19 +12165,26 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"tailwindcss@npm:^3.3.3": "tabbable@npm:^6.0.0":
version: 3.3.3 version: 6.2.0
resolution: "tailwindcss@npm:3.3.3" resolution: "tabbable@npm:6.2.0"
checksum: 980fa73476026e99dcacfc0d6e000d41d42c8e670faf4682496d30c625495e412c4369694f2a15cf1e5252d22de3c396f2b62edbe8d60b5dadc40d09e3f2dde3
languageName: node
linkType: hard
"tailwindcss@npm:^3.4.13":
version: 3.4.13
resolution: "tailwindcss@npm:3.4.13"
dependencies: dependencies:
"@alloc/quick-lru": "npm:^5.2.0" "@alloc/quick-lru": "npm:^5.2.0"
arg: "npm:^5.0.2" arg: "npm:^5.0.2"
chokidar: "npm:^3.5.3" chokidar: "npm:^3.5.3"
didyoumean: "npm:^1.2.2" didyoumean: "npm:^1.2.2"
dlv: "npm:^1.1.3" dlv: "npm:^1.1.3"
fast-glob: "npm:^3.2.12" fast-glob: "npm:^3.3.0"
glob-parent: "npm:^6.0.2" glob-parent: "npm:^6.0.2"
is-glob: "npm:^4.0.3" is-glob: "npm:^4.0.3"
jiti: "npm:^1.18.2" jiti: "npm:^1.21.0"
lilconfig: "npm:^2.1.0" lilconfig: "npm:^2.1.0"
micromatch: "npm:^4.0.5" micromatch: "npm:^4.0.5"
normalize-path: "npm:^3.0.0" normalize-path: "npm:^3.0.0"
@ -12033,7 +12201,7 @@ __metadata:
bin: bin:
tailwind: lib/cli.js tailwind: lib/cli.js
tailwindcss: lib/cli.js tailwindcss: lib/cli.js
checksum: bc47f40cc33aca95fb9d523ecef0a450241e51d2259c354ac283c6a06c4dcd7edd1ffbd6f065fc496390ff3ab4dd8349c968b10cce7e11e0bde101705fa0f4f1 checksum: 01b8dd35a65a028474c632b9ea7fb38634060a2c70f1f3fdfa2fe6ec74dec8224e2ee1178a5428182849790dad324e7a810de7301a9126946528c59d37f455cf
languageName: node languageName: node
linkType: hard linkType: hard
@ -12513,12 +12681,12 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"use-sync-external-store@npm:1.2.0": "use-sync-external-store@npm:1.2.2":
version: 1.2.0 version: 1.2.2
resolution: "use-sync-external-store@npm:1.2.0" resolution: "use-sync-external-store@npm:1.2.2"
peerDependencies: peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 react: ^16.8.0 || ^17.0.0 || ^18.0.0
checksum: a676216affc203876bd47981103f201f28c2731361bb186367e12d287a7566763213a8816910c6eb88265eccd4c230426eb783d64c373c4a180905be8820ed8e checksum: 671e9c190aab9a8374a5d468c6ba17f52c38b6fae970110bc196fc1e2b57204149aea9619be49a1bb5207fb6e51d8afd19c3bcb94afe61813fed039821461dc0
languageName: node languageName: node
linkType: hard linkType: hard
@ -13247,12 +13415,12 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"yaml@npm:^2.4.2": "yaml@npm:^2.4.5":
version: 2.4.2 version: 2.5.1
resolution: "yaml@npm:2.4.2" resolution: "yaml@npm:2.5.1"
bin: bin:
yaml: bin.mjs yaml: bin.mjs
checksum: 6eafbcd68dead734035f6f72af21bd820c29214caf7d8e40c595671a3c908535cef8092b9660a1c055c5833aa148aa640e0c5fa4adb5af2dacd6d28296ccd81c checksum: 0eecb679db75ea6a989ad97715a9fa5d946972945aa6aa7d2175bca66c213b5564502ccb1cdd04b1bf816ee38b5c43e4e2fda3ff6f5e09da24dabb51ae92c57d
languageName: node languageName: node
linkType: hard linkType: hard
@ -13308,19 +13476,22 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"zustand@npm:4.3.8": "zustand@npm:^4.5.5":
version: 4.3.8 version: 4.5.5
resolution: "zustand@npm:4.3.8" resolution: "zustand@npm:4.5.5"
dependencies: dependencies:
use-sync-external-store: "npm:1.2.0" use-sync-external-store: "npm:1.2.2"
peerDependencies: peerDependencies:
immer: ">=9.0" "@types/react": ">=16.8"
immer: ">=9.0.6"
react: ">=16.8" react: ">=16.8"
peerDependenciesMeta: peerDependenciesMeta:
"@types/react":
optional: true
immer: immer:
optional: true optional: true
react: react:
optional: true optional: true
checksum: 95a5335716414c8bef3a48165226ef099ca232931ab6cd1497515ee4241e8d5a8100edf5c3cc7d7131b72a07eb0484501405aa2c3222b4b93ba690cfa2b5593d checksum: 481b8210187b69678074a1ca51107654c2379688e90407bfcb7961e0803a259742bfd0d77171c3f07e290896ad55fe9659b3863f30d34cb2572650ead1249f25
languageName: node languageName: node
linkType: hard linkType: hard

Loading…
Cancel
Save