UNPKG

@print-one/grapesjs

Version:

Free and Open Source Web Builder Framework

169 lines (144 loc) 4.33 kB
import { isArray, isString, keys } from 'underscore'; import { Model, ObjectAny, ObjectHash } from '../../common'; import ParserHtml from '../../parser/model/ParserHtml'; import Selectors from '../../selector_manager/model/Selectors'; import { shallowDiff } from '../../utils/mixins'; export type StyleProps = Record<string, string | string[]>; export type UpdateStyleOptions = ObjectAny & { partial?: boolean; }; const parserHtml = ParserHtml(); export const getLastStyleValue = (value: string | string[]) => { return isArray(value) ? value[value.length - 1] : value; }; export default class StyleableModel<T extends ObjectHash = any> extends Model<T> { /** * Forward style string to `parseStyle` to be parse to an object * @param {string} str * @returns */ parseStyle(str: string) { return parserHtml.parseStyle(str); } /** * To trigger the style change event on models I have to * pass a new object instance * @param {Object} prop * @return {Object} */ extendStyle(prop: ObjectAny): ObjectAny { return { ...this.getStyle(), ...prop }; } /** * Get style object * @return {Object} */ getStyle(prop?: string | ObjectAny): StyleProps { const style = this.get('style') || {}; const result: ObjectAny = { ...style }; return prop && isString(prop) ? result[prop] : result; } /** * Set new style object * @param {Object|string} prop * @param {Object} opts * @return {Object} Applied properties */ setStyle(prop: string | ObjectAny = {}, opts: UpdateStyleOptions = {}) { if (isString(prop)) { prop = this.parseStyle(prop); } const propOrig = this.getStyle(opts); if (opts.partial || opts.avoidStore) { opts.avoidStore = true; prop.__p = true; } else { delete prop.__p; } const propNew = { ...prop }; const newStyle = { ...propNew }; // Remove empty style properties keys(newStyle).forEach(prop => { if (newStyle[prop] === '') { delete newStyle[prop]; } }); this.set('style', newStyle, opts as any); const diff = shallowDiff(propOrig, propNew); // Delete the property used for partial updates delete diff.__p; keys(diff).forEach(pr => { // @ts-ignore const { em } = this; if (opts.noEvent) return; this.trigger(`change:style:${pr}`); if (em) { em.trigger('styleable:change', this, pr, opts); em.trigger(`styleable:change:${pr}`, this, pr, opts); } }); return propNew; } /** * Add style property * @param {Object|string} prop * @param {string} value * @example * this.addStyle({color: 'red'}); * this.addStyle('color', 'blue'); */ addStyle(prop: string | ObjectAny, value: any = '', opts: UpdateStyleOptions = {}) { if (typeof prop == 'string') { prop = { [prop]: value, }; } else { opts = value || {}; } prop = this.extendStyle(prop); this.setStyle(prop, opts); } /** * Remove style property * @param {string} prop */ removeStyle(prop: string) { let style = this.getStyle(); delete style[prop]; this.setStyle(style); } /** * Returns string of style properties * @param {Object} [opts={}] Options * @return {String} */ styleToString(opts: ObjectAny = {}) { const result: string[] = []; const style = this.getStyle(opts); const imp = opts.important; for (let prop in style) { const important = isArray(imp) ? imp.indexOf(prop) >= 0 : imp; const firstChars = prop.substring(0, 2); const isPrivate = firstChars === '__'; if (isPrivate) continue; const value = style[prop]; const values = isArray(value) ? (value as string[]) : [value]; values.forEach((val: string) => { const value = `${val}${important ? ' !important' : ''}`; value && result.push(`${prop}:${value};`); }); } return result.join(''); } getSelectors() { return (this.get('selectors') || this.get('classes')) as Selectors; } getSelectorsString(opts?: ObjectAny) { // @ts-ignore return this.selectorsToString ? this.selectorsToString(opts) : this.getSelectors().getFullString(); } // @ts-ignore // _validate(attr, opts) { // return true; // } }