debian-mirror-gitlab/app/assets/javascripts/behaviors/shortcuts/keybindings.js
2022-11-25 23:54:43 +05:30

702 lines
18 KiB
JavaScript

import { memoize } from 'lodash';
import AccessorUtilities from '~/lib/utils/accessor';
import { __ } from '~/locale';
const isCustomizable = (command) =>
'customizable' in command ? Boolean(command.customizable) : true;
export const LOCAL_STORAGE_KEY = 'gl-keyboard-shortcuts-customizations';
/**
* @returns { Object.<string, string[]> } A map of command ID => keys of all
* keyboard shortcuts that have been customized by the user. These
* customizations are fetched from `localStorage`. This function is memoized,
* so its return value will not reflect changes made to the `localStorage` data
* after it has been called once.
*
* @example
* { "globalShortcuts.togglePerformanceBar": ["p e r f"] }
*/
export const getCustomizations = memoize(() => {
let parsedCustomizations = {};
const localStorageIsSafe = AccessorUtilities.canUseLocalStorage();
if (localStorageIsSafe) {
try {
parsedCustomizations = JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEY) || '{}');
} catch (e) {
/* do nothing */
}
}
return parsedCustomizations;
});
// All available commands
export const TOGGLE_KEYBOARD_SHORTCUTS_DIALOG = {
id: 'globalShortcuts.toggleKeyboardShortcutsDialog',
description: __('Toggle keyboard shortcuts help dialog'),
defaultKeys: ['?'],
};
export const GO_TO_YOUR_PROJECTS = {
id: 'globalShortcuts.goToYourProjects',
description: __('Go to your projects'),
defaultKeys: ['shift+p'],
};
export const GO_TO_YOUR_GROUPS = {
id: 'globalShortcuts.goToYourGroups',
description: __('Go to your groups'),
defaultKeys: ['shift+g'],
};
export const GO_TO_ACTIVITY_FEED = {
id: 'globalShortcuts.goToActivityFeed',
description: __('Go to the activity feed'),
defaultKeys: ['shift+a'],
};
export const GO_TO_MILESTONE_LIST = {
id: 'globalShortcuts.goToMilestoneList',
description: __('Go to the milestone list'),
defaultKeys: ['shift+l'],
};
export const GO_TO_YOUR_SNIPPETS = {
id: 'globalShortcuts.goToYourSnippets',
description: __('Go to your snippets'),
defaultKeys: ['shift+s'],
};
export const START_SEARCH = {
id: 'globalShortcuts.startSearch',
description: __('Start search'),
defaultKeys: ['s', '/'],
};
export const FOCUS_FILTER_BAR = {
id: 'globalShortcuts.focusFilterBar',
description: __('Focus filter bar'),
defaultKeys: ['f'],
};
export const GO_TO_YOUR_ISSUES = {
id: 'globalShortcuts.goToYourIssues',
description: __('Go to your issues'),
defaultKeys: ['shift+i'],
};
export const GO_TO_YOUR_MERGE_REQUESTS = {
id: 'globalShortcuts.goToYourMergeRequests',
description: __('Go to your merge requests'),
defaultKeys: ['shift+m'],
};
export const GO_TO_YOUR_TODO_LIST = {
id: 'globalShortcuts.goToYourTodoList',
description: __('Go to your To-Do list'),
defaultKeys: ['shift+t'],
};
export const TOGGLE_PERFORMANCE_BAR = {
id: 'globalShortcuts.togglePerformanceBar',
description: __('Toggle the Performance Bar'),
defaultKeys: ['p b'], // eslint-disable-line @gitlab/require-i18n-strings
};
export const HIDE_APPEARING_CONTENT = {
id: 'globalShortcuts.hideAppearingContent',
description: __('Hide tooltips or popovers'),
defaultKeys: ['esc'],
};
export const TOGGLE_CANARY = {
id: 'globalShortcuts.toggleCanary',
description: __('Toggle GitLab Next'),
defaultKeys: ['g x'], // eslint-disable-line @gitlab/require-i18n-strings
};
export const BOLD_TEXT = {
id: 'editing.boldText',
description: __('Bold text'),
defaultKeys: ['mod+b'],
customizable: false,
};
export const ITALIC_TEXT = {
id: 'editing.italicText',
description: __('Italic text'),
defaultKeys: ['mod+i'],
customizable: false,
};
export const STRIKETHROUGH_TEXT = {
id: 'editing.strikethroughText',
description: __('Strikethrough text'),
defaultKeys: ['mod+shift+x'],
customizable: false,
};
export const LINK_TEXT = {
id: 'editing.linkText',
description: __('Link text'),
defaultKeys: ['mod+k'],
customizable: false,
};
export const INDENT_LINE = {
id: 'editing.indentLine',
description: __('Indent line'),
defaultKeys: ['mod+]'], // eslint-disable-line @gitlab/require-i18n-strings
customizable: false,
};
export const OUTDENT_LINE = {
id: 'editing.outdentLine',
description: __('Outdent line'),
defaultKeys: ['mod+['], // eslint-disable-line @gitlab/require-i18n-strings
customizable: false,
};
export const TOGGLE_MARKDOWN_PREVIEW = {
id: 'editing.toggleMarkdownPreview',
description: __('Toggle Markdown preview'),
// Note: Ideally, keyboard shortcuts should be made cross-platform by using the special `mod` key
// instead of binding both `ctrl` and `command` versions of the shortcut.
// See https://docs.gitlab.com/ee/development/fe_guide/keyboard_shortcuts.html#make-cross-platform-shortcuts.
// However, this particular shortcut has been in place since before the `mod` key was available.
// We've chosen to leave this implemented as-is for the time being to avoid breaking people's workflows.
// See discussion in https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45308#note_527490548.
defaultKeys: ['ctrl+shift+p', 'command+shift+p'],
};
export const EDIT_RECENT_COMMENT = {
id: 'editing.editRecentComment',
description: __('Edit your most recent comment in a thread (from an empty textarea)'),
defaultKeys: ['up'],
};
export const EDIT_WIKI_PAGE = {
id: 'wiki.editWikiPage',
description: __('Edit wiki page'),
defaultKeys: ['e'],
};
export const REPO_GRAPH_SCROLL_LEFT = {
id: 'repositoryGraph.scrollLeft',
description: __('Scroll left'),
defaultKeys: ['left', 'h'],
};
export const REPO_GRAPH_SCROLL_RIGHT = {
id: 'repositoryGraph.scrollRight',
description: __('Scroll right'),
defaultKeys: ['right', 'l'],
};
export const REPO_GRAPH_SCROLL_UP = {
id: 'repositoryGraph.scrollUp',
description: __('Scroll up'),
defaultKeys: ['up', 'k'],
};
export const REPO_GRAPH_SCROLL_DOWN = {
id: 'repositoryGraph.scrollDown',
description: __('Scroll down'),
defaultKeys: ['down', 'j'],
};
export const REPO_GRAPH_SCROLL_TOP = {
id: 'repositoryGraph.scrollToTop',
description: __('Scroll to top'),
defaultKeys: ['shift+up', 'shift+k'],
};
export const REPO_GRAPH_SCROLL_BOTTOM = {
id: 'repositoryGraph.scrollToBottom',
description: __('Scroll to bottom'),
defaultKeys: ['shift+down', 'shift+j'],
};
export const GO_TO_PROJECT_OVERVIEW = {
id: 'project.goToOverview',
description: __("Go to the project's overview page"),
defaultKeys: ['g p'], // eslint-disable-line @gitlab/require-i18n-strings
};
export const GO_TO_PROJECT_ACTIVITY_FEED = {
id: 'project.goToActivityFeed',
description: __("Go to the project's activity feed"),
defaultKeys: ['g v'], // eslint-disable-line @gitlab/require-i18n-strings
};
export const GO_TO_PROJECT_RELEASES = {
id: 'project.goToReleases',
description: __('Go to releases'),
defaultKeys: ['g r'], // eslint-disable-line @gitlab/require-i18n-strings
};
export const GO_TO_PROJECT_FILES = {
id: 'project.goToFiles',
description: __('Go to files'),
defaultKeys: ['g f'], // eslint-disable-line @gitlab/require-i18n-strings
};
export const GO_TO_PROJECT_FIND_FILE = {
id: 'project.goToFindFile',
description: __('Go to find file'),
defaultKeys: ['t'],
};
export const GO_TO_PROJECT_COMMITS = {
id: 'project.goToCommits',
description: __('Go to commits'),
defaultKeys: ['g c'], // eslint-disable-line @gitlab/require-i18n-strings
};
export const GO_TO_PROJECT_REPO_GRAPH = {
id: 'project.goToRepoGraph',
description: __('Go to repository graph'),
defaultKeys: ['g n'], // eslint-disable-line @gitlab/require-i18n-strings
};
export const GO_TO_PROJECT_REPO_CHARTS = {
id: 'project.goToRepoCharts',
description: __('Go to repository charts'),
defaultKeys: ['g d'], // eslint-disable-line @gitlab/require-i18n-strings
};
export const GO_TO_PROJECT_ISSUES = {
id: 'project.goToIssues',
description: __('Go to issues'),
defaultKeys: ['g i'], // eslint-disable-line @gitlab/require-i18n-strings
};
export const NEW_ISSUE = {
id: 'project.newIssue',
description: __('New issue'),
defaultKeys: ['i'],
};
export const GO_TO_PROJECT_ISSUE_BOARDS = {
id: 'project.goToIssueBoards',
description: __('Go to issue boards'),
defaultKeys: ['g b'], // eslint-disable-line @gitlab/require-i18n-strings
};
export const GO_TO_PROJECT_MERGE_REQUESTS = {
id: 'project.goToMergeRequests',
description: __('Go to merge requests'),
defaultKeys: ['g m'], // eslint-disable-line @gitlab/require-i18n-strings
};
export const GO_TO_PROJECT_JOBS = {
id: 'project.goToJobs',
description: __('Go to jobs'),
defaultKeys: ['g j'], // eslint-disable-line @gitlab/require-i18n-strings
};
export const GO_TO_PROJECT_METRICS = {
id: 'project.goToMetrics',
description: __('Go to metrics'),
defaultKeys: ['g l'], // eslint-disable-line @gitlab/require-i18n-strings
};
export const GO_TO_PROJECT_ENVIRONMENTS = {
id: 'project.goToEnvironments',
description: __('Go to environments'),
defaultKeys: ['g e'], // eslint-disable-line @gitlab/require-i18n-strings
};
export const GO_TO_PROJECT_KUBERNETES = {
id: 'project.goToKubernetes',
description: __('Go to kubernetes'),
defaultKeys: ['g k'], // eslint-disable-line @gitlab/require-i18n-strings
};
export const GO_TO_PROJECT_SNIPPETS = {
id: 'project.goToSnippets',
description: __('Go to snippets'),
defaultKeys: ['g s'], // eslint-disable-line @gitlab/require-i18n-strings
};
export const GO_TO_PROJECT_WIKI = {
id: 'project.goToWiki',
description: __('Go to wiki'),
defaultKeys: ['g w'], // eslint-disable-line @gitlab/require-i18n-strings
};
export const GO_TO_PROJECT_WEBIDE = {
id: 'project.goToWebIDE',
description: __('Open in Web IDE'),
defaultKeys: ['.'],
};
export const PROJECT_FILES_MOVE_SELECTION_UP = {
id: 'projectFiles.moveSelectionUp',
description: __('Move selection up'),
defaultKeys: ['up'],
};
export const PROJECT_FILES_MOVE_SELECTION_DOWN = {
id: 'projectFiles.moveSelectionDown',
description: __('Move selection down'),
defaultKeys: ['down'],
};
export const PROJECT_FILES_OPEN_SELECTION = {
id: 'projectFiles.openSelection',
description: __('Open Selection'),
defaultKeys: ['enter'],
};
export const PROJECT_FILES_GO_BACK = {
id: 'projectFiles.goBack',
description: __('Go back (while searching for files)'),
defaultKeys: ['esc'],
};
export const PROJECT_FILES_GO_TO_PERMALINK = {
id: 'projectFiles.goToFilePermalink',
description: __('Go to file permalink (while viewing a file)'),
defaultKeys: ['y'],
};
export const ISSUABLE_COMMENT_OR_REPLY = {
id: 'issuables.commentReply',
description: __('Comment/Reply (quoting selected text)'),
defaultKeys: ['r'],
};
export const ISSUABLE_EDIT_DESCRIPTION = {
id: 'issuables.editDescription',
description: __('Edit description'),
defaultKeys: ['e'],
};
export const ISSUABLE_CHANGE_LABEL = {
id: 'issuables.changeLabel',
description: __('Change label'),
defaultKeys: ['l'],
};
export const ISSUE_MR_CHANGE_ASSIGNEE = {
id: 'issuesMRs.changeAssignee',
description: __('Change assignee'),
defaultKeys: ['a'],
};
export const ISSUE_MR_CHANGE_MILESTONE = {
id: 'issuesMRs.changeMilestone',
description: __('Change milestone'),
defaultKeys: ['m'],
};
export const MR_NEXT_FILE_IN_DIFF = {
id: 'mergeRequests.nextFileInDiff',
description: __('Next file in diff'),
defaultKeys: [']', 'j'],
};
export const MR_PREVIOUS_FILE_IN_DIFF = {
id: 'mergeRequests.previousFileInDiff',
description: __('Previous file in diff'),
defaultKeys: ['[', 'k'],
};
export const MR_GO_TO_FILE = {
id: 'mergeRequests.goToFile',
description: __('Go to file'),
defaultKeys: ['mod+p', 't'],
customizable: false,
};
export const MR_NEXT_UNRESOLVED_DISCUSSION = {
id: 'mergeRequests.nextUnresolvedDiscussion',
description: __('Next unresolved discussion'),
defaultKeys: ['n'],
};
export const MR_PREVIOUS_UNRESOLVED_DISCUSSION = {
id: 'mergeRequests.previousUnresolvedDiscussion',
description: __('Previous unresolved discussion'),
defaultKeys: ['p'],
};
export const MR_COPY_SOURCE_BRANCH_NAME = {
id: 'mergeRequests.copySourceBranchName',
description: __('Copy source branch name'),
defaultKeys: ['b'],
};
export const MR_COMMITS_NEXT_COMMIT = {
id: 'mergeRequestCommits.nextCommit',
description: __('Next commit'),
defaultKeys: ['c'],
};
export const MR_COMMITS_PREVIOUS_COMMIT = {
id: 'mergeRequestCommits.previousCommit',
description: __('Previous commit'),
defaultKeys: ['x'],
};
export const ISSUE_NEXT_DESIGN = {
id: 'issues.nextDesign',
description: __('Next design'),
defaultKeys: ['right'],
};
export const ISSUE_PREVIOUS_DESIGN = {
id: 'issues.previousDesign',
description: __('Previous design'),
defaultKeys: ['left'],
};
export const ISSUE_CLOSE_DESIGN = {
id: 'issues.closeDesign',
description: __('Close design'),
defaultKeys: ['esc'],
};
export const WEB_IDE_GO_TO_FILE = {
id: 'webIDE.goToFile',
description: __('Go to file'),
defaultKeys: ['mod+p'],
};
export const WEB_IDE_COMMIT = {
id: 'webIDE.commit',
description: __('Commit (when editing commit message)'),
defaultKeys: ['mod+enter'],
customizable: false,
};
export const METRICS_EXPAND_PANEL = {
id: 'metrics.expandPanel',
description: __('Expand panel'),
defaultKeys: ['e'],
customizable: false,
};
export const METRICS_VIEW_LOGS = {
id: 'metrics.viewLogs',
description: __('View logs'),
defaultKeys: ['l'],
customizable: false,
};
export const METRICS_DOWNLOAD_CSV = {
id: 'metrics.downloadCSV',
description: __('Download CSV'),
defaultKeys: ['d'],
customizable: false,
};
export const METRICS_COPY_LINK_TO_CHART = {
id: 'metrics.copyLinkToChart',
description: __('Copy link to chart'),
defaultKeys: ['c'],
customizable: false,
};
export const METRICS_SHOW_ALERTS = {
id: 'metrics.showAlerts',
description: __('Alerts'),
defaultKeys: ['a'],
customizable: false,
};
// All keybinding groups
export const GLOBAL_SHORTCUTS_GROUP = {
id: 'globalShortcuts',
name: __('Global Shortcuts'),
keybindings: [
TOGGLE_KEYBOARD_SHORTCUTS_DIALOG,
GO_TO_YOUR_PROJECTS,
GO_TO_YOUR_GROUPS,
GO_TO_ACTIVITY_FEED,
GO_TO_MILESTONE_LIST,
GO_TO_YOUR_SNIPPETS,
START_SEARCH,
FOCUS_FILTER_BAR,
GO_TO_YOUR_ISSUES,
GO_TO_YOUR_MERGE_REQUESTS,
GO_TO_YOUR_TODO_LIST,
TOGGLE_PERFORMANCE_BAR,
HIDE_APPEARING_CONTENT,
],
};
export const EDITING_SHORTCUTS_GROUP = {
id: 'editing',
name: __('Editing'),
keybindings: [
BOLD_TEXT,
ITALIC_TEXT,
STRIKETHROUGH_TEXT,
LINK_TEXT,
TOGGLE_MARKDOWN_PREVIEW,
EDIT_RECENT_COMMENT,
],
};
export const WIKI_SHORTCUTS_GROUP = {
id: 'wiki',
name: __('Wiki'),
keybindings: [EDIT_WIKI_PAGE],
};
export const REPOSITORY_GRAPH_SHORTCUTS_GROUP = {
id: 'repositoryGraph',
name: __('Repository Graph'),
keybindings: [
REPO_GRAPH_SCROLL_LEFT,
REPO_GRAPH_SCROLL_RIGHT,
REPO_GRAPH_SCROLL_UP,
REPO_GRAPH_SCROLL_DOWN,
REPO_GRAPH_SCROLL_TOP,
REPO_GRAPH_SCROLL_BOTTOM,
],
};
export const PROJECT_SHORTCUTS_GROUP = {
id: 'project',
name: __('Project'),
keybindings: [
GO_TO_PROJECT_OVERVIEW,
GO_TO_PROJECT_ACTIVITY_FEED,
GO_TO_PROJECT_RELEASES,
GO_TO_PROJECT_FILES,
GO_TO_PROJECT_FIND_FILE,
GO_TO_PROJECT_COMMITS,
GO_TO_PROJECT_REPO_GRAPH,
GO_TO_PROJECT_REPO_CHARTS,
GO_TO_PROJECT_ISSUES,
NEW_ISSUE,
GO_TO_PROJECT_ISSUE_BOARDS,
GO_TO_PROJECT_MERGE_REQUESTS,
GO_TO_PROJECT_JOBS,
GO_TO_PROJECT_METRICS,
GO_TO_PROJECT_ENVIRONMENTS,
GO_TO_PROJECT_KUBERNETES,
GO_TO_PROJECT_SNIPPETS,
GO_TO_PROJECT_WIKI,
GO_TO_PROJECT_WEBIDE,
],
};
export const PROJECT_FILES_SHORTCUTS_GROUP = {
id: 'projectFiles',
name: __('Project Files'),
keybindings: [
PROJECT_FILES_MOVE_SELECTION_UP,
PROJECT_FILES_MOVE_SELECTION_DOWN,
PROJECT_FILES_OPEN_SELECTION,
PROJECT_FILES_GO_BACK,
PROJECT_FILES_GO_TO_PERMALINK,
],
};
export const ISSUABLE_SHORTCUTS_GROUP = {
id: 'issuables',
name: __('Epics, issues, and merge requests'),
keybindings: [ISSUABLE_COMMENT_OR_REPLY, ISSUABLE_EDIT_DESCRIPTION, ISSUABLE_CHANGE_LABEL],
};
export const ISSUE_MR_SHORTCUTS_GROUP = {
id: 'issuesMRs',
name: __('Issues and merge requests'),
keybindings: [ISSUE_MR_CHANGE_ASSIGNEE, ISSUE_MR_CHANGE_MILESTONE],
};
export const MR_SHORTCUTS_GROUP = {
id: 'mergeRequests',
name: __('Merge requests'),
keybindings: [
MR_NEXT_FILE_IN_DIFF,
MR_PREVIOUS_FILE_IN_DIFF,
MR_GO_TO_FILE,
MR_NEXT_UNRESOLVED_DISCUSSION,
MR_PREVIOUS_UNRESOLVED_DISCUSSION,
MR_COPY_SOURCE_BRANCH_NAME,
],
};
export const MR_COMMITS_SHORTCUTS_GROUP = {
id: 'mergeRequestCommits',
name: __('Merge request commits'),
keybindings: [MR_COMMITS_NEXT_COMMIT, MR_COMMITS_PREVIOUS_COMMIT],
};
export const ISSUES_SHORTCUTS_GROUP = {
id: 'issues',
name: __('Issues'),
keybindings: [ISSUE_NEXT_DESIGN, ISSUE_PREVIOUS_DESIGN, ISSUE_CLOSE_DESIGN],
};
export const WEB_IDE_SHORTCUTS_GROUP = {
id: 'webIDE',
name: __('Web IDE'),
keybindings: [WEB_IDE_GO_TO_FILE, WEB_IDE_COMMIT],
};
export const METRICS_SHORTCUTS_GROUP = {
id: 'metrics',
name: __('Metrics'),
keybindings: [
METRICS_EXPAND_PANEL,
METRICS_VIEW_LOGS,
METRICS_DOWNLOAD_CSV,
METRICS_COPY_LINK_TO_CHART,
METRICS_SHOW_ALERTS,
],
};
export const MISC_SHORTCUTS_GROUP = {
id: 'misc',
name: __('Miscellaneous'),
keybindings: [TOGGLE_CANARY],
};
/** All keybindings, grouped and ordered with descriptions */
export const keybindingGroups = [
GLOBAL_SHORTCUTS_GROUP,
EDITING_SHORTCUTS_GROUP,
WIKI_SHORTCUTS_GROUP,
REPOSITORY_GRAPH_SHORTCUTS_GROUP,
PROJECT_SHORTCUTS_GROUP,
PROJECT_FILES_SHORTCUTS_GROUP,
ISSUABLE_SHORTCUTS_GROUP,
ISSUE_MR_SHORTCUTS_GROUP,
MR_SHORTCUTS_GROUP,
MR_COMMITS_SHORTCUTS_GROUP,
ISSUES_SHORTCUTS_GROUP,
WEB_IDE_SHORTCUTS_GROUP,
METRICS_SHORTCUTS_GROUP,
MISC_SHORTCUTS_GROUP,
];
/**
* Gets keyboard shortcuts associated with a command
*
* @param {string} command The command object. All command
* objects are available as imports from this file.
*
* @returns {string[]} An array of keyboard shortcut strings bound to the command
*
* @example
* import { keysFor, TOGGLE_PERFORMANCE_BAR } from '~/behaviors/shortcuts/keybindings'
*
* Mousetrap.bind(keysFor(TOGGLE_PERFORMANCE_BAR), handler);
*/
export const keysFor = (command) => {
if (!isCustomizable(command)) {
// if the command is defined with `customizable: false`,
// don't allow this command to be customized.
return command.defaultKeys;
}
return getCustomizations()[command.id] || command.defaultKeys;
};