Skip to content
Snippets Groups Projects
text-group-element.component.ts 6.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • jojohoch's avatar
    jojohoch committed
    import {
    
      AfterViewInit, Component, OnDestroy, OnInit, ViewChild
    
    jojohoch's avatar
    jojohoch committed
    } from '@angular/core';
    import { first, takeUntil } from 'rxjs/operators';
    import { Subject } from 'rxjs';
    
    import { TextComponent } from 'common/components/text/text.component';
    
    import { TextElement } from 'common/models/elements/text/text';
    
    import { ValueChangeElement } from 'common/models/elements/element';
    
    import { AnchorService } from 'player/src/app/services/anchor.service';
    
    import { TextMarkingService } from '../../../services/text-marking.service';
    import { NativeEventService } from '../../../services/native-event.service';
    import { UnitStateService } from '../../../services/unit-state.service';
    import { ElementGroupDirective } from '../../../directives/element-group.directive';
    import { ElementModelElementCodeMappingService } from '../../../services/element-model-element-code-mapping.service';
    
    jojohoch's avatar
    jojohoch committed
    
    @Component({
    
      selector: 'aspect-text-group-element',
      templateUrl: './text-group-element.component.html',
      styleUrls: ['./text-group-element.component.scss']
    
    jojohoch's avatar
    jojohoch committed
    })
    
    export class TextGroupElementComponent extends ElementGroupDirective implements OnInit, AfterViewInit, OnDestroy {
    
      @ViewChild('elementComponent') elementComponent!: TextComponent;
    
    jojohoch's avatar
    jojohoch committed
      TextElement!: TextElement;
    
    
      initialValue: string = '';
      isMarkingBarOpen: boolean = false;
      selectedColor: string | null = null;
      selectedMode: 'mark' | 'delete' | null = null;
    
    jojohoch's avatar
    jojohoch committed
      markingBarPosition: { top: number, left: number } = { top: 0, left: 0 };
    
      textComponentContainerScrollTop: number = 0;
    
    jojohoch's avatar
    jojohoch committed
      textComponentRect!: DOMRect;
    
      private ngUnsubscribe = new Subject<void>();
    
    
      constructor(
        private nativeEventService: NativeEventService,
    
        private unitStateElementMapperService: ElementModelElementCodeMappingService,
    
        public unitStateService: UnitStateService,
        private anchorService: AnchorService
    
    jojohoch's avatar
    jojohoch committed
    
      ngOnInit(): void {
    
        this.initialValue = this.unitStateElementMapperService
    
          .mapToElementModelValue(
            this.unitStateService.getElementCodeById(this.elementModel.id)?.value, this.elementModel
    
          ) as string;
      }
    
      ngAfterViewInit(): void {
    
        this.registerAtUnitStateService(
          this.elementModel.id,
    
          ElementModelElementCodeMappingService.mapToElementCodeValue(this.initialValue, this.elementModel.type),
    
          this.elementComponent,
          this.pageIndex);
    
      changeElementCodeValue(value: ValueChangeElement): void {
        this.unitStateService.changeElementCodeValue({
          id: value.id,
          value: ElementModelElementCodeMappingService
            .mapToElementCodeValue(value.value, this.elementModel.type)
        });
      }
    
    
        selection: { active: boolean; mode: 'mark' | 'delete'; color: string; colorName: string | undefined }
    
    jojohoch's avatar
    jojohoch committed
      ): void {
        if (selection.active) {
          this.selectedColor = selection.color;
          this.selectedMode = selection.mode;
    
          this.applyMarkingDataToText(selection.mode, selection.color);
    
    jojohoch's avatar
    jojohoch committed
        } else {
          this.selectedColor = null;
          this.selectedMode = null;
        }
      }
    
    
      startTextSelection(pointerDown: PointerEvent): void {
    
    jojohoch's avatar
    jojohoch committed
        this.isMarkingBarOpen = false;
    
        this.anchorService.reset();
    
    jojohoch's avatar
    jojohoch committed
        this.nativeEventService.pointerUp
          .pipe(takeUntil(this.ngUnsubscribe), first())
    
          .subscribe((pointerUp: PointerEvent) => {
    
              {
                startSelectionX: pointerDown.clientX,
                startSelectionY: pointerDown.clientY,
                stopSelectionX: pointerUp.clientX,
                stopSelectionY: pointerUp.clientY
              },
              pointerUp.ctrlKey
    
    jojohoch's avatar
    jojohoch committed
            );
    
            pointerUp.preventDefault();
            pointerUp.stopPropagation();
    
      applyMarkingDataToText(mode: 'mark' | 'delete', color: string): void {
    
        TextMarkingService
    
    jojohoch's avatar
    jojohoch committed
            mode,
            color,
    
    jojohoch's avatar
    jojohoch committed
          );
        this.isMarkingBarOpen = false;
      }
    
    
      private stopTextSelection(
    
        textSelectionPosition: {
          stopSelectionX: number, stopSelectionY: number, startSelectionX: number, startSelectionY: number
        },
        isControlKeyPressed: boolean // do not allow multiple selections (FF)
    
    jojohoch's avatar
    jojohoch committed
      ) {
        const selection = window.getSelection();
    
        if (selection && TextMarkingService.isSelectionValid(selection) && selection.rangeCount > 0) {
    
            .isRangeInside(
              selection.getRangeAt(0), this.elementComponent.textContainerRef.nativeElement
            ) || (isControlKeyPressed)) {
    
    jojohoch's avatar
    jojohoch committed
            selection.removeAllRanges();
          } else if (this.selectedMode && this.selectedColor) {
    
            this.applyMarkingDataToText(this.selectedMode, this.selectedColor);
    
    jojohoch's avatar
    jojohoch committed
          } else if (!this.isMarkingBarOpen) {
    
            // hack for windows mobile to prevent opening marking bar while selection is removed on FF
            setTimeout(() => {
              const selectionTest = window.getSelection();
              if (selectionTest && TextMarkingService.isSelectionValid(selectionTest) && selectionTest.rangeCount > 0) {
                this.openMarkingBar(textSelectionPosition);
              }
            }, 100);
    
    jojohoch's avatar
    jojohoch committed
          }
        }
      }
    
      private openMarkingBar(
    
        textSelectionPosition: {
          stopSelectionX: number, stopSelectionY: number, startSelectionX: number, startSelectionY: number
        }
    
    jojohoch's avatar
    jojohoch committed
      ) {
    
        this.markingBarPosition.left =
          textSelectionPosition.startSelectionX > textSelectionPosition.stopSelectionX ?
            textSelectionPosition.startSelectionX : textSelectionPosition.stopSelectionX;
        this.markingBarPosition.top =
          textSelectionPosition.startSelectionY > textSelectionPosition.stopSelectionY ?
            textSelectionPosition.startSelectionY : textSelectionPosition.stopSelectionY;
    
    jojohoch's avatar
    jojohoch committed
        this.textComponentContainerScrollTop =
    
          this.elementComponent.domElement.closest('.fixed-size-content')?.scrollTop || 0;
        this.textComponentRect = this.elementComponent.domElement.getBoundingClientRect();
    
    jojohoch's avatar
    jojohoch committed
        this.isMarkingBarOpen = true;
        this.nativeEventService.pointerDown
          .pipe(takeUntil(this.ngUnsubscribe), first())
          .subscribe(() => this.closeMarkingBar());
      }
    
      private closeMarkingBar() {
        this.isMarkingBarOpen = false;
      }
    
      ngOnDestroy(): void {
        this.ngUnsubscribe.next();
        this.ngUnsubscribe.complete();
      }
    }