diff --git a/src/App.tsx b/src/App.tsx index 10bad3f3b..6806aef6e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -213,6 +213,11 @@ const Routing = () => { const renderPageComponent = (PageComponent: PageComponent) => { if (!PageComponent.route.isPageEnabled) return null + const { parentPathBase: parentPathBaseFromRoute } = PageComponent.route + const parentPathBase = parentPathBaseFromRoute || "" + const updatedParentPathBase = PageComponent.route.path + ? `${parentPathBase}/${PageComponent.route.path}` + : parentPathBase return ( @@ -224,9 +229,17 @@ const renderPageComponent = (PageComponent: PageComponent) => { )} } + element={ + + } > - {PageComponent.route.pages?.map(renderPageComponent)} + {PageComponent.route.pages?.map((page) => { + page.route.parentPathBase = updatedParentPathBase + return renderPageComponent(page) + })} ) diff --git a/src/components/SubNavigationPills/index.tsx b/src/components/SubNavigationPills/index.tsx index b8117efbe..8621ed21f 100644 --- a/src/components/SubNavigationPills/index.tsx +++ b/src/components/SubNavigationPills/index.tsx @@ -7,16 +7,29 @@ import { Stack, useColorModeValue, } from "@threshold-network/components" -import { useMatch, useResolvedPath } from "react-router-dom" +import { matchPath, resolvePath, useLocation } from "react-router-dom" import { RouteProps } from "../../types" import Link from "../Link" -interface Props { +interface SubNavigationPillsProps { links: RouteProps[] } -const SubNavigationPills: FC = ({ links }) => { +interface PathMatchResult { + index: number + path: string + pathOverride?: string + resolvedPath: string + match: any +} +interface NavPill extends RouteProps { + isActive?: boolean +} + +const SubNavigationPills: FC = ({ links }) => { + const { pathname } = useLocation() const linksWithTitle = links.filter((link) => !!link.title) + const activePillIndex = getActivePillIndex(linksWithTitle, pathname) const wrapperBorderColor = useColorModeValue("gray.100", "gray.700") return ( @@ -38,16 +51,17 @@ const SubNavigationPills: FC = ({ links }) => { height="28px" as="ul" > - {linksWithTitle.map(renderPill)} + {linksWithTitle.map((linkWithTitle, index) => { + const isActive = index === activePillIndex + return renderPill(linkWithTitle, isActive) + })} ) } -const NavPill: FC = ({ path, pathOverride, title }) => { - const resolved = useResolvedPath(pathOverride || path) - const isActive = useMatch({ path: resolved.pathname, end: true }) +const NavPill: FC = ({ path, title, isActive = false }) => { const activeColor = useColorModeValue("brand.500", "gray.100") const underlineColor = useColorModeValue("brand.500", "white") @@ -81,6 +95,70 @@ const NavPill: FC = ({ path, pathOverride, title }) => { ) } -const renderPill = (pill: RouteProps) => +const renderPill = (pill: RouteProps, isActive = false) => ( + +) + +const getPathMatches = (pills: RouteProps[], locationPathname: string) => { + const pathMatches: PathMatchResult[] = [] + for (let i = 0; i < pills.length; i++) { + const { path, pathOverride, parentPathBase } = pills[i] + // This is a workaround for preview links. We have to remove the branch name + // from the pathname so first we check if `parentPathBase` is not an + // undefined. If it is then it means that this is the main page without any + // additional paths so we can just use an empty string here. If it's not + // undefined then we have to remove all the characters (if there are any) + // that are before the occurrence of the `parentPathBase`. If this is a + // preview then those removed characters should be a name of the branch. In + // other cases there should not be any character before the occurrence of + // the `parentPathBase` so nothing happens. + const currentPathname = parentPathBase + ? locationPathname.includes(parentPathBase) && + locationPathname.indexOf(parentPathBase) !== 0 + ? locationPathname.substring( + locationPathname.indexOf(parentPathBase), + locationPathname.length + ) + : locationPathname + : "" + const resolved = resolvePath( + pathOverride + ? `${parentPathBase}/${pathOverride}` + : `${parentPathBase}/${path}` + ) + const match = matchPath( + { path: resolved.pathname, end: true }, + currentPathname + ) + pathMatches.push({ + index: i, + path, + pathOverride, + resolvedPath: resolved.pathname, + match, + }) + } + return pathMatches +} + +const getActivePillIndex = (pills: RouteProps[], locationPathname: string) => { + const pathMatches = getPathMatches(pills, locationPathname) + const matchedPaths = pathMatches.filter((_) => { + return !!_.match + }) + if (matchedPaths.length === 0) return undefined + if (matchedPaths.length === 1) return matchedPaths[0].index + + const matchedElementWithLongestPathnameBase = matchedPaths.reduce( + (maxElement, currentElement) => { + return currentElement.match.pathnameBase.length > + maxElement.match.pathnameBase.length + ? currentElement + : maxElement + } + ) + + return matchedElementWithLongestPathnameBase.index +} export default SubNavigationPills diff --git a/src/pages/Staking/index.tsx b/src/pages/Staking/index.tsx index 4e0bf489a..2d2d58793 100644 --- a/src/pages/Staking/index.tsx +++ b/src/pages/Staking/index.tsx @@ -212,6 +212,7 @@ Auth.route = { StakingPage.route = { path: "", index: false, + pathOverride: "*", title: "Staking", isPageEnabled: true, } diff --git a/src/pages/tBTC/Bridge/index.tsx b/src/pages/tBTC/Bridge/index.tsx index 6b1614358..cfe2ba51b 100644 --- a/src/pages/tBTC/Bridge/index.tsx +++ b/src/pages/tBTC/Bridge/index.tsx @@ -1,5 +1,5 @@ import { useEffect } from "react" -import { Grid, Box, Skeleton, Stack } from "@threshold-network/components" +import { Grid, Box } from "@threshold-network/components" import { PageComponent } from "../../../types" import { TbtcBalanceCard } from "./TbtcBalanceCard" import { MintUnmintNav } from "./MintUnmintNav" @@ -70,7 +70,7 @@ const TBTCBridge: PageComponent = (props) => { TBTCBridge.route = { path: "", index: false, - pathOverride: "/tBTC/*", + pathOverride: "*", pages: [MintPage, UnmintPage], title: "Bridge", isPageEnabled: true, diff --git a/src/types/page.ts b/src/types/page.ts index 00cbc8458..c549758d8 100644 --- a/src/types/page.ts +++ b/src/types/page.ts @@ -8,6 +8,10 @@ export type RouteProps = { pages?: PageComponent[] hideFromMenu?: boolean isPageEnabled: boolean + // Paths combined from all Route parents of the current Route + parentPathBase?: string } -export type PageComponent = FC & { route: RouteProps } +export type PageComponent = FC & { + route: RouteProps +}