Skip to content

Repository files navigation

react-keybind ⌨️

npmdavid-dmBuild Status

A lightweight library to manage global keyboard shortcuts for your React application. Just how lightweight is it?

minified sizeminzipped size

Who should use this library?

  • Your application contains many components that require keyboard shortcuts
  • Your application frequently changes keyboard shortcuts depending on the screen
  • Your application needs a list of all active shortcuts on the screen
  • Your application needs a simple way to manage keyboard shortcuts

Why another keyboard shortcut library?

We wrote react-keybind with a few main goals:

  • No External Dependencies - We wanted full control over the experience and size of the library
  • No RFC/Experimental Features - We wanted to build on top of a stable API
  • TypeScript Support - We wanted to support TypeScript

Features

  • Register shortcuts for single keypresses
  • Register shortcuts for combination keypresses (e.g. ctrl+c, ctrl+alt+a)
  • Register shortcuts for keypresses held after a duration
  • Register shortcuts for sequenced keypresses (e.g. up, up, down, down, enter)
  • Creates one listener for all keyboard shortcuts - fast and lightweight!

Roadmap

  • Focus - Support executing shortcuts when a particular element is focused

Installation

$ yarn add react-keybind

Requirements

This library uses React Context which requires React 16.3+.

TypeScript

This library utilizes TypeScript and exposes a full set of TypeScript definitions.

Usage

This library exposes a default withShortcutHigher-Order Component which is used to wrap any component that wants to utilize keyboard shortcuts.

Your main application should be wrapped in the exposed <ShortcutProvider />.

Example

importReact,{PureComponent}from'react'importReactDOMfrom'react-dom'import{withShortcut,ShortcutProvider,ShortcutConsumer}from'react-keybind'/** * Sample component * * We define shortcuts when the component mounts. This is useful because we can globally keep track * of what keyboard shortcuts _currently_ exist for our application. * * This sample component utilizes CTRL+N and CTRL+S to "create" and "save" files. It shows the usage * of both a regular and async method being used as a shortcut for the app. */classMyComponentextendsPureComponent{state={isSaved: false,totalFiles: 0,}componentDidMount(){const{ shortcut }=this.propsshortcut.registerShortcut(this.save,['ctrl+s','cmd+s'],'Save','Save a file')shortcut.registerShortcut(this.create,['ctrl+n','cmd+n'],'New','Create a new file')}componentWillUnmount(){const{ shortcut }=this.propsshortcut.unregisterShortcut(['ctrl+n','cmd+n'])shortcut.unregisterShortcut(['ctrl+s','cmd+s'])}create=(e)=>{e.preventDefault()console.log('Creating file ...')const{ totalFiles }=this.statethis.setState({isSaved: false,totalFiles: totalFiles+1,})}save=async(e)=>{e.preventDefault()console.log('Saving file ...')awaitnewPromise(resolve=>setTimeout(resolve,1000))this.setState({isSaved: true})}render(){const{ isSaved, totalFiles }=this.statereturn(<div><div>Savethefilewith<strong>Ctrl+S</strong></div><div>Filestatus: {isSaved ? 'Saved' : 'Not Saved'}</div><div>TotalFiles: {totalFiles}</div></div>)}}// Wrap all components that need shortcut capabilities with the "withShortcut" HOCconstMyShortcutComponent=withShortcut(MyComponent)/** * Main Application * * Our main application gets wrapped with <ShortcutProvider /> to provide shortcut functionality * to the entire application. We can then use the <ShortcutConsumer /> in any child component to * access all of the active keybindings our application has. */constMyApp=()=>(<ShortcutProvider><MyShortcutComponent/><ShortcutConsumer>{({keys: allKeys})=>(<div><h1>AvailableKeys</h1><ul>{allKeys.map(binding=>(<likey={binding.id}>{binding.title}-{binding.description}</li>))}</ul></div>)}</ShortcutConsumer></ShortcutProvider>)// That's it, render your application however you normally doReactDOM.render(MyApp,'#app')

The same component above, using React Hooks:

importReact,{useCallback,useEffect,useState}from'react'constMyComponent=(props)=>{const[state,setState]=useState({isSaved: false,totalFiles: 0,})constcreate=useCallback((e)=>{e.preventDefault()console.log('Creating file ...')setState(({totalFiles})=>({isSaved: false,totalFiles: totalFiles+1,}))},[state])constsave=useCallback(async(e)=>{e.preventDefault()console.log('Saving file ...')awaitnewPromise(resolve=>setTimeout(resolve,1000))setState(({totalFiles})=>({isSaved: true,totalFiles: totalFiles,}))},[state])useEffect(()=>{const{ shortcut }=propsshortcut.registerShortcut(save,['ctrl+s','cmd+s'],'Save','Save a file')shortcut.registerShortcut(create,['ctrl+n','cmd+n'],'New','Create a new file')return()=>{const{ shortcut }=propsshortcut.unregisterShortcut(['ctrl+n','cmd+n'])shortcut.unregisterShortcut(['ctrl+s','cmd+s'])}},[])const{ isSaved, totalFiles }=statereturn(<div><div>Savethefilewith<strong>Ctrl+S</strong></div><div>Filestatus: {isSaved ? 'Saved' : 'Not Saved'}</div><div>TotalFiles: {totalFiles}</div></div>)}

API

react-keybind exposes a small set of Components to use in your application.

<ShortcutProvider />

Initializes the main provider for shortcuts. This component should be placed at the root of your application where you want to start listening on keyboard shortcuts.

Props

PropTypeDefaultDescription
ignoreKeysstring[][]Array of keys to ignore (e.g. ['shift', 'ctrl'])
ignoreTagNamesstring[]['input']Array of tagNames to ignore (e.g. ['input', 'article'])
preventDefaultbooleantrueCall preventDefault() automatically when a shortcut is executed

withShortcut(React.ReactNode)

Higher-Order Component to wrap your components with. Provides the following methods and state:

shortcut: {registerShortcut?: (method: (e?: React.KeyboardEvent<any>)=>any,keys: string[],title: string,description: string,holdDuration?: number,)=>voidregisterSequenceShortcut?: (method: ()=>any,keys: string[],title: string,description: string,)=>voidshortcuts: Shortcut[]triggerShortcut?: (key: string)=>anyunregisterShortcut?: (keys: string[])=>void}

<ShortcutConsumer />

An optional consumer that providers the same properties as the withShortcut HOC. Can be used as a direct way to access current shortcuts or access methods for register/unregister new shortcuts.

Use Cases

This library was built specifically for Astral TableTop, an online platform to play tabletop roleplaying games with people from all around the world.

The Astral platform contains many different screens than handle a wide variety of purposes such as a full-featured map editor and a unique online game screen for both Game Masters and players. Each screen in Astral might contain several dozens of keyboard shortcuts.

Instead of managing each screen individually and keeping track of which shortcuts are used where, we simplify the process by letting components decide which shortcuts they want to define and tracking the list of active shortcuts globally. This is especially useful for rendering a quick "Shortcut Menu" for our users no matter where the user might be in the application.

We open-sourced this library in hopes that other projects might find it useful 💙

License

MIT

About

A lightweight library to manage global keyboard shortcuts for your React application

Resources

Code of conduct

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages