src/treeview/tree-node.component.ts
AfterContentChecked
OnInit
OnDestroy
selector | cds-tree-node |
template |
|
Properties |
|
Methods |
Inputs |
Outputs |
Accessors |
constructor(treeViewService: TreeViewService)
|
||||||
Defined in src/treeview/tree-node.component.ts:177
|
||||||
Parameters :
|
active | |
Type : boolean
|
|
Default value : false
|
|
Defined in src/treeview/tree-node.component.ts:124
|
children | |
Type : Node[]
|
|
Default value : []
|
|
Defined in src/treeview/tree-node.component.ts:135
|
depth | |
Type : number
|
|
Default value : 0
|
|
Defined in src/treeview/tree-node.component.ts:141
|
|
Determines the depth of the node
Calculated by default when passing |
disabled | |
Type : boolean
|
|
Default value : false
|
|
Defined in src/treeview/tree-node.component.ts:125
|
expanded | |
Type : boolean
|
|
Default value : false
|
|
Defined in src/treeview/tree-node.component.ts:127
|
gap | |
Type : number
|
|
Default value : 0
|
|
Defined in src/treeview/tree-node.component.ts:134
|
icon | |
Type : string | TemplateRef<any>
|
|
Defined in src/treeview/tree-node.component.ts:132
|
iconContext | |
Type : any
|
|
Defined in src/treeview/tree-node.component.ts:133
|
id | |
Type : string
|
|
Default value : `tree-node-${TreeNodeComponent.treeNodeCount++}`
|
|
Defined in src/treeview/tree-node.component.ts:123
|
label | |
Type : string | TemplateRef<any>
|
|
Defined in src/treeview/tree-node.component.ts:128
|
labelContext | |
Type : any
|
|
Defined in src/treeview/tree-node.component.ts:129
|
node | |
Type : Node
|
|
Defined in src/treeview/tree-node.component.ts:147
|
|
Simple way to set all attributes of Node component via node object Would simplify setting component attributes when dynamically rendering node. |
selectable | |
Type : boolean
|
|
Default value : true
|
|
Defined in src/treeview/tree-node.component.ts:126
|
selected | |
Type : boolean
|
|
Default value : false
|
|
Defined in src/treeview/tree-node.component.ts:130
|
value | |
Type : any
|
|
Defined in src/treeview/tree-node.component.ts:131
|
nodeBlur | |
Type : EventEmitter
|
|
Defined in src/treeview/tree-node.component.ts:171
|
nodeFocus | |
Type : EventEmitter
|
|
Defined in src/treeview/tree-node.component.ts:170
|
nodeSelect | |
Type : EventEmitter
|
|
Defined in src/treeview/tree-node.component.ts:172
|
nodetoggle | |
Type : EventEmitter
|
|
Defined in src/treeview/tree-node.component.ts:173
|
calculateOffset |
calculateOffset()
|
Defined in src/treeview/tree-node.component.ts:230
|
Calculate the node offset
Returns :
number
Number |
emitBlurEvent | ||||
emitBlurEvent(event)
|
||||
Defined in src/treeview/tree-node.component.ts:255
|
||||
Parameters :
Returns :
void
|
emitFocusEvent | ||||
emitFocusEvent(event)
|
||||
Defined in src/treeview/tree-node.component.ts:249
|
||||
Parameters :
Returns :
void
|
Public isProjected |
isProjected()
|
Defined in src/treeview/tree-node.component.ts:302
|
Returns :
any
|
Public isTemplate | ||||
isTemplate(value)
|
||||
Defined in src/treeview/tree-node.component.ts:298
|
||||
Parameters :
Returns :
boolean
|
navigateTree | ||||||
navigateTree(event: KeyboardEvent)
|
||||||
Defined in src/treeview/tree-node.component.ts:275
|
||||||
Manages the keyboard accessibility for children expansion & selection
Parameters :
Returns :
void
|
ngAfterContentChecked |
ngAfterContentChecked()
|
Defined in src/treeview/tree-node.component.ts:184
|
Caclulate offset for margin/padding
Returns :
void
|
ngOnDestroy |
ngOnDestroy()
|
Defined in src/treeview/tree-node.component.ts:202
|
Unsubscribe from subscriptions
Returns :
void
|
ngOnInit |
ngOnInit()
|
Defined in src/treeview/tree-node.component.ts:191
|
Highlight the node
Returns :
void
|
nodeClick | ||||
nodeClick(event)
|
||||
Defined in src/treeview/tree-node.component.ts:210
|
||||
Selects the node and emits the event from the tree view component
Parameters :
Returns :
void
|
toggleExpanded | ||||||
toggleExpanded(event)
|
||||||
Defined in src/treeview/tree-node.component.ts:263
|
||||||
Expand children if not disabled
Parameters :
Returns :
void
|
Private _node |
Defined in src/treeview/tree-node.component.ts:176
|
offset |
Defined in src/treeview/tree-node.component.ts:175
|
Private subscription |
Type : Subscription
|
Defined in src/treeview/tree-node.component.ts:177
|
Static treeNodeCount |
Type : number
|
Default value : 0
|
Defined in src/treeview/tree-node.component.ts:122
|
node | ||||||
getnode()
|
||||||
Defined in src/treeview/tree-node.component.ts:166
|
||||||
setnode(node: Node)
|
||||||
Defined in src/treeview/tree-node.component.ts:147
|
||||||
Simple way to set all attributes of Node component via node object Would simplify setting component attributes when dynamically rendering node.
Parameters :
Returns :
void
|
import {
Component,
Input,
Output,
EventEmitter,
OnInit,
OnDestroy,
AfterContentInit,
TemplateRef,
AfterContentChecked
} from "@angular/core";
import { Subscription } from "rxjs";
import { TreeViewService } from "./treeview.service";
import { EventOnNode, Node } from "./tree-node.types";
@Component({
selector: "cds-tree-node",
template: `
<div
[id]="id"
class="cds--tree-node"
[ngClass]="{
'cds--tree-node--active': active,
'cds--tree-node--disabled': disabled,
'cds--tree-node--selected': selected,
'cds--tree-leaf-node': !children.length,
'cds--tree-parent-node': children.length,
'cds--tree-node--with-icon': icon
}"
[attr.aria-expanded]="expanded || null"
[attr.aria-current]="active || null"
[attr.aria-selected]="disabled ? null : selected"
[attr.aria-disabled]="disabled"
role="treeitem"
[attr.tabindex]="selected ? 0 : -1"
(focus)="emitFocusEvent($event)"
(blur)="emitBlurEvent($event)"
(keydown)="navigateTree($event)">
<div
*ngIf="!children.length"
class="cds--tree-node__label"
[style.padding-inline-start.rem]="offset"
[style.margin-inline-start.rem]="-offset"
(click)="nodeClick($event)">
<!-- Icon -->
<ng-container *ngIf="icon && !isTemplate(icon)">
<svg
class="cds--tree-node__icon"
[cdsIcon]="icon"
size="16">
</svg>
</ng-container>
<ng-template *ngIf="isTemplate(icon)" [ngTemplateOutlet]="icon"></ng-template>
<ng-container *ngIf="!isTemplate(label)">{{label}}</ng-container>
<ng-template
*ngIf="isTemplate(label)"
[ngTemplateOutlet]="label"
[ngTemplateOutletContext]="{ $implicit: labelContext }">
</ng-template>
</div>
<div
*ngIf="children.length"
class="cds--tree-node__label"
[style.padding-inline-start.rem]="offset"
[style.margin-inline-start.rem]="-offset"
role="group"
(click)="nodeClick($event)">
<span
class="cds--tree-parent-node__toggle"
[attr.disabled]="disabled || null"
(click)="toggleExpanded($event)">
<svg
class="cds--tree-parent-node__toggle-icon"
[ngClass]="{'cds--tree-parent-node__toggle-icon--expanded' : expanded}"
ibmIcon="caret--down"
size="16">
</svg>
</span>
<span class="cds--tree-node__label__details">
<!-- Icon -->
<ng-container *ngIf="icon && !isTemplate(icon)">
<svg
class="cds--tree-node__icon"
[cdsIcon]="icon"
size="16">
</svg>
</ng-container>
<ng-template
*ngIf="isTemplate(icon)"
[ngTemplateOutlet]="icon"
[ngTemplateOutletContext]="{ $implicit: iconContext }">
</ng-template>
<ng-container *ngIf="!isTemplate(label)">{{label}}</ng-container>
<ng-template
*ngIf="isTemplate(label)"
[ngTemplateOutlet]="label"
[ngTemplateOutletContext]="{ $implicit: labelContext }">
</ng-template>
</span>
</div>
<div
*ngIf="expanded"
role="group"
class="cds--tree-node__children">
<ng-container *ngIf="isProjected(); else notProjected">
<ng-content></ng-content>
</ng-container>
<ng-template #notProjected>
<cds-tree-node
*ngFor="let childNode of children"
[node]="childNode"
[depth]="depth + 1"
[disabled]="disabled"
(nodetoggle)="nodetoggle.emit($event)">
</cds-tree-node>
</ng-template>
</div>
</div>
`
})
export class TreeNodeComponent implements AfterContentChecked, OnInit, OnDestroy {
static treeNodeCount = 0;
@Input() id = `tree-node-${TreeNodeComponent.treeNodeCount++}`;
@Input() active = false;
@Input() disabled = false;
@Input() selectable = true;
@Input() expanded = false;
@Input() label: string | TemplateRef<any>;
@Input() labelContext: any;
@Input() selected = false;
@Input() value;
@Input() icon: string | TemplateRef<any>;
@Input() iconContext: any;
@Input() gap = 0;
@Input() children: Node[] = [];
/**
* Determines the depth of the node
* Calculated by default when passing `Node` array to `TreeViewComponent`, manual entry required otherwise
*/
@Input() depth = 0;
/**
* Simple way to set all attributes of Node component via node object
* Would simplify setting component attributes when dynamically rendering node.
*/
@Input() set node(node: Node) {
this._node = node;
this.id = node.id ?? this.id;
this.active = node.active ?? this.active;
this.disabled = node.disabled ?? this.disabled;
this.selectable = node.selectable ?? this.selectable;
this.expanded = node.expanded ?? this.expanded;
this.label = node.label ?? this.label;
this.labelContext = node.labelContext ?? this.labelContext;
this.value = node.value ?? this.value;
this.icon = node.icon ?? this.icon;
this.selected = node.selected ?? this.selected;
this.depth = node.depth ?? this.depth;
this.gap = node.gap ?? this.gap;
this.children = node.children ?? this.children;
this.iconContext = node.iconText ?? this.iconContext;
}
get node() {
return this._node;
}
@Output() nodeFocus = new EventEmitter<EventOnNode>();
@Output() nodeBlur = new EventEmitter<EventOnNode>();
@Output() nodeSelect = new EventEmitter<Node>();
@Output() nodetoggle = new EventEmitter<EventOnNode>();
offset;
private _node;
private subscription: Subscription;
constructor(private treeViewService: TreeViewService) {}
/**
* Caclulate offset for margin/padding
*/
ngAfterContentChecked(): void {
this.offset = this.calculateOffset();
}
/**
* Highlight the node
*/
ngOnInit(): void {
// Highlight the node
this.subscription = this.treeViewService.selectionObservable.subscribe((value: Map<string, Node>) => {
this.selected = this.selectable && value.has(this.id);
this.active = this.selectable && this.selected;
});
}
/**
* Unsubscribe from subscriptions
*/
ngOnDestroy(): void {
this.subscription?.unsubscribe();
}
/**
* Selects the node and emits the event from the tree view component
* @param event
*/
nodeClick(event) {
if (!this.disabled) {
event.target.parentElement.focus();
if (this.selectable || this.children.length === 0) {
this.selected = true;
this.active = true;
const node = { id: this.id, label: this.label, value: this.value };
// Passes event to all nodes to update highlighting & parent to emit
this.treeViewService.selectNode(node);
this.nodeSelect.emit(node);
} else {
this.toggleExpanded(event);
}
}
}
/**
* Calculate the node offset
* @returns Number
*/
calculateOffset() {
// Parent node with icon
if (this.children.length && this.icon) {
return this.depth + 1 + this.depth * 0.5;
}
// parent node without icon
if (this.children.length) {
return this.depth + 1;
}
// leaf node with icon
if (this.icon) {
return this.depth + 2 + this.depth * 0.5;
}
return this.depth + this.gap + 2.5;
}
emitFocusEvent(event) {
const node = { id: this.id, label: this.label, value: this.value };
this.nodeFocus.emit({ node, event });
this.treeViewService.focusNode(node);
}
emitBlurEvent(event) {
this.nodeBlur.emit({ node: { id: this.id, label: this.label, value: this.value }, event });
}
/**
* Expand children if not disabled
* @param event: Event
*/
toggleExpanded(event) {
if (!this.disabled) {
this.nodetoggle.emit({ node: { id: this.id, label: this.label, value: this.value }, event });
this.expanded = !this.expanded;
// Prevent selection of the node
event.stopPropagation();
}
}
/**
* Manages the keyboard accessibility for children expansion & selection
*/
navigateTree(event: KeyboardEvent) {
if (event.key === "ArrowLeft" || event.key === "ArrowRight" || event.key === "Enter") {
event.stopPropagation();
}
// Unexpand
if (event.key === "ArrowLeft") {
if (this.expanded && this.children) {
this.toggleExpanded(event);
}
}
if (event.key === "ArrowRight") {
if (!this.expanded && this.children) {
this.toggleExpanded(event);
}
}
if (event.key === "Enter") {
event.preventDefault();
this.nodeClick(event);
}
}
public isTemplate(value) {
return value instanceof TemplateRef;
}
public isProjected() {
return this.treeViewService.contentProjected;
}
}