refactor: enhance URL normalization logic and update sidebar navigation active state handling
This commit is contained in:
parent
fe3e6767ce
commit
2e1aebae97
@ -19,7 +19,7 @@ import product from '@/routes/product';
|
|||||||
const mainNavItems: NavItem[] = [
|
const mainNavItems: NavItem[] = [
|
||||||
{
|
{
|
||||||
title: 'Dashboard',
|
title: 'Dashboard',
|
||||||
href: dashboard(),
|
href: dashboard().url,
|
||||||
icon: LayoutGrid,
|
icon: LayoutGrid,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@ -44,7 +44,7 @@ export function AppSidebar() {
|
|||||||
<SidebarMenu>
|
<SidebarMenu>
|
||||||
<SidebarMenuItem>
|
<SidebarMenuItem>
|
||||||
<SidebarMenuButton size="lg" asChild>
|
<SidebarMenuButton size="lg" asChild>
|
||||||
<Link href={dashboard()} prefetch>
|
<Link href={dashboard().url} prefetch>
|
||||||
<AppLogo />
|
<AppLogo />
|
||||||
</Link>
|
</Link>
|
||||||
</SidebarMenuButton>
|
</SidebarMenuButton>
|
||||||
|
|||||||
@ -16,7 +16,7 @@ export function NavMain({
|
|||||||
items: NavItem[];
|
items: NavItem[];
|
||||||
label?: string;
|
label?: string;
|
||||||
}) {
|
}) {
|
||||||
const { isCurrentUrl } = useCurrentUrl();
|
const { isCurrentOrParentUrl } = useCurrentUrl();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SidebarGroup className="px-2 py-1">
|
<SidebarGroup className="px-2 py-1">
|
||||||
@ -26,7 +26,7 @@ export function NavMain({
|
|||||||
<SidebarMenuItem key={item.title}>
|
<SidebarMenuItem key={item.title}>
|
||||||
<SidebarMenuButton
|
<SidebarMenuButton
|
||||||
asChild
|
asChild
|
||||||
isActive={isCurrentUrl(item.href)}
|
isActive={item.isActive || isCurrentOrParentUrl(item.href)}
|
||||||
tooltip={{ children: item.title }}
|
tooltip={{ children: item.title }}
|
||||||
>
|
>
|
||||||
<Link href={item.href} prefetch>
|
<Link href={item.href} prefetch>
|
||||||
|
|||||||
@ -506,7 +506,7 @@ function SidebarMenuButton({
|
|||||||
data-slot="sidebar-menu-button"
|
data-slot="sidebar-menu-button"
|
||||||
data-sidebar="menu-button"
|
data-sidebar="menu-button"
|
||||||
data-size={size}
|
data-size={size}
|
||||||
data-active={isActive}
|
data-active={isActive || undefined}
|
||||||
className={cn(sidebarMenuButtonVariants({ variant, size }), className)}
|
className={cn(sidebarMenuButtonVariants({ variant, size }), className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -662,7 +662,7 @@ function SidebarMenuSubButton({
|
|||||||
data-slot="sidebar-menu-sub-button"
|
data-slot="sidebar-menu-sub-button"
|
||||||
data-sidebar="menu-sub-button"
|
data-sidebar="menu-sub-button"
|
||||||
data-size={size}
|
data-size={size}
|
||||||
data-active={isActive}
|
data-active={isActive || undefined}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 text-sidebar-foreground ring-sidebar-ring outline-hidden group-data-[collapsible=icon]:hidden hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[size=md]:text-sm data-[size=sm]:text-xs data-active:bg-sidebar-accent data-active:text-sidebar-accent-foreground [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-accent-foreground",
|
"flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 text-sidebar-foreground ring-sidebar-ring outline-hidden group-data-[collapsible=icon]:hidden hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[size=md]:text-sm data-[size=sm]:text-xs data-active:bg-sidebar-accent data-active:text-sidebar-accent-foreground [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-accent-foreground",
|
||||||
className
|
className
|
||||||
|
|||||||
@ -40,23 +40,34 @@ export function useCurrentUrl(): UseCurrentUrlReturn {
|
|||||||
currentUrl?: string,
|
currentUrl?: string,
|
||||||
startsWith: boolean = false,
|
startsWith: boolean = false,
|
||||||
) => {
|
) => {
|
||||||
const urlToCompare = currentUrl ?? currentUrlPath;
|
|
||||||
const urlString = toUrl(urlToCheck);
|
const urlString = toUrl(urlToCheck);
|
||||||
|
const currentPath = currentUrl ?? currentUrlPath;
|
||||||
|
|
||||||
const comparePath = (path: string): boolean =>
|
const clean = (p: string) => {
|
||||||
startsWith ? urlToCompare.startsWith(path) : path === urlToCompare;
|
if (!p) return '/';
|
||||||
|
// Remove query and hash, then trailing slash
|
||||||
if (!urlString.startsWith('http')) {
|
let path = p.split('?')[0].split('#')[0];
|
||||||
return comparePath(urlString);
|
if (path.startsWith('http')) {
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const absoluteUrl = new URL(urlString);
|
path = new URL(path).pathname;
|
||||||
|
|
||||||
return comparePath(absoluteUrl.pathname);
|
|
||||||
} catch {
|
} catch {
|
||||||
return false;
|
// Ignore
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return path.replace(/\/+$/, '') || '/';
|
||||||
|
};
|
||||||
|
|
||||||
|
const normCurrent = clean(currentPath);
|
||||||
|
const normCheck = clean(urlString);
|
||||||
|
|
||||||
|
if (startsWith) {
|
||||||
|
if (normCheck === '/') {
|
||||||
|
return normCurrent === '/';
|
||||||
|
}
|
||||||
|
return normCurrent.startsWith(normCheck);
|
||||||
|
}
|
||||||
|
|
||||||
|
return normCurrent === normCheck;
|
||||||
};
|
};
|
||||||
|
|
||||||
const isCurrentOrParentUrl: IsCurrentOrParentUrlFn = (
|
const isCurrentOrParentUrl: IsCurrentOrParentUrlFn = (
|
||||||
|
|||||||
@ -7,6 +7,9 @@ export function cn(...inputs: ClassValue[]) {
|
|||||||
return twMerge(clsx(inputs));
|
return twMerge(clsx(inputs));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toUrl(url: NonNullable<InertiaLinkProps['href']>): string {
|
export function toUrl(url: InertiaLinkProps['href']): string {
|
||||||
return typeof url === 'string' ? url : url.url;
|
if (!url) return '';
|
||||||
|
if (typeof url === 'string') return url;
|
||||||
|
if (typeof url === 'object' && url !== null && 'url' in url) return url.url as string;
|
||||||
|
return String(url);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user