Skip to content

Commit

Permalink
Dark theme (BuidlGuidl#93)
Browse files Browse the repository at this point in the history
  • Loading branch information
portdeveloper authored May 13, 2024
1 parent 41a975d commit c1e7ca0
Show file tree
Hide file tree
Showing 17 changed files with 175 additions and 115 deletions.
6 changes: 3 additions & 3 deletions packages/nextjs/components/MiniHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import { RainbowKitCustomConnectButton } from "~~/components/scaffold-eth";

export const MiniHeader = () => {
return (
<div className="sticky lg:static top-0 navbar bg-white border-b border-secondary min-h-0 flex-shrink-0 justify-between z-20 px-0 sm:px-2">
<div className="sticky lg:static top-0 navbar bg-base-200 border-b border-secondary min-h-0 flex-shrink-0 justify-between z-20 px-0 sm:px-2">
<div className="navbar-start w-auto lg:w-1/2">
<label htmlFor="sidebar" className="btn btn-ghost drawer-button sm:hidden ml-2">
<Bars3Icon className="h-1/2" />
</label>
<Link href="/" passHref className="flex items-center gap-2 ml-4 mr-6 shrink-0">
<Link href="/" passHref className="flex items-center gap-2 sm:ml-4 mr-6 shrink-0">
<div className="flex items-center">
<Image alt="Abi Ninja logo" src="/logo.png" width={50} height={50} />
<Image alt="Abi Ninja logo" src="/logo_inv.svg" width={50} height={50} />
<span className="hidden sm:flex ml-2">
<strong>ABI</strong> <span>Ninja</span>
</span>
Expand Down
27 changes: 23 additions & 4 deletions packages/nextjs/components/NetworksDropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useEffect, useState } from "react";
import Image from "next/image";
import { useTheme } from "next-themes";
import Select, { OptionProps, components } from "react-select";
import { getTargetNetworks } from "~~/utils/scaffold-eth";

Expand Down Expand Up @@ -58,6 +59,15 @@ const IconOption = (props: OptionProps<Options>) => (

export const NetworksDropdown = ({ onChange }: { onChange: (options: any) => any }) => {
const [isMobile, setIsMobile] = useState(false);
const { resolvedTheme } = useTheme();

const isDarkMode = resolvedTheme === "dark";

const [mounted, setMounted] = useState(false);

useEffect(() => {
setMounted(true);
}, []);

useEffect(() => {
if (typeof window !== "undefined") {
Expand All @@ -70,6 +80,7 @@ export const NetworksDropdown = ({ onChange }: { onChange: (options: any) => any
}
}, []);

if (!mounted) return null;
return (
<Select
defaultValue={groupedOptions["mainnet"].options[0]}
Expand All @@ -78,18 +89,26 @@ export const NetworksDropdown = ({ onChange }: { onChange: (options: any) => any
onChange={onChange}
components={{ Option: IconOption }}
isSearchable={!isMobile}
className="max-w-xs bg-white relative text-sm w-44"
className="max-w-xs relative text-sm w-44"
theme={theme => ({
...theme,
colors: {
...theme.colors,
primary25: "#efeaff",
primary50: "#c1aeff",
primary: "#551d98",
primary25: isDarkMode ? "#401574" : "#efeaff",
primary50: isDarkMode ? "#551d98" : "#c1aeff",
primary: isDarkMode ? "#BA8DE8" : "#551d98",
neutral0: isDarkMode ? "#130C25" : theme.colors.neutral0,
neutral80: isDarkMode ? "#ffffff" : theme.colors.neutral80,
},
})}
styles={{
menuList: provided => ({ ...provided, maxHeight: 280, overflow: "auto" }),
control: provided => ({ ...provided, borderRadius: 12 }),
indicatorSeparator: provided => ({ ...provided, display: "none" }),
menu: provided => ({
...provided,
border: `1px solid ${isDarkMode ? "#555555" : "#a3a3a3"}`,
}),
}}
/>
);
Expand Down
35 changes: 23 additions & 12 deletions packages/nextjs/components/SwitchTheme.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,42 @@
import { useEffect } from "react";
import { useDarkMode, useIsMounted } from "usehooks-ts";
import { useEffect, useState } from "react";
import { useTheme } from "next-themes";
import { MoonIcon, SunIcon } from "@heroicons/react/24/outline";

export const SwitchTheme = ({ className }: { className?: string }) => {
const { isDarkMode, toggle } = useDarkMode();
const isMounted = useIsMounted();
const { setTheme, resolvedTheme } = useTheme();
const [mounted, setMounted] = useState(false);

const isDarkMode = resolvedTheme === "dark";

const handleToggle = () => {
if (isDarkMode) {
setTheme("light");
return;
}
setTheme("dark");
};

useEffect(() => {
const body = document.body;
body.setAttribute("data-theme", isDarkMode ? "scaffoldEthDark" : "scaffoldEth");
}, [isDarkMode]);
setMounted(true);
}, []);

if (!mounted) return null;

return (
<div className={`flex space-x-2 text-sm ${className}`}>
<div className={`flex space-x-2 h-8 items-center justify-center text-sm ${className}`}>
<input
id="theme-toggle"
type="checkbox"
className="toggle toggle-primary bg-primary"
onChange={toggle}
className="toggle toggle-primary bg-primary hover:bg-primary border-primary"
onChange={handleToggle}
checked={isDarkMode}
/>
{isMounted() && (
{
<label htmlFor="theme-toggle" className={`swap swap-rotate ${!isDarkMode ? "swap-active" : ""}`}>
<SunIcon className="swap-on h-5 w-5" />
<MoonIcon className="swap-off h-5 w-5" />
</label>
)}
}
</div>
);
};
2 changes: 1 addition & 1 deletion packages/nextjs/components/scaffold-eth/Balance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export const Balance = ({ address, className = "" }: BalanceProps) => {
className={`btn btn-sm btn-ghost flex flex-col font-normal items-center hover:bg-transparent ${className}`}
onClick={onToggleBalance}
>
<div className="w-full flex items-center justify-center">
<div className="w-full flex items-center justify-center text-secondary-content">
{isEthBalance ? (
<>
<span>{balance?.toFixed(4)}</span>
Expand Down
14 changes: 7 additions & 7 deletions packages/nextjs/components/scaffold-eth/Contract/ContractUI.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ export const ContractUI = ({ className = "", initialContractData }: ContractUIPr
<input id="sidebar" type="checkbox" className="drawer-toggle" />
<div className="drawer-side h-full z-50 sm:z-10">
<label htmlFor="sidebar" aria-label="close sidebar" className="drawer-overlay"></label>
<ul className="menu p-6 pr-6 pb-3 bg-white h-full justify-between flex-nowrap">
<ul className="menu p-6 pr-6 pb-3 bg-base-200 h-full justify-between flex-nowrap">
<MethodSelector
readMethodsWithInputsAndWriteMethods={readMethodsWithInputsAndWriteMethods}
abi={abi}
Expand All @@ -159,8 +159,8 @@ export const ContractUI = ({ className = "", initialContractData }: ContractUIPr
<div className="col-span-6 grid grid-cols-1 gap-6 laptop:grid-cols-[repeat(13,_minmax(0,_1fr))] px-6 py-10">
<div className="laptop:col-span-8 flex flex-col gap-6">
<div className="z-10">
<div className="bg-white rounded-2xl shadow-xl border flex flex-col mt-10 relative">
<div className="h-[5rem] w-[5.5rem] bg-secondary absolute self-start rounded-[22px] -top-[38px] -left-[1px] -z-10 py-[0.65rem] shadow-lg shadow-base-300">
<div className="bg-base-200 rounded-2xl shadow-xl flex flex-col mt-10 relative">
<div className="h-[5rem] w-[5.5rem] bg-secondary absolute self-start rounded-[22px] -top-[38px] -left-[0px] -z-10 py-[0.65rem] shadow-lg shadow-base-300">
<div className="flex items-center justify-center space-x-2">
<p className="my-0 text-sm font-bold">Read</p>
</div>
Expand All @@ -174,8 +174,8 @@ export const ContractUI = ({ className = "", initialContractData }: ContractUIPr
</div>
</div>
<div className="z-10">
<div className="bg-white rounded-2xl shadow-xl border flex flex-col mt-10 relative">
<div className="h-[5rem] w-[5.5rem] bg-secondary absolute self-start rounded-[22px] -top-[38px] -left-[1px] -z-10 py-[0.65rem] shadow-lg shadow-base-300">
<div className="bg-base-200 rounded-2xl shadow-xl flex flex-col mt-10 relative">
<div className="h-[5rem] w-[5.5rem] bg-secondary absolute self-start rounded-[22px] -top-[38px] -left-[0px] -z-10 py-[0.65rem] shadow-lg shadow-base-300">
<div className="flex items-center justify-center space-x-2">
<p className="my-0 text-sm font-bold">Write</p>
</div>
Expand All @@ -192,7 +192,7 @@ export const ContractUI = ({ className = "", initialContractData }: ContractUIPr
</div>

<div className="laptop:col-span-5 flex flex-col mt-10">
<div className="bg-white border shadow-xl rounded-2xl px-6 mb-6 space-y-1 py-4">
<div className="bg-base-200 shadow-xl rounded-2xl px-6 mb-6 space-y-1 py-4">
<div className="flex">
<div className="flex flex-col gap-1">
<span className="font-bold pb-2">Contract Overview</span>
Expand All @@ -219,7 +219,7 @@ export const ContractUI = ({ className = "", initialContractData }: ContractUIPr
</p>
)}
</div>
<div className="bg-white shadow-xl rounded-2xl px-6 py-4">
<div className="bg-base-200 shadow-xl rounded-2xl px-6 py-4">
<span className="block font-bold pb-3">Contract Data</span>
<ContractVariables
refreshDisplayVariables={refreshDisplayVariables}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,14 @@ export const MethodSelector = ({
<div key={method.uid} className="flex items-center gap-2 w-full pr-4">
<button
className={`btn btn-sm btn-ghost font-normal pr-1 w-full justify-between ${
isMethodSelected(method.uid) ? "bg-purple-100 pointer-events-none" : ""
isMethodSelected(method.uid) ? "bg-neutral pointer-events-none" : ""
}`}
onClick={() => onMethodSelect(method.uid)}
>
{method.name}
{isMethodSelected(method.uid) && (
<button
className="ml-4 text-xs hover:bg-gray-100 rounded-md p-1 pointer-events-auto"
className="ml-4 text-xs hover:bg-base-100 rounded-md p-1 pointer-events-auto"
onClick={() => removeMethod(method.uid)}
>
<XMarkIcon className="h-4 w-4" />
Expand Down Expand Up @@ -95,14 +95,14 @@ export const MethodSelector = ({
<div key={index} className="flex items-center gap-2 w-full pr-4">
<button
className={`btn btn-sm btn-ghost font-normal pr-1 w-full justify-between ${
isMethodSelected(method.uid) ? "bg-purple-100 pointer-events-none" : ""
isMethodSelected(method.uid) ? "bg-neutral pointer-events-none" : ""
}`}
onClick={() => onMethodSelect(method.uid)}
>
{method.name}
{isMethodSelected(method.uid) && (
<button
className="ml-4 text-xs hover:bg-gray-100 rounded-md p-1 pointer-events-auto"
className="ml-4 text-xs hover:bg-base-100 rounded-md p-1 pointer-events-auto"
onClick={() => removeMethod(method.uid)}
>
<XMarkIcon className="h-4 w-4" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const InputBase = <T extends { toString: () => string } | undefined = str
);

return (
<div className={`flex bg-slate-100 rounded-lg text-accent ${modifier}`}>
<div className={`flex bg-neutral rounded-lg text-accent ${modifier}`}>
{prefix}
<input
className="input input-ghost focus:outline-none focus:bg-transparent focus:text-accent h-10 px-4 border w-full placeholder:text-gray-400 text-accent"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,15 @@ export const AddressInfoDropdown = ({
<>
<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">
<BlockieAvatar address={address} size={30} ensImage={ensAvatar} />
<span className="ml-2 mr-1">{displayName}</span>
<ChevronDownIcon className="h-6 w-4 ml-2 sm:ml-0" />
<div className="hidden sm:block">
<BlockieAvatar address={address} size={30} ensImage={ensAvatar} />
</div>
<span className="ml-2 mr-1 ">{displayName}</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 bg-white border shadow-xl rounded-2xl text-black gap-1"
className="dropdown-content menu z-[2] p-2 mt-2 bg-base-200 shadow-xl rounded-2xl text-secondary-content gap-1"
>
<NetworkOptions hidden={!selectingNetwork} />
<li className={selectingNetwork ? "hidden" : ""}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ type AddressQRCodeModalProps = {
export const AddressQRCodeModal = ({ address, modalId }: AddressQRCodeModalProps) => {
return (
<>
<div className="text-black">
<div className="text-secondary-content">
<input type="checkbox" id={`${modalId}`} className="modal-toggle" />
<label htmlFor={`${modalId}`} className="modal cursor-pointer">
<label className="modal-box relative">
Expand All @@ -22,7 +22,12 @@ export const AddressQRCodeModal = ({ address, modalId }: AddressQRCodeModalProps
<div className="space-y-3 py-6">
<div className="flex space-x-4 flex-col items-center gap-6">
<QRCodeSVG value={address} size={256} />
<Address address={address} format="long" disableAddressLink />
<div className="hidden md:block">
<Address address={address} format="long" disableAddressLink />
</div>
<div className="block md:hidden">
<Address address={address} format="short" disableAddressLink />
</div>
</div>
</div>
</label>
Expand Down
1 change: 1 addition & 0 deletions packages/nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"daisyui": "^4.4.19",
"next": "13.3.4",
"next-plausible": "^3.12.0",
"next-themes": "^0.3.0",
"nextjs-progressbar": "^0.0.16",
"qrcode.react": "^3.1.0",
"react": "^18.2.0",
Expand Down
4 changes: 3 additions & 1 deletion packages/nextjs/pages/[contractAddress]/[network].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { usePublicClient } from "wagmi";
import { ExclamationTriangleIcon } from "@heroicons/react/24/outline";
import { MetaHeader } from "~~/components/MetaHeader";
import { MiniHeader } from "~~/components/MiniHeader";
import { SwitchTheme } from "~~/components/SwitchTheme";
import { ContractUI } from "~~/components/scaffold-eth";
import { useAbiNinjaState } from "~~/services/store/store";
import { fetchContractABIFromAnyABI, fetchContractABIFromEtherscan } from "~~/utils/abi";
Expand Down Expand Up @@ -152,7 +153,7 @@ const ContractDetailPage = ({ addressFromUrl, chainIdFromUrl }: ServerSideProps)
) : contractData.abi?.length > 0 ? (
<ContractUI key={contractName} initialContractData={contractData} />
) : (
<div className="bg-white border shadow-xl rounded-2xl px-6 lg:px-8 m-4">
<div className="bg-base-200 border shadow-xl rounded-2xl px-6 lg:px-8 m-4">
<ExclamationTriangleIcon className="text-red-500 mt-4 h-8 w-8" />
<h2 className="text-2xl pt-2 flex items-end">{error}</h2>
<p className="break-all">
Expand All @@ -168,6 +169,7 @@ const ContractDetailPage = ({ addressFromUrl, chainIdFromUrl }: ServerSideProps)
)}
</div>
</div>
<SwitchTheme className="fixed bottom-3 right-6 z-50" />
</>
);
};
Expand Down
38 changes: 19 additions & 19 deletions packages/nextjs/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import type { AppProps } from "next/app";
import { RainbowKitProvider, darkTheme, lightTheme } from "@rainbow-me/rainbowkit";
import "@rainbow-me/rainbowkit/styles.css";
import PlausibleProvider from "next-plausible";
import { ThemeProvider, useTheme } from "next-themes";
import NextNProgress from "nextjs-progressbar";
import { Toaster } from "react-hot-toast";
import { useDarkMode } from "usehooks-ts";
import { WagmiConfig } from "wagmi";
import { BlockieAvatar } from "~~/components/scaffold-eth";
import { useNativeCurrencyPrice } from "~~/hooks/scaffold-eth";
Expand All @@ -17,6 +17,13 @@ import "~~/styles/globals.css";
const ScaffoldEthApp = ({ Component, pageProps }: AppProps) => {
const price = useNativeCurrencyPrice();
const setNativeCurrencyPrice = useGlobalState(state => state.setNativeCurrencyPrice);
const { resolvedTheme } = useTheme();
const isDarkMode = resolvedTheme === "dark";
const [mounted, setMounted] = useState(false);

useEffect(() => {
setMounted(true);
}, []);

useEffect(() => {
if (price > 0) {
Expand All @@ -25,37 +32,30 @@ const ScaffoldEthApp = ({ Component, pageProps }: AppProps) => {
}, [setNativeCurrencyPrice, price]);

return (
<>
<RainbowKitProvider
chains={appChains.chains}
avatar={BlockieAvatar}
theme={mounted ? (isDarkMode ? darkTheme() : lightTheme()) : lightTheme()}
>
<div className="flex min-h-screen flex-col">
<main className="relative flex flex-1 flex-col">
<Component {...pageProps} />
</main>
</div>
<Toaster />
</>
</RainbowKitProvider>
);
};

const ScaffoldEthAppWithProviders = (props: AppProps) => {
// This variable is required for initial client side rendering of correct theme for RainbowKit
const [isDarkTheme, setIsDarkTheme] = useState(true);
const { isDarkMode } = useDarkMode();
useEffect(() => {
setIsDarkTheme(isDarkMode);
}, [isDarkMode]);

return (
<PlausibleProvider domain="abi.ninja">
<WagmiConfig config={wagmiConfig}>
<NextNProgress />
<RainbowKitProvider
chains={appChains.chains}
avatar={BlockieAvatar}
theme={isDarkTheme ? darkTheme() : lightTheme()}
>
<ThemeProvider>
<WagmiConfig config={wagmiConfig}>
<NextNProgress />
<ScaffoldEthApp {...props} />
</RainbowKitProvider>
</WagmiConfig>
</WagmiConfig>
</ThemeProvider>
</PlausibleProvider>
);
};
Expand Down
Loading

0 comments on commit c1e7ca0

Please sign in to comment.