Merge pull request #239 from PasteBar/long-press-clip-orginize-update
long press to activate clips organize update
This commit is contained in:
commit
2144265b9c
5
.changeset/afraid-steaks-think.md
Normal file
5
.changeset/afraid-steaks-think.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'pastebar-app-ui': patch
|
||||
---
|
||||
|
||||
Added long press on clip to activate clips organize
|
@ -1,27 +0,0 @@
|
||||
export function useLongPress() {
|
||||
return function (callback) {
|
||||
let timeout
|
||||
|
||||
function start(e) {
|
||||
if (e.type === 'mousedown' && e.button !== 0) {
|
||||
return
|
||||
}
|
||||
timeout = setTimeout(() => {
|
||||
callback()
|
||||
}, 1500)
|
||||
}
|
||||
|
||||
function clear() {
|
||||
timeout && clearTimeout(timeout)
|
||||
}
|
||||
|
||||
return {
|
||||
onMouseDown: start,
|
||||
onTouchStart: start,
|
||||
onMouseUp: clear,
|
||||
onMouseLeave: clear,
|
||||
onTouchMove: clear,
|
||||
onTouchEnd: clear,
|
||||
}
|
||||
}
|
||||
}
|
150
packages/pastebar-app-ui/src/hooks/use-long-press.ts
Normal file
150
packages/pastebar-app-ui/src/hooks/use-long-press.ts
Normal file
@ -0,0 +1,150 @@
|
||||
import { useCallback, useRef } from 'react'
|
||||
|
||||
interface LongPressOptions {
|
||||
delay?: number
|
||||
threshold?: number
|
||||
cancelOnMove?: boolean
|
||||
preventDefault?: boolean
|
||||
}
|
||||
|
||||
interface LongPressHandlers {
|
||||
onMouseDown: (e: React.MouseEvent) => void
|
||||
onMouseUp: (e: React.MouseEvent) => void
|
||||
onMouseLeave: (e: React.MouseEvent) => void
|
||||
onMouseMove?: (e: React.MouseEvent) => void
|
||||
onTouchStart: (e: React.TouchEvent) => void
|
||||
onTouchEnd: (e: React.TouchEvent) => void
|
||||
onTouchMove: (e: React.TouchEvent) => void
|
||||
onTouchCancel: (e: React.TouchEvent) => void
|
||||
onContextMenu?: (e: React.MouseEvent) => void
|
||||
}
|
||||
|
||||
export function useLongPress(
|
||||
callback: () => void,
|
||||
options: LongPressOptions = {}
|
||||
): LongPressHandlers {
|
||||
const {
|
||||
delay = 600,
|
||||
threshold = 10,
|
||||
cancelOnMove = true,
|
||||
preventDefault = true
|
||||
} = options
|
||||
|
||||
const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
||||
const startPositionRef = useRef<{ x: number; y: number } | null>(null)
|
||||
const isLongPressTriggeredRef = useRef(false)
|
||||
const isTouchRef = useRef(false)
|
||||
|
||||
const clear = useCallback(() => {
|
||||
if (timeoutRef.current) {
|
||||
clearTimeout(timeoutRef.current)
|
||||
timeoutRef.current = null
|
||||
}
|
||||
startPositionRef.current = null
|
||||
isLongPressTriggeredRef.current = false
|
||||
isTouchRef.current = false
|
||||
}, [])
|
||||
|
||||
const start = useCallback(
|
||||
(e: React.MouseEvent | React.TouchEvent) => {
|
||||
// Prevent multiple simultaneous long press handlers
|
||||
if (timeoutRef.current) {
|
||||
clear()
|
||||
}
|
||||
|
||||
// Only handle left mouse button for mouse events
|
||||
if ('button' in e && e.button !== 0) {
|
||||
return
|
||||
}
|
||||
|
||||
// Determine if this is a touch event
|
||||
isTouchRef.current = 'touches' in e
|
||||
|
||||
// Get the starting position
|
||||
const touch = 'touches' in e ? e.touches[0] : e
|
||||
startPositionRef.current = {
|
||||
x: touch.clientX,
|
||||
y: touch.clientY
|
||||
}
|
||||
|
||||
if (preventDefault) {
|
||||
e.preventDefault()
|
||||
}
|
||||
|
||||
// Set up the long press timer
|
||||
timeoutRef.current = setTimeout(() => {
|
||||
isLongPressTriggeredRef.current = true
|
||||
callback()
|
||||
clear()
|
||||
}, delay)
|
||||
},
|
||||
[callback, clear, delay, preventDefault]
|
||||
)
|
||||
|
||||
const move = useCallback(
|
||||
(e: React.MouseEvent | React.TouchEvent) => {
|
||||
if (!cancelOnMove || !startPositionRef.current || !timeoutRef.current) {
|
||||
return
|
||||
}
|
||||
|
||||
// Get current position
|
||||
const touch = 'touches' in e ? e.touches[0] : e
|
||||
const currentX = touch.clientX
|
||||
const currentY = touch.clientY
|
||||
|
||||
// Calculate distance moved
|
||||
const deltaX = Math.abs(currentX - startPositionRef.current.x)
|
||||
const deltaY = Math.abs(currentY - startPositionRef.current.y)
|
||||
|
||||
// Cancel if moved beyond threshold
|
||||
if (deltaX > threshold || deltaY > threshold) {
|
||||
clear()
|
||||
}
|
||||
},
|
||||
[cancelOnMove, clear, threshold]
|
||||
)
|
||||
|
||||
const end = useCallback(
|
||||
(e: React.MouseEvent | React.TouchEvent) => {
|
||||
// Prevent default only if long press was triggered
|
||||
if (isLongPressTriggeredRef.current && preventDefault) {
|
||||
e.preventDefault()
|
||||
}
|
||||
clear()
|
||||
},
|
||||
[clear, preventDefault]
|
||||
)
|
||||
|
||||
const cancel = useCallback(() => {
|
||||
clear()
|
||||
}, [clear])
|
||||
|
||||
// Prevent context menu on long press
|
||||
const handleContextMenu = useCallback(
|
||||
(e: React.MouseEvent) => {
|
||||
if (isLongPressTriggeredRef.current || timeoutRef.current) {
|
||||
e.preventDefault()
|
||||
}
|
||||
},
|
||||
[]
|
||||
)
|
||||
|
||||
return {
|
||||
onMouseDown: start as (e: React.MouseEvent) => void,
|
||||
onMouseUp: end as (e: React.MouseEvent) => void,
|
||||
onMouseLeave: cancel,
|
||||
onMouseMove: cancelOnMove ? (move as (e: React.MouseEvent) => void) : undefined,
|
||||
onTouchStart: start as (e: React.TouchEvent) => void,
|
||||
onTouchEnd: end as (e: React.TouchEvent) => void,
|
||||
onTouchMove: move as (e: React.TouchEvent) => void,
|
||||
onTouchCancel: cancel,
|
||||
onContextMenu: preventDefault ? handleContextMenu : undefined
|
||||
}
|
||||
}
|
||||
|
||||
// Higher-order function version for backward compatibility
|
||||
export function useLongPressHOF() {
|
||||
return function (callback: () => void, options?: LongPressOptions) {
|
||||
return useLongPress(callback, options)
|
||||
}
|
||||
}
|
@ -209,7 +209,7 @@ export const ClipboardHistoryIconMenu = ({
|
||||
<Button
|
||||
variant="light"
|
||||
id="history-menu-button_tour"
|
||||
className="w-10 text-gray-400 hover:text-gray-500 hover:dark:text-gray-400 dark:text-gray-500 bg-transparent p-1 relative hover:bg-gray-100/70 dark:bg-gray-900 dark:hover:bg-gray-700/70"
|
||||
className="w-10 text-gray-400 hover:text-gray-500 hover:dark:text-gray-400 dark:text-gray-500 bg-transparent p-1 relative hover:bg-gray-100/70 dark:hover:bg-gray-700/70"
|
||||
>
|
||||
<HistoryIcon
|
||||
className="w-5 max-w-[22px] min-w-[16px] stroke-[1.3px]"
|
||||
|
@ -24,6 +24,7 @@ import {
|
||||
isKeyAltPressed,
|
||||
settingsStoreAtom,
|
||||
showClipFindKeyPressed,
|
||||
showClipsMoveOnBoardId,
|
||||
showLargeViewClipId,
|
||||
showLinkedClipId,
|
||||
} from '~/store'
|
||||
@ -363,7 +364,20 @@ export function ClipCard({
|
||||
const isSearch = useSignal(false)
|
||||
const searchTerm = useSignal('')
|
||||
const { updateItemById } = useUpdateItemById()
|
||||
// const onLongPress = useLongPress()
|
||||
const longPressHandlers = useLongPress(
|
||||
() => {
|
||||
if (isClipEdit || isShowDetails || isPinnedBoard) {
|
||||
return
|
||||
}
|
||||
showClipsMoveOnBoardId.value = clip.parentId
|
||||
},
|
||||
{
|
||||
delay: 1000,
|
||||
threshold: 10,
|
||||
cancelOnMove: true,
|
||||
preventDefault: true,
|
||||
}
|
||||
)
|
||||
|
||||
const [copiedItem, setCopiedItem, _, copyInProgressItemId] = useCopyClipItem({})
|
||||
const [pastedItem, pastingCountDown, setPastedItem] = usePasteClipItem({})
|
||||
@ -665,12 +679,7 @@ export function ClipCard({
|
||||
>
|
||||
<CardHeaderWithRef
|
||||
title={titleText}
|
||||
// {...onLongPress(() => {
|
||||
// if (isClipEdit || isShowDetails || isPinnedBoard) {
|
||||
// return
|
||||
// }
|
||||
// showClipsMoveOnBoardId.value = clip.parentId
|
||||
// })}
|
||||
{...longPressHandlers}
|
||||
onClickCapture={e => {
|
||||
if (e.shiftKey) {
|
||||
e.preventDefault()
|
||||
|
@ -110,7 +110,7 @@ export const MenuIconMenu = ({
|
||||
<Button
|
||||
variant="light"
|
||||
id="menu-icon-menu-button_tour"
|
||||
className="w-10 text-gray-400 hover:text-gray-500 hover:dark:text-gray-400 dark:text-gray-500 bg-transparent p-1 relative hover:bg-gray-100/70 dark:bg-gray-900 dark:hover:bg-gray-700/70"
|
||||
className="w-10 text-gray-400 hover:text-gray-500 hover:dark:text-gray-400 dark:text-gray-500 bg-transparent p-1 relative hover:bg-gray-100/70 dark:hover:bg-gray-700/70"
|
||||
>
|
||||
<MenuSquare className="stroke-[1.3px]" size={22} />
|
||||
{selectedItemIds.length > 1 && (
|
||||
|
Loading…
x
Reference in New Issue
Block a user