Browse Source

Extend Style Rules on add instead of simple override

pull/67/head
Artur Arseniev 9 years ago
parent
commit
ac8e0f406e
  1. 13
      src/css_composer/main.js
  2. 112
      src/dom_components/model/Components.js
  3. 8
      src/parser/model/ParserCss.js
  4. 41
      test/specs/css_composer/e2e/CssComposer.js
  5. 352
      test/specs/css_composer/main.js
  6. 4
      test/specs/parser/model/ParserCss.js

13
src/css_composer/main.js

@ -215,11 +215,13 @@ define(function(require) {
/**
* Add a raw collection of rule objects
* This method overrides styles, in case, of already defined rule
* @param {Array<Object>} data Array of rule objects
* @param {Array<Object>} data Array of rule objects, eg . [{selectors: ['class1'], style: {....}}, ..]
* @param {Object} opts Options
* @return {Array<Model>}
* @private
*/
addCollection: function(data){
addCollection: function(data, opts) {
var opt = opts || {};
var result = [];
var d = data instanceof Array ? data : [data];
for(var i = 0, l = d.length; i < l; i++){
@ -237,7 +239,12 @@ define(function(require) {
newSels.push(selec);
}
var model = this.add(newSels, rule.state, rule.maxWidth);
model.set('style', rule.style || {});
if (opt.extend) {
var newStyle = _.extend({}, model.get('style'), rule.style || {});
model.set('style', newStyle);
} else {
model.set('style', rule.style || {});
}
result.push(model);
}
return result;

112
src/dom_components/model/Components.js

@ -1,79 +1,79 @@
define([ 'backbone', 'require'],
function (Backbone, require) {
function (Backbone, require) {
return Backbone.Collection.extend({
return Backbone.Collection.extend({
initialize: function(models, opt){
initialize: function(models, opt){
this.on('add', this.onAdd);
this.on('add', this.onAdd);
this.config = opt && opt.config ? opt.config : null;
this.config = opt && opt.config ? opt.config : null;
// Inject editor
if(opt && opt.sm)
this.editor = opt.sm;
// Inject editor
if(opt && opt.sm)
this.editor = opt.sm;
this.model = function(attrs, options) {
var model;
this.model = function(attrs, options) {
var model;
if(!options.sm && opt && opt.sm)
options.sm = opt.sm;
if(!options.sm && opt && opt.sm)
options.sm = opt.sm;
if(opt && opt.config)
options.config = opt.config;
if(opt && opt.config)
options.config = opt.config;
if(opt && opt.defaultTypes)
options.defaultTypes = opt.defaultTypes;
if(opt && opt.defaultTypes)
options.defaultTypes = opt.defaultTypes;
if(opt && opt.componentTypes)
options.componentTypes = opt.componentTypes;
if(opt && opt.componentTypes)
options.componentTypes = opt.componentTypes;
var df = opt.defaultTypes;
var df = opt.defaultTypes;
for (var it = 0; it < df.length; it++) {
var dfId = df[it].id;
if(dfId == attrs.type) {
model = df[it].model;
break;
}
}
for (var it = 0; it < df.length; it++) {
var dfId = df[it].id;
if(dfId == attrs.type) {
model = df[it].model;
break;
}
}
if(!model) {
// get the last one
model = df[df.length - 1].model;
}
if(!model) {
// get the last one
model = df[df.length - 1].model;
}
return new model(attrs, options);
};
return new model(attrs, options);
};
},
add: function(models, opt){
if(typeof models === 'string'){
var parsed = this.editor.get('Parser').parseHtml(models);
models = parsed.html;
},
var cssc = this.editor.get('CssComposer');
if(parsed.css && cssc){
var added = cssc.addCollection(parsed.css);
}
}
add: function(models, opt){
if(typeof models === 'string'){
var parsed = this.editor.get('Parser').parseHtml(models);
models = parsed.html;
return Backbone.Collection.prototype.add.apply(this, [models, opt]);
},
var cssc = this.editor.get('CssComposer');
if(parsed.css && cssc){
var added = cssc.addCollection(parsed.css, {extend: 1});
}
}
onAdd: function(model, c, opts){
var style = model.get('style');
return Backbone.Collection.prototype.add.apply(this, [models, opt]);
},
if(!_.isEmpty(style) && this.editor){
var cssC = this.editor.get('CssComposer');
var newClass = this.editor.get('SelectorManager').add(model.cid);
model.set({style:{}});
model.get('classes').add(newClass);
var rule = cssC.add(newClass);
rule.set('style', style);
}
onAdd: function(model, c, opts){
var style = model.get('style');
if(!_.isEmpty(style) && this.editor){
var cssC = this.editor.get('CssComposer');
var newClass = this.editor.get('SelectorManager').add(model.cid);
model.set({style:{}});
model.get('classes').add(newClass);
var rule = cssC.add(newClass);
rule.set('style', style);
}
},
});
});
});

8
src/parser/model/ParserCss.js

@ -44,9 +44,13 @@ define(function(require) {
var sels = node.selectorText;
// It's a CSSMediaRule
if(node.cssRules){
if(node.cssRules) {
var subRules = this.parseNode(node);
var width = node.media.mediaText.match(/-width:(.*)\)/i)[1];
var widthA = node.media.mediaText.match(/-width:(.*)\)/i);
if(!widthA) {
continue;
}
var width = widthA[1];
for(var s = 0, lens = subRules.length; s < lens; s++){
var subRule = subRules[s];
subRule.maxWidth = width ? width.trim() : '';

41
test/specs/css_composer/e2e/CssComposer.js

@ -109,6 +109,47 @@ define(['GrapesJS'],function(GrapesJS) {
coll1.should.deep.equal(coll2);
});
it("Extend css rule style, if requested", function() {
var style1 = {color: 'red', width: '10px'};
var style2 = {height: '20px', width: '20px'};
var rule1 = {
selectors: ['test1'],
style: style1,
};
var rule2 = {
selectors: ['test1'],
style: style2,
};
var ruleOut = cssc.addCollection(rule1)[0];
// ruleOut is a Model
ruleOut = JSON.parse(JSON.stringify(ruleOut));
var ruleResult = {
maxWidth: '',
selectors: [{
active: true,
label: 'test1',
name: 'test1',
type: 'class',
}],
state: '',
stylable: true,
style: {
color: 'red',
width: '10px'
}
};
ruleOut.should.deep.equal(ruleResult);
var ruleOut = cssc.addCollection(rule2, {extend: 1})[0];
ruleOut = JSON.parse(JSON.stringify(ruleOut));
ruleResult.style = {
color: 'red',
height: '20px',
width: '20px',
}
ruleOut.should.deep.equal(ruleResult);
});
it('Add raw rule objects with width via addCollection', function() {
var coll1 = cssc.addCollection(rulesSet2);
coll1[2].get('maxWidth').should.equal(rulesSet2[2].maxWidth);

352
test/specs/css_composer/main.js

@ -1,180 +1,180 @@
var modulePath = './../../../test/specs/css_composer';
define([
'CssComposer',
modulePath + '/model/CssModels',
modulePath + '/view/CssRuleView',
modulePath + '/view/CssRulesView',
modulePath + '/e2e/CssComposer',
'./../test_utils.js'
],
function(
CssComposer,
Models,
CssRuleView,
CssRulesView,
e2e,
utils
) {
describe('Css Composer', function() {
describe('Main', function() {
var obj;
var config;
var storagMock = utils.storageMock();
var editorModel = {
getCss: function(){return 'testCss';},
getCacheLoad: function(){
return storagMock.load();
}
};
var setSmConfig = function(){
config.stm = storagMock;
config.stm.getConfig = function(){
return { storeCss: 1, storeStyles: 1}
};
};
var setEm = function(){
config.em = editorModel;
}
beforeEach(function () {
config = {};
obj = new CssComposer().init(config);
});
afterEach(function () {
delete obj;
});
it('Object exists', function() {
CssComposer.should.be.exist;
});
it('storageKey returns array', function() {
obj.storageKey().should.be.instanceOf(Array);
});
it('storageKey returns correct composition', function() {
setSmConfig();
obj.storageKey().should.eql(['css', 'styles']);
});
it('Store data', function() {
setSmConfig();
setEm();
var expected = { css: 'testCss', styles: '[]',};
obj.store(1).should.deep.equal(expected);
});
it("Rules are empty", function() {
obj.getAll().length.should.equal(0);
});
it('Create new rule with correct selectors', function() {
var sel = new obj.Selectors();
var s1 = sel.add({name: 'test1'});
var rule = obj.add(sel.models);
rule.get('selectors').at(0).should.deep.equal(s1);
});
it('Create new rule correctly', function() {
var sel = new obj.Selectors();
var s1 = sel.add({name: 'test1'});
var rule = obj.add(sel.models, 'state1', 'width1');
rule.get('state').should.equal('state1');
rule.get('maxWidth').should.equal('width1');
});
it("Add rule to collection", function() {
var sel = new obj.Selectors([{name: 'test1'}]);
var rule = obj.add(sel.models);
obj.getAll().length.should.equal(1);
obj.getAll().at(0).get('selectors').at(0).get('name').should.equal('test1');
});
it("Returns correct rule with the same selector", function() {
var sel = new obj.Selectors([{name: 'test1'}]);
var rule1 = obj.add(sel.models);
var rule2 = obj.get(sel.models);
rule1.should.deep.equal(rule2);
});
it("Returns correct rule with the same selectors", function() {
var sel1 = new obj.Selectors([{name: 'test1'}]);
var rule1 = obj.add(sel1.models);
var sel2 = new obj.Selectors([{name: 'test21'}, {name: 'test22'}]);
var rule2 = obj.add(sel2.models);
var rule3 = obj.get(sel2.models);
rule3.should.deep.equal(rule2);
});
it("Do not create multiple rules with the same name selectors", function() {
var sel1 = new obj.Selectors([{name: 'test21'}, {name: 'test22'}]);
var rule1 = obj.add(sel1.models);
var sel2 = new obj.Selectors([{name: 'test22'}, {name: 'test21'}]);
var rule2 = obj.add(sel2.models);
rule2.should.deep.equal(rule1);
});
it("Don't duplicate rules", function() {
var sel = new obj.Selectors([]);
var s1 = sel.add({name: 'test1'});
var s2 = sel.add({name: 'test2'});
var s3 = sel.add({name: 'test3'});
var rule1 = obj.add([s1, s3]);
var rule2 = obj.add([s3, s1]);
rule2.should.deep.equal(rule1);
});
it("Returns correct rule with the same mixed selectors", function() {
var sel = new obj.Selectors([]);
var s1 = sel.add({name: 'test1'});
var s2 = sel.add({name: 'test2'});
var s3 = sel.add({name: 'test3'});
var rule1 = obj.add([s1, s3]);
var rule2 = obj.get([s3, s1]);
rule2.should.deep.equal(rule1);
});
it("Returns correct rule with the same selectors and state", function() {
var sel = new obj.Selectors([]);
var s1 = sel.add({name: 'test1'});
var s2 = sel.add({name: 'test2'});
var s3 = sel.add({name: 'test3'});
var rule1 = obj.add([s1, s3], 'hover');
var rule2 = obj.get([s3, s1], 'hover');
rule2.should.deep.equal(rule1);
});
it("Returns correct rule with the same selectors, state and width", function() {
var sel = new obj.Selectors([]);
var s1 = sel.add({name: 'test1'});
var rule1 = obj.add([s1], 'hover','1');
var rule2 = obj.get([s1], 'hover', '1');
rule2.should.deep.equal(rule1);
});
it("Renders correctly", function() {
obj.render().should.be.ok;
});
});
Models.run();
CssRuleView.run();
CssRulesView.run();
e2e.run();
});
'CssComposer',
modulePath + '/model/CssModels',
modulePath + '/view/CssRuleView',
modulePath + '/view/CssRulesView',
modulePath + '/e2e/CssComposer',
'./../test_utils.js'
],
function(
CssComposer,
Models,
CssRuleView,
CssRulesView,
e2e,
utils
) {
describe('Css Composer', function() {
describe('Main', function() {
var obj;
var config;
var storagMock = utils.storageMock();
var editorModel = {
getCss: function(){return 'testCss';},
getCacheLoad: function(){
return storagMock.load();
}
};
var setSmConfig = function(){
config.stm = storagMock;
config.stm.getConfig = function(){
return { storeCss: 1, storeStyles: 1}
};
};
var setEm = function(){
config.em = editorModel;
}
beforeEach(function () {
config = {};
obj = new CssComposer().init(config);
});
afterEach(function () {
delete obj;
});
it('Object exists', function() {
CssComposer.should.be.exist;
});
it('storageKey returns array', function() {
obj.storageKey().should.be.instanceOf(Array);
});
it('storageKey returns correct composition', function() {
setSmConfig();
obj.storageKey().should.eql(['css', 'styles']);
});
it('Store data', function() {
setSmConfig();
setEm();
var expected = { css: 'testCss', styles: '[]',};
obj.store(1).should.deep.equal(expected);
});
it("Rules are empty", function() {
obj.getAll().length.should.equal(0);
});
it('Create new rule with correct selectors', function() {
var sel = new obj.Selectors();
var s1 = sel.add({name: 'test1'});
var rule = obj.add(sel.models);
rule.get('selectors').at(0).should.deep.equal(s1);
});
it('Create new rule correctly', function() {
var sel = new obj.Selectors();
var s1 = sel.add({name: 'test1'});
var rule = obj.add(sel.models, 'state1', 'width1');
rule.get('state').should.equal('state1');
rule.get('maxWidth').should.equal('width1');
});
it("Add rule to collection", function() {
var sel = new obj.Selectors([{name: 'test1'}]);
var rule = obj.add(sel.models);
obj.getAll().length.should.equal(1);
obj.getAll().at(0).get('selectors').at(0).get('name').should.equal('test1');
});
it("Returns correct rule with the same selector", function() {
var sel = new obj.Selectors([{name: 'test1'}]);
var rule1 = obj.add(sel.models);
var rule2 = obj.get(sel.models);
rule1.should.deep.equal(rule2);
});
it("Returns correct rule with the same selectors", function() {
var sel1 = new obj.Selectors([{name: 'test1'}]);
var rule1 = obj.add(sel1.models);
var sel2 = new obj.Selectors([{name: 'test21'}, {name: 'test22'}]);
var rule2 = obj.add(sel2.models);
var rule3 = obj.get(sel2.models);
rule3.should.deep.equal(rule2);
});
it("Do not create multiple rules with the same name selectors", function() {
var sel1 = new obj.Selectors([{name: 'test21'}, {name: 'test22'}]);
var rule1 = obj.add(sel1.models);
var sel2 = new obj.Selectors([{name: 'test22'}, {name: 'test21'}]);
var rule2 = obj.add(sel2.models);
rule2.should.deep.equal(rule1);
});
it("Don't duplicate rules", function() {
var sel = new obj.Selectors([]);
var s1 = sel.add({name: 'test1'});
var s2 = sel.add({name: 'test2'});
var s3 = sel.add({name: 'test3'});
var rule1 = obj.add([s1, s3]);
var rule2 = obj.add([s3, s1]);
rule2.should.deep.equal(rule1);
});
it("Returns correct rule with the same mixed selectors", function() {
var sel = new obj.Selectors([]);
var s1 = sel.add({name: 'test1'});
var s2 = sel.add({name: 'test2'});
var s3 = sel.add({name: 'test3'});
var rule1 = obj.add([s1, s3]);
var rule2 = obj.get([s3, s1]);
rule2.should.deep.equal(rule1);
});
it("Returns correct rule with the same selectors and state", function() {
var sel = new obj.Selectors([]);
var s1 = sel.add({name: 'test1'});
var s2 = sel.add({name: 'test2'});
var s3 = sel.add({name: 'test3'});
var rule1 = obj.add([s1, s3], 'hover');
var rule2 = obj.get([s3, s1], 'hover');
rule2.should.deep.equal(rule1);
});
it("Returns correct rule with the same selectors, state and width", function() {
var sel = new obj.Selectors([]);
var s1 = sel.add({name: 'test1'});
var rule1 = obj.add([s1], 'hover','1');
var rule2 = obj.get([s1], 'hover', '1');
rule2.should.deep.equal(rule1);
});
it("Renders correctly", function() {
obj.render().should.be.ok;
});
});
Models.run();
CssRuleView.run();
CssRulesView.run();
e2e.run();
});
});

4
test/specs/parser/model/ParserCss.js

@ -133,7 +133,7 @@ define([path + 'model/ParserCss',],
obj.parse(str).should.deep.equal(result);
});
// Phantom don't find 'node.conditionText' so will skip it
// Phantom doesn't find 'node.conditionText' so will skip it
it.skip('Parse rules inside media queries', function() {
var str = '.test1:hover{ color:white }@media (max-width: 992px){ .test1.test2:hover{ color:red } .test2{ color: blue }}';
var result = [{
@ -158,4 +158,4 @@ define([path + 'model/ParserCss',],
}
};
});
});

Loading…
Cancel
Save