{ "version": 3, "sources": ["../../../../src/lib/ui/hooks/useKeyboardShortcuts.ts"], "sourcesContent": ["import { preventDefault, useEditor, useValue } from '@tldraw/editor'\nimport hotkeys from 'hotkeys-js'\nimport { useEffect } from 'react'\nimport { useActions } from './useActions'\nimport { useReadonly } from './useReadonly'\nimport { useTools } from './useTools'\n\nconst SKIP_KBDS = [\n\t// we set these in useNativeClipboardEvents instead\n\t'copy',\n\t'cut',\n\t'paste',\n\t// There's also an upload asset action, so we don't want to set the kbd twice\n\t'asset',\n]\n\n/** @public */\nexport function useKeyboardShortcuts() {\n\tconst editor = useEditor()\n\n\tconst isReadonly = useReadonly()\n\tconst actions = useActions()\n\tconst tools = useTools()\n\tconst isFocused = useValue('is focused', () => editor.instanceState.isFocused, [editor])\n\n\tuseEffect(() => {\n\t\tif (!isFocused) return\n\n\t\tconst container = editor.getContainer()\n\n\t\thotkeys.setScope(editor.store.id)\n\n\t\tconst hot = (keys: string, callback: (event: KeyboardEvent) => void) => {\n\t\t\thotkeys(keys, { element: container, scope: editor.store.id }, callback)\n\t\t}\n\n\t\t// Add hotkeys for actions and tools.\n\t\t// Except those that in SKIP_KBDS!\n\t\tconst areShortcutsDisabled = () =>\n\t\t\teditor.isMenuOpen || editor.editingShapeId !== null || editor.crashingError\n\n\t\tfor (const action of Object.values(actions)) {\n\t\t\tif (!action.kbd) continue\n\t\t\tif (isReadonly && !action.readonlyOk) continue\n\t\t\tif (SKIP_KBDS.includes(action.id)) continue\n\n\t\t\thot(getHotkeysStringFromKbd(action.kbd), (event) => {\n\t\t\t\tif (areShortcutsDisabled()) return\n\t\t\t\tpreventDefault(event)\n\t\t\t\taction.onSelect('kbd')\n\t\t\t})\n\t\t}\n\n\t\tfor (const tool of Object.values(tools)) {\n\t\t\tif (!tool.kbd || (!tool.readonlyOk && editor.instanceState.isReadonly)) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif (SKIP_KBDS.includes(tool.id)) continue\n\n\t\t\thot(getHotkeysStringFromKbd(tool.kbd), (event) => {\n\t\t\t\tif (areShortcutsDisabled()) return\n\t\t\t\tpreventDefault(event)\n\t\t\t\ttool.onSelect('kbd')\n\t\t\t})\n\t\t}\n\n\t\treturn () => {\n\t\t\thotkeys.deleteScope(editor.store.id)\n\t\t}\n\t}, [actions, tools, isReadonly, editor, isFocused])\n}\n\nfunction getHotkeysStringFromKbd(kbd: string) {\n\treturn getKeys(kbd)\n\t\t.map((kbd) => {\n\t\t\tlet str = ''\n\t\t\tconst chars = kbd.split('')\n\t\t\tif (chars.length === 1) {\n\t\t\t\tstr = chars[0]\n\t\t\t} else {\n\t\t\t\tif (chars[0] === '!') {\n\t\t\t\t\tstr = `shift+${chars[1]}`\n\t\t\t\t} else if (chars[0] === '?') {\n\t\t\t\t\tif (chars.length === 3 && chars[1] === '!') {\n\t\t\t\t\t\tstr = `alt+shift+${chars[2]}`\n\t\t\t\t\t} else {\n\t\t\t\t\t\tstr = `alt+${chars[1]}`\n\t\t\t\t\t}\n\t\t\t\t} else if (chars[0] === '$') {\n\t\t\t\t\tif (chars[1] === '!') {\n\t\t\t\t\t\tstr = `cmd+shift+${chars[2]},ctrl+shift+${chars[2]}`\n\t\t\t\t\t} else if (chars[1] === '?') {\n\t\t\t\t\t\tstr = `cmd+\u2325+${chars[2]},ctrl+alt+${chars[2]}`\n\t\t\t\t\t} else {\n\t\t\t\t\t\tstr = `cmd+${chars[1]},ctrl+${chars[1]}`\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tstr = kbd\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn str\n\t\t})\n\t\t.join(',')\n}\n\n// Logic to split kbd string from hotkeys-js util.\nfunction getKeys(key: string) {\n\tif (typeof key !== 'string') key = ''\n\tkey = key.replace(/\\s/g, '')\n\tconst keys = key.split(',')\n\tlet index = keys.lastIndexOf('')\n\n\tfor (; index >= 0; ) {\n\t\tkeys[index - 1] += ','\n\t\tkeys.splice(index, 1)\n\t\tindex = keys.lastIndexOf('')\n\t}\n\n\treturn keys\n}\n"], "mappings": "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", "names": ["kbd"] }