Skip to content
Snippets Groups Projects
drop-list-simple.component.ts 7.39 KiB
Newer Older
  • Learn to ignore specific revisions
  • rhenck's avatar
    rhenck committed
    import { Component, Input } from '@angular/core';
    
    import { CdkDragDrop, CdkDragEnter, CdkDragStart } from '@angular/cdk/drag-drop/drag-events';
    
    rhenck's avatar
    rhenck committed
    import {
      CdkDrag, CdkDropList, moveItemInArray, transferArrayItem
    } from '@angular/cdk/drag-drop';
    
    import { FormElementComponent } from 'common/directives/form-element-component.directive';
    import { DropListSimpleElement } from
      'common/models/elements/compound-elements/cloze/cloze-child-elements/drop-list-simple';
    
    import { DragNDropValueObject } from 'common/models/elements/element';
    
    import { DropListComponent } from 'common/components/input-elements/drop-list.component';
    
    rhenck's avatar
    rhenck committed
    
    @Component({
    
      selector: 'aspect-drop-list-simple',
    
    rhenck's avatar
    rhenck committed
      template: `
        <div class="list-container">
    
            <!-- Border width is a workaround to enable/disable the Material cdk-drop-list-receiving class style.-->
    
    rhenck's avatar
    rhenck committed
          <div class="list"
    
               [class.errors]="elementFormControl.errors && elementFormControl.touched"
    
    rhenck's avatar
    rhenck committed
               [class.dropList-highlight]="elementModel.highlightReceivingDropList"
    
               [style.min-height.px]="elementModel.height"
    
    rhenck's avatar
    rhenck committed
               [style.border-color]="elementModel.highlightReceivingDropListColor"
               [style.border-width.px]="elementModel.highlightReceivingDropList ? 2 : 0"
    
               [style.color]="elementModel.styling.fontColor"
               [style.font-family]="elementModel.styling.font"
               [style.font-size.px]="elementModel.styling.fontSize"
               [style.font-weight]="elementModel.styling.bold ? 'bold' : ''"
               [style.font-style]="elementModel.styling.italic ? 'italic' : ''"
               [style.text-decoration]="elementModel.styling.underline ? 'underline' : ''"
               [style.backgroundColor]="elementModel.styling.backgroundColor"
    
               [matTooltip]="elementFormControl.errors && elementFormControl.touched ?
                             (elementFormControl.errors | errorTransform: elementModel) : ''"
    
               [matTooltipClass]="'error-tooltip'"
    
    rhenck's avatar
    rhenck committed
               cdkDropList
               [id]="elementModel.id"
               [cdkDropListData]="this"
               [cdkDropListConnectedTo]="elementModel.connectedTo"
               [cdkDropListEnterPredicate]="onlyOneItemPredicate"
    
               tabindex="0"
               (focusout)="elementFormControl.markAsTouched()"
    
    rhenck's avatar
    rhenck committed
               (cdkDropListDropped)="drop($event)">
    
            <ng-container *ngIf="!parentForm">
    
                <ng-container *ngFor="let dropListValueElement of $any(elementModel.value)">
                    <ng-container [ngTemplateOutlet]="dropObject"
                                  [ngTemplateOutletContext]="{ $implicit: dropListValueElement }">
    
                    </ng-container>
    
                </ng-container>
            </ng-container>
    
            <ng-container *ngIf="parentForm">
    
                <ng-container *ngFor="let value of elementFormControl.value">
                    <ng-container [ngTemplateOutlet]="dropObject" [ngTemplateOutletContext]="{ $implicit: value }">
                    </ng-container>
    
                </ng-container>
            </ng-container>
    
            <ng-template #dropObject let-value>
              <div class="item"
    
                   [style.background-color]="elementModel.styling.itemBackgroundColor"
    
                   cdkDrag [cdkDragData]="{ element: value }"
    
                   (cdkDragStarted)="dragStart($event)" (cdkDragEnded)="dragEnd()" (cdkDragEntered)="dragEnter($event)">
    
                     [style.font-size.px]="elementModel.styling.fontSize"
                     [style.background-color]="elementModel.styling.itemBackgroundColor">
    
                <div class="drag-placeholder" *cdkDragPlaceholder
    
                     [style.height.%]="placeholderDimensions.height"
                     [style.width.%]="placeholderDimensions.width">
    
    rhenck's avatar
    rhenck committed
              </div>
    
            </ng-template>
    
    rhenck's avatar
    rhenck committed
          </div>
        </div>
      `,
      styles: [
        '.list-container {display: flex; flex-direction: column; width: 100%; height: 100%;}',
        '.list {width: 100%; height: 100%; border-radius: 5px}',
    
    rhenck's avatar
    rhenck committed
        '.item {border-radius: 5px; padding: 0 5px; height: 100%; text-align: center;}',
    
    rhenck's avatar
    rhenck committed
        '.item:not(:last-child) {margin-bottom: 5px;}',
    
        '.item:active {cursor: grabbing}',
    
        '.errors {box-sizing: border-box; border: 2px solid #f44336 !important;}',
    
    rhenck's avatar
    rhenck committed
        '.error-message {font-size: 75%; margin-top: 10px;}',
    
        '.cdk-drag-preview {padding: 8px 20px; border-radius: 5px; box-shadow: 2px 2px 5px black;}',
    
        '.drag-placeholder {box-sizing: border-box; border-radius: 5px; background-color: lightgrey; border: dotted 3px #999;}',
    
    rhenck's avatar
    rhenck committed
        '.drag-placeholder {transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}',
        '.cdk-drag-animating {transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}',
    
        '.dropList-highlight.cdk-drop-list-receiving {border: solid;}',
    
        '.dropList-highlight.cdk-drop-list-dragging {border: solid;}'
    
    rhenck's avatar
    rhenck committed
      ]
    })
    export class DropListSimpleComponent extends FormElementComponent {
      @Input() elementModel!: DropListSimpleElement;
    
      placeholderDimensions: { width: number, height: number } = { width: 1, height: 1 };
    
      bodyElement: HTMLElement = document.body;
    
    
      dragStart(event: CdkDragStart<DropListSimpleComponent>): void {
    
        this.setPlaceholderDimensions(
    
          event.source.dropContainer.data.elementFormControl.value.length - 1,
          event.source.dropContainer.data.elementModel.orientation
        );
    
        this.bodyElement.classList.add('inheritCursors');
        this.bodyElement.style.cursor = 'grabbing';
      }
    
      dragEnd(): void {
        this.bodyElement.classList.remove('inheritCursors');
        this.bodyElement.style.cursor = 'unset';
      }
    
    
    rhenck's avatar
    rhenck committed
      drop(event: CdkDragDrop<DropListSimpleComponent>): void {
        if (!this.elementModel.readOnly) {
          if (event.previousContainer === event.container) {
    
            moveItemInArray(event.container.data.elementFormControl.value as unknown as DragNDropValueObject[],
    
              event.previousIndex,
              event.currentIndex);
    
            this.elementFormControl.setValue(
              (event.container.data.elementFormControl.value as DragNDropValueObject[])
            );
    
    rhenck's avatar
    rhenck committed
          } else {
            transferArrayItem(
    
              event.previousContainer.data.elementFormControl.value as unknown as DragNDropValueObject[],
              event.container.data.elementFormControl.value as unknown as DragNDropValueObject[],
    
    rhenck's avatar
    rhenck committed
              event.previousIndex,
              event.currentIndex
            );
    
            event.previousContainer.data.elementFormControl.setValue(
    
              (event.previousContainer.data.elementFormControl.value as DragNDropValueObject[])
    
          this.elementFormControl.setValue(
    
            (event.container.data.elementFormControl.value as DragNDropValueObject[])
    
      dragEnter(event: CdkDragEnter<DropListSimpleComponent | DropListComponent, { element: DragNDropValueObject }>) {
        const presentValueIDs = event.container.data.elementFormControl.value
          .map((value: DragNDropValueObject) => value.id);
        const itemCountOffset = presentValueIDs.includes(event.item.data.element.id) ? 1 : 0;
    
        this.setPlaceholderDimensions(
    
          presentValueIDs.length - itemCountOffset,
          event.container.data.elementModel.orientation);
      }
    
    
      setPlaceholderDimensions(itemsCount: number, orientation: unknown): void {
    
        this.placeholderDimensions.height = itemsCount && orientation !== 'horizontal' ? 1 : 100;
        this.placeholderDimensions.width = itemsCount && orientation === 'horizontal' ? 1 : 100;
    
    rhenck's avatar
    rhenck committed
      onlyOneItemPredicate = (drag: CdkDrag, drop: CdkDropList): boolean => (
    
        drop.data.elementFormControl.value.length < 1