diff --git a/packages/core/src/abstract/Module.ts b/packages/core/src/abstract/Module.ts index 8889ea433..4d8f4d06e 100644 --- a/packages/core/src/abstract/Module.ts +++ b/packages/core/src/abstract/Module.ts @@ -130,6 +130,7 @@ export abstract class ItemManagerModule< all: TCollection; view?: View; events!: Record; + protected _itemCache = new Map(); constructor( em: EditorModel, @@ -207,6 +208,51 @@ export abstract class ItemManagerModule< }, {} as any); } + protected _makeCacheKey(m: Model) { + return ''; + } + + protected _cacheItem(item: Model) { + const key = this._makeCacheKey(item); + key && this._itemCache.set(key, item); + } + + protected _uncacheItem(item: Model) { + const key = this._makeCacheKey(item); + key && this._itemCache.delete(key); + } + + protected _clearItemCache() { + this._itemCache.clear(); + } + + protected _onItemsResetCache(collection: Collection) { + this._clearItemCache(); + collection.each((item: Model) => this._cacheItem(item)); + } + + protected _onItemKeyChange(item: Model) { + let oldKey: string | undefined; + for (const [key, cachedItem] of (this._itemCache as any).entries()) { + if (cachedItem === item) { + oldKey = key; + break; + } + } + + if (oldKey) { + this._itemCache.delete(oldKey); + } + + this._cacheItem(item); + } + + protected _setupCacheListeners() { + this.em.listenTo(this.all, 'add', this._cacheItem.bind(this)); + this.em.listenTo(this.all, 'remove', this._uncacheItem.bind(this)); + this.em.listenTo(this.all, 'reset', this._onItemsResetCache.bind(this)); + } + __initListen(opts: any = {}) { const { all, em, events } = this; all && diff --git a/packages/core/src/css_composer/index.ts b/packages/core/src/css_composer/index.ts index 32b158179..4fd3e4f68 100644 --- a/packages/core/src/css_composer/index.ts +++ b/packages/core/src/css_composer/index.ts @@ -87,7 +87,7 @@ export default class CssComposer extends ItemManagerModule(); /** * Initializes module. Automatically called with a new instance of the editor * @param {Object} config Configurations @@ -104,6 +104,34 @@ export default class CssComposer extends ItemManagerModule (isString(selector) ? selector : selector.toString())).join(''); + + const selectorsRes = []; + selectorsStr && selectorsRes.push(`${selectorsStr}${state ? `:${state}` : ''}`); + selectorsAdd && selectorsRes.push(selectorsAdd); + const selectorsKey = selectorsRes.join(', '); + + const typeStr = atRuleType ? `@${atRuleType}` : mediaText ? '@media' : ''; + const atRuleKey = typeStr + (mediaText && typeStr ? ` ${mediaText}` : ''); + + return `${atRuleKey}__${selectorsKey}`; } /** @@ -112,6 +140,7 @@ export default class CssComposer extends ItemManagerModule, + ): CssRule | null { + let slc = selectors; + if (isString(selectors)) { + const sm = this.em.Selectors; + const singleSel = selectors.split(',')[0].trim(); + const node = this.em.Parser.parserCss.checkNode({ selectors: singleSel } as any)[0]; + slc = sm.get(node.selectors as string[]); + } + + const rule = this.rules.find((r) => r.compare(slc, state, width, ruleProps)) || null; + return rule; + } + /** * Add new rule to the collection, if not yet exists with the same selectors * @param {Array} selectors Array of selectors @@ -157,27 +208,38 @@ export default class CssComposer extends ItemManagerModule, - ): CssRule | undefined { - let slc = selectors; - if (isString(selectors)) { - const sm = this.em.Selectors; - const singleSel = selectors.split(',')[0].trim(); - const node = this.em.Parser.parserCss.checkNode({ selectors: singleSel } as any)[0]; - slc = sm.get(node.selectors as string[]); + get(selectors: any, state?: string, width?: string, ruleProps?: Omit) { + const key = this._makeCacheKeyFromProps({ + ...ruleProps, + selectors: Array.isArray(selectors) ? selectors : [selectors], + state, + width, + mediaText: width, + }); + const cached = this._itemCache.get(key); + if (cached) return cached; + + const rule = this._findRule(selectors, state, width, ruleProps); + + if (rule) { + this._cacheItem(rule); } - return this.rules.find((rule) => rule.compare(slc, state, width, ruleProps)) || null; + + return rule; } getAll() { @@ -485,8 +551,9 @@ export default class CssComposer extends ItemManagerModule { this.opt = opt; this.em = opt.em; this.ensureSelectors(null, null, {}); - this.on('change', this.__onChange); this.setStyle(this.get('style'), { skipWatcherUpdates: true }); + this.on('change', this.__onChange); } __onChange(m: CssRule, opts: any) { @@ -175,9 +175,12 @@ export default class CssRule extends StyleableModel { * cssRule.getAtRule(); // "@media (min-width: 500px)" */ getAtRule() { - const type = this.get('atRuleType'); - const condition = this.get('mediaText'); - // Avoid breaks with the last condition + return CssRule.getAtRuleFromProps(this.attributes); + } + + static getAtRuleFromProps(cssRuleProps: Partial) { + const type = cssRuleProps.atRuleType; + const condition = cssRuleProps.mediaText; const typeStr = type ? `@${type}` : condition ? '@media' : ''; return typeStr + (condition && typeStr ? ` ${condition}` : ''); diff --git a/packages/core/src/dom_components/model/Component.ts b/packages/core/src/dom_components/model/Component.ts index 17f644f76..ad2ca2782 100644 --- a/packages/core/src/dom_components/model/Component.ts +++ b/packages/core/src/dom_components/model/Component.ts @@ -272,7 +272,7 @@ export default class Component extends StyleableModel { views!: ComponentView[]; view?: ComponentView; viewLayer?: ItemView; - rule?: CssRule; + rule?: CssRule | null; prevColl?: Components; __hasUm?: boolean; __symbReady?: boolean; @@ -1123,7 +1123,7 @@ export default class Component extends StyleableModel { return coll as any; } else { coll.reset(undefined, opts); - return components ? this.append(components, opts) : ([] as any); + return (components ? this.append(components, opts) : []) as any; } } diff --git a/packages/core/src/dom_components/model/Components.ts b/packages/core/src/dom_components/model/Components.ts index 10178e0c6..2adbb4a10 100644 --- a/packages/core/src/dom_components/model/Components.ts +++ b/packages/core/src/dom_components/model/Components.ts @@ -418,15 +418,18 @@ Component> { onAdd(model: Component, c?: any, opts: { temporary?: boolean } = {}) { const { domc, em } = this; - const style = model.getStyle(); - const avoidInline = em && em.getConfig().avoidInlineStyle; + const avoidInline = em.config.avoidInlineStyle; domc && domc.Component.ensureInList(model); - if (!isEmpty(style) && !avoidInline && em && em.getConfig().forceClass && !opts.temporary) { - const name = model.cid; - em.Css.setClassRule(name, style); - model.setStyle({}); - model.addClass(name); + if (!avoidInline && em.config.forceClass && !opts.temporary) { + const style = model.getStyle(); + + if (!isEmpty(style)) { + const name = model.cid; + em.Css.setClassRule(name, style); + model.setStyle({}); + model.addClass(name); + } } model.__postAdd({ recursive: true }); diff --git a/packages/core/src/editor/model/Editor.ts b/packages/core/src/editor/model/Editor.ts index 69e65669f..d154efc0c 100644 --- a/packages/core/src/editor/model/Editor.ts +++ b/packages/core/src/editor/model/Editor.ts @@ -76,6 +76,7 @@ const storableDeps: (new (em: EditorModel) => IModule & IStorableModule)[] = [ CssComposer, PageManager, ComponentManager, + SelectorManager, ]; Extender({ $ }); diff --git a/packages/core/src/selector_manager/index.ts b/packages/core/src/selector_manager/index.ts index db059bb4f..274660e79 100644 --- a/packages/core/src/selector_manager/index.ts +++ b/packages/core/src/selector_manager/index.ts @@ -98,7 +98,6 @@ export default class SelectorManager extends ItemManagerModule( @@ -153,14 +151,6 @@ export default class SelectorManager extends ItemManagerModule}