Skip to content

Commit

Permalink
feat: show tooltip on toolbar button long-press
Browse files Browse the repository at this point in the history
  • Loading branch information
amanharwara committed Aug 16, 2023
1 parent c65d8e5 commit d6c4338
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { classNames } from '@standardnotes/snjs'
import { ReactNode, useState, MouseEvent } from 'react'
import { ReactNode, useState, useRef, useEffect, MouseEvent } from 'react'
import { Tooltip, TooltipAnchor, TooltipOptions, useTooltipStore } from '@ariakit/react'
import { Slot } from '@radix-ui/react-slot'
import { MutuallyExclusiveMediaQueryBreakpoints, useMediaQuery } from '@/Hooks/useMediaQuery'
import { getPositionedPopoverStyles } from '../Popover/GetPositionedPopoverStyles'
import { getAdjustedStylesForNonPortalPopover } from '../Popover/Utils/getAdjustedStylesForNonPortal'
import { useLongPressEvent } from '@/Hooks/useLongPress'

const StyledTooltip = ({
children,
Expand All @@ -30,20 +31,44 @@ const StyledTooltip = ({
hideTimeout: 0,
skipTimeout: 0,
open: forceOpen,
animated: true,
})

const anchorRef = useRef<HTMLElement>(null)
const { attachEvents: attachLongPressEvents, cleanupEvents: cleanupLongPressEvents } = useLongPressEvent(
anchorRef,
() => {
tooltip.show()
setTimeout(() => {
tooltip.hide()
}, 2000)
},
)

useEffect(() => {
if (!isMobile || !showOnMobile) {
return
}

attachLongPressEvents()

return () => {
cleanupLongPressEvents()
}
}, [attachLongPressEvents, cleanupLongPressEvents, isMobile, showOnMobile])

if (isMobile && !showOnMobile) {
return <>{children}</>
}

return (
<>
<TooltipAnchor
ref={anchorRef}
onClick={() => setForceOpen(false)}
onContextMenu={(event: MouseEvent) => {
if (isMobile && showOnMobile) {
event.preventDefault()
tooltip.show()
}
}}
onBlur={() => setForceOpen(undefined)}
Expand All @@ -59,6 +84,7 @@ const StyledTooltip = ({
store={tooltip}
className={classNames(
'z-tooltip max-w-max rounded border border-border translucent-ui:border-[--popover-border-color] bg-contrast translucent-ui:bg-[--popover-background-color] [backdrop-filter:var(--popover-backdrop-filter)] px-3 py-1.5 text-sm text-foreground shadow',
'opacity-60 [&[data-enter]]:opacity-100 [&[data-leave]]:opacity-60 transition-opacity duration-75',
className,
)}
updatePosition={() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -404,12 +404,11 @@ const MobileToolbarPlugin = () => {
>
{items.map((item) => {
return (
<StyledTooltip showOnMobile showOnHover label={item.name}>
<StyledTooltip showOnMobile showOnHover label={item.name} key={item.name}>
<button
className="flex items-center justify-center rounded p-0.5 disabled:opacity-50 select-none"
className="flex items-center justify-center rounded p-0.5 disabled:opacity-50 select-none hover:bg-default"
aria-label={item.name}
onClick={item.onSelect}
key={item.name}
disabled={item.disabled}
>
<div
Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/javascripts/Hooks/useContextMenuEvent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useLongPressEvent } from './useLongPress'
import { isIOS } from '@standardnotes/ui-services'

export const useContextMenuEvent = (elementRef: RefObject<HTMLElement>, listener: (x: number, y: number) => void) => {
const { attachEvents, cleanupEvents } = useLongPressEvent(elementRef, listener)
const { attachEvents, cleanupEvents } = useLongPressEvent(elementRef, listener, true)

const handleContextMenuEvent = useCallback(
(event: MouseEvent) => {
Expand Down
27 changes: 23 additions & 4 deletions packages/web/src/javascripts/Hooks/useLongPress.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ const ReactNativeLongpressDelay = 370
export const useLongPressEvent = (
elementRef: RefObject<HTMLElement>,
listener: (x: number, y: number) => void,
clearOnPointerMove = false,
delay = ReactNativeLongpressDelay,
) => {
const longPressTimeout = useRef<number>()
const pointerPosition = useRef<{ x: number; y: number }>()

const clearLongPressTimeout = useCallback(() => {
if (longPressTimeout.current) {
Expand All @@ -19,6 +21,7 @@ export const useLongPressEvent = (
const createLongPressTimeout = useCallback(
(event: PointerEvent) => {
clearLongPressTimeout()
pointerPosition.current = { x: event.clientX, y: event.clientY }
longPressTimeout.current = window.setTimeout(() => {
const x = event.clientX
const y = event.clientY
Expand All @@ -29,27 +32,43 @@ export const useLongPressEvent = (
[clearLongPressTimeout, delay, listener],
)

const clearLongPressTimeoutIfMoved = useCallback(
(event: PointerEvent) => {
if (
pointerPosition.current &&
(event.clientX !== pointerPosition.current.x || event.clientY !== pointerPosition.current.y)
) {
clearLongPressTimeout()
}
},
[clearLongPressTimeout],
)

const attachEvents = useCallback(() => {
if (!elementRef.current) {
return
}

elementRef.current.addEventListener('pointerdown', createLongPressTimeout)
elementRef.current.addEventListener('pointermove', clearLongPressTimeout)
if (clearOnPointerMove) {
elementRef.current.addEventListener('pointermove', clearLongPressTimeoutIfMoved)
}
elementRef.current.addEventListener('pointercancel', clearLongPressTimeout)
elementRef.current.addEventListener('pointerup', clearLongPressTimeout)
}, [clearLongPressTimeout, createLongPressTimeout, elementRef])
}, [clearLongPressTimeout, clearLongPressTimeoutIfMoved, clearOnPointerMove, createLongPressTimeout, elementRef])

const cleanupEvents = useCallback(() => {
if (!elementRef.current) {
return
}

elementRef.current.removeEventListener('pointerdown', createLongPressTimeout)
elementRef.current.removeEventListener('pointermove', clearLongPressTimeout)
if (clearOnPointerMove) {
elementRef.current.removeEventListener('pointermove', clearLongPressTimeoutIfMoved)
}
elementRef.current.removeEventListener('pointercancel', clearLongPressTimeout)
elementRef.current.removeEventListener('pointerup', clearLongPressTimeout)
}, [clearLongPressTimeout, createLongPressTimeout, elementRef])
}, [clearLongPressTimeout, clearLongPressTimeoutIfMoved, clearOnPointerMove, createLongPressTimeout, elementRef])

const memoizedReturn = useMemo(
() => ({
Expand Down

0 comments on commit d6c4338

Please sign in to comment.