Skip to content
Snippets Groups Projects
section-menu.component.ts 13.5 KiB
Newer Older
  Component, OnInit, OnDestroy, Input, Output, EventEmitter,
  ViewChild, ElementRef
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { Clipboard } from '@angular/cdk/clipboard';
import { UnitService } from '../../services/unit.service';
import { DialogService } from '../../services/dialog.service';
import { SelectionService } from '../../services/selection.service';
import { MessageService } from 'common/services/message.service';
import { UIElement } from 'common/models/elements/element';
import { Section } from 'common/models/section';
  selector: 'aspect-section-menu',
    <button mat-mini-fab [matMenuTriggerFor]="elementListMenu">
      <mat-icon>list</mat-icon>
    </button>
    <mat-menu #elementListMenu="matMenu" class="layoutMenu" xPosition="before">
      <mat-action-list>
        <ng-container *ngIf="section.elements.length === 0">
            Keine Elemente im Abschnitt
        </ng-container>
        <mat-list-item *ngFor="let element of section.elements"
                       (click)="selectElement(element)">
          {{element.id}}
        </mat-list-item>
      </mat-action-list>
    </mat-menu>
    <button mat-mini-fab
            (click)="openColorPicker()">
      <mat-icon>palette</mat-icon>
    </button>
    <input #colorPicker type="color" [style.display]="'none'"
           [value]="$any(section.backgroundColor)"
           (change)="updateModel('backgroundColor', $any($event.target).value)">

    <button mat-mini-fab [matMenuTriggerFor]="activeAfterIDMenu">
      <mat-icon>disabled_visible</mat-icon>
    </button>
    <mat-menu #activeAfterIDMenu="matMenu" xPosition="before">
      <mat-form-field appearance="fill">
        <mat-label>{{'section-menu.activeAfterID' | translate }}</mat-label>
        <input matInput
               [value]="$any(section.activeAfterID)"
               (click)="$any($event).stopPropagation()"
               (change)="updateModel('activeAfterID', $any($event.target).value)">
      </mat-form-field>
    </mat-menu>

    <button mat-mini-fab [matMenuTriggerFor]="layoutMenu">
      <mat-icon>space_dashboard</mat-icon>
    </button>
    <mat-menu #layoutMenu="matMenu" class="layoutMenu" xPosition="before">
      <div (click)="$event.stopPropagation()">
        <mat-checkbox class="menuItem" [checked]="section.dynamicPositioning"
                      (click)="$any($event).stopPropagation()"
                      (change)="updateModel('dynamicPositioning', $event.checked)">
          {{'section-menu.dynamic-positioning' | translate }}
        </mat-checkbox>
        <ng-container *ngIf="!section.dynamicPositioning">
          <mat-form-field appearance="fill">
            <mat-label>{{'section-menu.height' | translate }}</mat-label>
            <input matInput  type="number"
                   [value]="$any(section.height)"
