Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@solid-primitives/keyboard

Package Overview
Dependencies
Maintainers
3
Versions
25
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@solid-primitives/keyboard - npm Package Compare versions

Comparing version 0.0.100 to 1.0.0

125

dist/index.d.ts

@@ -1,19 +0,120 @@

declare type KeyModifier = "altKey" | "ctrlKey" | "metaKey" | "shiftKey";
declare type KeyToHold = KeyModifier | (string & {});
import { Accessor } from 'solid-js';
declare type ModifierKey = "Alt" | "Control" | "Meta" | "Shift";
declare type KbdKey = ModifierKey | (string & {});
/**
* Attaches keyboard event-listeners to `window`, and calls {@link onHoldChange} callback whenever user holds or stops holding specified {@link key}.
* Provides a signal with the list of currently held keys, ordered from least recent to most recent.
*
* Event listeners are automatically cleaned on root dispose.
* This is a [shared root primitive](https://github.com/solidjs-community/solid-primitives/tree/main/packages/rootless#createSharedRoot). *(signals and event-listeners are reused across dependents)*
*
* @param key keyboard key or modifier to listen for
* @param onHoldChange callback fired when the hold state changes
* @param options additional configuration
* - `options.preventDefault` — call `e.preventDefault()` on the keyboard event, when the specified {@link key} is pressed. *(Defaults to `false`)*
* - `options.allowOtherKeys` — Should the user be allowed to press other keys while holding the specified one *(Defaults to `false`)*
* @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/keyboard#useKeyDownList
*
* @returns
* Returns a signal of a list of keys, and a signal of last keydown event.
* ```ts
* [keys: Accessor<string[]>, other: { event: Accessor<KeyboardEvent | null> }]
* ```
*
* @example
* ```ts
* const [keys] = useKeyDownList();
* createEffect(() => {
* console.log(keys()) // => ["ALT", "CONTROL", "Q", "A"]
* })
* ```
*/
declare function makeKeyHoldListener(key: KeyToHold, onHoldChange: (isHolding: boolean) => void, options?: {
declare const useKeyDownList: () => [keys: Accessor<string[]>, other: {
event: Accessor<KeyboardEvent | null>;
}];
/**
* Provides a signal with the currently held single key. Pressing any other key at the same time will reset the signal to `null`.
*
* This is a [shared root primitive](https://github.com/solidjs-community/solid-primitives/tree/main/packages/rootless#createSharedRoot). *(signals and event-listeners are reused across dependents)*
*
* @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/keyboard#useCurrentlyHeldKey
*
* @returns
* ```ts
* Accessor<string | null>
* ```
*
* @example
* ```ts
* const key = useCurrentlyHeldKey();
* createEffect(() => {
* console.log(key()) // => "Q" | "ALT" | ... or null
* })
* ```
*/
declare const useCurrentlyHeldKey: () => Accessor<string | null>;
/**
* Provides a signal with a sequence of currently held keys, as they were pressed down and up.
*
* This is a [shared root primitive](https://github.com/solidjs-community/solid-primitives/tree/main/packages/rootless#createSharedRoot). *(signals and event-listeners are reused across dependents)*
*
* @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/keyboard#useKeyDownSequence
*
* @returns
* ```ts
* Accessor<string[][]>
* // [["CONTROL"], ["CONTROL", "Q"], ["CONTROL", "Q", "A"]]
* ```
*
* @example
* ```ts
* const sequence = useKeyDownSequence();
* createEffect(() => {
* console.log(sequence()) // => string[][]
* })
* ```
*/
declare const useKeyDownSequence: () => Accessor<string[][]>;
/**
* Provides a `boolean` signal indicating if provided {@link key} is currently being held down.
* Holding multiple keys at the same time will return `false` — holding only the specified one will return `true`.
*
* @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/keyboard#createKeyHold
*
* @param key The key to check for.
* @options The options for the key hold.
* - `preventDefault` — Controlls in the keydown event should have it's default action prevented. Enabled by default.
* @returns
* ```ts
* Accessor<boolean>
* ```
*
* @example
* ```ts
* const isHeld = createKeyHold("ALT");
* createEffect(() => {
* console.log(isHeld()) // => boolean
* })
* ```
*/
declare function createKeyHold(key: KbdKey, options?: {
preventDefault?: boolean;
allowOtherKeys?: boolean;
}): Accessor<boolean>;
/**
* Creates a keyboard shotcut observer. The provided {@link callback} will be called when the specified {@link keys} are pressed.
*
* @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/keyboard#createShortcut
*
* @param keys The sequence of keys to watch for.
* @param callback The callback to call when the keys are pressed.
* @options The options for the shortcut.
* - `preventDefault` — Controlls in the keydown event should have it's default action prevented. Enabled by default.
* - `requireReset` — If `true`, the shortcut will only be triggered once until all of the keys stop being pressed. Disabled by default.
*
* @example
* ```ts
* createShortcut(["CONTROL", "SHIFT", "C"], () => {
* console.log("Ctrl+Shift+C was pressed");
* });
* ```
*/
declare function createShortcut(keys: KbdKey[], callback: VoidFunction, options?: {
preventDefault?: boolean;
requireReset?: boolean;
}): void;
export { KeyModifier, KeyToHold, makeKeyHoldListener };
export { KbdKey, ModifierKey, createKeyHold, createShortcut, useCurrentlyHeldKey, useKeyDownList, useKeyDownSequence };

156

dist/index.js

@@ -1,47 +0,127 @@

// src/holdKeyListener.ts
"use strict";
// src/index.ts
import { makeEventListener } from "@solid-primitives/event-listener";
var keyModifiers = ["altKey", "ctrlKey", "metaKey", "shiftKey"];
function makeKeyHoldListener(key, onHoldChange, options = {}) {
const { preventDefault, allowOtherKeys } = options;
const modifier = keyModifiers.includes(key) ? key : void 0;
let state = false;
let actualPressed = false;
const updateState = (newState) => {
newState !== state && onHoldChange(state = newState);
};
makeEventListener(window, "keydown", modifier ? (e) => {
import { createSharedRoot } from "@solid-primitives/rootless";
import { arrayEquals } from "@solid-primitives/utils";
import { batch, createEffect, createMemo, createSignal, on, untrack } from "solid-js";
function equalsKeyHoldSequence(sequence, model) {
for (let i = sequence.length - 1; i >= 0; i--) {
const _model = model.slice(0, i + 1);
if (!arrayEquals(sequence[i], _model))
return false;
}
return true;
}
var useKeyDownList = /* @__PURE__ */ createSharedRoot(() => {
const [pressedKeys, setPressedKeys] = createSignal([]);
const [event, setEvent] = createSignal(null);
const reset = () => setPressedKeys([]);
makeEventListener(window, "keydown", (e) => {
if (e.repeat)
return;
if (e[modifier]) {
if (!state && !actualPressed) {
preventDefault && e.preventDefault();
onHoldChange(state = actualPressed = true);
} else if (!allowOtherKeys)
updateState(false);
} else
updateState(actualPressed = false);
} : (e) => {
if (e.key !== key) {
allowOtherKeys || updateState(false);
const key = e.key.toUpperCase();
if (pressedKeys().includes(key))
return;
batch(() => {
setEvent(e);
setPressedKeys((prev) => [...prev, key]);
});
});
makeEventListener(window, "keyup", (e) => {
const key = e.key.toUpperCase();
setPressedKeys((prev) => prev.filter((_key) => _key !== key));
});
makeEventListener(window, "blur", reset);
makeEventListener(window, "contextmenu", (e) => {
e.defaultPrevented || reset();
});
return [pressedKeys, { event }];
});
var useCurrentlyHeldKey = /* @__PURE__ */ createSharedRoot(() => {
const [keys] = useKeyDownList();
let prevKeys = untrack(keys);
return createMemo(() => {
const _keys = keys();
const prev = prevKeys;
prevKeys = _keys;
if (prev.length === 0 && _keys.length === 1)
return _keys[0];
return null;
});
});
var useKeyDownSequence = /* @__PURE__ */ createSharedRoot(() => {
const [keys] = useKeyDownList();
return createMemo((prev) => {
if (keys().length === 0)
return [];
return [...prev, keys()];
}, []);
});
function createKeyHold(key, options = {}) {
key = key.toUpperCase();
const { preventDefault = true } = options;
const [, { event }] = useKeyDownList();
const heldKey = useCurrentlyHeldKey();
return createMemo(() => {
if (heldKey() === key) {
preventDefault && event().preventDefault();
return true;
}
if (e.repeat)
return;
updateState(true);
return false;
});
makeEventListener(window, "keyup", modifier ? (e) => {
if (e[modifier])
allowOtherKeys || updateState(false);
else
updateState(actualPressed = false);
} : (e) => {
if (e.key !== key)
allowOtherKeys || updateState(false);
else
updateState(false);
});
makeEventListener(document, "visibilitychange", () => document.visibilityState !== "visible" && updateState(false));
}
function createShortcut(keys, callback, options = {}) {
if (!keys.length)
return;
keys = keys.map((key) => key.toUpperCase());
const { preventDefault = true, requireReset = false } = options;
const [, { event }] = useKeyDownList();
const sequence = useKeyDownSequence();
let reset = false;
const handleSequenceWithReset = (sequence2) => {
if (!sequence2.length)
return reset = false;
if (reset)
return;
if (sequence2.length < keys.length) {
if (equalsKeyHoldSequence(sequence2, keys.slice(0, sequence2.length))) {
preventDefault && event().preventDefault();
} else {
reset = true;
}
} else {
reset = true;
if (equalsKeyHoldSequence(sequence2, keys)) {
preventDefault && event().preventDefault();
callback();
}
}
};
const handleSequenceWithoutReset = (sequence2) => {
const last = sequence2.at(-1);
if (!last)
return;
if (preventDefault && last.length < keys.length) {
if (arrayEquals(last, keys.slice(0, keys.length - 1))) {
event().preventDefault();
}
return;
}
if (arrayEquals(last, keys)) {
const prev = sequence2.at(-2);
if (!prev || arrayEquals(prev, keys.slice(0, keys.length - 1))) {
preventDefault && event().preventDefault();
callback();
}
}
};
createEffect(on(sequence, requireReset ? handleSequenceWithReset : handleSequenceWithoutReset));
}
export {
makeKeyHoldListener
createKeyHold,
createShortcut,
useCurrentlyHeldKey,
useKeyDownList,
useKeyDownSequence
};

@@ -0,6 +1,16 @@

"use strict";
// src/server.ts
var makeKeyHoldListener = () => {
};
import { noop } from "@solid-primitives/utils";
var useKeyDownList = () => [() => [], { event: () => null }];
var useCurrentlyHeldKey = () => () => null;
var useKeyDownSequence = () => () => [];
var createKeyHold = () => () => false;
var createShortcut = noop;
export {
makeKeyHoldListener
createKeyHold,
createShortcut,
useCurrentlyHeldKey,
useKeyDownList,
useKeyDownSequence
};
{
"name": "@solid-primitives/keyboard",
"version": "0.0.100",
"version": "1.0.0",
"description": "A library of reactive promitives helping handling user's keyboard input.",

@@ -20,3 +20,7 @@ "author": "Damian Tarnwski <gthetarnav@gmail.com>",

"list": [
"makeKeyHoldListener"
"useKeyDownList",
"useCurrentlyHeldKey",
"useKeyDownSequence",
"createKeyHold",
"createShortcut"
],

@@ -46,4 +50,4 @@ "category": "Inputs"

"build": "tsup",
"test": "uvu -r solid-register",
"test:watch": "watchlist src test -- npm test"
"test": "vitest run test",
"test:watch": "vitest watch test"
},

@@ -60,12 +64,10 @@ "keywords": [

"prettier": "^2.7.1",
"solid-register": "^0.2.5",
"solid-js": "^1.4.4",
"tslib": "^2.4.0",
"tsup": "^6.1.2",
"typescript": "^4.7.3",
"typescript": "^4.7.4",
"unocss": "0.39.0",
"uvu": "^0.5.3",
"vite": "2.9.12",
"vite-plugin-solid": "2.2.6",
"watchlist": "^0.3.1",
"solid-js": "^1.4.4"
"vitest": "^0.18.0"
},

@@ -76,4 +78,6 @@ "peerDependencies": {

"dependencies": {
"@solid-primitives/event-listener": "^2.2.0"
"@solid-primitives/event-listener": "^2.2.0",
"@solid-primitives/rootless": "^1.1.0",
"@solid-primitives/utils": "^2.1.1"
}
}

@@ -14,3 +14,7 @@ <p>

- [`makeKeyHoldListener`](#makeKeyHoldListener) - Attaches keyboard event-listeners, and triggers callback whenever user holds or stops holding specified key.
- [`useKeyDownList`](#useKeyDownList) — Provides a signal with the list of currently held keys
- [`useCurrentlyHeldKey`](#useCurrentlyHeldKey) — Provides a signal with the currently held single key.
- [`useKeyDownSequence`](#useKeyDownSequence) — Provides a signal with a sequence of currently held keys, as they were pressed down and up.
- [`createKeyHold`](#createKeyHold) — Provides a signal indicating if provided key is currently being held down.
- [`createShortcut`](#createShortcut) — Creates a keyboard shotcut observer.

@@ -25,30 +29,124 @@ ## Installation

## `makeKeyHoldListener`
## `useKeyDownList`
Attaches keyboard event-listeners to `window`, and calls provided callback whenever user holds or stops holding specified key.
Provides a signal with the list of currently held keys, ordered from least recent to most recent.
Event listeners are automatically cleaned on root dispose.
This is a [shared root](https://github.com/solidjs-community/solid-primitives/tree/main/packages/rootless#createSharedRoot) primitive that will reuse event listeners and signals across dependents.
### How to use it
`makeKeyHoldListener` takes three arguments:
`useKeyDownList` takes no arguments, and returns a signal with the list of currently held keys, and last keydown event.
- `key` keyboard key or modifier to listen for
- `onHoldChange` callback fired when the hold state changes
- `options` additional configuration:
- `preventDefault` — call `e.preventDefault()` on the keyboard event, when the specified key is pressed. _(Defaults to `false`)_
- `allowOtherKeys` — Should the user be allowed to press other keys while holding the specified one _(Defaults to `false`)_
```tsx
import { useKeyDownList } from "@solid-primitives/keyboard";
const [keys, { event }] = useKeyDownList();
createEffect(() => {
console.log(keys()); // => string[] — list of currently held keys
console.log(event()); // => KeyboardEvent | null — last keydown event
});
<For each={keys()}>
{key => <kbd>{key}</kdb>}
</For>
```
## `useCurrentlyHeldKey`
Provides a signal with the currently held single key. Pressing any other key at the same time will reset the signal to `null`.
This is a [shared root](https://github.com/solidjs-community/solid-primitives/tree/main/packages/rootless#createSharedRoot) primitive that will reuse event listeners and signals across dependents.
### How to use it
`useCurrentlyHeldKey` takes no arguments, and returns a signal with the currently held single key.
```tsx
import { makeKeyHoldListener } from "@solid-primitives/keyboard";
import { useCurrentlyHeldKey } from "@solid-primitives/keyboard";
const [pressing, setPressing] = createSignal(false);
const key = useCurrentlyHeldKey();
makeKeyHoldListener("altKey", setPressing, {
preventDefault: true
createEffect(() => {
console.log(key()); // => string | null — currently held key
});
```
## `useKeyDownSequence`
Provides a signal with a sequence of currently held keys, as they were pressed down and up.
This is a [shared root](https://github.com/solidjs-community/solid-primitives/tree/main/packages/rootless#createSharedRoot) primitive that will reuse event listeners and signals across dependents.
### How to use it
`useKeyDownSequence` takes no arguments, and returns a single signal.
```tsx
import { useKeyDownSequence } from "@solid-primitives/keyboard";
const sequence = useKeyDownSequence();
createEffect(() => {
console.log(sequence()); // => string[][] — sequence of currently held keys
});
// example sequence of pressing Ctrl + Shift + A
// [["Control"], ["Control", "Shift"], ["Control", "Shift", "A"]]
```
## `createKeyHold`
Provides a `boolean` signal indicating if provided {@link key} is currently being held down.
Holding multiple keys at the same time will return `false` — holding only the specified one will return `true`.
### How to use it
`createKeyHold` takes two arguments:
- `key` keyboard key to listen for
- `options` additional configuration:
- `preventDefault` — call `e.preventDefault()` on the keyboard event, when the specified key is pressed. _(Defaults to `true`)_
```tsx
import { createKeyHold } from "@solid-primitives/keyboard";
const pressing = createKeyHold("Alt", { preventDefault: false });
<p>Is pressing Alt? {pressing() ? "YES" : "NO"}</p>;
```
## `createShortcut`
Creates a keyboard shotcut observer. The provided callback will be called when the specified keys are pressed.
### How to use it
`createShortcut` takes three arguments:
- `keys` — list of keys to listen for
- `callback` — callback to call when the specified keys are pressed
- `options` — additional configuration:
- `preventDefault` — call `e.preventDefault()` on the keyboard event, when the specified key is pressed. _(Defaults to `true`)_
- `requireReset` — If `true`, the shortcut will only be triggered once until all of the keys stop being pressed. Disabled by default.
```tsx
import { createShortcut } from "@solid-primitives/keyboard";
createShortcut(
["Control", "Shift", "A"],
() => {
console.log("Shortcut triggered");
},
{ preventDefault: false, requireReset: true }
);
```
### Preventing default
When `preventDefault` is `true`, `e.preventDefault()` will be called not only on the keydown event that have triggered the callback, but it will **optimistically** also prevend the default behavior of every previous keydown that will have the possibility to lead to the shotcut being pressed.
E.g. when listening for `Control + Shift + A`, all three keydown events will be prevented.
## Changelog

@@ -63,2 +161,14 @@

1.0.0
[PR#159](https://github.com/solidjs-community/solid-primitives/pull/159)
General package refactor. The single initial `makeKeyHoldListener` primitive has been replaced by:
- `useKeyDownList`,
- `useCurrentlyHeldKey`,
- `useKeyDownSequence`,
- `createKeyHold`,
- `createShortcut`
</details>

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc