DEV: API to add keyboard shortcuts to help modal (#16075)
This commit is contained in:
parent
c9dab6fd08
commit
c33cf3c5e6
|
@ -5,6 +5,8 @@ export default Component.extend({
|
|||
fixed: false,
|
||||
submitOnEnter: true,
|
||||
dismissable: true,
|
||||
attributeBindings: ["tabindex"],
|
||||
tabindex: -1,
|
||||
|
||||
didInsertElement() {
|
||||
this._super(...arguments);
|
||||
|
|
|
@ -185,9 +185,14 @@ export default Component.extend({
|
|||
!autofocusedElement ||
|
||||
document.activeElement !== autofocusedElement
|
||||
) {
|
||||
innerContainer
|
||||
.querySelectorAll(focusableElements + ", button:not(.modal-close)")[0]
|
||||
?.focus();
|
||||
// if there's not autofocus, or the activeElement, is not the autofocusable element
|
||||
// attempt to focus the first of the focusable elements or just the modal-body
|
||||
// to make it possible to scroll with arrow down/up
|
||||
(
|
||||
innerContainer.querySelector(
|
||||
focusableElements + ", button:not(.modal-close)"
|
||||
) || innerContainer.querySelector(".modal-body")
|
||||
)?.focus();
|
||||
}
|
||||
|
||||
return;
|
||||
|
|
|
@ -2,17 +2,28 @@ import Controller from "@ember/controller";
|
|||
import I18n from "I18n";
|
||||
import { translateModKey } from "discourse/lib/utilities";
|
||||
import ModalFunctionality from "discourse/mixins/modal-functionality";
|
||||
import { extraKeyboardShortcutsHelp } from "discourse/lib/keyboard-shortcuts";
|
||||
|
||||
const KEY = "keyboard_shortcuts_help";
|
||||
|
||||
const SHIFT = I18n.t("shortcut_modifier_key.shift");
|
||||
const ALT = translateModKey("Alt");
|
||||
const META = translateModKey("Meta");
|
||||
const CTRL = I18n.t("shortcut_modifier_key.ctrl");
|
||||
const ENTER = I18n.t("shortcut_modifier_key.enter");
|
||||
|
||||
const COMMA = I18n.t(`${KEY}.shortcut_key_delimiter_comma`);
|
||||
const PLUS = I18n.t(`${KEY}.shortcut_key_delimiter_plus`);
|
||||
|
||||
const translationForExtraShortcuts = {
|
||||
shift: SHIFT,
|
||||
alt: ALT,
|
||||
meta: META,
|
||||
ctrl: CTRL,
|
||||
enter: ENTER,
|
||||
comma: COMMA,
|
||||
plus: PLUS,
|
||||
};
|
||||
|
||||
function buildHTML(keys1, keys2, keysDelimiter, shortcutsDelimiter) {
|
||||
const allKeys = [keys1, keys2]
|
||||
.reject((keys) => keys.length === 0)
|
||||
|
@ -59,184 +70,272 @@ export default Controller.extend(ModalFunctionality, {
|
|||
},
|
||||
|
||||
_defineShortcuts() {
|
||||
this.set("shortcuts", {
|
||||
jump_to: {
|
||||
home: buildShortcut("jump_to.home", { keys1: ["g", "h"] }),
|
||||
latest: buildShortcut("jump_to.latest", { keys1: ["g", "l"] }),
|
||||
new: buildShortcut("jump_to.new", { keys1: ["g", "n"] }),
|
||||
unread: buildShortcut("jump_to.unread", { keys1: ["g", "u"] }),
|
||||
categories: buildShortcut("jump_to.categories", { keys1: ["g", "c"] }),
|
||||
top: buildShortcut("jump_to.top", { keys1: ["g", "t"] }),
|
||||
bookmarks: buildShortcut("jump_to.bookmarks", { keys1: ["g", "b"] }),
|
||||
profile: buildShortcut("jump_to.profile", { keys1: ["g", "p"] }),
|
||||
messages: buildShortcut("jump_to.messages", { keys1: ["g", "m"] }),
|
||||
drafts: buildShortcut("jump_to.drafts", { keys1: ["g", "d"] }),
|
||||
next: buildShortcut("jump_to.next", { keys1: ["g", "j"] }),
|
||||
previous: buildShortcut("jump_to.previous", { keys1: ["g", "k"] }),
|
||||
},
|
||||
navigation: {
|
||||
back: buildShortcut("navigation.back", { keys1: ["u"] }),
|
||||
jump: buildShortcut("navigation.jump", { keys1: ["#"] }),
|
||||
up_down: buildShortcut("navigation.up_down", {
|
||||
keys1: ["k"],
|
||||
keys2: ["j"],
|
||||
shortcutsDelimiter: "slash",
|
||||
}),
|
||||
open: buildShortcut("navigation.open", {
|
||||
keys1: ["o"],
|
||||
keys2: [ENTER],
|
||||
}),
|
||||
next_prev: buildShortcut("navigation.next_prev", {
|
||||
keys1: [SHIFT, "j"],
|
||||
keys2: [SHIFT, "k"],
|
||||
keysDelimiter: PLUS,
|
||||
shortcutsDelimiter: "slash",
|
||||
}),
|
||||
go_to_unread_post: buildShortcut("navigation.go_to_unread_post", {
|
||||
keys1: [SHIFT, "l"],
|
||||
keysDelimiter: PLUS,
|
||||
}),
|
||||
},
|
||||
let shortcuts = {
|
||||
jump_to: { shortcuts: this._buildJumpToSection() },
|
||||
application: {
|
||||
hamburger_menu: buildShortcut("application.hamburger_menu", {
|
||||
keys1: ["="],
|
||||
}),
|
||||
user_profile_menu: buildShortcut("application.user_profile_menu", {
|
||||
keys1: ["p"],
|
||||
}),
|
||||
create: buildShortcut("application.create", { keys1: ["c"] }),
|
||||
show_incoming_updated_topics: buildShortcut(
|
||||
"application.show_incoming_updated_topics",
|
||||
{ keys1: ["."] }
|
||||
),
|
||||
search: buildShortcut("application.search", {
|
||||
keys1: ["/"],
|
||||
keys2: [CTRL, ALT, "f"],
|
||||
keysDelimiter: PLUS,
|
||||
}),
|
||||
help: buildShortcut("application.help", { keys1: ["?"] }),
|
||||
dismiss_new: buildShortcut("application.dismiss_new", {
|
||||
keys1: ["x", "r"],
|
||||
}),
|
||||
dismiss_topics: buildShortcut("application.dismiss_topics", {
|
||||
keys1: ["x", "t"],
|
||||
}),
|
||||
log_out: buildShortcut("application.log_out", {
|
||||
keys1: [SHIFT, "z"],
|
||||
keys2: [SHIFT, "z"],
|
||||
keysDelimiter: PLUS,
|
||||
shortcutsDelimiter: "space",
|
||||
}),
|
||||
},
|
||||
composing: {
|
||||
return: buildShortcut("composing.return", {
|
||||
keys1: [SHIFT, "c"],
|
||||
keysDelimiter: PLUS,
|
||||
}),
|
||||
fullscreen: buildShortcut("composing.fullscreen", {
|
||||
keys1: [SHIFT, "F11"],
|
||||
keysDelimiter: PLUS,
|
||||
}),
|
||||
},
|
||||
bookmarks: {
|
||||
enter: buildShortcut("bookmarks.enter", { keys1: [ENTER] }),
|
||||
later_today: buildShortcut("bookmarks.later_today", {
|
||||
keys1: ["l", "t"],
|
||||
shortcutsDelimiter: "space",
|
||||
}),
|
||||
later_this_week: buildShortcut("bookmarks.later_this_week", {
|
||||
keys1: ["l", "w"],
|
||||
shortcutsDelimiter: "space",
|
||||
}),
|
||||
tomorrow: buildShortcut("bookmarks.tomorrow", {
|
||||
keys1: ["n", "d"],
|
||||
shortcutsDelimiter: "space",
|
||||
}),
|
||||
next_business_week: buildShortcut("bookmarks.next_business_week", {
|
||||
keys1: ["n", "b", "w"],
|
||||
shortcutsDelimiter: "space",
|
||||
}),
|
||||
next_business_day: buildShortcut("bookmarks.next_business_day", {
|
||||
keys1: ["n", "b", "d"],
|
||||
shortcutsDelimiter: "space",
|
||||
}),
|
||||
custom: buildShortcut("bookmarks.custom", {
|
||||
keys1: ["c", "r"],
|
||||
shortcutsDelimiter: "space",
|
||||
}),
|
||||
none: buildShortcut("bookmarks.none", {
|
||||
keys1: ["n", "r"],
|
||||
shortcutsDelimiter: "space",
|
||||
}),
|
||||
delete: buildShortcut("bookmarks.delete", {
|
||||
keys1: ["d", "d"],
|
||||
shortcutsDelimiter: "space",
|
||||
}),
|
||||
shortcuts: {
|
||||
hamburger_menu: buildShortcut("application.hamburger_menu", {
|
||||
keys1: ["="],
|
||||
}),
|
||||
user_profile_menu: buildShortcut("application.user_profile_menu", {
|
||||
keys1: ["p"],
|
||||
}),
|
||||
create: buildShortcut("application.create", { keys1: ["c"] }),
|
||||
show_incoming_updated_topics: buildShortcut(
|
||||
"application.show_incoming_updated_topics",
|
||||
{ keys1: ["."] }
|
||||
),
|
||||
search: buildShortcut("application.search", {
|
||||
keys1: ["/"],
|
||||
keys2: [CTRL, ALT, "f"],
|
||||
keysDelimiter: PLUS,
|
||||
}),
|
||||
help: buildShortcut("application.help", { keys1: ["?"] }),
|
||||
dismiss_new: buildShortcut("application.dismiss_new", {
|
||||
keys1: ["x", "r"],
|
||||
}),
|
||||
dismiss_topics: buildShortcut("application.dismiss_topics", {
|
||||
keys1: ["x", "t"],
|
||||
}),
|
||||
log_out: buildShortcut("application.log_out", {
|
||||
keys1: [SHIFT, "z"],
|
||||
keys2: [SHIFT, "z"],
|
||||
keysDelimiter: PLUS,
|
||||
shortcutsDelimiter: "space",
|
||||
}),
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
bookmark_topic: buildShortcut("actions.bookmark_topic", {
|
||||
keys1: ["f"],
|
||||
}),
|
||||
reply_as_new_topic: buildShortcut("actions.reply_as_new_topic", {
|
||||
keys1: ["t"],
|
||||
}),
|
||||
reply_topic: buildShortcut("actions.reply_topic", {
|
||||
keys1: [SHIFT, "r"],
|
||||
keysDelimiter: PLUS,
|
||||
}),
|
||||
reply_post: buildShortcut("actions.reply_post", { keys1: ["r"] }),
|
||||
quote_post: buildShortcut("actions.quote_post", { keys1: ["q"] }),
|
||||
pin_unpin_topic: buildShortcut("actions.pin_unpin_topic", {
|
||||
keys1: [SHIFT, "p"],
|
||||
keysDelimiter: PLUS,
|
||||
}),
|
||||
share_topic: buildShortcut("actions.share_topic", {
|
||||
keys1: [SHIFT, "s"],
|
||||
keysDelimiter: PLUS,
|
||||
}),
|
||||
share_post: buildShortcut("actions.share_post", { keys1: ["s"] }),
|
||||
like: buildShortcut("actions.like", { keys1: ["l"] }),
|
||||
flag: buildShortcut("actions.flag", { keys1: ["!"] }),
|
||||
bookmark: buildShortcut("actions.bookmark", { keys1: ["b"] }),
|
||||
edit: buildShortcut("actions.edit", { keys1: ["e"] }),
|
||||
delete: buildShortcut("actions.delete", { keys1: ["d"] }),
|
||||
mark_muted: buildShortcut("actions.mark_muted", { keys1: ["m", "m"] }),
|
||||
mark_regular: buildShortcut("actions.mark_regular", {
|
||||
keys1: ["m", "r"],
|
||||
}),
|
||||
mark_tracking: buildShortcut("actions.mark_tracking", {
|
||||
keys1: ["m", "t"],
|
||||
}),
|
||||
mark_watching: buildShortcut("actions.mark_watching", {
|
||||
keys1: ["m", "w"],
|
||||
}),
|
||||
print: buildShortcut("actions.print", {
|
||||
keys1: [translateModKey("Meta"), "p"],
|
||||
keysDelimiter: PLUS,
|
||||
}),
|
||||
defer: buildShortcut("actions.defer", {
|
||||
keys1: [SHIFT, "u"],
|
||||
keysDelimiter: PLUS,
|
||||
}),
|
||||
topic_admin_actions: buildShortcut("actions.topic_admin_actions", {
|
||||
keys1: [SHIFT, "a"],
|
||||
keysDelimiter: PLUS,
|
||||
}),
|
||||
shortcuts: {
|
||||
bookmark_topic: buildShortcut("actions.bookmark_topic", {
|
||||
keys1: ["f"],
|
||||
}),
|
||||
reply_as_new_topic: buildShortcut("actions.reply_as_new_topic", {
|
||||
keys1: ["t"],
|
||||
}),
|
||||
reply_topic: buildShortcut("actions.reply_topic", {
|
||||
keys1: [SHIFT, "r"],
|
||||
keysDelimiter: PLUS,
|
||||
}),
|
||||
reply_post: buildShortcut("actions.reply_post", { keys1: ["r"] }),
|
||||
quote_post: buildShortcut("actions.quote_post", { keys1: ["q"] }),
|
||||
pin_unpin_topic: buildShortcut("actions.pin_unpin_topic", {
|
||||
keys1: [SHIFT, "p"],
|
||||
keysDelimiter: PLUS,
|
||||
}),
|
||||
share_topic: buildShortcut("actions.share_topic", {
|
||||
keys1: [SHIFT, "s"],
|
||||
keysDelimiter: PLUS,
|
||||
}),
|
||||
share_post: buildShortcut("actions.share_post", { keys1: ["s"] }),
|
||||
like: buildShortcut("actions.like", { keys1: ["l"] }),
|
||||
flag: buildShortcut("actions.flag", { keys1: ["!"] }),
|
||||
bookmark: buildShortcut("actions.bookmark", { keys1: ["b"] }),
|
||||
edit: buildShortcut("actions.edit", { keys1: ["e"] }),
|
||||
delete: buildShortcut("actions.delete", { keys1: ["d"] }),
|
||||
mark_muted: buildShortcut("actions.mark_muted", {
|
||||
keys1: ["m", "m"],
|
||||
}),
|
||||
mark_regular: buildShortcut("actions.mark_regular", {
|
||||
keys1: ["m", "r"],
|
||||
}),
|
||||
mark_tracking: buildShortcut("actions.mark_tracking", {
|
||||
keys1: ["m", "t"],
|
||||
}),
|
||||
mark_watching: buildShortcut("actions.mark_watching", {
|
||||
keys1: ["m", "w"],
|
||||
}),
|
||||
print: buildShortcut("actions.print", {
|
||||
keys1: [META, "p"],
|
||||
keysDelimiter: PLUS,
|
||||
}),
|
||||
defer: buildShortcut("actions.defer", {
|
||||
keys1: [SHIFT, "u"],
|
||||
keysDelimiter: PLUS,
|
||||
}),
|
||||
topic_admin_actions: buildShortcut("actions.topic_admin_actions", {
|
||||
keys1: [SHIFT, "a"],
|
||||
keysDelimiter: PLUS,
|
||||
}),
|
||||
},
|
||||
},
|
||||
navigation: {
|
||||
shortcuts: {
|
||||
back: buildShortcut("navigation.back", { keys1: ["u"] }),
|
||||
jump: buildShortcut("navigation.jump", { keys1: ["#"] }),
|
||||
up_down: buildShortcut("navigation.up_down", {
|
||||
keys1: ["k"],
|
||||
keys2: ["j"],
|
||||
shortcutsDelimiter: "slash",
|
||||
}),
|
||||
open: buildShortcut("navigation.open", {
|
||||
keys1: ["o"],
|
||||
keys2: [ENTER],
|
||||
}),
|
||||
next_prev: buildShortcut("navigation.next_prev", {
|
||||
keys1: [SHIFT, "j"],
|
||||
keys2: [SHIFT, "k"],
|
||||
keysDelimiter: PLUS,
|
||||
shortcutsDelimiter: "slash",
|
||||
}),
|
||||
go_to_unread_post: buildShortcut("navigation.go_to_unread_post", {
|
||||
keys1: [SHIFT, "l"],
|
||||
keysDelimiter: PLUS,
|
||||
}),
|
||||
},
|
||||
},
|
||||
composing: {
|
||||
shortcuts: {
|
||||
return: buildShortcut("composing.return", {
|
||||
keys1: [SHIFT, "c"],
|
||||
keysDelimiter: PLUS,
|
||||
}),
|
||||
fullscreen: buildShortcut("composing.fullscreen", {
|
||||
keys1: [SHIFT, "F11"],
|
||||
keysDelimiter: PLUS,
|
||||
}),
|
||||
},
|
||||
},
|
||||
bookmarks: {
|
||||
shortcuts: {
|
||||
enter: buildShortcut("bookmarks.enter", { keys1: [ENTER] }),
|
||||
later_today: buildShortcut("bookmarks.later_today", {
|
||||
keys1: ["l", "t"],
|
||||
shortcutsDelimiter: "space",
|
||||
}),
|
||||
later_this_week: buildShortcut("bookmarks.later_this_week", {
|
||||
keys1: ["l", "w"],
|
||||
shortcutsDelimiter: "space",
|
||||
}),
|
||||
tomorrow: buildShortcut("bookmarks.tomorrow", {
|
||||
keys1: ["n", "d"],
|
||||
shortcutsDelimiter: "space",
|
||||
}),
|
||||
next_business_week: buildShortcut("bookmarks.next_business_week", {
|
||||
keys1: ["n", "b", "w"],
|
||||
shortcutsDelimiter: "space",
|
||||
}),
|
||||
next_business_day: buildShortcut("bookmarks.next_business_day", {
|
||||
keys1: ["n", "b", "d"],
|
||||
shortcutsDelimiter: "space",
|
||||
}),
|
||||
custom: buildShortcut("bookmarks.custom", {
|
||||
keys1: ["c", "r"],
|
||||
shortcutsDelimiter: "space",
|
||||
}),
|
||||
none: buildShortcut("bookmarks.none", {
|
||||
keys1: ["n", "r"],
|
||||
shortcutsDelimiter: "space",
|
||||
}),
|
||||
delete: buildShortcut("bookmarks.delete", {
|
||||
keys1: ["d", "d"],
|
||||
shortcutsDelimiter: "space",
|
||||
}),
|
||||
},
|
||||
},
|
||||
search_menu: {
|
||||
prev_next: buildShortcut("search_menu.prev_next", {
|
||||
keys1: ["↑"],
|
||||
keys2: ["↓"],
|
||||
shortcutsDelimiter: "slash",
|
||||
}),
|
||||
insert_url: buildShortcut("search_menu.insert_url", {
|
||||
keys1: ["a"],
|
||||
}),
|
||||
full_page_search: buildShortcut("search_menu.full_page_search", {
|
||||
keys1: [translateModKey("Meta"), "Enter"],
|
||||
keysDelimiter: PLUS,
|
||||
}),
|
||||
shortcuts: {
|
||||
prev_next: buildShortcut("search_menu.prev_next", {
|
||||
keys1: ["↑"],
|
||||
keys2: ["↓"],
|
||||
shortcutsDelimiter: "slash",
|
||||
}),
|
||||
insert_url: buildShortcut("search_menu.insert_url", {
|
||||
keys1: ["a"],
|
||||
}),
|
||||
full_page_search: buildShortcut("search_menu.full_page_search", {
|
||||
keys1: [META, "Enter"],
|
||||
keysDelimiter: PLUS,
|
||||
}),
|
||||
},
|
||||
},
|
||||
};
|
||||
this._buildExtraShortcuts(shortcuts);
|
||||
this._addCountsToShortcutCategories(shortcuts);
|
||||
this.set("shortcuts", shortcuts);
|
||||
},
|
||||
|
||||
_buildExtraShortcuts(shortcuts) {
|
||||
for (const [category, helps] of Object.entries(
|
||||
extraKeyboardShortcutsHelp
|
||||
)) {
|
||||
helps.forEach((help) => {
|
||||
if (!shortcuts[category]) {
|
||||
shortcuts[category] = {};
|
||||
}
|
||||
|
||||
if (!shortcuts[category].shortcuts) {
|
||||
shortcuts[category].shortcuts = {};
|
||||
}
|
||||
|
||||
shortcuts[category].shortcuts[help.name] = buildShortcut(
|
||||
help.name,
|
||||
this._transformExtraDefinition(help.definition)
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_addCountsToShortcutCategories(shortcuts) {
|
||||
for (const [category, shortcutCategory] of Object.entries(shortcuts)) {
|
||||
shortcuts[category].count = Object.keys(
|
||||
shortcutCategory.shortcuts
|
||||
).length;
|
||||
}
|
||||
},
|
||||
|
||||
_transformExtraDefinition(definition) {
|
||||
if (definition.keys1) {
|
||||
definition.keys1 = definition.keys1.map((key) =>
|
||||
this._translateKeys(key)
|
||||
);
|
||||
}
|
||||
if (definition.keys2) {
|
||||
definition.keys2 = definition.keys2.map((key) =>
|
||||
this._translateKeys(key)
|
||||
);
|
||||
}
|
||||
if (definition.keysDelimiter) {
|
||||
definition.keysDelimiter = this._translateKeys(definition.keysDelimiter);
|
||||
}
|
||||
if (definition.shortcutsDelimiter) {
|
||||
definition.shortcutsDelimiter = this._translateKeys(
|
||||
definition.shortcutsDelimiter
|
||||
);
|
||||
}
|
||||
return definition;
|
||||
},
|
||||
|
||||
_translateKeys(string) {
|
||||
for (const [matcher, replacement] of Object.entries(
|
||||
translationForExtraShortcuts
|
||||
)) {
|
||||
string = string.replace(matcher, replacement);
|
||||
}
|
||||
return string;
|
||||
},
|
||||
|
||||
_buildJumpToSection() {
|
||||
const shortcuts = {
|
||||
home: buildShortcut("jump_to.home", { keys1: ["g", "h"] }),
|
||||
latest: buildShortcut("jump_to.latest", { keys1: ["g", "l"] }),
|
||||
new: buildShortcut("jump_to.new", { keys1: ["g", "n"] }),
|
||||
unread: buildShortcut("jump_to.unread", { keys1: ["g", "u"] }),
|
||||
categories: buildShortcut("jump_to.categories", { keys1: ["g", "c"] }),
|
||||
top: buildShortcut("jump_to.top", { keys1: ["g", "t"] }),
|
||||
bookmarks: buildShortcut("jump_to.bookmarks", { keys1: ["g", "b"] }),
|
||||
profile: buildShortcut("jump_to.profile", { keys1: ["g", "p"] }),
|
||||
};
|
||||
if (this.siteSettings.enable_personal_messages) {
|
||||
shortcuts.messages = buildShortcut("jump_to.messages", {
|
||||
keys1: ["g", "m"],
|
||||
});
|
||||
}
|
||||
Object.assign(shortcuts, {
|
||||
drafts: buildShortcut("jump_to.drafts", { keys1: ["g", "d"] }),
|
||||
next: buildShortcut("jump_to.next", { keys1: ["g", "j"] }),
|
||||
previous: buildShortcut("jump_to.previous", { keys1: ["g", "k"] }),
|
||||
});
|
||||
return shortcuts;
|
||||
},
|
||||
});
|
||||
|
|
|
@ -13,6 +13,24 @@ import { INPUT_DELAY } from "discourse-common/config/environment";
|
|||
import { ajax } from "discourse/lib/ajax";
|
||||
import { headerOffset } from "discourse/lib/offset-calculator";
|
||||
|
||||
let extraKeyboardShortcutsHelp = {};
|
||||
function addExtraKeyboardShortcutHelp(help) {
|
||||
const category = help.category;
|
||||
if (extraKeyboardShortcutsHelp[category]) {
|
||||
extraKeyboardShortcutsHelp[category] = extraKeyboardShortcutsHelp[
|
||||
category
|
||||
].concat([help]);
|
||||
} else {
|
||||
extraKeyboardShortcutsHelp[category] = [help];
|
||||
}
|
||||
}
|
||||
|
||||
export function clearExtraKeyboardShortcutHelp() {
|
||||
extraKeyboardShortcutsHelp = {};
|
||||
}
|
||||
|
||||
export { extraKeyboardShortcutsHelp as extraKeyboardShortcutsHelp };
|
||||
|
||||
const DEFAULT_BINDINGS = {
|
||||
"!": { postAction: "showFlags" },
|
||||
"#": { handler: "goToPost", anonymous: true },
|
||||
|
@ -209,6 +227,16 @@ export default {
|
|||
* - path - a specific path to limit the shortcut to .e.g /latest
|
||||
* - postAction - binds the shortcut to fire the specified post action when a
|
||||
* post is selected
|
||||
* - help - adds the shortcut to the keyboard shortcuts modal. `help` is an object
|
||||
* with key/value pairs
|
||||
* {
|
||||
* category: String,
|
||||
* name: String,
|
||||
* definition: (See function `buildShortcut` in
|
||||
* app/assets/javascripts/discourse/app/controllers/keyboard-shortcuts-help.js
|
||||
* for definition structure)
|
||||
* }
|
||||
*
|
||||
* - click - allows to provide a selector on which a click event
|
||||
* will be triggered, eg: { click: ".topic.last .title" }
|
||||
**/
|
||||
|
@ -218,6 +246,9 @@ export default {
|
|||
shortcut = shortcut.trim();
|
||||
let newBinding = Object.assign({ handler: callback }, opts);
|
||||
this.bindKey(shortcut, newBinding);
|
||||
if (opts.help) {
|
||||
addExtraKeyboardShortcutHelp(opts.help);
|
||||
}
|
||||
},
|
||||
|
||||
// unbinds all the shortcuts in a key binding object e.g.
|
||||
|
|
|
@ -1,107 +1,12 @@
|
|||
{{#d-modal-body id="keyboard-shortcuts-help"}}
|
||||
<div class="column">
|
||||
<section>
|
||||
<h4>{{i18n "keyboard_shortcuts_help.jump_to.title"}}</h4>
|
||||
{{#each-in shortcuts as |category shortcutCategory|}}
|
||||
<section class="shortcut-category span-{{shortcutCategory.count}} shortcut-category-{{category}}">
|
||||
<h4>{{i18n (concat "keyboard_shortcuts_help." category ".title")}}</h4>
|
||||
<ul>
|
||||
<li>{{html-safe shortcuts.jump_to.home}}</li>
|
||||
<li>{{html-safe shortcuts.jump_to.latest}}</li>
|
||||
<li>{{html-safe shortcuts.jump_to.new}}</li>
|
||||
<li>{{html-safe shortcuts.jump_to.unread}}</li>
|
||||
<li>{{html-safe shortcuts.jump_to.categories}}</li>
|
||||
<li>{{html-safe shortcuts.jump_to.top}}</li>
|
||||
<li>{{html-safe shortcuts.jump_to.bookmarks}}</li>
|
||||
<li>{{html-safe shortcuts.jump_to.profile}}</li>
|
||||
{{#if siteSettings.enable_personal_messages}}
|
||||
<li>{{html-safe shortcuts.jump_to.messages}}</li>
|
||||
{{/if}}
|
||||
<li>{{html-safe shortcuts.jump_to.drafts}}</li>
|
||||
<li>{{html-safe shortcuts.jump_to.next}}</li>
|
||||
<li>{{html-safe shortcuts.jump_to.previous}}</li>
|
||||
{{#each-in shortcutCategory.shortcuts as |name shortcut|}}
|
||||
<li>{{html-safe shortcut}}</li>
|
||||
{{/each-in}}
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h4>{{i18n "keyboard_shortcuts_help.navigation.title"}}</h4>
|
||||
<ul>
|
||||
<li>{{html-safe shortcuts.navigation.back}}</li>
|
||||
<li>{{html-safe shortcuts.navigation.jump}}</li>
|
||||
<li>{{html-safe shortcuts.navigation.up_down}}</li>
|
||||
<li>{{html-safe shortcuts.navigation.open}}</li>
|
||||
<li>{{html-safe shortcuts.navigation.next_prev}}</li>
|
||||
<li>{{html-safe shortcuts.navigation.go_to_unread_post}}</li>
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
<div class="column">
|
||||
<section>
|
||||
<h4>{{i18n "keyboard_shortcuts_help.application.title"}}</h4>
|
||||
<ul>
|
||||
<li>{{html-safe shortcuts.application.hamburger_menu}}</li>
|
||||
<li>{{html-safe shortcuts.application.user_profile_menu}}</li>
|
||||
<li>{{html-safe shortcuts.application.show_incoming_updated_topics}}</li>
|
||||
<li>{{html-safe shortcuts.application.search}}</li>
|
||||
<li>{{html-safe shortcuts.application.help}}</li>
|
||||
<li>{{html-safe shortcuts.application.dismiss_new}}</li>
|
||||
<li>{{html-safe shortcuts.application.dismiss_topics}}</li>
|
||||
<li>{{html-safe shortcuts.application.log_out}}</li>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h4>{{i18n "keyboard_shortcuts_help.composing.title"}}</h4>
|
||||
<ul>
|
||||
<li>{{html-safe shortcuts.composing.return}}</li>
|
||||
<li>{{html-safe shortcuts.composing.fullscreen}}</li>
|
||||
<li>{{html-safe shortcuts.application.create}}</li>
|
||||
<li>{{html-safe shortcuts.actions.reply_as_new_topic}}</li>
|
||||
<li>{{html-safe shortcuts.actions.reply_topic}}</li>
|
||||
<li>{{html-safe shortcuts.actions.reply_post}}</li>
|
||||
<li>{{html-safe shortcuts.actions.quote_post}}</li>
|
||||
</ul>
|
||||
</section>
|
||||
<section class="keyboard-shortcuts-bookmark-section">
|
||||
<h4>{{i18n "keyboard_shortcuts_help.bookmarks.title"}}</h4>
|
||||
<ul>
|
||||
<li>{{html-safe shortcuts.bookmarks.enter}}</li>
|
||||
<li>{{html-safe shortcuts.bookmarks.later_today}}</li>
|
||||
<li>{{html-safe shortcuts.bookmarks.later_this_week}}</li>
|
||||
<li>{{html-safe shortcuts.bookmarks.tomorrow}}</li>
|
||||
<li>{{html-safe shortcuts.bookmarks.next_month}}</li>
|
||||
<li>{{html-safe shortcuts.bookmarks.next_business_week}}</li>
|
||||
<li>{{html-safe shortcuts.bookmarks.next_business_day}}</li>
|
||||
<li>{{html-safe shortcuts.bookmarks.custom}}</li>
|
||||
<li>{{html-safe shortcuts.bookmarks.none}}</li>
|
||||
<li>{{html-safe shortcuts.bookmarks.delete}}</li>
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
<div class="column">
|
||||
<section>
|
||||
<h4>{{i18n "keyboard_shortcuts_help.actions.title"}}</h4>
|
||||
<ul>
|
||||
<li>{{html-safe shortcuts.actions.bookmark_topic}}</li>
|
||||
<li>{{html-safe shortcuts.actions.pin_unpin_topic}}</li>
|
||||
<li>{{html-safe shortcuts.actions.share_topic}}</li>
|
||||
<li>{{html-safe shortcuts.actions.share_post}}</li>
|
||||
<li>{{html-safe shortcuts.actions.like}}</li>
|
||||
<li>{{html-safe shortcuts.actions.flag}}</li>
|
||||
<li>{{html-safe shortcuts.actions.bookmark}}</li>
|
||||
<li>{{html-safe shortcuts.actions.edit}}</li>
|
||||
<li>{{html-safe shortcuts.actions.delete}}</li>
|
||||
<li>{{html-safe shortcuts.actions.mark_muted}}</li>
|
||||
<li>{{html-safe shortcuts.actions.mark_regular}}</li>
|
||||
<li>{{html-safe shortcuts.actions.mark_tracking}}</li>
|
||||
<li>{{html-safe shortcuts.actions.mark_watching}}</li>
|
||||
<li>{{html-safe shortcuts.actions.defer}}</li>
|
||||
<li>{{html-safe shortcuts.actions.print}}</li>
|
||||
<li>{{html-safe shortcuts.actions.topic_admin_actions}}</li>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h4>{{i18n "keyboard_shortcuts_help.search_menu.title"}}</h4>
|
||||
<ul>
|
||||
<li>{{html-safe shortcuts.search_menu.prev_next}}</li>
|
||||
<li>{{html-safe shortcuts.search_menu.insert_url}}</li>
|
||||
<li>{{html-safe shortcuts.search_menu.full_page_search}}</li>
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
{{/each-in}}
|
||||
{{/d-modal-body}}
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
import { triggerKeyEvent, visit } from "@ember/test-helpers";
|
||||
import { click, triggerKeyEvent, visit } from "@ember/test-helpers";
|
||||
import KeyboardShortcuts from "discourse/lib/keyboard-shortcuts";
|
||||
import { acceptance } from "discourse/tests/helpers/qunit-helpers";
|
||||
import {
|
||||
acceptance,
|
||||
count,
|
||||
exists,
|
||||
} from "discourse/tests/helpers/qunit-helpers";
|
||||
import sinon from "sinon";
|
||||
import { test } from "qunit";
|
||||
import { withPluginApi } from "discourse/lib/plugin-api";
|
||||
|
@ -48,4 +52,50 @@ acceptance("Plugin Keyboard Shortcuts - Anonymous", function () {
|
|||
"bindToPath is called due to options provided"
|
||||
);
|
||||
});
|
||||
|
||||
test("a plugin can add a shortcut and create a new category in the shortcut help modal", async function (assert) {
|
||||
withPluginApi("0.8.38", (api) => {
|
||||
api.addKeyboardShortcut("meta+]", () => {}, {
|
||||
help: {
|
||||
category: "new_category",
|
||||
name: "new_category.test",
|
||||
definition: {
|
||||
keys1: ["meta", "]"],
|
||||
keys2: ["meta", "["],
|
||||
keysDelimiter: "plus",
|
||||
shortcutsDelimiter: "slash",
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
await visit("/");
|
||||
await triggerKeyEvent(document, "keypress", "?".charCodeAt(0));
|
||||
assert.ok(exists(".shortcut-category-new_category"));
|
||||
assert.strictEqual(count(".shortcut-category-new_category li"), 1);
|
||||
});
|
||||
|
||||
test("a plugin can add a shortcut to and existing category in the shortcut help modal", async function (assert) {
|
||||
await visit("/");
|
||||
await triggerKeyEvent(document, "keypress", "?".charCodeAt(0));
|
||||
const countBefore = count(".shortcut-category-application li");
|
||||
await click(".modal-close");
|
||||
|
||||
withPluginApi("0.8.38", (api) => {
|
||||
api.addKeyboardShortcut("meta+]", () => {}, {
|
||||
help: {
|
||||
category: "application",
|
||||
name: "application.test",
|
||||
definition: {
|
||||
keys1: ["]"],
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
await triggerKeyEvent(document, "keypress", "?".charCodeAt(0));
|
||||
assert.strictEqual(
|
||||
count(".shortcut-category-application li"),
|
||||
countBefore + 1
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -45,6 +45,7 @@ import {
|
|||
} from "discourse/lib/topic-list-tracker";
|
||||
import sinon from "sinon";
|
||||
import siteFixtures from "discourse/tests/fixtures/site-fixtures";
|
||||
import { clearExtraKeyboardShortcutHelp } from "discourse/lib/keyboard-shortcuts";
|
||||
import { clearResolverOptions } from "discourse-common/resolver";
|
||||
import { clearNavItems } from "discourse/models/nav-item";
|
||||
import {
|
||||
|
@ -168,6 +169,7 @@ function testCleanup(container, app) {
|
|||
resetComposerCustomizations();
|
||||
resetQuickSearchRandomTips();
|
||||
resetPostMenuExtraButtons();
|
||||
clearExtraKeyboardShortcutHelp();
|
||||
clearNavItems();
|
||||
setTopicList(null);
|
||||
_clearSnapshots();
|
||||
|
|
|
@ -36,10 +36,17 @@
|
|||
}
|
||||
|
||||
#keyboard-shortcuts-help {
|
||||
display: flex;
|
||||
display: grid;
|
||||
grid-gap: 0.5em 1.25em;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.column {
|
||||
width: 33.3%;
|
||||
@for $i from 1 through 25 {
|
||||
.span-#{$i} {
|
||||
grid-row-end: span $i;
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
|
|
Loading…
Reference in New Issue