CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-hotkeys-hook

React hook for handling keyboard shortcuts in components in a declarative way

Pending
Overview
Eval results
Files

scope-management.mddocs/

Scope Management

Context-based system for organizing hotkeys into groups and controlling their active state across an application, enabling complex hotkey hierarchies and conditional activation.

Capabilities

HotkeysProvider Component

Context provider for managing hotkey scopes across an application.

/**
 * Context provider for managing hotkey scopes across an application
 * @param initiallyActiveScopes - Scopes that are active when provider mounts (default: ['*'])
 * @param children - React children to wrap with hotkey context
 */
function HotkeysProvider({
  initiallyActiveScopes,
  children
}: {
  initiallyActiveScopes?: string[];
  children: ReactNode;
}): JSX.Element;

Usage Examples:

import { HotkeysProvider } from 'react-hotkeys-hook';

// Basic provider setup
function App() {
  return (
    <HotkeysProvider initiallyActiveScopes={['global']}>
      <MainContent />
    </HotkeysProvider>
  );
}

// Multiple initial scopes
function App() {
  return (
    <HotkeysProvider initiallyActiveScopes={['global', 'navigation']}>
      <MainContent />
    </HotkeysProvider>
  );
}

// Default wildcard scope (matches all hotkeys)
function App() {
  return (
    <HotkeysProvider>
      {/* initiallyActiveScopes defaults to ['*'] */}
      <MainContent />
    </HotkeysProvider>
  );
}

useHotkeysContext Hook

Hook to access hotkeys context for scope management and hotkey inspection.

/**
 * Hook to access hotkeys context for scope management
 * @returns Context object with scope control functions and registered hotkeys
 */
function useHotkeysContext(): HotkeysContextType;

interface HotkeysContextType {
  /** Array of currently registered hotkeys */
  hotkeys: ReadonlyArray<Hotkey>;
  /** Array of currently active scope names */
  activeScopes: string[];
  /** Toggle a scope's active state */
  toggleScope: (scope: string) => void;
  /** Enable a specific scope */
  enableScope: (scope: string) => void;
  /** Disable a specific scope */
  disableScope: (scope: string) => void;
}

Usage Examples:

import { useHotkeysContext } from 'react-hotkeys-hook';

function ScopeController() {
  const { activeScopes, enableScope, disableScope, toggleScope, hotkeys } = useHotkeysContext();

  return (
    <div>
      <p>Active scopes: {activeScopes.join(', ')}</p>
      <p>Registered hotkeys: {hotkeys.length}</p>
      
      <button onClick={() => enableScope('editor')}>
        Enable Editor Scope
      </button>
      <button onClick={() => disableScope('navigation')}>
        Disable Navigation
      </button>
      <button onClick={() => toggleScope('debug')}>
        Toggle Debug Mode
      </button>
    </div>
  );
}

Scope System Architecture

Wildcard Scope Behavior

The wildcard scope (*) is special and matches all hotkeys by default.

// These are equivalent when no scopes are specified
useHotkeys('ctrl+k', callback); // Implicitly uses '*' scope
useHotkeys('ctrl+k', callback, { scopes: ['*'] });

// When wildcard is active, scoped hotkeys also work
const { activeScopes } = useHotkeysContext();
// activeScopes: ['*']

useHotkeys('ctrl+j', callback, { scopes: ['editor'] }); // This works
useHotkeys('ctrl+l', callback, { scopes: ['navigation'] }); // This also works

Scope Activation Logic

When enabling specific scopes, the wildcard scope is automatically replaced.

function ScopeExample() {
  const { activeScopes, enableScope } = useHotkeysContext();
  
  // Initially: activeScopes = ['*']
  
  enableScope('editor');
  // Now: activeScopes = ['editor'] (wildcard removed)
  
  enableScope('navigation');
  // Now: activeScopes = ['editor', 'navigation']
  
  return null;
}

Multiple Scope Assignment

Hotkeys can belong to multiple scopes for flexible organization.

// Hotkey active in multiple contexts
useHotkeys('ctrl+s', handleSave, {
  scopes: ['editor', 'form', 'global']
});

// Array of scopes
useHotkeys('escape', handleCancel, {
  scopes: ['modal', 'dropdown', 'overlay']
});

Practical Examples

Modal Management

function Modal({ isOpen, onClose, children }) {
  const { enableScope, disableScope } = useHotkeysContext();
  
  useEffect(() => {
    if (isOpen) {
      enableScope('modal');
    } else {
      disableScope('modal');
    }
  }, [isOpen, enableScope, disableScope]);
  
  // Modal-specific hotkeys
  useHotkeys('escape', onClose, { scopes: ['modal'] });
  useHotkeys('ctrl+enter', handleSubmit, { scopes: ['modal'] });
  
  return isOpen ? <div className="modal">{children}</div> : null;
}

Editor with Multiple Modes

function CodeEditor() {
  const [mode, setMode] = useState('normal');
  const { activeScopes, enableScope, disableScope } = useHotkeysContext();
  
  useEffect(() => {
    // Clean up previous mode
    disableScope('normal');
    disableScope('insert');
    disableScope('visual');
    
    // Enable current mode
    enableScope(mode);
    enableScope('editor'); // Always active in editor
  }, [mode, enableScope, disableScope]);
  
  // Mode-specific hotkeys
  useHotkeys('i', () => setMode('insert'), { scopes: ['normal'] });
  useHotkeys('v', () => setMode('visual'), { scopes: ['normal'] });
  useHotkeys('escape', () => setMode('normal'), { scopes: ['insert', 'visual'] });
  
  // Global editor hotkeys
  useHotkeys('ctrl+s', handleSave, { scopes: ['editor'] });
  
  return <div className="editor">...</div>;
}

Debug Mode Toggle

function App() {
  const [debugMode, setDebugMode] = useState(false);
  const { toggleScope, activeScopes, hotkeys } = useHotkeysContext();
  
  useEffect(() => {
    if (debugMode) {
      toggleScope('debug');
    }
  }, [debugMode, toggleScope]);
  
  // Debug-only hotkeys
  useHotkeys('ctrl+shift+d', () => console.log('Debug info'), {
    scopes: ['debug']
  });
  
  useHotkeys('f12', () => setDebugMode(!debugMode));
  
  if (debugMode) {
    return (
      <div>
        <div>Active scopes: {activeScopes.join(', ')}</div>
        <div>Registered hotkeys: {hotkeys.length}</div>
        <MainApp />
      </div>
    );
  }
  
  return <MainApp />;
}

Types

Context and Hotkey Types

interface Hotkey extends KeyboardModifiers {
  keys?: readonly string[];
  scopes?: Scopes;
  description?: string;
  isSequence?: boolean;
}

interface KeyboardModifiers {
  alt?: boolean;
  ctrl?: boolean;
  meta?: boolean;
  shift?: boolean;
  mod?: boolean;
  useKey?: boolean;
}

type Scopes = string | readonly string[];

Install with Tessl CLI

npx tessl i tessl/npm-react-hotkeys-hook

docs

index.md

key-checking.md

key-recording.md

main-hook.md

scope-management.md

tile.json