import type { WalletName } from '@solana/wallet-adapter-base'; import { WalletReadyState } from '@solana/wallet-adapter-base'; import type { Wallet } from '@solana/wallet-adapter-react'; import { useWallet } from '@solana/wallet-adapter-react'; import type { FC, MouseEvent } from 'react'; import React, { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react'; import { createPortal } from 'react-dom'; import { Collapse } from './Collapse.js'; import { WalletListItem } from './WalletListItem.js'; import { WalletSVG } from './WalletSVG.js'; import { useWalletModal } from './useWalletModal.js'; export interface WalletModalProps { className?: string; container?: string; } export const WalletModal: FC = ({ className = '', container = 'body' }) => { const ref = useRef(null); const { wallets, select } = useWallet(); const { setVisible } = useWalletModal(); const [expanded, setExpanded] = useState(false); const [fadeIn, setFadeIn] = useState(false); const [portal, setPortal] = useState(null); const [listedWallets, collapsedWallets] = useMemo(() => { const installed: Wallet[] = []; const notInstalled: Wallet[] = []; for (const wallet of wallets) { if (wallet.readyState === WalletReadyState.Installed) { installed.push(wallet); } else { notInstalled.push(wallet); } } return installed.length ? [installed, notInstalled] : [notInstalled, []]; }, [wallets]); const hideModal = useCallback(() => { setFadeIn(false); setTimeout(() => setVisible(false), 150); }, [setVisible]); const handleClose = useCallback( (event: MouseEvent) => { event.preventDefault(); hideModal(); }, [hideModal] ); const handleWalletClick = useCallback( (event: MouseEvent, walletName: WalletName) => { select(walletName); handleClose(event); }, [select, handleClose] ); const handleCollapseClick = useCallback(() => setExpanded(!expanded), [expanded]); const handleTabKey = useCallback( (event: KeyboardEvent) => { const node = ref.current; if (!node) return; // here we query all focusable elements const focusableElements = node.querySelectorAll('button'); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const firstElement = focusableElements[0]!; // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const lastElement = focusableElements[focusableElements.length - 1]!; if (event.shiftKey) { // if going backward by pressing tab and firstElement is active, shift focus to last focusable element if (document.activeElement === firstElement) { lastElement.focus(); event.preventDefault(); } } else { // if going forward by pressing tab and lastElement is active, shift focus to first focusable element if (document.activeElement === lastElement) { firstElement.focus(); event.preventDefault(); } } }, [ref] ); useLayoutEffect(() => { const handleKeyDown = (event: KeyboardEvent) => { if (event.key === 'Escape') { hideModal(); } else if (event.key === 'Tab') { handleTabKey(event); } }; // Get original overflow const { overflow } = window.getComputedStyle(document.body); // Hack to enable fade in animation after mount setTimeout(() => setFadeIn(true), 0); // Prevent scrolling on mount document.body.style.overflow = 'hidden'; // Listen for keydown events window.addEventListener('keydown', handleKeyDown, false); return () => { // Re-enable scrolling when component unmounts document.body.style.overflow = overflow; window.removeEventListener('keydown', handleKeyDown, false); }; }, [hideModal, handleTabKey]); useLayoutEffect(() => setPortal(document.querySelector(container)), [container]); return ( portal && createPortal(
{listedWallets.length ? ( <>

Connect a wallet on Solana to continue

    {listedWallets.map((wallet) => ( handleWalletClick(event, wallet.adapter.name)} wallet={wallet} /> ))} {collapsedWallets.length ? ( {collapsedWallets.map((wallet) => ( handleWalletClick(event, wallet.adapter.name) } tabIndex={expanded ? 0 : -1} wallet={wallet} /> ))} ) : null}
{collapsedWallets.length ? ( ) : null} ) : ( <>

You'll need a wallet on Solana to continue

{collapsedWallets.length ? ( <>
    {collapsedWallets.map((wallet) => ( handleWalletClick(event, wallet.adapter.name) } tabIndex={expanded ? 0 : -1} wallet={wallet} /> ))}
) : null} )}
, portal ) ); };