All files / app/components/page-cv/cv-skill-bar cv-skill-bar.component.ts

100% Statements 25/25
100% Branches 5/5
100% Functions 7/7
100% Lines 24/24

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                                                1x         186x         186x         186x   186x   186x                 186x 186x               205x             206x   204x                         204x 204x 204x 204x 204x         204x                           2x               209x 85x   124x                 208x 208x                       2x      
import { CommonModule } from '@angular/common';
import {
  Component,
  ElementRef,
  HostListener,
  Input,
  OnChanges,
} from '@angular/core';
import { of } from 'rxjs';
import { PreloaderService } from 'src/app/services/preloader/preloader.service';
import { scriptVar } from 'src/scripts/template/tools/setUp';
import { debounce } from 'src/scripts/tools/debounce/debounce';
 
/**
 * Component used to display a skill with a animated bar representing a
 * percentage.
 */
@Component({
  selector: 'app-cv-skill-bar',
  templateUrl: './cv-skill-bar.component.html',
  styleUrls: ['./cv-skill-bar.component.css'],
  standalone: true,
  imports: [CommonModule],
})
export class CvSkillBarComponent implements OnChanges {
  /**
   * An input containinf the name of the skill to be displayed as an
   * `Observable<string>`.
   */
  @Input() skillName = of('SKILL');
  /**
   * The percentage, to be displayed both as a number but also as the amount the
   * bar will fill up.
   */
  @Input() percent = 50;
  /**
   * Width of the bar. Initially 0, and gets un to {@link percent}% when the bar
   * is in view, and goes back to 0 when it gets out of view.
   */
  width = '0';
  /** Minimal scroll value to have the bar in view. */
  posElementMin = 0;
  /** Maximal scroll value to have the bar in view. */
  posElementMax = 0;
 
  /**
   * Skill bar component constructor.
   *
   * @param preloaderService The {@link PreloaderService}
   * @param element The `ElementRef`
   */
  constructor(
    private preloaderService: PreloaderService,
    private element: ElementRef
  ) {}
 
  /**
   * If the input changes, the position has to be computed again and the width
   * has to be updated again.
   */
  ngOnChanges() {
    this.updateAnimation();
  }
  /**
   * Computes the position precise position of the element in the page so that
   * the animation triggers exactly as the bar enters or leaves the viewport.
   */
  getElPos() {
    if (!this.element.nativeElement) return;
    const posViewPort =
      this.element.nativeElement.getBoundingClientRect().y +
      this.element.nativeElement.firstElementChild.firstElementChild.getBoundingClientRect()
        .height +
      parseInt(
        getComputedStyle(
          this.element.nativeElement.firstElementChild.firstElementChild
        ).marginTop
      ) +
      parseInt(
        getComputedStyle(
          this.element.nativeElement.firstElementChild.firstElementChild
        ).marginBottom
      );
    const viewPortOffset = scrollY;
    const viewPortHeight = window.innerHeight;
    this.posElementMax = posViewPort + viewPortOffset;
    this.posElementMin = this.posElementMax - viewPortHeight;
    this.posElementMax -= parseInt(
      getComputedStyle(document.documentElement)
        .getPropertyValue(scriptVar.cssHeaderHeight)
        .split('px')[0]
    );
    this.posElementMax +=
      this.element.nativeElement.firstElementChild.firstElementChild.nextElementSibling.getBoundingClientRect().height;
  }
 
  /**
   * Update the trigger when the window is resized. Indeed, the bar position
   * will change since it is tied to viewport height. Uses the {@link debounce}
   * annotation to avoid firing this too much : resize events fire a lot during
   * resizing. Calls {@link updateAnimation} which does the actual update once
   * everything is loaded.
   */
  @HostListener('window:resize', ['$event'])
  @debounce()
  onResize() {
    this.updateAnimation();
  }
 
  /**
   * Checks if the width has to be changed (and thus, the animation started) and
   * do the change if it is the case.
   */
  updateWidth() {
    if (scrollY < this.posElementMin || scrollY > this.posElementMax) {
      this.width = '0';
    } else {
      this.width = this.percent + '%';
    }
  }
 
  /**
   * Does the actual update of the scroll trigger amount and the possible width
   * modification (to create the animation).
   */
  updateAnimation() {
    this.getElPos();
    this.updateWidth();
  }
 
  /**
   * Update the trigger when client scrolls. Uses the {@link debounce} annotation
   * to avoid firing this too much : resize events fire a lot during resizing.
   * Calls {@link updateAnimation} which does the actual update once everything
   * is loaded.
   */
  @HostListener('window:scroll', ['$event'])
  @debounce()
  onScroll() {
    this.updateAnimation();
  }
}