UNPKG

@progress/kendo-angular-dropdowns

Version:

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

174 lines (173 loc) 7.27 kB
/**----------------------------------------------------------------------------------------- * Copyright © 2025 Progress Software Corporation. All rights reserved. * Licensed under commercial license. See LICENSE.md in the project root for more information *-------------------------------------------------------------------------------------------*/ import { ChangeDetectorRef, Directive, ElementRef, EventEmitter, HostListener, Input, NgZone, Output, Renderer2 } from '@angular/core'; import { TreeViewComponent } from '@progress/kendo-angular-treeview'; import { fetchDescendentNodes, isPresent } from '../../common/util'; import { BaseCheckDirective } from './base-check.directive'; import * as i0 from "@angular/core"; /** * @hidden * * A directive which manages the in-memory checked state of the MultiSelectTree nodes. */ export class CheckAllDirective extends BaseCheckDirective { element; zone; cdr; renderer; lastAction; treeview; /** * Defines the collection that will store the full checked items. */ checkedItems; /** * The item key/keys by which the data items will be compared. */ valueField; focused; /** * 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(); currentCheckedState; currentIndeterminateState; handleChange(event) { // Need to store the current checkbox state at the moment of click this.currentCheckedState = event.checked; this.currentIndeterminateState = this.isIndeterminate; this.treeview.nodes.map((_value, index) => { const itemIndex = String(index); const itemLookup = this.treeview.itemLookup(itemIndex); this.checkNode(itemLookup); }); this.checkedItemsChange.emit(this.checkedItems.slice()); } get isIndeterminate() { const isIndeterminate = this.treeview.nodes.some((_node, index) => { const itemIndex = String(index); const itemLookup = this.treeview.itemLookup(itemIndex); return this.someChecked(itemLookup); }); return this.isChecked ? false : isIndeterminate; } get isChecked() { const isChecked = this.treeview.nodes.every((_node, index) => { const itemIndex = String(index); const itemLookup = this.treeview.itemLookup(itemIndex); return this.allChecked(itemLookup); }); return isChecked; } constructor(element, zone, cdr, renderer) { super(); this.element = element; this.zone = zone; this.cdr = cdr; this.renderer = renderer; } ngOnChanges(changes) { if (isPresent(changes['checkedItems'])) { this.updateItems(); this.renderer.setProperty(this.element.nativeElement, 'checked', this.isChecked); this.renderer.setProperty(this.element.nativeElement, 'indeterminate', this.isIndeterminate); } } ngOnInit() { if (this.focused) { this.nextTick(() => this.element.nativeElement.focus()); } } nextTick(fn) { this.zone.runOutsideAngular(() => setTimeout(fn)); } checkNode(itemLookup) { if (this.treeview.isDisabled(itemLookup.item.dataItem, itemLookup.item.index)) { return; } const pendingCheck = []; const filter = (item) => this.treeview.isVisible(item.dataItem, item.index) && !this.treeview.isDisabled(item.dataItem, item.index); pendingCheck.push(itemLookup.item); fetchDescendentNodes(itemLookup, filter) .forEach(lookup => pendingCheck.push(lookup.item)); pendingCheck.forEach(item => { if (this.currentIndeterminateState) { if (this.lastAction === 'check') { this.addItem(item); } else { this.removeItem(item); } return; } if (this.currentCheckedState) { this.addItem(item); } else { this.removeItem(item); } }); } allChecked(lookup) { const children = lookup && lookup.children; if (!Array.isArray(children)) { return; } const childrenChecked = children.every(child => { if (child.children.length) { return this.isItemChecked(child.item) && this.allChecked(child); } return this.isItemChecked(child.item); }); return childrenChecked && this.isItemChecked(lookup.item); } someChecked(lookup) { const children = lookup && lookup.children; if (!Array.isArray(children)) { return; } const childrenChecked = children.some(child => { if (child.children.length) { return this.isItemChecked(child.item) || this.someChecked(child); } return this.isItemChecked(child.item); }); return childrenChecked || this.isItemChecked(lookup.item); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CheckAllDirective, deps: [{ token: i0.ElementRef }, { token: i0.NgZone }, { token: i0.ChangeDetectorRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: CheckAllDirective, isStandalone: true, selector: "[checkAll]", inputs: { lastAction: "lastAction", treeview: "treeview", checkedItems: "checkedItems", valueField: "valueField", focused: ["checkAll", "focused"] }, outputs: { checkedItemsChange: "checkedItemsChange" }, host: { listeners: { "change": "handleChange($event.target)" } }, usesInheritance: true, usesOnChanges: true, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CheckAllDirective, decorators: [{ type: Directive, args: [{ // eslint-disable-next-line selector: '[checkAll]', standalone: true }] }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.NgZone }, { type: i0.ChangeDetectorRef }, { type: i0.Renderer2 }]; }, propDecorators: { lastAction: [{ type: Input }], treeview: [{ type: Input }], checkedItems: [{ type: Input }], valueField: [{ type: Input }], focused: [{ type: Input, args: ['checkAll'] }], checkedItemsChange: [{ type: Output }], handleChange: [{ type: HostListener, args: ['change', ['$event.target']] }] } });