diff options
Diffstat (limited to 'client/components/sidebar')
-rw-r--r-- | client/components/sidebar/events.js | 93 | ||||
-rw-r--r-- | client/components/sidebar/helpers.js | 51 | ||||
-rw-r--r-- | client/components/sidebar/infiniteScrolling.js | 37 | ||||
-rw-r--r-- | client/components/sidebar/rendered.js | 21 | ||||
-rw-r--r-- | client/components/sidebar/sidebar.js | 55 | ||||
-rw-r--r-- | client/components/sidebar/sidebar.styl | 154 | ||||
-rw-r--r-- | client/components/sidebar/templates.html.old | 307 | ||||
-rw-r--r-- | client/components/sidebar/templates.jade | 103 |
8 files changed, 821 insertions, 0 deletions
diff --git a/client/components/sidebar/events.js b/client/components/sidebar/events.js new file mode 100644 index 00000000..1067421f --- /dev/null +++ b/client/components/sidebar/events.js @@ -0,0 +1,93 @@ +Template.filterSidebar.events({ + 'click .js-toggle-label-filter': function(event) { + Filter.labelIds.toogle(this._id); + Filter.resetExceptions(); + event.preventDefault(); + }, + 'click .js-toogle-member-filter': function(event) { + Filter.members.toogle(this._id); + Filter.resetExceptions(); + event.preventDefault(); + }, + 'click .js-clear-all': function(event) { + Filter.reset(); + event.preventDefault(); + } +}); + +var getMemberIndex = function(board, searchId) { + for (var i = 0; i < board.members.length; i++) { + if (board.members[i].userId === searchId) + return i; + } + throw new Meteor.Error('Member not found'); +}; + +Template.memberPopup.events({ + 'click .js-filter-member': function() { + Filter.members.toogle(this.userId); + Popup.close(); + }, + 'click .js-change-role': Popup.open('changePermissions'), + 'click .js-remove-member': Popup.afterConfirm('removeMember', function() { + var currentBoard = Boards.findOne(Session.get('currentBoard')); + var memberIndex = getMemberIndex(currentBoard, this.userId); + var setQuery = {}; + setQuery[['members', memberIndex, 'isActive'].join('.')] = false; + Boards.update(currentBoard._id, { $set: setQuery }); + Popup.close(); + }), + 'click .js-leave-member': function() { + // @TODO + Popup.close(); + } +}); + +Template.membersWidget.events({ + 'click .js-open-manage-board-members': Popup.open('addMember'), + 'click .member': Popup.open('member') +}); + +Template.labelsWidget.events({ + 'click .js-label': Popup.open('editLabel'), + 'click .js-add-label': Popup.open('createLabel') +}); + +// Template.addMemberPopup.events({ +// 'click .pop-over-member-list li:not(.disabled)': function(event, t) { +// var userId = this._id; +// var boardId = t.data.board._id; +// var currentMembersIds = _.pluck(t.data.board.members, 'userId'); +// if (currentMembersIds.indexOf(userId) === -1) { +// Boards.update(boardId, { +// $push: { +// members: { +// userId: userId, +// isAdmin: false, +// isActive: true +// } +// } +// }); +// } else { +// var memberIndex = getMemberIndex(t.data.board, userId); +// var setQuery = {}; +// setQuery[['members', memberIndex, 'isActive'].join('.')] = true; +// Boards.update(boardId, { $set: setQuery }); +// } +// Popup.close(); +// } +// }); + +// Template.changePermissionsPopup.events({ +// 'click .js-set-admin, click .js-set-normal': function(event) { +// var currentBoard = Boards.findOne(Session.get('currentBoard')); +// var memberIndex = getMemberIndex(currentBoard, this.user._id); +// var isAdmin = $(event.currentTarget).hasClass('js-set-admin'); +// var setQuery = {}; +// setQuery[['members', memberIndex, 'isAdmin'].join('.')] = isAdmin; +// Boards.update(currentBoard._id, { +// $set: setQuery +// }); +// Popup.back(1); +// } +// }); diff --git a/client/components/sidebar/helpers.js b/client/components/sidebar/helpers.js new file mode 100644 index 00000000..a76dad7f --- /dev/null +++ b/client/components/sidebar/helpers.js @@ -0,0 +1,51 @@ +var widgetTitles = { + filter: 'filter-cards', + background: 'change-background' +}; + +Template.boardSidebar.helpers({ + currentWidget: function() { + return Session.get('currentWidget') + 'Sidebar'; + }, + currentWidgetTitle: function() { + return TAPi18n.__(widgetTitles[Session.get('currentWidget')]); + } +}); + +// Template.addMemberPopup.helpers({ +// isBoardMember: function() { +// var user = Users.findOne(this._id); +// return user && user.isBoardMember(); +// } +// }); + +Template.memberPopup.helpers({ + user: function() { + return Users.findOne(this.userId); + }, + memberType: function() { + var type = Users.findOne(this.userId).isBoardAdmin() ? 'admin' : 'normal'; + return TAPi18n.__(type).toLowerCase(); + } +}); + +// Template.removeMemberPopup.helpers({ +// user: function() { +// return Users.findOne(this.userId) +// }, +// board: function() { +// return currentBoard(); +// } +// }); + +// Template.changePermissionsPopup.helpers({ +// isAdmin: function() { +// return this.user.isBoardAdmin(); +// }, +// isLastAdmin: function() { +// if (! this.user.isBoardAdmin()) +// return false; +// var nbAdmins = _.where(currentBoard().members, { isAdmin: true }).length; +// return nbAdmins === 1; +// } +// }); diff --git a/client/components/sidebar/infiniteScrolling.js b/client/components/sidebar/infiniteScrolling.js new file mode 100644 index 00000000..df3b8901 --- /dev/null +++ b/client/components/sidebar/infiniteScrolling.js @@ -0,0 +1,37 @@ +var peakAnticipation = 200; + +Mixins.InfiniteScrolling = BlazeComponent.extendComponent({ + onCreated: function() { + this._nextPeak = Infinity; + }, + + setNextPeak: function(v) { + this._nextPeak = v; + }, + + getNextPeak: function() { + return this._nextPeak; + }, + + resetNextPeak: function() { + this._nextPeak = Infinity; + }, + + // To be overwritten by consumers of this mixin + reachNextPeak: function() { + + }, + + events: function() { + return [{ + scroll: function(evt) { + var domElement = evt.currentTarget; + var altitude = domElement.scrollTop + domElement.offsetHeight; + altitude += peakAnticipation; + if (altitude >= this.callFirstWith(null, 'getNextPeak')) { + this.callFirstWith(null, 'reachNextPeak'); + } + } + }]; + } +}); diff --git a/client/components/sidebar/rendered.js b/client/components/sidebar/rendered.js new file mode 100644 index 00000000..2b58bf33 --- /dev/null +++ b/client/components/sidebar/rendered.js @@ -0,0 +1,21 @@ +Template.membersWidget.rendered = function() { + if (! Meteor.user().isBoardMember()) + return; + + _.each(['.js-member', '.js-label'], function(className) { + Utils.liveEvent('mouseover', function($this) { + $this.find(className).draggable({ + appendTo: 'body', + helper: 'clone', + revert: 'invalid', + revertDuration: 150, + snap: false, + snapMode: 'both', + start: function() { + Popup.close(); + } + }); + }); + }); +}; + diff --git a/client/components/sidebar/sidebar.js b/client/components/sidebar/sidebar.js new file mode 100644 index 00000000..3f0142d4 --- /dev/null +++ b/client/components/sidebar/sidebar.js @@ -0,0 +1,55 @@ +BlazeComponent.extendComponent({ + template: function() { + return 'boardSidebar'; + }, + + mixins: function() { + return [Mixins.InfiniteScrolling]; + }, + + onCreated: function() { + this._isOpen = new ReactiveVar(true); + }, + + isOpen: function() { + return this._isOpen.get(); + }, + + open: function() { + if (! this._isOpen.get()) { + this._isOpen.set(true); + } + }, + + hide: function() { + if (this._isOpen.get()) { + this._isOpen.set(false); + } + }, + + toogle: function() { + this._isOpen.set(! this._isOpen.get()); + }, + + calculateNextPeak: function() { + var altitude = this.find('.js-board-sidebar-content').scrollHeight; + this.callFirstWith(this, 'setNextPeak', altitude); + }, + + reachNextPeak: function() { + var activitiesComponent = this.componentChildren('activities')[0]; + activitiesComponent.loadNextPage(); + }, + + isTongueHidden: function() { + return this.isOpen() && Filter.isActive(); + }, + + events: function() { + // XXX Hacky, we need some kind of `super` + var mixinEvents = this.getMixin(Mixins.InfiniteScrolling).events(); + return mixinEvents.concat([{ + 'click .js-toogle-sidebar': this.toogle + }]); + } +}).register('boardSidebar'); diff --git a/client/components/sidebar/sidebar.styl b/client/components/sidebar/sidebar.styl new file mode 100644 index 00000000..4b741dc7 --- /dev/null +++ b/client/components/sidebar/sidebar.styl @@ -0,0 +1,154 @@ +@import 'nib' + +.sidebar + .sidebar-content + padding: 10px 20px + background: white + box-shadow: -10px 0px 5px -10px darken(white, 30%) + z-index: 10 + position: absolute + top: 0 + bottom: 0 + right: 0 + left: 0 + overflow-x: hidden + overflow-y: auto + + h3 + color: darken(white, 50%) + + hr + margin: 8px 0 + +.board-sidebar + width: 248px + position: absolute + top: 0 + right: -@width + bottom: 0 + transition: top .1s, right .1s, width .1s + + &.is-open + right: 0 + +.board-widget-nav + border-radius: 3px + background: #dcdcdc + overflow: hidden + padding: 0 + position: relative + + .toggle-widget-nav + border-radius: 3px + color: #8c8c8c + margin: 0 + padding: 7px 10px + position: relative + cursor: pointer + + .toggle-menu-icon + position: absolute + top: 8px + right: 8px + + &:hover + background: #ccc + color: #4d4d4d + + .nav-list + display: block + opacity: 1 + max-height: 400px + + hr + margin: 2px 0 + color: #ccc + background: #ccc + + .nav-list-item + display: block + font-weight: 700 + line-height: 30px + overflow: hidden + padding: 0 8px 0 36px + position: relative + text-decoration: none + + .icon-type + left: 10px + position: absolute + top: 6px + + &:hover + background: #ccc + + .icon-type + color: #686868 + + .nav-list-sub-item + font-weight: 400 + color: #666 + + &:hover + color: #4d4d4d + + &.collapsed + + .nav-list + max-height: 0 + opacity: 0 + + hr + margin: 0 + + .toggle-widget-nav + color: #4d4d4d + + +.board-widget-title + display: block + min-height: 20px + margin-bottom: 6px + +.board-widget-content + position: relative + z-index: 1 + +.board-widget h4 + margin: 5px 0 + +.board-widget-activity + margin-right: -4px + +.sidebar-tongue + display: block + width: 30px + height: @width + left: -@width + position: absolute + top: 12px + z-index: 15 + background: white + border-radius: left 3px + box-shadow: -4px 0px 7px -4px darken(white, 30%) + color: darken(white, 50%) + transition: left .1s + + i.fa + margin: 9px + transition: transform 0.5s + + .board-sidebar.is-open & + left: -@width + 2px + + // XXX Bug: we should add a padding left + &:hover + left: -@width + 5px + + i.fa + transform: rotate(180deg) + + &.is-hidden, + .board-sidebar.is-open &.is-hidden + z-index: 0 + left: 5px diff --git a/client/components/sidebar/templates.html.old b/client/components/sidebar/templates.html.old new file mode 100644 index 00000000..d8b063f0 --- /dev/null +++ b/client/components/sidebar/templates.html.old @@ -0,0 +1,307 @@ +<template name="boardWidgets"> + <a href="#" class="sidebar-show-btn dark-hover js-show-sidebar"> + <span class="icon-sm fa fa-chevron-left"></span> + <span class="text">{{_ 'show-sidebar'}}</span> + </a> + <div class="board-widgets {{#if session 'sidebarIsOpen'}}show{{else}}hide{{/if}}"> + <div> + <a href="#" class="sidebar-hide-btn dark-hover js-hide-sidebar" title="{{_ 'close-sidebar-title'}}"> + <span class="icon-sm fa fa-chevron-right"></span> + </a> + {{#unless isTrue currentWidget "homeWidget"}} + <div class="board-widgets-title clearfix"> + <a href="#" class="board-sidebar-back-btn js-pop-widget-view"> + <span class="left-arrow"></span>{{_ 'back'}} + </a> + <h3 class="text">{{currentWidgetTitle}}</h3> + <hr> + </div> + {{/unless}} + <div class="board-widgets-content-wrapper"> + <div class="board-widgets-content default fancy-scrollbar short{{#unless session 'menuWidgetIsOpen'}} short{{/unless}}"> + {{> UI.dynamic template=currentWidget data=this }} + </div> + </div> + </div> + </div> +</template> + +<template name="homeWidget"> +{{ > menuWidget }} +{{ > membersWidget }} +{{ > activityWidget }} +</template> + +<template name="menuWidget"> + <div class="board-widget board-widget-nav clearfix{{#unless session 'menuWidgetIsOpen'}} collapsed{{/unless}}"> + <h3 class="dark-hover toggle-widget-nav js-toggle-widget-nav">{{_ 'menu'}} + <span class="icon-sm fa fa-chevron-circle-down toggle-menu-icon"></span> + </h3> + <ul class="nav-list"> + <hr style="margin-top: 0;"> + <li> + <a href="#" class="nav-list-item js-open-archive"> + <span class="icon-sm fa fa-archive icon-type"></span> + {{_ 'archived-items'}} + </a> + </li> + <li> + <a href="#" class="nav-list-item js-open-card-filter"> + <span class="icon-sm fa fa-filter icon-type"></span> + {{_ 'filter-cards'}} + </a> + </li> + {{#if currentUser.isBoardAdmin}} + <hr> + <li> + <a class="nav-list-item nav-list-sub-item board-settings-background js-change-background"> + <span class="board-settings-background-preview" style="background-color:{{board.background.color}}"></span> + {{_ 'change-background'}}… + </a> + </li> + {{#unless isSandstorm }} + <li> + <a class="nav-list-item nav-list-sub-item js-close-board" href="#">{{_ 'close-board'}}</a> + </li> + {{/unless}} + {{/if}} + {{! + XXX Language should be handled by sandstorm, but for now display a language selection link in the board menu. + This link is normally present in the header bar that is not displayed on sandstorm. + }} + {{#if isSandstorm}} + <hr> + <li> + <a class="nav-list-item nav-list-sub-item js-language">{{_ 'language'}}</a> + </li> + {{/if}} + </ul> + </div> +</template> + +<template name="membersWidget"> + <hr> + <div class="board-widget board-widget-members clearfix"> + <div class="board-widget-title"> + <h3>{{_ 'members'}}</h3> + </div> + <div class="board-widget-content"> + <div class="board-widget-members js-list-board-members clearfix js-list-draggable-board-members"> + {{# each board.members }} + {{> userAvatar userId=this.userId draggable=true size="small" showBadges=true}} + {{/ each }} + </div> + {{# unless isSandstrom }} + {{# if currentUser.isBoardAdmin }} + <a href="#" class="button-link js-open-manage-board-members"> + <span class="icon-sm fa fa-user"></span> {{_ 'add-members'}} + </a> + {{/ if }} + {{/ unless }} + </div> + </div> +</template> + +<template name="activityWidget"> + {{# if board.activities.count }} + <hr> + <div class="board-widget board-widget-activity bottom clearfix"> + <div class="board-widget-title"> + <h3>{{_ 'activity'}}</h3> + </div> + <div class="board-widget-content"> + <div class="activity-gradient-t"></div> + <div class="activity-gradient-b"></div> + <div class="board-actions-list fancy-scrollbar"> + {{ > activities }} + </div> + </div> + </div> + {{/if}} +</template> + +<template name="memberPopup"> + <div class="board-member-menu"> + <div class="mini-profile-info"> + {{> userAvatar user=user}} + <div class="info"> + <h3 class="bottom" style="margin-right: 40px;"> + <a class="js-profile" href="{{ pathFor route='Profile' username=user.username }}">{{ user.profile.name }}</a> + </h3> + <p class="quiet bottom">@{{ user.username }}</p> + </div> + </div> + {{# if currentUser.isBoardMember }} + <ul class="pop-over-list"> + {{# if currentUser.isBoardAdmin }} + <li> + <a class="js-change-role" href="#"> + {{_ 'change-permissions'}} <span class="quiet" style="font-weight: normal;">({{ memberType }})</span> + </a> + </li> + {{/ if }} + + <li> + {{# if currentUser.isBoardAdmin }} + <a class="js-remove-member">{{_ 'remove-from-board'}}</a> + {{ else }} + <a class="js-leave-member">{{_ 'leave-board'}}</a> + {{/ if }} + </li> + </ul> + {{/ if }} + </div> +</template> + +<template name="filterWidget"> + <ul class="pop-over-label-list checkable"> + {{#each board.labels}} + <li class="item matches-filter"> + <a class="name js-toggle-label-filter"> + <span class="card-label card-label-{{color}}"></span> + <span class="full-name"> + {{#if name}} + {{name}} + {{else}} + <span class="quiet">{{_ "label-default" color}}</span> + {{/if}} + </span> + {{#if Filter.labelIds.isSelected _id}} + <span class="icon-sm fa fa-check"></span> + {{/if}} + </a> + </li> + {{/each}} + </ul> + <hr> + <ul class="pop-over-member-list checkable"> + {{#each board.members}} + {{#with getUser userId}} + <li class="item js-member-item {{#if Filter.members.isSelected _id}}active{{/if}}"> + <a href="#" class="name js-toogle-member-filter"> + {{> userAvatar user=this size="small" }} + <span class="full-name"> + {{ profile.name }} + (<span class="username">{{ username }}</span>) + </span> + {{#if Filter.members.isSelected _id}} + <span class="icon-sm fa fa-check checked-icon"></span> + {{/if}} + </a> + </li> + {{/with}} + {{/each}} + </ul> + <hr> + <ul class="pop-over-list inset normal-weight"> + <li> + <a class="js-clear-all {{#unless Filter.isActive}}disabled{{/unless}}" style="padding-left: 40px;"> + {{_ 'filter-clear'}} + </a> + </li> + </ul> +</template> + +<template name="backgroundWidget"> + <div class="board-widgets-content-wrapper fancy-scrollbar"> + <div class="board-widgets-content"> + <div class="board-backgrounds-list clearfix"> + {{#each backgroundColors}} + <div class="board-background-select js-select-background"> + <span class="background-box " style="background-color: {{this}}; "></span> + </div> + {{/each}} + </div> + {{!-- + <h2 class="clear">Photos</h2> + <div class="board-backgrounds-list relative clearfix js-gold-photos-list disabled"> + <div class="board-background-select js-select-background"> + <span class="background-box " style="background-image: url("{{url}}");"> + <a class="background-option js-background-attribution" href={{href}} target="_blank" title={{title}}> + <img src="https://d78fikflryjgj.cloudfront.net/images/d906fe5c1274c56c5571d49705547587/cc.png" style="height: 14px; width: 14px; vertical-align: text-top;" title="http://creativecommons.org/licenses/by/2.0/deed.en"> + <span class="text" style="margin-left: 2px;">{{author}}</span> + </a> + </span> + </div> + </div> + --}} + </div> + </div> +</template> + +<template name="closeBoardPopup"> + <p>{{_ 'close-board-pop'}}</p> + <input type="submit" class="js-confirm negate full" value="{{_ 'close'}}"> +</template> + +<template name="removeMemberPopup"> + <p>{{_ 'remove-member-pop' + name=user.profile.name + username=user.username + boardTitle=board.title}}</p> + <input type="submit" class="js-confirm negate full" value="{{_ 'remove-member'}}"> +</template> + +<template name="addMemberPopup"> + <div class="search-with-spinner"> + {{> esInput index="users" }} + </div> + + <div class="manage-member-section hide js-search-results" style="display: block;"> + <ul class="pop-over-member-list options js-list"> + {{# esEach index="users"}} + <li class="item js-member-item {{# if isBoardMember }}disabled{{/if}}"> + <a href="#" class="name js-select-member {{# if isBoardMember }}multi-line{{/if}}" title="{{ profile.name }} ({{ username }})"> + {{> userAvatar user=this size="small" }} + <span class="full-name"> + {{ profile.name }} (<span class="username">{{ username }}</span>) + </span> + {{# if isBoardMember }} + <div class="extra-text quiet">({{_ 'joined'}})</div> + {{/if}} + <span class="icon-sm fa fa-chevron-right light option js-open-option"></span> + </a> + </li> + {{/esEach }} + </ul> + </div> + + {{# ifEsIsSearching index='users' }} + <div class="tac"> + <span class="tabbed-pane-main-col-loading-spinner spinner"></span> + </div> + {{ /ifEsIsSearching }} + + {{# ifEsHasNoResults index="users" }} + <div class="manage-member-section js-no-results"> + <p class="quiet center" style="padding: 16px 4px;">{{_ 'no-results'}}</p> + </div> + {{ /ifEsHasNoResults }} + + <div class="manage-member-section js-helper"> + <p class="bottom quiet" style="padding: 6px;">{{_ 'search-member-desc'}}</p> + </div> +</template> + +<template name="changePermissionsPopup"> + <ul class="pop-over-list"> + <li> + <a class="{{#if isLastAdmin}}disabled{{else}}js-set-admin{{/if}}"> + {{_ 'admin'}} + {{#if isAdmin}}<span class="icon-sm fa fa-check"></span>{{/if}} + <span class="sub-name">{{_ 'admin-desc'}}</span> + </a> + </li> + <li> + <a class="{{#if isLastAdmin}}disabled{{else}}js-set-normal{{/if}}"> + {{_ 'normal'}} + {{#unless isAdmin}}<span class="icon-sm fa fa-check"></span>{{/unless}} + <span class="sub-name">{{_ 'normal-desc'}}</span> + </a> + </li> + </ul> + {{#if isLastAdmin}} + <hr> + <p class="quiet bottom">{{_ 'last-admin-desc'}}</p> + {{/if}} +</template> diff --git a/client/components/sidebar/templates.jade b/client/components/sidebar/templates.jade new file mode 100644 index 00000000..23a1a87e --- /dev/null +++ b/client/components/sidebar/templates.jade @@ -0,0 +1,103 @@ +template(name="boardSidebar") + .board-sidebar.sidebar(class="{{#if isOpen}}is-open{{/if}}") + a.sidebar-tongue.js-toogle-sidebar( + class="{{#if isTongueHidden}}is-hidden{{/if}}") + i.fa.fa-chevron-left + .sidebar-content.js-board-sidebar-content + //- XXX https://github.com/peerlibrary/meteor-blaze-components/issues/30 + if Filter.isActive + +filterSidebar + else + +homeSidebar + +template(name='homeSidebar') + +membersWidget + hr.clear + +labelsWidget + hr.clear + h3 + i.fa.fa-comments-o + | {{_ 'activities'}} + +activities(mode="board") + +template(name="filterSidebar") + ul.pop-over-label-list.checkable + each currentBoard.labels + li.item.matches-filter + a.name.js-toggle-label-filter + span.card-label(class="card-label-{{color}}") + span.full-name + if name + = name + else + span.quiet {{_ "label-default" color}} + if Filter.labelIds.isSelected _id}} + span.icon-sm.fa.fa-check + hr + ul.pop-over-member-list.checkable + each currentBoard.members + if isActive + with getUser userId + li.item.js-member-item( + class="{{#if Filter.members.isSelected _id}}active{{/if}}") + a.name.js-toogle-member-filter + +userAvatar(user=this size="small") + span.full-name + = profile.name + | (<span class="username">{{ username }}</span>) + if Filter.members.isSelected _id + span.icon-sm.fa.fa-check + hr + a.js-clear-all(class="{{#unless Filter.isActive}}disabled{{/unless}}") + | {{_ 'filter-clear'}} + +template(name="membersWidget") + .board-widget.board-widget-members + h3 + i.fa.fa-user + | {{_ 'members'}} + .board-widget-content + each currentBoard.members + +userAvatar( + userId=this.userId + draggable=true + size="small" + showBadges=true) + unless isSandstorm + if currentUser.isBoardAdmin + a.js-open-manage-board-members + +template(name="labelsWidget") + .board-widget.board-widget-labels + h3 + i.fa.fa-tags + | {{_ 'labels'}} + .board-widget-content + each currentBoard.labels + a.card-label(class="card-label-{{color}}").js-label + span.card-label-name= name + a.card-label.js-add-label + i.fa.fa-plus + +template(name="memberPopup") + .board-member-menu: .mini-profile-info + +userAvatar(user=user) + .info + h3.bottom + a.js-profile(href="{{pathFor route='Profile' username=user.username}}") + = user.profile.name + p.quiet.bottom @#{user.username} + if currentUser.isBoardMember + ul.pop-over-list + li + a.js-filter-member Filter cards + if currentUser.isBoardAdmin + li + a.js-change-role + | {{_ 'change-permissions'}} + span.quiet (#{memberType}) + li + if currentUser.isBoardAdmin + a.js-remove-member {{_ 'remove-from-board'}} + else + a.js-leave-member {{_ 'leave-board'}} |