Fix migration. Replace old checklist-item sort algorithm.

This commit is contained in:
Andrés Manelli 2018-03-19 15:03:44 -03:00
parent bf7de463f1
commit 153960742c
7 changed files with 114 additions and 64 deletions

View file

@ -70,7 +70,7 @@ template(name="editChecklistItemForm")
template(name="checklistItems") template(name="checklistItems")
.checklist-items.js-checklist-items .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) +inlinedForm(classNames="js-edit-checklist-item" item = item checklist = checklist)
+editChecklistItemForm(type = 'item' item = item checklist = checklist) +editChecklistItemForm(type = 'item' item = item checklist = checklist)
else else
@ -84,7 +84,7 @@ template(name="checklistItems")
| {{_ 'add-checklist-item'}}... | {{_ 'add-checklist-item'}}...
template(name='itemDetail') template(name='itemDetail')
.item.js-checklist-item .js-checklist-item.checklist-item
if canModifyCard if canModifyCard
.check-box.materialCheckBox(class="{{#if item.isFinished }}is-checked{{/if}}") .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}}") .item-title.js-open-inlined-form.is-editable(class="{{#if item.isFinished }}is-checked{{/if}}")

View file

@ -1,11 +1,14 @@
const { calculateIndexData } = Utils;
function initSorting(items) { function initSorting(items) {
items.sortable({ items.sortable({
tolerance: 'pointer', tolerance: 'pointer',
helper: 'clone', helper: 'clone',
items: '.js-checklist-item:not(.placeholder)', items: '.js-checklist-item:not(.placeholder)',
axis: 'y', connectWith: '.js-checklist-items',
appendTo: '.board-canvas',
distance: 7, distance: 7,
placeholder: 'placeholder', placeholder: 'checklist-item placeholder',
scroll: false, scroll: false,
start(evt, ui) { start(evt, ui) {
ui.placeholder.height(ui.helper.height()); ui.placeholder.height(ui.helper.height());
@ -13,33 +16,24 @@ function initSorting(items) {
}, },
stop(evt, ui) { stop(evt, ui) {
const parent = ui.item.parents('.js-checklist-items'); const parent = ui.item.parents('.js-checklist-items');
const orderedItems = []; const checklistId = Blaze.getData(parent.get(0)).checklist._id;
parent.find('.js-checklist-item').each(function(i, item) { let prevItem = ui.item.prev('.js-checklist-item').get(0);
const checklistItem = Blaze.getData(item).item; if (prevItem) {
orderedItems.push(checklistItem._id); prevItem = Blaze.getData(prevItem).item;
});
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);
} }
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; const checklist = this.currentData().checklist;
if (title) { if (title) {
checklist.addItem(title); ChecklistItems.insert({
title,
checklistId: checklist._id,
cardId: checklist.cardId,
sort: checklist.itemCount(),
});
} }
// We keep the form opened, empty it. // We keep the form opened, empty it.
textarea.value = ''; textarea.value = '';
@ -118,7 +117,7 @@ BlazeComponent.extendComponent({
const checklist = this.currentData().checklist; const checklist = this.currentData().checklist;
const item = this.currentData().item; const item = this.currentData().item;
if (checklist && item && item._id) { 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 textarea = this.find('textarea.js-edit-checklist-item');
const title = textarea.value.trim(); const title = textarea.value.trim();
const itemId = this.currentData().item._id; const item = this.currentData().item;
const checklist = this.currentData().checklist; item.setTitle(title);
checklist.editItem(itemId, title);
}, },
onCreated() { onCreated() {
@ -211,7 +209,7 @@ BlazeComponent.extendComponent({
const checklist = this.currentData().checklist; const checklist = this.currentData().checklist;
const item = this.currentData().item; const item = this.currentData().item;
if (checklist && item && item._id) { if (checklist && item && item._id) {
checklist.toggleItem(item._id); item.toggleItem();
} }
}, },
events() { events() {

View file

@ -78,34 +78,45 @@ textarea.js-add-checklist-item, textarea.js-edit-checklist-item
bottom: -600px bottom: -600px
right: 0 right: 0
.checklist-items .checklist-item
margin: 0 0 0.5em 1.33em margin: 0 0 0.5em 1.33em
line-height: 25px
font-size: 1.1em
margin-top: 3px
display: flex
.item &.placeholder
line-height: 25px background: darken(white, 20%)
font-size: 1.1em border-radius: 2px
margin-top: 3px
display: flex
&:hover
background-color: darken(white, 8%)
.check-box &.ui-sortable-helper
margin-top: 5px box-shadow: -2px 2px 8px rgba(0, 0, 0, .3),
&.is-checked 0 0 1px rgba(0, 0, 0, .5)
border-bottom: 2px solid #3cb500 transform: rotate(4deg)
border-right: 2px solid #3cb500 cursor: grabbing
.item-title &:hover
flex: 1 background-color: darken(white, 8%)
padding-left: 10px;
&.is-checked
color: #8c8c8c
font-style: italic
.js-delete-checklist-item .check-box
@extends .delete-text margin-top: 5px
padding: 12px 0 0 0 &.is-checked
border-bottom: 2px solid #3cb500
border-right: 2px solid #3cb500
.add-checklist-item .item-title
padding-top: 0.5em flex: 1
display: inline-block 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
margin: 0 0 0.5em 1.33em
padding-top: 0.5em
display: inline-block

View file

@ -33,6 +33,37 @@ Utils = {
return $(window).width() <= 800; 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 // Determine the new sort index
calculateIndex(prevCardDomElement, nextCardDomElement, nCards = 1) { calculateIndex(prevCardDomElement, nextCardDomElement, nCards = 1) {
let base, increment; let base, increment;

View file

@ -42,7 +42,7 @@ Activities.helpers({
return Checklists.findOne(this.checklistId); return Checklists.findOne(this.checklistId);
}, },
checklistItem() { checklistItem() {
return Checklists.findOne(this.checklistId).getItem(this.checklistItemId); return ChecklistItems.findOne(this.checklistItemId);
}, },
}); });

View file

@ -47,6 +47,16 @@ ChecklistItems.mutations({
toggleItem() { toggleItem() {
return { $set: { isFinished: !this.isFinished } }; return { $set: { isFinished: !this.isFinished } };
}, },
move(checklistId, sortIndex) {
const cardId = Checklists.findOne(checklistId).cardId;
const mutatedFields = {
cardId,
checklistId,
sort: sortIndex,
};
return {$set: mutatedFields};
}
}); });
// Activities helper // Activities helper

View file

@ -191,10 +191,10 @@ Migrations.add('add-views', () => {
Migrations.add('add-checklist-items', () => { Migrations.add('add-checklist-items', () => {
Checklists.find().forEach((checklist) => { Checklists.find().forEach((checklist) => {
// Create new items // Create new items
_.sortBy(checklist.items, 'sort').forEach((item) => { _.sortBy(checklist.items, 'sort').forEach((item, index) => {
ChecklistItems.direct.insert({ ChecklistItems.direct.insert({
title: item.title, title: item.title,
sort: item.sort, sort: index,
isFinished: item.isFinished, isFinished: item.isFinished,
checklistId: checklist._id, checklistId: checklist._id,
cardId: checklist.cardId, cardId: checklist.cardId,