FIX: make composer full screen shortcut work when inputs have focus (#6907)

- Uses a Mousetrap plugin for global shortcuts
- Implemented for search `ctrl+alt+f` and composer fullscreen `shift+f11` shortcuts
This commit is contained in:
Penar Musaraj
2019-02-14 00:19:27 -05:00
committed by GitHub
parent ed6f4dfc40
commit 090e9c8432
8 changed files with 76 additions and 16 deletions

View File

@ -108,6 +108,13 @@ export default Ember.Component.extend({
this._resetUpload(true); this._resetUpload(true);
}, },
@observes("focusTarget")
setFocus() {
if (this.get("focusTarget") === "editor") {
this.$("textarea").putCursorAtEnd();
}
},
@computed @computed
markdownOptions() { markdownOptions() {
return { return {

View File

@ -256,15 +256,6 @@ export default Ember.Component.extend({
const mouseTrap = Mousetrap(this.$(".d-editor-input")[0]); const mouseTrap = Mousetrap(this.$(".d-editor-input")[0]);
const shortcuts = this.get("toolbar.shortcuts"); const shortcuts = this.get("toolbar.shortcuts");
// for some reason I am having trouble bubbling this so hack it in
mouseTrap.bind(["ctrl+alt+f"], event => {
this.appEvents.trigger("header:keyboard-trigger", {
type: "search",
event
});
return true;
});
Object.keys(shortcuts).forEach(sc => { Object.keys(shortcuts).forEach(sc => {
const button = shortcuts[sc]; const button = shortcuts[sc];
mouseTrap.bind(sc, () => { mouseTrap.bind(sc, () => {
@ -323,7 +314,6 @@ export default Ember.Component.extend({
Object.keys(this.get("toolbar.shortcuts")).forEach(sc => Object.keys(this.get("toolbar.shortcuts")).forEach(sc =>
mouseTrap.unbind(sc) mouseTrap.unbind(sc)
); );
mouseTrap.unbind("ctrl+/", "command+/");
this.$(".d-editor-preview").off("click.preview"); this.$(".d-editor-preview").off("click.preview");
}, },

View File

@ -133,9 +133,10 @@ export default Ember.Controller.extend({
@computed( @computed(
"model.replyingToTopic", "model.replyingToTopic",
"model.creatingPrivateMessage", "model.creatingPrivateMessage",
"model.targetUsernames" "model.targetUsernames",
"model.composeState"
) )
focusTarget(replyingToTopic, creatingPM, usernames) { focusTarget(replyingToTopic, creatingPM, usernames, composeState) {
if (this.capabilities.isIOS) { if (this.capabilities.isIOS) {
return "none"; return "none";
} }
@ -153,6 +154,10 @@ export default Ember.Controller.extend({
return "reply"; return "reply";
} }
if (composeState === Composer.FULLSCREEN) {
return "editor";
}
return "title"; return "title";
}, },

View File

@ -7,7 +7,7 @@ const bindings = {
"!": { postAction: "showFlags" }, "!": { postAction: "showFlags" },
"#": { handler: "goToPost", anonymous: true }, "#": { handler: "goToPost", anonymous: true },
"/": { handler: "toggleSearch", anonymous: true }, "/": { handler: "toggleSearch", anonymous: true },
"ctrl+alt+f": { handler: "toggleSearch", anonymous: true }, "ctrl+alt+f": { handler: "toggleSearch", anonymous: true, global: true },
"=": { handler: "toggleHamburgerMenu", anonymous: true }, "=": { handler: "toggleHamburgerMenu", anonymous: true },
"?": { handler: "showHelpModal", anonymous: true }, "?": { handler: "showHelpModal", anonymous: true },
".": { click: ".alert.alert-info.clickable", anonymous: true }, // show incoming/updated topics ".": { click: ".alert.alert-info.clickable", anonymous: true }, // show incoming/updated topics
@ -67,7 +67,7 @@ const bindings = {
"shift+s": { click: "#topic-footer-buttons button.share", anonymous: true }, // share topic "shift+s": { click: "#topic-footer-buttons button.share", anonymous: true }, // share topic
"shift+u": { handler: "goToUnreadPost" }, "shift+u": { handler: "goToUnreadPost" },
"shift+z shift+z": { handler: "logout" }, "shift+z shift+z": { handler: "logout" },
"shift+f11": { handler: "fullscreenComposer" }, "shift+f11": { handler: "fullscreenComposer", global: true },
t: { postAction: "replyAsNewTopic" }, t: { postAction: "replyAsNewTopic" },
u: { handler: "goBack", anonymous: true }, u: { handler: "goBack", anonymous: true },
"x r": { "x r": {
@ -101,7 +101,12 @@ export default {
if (binding.path) { if (binding.path) {
this._bindToPath(binding.path, key); this._bindToPath(binding.path, key);
} else if (binding.handler) { } else if (binding.handler) {
if (binding.global) {
// global shortcuts will trigger even while focusing on input/textarea
this._globalBindToFunction(binding.handler, key);
} else {
this._bindToFunction(binding.handler, key); this._bindToFunction(binding.handler, key);
}
} else if (binding.postAction) { } else if (binding.postAction) {
this._bindToSelectedPost(binding.postAction, key); this._bindToSelectedPost(binding.postAction, key);
} else if (binding.click) { } else if (binding.click) {
@ -399,6 +404,12 @@ export default {
}); });
}, },
_globalBindToFunction(func, binding) {
if (typeof this[func] === "function") {
this.keyTrapper.bindGlobal(binding, _.bind(this[func], this));
}
},
_bindToFunction(func, binding) { _bindToFunction(func, binding) {
if (typeof this[func] === "function") { if (typeof this[func] === "function") {
this.keyTrapper.bind(binding, _.bind(this[func], this)); this.keyTrapper.bind(binding, _.bind(this[func], this));

View File

@ -111,7 +111,8 @@
importQuote=(action "importQuote") importQuote=(action "importQuote")
togglePreview=(action "togglePreview") togglePreview=(action "togglePreview")
showToolbar=showToolbar showToolbar=showToolbar
afterRefresh=(action "afterRefresh")}} afterRefresh=(action "afterRefresh")
focusTarget=focusTarget}}
<div class='submit-panel'> <div class='submit-panel'>
{{plugin-outlet name="composer-fields-below" args=(hash model=model)}} {{plugin-outlet name="composer-fields-below" args=(hash model=model)}}

View File

@ -22,6 +22,7 @@
//= require jquery.sortable.js //= require jquery.sortable.js
//= require lodash.js //= require lodash.js
//= require mousetrap.js //= require mousetrap.js
//= require mousetrap-global-bind.js
//= require rsvp.js //= require rsvp.js
//= require show-html.js //= require show-html.js
//= require break_string //= require break_string

View File

@ -73,6 +73,8 @@ task 'javascript:update' do
destination: 'moment-locale', destination: 'moment-locale',
}, { }, {
source: 'moment-timezone/builds/moment-timezone-with-data.js' source: 'moment-timezone/builds/moment-timezone-with-data.js'
}, {
source: 'mousetrap/plugins/global-bind/mousetrap-global-bind.js'
}, { }, {
source: 'resumablejs/resumable.js' source: 'resumablejs/resumable.js'
}, { }, {

View File

@ -0,0 +1,43 @@
/**
* adds a bindGlobal method to Mousetrap that allows you to
* bind specific keyboard shortcuts that will still work
* inside a text input field
*
* usage:
* Mousetrap.bindGlobal('ctrl+s', _saveChanges);
*/
/* global Mousetrap:true */
(function(Mousetrap) {
var _globalCallbacks = {};
var _originalStopCallback = Mousetrap.prototype.stopCallback;
Mousetrap.prototype.stopCallback = function(e, element, combo, sequence) {
var self = this;
if (self.paused) {
return true;
}
if (_globalCallbacks[combo] || _globalCallbacks[sequence]) {
return false;
}
return _originalStopCallback.call(self, e, element, combo);
};
Mousetrap.prototype.bindGlobal = function(keys, callback, action) {
var self = this;
self.bind(keys, callback, action);
if (keys instanceof Array) {
for (var i = 0; i < keys.length; i++) {
_globalCallbacks[keys[i]] = true;
}
return;
}
_globalCallbacks[keys] = true;
};
Mousetrap.init();
}) (Mousetrap);