Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 13 additions & 61 deletions components/Layout.js
Original file line number Diff line number Diff line change
@@ -1,70 +1,22 @@
import React, { useState } from "react";
import React from "react";
import Balance from "../views/Balance";
import Header from "../views/Header";
// import Banner from "./Banner";
// import Vesting from "../views/Vesting";
import MobileTabBar from "./MobileTabBar";
import ScrollableSectionContainer from "./ScrollableSectionContainer";

export default function Layout({ children }) {
const [leftCol, setLeftCol] = useState(false);
return (
<>
<div className="d-none d-md-block am_app_container">
{/* <Banner /> */}
<Header setLeftCol={setLeftCol} />
<main
className="container-xxl pt-4 h-100"
style={{ maxWidth: "1920px" }}
>
<div className="row px-2 position-relative h-100 pb-2">
<ScrollableSectionContainer
className={`col-12 col-lg-3 d-none d-lg-flex flex-column gap-3 ${
leftCol
? "am_resp_right_ham w-50 top-0 start-0 bottom-0 d-flex align-items-center justify-content-center nav-bg p-4 p-lg-0 gap-5 gap-lg-3"
: ""
}`}
>
<Balance />
{/* <Vesting /> */}
{leftCol && (
<div
className="d-flex d-lg-none position-absolute top-0 end-0 p-3"
role="button"
onClick={() => setLeftCol(false)}
>
<i className="bi bi-x-lg h2"></i>
</div>
)}
</ScrollableSectionContainer>
<div className="col-12 col-lg-9 h-100">{children}</div>
</div>
</main>
</div>
<div
className="d-flex d-md-none overflow-hidden show-on-mobile align-items-center justify-content-center p-3"
style={{ height: "100dvh" }}
>
<div
className="d-flex flex-column rounded-3 bg-gray-700 overflow-hidden"
style={{ width: "min(400px,100%)" }}
>
<div className="d-flex align-items-center justify-content-between nav-bg p-3">
<div className="d-flex gap-2">
<i className="bi bi-circle-fill text-error"></i>
<i className="bi bi-circle-fill text-primary"></i>
<i className="bi bi-circle-fill text-success"></i>
</div>
<i className="bi bi-info-circle text-blue-300"></i>
</div>
<div className="h2 d-flex align-items-center gap-4 p-3">
<i className="bi bi-exclamation-triangle-fill"></i>
<h1 className="m-auto text-primary text-center">
Please open the app on a Tab or Larger device.
</h1>
<i className="bi bi-exclamation-triangle-fill"></i>
</div>
<div className="am_app_container">
<Header />
<main className="container-xxl pt-4 h-100" style={{ maxWidth: "1920px" }}>
<div className="row px-2 position-relative h-100 pb-2">
<ScrollableSectionContainer className="col-12 col-lg-3 d-none d-lg-flex flex-column gap-3">
<Balance />
</ScrollableSectionContainer>
<div className="col-12 col-lg-9 h-100">{children}</div>
</div>
</div>
</>
</main>
<MobileTabBar />
</div>
);
}
128 changes: 128 additions & 0 deletions components/MobileTabBar.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import Link from "next/link";
import { useRouter } from "next/router";
import React, { useState } from "react";
import { NavBarData } from "../data";

// Bottom tab bar for < xl viewports (phone + tablet). The desktop top nav in
// Header.js stays for xl+. Five primary destinations sit on the bar; the rest
// of NavBarData.navs (plus the external links) live in a "More" sheet so every
// section stays reachable on a phone.
const PRIMARY_HREFS = ["/", "/stake", "/vote", "/bridge"];

function isActive(router, href) {
return router.asPath === href || router.asPath.split("?")[0] === href;
}

function TabLink({ item, active }) {
return (
<Link href={item.href}>
<a
target={item.target || "_self"}
aria-current={active ? "page" : undefined}
className={`d-flex flex-column align-items-center justify-content-center gap-1 flex-fill text-white am-tab ${
active ? "active text-primary" : ""
}`}
>
<span className="h4 m-0 icon d-flex">{item.icon}</span>
<span className="caption2">{item.title || "Help"}</span>
</a>
</Link>
);
}

