Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | 21x 21x 22x 21x 22x 22x 22x 22x 22x 22x 22x 22x 22x 22x 22x 22x 22x 7x 7x 1x 1x 1x 1x 1x 1x 7x 7x | import { useEffect, useMemo, useRef } from 'react'
import { isTauri } from '../mock-tauri'
import { filterEntries, sortByModified, buildRelationshipGroups } from '../components/NoteList'
import type { VaultEntry, SidebarSelection } from '../types'
interface Tab {
entry: VaultEntry
content: string
}
interface KeyboardNavigationOptions {
tabs: Tab[]
activeTabPath: string | null
entries: VaultEntry[]
selection: SidebarSelection
allContent: Record<string, string>
onSwitchTab: (path: string) => void
onReplaceActiveTab: (entry: VaultEntry) => void
onSelectNote: (entry: VaultEntry) => void
}
function computeVisibleNotes(
entries: VaultEntry[],
selection: SidebarSelection,
allContent: Record<string, string>,
): VaultEntry[] {
Iif (selection.kind === 'entity') {
return buildRelationshipGroups(selection.entry, entries, allContent)
.flatMap((g) => g.entries)
}
return [...filterEntries(entries, selection)].sort(sortByModified)
}
function navigateTab(
tabsRef: React.RefObject<Tab[]>,
activeTabPathRef: React.RefObject<string | null>,
onSwitchTab: React.RefObject<(path: string) => void>,
direction: 1 | -1,
) {
const currentTabs = tabsRef.current!
if (currentTabs.length === 0) return
const currentPath = activeTabPathRef.current
const currentIndex = currentTabs.findIndex((t) => t.entry.path === currentPath)
const nextIndex = (currentIndex + direction + currentTabs.length) % currentTabs.length
onSwitchTab.current!(currentTabs[nextIndex].entry.path)
}
function navigateNote(
visibleNotesRef: React.RefObject<VaultEntry[]>,
activeTabPathRef: React.RefObject<string | null>,
onReplace: React.RefObject<(entry: VaultEntry) => void>,
onSelect: React.RefObject<(entry: VaultEntry) => void>,
direction: 1 | -1,
) {
const notes = visibleNotesRef.current!
if (notes.length === 0) return
const currentPath = activeTabPathRef.current
const currentIndex = notes.findIndex((n) => n.path === currentPath)
const nextIndex = currentIndex === -1
? (direction === 1 ? 0 : notes.length - 1)
: (currentIndex + direction + notes.length) % notes.length
const nextNote = notes[nextIndex]
if (currentPath) {
onReplace.current!(nextNote)
} else {
onSelect.current!(nextNote)
}
}
export function useKeyboardNavigation({
tabs,
activeTabPath,
entries,
selection,
allContent,
onSwitchTab,
onReplaceActiveTab,
onSelectNote,
}: KeyboardNavigationOptions) {
const visibleNotes = useMemo(
() => computeVisibleNotes(entries, selection, allContent),
[entries, selection, allContent],
)
const tabsRef = useRef(tabs)
tabsRef.current = tabs
const activeTabPathRef = useRef(activeTabPath)
activeTabPathRef.current = activeTabPath
const visibleNotesRef = useRef(visibleNotes)
visibleNotesRef.current = visibleNotes
const onSwitchTabRef = useRef(onSwitchTab)
onSwitchTabRef.current = onSwitchTab
const onReplaceRef = useRef(onReplaceActiveTab)
onReplaceRef.current = onReplaceActiveTab
const onSelectNoteRef = useRef(onSelectNote)
onSelectNoteRef.current = onSelectNote
useEffect(() => {
const isRunningInTauri = isTauri()
const handleKeyDown = (e: KeyboardEvent) => {
const mod = e.metaKey || e.ctrlKey
Iif (!mod) return
const isTabShortcut = isRunningInTauri
? e.altKey && !e.shiftKey
: e.shiftKey && !e.altKey
const isNoteShortcut = e.altKey && !e.shiftKey
Iif (isTabShortcut && (e.key === 'ArrowLeft' || e.key === 'ArrowRight')) {
e.preventDefault()
navigateTab(tabsRef, activeTabPathRef, onSwitchTabRef, e.key === 'ArrowRight' ? 1 : -1)
I} else if (isNoteShortcut && (e.key === 'ArrowUp' || e.key === 'ArrowDown')) {
e.preventDefault()
navigateNote(visibleNotesRef, activeTabPathRef, onReplaceRef, onSelectNoteRef, e.key === 'ArrowDown' ? 1 : -1)
}
}
window.addEventListener('keydown', handleKeyDown)
return () => window.removeEventListener('keydown', handleKeyDown)
}, [])
}
|