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'; import { useWalletModal } from './useWalletModal'; import { WalletListItem } from './WalletListItem'; import { WalletSVG } from './WalletSVG'; 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 [installedWallets, otherWallets] = useMemo(() => { const installed: Wallet[] = []; const notDetected: Wallet[] = []; const loadable: Wallet[] = []; for (const wallet of wallets) { if (wallet.readyState === WalletReadyState.NotDetected) { notDetected.push(wallet); } else if (wallet.readyState === WalletReadyState.Loadable) { loadable.push(wallet); } else if (wallet.readyState === WalletReadyState.Installed) { installed.push(wallet); } } return [installed, [...loadable, ...notDetected]]; }, [wallets]); const getStartedWallet = useMemo(() => { return installedWallets.length ? installedWallets[0]! : wallets.find((wallet: { adapter: { name: WalletName } }) => wallet.adapter.name === 'Torus') || wallets.find((wallet: { adapter: { name: WalletName } }) => wallet.adapter.name === 'Phantom') || wallets.find((wallet: { readyState: any }) => wallet.readyState === WalletReadyState.Loadable) || otherWallets[0]!; }, [installedWallets, wallets, otherWallets]); const hideModal = useCallback(() => { setFadeIn(false); setTimeout(() => setVisible(false), 150); }, []); 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'); const firstElement = focusableElements[0]!; 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(
{installedWallets.length ? ( <>

Connect a wallet on Solana to continue

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

You'll need a wallet on Solana to continue

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