Browse Source

Merge pull request #4220 from lexoyo/dev

Fix  BUG: Dragging multiple blocks reverses the order #3547
pull/4238/head
Artur Arseniev 4 years ago
committed by GitHub
parent
commit
d8bd222a52
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 69
      src/utils/Sorter.js
  2. 121
      test/specs/utils/Sorter.js

69
src/utils/Sorter.js

@ -982,6 +982,35 @@ export default Backbone.View.extend({
if (h) plh.style.height = h;
},
/**
* Build an array of all the parents, including the component itself
* @return {Model|null}
*/
parents(model) {
return model ? [model].concat(this.parents(model.parent())) : [];
},
/**
* Sort according to the position in the dom
* @param {Object} obj1 contains {model, parents}
* @param {Object} obj2 contains {model, parents}
*/
sort(obj1, obj2) {
// common ancesters
const ancesters = obj1.parents.filter(p => obj2.parents.includes(p));
const ancester = ancesters[0];
if (!ancester) {
// this is never supposed to happen
return obj2.model.index() - obj1.model.index();
}
// find siblings in the common ancester
// the sibling is the element inside the ancester
const s1 = obj1.parents[obj1.parents.indexOf(ancester) - 1];
const s2 = obj2.parents[obj2.parents.indexOf(ancester) - 1];
// order according to the position in the DOM
return s2.index() - s1.index();
},
/**
* Leave item
* @param event
@ -1013,9 +1042,43 @@ export default Backbone.View.extend({
if (this.moved && target) {
const toMove = this.toMove;
const toMoveArr = isArray(toMove) ? toMove : toMove ? [toMove] : [src];
toMoveArr.forEach(model => {
moved.push(this.move(target, model, lastPos));
});
let domPositionOffset = 0;
if (toMoveArr.length === 1) {
// do not sort the array in this case
// there are cases for the sorter where toMoveArr is [undefined]
// which allows the drop from blocks, native D&D and sort of layers in Style Manager
this.move(target, toMoveArr[0], lastPos);
} else {
toMoveArr
// add the model's parents
.map(model => ({
model,
parents: this.parents(model),
}))
// sort based on elements positions in the dom
.sort(this.sort)
// move each component to the new parent and position
.forEach(({ model }) => {
// store state before move
const index = model.index();
const parent = model.parent().getEl();
// move the component to the desired position
moved.push(
this.move(target, model, {
...lastPos,
indexEl: lastPos.indexEl - domPositionOffset,
index: lastPos.index - domPositionOffset,
})
);
// when the element is dragged to the same parent and after its position
// it will be removed from the children list
// in that case we need to adjust the following elements target position
if (parent === target && index <= lastPos.index) {
// the next elements will be inserted 1 element before this one
domPositionOffset++;
}
});
}
}
if (this.plh) this.plh.style.display = 'none';

121
test/specs/utils/Sorter.js

@ -13,7 +13,7 @@ describe('Sorter', () => {
var plh;
var html;
beforeAll(function() {
beforeAll(function () {
fixture = $('<div class="sorter-fixture"></div>').get(0);
});
@ -260,7 +260,7 @@ describe('Sorter', () => {
[0, 0, 50, 100, true],
[50, 0, 50, 100, true],
[100, 0, 50, 100, true],
[150, 0, 50, 70, true]
[150, 0, 50, 70, true],
];
});
@ -276,7 +276,7 @@ describe('Sorter', () => {
obj.onMove({
pageX: 0,
pageY: 0,
target: target
target: target,
});
expect(obj.moved).toEqual(1);
});
@ -293,7 +293,7 @@ describe('Sorter', () => {
el.style.position = 'absolute';
el.style.top = '0';
var ch = obj.getChildrenDim(el);
ch = ch.map(function(v) {
ch = ch.map(function (v) {
return v.slice(0, 5);
});
var subPos = obj.offset(sib1);
@ -303,7 +303,7 @@ describe('Sorter', () => {
[top, left, 50, 100, true],
[top + 50, left + 0, 50, 100, true],
[top + 100, left + 0, 50, 100, true],
[top + 100, left + 100, 50, 70, true]
[top + 100, left + 100, 50, 70, true],
];
expect(ch).toEqual(result);
});
@ -335,15 +335,15 @@ describe('Sorter', () => {
[top, left, 50, 100, true],
[top + 50, left + 0, 50, 100, true],
[topSib3, leftSib3, 50, 100, true],
[top + 100, left + 100, 50, 70, true]
[top + 100, left + 100, 50, 70, true],
];
var resultChildren = [
[topSib3, leftSib3, 30, 30, true],
[topSib3 + 30, left + 0, 20, 30, true]
[topSib3 + 30, left + 0, 20, 30, true],
];
var dims = obj.dimsFromTarget(sib3);
dims = dims.map(function(v) {
dims = dims.map(function (v) {
return v.slice(0, 5);
});
@ -351,30 +351,22 @@ describe('Sorter', () => {
// Inside target
var dims = obj.dimsFromTarget(sib3, leftSib3 + 15, topSib3 + 15);
dims = dims.map(function(v) {
dims = dims.map(function (v) {
return v.slice(0, 5);
});
expect(dims).toEqual(resultChildren);
// Exactly on border
var bOffset = obj.borderOffset;
var dims = obj.dimsFromTarget(
sib3,
leftSib3 + bOffset,
topSib3 + bOffset
);
dims = dims.map(function(v) {
var dims = obj.dimsFromTarget(sib3, leftSib3 + bOffset, topSib3 + bOffset);
dims = dims.map(function (v) {
return v.slice(0, 5);
});
expect(dims).toEqual(resultChildren);
// Slightly near border
var dims = obj.dimsFromTarget(
sib3,
leftSib3 + bOffset - 3,
topSib3 + bOffset
);
dims = dims.map(function(v) {
var dims = obj.dimsFromTarget(sib3, leftSib3 + bOffset - 3, topSib3 + bOffset);
dims = dims.map(function (v) {
return v.slice(0, 5);
});
expect(dims).toEqual(resultParent);
@ -411,7 +403,7 @@ describe('Sorter', () => {
[0, 10, 50, 100, true],
[50, 20, 50, 70, true],
[100, 30, 50, 100, true],
[150, 40, 50, 70, true]
[150, 40, 50, 70, true],
];
});
@ -444,10 +436,10 @@ describe('Sorter', () => {
parentModel = new Component({
droppable: (srcModel, trgModel) => {
return srcModel.getEl().className === 'canDrop';
}
},
});
parentView = new ComponentTextView({
model: parentModel
model: parentModel,
});
});
@ -460,10 +452,10 @@ describe('Sorter', () => {
tagName: 'div',
draggable: true,
content: 'Content text',
attributes: { class: 'canDrop' }
attributes: { class: 'canDrop' },
});
var srcView = new ComponentTextView({
model: srcModel
model: srcModel,
});
expect(obj.validTarget(parentView.el, srcView.el).valid).toEqual(true);
@ -474,10 +466,10 @@ describe('Sorter', () => {
tagName: 'div',
draggable: true,
content: 'Content text',
attributes: { class: 'cannotDrop' }
attributes: { class: 'cannotDrop' },
});
var srcView = new ComponentTextView({
model: srcModel
model: srcModel,
});
expect(obj.validTarget(parentView.el, srcView.el).valid).toEqual(false);
@ -492,10 +484,10 @@ describe('Sorter', () => {
srcModel = new Component({
draggable: (srcModel, trgModel) => {
return trgModel.getEl().className === 'canDrag';
}
},
});
srcView = new ComponentTextView({
model: srcModel
model: srcModel,
});
});
@ -508,10 +500,10 @@ describe('Sorter', () => {
tagName: 'div',
droppable: true,
content: 'Content text',
attributes: { class: 'canDrag' }
attributes: { class: 'canDrag' },
});
var parentView = new ComponentTextView({
model: parentModel
model: parentModel,
});
expect(obj.validTarget(parentView.el, srcView.el).valid).toEqual(true);
@ -522,14 +514,75 @@ describe('Sorter', () => {
tagName: 'div',
droppable: true,
content: 'Content text',
attributes: { class: 'cannotDrag' }
attributes: { class: 'cannotDrag' },
});
var parentView = new ComponentTextView({
model: parentModel
model: parentModel,
});
expect(obj.validTarget(parentView.el, srcView.el).valid).toEqual(false);
});
});
});
describe('Parents', () => {
var child00;
var child01;
var child0;
var child10;
var child1;
var child2;
var root;
beforeAll(() => {
child00 = new Component({
tagName: 'div',
name: 'child00',
});
child01 = new Component({
tagName: 'div',
name: 'child01',
});
child0 = new Component({
tagName: 'div',
name: 'child0',
components: [child00, child01],
});
child10 = new Component({
tagName: 'div',
name: 'child10',
});
child1 = new Component({
tagName: 'div',
name: 'child1',
components: [child10],
});
child2 = new Component({
tagName: 'div',
name: 'child2',
});
root = new Component({
tagName: 'div',
name: 'root',
components: [child0, child1, child2],
});
});
test('Parents', () => {
expect(obj.parents(root)).toEqual([root]);
expect(obj.parents(child0)).toEqual([child0, root]);
expect(obj.parents(child00)).toEqual([child00, child0, root]);
});
test('Sort', () => {
const withParents = model => ({ model, parents: obj.parents(model) });
expect(obj.sort(withParents(child00), withParents(child1))).toEqual(1);
expect(obj.sort(withParents(child00), withParents(child01))).toEqual(1);
expect(obj.sort(withParents(child00), withParents(child10))).toEqual(1);
expect(obj.sort(withParents(child1), withParents(child2))).toEqual(1);
expect(obj.sort(withParents(child10), withParents(child2))).toEqual(1);
expect(obj.sort(withParents(child1), withParents(child00))).toEqual(-1);
expect(obj.sort(withParents(child01), withParents(child00))).toEqual(-1);
expect(obj.sort(withParents(child10), withParents(child00))).toEqual(-1);
expect(obj.sort(withParents(child2), withParents(child1))).toEqual(-1);
expect(obj.sort(withParents(child2), withParents(child10))).toEqual(-1);
});
});
});

Loading…
Cancel
Save