UNPKG

@keybindy/react

Version:

Keybindy for React: Simple, scoped keyboard shortcuts that require little setup. designed to smoothly blend in with your React applications, allowing for robust keybinding functionality without the overhead.

254 lines (251 loc) 8.23 kB
import React from 'react'; import ShortcutManager from '@keybindy/core'; let sharedInstance = null; /** * React hook to manage keyboard shortcuts using a shared instance of `ShortcutManager`. * Automatically cleans up shortcuts registered by the component on unmount. * * @param {Object} config - Configuration object. * @param {boolean} [config.logs=false] - Whether to enable debug logs in the console. * * @returns {Object} Object containing shortcut management methods. * * @example * const { * register, * setScope, * getCheatSheet, * } = useKeybindy({ logs: true }); * * useEffect(() => { * register(['ctrl', 's'], () => save(), { * scope: 'editor', * data: { description: 'Save document' } * }); * setScope('editor'); * }, []); */ function useKeybindy({ logs = false, onShortcutFired, } = {}) { const managerRef = React.useRef(null); const registeredIds = React.useRef(new Set()); if (!sharedInstance) { sharedInstance = new ShortcutManager(onShortcutFired); } if (!managerRef.current) { managerRef.current = sharedInstance; managerRef.current.start(); } const log = (...args) => { if (logs) console.log('[Keybindy]', ...args); }; const warn = (...args) => { if (logs) console.warn('[Keybindy]', ...args); }; /** * Registers a new keyboard shortcut. * * @param {Keys[] | Keys[][]} keys - Key combination(s) to listen for. * @param {ShortcutHandler} handler - Callback function to invoke when shortcut is triggered. * @param {ShortcutOptions} [options] - Optional configuration, including scope and metadata. */ const register = React.useCallback((keys, handler, options) => { if (keys.length === 0) { warn('No keys provided to register'); return; } const id = options?.data?.id; if (id) registeredIds.current.add(id); log('Registered:', id ?? keys); managerRef.current?.register(keys, handler, options); }, []); /** * Unregisters a previously registered keyboard shortcut. * * @param {Keys[]} keys - Key combination to unregister. * @param {string} [scope] - Optional scope for more targeted unregistration. */ const unregister = React.useCallback((keys, scope) => { if (keys.length === 0) { warn('No keys provided to unregister'); return; } managerRef.current?.unregister(keys, scope); log('Unregistered:', keys); }, []); /** * Enables a previously disabled shortcut. * * @param {Keys[]} keys - Key combination to enable. * @param {string} [scope] - Optional scope to target a specific set. */ const enable = React.useCallback((keys, scope) => { if (keys.length === 0) { warn('No keys provided to enable'); return; } managerRef.current?.enable(keys, scope); log('Enabled:', keys); }, []); /** * Disables a shortcut so it no longer triggers its handler. * * @param {Keys[]} keys - Key combination to disable. * @param {string} [scope] - Optional scope to target a specific set. */ const disable = React.useCallback((keys, scope) => { if (keys.length === 0) { warn('No keys provided to disable'); return; } managerRef.current?.disable(keys, scope); log('Disabled:', keys); }, []); /** * Toggles a shortcut between enabled and disabled. * * @param {Keys[]} keys - Key combination to toggle. * @param {string} [scope] - Optional scope to target a specific set. */ const toggle = React.useCallback((keys, scope) => { if (keys.length === 0) { warn('No keys provided to toggle'); return; } managerRef.current?.toggle(keys, scope); log('Toggled:', keys); }, []); /** * Returns a list of shortcuts registered in a given scope. * * @param {string} [scope] - The scope to query. Defaults to the active scope. * @returns {Array} List of shortcut definitions. */ const getCheatSheet = React.useCallback((scope) => { return managerRef.current?.getCheatSheet(scope); }, []); /** * Returns the currently active scope. * @returns {string} The active scope. */ const getActiveScope = React.useCallback(() => { return managerRef.current?.getActiveScope(); }, []); /** * Disables all shortcuts in the specified scope or all scopes if no scope is provided. * @param scope - The scope to disable shortcuts in. */ const disableAll = React.useCallback((scope) => { managerRef.current?.disableAll(scope); log(`Disabled all shortcuts${scope ? ` in scope "${scope}"` : ''}`); }, []); /** * Enables all shortcuts in the specified scope or all scopes if no scope is provided. * @param scope - The scope to enable shortcuts in. */ const enableAll = React.useCallback((scope) => { managerRef.current?.enableAll(scope); log(`Enabled all shortcuts${scope ? ` in scope "${scope}"` : ''}`); }, []); /** * Sets the currently active shortcut scope. * * @param {string} scope - The scope name to set as active. */ const setScope = React.useCallback((scope) => { managerRef.current?.setActiveScope(scope); log('Scope set to:', scope); }, []); /** * Resets the scope stack to the default state. */ const resetScope = React.useCallback(() => { managerRef.current?.resetScope(); log('Reset scope'); }, []); /** * Returns all scopes in the stack. * @returns An array of scopes. */ const getScopes = React.useCallback(() => { return managerRef.current?.getScopes(); }, []); /** * Checks if the given scope is active. * @param scope - The scope to check. * @returns `true` if the scope is active, `false` otherwise. */ const isScopeActive = React.useCallback((scope) => { return managerRef.current?.isScopeActive(scope); }, []); /** * Registers a callback to be called when a key is typed. * @param callback - The callback function to be called. */ const onTyping = React.useCallback((callback) => { managerRef.current?.onTyping(callback); }, []); /** * Pops the last scope from the scope stack. */ const popScope = React.useCallback(() => { managerRef.current?.popScope(); log('Popped scope, active scope is:', managerRef.current?.getActiveScope()); }, []); /** * Pushes a new scope onto the scope stack. * @param scope - The scope to push. */ const pushScope = React.useCallback((scope) => { managerRef.current?.pushScope(scope); log('Pushed scope:', scope); }, []); /** * Returns internal information about the registered scopes and shortcuts. * * @param {string} [scope] - Optional scope to filter results. * @returns {Object} Scope information. */ const getScopeInfo = React.useCallback((scope) => { return managerRef.current?.getScopesInfo(scope); }, []); /** * Destroys the instance of `ShortcutManager`. * This should be called explicitly when you no longer need the manager. */ const destroy = () => { managerRef.current?.destroy(); }; /** * Clears the internal state, removing all pressed keys and event listeners. * This does not unregister shortcuts. */ const clear = () => { managerRef.current?.clear(); }; return { register, unregister, enable, disable, toggle, setScope, getCheatSheet, destroy, getScopeInfo, getActiveScope, popScope, pushScope, resetScope, getScopes, isScopeActive, onTyping, enableAll, clear, disableAll, manager: managerRef.current, }; } export { useKeybindy };