blob: 6ee155bd0ef90778d11b028bd71ec1607f77bc9c [file] [log] [blame]
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, Input, Inject, ElementRef } from "@angular/core";
import { UserOptions } from "viewers/common/user_options";
import { PersistentStore } from "common/persistent_store";
import { HierarchyTree, diffClass, isHighlighted, Tree } from "viewers/common/tree_utils";
import { nodeStyles } from "viewers/components/styles/node.styles";
import { ViewerEvents } from "viewers/common/viewer_events";
import { TraceType } from "common/trace/trace_type";
@Component({
selector: "hierarchy-view",
template: `
<mat-card-header class="view-header">
<div class="title-filter">
<span class="hierarchy-title">Hierarchy</span>
<mat-form-field class="filter-field">
<mat-label>Filter...</mat-label>
<input
matInput
[(ngModel)]="filterString"
(ngModelChange)="filterTree()"
name="filter"
/>
</mat-form-field>
</div>
<div class="view-controls">
<mat-checkbox
*ngFor="let option of objectKeys(userOptions)"
class="trace-box"
[(ngModel)]="userOptions[option].enabled"
(ngModelChange)="updateTree()"
>{{userOptions[option].name}}</mat-checkbox>
</div>
<div class="pinned-items" *ngIf="pinnedItems.length > 0">
<tree-node
*ngFor="let pinnedItem of pinnedItems"
class="node"
[class]="diffClass(pinnedItem)"
[class.selected]="isHighlighted(pinnedItem, highlightedItems)"
[class.clickable]="true"
[item]="pinnedItem"
[isPinned]="true"
[isInPinnedSection]="true"
(pinNodeChange)="pinnedItemChange($event)"
(click)="onPinnedNodeClick($event, pinnedItem)"
></tree-node>
</div>
</mat-card-header>
<mat-card-content class="hierarchy-content" [style]="maxHierarchyHeight()">
<div class="tree-wrapper">
<tree-view
class="tree-view"
*ngIf="tree"
[isFlattened]="isFlattened()"
[isShaded]="true"
[item]="tree"
[dependencies]="dependencies"
[store]="store"
[useGlobalCollapsedState]="true"
[itemsClickable]="true"
[highlightedItems]="highlightedItems"
[pinnedItems]="pinnedItems"
(highlightedItemChange)="highlightedItemChange($event)"
(pinnedItemChange)="pinnedItemChange($event)"
(selectedTreeChange)="selectedTreeChange($event)"
></tree-view>
</div>
</mat-card-content>
`,
styles: [
`
.view-header {
position: relative;
display: block;
width: 100%;
min-height: 3.75rem;
align-items: center;
border-bottom: 1px solid var(--default-border);
}
.title-filter {
position: relative;
display: flex;
align-items: center;
width: 100%;
margin-bottom: 12px;
}
.hierarchy-title {
font-weight: medium;
font-size: 16px;
}
.filter-field {
font-size: 16px;
transform: scale(0.7);
right: 0px;
position: absolute;
}
.view-controls {
display: inline-block;
font-size: 12px;
font-weight: normal;
margin-left: 5px;
}
.hierarchy-content {
display: flex;
flex-direction: column;
overflow-y: auto;
overflow-x: hidden;
}
.tree-view {
white-space: pre-line;
flex: 1 0 0;
height: 100%;
overflow-y: auto;
}
.pinned-items {
border: 2px solid yellow;
position: relative;
display: block;
width: 100%;
}
`,
nodeStyles
],
})
export class HierarchyComponent {
objectKeys = Object.keys;
filterString = "";
diffClass = diffClass;
isHighlighted = isHighlighted;
@Input() tree!: HierarchyTree | null;
@Input() dependencies: Array<TraceType> = [];
@Input() highlightedItems: Array<string> = [];
@Input() pinnedItems: Array<HierarchyTree> = [];
@Input() store!: PersistentStore;
@Input() userOptions: UserOptions = {};
constructor(
@Inject(ElementRef) private elementRef: ElementRef,
) {}
public isFlattened() {
return this.userOptions["flat"]?.enabled;
}
public maxHierarchyHeight() {
const headerHeight = this.elementRef.nativeElement.querySelector(".view-header").clientHeight;
return {
height: `${800 - headerHeight}px`
};
}
public onPinnedNodeClick(event: MouseEvent, pinnedItem: HierarchyTree) {
event.preventDefault();
if (window.getSelection()?.type === "range") {
return;
}
if (pinnedItem.id) this.highlightedItemChange(`${pinnedItem.id}`);
this.selectedTreeChange(pinnedItem);
}
public updateTree() {
const event: CustomEvent = new CustomEvent(
ViewerEvents.HierarchyUserOptionsChange,
{
bubbles: true,
detail: { userOptions: this.userOptions }
});
this.elementRef.nativeElement.dispatchEvent(event);
}
public filterTree() {
const event: CustomEvent = new CustomEvent(
ViewerEvents.HierarchyFilterChange,
{
bubbles: true,
detail: { filterString: this.filterString }
});
this.elementRef.nativeElement.dispatchEvent(event);
}
public highlightedItemChange(newId: string) {
const event: CustomEvent = new CustomEvent(
ViewerEvents.HighlightedChange,
{
bubbles: true,
detail: { id: newId }
});
this.elementRef.nativeElement.dispatchEvent(event);
}
public selectedTreeChange(item: Tree) {
if (!(item instanceof HierarchyTree)) {
return;
}
const event: CustomEvent = new CustomEvent(
ViewerEvents.SelectedTreeChange,
{
bubbles: true,
detail: { selectedItem: item }
});
this.elementRef.nativeElement.dispatchEvent(event);
}
public pinnedItemChange(item: Tree) {
if (!(item instanceof HierarchyTree)) {
return;
}
const event: CustomEvent = new CustomEvent(
ViewerEvents.HierarchyPinnedChange,
{
bubbles: true,
detail: { pinnedItem: item }
});
this.elementRef.nativeElement.dispatchEvent(event);
}
}