ui icon indicating copy to clipboard operation
ui copied to clipboard

Position of NavigationMenuViewport

Open MiljenkoLogozar opened this issue 2 years ago • 18 comments

When we need a more extensive Navigation Menu, Viewport with Content does not follow the position of the Trigger. Instead, all menus are in the center of the screen. You can see behavior on this test page: https://chakras-edtsr25u2-miljenko-storydeckio.vercel.app/

The same issues are mentioned here: https://github.com/radix-ui/primitives/issues/1462

Is there any parameter to position Viewport under the Trigger point?

MiljenkoLogozar avatar Feb 16 '23 20:02 MiljenkoLogozar

I'm also pretty confused about the best way to fix this. I simply want the viewport to show underneath the trigger.

joepetrillo avatar Feb 21 '23 21:02 joepetrillo

There's a fix here https://github.com/shadcn/ui/pull/102. Does this help?

shadcn avatar Feb 22 '23 07:02 shadcn

Fix #102 is ok if you have a few items in the menu bar. In our case, we have six "MenuTrigers" solution #102 is suitable for "MenuTriggers" in the middle of the screen but not ideal for the first and last "MenuTrigger" elements.

We are looking for this type of solution: https://codesandbox.io/s/simple-navigation-menu-em1qrc?file=/src/App.js

Each "MenuContent" drop-down element is strictly under "MenuTrigger."

MiljenkoLogozar avatar Feb 25 '23 12:02 MiljenkoLogozar

Anyone got an update to this? I have the same issue

xCraith avatar Aug 20 '23 21:08 xCraith

Any resolution, I'm currently facing the same problem.

Elly0816 avatar Sep 15 '23 16:09 Elly0816

I also faced this problem, do you have any solutions?

OleinikovArtem avatar Sep 27 '23 06:09 OleinikovArtem

I fixed by removing NavigationMenuViewport in NavigationMenu component and using <NavigationMenuContent className="absolute top-full"> instead to display the submenu items.

Hope this will help

A better solution would be to make it as Prop to be able to set it dynamically.

artur-dani avatar Sep 29 '23 20:09 artur-dani

I tried @artur-dani answer but didn't work.

I found this answer instead: Add right:0 absolute left-auto top-full w-auto to <NavigationMenuContent ... /> and completely remove <NavigationMenuViewport /> from <NavigationMenu />

I don't know if it will break something, but is working at least

jackson-batista avatar Oct 02 '23 19:10 jackson-batista

I tried @artur-dani answer but didn't work.

I found this answer instead: Add right:0 absolute left-auto top-full w-auto to <NavigationMenuContent ... /> and completely remove <NavigationMenuViewport /> from <NavigationMenu />

I don't know if it will break something, but is working at least

This work but it will disable the closing of the submenu when clicked outside. What I did is to add submenu-trigger class to all NavigationMenuTrigger and submenu-viewport class to NavigationMenuViewport and use the following script to update the left position of the viewport using the @update:model-value on NavigationMenu.

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`
    }
  })
}

<NavigationMenu @update:model-value="onNavChange">
<NavigationMenuViewport class="submenu-viewport">
<NavigationMenuTrigger class="submenu-trigger">

SGDS-SyL avatar Oct 14 '23 16:10 SGDS-SyL

I tried @artur-dani answer but didn't work. I found this answer instead: Add right:0 absolute left-auto top-full w-auto to <NavigationMenuContent ... /> and completely remove <NavigationMenuViewport /> from <NavigationMenu /> I don't know if it will break something, but is working at least

This work but it will disable the closing of the submenu when clicked outside. What I did is to add submenu-trigger class to all NavigationMenuTrigger and submenu-viewport class to NavigationMenuViewport and use the following script to update the left position of the viewport using the @update:model-value on NavigationMenu.

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`
    }
  })
}

<NavigationMenu @update:model-value="onNavChange">
<NavigationMenuViewport class="submenu-viewport">
<NavigationMenuTrigger class="submenu-trigger">

Could you elaborate on the @update:model-value="onNavChange" part? trying to use this in nextjs in the NavigationMenu component is giving me errors.

