From 153960742cac53c52f176f1bc645d850f25ac966 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Manelli?= Date: Mon, 19 Mar 2018 15:03:44 -0300 Subject: Fix migration. Replace old checklist-item sort algorithm. --- client/components/cards/checklists.jade | 4 +- client/components/cards/checklists.js | 66 ++++++++++++++++---------------- client/components/cards/checklists.styl | 67 +++++++++++++++++++-------------- client/lib/utils.js | 31 +++++++++++++++ 4 files changed, 104 insertions(+), 64 deletions(-) (limited to 'client') diff --git a/client/components/cards/checklists.jade b/client/components/cards/checklists.jade index 42fe3bd4..c73602b5 100644 --- a/client/components/cards/checklists.jade +++ b/client/components/cards/checklists.jade @@ -70,7 +70,7 @@ template(name="editChecklistItemForm") template(name="checklistItems") .checklist-items.js-checklist-items - each item in checklist.getItemsSorted + each item in checklist.items +inlinedForm(classNames="js-edit-checklist-item" item = item checklist = checklist) +editChecklistItemForm(type = 'item' item = item checklist = checklist) else @@ -84,7 +84,7 @@ template(name="checklistItems") | {{_ 'add-checklist-item'}}... template(name='itemDetail') - .item.js-checklist-item + .js-checklist-item.checklist-item if canModifyCard .check-box.materialCheckBox(class="{{#if item.isFinished }}is-checked{{/if}}") .item-title.js-open-inlined-form.is-editable(class="{{#if item.isFinished }}is-checked{{/if}}") diff --git a/client/components/cards/checklists.js b/client/components/cards/checklists.js index 5c0e3d2e..232cc6e4 100644 --- a/client/components/cards/checklists.js +++ b/client/components/cards/checklists.js @@ -1,11 +1,14 @@ +const { calculateIndexData } = Utils; + function initSorting(items) { items.sortable({ tolerance: 'pointer', helper: 'clone', items: '.js-checklist-item:not(.placeholder)', - axis: 'y', + connectWith: '.js-checklist-items', + appendTo: '.board-canvas', distance: 7, - placeholder: 'placeholder', + placeholder: 'checklist-item placeholder', scroll: false, start(evt, ui) { ui.placeholder.height(ui.helper.height()); @@ -13,33 +16,24 @@ function initSorting(items) { }, stop(evt, ui) { const parent = ui.item.parents('.js-checklist-items'); - const orderedItems = []; - parent.find('.js-checklist-item').each(function(i, item) { - const checklistItem = Blaze.getData(item).item; - orderedItems.push(checklistItem._id); - }); - items.sortable('cancel'); - const formerParent = ui.item.parents('.js-checklist-items'); - const checklist = Blaze.getData(parent.get(0)).checklist; - const oldChecklist = Blaze.getData(formerParent.get(0)).checklist; - if (oldChecklist._id !== checklist._id) { - const currentItem = Blaze.getData(ui.item.get(0)).item; - for (let i = 0; i < orderedItems.length; i++) { - const itemId = orderedItems[i]; - if (itemId !== currentItem._id) continue; - const newItem = { - _id: checklist.getNewItemId(), - title: currentItem.title, - sort: i, - isFinished: currentItem.isFinished, - }; - checklist.addFullItem(newItem); - orderedItems[i] = currentItem._id; - oldChecklist.removeItem(itemId); - } - } else { - checklist.sortItems(orderedItems); + const checklistId = Blaze.getData(parent.get(0)).checklist._id; + let prevItem = ui.item.prev('.js-checklist-item').get(0); + if (prevItem) { + prevItem = Blaze.getData(prevItem).item; + } + let nextItem = ui.item.next('.js-checklist-item').get(0); + if (nextItem) { + nextItem = Blaze.getData(nextItem).item; } + const nItems = 1; + const sortIndex = calculateIndexData(prevItem, nextItem, nItems); + const checklistDomElement = ui.item.get(0); + const checklistData = Blaze.getData(checklistDomElement); + const checklistItem = checklistData.item; + + items.sortable('cancel'); + + checklistItem.move(checklistId, sortIndex.base); }, }); } @@ -95,7 +89,12 @@ BlazeComponent.extendComponent({ const checklist = this.currentData().checklist; if (title) { - checklist.addItem(title); + ChecklistItems.insert({ + title, + checklistId: checklist._id, + cardId: checklist.cardId, + sort: checklist.itemCount(), + }); } // We keep the form opened, empty it. textarea.value = ''; @@ -118,7 +117,7 @@ BlazeComponent.extendComponent({ const checklist = this.currentData().checklist; const item = this.currentData().item; if (checklist && item && item._id) { - checklist.removeItem(item._id); + ChecklistItems.remove(item._id); } }, @@ -135,9 +134,8 @@ BlazeComponent.extendComponent({ const textarea = this.find('textarea.js-edit-checklist-item'); const title = textarea.value.trim(); - const itemId = this.currentData().item._id; - const checklist = this.currentData().checklist; - checklist.editItem(itemId, title); + const item = this.currentData().item; + item.setTitle(title); }, onCreated() { @@ -211,7 +209,7 @@ BlazeComponent.extendComponent({ const checklist = this.currentData().checklist; const item = this.currentData().item; if (checklist && item && item._id) { - checklist.toggleItem(item._id); + item.toggleItem(); } }, events() { diff --git a/client/components/cards/checklists.styl b/client/components/cards/checklists.styl index d4776397..3f4445ab 100644 --- a/client/components/cards/checklists.styl +++ b/client/components/cards/checklists.styl @@ -78,34 +78,45 @@ textarea.js-add-checklist-item, textarea.js-edit-checklist-item bottom: -600px right: 0 -.checklist-items +.checklist-item margin: 0 0 0.5em 1.33em + line-height: 25px + font-size: 1.1em + margin-top: 3px + display: flex - .item - line-height: 25px - font-size: 1.1em - margin-top: 3px - display: flex - &:hover - background-color: darken(white, 8%) - - .check-box - margin-top: 5px - &.is-checked - border-bottom: 2px solid #3cb500 - border-right: 2px solid #3cb500 - - .item-title - flex: 1 - padding-left: 10px; - &.is-checked - color: #8c8c8c - font-style: italic - - .js-delete-checklist-item - @extends .delete-text - padding: 12px 0 0 0 + &.placeholder + background: darken(white, 20%) + border-radius: 2px + + &.ui-sortable-helper + box-shadow: -2px 2px 8px rgba(0, 0, 0, .3), + 0 0 1px rgba(0, 0, 0, .5) + transform: rotate(4deg) + cursor: grabbing + + &:hover + background-color: darken(white, 8%) + + .check-box + margin-top: 5px + &.is-checked + border-bottom: 2px solid #3cb500 + border-right: 2px solid #3cb500 + + .item-title + flex: 1 + padding-left: 10px; + &.is-checked + color: #8c8c8c + font-style: italic + +.js-delete-checklist-item + margin: 0 0 0.5em 1.33em + @extends .delete-text + padding: 12px 0 0 0 - .add-checklist-item - padding-top: 0.5em - display: inline-block +.add-checklist-item + margin: 0 0 0.5em 1.33em + padding-top: 0.5em + display: inline-block diff --git a/client/lib/utils.js b/client/lib/utils.js index 9a9ff654..1f44c60d 100644 --- a/client/lib/utils.js +++ b/client/lib/utils.js @@ -33,6 +33,37 @@ Utils = { return $(window).width() <= 800; }, + calculateIndexData(prevData, nextData, nItems = 1) { + let base, increment; + // If we drop the card to an empty column + if (!prevData && !nextData) { + base = 0; + increment = 1; + // If we drop the card in the first position + } else if (!prevData) { + base = nextData.sort - 1; + increment = -1; + // If we drop the card in the last position + } else if (!nextData) { + base = prevData.sort + 1; + increment = 1; + } + // In the general case take the average of the previous and next element + // sort indexes. + else { + const prevSortIndex = prevData.sort; + const nextSortIndex = nextData.sort; + increment = (nextSortIndex - prevSortIndex) / (nItems + 1); + base = prevSortIndex + increment; + } + // XXX Return a generator that yield values instead of a base with a + // increment number. + return { + base, + increment, + }; + }, + // Determine the new sort index calculateIndex(prevCardDomElement, nextCardDomElement, nCards = 1) { let base, increment; -- cgit v1.2.3-1-g7c22 From 243af32fc798f161e72eddf9e43053b1ba89d98a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Manelli?= Date: Mon, 19 Mar 2018 16:47:07 -0300 Subject: Add checklist ordering capability --- client/components/cards/cardDetails.js | 46 +++++++++++++++++++++++++++++++++ client/components/cards/checklists.jade | 35 +++++++++++++------------ client/components/cards/checklists.js | 42 +++++++++++++++++------------- 3 files changed, 88 insertions(+), 35 deletions(-) (limited to 'client') diff --git a/client/components/cards/cardDetails.js b/client/components/cards/cardDetails.js index ab8a6288..066e52ec 100644 --- a/client/components/cards/cardDetails.js +++ b/client/components/cards/cardDetails.js @@ -1,4 +1,5 @@ const subManager = new SubsManager(); +const { calculateIndexData } = Utils; BlazeComponent.extendComponent({ mixins() { @@ -66,6 +67,51 @@ BlazeComponent.extendComponent({ onRendered() { if (!Utils.isMiniScreen()) this.scrollParentContainer(); + const $checklistsDom = this.$('.card-checklist-items'); + + $checklistsDom.sortable({ + tolerance: 'pointer', + helper: 'clone', + handle: '.checklist-title', + items: '.js-checklist', + placeholder: 'js-checklist placeholder', + distance: 7, + start(evt, ui) { + ui.placeholder.height(ui.helper.height()); + EscapeActions.executeUpTo('popup-close'); + }, + stop(evt, ui) { + let prevChecklist = ui.item.prev('.js-checklist').get(0); + if (prevChecklist) { + prevChecklist = Blaze.getData(prevChecklist).checklist; + } + let nextChecklist = ui.item.next('.js-checklist').get(0); + if (nextChecklist) { + nextChecklist = Blaze.getData(nextChecklist).checklist; + } + const sortIndex = calculateIndexData(prevChecklist, nextChecklist, 1); + + $checklistsDom.sortable('cancel'); + const checklist = Blaze.getData(ui.item.get(0)).checklist; + + Checklists.update(checklist._id, { + $set: { + sort: sortIndex.base, + }, + }); + }, + }); + + function userIsMember() { + return Meteor.user() && Meteor.user().isBoardMember(); + } + + // Disable sorting if the current user is not a board member + this.autorun(() => { + if ($checklistsDom.data('sortable')) { + $checklistsDom.sortable('option', 'disabled', !userIsMember()); + } + }); }, onDestroyed() { diff --git a/client/components/cards/checklists.jade b/client/components/cards/checklists.jade index c73602b5..4753c2c6 100644 --- a/client/components/cards/checklists.jade +++ b/client/components/cards/checklists.jade @@ -18,24 +18,25 @@ template(name="checklists") | {{_ 'add-checklist'}}... template(name="checklistDetail") - +inlinedForm(classNames="js-edit-checklist-title" checklist = checklist) - +editChecklistItemForm(checklist = checklist) - else - .checklist-title - .checkbox.fa.fa-check-square-o - if canModifyCard - a.js-delete-checklist.toggle-delete-checklist-dialog {{_ "delete"}}... - - span.checklist-stat(class="{{#if checklist.isFinished}}is-finished{{/if}}") {{checklist.finishedCount}}/{{checklist.itemCount}} - if canModifyCard - h2.title.js-open-inlined-form.is-editable - +viewer - = checklist.title - else - h2.title - +viewer + .js-checklist + +inlinedForm(classNames="js-edit-checklist-title" checklist = checklist) + +editChecklistItemForm(checklist = checklist) + else + .checklist-title + .checkbox.fa.fa-check-square-o + if canModifyCard + a.js-delete-checklist.toggle-delete-checklist-dialog {{_ "delete"}}... + + span.checklist-stat(class="{{#if checklist.isFinished}}is-finished{{/if}}") {{checklist.finishedCount}}/{{checklist.itemCount}} + if canModifyCard + h2.title.js-open-inlined-form.is-editable + +viewer = checklist.title - +checklistItems(checklist = checklist) + else + h2.title + +viewer + = checklist.title + +checklistItems(checklist = checklist) template(name="checklistDeleteDialog") .js-confirm-checklist-delete diff --git a/client/components/cards/checklists.js b/client/components/cards/checklists.js index 232cc6e4..d68a1e47 100644 --- a/client/components/cards/checklists.js +++ b/client/components/cards/checklists.js @@ -38,26 +38,32 @@ function initSorting(items) { }); } -Template.checklists.onRendered(function () { - const self = BlazeComponent.getComponentForElement(this.firstNode); - self.itemsDom = this.$('.card-checklist-items'); - initSorting(self.itemsDom); - self.itemsDom.mousedown(function(evt) { - evt.stopPropagation(); - }); +BlazeComponent.extendComponent({ + onRendered() { + const self = this; + self.itemsDom = this.$('.js-checklist-items'); + initSorting(self.itemsDom); + self.itemsDom.mousedown(function(evt) { + evt.stopPropagation(); + }); + + function userIsMember() { + return Meteor.user() && Meteor.user().isBoardMember(); + } - function userIsMember() { - return Meteor.user() && Meteor.user().isBoardMember(); - } + // Disable sorting if the current user is not a board member + self.autorun(() => { + const $itemsDom = $(self.itemsDom); + if ($itemsDom.data('sortable')) { + $(self.itemsDom).sortable('option', 'disabled', !userIsMember()); + } + }); + }, - // Disable sorting if the current user is not a board member - self.autorun(() => { - const $itemsDom = $(self.itemsDom); - if ($itemsDom.data('sortable')) { - $(self.itemsDom).sortable('option', 'disabled', !userIsMember()); - } - }); -}); + canModifyCard() { + return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly(); + }, +}).register('checklistDetail'); BlazeComponent.extendComponent({ -- cgit v1.2.3-1-g7c22 From 06e64d24a80be9df55bf1b0c91f674b6f947882d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Manelli?= Date: Mon, 19 Mar 2018 17:19:46 -0300 Subject: Add styling --- client/components/cards/cardDetails.js | 2 +- client/components/cards/checklists.jade | 2 +- client/components/cards/checklists.styl | 15 +++++++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) (limited to 'client') diff --git a/client/components/cards/cardDetails.js b/client/components/cards/cardDetails.js index 066e52ec..77593a74 100644 --- a/client/components/cards/cardDetails.js +++ b/client/components/cards/cardDetails.js @@ -74,7 +74,7 @@ BlazeComponent.extendComponent({ helper: 'clone', handle: '.checklist-title', items: '.js-checklist', - placeholder: 'js-checklist placeholder', + placeholder: 'checklist placeholder', distance: 7, start(evt, ui) { ui.placeholder.height(ui.helper.height()); diff --git a/client/components/cards/checklists.jade b/client/components/cards/checklists.jade index 4753c2c6..c79eb5aa 100644 --- a/client/components/cards/checklists.jade +++ b/client/components/cards/checklists.jade @@ -18,7 +18,7 @@ template(name="checklists") | {{_ 'add-checklist'}}... template(name="checklistDetail") - .js-checklist + .js-checklist.checklist +inlinedForm(classNames="js-edit-checklist-title" checklist = checklist) +editChecklistItemForm(checklist = checklist) else diff --git a/client/components/cards/checklists.styl b/client/components/cards/checklists.styl index 3f4445ab..7b35488f 100644 --- a/client/components/cards/checklists.styl +++ b/client/components/cards/checklists.styl @@ -78,12 +78,27 @@ textarea.js-add-checklist-item, textarea.js-edit-checklist-item bottom: -600px right: 0 +.checklist + background: darken(white, 3%) + + &.placeholder + background: darken(white, 20%) + border-radius: 2px + + &.ui-sortable-helper + box-shadow: -2px 2px 8px rgba(0, 0, 0, .3), + 0 0 1px rgba(0, 0, 0, .5) + transform: rotate(4deg) + cursor: grabbing + + .checklist-item margin: 0 0 0.5em 1.33em line-height: 25px font-size: 1.1em margin-top: 3px display: flex + background: darken(white, 3%) &.placeholder background: darken(white, 20%) -- cgit v1.2.3-1-g7c22 From 5e5a5ed9be33fe020b76dfb1ab4b32aec0753a20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Manelli?= Date: Mon, 19 Mar 2018 18:49:00 -0300 Subject: Fix class name change when clicking check box --- client/components/cards/checklists.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'client') diff --git a/client/components/cards/checklists.js b/client/components/cards/checklists.js index d68a1e47..1f05aded 100644 --- a/client/components/cards/checklists.js +++ b/client/components/cards/checklists.js @@ -220,7 +220,7 @@ BlazeComponent.extendComponent({ }, events() { return [{ - 'click .item .check-box': this.toggleItem, + 'click .js-checklist-item .check-box': this.toggleItem, }]; }, }).register('itemDetail'); -- cgit v1.2.3-1-g7c22