export default function MobileTabBar() {
const router = useRouter();
const [moreOpen, setMoreOpen] = useState(false);

const primary = PRIMARY_HREFS.map((href) =>
NavBarData.navs.find((n) => n.href === href)
).filter(Boolean);
const overflow = [
...NavBarData.navs.filter((n) => !PRIMARY_HREFS.includes(n.href)),
...NavBarData.rightNav,
];
const moreActive = overflow.some(
(n) => !n.target && isActive(router, n.href)
);

return (
<>
{moreOpen && (
<div
className="d-flex d-xl-none position-fixed top-0 start-0 end-0 bottom-0 am-tab-scrim"
style={{ zIndex: 1040 }}
role="button"
aria-label="Close menu"
onClick={() => setMoreOpen(false)}
/>
)}
<div
className={`d-flex d-xl-none flex-column position-fixed start-0 end-0 nav-bg am-tab-sheet ${
moreOpen ? "open" : ""
}`}
style={{ zIndex: 1041 }}
role="dialog"
aria-label="More sections"
aria-hidden={!moreOpen}
>
<div className="d-flex align-items-center justify-content-between p-3 pb-2">
<h2 className="body2 text-white m-0">More</h2>
<button
className="text-white h4 m-0 lh-1"
aria-label="Close"
onClick={() => setMoreOpen(false)}
>
<i className="bi bi-x-lg" />
</button>
</div>
<div className="d-flex flex-column pb-3">
{overflow.map((item, i) => (
<Link href={item.href} key={i}>
<a
target={item.target || "_self"}
onClick={() => setMoreOpen(false)}
className={`d-flex align-items-center gap-3 px-4 py-3 text-white am-tab-row ${
!item.target && isActive(router, item.href)
? "active text-primary"
: ""
}`}
>
<span className="h4 m-0 icon d-flex">{item.icon}</span>
<span className="body2">{item.title || "Help"}</span>
{item.endIcon && (
<span className="ms-auto icon d-flex">{item.endIcon}</span>
)}
</a>
</Link>
))}
</div>
</div>

<nav
className="d-flex d-xl-none position-fixed start-0 end-0 bottom-0 nav-bg am-tab-bar"
style={{ zIndex: 1042 }}
aria-label="Primary"
>
{primary.map((item) => (
<TabLink
key={item.href}
item={item}
active={isActive(router, item.href)}
/>
))}
<button
className={`d-flex flex-column align-items-center justify-content-center gap-1 flex-fill text-white am-tab bg-transparent ${
moreOpen || moreActive ? "active text-primary" : ""
}`}
aria-expanded={moreOpen}
onClick={() => setMoreOpen((v) => !v)}
>
<span className="h4 m-0 icon d-flex">
<i className="bi bi-grid" />
</span>
<span className="caption2">More</span>
</button>
</nav>
</>
);
}
3 changes: 1 addition & 2 deletions components/ScrollableSectionContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import React from "react";
export default function ScrollableSectionContainer({ className, children }) {
return (
<div
className={`h-100 d-flex flex-column gap-2 pb-5 ${className}`}
style={{ overflowY: "auto" }}
className={`am-scroll-section h-100 d-flex flex-column gap-2 pb-5 ${className}`}
>
{children}
<div className="p-2"></div>
Expand Down
85 changes: 85 additions & 0 deletions config/styles/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -1310,3 +1310,88 @@ body::before {
opacity: 1;
}
}

// Mobile bottom tab bar + "More" sheet (< xl). Surface + active accent reuse
// .nav-bg / .text-primary from the theme; only layout/animation live here.
.am-tab-bar {
border-top: 1px solid $white-400;
padding-bottom: env(safe-area-inset-bottom, 0px);
min-height: 56px;
}
.am-tab {
padding: 0.5rem 0.25rem;
text-decoration: none;
background: transparent;
border: 0;
transition: color 150ms ease;
.caption2 {
line-height: 1;
}
&:focus-visible {
outline: 2px solid $primary-main;
outline-offset: -2px;
}
}
.am-tab-scrim {
background: rgba(0, 0, 0, 0.5);
}
.am-tab-sheet {
bottom: calc(56px + env(safe-area-inset-bottom, 0px));
border-top: 1px solid $white-400;
border-top-left-radius: 16px;
border-top-right-radius: 16px;
max-height: 70vh;
overflow-y: auto;
transform: translateY(150%);
transition: transform 220ms ease;
&.open {
transform: translateY(0);
}
}
.am-tab-row {
text-decoration: none;
&:active {
background: rgba(255, 255, 255, 0.06);
}
}
// keep page content clear of the fixed bar on < xl
@media (max-width: 1199.98px) {
.am_app_container {
padding-bottom: calc(56px + env(safe-area-inset-bottom, 0px));
}
}
@media (prefers-reduced-motion: reduce) {
.am-tab-sheet {
transition: none;
}
}

// Desktop uses a fixed-viewport-height shell where each section scrolls on its
// own (.am-scroll-section). On phone/tablet (< lg) that fights natural reading:
// relax the height chain so the PAGE flows and scrolls, and sections size to
// their content and stack.
.am-scroll-section {
overflow-y: auto;
}
@media (max-width: 991.98px) {
.am_app_container .am-scroll-section {
height: auto !important;
overflow-y: visible;
padding-bottom: 1rem;
}
.am_app_container main.h-100,
.am_app_container .row.h-100,
.am_app_container .col-lg-9.h-100,
.am_app_container .col-lg-3 {
height: auto !important;
}
}

// Data tables (validators, proposals) keep their column widths on phone/tablet
// and scroll horizontally inside their existing overflow wrapper, instead of
// squishing columns until content clips.
@media (max-width: 991.98px) {
.am_app_container table.table {
min-width: 620px;
}
}
4 changes: 2 additions & 2 deletions pages/bridge.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export default function Bridge() {
<title>Bridge | MantleWallet</title>
</Head>
<main className="row h-100">
<ScrollableSectionContainer className="col-8">
<ScrollableSectionContainer className="col-12 col-lg-8">
<section className="rounded-4 p-3 bg-gray-800 width-100 d-flex flex-column gap-3 transitionAll">
<nav className="d-flex align-items-center justify-content-between gap-3">
<div className="d-flex gap-3 align-items-center">
Expand All @@ -38,7 +38,7 @@ export default function Bridge() {
</div>
</section>
</ScrollableSectionContainer>
<ScrollableSectionContainer className="col-4 d-flex flex-column gap-3">
<ScrollableSectionContainer className="col-12 col-lg-4 d-flex flex-column gap-3">
<div className="rounded-4 p-3 bg-gray-800 width-100 text-white-300">
<Tooltip
titlePrimary={true}
Expand Down
4 changes: 2 additions & 2 deletions pages/earn.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ export default function Earn() {
</Head>
<h1 className="visually-hidden">Earn</h1>
<section className="row h-100">
<ScrollableSectionContainer className="col-8 h-100">
<ScrollableSectionContainer className="col-12 col-lg-8 h-100">
<Pool690IncentiveProgram />
</ScrollableSectionContainer>
<ScrollableSectionContainer className="col-4 d-flex flex-column gap-3">
<ScrollableSectionContainer className="col-12 col-lg-4 d-flex flex-column gap-3">
<Pool690MetricsPanel />
</ScrollableSectionContainer>
</section>
Expand Down
4 changes: 2 additions & 2 deletions pages/farm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,15 @@ export default function Farm() {
<title>Farm | MantleWallet</title>
</Head>
<section className="row h-100">
<div className="col-8 h-100">
<div className="col-12 col-lg-8 h-100">
<ScrollableSectionContainer className="d-flex h-100">
{/* New UI starts from here */}
<div className="bg-gray-800 rounded-4 p-3 d-flex flex-column gap-3">
{farmPoolJSX}
</div>
</ScrollableSectionContainer>
</div>
<ScrollableSectionContainer className="col-4 d-flex flex-column gap-3 h-90">
<ScrollableSectionContainer className="col-12 col-lg-4 d-flex flex-column gap-3 h-90">
{/* New UI starts from here */}
<div className="bg-gray-800 rounded-4 p-3 d-flex flex-column gap-3 mt-2">
<h2 className="body1 text-primary">Chains</h2>
Expand Down
6 changes: 3 additions & 3 deletions pages/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -404,8 +404,8 @@ export default function Transact() {
<Head>
<title>Transact | MantleWallet</title>
</Head>
<section className="row h-100">
<ScrollableSectionContainer className="col-8">
<section className="row h-100 g-3">
<ScrollableSectionContainer className="col-12 col-lg-8">
<div className="rounded-4 p-3 bg-gray-800 width-100 d-flex flex-column gap-2 transitionAll">
<nav className="d-flex align-items-center justify-content-between gap-3">
<div className="d-flex gap-3 align-items-center">
Expand Down Expand Up @@ -625,7 +625,7 @@ export default function Transact() {
</div>
</div>
</ScrollableSectionContainer>
<ScrollableSectionContainer className="col-4">
<ScrollableSectionContainer className="col-12 col-lg-4">
{status === "Connected" ? (
<ConnectedRecieve displayAddress={displayAddress} />
) : (
Expand Down
4 changes: 2 additions & 2 deletions pages/stake.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export default function Stake() {
<title>Stake | MantleWallet</title>
</Head>
<section className="row h-100">
<div className="col-8 h-100">
<div className="col-12 col-lg-8 h-100">
<div
className="bg-gray-800 p-3 rounded-4 d-flex flex-column gap-2"
style={{ height: "90%" }}
Expand Down Expand Up @@ -410,7 +410,7 @@ export default function Stake() {
</div>
</div>

<ScrollableSectionContainer className="col-4">
<ScrollableSectionContainer className="col-12 col-lg-4">
<StakedToken
delegated={delegated}
setDelegated={setDelegated}
Expand Down
Loading
Loading