[READ-ONLY] a fast, modern browser for the npm registry
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: ignore shortcuts on text input (#547)

Co-authored-by: Philippe Serhal <philippe.serhal@gmail.com>

authored by

SHAMIL
Philippe Serhal
and committed by
GitHub
8dc7254d 21682bba

+38 -23
+2 -6
app/app.vue
··· 1 1 <script setup lang="ts"> 2 2 import type { Directions } from '@nuxtjs/i18n' 3 3 import { useEventListener } from '@vueuse/core' 4 + import { isEditableElement } from '~/utils/input' 4 5 5 6 const route = useRoute() 6 7 const router = useRouter() ··· 39 40 // "/" focuses search or navigates to search page 40 41 // "?" highlights all keyboard shortcut elements 41 42 function handleGlobalKeydown(e: KeyboardEvent) { 42 - const target = e.target as HTMLElement 43 - 44 - const isEditableTarget = 45 - target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable 46 - 47 - if (isEditableTarget) return 43 + if (isEditableElement(e.target)) return 48 44 49 45 if (e.key === '/') { 50 46 e.preventDefault()
+4 -11
app/components/AppHeader.vue
··· 1 1 <script setup lang="ts"> 2 + import { isEditableElement } from '~/utils/input' 3 + 2 4 withDefaults( 3 5 defineProps<{ 4 6 showLogo?: boolean ··· 62 64 onKeyStroke( 63 65 ',', 64 66 e => { 65 - // Don't trigger if user is typing in an input 66 - const target = e.target as HTMLElement 67 - if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable) { 68 - return 69 - } 67 + if (isEditableElement(e.target)) return 70 68 71 69 e.preventDefault() 72 70 navigateTo('/settings') ··· 79 77 e => { 80 78 // Allow more specific handlers to take precedence 81 79 if (e.defaultPrevented) return 82 - 83 - // Don't trigger if user is typing in an input 84 - const target = e.target as HTMLElement 85 - if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable) { 86 - return 87 - } 80 + if (isEditableElement(e.target)) return 88 81 89 82 e.preventDefault() 90 83 navigateTo('/compare')
+23 -6
app/pages/[...package].vue
··· 4 4 import { assertValidPackageName } from '#shared/utils/npm' 5 5 import { joinURL } from 'ufo' 6 6 import { areUrlsEquivalent } from '#shared/utils/url' 7 + import { isEditableElement } from '~/utils/input' 7 8 8 9 definePageMeta({ 9 10 name: 'package', ··· 113 114 114 115 // If latest is deprecated, show "package deprecated" 115 116 if (isLatestDeprecated) { 116 - return { type: 'package' as const, message: displayVersion.value.deprecated } 117 + return { 118 + type: 'package' as const, 119 + message: displayVersion.value.deprecated, 120 + } 117 121 } 118 122 119 123 // Otherwise show "version deprecated" ··· 215 219 216 220 return { 217 221 name: 'docs' as const, 218 - params: { path: [...pkg.value!.name.split('/'), 'v', displayVersion.value.version] }, 222 + params: { 223 + path: [...pkg.value!.name.split('/'), 'v', displayVersion.value.version], 224 + }, 219 225 } 220 226 }) 221 227 ··· 314 320 onKeyStroke( 315 321 '.', 316 322 e => { 323 + if (isEditableElement(e.target)) return 317 324 if (pkg.value && displayVersion.value) { 318 325 e.preventDefault() 319 326 navigateTo({ ··· 330 337 onKeyStroke( 331 338 'd', 332 339 e => { 340 + if (isEditableElement(e.target)) return 333 341 if (docsLink.value) { 334 342 e.preventDefault() 335 343 navigateTo(docsLink.value) ··· 339 347 ) 340 348 341 349 onKeyStroke('c', e => { 350 + if (isEditableElement(e.target)) return 342 351 if (pkg.value) { 343 352 e.preventDefault() 344 353 router.push({ path: '/compare', query: { packages: pkg.value.name } }) ··· 487 496 <NuxtLink 488 497 :to="{ 489 498 name: 'code', 490 - params: { path: [...pkg.name.split('/'), 'v', displayVersion.version] }, 499 + params: { 500 + path: [...pkg.name.split('/'), 'v', displayVersion.version], 501 + }, 491 502 }" 492 503 class="px-2 py-1.5 font-mono text-xs rounded transition-colors duration-150 border border-transparent text-fg-subtle hover:text-fg hover:bg-bg hover:shadow hover:border-border focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50 inline-flex items-center gap-1.5" 493 504 aria-keyshortcuts="." ··· 637 648 <NuxtLink 638 649 :to="{ 639 650 name: 'code', 640 - params: { path: [...pkg.name.split('/'), 'v', displayVersion.version] }, 651 + params: { 652 + path: [...pkg.name.split('/'), 'v', displayVersion.version], 653 + }, 641 654 }" 642 655 class="link-subtle font-mono text-sm inline-flex items-center gap-1.5" 643 656 > ··· 671 684 <p v-if="deprecationNotice.message" class="text-base m-0"> 672 685 <MarkdownText :text="deprecationNotice.message" /> 673 686 </p> 674 - <p v-else class="text-base m-0 italic">{{ $t('package.deprecation.no_reason') }}</p> 687 + <p v-else class="text-base m-0 italic"> 688 + {{ $t('package.deprecation.no_reason') }} 689 + </p> 675 690 </div> 676 691 677 692 <!-- Stats grid --> ··· 1057 1072 role="alert" 1058 1073 class="flex flex-col items-center py-20 text-center" 1059 1074 > 1060 - <h1 class="font-mono text-2xl font-medium mb-4">{{ $t('package.not_found') }}</h1> 1075 + <h1 class="font-mono text-2xl font-medium mb-4"> 1076 + {{ $t('package.not_found') }} 1077 + </h1> 1061 1078 <p class="text-fg-muted mb-8"> 1062 1079 {{ error?.message ?? $t('package.not_found_message') }} 1063 1080 </p>
+9
app/utils/input.ts
··· 4 4 autocorrect: 'off', 5 5 spellcheck: 'false', 6 6 } as const 7 + 8 + /** 9 + * Check if an event target is an editable element (input, textarea, or contenteditable). 10 + * Useful for keyboard shortcut handlers that should not trigger when the user is typing. 11 + */ 12 + export function isEditableElement(target: EventTarget | null): boolean { 13 + if (!target || !(target instanceof HTMLElement)) return false 14 + return target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable 15 + }