diff options
author | Lauri Ojansivu <x@xet7.org> | 2019-10-29 19:05:44 +0200 |
---|---|---|
committer | Lauri Ojansivu <x@xet7.org> | 2019-10-29 19:05:44 +0200 |
commit | 7d6d3af54a2fc1fb68634725eb754b22f02fd430 (patch) | |
tree | 8703d32a5489dfce95e026bc535d6459c1743784 /client | |
parent | 13a2bd6380ced34a828b9469e48786ed21fcb380 (diff) | |
download | wekan-7d6d3af54a2fc1fb68634725eb754b22f02fd430.tar.gz wekan-7d6d3af54a2fc1fb68634725eb754b22f02fd430.tar.bz2 wekan-7d6d3af54a2fc1fb68634725eb754b22f02fd430.zip |
Add Features: allowing lists to be sorted by modifiedAt when not in draggable mode.
Bug Fix #2093: the broken should be prior to file attachment feature introduced, and tested export board is working.
Thanks to whowillcare !
( xet7 merged this pull request manually from https://github.com/wekan/wekan/pull/2756 )
Closes #2093
Diffstat (limited to 'client')
22 files changed, 289 insertions, 30 deletions
diff --git a/client/components/boards/boardBody.js b/client/components/boards/boardBody.js index d64636f4..47042ae7 100644 --- a/client/components/boards/boardBody.js +++ b/client/components/boards/boardBody.js @@ -89,7 +89,7 @@ BlazeComponent.extendComponent({ helper.append(list.clone()); return helper; }, - handle: '.js-swimlane-header', + handle: '.js-swimlane-header-handle', items: '.swimlane:not(.placeholder)', placeholder: 'swimlane placeholder', distance: 7, diff --git a/client/components/boards/boardHeader.jade b/client/components/boards/boardHeader.jade index fe533f95..175cc2c2 100644 --- a/client/components/boards/boardHeader.jade +++ b/client/components/boards/boardHeader.jade @@ -77,6 +77,10 @@ template(name="boardHeaderBar") i.fa.fa-archive span {{_ 'archives'}} + if showSort + a.board-header-btn.js-open-sort-view(title="{{_ 'sort-desc'}}") + i.fa(class="{{directionClass}}") + span {{_ 'sort'}}{{_ listSortShortDesc}} a.board-header-btn.js-open-filter-view( title="{{#if Filter.isActive}}{{_ 'filter-on-desc'}}{{else}}{{_ 'filter'}}{{/if}}" class="{{#if Filter.isActive}}emphasis{{/if}}") @@ -194,6 +198,20 @@ template(name="createBoard") | / a.js-board-template {{_ 'template'}} +template(name="listsortPopup") + h2 + | {{_ 'list-sort-by'}} + hr + ul.pop-over-list + each value in allowedSortValues + li + a.js-sort-by(name="{{value.name}}") + if $eq sortby value.name + i(class="fa {{Direction}}") + | {{_ value.label }}{{_ value.shortLabel}} + if $eq sortby value.name + i(class="fa fa-check") + template(name="boardChangeTitlePopup") form label diff --git a/client/components/boards/boardHeader.js b/client/components/boards/boardHeader.js index cb84c233..e14b1444 100644 --- a/client/components/boards/boardHeader.js +++ b/client/components/boards/boardHeader.js @@ -1,3 +1,5 @@ +const DOWNCLS = 'fa-sort-down'; +const UPCLS = 'fa-sort-up'; Template.boardMenuPopup.events({ 'click .js-rename-board': Popup.open('boardChangeTitle'), 'click .js-custom-fields'() { @@ -80,7 +82,25 @@ BlazeComponent.extendComponent({ const currentBoard = Boards.findOne(Session.get('currentBoard')); return currentBoard && currentBoard.stars >= 2; }, - + showSort() { + return Meteor.user().hasSortBy(); + }, + directionClass() { + return this.currentDirection() === -1 ? DOWNCLS : UPCLS; + }, + changeDirection() { + const direction = 0 - this.currentDirection() === -1 ? '-' : ''; + Meteor.call('setListSortBy', direction + this.currentListSortBy()); + }, + currentDirection() { + return Meteor.user().getListSortByDirection(); + }, + currentListSortBy() { + return Meteor.user().getListSortBy(); + }, + listSortShortDesc() { + return `list-label-short-${this.currentListSortBy()}`; + }, events() { return [ { @@ -118,6 +138,16 @@ BlazeComponent.extendComponent({ 'click .js-open-filter-view'() { Sidebar.setView('filter'); }, + 'click .js-open-sort-view'(evt) { + const target = evt.target; + if (target.tagName === 'I') { + // click on the text, popup choices + this.changeDirection(); + } else { + // change the sort order + Popup.open('listsort')(evt); + } + }, 'click .js-filter-reset'(event) { event.stopPropagation(); Sidebar.setView(); @@ -277,3 +307,73 @@ BlazeComponent.extendComponent({ ]; }, }).register('boardChangeWatchPopup'); + +BlazeComponent.extendComponent({ + onCreated() { + //this.sortBy = new ReactiveVar(); + ////this.sortDirection = new ReactiveVar(); + //this.setSortBy(); + this.downClass = DOWNCLS; + this.upClass = UPCLS; + }, + allowedSortValues() { + const types = []; + const pushed = {}; + Meteor.user() + .getListSortTypes() + .forEach(type => { + const key = type.replace(/^-/, ''); + if (pushed[key] === undefined) { + types.push({ + name: key, + label: `list-label-${key}`, + shortLabel: `list-label-short-${key}`, + }); + pushed[key] = 1; + } + }); + return types; + }, + Direction() { + return Meteor.user().getListSortByDirection() === -1 + ? this.downClass + : this.upClass; + }, + sortby() { + return Meteor.user().getListSortBy(); + }, + + setSortBy(type = null) { + const user = Meteor.user(); + if (type === null) { + type = user._getListSortBy(); + } else { + let value = ''; + if (type.map) { + // is an array + value = (type[1] === -1 ? '-' : '') + type[0]; + } + Meteor.call('setListSortBy', value); + } + //this.sortBy.set(type[0]); + //this.sortDirection.set(type[1]); + }, + + events() { + return [ + { + 'click .js-sort-by'(evt) { + evt.preventDefault(); + const target = evt.target; + const sortby = target.getAttribute('name'); + const down = !!target.querySelector(`.${this.upClass}`); + const direction = down ? -1 : 1; + this.setSortBy([sortby, direction]); + if (Utils.isMiniScreen) { + Popup.close(); + } + }, + }, + ]; + }, +}).register('listsortPopup'); diff --git a/client/components/cards/minicard.jade b/client/components/cards/minicard.jade index 3806ce41..ba0c5707 100644 --- a/client/components/cards/minicard.jade +++ b/client/components/cards/minicard.jade @@ -3,6 +3,13 @@ template(name="minicard") class="{{#if isLinkedCard}}linked-card{{/if}}" class="{{#if isLinkedBoard}}linked-board{{/if}}" class="minicard-{{colorClass}}") + if isMiniScreen + .handle + .fa.fa-arrows + unless isMiniScreen + if showDesktopDragHandles + .handle + .fa.fa-arrows if cover .minicard-cover(style="background-image: url('{{cover.url}}');") if labels @@ -15,8 +22,6 @@ template(name="minicard") if hiddenMinicardLabelText .minicard-label(class="card-label-{{color}}" title="{{name}}") .minicard-title - .handle - .fa.fa-arrows if $eq 'prefix-with-full-path' currentBoard.presentParentTask .parent-prefix | {{ parentString ' > ' }} @@ -53,6 +58,8 @@ template(name="minicard") if getDue .date +minicardDueDate + if getEnd + +minicardEndDate if getSpentTime .date +cardSpentTime diff --git a/client/components/cards/minicard.js b/client/components/cards/minicard.js index 4c25c11d..4c76db46 100644 --- a/client/components/cards/minicard.js +++ b/client/components/cards/minicard.js @@ -26,6 +26,9 @@ BlazeComponent.extendComponent({ }).register('minicard'); Template.minicard.helpers({ + showDesktopDragHandles() { + return Meteor.user().hasShowDesktopDragHandles(); + }, hiddenMinicardLabelText() { return Meteor.user().hasHiddenMinicardLabelText(); }, diff --git a/client/components/cards/minicard.styl b/client/components/cards/minicard.styl index 7c27cba1..9997fd5f 100644 --- a/client/components/cards/minicard.styl +++ b/client/components/cards/minicard.styl @@ -105,8 +105,7 @@ right: 5px; top: 5px; display:none; - // @media only screen and (max-width: 1199px) { - @media only screen and (max-width: 800px) { + @media only screen { display:block; } .fa-arrows @@ -128,7 +127,7 @@ .badges float: left margin-top: 7px - color: darken(white, 80%) + color: darken(white, 50%) &:empty display: none diff --git a/client/components/lists/list.js b/client/components/lists/list.js index bde43520..b7b8b2e0 100644 --- a/client/components/lists/list.js +++ b/client/components/lists/list.js @@ -31,7 +31,13 @@ BlazeComponent.extendComponent({ const itemsSelector = '.js-minicard:not(.placeholder, .js-card-composer)'; const $cards = this.$('.js-minicards'); - if (Utils.isMiniScreen()) { + if (Utils.isMiniScreen) { + $('.js-minicards').sortable({ + handle: '.handle', + }); + } + + if (!Utils.isMiniScreen && showDesktopDragHandles) { $('.js-minicards').sortable({ handle: '.handle', }); @@ -155,6 +161,12 @@ BlazeComponent.extendComponent({ }, }).register('list'); +Template.list.helpers({ + showDesktopDragHandles() { + return Meteor.user().hasShowDesktopDragHandles(); + }, +}); + Template.miniList.events({ 'click .js-select-list'() { const listId = this._id; diff --git a/client/components/lists/list.styl b/client/components/lists/list.styl index 81938c1a..0d3ccfce 100644 --- a/client/components/lists/list.styl +++ b/client/components/lists/list.styl @@ -84,17 +84,16 @@ padding-left: 10px color: #a6a6a6 - .list-header-menu position: absolute padding: 27px 19px margin-top: 1px top: -7px - right: -7px + right: 3px .list-header-plus-icon color: #a6a6a6 - margin-right: 10px + margin-right: 15px .highlight color: #ce1414 @@ -165,7 +164,16 @@ @media screen and (max-width: 800px) .list-header-menu - margin-right: 30px + position: absolute + padding: 27px 19px + margin-top: 1px + top: -7px + margin-right: 50px + right: -3px + + .list-header + .list-header-name + margin-left: 1.4rem .mini-list flex: 0 0 60px @@ -221,9 +229,17 @@ padding: 7px top: 50% transform: translateY(-50%) - right: 17px + margin-right: 27px font-size: 20px + .list-header-menu-handle + position: absolute + padding: 7px + top: 50% + transform: translateY(-50%) + right: 10px + font-size: 24px + .link-board-wrapper display: flex align-items: baseline diff --git a/client/components/lists/listHeader.jade b/client/components/lists/listHeader.jade index f930e57a..3b3a0242 100644 --- a/client/components/lists/listHeader.jade +++ b/client/components/lists/listHeader.jade @@ -9,6 +9,7 @@ template(name="listHeader") if currentList a.list-header-left-icon.fa.fa-angle-left.js-unselect-list h2.list-header-name( + title="{{ moment modifiedAt 'LLL' }}" class="{{#if currentUser.isBoardMember}}{{#unless currentUser.isCommentOnly}}js-open-inlined-form is-editable{{/unless}}{{/if}}") +viewer = title @@ -29,16 +30,22 @@ template(name="listHeader") if canSeeAddCard a.js-add-card.fa.fa-plus.list-header-plus-icon a.fa.fa-navicon.js-open-list-menu + a.list-header-menu-handle.handle.fa.fa-arrows.js-list-handle else a.list-header-menu-icon.fa.fa-angle-right.js-select-list + a.list-header-menu-handle.handle.fa.fa-arrows.js-list-handle else if currentUser.isBoardMember if isWatching i.list-header-watch-icon.fa.fa-eye div.list-header-menu unless currentUser.isCommentOnly + if isBoardAdmin + a.fa.js-list-star.list-header-plus-icon(class="fa-star{{#unless starred}}-o{{/unless}}") if canSeeAddCard a.js-add-card.fa.fa-plus.list-header-plus-icon a.fa.fa-navicon.js-open-list-menu + if showDesktopDragHandles + a.list-header-menu-handle.handle.fa.fa-arrows.js-list-handle template(name="editListTitleForm") .list-composer diff --git a/client/components/lists/listHeader.js b/client/components/lists/listHeader.js index e8a82499..b524d4e0 100644 --- a/client/components/lists/listHeader.js +++ b/client/components/lists/listHeader.js @@ -13,6 +13,20 @@ BlazeComponent.extendComponent({ ); }, + isBoardAdmin() { + return Meteor.user().isBoardAdmin(); + }, + starred(check = undefined) { + const list = Template.currentData(); + const status = list.isStarred(); + if (check === undefined) { + // just check + return status; + } else { + list.star(!status); + return !status; + } + }, editTitle(event) { event.preventDefault(); const newTitle = this.childComponents('inlinedForm')[0] @@ -61,6 +75,10 @@ BlazeComponent.extendComponent({ events() { return [ { + 'click .js-list-star'(event) { + event.preventDefault(); + this.starred(!this.starred()); + }, 'click .js-open-list-menu': Popup.open('listAction'), 'click .js-add-card'(event) { const listDom = $(event.target).parents( @@ -80,6 +98,12 @@ BlazeComponent.extendComponent({ }, }).register('listHeader'); +Template.listHeader.helpers({ + showDesktopDragHandles() { + return Meteor.user().hasShowDesktopDragHandles(); + }, +}); + Template.listActionPopup.helpers({ isWipLimitEnabled() { return Template.currentData().getWipLimit('enabled'); diff --git a/client/components/sidebar/sidebarFilters.jade b/client/components/sidebar/sidebarFilters.jade index 55ab213a..5f929cb9 100644 --- a/client/components/sidebar/sidebarFilters.jade +++ b/client/components/sidebar/sidebarFilters.jade @@ -5,6 +5,10 @@ template(name="filterSidebar") ul.sidebar-list + span {{_ 'list-filter-label'}} + form.js-list-filter + input(type="text") + ul.sidebar-list li(class="{{#if Filter.labelIds.isSelected undefined}}active{{/if}}") a.name.js-toggle-label-filter span.sidebar-list-item-description diff --git a/client/components/sidebar/sidebarFilters.js b/client/components/sidebar/sidebarFilters.js index 3483d00c..ee0176b9 100644 --- a/client/components/sidebar/sidebarFilters.js +++ b/client/components/sidebar/sidebarFilters.js @@ -4,6 +4,10 @@ BlazeComponent.extendComponent({ events() { return [ { + 'submit .js-list-filter'(evt) { + evt.preventDefault(); + Filter.lists.set(this.find('.js-list-filter input').value.trim()); + }, 'click .js-toggle-label-filter'(evt) { evt.preventDefault(); Filter.labelIds.toggle(this.currentData()._id); diff --git a/client/components/sidebar/sidebarSearches.jade b/client/components/sidebar/sidebarSearches.jade index 96877c50..4ee7fc9c 100644 --- a/client/components/sidebar/sidebarSearches.jade +++ b/client/components/sidebar/sidebarSearches.jade @@ -2,6 +2,10 @@ template(name="searchSidebar") form.js-search-term-form input(type="text" name="searchTerm" placeholder="{{_ 'search-example'}}" autofocus dir="auto") .list-body.js-perfect-scrollbar + .minilists.clearfix.js-minilists + each (lists) + a.minilist-wrapper.js-minilist(href=absoluteUrl) + +minilist(this) .minicards.clearfix.js-minicards each (results) a.minicard-wrapper.js-minicard(href=absoluteUrl) diff --git a/client/components/sidebar/sidebarSearches.js b/client/components/sidebar/sidebarSearches.js index 8944c04e..02677260 100644 --- a/client/components/sidebar/sidebarSearches.js +++ b/client/components/sidebar/sidebarSearches.js @@ -8,6 +8,11 @@ BlazeComponent.extendComponent({ return currentBoard.searchCards(this.term.get()); }, + lists() { + const currentBoard = Boards.findOne(Session.get('currentBoard')); + return currentBoard.searchLists(this.term.get()); + }, + events() { return [ { diff --git a/client/components/swimlanes/swimlaneHeader.jade b/client/components/swimlanes/swimlaneHeader.jade index 8c6aa5a3..fb6ef21d 100644 --- a/client/components/swimlanes/swimlaneHeader.jade +++ b/client/components/swimlanes/swimlaneHeader.jade @@ -16,6 +16,8 @@ template(name="swimlaneFixedHeader") unless currentUser.isCommentOnly a.fa.fa-plus.js-open-add-swimlane-menu.swimlane-header-plus-icon a.fa.fa-navicon.js-open-swimlane-menu + if showDesktopDragHandles + a.swimlane-header-menu-handle.handle.fa.fa-arrows.js-swimlane-header-handle template(name="editSwimlaneTitleForm") .list-composer diff --git a/client/components/swimlanes/swimlaneHeader.js b/client/components/swimlanes/swimlaneHeader.js index ee21d100..6f8029fd 100644 --- a/client/components/swimlanes/swimlaneHeader.js +++ b/client/components/swimlanes/swimlaneHeader.js @@ -28,6 +28,12 @@ BlazeComponent.extendComponent({ }, }).register('swimlaneHeader'); +Template.swimlaneHeader.helpers({ + showDesktopDragHandles() { + return Meteor.user().hasShowDesktopDragHandles(); + }, +}); + Template.swimlaneActionPopup.events({ 'click .js-set-swimlane-color': Popup.open('setSwimlaneColor'), 'click .js-close-swimlane'(event) { diff --git a/client/components/swimlanes/swimlanes.jade b/client/components/swimlanes/swimlanes.jade index 3ad43777..98af6d54 100644 --- a/client/components/swimlanes/swimlanes.jade +++ b/client/components/swimlanes/swimlanes.jade @@ -12,13 +12,13 @@ template(name="swimlane") unless currentUser.isCommentOnly +addListForm else + if currentUser.isBoardMember + unless currentUser.isCommentOnly + +addListForm each lists +list(this) if currentCardIsInThisList _id ../_id +cardDetails(currentCard) - if currentUser.isBoardMember - unless currentUser.isCommentOnly - +addListForm template(name="listsGroup") .swimlane.list-group.js-lists @@ -26,23 +26,23 @@ template(name="listsGroup") if currentList +list(currentList) else - each lists - +miniList(this) if currentUser.isBoardMember unless currentUser.isCommentOnly +addListForm + each lists + +miniList(this) else + if currentUser.isBoardMember + unless currentUser.isCommentOnly + +addListForm each lists if visible this +list(this) if currentCardIsInThisList _id null +cardDetails(currentCard) - if currentUser.isBoardMember - unless currentUser.isCommentOnly - +addListForm template(name="addListForm") - .list.list-composer.js-list-composer + .list.list-composer.js-list-composer(class="{{#if isMiniScreen}}mini-list{{/if}}") .list-header-add +inlinedForm(autoclose=false) input.list-name-input.full-line(type="text" placeholder="{{_ 'add-list'}}" diff --git a/client/components/swimlanes/swimlanes.js b/client/components/swimlanes/swimlanes.js index e0857003..8faad870 100644 --- a/client/components/swimlanes/swimlanes.js +++ b/client/components/swimlanes/swimlanes.js @@ -53,10 +53,21 @@ function initSortable(boardComponent, $listsDom) { }, }; + if (Utils.isMiniScreen) { + $listsDom.sortable({ + handle: '.js-list-handle', + }); + } + + if (!Utils.isMiniScreen && showDesktopDragHandles) { + $listsDom.sortable({ + handle: '.js-list-header', + }); + } + $listsDom.sortable({ tolerance: 'pointer', helper: 'clone', - handle: '.js-list-header', items: '.js-list:not(.js-list-composer)', placeholder: 'list placeholder', distance: 7, @@ -151,13 +162,13 @@ BlazeComponent.extendComponent({ // define a list of elements in which we disable the dragging because // the user will legitimately expect to be able to select some text with // his mouse. - const noDragInside = [ - 'a', - 'input', - 'textarea', - 'p', - '.js-list-header', - ]; + + const noDragInside = ['a', 'input', 'textarea', 'p'].concat( + Util.isMiniScreen || (!Util.isMiniScreen && showDesktopDragHandles) + ? ['.js-list-handle', '.js-swimlane-header-handle'] + : ['.js-list-header'], + ); + if ( $(evt.target).closest(noDragInside.join(',')).length === 0 && this.$('.swimlane').prop('clientHeight') > evt.offsetY @@ -233,6 +244,9 @@ BlazeComponent.extendComponent({ }).register('addListForm'); Template.swimlane.helpers({ + showDesktopDragHandles() { + return Meteor.user().hasShowDesktopDragHandles(); + }, canSeeAddList() { return ( Meteor.user() && @@ -253,6 +267,11 @@ BlazeComponent.extendComponent({ return false; } } + if (Filter.lists._isActive()) { + if (!list.title.match(Filter.lists.getRegexSelector())) { + return false; + } + } if (Filter.hideEmpty.isSelected()) { const swimlaneId = this.parentComponent() .parentComponent() diff --git a/client/components/swimlanes/swimlanes.styl b/client/components/swimlanes/swimlanes.styl index 1056e1e3..503091ee 100644 --- a/client/components/swimlanes/swimlanes.styl +++ b/client/components/swimlanes/swimlanes.styl @@ -50,6 +50,14 @@ margin-left: 5px margin-right: 10px + .swimlane-header-menu-handle + position: absolute + padding: 7px + top: 50% + transform: translateY(-50%) + left: 300px + font-size: 18px + .list-group height: 100% diff --git a/client/components/users/userHeader.jade b/client/components/users/userHeader.jade index 946bdab1..50a80396 100644 --- a/client/components/users/userHeader.jade +++ b/client/components/users/userHeader.jade @@ -79,6 +79,11 @@ template(name="changeSettingsPopup") if hiddenSystemMessages i.fa.fa-check li + a.js-toggle-desktop-drag-handles + | {{_ 'show-desktop-drag-handles'}} + if showDesktopDragHandles + i.fa.fa-check + li label.bold | {{_ 'show-cards-minimum-count'}} input#show-cards-count-at.inline-input.left(type="number" value="#{showCardsCountAt}" min="0" max="99" onkeydown="return false") diff --git a/client/components/users/userHeader.js b/client/components/users/userHeader.js index 36fb2020..194f990f 100644 --- a/client/components/users/userHeader.js +++ b/client/components/users/userHeader.js @@ -161,6 +161,9 @@ Template.changeLanguagePopup.events({ }); Template.changeSettingsPopup.helpers({ + showDesktopDragHandles() { + return Meteor.user().hasShowDesktopDragHandles(); + }, hiddenSystemMessages() { return Meteor.user().hasHiddenSystemMessages(); }, @@ -170,6 +173,9 @@ Template.changeSettingsPopup.helpers({ }); Template.changeSettingsPopup.events({ + 'click .js-toggle-desktop-drag-handles'() { + Meteor.call('toggleDesktopDragHandles'); + }, 'click .js-toggle-system-messages'() { Meteor.call('toggleSystemMessages'); }, diff --git a/client/lib/filter.js b/client/lib/filter.js index 1ca3a280..9ddea65c 100644 --- a/client/lib/filter.js +++ b/client/lib/filter.js @@ -439,6 +439,14 @@ class AdvancedFilter { const commands = this._filterToCommands(); return this._arrayToSelector(commands); } + getRegexSelector() { + // generate a regex for filter list + this._dep.depend(); + return new RegExp( + `^.*${this._filter.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}.*$`, + 'i', + ); + } } // The global Filter object. @@ -455,6 +463,7 @@ Filter = { hideEmpty: new SetFilter(), customFields: new SetFilter('_id'), advanced: new AdvancedFilter(), + lists: new AdvancedFilter(), // we need the ability to filter list by name as well _fields: ['labelIds', 'members', 'archive', 'hideEmpty', 'customFields'], @@ -533,6 +542,7 @@ Filter = { const filter = this[fieldName]; filter.reset(); }); + this.lists.reset(); this.advanced.reset(); this.resetExceptions(); }, |