@waradu/keyboard
Version:
A keyboard manager compatible with JavaScript, TypeScript and Nuxt
200 lines (196 loc) • 6.42 kB
text/typescript
import { KeyValue, AnyKey, ModifierValue, PlatformValue, KeyString } from '../keys.mjs';
declare class Keybind {
key: KeyValue | AnyKey;
modifiers: Record<ModifierValue, boolean>;
platform?: PlatformValue;
private constructor();
/**
* Format this keybind as a canonical key string.
*
* @returns A valid `KeyString`.
*/
toString(): string;
/**
* Format the main key for display.
*
* @returns A human-readable key label.
*/
toReadableKey(): string;
/**
* Format the platform constraint for display.
*
* @returns A human-readable platform label.
*/
toReadablePlatform(): "Linux" | "macOS" | "Windows" | "macOS & Windows" | "Linux & Windows" | "Linux & macOS" | "All Platforms";
/**
* Format this keybind as display parts.
*
* The returned parts can be joined into a string or rendered as separate keycaps.
*
* @returns Human-readable modifier and key labels.
*/
toReadable(): string[];
/**
* Format this keybind as display parts for a platform.
*
* When no platform is provided, the browser platform is detected. Passing a platform
* is recommended for SSR and tests.
*
* @param config Optional platform override.
* @returns Human-readable modifier and key labels.
*/
toLocalReadable(config?: {
platform?: Os;
}): string[];
/**
* Convert this keybind into a serializable object shape.
*
* @returns A `KeybindShape`.
*/
toShape(): KeybindShape;
/**
* Compare two keybind inputs.
*
* @param a First keybind input.
* @param b Second keybind input.
* @returns Whether both inputs resolve to the same canonical key string.
*/
static equals(a: OptionsKeys, b: OptionsKeys): boolean;
/**
* Compare this keybind with another keybind input.
*
* @param other Keybind input to compare against.
* @returns Whether both inputs resolve to the same canonical key string.
*/
equals: (other: OptionsKeys) => boolean;
/**
* Parse a key string into a keybind.
*
* @param string Key string to parse.
* @returns A `Keybind`, or `undefined` when the string is invalid.
*/
static fromString(string: KeyString): Keybind | undefined;
/**
* Create a keybind from a plain object shape.
*
* Missing modifiers default to `false`.
*
* @param shape Keybind shape to normalize.
* @returns A `Keybind`.
*/
static fromShape(shape: CreateKeybindShape): Keybind;
/**
* Normalize any supported keybind input.
*
* @param keybind Key string, keybind shape, or existing `Keybind`.
* @returns A `Keybind`, or `undefined` when the input is invalid.
*/
static from(keybind: OptionsKeys): Keybind | undefined;
}
type Os = "macos" | "linux" | "windows" | "unknown";
interface KeyboardConfig {
/**
* Print debug messages.
* @default false
*/
debug?: boolean;
/**
* Platform of the user. Set this manually to override automatic detection.
* If not set, the package will try to detect the platform itself.
*/
platform?: Os;
/**
* Disable the automatic initialization attempt if window is available.
*/
noInit?: boolean;
signal?: AbortSignal;
}
interface KeybindShape {
platform?: PlatformValue;
modifiers: Record<ModifierValue, boolean>;
key: KeyValue | AnyKey;
}
interface CreateKeybindShape {
platform?: PlatformValue;
modifiers?: Partial<Record<ModifierValue, boolean>>;
key: KeyValue | AnyKey;
}
interface HandlerContext {
template?: number;
handler: Handler;
event: KeyboardEvent;
}
type When = boolean | null | undefined | void;
interface Config {
/**
* Prevent default.
* @default false
*/
prevent?: boolean;
/**
* Stop propagation.
* Note: this won't prevent other keyboard listeners on the same instance. You have to handle this yourself.
*
* Possible values:
* - `true`:
* Calls `event.stopPropagation()`, preventing the event from reaching parent targets but allowing any remaining listeners on this same element to run.
* - `"immediate"`:
* Calls `event.stopImmediatePropagation()`, preventing any further listeners on this same element and parents from running.
* - `"both"`:
* Stop *immediate* propagation (no further listeners on this same target)
* **and** prevent any propagation to parent targets.
*
* @default false
*/
stop?: "immediate" | "both" | boolean;
/**
* Ignore listener if the user currently is in an editable element like input or textarea.
* @default false
*/
ignoreIfEditable?: boolean;
/**
* Only run listener if one of the `runIfFocused` elements is focused.
*
* **IMPORTANT**: if runIfFocused is an empty list the listener will not run.
* @example
* ```ts
* { ... } // listener will run,
* { ..., runIfFocused: [element, element] } // listener will run if one of the elements is focused
* { ..., runIfFocused: [] } // listener will not run!
* ```
*/
runIfFocused?: (HTMLElement | null | undefined)[];
/**
* Only listen once and then remove the listener.
* @default false
*/
once?: boolean;
/**
* Keybind layer.
* Prefer the use of `keyboard.layer` or `useKeybindLayer` (nuxt) to group listeners into layers.
*/
layers?: string[];
/**
* Boolish value or predicate function before each run whether the listener should run.
*/
when?: When | (() => When);
signal?: AbortSignal;
}
type HandlerFunc = (context: HandlerContext) => unknown;
interface Handler {
id: string;
off: () => unknown;
keys: Keybind[];
handler: HandlerFunc;
config: Config;
}
type Handlers = Handler[];
type OptionsKeys = KeyString | CreateKeybindShape | Keybind;
interface Options {
keys: OptionsKeys | OptionsKeys[];
run: HandlerFunc;
config?: Config;
}
type SubscribeCallback = (handlers: Handlers) => unknown;
export { Keybind as c };
export type { Config as C, Handler as H, KeyboardConfig as K, Os as O, SubscribeCallback as S, Options as a, OptionsKeys as b, CreateKeybindShape as d, HandlerContext as e, HandlerFunc as f, Handlers as g, KeybindShape as h };