@dxos/keyboard 0.3.10 → 0.3.11-main.00a28cb
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 +159 -24
- package/dist/lib/browser/index.mjs +74 -37
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +76 -38
- package/dist/lib/node/index.cjs.map +3 -3
- package/dist/lib/node/meta.json +1 -1
- package/dist/types/src/keyboard.d.ts +3 -2
- package/dist/types/src/keyboard.d.ts.map +1 -1
- package/dist/types/src/util.d.ts.map +1 -1
- package/package.json +7 -6
- package/src/keyboard.stories.tsx +5 -5
- package/src/keyboard.test.ts +1 -1
- package/src/keyboard.ts +17 -15
- package/src/util.ts +56 -14
package/README.md
CHANGED
|
@@ -1,28 +1,163 @@
|
|
|
1
|
-
##
|
|
1
|
+
## @dxos/keyboard
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Utility for managing contextual keyboard bindings.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Common Shortcuts
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
"jsdom": true,
|
|
11
|
-
"testingFramework": "mocha"
|
|
12
|
-
}
|
|
13
|
-
```
|
|
7
|
+
- Meta => Command (macos), Windows (Windows), Super (Linux)
|
|
8
|
+
- Alt => Option (macos)
|
|
9
|
+
- Tip: Use https://tableconvert.com/markdown-to-markdown to edit the table.
|
|
14
10
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
11
|
+
| | macos | windows | chrome (macos) | chrome (windows) |
|
|
12
|
+
|-------------------|------------------------------------------|----------------------------------------------------------------------------------------------------------------|----------------|-----------------------------------------------------------------------------------|
|
|
13
|
+
| | https://support.apple.com/en-us/HT201236 | https://support.microsoft.com/en-us/windows/keyboard-shortcuts-in-windows-dcc61a57-8ff0-cffe-9796-cb9706c75eec | | https://support.google.com/chrome/answer/157179?hl=en&co=GENIE.Platform%3DDesktop |
|
|
14
|
+
| Alt-Tab | | x | | |
|
|
15
|
+
| Alt-F4 | | x | | |
|
|
16
|
+
| Alt-F8 | | x | | |
|
|
17
|
+
| Alt-Esc | | x | | |
|
|
18
|
+
| Alt-Enter | | x | | |
|
|
19
|
+
| Alt-Space | | x | | |
|
|
20
|
+
| Alt-A | | x | | |
|
|
21
|
+
| Alt-E | | | | x |
|
|
22
|
+
| Alt-F | | | | x |
|
|
23
|
+
| Alt-Meta-Esc | x | | | |
|
|
24
|
+
| Alt-Meta-Enter | | x | | |
|
|
25
|
+
| Alt-Meta-B | | x | x | |
|
|
26
|
+
| Alt-Meta-F | | | x | |
|
|
27
|
+
| Alt-Meta-I | | | x | |
|
|
28
|
+
| Alt-Meta-J | | | x | |
|
|
29
|
+
| Alt-Meta-K | | x | | |
|
|
30
|
+
| Alt-Meta-P | | | x | |
|
|
31
|
+
| Alt-Meta-R | | x | | |
|
|
32
|
+
| Alt-Meta-U | | | x | |
|
|
33
|
+
| Alt-Meta-Shift-A | | | x | |
|
|
34
|
+
| Alt-Meta-Shift-I | | | x | |
|
|
35
|
+
| "Alt-Ctrl-Meta-," | x | | | |
|
|
36
|
+
| Alt-Ctrl-Meta-. | x | | | |
|
|
37
|
+
| Alt-Ctrl-Meta-8 | x | | | |
|
|
38
|
+
| Alt-Ctrl-Tab | | x | | |
|
|
39
|
+
| Alt-Shift-A | | | | x |
|
|
40
|
+
| Alt-Shift-I | | | | x |
|
|
41
|
+
| Alt-Shift-T | | | | x |
|
|
42
|
+
| Ctrl-Esc | | x | | |
|
|
43
|
+
| Ctrl-Backspace | | | | x |
|
|
44
|
+
| Ctrl-Space | | x | | |
|
|
45
|
+
| Ctrl-Tab | | | | x |
|
|
46
|
+
| Ctrl-+ | | | | x |
|
|
47
|
+
| Ctrl-- | | | | x |
|
|
48
|
+
| Ctrl-F2 | x | | x | |
|
|
49
|
+
| Ctrl-F3 | x | | | |
|
|
50
|
+
| Ctrl-F4 | x | | | |
|
|
51
|
+
| Ctrl-F5 | x | | | x |
|
|
52
|
+
| Ctrl-F6 | x | | | x |
|
|
53
|
+
| Ctrl-F7 | x | | | |
|
|
54
|
+
| Ctrl-F8 | x | | | |
|
|
55
|
+
| Ctrl-F4 | | x | | |
|
|
56
|
+
| Ctrl-A | x | x | | |
|
|
57
|
+
| Ctrl-B | x | | | |
|
|
58
|
+
| Ctrl-C | | x | | |
|
|
59
|
+
| Ctrl-D | | x | | x |
|
|
60
|
+
| Ctrl-E | x | | | x |
|
|
61
|
+
| Ctrl-F | x | | | x |
|
|
62
|
+
| Ctrl-G | | | | x |
|
|
63
|
+
| Ctrl-H | | | | x |
|
|
64
|
+
| Ctrl-J | | | | x |
|
|
65
|
+
| Ctrl-K | | | | x |
|
|
66
|
+
| Ctrl-L | x | | | x |
|
|
67
|
+
| Ctrl-N | x | | | x |
|
|
68
|
+
| Ctrl-O | x | | | x |
|
|
69
|
+
| Ctrl-P | x | | | x |
|
|
70
|
+
| Ctrl-R | | x | | x |
|
|
71
|
+
| Ctrl-S | | | | x |
|
|
72
|
+
| Ctrl-T | x | | | x |
|
|
73
|
+
| Ctrl-U | | | | x |
|
|
74
|
+
| Ctrl-V | | x | | |
|
|
75
|
+
| Ctrl-W | | | | x |
|
|
76
|
+
| Ctrl-X | | x | | |
|
|
77
|
+
| Ctrl-Y | | x | | |
|
|
78
|
+
| Ctrl-Z | | x | | |
|
|
79
|
+
| Ctrl-Meta-Enter | | | | |
|
|
80
|
+
| Ctrl-Meta-Space | x | | | |
|
|
81
|
+
| Ctrl-Meta-C | | x | | |
|
|
82
|
+
| Ctrl-Meta-Q | x | x | | |
|
|
83
|
+
| Ctrl-Meta-F | x | x | x | |
|
|
84
|
+
| Ctrl-Meta-Shift-B | | x | | |
|
|
85
|
+
| Ctrl-Shift-F6 | x | | | |
|
|
86
|
+
| Ctrl-Shift-Esc | | x | | |
|
|
87
|
+
| Ctrl-Shift-Delete | | | | x |
|
|
88
|
+
| Ctrl-Shift-B | | | | x |
|
|
89
|
+
| Ctrl-Shift-D | | | | x |
|
|
90
|
+
| Ctrl-Shift-G | | | | x |
|
|
91
|
+
| Ctrl-Shift-J | | | | x |
|
|
92
|
+
| Ctrl-Shift-M | | | | |
|
|
93
|
+
| Ctrl-Shift-N | | | | x |
|
|
94
|
+
| Ctrl-Shift-O | | | | x |
|
|
95
|
+
| Ctrl-Shift-R | | | | x |
|
|
96
|
+
| Ctrl-Shift-T | | | | x |
|
|
97
|
+
| Ctrl-Shift-V | | x | | |
|
|
98
|
+
| Meta-. | | x | | |
|
|
99
|
+
| Meta-; | | x | | |
|
|
100
|
+
| "Meta-," | | x | x | |
|
|
101
|
+
| Meta-` | x | | | |
|
|
102
|
+
| Meta-+ | | x | x | |
|
|
103
|
+
| Meta-- | | x | x | |
|
|
104
|
+
| Meta-/ | | x | | |
|
|
105
|
+
| Meta-[ | | | x | |
|
|
106
|
+
| Meta-] | | | x | |
|
|
107
|
+
| Meta-Backspace | x | | | |
|
|
108
|
+
| Meta-Esc | | x | | |
|
|
109
|
+
| Meta-Space | x | x | | |
|
|
110
|
+
| Meta-Tab | x | x | | |
|
|
111
|
+
| Meta-A | x | x | | |
|
|
112
|
+
| Meta-B | | x | | |
|
|
113
|
+
| Meta-C | x | x | | |
|
|
114
|
+
| Meta-D | | x | x | |
|
|
115
|
+
| Meta-E | | x | x | |
|
|
116
|
+
| Meta-F | x | x | x | |
|
|
117
|
+
| Meta-G | x | x | x | |
|
|
118
|
+
| Meta-H | x | x | x | |
|
|
119
|
+
| Meta-I | | x | | |
|
|
120
|
+
| Meta-J | | x | | |
|
|
121
|
+
| Meta-K | | x | | |
|
|
122
|
+
| Meta-L | | x | x | |
|
|
123
|
+
| Meta-M | x | x | x | |
|
|
124
|
+
| Meta-N | | x | x | |
|
|
125
|
+
| Meta-O | x | x | x | |
|
|
126
|
+
| Meta-P | x | x | x | |
|
|
127
|
+
| Meta-Q | x | x | x | |
|
|
128
|
+
| Meta-R | | x | | |
|
|
129
|
+
| Meta-S | x | x | x | |
|
|
130
|
+
| Meta-T | x | x | x | |
|
|
131
|
+
| Meta-U | | x | | |
|
|
132
|
+
| Meta-V | x | x | | |
|
|
133
|
+
| Meta-W | x | x | x | |
|
|
134
|
+
| Meta-X | x | x | | |
|
|
135
|
+
| Meta-Y | | x | x | |
|
|
136
|
+
| Meta-Z | x | x | | |
|
|
137
|
+
| Meta-Shift-Space | | x | | |
|
|
138
|
+
| Meta-Shift-Delete | | | x | |
|
|
139
|
+
| Meta-Shift-+ | x | | | |
|
|
140
|
+
| Meta-Shift-- | x | | | |
|
|
141
|
+
| Meta-Shift-? | x | | | |
|
|
142
|
+
| Meta-Shift-3 | x | | | |
|
|
143
|
+
| Meta-Shift-4 | x | | | |
|
|
144
|
+
| Meta-Shift-5 | x | | | |
|
|
145
|
+
| Meta-Shift-B | | | x | |
|
|
146
|
+
| Meta-Shift-C | | x | | |
|
|
147
|
+
| Meta-Shift-D | | | x | |
|
|
148
|
+
| Meta-Shift-G | | | x | |
|
|
149
|
+
| Meta-Shift-H | | | x | |
|
|
150
|
+
| Meta-Shift-I | | | x | |
|
|
151
|
+
| Meta-Shift-J | | | x | |
|
|
152
|
+
| Meta-Shift-M | | x | x | |
|
|
153
|
+
| Meta-Shift-N | | | x | |
|
|
154
|
+
| Meta-Shift-R | | | | |
|
|
155
|
+
| Meta-Shift-S | x | x | | |
|
|
156
|
+
| Meta-Shift-T | | | x | |
|
|
157
|
+
| Meta-Shift-V | | x | | |
|
|
158
|
+
| Meta-Shift-W | | | x | |
|
|
159
|
+
| Meta-Shift-Q | x | | | |
|
|
160
|
+
| Meta-Shift-` | x | | | |
|
|
161
|
+
| Alt-Meta-` | x | | | |
|
|
162
|
+
| Ctrl-Tab | x | | | |
|
|
163
|
+
| Ctrl-Shift-Tab | x | | | |
|
|
@@ -9,13 +9,30 @@ import {
|
|
|
9
9
|
|
|
10
10
|
// packages/common/keyboard/src/keyboard.ts
|
|
11
11
|
import { invariant } from "@dxos/invariant";
|
|
12
|
-
var __dxlog_file = "/home/
|
|
12
|
+
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/common/keyboard/src/keyboard.ts";
|
|
13
13
|
var modifiers = [
|
|
14
|
-
"alt",
|
|
15
14
|
"ctrl",
|
|
16
15
|
"shift",
|
|
16
|
+
"alt",
|
|
17
17
|
"meta"
|
|
18
18
|
];
|
|
19
|
+
var parseShortcut = (shortcut, delimiter = /[+-]/) => {
|
|
20
|
+
const parts = shortcut.split(delimiter);
|
|
21
|
+
const mods = modifiers.filter((key) => parts.includes(key));
|
|
22
|
+
invariant(mods.length === 0 || mods.length === parts.length - 1, void 0, {
|
|
23
|
+
F: __dxlog_file,
|
|
24
|
+
L: 29,
|
|
25
|
+
S: void 0,
|
|
26
|
+
A: [
|
|
27
|
+
"mods.length === 0 || mods.length === parts.length - 1",
|
|
28
|
+
""
|
|
29
|
+
]
|
|
30
|
+
});
|
|
31
|
+
return mods.length ? [
|
|
32
|
+
...mods,
|
|
33
|
+
parts[parts.length - 1]
|
|
34
|
+
].join("+") : shortcut;
|
|
35
|
+
};
|
|
19
36
|
var KeyboardContext = class {
|
|
20
37
|
constructor() {
|
|
21
38
|
this._keyMap = /* @__PURE__ */ new Map();
|
|
@@ -27,25 +44,8 @@ var KeyboardContext = class {
|
|
|
27
44
|
return this._keyMap.get(binding);
|
|
28
45
|
}
|
|
29
46
|
bind(config) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const mods = modifiers.filter((key) => parts.includes(key));
|
|
33
|
-
invariant(mods.length === 0 || mods.length === parts.length - 1, void 0, {
|
|
34
|
-
F: __dxlog_file,
|
|
35
|
-
L: 40,
|
|
36
|
-
S: this,
|
|
37
|
-
A: [
|
|
38
|
-
"mods.length === 0 || mods.length === parts.length - 1",
|
|
39
|
-
""
|
|
40
|
-
]
|
|
41
|
-
});
|
|
42
|
-
if (mods.length) {
|
|
43
|
-
config.binding = [
|
|
44
|
-
...mods,
|
|
45
|
-
parts[parts.length - 1]
|
|
46
|
-
].join("+");
|
|
47
|
-
}
|
|
48
|
-
this._keyMap.set(config.binding, config);
|
|
47
|
+
config.shortcut = parseShortcut(config.shortcut);
|
|
48
|
+
this._keyMap.set(config.shortcut, config);
|
|
49
49
|
}
|
|
50
50
|
unbind(binding) {
|
|
51
51
|
this._keyMap.delete(binding);
|
|
@@ -100,7 +100,7 @@ var Keyboard = class _Keyboard {
|
|
|
100
100
|
const path = this._contexts[i];
|
|
101
101
|
if (this._path.startsWith(path)) {
|
|
102
102
|
this.getContext(path).bindings.forEach((binding) => {
|
|
103
|
-
bindings.set(binding.
|
|
103
|
+
bindings.set(binding.shortcut, binding);
|
|
104
104
|
});
|
|
105
105
|
}
|
|
106
106
|
}
|
|
@@ -125,7 +125,7 @@ var Keyboard = class _Keyboard {
|
|
|
125
125
|
if (handler && (!isInput || !disableInput)) {
|
|
126
126
|
const result = handler({
|
|
127
127
|
context: path,
|
|
128
|
-
|
|
128
|
+
shortcut: str,
|
|
129
129
|
data,
|
|
130
130
|
event
|
|
131
131
|
});
|
|
@@ -142,25 +142,62 @@ var Keyboard = class _Keyboard {
|
|
|
142
142
|
};
|
|
143
143
|
|
|
144
144
|
// packages/common/keyboard/src/util.ts
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
145
|
+
import { getHostPlatform } from "@dxos/util";
|
|
146
|
+
var ctrl = {
|
|
147
|
+
macos: "\u2303",
|
|
148
|
+
ios: "\u2303",
|
|
149
|
+
windows: "Ctrl",
|
|
150
|
+
linux: "Ctrl",
|
|
151
|
+
unknown: "Ctrl"
|
|
152
|
+
};
|
|
153
|
+
var alt = {
|
|
154
|
+
macos: "\u2325",
|
|
155
|
+
ios: "\u2325",
|
|
156
|
+
windows: "Alt",
|
|
157
|
+
linux: "Alt",
|
|
158
|
+
unknown: "Alt"
|
|
159
|
+
};
|
|
160
|
+
var meta = {
|
|
161
|
+
macos: "\u2318",
|
|
162
|
+
ios: "\u2318",
|
|
163
|
+
windows: "\u229E",
|
|
164
|
+
// TODO(wittjosiah): Use ⌘ or ⊞ instead? Wait for user feedback.
|
|
165
|
+
// From https://en.wikipedia.org/wiki/Super_key_(keyboard_button).
|
|
166
|
+
linux: "\u2756",
|
|
167
|
+
unknown: "\u2756"
|
|
168
|
+
};
|
|
169
|
+
var getSymbol = (part) => {
|
|
170
|
+
const platform = getHostPlatform();
|
|
171
|
+
switch (part) {
|
|
172
|
+
case "alt":
|
|
173
|
+
return alt[platform];
|
|
174
|
+
case "ctrl":
|
|
175
|
+
return ctrl[platform];
|
|
176
|
+
case "meta":
|
|
177
|
+
return meta[platform];
|
|
178
|
+
case "shift":
|
|
179
|
+
return "\u21E7";
|
|
180
|
+
case "Backspace":
|
|
181
|
+
return "\u232B";
|
|
182
|
+
case "Enter":
|
|
183
|
+
return "\u23CE";
|
|
184
|
+
case "Escape":
|
|
185
|
+
return "\u238B";
|
|
186
|
+
case "Space":
|
|
187
|
+
return "\u2423";
|
|
188
|
+
case "Tab":
|
|
189
|
+
return "\u21E5";
|
|
190
|
+
default:
|
|
191
|
+
return part.toUpperCase();
|
|
192
|
+
}
|
|
157
193
|
};
|
|
158
194
|
var keySymbols = (keyBinding) => {
|
|
159
195
|
const parts = keyBinding.split("+");
|
|
160
|
-
return parts.map(
|
|
196
|
+
return parts.map(getSymbol);
|
|
161
197
|
};
|
|
162
198
|
export {
|
|
163
199
|
Keyboard,
|
|
164
|
-
keySymbols
|
|
200
|
+
keySymbols,
|
|
201
|
+
parseShortcut
|
|
165
202
|
};
|
|
166
203
|
//# sourceMappingURL=index.mjs.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/keyboard.ts", "../../../src/util.ts"],
|
|
4
|
-
"sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nimport { invariant } from '@dxos/invariant';\n\nexport type KeyHandler = (props: {\n context: string;\n
|
|
5
|
-
"mappings": ";;;;;;;;;;AAIA,SAASA,iBAAiB;;
|
|
6
|
-
"names": ["invariant", "modifiers", "
|
|
4
|
+
"sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nimport { invariant } from '@dxos/invariant';\n\nexport type KeyHandler = (props: {\n context: string;\n shortcut: string;\n data?: any;\n event: KeyboardEvent;\n}) => boolean | void;\n\nexport type KeyBinding = {\n shortcut: string;\n handler: KeyHandler;\n disableInput?: boolean;\n data?: any;\n};\n\n// Keybinding is normalized to this order.\n// https://support.apple.com/en-us/HT201236\nconst modifiers = ['ctrl', 'shift', 'alt', 'meta'];\n\n// Normalize order of modifiers.\nexport const parseShortcut = (shortcut: string, delimiter = /[+-]/): string => {\n const parts = shortcut.split(delimiter);\n const mods = modifiers.filter((key) => parts.includes(key));\n invariant(mods.length === 0 || mods.length === parts.length - 1);\n // Assume single natural key.\n return mods.length ? [...mods, parts[parts.length - 1]].join('+') : shortcut;\n};\n\nclass KeyboardContext {\n readonly _keyMap = new Map<string, KeyBinding>();\n\n get bindings() {\n return Array.from(this._keyMap.values());\n }\n\n get(binding: string) {\n return this._keyMap.get(binding);\n }\n\n bind(config: KeyBinding) {\n config.shortcut = parseShortcut(config.shortcut);\n this._keyMap.set(config.shortcut, config);\n }\n\n unbind(binding: string) {\n this._keyMap.delete(binding);\n }\n}\n\nconst ROOT = '';\n\n/**\n * Manages context-aware key bindings.\n */\nexport class Keyboard {\n static singleton = new Keyboard();\n\n private readonly _root = new KeyboardContext();\n private readonly _keyMap = new Map<string, KeyboardContext>([[ROOT, this._root]]);\n private readonly _contexts: string[] = [ROOT];\n private readonly _keyHandler = this.handleKeyDown.bind(this);\n private _path = ROOT;\n\n initialize() {\n document.addEventListener('keydown', this._keyHandler);\n }\n\n destroy() {\n document.removeEventListener('keydown', this._keyHandler);\n }\n\n bind = this._root.bind.bind(this._root);\n unbind = this._root.unbind.bind(this._root);\n\n setCurrentContext(path = ROOT) {\n this._path = path;\n }\n\n getCurrentContext() {\n return this._path;\n }\n\n getContext(path = ROOT): KeyboardContext {\n let context = this._keyMap.get(path);\n if (!context) {\n context = new KeyboardContext();\n this._keyMap.set(path, context);\n this._contexts.push(path);\n this._contexts.sort();\n }\n\n return context;\n }\n\n getBindings() {\n const bindings = new Map<string, KeyBinding>();\n for (let i = 0; i < this._contexts.length; ++i) {\n const path = this._contexts[i];\n if (this._path.startsWith(path)) {\n this.getContext(path).bindings.forEach((binding) => {\n bindings.set(binding.shortcut, binding);\n });\n }\n }\n\n return Array.from(bindings.values());\n }\n\n handleKeyDown(event: KeyboardEvent) {\n const { altKey, ctrlKey, metaKey, shiftKey, key } = event;\n\n if (key !== 'Alt' && key !== 'Control' && key !== 'Meta' && key !== 'Shift') {\n // Binding option to check for input or contenteditable.\n const tagName = (event.target as any)?.tagName;\n const isInput =\n tagName === 'INPUT' || tagName === 'TEXTAREA' || (event.target as any)?.getAttribute('contenteditable');\n\n // Normalized key binding.\n const str = [altKey && 'alt', ctrlKey && 'ctrl', shiftKey && 'shift', metaKey && 'meta', key]\n .filter(Boolean)\n .join('+');\n\n // Scan matching contexts.\n for (let i = this._contexts.length - 1; i >= 0; --i) {\n const path = this._contexts[i];\n if (this._path.startsWith(path)) {\n const { data, handler, disableInput } = this.getContext(path).get(str) ?? {};\n if (handler && (!isInput || !disableInput)) {\n const result = handler({ context: path, shortcut: str, data, event });\n if (result !== false) {\n // TODO(burdon): This doesn't prevent actions in markdown editor.\n // Reference: https://codemirror.net/docs/ref/#view.KeyBinding\n event.preventDefault();\n event.stopPropagation();\n }\n\n return;\n }\n }\n }\n }\n }\n}\n", "//\n// Copyright 2023 DXOS.org\n//\n\nimport { getHostPlatform } from '@dxos/util';\n\n// Resources.\n// https://www.w3.org/TR/DOM-Level-3-Events/#events-keyboardevents\n// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key\n// https://developer.apple.com/design/human-interface-guidelines/designing-for-macos\n// https://support.apple.com/en-us/HT201236\n// https://support.apple.com/guide/mac-help/what-are-those-symbols-shown-in-menus-cpmh0011/mac\n\nconst ctrl: Record<string, string> = {\n macos: '⌃',\n ios: '⌃',\n windows: 'Ctrl',\n linux: 'Ctrl',\n unknown: 'Ctrl',\n};\n\nconst alt: Record<string, string> = {\n macos: '⌥',\n ios: '⌥',\n windows: 'Alt',\n linux: 'Alt',\n unknown: 'Alt',\n};\n\nconst meta: Record<string, string> = {\n macos: '⌘',\n ios: '⌘',\n windows: '⊞',\n // TODO(wittjosiah): Use ⌘ or ⊞ instead? Wait for user feedback.\n // From https://en.wikipedia.org/wiki/Super_key_(keyboard_button).\n linux: '❖',\n unknown: '❖',\n};\n\nconst getSymbol = (part: string) => {\n const platform = getHostPlatform();\n switch (part) {\n // Mods.\n case 'alt':\n return alt[platform];\n case 'ctrl':\n return ctrl[platform];\n case 'meta':\n return meta[platform];\n case 'shift':\n return '⇧';\n // Special keys.\n case 'Backspace':\n return '⌫';\n case 'Enter':\n return '⏎';\n case 'Escape':\n return '⎋';\n case 'Space':\n return '␣';\n case 'Tab':\n return '⇥';\n default:\n return part.toUpperCase();\n }\n};\n\nexport const keySymbols = (keyBinding: string): string[] => {\n const parts = keyBinding.split('+');\n return parts.map(getSymbol);\n};\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["invariant", "modifiers", "parseShortcut", "shortcut", "delimiter", "parts", "split", "mods", "filter", "key", "includes", "length", "join", "KeyboardContext", "_keyMap", "Map", "bindings", "Array", "from", "values", "get", "binding", "bind", "config", "set", "unbind", "delete", "ROOT", "Keyboard", "_root", "_contexts", "_keyHandler", "handleKeyDown", "_path", "singleton", "initialize", "document", "addEventListener", "destroy", "removeEventListener", "setCurrentContext", "path", "getCurrentContext", "getContext", "context", "push", "sort", "getBindings", "i", "startsWith", "forEach", "event", "altKey", "ctrlKey", "metaKey", "shiftKey", "tagName", "target", "isInput", "getAttribute", "str", "Boolean", "data", "handler", "disableInput", "result", "preventDefault", "stopPropagation", "getHostPlatform", "ctrl", "macos", "ios", "windows", "linux", "unknown", "alt", "meta", "getSymbol", "part", "platform", "getHostPlatform", "toUpperCase", "keySymbols", "keyBinding", "parts", "split", "map"]
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"inputs":{"inject-globals:@inject-globals":{"bytes":384,"imports":[{"path":"@dxos/node-std/inject-globals","kind":"import-statement","external":true}],"format":"esm"},"packages/common/keyboard/src/keyboard.ts":{"bytes":
|
|
1
|
+
{"inputs":{"inject-globals:@inject-globals":{"bytes":384,"imports":[{"path":"@dxos/node-std/inject-globals","kind":"import-statement","external":true}],"format":"esm"},"packages/common/keyboard/src/keyboard.ts":{"bytes":15531,"imports":[{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@inject-globals","kind":"import-statement","external":true}],"format":"esm"},"packages/common/keyboard/src/util.ts":{"bytes":5602,"imports":[{"path":"@dxos/util","kind":"import-statement","external":true},{"path":"@inject-globals","kind":"import-statement","external":true}],"format":"esm"},"packages/common/keyboard/src/index.ts":{"bytes":544,"imports":[{"path":"packages/common/keyboard/src/keyboard.ts","kind":"import-statement","original":"./keyboard"},{"path":"packages/common/keyboard/src/util.ts","kind":"import-statement","original":"./util"},{"path":"@inject-globals","kind":"import-statement","external":true}],"format":"esm"}},"outputs":{"packages/common/keyboard/dist/lib/browser/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":10382},"packages/common/keyboard/dist/lib/browser/index.mjs":{"imports":[{"path":"@dxos/node-std/inject-globals","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true}],"exports":["Keyboard","keySymbols","parseShortcut"],"entryPoint":"packages/common/keyboard/src/index.ts","inputs":{"inject-globals:@inject-globals":{"bytesInOutput":79},"packages/common/keyboard/src/keyboard.ts":{"bytesInOutput":3623},"packages/common/keyboard/src/index.ts":{"bytesInOutput":0},"packages/common/keyboard/src/util.ts":{"bytesInOutput":1136}},"bytes":5081}}}
|
package/dist/lib/node/index.cjs
CHANGED
|
@@ -19,17 +19,36 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
19
19
|
var node_exports = {};
|
|
20
20
|
__export(node_exports, {
|
|
21
21
|
Keyboard: () => Keyboard,
|
|
22
|
-
keySymbols: () => keySymbols
|
|
22
|
+
keySymbols: () => keySymbols,
|
|
23
|
+
parseShortcut: () => parseShortcut
|
|
23
24
|
});
|
|
24
25
|
module.exports = __toCommonJS(node_exports);
|
|
25
26
|
var import_invariant = require("@dxos/invariant");
|
|
26
|
-
var
|
|
27
|
+
var import_util = require("@dxos/util");
|
|
28
|
+
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/common/keyboard/src/keyboard.ts";
|
|
27
29
|
var modifiers = [
|
|
28
|
-
"alt",
|
|
29
30
|
"ctrl",
|
|
30
31
|
"shift",
|
|
32
|
+
"alt",
|
|
31
33
|
"meta"
|
|
32
34
|
];
|
|
35
|
+
var parseShortcut = (shortcut, delimiter = /[+-]/) => {
|
|
36
|
+
const parts = shortcut.split(delimiter);
|
|
37
|
+
const mods = modifiers.filter((key) => parts.includes(key));
|
|
38
|
+
(0, import_invariant.invariant)(mods.length === 0 || mods.length === parts.length - 1, void 0, {
|
|
39
|
+
F: __dxlog_file,
|
|
40
|
+
L: 29,
|
|
41
|
+
S: void 0,
|
|
42
|
+
A: [
|
|
43
|
+
"mods.length === 0 || mods.length === parts.length - 1",
|
|
44
|
+
""
|
|
45
|
+
]
|
|
46
|
+
});
|
|
47
|
+
return mods.length ? [
|
|
48
|
+
...mods,
|
|
49
|
+
parts[parts.length - 1]
|
|
50
|
+
].join("+") : shortcut;
|
|
51
|
+
};
|
|
33
52
|
var KeyboardContext = class {
|
|
34
53
|
constructor() {
|
|
35
54
|
this._keyMap = /* @__PURE__ */ new Map();
|
|
@@ -41,25 +60,8 @@ var KeyboardContext = class {
|
|
|
41
60
|
return this._keyMap.get(binding);
|
|
42
61
|
}
|
|
43
62
|
bind(config) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const mods = modifiers.filter((key) => parts.includes(key));
|
|
47
|
-
(0, import_invariant.invariant)(mods.length === 0 || mods.length === parts.length - 1, void 0, {
|
|
48
|
-
F: __dxlog_file,
|
|
49
|
-
L: 40,
|
|
50
|
-
S: this,
|
|
51
|
-
A: [
|
|
52
|
-
"mods.length === 0 || mods.length === parts.length - 1",
|
|
53
|
-
""
|
|
54
|
-
]
|
|
55
|
-
});
|
|
56
|
-
if (mods.length) {
|
|
57
|
-
config.binding = [
|
|
58
|
-
...mods,
|
|
59
|
-
parts[parts.length - 1]
|
|
60
|
-
].join("+");
|
|
61
|
-
}
|
|
62
|
-
this._keyMap.set(config.binding, config);
|
|
63
|
+
config.shortcut = parseShortcut(config.shortcut);
|
|
64
|
+
this._keyMap.set(config.shortcut, config);
|
|
63
65
|
}
|
|
64
66
|
unbind(binding) {
|
|
65
67
|
this._keyMap.delete(binding);
|
|
@@ -114,7 +116,7 @@ var Keyboard = class _Keyboard {
|
|
|
114
116
|
const path = this._contexts[i];
|
|
115
117
|
if (this._path.startsWith(path)) {
|
|
116
118
|
this.getContext(path).bindings.forEach((binding) => {
|
|
117
|
-
bindings.set(binding.
|
|
119
|
+
bindings.set(binding.shortcut, binding);
|
|
118
120
|
});
|
|
119
121
|
}
|
|
120
122
|
}
|
|
@@ -139,7 +141,7 @@ var Keyboard = class _Keyboard {
|
|
|
139
141
|
if (handler && (!isInput || !disableInput)) {
|
|
140
142
|
const result = handler({
|
|
141
143
|
context: path,
|
|
142
|
-
|
|
144
|
+
shortcut: str,
|
|
143
145
|
data,
|
|
144
146
|
event
|
|
145
147
|
});
|
|
@@ -154,26 +156,62 @@ var Keyboard = class _Keyboard {
|
|
|
154
156
|
}
|
|
155
157
|
}
|
|
156
158
|
};
|
|
157
|
-
var
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
159
|
+
var ctrl = {
|
|
160
|
+
macos: "\u2303",
|
|
161
|
+
ios: "\u2303",
|
|
162
|
+
windows: "Ctrl",
|
|
163
|
+
linux: "Ctrl",
|
|
164
|
+
unknown: "Ctrl"
|
|
165
|
+
};
|
|
166
|
+
var alt = {
|
|
167
|
+
macos: "\u2325",
|
|
168
|
+
ios: "\u2325",
|
|
169
|
+
windows: "Alt",
|
|
170
|
+
linux: "Alt",
|
|
171
|
+
unknown: "Alt"
|
|
172
|
+
};
|
|
173
|
+
var meta = {
|
|
174
|
+
macos: "\u2318",
|
|
175
|
+
ios: "\u2318",
|
|
176
|
+
windows: "\u229E",
|
|
177
|
+
// TODO(wittjosiah): Use ⌘ or ⊞ instead? Wait for user feedback.
|
|
178
|
+
// From https://en.wikipedia.org/wiki/Super_key_(keyboard_button).
|
|
179
|
+
linux: "\u2756",
|
|
180
|
+
unknown: "\u2756"
|
|
181
|
+
};
|
|
182
|
+
var getSymbol = (part) => {
|
|
183
|
+
const platform = (0, import_util.getHostPlatform)();
|
|
184
|
+
switch (part) {
|
|
185
|
+
case "alt":
|
|
186
|
+
return alt[platform];
|
|
187
|
+
case "ctrl":
|
|
188
|
+
return ctrl[platform];
|
|
189
|
+
case "meta":
|
|
190
|
+
return meta[platform];
|
|
191
|
+
case "shift":
|
|
192
|
+
return "\u21E7";
|
|
193
|
+
case "Backspace":
|
|
194
|
+
return "\u232B";
|
|
195
|
+
case "Enter":
|
|
196
|
+
return "\u23CE";
|
|
197
|
+
case "Escape":
|
|
198
|
+
return "\u238B";
|
|
199
|
+
case "Space":
|
|
200
|
+
return "\u2423";
|
|
201
|
+
case "Tab":
|
|
202
|
+
return "\u21E5";
|
|
203
|
+
default:
|
|
204
|
+
return part.toUpperCase();
|
|
205
|
+
}
|
|
169
206
|
};
|
|
170
207
|
var keySymbols = (keyBinding) => {
|
|
171
208
|
const parts = keyBinding.split("+");
|
|
172
|
-
return parts.map(
|
|
209
|
+
return parts.map(getSymbol);
|
|
173
210
|
};
|
|
174
211
|
// Annotate the CommonJS export names for ESM import in node:
|
|
175
212
|
0 && (module.exports = {
|
|
176
213
|
Keyboard,
|
|
177
|
-
keySymbols
|
|
214
|
+
keySymbols,
|
|
215
|
+
parseShortcut
|
|
178
216
|
});
|
|
179
217
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/keyboard.ts", "../../../src/util.ts"],
|
|
4
|
-
"sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nimport { invariant } from '@dxos/invariant';\n\nexport type KeyHandler = (props: {\n context: string;\n
|
|
5
|
-
"mappings": "
|
|
6
|
-
"names": ["modifiers", "
|
|
4
|
+
"sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nimport { invariant } from '@dxos/invariant';\n\nexport type KeyHandler = (props: {\n context: string;\n shortcut: string;\n data?: any;\n event: KeyboardEvent;\n}) => boolean | void;\n\nexport type KeyBinding = {\n shortcut: string;\n handler: KeyHandler;\n disableInput?: boolean;\n data?: any;\n};\n\n// Keybinding is normalized to this order.\n// https://support.apple.com/en-us/HT201236\nconst modifiers = ['ctrl', 'shift', 'alt', 'meta'];\n\n// Normalize order of modifiers.\nexport const parseShortcut = (shortcut: string, delimiter = /[+-]/): string => {\n const parts = shortcut.split(delimiter);\n const mods = modifiers.filter((key) => parts.includes(key));\n invariant(mods.length === 0 || mods.length === parts.length - 1);\n // Assume single natural key.\n return mods.length ? [...mods, parts[parts.length - 1]].join('+') : shortcut;\n};\n\nclass KeyboardContext {\n readonly _keyMap = new Map<string, KeyBinding>();\n\n get bindings() {\n return Array.from(this._keyMap.values());\n }\n\n get(binding: string) {\n return this._keyMap.get(binding);\n }\n\n bind(config: KeyBinding) {\n config.shortcut = parseShortcut(config.shortcut);\n this._keyMap.set(config.shortcut, config);\n }\n\n unbind(binding: string) {\n this._keyMap.delete(binding);\n }\n}\n\nconst ROOT = '';\n\n/**\n * Manages context-aware key bindings.\n */\nexport class Keyboard {\n static singleton = new Keyboard();\n\n private readonly _root = new KeyboardContext();\n private readonly _keyMap = new Map<string, KeyboardContext>([[ROOT, this._root]]);\n private readonly _contexts: string[] = [ROOT];\n private readonly _keyHandler = this.handleKeyDown.bind(this);\n private _path = ROOT;\n\n initialize() {\n document.addEventListener('keydown', this._keyHandler);\n }\n\n destroy() {\n document.removeEventListener('keydown', this._keyHandler);\n }\n\n bind = this._root.bind.bind(this._root);\n unbind = this._root.unbind.bind(this._root);\n\n setCurrentContext(path = ROOT) {\n this._path = path;\n }\n\n getCurrentContext() {\n return this._path;\n }\n\n getContext(path = ROOT): KeyboardContext {\n let context = this._keyMap.get(path);\n if (!context) {\n context = new KeyboardContext();\n this._keyMap.set(path, context);\n this._contexts.push(path);\n this._contexts.sort();\n }\n\n return context;\n }\n\n getBindings() {\n const bindings = new Map<string, KeyBinding>();\n for (let i = 0; i < this._contexts.length; ++i) {\n const path = this._contexts[i];\n if (this._path.startsWith(path)) {\n this.getContext(path).bindings.forEach((binding) => {\n bindings.set(binding.shortcut, binding);\n });\n }\n }\n\n return Array.from(bindings.values());\n }\n\n handleKeyDown(event: KeyboardEvent) {\n const { altKey, ctrlKey, metaKey, shiftKey, key } = event;\n\n if (key !== 'Alt' && key !== 'Control' && key !== 'Meta' && key !== 'Shift') {\n // Binding option to check for input or contenteditable.\n const tagName = (event.target as any)?.tagName;\n const isInput =\n tagName === 'INPUT' || tagName === 'TEXTAREA' || (event.target as any)?.getAttribute('contenteditable');\n\n // Normalized key binding.\n const str = [altKey && 'alt', ctrlKey && 'ctrl', shiftKey && 'shift', metaKey && 'meta', key]\n .filter(Boolean)\n .join('+');\n\n // Scan matching contexts.\n for (let i = this._contexts.length - 1; i >= 0; --i) {\n const path = this._contexts[i];\n if (this._path.startsWith(path)) {\n const { data, handler, disableInput } = this.getContext(path).get(str) ?? {};\n if (handler && (!isInput || !disableInput)) {\n const result = handler({ context: path, shortcut: str, data, event });\n if (result !== false) {\n // TODO(burdon): This doesn't prevent actions in markdown editor.\n // Reference: https://codemirror.net/docs/ref/#view.KeyBinding\n event.preventDefault();\n event.stopPropagation();\n }\n\n return;\n }\n }\n }\n }\n }\n}\n", "//\n// Copyright 2023 DXOS.org\n//\n\nimport { getHostPlatform } from '@dxos/util';\n\n// Resources.\n// https://www.w3.org/TR/DOM-Level-3-Events/#events-keyboardevents\n// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key\n// https://developer.apple.com/design/human-interface-guidelines/designing-for-macos\n// https://support.apple.com/en-us/HT201236\n// https://support.apple.com/guide/mac-help/what-are-those-symbols-shown-in-menus-cpmh0011/mac\n\nconst ctrl: Record<string, string> = {\n macos: '⌃',\n ios: '⌃',\n windows: 'Ctrl',\n linux: 'Ctrl',\n unknown: 'Ctrl',\n};\n\nconst alt: Record<string, string> = {\n macos: '⌥',\n ios: '⌥',\n windows: 'Alt',\n linux: 'Alt',\n unknown: 'Alt',\n};\n\nconst meta: Record<string, string> = {\n macos: '⌘',\n ios: '⌘',\n windows: '⊞',\n // TODO(wittjosiah): Use ⌘ or ⊞ instead? Wait for user feedback.\n // From https://en.wikipedia.org/wiki/Super_key_(keyboard_button).\n linux: '❖',\n unknown: '❖',\n};\n\nconst getSymbol = (part: string) => {\n const platform = getHostPlatform();\n switch (part) {\n // Mods.\n case 'alt':\n return alt[platform];\n case 'ctrl':\n return ctrl[platform];\n case 'meta':\n return meta[platform];\n case 'shift':\n return '⇧';\n // Special keys.\n case 'Backspace':\n return '⌫';\n case 'Enter':\n return '⏎';\n case 'Escape':\n return '⎋';\n case 'Space':\n return '␣';\n case 'Tab':\n return '⇥';\n default:\n return part.toUpperCase();\n }\n};\n\nexport const keySymbols = (keyBinding: string): string[] => {\n const parts = keyBinding.split('+');\n return parts.map(getSymbol);\n};\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["modifiers", "parseShortcut", "shortcut", "delimiter", "parts", "split", "mods", "filter", "key", "includes", "invariant", "length", "join", "KeyboardContext", "_keyMap", "Map", "bindings", "Array", "from", "values", "get", "binding", "bind", "config", "set", "unbind", "delete", "ROOT", "Keyboard", "_root", "_contexts", "_keyHandler", "handleKeyDown", "_path", "singleton", "initialize", "document", "addEventListener", "destroy", "removeEventListener", "setCurrentContext", "path", "getCurrentContext", "getContext", "context", "push", "sort", "getBindings", "i", "startsWith", "forEach", "event", "altKey", "ctrlKey", "metaKey", "shiftKey", "tagName", "target", "isInput", "getAttribute", "str", "Boolean", "data", "handler", "disableInput", "result", "preventDefault", "stopPropagation", "ctrl", "macos", "ios", "windows", "linux", "unknown", "alt", "meta", "getSymbol", "part", "platform", "getHostPlatform", "toUpperCase", "keySymbols", "keyBinding", "map"]
|
|
7
7
|
}
|
package/dist/lib/node/meta.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"inputs":{"packages/common/keyboard/src/keyboard.ts":{"bytes":
|
|
1
|
+
{"inputs":{"packages/common/keyboard/src/keyboard.ts":{"bytes":15531,"imports":[{"path":"@dxos/invariant","kind":"import-statement","external":true}],"format":"esm"},"packages/common/keyboard/src/util.ts":{"bytes":5602,"imports":[{"path":"@dxos/util","kind":"import-statement","external":true}],"format":"esm"},"packages/common/keyboard/src/index.ts":{"bytes":544,"imports":[{"path":"packages/common/keyboard/src/keyboard.ts","kind":"import-statement","original":"./keyboard"},{"path":"packages/common/keyboard/src/util.ts","kind":"import-statement","original":"./util"}],"format":"esm"}},"outputs":{"packages/common/keyboard/dist/lib/node/index.cjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":10373},"packages/common/keyboard/dist/lib/node/index.cjs":{"imports":[{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true}],"exports":["Keyboard","keySymbols","parseShortcut"],"entryPoint":"packages/common/keyboard/src/index.ts","inputs":{"packages/common/keyboard/src/keyboard.ts":{"bytesInOutput":3623},"packages/common/keyboard/src/index.ts":{"bytesInOutput":0},"packages/common/keyboard/src/util.ts":{"bytesInOutput":1136}},"bytes":4933}}}
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
export type KeyHandler = (props: {
|
|
2
2
|
context: string;
|
|
3
|
-
|
|
3
|
+
shortcut: string;
|
|
4
4
|
data?: any;
|
|
5
5
|
event: KeyboardEvent;
|
|
6
6
|
}) => boolean | void;
|
|
7
7
|
export type KeyBinding = {
|
|
8
|
-
|
|
8
|
+
shortcut: string;
|
|
9
9
|
handler: KeyHandler;
|
|
10
10
|
disableInput?: boolean;
|
|
11
11
|
data?: any;
|
|
12
12
|
};
|
|
13
|
+
export declare const parseShortcut: (shortcut: string, delimiter?: RegExp) => string;
|
|
13
14
|
declare class KeyboardContext {
|
|
14
15
|
readonly _keyMap: Map<string, KeyBinding>;
|
|
15
16
|
get bindings(): KeyBinding[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keyboard.d.ts","sourceRoot":"","sources":["../../../src/keyboard.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,UAAU,GAAG,CAAC,KAAK,EAAE;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,
|
|
1
|
+
{"version":3,"file":"keyboard.d.ts","sourceRoot":"","sources":["../../../src/keyboard.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,UAAU,GAAG,CAAC,KAAK,EAAE;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,KAAK,EAAE,aAAa,CAAC;CACtB,KAAK,OAAO,GAAG,IAAI,CAAC;AAErB,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,UAAU,CAAC;IACpB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,IAAI,CAAC,EAAE,GAAG,CAAC;CACZ,CAAC;AAOF,eAAO,MAAM,aAAa,aAAc,MAAM,yBAAuB,MAMpE,CAAC;AAEF,cAAM,eAAe;IACnB,QAAQ,CAAC,OAAO,0BAAiC;IAEjD,IAAI,QAAQ,iBAEX;IAED,GAAG,CAAC,OAAO,EAAE,MAAM;IAInB,IAAI,CAAC,MAAM,EAAE,UAAU;IAKvB,MAAM,CAAC,OAAO,EAAE,MAAM;CAGvB;AAID;;GAEG;AACH,qBAAa,QAAQ;IACnB,MAAM,CAAC,SAAS,WAAkB;IAElC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAyB;IAC/C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA0D;IAClF,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAoB;IAC9C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAiC;IAC7D,OAAO,CAAC,KAAK,CAAQ;IAErB,UAAU;IAIV,OAAO;IAIP,IAAI,+BAAoC;IACxC,MAAM,4BAAsC;IAE5C,iBAAiB,CAAC,IAAI,SAAO;IAI7B,iBAAiB;IAIjB,UAAU,CAAC,IAAI,SAAO,GAAG,eAAe;IAYxC,WAAW;IAcX,aAAa,CAAC,KAAK,EAAE,aAAa;CAkCnC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../../src/util.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../../src/util.ts"],"names":[],"mappings":"AAmEA,eAAO,MAAM,UAAU,eAAgB,MAAM,KAAG,MAAM,EAGrD,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/keyboard",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.11-main.00a28cb",
|
|
4
4
|
"description": "Keyboard bindings",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -16,15 +16,16 @@
|
|
|
16
16
|
"src"
|
|
17
17
|
],
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@dxos/
|
|
20
|
-
"@dxos/
|
|
19
|
+
"@dxos/invariant": "0.3.11-main.00a28cb",
|
|
20
|
+
"@dxos/node-std": "0.3.11-main.00a28cb",
|
|
21
|
+
"@dxos/util": "0.3.11-main.00a28cb"
|
|
21
22
|
},
|
|
22
23
|
"devDependencies": {
|
|
23
24
|
"react": "^18.2.0",
|
|
24
25
|
"react-dom": "^18.2.0",
|
|
25
|
-
"@dxos/react-ui-theme": "0.3.
|
|
26
|
-
"@dxos/storybook-utils": "0.3.
|
|
27
|
-
"@dxos/react-ui": "0.3.
|
|
26
|
+
"@dxos/react-ui-theme": "0.3.11-main.00a28cb",
|
|
27
|
+
"@dxos/storybook-utils": "0.3.11-main.00a28cb",
|
|
28
|
+
"@dxos/react-ui": "0.3.11-main.00a28cb"
|
|
28
29
|
},
|
|
29
30
|
"publishConfig": {
|
|
30
31
|
"access": "public"
|
package/src/keyboard.stories.tsx
CHANGED
|
@@ -18,13 +18,13 @@ const Story = () => {
|
|
|
18
18
|
const [bindings, setBindings] = useState({});
|
|
19
19
|
|
|
20
20
|
useEffect(() => {
|
|
21
|
-
const handler: KeyHandler = ({ context,
|
|
21
|
+
const handler: KeyHandler = ({ context, shortcut, data }) => console.log({ context, shorcut: shortcut, data });
|
|
22
22
|
|
|
23
23
|
Keyboard.singleton.initialize();
|
|
24
|
-
Keyboard.singleton.bind({
|
|
25
|
-
Keyboard.singleton.getContext('/space/2').bind({
|
|
26
|
-
Keyboard.singleton.getContext('/space/2/b').bind({
|
|
27
|
-
Keyboard.singleton.getContext('/space/1').bind({
|
|
24
|
+
Keyboard.singleton.bind({ shortcut: 'meta+k', handler, data: 'commands' });
|
|
25
|
+
Keyboard.singleton.getContext('/space/2').bind({ shortcut: 'meta+/', handler, data: 'space 2' });
|
|
26
|
+
Keyboard.singleton.getContext('/space/2/b').bind({ shortcut: 'shift+meta+.', handler });
|
|
27
|
+
Keyboard.singleton.getContext('/space/1').bind({ shortcut: 'meta+/', handler, data: 'space 1' });
|
|
28
28
|
|
|
29
29
|
setBindings(Keyboard.singleton.getBindings());
|
|
30
30
|
|
package/src/keyboard.test.ts
CHANGED
|
@@ -11,7 +11,7 @@ import { Keyboard } from './keyboard';
|
|
|
11
11
|
describe('keyboard', () => {
|
|
12
12
|
test('should pass', () => {
|
|
13
13
|
const keyboard = new Keyboard();
|
|
14
|
-
keyboard.bind({
|
|
14
|
+
keyboard.bind({ shortcut: 'meta+k', handler: () => true });
|
|
15
15
|
expect(keyboard.getBindings()).to.have.length(1);
|
|
16
16
|
});
|
|
17
17
|
});
|
package/src/keyboard.ts
CHANGED
|
@@ -6,20 +6,30 @@ import { invariant } from '@dxos/invariant';
|
|
|
6
6
|
|
|
7
7
|
export type KeyHandler = (props: {
|
|
8
8
|
context: string;
|
|
9
|
-
|
|
9
|
+
shortcut: string;
|
|
10
10
|
data?: any;
|
|
11
11
|
event: KeyboardEvent;
|
|
12
12
|
}) => boolean | void;
|
|
13
13
|
|
|
14
14
|
export type KeyBinding = {
|
|
15
|
-
|
|
15
|
+
shortcut: string;
|
|
16
16
|
handler: KeyHandler;
|
|
17
17
|
disableInput?: boolean;
|
|
18
18
|
data?: any;
|
|
19
19
|
};
|
|
20
20
|
|
|
21
21
|
// Keybinding is normalized to this order.
|
|
22
|
-
|
|
22
|
+
// https://support.apple.com/en-us/HT201236
|
|
23
|
+
const modifiers = ['ctrl', 'shift', 'alt', 'meta'];
|
|
24
|
+
|
|
25
|
+
// Normalize order of modifiers.
|
|
26
|
+
export const parseShortcut = (shortcut: string, delimiter = /[+-]/): string => {
|
|
27
|
+
const parts = shortcut.split(delimiter);
|
|
28
|
+
const mods = modifiers.filter((key) => parts.includes(key));
|
|
29
|
+
invariant(mods.length === 0 || mods.length === parts.length - 1);
|
|
30
|
+
// Assume single natural key.
|
|
31
|
+
return mods.length ? [...mods, parts[parts.length - 1]].join('+') : shortcut;
|
|
32
|
+
};
|
|
23
33
|
|
|
24
34
|
class KeyboardContext {
|
|
25
35
|
readonly _keyMap = new Map<string, KeyBinding>();
|
|
@@ -33,16 +43,8 @@ class KeyboardContext {
|
|
|
33
43
|
}
|
|
34
44
|
|
|
35
45
|
bind(config: KeyBinding) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const parts = binding.split('+');
|
|
39
|
-
const mods = modifiers.filter((key) => parts.includes(key));
|
|
40
|
-
invariant(mods.length === 0 || mods.length === parts.length - 1);
|
|
41
|
-
if (mods.length) {
|
|
42
|
-
config.binding = [...mods, parts[parts.length - 1]].join('+');
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
this._keyMap.set(config.binding, config);
|
|
46
|
+
config.shortcut = parseShortcut(config.shortcut);
|
|
47
|
+
this._keyMap.set(config.shortcut, config);
|
|
46
48
|
}
|
|
47
49
|
|
|
48
50
|
unbind(binding: string) {
|
|
@@ -101,7 +103,7 @@ export class Keyboard {
|
|
|
101
103
|
const path = this._contexts[i];
|
|
102
104
|
if (this._path.startsWith(path)) {
|
|
103
105
|
this.getContext(path).bindings.forEach((binding) => {
|
|
104
|
-
bindings.set(binding.
|
|
106
|
+
bindings.set(binding.shortcut, binding);
|
|
105
107
|
});
|
|
106
108
|
}
|
|
107
109
|
}
|
|
@@ -129,7 +131,7 @@ export class Keyboard {
|
|
|
129
131
|
if (this._path.startsWith(path)) {
|
|
130
132
|
const { data, handler, disableInput } = this.getContext(path).get(str) ?? {};
|
|
131
133
|
if (handler && (!isInput || !disableInput)) {
|
|
132
|
-
const result = handler({ context: path,
|
|
134
|
+
const result = handler({ context: path, shortcut: str, data, event });
|
|
133
135
|
if (result !== false) {
|
|
134
136
|
// TODO(burdon): This doesn't prevent actions in markdown editor.
|
|
135
137
|
// Reference: https://codemirror.net/docs/ref/#view.KeyBinding
|
package/src/util.ts
CHANGED
|
@@ -2,28 +2,70 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { getHostPlatform } from '@dxos/util';
|
|
6
|
+
|
|
7
|
+
// Resources.
|
|
5
8
|
// https://www.w3.org/TR/DOM-Level-3-Events/#events-keyboardevents
|
|
6
9
|
// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key
|
|
10
|
+
// https://developer.apple.com/design/human-interface-guidelines/designing-for-macos
|
|
7
11
|
// https://support.apple.com/en-us/HT201236
|
|
8
12
|
// https://support.apple.com/guide/mac-help/what-are-those-symbols-shown-in-menus-cpmh0011/mac
|
|
9
13
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
const ctrl: Record<string, string> = {
|
|
15
|
+
macos: '⌃',
|
|
16
|
+
ios: '⌃',
|
|
17
|
+
windows: 'Ctrl',
|
|
18
|
+
linux: 'Ctrl',
|
|
19
|
+
unknown: 'Ctrl',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const alt: Record<string, string> = {
|
|
23
|
+
macos: '⌥',
|
|
24
|
+
ios: '⌥',
|
|
25
|
+
windows: 'Alt',
|
|
26
|
+
linux: 'Alt',
|
|
27
|
+
unknown: 'Alt',
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const meta: Record<string, string> = {
|
|
31
|
+
macos: '⌘',
|
|
32
|
+
ios: '⌘',
|
|
33
|
+
windows: '⊞',
|
|
34
|
+
// TODO(wittjosiah): Use ⌘ or ⊞ instead? Wait for user feedback.
|
|
35
|
+
// From https://en.wikipedia.org/wiki/Super_key_(keyboard_button).
|
|
36
|
+
linux: '❖',
|
|
37
|
+
unknown: '❖',
|
|
38
|
+
};
|
|
17
39
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
40
|
+
const getSymbol = (part: string) => {
|
|
41
|
+
const platform = getHostPlatform();
|
|
42
|
+
switch (part) {
|
|
43
|
+
// Mods.
|
|
44
|
+
case 'alt':
|
|
45
|
+
return alt[platform];
|
|
46
|
+
case 'ctrl':
|
|
47
|
+
return ctrl[platform];
|
|
48
|
+
case 'meta':
|
|
49
|
+
return meta[platform];
|
|
50
|
+
case 'shift':
|
|
51
|
+
return '⇧';
|
|
52
|
+
// Special keys.
|
|
53
|
+
case 'Backspace':
|
|
54
|
+
return '⌫';
|
|
55
|
+
case 'Enter':
|
|
56
|
+
return '⏎';
|
|
57
|
+
case 'Escape':
|
|
58
|
+
return '⎋';
|
|
59
|
+
case 'Space':
|
|
60
|
+
return '␣';
|
|
61
|
+
case 'Tab':
|
|
62
|
+
return '⇥';
|
|
63
|
+
default:
|
|
64
|
+
return part.toUpperCase();
|
|
65
|
+
}
|
|
24
66
|
};
|
|
25
67
|
|
|
26
68
|
export const keySymbols = (keyBinding: string): string[] => {
|
|
27
69
|
const parts = keyBinding.split('+');
|
|
28
|
-
return parts.map(
|
|
70
|
+
return parts.map(getSymbol);
|
|
29
71
|
};
|