Browse Source

up DataSources

dynamic-values-improvements
mohamedsalem401 10 months ago
parent
commit
1ceb68ffbf
  1. 24
      packages/core/src/data_sources/model/ComponentDataVariable.ts
  2. 43
      packages/core/src/data_sources/model/conditional_variables/ComponentDataCondition.ts
  3. 6
      packages/core/src/data_sources/model/conditional_variables/ConditionStatement.ts
  4. 20
      packages/core/src/data_sources/model/conditional_variables/DataCondition.ts
  5. 104
      packages/core/src/data_sources/model/conditional_variables/DataConditionEvaluator.ts
  6. 11
      packages/core/src/data_sources/model/conditional_variables/operators/AnyTypeOperator.ts
  7. 16
      packages/core/src/data_sources/model/conditional_variables/operators/BaseOperator.ts
  8. 12
      packages/core/src/data_sources/model/conditional_variables/operators/BooleanOperator.ts
  9. 13
      packages/core/src/data_sources/model/conditional_variables/operators/NumberOperator.ts
  10. 16
      packages/core/src/data_sources/model/conditional_variables/operators/StringOperator.ts
  11. 6
      packages/core/src/data_sources/model/conditional_variables/operators/types.ts
  12. 7
      packages/core/src/data_sources/model/conditional_variables/types.ts
  13. 101
      packages/core/src/data_sources/model/data_collection/ComponentDataCollection.ts
  14. 8
      packages/core/src/data_sources/utils.ts
  15. 3
      packages/core/src/dom_components/model/Component.ts

24
packages/core/src/data_sources/model/ComponentDataVariable.ts

