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) autocomplete="off" autofocus value=getCache)
div.edit-controls.clearfix div.edit-controls.clearfix
button.primary.confirm.js-save-edit(type="submit") {{_ 'save'}} 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 else
.js-open-inlined-form .js-open-inlined-form
i.fa.fa-plus 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") template(name="boards")
+boardList
template(name="boardList")
if boards if boards
ul.board-list.clearfix ul.board-list.clearfix
each boards 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, &#header,
&.sk-spinner div, &.sk-spinner div,
.board-backgrounds-list &.background-box, .board-backgrounds-list &.background-box,
&.pop-over .pop-over-list li a:hover,
.board-list & a .board-list & a
background-color: color background-color: color
& .minicard.is-selected .minicard-details & .minicard.is-selected .minicard-details
border-left: 3px solid color border-left: 3px solid color
&.pop-over .pop-over-list li a:hover,
button[type=submit].primary, input[type=submit].primary button[type=submit].primary, input[type=submit].primary
background-color: darken(color, 20%) 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 .board-color-nephritis
setBoardColor(#27AE60) 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() { Blaze.registerHelper('currentBoard', function() {
var boardId = Session.get('currentBoard'); var boardId = Session.get('currentBoard');
if (boardId) { if (boardId) {

View file

@ -4,7 +4,7 @@ template(name="cardDetails")
.card-detail-cover(style="background-image: url({{ card.cover.url }})") .card-detail-cover(style="background-image: url({{ card.cover.url }})")
.card-detail-header(class="{{#if currentUser.isBoardMember}}editable{{/if}}") .card-detail-header(class="{{#if currentUser.isBoardMember}}editable{{/if}}")
a.js-close-card-detail a.js-close-card-detail
i.fa.fa-times i.fa.fa-times-thin
h2.card-detail-title.js-card-title= title h2.card-detail-title.js-card-title= title
p.card-detail-list.js-move-card p.card-detail-list.js-move-card
| {{_ 'in-list'}} | {{_ 'in-list'}}
@ -25,7 +25,7 @@ template(name="cardDetails")
if currentUser.isBoardMember if currentUser.isBoardMember
h3 Description h3 Description
+inlinedForm(classNames="js-card-description") +inlinedForm(classNames="js-card-description")
i.fa.fa-times.js-close-inlined-form a.fa.fa-times-thin.js-close-inlined-form
+editor(autofocus=true) +editor(autofocus=true)
= description = description
button(type="submit") {{_ 'edit'}} button(type="submit") {{_ 'edit'}}

View file

@ -1,64 +1,3 @@
// Template.cards.events({
// // 'click .js-cancel': function(event, t) {
// // var composer = t.$('.card-composer');
// // // Keep the old value in memory to display it again next time
// // var inputCacheKey = "addCard-" + this.listId;
// // var oldValue = composer.find('.js-card-title').val();
// // InputsCache.set(inputCacheKey, oldValue);
// // // add composer hide class
// // composer.addClass('hide');
// // composer.find('.js-card-title').val('');
// // // remove hide open link class
// // $('.js-open-card-composer').removeClass('hide');
// // },
// 'submit': function(evt, tpl) {
// evt.preventDefault();
// var textarea = $(evt.currentTarget).find('textarea');
// var title = textarea.val();
// var lastCard = tpl.find('.js-minicard:last-child');
// var sort;
// if (lastCard === null) {
// sort = 0;
// } else {
// sort = Blaze.getData(lastCard).sort + 1;
// }
// // debugger
// // Clear the form in-memory cache
// // var inputCacheKey = "addCard-" + this.listId;
// // InputsCache.set(inputCacheKey, '');
// // title trim if not empty then
// if ($.trim(title)) {
// Cards.insert({
// title: title,
// listId: Template.currentData().listId,
// boardId: Template.currentData().board._id,
// sort: sort
// }, function(err, _id) {
// // In case the filter is active we need to add the newly
// // inserted card in the list of exceptions -- cards that are
// // not filtered. Otherwise the card will disappear instantly.
// // See https://github.com/libreboard/libreboard/issues/80
// Filter.addException(_id);
// });
// // empty and focus.
// textarea.val('').focus();
// // focus complete then scroll top
// Utils.Scroll(tpl.find('.js-minicards')).top(1000, true);
// }
// }
// });
// Template.cards.events({
// 'click .member': Popup.open('cardMember')
// });
Template.cardMemberPopup.events({ Template.cardMemberPopup.events({
'click .js-remove-member': function() { 'click .js-remove-member': function() {
Cards.update(this.cardId, {$pull: {members: this.userId}}); Cards.update(this.cardId, {$pull: {members: this.userId}});

View file

@ -0,0 +1,27 @@
template(name="minicard")
.minicard.card.js-minicard(
class="{{#if isSelected}}is-selected{{/if}}")
a.minicard-details.clearfix.show(href=absoluteUrl)
if cover
.minicard-cover.js-card-cover(style="background-image: url({{cover.url}});")
if labels
.minicard-labels
each labels
.minicard-label(class="card-label-{{color}}" title="{{name}}")
.minicard-title= title
if members
.minicard-members.js-minicard-members
each members
+userAvatar(userId=this size="small" cardId="{{../_id}}")
.badges
if comments.count
.badge(title="{{_ 'card-comments-title' comments.count }}")
span.badge-icon.icon-sm.fa.fa-comment-o
.badge-text= comments.count
if description
.badge.badge-state-image-only(title=description)
span.badge-icon.icon-sm.fa.fa-align-left
if attachments.count
.badge
span.badge-icon.icon-sm.fa.fa-paperclip
span.badge-text= attachments.count

View file

@ -0,0 +1,14 @@
// Template.cards.events({
// 'click .member': Popup.open('cardMember')
// });
BlazeComponent.extendComponent({
template: function() {
return 'minicard';
},
isSelected: function() {
return Session.equals('currentCard', this.currentData()._id);
}
}).register('minicard');

View file

@ -229,26 +229,9 @@ textarea
padding-top: 5px padding-top: 5px
padding-bottom: 5px padding-bottom: 5px
i.fa.fa-times .fa-times-thin
font-size: 20px font-size: 26px
margin: 3px 4px
.option
border-color: transparent
border-radius: 3px
color: #8c8c8c
display: block
float: right
height: 30px
line-height: 30px
padding: 0 8px
margin: 0 2px
&:hover
background-color: #dbdbdb
color: #4d4d4d
&:active
background-color: #ccc
.button-link .button-link
background: #fff background: #fff

View file

@ -30,11 +30,12 @@ BlazeComponent.extendComponent({
this.isOpen = new ReactiveVar(false); this.isOpen = new ReactiveVar(false);
}, },
onDestroyed: function() {
currentlyOpenedForm.set(null);
},
open: function() { open: function() {
// Close currently opened form, if any // Close currently opened form, if any
// if (currentlyOpenedForm.get() !== null) {
// currentlyOpenedForm.get().close();
// }
EscapeActions.executeLowerThan('inlinedForm'); EscapeActions.executeLowerThan('inlinedForm');
this.isOpen.set(true); this.isOpen.set(true);
currentlyOpenedForm.set(this); currentlyOpenedForm.set(this);

View file

@ -5,32 +5,7 @@ template(name="listBody")
+inlinedForm(autoclose=false position="top") +inlinedForm(autoclose=false position="top")
+addCardForm(listId=_id position="top") +addCardForm(listId=_id position="top")
each cards each cards
.minicard.card.js-minicard( +minicard(this)
class="{{#if isSelected}}is-selected{{/if}}")
a.minicard-details.clearfix.show(href=absoluteUrl)
if cover
.minicard-cover.js-card-cover(style="background-image: url({{cover.url}});")
if labels
.minicard-labels
each labels
.minicard-label(class="card-label-{{color}}" title="{{name}}")
.minicard-title= title
if members
.minicard-members.js-minicard-members
each members
+userAvatar(userId=this size="small" cardId="{{../_id}}")
.badges
if comments.count
.badge(title="{{_ 'card-comments-title' comments.count }}")
span.badge-icon.icon-sm.fa.fa-comment-o
.badge-text= comments.count
if description
.badge.badge-state-image-only(title=description)
span.badge-icon.icon-sm.fa.fa-align-left
if attachments.count
.badge
span.badge-icon.icon-sm.fa.fa-paperclip
span.badge-text= attachments.count
if currentUser.isBoardMember if currentUser.isBoardMember
+inlinedForm(autoclose=false position="bottom") +inlinedForm(autoclose=false position="bottom")
+addCardForm(listId=_id position="bottom") +addCardForm(listId=_id position="bottom")
@ -49,4 +24,4 @@ template(name="addCardForm")
.minicard-members.js-minicard-composer-members .minicard-members.js-minicard-composer-members
.add-controls.clearfix .add-controls.clearfix
button.primary.confirm(type="submit") {{_ 'add'}} button.primary.confirm(type="submit") {{_ 'add'}}
a.fa.fa-times.dark-hover.cancel.js-close-inlined-form a.fa.fa-times-thin.js-close-inlined-form

View file

@ -7,10 +7,6 @@ BlazeComponent.extendComponent({
return [Mixins.PerfectScrollbar]; return [Mixins.PerfectScrollbar];
}, },
isSelected: function() {
return Session.equals('currentCard', this.currentData()._id);
},
openForm: function(options) { openForm: function(options) {
options = options || {}; options = options || {};
options.position = options.position || 'top'; options.position = options.position || 'top';
@ -37,11 +33,6 @@ BlazeComponent.extendComponent({
sortIndex = Utils.getSortIndex(this.find('.js-minicard:last'), null); sortIndex = Utils.getSortIndex(this.find('.js-minicard:last'), null);
} }
// Clear the form in-memory cache
// var inputCacheKey = "addCard-" + this.listId;
// InputsCache.set(inputCacheKey, '');
// title trim if not empty then
if ($.trim(title)) { if ($.trim(title)) {
Cards.insert({ Cards.insert({
title: title, title: title,
@ -49,16 +40,18 @@ BlazeComponent.extendComponent({
boardId: this.data().board()._id, boardId: this.data().board()._id,
sort: sortIndex sort: sortIndex
}, function(err, _id) { }, function(err, _id) {
// In case the filter is active we need to add the newly // In case the filter is active we need to add the newly inserted card
// inserted card in the list of exceptions -- cards that are // in the list of exceptions -- cards that are not filtered. Otherwise
// not filtered. Otherwise the card will disappear instantly. // the card will disappear instantly.
// See https://github.com/libreboard/libreboard/issues/80 // See https://github.com/libreboard/libreboard/issues/80
Filter.addException(_id); Filter.addException(_id);
}); });
// We keep the form opened, empty it, and scroll to it. // We keep the form opened, empty it, and scroll to it.
textarea.val('').focus(); textarea.val('').focus();
Utils.Scroll(this.find('.js-minicards')).top(1000, true); if (position === 'bottom') {
this.scrollToBottom();
}
} }
}, },
@ -67,9 +60,9 @@ BlazeComponent.extendComponent({
}, },
scrollToBottom: function() { scrollToBottom: function() {
var $container = $(this.firstNode()); var container = this.firstNode();
$container.animate({ $(container).animate({
scrollTop: $container.height() scrollTop: container.scrollHeight
}); });
}, },
@ -94,7 +87,12 @@ BlazeComponent.extendComponent({
// Pressing Enter should submit the card // Pressing Enter should submit the card
if (evt.keyCode === 13) { if (evt.keyCode === 13) {
evt.preventDefault(); evt.preventDefault();
$(evt.currentTarget).parents('form:first').submit(); var $form = $(evt.currentTarget).parents('form:first');
// XXX For some reason $form.submit() does not work (it's probably a bug
// of blaze-component related to the fact that the submit event is non-
// bubbling). This is why we click on the submit button instead -- which
// work.
$form.find('button[type=submit]').click();
// Pressing Tab should open the form of the next column, and Maj+Tab go // Pressing Tab should open the form of the next column, and Maj+Tab go
// in the reverse order // in the reverse order
@ -102,7 +100,7 @@ BlazeComponent.extendComponent({
evt.preventDefault(); evt.preventDefault();
var isReverse = evt.shiftKey; var isReverse = evt.shiftKey;
var list = $('#js-list-' + this.data().listId); var list = $('#js-list-' + this.data().listId);
var listSelector = '.js-list:not(.js-add-list)'; var listSelector = '.js-list:not(.js-list-composer)';
var nextList = list[isReverse ? 'prev' : 'next'](listSelector).get(0); var nextList = list[isReverse ? 'prev' : 'next'](listSelector).get(0);
// If there isn't no next list, loop back to the beginning. // If there isn't no next list, loop back to the beginning.
if (! nextList) { if (! nextList) {

View file

@ -24,58 +24,69 @@ BlazeComponent.extendComponent({
// powerful enough for our use casesShould we “simply” write the drag&drop // powerful enough for our use casesShould we “simply” write the drag&drop
// code ourselves? // code ourselves?
onRendered: function() { onRendered: function() {
if (Meteor.user().isBoardMember()) { var self = this;
var boardComponent = this.componentParent(); if (! Meteor.userId() || ! Meteor.user().isBoardMember())
var itemsSelector = '.js-minicard:not(.placeholder, .hide, .js-composer)'; return;
var $cards = this.$('.js-minicards');
$cards.sortable({
connectWith: '.js-minicards',
tolerance: 'pointer',
appendTo: '.js-lists',
helper: 'clone',
items: itemsSelector,
placeholder: 'minicard placeholder',
start: function(event, ui) {
$('.minicard.placeholder').height(ui.item.height());
Popup.close();
boardComponent.showNewCardForms(false);
},
stop: function(event, ui) {
// To attribute the new index number, we need to get the dom element
// of the previous and the following card -- if any.
var cardDomElement = ui.item.get(0);
var prevCardDomElement = ui.item.prev('.js-minicard').get(0);
var nextCardDomElement = ui.item.next('.js-minicard').get(0);
var sort = Utils.getSortIndex(prevCardDomElement, nextCardDomElement);
var cardId = Blaze.getData(cardDomElement)._id;
var listId = Blaze.getData(ui.item.parents('.list').get(0))._id;
Cards.update(cardId, {
$set: {
listId: listId,
sort: sort
}
});
boardComponent.showNewCardForms(true);
}
}).disableSelection();
$(document).on('mouseover', function() { var boardComponent = self.componentParent();
var itemsSelector = '.js-minicard:not(.placeholder, .hide, .js-composer)';
var $cards = self.$('.js-minicards');
$cards.sortable({
connectWith: '.js-minicards',
tolerance: 'pointer',
appendTo: '.js-lists',
helper: 'clone',
items: itemsSelector,
placeholder: 'minicard placeholder',
start: function(event, ui) {
$('.minicard.placeholder').height(ui.item.height());
Popup.close();
boardComponent.showNewCardForms(false);
},
stop: function(event, ui) {
// To attribute the new index number, we need to get the dom element
// of the previous and the following card -- if any.
var cardDomElement = ui.item.get(0);
var prevCardDomElement = ui.item.prev('.js-minicard').get(0);
var nextCardDomElement = ui.item.next('.js-minicard').get(0);
var sort = Utils.getSortIndex(prevCardDomElement, nextCardDomElement);
var cardId = Blaze.getData(cardDomElement)._id;
var listId = Blaze.getData(ui.item.parents('.list').get(0))._id;
Cards.update(cardId, {
$set: {
listId: listId,
sort: sort
}
});
boardComponent.showNewCardForms(true);
}
});
// We want to re-run this function any time a card is added.
self.autorun(function() {
var currentBoardId = Tracker.nonreactive(function() {
return Session.get('currentBoard');
});
Cards.find({ boardId: currentBoardId }).fetch();
Tracker.afterFlush(function() {
$cards.find(itemsSelector).droppable({ $cards.find(itemsSelector).droppable({
hoverClass: 'draggable-hover-card', hoverClass: 'draggable-hover-card',
accept: '.js-member,.js-label', accept: '.js-member,.js-label',
drop: function(event, ui) { drop: function(event, ui) {
var cardId = Blaze.getData(this)._id; var cardId = Blaze.getData(this)._id;
var addToSet;
if (ui.draggable.hasClass('js-member')) { if (ui.draggable.hasClass('js-member')) {
var memberId = Blaze.getData(ui.draggable.get(0)).userId; var memberId = Blaze.getData(ui.draggable.get(0)).userId;
Cards.update(cardId, {$addToSet: {members: memberId}}); addToSet = { members: memberId };
} else { } else {
var labelId = Blaze.getData(ui.draggable.get(0))._id; var labelId = Blaze.getData(ui.draggable.get(0))._id;
Cards.update(cardId, {$addToSet: {labelIds: labelId}}); addToSet = { labelIds: labelId };
} }
Cards.update(cardId, { $addToSet: addToSet });
} }
}); });
}); });
} });
} }
}).register('list'); }).register('list');

View file

@ -1,7 +1,5 @@
@import 'nib' @import 'nib'
global-reset()
#header #header
color: white color: white
transition: background-color 0.4s transition: background-color 0.4s
@ -9,15 +7,16 @@ global-reset()
#header-quick-access #header-quick-access
background-color: rgba(0, 0, 0, 0.2) background-color: rgba(0, 0, 0, 0.2)
padding: 4px 10px padding: 0px 10px
height: 16px height: 28px
font-size: 12px font-size: 12px
display: flex display: flex
ul li, #header-user-bar #header-user-bar
ul li
color: darken(white, 17%) color: darken(white, 17%)
a a, .fa
color: inherit color: inherit
text-decoration: none text-decoration: none
@ -27,240 +26,101 @@ global-reset()
ul ul
flex: 1 flex: 1
transition: opacity 0.2s transition: opacity 0.2s
margin-left: 5px margin: 4px 0 0 5px
li li
display: block display: block
float: left float: left
width: auto width: auto
color: darken(white, 15%) color: darken(white, 15%)
padding: 0 4px 1px 4px padding: 2px 5px 0
&.separator
padding: 0 2px 1px 2px
&.current &.current
font-style: italic color: darken(white, 5%)
&:first-child .fa-home &:first-child .fa-home
margin-right: 5px margin-right: 5px
#header-main-bar a.js-create-board
height: 30px
padding: 8px
h1
font-size: 19px
line-height: 1.7em
margin: 0 20px 0 10px
float: left
&.header-board-menu
cursor: pointer
.fa-angle-down
font-size: 0.8em
// line-height: 1.1em
margin-left: 5px margin-left: 5px
.board-header-starred .fa #header-user-bar
color: yellow margin: 2px 0
.board-header-members
float: right
.member .member
display: block width: 24px
width: 32px
height: @width height: @width
float: left
margin: 0
margin-top: 1px
.add-board-member .header-user-bar-name
color: white margin: 4px 8px 0 0
display: flex float: left
align-items: center
justify-content: center
border: 1px solid white
height: 32px - 2px
width: @height
i.fa-plus #header-main-bar
margin-top: 2px height: 28px * 1.618034 - 6px
padding: 7px 10px 0
.header-btn:last-child h1
margin-right: 0 font-size: 20px
line-height: 1.7em
padding: 0 10px
margin: 0
margin-right: 10px
float: left
border-radius: 3px
&.is-clickable
cursor: pointer
.board-header-btns
display: block
margin-top: 3px
width: auto
// #header { // XXX Use a flexbox instead of floats?
// background: #138871; &.left
// height: 30px; float: left
// overflow: hidden;
// padding: 5px;
// position: relative;
// z-index: 10;
// }
// .header-logo { &.right
// bottom: 0; float: right
// display: block;
// height: 25px;
// left: 50%;
// position: absolute;
// top: 8px;
// width: 80px;
// margin-left: - @width/2;
// text-align: center;
// z-index: 2;
// opacity: .5;
// transition: opacity ease-in 85ms;
// color: white;
// font-size: 22px;
// text-decoration: none;
// background-image: url('/logos/white_logo.png');
// &:hover { .board-header-btn
// opacity: .8; border-radius: 3px
// color: white; color: darken(white, 5%)
// } padding: 0
// } height: 28px
font-size: 13px
float: left
overflow: hidden
line-height: @height
margin: 0 2px
// .header-btn.header-btn-feedback { i.fa
// background: rgba(255, 255, 255, .1); float: left
// background: linear-gradient(to bottom, rgba(255, 255, 255, .1) 0, rgba(255, 255, 255, .05) 100%); display: block
// padding-left: 22px; line-height: 28px
// margin-right: 16px; color: darken(white, 5%)
margin: 0 10px
// .header-btn-icon { + span
// top: 1px; margin-right: 10px
// }
// }
.header-btn { .board-header-btn-close
border-radius: 3px; float: right
user-select: none;
background: rgba(255, 255, 255, .3);
background: linear-gradient(to bottom, rgba(255, 255, 255, .3) 0, rgba(255, 255, 255, .2) 100%);
color: #f3f3f3;
display: block;
float: left;
font-weight: 700;
height: 30px;
line-height: 30px;
padding: 0 10px;
position: relative;
margin-right: 8px;
min-width: 30px;
text-decoration: none;
cursor: pointer;
.header-btn-icon { i.fa
font-size: 16px; margin: 0 6px
line-height: 28px;
position: absolute;
top: 0;
left: 0;
}
&.new-notifications { .board-header-btn,
background: #ba1212; h1.is-clickable
&.is-hovered,
&:hover
background: rgba(0, 0, 0, .15)
&:hover { .separator
background: #d11515; margin: 2px 4px
} border-left: 1px solid rgba(255, 255, 255, .3)
} height: 24px
float: left
&.header-member .member {
margin: 0;
border-top-left-radius: 3px;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border-bottom-left-radius: 3px;
&:hover .member-avatar {
opacity: 1;
}
}
&:hover {
background: rgba(255, 255, 255, .4);
background: linear-gradient(to bottom, rgba(255, 255, 255, .4) 0, rgba(255, 255, 255, .3) 100%);
color: #fff;
.header-btn-count {
background: #d11515;
}
}
&:active {
background: rgba(255, 255, 255, .4);
background: linear-gradient(to bottom, rgba(255, 255, 255, .4) 0, rgba(255, 255, 255, .3) 100%);
}
&.upgrade {
margin-right: 16px;
.icon-sm {
padding: 6px 2px 6px 4px;
}
}
&.upgrade,
&.header-boards {
padding-left: 4px;
}
&.header-boards {
padding-right: 4px;
}
&.header-login,
&.header-signup {
padding: 0 12px;
}
&.header-signup {
background: #48b512;
background: linear-gradient(to bottom, #48b512 0, #3d990f 100%);
&:hover {
background: #3d990f;
background: linear-gradient(to bottom, #3d990f 0, #327d0c 100%);
}
&:active {
background: #327d0c;
}
}
&.header-go-to-boards {
padding: 0 8px 0 38px;
}
&.header-go-to-boards .member {
border-top-left-radius: 3px;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border-bottom-left-radius: 3px;
position: absolute;
left: 0;
}
}
// .header-btn-text {
// padding: 0 8px;
// }
// .header-notification-list ul {
// margin-top: 8px;
// }
// .header-notification-list .action-comment {
// max-height: 250px;
// overflow-y: auto;
// }
// .header-user {
// position: absolute;
// top: 5px;
// right: 0;
// }

View file

@ -1,11 +1,21 @@
// XXX This event list must be abstracted somewhere else. // XXX This event list must be abstracted somewhere else.
var endTransitionEvents = [ function whichTransitionEvent() {
'webkitTransitionEnd', var t;
'otransitionend', var el = document.createElement('fakeelement');
'oTransitionEnd', var transitions = {
'msTransitionEnd', transition:'transitionend',
'transitionend' OTransition:'oTransitionEnd',
].join(' '); MozTransition:'transitionend',
WebkitTransition:'webkitTransitionEnd'
};
for (t in transitions) {
if (el.style[t] !== undefined) {
return transitions[t];
}
}
}
var transitionEvent = whichTransitionEvent();
Popup.template.events({ Popup.template.events({
click: function(evt) { click: function(evt) {
@ -32,7 +42,7 @@ Popup.template.onRendered(function() {
container._uihooks = { container._uihooks = {
removeElement: function(node) { removeElement: function(node) {
$(node).addClass('no-height'); $(node).addClass('no-height');
$(container).one(endTransitionEvents, function() { $(container).one(transitionEvent, function() {
node.parentNode.removeChild(node); node.parentNode.removeChild(node);
}); });
} }

View file

@ -91,6 +91,10 @@
top: 0 top: 0
right: 0 right: 0
&.no-title .header
background: none
.content-wrapper .content-wrapper
width: 100% width: 100%
@ -527,78 +531,3 @@
&.limited li.exceeds-limit &.limited li.exceeds-limit
display: none display: none
.pop-over-emoji-list li > a
padding: 2px 4px
.emoji
margin: 0 6px
.pop-over-card-list li > a
padding: 2px 4px
.login-signup-popover
padding: 15px
.form-tabs
display: none
h1
margin-bottom: 15px
p
margin: 8px 0
.form-parts-container
position: relative
.active-box
position: absolute
top: 0
background: #e2e2e2
border: 1px solid #c9c9c9
border-radius: 3px
z-index: 1
height: 100%
width: 49%
transition-property: all
transition-duration: .4s
opacity: 1
&.start
opacity: 0
left: 25%
.signup-form,
.login-form
position: relative
box-sizing: border-box
padding: 20px
width: 50%
z-index: 2
opacity: .3
transition-property: opacity
transition-duration: .2s
.active
opacity: 1
.js-signup-form-pos
left: 0
.login-form
position: absolute
top: 0
.login-form .icon-google
position: absolute
left: 5px
top: 3px
.login-form .button.google
padding-left: 40px
margin: 0 0 15px 0
.js-login-form-pos
left: 50%

View file

@ -1,13 +1,14 @@
.pop-over.clearfix( .pop-over.clearfix(
class="{{#unless title}}miniprofile{{/unless}}" class="{{#unless title}}miniprofile{{/unless}}"
class=currentBoard.colorClass class=currentBoard.colorClass
class="{{#unless title}}no-title{{/unless}}"
style="display:block; left:{{offset.left}}px; top:{{offset.top}}px;") style="display:block; left:{{offset.left}}px; top:{{offset.top}}px;")
.header.clearfix .header
a.back-btn.js-back-view(class="{{#unless hasPopupParent}}is-hidden{{/unless}}") a.back-btn.js-back-view(class="{{#unless hasPopupParent}}is-hidden{{/unless}}")
i.fa.fa-chevron-left i.fa.fa-chevron-left
span.header-title= title span.header-title= title
a.close-btn.js-close-pop-over a.close-btn.js-close-pop-over
i.fa.fa-times i.fa.fa-times-thin
.content-wrapper .content-wrapper
//- //-
We display the all stack of popup content next to each other and move We display the all stack of popup content next to each other and move

View file

@ -68,23 +68,39 @@ BlazeComponent.extendComponent({
return this.getView() + 'Sidebar'; return this.getView() + 'Sidebar';
}, },
// Board members can assign people or labels by drag-dropping elements from
// the sidebar to the cards on the board. In order to re-initialize the
// jquery-ui plugin any time a draggable member or label is modified or
// removed we use a autorun function and register a dependency on the both
// members and labels fields of the current board document.
onRendered: function() { onRendered: function() {
var self = this; var self = this;
if (! Meteor.userId() || ! Meteor.user().isBoardMember()) if (! Meteor.userId() || ! Meteor.user().isBoardMember())
return; return;
$(document).on('mouseover', function() { self.autorun(function() {
self.$('.js-member,.js-label').draggable({ var currentBoardId = Tracker.nonreactive(function() {
appendTo: 'body', return Session.get('currentBoard');
helper: 'clone', });
revert: 'invalid', Boards.findOne(currentBoardId, {
revertDuration: 150, fields: {
snap: false, members: 1,
snapMode: 'both', labels: 1
start: function() {
Popup.close();
} }
}); });
Tracker.afterFlush(function() {
self.$('.js-member,.js-label').draggable({
appendTo: 'body',
helper: 'clone',
revert: 'invalid',
revertDuration: 150,
snap: false,
snapMode: 'both',
start: function() {
EscapeActions.executeLowerThan('popup');
}
});
});
}); });
}, },

View file

@ -1,16 +1,12 @@
template(name="headerUserBar") template(name="headerUserBar")
#header-user-bar a#header-user-bar.js-open-header-member-menu
if currentUser .header-user-bar-name
a.js-open-header-member-menu i.fa.fa-chevron-down
if currentUser.profile.name if currentUser.profile.name
= currentUser.profile.name = currentUser.profile.name
else else
= currentUser.username = currentUser.username
i.fa.fa-chevron-down +userAvatar(user=currentUser)
else
a(href="{{pathFor route='signUp'}}") Sign in
span.separator -
a(href="{{pathFor route='signIn'}}") Log in
template(name="memberHeader") template(name="memberHeader")
a.header-member.js-open-header-member-menu a.header-member.js-open-header-member-menu

View file

@ -1,23 +1,4 @@
Utils = { Utils = {
error: function(err) {
Session.set('error', (err && err.message || false));
},
// scroll
Scroll: function(selector) {
var $el = $(selector);
return {
top: function(px, add) {
var t = $el.scrollTop();
$el.animate({ scrollTop: (add ? (t + px) : px) });
},
left: function(px, add) {
var l = $el.scrollLeft();
$el.animate({ scrollLeft: (add ? (l + px) : px) });
}
};
},
// XXX We should remove these two methods // XXX We should remove these two methods
goBoardId: function(_id) { goBoardId: function(_id) {
var board = Boards.findOne(_id); var board = Boards.findOne(_id);

28
client/styles/icons.styl Normal file
View file

@ -0,0 +1,28 @@
.emoji
height: 18px
width: 18px
vertical-align: text-bottom
// Implement a thiner close icon as suggested in
// https://github.com/FortAwesome/Font-Awesome/issues/1540#issuecomment-68689950
.fa.fa-times-thin:before
content: '\00d7';
.fa.fa-globe.colorful
color: #4caf50
.fa.fa-lock.colorful
color: #f44336
.pop-over .pop-over-list li a:hover
.fa, .fa.colorful
color: white
&:hover
color: white
a.fa, a i.fa
color: darken(white, 35%)
&:hover
color: darken(white, 60%)

View file

@ -1,5 +1,7 @@
@import 'nib' @import 'nib'
global-reset()
html, body, input, select, textarea, button html, body, input, select, textarea, button
font: 14px "Helvetica Neue", Arial, Helvetica, sans-serif font: 14px "Helvetica Neue", Arial, Helvetica, sans-serif
line-height: 18px line-height: 18px
@ -7,77 +9,8 @@ html, body, input, select, textarea, button
html html
font-size: 100% font-size: 100%
-webkit-text-size-adjust: 100%
p
margin: 0
ol,
ul
list-style: none
margin: 0
padding: 0
blockquote, q
quotes: none
&:before,
&:after
content: none
ins
text-decoration: none
del
text-decoration: line-through
table
border-collapse: collapse
border-spacing: 0
width: 100%
hr
height: 1px
border: 0
border: none
width: 100%
background: #dbdbdb
color: #dbdbdb
margin: 15px 0
padding: 0
article,
aside,
figure,
footer,
header,
hgroup,
nav,
section
display: block
caption, th, td
text-align: left
font-weight: 400
a img
border: none
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
nav,
section,
summary
display: block
html
max-height: 100% max-height: 100%
-webkit-text-size-adjust: 100%
body body
background: darken(white, 10%) background: darken(white, 10%)
@ -162,6 +95,16 @@ blockquote
color: #666 color: #666
padding: 0 0 0 8px padding: 0 0 0 8px
hr
height: 1px
border: 0
border: none
width: 100%
background: #dbdbdb
color: #dbdbdb
margin: 15px 0
padding: 0
table, td, th table, td, th
vertical-align: top vertical-align: top
border-top: 1px solid #ccc border-top: 1px solid #ccc
@ -188,11 +131,6 @@ dl, dt
dd dd
margin: 0 0 16px 24px margin: 0 0 16px 24px
.emoji
height: 18px
width: 18px
vertical-align: text-bottom
.edit .edit
display: none display: none
position: relative position: relative
@ -285,7 +223,6 @@ dd
right: 9px right: 9px
&.focus &.focus
.member .member
opacity: 1 opacity: 1
@ -408,10 +345,6 @@ dd
top: 0 top: 0
width: 18px width: 18px
.chrome .minicard.ui-sortable-helper,
.safari .minicard.ui-sortable-helper
box-shadow: -2px 2px 6px rgba(0, 0, 0, .2)
input[type="text"].attachment-add-link-input input[type="text"].attachment-add-link-input
float: left float: left
margin: 0 0 8px margin: 0 0 8px

View file

@ -62,8 +62,9 @@ Boards.attachSchema(new SimpleSchema({
}, },
color: { color: {
type: String, type: String,
allowedValues: ['nephritis', 'pomegranate', 'belize', allowedValues: [
'wisteria', 'midnight', 'pumpkin'] 'nephritis', 'pomegranate', 'belize',
'wisteria', 'midnight', 'pumpkin']
} }
})); }));

View file

@ -29,6 +29,15 @@ Users.helpers({
var board = Boards.findOne(Session.get('currentBoard')); var board = Boards.findOne(Session.get('currentBoard'));
if (this.isBoardMember(board)) if (this.isBoardMember(board))
return _.where(board.members, {userId: this._id})[0].isAdmin; return _.where(board.members, {userId: this._id})[0].isAdmin;
},
toggleBoardStar: function(boardId) {
var queryType = Meteor.user().hasStarred(boardId) ? '$pull' : '$addToSet';
var query = {};
query[queryType] = {
'profile.starredBoards': boardId
};
Meteor.users.update(Meteor.userId(), query);
} }
}); });

View file

@ -157,7 +157,7 @@
"cardMorePopup-title": "More", "cardMorePopup-title": "More",
"cardDeletePopup-title": "Delete Card?", "cardDeletePopup-title": "Delete Card?",
"boardChangeTitlePopup-title": "Rename Board", "boardChangeTitlePopup-title": "Rename Board",
"boardChangePermissionPopup-title": "Change Visibility", "boardChangeVisibilityPopup-title": "Change Visibility",
"addMemberPopup-title": "Members", "addMemberPopup-title": "Members",
"closeBoardPopup-title": "Close Board?", "closeBoardPopup-title": "Close Board?",
"removeMemberPopup-title": "Remove Member?", "removeMemberPopup-title": "Remove Member?",
@ -172,4 +172,4 @@
"setLanguagePopup-title": "Change Language", "setLanguagePopup-title": "Change Language",
"cardAttachmentsPopup-title": "Attach From…", "cardAttachmentsPopup-title": "Attach From…",
"attachmentDeletePopup-title": "Delete Attachment?" "attachmentDeletePopup-title": "Delete Attachment?"
} }

View file

@ -157,7 +157,7 @@
"cardMorePopup-title": "Mais", "cardMorePopup-title": "Mais",
"cardDeletePopup-title": "Excluir Cartão?", "cardDeletePopup-title": "Excluir Cartão?",
"boardChangeTitlePopup-title": "Renomear Quadro", "boardChangeTitlePopup-title": "Renomear Quadro",
"boardChangePermissionPopup-title": "Alterar Visibilidade", "boardChangeVisibilityPopup-title": "Alterar Visibilidade",
"addMemberPopup-title": "Membros", "addMemberPopup-title": "Membros",
"closeBoardPopup-title": "Fechar Quadro?", "closeBoardPopup-title": "Fechar Quadro?",
"removeMemberPopup-title": "Remover Membro?", "removeMemberPopup-title": "Remover Membro?",
@ -172,4 +172,4 @@
"setLanguagePopup-title": "Alterar Idioma", "setLanguagePopup-title": "Alterar Idioma",
"cardAttachmentsPopup-title": "Anexar de…", "cardAttachmentsPopup-title": "Anexar de…",
"attachmentDeletePopup-title": "Excluir Anexo?" "attachmentDeletePopup-title": "Excluir Anexo?"
} }

View file

@ -157,7 +157,7 @@
"cardMorePopup-title": "More", "cardMorePopup-title": "More",
"cardDeletePopup-title": "Delete Card?", "cardDeletePopup-title": "Delete Card?",
"boardChangeTitlePopup-title": "Rename Board", "boardChangeTitlePopup-title": "Rename Board",
"boardChangePermissionPopup-title": "Change Visibility", "boardChangeVisibilityPopup-title": "Change Visibility",
"addMemberPopup-title": "Members", "addMemberPopup-title": "Members",
"closeBoardPopup-title": "Close Board?", "closeBoardPopup-title": "Close Board?",
"removeMemberPopup-title": "Remove Member?", "removeMemberPopup-title": "Remove Member?",
@ -172,4 +172,4 @@
"setLanguagePopup-title": "Change Language", "setLanguagePopup-title": "Change Language",
"cardAttachmentsPopup-title": "Attach From…", "cardAttachmentsPopup-title": "Attach From…",
"attachmentDeletePopup-title": "Delete Attachment?" "attachmentDeletePopup-title": "Delete Attachment?"
} }

View file

@ -157,7 +157,7 @@
"cardMorePopup-title": "更多", "cardMorePopup-title": "更多",
"cardDeletePopup-title": "删除卡片?", "cardDeletePopup-title": "删除卡片?",
"boardChangeTitlePopup-title": "重命名看板", "boardChangeTitlePopup-title": "重命名看板",
"boardChangePermissionPopup-title": "更改可视级别", "boardChangeVisibilityPopup-title": "更改可视级别",
"addMemberPopup-title": "成员", "addMemberPopup-title": "成员",
"closeBoardPopup-title": "关闭看板?", "closeBoardPopup-title": "关闭看板?",
"removeMemberPopup-title": "删除成员?", "removeMemberPopup-title": "删除成员?",
@ -172,4 +172,4 @@
"setLanguagePopup-title": "更改语言", "setLanguagePopup-title": "更改语言",
"cardAttachmentsPopup-title": "附加自...", "cardAttachmentsPopup-title": "附加自...",
"attachmentDeletePopup-title": "删除附件?" "attachmentDeletePopup-title": "删除附件?"
} }

View file

@ -157,7 +157,7 @@
"cardMorePopup-title": "More", "cardMorePopup-title": "More",
"cardDeletePopup-title": "Delete Card?", "cardDeletePopup-title": "Delete Card?",
"boardChangeTitlePopup-title": "Rename Board", "boardChangeTitlePopup-title": "Rename Board",
"boardChangePermissionPopup-title": "Change Visibility", "boardChangeVisibilityPopup-title": "Change Visibility",
"addMemberPopup-title": "Members", "addMemberPopup-title": "Members",
"closeBoardPopup-title": "Close Board?", "closeBoardPopup-title": "Close Board?",
"removeMemberPopup-title": "Remove Member?", "removeMemberPopup-title": "Remove Member?",
@ -172,4 +172,4 @@
"setLanguagePopup-title": "Change Language", "setLanguagePopup-title": "Change Language",
"cardAttachmentsPopup-title": "Attach From…", "cardAttachmentsPopup-title": "Attach From…",
"attachmentDeletePopup-title": "Delete Attachment?" "attachmentDeletePopup-title": "Delete Attachment?"
} }

View file

@ -157,7 +157,7 @@
"cardMorePopup-title": "Mehr", "cardMorePopup-title": "Mehr",
"cardDeletePopup-title": "Karte entfernen?", "cardDeletePopup-title": "Karte entfernen?",
"boardChangeTitlePopup-title": "Bord umbenennen", "boardChangeTitlePopup-title": "Bord umbenennen",
"boardChangePermissionPopup-title": "Ändere Sichbarkeit", "boardChangeVisibilityPopup-title": "Ändere Sichbarkeit",
"addMemberPopup-title": "Nutzer", "addMemberPopup-title": "Nutzer",
"closeBoardPopup-title": "Schliese Bord?", "closeBoardPopup-title": "Schliese Bord?",
"removeMemberPopup-title": "Entferne Nutzer?", "removeMemberPopup-title": "Entferne Nutzer?",
@ -172,4 +172,4 @@
"setLanguagePopup-title": "Ändere Sprache", "setLanguagePopup-title": "Ändere Sprache",
"cardAttachmentsPopup-title": "Attach From…", "cardAttachmentsPopup-title": "Attach From…",
"attachmentDeletePopup-title": "Delete Attachment?" "attachmentDeletePopup-title": "Delete Attachment?"
} }

View file

@ -37,6 +37,8 @@
"board-list-btn-title": "View list of boards", "board-list-btn-title": "View list of boards",
"board-not-found": "Board not found", "board-not-found": "Board not found",
"board-public-info": "This board will be <strong>public</strong>.", "board-public-info": "This board will be <strong>public</strong>.",
"board-private-info": "This board will be <strong>private</strong>.",
"board-nb-stars": "%s stars",
"boards": "Boards", "boards": "Boards",
"bucket-example": "Like “Bucket List” for example…", "bucket-example": "Like “Bucket List” for example…",
"cancel": "Cancel", "cancel": "Cancel",
@ -70,9 +72,10 @@
"email": "Email", "email": "Email",
"email-or-username": "Email or username", "email-or-username": "Email or username",
"email-placeholder": "e.g., doc@frankenstein.com", "email-placeholder": "e.g., doc@frankenstein.com",
"filter": "Filter",
"filter-cards": "Filter Cards", "filter-cards": "Filter Cards",
"filter-clear": "Clear filter.", "filter-clear": "Clear filter.",
"filter-on": "Filtering is on.", "filter-on": "Filter is on",
"filter-on-desc": "You are filtering cards on this board. Click here to edit filter.", "filter-on-desc": "You are filtering cards on this board. Click here to edit filter.",
"fullname": "Full Name", "fullname": "Full Name",
"gloabal-search": "Global Search", "gloabal-search": "Global Search",
@ -163,7 +166,7 @@
"cardDeletePopup-title": "Delete Card?", "cardDeletePopup-title": "Delete Card?",
"boardMenuPopup-title": "Board Menu", "boardMenuPopup-title": "Board Menu",
"boardChangeTitlePopup-title": "Rename Board", "boardChangeTitlePopup-title": "Rename Board",
"boardChangePermissionPopup-title": "Change Visibility", "boardChangeVisibilityPopup-title": "Change Visibility",
"addMemberPopup-title": "Members", "addMemberPopup-title": "Members",
"closeBoardPopup-title": "Close Board?", "closeBoardPopup-title": "Close Board?",
"removeMemberPopup-title": "Remove Member?", "removeMemberPopup-title": "Remove Member?",

View file

@ -157,7 +157,7 @@
"cardMorePopup-title": "Más", "cardMorePopup-title": "Más",
"cardDeletePopup-title": "¿Borrar ficha?", "cardDeletePopup-title": "¿Borrar ficha?",
"boardChangeTitlePopup-title": "Renombrar tablero", "boardChangeTitlePopup-title": "Renombrar tablero",
"boardChangePermissionPopup-title": "Cambiar visibilidad", "boardChangeVisibilityPopup-title": "Cambiar visibilidad",
"addMemberPopup-title": "Miembros", "addMemberPopup-title": "Miembros",
"closeBoardPopup-title": "Cerrar el tablero", "closeBoardPopup-title": "Cerrar el tablero",
"removeMemberPopup-title": "¿Eliminar miembro?", "removeMemberPopup-title": "¿Eliminar miembro?",
@ -172,4 +172,4 @@
"setLanguagePopup-title": "Cambiar idioma", "setLanguagePopup-title": "Cambiar idioma",
"cardAttachmentsPopup-title": "Adjuntar de...", "cardAttachmentsPopup-title": "Adjuntar de...",
"attachmentDeletePopup-title": "¿Borrar adjunto?" "attachmentDeletePopup-title": "¿Borrar adjunto?"
} }

View file

@ -157,7 +157,7 @@
"cardMorePopup-title": "Lisää", "cardMorePopup-title": "Lisää",
"cardDeletePopup-title": "Poista kortti?", "cardDeletePopup-title": "Poista kortti?",
"boardChangeTitlePopup-title": "Nimeä taulu uudelleen", "boardChangeTitlePopup-title": "Nimeä taulu uudelleen",
"boardChangePermissionPopup-title": "Vaihda näkyvyyttä", "boardChangeVisibilityPopup-title": "Vaihda näkyvyyttä",
"addMemberPopup-title": "Jäsenet", "addMemberPopup-title": "Jäsenet",
"closeBoardPopup-title": "Sulje taulu?", "closeBoardPopup-title": "Sulje taulu?",
"removeMemberPopup-title": "Poista jäsen?", "removeMemberPopup-title": "Poista jäsen?",
@ -172,4 +172,4 @@
"setLanguagePopup-title": "Vaihda kieltä", "setLanguagePopup-title": "Vaihda kieltä",
"cardAttachmentsPopup-title": "Liitä mistä...", "cardAttachmentsPopup-title": "Liitä mistä...",
"attachmentDeletePopup-title": "Poista liitetiedosto?" "attachmentDeletePopup-title": "Poista liitetiedosto?"
} }

View file

@ -157,7 +157,7 @@
"cardMorePopup-title": "Plus", "cardMorePopup-title": "Plus",
"cardDeletePopup-title": "Supprimer la carte ?", "cardDeletePopup-title": "Supprimer la carte ?",
"boardChangeTitlePopup-title": "Renommer le tableau", "boardChangeTitlePopup-title": "Renommer le tableau",
"boardChangePermissionPopup-title": "Changer la visibilité", "boardChangeVisibilityPopup-title": "Changer la visibilité",
"addMemberPopup-title": "Membres", "addMemberPopup-title": "Membres",
"closeBoardPopup-title": "Fermer le tableau ?", "closeBoardPopup-title": "Fermer le tableau ?",
"removeMemberPopup-title": "Supprimer le membre ?", "removeMemberPopup-title": "Supprimer le membre ?",
@ -172,4 +172,4 @@
"setLanguagePopup-title": "Changer la langue", "setLanguagePopup-title": "Changer la langue",
"cardAttachmentsPopup-title": "Joindre depuis…", "cardAttachmentsPopup-title": "Joindre depuis…",
"attachmentDeletePopup-title": "Supprimer la pièce jointe ?" "attachmentDeletePopup-title": "Supprimer la pièce jointe ?"
} }

View file

@ -157,7 +157,7 @@
"cardMorePopup-title": "More", "cardMorePopup-title": "More",
"cardDeletePopup-title": "Delete Card?", "cardDeletePopup-title": "Delete Card?",
"boardChangeTitlePopup-title": "Rename Board", "boardChangeTitlePopup-title": "Rename Board",
"boardChangePermissionPopup-title": "Change Visibility", "boardChangeVisibilityPopup-title": "Change Visibility",
"addMemberPopup-title": "Members", "addMemberPopup-title": "Members",
"closeBoardPopup-title": "Close Board?", "closeBoardPopup-title": "Close Board?",
"removeMemberPopup-title": "Remove Member?", "removeMemberPopup-title": "Remove Member?",
@ -172,4 +172,4 @@
"setLanguagePopup-title": "Change Language", "setLanguagePopup-title": "Change Language",
"cardAttachmentsPopup-title": "Attach From…", "cardAttachmentsPopup-title": "Attach From…",
"attachmentDeletePopup-title": "Delete Attachment?" "attachmentDeletePopup-title": "Delete Attachment?"
} }

View file

@ -157,7 +157,7 @@
"cardMorePopup-title": "More", "cardMorePopup-title": "More",
"cardDeletePopup-title": "Delete Card?", "cardDeletePopup-title": "Delete Card?",
"boardChangeTitlePopup-title": "Rename Board", "boardChangeTitlePopup-title": "Rename Board",
"boardChangePermissionPopup-title": "Change Visibility", "boardChangeVisibilityPopup-title": "Change Visibility",
"addMemberPopup-title": "Members", "addMemberPopup-title": "Members",
"closeBoardPopup-title": "Close Board?", "closeBoardPopup-title": "Close Board?",
"removeMemberPopup-title": "Remove Member?", "removeMemberPopup-title": "Remove Member?",
@ -172,4 +172,4 @@
"setLanguagePopup-title": "Change Language", "setLanguagePopup-title": "Change Language",
"cardAttachmentsPopup-title": "Attach From…", "cardAttachmentsPopup-title": "Attach From…",
"attachmentDeletePopup-title": "Delete Attachment?" "attachmentDeletePopup-title": "Delete Attachment?"
} }

View file

@ -157,7 +157,7 @@
"cardMorePopup-title": "More", "cardMorePopup-title": "More",
"cardDeletePopup-title": "カードを削除しますか?", "cardDeletePopup-title": "カードを削除しますか?",
"boardChangeTitlePopup-title": "ボード名の変更", "boardChangeTitlePopup-title": "ボード名の変更",
"boardChangePermissionPopup-title": "公開範囲の変更", "boardChangeVisibilityPopup-title": "公開範囲の変更",
"addMemberPopup-title": "メンバー", "addMemberPopup-title": "メンバー",
"closeBoardPopup-title": "ボードを閉じますか?", "closeBoardPopup-title": "ボードを閉じますか?",
"removeMemberPopup-title": "メンバーを外しますか?", "removeMemberPopup-title": "メンバーを外しますか?",
@ -172,4 +172,4 @@
"setLanguagePopup-title": "言語の変更", "setLanguagePopup-title": "言語の変更",
"cardAttachmentsPopup-title": "Attach From…", "cardAttachmentsPopup-title": "Attach From…",
"attachmentDeletePopup-title": "Delete Attachment?" "attachmentDeletePopup-title": "Delete Attachment?"
} }

View file

@ -157,7 +157,7 @@
"cardMorePopup-title": "더보기", "cardMorePopup-title": "더보기",
"cardDeletePopup-title": "카드를 삭제합니까?", "cardDeletePopup-title": "카드를 삭제합니까?",
"boardChangeTitlePopup-title": "보드 이름 바꾸기", "boardChangeTitlePopup-title": "보드 이름 바꾸기",
"boardChangePermissionPopup-title": "표시 여부 변경", "boardChangeVisibilityPopup-title": "표시 여부 변경",
"addMemberPopup-title": "멤버", "addMemberPopup-title": "멤버",
"closeBoardPopup-title": "보드를 닫습니까?", "closeBoardPopup-title": "보드를 닫습니까?",
"removeMemberPopup-title": "멤버를 제거합니까?", "removeMemberPopup-title": "멤버를 제거합니까?",
@ -172,4 +172,4 @@
"setLanguagePopup-title": "언어 변경", "setLanguagePopup-title": "언어 변경",
"cardAttachmentsPopup-title": "첨부할 위치...", "cardAttachmentsPopup-title": "첨부할 위치...",
"attachmentDeletePopup-title": "첨부 파일을 삭제합니까?" "attachmentDeletePopup-title": "첨부 파일을 삭제합니까?"
} }

