UNPKG

@progress/kendo-angular-grid

Version:

Kendo UI Grid for Angular - high performance data grid with paging, filtering, virtualization, CRUD, and more.

236 lines (235 loc) 9.67 kB
/**----------------------------------------------------------------------------------------- * Copyright © 2025 Progress Software Corporation. All rights reserved. * Licensed under commercial license. See LICENSE.md in the project root for more information *-------------------------------------------------------------------------------------------*/ import { EventEmitter, Injectable, Output, Renderer2 } from '@angular/core'; import { isDocumentAvailable, isPresent } from '@progress/kendo-angular-common'; import { getOffset, isNextSibling, isPreviousSibling, dropPosition, hintIcons, hintSVGIcons, hintClasses, hintStyles, dropIndicatorClasses, dropIndicatorStyles, isDifferentParent, defaultSelectors } from './utils'; import * as i0 from "@angular/core"; /** * @hidden */ export class RowReorderService { renderer; hintElement = null; defaultSelectors = defaultSelectors; hintText = ''; skip; dropIndicator; lastDropPosition = dropPosition.forbidden; dragTarget = null; dropTarget = null; offsetY; rowReorder = new EventEmitter(); constructor(renderer) { this.renderer = renderer; } press(ev) { this.dragTarget = ev.dragTarget; this.offsetY = ev.dragEvent.offsetY; } dragStart() { this.createDropIndicator(); } drag(ev) { if (isPresent(ev.hintElement) && !isPresent(this.hintElement)) { this.hintElement = ev.hintElement; this.decorateHint(); } const position = { x: ev.dragEvent.clientX, y: ev.dragEvent.clientY - this.offsetY }; if (isPresent(this.hintElement)) { this.renderer.setStyle(this.hintElement, 'left', `${position.x}px`); this.renderer.setStyle(this.hintElement, 'top', `${position.y}px`); } this.positionDropIndicator(ev); } dragEnter(ev) { this.dropTarget = ev.dropTarget; } dragLeave() { this.dropTarget = null; this.hide(); } dragEnd() { this.destroyDropIndicator(); this.dragTarget = null; this.dropTarget = null; this.hintElement = null; } drop(ev) { this.destroyDropIndicator(); const rowReorderArgs = this.rowReorderArgs(this.dragTarget, this.dropTarget, ev.dragData); this.rowReorder.emit(rowReorderArgs); } reorderRows(ev, collection) { if (this.lastDropPosition === dropPosition.forbidden) { return; } const { draggedRows, dropTargetRow } = ev; const draggedDataItem = draggedRows[0].dataItem; const dropTargetDataItem = dropTargetRow.dataItem; const draggedItemIndex = collection.indexOf(draggedDataItem); const dropTargetIndex = collection.indexOf(dropTargetDataItem); const idxToAdd = this.calculateIndexToAdd(draggedItemIndex, dropTargetIndex); collection.splice(draggedItemIndex, 1); collection.splice(idxToAdd, 0, draggedDataItem); } get hintIcon() { return hintIcons[this.lastDropPosition]; } get hintSVGIcon() { return hintSVGIcons[this.lastDropPosition]; } getDefaultHintText(columns, data) { let hintText = ''; const columnFieldsArray = columns .toArray() .filter(column => !column.hidden && isPresent(column.field)) .map(column => column.field); const draggedDragRow = this.getDragRowPerElement(this.dragTarget, data); const draggedDataItem = draggedDragRow?.dataItem; isPresent(draggedDataItem) && columnFieldsArray.forEach(column => { const columnValue = draggedDataItem[column]; isPresent(columnValue) ? hintText += `${columnValue} ` : null; }); return hintText.trim(); } getDraggedRow(data) { return this.getDragRowPerElement(this.dragTarget, data); } rowReorderArgs(dragRow, dropRow, data) { const dragRowData = this.getDragRowPerElement(dragRow, data); const dropRowData = this.getDragRowPerElement(dropRow, data); return { draggedRows: [dragRowData], dropTargetRow: dropRowData, dropPosition: this.lastDropPosition }; } getDragRowPerElement(row, data) { let rowIndex = row?.getAttribute('data-kendo-grid-item-index'); rowIndex = rowIndex ? parseInt(rowIndex, 10) : -1; const skip = this.skip || 0; const dataItem = rowIndex === -1 ? null : data[rowIndex - skip]; return { dataItem, rowIndex, element: row }; } createDropIndicator() { if (!isDocumentAvailable()) { return; } this.dropIndicator = document.createElement('div'); this.decorateDropIndicator(); this.dropIndicator.innerHTML = ` <div class="k-drop-hint-start"></div> <div class="k-drop-hint-line"></div> `; document.body.appendChild(this.dropIndicator); this.hide(); } destroyDropIndicator() { if (!isDocumentAvailable()) { return; } if (this.dropIndicator && this.dropIndicator.parentElement) { document.body.removeChild(this.dropIndicator); this.dropIndicator = null; } } decorateHint() { hintClasses.forEach(className => this.renderer.addClass(this.hintElement, className)); Object.keys(hintStyles) .forEach(style => this.renderer.setStyle(this.hintElement, style, hintStyles[style])); } positionDropIndicator(ev) { this.lastDropPosition = this.getDropPosition(ev.dragEvent); this.updateDropIndicatorPosition(); } calculateIndexToAdd(dragIndex, dropIndex) { if (dragIndex > dropIndex && this.lastDropPosition === dropPosition.after) { return dropIndex + 1; } else if (dragIndex > dropIndex && this.lastDropPosition === dropPosition.before) { return dropIndex; } else if (dragIndex < dropIndex && this.lastDropPosition === dropPosition.after) { return dropIndex; } else if (dragIndex < dropIndex && this.lastDropPosition === dropPosition.before) { return dropIndex - 1; } } decorateDropIndicator() { dropIndicatorClasses.forEach(className => this.renderer.addClass(this.dropIndicator, className)); Object.keys(dropIndicatorStyles) .forEach(style => this.renderer.setStyle(this.dropIndicator, style, dropIndicatorStyles[style])); } getDropPosition(e) { if (this.dropTarget === this.dragTarget || !isPresent(this.dropTarget)) { return dropPosition.forbidden; } if (isDifferentParent(this.dropTarget, this.dragTarget)) { return dropPosition.forbidden; } const itemViewPortCoords = this.dropTarget.getBoundingClientRect(); const itemDivisionsCount = 2; const itemDivisionHeight = itemViewPortCoords.height / itemDivisionsCount; const pointerPosition = e.clientY; const itemTop = itemViewPortCoords.top; let currentDropPosition = null; if (pointerPosition < itemTop + itemDivisionHeight) { currentDropPosition = dropPosition.before; } else if (pointerPosition >= itemTop + itemViewPortCoords.height - itemDivisionHeight) { currentDropPosition = dropPosition.after; } if (currentDropPosition === dropPosition.before && isNextSibling(this.dropTarget, this.dragTarget)) { currentDropPosition = dropPosition.forbidden; } else if (currentDropPosition === dropPosition.after && isPreviousSibling(this.dropTarget, this.dragTarget)) { currentDropPosition = dropPosition.forbidden; } return currentDropPosition; } updateDropIndicatorPosition() { if (this.shouldHideDropIndicator() || !this.dropTarget) { this.hide(); return; } this.show(); const destinationItemOffset = getOffset(this.dropTarget); let indicatorOffsetTop = destinationItemOffset.top; const indicatorOffsetLeft = destinationItemOffset.left + this.dropIndicator.offsetWidth / 2; if (this.lastDropPosition === dropPosition.after) { indicatorOffsetTop += this.dropTarget.offsetHeight; } this.renderer.setStyle(this.dropIndicator, 'left', `${indicatorOffsetLeft}px`); this.renderer.setStyle(this.dropIndicator, 'top', `${indicatorOffsetTop}px`); } shouldHideDropIndicator() { return this.lastDropPosition === dropPosition.forbidden; } hide() { if (isPresent(this.dropIndicator)) { this.dropIndicator.style.display = 'none'; } } show() { if (isPresent(this.dropIndicator)) { this.dropIndicator.style.display = ''; } } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RowReorderService, deps: [{ token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RowReorderService }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RowReorderService, decorators: [{ type: Injectable }], ctorParameters: function () { return [{ type: i0.Renderer2 }]; }, propDecorators: { rowReorder: [{ type: Output }] } });