From 679e23cb2508d836e28be181153dadd37c706065 Mon Sep 17 00:00:00 2001 From: Alex Ritter Date: Mon, 3 Jun 2024 10:32:43 +0200 Subject: [PATCH] Cleanup Component models (#5843) --- src/dom_components/model/Component.ts | 16 ++-- src/dom_components/model/ComponentImage.ts | 5 +- src/dom_components/model/ComponentMap.ts | 6 +- src/dom_components/model/ComponentTable.ts | 5 +- .../model/ComponentTableBody.ts | 5 +- src/dom_components/model/ComponentText.ts | 5 +- src/dom_components/model/ComponentVideo.ts | 6 +- src/dom_components/model/Components.ts | 75 +++++++------------ src/dom_components/model/types.ts | 18 +++-- src/parser/model/ParserHtml.ts | 6 +- test/specs/dom_components/model/Component.ts | 8 +- .../dom_components/view/ComponentImageView.ts | 2 +- .../selector_manager/view/ClassTagsView.ts | 4 +- .../style_manager/view/PropertyColorView.ts | 2 +- test/specs/style_manager/view/PropertyView.ts | 2 +- test/specs/trait_manager/model/TraitsModel.ts | 2 +- test/specs/trait_manager/view/TraitsView.ts | 5 +- 17 files changed, 82 insertions(+), 90 deletions(-) diff --git a/src/dom_components/model/Component.ts b/src/dom_components/model/Component.ts index 0634ce65e..e642c6374 100644 --- a/src/dom_components/model/Component.ts +++ b/src/dom_components/model/Component.ts @@ -22,6 +22,7 @@ import EditorModel from '../../editor/model/Editor'; import { AddComponentsOption, ComponentAdd, + ComponentAddType, ComponentDefinition, ComponentDefinitionDefined, ComponentOptions, @@ -234,7 +235,8 @@ export default class Component extends StyleableModel { * @ts-ignore */ collection!: Components; - initialize(props = {}, opt: ComponentOptions = {}) { + constructor(props: ComponentProperties = {}, opt: ComponentOptions) { + super(props, opt); bindAll(this, '__upSymbProps', '__upSymbCls', '__upSymbComps'); const em = opt.em; @@ -527,12 +529,12 @@ export default class Component extends StyleableModel { * const result = component.replaceWith('
Some new content
'); * // result -> [Component] */ - replaceWith(el: ComponentAdd, opts: AddOptions = {}): C[] { + replaceWith(el: ComponentAdd, opts: AddOptions = {}): Component[] { const coll = this.collection; const at = coll.indexOf(this); coll.remove(this); const result = coll.add(el, { ...opts, at }); - return isArray(result) ? result : [result]; + return isArray(result) ? result : [result];; } /** @@ -2121,11 +2123,9 @@ export default class Component extends StyleableModel { } static getList(model: Component) { - const { opt = {} } = model; - // @ts-ignore - const { domc, em } = opt; - const dm = domc || em?.Components; - return dm ? dm.componentsById : {}; + const { em } = model; + const dm = em?.Components; + return dm?.componentsById ?? {}; } static checkId( diff --git a/src/dom_components/model/ComponentImage.ts b/src/dom_components/model/ComponentImage.ts index 69404377f..fc289f366 100644 --- a/src/dom_components/model/ComponentImage.ts +++ b/src/dom_components/model/ComponentImage.ts @@ -2,6 +2,7 @@ import { result } from 'underscore'; import Component from './Component'; import { toLowerCase, buildBase64UrlFromSvg, hasWin } from '../../utils/mixins'; import { ObjectStrings } from '../../common'; +import { ComponentOptions, ComponentProperties } from './types'; const svgAttrs = 'xmlns="http://www.w3.org/2000/svg" width="100" viewBox="0 0 24 24" style="fill: rgba(0,0,0,0.15); transform: scale(0.75)"'; @@ -35,8 +36,8 @@ export default class ComponentImage extends Component { }; } - initialize(props: any, opts: any) { - super.initialize(props, opts); + constructor(props: ComponentProperties = {}, opt: ComponentOptions) { + super(props, opt); const { src } = this.get('attributes')!; if (src && buildBase64UrlFromSvg(result(this, 'defaults').src) !== src) { this.set('src', src, { silent: true }); diff --git a/src/dom_components/model/ComponentMap.ts b/src/dom_components/model/ComponentMap.ts index 585d098eb..2689b04cc 100644 --- a/src/dom_components/model/ComponentMap.ts +++ b/src/dom_components/model/ComponentMap.ts @@ -1,5 +1,6 @@ import ComponentImage from './ComponentImage'; import { toLowerCase } from '../../utils/mixins'; +import { ComponentOptions, ComponentProperties } from './types'; export default class ComponentMap extends ComponentImage { /** @ts-ignore */ @@ -49,10 +50,11 @@ export default class ComponentMap extends ComponentImage { }; } - initialize(props: any, opts: any) { + constructor(props: ComponentProperties = {}, opt: ComponentOptions) { + super(props, opt); if (this.get('src')) this.parseFromSrc(); else this.updateSrc(); - super.initialize(props, opts); + this.listenTo(this, 'change:address change:zoom change:mapType', this.updateSrc); } diff --git a/src/dom_components/model/ComponentTable.ts b/src/dom_components/model/ComponentTable.ts index e47d87d09..c9b4394e5 100644 --- a/src/dom_components/model/ComponentTable.ts +++ b/src/dom_components/model/ComponentTable.ts @@ -1,5 +1,6 @@ import Component from './Component'; import { toLowerCase } from '../../utils/mixins'; +import { ComponentOptions, ComponentProperties } from './types'; const type = 'table'; @@ -14,8 +15,8 @@ export default class ComponentTable extends Component { }; } - initialize(props: any, opts: any) { - super.initialize(props, opts); + constructor(props: ComponentProperties = {}, opt: ComponentOptions) { + super(props, opt); const components = this.get('components')!; !components.length && components.add({ type: 'tbody' }); } diff --git a/src/dom_components/model/ComponentTableBody.ts b/src/dom_components/model/ComponentTableBody.ts index ef12c8702..d4d4dbda9 100644 --- a/src/dom_components/model/ComponentTableBody.ts +++ b/src/dom_components/model/ComponentTableBody.ts @@ -1,5 +1,6 @@ import Component from './Component'; import { toLowerCase } from '../../utils/mixins'; +import { ComponentOptions, ComponentProperties } from './types'; const type = 'tbody'; @@ -17,8 +18,8 @@ export default class ComponentTableBody extends Component { }; } - initialize(props: any, opts: any) { - super.initialize(props, opts); + constructor(props: ComponentProperties = {}, opt: ComponentOptions) { + super(props, opt); const components = this.get('components')!; let columns = this.get('columns'); let rows = this.get('rows'); diff --git a/src/dom_components/model/ComponentText.ts b/src/dom_components/model/ComponentText.ts index 810a24459..7561e4d7a 100644 --- a/src/dom_components/model/ComponentText.ts +++ b/src/dom_components/model/ComponentText.ts @@ -1,5 +1,6 @@ import { isFunction } from 'underscore'; import Component from './Component'; +import { ComponentOptions, ComponentProperties } from './types'; export default class ComponentText extends Component { get defaults() { @@ -12,8 +13,8 @@ export default class ComponentText extends Component { }; } - initialize(props: any, opts: any) { - super.initialize(props, opts); + constructor(props: ComponentProperties = {}, opt: ComponentOptions) { + super(props, opt); this.__checkInnerChilds(); } diff --git a/src/dom_components/model/ComponentVideo.ts b/src/dom_components/model/ComponentVideo.ts index 709560c54..2100d83a9 100644 --- a/src/dom_components/model/ComponentVideo.ts +++ b/src/dom_components/model/ComponentVideo.ts @@ -1,6 +1,7 @@ import { ObjectAny } from '../../common'; import { isDef, isEmptyObj, toLowerCase } from '../../utils/mixins'; import ComponentImage from './ComponentImage'; +import { ComponentOptions, ComponentProperties } from './types'; const type = 'video'; const yt = 'yt'; @@ -38,14 +39,13 @@ export default class ComponentVideo extends ComponentImage { }; } - initialize(props: any, opts: any) { - this.em = opts.em; + constructor(props: ComponentProperties = {}, opt: ComponentOptions) { + super(props, opt); if (this.get('src')) this.parseFromSrc(); this.updatePropsFromAttr(); this.updateTraits(); this.on('change:provider', this.updateTraits); this.on('change:videoId change:provider', this.updateSrc); - super.initialize(props, opts); } updatePropsFromAttr() { diff --git a/src/dom_components/model/Components.ts b/src/dom_components/model/Components.ts index 71dfcbfe9..bc0181d32 100644 --- a/src/dom_components/model/Components.ts +++ b/src/dom_components/model/Components.ts @@ -1,11 +1,12 @@ import { isEmpty, isArray, isString, isFunction, each, includes, extend, flatten, keys } from 'underscore'; import Component from './Component'; -import { AddOptions, Collection, ObjectAny, OptionAsDocument } from '../../common'; +import { AddOptions, Collection, OptionAsDocument } from '../../common'; import { DomComponentsConfig } from '../config/config'; import EditorModel from '../../editor/model/Editor'; import ComponentManager from '..'; import CssRule from '../../css_composer/model/CssRule'; -import { ComponentAdd, ComponentDefinitionDefined, ComponentProperties } from './types'; + +import { ComponentAdd, ComponentAddType, ComponentDefinition, ComponentDefinitionDefined, ComponentProperties } from './types'; import ComponentText from './ComponentText'; import ComponentWrapper from './ComponentWrapper'; import { ComponentsEvents } from '../types'; @@ -71,7 +72,7 @@ const getComponentsFromDefs = ( }; export interface ComponentsOptions { - em?: EditorModel; + em: EditorModel; config?: DomComponentsConfig; domc?: ComponentManager; } @@ -82,19 +83,19 @@ export default class Components extends Collection { opt!: ComponentsOptions; config?: DomComponentsConfig; - em!: EditorModel; + em: EditorModel; domc?: ComponentManager; parent?: Component; - __firstAdd?: any; - initialize(models: any, opt: ComponentsOptions = {}) { + constructor(models: any, opt: ComponentsOptions) { + super(models, opt); this.opt = opt; this.listenTo(this, 'add', this.onAdd); this.listenTo(this, 'remove', this.removeChildren); this.listenTo(this, 'reset', this.resetChildren); const { em, config } = opt; this.config = config; - this.em = em!; + this.em = em; this.domc = opt.domc || em?.Components; } @@ -263,14 +264,17 @@ Component> { return components; } - /** @ts-ignore */ - add(models: ComponentAdd, opt: AddOptions & { previousModels?: Component[]; keepIds?: string[] } = {}) { + add(model: Exclude, opt?: AddOptions & { previousModels?: Component[]; keepIds?: string[] }): Component; + add(models: ComponentAddType[], opt?: AddOptions & { previousModels?: Component[]; keepIds?: string[] }): Component[]; + add(models: ComponentAdd, opt?: AddOptions & { previousModels?: Component[]; keepIds?: string[] }): Component|Component[]; + add(models: unknown, opt: AddOptions & { previousModels?: Component[]; keepIds?: string[] } = {}): unknown { + if (models == undefined) return; + opt.keepIds = [...(opt.keepIds || []), ...getComponentIds(opt.previousModels)]; if (isString(models)) { models = this.parseString(models, opt)!; } else if (isArray(models)) { - models = [...models]; models.forEach((item: string, index: number) => { if (isString(item)) { const nodes = this.parseString(item, opt); @@ -279,21 +283,19 @@ Component> { }); } - const isMult = isArray(models); - // @ts-ignore - models = (isMult ? models : [models]).filter(Boolean).map((model: any) => this.processDef(model)); - // @ts-ignore - models = isMult ? flatten(models as any, 1) : models[0]; + const processedModels = (isArray(models) ? models : [models]) + .filter(Boolean) + .map((model: any) => this.processDef(model)); - const result = Collection.prototype.add.apply(this, [models as any, opt]); - this.__firstAdd = result; - return result; + models = isArray(models) ? flatten(processedModels as any, 1) : processedModels[0]; + + return super.add(models as any, opt); } /** * Process component definition. */ - processDef(mdl: any) { + processDef(mdl: Component | ComponentDefinition | ComponentDefinitionDefined) { // Avoid processing Models if (mdl.cid && mdl.ccid) return mdl; const { em, config = {} } = this; @@ -304,12 +306,14 @@ Component> { model = { ...model }; // Avoid 'Cannot delete property ...' const modelPr = processor(model); if (modelPr) { + //@ts-ignore each(model, (val, key) => delete model[key]); extend(model, modelPr); } } // React JSX preset + //@ts-ignore if (model.$$typeof && typeof model.props == 'object') { model = { ...model }; model.props = { ...model.props }; @@ -318,6 +322,7 @@ Component> { const { parserHtml } = parser; each(model, (value, key) => { + //@ts-ignore if (!includes(['props', 'type'], key)) delete model[key]; }); const { props } = model; @@ -349,8 +354,7 @@ Component> { const avoidInline = em && em.getConfig().avoidInlineStyle; domc && domc.Component.ensureInList(model); - // @ts-ignore - if (!isEmpty(style) && !avoidInline && em && em.get && em.getConfig().forceClass && !opts.temporary) { + if (!isEmpty(style) && !avoidInline && em && em.getConfig().forceClass && !opts.temporary) { const name = model.cid; em.Css.setClassRule(name, style); model.setStyle({}); @@ -366,34 +370,5 @@ Component> { }; triggerAdd(model); } - // this.__onAddEnd(); } - - // __onAddEnd = debounce(function () { - // // TODO to check symbols on load, probably this might be removed as symbols - // // are always recovered from the model - // // const { domc } = this; - // // const allComp = (domc && domc.allById()) || {}; - // // const firstAdd = this.__firstAdd; - // // const toCheck = isArray(firstAdd) ? firstAdd : [firstAdd]; - // // const silent = { silent: true }; - // // const onAll = comps => { - // // comps.forEach(comp => { - // // const symbol = comp.get(keySymbols); - // // const symbolOf = comp.get(keySymbol); - // // if (symbol && isArray(symbol) && isString(symbol[0])) { - // // comp.set( - // // keySymbols, - // // symbol.map(smb => allComp[smb]).filter(i => i), - // // silent - // // ); - // // } - // // if (isString(symbolOf)) { - // // comp.set(keySymbol, allComp[symbolOf], silent); - // // } - // // onAll(comp.components()); - // // }); - // // }; - // // onAll(toCheck); - // }); } diff --git a/src/dom_components/model/types.ts b/src/dom_components/model/types.ts index 17b9a54d6..4a94f52d1 100644 --- a/src/dom_components/model/types.ts +++ b/src/dom_components/model/types.ts @@ -10,6 +10,7 @@ import ComponentView from '../view/ComponentView'; import Component from './Component'; import Components from './Components'; import { ToolbarButtonProps } from './ToolbarButton'; +import { ParseNodeOptions } from '../../parser/config/config'; export type DragMode = 'translate' | 'absolute' | ''; @@ -17,10 +18,15 @@ export type DraggableDroppableFn = (source: Component, target: Component, index? export interface AddComponentsOption extends AddOptions, OptionAsDocument {} -export interface ComponentStackItem { +interface ComponentWithCheck{ + new (props: any, opt: ComponentOptions): C; + isComponent(node: HTMLElement, opts?: ParseNodeOptions): ComponentDefinitionDefined|undefined|boolean; +} + +export interface ComponentStackItem = ComponentView>{ id: string; - model: typeof Component; - view: typeof ComponentView; + model: ComponentWithCheck; + view: new (opt: any) => CV; } /** @@ -262,7 +268,7 @@ export interface ComponentModelProperties extends ComponentProperties { [key: string]: any; } -type ComponentAddType = Component | ComponentDefinition | ComponentDefinitionDefined | string; +export type ComponentAddType = Component | ComponentDefinition | ComponentDefinitionDefined | string; export type ComponentAdd = ComponentAddType | ComponentAddType[]; @@ -290,8 +296,8 @@ export interface ToHTMLOptions extends OptionAsDocument { } export interface ComponentOptions { - em?: EditorModel; - config?: DomComponentsConfig; + em: EditorModel; + config: DomComponentsConfig; frame?: Frame; temporary?: boolean; avoidChildren?: boolean; diff --git a/src/parser/model/ParserHtml.ts b/src/parser/model/ParserHtml.ts index f91e3ac95..d98c77d65 100644 --- a/src/parser/model/ParserHtml.ts +++ b/src/parser/model/ParserHtml.ts @@ -164,7 +164,6 @@ const ParserHtml = (em?: EditorModel, config: ParserConfig & { returnArray?: boo let result: ComponentDefinitionDefined = {}; if (compTypes) { - let obj; const type = node.getAttribute?.(`${this.modelAttrStart}type`); // If the type is already defined, use it @@ -174,17 +173,16 @@ const ParserHtml = (em?: EditorModel, config: ParserConfig & { returnArray?: boo // Find the component type for (let i = 0; i < compTypes.length; i++) { const compType = compTypes[i]; - obj = compType.model.isComponent(node, opts); + let obj = compType.model.isComponent(node, opts); if (obj) { if (typeof obj !== 'object') { obj = { type: compType.id }; } + result = obj break; } } - - result = obj as ComponentDefinitionDefined; } } diff --git a/test/specs/dom_components/model/Component.ts b/test/specs/dom_components/model/Component.ts index 3ec3415ef..1d6b27e6b 100644 --- a/test/specs/dom_components/model/Component.ts +++ b/test/specs/dom_components/model/Component.ts @@ -328,8 +328,9 @@ describe('Component', () => { obj.append([{}, {}]); const comps = obj.components(); expect(comps.length).toEqual(2); - obj.append({}); + const result = obj.append({}); expect(comps.length).toEqual(3); + expect(result[0].em).toEqual(em); }); test('components() set new collection', () => { @@ -338,6 +339,8 @@ describe('Component', () => { const result = obj.components(); expect(result.length).toEqual(1); expect(result.models[0].get('tagName')).toEqual('span'); + + expect(result.em).toEqual(em); }); test('Propagate properties to children', () => { @@ -664,18 +667,21 @@ describe('Components', () => { var c = new Components([], compOpts); var m = c.add({}); expect(m instanceof Component).toEqual(true); + expect(m.em).toEqual(em); }); test('Creates image component correctly', () => { var c = new Components([], compOpts); var m = c.add({ type: 'image' }); expect(m instanceof ComponentImage).toEqual(true); + expect(m.em).toEqual(em); }); test('Creates text component correctly', () => { var c = new Components([], compOpts); var m = c.add({ type: 'text' }); expect(m instanceof ComponentText).toEqual(true); + expect(m.em).toEqual(em); }); test('Avoid conflicting components with the same ID', () => { diff --git a/test/specs/dom_components/view/ComponentImageView.ts b/test/specs/dom_components/view/ComponentImageView.ts index 189be3b28..86b998576 100644 --- a/test/specs/dom_components/view/ComponentImageView.ts +++ b/test/specs/dom_components/view/ComponentImageView.ts @@ -9,7 +9,7 @@ describe('ComponentImageView', () => { beforeEach(() => { em = new Editor(); - model = new Component({}, { em }); + model = new Component({}, { em, config: em.Components.config }); const cmpViewOpts = { model, config: { em }, diff --git a/test/specs/selector_manager/view/ClassTagsView.ts b/test/specs/selector_manager/view/ClassTagsView.ts index dfb03ef73..95c368e78 100644 --- a/test/specs/selector_manager/view/ClassTagsView.ts +++ b/test/specs/selector_manager/view/ClassTagsView.ts @@ -16,7 +16,7 @@ describe('ClassTagsView', () => { let em: Editor; let compTest: Component; const getSelectorNames = (arr: Selector[] | Selectors) => arr.map(item => item.getFullName()); - const newComponent = (obj: any) => new Component(obj, { em }); + const newComponent = (obj: any) => new Component(obj, { em, config: em.Components.config }); const newRule = (obj: any) => new Rule(obj, { em }); beforeAll(() => { @@ -45,7 +45,7 @@ describe('ClassTagsView', () => { }, }; - compTest = new Component({}, { em }); + compTest = new Component({}, { em, config: em.Components.config }); testContext.compTargetStub = compTest; fixtures.innerHTML = ''; diff --git a/test/specs/style_manager/view/PropertyColorView.ts b/test/specs/style_manager/view/PropertyColorView.ts index f68cacf28..5b5114d05 100644 --- a/test/specs/style_manager/view/PropertyColorView.ts +++ b/test/specs/style_manager/view/PropertyColorView.ts @@ -91,7 +91,7 @@ describe('PropertyColorView', () => { describe('Init property', () => { beforeEach(() => { em = new Editor(); - component = new Component({}, { em }); + component = new Component({}, { em, config: em.Components.config }); model = new Property({ type: 'color', property: propName, diff --git a/test/specs/style_manager/view/PropertyView.ts b/test/specs/style_manager/view/PropertyView.ts index 394f72123..8942c8b00 100644 --- a/test/specs/style_manager/view/PropertyView.ts +++ b/test/specs/style_manager/view/PropertyView.ts @@ -78,7 +78,7 @@ describe('PropertyView', () => { describe('Init property', () => { beforeEach(() => { em = new Editor({}); - component = new Component({}, { em }); + component = new Component({}, { em, config: em.Components.config }); model = new Property({ property: propName, default: defValue, diff --git a/test/specs/trait_manager/model/TraitsModel.ts b/test/specs/trait_manager/model/TraitsModel.ts index 7a6b5ad9b..904c85541 100644 --- a/test/specs/trait_manager/model/TraitsModel.ts +++ b/test/specs/trait_manager/model/TraitsModel.ts @@ -12,7 +12,7 @@ describe('TraitModels', () => { beforeEach(() => { em = new Editor().getModel(); - target = new Component({}, { em }); + target = new Component({}, { em, config: em.Components.config }); trait = new Trait( { name: modelName, diff --git a/test/specs/trait_manager/view/TraitsView.ts b/test/specs/trait_manager/view/TraitsView.ts index 56fd17150..88749f3b8 100644 --- a/test/specs/trait_manager/view/TraitsView.ts +++ b/test/specs/trait_manager/view/TraitsView.ts @@ -3,6 +3,7 @@ import TraitView from '../../../../src/trait_manager/view/TraitView'; import Component from '../../../../src/dom_components/model/Component'; import EditorModel from '../../../../src/editor/model/Editor'; import Editor from '../../../../src/editor'; +import { ComponentOptions } from '../../../../src/dom_components/model/types'; describe('TraitView', () => { let obj: TraitView; @@ -10,11 +11,11 @@ describe('TraitView', () => { let modelName = 'title'; let target: Component; let em: EditorModel; - let config: { em: EditorModel }; + let config: ComponentOptions; beforeEach(() => { em = new Editor().getModel(); - config = { em }; + config = { em, config: em.Components.config }; target = new Component({}, config); trait = new Trait( {