0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

WebComponentsを使ってMUI のPopoverを同じページの複数箇所に出したいときの方法

Last updated at Posted at 2024-12-06

(2年ほど前のメモが残っていたので記事化…)

困ったこと

WebComponents内でMUIのPopoverを同じコンポーネント内の複数箇所に出したいとき、公式ドキュメントの実装例をそのまま真似してしまうと、複数箇所で同時にPopoverが表示されてしまう

※補足:

  • react-to-webcomponentを使って作成したWebComponents内での実装。
  • WebComponentsを使わない場合、普通に書いてもちゃんと別々に表示される。

環境

React 18.1.0
TypeScript 4.6.4
Material UI(MUI) v5
react-to-webcomponent

結論

  • それぞれのPopoverに対して個別のkeyとなる文字列を設定する
  • useStateで、↑の個別のkeyの選択状態を管理するstateを定義する
  • Popoverのopen内に書く条件を、「Popoverを開く=true」AND「keyの状態を管理するstateがそのPopoverのkeyと一致している」とする

実装例

const MyComponent: React.FC = () => {
  // Popover表示位置のref
  const firstRef = useRef<HTMLDivElement>(null);
  const secondRef = useRef<HTMLDivElement>(null);

  // Popover表示制御
  const [anchorEl, setAnchorEl] = React.useState<HTMLElement | null>(null);
  const [popOverKey, setPopOverKey] = useState<string | null>(null);
  const popOverKeys = {
    firstKey: 'first-popover',
    secondKey: 'second-popover',
  };
  const handlePopoverOpen =
    (key: string) => (event: React.MouseEvent<HTMLElement>) => {
      setPopOverKey(key);
      setAnchorEl(event.currentTarget);
    };
  const handlePopoverClose = () => {
    setPopOverKey(null);
    setAnchorEl(null);
  };
  const open = Boolean(anchorEl);

  return (
    <Grid item style={{ flex: 1 }}>
      <Box>
        <Grid item>
          <div ref={firstRef}></div>
          <Typography
            aria-owns={open ? popOverKeys.firstKey : undefined}
            aria-haspopup="true"
            onMouseEnter={handlePopoverOpen(popOverKeys.firstKey)}
            onMouseLeave={handlePopoverClose}>
            ここにマウスをかざしたときにPopover その1を表示したい
          </Typography>
          <Popover
            id={popOverKeys.firstKey}
            open={open && popOverKey == popOverKeys.firstKey}
            anchorEl={anchorEl}
            onClose={handlePopoverClose}
            container={() => {
              return firstRef.current;
            }}
            anchorOrigin={{
              vertical: 'bottom',
              horizontal: 'left',
            }}
            transformOrigin={{
              vertical: 'top',
              horizontal: 'left',
            }}
            disableRestoreFocus
            style={{ pointerEvents: 'none' }}>
            Popover その1
          </Popover>
        </Grid>
      </Box>
      <Grid container justifyContent="flex-end" style={{ display: 'flex' }}>
        <div ref={secondRef}></div>
        <Grid item style={{ minWidth: '85px' }}>
          <Typography
            align="right"
            aria-owns={open ? popOverKeys.secondKey : undefined}
            aria-haspopup="true"
            onMouseEnter={handlePopoverOpen(popOverKeys.secondKey)}
            onMouseLeave={handlePopoverClose}>
            ここにマウスをかざしたときにPopover その2を表示したい
          </Typography>
          <Popover
            id={popOverKeys.secondKey}
            open={open && popOverKey == popOverKeys.secondKey}
            anchorEl={anchorEl}
            onClose={handlePopoverClose}
            container={() => {
              return secondRef.current;
            }}
            anchorOrigin={{
              vertical: 'bottom',
              horizontal: 'left',
            }}
            transformOrigin={{
              vertical: 'top',
              horizontal: 'left',
            }}
            disableRestoreFocus
            style={{ pointerEvents: 'none' }}>
            Popover その2
          </Popover>
        </Grid>
      </Grid>
    </Grid>
  );
};
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?