UI improvements

* Implement visibility choice on board creation;

* Rework the board header bar. Remove links to un-implemented
features;

* Implement a board star counter (visible if the board have >2 stars);

* Define a new icon (a thin cross) to close elements;

* Remove $(document).on('mouseover') event handlers that were
basically fired hundreds of times for nothing, we now define a proper
Tracker dependency to execute jquery-ui plugin initialization only
when something has changed;

* Bug fixes related to list scrolling.
This commit is contained in:
Maxime Quandalle 2015-05-27 17:17:00 +02:00
parent 42f6dc686f
commit dcc64f44f9
51 changed files with 644 additions and 990 deletions

View file

@ -27,7 +27,7 @@ template(name="addListForm")
autocomplete="off" autofocus value=getCache)
div.edit-controls.clearfix
button.primary.confirm.js-save-edit(type="submit") {{_ 'save'}}
a.fa.fa-times.dark-hover.cancel.js-close-inlined-form
a.fa.fa-times-thin.js-close-inlined-form
else
.js-open-inlined-form
i.fa.fa-plus

View file

@ -0,0 +1,94 @@
template(name="headerBoard")
h1.header-board-menu(
class="{{#if currentUser.isBoardMember}}is-clickable js-edit-board-title{{/if}}")
= title
.board-header-btns.left
unless isSandstorm
if currentUser
a.board-header-btn.js-star-board(class="{{#if isStarred}}is-hovered{{/if}}"
title="{{#if isStarred}}{{_ 'click-to-unstar'}}{{else}}{{_ 'click-to-star'}}{{/if}} {{_ 'starred-boards-description'}}")
i.fa(class="fa-star{{#unless isStarred}}-o{{/unless}}")
if showStarCounter
span {{_ 'board-nb-stars' stars}}
a.board-header-btn.js-change-visibility(class="{{#unless currentUser.isBoardAdmin}}no-edit{{/unless}}")
i.fa(class="{{#if isPublic}}fa-globe{{else}}fa-lock{{/if}}")
span {{_ permission}}
.board-header-btns.right
a.board-header-btn.js-open-filter-view(
title="{{#if Filter.isActive}}{{_ 'filter-on-desc'}}{{/if}}"
class="{{#if Filter.isActive}}emphasis{{/if}}")
i.fa.fa-filter
if Filter.isActive
span {{_ 'filter-on'}}
a.board-header-btn-close.js-filter-reset(title="{{_ 'filter-clear'}}")
i.fa.fa-times-thin
else
span {{_ 'filter'}}
.separator
a.board-header-btn.js-open-board-menu
i.board-header-btn-icon.fa.fa-cog
template(name="boardMenuPopup")
ul.pop-over-list
li: a.js-change-board-color Change color
li: a Copy this board
li: a Permissions
template(name="boardVisibilityList")
ul.pop-over-list
li
with "private"
a.js-select-visibility
i.fa.fa-lock.colorful
| {{_ 'private'}}
if visibilityCheck
i.fa.fa-check
span.sub-name {{_ 'private-desc'}}
li
with "public"
a.js-select-visibility
i.fa.fa-globe.colorful
| {{_ 'public'}}
if visibilityCheck
i.fa.fa-check
span.sub-name {{_ 'public-desc'}}
template(name="boardChangeVisibilityPopup")
+boardVisibilityList
template(name="boardChangeColorPopup")
.board-backgrounds-list.clearfix
each backgroundColors
.board-background-select.js-select-background
span.background-box(class="board-color-{{this}}")
if isSelected
i.fa.fa-check
template(name="createBoardPopup")
form
label
| {{_ 'title'}}
input.js-new-board-title(type="text" placeholder="{{_ 'bucket-example'}}" autofocus required)
if visibilityMenuIsOpen.get
+boardVisibilityList
else
p.quiet
if $eq visibility.get 'public'
span.fa.fa-globe.colorful
| {{{_ 'board-public-info'}}}
else
span.fa.fa-lock.colorful
| {{{_ 'board-private-info'}}}
a.js-change-visibility Change.
input.primary.wide(type="submit" value="{{_ 'create'}}")
template(name="boardChangeTitlePopup")
form
label
| {{_ 'title'}}
input.js-board-name(type="text" value="{{title}}" autofocus)
input.primary.wide(type="submit" value="{{_ 'rename'}}")

View file

