Skip to content

zakodium-oss/react-kbs

Repository files navigation

react-kbs

React library to manage your application's keyboard shortcuts.

Zakodium logo

Maintained by Zakodium

NPM version build status npm download

Installation

npm i react-kbs

Demo

https://zakodium-oss.github.io/react-kbs/

Documentation

Shortcut definition

A shortcut is defined using an object with at least two fields: shortcut and handler.

  • shortcut is a definition of the key combination that must be used to trigger the shortcut. It can be:
    • a string defining the keyboard's key (case-insensitive). Example: 'a'.
    • an object of the form { key: string; ctrl?: boolean; shift?: boolean; alt?: boolean }. Example: { key: 's', ctrl: true }.
    • an object of the form { code: string; ctrl?: boolean; shift?: boolean; alt?: boolean }. Example: { code: 'Digit1', shift: true }.
    • an array of such strings and/or objects. This allows to define aliases for the same handler. Example: ['/', { key: 'k', ctrl: true }]
  • handler is the function that will be called when the shortcut is triggered.
  • meta is an optional object that can be used to store any additional information about the shortcut. It will be available in the data returned by useKbsGlobalList().
  • maxFrequency is an optional number that defines the maximum number of times the shortcut can be triggered per second. This only affects repeated triggers of the same shortcut when a key is held down.

Use key when you want to refer to the character written by typing on the key and code when you want to use the physical key code (e.g. Digit1 for the 1 key).

There are some things to note about the behavior of shortcuts in react-kbs:

  • On macOS, ctrl automatically maps to the command key. This means that if a shortcut is defined with { key: 's', ctrl: true }, the Ctrl+S combination must be pressed on Linux and Windows, whereas Cmd+Smust be pressed on macOS.
  • By default, if the shift property is not specified, the state of the Shift key doesn't matter. In other words, the shortcut will be triggered with or without the Shift key being pressed. This is in order to make the shortcuts case-insensitive and to support any keyboard layout (the need to press Shift for some keys depend on the user's layout).

Global shortcuts

Global shortcuts are shortcuts that are active anywhere on the page, as long as no specific element has the focus.

To setup global shortcuts, you need to render the KbsProvider component high in your React component tree:

import { KbsProvider } from 'react-kbs';

export default function App() {
  return (
    <KbsProvider>
      <SubComponent />
    </KbsProvider>
  );
}

Then, anywhere down the tree, you can call the useKbsGlobal hook with an array of shortcut definitions to add global shortcuts to the context:

import { useKbsGlobal } from 'react-kbs';

export function MyComponent() {
  const [counter, setCounter] = useState(0);
  useKbsGlobal([
    {
      shortcut: 'i',
      handler() {
        setCounter((current) => current + 1);
      },
    },
    {
      shortcut: 'd',
      handler() {
        setCounter((current) => current - 1);
      },
    },
  ]);

  return <div>Count: {counter}</div>;
}

Local shortcuts

Local shortcuts are shortcuts that are only active when a specific element on the page, or one of its children, is focused.

It is possible to nest such elements. In that case, the first shortcut definition that matches the user input will apply.

To setup local shortcuts, call the useKbs hook with an array of shortcut definitions and pass the value it returns as props to the element for which you want to enable the shortcuts. The following attributes will be set:

  • tabIndex={0}: makes the element focusable
  • onKeyDown={handler}: event handler
function MyComponent(props) {
  const shortcutProps = useKbs([
    {
      shortcut: ['delete', 'backspace'],
      handler: props.onDelete,
    },
  ]);

  return (
    <div {...shortcutProps}>
      <SomeContent />
    </div>
  );
}

Effect of focus on keyboard shortcuts

When no particular element is focused, global shortcuts take effect.
When simple focusable elements (buttons, links, elements that have a tabindex), are focused, global or local shortcuts will usually be triggered, unless one of the following conditions applies:

  • The element is an HTML <input>, <textarea>, or <select>.
  • The element is contenteditable.
  • The element has the data-kbs-ignore attribute.

Disable global shortcuts

It may be useful in some cases to disable all global shortcuts (for example when a dialog is open). To do this, call the useKbsDisableGlobal hook:

import { useKbsDisableGlobal } from 'react-kbs';

function MyDialog({ open }) {
  useKbsDisableGlobal(open);
}

Get the list of global shortcuts

To get the list of all currently defined global shortcuts, call the useKbsGlobalList hook:

import { useKbsGlobalList } from 'react-kbs';

function MyComponent() {
  const shortcuts = useKbsGlobalList();

  return <ShortcutHelp shortcuts={shortcuts} />;
}

Shortcut metadata

It is possible to pass optional metadata with each shortcut in the meta field. The metadata will then be available in the objects returned by useKbsGlobalList().

react-kbs doesn't use the metadata object, which can have any shape. A common use case is to dynamically render a list with all shortcuts and documentation about them.

useKbsGlobal([
  {
    shortcut: 'delete',
    handler: handleDelete,
    meta: {
      description: 'Delete the selected element',
    },
  },
]);

If using TypeScript, you will have to define the shape of the meta object to suit your needs:

import 'react-kbs';

declare module 'react-kbs' {
  interface KbsMetadata {
    description: string;
  }
}