From 369caed2f3d47c85415b120903100c0e8c44690a Mon Sep 17 00:00:00 2001 From: mohamedsalem401 Date: Fri, 3 Jan 2025 15:28:40 +0200 Subject: [PATCH] Move ovveriding collection variables to component watcher --- .../CollectionComponent.ts | 95 +------------------ .../src/dom_components/model/Component.ts | 10 +- .../model/ComponentDynamicValueWatcher.ts | 31 +++++- .../model/DynamicValueWatcher.ts | 41 ++++---- 4 files changed, 57 insertions(+), 120 deletions(-) diff --git a/packages/core/src/data_sources/model/collection_component/CollectionComponent.ts b/packages/core/src/data_sources/model/collection_component/CollectionComponent.ts index 2be747d91..f6a69c5f3 100644 --- a/packages/core/src/data_sources/model/collection_component/CollectionComponent.ts +++ b/packages/core/src/data_sources/model/collection_component/CollectionComponent.ts @@ -8,12 +8,7 @@ import { ObjectAny } from '../../../common'; import EditorModel from '../../../editor/model/Editor'; import { keyCollectionsStateMap } from '../../../dom_components/model/Component'; import { CollectionDefinition, CollectionState, CollectionsStateMap } from './types'; -import { - keyCollectionDefinition, - keyInnerCollectionState, - CollectionComponentType, - CollectionVariableType, -} from './constants'; +import { keyCollectionDefinition, keyInnerCollectionState, CollectionComponentType } from './constants'; export default class CollectionComponent extends Component { constructor(props: CollectionDefinition & ComponentProperties, opt: ComponentOptions) { @@ -53,12 +48,11 @@ export default class CollectionComponent extends Component { }; if (index === start_index) { - const { clonedBlock } = resolveBlockValues(collectionsStateMap, block); - const type = em.Components.getType(clonedBlock?.type || 'default'); + const type = em.Components.getType(block?.type || 'default'); const model = type.model; blockComponent = new model( { - ...clonedBlock, + ...block, [keyCollectionsStateMap]: collectionsStateMap, }, opt, @@ -126,9 +120,8 @@ function resolveComponent( collectionsStateMap: CollectionsStateMap, em: EditorModel, ) { - const { resolvedCollectionValues } = resolveBlockValues(collectionsStateMap, block); - Object.keys(resolvedCollectionValues).length && component!.setSymbolOverride(Object.keys(resolvedCollectionValues)); - component!.set(resolvedCollectionValues); + // @ts-ignore + component!.set(block); const children: ComponentDefinition[] = []; for (let index = 0; index < component!.components().length; index++) { @@ -147,81 +140,3 @@ function resolveComponent( return componentDefinition; } - -// TODO: remove this function -function resolveBlockValues(collectionsStateMap: CollectionsStateMap, block: ObjectAny) { - const clonedBlock = deepCloneObject(block); - const resolvedCollectionValues: ObjectAny = {}; - - if (typeof clonedBlock === 'object') { - const blockKeys = Object.keys(clonedBlock); - for (const key of blockKeys) { - let blockValue = clonedBlock[key]; - if (key === keyCollectionDefinition) continue; - let hasCollectionVariable = false; - - if (typeof blockValue === 'object') { - const isCollectionVariable = blockValue.type === 'parent-collection-variable'; - if (isCollectionVariable) { - hasCollectionVariable = true; - } else if (Array.isArray(blockValue)) { - clonedBlock[key] = blockValue.map((arrayItem: any) => { - const { clonedBlock, resolvedCollectionValues: itemOverrideKeys } = resolveBlockValues( - collectionsStateMap, - arrayItem, - ); - if (!isEmptyObject(itemOverrideKeys)) { - hasCollectionVariable = true; - } - - return typeof arrayItem === 'object' ? clonedBlock : arrayItem; - }); - } else { - const { clonedBlock, resolvedCollectionValues: itemOverrideKeys } = resolveBlockValues( - collectionsStateMap, - blockValue, - ); - clonedBlock[key] = clonedBlock; - - if (!isEmptyObject(itemOverrideKeys)) { - hasCollectionVariable = true; - } - } - - if (hasCollectionVariable && key !== 'components') { - resolvedCollectionValues[key] = clonedBlock[key]; - } - } - } - } - - return { clonedBlock, resolvedCollectionValues }; -} - -function isEmptyObject(itemOverrideKeys: ObjectAny) { - return Object.keys(itemOverrideKeys).length === 0; -} - -/** - * Deeply clones an object. - * @template T The type of the object to clone. - * @param {T} obj The object to clone. - * @returns {T} A deep clone of the object, or the original object if it's not an object or is null. Returns undefined if input is undefined. - */ -function deepCloneObject | null | undefined>(obj: T): T { - if (obj === null) return null as T; - if (obj === undefined) return undefined as T; - if (typeof obj !== 'object' || Array.isArray(obj)) { - return obj; // Return primitives directly - } - - const clonedObj: Record = {}; - - for (const key in obj) { - if (Object.prototype.hasOwnProperty.call(obj, key)) { - clonedObj[key] = deepCloneObject(obj[key]); - } - } - - return clonedObj as T; -} diff --git a/packages/core/src/dom_components/model/Component.ts b/packages/core/src/dom_components/model/Component.ts index 6f9b3b1c4..e0250e513 100644 --- a/packages/core/src/dom_components/model/Component.ts +++ b/packages/core/src/dom_components/model/Component.ts @@ -368,10 +368,10 @@ export default class Component extends StyleableModel { } // @ts-ignore - const componentDVListener = this.componentDVListener || options.componentDVListener; - const evaluatedAttributes = componentDVListener.addProps(attributes, options); + this.componentDVListener = this.componentDVListener || options.componentDVListener; + const evaluatedProps = this.componentDVListener.addProps(attributes, options); - return super.set(evaluatedAttributes, options); + return super.set(evaluatedProps, options); } __postAdd(opts: { recursive?: boolean } = {}) { @@ -692,9 +692,7 @@ export default class Component extends StyleableModel { * component.setAttributes({ id: 'test', 'data-key': 'value' }); */ setAttributes(attrs: ObjectAny, opts: SetAttrOptions = { skipWatcherUpdates: false, fromDataSource: false }) { - // @ts-ignore - const componentDVListener = this.componentDVListener || opts.componentDVListener; - const evaluatedAttributes = componentDVListener.setAttributes(attrs, opts); + const evaluatedAttributes = this.componentDVListener.setAttributes(attrs, opts); this.set('attributes', { ...evaluatedAttributes }, opts); return this; diff --git a/packages/core/src/dom_components/model/ComponentDynamicValueWatcher.ts b/packages/core/src/dom_components/model/ComponentDynamicValueWatcher.ts index e311b969d..8abe42e49 100644 --- a/packages/core/src/dom_components/model/ComponentDynamicValueWatcher.ts +++ b/packages/core/src/dom_components/model/ComponentDynamicValueWatcher.ts @@ -1,4 +1,5 @@ import { ObjectAny } from '../../common'; +import { CollectionVariableType } from '../../data_sources/model/collection_component/constants'; import { CollectionsStateMap } from '../../data_sources/model/collection_component/types'; import EditorModel from '../../editor/model/Editor'; import Component from './Component'; @@ -10,7 +11,7 @@ export class ComponentDynamicValueWatcher { private attributeWatcher: DynamicValueWatcher; constructor( - component: Component | undefined, + private component: Component | undefined, options: { em: EditorModel; collectionsStateMap: CollectionsStateMap; @@ -19,6 +20,7 @@ export class ComponentDynamicValueWatcher { this.propertyWatcher = new DynamicValueWatcher(component, this.createPropertyUpdater(), options); this.attributeWatcher = new DynamicValueWatcher(component, this.createAttributeUpdater(), options); } + private createPropertyUpdater() { return (component: Component | undefined, key: string, value: any) => { if (!component) return; @@ -34,24 +36,45 @@ export class ComponentDynamicValueWatcher { } bindComponent(component: Component) { + this.component = component; this.propertyWatcher.bindComponent(component); this.attributeWatcher.bindComponent(component); + this.updateSymbolOverride(); } addProps(props: ObjectAny, options?: DynamicWatchersOptions) { - return this.propertyWatcher.addDynamicValues(props, options); + const evaluatedProps = this.propertyWatcher.addDynamicValues(props, options); + return evaluatedProps; } addAttributes(attributes: ObjectAny, options?: DynamicWatchersOptions) { - return this.attributeWatcher.addDynamicValues(attributes, options); + const evaluatedAttributes = this.attributeWatcher.addDynamicValues(attributes, options); + this.updateSymbolOverride(); + return evaluatedAttributes; } setAttributes(attributes: ObjectAny, options?: DynamicWatchersOptions) { - return this.attributeWatcher.setDynamicValues(attributes, options); + const evaluatedAttributes = this.attributeWatcher.setDynamicValues(attributes, options); + this.updateSymbolOverride(); + return evaluatedAttributes; } removeAttributes(attributes: string[]) { this.attributeWatcher.removeListeners(attributes); + this.updateSymbolOverride(); + } + + updateSymbolOverride() { + if (!this.component) return; + + const keys = this.propertyWatcher.getDynamicValuesOfType(CollectionVariableType); + const attributesKeys = this.attributeWatcher.getDynamicValuesOfType(CollectionVariableType); + + const combinedKeys = [...keys]; + const haveOverridenAttributes = Object.keys(attributesKeys).length; + if (haveOverridenAttributes) combinedKeys.push('attributes'); + + this.component.setSymbolOverride(combinedKeys); } getDynamicPropsDefs() { diff --git a/packages/core/src/dom_components/model/DynamicValueWatcher.ts b/packages/core/src/dom_components/model/DynamicValueWatcher.ts index 4e17b1826..8d12560fd 100644 --- a/packages/core/src/dom_components/model/DynamicValueWatcher.ts +++ b/packages/core/src/dom_components/model/DynamicValueWatcher.ts @@ -1,3 +1,4 @@ +import { DynamicValueDefinition } from './../../data_sources/types'; import { CollectionsStateMap } from '../../data_sources/model/collection_component/types'; import { ObjectAny } from '../../common'; import DynamicVariableListenerManager from '../../data_sources/model/DataVariableListenerManager'; @@ -5,6 +6,7 @@ import { evaluateDynamicValueDefinition, isDynamicValueDefinition } from '../../ import { DynamicValue } from '../../data_sources/types'; import EditorModel from '../../editor/model/Editor'; import Component from './Component'; +import CollectionVariable from '../../data_sources/model/collection_component/CollectionVariable'; export interface DynamicWatchersOptions { skipWatcherUpdates?: boolean; @@ -22,22 +24,6 @@ export class DynamicValueWatcher { }, ) {} - getStaticValues(values: ObjectAny | undefined): ObjectAny { - if (!values) return {}; - const evaluatedValues: ObjectAny = { ...values }; - const propsKeys = Object.keys(values); - - for (const key of propsKeys) { - const valueDefinition = values[key]; - if (!isDynamicValueDefinition(valueDefinition)) continue; - - const { value } = evaluateDynamicValueDefinition(valueDefinition, this.options); - evaluatedValues[key] = value; - } - - return evaluatedValues; - } - bindComponent(component: Component) { this.component = component; } @@ -53,7 +39,7 @@ export class DynamicValueWatcher { addDynamicValues(values: ObjectAny | undefined, options: DynamicWatchersOptions = {}) { if (!values) return {}; - const { evaluatedValues, dynamicValues } = this.getDynamicValues(values); + const { evaluatedValues, dynamicValues } = this.evaluateValues(values); const shouldSkipWatcherUpdates = options.skipWatcherUpdates || options.fromDataSource; if (!shouldSkipWatcherUpdates) { @@ -79,13 +65,14 @@ export class DynamicValueWatcher { } } - private getDynamicValues(values: ObjectAny) { + private evaluateValues(values: ObjectAny) { const dynamicValues: { [key: string]: DynamicValue; } = {}; const evaluatedValues: { - [key: string]: DynamicValue; + [key: string]: any; } = { ...values }; + const valuesToBeOverriden: string[] = []; const propsKeys = Object.keys(values); for (let index = 0; index < propsKeys.length; index++) { const key = propsKeys[index]; @@ -95,9 +82,12 @@ export class DynamicValueWatcher { const { value, variable } = evaluateDynamicValueDefinition(values[key], this.options); evaluatedValues[key] = value; dynamicValues[key] = variable; + if (variable instanceof CollectionVariable) { + valuesToBeOverriden.push(key); + } } - return { evaluatedValues, dynamicValues }; + return { evaluatedValues, dynamicValues, valuesToBeOverriden }; } /** @@ -113,6 +103,8 @@ export class DynamicValueWatcher { delete this.dynamicVariableListeners[key]; } }); + + return propsKeys; } getSerializableValues(values: ObjectAny | undefined) { @@ -139,4 +131,13 @@ export class DynamicValueWatcher { return serializableValues; } + + getDynamicValuesOfType(type: DynamicValueDefinition['type']) { + const keys = Object.keys(this.dynamicVariableListeners).filter((key: string) => { + // @ts-ignore + return this.dynamicVariableListeners[key].dynamicVariable.get('type') === type; + }); + + return keys; + } }