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[] = [
|
||||
{
|
||||
title: 'Dashboard',
|
||||
href: dashboard(),
|
||||
href: dashboard().url,
|
||||
icon: LayoutGrid,
|
||||
},
|
||||
];
|
||||
@ -44,7 +44,7 @@ export function AppSidebar() {
|
||||
<SidebarMenu>
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton size="lg" asChild>
|
||||
<Link href={dashboard()} prefetch>
|
||||
<Link href={dashboard().url} prefetch>
|
||||
<AppLogo />
|
||||
</Link>
|
||||
</SidebarMenuButton>
|
||||
|
||||
@ -16,7 +16,7 @@ export function NavMain({
|
||||
items: NavItem[];
|
||||
label?: string;
|
||||
}) {
|
||||
const { isCurrentUrl } = useCurrentUrl();
|
||||
const { isCurrentOrParentUrl } = useCurrentUrl();
|
||||
|
||||
return (
|
||||
<SidebarGroup className="px-2 py-1">
|
||||
@ -26,7 +26,7 @@ export function NavMain({
|
||||
<SidebarMenuItem key={item.title}>
|
||||
<SidebarMenuButton
|
||||
asChild
|
||||
isActive={isCurrentUrl(item.href)}
|
||||
isActive={item.isActive || isCurrentOrParentUrl(item.href)}
|
||||
tooltip={{ children: item.title }}
|
||||
>
|
||||
<Link href={item.href} prefetch>
|
||||
|
||||
@ -506,7 +506,7 @@ function SidebarMenuButton({
|
||||
data-slot="sidebar-menu-button"
|
||||
data-sidebar="menu-button"
|
||||
data-size={size}
|
||||
data-active={isActive}
|
||||
data-active={isActive || undefined}
|
||||
className={cn(sidebarMenuButtonVariants({ variant, size }), className)}
|
||||
{...props}
|
||||
/>
|
||||
@ -662,7 +662,7 @@ function SidebarMenuSubButton({
|
||||
data-slot="sidebar-menu-sub-button"
|
||||
data-sidebar="menu-sub-button"
|
||||
data-size={size}
|
||||
data-active={isActive}
|
||||
data-active={isActive || undefined}
|
||||
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",
|
||||
className
|
||||
|
||||
@ -40,23 +40,34 @@ export function useCurrentUrl(): UseCurrentUrlReturn {
|
||||
currentUrl?: string,
|
||||
startsWith: boolean = false,
|
||||
) => {
|
||||
const urlToCompare = currentUrl ?? currentUrlPath;
|
||||
const urlString = toUrl(urlToCheck);
|
||||
const currentPath = currentUrl ?? currentUrlPath;
|
||||
|
||||
const comparePath = (path: string): boolean =>
|
||||
startsWith ? urlToCompare.startsWith(path) : path === urlToCompare;
|
||||
const clean = (p: string) => {
|
||||
if (!p) return '/';
|
||||
// Remove query and hash, then trailing slash
|
||||
let path = p.split('?')[0].split('#')[0];
|
||||
if (path.startsWith('http')) {
|
||||
try {
|
||||
path = new URL(path).pathname;
|
||||
} catch {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
return path.replace(/\/+$/, '') || '/';
|
||||
};
|
||||
|
||||
if (!urlString.startsWith('http')) {
|
||||
return comparePath(urlString);
|
||||
const normCurrent = clean(currentPath);
|
||||
const normCheck = clean(urlString);
|
||||
|
||||
if (startsWith) {
|
||||
if (normCheck === '/') {
|
||||
return normCurrent === '/';
|
||||
}
|
||||
return normCurrent.startsWith(normCheck);
|
||||
}
|
||||
|
||||
try {
|
||||
const absoluteUrl = new URL(urlString);
|
||||
|
||||
return comparePath(absoluteUrl.pathname);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
return normCurrent === normCheck;
|
||||
};
|
||||
|
||||
const isCurrentOrParentUrl: IsCurrentOrParentUrlFn = (
|
||||
|
||||
@ -7,6 +7,9 @@ export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
|
||||
export function toUrl(url: NonNullable<InertiaLinkProps['href']>): string {
|
||||
return typeof url === 'string' ? url : url.url;
|
||||
export function toUrl(url: InertiaLinkProps['href']): string {
|
||||
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