Fix clicking off the context menu (#2355)

Fixes https://github.com/tldraw/tldraw/issues/2357

This PR fixes not being able to close the context menu by clicking
- your selected shape
- the ui

It seems like the Radix's `modal` setting causes trouble for us. I think
we're better off turning it off.
We now show an indicator on hovered shapes when the context menu. This
is consistent with how our other menus currently work.

![2023-12-19 at 14 19 14 - Cyan
Bovid](https://github.com/tldraw/tldraw/assets/15892272/88b492c2-8f3b-40bc-9dec-744fe72cda3b)

![2023-12-19 at 14 21 36 - Amaranth
Vulture](https://github.com/tldraw/tldraw/assets/15892272/1f19751d-499b-40c3-9b28-9f41a2f27ab2)

### Change Type

- [x] `patch` — Bug fix
- [ ] `minor` — New feature
- [ ] `major` — Breaking change
- [ ] `dependencies` — Changes to package dependencies[^1]
- [ ] `documentation` — Changes to the documentation only[^2]
- [ ] `tests` — Changes to any test code only[^2]
- [ ] `internal` — Any other changes that don't affect the published
package[^2]
- [ ] I don't know

[^1]: publishes a `patch` release, for devDependencies use `internal`
[^2]: will not publish a new version

### Test Plan

1. Add a step-by-step description of how to test your PR here.
2.

- [ ] Unit Tests
- [ ] End to end tests

### Release Notes

- Fix not being able to close the context menu by clicking on the UI or
your selected shape.

---------

Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
This commit is contained in:
Lu Wilson 2023-12-20 14:39:34 +00:00 committed by GitHub
parent 95e54e46d3
commit 6549ab70e2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 57 additions and 9 deletions

View file

@ -585,6 +585,7 @@ export class Editor extends EventEmitter<TLEventMap> {
// @internal (undocumented) // @internal (undocumented)
capturedPointerId: null | number; capturedPointerId: null | number;
centerOnPoint(point: VecLike, animation?: TLAnimationOptions): this; centerOnPoint(point: VecLike, animation?: TLAnimationOptions): this;
clearOpenMenus(): this;
// @internal // @internal
protected _clickManager: ClickManager; protected _clickManager: ClickManager;
complete(): this; complete(): this;

View file

@ -7544,6 +7544,37 @@
"isAbstract": false, "isAbstract": false,
"name": "centerOnPoint" "name": "centerOnPoint"
}, },
{
"kind": "Method",
"canonicalReference": "@tldraw/editor!Editor#clearOpenMenus:member(1)",
"docComment": "/**\n * Clear all open menus.\n *\n * @example\n * ```ts\n * editor.clearOpenMenus()\n * ```\n *\n * @public\n */\n",
"excerptTokens": [
{
"kind": "Content",
"text": "clearOpenMenus(): "
},
{
"kind": "Content",
"text": "this"
},
{
"kind": "Content",
"text": ";"
}
],
"isStatic": false,
"returnTypeTokenRange": {
"startIndex": 1,
"endIndex": 2
},
"releaseTag": "Public",
"isProtected": false,
"overloadIndex": 1,
"parameters": [],
"isOptional": false,
"isAbstract": false,
"name": "clearOpenMenus"
},
{ {
"kind": "Method", "kind": "Method",
"canonicalReference": "@tldraw/editor!Editor#complete:member(1)", "canonicalReference": "@tldraw/editor!Editor#complete:member(1)",

View file

@ -1309,6 +1309,23 @@ export class Editor extends EventEmitter<TLEventMap> {
return this return this
} }
/**
* Clear all open menus.
*
* @example
* ```ts
* editor.clearOpenMenus()
* ```
*
* @public
*/
clearOpenMenus(): this {
if (this.getOpenMenus().length) {
this.updateInstanceState({ openMenus: [] })
}
return this
}
/** /**
* Get whether any menus are open. * Get whether any menus are open.
* *
@ -8696,6 +8713,8 @@ export class Editor extends EventEmitter<TLEventMap> {
switch (info.name) { switch (info.name) {
case 'pointer_down': { case 'pointer_down': {
this.clearOpenMenus()
this._selectedShapeIdsAtPointerDown = this.getSelectedShapeIds() this._selectedShapeIdsAtPointerDown = this.getSelectedShapeIds()
// Firefox bug fix... // Firefox bug fix...

View file

@ -15722,7 +15722,7 @@
"text": "export interface TLUiContextMenuProps " "text": "export interface TLUiContextMenuProps "
} }
], ],
"fileUrlPath": "packages/tldraw/.tsbuild-api/lib/ui/components/ContextMenu.d.ts", "fileUrlPath": "packages/tldraw/src/lib/ui/components/ContextMenu.tsx",
"releaseTag": "Public", "releaseTag": "Public",
"name": "TLUiContextMenuProps", "name": "TLUiContextMenuProps",
"preserveMemberOrder": false, "preserveMemberOrder": false,
@ -15745,7 +15745,6 @@
"text": ";" "text": ";"
} }
], ],
"fileUrlPath": "packages/tldraw/src/lib/ui/components/ContextMenu.tsx",
"isReadonly": false, "isReadonly": false,
"isOptional": false, "isOptional": false,
"releaseTag": "Public", "releaseTag": "Public",

View file

@ -65,9 +65,7 @@ export const ContextMenu = function ContextMenu({ children }: { children: any })
[editor] [editor]
) )
const container = useContainer() const [isOpen, handleOpenChange] = useMenuIsOpen('context menu', cb)
const [_, handleOpenChange] = useMenuIsOpen('context menu', cb)
// If every item in the menu is readonly, then we don't want to show the menu // If every item in the menu is readonly, then we don't want to show the menu
const isReadonly = useReadonly() const isReadonly = useReadonly()
@ -76,6 +74,7 @@ export const ContextMenu = function ContextMenu({ children }: { children: any })
contextTLUiMenuSchema.length === 0 || contextTLUiMenuSchema.length === 0 ||
(isReadonly && contextTLUiMenuSchema.every((item) => !item.readonlyOk)) (isReadonly && contextTLUiMenuSchema.every((item) => !item.readonlyOk))
// Todo: remove this dependency on the select tool; not sure how else to say "only show the context menu when we're using a tool that supports it"
const selectToolActive = useValue( const selectToolActive = useValue(
'isSelectToolActive', 'isSelectToolActive',
() => editor.getCurrentToolId() === 'select', () => editor.getCurrentToolId() === 'select',
@ -85,7 +84,7 @@ export const ContextMenu = function ContextMenu({ children }: { children: any })
const disabled = !selectToolActive || noItemsToShow const disabled = !selectToolActive || noItemsToShow
return ( return (
<_ContextMenu.Root dir="ltr" onOpenChange={handleOpenChange}> <_ContextMenu.Root dir="ltr" onOpenChange={handleOpenChange} modal={false}>
<_ContextMenu.Trigger <_ContextMenu.Trigger
onContextMenu={disabled ? preventDefault : undefined} onContextMenu={disabled ? preventDefault : undefined}
dir="ltr" dir="ltr"
@ -93,9 +92,7 @@ export const ContextMenu = function ContextMenu({ children }: { children: any })
> >
{children} {children}
</_ContextMenu.Trigger> </_ContextMenu.Trigger>
<_ContextMenu.Portal container={container}> {isOpen && <ContextMenuContent />}
<ContextMenuContent />
</_ContextMenu.Portal>
</_ContextMenu.Root> </_ContextMenu.Root>
) )
} }
@ -104,6 +101,7 @@ const ContextMenuContent = forwardRef(function ContextMenuContent() {
const editor = useEditor() const editor = useEditor()
const msg = useTranslation() const msg = useTranslation()
const menuSchema = useContextMenuSchema() const menuSchema = useContextMenuSchema()
const [_, handleSubOpenChange] = useMenuIsOpen('context menu sub') const [_, handleSubOpenChange] = useMenuIsOpen('context menu sub')
const isReadonly = useReadonly() const isReadonly = useReadonly()