launchthatbrand avatar Jan 07 '24 15:01 launchthatbrand

I just found a solution for me

I just create a component for NavigationMenuItem and add 'relative' to it.

const NavigationMenuItem = React.forwardRef<
  React.ElementRef<typeof NavigationMenuPrimitive.Item>,
  React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Item>
>(({ className, ...props }, ref) => (
  <NavigationMenuPrimitive.Item
    ref={ref}
    className={cn("relative", className)}
    {...props}
  />
));
NavigationMenuItem.displayName = 'NavigationMenuItem';

BTW I remove Navigation MenuViewport too and add this to the Menu

const NavigationMenuContent = React.forwardRef<
  React.ElementRef<typeof NavigationMenuPrimitive.Content>,
  React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Content>
>(({ className, ...props }, ref) => (
  <NavigationMenuPrimitive.Content
    ref={ref}
    className={cn(
      "right-0  absolute",
      "absolute top-full w-fit bg-popover mt-[5px]",
      "data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52",
      className
    )}
    {...props}
  />
));

Hope it helps.

Example Image: image

dongnez avatar Feb 17 '24 14:02 dongnez

I just found a solution for me

I just create a component for NavigationMenuItem and add 'relative' to it.

const NavigationMenuItem = React.forwardRef<
  React.ElementRef<typeof NavigationMenuPrimitive.Item>,
  React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Item>
>(({ className, ...props }, ref) => (
  <NavigationMenuPrimitive.Item
    ref={ref}
    className={cn("relative", className)}
    {...props}
  />
));
NavigationMenuItem.displayName = 'NavigationMenuItem';

BTW I remove Navigation MenuViewport too and add this to the Menu

const NavigationMenuContent = React.forwardRef<
  React.ElementRef<typeof NavigationMenuPrimitive.Content>,
  React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Content>
>(({ className, ...props }, ref) => (
  <NavigationMenuPrimitive.Content
    ref={ref}
    className={cn(
      "right-0  absolute",
      "absolute top-full w-fit bg-popover mt-[5px]",
      "data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52",
      className
    )}
    {...props}
  />
));

Hope it helps.

Example Image: image

thanks that works for me perfectly.

hadi-alnaqash avatar Feb 21 '24 14:02 hadi-alnaqash

I just found a solution for me

I just create a component for NavigationMenuItem and add 'relative' to it.

const NavigationMenuItem = React.forwardRef<
  React.ElementRef<typeof NavigationMenuPrimitive.Item>,
  React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Item>
>(({ className, ...props }, ref) => (
  <NavigationMenuPrimitive.Item
    ref={ref}
    className={cn("relative", className)}
    {...props}
  />
));
NavigationMenuItem.displayName = 'NavigationMenuItem';

BTW I remove Navigation MenuViewport too and add this to the Menu

const NavigationMenuContent = React.forwardRef<
  React.ElementRef<typeof NavigationMenuPrimitive.Content>,
  React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Content>
>(({ className, ...props }, ref) => (
  <NavigationMenuPrimitive.Content
    ref={ref}
    className={cn(
      "right-0  absolute",
      "absolute top-full w-fit bg-popover mt-[5px]",
      "data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52",
      className
    )}
    {...props}
  />
));

Hope it helps.

Example Image: image

A full year and this is such a gem to find... Thank you, it still works which means the problem was never looked into after this issue opened.

brighton-ifaya avatar Feb 29 '24 12:02 brighton-ifaya

I removed the reference to NavigationMenuViewport in NavigationMenu in the navigation-menu.ts file.

const NavigationMenu = React.forwardRef<
  React.ElementRef<typeof NavigationMenuPrimitive.Root>,
  React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Root>
>(({ className, children, ...props }, ref) => (
  <NavigationMenuPrimitive.Root
    ref={ref}
    className={cn(
      "relative z-10 flex max-w-max flex-1 items-center justify-center",
      className
    )}
    {...props}
  >
    {children}
    {/* <NavigationMenuViewport /> */}
  </NavigationMenuPrimitive.Root>
))

