mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
refactor(devtools): remove dependency on ngx-charts
This commit is contained in:
@@ -44,7 +44,6 @@
|
||||
"@angular/material": "~9.2.0",
|
||||
"@angular/platform-browser": "github:angular/platform-browser-builds#77ea10310c138f2fedf5fd625b567d8097ef8bf8",
|
||||
"@angular/platform-browser-dynamic": "github:angular/platform-browser-dynamic-builds#d4a24f6679d86655a650ded46d78b4e290529f94",
|
||||
"@swimlane/ngx-charts": "^14.0.0",
|
||||
"memo-decorator": "^2.0.1",
|
||||
"ngx-flamegraph": "0.0.7",
|
||||
"rxjs": "~6.5.3",
|
||||
|
||||
+2
-5
@@ -5,7 +5,7 @@ import memo from 'memo-decorator';
|
||||
export interface BargraphNode {
|
||||
parents: ElementProfile[];
|
||||
value: number;
|
||||
name: string;
|
||||
label: string;
|
||||
original: ElementProfile;
|
||||
}
|
||||
|
||||
@@ -31,11 +31,10 @@ export class BarGraphFormatter extends RecordFormatter<BargraphNode[]> {
|
||||
timeSpent += super.getValue(element);
|
||||
|
||||
element.directives.forEach((dir) => {
|
||||
const suffix = addSpaces(nodes.length);
|
||||
const innerNode: BargraphNode = {
|
||||
parents,
|
||||
value: super.getDirectiveValue(dir),
|
||||
name: dir.name + suffix,
|
||||
label: dir.name,
|
||||
original: element,
|
||||
};
|
||||
nodes.push(innerNode);
|
||||
@@ -44,5 +43,3 @@ export class BarGraphFormatter extends RecordFormatter<BargraphNode[]> {
|
||||
return timeSpent;
|
||||
}
|
||||
}
|
||||
|
||||
const addSpaces = (length: number) => ' '.repeat(length);
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
<div class="wrapper">
|
||||
<div *ngFor="let bar of internalData; let i = index" class="element-wrapper">
|
||||
<div (click)="select(i)" class="bar" [style.backgroundColor]="color" [style.width.%]="bar.value">
|
||||
<span>{{ bar.label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
.bar {
|
||||
height: 20px;
|
||||
width: 0%;
|
||||
margin-bottom: 7px;
|
||||
padding-top: 3px;
|
||||
padding-bottom: 3px;
|
||||
opacity: 0.8;
|
||||
cursor: pointer;
|
||||
transition: opacity 0.3s ease-out, width 0.3s ease;
|
||||
}
|
||||
|
||||
.bar span {
|
||||
margin-left: 10px;
|
||||
max-width: calc(100% - 20px);
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
padding-top: 3px;
|
||||
}
|
||||
|
||||
.bar:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
:host,
|
||||
.wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
|
||||
interface Data {
|
||||
label: string;
|
||||
value: number;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'ng-bar-chart',
|
||||
templateUrl: './bar-chart.component.html',
|
||||
styleUrls: ['./bar-chart.component.scss'],
|
||||
})
|
||||
export class BarChartComponent {
|
||||
@Input() set data(nodes: Data[]) {
|
||||
this._originals = nodes;
|
||||
this.internalData = [];
|
||||
const max = nodes.reduce((a: number, c) => Math.max(a, c.value), -Infinity);
|
||||
for (const node of nodes) {
|
||||
this.internalData.push({
|
||||
label: node.label,
|
||||
value: (node.value / max) * 100,
|
||||
});
|
||||
}
|
||||
}
|
||||
@Input() color: string;
|
||||
@Output() pick = new EventEmitter<Data>();
|
||||
private _originals: Data[];
|
||||
|
||||
internalData: Data[] = [];
|
||||
|
||||
select(idx: number): void {
|
||||
this.pick.emit(this._originals[idx]);
|
||||
}
|
||||
}
|
||||
+3
-21
@@ -1,31 +1,13 @@
|
||||
<div class="level-profile-wrapper" [class.split-width]="selectedEntry">
|
||||
<ngx-charts-bar-horizontal
|
||||
[view]="view"
|
||||
[scheme]="colorScheme"
|
||||
[results]="profileRecords"
|
||||
[xAxis]="true"
|
||||
[yAxis]="true"
|
||||
[roundEdges]="false"
|
||||
(select)="selectNode($event)"
|
||||
>
|
||||
</ngx-charts-bar-horizontal>
|
||||
<ng-bar-chart (pick)="selectNode($event)" [color]="barColor" [data]="profileRecords"> </ng-bar-chart>
|
||||
</div>
|
||||
|
||||
<div class="selected-entry" *ngIf="selectedEntry">
|
||||
<header>{{ selectedEntry.name }} details</header>
|
||||
<header>{{ selectedEntry.label }} details</header>
|
||||
<section class="entry-statistics">
|
||||
<div class="txt-total-time"><label>Total time spent:</label> {{ selectedEntry.value | number }}ms</div>
|
||||
<div *ngIf="selectedEntry.value > 0">
|
||||
<ngx-charts-pie-chart
|
||||
legendTitle=""
|
||||
[view]="pieChartView"
|
||||
[scheme]="pieChartColorScheme"
|
||||
[results]="pieChartData"
|
||||
[tooltipText]="formatToolTip"
|
||||
legend="true"
|
||||
legendPosition="bottom"
|
||||
>
|
||||
</ngx-charts-pie-chart>
|
||||
<ng-execution-details [data]="selectedDirectives"> </ng-execution-details>
|
||||
</div>
|
||||
<div *ngIf="parentHierarchy.length > 0">
|
||||
<div class="txt-total-time"><label>Parent Hierarchy</label></div>
|
||||
|
||||
+12
-17
@@ -3,7 +3,8 @@ import { BargraphNode, BarGraphFormatter } from '../../record-formatter/bargraph
|
||||
import { ProfilerFrame } from 'protocol';
|
||||
|
||||
export interface GraphNode {
|
||||
name: string;
|
||||
directive: string;
|
||||
method: string;
|
||||
value: number;
|
||||
}
|
||||
|
||||
@@ -13,17 +14,10 @@ export interface GraphNode {
|
||||
styleUrls: ['./bargraph-visualizer.component.scss'],
|
||||
})
|
||||
export class BargraphVisualizerComponent {
|
||||
view: [number, number] = [600, 600];
|
||||
colorScheme = {
|
||||
domain: ['rgb(237, 213, 94)'],
|
||||
};
|
||||
barColor = 'rgb(237, 213, 94)';
|
||||
profileRecords: BargraphNode[];
|
||||
selectedEntry: BargraphNode | null = null;
|
||||
pieChartView: [number, number] = [235, 200];
|
||||
pieChartColorScheme = {
|
||||
domain: ['#E71D36', '#2EC4B6', '#FF9F1C', '#011627'],
|
||||
};
|
||||
pieChartData: GraphNode[] = [];
|
||||
selectedDirectives: GraphNode[] = [];
|
||||
parentHierarchy: { name: string }[] = [];
|
||||
|
||||
private _formatter = new BarGraphFormatter();
|
||||
@@ -31,22 +25,23 @@ export class BargraphVisualizerComponent {
|
||||
@Input() set frame(data: ProfilerFrame) {
|
||||
this.profileRecords = this._formatter.formatFrame(data);
|
||||
this.selectedEntry = null;
|
||||
this.view = [1000, this.profileRecords.length * 30];
|
||||
}
|
||||
|
||||
formatPieChartData(bargraphNode: BargraphNode): GraphNode[] {
|
||||
const graphData: GraphNode[] = [];
|
||||
bargraphNode.original.directives.forEach((node) => {
|
||||
const {changeDetection} = node;
|
||||
const { changeDetection } = node;
|
||||
if (changeDetection) {
|
||||
graphData.push({
|
||||
name: `${node.name} changeDetection`,
|
||||
directive: node.name,
|
||||
method: 'changes',
|
||||
value: parseFloat(changeDetection.toFixed(2)),
|
||||
});
|
||||
}
|
||||
Object.keys(node.lifecycle).forEach((key) => {
|
||||
graphData.push({
|
||||
name: `${node.name} ${key}`,
|
||||
directive: node.name,
|
||||
method: key,
|
||||
value: +node.lifecycle[key].toFixed(2),
|
||||
});
|
||||
});
|
||||
@@ -54,13 +49,13 @@ export class BargraphVisualizerComponent {
|
||||
return graphData;
|
||||
}
|
||||
|
||||
selectNode(data: any): void {
|
||||
const index = this.profileRecords.findIndex((element) => element.name === data.name);
|
||||
selectNode(data: BargraphNode): void {
|
||||
const index = this.profileRecords.findIndex((element) => element.label === data.label);
|
||||
this.selectedEntry = this.profileRecords[index];
|
||||
this.parentHierarchy = this.selectedEntry.parents.map((element) => {
|
||||
return { name: element.directives[0].name };
|
||||
});
|
||||
this.pieChartData = this.formatPieChartData(this.selectedEntry);
|
||||
this.selectedDirectives = this.formatPieChartData(this.selectedEntry);
|
||||
}
|
||||
|
||||
formatToolTip(data: any): string {
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
<table>
|
||||
<thead>
|
||||
<th class="name">Directive</th>
|
||||
<th class="method">Method</th>
|
||||
<th class="value">Time</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let entry of data">
|
||||
<td class="name">
|
||||
{{ entry.directive }}
|
||||
</td>
|
||||
<td class="method">
|
||||
{{ entry.method }}
|
||||
</td>
|
||||
<td class="value">
|
||||
{{ entry.value }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
.name {
|
||||
width: 80px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.value {
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
.method {
|
||||
width: 50px;
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
|
||||
export interface GraphNode {
|
||||
directive: string;
|
||||
method: string;
|
||||
value: number;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'ng-execution-details',
|
||||
templateUrl: './execution-details.component.html',
|
||||
styleUrls: ['./execution-details.component.scss'],
|
||||
})
|
||||
export class ExecutionDetailsComponent {
|
||||
@Input() data: GraphNode[];
|
||||
}
|
||||
+1
-10
@@ -20,16 +20,7 @@
|
||||
<label>Did not run change detection</label>
|
||||
</div>
|
||||
<div *ngIf="selectedEntry.value > 0">
|
||||
<ngx-charts-pie-chart
|
||||
legendTitle=""
|
||||
[view]="view"
|
||||
[scheme]="colorScheme"
|
||||
[results]="graphData"
|
||||
[tooltipText]="formatToolTip"
|
||||
legend="true"
|
||||
legendPosition="bottom"
|
||||
>
|
||||
</ngx-charts-pie-chart>
|
||||
<ng-execution-details [data]="graphData"></ng-execution-details>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
+6
-3
@@ -10,7 +10,8 @@ import { RawData } from 'ngx-flamegraph/lib/utils';
|
||||
import { ProfilerFrame } from 'protocol';
|
||||
|
||||
export interface GraphNode {
|
||||
name: string;
|
||||
directive: string;
|
||||
method: string;
|
||||
value: number;
|
||||
}
|
||||
|
||||
@@ -60,13 +61,15 @@ export class FlamegraphVisualizerComponent {
|
||||
const changeDetection = node.changeDetection;
|
||||
if (changeDetection !== undefined) {
|
||||
graphData.push({
|
||||
name: `${node.name} changeDetection`,
|
||||
directive: node.name,
|
||||
method: 'changes',
|
||||
value: parseFloat(changeDetection.toFixed(2)),
|
||||
});
|
||||
}
|
||||
Object.keys(node.lifecycle).forEach((key) => {
|
||||
graphData.push({
|
||||
name: `${node.name} ${key}`,
|
||||
directive: node.name,
|
||||
method: key,
|
||||
value: +node.lifecycle[key].toFixed(2),
|
||||
});
|
||||
});
|
||||
|
||||
+9
-4
@@ -1,21 +1,26 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { TimelineVisualizerComponent } from './timeline-visualizer.component';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
|
||||
import { NgxFlamegraphModule } from 'ngx-flamegraph';
|
||||
import { NgxChartsModule } from '@swimlane/ngx-charts';
|
||||
|
||||
import { FlamegraphVisualizerComponent } from './flamegraph-visualizer/flamegraph-visualizer.component';
|
||||
import { BargraphVisualizerComponent } from './bargraph-visualizer/bargraph-visualizer.component';
|
||||
import { TreeMapVisualizerComponent } from './tree-map-visualizer/tree-map-visualizer.component';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { TimelineVisualizerComponent } from './timeline-visualizer.component';
|
||||
import { BarChartComponent } from './bar-chart/bar-chart.component';
|
||||
import { ExecutionDetailsComponent } from './execution-details/execution-details.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
ExecutionDetailsComponent,
|
||||
BarChartComponent,
|
||||
TimelineVisualizerComponent,
|
||||
FlamegraphVisualizerComponent,
|
||||
TreeMapVisualizerComponent,
|
||||
BargraphVisualizerComponent,
|
||||
],
|
||||
imports: [CommonModule, NgxFlamegraphModule, NgxChartsModule, MatCheckboxModule],
|
||||
imports: [CommonModule, NgxFlamegraphModule, MatCheckboxModule],
|
||||
exports: [TimelineVisualizerComponent],
|
||||
})
|
||||
export class RecordingVisualizerModule {}
|
||||
|
||||
Reference in New Issue
Block a user