UNPKG

reakeys

Version:
1 lines 8.49 kB
{"version":3,"file":"index.umd.cjs","sources":["../src/utils.ts","../src/useHotkeys.ts"],"sourcesContent":["type ModifierKey = '⌘' | 'CTRL';\n\nexport function isMac(): boolean {\n try {\n return navigator.platform.toUpperCase().indexOf('MAC') >= 0;\n } catch {\n return false;\n }\n}\n\nfunction getModifierKey(): ModifierKey {\n return isMac() ? '⌘' : 'CTRL';\n}\n\nexport const MODIFIER_KEY = getModifierKey();\n\nexport function getHotkeyText(hotkey: string) {\n return hotkey.toLowerCase().replace('modifier', getModifierKey()).replace('mod', getModifierKey()).replace('shift', '⇧');\n}\n","import { RefObject, useEffect, useLayoutEffect, useState } from 'react';\nimport type { Callback, HandlerInterface, Key } from 'ctrl-keys';\nimport { keys } from 'ctrl-keys';\nimport { isMac } from './utils';\n\ntype Keys = [Key] | [Key, Key] | [Key, Key, Key] | [Key, Key, Key, Key];\n\nexport interface HotkeyShortcuts {\n name: string;\n keys: string | string[];\n ref?: RefObject<HTMLElement | null>;\n disabled?: boolean;\n callback: Callback;\n action?: 'keypress' | 'keydown' | 'keyup';\n description?: string;\n category?: string;\n hidden?: boolean;\n}\n\nlet isGlobalListenersBinded = false;\n\nconst keypressGlobalHandler = keys();\nconst keyupGlobalHandler = keys();\nconst keydownGlobalHandler = keys();\n\n/**\n * Map of specific elements handlers\n */\nconst handlers = new Map<HTMLElement, HandlerInterface>();\nlet hotkeys: HotkeyShortcuts[] = [];\n\nconst extractKeys = (keys: string | string[]): Keys => {\n const normalizedKeys = Array.isArray(keys) ? keys.map((key) => key.toLowerCase()) : [keys.toLowerCase()];\n const modKey = isMac() ? 'meta' : 'ctrl';\n\n return normalizedKeys.map((key) => key.replace('modifier', modKey).replace('mod', modKey)) as Keys;\n};\n\nconst focusInputWrapper = (callback: Callback) => (event: any) => {\n const target = event.target;\n\n const isInput = target.tagName === 'INPUT' && !['checkbox', 'radio', 'range', 'button', 'file', 'reset', 'submit', 'color'].includes(target.type);\n if (target.isContentEditable || ((isInput || target.tagName === 'TEXTAREA' || target.tagName === 'SELECT') && !target.readOnly)) {\n return;\n }\n\n return callback(event);\n};\n\nconst registerGlobalShortcut = (shortcut: HotkeyShortcuts) => {\n if (!shortcut.action || shortcut.action === 'keypress') {\n keypressGlobalHandler.add(...extractKeys(shortcut.keys), shortcut.callback);\n }\n if (shortcut.action === 'keyup') {\n keyupGlobalHandler.add(...extractKeys(shortcut.keys), shortcut.callback);\n }\n if (shortcut.action === 'keydown') {\n keydownGlobalHandler.add(...extractKeys(shortcut.keys), shortcut.callback);\n }\n};\n\nconst removeGlobalShortcut = (shortcut: HotkeyShortcuts) => {\n if (!shortcut.action || shortcut.action === 'keypress') {\n keypressGlobalHandler.remove(...extractKeys(shortcut.keys), shortcut.callback);\n }\n if (shortcut.action === 'keyup') {\n keyupGlobalHandler.remove(...extractKeys(shortcut.keys), shortcut.callback);\n }\n if (shortcut.action === 'keydown') {\n keydownGlobalHandler.remove(...extractKeys(shortcut.keys), shortcut.callback);\n }\n};\n\nconst registerElementShortcut = (shortcut: HotkeyShortcuts) => {\n const handler = keys();\n\n handler.add(...extractKeys(shortcut.keys), shortcut.callback);\n\n shortcut.ref?.current?.addEventListener(shortcut.action ?? 'keypress', handler.handle);\n\n handlers.set(shortcut.ref?.current as HTMLElement, handler);\n};\n\nconst removeElementShortcut = (shortcut: HotkeyShortcuts) => {\n if (shortcut.ref?.current && !shortcut.disabled) {\n const handler = handlers.get(shortcut.ref?.current) as HandlerInterface;\n\n handler?.remove(...extractKeys(shortcut.keys), shortcut.callback);\n shortcut.ref?.current?.removeEventListener(shortcut.action ?? 'keypress', handler.handle);\n }\n};\n\nexport const useHotkeys = (shortcuts: HotkeyShortcuts[] = []) => {\n const [registered, setRegistered] = useState<HotkeyShortcuts[]>([]);\n /**\n * Register global listeners for \"keypress\", \"keyup\" and \"keydown\" events\n */\n useLayoutEffect(() => {\n if (!isGlobalListenersBinded && window !== undefined) {\n window.addEventListener('keypress', keypressGlobalHandler.handle);\n window.addEventListener('keyup', keyupGlobalHandler.handle);\n window.addEventListener('keydown', keydownGlobalHandler.handle);\n\n isGlobalListenersBinded = true;\n }\n }, []);\n\n /**\n * Register shortcuts\n */\n useLayoutEffect(() => {\n shortcuts.map((shortcut) => {\n if (shortcut.disabled) {\n return;\n }\n\n // Wrap callback in input focus wrapper to avoid trigger shortcut for input\n shortcut.callback = focusInputWrapper(shortcut.callback);\n\n if (shortcut.ref?.current) {\n registerElementShortcut(shortcut);\n hotkeys = [...hotkeys, shortcut];\n } else if (!shortcut.ref) {\n registerGlobalShortcut(shortcut);\n hotkeys = [...hotkeys, shortcut];\n }\n });\n\n // Remove all shortcuts on destroy\n return () => {\n shortcuts.map((shortcut) => {\n removeElementShortcut(shortcut);\n removeGlobalShortcut(shortcut);\n hotkeys = hotkeys.filter((hotkey) => shortcut !== hotkey);\n });\n };\n }, [shortcuts]);\n\n useEffect(() => {\n setRegistered(hotkeys);\n }, []);\n\n return registered;\n};\n"],"names":["keys","useState","useLayoutEffect","useEffect"],"mappings":"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"}