@ -0,0 +1,157 @@
Template.boardMenuPopup.events({
'click .js-rename-board': Popup.open('boardChangeTitle'),
'click .js-change-board-color': Popup.open('boardChangeColor')
});
Template.boardChangeTitlePopup.events({
submit: function(evt, t) {
var title = t.$('.js-board-name').val().trim();
if (title) {
Boards.update(this._id, {
$set: {
title: title
}
});
Popup.close();
}
evt.preventDefault();
}
});
BlazeComponent.extendComponent({
template: function() {
return 'headerBoard';
},
isStarred: function() {
var boardId = this.currentData()._id;
var user = Meteor.user();
return boardId && user && user.hasStarred(boardId);
},
// Only show the star counter if the number of star is greater than 2
showStarCounter: function() {
return this.currentData().stars > 2;
},
events: function() {
return [{
'click .js-edit-board-title': Popup.open('boardChangeTitle'),
'click .js-star-board': function() {
Meteor.user().toggleBoardStar(Session.get('currentBoard'));
},
'click .js-open-board-menu': Popup.open('boardMenu'),
'click .js-change-visibility': Popup.open('boardChangeVisibility'),
'click .js-open-filter-view': function() {
Sidebar.setView('filter');
},
'click .js-filter-reset': function(evt) {
evt.stopPropagation();
Sidebar.setView();
Filter.reset();
}
}];
}
}).register('headerBoard');
BlazeComponent.extendComponent({
template: function() {
return 'boardChangeColorPopup';
},
backgroundColors: function() {
return Boards.simpleSchema()._schema.color.allowedValues;
},
isSelected: function() {
var currentBoard = Boards.findOne(Session.get('currentBoard'));
return currentBoard.color === this.currentData().toString();
},
events: function() {
return [{
'click .js-select-background': function(evt) {
var currentBoardId = Session.get('currentBoard');
Boards.update(currentBoardId, {
$set: {
color: this.currentData().toString()
}
});
evt.preventDefault();
}
}];
}
}).register('boardChangeColorPopup');
BlazeComponent.extendComponent({
template: function() {
return 'createBoardPopup';
},
onCreated: function() {
this.visibilityMenuIsOpen = new ReactiveVar(false);
this.visibility = new ReactiveVar('private');
},
visibilityCheck: function() {
return this.currentData() === this.visibility.get();
},
setVisibility: function(visibility) {
this.visibility.set(visibility);
this.visibilityMenuIsOpen.set(false);
},
toogleVisibilityMenu: function() {
this.visibilityMenuIsOpen.set(! this.visibilityMenuIsOpen.get());
},
onSubmit: function(evt) {
evt.preventDefault();
var title = this.find('.js-new-board-title').value;
var visibility = this.visibility.get();
var boardId = Boards.insert({
title: title,
permission: visibility
});
Utils.goBoardId(boardId);
},
events: function() {
return [{
'click .js-select-visibility': function() {
this.setVisibility(this.currentData());
},
'click .js-change-visibility': this.toogleVisibilityMenu,
submit: this.onSubmit
}];
}
}).register('createBoardPopup');
BlazeComponent.extendComponent({
template: function() {
return 'boardChangeVisibilityPopup';
},
visibilityCheck: function() {
var currentBoard = Boards.findOne(Session.get('currentBoard'));
return this.currentData() === currentBoard.permission;
},
selectBoardVisibility: function() {
Boards.update(Session.get('currentBoard'), {
$set: {
permission: this.currentData()
}
});
Popup.close();
},
events: function() {
return [{
'click .js-select-visibility': this.selectBoardVisibility
}];
}
}).register('boardChangeVisibilityPopup');

View file

@ -1,4 +1,11 @@
//-
XXX This template can't be transformed into a component because it is
included by iron-router. That's a bug.
See https://github.com/peerlibrary/meteor-blaze-components/issues/44
template(name="boards")
+boardList
template(name="boardList")
if boards
ul.board-list.clearfix
each boards

View file

@ -0,0 +1,34 @@
BlazeComponent.extendComponent({
template: function() {
return 'boardList';
},
boards: function() {
return Boards.find({}, {
sort: ['title']
});
},
starredBoards: function() {
var cursor = Boards.find({
_id: { $in: Meteor.user().profile.starredBoards || [] }
}, {
sort: ['title']
});
return cursor.count() === 0 ? null : cursor;
},
isStarred: function() {
var user = Meteor.user();
return user && user.hasStarred(this._id);
},
events: function() {
return [{
'click .js-star-board': function(evt) {
Meteor.user().toggleBoardStar(this._id);
evt.preventDefault();
}
}];
}
}).register('boardList');

View file

