import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core'; import { SwitchState, TypeToken } from '@yourcause/common'; import { GuidService } from '@yourcause/common/utils'; import { LogicBuilderService } from '../logic-builder.service'; import { LogicColumn, LogicColumnDisplay, LogicCondition, LogicGroupType } from '../logic-builder.typing'; @Component({ selector: 'gc-conditional-logic-builder-group', templateUrl: './conditional-logic-builder-group.component.html', styleUrls: ['./conditional-logic-builder-group.component.scss'] }) export class ConditionalLogicBuilderGroupComponent implements OnInit, OnChanges { @Input() group: LogicGroupType; @Input() availableColumns: LogicColumnDisplay[]; @Input() allowNoConditions = true; @Input() currentDepth: number; @Input() maxDepth: number; @Output() groupChange = new EventEmitter>(); @Output() validChange = new EventEmitter(); @Output() onRemove = new EventEmitter(); SwitchState = SwitchState; validMap: Record = {}; afterInit = false; private _useAnd: SwitchState = SwitchState.Untoggled; $logicConditionType = new TypeToken>>(); $logicGroupType = new TypeToken>(); constructor ( private logicBuilderService: LogicBuilderService, private guidService: GuidService ) { } get useAnd (): SwitchState { return this._useAnd; } set useAnd (value: SwitchState) { this._useAnd = value; this.handleSwitchStateChange(); } get hasConditions () { return this.group?.conditions?.length > 0; } ngOnInit () { let useAnd = this.group.conditions.every(condition => condition.useAnd); if (this.group.conditions.length === 0) { useAnd = false; } this.useAnd = useAnd ? SwitchState.Toggled : SwitchState.Untoggled; this.group.conditions.forEach((_, index) => { this.validMap[index] = true; }); if (!this.group.identifier) { this.group.identifier = this.guidService.nonce(); this.groupChange.emit(this.group); } this.emitValidity(); this.afterInit = true; } ngOnChanges (changes: SimpleChanges) { if (changes.group && this.afterInit) { this.emitValidity(); } } trackBy (_: number, row: LogicGroupType|LogicCondition>) { return row.identifier; } emitValidity () { const allValid = Object.keys(this.validMap).every((key) => { return this.validMap[key]; }); const passes = allValid && (this.allowNoConditions ? true : this.hasConditions); this.validChange.emit(passes); } handleSwitchStateChange () { const useAnd = this._useAnd === SwitchState.Toggled; let changed = false; this.group = { ...this.group, conditions: this.group.conditions.map((condition) => { if (condition.useAnd !== useAnd) { changed = true; return { ...condition, useAnd }; } return condition; }) }; if (changed) { this.groupChange.emit(this.group); } } handleConditionOrGroupValidChange ( valid: boolean, index: number ) { this.validMap = { ...this.validMap, [index]: valid }; this.emitValidity(); } handleConditionOrGroupChange ( conditionOrGroup: LogicCondition>|LogicGroupType, index: number ) { this.group = { ...this.group, conditions: [ ...this.group.conditions.slice(0, index), conditionOrGroup, ...this.group.conditions.slice(index + 1) ] }; this.groupChange.emit(this.group); if ('conditions' in conditionOrGroup && conditionOrGroup.conditions.length === 0) { this.handleRemoveCondition(index); } } handleRemoveCondition (index: number) { this.group = { ...this.group, conditions: [ ...this.group.conditions.slice(0, index), ...this.group.conditions.slice(index + 1) ] }; delete this.validMap[index]; this.groupChange.emit(this.group); this.emitValidity(); } addCondition () { const useAnd = this._useAnd === SwitchState.Toggled; const newCondition = this.logicBuilderService.getDefaultCondition(); newCondition.useAnd = useAnd; this.group = { ...this.group, conditions: [ ...this.group.conditions, newCondition ] }; this.groupChange.emit(this.group); } addGroup () { const useAnd = this.useAnd === SwitchState.Toggled; const newGroup: LogicGroupType = { conditions: [ this.logicBuilderService.getDefaultCondition() ], useAnd, identifier: this.guidService.nonce() }; this.group = { ...this.group, conditions: [ ...this.group.conditions, newGroup ] }; this.groupChange.emit(this.group); } }