UX tweak for tooltips

* When pointer moves from one tooltip-ed node to another, their tooltips don't flash out and in. The new tooltip replaces the old one immediatgely.
This commit is contained in:
Aria Minaei 2021-08-19 21:04:55 +02:00
parent e63d273830
commit 69f787d5cf
2 changed files with 30 additions and 20 deletions

View file

@ -1,3 +1,4 @@
import type {IDerivation} from '@theatre/dataverse'
import {Box} from '@theatre/dataverse'
import useRefAndState from '@theatre/studio/utils/useRefAndState'
import React, {
@ -9,8 +10,9 @@ import React, {
} from 'react'
const ctx = createContext<{
currentTooltipId: Box<number>
portalTarget: HTMLElement
cur: IDerivation<number>
set: (id: number, delay: number) => void
}>(null!)
let lastTooltipId = 0
@ -20,29 +22,20 @@ export const useTooltipOpenState = (): [
setIsOpen: (isOpen: boolean, delay: number) => void,
] => {
const id = useMemo(() => lastTooltipId++, [])
const {currentTooltipId} = useContext(ctx)
const {cur, set} = useContext(ctx)
const [isOpenRef, isOpen] = useRefAndState<boolean>(false)
const setIsOpen = useCallback((shouldOpen: boolean, delay: number) => {
if (shouldOpen) {
if (currentTooltipId.get() !== id) {
currentTooltipId.set(id)
}
} else {
if (currentTooltipId.get() === id) {
currentTooltipId.set(-1)
}
}
set(shouldOpen ? id : -1, delay)
}, [])
useEffect(() => {
const {derivation} = currentTooltipId
return derivation.changesWithoutValues().tap(() => {
const flag = derivation.getValue() === id
return cur.changesWithoutValues().tap(() => {
const flag = cur.getValue() === id
if (isOpenRef.current !== flag) isOpenRef.current = flag
})
}, [currentTooltipId, id])
}, [cur, id])
return [isOpen, setIsOpen]
}
@ -52,11 +45,28 @@ const TooltipContext: React.FC<{portalTarget: HTMLElement}> = ({
portalTarget,
}) => {
const currentTooltipId = useMemo(() => new Box(-1), [])
const cur = currentTooltipId.derivation
const set = useMemo(() => {
let lastTimeout: NodeJS.Timeout | undefined = undefined
return (id: number, delay: number) => {
if (lastTimeout !== undefined) {
clearTimeout(lastTimeout)
lastTimeout = undefined
}
if (delay === 0) {
currentTooltipId.set(id)
} else {
lastTimeout = setTimeout(() => {
currentTooltipId.set(id)
lastTimeout = undefined
}, delay)
}
}
}, [])
return (
<ctx.Provider value={{currentTooltipId, portalTarget}}>
{children}
</ctx.Provider>
<ctx.Provider value={{cur, set, portalTarget}}>{children}</ctx.Provider>
)
}

View file

@ -3,7 +3,7 @@ import {outlinePanelTheme} from '@theatre/studio/panels/OutlinePanel/BaseItem'
import {darken, opacify} from 'polished'
import {pointerEventsAutoInNormalMode} from '@theatre/studio/css'
import React from 'react'
import type {$IntentionalAny} from '@theatre/shared/utils/types'
import type {$FixMe, $IntentionalAny} from '@theatre/shared/utils/types'
import useTooltip from '@theatre/studio/uiComponents/Popover/useTooltip'
import mergeRefs from 'react-merge-refs'
import MinimalTooltip from '@theatre/studio/uiComponents/Popover/MinimalTooltip'
@ -58,7 +58,7 @@ const Container = styled.button`
`
const ToolbarIconButton: typeof Container = React.forwardRef(
({title, ...props}, ref: $IntentionalAny) => {
({title, ...props}: $FixMe, ref: $FixMe) => {
const [tooltip, localRef] = useTooltip(
{enabled: typeof title === 'string'},
() => <MinimalTooltip>{title}</MinimalTooltip>,