139 lines
4.0 KiB
TypeScript
139 lines
4.0 KiB
TypeScript
import { onBeforeUnmount, onMounted } from "@nuxtjs/composition-api"
|
|
import { HoppAction, invokeAction } from "./actions"
|
|
import { isAppleDevice } from "./platformutils"
|
|
|
|
/**
|
|
* This variable keeps track whether keybindings are being accepted
|
|
* true -> Keybindings are checked
|
|
* false -> Key presses are ignored (Keybindings are not checked)
|
|
*/
|
|
let keybindingsEnabled = true
|
|
|
|
/**
|
|
* Alt is also regarded as macOS OPTION (⌥) key
|
|
* Ctrl is also regarded as macOS COMMAND (⌘) key (NOTE: this differs from HTML Keyboard spec where COMMAND is Meta key!)
|
|
*/
|
|
type ModifierKeys = "ctrl" | "alt"
|
|
|
|
/* eslint-disable prettier/prettier */
|
|
type Key = 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k'
|
|
| 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x'
|
|
| 'y' | 'z' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
|
|
| "up" | "down" | "left" | "right"
|
|
/* eslint-enable */
|
|
|
|
type ShortcutKey = `${ModifierKeys}-${Key}`
|
|
|
|
export const bindings: {
|
|
// eslint-disable-next-line no-unused-vars
|
|
[_ in ShortcutKey]?: HoppAction
|
|
} = {
|
|
"ctrl-g": "request.send-cancel",
|
|
"ctrl-i": "request.reset",
|
|
"ctrl-k": "request.copy-link",
|
|
"ctrl-s": "request.save",
|
|
"alt-up": "request.method.next",
|
|
"alt-down": "request.method.prev",
|
|
"alt-g": "request.method.get",
|
|
"alt-h": "request.method.head",
|
|
"alt-p": "request.method.post",
|
|
"alt-u": "request.method.put",
|
|
"alt-x": "request.method.delete",
|
|
}
|
|
|
|
/**
|
|
* A composable that hooks to the caller component's
|
|
* lifecycle and hooks to the keyboard events to fire
|
|
* the appropriate actions based on keybindings
|
|
*/
|
|
export function hookKeybindingsListener() {
|
|
onMounted(() => {
|
|
document.addEventListener("keydown", handleKeyDown)
|
|
})
|
|
|
|
onBeforeUnmount(() => {
|
|
document.removeEventListener("keydown", handleKeyDown)
|
|
})
|
|
}
|
|
|
|
function handleKeyDown(ev: KeyboardEvent) {
|
|
// Do not check keybinds if the mode is disabled
|
|
if (!keybindingsEnabled) return
|
|
|
|
const binding = generateKeybindingString(ev)
|
|
if (!binding) return
|
|
|
|
const boundAction = bindings[binding]
|
|
if (!boundAction) return
|
|
|
|
ev.preventDefault()
|
|
invokeAction(boundAction)
|
|
}
|
|
|
|
function generateKeybindingString(ev: KeyboardEvent): ShortcutKey | null {
|
|
// All our keybinds need to have one modifier pressed atleast
|
|
const modifierKey = getActiveModifier(ev)
|
|
if (!modifierKey) return null
|
|
|
|
const key = getPressedKey(ev)
|
|
if (!key) return null
|
|
|
|
return `${modifierKey}-${key}` as ShortcutKey
|
|
}
|
|
|
|
function getPressedKey(ev: KeyboardEvent): Key | null {
|
|
const val = ev.key.toLowerCase()
|
|
|
|
// Check arrow keys
|
|
if (val === "arrowup") return "up"
|
|
else if (val === "arrowdown") return "down"
|
|
else if (val === "arrowleft") return "left"
|
|
else if (val === "arrowright") return "right"
|
|
|
|
// Check letter keys
|
|
if (val.length === 1 && val.toUpperCase() !== val.toLowerCase())
|
|
return val as Key
|
|
|
|
// Check if number keys
|
|
if (val.length === 1 && !isNaN(val as any)) return val as Key
|
|
|
|
// If no other cases match, this is not a valid key
|
|
return null
|
|
}
|
|
|
|
function getActiveModifier(ev: KeyboardEvent): ModifierKeys | null {
|
|
// Just ignore everything if Shift is pressed (for now)
|
|
if (ev.shiftKey) return null
|
|
|
|
// We only allow one modifier key to be pressed (for now)
|
|
// Control key (+ Command) gets priority and if Alt is also pressed, it is ignored
|
|
if (isAppleDevice() && ev.metaKey) return "ctrl"
|
|
else if (!isAppleDevice() && ev.ctrlKey) return "ctrl"
|
|
|
|
// Test for Alt key
|
|
if (ev.altKey) return "alt"
|
|
|
|
return null
|
|
}
|
|
|
|
/**
|
|
* This composable allows for the UI component to be disabled if the component in question is mounted
|
|
*/
|
|
export function useKeybindingDisabler() {
|
|
// TODO: Move to a lock based system that keeps the bindings disabled until all locks are lifted
|
|
const disableKeybindings = () => {
|
|
keybindingsEnabled = false
|
|
console.log("Keybinds disabled by a component")
|
|
}
|
|
|
|
const enableKeybindings = () => {
|
|
keybindingsEnabled = true
|
|
console.log("Keybinds enabled by a component")
|
|
}
|
|
|
|
return {
|
|
disableKeybindings,
|
|
enableKeybindings,
|
|
}
|
|
}
|