# keymap

> Why JS: a page-wide shortcut answers a key pressed anywhere in the document, and HTML has no declarative wiring from a keystroke to an action.

```ts
import { keymap } from "@sdxc/ui/mixins";
```

Binds a map of page-wide shortcuts for as long as the host stays mounted, with the host
as the bindings' scope: a dialog inside it answers them, any other dialog and any field
being typed in keeps its keys. The latest map passed is the one a keystroke runs.

## Signature

```ts
keymap(bindings: Keymap.Bindings): MixinDescriptor<HTMLElement>
```

## Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| `bindings` | `Keymap.Bindings` | Combos mapped to what each runs. |

## Examples

```tsx
<div mix={[keymap({ j: next, k: previous, "?": openHelp })]}>…</div>
```

## Used with it

The events, constants and types its module publishes, each imported from `@sdxc/ui/mixins` too.

### isClaimedKeystroke

Whether a keystroke belongs to someone else: handled nearer its target already, part
of an IME composition, typed into a field, or pressed inside a dialog outside `scope`.

#### Signature

```ts
isClaimedKeystroke(event: KeyboardEvent, scope?: Element | null): boolean
```

Returns: Whether the bindings stand down for this keystroke.

#### Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| `event` | `KeyboardEvent` | The keystroke as it reached the document. |
| `scope?` | `Element \| null` | The element whose own dialogs still answer the bindings. |

### bindKeymap

Listens for `bindings` on `document` in the bubble phase, so a handler nearer the
target that claims the keystroke first keeps it.

#### Signature

```ts
bindKeymap(document: Document, bindings: Keymap.Bindings, options: Keymap.Options): void
```

#### Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| `document` | `Document` | The document to listen on. |
| `bindings` | `Keymap.Bindings` | Combos mapped to what each runs. |
| `options` | `Keymap.Options` | The signal that removes the listener, and the bindings' own scope. |

#### Examples

```tsx
bindKeymap(document, { j: next, k: previous, "?": openHelp }, { signal });
```

### Keymap.Bindings

Each combo, as parseKeyCombo reads it, mapped to what pressing it runs. The
first entry a keystroke matches runs, and the keystroke is then default-prevented.

### Keymap.Options

Options for bindKeymap.

#### Members

| Member | Type | Description |
| --- | --- | --- |
| `signal` | `AbortSignal` | Takes the bindings off the document. |
| `scope?` | `Element \| null` | The element the bindings belong to. A keystroke inside a `<dialog>` stands the bindings down unless that dialog sits inside this element, so a modal layered over the page keeps its keys while the keymap's own help panel still answers them. |
