Skip to content
This repository was archived by the owner on Mar 17, 2026. It is now read-only.

Commit 8cedb56

Browse files
add gas price dropdown back on desktop & mobile (#2532)
1 parent 2828113 commit 8cedb56

4 files changed

Lines changed: 222 additions & 91 deletions

File tree

apps/web/app/(base-org)/layout.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { Metadata } from 'next';
22
import Sidebar from 'apps/web/src/components/Layout/Navigation/Sidebar';
33
import { Footer } from 'apps/web/src/components/Layout/Footer/Footer';
44
import MobileNav from 'apps/web/src/components/Layout/Navigation/MobileNav';
5+
import { DynamicWrappedGasPriceDropdown } from 'apps/web/src/components/Layout/Navigation/GasPriceDropdown';
56
import AnalyticsProvider from 'apps/web/contexts/Analytics';
67

78
export const metadata: Metadata = {
@@ -29,7 +30,7 @@ export default async function BaseOrgLayout({
2930
children: React.ReactNode;
3031
}) {
3132
return (
32-
<div className="bg-white text-black transition-colors">
33+
<div className="text-black bg-white transition-colors">
3334
<div className="min-w-screen relative mx-auto grid min-h-screen w-full max-w-[1920px] grid-cols-1 selection:bg-blue-5 selection:text-base-blue lg:grid-cols-[13.438rem_1fr]">
3435
<AnalyticsProvider context="sidenav">
3536
<Sidebar />
@@ -40,6 +41,11 @@ export default async function BaseOrgLayout({
4041
</main>
4142
<Footer />
4243
</div>
44+
45+
{/* Gas Price Dropdown - Top Right */}
46+
<div className="hidden fixed top-4 right-4 z-50 lg:block">
47+
<DynamicWrappedGasPriceDropdown />
48+
</div>
4349
</div>
4450
);
4551
}

apps/web/app/(base-org-dark)/layout.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { Metadata } from 'next';
22
import Sidebar from 'apps/web/src/components/Layout/Navigation/Sidebar';
33
import { Footer } from 'apps/web/src/components/Layout/Footer/Footer';
44
import MobileNav from 'apps/web/src/components/Layout/Navigation/MobileNav';
5+
import { DynamicWrappedGasPriceDropdown } from 'apps/web/src/components/Layout/Navigation/GasPriceDropdown';
56
import AnalyticsProvider from 'apps/web/contexts/Analytics';
67

78
export const metadata: Metadata = {
@@ -40,6 +41,11 @@ export default function BaseOrgLayoutDark({
4041
</main>
4142
<Footer />
4243
</div>
44+
45+
{/* Gas Price Dropdown - Top Right */}
46+
<div className="fixed right-4 top-4 z-50 hidden lg:block">
47+
<DynamicWrappedGasPriceDropdown />
48+
</div>
4349
</div>
4450
);
4551
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
'use client';
2+
3+
import Card from 'apps/web/src/components/base-org/Card';
4+
import { Icon } from 'apps/web/src/components/Icon/Icon';
5+
import { base, mainnet } from 'viem/chains';
6+
import { useGasPrice } from 'wagmi';
7+
import { DynamicCryptoProviders } from 'apps/web/app/CryptoProviders.dynamic';
8+
9+
const convertWeiToMwei = (weiValue: bigint): number => {
10+
// 1 mwei = 10^6 wei
11+
const mweiValue = Number(weiValue) / 1_000_000;
12+
return Number(mweiValue.toFixed(2)); // Round to 2 decimal places
13+
};
14+
15+
export function DynamicWrappedGasPriceDropdown() {
16+
return (
17+
<DynamicCryptoProviders>
18+
<GasPriceDropdown />
19+
</DynamicCryptoProviders>
20+
);
21+
}
22+
23+
export function GasPriceDropdown() {
24+
const { data: baseGasPriceInWei } = useGasPrice({
25+
chainId: base.id,
26+
query: {
27+
refetchInterval: 10_000,
28+
},
29+
});
30+
31+
const { data: mainnetGasPriceInWei } = useGasPrice({
32+
chainId: mainnet.id,
33+
query: {
34+
refetchInterval: 10_000,
35+
},
36+
});
37+
38+
return (
39+
<div className="relative group">
40+
<div className="flex cursor-pointer flex-row items-center gap-2 rounded-lg bg-[#FAFAFA] px-3 py-1 transition-all">
41+
<span className="animate-pulse text-palette-positive">
42+
<Icon name="blueCircle" color="currentColor" height="0.5rem" width="0.5rem" />
43+
</span>
44+
<div className="flex gap-1 items-center">
45+
<span className="font-bold text-black font-doto">
46+
{baseGasPriceInWei ? convertWeiToMwei(baseGasPriceInWei) : <>&mdash;</>}
47+
</span>
48+
<span className="text-sm text-base-gray-200">Mwei</span>
49+
</div>
50+
</div>
51+
<div className="hidden absolute right-0 top-full pt-2 lg:group-hover:inline-block">
52+
<Card
53+
innerClassName="p-3 border border-base-black bg-white hover:bg-white font-sans text-[0.875rem]"
54+
radius={9}
55+
>
56+
<ul className="flex flex-col gap-2 whitespace-nowrap">
57+
<li className="flex gap-2">
58+
<strong className="font-normal">{base.name}</strong>
59+
<div className="flex gap-1 items-center">
60+
<span className="font-bold font-doto">
61+
{baseGasPriceInWei ? convertWeiToMwei(baseGasPriceInWei) : <>&mdash;</>}
62+
</span>
63+
<span className="text-base-gray-200">Mwei</span>
64+
</div>
65+
</li>
66+
<li className="flex gap-2">
67+
<strong className="font-normal">{mainnet.name}</strong>
68+
<div className="flex gap-1 items-center">
69+
<span className="font-bold font-doto">
70+
{mainnetGasPriceInWei ? convertWeiToMwei(mainnetGasPriceInWei) : <>&mdash;</>}
71+
</span>
72+
<span className="text-base-gray-200">Mwei</span>
73+
</div>
74+
</li>
75+
</ul>
76+
</Card>
77+
</div>
78+
</div>
79+
);
80+
}
81+
82+
export function DynamicWrappedGasPriceDropdownItem() {
83+
return (
84+
<DynamicCryptoProviders>
85+
<GasPriceDropdownItem />
86+
</DynamicCryptoProviders>
87+
);
88+
}
89+
90+
export function GasPriceDropdownItem() {
91+
const { data: baseGasPriceInWei } = useGasPrice({
92+
chainId: base.id,
93+
query: {
94+
refetchInterval: 10_000,
95+
},
96+
});
97+
98+
return (
99+
<div className="flex cursor-pointer flex-row items-center gap-2 rounded-lg bg-[#FAFAFA] px-3 py-2 transition-all">
100+
<span className="animate-pulse text-palette-positive">
101+
<Icon name="blueCircle" color="currentColor" height="0.5rem" width="0.5rem" />
102+
</span>
103+
<div className="flex gap-1 items-center">
104+
<span className="font-bold text-black font-doto">
105+
{baseGasPriceInWei ? convertWeiToMwei(baseGasPriceInWei) : <>&mdash;</>}
106+
</span>
107+
<span className="text-sm text-base-gray-200">Mwei</span>
108+
</div>
109+
</div>
110+
);
111+
}

apps/web/src/components/Layout/Navigation/MobileNav/index.tsx

Lines changed: 98 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
ButtonSizes,
2020
ButtonVariants,
2121
} from 'apps/web/src/components/Button/Redesign/Button';
22+
import { DynamicWrappedGasPriceDropdownItem } from 'apps/web/src/components/Layout/Navigation/GasPriceDropdown';
2223

2324
const navItemVariants = {
2425
initial: { opacity: 0, x: -20 },
@@ -81,110 +82,117 @@ export default function MobileNav({ className }: { className?: string }) {
8182
className,
8283
)}
8384
>
84-
<Link href="/" className="flex w-fit items-center justify-between">
85+
<Link href="/" className="flex justify-between items-center w-fit">
8586
<MobileLogo className="size-10" />
8687
</Link>
87-
<Dialog.Root open={isMobileMenuOpen} onOpenChange={handleToggleMenu}>
88-
<Dialog.Trigger
89-
aria-label="Toggle menu"
90-
aria-expanded={isMobileMenuOpen}
91-
aria-controls="mobile-menu"
92-
data-focus-visible="false"
93-
className="focus:outline-none"
94-
>
95-
<MenuIcon isOpen={isMobileMenuOpen} />
96-
</Dialog.Trigger>
97-
98-
<Dialog.Portal>
99-
<Dialog.Content
100-
className={classNames(
101-
'fixed inset-y-0 left-0 top-[72px] z-[100] flex h-[calc(100dvh-72px)] w-full flex-col gap-4 outline-none',
102-
isClosingFromResize
103-
? 'transition-none'
104-
: 'transition ease-in-out data-[state=closed]:duration-500 data-[state=open]:duration-500 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left',
105-
)}
88+
<div className="flex gap-2 items-center">
89+
<DynamicWrappedGasPriceDropdownItem />
90+
<Dialog.Root open={isMobileMenuOpen} onOpenChange={handleToggleMenu}>
91+
<Dialog.Trigger
92+
aria-label="Toggle menu"
93+
aria-expanded={isMobileMenuOpen}
94+
aria-controls="mobile-menu"
95+
data-focus-visible="false"
96+
className="focus:outline-none"
10697
>
107-
<Dialog.Title className="sr-only">Mobile Menu</Dialog.Title>
108-
<Dialog.Description className="sr-only">
109-
Mobile site navigation menu.
110-
</Dialog.Description>
111-
112-
<div className="pointer-events-none absolute inset-0 -top-[72px] -z-10 h-[calc(100dvh+72px)] w-full bg-white dark:bg-black" />
113-
114-
<nav className="flex h-full flex-1 flex-col">
115-
{isBrand ? (
116-
<div className="relative flex h-full flex-1 flex-col">
117-
<ul className="ml-4 mt-3 flex flex-col gap-2.5 overflow-y-auto md:mb-10 md:ml-6">
118-
<AnimatePresence>
119-
{isMobileMenuOpen &&
120-
routes.map((route, index) => (
121-
<motion.li
122-
key={route.href}
98+
<MenuIcon isOpen={isMobileMenuOpen} />
99+
</Dialog.Trigger>
100+
101+
<Dialog.Portal>
102+
<Dialog.Content
103+
className={classNames(
104+
'fixed inset-y-0 left-0 top-[72px] z-[100] flex h-[calc(100dvh-72px)] w-full flex-col gap-4 outline-none',
105+
isClosingFromResize
106+
? 'transition-none'
107+
: 'transition ease-in-out data-[state=closed]:duration-500 data-[state=open]:duration-500 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left',
108+
)}
109+
>
110+
<Dialog.Title className="sr-only">Mobile Menu</Dialog.Title>
111+
<Dialog.Description className="sr-only">
112+
Mobile site navigation menu.
113+
</Dialog.Description>
114+
115+
<div className="pointer-events-none absolute inset-0 -top-[72px] -z-10 h-[calc(100dvh+72px)] w-full bg-white dark:bg-black" />
116+
117+
<nav className="flex flex-col flex-1 h-full">
118+
{isBrand ? (
119+
<div className="flex relative flex-col flex-1 h-full">
120+
<ul className="ml-4 mt-3 flex flex-col gap-2.5 overflow-y-auto md:mb-10 md:ml-6">
121+
<AnimatePresence>
122+
{isMobileMenuOpen &&
123+
routes.map((route, index) => (
124+
<motion.li
125+
key={route.href}
126+
variants={navItemVariants}
127+
initial="initial"
128+
animate="animate"
129+
exit="exit"
130+
transition={getNavItemTransition(index)}
131+
>
132+
<Link
133+
target={route.newTab ? '_blank' : '_self'}
134+
rel={route.newTab ? 'noopener noreferrer' : undefined}
135+
href={route.href}
136+
className={classNames(
137+
'flex w-fit items-center justify-between rounded-[4px] p-3 leading-[114%] transition-colors duration-150 hover:bg-base-gray-30',
138+
isLinkActive({
139+
pathname,
140+
href: route.href,
141+
}) && 'bg-base-gray-30 dark:bg-gray-90 dark:active:text-black',
142+
)}
143+
onClick={handleToggleMenu}
144+
>
145+
<Text variant={TextVariant.CTALabelSm}>{route.label}</Text>
146+
</Link>
147+
</motion.li>
148+
))}
149+
</AnimatePresence>
150+
</ul>
151+
152+
<div className="flex flex-col gap-3 px-4 pb-4 mt-auto md:px-6">
153+
<AnimatePresence>
154+
{isMobileMenuOpen && (
155+
<motion.div
123156
variants={navItemVariants}
124-
initial="initial"
125-
animate="animate"
157+
initial="buttonInitial"
158+
animate="buttonAnimate"
126159
exit="exit"
127-
transition={getNavItemTransition(index)}
160+
transition={getButtonTransition(1, routes.length)}
128161
>
129-
<Link
130-
target={route.newTab ? '_blank' : '_self'}
131-
rel={route.newTab ? 'noopener noreferrer' : undefined}
132-
href={route.href}
133-
className={classNames(
134-
'flex w-fit items-center justify-between rounded-[4px] p-3 leading-[114%] transition-colors duration-150 hover:bg-base-gray-30',
135-
isLinkActive({
136-
pathname,
137-
href: route.href,
138-
}) && 'bg-base-gray-30 dark:bg-gray-90 dark:active:text-black',
139-
)}
140-
onClick={handleToggleMenu}
162+
<Button
163+
variant={ButtonVariants.Primary}
164+
size={ButtonSizes.Small}
165+
asChild
141166
>
142-
<Text variant={TextVariant.CTALabelSm}>{route.label}</Text>
143-
</Link>
144-
</motion.li>
145-
))}
146-
</AnimatePresence>
147-
</ul>
148-
149-
<div className="mt-auto flex flex-col gap-3 px-4 pb-4 md:px-6">
150-
<AnimatePresence>
151-
{isMobileMenuOpen && (
152-
<motion.div
153-
variants={navItemVariants}
154-
initial="buttonInitial"
155-
animate="buttonAnimate"
156-
exit="exit"
157-
transition={getButtonTransition(1, routes.length)}
158-
>
159-
<Button variant={ButtonVariants.Primary} size={ButtonSizes.Small} asChild>
160-
<Link
161-
prefetch={false}
162-
download="/base-brand.zip"
163-
href="/base-brand.zip"
164-
className="h-10 w-full"
165-
>
166-
Download Brand Assets
167-
</Link>
168-
</Button>
169-
</motion.div>
170-
)}
171-
</AnimatePresence>
167+
<Link
168+
prefetch={false}
169+
download="/base-brand.zip"
170+
href="/base-brand.zip"
171+
className="w-full h-10"
172+
>
173+
Download Brand Assets
174+
</Link>
175+
</Button>
176+
</motion.div>
177+
)}
178+
</AnimatePresence>
179+
</div>
172180
</div>
173-
</div>
174-
) : (
175-
<BaseNavigation isMobile />
176-
)}
177-
</nav>
178-
</Dialog.Content>
179-
</Dialog.Portal>
180-
</Dialog.Root>
181+
) : (
182+
<BaseNavigation isMobile />
183+
)}
184+
</nav>
185+
</Dialog.Content>
186+
</Dialog.Portal>
187+
</Dialog.Root>
188+
</div>
181189
</header>
182190
);
183191
}
184192

185193
function MenuIcon({ isOpen }: { isOpen: boolean }) {
186194
return (
187-
<div className="relative grid size-10 place-items-center rounded-md bg-base-gray-30 will-change-transform">
195+
<div className="grid relative place-items-center rounded-md size-10 bg-base-gray-30 will-change-transform">
188196
<div
189197
className={classNames(
190198
'ease-[cubic-bezier(0.4,0.2,0,1)] absolute size-5 h-[1px] bg-black transition-all duration-300',

0 commit comments

Comments
 (0)