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
This commit is contained in:
parent
8680f9d89e
commit
743254a6c6
17 changed files with 169 additions and 128 deletions
|
@ -1,8 +1,8 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import ReactDOM from 'react-dom'
|
import ReactDOM from 'react-dom'
|
||||||
import App from './App'
|
import App from './App'
|
||||||
import type {ISheetObject} from '@theatre/core';
|
import type {ISheetObject} from '@theatre/core'
|
||||||
import { onChange, types, val} from '@theatre/core'
|
import {onChange, types, val} from '@theatre/core'
|
||||||
import studio from '@theatre/studio'
|
import studio from '@theatre/studio'
|
||||||
import extension from '@theatre/r3f/dist/extension'
|
import extension from '@theatre/r3f/dist/extension'
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,7 @@ const ProjectDetails: React.FC<{
|
||||||
}, 40000)
|
}, 40000)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const [tooltip, openExportTooltip] = usePopover(
|
const exportTooltip = usePopover(
|
||||||
{debugName: 'ProjectDetails', pointerDistanceThreshold: 50},
|
{debugName: 'ProjectDetails', pointerDistanceThreshold: 50},
|
||||||
() => (
|
() => (
|
||||||
<ExportTooltip>
|
<ExportTooltip>
|
||||||
|
@ -81,13 +81,13 @@ const ProjectDetails: React.FC<{
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{tooltip}
|
{exportTooltip.node}
|
||||||
<Container>
|
<Container>
|
||||||
<StateConflictRow projectId={projectId} />
|
<StateConflictRow projectId={projectId} />
|
||||||
<TheExportRow>
|
<TheExportRow>
|
||||||
<DetailPanelButton
|
<DetailPanelButton
|
||||||
onMouseEnter={(e) =>
|
onMouseEnter={(e) =>
|
||||||
openExportTooltip(e, e.target as unknown as HTMLButtonElement)
|
exportTooltip.open(e, e.target as unknown as HTMLButtonElement)
|
||||||
}
|
}
|
||||||
onClick={!downloaded ? exportProject : undefined}
|
onClick={!downloaded ? exportProject : undefined}
|
||||||
disabled={downloaded}
|
disabled={downloaded}
|
||||||
|
|
|
@ -55,7 +55,11 @@ export const AggregateKeyframeConnector: React.VFC<IAggregateKeyframeConnectorPr
|
||||||
const [contextMenu] = useConnectorContextMenu(props, node)
|
const [contextMenu] = useConnectorContextMenu(props, node)
|
||||||
const [isDragging] = useDragKeyframe(node, props.editorProps)
|
const [isDragging] = useDragKeyframe(node, props.editorProps)
|
||||||
|
|
||||||
const [popoverNode, openPopover, closePopover] = usePopover(
|
const {
|
||||||
|
node: popoverNode,
|
||||||
|
toggle: togglePopover,
|
||||||
|
close: closePopover,
|
||||||
|
} = usePopover(
|
||||||
() => {
|
() => {
|
||||||
const rightDims = val(editorProps.layoutP.rightDims)
|
const rightDims = val(editorProps.layoutP.rightDims)
|
||||||
|
|
||||||
|
@ -89,7 +93,7 @@ export const AggregateKeyframeConnector: React.VFC<IAggregateKeyframeConnectorPr
|
||||||
isSelected={connected ? connected.selected : false}
|
isSelected={connected ? connected.selected : false}
|
||||||
isPopoverOpen={isAggregateEditingInCurvePopover}
|
isPopoverOpen={isAggregateEditingInCurvePopover}
|
||||||
openPopover={(e) => {
|
openPopover={(e) => {
|
||||||
if (node) openPopover(e, node)
|
if (node) togglePopover(e, node)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{popoverNode}
|
{popoverNode}
|
||||||
|
|
|
@ -99,14 +99,13 @@ export function AggregateKeyframeDot(
|
||||||
const logger = useLogger('AggregateKeyframeDot')
|
const logger = useLogger('AggregateKeyframeDot')
|
||||||
const {cur} = props.utils
|
const {cur} = props.utils
|
||||||
|
|
||||||
const [inlineEditorPopover, openEditor, _, isInlineEditorPopoverOpen] =
|
const inlineEditorPopover = useKeyframeInlineEditorPopover(
|
||||||
useKeyframeInlineEditorPopover(
|
props.editorProps.viewModel.type === 'sheetObject'
|
||||||
props.editorProps.viewModel.type === 'sheetObject'
|
? sheetObjectBuild(props.editorProps.viewModel, cur.keyframes)
|
||||||
? sheetObjectBuild(props.editorProps.viewModel, cur.keyframes)
|
?.children ?? null
|
||||||
?.children ?? null
|
: propWithChildrenBuild(props.editorProps.viewModel, cur.keyframes)
|
||||||
: propWithChildrenBuild(props.editorProps.viewModel, cur.keyframes)
|
?.children ?? null,
|
||||||
?.children ?? null,
|
)
|
||||||
)
|
|
||||||
|
|
||||||
const presence = usePresence(props.utils.itemKey)
|
const presence = usePresence(props.utils.itemKey)
|
||||||
presence.useRelations(
|
presence.useRelations(
|
||||||
|
@ -136,7 +135,7 @@ export function AggregateKeyframeDot(
|
||||||
// Need this for the dragging logic to be able to get the keyframe props
|
// Need this for the dragging logic to be able to get the keyframe props
|
||||||
// based on the position.
|
// based on the position.
|
||||||
{...DopeSnap.includePositionSnapAttrs(cur.position)}
|
{...DopeSnap.includePositionSnapAttrs(cur.position)}
|
||||||
onClick={(e) => openEditor(e, ref.current!)}
|
onClick={(e) => inlineEditorPopover.open(e, ref.current!)}
|
||||||
/>
|
/>
|
||||||
<AggregateKeyframeVisualDot
|
<AggregateKeyframeVisualDot
|
||||||
flag={presence.flag}
|
flag={presence.flag}
|
||||||
|
@ -144,7 +143,7 @@ export function AggregateKeyframeDot(
|
||||||
isSelected={cur.selected}
|
isSelected={cur.selected}
|
||||||
/>
|
/>
|
||||||
{contextMenu}
|
{contextMenu}
|
||||||
{inlineEditorPopover}
|
{inlineEditorPopover.node}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,11 @@ const BasicKeyframeConnector: React.VFC<IBasicKeyframeConnectorProps> = (
|
||||||
|
|
||||||
const [nodeRef, node] = useRefAndState<HTMLDivElement | null>(null)
|
const [nodeRef, node] = useRefAndState<HTMLDivElement | null>(null)
|
||||||
|
|
||||||
const [popoverNode, openPopover, closePopover, isPopoverOpen] = usePopover(
|
const {
|
||||||
|
node: popoverNode,
|
||||||
|
toggle: togglePopover,
|
||||||
|
close: closePopover,
|
||||||
|
} = usePopover(
|
||||||
() => {
|
() => {
|
||||||
const rightDims = val(props.layoutP.rightDims)
|
const rightDims = val(props.layoutP.rightDims)
|
||||||
return {
|
return {
|
||||||
|
@ -83,7 +87,7 @@ const BasicKeyframeConnector: React.VFC<IBasicKeyframeConnectorProps> = (
|
||||||
connectorLengthInUnitSpace={connectorLengthInUnitSpace}
|
connectorLengthInUnitSpace={connectorLengthInUnitSpace}
|
||||||
{...themeValues}
|
{...themeValues}
|
||||||
openPopover={(e) => {
|
openPopover={(e) => {
|
||||||
if (node) openPopover(e, node)
|
if (node) togglePopover(e, node)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{popoverNode}
|
{popoverNode}
|
||||||
|
|
|
@ -101,20 +101,23 @@ const SingleKeyframeDot: React.VFC<ISingleKeyframeDotProps> = (props) => {
|
||||||
const [ref, node] = useRefAndState<HTMLDivElement | null>(null)
|
const [ref, node] = useRefAndState<HTMLDivElement | null>(null)
|
||||||
|
|
||||||
const [contextMenu] = useSingleKeyframeContextMenu(node, logger, props)
|
const [contextMenu] = useSingleKeyframeContextMenu(node, logger, props)
|
||||||
const [inlineEditorPopover, openEditor, _, isInlineEditorPopoverOpen] =
|
const {
|
||||||
useKeyframeInlineEditorPopover([
|
node: inlineEditorPopover,
|
||||||
{
|
toggle: toggleEditor,
|
||||||
type: 'primitiveProp',
|
isOpen: isInlineEditorPopoverOpen,
|
||||||
keyframe: props.keyframe,
|
} = useKeyframeInlineEditorPopover([
|
||||||
pathToProp: props.leaf.pathToProp,
|
{
|
||||||
propConfig: props.leaf.propConf,
|
type: 'primitiveProp',
|
||||||
sheetObject: props.leaf.sheetObject,
|
keyframe: props.keyframe,
|
||||||
trackId: props.leaf.trackId,
|
pathToProp: props.leaf.pathToProp,
|
||||||
},
|
propConfig: props.leaf.propConf,
|
||||||
])
|
sheetObject: props.leaf.sheetObject,
|
||||||
|
trackId: props.leaf.trackId,
|
||||||
|
},
|
||||||
|
])
|
||||||
const [isDragging] = useDragForSingleKeyframeDot(node, props, {
|
const [isDragging] = useDragForSingleKeyframeDot(node, props, {
|
||||||
onClickFromDrag(dragStartEvent) {
|
onClickFromDrag(dragStartEvent) {
|
||||||
openEditor(dragStartEvent, ref.current!)
|
toggleEditor(dragStartEvent, ref.current!)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -213,6 +216,8 @@ function useDragForSingleKeyframeDot(
|
||||||
const propsRef = useRef(props)
|
const propsRef = useRef(props)
|
||||||
propsRef.current = props
|
propsRef.current = props
|
||||||
|
|
||||||
|
const {onClickFromDrag} = options
|
||||||
|
|
||||||
const useDragOpts = useMemo<UseDragOpts>(() => {
|
const useDragOpts = useMemo<UseDragOpts>(() => {
|
||||||
return {
|
return {
|
||||||
debugName: 'KeyframeDot/useDragKeyframe',
|
debugName: 'KeyframeDot/useDragKeyframe',
|
||||||
|
@ -268,7 +273,7 @@ function useDragForSingleKeyframeDot(
|
||||||
return (
|
return (
|
||||||
handlers && {
|
handlers && {
|
||||||
...handlers,
|
...handlers,
|
||||||
onClick: options.onClickFromDrag,
|
onClick: onClickFromDrag,
|
||||||
onDragEnd: (...args) => {
|
onDragEnd: (...args) => {
|
||||||
handlers.onDragEnd?.(...args)
|
handlers.onDragEnd?.(...args)
|
||||||
snapToNone()
|
snapToNone()
|
||||||
|
@ -324,12 +329,12 @@ function useDragForSingleKeyframeDot(
|
||||||
snapToNone()
|
snapToNone()
|
||||||
},
|
},
|
||||||
onClick(ev) {
|
onClick(ev) {
|
||||||
options.onClickFromDrag(ev)
|
onClickFromDrag(ev)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}, [])
|
}, [onClickFromDrag])
|
||||||
|
|
||||||
const [isDragging] = useDrag(node, useDragOpts)
|
const [isDragging] = useDrag(node, useDragOpts)
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ const LengthEditorPopover: React.FC<{
|
||||||
* Called when user hits enter/escape
|
* Called when user hits enter/escape
|
||||||
*/
|
*/
|
||||||
onRequestClose: (reason: string) => void
|
onRequestClose: (reason: string) => void
|
||||||
}> = ({layoutP, onRequestClose}) => {
|
}> = ({layoutP}) => {
|
||||||
const sheet = useVal(layoutP.sheet)
|
const sheet = useVal(layoutP.sheet)
|
||||||
|
|
||||||
const fns = useMemo(() => {
|
const fns = useMemo(() => {
|
||||||
|
@ -89,7 +89,6 @@ const LengthEditorPopover: React.FC<{
|
||||||
{...fns}
|
{...fns}
|
||||||
isValid={greaterThanZero}
|
isValid={greaterThanZero}
|
||||||
inputRef={inputRef}
|
inputRef={inputRef}
|
||||||
onBlur={onRequestClose.bind(null, 'length editor number input blur')}
|
|
||||||
nudge={nudge}
|
nudge={nudge}
|
||||||
/>
|
/>
|
||||||
</Container>
|
</Container>
|
||||||
|
|
|
@ -138,19 +138,17 @@ const RENDER_OUT_OF_VIEW_X = -10000
|
||||||
const LengthIndicator: React.FC<IProps> = ({layoutP}) => {
|
const LengthIndicator: React.FC<IProps> = ({layoutP}) => {
|
||||||
const [nodeRef, node] = useRefAndState<HTMLDivElement | null>(null)
|
const [nodeRef, node] = useRefAndState<HTMLDivElement | null>(null)
|
||||||
const [isDragging] = useDragBulge(node, {layoutP})
|
const [isDragging] = useDragBulge(node, {layoutP})
|
||||||
const [popoverNode, openPopover, closePopover, isPopoverOpen] = usePopover(
|
const {
|
||||||
{debugName: 'LengthIndicator'},
|
node: popoverNode,
|
||||||
() => {
|
toggle: togglePopover,
|
||||||
return (
|
close: closePopover,
|
||||||
<BasicPopover>
|
} = usePopover({debugName: 'LengthIndicator'}, () => {
|
||||||
<LengthEditorPopover
|
return (
|
||||||
layoutP={layoutP}
|
<BasicPopover>
|
||||||
onRequestClose={closePopover}
|
<LengthEditorPopover layoutP={layoutP} onRequestClose={closePopover} />
|
||||||
/>
|
</BasicPopover>
|
||||||
</BasicPopover>
|
)
|
||||||
)
|
})
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
return usePrism(() => {
|
return usePrism(() => {
|
||||||
const sheet = val(layoutP.sheet)
|
const sheet = val(layoutP.sheet)
|
||||||
|
@ -191,7 +189,7 @@ const LengthIndicator: React.FC<IProps> = ({layoutP}) => {
|
||||||
ref={nodeRef}
|
ref={nodeRef}
|
||||||
// title="Length of the sequence. Drag or click to change."
|
// title="Length of the sequence. Drag or click to change."
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
openPopover(e, node!)
|
togglePopover(e, node!)
|
||||||
}}
|
}}
|
||||||
{...includeLockFrameStampAttrs('hide')}
|
{...includeLockFrameStampAttrs('hide')}
|
||||||
>
|
>
|
||||||
|
|
|
@ -70,24 +70,26 @@ const GraphEditorDotNonScalar: React.VFC<IProps> = (props) => {
|
||||||
|
|
||||||
const curValue = props.which === 'left' ? 0 : 1
|
const curValue = props.which === 'left' ? 0 : 1
|
||||||
|
|
||||||
const [inlineEditorPopover, openEditor, _, _isInlineEditorPopoverOpen] =
|
const inlineEditorPopover = useKeyframeInlineEditorPopover([
|
||||||
useKeyframeInlineEditorPopover([
|
{
|
||||||
{
|
type: 'primitiveProp',
|
||||||
type: 'primitiveProp',
|
keyframe: props.keyframe,
|
||||||
keyframe: props.keyframe,
|
pathToProp: props.pathToProp,
|
||||||
pathToProp: props.pathToProp,
|
propConfig: props.propConfig,
|
||||||
propConfig: props.propConfig,
|
sheetObject: props.sheetObject,
|
||||||
sheetObject: props.sheetObject,
|
trackId: props.trackId,
|
||||||
trackId: props.trackId,
|
},
|
||||||
},
|
])
|
||||||
])
|
|
||||||
|
|
||||||
const isDragging = useDragKeyframe({
|
const isDragging = useDragKeyframe({
|
||||||
node,
|
node,
|
||||||
props,
|
props,
|
||||||
// dragging does not work with also having a click listener
|
// dragging does not work with also having a click listener
|
||||||
onDetectedClick: (event) =>
|
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)
|
const cyInExtremumSpace = props.extremumSpace.fromValueSpace(curValue)
|
||||||
|
@ -114,7 +116,7 @@ const GraphEditorDotNonScalar: React.VFC<IProps> = (props) => {
|
||||||
fill: presence.flag === PresenceFlag.Primary ? 'white' : undefined,
|
fill: presence.flag === PresenceFlag.Primary ? 'white' : undefined,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{inlineEditorPopover}
|
{inlineEditorPopover.node}
|
||||||
{contextMenu}
|
{contextMenu}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|
|
@ -70,24 +70,26 @@ const GraphEditorDotScalar: React.VFC<IProps> = (props) => {
|
||||||
const curValue = cur.value as number
|
const curValue = cur.value as number
|
||||||
|
|
||||||
const cyInExtremumSpace = props.extremumSpace.fromValueSpace(curValue)
|
const cyInExtremumSpace = props.extremumSpace.fromValueSpace(curValue)
|
||||||
const [inlineEditorPopover, openEditor, _, _isInlineEditorPopoverOpen] =
|
const inlineEditorPopover = useKeyframeInlineEditorPopover([
|
||||||
useKeyframeInlineEditorPopover([
|
{
|
||||||
{
|
type: 'primitiveProp',
|
||||||
type: 'primitiveProp',
|
keyframe: props.keyframe,
|
||||||
keyframe: props.keyframe,
|
pathToProp: props.pathToProp,
|
||||||
pathToProp: props.pathToProp,
|
propConfig: props.propConfig,
|
||||||
propConfig: props.propConfig,
|
sheetObject: props.sheetObject,
|
||||||
sheetObject: props.sheetObject,
|
trackId: props.trackId,
|
||||||
trackId: props.trackId,
|
},
|
||||||
},
|
])
|
||||||
])
|
|
||||||
|
|
||||||
const isDragging = useDragKeyframe({
|
const isDragging = useDragKeyframe({
|
||||||
node,
|
node,
|
||||||
props,
|
props,
|
||||||
// dragging does not work with also having a click listener
|
// dragging does not work with also having a click listener
|
||||||
onDetectedClick: (event) =>
|
onDetectedClick: (event) =>
|
||||||
openEditor(event, event.target instanceof Element ? event.target : node!),
|
inlineEditorPopover.toggle(
|
||||||
|
event,
|
||||||
|
event.target instanceof Element ? event.target : node!,
|
||||||
|
),
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -112,7 +114,7 @@ const GraphEditorDotScalar: React.VFC<IProps> = (props) => {
|
||||||
fill: presence.flag === PresenceFlag.Primary ? 'white' : undefined,
|
fill: presence.flag === PresenceFlag.Primary ? 'white' : undefined,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{inlineEditorPopover}
|
{inlineEditorPopover.node}
|
||||||
{contextMenu}
|
{contextMenu}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|
|
@ -193,19 +193,20 @@ const Playhead: React.FC<{layoutP: Pointer<SequenceEditorPanelLayout>}> = ({
|
||||||
}) => {
|
}) => {
|
||||||
const [thumbRef, thumbNode] = useRefAndState<HTMLElement | null>(null)
|
const [thumbRef, thumbNode] = useRefAndState<HTMLElement | null>(null)
|
||||||
|
|
||||||
const [popoverNode, openPopover, closePopover, isPopoverOpen] = usePopover(
|
const {
|
||||||
{debugName: 'Playhead'},
|
node: popoverNode,
|
||||||
() => {
|
toggle: togglePopover,
|
||||||
return (
|
close: closePopover,
|
||||||
<BasicPopover>
|
} = usePopover({debugName: 'Playhead'}, () => {
|
||||||
<PlayheadPositionPopover
|
return (
|
||||||
layoutP={layoutP}
|
<BasicPopover>
|
||||||
onRequestClose={closePopover}
|
<PlayheadPositionPopover
|
||||||
/>
|
layoutP={layoutP}
|
||||||
</BasicPopover>
|
onRequestClose={closePopover}
|
||||||
)
|
/>
|
||||||
},
|
</BasicPopover>
|
||||||
)
|
)
|
||||||
|
})
|
||||||
|
|
||||||
const gestureHandlers = useMemo((): Parameters<typeof useDrag>[1] => {
|
const gestureHandlers = useMemo((): Parameters<typeof useDrag>[1] => {
|
||||||
return {
|
return {
|
||||||
|
@ -236,7 +237,7 @@ const Playhead: React.FC<{layoutP: Pointer<SequenceEditorPanelLayout>}> = ({
|
||||||
snapToNone()
|
snapToNone()
|
||||||
},
|
},
|
||||||
onClick(e) {
|
onClick(e) {
|
||||||
openPopover(e, thumbRef.current!)
|
togglePopover(e, thumbRef.current!)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -33,7 +33,7 @@ const PlayheadPositionPopover: React.FC<{
|
||||||
* Called when user hits enter/escape
|
* Called when user hits enter/escape
|
||||||
*/
|
*/
|
||||||
onRequestClose: (reason: string) => void
|
onRequestClose: (reason: string) => void
|
||||||
}> = ({layoutP, onRequestClose}) => {
|
}> = ({layoutP}) => {
|
||||||
const sheet = val(layoutP.sheet)
|
const sheet = val(layoutP.sheet)
|
||||||
const sequence = sheet.getSequence()
|
const sequence = sheet.getSequence()
|
||||||
|
|
||||||
|
@ -80,7 +80,6 @@ const PlayheadPositionPopover: React.FC<{
|
||||||
{...fns}
|
{...fns}
|
||||||
isValid={greaterThanOrEqualToZero}
|
isValid={greaterThanOrEqualToZero}
|
||||||
inputRef={inputRef}
|
inputRef={inputRef}
|
||||||
onBlur={onRequestClose.bind(null, 'number input blur')}
|
|
||||||
nudge={nudge}
|
nudge={nudge}
|
||||||
/>
|
/>
|
||||||
</Container>
|
</Container>
|
||||||
|
|
|
@ -75,31 +75,28 @@ function RgbaPropEditor({
|
||||||
[editingTools],
|
[editingTools],
|
||||||
)
|
)
|
||||||
|
|
||||||
const [popoverNode, openPopover] = usePopover(
|
const popover = usePopover({debugName: 'RgbaPropEditor'}, () => (
|
||||||
{debugName: 'RgbaPropEditor'},
|
<RgbaPopover>
|
||||||
() => (
|
<RgbaColorPicker
|
||||||
<RgbaPopover>
|
color={{
|
||||||
<RgbaColorPicker
|
r: value.r,
|
||||||
color={{
|
g: value.g,
|
||||||
r: value.r,
|
b: value.b,
|
||||||
g: value.g,
|
a: value.a,
|
||||||
b: value.b,
|
}}
|
||||||
a: value.a,
|
temporarilySetValue={(color) => {
|
||||||
}}
|
const rgba = decorateRgba(color)
|
||||||
temporarilySetValue={(color) => {
|
editingTools.temporarilySetValue(rgba)
|
||||||
const rgba = decorateRgba(color)
|
}}
|
||||||
editingTools.temporarilySetValue(rgba)
|
permanentlySetValue={(color) => {
|
||||||
}}
|
// console.log('perm')
|
||||||
permanentlySetValue={(color) => {
|
const rgba = decorateRgba(color)
|
||||||
// console.log('perm')
|
editingTools.permanentlySetValue(rgba)
|
||||||
const rgba = decorateRgba(color)
|
}}
|
||||||
editingTools.permanentlySetValue(rgba)
|
discardTemporaryValue={editingTools.discardTemporaryValue}
|
||||||
}}
|
/>
|
||||||
discardTemporaryValue={editingTools.discardTemporaryValue}
|
</RgbaPopover>
|
||||||
/>
|
))
|
||||||
</RgbaPopover>
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -108,7 +105,7 @@ function RgbaPropEditor({
|
||||||
rgbaColor={value}
|
rgbaColor={value}
|
||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
openPopover(e, containerRef.current)
|
popover.toggle(e, containerRef.current)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<HexInput
|
<HexInput
|
||||||
|
@ -120,7 +117,7 @@ function RgbaPropEditor({
|
||||||
autoFocus={autoFocus}
|
autoFocus={autoFocus}
|
||||||
/>
|
/>
|
||||||
</RowContainer>
|
</RowContainer>
|
||||||
{popoverNode}
|
{popover.node}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,7 @@ const HasUpdatesBadge = styled.div`
|
||||||
`
|
`
|
||||||
|
|
||||||
const GroupDivider = styled.div`
|
const GroupDivider = styled.div`
|
||||||
position: abolute;
|
position: absolute;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
width: 1px;
|
width: 1px;
|
||||||
background: #373b40;
|
background: #373b40;
|
||||||
|
@ -98,7 +98,7 @@ const GlobalToolbar: React.FC = () => {
|
||||||
const hasUpdates =
|
const hasUpdates =
|
||||||
useVal(getStudio().atomP.ahistoric.updateChecker.result.hasUpdates) === true
|
useVal(getStudio().atomP.ahistoric.updateChecker.result.hasUpdates) === true
|
||||||
|
|
||||||
const [moreMenu, openMoreMenu] = usePopover(
|
const moreMenu = usePopover(
|
||||||
() => {
|
() => {
|
||||||
const triggerBounds = moreMenuTriggerRef.current!.getBoundingClientRect()
|
const triggerBounds = moreMenuTriggerRef.current!.getBoundingClientRect()
|
||||||
return {
|
return {
|
||||||
|
@ -166,11 +166,11 @@ const GlobalToolbar: React.FC = () => {
|
||||||
<ExtensionToolbar showLeftDivider toolbarId="global" />
|
<ExtensionToolbar showLeftDivider toolbarId="global" />
|
||||||
</SubContainer>
|
</SubContainer>
|
||||||
<SubContainer>
|
<SubContainer>
|
||||||
{moreMenu}
|
{moreMenu.node}
|
||||||
<ToolbarIconButton
|
<ToolbarIconButton
|
||||||
ref={moreMenuTriggerRef}
|
ref={moreMenuTriggerRef}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
openMoreMenu(e, moreMenuTriggerRef.current!)
|
moreMenu.toggle(e, moreMenuTriggerRef.current!)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Ellipsis />
|
<Ellipsis />
|
||||||
|
|
|
@ -138,7 +138,10 @@ const TooltipWrapper: React.FC<{
|
||||||
props.onPointerOutside,
|
props.onPointerOutside,
|
||||||
])
|
])
|
||||||
|
|
||||||
useOnClickOutside(container, props.onClickOutside ?? noop)
|
useOnClickOutside(
|
||||||
|
[container, props.target ?? null],
|
||||||
|
props.onClickOutside ?? noop,
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ArrowContext.Provider value={arrowContextValue}>
|
<ArrowContext.Provider value={arrowContextValue}>
|
||||||
|
|
|
@ -48,10 +48,22 @@ type Opts = {
|
||||||
verticalGap?: number
|
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(
|
export default function usePopover(
|
||||||
opts: Opts | (() => Opts),
|
opts: Opts | (() => Opts),
|
||||||
render: () => React.ReactElement,
|
render: () => React.ReactElement,
|
||||||
): [node: React.ReactNode, open: OpenFn, close: CloseFn, isOpen: boolean] {
|
): IPopover {
|
||||||
const _debug = (...args: any) => {}
|
const _debug = (...args: any) => {}
|
||||||
|
|
||||||
// want to make sure that we don't close a popover when dragging something (like a curve editor handle)
|
// 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<OpenFn>((...args) => {
|
||||||
|
if (stateRef.current.isOpen) {
|
||||||
|
close('toggled')
|
||||||
|
} else {
|
||||||
|
open(...args)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See doc comment on {@link useAutoCloseLockState}.
|
* See doc comment on {@link useAutoCloseLockState}.
|
||||||
* Used to ensure that moving far away from a parent popover doesn't
|
* 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}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -2,15 +2,23 @@ import type {$IntentionalAny} from '@theatre/shared/utils/types'
|
||||||
import {useEffect} from 'react'
|
import {useEffect} from 'react'
|
||||||
|
|
||||||
export default function useOnClickOutside(
|
export default function useOnClickOutside(
|
||||||
container: Element | null,
|
container: Element | null | (Element | null)[],
|
||||||
onOutside: (e: MouseEvent) => void,
|
onOutside: (e: MouseEvent) => void,
|
||||||
enabled?: boolean,
|
enabled?: boolean,
|
||||||
|
// Can be used e.g. to prevent unexpected closing-reopening when clicking on a
|
||||||
|
// popover's trigger.
|
||||||
) {
|
) {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!container || enabled === false) return
|
if (!container || enabled === false) return
|
||||||
|
|
||||||
|
const containers = Array.isArray(container)
|
||||||
|
? (container.filter((container) => container) as Element[])
|
||||||
|
: [container]
|
||||||
|
|
||||||
const onMouseDown = (e: MouseEvent) => {
|
const onMouseDown = (e: MouseEvent) => {
|
||||||
if (!e.composedPath().includes(container)) {
|
if (
|
||||||
|
containers.every((container) => !e.composedPath().includes(container))
|
||||||
|
) {
|
||||||
onOutside(e)
|
onOutside(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue