Skip to content

Commit

Permalink
feat(tooltip): remove tooltip on touch scroll
Browse files Browse the repository at this point in the history
Remove tooltip on touch-devices if user
scrolls more than 150 pixels.
  • Loading branch information
danielkaxis committed Mar 21, 2023
1 parent 2842e1c commit 4bdde64
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 1 deletion.
18 changes: 17 additions & 1 deletion packages/core/src/Tooltip/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { Typography, TypographyProps } from '../Typography'
import { PopOver, PopOverProps } from '../PopOver'
import { shape, spacing, componentSize } from '../designparams'
import { font } from '../theme'
import { useTouchScrollDistance } from './utils'

/**
* Tooltip
Expand Down Expand Up @@ -252,7 +253,6 @@ export const Tooltip: FC<TooltipProps | ExpandedTooltipProps> = ({
(props.variant === 'expanded' ? props.placement : undefined) ?? 'up-down'
const child = Children.only(children) as ReactElement
const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null)

// State for click
const [visibleByClick, showByClick] = useState(false)
// Delayed state for pointer
Expand All @@ -265,6 +265,8 @@ export const Tooltip: FC<TooltipProps | ExpandedTooltipProps> = ({
// If tooltip should be shown
const visible = visibleByClick || debouncedVisible

const touchScrollDistance = useTouchScrollDistance()

const toggle = useCallback(
(event: PointerEvent) => {
// When using touch instead of mouse, we have to toggle the tooltip
Expand All @@ -278,6 +280,20 @@ export const Tooltip: FC<TooltipProps | ExpandedTooltipProps> = ({
[showByClick]
)

/**
* If the delta for any axis is larger than 150 pixels,
* remove the tooltip from the screen.
*/
useLayoutEffect(() => {
if (!visible) {
return
}
const { x, y } = touchScrollDistance
if (Math.max(Math.abs(x), Math.abs(y)) > 150) {
showByClick(false)
}
}, [touchScrollDistance])

useEffect(() => {
const delayVisible = () => setDebouncedVisible(visibleDelayed)
const delayed = setTimeout(delayVisible, TOOLTIP_DELAY_MS)
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/Tooltip/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './useTouchScrollDistance'
70 changes: 70 additions & 0 deletions packages/core/src/Tooltip/utils/useTouchScrollDistance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { useState, useEffect, useLayoutEffect } from 'react'

export const useTouchScrollDistance = () => {
const [origin, setOrigin] = useState<TouchList | null>(null)
const [touches, setTouches] = useState<TouchList | null>(null)
/**
* The distance between touch origin and touch current for both
* x-axis and y-axis
*/
const [touchScrollDistance, setTouchScrollDistance] = useState({ x: 0, y: 0 })

useEffect(() => {
const touchStartHandler = (event: TouchEvent) => {
if (origin === null) {
setOrigin(event.touches)
}
}

const touchMoveHandler = (event: TouchEvent) =>
setTouches(event.changedTouches)

const touchEndHandler = (event: TouchEvent) => {
if (event.touches.length === 0) {
setOrigin(null)
setTouches(null)
setTouchScrollDistance({ x: 0, y: 0 })
}
}

const touchCancelHandler = () => {
setOrigin(null)
setTouches(null)
setTouchScrollDistance({ x: 0, y: 0 })
}

document.addEventListener('touchstart', touchStartHandler)
document.addEventListener('touchmove', touchMoveHandler)
document.addEventListener('touchend', touchEndHandler)
document.addEventListener('touchcancel', touchCancelHandler)

return () => {
document.removeEventListener('touchstart', touchStartHandler)
document.removeEventListener('touchmove', touchMoveHandler)
document.removeEventListener('touchend', touchEndHandler)
document.removeEventListener('touchcancel', touchCancelHandler)
}
}, [origin])

/**
* Calculates the distance in pixels between the origin of
* a touch event and position updates to that touch event.
*/
useLayoutEffect(() => {
if (origin === null || touches === null) {
return
}

// User is not scrolling
if (touches.length > 1) {
return
}

const deltaX = touches[0].clientX - origin[0].clientX
const deltaY = touches[0].clientY - origin[0].clientY

setTouchScrollDistance({ x: deltaX, y: deltaY })
}, [origin, touches])

return touchScrollDistance
}

0 comments on commit 4bdde64

Please sign in to comment.