Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shadcn rainbowkit button + dropdown #937

Merged
merged 12 commits into from
Sep 19, 2024
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useRef, useState } from "react";
import { useState } from "react";
import { AddressQRCodeModal } from "./AddressQRCodeModal";
import { NetworkOptions } from "./NetworkOptions";
import CopyToClipboard from "react-copy-to-clipboard";
import { getAddress } from "viem";
Expand All @@ -14,7 +15,13 @@ import {
QrCodeIcon,
} from "@heroicons/react/24/outline";
import { BlockieAvatar, isENS } from "~~/components/scaffold-eth";
import { useOutsideClick } from "~~/hooks/scaffold-eth";
import { Button } from "~~/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "~~/components/ui/dropdown-menu";
import { getTargetNetworks } from "~~/utils/scaffold-eth";

const allowedNetworks = getTargetNetworks();
Expand All @@ -34,104 +41,112 @@ export const AddressInfoDropdown = ({
}: AddressInfoDropdownProps) => {
const { disconnect } = useDisconnect();
const checkSumAddress = getAddress(address);
const [open, setOpen] = useState(false);

const [addressCopied, setAddressCopied] = useState(false);

const [selectingNetwork, setSelectingNetwork] = useState(false);
const dropdownRef = useRef<HTMLDetailsElement>(null);
const closeDropdown = () => {
setSelectingNetwork(false);
dropdownRef.current?.removeAttribute("open");
setOpen(false);
};
useOutsideClick(dropdownRef, closeDropdown);

return (
<>
<details ref={dropdownRef} className="dropdown dropdown-end leading-3">
<summary tabIndex={0} className="btn btn-secondary btn-sm pl-0 pr-2 shadow-md dropdown-toggle gap-0 !h-auto">
<DropdownMenu open={open} onOpenChange={open ? undefined : setOpen}>
<DropdownMenuTrigger asChild onClick={() => setOpen(open => !open)}>
<Button size="sm" variant="secondary">
<BlockieAvatar address={checkSumAddress} size={30} ensImage={ensAvatar} />
<span className="ml-2 mr-1">
{isENS(displayName) ? displayName : checkSumAddress?.slice(0, 6) + "..." + checkSumAddress?.slice(-4)}
</span>
<ChevronDownIcon className="h-6 w-4 ml-2 sm:ml-0" />
</summary>
<ul
tabIndex={0}
className="dropdown-content menu z-[2] p-2 mt-2 shadow-center shadow-accent bg-base-200 rounded-box gap-1"
>
<NetworkOptions hidden={!selectingNetwork} />
<li className={selectingNetwork ? "hidden" : ""}>
{addressCopied ? (
<div className="btn-sm !rounded-xl flex gap-3 py-3">
<CheckCircleIcon
className="text-xl font-normal h-6 w-4 cursor-pointer ml-2 sm:ml-0"
aria-hidden="true"
/>
<span className=" whitespace-nowrap">Copy address</span>
</div>
) : (
<CopyToClipboard
text={checkSumAddress}
onCopy={() => {
setAddressCopied(true);
setTimeout(() => {
setAddressCopied(false);
}, 800);
}}
>
<div className="btn-sm !rounded-xl flex gap-3 py-3">
<DocumentDuplicateIcon
className="text-xl font-normal h-6 w-4 cursor-pointer ml-2 sm:ml-0"
aria-hidden="true"
/>
<span className=" whitespace-nowrap">Copy address</span>
</div>
</CopyToClipboard>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
align="end"
onInteractOutside={closeDropdown}
onEscapeKeyDown={closeDropdown}
className={open ? "" : "!pointer-events-none"}
>
<NetworkOptions hidden={!selectingNetwork} />
{!selectingNetwork && (
<>
<DropdownMenuItem asChild>
<Button variant="ghost" size="sm" className="w-full justify-start">
{addressCopied ? (
<div className="flex items-center">
<CheckCircleIcon
className="text-xl font-normal h-6 w-4 cursor-pointer mr-2 ml-2 sm:ml-0"
aria-hidden="true"
/>
<span className="whitespace-nowrap">Copied!</span>
</div>
) : (
<CopyToClipboard
text={checkSumAddress}
onCopy={() => {
setAddressCopied(true);
setTimeout(() => {
setAddressCopied(false);
}, 800);
}}
>
<div className="flex items-center w-full">
<DocumentDuplicateIcon className="text-xl font-normal h-6 w-4 mr-2" aria-hidden="true" />
<span className="whitespace-nowrap">Copy address</span>
</div>
</CopyToClipboard>
)}
</Button>
</DropdownMenuItem>
<AddressQRCodeModal address={address}>
<DropdownMenuItem asChild>
<Button variant="ghost" size="sm" className="w-full">
<div className="flex items-center w-full">
<QrCodeIcon className="mr-2 h-4 w-4" />
<span>View QR Code</span>
</div>
</Button>
</DropdownMenuItem>
</AddressQRCodeModal>
<DropdownMenuItem asChild>
<Button variant="ghost" size="sm" className="w-full" asChild>
<a target="_blank" href={blockExplorerAddressLink} rel="noopener noreferrer">
<ArrowTopRightOnSquareIcon className="mr-2 h-4 w-4" />
View on Block Explorer
</a>
</Button>
</DropdownMenuItem>
{allowedNetworks.length > 1 && (
<DropdownMenuItem asChild>
<Button
variant="ghost"
size="sm"
className="w-full justify-start"
onClick={() => setSelectingNetwork(true)}
>
<ArrowsRightLeftIcon className="mr-2 h-4 w-4" />
<span>Switch Network</span>
</Button>
</DropdownMenuItem>
)}
</li>
<li className={selectingNetwork ? "hidden" : ""}>
<label htmlFor="qrcode-modal" className="btn-sm !rounded-xl flex gap-3 py-3">
<QrCodeIcon className="h-6 w-4 ml-2 sm:ml-0" />
<span className="whitespace-nowrap">View QR Code</span>
</label>
</li>
<li className={selectingNetwork ? "hidden" : ""}>
<button className="menu-item btn-sm !rounded-xl flex gap-3 py-3" type="button">
<ArrowTopRightOnSquareIcon className="h-6 w-4 ml-2 sm:ml-0" />
<a
target="_blank"
href={blockExplorerAddressLink}
rel="noopener noreferrer"
className="whitespace-nowrap"
>
View on Block Explorer
</a>
</button>
</li>
{allowedNetworks.length > 1 ? (
<li className={selectingNetwork ? "hidden" : ""}>
<button
className="btn-sm !rounded-xl flex gap-3 py-3"
type="button"
<DropdownMenuItem asChild>
<Button
variant="ghost"
size="sm"
className="w-full justify-start"
onClick={() => {
setSelectingNetwork(true);
disconnect();
closeDropdown();
}}
>
<ArrowsRightLeftIcon className="h-6 w-4 ml-2 sm:ml-0" /> <span>Switch Network</span>
</button>
</li>
) : null}
<li className={selectingNetwork ? "hidden" : ""}>
<button
className="menu-item text-error btn-sm !rounded-xl flex gap-3 py-3"
type="button"
onClick={() => disconnect()}
>
<ArrowLeftOnRectangleIcon className="h-6 w-4 ml-2 sm:ml-0" /> <span>Disconnect</span>
</button>
</li>
</ul>
</details>
</>
<ArrowLeftOnRectangleIcon className="mr-2 h-4 w-4" />
<span>Disconnect</span>
</Button>
</DropdownMenuItem>
</>
)}
</DropdownMenuContent>
</DropdownMenu>
);
};
Original file line number Diff line number Diff line change
@@ -1,33 +1,26 @@
import { QRCodeSVG } from "qrcode.react";
import { Address as AddressType } from "viem";
import { Address } from "~~/components/scaffold-eth";
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "~~/components/ui/dialog";

type AddressQRCodeModalProps = {
address: AddressType;
modalId: string;
children: React.ReactNode;
};

export const AddressQRCodeModal = ({ address, modalId }: AddressQRCodeModalProps) => {
export const AddressQRCodeModal = ({ address, children }: AddressQRCodeModalProps) => {
return (
<>
<div>
<input type="checkbox" id={`${modalId}`} className="modal-toggle" />
<label htmlFor={`${modalId}`} className="modal cursor-pointer">
<label className="modal-box relative">
{/* dummy input to capture event onclick on modal box */}
<input className="h-0 w-0 absolute top-0 left-0" />
<label htmlFor={`${modalId}`} className="btn btn-ghost btn-sm btn-circle absolute right-3 top-3">
</label>
<div className="space-y-3 py-6">
<div className="flex flex-col items-center gap-6">
<QRCodeSVG value={address} size={256} />
<Address address={address} format="long" disableAddressLink />
</div>
</div>
</label>
</label>
</div>
</>
<Dialog>
<DialogTrigger asChild>{children}</DialogTrigger>
<DialogContent onOpenAutoFocus={e => e.preventDefault()}>
<DialogHeader>
<DialogTitle>Address QR Code</DialogTitle>
</DialogHeader>
<div className="flex flex-col items-center gap-6 py-6 break-all">
<QRCodeSVG value={address} size={256} />
<Address address={address} format="long" disableAddressLink />
</div>
</DialogContent>
</Dialog>
);
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { useTheme } from "next-themes";
import { useAccount, useSwitchChain } from "wagmi";
import { ArrowsRightLeftIcon } from "@heroicons/react/24/solid";
import { Button } from "~~/components/ui/button";
import { DropdownMenuItem } from "~~/components/ui/dropdown-menu";
import { getNetworkColor } from "~~/hooks/scaffold-eth";
import { getTargetNetworks } from "~~/utils/scaffold-eth";

Expand All @@ -21,15 +23,16 @@ export const NetworkOptions = ({ hidden = false }: NetworkOptionsProps) => {
{allowedNetworks
.filter(allowedNetwork => allowedNetwork.id !== chain?.id)
.map(allowedNetwork => (
<li key={allowedNetwork.id} className={hidden ? "hidden" : ""}>
<button
className="menu-item btn-sm !rounded-xl flex gap-3 py-3 whitespace-nowrap"
type="button"
<DropdownMenuItem asChild key={allowedNetwork.id} className={hidden ? "hidden" : ""}>
<Button
variant="ghost"
size="sm"
className="w-full justify-start"
onClick={() => {
switchChain?.({ chainId: allowedNetwork.id });
}}
>
<ArrowsRightLeftIcon className="h-6 w-4 ml-2 sm:ml-0" />
<ArrowsRightLeftIcon className="h-4 w-4 mr-2" />
<span>
Switch to{" "}
<span
Expand All @@ -40,8 +43,8 @@ export const NetworkOptions = ({ hidden = false }: NetworkOptionsProps) => {
{allowedNetwork.name}
</span>
</span>
</button>
</li>
</Button>
</DropdownMenuItem>
))}
</>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,35 @@
import { NetworkOptions } from "./NetworkOptions";
import { ChevronDown } from "lucide-react";
import { useDisconnect } from "wagmi";
import { ArrowLeftOnRectangleIcon, ChevronDownIcon } from "@heroicons/react/24/outline";
import { ArrowLeftOnRectangleIcon } from "@heroicons/react/24/outline";
import { Button } from "~~/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "~~/components/ui/dropdown-menu";

export const WrongNetworkDropdown = () => {
const { disconnect } = useDisconnect();

return (
<div className="dropdown dropdown-end mr-2">
<label tabIndex={0} className="btn btn-error btn-sm dropdown-toggle gap-1">
<span>Wrong network</span>
<ChevronDownIcon className="h-6 w-4 ml-2 sm:ml-0" />
</label>
<ul
tabIndex={0}
className="dropdown-content menu p-2 mt-1 shadow-center shadow-accent bg-base-200 rounded-box gap-1"
>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="destructive" size="sm">
Wrong network
<ChevronDown className="h-4 w-4 ml-2" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<NetworkOptions />
<li>
<button
className="menu-item text-error btn-sm !rounded-xl flex gap-3 py-3"
type="button"
onClick={() => disconnect()}
>
<ArrowLeftOnRectangleIcon className="h-6 w-4 ml-2 sm:ml-0" />
<DropdownMenuItem asChild>
<Button variant="ghost" size="sm" className="w-full justify-start" onClick={() => disconnect()}>
<ArrowLeftOnRectangleIcon className="mr-2 h-4 w-4" />
<span>Disconnect</span>
</button>
</li>
</ul>
</div>
</Button>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { AddressQRCodeModal } from "./AddressQRCodeModal";
import { WrongNetworkDropdown } from "./WrongNetworkDropdown";
import { ConnectButton } from "@rainbow-me/rainbowkit";
import { Address } from "viem";
import { Button } from "~~/components/ui/button";
import { useNetworkColor } from "~~/hooks/scaffold-eth";
import { useTargetNetwork } from "~~/hooks/scaffold-eth/useTargetNetwork";
import { getBlockExplorerAddressLink } from "~~/utils/scaffold-eth";
Expand All @@ -31,9 +32,9 @@ export const RainbowKitCustomConnectButton = () => {
{(() => {
if (!connected) {
return (
<button className="btn btn-primary btn-sm" onClick={openConnectModal} type="button">
<Button variant="default" size="sm" onClick={openConnectModal}>
Connect Wallet
</button>
</Button>
);
}

Expand All @@ -55,7 +56,7 @@ export const RainbowKitCustomConnectButton = () => {
ensAvatar={account.ensAvatar}
blockExplorerAddressLink={blockExplorerAddressLink}
/>
<AddressQRCodeModal address={account.address as Address} modalId="qrcode-modal" />
<AddressQRCodeModal address={account.address as Address}>QR</AddressQRCodeModal>
</>
);
})()}
Expand Down
Loading