rhenck's avatar
rhenck committed
                   (click)="$any($event).stopPropagation()"
                   (change)="updateModel('height', $any($event.target).value)">
          </mat-form-field>
        </ng-container>
        <div *ngIf="section.dynamicPositioning">
          <fieldset>
            <legend>{{'section-menu.columns' | translate }}</legend>

            <div class="size-group">
              <mat-checkbox class="menuItem" [checked]="section.autoColumnSize"
                            (click)="$any($event).stopPropagation()"
                            (change)="updateModel('autoColumnSize', $event.checked)">
                {{'section-menu.autoColumnSize' | translate }}
              </mat-checkbox>
              <ng-container *ngIf="!section.autoColumnSize">
                <mat-form-field>
                  <mat-label>{{'section-menu.columnCount' | translate }}</mat-label>
                  <input matInput type="number"
                         [value]="$any(section.gridColumnSizes.split(' ').length)"
                         (click)="$any($event).stopPropagation()"
                         (change)="modifySizeArray('gridColumnSizes', $any($event).target.value)">
                </mat-form-field>
                <div *ngFor="let size of columnSizes ; let i = index" class="size-inputs" fxLayout="row">
                  <mat-form-field>
                    <mat-label>{{'section-menu.width' | translate }} {{i + 1}}</mat-label>
                    <input matInput type="number"
                           [value]="size.value"
                           (click)="$any($event).stopPropagation()"
                           (change)="changeGridSize('gridColumnSizes', i, false, $any($event).target.value)">
                  </mat-form-field>
                  <mat-select [value]="size.unit"
                              (click)="$any($event).stopPropagation()"
                              (selectionChange)="changeGridSize('gridColumnSizes', i, true, $event.value)">
                    <mat-option value="fr">{{'section-menu.fraction' | translate }}</mat-option>
                    <mat-option value="px">{{'section-menu.pixel' | translate }}</mat-option>
                  </mat-select>
                </div>
              </ng-container>
            </div>
          </fieldset>

          <fieldset>
            <legend>{{'section-menu.rows' | translate }}</legend>
            <div class="size-group">
              <mat-checkbox class="menuItem" [checked]="section.autoRowSize"
                            (click)="$any($event).stopPropagation()"
                            (change)="updateModel('autoRowSize', $event.checked)">
                {{'section-menu.autoRowSize' | translate }}
              </mat-checkbox>
              <ng-container *ngIf="!section.autoRowSize">

                <mat-form-field appearance="fill">
                  <mat-label>{{'section-menu.height' | translate }}</mat-label>
                  <input matInput mat-menu-item type="number"
                         [value]="$any(section.height)"
                         (click)="$any($event).stopPropagation()"
                         (change)="updateModel('height', $any($event.target).value)">
                </mat-form-field>

                  <mat-label>{{'section-menu.rowCount' | translate }}</mat-label>
                  <input matInput type="number"
                         [value]="$any(section.gridRowSizes.split(' ').length)"
                         (click)="$any($event).stopPropagation()"
                         (change)="modifySizeArray('gridRowSizes', $any($event).target.value)">
                </mat-form-field>


                <fieldset>
                  <legend>Zeilenhöhen</legend>
                  <div *ngFor="let size of rowSizes ; let i = index" class="size-inputs" fxLayout="row">
                    <mat-form-field>
                      <mat-label>{{'section-menu.height' | translate }} {{i + 1}}</mat-label>
                      <input matInput type="number"
                             [value]="size.value"
                             (click)="$any($event).stopPropagation()"
                             (change)="changeGridSize('gridRowSizes', i, false, $any($event).target.value)">
                    </mat-form-field>
                    <mat-select [value]="size.unit"
                                (click)="$any($event).stopPropagation()"
                                (selectionChange)="changeGridSize('gridRowSizes', i, true, $event.value)">
                      <mat-option value="fr">{{'section-menu.fraction' | translate }}</mat-option>
                      <mat-option value="px">{{'section-menu.pixel' | translate }}</mat-option>
                    </mat-select>
                  </div>
                </fieldset>
              </ng-container>
            </div>
          </fieldset>
      </div>
    </mat-menu>

    <button mat-mini-fab
            (click)="copySectionToClipboard()">
      <mat-icon>content_copy</mat-icon>
    </button>

    <button mat-mini-fab [matMenuTriggerFor]="pasteSectionMenu">
      <mat-icon>content_paste</mat-icon>
    </button>
    <mat-menu #pasteSectionMenu="matMenu" xPosition="before">
      <mat-form-field appearance="fill" (click)="$any($event).stopPropagation()">
<!--        <mat-label>{{'section-menu.activeAfterID' | translate }}</mat-label>-->
        <input matInput (click)="$any($event).stopPropagation()"
               (paste)="pasteSectionFromClipboard($event);">
      </mat-form-field>
    </mat-menu>

    <button *ngIf="allowMoveUp" mat-mini-fab
            (click)="this.moveSection.emit('up')">
      <mat-icon>north</mat-icon>
    </button>
    <button *ngIf="allowMoveDown" mat-mini-fab
            (click)="this.moveSection.emit('down')">
      <mat-icon>south</mat-icon>
    </button>
    <button mat-mini-fab
            (click)="duplicateSection.emit()">
      <mat-icon>control_point_duplicate</mat-icon>
    </button>
    <button *ngIf="allowDelete" mat-mini-fab
            (click)="deleteSection()">
      <mat-icon>clear</mat-icon>
    '::ng-deep .layoutMenu .size-inputs .mat-form-field-infix {width: 65px}',
    '.size-group {background-color: #f5f5f5; margin: 0 0 15px 0}',
    '::ng-deep .layoutMenu .size-group mat-select {padding-top: 24px; padding-left: 15px;}',
    '::ng-deep .layoutMenu {padding: 0 15px}'
