Skip to content

Commit

Permalink
Block tooltip when a gesture/popover is active
Browse files Browse the repository at this point in the history
  • Loading branch information
AriaMinaei committed Oct 15, 2023
1 parent 6e1f7fe commit 7ebd6de
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 85 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
import {generateSequenceMarkerId} from '@theatre/shared/utils/ids'
import useChordial from '@theatre/studio/uiComponents/chordial/useChodrial'
import {mergeRefs} from 'react-merge-refs'
import usePopover from '@theatre/studio/uiComponents/Popover/usePopover'

const Container = styled.div<{isVisible: boolean}>`
--thumbColor: #00e0ff;
Expand Down Expand Up @@ -79,7 +80,7 @@ const Thumb = styled.div`
${pointerEventsAutoInNormalMode};
${Container}.seeking > & {
${Container}.seeking > &, ${Container}.popoverOpen > & {
pointer-events: none !important;
}
Expand Down Expand Up @@ -217,6 +218,11 @@ const Playhead: React.FC<{layoutP: Pointer<SequenceEditorPanelLayout>}> = ({
sequence.closestGridPosition(posInUnitSpace),
),
menuTitle: 'Playhead',
invoke: (e) => {
if (e?.type === 'MouseEvent') {
popover.open(e.event, thumbRef.current!)
}
},
items: [
{
type: 'normal',
Expand All @@ -242,19 +248,6 @@ const Playhead: React.FC<{layoutP: Pointer<SequenceEditorPanelLayout>}> = ({
},
},
],
focus: {
type: 'Popover',
node: (
<BasicPopover>
<PlayheadPositionPopover
layoutP={layoutP}
onRequestClose={() => {
// todo close popover
}}
/>
</BasicPopover>
),
},
drag: {
debugName: 'RightOverlay/Playhead',
onDragStart() {
Expand Down Expand Up @@ -284,9 +277,6 @@ const Playhead: React.FC<{layoutP: Pointer<SequenceEditorPanelLayout>}> = ({
setIsDragging(false)
snapToNone()
},
onClick(e) {
// togglePopover(e, thumbRef.current!)
},
}
},
},
Expand All @@ -298,14 +288,29 @@ const Playhead: React.FC<{layoutP: Pointer<SequenceEditorPanelLayout>}> = ({
// hide the frame stamp when seeking
useLockFrameStampPosition(useVal(layoutP.seeker.isSeeking) || isDragging, -1)

const popover = usePopover({debugName: 'Playhead'}, () => {
return (
<BasicPopover>
<PlayheadPositionPopover
layoutP={layoutP}
onRequestClose={popover.close}
/>
</BasicPopover>
)
})

c.useDisableTooltip(popover.isOpen)

return (
<>
{popover.node}

<Container
isVisible={isVisible}
style={{transform: `translate3d(${posInClippedSpace}px, 0, 0)`}}
className={`${isSeeking && 'seeking'} ${
isPlayheadAttachedToFocusRange && 'playheadattachedtofocusrange'
}`}
popover.isOpen && 'popoverOpen'
} ${isPlayheadAttachedToFocusRange && 'playheadattachedtofocusrange'}`}
{...includeLockFrameStampAttrs('hide')}
>
<Thumb
Expand Down
5 changes: 4 additions & 1 deletion theatre/studio/src/uiComponents/chordial/TooltipOverlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import {tooltipTarget} from './tooltipActor'
export const TooltipOverlay: React.FC<{}> = () => {
const currentTarget = useVal(tooltipTarget)

const tooltipDisabled =
useVal(currentTarget?.atom.pointer.tooltipDisabled) ?? false

const title = usePrism((): React.ReactNode => {
const chordial = currentTarget
if (!chordial) return null
Expand All @@ -31,7 +34,7 @@ export const TooltipOverlay: React.FC<{}> = () => {
}> = []

const chordial = currentTarget
if (chordial && positioning) {
if (chordial && positioning && !tooltipDisabled) {
data.push({
key: chordial.id,
title,
Expand Down
28 changes: 18 additions & 10 deletions theatre/studio/src/uiComponents/chordial/chordialInternals.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {Atom} from '@theatre/dataverse'
import type {$IntentionalAny, VoidFn} from '@theatre/utils/types'
import type {ElementType, MutableRefObject} from 'react'
import type {$IntentionalAny} from '@theatre/utils/types'
import {useEffect, type ElementType, type MutableRefObject} from 'react'
import type {DragOpts} from '@theatre/studio/uiComponents/useDrag'
import type React from 'react'

Expand All @@ -10,12 +10,12 @@ export type ChordialOpts = {
// shown as the top item in the menu
menuTitle?: string | React.ReactNode
items: Array<ContextMenuItem>
focus?:
| {
type: 'callback'
callback: (e: MouseEvent) => VoidFn
}
| {type: 'Popover'; node: React.ReactNode}
invoke?: (
e:
| {type: 'MouseEvent'; event: MouseEvent}
| {type: 'KeyboardEvent'; event: KeyboardEvent}
| undefined,
) => void
drag?: DragOpts
}

Expand All @@ -34,9 +34,10 @@ export type ChodrialElement = {
id: string
returnValue: {
targetRef: MutableRefObject<$IntentionalAny>
useDisableTooltip: (disable: boolean) => void
}
target: HTMLElement | null | undefined
atom: Atom<{optsFn: ChordialOptsFn}>
atom: Atom<{optsFn: ChordialOptsFn; tooltipDisabled: boolean}>
}

export type MaybeChodrialEl = ChodrialElement | undefined
Expand All @@ -45,11 +46,18 @@ let lastId = 0

export function createChordialElement(optsFn: ChordialOptsFn): ChodrialElement {
const id = (lastId++).toString()
const atom = new Atom({optsFn, tooltipDisabled: false})
const chordialRef: ChodrialElement = {
id,
target: null,
atom: new Atom({optsFn}),
atom,
returnValue: {
useDisableTooltip: (disable: boolean) => {
useEffect(() => {
atom.setByPointer((p) => p.tooltipDisabled, disable)
return () => atom.setByPointer((p) => p.tooltipDisabled, false)
}, [disable])
},
targetRef: {
get current() {
return chordialRef.target
Expand Down
108 changes: 70 additions & 38 deletions theatre/studio/src/uiComponents/chordial/gestureActor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,57 +8,73 @@ import {
import {isSafari} from '@theatre/studio/uiComponents/isSafari'
import type {CapturedPointer} from '@theatre/studio/UIRoot/PointerCapturing'
import {createPointerCapturing} from '@theatre/studio/UIRoot/PointerCapturing'
import type {ChodrialElement} from './chordialInternals'
import type {ChodrialElement, ChordialOpts} from './chordialInternals'
import {findChodrialByDomNode} from './chordialInternals'

export const gestureActor = basicFSM<
| {type: 'mousedown'; mouseEvent: MouseEvent}
| {type: 'mouseup'; mouseEvent: MouseEvent}
| {type: 'mousemove'; mouseEvent: MouseEvent},
| {type: 'mousemove'; mouseEvent: MouseEvent}
| {type: 'click'; mouseEvent: MouseEvent},
undefined | ChodrialElement
>((t) => {
function idle() {
t('idle', undefined, (e) => {
switch (e.type) {
case 'click':
{
const el = findChodrialByDomNode(e.mouseEvent.target)
if (!el) return
const {invoke} = el.atom.get().optsFn()
if (!invoke) return
invoke({type: 'MouseEvent', event: e.mouseEvent})
}
break
case 'mousedown':
const el = findChodrialByDomNode(e.mouseEvent.target)
if (!el) return
const {drag} = el.atom.get().optsFn()
if (!drag || drag.disabled === true) return

// defensively release
// TIODO
// capturedPointerRef.current?.release()
const acceptedButtons: MouseButton[] = drag.buttons ?? [
MouseButton.Left,
]

if (!acceptedButtons.includes(e.mouseEvent.button)) return

const dragHandlers = drag.onDragStart(e.mouseEvent)
{
const el = findChodrialByDomNode(e.mouseEvent.target)
if (!el) return
const opts = el.atom.get().optsFn()
const {drag} = opts
if (!drag || drag.disabled === true) return

// defensively release
// TIODO
// capturedPointerRef.current?.release()
const acceptedButtons: MouseButton[] = drag.buttons ?? [
MouseButton.Left,
]

if (!acceptedButtons.includes(e.mouseEvent.button)) return

const dragHandlers = drag.onDragStart(e.mouseEvent)

if (dragHandlers === false) {
// we should ignore the gesture
return
}

if (dragHandlers === false) {
// we should ignore the gesture
return
}
// need to capture pointer after we know the provided handler wants to handle drag start
const capturedPointer =
createPointerCapturing('Drag').capturing.capturePointer(
'dragStart',
)

// need to capture pointer after we know the provided handler wants to handle drag start
const capturedPointer =
createPointerCapturing('Drag').capturing.capturePointer('dragStart')
if (!drag.dontBlockMouseDown) {
e.mouseEvent.stopPropagation()
e.mouseEvent.preventDefault()
}

if (!drag.dontBlockMouseDown) {
e.mouseEvent.stopPropagation()
e.mouseEvent.preventDefault()
beforeDetected(
el,
opts,
drag,
e.mouseEvent,
dragHandlers,
0,
capturedPointer,
)
}

beforeDetected(
el,
drag,
e.mouseEvent,
dragHandlers,
0,
capturedPointer,
)
break
default:
break
Expand All @@ -67,6 +83,7 @@ export const gestureActor = basicFSM<
}

function handleMouseup(
opts: ChordialOpts,
dragOpts: DragOpts,
e: MouseEvent,
handlers: DragHandlers,
Expand All @@ -85,12 +102,16 @@ export const gestureActor = basicFSM<
// Fixes https://linear.app/theatre/issue/P-177/beginners-scrubbing-the-playhead-from-within-an-iframe-then-[space]
window.focus()

handlers.onClick?.(e)
if (!dragHappened) {
handlers.onClick?.(e)
opts.invoke?.({type: 'MouseEvent', event: e})
}
idle()
}

function beforeDetected(
el: ChodrialElement,
opts: ChordialOpts,
dragOpts: DragOpts,
mousedownEvent: MouseEvent,
handlers: DragHandlers,
Expand All @@ -101,6 +122,7 @@ export const gestureActor = basicFSM<
switch (e.mouseEvent.type) {
case 'mouseup':
handleMouseup(
opts,
dragOpts,
e.mouseEvent,
handlers,
Expand Down Expand Up @@ -137,6 +159,7 @@ export const gestureActor = basicFSM<

afterDetected(
el,
opts,
dragOpts,
mousedownEvent,
handlers,
Expand All @@ -154,6 +177,7 @@ export const gestureActor = basicFSM<

function afterDetected(
el: ChodrialElement,
opts: ChordialOpts,
dragOpts: DragOpts,
mousedownEvent: MouseEvent,
handlers: DragHandlers,
Expand All @@ -165,7 +189,14 @@ export const gestureActor = basicFSM<
t('afterDetected', el, (e) => {
switch (e.mouseEvent.type) {
case 'mouseup':
handleMouseup(dragOpts, e.mouseEvent, handlers, true, capturedPointer)
handleMouseup(
opts,
dragOpts,
e.mouseEvent,
handlers,
true,
capturedPointer,
)
break
case 'mousemove':
const isPointerLockUsed = dragOpts.shouldPointerLock && !isSafari
Expand Down Expand Up @@ -205,6 +236,7 @@ export const gestureActor = basicFSM<

afterDetected(
el,
opts,
dragOpts,
mousedownEvent,
handlers,
Expand Down
11 changes: 11 additions & 0 deletions theatre/studio/src/uiComponents/chordial/mousedownActor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {basicFSM} from '@theatre/utils/basicFSM'

export const mousedownActor = basicFSM<[isDown: boolean], boolean>((t) => {
function toggle(original: boolean) {
t('down', original, ([isDown]) => {
if (isDown !== original) toggle(isDown)
})
}

toggle(false)
})({name: 'mouseDownActor'})
Loading

1 comment on commit 7ebd6de

@vercel
Copy link

@vercel vercel bot commented on 7ebd6de Oct 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.