Skip to content
Snippets Groups Projects
section.component.ts 7.58 KiB
Newer Older
import {
  Component, Input, OnInit, QueryList, ViewChildren
} from '@angular/core';
import { SectionMenuComponent } from 'editor/src/app/components/unit-view/page/section-menu.component';
import { SelectionService } from 'editor/src/app/services/selection.service';
import { UnitService } from 'editor/src/app/services/unit-services/unit.service';
import { ElementService } from 'editor/src/app/services/unit-services/element.service';
import { SectionService } from 'editor/src/app/services/unit-services/section.service';
import { Section } from 'common/models/section';
import { PositionedUIElement, UIElement } from 'common/models/elements/element';
import { CanvasElementOverlay } from 'editor/src/app/components/unit-view/element-overlay/canvas-element-overlay';
import { SectionStaticComponent } from 'editor/src/app/components/unit-view/section/section-static.component';
import { SectionDynamicComponent } from 'editor/src/app/components/unit-view/section/section-dynamic.component';
import { CdkDragDrop, CdkDropList } from '@angular/cdk/drag-drop';
import { NgClass, NgIf } from '@angular/common';
import { SectionCounter } from 'common/util/section-counter';

@Component({
  selector: 'aspect-editor-section',
  standalone: true,
  imports: [
    NgIf, NgClass,
    CdkDropList,
    SectionMenuComponent, SectionStaticComponent, SectionDynamicComponent
  ],
  template: `
    <aspect-section-menu [class.hidden]="selectionService.selectedSectionIndex !== sectionIndex"
                         class="section-menu fx-column-start-stretch"
                         [style.left.px]="-45" [style.z-index]="1" [style.position]="'absolute'"
                         [section]="section" [sectionIndex]="sectionIndex"
                         [lastSectionIndex]="lastSectionIndex"
                         (selectElementComponent)="selectElementOverlay($event)">
    </aspect-section-menu>

    <div class="section-wrapper"
         [ngClass]="unitService.unit.sectionNumberingPosition === 'left' ? 'row-align' : 'column-align'">
      <div *ngIf="unitService.unit.enableSectionNumbering" class="numbering-box">
        <b *ngIf="sectionCounter">{{sectionCounter}}.</b>
      </div>
      <aspect-section-static *ngIf="!section.dynamicPositioning"
                             #sectionComponent
                             class="section" id="section-{{sectionIndex}}"
                             [section]="section"
                             [isSelected]="selectionService.selectedSectionIndex === sectionIndex"
                             (elementSelected)="selectionService.selectedSectionIndex = sectionIndex"
                             cdkDropList cdkDropListSortingDisabled
                             [cdkDropListData]="{ sectionIndex: sectionIndex }"
                             (cdkDropListDropped)="elementDropped($event)"
                             (click)="selectionService.selectedSectionIndex = sectionIndex">
      </aspect-section-static>
      <aspect-section-dynamic *ngIf="section.dynamicPositioning"
                              #sectionComponent
                              class="section"
                              [section]="section" [sectionIndex]="sectionIndex"
                              [isSelected]="selectionService.selectedSectionIndex === sectionIndex"
                              (elementSelected)="selectionService.selectedSectionIndex = sectionIndex"
                              (transferElement)="moveElementsBetweenSections(selectionService.getSelectedElements(),
                                                                 $event.previousSectionIndex,
                                                                 $event.newSectionIndex)"
                              (click)="selectionService.selectedSectionIndex = sectionIndex">
      </aspect-section-dynamic>
    </div>
  `,
  styles: `
    .hidden {
      display: none !important;
    }
    .section-wrapper {display: flex;}
    .section-wrapper.row-align {flex-direction: row;}
    .section-wrapper.column-align {flex-direction: column;}
    .section-wrapper .numbering-box {min-width: 35px; font-size: 20px; padding-top: 1px;}
    .section-wrapper .section {flex-grow: 1;}
  `
})
export class SectionComponent implements OnInit {
  @Input() section!: Section;
  @Input() sectionIndex!: number;
  @Input() lastSectionIndex!: number;
  @Input() alwaysVisiblePage: boolean = false;

  @ViewChildren('sectionComponent')
    sectionComponents!: QueryList<SectionStaticComponent | SectionDynamicComponent>;

  sectionCounter: number | undefined;

  constructor(public selectionService: SelectionService,
              public unitService: UnitService,
              public elementService: ElementService,
              public sectionService: SectionService) { }

  ngOnInit(): void {
    this.updateSectionCounter();
  }

  updateSectionCounter(): void {
    this.sectionCounter = undefined;
    if (this.unitService.unit.enableSectionNumbering &&
        !this.alwaysVisiblePage &&
        !this.section.ignoreNumbering) {
      this.sectionCounter = SectionCounter.getNext();
    }
  }

  selectElementOverlay(element: UIElement): void {
    const elementComponent = this.getElementOverlay(element);
    if (elementComponent) {
      this.selectionService.selectElement({ elementComponent: elementComponent, multiSelect: false });
    } else {
      throw Error('Element not found. This is a bug!');
    }
  }

  private getElementOverlay(element: UIElement): CanvasElementOverlay | null {
    for (const sectionComponent of this.sectionComponents.toArray()) {
      for (const elementComponent of sectionComponent.childElementComponents.toArray()) {
        if (elementComponent.element.id === element.id) {
          return elementComponent;
        }
      }
    }
    return null;
  }

  elementDropped(event: CdkDragDrop<{ sectionIndex: number; gridCoordinates?: number[]; }>): void {
    const selectedElements = this.selectionService.getSelectedElements() as PositionedUIElement[];

    if (event.previousContainer !== event.container) {
      this.moveElementsBetweenSections(selectedElements,
        event.previousContainer.data.sectionIndex,
        event.container.data.sectionIndex);
    } else {
      const page = this.unitService.getSelectedPage();
      selectedElements.forEach((element: PositionedUIElement) => {
        let newXPosition = element.position.xPosition + event.distance.x;
        if (newXPosition < 0) {
          newXPosition = 0;
        }
        if (page.hasMaxWidth && newXPosition > page.maxWidth - element.dimensions.width) {
          newXPosition = page.maxWidth - element.dimensions.width;
        }
        this.elementService.updateElementsPositionProperty([element], 'xPosition', newXPosition);

        let newYPosition = element.position.yPosition + event.distance.y;
        if (newYPosition < 0) {
          newYPosition = 0;
        }
        if (newYPosition > this.getPageHeight() - element.dimensions.height) {
          newYPosition = this.getPageHeight() - element.dimensions.height;
        }
        this.elementService.updateElementsPositionProperty([element], 'yPosition', newYPosition);
      });
    }
  }

  getPageHeight(): number { // TODO weg
    const page = this.unitService.getSelectedPage();
    const reduceFct = (accumulator: number, currentValue: Section) => accumulator + currentValue.height;
    return page.sections.reduce(reduceFct, 0);
  }

  moveElementsBetweenSections(elements: UIElement[], previousSectionIndex: number, newSectionIndex: number): void {
    const page = this.unitService.getSelectedPage();
    this.sectionService.transferElements(elements,
      page.sections[previousSectionIndex],
      page.sections[newSectionIndex]);
  }
}