mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
refactor(devtools): refine and improve the new profiler layout (#62215)
Improve the styles and the components' layout in general. PR Close #62215
This commit is contained in:
+1
-1
@@ -19,7 +19,7 @@
|
||||
<br />
|
||||
<input
|
||||
type="file"
|
||||
class="ng-input"
|
||||
class="ng-input size-mid"
|
||||
(change)="importProfilerResults($event)"
|
||||
placeholder="Upload file"
|
||||
accept=".json"
|
||||
|
||||
+1
@@ -29,5 +29,6 @@ ng_project(
|
||||
"//:node_modules/@angular/material",
|
||||
"//devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/recording-timeline/record-formatter:record-formatter_rjs",
|
||||
"//devtools/projects/ng-devtools/src/lib/devtools-tabs/tab-update:tab-update_rjs",
|
||||
"//devtools/projects/ng-devtools/src/lib/shared/button:button_rjs",
|
||||
],
|
||||
)
|
||||
|
||||
+43
-41
@@ -1,44 +1,46 @@
|
||||
<mat-card class="bar-graph-container">
|
||||
<p class="txt-frames" matTooltip="{{ selectionLabel() }} / {{ frameCount() }}">
|
||||
{{ selectionLabel() }} / {{ frameCount() }}
|
||||
</p>
|
||||
<p class="selected-frame">{{ selectionLabel() }} / {{ frameCount() }}</p>
|
||||
|
||||
<button
|
||||
mat-icon-button
|
||||
(click)="move(-1)"
|
||||
[disabled]="disablePreviousFrameButton()"
|
||||
matTooltip="Select previous frame"
|
||||
aria-label="Select previous frame"
|
||||
>
|
||||
<mat-icon>chevron_left</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
ng-button
|
||||
btnType="icon"
|
||||
(click)="move(-1)"
|
||||
[disabled]="disablePreviousFrameButton()"
|
||||
matTooltip="Select previous frame"
|
||||
aria-label="Select previous frame"
|
||||
>
|
||||
<mat-icon>chevron_left</mat-icon>
|
||||
</button>
|
||||
|
||||
<cdk-virtual-scroll-viewport
|
||||
#barContainer
|
||||
orientation="horizontal"
|
||||
[itemSize]="itemWidth"
|
||||
class="bar-container"
|
||||
(mouseleave)="stopDragScrolling()"
|
||||
(mousedown)="startDragScroll($event)"
|
||||
(mouseup)="stopDragScrolling()"
|
||||
(mousemove)="dragScroll($event)"
|
||||
>
|
||||
<div
|
||||
*cdkVirtualFor="let d of graphData(); let i = index"
|
||||
[ngStyle]="d.style"
|
||||
class="frame-bar"
|
||||
[class.selected]="selectedFrameIndexes().has(i)"
|
||||
(click)="handleFrameSelection(i, $event)"
|
||||
></div>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
<cdk-virtual-scroll-viewport
|
||||
#barContainer
|
||||
orientation="horizontal"
|
||||
[itemSize]="itemWidth"
|
||||
class="bar-container"
|
||||
(mouseleave)="stopDragScrolling()"
|
||||
(mousedown)="startDragScroll($event)"
|
||||
(mouseup)="stopDragScrolling()"
|
||||
(mousemove)="dragScroll($event)"
|
||||
[class.drag-scrolling]="dragScrolling()"
|
||||
>
|
||||
<div
|
||||
*cdkVirtualFor="let d of graphData(); let i = index"
|
||||
[ngStyle]="d.style"
|
||||
class="frame-bar"
|
||||
[class.selected]="selectedFrameIndexes().has(i)"
|
||||
(click)="handleFrameSelection(i, $event)"
|
||||
[matTooltip]="'Time spent: ' + (d.frame.duration | number) + 'ms\nSource: ' + d.frame.source"
|
||||
[matTooltipShowDelay]="375"
|
||||
matTooltipClass="multiline-tooltip"
|
||||
></div>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
|
||||
<button
|
||||
mat-icon-button
|
||||
(click)="move(1)"
|
||||
[disabled]="disableNextFrameButton()"
|
||||
matTooltip="Select next frame"
|
||||
aria-label="Select next frame"
|
||||
>
|
||||
<mat-icon>chevron_right</mat-icon>
|
||||
</button>
|
||||
</mat-card>
|
||||
<button
|
||||
ng-button
|
||||
btnType="icon"
|
||||
(click)="move(1)"
|
||||
[disabled]="disableNextFrameButton()"
|
||||
matTooltip="Select next frame"
|
||||
aria-label="Select next frame"
|
||||
>
|
||||
<mat-icon>chevron_right</mat-icon>
|
||||
</button>
|
||||
|
||||
+52
-26
@@ -1,64 +1,90 @@
|
||||
@use '../../../../../styles/typography';
|
||||
|
||||
.bar-graph-container {
|
||||
padding: 2px;
|
||||
:host {
|
||||
position: relative;
|
||||
height: var(--max-bar-height);
|
||||
flex: 0 0 var(--max-bar-height);
|
||||
background-color: var(--octonary-contrast);
|
||||
border-bottom: 1px solid var(--color-separator);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
padding: 0.5rem;
|
||||
gap: 0.375rem;
|
||||
|
||||
.txt-frames {
|
||||
@extend %body-bold-01;
|
||||
padding: 0px;
|
||||
margin: 0px 10px;
|
||||
width: 150px;
|
||||
.selected-frame {
|
||||
@extend %body-01;
|
||||
position: absolute;
|
||||
top: 0.5rem;
|
||||
right: 2rem;
|
||||
margin: 0;
|
||||
padding: 0.1rem 0.375rem;
|
||||
text-align: center;
|
||||
background: var(--dynamic-transparent-01);
|
||||
backdrop-filter: blur(10px);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
border-radius: 0.75rem;
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.bar-container {
|
||||
max-width: calc(100vw - 150px);
|
||||
align-items: baseline;
|
||||
overflow-x: auto;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
cursor: pointer;
|
||||
|
||||
::ng-deep .cdk-virtual-scroll-content-wrapper {
|
||||
display: flex;
|
||||
&.drag-scrolling {
|
||||
/* We use an animation for the cursor to avoid
|
||||
applying the style when we perform a regular click. */
|
||||
animation: grabbing-cursor 1 forwards;
|
||||
animation-delay: 200ms;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.frame-bar {
|
||||
margin-left: 2.5px;
|
||||
margin-right: 2.5px;
|
||||
margin-top: 2px;
|
||||
::ng-deep .cdk-virtual-scroll-content-wrapper {
|
||||
display: flex;
|
||||
column-gap: 2.5px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.frame-bar {
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -2px;
|
||||
left: -2px;
|
||||
right: -2px;
|
||||
bottom: -2px;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
&:hover::before {
|
||||
background-color: var(--septenary-contrast);
|
||||
}
|
||||
|
||||
&.selected {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
margin-top: 0;
|
||||
padding-left: 0.5px;
|
||||
padding-right: 0.5px;
|
||||
|
||||
&.selected::before {
|
||||
background-color: var(--dynamic-blue-01);
|
||||
border: 2px solid var(--dynamic-blue-01);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
margin-bottom: 5px;
|
||||
@keyframes grabbing-cursor {
|
||||
from {
|
||||
cursor: default;
|
||||
}
|
||||
to {
|
||||
cursor: grabbing;
|
||||
}
|
||||
}
|
||||
|
||||
+38
-21
@@ -18,7 +18,9 @@ import {
|
||||
ElementRef,
|
||||
inject,
|
||||
input,
|
||||
linkedSignal,
|
||||
output,
|
||||
Signal,
|
||||
signal,
|
||||
viewChild,
|
||||
} from '@angular/core';
|
||||
@@ -28,13 +30,27 @@ import {TabUpdate} from '../../../tab-update/index';
|
||||
import {GraphNode} from '../record-formatter/record-formatter';
|
||||
import {MatIcon} from '@angular/material/icon';
|
||||
import {MatTooltip} from '@angular/material/tooltip';
|
||||
import {MatCard} from '@angular/material/card';
|
||||
import {NgStyle} from '@angular/common';
|
||||
import {MatIconButton} from '@angular/material/button';
|
||||
import {DecimalPipe, NgStyle} from '@angular/common';
|
||||
import {ProfilerFrame} from '../../../../../../../protocol';
|
||||
import {ButtonComponent} from '../../../../shared/button/button.component';
|
||||
|
||||
const ITEM_WIDTH = 25;
|
||||
const ITEM_GAP = 2.5;
|
||||
const ITEM_WIDTH = 18;
|
||||
const MAX_HEIGHT = 100;
|
||||
const DRAG_SCROLL_SPEED = 2;
|
||||
|
||||
/** Returns a linked signal that resets on source change. */
|
||||
function framesBoundSignal<T>(source: Signal<ProfilerFrame[]>, defaultValue: T) {
|
||||
return linkedSignal<ProfilerFrame[], T>({
|
||||
source,
|
||||
computation: (source, prev) => {
|
||||
if (source.length !== prev?.source.length) {
|
||||
return defaultValue;
|
||||
}
|
||||
return prev.value;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'ng-frame-selector',
|
||||
@@ -44,14 +60,14 @@ const MAX_HEIGHT = 100;
|
||||
:host { --max-bar-height: ${MAX_HEIGHT}px }
|
||||
`,
|
||||
imports: [
|
||||
MatCard,
|
||||
MatTooltip,
|
||||
MatIcon,
|
||||
MatIconButton,
|
||||
CdkVirtualScrollViewport,
|
||||
CdkFixedSizeVirtualScroll,
|
||||
CdkVirtualForOf,
|
||||
NgStyle,
|
||||
ButtonComponent,
|
||||
DecimalPipe,
|
||||
],
|
||||
})
|
||||
export class FrameSelectorComponent {
|
||||
@@ -64,9 +80,11 @@ export class FrameSelectorComponent {
|
||||
|
||||
readonly viewport = viewChild.required<CdkVirtualScrollViewport>(CdkVirtualScrollViewport);
|
||||
|
||||
readonly startFrameIndex = signal(-1);
|
||||
readonly endFrameIndex = signal(-1);
|
||||
readonly selectedFrameIndexes = signal(new Set<number>());
|
||||
readonly startFrameIndex = framesBoundSignal<number>(this.frames, -1);
|
||||
readonly endFrameIndex = framesBoundSignal<number>(this.frames, -1);
|
||||
readonly selectedFrameIndexes = framesBoundSignal<Set<number>>(this.frames, new Set());
|
||||
|
||||
readonly dragScrolling = signal(false);
|
||||
readonly frameCount = computed(() => this.frames().length);
|
||||
readonly disableNextFrameButton = computed(
|
||||
() => this.endFrameIndex() >= this.frameCount() - 1 || this.selectedFrameIndexes().size > 1,
|
||||
@@ -84,7 +102,7 @@ export class FrameSelectorComponent {
|
||||
|
||||
private _viewportScrollState = {scrollLeft: 0, xCoordinate: 0, isDragScrolling: false};
|
||||
|
||||
readonly itemWidth = ITEM_WIDTH;
|
||||
readonly itemWidth = ITEM_WIDTH + ITEM_GAP;
|
||||
|
||||
private readonly maxFrameDuration = computed(() =>
|
||||
this.frames().reduce((acc: number, frame: ProfilerFrame) => Math.max(acc, frame.duration), 0),
|
||||
@@ -212,10 +230,12 @@ export class FrameSelectorComponent {
|
||||
}
|
||||
|
||||
stopDragScrolling(): void {
|
||||
this.dragScrolling.set(false);
|
||||
this._viewportScrollState.isDragScrolling = false;
|
||||
}
|
||||
|
||||
startDragScroll(event: MouseEvent): void {
|
||||
this.dragScrolling.set(true);
|
||||
this._viewportScrollState = {
|
||||
xCoordinate: event.clientX,
|
||||
scrollLeft: this.viewport().elementRef.nativeElement.scrollLeft,
|
||||
@@ -228,10 +248,9 @@ export class FrameSelectorComponent {
|
||||
return;
|
||||
}
|
||||
|
||||
const dragScrollSpeed = 2;
|
||||
const dx = event.clientX - this._viewportScrollState.xCoordinate;
|
||||
this.viewport().elementRef.nativeElement.scrollLeft =
|
||||
this._viewportScrollState.scrollLeft - dx * dragScrollSpeed;
|
||||
this._viewportScrollState.scrollLeft - dx * DRAG_SCROLL_SPEED;
|
||||
}
|
||||
|
||||
private getBarStyles(frame: ProfilerFrame, multiplicationFactor: number): GraphNode {
|
||||
@@ -243,9 +262,7 @@ export class FrameSelectorComponent {
|
||||
|
||||
const style = {
|
||||
'background-image': `-webkit-linear-gradient(bottom, ${backgroundColor} ${colorPercentage}%, transparent ${colorPercentage}%)`,
|
||||
cursor: 'pointer',
|
||||
'min-width': '25px',
|
||||
width: '25px',
|
||||
width: ITEM_WIDTH + 'px',
|
||||
height: MAX_HEIGHT + 'px',
|
||||
};
|
||||
const toolTip = `${frame.source} TimeSpent: ${frame.duration.toFixed(3)}ms`;
|
||||
@@ -254,12 +271,12 @@ export class FrameSelectorComponent {
|
||||
|
||||
private getColorByFrameRate(framerate: number): string {
|
||||
if (framerate >= 60) {
|
||||
return '#d6f0d1';
|
||||
} else if (framerate < 60 && framerate >= 30) {
|
||||
return '#f2dca2';
|
||||
} else if (framerate < 30 && framerate >= 15) {
|
||||
return '#f9cc9d';
|
||||
return 'var(--dynamic-green-01)';
|
||||
} else if (60 > framerate && framerate >= 30) {
|
||||
return 'var(--dynamic-yellow-01)';
|
||||
} else if (30 > framerate && framerate >= 15) {
|
||||
return 'var(--dynamic-red-03)';
|
||||
}
|
||||
return '#fad1d1';
|
||||
return 'var(--dynamic-red-01)';
|
||||
}
|
||||
}
|
||||
|
||||
+6
-3
@@ -1,7 +1,10 @@
|
||||
<input
|
||||
matInput
|
||||
class="ng-input filter-input"
|
||||
class="ng-input size-mid filter-input"
|
||||
(keyup)="filter.emit($any($event.target).value)"
|
||||
placeholder="duration: >30 source: click"
|
||||
(focus)="filterPlaceholder.set('duration: >30 source: click')"
|
||||
(blur)="filterPlaceholder.set(FILTER_PLACEHOLDER)"
|
||||
[placeholder]="filterPlaceholder()"
|
||||
aria-label="Frames filter"
|
||||
/>
|
||||
<button ng-button (click)="exportProfile.emit()">Save Profile</button>
|
||||
<button ng-button size="mid" (click)="exportProfile.emit()">Save Profile</button>
|
||||
|
||||
+4
@@ -3,4 +3,8 @@
|
||||
justify-content: space-between;
|
||||
padding: 0.5rem;
|
||||
border-bottom: 1px solid var(--color-separator);
|
||||
|
||||
input {
|
||||
width: 10rem;
|
||||
}
|
||||
}
|
||||
|
||||
+8
-3
@@ -6,16 +6,21 @@
|
||||
* found in the LICENSE file at https://angular.dev/license
|
||||
*/
|
||||
|
||||
import {Component, output} from '@angular/core';
|
||||
import {ChangeDetectionStrategy, Component, output, signal} from '@angular/core';
|
||||
import {ButtonComponent} from '../../../../shared/button/button.component';
|
||||
|
||||
const FILTER_PLACEHOLDER = 'Filter';
|
||||
|
||||
@Component({
|
||||
selector: 'ng-recording-timeline-controls',
|
||||
templateUrl: './recording-timeline-controls.component.html',
|
||||
styleUrls: ['./recording-timeline-controls.component.scss'],
|
||||
imports: [ButtonComponent],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class RecordingTimelineControlsComponent {
|
||||
readonly exportProfile = output<void>();
|
||||
readonly filter = output<string>();
|
||||
protected readonly exportProfile = output<void>();
|
||||
protected readonly filter = output<string>();
|
||||
protected readonly filterPlaceholder = signal(FILTER_PLACEHOLDER);
|
||||
protected readonly FILTER_PLACEHOLDER = FILTER_PLACEHOLDER;
|
||||
}
|
||||
|
||||
+8
-5
@@ -3,25 +3,28 @@
|
||||
@if (!hasFrames() && !visualizing()) {
|
||||
<ng-recording-modal />
|
||||
}
|
||||
|
||||
@if (!hasFrames() && visualizing()) {
|
||||
<p class="info">There's no information to show.</p>
|
||||
}
|
||||
|
||||
@if (hasFrames()) {
|
||||
<ng-recording-timeline-controls
|
||||
(exportProfile)="exportProfile.emit($event)"
|
||||
(filter)="setFilter($event)"
|
||||
/>
|
||||
<ng-frame-selector [frames]="frames()" (selectFrames)="selectFrames.set($event.indexes)" />
|
||||
}
|
||||
|
||||
@if (hasFrames() && !frame) {
|
||||
<p class="info">Select a bar to preview a particular change detection cycle.</p>
|
||||
} @else if (hasFrames() && frame) {
|
||||
<ng-visualizer-controls
|
||||
[record]="frame!"
|
||||
[record]="frame"
|
||||
[estimatedFrameRate]="currentFrameRate()"
|
||||
[(visualizationMode)]="visualizationMode"
|
||||
[(changeDetection)]="changeDetection"
|
||||
/>
|
||||
}
|
||||
@if (hasFrames() && !frame) {
|
||||
<p class="info">Select a bar to preview a particular change detection cycle.</p>
|
||||
} @else if (hasFrames() && frame) {
|
||||
<ng-recording-visualizer
|
||||
[visualizationMode]="visualizationMode()"
|
||||
[frame]="frame"
|
||||
|
||||
-1
@@ -4,7 +4,6 @@
|
||||
display: block;
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
padding-bottom: 1rem;
|
||||
|
||||
::ng-deep {
|
||||
.as-split-gutter-icon {
|
||||
|
||||
-1
@@ -22,7 +22,6 @@ ng_project(
|
||||
"//packages/core",
|
||||
],
|
||||
deps = [
|
||||
"//:node_modules/@angular/material",
|
||||
"//devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/recording-timeline/visualization-mode:visualization-mode_rjs",
|
||||
"//devtools/projects/protocol:protocol_rjs",
|
||||
],
|
||||
|
||||
+37
-38
@@ -1,43 +1,42 @@
|
||||
<div
|
||||
class="details"
|
||||
[class.flame-details]="visualizationMode() === VisMode.FlameGraph"
|
||||
[class.bar-details]="visualizationMode() === VisMode.BarGraph"
|
||||
>
|
||||
@if (estimatedFrameRate() >= 60 && record()) {
|
||||
<label>
|
||||
Time spent: <span class="value">{{ record()?.duration | number }} ms</span>
|
||||
</label>
|
||||
}
|
||||
@if (estimatedFrameRate() < 60 && record()) {
|
||||
<label class="warning-label">
|
||||
Time spent: <span class="value">{{ record()?.duration | number }} ms</span>
|
||||
</label>
|
||||
}
|
||||
@if (estimatedFrameRate() < 60 && record()) {
|
||||
<label class="warning-label">
|
||||
@let frameRecord = record();
|
||||
|
||||
<div class="details">
|
||||
<p [class.warning-label]="estimatedFrameRate() < 60">
|
||||
Time spent: <span class="value">{{ frameRecord.duration | number }} ms</span>
|
||||
</p>
|
||||
@if (estimatedFrameRate() < 60) {
|
||||
<p class="warning-label">
|
||||
Frame rate: <span class="value">{{ estimatedFrameRate() }} fps</span>
|
||||
</label>
|
||||
</p>
|
||||
}
|
||||
@if (record()?.source && record()) {
|
||||
<label>
|
||||
Source: <span class="value">{{ record()?.source }}</span>
|
||||
</label>
|
||||
}
|
||||
@if (visualizationMode() === VisMode.FlameGraph) {
|
||||
<mat-checkbox [checked]="changeDetection()" (change)="changeDetection.set($event.checked)">
|
||||
Show only change detection
|
||||
</mat-checkbox>
|
||||
@if (frameRecord.source) {
|
||||
<p>
|
||||
Source: <span class="value">{{ frameRecord.source }}</span>
|
||||
</p>
|
||||
}
|
||||
</div>
|
||||
|
||||
<select [hidden]="!record()" (change)="onVisualizationChange($event)" class="ng-select">
|
||||
<option [value]="VisMode.FlameGraph" [selected]="visualizationMode() === VisMode.FlameGraph">
|
||||
Flame graph
|
||||
</option>
|
||||
<option [value]="VisMode.TreeMap" [selected]="visualizationMode() === VisMode.TreeMap">
|
||||
Tree map
|
||||
</option>
|
||||
<option [value]="VisMode.BarGraph" [selected]="visualizationMode() === VisMode.BarGraph">
|
||||
Bar chart
|
||||
</option>
|
||||
</select>
|
||||
<div class="controls">
|
||||
@if (visualizationMode() === VisMode.FlameGraph) {
|
||||
<input
|
||||
id="cd-only-checkbox"
|
||||
type="checkbox"
|
||||
[checked]="changeDetection()"
|
||||
(change)="changeDetection.set($any($event.target).checked)"
|
||||
/>
|
||||
<label for="cd-only-checkbox"> Show only change detection </label>
|
||||
<div class="ver-ruler"></div>
|
||||
}
|
||||
|
||||
<select #select (change)="onVisualizationChange(select.value)" class="ng-select size-mid">
|
||||
<option [value]="VisMode.FlameGraph" [selected]="visualizationMode() === VisMode.FlameGraph">
|
||||
Flame graph
|
||||
</option>
|
||||
<option [value]="VisMode.TreeMap" [selected]="visualizationMode() === VisMode.TreeMap">
|
||||
Tree map
|
||||
</option>
|
||||
<option [value]="VisMode.BarGraph" [selected]="visualizationMode() === VisMode.BarGraph">
|
||||
Bar chart
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
+22
-20
@@ -1,42 +1,44 @@
|
||||
:host {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: start;
|
||||
padding: 0.5rem;
|
||||
border-bottom: 1px solid var(--color-separator);
|
||||
|
||||
.details {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
-moz-user-select: text;
|
||||
-khtml-user-select: text;
|
||||
-webkit-user-select: text;
|
||||
-ms-user-select: text;
|
||||
user-select: text;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
||||
&.bar-details {
|
||||
min-height: 60px;
|
||||
}
|
||||
|
||||
&.flame-details {
|
||||
min-height: 85px;
|
||||
}
|
||||
|
||||
label {
|
||||
p {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
margin: 0;
|
||||
|
||||
&:not(:last-of-type) {
|
||||
margin-bottom: 0.2rem;
|
||||
}
|
||||
|
||||
&.warning-label {
|
||||
color: var(--dynamic-red-01);
|
||||
}
|
||||
}
|
||||
|
||||
.value {
|
||||
color: var(--dynamic-purple-01);
|
||||
.value {
|
||||
color: var(--dynamic-purple-01);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
display: block;
|
||||
.controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 24px;
|
||||
|
||||
#cd-only-checkbox {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
-9
@@ -8,7 +8,6 @@
|
||||
|
||||
import {ChangeDetectionStrategy, Component, input, model} from '@angular/core';
|
||||
import {DecimalPipe} from '@angular/common';
|
||||
import {MatCheckbox} from '@angular/material/checkbox';
|
||||
|
||||
import {ProfilerFrame} from '../../../../../../../protocol';
|
||||
import {VisualizationMode} from '../visualization-mode';
|
||||
@@ -17,20 +16,20 @@ import {VisualizationMode} from '../visualization-mode';
|
||||
selector: 'ng-visualizer-controls',
|
||||
templateUrl: './visualizer-controls.component.html',
|
||||
styleUrl: './visualizer-controls.component.scss',
|
||||
imports: [MatCheckbox, DecimalPipe],
|
||||
imports: [DecimalPipe],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class VisualizerControlsComponent {
|
||||
readonly record = input<ProfilerFrame>();
|
||||
readonly estimatedFrameRate = input.required<number>();
|
||||
protected readonly record = input.required<ProfilerFrame>();
|
||||
protected readonly estimatedFrameRate = input.required<number>();
|
||||
|
||||
readonly visualizationMode = model.required<VisualizationMode>();
|
||||
readonly changeDetection = model.required<boolean>();
|
||||
protected readonly visualizationMode = model.required<VisualizationMode>();
|
||||
protected readonly changeDetection = model.required<boolean>();
|
||||
|
||||
VisMode = VisualizationMode;
|
||||
protected readonly VisMode = VisualizationMode;
|
||||
|
||||
onVisualizationChange(e: Event) {
|
||||
const selected = parseInt((e.target as HTMLSelectElement).value, 10);
|
||||
protected onVisualizationChange(value: string) {
|
||||
const selected = parseInt(value, 10);
|
||||
this.visualizationMode.set(selected);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
cursor: pointer;
|
||||
padding: 0.375rem 0.75rem;
|
||||
|
||||
&.size-mid {
|
||||
padding: 0.25rem 0.75rem;
|
||||
}
|
||||
|
||||
&.size-compact {
|
||||
padding: 0.1rem 0.5rem;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
import {ChangeDetectionStrategy, Component, input} from '@angular/core';
|
||||
|
||||
type ButtonType = 'primary' | 'icon';
|
||||
type ButtonSize = 'standard' | 'compact';
|
||||
type ButtonSize = 'standard' | 'mid' | 'compact';
|
||||
|
||||
@Component({
|
||||
selector: 'button[ng-button]',
|
||||
@@ -21,6 +21,7 @@ type ButtonSize = 'standard' | 'compact';
|
||||
'[class.type-primary]': `btnType() === 'primary'`,
|
||||
'[class.type-icon]': `btnType() === 'icon'`,
|
||||
'[class.size-compact]': `size() === 'compact'`,
|
||||
'[class.size-mid]': `size() === 'mid'`,
|
||||
},
|
||||
})
|
||||
export class ButtonComponent {
|
||||
|
||||
@@ -31,6 +31,14 @@ $_colors: (
|
||||
purple-02: oklch(57.9% 0.26 295),
|
||||
purple-03: oklch(76% 0.15 305),
|
||||
|
||||
/* Yellows */
|
||||
yellow-01: oklch(70% 0.14 81),
|
||||
yellow-02: oklch(83% 0.14 81),
|
||||
|
||||
/* Oranges */
|
||||
orange-01: oklch(69% 0.17 50),
|
||||
orange-02: oklch(79% 0.17 50),
|
||||
|
||||
/* Grayscale */
|
||||
gray-1000: oklch(16.93% 0 0),
|
||||
gray-900: oklch(19.37% 0 0),
|
||||
@@ -72,6 +80,8 @@ $_colors: (
|
||||
--dynamic-blue-01: var(--blue-05);
|
||||
--dynamic-blue-02: var(--blue-02);
|
||||
--dynamic-purple-01: var(--purple-01);
|
||||
--dynamic-yellow-01: var(--yellow-01);
|
||||
--dynamic-orange-01: var(--orange-01);
|
||||
|
||||
--dynamic-transparent-01: var(--transparent-04);
|
||||
--dynamic-transparent-02: var(--transparent-02);
|
||||
@@ -114,6 +124,8 @@ $_colors: (
|
||||
--dynamic-blue-01: var(--blue-01);
|
||||
--dynamic-blue-02: var(--blue-03);
|
||||
--dynamic-purple-01: var(--purple-03);
|
||||
--dynamic-yellow-01: var(--yellow-02);
|
||||
--dynamic-orange-01: var(--orange-02);
|
||||
|
||||
--dynamic-transparent-01: var(--transparent-03);
|
||||
--dynamic-transparent-02: var(--transparent-01);
|
||||
|
||||
@@ -143,6 +143,17 @@ mat-form-field {
|
||||
}
|
||||
}
|
||||
|
||||
.multiline-tooltip {
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.ver-ruler {
|
||||
width: 1px;
|
||||
height: 100%;
|
||||
background-color: var(--color-separator);
|
||||
margin-inline: 0.875rem;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,10 @@
|
||||
color: var(--tertiary-contrast);
|
||||
}
|
||||
|
||||
&[type='checkbox'] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&.size-mid {
|
||||
padding: 0.25rem 0.75rem;
|
||||
}
|
||||
@@ -56,3 +60,7 @@
|
||||
.ng-textarea {
|
||||
border-radius: 0.75rem;
|
||||
}
|
||||
|
||||
label[for] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user