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 {Box} from '@theatre/dataverse'
|
||||||
import useRefAndState from '@theatre/studio/utils/useRefAndState'
|
import useRefAndState from '@theatre/studio/utils/useRefAndState'
|
||||||
import React, {
|
import React, {
|
||||||
|
@ -9,8 +10,9 @@ import React, {
|
||||||
} from 'react'
|
} from 'react'
|
||||||
|
|
||||||
const ctx = createContext<{
|
const ctx = createContext<{
|
||||||
currentTooltipId: Box<number>
|
|
||||||
portalTarget: HTMLElement
|
portalTarget: HTMLElement
|
||||||
|
cur: IDerivation<number>
|
||||||
|
set: (id: number, delay: number) => void
|
||||||
}>(null!)
|
}>(null!)
|
||||||
|
|
||||||
let lastTooltipId = 0
|
let lastTooltipId = 0
|
||||||
|
@ -20,29 +22,20 @@ export const useTooltipOpenState = (): [
|
||||||
setIsOpen: (isOpen: boolean, delay: number) => void,
|
setIsOpen: (isOpen: boolean, delay: number) => void,
|
||||||
] => {
|
] => {
|
||||||
const id = useMemo(() => lastTooltipId++, [])
|
const id = useMemo(() => lastTooltipId++, [])
|
||||||
const {currentTooltipId} = useContext(ctx)
|
const {cur, set} = useContext(ctx)
|
||||||
const [isOpenRef, isOpen] = useRefAndState<boolean>(false)
|
const [isOpenRef, isOpen] = useRefAndState<boolean>(false)
|
||||||
|
|
||||||
const setIsOpen = useCallback((shouldOpen: boolean, delay: number) => {
|
const setIsOpen = useCallback((shouldOpen: boolean, delay: number) => {
|
||||||
if (shouldOpen) {
|
set(shouldOpen ? id : -1, delay)
|
||||||
if (currentTooltipId.get() !== id) {
|
|
||||||
currentTooltipId.set(id)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (currentTooltipId.get() === id) {
|
|
||||||
currentTooltipId.set(-1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const {derivation} = currentTooltipId
|
return cur.changesWithoutValues().tap(() => {
|
||||||
return derivation.changesWithoutValues().tap(() => {
|
const flag = cur.getValue() === id
|
||||||
const flag = derivation.getValue() === id
|
|
||||||
|
|
||||||
if (isOpenRef.current !== flag) isOpenRef.current = flag
|
if (isOpenRef.current !== flag) isOpenRef.current = flag
|
||||||
})
|
})
|
||||||
}, [currentTooltipId, id])
|
}, [cur, id])
|
||||||
|
|
||||||
return [isOpen, setIsOpen]
|
return [isOpen, setIsOpen]
|
||||||
}
|
}
|
||||||
|
@ -52,11 +45,28 @@ const TooltipContext: React.FC<{portalTarget: HTMLElement}> = ({
|
||||||
portalTarget,
|
portalTarget,
|
||||||
}) => {
|
}) => {
|
||||||
const currentTooltipId = useMemo(() => new Box(-1), [])
|
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 (
|
return (
|
||||||
<ctx.Provider value={{currentTooltipId, portalTarget}}>
|
<ctx.Provider value={{cur, set, portalTarget}}>{children}</ctx.Provider>
|
||||||
{children}
|
|
||||||
</ctx.Provider>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import {outlinePanelTheme} from '@theatre/studio/panels/OutlinePanel/BaseItem'
|
||||||
import {darken, opacify} from 'polished'
|
import {darken, opacify} from 'polished'
|
||||||
import {pointerEventsAutoInNormalMode} from '@theatre/studio/css'
|
import {pointerEventsAutoInNormalMode} from '@theatre/studio/css'
|
||||||
import React from 'react'
|
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 useTooltip from '@theatre/studio/uiComponents/Popover/useTooltip'
|
||||||
import mergeRefs from 'react-merge-refs'
|
import mergeRefs from 'react-merge-refs'
|
||||||
import MinimalTooltip from '@theatre/studio/uiComponents/Popover/MinimalTooltip'
|
import MinimalTooltip from '@theatre/studio/uiComponents/Popover/MinimalTooltip'
|
||||||
|
@ -58,7 +58,7 @@ const Container = styled.button`
|
||||||
`
|
`
|
||||||
|
|
||||||
const ToolbarIconButton: typeof Container = React.forwardRef(
|
const ToolbarIconButton: typeof Container = React.forwardRef(
|
||||||
({title, ...props}, ref: $IntentionalAny) => {
|
({title, ...props}: $FixMe, ref: $FixMe) => {
|
||||||
const [tooltip, localRef] = useTooltip(
|
const [tooltip, localRef] = useTooltip(
|
||||||
{enabled: typeof title === 'string'},
|
{enabled: typeof title === 'string'},
|
||||||
() => <MinimalTooltip>{title}</MinimalTooltip>,
|
() => <MinimalTooltip>{title}</MinimalTooltip>,
|
||||||
|
|
Loading…
Reference in a new issue