UNPKG

@progress/kendo-angular-dropdowns

Version:

A wide variety of native Angular dropdown components including AutoComplete, ComboBox, DropDownList, DropDownTree, MultiColumnComboBox, MultiSelect, and MultiSelectTree

173 lines (172 loc) 6.95 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, EventEmitter, Input, Output } from '@angular/core'; import { TreeViewComponent } from '@progress/kendo-angular-treeview'; import { Subscription } from 'rxjs'; import { filter } from 'rxjs/operators'; import { fetchDescendentNodes, isPresent } from '../../common/util'; import { BaseCheckDirective } from './base-check.directive'; import * as i0 from "@angular/core"; import * as i1 from "@progress/kendo-angular-treeview"; /** * @hidden * * A directive which manages the in-memory checked state of the MultiSelectTree nodes. */ export class CheckDirective extends BaseCheckDirective { treeView; /** * Specifies whether items should be checked on click, * and whether checking a node will also check the node children as well. * The `checkChildren` prop also enables or disables parent item checking (i.e. checking all child items automatically checks the parent). */ checkable; /** * The item key/keys by which the data items will be compared. */ valueField; /** * Defines the collection that will store the full checked items. */ checkedItems; /** * Fires when the `checkedItems` collection was updated. */ checkedItemsChange = new EventEmitter(); /** * Holds a Set with just the checked item keys. * * Should be updated each time the `checkedItems` value or content is changed. * Can be used for efficient look-up of whether an item is checked or not (O(1) access time). */ checkedKeys = new Set(); subscriptions = new Subscription(); clickSubscription; constructor(treeView) { super(); this.treeView = treeView; this.subscriptions.add(this.treeView.checkedChange .subscribe(this.handleCheckedChange.bind(this))); this.treeView.isChecked = this.getCheckedState.bind(this); } ngOnChanges(changes) { if (isPresent(changes['checkable'])) { this.toggleCheckOnClick(); } if (isPresent(changes['checkedItems'])) { this.updateItems(); } } ngOnDestroy() { this.subscriptions.unsubscribe(); this.unsubscribeClick(); } getCheckedState(dataItem, index) { if (this.isItemChecked({ dataItem, index })) { return 'checked'; } else if (this.checkable.checkChildren && this.isItemIndeterminate(this.treeView.itemLookup(index))) { return 'indeterminate'; } else { return 'none'; } } handleCheckedChange(node) { this.checkNode(node); // parents should be checked if `checkChildren` is set to `true` (single config option for both) const checkParents = this.checkable.checkChildren; if (checkParents) { this.checkParents(node.parent); } this.checkedItemsChange.emit(this.checkedItems.slice()); } toggleCheckOnClick() { this.unsubscribeClick(); if (this.checkable.checkOnClick) { this.clickSubscription = this.treeView.nodeClick .pipe(filter(event => event.type === 'click')) .subscribe(event => { const lookup = this.treeView.itemLookup(event.item.index); this.handleCheckedChange(lookup); }); } } unsubscribeClick() { if (this.clickSubscription) { this.clickSubscription.unsubscribe(); this.clickSubscription = null; } } checkNode(lookup) { if (this.treeView.isDisabled(lookup.item.dataItem, lookup.item.index)) { return; } const target = lookup.item; const pendingCheck = [target]; // TODO: extract in a separate `checkChildren` method? if (this.checkable.checkChildren) { const filter = (item) => this.treeView.isVisible(item.dataItem, item.index) && !this.treeView.isDisabled(item.dataItem, item.index); fetchDescendentNodes(lookup, filter) .forEach(lookup => pendingCheck.push(lookup.item)); } const shouldCheck = !this.isItemChecked(target); pendingCheck.forEach(item => { if (shouldCheck) { this.addItem(item); } else { this.removeItem(item); } }); } checkParents(parent) { let currentParent = parent; while (currentParent) { const allChildrenSelected = currentParent.children.every(item => this.isItemChecked(item)); if (allChildrenSelected) { this.addItem(currentParent.item); } else { this.removeItem(currentParent.item); } currentParent = currentParent.parent; } } isItemIndeterminate(lookup) { const children = lookup.children; if (!Array.isArray(children) || children.length === 0) { return false; } let index = 0; let child = children[index]; while (isPresent(child)) { if (this.isItemChecked(child.item) || this.isItemIndeterminate(child)) { return true; } index += 1; child = children[index]; } return false; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CheckDirective, deps: [{ token: i1.TreeViewComponent }], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: CheckDirective, isStandalone: true, selector: "[kendoMultiSelectTreeCheckable]", inputs: { checkable: "checkable", valueField: "valueField", checkedItems: "checkedItems" }, outputs: { checkedItemsChange: "checkedItemsChange" }, usesInheritance: true, usesOnChanges: true, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CheckDirective, decorators: [{ type: Directive, args: [{ selector: '[kendoMultiSelectTreeCheckable]', standalone: true }] }], ctorParameters: function () { return [{ type: i1.TreeViewComponent }]; }, propDecorators: { checkable: [{ type: Input }], valueField: [{ type: Input }], checkedItems: [{ type: Input }], checkedItemsChange: [{ type: Output }] } });