pages/ValuePartnership/ValuePartnership.jsxjavascript
import { lazy, Suspense, useState } from "react";
import { Navigate, Route, Routes, useLocation } from "react-router";
import { ValuePartnershipNav } from "./ValuePartnershipNav";
import { GlobePage } from "./GlobePage/GlobePage";
import "./ValuePartnership.scss";
// code-split: ValueExcellencePage pulls in venn-diagram-svg.js (a 312KB
// inline SVG string) and PartnershipsPage pulls in the `qrcode` library -
// neither is needed by a visitor who only looks at the globe (the default
// tab, see the index route below), so both load on demand instead of
// bloating the initial Value Partnership bundle
const ValueExcellencePage = lazy(() => import("./ValueExcellencePage/ValueExcellencePage"));
const PartnershipsPage = lazy(() => import("./PartnershipsPage/PartnershipsPage"));
/**
* "Value Partnership" presentation app. Unlike ValueAdvisoryServices/
* RadEnablement (linear SlidePage decks), this is a bespoke sub-app with 3
* independent interactive sections (globe, excellence drivers, partnerships/
* slideshow) sharing one nav bar and one content file - so it owns its own
* nested route table instead of going through SlidePage.
*
* GlobePage is deliberately kept outside that route table and always
* rendered (just hidden via CSS - see .value-partnership-page__globe--hidden
* - while another tab is active) instead of being mounted/unmounted per
* route like Excellence/Partnerships are. It owns a full Three.js/WebGL
* scene (react-globe.gl); tearing that down and rebuilding it on every tab
* switch is both slow and a known memory-leak risk class for this kind of
* library on repeated unmount. GlobePage's own `active` prop drives pausing
* its render loop while hidden instead (see InteractiveGlobe.jsx).
*/
export const ValuePartnershipPage = () => {
const { pathname, search } = useLocation();
// GlobePage's GlobeBottomNav (marker prev/next/home) and ValueExcellencePage's
// ValueDriverNav each take over this same bottom-bar slot while a marker/driver
// is selected - actually unmounting ValuePartnershipNav here (rather than just
// painting over it) so there's no chance of it showing through underneath.
const [suppressNav, setSuppressNav] = useState(false);
// PartnershipsPage's "Show key slides"/"Show all presentations" buttons
// render into ValuePartnershipNav itself (its `children` slot) rather than
// in PartnershipsPage's own markup, so they read as part of the shared
// bottom bar instead of floating over the page content.
const [partnershipsActions, setPartnershipsActions] = useState(null);
const isGlobeActive = pathname.endsWith("/globe");
return (
<div className="value-partnership-page page">
<div
className={
isGlobeActive
? "value-partnership-page__globe"
: "value-partnership-page__globe value-partnership-page__globe--hidden"
}>
<GlobePage active={isGlobeActive} onSelectionChange={setSuppressNav} />
</div>
<Suspense fallback={null}>
<Routes>
<Route index element={<Navigate to={`globe${search}`} replace />} />
<Route path="globe" element={null} />
<Route
path="excellence"
element={<ValueExcellencePage onSelectionChange={setSuppressNav} />}
/>
<Route
path="partnerships"
element={<PartnershipsPage onActionsChange={setPartnershipsActions} />}
/>
</Routes>
</Suspense>
{!suppressNav && <ValuePartnershipNav>{partnershipsActions}</ValuePartnershipNav>}
</div>
);
};