@tanstack/hotkeys 0.10.1 → 0.11.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/README.md +3 -0
- package/dist/adapter.d.ts +27 -0
- package/dist/adapter.js +109 -0
- package/dist/constants.d.ts +1 -1
- package/dist/hotkey-recorder.d.ts +2 -1
- package/dist/hotkey-recorder.js +12 -4
- package/dist/hotkey-sequence-recorder.d.ts +2 -1
- package/dist/hotkey-sequence-recorder.js +13 -8
- package/dist/index.d.ts +3 -3
- package/dist/parse.d.ts +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -67,6 +67,9 @@ Type-safe keyboard shortcuts for the web. Template-string bindings, parsed objec
|
|
|
67
67
|
> [!NOTE]
|
|
68
68
|
> You may know **TanStack Hotkeys** by our adapter names, too!
|
|
69
69
|
>
|
|
70
|
+
> - [**Alpine Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/alpine/quick-start)
|
|
71
|
+
> - [**Ember Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/ember/quick-start)
|
|
72
|
+
> - [**Octane Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/octane/quick-start)
|
|
70
73
|
> - [**React Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/react/react-hotkeys)
|
|
71
74
|
> - [**Preact Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/preact/preact-hotkeys)
|
|
72
75
|
> - [**Solid Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/solid/reference)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { HotkeyCallback, RegisterableHotkey } from "./hotkey.types.js";
|
|
2
|
+
import { HotkeyOptions } from "./hotkey-manager.js";
|
|
3
|
+
import { HotkeySequence, SequenceOptions } from "./sequence-manager.js";
|
|
4
|
+
//#region src/adapter.d.ts
|
|
5
|
+
/** A hotkey and its callback, with optional per-binding options. */
|
|
6
|
+
export interface HotkeyDefinition {
|
|
7
|
+
hotkey: RegisterableHotkey;
|
|
8
|
+
callback: HotkeyCallback;
|
|
9
|
+
options?: HotkeyOptions;
|
|
10
|
+
}
|
|
11
|
+
/** A sequence and its callback, with optional per-binding options. */
|
|
12
|
+
export interface HotkeySequenceDefinition {
|
|
13
|
+
sequence: HotkeySequence;
|
|
14
|
+
callback: HotkeyCallback;
|
|
15
|
+
options?: SequenceOptions;
|
|
16
|
+
}
|
|
17
|
+
/** Reconciles adapter registrations while retaining handles for option updates. */
|
|
18
|
+
export declare function createHotkeyBindings(): {
|
|
19
|
+
update(definitions: Array<HotkeyDefinition>, commonOptions?: HotkeyOptions): void;
|
|
20
|
+
destroy: () => void;
|
|
21
|
+
};
|
|
22
|
+
/** Reconciles sequence bindings; empty sequences and explicit null targets are skipped. */
|
|
23
|
+
export declare function createHotkeySequenceBindings(): {
|
|
24
|
+
update(definitions: Array<HotkeySequenceDefinition>, commonOptions?: SequenceOptions): void;
|
|
25
|
+
destroy: () => void;
|
|
26
|
+
};
|
|
27
|
+
//#endregion
|
package/dist/adapter.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { detectPlatform } from "./platform.js";
|
|
2
|
+
import { normalizeRegisterableHotkey, parseHotkey } from "./parse.js";
|
|
3
|
+
import { defaultHotkeyOptions, getDefaultIgnoreInputs } from "./_registration.js";
|
|
4
|
+
import { getHotkeyManager } from "./hotkey-manager.js";
|
|
5
|
+
import { DEFAULT_SEQUENCE_TIMEOUT, getSequenceManager } from "./sequence-manager.js";
|
|
6
|
+
|
|
7
|
+
//#region src/adapter.ts
|
|
8
|
+
function createBindings() {
|
|
9
|
+
const registrations = /* @__PURE__ */ new Map();
|
|
10
|
+
return {
|
|
11
|
+
update(bindings) {
|
|
12
|
+
const keys = new Set(bindings.map((binding) => binding.key));
|
|
13
|
+
for (const [key, registration] of registrations) if (!keys.has(key)) {
|
|
14
|
+
registration.handle.unregister();
|
|
15
|
+
registrations.delete(key);
|
|
16
|
+
}
|
|
17
|
+
for (const binding of bindings) {
|
|
18
|
+
const previous = registrations.get(binding.key);
|
|
19
|
+
const { target, platform } = binding.options;
|
|
20
|
+
if (previous?.handle.isActive && previous.target === target && previous.platform === platform) {
|
|
21
|
+
previous.handle.callback = binding.callback;
|
|
22
|
+
const reset = Object.fromEntries(Object.keys(previous.options).map((key) => [key, void 0]));
|
|
23
|
+
previous.handle.setOptions({
|
|
24
|
+
...reset,
|
|
25
|
+
...binding.options
|
|
26
|
+
});
|
|
27
|
+
previous.options = binding.options;
|
|
28
|
+
} else {
|
|
29
|
+
previous?.handle.unregister();
|
|
30
|
+
registrations.set(binding.key, {
|
|
31
|
+
handle: binding.register(),
|
|
32
|
+
target,
|
|
33
|
+
platform,
|
|
34
|
+
options: binding.options
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
destroy() {
|
|
40
|
+
for (const { handle } of registrations.values()) handle.unregister();
|
|
41
|
+
registrations.clear();
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function resolveOptions(options) {
|
|
46
|
+
return {
|
|
47
|
+
...defaultHotkeyOptions,
|
|
48
|
+
requireReset: false,
|
|
49
|
+
...options,
|
|
50
|
+
ignoreInputs: options.ignoreInputs,
|
|
51
|
+
target: "target" in options ? options.target ?? null : typeof document === "undefined" ? null : document,
|
|
52
|
+
platform: options.platform ?? detectPlatform()
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/** Reconciles adapter registrations while retaining handles for option updates. */
|
|
56
|
+
function createHotkeyBindings() {
|
|
57
|
+
const bindings = createBindings();
|
|
58
|
+
return {
|
|
59
|
+
update(definitions, commonOptions = {}) {
|
|
60
|
+
const prepared = [];
|
|
61
|
+
definitions.forEach((definition, index) => {
|
|
62
|
+
const options = resolveOptions({
|
|
63
|
+
...commonOptions,
|
|
64
|
+
...definition.options
|
|
65
|
+
});
|
|
66
|
+
if (!options.target) return;
|
|
67
|
+
const hotkey = normalizeRegisterableHotkey(definition.hotkey, options.platform);
|
|
68
|
+
options.ignoreInputs ??= getDefaultIgnoreInputs(parseHotkey(hotkey, options.platform));
|
|
69
|
+
prepared.push({
|
|
70
|
+
key: `${index}:${hotkey}`,
|
|
71
|
+
callback: definition.callback,
|
|
72
|
+
options,
|
|
73
|
+
register: () => getHotkeyManager().register(hotkey, definition.callback, options)
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
bindings.update(prepared);
|
|
77
|
+
},
|
|
78
|
+
destroy: bindings.destroy
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/** Reconciles sequence bindings; empty sequences and explicit null targets are skipped. */
|
|
82
|
+
function createHotkeySequenceBindings() {
|
|
83
|
+
const bindings = createBindings();
|
|
84
|
+
return {
|
|
85
|
+
update(definitions, commonOptions = {}) {
|
|
86
|
+
const prepared = [];
|
|
87
|
+
definitions.forEach((definition, index) => {
|
|
88
|
+
const options = resolveOptions({
|
|
89
|
+
timeout: DEFAULT_SEQUENCE_TIMEOUT,
|
|
90
|
+
...commonOptions,
|
|
91
|
+
...definition.options
|
|
92
|
+
});
|
|
93
|
+
if (!options.target || definition.sequence.length === 0) return;
|
|
94
|
+
options.ignoreInputs ??= getDefaultIgnoreInputs(parseHotkey(definition.sequence[0], options.platform));
|
|
95
|
+
prepared.push({
|
|
96
|
+
key: `${index}:${JSON.stringify(definition.sequence)}`,
|
|
97
|
+
callback: definition.callback,
|
|
98
|
+
options,
|
|
99
|
+
register: () => getSequenceManager().register(definition.sequence, definition.callback, options)
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
bindings.update(prepared);
|
|
103
|
+
},
|
|
104
|
+
destroy: bindings.destroy
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
//#endregion
|
|
109
|
+
export { createHotkeyBindings, createHotkeySequenceBindings };
|
package/dist/constants.d.ts
CHANGED
|
@@ -118,7 +118,7 @@ export declare const PUNCTUATION_CODE_MAP: Record<string, string>;
|
|
|
118
118
|
* @see {@link EDITING_KEYS}
|
|
119
119
|
* @see {@link PUNCTUATION_KEYS}
|
|
120
120
|
*/
|
|
121
|
-
export declare const ALL_KEYS: Set<
|
|
121
|
+
export declare const ALL_KEYS: Set<"CapsLock" | "ContextMenu" | "Convert" | "KanaMode" | "NonConvert" | "Help" | "Insert" | "NumLock" | "Fn" | "FnLock" | "PrintScreen" | "ScrollLock" | "Pause" | "BrowserBack" | "BrowserFavorites" | "BrowserForward" | "BrowserHome" | "BrowserRefresh" | "BrowserSearch" | "BrowserStop" | "Eject" | "LaunchMail" | "MediaPlayPause" | "MediaStop" | "MediaTrackNext" | "MediaTrackPrevious" | "Power" | "AudioVolumeDown" | "AudioVolumeMute" | "AudioVolumeUp" | "WakeUp" | "Hyper" | "Super" | "Again" | "Copy" | "Cut" | "Find" | "Open" | "Paste" | "Props" | "Select" | "Undo" | "Hiragana" | "Katakana" | "Clear" | "Cancel" | "Standby" | "LaunchApplication1" | "LaunchApplication2" | "LaunchMediaPlayer" | "HangulMode" | "HanjaMode" | "Zenkaku" | "Hankaku" | "ZenkakuHankaku" | LetterKey | NumberKey | FunctionKey | NavigationKey | EditingKey | PunctuationKey>;
|
|
122
122
|
/**
|
|
123
123
|
* Normalizes a key name to its canonical form.
|
|
124
124
|
*
|
|
@@ -78,7 +78,8 @@ export declare class HotkeyRecorder {
|
|
|
78
78
|
* Use this to subscribe to state changes or access current state.
|
|
79
79
|
*/
|
|
80
80
|
readonly store: Store<HotkeyRecorderState>;
|
|
81
|
-
|
|
81
|
+
/** Options may be a getter to read current configuration during an active session. */
|
|
82
|
+
constructor(options: HotkeyRecorderOptions | (() => HotkeyRecorderOptions));
|
|
82
83
|
/**
|
|
83
84
|
* Updates the recorder options, including callbacks.
|
|
84
85
|
* This allows framework adapters to sync callback changes without recreating the recorder.
|
package/dist/hotkey-recorder.js
CHANGED
|
@@ -58,10 +58,18 @@ var HotkeyRecorder = class {
|
|
|
58
58
|
recordedHotkey: null
|
|
59
59
|
});
|
|
60
60
|
#keydownHandler = null;
|
|
61
|
-
#
|
|
61
|
+
#optionsSource;
|
|
62
|
+
#optionOverrides = {};
|
|
63
|
+
get #options() {
|
|
64
|
+
return {
|
|
65
|
+
...typeof this.#optionsSource === "function" ? this.#optionsSource() : this.#optionsSource,
|
|
66
|
+
...this.#optionOverrides
|
|
67
|
+
};
|
|
68
|
+
}
|
|
62
69
|
#platform;
|
|
70
|
+
/** Options may be a getter to read current configuration during an active session. */
|
|
63
71
|
constructor(options) {
|
|
64
|
-
this.#
|
|
72
|
+
this.#optionsSource = options;
|
|
65
73
|
this.#platform = detectPlatform();
|
|
66
74
|
}
|
|
67
75
|
/**
|
|
@@ -69,8 +77,8 @@ var HotkeyRecorder = class {
|
|
|
69
77
|
* This allows framework adapters to sync callback changes without recreating the recorder.
|
|
70
78
|
*/
|
|
71
79
|
setOptions(options) {
|
|
72
|
-
this.#
|
|
73
|
-
...this.#
|
|
80
|
+
this.#optionOverrides = {
|
|
81
|
+
...this.#optionOverrides,
|
|
74
82
|
...options
|
|
75
83
|
};
|
|
76
84
|
}
|
|
@@ -65,7 +65,8 @@ export interface HotkeySequenceRecorderOptions extends RecorderOptions {
|
|
|
65
65
|
export declare class HotkeySequenceRecorder {
|
|
66
66
|
#private;
|
|
67
67
|
readonly store: Store<HotkeySequenceRecorderState>;
|
|
68
|
-
|
|
68
|
+
/** Options may be a getter to read current configuration during an active session. */
|
|
69
|
+
constructor(options: HotkeySequenceRecorderOptions | (() => HotkeySequenceRecorderOptions));
|
|
69
70
|
/** Merges current callbacks and options without discarding recorded steps. */
|
|
70
71
|
setOptions(options: Partial<HotkeySequenceRecorderOptions>): void;
|
|
71
72
|
/** Starts a fresh recording; repeated starts during an active session are ignored. */
|
|
@@ -32,22 +32,27 @@ var HotkeySequenceRecorder = class {
|
|
|
32
32
|
recordedSequence: null
|
|
33
33
|
});
|
|
34
34
|
#keydownHandler = null;
|
|
35
|
-
#
|
|
35
|
+
#optionsSource;
|
|
36
|
+
#optionOverrides = {};
|
|
37
|
+
get #options() {
|
|
38
|
+
return {
|
|
39
|
+
...defaultHotkeySequenceRecorderOptions,
|
|
40
|
+
...typeof this.#optionsSource === "function" ? this.#optionsSource() : this.#optionsSource,
|
|
41
|
+
...this.#optionOverrides
|
|
42
|
+
};
|
|
43
|
+
}
|
|
36
44
|
#platform;
|
|
37
45
|
#events = [];
|
|
38
46
|
#idleTimer = null;
|
|
47
|
+
/** Options may be a getter to read current configuration during an active session. */
|
|
39
48
|
constructor(options) {
|
|
40
|
-
this.#
|
|
41
|
-
...defaultHotkeySequenceRecorderOptions,
|
|
42
|
-
...options
|
|
43
|
-
};
|
|
49
|
+
this.#optionsSource = options;
|
|
44
50
|
this.#platform = detectPlatform();
|
|
45
51
|
}
|
|
46
52
|
/** Merges current callbacks and options without discarding recorded steps. */
|
|
47
53
|
setOptions(options) {
|
|
48
|
-
this.#
|
|
49
|
-
...
|
|
50
|
-
...this.#options,
|
|
54
|
+
this.#optionOverrides = {
|
|
55
|
+
...this.#optionOverrides,
|
|
51
56
|
...options
|
|
52
57
|
};
|
|
53
58
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { CanonicalModifier, EditingKey, FunctionKey, IndividualKey, Key, LetterKey, LogicalKey, Modifier, NamedKey, NavigationKey, NonPunctuationKey, NormalizedKeyboardEvent, NumberKey, PhysicalKey, PhysicalKeyCode, PunctuationKey } from "./key.types.js";
|
|
2
|
+
import { ConflictBehavior, FourModifierHotkey, Hotkey, HotkeyCallback, HotkeyCallbackContext, HotkeyMeta, ParsedHotkey, ParsedModifiers, RawHotkey, RawModifiers, RegisterableHotkey, SingleModifierHotkey, ThreeModifierHotkey, TwoModifierHotkey } from "./hotkey.types.js";
|
|
3
|
+
import { HotkeyManager, HotkeyOptions, HotkeyRegistration, HotkeyRegistrationHandle, HotkeyRegistrationView, getHotkeyManager, toHotkeyRegistrationView } from "./hotkey-manager.js";
|
|
4
|
+
import { DEFAULT_SEQUENCE_TIMEOUT, HotkeySequence, SequenceManager, SequenceOptions, SequenceRegistration, SequenceRegistrationHandle, SequenceRegistrationView, Target, createSequenceMatcher, getSequenceManager } from "./sequence-manager.js";
|
|
2
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";
|
|
3
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";
|
|
4
7
|
import { detectPlatform, resolveModifier } from "./platform.js";
|
|
5
|
-
import { ConflictBehavior, FourModifierHotkey, Hotkey, HotkeyCallback, HotkeyCallbackContext, HotkeyMeta, ParsedHotkey, ParsedModifiers, RawHotkey, RawModifiers, RegisterableHotkey, SingleModifierHotkey, ThreeModifierHotkey, TwoModifierHotkey } from "./hotkey.types.js";
|
|
6
8
|
import { DisplayHotkey, FormatDisplayOptions, formatForDisplay, formatHotkey, formatHotkeySequence, formatWithLabels } from "./format.js";
|
|
7
9
|
import { hasNonModifierKey, isModifierKey, normalizeHotkey, normalizeHotkeyFromEvent, normalizeHotkeyFromParsed, normalizeRegisterableHotkey, parseHotkey, parseKeyboardEvent, parseRegisterableHotkey, rawHotkeyToParsedHotkey } from "./parse.js";
|
|
8
10
|
import { ValidationResult, assertValidHotkey, checkHotkey, validateHotkey } from "./validate.js";
|
|
9
11
|
import { CreateHotkeyHandlerOptions, KeyboardEventMatch, MultiHotkeyHandler, areHotkeysEqual, createHotkeyHandler, createMultiHotkeyHandler, matchesKeyboardEvent } from "./match.js";
|
|
10
12
|
import { HeldModifierOptions, matchesHeldModifiers } from "./hint.js";
|
|
11
|
-
import { HotkeyManager, HotkeyOptions, HotkeyRegistration, HotkeyRegistrationHandle, HotkeyRegistrationView, getHotkeyManager, toHotkeyRegistrationView } from "./hotkey-manager.js";
|
|
12
|
-
import { DEFAULT_SEQUENCE_TIMEOUT, HotkeySequence, SequenceManager, SequenceOptions, SequenceRegistration, SequenceRegistrationHandle, SequenceRegistrationView, Target, createSequenceMatcher, getSequenceManager } from "./sequence-manager.js";
|
|
13
13
|
import { HotkeyConflict, HotkeyConflictOptions, findHotkeyConflicts } from "./conflicts.js";
|
|
14
14
|
import { KeyStateTracker, KeyStateTrackerState, getKeyStateTracker } from "./key-state-tracker.js";
|
|
15
15
|
import { HotkeyRecorderValidationContext, HotkeySequenceRecorderValidationContext, RecorderKeyMode, RecorderOptions, RecorderRejection } from "./recorder-options.js";
|
package/dist/parse.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Key } from "./key.types.js";
|
|
2
|
-
import { MODIFIER_ALIASES } from "./constants.js";
|
|
3
2
|
import { Hotkey, ParsedHotkey, RawHotkey, RegisterableHotkey } from "./hotkey.types.js";
|
|
3
|
+
import { MODIFIER_ALIASES } from "./constants.js";
|
|
4
4
|
//#region src/parse.d.ts
|
|
5
5
|
/**
|
|
6
6
|
* Parses a hotkey string into its component parts.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/hotkeys",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Type-safe, framework-agnostic keyboard hotkey management for the browser",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"types": "./dist/index.d.ts",
|
|
28
28
|
"exports": {
|
|
29
29
|
".": "./dist/index.js",
|
|
30
|
+
"./adapter": "./dist/adapter.js",
|
|
30
31
|
"./package.json": "./package.json"
|
|
31
32
|
},
|
|
32
33
|
"sideEffects": false,
|
|
@@ -37,7 +38,7 @@
|
|
|
37
38
|
"dist"
|
|
38
39
|
],
|
|
39
40
|
"dependencies": {
|
|
40
|
-
"@tanstack/store": "^0.11.
|
|
41
|
+
"@tanstack/store": "^0.11.2"
|
|
41
42
|
},
|
|
42
43
|
"scripts": {
|
|
43
44
|
"clean": "premove ./build ./dist",
|