fix: context menu on longpress on ios safari (#1405)
This commit is contained in:
38
packages/web/src/javascripts/Hooks/useContextMenuEvent.tsx
Normal file
38
packages/web/src/javascripts/Hooks/useContextMenuEvent.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import { isIOS } from '@/Utils'
|
||||
import { RefObject, useCallback, useEffect } from 'react'
|
||||
import { useLongPressEvent } from './useLongPress'
|
||||
|
||||
export const useContextMenuEvent = (elementRef: RefObject<HTMLElement>, listener: (x: number, y: number) => void) => {
|
||||
const { attachEvents, cleanupEvents } = useLongPressEvent(elementRef, listener)
|
||||
|
||||
const handleContextMenuEvent = useCallback(
|
||||
(event: MouseEvent) => {
|
||||
event.preventDefault()
|
||||
listener(event.clientX, event.clientY)
|
||||
},
|
||||
[listener],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
const element = elementRef.current
|
||||
|
||||
if (!element) {
|
||||
return
|
||||
}
|
||||
|
||||
const shouldUseLongPress = isIOS()
|
||||
|
||||
element.addEventListener('contextmenu', handleContextMenuEvent)
|
||||
|
||||
if (shouldUseLongPress) {
|
||||
attachEvents()
|
||||
}
|
||||
|
||||
return () => {
|
||||
element.removeEventListener('contextmenu', handleContextMenuEvent)
|
||||
if (shouldUseLongPress) {
|
||||
cleanupEvents()
|
||||
}
|
||||
}
|
||||
}, [attachEvents, cleanupEvents, elementRef, handleContextMenuEvent, listener])
|
||||
}
|
||||
59
packages/web/src/javascripts/Hooks/useLongPress.tsx
Normal file
59
packages/web/src/javascripts/Hooks/useLongPress.tsx
Normal file
@@ -0,0 +1,59 @@
|
||||
import { RefObject, useCallback, useMemo, useRef } from 'react'
|
||||
|
||||
// According to https://reactnative.dev/docs/touchablewithoutfeedback#onlongpress
|
||||
const ReactNativeLongpressDelay = 370
|
||||
|
||||
export const useLongPressEvent = (
|
||||
elementRef: RefObject<HTMLElement>,
|
||||
listener: (x: number, y: number) => void,
|
||||
delay = ReactNativeLongpressDelay,
|
||||
) => {
|
||||
const longPressTimeout = useRef<number>()
|
||||
|
||||
const clearLongPressTimeout = useCallback(() => {
|
||||
if (longPressTimeout.current) {
|
||||
clearTimeout(longPressTimeout.current)
|
||||
}
|
||||
}, [])
|
||||
|
||||
const createLongPressTimeout = useCallback(
|
||||
(event: PointerEvent) => {
|
||||
clearLongPressTimeout()
|
||||
longPressTimeout.current = window.setTimeout(() => {
|
||||
const x = event.clientX
|
||||
const y = event.clientY
|
||||
|
||||
listener(x, y)
|
||||
}, delay)
|
||||
},
|
||||
[clearLongPressTimeout, delay, listener],
|
||||
)
|
||||
|
||||
const attachEvents = useCallback(() => {
|
||||
if (!elementRef.current) {
|
||||
return
|
||||
}
|
||||
|
||||
elementRef.current.addEventListener('pointerdown', createLongPressTimeout)
|
||||
elementRef.current.addEventListener('pointercancel', clearLongPressTimeout)
|
||||
}, [clearLongPressTimeout, createLongPressTimeout, elementRef])
|
||||
|
||||
const cleanupEvents = useCallback(() => {
|
||||
if (!elementRef.current) {
|
||||
return
|
||||
}
|
||||
|
||||
elementRef.current.removeEventListener('pointerdown', createLongPressTimeout)
|
||||
elementRef.current.removeEventListener('pointercancel', clearLongPressTimeout)
|
||||
}, [clearLongPressTimeout, createLongPressTimeout, elementRef])
|
||||
|
||||
const memoizedReturn = useMemo(
|
||||
() => ({
|
||||
attachEvents,
|
||||
cleanupEvents,
|
||||
}),
|
||||
[attachEvents, cleanupEvents],
|
||||
)
|
||||
|
||||
return memoizedReturn
|
||||
}
|
||||
Reference in New Issue
Block a user