Hotkeys
HotkeysTarget binds keyboard shortcuts to part of your UI. Local hotkeys fire while that part has focus. Global hotkeys fire anywhere on the page.
Usage#
import { HotkeysTarget, KeyComboTag } from "@clawscale/react";<HotkeysTarget hotkeys={hotkeys}>
{({ handleKeyDown, handleKeyUp }) => (
<div tabIndex={0} onKeyDown={handleKeyDown} onKeyUp={handleKeyUp}>
<PipelineList />
</div>
)}
</HotkeysTarget>A HotkeysProvider above them collects hotkeys for the dialog. ClawscaleProvider includes one. In function components, the useHotkeys hook does the same job without a wrapper. HotkeysTarget2 is a deprecated alias of HotkeysTarget.
Memoize the hotkeys array, for example with useMemo. A new array on every render registers the hotkeys again.
Examples#
Local hotkeys#
A local hotkey needs a group, which titles it in the hotkeys dialog. Bind handleKeyDown and handleKeyUp to an element with a tabIndex so it can take focus. Click the list, then press J or K.
import { Card, CardList, HotkeysTarget } from "@clawscale/react";
import { useMemo, useState } from "react";
const PIPELINES = ["ingest-orders", "enrich-customers", "export-ledger", "train-forecast"];
export default function HotkeysLocal() {
const [index, setIndex] = useState(0);
const hotkeys = useMemo(
() => [
{
combo: "j",
group: "Pipeline list",
label: "Select next pipeline",
onKeyDown: () => setIndex((i) => Math.min(i + 1, PIPELINES.length - 1)),
},
{
combo: "k",
group: "Pipeline list",
label: "Select previous pipeline",
onKeyDown: () => setIndex((i) => Math.max(i - 1, 0)),
},
],
[],
);
return (
<HotkeysTarget hotkeys={hotkeys}>
{({ handleKeyDown, handleKeyUp }) => (
<CardList compact tabIndex={0} onKeyDown={handleKeyDown} onKeyUp={handleKeyUp} style={{ maxWidth: 320 }}>
{PIPELINES.map((name, i) => (
<Card role="listitem" key={name} selected={i === index}>
{name}
</Card>
))}
</CardList>
)}
</HotkeysTarget>
);
}Global hotkeys#
global: true fires a hotkey wherever focus is. When every hotkey is global, pass an element instead of a render function. No hotkey fires in a text input unless it sets allowInInput.
import { HotkeysTarget, KeyComboTag, Tag } from "@clawscale/react";
import { useMemo, useState } from "react";
export default function HotkeysGlobal() {
const [paused, setPaused] = useState(false);
const hotkeys = useMemo(
() => [{ combo: "p", global: true, label: "Pause or resume ingestion", onKeyDown: () => setPaused((p) => !p) }],
[],
);
return (
<HotkeysTarget hotkeys={hotkeys}>
<div style={{ display: "flex", alignItems: "center", gap: 8 }}>
<span>Press</span>
<KeyComboTag combo="p" />
<span>anywhere on the page.</span>
<Tag minimal intent={paused ? "warning" : "success"}>
{paused ? "Ingestion paused" : "Ingestion running"}
</Tag>
</div>
</HotkeysTarget>
);
}Key combos#
KeyComboTag renders a combo string such as mod+s. mod shows as cmd on macOS and ctrl elsewhere, and macOS shows modifier keys as icons. On the server it renders PC keys, then switches to the visitor's platform after hydration. minimal drops the key caps.
| Action | Default | Minimal |
|---|---|---|
| Save view | ||
| Refresh all panels | ||
| Move row up | ||
| Clear selection |
import { HTMLTable, KeyComboTag } from "@clawscale/react";
import { useEffect, useState } from "react";
const SHORTCUTS = [
{ combo: "mod+s", label: "Save view" },
{ combo: "shift+r", label: "Refresh all panels" },
{ combo: "alt+up", label: "Move row up" },
{ combo: "esc", label: "Clear selection" },
];
export default function HotkeysKeyCombo() {
// KeyComboTag output depends on the platform, so render it after mount.
const [mounted, setMounted] = useState(false);
useEffect(() => setMounted(true), []);
return (
<HTMLTable compact>
<thead>
<tr>
<th>Action</th>
<th>Default</th>
<th>Minimal</th>
</tr>
</thead>
<tbody>
{SHORTCUTS.map(({ combo, label }) => (
<tr key={combo}>
<td>{label}</td>
<td>{mounted && <KeyComboTag combo={combo} />}</td>
<td>{mounted && <KeyComboTag combo={combo} minimal />}</td>
</tr>
))}
</tbody>
</HTMLTable>
);
}Hotkeys dialog#
Press ? on this page to open the hotkeys dialog. It lists every registered hotkey by group, global hotkeys first. Set showDialogKeyCombo in options to change the combo, or pass false to turn it off.
Props interface#
interface HotkeysTargetProps@clawscale/react| Props | Description |
|---|---|
childrenrequired | React.JSX.Element | ((props: HotkeysTargetRenderProps) => React.JSX.Element)Render prop which receives the same callback handlers generated by the useHotkeys hook. If your hotkey definitions are all global, you may supply an element instead. |
hotkeysrequired | readonly HotkeyConfig[]Hotkey definitions. |
options | UseHotkeysOptionsHook customization options. |
Hotkey configuration#
Each entry in hotkeys takes these fields.
interface HotkeyConfig@clawscale/react| Props | Description |
|---|---|
allowInInput | boolean = falseWhether the hotkey should be triggerable when focused in a text input. |
comborequired | stringHotkey combination string (AKA "key combo"), such as "space" or "cmd+n". |
disabled | boolean = falseWhether the hotkey cannot be triggered. |
global | boolean = falseIf 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 | stringUnless 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.ReactNodeHuman-friendly label for the hotkey. |
onKeyDown | (e: KeyboardEvent): anykeydown event handler. |
onKeyUp | (e: KeyboardEvent): anykeyup event handler. |
preventDefault | boolean = falseWhen true, invokes event.preventDefault() before the respective onKeyDown and onKeyUp callbacks are invoked. Enabling this can simplify handler implementations. |
stopPropagation | boolean = falseWhen true, invokes event.stopPropagation() before the respective onKeyDown and onKeyUp callbacks are invoked. Enabling this can simplify handler implementations. |
KeyComboTag#
interface KeyComboTagProps extends Props@clawscale/react| Props | Description |
|---|---|
className | stringA space-delimited list of class names to pass along to a child element. Inherited from Props |
comborequired | stringThe key combo to display, such as "cmd + s". |
minimal | boolean = falseWhether to render in a minimal style. If false, each key in the combo will be rendered inside a <kbd> tag. If true, only the icon or short name of a key will be rendered with no wrapper styles. |