Match Shift+letter shortcuts (Shift+I, Shift+U) (#399)
comboOf() let a shifted letter encode Shift in its case, so Shift+I produced "I" and never matched the "shift+i" / "shift+u" bindings for mark as read / unread. Shifted letters now yield "shift+<letter>"; symbols such as "#" and "!" still carry Shift in the character. Fixes #398 Co-authored-by: Joe Esteves <[email protected]>
This commit is contained in:
@@ -11,8 +11,8 @@ import { isTextEntry, keyboard } from "@/lib/input/keyboard";
|
|||||||
* swallows a keystroke in the first place.
|
* swallows a keystroke in the first place.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const pressFrom = (el: Element, key: string) => {
|
const pressFrom = (el: EventTarget, key: string, init?: KeyboardEventInit) => {
|
||||||
const e = new KeyboardEvent("keydown", { key, bubbles: true, cancelable: true });
|
const e = new KeyboardEvent("keydown", { key, bubbles: true, cancelable: true, ...init });
|
||||||
el.dispatchEvent(e);
|
el.dispatchEvent(e);
|
||||||
return e;
|
return e;
|
||||||
};
|
};
|
||||||
@@ -92,3 +92,22 @@ describe("shortcuts with a checkbox focused", () => {
|
|||||||
expect(handler).not.toHaveBeenCalled();
|
expect(handler).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("shifted letter shortcuts", () => {
|
||||||
|
it("matches Shift+I and Shift+U bindings", () => {
|
||||||
|
const read = vi.fn();
|
||||||
|
const unread = vi.fn();
|
||||||
|
pop = keyboard.pushScope("test", [
|
||||||
|
{ keys: "shift+i", description: "Mark as read", group: "Actions", handler: read },
|
||||||
|
{ keys: "shift+u", description: "Mark as unread", group: "Actions", handler: unread },
|
||||||
|
]);
|
||||||
|
|
||||||
|
const readEvent = pressFrom(window, "I", { shiftKey: true });
|
||||||
|
const unreadEvent = pressFrom(window, "U", { shiftKey: true });
|
||||||
|
|
||||||
|
expect(read).toHaveBeenCalledOnce();
|
||||||
|
expect(unread).toHaveBeenCalledOnce();
|
||||||
|
expect(readEvent.defaultPrevented).toBe(true);
|
||||||
|
expect(unreadEvent.defaultPrevented).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ describe("a keydown with no key", () => {
|
|||||||
|
|
||||||
it("leaves real keys alone", () => {
|
it("leaves real keys alone", () => {
|
||||||
expect(comboOf(new KeyboardEvent("keydown", { key: "e" }))).toBe("e");
|
expect(comboOf(new KeyboardEvent("keydown", { key: "e" }))).toBe("e");
|
||||||
expect(comboOf(new KeyboardEvent("keydown", { key: "E", shiftKey: true }))).toBe("E");
|
expect(comboOf(new KeyboardEvent("keydown", { key: "E", shiftKey: true }))).toBe("shift+e");
|
||||||
expect(comboOf(new KeyboardEvent("keydown", { key: "Enter", ctrlKey: true }))).toMatch(/enter$/);
|
expect(comboOf(new KeyboardEvent("keydown", { key: "Enter", ctrlKey: true }))).toMatch(/enter$/);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -145,13 +145,14 @@ export function comboOf(e: KeyboardEvent): string | null {
|
|||||||
const mod = isMac ? e.metaKey : e.ctrlKey;
|
const mod = isMac ? e.metaKey : e.ctrlKey;
|
||||||
if (mod) parts.push("mod");
|
if (mod) parts.push("mod");
|
||||||
if (e.altKey) parts.push("alt");
|
if (e.altKey) parts.push("alt");
|
||||||
if (e.shiftKey && key.length > 1) parts.push("shift");
|
const shiftedLetter = e.shiftKey && /^[a-z]$/i.test(key);
|
||||||
|
if (e.shiftKey && (key.length > 1 || shiftedLetter)) parts.push("shift");
|
||||||
let k = key;
|
let k = key;
|
||||||
if (k === " ") k = "space";
|
if (k === " ") k = "space";
|
||||||
else if (k === "Escape") k = "esc";
|
else if (k === "Escape") k = "esc";
|
||||||
else if (k.length === 1) {
|
else if (k.length === 1) {
|
||||||
// Single chars: shift is encoded by the character itself (e.g. "#", "!").
|
// Symbols encode Shift in the character itself (e.g. "#", "!").
|
||||||
k = k.length === 1 && !e.shiftKey ? k.toLowerCase() : k;
|
k = shiftedLetter || !e.shiftKey ? k.toLowerCase() : k;
|
||||||
} else k = k.toLowerCase();
|
} else k = k.toLowerCase();
|
||||||
parts.push(k);
|
parts.push(k);
|
||||||
return parts.join("+");
|
return parts.join("+");
|
||||||
|
|||||||
Reference in New Issue
Block a user