@jupyterlab/shortcuts-extension 4.0.0-alpha.2 → 4.0.0-alpha.21
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/lib/components/ShortcutInput.d.ts +78 -0
- package/lib/components/ShortcutInput.js +348 -0
- package/lib/components/ShortcutInput.js.map +1 -0
- package/lib/components/ShortcutItem.d.ts +62 -0
- package/lib/components/ShortcutItem.js +282 -0
- package/lib/components/ShortcutItem.js.map +1 -0
- package/lib/components/ShortcutList.d.ts +23 -0
- package/lib/components/ShortcutList.js +19 -0
- package/lib/components/ShortcutList.js.map +1 -0
- package/lib/components/ShortcutTitleItem.d.ts +9 -0
- package/lib/components/ShortcutTitleItem.js +16 -0
- package/lib/components/ShortcutTitleItem.js.map +1 -0
- package/lib/components/ShortcutUI.d.ts +56 -0
- package/lib/components/ShortcutUI.js +363 -0
- package/lib/components/ShortcutUI.js.map +1 -0
- package/lib/components/TopNav.d.ts +48 -0
- package/lib/components/TopNav.js +92 -0
- package/lib/components/TopNav.js.map +1 -0
- package/lib/components/index.d.ts +2 -0
- package/lib/components/index.js +6 -0
- package/lib/components/index.js.map +1 -0
- package/lib/index.js +48 -5
- package/lib/index.js.map +1 -1
- package/lib/renderer.d.ts +4 -0
- package/lib/renderer.js +10 -0
- package/lib/renderer.js.map +1 -0
- package/package.json +34 -15
- package/src/components/ShortcutInput.tsx +501 -0
- package/src/components/ShortcutItem.tsx +491 -0
- package/src/components/ShortcutList.tsx +61 -0
- package/src/components/ShortcutTitleItem.tsx +33 -0
- package/src/components/ShortcutUI.tsx +512 -0
- package/src/components/TopNav.tsx +193 -0
- package/src/components/index.ts +7 -0
- package/src/index.ts +297 -0
- package/src/renderer.tsx +13 -0
- package/style/base.css +393 -0
- package/style/index.css +10 -0
- package/style/index.js +10 -0
package/lib/renderer.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Jupyter Development Team.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
import React from 'react';
|
|
6
|
+
import { ShortcutUI } from './components';
|
|
7
|
+
export const renderShortCut = (props) => {
|
|
8
|
+
return React.createElement(ShortcutUI, { external: props.external, height: 1000, width: 1000 });
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=renderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderer.js","sourceRoot":"","sources":["../src/renderer.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAuB,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/D,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,KAE9B,EAAe,EAAE;IAChB,OAAO,oBAAC,UAAU,IAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,GAAI,CAAC;AAC7E,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jupyterlab/shortcuts-extension",
|
|
3
|
-
"version": "4.0.0-alpha.
|
|
3
|
+
"version": "4.0.0-alpha.21",
|
|
4
4
|
"description": "JupyterLab - Shortcuts Extension",
|
|
5
5
|
"homepage": "https://github.com/jupyterlab/jupyterlab",
|
|
6
6
|
"bugs": {
|
|
@@ -12,36 +12,54 @@
|
|
|
12
12
|
},
|
|
13
13
|
"license": "BSD-3-Clause",
|
|
14
14
|
"author": "Project Jupyter",
|
|
15
|
-
"sideEffects":
|
|
15
|
+
"sideEffects": [
|
|
16
|
+
"style/*.css",
|
|
17
|
+
"style/index.js"
|
|
18
|
+
],
|
|
16
19
|
"main": "lib/index.js",
|
|
17
20
|
"types": "lib/index.d.ts",
|
|
21
|
+
"style": "style/index.css",
|
|
18
22
|
"directories": {
|
|
19
23
|
"lib": "lib/"
|
|
20
24
|
},
|
|
21
25
|
"files": [
|
|
22
|
-
"lib
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"schema/*.json"
|
|
26
|
+
"lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}",
|
|
27
|
+
"style/**/*.{css,eot,gif,html,jpg,json,png,svg,woff2,ttf}",
|
|
28
|
+
"style/index.js",
|
|
29
|
+
"schema/*.json",
|
|
30
|
+
"src/**/*.{ts,tsx}"
|
|
26
31
|
],
|
|
27
32
|
"scripts": {
|
|
28
33
|
"build": "tsc -b",
|
|
34
|
+
"build:test": "tsc --build tsconfig.test.json",
|
|
29
35
|
"clean": "rimraf lib && rimraf tsconfig.tsbuildinfo",
|
|
30
36
|
"docs": "typedoc src",
|
|
37
|
+
"test": "jest",
|
|
38
|
+
"test:cov": "jest --collect-coverage",
|
|
39
|
+
"test:debug": "node --inspect-brk node_modules/.bin/jest --runInBand",
|
|
40
|
+
"test:debug:watch": "node --inspect-brk node_modules/.bin/jest --runInBand --watch",
|
|
31
41
|
"watch": "tsc -b --watch"
|
|
32
42
|
},
|
|
33
43
|
"dependencies": {
|
|
34
|
-
"@jupyterlab/application": "^4.0.0-alpha.
|
|
35
|
-
"@jupyterlab/settingregistry": "^4.0.0-alpha.
|
|
36
|
-
"@jupyterlab/translation": "^4.0.0-alpha.
|
|
37
|
-
"@
|
|
38
|
-
"@lumino/
|
|
39
|
-
"@lumino/
|
|
44
|
+
"@jupyterlab/application": "^4.0.0-alpha.21",
|
|
45
|
+
"@jupyterlab/settingregistry": "^4.0.0-alpha.21",
|
|
46
|
+
"@jupyterlab/translation": "^4.0.0-alpha.21",
|
|
47
|
+
"@jupyterlab/ui-components": "^4.0.0-alpha.36",
|
|
48
|
+
"@lumino/algorithm": "^2.0.0-rc.1",
|
|
49
|
+
"@lumino/commands": "^2.0.0-rc.1",
|
|
50
|
+
"@lumino/coreutils": "^2.0.0-rc.1",
|
|
51
|
+
"@lumino/disposable": "^2.0.0-rc.1",
|
|
52
|
+
"@lumino/domutils": "^2.0.0-rc.1",
|
|
53
|
+
"@lumino/keyboard": "^2.0.0-rc.1",
|
|
54
|
+
"@lumino/widgets": "^2.0.0-rc.1",
|
|
55
|
+
"react": "^18.2.0"
|
|
40
56
|
},
|
|
41
57
|
"devDependencies": {
|
|
58
|
+
"@jupyterlab/testing": "^4.0.0-alpha.21",
|
|
59
|
+
"@types/jest": "^29.2.0",
|
|
42
60
|
"rimraf": "~3.0.0",
|
|
43
|
-
"typedoc": "~0.
|
|
44
|
-
"typescript": "~
|
|
61
|
+
"typedoc": "~0.23.25",
|
|
62
|
+
"typescript": "~5.0.1-rc"
|
|
45
63
|
},
|
|
46
64
|
"publishConfig": {
|
|
47
65
|
"access": "public"
|
|
@@ -49,5 +67,6 @@
|
|
|
49
67
|
"jupyterlab": {
|
|
50
68
|
"extension": true,
|
|
51
69
|
"schemaDir": "schema"
|
|
52
|
-
}
|
|
70
|
+
},
|
|
71
|
+
"styleModule": "style/index.js"
|
|
53
72
|
}
|
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Jupyter Development Team.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as React from 'react';
|
|
7
|
+
|
|
8
|
+
import { ITranslator } from '@jupyterlab/translation';
|
|
9
|
+
|
|
10
|
+
import { EN_US } from '@lumino/keyboard';
|
|
11
|
+
import { checkIcon, errorIcon } from '@jupyterlab/ui-components';
|
|
12
|
+
|
|
13
|
+
export interface IShortcutInputProps {
|
|
14
|
+
handleUpdate: Function;
|
|
15
|
+
deleteShortcut: Function;
|
|
16
|
+
toggleInput: Function;
|
|
17
|
+
shortcut: ShortcutObject;
|
|
18
|
+
shortcutId: string;
|
|
19
|
+
toSymbols: Function;
|
|
20
|
+
keyBindingsUsed: { [index: string]: TakenByObject };
|
|
21
|
+
sortConflict: Function;
|
|
22
|
+
clearConflicts: Function;
|
|
23
|
+
displayInput: boolean;
|
|
24
|
+
newOrReplace: string;
|
|
25
|
+
placeholder: string;
|
|
26
|
+
translator: ITranslator;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface IShortcutInputState {
|
|
30
|
+
value: string;
|
|
31
|
+
userInput: string;
|
|
32
|
+
isAvailable: boolean;
|
|
33
|
+
isFunctional: boolean;
|
|
34
|
+
takenByObject: TakenByObject;
|
|
35
|
+
keys: Array<string>;
|
|
36
|
+
currentChain: string;
|
|
37
|
+
selected: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Object for shortcut items */
|
|
41
|
+
export class ShortcutObject {
|
|
42
|
+
commandName: string;
|
|
43
|
+
label: string;
|
|
44
|
+
keys: { [index: string]: Array<string> };
|
|
45
|
+
source: string;
|
|
46
|
+
selector: string;
|
|
47
|
+
category: string;
|
|
48
|
+
id: string;
|
|
49
|
+
hasConflict: boolean;
|
|
50
|
+
numberOfShortcuts: number;
|
|
51
|
+
|
|
52
|
+
constructor() {
|
|
53
|
+
this.commandName = '';
|
|
54
|
+
this.label = '';
|
|
55
|
+
this.keys = {};
|
|
56
|
+
this.source = '';
|
|
57
|
+
this.selector = '';
|
|
58
|
+
this.category = '';
|
|
59
|
+
this.id = '';
|
|
60
|
+
this.numberOfShortcuts = 0;
|
|
61
|
+
this.hasConflict = false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
get(sortCriteria: string): string {
|
|
65
|
+
if (sortCriteria === 'label') {
|
|
66
|
+
return this.label;
|
|
67
|
+
} else if (sortCriteria === 'selector') {
|
|
68
|
+
return this.selector;
|
|
69
|
+
} else if (sortCriteria === 'category') {
|
|
70
|
+
return this.category;
|
|
71
|
+
} else if (sortCriteria === 'source') {
|
|
72
|
+
return this.source;
|
|
73
|
+
} else {
|
|
74
|
+
return '';
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** Object for conflicting shortcut error messages */
|
|
79
|
+
export class ErrorObject extends ShortcutObject {
|
|
80
|
+
takenBy: TakenByObject;
|
|
81
|
+
|
|
82
|
+
constructor() {
|
|
83
|
+
super();
|
|
84
|
+
this.takenBy = new TakenByObject();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Object for showing which shortcut conflicts with the new one */
|
|
89
|
+
export class TakenByObject {
|
|
90
|
+
takenBy: ShortcutObject;
|
|
91
|
+
takenByKey: string;
|
|
92
|
+
takenByLabel: string;
|
|
93
|
+
id: string;
|
|
94
|
+
|
|
95
|
+
constructor(shortcut?: ShortcutObject) {
|
|
96
|
+
if (shortcut) {
|
|
97
|
+
this.takenBy = shortcut;
|
|
98
|
+
this.takenByKey = '';
|
|
99
|
+
this.takenByLabel = shortcut.category + ': ' + shortcut.label;
|
|
100
|
+
this.id = shortcut.commandName + '_' + shortcut.selector;
|
|
101
|
+
} else {
|
|
102
|
+
this.takenBy = new ShortcutObject();
|
|
103
|
+
this.takenByKey = '';
|
|
104
|
+
this.takenByLabel = '';
|
|
105
|
+
this.id = '';
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export class ShortcutInput extends React.Component<
|
|
111
|
+
IShortcutInputProps,
|
|
112
|
+
IShortcutInputState
|
|
113
|
+
> {
|
|
114
|
+
constructor(props: IShortcutInputProps) {
|
|
115
|
+
super(props);
|
|
116
|
+
|
|
117
|
+
this.state = {
|
|
118
|
+
value: this.props.placeholder,
|
|
119
|
+
userInput: '',
|
|
120
|
+
isAvailable: true,
|
|
121
|
+
isFunctional: this.props.newOrReplace === 'replace',
|
|
122
|
+
takenByObject: new TakenByObject(),
|
|
123
|
+
keys: new Array<string>(),
|
|
124
|
+
currentChain: '',
|
|
125
|
+
selected: true
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
handleUpdate = () => {
|
|
130
|
+
let keys = this.state.keys;
|
|
131
|
+
keys.push(this.state.currentChain);
|
|
132
|
+
this.setState({ keys: keys });
|
|
133
|
+
this.props.handleUpdate(this.props.shortcut, this.state.keys);
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
handleOverwrite = async () => {
|
|
137
|
+
this.props
|
|
138
|
+
.deleteShortcut(
|
|
139
|
+
this.state.takenByObject.takenBy,
|
|
140
|
+
this.state.takenByObject.takenByKey
|
|
141
|
+
)
|
|
142
|
+
.then(this.handleUpdate());
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
handleReplace = async () => {
|
|
146
|
+
let keys = this.state.keys;
|
|
147
|
+
keys.push(this.state.currentChain);
|
|
148
|
+
this.props.toggleInput();
|
|
149
|
+
await this.props.deleteShortcut(this.props.shortcut, this.props.shortcutId);
|
|
150
|
+
this.props.handleUpdate(this.props.shortcut, keys);
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
/** Parse user input for chained shortcuts */
|
|
154
|
+
parseChaining = (
|
|
155
|
+
event: React.KeyboardEvent,
|
|
156
|
+
value: string,
|
|
157
|
+
userInput: string,
|
|
158
|
+
keys: Array<string>,
|
|
159
|
+
currentChain: string
|
|
160
|
+
): Array<any> => {
|
|
161
|
+
let key = EN_US.keyForKeydownEvent(event.nativeEvent);
|
|
162
|
+
|
|
163
|
+
const modKeys = ['Shift', 'Control', 'Alt', 'Meta', 'Ctrl', 'Accel'];
|
|
164
|
+
|
|
165
|
+
if (event.key === 'Backspace') {
|
|
166
|
+
userInput = '';
|
|
167
|
+
value = '';
|
|
168
|
+
keys = [];
|
|
169
|
+
currentChain = '';
|
|
170
|
+
this.setState({
|
|
171
|
+
value: value,
|
|
172
|
+
userInput: userInput,
|
|
173
|
+
keys: keys,
|
|
174
|
+
currentChain: currentChain
|
|
175
|
+
});
|
|
176
|
+
} else if (event.key !== 'CapsLock') {
|
|
177
|
+
const lastKey = userInput
|
|
178
|
+
.substr(userInput.lastIndexOf(' ') + 1, userInput.length)
|
|
179
|
+
.trim();
|
|
180
|
+
|
|
181
|
+
/** if last key was not a modefier then there is a chain */
|
|
182
|
+
if (modKeys.lastIndexOf(lastKey) === -1 && lastKey != '') {
|
|
183
|
+
userInput = userInput + ',';
|
|
184
|
+
keys.push(currentChain);
|
|
185
|
+
currentChain = '';
|
|
186
|
+
|
|
187
|
+
/** check if a modefier key was held down through chain */
|
|
188
|
+
if (event.ctrlKey && event.key != 'Control') {
|
|
189
|
+
userInput = (userInput + ' Ctrl').trim();
|
|
190
|
+
currentChain = (currentChain + ' Ctrl').trim();
|
|
191
|
+
}
|
|
192
|
+
if (event.metaKey && event.key != 'Meta') {
|
|
193
|
+
userInput = (userInput + ' Accel').trim();
|
|
194
|
+
currentChain = (currentChain + ' Accel').trim();
|
|
195
|
+
}
|
|
196
|
+
if (event.altKey && event.key != 'Alt') {
|
|
197
|
+
userInput = (userInput + ' Alt').trim();
|
|
198
|
+
currentChain = (currentChain + ' Alt').trim();
|
|
199
|
+
}
|
|
200
|
+
if (event.shiftKey && event.key != 'Shift') {
|
|
201
|
+
userInput = (userInput + ' Shift').trim();
|
|
202
|
+
currentChain = (currentChain + ' Shift').trim();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** if not a modefier key, add to user input and current chain */
|
|
206
|
+
if (modKeys.lastIndexOf(event.key) === -1) {
|
|
207
|
+
userInput = (userInput + ' ' + key).trim();
|
|
208
|
+
currentChain = (currentChain + ' ' + key).trim();
|
|
209
|
+
|
|
210
|
+
/** if a modefier key, add to user input and current chain */
|
|
211
|
+
} else {
|
|
212
|
+
if (event.key === 'Meta') {
|
|
213
|
+
userInput = (userInput + ' Accel').trim();
|
|
214
|
+
currentChain = (currentChain + ' Accel').trim();
|
|
215
|
+
} else if (event.key === 'Control') {
|
|
216
|
+
userInput = (userInput + ' Ctrl').trim();
|
|
217
|
+
currentChain = (currentChain + ' Ctrl').trim();
|
|
218
|
+
} else if (event.key === 'Shift') {
|
|
219
|
+
userInput = (userInput + ' Shift').trim();
|
|
220
|
+
currentChain = (currentChain + ' Shift').trim();
|
|
221
|
+
} else if (event.key === 'Alt') {
|
|
222
|
+
userInput = (userInput + ' Alt').trim();
|
|
223
|
+
currentChain = (currentChain + ' Alt').trim();
|
|
224
|
+
} else {
|
|
225
|
+
userInput = (userInput + ' ' + event.key).trim();
|
|
226
|
+
currentChain = (currentChain + ' ' + event.key).trim();
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** if not a chain, add the key to user input and current chain */
|
|
231
|
+
} else {
|
|
232
|
+
/** if modefier key, rename */
|
|
233
|
+
if (event.key === 'Control') {
|
|
234
|
+
userInput = (userInput + ' Ctrl').trim();
|
|
235
|
+
currentChain = (currentChain + ' Ctrl').trim();
|
|
236
|
+
} else if (event.key === 'Meta') {
|
|
237
|
+
userInput = (userInput + ' Accel').trim();
|
|
238
|
+
currentChain = (currentChain + ' Accel').trim();
|
|
239
|
+
} else if (event.key === 'Shift') {
|
|
240
|
+
userInput = (userInput + ' Shift').trim();
|
|
241
|
+
currentChain = (currentChain + ' Shift').trim();
|
|
242
|
+
} else if (event.key === 'Alt') {
|
|
243
|
+
userInput = (userInput + ' Alt').trim();
|
|
244
|
+
currentChain = (currentChain + ' Alt').trim();
|
|
245
|
+
|
|
246
|
+
/** if not a modefier key, add it regularly */
|
|
247
|
+
} else {
|
|
248
|
+
userInput = (userInput + ' ' + key).trim();
|
|
249
|
+
currentChain = (currentChain + ' ' + key).trim();
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/** update state of keys and currentChain */
|
|
255
|
+
this.setState({
|
|
256
|
+
keys: keys,
|
|
257
|
+
currentChain: currentChain
|
|
258
|
+
});
|
|
259
|
+
return [userInput, keys, currentChain];
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Check if shorcut being typed will work
|
|
264
|
+
* (does not end with ctrl, alt, command, or shift)
|
|
265
|
+
* */
|
|
266
|
+
checkNonFunctional = (shortcut: string): boolean => {
|
|
267
|
+
const dontEnd = ['Ctrl', 'Alt', 'Accel', 'Shift'];
|
|
268
|
+
const shortcutKeys = this.state.currentChain.split(' ');
|
|
269
|
+
const last = shortcutKeys[shortcutKeys.length - 1];
|
|
270
|
+
this.setState({
|
|
271
|
+
isFunctional: !(dontEnd.indexOf(last) !== -1)
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
return dontEnd.indexOf(last) !== -1;
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
/** Check if shortcut being typed is already taken */
|
|
278
|
+
checkShortcutAvailability = (
|
|
279
|
+
userInput: string,
|
|
280
|
+
keys: string[],
|
|
281
|
+
currentChain: string
|
|
282
|
+
): TakenByObject => {
|
|
283
|
+
/** First, check whole shortcut */
|
|
284
|
+
let isAvailable =
|
|
285
|
+
Object.keys(this.props.keyBindingsUsed).indexOf(
|
|
286
|
+
keys.join(' ') + currentChain + '_' + this.props.shortcut.selector
|
|
287
|
+
) === -1 || userInput === '';
|
|
288
|
+
let takenByObject: TakenByObject = new TakenByObject();
|
|
289
|
+
if (isAvailable) {
|
|
290
|
+
/** Next, check each piece of a chain */
|
|
291
|
+
for (let binding of keys) {
|
|
292
|
+
if (
|
|
293
|
+
Object.keys(this.props.keyBindingsUsed).indexOf(
|
|
294
|
+
binding + '_' + this.props.shortcut.selector
|
|
295
|
+
) !== -1 &&
|
|
296
|
+
binding !== ''
|
|
297
|
+
) {
|
|
298
|
+
isAvailable = false;
|
|
299
|
+
takenByObject =
|
|
300
|
+
this.props.keyBindingsUsed[
|
|
301
|
+
binding + '_' + this.props.shortcut.selector
|
|
302
|
+
];
|
|
303
|
+
break;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/** Check current chain */
|
|
308
|
+
if (
|
|
309
|
+
isAvailable &&
|
|
310
|
+
Object.keys(this.props.keyBindingsUsed).indexOf(
|
|
311
|
+
currentChain + '_' + this.props.shortcut.selector
|
|
312
|
+
) !== -1 &&
|
|
313
|
+
currentChain !== ''
|
|
314
|
+
) {
|
|
315
|
+
isAvailable = false;
|
|
316
|
+
takenByObject =
|
|
317
|
+
this.props.keyBindingsUsed[
|
|
318
|
+
currentChain + '_' + this.props.shortcut.selector
|
|
319
|
+
];
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/** If unavailable set takenByObject */
|
|
323
|
+
} else {
|
|
324
|
+
takenByObject =
|
|
325
|
+
this.props.keyBindingsUsed[
|
|
326
|
+
keys.join(' ') + currentChain + '_' + this.props.shortcut.selector
|
|
327
|
+
];
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/** allow to set shortcut to what it initially was if replacing */
|
|
331
|
+
if (!isAvailable) {
|
|
332
|
+
if (
|
|
333
|
+
takenByObject.takenBy.id === this.props.shortcut.id &&
|
|
334
|
+
this.props.newOrReplace === 'replace'
|
|
335
|
+
) {
|
|
336
|
+
isAvailable = true;
|
|
337
|
+
takenByObject = new TakenByObject();
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
this.setState({ isAvailable: isAvailable });
|
|
342
|
+
return takenByObject;
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
checkConflict(takenByObject: TakenByObject, keys: string): void {
|
|
346
|
+
if (
|
|
347
|
+
takenByObject.id !== '' &&
|
|
348
|
+
takenByObject.takenBy.id !== this.props.shortcut.id
|
|
349
|
+
) {
|
|
350
|
+
this.props.sortConflict(
|
|
351
|
+
this.props.shortcut,
|
|
352
|
+
takenByObject,
|
|
353
|
+
takenByObject.takenByLabel,
|
|
354
|
+
''
|
|
355
|
+
);
|
|
356
|
+
} else {
|
|
357
|
+
this.props.clearConflicts();
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/** Parse and normalize user input */
|
|
362
|
+
handleInput = (event: React.KeyboardEvent): void => {
|
|
363
|
+
event.preventDefault();
|
|
364
|
+
this.setState({ selected: false });
|
|
365
|
+
const parsed = this.parseChaining(
|
|
366
|
+
event,
|
|
367
|
+
this.state.value,
|
|
368
|
+
this.state.userInput,
|
|
369
|
+
this.state.keys,
|
|
370
|
+
this.state.currentChain
|
|
371
|
+
);
|
|
372
|
+
const userInput = parsed[0];
|
|
373
|
+
const keys = parsed[1];
|
|
374
|
+
const currentChain = parsed[2];
|
|
375
|
+
|
|
376
|
+
const value = this.props.toSymbols(userInput);
|
|
377
|
+
let takenByObject = this.checkShortcutAvailability(
|
|
378
|
+
userInput,
|
|
379
|
+
keys,
|
|
380
|
+
currentChain
|
|
381
|
+
);
|
|
382
|
+
this.checkConflict(takenByObject, keys);
|
|
383
|
+
|
|
384
|
+
this.setState(
|
|
385
|
+
{
|
|
386
|
+
value: value,
|
|
387
|
+
userInput: userInput,
|
|
388
|
+
takenByObject: takenByObject,
|
|
389
|
+
keys: keys,
|
|
390
|
+
currentChain: currentChain
|
|
391
|
+
},
|
|
392
|
+
() => this.checkNonFunctional(this.state.userInput)
|
|
393
|
+
);
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
handleBlur = (event: React.FocusEvent<HTMLDivElement>) => {
|
|
397
|
+
if (
|
|
398
|
+
event.relatedTarget === null ||
|
|
399
|
+
((event.relatedTarget as HTMLElement).id !== 'no-blur' &&
|
|
400
|
+
(event.relatedTarget as HTMLElement).id !== 'overwrite')
|
|
401
|
+
) {
|
|
402
|
+
this.props.toggleInput();
|
|
403
|
+
this.setState({
|
|
404
|
+
value: '',
|
|
405
|
+
userInput: ''
|
|
406
|
+
});
|
|
407
|
+
this.props.clearConflicts();
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
render() {
|
|
412
|
+
const trans = this.props.translator.load('jupyterlab');
|
|
413
|
+
let inputClassName = 'jp-Shortcuts-Input';
|
|
414
|
+
if (!this.state.isAvailable) {
|
|
415
|
+
inputClassName += ' jp-mod-unavailable-Input';
|
|
416
|
+
}
|
|
417
|
+
return (
|
|
418
|
+
<div
|
|
419
|
+
className={
|
|
420
|
+
this.props.displayInput
|
|
421
|
+
? this.props.newOrReplace === 'new'
|
|
422
|
+
? 'jp-Shortcuts-InputBox jp-Shortcuts-InputBoxNew'
|
|
423
|
+
: 'jp-Shortcuts-InputBox'
|
|
424
|
+
: 'jp-mod-hidden'
|
|
425
|
+
}
|
|
426
|
+
onBlur={event => this.handleBlur(event)}
|
|
427
|
+
>
|
|
428
|
+
<div
|
|
429
|
+
tabIndex={0}
|
|
430
|
+
id="no-blur"
|
|
431
|
+
className={inputClassName}
|
|
432
|
+
onKeyDown={this.handleInput}
|
|
433
|
+
ref={input => input && input.focus()}
|
|
434
|
+
>
|
|
435
|
+
<p
|
|
436
|
+
className={
|
|
437
|
+
this.state.selected && this.props.newOrReplace === 'replace'
|
|
438
|
+
? 'jp-Shortcuts-InputText jp-mod-selected-InputText'
|
|
439
|
+
: this.state.value === ''
|
|
440
|
+
? 'jp-Shortcuts-InputText jp-mod-waiting-InputText'
|
|
441
|
+
: 'jp-Shortcuts-InputText'
|
|
442
|
+
}
|
|
443
|
+
>
|
|
444
|
+
{this.state.value === ''
|
|
445
|
+
? trans.__('press keys')
|
|
446
|
+
: this.state.value}
|
|
447
|
+
</p>
|
|
448
|
+
</div>
|
|
449
|
+
<button
|
|
450
|
+
className={
|
|
451
|
+
!this.state.isFunctional
|
|
452
|
+
? 'jp-Shortcuts-Submit jp-mod-defunc-Submit'
|
|
453
|
+
: !this.state.isAvailable
|
|
454
|
+
? 'jp-Shortcuts-Submit jp-mod-conflict-Submit'
|
|
455
|
+
: 'jp-Shortcuts-Submit'
|
|
456
|
+
}
|
|
457
|
+
id={'no-blur'}
|
|
458
|
+
disabled={!this.state.isAvailable || !this.state.isFunctional}
|
|
459
|
+
onClick={() => {
|
|
460
|
+
if (this.props.newOrReplace === 'new') {
|
|
461
|
+
this.handleUpdate();
|
|
462
|
+
this.setState({
|
|
463
|
+
value: '',
|
|
464
|
+
keys: [],
|
|
465
|
+
currentChain: ''
|
|
466
|
+
});
|
|
467
|
+
this.props.toggleInput();
|
|
468
|
+
} else {
|
|
469
|
+
/** don't replace if field has not been edited */
|
|
470
|
+
if (this.state.selected) {
|
|
471
|
+
this.props.toggleInput();
|
|
472
|
+
this.setState({
|
|
473
|
+
value: '',
|
|
474
|
+
userInput: ''
|
|
475
|
+
});
|
|
476
|
+
this.props.clearConflicts();
|
|
477
|
+
} else {
|
|
478
|
+
void this.handleReplace();
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}}
|
|
482
|
+
>
|
|
483
|
+
{this.state.isAvailable ? <checkIcon.react /> : <errorIcon.react />}
|
|
484
|
+
</button>
|
|
485
|
+
{!this.state.isAvailable && (
|
|
486
|
+
<button
|
|
487
|
+
hidden
|
|
488
|
+
id="overwrite"
|
|
489
|
+
onClick={() => {
|
|
490
|
+
void this.handleOverwrite();
|
|
491
|
+
this.props.clearConflicts();
|
|
492
|
+
this.props.toggleInput();
|
|
493
|
+
}}
|
|
494
|
+
>
|
|
495
|
+
{trans.__('Overwrite')}
|
|
496
|
+
</button>
|
|
497
|
+
)}
|
|
498
|
+
</div>
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
}
|