Tldraw/packages/tldraw/src/lib/ui/hooks/useMenuIsOpen.ts

65 wiersze
1.7 KiB
TypeScript

import { useEditor, useValue } from '@tldraw/editor'
import { useCallback, useEffect, useRef } from 'react'
import { useUiEvents } from '../context/events'
/** @public */
export function useMenuIsOpen(id: string, cb?: (isOpen: boolean) => void) {
const editor = useEditor()
const rIsOpen = useRef(false)
const trackEvent = useUiEvents()
const onOpenChange = useCallback(
(isOpen: boolean) => {
rIsOpen.current = isOpen
if (isOpen) {
editor.complete()
editor.addOpenMenu(id)
} else {
editor.updateInstanceState({
openMenus: editor.getOpenMenus().filter((m) => !m.startsWith(id)),
})
}
cb?.(isOpen)
},
[editor, id, cb]
)
const isOpen = useValue('is menu open', () => editor.getOpenMenus().includes(id), [editor, id])
useEffect(() => {
// When the effect runs, if the menu is open then
// add it to the open menus list.
// This is necessary for cases where the user closes
// the parent of a submenu before closing the submenu.
// There is some duplication between this and `onOpenChange`
// hook but it's necessary to handle the case where the
// this effect runs twice or re-runs.
if (rIsOpen.current) {
trackEvent('open-menu', { source: 'unknown', id })
editor.addOpenMenu(id)
}
return () => {
if (rIsOpen.current) {
// Close menu on unmount
editor.deleteOpenMenu(id)
// Close menu and all submenus when the parent is closed
editor.getOpenMenus().forEach((menuId) => {
if (menuId.startsWith(id)) {
trackEvent('close-menu', { source: 'unknown', id })
editor.deleteOpenMenu(menuId)
}
})
rIsOpen.current = false
}
}
}, [editor, id, trackEvent])
return [isOpen, onOpenChange] as const
}