UNPKG

tldraw

Version:

A tiny little drawing editor.

162 lines (161 loc) • 4.97 kB
import { isAccelKey, preventDefault, useEditor, useValue } from "@tldraw/editor"; import hotkeys from "hotkeys-js"; import { useEffect } from "react"; import { useActions } from "../context/actions.mjs"; import { useReadonly } from "./useReadonly.mjs"; import { useTools } from "./useTools.mjs"; const SKIP_KBDS = [ // we set these in useNativeClipboardEvents instead "copy", "cut", "paste", // There's also an upload asset action, so we don't want to set the kbd twice "asset" ]; function useKeyboardShortcuts() { const editor = useEditor(); const isReadonlyMode = useReadonly(); const actions = useActions(); const tools = useTools(); const isFocused = useValue("is focused", () => editor.getInstanceState().isFocused, [editor]); useEffect(() => { if (!isFocused) return; const disposables = new Array(); const hot = (keys, callback) => { hotkeys(keys, { element: document.body }, callback); disposables.push(() => { hotkeys.unbind(keys, callback); }); }; const hotUp = (keys, callback) => { hotkeys(keys, { element: document.body, keyup: true, keydown: false }, callback); disposables.push(() => { hotkeys.unbind(keys, callback); }); }; for (const action of Object.values(actions)) { if (!action.kbd) continue; if (isReadonlyMode && !action.readonlyOk) continue; if (SKIP_KBDS.includes(action.id)) continue; hot(getHotkeysStringFromKbd(action.kbd), (event) => { if (areShortcutsDisabled(editor)) return; preventDefault(event); action.onSelect("kbd"); }); } for (const tool of Object.values(tools)) { if (!tool.kbd || !tool.readonlyOk && editor.getIsReadonly()) { continue; } if (SKIP_KBDS.includes(tool.id)) continue; hot(getHotkeysStringFromKbd(tool.kbd), (event) => { if (areShortcutsDisabled(editor)) return; preventDefault(event); tool.onSelect("kbd"); }); } hot(",", (e) => { if (areShortcutsDisabled(editor)) return; if (editor.inputs.keys.has("Comma")) return; preventDefault(e); editor.focus(); editor.inputs.keys.add("Comma"); const { x, y, z } = editor.inputs.currentPagePoint; const screenpoints = editor.pageToScreen({ x, y }); const info = { type: "pointer", name: "pointer_down", point: { x: screenpoints.x, y: screenpoints.y, z }, shiftKey: e.shiftKey, altKey: e.altKey, ctrlKey: e.metaKey || e.ctrlKey, metaKey: e.metaKey, accelKey: isAccelKey(e), pointerId: 0, button: 0, isPen: editor.getInstanceState().isPenMode, target: "canvas" }; editor.dispatch(info); }); hotUp(",", (e) => { if (areShortcutsDisabled(editor)) return; if (!editor.inputs.keys.has("Comma")) return; editor.inputs.keys.delete("Comma"); const { x, y, z } = editor.inputs.currentScreenPoint; const info = { type: "pointer", name: "pointer_up", point: { x, y, z }, shiftKey: e.shiftKey, altKey: e.altKey, ctrlKey: e.metaKey || e.ctrlKey, metaKey: e.metaKey, accelKey: isAccelKey(e), pointerId: 0, button: 0, isPen: editor.getInstanceState().isPenMode, target: "canvas" }; editor.dispatch(info); }); return () => { disposables.forEach((d) => d()); }; }, [actions, tools, isReadonlyMode, editor, isFocused]); } function getHotkeysStringFromKbd(kbd) { return getKeys(kbd).map((kbd2) => { let str = ""; const chars = kbd2.split(""); if (chars.length === 1) { str = chars[0]; } else { if (chars[0] === "!") { str = `shift+${chars[1]}`; } else if (chars[0] === "?") { if (chars.length === 3 && chars[1] === "!") { str = `alt+shift+${chars[2]}`; } else { str = `alt+${chars[1]}`; } } else if (chars[0] === "$") { if (chars[1] === "!") { str = `cmd+shift+${chars[2]},ctrl+shift+${chars[2]}`; } else if (chars[1] === "?") { str = `cmd+\u2325+${chars[2]},ctrl+alt+${chars[2]}`; } else { str = `cmd+${chars[1]},ctrl+${chars[1]}`; } } else { str = kbd2; } } return str; }).join(","); } function getKeys(key) { if (typeof key !== "string") key = ""; key = key.replace(/\s/g, ""); const keys = key.split(","); let index = keys.lastIndexOf(""); for (; index >= 0; ) { keys[index - 1] += ","; keys.splice(index, 1); index = keys.lastIndexOf(""); } return keys; } function areShortcutsDisabled(editor) { return editor.menus.hasAnyOpenMenus() || editor.getEditingShapeId() !== null || editor.getCrashingError(); } export { areShortcutsDisabled, useKeyboardShortcuts }; //# sourceMappingURL=useKeyboardShortcuts.mjs.map