reactjs - How to anchor React material popovermenu to the right instead of left - Stack Overflow

admin2025-04-18  3

I added my auth buttons into a material menu on my site, and it's proving to be a pain. I have the menu anchored to the right, so it aligns with the bottom right of my Login button and expands to the left. However, the Google button resizes itself in an iframe after a login attempt on the app, and since the material menu anchors with the left position, it resizes to the right.

I tried aligning the button to the right of the li, but this does not prevent the menu from resizing to the right because it is always absolutely positioned with top and left no matter what your anchor configuration is.

I have also tried using a style override on the component. Setting right css styling partially works, but material insists on setting left regardless of whether I try to override null or undefined, so it doesn't allow the menu to resize vertically.

I am attempting to use a ref to the menu paper scroll width to set left, but the ref is null on a useEffect with respect to isMenuOpen, so I'll have to dig into that more.

<Menu
  id='login-menu'
  ref={menuRef}
  disableAutoFocusItem
  disableScrollLock={true}
  anchorEl={menuAnchorEl}
  anchorOrigin={{
    vertical: 'bottom',
    horizontal: 'right',
  }}
  transformOrigin={{
    vertical: 'top',
    horizontal: 'right',
  }}
  open={isMenuOpen}
  onClose={() => setMenuAnchorEl(undefined)}
  MenuListProps={{
    'aria-labelledby': 'login-button',
    style: { padding: 0 },
  }}
  slotProps={{
    root: {
      style: { display: 'contents' },
    },
    paper: {
      ref: menuPaperRef,
      style: {
        left:
          (menuAnchorEl?.getBoundingClientRect().right ??
            window.innerWidth) - (menuPaperRef.current?.scrollWidth ?? 0),
        right:
          window.innerWidth -
          (menuAnchorEl?.getBoundingClientRect().right ?? 0),
      },
    },
  }}
>

Example of the issue (the right edge of the menu should align with the right edge of the Login button):

I added my auth buttons into a material menu on my site, and it's proving to be a pain. I have the menu anchored to the right, so it aligns with the bottom right of my Login button and expands to the left. However, the Google button resizes itself in an iframe after a login attempt on the app, and since the material menu anchors with the left position, it resizes to the right.

I tried aligning the button to the right of the li, but this does not prevent the menu from resizing to the right because it is always absolutely positioned with top and left no matter what your anchor configuration is.

I have also tried using a style override on the component. Setting right css styling partially works, but material insists on setting left regardless of whether I try to override null or undefined, so it doesn't allow the menu to resize vertically.

I am attempting to use a ref to the menu paper scroll width to set left, but the ref is null on a useEffect with respect to isMenuOpen, so I'll have to dig into that more.

<Menu
  id='login-menu'
  ref={menuRef}
  disableAutoFocusItem
  disableScrollLock={true}
  anchorEl={menuAnchorEl}
  anchorOrigin={{
    vertical: 'bottom',
    horizontal: 'right',
  }}
  transformOrigin={{
    vertical: 'top',
    horizontal: 'right',
  }}
  open={isMenuOpen}
  onClose={() => setMenuAnchorEl(undefined)}
  MenuListProps={{
    'aria-labelledby': 'login-button',
    style: { padding: 0 },
  }}
  slotProps={{
    root: {
      style: { display: 'contents' },
    },
    paper: {
      ref: menuPaperRef,
      style: {
        left:
          (menuAnchorEl?.getBoundingClientRect().right ??
            window.innerWidth) - (menuPaperRef.current?.scrollWidth ?? 0),
        right:
          window.innerWidth -
          (menuAnchorEl?.getBoundingClientRect().right ?? 0),
      },
    },
  }}
>

Example of the issue (the right edge of the menu should align with the right edge of the Login button):

Share edited Jan 30 at 16:21 Drew Reese 204k18 gold badges245 silver badges273 bronze badges asked Jan 30 at 8:35 TCFPTCFP 1851 silver badge6 bronze badges
Add a comment  | 

2 Answers 2

Reset to default 0

I want to share a somewhat hacky workaround - using a ResizeObserver on the problem button, I was able to effectively force the left position based on the right and the resulting width (reference).

I want to stress that this solution is very inconsistent and less performant than simply using right instead of left, so if there is a way to do so, I'm still on the lookout for answers.

I'm not sure I understand your problem but you can use this utility function to get position of a ref and then apply an offset.

type Align = 'left' | 'right';

interface CalculatePositionParams {
  ref: HTMLElement;
  align?: Align;
  verticalOffset?: number;
  horizontalOffset?: number;
}

const getPosition = (params: CalculatePositionParams): PositionResult => {
  const { ref, align = 'left', verticalOffset = 0, horizontalOffset = 0 } = params;
  const rect = ref.getBoundingClientRect();
  let top = rect.bottom + verticalOffset + window.scrollY;
  let left: number | undefined;
  let right: number | undefined;

  if (align === 'left') {
    left = rect.left + horizontalOffset;
  } else if (align === 'right') {
    right = window.innerWidth - rect.right + horizontalOffset;
  }

  return { top, left, right };
};

This will return an object that you can use on an html element as a style.

转载请注明原文地址:http://www.anycun.com/QandA/1744931131a89652.html