Skip to content

Commit 3794dae

Browse files
dertieranfregante
andcommitted
Fix and refactor shortcuts (refined-github#1734)
The shortcuts are now displayed in their own box. Co-authored-by: Federico Brigante <github@bfred.it>
1 parent b94161c commit 3794dae

12 files changed

Lines changed: 89 additions & 123 deletions

source/features/comment-fields-keyboard-shortcuts.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import select from 'select-dom';
22
import delegate from 'delegate';
33
import features from '../libs/features';
44
import indentTextarea from '../libs/indent-textarea';
5-
import {registerShortcut} from './improve-shortcut-help';
65

76
// Element.blur() will reset the tab focus to the start of the document.
87
// This places it back next to the blurred field
@@ -20,8 +19,6 @@ function blurAccessibly(field) {
2019
}
2120

2221
function init() {
23-
registerShortcut('issues', '↑', 'Edit your last comment');
24-
2522
delegate('.js-comment-field', 'keydown', event => {
2623
const field = event.target;
2724

@@ -66,5 +63,8 @@ function init() {
6663

6764
features.add({
6865
id: 'comment-fields-keyboard-shortcuts',
66+
shortcuts: {
67+
'↑': 'Edit your last comment'
68+
},
6969
init
7070
});

source/features/create-release-shortcut.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import select from 'select-dom';
22
import features from '../libs/features';
3-
import {registerShortcut} from './improve-shortcut-help';
43

54
function init() {
6-
registerShortcut('releases', 'c', 'Create a new release');
75
const createReleaseButton = select('a[href$="/releases/new"]:not([data-hotkey])');
86
if (createReleaseButton) {
97
createReleaseButton.setAttribute('data-hotkey', 'c');
@@ -16,5 +14,8 @@ features.add({
1614
features.isReleasesOrTags
1715
],
1816
load: features.onAjaxedPages,
17+
shortcuts: {
18+
c: 'Create a new release'
19+
},
1920
init
2021
});

source/features/diff-view-without-whitespace-option.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import {React} from 'dom-chef/react';
22
import select from 'select-dom';
33
import * as icons from '../libs/icons';
44
import features from '../libs/features';
5-
import {registerShortcut} from './improve-shortcut-help';
65

76
function init() {
87
const container = select([
@@ -35,7 +34,6 @@ function init() {
3534
</a>
3635
</div>
3736
);
38-
registerShortcut('source', 'd w', 'Show/hide whitespaces in diffs');
3937

4038
// Make space for the new button by removing "Changes from" #655
4139
const uselessCopy = select('[data-hotkey="c"]');
@@ -50,5 +48,8 @@ features.add({
5048
features.isRepo
5149
],
5250
load: features.onAjaxedPages,
51+
shortcuts: {
52+
'd w': 'Show/hide whitespaces in diffs'
53+
},
5354
init
5455
});
Lines changed: 34 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,117 +1,57 @@
11
import {React} from 'dom-chef/react';
22
import select from 'select-dom';
3-
import domify from '../libs/domify';
43
import features from '../libs/features';
5-
import observeEl from '../libs/simplified-element-observer';
6-
import {isProject} from '../libs/page-detect';
7-
8-
/**
9-
* A map of the shortcut group titles and their respective group IDs.
10-
* The IDs can be used to register new shortcuts using `registerShortcut`.
11-
*/
12-
const groups = {
13-
'Site wide shortcuts': 'site',
14-
Repositories: 'repos',
15-
'Source code browsing': 'source',
16-
'Pull request list': 'pr',
17-
'Pull request - Files changed tab': 'prFiles',
18-
Issues: 'issues',
19-
'Browsing commits': 'commits',
20-
'Commit list': 'commitList',
21-
Dashboards: 'dashboards',
22-
Notifications: 'notifications',
23-
'Network Graph': 'networkGraph',
24-
'Moving a column': 'projectColumns',
25-
'Moving a card': 'projectCards'
26-
};
27-
const shortcuts = new Map();
28-
29-
/**
30-
* Registers a new shortcut to be displayed in the shortcut help modal.
31-
* @param {String} groupId The ID of the group as defined in the `groups` map.
32-
* @param {String} hotkey The hotkey with keys separated by spaces.
33-
* @param {String} description What the shortcut does.
34-
*/
35-
export function registerShortcut(groupId, hotkey, description) {
36-
shortcuts.set(hotkey, {groupId, hotkey, description});
37-
}
384

395
function splitKeys(keys) {
40-
return keys.replace(/\S+/g, '<kbd>$&</kbd>');
6+
return keys.split(' ').map(key => <>{' '}<kbd>{key}</kbd></>);
417
}
428

43-
function improveShortcutHelp() {
44-
// Remove redundant "Show All" button
45-
select('.js-see-all-keyboard-shortcuts').remove();
46-
47-
// Close the modal with Esc
48-
select('.js-facebox-close').dataset.hotkey = 'Escape';
49-
50-
const modal = select('.shortcuts');
51-
const groupElements = select.all('tbody', modal);
52-
for (const groupElement of groupElements) {
53-
const groupTitle = select('tr th:nth-child(2)', groupElement).textContent;
54-
if (groupTitle in groups) {
55-
const groupId = groups[groupTitle];
56-
57-
if (groupElement.style.display === 'none') {
58-
if (groupId.startsWith('project') && !isProject()) {
59-
// The "Projects"-related groups are quite big and not interesting to most users
60-
groupElement.remove();
61-
} else {
62-
// Reduce opacity of previously hidden groups
63-
groupElement.removeAttribute('style');
64-
groupElement.classList.remove('js-hidden-pane');
65-
groupElement.classList.add('rgh-inactive-shortcut-group');
66-
}
67-
} else {
68-
// Push relevant (not hidden) groups to the top
69-
groupElement.parentElement.prepend(groupElement);
70-
}
71-
72-
for (const {hotkey, description, groupId: thisGroupId} of shortcuts.values()) {
73-
if (thisGroupId === groupId) {
74-
groupElement.append(
75-
<tr>
76-
<td class="keys" dangerouslySetInnerHTML={{__html: splitKeys(hotkey)}}/>
77-
<td>
78-
{description}
79-
<div
80-
class="rgh-shortcut-circle tooltipped tooltipped-nw"
81-
aria-label="Shortcut added by Refined GitHub"
82-
/>
83-
</td>
84-
</tr>
85-
);
86-
}
87-
}
88-
}
89-
}
9+
function improveShortcutHelp(dialog) {
10+
select('.Box-body .col-5 .Box:first-child', dialog).after(
11+
<div class="Box Box--condensed m-4">
12+
<div class="Box-header">
13+
<h3 class="Box-title">Added by Refined GitHub</h3>
14+
</div>
15+
16+
<ul>
17+
{features.getShortcuts().map(({hotkey, description}) => (
18+
<li class="Box-row d-flex flex-row">
19+
<div class="flex-auto">{description}</div>
20+
<div class="ml-2 no-wrap">
21+
<kbd>{hotkey}</kbd>
22+
</div>
23+
</li>
24+
))}
25+
</ul>
26+
</div>
27+
);
9028
}
9129

92-
function fixKeys() {
93-
for (const key of select.all('.keys kbd')) {
30+
function fixKeys(dialog) {
31+
for (const key of select.all('kbd', dialog)) {
9432
if (key.textContent.includes(' ')) {
95-
key.replaceWith(domify(splitKeys(key.textContent)));
33+
key.replaceWith(...splitKeys(key.textContent));
9634
}
9735
}
9836
}
9937

38+
const observer = new MutationObserver(([{target}]) => {
39+
if (!select.exists('.js-details-dialog-spinner', target as Element)) {
40+
improveShortcutHelp(target);
41+
fixKeys(target);
42+
observer.disconnect();
43+
}
44+
});
45+
10046
function init() {
101-
observeEl('#facebox', records => {
102-
if ([...records].some(record => record.target.matches('.shortcuts') &&
103-
[...record.removedNodes].some(element => element.matches('.facebox-loading')))) {
104-
improveShortcutHelp();
105-
fixKeys();
47+
document.addEventListener('keypress', ({key}) => {
48+
if (key === '?') {
49+
observer.observe(select('.kb-shortcut-dialog'), {childList: true});
10650
}
107-
}, {
108-
childList: true,
109-
subtree: true
11051
});
11152
}
11253

11354
features.add({
11455
id: 'improve-shortcut-help',
115-
load: features.onDomReady,
11656
init
11757
});
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
import select from 'select-dom';
22
import features from '../libs/features';
3-
import {registerShortcut} from './improve-shortcut-help';
43

54
function init() {
6-
registerShortcut('site', 'ArrowRight', 'Go to the next page.');
75
const createNextPageButton = select('a.next_page');
86
if (createNextPageButton) {
97
createNextPageButton.setAttribute('data-hotkey', 'ArrowRight');
108
}
119

12-
registerShortcut('site', 'ArrowLeft', 'Go to the previous page.');
1310
const createPreviousPageButton = select('a.previous_page');
1411
if (createPreviousPageButton) {
1512
createPreviousPageButton.setAttribute('data-hotkey', 'ArrowLeft');
@@ -19,5 +16,9 @@ function init() {
1916
features.add({
2017
id: 'navigate-pages-with-arrow-keys',
2118
load: features.onAjaxedPages,
19+
shortcuts: {
20+
'→': 'Go to the next page',
21+
'←': 'Go to the previous page'
22+
},
2223
init
2324
});

source/features/profile-hotkey.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
import select from 'select-dom';
22
import features from '../libs/features';
33
import {getUsername} from '../libs/utils';
4-
import {registerShortcut} from './improve-shortcut-help';
54

65
function init() {
76
const menuItem = select(`#user-links a.dropdown-item[href="/${getUsername()}"]`);
87

98
if (menuItem) {
109
menuItem.setAttribute('data-hotkey', 'g m');
11-
registerShortcut('site', 'g m', 'Go to Profile');
1210
} else {
1311
return false;
1412
}
@@ -17,5 +15,8 @@ function init() {
1715
features.add({
1816
id: 'profile-hotkey',
1917
load: features.onDomReady,
18+
shortcuts: {
19+
'g m': 'Go to Profile'
20+
},
2021
init
2122
});

source/features/pull-request-hotkey.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,7 @@
11
import select from 'select-dom';
22
import features from '../libs/features';
3-
import {registerShortcut} from './improve-shortcut-help';
43

54
function init() {
6-
registerShortcut('pr', 'g 1', 'Go to Conversation');
7-
registerShortcut('pr', 'g 2', 'Go to Commits');
8-
registerShortcut('pr', 'g 3', 'Go to Checks');
9-
registerShortcut('pr', 'g 4', 'Go to Files changed');
10-
115
const tabs = select.all('.tabnav-pr .tabnav-tab');
126
const selectedIndex = tabs.indexOf(select('.tabnav-pr .selected'));
137
const lastTab = tabs.length - 1;
@@ -30,5 +24,11 @@ features.add({
3024
features.isPR
3125
],
3226
load: features.onAjaxedPages,
27+
shortcuts: {
28+
'g 1': 'Go to Conversation',
29+
'g 2': 'Go to Commits',
30+
'g 3': 'Go to Checks',
31+
'g 4': 'Go to Files changed'
32+
},
3333
init
3434
});

source/features/releases-tab.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import * as cache from '../libs/cache';
1313
import {getRepoURL} from '../libs/utils';
1414
import {safeElementReady} from '../libs/dom-utils';
1515
import {isRepoRoot, isReleasesOrTags} from '../libs/page-detect';
16-
import {registerShortcut} from './improve-shortcut-help';
1716

1817
const repoUrl = getRepoURL();
1918
const repoKey = `releases-count:${repoUrl}`;
@@ -48,8 +47,6 @@ async function init() {
4847
);
4948
select('.reponav-dropdown').before(releasesTab);
5049

51-
registerShortcut('repos', 'g r', 'Go to Releases');
52-
5350
if (isReleasesOrTags()) {
5451
const selected = select('.reponav-item.selected');
5552
if (selected) {
@@ -67,5 +64,8 @@ features.add({
6764
features.isRepo
6865
],
6966
load: features.onAjaxedPages,
67+
shortcuts: {
68+
'g r': 'Go to Releases'
69+
},
7070
init
7171
});

source/features/selection-in-new-tab.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
import select from 'select-dom';
22
import features from '../libs/features';
3-
import {registerShortcut} from './improve-shortcut-help';
43

54
function init() {
6-
registerShortcut('site', 'shift o', 'Open selection in new tab');
7-
85
document.addEventListener('keypress', event => {
96
const selected = select('.navigation-focus .js-navigation-open[href]');
107
if (selected && event.key === 'O') {
@@ -21,5 +18,8 @@ function init() {
2118

2219
features.add({
2320
id: 'selection-in-new-tab',
21+
shortcuts: {
22+
'shift o': 'Open selection in new tab'
23+
},
2424
init
2525
});

source/features/star-repo-hotkey.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
import select from 'select-dom';
22
import features from '../libs/features';
3-
import {registerShortcut} from './improve-shortcut-help';
43

54
function init() {
6-
registerShortcut('repos', 'g s', 'Star and unstar repository');
7-
85
// There are two buttons: unstar and star
96
for (const button of select.all('.js-social-form > button')) {
107
button.setAttribute('data-hotkey', 'g s');
@@ -17,5 +14,8 @@ features.add({
1714
features.isRepo
1815
],
1916
load: features.onAjaxedPages,
17+
shortcuts: {
18+
'g s': 'Star and unstar repository'
19+
},
2020
init
2121
});

0 commit comments

Comments
 (0)