@solid-primitives/keyboard 2.0.0-next.0 → 2.0.0-next.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +41 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -156,6 +156,7 @@ Creates a keyboard shortcut observer. The provided callback will be called when
|
|
|
156
156
|
- `options` — additional configuration:
|
|
157
157
|
- `preventDefault` — call `e.preventDefault()` on the keyboard event, when the specified key is pressed. _(Defaults to `true`)_
|
|
158
158
|
- `requireReset` — If `true`, the shortcut will only be triggered once until all of the keys stop being pressed. Disabled by default.
|
|
159
|
+
- `ignoreWithinInputs` — If `true`, the shortcut is ignored while focus is on an `input`, `textarea`, `select`, or `contenteditable` element, so it doesn't interrupt typing. Disabled by default.
|
|
159
160
|
|
|
160
161
|
```tsx
|
|
161
162
|
import { createShortcut } from "@solid-primitives/keyboard";
|
|
@@ -175,6 +176,17 @@ When `preventDefault` is `true`, `e.preventDefault()` will be called not only on
|
|
|
175
176
|
|
|
176
177
|
E.g. when listening for `Control + Shift + A`, all three keydown events will be prevented.
|
|
177
178
|
|
|
179
|
+
### Ignoring shortcuts while typing
|
|
180
|
+
|
|
181
|
+
Single, unmodified-key shortcuts (e.g. `["S"]`) conflict with typing — pressing "s" in a text field would both type the character and trigger the shortcut. Set `ignoreWithinInputs: true` to skip the shortcut entirely while focus is on a form control or `contenteditable` element:
|
|
182
|
+
|
|
183
|
+
```tsx
|
|
184
|
+
// won't fire while the user is typing in a text field
|
|
185
|
+
createShortcut(["S"], () => console.log("S was pressed"), { ignoreWithinInputs: true });
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Combos that include a modifier (e.g. `Control + S`) don't have this problem, since the modifier itself prevents a character from being typed — `ignoreWithinInputs` is usually unnecessary for those.
|
|
189
|
+
|
|
178
190
|
## `createKeyDown`
|
|
179
191
|
|
|
180
192
|
Listens for a `keydown` event for a specific key on a document. Useful for global keyboard handlers that need to respond to a single key without setting up a full shortcut.
|
package/dist/index.d.ts
CHANGED
|
@@ -131,17 +131,25 @@ export declare function createKeyHold(key: KbdKey, options?: {
|
|
|
131
131
|
* @param options The options for the shortcut.
|
|
132
132
|
* - `preventDefault` — Controls if the keydown event should have its default action prevented. Enabled by default.
|
|
133
133
|
* - `requireReset` — If `true`, the shortcut will only be triggered once until all of the keys stop being pressed. Disabled by default.
|
|
134
|
+
* - `ignoreWithinInputs` — If `true`, the shortcut is ignored while focus is on an `input`, `textarea`, `select`,
|
|
135
|
+
* or `contenteditable` element, so typing isn't interrupted. Disabled by default — enable it for shortcuts
|
|
136
|
+
* made of plain, unmodified keys (e.g. a single letter); combos like `Control+S` are usually fine either way,
|
|
137
|
+
* since the modifier prevents a character from being typed.
|
|
134
138
|
*
|
|
135
139
|
* @example
|
|
136
140
|
* ```ts
|
|
137
141
|
* createShortcut(["CONTROL", "SHIFT", "C"], () => {
|
|
138
142
|
* console.log("Ctrl+Shift+C was pressed");
|
|
139
143
|
* });
|
|
144
|
+
*
|
|
145
|
+
* // won't fire while typing in a text field
|
|
146
|
+
* createShortcut(["S"], () => console.log("S was pressed"), { ignoreWithinInputs: true });
|
|
140
147
|
* ```
|
|
141
148
|
*/
|
|
142
149
|
export declare function createShortcut(keys: KbdKey[], callback: (event: KeyboardEvent | null) => void, options?: {
|
|
143
150
|
preventDefault?: boolean;
|
|
144
151
|
requireReset?: boolean;
|
|
152
|
+
ignoreWithinInputs?: boolean;
|
|
145
153
|
}): void;
|
|
146
154
|
export interface CreateKeyDownOptions {
|
|
147
155
|
/** Whether the listener should be inactive. */
|
package/dist/index.js
CHANGED
|
@@ -11,6 +11,19 @@ function equalsKeyHoldSequence(sequence, model) {
|
|
|
11
11
|
}
|
|
12
12
|
return true;
|
|
13
13
|
}
|
|
14
|
+
/** Is the event target an editable form control, or inside a `contenteditable` element? */
|
|
15
|
+
function isEditableTarget(target) {
|
|
16
|
+
if (!(target instanceof HTMLElement))
|
|
17
|
+
return false;
|
|
18
|
+
switch (target.tagName) {
|
|
19
|
+
case "INPUT":
|
|
20
|
+
case "TEXTAREA":
|
|
21
|
+
case "SELECT":
|
|
22
|
+
return true;
|
|
23
|
+
default:
|
|
24
|
+
return target.isContentEditable;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
14
27
|
/**
|
|
15
28
|
* Provides a signal with the last keydown event.
|
|
16
29
|
*
|
|
@@ -108,7 +121,16 @@ export const useKeyDownList = /*#__PURE__*/ createSingletonRoot(() => {
|
|
|
108
121
|
if (typeof e.key !== "string")
|
|
109
122
|
return;
|
|
110
123
|
const key = e.key.toUpperCase();
|
|
111
|
-
|
|
124
|
+
// macOS never fires keyup for other keys held down together with Meta —
|
|
125
|
+
// only Meta's own keyup arrives — so its release must clear everything,
|
|
126
|
+
// or the other keys' stale state corrupts the next press.
|
|
127
|
+
// See https://github.com/solidjs-community/solid-primitives/issues/269
|
|
128
|
+
if (key === "META") {
|
|
129
|
+
reset();
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
setPressedKeys(prev => prev.filter(_key => _key !== key));
|
|
133
|
+
}
|
|
112
134
|
});
|
|
113
135
|
makeEventListener(window, "blur", reset);
|
|
114
136
|
makeEventListener(window, "contextmenu", e => {
|
|
@@ -235,12 +257,19 @@ export function createKeyHold(key, options = {}) {
|
|
|
235
257
|
* @param options The options for the shortcut.
|
|
236
258
|
* - `preventDefault` — Controls if the keydown event should have its default action prevented. Enabled by default.
|
|
237
259
|
* - `requireReset` — If `true`, the shortcut will only be triggered once until all of the keys stop being pressed. Disabled by default.
|
|
260
|
+
* - `ignoreWithinInputs` — If `true`, the shortcut is ignored while focus is on an `input`, `textarea`, `select`,
|
|
261
|
+
* or `contenteditable` element, so typing isn't interrupted. Disabled by default — enable it for shortcuts
|
|
262
|
+
* made of plain, unmodified keys (e.g. a single letter); combos like `Control+S` are usually fine either way,
|
|
263
|
+
* since the modifier prevents a character from being typed.
|
|
238
264
|
*
|
|
239
265
|
* @example
|
|
240
266
|
* ```ts
|
|
241
267
|
* createShortcut(["CONTROL", "SHIFT", "C"], () => {
|
|
242
268
|
* console.log("Ctrl+Shift+C was pressed");
|
|
243
269
|
* });
|
|
270
|
+
*
|
|
271
|
+
* // won't fire while typing in a text field
|
|
272
|
+
* createShortcut(["S"], () => console.log("S was pressed"), { ignoreWithinInputs: true });
|
|
244
273
|
* ```
|
|
245
274
|
*/
|
|
246
275
|
export function createShortcut(keys, callback, options = {}) {
|
|
@@ -248,7 +277,7 @@ export function createShortcut(keys, callback, options = {}) {
|
|
|
248
277
|
return;
|
|
249
278
|
}
|
|
250
279
|
keys = keys.map(key => key.toUpperCase());
|
|
251
|
-
const { preventDefault = true, requireReset = false } = options;
|
|
280
|
+
const { preventDefault = true, requireReset = false, ignoreWithinInputs = false } = options;
|
|
252
281
|
// Track pressed keys and sequence locally with plain JS state rather than
|
|
253
282
|
// reactive signals. A signal reads from event listeners return
|
|
254
283
|
// the pre-batch committed value, so synchronous shortcut checking requires
|
|
@@ -264,6 +293,8 @@ export function createShortcut(keys, callback, options = {}) {
|
|
|
264
293
|
makeEventListener(window, "keydown", (e) => {
|
|
265
294
|
if (e.repeat || typeof e.key !== "string")
|
|
266
295
|
return;
|
|
296
|
+
if (ignoreWithinInputs && isEditableTarget(e.target))
|
|
297
|
+
return;
|
|
267
298
|
const key = e.key.toUpperCase();
|
|
268
299
|
if (!pressedKeys.includes(key)) {
|
|
269
300
|
const newKeys = [...pressedKeys];
|
|
@@ -328,6 +359,14 @@ export function createShortcut(keys, callback, options = {}) {
|
|
|
328
359
|
if (typeof e.key !== "string")
|
|
329
360
|
return;
|
|
330
361
|
const key = e.key.toUpperCase();
|
|
362
|
+
// macOS never fires keyup for other keys held down together with Meta —
|
|
363
|
+
// only Meta's own keyup arrives. Treat it as a signal that every other
|
|
364
|
+
// tracked key must have been released too, or their stale state corrupts
|
|
365
|
+
// the next press (see https://github.com/solidjs-community/solid-primitives/issues/269).
|
|
366
|
+
if (key === "META") {
|
|
367
|
+
resetAll();
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
331
370
|
pressedKeys = pressedKeys.filter(k => k !== key);
|
|
332
371
|
if (pressedKeys.length === 0) {
|
|
333
372
|
sequence = [];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solid-primitives/keyboard",
|
|
3
|
-
"version": "2.0.0-next.
|
|
3
|
+
"version": "2.0.0-next.2",
|
|
4
4
|
"description": "A library of reactive promitives helping handling user's keyboard input.",
|
|
5
5
|
"author": "Damian Tarnwski <gthetarnav@gmail.com>",
|
|
6
6
|
"contributors": [],
|