22import { splitKeyCombination , splitKeySequence } from '@/composables/hotkey/hotkey-parsing'
33
44// Utilities
5- import { onBeforeUnmount , toValue , watch } from 'vue'
5+ import { onScopeDispose , toValue , watch } from 'vue'
66import { IN_BROWSER } from '@/util'
7- import { getCurrentInstance } from '@/util/getCurrentInstance'
87
98// Types
109import type { MaybeRef } from '@/util'
@@ -36,13 +35,6 @@ export function useHotkey (
3635 let isSequence = false
3736 let groupIndex = 0
3837
39- function clearTimer ( ) {
40- if ( ! timeout ) return
41-
42- clearTimeout ( timeout )
43- timeout = 0
44- }
45-
4638 function isInputFocused ( ) {
4739 if ( toValue ( inputs ) ) return false
4840
@@ -58,15 +50,15 @@ export function useHotkey (
5850
5951 function resetSequence ( ) {
6052 groupIndex = 0
61- clearTimer ( )
53+ clearTimeout ( timeout )
6254 }
6355
6456 function handler ( e : KeyboardEvent ) {
6557 const group = keyGroups [ groupIndex ]
6658
6759 if ( ! group || isInputFocused ( ) ) return
6860
69- if ( ! matchesKeyGroup ( e , group ) ) {
61+ if ( ! matchesKeyGroup ( e , group , isMac ) ) {
7062 if ( isSequence ) resetSequence ( )
7163 return
7264 }
@@ -78,7 +70,7 @@ export function useHotkey (
7870 return
7971 }
8072
81- clearTimer ( )
73+ clearTimeout ( timeout )
8274 groupIndex ++
8375
8476 if ( groupIndex === keyGroups . length ) {
@@ -92,14 +84,14 @@ export function useHotkey (
9284
9385 function cleanup ( ) {
9486 window . removeEventListener ( toValue ( event ) , handler )
95- clearTimer ( )
87+ clearTimeout ( timeout )
9688 }
9789
98- watch ( ( ) => toValue ( keys ) , function ( unrefKeys ) {
90+ watch ( ( ) => toValue ( keys ) , newKeys => {
9991 cleanup ( )
10092
101- if ( unrefKeys ) {
102- const groups = splitKeySequence ( unrefKeys . toLowerCase ( ) )
93+ if ( newKeys ) {
94+ const groups = splitKeySequence ( newKeys . toLowerCase ( ) )
10395 isSequence = groups . length > 1
10496 keyGroups = groups
10597 resetSequence ( )
@@ -108,59 +100,54 @@ export function useHotkey (
108100 } , { immediate : true } )
109101
110102 // Watch for changes in the event type to re-register the listener
111- watch ( ( ) => toValue ( event ) , function ( newEvent , oldEvent ) {
103+ watch ( ( ) => toValue ( event ) , ( newEvent , oldEvent ) => {
112104 if ( oldEvent && keyGroups && keyGroups . length > 0 ) {
113105 window . removeEventListener ( oldEvent , handler )
114106 window . addEventListener ( newEvent , handler )
115107 }
116108 } )
117109
118- try {
119- getCurrentInstance ( 'useHotkey' )
120- onBeforeUnmount ( cleanup )
121- } catch {
122- // Not in Vue setup context
123- }
110+ onScopeDispose ( cleanup , true )
111+
112+ return cleanup
113+ }
124114
125- function parseKeyGroup ( group : string ) {
126- const MODIFIERS = [ 'ctrl' , 'shift' , 'alt' , 'meta' , 'cmd' ]
115+ function matchesKeyGroup ( e : KeyboardEvent , group : string , isMac : boolean ) {
116+ const { modifiers , actualKey } = parseKeyGroup ( group )
127117
128- // Use the shared combination splitting logic
129- const parts = splitKeyCombination ( group . toLowerCase ( ) )
118+ const expectCtrl = modifiers . ctrl || ( ! isMac && ( modifiers . cmd || modifiers . meta ) )
119+ const expectMeta = isMac && ( modifiers . cmd || modifiers . meta )
130120
131- // If the combination is invalid, return empty result
132- if ( parts . length === 0 ) {
133- return { modifiers : Object . fromEntries ( MODIFIERS . map ( m => [ m , false ] ) ) , actualKey : undefined }
134- }
121+ return (
122+ e . ctrlKey === expectCtrl &&
123+ e . metaKey === expectMeta &&
124+ e . shiftKey === modifiers . shift &&
125+ e . altKey === modifiers . alt &&
126+ e . key . toLowerCase ( ) === actualKey ?. toLowerCase ( )
127+ )
128+ }
135129
136- const modifiers = Object . fromEntries ( MODIFIERS . map ( m => [ m , false ] ) ) as Record < string , boolean >
137- let actualKey : string | undefined
130+ function parseKeyGroup ( group : string ) {
131+ const MODIFIERS = [ 'ctrl' , 'shift' , 'alt' , 'meta' , 'cmd' ]
138132
139- for ( const part of parts ) {
140- if ( MODIFIERS . includes ( part ) ) {
141- modifiers [ part ] = true
142- } else {
143- actualKey = part
144- }
145- }
133+ // Use the shared combination splitting logic
134+ const parts = splitKeyCombination ( group . toLowerCase ( ) )
146135
147- return { modifiers, actualKey }
136+ // If the combination is invalid, return empty result
137+ if ( parts . length === 0 ) {
138+ return { modifiers : Object . fromEntries ( MODIFIERS . map ( m => [ m , false ] ) ) , actualKey : undefined }
148139 }
149140
150- function matchesKeyGroup ( e : KeyboardEvent , group : string ) {
151- const { modifiers , actualKey } = parseKeyGroup ( group )
141+ const modifiers = Object . fromEntries ( MODIFIERS . map ( m => [ m , false ] ) ) as Record < string , boolean >
142+ let actualKey : string | undefined
152143
153- const expectCtrl = modifiers . ctrl || ( ! isMac && ( modifiers . cmd || modifiers . meta ) )
154- const expectMeta = isMac && ( modifiers . cmd || modifiers . meta )
155-
156- return (
157- e . ctrlKey === expectCtrl &&
158- e . metaKey === expectMeta &&
159- e . shiftKey === modifiers . shift &&
160- e . altKey === modifiers . alt &&
161- e . key . toLowerCase ( ) === actualKey ?. toLowerCase ( )
162- )
144+ for ( const part of parts ) {
145+ if ( MODIFIERS . includes ( part ) ) {
146+ modifiers [ part ] = true
147+ } else {
148+ actualKey = part
149+ }
163150 }
164151
165- return cleanup
152+ return { modifiers , actualKey }
166153}
0 commit comments