diff options
| author | kj_sh604 | 2026-03-15 16:19:35 -0400 |
|---|---|---|
| committer | kj_sh604 | 2026-03-15 16:19:35 -0400 |
| commit | 6ec259a0e71174651bae95d4628138bf6fd68742 (patch) | |
| tree | 5e33c6a5ec091ecabfcb257fdc7b6a88ed8754ac /packages/excalidraw/keys.ts | |
| parent | 16c8578b15c727f22921f8a80a56ee4d4e7f2272 (diff) | |
refactor: packages/
Diffstat (limited to 'packages/excalidraw/keys.ts')
| -rw-r--r-- | packages/excalidraw/keys.ts | 150 |
1 files changed, 150 insertions, 0 deletions
diff --git a/packages/excalidraw/keys.ts b/packages/excalidraw/keys.ts new file mode 100644 index 0000000..2088f89 --- /dev/null +++ b/packages/excalidraw/keys.ts @@ -0,0 +1,150 @@ +import { isDarwin } from "./constants"; +import type { ValueOf } from "./utility-types"; + +export const CODES = { + EQUAL: "Equal", + MINUS: "Minus", + NUM_ADD: "NumpadAdd", + NUM_SUBTRACT: "NumpadSubtract", + NUM_ZERO: "Numpad0", + BRACKET_RIGHT: "BracketRight", + BRACKET_LEFT: "BracketLeft", + ONE: "Digit1", + TWO: "Digit2", + THREE: "Digit3", + NINE: "Digit9", + QUOTE: "Quote", + ZERO: "Digit0", + SLASH: "Slash", + C: "KeyC", + D: "KeyD", + H: "KeyH", + V: "KeyV", + Z: "KeyZ", + Y: "KeyY", + R: "KeyR", + S: "KeyS", +} as const; + +export const KEYS = { + ARROW_DOWN: "ArrowDown", + ARROW_LEFT: "ArrowLeft", + ARROW_RIGHT: "ArrowRight", + ARROW_UP: "ArrowUp", + PAGE_UP: "PageUp", + PAGE_DOWN: "PageDown", + BACKSPACE: "Backspace", + ALT: "Alt", + CTRL_OR_CMD: isDarwin ? "metaKey" : "ctrlKey", + DELETE: "Delete", + ENTER: "Enter", + ESCAPE: "Escape", + QUESTION_MARK: "?", + SPACE: " ", + TAB: "Tab", + CHEVRON_LEFT: "<", + CHEVRON_RIGHT: ">", + PERIOD: ".", + COMMA: ",", + SUBTRACT: "-", + SLASH: "/", + + A: "a", + C: "c", + D: "d", + E: "e", + F: "f", + G: "g", + H: "h", + I: "i", + L: "l", + O: "o", + P: "p", + Q: "q", + R: "r", + S: "s", + T: "t", + V: "v", + X: "x", + Y: "y", + Z: "z", + K: "k", + W: "w", + + 0: "0", + 1: "1", + 2: "2", + 3: "3", + 4: "4", + 5: "5", + 6: "6", + 7: "7", + 8: "8", + 9: "9", +} as const; + +export type Key = keyof typeof KEYS; + +// defines key code mapping for matching codes as fallback to respective keys on non-latin keyboard layouts +export const KeyCodeMap = new Map<ValueOf<typeof KEYS>, ValueOf<typeof CODES>>([ + [KEYS.Z, CODES.Z], + [KEYS.Y, CODES.Y], +]); + +export const isLatinChar = (key: string) => /^[a-z]$/.test(key.toLowerCase()); + +/** + * Used to match key events for any keyboard layout, especially on Windows and Linux, + * where non-latin character with modified (CMD) is not substituted with latin-based alternative. + * + * Uses `event.key` when it's latin, otherwise fallbacks to `event.code` (if mapping exists). + * + * Example of pressing "z" on different layouts, with the chosen key or code highlighted in []: + * + * Layout | Code | Key | Comment + * --------------------- | ----- | --- | ------- + * U.S. | KeyZ | [z] | + * Czech | KeyY | [z] | + * Turkish | KeyN | [z] | + * French | KeyW | [z] | + * Macedonian | [KeyZ] | з | z with cmd; з is Cyrillic equivalent of z + * Russian | [KeyZ] | я | z with cmd + * Serbian | [KeyZ] | ѕ | z with cmd + * Greek | [KeyZ] | ζ | z with cmd; also ζ is Greek equivalent of z + * Hebrew | [KeyZ] | ז | z with cmd; also ז is Hebrew equivalent of z + * Pinyin - Simplified | KeyZ | [z] | due to IME + * Cangije - Traditional | [KeyZ] | 重 | z with cmd + * Japanese | [KeyZ] | つ | z with cmd + * 2-Set Korean | [KeyZ] | ㅋ | z with cmd + * + * More details in https://github.com/excalidraw/excalidraw/pull/5944 + */ +export const matchKey = ( + event: KeyboardEvent | React.KeyboardEvent<Element>, + key: ValueOf<typeof KEYS>, +): boolean => { + // for latin layouts use key + if (key === event.key.toLowerCase()) { + return true; + } + + // non-latin layouts fallback to code + const code = KeyCodeMap.get(key); + return Boolean(code && !isLatinChar(event.key) && event.code === code); +}; + +export const isArrowKey = (key: string) => + key === KEYS.ARROW_LEFT || + key === KEYS.ARROW_RIGHT || + key === KEYS.ARROW_DOWN || + key === KEYS.ARROW_UP; + +export const shouldResizeFromCenter = (event: MouseEvent | KeyboardEvent) => + event.altKey; + +export const shouldMaintainAspectRatio = (event: MouseEvent | KeyboardEvent) => + event.shiftKey; + +export const shouldRotateWithDiscreteAngle = ( + event: MouseEvent | KeyboardEvent | React.PointerEvent<HTMLCanvasElement>, +) => event.shiftKey; |
