fix: avoid shortcut conflicts in CodeMirror editors (#5224)
Prevents `alt+up` and `alt+down` from triggering global keybindings when focus is in CodeMirror editors or other typable elements.
This commit is contained in:
parent
0b605fe9cb
commit
bf3e135679
3 changed files with 97 additions and 1 deletions
|
|
@ -1,7 +1,7 @@
|
|||
import { onBeforeUnmount, onMounted } from "vue"
|
||||
import { HoppActionWithOptionalArgs, invokeAction } from "./actions"
|
||||
import { isAppleDevice } from "./platformutils"
|
||||
import { isDOMElement, isTypableElement } from "./utils/dom"
|
||||
import { isCodeMirrorEditor, isDOMElement, isTypableElement } from "./utils/dom"
|
||||
import { getKernelMode } from "@hoppscotch/kernel"
|
||||
import { listen } from "@tauri-apps/api/event"
|
||||
|
||||
|
|
@ -193,6 +193,15 @@ function generateKeybindingString(ev: KeyboardEvent): ShortcutKey | null {
|
|||
return null
|
||||
}
|
||||
|
||||
// Restrict alt+up and alt+down when the target is a codemirror editor
|
||||
if (
|
||||
modifierKey === "alt" &&
|
||||
(key === "up" || key === "down") &&
|
||||
isCodeMirrorEditor(target)
|
||||
) {
|
||||
return null
|
||||
}
|
||||
|
||||
return `${modifierKey}-${key}`
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,77 @@
|
|||
import { describe, expect, test } from "vitest"
|
||||
import { isDOMElement, isTypableElement, isCodeMirrorEditor } from "../dom"
|
||||
|
||||
describe("isDOMElement", () => {
|
||||
test("returns true for valid HTMLElement", () => {
|
||||
const div = document.createElement("div")
|
||||
expect(isDOMElement(div)).toBe(true)
|
||||
})
|
||||
|
||||
test("returns false for non-HTMLElement inputs", () => {
|
||||
expect(isDOMElement(null)).toBe(false)
|
||||
expect(isDOMElement(undefined)).toBe(false)
|
||||
expect(isDOMElement("div")).toBe(false)
|
||||
expect(isDOMElement(123)).toBe(false)
|
||||
expect(isDOMElement({})).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe("isTypableElement", () => {
|
||||
test("returns true for enabled <input>", () => {
|
||||
const input = document.createElement("input")
|
||||
expect(isTypableElement(input)).toBe(true)
|
||||
})
|
||||
|
||||
test("returns false for disabled <input>", () => {
|
||||
const input = document.createElement("input")
|
||||
input.disabled = true
|
||||
expect(isTypableElement(input)).toBe(false)
|
||||
})
|
||||
|
||||
test("returns true for enabled <textarea>", () => {
|
||||
const textarea = document.createElement("textarea")
|
||||
expect(isTypableElement(textarea)).toBe(true)
|
||||
})
|
||||
|
||||
test("returns false for disabled <textarea>", () => {
|
||||
const textarea = document.createElement("textarea")
|
||||
textarea.disabled = true
|
||||
expect(isTypableElement(textarea)).toBe(false)
|
||||
})
|
||||
|
||||
test("returns true for contentEditable element", () => {
|
||||
const div = document.createElement("div")
|
||||
div.isContentEditable = true
|
||||
expect(isTypableElement(div)).toBe(true)
|
||||
})
|
||||
|
||||
test("returns false for regular non-typable div", () => {
|
||||
const div = document.createElement("div")
|
||||
expect(isTypableElement(div)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe("isCodeMirrorEditor", () => {
|
||||
test("returns true if element is inside .cm-editor", () => {
|
||||
const wrapper = document.createElement("div")
|
||||
wrapper.classList.add("cm-editor")
|
||||
|
||||
const child = document.createElement("div")
|
||||
wrapper.appendChild(child)
|
||||
document.body.appendChild(wrapper)
|
||||
|
||||
expect(isCodeMirrorEditor(child)).toBe(true)
|
||||
})
|
||||
|
||||
test("returns false if element is not inside .cm-editor", () => {
|
||||
const div = document.createElement("div")
|
||||
document.body.appendChild(div)
|
||||
|
||||
expect(isCodeMirrorEditor(div)).toBe(false)
|
||||
})
|
||||
|
||||
test("returns false if input is not an HTMLElement", () => {
|
||||
expect(isCodeMirrorEditor(null)).toBe(false)
|
||||
expect(isCodeMirrorEditor("not-an-element")).toBe(false)
|
||||
})
|
||||
})
|
||||
|
|
@ -17,3 +17,13 @@ export function isTypableElement(el: HTMLElement): boolean {
|
|||
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an element is a CodeMirror editor.
|
||||
* @param el The element to check. If this is not an HTMLElement, the function will return false.
|
||||
* @returns True if the element is a CodeMirror editor, false otherwise.
|
||||
*/
|
||||
export function isCodeMirrorEditor(el: EventTarget | null): boolean {
|
||||
if (!(el instanceof HTMLElement)) return false
|
||||
return el.closest(".cm-editor") !== null
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue