blob: 063bd409ca4e1af8596a532538c651519250eba4 (
plain) (
tree)
|
|
import { closeModal, modals } from 'svelte-modals';
import { get } from 'svelte/store';
type LowercaseLetter =
| 'a'
| 'b'
| 'c'
| 'd'
| 'e'
| 'f'
| 'g'
| 'h'
| 'i'
| 'j'
| 'l'
| 'm'
| 'n'
| 'o'
| 'p'
| 'q'
| 'r'
| 's'
| 't'
| 'u'
| 'v'
| 'w'
| 'x'
| 'y'
| 'z';
type UppercaseLetter = Uppercase<LowercaseLetter>;
type Letter = LowercaseLetter | UppercaseLetter;
type Special = '?' | 'Enter' | 'Escape' | 'Delete';
const modeSwitches = ['n', 'g', 'i'] as const;
type ModeSwitch = (typeof modeSwitches)[number];
function isModeSwitch(s: string): s is ModeSwitch {
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
return modeSwitches.indexOf(s as any) !== -1;
}
type Key = Letter | Special;
type KeyCombo = `${ModeSwitch}${Letter}`;
export type Shortcut = Key | KeyCombo;
type EventAction = (event: KeyboardEvent) => void;
type FocusAction = HTMLInputElement;
type ClickAction = HTMLElement;
type Action = EventAction | FocusAction | ClickAction;
const handlers = new Map<string, Action>();
let mode: ModeSwitch | undefined;
export function handleShortcuts(event: KeyboardEvent) {
if (isInputElement(event.target)) {
if (event.key === 'Escape') {
event.target.blur();
event.preventDefault();
event.stopImmediatePropagation();
}
return;
}
if (event.ctrlKey) {
return;
}
if (event.key === 'Escape') {
if (get(modals).length > 0) {
closeModal();
event.preventDefault();
event.stopImmediatePropagation();
return;
}
}
if (isModeSwitch(event.key) && mode === undefined) {
mode = event.key;
event.preventDefault();
return;
}
const handler = handlers.get(mode === undefined ? event.key : `${mode}${event.key}`);
if (!handler || get(modals).length > 0) {
mode = undefined;
return;
}
if (handler instanceof HTMLInputElement) {
handler.focus();
} else if (handler instanceof HTMLElement) {
handler.click();
} else {
handler(event);
}
mode = undefined;
event.preventDefault();
}
export function accelerator(node: HTMLElement | HTMLInputElement, sc: Shortcut) {
handlers.set(sc, node);
return {
destroy() {
handlers.delete(sc);
}
};
}
export function binds(node: Document, scs: [string, EventAction][]) {
const handlers = new Map<string, EventAction>();
for (const [k, a] of scs) {
handlers.set(k, a);
}
function keydown(event: KeyboardEvent) {
if (isInputElement(event.target)) return;
const handler = handlers.get(event.key);
if (!handler) return;
handler(event);
event.preventDefault();
}
node.addEventListener('keydown', keydown);
return {
destroy() {
node.removeEventListener('keydown', keydown);
}
};
}
export function addShortcut(sc: Shortcut, action: EventAction) {
handlers.set(sc, action);
}
function isInputElement(target: EventTarget | null): target is HTMLElement {
return (
target instanceof HTMLElement &&
(target instanceof HTMLInputElement ||
target instanceof HTMLSelectElement ||
target instanceof HTMLTextAreaElement ||
target.isContentEditable)
);
}
|