import { Component, AfterViewInit, Renderer2, NgZone, OnDestroy, ViewEncapsulation } from '@angular/core'; import { GridComponent } from '@progress/kendo-angular-grid'; import { take } from 'rxjs/operators'; import { Product } from './model'; import { sampleProducts } from './products'; import { tableRow, closest } from './utils'; @Component({ selector: 'my-app', encapsulation: ViewEncapsulation.None, styleUrls: ['./styles.css'], template: `
; constructor(private renderer: Renderer2, public zone: NgZone) {} public ngAfterViewInit() { this.setDraggableRows(); } public ngOnDestroy() { this.destroyListeners(); } public destroyListeners(): void { const tableRows: Array = this.getAllRows(); tableRows.forEach((item) => { item.removeEventListener('dragstart', () => {}); }); } public setDraggableRows(): void { const tableRows: Array = this.getAllRows(); tableRows.forEach((item: HTMLElement) => { this.renderer.setAttribute(item, 'draggable', 'true'); this.addDragListeners(item); }); } public getAllRows(): Array { return Array.from(document.querySelectorAll('.k-grid tr')); } public addDragListeners(item: HTMLElement): void { item.addEventListener('dragstart', (e: DragEvent) => { const rowItem: HTMLTableDataCellElement = item.querySelector('td'); // Prevents dragging Grid header row if (rowItem === null) { return; } let selectedItem: Product = sampleProducts.find((i) => i.ProductID === Number(rowItem.textContent)); let dataItem = JSON.stringify(selectedItem); e.dataTransfer.setData('text/plain', dataItem); }); } // Prevents dragging header and 'no records' row. public onDragStart(e: DragEvent, grid: GridComponent): void { this.currentGridRef = grid; const draggedElement: string = (e.target as HTMLElement).innerText; if (draggedElement.includes('ID') || draggedElement === this.noRecordsMsg) { e.preventDefault(); } } public onDrop(e: DragEvent, dragStartGridRef: GridComponent, droppedRowGridRef: GridComponent): void { let data = e.dataTransfer.getData('text/plain'); let droppedItem: Product = JSON.parse(data); // Prevent dropping row in the same Grid if (dragStartGridRef !== this.currentGridRef) { this.updateGridsData(droppedItem, droppedRowGridRef, dragStartGridRef); } // When new row is added to a table, the draggable attributes is set to that row. this.zone.onStable.pipe(take(1)).subscribe(() => { this.destroyListeners(); this.setDraggableRows(); }); this.removeLineIndicators(); } public onDragOver(e: DragEvent, grid: GridComponent): void { e.preventDefault(); if (this.targetCells) { this.removeLineIndicators(); } const targetEl = e.target as HTMLElement; if (this.currentGridRef !== grid && (targetEl.tagName === 'TD' || targetEl.tagName === 'TH')) { // Set drop line indication this.targetCells = targetEl.parentElement.querySelectorAll('td, th'); this.targetCells.forEach((td: HTMLElement) => { const gridData: any[] = grid.data as any[]; if (td.tagName === 'TH' && gridData.length !== 0) { this.renderer.addClass(td, 'th-line'); this.dropIndex = 0; } else if (td.tagName === 'TD') { this.renderer.addClass(td, 'td-line'); this.dropIndex = closest(e.target, tableRow).rowIndex + 1; } }); } } public removeLineIndicators() { this.targetCells.forEach((td: HTMLElement) => { this.renderer.removeAttribute(td, 'class'); }); } public updateGridsData(droppedItem: Product, droppedRowGridRef: GridComponent, dragStartGridRef: GridComponent): void { const droppedRowGridData = droppedRowGridRef.data as Array; const dragStartGridData = dragStartGridRef.data as Array; let index: number = droppedRowGridData.findIndex((i) => i.ProductID === droppedItem.ProductID); droppedRowGridData.splice(index, 1); dragStartGridData.splice(this.dropIndex, 0, droppedItem); } }