I modified NavigationMenuContent to the following because the previous example left out the styling of the default MenuViewport which includes a border and shadow.

const NavigationMenuContent = React.forwardRef<
  React.ElementRef<typeof NavigationMenuPrimitive.Content>,
  React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Content>
>(({ className, ...props }, ref) => (
  <NavigationMenuPrimitive.Content
    ref={ref}
    className={cn(
      "left-0  absolute",
      "absolute top-full w-fit bg-popover mt-[5px]",
      "data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52",
      "origin-top-center absolute mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 md:w-[var(--radix-navigation-menu-viewport-width)]",
      className
    )}
    {...props}
  />
))

Here is the result:

image

AoverK avatar Mar 09 '24 17:03 AoverK

I removed the reference to NavigationMenuViewport in NavigationMenu in the navigation-menu.ts file.

Yep, also just found that solution

DMX3007 avatar Mar 13 '24 05:03 DMX3007

Finally found a solution here. Thanks for this thread, I've been trying to fix this for hours

Olalexy1 avatar May 10 '24 11:05 Olalexy1

The default behavior is so broken, both implementations from @dongnez and @AoverK are way better, thanks for sharing 💜

gsporto avatar May 27 '24 23:05 gsporto

I just found a solution for me

I just create a component for NavigationMenuItem and add 'relative' to it.

const NavigationMenuItem = React.forwardRef<
  React.ElementRef<typeof NavigationMenuPrimitive.Item>,
  React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Item>
>(({ className, ...props }, ref) => (
  <NavigationMenuPrimitive.Item
    ref={ref}
    className={cn("relative", className)}
    {...props}
  />
));
NavigationMenuItem.displayName = 'NavigationMenuItem';

BTW I remove Navigation MenuViewport too and add this to the Menu

const NavigationMenuContent = React.forwardRef<
  React.ElementRef<typeof NavigationMenuPrimitive.Content>,
  React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Content>
>(({ className, ...props }, ref) => (
  <NavigationMenuPrimitive.Content
    ref={ref}
    className={cn(
      "right-0  absolute",
      "absolute top-full w-fit bg-popover mt-[5px]",
      "data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52",
      className
    )}
    {...props}
  />
));

Hope it helps.

Example Image: image

thanks worked for me

kcritesh avatar Jun 25 '24 11:06 kcritesh

I removed the reference to NavigationMenuViewport in NavigationMenu in the navigation-menu.ts file.

const NavigationMenu = React.forwardRef<
  React.ElementRef<typeof NavigationMenuPrimitive.Root>,
  React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Root>
>(({ className, children, ...props }, ref) => (
  <NavigationMenuPrimitive.Root
    ref={ref}
    className={cn(
      "relative z-10 flex max-w-max flex-1 items-center justify-center",
      className
    )}
    {...props}
  >
    {children}
    {/* <NavigationMenuViewport /> */}
  </NavigationMenuPrimitive.Root>
))

I modified NavigationMenuContent to the following because the previous example left out the styling of the default MenuViewport which includes a border and shadow.

const NavigationMenuContent = React.forwardRef<
  React.ElementRef<typeof NavigationMenuPrimitive.Content>,
  React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Content>
>(({ className, ...props }, ref) => (
  <NavigationMenuPrimitive.Content
    ref={ref}
    className={cn(
      "left-0  absolute",
      "absolute top-full w-fit bg-popover mt-[5px]",
      "data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52",
      "origin-top-center absolute mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 md:w-[var(--radix-navigation-menu-viewport-width)]",
      className
    )}
    {...props}
  />
))

Here is the result:

image

Thank you!!!! This problem was driving me crazy!

austinwhite avatar Jun 30 '24 06:06 austinwhite

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.

shadcn avatar Jul 23 '24 23:07 shadcn

This is still an issue.

BrendanC23 avatar Jul 24 '24 13:07 BrendanC23

This is still an issue.

MahykBurak avatar Jul 25 '24 12:07 MahykBurak

Came here to say this is still an issue. Thanks to @AoverK and @dongnez for your fixes!

halimahexe avatar Aug 06 '24 15:08 halimahexe