From 5658e4c3b8e4f98db02ed1f65d72c323c9af0726 Mon Sep 17 00:00:00 2001 From: thesn10 <38666407+thesn10@users.noreply.github.com> Date: Wed, 24 Jun 2026 23:33:10 +0200 Subject: [PATCH] feat: add connector wallet components (ConnectButton, WalletModal) --- .../components/connector/connect-button.tsx | 108 +++++ .../connector/shared/hidden-wallet-icons.tsx | 45 ++ .../connector/wallet-dropdown-content.tsx | 85 ++++ app/src/components/connector/wallet-modal.tsx | 435 ++++++++++++++++++ 4 files changed, 673 insertions(+) create mode 100644 app/src/components/connector/connect-button.tsx create mode 100644 app/src/components/connector/shared/hidden-wallet-icons.tsx create mode 100644 app/src/components/connector/wallet-dropdown-content.tsx create mode 100644 app/src/components/connector/wallet-modal.tsx diff --git a/app/src/components/connector/connect-button.tsx b/app/src/components/connector/connect-button.tsx new file mode 100644 index 0000000..47c5f1a --- /dev/null +++ b/app/src/components/connector/connect-button.tsx @@ -0,0 +1,108 @@ +'use client'; + +import { useConnector } from '@solana/connector/react'; +import { useState } from 'react'; +import { WalletModal } from './wallet-modal'; +import { WalletDropdownContent } from './wallet-dropdown-content'; +import { Button } from '@/components/ui-base/button'; +import { Menu, MenuTrigger, MenuPortal, MenuPositioner, MenuPopup } from '@/components/ui-base/menu'; +import { Wallet, ChevronDown } from 'lucide-react'; + +interface ConnectButtonProps { + className?: string; +} + +// Custom Avatar component for Base UI +function Avatar({ + src, + alt, + fallback, + className, +}: { + src?: string; + alt?: string; + fallback?: React.ReactNode; + className?: string; +}) { + const [hasError, setHasError] = useState(false); + + return ( +
+ {src && !hasError ? ( + {alt} setHasError(true)} + /> + ) : ( +
{fallback}
+ )} +
+ ); +} + +// Spinner component +function Spinner({ className }: { className?: string }) { + return ( +
+ ); +} + +export function ConnectButton({ className }: ConnectButtonProps) { + const [isModalOpen, setIsModalOpen] = useState(false); + + const { isConnected, isConnecting, account, connector } = useConnector(); + + if (isConnected && account && connector) { + const shortAddress = `${account.slice(0, 4)}...${account.slice(-4)}`; + const walletIcon = connector.icon || undefined; + + return ( + + + } + className="h-5 w-5" + /> + {shortAddress} + + + + + + + + + + + ); + } + + // Show loading button when connecting (but modal stays rendered) + const buttonContent = isConnecting ? ( + <> + + Connecting... + + ) : ( + Connect Wallet + ); + + return ( + <> + + + + ); +} diff --git a/app/src/components/connector/shared/hidden-wallet-icons.tsx b/app/src/components/connector/shared/hidden-wallet-icons.tsx new file mode 100644 index 0000000..2676067 --- /dev/null +++ b/app/src/components/connector/shared/hidden-wallet-icons.tsx @@ -0,0 +1,45 @@ +/* eslint-disable @next/next/no-img-element */ +import { cn } from '@/lib/utils'; + +interface WalletIconSource { + id: string; + name: string; + icon?: string | null; +} + +interface HiddenWalletIconsProps { + wallets: WalletIconSource[]; + maxIcons?: number; + className?: string; +} + +export function HiddenWalletIcons({ wallets, maxIcons = 4, className }: HiddenWalletIconsProps) { + const previewWallets = wallets.slice(0, maxIcons); + const placeholderCount = Math.max(0, maxIcons - previewWallets.length); + + return ( +