ShortcutListener

Register page-specific shortcuts that only work on specific pages or components.

Basic Usage

Use ShortcutListener to register shortcuts that are specific to a page or component:

import { ShortcutListener } from '@chrisnski/webshorts';

function MyPage() {
  const handleSave = () => {
    console.log('Saving...');
  };

  return (
    <div>
      <h1>My Page</h1>
      
      {/* Register shortcuts for this page */}
      <ShortcutListener keys='CTRL + S' action={handleSave} />
      <ShortcutListener keys='ALT + N' action={() => console.log('New item')} />
      
      {/* Your page content */}
    </div>
  );
}

Props Reference

The ShortcutListener component accepts several props to customize its behavior:

<ShortcutListener
  keys='CTRL + S'
  action={handleSave}
  page='/dashboard' // Optional, defaults to current page
/>

Required Props

keys

Key combination (e.g., "CTRL + SHIFT + A")

action

Function to execute when shortcut is pressed

Optional Props

page

Page where this shortcut is active (defaults to current page)

children

Optional children for semantic grouping (not required for functionality)

Usage Examples

Basic Shortcut

// Simple shortcut with function
<ShortcutListener 
  keys='CTRL + S' 
  action={() => saveDocument()} 
/>

With Specific Page

// Shortcut that only works on a specific page
<ShortcutListener 
  keys='CTRL + B' 
  action={handleBold}
  page='/editor'
/>

Multiple Shortcuts

// Register multiple shortcuts in one component
function EditorPage() {
  return (
    <div>
      <ShortcutListener keys='CTRL + S' action={handleSave} />
      <ShortcutListener keys='CTRL + Z' action={handleUndo} />
      <ShortcutListener keys='CTRL + Y' action={handleRedo} />
      <ShortcutListener keys='CTRL + B' action={handleBold} />
      <ShortcutListener keys='CTRL + I' action={handleItalic} />
      
      {/* Editor content */}
    </div>
  );
}

With Children (Optional)

// ShortcutListener can wrap children for semantic grouping
// Children are optional and don't affect functionality
<ShortcutListener keys='ESC' action={handleEscape}>
  <div className='editor-toolbar'>
    {/* Toolbar content */}
  </div>
</ShortcutListener>

// Or without children (functionally identical)
<ShortcutListener keys='ESC' action={handleEscape} />
<div className='editor-toolbar'>
  {/* Toolbar content */}
</div>

Children Functionality

ShortcutListener can optionally wrap children for semantic grouping, but this is purely for code organization and readability. The children don't affect the shortcut functionality in any way.

Semantic Grouping

  • Optional - Children are not required for shortcut functionality
  • Semantic - Useful for grouping related UI elements with their shortcuts
  • Readability - Makes code more self-documenting
  • No Impact - Children don't affect shortcut behavior or registration
// These two approaches are functionally identical:

// With children (semantic grouping)
<ShortcutListener keys='CTRL + S' action={handleSave}>
  <button className='save-button'>Save</button>
</ShortcutListener>

// Without children (same functionality)
<ShortcutListener keys='CTRL + S' action={handleSave} />
<button className='save-button'>Save</button>

Component Lifecycle

ShortcutListener automatically handles registration and cleanup of shortcuts:

function MyComponent() {
  const [isEditing, setIsEditing] = useState(false);

  return (
    <div>
      {/* This shortcut is registered when component mounts */}
      <ShortcutListener 
        keys='CTRL + S' 
        action={handleSave} 
      />
      
      {/* This shortcut is only registered when isEditing is true */}
      {isEditing && (
        <ShortcutListener 
          keys='ESC' 
          action={() => setIsEditing(false)} 
        />
      )}
    </div>
  );
}

Automatic Cleanup

  • Registration - Shortcuts are registered when the component mounts
  • Cleanup - Shortcuts are automatically unregistered when the component unmounts
  • Conditional - Shortcuts can be conditionally registered based on component state
  • Memory safe - No memory leaks from forgotten event listeners

Common Patterns

Form Shortcuts

function ContactForm() {
  return (
    <form>
      <ShortcutListener keys='CTRL + ENTER' action={handleSubmit} />
      <ShortcutListener keys='ESC' action={handleCancel} />
      
      {/* Form fields */}
    </form>
  );
}

Button Grouping (Semantic)

function Toolbar() {
  return (
    <div className='toolbar'>
      <ShortcutListener keys='CTRL + S' action={handleSave}>
        <button className='toolbar-button'>Save</button>
      </ShortcutListener>
      
      <ShortcutListener keys='CTRL + Z' action={handleUndo}>
        <button className='toolbar-button'>Undo</button>
      </ShortcutListener>
      
      <ShortcutListener keys='CTRL + Y' action={handleRedo}>
        <button className='toolbar-button'>Redo</button>
      </ShortcutListener>
    </div>
  );
}

Editor Shortcuts

function TextEditor() {
  const [isEditing, setIsEditing] = useState(false);

  return (
    <div>
      <ShortcutListener keys='CTRL + E' action={() => setIsEditing(!isEditing)} />
      
      {isEditing && (
        <>
          <ShortcutListener keys='CTRL + S' action={handleSave} />
          <ShortcutListener keys='CTRL + Z' action={handleUndo} />
          <ShortcutListener keys='ESC' action={() => setIsEditing(false)} />
        </>
      )}
      
      {/* Editor content */}
    </div>
  );
}

Navigation Shortcuts

function DashboardPage() {
  return (
    <div>
      <ShortcutListener keys='ALT + 1' action={() => navigate('/dashboard')} />
      <ShortcutListener keys='ALT + 2' action={() => navigate('/profile')} />
      <ShortcutListener keys='ALT + 3' action={() => navigate('/settings')} />
      
      {/* Dashboard content */}
    </div>
  );
}

Troubleshooting

Shortcut not working?

  • • Check that the component is mounted and rendered
  • • Verify the key combination format (e.g., "CTRL + S" not "Ctrl + S")
  • • Ensure the action function is properly defined
  • • Check for conflicts with other shortcuts or browser defaults
  • • Enable debug mode to see registration feedback

Shortcut conflicts?

  • • Check for duplicate shortcuts on the same page
  • • Verify that the page prop is correctly set
  • • Ensure conditional rendering logic is correct
  • • Test with debug mode enabled to see all registered shortcuts