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:
ogrechko committed 2026-08-29 12:27:08 +00:00
1 parent b6c604feaf
commit 4f206c2ddc
3 files changed
+19 -6

No files matched your search

+3 -3
View File
@@ -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 (
+12
View File
@@ -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;
}
+4 -3
View File
@@ -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}