[fix] Context menu + menus not closing correctly (#2086)
This PR fixes a bug that causes menus not to close correctly when open. ### Change Type - [x] `patch` — Bug fix ### Test Plan 1. On mobile, open the menu. 2. Tap the canvas—it should close the panel. 3. Open the mobile style panel. 4. Tap the canvas—it should close the panel. 5. Open the mobile style panel. 6. Tap the mobile style panel button—it should close the panel. 7. On mobile, long press to open the context menu ### Release Notes - [fix] bug with menus
This commit is contained in:
parent
ca609a0fa5
commit
3b7acb8997
8 changed files with 46 additions and 8 deletions
|
@ -34,6 +34,7 @@ import { useForceUpdate } from './hooks/useForceUpdate'
|
|||
import { useLocalStore } from './hooks/useLocalStore'
|
||||
import { useSafariFocusOutFix } from './hooks/useSafariFocusOutFix'
|
||||
import { useZoomCss } from './hooks/useZoomCss'
|
||||
import { stopEventPropagation } from './utils/dom'
|
||||
import { TLStoreWithStatus } from './utils/sync/StoreWithStatus'
|
||||
|
||||
/**
|
||||
|
@ -162,6 +163,7 @@ export const TldrawEditor = memo(function TldrawEditor({
|
|||
ref={rContainer}
|
||||
draggable={false}
|
||||
className={classNames('tl-container tl-theme__light', className)}
|
||||
onPointerDown={stopEventPropagation}
|
||||
tabIndex={-1}
|
||||
>
|
||||
<OptionalErrorBoundary
|
||||
|
|
|
@ -17,8 +17,6 @@ export function useCanvasEvents() {
|
|||
let lastX: number, lastY: number
|
||||
|
||||
function onPointerDown(e: React.PointerEvent) {
|
||||
stopEventPropagation(e)
|
||||
|
||||
if ((e as any).isKilled) return
|
||||
|
||||
if (e.button === 2) {
|
||||
|
@ -41,6 +39,11 @@ export function useCanvasEvents() {
|
|||
name: 'pointer_down',
|
||||
...getPointerInfo(e),
|
||||
})
|
||||
|
||||
if (editor.openMenus.length > 0) {
|
||||
document.body.click()
|
||||
editor.getContainer().focus()
|
||||
}
|
||||
}
|
||||
|
||||
function onPointerMove(e: React.PointerEvent) {
|
||||
|
|
|
@ -110,10 +110,14 @@ export function useDocumentEvents() {
|
|||
// returns to the select tool. When the user has selected shapes,
|
||||
// escape de-selects them. Only when the user's selection is empty
|
||||
// should we allow escape to do its normal thing.
|
||||
|
||||
if (editor.editingShape || editor.selectedShapeIds.length > 0) {
|
||||
e.preventDefault()
|
||||
}
|
||||
|
||||
// Don't do anything if we open menus open
|
||||
if (editor.openMenus.length > 0) return
|
||||
|
||||
if (!editor.inputs.keys.has('Escape')) {
|
||||
editor.inputs.keys.add('Escape')
|
||||
|
||||
|
|
|
@ -576,6 +576,7 @@
|
|||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
color: var(--color-text-1);
|
||||
}
|
||||
|
||||
.tlui-style-panel__section *[data-state='open']::after {
|
||||
|
|
|
@ -63,6 +63,8 @@ export const ContextMenu = function ContextMenu({ children }: { children: any })
|
|||
[editor]
|
||||
)
|
||||
|
||||
const container = useContainer()
|
||||
|
||||
const [_, handleOpenChange] = useMenuIsOpen('context menu', cb)
|
||||
|
||||
// If every item in the menu is readonly, then we don't want to show the menu
|
||||
|
@ -87,7 +89,9 @@ export const ContextMenu = function ContextMenu({ children }: { children: any })
|
|||
>
|
||||
{children}
|
||||
</_ContextMenu.Trigger>
|
||||
<ContextMenuContent />
|
||||
<_ContextMenu.Portal container={container}>
|
||||
<ContextMenuContent />
|
||||
</_ContextMenu.Portal>
|
||||
</_ContextMenu.Root>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import {
|
|||
useValue,
|
||||
} from '@tldraw/editor'
|
||||
import { useCallback } from 'react'
|
||||
import { useOpenMenuCloser } from '../hooks/useOpenMenuCloser'
|
||||
import { useRelevantStyles } from '../hooks/useRevelantStyles'
|
||||
import { useTranslation } from '../hooks/useTranslation/useTranslation'
|
||||
import { StylePanel } from './StylePanel/StylePanel'
|
||||
|
@ -39,6 +40,8 @@ export function MobileStylePanel() {
|
|||
[editor]
|
||||
)
|
||||
|
||||
const extraEventsToToggleMenu = useOpenMenuCloser()
|
||||
|
||||
return (
|
||||
<Popover id="style menu" onOpenChange={handleStylesOpenChange}>
|
||||
<PopoverTrigger disabled={disableStylePanel}>
|
||||
|
@ -49,6 +52,7 @@ export function MobileStylePanel() {
|
|||
color: disableStylePanel ? 'var(--color-muted-1)' : currentColor,
|
||||
}}
|
||||
title={msg('style-panel.title')}
|
||||
{...extraEventsToToggleMenu}
|
||||
>
|
||||
<Icon icon={disableStylePanel ? 'blob' : color?.type === 'mixed' ? 'mixed' : 'blob'} />
|
||||
</Button>
|
||||
|
|
|
@ -16,11 +16,8 @@ export function useMenuIsOpen(id: string, cb?: (isOpen: boolean) => void) {
|
|||
editor.complete()
|
||||
editor.addOpenMenu(id)
|
||||
} else {
|
||||
editor.deleteOpenMenu(id)
|
||||
editor.openMenus.forEach((menuId) => {
|
||||
if (menuId.startsWith(id)) {
|
||||
editor.deleteOpenMenu(menuId)
|
||||
}
|
||||
editor.updateInstanceState({
|
||||
openMenus: editor.openMenus.filter((m) => !m.startsWith(id)),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
23
packages/tldraw/src/lib/ui/hooks/useOpenMenuCloser.tsx
Normal file
23
packages/tldraw/src/lib/ui/hooks/useOpenMenuCloser.tsx
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { preventDefault, useEditor } from '@tldraw/editor'
|
||||
import { useCallback, useRef } from 'react'
|
||||
|
||||
export function useOpenMenuCloser() {
|
||||
const editor = useEditor()
|
||||
const rIsPointingToClose = useRef(false)
|
||||
|
||||
const handlePointerDown = useCallback(() => {
|
||||
if (editor.openMenus.length > 0) {
|
||||
editor.updateInstanceState({ openMenus: [] })
|
||||
rIsPointingToClose.current = true
|
||||
}
|
||||
}, [editor])
|
||||
|
||||
const handleClick = useCallback((e: React.MouseEvent) => {
|
||||
if (rIsPointingToClose.current) {
|
||||
preventDefault(e)
|
||||
}
|
||||
rIsPointingToClose.current = false
|
||||
}, [])
|
||||
|
||||
return { handlePointerDown, handleClick }
|
||||
}
|
Loading…
Reference in a new issue