From 743254a6c6f3efbeb96f88f06d32c6456517ac22 Mon Sep 17 00:00:00 2001 From: Andrew Prifer <2991360+AndrewPrifer@users.noreply.github.com> Date: Wed, 14 Sep 2022 20:05:09 +0700 Subject: [PATCH] Fix popover behavior when popover is open and the trigger is clicked (#211) * Fix popover behavior when open and clicking on trigger * Remove console log * Resolve merge conflicts * Remove destructuring in favor of property access * Extract usePopover return type into an interface * Fix merge --- .../index.tsx | 4 +- .../src/panels/DetailPanel/ProjectDetails.tsx | 6 +-- .../AggregateKeyframeConnector.tsx | 8 ++- .../AggregateKeyframeDot.tsx | 19 ++++--- .../KeyframeEditor/BasicKeyframeConnector.tsx | 8 ++- .../KeyframeEditor/SingleKeyframeDot.tsx | 35 +++++++------ .../LengthIndicator/LengthEditorPopover.tsx | 3 +- .../Right/LengthIndicator/LengthIndicator.tsx | 26 +++++----- .../GraphEditorDotNonScalar.tsx | 28 +++++----- .../KeyframeEditor/GraphEditorDotScalar.tsx | 28 +++++----- .../RightOverlay/Playhead.tsx | 29 ++++++----- .../RightOverlay/PlayheadPositionPopover.tsx | 3 +- .../simpleEditors/RgbaPropEditor.tsx | 51 +++++++++---------- theatre/studio/src/toolbars/GlobalToolbar.tsx | 8 +-- .../uiComponents/Popover/TooltipWrapper.tsx | 5 +- .../src/uiComponents/Popover/usePopover.tsx | 24 ++++++++- .../src/uiComponents/useOnClickOutside.ts | 12 ++++- 17 files changed, 169 insertions(+), 128 deletions(-) diff --git a/packages/playground/src/shared/hello-world-extension-using-sheet-object/index.tsx b/packages/playground/src/shared/hello-world-extension-using-sheet-object/index.tsx index d32c4b3..88ce27a 100644 --- a/packages/playground/src/shared/hello-world-extension-using-sheet-object/index.tsx +++ b/packages/playground/src/shared/hello-world-extension-using-sheet-object/index.tsx @@ -1,8 +1,8 @@ import React from 'react' import ReactDOM from 'react-dom' import App from './App' -import type {ISheetObject} from '@theatre/core'; -import { onChange, types, val} from '@theatre/core' +import type {ISheetObject} from '@theatre/core' +import {onChange, types, val} from '@theatre/core' import studio from '@theatre/studio' import extension from '@theatre/r3f/dist/extension' diff --git a/theatre/studio/src/panels/DetailPanel/ProjectDetails.tsx b/theatre/studio/src/panels/DetailPanel/ProjectDetails.tsx index cff3fed..987fffc 100644 --- a/theatre/studio/src/panels/DetailPanel/ProjectDetails.tsx +++ b/theatre/studio/src/panels/DetailPanel/ProjectDetails.tsx @@ -62,7 +62,7 @@ const ProjectDetails: React.FC<{ }, 40000) }, []) - const [tooltip, openExportTooltip] = usePopover( + const exportTooltip = usePopover( {debugName: 'ProjectDetails', pointerDistanceThreshold: 50}, () => ( @@ -81,13 +81,13 @@ const ProjectDetails: React.FC<{ return ( <> - {tooltip} + {exportTooltip.node} - openExportTooltip(e, e.target as unknown as HTMLButtonElement) + exportTooltip.open(e, e.target as unknown as HTMLButtonElement) } onClick={!downloaded ? exportProject : undefined} disabled={downloaded} diff --git a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/AggregatedKeyframeTrack/AggregateKeyframeEditor/AggregateKeyframeConnector.tsx b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/AggregatedKeyframeTrack/AggregateKeyframeEditor/AggregateKeyframeConnector.tsx index 99cebe7..218602d 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/AggregatedKeyframeTrack/AggregateKeyframeEditor/AggregateKeyframeConnector.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/AggregatedKeyframeTrack/AggregateKeyframeEditor/AggregateKeyframeConnector.tsx @@ -55,7 +55,11 @@ export const AggregateKeyframeConnector: React.VFC { const rightDims = val(editorProps.layoutP.rightDims) @@ -89,7 +93,7 @@ export const AggregateKeyframeConnector: React.VFC { - if (node) openPopover(e, node) + if (node) togglePopover(e, node) }} /> {popoverNode} diff --git a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/AggregatedKeyframeTrack/AggregateKeyframeEditor/AggregateKeyframeDot.tsx b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/AggregatedKeyframeTrack/AggregateKeyframeEditor/AggregateKeyframeDot.tsx index 1e6241e..d2c01c8 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/AggregatedKeyframeTrack/AggregateKeyframeEditor/AggregateKeyframeDot.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/AggregatedKeyframeTrack/AggregateKeyframeEditor/AggregateKeyframeDot.tsx @@ -99,14 +99,13 @@ export function AggregateKeyframeDot( const logger = useLogger('AggregateKeyframeDot') const {cur} = props.utils - const [inlineEditorPopover, openEditor, _, isInlineEditorPopoverOpen] = - useKeyframeInlineEditorPopover( - props.editorProps.viewModel.type === 'sheetObject' - ? sheetObjectBuild(props.editorProps.viewModel, cur.keyframes) - ?.children ?? null - : propWithChildrenBuild(props.editorProps.viewModel, cur.keyframes) - ?.children ?? null, - ) + const inlineEditorPopover = useKeyframeInlineEditorPopover( + props.editorProps.viewModel.type === 'sheetObject' + ? sheetObjectBuild(props.editorProps.viewModel, cur.keyframes) + ?.children ?? null + : propWithChildrenBuild(props.editorProps.viewModel, cur.keyframes) + ?.children ?? null, + ) const presence = usePresence(props.utils.itemKey) presence.useRelations( @@ -136,7 +135,7 @@ export function AggregateKeyframeDot( // Need this for the dragging logic to be able to get the keyframe props // based on the position. {...DopeSnap.includePositionSnapAttrs(cur.position)} - onClick={(e) => openEditor(e, ref.current!)} + onClick={(e) => inlineEditorPopover.open(e, ref.current!)} /> {contextMenu} - {inlineEditorPopover} + {inlineEditorPopover.node} ) } diff --git a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/BasicKeyframeConnector.tsx b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/BasicKeyframeConnector.tsx index e3767e4..0e2e0a0 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/BasicKeyframeConnector.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/BasicKeyframeConnector.tsx @@ -41,7 +41,11 @@ const BasicKeyframeConnector: React.VFC = ( const [nodeRef, node] = useRefAndState(null) - const [popoverNode, openPopover, closePopover, isPopoverOpen] = usePopover( + const { + node: popoverNode, + toggle: togglePopover, + close: closePopover, + } = usePopover( () => { const rightDims = val(props.layoutP.rightDims) return { @@ -83,7 +87,7 @@ const BasicKeyframeConnector: React.VFC = ( connectorLengthInUnitSpace={connectorLengthInUnitSpace} {...themeValues} openPopover={(e) => { - if (node) openPopover(e, node) + if (node) togglePopover(e, node) }} > {popoverNode} diff --git a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/SingleKeyframeDot.tsx b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/SingleKeyframeDot.tsx index 7274f8c..a675d0b 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/SingleKeyframeDot.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/SingleKeyframeDot.tsx @@ -101,20 +101,23 @@ const SingleKeyframeDot: React.VFC = (props) => { const [ref, node] = useRefAndState(null) const [contextMenu] = useSingleKeyframeContextMenu(node, logger, props) - const [inlineEditorPopover, openEditor, _, isInlineEditorPopoverOpen] = - useKeyframeInlineEditorPopover([ - { - type: 'primitiveProp', - keyframe: props.keyframe, - pathToProp: props.leaf.pathToProp, - propConfig: props.leaf.propConf, - sheetObject: props.leaf.sheetObject, - trackId: props.leaf.trackId, - }, - ]) + const { + node: inlineEditorPopover, + toggle: toggleEditor, + isOpen: isInlineEditorPopoverOpen, + } = useKeyframeInlineEditorPopover([ + { + type: 'primitiveProp', + keyframe: props.keyframe, + pathToProp: props.leaf.pathToProp, + propConfig: props.leaf.propConf, + sheetObject: props.leaf.sheetObject, + trackId: props.leaf.trackId, + }, + ]) const [isDragging] = useDragForSingleKeyframeDot(node, props, { onClickFromDrag(dragStartEvent) { - openEditor(dragStartEvent, ref.current!) + toggleEditor(dragStartEvent, ref.current!) }, }) @@ -213,6 +216,8 @@ function useDragForSingleKeyframeDot( const propsRef = useRef(props) propsRef.current = props + const {onClickFromDrag} = options + const useDragOpts = useMemo(() => { return { debugName: 'KeyframeDot/useDragKeyframe', @@ -268,7 +273,7 @@ function useDragForSingleKeyframeDot( return ( handlers && { ...handlers, - onClick: options.onClickFromDrag, + onClick: onClickFromDrag, onDragEnd: (...args) => { handlers.onDragEnd?.(...args) snapToNone() @@ -324,12 +329,12 @@ function useDragForSingleKeyframeDot( snapToNone() }, onClick(ev) { - options.onClickFromDrag(ev) + onClickFromDrag(ev) }, } }, } - }, []) + }, [onClickFromDrag]) const [isDragging] = useDrag(node, useDragOpts) diff --git a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/LengthIndicator/LengthEditorPopover.tsx b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/LengthIndicator/LengthEditorPopover.tsx index 006f5de..82dbb5d 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/LengthIndicator/LengthEditorPopover.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/LengthIndicator/LengthEditorPopover.tsx @@ -32,7 +32,7 @@ const LengthEditorPopover: React.FC<{ * Called when user hits enter/escape */ onRequestClose: (reason: string) => void -}> = ({layoutP, onRequestClose}) => { +}> = ({layoutP}) => { const sheet = useVal(layoutP.sheet) const fns = useMemo(() => { @@ -89,7 +89,6 @@ const LengthEditorPopover: React.FC<{ {...fns} isValid={greaterThanZero} inputRef={inputRef} - onBlur={onRequestClose.bind(null, 'length editor number input blur')} nudge={nudge} /> diff --git a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/LengthIndicator/LengthIndicator.tsx b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/LengthIndicator/LengthIndicator.tsx index fb81d37..461aebf 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/LengthIndicator/LengthIndicator.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/LengthIndicator/LengthIndicator.tsx @@ -138,19 +138,17 @@ const RENDER_OUT_OF_VIEW_X = -10000 const LengthIndicator: React.FC = ({layoutP}) => { const [nodeRef, node] = useRefAndState(null) const [isDragging] = useDragBulge(node, {layoutP}) - const [popoverNode, openPopover, closePopover, isPopoverOpen] = usePopover( - {debugName: 'LengthIndicator'}, - () => { - return ( - - - - ) - }, - ) + const { + node: popoverNode, + toggle: togglePopover, + close: closePopover, + } = usePopover({debugName: 'LengthIndicator'}, () => { + return ( + + + + ) + }) return usePrism(() => { const sheet = val(layoutP.sheet) @@ -191,7 +189,7 @@ const LengthIndicator: React.FC = ({layoutP}) => { ref={nodeRef} // title="Length of the sequence. Drag or click to change." onClick={(e) => { - openPopover(e, node!) + togglePopover(e, node!) }} {...includeLockFrameStampAttrs('hide')} > diff --git a/theatre/studio/src/panels/SequenceEditorPanel/GraphEditor/BasicKeyframedTrack/KeyframeEditor/GraphEditorDotNonScalar.tsx b/theatre/studio/src/panels/SequenceEditorPanel/GraphEditor/BasicKeyframedTrack/KeyframeEditor/GraphEditorDotNonScalar.tsx index 2d92a3e..7440d6a 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/GraphEditor/BasicKeyframedTrack/KeyframeEditor/GraphEditorDotNonScalar.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/GraphEditor/BasicKeyframedTrack/KeyframeEditor/GraphEditorDotNonScalar.tsx @@ -70,24 +70,26 @@ const GraphEditorDotNonScalar: React.VFC = (props) => { const curValue = props.which === 'left' ? 0 : 1 - const [inlineEditorPopover, openEditor, _, _isInlineEditorPopoverOpen] = - useKeyframeInlineEditorPopover([ - { - type: 'primitiveProp', - keyframe: props.keyframe, - pathToProp: props.pathToProp, - propConfig: props.propConfig, - sheetObject: props.sheetObject, - trackId: props.trackId, - }, - ]) + const inlineEditorPopover = useKeyframeInlineEditorPopover([ + { + type: 'primitiveProp', + keyframe: props.keyframe, + pathToProp: props.pathToProp, + propConfig: props.propConfig, + sheetObject: props.sheetObject, + trackId: props.trackId, + }, + ]) const isDragging = useDragKeyframe({ node, props, // dragging does not work with also having a click listener onDetectedClick: (event) => - openEditor(event, event.target instanceof Element ? event.target : node!), + inlineEditorPopover.toggle( + event, + event.target instanceof Element ? event.target : node!, + ), }) const cyInExtremumSpace = props.extremumSpace.fromValueSpace(curValue) @@ -114,7 +116,7 @@ const GraphEditorDotNonScalar: React.VFC = (props) => { fill: presence.flag === PresenceFlag.Primary ? 'white' : undefined, }} /> - {inlineEditorPopover} + {inlineEditorPopover.node} {contextMenu} ) diff --git a/theatre/studio/src/panels/SequenceEditorPanel/GraphEditor/BasicKeyframedTrack/KeyframeEditor/GraphEditorDotScalar.tsx b/theatre/studio/src/panels/SequenceEditorPanel/GraphEditor/BasicKeyframedTrack/KeyframeEditor/GraphEditorDotScalar.tsx index 1e37624..4e472fb 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/GraphEditor/BasicKeyframedTrack/KeyframeEditor/GraphEditorDotScalar.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/GraphEditor/BasicKeyframedTrack/KeyframeEditor/GraphEditorDotScalar.tsx @@ -70,24 +70,26 @@ const GraphEditorDotScalar: React.VFC = (props) => { const curValue = cur.value as number const cyInExtremumSpace = props.extremumSpace.fromValueSpace(curValue) - const [inlineEditorPopover, openEditor, _, _isInlineEditorPopoverOpen] = - useKeyframeInlineEditorPopover([ - { - type: 'primitiveProp', - keyframe: props.keyframe, - pathToProp: props.pathToProp, - propConfig: props.propConfig, - sheetObject: props.sheetObject, - trackId: props.trackId, - }, - ]) + const inlineEditorPopover = useKeyframeInlineEditorPopover([ + { + type: 'primitiveProp', + keyframe: props.keyframe, + pathToProp: props.pathToProp, + propConfig: props.propConfig, + sheetObject: props.sheetObject, + trackId: props.trackId, + }, + ]) const isDragging = useDragKeyframe({ node, props, // dragging does not work with also having a click listener onDetectedClick: (event) => - openEditor(event, event.target instanceof Element ? event.target : node!), + inlineEditorPopover.toggle( + event, + event.target instanceof Element ? event.target : node!, + ), }) return ( @@ -112,7 +114,7 @@ const GraphEditorDotScalar: React.VFC = (props) => { fill: presence.flag === PresenceFlag.Primary ? 'white' : undefined, }} /> - {inlineEditorPopover} + {inlineEditorPopover.node} {contextMenu} ) diff --git a/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/Playhead.tsx b/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/Playhead.tsx index 6af70b5..b19ac57 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/Playhead.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/Playhead.tsx @@ -193,19 +193,20 @@ const Playhead: React.FC<{layoutP: Pointer}> = ({ }) => { const [thumbRef, thumbNode] = useRefAndState(null) - const [popoverNode, openPopover, closePopover, isPopoverOpen] = usePopover( - {debugName: 'Playhead'}, - () => { - return ( - - - - ) - }, - ) + const { + node: popoverNode, + toggle: togglePopover, + close: closePopover, + } = usePopover({debugName: 'Playhead'}, () => { + return ( + + + + ) + }) const gestureHandlers = useMemo((): Parameters[1] => { return { @@ -236,7 +237,7 @@ const Playhead: React.FC<{layoutP: Pointer}> = ({ snapToNone() }, onClick(e) { - openPopover(e, thumbRef.current!) + togglePopover(e, thumbRef.current!) }, } }, diff --git a/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/PlayheadPositionPopover.tsx b/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/PlayheadPositionPopover.tsx index 0f704a4..da776c9 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/PlayheadPositionPopover.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/PlayheadPositionPopover.tsx @@ -33,7 +33,7 @@ const PlayheadPositionPopover: React.FC<{ * Called when user hits enter/escape */ onRequestClose: (reason: string) => void -}> = ({layoutP, onRequestClose}) => { +}> = ({layoutP}) => { const sheet = val(layoutP.sheet) const sequence = sheet.getSequence() @@ -80,7 +80,6 @@ const PlayheadPositionPopover: React.FC<{ {...fns} isValid={greaterThanOrEqualToZero} inputRef={inputRef} - onBlur={onRequestClose.bind(null, 'number input blur')} nudge={nudge} /> diff --git a/theatre/studio/src/propEditors/simpleEditors/RgbaPropEditor.tsx b/theatre/studio/src/propEditors/simpleEditors/RgbaPropEditor.tsx index f9a5e26..b816b97 100644 --- a/theatre/studio/src/propEditors/simpleEditors/RgbaPropEditor.tsx +++ b/theatre/studio/src/propEditors/simpleEditors/RgbaPropEditor.tsx @@ -75,31 +75,28 @@ function RgbaPropEditor({ [editingTools], ) - const [popoverNode, openPopover] = usePopover( - {debugName: 'RgbaPropEditor'}, - () => ( - - { - const rgba = decorateRgba(color) - editingTools.temporarilySetValue(rgba) - }} - permanentlySetValue={(color) => { - // console.log('perm') - const rgba = decorateRgba(color) - editingTools.permanentlySetValue(rgba) - }} - discardTemporaryValue={editingTools.discardTemporaryValue} - /> - - ), - ) + const popover = usePopover({debugName: 'RgbaPropEditor'}, () => ( + + { + const rgba = decorateRgba(color) + editingTools.temporarilySetValue(rgba) + }} + permanentlySetValue={(color) => { + // console.log('perm') + const rgba = decorateRgba(color) + editingTools.permanentlySetValue(rgba) + }} + discardTemporaryValue={editingTools.discardTemporaryValue} + /> + + )) return ( <> @@ -108,7 +105,7 @@ function RgbaPropEditor({ rgbaColor={value} ref={containerRef} onClick={(e) => { - openPopover(e, containerRef.current) + popover.toggle(e, containerRef.current) }} /> - {popoverNode} + {popover.node} ) } diff --git a/theatre/studio/src/toolbars/GlobalToolbar.tsx b/theatre/studio/src/toolbars/GlobalToolbar.tsx index c42b5dc..4efe57b 100644 --- a/theatre/studio/src/toolbars/GlobalToolbar.tsx +++ b/theatre/studio/src/toolbars/GlobalToolbar.tsx @@ -58,7 +58,7 @@ const HasUpdatesBadge = styled.div` ` const GroupDivider = styled.div` - position: abolute; + position: absolute; height: 32px; width: 1px; background: #373b40; @@ -98,7 +98,7 @@ const GlobalToolbar: React.FC = () => { const hasUpdates = useVal(getStudio().atomP.ahistoric.updateChecker.result.hasUpdates) === true - const [moreMenu, openMoreMenu] = usePopover( + const moreMenu = usePopover( () => { const triggerBounds = moreMenuTriggerRef.current!.getBoundingClientRect() return { @@ -166,11 +166,11 @@ const GlobalToolbar: React.FC = () => { - {moreMenu} + {moreMenu.node} { - openMoreMenu(e, moreMenuTriggerRef.current!) + moreMenu.toggle(e, moreMenuTriggerRef.current!) }} > diff --git a/theatre/studio/src/uiComponents/Popover/TooltipWrapper.tsx b/theatre/studio/src/uiComponents/Popover/TooltipWrapper.tsx index 4ebb598..edea95d 100644 --- a/theatre/studio/src/uiComponents/Popover/TooltipWrapper.tsx +++ b/theatre/studio/src/uiComponents/Popover/TooltipWrapper.tsx @@ -138,7 +138,10 @@ const TooltipWrapper: React.FC<{ props.onPointerOutside, ]) - useOnClickOutside(container, props.onClickOutside ?? noop) + useOnClickOutside( + [container, props.target ?? null], + props.onClickOutside ?? noop, + ) return ( diff --git a/theatre/studio/src/uiComponents/Popover/usePopover.tsx b/theatre/studio/src/uiComponents/Popover/usePopover.tsx index dc47509..27a877d 100644 --- a/theatre/studio/src/uiComponents/Popover/usePopover.tsx +++ b/theatre/studio/src/uiComponents/Popover/usePopover.tsx @@ -48,10 +48,22 @@ type Opts = { verticalGap?: number } +export interface IPopover { + /** + * The React node of the popover. Insert into your JSX using \{node\}. Its state + * will be managed automatically. + */ + node: React.ReactNode + open: OpenFn + close: CloseFn + toggle: OpenFn + isOpen: boolean +} + export default function usePopover( opts: Opts | (() => Opts), render: () => React.ReactElement, -): [node: React.ReactNode, open: OpenFn, close: CloseFn, isOpen: boolean] { +): IPopover { const _debug = (...args: any) => {} // want to make sure that we don't close a popover when dragging something (like a curve editor handle) @@ -104,6 +116,14 @@ export default function usePopover( } }, []) + const toggle = useCallback((...args) => { + if (stateRef.current.isOpen) { + close('toggled') + } else { + open(...args) + } + }, []) + /** * See doc comment on {@link useAutoCloseLockState}. * Used to ensure that moving far away from a parent popover doesn't @@ -146,7 +166,7 @@ export default function usePopover( <> ) - return [node, open, close, state.isOpen] + return {node, open, close, toggle, isOpen: state.isOpen} } /** diff --git a/theatre/studio/src/uiComponents/useOnClickOutside.ts b/theatre/studio/src/uiComponents/useOnClickOutside.ts index 96d533f..31e8fce 100644 --- a/theatre/studio/src/uiComponents/useOnClickOutside.ts +++ b/theatre/studio/src/uiComponents/useOnClickOutside.ts @@ -2,15 +2,23 @@ import type {$IntentionalAny} from '@theatre/shared/utils/types' import {useEffect} from 'react' export default function useOnClickOutside( - container: Element | null, + container: Element | null | (Element | null)[], onOutside: (e: MouseEvent) => void, enabled?: boolean, + // Can be used e.g. to prevent unexpected closing-reopening when clicking on a + // popover's trigger. ) { useEffect(() => { if (!container || enabled === false) return + const containers = Array.isArray(container) + ? (container.filter((container) => container) as Element[]) + : [container] + const onMouseDown = (e: MouseEvent) => { - if (!e.composedPath().includes(container)) { + if ( + containers.every((container) => !e.composedPath().includes(container)) + ) { onOutside(e) } }