UNPKG

@angular/cdk

Version:

Angular Material Component Development Kit

1,553 lines (1,540 loc) 46.7 kB
import { SelectionModel } from './_selection-model-chunk.mjs'; import { isObservable, Subject, BehaviorSubject, of, combineLatest, EMPTY, concat } from 'rxjs'; import { take, filter, takeUntil, startWith, tap, switchMap, map, reduce, concatMap, distinctUntilChanged } from 'rxjs/operators'; import * as i0 from '@angular/core'; import { InjectionToken, inject, ViewContainerRef, Directive, TemplateRef, IterableDiffers, ChangeDetectorRef, ElementRef, Component, ViewEncapsulation, ChangeDetectionStrategy, Input, ViewChild, ContentChildren, EventEmitter, booleanAttribute, Output, numberAttribute, NgModule } from '@angular/core'; import { TREE_KEY_MANAGER } from './_tree-key-manager-chunk.mjs'; import { Directionality } from './_directionality-chunk.mjs'; import { isDataSource } from './_data-source-chunk.mjs'; import { coerceObservable } from './coercion-private.mjs'; import './_typeahead-chunk.mjs'; import './_keycodes-chunk.mjs'; class BaseTreeControl { dataNodes; expansionModel = new SelectionModel(true); trackBy; getLevel; isExpandable; getChildren; toggle(dataNode) { this.expansionModel.toggle(this._trackByValue(dataNode)); } expand(dataNode) { this.expansionModel.select(this._trackByValue(dataNode)); } collapse(dataNode) { this.expansionModel.deselect(this._trackByValue(dataNode)); } isExpanded(dataNode) { return this.expansionModel.isSelected(this._trackByValue(dataNode)); } toggleDescendants(dataNode) { this.expansionModel.isSelected(this._trackByValue(dataNode)) ? this.collapseDescendants(dataNode) : this.expandDescendants(dataNode); } collapseAll() { this.expansionModel.clear(); } expandDescendants(dataNode) { let toBeProcessed = [dataNode]; toBeProcessed.push(...this.getDescendants(dataNode)); this.expansionModel.select(...toBeProcessed.map(value => this._trackByValue(value))); } collapseDescendants(dataNode) { let toBeProcessed = [dataNode]; toBeProcessed.push(...this.getDescendants(dataNode)); this.expansionModel.deselect(...toBeProcessed.map(value => this._trackByValue(value))); } _trackByValue(value) { return this.trackBy ? this.trackBy(value) : value; } } class FlatTreeControl extends BaseTreeControl { getLevel; isExpandable; options; constructor(getLevel, isExpandable, options) { super(); this.getLevel = getLevel; this.isExpandable = isExpandable; this.options = options; if (this.options) { this.trackBy = this.options.trackBy; } } getDescendants(dataNode) { const startIndex = this.dataNodes.indexOf(dataNode); const results = []; for (let i = startIndex + 1; i < this.dataNodes.length && this.getLevel(dataNode) < this.getLevel(this.dataNodes[i]); i++) { results.push(this.dataNodes[i]); } return results; } expandAll() { this.expansionModel.select(...this.dataNodes.map(node => this._trackByValue(node))); } } class NestedTreeControl extends BaseTreeControl { getChildren; options; constructor(getChildren, options) { super(); this.getChildren = getChildren; this.options = options; if (this.options) { this.trackBy = this.options.trackBy; } if (this.options?.isExpandable) { this.isExpandable = this.options.isExpandable; } } expandAll() { this.expansionModel.clear(); const allNodes = this.dataNodes.reduce((accumulator, dataNode) => [...accumulator, ...this.getDescendants(dataNode), dataNode], []); this.expansionModel.select(...allNodes.map(node => this._trackByValue(node))); } getDescendants(dataNode) { const descendants = []; this._getDescendants(descendants, dataNode); return descendants.splice(1); } _getDescendants(descendants, dataNode) { descendants.push(dataNode); const childrenNodes = this.getChildren(dataNode); if (Array.isArray(childrenNodes)) { childrenNodes.forEach(child => this._getDescendants(descendants, child)); } else if (isObservable(childrenNodes)) { childrenNodes.pipe(take(1), filter(Boolean)).subscribe(children => { for (const child of children) { this._getDescendants(descendants, child); } }); } } } const CDK_TREE_NODE_OUTLET_NODE = new InjectionToken('CDK_TREE_NODE_OUTLET_NODE'); class CdkTreeNodeOutlet { viewContainer = inject(ViewContainerRef); _node = inject(CDK_TREE_NODE_OUTLET_NODE, { optional: true }); constructor() {} static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: CdkTreeNodeOutlet, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.0", type: CdkTreeNodeOutlet, isStandalone: true, selector: "[cdkTreeNodeOutlet]", ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: CdkTreeNodeOutlet, decorators: [{ type: Directive, args: [{ selector: '[cdkTreeNodeOutlet]' }] }], ctorParameters: () => [] }); class CdkTreeNodeOutletContext { $implicit; level; index; count; constructor(data) { this.$implicit = data; } } class CdkTreeNodeDef { template = inject(TemplateRef); when; constructor() {} static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: CdkTreeNodeDef, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.0", type: CdkTreeNodeDef, isStandalone: true, selector: "[cdkTreeNodeDef]", inputs: { when: ["cdkTreeNodeDefWhen", "when"] }, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: CdkTreeNodeDef, decorators: [{ type: Directive, args: [{ selector: '[cdkTreeNodeDef]', inputs: [{ name: 'when', alias: 'cdkTreeNodeDefWhen' }] }] }], ctorParameters: () => [] }); function getTreeNoValidDataSourceError() { return Error(`A valid data source must be provided.`); } function getTreeMultipleDefaultNodeDefsError() { return Error(`There can only be one default row without a when predicate function.`); } function getTreeMissingMatchingNodeDefError() { return Error(`Could not find a matching node definition for the provided node data.`); } function getTreeControlMissingError() { return Error(`Could not find a tree control, levelAccessor, or childrenAccessor for the tree.`); } function getMultipleTreeControlsError() { return Error(`More than one of tree control, levelAccessor, or childrenAccessor were provided.`); } class CdkTree { _differs = inject(IterableDiffers); _changeDetectorRef = inject(ChangeDetectorRef); _elementRef = inject(ElementRef); _dir = inject(Directionality); _onDestroy = new Subject(); _dataDiffer; _defaultNodeDef; _dataSubscription; _levels = new Map(); _parents = new Map(); _ariaSets = new Map(); get dataSource() { return this._dataSource; } set dataSource(dataSource) { if (this._dataSource !== dataSource) { this._switchDataSource(dataSource); } } _dataSource; treeControl; levelAccessor; childrenAccessor; trackBy; expansionKey; _nodeOutlet; _nodeDefs; viewChange = new BehaviorSubject({ start: 0, end: Number.MAX_VALUE }); _expansionModel; _flattenedNodes = new BehaviorSubject([]); _nodeType = new BehaviorSubject(null); _nodes = new BehaviorSubject(new Map()); _keyManagerNodes = new BehaviorSubject([]); _keyManagerFactory = inject(TREE_KEY_MANAGER); _keyManager; _viewInit = false; constructor() {} ngAfterContentInit() { this._initializeKeyManager(); } ngAfterContentChecked() { this._updateDefaultNodeDefinition(); this._subscribeToDataChanges(); } ngOnDestroy() { this._nodeOutlet.viewContainer.clear(); this._nodes.complete(); this._keyManagerNodes.complete(); this._nodeType.complete(); this._flattenedNodes.complete(); this.viewChange.complete(); this._onDestroy.next(); this._onDestroy.complete(); if (this._dataSource && typeof this._dataSource.disconnect === 'function') { this.dataSource.disconnect(this); } if (this._dataSubscription) { this._dataSubscription.unsubscribe(); this._dataSubscription = null; } this._keyManager?.destroy(); } ngOnInit() { this._checkTreeControlUsage(); this._initializeDataDiffer(); } ngAfterViewInit() { this._viewInit = true; } _updateDefaultNodeDefinition() { const defaultNodeDefs = this._nodeDefs.filter(def => !def.when); if (defaultNodeDefs.length > 1 && (typeof ngDevMode === 'undefined' || ngDevMode)) { throw getTreeMultipleDefaultNodeDefsError(); } this._defaultNodeDef = defaultNodeDefs[0]; } _setNodeTypeIfUnset(newType) { const currentType = this._nodeType.value; if (currentType === null) { this._nodeType.next(newType); } else if ((typeof ngDevMode === 'undefined' || ngDevMode) && currentType !== newType) { console.warn(`Tree is using conflicting node types which can cause unexpected behavior. ` + `Please use tree nodes of the same type (e.g. only flat or only nested). ` + `Current node type: "${currentType}", new node type "${newType}".`); } } _switchDataSource(dataSource) { if (this._dataSource && typeof this._dataSource.disconnect === 'function') { this.dataSource.disconnect(this); } if (this._dataSubscription) { this._dataSubscription.unsubscribe(); this._dataSubscription = null; } if (!dataSource) { this._nodeOutlet.viewContainer.clear(); } this._dataSource = dataSource; if (this._nodeDefs) { this._subscribeToDataChanges(); } } _getExpansionModel() { if (!this.treeControl) { this._expansionModel ??= new SelectionModel(true); return this._expansionModel; } return this.treeControl.expansionModel; } _subscribeToDataChanges() { if (this._dataSubscription) { return; } let dataStream; if (isDataSource(this._dataSource)) { dataStream = this._dataSource.connect(this); } else if (isObservable(this._dataSource)) { dataStream = this._dataSource; } else if (Array.isArray(this._dataSource)) { dataStream = of(this._dataSource); } if (!dataStream) { if (typeof ngDevMode === 'undefined' || ngDevMode) { throw getTreeNoValidDataSourceError(); } return; } this._dataSubscription = this._getRenderData(dataStream).pipe(takeUntil(this._onDestroy)).subscribe(renderingData => { this._renderDataChanges(renderingData); }); } _getRenderData(dataStream) { const expansionModel = this._getExpansionModel(); return combineLatest([dataStream, this._nodeType, expansionModel.changed.pipe(startWith(null), tap(expansionChanges => { this._emitExpansionChanges(expansionChanges); }))]).pipe(switchMap(([data, nodeType]) => { if (nodeType === null) { return of({ renderNodes: data, flattenedNodes: null, nodeType }); } return this._computeRenderingData(data, nodeType).pipe(map(convertedData => ({ ...convertedData, nodeType }))); })); } _renderDataChanges(data) { if (data.nodeType === null) { this.renderNodeChanges(data.renderNodes); return; } this._updateCachedData(data.flattenedNodes); this.renderNodeChanges(data.renderNodes); this._updateKeyManagerItems(data.flattenedNodes); } _emitExpansionChanges(expansionChanges) { if (!expansionChanges) { return; } const nodes = this._nodes.value; for (const added of expansionChanges.added) { const node = nodes.get(added); node?._emitExpansionState(true); } for (const removed of expansionChanges.removed) { const node = nodes.get(removed); node?._emitExpansionState(false); } } _initializeKeyManager() { const items = combineLatest([this._keyManagerNodes, this._nodes]).pipe(map(([keyManagerNodes, renderNodes]) => keyManagerNodes.reduce((items, data) => { const node = renderNodes.get(this._getExpansionKey(data)); if (node) { items.push(node); } return items; }, []))); const keyManagerOptions = { trackBy: node => this._getExpansionKey(node.data), skipPredicate: node => !!node.isDisabled, typeAheadDebounceInterval: true, horizontalOrientation: this._dir.value }; this._keyManager = this._keyManagerFactory(items, keyManagerOptions); } _initializeDataDiffer() { const trackBy = this.trackBy ?? ((_index, item) => this._getExpansionKey(item)); this._dataDiffer = this._differs.find([]).create(trackBy); } _checkTreeControlUsage() { if (typeof ngDevMode === 'undefined' || ngDevMode) { let numTreeControls = 0; if (this.treeControl) { numTreeControls++; } if (this.levelAccessor) { numTreeControls++; } if (this.childrenAccessor) { numTreeControls++; } if (!numTreeControls) { throw getTreeControlMissingError(); } else if (numTreeControls > 1) { throw getMultipleTreeControlsError(); } } } renderNodeChanges(data, dataDiffer = this._dataDiffer, viewContainer = this._nodeOutlet.viewContainer, parentData) { const changes = dataDiffer.diff(data); if (!changes && !this._viewInit) { return; } changes?.forEachOperation((item, adjustedPreviousIndex, currentIndex) => { if (item.previousIndex == null) { this.insertNode(data[currentIndex], currentIndex, viewContainer, parentData); } else if (currentIndex == null) { viewContainer.remove(adjustedPreviousIndex); } else { const view = viewContainer.get(adjustedPreviousIndex); viewContainer.move(view, currentIndex); } }); changes?.forEachIdentityChange(record => { const newData = record.item; if (record.currentIndex != undefined) { const view = viewContainer.get(record.currentIndex); view.context.$implicit = newData; } }); if (parentData) { this._changeDetectorRef.markForCheck(); } else { this._changeDetectorRef.detectChanges(); } } _getNodeDef(data, i) { if (this._nodeDefs.length === 1) { return this._nodeDefs.first; } const nodeDef = this._nodeDefs.find(def => def.when && def.when(i, data)) || this._defaultNodeDef; if (!nodeDef && (typeof ngDevMode === 'undefined' || ngDevMode)) { throw getTreeMissingMatchingNodeDefError(); } return nodeDef; } insertNode(nodeData, index, viewContainer, parentData) { const levelAccessor = this._getLevelAccessor(); const node = this._getNodeDef(nodeData, index); const key = this._getExpansionKey(nodeData); const context = new CdkTreeNodeOutletContext(nodeData); context.index = index; parentData ??= this._parents.get(key) ?? undefined; if (levelAccessor) { context.level = levelAccessor(nodeData); } else if (parentData !== undefined && this._levels.has(this._getExpansionKey(parentData))) { context.level = this._levels.get(this._getExpansionKey(parentData)) + 1; } else { context.level = 0; } this._levels.set(key, context.level); const container = viewContainer ? viewContainer : this._nodeOutlet.viewContainer; container.createEmbeddedView(node.template, context, index); if (CdkTreeNode.mostRecentTreeNode) { CdkTreeNode.mostRecentTreeNode.data = nodeData; } } isExpanded(dataNode) { return !!(this.treeControl?.isExpanded(dataNode) || this._expansionModel?.isSelected(this._getExpansionKey(dataNode))); } toggle(dataNode) { if (this.treeControl) { this.treeControl.toggle(dataNode); } else if (this._expansionModel) { this._expansionModel.toggle(this._getExpansionKey(dataNode)); } } expand(dataNode) { if (this.treeControl) { this.treeControl.expand(dataNode); } else if (this._expansionModel) { this._expansionModel.select(this._getExpansionKey(dataNode)); } } collapse(dataNode) { if (this.treeControl) { this.treeControl.collapse(dataNode); } else if (this._expansionModel) { this._expansionModel.deselect(this._getExpansionKey(dataNode)); } } toggleDescendants(dataNode) { if (this.treeControl) { this.treeControl.toggleDescendants(dataNode); } else if (this._expansionModel) { if (this.isExpanded(dataNode)) { this.collapseDescendants(dataNode); } else { this.expandDescendants(dataNode); } } } expandDescendants(dataNode) { if (this.treeControl) { this.treeControl.expandDescendants(dataNode); } else if (this._expansionModel) { const expansionModel = this._expansionModel; expansionModel.select(this._getExpansionKey(dataNode)); this._getDescendants(dataNode).pipe(take(1), takeUntil(this._onDestroy)).subscribe(children => { expansionModel.select(...children.map(child => this._getExpansionKey(child))); }); } } collapseDescendants(dataNode) { if (this.treeControl) { this.treeControl.collapseDescendants(dataNode); } else if (this._expansionModel) { const expansionModel = this._expansionModel; expansionModel.deselect(this._getExpansionKey(dataNode)); this._getDescendants(dataNode).pipe(take(1), takeUntil(this._onDestroy)).subscribe(children => { expansionModel.deselect(...children.map(child => this._getExpansionKey(child))); }); } } expandAll() { if (this.treeControl) { this.treeControl.expandAll(); } else if (this._expansionModel) { this._forEachExpansionKey(keys => this._expansionModel?.select(...keys)); } } collapseAll() { if (this.treeControl) { this.treeControl.collapseAll(); } else if (this._expansionModel) { this._forEachExpansionKey(keys => this._expansionModel?.deselect(...keys)); } } _getLevelAccessor() { return this.treeControl?.getLevel?.bind(this.treeControl) ?? this.levelAccessor; } _getChildrenAccessor() { return this.treeControl?.getChildren?.bind(this.treeControl) ?? this.childrenAccessor; } _getDirectChildren(dataNode) { const levelAccessor = this._getLevelAccessor(); const expansionModel = this._expansionModel ?? this.treeControl?.expansionModel; if (!expansionModel) { return of([]); } const key = this._getExpansionKey(dataNode); const isExpanded = expansionModel.changed.pipe(switchMap(changes => { if (changes.added.includes(key)) { return of(true); } else if (changes.removed.includes(key)) { return of(false); } return EMPTY; }), startWith(this.isExpanded(dataNode))); if (levelAccessor) { return combineLatest([isExpanded, this._flattenedNodes]).pipe(map(([expanded, flattenedNodes]) => { if (!expanded) { return []; } return this._findChildrenByLevel(levelAccessor, flattenedNodes, dataNode, 1); })); } const childrenAccessor = this._getChildrenAccessor(); if (childrenAccessor) { return coerceObservable(childrenAccessor(dataNode) ?? []); } throw getTreeControlMissingError(); } _findChildrenByLevel(levelAccessor, flattenedNodes, dataNode, levelDelta) { const key = this._getExpansionKey(dataNode); const startIndex = flattenedNodes.findIndex(node => this._getExpansionKey(node) === key); const dataNodeLevel = levelAccessor(dataNode); const expectedLevel = dataNodeLevel + levelDelta; const results = []; for (let i = startIndex + 1; i < flattenedNodes.length; i++) { const currentLevel = levelAccessor(flattenedNodes[i]); if (currentLevel <= dataNodeLevel) { break; } if (currentLevel <= expectedLevel) { results.push(flattenedNodes[i]); } } return results; } _registerNode(node) { this._nodes.value.set(this._getExpansionKey(node.data), node); this._nodes.next(this._nodes.value); } _unregisterNode(node) { this._nodes.value.delete(this._getExpansionKey(node.data)); this._nodes.next(this._nodes.value); } _getLevel(node) { return this._levels.get(this._getExpansionKey(node)); } _getSetSize(dataNode) { const set = this._getAriaSet(dataNode); return set.length; } _getPositionInSet(dataNode) { const set = this._getAriaSet(dataNode); const key = this._getExpansionKey(dataNode); return set.findIndex(node => this._getExpansionKey(node) === key) + 1; } _getNodeParent(node) { const parent = this._parents.get(this._getExpansionKey(node.data)); return parent && this._nodes.value.get(this._getExpansionKey(parent)); } _getNodeChildren(node) { return this._getDirectChildren(node.data).pipe(map(children => children.reduce((nodes, child) => { const value = this._nodes.value.get(this._getExpansionKey(child)); if (value) { nodes.push(value); } return nodes; }, []))); } _sendKeydownToKeyManager(event) { if (event.target === this._elementRef.nativeElement) { this._keyManager.onKeydown(event); } else { const nodes = this._nodes.getValue(); for (const [, node] of nodes) { if (event.target === node._elementRef.nativeElement) { this._keyManager.onKeydown(event); break; } } } } _getDescendants(dataNode) { if (this.treeControl) { return of(this.treeControl.getDescendants(dataNode)); } if (this.levelAccessor) { const results = this._findChildrenByLevel(this.levelAccessor, this._flattenedNodes.value, dataNode, Infinity); return of(results); } if (this.childrenAccessor) { return this._getAllChildrenRecursively(dataNode).pipe(reduce((allChildren, nextChildren) => { allChildren.push(...nextChildren); return allChildren; }, [])); } throw getTreeControlMissingError(); } _getAllChildrenRecursively(dataNode) { if (!this.childrenAccessor) { return of([]); } return coerceObservable(this.childrenAccessor(dataNode)).pipe(take(1), switchMap(children => { for (const child of children) { this._parents.set(this._getExpansionKey(child), dataNode); } return of(...children).pipe(concatMap(child => concat(of([child]), this._getAllChildrenRecursively(child)))); })); } _getExpansionKey(dataNode) { return this.expansionKey?.(dataNode) ?? dataNode; } _getAriaSet(node) { const key = this._getExpansionKey(node); const parent = this._parents.get(key); const parentKey = parent ? this._getExpansionKey(parent) : null; const set = this._ariaSets.get(parentKey); return set ?? [node]; } _findParentForNode(node, index, cachedNodes) { if (!cachedNodes.length) { return null; } const currentLevel = this._levels.get(this._getExpansionKey(node)) ?? 0; for (let parentIndex = index - 1; parentIndex >= 0; parentIndex--) { const parentNode = cachedNodes[parentIndex]; const parentLevel = this._levels.get(this._getExpansionKey(parentNode)) ?? 0; if (parentLevel < currentLevel) { return parentNode; } } return null; } _flattenNestedNodesWithExpansion(nodes, level = 0) { const childrenAccessor = this._getChildrenAccessor(); if (!childrenAccessor) { return of([...nodes]); } return of(...nodes).pipe(concatMap(node => { const parentKey = this._getExpansionKey(node); if (!this._parents.has(parentKey)) { this._parents.set(parentKey, null); } this._levels.set(parentKey, level); const children = coerceObservable(childrenAccessor(node)); return concat(of([node]), children.pipe(take(1), tap(childNodes => { this._ariaSets.set(parentKey, [...(childNodes ?? [])]); for (const child of childNodes ?? []) { const childKey = this._getExpansionKey(child); this._parents.set(childKey, node); this._levels.set(childKey, level + 1); } }), switchMap(childNodes => { if (!childNodes) { return of([]); } return this._flattenNestedNodesWithExpansion(childNodes, level + 1).pipe(map(nestedNodes => this.isExpanded(node) ? nestedNodes : [])); }))); }), reduce((results, children) => { results.push(...children); return results; }, [])); } _computeRenderingData(nodes, nodeType) { if (this.childrenAccessor && nodeType === 'flat') { this._clearPreviousCache(); this._ariaSets.set(null, [...nodes]); return this._flattenNestedNodesWithExpansion(nodes).pipe(map(flattenedNodes => ({ renderNodes: flattenedNodes, flattenedNodes }))); } else if (this.levelAccessor && nodeType === 'nested') { const levelAccessor = this.levelAccessor; return of(nodes.filter(node => levelAccessor(node) === 0)).pipe(map(rootNodes => ({ renderNodes: rootNodes, flattenedNodes: nodes })), tap(({ flattenedNodes }) => { this._calculateParents(flattenedNodes); })); } else if (nodeType === 'flat') { return of({ renderNodes: nodes, flattenedNodes: nodes }).pipe(tap(({ flattenedNodes }) => { this._calculateParents(flattenedNodes); })); } else { this._clearPreviousCache(); this._ariaSets.set(null, [...nodes]); return this._flattenNestedNodesWithExpansion(nodes).pipe(map(flattenedNodes => ({ renderNodes: nodes, flattenedNodes }))); } } _updateCachedData(flattenedNodes) { this._flattenedNodes.next(flattenedNodes); } _updateKeyManagerItems(flattenedNodes) { this._keyManagerNodes.next(flattenedNodes); } _calculateParents(flattenedNodes) { const levelAccessor = this._getLevelAccessor(); if (!levelAccessor) { return; } this._clearPreviousCache(); for (let index = 0; index < flattenedNodes.length; index++) { const dataNode = flattenedNodes[index]; const key = this._getExpansionKey(dataNode); this._levels.set(key, levelAccessor(dataNode)); const parent = this._findParentForNode(dataNode, index, flattenedNodes); this._parents.set(key, parent); const parentKey = parent ? this._getExpansionKey(parent) : null; const group = this._ariaSets.get(parentKey) ?? []; group.splice(index, 0, dataNode); this._ariaSets.set(parentKey, group); } } _forEachExpansionKey(callback) { const toToggle = []; const observables = []; this._nodes.value.forEach(node => { toToggle.push(this._getExpansionKey(node.data)); observables.push(this._getDescendants(node.data)); }); if (observables.length > 0) { combineLatest(observables).pipe(take(1), takeUntil(this._onDestroy)).subscribe(results => { results.forEach(inner => inner.forEach(r => toToggle.push(this._getExpansionKey(r)))); callback(toToggle); }); } else { callback(toToggle); } } _clearPreviousCache() { this._parents.clear(); this._levels.clear(); this._ariaSets.clear(); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: CdkTree, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.0", type: CdkTree, isStandalone: true, selector: "cdk-tree", inputs: { dataSource: "dataSource", treeControl: "treeControl", levelAccessor: "levelAccessor", childrenAccessor: "childrenAccessor", trackBy: "trackBy", expansionKey: "expansionKey" }, host: { attributes: { "role": "tree" }, listeners: { "keydown": "_sendKeydownToKeyManager($event)" }, classAttribute: "cdk-tree" }, queries: [{ propertyName: "_nodeDefs", predicate: CdkTreeNodeDef, descendants: true }], viewQueries: [{ propertyName: "_nodeOutlet", first: true, predicate: CdkTreeNodeOutlet, descendants: true, static: true }], exportAs: ["cdkTree"], ngImport: i0, template: `<ng-container cdkTreeNodeOutlet></ng-container>`, isInline: true, dependencies: [{ kind: "directive", type: CdkTreeNodeOutlet, selector: "[cdkTreeNodeOutlet]" }], changeDetection: i0.ChangeDetectionStrategy.Default, encapsulation: i0.ViewEncapsulation.None }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: CdkTree, decorators: [{ type: Component, args: [{ selector: 'cdk-tree', exportAs: 'cdkTree', template: `<ng-container cdkTreeNodeOutlet></ng-container>`, host: { 'class': 'cdk-tree', 'role': 'tree', '(keydown)': '_sendKeydownToKeyManager($event)' }, encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.Default, imports: [CdkTreeNodeOutlet] }] }], ctorParameters: () => [], propDecorators: { dataSource: [{ type: Input }], treeControl: [{ type: Input }], levelAccessor: [{ type: Input }], childrenAccessor: [{ type: Input }], trackBy: [{ type: Input }], expansionKey: [{ type: Input }], _nodeOutlet: [{ type: ViewChild, args: [CdkTreeNodeOutlet, { static: true }] }], _nodeDefs: [{ type: ContentChildren, args: [CdkTreeNodeDef, { descendants: true }] }] } }); class CdkTreeNode { _elementRef = inject(ElementRef); _tree = inject(CdkTree); _tabindex = -1; _type = 'flat'; get role() { return 'treeitem'; } set role(_role) {} get isExpandable() { return this._isExpandable(); } set isExpandable(isExpandable) { this._inputIsExpandable = isExpandable; if (this.data && !this._isExpandable || !this._inputIsExpandable) { return; } if (this._inputIsExpanded) { this.expand(); } else if (this._inputIsExpanded === false) { this.collapse(); } } get isExpanded() { return this._tree.isExpanded(this._data); } set isExpanded(isExpanded) { this._inputIsExpanded = isExpanded; if (isExpanded) { this.expand(); } else { this.collapse(); } } isDisabled; typeaheadLabel; getLabel() { return this.typeaheadLabel || this._elementRef.nativeElement.textContent?.trim() || ''; } activation = new EventEmitter(); expandedChange = new EventEmitter(); static mostRecentTreeNode = null; _destroyed = new Subject(); _dataChanges = new Subject(); _inputIsExpandable = false; _inputIsExpanded = undefined; _shouldFocus = true; _parentNodeAriaLevel; get data() { return this._data; } set data(value) { if (value !== this._data) { this._data = value; this._dataChanges.next(); } } _data; get isLeafNode() { if (this._tree.treeControl?.isExpandable !== undefined && !this._tree.treeControl.isExpandable(this._data)) { return true; } else if (this._tree.treeControl?.isExpandable === undefined && this._tree.treeControl?.getDescendants(this._data).length === 0) { return true; } return false; } get level() { return this._tree._getLevel(this._data) ?? this._parentNodeAriaLevel; } _isExpandable() { if (this._tree.treeControl) { if (this.isLeafNode) { return false; } return true; } return this._inputIsExpandable; } _getAriaExpanded() { if (!this._isExpandable()) { return null; } return String(this.isExpanded); } _getSetSize() { return this._tree._getSetSize(this._data); } _getPositionInSet() { return this._tree._getPositionInSet(this._data); } _changeDetectorRef = inject(ChangeDetectorRef); constructor() { CdkTreeNode.mostRecentTreeNode = this; } ngOnInit() { this._parentNodeAriaLevel = getParentNodeAriaLevel(this._elementRef.nativeElement); this._tree._getExpansionModel().changed.pipe(map(() => this.isExpanded), distinctUntilChanged(), takeUntil(this._destroyed)).pipe(takeUntil(this._destroyed)).subscribe(() => this._changeDetectorRef.markForCheck()); this._tree._setNodeTypeIfUnset(this._type); this._tree._registerNode(this); } ngOnDestroy() { if (CdkTreeNode.mostRecentTreeNode === this) { CdkTreeNode.mostRecentTreeNode = null; } this._dataChanges.complete(); this._destroyed.next(); this._destroyed.complete(); } getParent() { return this._tree._getNodeParent(this) ?? null; } getChildren() { return this._tree._getNodeChildren(this); } focus() { this._tabindex = 0; if (this._shouldFocus) { this._elementRef.nativeElement.focus(); } this._changeDetectorRef.markForCheck(); } unfocus() { this._tabindex = -1; this._changeDetectorRef.markForCheck(); } activate() { if (this.isDisabled) { return; } this.activation.next(this._data); } collapse() { if (this.isExpandable) { this._tree.collapse(this._data); } } expand() { if (this.isExpandable) { this._tree.expand(this._data); } } makeFocusable() { this._tabindex = 0; this._changeDetectorRef.markForCheck(); } _focusItem() { if (this.isDisabled) { return; } this._tree._keyManager.focusItem(this); } _setActiveItem() { if (this.isDisabled) { return; } this._shouldFocus = false; this._tree._keyManager.focusItem(this); this._shouldFocus = true; } _emitExpansionState(expanded) { this.expandedChange.emit(expanded); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: CdkTreeNode, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "16.1.0", version: "21.0.0", type: CdkTreeNode, isStandalone: true, selector: "cdk-tree-node", inputs: { role: "role", isExpandable: ["isExpandable", "isExpandable", booleanAttribute], isExpanded: "isExpanded", isDisabled: ["isDisabled", "isDisabled", booleanAttribute], typeaheadLabel: ["cdkTreeNodeTypeaheadLabel", "typeaheadLabel"] }, outputs: { activation: "activation", expandedChange: "expandedChange" }, host: { attributes: { "role": "treeitem" }, listeners: { "click": "_setActiveItem()", "focus": "_focusItem()" }, properties: { "attr.aria-expanded": "_getAriaExpanded()", "attr.aria-level": "level + 1", "attr.aria-posinset": "_getPositionInSet()", "attr.aria-setsize": "_getSetSize()", "tabindex": "_tabindex" }, classAttribute: "cdk-tree-node" }, exportAs: ["cdkTreeNode"], ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: CdkTreeNode, decorators: [{ type: Directive, args: [{ selector: 'cdk-tree-node', exportAs: 'cdkTreeNode', host: { 'class': 'cdk-tree-node', '[attr.aria-expanded]': '_getAriaExpanded()', '[attr.aria-level]': 'level + 1', '[attr.aria-posinset]': '_getPositionInSet()', '[attr.aria-setsize]': '_getSetSize()', '[tabindex]': '_tabindex', 'role': 'treeitem', '(click)': '_setActiveItem()', '(focus)': '_focusItem()' } }] }], ctorParameters: () => [], propDecorators: { role: [{ type: Input }], isExpandable: [{ type: Input, args: [{ transform: booleanAttribute }] }], isExpanded: [{ type: Input }], isDisabled: [{ type: Input, args: [{ transform: booleanAttribute }] }], typeaheadLabel: [{ type: Input, args: ['cdkTreeNodeTypeaheadLabel'] }], activation: [{ type: Output }], expandedChange: [{ type: Output }] } }); function getParentNodeAriaLevel(nodeElement) { let parent = nodeElement.parentElement; while (parent && !isNodeElement(parent)) { parent = parent.parentElement; } if (!parent) { if (typeof ngDevMode === 'undefined' || ngDevMode) { throw Error('Incorrect tree structure containing detached node.'); } else { return -1; } } else if (parent.classList.contains('cdk-nested-tree-node')) { return numberAttribute(parent.getAttribute('aria-level')); } else { return 0; } } function isNodeElement(element) { const classList = element.classList; return !!(classList?.contains('cdk-nested-tree-node') || classList?.contains('cdk-tree')); } class CdkNestedTreeNode extends CdkTreeNode { _type = 'nested'; _differs = inject(IterableDiffers); _dataDiffer; _children; nodeOutlet; constructor() { super(); } ngAfterContentInit() { this._dataDiffer = this._differs.find([]).create(this._tree.trackBy); this._tree._getDirectChildren(this.data).pipe(takeUntil(this._destroyed)).subscribe(result => this.updateChildrenNodes(result)); this.nodeOutlet.changes.pipe(takeUntil(this._destroyed)).subscribe(() => this.updateChildrenNodes()); } ngOnDestroy() { this._clear(); super.ngOnDestroy(); } updateChildrenNodes(children) { const outlet = this._getNodeOutlet(); if (children) { this._children = children; } if (outlet && this._children) { const viewContainer = outlet.viewContainer; this._tree.renderNodeChanges(this._children, this._dataDiffer, viewContainer, this._data); } else { this._dataDiffer.diff([]); } } _clear() { const outlet = this._getNodeOutlet(); if (outlet) { outlet.viewContainer.clear(); this._dataDiffer.diff([]); } } _getNodeOutlet() { const outlets = this.nodeOutlet; return outlets && outlets.find(outlet => !outlet._node || outlet._node === this); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: CdkNestedTreeNode, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.0", type: CdkNestedTreeNode, isStandalone: true, selector: "cdk-nested-tree-node", host: { classAttribute: "cdk-nested-tree-node" }, providers: [{ provide: CdkTreeNode, useExisting: CdkNestedTreeNode }, { provide: CDK_TREE_NODE_OUTLET_NODE, useExisting: CdkNestedTreeNode }], queries: [{ propertyName: "nodeOutlet", predicate: CdkTreeNodeOutlet, descendants: true }], exportAs: ["cdkNestedTreeNode"], usesInheritance: true, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: CdkNestedTreeNode, decorators: [{ type: Directive, args: [{ selector: 'cdk-nested-tree-node', exportAs: 'cdkNestedTreeNode', providers: [{ provide: CdkTreeNode, useExisting: CdkNestedTreeNode }, { provide: CDK_TREE_NODE_OUTLET_NODE, useExisting: CdkNestedTreeNode }], host: { 'class': 'cdk-nested-tree-node' } }] }], ctorParameters: () => [], propDecorators: { nodeOutlet: [{ type: ContentChildren, args: [CdkTreeNodeOutlet, { descendants: true }] }] } }); const cssUnitPattern = /([A-Za-z%]+)$/; class CdkTreeNodePadding { _treeNode = inject(CdkTreeNode); _tree = inject(CdkTree); _element = inject(ElementRef); _dir = inject(Directionality, { optional: true }); _currentPadding; _destroyed = new Subject(); indentUnits = 'px'; get level() { return this._level; } set level(value) { this._setLevelInput(value); } _level; get indent() { return this._indent; } set indent(indent) { this._setIndentInput(indent); } _indent = 40; constructor() { this._setPadding(); this._dir?.change.pipe(takeUntil(this._destroyed)).subscribe(() => this._setPadding(true)); this._treeNode._dataChanges.subscribe(() => this._setPadding()); } ngOnDestroy() { this._destroyed.next(); this._destroyed.complete(); } _paddingIndent() { const nodeLevel = (this._treeNode.data && this._tree._getLevel(this._treeNode.data)) ?? null; const level = this._level == null ? nodeLevel : this._level; return typeof level === 'number' ? `${level * this._indent}${this.indentUnits}` : null; } _setPadding(forceChange = false) { const padding = this._paddingIndent(); if (padding !== this._currentPadding || forceChange) { const element = this._element.nativeElement; const paddingProp = this._dir && this._dir.value === 'rtl' ? 'paddingRight' : 'paddingLeft'; const resetProp = paddingProp === 'paddingLeft' ? 'paddingRight' : 'paddingLeft'; element.style[paddingProp] = padding || ''; element.style[resetProp] = ''; this._currentPadding = padding; } } _setLevelInput(value) { this._level = isNaN(value) ? null : value; this._setPadding(); } _setIndentInput(indent) { let value = indent; let units = 'px'; if (typeof indent === 'string') { const parts = indent.split(cssUnitPattern); value = parts[0]; units = parts[1] || units; } this.indentUnits = units; this._indent = numberAttribute(value); this._setPadding(); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: CdkTreeNodePadding, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "16.1.0", version: "21.0.0", type: CdkTreeNodePadding, isStandalone: true, selector: "[cdkTreeNodePadding]", inputs: { level: ["cdkTreeNodePadding", "level", numberAttribute], indent: ["cdkTreeNodePaddingIndent", "indent"] }, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: CdkTreeNodePadding, decorators: [{ type: Directive, args: [{ selector: '[cdkTreeNodePadding]' }] }], ctorParameters: () => [], propDecorators: { level: [{ type: Input, args: [{ alias: 'cdkTreeNodePadding', transform: numberAttribute }] }], indent: [{ type: Input, args: ['cdkTreeNodePaddingIndent'] }] } }); class CdkTreeNodeToggle { _tree = inject(CdkTree); _treeNode = inject(CdkTreeNode); recursive = false; constructor() {} _toggle() { this.recursive ? this._tree.toggleDescendants(this._treeNode.data) : this._tree.toggle(this._treeNode.data); this._tree._keyManager.focusItem(this._treeNode); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: CdkTreeNodeToggle, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "16.1.0", version: "21.0.0", type: CdkTreeNodeToggle, isStandalone: true, selector: "[cdkTreeNodeToggle]", inputs: { recursive: ["cdkTreeNodeToggleRecursive", "recursive", booleanAttribute] }, host: { attributes: { "tabindex": "-1" }, listeners: { "click": "_toggle(); $event.stopPropagation();", "keydown.Enter": "_toggle(); $event.preventDefault();", "keydown.Space": "_toggle(); $event.preventDefault();" } }, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: CdkTreeNodeToggle, decorators: [{ type: Directive, args: [{ selector: '[cdkTreeNodeToggle]', host: { '(click)': '_toggle(); $event.stopPropagation();', '(keydown.Enter)': '_toggle(); $event.preventDefault();', '(keydown.Space)': '_toggle(); $event.preventDefault();', 'tabindex': '-1' } }] }], ctorParameters: () => [], propDecorators: { recursive: [{ type: Input, args: [{ alias: 'cdkTreeNodeToggleRecursive', transform: booleanAttribute }] }] } }); const EXPORTED_DECLARATIONS = [CdkNestedTreeNode, CdkTreeNodeDef, CdkTreeNodePadding, CdkTreeNodeToggle, CdkTree, CdkTreeNode, CdkTreeNodeOutlet]; class CdkTreeModule { static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: CdkTreeModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.0.0", ngImport: i0, type: CdkTreeModule, imports: [CdkNestedTreeNode, CdkTreeNodeDef, CdkTreeNodePadding, CdkTreeNodeToggle, CdkTree, CdkTreeNode, CdkTreeNodeOutlet], exports: [CdkNestedTreeNode, CdkTreeNodeDef, CdkTreeNodePadding, CdkTreeNodeToggle, CdkTree, CdkTreeNode, CdkTreeNodeOutlet] }); static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: CdkTreeModule }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: CdkTreeModule, decorators: [{ type: NgModule, args: [{ imports: EXPORTED_DECLARATIONS, exports: EXPORTED_DECLARATIONS }] }] }); export { BaseTreeControl, CDK_TREE_NODE_OUTLET_NODE, CdkNestedTreeNode, CdkTree, CdkTreeModule, CdkTreeNode, CdkTreeNodeDef, CdkTreeNodeOutlet, CdkTreeNodeOutletContext, CdkTreeNodePadding, CdkTreeNodeToggle, FlatTreeControl, NestedTreeControl, getMultipleTreeControlsError, getTreeControlMissingError, getTreeMissingMatchingNodeDefError, getTreeMultipleDefaultNodeDefsError, getTreeNoValidDataSourceError }; //# sourceMappingURL=tree.mjs.map