From 4a8e47d8156788dedbebd2c72b758ba7156ee8ac Mon Sep 17 00:00:00 2001 From: Peter Beverloo Date: Sat, 6 Jan 2024 23:08:26 +0000 Subject: [PATCH] Make active menu option highlighting more consistent --- app/admin/AdminSidebar.tsx | 29 ++++++++++++++++++++++++++++- app/admin/TopLevelLayout.tsx | 1 + app/admin/events/[slug]/layout.tsx | 2 ++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/app/admin/AdminSidebar.tsx b/app/admin/AdminSidebar.tsx index 044c330c..55e725de 100644 --- a/app/admin/AdminSidebar.tsx +++ b/app/admin/AdminSidebar.tsx @@ -95,6 +95,18 @@ interface AdminSidebarMenuButtonItem { * The URL for this list item entry. Must be absolute from the domain root. */ url: string; + + /** + * Match to apply to the URL (or URL prefix) when deciding on highlight state. A prefix match is + * executed by default, but for root pages a strict match may be more appropriate. + */ + urlMatchMode?: 'prefix' | 'strict'; + + /** + * The URL prefix for this list entry, all sub-pages of which will be captured for active tab + * state. Defaults to the URL. + */ + urlPrefix?: string; } /** @@ -120,6 +132,21 @@ export type AdminSidebarMenuEntry = AdminSidebarMenuDivider | (AdminSidebarMenuItemCommon & (AdminSidebarMenuButtonItem | AdminSidebarMenuSubMenuItem)); +/** + * Decides whether a particular menu entry should be highlighted. Considered to be the case when the + * `pathname` starts with the `entry`'s URL (or URL prefix). + */ +function shouldHighlightEntry(pathname: string, entry: AdminSidebarMenuButtonItem) { + const matchMode = entry.urlMatchMode ?? 'prefix'; + switch (matchMode) { + case 'prefix': + return pathname.startsWith(entry.urlPrefix ?? entry.url); + case 'strict': + return pathname === entry.url; + } +} + + /** * Props accepted by the component. */ @@ -221,7 +248,7 @@ function RenderSidebarMenu(props: RenderSidebarMenuProps) { : kStyles.active } component={Link} href={entry.url} - selected={entry.url === pathname}> + selected={shouldHighlightEntry(pathname, entry)}> { entry.icon && diff --git a/app/admin/TopLevelLayout.tsx b/app/admin/TopLevelLayout.tsx index 213fbf17..7cabc8c3 100644 --- a/app/admin/TopLevelLayout.tsx +++ b/app/admin/TopLevelLayout.tsx @@ -26,6 +26,7 @@ export default async function TopLevelLayout(props: React.PropsWithChildren) { icon: , label: 'Dashboard', url: '/admin', + urlMatchMode: 'strict', }, { icon: , diff --git a/app/admin/events/[slug]/layout.tsx b/app/admin/events/[slug]/layout.tsx index afb64182..19e3e7b8 100644 --- a/app/admin/events/[slug]/layout.tsx +++ b/app/admin/events/[slug]/layout.tsx @@ -134,6 +134,7 @@ export default async function EventLayout(props: React.PropsWithChildren, label: 'Program', url: `/admin/events/${slug}/program/requests`, + urlPrefix: `/admin/events/${slug}/program` }); } @@ -142,6 +143,7 @@ export default async function EventLayout(props: React.PropsWithChildren, label: 'Dashboard', url: `/admin/events/${slug}`, + urlMatchMode: 'strict', }, { icon: ,