UNPKG

@angular/cdk

Version:

Angular Material Component Development Kit

1,583 lines (1,568 loc) 50.1 kB
import { SelectionModel, isDataSource } from '@angular/cdk/collections'; import { Observable, Subject, BehaviorSubject, of } from 'rxjs'; import { take, filter, takeUntil } from 'rxjs/operators'; import { InjectionToken, Directive, ViewContainerRef, Inject, Optional, TemplateRef, Component, ViewEncapsulation, ChangeDetectionStrategy, IterableDiffers, ChangeDetectorRef, Input, ViewChild, ContentChildren, ElementRef, Renderer2, HostListener, NgModule } from '@angular/core'; import { Directionality } from '@angular/cdk/bidi'; import { coerceNumberProperty, coerceBooleanProperty } from '@angular/cdk/coercion'; import { FocusMonitor } from '@angular/cdk/a11y'; import { CommonModule } from '@angular/common'; /** * @fileoverview added by tsickle * Generated from: src/cdk/tree/control/base-tree-control.ts * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * Base tree control. It has basic toggle/expand/collapse operations on a single data node. * @abstract * @template T */ 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. * @param {?} dataNode * @return {?} */ toggle(dataNode) { this.expansionModel.toggle(dataNode); } /** * Expands one single data node. * @param {?} dataNode * @return {?} */ expand(dataNode) { this.expansionModel.select(dataNode); } /** * Collapses one single data node. * @param {?} dataNode * @return {?} */ collapse(dataNode) { this.expansionModel.deselect(dataNode); } /** * Whether a given data node is expanded or not. Returns true if the data node is expanded. * @param {?} dataNode * @return {?} */ isExpanded(dataNode) { return this.expansionModel.isSelected(dataNode); } /** * Toggles a subtree rooted at `node` recursively. * @param {?} dataNode * @return {?} */ toggleDescendants(dataNode) { this.expansionModel.isSelected(dataNode) ? this.collapseDescendants(dataNode) : this.expandDescendants(dataNode); } /** * Collapse all dataNodes in the tree. * @return {?} */ collapseAll() { this.expansionModel.clear(); } /** * Expands a subtree rooted at given data node recursively. * @param {?} dataNode * @return {?} */ expandDescendants(dataNode) { /** @type {?} */ let toBeProcessed = [dataNode]; toBeProcessed.push(...this.getDescendants(dataNode)); this.expansionModel.select(...toBeProcessed); } /** * Collapses a subtree rooted at given data node recursively. * @param {?} dataNode * @return {?} */ collapseDescendants(dataNode) { /** @type {?} */ let toBeProcessed = [dataNode]; toBeProcessed.push(...this.getDescendants(dataNode)); this.expansionModel.deselect(...toBeProcessed); } } if (false) { /** * Saved data node for `expandAll` action. * @type {?} */ BaseTreeControl.prototype.dataNodes; /** * A selection model with multi-selection to track expansion status. * @type {?} */ BaseTreeControl.prototype.expansionModel; /** * Get depth of a given data node, return the level number. This is for flat tree node. * @type {?} */ BaseTreeControl.prototype.getLevel; /** * Whether the data node is expandable. Returns true if expandable. * This is for flat tree node. * @type {?} */ BaseTreeControl.prototype.isExpandable; /** * Gets a stream that emits whenever the given data node's children change. * @type {?} */ BaseTreeControl.prototype.getChildren; /** * Gets a list of descendent data nodes of a subtree rooted at given data node recursively. * @abstract * @param {?} dataNode * @return {?} */ BaseTreeControl.prototype.getDescendants = function (dataNode) { }; /** * Expands all data nodes in the tree. * @abstract * @return {?} */ BaseTreeControl.prototype.expandAll = function () { }; } /** * @fileoverview added by tsickle * Generated from: src/cdk/tree/control/flat-tree-control.ts * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * Flat tree control. Able to expand/collapse a subtree recursively for flattened tree. * @template T */ class FlatTreeControl extends BaseTreeControl { /** * Construct with flat tree data node functions getLevel and isExpandable. * @param {?} getLevel * @param {?} isExpandable */ constructor(getLevel, isExpandable) { super(); this.getLevel = getLevel; this.isExpandable = isExpandable; } /** * 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. * @param {?} dataNode * @return {?} */ getDescendants(dataNode) { /** @type {?} */ const startIndex = this.dataNodes.indexOf(dataNode); /** @type {?} */ 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. * @return {?} */ expandAll() { this.expansionModel.select(...this.dataNodes); } } if (false) { /** @type {?} */ FlatTreeControl.prototype.getLevel; /** @type {?} */ FlatTreeControl.prototype.isExpandable; } /** * @fileoverview added by tsickle * Generated from: src/cdk/tree/control/nested-tree-control.ts * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * Nested tree control. Able to expand/collapse a subtree recursively for NestedNode type. * @template T */ class NestedTreeControl extends BaseTreeControl { /** * Construct with nested tree function getChildren. * @param {?} getChildren */ constructor(getChildren) { super(); this.getChildren = getChildren; } /** * 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. * @return {?} */ expandAll() { this.expansionModel.clear(); /** @type {?} */ const allNodes = this.dataNodes.reduce((/** * @param {?} accumulator * @param {?} dataNode * @return {?} */ (accumulator, dataNode) => [...accumulator, ...this.getDescendants(dataNode), dataNode]), []); this.expansionModel.select(...allNodes); } /** * Gets a list of descendant dataNodes of a subtree rooted at given data node recursively. * @param {?} dataNode * @return {?} */ getDescendants(dataNode) { /** @type {?} */ const descendants = []; this._getDescendants(descendants, dataNode); // Remove the node itself return descendants.splice(1); } /** * A helper function to get descendants recursively. * @protected * @param {?} descendants * @param {?} dataNode * @return {?} */ _getDescendants(descendants, dataNode) { descendants.push(dataNode); /** @type {?} */ const childrenNodes = this.getChildren(dataNode); if (Array.isArray(childrenNodes)) { childrenNodes.forEach((/** * @param {?} child * @return {?} */ (child) => this._getDescendants(descendants, child))); } else if (childrenNodes instanceof Observable) { // 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((/** @type {?} */ (Boolean)))) .subscribe((/** * @param {?} children * @return {?} */ children => { for (const child of children) { this._getDescendants(descendants, child); } })); } } } if (false) { /** @type {?} */ NestedTreeControl.prototype.getChildren; } /** * @fileoverview added by tsickle * Generated from: src/cdk/tree/control/tree-control.ts * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * Tree control interface. User can implement TreeControl to expand/collapse dataNodes in the tree. * The CDKTree will use this TreeControl to expand/collapse a node. * User can also use it outside the `<cdk-tree>` to control the expansion status of the tree. * @record * @template T */ function TreeControl() { } if (false) { /** * The saved tree nodes data for `expandAll` action. * @type {?} */ TreeControl.prototype.dataNodes; /** * The expansion model * @type {?} */ TreeControl.prototype.expansionModel; /** * Get depth of a given data node, return the level number. This is for flat tree node. * @type {?} */ TreeControl.prototype.getLevel; /** * Whether the data node is expandable. Returns true if expandable. * This is for flat tree node. * @type {?} */ TreeControl.prototype.isExpandable; /** * Gets a stream that emits whenever the given data node's children change. * @type {?} */ TreeControl.prototype.getChildren; /** * Whether the data node is expanded or collapsed. Return true if it's expanded. * @param {?} dataNode * @return {?} */ TreeControl.prototype.isExpanded = function (dataNode) { }; /** * Get all descendants of a data node * @param {?} dataNode * @return {?} */ TreeControl.prototype.getDescendants = function (dataNode) { }; /** * Expand or collapse data node * @param {?} dataNode * @return {?} */ TreeControl.prototype.toggle = function (dataNode) { }; /** * Expand one data node * @param {?} dataNode * @return {?} */ TreeControl.prototype.expand = function (dataNode) { }; /** * Collapse one data node * @param {?} dataNode * @return {?} */ TreeControl.prototype.collapse = function (dataNode) { }; /** * Expand all the dataNodes in the tree * @return {?} */ TreeControl.prototype.expandAll = function () { }; /** * Collapse all the dataNodes in the tree * @return {?} */ TreeControl.prototype.collapseAll = function () { }; /** * Toggle a data node by expand/collapse it and all its descendants * @param {?} dataNode * @return {?} */ TreeControl.prototype.toggleDescendants = function (dataNode) { }; /** * Expand a data node and all its descendants * @param {?} dataNode * @return {?} */ TreeControl.prototype.expandDescendants = function (dataNode) { }; /** * Collapse a data node and all its descendants * @param {?} dataNode * @return {?} */ TreeControl.prototype.collapseDescendants = function (dataNode) { }; } /** * @fileoverview added by tsickle * Generated from: src/cdk/tree/outlet.ts * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * Injection token used to provide a `CdkTreeNode` to its outlet. * Used primarily to avoid circular imports. * \@docs-private * @type {?} */ 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 { /** * @param {?} viewContainer * @param {?=} _node */ constructor(viewContainer, _node) { this.viewContainer = viewContainer; this._node = _node; } } CdkTreeNodeOutlet.decorators = [ { type: Directive, args: [{ selector: '[cdkTreeNodeOutlet]' },] } ]; /** @nocollapse */ CdkTreeNodeOutlet.ctorParameters = () => [ { type: ViewContainerRef }, { type: undefined, decorators: [{ type: Inject, args: [CDK_TREE_NODE_OUTLET_NODE,] }, { type: Optional }] } ]; if (false) { /** @type {?} */ CdkTreeNodeOutlet.prototype.viewContainer; /** @type {?} */ CdkTreeNodeOutlet.prototype._node; } /** * @fileoverview added by tsickle * Generated from: src/cdk/tree/node.ts * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * Context provided to the tree node component. * @template T */ class CdkTreeNodeOutletContext { /** * @param {?} data */ constructor(data) { this.$implicit = data; } } if (false) { /** * Data for the node. * @type {?} */ CdkTreeNodeOutletContext.prototype.$implicit; /** * Depth of the node. * @type {?} */ CdkTreeNodeOutletContext.prototype.level; /** * Index location of the node. * @type {?} */ CdkTreeNodeOutletContext.prototype.index; /** * Length of the number of total dataNodes. * @type {?} */ CdkTreeNodeOutletContext.prototype.count; } /** * Data node definition for the CdkTree. * Captures the node's template and a when predicate that describes when this node should be used. * @template T */ class CdkTreeNodeDef { /** * \@docs-private * @param {?} template */ constructor(template) { this.template = template; } } CdkTreeNodeDef.decorators = [ { type: Directive, args: [{ selector: '[cdkTreeNodeDef]', inputs: [ 'when: cdkTreeNodeDefWhen' ], },] } ]; /** @nocollapse */ CdkTreeNodeDef.ctorParameters = () => [ { type: TemplateRef } ]; if (false) { /** * Function that should return true if this node template should be used for the provided node * data and index. If left undefined, this node will be considered the default node template to * use when no other when functions return true for the data. * For every node, there must be at least one when function that passes or an undefined to * default. * @type {?} */ CdkTreeNodeDef.prototype.when; /** @type {?} */ CdkTreeNodeDef.prototype.template; } /** * @fileoverview added by tsickle * Generated from: src/cdk/tree/tree-errors.ts * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ /** * Returns an error to be thrown when there is no usable data. * \@docs-private * @return {?} */ 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 * @return {?} */ 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 * @return {?} */ 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 * @return {?} */ 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 * @return {?} */ function getTreeControlFunctionsMissingError() { return Error(`Could not find functions for nested/flat tree in tree control.`); } /** * @fileoverview added by tsickle * Generated from: src/cdk/tree/tree.ts * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * 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. * @template T */ class CdkTree { /** * @param {?} _differs * @param {?} _changeDetectorRef */ 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 }); } /** * 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. * @return {?} */ get dataSource() { return this._dataSource; } /** * @param {?} dataSource * @return {?} */ set dataSource(dataSource) { if (this._dataSource !== dataSource) { this._switchDataSource(dataSource); } } /** * @return {?} */ ngOnInit() { this._dataDiffer = this._differs.find([]).create(this.trackBy); if (!this.treeControl) { throw getTreeControlMissingError(); } } /** * @return {?} */ ngOnDestroy() { this._nodeOutlet.viewContainer.clear(); this._onDestroy.next(); this._onDestroy.complete(); if (this._dataSource && typeof ((/** @type {?} */ (this._dataSource))).disconnect === 'function') { ((/** @type {?} */ (this.dataSource))).disconnect(this); } if (this._dataSubscription) { this._dataSubscription.unsubscribe(); this._dataSubscription = null; } } /** * @return {?} */ ngAfterContentChecked() { /** @type {?} */ const defaultNodeDefs = this._nodeDefs.filter((/** * @param {?} def * @return {?} */ def => !def.when)); if (defaultNodeDefs.length > 1) { 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. * @private * @param {?} dataSource * @return {?} */ _switchDataSource(dataSource) { if (this._dataSource && typeof ((/** @type {?} */ (this._dataSource))).disconnect === 'function') { ((/** @type {?} */ (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. * @private * @return {?} */ _observeRenderChanges() { /** @type {?} */ let dataStream; if (isDataSource(this._dataSource)) { dataStream = this._dataSource.connect(this); } else if (this._dataSource instanceof Observable) { dataStream = this._dataSource; } else if (Array.isArray(this._dataSource)) { dataStream = of(this._dataSource); } if (dataStream) { this._dataSubscription = dataStream.pipe(takeUntil(this._onDestroy)) .subscribe((/** * @param {?} data * @return {?} */ data => this.renderNodeChanges(data))); } else { throw getTreeNoValidDataSourceError(); } } /** * Check for changes made in the data and render each change (node added/removed/moved). * @param {?} data * @param {?=} dataDiffer * @param {?=} viewContainer * @param {?=} parentData * @return {?} */ renderNodeChanges(data, dataDiffer = this._dataDiffer, viewContainer = this._nodeOutlet.viewContainer, parentData) { /** @type {?} */ const changes = dataDiffer.diff(data); if (!changes) { return; } changes.forEachOperation((/** * @param {?} item * @param {?} adjustedPreviousIndex * @param {?} currentIndex * @return {?} */ (item, adjustedPreviousIndex, currentIndex) => { if (item.previousIndex == null) { this.insertNode(data[(/** @type {?} */ (currentIndex))], (/** @type {?} */ (currentIndex)), viewContainer, parentData); } else if (currentIndex == null) { viewContainer.remove((/** @type {?} */ (adjustedPreviousIndex))); this._levels.delete(item.item); } else { /** @type {?} */ const view = viewContainer.get((/** @type {?} */ (adjustedPreviousIndex))); viewContainer.move((/** @type {?} */ (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. * @param {?} data * @param {?} i * @return {?} */ _getNodeDef(data, i) { if (this._nodeDefs.length === 1) { return this._nodeDefs.first; } /** @type {?} */ const nodeDef = this._nodeDefs.find((/** * @param {?} def * @return {?} */ def => def.when && def.when(i, data))) || this._defaultNodeDef; if (!nodeDef) { 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. * @param {?} nodeData * @param {?} index * @param {?=} viewContainer * @param {?=} parentData * @return {?} */ insertNode(nodeData, index, viewContainer, parentData) { /** @type {?} */ const node = this._getNodeDef(nodeData, index); // Node context that will be provided to created embedded view /** @type {?} */ 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 = (/** @type {?} */ (this._levels.get(parentData))) + 1; } else { context.level = 0; } this._levels.set(nodeData, context.level); // Use default tree nodeOutlet, or nested node's nodeOutlet /** @type {?} */ 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; } } } 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 }] } ]; /** @nocollapse */ CdkTree.ctorParameters = () => [ { type: IterableDiffers }, { type: ChangeDetectorRef } ]; CdkTree.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 },] }] }; if (false) { /** * Subject that emits when the component has been destroyed. * @type {?} * @private */ CdkTree.prototype._onDestroy; /** * Differ used to find the changes in the data provided by the data source. * @type {?} * @private */ CdkTree.prototype._dataDiffer; /** * Stores the node definition that does not have a when predicate. * @type {?} * @private */ CdkTree.prototype._defaultNodeDef; /** * Data subscription * @type {?} * @private */ CdkTree.prototype._dataSubscription; /** * Level of nodes * @type {?} * @private */ CdkTree.prototype._levels; /** * @type {?} * @private */ CdkTree.prototype._dataSource; /** * The tree controller * @type {?} */ CdkTree.prototype.treeControl; /** * Tracking function that will be used to check the differences in data changes. Used similarly * to `ngFor` `trackBy` function. Optimize node operations by identifying a node based on its data * relative to the function to know if a node should be added/removed/moved. * Accepts a function that takes two parameters, `index` and `item`. * @type {?} */ CdkTree.prototype.trackBy; /** @type {?} */ CdkTree.prototype._nodeOutlet; /** * The tree node template for the tree * @type {?} */ CdkTree.prototype._nodeDefs; /** * 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. * @type {?} */ CdkTree.prototype.viewChange; /** * @type {?} * @private */ CdkTree.prototype._differs; /** * @type {?} * @private */ CdkTree.prototype._changeDetectorRef; } /** * Tree node for CdkTree. It contains the data in the tree node. * @template T */ class CdkTreeNode { /** * @param {?} _elementRef * @param {?} _tree */ 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(); /** * The role of the node should be 'group' if it's an internal node, * and 'treeitem' if it's a leaf node. */ this.role = 'treeitem'; CdkTreeNode.mostRecentTreeNode = (/** @type {?} */ (this)); } /** * The tree node's data. * @return {?} */ get data() { return this._data; } /** * @param {?} value * @return {?} */ set data(value) { if (value !== this._data) { this._data = value; this._setRoleFromData(); this._dataChanges.next(); } } /** * @return {?} */ get isExpanded() { return this._tree.treeControl.isExpanded(this._data); } /** * @return {?} */ get level() { return this._tree.treeControl.getLevel ? this._tree.treeControl.getLevel(this._data) : 0; } /** * @return {?} */ 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. * @return {?} */ focus() { this._elementRef.nativeElement.focus(); } /** * @protected * @return {?} */ _setRoleFromData() { if (this._tree.treeControl.isExpandable) { this.role = this._tree.treeControl.isExpandable(this._data) ? 'group' : 'treeitem'; } else { if (!this._tree.treeControl.getChildren) { throw getTreeControlFunctionsMissingError(); } /** @type {?} */ const childrenNodes = this._tree.treeControl.getChildren(this._data); if (Array.isArray(childrenNodes)) { this._setRoleFromChildren((/** @type {?} */ (childrenNodes))); } else if (childrenNodes instanceof Observable) { childrenNodes.pipe(takeUntil(this._destroyed)) .subscribe((/** * @param {?} children * @return {?} */ children => this._setRoleFromChildren(children))); } } } /** * @protected * @param {?} children * @return {?} */ _setRoleFromChildren(children) { this.role = children && children.length ? 'group' : 'treeitem'; } } /** * The most recently created `CdkTreeNode`. We save it in static variable so we can retrieve it * in `CdkTree` and set the data to it. */ CdkTreeNode.mostRecentTreeNode = null; CdkTreeNode.decorators = [ { type: Directive, args: [{ selector: 'cdk-tree-node', exportAs: 'cdkTreeNode', host: { '[attr.aria-expanded]': 'isExpanded', '[attr.aria-level]': 'role === "treeitem" ? level : null', '[attr.role]': 'role', 'class': 'cdk-tree-node', }, },] } ]; /** @nocollapse */ CdkTreeNode.ctorParameters = () => [ { type: ElementRef }, { type: CdkTree } ]; CdkTreeNode.propDecorators = { role: [{ type: Input }] }; if (false) { /** * The most recently created `CdkTreeNode`. We save it in static variable so we can retrieve it * in `CdkTree` and set the data to it. * @type {?} */ CdkTreeNode.mostRecentTreeNode; /** * Subject that emits when the component has been destroyed. * @type {?} * @protected */ CdkTreeNode.prototype._destroyed; /** * Emits when the node's data has changed. * @type {?} */ CdkTreeNode.prototype._dataChanges; /** * @type {?} * @protected */ CdkTreeNode.prototype._data; /** * The role of the node should be 'group' if it's an internal node, * and 'treeitem' if it's a leaf node. * @type {?} */ CdkTreeNode.prototype.role; /** * @type {?} * @protected */ CdkTreeNode.prototype._elementRef; /** * @type {?} * @protected */ CdkTreeNode.prototype._tree; } /** * @fileoverview added by tsickle * Generated from: src/cdk/tree/nested-node.ts * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * 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`. * @template T */ class CdkNestedTreeNode extends CdkTreeNode { /** * @param {?} _elementRef * @param {?} _tree * @param {?} _differs */ constructor(_elementRef, _tree, _differs) { super(_elementRef, _tree); this._elementRef = _elementRef; this._tree = _tree; this._differs = _differs; } /** * @return {?} */ ngAfterContentInit() { this._dataDiffer = this._differs.find([]).create(this._tree.trackBy); if (!this._tree.treeControl.getChildren) { throw getTreeControlFunctionsMissingError(); } /** @type {?} */ const childrenNodes = this._tree.treeControl.getChildren(this.data); if (Array.isArray(childrenNodes)) { this.updateChildrenNodes((/** @type {?} */ (childrenNodes))); } else if (childrenNodes instanceof Observable) { childrenNodes.pipe(takeUntil(this._destroyed)) .subscribe((/** * @param {?} result * @return {?} */ result => this.updateChildrenNodes(result))); } this.nodeOutlet.changes.pipe(takeUntil(this._destroyed)) .subscribe((/** * @return {?} */ () => this.updateChildrenNodes())); } /** * @return {?} */ ngOnDestroy() { this._clear(); super.ngOnDestroy(); } /** * Add children dataNodes to the NodeOutlet * @protected * @param {?=} children * @return {?} */ updateChildrenNodes(children) { /** @type {?} */ const outlet = this._getNodeOutlet(); if (children) { this._children = children; } if (outlet && this._children) { /** @type {?} */ 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. * @protected * @return {?} */ _clear() { /** @type {?} */ const outlet = this._getNodeOutlet(); if (outlet) { outlet.viewContainer.clear(); this._dataDiffer.diff([]); } } /** * Gets the outlet for the current node. * @private * @return {?} */ _getNodeOutlet() { /** @type {?} */ 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((/** * @param {?} outlet * @return {?} */ outlet => !outlet._node || outlet._node === this)); } } CdkNestedTreeNode.decorators = [ { type: Directive, args: [{ selector: 'cdk-nested-tree-node', exportAs: 'cdkNestedTreeNode', host: { '[attr.aria-expanded]': 'isExpanded', '[attr.role]': 'role', 'class': 'cdk-tree-node cdk-nested-tree-node', }, providers: [ { provide: CdkTreeNode, useExisting: CdkNestedTreeNode }, { provide: CDK_TREE_NODE_OUTLET_NODE, useExisting: CdkNestedTreeNode } ] },] } ]; /** @nocollapse */ CdkNestedTreeNode.ctorParameters = () => [ { type: ElementRef }, { type: CdkTree }, { type: IterableDiffers } ]; CdkNestedTreeNode.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 },] }] }; if (false) { /** * Differ used to find the changes in the data provided by the data source. * @type {?} * @private */ CdkNestedTreeNode.prototype._dataDiffer; /** * The children data dataNodes of current node. They will be placed in `CdkTreeNodeOutlet`. * @type {?} * @protected */ CdkNestedTreeNode.prototype._children; /** * The children node placeholder. * @type {?} */ CdkNestedTreeNode.prototype.nodeOutlet; /** * @type {?} * @protected */ CdkNestedTreeNode.prototype._elementRef; /** * @type {?} * @protected */ CdkNestedTreeNode.prototype._tree; /** * @type {?} * @protected */ CdkNestedTreeNode.prototype._differs; } /** * @fileoverview added by tsickle * Generated from: src/cdk/tree/padding.ts * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * Regex used to split a string on its CSS units. * @type {?} */ const cssUnitPattern = /([A-Za-z%]+)$/; /** * Indent for the children tree dataNodes. * This directive will add left-padding to the node to show hierarchy. * @template T */ class CdkTreeNodePadding { /** * @param {?} _treeNode * @param {?} _tree * @param {?} _renderer * @param {?} _element * @param {?} _dir */ constructor(_treeNode, _tree, _renderer, _element, _dir) { this._treeNode = _treeNode; this._tree = _tree; this._renderer = _renderer; 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((/** * @return {?} */ () => 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((/** * @return {?} */ () => this._setPadding())); } /** * The level of depth of the tree node. The padding will be `level * indent` pixels. * @return {?} */ get level() { return this._level; } /** * @param {?} value * @return {?} */ set level(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 = (/** @type {?} */ (coerceNumberProperty(value, null))); this._setPadding(); } /** * The indent for each level. Can be a number or a CSS string. * Default number 40px from material design menu sub-menu spec. * @return {?} */ get indent() { return this._indent; } /** * @param {?} indent * @return {?} */ set indent(indent) { /** @type {?} */ let value = indent; /** @type {?} */ let units = 'px'; if (typeof indent === 'string') { /** @type {?} */ const parts = indent.split(cssUnitPattern); value = parts[0]; units = parts[1] || units; } this.indentUnits = units; this._indent = coerceNumberProperty(value); this._setPadding(); } /** * @return {?} */ ngOnDestroy() { this._destroyed.next(); this._destroyed.complete(); } /** * The padding indent value for the tree node. Returns a string with px numbers if not null. * @return {?} */ _paddingIndent() { /** @type {?} */ const nodeLevel = (this._treeNode.data && this._tree.treeControl.getLevel) ? this._tree.treeControl.getLevel(this._treeNode.data) : null; /** @type {?} */ const level = this._level == null ? nodeLevel : this._level; return typeof level === 'number' ? `${level * this._indent}${this.indentUnits}` : null; } /** * @param {?=} forceChange * @return {?} */ _setPadding(forceChange = false) { /** @type {?} */ const padding = this._paddingIndent(); if (padding !== this._currentPadding || forceChange) { /** @type {?} */ const element = this._element.nativeElement; /** @type {?} */ const paddingProp = this._dir && this._dir.value === 'rtl' ? 'paddingRight' : 'paddingLeft'; /** @type {?} */ const resetProp = paddingProp === 'paddingLeft' ? 'paddingRight' : 'paddingLeft'; this._renderer.setStyle(element, paddingProp, padding); this._renderer.setStyle(element, resetProp, null); this._currentPadding = padding; } } } CdkTreeNodePadding.decorators = [ { type: Directive, args: [{ selector: '[cdkTreeNodePadding]', },] } ]; /** @nocollapse */ CdkTreeNodePadding.ctorParameters = () => [ { type: CdkTreeNode }, { type: CdkTree }, { type: Renderer2 }, { type: ElementRef }, { type: Directionality, decorators: [{ type: Optional }] } ]; CdkTreeNodePadding.propDecorators = { level: [{ type: Input, args: ['cdkTreeNodePadding',] }], indent: [{ type: Input, args: ['cdkTreeNodePaddingIndent',] }] }; if (false) { /** @type {?} */ CdkTreeNodePadding.ngAcceptInputType_level; /** * Current padding value applied to the element. Used to avoid unnecessarily hitting the DOM. * @type {?} * @private */ CdkTreeNodePadding.prototype._currentPadding; /** * Subject that emits when the component has been destroyed. * @type {?} * @private */ CdkTreeNodePadding.prototype._destroyed; /** * CSS units used for the indentation value. * @type {?} */ CdkTreeNodePadding.prototype.indentUnits; /** @type {?} */ CdkTreeNodePadding.prototype._level; /** @type {?} */ CdkTreeNodePadding.prototype._indent; /** * @type {?} * @private */ CdkTreeNodePadding.prototype._treeNode; /** * @type {?} * @private */ CdkTreeNodePadding.prototype._tree; /** * @type {?} * @private */ CdkTreeNodePadding.prototype._renderer; /** * @type {?} * @private */ CdkTreeNodePadding.prototype._element; /** * @type {?} * @private */ CdkTreeNodePadding.prototype._dir; } /** * @fileoverview added by tsickle * Generated from: src/cdk/tree/toggle.ts * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * Node toggle to expand/collapse the node. * @template T */ class CdkTreeNodeToggle { /** * @param {?} _tree * @param {?} _treeNode */ constructor(_tree, _treeNode) { this._tree = _tree; this._treeNode = _treeNode; this._recursive = false; } /** * Whether expand/collapse the node recursively. * @return {?} */ get recursive() { return this._recursive; } /** * @param {?} value * @return {?} */ set recursive(value) { this._recursive = coerceBooleanProperty(value); } // We have to use a `HostListener` here in order to support both Ivy and ViewEngine. // In Ivy the `host` bindings will be merged when this class is extended, whereas in // ViewEngine they're overwritten. // TODO(crisbeto): we move this back into `host` once Ivy is turned on by default. // tslint:disable-next-line:no-host-decorator-in-concrete /** * @param {?} event * @return {?} */ _toggle(event) { this.recursive ? this._tree.treeControl.toggleDescendants(this._treeNode.data) : this._tree.treeControl.toggle(this._treeNode.data); event.stopPropagation(); } } CdkTreeNodeToggle.decorators = [ { type: Directive, args: [{ selector: '[cdkTreeNodeToggle]' },] } ]; /** @nocollapse */ CdkTreeNodeToggle.ctorParameters = () => [ { type: CdkTree }, { type: CdkTreeNode } ]; CdkTreeNodeToggle.propDecorators = { recursive: [{ type: Input, args: ['cdkTreeNodeToggleRecursive',] }], _toggle: [{ type: HostListener, args: ['click', ['$event'],] }] }; if (false) { /** @type {?} */ CdkTreeNodeToggle.ngAcceptInputType_recursive; /** * @type {?} * @protected */ CdkTreeNodeToggle.prototype._recursive; /** * @type {?} * @protected */ CdkTreeNodeToggle.prototype._tree; /** * @type {?} * @protected */ CdkTreeNodeToggle.prototype._treeNode; } /** * @fileoverview added by tsickle * Generated from: src/cdk/tree/tree-module.ts * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** @type {?} */ const EXPORTED_DECLARATIONS = [ CdkNestedTreeNode, CdkTreeNodeDef, CdkTreeNodePadding, CdkTreeNodeToggle, CdkTree, CdkTreeNode, CdkTreeNodeOutlet, ]; class CdkTreeModule { } CdkTreeModule.decorators = [ { type: NgModule, args: [{ imports: [CommonModule], exports: EXPORTED_DECLARATIONS, declarations: EXPORTED_DECLARATIONS, providers: [FocusMonitor, CdkTreeNodeDef] },] } ]; /** * @fileoverview added by tsickle * Generated from: src/cdk/tree/public-api.ts * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * 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, getTreeMultipleDefa