export class SectionMenuComponent implements OnInit, OnDestroy {
  @Input() section!: Section;
  @Input() sectionIndex!: number;
  @Input() allowMoveUp!: boolean;
  @Input() allowMoveDown!: boolean;
  @Input() allowDelete!: boolean;
  @Output() moveSection = new EventEmitter<'up' | 'down'>();
  @Output() duplicateSection = new EventEmitter();
  @Output() selectElementComponent = new EventEmitter<UIElement>();

  @ViewChild('colorPicker') colorPicker!: ElementRef;
  columnSizes: { value: string, unit: string }[] = [];
  rowSizes: { value: string, unit: string }[] = [];
  private ngUnsubscribe = new Subject<void>();
  constructor(public unitService: UnitService,
              private selectionService: SelectionService,
              private dialogService: DialogService,
              private messageService: MessageService,
              private clipboard: Clipboard) { }
    this.updateGridSizes();
  }

  updateModel(property: string, value: string | number | boolean): void {
    this.unitService.updateSectionProperty(this.section, property, value);
  }

  selectElement(element: UIElement): void {
    this.selectElementComponent.emit(element);
  }

  deleteSection(): void {
    this.dialogService.showConfirmDialog('Abschnitt löschen?')
      .pipe(takeUntil(this.ngUnsubscribe))
      .subscribe((result: boolean) => {
        if (result) {
          this.unitService.deleteSection(this.section);
          if (this.sectionIndex === this.selectionService.selectedPageSectionIndex &&
            this.selectionService.selectedPageSectionIndex > 0) {
            this.selectionService.selectedPageSectionIndex -= 1;
          }
  /* Initialize internal array of grid sizes. Where value and unit are put, split up, in an array. */
  private updateGridSizes(): void {
    this.columnSizes = [];
    this.section.gridColumnSizes.split(' ').forEach((size: string) => {
      this.columnSizes.push({ value: size.slice(0, -2), unit: size.slice(-2) });
    });
    this.rowSizes = [];
    this.section.gridRowSizes.split(' ').forEach((size: string) => {
      this.rowSizes.push({ value: size.slice(0, -2), unit: size.slice(-2) });
    });
  }

  /* Add elements to size array. Default value 1fr. */
  modifySizeArray(property: 'gridColumnSizes' | 'gridRowSizes', newLength: number): void {
    const oldSizesAsArray = property === 'gridColumnSizes' ?
      this.section.gridColumnSizes.split(' ') :
      this.section.gridRowSizes.split(' ');

    let newArray = [];
    if (newLength < oldSizesAsArray.length) {
      newArray = oldSizesAsArray.slice(0, newLength);
    } else {
      newArray.push(
        ...oldSizesAsArray,
        ...Array(newLength - oldSizesAsArray.length).fill('1fr')
      );
    }
    this.updateModel(property, newArray.join(' '));
    this.updateGridSizes();
  /* Replace number or unit is size string. '2fr 1fr' -> '2px 3fr' */
  changeGridSize(property: string, index: number, unit: boolean = false, newValue: string): void {
    const oldSizesAsArray = property === 'gridColumnSizes' ?
      this.section.gridColumnSizes.split(' ') :
      this.section.gridRowSizes.split(' ');

    if (unit) {
      oldSizesAsArray[index] = oldSizesAsArray[index].slice(0, -2) + newValue;
    } else {
      oldSizesAsArray[index] = newValue + oldSizesAsArray[index].slice(-2);
    }

    this.updateModel(property, oldSizesAsArray.join(' '));
    this.updateGridSizes();
  }
rhenck's avatar
rhenck committed
  openColorPicker(): void {
    this.colorPicker.nativeElement.click();
  }

  ngOnDestroy(): void {
    this.ngUnsubscribe.next();
    this.ngUnsubscribe.complete();
  }

  copySectionToClipboard() {
    this.clipboard.copy(JSON.stringify(this.section));
    console.log('bla', navigator.clipboard);
  }

  pasteSectionFromClipboard(event: ClipboardEvent) {
    console.log('paste', event);
    console.log('paste2', event.clipboardData?.getData('Text'));
    const pastedText = event.clipboardData?.getData('Text');
    // TODO try catch
    if (!pastedText) return;
    try {
      const newSection = new Section(JSON.parse(pastedText) as Section);
      this.unitService.replaceSection(this.selectionService.selectedPageIndex, this.sectionIndex, newSection);
    } catch (e) {
      this.messageService.showError('Fehler beim Lesen der Sektion');
    }
    // console.log('evvv', event.target);
    // (event.target as HTMLInputElement).value = 'abc';
    event.stopPropagation();
  }