UNPKG

@progress/kendo-angular-treelist

Version:

Kendo UI TreeList for Angular - Display hierarchical data in an Angular tree grid view that supports sorting, filtering, paging, and much more.

197 lines (196 loc) 8.17 kB
/**----------------------------------------------------------------------------------------- * Copyright © 2025 Progress Software Corporation. All rights reserved. * Licensed under commercial license. See LICENSE.md in the project root for more information *-------------------------------------------------------------------------------------------*/ import { Directive, HostBinding, ChangeDetectorRef } from '@angular/core'; import { DraggableDirective, isDocumentAvailable } from '@progress/kendo-angular-common'; import { SelectionService } from './selection.service'; import { createState } from './selection-state'; import * as i0 from "@angular/core"; import * as i1 from "@progress/kendo-angular-common"; import * as i2 from "./selection.service"; const MINIMAL_DRAG_DISTANCE = 5; const createElement = () => { if (!isDocumentAvailable()) { return; } const marquee = document.createElement("div"); marquee.className = "k-marquee"; const marqueeColor = document.createElement("div"); marqueeColor.className = "k-marquee-color"; marquee.appendChild(marqueeColor); return marquee; }; const elementUnderCursor = ({ clientX, clientY }) => document && document.elementFromPoint(clientX, clientY); /** * @hidden */ export class MarqueeDirective { draggable; selection; changeDetector; get userSelection() { return this.selection.enableMarquee ? 'none' : null; } // possibly add snap pressArgs; marqueeElement; pressTarget; currentTarget; selectionSelected; state; subscriptions; selectionStarted = false; constructor(draggable, selection, changeDetector) { this.draggable = draggable; this.selection = selection; this.changeDetector = changeDetector; this.cellSelected = this.cellSelected.bind(this); this.rowSelected = this.rowSelected.bind(this); } ngOnInit() { // handle esc // trigger cancel this.subscriptions = this.draggable.kendoPress.subscribe(this.start.bind(this)); this.subscriptions.add(this.draggable.kendoDrag.subscribe(this.moveMarquee.bind(this))); this.subscriptions.add(this.draggable.kendoRelease.subscribe(this.endSelection.bind(this))); } ngOnDestroy() { this.subscriptions.unsubscribe(); this.clean(); } cellSelected(dataItem, column) { return this.state.has(dataItem, column); } rowSelected(dataItem) { return this.state.has(dataItem); } start(args) { const pressTarget = this.targetArgs(args, true); if (!pressTarget) { return; } this.pressTarget = pressTarget; this.pressArgs = args; } moveMarquee(args) { const press = this.pressArgs; if (!this.selectionStarted && press) { const distance = Math.sqrt((args.pageX - press.pageX) ** 2 + (args.pageY - press.pageY) ** 2); if (distance > MINIMAL_DRAG_DISTANCE) { this.selectionStarted = true; } else { return; } } if (this.pressTarget && !this.state) { this.initMarquee(); } if (this.marqueeElement) { const element = this.marqueeElement; const left = Math.min(args.pageX, press.pageX); const top = Math.min(args.pageY, press.pageY); const width = Math.abs(args.pageX - press.pageX); const height = Math.abs(args.pageY - press.pageY); element.style.left = `${left}px`; element.style.top = `${top}px`; element.style.width = `${width}px`; element.style.height = `${height}px`; } else if (this.state) { const currentTarget = this.targetArgs(args); if (currentTarget && (!this.currentTarget || this.currentTarget.item.data !== currentTarget.item.data || (this.selection.settings.mode === 'cell' && this.currentTarget.column !== currentTarget.column))) { this.currentTarget = currentTarget; this.state.fromArray(this.selection.rangeItems(this.pressTarget, currentTarget).map(item => ({ itemKey: item.dataItem, columnKey: item.column }))); this.selection.updateSelectedState(); this.changeDetector.detectChanges(); } } } endSelection(args) { if (!this.state) { return; } if ((this.pressArgs.pageX !== args.pageX || this.pressArgs.pageY !== args.pageY)) { const pressTarget = this.pressTarget; const releaseTarget = this.targetArgs(args); this.clean(); // if one is missing select first / last viewItem depending on the position // select column based on coordinates if (pressTarget && releaseTarget) { this.selection.dragging = true; this.selection.selectRange(pressTarget, releaseTarget); } else { this.changeDetector.detectChanges(); } } else { this.clean(); this.changeDetector.detectChanges(); } } clean() { if (this.marqueeElement) { document.body.removeChild(this.marqueeElement); this.marqueeElement = null; } if (this.selectionSelected) { if (this.selection.settings.mode === 'cell') { this.selection.isCellSelected = this.selectionSelected; } else { this.selection.isRowSelected = this.selectionSelected; } this.selectionSelected = null; } if (this.state) { this.state.clear(); this.state = null; } this.pressTarget = null; this.pressArgs = null; this.selectionStarted = false; this.selection.dragging = false; } targetArgs(args, skipFocusable) { let target = args.originalEvent.target; if (this.marqueeElement) { this.marqueeElement.style.display = 'none'; target = elementUnderCursor(args); this.marqueeElement.style.display = 'block'; } return this.selection.targetArgs(target, skipFocusable); } initMarquee() { this.state = createState(this.selection.settings); if (this.selection.settings.mode === 'cell') { this.selectionSelected = this.selection.isCellSelected; this.selection.isCellSelected = this.cellSelected; } else { this.selectionSelected = this.selection.isRowSelected; this.selection.isRowSelected = this.rowSelected; } this.changeDetector.detectChanges(); const drag = this.selection.settings.drag; if (!(drag && drag.snap)) { this.marqueeElement = createElement(); document.body.appendChild(this.marqueeElement); } } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: MarqueeDirective, deps: [{ token: i1.DraggableDirective }, { token: i2.SelectionService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: MarqueeDirective, isStandalone: true, selector: "[kendoTreeListSelectionMarquee]", host: { properties: { "style.user-select": "this.userSelection" } }, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: MarqueeDirective, decorators: [{ type: Directive, args: [{ selector: '[kendoTreeListSelectionMarquee]', standalone: true }] }], ctorParameters: function () { return [{ type: i1.DraggableDirective }, { type: i2.SelectionService }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { userSelection: [{ type: HostBinding, args: ['style.user-select'] }] } });