@ -5,16 +5,29 @@ setBoardColor(color)
&#header,
&.sk-spinner div,
.board-backgrounds-list &.background-box,
&.pop-over .pop-over-list li a:hover,
.board-list & a
background-color: color
& .minicard.is-selected .minicard-details
border-left: 3px solid color
&.pop-over .pop-over-list li a:hover,
button[type=submit].primary, input[type=submit].primary
background-color: darken(color, 20%)
&#header #header-quick-access ul li.current
border-bottom: 2px solid lighten(color, 10%)
&#header #header-main-bar .board-header-btn.emphasis
background: complement(color)
&:hover,
.board-header-btn-close
background: darken(complement(color), 10%)
&:hover .board-header-btn-close
background: darken(complement(color), 20%)
.board-color-nephritis
setBoardColor(#27AE60)

View file

@ -1,96 +0,0 @@
var toggleBoardStar = function(boardId) {
var queryType = Meteor.user().hasStarred(boardId) ? '$pull' : '$addToSet';
var query = {};
query[queryType] = {
'profile.starredBoards': boardId
};
Meteor.users.update(Meteor.userId(), query);
};
Template.boards.events({
'click .js-star-board': function(evt) {
toggleBoardStar(this._id);
evt.preventDefault();
}
});
Template.headerBoard.events({
'click .js-star-board': function() {
toggleBoardStar(this._id);
},
'click .js-open-board-menu': Popup.open('boardMenu'),
'click #permission-level:not(.no-edit)': Popup.open('boardChangePermission'),
'click .js-filter-cards-indicator': function(evt) {
Session.set('currentWidget', 'filter');
evt.preventDefault();
},
'click .js-filter-card-clear': function(evt) {
Filter.reset();
evt.stopPropagation();
}
});
Template.boardMenuPopup.events({
'click .js-rename-board': Popup.open('boardChangeTitle'),
'click .js-change-board-color': Popup.open('boardChangeColor')
});
Template.createBoardPopup.events({
'submit #CreateBoardForm': function(evt, t) {
var title = t.$('#boardNewTitle');
// trim value title
if ($.trim(title.val())) {
// İnsert Board title
var boardId = Boards.insert({
title: title.val(),
permission: 'public'
});
// Go to Board _id
Utils.goBoardId(boardId);
}
evt.preventDefault();
}
});
Template.boardChangeTitlePopup.events({
'submit #ChangeBoardTitleForm': function(evt, t) {
var title = t.$('.js-board-name').val().trim();
if (title) {
Boards.update(this._id, {
$set: {
title: title
}
});
Popup.close();
}
evt.preventDefault();
}
});
Template.boardChangePermissionPopup.events({
'click .js-select': function(evt) {
var $this = $(evt.currentTarget);
var permission = $this.attr('name');
Boards.update(this._id, {
$set: {
permission: permission
}
});
Popup.close();
}
});
Template.boardChangeColorPopup.events({
'click .js-select-background': function(evt) {
var currentBoardId = Session.get('currentBoard');
Boards.update(currentBoardId, {
$set: {
color: this.toString()
}
});
evt.preventDefault();
}
});

View file

@ -1,87 +0,0 @@
template(name="headerBoard")
h1.header-board-menu.js-open-board-menu
= title
span.fa.fa-angle-down
.board-header-btns.left
unless isSandstorm
a.board-header-btn.js-star-board(class="{{#if isStarred}}board-header-starred{{/if}}"
title="{{# if isStarred }}{{_ 'click-to-unstar'}}{{ else }}{{_ 'click-to-star'}}{{/ if }} {{_ 'starred-boards-description'}}")
span.board-header-btn-icon.icon-sm.fa(class="fa-star{{#unless isStarred}}-o{{/unless}}")
//- XXX To implement
span.board-header-btn-text Starred
//-
XXX Normally we would disable this field for sandstorm, but we keep it
until sandstorm implements sharing capabilities
a.board-header-btn.perms-btn.js-change-vis(class="{{#unless currentUser.isBoardAdmin}}no-edit{{/ unless}}" id="permission-level")
span.board-header-btn-icon.icon-sm.fa(class="{{#if isPublic}}fa-globe{{else}}fa-lock{{/if}}")
span.board-header-btn-text {{_ permission}}
a.board-header-btn.js-search
span.board-header-btn-icon.icon-sm.fa.fa-tag
span.board-header-btn-text Labels
//- XXX Clicking here should open a search field
a.board-header-btn.js-search
span.board-header-btn-icon.icon-sm.fa.fa-search
span.board-header-btn-text {{_ 'search'}}
//- +boardMembersHeader
template(name="boardMembersHeader")
.board-header-members
each currentBoard.members
+userAvatar(userId=userId draggable=true showBadges=true)
unless isSandstorm
if currentUser.isBoardAdmin
a.member.add-board-member.js-open-manage-board-members
i.fa.fa-plus
template(name="boardMenuPopup")
ul.pop-over-list
li: a.js-rename-board {{_ 'rename-board'}}
li: a.js-change-board-color Change color
li: a Copy this board
li: a Rules
template(name="boardChangeTitlePopup")
form#ChangeBoardTitleForm
label {{_ 'name'}}
input.js-board-name(type="text" value="{{ title }}" autofocus)
input.primary.wide.js-rename-board(type="submit" value="{{_ 'rename'}}")
template(name="boardChangePermissionPopup")
ul.pop-over-list
li
a.js-select.light-hover(name="private")
span.icon-sm.fa.fa-lock.vis-icon
| {{_ 'private'}}
if check 'private'
span.icon-sm.fa.fa-check
span.sub-name {{_ 'private-desc'}}
li
a.js-select.light-hover(name="public")
span.icon-sm.fa.fa-globe.vis-icon
| {{_ 'public'}}
if check 'public'
span.icon-sm.fa.fa-check
span.sub-name {{_ 'public-desc'}}
template(name="boardChangeColorPopup")
.board-backgrounds-list.clearfix
each backgroundColors
.board-background-select.js-select-background
span.background-box(class="board-color-{{this}}")
if isSelected
i.fa.fa-check
template(name="createBoardPopup")
.content.clearfix
form#CreateBoardForm
label(for="boardNewTitle") {{_ 'title'}}
input#boardNewTitle.non-empty(type="text" name="name" placeholder="{{_ 'bucket-example'}}" autofocus)
p.quiet
span.icon-sm.fa.fa-globe
| {{{_ 'board-public-info'}}}
input.primary.wide(type="submit" value="{{_ 'create'}}")

View file

@ -1,7 +0,0 @@
Template.headerBoard.helpers({
isStarred: function() {
var boardId = Session.get('currentBoard');
var user = Meteor.user();
return boardId && user && user.hasStarred(boardId);
}
});

View file

@ -1,137 +0,0 @@
@import 'nib'
.board-header {
height: auto;
overflow: hidden;
padding: 10px 30px 10px 8px;
position: relative;
transition: padding .15s ease-in;
}
.board-header-btns {
position: relative;
display: block;
}
.board-header-btn {
border-radius: 3px;
color: #f6f6f6;
cursor: default;
float: left;
font-size: 12px;
height: 30px;
line-height: 32px;
margin: 2px 4px 0 0;
overflow: hidden;
padding-left: 30px;
position: relative;
text-decoration: none;
}
.board-header-btn:empty {
display: none;
}
.board-header-btn-without-icon {
padding-left: 8px;
}
.board-header-btn-icon {
background-clip: content-box;
background-origin: content-box;
color: #f6f6f6 !important;
padding: 6px;
position: absolute;
top: 0;
left: 0;
}
.board-header-btn-text {
padding-right: 8px;
}
.board-header-btn:not(.no-edit) .text {
text-decoration: underline;
}
.board-header-btn:not(.no-edit):hover {
background: rgba(0, 0, 0, .12);
cursor: pointer;
}
.board-header-btn:hover {
color: #f6f6f6;
}
.board-header-btn.board-header-btn-enabled {
background-color: rgba(0, 0, 0, .1);
&:hover {
background-color: rgba(0, 0, 0, .3);
}
.board-header-btn-icon.icon-star {
color: #e6bf00 !important;
}
}
.board-header-btn-name {
cursor: default;
font-size: 18px;
font-weight: 700;
line-height: 30px;
padding-left: 4px;
text-decoration: none;
.board-header-btn-text {
padding-left: 6px;
}
}
.board-header-btn-name-org-logo {
border-radius: 3px;
height: 30px;
left: 0;
position: absolute;
top: 0;
width: 30px;
.board-header-btn-text {
padding-left: 32px;
}
}
.board-header-btn-org-name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 400px;
}
.board-header-btn-filter-indicator {
background: #3d990f;
padding-right: 30px;
color: #fff;
text-shadow: 0;
&:hover {
background: #43a711 !important;
}
.board-header-btn-icon-close {
background: #43a711;
border-top-left-radius: 0;
border-top-right-radius: 3px;
border-bottom-right-radius: 3px;
border-bottom-left-radius: 0;
color: #fff;
padding: 6px;
position: absolute;
right: 0;
top: 0;
&:hover {
background: #48b512;
}
}
}

View file

@ -1,42 +1,3 @@
Template.boards.helpers({
boards: function() {
return Boards.find({}, {
sort: ['title']
});
},
starredBoards: function() {
var cursor = Boards.find({
_id: { $in: Meteor.user().profile.starredBoards || [] }
}, {
sort: ['title']
});
return cursor.count() === 0 ? null : cursor;
},
isStarred: function() {
var user = Meteor.user();
return user && user.hasStarred(this._id);
}
});
Template.boardChangePermissionPopup.helpers({
check: function(perm) {
return this.permission === perm;
}
});
Template.boardChangeColorPopup.helpers({
backgroundColors: function() {
return Boards.simpleSchema()._schema.color.allowedValues;
},
isSelected: function() {
var currentBoard = Boards.findOne(Session.get('currentBoard'));
return currentBoard.color === this.toString();
}
});
Blaze.registerHelper('currentBoard', function() {
var boardId = Session.get('currentBoard');
if (boardId) {