(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>
);
};