View file

@ -157,7 +157,7 @@
"cardMorePopup-title": "Поделиться", "cardMorePopup-title": "Поделиться",
"cardDeletePopup-title": "Удалить карточку?", "cardDeletePopup-title": "Удалить карточку?",
"boardChangeTitlePopup-title": "Переименовать доску", "boardChangeTitlePopup-title": "Переименовать доску",
"boardChangePermissionPopup-title": "Изменить настройки видимости", "boardChangeVisibilityPopup-title": "Изменить настройки видимости",
"addMemberPopup-title": "Участники", "addMemberPopup-title": "Участники",
"closeBoardPopup-title": "Закрыть доску?", "closeBoardPopup-title": "Закрыть доску?",
"removeMemberPopup-title": "Удалить участника?", "removeMemberPopup-title": "Удалить участника?",
@ -172,4 +172,4 @@
"setLanguagePopup-title": "Сменить язык", "setLanguagePopup-title": "Сменить язык",
"cardAttachmentsPopup-title": "Источник", "cardAttachmentsPopup-title": "Источник",
"attachmentDeletePopup-title": "Удалить вложение?" "attachmentDeletePopup-title": "Удалить вложение?"
} }

View file

@ -157,7 +157,7 @@
"cardMorePopup-title": "More", "cardMorePopup-title": "More",
"cardDeletePopup-title": "Kart Silinsin mi?", "cardDeletePopup-title": "Kart Silinsin mi?",
"boardChangeTitlePopup-title": "Pano Adı Değiştirme", "boardChangeTitlePopup-title": "Pano Adı Değiştirme",
"boardChangePermissionPopup-title": "Görünebilirliği Değiştir", "boardChangeVisibilityPopup-title": "Görünebilirliği Değiştir",
"addMemberPopup-title": "Üyeler", "addMemberPopup-title": "Üyeler",
"closeBoardPopup-title": "Pano Kapatılsın mı?", "closeBoardPopup-title": "Pano Kapatılsın mı?",
"removeMemberPopup-title": "Üyeyi Çıkarmak mı?", "removeMemberPopup-title": "Üyeyi Çıkarmak mı?",
@ -172,4 +172,4 @@
"setLanguagePopup-title": "Dil Değiştir", "setLanguagePopup-title": "Dil Değiştir",
"cardAttachmentsPopup-title": "Şuradan Ekle...", "cardAttachmentsPopup-title": "Şuradan Ekle...",
"attachmentDeletePopup-title": "Ek Dosya Silinsin Mi?" "attachmentDeletePopup-title": "Ek Dosya Silinsin Mi?"
} }

View file

@ -109,7 +109,7 @@ if (isSandstorm && Meteor.isClient) {
Meteor.absoluteUrl.defaultOptions = _defaultOptions; Meteor.absoluteUrl.defaultOptions = _defaultOptions;
} }
// We use this blaze helper in the UI to hide some template that does not make // We use this blaze helper in the UI to hide some templates that does not make
// sense in the context of sandstorm, like board staring, board archiving, user // sense in the context of sandstorm, like board staring, board archiving, user
// name edition, etc. // name edition, etc.
Blaze.registerHelper('isSandstorm', function() { Blaze.registerHelper('isSandstorm', function() {