@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
JavaScript
/**-----------------------------------------------------------------------------------------
* 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']
}] } });