@solid-primitives/keyboard
Advanced tools
Comparing version 1.1.0 to 1.2.0
@@ -6,2 +6,31 @@ import { Accessor } from 'solid-js'; | ||
/** | ||
* 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 | ||
* } | ||
* }) | ||
* ``` | ||
*/ | ||
declare const useKeyDownEvent: () => Accessor<KeyboardEvent | null>; | ||
/** | ||
* Provides a signal with the list of currently held keys, ordered from least recent to most recent. | ||
@@ -14,5 +43,5 @@ * | ||
* @returns | ||
* Returns a signal of a list of keys, and a signal of last keydown event. | ||
* Returns a signal of a list of keys | ||
* ```ts | ||
* [keys: Accessor<string[]>, other: { event: Accessor<KeyboardEvent | null> }] | ||
* Accessor<string[]> | ||
* ``` | ||
@@ -22,3 +51,3 @@ * | ||
* ```ts | ||
* const [keys] = useKeyDownList(); | ||
* const keys = useKeyDownList(); | ||
* createEffect(() => { | ||
@@ -29,5 +58,3 @@ * console.log(keys()) // => ["ALT", "CONTROL", "Q", "A"] | ||
*/ | ||
declare const useKeyDownList: () => [keys: Accessor<string[]>, other: { | ||
event: Accessor<KeyboardEvent | null>; | ||
}]; | ||
declare const useKeyDownList: () => Accessor<string[]>; | ||
/** | ||
@@ -119,3 +146,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 function createShortcut(keys: KbdKey[], callback: (event: KeyboardEvent) => void, options?: { | ||
declare function createShortcut(keys: KbdKey[], callback: (event: KeyboardEvent | null) => void, options?: { | ||
preventDefault?: boolean; | ||
@@ -125,2 +152,2 @@ requireReset?: boolean; | ||
export { KbdKey, ModifierKey, createKeyHold, createShortcut, useCurrentlyHeldKey, useKeyDownList, useKeyDownSequence }; | ||
export { KbdKey, ModifierKey, createKeyHold, createShortcut, useCurrentlyHeldKey, useKeyDownEvent, useKeyDownList, useKeyDownSequence }; |
import { makeEventListener } from '@solid-primitives/event-listener'; | ||
import { createSingletonRoot } from '@solid-primitives/rootless'; | ||
import { arrayEquals } from '@solid-primitives/utils'; | ||
import { createSignal, batch, untrack, createMemo, createEffect, on } from 'solid-js'; | ||
import { createSignal, untrack, createMemo, createEffect, on } from 'solid-js'; | ||
import { isServer } from 'solid-js/web'; | ||
@@ -16,19 +16,45 @@ | ||
} | ||
var useKeyDownEvent = /* @__PURE__ */ createSingletonRoot( | ||
() => { | ||
if (isServer) { | ||
return () => null; | ||
} | ||
const [event, setEvent] = createSignal(null); | ||
makeEventListener(window, "keydown", (e) => { | ||
setEvent(e); | ||
setTimeout(() => setEvent(null)); | ||
}); | ||
return event; | ||
} | ||
); | ||
var useKeyDownList = /* @__PURE__ */ createSingletonRoot(() => { | ||
if (isServer) { | ||
return [() => [], { event: () => null }]; | ||
const keys = () => []; | ||
keys[0] = keys; | ||
keys[1] = { event: () => null }; | ||
keys[Symbol.iterator] = function* () { | ||
yield keys[0]; | ||
yield keys[1]; | ||
}; | ||
return keys; | ||
} | ||
const [pressedKeys, setPressedKeys] = createSignal([]); | ||
const [event, setEvent] = createSignal(null); | ||
const reset = () => setPressedKeys([]); | ||
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(); | ||
if (pressedKeys().includes(key)) | ||
const key = e.key.toUpperCase(), currentKeys = pressedKeys(); | ||
if (currentKeys.includes(key)) | ||
return; | ||
batch(() => { | ||
setEvent(e); | ||
setPressedKeys((prev) => [...prev, key]); | ||
}); | ||
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); | ||
}); | ||
@@ -45,3 +71,9 @@ makeEventListener(window, "keyup", (e) => { | ||
}); | ||
return [pressedKeys, { event }]; | ||
pressedKeys[0] = pressedKeys; | ||
pressedKeys[1] = { event }; | ||
pressedKeys[Symbol.iterator] = function* () { | ||
yield pressedKeys[0]; | ||
yield pressedKeys[1]; | ||
}; | ||
return pressedKeys; | ||
}); | ||
@@ -53,3 +85,3 @@ var useCurrentlyHeldKey = /* @__PURE__ */ createSingletonRoot( | ||
} | ||
const [keys] = useKeyDownList(); | ||
const keys = useKeyDownList(); | ||
let prevKeys = untrack(keys); | ||
@@ -70,3 +102,3 @@ return createMemo(() => { | ||
} | ||
const [keys] = useKeyDownList(); | ||
const keys = useKeyDownList(); | ||
return createMemo((prev) => { | ||
@@ -83,12 +115,4 @@ if (keys().length === 0) | ||
key = key.toUpperCase(); | ||
const { preventDefault = true } = options; | ||
const [, { event }] = useKeyDownList(); | ||
const heldKey = useCurrentlyHeldKey(); | ||
return createMemo(() => { | ||
if (heldKey() === key) { | ||
preventDefault && event().preventDefault(); | ||
return true; | ||
} | ||
return false; | ||
}); | ||
const { preventDefault = true } = options, event = useKeyDownEvent(), heldKey = useCurrentlyHeldKey(); | ||
return createMemo(() => heldKey() === key && (preventDefault && event()?.preventDefault(), true)); | ||
} | ||
@@ -100,5 +124,3 @@ function createShortcut(keys, callback, options = {}) { | ||
keys = keys.map((key) => key.toUpperCase()); | ||
const { preventDefault = true, requireReset = false } = options; | ||
const [, { event }] = useKeyDownList(); | ||
const sequence = useKeyDownSequence(); | ||
const { preventDefault = true } = options, event = useKeyDownEvent(), sequence = useKeyDownSequence(); | ||
let reset = false; | ||
@@ -113,3 +135,3 @@ const handleSequenceWithReset = (sequence2) => { | ||
if (equalsKeyHoldSequence(sequence2, keys.slice(0, sequence2.length))) { | ||
preventDefault && e.preventDefault(); | ||
preventDefault && e && e.preventDefault(); | ||
} else { | ||
@@ -121,3 +143,3 @@ reset = true; | ||
if (equalsKeyHoldSequence(sequence2, keys)) { | ||
preventDefault && e.preventDefault(); | ||
preventDefault && e && e.preventDefault(); | ||
callback(e); | ||
@@ -134,3 +156,3 @@ } | ||
if (arrayEquals(last, keys.slice(0, keys.length - 1))) { | ||
e.preventDefault(); | ||
e && e.preventDefault(); | ||
} | ||
@@ -142,3 +164,3 @@ return; | ||
if (!prev || arrayEquals(prev, keys.slice(0, keys.length - 1))) { | ||
preventDefault && e.preventDefault(); | ||
preventDefault && e && e.preventDefault(); | ||
callback(e); | ||
@@ -148,5 +170,7 @@ } | ||
}; | ||
createEffect(on(sequence, requireReset ? handleSequenceWithReset : handleSequenceWithoutReset)); | ||
createEffect( | ||
on(sequence, options.requireReset ? handleSequenceWithReset : handleSequenceWithoutReset) | ||
); | ||
} | ||
export { createKeyHold, createShortcut, useCurrentlyHeldKey, useKeyDownList, useKeyDownSequence }; | ||
export { createKeyHold, createShortcut, useCurrentlyHeldKey, useKeyDownEvent, useKeyDownList, useKeyDownSequence }; |
{ | ||
"name": "@solid-primitives/keyboard", | ||
"version": "1.1.0", | ||
"version": "1.2.0", | ||
"description": "A library of reactive promitives helping handling user's keyboard input.", | ||
@@ -55,3 +55,3 @@ "author": "Damian Tarnwski <gthetarnav@gmail.com>", | ||
"@solid-primitives/rootless": "^1.3.2", | ||
"@solid-primitives/utils": "^6.0.0" | ||
"@solid-primitives/utils": "^6.1.0" | ||
}, | ||
@@ -58,0 +58,0 @@ "peerDependencies": { |
@@ -14,2 +14,3 @@ <p> | ||
- [`useKeyDownEvent`](#useKeyDownEvent) — Provides a signal with the last keydown event. | ||
- [`useKeyDownList`](#useKeyDownList) — Provides a signal with the list of currently held keys | ||
@@ -26,5 +27,33 @@ - [`useCurrentlyHeldKey`](#useCurrentlyHeldKey) — Provides a signal with the currently held single key. | ||
# or | ||
pnpm add @solid-primitives/keyboard | ||
# or | ||
yarn add @solid-primitives/keyboard | ||
``` | ||
## `useKeyDownEvent` | ||
Provides a signal with the last keydown event. | ||
This is a [singleton root](https://github.com/solidjs-community/solid-primitives/tree/main/packages/rootless#createSingletonRoot) primitive that will reuse event listeners and signals across dependents. | ||
### How to use it | ||
`useKeyDownEvent` takes no arguments, and returns a signal with the last keydown event. | ||
```tsx | ||
import { useKeyDownEvent } from "@solid-primitives/keyboard"; | ||
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 | ||
} | ||
}); | ||
``` | ||
## `useKeyDownList` | ||
@@ -38,3 +67,3 @@ | ||
`useKeyDownList` takes no arguments, and returns a signal with the list of currently held keys, and last keydown event. | ||
`useKeyDownList` takes no arguments, and returns a signal with the list of currently held keys | ||
@@ -44,7 +73,6 @@ ```tsx | ||
const [keys, { event }] = useKeyDownList(); | ||
const keys = useKeyDownList(); | ||
createEffect(() => { | ||
console.log(keys()); // => string[] — list of currently held keys | ||
console.log(event()); // => KeyboardEvent | null — last keydown event | ||
}); | ||
@@ -51,0 +79,0 @@ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
24871
473
187