@ -40,6 +40,14 @@ export default class ComponentDataVariable extends Component {
return this.dataResolver.get('path');
}
getCollectionId() {
return this.dataResolver.get('collectionId');
}
getVariableType() {
return this.dataResolver.get('variableType');
}
getDefaultValue() {
return this.dataResolver.get('defaultValue');
}
@ -48,14 +56,22 @@ export default class ComponentDataVariable extends Component {
return this.dataResolver.getDataValue();
}
getInnerHTML() {
return this.getDataValue();
resolvesFromCollection() {
return this.dataResolver.resolvesFromCollection();
}
getCollectionsStateMap() {
return this.get(keyCollectionsStateMap) ?? {};
}
getDataResolver() {
return this.get('dataResolver');
}
getInnerHTML() {
return this.getDataValue();
}
setPath(newPath: string) {
this.dataResolver.set('path', newPath);
}
@ -65,7 +81,7 @@ export default class ComponentDataVariable extends Component {
}
setDataResolver(props: DataVariableProps) {
this.dataResolver.set(props);
this.set('dataResolver', props);
}
/**
@ -90,9 +106,11 @@ export default class ComponentDataVariable extends Component {
this.__changesUp({ m: this });
}).bind(this),
);
this.on('change:dataResolver', () => {
this.dataResolver.set(this.get('dataResolver'));
});
this.on(`change:${keyCollectionsStateMap}`, (_: Component, value: DataCollectionStateMap) => {
this.dataResolver.updateCollectionsStateMap(value);
});

43
packages/core/src/data_sources/model/conditional_variables/ComponentDataCondition.ts

@ -7,11 +7,14 @@ import {
ComponentAddType,
} from '../../../dom_components/model/types';
import { toLowerCase } from '../../../utils/mixins';
import { DataCondition, DataConditionOutputChangedEvent, DataConditionProps, DataConditionType } from './DataCondition';
import { DataCondition, DataConditionProps, DataConditionType } from './DataCondition';
import { ConditionProps } from './DataConditionEvaluator';
import { StringOperation } from './operators/StringOperator';
import { ObjectAny } from '../../../common';
import { DataConditionIfTrueType, DataConditionIfFalseType } from './constants';
import { ModelDestroyOptions } from 'backbone';
import { keyCollectionsStateMap } from '../data_collection/constants';
import { DataCollectionStateMap } from '../data_collection/types';
export type DataConditionDisplayType = typeof DataConditionIfTrueType | typeof DataConditionIfFalseType;
@ -48,15 +51,20 @@ export default class ComponentDataCondition extends Component {
}
constructor(props: ComponentDataConditionProps, opt: ComponentOptions) {
const collectionsStateMap = props[keyCollectionsStateMap] as DataCollectionStateMap;
// @ts-ignore
super(props, opt);
const { condition } = props.dataResolver;
this.dataResolver = new DataCondition({ condition }, { em: opt.em });
this.dataResolver = new DataCondition({ condition }, { em: opt.em, collectionsStateMap });
this.listenToPropsChange();
}
getDataResolver() {
return this.get('dataResolver');
}
isTrue() {
return this.dataResolver.isTrue();
}
@ -77,6 +85,10 @@ export default class ComponentDataCondition extends Component {
return this.isTrue() ? this.getIfTrueContent() : this.getIfFalseContent();
}
setDataResolver(props: DataConditionProps) {
return this.set('dataResolver', props);
}
setCondition(newCondition: ConditionProps) {
this.dataResolver.setCondition(newCondition);
}
@ -89,6 +101,10 @@ export default class ComponentDataCondition extends Component {
this.setComponentsAtIndex(1, content);
}
getCollectionsStateMap() {
return this.get(keyCollectionsStateMap) ?? {};
}
getInnerHTML(opts?: ToHTMLOptions): string {
return this.getOutputContent()?.getInnerHTML(opts) ?? '';
}
@ -99,9 +115,27 @@ export default class ComponentDataCondition extends Component {
}
private listenToPropsChange() {
this.listenTo(
this.dataResolver,
'change',
(() => {
this.__changesUp({ m: this });
}).bind(this),
);
this.on('change:dataResolver', () => {
this.dataResolver.set(this.get('dataResolver'));
});
this.on(`change:${keyCollectionsStateMap}`, () => {
this.dataResolver.updateCollectionsStateMap(this.get(keyCollectionsStateMap));
});
}
private removePropsListeners() {
this.stopListening(this.dataResolver);
this.off('change:dataResolver');
this.off(`change:${keyCollectionsStateMap}`);
}
toJSON(opts?: ObjectAny): ComponentProperties {
@ -117,6 +151,11 @@ export default class ComponentDataCondition extends Component {
};
}
destroy(options?: ModelDestroyOptions | undefined): false | JQueryXHR {
this.removePropsListeners();
return super.destroy(options);
}
static isComponent(el: HTMLElement) {
return toLowerCase(el.tagName) === DataConditionType;
}

6
packages/core/src/data_sources/model/conditional_variables/ConditionStatement.ts

@ -1,10 +1,10 @@
import { Operator } from './operators/BaseOperator';
import { DataConditionOperation } from './operators/types';
import { SimpleOperator } from './operators/BaseOperator';
import { DataConditionSimpleOperation } from './types';
export class ConditionStatement {
constructor(
private leftValue: any,
private operator: Operator<DataConditionOperation>,
private operator: SimpleOperator<DataConditionSimpleOperation>,
private rightValue: any,
) {}

20
packages/core/src/data_sources/model/conditional_variables/DataCondition.ts

@ -4,12 +4,11 @@ import DataVariable, { DataVariableProps } from '../DataVariable';
import DataResolverListener from '../DataResolverListener';
import { resolveDynamicValue, isDataVariable } from '../../utils';
import { DataConditionEvaluator, ConditionProps } from './DataConditionEvaluator';
import { AnyTypeOperation } from './operators/AnyTypeOperator';
import { BooleanOperation } from './operators/BooleanOperator';
import { NumberOperation } from './operators/NumberOperator';
import { StringOperation } from './operators/StringOperator';
import { isUndefined } from 'underscore';
import { DataCollectionStateMap } from '../data_collection/types';
import { DataConditionSimpleOperation } from './types';
export const DataConditionType = 'data-condition' as const;
export const DataConditionEvaluationChangedEvent = 'data-condition-evaluation-changed';
@ -17,7 +16,7 @@ export const DataConditionOutputChangedEvent = 'data-condition-output-changed';
export interface ExpressionProps {
left?: any;
operator?: AnyTypeOperation | StringOperation | NumberOperation;
operator?: DataConditionSimpleOperation;
right?: any;
}
@ -33,6 +32,12 @@ export interface DataConditionProps {
ifFalse?: any;
}
type DataConditionOptions = {
em: EditorModel;
onValueChange?: () => void;
collectionsStateMap?: DataCollectionStateMap;
};
export class DataCondition extends Model<DataConditionProps> {
private em: EditorModel;
private collectionsStateMap: DataCollectionStateMap = {};
@ -53,13 +58,14 @@ export class DataCondition extends Model<DataConditionProps> {
};
}
constructor(props: DataConditionProps, opts: { em: EditorModel; onValueChange?: () => void }) {
constructor(props: DataConditionProps, opts: DataConditionOptions) {
if (isUndefined(props.condition)) {
opts.em.logError('No condition was provided to a conditional component.');
}
super(props, opts);
this.em = opts.em;
this.collectionsStateMap = opts.collectionsStateMap ?? {};
const { condition = {} } = props;
const instance = new DataConditionEvaluator({ condition }, { em: this.em });
@ -80,7 +86,12 @@ export class DataCondition extends Model<DataConditionProps> {
return this.get('ifFalse');
}
getOperations() {
return this._conditionEvaluator.getOperations();
}
setCondition(condition: ConditionProps) {
this.set('condition', condition);
this._conditionEvaluator.set('condition', condition);
this.trigger(DataConditionOutputChangedEvent, this.getDataValue());
}
@ -115,6 +126,7 @@ export class DataCondition extends Model<DataConditionProps> {
updateCollectionsStateMap(collectionsStateMap: DataCollectionStateMap) {
this.collectionsStateMap = collectionsStateMap;
this.emitConditionEvaluationChange();
}
private listenToPropsChange() {

104
packages/core/src/data_sources/model/conditional_variables/DataConditionEvaluator.ts

@ -1,15 +1,17 @@
import { DataVariableProps } from '../DataVariable';
import EditorModel from '../../../editor/model/Editor';
import { resolveDynamicValue, isDataVariable } from '../../utils';
import { resolveDynamicValue, isDataVariable, getDataResolverInstanceValue } from '../../utils';
import { ExpressionProps, LogicGroupProps } from './DataCondition';
import { LogicalGroupEvaluator } from './LogicalGroupEvaluator';
import { Operator } from './operators/BaseOperator';
import { SimpleOperator } from './operators/BaseOperator';
import { AnyTypeOperation, AnyTypeOperator } from './operators/AnyTypeOperator';
import { BooleanOperator } from './operators/BooleanOperator';
import { NumberOperator, NumberOperation } from './operators/NumberOperator';
import { StringOperator, StringOperation } from './operators/StringOperator';
import { Model } from '../../../common';
import { DataConditionOperation } from './operators/types';
import { DataConditionSimpleOperation } from './types';
import { isBoolean } from 'underscore';
import { DataCollectionStateMap } from '../data_collection/types';
export type ConditionProps = ExpressionProps | LogicGroupProps | boolean;
@ -17,45 +19,78 @@ interface DataConditionEvaluatorProps {
condition: ConditionProps;
}
interface DataConditionEvaluatorOptions {
em: EditorModel;
collectionsStateMap?: DataCollectionStateMap;
}
export class DataConditionEvaluator extends Model<DataConditionEvaluatorProps> {
private em: EditorModel;
private collectionsStateMap: DataCollectionStateMap = {};
constructor(props: DataConditionEvaluatorProps, opts: { em: EditorModel }) {
constructor(props: DataConditionEvaluatorProps, opts: DataConditionEvaluatorOptions) {
super(props);
this.em = opts.em;
this.collectionsStateMap = opts.collectionsStateMap ?? {};
}
evaluate(): boolean {
const condition = this.get('condition');
if (!condition || isBoolean(condition)) return !!condition;
const resolvedOperator = this.getOperator();
if (!resolvedOperator) return false;
return resolvedOperator.evaluate(this.getResolvedLeftValue(), this.getResolvedRightValue());
}
getDependentDataVariables(): DataVariableProps[] {
const condition = this.get('condition');
if (!condition) return [];
return this.extractDataVariables(condition);
}
getOperations() {
const operator = this.getOperator();
if (!operator || operator instanceof LogicalGroupEvaluator) return [];
return operator.getOperations();
}
updateCollectionStateMap(collectionsStateMap: DataCollectionStateMap) {
this.collectionsStateMap = collectionsStateMap;
}
private getOperator() {
const em = this.em;
const condition = this.get('condition');
if (typeof condition === 'boolean') return condition;
if (!condition || isBoolean(condition)) return;
let resolvedOperator: SimpleOperator<DataConditionSimpleOperation> | LogicalGroupEvaluator | undefined;
if (this.isLogicGroup(condition)) {
const { logicalOperator, statements } = condition;
const operator = new BooleanOperator(logicalOperator, { em });
const logicalGroup = new LogicalGroupEvaluator(operator, statements, { em });
return logicalGroup.evaluate();
resolvedOperator = new LogicalGroupEvaluator(operator, statements, { em });
}
if (this.isExpression(condition)) {
const { left, operator, right } = condition;
const evaluateLeft = resolveDynamicValue(left, this.em);
const evaluateRight = resolveDynamicValue(right, this.em);
const op = this.getOperator(evaluateLeft, operator);
if (!op) return false;
const evaluated = op.evaluate(evaluateLeft, evaluateRight);
return evaluated;
const { left, operator } = condition;
const evaluatedLeft = resolveDynamicValue(left, em);
resolvedOperator = this.resolveOperator(evaluatedLeft, operator);
}
this.em.logError('Invalid condition type.');
return false;
return resolvedOperator;
}
/**
* Factory method for creating operators based on the data type.
*/
private getOperator(left: any, operator: string | undefined): Operator<DataConditionOperation> | undefined {
private resolveOperator(
left: any,
operator: string | undefined,
): SimpleOperator<DataConditionSimpleOperation> | undefined {
const em = this.em;
if (this.isOperatorInEnum(operator, AnyTypeOperation)) {
@ -66,17 +101,9 @@ export class DataConditionEvaluator extends Model<DataConditionEvaluatorProps> {
return new StringOperator(operator as StringOperation, { em });
}
this.em?.logError(`Unsupported data type: ${typeof left}`);
return;
}
getDependentDataVariables(): DataVariableProps[] {
const condition = this.get('condition');
if (!condition) return [];
return this.extractDataVariables(condition);
}
private extractDataVariables(condition: ConditionProps): DataVariableProps[] {
const variables: DataVariableProps[] = [];
@ -102,6 +129,31 @@ export class DataConditionEvaluator extends Model<DataConditionEvaluatorProps> {
return Object.values(enumObject).includes(operator);
}
private resolveExpressionSide(property: 'left' | 'right'): any {
const condition = this.get('condition');
const { em, collectionsStateMap } = this;
if (!condition || typeof condition === 'boolean') {
return condition;
}
if (condition && typeof condition === 'object' && property in condition) {
const value = (condition as ExpressionProps)[property];
return getDataResolverInstanceValue(value, { em, collectionsStateMap });
}
return undefined;
}
private getResolvedLeftValue(): any {
return this.resolveExpressionSide('left');
}
private getResolvedRightValue(): any {
return this.resolveExpressionSide('right');
}
toJSON(options?: any) {
const condition = this.get('condition');
if (typeof condition === 'object') {

11
packages/core/src/data_sources/model/conditional_variables/operators/AnyTypeOperator.ts

@ -1,5 +1,6 @@
import { enumToArray } from '../../../utils';
import DataVariable from '../../DataVariable';
import { Operator } from './BaseOperator';
import { SimpleOperator } from './BaseOperator';
export enum AnyTypeOperation {
equals = 'equals',
@ -16,9 +17,11 @@ export enum AnyTypeOperation {
isDefaultValue = 'isDefaultValue', // For Datasource variables
}
export class AnyTypeOperator extends Operator<AnyTypeOperation> {
export class AnyTypeOperator extends SimpleOperator<AnyTypeOperation> {
protected operationsEnum = AnyTypeOperation;
evaluate(left: any, right: any): boolean {
switch (this.operation) {
switch (this.operationString) {
case 'equals':
return left === right;
case 'isTruthy':
@ -44,7 +47,7 @@ export class AnyTypeOperator extends Operator<AnyTypeOperation> {
case 'isDefaultValue':
return left instanceof DataVariable && left.get('defaultValue') === right;
default:
this.em?.logError(`Unsupported generic operation: ${this.operation}`);
this.em?.logWarning(`Unsupported generic operation: ${this.operationString}`);
return false;
}
}

16
packages/core/src/data_sources/model/conditional_variables/operators/BaseOperator.ts

@ -1,14 +1,20 @@
import EditorModel from '../../../../editor/model/Editor';
import { DataConditionOperation } from './types';
import { enumToArray } from '../../../utils';
import { DataConditionSimpleOperation } from '../types';
export abstract class Operator<OperationType extends DataConditionOperation> {
export abstract class SimpleOperator<OperationType extends DataConditionSimpleOperation> {
protected em: EditorModel;
protected operation: OperationType;
protected operationString: OperationType;
protected abstract operationsEnum: Record<string, OperationType>;
constructor(operation: any, opts: { em: EditorModel }) {
this.operation = operation;
constructor(operationString: any, opts: { em: EditorModel }) {
this.operationString = operationString;
this.em = opts.em;
}
abstract evaluate(left: any, right: any): boolean;
getOperations(): DataConditionSimpleOperation[] {
return enumToArray(this.operationsEnum);
}
}

12
packages/core/src/data_sources/model/conditional_variables/operators/BooleanOperator.ts

@ -1,4 +1,6 @@
import { Operator } from './BaseOperator';
import { enumToArray } from '../../../utils';
import { DataConditionSimpleOperation } from '../types';
import { SimpleOperator } from './BaseOperator';
export enum BooleanOperation {
and = 'and',
@ -6,11 +8,13 @@ export enum BooleanOperation {
xor = 'xor',
}
export class BooleanOperator extends Operator<BooleanOperation> {
export class BooleanOperator extends SimpleOperator<BooleanOperation> {
protected operationsEnum = BooleanOperation;
evaluate(statements: boolean[]): boolean {
if (!statements?.length) return false;
switch (this.operation) {
switch (this.operationString) {
case BooleanOperation.and:
return statements.every(Boolean);
case BooleanOperation.or:
@ -18,7 +22,7 @@ export class BooleanOperator extends Operator<BooleanOperation> {
case BooleanOperation.xor:
return statements.filter(Boolean).length === 1;
default:
this.em.logError(`Unsupported boolean operation: ${this.operation}`);
this.em.logWarning(`Unsupported boolean operation: ${this.operationString}`);
return false;
}
}

13
packages/core/src/data_sources/model/conditional_variables/operators/NumberOperator.ts

@ -1,4 +1,5 @@
import { Operator } from './BaseOperator';
import { enumToArray } from '../../../utils';
import { SimpleOperator } from './BaseOperator';
export enum NumberOperation {
greaterThan = '>',
@ -9,9 +10,13 @@ export enum NumberOperation {
notEquals = '!=',
}
export class NumberOperator extends Operator<NumberOperation> {
export class NumberOperator extends SimpleOperator<NumberOperation> {
protected operationsEnum = NumberOperation;
evaluate(left: number, right: number): boolean {
switch (this.operation) {
if (typeof left !== 'number') return false;
switch (this.operationString) {
case NumberOperation.greaterThan:
return left > right;
case NumberOperation.lessThan:
@ -25,7 +30,7 @@ export class NumberOperator extends Operator<NumberOperation> {
case NumberOperation.notEquals:
return left !== right;
default:
this.em.logError(`Unsupported number operation: ${this.operation}`);
this.em.logWarning(`Unsupported number operation: ${this.operationString}`);
return false;
}
}

16
packages/core/src/data_sources/model/conditional_variables/operators/StringOperator.ts

@ -1,4 +1,4 @@
import { Operator } from './BaseOperator';
import { SimpleOperator } from './BaseOperator';
export enum StringOperation {
contains = 'contains',
@ -9,9 +9,13 @@ export enum StringOperation {
trimEquals = 'trimEquals',
}
export class StringOperator extends Operator<StringOperation> {
export class StringOperator extends SimpleOperator<StringOperation> {
protected operationsEnum = StringOperation;
evaluate(left: string, right: string) {
switch (this.operation) {
if (typeof left !== 'string') return false;
switch (this.operationString) {
case StringOperation.contains:
return left.includes(right);
case StringOperation.startsWith:
@ -19,14 +23,14 @@ export class StringOperator extends Operator<StringOperation> {
case StringOperation.endsWith:
return left.endsWith(right);
case StringOperation.matchesRegex:
if (!right) this.em.logError('Regex pattern must be provided.');
return new RegExp(right).test(left);
if (!right) this.em.logWarning('Regex pattern must be provided.');
return new RegExp(right ?? '').test(left);
case StringOperation.equalsIgnoreCase:
return left.toLowerCase() === right.toLowerCase();
case StringOperation.trimEquals:
return left.trim() === right.trim();
default:
this.em.logError(`Unsupported string operation: ${this.operation}`);
this.em.logWarning(`Unsupported string operation: ${this.operationString}`);
return false;
}
}

6
packages/core/src/data_sources/model/conditional_variables/operators/types.ts

@ -1,6 +0,0 @@
import { AnyTypeOperation } from './AnyTypeOperator';
import { BooleanOperation } from './BooleanOperator';
import { NumberOperation } from './NumberOperator';
import { StringOperation } from './StringOperator';
export type DataConditionOperation = AnyTypeOperation | StringOperation | NumberOperation | BooleanOperation;

7
packages/core/src/data_sources/model/conditional_variables/types.ts

@ -0,0 +1,7 @@
import { AnyTypeOperation } from './operators/AnyTypeOperator';
import { BooleanOperation } from './operators/BooleanOperator';
import { NumberOperation } from './operators/NumberOperator';
import { StringOperation } from './operators/StringOperator';
export type DataConditionSimpleOperation = AnyTypeOperation | StringOperation | NumberOperation | BooleanOperation;
export type DataConditionCompositeOperation = DataConditionSimpleOperation;

101
packages/core/src/data_sources/model/data_collection/ComponentDataCollection.ts

@ -1,4 +1,4 @@
import { bindAll, isArray } from 'underscore';
import { isArray } from 'underscore';
import { ObjectAny } from '../../../common';
import Component, { keySymbol } from '../../../dom_components/model/Component';
import { ComponentAddType, ComponentDefinitionDefined, ComponentOptions } from '../../../dom_components/model/types';
@ -25,6 +25,7 @@ import {
import { getSymbolsToUpdate } from '../../../dom_components/model/SymbolUtils';
import { StyleProps, UpdateStyleOptions } from '../../../domain_abstract/model/StyleableModel';
import { updateFromWatcher } from '../../../dom_components/model/ComponentDataResolverWatchers';
import { ModelDestroyOptions } from 'backbone';
const AvoidStoreOptions = { avoidStore: true, partial: true };
export default class ComponentDataCollection extends Component {
@ -59,14 +60,17 @@ export default class ComponentDataCollection extends Component {
return cmp;
}
bindAll(this, 'rebuildChildrenFromCollection');
this.listenTo(this, `change:${keyCollectionDefinition}`, this.rebuildChildrenFromCollection);
this.rebuildChildrenFromCollection = this.rebuildChildrenFromCollection.bind(this);
this.listenToPropsChange();
this.rebuildChildrenFromCollection();
this.listenToDataSource();
return cmp;
}
getDataResolver() {
return this.get('dataResolver');
}
getItemsCount() {
const items = this.getDataSourceItems();
const startIndex = Math.max(0, this.getConfigStartIndex() ?? 0);
@ -97,6 +101,10 @@ export default class ComponentDataCollection extends Component {
return this.firstChild.components();
}
setDataResolver(props: DataCollectionProps) {
return this.set('dataResolver', props);
}
setCollectionId(collectionId: string) {
this.updateCollectionConfig({ collectionId });
}
@ -114,13 +122,6 @@ export default class ComponentDataCollection extends Component {
this.updateCollectionConfig({ endIndex });
}
private updateCollectionConfig(updates: Partial<DataCollectionProps>): void {
this.set(keyCollectionDefinition, {
...this.dataResolver,
...updates,
});
}
setDataSource(dataSource: DataCollectionDataSource) {
this.set(keyCollectionDefinition, {
...this.dataResolver,
@ -136,6 +137,13 @@ export default class ComponentDataCollection extends Component {
return this.components().at(0);
}
private updateCollectionConfig(updates: Partial<DataCollectionProps>): void {
this.set(keyCollectionDefinition, {
...this.dataResolver,
...updates,
});
}
private getDataSourceItems() {
return getDataSourceItems(this.dataResolver.dataSource, this.em);
}
@ -246,6 +254,19 @@ export default class ComponentDataCollection extends Component {
);
}
private listenToPropsChange() {
this.on(`change:${keyCollectionDefinition}`, () => {
this.rebuildChildrenFromCollection();
this.listenToDataSource();
});
this.listenToDataSource();
}
private removePropsListeners() {
this.off(`change:${keyCollectionDefinition}`);
this.dataSourceWatcher?.destroy();
}
static isComponent(el: HTMLElement) {
return toLowerCase(el.tagName) === DataCollectionType;
}
@ -259,6 +280,11 @@ export default class ComponentDataCollection extends Component {
const firstChild = this.firstChild as any;
return { ...json, components: [firstChild] };
}
destroy(options?: ModelDestroyOptions | undefined): false | JQueryXHR {
this.removePropsListeners();
return super.destroy(options);
}
}
function applyToComponentAndChildren(operation: (cmp: Component) => void, component: Component) {
@ -284,36 +310,57 @@ function setCollectionStateMapAndPropagate(
const listenerKey = `_hasAddListener${collectionId ? `_${collectionId}` : ''}`;
const cmps = cmp.components();
if (!cmp.collectionStateListeners.includes(listenerKey)) {
cmp.listenTo(cmps, 'add', addListener);
cmp.collectionStateListeners.push(listenerKey);
const removeListener = (component: Component) => {
const index = component.collectionStateListeners?.indexOf(listenerKey) ?? -1;
if (index !== -1) {
component.collectionStateListeners?.splice(index, 1);
}
const removeListener = (component: Component) => {
component.stopListening(component.components(), 'add', addListener);
if (!component.collectionStateListeners?.length) {
component.off(`change:${keyCollectionsStateMap}`, handleCollectionStateMapChange);
const index = component.collectionStateListeners.indexOf(listenerKey);
if (index !== -1) {
component.collectionStateListeners.splice(index, 1);
}
component.stopListening(component.components(), 'add', addListener);
component.stopListening(component.components(), 'remove', removeListener);
}
const collectionsStateMap = component.get(keyCollectionsStateMap);
const currentCollectionsStateMap = component.get(keyCollectionsStateMap);
if (currentCollectionsStateMap) {
component.set(keyCollectionsStateMap, {
...collectionsStateMap,
...currentCollectionsStateMap,
[collectionId]: undefined,
});
};
}
};
cmp.listenTo(cmps, 'remove', removeListener);
const removeAllListeners = (cmp: Component) => {
cmp.components().forEach((child) => removeAllListeners(child));
cmp.off(`change:${keyCollectionsStateMap}`);
cmp.stopListening(cmp.components(), 'add');
cmp.stopListening(cmp.components(), 'remove');
cmp.collectionStateListeners = [];
};
if (!cmp.collectionStateListeners) {
cmp.collectionStateListeners = [];
}
if (!cmp.collectionStateListeners.length) {
cmp.on(`change:${keyCollectionsStateMap}`, handleCollectionStateMapChange);
}
cmps.forEach((cmp) => setCollectionStateMapAndPropagate(cmp, collectionsStateMap, collectionId));
if (!cmp.collectionStateListeners.includes(listenerKey)) {
cmp.collectionStateListeners.push(listenerKey);
cmp.listenTo(cmps, 'add', addListener);
cmp.listenTo(cmps, 'remove', removeListener);
}
cmp.on(`change:${keyCollectionsStateMap}`, handleCollectionStateMapChange);
cmp.__onDestroy = () => removeAllListeners(cmp);
cmps.forEach((childCmp) => setCollectionStateMapAndPropagate(childCmp, collectionsStateMap, collectionId));
}, cmp);
}
function handleCollectionStateMapChange(this: Component) {
const updatedCollectionsStateMap = this.get(keyCollectionsStateMap);
this.components()
?.toArray()
.forEach((component: Component) => {
@ -357,7 +404,7 @@ function setCollectionStateMap(collectionsStateMap: DataCollectionStateMap) {
...cmp.get(keyCollectionsStateMap),
...collectionsStateMap,
};
cmp.set(keyCollectionsStateMap, updatedCollectionStateMap);
cmp.set(keyCollectionsStateMap, updatedCollectionStateMap, AvoidStoreOptions);
cmp.dataResolverWatchers.updateCollectionStateMap(updatedCollectionStateMap);
const parentCollectionsId = Object.keys(updatedCollectionStateMap);

8
packages/core/src/data_sources/utils.ts

@ -47,7 +47,7 @@ export function getDataResolverInstance(
break;
}
default:
options.em?.logError(`Unsupported dynamic type: ${type}`);
options.em?.logWarning(`Unsupported dynamic type: ${type}`);
return;
}
@ -83,3 +83,9 @@ export const ensureComponentInstance = (
export const isComponentDataOutputType = (type: string | undefined) => {
return !!type && [DataCollectionItemType, DataConditionIfTrueType, DataConditionIfFalseType].includes(type);
};
export function enumToArray(enumObj: any) {
return Object.keys(enumObj)
.filter((key) => isNaN(Number(key)))
.map((key) => enumObj[key]);
}

3
packages/core/src/dom_components/model/Component.ts

@ -450,6 +450,8 @@ export default class Component extends StyleableModel<ComponentProperties> {
updateSymbolComps(this, m, c, o);
}
__onDestroy() {}
/**
* Check component's type
* @param {string} type Component type
@ -1832,6 +1834,7 @@ export default class Component extends StyleableModel<ComponentProperties> {
destroy(options?: ModelDestroyOptions | undefined): false | JQueryXHR {
this.dataResolverWatchers.destroy();
this.__onDestroy();
return super.destroy(options);
}

Loading…
Cancel
Save