UNPKG

@waradu/keyboard

Version:
306 lines (236 loc) 7.4 kB
## Keyboard Manager [![testing](https://github.com/Waradu/keyboard/actions/workflows/testing.yml/badge.svg)](https://github.com/Waradu/keyboard/actions/workflows/testing.yml) - [Install](#install) - [Get Started](#get-started) - [Nuxt](#nuxt) - [Usage](#usage) - [Key Sequence](#key-sequence) - [Handler](#handler) - [Config](#config) - [v6 Changes](#v6-changes) - [Development](#development) - [Examples](#examples) ### Install ```bash bun install @waradu/keyboard ``` ### Get Started Start by importing `useKeyboard` and create a new keyboard instance. ```ts import { useKeyboard } from "@waradu/keyboard"; const keyboard = useKeyboard(); ``` ### Nuxt Nuxt users can use the built-in module that automatically creates and initializes a keyboard instance. It also cleans up listeners when the component unmounts. First add the package to the modules in `nuxt.config.ts`: ```ts export default defineNuxtConfig({ modules: ["@waradu/keyboard/nuxt"], }); ``` And then use it like this: ```ts useKeybind({ keys: ["a"], run() { console.log("A key pressed"); }, }); ``` It is also possible to set up your own composable and plugin for more control. Just copy the templates from the links below and skip adding `@waradu/keyboard/nuxt` to your `nuxt.config.ts`: - [Plugin](https://github.com/Waradu/keyboard/blob/main/src/nuxt/runtime/plugin.ts) - [Composable](https://github.com/Waradu/keyboard/blob/main/src/nuxt/runtime/composable.ts) If you need to access the useKeyboard instance use the Nuxt plugin. ```ts const { $keyboard } = useNuxtApp(); $keyboard.destroy(); ``` ### Usage Do not forget to call `keyboard.init();` once window is available. A listener can be really simple. You just need one or more [key sequences](#key-sequence), a [handler](#handler) and the [config](#config) (optional). ```ts const unlisten = keyboard.listen({ keys: ["control_y", "control_shift_z"], // key sequences run() { // handler console.log("redo"); }, config: { // config }, }); ``` `keyboard.listen` returns a unlisten function that can be called to remove the listener. ```ts const unlisten = keyboard.listen(...); unlisten(); ``` It is also possible to define multiple keybinds in one `listen` call. ```ts keyboard.listen([ { keys: ["control_z"], run() { console.log("undo"); }, }, { keys: ["control_shift_z"], run() { console.log("redo"); }, }, ]); ``` ### Key Sequence Key sequences are just strings of characters defining the key that needs to be pressed to activate the listener. A listener can have multiple key sequences. The structure looks like this (`?` = optional, `!` = required): `"(platform:)?(meta_)?(control_)?(alt_)?(shift_)?(key)!"` or `"any"` - `platform`: Optionally include or exclude certain platforms, for example `macos` or `no-linux`. **(experimental)** - `modifiers`: Keys like `control` or `shift`. They have a fixed order but are optional. - `key`: The actual key. Supports letters, numbers, and more (`f4`, `$`, `arrow-up` etc.). This part is required. If you notice a missing character or symbol, please open an issue. Meta is the equivalent of `windows key` on windows or `cmd` on macos. The order is fixed, the `key` will always come last, `control` always after `meta` etc. The modifiers are not required. Some examples to get a better understanding: - `"control_x"`: ✅ - `"meta_control_alt_shift_arrow-up"`: ✅ - `"c"`: ✅ - `"macos:x"`: ✅ - `"any"`: ✅ (catch all) - `""`: ❌ (empty string) - `"shift_alt_y"`: ❌ (`shift` comes after `alt`) - `"meta_control"`: ❌ (`key` is required) - `"lunix:x"`: ❌ (`lunix` is not a valid platform) - `"xy"`: ❌ (only one `key` at a time) Platform detection is not always reliable. Use it at your own risk, or create your own platform detector and set it through the [config](#config). Why move from the old way (`[Key.Control, Key.X]`)? 1. You don't need to import a separate property anymore. 2. A valid key sequence is enforced (`[Key.X, Key.X]` was valid). 3. The order is fixed so it is more consistent. 4. Easier to read. 5. Allows for easy prefixes like `macos:`. ### Handler The handler is a function that runs when the key sequence is pressed. It can be written in multiple ways. ```ts keyboard.listen({ ... run(event) { ... } // object method (preferred) run: (event) => { ... } // arrow function run: function (event) { ... } // function expression run: handleEvent // external function ... }); ``` ### Config You can set your own platform and skip the built-in detection from `keyboard.init`. Just pass one of these values as the `platform` option: "macos" | "linux" | "windows" | "unknown". This is needed for the [key sequences](#key-sequence) platform prefix. ```ts const detectedPlatform = await yourOwnPlatformDetection(); const keyboard = useKeyboard({ platform: detectedPlatform, }); ``` Each listener can also be configure separately. All keys are optional. ```ts const emailInput = document.getElementById("emailInput"); // Normal const passwordInput = useTemplateRef("passwordInput"); // Nuxt keyboard.listen({ ... config: { // Remove the listener after one run. once: true, // Ignore the listener if any text element like input is focused. ignoreIfEditable: true, // A list of elements which one has to be focused for the listener to run. // The DOM needs to be ready ("DOMContentLoaded" or onMounted (nuxt)). runIfFocused: [emailInput, passwordInput], // Call preventDefault() before run. prevent: true, // Call stopPropagation() before run. (use "immediate" for stopImmediatePropagation() and "both" for both). stop: true, } }); ``` Also you can pass a `signal` to the config or the useKeyboard to abort them with a `signal`. ### v6 Changes - Use `e.key` instead of `e.code` - Support multiple keybinds per listener - No longer need to use `Key.*` - Rewrite `runIfFocused` to `elements` to allow multiple targets - Ignore `event.isComposing` and Dead keys - Remove `ignoreCase` - Platform specific keybinds. - Restructure ### Development Use [bun](https://bun.sh). 1. `bun install` 2. `cd playground` 3. `bun install` 4. `cd ..` 5. 👍 - `bun test`: run tests - `bun test-types`: run type tests - `bun playground`: start playground ### Examples Catch any key press: ```ts keyboard.listen({ keys: ["any"], run(event) { console.log("Key pressed:", event.key); }, }); ``` Run only when an input is focused: ```ts const input = document.getElementById("myInput"); keyboard.listen({ keys: ["enter"], run() { console.log("Enter pressed while input is focused"); }, config: { runIfFocused: [input], }, }); ``` Prevent default behavior (disable refresh with Ctrl+R): ```ts keyboard.listen({ keys: ["control_r"], run() { console.log("Refresh prevented!"); }, config: { prevent: true, }, }); ``` Run a listener only once: ```ts keyboard.listen({ keys: ["escape"], run() { console.log("Escape pressed, this will only log once."); }, config: { once: true, }, }); ``` Platform aware undo/redo: ```ts keyboard.listen([ { keys: ["no-macos:control_z", "macos:meta_z"], run() { console.log("undo"); }, }, { keys: ["no-macos:control_shift_z", "macos:meta_shift_z"], run() { console.log("redo"); }, }, ]); ```