| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867 |
- import { SelectionModel, isDataSource } from '@angular/cdk/collections';
- import { isObservable, Subject, BehaviorSubject, of } from 'rxjs';
- import { take, filter, takeUntil } from 'rxjs/operators';
- import * as i0 from '@angular/core';
- import { InjectionToken, Directive, Inject, Optional, Component, ViewEncapsulation, ChangeDetectionStrategy, Input, ViewChild, ContentChildren, NgModule } from '@angular/core';
- import { coerceNumberProperty, coerceBooleanProperty } from '@angular/cdk/coercion';
- import * as i2 from '@angular/cdk/bidi';
- /** Base tree control. It has basic toggle/expand/collapse operations on a single data node. */
- class BaseTreeControl {
- constructor() {
- /** A selection model with multi-selection to track expansion status. */
- this.expansionModel = new SelectionModel(true);
- }
- /** Toggles one single data node's expanded/collapsed state. */
- toggle(dataNode) {
- this.expansionModel.toggle(this._trackByValue(dataNode));
- }
- /** Expands one single data node. */
- expand(dataNode) {
- this.expansionModel.select(this._trackByValue(dataNode));
- }
- /** Collapses one single data node. */
- collapse(dataNode) {
- this.expansionModel.deselect(this._trackByValue(dataNode));
- }
- /** Whether a given data node is expanded or not. Returns true if the data node is expanded. */
- isExpanded(dataNode) {
- return this.expansionModel.isSelected(this._trackByValue(dataNode));
- }
- /** Toggles a subtree rooted at `node` recursively. */
- toggleDescendants(dataNode) {
- this.expansionModel.isSelected(this._trackByValue(dataNode))
- ? this.collapseDescendants(dataNode)
- : this.expandDescendants(dataNode);
- }
- /** Collapse all dataNodes in the tree. */
- collapseAll() {
- this.expansionModel.clear();
- }
- /** Expands a subtree rooted at given data node recursively. */
- expandDescendants(dataNode) {
- let toBeProcessed = [dataNode];
- toBeProcessed.push(...this.getDescendants(dataNode));
- this.expansionModel.select(...toBeProcessed.map(value => this._trackByValue(value)));
- }
- /** Collapses a subtree rooted at given data node recursively. */
- 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;
- }
- }
- /** Flat tree control. Able to expand/collapse a subtree recursively for flattened tree. */
- class FlatTreeControl extends BaseTreeControl {
- /** Construct with flat tree data node functions getLevel and isExpandable. */
- constructor(getLevel, isExpandable, options) {
- super();
- this.getLevel = getLevel;
- this.isExpandable = isExpandable;
- this.options = options;
- if (this.options) {
- this.trackBy = this.options.trackBy;
- }
- }
- /**
- * Gets a list of the data node's subtree of descendent data nodes.
- *
- * To make this working, the `dataNodes` of the TreeControl must be flattened tree nodes
- * with correct levels.
- */
- getDescendants(dataNode) {
- const startIndex = this.dataNodes.indexOf(dataNode);
- const results = [];
- // Goes through flattened tree nodes in the `dataNodes` array, and get all descendants.
- // The level of descendants of a tree node must be greater than the level of the given
- // tree node.
- // If we reach a node whose level is equal to the level of the tree node, we hit a sibling.
- // If we reach a node whose level is greater than the level of the tree node, we hit a
- // sibling of an ancestor.
- 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;
- }
- /**
- * Expands all data nodes in the tree.
- *
- * To make this working, the `dataNodes` variable of the TreeControl must be set to all flattened
- * data nodes of the tree.
- */
- expandAll() {
- this.expansionModel.select(...this.dataNodes.map(node => this._trackByValue(node)));
- }
- }
- /** Nested tree control. Able to expand/collapse a subtree recursively for NestedNode type. */
- class NestedTreeControl extends BaseTreeControl {
- /** Construct with nested tree function getChildren. */
- constructor(getChildren, options) {
- super();
- this.getChildren = getChildren;
- this.options = options;
- if (this.options) {
- this.trackBy = this.options.trackBy;
- }
- }
- /**
- * Expands all dataNodes in the tree.
- *
- * To make this working, the `dataNodes` variable of the TreeControl must be set to all root level
- * data nodes of the tree.
- */
- 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)));
- }
- /** Gets a list of descendant dataNodes of a subtree rooted at given data node recursively. */
- getDescendants(dataNode) {
- const descendants = [];
- this._getDescendants(descendants, dataNode);
- // Remove the node itself
- return descendants.splice(1);
- }
- /** A helper function to get descendants recursively. */
- _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)) {
- // TypeScript as of version 3.5 doesn't seem to treat `Boolean` like a function that
- // returns a `boolean` specifically in the context of `filter`, so we manually clarify that.
- childrenNodes.pipe(take(1), filter(Boolean)).subscribe(children => {
- for (const child of children) {
- this._getDescendants(descendants, child);
- }
- });
- }
- }
- }
- /**
- * Injection token used to provide a `CdkTreeNode` to its outlet.
- * Used primarily to avoid circular imports.
- * @docs-private
- */
- const CDK_TREE_NODE_OUTLET_NODE = new InjectionToken('CDK_TREE_NODE_OUTLET_NODE');
- /**
- * Outlet for nested CdkNode. Put `[cdkTreeNodeOutlet]` on a tag to place children dataNodes
- * inside the outlet.
- */
- class CdkTreeNodeOutlet {
- constructor(viewContainer, _node) {
- this.viewContainer = viewContainer;
- this._node = _node;
- }
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkTreeNodeOutlet, deps: [{ token: i0.ViewContainerRef }, { token: CDK_TREE_NODE_OUTLET_NODE, optional: true }], target: i0.ɵɵFactoryTarget.Directive }); }
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: CdkTreeNodeOutlet, selector: "[cdkTreeNodeOutlet]", ngImport: i0 }); }
- }
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkTreeNodeOutlet, decorators: [{
- type: Directive,
- args: [{
- selector: '[cdkTreeNodeOutlet]',
- }]
- }], ctorParameters: function () { return [{ type: i0.ViewContainerRef }, { type: undefined, decorators: [{
- type: Inject,
- args: [CDK_TREE_NODE_OUTLET_NODE]
- }, {
- type: Optional
- }] }]; } });
- /** Context provided to the tree node component. */
- class CdkTreeNodeOutletContext {
- constructor(data) {
- this.$implicit = data;
- }
- }
- /**
- * Data node definition for the CdkTree.
- * Captures the node's template and a when predicate that describes when this node should be used.
- */
- class CdkTreeNodeDef {
- /** @docs-private */
- constructor(template) {
- this.template = template;
- }
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkTreeNodeDef, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); }
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: CdkTreeNodeDef, selector: "[cdkTreeNodeDef]", inputs: { when: ["cdkTreeNodeDefWhen", "when"] }, ngImport: i0 }); }
- }
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkTreeNodeDef, decorators: [{
- type: Directive,
- args: [{
- selector: '[cdkTreeNodeDef]',
- inputs: ['when: cdkTreeNodeDefWhen'],
- }]
- }], ctorParameters: function () { return [{ type: i0.TemplateRef }]; } });
- /**
- * Returns an error to be thrown when there is no usable data.
- * @docs-private
- */
- function getTreeNoValidDataSourceError() {
- return Error(`A valid data source must be provided.`);
- }
- /**
- * Returns an error to be thrown when there are multiple nodes that are missing a when function.
- * @docs-private
- */
- function getTreeMultipleDefaultNodeDefsError() {
- return Error(`There can only be one default row without a when predicate function.`);
- }
- /**
- * Returns an error to be thrown when there are no matching node defs for a particular set of data.
- * @docs-private
- */
- function getTreeMissingMatchingNodeDefError() {
- return Error(`Could not find a matching node definition for the provided node data.`);
- }
- /**
- * Returns an error to be thrown when there are tree control.
- * @docs-private
- */
- function getTreeControlMissingError() {
- return Error(`Could not find a tree control for the tree.`);
- }
- /**
- * Returns an error to be thrown when tree control did not implement functions for flat/nested node.
- * @docs-private
- */
- function getTreeControlFunctionsMissingError() {
- return Error(`Could not find functions for nested/flat tree in tree control.`);
- }
- /**
- * CDK tree component that connects with a data source to retrieve data of type `T` and renders
- * dataNodes with hierarchy. Updates the dataNodes when new data is provided by the data source.
- */
- class CdkTree {
- /**
- * Provides a stream containing the latest data array to render. Influenced by the tree's
- * stream of view window (what dataNodes are currently on screen).
- * Data source can be an observable of data array, or a data array to render.
- */
- get dataSource() {
- return this._dataSource;
- }
- set dataSource(dataSource) {
- if (this._dataSource !== dataSource) {
- this._switchDataSource(dataSource);
- }
- }
- constructor(_differs, _changeDetectorRef) {
- this._differs = _differs;
- this._changeDetectorRef = _changeDetectorRef;
- /** Subject that emits when the component has been destroyed. */
- this._onDestroy = new Subject();
- /** Level of nodes */
- this._levels = new Map();
- // TODO(tinayuangao): Setup a listener for scrolling, emit the calculated view to viewChange.
- // Remove the MAX_VALUE in viewChange
- /**
- * Stream containing the latest information on what rows are being displayed on screen.
- * Can be used by the data source to as a heuristic of what data should be provided.
- */
- this.viewChange = new BehaviorSubject({
- start: 0,
- end: Number.MAX_VALUE,
- });
- }
- ngOnInit() {
- this._dataDiffer = this._differs.find([]).create(this.trackBy);
- if (!this.treeControl && (typeof ngDevMode === 'undefined' || ngDevMode)) {
- throw getTreeControlMissingError();
- }
- }
- ngOnDestroy() {
- this._nodeOutlet.viewContainer.clear();
- 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;
- }
- }
- ngAfterContentChecked() {
- const defaultNodeDefs = this._nodeDefs.filter(def => !def.when);
- if (defaultNodeDefs.length > 1 && (typeof ngDevMode === 'undefined' || ngDevMode)) {
- throw getTreeMultipleDefaultNodeDefsError();
- }
- this._defaultNodeDef = defaultNodeDefs[0];
- if (this.dataSource && this._nodeDefs && !this._dataSubscription) {
- this._observeRenderChanges();
- }
- }
- // TODO(tinayuangao): Work on keyboard traversal and actions, make sure it's working for RTL
- // and nested trees.
- /**
- * Switch to the provided data source by resetting the data and unsubscribing from the current
- * render change subscription if one exists. If the data source is null, interpret this by
- * clearing the node outlet. Otherwise start listening for new data.
- */
- _switchDataSource(dataSource) {
- if (this._dataSource && typeof this._dataSource.disconnect === 'function') {
- this.dataSource.disconnect(this);
- }
- if (this._dataSubscription) {
- this._dataSubscription.unsubscribe();
- this._dataSubscription = null;
- }
- // Remove the all dataNodes if there is now no data source
- if (!dataSource) {
- this._nodeOutlet.viewContainer.clear();
- }
- this._dataSource = dataSource;
- if (this._nodeDefs) {
- this._observeRenderChanges();
- }
- }
- /** Set up a subscription for the data provided by the data source. */
- _observeRenderChanges() {
- 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) {
- this._dataSubscription = dataStream
- .pipe(takeUntil(this._onDestroy))
- .subscribe(data => this.renderNodeChanges(data));
- }
- else if (typeof ngDevMode === 'undefined' || ngDevMode) {
- throw getTreeNoValidDataSourceError();
- }
- }
- /** Check for changes made in the data and render each change (node added/removed/moved). */
- renderNodeChanges(data, dataDiffer = this._dataDiffer, viewContainer = this._nodeOutlet.viewContainer, parentData) {
- const changes = dataDiffer.diff(data);
- if (!changes) {
- return;
- }
- changes.forEachOperation((item, adjustedPreviousIndex, currentIndex) => {
- if (item.previousIndex == null) {
- this.insertNode(data[currentIndex], currentIndex, viewContainer, parentData);
- }
- else if (currentIndex == null) {
- viewContainer.remove(adjustedPreviousIndex);
- this._levels.delete(item.item);
- }
- else {
- const view = viewContainer.get(adjustedPreviousIndex);
- viewContainer.move(view, currentIndex);
- }
- });
- this._changeDetectorRef.detectChanges();
- }
- /**
- * Finds the matching node definition that should be used for this node data. If there is only
- * one node definition, it is returned. Otherwise, find the node definition that has a when
- * predicate that returns true with the data. If none return true, return the default node
- * definition.
- */
- _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;
- }
- /**
- * Create the embedded view for the data node template and place it in the correct index location
- * within the data node view container.
- */
- insertNode(nodeData, index, viewContainer, parentData) {
- const node = this._getNodeDef(nodeData, index);
- // Node context that will be provided to created embedded view
- const context = new CdkTreeNodeOutletContext(nodeData);
- // If the tree is flat tree, then use the `getLevel` function in flat tree control
- // Otherwise, use the level of parent node.
- if (this.treeControl.getLevel) {
- context.level = this.treeControl.getLevel(nodeData);
- }
- else if (typeof parentData !== 'undefined' && this._levels.has(parentData)) {
- context.level = this._levels.get(parentData) + 1;
- }
- else {
- context.level = 0;
- }
- this._levels.set(nodeData, context.level);
- // Use default tree nodeOutlet, or nested node's nodeOutlet
- const container = viewContainer ? viewContainer : this._nodeOutlet.viewContainer;
- container.createEmbeddedView(node.template, context, index);
- // Set the data to just created `CdkTreeNode`.
- // The `CdkTreeNode` created from `createEmbeddedView` will be saved in static variable
- // `mostRecentTreeNode`. We get it from static variable and pass the node data to it.
- if (CdkTreeNode.mostRecentTreeNode) {
- CdkTreeNode.mostRecentTreeNode.data = nodeData;
- }
- }
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkTree, deps: [{ token: i0.IterableDiffers }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.0.0", type: CdkTree, selector: "cdk-tree", inputs: { dataSource: "dataSource", treeControl: "treeControl", trackBy: "trackBy" }, host: { attributes: { "role": "tree" }, 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: "16.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',
- },
- encapsulation: ViewEncapsulation.None,
- // The "OnPush" status for the `CdkTree` component is effectively a noop, so we are removing it.
- // The view for `CdkTree` consists entirely of templates declared in other views. As they are
- // declared elsewhere, they are checked when their declaration points are checked.
- // tslint:disable-next-line:validate-decorators
- changeDetection: ChangeDetectionStrategy.Default,
- }]
- }], ctorParameters: function () { return [{ type: i0.IterableDiffers }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { dataSource: [{
- type: Input
- }], treeControl: [{
- type: Input
- }], trackBy: [{
- type: Input
- }], _nodeOutlet: [{
- type: ViewChild,
- args: [CdkTreeNodeOutlet, { static: true }]
- }], _nodeDefs: [{
- type: ContentChildren,
- args: [CdkTreeNodeDef, {
- // We need to use `descendants: true`, because Ivy will no longer match
- // indirect descendants if it's left as false.
- descendants: true,
- }]
- }] } });
- /**
- * Tree node for CdkTree. It contains the data in the tree node.
- */
- class CdkTreeNode {
- /**
- * The role of the tree node.
- * @deprecated The correct role is 'treeitem', 'group' should not be used. This input will be
- * removed in a future version.
- * @breaking-change 12.0.0 Remove this input
- */
- get role() {
- return 'treeitem';
- }
- set role(_role) {
- // TODO: move to host after View Engine deprecation
- this._elementRef.nativeElement.setAttribute('role', _role);
- }
- /**
- * The most recently created `CdkTreeNode`. We save it in static variable so we can retrieve it
- * in `CdkTree` and set the data to it.
- */
- static { this.mostRecentTreeNode = null; }
- /** The tree node's data. */
- get data() {
- return this._data;
- }
- set data(value) {
- if (value !== this._data) {
- this._data = value;
- this._setRoleFromData();
- this._dataChanges.next();
- }
- }
- get isExpanded() {
- return this._tree.treeControl.isExpanded(this._data);
- }
- get level() {
- // If the treeControl has a getLevel method, use it to get the level. Otherwise read the
- // aria-level off the parent node and use it as the level for this node (note aria-level is
- // 1-indexed, while this property is 0-indexed, so we don't need to increment).
- return this._tree.treeControl.getLevel
- ? this._tree.treeControl.getLevel(this._data)
- : this._parentNodeAriaLevel;
- }
- constructor(_elementRef, _tree) {
- this._elementRef = _elementRef;
- this._tree = _tree;
- /** Subject that emits when the component has been destroyed. */
- this._destroyed = new Subject();
- /** Emits when the node's data has changed. */
- this._dataChanges = new Subject();
- CdkTreeNode.mostRecentTreeNode = this;
- this.role = 'treeitem';
- }
- ngOnInit() {
- this._parentNodeAriaLevel = getParentNodeAriaLevel(this._elementRef.nativeElement);
- this._elementRef.nativeElement.setAttribute('aria-level', `${this.level + 1}`);
- }
- ngOnDestroy() {
- // If this is the last tree node being destroyed,
- // clear out the reference to avoid leaking memory.
- if (CdkTreeNode.mostRecentTreeNode === this) {
- CdkTreeNode.mostRecentTreeNode = null;
- }
- this._dataChanges.complete();
- this._destroyed.next();
- this._destroyed.complete();
- }
- /** Focuses the menu item. Implements for FocusableOption. */
- focus() {
- this._elementRef.nativeElement.focus();
- }
- // TODO: role should eventually just be set in the component host
- _setRoleFromData() {
- if (!this._tree.treeControl.isExpandable &&
- !this._tree.treeControl.getChildren &&
- (typeof ngDevMode === 'undefined' || ngDevMode)) {
- throw getTreeControlFunctionsMissingError();
- }
- this.role = 'treeitem';
- }
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkTreeNode, deps: [{ token: i0.ElementRef }, { token: CdkTree }], target: i0.ɵɵFactoryTarget.Directive }); }
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: CdkTreeNode, selector: "cdk-tree-node", inputs: { role: "role" }, host: { properties: { "attr.aria-expanded": "isExpanded" }, classAttribute: "cdk-tree-node" }, exportAs: ["cdkTreeNode"], ngImport: i0 }); }
- }
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkTreeNode, decorators: [{
- type: Directive,
- args: [{
- selector: 'cdk-tree-node',
- exportAs: 'cdkTreeNode',
- host: {
- 'class': 'cdk-tree-node',
- '[attr.aria-expanded]': 'isExpanded',
- },
- }]
- }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: CdkTree }]; }, propDecorators: { role: [{
- type: Input
- }] } });
- 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 coerceNumberProperty(parent.getAttribute('aria-level'));
- }
- else {
- // The ancestor element is the cdk-tree itself
- return 0;
- }
- }
- function isNodeElement(element) {
- const classList = element.classList;
- return !!(classList?.contains('cdk-nested-tree-node') || classList?.contains('cdk-tree'));
- }
- /**
- * Nested node is a child of `<cdk-tree>`. It works with nested tree.
- * By using `cdk-nested-tree-node` component in tree node template, children of the parent node will
- * be added in the `cdkTreeNodeOutlet` in tree node template.
- * The children of node will be automatically added to `cdkTreeNodeOutlet`.
- */
- class CdkNestedTreeNode extends CdkTreeNode {
- constructor(elementRef, tree, _differs) {
- super(elementRef, tree);
- this._differs = _differs;
- }
- ngAfterContentInit() {
- this._dataDiffer = this._differs.find([]).create(this._tree.trackBy);
- if (!this._tree.treeControl.getChildren && (typeof ngDevMode === 'undefined' || ngDevMode)) {
- throw getTreeControlFunctionsMissingError();
- }
- const childrenNodes = this._tree.treeControl.getChildren(this.data);
- if (Array.isArray(childrenNodes)) {
- this.updateChildrenNodes(childrenNodes);
- }
- else if (isObservable(childrenNodes)) {
- childrenNodes
- .pipe(takeUntil(this._destroyed))
- .subscribe(result => this.updateChildrenNodes(result));
- }
- this.nodeOutlet.changes
- .pipe(takeUntil(this._destroyed))
- .subscribe(() => this.updateChildrenNodes());
- }
- // This is a workaround for https://github.com/angular/angular/issues/23091
- // In aot mode, the lifecycle hooks from parent class are not called.
- ngOnInit() {
- super.ngOnInit();
- }
- ngOnDestroy() {
- this._clear();
- super.ngOnDestroy();
- }
- /** Add children dataNodes to the NodeOutlet */
- 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 {
- // Reset the data differ if there's no children nodes displayed
- this._dataDiffer.diff([]);
- }
- }
- /** Clear the children dataNodes. */
- _clear() {
- const outlet = this._getNodeOutlet();
- if (outlet) {
- outlet.viewContainer.clear();
- this._dataDiffer.diff([]);
- }
- }
- /** Gets the outlet for the current node. */
- _getNodeOutlet() {
- const outlets = this.nodeOutlet;
- // Note that since we use `descendants: true` on the query, we have to ensure
- // that we don't pick up the outlet of a child node by accident.
- return outlets && outlets.find(outlet => !outlet._node || outlet._node === this);
- }
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkNestedTreeNode, deps: [{ token: i0.ElementRef }, { token: CdkTree }, { token: i0.IterableDiffers }], target: i0.ɵɵFactoryTarget.Directive }); }
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: CdkNestedTreeNode, selector: "cdk-nested-tree-node", inputs: { role: "role", disabled: "disabled", tabIndex: "tabIndex" }, 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: "16.0.0", ngImport: i0, type: CdkNestedTreeNode, decorators: [{
- type: Directive,
- args: [{
- selector: 'cdk-nested-tree-node',
- exportAs: 'cdkNestedTreeNode',
- inputs: ['role', 'disabled', 'tabIndex'],
- providers: [
- { provide: CdkTreeNode, useExisting: CdkNestedTreeNode },
- { provide: CDK_TREE_NODE_OUTLET_NODE, useExisting: CdkNestedTreeNode },
- ],
- host: {
- 'class': 'cdk-nested-tree-node',
- },
- }]
- }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: CdkTree }, { type: i0.IterableDiffers }]; }, propDecorators: { nodeOutlet: [{
- type: ContentChildren,
- args: [CdkTreeNodeOutlet, {
- // We need to use `descendants: true`, because Ivy will no longer match
- // indirect descendants if it's left as false.
- descendants: true,
- }]
- }] } });
- /** Regex used to split a string on its CSS units. */
- const cssUnitPattern = /([A-Za-z%]+)$/;
- /**
- * Indent for the children tree dataNodes.
- * This directive will add left-padding to the node to show hierarchy.
- */
- class CdkTreeNodePadding {
- /** The level of depth of the tree node. The padding will be `level * indent` pixels. */
- get level() {
- return this._level;
- }
- set level(value) {
- this._setLevelInput(value);
- }
- /**
- * The indent for each level. Can be a number or a CSS string.
- * Default number 40px from material design menu sub-menu spec.
- */
- get indent() {
- return this._indent;
- }
- set indent(indent) {
- this._setIndentInput(indent);
- }
- constructor(_treeNode, _tree, _element, _dir) {
- this._treeNode = _treeNode;
- this._tree = _tree;
- this._element = _element;
- this._dir = _dir;
- /** Subject that emits when the component has been destroyed. */
- this._destroyed = new Subject();
- /** CSS units used for the indentation value. */
- this.indentUnits = 'px';
- this._indent = 40;
- this._setPadding();
- if (_dir) {
- _dir.change.pipe(takeUntil(this._destroyed)).subscribe(() => this._setPadding(true));
- }
- // In Ivy the indentation binding might be set before the tree node's data has been added,
- // which means that we'll miss the first render. We have to subscribe to changes in the
- // data to ensure that everything is up to date.
- _treeNode._dataChanges.subscribe(() => this._setPadding());
- }
- ngOnDestroy() {
- this._destroyed.next();
- this._destroyed.complete();
- }
- /** The padding indent value for the tree node. Returns a string with px numbers if not null. */
- _paddingIndent() {
- const nodeLevel = this._treeNode.data && this._tree.treeControl.getLevel
- ? this._tree.treeControl.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;
- }
- }
- /**
- * This has been extracted to a util because of TS 4 and VE.
- * View Engine doesn't support property rename inheritance.
- * TS 4.0 doesn't allow properties to override accessors or vice-versa.
- * @docs-private
- */
- _setLevelInput(value) {
- // Set to null as the fallback value so that _setPadding can fall back to the node level if the
- // consumer set the directive as `cdkTreeNodePadding=""`. We still want to take this value if
- // they set 0 explicitly.
- this._level = coerceNumberProperty(value, null);
- this._setPadding();
- }
- /**
- * This has been extracted to a util because of TS 4 and VE.
- * View Engine doesn't support property rename inheritance.
- * TS 4.0 doesn't allow properties to override accessors or vice-versa.
- * @docs-private
- */
- _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 = coerceNumberProperty(value);
- this._setPadding();
- }
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkTreeNodePadding, deps: [{ token: CdkTreeNode }, { token: CdkTree }, { token: i0.ElementRef }, { token: i2.Directionality, optional: true }], target: i0.ɵɵFactoryTarget.Directive }); }
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: CdkTreeNodePadding, selector: "[cdkTreeNodePadding]", inputs: { level: ["cdkTreeNodePadding", "level"], indent: ["cdkTreeNodePaddingIndent", "indent"] }, ngImport: i0 }); }
- }
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkTreeNodePadding, decorators: [{
- type: Directive,
- args: [{
- selector: '[cdkTreeNodePadding]',
- }]
- }], ctorParameters: function () { return [{ type: CdkTreeNode }, { type: CdkTree }, { type: i0.ElementRef }, { type: i2.Directionality, decorators: [{
- type: Optional
- }] }]; }, propDecorators: { level: [{
- type: Input,
- args: ['cdkTreeNodePadding']
- }], indent: [{
- type: Input,
- args: ['cdkTreeNodePaddingIndent']
- }] } });
- /**
- * Node toggle to expand/collapse the node.
- */
- class CdkTreeNodeToggle {
- /** Whether expand/collapse the node recursively. */
- get recursive() {
- return this._recursive;
- }
- set recursive(value) {
- this._recursive = coerceBooleanProperty(value);
- }
- constructor(_tree, _treeNode) {
- this._tree = _tree;
- this._treeNode = _treeNode;
- this._recursive = false;
- }
- _toggle(event) {
- this.recursive
- ? this._tree.treeControl.toggleDescendants(this._treeNode.data)
- : this._tree.treeControl.toggle(this._treeNode.data);
- event.stopPropagation();
- }
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkTreeNodeToggle, deps: [{ token: CdkTree }, { token: CdkTreeNode }], target: i0.ɵɵFactoryTarget.Directive }); }
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: CdkTreeNodeToggle, selector: "[cdkTreeNodeToggle]", inputs: { recursive: ["cdkTreeNodeToggleRecursive", "recursive"] }, host: { listeners: { "click": "_toggle($event)" } }, ngImport: i0 }); }
- }
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkTreeNodeToggle, decorators: [{
- type: Directive,
- args: [{
- selector: '[cdkTreeNodeToggle]',
- host: {
- '(click)': '_toggle($event)',
- },
- }]
- }], ctorParameters: function () { return [{ type: CdkTree }, { type: CdkTreeNode }]; }, propDecorators: { recursive: [{
- type: Input,
- args: ['cdkTreeNodeToggleRecursive']
- }] } });
- const EXPORTED_DECLARATIONS = [
- CdkNestedTreeNode,
- CdkTreeNodeDef,
- CdkTreeNodePadding,
- CdkTreeNodeToggle,
- CdkTree,
- CdkTreeNode,
- CdkTreeNodeOutlet,
- ];
- class CdkTreeModule {
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkTreeModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "16.0.0", ngImport: i0, type: CdkTreeModule, declarations: [CdkNestedTreeNode,
- CdkTreeNodeDef,
- CdkTreeNodePadding,
- CdkTreeNodeToggle,
- CdkTree,
- CdkTreeNode,
- CdkTreeNodeOutlet], exports: [CdkNestedTreeNode,
- CdkTreeNodeDef,
- CdkTreeNodePadding,
- CdkTreeNodeToggle,
- CdkTree,
- CdkTreeNode,
- CdkTreeNodeOutlet] }); }
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkTreeModule }); }
- }
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkTreeModule, decorators: [{
- type: NgModule,
- args: [{
- exports: EXPORTED_DECLARATIONS,
- declarations: EXPORTED_DECLARATIONS,
- }]
- }] });
- /**
- * Generated bundle index. Do not edit.
- */
- export { BaseTreeControl, CDK_TREE_NODE_OUTLET_NODE, CdkNestedTreeNode, CdkTree, CdkTreeModule, CdkTreeNode, CdkTreeNodeDef, CdkTreeNodeOutlet, CdkTreeNodeOutletContext, CdkTreeNodePadding, CdkTreeNodeToggle, FlatTreeControl, NestedTreeControl, getTreeControlFunctionsMissingError, getTreeControlMissingError, getTreeMissingMatchingNodeDefError, getTreeMultipleDefaultNodeDefsError, getTreeNoValidDataSourceError };
- //# sourceMappingURL=tree.mjs.map
|