[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:
Steve Ruiz 2023-10-17 14:06:53 +01:00 committed by GitHub
parent ca609a0fa5
commit 3b7acb8997
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 46 additions and 8 deletions

View file

@ -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

View file

@ -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) {

View file

@ -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')

View file

@ -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 {

View file

@ -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>
)
}

View file

@ -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>

View file

@ -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)),
})
}

View 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 }
}