123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171 |
- import { onBeforeUnmount, onMounted } from "@nuxtjs/composition-api"
- import { HoppAction, invokeAction } from "./actions"
- import { isAppleDevice } from "./platformutils"
- import { isDOMElement, isTypableElement } from "./utils/dom"
- /**
- * 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" | "ctrl-shift" | "alt-shift"
- /* eslint-disable prettier/prettier */
- // prettier-ignore
- 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 ModifierBasedShortcutKey = `${ModifierKeys}-${Key}`
- // Singular keybindings (these will be disabled when an input-ish area has been focused)
- type SingleCharacterShortcutKey = `${Key}`
- type ShortcutKey = ModifierBasedShortcutKey | SingleCharacterShortcutKey
- export const bindings: {
- // eslint-disable-next-line no-unused-vars
- [_ in ShortcutKey]?: HoppAction
- } = {
- "ctrl-g": "request.send-cancel",
- "ctrl-i": "request.reset",
- "ctrl-u": "request.copy-link",
- "ctrl-s": "request.save",
- "ctrl-shift-s": "request.save-as",
- "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",
- "ctrl-k": "flyouts.keybinds.toggle",
- "/": "modals.search.toggle",
- "?": "modals.support.toggle",
- "ctrl-m": "modals.share.toggle",
- "alt-r": "navigation.jump.rest",
- "alt-q": "navigation.jump.graphql",
- "alt-w": "navigation.jump.realtime",
- "alt-d": "navigation.jump.documentation",
- "alt-s": "navigation.jump.settings",
- "ctrl-left": "navigation.jump.back",
- "ctrl-right": "navigation.jump.forward",
- }
- /**
- * 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)
- const target = ev.target
- if (!modifierKey && !(isDOMElement(target) && isTypableElement(target))) {
- // Check if we are having singulars instead
- const key = getPressedKey(ev)
- if (!key) return null
- else return `${key}` as ShortcutKey
- }
- 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
- // Check if question mark
- if (val === "?") return "?"
- // Check if question mark
- if (val === "/") return "/"
- // If no other cases match, this is not a valid key
- return null
- }
- function getActiveModifier(ev: KeyboardEvent): ModifierKeys | null {
- const isShiftKey = ev.shiftKey
- // 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 isShiftKey ? "ctrl-shift" : "ctrl"
- else if (!isAppleDevice() && ev.ctrlKey)
- return isShiftKey ? "ctrl-shift" : "ctrl"
- // Test for Alt key
- if (ev.altKey) return isShiftKey ? "alt-shift" : "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
- }
- const enableKeybindings = () => {
- keybindingsEnabled = true
- }
- return {
- disableKeybindings,
- enableKeybindings,
- }
- }
|