Изменение размера div влияет только на смежную ширину - PullRequest
0 голосов
/ 20 февраля 2020

У меня есть приложение angular, и у меня возникла проблема при попытке создать функциональность в одном из моих компонентов. Просто для контекста: у меня есть приложение для бэк-офиса, которое управляет проигрывателем HTML. В одном из компонентов я могу настроить расположение строк, представленных в плеере. Вот распечатка компонента: enter image description here

Я хочу иметь возможность изменять размеры ячеек строки, и для этого я построил «resizer» (зеленая часть компонента) внутри компонента. Для этого я использую некоторые логи c функциональности Drag and Drop в Angular Material (https://material.angular.io/cdk/drag-drop/overview).

Проблема в том, что когда изменение размера ячейки влияет на ширину всех ячеек.

Например: если я пытаюсь переместить этот обработчик «resizer» (как показано на рисунке ниже) вправо: enter image description here

... влияет на ширину 3-х текущих делений (для красной ячейки, ячейки «A» и ячейки «Service Name»). То, что мне нужно сделать, это: при перемещении обработчика вправо влияют только на ширину ячейки «A» и «Имя службы». Другими словами, Я хочу повлиять на ширину только для соседнего элемента .

Я поделюсь кодом моего компонента ниже:

player- row-editor.component.ts

import {
  Component,
  Input,
  Output,
  EventEmitter,
  ViewChild,
  ElementRef
} from "@angular/core";
import { CdkDragDrop, moveItemInArray } from "@angular/cdk/drag-drop";

import { PlayerRowEditorItem } from "@moviik-ui/typings/player-row-editor.interface";

@Component({
  selector: "mik-player-row-editor",
  templateUrl: "./player-row-editor.component.html",
  styleUrls: ["./player-row-editor.component.scss"]
})
export class PlayerRowEditorComponent {
  @Input() rowBackground = "yellow";
  @Input() shadow = false;
  @Input() cornerRadius = "";
  @Input() columnGap: number;
  @Input() items: PlayerRowEditorItem[];
  @Input() readonly: boolean;

  @Output() removeCell: EventEmitter<PlayerRowEditorItem> = new EventEmitter();
  @Output() openCell: EventEmitter<PlayerRowEditorItem> = new EventEmitter();
  @Output() columnsWidthChanged: EventEmitter<number[]> = new EventEmitter();
  @Output() columnsPositionChanged: EventEmitter<string[]> = new EventEmitter();

  @ViewChild("row", { static: true }) row: ElementRef<HTMLTableRowElement>;

  startX: number;
  startWidth: number;
  currentCell: HTMLTableDataCellElement;
  currentDirection: 1 | -1 = 1;

  drop(event: CdkDragDrop<string[]>) {
    if (this.readonly) return;

    moveItemInArray(this.items, event.previousIndex, event.currentIndex);
    const itemsId = [];
    this.items.map(val => {
      itemsId.push(val.id);
    });
    this.columnsPositionChanged.emit(itemsId);
  }

  trackBy(index: number, item: PlayerRowEditorItem) {
    return item.id;
  }

  startResize = (e: MouseEvent | TouchEvent, direction: 1 | -1) => {
    if (this.readonly) return;

    this.currentDirection = direction;
    if (e instanceof TouchEvent) {
      this.startX = e.touches[0].clientX;
      document.addEventListener("touchmove", this.moveResize, false);
      document.addEventListener("touchend", this.stopResize, false);
    } else {
      this.startX = e.clientX;
      document.addEventListener("mousemove", this.moveResize, false);
      document.addEventListener("mouseup", this.stopResize, false);
    }

    this.startWidth = parseInt(
      document.defaultView.getComputedStyle(this.currentCell).width,
      10
    );
  };

  moveResize = (e: MouseEvent | TouchEvent) => {
    if (this.readonly) return;

    let width = 0;
    if (e instanceof TouchEvent) {
      width = e.touches[0].clientX - this.startX;
    } else {
      width = e.clientX - this.startX;
    }
    const total = this.startWidth + this.currentDirection * width;
    const rowWidth = this.row.nativeElement.getBoundingClientRect().width;
    this.currentCell.style.width = `${(total / rowWidth) * 100}%`;
  };

  stopResize = (e: MouseEvent | TouchEvent) => {
    if (this.readonly) return;

    if (e instanceof TouchEvent) {
      document.removeEventListener("touchmove", this.moveResize, false);
      document.removeEventListener("touchend", this.stopResize, false);
    } else {
      document.removeEventListener("mousemove", this.moveResize, false);
      document.removeEventListener("mouseup", this.stopResize, false);
    }

    const row = this.row.nativeElement;
    const totalWidth = row.getBoundingClientRect().width;
    const cells = row.querySelectorAll("div.cell");
    const cellsWidth = Array.from(cells).map(td => {
      const width = td.getBoundingClientRect().width;
      return (width / totalWidth) * 100;
    });
    this.columnsWidthChanged.emit(cellsWidth);
  };
}

player-row-editor.component. html:

<div
  cdkDropList
  cdkDropListOrientation="horizontal"
  class="container"
  (cdkDropListDropped)="drop($event)"
>
  <!-- Row titles -->
  <div class="row-item">
    <div
      *ngFor="let item of items; let last = last"
      [style.width.%]="item.width"
      [style.margin-right]="!last && columnGap + '%'"
      class="table-header mdc-typography--caption mdc-theme--text-body"
    >
      <span class="truncate-text--1-line" title="{{ item.title }}">
        {{ item.title }}
      </span>
    </div>
  </div>

  <!-- Row -->
  <div
    class="row-item"
    [style.background]="rowBackground"
    [class.mdc-theme--disabled-bg]="!items.length"
    [class.shadow]="(!columnGap || columnGap <= 0) && shadow"
    [style.borderRadius]="cornerRadius + 'px'"
    #row
  >
    <!-- Empty row placeholder -->
    <div
      class="cell left-corner-radius right-corner-radius"
      *ngIf="!items.length"
    ></div>

    <!-- Cells -->
    <ng-container
      *ngFor="
        let item of items;
        let first = first;
        let last = last;
        trackBy: trackBy
      "
    >
      <div
        cdkDrag
        [cdkDragDisabled]="readonly || cell === currentCell"
        class="cell"
        [class.draggable]="!readonly && cell !== currentCell"
        [ngStyle]="item.style"
        [style.margin-right]="!last && columnGap + '%'"
        [class.left-corner-radius]="first || (columnGap && columnGap > 0)"
        [class.right-corner-radius]="last || (columnGap && columnGap > 0)"
        [style.width.%]="item.width"
        [class.border-color]="item.id === 'service_color'"
        [class.shadow]="columnGap && columnGap > 0 && shadow"
        (click)="
          currentCell === cell ? (currentCell = null) : (currentCell = cell)
        "
        #cell
      >
        <!-- Generic cell -->
        <div>
          <p
            *ngIf="
              item.id !== 'queuing_statistics' &&
              item.id !== 'agent_photo' &&
              item.id !== 'service_color'
            "
            class="mdc-typography--headline4 truncate-text--1-line"
          >
            {{ item.example }}
          </p>

          <p
            class="mdc-typography--subtitle1 truncate-text--1-line"
            *ngIf="item?.options?.show_description"
          >
            {{ 'devices.playerRowEditor.description' | i18n }}
          </p>
        </div>

        <!-- Agent Photo cell -->
        <div class="agent-photo" *ngIf="item.id === 'agent_photo'">
          <mik-avatar size="xs" icon="User"></mik-avatar>
        </div>

        <!-- Colored cell -->
        <div *ngIf="item.id === 'service_color'">
          <!-- <mik-avatar size="xs" icon="Appearance"></mik-avatar> -->
        </div>

        <!-- Queuing Statistics cell -->
        <div
          *ngIf="item.id === 'queuing_statistics'"
          class="mdc-typography--body1 queuing-statistics"
        >
          <div
            *ngIf="item?.options?.show_time_expected"
            fxLayout="row"
            fxLayoutAlign="start center"
            fxLayoutGap="16px"
          >
            <mik-icon name="KPI" theme=""></mik-icon>
            <span>{{ 'devices.playerRowEditor.kpi' | i18n }}</span>
          </div>
          <div
            *ngIf="item?.options?.show_tickets_ahead"
            fxLayout="row"
            fxLayoutAlign="start center"
            fxLayoutGap="16px"
          >
            <mik-icon name="VisitorsQueue" theme=""></mik-icon>
            <span>{{ 'devices.playerRowEditor.visitors' | i18n }}</span>
          </div>
        </div>

        <!-- Resizer -->
        <div
          class="resizer"
          [class.visible]="currentCell === cell"
          *ngIf="!readonly"
        >
          <div
            class="handle"
            (touchstart)="startResize($event, -1)"
            (mousedown)="startResize($event, -1)"
          ></div>
          <div
            class="handle"
            (touchstart)="startResize($event, 1)"
            (mousedown)="startResize($event, 1)"
          ></div>
          <div class="tools">
            <div class="remove" (click)="removeCell.emit(item)">
              <i class="icons icon-Delete"></i>
            </div>
            <div class="edit" (click)="openCell.emit(item)">
              <i class="icons icon-Edit"></i>
            </div>
          </div>
        </div>
      </div>
    </ng-container>
  </div>
</div>

player -row-editor.component.s css:

@import 'variables';

.container {
  width: 100%;
  max-width: 100%;
  border-collapse: collapse;
  word-break: break-all;
}

.shadow {
  box-shadow: 0 16px 16px -16px rgba(0, 0, 0, 0.9);
}

.left-corner-radius {
  border-top-left-radius: inherit;
  border-bottom-left-radius: inherit;
}

.right-corner-radius {
  border-top-right-radius: inherit;
  border-bottom-right-radius: inherit;
}

.table-header {
  flex: 1 1 auto;
  padding-bottom: 16px;
}

.agent-photo,
.queuing-statistics {
  display: inline-block;
}

.queuing-statistics mik-icon {
  opacity: 0.8;
}

.row-item {
  display: flex;
  align-items: center;
}

.cell {
  // overflow: auto;
  // resize: horizontal;
  position: relative;
  height: 96px;
  padding: 0 16px;
  display: flex;
  align-items: center;
  box-sizing: border-box; /* Use box-sizing so that element's outer width will match width property */
  flex: 1 1 auto;
  border: 2px dashed rgba($mik-text-color, 0.1);

  &::after {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: #0001;
  }

  &:hover {
    &::after {
      border-radius: inherit;
      content: '';
    }
  }

  p {
    margin: 0;
  }

  &.draggable {
    cursor: move;
  }

  .resizer {
    display: none;
    width: 100%;
    height: 100%;
    position: absolute;
    top: 0;
    left: 0;
    box-shadow: 0 0 0 2px $mdc-theme-primary inset;
    user-select: none;
    border-radius: inherit;

    &.visible {
      display: block;
    }

    .handle {
      position: absolute;
      height: 12px;
      width: 12px;
      top: 50%;
      transform: translateY(-50%);
      background: #fff;
      border: 2px solid $mdc-theme-primary;
      cursor: col-resize;
      z-index: 99;

      &:first-child {
        left: -6px;
      }
      &:nth-child(2) {
        right: -6px;
      }
    }

    .tools {
      position: absolute;
      right: 0;
      bottom: -40px;
      background: $mdc-theme-primary;
      display: flex;

      & > * {
        cursor: pointer;
        color: #fff;
        width: 40px;
        height: 40px;
        display: flex;
        justify-content: center;
        align-items: center;
      }
    }
  }
}

.border-color {
  padding: 0 1px;
  justify-content: center !important;
}

Любая помощь приветствуется. Спасибо.

...