From d2936cb0228b7cb7fc7a390a3f75a0139e43e1af Mon Sep 17 00:00:00 2001 From: Gregory Schier Date: Sat, 11 Jan 2025 14:16:37 -0800 Subject: [PATCH] Ensure only one dropdown can be open at a time --- src-web/components/core/Dropdown.tsx | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src-web/components/core/Dropdown.tsx b/src-web/components/core/Dropdown.tsx index d5770b9a..0a3ef716 100644 --- a/src-web/components/core/Dropdown.tsx +++ b/src-web/components/core/Dropdown.tsx @@ -26,6 +26,7 @@ import { useClickOutside } from '../../hooks/useClickOutside'; import type { HotkeyAction } from '../../hooks/useHotKey'; import { useHotKey } from '../../hooks/useHotKey'; import { useStateWithDeps } from '../../hooks/useStateWithDeps'; +import {generateId} from "../../lib/generateId"; import { getNodeText } from '../../lib/getNodeText'; import { Overlay } from '../Overlay'; import { Button } from './Button'; @@ -33,6 +34,7 @@ import { HotKey } from './HotKey'; import { Icon } from './Icon'; import { Separator } from './Separator'; import { HStack, VStack } from './Stacks'; +import { atom, useAtom } from 'jotai'; export type DropdownItemSeparator = { type: 'separator'; @@ -76,19 +78,28 @@ export interface DropdownRef { select?: () => void; } +// Every dropdown gets a unique ID and we use this global atom to ensure +// only one dropdown can be open at a time. +const openAtom = atom(null); + export const Dropdown = forwardRef(function Dropdown( { children, items, onOpen, onClose, hotKeyAction, fullWidth }: DropdownProps, ref, ) { - const [isOpen, _setIsOpen] = useState(false); + const id = useRef(generateId()).current; + const [openId, setOpenId] = useAtom(openAtom); + const isOpen = openId === id; + + // const [isOpen, _setIsOpen] = useState(false); const [defaultSelectedIndex, setDefaultSelectedIndex] = useState(null); const buttonRef = useRef(null); const menuRef = useRef>(null); const setIsOpen = useCallback( (o: SetStateAction) => { - _setIsOpen((prev) => { - const newIsOpen = typeof o === 'function' ? o(prev) : o; + setOpenId((prevId) => { + const prevIsOpen = prevId === id; + const newIsOpen = typeof o === 'function' ? o(prevIsOpen) : o; if (newIsOpen) onOpen?.(); else onClose?.(); @@ -96,10 +107,11 @@ export const Dropdown = forwardRef(function Dropdown // Set to different value when opened and closed to force it to update. This is to force // to reset its selected-index state, which it does when this prop changes setDefaultSelectedIndex(newIsOpen ? -1 : null); - return newIsOpen; + + return newIsOpen ? id : null; // Set global atom to current ID to signify open state }); }, - [onClose, onOpen], + [id, onClose, onOpen, setOpenId], ); const handleClose = useCallback(() => {