@tanstack/hotkeys 0.17.0 → 0.18.0
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/dist/_event-target.js +12 -1
- package/dist/app-hotkeys.js +2 -3
- package/dist/hotkey-manager.d.ts +29 -3
- package/dist/hotkey-manager.js +46 -6
- package/dist/index.d.ts +2 -2
- package/package.json +1 -1
- package/skills/hotkeys-core/SKILL.md +1 -1
- package/skills/hotkeys-core/references/scopes.md +2 -0
package/dist/_event-target.js
CHANGED
|
@@ -17,6 +17,17 @@ function getTargetDocument(target) {
|
|
|
17
17
|
return target.ownerDocument;
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
20
|
+
* Whether key presses reach a listener on `target` right now. Document and
|
|
21
|
+
* window listeners receive every key press in their document. An element
|
|
22
|
+
* receives them only while focus is inside it, including inside a shadow
|
|
23
|
+
* root it contains.
|
|
24
|
+
*/
|
|
25
|
+
function targetHasFocus(target) {
|
|
26
|
+
if (isWindow(target) || target.nodeType === 9) return true;
|
|
27
|
+
const active = target.getRootNode().activeElement;
|
|
28
|
+
return !!active && target.contains(active);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
20
31
|
* Checks if an element is an input-like element that should be ignored for hotkeys.
|
|
21
32
|
*
|
|
22
33
|
* This includes:
|
|
@@ -191,4 +202,4 @@ function isEventForTarget(event, target) {
|
|
|
191
202
|
}
|
|
192
203
|
|
|
193
204
|
//#endregion
|
|
194
|
-
export { getActiveElementForListenerTarget, getTargetDocument, isEventForTarget, isInputElement, isTypingEvent, shouldIgnoreInputEvent };
|
|
205
|
+
export { getActiveElementForListenerTarget, getTargetDocument, isEventForTarget, isInputElement, isTypingEvent, shouldIgnoreInputEvent, targetHasFocus };
|
package/dist/app-hotkeys.js
CHANGED
|
@@ -227,13 +227,12 @@ var AppHotkeysStore = class {
|
|
|
227
227
|
if (list) list.push(registration);
|
|
228
228
|
else byId.set(id, [registration]);
|
|
229
229
|
}
|
|
230
|
-
const manager = getHotkeyManager();
|
|
231
230
|
return Object.values(this.store.state.appHotkeys).map((appHotkey) => {
|
|
232
231
|
const registered = byId.get(appHotkey.id) ?? [];
|
|
233
|
-
const
|
|
232
|
+
const has = (status) => registered.some((r) => r.status === status);
|
|
234
233
|
return {
|
|
235
234
|
appHotkey,
|
|
236
|
-
status: appHotkey.hotkey === null ? "unbound" : registered.length === 0 ? "inactive" :
|
|
235
|
+
status: appHotkey.hotkey === null ? "unbound" : registered.length === 0 ? "inactive" : has("active") ? "active" : has("suspended") ? "suspended" : "disabled",
|
|
237
236
|
isCustom: isCustomHotkey(appHotkey, this.#platform),
|
|
238
237
|
registrations: registered
|
|
239
238
|
};
|
package/dist/hotkey-manager.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ConflictBehavior, HotkeyCallback, HotkeyCallbackContextFor, HotkeyChord, HotkeyMeta, HotkeySequence, ParsedHotkeyChord, RegisterableHotkey, RegisterableHotkeyChord, RegisterableHotkeySequence } from "./hotkey.types.js";
|
|
2
|
-
import { AppHotkeysStore } from "./app-hotkeys.js";
|
|
2
|
+
import { AppHotkey, AppHotkeysStore } from "./app-hotkeys.js";
|
|
3
3
|
import { Store } from "@tanstack/store";
|
|
4
4
|
//#region src/hotkey-manager.d.ts
|
|
5
5
|
/** An element, document, or window that hotkeys can listen on. */
|
|
@@ -41,9 +41,14 @@ export interface HotkeyOptions {
|
|
|
41
41
|
platform?: 'mac' | 'windows' | 'linux';
|
|
42
42
|
/**
|
|
43
43
|
* Prevent the default browser action when the hotkey matches. For a
|
|
44
|
-
* sequence, every step that advances it does. Defaults to true
|
|
44
|
+
* sequence, every step that advances it does. Defaults to true.
|
|
45
|
+
*
|
|
46
|
+
* `'always'` also prevents it while a chord can't fire because it is
|
|
47
|
+
* disabled or its scope is inactive or suspended, unless another hotkey
|
|
48
|
+
* handles the key. It never stops propagation for such a chord. Sequences
|
|
49
|
+
* treat `'always'` as `true`.
|
|
45
50
|
*/
|
|
46
|
-
preventDefault?: boolean;
|
|
51
|
+
preventDefault?: boolean | 'always';
|
|
47
52
|
/**
|
|
48
53
|
* Stop event propagation when the hotkey matches. For a sequence, only the
|
|
49
54
|
* last step does, so earlier steps reach other listeners. Defaults to true
|
|
@@ -118,8 +123,18 @@ export interface HotkeySequenceRegistration extends RegistrationBase<HotkeySeque
|
|
|
118
123
|
}
|
|
119
124
|
/** A registered hotkey in the HotkeyManager. */
|
|
120
125
|
export type HotkeyRegistration = HotkeyChordRegistration | HotkeySequenceRegistration;
|
|
126
|
+
/**
|
|
127
|
+
* Whether a registration is on. Focus isn't checked: a registration on an
|
|
128
|
+
* element also needs focus inside that element to fire.
|
|
129
|
+
* - `'active'`: enabled, and its scope is active
|
|
130
|
+
* - `'suspended'`: enabled, but its scope is inactive or suspended by an exclusive scope
|
|
131
|
+
* - `'disabled'`: registered with `enabled: false`
|
|
132
|
+
*/
|
|
133
|
+
export type HotkeyRegistrationStatus = 'active' | 'suspended' | 'disabled';
|
|
121
134
|
interface RegistrationViewBase {
|
|
122
135
|
id: string;
|
|
136
|
+
/** Whether the registration is enabled and in an active scope. */
|
|
137
|
+
status: HotkeyRegistrationStatus;
|
|
123
138
|
target: Target;
|
|
124
139
|
triggerCount: number;
|
|
125
140
|
hasFired: boolean;
|
|
@@ -372,6 +387,17 @@ export declare class HotkeyManager {
|
|
|
372
387
|
* @returns True if a matching registration exists
|
|
373
388
|
*/
|
|
374
389
|
isRegistered(hotkey: RegisterableHotkey, target?: Target): boolean;
|
|
390
|
+
/**
|
|
391
|
+
* Checks whether a hotkey can fire now: at least one registration is
|
|
392
|
+
* enabled, in an active scope, and listening where key presses go. A
|
|
393
|
+
* registration on `document` or `window` always is; one on an element is
|
|
394
|
+
* only while focus is inside that element. An app hotkey matches
|
|
395
|
+
* registrations by id, so custom hotkeys apply.
|
|
396
|
+
*
|
|
397
|
+
* @param hotkey - A chord, sequence, or app hotkey
|
|
398
|
+
* @returns True if a registration that can fire exists
|
|
399
|
+
*/
|
|
400
|
+
hasActiveRegistration(hotkey: RegisterableHotkey | AppHotkey): boolean;
|
|
375
401
|
/**
|
|
376
402
|
* Destroys the manager and removes all listeners.
|
|
377
403
|
*/
|
package/dist/hotkey-manager.js
CHANGED
|
@@ -5,7 +5,7 @@ import { isHotkeySequence, isModifierKey, parseHotkey, simplifyHotkey } from "./
|
|
|
5
5
|
import { formatHotkey } from "./format.js";
|
|
6
6
|
import { matchKeyboardEvent, parsedChordsEqual } from "./_match.js";
|
|
7
7
|
import { DEFAULT_SEQUENCE_TIMEOUT, areHotkeysEqual } from "./match.js";
|
|
8
|
-
import { getTargetDocument, isEventForTarget, shouldIgnoreInputEvent } from "./_event-target.js";
|
|
8
|
+
import { getTargetDocument, isEventForTarget, shouldIgnoreInputEvent, targetHasFocus } from "./_event-target.js";
|
|
9
9
|
import { TargetListeners } from "./_target-listeners.js";
|
|
10
10
|
import { managerRef } from "./_manager-ref.js";
|
|
11
11
|
import { isRecordingEvent } from "./_recording-guard.js";
|
|
@@ -13,10 +13,16 @@ import { GLOBAL_SCOPE, defaultHotkeyOptions, getDefaultIgnoreInputs, handleConfl
|
|
|
13
13
|
import { Store } from "@tanstack/store";
|
|
14
14
|
|
|
15
15
|
//#region src/hotkey-manager.ts
|
|
16
|
+
/** Whether a registration can fire now. Without a manager, scopes don't apply. */
|
|
17
|
+
function getRegistrationStatus(options, manager) {
|
|
18
|
+
if (options.enabled === false) return "disabled";
|
|
19
|
+
return manager?.isScopeActive(options.scope) === false ? "suspended" : "active";
|
|
20
|
+
}
|
|
16
21
|
/** Creates a public view from an internal registration, stripping the callback. */
|
|
17
22
|
function toHotkeyRegistrationView(reg) {
|
|
18
23
|
const base = {
|
|
19
24
|
id: reg.id,
|
|
25
|
+
status: getRegistrationStatus(reg.options, managerRef.current),
|
|
20
26
|
target: reg.target,
|
|
21
27
|
triggerCount: reg.triggerCount,
|
|
22
28
|
hasFired: reg.hasFired
|
|
@@ -270,7 +276,20 @@ var HotkeyManager = class HotkeyManager {
|
|
|
270
276
|
}
|
|
271
277
|
/** Whether a registration can fire now: enabled, in an active scope. */
|
|
272
278
|
#isLive(registration) {
|
|
273
|
-
return registration.options
|
|
279
|
+
return getRegistrationStatus(registration.options, this) === "active";
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Whether a hotkey that can fire would handle this key on any listener the
|
|
283
|
+
* event reaches, including other targets and phases. A sequence counts when
|
|
284
|
+
* the key matches its next step or its first step.
|
|
285
|
+
*/
|
|
286
|
+
#handledByLiveHotkey(event, eventType) {
|
|
287
|
+
const path = event.composedPath();
|
|
288
|
+
for (const registration of this.registrations.state.values()) {
|
|
289
|
+
if (!this.#isLive(registration) || registration.options.eventType !== eventType || !path.includes(registration.target) || ignoresInputs(registration) && shouldIgnoreInputEvent(event, registration.target, registration.target)) continue;
|
|
290
|
+
if ((registration.kind === "chord" ? [registration.parsedChord] : [registration.parsedSequence[registration.currentIndex], registration.parsedSequence[0]]).some((step) => matchKeyboardEvent(event, step, registration.options.platform).matched)) return true;
|
|
291
|
+
}
|
|
292
|
+
return false;
|
|
274
293
|
}
|
|
275
294
|
/** Notifies subscribers after in-place updates such as counts and sequence progress. */
|
|
276
295
|
#notify() {
|
|
@@ -455,14 +474,20 @@ var HotkeyManager = class HotkeyManager {
|
|
|
455
474
|
if (isRecordingEvent(event) || this.#sequenceOwned.get(event)) return;
|
|
456
475
|
const matches = /* @__PURE__ */ new Map();
|
|
457
476
|
let bestScore = 0;
|
|
477
|
+
let reserved = false;
|
|
458
478
|
for (const registration of registrations) {
|
|
459
|
-
|
|
479
|
+
const live = this.#isLive(registration);
|
|
480
|
+
if (!live && registration.options.preventDefault !== "always" || registration.options.eventType !== eventType || !isEventForTarget(event, target) || ignoresInputs(registration) && shouldIgnoreInputEvent(event, target, registration.target)) continue;
|
|
460
481
|
const result = matchKeyboardEvent(event, registration.parsedChord, registration.options.platform);
|
|
461
|
-
if (result.matched)
|
|
462
|
-
|
|
463
|
-
|
|
482
|
+
if (!result.matched) continue;
|
|
483
|
+
if (!live) {
|
|
484
|
+
reserved = true;
|
|
485
|
+
continue;
|
|
464
486
|
}
|
|
487
|
+
matches.set(registration.id, result);
|
|
488
|
+
bestScore = Math.max(bestScore, result.score);
|
|
465
489
|
}
|
|
490
|
+
if (reserved && matches.size === 0 && !this.#handledByLiveHotkey(event, eventType)) event.preventDefault();
|
|
466
491
|
let bestRank = -1;
|
|
467
492
|
for (const registration of registrations) if (matches.get(registration.id)?.score === bestScore) bestRank = Math.max(bestRank, this.#scopeRank(registration.options.scope));
|
|
468
493
|
for (const snapshot of registrations) {
|
|
@@ -679,6 +704,21 @@ var HotkeyManager = class HotkeyManager {
|
|
|
679
704
|
return false;
|
|
680
705
|
}
|
|
681
706
|
/**
|
|
707
|
+
* Checks whether a hotkey can fire now: at least one registration is
|
|
708
|
+
* enabled, in an active scope, and listening where key presses go. A
|
|
709
|
+
* registration on `document` or `window` always is; one on an element is
|
|
710
|
+
* only while focus is inside that element. An app hotkey matches
|
|
711
|
+
* registrations by id, so custom hotkeys apply.
|
|
712
|
+
*
|
|
713
|
+
* @param hotkey - A chord, sequence, or app hotkey
|
|
714
|
+
* @returns True if a registration that can fire exists
|
|
715
|
+
*/
|
|
716
|
+
hasActiveRegistration(hotkey) {
|
|
717
|
+
const id = typeof hotkey === "object" && "~appHotkey" in hotkey ? hotkey.id : void 0;
|
|
718
|
+
for (const registration of this.registrations.state.values()) if (this.#isLive(registration) && targetHasFocus(registration.target) && (id === void 0 ? areHotkeysEqual(registration.hotkey, hotkey, registration.options.platform) : registration.options.appHotkeyId === id)) return true;
|
|
719
|
+
return false;
|
|
720
|
+
}
|
|
721
|
+
/**
|
|
682
722
|
* Destroys the manager and removes all listeners.
|
|
683
723
|
*/
|
|
684
724
|
destroy() {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CanonicalModifier, EditingKey, FunctionKey, IndividualKey, Key, LetterKey, LogicalKey, Modifier, NamedKey, NavigationKey, NonPunctuationKey, NormalizedKeyboardEvent, NumberKey, PhysicalKey, PhysicalKeyCode, PunctuationKey } from "./key.types.js";
|
|
2
2
|
import { ConflictBehavior, FourModifierHotkeyChord, Hotkey, HotkeyCallback, HotkeyCallbackContext, HotkeyCallbackContextFor, HotkeyChord, HotkeyChordCallbackContext, HotkeyMeta, HotkeySequence, HotkeySequenceCallbackContext, ParsedHotkeyChord, ParsedModifiers, RawHotkeyChord, RawModifiers, RegisterableHotkey, RegisterableHotkeyChord, RegisterableHotkeySequence, SingleModifierHotkeyChord, ThreeModifierHotkeyChord, TwoModifierHotkeyChord } from "./hotkey.types.js";
|
|
3
|
-
import { ActivateHotkeyScopeOptions, HotkeyChordOptions, HotkeyChordRegistration, HotkeyChordRegistrationView, HotkeyManager, HotkeyOptions, HotkeyOptionsFor, HotkeyRegistration, HotkeyRegistrationHandle, HotkeyRegistrationView, HotkeyScopesState, HotkeySequenceOptions, HotkeySequenceRegistration, HotkeySequenceRegistrationView, Target, getHotkeyManager, toHotkeyRegistrationView } from "./hotkey-manager.js";
|
|
3
|
+
import { ActivateHotkeyScopeOptions, HotkeyChordOptions, HotkeyChordRegistration, HotkeyChordRegistrationView, HotkeyManager, HotkeyOptions, HotkeyOptionsFor, HotkeyRegistration, HotkeyRegistrationHandle, HotkeyRegistrationStatus, HotkeyRegistrationView, HotkeyScopesState, HotkeySequenceOptions, HotkeySequenceRegistration, HotkeySequenceRegistrationView, Target, getHotkeyManager, toHotkeyRegistrationView } from "./hotkey-manager.js";
|
|
4
4
|
import { AppHotkey, AppHotkeyChordOptions, AppHotkeyDefaultKind, AppHotkeyId, AppHotkeyInput, AppHotkeyListItem, AppHotkeyOptions, AppHotkeySequenceOptions, AppHotkeyStatus, AppHotkeys, AppHotkeysState, AppHotkeysStore, AppHotkeysStoreOptions, CustomHotkeys, DefinedAppHotkeys, Register, RegisteredAppHotkeys, defineAppHotkeys, getAppHotkeyRegistrationOptions, isAppHotkey, isCustomHotkey, provideAppHotkeys, publishAppHotkeysStore, resolveAppHotkey } from "./app-hotkeys.js";
|
|
5
5
|
import { ALL_KEYS, EDITING_KEYS, FUNCTION_KEYS, LETTER_KEYS, MODIFIER_ALIASES, MODIFIER_KEYS, MODIFIER_ORDER, NAVIGATION_KEYS, NUMBER_KEYS, PUNCTUATION_CODE_MAP, PUNCTUATION_KEYS, isSingleLetterKey, normalizeKeyName } from "./constants.js";
|
|
6
6
|
import { KEY_DISPLAY_SYMBOLS, LINUX_MODIFIER_LABELS, MAC_MODIFIER_LABELS, MAC_MODIFIER_SYMBOLS, PUNCTUATION_KEY_DISPLAY_LABELS, WINDOWS_MODIFIER_LABELS } from "./display-labels.js";
|
|
@@ -13,4 +13,4 @@ import { HeldModifierOptions, matchesHeldModifiers } from "./hint.js";
|
|
|
13
13
|
import { HotkeyConflict, HotkeyConflictOptions, findHotkeyConflicts } from "./conflicts.js";
|
|
14
14
|
import { KeyStateTracker, KeyStateTrackerState, getKeyStateTracker } from "./key-state-tracker.js";
|
|
15
15
|
import { HotkeyChordRecorderOptions, HotkeyChordRecorderValidationContext, HotkeyRecorder, HotkeyRecorderCommitKeys, HotkeyRecorderOptions, HotkeyRecorderState, HotkeyRecorderValidationContext, HotkeySequenceRecorderOptions, HotkeySequenceRecorderValidationContext, RecorderKeyMode, RecorderOptions, RecorderRejection } from "./hotkey-recorder.js";
|
|
16
|
-
export { ALL_KEYS, ActivateHotkeyScopeOptions, AppHotkey, AppHotkeyChordOptions, AppHotkeyDefaultKind, AppHotkeyId, AppHotkeyInput, AppHotkeyListItem, AppHotkeyOptions, AppHotkeySequenceOptions, AppHotkeyStatus, AppHotkeys, AppHotkeysState, AppHotkeysStore, AppHotkeysStoreOptions, CanonicalModifier, ConflictBehavior, CreateHotkeyChordHandlerOptions, CustomHotkeys, DEFAULT_SEQUENCE_TIMEOUT, DefinedAppHotkeys, DisplayHotkey, DisplayHotkeyChord, EDITING_KEYS, EditingKey, FUNCTION_KEYS, FormatDisplayOptions, FourModifierHotkeyChord, FunctionKey, HeldModifierOptions, Hotkey, HotkeyCallback, HotkeyCallbackContext, HotkeyCallbackContextFor, HotkeyChord, HotkeyChordCallbackContext, HotkeyChordOptions, HotkeyChordRecorderOptions, HotkeyChordRecorderValidationContext, HotkeyChordRegistration, HotkeyChordRegistrationView, HotkeyConflict, HotkeyConflictOptions, HotkeyManager, HotkeyMeta, HotkeyOptions, HotkeyOptionsFor, HotkeyRecorder, HotkeyRecorderCommitKeys, HotkeyRecorderOptions, HotkeyRecorderState, HotkeyRecorderValidationContext, HotkeyRegistration, HotkeyRegistrationHandle, HotkeyRegistrationView, HotkeyScopesState, HotkeySequence, HotkeySequenceCallbackContext, HotkeySequenceOptions, HotkeySequenceRecorderOptions, HotkeySequenceRecorderValidationContext, HotkeySequenceRegistration, HotkeySequenceRegistrationView, IndividualKey, KEY_DISPLAY_SYMBOLS, Key, KeyStateTracker, KeyStateTrackerState, KeyboardEventMatch, LETTER_KEYS, LINUX_MODIFIER_LABELS, LetterKey, LogicalKey, MAC_MODIFIER_LABELS, MAC_MODIFIER_SYMBOLS, MODIFIER_ALIASES, MODIFIER_KEYS, MODIFIER_ORDER, Modifier, MultiHotkeyChordHandler, NAVIGATION_KEYS, NUMBER_KEYS, NamedKey, NavigationKey, NonPunctuationKey, NormalizedKeyboardEvent, NumberKey, PUNCTUATION_CODE_MAP, PUNCTUATION_KEYS, PUNCTUATION_KEY_DISPLAY_LABELS, ParsedHotkeyChord, ParsedModifiers, PhysicalKey, PhysicalKeyCode, PunctuationKey, RawHotkeyChord, RawModifiers, RecorderKeyMode, RecorderOptions, RecorderRejection, Register, RegisterableHotkey, RegisterableHotkeyChord, RegisterableHotkeySequence, RegisteredAppHotkeys, SingleModifierHotkeyChord, Target, ThreeModifierHotkeyChord, TwoModifierHotkeyChord, ValidatableHotkey, ValidationResult, WINDOWS_MODIFIER_LABELS, areHotkeysEqual, assertValidHotkey, checkHotkey, createHotkeyChordHandler, createMultiHotkeyChordHandler, createSequenceMatcher, defineAppHotkeys, detectPlatform, findHotkeyConflicts, formatForDisplay, formatHotkey, getAppHotkeyRegistrationOptions, getHotkeyManager, getKeyStateTracker, hasNonModifierKey, isAppHotkey, isCustomHotkey, isHotkeySequence, isModifierKey, isSingleLetterKey, matchesHeldModifiers, matchesKeyboardEvent, normalizeHotkey, normalizeKeyName, parseHotkey, parseKeyboardEvent, provideAppHotkeys, publishAppHotkeysStore, resolveAppHotkey, resolveModifier, simplifyHotkey, toHotkeyRegistrationView, validateHotkey };
|
|
16
|
+
export { ALL_KEYS, ActivateHotkeyScopeOptions, AppHotkey, AppHotkeyChordOptions, AppHotkeyDefaultKind, AppHotkeyId, AppHotkeyInput, AppHotkeyListItem, AppHotkeyOptions, AppHotkeySequenceOptions, AppHotkeyStatus, AppHotkeys, AppHotkeysState, AppHotkeysStore, AppHotkeysStoreOptions, CanonicalModifier, ConflictBehavior, CreateHotkeyChordHandlerOptions, CustomHotkeys, DEFAULT_SEQUENCE_TIMEOUT, DefinedAppHotkeys, DisplayHotkey, DisplayHotkeyChord, EDITING_KEYS, EditingKey, FUNCTION_KEYS, FormatDisplayOptions, FourModifierHotkeyChord, FunctionKey, HeldModifierOptions, Hotkey, HotkeyCallback, HotkeyCallbackContext, HotkeyCallbackContextFor, HotkeyChord, HotkeyChordCallbackContext, HotkeyChordOptions, HotkeyChordRecorderOptions, HotkeyChordRecorderValidationContext, HotkeyChordRegistration, HotkeyChordRegistrationView, HotkeyConflict, HotkeyConflictOptions, HotkeyManager, HotkeyMeta, HotkeyOptions, HotkeyOptionsFor, HotkeyRecorder, HotkeyRecorderCommitKeys, HotkeyRecorderOptions, HotkeyRecorderState, HotkeyRecorderValidationContext, HotkeyRegistration, HotkeyRegistrationHandle, HotkeyRegistrationStatus, HotkeyRegistrationView, HotkeyScopesState, HotkeySequence, HotkeySequenceCallbackContext, HotkeySequenceOptions, HotkeySequenceRecorderOptions, HotkeySequenceRecorderValidationContext, HotkeySequenceRegistration, HotkeySequenceRegistrationView, IndividualKey, KEY_DISPLAY_SYMBOLS, Key, KeyStateTracker, KeyStateTrackerState, KeyboardEventMatch, LETTER_KEYS, LINUX_MODIFIER_LABELS, LetterKey, LogicalKey, MAC_MODIFIER_LABELS, MAC_MODIFIER_SYMBOLS, MODIFIER_ALIASES, MODIFIER_KEYS, MODIFIER_ORDER, Modifier, MultiHotkeyChordHandler, NAVIGATION_KEYS, NUMBER_KEYS, NamedKey, NavigationKey, NonPunctuationKey, NormalizedKeyboardEvent, NumberKey, PUNCTUATION_CODE_MAP, PUNCTUATION_KEYS, PUNCTUATION_KEY_DISPLAY_LABELS, ParsedHotkeyChord, ParsedModifiers, PhysicalKey, PhysicalKeyCode, PunctuationKey, RawHotkeyChord, RawModifiers, RecorderKeyMode, RecorderOptions, RecorderRejection, Register, RegisterableHotkey, RegisterableHotkeyChord, RegisterableHotkeySequence, RegisteredAppHotkeys, SingleModifierHotkeyChord, Target, ThreeModifierHotkeyChord, TwoModifierHotkeyChord, ValidatableHotkey, ValidationResult, WINDOWS_MODIFIER_LABELS, areHotkeysEqual, assertValidHotkey, checkHotkey, createHotkeyChordHandler, createMultiHotkeyChordHandler, createSequenceMatcher, defineAppHotkeys, detectPlatform, findHotkeyConflicts, formatForDisplay, formatHotkey, getAppHotkeyRegistrationOptions, getHotkeyManager, getKeyStateTracker, hasNonModifierKey, isAppHotkey, isCustomHotkey, isHotkeySequence, isModifierKey, isSingleLetterKey, matchesHeldModifiers, matchesKeyboardEvent, normalizeHotkey, normalizeKeyName, parseHotkey, parseKeyboardEvent, provideAppHotkeys, publishAppHotkeysStore, resolveAppHotkey, resolveModifier, simplifyHotkey, toHotkeyRegistrationView, validateHotkey };
|
package/package.json
CHANGED
|
@@ -56,7 +56,7 @@ When nesting scopes, stacking dialogs, or keeping some hotkeys working during an
|
|
|
56
56
|
|
|
57
57
|
## Pitfalls
|
|
58
58
|
|
|
59
|
-
- **A disabled hotkey doesn't block the browser.** With `enabled: false`, `Mod+S` opens the Save Page dialog.
|
|
59
|
+
- **A disabled hotkey doesn't block the browser.** With `enabled: false`, `Mod+S` opens the Save Page dialog. Add `preventDefault: 'always'` to keep the action blocked while the hotkey can't fire, whether it's disabled or its scope is off. Skip it for hotkeys turned off to give the key back to the browser, such as `Mod+C`, `Mod+Z`, `Tab`, or single keys behind a user's off switch. Don't keep the hotkey enabled and return early from the callback; the hotkey would also stay `active` in app hotkey lists and show its hint.
|
|
60
60
|
- **Modifiers match exactly.** `ArrowDown` never fires while Shift is held, so register `Shift+ArrowDown` instead of checking `event.shiftKey`. Shift that a symbol needs is the exception: `?` matches Shift+/.
|
|
61
61
|
- **Chords repeat while held.** Set `requireReset: true` on toggles, or holding the keys flickers them.
|
|
62
62
|
- **Letters follow the typed character.** For position-based controls such as WASD, use physical bindings such as `'[KeyW]'`. Keep letters for mnemonic commands such as `Mod+S`.
|
|
@@ -9,3 +9,5 @@
|
|
|
9
9
|
- A hotkey's `scope` option, or an app hotkey's declared `scope`, overrides the provider's.
|
|
10
10
|
- Scopes follow the component tree, not the DOM, so a portaled dialog keeps its scope.
|
|
11
11
|
- The hotkey that opens an exclusive dialog is suspended with the rest of the page. To close the dialog with the same keys, register that hotkey inside the dialog too.
|
|
12
|
+
- To show which scope owns the keyboard, use the adapter's scope state API, such as `useHotkeyScopesState()` in React. Don't subscribe to `manager.scopes` by hand.
|
|
13
|
+
- To list only hotkeys that are on, filter the adapter's registrations by `status === 'active'`. `'suspended'` means the hotkey's scope is inactive or suspended, and `'disabled'` means `enabled: false`.
|