Skip to content

Commit dc6195e

Browse files
committed
feat: try extract shortcuts
1 parent 9d52025 commit dc6195e

File tree

4 files changed

+60
-42
lines changed

4 files changed

+60
-42
lines changed

app/app.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ if (import.meta.server) {
5050
const keyboardShortcuts = useKeyboardShortcuts()
5151
const { settings } = useSettings()
5252
53+
initKeyShortcuts()
54+
5355
onKeyDown(
5456
'/',
5557
e => {

app/components/AppHeader.vue

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
<script setup lang="ts">
22
import { LinkBase } from '#components'
33
import type { NavigationConfig, NavigationConfigWithGroups } from '~/types'
4-
import { isEditableElement } from '~/utils/input'
54
import { NPMX_DOCS_SITE } from '#shared/utils/constants'
65
7-
const keyboardShortcuts = useKeyboardShortcuts()
86
const discord = useDiscordLink()
97
108
withDefaults(
@@ -200,22 +198,10 @@ function handleSearchFocus() {
200198
showFullSearch.value = true
201199
}
202200
203-
onKeyStroke(
204-
e => {
205-
if (!keyboardShortcuts.value || isEditableElement(e.target)) {
206-
return
207-
}
208-
209-
for (const link of desktopLinks.value) {
210-
if (link.to && link.keyshortcut && isKeyWithoutModifiers(e, link.keyshortcut)) {
211-
e.preventDefault()
212-
navigateTo(link.to)
213-
break
214-
}
215-
}
216-
},
217-
{ dedupe: true },
218-
)
201+
useShortcuts({
202+
c: () => ({ name: 'compare' }),
203+
',': () => ({ name: 'settings' }),
204+
})
219205
</script>
220206

221207
<template>

app/components/Package/Header.vue

Lines changed: 7 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
<script setup lang="ts">
2-
import type { RouteLocationRaw } from 'vue-router'
32
import { SCROLL_TO_TOP_THRESHOLD } from '~/composables/useScrollToTop'
4-
import { isEditableElement } from '~/utils/input'
53
64
const props = defineProps<{
75
pkg?: Pick<SlimPackument, 'name' | 'versions' | 'dist-tags'> | null
@@ -119,28 +117,13 @@ const diffLink = computed((): RouteLocationRaw | null => {
119117
return diffRoute(props.pkg.name, props.resolvedVersion, props.latestVersion.version)
120118
})
121119
122-
const keyboardShortcuts = useKeyboardShortcuts()
123-
124-
const shortcuts: [string, () => RouteLocationRaw | null | false | undefined][] = [
125-
['.', () => codeLink.value],
126-
['m', () => mainLink.value],
127-
['d', () => docsLink.value],
128-
['c', () => props.pkg && { name: 'compare' as const, query: { packages: props.pkg.name } }],
129-
['f', () => diffLink.value],
130-
]
131-
132-
for (const [key, getTarget] of shortcuts) {
133-
onKeyStroke(
134-
e => keyboardShortcuts.value && isKeyWithoutModifiers(e, key) && !isEditableElement(e.target),
135-
e => {
136-
const target = getTarget()
137-
if (!target) return
138-
e.preventDefault()
139-
navigateTo(target)
140-
},
141-
{ dedupe: true },
142-
)
143-
}
120+
useShortcuts({
121+
'.': () => codeLink.value,
122+
'm': () => mainLink.value,
123+
'd': () => docsLink.value,
124+
'c': () => props.pkg && { name: 'compare' as const, query: { packages: props.pkg.name } },
125+
'f': () => diffLink.value,
126+
})
144127
145128
const fundingUrl = computed(() => {
146129
let funding = props.displayVersion?.funding

app/composables/useShortcuts.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import type { RouteLocationRaw } from 'vue-router'
2+
import { isEditableElement, isKeyWithoutModifiers } from '~/utils/input'
3+
4+
type ShortcutTarget = RouteLocationRaw | null | false | undefined
5+
type ShortcutTargetFactory = () => ShortcutTarget
6+
7+
const registry = new Map<string, ShortcutTargetFactory[]>()
8+
9+
export function initKeyShortcuts() {
10+
const keyboardShortcuts = useKeyboardShortcuts()
11+
12+
onKeyStroke(
13+
e => !e.repeat && keyboardShortcuts.value && !isEditableElement(e.target),
14+
e => {
15+
for (const [key, stack] of registry) {
16+
if (!isKeyWithoutModifiers(e, key)) continue
17+
const getTarget = stack.at(-1)
18+
if (!getTarget) continue
19+
const target = getTarget()
20+
if (!target) return
21+
e.preventDefault()
22+
navigateTo(target)
23+
return
24+
}
25+
},
26+
)
27+
}
28+
29+
export function useShortcuts(map: Record<string, () => ShortcutTarget>) {
30+
if (!import.meta.client) return
31+
32+
for (const [key, fn] of Object.entries(map)) {
33+
const entry = registry.get(key) ?? []
34+
if (entry.includes(fn)) continue
35+
entry.push(fn)
36+
registry.set(key, entry)
37+
}
38+
39+
onScopeDispose(() => {
40+
for (const [key, fn] of Object.entries(map)) {
41+
const stack = registry.get(key)
42+
if (!stack) continue
43+
const idx = stack.lastIndexOf(fn)
44+
if (idx !== -1) stack.splice(idx, 1)
45+
}
46+
})
47+
}

0 commit comments

Comments
 (0)