У меня есть приложение angular, и у меня возникла проблема при попытке создать функциональность в одном из моих компонентов. Просто для контекста: у меня есть приложение для бэк-офиса, которое управляет проигрывателем HTML. В одном из компонентов я могу настроить расположение строк, представленных в плеере. Вот распечатка компонента:
Я хочу иметь возможность изменять размеры ячеек строки, и для этого я построил «resizer» (зеленая часть компонента) внутри компонента. Для этого я использую некоторые логи c функциональности Drag and Drop в Angular Material (https://material.angular.io/cdk/drag-drop/overview).
Проблема в том, что когда изменение размера ячейки влияет на ширину всех ячеек.
Например: если я пытаюсь переместить этот обработчик «resizer» (как показано на рисунке ниже) вправо:
... влияет на ширину 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;
}
Любая помощь приветствуется. Спасибо.