Skip to content
Snippets Groups Projects
section-visibility-handling.directive.ts 6.85 KiB
Newer Older
  • Learn to ignore specific revisions
  •   Directive, ElementRef, Input, OnDestroy, OnInit
    
    } from '@angular/core';
    import { Subject } from 'rxjs';
    
    import { Section } from 'common/models/section';
    
    import { takeUntil } from 'rxjs/operators';
    
    import { UnitStateService } from 'player/src/app/services/unit-state.service';
    
    import { StorableTimer } from 'player/src/app/classes/storable-timer';
    
    import { ValueChangeElement } from 'common/models/elements/element';
    
    import { ElementCode } from 'player/modules/verona/models/verona';
    import { Storable } from 'player/src/app/classes/storable';
    
    @Directive({
      selector: '[aspectSectionVisibilityHandling]'
    
    export class SectionVisibilityHandlingDirective implements OnInit, OnDestroy {
    
      @Input() section!: Section;
    
      @Input() pageSections!: Section[];
    
      @Input() pageIndex!: number;
      @Input() sectionIndex!: number;
    
    
      private ngUnsubscribe = new Subject<void>();
    
      private rulesAreFulfilled: boolean = false;
    
      constructor(
        private elementRef: ElementRef,
        private unitStateService: UnitStateService
      ) {}
    
        if (this.section.visibilityRules.length) {
    
          this.addSubscription();
        }
      }
    
      private addSubscription(): void {
    
        if (this.section.enableReHide ||
          (this.section.visibilityDelay && !this.isTimerStateFullFilled) ||
          (this.section.animatedVisibility && !this.isAnimatedVisibilityFullFilled)
        ) {
    
          this.unitStateService.elementCodeChanged
            .pipe(takeUntil(this.ngUnsubscribe))
            .subscribe(code => {
    
              if (this.isRuleCode(code)) {
                this.displayHiddenSection();
              }
    
      private get isTimerStateFullFilled(): boolean {
        return this.unitStateService
          .getElementCodeById(this.timerStateVariableId)?.value as number >= this.section.visibilityDelay;
      }
    
      private get isAnimatedVisibilityFullFilled(): boolean {
        return this.unitStateService
          .getElementCodeById(this.animationVariableId)?.value === 1;
      }
    
    
      private isRuleCode(code: ElementCode): boolean {
        return this.section.visibilityRules
          .map(rule => rule.id)
          .some(id => id === code.id) ||
    
          (!!this.section.visibilityDelay && code.id === this.timerStateVariableId) ||
          (this.section.animatedVisibility && code.id === this.animationVariableId);
    
      private get timerStateVariableId(): string {
    
        const firstRule = this.section.visibilityRules[0];
        return `${firstRule.id}-${firstRule.value}-${this.pageIndex}-${this.sectionIndex}-${this.section.visibilityDelay}`;
      }
    
      private get animationVariableId(): string {
        const firstRule = this.section.visibilityRules[0];
        return `${firstRule.id}-${firstRule.value}-${this.pageIndex}-${this.sectionIndex}-scroll-animation`;
    
      }
    
      private initTimerStateVariable(): void {
    
        const timerStateVariable = new StorableTimer(
    
          this.timerStateVariableId, this.section.visibilityDelay
        );
        this.unitStateService.registerElement(timerStateVariable.id, timerStateVariable.value);
        timerStateVariable.timerStateValueChanged
          .subscribe((value: ValueChangeElement) => {
            this.unitStateService.changeElementCodeValue(value);
          });
        timerStateVariable.run();
    
      private displayHiddenSection(): void {
    
        if (this.areVisibilityRulesFulfilled() || this.rulesAreFulfilled) {
    
          if (this.section.visibilityDelay) {
    
            if (!this.unitStateService.getElementCodeById(this.timerStateVariableId)) {
    
            this.setVisibility(this.isTimerStateFullFilled);
    
          this.setVisibility(false);
        }
      }
    
      private setVisibility(visible: boolean): void {
        this.elementRef.nativeElement.style.display = visible ? null : 'none';
    
          if (this.section.animatedVisibility && !this.isAnimatedVisibilityFullFilled) {
            const animationVariable = new Storable(this.animationVariableId, 1);
            this.unitStateService.registerElement(animationVariable.id, 1);
    
            this.scrollIntoView();
          }
          if (!this.section.enableReHide) {
            this.ngUnsubscribe.next();
            this.ngUnsubscribe.complete();
          }
    
      private scrollIntoView(): void {
        this.elementRef.nativeElement.style.scrollMarginTop = 100;
        setTimeout(() => {
          this.elementRef.nativeElement.scrollIntoView({ behavior: 'smooth', block: 'start' });
        });
      }
    
    
      private areVisibilityRulesFulfilled(): boolean {
    
        return this.section.visibilityRules.some(rule => {
          if (this.unitStateService.getElementCodeById(rule.id)) {
            switch (rule.operator) {
              case '=':
    
                return this.unitStateService.getElementCodeById(rule.id)?.value?.toString() === rule.value;
    
                return this.unitStateService.getElementCodeById(rule.id)?.value?.toString() !== rule.value;
    
              case '>':
                return Number(this.unitStateService.getElementCodeById(rule.id)?.value) > Number(rule.value);
              case '<':
                return Number(this.unitStateService.getElementCodeById(rule.id)?.value) < Number(rule.value);
    
                return Number(this.unitStateService.getElementCodeById(rule.id)?.value) >= Number(rule.value);
    
                return Number(this.unitStateService.getElementCodeById(rule.id)?.value) <= Number(rule.value);
    
              case 'contains':
                return this.unitStateService.getElementCodeById(rule.id)?.value?.toString().includes(rule.value);
              case 'pattern':
                // We use a similar implementation to Angular's PatternValidator
                if (this.unitStateService.getElementCodeById(rule.id)?.value !== null) {
                  let regexStr = (rule.value.charAt(0) !== '^') ? `^${rule.value}` : rule.value;
                  if (rule.value.charAt(rule.value.length - 1) !== '$') regexStr += '$';
                  const regex = new RegExp(regexStr);
                  return regex.test(this.unitStateService.getElementCodeById(rule.id)?.value?.toString() as string);
                }
                return false;
              case 'minLength':
                return (this.unitStateService.getElementCodeById(rule.id)?.value as string)
                  .toString()?.length >= Number(rule.value);
              case 'maxLength':
                return (this.unitStateService.getElementCodeById(rule.id)?.value as string)
                  .toString()?.length <= Number(rule.value);
    
      // private hasSeenElements(): boolean {
      //   return this.section.getAllElements()
      //     .map(element => this.unitStateService.getElementCodeById(element.id))
      //     .some(code => (code ?
      //       ElementCodeStatusValue[code.status] > ElementCodeStatusValue.NOT_REACHED :
      //       false));
      // }
    
      ngOnDestroy(): void {
        this.ngUnsubscribe.next();
        this.ngUnsubscribe.complete();
      }
    }