Browse Source

Update CSSRule, maxWidth -> mediaText (more generic)

pull/79/merge
Artur Arseniev 9 years ago
parent
commit
9841c54a8b
  1. 2
      bower.json
  2. 10
      dist/grapes.min.js
  3. 2
      package.json
  4. 10
      src/code_manager/model/CssGenerator.js
  5. 4
      src/css_composer/main.js
  6. 4
      src/css_composer/model/CssRule.js
  7. 8
      src/css_composer/view/CssRuleView.js
  8. 12
      src/parser/model/ParserCss.js
  9. 275
      src/style_manager/view/SectorsView.js
  10. 8
      test/specs/code_manager/model/CodeModels.js
  11. 6
      test/specs/css_composer/e2e/CssComposer.js
  12. 2
      test/specs/css_composer/main.js
  13. 6
      test/specs/css_composer/model/CssModels.js
  14. 4
      test/specs/css_composer/view/CssRuleView.js
  15. 22
      test/specs/parser/model/ParserCss.js

2
bower.json

@ -1,7 +1,7 @@
{
"name": "grapesjs",
"description": "Open source Web Template Editor",
"version": "0.5.24",
"version": "0.5.27",
"author": "Artur Arseniev",
"homepage": "http://grapesjs.com",
"main": [

10
dist/grapes.min.js

File diff suppressed because one or more lines are too long

2
package.json

@ -1,7 +1,7 @@
{
"name": "grapesjs",
"description": "Open source Web Template Editor",
"version": "0.5.24",
"version": "0.5.27",
"author": "Artur Arseniev",
"license": "BSD-3-Clause",
"homepage": "http://grapesjs.com",

10
src/code_manager/model/CssGenerator.js

@ -70,8 +70,8 @@ define(['backbone'],
if(cssc){
var rules = cssc.getAll();
var mediaRules = {};
rules.each(function(rule){
var width = rule.get('maxWidth');
rules.each(function(rule) {
var width = rule.get('mediaText');
// If width setted will render it later
if(width){
@ -93,8 +93,10 @@ define(['backbone'],
for(var i = 0, len = meRules.length; i < len; i++){
ruleC += this.buildFromRule(meRules[i]);
}
if(ruleC)
code += '@media (max-width: ' + ruleW + '){' + ruleC + '}';
if (ruleC) {
code += '@media ' + ruleW + '{' + ruleC + '}';
}
}
}

4
src/css_composer/main.js

@ -170,7 +170,7 @@ define(function(require) {
return rule;
else {
opt.state = s;
opt.maxWidth = w;
opt.mediaText = w;
opt.selectors = '';
rule = new CssRule(opt);
rule.get('selectors').add(selectors);
@ -244,7 +244,7 @@ define(function(require) {
newSels.push(selec);
}
var model = this.add(newSels, rule.state, rule.maxWidth, rule);
var model = this.add(newSels, rule.state, rule.mediaText, rule);
if (opt.extend) {
var newStyle = _.extend({}, model.get('style'), rule.style || {});
model.set('style', newStyle);

4
src/css_composer/model/CssRule.js

@ -13,7 +13,7 @@ define(['backbone', './Selectors'],
style: {},
// On which device width this rule should be rendered, eg. @media (max-width: 1000px)
maxWidth: '',
mediaText: '',
// State of the rule, eg: hover | pressed | focused
state: '',
@ -77,7 +77,7 @@ define(['backbone', './Selectors'],
if(this.get('state') !== st)
return f;
if(this.get('maxWidth') !== wd)
if(this.get('mediaText') !== wd)
return f;
return true;

8
src/css_composer/view/CssRuleView.js

@ -9,7 +9,7 @@ define(['backbone'],
this.listenTo(this.model, 'change:style', this.render);
this.listenTo(this.model, 'change:state', this.render);
this.listenTo(this.model, 'destroy remove', this.remove);
this.listenTo(this.model, 'change:maxWidth', this.render);
this.listenTo(this.model, 'change:mediaText', this.render);
this.listenTo(this.model.get('selectors'), 'change', this.selChanged);
},
@ -60,15 +60,15 @@ define(['backbone'],
this.selStr = this.renderSelectors();
var prpStr = this.renderProperties();
var stateStr = this.model.get('state');
var width = this.model.get('maxWidth');
var mediaText = this.model.get('mediaText');
if(this.selStr){
stateStr = stateStr ? ':' + stateStr : '';
block = prpStr !== '' ? '{' + prpStr + '}' : '';
}
o = this.selStr && block ? this.selStr + stateStr + block : '';
if(width && o)
o = '@media (max-width: ' + width + '){' + o + '}';
if(mediaText && o)
o = '@media ' + mediaText + '{' + o + '}';
this.$el.html(o);
return this;

12
src/parser/model/ParserCss.js

@ -57,15 +57,13 @@ define(function(require) {
// It's a CSSMediaRule
if(node.cssRules) {
var subRules = this.parseNode(node);
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 mediaText = node.media.mediaText;
for( var s = 0, lens = subRules.length; s < lens; s++) {
var subRule = subRules[s];
subRule.maxWidth = width ? width.trim() : '';
subRule.mediaText = mediaText ? mediaText.trim() : '';
}
result = result.concat(subRules);
}

275
src/style_manager/view/SectorsView.js

@ -1,139 +1,140 @@
define(['backbone', './SectorView'],
function (Backbone, SectorView) {
return Backbone.View.extend({
initialize: function(o) {
this.config = o.config || {};
this.pfx = this.config.stylePrefix || '';
this.target = o.target || {};
// The taget that will emit events for properties
this.propTarget = {};
_.extend(this.propTarget, Backbone.Events);
this.listenTo( this.collection, 'add', this.addTo);
this.listenTo( this.collection, 'reset', this.render);
this.listenTo( this.target, 'change:selectedComponent targetClassAdded targetClassRemoved targetClassUpdated ' +
'targetStateUpdated targetStyleUpdated change:device', this.targetUpdated);
},
/**
* Add to collection
* @param {Object} model Model
* @return {Object}
* @private
* */
addTo: function(model){
this.addToCollection(model);
},
/**
* Fired when target is updated
* @private
*/
targetUpdated: function() {
var el = this.target.get('selectedComponent');
if(!el)
return;
var previewMode = this.target.get('Config').devicePreviewMode;
var classes = el.get('classes');
var pt = this.propTarget;
var device = this.target.getDeviceModel();
var state = !previewMode ? el.get('state') : '';
var deviceW = device && !previewMode ? device.get('width') : '';
pt.helper = null;
if(classes.length){
var cssC = this.target.get('CssComposer');
var valid = _.filter(classes.models, function(item){
return item.get('active');
});
var iContainer = cssC.get(valid, state, deviceW);
if(!iContainer){
iContainer = cssC.add(valid, state, deviceW);
// Get styles from the component
iContainer.set('style', el.get('style'));
//cssC.addRule(iContainer);
el.set('style', {});
}else{
// Ensure to clean element
//if(classes.length == 1)
//el.set('style', {});
}
// If the state is not empty, there should be a helper rule in play
// The helper rule will get the same style of the iContainer
if(state){
var clm = this.target.get('SelectorManager');
var helperClass = clm.add('hc-state');
var helperRule = cssC.get([helperClass]);
if(!helperRule)
helperRule = cssC.add([helperClass]);
else{
// I will make it last again, otherwise it could be overridden
cssC.getAll().remove(helperRule);
cssC.getAll().add(helperRule);
}
helperRule.set('style', iContainer.get('style'));
pt.helper = helperRule;
}
pt.model = iContainer;
pt.trigger('update');
return;
}
pt.model = el;
pt.trigger('update');
},
/**
* Add new object to collection
* @param {Object} model Model
* @param {Object} fragmentEl collection
* @return {Object} Object created
* @private
* */
addToCollection: function(model, fragmentEl){
var fragment = fragmentEl || null;
var viewObject = SectorView;
var view = new viewObject({
model: model,
id: this.pfx + model.get('name').replace(' ','_').toLowerCase(),
name: model.get('name'),
properties: model.get('properties'),
target: this.target,
propTarget: this.propTarget,
config: this.config,
});
var rendered = view.render().el;
if(fragment){
fragment.appendChild(rendered);
}else{
this.$el.append(rendered);
}
return rendered;
},
render: function() {
var fragment = document.createDocumentFragment();
this.$el.empty();
this.collection.each(function(model){
this.addToCollection(model, fragment);
}, this);
this.$el.attr('id', this.pfx + 'sectors');
this.$el.append(fragment);
return this;
}
});
function (Backbone, SectorView) {
return Backbone.View.extend({
initialize: function(o) {
this.config = o.config || {};
this.pfx = this.config.stylePrefix || '';
this.target = o.target || {};
// The taget that will emit events for properties
this.propTarget = {};
_.extend(this.propTarget, Backbone.Events);
this.listenTo( this.collection, 'add', this.addTo);
this.listenTo( this.collection, 'reset', this.render);
this.listenTo( this.target, 'change:selectedComponent targetClassAdded targetClassRemoved targetClassUpdated ' +
'targetStateUpdated targetStyleUpdated change:device', this.targetUpdated);
},
/**
* Add to collection
* @param {Object} model Model
* @return {Object}
* @private
* */
addTo: function(model){
this.addToCollection(model);
},
/**
* Fired when target is updated
* @private
*/
targetUpdated: function() {
var el = this.target.get('selectedComponent');
if(!el)
return;
var previewMode = this.target.get('Config').devicePreviewMode;
var classes = el.get('classes');
var pt = this.propTarget;
var device = this.target.getDeviceModel();
var state = !previewMode ? el.get('state') : '';
var mediaText = device && !previewMode ?
'(max-width: ' + device.get('width') + ')' : '';
pt.helper = null;
if(classes.length){
var cssC = this.target.get('CssComposer');
var valid = _.filter(classes.models, function(item) {
return item.get('active');
});
var iContainer = cssC.get(valid, state, mediaText);
if(!iContainer){
iContainer = cssC.add(valid, state, mediaText);
// Get styles from the component
iContainer.set('style', el.get('style'));
//cssC.addRule(iContainer);
el.set('style', {});
}else{
// Ensure to clean element
//if(classes.length == 1)
//el.set('style', {});
}
// If the state is not empty, there should be a helper rule in play
// The helper rule will get the same style of the iContainer
if(state){
var clm = this.target.get('SelectorManager');
var helperClass = clm.add('hc-state');
var helperRule = cssC.get([helperClass]);
if(!helperRule)
helperRule = cssC.add([helperClass]);
else{
// I will make it last again, otherwise it could be overridden
cssC.getAll().remove(helperRule);
cssC.getAll().add(helperRule);
}
helperRule.set('style', iContainer.get('style'));
pt.helper = helperRule;
}
pt.model = iContainer;
pt.trigger('update');
return;
}
pt.model = el;
pt.trigger('update');
},
/**
* Add new object to collection
* @param {Object} model Model
* @param {Object} fragmentEl collection
* @return {Object} Object created
* @private
* */
addToCollection: function(model, fragmentEl){
var fragment = fragmentEl || null;
var viewObject = SectorView;
var view = new viewObject({
model: model,
id: this.pfx + model.get('name').replace(' ','_').toLowerCase(),
name: model.get('name'),
properties: model.get('properties'),
target: this.target,
propTarget: this.propTarget,
config: this.config,
});
var rendered = view.render().el;
if(fragment){
fragment.appendChild(rendered);
}else{
this.$el.append(rendered);
}
return rendered;
},
render: function() {
var fragment = document.createDocumentFragment();
this.$el.empty();
this.collection.each(function(model){
this.addToCollection(model, fragment);
}, this);
this.$el.attr('id', this.pfx + 'sectors');
this.$el.append(fragment);
return this;
}
});
});

8
test/specs/code_manager/model/CodeModels.js

@ -174,7 +174,7 @@ define([path + 'HtmlGenerator',
var cssc = newCssComp();
var rule = cssc.add([cls1, cls2]);
rule.set('style',{'prop1':'value1'});
rule.set('maxWidth', '999px');
rule.set('mediaText', '(max-width: 999px)');
this.obj.build(comp, cssc).should.equal('@media (max-width: 999px){.class1.class2{prop1:value1;}}');
});
@ -191,12 +191,12 @@ define([path + 'HtmlGenerator',
var rule2 = cssc.add(cls2);
rule2.set('style',{'prop2':'value2'});
var rule3 = cssc.add(cls1, '', '999px');
var rule3 = cssc.add(cls1, '', '(max-width: 999px)');
rule3.set('style',{'prop3':'value3'});
var rule4 = cssc.add(cls2, '', '999px');
var rule4 = cssc.add(cls2, '', '(max-width: 999px)');
rule4.set('style',{'prop4':'value4'});
var rule5 = cssc.add(cls1, '', '100px');
var rule5 = cssc.add(cls1, '', '(max-width: 100px)');
rule5.set('style',{'prop5':'value5'});
this.obj.build(comp, cssc).should.equal('.class1.class2{prop1:value1;}.class2{prop2:value2;}'+

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

@ -39,7 +39,7 @@ define(['GrapesJS'],function(GrapesJS) {
rulesSet2 = [
{ selectors: [{name: 'test1'}, {name: 'test2'}], state:':active' },
{ selectors: [{name: 'test2'}, {name: 'test3'}] },
{ selectors: [{name: 'test3'}], maxWidth:'900px' }
{ selectors: [{name: 'test3'}], mediaText:'(max-width: 900px)' }
];
});
@ -125,7 +125,7 @@ define(['GrapesJS'],function(GrapesJS) {
// ruleOut is a Model
ruleOut = JSON.parse(JSON.stringify(ruleOut));
var ruleResult = {
maxWidth: '',
mediaText: '',
selectors: [{
active: true,
label: 'test1',
@ -154,7 +154,7 @@ define(['GrapesJS'],function(GrapesJS) {
it('Add raw rule objects with width via addCollection', function() {
var coll1 = cssc.addCollection(rulesSet2);
coll1[2].get('maxWidth').should.equal(rulesSet2[2].maxWidth);
coll1[2].get('mediaText').should.equal(rulesSet2[2].mediaText);
});
});

2
test/specs/css_composer/main.js

@ -88,7 +88,7 @@ define([
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');
rule.get('mediaText').should.equal('width1');
});
it("Add rule to collection", function() {

6
test/specs/css_composer/model/CssModels.js

@ -47,11 +47,11 @@ define([path + 'CssRule',
this.obj.compare([s1, s2], 'hover').should.equal(true);
});
it('Compare with different maxWidth', function() {
it('Compare with different mediaText', function() {
var s1 = this.obj.get('selectors').add({ name: 'test1' });
var s2 = this.obj.get('selectors').add({ name: 'test2' });
this.obj.set('state','hover');
this.obj.set('maxWidth','1000');
this.obj.set('mediaText','1000');
this.obj.compare([s1, s2]).should.equal(false);
this.obj.compare([s1, s2], 'hover').should.equal(false);
this.obj.compare([s2, s1], 'hover', '1000').should.equal(true);
@ -81,4 +81,4 @@ define([path + 'CssRule',
}
};
});
});

4
test/specs/css_composer/view/CssRuleView.js

@ -101,7 +101,7 @@ define([path + 'CssRuleView', 'CssComposer/model/CssRule'],
it('Render media queries', function() {
this.regView.model.set('style', {'prop':'value'});
this.regView.model.set('maxWidth', '999px');
this.regView.model.set('mediaText', '(max-width: 999px)');
this.regView.$el.html().should.equal('@media (max-width: 999px){.test1.test2{prop:value;}}');
});
@ -117,4 +117,4 @@ define([path + 'CssRuleView', 'CssComposer/model/CssRule'],
}
};
});
});

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

@ -122,19 +122,31 @@ define([path + 'model/ParserCss',],
});
// Phantom don't find 'node.conditionText' so will skip it
it.skip('Parse rule inside media query', function() {
it('Parse rule inside media query', function() {
var str = '@media only screen and (max-width: 992px){ .test1.test2:hover{ color:red }}';
var result = {
selectors: ['test1', 'test2'],
style: { color: 'red'},
state: 'hover',
mediaText: 'only screen and (max-width: 992px)',
};
obj.parse(str).should.deep.equal(result);
});
// Phantom don't find 'node.conditionText' so will skip it
it('Parse rule inside media query', function() {
var str = '@media (max-width: 992px){ .test1.test2:hover{ color:red }}';
var result = {
selectors: ['test1', 'test2'],
style: { color: 'red'},
state: 'hover',
maxWidth: '992px',
mediaText: '(max-width: 992px)',
};
obj.parse(str).should.deep.equal(result);
});
// Phantom doesn't find 'node.conditionText' so will skip it
it.skip('Parse rules inside media queries', function() {
it('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 = [{
selectors: ['test1'],
@ -144,11 +156,11 @@ define([path + 'model/ParserCss',],
selectors: ['test1', 'test2'],
style: { color: 'red'},
state: 'hover',
maxWidth: '992px',
mediaText: '(max-width: 992px)',
},{
selectors: ['test2'],
style: { color: 'blue'},
maxWidth: '992px',
mediaText: '(max-width: 992px)',
}];
obj.parse(str).should.deep.equal(result);
});

Loading…
Cancel
Save