Implement a new system to handle "escape actions"

The new EscapeActions object decide what to do when the user press the
Escape key (such as closing a opened popup or inlined form).

This commit also re-introduced the sidebar current view as a sidebar
component local state.
This commit is contained in:
Maxime Quandalle 2015-05-26 20:30:01 +02:00
parent 1b4fcc67f4
commit 40c2411f2a
13 changed files with 148 additions and 53 deletions

View file

@ -39,7 +39,7 @@ Router.route('/boards/:boardId/:slug/:cardId', {
template: 'board',
onAfterAction: function() {
Tracker.nonreactive(function() {
if (! Session.get('currentCard') && typeof Sidebar !== 'undefined') {
if (! Session.get('currentCard') && Sidebar) {
Sidebar.hide();
}
});
@ -55,3 +55,9 @@ Router.route('/boards/:boardId/:slug/:cardId', {
return Boards.findOne(this.params.boardId);
}
});
// Close the card details pane by pressing escape
EscapeActions.register(50,
function() { return ! Session.equals('currentCard', null); },
function() { Utils.goBoardId(Session.get('currentBoard')); }
);

View file

@ -26,7 +26,8 @@ template(name="cardDetails")
h3 Description
+inlinedForm(classNames="js-card-description")
i.fa.fa-times.js-close-inlined-form
textarea(autofocus)= description
+editor(autofocus=true)
= description
button(type="submit") {{_ 'edit'}}
else
.js-open-inlined-form

View file

@ -15,7 +15,9 @@
// We can only have one inlined form element opened at a time
// XXX Could we avoid using a global here ? This is used in Mousetrap
// keyboard.js
currentlyOpenedForm = new ReactiveVar(null);
var currentlyOpenedForm = new ReactiveVar(null);
var inlinedFormEscapePriority = 30;
BlazeComponent.extendComponent({
template: function() {
@ -32,9 +34,10 @@ BlazeComponent.extendComponent({
open: function() {
// Close currently opened form, if any
if (currentlyOpenedForm.get() !== null) {
currentlyOpenedForm.get().close();
}
// if (currentlyOpenedForm.get() !== null) {
// currentlyOpenedForm.get().close();
// }
EscapeActions.executeLowerThan(inlinedFormEscapePriority);
this.isOpen.set(true);
currentlyOpenedForm.set(this);
},
@ -46,7 +49,8 @@ BlazeComponent.extendComponent({
},
getValue: function() {
return this.isOpen.get() && this.find('textarea,input[type=text]').value;
var input = this.find('textarea,input[type=text]');
return this.isOpen.get() && input && input.value;
},
saveValue: function() {
@ -66,7 +70,7 @@ BlazeComponent.extendComponent({
'keydown form input, keydown form textarea': function(evt) {
if (evt.keyCode === 27) {
evt.preventDefault();
this.close();
EscapeActions.executeLowest();
}
},
@ -91,3 +95,9 @@ BlazeComponent.extendComponent({
}];
}
}).register('inlinedForm');
// Press escape to close the currently opened inlinedForm
EscapeActions.register(inlinedFormEscapePriority,
function() { return currentlyOpenedForm.get() !== null; },
function() { currentlyOpenedForm.get().close(); }
);

View file

@ -1,5 +1,9 @@
Template.editor.rendered = function() {
this.$('textarea').textcomplete([
var dropdownMenuIsOpened = false;
Template.editor.onRendered(function() {
var $textarea = this.$('textarea');
$textarea.textcomplete([
// Emojies
{
match: /\B:([\-+\w]*)$/,
@ -37,4 +41,26 @@ Template.editor.rendered = function() {
index: 1
}
]);
};
// Since commit d474017 jquery-textComplete automatically closes a potential
// opened dropdown menu when the user press Escape. This behavior conflicts
// with our EscapeActions system, but it's too complicated and hacky to
// monkey-pach textComplete to disable it -- I tried. Instead we listen to
// 'open' and 'hide' events, and create a ghost escapeAction when the dropdown
// is opened (and rely on textComplete to execute the actual action).
$textarea.on({
'textComplete:show': function() {
dropdownMenuIsOpened = true;
},
'textComplete:hide': function() {
Tracker.afterFlush(function() {
dropdownMenuIsOpened = false;
});
}
});
});
EscapeActions.register(10,
function() { return dropdownMenuIsOpened; },
function() {}
);

View file

@ -1,8 +0,0 @@
Template.editor.events({
// Pressing Ctrl+Enter should submit the form.
'keydown textarea': function(event) {
if (event.keyCode === 13 && (event.metaKey || event.ctrlKey)) {
$(event.currentTarget).parents('form:first').submit();
}
}
});

View file

@ -12,7 +12,7 @@
</template>
<template name="editor">
<textarea class="{{class}}" placeholder="{{_ 'comment-placeholder'}}" id="{{id}}" tabindex="1">{{> UI.contentBlock }}</textarea>
<textarea class="{{class}}" placeholder="{{_ 'comment-placeholder'}}" id="{{id}}" autofocus="{{autofocus}}">{{> UI.contentBlock}}</textarea>
</template>
<template name="viewer">{{#markdown}}{{#emoji}}{{#mentions}}{{> UI.contentBlock }}{{/mentions}}{{/emoji}}{{/markdown}}</template>

View file

@ -4,11 +4,7 @@ template(name="sidebar")
class="{{#if isTongueHidden}}is-hidden{{/if}}")
i.fa.fa-chevron-left
.sidebar-content.js-board-sidebar-content.js-perfect-scrollbar
//- XXX https://github.com/peerlibrary/meteor-blaze-components/issues/30
if Filter.isActive
+filterSidebar
else
+homeSidebar
+Template.dynamic(template=getViewTemplate)
template(name='homeSidebar')
+membersWidget

View file

@ -1,3 +1,7 @@
var defaultView = 'home';
Sidebar = null;
BlazeComponent.extendComponent({
template: function() {
return 'sidebar';
@ -9,9 +13,14 @@ BlazeComponent.extendComponent({
onCreated: function() {
this._isOpen = new ReactiveVar(! Session.get('currentCard'));
this._view = new ReactiveVar(defaultView);
Sidebar = this;
},
onDestroyed: function() {
Sidebar = null;
},
isOpen: function() {
return this._isOpen.get();
},
@ -43,7 +52,20 @@ BlazeComponent.extendComponent({
},
isTongueHidden: function() {
return this.isOpen() && Filter.isActive();
return this.isOpen() && this.getView() !== defaultView;
},
getView: function() {
return this._view.get();
},
setView: function(view) {
view = view || defaultView;
this._view.set(view);
},
getViewTemplate: function() {
return this.getView() + 'Sidebar';
},
onRendered: function() {
@ -74,3 +96,8 @@ BlazeComponent.extendComponent({
}]);
}
}).register('sidebar');
EscapeActions.register(40,
function() { return Sidebar && Sidebar.getView() !== defaultView; },
function() { Sidebar.setView(defaultView); }
);