@inklu/keys 2.1.0 → 3.0.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 +106 -135
- package/dist/attributes.d.mts +1 -1
- package/dist/attributes.mjs +15 -18
- package/dist/index-BnB38jCF.d.mts +139 -0
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +2 -2
- package/dist/react.d.mts +19 -41
- package/dist/react.mjs +26 -145
- package/dist/src-CGKvYKjd.mjs +1333 -0
- package/dist/types-BxMI3b4a.d.mts +256 -0
- package/package.json +2 -3
- package/dist/index-3d9f54ln.d.mts +0 -156
- package/dist/src-B76YvObL.mjs +0 -1687
- package/dist/types-QEXldPnL.d.mts +0 -398
package/dist/react.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { C as
|
|
2
|
-
import { a as
|
|
1
|
+
import { C as Chord, S as SerializedKeymap, T as Platform, _ as RunBlockReason, a as CommandDefinition, b as SequenceBinding, c as CommandSource, d as Conflict, f as ConflictOptions, g as PlatformBindings, h as KeymapStore, i as CommandContext, l as Commands, m as KeyCapture, n as BindingInput, o as CommandHandler, p as Json, r as Command, s as CommandKeymap, t as Binding, u as CommandsOptions, v as RunOptions, w as Key, x as SerializedBinding, y as ScopeOptions } from "./types-BxMI3b4a.mjs";
|
|
2
|
+
import { a as Rebinder, c as importKeymap, d as bindingTokens, f as formatBinding, i as RebindState, l as webStorage, m as seq, n as createCommands, o as createRebinder, p as isSingleKeyBinding, r as RebindOptions, s as exportKeymap, t as CommandId, u as ariaKeyShortcuts } from "./index-BnB38jCF.mjs";
|
|
3
3
|
//#region src/react/hooks.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* Mounts a commands registry for the lifecycle of the component.
|
|
@@ -13,26 +13,27 @@ import { a as RebindOptions, c as createRebinder, d as formatBinding, f as spoke
|
|
|
13
13
|
* ```
|
|
14
14
|
*/
|
|
15
15
|
export declare function useMountCommands<Id extends string>(registry: Commands<Id>): void;
|
|
16
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* Returns all registered commands, re-rendering when the registry changes.
|
|
18
|
+
* Hydrates against `serverList()`, so a subtree that hydrates late still
|
|
19
|
+
* matches the server's HTML.
|
|
20
|
+
*/
|
|
17
21
|
export declare function useCommands<Id extends string>(registry: Commands<Id>): ReadonlyArray<Command<Id>>;
|
|
18
|
-
/** Returns
|
|
22
|
+
/** Returns one command's snapshot, re-rendering only when that command changes. */
|
|
19
23
|
export declare function useCommand<Id extends string>(registry: Commands<Id>, id: NoInfer<Id>): Command<Id> | undefined;
|
|
20
24
|
/**
|
|
21
|
-
* Returns
|
|
22
|
-
*
|
|
23
|
-
* (chords only), and `spoken` for screen-reader text such as `"G then I"`.
|
|
25
|
+
* Returns a command's primary binding for people: `display` to read,
|
|
26
|
+
* `tokens` for `<kbd>` elements, and `aria` for `aria-keyshortcuts` (chords only).
|
|
24
27
|
*/
|
|
25
28
|
export declare function useShortcut<Id extends string>(registry: Commands<Id>, id: NoInfer<Id>): {
|
|
26
29
|
display: string;
|
|
27
30
|
tokens: ReadonlyArray<string>;
|
|
28
31
|
aria: string;
|
|
29
|
-
spoken: string;
|
|
30
32
|
};
|
|
31
33
|
/**
|
|
32
34
|
* Activates a scope for the lifecycle of the component. Scopes rank in the
|
|
33
|
-
* order
|
|
34
|
-
*
|
|
35
|
-
* the same render the child is pushed first.
|
|
35
|
+
* order React runs effects in: of a parent and a child opening in the same
|
|
36
|
+
* render, the child is pushed first.
|
|
36
37
|
*
|
|
37
38
|
* @example
|
|
38
39
|
* ```tsx
|
|
@@ -41,12 +42,10 @@ export declare function useShortcut<Id extends string>(registry: Commands<Id>, i
|
|
|
41
42
|
*/
|
|
42
43
|
export declare function useScope<Id extends string>(registry: Commands<Id>, name: string, active?: boolean, options?: ScopeOptions): void;
|
|
43
44
|
/**
|
|
44
|
-
* Attaches what a command does for the lifecycle of the component
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
* The handler attached last wins, and React attaches a parent's after its
|
|
49
|
-
* children's when they mount together.
|
|
45
|
+
* Attaches what a command does for the lifecycle of the component. The latest
|
|
46
|
+
* `run` is always the one called, and the handler attached last wins. While
|
|
47
|
+
* `enabled` is `false` the command falls to the handler attached before it,
|
|
48
|
+
* or cannot run if there is none.
|
|
50
49
|
*
|
|
51
50
|
* @example
|
|
52
51
|
* ```tsx
|
|
@@ -64,35 +63,14 @@ interface UseRebindResult<Id extends string = string> extends RebindState<Id> {
|
|
|
64
63
|
/** Whether the shortcut has been customized by the user. */
|
|
65
64
|
overridden: boolean;
|
|
66
65
|
start: () => void;
|
|
67
|
-
stop: () => void;
|
|
68
66
|
cancel: () => void;
|
|
69
67
|
commit: () => void;
|
|
68
|
+
/** Unbinds the command. */
|
|
70
69
|
clear: () => void;
|
|
70
|
+
/** Restores the default shortcut. */
|
|
71
71
|
reset: () => void;
|
|
72
72
|
}
|
|
73
73
|
/** Hook for recording and configuring a command's keybindings. */
|
|
74
74
|
export declare function useRebind<Id extends string>(registry: Commands<Id>, id: NoInfer<Id>, options?: RebindOptions<Id>): UseRebindResult<Id>;
|
|
75
|
-
/** Returns an array of keys currently held down. */
|
|
76
|
-
export declare function useHeldKeys(): ReadonlyArray<string>;
|
|
77
|
-
/**
|
|
78
|
-
* Checks whether a specific key is currently held down.
|
|
79
|
-
*
|
|
80
|
-
* @example
|
|
81
|
-
* ```tsx
|
|
82
|
-
* const shift = useKeyHold("Shift");
|
|
83
|
-
* ```
|
|
84
|
-
*/
|
|
85
|
-
export declare function useKeyHold(key: string): boolean;
|
|
86
|
-
/** Returns a map of held key identifiers to physical event codes. */
|
|
87
|
-
export declare function useHeldKeyCodes(): Readonly<Record<string, string>>;
|
|
88
|
-
/**
|
|
89
|
-
* Returns currently active partially-typed sequences.
|
|
90
|
-
*
|
|
91
|
-
* @example
|
|
92
|
-
* ```tsx
|
|
93
|
-
* const pending = usePendingSequences(commands);
|
|
94
|
-
* ```
|
|
95
|
-
*/
|
|
96
|
-
export declare function usePendingSequences<Id extends string>(registry: Commands<Id>): ReadonlyArray<PendingSequence<Id>>;
|
|
97
75
|
//#endregion
|
|
98
|
-
export { type Binding, type BindingInput, type
|
|
76
|
+
export { type Binding, type BindingInput, type Chord, type Command, type CommandContext, type CommandDefinition, type CommandHandler, type CommandId, type CommandKeymap, type CommandSource, type Commands, type CommandsOptions, type Conflict, type ConflictOptions, type Json, type Key, type KeyCapture, type KeymapStore, type Platform, type PlatformBindings, type RebindOptions, type RebindState, type Rebinder, type RunBlockReason, type RunOptions, type ScopeOptions, type SequenceBinding, type SerializedBinding, type SerializedKeymap, type UseRebindResult, ariaKeyShortcuts, bindingTokens, createCommands, createRebinder, exportKeymap, formatBinding, importKeymap, isSingleKeyBinding, seq, webStorage };
|
package/dist/react.mjs
CHANGED
|
@@ -1,81 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { a as
|
|
2
|
+
import { a as webStorage, c as formatBinding, i as importKeymap, l as isSingleKeyBinding, n as createRebinder, o as ariaKeyShortcuts, r as exportKeymap, s as bindingTokens, t as createCommands, u as seq } from "./src-CGKvYKjd.mjs";
|
|
3
3
|
import { useCallback, useEffect, useMemo, useRef, useSyncExternalStore } from "react";
|
|
4
|
-
//#region src/held.ts
|
|
5
|
-
/** Which keys are down right now. Listens only while something is subscribed. */
|
|
6
|
-
const NONE = {
|
|
7
|
-
keys: [],
|
|
8
|
-
codes: {}
|
|
9
|
-
};
|
|
10
|
-
/** Held keys by physical key, so two keys producing one character stay apart. */
|
|
11
|
-
const down = /* @__PURE__ */ new Map();
|
|
12
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
13
|
-
let state = NONE;
|
|
14
|
-
const identify = (event) => event.code || `key:${keyName(event.key)}`;
|
|
15
|
-
function publish() {
|
|
16
|
-
const entries = [...down.values()];
|
|
17
|
-
state = entries.length === 0 ? NONE : {
|
|
18
|
-
keys: [...new Set(entries.map((entry) => entry.key))],
|
|
19
|
-
codes: Object.fromEntries(entries.map((entry) => [entry.key, entry.code]))
|
|
20
|
-
};
|
|
21
|
-
for (const listener of listeners) listener();
|
|
22
|
-
}
|
|
23
|
-
function onKeyDown(event) {
|
|
24
|
-
if (!event.key || down.has(identify(event))) return;
|
|
25
|
-
down.set(identify(event), {
|
|
26
|
-
key: keyName(event.key),
|
|
27
|
-
code: event.code
|
|
28
|
-
});
|
|
29
|
-
publish();
|
|
30
|
-
}
|
|
31
|
-
function onKeyUp(event) {
|
|
32
|
-
down.delete(identify(event));
|
|
33
|
-
if (event.key === "Meta") {
|
|
34
|
-
for (const [identity, entry] of down) if (!isModifierKey(entry.key)) down.delete(identity);
|
|
35
|
-
}
|
|
36
|
-
publish();
|
|
37
|
-
}
|
|
38
|
-
/** Leaving the window swallows the keyups, so nothing is held any more. */
|
|
39
|
-
function onBlur() {
|
|
40
|
-
if (down.size === 0) return;
|
|
41
|
-
down.clear();
|
|
42
|
-
publish();
|
|
43
|
-
}
|
|
44
|
-
/** The keys held right now. Empty unless something is subscribed. */
|
|
45
|
-
function heldKeys() {
|
|
46
|
-
return state;
|
|
47
|
-
}
|
|
48
|
-
/** Whether `key` is held, by the name `KeyboardEvent.key` gives it. */
|
|
49
|
-
function isHeld(key) {
|
|
50
|
-
return state.keys.includes(keyName(key));
|
|
51
|
-
}
|
|
52
|
-
/** Watches held keys until the returned function is called. */
|
|
53
|
-
function subscribeHeldKeys(listener) {
|
|
54
|
-
listeners.add(listener);
|
|
55
|
-
if (listeners.size === 1) {
|
|
56
|
-
document.addEventListener("keydown", onKeyDown, true);
|
|
57
|
-
document.addEventListener("keyup", onKeyUp, true);
|
|
58
|
-
window.addEventListener("blur", onBlur);
|
|
59
|
-
}
|
|
60
|
-
return () => {
|
|
61
|
-
listeners.delete(listener);
|
|
62
|
-
if (listeners.size > 0) return;
|
|
63
|
-
document.removeEventListener("keydown", onKeyDown, true);
|
|
64
|
-
document.removeEventListener("keyup", onKeyUp, true);
|
|
65
|
-
window.removeEventListener("blur", onBlur);
|
|
66
|
-
down.clear();
|
|
67
|
-
state = NONE;
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
//#endregion
|
|
71
4
|
//#region src/react/hooks.ts
|
|
72
5
|
const EMPTY_TOKENS = [];
|
|
73
|
-
const EMPTY_CODES = {};
|
|
74
|
-
const EMPTY_PENDING = [];
|
|
75
|
-
const getEmptyTokens = () => EMPTY_TOKENS;
|
|
76
|
-
const getEmptyCodes = () => EMPTY_CODES;
|
|
77
|
-
const getEmptyPending = () => EMPTY_PENDING;
|
|
78
|
-
const getFalse = () => false;
|
|
79
6
|
/**
|
|
80
7
|
* Mounts a commands registry for the lifecycle of the component.
|
|
81
8
|
*
|
|
@@ -90,40 +17,40 @@ const getFalse = () => false;
|
|
|
90
17
|
function useMountCommands(registry) {
|
|
91
18
|
useEffect(() => registry.mount(), [registry]);
|
|
92
19
|
}
|
|
93
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Returns all registered commands, re-rendering when the registry changes.
|
|
22
|
+
* Hydrates against `serverList()`, so a subtree that hydrates late still
|
|
23
|
+
* matches the server's HTML.
|
|
24
|
+
*/
|
|
25
|
+
function useCommands(registry) {
|
|
94
26
|
const subscribe = useCallback((onChange) => registry.subscribe(onChange), [registry]);
|
|
95
27
|
const getSnapshot = useCallback(() => registry.list(), [registry]);
|
|
96
28
|
const getServerSnapshot = useCallback(() => registry.serverList(), [registry]);
|
|
97
29
|
return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|
98
30
|
}
|
|
99
|
-
/** Returns
|
|
100
|
-
function useCommands(registry) {
|
|
101
|
-
return useCommandList(registry);
|
|
102
|
-
}
|
|
103
|
-
/** Returns a specific command snapshot by ID. */
|
|
31
|
+
/** Returns one command's snapshot, re-rendering only when that command changes. */
|
|
104
32
|
function useCommand(registry, id) {
|
|
105
|
-
const
|
|
106
|
-
|
|
33
|
+
const subscribe = useCallback((onChange) => registry.subscribe(onChange), [registry]);
|
|
34
|
+
const getSnapshot = useCallback(() => registry.get(id), [registry, id]);
|
|
35
|
+
const getServerSnapshot = useCallback(() => registry.serverList().find((command) => command.id === id), [registry, id]);
|
|
36
|
+
return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|
107
37
|
}
|
|
108
38
|
/**
|
|
109
|
-
* Returns
|
|
110
|
-
*
|
|
111
|
-
* (chords only), and `spoken` for screen-reader text such as `"G then I"`.
|
|
39
|
+
* Returns a command's primary binding for people: `display` to read,
|
|
40
|
+
* `tokens` for `<kbd>` elements, and `aria` for `aria-keyshortcuts` (chords only).
|
|
112
41
|
*/
|
|
113
42
|
function useShortcut(registry, id) {
|
|
114
43
|
const command = useCommand(registry, id);
|
|
115
44
|
return useMemo(() => ({
|
|
116
45
|
display: command?.display[0] ?? "",
|
|
117
46
|
tokens: command?.tokens[0] ?? EMPTY_TOKENS,
|
|
118
|
-
aria: command?.aria ?? ""
|
|
119
|
-
spoken: command?.spoken ?? ""
|
|
47
|
+
aria: command?.aria ?? ""
|
|
120
48
|
}), [command]);
|
|
121
49
|
}
|
|
122
50
|
/**
|
|
123
51
|
* Activates a scope for the lifecycle of the component. Scopes rank in the
|
|
124
|
-
* order
|
|
125
|
-
*
|
|
126
|
-
* the same render the child is pushed first.
|
|
52
|
+
* order React runs effects in: of a parent and a child opening in the same
|
|
53
|
+
* render, the child is pushed first.
|
|
127
54
|
*
|
|
128
55
|
* @example
|
|
129
56
|
* ```tsx
|
|
@@ -143,12 +70,10 @@ function useScope(registry, name, active = true, options = {}) {
|
|
|
143
70
|
]);
|
|
144
71
|
}
|
|
145
72
|
/**
|
|
146
|
-
* Attaches what a command does for the lifecycle of the component
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
* The handler attached last wins, and React attaches a parent's after its
|
|
151
|
-
* children's when they mount together.
|
|
73
|
+
* Attaches what a command does for the lifecycle of the component. The latest
|
|
74
|
+
* `run` is always the one called, and the handler attached last wins. While
|
|
75
|
+
* `enabled` is `false` the command falls to the handler attached before it,
|
|
76
|
+
* or cannot run if there is none.
|
|
152
77
|
*
|
|
153
78
|
* @example
|
|
154
79
|
* ```tsx
|
|
@@ -172,31 +97,21 @@ function useCommandHandler(registry, id, run, enabled = true) {
|
|
|
172
97
|
function useRebind(registry, id, options = {}) {
|
|
173
98
|
const latest = useRef(options);
|
|
174
99
|
latest.current = options;
|
|
175
|
-
const
|
|
100
|
+
const sequence = options.sequence === true;
|
|
176
101
|
const rebinder = useMemo(() => createRebinder(registry, id, {
|
|
177
|
-
|
|
102
|
+
sequence,
|
|
178
103
|
get apply() {
|
|
179
104
|
return latest.current.apply;
|
|
180
105
|
},
|
|
181
|
-
get add() {
|
|
182
|
-
return latest.current.add;
|
|
183
|
-
},
|
|
184
106
|
get rejectConflicts() {
|
|
185
107
|
return latest.current.rejectConflicts;
|
|
186
108
|
},
|
|
187
|
-
get ignoreInputs() {
|
|
188
|
-
return latest.current.ignoreInputs;
|
|
189
|
-
},
|
|
190
|
-
get idleTimeout() {
|
|
191
|
-
return latest.current.idleTimeout;
|
|
192
|
-
},
|
|
193
109
|
onRecord: (binding, conflicts) => latest.current.onRecord?.(binding, conflicts),
|
|
194
|
-
onCancel: () => latest.current.onCancel?.()
|
|
195
|
-
onClear: () => latest.current.onClear?.()
|
|
110
|
+
onCancel: () => latest.current.onCancel?.()
|
|
196
111
|
}), [
|
|
197
112
|
registry,
|
|
198
113
|
id,
|
|
199
|
-
|
|
114
|
+
sequence
|
|
200
115
|
]);
|
|
201
116
|
useEffect(() => rebinder.destroy, [rebinder]);
|
|
202
117
|
const getState = useCallback(() => rebinder.state, [rebinder]);
|
|
@@ -208,7 +123,6 @@ function useRebind(registry, id, options = {}) {
|
|
|
208
123
|
tokens: command?.tokens[0] ?? EMPTY_TOKENS,
|
|
209
124
|
overridden: command?.overridden ?? false,
|
|
210
125
|
start: rebinder.start,
|
|
211
|
-
stop: rebinder.stop,
|
|
212
126
|
cancel: rebinder.cancel,
|
|
213
127
|
commit: rebinder.commit,
|
|
214
128
|
clear: () => registry.keymap.set(id, null),
|
|
@@ -221,38 +135,5 @@ function useRebind(registry, id, options = {}) {
|
|
|
221
135
|
id
|
|
222
136
|
]);
|
|
223
137
|
}
|
|
224
|
-
/** Returns an array of keys currently held down. */
|
|
225
|
-
function useHeldKeys() {
|
|
226
|
-
return useSyncExternalStore(subscribeHeldKeys, () => heldKeys().keys, getEmptyTokens);
|
|
227
|
-
}
|
|
228
|
-
/**
|
|
229
|
-
* Checks whether a specific key is currently held down.
|
|
230
|
-
*
|
|
231
|
-
* @example
|
|
232
|
-
* ```tsx
|
|
233
|
-
* const shift = useKeyHold("Shift");
|
|
234
|
-
* ```
|
|
235
|
-
*/
|
|
236
|
-
function useKeyHold(key) {
|
|
237
|
-
const getSnapshot = useCallback(() => isHeld(key), [key]);
|
|
238
|
-
return useSyncExternalStore(subscribeHeldKeys, getSnapshot, getFalse);
|
|
239
|
-
}
|
|
240
|
-
/** Returns a map of held key identifiers to physical event codes. */
|
|
241
|
-
function useHeldKeyCodes() {
|
|
242
|
-
return useSyncExternalStore(subscribeHeldKeys, () => heldKeys().codes, getEmptyCodes);
|
|
243
|
-
}
|
|
244
|
-
/**
|
|
245
|
-
* Returns currently active partially-typed sequences.
|
|
246
|
-
*
|
|
247
|
-
* @example
|
|
248
|
-
* ```tsx
|
|
249
|
-
* const pending = usePendingSequences(commands);
|
|
250
|
-
* ```
|
|
251
|
-
*/
|
|
252
|
-
function usePendingSequences(registry) {
|
|
253
|
-
const subscribe = useCallback((onChange) => registry.subscribePending(onChange), [registry]);
|
|
254
|
-
const getSnapshot = useCallback(() => registry.pending(), [registry]);
|
|
255
|
-
return useSyncExternalStore(subscribe, getSnapshot, getEmptyPending);
|
|
256
|
-
}
|
|
257
138
|
//#endregion
|
|
258
|
-
export { ariaKeyShortcuts, bindingTokens, createCommands, createRebinder, formatBinding,
|
|
139
|
+
export { ariaKeyShortcuts, bindingTokens, createCommands, createRebinder, exportKeymap, formatBinding, importKeymap, isSingleKeyBinding, seq, useCommand, useCommandHandler, useCommands, useMountCommands, useRebind, useScope, useShortcut, webStorage };
|