All files / use-key-press/src types.ts

0% Statements 0/0
0% Branches 0/0
0% Functions 0/0
0% Lines 0/0

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177                                                                                                                                                                                                                                                                                                                                                                 
import type { RefObject } from "react";
 
/**
 * The set of DOM targets that {@link useKeyPress} can attach its keyboard
 * listeners to.
 *
 * - `undefined` (omitted) defaults to `window`.
 * - `null` disables the listener (useful for conditional targets).
 * - A `RefObject` is resolved lazily to its `.current` element.
 */
export type KeyPressEventTarget =
  | Window
  | Document
  | HTMLElement
  | RefObject<HTMLElement | null>
  | null
  | undefined;
 
/**
 * A predicate that receives the raw {@link KeyboardEvent} and returns whether
 * it should be considered a match. Use this for advanced matching logic that
 * cannot be expressed as a shortcut string.
 *
 * @example
 * ```tsx
 * // Match any digit key
 * useKeyPress((event) => /^[0-9]$/.test(event.key));
 * ```
 */
export type KeyPressPredicate = (event: KeyboardEvent) => boolean;
 
/**
 * Describes which key(s) {@link useKeyPress} should react to.
 *
 * - A **string** is a single binding. It may be a bare key (`"Escape"`,
 *   `"a"`, `"ArrowUp"`) or a modifier combination joined with `+`
 *   (`"ctrl+s"`, `"mod+shift+k"`).
 * - A **string array** is a list of **alternative** bindings — the hook
 *   matches if *any* of them is satisfied (logical OR). This is ideal for
 *   cross-platform shortcuts, e.g. `["ctrl+s", "meta+s"]`.
 * - A **predicate** gives full control over matching.
 *
 * @example
 * ```tsx
 * useKeyPress("Escape");                 // single key
 * useKeyPress("mod+k");                   // combination ("mod" = ctrl on Win/Linux, cmd on Mac)
 * useKeyPress(["ctrl+s", "meta+s"]);      // alternatives (OR)
 * useKeyPress((e) => e.key === "Enter");  // predicate
 * ```
 */
export type KeyPressTarget = string | string[] | KeyPressPredicate;
 
/**
 * Which keyboard event(s) drive the pressed state.
 *
 * - `"keydown"` — pressed becomes `true` on key down only (never resets on its own).
 * - `"keyup"` — reacts to key up only.
 * - `"both"` — tracks the full press/release lifecycle so the returned boolean
 *   reflects whether the key is *currently held*. This is the default.
 */
export type KeyPressEventType = "keydown" | "keyup" | "both";
 
/**
 * Whether shortcuts are matched against the logical key (`event.key`, which is
 * affected by keyboard layout and modifiers) or the physical key
 * (`event.code`, layout-independent — useful for WASD-style controls).
 */
export type KeyPressMatchBy = "key" | "code";
 
/**
 * Configuration options for {@link useKeyPress}.
 */
export interface UseKeyPressOptions {
  /**
   * The element the keyboard listeners are attached to.
   * Accepts `window`, `document`, an `HTMLElement`, or a React `RefObject`.
   * Pass `null` to temporarily detach.
   * @default window
   */
  target?: KeyPressEventTarget;
 
  /**
   * Which keyboard event(s) drive the returned pressed state.
   * @default "both"
   */
  eventType?: KeyPressEventType;
 
  /**
   * When `false`, no listeners are attached and the hook always reports `false`.
   * @default true
   */
  enabled?: boolean;
 
  /**
   * Call `event.preventDefault()` when the target matches.
   * Useful for overriding browser shortcuts such as `ctrl+s`.
   * @default false
   */
  preventDefault?: boolean;
 
  /**
   * Call `event.stopPropagation()` when the target matches.
   * @default false
   */
  stopPropagation?: boolean;
 
  /**
   * Ignore auto-repeated `keydown` events (fired while a key is held down)
   * when invoking {@link UseKeyPressOptions.onPress}. Does not affect the
   * returned boolean state.
   * @default true
   */
  ignoreRepeat?: boolean;
 
  /**
   * Ignore key events that originate from editable elements — `<input>`,
   * `<textarea>`, `<select>`, and `contenteditable` nodes. Essential for
   * app-level shortcuts that must not fire while the user is typing.
   * @default false
   */
  ignoreInputElements?: boolean;
 
  /**
   * Match keys case-sensitively. When `false`, `"a"` also matches `"A"`.
   * Only applies when {@link UseKeyPressOptions.matchBy} is `"key"`.
   * @default false
   */
  caseSensitive?: boolean;
 
  /**
   * Match against the logical key (`"key"`) or the physical key (`"code"`).
   * @default "key"
   */
  matchBy?: KeyPressMatchBy;
 
  /**
   * Require an exact modifier match for combinations. When `true`,
   * `"ctrl+s"` will **not** match `ctrl+shift+s` because an extra modifier is
   * held. When `false`, additional modifiers are permitted.
   * @default true
   */
  exactModifiers?: boolean;
 
  /**
   * Called when the target is matched on `keydown`, receiving the raw event.
   * This is the correct place to call `event.preventDefault()` manually.
   */
  onPress?: (event: KeyboardEvent) => void;
 
  /**
   * Called when a previously matched target is released on `keyup`.
   */
  onRelease?: (event: KeyboardEvent) => void;
}
 
/**
 * A parsed representation of a single shortcut binding such as `"mod+shift+k"`.
 * Modifier requirements are resolved to concrete booleans; `"mod"` is expanded
 * to `metaKey` on Apple platforms and `ctrlKey` elsewhere.
 */
export interface ParsedShortcut {
  /** Whether the Ctrl modifier must be held. */
  ctrl: boolean;
  /** Whether the Shift modifier must be held. */
  shift: boolean;
  /** Whether the Alt/Option modifier must be held. */
  alt: boolean;
  /** Whether the Meta/Command modifier must be held. */
  meta: boolean;
  /**
   * The non-modifier key that must match, already normalized for
   * case-insensitive comparison when applicable. `null` for modifier-only
   * bindings such as `"shift"`.
   */
  key: string | null;
}