From 9093df6c7b8791b8cd44acd636bcb762352f018a Mon Sep 17 00:00:00 2001 From: mohamedsalem401 Date: Fri, 20 Dec 2024 16:08:35 +0200 Subject: [PATCH] Allow traits to be dynamic ( again ) --- .../src/dom_components/model/Component.ts | 28 +++++++++--- .../model/ComponentDynamicValueListener.ts | 45 ++++++++++++++++++- .../core/src/trait_manager/model/Trait.ts | 35 --------------- .../ConditionalTraits.ts | 35 +++++++-------- .../__snapshots__/ConditionalTraits.ts.snap | 31 +++++++++---- 5 files changed, 104 insertions(+), 70 deletions(-) diff --git a/packages/core/src/dom_components/model/Component.ts b/packages/core/src/dom_components/model/Component.ts index 5708822a3..82c98e8b5 100644 --- a/packages/core/src/dom_components/model/Component.ts +++ b/packages/core/src/dom_components/model/Component.ts @@ -54,9 +54,9 @@ import { import { ComponentDynamicValueListener } from './ComponentDynamicValueListener'; import { DynamicValueWatcher } from './DynamicValueWatcher'; -export interface IComponent extends ExtractMethods {} +export interface IComponent extends ExtractMethods { } -export interface SetAttrOptions extends SetOptions, UpdateStyleOptions {} +export interface SetAttrOptions extends SetOptions, UpdateStyleOptions { } const escapeRegExp = (str: string) => { return str.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&'); @@ -70,6 +70,7 @@ export const keySymbol = '__symbol'; export const keySymbolOvrd = '__symbol_ovrd'; export const keyUpdate = ComponentsEvents.update; export const keyUpdateInside = ComponentsEvents.updateInside; +export const dynamicAttrKey = 'attributes-dynamic-value'; /** * The Component object represents a single node of our template structure, so when you update its properties the changes are @@ -222,12 +223,12 @@ export default class Component extends StyleableModel { return this.frame?.getPage(); } - preInit() {} + preInit() { } /** * Hook method, called once the model is created */ - init() {} + init() { } /** * Hook method, called when the model has been updated (eg. updated some model's property) @@ -235,12 +236,12 @@ export default class Component extends StyleableModel { * @param {*} value Property value, if triggered after some property update * @param {*} previous Property previous value, if triggered after some property update */ - updated(property: string, value: any, previous: any) {} + updated(property: string, value: any, previous: any) { } /** * Hook method, called once the model has been removed */ - removed() {} + removed() { } em!: EditorModel; opt!: ComponentOptions; @@ -1570,6 +1571,21 @@ export default class Component extends StyleableModel { let obj = Model.prototype.toJSON.call(this, opts); obj = { ...obj, ...this.componentDVListener.getDynamicPropsDefs() }; obj.attributes = this.componentDVListener.getAttributesDefsOrValues(this.getAttributes({ noClass: true })); + const dynamicTraitsObj = this.componentDVListener.getTraitsDefs(); + const keys = Object.keys(dynamicTraitsObj); + const serializedTraits: ObjectAny[] = []; + keys.forEach(key => { + const traitJSON = this.getTrait(key).toJSON(); + const traitValue = dynamicTraitsObj[key]; + serializedTraits.push({ + ...traitJSON, + name: key, + value: traitValue + }) + }); + if (serializedTraits.length > 0) { + obj[dynamicAttrKey] = serializedTraits; + } delete obj.componentDVListener; delete obj.traits; delete obj.attributes.class; diff --git a/packages/core/src/dom_components/model/ComponentDynamicValueListener.ts b/packages/core/src/dom_components/model/ComponentDynamicValueListener.ts index 6086b7594..a960f611d 100644 --- a/packages/core/src/dom_components/model/ComponentDynamicValueListener.ts +++ b/packages/core/src/dom_components/model/ComponentDynamicValueListener.ts @@ -1,11 +1,12 @@ import { ObjectAny } from '../../common'; import EditorModel from '../../editor/model/Editor'; -import Component from './Component'; +import Component, { dynamicAttrKey } from './Component'; import { DynamicValueWatcher } from './DynamicValueWatcher'; export class ComponentDynamicValueListener { propertyWatchClass: DynamicValueWatcher; attributeWatchClass: DynamicValueWatcher; + traitsWatchClass: DynamicValueWatcher; constructor( private component: Component, @@ -18,20 +19,46 @@ export class ComponentDynamicValueListener { this.attributeWatchClass = new DynamicValueWatcher((key: string, value: any) => { this.component.setAttributes({ [key]: value }); }, em); + + this.traitsWatchClass = new DynamicValueWatcher((key: string, value: any) => { + this.component.updateTrait(key, { value }); + const trait = this.component.getTrait(key); + trait.setTargetValue(value); + }, em); } static evaluateComponentDef(values: ObjectAny, em: EditorModel) { const props = DynamicValueWatcher.getStaticValues(values, em); + if (values.attributes) { props.attributes = DynamicValueWatcher.getStaticValues(values.attributes, em); } + if (Array.isArray(values[dynamicAttrKey]) && values[dynamicAttrKey].length > 0) { + values.traits = values.traits + ? [...values[dynamicAttrKey], ...values.traits] + : values[dynamicAttrKey]; + } + + if (values.traits) { + const evaluatedTraitsValues = DynamicValueWatcher.getStaticValues( + values.traits.map((trait: any) => trait.value), + em + ); + + props.traits = values.traits.map((trait: any, index: number) => ({ + ...trait, + value: evaluatedTraitsValues[index] + })); + } + return props; } watchComponentDef(values: ObjectAny) { this.watchProps(values); this.watchAttributes(values.attributes); + this.watchTraits(values.traits); } watchProps(props: ObjectAny) { @@ -52,6 +79,18 @@ export class ComponentDynamicValueListener { this.attributeWatchClass.watchDynamicValue(attributes); } + watchTraits(traits: (string | ObjectAny)[]) { + const evaluatedTraits: { [key: string]: ObjectAny } = {} + traits?.forEach((trait: any) => { + if (typeof trait === 'string' || !trait.name) { + return; + } else if (typeof trait === 'object') { + evaluatedTraits[trait.name] = trait.value; + } + }); + this.traitsWatchClass.watchDynamicValue(evaluatedTraits); + } + removeAttributes(attributes: string[]) { this.attributeWatchClass.removeListeners(attributes); } @@ -60,6 +99,10 @@ export class ComponentDynamicValueListener { return this.attributeWatchClass.getSerializableValues(attributes); } + getTraitsDefs() { + return this.traitsWatchClass.getAllSerializableValues(); + } + getPropsDefsOrValues(props: ObjectAny) { return this.propertyWatchClass.getSerializableValues(props); } diff --git a/packages/core/src/trait_manager/model/Trait.ts b/packages/core/src/trait_manager/model/Trait.ts index c3d17b889..20e33153b 100644 --- a/packages/core/src/trait_manager/model/Trait.ts +++ b/packages/core/src/trait_manager/model/Trait.ts @@ -58,30 +58,6 @@ export default class Trait extends Model { this.setTarget(target); } this.em = em; - - if (isDynamicValueDefinition(this.attributes.value)) { - const dataType = this.attributes.value.type; - switch (dataType) { - case DataVariableType: - this.dynamicVariable = new TraitDataVariable(this.attributes.value, { em: this.em, trait: this }); - break; - case ConditionalVariableType: { - const { condition, ifTrue, ifFalse } = this.attributes.value; - this.dynamicVariable = new DataCondition(condition, ifTrue, ifFalse, { em: this.em }); - break; - } - default: - return; - } - - const dv = this.dynamicVariable.getDataValue(); - this.set({ value: dv }); - this.dynamicVariableListener = new DynamicVariableListenerManager({ - em: this.em, - dataVariable: this.dynamicVariable, - updateValueFromDataVariable: this.updateValueFromDataVariable.bind(this), - }); - } } get parent() { @@ -116,11 +92,6 @@ export default class Trait extends Model { } } - updateValueFromDataVariable(value: string) { - this.setValue(value); - this.trigger('change:value'); - } - /** * Get the trait id. * @returns {String} @@ -166,12 +137,6 @@ export default class Trait extends Model { * @returns {any} */ getValue(opts?: TraitGetValueOptions) { - if (this.dynamicVariable) { - const dValue = this.dynamicVariable.getDataValue(); - - return dValue; - } - return this.getTargetValue(opts); } diff --git a/packages/core/test/specs/data_sources/model/conditional_variables/ConditionalTraits.ts b/packages/core/test/specs/data_sources/model/conditional_variables/ConditionalTraits.ts index 5a2419ce9..4734cf99b 100644 --- a/packages/core/test/specs/data_sources/model/conditional_variables/ConditionalTraits.ts +++ b/packages/core/test/specs/data_sources/model/conditional_variables/ConditionalTraits.ts @@ -4,7 +4,7 @@ import { MissingConditionError } from '../../../../../src/data_sources/model/con import { ConditionalVariableType } from '../../../../../src/data_sources/model/conditional_variables/DataCondition'; import { GenericOperation } from '../../../../../src/data_sources/model/conditional_variables/operators/GenericOperator'; import { NumberOperation } from '../../../../../src/data_sources/model/conditional_variables/operators/NumberOperator'; -import Component from '../../../../../src/dom_components/model/Component'; +import Component, { dynamicAttrKey } from '../../../../../src/dom_components/model/Component'; import ComponentWrapper from '../../../../../src/dom_components/model/ComponentWrapper'; import EditorModel from '../../../../../src/editor/model/Editor'; import { filterObjectForSnapshot, setupTestEditor } from '../../../../common'; @@ -135,8 +135,7 @@ describe('TraitConditionalVariable', () => { }).toThrow(MissingConditionError); }); - // TODO: Should we keep or delete saving triats with dynamic values? - it.skip('should store traits with conditional values correctly', () => { + it('should store traits with conditional values correctly', () => { const conditionalTrait = { type: ConditionalVariableType, condition: { @@ -165,13 +164,10 @@ describe('TraitConditionalVariable', () => { const frame = page.frames[0]; const storedComponent = frame.component.components[0]; - // expect(storedComponent[dynamicAttrKey]).toEqual({ - // dynamicTrait: conditionalTrait, - // }); + expect(storedComponent[dynamicAttrKey][0].value).toEqual(conditionalTrait); }); - // TODO: Should we keep or delete saving triats with dynamic values? - it.skip('should load traits with conditional values correctly', () => { + it('should load traits with conditional values correctly', () => { const projectData = { pages: [ { @@ -183,17 +179,18 @@ describe('TraitConditionalVariable', () => { attributes: { dynamicTrait: 'Default', }, - // [dynamicAttrKey]: { - // dynamicTrait: { - // condition: { - // left: 0, - // operator: '>', - // right: -1, - // }, - // ifTrue: 'Positive', - // type: 'conditional-variable', - // }, - // }, + [dynamicAttrKey]: [{ + name: 'dynamicTrait', + value: { + condition: { + left: 0, + operator: '>', + right: -1, + }, + ifTrue: 'Positive', + type: 'conditional-variable', + }, + }], type: 'text', }, ], diff --git a/packages/core/test/specs/data_sources/model/conditional_variables/__snapshots__/ConditionalTraits.ts.snap b/packages/core/test/specs/data_sources/model/conditional_variables/__snapshots__/ConditionalTraits.ts.snap index 34066fa8c..cc410caa3 100644 --- a/packages/core/test/specs/data_sources/model/conditional_variables/__snapshots__/ConditionalTraits.ts.snap +++ b/packages/core/test/specs/data_sources/model/conditional_variables/__snapshots__/ConditionalTraits.ts.snap @@ -14,17 +14,30 @@ exports[`TraitConditionalVariable should store traits with conditional values co "attributes": { "dynamicTrait": "Positive", }, - "attributes-dynamic-value": { - "dynamicTrait": { - "condition": { - "left": 0, - "operator": ">", - "right": -1, + "attributes-dynamic-value": [ + { + "category": "", + "changeProp": false, + "default": "", + "id": "data-variable-id", + "label": "", + "name": "dynamicTrait", + "options": [], + "placeholder": "", + "step": 1, + "type": "text", + "unit": "", + "value": { + "condition": { + "left": 0, + "operator": ">", + "right": -1, + }, + "ifTrue": "Positive", + "type": "conditional-variable", }, - "ifTrue": "Positive", - "type": "conditional-variable", }, - }, + ], "tagName": "h1", "type": "text", },