diff --git a/packages/core/src/dom_components/model/Component.ts b/packages/core/src/dom_components/model/Component.ts index c82e76f8e..345dcff3f 100644 --- a/packages/core/src/dom_components/model/Component.ts +++ b/packages/core/src/dom_components/model/Component.ts @@ -2135,10 +2135,15 @@ export default class Component extends StyleableModel { components: ComponentDefinitionDefined | ComponentDefinitionDefined[], styles: CssRuleJSON[] = [], list: ObjectAny = {}, - opts: { keepIds?: string[]; idMap?: PrevToNewIdMap } = {}, + opts: { + keepIds?: string[]; + idMap?: PrevToNewIdMap; + updatedIds?: Record; + } = {}, ) { + opts.updatedIds = opts.updatedIds || {}; const comps = isArray(components) ? components : [components]; - const { keepIds = [], idMap = {} } = opts; + const { keepIds = [], idMap = {}, updatedIds } = opts; comps.forEach((comp) => { comp.attributes; const { attributes = {}, components } = comp; @@ -2147,6 +2152,7 @@ export default class Component extends StyleableModel { // Check if we have collisions with current components if (id && list[id] && keepIds.indexOf(id) < 0) { const newId = Component.getIncrementId(id, list); + updatedIds[id] = updatedIds[id] ? [...updatedIds[id], comp] : [comp]; idMap[id] = newId; attributes.id = newId; // Update passed styles @@ -2161,5 +2167,9 @@ export default class Component extends StyleableModel { components && Component.checkId(components, styles, list, opts); }); + + return { + updatedIds: opts.updatedIds, + }; } } diff --git a/packages/core/src/dom_components/model/Components.ts b/packages/core/src/dom_components/model/Components.ts index ac32f1be3..f62832eaf 100644 --- a/packages/core/src/dom_components/model/Components.ts +++ b/packages/core/src/dom_components/model/Components.ts @@ -25,7 +25,7 @@ export interface ResetCommonUpdateProps { } export interface ResetFromStringOptions { - visitedCmps?: Record; + visitedCmps?: Record; keepIds?: string[]; updateOptions?: { onAttributes?: (props: ResetCommonUpdateProps & { attributes: Record }) => void; @@ -161,32 +161,11 @@ Component> { resetFromString(input = '', opts: ResetFromStringOptions = {}) { opts.keepIds = getComponentIds(this); const { domc, em, parent } = this; - const cssc = em?.Css; const allByID = domc?.allById() || {}; - const parsed = this.parseString(input, opts); + const parsed = this.parseString(input, { ...opts, cloneRules: true }); const fromDefOpts = { skipViewUpdate: true, ...opts }; const newCmps = getComponentsFromDefs(parsed, allByID, fromDefOpts); - const { visitedCmps = {} } = fromDefOpts; - - // Clone styles for duplicated components - Object.keys(visitedCmps).forEach((id) => { - const cmps = visitedCmps[id]; - if (cmps.length) { - // Get all available rules of the component - const rulesToClone = cssc?.getRules(`#${id}`) || []; - - if (rulesToClone.length) { - cmps.forEach((cmp) => { - rulesToClone.forEach((rule) => { - const newRule = rule.clone(); - // @ts-ignore - newRule.set('selectors', [`#${cmp.attributes.id}`]); - cssc!.getAll().add(newRule); - }); - }); - } - } - }); + Components.cloneCssRules(em, fromDefOpts.visitedCmps); this.reset(newCmps, opts as any); em?.trigger('component:content', parent, opts, input); @@ -318,7 +297,8 @@ Component> { } // We need this to avoid duplicate IDs - Component.checkId(components, parsed.css, domc!.componentsById, opt); + const result = Component.checkId(components, parsed.css, domc!.componentsById, opt); + opt.cloneRules && Components.cloneCssRules(em, result.updatedIds); if (parsed.css && cssc && !opt.temporary) { const { at, ...optsToPass } = opt; @@ -447,4 +427,26 @@ Component> { } } } + + static cloneCssRules(em: EditorModel, cmpsMap: Record = {}) { + const { Css } = em; + Object.keys(cmpsMap).forEach((id) => { + const cmps = cmpsMap[id]; + if (cmps.length) { + // Get all available rules of the component + const rulesToClone = (Css.getRules(`#${id}`) || []).filter((rule) => !isEmpty(rule.attributes.style)); + + if (rulesToClone.length) { + const rules = Css.getAll(); + cmps.forEach((cmp) => { + rulesToClone.forEach((rule) => { + const newRule = rule.clone(); + newRule.set('selectors', [`#${cmp.attributes.id}`] as any); + rules.add(newRule); + }); + }); + } + } + }); + } } diff --git a/packages/core/src/dom_components/types.ts b/packages/core/src/dom_components/types.ts index 4f62ecfe8..d0c5782fd 100644 --- a/packages/core/src/dom_components/types.ts +++ b/packages/core/src/dom_components/types.ts @@ -19,6 +19,7 @@ export interface SymbolInfo { export interface ParseStringOptions extends AddOptions, OptionAsDocument, WithHTMLParserOptions { keepIds?: string[]; + cloneRules?: boolean; } export enum ComponentsEvents { diff --git a/packages/core/test/specs/dom_components/model/Component.ts b/packages/core/test/specs/dom_components/model/Component.ts index 8f8aa2ce0..3fee7487c 100644 --- a/packages/core/test/specs/dom_components/model/Component.ts +++ b/packages/core/test/specs/dom_components/model/Component.ts @@ -460,6 +460,30 @@ describe('Component', () => { ); }); + test('Ensure duplicated component clones the rules also cross components', () => { + const idName = 'test'; + const cmp = dcomp.addComponent(` +
+
Comp 1
+
+ + `) as Component; + const cmp2 = dcomp.addComponent(`
Text
`) as Component; + cmp2.components().resetFromString(`
Comp 2
`); + const newId = cmp2.components().at(0).getId(); + expect(em.getHtml()).toBe( + `
Comp 1
Comp 2
`, + ); + expect(em.getCss()).toBe( + `#test{color:red;}#${newId}{color:red;}@media (max-width: 992px){#test{color:blue;}#${newId}{color:blue;}}`, + ); + }); + test('Ability to stop/change propagation chain', () => { obj.append({ removable: false,