diff --git a/.eslintrc b/.eslintrc index 8d05f63..9edd5f1 100644 --- a/.eslintrc +++ b/.eslintrc @@ -30,6 +30,14 @@ "@typescript-eslint/no-floating-promises": ["error"], "@typescript-eslint/no-non-null-assertion": ["off"], "@typescript-eslint/no-require-imports": ["warn"], - "jsx-a11y/alt-text": ["off"] + "@typescript-eslint/no-unused-vars": [ + "error", + { + "argsIgnorePattern": "^_", + "varsIgnorePattern": "^_", + "caughtErrorsIgnorePattern": "^_" + } + ], + "jsx-a11y/alt-text": ["off"], } } diff --git a/package.json b/package.json index 673ed65..c32fb4c 100644 --- a/package.json +++ b/package.json @@ -1,18 +1,18 @@ { "name": "@hyperlane-xyz/explorer", "description": "An interchain explorer for the Hyperlane protocol and network.", - "version": "3.13.0", + "version": "5.4.0", "author": "J M Rossy", "dependencies": { - "@headlessui/react": "^1.7.17", - "@hyperlane-xyz/registry": "4.3.6", - "@hyperlane-xyz/sdk": "5.2.1", - "@hyperlane-xyz/utils": "5.2.1", - "@hyperlane-xyz/widgets": "5.2.1", - "@metamask/jazzicon": "https://github.com/jmrossy/jazzicon#7a8df28974b4e81129bfbe3cab76308b889032a6", + "@headlessui/react": "^2.1.8", + "@hyperlane-xyz/registry": "4.6.0", + "@hyperlane-xyz/sdk": "5.5.0", + "@hyperlane-xyz/utils": "5.5.0", + "@hyperlane-xyz/widgets": "5.5.0", "@tanstack/react-query": "^5.35.5", "bignumber.js": "^9.1.2", "buffer": "^6.0.3", + "clsx": "^2.1.1", "ethers": "^5.7.2", "formik": "^2.2.9", "graphql": "^16.6.0", @@ -23,9 +23,9 @@ "react-toastify": "^9.1.1", "react-tooltip": "^5.26.3", "urql": "^3.0.3", - "yaml": "^2.4.2", + "yaml": "^2.4.5", "zod": "^3.21.2", - "zustand": "4.3.8" + "zustand": "^4.5.5" }, "devDependencies": { "@trivago/prettier-plugin-sort-imports": "^4.1.1", @@ -42,7 +42,7 @@ "jest": "^29.6.3", "postcss": "^8.4.21", "prettier": "^2.8.4", - "tailwindcss": "^3.3.3", + "tailwindcss": "^3.4.13", "ts-node": "^10.9.1", "typescript": "^5.5.4" }, diff --git a/public/images/background.svg b/public/images/background.svg new file mode 100644 index 0000000..1d4a803 --- /dev/null +++ b/public/images/background.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/layout/AppLayout.tsx b/src/AppLayout.tsx similarity index 85% rename from src/components/layout/AppLayout.tsx rename to src/AppLayout.tsx index 58c082e..7e9e16b 100644 --- a/src/components/layout/AppLayout.tsx +++ b/src/AppLayout.tsx @@ -3,8 +3,8 @@ import { PropsWithChildren } from 'react'; import { toTitleCase } from '@hyperlane-xyz/utils'; -import { Footer } from '../nav/Footer'; -import { Header } from '../nav/Header'; +import { Footer } from './components/nav/Footer'; +import { Header } from './components/nav/Header'; interface Props { pathName: string; @@ -25,7 +25,7 @@ export function AppLayout({ pathName, children }: PropsWithChildren) { {/* */} - + {children} @@ -43,10 +43,10 @@ function getHeadTitle(pathName: string) { const styles = { container: { - backgroundImage: 'url(/images/lines-bg-top.svg)', - backgroundSize: '94vw', + backgroundImage: 'url(/images/background.svg)', + backgroundSize: 'cover', backgroundRepeat: 'no-repeat', - backgroundPosition: 'center 80px', + backgroundPosition: 'center', }, main: { width: 'min(900px,96vw)', diff --git a/src/components/buttons/BackButton.tsx b/src/components/buttons/BackButton.tsx deleted file mode 100644 index c1c4694..0000000 --- a/src/components/buttons/BackButton.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import LeftArrow from '../../images/icons/arrow-left-circle.svg'; - -import { IconButton, IconButtonProps } from './IconButton'; - -export function BackButton(props: IconButtonProps) { - return ; -} diff --git a/src/components/buttons/IconButton.tsx b/src/components/buttons/IconButton.tsx deleted file mode 100644 index cda1daa..0000000 --- a/src/components/buttons/IconButton.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import Image from 'next/image'; -import { PropsWithChildren } from 'react'; - -export interface IconButtonProps { - width?: number; - height?: number; - classes?: string; - onClick?: () => void; - disabled?: boolean; - imgSrc?: any; - title?: string; - type?: 'button' | 'submit'; - passThruProps?: any; -} - -export function IconButton(props: PropsWithChildren) { - const { - width, - height, - classes, - onClick, - imgSrc, - disabled, - title, - type, - children, - passThruProps, - } = props; - - const base = 'flex items-center justify-center transition-all'; - const onHover = 'hover:opacity-70'; - const onDisabled = 'disabled:opacity-50'; - const onActive = 'active:opacity-60'; - const allClasses = `${base} ${onHover} ${onDisabled} ${onActive} ${classes}`; - - return ( - - - {children} - - ); -} diff --git a/src/components/buttons/XIconButton.tsx b/src/components/buttons/XIconButton.tsx deleted file mode 100644 index 7017fa6..0000000 --- a/src/components/buttons/XIconButton.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { memo } from 'react'; - -import X from '../../images/icons/x.svg'; - -import { IconButton } from './IconButton'; - -function _XIconButton({ - onClick, - title, - size = 20, -}: { - onClick: () => void; - title?: string; - size?: number; -}) { - return ( - - ); -} - -export const XIconButton = memo(_XIconButton); diff --git a/src/components/icons/Chevron.tsx b/src/components/icons/Chevron.tsx deleted file mode 100644 index b34fdf6..0000000 --- a/src/components/icons/Chevron.tsx +++ /dev/null @@ -1,134 +0,0 @@ -import { memo } from 'react'; - -import { Color } from '../../styles/Color'; - -interface Props { - width?: string | number; - height?: string | number; - direction: 'n' | 'e' | 's' | 'w'; - color?: string; - classes?: string; -} - -function _ChevronIcon({ width, height, direction, color, classes }: Props) { - let directionClass; - switch (direction) { - case 'n': - directionClass = 'rotate-180'; - break; - case 'e': - directionClass = '-rotate-90'; - break; - case 's': - directionClass = ''; - break; - case 'w': - directionClass = 'rotate-90'; - break; - default: - throw new Error(`Invalid chevron direction ${direction}`); - } - - return ( - - - - ); -} - -export const ChevronIcon = memo(_ChevronIcon); - -interface Props { - width?: string | number; - height?: string | number; - direction: 'n' | 'e' | 's' | 'w'; - color?: string; - classes?: string; -} - -function _HyperlaneChevron({ width, height, direction, color, classes }: Props) { - let directionClass; - switch (direction) { - case 'n': - directionClass = '-rotate-90'; - break; - case 'e': - directionClass = ''; - break; - case 's': - directionClass = 'rotate-90'; - break; - case 'w': - directionClass = 'rotate-180'; - break; - default: - throw new Error(`Invalid chevron direction ${direction}`); - } - - return ( - - - - ); -} - -export const HyperlaneChevron = memo(_HyperlaneChevron); - -function _HyperlaneWideChevron({ width, height, direction, color, classes }: Props) { - let directionClass; - switch (direction) { - case 'n': - directionClass = '-rotate-90'; - break; - case 'e': - directionClass = ''; - break; - case 's': - directionClass = 'rotate-90'; - break; - case 'w': - directionClass = 'rotate-180'; - break; - default: - throw new Error(`Invalid chevron direction ${direction}`); - } - - return ( - - - - ); -} - -export const HyperlaneWideChevron = memo(_HyperlaneWideChevron); diff --git a/src/components/icons/HelpIcon.tsx b/src/components/icons/HelpIcon.tsx index d7ead41..c101831 100644 --- a/src/components/icons/HelpIcon.tsx +++ b/src/components/icons/HelpIcon.tsx @@ -1,22 +1,26 @@ import { memo } from 'react'; -import Question from '../../images/icons/question-circle.svg'; -import { IconButton } from '../buttons/IconButton'; +import { IconButton, QuestionMarkIcon } from '@hyperlane-xyz/widgets'; -function _HelpIcon({ text, size = 20 }: { text: string; size?: number }) { +import { Color } from '../../styles/Color'; + +function _HelpIcon({ text, size = 16 }: { text: string; size?: number }) { + const tooltipProps = { + 'data-tooltip-content': text, + 'data-tooltip-id': 'root-tooltip', + 'data-tooltip-place': 'top-start', + }; return ( + // @ts-ignore allow pass-thru tooltip props + className="border border-gray-400 rounded-full p-px" + {...tooltipProps} + > + + ); } diff --git a/src/components/icons/HyperlaneLogo.tsx b/src/components/icons/HyperlaneLogo.tsx deleted file mode 100644 index 5281046..0000000 --- a/src/components/icons/HyperlaneLogo.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { memo } from 'react'; - -function _HyperlaneLogo({ - width, - height, - fill, - className = '', -}: { - width?: number | string; - height?: number | string; - fill?: string; - className?: string; -}) { - return ( - - - - - - ); -} - -export const HyperlaneLogo = memo(_HyperlaneLogo); diff --git a/src/components/icons/XIcon.tsx b/src/components/icons/XIcon.tsx deleted file mode 100644 index 311510f..0000000 --- a/src/components/icons/XIcon.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { memo } from 'react'; - -function _XIcon({ - width, - height, - fill, - className = '', -}: { - width?: number | string; - height?: number | string; - fill?: string; - className?: string; -}) { - return ( - - - - - ); -} - -export const XIcon = memo(_XIcon); diff --git a/src/components/input/Checkbox.module.css b/src/components/input/Checkbox.module.css deleted file mode 100644 index 8e35255..0000000 --- a/src/components/input/Checkbox.module.css +++ /dev/null @@ -1,33 +0,0 @@ -.checkbox { - appearance: none; - background-color: #fff; - margin: 0; - width: 0.9rem; - height: 0.9rem; - border: 2px solid #1f2937; - border-radius: 0.2rem; - transform: translateY(-0.075em); - display: grid; - place-content: center; - cursor: pointer; - transition: 200ms all ease-in-out; -} - -.checkbox::before { - content: ''; - width: 0.9rem; - height: 0.9rem; - border-radius: 0.2rem; - transform: scale(0); - transition: 200ms all ease-in-out; - background-color: #2362c0; -} - -.checkbox:checked::before { - transform: scale(1); -} - -.checkbox:disabled { - border-color: #bbb; - cursor: not-allowed; -} diff --git a/src/components/input/Checkbox.tsx b/src/components/input/Checkbox.tsx deleted file mode 100644 index 8fc8d3c..0000000 --- a/src/components/input/Checkbox.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import React from 'react'; - -import styles from './Checkbox.module.css'; - -interface Props { - checked: boolean; - onToggle: (c: boolean) => void; - name?: string; -} - -export function CheckBox({ checked, onToggle, name, children }: React.PropsWithChildren) { - const onChange = () => { - onToggle(!checked); - }; - - return ( - - - {children} - - ); -} diff --git a/src/components/input/TextField.tsx b/src/components/input/TextField.tsx deleted file mode 100644 index 84adb3d..0000000 --- a/src/components/input/TextField.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { Field } from 'formik'; -import { ComponentProps } from 'react'; - -export function TextField(props: ComponentProps) { - return ( - - ); -} diff --git a/src/components/layout/BackgroundBanner.tsx b/src/components/layout/BackgroundBanner.tsx deleted file mode 100644 index 91a0cbb..0000000 --- a/src/components/layout/BackgroundBanner.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { WideChevron } from '@hyperlane-xyz/widgets'; - -import { useStore } from '../../store'; -import { classNameToColor } from '../../styles/Color'; - -export function BackgroundBanner() { - const bannerClassName = useStore((s) => s.bannerClassName); - const colorClass = bannerClassName || 'bg-blue-500'; - - return ( - - - - - ); -} - -function Chevron({ color, pos }: { color: string; pos: string }) { - return ( - - - - ); -} diff --git a/src/components/layout/Card.tsx b/src/components/layout/Card.tsx index c50b6a4..152598b 100644 --- a/src/components/layout/Card.tsx +++ b/src/components/layout/Card.tsx @@ -7,10 +7,6 @@ interface Props { export function Card({ className, padding = 'p-4 sm:p-5', children }: PropsWithChildren) { return ( - - {children} - + {children} ); } diff --git a/src/components/layout/Dropdown.tsx b/src/components/layout/Dropdown.tsx deleted file mode 100644 index 1aaf111..0000000 --- a/src/components/layout/Dropdown.tsx +++ /dev/null @@ -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 ( - - - {({ open }) => } - - - - {menuItems.map((mi, i) => ( - {({ close }) => mi(close)} - ))} - - - - ); -} - -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 ( - - - {buttonContent} - - - - {({ close }) => modalContent(close)} - - - - ); -} - -function DropdownTransition({ children }: PropsWithChildren) { - return ( - - {children} - - ); -} diff --git a/src/components/layout/Modal.tsx b/src/components/layout/Modal.tsx deleted file mode 100644 index a5158a5..0000000 --- a/src/components/layout/Modal.tsx +++ /dev/null @@ -1,64 +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 ( - - - - - - - - - - - - {title} - - {children} - - - - - - - - - - ); -} diff --git a/src/components/nav/Footer.tsx b/src/components/nav/Footer.tsx index 8adbbf5..11206c4 100644 --- a/src/components/nav/Footer.tsx +++ b/src/components/nav/Footer.tsx @@ -1,17 +1,12 @@ // Partly copied from https://github.com/hyperlane-xyz/hyperlane-website/blob/main/src/components/nav/Footer.tsx -import Image from 'next/image'; import Link from 'next/link'; +import { HyperlaneLogo } from '@hyperlane-xyz/widgets'; + import { docLinks, links } from '../../consts/links'; -// import FooterLine from '../../images/backgrounds/footer-line-desktop.svg'; -// import FooterLineMobile from '../../images/backgrounds/footer-line-mobile.svg'; -import FooterBg from '../../images/backgrounds/footer-bg.svg'; -import FooterTopBorder from '../../images/backgrounds/footer-top-border.svg'; import { Color } from '../../styles/Color'; import { Discord } from '../icons/Discord'; import { Github } from '../icons/Github'; -import { HyperlaneLogo } from '../icons/HyperlaneLogo'; -import { Medium } from '../icons/Medium'; import { Twitter } from '../icons/Twitter'; const footerLinks1 = [ @@ -34,88 +29,69 @@ const footerLinks3 = [ { title: 'Twitter', url: links.twitter, external: true, icon: }, { title: 'Discord', url: links.discord, external: true, icon: }, { title: 'Github', url: links.github, external: true, icon: }, - { title: 'Blog', url: links.blog, external: true, icon: }, ]; export function Footer() { return ( -