mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-07-30 07:44:43 -05:00
Detect active nav item via route handle instead of path matching
This commit is contained in:
parent
85fda204c0
commit
3ce7c00d5b
|
|
@ -1,7 +1,8 @@
|
|||
import { Link, useLocation } from "@remix-run/react";
|
||||
import { Link, useMatches } from "@remix-run/react";
|
||||
import * as React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import type { RootLoaderData } from "~/root";
|
||||
import { type SendouRouteHandle } from "~/utils/remix";
|
||||
import { LOGO_PATH, navIconUrl } from "~/utils/urls";
|
||||
import { Image } from "../Image";
|
||||
import { ColorModeToggle } from "./ColorModeToggle";
|
||||
|
|
@ -12,6 +13,25 @@ import { Menu } from "./Menu";
|
|||
import navItems from "./nav-items.json";
|
||||
import { UserItem } from "./UserItem";
|
||||
|
||||
function useActiveNavItem() {
|
||||
const matches = useMatches();
|
||||
|
||||
return React.useMemo(() => {
|
||||
let activeItem: { name: string; url: string } | undefined = undefined;
|
||||
|
||||
for (const match of matches.reverse()) {
|
||||
const handle = match.handle as SendouRouteHandle | undefined;
|
||||
|
||||
if (handle?.navItemName) {
|
||||
activeItem = navItems.find(({ name }) => name === handle.navItemName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return activeItem;
|
||||
}, [matches]);
|
||||
}
|
||||
|
||||
export const Layout = React.memo(function Layout({
|
||||
children,
|
||||
patrons,
|
||||
|
|
@ -22,12 +42,8 @@ export const Layout = React.memo(function Layout({
|
|||
isCatchBoundary?: boolean;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const location = useLocation();
|
||||
const [menuOpen, setMenuOpen] = React.useState(false);
|
||||
|
||||
const currentPagesNavItem = navItems.find((navItem) =>
|
||||
location.pathname.includes(navItem.name)
|
||||
);
|
||||
const activeNavItem = useActiveNavItem();
|
||||
|
||||
return (
|
||||
<div className="layout__container">
|
||||
|
|
@ -51,15 +67,15 @@ export const Layout = React.memo(function Layout({
|
|||
</div>
|
||||
</header>
|
||||
<Menu expanded={menuOpen} closeMenu={() => setMenuOpen(false)} />
|
||||
{currentPagesNavItem && (
|
||||
{activeNavItem && (
|
||||
<div className="layout__page-title-header">
|
||||
<Image
|
||||
path={navIconUrl(currentPagesNavItem.name)}
|
||||
path={navIconUrl(activeNavItem.name)}
|
||||
width={28}
|
||||
height={28}
|
||||
alt=""
|
||||
/>
|
||||
{t(`pages.${currentPagesNavItem.name}` as any)}
|
||||
{t(`pages.${activeNavItem.name}` as any)}
|
||||
</div>
|
||||
)}
|
||||
{children}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,11 @@ import { Main } from "~/components/Main";
|
|||
import { requireUser } from "~/modules/auth";
|
||||
import { getUser, isImpersonating } from "~/modules/auth/user.server";
|
||||
import { canPerformAdminActions } from "~/permissions";
|
||||
import { parseRequestFormData, validate } from "~/utils/remix";
|
||||
import {
|
||||
parseRequestFormData,
|
||||
type SendouRouteHandle,
|
||||
validate,
|
||||
} from "~/utils/remix";
|
||||
import { makeTitle } from "~/utils/strings";
|
||||
import { impersonateUrl, SEED_URL, STOP_IMPERSONATING_URL } from "~/utils/urls";
|
||||
import { db } from "~/db";
|
||||
|
|
@ -69,6 +73,10 @@ export const loader: LoaderFunction = async ({ request }) => {
|
|||
});
|
||||
};
|
||||
|
||||
export const handle: SendouRouteHandle = {
|
||||
navItemName: "admin",
|
||||
};
|
||||
|
||||
export default function AdminPage() {
|
||||
return (
|
||||
<Main className="stack lg">
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ export const links: LinksFunction = () => {
|
|||
|
||||
export const handle: SendouRouteHandle = {
|
||||
i18n: ["weapons", "analyzer"],
|
||||
navItemName: "analyzer",
|
||||
};
|
||||
|
||||
export default function BuildAnalyzerPage() {
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ export interface BadgesLoaderData {
|
|||
|
||||
export const handle: SendouRouteHandle = {
|
||||
i18n: "badges",
|
||||
navItemName: "badges",
|
||||
};
|
||||
|
||||
export const loader: LoaderFunction = () => {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ export const links: LinksFunction = () => {
|
|||
export const handle: SendouRouteHandle = {
|
||||
i18n: ["weapons", "builds"],
|
||||
breadcrumb: ({ t }) => t("pages.builds"),
|
||||
navItemName: "builds",
|
||||
};
|
||||
|
||||
export default function BuildsLayoutPage() {
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ export const meta: MetaFunction = (args) => {
|
|||
|
||||
export const handle: SendouRouteHandle = {
|
||||
i18n: "calendar",
|
||||
navItemName: "calendar",
|
||||
};
|
||||
|
||||
const loaderSearchParamsSchema = z.object({
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ export const meta: MetaFunction = (args) => {
|
|||
|
||||
export const handle: SendouRouteHandle = {
|
||||
i18n: "game-misc",
|
||||
navItemName: "maps",
|
||||
};
|
||||
|
||||
export const loader = async ({ request }: LoaderArgs) => {
|
||||
|
|
|
|||
|
|
@ -3,11 +3,16 @@ import { Outlet } from "@remix-run/react";
|
|||
import { Main } from "~/components/Main";
|
||||
import { SubNav, SubNavLink } from "~/components/SubNav";
|
||||
import styles from "~/styles/plus.css";
|
||||
import { type SendouRouteHandle } from "~/utils/remix";
|
||||
|
||||
export const links: LinksFunction = () => {
|
||||
return [{ rel: "stylesheet", href: styles }];
|
||||
};
|
||||
|
||||
export const handle: SendouRouteHandle = {
|
||||
navItemName: "plus",
|
||||
};
|
||||
|
||||
export default function PlusPageLayout() {
|
||||
return (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -102,6 +102,7 @@ export function validate(condition: any, status = 400): asserts condition {
|
|||
export type SendouRouteHandle = {
|
||||
/** The i18n translation files used for this route, via remix-i18next */
|
||||
i18n?: Namespace;
|
||||
|
||||
/**
|
||||
* A function that returns the breadcrumb text that should be displayed in
|
||||
* the <Breadcrumb> component
|
||||
|
|
@ -110,4 +111,7 @@ export type SendouRouteHandle = {
|
|||
match: RouteMatch;
|
||||
t: TFunction<"common", undefined>;
|
||||
}) => string | undefined;
|
||||
|
||||
/** The name of a navItem that is active on this route. See nav-items.json */
|
||||
navItemName?: string;
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user