1/* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16import {Component, EventEmitter, Input, Output} from '@angular/core'; 17import {DiffType, HierarchyTreeNode, UiTreeNode, UiTreeUtils} from 'viewers/common/ui_tree_utils'; 18import {nodeInnerItemStyles} from 'viewers/components/styles/node.styles'; 19 20@Component({ 21 selector: 'tree-node', 22 template: ` 23 <button *ngIf="showChevron()" class="icon-button toggle-tree-btn" (click)="toggleTree($event)"> 24 <mat-icon> 25 {{ isCollapsed ? 'arrow_drop_down' : 'chevron_right' }} 26 </mat-icon> 27 </button> 28 29 <div *ngIf="showLeafNodeIcon()" class="leaf-node-icon-wrapper"> 30 <mat-icon class="leaf-node-icon"></mat-icon> 31 </div> 32 33 <button *ngIf="showPinNodeIcon()" class="icon-button pin-node-btn" (click)="pinNode($event)"> 34 <mat-icon> 35 {{ isPinned ? 'star' : 'star_border' }} 36 </mat-icon> 37 </button> 38 39 <div class="description"> 40 <tree-node-data-view *ngIf="!isPropertiesTreeNode()" [item]="item"></tree-node-data-view> 41 <tree-node-properties-data-view 42 *ngIf="isPropertiesTreeNode()" 43 [item]="item"></tree-node-properties-data-view> 44 </div> 45 46 <button 47 *ngIf="hasChildren && !isCollapsed" 48 class="icon-button expand-tree-btn" 49 [class]="collapseDiffClass" 50 (click)="expandTree($event)"> 51 <mat-icon aria-hidden="true"> more_horiz </mat-icon> 52 </button> 53 `, 54 styles: [nodeInnerItemStyles], 55}) 56export class TreeNodeComponent { 57 @Input() item!: UiTreeNode; 58 @Input() isLeaf?: boolean; 59 @Input() flattened?: boolean; 60 @Input() isCollapsed?: boolean; 61 @Input() hasChildren?: boolean = false; 62 @Input() isPinned?: boolean = false; 63 @Input() isInPinnedSection?: boolean = false; 64 @Input() isAlwaysCollapsed?: boolean; 65 66 @Output() toggleTreeChange = new EventEmitter<void>(); 67 @Output() expandTreeChange = new EventEmitter<boolean>(); 68 @Output() pinNodeChange = new EventEmitter<UiTreeNode>(); 69 70 collapseDiffClass = ''; 71 72 ngOnChanges() { 73 this.collapseDiffClass = this.updateCollapseDiffClass(); 74 } 75 76 isPropertiesTreeNode() { 77 return !(this.item instanceof HierarchyTreeNode); 78 } 79 80 showPinNodeIcon() { 81 return ( 82 (!this.isPropertiesTreeNode() && !UiTreeUtils.isParentNode(this.item.kind ?? '')) ?? false 83 ); 84 } 85 86 toggleTree(event: MouseEvent) { 87 if (!this.isAlwaysCollapsed) { 88 event.stopPropagation(); 89 this.toggleTreeChange.emit(); 90 } 91 } 92 93 showChevron() { 94 return !this.isLeaf && !this.flattened && !this.isInPinnedSection; 95 } 96 97 showLeafNodeIcon() { 98 return !this.showChevron() && !this.isInPinnedSection; 99 } 100 101 expandTree(event: MouseEvent) { 102 event.stopPropagation(); 103 this.expandTreeChange.emit(); 104 } 105 106 pinNode(event: MouseEvent) { 107 event.stopPropagation(); 108 this.pinNodeChange.emit(this.item); 109 } 110 111 updateCollapseDiffClass() { 112 if (this.isCollapsed) { 113 return ''; 114 } 115 116 const childrenDiffClasses = this.getAllDiffTypesOfChildren(this.item); 117 118 childrenDiffClasses.delete(DiffType.NONE); 119 childrenDiffClasses.delete(undefined); 120 121 if (childrenDiffClasses.size === 0) { 122 return ''; 123 } 124 if (childrenDiffClasses.size === 1) { 125 const diffType = childrenDiffClasses.values().next().value; 126 return diffType; 127 } 128 return DiffType.MODIFIED; 129 } 130 131 private getAllDiffTypesOfChildren(item: UiTreeNode) { 132 if (!item.children) { 133 return new Set(); 134 } 135 136 const classes = new Set(); 137 for (const child of item.children) { 138 if (child.diffType) { 139 classes.add(child.diffType); 140 } 141 for (const diffClass of this.getAllDiffTypesOfChildren(child)) { 142 classes.add(diffClass); 143 } 144 } 145 146 return classes; 147 } 148} 149