Clawscale

useHotkeys

useHotkeys registers keyboard shortcuts for a component and lists them in the hotkeys dialog. Global hotkeys work anywhere on the page. Local hotkeys work while focus is inside an element you choose.

Usage#

import { useHotkeys } from "@clawscale/react";
const hotkeys = useMemo(() => [{ combo: "mod+s", global: true, label: "Save view", onKeyDown: saveView }], [saveView]);
const { handleKeyDown, handleKeyUp } = useHotkeys(hotkeys);

The hook needs a HotkeysProvider, which ClawscaleProvider includes. Memoize the hotkeys array, or the hook binds them again on every render.

Each hotkey is either global or has a group, the heading it appears under in the dialog. Hotkeys do not fire while a text field has focus, unless they set allowInInput.

Examples#

Global hotkeys#

global: true binds the hotkey to the whole document. Press R anywhere on this page.

RNot refreshed yet
import { Button, Classes, KeyComboTag, useHotkeys } from "@clawscale/react";
import { useCallback, useMemo, useState } from "react";

export default function UseHotkeysGlobal() {
  const [refreshedAt, setRefreshedAt] = useState<string>();
  const refresh = useCallback(() => setRefreshedAt(new Date().toLocaleTimeString("en-GB")), []);

  // Memoize the list, or the hook binds the hotkeys again on every render.
  const hotkeys = useMemo(
    () => [{ combo: "r", global: true, label: "Refresh run list", onKeyDown: refresh }],
    [refresh],
  );
  useHotkeys(hotkeys);

  return (
    <>
      <Button icon="refresh" text="Refresh runs" onClick={refresh} />
      <KeyComboTag combo="r" />
      <span className={Classes.TEXT_MUTED}>{refreshedAt ? `Refreshed at ${refreshedAt}` : "Not refreshed yet"}</span>
    </>
  );
}

Local hotkeys#

Other hotkeys fire only for key events that reach handleKeyDown and handleKeyUp. Attach both to a focusable element.

import { Card, Classes, KeyComboTag, Menu, MenuItem, useHotkeys } from "@clawscale/react";
import { useMemo, useState } from "react";

const pipelines = ["ingest-orders", "sessionize-events", "nightly-rollup", "export-finance"];

export default function UseHotkeysLocal() {
  const [selected, setSelected] = useState(0);
  const hotkeys = useMemo(
    () => [
      {
        combo: "down",
        group: "Pipeline list",
        label: "Select next pipeline",
        preventDefault: true,
        onKeyDown: () => setSelected((index) => Math.min(index + 1, pipelines.length - 1)),
      },
      {
        combo: "up",
        group: "Pipeline list",
        label: "Select previous pipeline",
        preventDefault: true,
        onKeyDown: () => setSelected((index) => Math.max(index - 1, 0)),
      },
    ],
    [],
  );
  const { handleKeyDown, handleKeyUp } = useHotkeys(hotkeys);

  return (
    <Card onKeyDown={handleKeyDown} onKeyUp={handleKeyUp} style={{ width: "100%", maxWidth: 300 }} tabIndex={0}>
      <Menu>
        {pipelines.map((name, index) => (
          <MenuItem active={index === selected} icon="data-lineage" key={name} text={name} />
        ))}
      </Menu>
      <p className={Classes.TEXT_MUTED} style={{ margin: "8px 0 0" }}>
        Focus the card, then press <KeyComboTag combo="up" minimal /> or <KeyComboTag combo="down" minimal />.
      </p>
    </Card>
  );
}

Hotkeys dialog#

Press ? to open a dialog that lists the hotkeys of this page, grouped by group. showDialogKeyCombo changes the key, and false turns it off. Hotkeys provider shows how to customize the dialog.

Key combos#

A combo is zero or more modifiers and one key, joined with +, such as shift+r or mod+k. Letters are case-insensitive. Spaces are ignored.

KindValues
Modifiersshift, alt or option, ctrl, meta or cmd, mod
Named keysenter, esc, space, tab, backspace, del, home, end, pageup, pagedown, up, down, left, right, plus, minus

mod is cmd on macOS and ctrl elsewhere.

Props interface#

interface UseHotkeysOptions@clawscale/react
PropsDescription
document
Document = window.document
A custom document to reference when binding global event handlers. This can be useful when using iframes in an application.
showDialogKeyCombo
string | false = "?"
The key combo which will trigger the hotkeys dialog to open. Pass false to disable the built-in trigger entirely; the dialog can still be opened programmatically via {@link HotkeysContext }.
interface HotkeyConfig@clawscale/react
PropsDescription
allowInInput
boolean = false
Whether the hotkey should be triggerable when focused in a text input.
comborequired
string
Hotkey combination string (AKA "key combo"), such as "space" or "cmd+n".
disabled
boolean = false
Whether the hotkey cannot be triggered.
global
boolean = false
If false, the hotkey is active only when the target is focused. If true, the hotkey can be triggered regardless of what component is focused.
group
string
Unless the hotkey is global, you must specify a group where the hotkey will be displayed in the hotkeys dialog. This string will be displayed in a header at the start of the group of hotkeys.
labelrequired
React.ReactNode
Human-friendly label for the hotkey.
onKeyDown
(e: KeyboardEvent): any
keydown event handler.
onKeyUp
(e: KeyboardEvent): any
keyup event handler.
preventDefault
boolean = false
When true, invokes event.preventDefault() before the respective onKeyDown and onKeyUp callbacks are invoked. Enabling this can simplify handler implementations.
stopPropagation
boolean = false
When true, invokes event.stopPropagation() before the respective onKeyDown and onKeyUp callbacks are invoked. Enabling this can simplify handler implementations.