New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@solid-primitives/keyboard

Package Overview
Dependencies
Maintainers
3
Versions
26
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 1.2.8 to 1.3.0

21

dist/index.d.ts

@@ -1,5 +0,4 @@

import { Accessor } from 'solid-js';
type ModifierKey = "Alt" | "Control" | "Meta" | "Shift";
type KbdKey = ModifierKey | (string & {});
import { Accessor } from "solid-js";
export type ModifierKey = "Alt" | "Control" | "Meta" | "Shift";
export type KbdKey = ModifierKey | (string & {});
/**

@@ -33,3 +32,3 @@ * Provides a signal with the last keydown event.

*/
declare const useKeyDownEvent: () => Accessor<KeyboardEvent | null>;
export declare const useKeyDownEvent: () => Accessor<KeyboardEvent | null>;
/**

@@ -56,3 +55,3 @@ * Provides a signal with the list of currently held keys, ordered from least recent to most recent.

*/
declare const useKeyDownList: () => Accessor<string[]>;
export declare const useKeyDownList: () => Accessor<string[]>;
/**

@@ -78,3 +77,3 @@ * Provides a signal with the currently held single key. Pressing any other key at the same time will reset the signal to `null`.

*/
declare const useCurrentlyHeldKey: () => Accessor<string | null>;
export declare const useCurrentlyHeldKey: () => Accessor<string | null>;
/**

@@ -101,3 +100,3 @@ * Provides a signal with a sequence of currently held keys, as they were pressed down and up.

*/
declare const useKeyDownSequence: () => Accessor<string[][]>;
export declare const useKeyDownSequence: () => Accessor<string[][]>;
/**

@@ -125,3 +124,3 @@ * Provides a `boolean` signal indicating if provided {@link key} is currently being held down.

*/
declare function createKeyHold(key: KbdKey, options?: {
export declare function createKeyHold(key: KbdKey, options?: {
preventDefault?: boolean;

@@ -147,7 +146,5 @@ }): Accessor<boolean>;

*/
declare function createShortcut(keys: KbdKey[], callback: (event: KeyboardEvent | null) => void, options?: {
export declare function createShortcut(keys: KbdKey[], callback: (event: KeyboardEvent | null) => void, options?: {
preventDefault?: boolean;
requireReset?: boolean;
}): void;
export { KbdKey, ModifierKey, createKeyHold, createShortcut, useCurrentlyHeldKey, useKeyDownEvent, useKeyDownList, useKeyDownSequence };

@@ -1,82 +0,158 @@

import { makeEventListener } from '@solid-primitives/event-listener';
import { createSingletonRoot } from '@solid-primitives/rootless';
import { arrayEquals } from '@solid-primitives/utils';
import { createSignal, untrack, createMemo, createEffect, on } from 'solid-js';
import { isServer } from 'solid-js/web';
// src/index.ts
import { makeEventListener } from "@solid-primitives/event-listener";
import { createSingletonRoot } from "@solid-primitives/rootless";
import { arrayEquals } from "@solid-primitives/utils";
import { createEffect, createMemo, createSignal, on, untrack } from "solid-js";
import { isServer } from "solid-js/web";
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;
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 useKeyDownEvent = /* @__PURE__ */ createSingletonRoot(
() => {
/**
* Provides a signal with the last keydown event.
*
* The signal is `null` initially, and is reset to that after a timeout.
*
* @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/keyboard#useKeyDownEvent
*
* @returns
* Returns a signal of the last keydown event
* ```ts
* Accessor<KeyboardEvent | null>
* ```
*
* @example
* ```ts
* const event = useKeyDownEvent();
*
* createEffect(() => {
* const e = event();
* console.log(e) // => KeyboardEvent | null
*
* if (e) {
* console.log(e.key) // => "Q" | "ALT" | ... or null
* e.preventDefault(); // prevent default behavior or last keydown event
* }
* })
* ```
*/
export const useKeyDownEvent = /*#__PURE__*/ createSingletonRoot(() => {
if (isServer) {
return () => null;
return () => null;
}
const [event, setEvent] = createSignal(null);
makeEventListener(window, "keydown", (e) => {
setEvent(e);
setTimeout(() => setEvent(null));
makeEventListener(window, "keydown", e => {
setEvent(e);
setTimeout(() => setEvent(null));
});
return event;
}
);
var useKeyDownList = /* @__PURE__ */ createSingletonRoot(() => {
if (isServer) {
const keys = () => [];
keys[0] = keys;
keys[1] = { event: () => null };
keys[Symbol.iterator] = function* () {
yield keys[0];
yield keys[1];
});
/**
* Provides a signal with the list of currently held keys, ordered from least recent to most recent.
*
* This is a [singleton root primitive](https://github.com/solidjs-community/solid-primitives/tree/main/packages/rootless#createSingletonRoot). *(signals and event-listeners are reused across dependents)*
*
* @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/keyboard#useKeyDownList
*
* @returns
* Returns a signal of a list of keys
* ```ts
* Accessor<string[]>
* ```
*
* @example
* ```ts
* const keys = useKeyDownList();
* createEffect(() => {
* console.log(keys()) // => ["ALT", "CONTROL", "Q", "A"]
* })
* ```
*/
export const useKeyDownList = /*#__PURE__*/ createSingletonRoot(() => {
if (isServer) {
const keys = () => [];
// this is for backwards compatibility
// TODO remove in the next major version
keys[0] = keys;
keys[1] = { event: () => null };
keys[Symbol.iterator] = function* () {
yield keys[0];
yield keys[1];
};
return keys;
}
const [pressedKeys, setPressedKeys] = createSignal([]), reset = () => setPressedKeys([]), event = useKeyDownEvent();
makeEventListener(window, "keydown", e => {
// e.key may be undefined when used with <datalist> el
// gh issue: https://github.com/solidjs-community/solid-primitives/issues/246
if (e.repeat || typeof e.key !== "string")
return;
const key = e.key.toUpperCase(), currentKeys = pressedKeys();
if (currentKeys.includes(key))
return;
const keys = [...currentKeys, key];
// if the modifier is pressed before we start listening
// we should add it to the list
if (currentKeys.length === 0 &&
key !== "ALT" &&
key !== "CONTROL" &&
key !== "META" &&
key !== "SHIFT") {
if (e.shiftKey)
keys.unshift("SHIFT");
if (e.altKey)
keys.unshift("ALT");
if (e.ctrlKey)
keys.unshift("CONTROL");
if (e.metaKey)
keys.unshift("META");
}
setPressedKeys(keys);
});
makeEventListener(window, "keyup", e => {
if (typeof e.key !== "string")
return;
const key = e.key.toUpperCase();
setPressedKeys(prev => prev.filter(_key => _key !== key));
});
makeEventListener(window, "blur", reset);
makeEventListener(window, "contextmenu", e => {
e.defaultPrevented || reset();
});
// this is for backwards compatibility
// TODO remove in the next major version
pressedKeys[0] = pressedKeys;
pressedKeys[1] = { event };
pressedKeys[Symbol.iterator] = function* () {
yield pressedKeys[0];
yield pressedKeys[1];
};
return keys;
}
const [pressedKeys, setPressedKeys] = createSignal([]), reset = () => setPressedKeys([]), event = useKeyDownEvent();
makeEventListener(window, "keydown", (e) => {
if (e.repeat || typeof e.key !== "string")
return;
const key = e.key.toUpperCase(), currentKeys = pressedKeys();
if (currentKeys.includes(key))
return;
const keys = [...currentKeys, key];
if (currentKeys.length === 0 && key !== "ALT" && key !== "CONTROL" && key !== "META" && key !== "SHIFT") {
if (e.shiftKey)
keys.unshift("SHIFT");
if (e.altKey)
keys.unshift("ALT");
if (e.ctrlKey)
keys.unshift("CONTROL");
if (e.metaKey)
keys.unshift("META");
}
setPressedKeys(keys);
});
makeEventListener(window, "keyup", (e) => {
if (typeof e.key !== "string")
return;
const key = e.key.toUpperCase();
setPressedKeys((prev) => prev.filter((_key) => _key !== key));
});
makeEventListener(window, "blur", reset);
makeEventListener(window, "contextmenu", (e) => {
e.defaultPrevented || reset();
});
pressedKeys[0] = pressedKeys;
pressedKeys[1] = { event };
pressedKeys[Symbol.iterator] = function* () {
yield pressedKeys[0];
yield pressedKeys[1];
};
return pressedKeys;
return pressedKeys;
});
var useCurrentlyHeldKey = /* @__PURE__ */ createSingletonRoot(
() => {
/**
* 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 [singleton root primitive](https://github.com/solidjs-community/solid-primitives/tree/main/packages/rootless#createSingletonRoot). *(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
* })
* ```
*/
export const useCurrentlyHeldKey = /*#__PURE__*/ createSingletonRoot(() => {
if (isServer) {
return () => null;
return () => null;
}

@@ -86,81 +162,143 @@ const keys = useKeyDownList();

return createMemo(() => {
const _keys = keys();
const prev = prevKeys;
prevKeys = _keys;
if (prev.length === 0 && _keys.length === 1)
return _keys[0];
return null;
const _keys = keys();
const prev = prevKeys;
prevKeys = _keys;
if (prev.length === 0 && _keys.length === 1)
return _keys[0];
return null;
});
}
);
var useKeyDownSequence = /* @__PURE__ */ createSingletonRoot(() => {
if (isServer) {
return () => [];
}
const keys = useKeyDownList();
return createMemo((prev) => {
if (keys().length === 0)
return [];
return [...prev, keys()];
}, []);
});
function createKeyHold(key, options = {}) {
if (isServer) {
return () => false;
}
key = key.toUpperCase();
const { preventDefault = true } = options, event = useKeyDownEvent(), heldKey = useCurrentlyHeldKey();
return createMemo(() => heldKey() === key && (preventDefault && event()?.preventDefault(), true));
}
function createShortcut(keys, callback, options = {}) {
if (isServer || !keys.length) {
return;
}
keys = keys.map((key) => key.toUpperCase());
const { preventDefault = true } = options, event = useKeyDownEvent(), sequence = useKeyDownSequence();
let reset = false;
const handleSequenceWithReset = (sequence2) => {
if (!sequence2.length)
return reset = false;
if (reset)
return;
const e = event();
if (sequence2.length < keys.length) {
if (equalsKeyHoldSequence(sequence2, keys.slice(0, sequence2.length))) {
preventDefault && e && e.preventDefault();
} else {
reset = true;
}
} else {
reset = true;
if (equalsKeyHoldSequence(sequence2, keys)) {
preventDefault && e && e.preventDefault();
callback(e);
}
/**
* Provides a signal with a sequence of currently held keys, as they were pressed down and up.
*
* This is a [singleton root primitive](https://github.com/solidjs-community/solid-primitives/tree/main/packages/rootless#createSingletonRoot). *(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[][]
* })
* ```
*/
export const useKeyDownSequence = /*#__PURE__*/ createSingletonRoot(() => {
if (isServer) {
return () => [];
}
};
const handleSequenceWithoutReset = (sequence2) => {
const last = sequence2.at(-1);
if (!last)
return;
const e = event();
if (preventDefault && last.length < keys.length) {
if (arrayEquals(last, keys.slice(0, keys.length - 1))) {
e && e.preventDefault();
}
return;
const keys = useKeyDownList();
return createMemo(prev => {
if (keys().length === 0)
return [];
return [...prev, keys()];
}, []);
});
/**
* 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
* })
* ```
*/
export function createKeyHold(key, options = {}) {
if (isServer) {
return () => false;
}
if (arrayEquals(last, keys)) {
const prev = sequence2.at(-2);
if (!prev || arrayEquals(prev, keys.slice(0, keys.length - 1))) {
preventDefault && e && e.preventDefault();
callback(e);
}
key = key.toUpperCase();
const { preventDefault = true } = options, event = useKeyDownEvent(), heldKey = useCurrentlyHeldKey();
return createMemo(() => heldKey() === key && (preventDefault && event()?.preventDefault(), true));
}
/**
* 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");
* });
* ```
*/
export function createShortcut(keys, callback, options = {}) {
if (isServer || !keys.length) {
return;
}
};
createEffect(
on(sequence, options.requireReset ? handleSequenceWithReset : handleSequenceWithoutReset)
);
keys = keys.map(key => key.toUpperCase());
const { preventDefault = true } = options, event = useKeyDownEvent(), sequence = useKeyDownSequence();
let reset = false;
// allow to check the sequence only once the user has released all keys
const handleSequenceWithReset = (sequence) => {
if (!sequence.length)
return (reset = false);
if (reset)
return;
const e = event();
if (sequence.length < keys.length) {
// optimistically preventDefault behavior if we yet don't have enough keys
if (equalsKeyHoldSequence(sequence, keys.slice(0, sequence.length))) {
preventDefault && e && e.preventDefault();
}
else {
reset = true;
}
}
else {
reset = true;
if (equalsKeyHoldSequence(sequence, keys)) {
preventDefault && e && e.preventDefault();
callback(e);
}
}
};
// allow checking the sequence even if the user is still holding down keys
const handleSequenceWithoutReset = (sequence) => {
const last = sequence.at(-1);
if (!last)
return;
const e = event();
// optimistically preventDefault behavior if we yet don't have enough keys
if (preventDefault && last.length < keys.length) {
if (arrayEquals(last, keys.slice(0, keys.length - 1))) {
e && e.preventDefault();
}
return;
}
if (arrayEquals(last, keys)) {
const prev = sequence.at(-2);
if (!prev || arrayEquals(prev, keys.slice(0, keys.length - 1))) {
preventDefault && e && e.preventDefault();
callback(e);
}
}
};
createEffect(on(sequence, options.requireReset ? handleSequenceWithReset : handleSequenceWithoutReset));
}
export { createKeyHold, createShortcut, useCurrentlyHeldKey, useKeyDownEvent, useKeyDownList, useKeyDownSequence };
{
"name": "@solid-primitives/keyboard",
"version": "1.2.8",
"version": "1.3.0",
"description": "A library of reactive promitives helping handling user's keyboard input.",

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

"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.js",

@@ -47,15 +46,12 @@ "types": "./dist/index.d.ts",

"exports": {
"@solid-primitives/source": "./src/index.ts",
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
},
"dependencies": {
"@solid-primitives/event-listener": "^2.3.3",
"@solid-primitives/utils": "^6.2.3",
"@solid-primitives/rootless": "^1.4.5"
"@solid-primitives/event-listener": "^2.4.0",
"@solid-primitives/rootless": "^1.5.0",
"@solid-primitives/utils": "^6.3.0"
},

@@ -62,0 +58,0 @@ "peerDependencies": {

@@ -14,8 +14,8 @@ <p>

- [`useKeyDownEvent`](#useKeyDownEvent) — Provides a signal with the last keydown event.
- [`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.
- [`useKeyDownEvent`](#usekeydownevent) — Provides a signal with the last keydown event.
- [`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.

@@ -22,0 +22,0 @@ ## Installation

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