feat: implement keyboard delete confirmation for clipboard history
- Added `useKeyboardDeleteConfirmation` hook for enhanced delete interactions. - Integrated keyboard shortcuts for delete confirmations in clipboard history
This commit is contained in:
parent
307127c296
commit
6ef092d896
@ -13,7 +13,7 @@ const useDeleteConfirmationTimer = ({
|
||||
onConfirmedDelete: () => Promise<void>
|
||||
onConfirmedReset?: () => void
|
||||
selectedHistoryItems: UniqueIdentifier[]
|
||||
hoveringHistoryRowId: Signal<UniqueIdentifier | null> | null
|
||||
hoveringHistoryRowId: Signal<UniqueIdentifier | null>
|
||||
timerDuration?: number
|
||||
}) => {
|
||||
const timerRef = useRef(null) as React.MutableRefObject<NodeJS.Timeout | null>
|
||||
@ -34,15 +34,17 @@ const useDeleteConfirmationTimer = ({
|
||||
if (timerRef.current) {
|
||||
clearTimeout(timerRef.current)
|
||||
}
|
||||
if (hoveringHistoryRowId?.value && selectedHistoryItems.length === 0) {
|
||||
|
||||
if (selectedHistoryItems.length === 0) {
|
||||
seHoveringHistoryIdDelete(hoveringHistoryRowId.value)
|
||||
}
|
||||
|
||||
setShowConfirmation(true)
|
||||
|
||||
timerRef.current = setTimeout(() => {
|
||||
resetTimer()
|
||||
}, timerDuration)
|
||||
}, [timerDuration, resetTimer, selectedHistoryItems])
|
||||
}, [timerDuration, resetTimer, selectedHistoryItems, hoveringHistoryRowId])
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
|
@ -0,0 +1,102 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { UniqueIdentifier } from '@dnd-kit/core'
|
||||
import { Signal } from '@preact/signals-react'
|
||||
import { useHotkeys } from 'react-hotkeys-hook'
|
||||
|
||||
const useKeyboardDeleteConfirmation = ({
|
||||
onConfirmedDelete,
|
||||
keyboardSelectedItemId,
|
||||
onConfirmedReset,
|
||||
selectedHistoryItems,
|
||||
timerDuration = 3000,
|
||||
}: {
|
||||
onConfirmedDelete: () => Promise<void>
|
||||
onConfirmedReset?: () => void
|
||||
selectedHistoryItems: UniqueIdentifier[]
|
||||
keyboardSelectedItemId: Signal<UniqueIdentifier | null>
|
||||
timerDuration?: number
|
||||
}) => {
|
||||
const timerRef = useRef(null) as React.MutableRefObject<NodeJS.Timeout | null>
|
||||
const [showConfirmation, setShowConfirmation] = useState(false)
|
||||
const [keyboardItemIdDelete, setKeyboardItemIdDelete] =
|
||||
useState<UniqueIdentifier | null>(null)
|
||||
|
||||
const resetTimer = useCallback(() => {
|
||||
if (timerRef.current) {
|
||||
clearTimeout(timerRef.current)
|
||||
}
|
||||
setKeyboardItemIdDelete(null)
|
||||
setShowConfirmation(false)
|
||||
onConfirmedReset?.()
|
||||
}, [onConfirmedReset])
|
||||
|
||||
const startTimer = useCallback(() => {
|
||||
if (timerRef.current) {
|
||||
clearTimeout(timerRef.current)
|
||||
}
|
||||
|
||||
// Only proceed if there's a keyboard selected item and no multi-selection
|
||||
if (!keyboardSelectedItemId.value || selectedHistoryItems.length > 0) {
|
||||
return
|
||||
}
|
||||
|
||||
setKeyboardItemIdDelete(keyboardSelectedItemId.value)
|
||||
setShowConfirmation(true)
|
||||
|
||||
timerRef.current = setTimeout(() => {
|
||||
resetTimer()
|
||||
}, timerDuration)
|
||||
}, [timerDuration, resetTimer, selectedHistoryItems, keyboardSelectedItemId])
|
||||
|
||||
// Reset confirmation when the keyboard selected item changes
|
||||
useEffect(() => {
|
||||
if (showConfirmation && keyboardItemIdDelete !== keyboardSelectedItemId.value) {
|
||||
resetTimer()
|
||||
}
|
||||
}, [keyboardSelectedItemId.value, showConfirmation, keyboardItemIdDelete, resetTimer])
|
||||
|
||||
// Reset confirmation when there are selected items (multi-selection mode)
|
||||
useEffect(() => {
|
||||
if (showConfirmation && selectedHistoryItems.length > 0) {
|
||||
resetTimer()
|
||||
}
|
||||
}, [selectedHistoryItems.length, showConfirmation, resetTimer])
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (timerRef.current) {
|
||||
clearTimeout(timerRef.current)
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
useHotkeys(
|
||||
['delete', 'backspace'],
|
||||
async (e) => {
|
||||
e.preventDefault()
|
||||
|
||||
// Only handle keyboard delete when there's a keyboard selected item and no multi-selection
|
||||
if (!keyboardSelectedItemId.value || selectedHistoryItems.length > 0) {
|
||||
return
|
||||
}
|
||||
|
||||
if (showConfirmation) {
|
||||
await onConfirmedDelete()
|
||||
resetTimer()
|
||||
} else {
|
||||
startTimer()
|
||||
}
|
||||
},
|
||||
{
|
||||
enableOnFormTags: false,
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
showConfirmation,
|
||||
keyboardItemIdDelete,
|
||||
resetTimer,
|
||||
}
|
||||
}
|
||||
|
||||
export default useKeyboardDeleteConfirmation
|
@ -63,7 +63,6 @@ export const ClipboardHistoryIconMenu = ({
|
||||
onDelete,
|
||||
setIsDeleting,
|
||||
isDark,
|
||||
setSelectHistoryItem,
|
||||
setSelectedHistoryItems,
|
||||
showSelectHistoryItems,
|
||||
}: ClipboardHistoryIconMenuProps) => {
|
||||
@ -85,12 +84,6 @@ export const ClipboardHistoryIconMenu = ({
|
||||
setShowSelectHistoryItems(!showSelectHistoryItems)
|
||||
})
|
||||
|
||||
useHotkeys(['control+s'], () => {
|
||||
if (hoveringHistoryRowId.value) {
|
||||
setSelectHistoryItem(hoveringHistoryRowId.value)
|
||||
}
|
||||
})
|
||||
|
||||
useHotkeys(['alt+h', 'meta+h'], () => {
|
||||
setIsHistoryEnabled(!isHistoryEnabled)
|
||||
})
|
||||
|
@ -387,22 +387,22 @@ export function ClipboardHistoryRowComponent({
|
||||
|
||||
const pinnedTopOffsetFirst = !isPinnedTopFirst ? 'top-[-10px]' : 'top-[5px]'
|
||||
const bgToolsPanel = `${
|
||||
isKeyboardSelected
|
||||
? 'bg-blue-50 dark:bg-blue-950/80'
|
||||
: !isPinnedTop && isOverPinned && !isNowItem
|
||||
? 'bg-orange-50 dark:!bg-transparent'
|
||||
: isDeleting || isDeleteConfirmationFromContext.value
|
||||
? 'bg-red-50 dark:bg-red-950/80'
|
||||
: contextMenuOpen.value
|
||||
? `bg-slate-100 dark:bg-slate-900 ${
|
||||
isNowItem ? 'bg-teal-50/80 dark:bg-sky-900/80' : ''
|
||||
}`
|
||||
: isCopiedOrPasted
|
||||
? 'dark:bg-green-950/80'
|
||||
: isSaved
|
||||
? 'dark:bg-sky-950/80'
|
||||
: isSelected
|
||||
? 'bg-yellow-50 dark:bg-amber-950/80'
|
||||
!isPinnedTop && isOverPinned && !isNowItem
|
||||
? 'bg-orange-50 dark:!bg-transparent'
|
||||
: isDeleting || isDeleteConfirmationFromContext.value
|
||||
? 'bg-red-50 dark:bg-red-950/80'
|
||||
: isSelected
|
||||
? 'bg-yellow-50 dark:bg-amber-950/80'
|
||||
: isKeyboardSelected
|
||||
? 'bg-blue-50 dark:bg-blue-950/80'
|
||||
: contextMenuOpen.value
|
||||
? `bg-slate-100 dark:bg-slate-900 ${
|
||||
isNowItem ? 'bg-teal-50/80 dark:bg-sky-900/80' : ''
|
||||
}`
|
||||
: isCopiedOrPasted
|
||||
? 'dark:bg-green-950/80'
|
||||
: isSaved
|
||||
? 'dark:bg-sky-950/80'
|
||||
: isNowItem
|
||||
? 'bg-teal-50/90 dark:bg-sky-950'
|
||||
: 'bg-white dark:bg-slate-950/80'
|
||||
@ -501,6 +501,10 @@ export function ClipboardHistoryRowComponent({
|
||||
>
|
||||
<Box
|
||||
className={`rounded-md justify-start duration-300 history-box relative px-3 py-1 hover:shadow-sm my-0.5 shadow-none border-2 flex flex-col ${
|
||||
isKeyboardSelected
|
||||
? 'ring-2 scale-[.98] ring-blue-400 dark:!ring-blue-600 ring-offset-1 !shadow-sm ring-offset-white dark:ring-offset-gray-800'
|
||||
: ''
|
||||
} ${
|
||||
index === 0 &&
|
||||
clipboard.updatedAt > Date.now() - MINUTE_IN_MS &&
|
||||
!isCopiedOrPasted &&
|
||||
@ -508,23 +512,23 @@ export function ClipboardHistoryRowComponent({
|
||||
!isKeyboardSelected &&
|
||||
!isSelected
|
||||
? 'bg-teal-50 hover:border-slate-300 dark:bg-sky-900/40 dark:hover:border-slate-700 hover:bg-teal-50/90 hover:dark:bg-sky-950'
|
||||
: isKeyboardSelected
|
||||
? `bg-blue-50 ring-2 scale-[.98] ring-blue-400 dark:!ring-blue-600 ring-offset-1 !shadow-sm border-blue-300 dark:bg-blue-950/80 dark:hover:border-blue-800 hover:bg-blue-50/80 ring-offset-white dark:ring-offset-gray-800 ${
|
||||
isPinnedTop ? ' dark:!bg-amber-950' : ''
|
||||
}`
|
||||
: (isDeleting || isDeleteConfirmationFromContext.value) &&
|
||||
: (isDeleting || isDeleteConfirmationFromContext.value) &&
|
||||
!isDragPreview
|
||||
? 'border-red-400 bg-red-50 dark:bg-red-950/80 dark:border-red-900/80 dark:hover:border-red-800'
|
||||
: isSelected
|
||||
? `bg-amber-50 border-amber-300 dark:bg-amber-950/80 dark:border-amber-900/80 hover:border-amber-300/80 dark:hover:border-amber-800 hover:bg-amber-50/80 ${
|
||||
isPinnedTop ? '!border dark:!bg-amber-950' : ''
|
||||
}`
|
||||
: isKeyboardSelected
|
||||
? `bg-blue-50 border-blue-300 dark:bg-blue-950/80 dark:hover:border-blue-800 hover:bg-blue-50/80 ${
|
||||
isPinnedTop ? ' dark:!bg-amber-950' : ''
|
||||
}`
|
||||
: contextMenuOpen.value
|
||||
? 'bg-slate-100 dark:bg-slate-950/80 border-slate-300 dark:border-slate-600'
|
||||
: isSaved && !isDragPreview
|
||||
? 'bg-sky-50 border-sky-600 dark:bg-sky-950/80 dark:border-sky-900/80 dark:hover:border-sky-800'
|
||||
: isCopiedOrPasted && !isDragPreview
|
||||
? `bg-green-50 border-green-600 dark:bg-green-950/80 dark:border-green-800`
|
||||
: isSelected
|
||||
? `bg-amber-50 border-amber-300 dark:bg-amber-950/80 dark:border-amber-900/80 hover:border-amber-300/80 dark:hover:border-amber-800 hover:bg-amber-50/80 ${
|
||||
isPinnedTop ? '!border dark:!bg-amber-950' : ''
|
||||
}`
|
||||
: `hover:bg-white dark:hover:bg-slate-950/80 ${
|
||||
isLargeView
|
||||
? 'border-slate-500 bg-white dark:bg-slate-950 hover:dark:border-slate-500'
|
||||
|
@ -70,12 +70,6 @@ export const ClipboardHistoryWindowIcons = ({
|
||||
setShowSelectHistoryItems(!showSelectHistoryItems)
|
||||
})
|
||||
|
||||
useHotkeys(['control+s'], () => {
|
||||
if (hoveringHistoryRowId.value) {
|
||||
setSelectHistoryItem(hoveringHistoryRowId.value)
|
||||
}
|
||||
})
|
||||
|
||||
useHotkeys(['alt+h', 'meta+h'], () => {
|
||||
setIsHistoryEnabled(!isHistoryEnabled)
|
||||
})
|
||||
|
@ -235,7 +235,7 @@ export default function ClipboardHistoryRowContextMenu({
|
||||
if (e.key === 'Delete' || e.key === 'Backspace') {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
|
||||
|
||||
if (isSelected && selectedHistoryItems && selectedHistoryItems.length > 1) {
|
||||
// Multi-select delete
|
||||
if (pendingDeleteId === 'multi') {
|
||||
@ -836,7 +836,9 @@ export default function ClipboardHistoryRowContextMenu({
|
||||
<ContextMenuSeparator />
|
||||
{isSelected && selectedHistoryItems && selectedHistoryItems.length > 1 ? (
|
||||
<ContextMenuItem
|
||||
className={pendingDeleteId === 'multi' ? 'bg-red-500/20 dark:bg-red-600/20' : ''}
|
||||
className={
|
||||
pendingDeleteId === 'multi' ? 'bg-red-500/20 dark:bg-red-600/20' : ''
|
||||
}
|
||||
onSelect={async e => {
|
||||
e.preventDefault()
|
||||
|
||||
@ -882,7 +884,9 @@ export default function ClipboardHistoryRowContextMenu({
|
||||
</ContextMenuItem>
|
||||
) : (
|
||||
<ContextMenuItem
|
||||
className={pendingDeleteId === historyId ? 'bg-red-500/20 dark:bg-red-600/20' : ''}
|
||||
className={
|
||||
pendingDeleteId === historyId ? 'bg-red-500/20 dark:bg-red-600/20' : ''
|
||||
}
|
||||
onSelect={async e => {
|
||||
e.preventDefault()
|
||||
|
||||
|
@ -35,6 +35,7 @@ import {
|
||||
showClipsMoveOnBoardId,
|
||||
showDetailsClipId,
|
||||
showHistoryDeleteConfirmationId,
|
||||
showHistoryMultiDeleteConfirmationIds,
|
||||
showKeyboardNavContextMenuClipId,
|
||||
showKeyboardNavContextMenuHistoryId,
|
||||
showLargeViewClipId,
|
||||
@ -122,6 +123,7 @@ import {
|
||||
} from '~/hooks/use-copypaste-history-item'
|
||||
import { useDebounce } from '~/hooks/use-debounce'
|
||||
import useDeleteConfirmationTimer from '~/hooks/use-delete-confirmation-items'
|
||||
import useKeyboardDeleteConfirmation from '~/hooks/use-keyboard-delete-confirmation'
|
||||
import { useSignal } from '~/hooks/use-signal'
|
||||
import {
|
||||
specialCopiedItem,
|
||||
@ -276,6 +278,38 @@ export default function ClipboardHistoryPage() {
|
||||
},
|
||||
})
|
||||
|
||||
const { showConfirmation: showConfirmationKeyboardDelete, keyboardItemIdDelete, resetTimer: resetKeyboardDeleteTimer } =
|
||||
useKeyboardDeleteConfirmation({
|
||||
keyboardSelectedItemId: keyboardSelectedItemId,
|
||||
selectedHistoryItems,
|
||||
onConfirmedDelete: async () => {
|
||||
if (keyboardSelectedItemId.value) {
|
||||
// Calculate next selection before deletion
|
||||
const currentIndex = clipboardHistory.findIndex(
|
||||
item => item.historyId === keyboardSelectedItemId.value
|
||||
)
|
||||
let nextSelectedId: UniqueIdentifier | null = null
|
||||
if (currentIndex !== -1) {
|
||||
if (currentIndex < clipboardHistory.length - 1) {
|
||||
// Select next item
|
||||
nextSelectedId = clipboardHistory[currentIndex + 1].historyId
|
||||
} else if (currentIndex > 0) {
|
||||
// Select previous item
|
||||
nextSelectedId = clipboardHistory[currentIndex - 1].historyId
|
||||
}
|
||||
// If only one item, nextSelectedId remains null
|
||||
}
|
||||
|
||||
await deleteClipboardHistoryByIds({
|
||||
historyIds: [keyboardSelectedItemId.value],
|
||||
})
|
||||
|
||||
// Update selection to the calculated next item
|
||||
keyboardSelectedItemId.value = nextSelectedId
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const isPinnedPanelHoverOpen = useMemo(() => {
|
||||
return isPinnedPanelKeepOpen.value || isPinnedPanelHovering.value
|
||||
}, [isPinnedPanelHovering.value, isPinnedPanelKeepOpen.value])
|
||||
@ -455,6 +489,8 @@ export default function ClipboardHistoryPage() {
|
||||
currentNavigationContext.value === null) &&
|
||||
keyboardSelectedItemId.value
|
||||
) {
|
||||
// Reset keyboard delete confirmation when copying
|
||||
resetKeyboardDeleteTimer()
|
||||
setCopiedItem(keyboardSelectedItemId.value)
|
||||
} else if (
|
||||
(currentNavigationContext.value === 'history' ||
|
||||
@ -485,6 +521,9 @@ export default function ClipboardHistoryPage() {
|
||||
e => {
|
||||
e.preventDefault()
|
||||
|
||||
// Reset delete confirmation when navigating
|
||||
showHistoryDeleteConfirmationId.value = null
|
||||
|
||||
if (keyboardSelectedBoardId.value) {
|
||||
const clipsOnBoard = clipItems
|
||||
.filter(
|
||||
@ -647,10 +686,46 @@ export default function ClipboardHistoryPage() {
|
||||
}
|
||||
}
|
||||
|
||||
useHotkeys(['control+s'], e => {
|
||||
if (hoveringHistoryRowId.value) {
|
||||
setSelectHistoryItem(hoveringHistoryRowId.value)
|
||||
}
|
||||
})
|
||||
|
||||
useHotkeys(['tab'], handleTabNavigation('forward'), {
|
||||
enabled: !shouldKeyboardNavigationBeDisabled.value,
|
||||
})
|
||||
|
||||
useHotkeys(
|
||||
['space'],
|
||||
() => {
|
||||
if (
|
||||
currentNavigationContext.value === 'history' ||
|
||||
currentNavigationContext.value === null
|
||||
) {
|
||||
if (keyboardSelectedItemId.value) {
|
||||
// Reset keyboard delete confirmation when selecting
|
||||
resetKeyboardDeleteTimer()
|
||||
|
||||
setSelectHistoryItem(keyboardSelectedItemId.value)
|
||||
const currentItemIndex = clipboardHistory.findIndex(
|
||||
item => item.historyId === keyboardSelectedItemId.value
|
||||
)
|
||||
const nextItem = clipboardHistory[currentItemIndex + 1]
|
||||
if (nextItem) {
|
||||
keyboardSelectedItemId.value = nextItem.historyId
|
||||
if (showLargeViewHistoryId.value) {
|
||||
showLargeViewHistoryId.value = nextItem.historyId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
enabled: !shouldKeyboardNavigationBeDisabled.value,
|
||||
}
|
||||
)
|
||||
|
||||
useHotkeys(['shift+tab'], handleTabNavigation('backward'), {
|
||||
enabled: !shouldKeyboardNavigationBeDisabled.value,
|
||||
})
|
||||
@ -658,6 +733,18 @@ export default function ClipboardHistoryPage() {
|
||||
useHotkeys(
|
||||
'esc',
|
||||
() => {
|
||||
// Clear any delete timeout when escaping
|
||||
if (deleteTimeoutRef.current) {
|
||||
clearTimeout(deleteTimeoutRef.current)
|
||||
deleteTimeoutRef.current = null
|
||||
}
|
||||
|
||||
// Reset delete confirmation on escape
|
||||
showHistoryDeleteConfirmationId.value = null
|
||||
|
||||
// Reset keyboard delete confirmation on escape
|
||||
resetKeyboardDeleteTimer()
|
||||
|
||||
// Escape closes large view first, then performs normal escape behavior
|
||||
if (showKeyboardNavContextMenuHistoryId.value) {
|
||||
showKeyboardNavContextMenuHistoryId.value = null
|
||||
@ -682,6 +769,19 @@ export default function ClipboardHistoryPage() {
|
||||
['arrowdown'],
|
||||
e => {
|
||||
e.preventDefault()
|
||||
|
||||
// Clear any delete timeout when navigating away
|
||||
if (deleteTimeoutRef.current) {
|
||||
clearTimeout(deleteTimeoutRef.current)
|
||||
deleteTimeoutRef.current = null
|
||||
}
|
||||
|
||||
// Reset delete confirmation when navigating to a different item
|
||||
showHistoryDeleteConfirmationId.value = null
|
||||
|
||||
// Reset keyboard delete confirmation when navigating
|
||||
resetKeyboardDeleteTimer()
|
||||
|
||||
const currentItemIndex = clipboardHistory.findIndex(
|
||||
item => item.historyId === keyboardSelectedItemId.value
|
||||
)
|
||||
@ -705,6 +805,19 @@ export default function ClipboardHistoryPage() {
|
||||
['arrowup'],
|
||||
e => {
|
||||
e.preventDefault()
|
||||
|
||||
// Clear any delete timeout when navigating away
|
||||
if (deleteTimeoutRef.current) {
|
||||
clearTimeout(deleteTimeoutRef.current)
|
||||
deleteTimeoutRef.current = null
|
||||
}
|
||||
|
||||
// Reset delete confirmation when navigating to a different item
|
||||
showHistoryDeleteConfirmationId.value = null
|
||||
|
||||
// Reset keyboard delete confirmation when navigating
|
||||
resetKeyboardDeleteTimer()
|
||||
|
||||
if (
|
||||
currentNavigationContext.value === 'history' ||
|
||||
currentNavigationContext.value === null
|
||||
@ -966,6 +1079,9 @@ export default function ClipboardHistoryPage() {
|
||||
}
|
||||
)
|
||||
|
||||
// Store timeout reference to clear it if needed
|
||||
const deleteTimeoutRef = useRef<NodeJS.Timeout | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Control' || e.key === 'Meta') {
|
||||
@ -975,6 +1091,11 @@ export default function ClipboardHistoryPage() {
|
||||
window.addEventListener('keydown', handleKeyDown)
|
||||
return () => {
|
||||
window.removeEventListener('keydown', handleKeyDown)
|
||||
// Clean up delete timeout on unmount
|
||||
if (deleteTimeoutRef.current) {
|
||||
clearTimeout(deleteTimeoutRef.current)
|
||||
deleteTimeoutRef.current = null
|
||||
}
|
||||
}
|
||||
}, [
|
||||
clipboardHistory,
|
||||
@ -1225,13 +1346,22 @@ export default function ClipboardHistoryPage() {
|
||||
|
||||
const hasIsDeleting = (historyId: UniqueIdentifier) => {
|
||||
return (
|
||||
(showConfirmation && selectedHistoryItems.includes(historyId)) ||
|
||||
// Keyboard delete confirmation - only for the specific keyboard selected item
|
||||
(showConfirmationKeyboardDelete &&
|
||||
historyId === keyboardItemIdDelete &&
|
||||
historyId === keyboardSelectedItemId.value) ||
|
||||
// Mouse delete confirmation - only when keyboard delete is NOT active
|
||||
(showConfirmation && !showConfirmationKeyboardDelete && selectedHistoryItems.includes(historyId)) ||
|
||||
// Single item delete confirmation
|
||||
historyId === showHistoryDeleteConfirmationId.value ||
|
||||
(showConfirmation && historyId === hoveringHistoryIdDelete) ||
|
||||
// Hovering delete confirmation - only when keyboard delete is NOT active
|
||||
(showConfirmation && !showConfirmationKeyboardDelete && historyId === hoveringHistoryIdDelete) ||
|
||||
// Drag over trash
|
||||
historyId === dragOverTrashId ||
|
||||
(Boolean(dragOverTrashId) &&
|
||||
Boolean(activeDragId) &&
|
||||
selectedHistoryItems.includes(historyId)) ||
|
||||
// Menu deleting
|
||||
(isMenuDeleting && selectedHistoryItems.includes(historyId))
|
||||
)
|
||||
}
|
||||
@ -2107,6 +2237,7 @@ export default function ClipboardHistoryPage() {
|
||||
className="pointer-events-auto rounded-full bg-slate-300 dark:bg-slate-600 hover:bg-slate-200 hover:dark:bg-slate-700"
|
||||
onClick={() => {
|
||||
scrollToTopHistoryList(true)
|
||||
resetKeyboardNavigation()
|
||||
}}
|
||||
>
|
||||
<Text className="text-mute text-xs text-center px-3">
|
||||
|
@ -13,7 +13,10 @@ import { atomWithStore } from 'jotai-zustand'
|
||||
import { createStore } from 'zustand/vanilla'
|
||||
|
||||
import DOMPurify from '../components/libs/dompurify'
|
||||
import { DEFAULT_SPECIAL_PASTE_OPERATIONS, DEFAULT_SPECIAL_PASTE_CATEGORIES } from './constants'
|
||||
import {
|
||||
DEFAULT_SPECIAL_PASTE_CATEGORIES,
|
||||
DEFAULT_SPECIAL_PASTE_OPERATIONS,
|
||||
} from './constants'
|
||||
import {
|
||||
availableVersionBody,
|
||||
availableVersionDate,
|
||||
@ -549,7 +552,6 @@ export const settingsStore = createStore<SettingsStoreState & Settings>()((set,
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
return set(() => ({ [name]: value }))
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
|
@ -37,6 +37,9 @@ export const resetTimeModalInterval = signal<NodeJS.Timeout | null>(null)
|
||||
|
||||
// Clipboard History Signals
|
||||
export const showHistoryDeleteConfirmationId = signal<UniqueIdentifier | null>(null)
|
||||
export const showHistoryMultiDeleteConfirmationIds = signal<UniqueIdentifier[] | null>(
|
||||
null
|
||||
)
|
||||
export const hoveringHistoryRowId = signal<UniqueIdentifier | null>(null)
|
||||
export const showLargeViewHistoryId = signal<UniqueIdentifier | null>(null)
|
||||
export const showKeyboardNavContextMenuHistoryId = signal<UniqueIdentifier | null>(null)
|
||||
@ -147,7 +150,7 @@ export function resetMenuCreateOrEdit() {
|
||||
export function resetKeyboardNavigation() {
|
||||
currentNavigationContext.value = null
|
||||
keyboardSelectedItemId.value = null
|
||||
hoveringHistoryRowId.value = null
|
||||
// hoveringHistoryRowId.value = null
|
||||
keyboardSelectedBoardId.value = null
|
||||
keyboardSelectedClipId.value = null
|
||||
currentBoardIndex.value = 0
|
||||
|
Loading…
x
Reference in New Issue
Block a user