Browse Source

Merge branch 'improve-sm-extend' into dev

pull/1518/head
Artur Arseniev 7 years ago
parent
commit
afd1dc3516
  1. 2
      dist/css/grapes.min.css
  2. 11
      src/domain_abstract/model/TypeableCollection.js
  3. 1
      src/domain_abstract/ui/Input.js
  4. 1
      src/domain_abstract/ui/InputNumber.js
  5. 9
      src/style_manager/model/Layer.js
  6. 9
      src/style_manager/model/Layers.js
  7. 317
      src/style_manager/model/Property.js
  8. 15
      src/style_manager/model/PropertyComposite.js
  9. 36
      src/style_manager/model/PropertyStack.js
  10. 12
      src/style_manager/view/LayerView.js
  11. 7
      src/style_manager/view/PropertyIntegerView.js
  12. 5
      src/style_manager/view/PropertySliderView.js
  13. 5
      src/style_manager/view/PropertyStackView.js
  14. 8
      src/style_manager/view/PropertyView.js
  15. 1
      src/styles/scss/_gjs_style_manager.scss
  16. 1
      test/specs/style_manager/index.js
  17. 4
      test/specs/style_manager/model/Models.js

2
dist/css/grapes.min.css

File diff suppressed because one or more lines are too long

11
src/domain_abstract/model/TypeableCollection.js

