UNPKG

drapcode-builder

Version:

Drapcode Builder Library

235 lines (203 loc) 6.69 kB
import Backbone from 'backbone'; import { isUndefined, isString } from 'underscore'; import { capitalize } from 'utils/mixins'; const Property = 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, // Specifies dependency on other properties of the selected object. // Property is shown only when all conditions are matched. // // example: { display: ['flex', 'block'], position: ['absolute'] }; // in this case the property is only shown when display is // of value 'flex' or 'block' AND position is 'absolute' requires: null, // Specifies dependency on properties of the parent of the selected object. // Property is shown only when all conditions are matched. requiresParent: null }, initialize(props = {}, opts = {}) { const id = this.get('id') || ''; const name = this.get('name') || ''; !this.get('property') && this.set('property', (name || id).replace(/ /g, '-')); const prop = this.get('property'); !this.get('id') && this.set('id', prop); !name && this.set('name', capitalize(prop).replace(/-/g, ' ')); Property.callInit(this, props, opts); }, init() {}, /** * Clear the value * @return {this} */ clearValue(opts = {}) { this.set({ value: undefined, status: '' }, 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); const avoidStore = !complete; !avoidStore && this.set({ value: undefined }, { avoidStore, silent: true }); this.set(parsed, { avoidStore, ...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(')'); const functionName = valueStr.substring(0, start - 1); if (functionName) result.functionName = functionName; 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; } return result; }, /** * Helper function to safely split a string of values. * Useful when style values are inside functions * eg: * -> input: 'value(1,2,4), 123, value(4,5)' -- default separator: ',' * -> output: ['value(1,2,4)', '123', 'value(4,5)'] * @param {String} values Values to split * @param {String} [separator] Separator */ splitValues(values, separator = ',') { const res = []; const op = '('; const cl = ')'; let curr = ''; let acc = 0; (values || '').split('').forEach(str => { if (str == op) { acc++; curr = curr + op; } else if (str == cl && acc > 0) { acc--; curr = curr + cl; } else if (str === separator && acc == 0) { res.push(curr); curr = ''; } else { curr = curr + str; } }); curr !== '' && res.push(curr); return res.map(i => i.trim()); }, /** * 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'); const def = this.getDefaultValue(); let value = isUndefined(val) ? this.get('value') : val; const hasValue = !isUndefined(value) && value !== ''; if (value && def && value === def) { return def; } if (fn && hasValue) { const fnParameter = fn === 'url' ? `'${value.replace(/'/g, '')}'` : value; value = `${fn}(${fnParameter})`; } if (hasValue && this.get('important')) { value = `${value} !important`; } return value || ''; } }, { callParentInit(property, ctx, props, opts = {}) { property.prototype.initialize.apply(ctx, [ props, { ...opts, skipInit: 1 } ]); }, callInit(context, props, opts = {}) { !opts.skipInit && context.init(props, opts); } } ); export default Property;