Fix bottom/top nav: highlight only the most specific matching link (e.g. /bookings/new no longer also lights up Бронирования)
This commit is contained in:
1 parent
b6c604feaf
commit
4f206c2ddc
3 files changed
+19
-6
No files matched your search
@@ -4,7 +4,7 @@ import { useState } from "react";
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { usePathname } from "next/navigation";
|
import { usePathname } from "next/navigation";
|
||||||
import { MoreHorizontal, X } from "lucide-react";
|
import { MoreHorizontal, X } from "lucide-react";
|
||||||
import { links, PRIMARY_COUNT } from "./nav-data";
|
import { links, PRIMARY_COUNT, isLinkActive } from "./nav-data";
|
||||||
|
|
||||||
export function MobileNav({ isAdmin }: { isAdmin: boolean }) {
|
export function MobileNav({ isAdmin }: { isAdmin: boolean }) {
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
@@ -13,10 +13,10 @@ export function MobileNav({ isAdmin }: { isAdmin: boolean }) {
|
|||||||
const visibleLinks = links.filter((link) => !link.adminOnly || isAdmin);
|
const visibleLinks = links.filter((link) => !link.adminOnly || isAdmin);
|
||||||
const primary = visibleLinks.slice(0, PRIMARY_COUNT);
|
const primary = visibleLinks.slice(0, PRIMARY_COUNT);
|
||||||
const overflow = visibleLinks.slice(PRIMARY_COUNT);
|
const overflow = visibleLinks.slice(PRIMARY_COUNT);
|
||||||
const overflowActive = overflow.some((l) => pathname.startsWith(l.href));
|
const overflowActive = overflow.some((l) => isLinkActive(l, pathname, visibleLinks));
|
||||||
|
|
||||||
function isActive(link: (typeof links)[number]) {
|
function isActive(link: (typeof links)[number]) {
|
||||||
return link.exact ? pathname === link.href : pathname.startsWith(link.href);
|
return isLinkActive(link, pathname, visibleLinks);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -23,3 +23,15 @@ export const links: NavLink[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export const PRIMARY_COUNT = 4;
|
export const PRIMARY_COUNT = 4;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Highlights only the most specific match — e.g. on "/bookings/new", both it
|
||||||
|
* (exact) and "/bookings" (prefix) technically match; without this, both
|
||||||
|
* would light up at once. Picks the matching link with the longest href.
|
||||||
|
*/
|
||||||
|
export function isLinkActive(link: NavLink, pathname: string, allLinks: NavLink[]): boolean {
|
||||||
|
const matches = allLinks.filter((l) => (l.exact ? pathname === l.href : pathname.startsWith(l.href)));
|
||||||
|
if (matches.length === 0) return false;
|
||||||
|
const best = matches.reduce((a, b) => (b.href.length > a.href.length ? b : a));
|
||||||
|
return best.href === link.href;
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { usePathname } from "next/navigation";
|
import { usePathname } from "next/navigation";
|
||||||
import { links } from "./nav-data";
|
import { links, isLinkActive } from "./nav-data";
|
||||||
|
|
||||||
export function NavLinks({ isAdmin }: { isAdmin: boolean }) {
|
export function NavLinks({ isAdmin }: { isAdmin: boolean }) {
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
@@ -10,8 +10,9 @@ export function NavLinks({ isAdmin }: { isAdmin: boolean }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<nav className="flex items-center gap-1">
|
<nav className="flex items-center gap-1">
|
||||||
{visibleLinks.map(({ href, label, icon: Icon, exact }) => {
|
{visibleLinks.map((link) => {
|
||||||
const active = exact ? pathname === href : pathname.startsWith(href);
|
const { href, label, icon: Icon } = link;
|
||||||
|
const active = isLinkActive(link, pathname, visibleLinks);
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
key={href}
|
key={href}
|
||||||
|
|||||||
Reference in new issue
Block a user