diff --git a/packages/core/src/code_manager/model/CssGenerator.ts b/packages/core/src/code_manager/model/CssGenerator.ts index 3bcfbc401..ee98afc47 100644 --- a/packages/core/src/code_manager/model/CssGenerator.ts +++ b/packages/core/src/code_manager/model/CssGenerator.ts @@ -172,7 +172,7 @@ export default class CssGenerator extends Model { }); if ((selectorStrNoAdd && found) || selectorsAdd || singleAtRule || !model) { - const block = rule.getDeclaration({ body: 1 }); + const block = rule.getDeclaration(); block && (opts.json ? (result = rule) : (result += block)); } else { dump.push(rule); diff --git a/packages/core/src/css_composer/index.ts b/packages/core/src/css_composer/index.ts index 562b77bff..d1c56e4f2 100644 --- a/packages/core/src/css_composer/index.ts +++ b/packages/core/src/css_composer/index.ts @@ -40,6 +40,8 @@ import EditorModel from '../editor/model/Editor'; import Component from '../dom_components/model/Component'; import { ObjectAny, PrevToNewIdMap } from '../common'; import { UpdateStyleOptions } from '../domain_abstract/model/StyleableModel'; +import { CssEvents } from './types'; +import CssRuleView from './view/CssRuleView'; /** @private */ interface RuleOptions { @@ -72,8 +74,15 @@ export interface GetSetRuleOptions extends UpdateStyleOptions { type CssRuleStyle = Required['style']; export default class CssComposer extends ItemManagerModule { + classes = { + CssRule, + CssRules, + CssRuleView, + CssRulesView, + }; rules: CssRules; rulesView?: CssRulesView; + events = CssEvents; Selectors = Selectors; diff --git a/packages/core/src/css_composer/model/CssRule.ts b/packages/core/src/css_composer/model/CssRule.ts index 2579eba49..e74f20b8d 100644 --- a/packages/core/src/css_composer/model/CssRule.ts +++ b/packages/core/src/css_composer/model/CssRule.ts @@ -1,6 +1,6 @@ import { isEmpty, forEach, isString, isArray } from 'underscore'; -import { Model, ObjectAny, View } from '../../common'; -import StyleableModel from '../../domain_abstract/model/StyleableModel'; +import { Model, ObjectAny } from '../../common'; +import StyleableModel, { StyleProps } from '../../domain_abstract/model/StyleableModel'; import Selectors from '../../selector_manager/model/Selectors'; import { getMediaLength } from '../../code_manager/model/CssGenerator'; import { isEmptyObj, hasWin } from '../../utils/mixins'; @@ -8,6 +8,13 @@ import Selector, { SelectorProps } from '../../selector_manager/model/Selector'; import EditorModel from '../../editor/model/Editor'; import CssRuleView from '../view/CssRuleView'; +export interface ToCssOptions { + important?: boolean | string[]; + allowEmpty?: boolean; + style?: StyleProps; + inline?: boolean; +} + /** @private */ export interface CssRuleProperties { /** @@ -214,7 +221,7 @@ export default class CssRule extends StyleableModel { * }); * cssRule.getDeclaration() // ".class1{color:red;}" */ - getDeclaration(opts: ObjectAny = {}) { + getDeclaration(opts: ToCssOptions = {}) { let result = ''; const { important } = this.attributes; const selectors = this.selectorsToString(opts); @@ -285,7 +292,7 @@ export default class CssRule extends StyleableModel { * }); * cssRule.toCSS() // "@media (min-width: 500px){.class1{color:red;}}" */ - toCSS(opts: ObjectAny = {}) { + toCSS(opts: ToCssOptions = {}) { let result = ''; const atRule = this.getAtRule(); const block = this.getDeclaration(opts); diff --git a/packages/core/src/css_composer/types.ts b/packages/core/src/css_composer/types.ts new file mode 100644 index 000000000..708950c53 --- /dev/null +++ b/packages/core/src/css_composer/types.ts @@ -0,0 +1,9 @@ +export enum CssEvents { + /** + * @event `css:mount` CSS rule is mounted in the canvas. + * @example + * editor.on('css:mount', ({ rule }) => { ... }); + */ + mount = 'css:mount', + mountBefore = 'css:mount:before', +} diff --git a/packages/core/src/css_composer/view/CssRuleView.ts b/packages/core/src/css_composer/view/CssRuleView.ts index 16dd8e6c9..7d9009a74 100644 --- a/packages/core/src/css_composer/view/CssRuleView.ts +++ b/packages/core/src/css_composer/view/CssRuleView.ts @@ -1,6 +1,7 @@ import FrameView from '../../canvas/view/FrameView'; import { View } from '../../common'; import CssRule from '../model/CssRule'; +import { CssEvents } from '../types'; export default class CssRuleView extends View { config: any; @@ -19,6 +20,10 @@ export default class CssRuleView extends View { return this.config.frameView; } + get em() { + return this.model.em!; + } + remove() { super.remove(); this.model.removeView(this); @@ -35,9 +40,13 @@ export default class CssRuleView extends View { } render() { - const { model, el } = this; + const { model, el, em } = this; const important = model.get('important'); - el.innerHTML = model.toCSS({ important }); + const css = model.toCSS({ important }); + const mountProps = { rule: model, ruleView: this, css }; + em?.trigger(CssEvents.mountBefore, mountProps); + el.innerHTML = mountProps.css; + em?.trigger(CssEvents.mount, mountProps); return this; } } diff --git a/packages/core/src/dom_components/types.ts b/packages/core/src/dom_components/types.ts index 640f31f3d..dc367099a 100644 --- a/packages/core/src/dom_components/types.ts +++ b/packages/core/src/dom_components/types.ts @@ -69,6 +69,13 @@ export enum ComponentsEvents { select = 'component:select', selectBefore = 'component:select:before', + /** + * @event `component:mount` Component is mounted in the canvas. + * @example + * editor.on('component:mount', (component) => { ... }); + */ + mount = 'component:mount', + /** * @event `component:script:mount` Component with script is mounted. * @example diff --git a/packages/core/src/dom_components/view/ComponentsView.ts b/packages/core/src/dom_components/view/ComponentsView.ts index 7ae09e0b0..7daf853b3 100644 --- a/packages/core/src/dom_components/view/ComponentsView.ts +++ b/packages/core/src/dom_components/view/ComponentsView.ts @@ -8,6 +8,7 @@ import ComponentView from './ComponentView'; import FrameView from '../../canvas/view/FrameView'; import Components from '../model/Components'; import { ResetComponentsOptions } from '../model/types'; +import { ComponentsEvents } from '../types'; export default class ComponentsView extends View { opts!: any; @@ -119,7 +120,7 @@ export default class ComponentsView extends View { } if (!model.opt.temporary) { - em?.trigger('component:mount', model); + em?.trigger(ComponentsEvents.mount, model); } return rendered; diff --git a/packages/core/src/domain_abstract/model/StyleableModel.ts b/packages/core/src/domain_abstract/model/StyleableModel.ts index fcf5fa952..f50769902 100644 --- a/packages/core/src/domain_abstract/model/StyleableModel.ts +++ b/packages/core/src/domain_abstract/model/StyleableModel.ts @@ -17,6 +17,7 @@ import { isDataResolverProps, } from '../../data_sources/utils'; import { DataResolver } from '../../data_sources/types'; +import { ToCssOptions } from '../../css_composer/model/CssRule'; export type StyleProps = Record; @@ -241,9 +242,9 @@ export default class StyleableModel extends Model * @param {Object} [opts={}] Options * @return {String} */ - styleToString(opts: ObjectAny = {}) { + styleToString(opts: ToCssOptions = {}) { const result: string[] = []; - const style = this.getStyle(opts); + const style = opts.style || this.getStyle(opts); const imp = opts.important; for (let prop in style) { diff --git a/packages/core/test/specs/css_composer/index.ts b/packages/core/test/specs/css_composer/index.ts index 1714a71d9..2728a5e61 100644 --- a/packages/core/test/specs/css_composer/index.ts +++ b/packages/core/test/specs/css_composer/index.ts @@ -162,7 +162,7 @@ describe('Css Composer', () => { const rule = obj.getIdRule(name)!; expect(rule.selectorsToString()).toEqual(`#${name}`); expect(rule.styleToString()).toEqual('color:red;'); - expect(rule.styleToString({ important: 1 })).toEqual('color:red !important;'); + expect(rule.styleToString({ important: true })).toEqual('color:red !important;'); expect(rule.styleToString({ important: ['color'] })).toEqual('color:red !important;'); });