|
|
@ -87,7 +87,7 @@ export default class CssComposer extends ItemManagerModule<CssComposerConfig & { |
|
|
Selectors = Selectors; |
|
|
Selectors = Selectors; |
|
|
|
|
|
|
|
|
storageKey = 'styles'; |
|
|
storageKey = 'styles'; |
|
|
|
|
|
protected _itemCache = new Map<string, CssRule>(); |
|
|
/** |
|
|
/** |
|
|
* Initializes module. Automatically called with a new instance of the editor |
|
|
* Initializes module. Automatically called with a new instance of the editor |
|
|
* @param {Object} config Configurations |
|
|
* @param {Object} config Configurations |
|
|
@ -104,6 +104,34 @@ export default class CssComposer extends ItemManagerModule<CssComposerConfig & { |
|
|
config.rules = this.em.config.style || config.rules || ''; |
|
|
config.rules = this.em.config.style || config.rules || ''; |
|
|
|
|
|
|
|
|
this.rules = new CssRules([], config); |
|
|
this.rules = new CssRules([], config); |
|
|
|
|
|
this._setupCacheListeners(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
protected override _setupCacheListeners() { |
|
|
|
|
|
super._setupCacheListeners(); |
|
|
|
|
|
this.em.listenTo(this.rules, 'change:selectors change:state change:mediaText', this._onItemKeyChange.bind(this)); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
protected _makeCacheKey(rule: CssRule) { |
|
|
|
|
|
const atRuleKey = rule.getAtRule(); |
|
|
|
|
|
const selectorsKey = rule.selectorsToString(); |
|
|
|
|
|
return `${atRuleKey}__${selectorsKey}`; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
_makeCacheKeyFromProps(ruleProps: CssRuleProperties) { |
|
|
|
|
|
const { atRuleType = '', mediaText = '', state = '', selectorsAdd = '', selectors = [] } = ruleProps; |
|
|
|
|
|
|
|
|
|
|
|
const selectorsStr = selectors.map((selector) => (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<CssComposerConfig & { |
|
|
*/ |
|
|
*/ |
|
|
onLoad() { |
|
|
onLoad() { |
|
|
this.rules.add(this.config.rules, { silent: true }); |
|
|
this.rules.add(this.config.rules, { silent: true }); |
|
|
|
|
|
this._onItemsResetCache(this.rules as any); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
@ -134,6 +163,28 @@ export default class CssComposer extends ItemManagerModule<CssComposerConfig & { |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Find a rule in the collection by its properties. |
|
|
|
|
|
* @private |
|
|
|
|
|
*/ |
|
|
|
|
|
_findRule( |
|
|
|
|
|
selectors: any, |
|
|
|
|
|
state?: string, |
|
|
|
|
|
width?: string, |
|
|
|
|
|
ruleProps?: Omit<CssRuleProperties, 'selectors'>, |
|
|
|
|
|
): 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 |
|
|
* Add new rule to the collection, if not yet exists with the same selectors |
|
|
* @param {Array<Selector>} selectors Array of selectors |
|
|
* @param {Array<Selector>} selectors Array of selectors |
|
|
@ -157,27 +208,38 @@ export default class CssComposer extends ItemManagerModule<CssComposerConfig & { |
|
|
const s = state || ''; |
|
|
const s = state || ''; |
|
|
const w = width || ''; |
|
|
const w = width || ''; |
|
|
const opt = { ...opts } as CssRuleProperties; |
|
|
const opt = { ...opts } as CssRuleProperties; |
|
|
let rule = this.get(selectors, s, w, opt); |
|
|
const key = this._makeCacheKeyFromProps({ |
|
|
|
|
|
state: s, |
|
|
|
|
|
mediaText: w, |
|
|
|
|
|
...opt, |
|
|
|
|
|
selectors: Array.isArray(selectors) ? selectors : [selectors], |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
const cached = this._itemCache.get(key); |
|
|
|
|
|
if (cached && cached.config && !cached.config.singleAtRule) { |
|
|
|
|
|
return cached; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
let rule = this._findRule(selectors, s, w, opt); |
|
|
|
|
|
|
|
|
// do not create rules that were found before
|
|
|
|
|
|
// unless this is a single at-rule, for which multiple declarations
|
|
|
|
|
|
// make sense (e.g. multiple `@font-type`s)
|
|
|
|
|
|
if (rule && rule.config && !rule.config.singleAtRule) { |
|
|
if (rule && rule.config && !rule.config.singleAtRule) { |
|
|
return rule; |
|
|
this._cacheItem(rule); |
|
|
} else { |
|
|
|
|
|
opt.state = s; |
|
|
|
|
|
opt.mediaText = w; |
|
|
|
|
|
opt.selectors = []; |
|
|
|
|
|
// #4727: Prevent updating atRuleType if already defined
|
|
|
|
|
|
if (w && !opt.atRuleType) { |
|
|
|
|
|
opt.atRuleType = 'media'; |
|
|
|
|
|
} |
|
|
|
|
|
rule = new CssRule(opt, this.config); |
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
|
rule.get('selectors').add(selectors, addOpts); |
|
|
|
|
|
this.rules.add(rule, addOpts); |
|
|
|
|
|
return rule; |
|
|
return rule; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
opt.state = s; |
|
|
|
|
|
opt.mediaText = w; |
|
|
|
|
|
opt.selectors = []; |
|
|
|
|
|
if (w && !opt.atRuleType) opt.atRuleType = 'media'; |
|
|
|
|
|
|
|
|
|
|
|
rule = new CssRule(opt, this.config); |
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
|
rule.get('selectors').add(selectors, addOpts); |
|
|
|
|
|
this.rules.add(rule, addOpts); |
|
|
|
|
|
|
|
|
|
|
|
this._cacheItem(rule); |
|
|
|
|
|
|
|
|
|
|
|
return rule; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
@ -199,20 +261,24 @@ export default class CssComposer extends ItemManagerModule<CssComposerConfig & { |
|
|
* color: '#000', |
|
|
* color: '#000', |
|
|
* }); |
|
|
* }); |
|
|
* */ |
|
|
* */ |
|
|
get( |
|
|
get(selectors: any, state?: string, width?: string, ruleProps?: Omit<CssRuleProperties, 'selectors'>) { |
|
|
selectors: any, |
|
|
const key = this._makeCacheKeyFromProps({ |
|
|
state?: string, |
|
|
...ruleProps, |
|
|
width?: string, |
|
|
selectors: Array.isArray(selectors) ? selectors : [selectors], |
|
|
ruleProps?: Omit<CssRuleProperties, 'selectors'>, |
|
|
state, |
|
|
): CssRule | undefined { |
|
|
width, |
|
|
let slc = selectors; |
|
|
mediaText: width, |
|
|
if (isString(selectors)) { |
|
|
}); |
|
|
const sm = this.em.Selectors; |
|
|
const cached = this._itemCache.get(key); |
|
|
const singleSel = selectors.split(',')[0].trim(); |
|
|
if (cached) return cached; |
|
|
const node = this.em.Parser.parserCss.checkNode({ selectors: singleSel } as any)[0]; |
|
|
|
|
|
slc = sm.get(node.selectors as string[]); |
|
|
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() { |
|
|
getAll() { |
|
|
@ -485,8 +551,9 @@ export default class CssComposer extends ItemManagerModule<CssComposerConfig & { |
|
|
*/ |
|
|
*/ |
|
|
remove(rule: string | CssRule, opts?: any) { |
|
|
remove(rule: string | CssRule, opts?: any) { |
|
|
const toRemove = isString(rule) ? this.getRules(rule) : rule; |
|
|
const toRemove = isString(rule) ? this.getRules(rule) : rule; |
|
|
const result = this.getAll().remove(toRemove, opts); |
|
|
const arr = Array.isArray(toRemove) ? toRemove : [toRemove]; |
|
|
return isArray(result) ? result : [result]; |
|
|
const result = this.getAll().remove(arr, opts); |
|
|
|
|
|
return Array.isArray(result) ? result : [result]; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
@ -494,6 +561,7 @@ export default class CssComposer extends ItemManagerModule<CssComposerConfig & { |
|
|
* @return {this} |
|
|
* @return {this} |
|
|
*/ |
|
|
*/ |
|
|
clear(opts = {}) { |
|
|
clear(opts = {}) { |
|
|
|
|
|
this._clearItemCache(); |
|
|
this.getAll().reset([], opts); |
|
|
this.getAll().reset([], opts); |
|
|
return this; |
|
|
return this; |
|
|
} |
|
|
} |
|
|
|