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:
parent
e63d273830
commit
69f787d5cf
2 changed files with 30 additions and 20 deletions
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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>,
|
||||
|
|
Loading…
Reference in a new issue