📄 useMagicKeys¶
📊 Analysis Summary¶
| Metric | Count |
|---|---|
| 🔧 Functions | 6 |
| 📦 Imports | 10 |
| 🔄 Re-exports | 1 |
| 🟢 Vue Composition API | 3 |
| 📐 Interfaces | 2 |
| 📑 Type Aliases | 1 |
📚 Table of Contents¶
🛠️ File Location:¶
📂 packages/core/useMagicKeys/index.ts
📦 Imports¶
| Name | Source |
|---|---|
ComputedRef |
vue |
MaybeRefOrGetter |
vue |
noop |
@vueuse/shared |
computed |
vue |
reactive |
vue |
shallowRef |
vue |
toValue |
vue |
defaultWindow |
../_configurable |
useEventListener |
../useEventListener |
DefaultMagicKeysAliasMap |
./aliasMap |
Re-exports¶
| Type | Source | Exported Names |
|---|---|---|
| named | ./aliasMap |
DefaultMagicKeysAliasMap |
Vue Composition API¶
| Name | Type | Reactive Variables | Composables |
|---|---|---|---|
reactive |
reactive | none | none |
reactive |
reactive | none | none |
computed |
computed | none | none |
Functions¶
useMagicKeys(options: UseMagicKeysOptions<T>): UseMagicKeysReturn<T>¶
Reactive keys pressed state, with magical keys combination support.
See: https://vueuse.org/useMagicKeys
Raw JSDoc
Calls:
reactive (from vue)current.clearsetRefse.getModifierStatekeys.forEachdepsSet.addkey[0].toUpperCasekey.slicedepsMap.get['shift', 'alt'].includesArray.fromdepsArray.indexOfdepsArray.forEachcurrent.deletedeps.cleare.key?.toLowerCasee.code?.toLowerCase[code, key].filtercurrent.addusedKeys.addupdateDepsclearDepsmetaDeps.forEachmetaDeps.clearuseEventListener (from ../useEventListener)updateRefsonEventFiredReflect.getprop.toLowerCase/[+_-]/.testprop.split(/[+_-]/g).mapi.trimcomputed (from vue)keys.map(key => toValue(proxy[key])).everyshallowRef (from vue)toValue (from vue)
Internal Comments:
// current set
// #1312
// In macOS, keys won't trigger "keyup" event when Meta key is released
// We track it's combination and release manually
// Meta key released (x4)
// #1350 (x3)
// alias
// create new tracking
Code
export function useMagicKeys<T extends boolean = false>(options: UseMagicKeysOptions<T> = {}): UseMagicKeysReturn<T> {
const {
reactive: useReactive = false,
target = defaultWindow,
aliasMap = DefaultMagicKeysAliasMap,
passive = true,
onEventFired = noop,
} = options
const current = reactive(new Set<string>())
const obj = {
toJSON() { return {} },
current,
}
const refs: Record<string, any> = useReactive ? reactive(obj) : obj
const metaDeps = new Set<string>()
const depsMap = new Map<string, Set<string>>([
['Meta', metaDeps],
['Shift', new Set<string>()],
['Alt', new Set<string>()],
])
const usedKeys = new Set<string>()
function setRefs(key: string, value: boolean) {
if (key in refs) {
if (useReactive)
refs[key] = value
else
refs[key].value = value
}
}
function reset() {
current.clear()
for (const key of usedKeys)
setRefs(key, false)
}
function updateDeps(value: boolean, e: KeyboardEvent, keys: string[]) {
if (!value || typeof e.getModifierState !== 'function')
return
for (const [modifier, depsSet] of depsMap) {
if (e.getModifierState(modifier)) {
keys.forEach(key => depsSet.add(key))
break
}
}
}
function clearDeps(value: boolean, key: string) {
if (value)
return
const depsMapKey = `${key[0].toUpperCase()}${key.slice(1)}`
const deps = depsMap.get(depsMapKey)
if (!(['shift', 'alt'].includes(key)) || !deps)
return
const depsArray = Array.from(deps)
const depsIndex = depsArray.indexOf(key)
depsArray.forEach((key, index) => {
if (index >= depsIndex) {
current.delete(key)
setRefs(key, false)
}
})
deps.clear()
}
function updateRefs(e: KeyboardEvent, value: boolean) {
const key = e.key?.toLowerCase()
const code = e.code?.toLowerCase()
const values = [code, key].filter(Boolean)
if (!key)
return
// current set
if (key) {
if (value)
current.add(key)
else
current.delete(key)
}
for (const key of values) {
usedKeys.add(key)
setRefs(key, value)
}
updateDeps(value, e, [...current, ...values])
clearDeps(value, key)
// #1312
// In macOS, keys won't trigger "keyup" event when Meta key is released
// We track it's combination and release manually
if (key === 'meta' && !value) {
// Meta key released
metaDeps.forEach((key) => {
current.delete(key)
setRefs(key, false)
})
metaDeps.clear()
}
}
useEventListener(target, 'keydown', (e: KeyboardEvent) => {
updateRefs(e, true)
return onEventFired(e)
}, { passive })
useEventListener(target, 'keyup', (e: KeyboardEvent) => {
updateRefs(e, false)
return onEventFired(e)
}, { passive })
// #1350
useEventListener('blur', reset, { passive })
useEventListener('focus', reset, { passive })
const proxy = new Proxy(
refs,
{
get(target, prop, rec) {
if (typeof prop !== 'string')
return Reflect.get(target, prop, rec)
prop = prop.toLowerCase()
// alias
if (prop in aliasMap)
prop = aliasMap[prop]
// create new tracking
if (!(prop in refs)) {
if (/[+_-]/.test(prop)) {
const keys = prop.split(/[+_-]/g).map(i => i.trim())
refs[prop] = computed(() => keys.map(key => toValue(proxy[key])).every(Boolean))
}
else {
refs[prop] = shallowRef(false)
}
}
const r = Reflect.get(target, prop, rec)
return useReactive ? toValue(r) : r
},
},
)
return proxy as UseMagicKeysReturn<T>
}
Internal helpers¶
Declared inside another function in this file.
setRefs(key: string, value: boolean): void¶
Parameters:
keystringvalueboolean
Returns: void
Code
reset(): void¶
Returns: void
Calls:
current.clearsetRefs
updateDeps(value: boolean, e: KeyboardEvent, keys: string[]): void¶
Parameters:
valuebooleaneKeyboardEventkeysstring[]
Returns: void
Calls:
e.getModifierStatekeys.forEachdepsSet.add
Code
clearDeps(value: boolean, key: string): void¶
Parameters:
valuebooleankeystring
Returns: void
Calls:
key[0].toUpperCasekey.slicedepsMap.get['shift', 'alt'].includesArray.fromdepsArray.indexOfdepsArray.forEachcurrent.deletesetRefsdeps.clear
Code
function clearDeps(value: boolean, key: string) {
if (value)
return
const depsMapKey = `${key[0].toUpperCase()}${key.slice(1)}`
const deps = depsMap.get(depsMapKey)
if (!(['shift', 'alt'].includes(key)) || !deps)
return
const depsArray = Array.from(deps)
const depsIndex = depsArray.indexOf(key)
depsArray.forEach((key, index) => {
if (index >= depsIndex) {
current.delete(key)
setRefs(key, false)
}
})
deps.clear()
}
updateRefs(e: KeyboardEvent, value: boolean): void¶
Parameters:
eKeyboardEventvalueboolean
Returns: void
Calls:
e.key?.toLowerCasee.code?.toLowerCase[code, key].filtercurrent.addcurrent.deleteusedKeys.addsetRefsupdateDepsclearDepsmetaDeps.forEachmetaDeps.clear
Internal Comments:
// current set
// #1312
// In macOS, keys won't trigger "keyup" event when Meta key is released
// We track it's combination and release manually
// Meta key released (x4)
Code
function updateRefs(e: KeyboardEvent, value: boolean) {
const key = e.key?.toLowerCase()
const code = e.code?.toLowerCase()
const values = [code, key].filter(Boolean)
if (!key)
return
// current set
if (key) {
if (value)
current.add(key)
else
current.delete(key)
}
for (const key of values) {
usedKeys.add(key)
setRefs(key, value)
}
updateDeps(value, e, [...current, ...values])
clearDeps(value, key)
// #1312
// In macOS, keys won't trigger "keyup" event when Meta key is released
// We track it's combination and release manually
if (key === 'meta' && !value) {
// Meta key released
metaDeps.forEach((key) => {
current.delete(key)
setRefs(key, false)
})
metaDeps.clear()
}
}
Interfaces¶
UseMagicKeysOptions<Reactive extends boolean>¶
Interface Code
export interface UseMagicKeysOptions<Reactive extends boolean> {
/**
* Returns a reactive object instead of an object of refs
*
* @default false
*/
reactive?: Reactive
/**
* Target for listening events
*
* @default window
*/
target?: MaybeRefOrGetter<EventTarget>
/**
* Alias map for keys, all the keys should be lowercase
* { target: keycode }
*
* @example { ctrl: "control" }
* @default <predefined-map>
*/
aliasMap?: Record<string, string>
/**
* Register passive listener
*
* @default true
*/
passive?: boolean
/**
* Custom event handler for keydown/keyup event.
* Useful when you want to apply custom logic.
*
* When using `e.preventDefault()`, you will need to pass `passive: false` to useMagicKeys().
*/
onEventFired?: (e: KeyboardEvent) => void | boolean
}
Properties¶
| Name | Type | Optional | Description |
|---|---|---|---|
reactive |
Reactive |
✓ | not shown |
target |
MaybeRefOrGetter<EventTarget> |
✓ | not shown |
aliasMap |
Record<string, string> |
✓ | not shown |
passive |
boolean |
✓ | not shown |
onEventFired |
(e: KeyboardEvent) => void \| boolean |
✓ | not shown |
MagicKeysInternal¶
Interface Code
Properties¶
| Name | Type | Optional | Description |
|---|---|---|---|
current |
Set<string> |
✗ | not shown |
Type Aliases¶
UseMagicKeysReturn<Reactive extends boolean>¶
type UseMagicKeysReturn<Reactive extends boolean> = Readonly<
Record<
string,
Reactive extends true ? boolean : ComputedRef<boolean>
> & MagicKeysInternal
>;
Generated by Syntax Scribe