From c8945679872a0708eb67a477a99a65d508c84cb0 Mon Sep 17 00:00:00 2001 From: Maxime Quandalle Date: Fri, 12 Jun 2015 13:59:39 +0200 Subject: Work on the card activities and comments This commit also introduces a new CSSEvents object that is used to abstract vendor specifics events related to CSS transitions and animations. Fixes #183. Fixes #179. --- client/components/activities/activities.jade | 107 +++++++++++++++- client/components/activities/activities.js | 36 +++++- client/components/activities/activities.styl | 28 +++++ client/components/activities/comments.jade | 8 ++ client/components/activities/comments.js | 49 ++++++++ client/components/activities/comments.styl | 40 +++--- client/components/activities/events.js | 30 ----- client/components/activities/templates.html | 154 ----------------------- client/components/boards/boardBody.js | 11 +- client/components/boards/boardHeader.jade | 2 + client/components/cards/details.jade | 32 +++-- client/components/cards/details.js | 31 +++-- client/components/cards/details.styl | 69 ++-------- client/components/cards/events.js | 182 --------------------------- client/components/cards/helpers.js | 48 ------- client/components/cards/labels.jade | 27 ++++ client/components/cards/labels.js | 126 +++++++++++++++++++ client/components/cards/labels.styl | 16 +-- client/components/cards/minicard.jade | 2 +- client/components/cards/minicard.styl | 26 ++-- client/components/cards/templates.html | 110 ---------------- client/components/forms/forms.styl | 5 + client/components/main/editor.styl | 2 - client/components/main/popup.js | 21 +--- client/components/users/userAvatar.jade | 22 +++- client/components/users/userAvatar.js | 37 ++++++ client/config/helpers.js | 6 + client/lib/cssEvents.js | 42 +++++++ client/styles/main.styl | 3 + 29 files changed, 589 insertions(+), 683 deletions(-) create mode 100644 client/components/activities/activities.styl delete mode 100644 client/components/activities/events.js delete mode 100644 client/components/activities/templates.html delete mode 100644 client/components/cards/helpers.js create mode 100644 client/components/cards/labels.jade create mode 100644 client/components/cards/labels.js delete mode 100644 client/components/main/editor.styl create mode 100644 client/config/helpers.js create mode 100644 client/lib/cssEvents.js (limited to 'client') diff --git a/client/components/activities/activities.jade b/client/components/activities/activities.jade index 1c6b9faf..dd47af7f 100644 --- a/client/components/activities/activities.jade +++ b/client/components/activities/activities.jade @@ -1,8 +1,113 @@ template(name="activities") - .js-sidebar-activities + .activities.js-sidebar-activities //- We should use Template.dynamic here but there is a bug with //- blaze-components: https://github.com/peerlibrary/meteor-blaze-components/issues/30 if $eq mode "board" +boardActivities else +cardActivities + +template(name="boardActivities") + each currentBoard.activities + .activity + +userAvatar(userId=user._id) + p.activity-desc + +memberName(user=user) + + if($eq activityType 'createBoard') + | {{_ 'activity-created' boardLabel}}. + + if($eq activityType 'createList') + | {{_ 'activity-added' list.title boardLabel}}. + + if($eq activityType 'archivedList') + | {{_ 'activity-archived' list.title}}. + + if($eq activityType 'createCard') + | {{{_ 'activity-added' cardLink boardLabel}}}. + + if($eq activityType 'archivedCard') + | {{{_ 'activity-archived' cardLink}}}. + + if($eq activityType 'restoredCard') + | {{{_ 'activity-sent' cardLink boardLabel}}}. + + if($eq activityType 'moveCard') + | {{{_ 'activity-moved' cardLink oldList.title list.title}}}. + + if($eq activityType 'addBoardMember') + | {{{_ 'activity-added' memberLink boardLabel}}}. + + if($eq activityType 'removeBoardMember') + | {{{_ 'activity-excluded' memberLink boardLabel}}}. + + if($eq activityType 'joinMember') + if($eq currentUser._id member._id) + | {{{_ 'activity-joined' cardLink}}}. + else + | {{{_ 'activity-added' memberLink cardLink}}}. + + if($eq activityType 'unjoinMember') + if($eq currentUser._id member._id) + | {{{_ 'activity-unjoined' cardLink}}}. + else + | {{{_ 'activity-removed' memberLink cardLink}}}. + + if($eq activityType 'addComment') + | {{{_ 'activity-on' cardLink}}} + a.activity-comment(href="{{ card.absoluteUrl }}") + +viewer + = comment.text + + if($eq activityType 'addAttachment') + | {{{_ 'activity-attached' attachmentLink cardLink}}}. + + span.activity-meta {{ moment createdAt }} + +template(name="cardActivities") + each currentCard.activities + .activity + +userAvatar(userId=user._id) + p.activity-desc + +memberName(user=user) + if($eq activityType 'createCard') + | {{_ 'activity-added' cardLabel list.title}}. + if($eq activityType 'joinMember') + if($eq currentUser._id member._id) + | {{_ 'activity-joined' cardLabel}}. + else + | {{{_ 'activity-added' cardLabel memberLink}}}. + if($eq activityType 'unjoinMember') + if($eq currentUser._id member._id) + | {{_ 'activity-unjoined' cardLabel}}. + else + | {{{_ 'activity-removed' cardLabel memberLink}}}. + if($eq activityType 'archivedCard') + | {{_ 'activity-archived' cardLabel}}. + if($eq activityType 'restoredCard') + | {{_ 'activity-sent' cardLabel boardLabel}}. + if($eq activityType 'moveCard') + | {{_ 'activity-moved' cardLabel oldList.title list.title}}. + if($eq activityType 'addAttachment') + | {{{_ 'activity-attached' attachmentLink cardLabel}}}. + if attachment.isImage + img.attachment-image-preview(src=attachment.url) + + if($eq activityType 'addComment') + +inlinedForm(classNames='js-edit-comment') + +editor(autofocus=true) + = comment.text + .edit-controls + button.primary(type="submit") {{_ 'edit'}} + else + .activity-comment + +viewer + = comment.text + span.activity-meta + | {{ moment createdAt }} - + a.js-open-inlined-form {{_ "edit"}} + = ' - ' + a.js-delete-comment {{_ "delete"}} + + else + span.activity-meta {{ moment createdAt }} diff --git a/client/components/activities/activities.js b/client/components/activities/activities.js index c806e87b..5d95006a 100644 --- a/client/components/activities/activities.js +++ b/client/components/activities/activities.js @@ -49,10 +49,6 @@ BlazeComponent.extendComponent({ return TAPi18n.__('this-board'); }, - cardLabel: function() { - return TAPi18n.__('this-card'); - }, - cardLink: function() { var card = this.currentData().card(); return Blaze.toHTML(HTML.A({ @@ -75,3 +71,35 @@ BlazeComponent.extendComponent({ }, attachment.name())); } }).register('activities'); + +BlazeComponent.extendComponent({ + template: function() { + return 'cardActivities'; + }, + + cardLabel: function() { + return TAPi18n.__('this-card'); + }, + + events: function() { + return [{ + // XXX We should use Popup.afterConfirmation here + 'click .js-delete-comment': function() { + var commentId = this.currentData().commentId; + CardComments.remove(commentId); + }, + 'submit .js-edit-comment': function(evt) { + evt.preventDefault(); + var commentText = this.currentComponent().getValue(); + var commentId = Template.parentData().commentId; + if ($.trim(commentText)) { + CardComments.update(commentId, { + $set: { + text: commentText + } + }); + } + } + }]; + } +}).register('cardActivities'); diff --git a/client/components/activities/activities.styl b/client/components/activities/activities.styl new file mode 100644 index 00000000..de2b796d --- /dev/null +++ b/client/components/activities/activities.styl @@ -0,0 +1,28 @@ +.activities + clear: both + + .activity + margin: 6px 0 + display: flex + + .member + width: 24px + height: @width + + .activity-desc + flex: 1 + align-self: center + margin: 0 + + .activity-comment + display: block + border-radius: 3px + background: white + text-decoration: none + box-shadow: 0 1px 2px rgba(0,0,0,.2) + margin-top: 5px + padding: 5px + + .activity-meta + font-size: 0.8em + color: darken(white, 40%) diff --git a/client/components/activities/comments.jade b/client/components/activities/comments.jade index e69de29b..3b47cbf6 100644 --- a/client/components/activities/comments.jade +++ b/client/components/activities/comments.jade @@ -0,0 +1,8 @@ +template(name="commentForm") + .new-comment.js-new-comment( + class="{{#if commentFormIsOpen}}is-open{{/if}}") + +userAvatar(userId=currentUser._id) + form.js-new-comment-form + +editor(class="js-new-comment-input") + .add-controls + button.primary.confirm.clear.js-add-comment(type="submit") {{_ 'comment'}} diff --git a/client/components/activities/comments.js b/client/components/activities/comments.js index e69de29b..d41b86dd 100644 --- a/client/components/activities/comments.js +++ b/client/components/activities/comments.js @@ -0,0 +1,49 @@ +var commentFormIsOpen = new ReactiveVar(false); + +Template.commentForm.helpers({ + commentFormIsOpen: function() { + return commentFormIsOpen.get(); + } +}); + +Template.commentForm.events({ + 'click .js-new-comment:not(.focus)': function() { + commentFormIsOpen.set(true); + }, + 'submit .js-new-comment-form': function(evt, tpl) { + var input = tpl.$('.js-new-comment-input'); + if ($.trim(input.val())) { + CardComments.insert({ + boardId: this.boardId, + cardId: this._id, + text: input.val() + }); + input.val(''); + input.blur(); + commentFormIsOpen.set(false); + Tracker.flush(); + autosize.update(input); + } + evt.preventDefault(); + }, + // Pressing Ctrl+Enter should submit the form + 'keydown form textarea': function(evt, tpl) { + if (evt.keyCode === 13 && (evt.metaKey || evt.ctrlKey)) { + tpl.find('button[type=submit]').click(); + } + } +}); + +Template.commentForm.onDestroyed(function() { + commentFormIsOpen.set(false); +}); + +EscapeActions.register('inlinedForm', + function() { + commentFormIsOpen.set(false); + $('.js-new-comment-input').blur(); + }, + function() { return commentFormIsOpen.get(); }, { + noClickEscapeOn: '.js-new-comment' + } +); diff --git a/client/components/activities/comments.styl b/client/components/activities/comments.styl index f372e5ef..1857ca99 100644 --- a/client/components/activities/comments.styl +++ b/client/components/activities/comments.styl @@ -8,15 +8,15 @@ top: 1px left: -38px - &.focus + &.is-open .member opacity: 1 .helper display: inline-block - .new-comment-input - min-height: 108px + textarea + min-height: 100px color: #4d4d4d cursor: auto overflow: hidden @@ -25,22 +25,22 @@ .too-long margin-top: 8px -.new-comment-input - background-color: #fff - border: 0 - box-shadow: 0 1px 2px rgba(0, 0, 0, .23) - color: #8c8c8c - height: 36px - margin: 4px 4px 6px 0 - padding: 9px 11px - width: 100% - - &:hover, - &:focus + textarea background-color: #fff - box-shadow: 0 1px 3px rgba(0, 0, 0, .33) border: 0 - cursor: pointer - - &:focus - cursor: auto + box-shadow: 0 1px 2px rgba(0, 0, 0, .23) + color: #8c8c8c + height: 36px + margin: 4px 4px 6px 0 + padding: 9px 11px + width: 100% + + &:hover, + &:is-open + background-color: #fff + box-shadow: 0 1px 3px rgba(0, 0, 0, .33) + border: 0 + cursor: pointer + + &:is-open + cursor: auto diff --git a/client/components/activities/events.js b/client/components/activities/events.js deleted file mode 100644 index ea98e65f..00000000 --- a/client/components/activities/events.js +++ /dev/null @@ -1,30 +0,0 @@ -Template.cardActivities.events({ - 'click .js-edit-action': function(evt) { - var $this = $(evt.currentTarget); - var container = $this.parents('.phenom-comment'); - - // open and focus - container.addClass('editing'); - container.find('textarea').focus(); - }, - 'click .js-confirm-delete-action': function() { - CardComments.remove(this._id); - }, - 'submit form': function(evt) { - var $this = $(evt.currentTarget); - var container = $this.parents('.phenom-comment'); - var text = container.find('textarea'); - - if ($.trim(text.val())) { - CardComments.update(this._id, { - $set: { - text: text.val() - } - }); - - // reset editing class - $('.editing').removeClass('editing'); - } - evt.preventDefault(); - } -}); diff --git a/client/components/activities/templates.html b/client/components/activities/templates.html deleted file mode 100644 index 5a595a36..00000000 --- a/client/components/activities/templates.html +++ /dev/null @@ -1,154 +0,0 @@ - - - diff --git a/client/components/boards/boardBody.js b/client/components/boards/boardBody.js index e1b39ce8..1fd64cbc 100644 --- a/client/components/boards/boardBody.js +++ b/client/components/boards/boardBody.js @@ -1,12 +1,3 @@ -// XXX This event list must be abstracted somewhere else. -var endTransitionEvents = [ - 'webkitTransitionEnd', - 'otransitionend', - 'oTransitionEnd', - 'msTransitionEnd', - 'transitionend' -].join(' '); - BlazeComponent.extendComponent({ template: function() { return 'boardComponent'; @@ -69,7 +60,7 @@ BlazeComponent.extendComponent({ flexBasis: 0, padding: 0 }); - $(lists).one(endTransitionEvents, removeNode); + $(lists).one(CSSEvents.transitionend, removeNode); } else { removeNode(); } diff --git a/client/components/boards/boardHeader.jade b/client/components/boards/boardHeader.jade index b0f0eebe..e78de3b2 100644 --- a/client/components/boards/boardHeader.jade +++ b/client/components/boards/boardHeader.jade @@ -106,9 +106,11 @@ template(name="createBoardPopup") 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'}}") diff --git a/client/components/cards/details.jade b/client/components/cards/details.jade index be166ad8..1eeda71d 100644 --- a/client/components/cards/details.jade +++ b/client/components/cards/details.jade @@ -1,5 +1,5 @@ template(name="cardDetails") - section.card-details.js-card-details: .card-details-canvas + section.card-details.js-card-details.js-perfect-scrollbar: .card-details-canvas if cover .card-details-cover(style="background-image: url({{ cover.url }})") @@ -42,7 +42,7 @@ template(name="cardDetails") //- XXX We should use "editable" to avoide repetiting ourselves if currentUser.isBoardMember h3.card-details-item-title Description - +inlinedForm(classNames="js-card-description") + +inlinedForm(classNames="card-description js-card-description") +editor(autofocus=true) = description .edit-controls.clearfix @@ -62,9 +62,13 @@ template(name="cardDetails") if attachments.count hr +WindowAttachmentsModule(card=this) - if isLoaded - hr - +WindowActivityModule(card=this) + + hr + h2 {{ _ 'activity'}} + if currentUser.isBoardMember + +commentForm + if isLoaded.get + +activities(card=this mode="card") template(name="cardDetailsActionsPopup") if currentUser.isBoardMember @@ -75,14 +79,15 @@ template(name="cardDetailsActionsPopup") hr ul.pop-over-list li: a.js-copy Copy Card - li: a.js-archive Archive Card - li: a.js-delete Delete Card + unless archived + li: a.js-archive Archive Card + li: a.js-more More template(name="moveCardPopup") +boardLists template(name="cardMembersPopup") - ul.pop-over-member-list + ul.pop-over-list.pop-over-member-list each board.members li.item(class="{{#if isCardMember}}active{{/if}}") a.name.js-select-member(href="#") @@ -105,6 +110,17 @@ template(name="cardLabelsPopup") span.card-label-selectable-icon.fa.fa-check a.quiet-button.full.js-add-label {{_ 'label-create'}} +template(name="cardMorePopup") + p.quiet + span.clearfix + span {{_ 'link-card'}} + = ' ' + i.fa.colorful(class="{{#if board.isPublic}}fa-globe{{else}}fa-lock{{/if}}") + input.inline-input(type="text" readonly value="{{ rootUrl }}") + | {{_ 'added'}} + span.date(title=card.createdAt) {{ moment createdAt 'LLL' }} + a.js-delete(title="{{_ 'card-delete-notice'}}") {{_ 'delete'}} + template(name="cardDeletePopup") p {{_ "card-delete-pop"}} unless archived diff --git a/client/components/cards/details.js b/client/components/cards/details.js index f3d03793..05da2053 100644 --- a/client/components/cards/details.js +++ b/client/components/cards/details.js @@ -4,7 +4,7 @@ BlazeComponent.extendComponent({ }, mixins: function() { - return [Mixins.InfiniteScrolling]; + return [Mixins.InfiniteScrolling, Mixins.PerfectScrollbar]; }, calculateNextPeak: function() { @@ -35,8 +35,19 @@ BlazeComponent.extendComponent({ }); }, + onCreated: function() { + this.isLoaded = new ReactiveVar(false); + }, + events: function() { - return [{ + // XXX We can't define this event directly in the event map below because we + // miss ES6 object keys interpolation. + var events = {}; + events[CSSEvents.animationend + ' .js-card-details'] = function() { + this.isLoaded.set(true); + }; + + return [_.extend(events, { 'click .js-close-card-details': function() { Utils.goBoardId(this.data().boardId); }, @@ -60,7 +71,7 @@ BlazeComponent.extendComponent({ 'mouseenter .js-card-details': function() { this.componentParent().showOverlay.set(true); } - }]; + })]; } }).register('cardDetails'); @@ -78,11 +89,7 @@ Template.cardDetailsActionsPopup.events({ }); Popup.close(); }, - 'click .js-delete': Popup.afterConfirm('cardDelete', function() { - var cardId = this._id; - Cards.remove(cardId); - Popup.close(); - }) + 'click .js-more': Popup.open('cardMore') }); Template.moveCardPopup.events({ @@ -100,6 +107,14 @@ Template.moveCardPopup.events({ } }); +Template.cardMorePopup.events({ + 'click .js-delete': Popup.afterConfirm('cardDelete', function() { + Popup.close(); + Cards.remove(this._id); + Utils.goBoardId(this.board()._id); + }) +}); + // Close the card details pane by pressing escape EscapeActions.register('detailsPane', function() { Utils.goBoardId(Session.get('currentBoard')); }, diff --git a/client/components/cards/details.styl b/client/components/cards/details.styl index 94c75cf5..72e8c7c9 100644 --- a/client/components/cards/details.styl +++ b/client/components/cards/details.styl @@ -10,9 +10,9 @@ background: white border-radius: 3px z-index: 20 !important - animation: flexGrowIn 0.2s + animation: flexGrowIn 0.1s box-shadow: 0 0 7px 0 darken(white, 30%) - transition: flex-basis 0.2s, padding 0.2s + transition: flex-basis 0.1s margin-top: -9px .card-details-canvas @@ -62,13 +62,18 @@ border-radius: 3px padding: 0px 5px + .card-description textarea + min-height: 100px .card-details-items display: flex margin: 15px 0 .card-details-item - flex-grow: 1 + &.card-details-item-labels, + &.card-details-item-members + width: 50% + flex-shrink: 1 .card-details-item-title font-size: 14px @@ -78,62 +83,8 @@ padding-top: 5px padding-bottom: 5px -.new-comment - position: relative - margin: 0 0 20px 38px - - .member - opacity: .7 - position: absolute - top: 1px - left: -38px - - .helper - bottom: 0 - display: none - position: absolute - right: 9px - - &.focus - - .member - opacity: 1 - - .helper - display: inline-block - - .new-comment-input - min-height: 108px - color: #4d4d4d - cursor: auto - overflow: hidden - word-wrap: break-word - - .too-long - margin-top: 8px - -.new-comment-input - background-color: #fff - border: 0 - box-shadow: 0 1px 2px rgba(0, 0, 0, .23) - color: #8c8c8c - height: 36px - margin: 4px 4px 6px 0 - padding: 9px 11px - width: 100% - - &:hover, - &:focus - background-color: #fff - box-shadow: 0 1px 3px rgba(0, 0, 0, .33) - border: 0 - cursor: pointer - - &:focus - cursor: auto - -.card-composer - padding-bottom: 8px + .activities + padding-top: 10px input[type="text"].attachment-add-link-input float: left diff --git a/client/components/cards/events.js b/client/components/cards/events.js index 2363f4de..ca2ddb50 100644 --- a/client/components/cards/events.js +++ b/client/components/cards/events.js @@ -1,61 +1,3 @@ -Template.cardMemberPopup.events({ - 'click .js-remove-member': function() { - Cards.update(this.cardId, {$pull: {members: this.userId}}); - Popup.close(); - } -}); - -Template.WindowActivityModule.events({ - 'click .js-new-comment:not(.focus)': function(evt) { - var $this = $(evt.currentTarget); - $this.addClass('focus'); - }, - 'submit #CommentForm': function(evt, t) { - var text = t.$('.js-new-comment-input'); - if ($.trim(text.val())) { - CardComments.insert({ - boardId: this.card.boardId, - cardId: this.card._id, - text: text.val() - }); - text.val(''); - $('.focus').removeClass('focus'); - } - evt.preventDefault(); - } -}); - -Template.WindowSidebarModule.events({ - 'click .js-change-card-members': Popup.open('cardMembers'), - 'click .js-edit-labels': Popup.open('cardLabels'), - 'click .js-archive-card': function(evt) { - // Update - Cards.update(this.card._id, { - $set: { - archived: true - } - }); - evt.preventDefault(); - }, - 'click .js-unarchive-card': function(evt) { - Cards.update(this.card._id, { - $set: { - archived: false - } - }); - evt.preventDefault(); - }, - 'click .js-delete-card': Popup.afterConfirm('cardDelete', function() { - Cards.remove(this.card._id); - - // redirect board - Utils.goBoardId(this.card.board()._id); - Popup.close(); - }), - 'click .js-more-menu': Popup.open('cardMore'), - 'click .js-attach': Popup.open('cardAttachments') -}); - Template.WindowAttachmentsModule.events({ 'click .js-attach': Popup.open('cardAttachments'), 'click .js-confirm-delete': Popup.afterConfirm('attachmentDelete', @@ -77,130 +19,6 @@ Template.WindowAttachmentsModule.events({ } }); -Template.cardMembersPopup.events({ - 'click .js-select-member': function(evt) { - var cardId = Template.parentData(2).data._id; - var memberId = this.userId; - var operation; - if (Cards.find({ _id: cardId, members: memberId}).count() === 0) - operation = '$addToSet'; - else - operation = '$pull'; - - var query = {}; - query[operation] = { - members: memberId - }; - Cards.update(cardId, query); - evt.preventDefault(); - } -}); - -Template.cardLabelsPopup.events({ - 'click .js-select-label': function(evt) { - var cardId = Template.parentData(2).data._id; - var labelId = this._id; - var operation; - if (Cards.find({ _id: cardId, labelIds: labelId}).count() === 0) - operation = '$addToSet'; - else - operation = '$pull'; - - var query = {}; - query[operation] = { - labelIds: labelId - }; - Cards.update(cardId, query); - evt.preventDefault(); - }, - 'click .js-edit-label': Popup.open('editLabel'), - 'click .js-add-label': Popup.open('createLabel') -}); - -Template.formLabel.events({ - 'click .js-palette-color': function(evt) { - var $this = $(evt.currentTarget); - - // hide selected ll colors - $('.js-palette-select').addClass('hide'); - - // show select color - $this.find('.js-palette-select').removeClass('hide'); - } -}); - -Template.createLabelPopup.events({ - // Create the new label - 'submit .create-label': function(evt, tpl) { - var name = tpl.$('#labelName').val().trim(); - var boardId = Session.get('currentBoard'); - var selectLabelDom = tpl.$('.js-palette-select').get(0); - var selectLabel = Blaze.getData(selectLabelDom); - Boards.update(boardId, { - $push: { - labels: { - _id: Random.id(6), - name: name, - color: selectLabel.color - } - } - }); - Popup.back(); - evt.preventDefault(); - } -}); - -Template.editLabelPopup.events({ - 'click .js-delete-label': Popup.afterConfirm('deleteLabel', function() { - var boardId = Session.get('currentBoard'); - Boards.update(boardId, { - $pull: { - labels: { - _id: this._id - } - } - }); - Popup.back(2); - }), - 'submit .edit-label': function(evt, tpl) { - var name = tpl.$('#labelName').val().trim(); - var boardId = Session.get('currentBoard'); - var getLabel = Utils.getLabelIndex(boardId, this._id); - var selectLabelDom = tpl.$('.js-palette-select').get(0); - var selectLabel = Blaze.getData(selectLabelDom); - var $set = {}; - - // set label index - $set[getLabel.key('name')] = name; - - // set color - $set[getLabel.key('color')] = selectLabel.color; - - // update - Boards.update(boardId, { $set: $set }); - - // return to the previous popup view trigger - Popup.back(); - - evt.preventDefault(); - }, - 'click .js-select-label': function() { - Cards.remove(this.cardId); - - // redirect board - Utils.goBoardId(this.boardId); - } -}); - -Template.cardMorePopup.events({ - 'click .js-delete': Popup.afterConfirm('cardDelete', function() { - Cards.remove(this.card._id); - - // redirect board - Utils.goBoardId(this.card.board()._id); - }) -}); - Template.cardAttachmentsPopup.events({ 'change .js-attach-file': function(evt) { var card = this.card; diff --git a/client/components/cards/helpers.js b/client/components/cards/helpers.js deleted file mode 100644 index ce85002d..00000000 --- a/client/components/cards/helpers.js +++ /dev/null @@ -1,48 +0,0 @@ -Template.cardMembersPopup.helpers({ - isCardMember: function() { - var cardId = Template.parentData()._id; - var cardMembers = Cards.findOne(cardId).members || []; - return _.contains(cardMembers, this.userId); - }, - user: function() { - return Users.findOne(this.userId); - } -}); - -Template.cardLabelsPopup.helpers({ - isLabelSelected: function(cardId) { - return _.contains(Cards.findOne(cardId).labelIds, this._id); - } -}); - -var labelColors; -Meteor.startup(function() { - labelColors = Boards.simpleSchema()._schema['labels.$.color'].allowedValues; -}); - -Template.createLabelPopup.helpers({ - // This is the default color for a new label. We search the first color that - // is not already used in the board (although it's not a problem if two - // labels have the same color). - defaultColor: function() { - var labels = this.labels || this.card.board().labels; - var usedColors = _.pluck(labels, 'color'); - var availableColors = _.difference(labelColors, usedColors); - return availableColors.length > 1 ? availableColors[0] : labelColors[0]; - } -}); - -Template.formLabel.helpers({ - labels: function() { - return _.map(labelColors, function(color) { - return { color: color, name: '' }; - }); - } -}); - -Blaze.registerHelper('currentCard', function() { - var cardId = Session.get('currentCard'); - if (cardId) { - return Cards.findOne(cardId); - } -}); diff --git a/client/components/cards/labels.jade b/client/components/cards/labels.jade new file mode 100644 index 00000000..acd1b5ae --- /dev/null +++ b/client/components/cards/labels.jade @@ -0,0 +1,27 @@ +template(name="formLabel") + .colors + label(for="labelName") {{_ 'name'}} + input.js-label-name#labelName(type="text" name="name" value=name autofocus) + + label {{_ "select-color"}} + each labels + span.card-label.card-label--selectable.palette-color.js-palette-color(class="card-label-{{color}}") + if($eq color ../color) + i.fa.fa-check + +template(name="createLabelPopup") + form.create-label + with(color=defaultColor) + +formLabel + button.primary.wide(type="submit") {{_ 'create'}} + +template(name="editLabelPopup") + form.edit-label + +formLabel + button.primary.wide.left(type="submit") {{_ 'save'}} + span.right + + +template(name="deleteLabelPopup") + p {{_ "label-delete-pop"}} + button.js-confirm.negate.full(type="submit") {{_ 'delete'}} diff --git a/client/components/cards/labels.js b/client/components/cards/labels.js new file mode 100644 index 00000000..36a35f82 --- /dev/null +++ b/client/components/cards/labels.js @@ -0,0 +1,126 @@ +Template.cardLabelsPopup.events({ + 'click .js-select-label': function(evt) { + var cardId = Template.parentData(2).data._id; + var labelId = this._id; + var operation; + if (Cards.find({ _id: cardId, labelIds: labelId}).count() === 0) + operation = '$addToSet'; + else + operation = '$pull'; + + var query = {}; + query[operation] = { + labelIds: labelId + }; + Cards.update(cardId, query); + evt.preventDefault(); + }, + 'click .js-edit-label': Popup.open('editLabel'), + 'click .js-add-label': Popup.open('createLabel') +}); + +Template.formLabel.events({ + 'click .js-palette-color': function(evt) { + var $this = $(evt.currentTarget); + + // hide selected ll colors + $('.js-palette-select').addClass('hide'); + + // show select color + $this.find('.js-palette-select').removeClass('hide'); + } +}); + +Template.createLabelPopup.events({ + // Create the new label + 'submit .create-label': function(evt, tpl) { + var name = tpl.$('#labelName').val().trim(); + var boardId = Session.get('currentBoard'); + var selectLabelDom = tpl.$('.js-palette-select').get(0); + var selectLabel = Blaze.getData(selectLabelDom); + Boards.update(boardId, { + $push: { + labels: { + _id: Random.id(6), + name: name, + color: selectLabel.color + } + } + }); + Popup.back(); + evt.preventDefault(); + } +}); + +Template.editLabelPopup.events({ + 'click .js-delete-label': Popup.afterConfirm('deleteLabel', function() { + var boardId = Session.get('currentBoard'); + Boards.update(boardId, { + $pull: { + labels: { + _id: this._id + } + } + }); + Popup.back(2); + }), + 'submit .edit-label': function(evt, tpl) { + var name = tpl.$('#labelName').val().trim(); + var boardId = Session.get('currentBoard'); + var getLabel = Utils.getLabelIndex(boardId, this._id); + var selectLabelDom = tpl.$('.js-palette-select').get(0); + var selectLabel = Blaze.getData(selectLabelDom); + var $set = {}; + + // set label index + $set[getLabel.key('name')] = name; + + // set color + $set[getLabel.key('color')] = selectLabel.color; + + // update + Boards.update(boardId, { $set: $set }); + + // return to the previous popup view trigger + Popup.back(); + + evt.preventDefault(); + }, + 'click .js-select-label': function() { + Cards.remove(this.cardId); + + // redirect board + Utils.goBoardId(this.boardId); + } +}); + +Template.cardLabelsPopup.helpers({ + isLabelSelected: function(cardId) { + return _.contains(Cards.findOne(cardId).labelIds, this._id); + } +}); + +var labelColors; +Meteor.startup(function() { + labelColors = Boards.simpleSchema()._schema['labels.$.color'].allowedValues; +}); + +Template.createLabelPopup.helpers({ + // This is the default color for a new label. We search the first color that + // is not already used in the board (although it's not a problem if two + // labels have the same color). + defaultColor: function() { + var labels = this.labels || this.card.board().labels; + var usedColors = _.pluck(labels, 'color'); + var availableColors = _.difference(labelColors, usedColors); + return availableColors.length > 1 ? availableColors[0] : labelColors[0]; + } +}); + +Template.formLabel.helpers({ + labels: function() { + return _.map(labelColors, function(color) { + return { color: color, name: '' }; + }); + } +}); diff --git a/client/components/cards/labels.styl b/client/components/cards/labels.styl index 39f9844f..cbe4d2b3 100644 --- a/client/components/cards/labels.styl +++ b/client/components/cards/labels.styl @@ -85,10 +85,6 @@ left: 0 width: 260px -.editable-labels .card-label:hover - cursor: pointer - opacity: .75 - .edit-labels-pop-over margin-bottom: 8px @@ -98,7 +94,9 @@ .card-label-selectable border-radius: 3px cursor: pointer - margin: 0 50px 4px 0 + margin: 0 + margin-bottom: 3px + width: 190px min-height: 18px padding: 8px position: relative @@ -113,21 +111,13 @@ &.active, &.active.selected:hover, &.active.selected - margin-right: 38px padding-right: 32px .card-label-selectable-icon right: 6px - &.active:hover:hover, - &.active:hover, - &.active.selected:hover:hover, - &.active.selected:hover - margin-right: 38px - &.selected, &:hover - margin-right: 38px opacity: .8 .active .card-label-selectable diff --git a/client/components/cards/minicard.jade b/client/components/cards/minicard.jade index 670b1f89..2eee3164 100644 --- a/client/components/cards/minicard.jade +++ b/client/components/cards/minicard.jade @@ -15,7 +15,7 @@ template(name="minicard") if comments.count .badge(title="{{_ 'card-comments-title' comments.count }}") span.badge-icon.fa.fa-comment-o - .badge-text= comments.count + span.badge-text= comments.count if description .badge.badge-state-image-only(title=description) span.badge-icon.fa.fa-align-left diff --git a/client/components/cards/minicard.styl b/client/components/cards/minicard.styl index ebad8dec..c854b1f5 100644 --- a/client/components/cards/minicard.styl +++ b/client/components/cards/minicard.styl @@ -78,20 +78,34 @@ margin-bottom: 2px text-decoration: none word-wrap: break-word - clear: both &::selection background: transparent .minicard-labels float: right + display: flex .minicard-label - float: right width: 11px height: @width border-radius: 2px - margin-right: 3px + margin-left: 3px + + .badges + float: left + margin-top: 5px + color: darken(white, 60%) + + &:empty + display: none + + .badge + float: left + margin-right: 10px + + .badge-text + font-size: 0.9em .minicard-members float: right @@ -109,12 +123,6 @@ .minicard-members:empty display: none - .badges - float: left - - &:empty - display: none - &.minicard-composer margin-bottom: 10px diff --git a/client/components/cards/templates.html b/client/components/cards/templates.html index dfa21ace..f61c3df5 100644 --- a/client/components/cards/templates.html +++ b/client/components/cards/templates.html @@ -1,34 +1,3 @@ - - - - - - - - - - - - - - diff --git a/client/components/forms/forms.styl b/client/components/forms/forms.styl index a71aee81..06ae1302 100644 --- a/client/components/forms/forms.styl +++ b/client/components/forms/forms.styl @@ -95,6 +95,11 @@ textarea resize: vertical width: 100% + &.editor + resize: none + padding-bottom: 22px + + .button border-radius: 3px text-decoration: none diff --git a/client/components/main/editor.styl b/client/components/main/editor.styl deleted file mode 100644 index 1dc02047..00000000 --- a/client/components/main/editor.styl +++ /dev/null @@ -1,2 +0,0 @@ -textarea.editor - min-height: 100px diff --git a/client/components/main/popup.js b/client/components/main/popup.js index 8cb12dd0..5fc4e979 100644 --- a/client/components/main/popup.js +++ b/client/components/main/popup.js @@ -1,22 +1,3 @@ -// XXX This event list must be abstracted somewhere else. -function whichTransitionEvent() { - var t; - var el = document.createElement('fakeelement'); - var transitions = { - transition:'transitionend', - OTransition:'oTransitionEnd', - MozTransition:'transitionend', - WebkitTransition:'webkitTransitionEnd' - }; - - for (t in transitions) { - if (el.style[t] !== undefined) { - return transitions[t]; - } - } -} -var transitionEvent = whichTransitionEvent(); - Popup.template.events({ 'click .js-back-view': function() { Popup.back(); @@ -50,7 +31,7 @@ Popup.template.onRendered(function() { container._uihooks = { removeElement: function(node) { $(node).addClass('no-height'); - $(container).one(transitionEvent, function() { + $(container).one(CSSEvents.transitionend, function() { node.parentNode.removeChild(node); }); } diff --git a/client/components/users/userAvatar.jade b/client/components/users/userAvatar.jade index 4c0e5c63..2ea3aa20 100644 --- a/client/components/users/userAvatar.jade +++ b/client/components/users/userAvatar.jade @@ -19,14 +19,17 @@ template(name="userPopup") +userAvatar(userId=user._id) .info h3.bottom - a.js-profile(href="{{pathFor route='Profile' username=user.username}}")= user.profile.name + = user.profile.fullname p.quiet.bottom @{{ user.username }} template(name="memberName") - a.js-show-mem-menu(href="{{pathFor route='Profile' username=user.username}}") + if showBoth = user.profile.fullname - if username - | ({{ user.username }}) + | ({{ user.username }}) + else if user.profile.fullname + = user.profile.fullname + else + = user.username template(name="changeAvatarPopup") ul.pop-over-list @@ -54,3 +57,14 @@ template(name="changeAvatarPopup") button.full.js-upload-avatar i.fa.fa-upload | Upload an avatar + +template(name="cardMemberPopup") + .board-member-menu + .mini-profile-info + +userAvatar(userId=user._id) + .info + h3.bottom= user.profile.fullname + p.quiet.bottom @{{ user.username }} + if currentUser.isBoardMember + ul.pop-over-list + li: a.js-remove-member {{_ 'remove-member-from-card'}} diff --git a/client/components/users/userAvatar.js b/client/components/users/userAvatar.js index a02646c1..64975141 100644 --- a/client/components/users/userAvatar.js +++ b/client/components/users/userAvatar.js @@ -111,3 +111,40 @@ BlazeComponent.extendComponent({ }]; } }).register('changeAvatarPopup'); + +Template.cardMembersPopup.helpers({ + isCardMember: function() { + var cardId = Template.parentData()._id; + var cardMembers = Cards.findOne(cardId).members || []; + return _.contains(cardMembers, this.userId); + }, + user: function() { + return Users.findOne(this.userId); + } +}); + +Template.cardMembersPopup.events({ + 'click .js-select-member': function(evt) { + var cardId = Template.parentData(2).data._id; + var memberId = this.userId; + var operation; + if (Cards.find({ _id: cardId, members: memberId}).count() === 0) + operation = '$addToSet'; + else + operation = '$pull'; + + var query = {}; + query[operation] = { + members: memberId + }; + Cards.update(cardId, query); + evt.preventDefault(); + } +}); + +Template.cardMemberPopup.events({ + 'click .js-remove-member': function() { + Cards.update(this.cardId, {$pull: {members: this.userId}}); + Popup.close(); + } +}); diff --git a/client/config/helpers.js b/client/config/helpers.js new file mode 100644 index 00000000..01a28f9b --- /dev/null +++ b/client/config/helpers.js @@ -0,0 +1,6 @@ +Blaze.registerHelper('currentCard', function() { + var cardId = Session.get('currentCard'); + if (cardId) { + return Cards.findOne(cardId); + } +}); diff --git a/client/lib/cssEvents.js b/client/lib/cssEvents.js new file mode 100644 index 00000000..487ba69b --- /dev/null +++ b/client/lib/cssEvents.js @@ -0,0 +1,42 @@ +// XXX Should we use something like Moderniz instead of our custom detector? + +var whichTransitionEvent = function() { + var t; + var el = document.createElement('fakeelement'); + var transitions = { + transition:'transitionend', + OTransition:'oTransitionEnd', + MSTransition:'msTransitionEnd', + MozTransition:'transitionend', + WebkitTransition:'webkitTransitionEnd' + }; + + for (t in transitions) { + if (el.style[t] !== undefined) { + return transitions[t]; + } + } +}; + +var whichAnimationEvent = function() { + var t; + var el = document.createElement('fakeelement'); + var transitions = { + animation:'animationend', + OAnimation:'oAnimationEnd', + MSTransition:'msAnimationEnd', + MozAnimation:'animationend', + WebkitAnimation:'webkitAnimationEnd' + }; + + for (t in transitions) { + if (el.style[t] !== undefined) { + return transitions[t]; + } + } +}; + +CSSEvents = { + transitionend: whichTransitionEvent(), + animationend: whichAnimationEvent() +}; diff --git a/client/styles/main.styl b/client/styles/main.styl index 3509d5c9..c5d169d3 100644 --- a/client/styles/main.styl +++ b/client/styles/main.styl @@ -77,6 +77,9 @@ a cursor: default text-decoration: none +strong + font-weight: bold + p a text-decoration: underline -- cgit v1.2.3-1-g7c22