foldkit 0.161.0 → 0.162.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/subscription/{keyboardShortcuts.d.ts → keyBindings.d.ts} +23 -23
- package/dist/subscription/keyBindings.d.ts.map +1 -0
- package/dist/subscription/{keyboardShortcuts.js → keyBindings.js} +42 -43
- package/dist/subscription/public.d.ts +2 -2
- package/dist/subscription/public.d.ts.map +1 -1
- package/dist/subscription/public.js +1 -1
- package/package.json +1 -1
- package/dist/subscription/keyboardShortcuts.d.ts.map +0 -1
|
@@ -1,41 +1,41 @@
|
|
|
1
1
|
import { Duration, Stream } from 'effect';
|
|
2
|
-
/** Whether a
|
|
2
|
+
/** Whether a key binding may fire when its event comes from an editable element. */
|
|
3
3
|
export type WhileTyping = 'Suppress' | 'Allow';
|
|
4
4
|
/** A single key press or a sequence of two or more key presses. */
|
|
5
|
-
export type
|
|
5
|
+
export type KeySequence = string | Readonly<[string, string, ...Array<string>]>;
|
|
6
6
|
type BindingBase<Message> = Readonly<{
|
|
7
|
-
|
|
7
|
+
keys: KeySequence;
|
|
8
8
|
toMessage: (event: KeyboardEvent) => Message;
|
|
9
9
|
isEnabled?: boolean;
|
|
10
10
|
whileTyping?: WhileTyping;
|
|
11
11
|
preventDefault?: boolean;
|
|
12
12
|
}>;
|
|
13
13
|
/**
|
|
14
|
-
* One entry in a {@link
|
|
14
|
+
* One entry in a {@link keyBindings} binding table.
|
|
15
15
|
*
|
|
16
16
|
* A string describes one key press, such as `'/'`, `'Escape'`, or `'Mod+K'`.
|
|
17
17
|
* An array describes a sequence of at least two presses, such as
|
|
18
18
|
* `['G', 'H']` or `['G', 'Shift+G']`.
|
|
19
19
|
*/
|
|
20
|
-
export type
|
|
21
|
-
|
|
20
|
+
export type KeyBinding<Message> = BindingBase<Message> & (Readonly<{
|
|
21
|
+
keys: string;
|
|
22
22
|
whenRepeated?: 'Ignore' | 'Allow';
|
|
23
23
|
}> | Readonly<{
|
|
24
|
-
|
|
24
|
+
keys: Readonly<[string, string, ...Array<string>]>;
|
|
25
25
|
whenRepeated?: never;
|
|
26
26
|
}>);
|
|
27
27
|
type ModKey = 'Control' | 'Meta';
|
|
28
|
-
/** Configuration for the {@link
|
|
29
|
-
export type
|
|
30
|
-
bindings: ReadonlyArray<
|
|
28
|
+
/** Configuration for the {@link keyBindings} Stream helper. */
|
|
29
|
+
export type KeyBindingsConfig<Message> = Readonly<{
|
|
30
|
+
bindings: ReadonlyArray<KeyBinding<Message>>;
|
|
31
31
|
target?: EventTarget | (() => EventTarget);
|
|
32
32
|
modKey?: ModKey;
|
|
33
33
|
sequenceTimeout?: Duration.Input;
|
|
34
34
|
}>;
|
|
35
35
|
/**
|
|
36
|
-
* Build a Stream that turns declarative
|
|
36
|
+
* Build a Stream that turns declarative key bindings into Messages.
|
|
37
37
|
*
|
|
38
|
-
* A string
|
|
38
|
+
* A string describes one key press. Modifiers are joined with `+`:
|
|
39
39
|
* `'Mod+K'`, `'Control+Shift+P'`, or `'Alt+ArrowDown'`. The supported modifiers
|
|
40
40
|
* are `Mod`, `Control`, `Meta`, `Alt`, and `Shift`. `Mod` resolves to Meta on
|
|
41
41
|
* Apple platforms and Control elsewhere; `modKey` can override that choice.
|
|
@@ -47,17 +47,17 @@ export type KeyboardShortcutsConfig<Message> = Readonly<{
|
|
|
47
47
|
* one second by default; `sequenceTimeout` accepts any Effect Duration input.
|
|
48
48
|
* Modifier-only events and repeated keydowns do not advance a sequence.
|
|
49
49
|
*
|
|
50
|
-
*
|
|
50
|
+
* Bindings are suppressed by default when the event's composed path contains
|
|
51
51
|
* an `input`, `textarea`, `select`, or contenteditable element. Set
|
|
52
52
|
* `whileTyping` to `'Allow'` for a binding that must work there. Events emitted
|
|
53
53
|
* during IME composition are always ignored. Repeated keydowns are ignored for
|
|
54
|
-
* one-press
|
|
54
|
+
* one-press bindings unless `whenRepeated` is `'Allow'`. An event another
|
|
55
55
|
* handler already canceled is ignored and clears any sequence in progress.
|
|
56
56
|
*
|
|
57
57
|
* Matched key presses call `preventDefault()` before dispatching. For a
|
|
58
58
|
* sequence, that policy applies to every matched press. Set `preventDefault`
|
|
59
59
|
* to `false` to opt out. Sequences sharing a prefix must use the same policy.
|
|
60
|
-
* Duplicate bindings and a complete
|
|
60
|
+
* Duplicate bindings and a complete binding that is also a sequence prefix
|
|
61
61
|
* are rejected when the Stream is created.
|
|
62
62
|
*
|
|
63
63
|
* This helper returns a Stream, not a complete Subscription entry. Use
|
|
@@ -66,7 +66,7 @@ export type KeyboardShortcutsConfig<Message> = Readonly<{
|
|
|
66
66
|
* derive each binding's `isEnabled` from the dependency record. A dependency
|
|
67
67
|
* change opens a new Stream scope and resets any sequence in progress. If a
|
|
68
68
|
* parent owns a condition for a lifted child, declare the table at that parent
|
|
69
|
-
* or put
|
|
69
|
+
* or put bindings with different parent-owned lifetimes in separate child
|
|
70
70
|
* entries so `Subscription.lift` can gate them individually. If the meaning
|
|
71
71
|
* of a key depends on the Model, dispatch a factual key Message and decide
|
|
72
72
|
* what it means in update instead of reading the Model from `toMessage`.
|
|
@@ -74,28 +74,28 @@ export type KeyboardShortcutsConfig<Message> = Readonly<{
|
|
|
74
74
|
* @example
|
|
75
75
|
* ```typescript
|
|
76
76
|
* const subscriptions = Subscription.make<Model, Message>()(entry => ({
|
|
77
|
-
*
|
|
77
|
+
* keyBindings: entry(
|
|
78
78
|
* { isPaletteOpen: Schema.Boolean },
|
|
79
79
|
* {
|
|
80
80
|
* modelToDependencies: model => ({
|
|
81
81
|
* isPaletteOpen: model.paletteState._tag === 'Open',
|
|
82
82
|
* }),
|
|
83
83
|
* dependenciesToStream: ({ isPaletteOpen }) =>
|
|
84
|
-
* Subscription.
|
|
84
|
+
* Subscription.keyBindings<Message>({
|
|
85
85
|
* bindings: [
|
|
86
86
|
* {
|
|
87
|
-
*
|
|
87
|
+
* keys: 'Escape',
|
|
88
88
|
* isEnabled: isPaletteOpen,
|
|
89
89
|
* whileTyping: 'Allow',
|
|
90
90
|
* toMessage: () => Message.PressedEscape(),
|
|
91
91
|
* },
|
|
92
92
|
* {
|
|
93
|
-
*
|
|
93
|
+
* keys: 'Mod+K',
|
|
94
94
|
* whileTyping: 'Allow',
|
|
95
95
|
* toMessage: () => Message.PressedSearchShortcut(),
|
|
96
96
|
* },
|
|
97
97
|
* {
|
|
98
|
-
*
|
|
98
|
+
* keys: ['G', 'L'],
|
|
99
99
|
* toMessage: () => Message.PressedListShortcut(),
|
|
100
100
|
* },
|
|
101
101
|
* ],
|
|
@@ -105,6 +105,6 @@ export type KeyboardShortcutsConfig<Message> = Readonly<{
|
|
|
105
105
|
* }))
|
|
106
106
|
* ```
|
|
107
107
|
*/
|
|
108
|
-
export declare const
|
|
108
|
+
export declare const keyBindings: <Message>(config: KeyBindingsConfig<Message>) => Stream.Stream<Message>;
|
|
109
109
|
export {};
|
|
110
|
-
//# sourceMappingURL=
|
|
110
|
+
//# sourceMappingURL=keyBindings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyBindings.d.ts","sourceRoot":"","sources":["../../src/subscription/keyBindings.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,QAAQ,EAOR,MAAM,EAGP,MAAM,QAAQ,CAAA;AAEf,oFAAoF;AACpF,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,OAAO,CAAA;AAE9C,mEAAmE;AACnE,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,QAAQ,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;AAE/E,KAAK,WAAW,CAAC,OAAO,IAAI,QAAQ,CAAC;IACnC,IAAI,EAAE,WAAW,CAAA;IACjB,SAAS,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,OAAO,CAAA;IAC5C,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB,CAAC,CAAA;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,CAAC,OAAO,IAAI,WAAW,CAAC,OAAO,CAAC,GACpD,CACI,QAAQ,CAAC;IACP,IAAI,EAAE,MAAM,CAAA;IACZ,YAAY,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAA;CAClC,CAAC,GACF,QAAQ,CAAC;IACP,IAAI,EAAE,QAAQ,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;IAClD,YAAY,CAAC,EAAE,KAAK,CAAA;CACrB,CAAC,CACL,CAAA;AAEH,KAAK,MAAM,GAAG,SAAS,GAAG,MAAM,CAAA;AAEhC,+DAA+D;AAC/D,MAAM,MAAM,iBAAiB,CAAC,OAAO,IAAI,QAAQ,CAAC;IAChD,QAAQ,EAAE,aAAa,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAA;IAC5C,MAAM,CAAC,EAAE,WAAW,GAAG,CAAC,MAAM,WAAW,CAAC,CAAA;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,eAAe,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAA;CACjC,CAAC,CAAA;AA2sBF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwEG;AACH,eAAO,MAAM,WAAW,GAAI,OAAO,UACzB,iBAAiB,CAAC,OAAO,CAAC,KACjC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAuD,CAAA"}
|
|
@@ -18,7 +18,7 @@ const MODIFIER_KEYS = [
|
|
|
18
18
|
'symbol',
|
|
19
19
|
'symbollock',
|
|
20
20
|
];
|
|
21
|
-
const
|
|
21
|
+
const BINDING_MODIFIERS = [
|
|
22
22
|
'alt',
|
|
23
23
|
'control',
|
|
24
24
|
'meta',
|
|
@@ -53,37 +53,37 @@ const pressIdentifier = (press, modKey) => {
|
|
|
53
53
|
Option.some(displayKey(press.key)),
|
|
54
54
|
], Array.getSomes, Array.join('+'));
|
|
55
55
|
};
|
|
56
|
-
const
|
|
57
|
-
throw new Error(`Invalid
|
|
56
|
+
const throwInvalidKeyPress = (keyPress, reason) => {
|
|
57
|
+
throw new Error(`Invalid key binding "${keyPress}": ${reason}`);
|
|
58
58
|
};
|
|
59
59
|
const hasDuplicateModifiers = (modifiers) => Array.length(Array.dedupe(modifiers)) !== Array.length(modifiers);
|
|
60
|
-
const parsePress = (
|
|
61
|
-
const tokens = pipe(
|
|
60
|
+
const parsePress = (keyPress) => {
|
|
61
|
+
const tokens = pipe(keyPress, String.split('+'), Array.map(token => pipe(token, String.trim, String.toLowerCase)));
|
|
62
62
|
if (Array.some(tokens, String.isEmpty)) {
|
|
63
|
-
return
|
|
63
|
+
return throwInvalidKeyPress(keyPress, 'each modifier and key must be named; use "Plus" for the + key');
|
|
64
64
|
}
|
|
65
65
|
const maybeNonCanonicalModifier = Array.findFirst(tokens, token => Array.contains(NON_CANONICAL_MODIFIERS, token));
|
|
66
66
|
if (Option.isSome(maybeNonCanonicalModifier)) {
|
|
67
|
-
return
|
|
67
|
+
return throwInvalidKeyPress(keyPress, `unknown modifier "${maybeNonCanonicalModifier.value}"`);
|
|
68
68
|
}
|
|
69
69
|
const [modifiers, keyToken] = Array.unappend(tokens);
|
|
70
70
|
const key = normalizeKey(keyToken);
|
|
71
|
-
if (Array.contains(
|
|
71
|
+
if (Array.contains(BINDING_MODIFIERS, keyToken) ||
|
|
72
72
|
Array.contains(MODIFIER_KEYS, key)) {
|
|
73
|
-
return
|
|
73
|
+
return throwInvalidKeyPress(keyPress, 'a non-modifier key is required');
|
|
74
74
|
}
|
|
75
|
-
const maybeUnknownModifier = Array.findFirst(modifiers, modifier => !Array.contains(
|
|
75
|
+
const maybeUnknownModifier = Array.findFirst(modifiers, modifier => !Array.contains(BINDING_MODIFIERS, modifier));
|
|
76
76
|
if (Option.isSome(maybeUnknownModifier)) {
|
|
77
|
-
return
|
|
77
|
+
return throwInvalidKeyPress(keyPress, `unknown modifier "${maybeUnknownModifier.value}"`);
|
|
78
78
|
}
|
|
79
79
|
if (hasDuplicateModifiers(modifiers)) {
|
|
80
|
-
return
|
|
80
|
+
return throwInvalidKeyPress(keyPress, 'a modifier is repeated');
|
|
81
81
|
}
|
|
82
82
|
const isModRequired = Array.contains(modifiers, 'mod');
|
|
83
83
|
const isControlRequired = Array.contains(modifiers, 'control');
|
|
84
84
|
const isMetaRequired = Array.contains(modifiers, 'meta');
|
|
85
85
|
if (isModRequired && (isControlRequired || isMetaRequired)) {
|
|
86
|
-
return
|
|
86
|
+
return throwInvalidKeyPress(keyPress, 'Mod cannot be combined with Control or Meta');
|
|
87
87
|
}
|
|
88
88
|
const isAltRequired = Array.contains(modifiers, 'alt');
|
|
89
89
|
const isShiftRequired = Array.contains(modifiers, 'shift');
|
|
@@ -101,13 +101,12 @@ const parsePress = (shortcut) => {
|
|
|
101
101
|
};
|
|
102
102
|
};
|
|
103
103
|
const compileBinding = (binding) => {
|
|
104
|
-
if (!Predicate.isString(binding.
|
|
105
|
-
|
|
106
|
-
throw new Error('A keyboard shortcut sequence requires at least two keys');
|
|
104
|
+
if (!Predicate.isString(binding.keys) && Array.length(binding.keys) < 2) {
|
|
105
|
+
throw new Error('A key binding sequence requires at least two key presses');
|
|
107
106
|
}
|
|
108
|
-
const presses = Predicate.isString(binding.
|
|
109
|
-
? Array.of(parsePress(binding.
|
|
110
|
-
: Array.map(binding.
|
|
107
|
+
const presses = Predicate.isString(binding.keys)
|
|
108
|
+
? Array.of(parsePress(binding.keys))
|
|
109
|
+
: Array.map(binding.keys, parsePress);
|
|
111
110
|
return {
|
|
112
111
|
presses,
|
|
113
112
|
toMessage: binding.toMessage,
|
|
@@ -131,12 +130,12 @@ const validateBindingPair = (binding, otherBinding, modKey) => {
|
|
|
131
130
|
if (bindingIsPrefix || otherBindingIsPrefix) {
|
|
132
131
|
const relation = Array.length(binding.presses) === Array.length(otherBinding.presses)
|
|
133
132
|
? 'duplicates'
|
|
134
|
-
: 'overlaps as a complete
|
|
135
|
-
throw new Error(`
|
|
133
|
+
: 'overlaps as a complete binding and a sequence prefix';
|
|
134
|
+
throw new Error(`Key binding "${bindingLabel(binding)}" ${relation} "${bindingLabel(otherBinding)}"`);
|
|
136
135
|
}
|
|
137
136
|
if (sharesFirstPress(binding, otherBinding, modKey) &&
|
|
138
137
|
binding.preventDefault !== otherBinding.preventDefault) {
|
|
139
|
-
throw new Error(`
|
|
138
|
+
throw new Error(`Key binding sequences beginning with the same key must use the same preventDefault setting: "${bindingLabel(binding)}" and "${bindingLabel(otherBinding)}"`);
|
|
140
139
|
}
|
|
141
140
|
};
|
|
142
141
|
const validateBindings = (bindings, modKey) => {
|
|
@@ -193,7 +192,7 @@ const resolveValidationModKey = (configuredModKey) => {
|
|
|
193
192
|
}
|
|
194
193
|
return Option.some(resolveModKey(undefined));
|
|
195
194
|
};
|
|
196
|
-
const
|
|
195
|
+
const compileKeyBindingsConfig = (config) => {
|
|
197
196
|
const bindings = compileEnabledBindings(config.bindings);
|
|
198
197
|
const sequenceTimeout = Duration.toMillis(config.sequenceTimeout ?? DEFAULT_SEQUENCE_TIMEOUT);
|
|
199
198
|
if (!globalThis.Number.isFinite(sequenceTimeout) || sequenceTimeout <= 0) {
|
|
@@ -292,7 +291,7 @@ const continueSequence = (context, sequence, event) => {
|
|
|
292
291
|
event.preventDefault();
|
|
293
292
|
}
|
|
294
293
|
};
|
|
295
|
-
const
|
|
294
|
+
const handleKeyBindingEvent = (context, event) => {
|
|
296
295
|
if (!(event instanceof KeyboardEvent)) {
|
|
297
296
|
return;
|
|
298
297
|
}
|
|
@@ -312,7 +311,7 @@ const handleKeyboardShortcutEvent = (context, event) => {
|
|
|
312
311
|
onSome: sequence => continueSequence(context, sequence, event),
|
|
313
312
|
});
|
|
314
313
|
};
|
|
315
|
-
const
|
|
314
|
+
const makeKeyBindingHandler = (config) => {
|
|
316
315
|
const sequenceController = makeSequenceController(config.sequenceTimeout);
|
|
317
316
|
const context = {
|
|
318
317
|
bindings: config.bindings,
|
|
@@ -322,7 +321,7 @@ const makeKeyboardShortcutHandler = (config) => {
|
|
|
322
321
|
};
|
|
323
322
|
return {
|
|
324
323
|
clearSequence: sequenceController.clearSequence,
|
|
325
|
-
handleEvent: event =>
|
|
324
|
+
handleEvent: event => handleKeyBindingEvent(context, event),
|
|
326
325
|
};
|
|
327
326
|
};
|
|
328
327
|
const resolveOwnerDocument = (target) => {
|
|
@@ -334,11 +333,11 @@ const resolveOwnerDocument = (target) => {
|
|
|
334
333
|
}
|
|
335
334
|
return document;
|
|
336
335
|
};
|
|
337
|
-
const
|
|
336
|
+
const acquireKeyBindingListener = (config, emitMessage) => {
|
|
338
337
|
const target = resolveTarget(config.target);
|
|
339
338
|
const modKey = resolveModKey(config.modKey);
|
|
340
339
|
validateBindings(config.bindings, modKey);
|
|
341
|
-
const handler =
|
|
340
|
+
const handler = makeKeyBindingHandler({
|
|
342
341
|
bindings: config.bindings,
|
|
343
342
|
emitMessage,
|
|
344
343
|
modKey,
|
|
@@ -356,22 +355,22 @@ const acquireKeyboardShortcutListener = (config, emitMessage) => {
|
|
|
356
355
|
target,
|
|
357
356
|
};
|
|
358
357
|
};
|
|
359
|
-
const
|
|
358
|
+
const releaseKeyBindingListener = (listener) => {
|
|
360
359
|
listener.target.removeEventListener('keydown', listener.handleEvent);
|
|
361
360
|
listener.ownerWindow.removeEventListener('blur', listener.clearSequence);
|
|
362
361
|
listener.ownerDocument.removeEventListener('visibilitychange', listener.clearSequence);
|
|
363
362
|
listener.clearSequence();
|
|
364
363
|
};
|
|
365
|
-
const
|
|
364
|
+
const keyBindingStream = (config) => Stream.callback(queue => {
|
|
366
365
|
const emitMessage = (message) => {
|
|
367
366
|
Queue.offerUnsafe(queue, message);
|
|
368
367
|
};
|
|
369
|
-
return Effect.acquireRelease(Effect.sync(() =>
|
|
368
|
+
return Effect.acquireRelease(Effect.sync(() => acquireKeyBindingListener(config, emitMessage)), listener => Effect.sync(() => releaseKeyBindingListener(listener)));
|
|
370
369
|
});
|
|
371
370
|
/**
|
|
372
|
-
* Build a Stream that turns declarative
|
|
371
|
+
* Build a Stream that turns declarative key bindings into Messages.
|
|
373
372
|
*
|
|
374
|
-
* A string
|
|
373
|
+
* A string describes one key press. Modifiers are joined with `+`:
|
|
375
374
|
* `'Mod+K'`, `'Control+Shift+P'`, or `'Alt+ArrowDown'`. The supported modifiers
|
|
376
375
|
* are `Mod`, `Control`, `Meta`, `Alt`, and `Shift`. `Mod` resolves to Meta on
|
|
377
376
|
* Apple platforms and Control elsewhere; `modKey` can override that choice.
|
|
@@ -383,17 +382,17 @@ const keyboardShortcutStream = (config) => Stream.callback(queue => {
|
|
|
383
382
|
* one second by default; `sequenceTimeout` accepts any Effect Duration input.
|
|
384
383
|
* Modifier-only events and repeated keydowns do not advance a sequence.
|
|
385
384
|
*
|
|
386
|
-
*
|
|
385
|
+
* Bindings are suppressed by default when the event's composed path contains
|
|
387
386
|
* an `input`, `textarea`, `select`, or contenteditable element. Set
|
|
388
387
|
* `whileTyping` to `'Allow'` for a binding that must work there. Events emitted
|
|
389
388
|
* during IME composition are always ignored. Repeated keydowns are ignored for
|
|
390
|
-
* one-press
|
|
389
|
+
* one-press bindings unless `whenRepeated` is `'Allow'`. An event another
|
|
391
390
|
* handler already canceled is ignored and clears any sequence in progress.
|
|
392
391
|
*
|
|
393
392
|
* Matched key presses call `preventDefault()` before dispatching. For a
|
|
394
393
|
* sequence, that policy applies to every matched press. Set `preventDefault`
|
|
395
394
|
* to `false` to opt out. Sequences sharing a prefix must use the same policy.
|
|
396
|
-
* Duplicate bindings and a complete
|
|
395
|
+
* Duplicate bindings and a complete binding that is also a sequence prefix
|
|
397
396
|
* are rejected when the Stream is created.
|
|
398
397
|
*
|
|
399
398
|
* This helper returns a Stream, not a complete Subscription entry. Use
|
|
@@ -402,7 +401,7 @@ const keyboardShortcutStream = (config) => Stream.callback(queue => {
|
|
|
402
401
|
* derive each binding's `isEnabled` from the dependency record. A dependency
|
|
403
402
|
* change opens a new Stream scope and resets any sequence in progress. If a
|
|
404
403
|
* parent owns a condition for a lifted child, declare the table at that parent
|
|
405
|
-
* or put
|
|
404
|
+
* or put bindings with different parent-owned lifetimes in separate child
|
|
406
405
|
* entries so `Subscription.lift` can gate them individually. If the meaning
|
|
407
406
|
* of a key depends on the Model, dispatch a factual key Message and decide
|
|
408
407
|
* what it means in update instead of reading the Model from `toMessage`.
|
|
@@ -410,28 +409,28 @@ const keyboardShortcutStream = (config) => Stream.callback(queue => {
|
|
|
410
409
|
* @example
|
|
411
410
|
* ```typescript
|
|
412
411
|
* const subscriptions = Subscription.make<Model, Message>()(entry => ({
|
|
413
|
-
*
|
|
412
|
+
* keyBindings: entry(
|
|
414
413
|
* { isPaletteOpen: Schema.Boolean },
|
|
415
414
|
* {
|
|
416
415
|
* modelToDependencies: model => ({
|
|
417
416
|
* isPaletteOpen: model.paletteState._tag === 'Open',
|
|
418
417
|
* }),
|
|
419
418
|
* dependenciesToStream: ({ isPaletteOpen }) =>
|
|
420
|
-
* Subscription.
|
|
419
|
+
* Subscription.keyBindings<Message>({
|
|
421
420
|
* bindings: [
|
|
422
421
|
* {
|
|
423
|
-
*
|
|
422
|
+
* keys: 'Escape',
|
|
424
423
|
* isEnabled: isPaletteOpen,
|
|
425
424
|
* whileTyping: 'Allow',
|
|
426
425
|
* toMessage: () => Message.PressedEscape(),
|
|
427
426
|
* },
|
|
428
427
|
* {
|
|
429
|
-
*
|
|
428
|
+
* keys: 'Mod+K',
|
|
430
429
|
* whileTyping: 'Allow',
|
|
431
430
|
* toMessage: () => Message.PressedSearchShortcut(),
|
|
432
431
|
* },
|
|
433
432
|
* {
|
|
434
|
-
*
|
|
433
|
+
* keys: ['G', 'L'],
|
|
435
434
|
* toMessage: () => Message.PressedListShortcut(),
|
|
436
435
|
* },
|
|
437
436
|
* ],
|
|
@@ -441,4 +440,4 @@ const keyboardShortcutStream = (config) => Stream.callback(queue => {
|
|
|
441
440
|
* }))
|
|
442
441
|
* ```
|
|
443
442
|
*/
|
|
444
|
-
export const
|
|
443
|
+
export const keyBindings = (config) => keyBindingStream(compileKeyBindingsConfig(config));
|
|
@@ -4,6 +4,6 @@ export { animationFrame } from './animationFrame.js';
|
|
|
4
4
|
export type { AnimationFrameConfig } from './animationFrame.js';
|
|
5
5
|
export { fromEvent, fromEventFilterMap, fromEventFilterMapPreventDefault, } from './fromEvent.js';
|
|
6
6
|
export type { FromEventConfig, FromEventFilterMapConfig, FromEventFilterMapPreventDefaultConfig, TypedEventTarget, } from './fromEvent.js';
|
|
7
|
-
export {
|
|
8
|
-
export type {
|
|
7
|
+
export { keyBindings } from './keyBindings.js';
|
|
8
|
+
export type { KeyBinding, KeyBindingsConfig, KeySequence, WhileTyping, } from './keyBindings.js';
|
|
9
9
|
//# sourceMappingURL=public.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/subscription/public.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAErE,YAAY,EACV,qBAAqB,EACrB,iBAAiB,EACjB,YAAY,EACZ,aAAa,GACd,MAAM,mBAAmB,CAAA;AAE1B,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAEpD,YAAY,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAE/D,OAAO,EACL,SAAS,EACT,kBAAkB,EAClB,gCAAgC,GACjC,MAAM,gBAAgB,CAAA;AAEvB,YAAY,EACV,eAAe,EACf,wBAAwB,EACxB,sCAAsC,EACtC,gBAAgB,GACjB,MAAM,gBAAgB,CAAA;AAEvB,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/subscription/public.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAErE,YAAY,EACV,qBAAqB,EACrB,iBAAiB,EACjB,YAAY,EACZ,aAAa,GACd,MAAM,mBAAmB,CAAA;AAE1B,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAEpD,YAAY,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAE/D,OAAO,EACL,SAAS,EACT,kBAAkB,EAClB,gCAAgC,GACjC,MAAM,gBAAgB,CAAA;AAEvB,YAAY,EACV,eAAe,EACf,wBAAwB,EACxB,sCAAsC,EACtC,gBAAgB,GACjB,MAAM,gBAAgB,CAAA;AAEvB,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAE9C,YAAY,EACV,UAAU,EACV,iBAAiB,EACjB,WAAW,EACX,WAAW,GACZ,MAAM,kBAAkB,CAAA"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { aggregate, lift, make, persistent } from './subscription.js';
|
|
2
2
|
export { animationFrame } from './animationFrame.js';
|
|
3
3
|
export { fromEvent, fromEventFilterMap, fromEventFilterMapPreventDefault, } from './fromEvent.js';
|
|
4
|
-
export {
|
|
4
|
+
export { keyBindings } from './keyBindings.js';
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"keyboardShortcuts.d.ts","sourceRoot":"","sources":["../../src/subscription/keyboardShortcuts.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,QAAQ,EAOR,MAAM,EAGP,MAAM,QAAQ,CAAA;AAEf,0FAA0F;AAC1F,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,OAAO,CAAA;AAE9C,mEAAmE;AACnE,MAAM,MAAM,gBAAgB,GACxB,MAAM,GACN,QAAQ,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;AAEhD,KAAK,WAAW,CAAC,OAAO,IAAI,QAAQ,CAAC;IACnC,QAAQ,EAAE,gBAAgB,CAAA;IAC1B,SAAS,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,OAAO,CAAA;IAC5C,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB,CAAC,CAAA;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,uBAAuB,CAAC,OAAO,IAAI,WAAW,CAAC,OAAO,CAAC,GACjE,CACI,QAAQ,CAAC;IACP,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAA;CAClC,CAAC,GACF,QAAQ,CAAC;IACP,QAAQ,EAAE,QAAQ,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;IACtD,YAAY,CAAC,EAAE,KAAK,CAAA;CACrB,CAAC,CACL,CAAA;AAEH,KAAK,MAAM,GAAG,SAAS,GAAG,MAAM,CAAA;AAEhC,qEAAqE;AACrE,MAAM,MAAM,uBAAuB,CAAC,OAAO,IAAI,QAAQ,CAAC;IACtD,QAAQ,EAAE,aAAa,CAAC,uBAAuB,CAAC,OAAO,CAAC,CAAC,CAAA;IACzD,MAAM,CAAC,EAAE,WAAW,GAAG,CAAC,MAAM,WAAW,CAAC,CAAA;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,eAAe,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAA;CACjC,CAAC,CAAA;AA8sBF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwEG;AACH,eAAO,MAAM,iBAAiB,GAAI,OAAO,UAC/B,uBAAuB,CAAC,OAAO,CAAC,KACvC,MAAM,CAAC,MAAM,CAAC,OAAO,CACwC,CAAA"}
|