From 691125c1d12bb8cd53018ec1c5ab956a8d654a13 Mon Sep 17 00:00:00 2001 From: plouc Date: Fri, 26 Jun 2020 04:00:28 +0900 Subject: [PATCH] feat(tooltip): restore animation and use new measure hook --- packages/tooltip/package.json | 1 - .../tooltip/src/components/TooltipWrapper.js | 79 ++++++------------- packages/tooltip/src/hooks.js | 39 ++++++--- 3 files changed, 52 insertions(+), 67 deletions(-) diff --git a/packages/tooltip/package.json b/packages/tooltip/package.json index 3f2fa28978..42d9adc6b1 100644 --- a/packages/tooltip/package.json +++ b/packages/tooltip/package.json @@ -15,7 +15,6 @@ "dist/" ], "dependencies": { - "react-measure": "^2.2.4", "react-spring": "^8.0.27" }, "peerDependencies": { diff --git a/packages/tooltip/src/components/TooltipWrapper.js b/packages/tooltip/src/components/TooltipWrapper.js index caa8df76b6..2a78e8af35 100644 --- a/packages/tooltip/src/components/TooltipWrapper.js +++ b/packages/tooltip/src/components/TooltipWrapper.js @@ -6,11 +6,10 @@ * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -import React, { memo, useMemo, useState, useRef, useEffect } from 'react' +import React, { memo, useMemo } from 'react' import PropTypes from 'prop-types' -import Measure from 'react-measure' import { useSpring, animated } from 'react-spring' -import { useTheme, useMotionConfig } from '@nivo/core' +import { useTheme, useMotionConfig, useMeasure } from '@nivo/core' const TOOLTIP_OFFSET = 14 @@ -22,84 +21,56 @@ const tooltipStyle = { left: 0, } -const TooltipWrapper = memo(({ position, anchor, children }) => { +const TooltipWrapper = ({ position, anchor, children }) => { const theme = useTheme() - const { config: springConfig } = useMotionConfig() - - const [dimensions, setDimensions] = useState(null) - const previousDimensions = useRef(null) - useEffect(() => { - previousDimensions.current = dimensions - }) + const { animate, config: springConfig } = useMotionConfig() + const [measureRef, bounds] = useMeasure() let x = Math.round(position[0]) let y = Math.round(position[1]) - if (dimensions !== null) { + + const hasDimension = bounds.width > 0 && bounds.height > 0 + if (hasDimension !== null) { if (anchor === 'top') { - x -= dimensions[0] / 2 - y -= dimensions[1] + TOOLTIP_OFFSET + x -= bounds.width / 2 + y -= bounds.height + TOOLTIP_OFFSET } else if (anchor === 'right') { x += TOOLTIP_OFFSET - y -= dimensions[1] / 2 + y -= bounds.height / 2 } else if (anchor === 'bottom') { - x -= dimensions[0] / 2 + x -= bounds.width / 2 y += TOOLTIP_OFFSET } else if (anchor === 'left') { - x -= dimensions[0] + TOOLTIP_OFFSET - y -= dimensions[1] / 2 + x -= bounds.width + TOOLTIP_OFFSET + y -= bounds.height / 2 } else if (anchor === 'center') { - x -= dimensions[0] / 2 - y -= dimensions[1] / 2 + x -= bounds.width / 2 + y -= bounds.height / 2 } } - const isInitializing = dimensions === null || previousDimensions.current === null - const animatedProps = useSpring({ transform: `translate(${x}px, ${y}px)`, config: springConfig, - // the way the animated tooltip works is not ideal for now - immediate: true, // !animate || isInitializing, + immediate: !animate || !hasDimension, }) const style = useMemo( () => ({ ...tooltipStyle, ...theme.tooltip, - opacity: isInitializing ? 0 : 1, + transform: animatedProps.transform, + opacity: hasDimension ? 1 : 0, }), - [dimensions, theme.tooltip, isInitializing] + [hasDimension, theme.tooltip, animatedProps.transform] ) - // if we don't have dimensions yet, we use - // the non animated version with a 0 opacity - // to avoid a flash effect and weird initial transition return ( - { - setDimensions([bounds.width, bounds.height]) - }} - > - {({ measureRef }) => { - return ( - - {children} - - ) - }} - + + {children} + ) -}) +} TooltipWrapper.displayName = 'TooltipWrapper' TooltipWrapper.propTypes = { @@ -111,4 +82,4 @@ TooltipWrapper.defaultProps = { anchor: 'top', } -export default TooltipWrapper +export default memo(TooltipWrapper) diff --git a/packages/tooltip/src/hooks.js b/packages/tooltip/src/hooks.js index cd22862004..a51d549600 100644 --- a/packages/tooltip/src/hooks.js +++ b/packages/tooltip/src/hooks.js @@ -6,7 +6,7 @@ * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -import { useState, useContext, useCallback } from 'react' +import { useState, useContext, useCallback, useMemo } from 'react' import { tooltipContext } from './context' export const useTooltipHandlers = container => { @@ -16,14 +16,17 @@ export const useTooltipHandlers = container => { position: {}, }) - const showTooltipAt = useCallback((content, [x, y], anchor) => { - setState({ - isVisible: true, - position: [x, y], - anchor, - content, - }) - }, []) + const showTooltipAt = useCallback( + (content, [x, y], anchor) => { + setState({ + isVisible: true, + position: [x, y], + anchor, + content, + }) + }, + [setState] + ) const showTooltipFromEvent = useCallback( (content, event, anchor) => { @@ -43,12 +46,12 @@ export const useTooltipHandlers = container => { content, }) }, - [container] + [container, setState] ) const hideTooltip = useCallback(() => { setState({ isVisible: false, content: null }) - }) + }, [setState]) return { showTooltipAt, @@ -61,4 +64,16 @@ export const useTooltipHandlers = container => { } } -export const useTooltip = () => useContext(tooltipContext) +export const useTooltip = () => { + const context = useContext(tooltipContext) + + const memoizedContext = useMemo(() => { + return { + showTooltipAt: context.showTooltipAt, + showTooltipFromEvent: context.showTooltipFromEvent, + hideTooltip: context.hideTooltip, + } + }, [context.showTooltipAt, context.showTooltipFromEvent, context.hideTooltip]) + + return memoizedContext +}