diff --git a/index.html b/index.html index eadba1299..05a5a9969 100755 --- a/index.html +++ b/index.html @@ -29,7 +29,21 @@
Hover me
- +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Flex is the new black
@@ -445,7 +459,7 @@ background-image:url("http://placehold.it/350x250/e868a2/fff/image6.jpg"); } .card-body{ - padding: 15px; + padding: 15px 15px 5px 15px; color: #555; } .card-title{ diff --git a/src/commands/view/CommandAbstract.js b/src/commands/view/CommandAbstract.js index 785bf81ed..ef849ec9d 100644 --- a/src/commands/view/CommandAbstract.js +++ b/src/commands/view/CommandAbstract.js @@ -17,9 +17,11 @@ define(['backbone'], this.canvasId = this.config.canvasId || ''; this.wrapperId = this.config.wrapperId || 'wrapper'; this.pfx = this.config.stylePrefix; + this.ppfx = this.config.pStylePrefix; this.hoverClass = this.pfx + 'hover'; this.badgeClass = this.pfx + 'badge'; this.plhClass = this.pfx + 'placeholder'; + this.freezClass = this.ppfx + 'freezed'; if(this.editorModel.get) this.setElement(this.editorModel.get('$editor').find('#'+this.canvasId)); this.$canvas = this.$el; diff --git a/src/commands/view/MoveComponent.js b/src/commands/view/MoveComponent.js index 90cd2273c..d49daf48f 100644 --- a/src/commands/view/MoveComponent.js +++ b/src/commands/view/MoveComponent.js @@ -8,7 +8,7 @@ define(['backbone', './SelectComponent','./SelectPosition'], init: function(o){ SelectComponent.init.apply(this, arguments); - _.bindAll(this,'startMove','onMove','endMove','rollback','selectingPosition','itemLeft'); + _.bindAll(this,'initSorter','startMove','onMove','endMove','rollback','selectingPosition','itemLeft', 'onEndMove'); this.opt = o; this.hoverClass = this.pfx + 'hover-move'; this.badgeClass = this.pfx + 'badge-yellow'; @@ -19,20 +19,38 @@ define(['backbone', './SelectComponent','./SelectPosition'], this.canvasTop = this.$canvas.offset().top; this.canvasLeft = this.$canvas.offset().left; this.$el.css('cursor','move'); - this.$el.on('mousedown', this.startMove); + //this.$el.on('mousedown', this.startMove); + this.$el.on('mousedown', this.initSorter); this.startSelectComponent(); //Avoid strange moving behavior this.$el.addClass(this.noSelClass); }, + /** + * Delegate sorting + * @param {Event} e + * */ + initSorter: function(e){ + if(this.sorter) + this.sorter.startSort(e.target); + this.stopSelectComponent(e); + this.$el.off('mousedown', this.initSorter); + }, + + /** + * Callback after sorting + */ + onEndMove: function(){ + this.enable(); + }, + /** * Hover command * @param {Object} e * @private */ - onHover: function(e) - { + onHover: function(e) { e.stopPropagation(); var $this = $(e.target); @@ -140,7 +158,7 @@ define(['backbone', './SelectComponent','./SelectPosition'], * */ freezeComponent: function($component){ $component.css({'pointer-events':'none'}); - $component.addClass('freezed'); + $component.addClass(this.freezClass); }, /** Make component touchable @@ -149,7 +167,7 @@ define(['backbone', './SelectComponent','./SelectPosition'], * */ unfreezeComponent: function($component){ $component.css({'pointer-events':'auto'}); - $component.removeClass('freezed'); + $component.removeClass(this.freezClass); }, /** Used to bring the previous situation before start moving the component @@ -179,7 +197,23 @@ define(['backbone', './SelectComponent','./SelectPosition'], $(document).off('keypress', this.rollback); }, - run: function(){ + run: function(editor, sender, opts){ + this.editor = editor; + + // Activate sorter, at any run? In layers, is called once at any stack + var utils = this.editor.Utils; + if(utils && utils.Sorter) + this.sorter = new utils.Sorter({ + container: this.$el.get(0), + containerSel: '*', + itemSel: '*', + pfx: this.ppfx, + onEndMove: this.onEndMove, + direction: 'a', + nested: 1, + freezeClass: this.freezClass, + }); + this.enable(); }, diff --git a/src/dom_components/view/ComponentView.js b/src/dom_components/view/ComponentView.js index 8e278e581..98d5e36ed 100644 --- a/src/dom_components/view/ComponentView.js +++ b/src/dom_components/view/ComponentView.js @@ -25,6 +25,7 @@ define(['backbone', './ComponentsView'], this.listenTo(this.model.get('classes'), 'add remove change', this.updateClasses); this.$el.data("model", this.model); this.$el.data("model-comp", this.components); + this.$el.data("collection", this.components); if(this.model.get('classes').length) this.importClasses(); diff --git a/src/editor/model/Editor.js b/src/editor/model/Editor.js index 1c2ea520a..d7f77916a 100644 --- a/src/editor/model/Editor.js +++ b/src/editor/model/Editor.js @@ -338,6 +338,7 @@ define([ initCommands: function() { var cfg = this.config.commands, pfx = cfg.stylePrefix || 'com-'; + cfg.pStylePrefix = this.config.stylePrefix; cfg.stylePrefix = this.config.stylePrefix + pfx; cfg.em = this; cfg.canvasId = this.config.idCanvas; diff --git a/src/utils/Sorter.js b/src/utils/Sorter.js index c4e51ffa5..29f023bfb 100644 --- a/src/utils/Sorter.js +++ b/src/utils/Sorter.js @@ -18,6 +18,8 @@ define(['backbone'], this.itemSel = o.itemSel || 'div'; this.nested = o.nested || 0; this.pfx = o.pfx || ''; + this.onEndMove = o.onEndMove || ''; + this.direction = o.direction || 'v'; // v (vertical), h (horizontal), a (auto) }, /** @@ -152,7 +154,16 @@ define(['backbone'], if(!this.matches(el, this.itemSel)) continue; var dim = this.getDim(el); - dim.push(true); //TODO check if in flow, now only for vertical elements + var dir = this.direction; + + if(dir == 'v') + dir = true; + else if(dir == 'h') + dir = false; + else + dir = this.isInFlow(el, elem); + + dim.push(dir); dim.push(el); dims.push(dim); } @@ -169,6 +180,60 @@ define(['backbone'], return [o.top - this.elT, o.left - this.elL, el.offsetHeight, el.offsetWidth]; }, + /** + * Returns true if the elements is in flow, so is not in flow where + * for example the component is with float:left + * @param {HTMLElement} el + * @param {HTMLElement} parent + * @return {Boolean} + * @private + * */ + isInFlow: function(el, parent) { + if(!el) + return false; + + parent = parent || document.body; + var ch = -1, h; + var elem = el; + //while (elem !== document.body) { + h = elem.offsetHeight; + if (h < ch || !this.styleInFlow(elem, parent)) + return false; + else + return true; + }, + + /** + * Check if el has style to be in flow + * @param {HTMLElement} el + * @param {HTMLElement} parent + * @return {Boolean} + * @private + */ + styleInFlow: function(el, parent) { + var style = el.style; + if (style.float && style.float !== 'none') + return; + if (style.overflow && style.overflow !== 'visible') + return; + if(parent && $(parent).css('display') == 'flex') + return; + switch (style.position) { + case 'static': case 'relative': case '': + break; + default: + return; + } + var disp = $(el).css('display'); + switch (disp) { + case 'block': + case 'list-item': + case 'table': + return true; + } + return; + }, + /** * Get dimensions of nodes relative to the coordinates * @param {HTMLElement} target @@ -315,9 +380,8 @@ define(['backbone'], t = elDim[0] + marg; l = (method == 'before') ? (elDim[1] - marg) : (elDim[1] + elDim[3] - marg); }else{ - //w = '100%'; w = elDim[3] + un; - //h = elDim[3] + un; + h = 'auto'; t = (method == 'before') ? (elDim[0] - marg) : (elDim[0] + elDim[2] - marg); l = elDim[1]; } @@ -327,9 +391,10 @@ define(['backbone'], return; } if(trgDim){ - t = trgDim[0] + margI + 17; - l = trgDim[1] + margI * 7; - w = (parseInt(trgDim[3]) - margI * 14) + un; + t = trgDim[0] + margI; + l = trgDim[1] + margI; + w = (parseInt(trgDim[3]) - margI * 2) + un; + h = 'auto'; } } plh.style.top = t + un; @@ -355,6 +420,9 @@ define(['backbone'], this.eV.className = this.eV.className.replace(clsReg, ''); if(this.moved) this.move(this.target, this.eV, this.lastPos); + + if(typeof this.onEndMove === 'function') + this.onEndMove(); }, /** @@ -367,9 +435,12 @@ define(['backbone'], var index = pos.index; var model = $(src).data('model'); var collection = model.collection; - var targetCollection = $(dst).data('collection'); + var $dst = $(dst); + var targetCollection = $dst.data('collection'); + var targetModel = $dst.data('model'); + var droppable = targetModel ? targetModel.get('droppable') : 1; - if(targetCollection){// && targetModel.get('droppable') + if(targetCollection && droppable){ // TODO && targetModel.get('droppable') index = pos.method === 'after' ? index + 1 : index; var modelTemp = targetCollection.add({}, {at: index, noIncrement: 1}); var modelRemoved = model.collection.remove(model); diff --git a/styles/css/main.css b/styles/css/main.css index 715f41df2..224ea1b9e 100644 --- a/styles/css/main.css +++ b/styles/css/main.css @@ -2602,6 +2602,11 @@ See http://bgrins.github.io/spectrum/themes/ for instructions. height: 100%; min-width: 1250px; } +.wte-freezed { + opacity: 0.5; + filter: alpha(opacity=50); + pointer-events: none; } + /************* CANVAS ****************/ .wte-cv-canvas { position: absolute; @@ -2612,7 +2617,7 @@ See http://bgrins.github.io/spectrum/themes/ for instructions. overflow: auto; z-index: 1; /* This simulate body behaviour */ } - .wte-cv-canvas > div { + .wte-cv-canvas > div:first-child { background-color: #fff; position: relative; height: 100%; diff --git a/styles/scss/main.scss b/styles/scss/main.scss index ef82b4d5d..6c1d3c117 100644 --- a/styles/scss/main.scss +++ b/styles/scss/main.scss @@ -87,6 +87,10 @@ $imageCompDim: 50px; height: 100%; min-width: 1250px; } +.#{$app-prefix}freezed{ + @include opacity(0.50); + pointer-events: none; +} /************* CANVAS ****************/ .#{$cv-prefix}canvas { position: absolute; @@ -97,7 +101,7 @@ $imageCompDim: 50px; z-index:1; /* This simulate body behaviour */ - > div { + > div:first-child { background-color: #fff; position: relative; height: 100%; diff --git a/test/specs/utils/Sorter.js b/test/specs/utils/Sorter.js index fdc7a3213..bc14c640e 100644 --- a/test/specs/utils/Sorter.js +++ b/test/specs/utils/Sorter.js @@ -11,6 +11,7 @@ define([path + 'Sorter',], var obj; var parent; var plh; + var html; before(function () { fixture = $('
').get(0); @@ -24,11 +25,38 @@ define([path + 'Sorter',], obj = new Sorter({container: '.parent1'}); document.body.appendChild(fixture); fixture.appendChild(parent); + html = '
'+ + '
ba' + + '

baa

' + + 'bab' + + 'bac'+ + '
eldiv
'+ + '
' + + '
' + + '
' + + '
aa' + + '

aaa

' + + 'aab' + + 'aac' + + '
' + + '
ab
' + + '
ac' + + '
aca
' + + '
acb
' + + '
' + + '' + + '
'; }); afterEach(function () { document.body.removeChild(fixture); delete obj; + delete parent; + delete html; }); it('matches class', function() { @@ -61,6 +89,102 @@ define([path + 'Sorter',], obj.createPlaceholder().className.should.equal('placeholder'); }); + it('isInFlow to overflow hidden', function(){ + parent.innerHTML = html; + var el = parent.querySelector('#el1'); + obj.isInFlow(el).should.equal(false); + }); + + it('isInFlow inner to overflow', function(){ + parent.innerHTML = html; + var el = parent.querySelector('#el2'); + if(!el){ + console.log('phantom issue'); + return; + } + obj.isInFlow(el).should.equal(true); + }); + + it('isInFlow for span', function(){ + parent.innerHTML = html; + var el = parent.querySelector('#elspan'); + obj.isInFlow(el).should.equal(false); + }); + + it('isInFlow for div #a', function(){ + parent.innerHTML = html; + var el = parent.querySelector('#a'); + if(!el){ + console.log('phantom issue'); + return; + } + obj.isInFlow(el).should.equal(true); + }); + + it('isInFlow for div #aa', function(){ + parent.innerHTML = html; + var el = parent.querySelector('#aa'); + if(!el){ + console.log('phantom issue'); + return; + } + obj.isInFlow(el).should.equal(true); + }); + + it('isInFlow for p #aaa', function(){ + parent.innerHTML = html; + var el = parent.querySelector('#aaa'); + if(!el){ + console.log('phantom issue'); + return; + } + obj.isInFlow(el).should.equal(true); + }); + + it('isInFlow for span #aab', function(){ + parent.innerHTML = html; + var el = parent.querySelector('#aab'); + obj.isInFlow(el).should.equal(false); + }); + + it('isInFlow for span #aac with display block', function(){ + parent.innerHTML = html; + var el = parent.querySelector('#aac'); + if(!el) // in phantom doesnt work + return; + obj.isInFlow(el).should.equal(true); + }); + + it('isInFlow for div #ab with float left', function(){ + parent.innerHTML = html; + var el = parent.querySelector('#ab'); + obj.isInFlow(el).should.equal(false); + }); + + it('isInFlow for div #ac in absolute', function(){ + parent.innerHTML = html; + var el = parent.querySelector('#ac'); + obj.isInFlow(el).should.equal(false); + }); + + it('isInFlow for div #acb inside absolute', function(){ + parent.innerHTML = html; + var el = parent.querySelector('#acb'); + if(!el){ + console.log('phantom issue'); + return; + } + obj.isInFlow(el).should.equal(true); + }); + + it('isInFlow for div #ad overflow hidden', function(){ + parent.innerHTML = html; + var el = parent.querySelector('#ad'); + obj.isInFlow(el).should.equal(false); + }); + + + describe('Closest method', function() { var parent2; var parent3;