ui
ui copied to clipboard
Navigation Menu, Menu is not centered to parent
Im triying to use navigaton menu, but the menu is not centered to parent

<div className='hidden lg:flex h-full'>
<div className='flex'>
<NavigationMenu>
<NavigationMenuList>
{routes.map((route) => {
return (
<NavigationMenuItem key={route.path}>
{route.subRoutes ? (
<Link href={route.path}>
<NavigationMenuTrigger>{route.label}</NavigationMenuTrigger>
<NavigationMenuContent>
<ul>
{route.subRoutes.map((subRoute) => (
<li key={subRoute.path} className='whitespace-nowrap uppercase'>
<Link href={subRoute.path}>{subRoute.label}</Link>
</li>
))}
</ul>
</NavigationMenuContent>
</Link>
) : (
<Link href={route.path}>
<NavigationMenuLink className={navigationMenuTriggerStyle()}>
{route.label}
</NavigationMenuLink>
</Link>
)}
</NavigationMenuItem>
);
})}
</NavigationMenuList>
</NavigationMenu>
</div>
</div>
reading the code seems like this is the correct behavior o.o
try this: https://codesandbox.io/s/simple-navigation-menu-em1qrc?file=/src/App.js this is a simple example.
@AlejandroSanchez90 any solution? I copied the demo code into my project same behaviour.
Hi, @krunalinfynno @Josevictor2 @AlejandroSanchez90
Refer to this answer https://github.com/shadcn-ui/ui/issues/94#issuecomment-1763020702 I try reproduce and found the solution, seems like this:
- Add
submenu-viewportclass onNavigationMenuViewport
// components/ui/navigation-menu.tsx
const NavigationMenuViewport = React.forwardRef<
React.ElementRef<typeof NavigationMenuPrimitive.Viewport>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Viewport>
>(({ className, ...props }, ref) => (
<div className={cn("absolute left-0 top-full flex justify-center")}>
<NavigationMenuPrimitive.Viewport
className={cn(
"submenu-viewport ....",
className
)}
ref={ref}
{...props}
/>
</div>
))
NavigationMenuViewport.displayName =
NavigationMenuPrimitive.Viewport.displayName
- Add this function on your component:
function onNavChange() {
setTimeout(() => {
const triggers = document.querySelectorAll(
'.submenu-trigger[data-state="open"]'
)
if (triggers.length === 0) return
const firstTrigger = triggers[0] as HTMLElement
const viewports = document.getElementsByClassName("submenu-viewport")
if (viewports.length > 0) {
const viewport = viewports[0] as HTMLElement
viewport.style.left = `${firstTrigger.offsetLeft}px`
}
})
}
- Add
onValueChangeinside<NavigationMenu>
<NavigationMenu onValueChange={onNavChange}>
- Add class
submenu-triggeron<NavigationMenuTrigger>
<NavigationMenuTrigger className="submenu-trigger">
- Last one, make sure you didn't add
<NavigationMenuViewport>
It's work on my app
@akbarsaputrait This solution work for me, however, is there any way to center the NavigationMenuContent below the trigger instead of left-aligning? Many thanks!
@akbarsaputrait This solution work for me, however, is there any way to center the NavigationMenuContent below the trigger instead of left-aligning? Many thanks!
Not beautiful but working solution that I used for aligning on right instead of left was this:
<NavigationMenu className="[&>.absolute>.relative]:mt-0 [&>.absolute>.relative]:rounded-t-none [&>.absolute>.relative]:border-t-0 [&>.absolute]:-right-3 [&>.absolute]:left-auto [&>.absolute]:mt-5">
<NavigationMenuItem className="left-[unset] right-0">
You tweak and get to your solution from this I think.
Hi, @krunalinfynno @Josevictor2 @AlejandroSanchez90
Refer to this answer #94 (comment) I try reproduce and found the solution, seems like this:
- Add
submenu-viewportclass onNavigationMenuViewport// components/ui/navigation-menu.tsx const NavigationMenuViewport = React.forwardRef< React.ElementRef<typeof NavigationMenuPrimitive.Viewport>, React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Viewport> >(({ className, ...props }, ref) => ( <div className={cn("absolute left-0 top-full flex justify-center")}> <NavigationMenuPrimitive.Viewport className={cn( "submenu-viewport ....", className )} ref={ref} {...props} /> </div> )) NavigationMenuViewport.displayName = NavigationMenuPrimitive.Viewport.displayName
- Add this function on your component:
function onNavChange() { setTimeout(() => { const triggers = document.querySelectorAll( '.submenu-trigger[data-state="open"]' ) if (triggers.length === 0) return const firstTrigger = triggers[0] as HTMLElement const viewports = document.getElementsByClassName("submenu-viewport") if (viewports.length > 0) { const viewport = viewports[0] as HTMLElement viewport.style.left = `${firstTrigger.offsetLeft}px` } }) }
- Add
onValueChangeinside<NavigationMenu><NavigationMenu onValueChange={onNavChange}>
- Add class
submenu-triggeron<NavigationMenuTrigger><NavigationMenuTrigger className="submenu-trigger">
- Last one, make sure you didn't add
<NavigationMenuViewport>It's work on my app
I will add a little improvement.
NO IDEA WHY BUT...
Problem:
- When onNavChange Triggered first time, .submenu-viewport is could not be found in DOM
- On the second trigger everything works.
Solution:
Instead of adding submenu-viewport class add left-[var(--menu-left-position)] to NavigationMenuViewport and just set global css variable with position
onNavChange:
function onNavChange() {
setTimeout(() => {
const triggers = document.querySelectorAll(
'.submenu-trigger[data-state="open"]'
)
if (triggers.length === 0) return
const firstTrigger = triggers[0] as HTMLElement
document.documentElement.style.setProperty(
"--menu-left-position",
`${firstTrigger.offsetLeft}px`
)
})
}
NavigationMenuViewport:
// components/ui/navigation-menu.tsx
const NavigationMenuViewport = React.forwardRef<
React.ElementRef<typeof NavigationMenuPrimitive.Viewport>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Viewport>
>(({ className, ...props }, ref) => (
<div className={cn("absolute left-0 top-full flex justify-center")}>
<NavigationMenuPrimitive.Viewport
className={cn(
"left-[var(--menu-left-position)] ...",
className,
)}
ref={ref}
{...props}
/>
</div>
));
NavigationMenu as before
<NavigationMenu onValueChange={onNavChange}>
NavigationMenuTrigger as before
<NavigationMenuTrigger className="submenu-trigger">
@akbarsaputrait Any questions ? )))
After a few long hours of trying to make this work for me too, I'll add my variant as this seems to have fixed my issue and allowed me to center the dropdown under the button.
onNavChange
function onNavChange() {
setTimeout(() => {
// Select elements with the state "open"
const triggers = document.querySelectorAll(
'.submenu-trigger[data-state="open"]'
);
const dropdowns = document.querySelectorAll(
'.nav-viewport[data-state="open"]'
);
// Check if both triggers and dropdowns are present
if (!triggers.length || !dropdowns.length) return;
// Simplify the calculation by extracting it into a variable
const { offsetLeft, offsetWidth } = triggers[0] as HTMLElement;
const menuWidth = dropdowns[0].children[0].clientWidth;
const menuLeftPosition = offsetLeft + offsetWidth / 2 - menuWidth / 2;
// Apply the calculated position
document.documentElement.style.setProperty(
"--menu-left-position",
`${menuLeftPosition}px`
);
});
}
NavigationMenuViewport (Additional nav-viewport)
const NavigationMenuViewport = React.forwardRef<
React.ElementRef<typeof NavigationMenuPrimitive.Viewport>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Viewport>
>(({ className, ...props }, ref) => (
<div className={cn("absolute left-0 top-full flex justify-center")}>
<NavigationMenuPrimitive.Viewport
className={cn(
"left-[var(--menu-left-position)] nav-viewport ...",
className
)}
ref={ref}
{...props}
/>
</div>
));
NavigationMenu as before
<NavigationMenu onValueChange={onNavChange}>
NavigationMenuTrigger as before
<NavigationMenuTrigger className="submenu-trigger">
@ConnorC18 Perfect!
Still having multiple NavMenus and moving Viewport by changing position property (left) is not a good practice in animation as it is being drawn on a specific layer when rendering a page.
Anyways, GJ
Is it fixed i am facing same issue.?
This issue has been automatically closed because it received no activity for a while. If you think it was closed by accident, please leave a comment. Thank you.
https://codesandbox.io/s/simple-navigation-menu-em1qrc?file=/src/App.js
Not fixed lol, there's quite a few issues around this