@ -1,3 +1,4 @@
import { isFunction } from 'underscore';
const Model = Backbone.Model;
const View = Backbone.View;
@ -112,8 +113,14 @@ export default {
const ModelInst = type ? type.model : baseType.model;
const ViewInst = type ? type.view : baseType.view;
let { model, view, isType } = definition;
model = model instanceof Model ? model : ModelInst.extend(model || {});
view = view instanceof View ? view : ViewInst.extend(view || {});
model =
model instanceof Model || isFunction(model)
? model
: ModelInst.extend(model || {});
view =
view instanceof View || isFunction(view)
? view
: ViewInst.extend(view || {});
if (type) {
type.model = model;

1
src/domain_abstract/ui/Input.js

@ -75,6 +75,7 @@ module.exports = Backbone.View.extend({
},
render() {
this.inputEl = null;
const el = this.$el;
el.addClass(this.inputClass());
el.html(this.template());

1
src/domain_abstract/ui/InputNumber.js

@ -269,6 +269,7 @@ module.exports = Input.extend({
render() {
Input.prototype.render.call(this);
this.unitEl = null;
const unit = this.getUnitEl();
unit &&
this.$el

9
src/style_manager/model/Layer.js

@ -33,6 +33,15 @@ module.exports = Backbone.Model.extend({
}
},
/**
* Get property at some index
* @param {Number} index
* @return {Object}
*/
getPropertyAt(index) {
return this.get('properties').at(index);
},
getPropertyValue(property) {
let result = '';
this.get('properties').each(prop => {

9
src/style_manager/model/Layers.js

@ -18,6 +18,11 @@ module.exports = Backbone.Collection.extend({
this.idx = 1;
},
getSeparator() {
const { property } = this;
return property ? property.get('layerSeparator') : ', ';
},
/**
* Get layers from a value string (for not detached properties),
* example of input:
@ -35,7 +40,7 @@ module.exports = Backbone.Collection.extend({
var cleaned = match.replace(/,\s*/g, ',');
value = value.replace(match, cleaned);
});
const layerValues = value ? value.split(', ') : [];
const layerValues = value ? value.split(this.getSeparator()) : [];
layerValues.forEach(layerValue => {
layers.push({ properties: this.properties.parseValue(layerValue) });
});
@ -101,7 +106,7 @@ module.exports = Backbone.Collection.extend({
getFullValue() {
let result = [];
this.each(layer => result.push(layer.getFullValue()));
return result.join(', ');
return result.join(this.getSeparator());
},
getPropertyValues(property) {

317
src/style_manager/model/Property.js

@ -1,152 +1,181 @@
import { isUndefined, isString } from 'underscore';
module.exports = require('backbone').Model.extend({
defaults: {
name: '',
property: '',
type: '',
defaults: '',
info: '',
value: '',
icon: '',
functionName: '',
status: '',
visible: true,
fixedValues: ['initial', 'inherit'],
// If true to the value will be added '!important'
important: 0,
// If true, will be hidden by default and will show up only for targets
// which require this property (via `stylable-require`)
// Use case:
// you can add all SVG CSS properties with toRequire as true
// and then require them on SVG Components
toRequire: 0
},
initialize(opt) {
var o = opt || {};
var name = this.get('name');
var prop = this.get('property');
if (!name) {
this.set(
'name',
prop.charAt(0).toUpperCase() + prop.slice(1).replace(/-/g, ' ')
);
}
const init = this.init && this.init.bind(this);
init && init();
},
/**
* Clear the value
* @return {this}
*/
clearValue(opts = {}) {
this.set({ value: undefined }, opts);
return this;
},
/**
* Update value
* @param {any} value
* @param {Boolen} [complete=true] Indicates if it's a final state
* @param {Object} [opts={}] Options
*/
setValue(value, complete = 1, opts = {}) {
const parsed = this.parseValue(value);
this.set(parsed, { ...opts, avoidStore: 1 });
// It's important to set an empty value, otherwise the
// UndoManager won't see the change
if (complete) {
this.set('value', '', opts);
this.set(parsed, opts);
}
},
/**
* Like `setValue` but, in addition, prevents the update of the input element
* as the changes should come from the input itself.
* This method is useful with the definition of custom properties
* @param {any} value
* @param {Boolen} [complete=true] Indicates if it's a final state
* @param {Object} [opts={}] Options
*/
setValueFromInput(value, complete, opts = {}) {
this.setValue(value, complete, { ...opts, fromInput: 1 });
},
const Property = require('backbone').Model.extend(
{
defaults: {
name: '',
property: '',
type: '',
defaults: '',
info: '',
value: '',
icon: '',
functionName: '',
status: '',
visible: true,
fixedValues: ['initial', 'inherit'],
// If true, the property will be forced to be full width
full: 0,
// If true to the value will be added '!important'
important: 0,
// If true, will be hidden by default and will show up only for targets
// which require this property (via `stylable-require`)
// Use case:
// you can add all SVG CSS properties with toRequire as true
// and then require them on SVG Components
toRequire: 0
},
initialize(props = {}, opts = {}) {
var name = this.get('name');
var prop = this.get('property');
if (!name) {
this.set(
'name',
prop.charAt(0).toUpperCase() + prop.slice(1).replace(/-/g, ' ')
);
}
Property.callInit(this, props, opts);
},
init() {},
/**
* Clear the value
* @return {this}
*/
clearValue(opts = {}) {
this.set({ value: undefined }, opts);
return this;
},
/**
* Update value
* @param {any} value
* @param {Boolen} [complete=true] Indicates if it's a final state
* @param {Object} [opts={}] Options
*/
setValue(value, complete = 1, opts = {}) {
const parsed = this.parseValue(value);
this.set(parsed, { ...opts, avoidStore: 1 });
// It's important to set an empty value, otherwise the
// UndoManager won't see the change
if (complete) {
this.set('value', '', opts);
this.set(parsed, opts);
}
},
/**
* Like `setValue` but, in addition, prevents the update of the input element
* as the changes should come from the input itself.
* This method is useful with the definition of custom properties
* @param {any} value
* @param {Boolen} [complete=true] Indicates if it's a final state
* @param {Object} [opts={}] Options
*/
setValueFromInput(value, complete, opts = {}) {
this.setValue(value, complete, { ...opts, fromInput: 1 });
},
/**
* Parse a raw value, generally fetched from the target, for this property
* @param {string} value Raw value string
* @return {Object}
* @example
* // example with an Input type
* prop.parseValue('translateX(10deg)');
* // -> { value: 10, unit: 'deg', functionName: 'translateX' }
*
*/
parseValue(value, opts = {}) {
const result = { value };
const imp = '!important';
if (isString(value) && value.indexOf(imp) !== -1) {
result.value = value.replace(imp, '').trim();
result.important = 1;
}
if (!this.get('functionName') && !opts.complete) {
return result;
}
const args = [];
let valueStr = `${result.value}`;
let start = valueStr.indexOf('(') + 1;
let end = valueStr.lastIndexOf(')');
result.functionName = valueStr.substring(0, start - 1);
args.push(start);
// Will try even if the last closing parentheses is not found
if (end >= 0) {
args.push(end);
}
result.value = String.prototype.substring.apply(valueStr, args);
if (opts.numeric) {
const num = parseFloat(result.value);
result.unit = result.value.replace(num, '');
result.value = num;
}
/**
* Parse a raw value, generally fetched from the target, for this property
* @param {string} value Raw value string
* @return {Object}
* @example
* // example with an Input type
* prop.parseValue('translateX(10deg)');
* // -> { value: 10, unit: 'deg', functionName: 'translateX' }
*
*/
parseValue(value) {
const result = { value };
const imp = '!important';
if (isString(value) && value.indexOf(imp) !== -1) {
result.value = value.replace(imp, '').trim();
result.important = 1;
}
if (!this.get('functionName')) {
return result;
},
/**
* Get the default value
* @return {string}
* @private
*/
getDefaultValue() {
return this.get('defaults');
},
/**
* Get a complete value of the property.
* This probably will replace the getValue when all
* properties models will be splitted
* @param {string} val Custom value to replace the one on the model
* @return {string}
* @private
*/
getFullValue(val) {
const fn = this.get('functionName');
let value = isUndefined(val) ? this.get('value') : val;
if (fn && !isUndefined(value)) {
value = `${fn}(${value})`;
}
if (this.get('important')) {
value = `${value} !important`;
}
return value || '';
}
const args = [];
let valueStr = `${result.value}`;
let start = valueStr.indexOf('(') + 1;
let end = valueStr.lastIndexOf(')');
args.push(start);
// Will try even if the last closing parentheses is not found
if (end >= 0) {
args.push(end);
}
result.value = String.prototype.substring.apply(valueStr, args);
return result;
},
/**
* Get the default value
* @return {string}
* @private
*/
getDefaultValue() {
return this.get('defaults');
},
/**
* Get a complete value of the property.
* This probably will replace the getValue when all
* properties models will be splitted
* @param {string} val Custom value to replace the one on the model
* @return {string}
* @private
*/
getFullValue(val) {
const fn = this.get('functionName');
let value = isUndefined(val) ? this.get('value') : val;
if (fn && !isUndefined(value)) {
value = `${fn}(${value})`;
}
if (this.get('important')) {
value = `${value} !important`;
{
callParentInit(property, ctx, props, opts = {}) {
property.prototype.initialize.apply(ctx, [
props,
{
...opts,
skipInit: 1
}
]);
},
callInit(context, props, opts = {}) {
!opts.skipInit && context.init(props, opts);
}
return value || '';
}
});
);
module.exports = Property;

15
src/style_manager/model/PropertyComposite.js

@ -21,11 +21,13 @@ module.exports = Property.extend({
separator: ' '
},
init() {
initialize(props = {}, opts = {}) {
Property.callParentInit(Property, this, props, opts);
const properties = this.get('properties') || [];
const Properties = require('./Properties');
this.set('properties', new Properties(properties));
this.listenTo(this, 'change:value', this.updateValues);
Property.callInit(this, props, opts);
},
/**
@ -48,7 +50,7 @@ module.exports = Property.extend({
// 11px -> 11px 11px 11px 11xp
// 11px 22px -> 11px 22px 11px 22xp
const value =
values[i] || values[i % len + (len != 1 && len % 2 ? 1 : 0)];
values[i] || values[(i % len) + (len != 1 && len % 2 ? 1 : 0)];
// There some issue with UndoManager
//property.setValue(value, 0, {fromParent: 1});
});
@ -78,5 +80,14 @@ module.exports = Property.extend({
}
return this.get('properties').getFullValue();
},
/**
* Get property at some index
* @param {Number} index
* @return {Object}
*/
getPropertyAt(index) {
return this.get('properties').at(index);
}
});

36
src/style_manager/model/PropertyStack.js

@ -7,19 +7,51 @@ module.exports = Property.extend({
// Array of layers (which contain properties)
layers: [],
// The separator used to join layer values
layerSeparator: ', ',
// Layer preview
preview: 0
},
init() {
Property.prototype.init.apply(this, arguments);
initialize(props = {}, opts = {}) {
Property.callParentInit(Property, this, props, opts);
const layers = this.get('layers');
const layersColl = new Layers(layers);
layersColl.property = this;
layersColl.properties = this.get('properties');
this.set('layers', layersColl);
Property.callInit(this, props, opts);
},
getLayers() {
return this.get('layers');
},
getCurrentLayer() {
return this.getLayers().filter(layer => layer.get('active'))[0];
},
getFullValue() {
return this.get('detached') ? '' : this.get('layers').getFullValue();
},
/**
* This method allows to customize layers returned from the target
* @param {Object} target
* @return {Array} Should return an array of layers
* @example
* // return example
* [
* {
* properties: [
* { property: 'width', ... }
* { property: 'height', ... }
* ]
* }
* ]
*/
getLayersFromTarget(target) {
return;
}
});

12
src/style_manager/view/LayerView.js

@ -41,10 +41,6 @@ module.exports = Backbone.View.extend({
this.listenTo(model, 'change:active', this.updateVisibility);
this.listenTo(model.get('properties'), 'change', this.updatePreview);
if (!model.get('preview')) {
this.$el.addClass(this.pfx + 'no-preview');
}
// For the sorter
model.view = this;
model.set({ droppable: 0, draggable: 1 });
@ -152,9 +148,8 @@ module.exports = Backbone.View.extend({
render() {
const PropertiesView = require('./PropertiesView');
const propsConfig = this.propsConfig;
const className = `${this.pfx}layer`;
const model = this.model;
const el = this.el;
const { model, el, pfx } = this;
const preview = model.get('preview');
const properties = new PropertiesView({
collection: model.get('properties'),
config: this.config,
@ -163,8 +158,9 @@ module.exports = Backbone.View.extend({
propTarget: propsConfig.propTarget,
onChange: propsConfig.onChange
}).render().el;
el.innerHTML = this.template(model);
el.className = className;
el.className = `${pfx}layer${!preview ? ` ${pfx}no-preview` : ''}`;
this.getPropertiesWrapper().appendChild(properties);
this.updateVisibility();
this.updatePreview();

7
src/style_manager/view/PropertyIntegerView.js

@ -13,6 +13,7 @@ module.exports = PropertyView.extend({
const model = this.model;
this.listenTo(model, 'change:unit', this.modelValueChanged);
this.listenTo(model, 'el:change', this.elementUpdated);
this.listenTo(model, 'change:units', this.render);
},
setValue(value) {
@ -36,5 +37,11 @@ module.exports = PropertyView.extend({
this.input = this.$input.get(0);
this.inputInst = input;
}
},
clearCached() {
PropertyView.prototype.clearCached.apply(this, arguments);
this.unit = null;
this.$unit = null;
}
});

5
src/style_manager/view/PropertySliderView.js

@ -46,8 +46,9 @@ module.exports = Property.extend({
},
setValue(value) {
this.getSliderEl().value = parseFloat(value);
this.inputInst.setValue(value, { silent: 1 });
const parsed = this.model.parseValue(value);
this.getSliderEl().value = parseFloat(parsed.value);
Property.prototype.setValue.apply(this, arguments);
},
onRender() {

5
src/style_manager/view/PropertyStackView.js

@ -117,10 +117,10 @@ module.exports = PropertyCompositeView.extend({
const model = this.model;
const layers = this.getLayers();
const detached = model.get('detached');
const target = this.getTarget();
// With detached layers values will be assigned to their properties
if (detached) {
const target = this.getTarget();
const style = target ? target.getStyle() : {};
layersObj = layers.getLayersFromStyle(style);
} else {
@ -129,8 +129,9 @@ module.exports = PropertyCompositeView.extend({
layersObj = layers.getLayersFromValue(value);
}
const toAdd = model.getLayersFromTarget(target) || layersObj;
layers.reset();
layers.add(layersObj);
layers.add(toAdd);
model.set({ stackIndex: null }, { silent: true });
},

8
src/style_manager/view/PropertyView.js

@ -484,8 +484,14 @@ module.exports = Backbone.View.extend({
const pfx = this.pfx;
const model = this.model;
const el = this.el;
const property = model.get('property');
const full = model.get('full');
const className = `${pfx}property`;
el.innerHTML = this.template(model);
el.className = `${pfx}property ${pfx}${model.get('type')}`;
el.className = `${className} ${pfx}${model.get(
'type'
)} ${className}__${property}`;
el.className += full ? ` ${className}--full` : '';
this.updateStatus();
const onRender = this.onRender && this.onRender.bind(this);

1
src/styles/scss/_gjs_style_manager.scss

@ -252,6 +252,7 @@
margin-bottom: 5px;
padding: 0 5px;
&--full,
&.#{$sm-prefix}composite,
&.#{$sm-prefix}file,
&.#{$sm-prefix}list,

1
test/specs/style_manager/index.js

@ -155,6 +155,7 @@ describe('StyleManager', () => {
}
]
});
obj.onLoad();
});
afterEach(() => {

4
test/specs/style_manager/model/Models.js

@ -155,7 +155,7 @@ module.exports = {
test('parseValue with function and functionName', () => {
obj = new Property({ functionName: 'fn' });
const result = { value: 'testValue' };
const result = { value: 'testValue', functionName: 'fn' };
expect(obj.parseValue('fn(testValue)')).toEqual(result);
expect(obj.parseValue('fn(testValue')).toEqual(result);
});
@ -194,7 +194,7 @@ module.exports = {
units: ['px', 'deg'],
functionName: 'test'
});
const result = { value: 55, unit: 'deg' };
const result = { value: 55, unit: 'deg', functionName: 'test' };
expect(obj.parseValue('test(55deg)')).toEqual(result);
});

Loading…
Cancel
Save