Skip to content

Commit cbc82da

Browse files
committed
[#3444] Add key-maps
This PR add more shortcuts to the map which user can change.
1 parent 425818a commit cbc82da

File tree

5 files changed

+137
-73
lines changed

5 files changed

+137
-73
lines changed

client/modules/IDE/components/Editor/contexts.jsx

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
import React, { useContext } from 'react';
22
import PropTypes from 'prop-types';
33
import { createContext, useState } from 'react';
4+
import { metaKey } from '../../../../utils/metaKey';
45

56
export const EditorKeyMapsContext = createContext();
67

78
export function EditorKeyMapProvider({ children }) {
8-
const [keyMaps, setKeyMaps] = useState({ tidy: 'Shift-Ctrl-F' });
9+
const [keyMaps, setKeyMaps] = useState({
10+
tidy: `Shift-${metaKey}-F`,
11+
findPersistent: `${metaKey}-F`,
12+
findPersistentNext: `${metaKey}-G`,
13+
findPersistentPrev: `Shift-${metaKey}-G`,
14+
colorPicker: `${metaKey}-K`
15+
});
916

1017
const updateKeyMap = (key, value) => {
1118
if (key in keyMaps) {

client/modules/IDE/components/Editor/index.jsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -520,15 +520,15 @@ class Editor extends React.Component {
520520
[`Shift-Tab`]: false,
521521
[`${metaKey}-Enter`]: () => null,
522522
[`Shift-${metaKey}-Enter`]: () => null,
523-
[`${metaKey}-F`]: 'findPersistent',
523+
[`${keyMaps.findPersistent}`]: 'findPersistent',
524524
[`${keyMaps.tidy}`]: this.tidyCode,
525-
[`${metaKey}-G`]: 'findPersistentNext',
526-
[`Shift-${metaKey}-G`]: 'findPersistentPrev',
525+
[`${keyMaps.findPersistentNext}`]: 'findPersistentNext',
526+
[`${keyMaps.findPersistentPrev}`]: 'findPersistentPrev',
527527
[replaceCommand]: 'replace',
528528
// Cassie Tarakajian: If you don't set a default color, then when you
529529
// choose a color, it deletes characters inline. This is a
530530
// hack to prevent that.
531-
[`${metaKey}-K`]: (cm, event) =>
531+
[`${keyMaps.colorPicker}`]: (cm, event) =>
532532
cm.state.colorpicker.popup_color_picker({ length: 0 }),
533533
[`${metaKey}-.`]: 'toggleComment' // Note: most adblockers use the shortcut ctrl+.
534534
});

client/modules/IDE/components/KeyboardShortcutItem.jsx

+104-45
Original file line numberDiff line numberDiff line change
@@ -2,78 +2,137 @@ import React, { useRef, useState } from 'react';
22
import PropTypes from 'prop-types';
33
import { useEditorKeyMap } from './Editor/contexts';
44

5-
function KeyboardShortcutItem({ shortcut, desc }) {
5+
function KeyboardShortcutItem({ desc, keyName }) {
66
const [edit, setEdit] = useState(false);
77
const pressedKeyCombination = useRef({});
88
const inputRef = useRef(null);
9-
const { updateKeyMap } = useEditorKeyMap();
9+
const { updateKeyMap, keyMaps } = useEditorKeyMap();
1010

11-
const handleEdit = (state) => {
11+
if (!Object.keys(keyMaps).includes(keyName)) {
12+
return null;
13+
}
14+
15+
const cancelEdit = () => {
16+
setEdit(false);
17+
pressedKeyCombination.current = {};
18+
inputRef.current.innerText = keyMaps[keyName];
19+
};
20+
21+
const handleEdit = (state, key) => {
1222
setEdit(state);
13-
if (state) {
14-
inputRef.current.focus();
15-
} else {
16-
inputRef.current.blur();
17-
updateKeyMap('tidy', inputRef.current.innerText);
23+
if (!state) {
24+
updateKeyMap(key, inputRef.current.innerText);
25+
cancelEdit();
26+
}
27+
};
28+
29+
const handleKeyDown = (event) => {
30+
if (!edit) return;
31+
event.preventDefault();
32+
event.stopPropagation();
33+
let { key } = event;
34+
if (key === 'Control') {
35+
key = 'Ctrl';
36+
}
37+
if (key === ' ') {
38+
key = 'Space';
39+
}
40+
if (key.length === 1 && key.match(/[a-z]/i)) {
41+
key = key.toUpperCase();
42+
}
43+
44+
pressedKeyCombination.current[key] = true;
45+
46+
const allKeys = Object.keys(pressedKeyCombination.current).filter(
47+
(k) => !['Shift', 'Ctrl', 'Alt'].includes(k)
48+
);
49+
50+
if (event.altKey) {
51+
allKeys.unshift('Alt');
52+
}
53+
if (event.ctrlKey) {
54+
allKeys.unshift('Ctrl');
55+
}
56+
if (event.shiftKey) {
57+
allKeys.unshift('Shift');
1858
}
59+
60+
event.currentTarget.innerText = allKeys.join('-');
61+
};
62+
63+
const handleKeyUp = (event) => {
64+
if (!edit) return;
65+
event.preventDefault();
66+
event.stopPropagation();
67+
let { key } = event;
68+
if (key === 'Control') {
69+
key = 'Ctrl';
70+
}
71+
if (key === ' ') {
72+
key = 'Space';
73+
}
74+
if (key.length === 1 && key.match(/[a-z]/i)) {
75+
key = key.toUpperCase();
76+
}
77+
78+
delete pressedKeyCombination.current[key];
1979
};
2080

2181
return (
2282
<li className="keyboard-shortcut-item">
23-
<button type="button" title="edit" onClick={() => handleEdit(!edit)}>
83+
<button
84+
type="button"
85+
title="edit shortcut"
86+
className="keyboard-shortcut__edit"
87+
style={{
88+
display: edit ? 'none' : 'block'
89+
}}
90+
onClick={() => handleEdit(true, keyName)}
91+
>
2492
&#x270E;
2593
</button>
94+
<button
95+
type="button"
96+
title="cancel shortcut edit"
97+
className="keyboard-shortcut__edit"
98+
style={{
99+
display: !edit ? 'none' : 'block'
100+
}}
101+
onClick={cancelEdit}
102+
>
103+
&#10799;
104+
</button>
105+
<button
106+
type="button"
107+
title="save shortcut"
108+
className="keyboard-shortcut__edit"
109+
style={{
110+
display: !edit ? 'none' : 'block'
111+
}}
112+
onClick={() => handleEdit(false, keyName)}
113+
>
114+
&#10003;
115+
</button>
26116
<span
27117
className="keyboard-shortcut__command"
28118
role="textbox"
29119
ref={inputRef}
30120
tabIndex={0}
31121
contentEditable={edit}
32122
suppressContentEditableWarning
33-
onKeyDown={(event) => {
34-
if (!edit) return;
35-
36-
event.preventDefault();
37-
event.stopPropagation();
38-
let { key } = event;
39-
if (key === 'Control') {
40-
key = 'Ctrl';
41-
}
42-
if (key === ' ') {
43-
key = 'Space';
44-
}
45-
46-
pressedKeyCombination.current[key] = true;
47-
48-
event.currentTarget.innerText = Object.keys(
49-
pressedKeyCombination.current
50-
).join('-');
51-
}}
52-
onKeyUp={(event) => {
53-
if (!edit) return;
54-
event.preventDefault();
55-
event.stopPropagation();
56-
let { key } = event;
57-
if (key === 'Control') {
58-
key = 'Ctrl';
59-
}
60-
if (key === ' ') {
61-
key = 'Space';
62-
}
63-
64-
delete pressedKeyCombination.current[key];
65-
}}
123+
onKeyDown={handleKeyDown}
124+
onKeyUp={handleKeyUp}
66125
>
67-
{shortcut}
126+
{keyMaps[keyName]}
68127
</span>
69128
<span>{desc}</span>
70129
</li>
71130
);
72131
}
73132

74133
KeyboardShortcutItem.propTypes = {
75-
shortcut: PropTypes.string.isRequired,
76-
desc: PropTypes.string.isRequired
134+
desc: PropTypes.string.isRequired,
135+
keyName: PropTypes.string.isRequired
77136
};
78137

79138
export default KeyboardShortcutItem;

client/modules/IDE/components/KeyboardShortcutModal.jsx

+17-23
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@ import React from 'react';
22
import { useTranslation } from 'react-i18next';
33
import { metaKeyName, metaKey } from '../../../utils/metaKey';
44
import KeyboardShortcutItem from './KeyboardShortcutItem';
5-
import { useEditorKeyMap } from './Editor/contexts';
65

76
function KeyboardShortcutModal() {
87
const { t } = useTranslation();
9-
const { keyMaps } = useEditorKeyMap();
108

119
const replaceCommand =
1210
metaKey === 'Ctrl' ? `${metaKeyName} + H` : `${metaKeyName} + ⌥ + F`;
@@ -30,25 +28,21 @@ function KeyboardShortcutModal() {
3028
</p>
3129
<ul className="keyboard-shortcuts__list">
3230
<KeyboardShortcutItem
33-
shortcut={keyMaps.tidy}
3431
desc={t('KeyboardShortcuts.CodeEditing.Tidy')}
32+
keyName="tidy"
33+
/>
34+
<KeyboardShortcutItem
35+
desc={t('KeyboardShortcuts.CodeEditing.FindText')}
36+
keyName="findPersistent"
37+
/>
38+
<KeyboardShortcutItem
39+
desc={t('KeyboardShortcuts.CodeEditing.FindNextTextMatch')}
40+
keyName="findPersistentNext"
41+
/>
42+
<KeyboardShortcutItem
43+
desc={t('KeyboardShortcuts.CodeEditing.FindPreviousTextMatch')}
44+
keyName="findPersistentPrev"
3545
/>
36-
<li className="keyboard-shortcut-item">
37-
<span className="keyboard-shortcut__command">{metaKeyName} + F</span>
38-
<span>{t('KeyboardShortcuts.CodeEditing.FindText')}</span>
39-
</li>
40-
<li className="keyboard-shortcut-item">
41-
<span className="keyboard-shortcut__command">{metaKeyName} + G</span>
42-
<span>{t('KeyboardShortcuts.CodeEditing.FindNextTextMatch')}</span>
43-
</li>
44-
<li className="keyboard-shortcut-item">
45-
<span className="keyboard-shortcut__command">
46-
{metaKeyName} + Shift + G
47-
</span>
48-
<span>
49-
{t('KeyboardShortcuts.CodeEditing.FindPreviousTextMatch')}
50-
</span>
51-
</li>
5246
<li className="keyboard-shortcut-item">
5347
<span className="keyboard-shortcut__command">{replaceCommand}</span>
5448
<span>{t('KeyboardShortcuts.CodeEditing.ReplaceTextMatch')}</span>
@@ -69,10 +63,10 @@ function KeyboardShortcutModal() {
6963
<span className="keyboard-shortcut__command">{metaKeyName} + .</span>
7064
<span>{t('KeyboardShortcuts.CodeEditing.CommentLine')}</span>
7165
</li>
72-
<li className="keyboard-shortcut-item">
73-
<span className="keyboard-shortcut__command">{metaKeyName} + K</span>
74-
<span>{t('KeyboardShortcuts.CodeEditing.ColorPicker')}</span>
75-
</li>
66+
<KeyboardShortcutItem
67+
desc={t('KeyboardShortcuts.CodeEditing.ColorPicker')}
68+
keyName="colorPicker"
69+
/>
7670
<li className="keyboard-shortcut-item">
7771
<span className="keyboard-shortcut__command">{newFileCommand}</span>
7872
<span>{t('KeyboardShortcuts.CodeEditing.CreateNewFile')}</span>

client/styles/components/_keyboard-shortcuts.scss

+4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121
align-items: baseline;
2222
}
2323

24+
.keyboard-shortcut__edit {
25+
margin-right: 2px;
26+
}
27+
2428
.keyboard-shortcut__command {
2529
font-weight: bold;
2630
text-align: right;

0 commit comments

Comments
 (0)