All files / app/components/common/back-to-top back-to-top.component.ts

100% Statements 32/32
100% Branches 6/6
100% Functions 9/9
100% Lines 32/32

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161                                                1x                     77x                         77x         77x         77x   77x   77x                   77x 77x   77x 77x                       30x 21x 21x                   80x                   18x 12x 12x 12x     18x 6x 6x 6x                       3x 3x                 12x 6x 6x   12x 6x 6x                   32x      
import { CommonModule } from '@angular/common';
import {
  Component,
  ElementRef,
  HostListener,
  OnDestroy,
  ViewChild,
} from '@angular/core';
import { of } from 'rxjs';
import { ImgLoadDirective } from 'src/app/directives/imgLoad/img-load.directive';
import { ComponentWithText } from 'src/app/interfaces/ComponentWithText';
import { TextService } from 'src/app/services/db/text/text.service';
import { Preloaders } from 'src/app/services/preloader/preloaders/preloaders';
import { VisibleToLoadTextService } from 'src/app/services/visibletoloadtext/visible-to-load-text.service';
import { scriptVar } from 'src/scripts/template/tools/setUp';
 
/** Component for the back to top icon. */
@Component({
  selector: 'app-back-to-top',
  templateUrl: './back-to-top.component.html',
  styleUrls: ['./back-to-top.component.css'],
  standalone: true,
  imports: [ImgLoadDirective, CommonModule],
})
export class BackToTopComponent implements ComponentWithText, OnDestroy {
  /** The main a element of the component. */
  @ViewChild('mainA') mainA!: ElementRef<HTMLElement>;
  /**
   * Scroll trigger value for the icon to appear.
   *
   * Defined to be the height of the icon, plus twice the spacing between the
   * icon and the bottom of the screen. That way, since the banner is taking the
   * entire viewport height, when the icon (dis)appears, it is neatly evenly
   * seperated from the bottom of the screen and the banner.
   */
  trigger =
    parseInt(
      getComputedStyle(document.documentElement)
        .getPropertyValue('--back-to-top-size')
        .split('px')[0]
    ) +
    2 *
      parseInt(
        getComputedStyle(document.documentElement)
          .getPropertyValue('--back-to-top-bottom')
          .split('px')[0]
      );
  /** Icon opacity, to make it disappear when it should. */
  iconOpacity = '0';
  /**
   * The pointer events css property, to make it unclickable and not to change
   * the cursor pointer in any way when the icon disappears.
   */
  iconPointerEvent = 'none';
  /**
   * The state of the icon, based on the scroll amount compared with the
   * trigger.
   */
  backToTopState = scriptVar.backToTopInvisibleState;
  /** Alt text of the image. */
  altTxt = of('');
  /** {@link Preloaders} used for the icon image. */
  preloaders = [Preloaders.MAIN];
 
  /**
   * Component constructor.
   *
   * @param languageService The {@link LanguageService}
   * @param textService The {@link TextService}
   * @param visibleToLoadTextService The {@link VisibleToLoadTextService}
   */
  constructor(
    private textService: TextService,
    public visibleToLoadTextService: VisibleToLoadTextService
  ) {
    setTimeout(() => {
      this.visibleToLoadTextService.subscribe(this);
    }, 0);
  }
 
  /**
   * Update the component's texts when the language is updated. See
   * {@link VisibleToLoadTextService}. The subscriber design pattern is used and
   * this function is used when the service notifies its subscribers to update
   * the text contents after a language change. Uses {@link TextService} to get
   * those contents from the database.
   */
  updateTexts(): void {
    this.textService.get('back-to-top-alt').subscribe((v) => {
      this.altTxt = of(v);
      this.visibleToLoadTextService.textLoaded(this);
    });
  }
 
  /**
   * On destroy, the component has to be unsubscribed rom the
   * {@link VisibleToLoadTextService} to avoid having the service try to notify a
   * destroyed subscriber.
   */
  ngOnDestroy(): void {
    this.visibleToLoadTextService.unsubscribe(this);
  }
 
  /**
   * On scroll event. This checks whether or not the state has to be changed. If
   * it has to be, it changed the state and calls the method
   * {@link updateBackToTop} responsible for actually updating the component.
   */
  @HostListener('window:scroll', ['$event'])
  onScroll(event: Event) {
    if ((event.currentTarget as Window).scrollY > this.trigger) {
      if (this.backToTopState == scriptVar.backToTopInvisibleState) {
        this.backToTopState = scriptVar.backToTopVisibleState;
        this.updateBackToTop();
      }
    }
    if ((event.currentTarget as Window).scrollY <= this.trigger) {
      if (this.backToTopState == scriptVar.backToTopVisibleState) {
        this.backToTopState = scriptVar.backToTopInvisibleState;
        this.updateBackToTop();
      }
    }
  }
 
  /**
   * On click event. Scrolls back to top smoothly.
   *
   * @param event The event, specified mainly to prevent the default behaviour
   *   of the link
   */
  onClick(event: Event) {
    document.querySelector('html')?.scrollTo({ top: 0, behavior: 'smooth' });
    event.preventDefault();
  }
 
  /**
   * Actual update of the style of the elements according to the state set in
   * backToTopState. It changes the opacity of the component and its
   * clickability.
   */
  updateBackToTop() {
    if (this.backToTopState === scriptVar.backToTopInvisibleState) {
      this.iconOpacity = '0';
      this.iconPointerEvent = 'none';
    }
    if (this.backToTopState === scriptVar.backToTopVisibleState) {
      this.iconOpacity = '1';
      this.iconPointerEvent = 'all';
    }
  }
 
  /**
   * Get the main component element.
   *
   * @returns The element.
   */
  getElement(): ElementRef<HTMLElement> {
    return this.mainA;
  }
}