mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
feat(devtools): tone down obtrusive elements in the Injector Tree tab (#59499)
Some of the elements are a bit too large so they have been shrunken to fit in with the rest of the UI. PR Close #59499
This commit is contained in:
parent
47e48b44dc
commit
0bb81c5ab4
9 changed files with 313 additions and 232 deletions
|
|
@ -1,7 +1,7 @@
|
|||
load("//devtools/tools:ng_module.bzl", "ng_module")
|
||||
load("@io_bazel_rules_sass//:defs.bzl", "sass_binary")
|
||||
load("//devtools/tools:typescript.bzl", "ts_library", "ts_test_library")
|
||||
load("//devtools/tools:defaults.bzl", "karma_web_test_suite")
|
||||
load("//devtools/tools:ng_module.bzl", "ng_module")
|
||||
load("//devtools/tools:typescript.bzl", "ts_library", "ts_test_library")
|
||||
|
||||
package(default_visibility = ["//:__subpackages__"])
|
||||
|
||||
|
|
@ -20,10 +20,10 @@ ng_module(
|
|||
"injector-tree.component.html",
|
||||
],
|
||||
deps = [
|
||||
":injector_providers",
|
||||
":injector_tree_fns",
|
||||
"//devtools/projects/ng-devtools/src/lib/devtools-tabs/dependency-injection:injector_tree_visualizer",
|
||||
"//devtools/projects/ng-devtools/src/lib/devtools-tabs/dependency-injection:resolution_path",
|
||||
"//devtools/projects/ng-devtools/src/lib/devtools-tabs/injector-tree/injector-providers",
|
||||
"//devtools/projects/ng-devtools/src/lib/vendor/angular-split",
|
||||
"//devtools/projects/protocol",
|
||||
"//packages/common",
|
||||
|
|
@ -35,21 +35,6 @@ ng_module(
|
|||
],
|
||||
)
|
||||
|
||||
ng_module(
|
||||
name = "injector_providers",
|
||||
srcs = [
|
||||
"injector-providers.component.ts",
|
||||
],
|
||||
deps = [
|
||||
"//devtools/projects/ng-devtools/src/lib/devtools-tabs/dependency-injection:resolution_path",
|
||||
"//devtools/projects/protocol",
|
||||
"//packages/animations",
|
||||
"//packages/common",
|
||||
"//packages/core",
|
||||
"@npm//@angular/material",
|
||||
],
|
||||
)
|
||||
|
||||
karma_web_test_suite(
|
||||
name = "test",
|
||||
deps = [
|
||||
|
|
|
|||
|
|
@ -1,205 +0,0 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.dev/license
|
||||
*/
|
||||
|
||||
import {Component, computed, inject, input, signal} from '@angular/core';
|
||||
import {MatOption} from '@angular/material/core';
|
||||
import {MatFormField, MatLabel} from '@angular/material/form-field';
|
||||
import {MatIcon} from '@angular/material/icon';
|
||||
import {MatInput} from '@angular/material/input';
|
||||
import {MatSelect} from '@angular/material/select';
|
||||
import {MatTableModule} from '@angular/material/table';
|
||||
import {MatTooltip} from '@angular/material/tooltip';
|
||||
import {Events, MessageBus, SerializedInjector, SerializedProviderRecord} from 'protocol';
|
||||
|
||||
@Component({
|
||||
selector: 'ng-injector-providers',
|
||||
template: `
|
||||
<h1 class="providers-title">Providers for {{ injector()?.name }}</h1>
|
||||
@if (injector()) {
|
||||
<div class="injector-providers">
|
||||
<mat-form-field appearance="fill" class="form-field-spacer">
|
||||
<mat-label>Search by token</mat-label>
|
||||
<input
|
||||
type="text"
|
||||
matInput
|
||||
placeholder="Provider token"
|
||||
(input)="searchToken.set($event.target.value)"
|
||||
[value]="searchToken()"
|
||||
/>
|
||||
<mat-icon matSuffix (click)="searchToken.set('')">close</mat-icon>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="form-field-spacer">
|
||||
<mat-label>Search by type</mat-label>
|
||||
<mat-select [value]="searchType()" (selectionChange)="searchType.set($event.value)">
|
||||
<mat-option>None</mat-option>
|
||||
@for (type of providerTypes; track type) {
|
||||
<mat-option [value]="type">{{ $any(providerTypeToLabel)[type] }}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
@if (visibleProviders().length > 0) {
|
||||
<table mat-table [dataSource]="visibleProviders()" class="mat-elevation-z4">
|
||||
<ng-container matColumnDef="token">
|
||||
<th mat-header-cell *matHeaderCellDef><h3 class="column-title">Token</h3></th>
|
||||
<td mat-cell *matCellDef="let provider">{{ provider.token }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="type">
|
||||
<th mat-header-cell *matHeaderCellDef><h3 class="column-title">Type</h3></th>
|
||||
<td mat-cell *matCellDef="let provider">
|
||||
@if (provider.type === 'multi') { multi (x{{ provider.index.length }}) } @else {
|
||||
{{ $any(providerTypeToLabel)[provider.type] }}
|
||||
}
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="isViewProvider">
|
||||
<th mat-header-cell *matHeaderCellDef><h3 class="column-title">Is View Provider</h3></th>
|
||||
<td mat-cell *matCellDef="let provider">
|
||||
<mat-icon>{{ provider.isViewProvider ? 'check_circle' : 'cancel' }}</mat-icon>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="log">
|
||||
<th mat-header-cell *matHeaderCellDef><h3 class="column-title"></h3></th>
|
||||
<td mat-cell *matCellDef="let provider">
|
||||
<mat-icon
|
||||
matTooltipPosition="left"
|
||||
matTooltip="Log provider in console"
|
||||
class="select"
|
||||
(click)="select(provider)"
|
||||
>send</mat-icon
|
||||
>
|
||||
</td>
|
||||
</ng-container>
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
|
||||
</table>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
`,
|
||||
styles: [
|
||||
`
|
||||
.select {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.form-field-spacer {
|
||||
margin: 0 4px 0 4px;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.column-title {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
tr.example-detail-row {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.example-element-row td {
|
||||
border-bottom-width: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.example-element-detail {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.example-element-diagram {
|
||||
min-width: 80px;
|
||||
border: 2px solid black;
|
||||
padding: 8px;
|
||||
font-weight: lighter;
|
||||
margin: 8px 0;
|
||||
height: 104px;
|
||||
}
|
||||
|
||||
.example-element-symbol {
|
||||
font-weight: bold;
|
||||
font-size: 40px;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.example-element-description {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.example-element-description-attribution {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
:host-context(.dark-theme) {
|
||||
.providers-title {
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
`,
|
||||
],
|
||||
imports: [
|
||||
MatTableModule,
|
||||
MatIcon,
|
||||
MatTooltip,
|
||||
MatInput,
|
||||
MatSelect,
|
||||
MatFormField,
|
||||
MatLabel,
|
||||
MatOption,
|
||||
],
|
||||
})
|
||||
export class InjectorProvidersComponent {
|
||||
readonly injector = input.required<SerializedInjector>();
|
||||
readonly providers = input<SerializedProviderRecord[]>([]);
|
||||
|
||||
readonly searchToken = signal('');
|
||||
readonly searchType = signal('');
|
||||
readonly visibleProviders = computed(() => {
|
||||
const searchToken = this.searchToken().toLowerCase();
|
||||
const searchType = this.searchType();
|
||||
|
||||
const predicates: ((provider: SerializedProviderRecord) => boolean)[] = [];
|
||||
searchToken &&
|
||||
predicates.push((provider) => provider.token.toLowerCase().includes(searchToken));
|
||||
searchType && predicates.push((provider) => provider.type === searchType);
|
||||
|
||||
return this.providers().filter((provider) =>
|
||||
predicates.every((predicate) => predicate(provider)),
|
||||
);
|
||||
});
|
||||
|
||||
providerTypeToLabel = {
|
||||
type: 'Type',
|
||||
existing: 'useExisting',
|
||||
factory: 'useFactory',
|
||||
class: 'useClass',
|
||||
value: 'useValue',
|
||||
};
|
||||
|
||||
providerTypes = Object.keys(this.providerTypeToLabel);
|
||||
|
||||
messageBus = inject<MessageBus<Events>>(MessageBus);
|
||||
|
||||
select(row: SerializedProviderRecord) {
|
||||
const {id, type, name} = this.injector();
|
||||
this.messageBus.emit('logProvider', [{id, type, name}, row]);
|
||||
}
|
||||
|
||||
get displayedColumns(): string[] {
|
||||
if (this.injector()?.type === 'element') {
|
||||
return ['token', 'type', 'isViewProvider', 'log'];
|
||||
}
|
||||
return ['token', 'type', 'log'];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
load("@io_bazel_rules_sass//:defs.bzl", "sass_binary")
|
||||
load("//devtools/tools:ng_module.bzl", "ng_module")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
sass_binary(
|
||||
name = "injector_providers_component_styles",
|
||||
src = "injector-providers.component.scss",
|
||||
include_paths = [
|
||||
"external/npm/node_modules",
|
||||
],
|
||||
deps = ["//devtools:material_sass_deps"],
|
||||
)
|
||||
|
||||
ng_module(
|
||||
name = "injector-providers",
|
||||
srcs = [
|
||||
"injector-providers.component.ts",
|
||||
],
|
||||
angular_assets = [
|
||||
"injector-providers.component.html",
|
||||
":injector_providers_component_styles",
|
||||
],
|
||||
deps = [
|
||||
"//devtools/projects/ng-devtools/src/lib/devtools-tabs/dependency-injection:resolution_path",
|
||||
"//devtools/projects/protocol",
|
||||
"//packages/animations",
|
||||
"//packages/common",
|
||||
"//packages/core",
|
||||
"@npm//@angular/material",
|
||||
],
|
||||
)
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
<h2 class="providers-title">Providers for {{ injector()?.name }}</h2>
|
||||
@if (injector()) {
|
||||
<div class="injector-providers">
|
||||
<div class="filter">
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Search by token</mat-label>
|
||||
<input
|
||||
type="text"
|
||||
matInput
|
||||
placeholder="Provider token"
|
||||
(input)="searchToken.set($event.target.value)"
|
||||
[value]="searchToken()"
|
||||
/>
|
||||
<mat-icon matSuffix (click)="searchToken.set('')">close</mat-icon>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Search by type</mat-label>
|
||||
<mat-select [value]="searchType()" (selectionChange)="searchType.set($event.value)">
|
||||
<mat-option>None</mat-option>
|
||||
@for (type of providerTypes; track type) {
|
||||
<mat-option [value]="type">{{ $any(providerTypeToLabel)[type] }}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
@if (visibleProviders().length > 0) {
|
||||
<table mat-table [dataSource]="visibleProviders()" class="mat-elevation-z4">
|
||||
<ng-container matColumnDef="token">
|
||||
<th mat-header-cell *matHeaderCellDef><h3 class="column-title">Token</h3></th>
|
||||
<td mat-cell *matCellDef="let provider">{{ provider.token }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="type">
|
||||
<th mat-header-cell *matHeaderCellDef><h3 class="column-title">Type</h3></th>
|
||||
<td mat-cell *matCellDef="let provider">
|
||||
@if (provider.type === 'multi') {
|
||||
multi (x{{ provider.index.length }})
|
||||
} @else {
|
||||
{{ $any(providerTypeToLabel)[provider.type] }}
|
||||
}
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="isViewProvider">
|
||||
<th mat-header-cell *matHeaderCellDef><h3 class="column-title">Is View Provider</h3></th>
|
||||
<td mat-cell *matCellDef="let provider">
|
||||
<mat-icon>{{ provider.isViewProvider ? 'check_circle' : 'cancel' }}</mat-icon>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="log">
|
||||
<th mat-header-cell *matHeaderCellDef><h3 class="column-title"></h3></th>
|
||||
<td mat-cell *matCellDef="let provider">
|
||||
<mat-icon
|
||||
matTooltipPosition="left"
|
||||
matTooltip="Log provider in console"
|
||||
class="select"
|
||||
(click)="select(provider)"
|
||||
>send</mat-icon
|
||||
>
|
||||
</td>
|
||||
</ng-container>
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
|
||||
</table>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
@use '@angular/material' as mat;
|
||||
|
||||
h2 {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.select {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.filter {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
|
||||
mat-form-field {
|
||||
@include mat.form-field-density(-5);
|
||||
@include mat.form-field-overrides(
|
||||
(
|
||||
container-text-size: 0.8rem,
|
||||
outlined-label-text-size: 0.8rem,
|
||||
)
|
||||
);
|
||||
|
||||
&:first-child {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
--mat-table-row-item-label-text-size: 0.8rem;
|
||||
--mat-table-header-container-height: 42px;
|
||||
--mat-table-row-item-container-height: 42px;
|
||||
}
|
||||
|
||||
.column-title {
|
||||
margin: 0;
|
||||
font-size: 0.8rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
tr.example-detail-row {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.example-element-row td {
|
||||
border-bottom-width: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.example-element-detail {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.example-element-diagram {
|
||||
min-width: 80px;
|
||||
border: 2px solid black;
|
||||
padding: 8px;
|
||||
font-weight: lighter;
|
||||
margin: 8px 0;
|
||||
height: 104px;
|
||||
}
|
||||
|
||||
.example-element-symbol {
|
||||
font-weight: bold;
|
||||
font-size: 40px;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.example-element-description {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.example-element-description-attribution {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
:host-context(.dark-theme) {
|
||||
.providers-title {
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.dev/license
|
||||
*/
|
||||
|
||||
import {Component, computed, inject, input, signal} from '@angular/core';
|
||||
import {MatOption} from '@angular/material/core';
|
||||
import {MatFormField, MatLabel} from '@angular/material/form-field';
|
||||
import {MatIcon} from '@angular/material/icon';
|
||||
import {MatInput} from '@angular/material/input';
|
||||
import {MatSelect} from '@angular/material/select';
|
||||
import {MatTableModule} from '@angular/material/table';
|
||||
import {MatTooltip} from '@angular/material/tooltip';
|
||||
import {Events, MessageBus, SerializedInjector, SerializedProviderRecord} from 'protocol';
|
||||
|
||||
@Component({
|
||||
selector: 'ng-injector-providers',
|
||||
templateUrl: './injector-providers.component.html',
|
||||
styleUrl: './injector-providers.component.scss',
|
||||
imports: [
|
||||
MatTableModule,
|
||||
MatIcon,
|
||||
MatTooltip,
|
||||
MatInput,
|
||||
MatSelect,
|
||||
MatFormField,
|
||||
MatLabel,
|
||||
MatOption,
|
||||
],
|
||||
})
|
||||
export class InjectorProvidersComponent {
|
||||
readonly injector = input.required<SerializedInjector>();
|
||||
readonly providers = input<SerializedProviderRecord[]>([]);
|
||||
|
||||
readonly searchToken = signal('');
|
||||
readonly searchType = signal('');
|
||||
readonly visibleProviders = computed(() => {
|
||||
const searchToken = this.searchToken().toLowerCase();
|
||||
const searchType = this.searchType();
|
||||
|
||||
const predicates: ((provider: SerializedProviderRecord) => boolean)[] = [];
|
||||
searchToken &&
|
||||
predicates.push((provider) => provider.token.toLowerCase().includes(searchToken));
|
||||
searchType && predicates.push((provider) => provider.type === searchType);
|
||||
|
||||
return this.providers().filter((provider) =>
|
||||
predicates.every((predicate) => predicate(provider)),
|
||||
);
|
||||
});
|
||||
|
||||
providerTypeToLabel = {
|
||||
type: 'Type',
|
||||
existing: 'useExisting',
|
||||
factory: 'useFactory',
|
||||
class: 'useClass',
|
||||
value: 'useValue',
|
||||
};
|
||||
|
||||
providerTypes = Object.keys(this.providerTypeToLabel);
|
||||
|
||||
messageBus = inject<MessageBus<Events>>(MessageBus);
|
||||
|
||||
select(row: SerializedProviderRecord) {
|
||||
const {id, type, name} = this.injector();
|
||||
this.messageBus.emit('logProvider', [{id, type, name}, row]);
|
||||
}
|
||||
|
||||
get displayedColumns(): string[] {
|
||||
if (this.injector()?.type === 'element') {
|
||||
return ['token', 'type', 'isViewProvider', 'log'];
|
||||
}
|
||||
return ['token', 'type', 'log'];
|
||||
}
|
||||
}
|
||||
|
|
@ -11,10 +11,14 @@
|
|||
[disabled]="true"
|
||||
>
|
||||
<as-split-area size="50">
|
||||
<mat-checkbox (change)="toggleHideInjectorsWithNoProviders()">
|
||||
Hide injectors with no providers
|
||||
</mat-checkbox>
|
||||
<mat-checkbox (change)="toggleHideAngularInjectors()"> Hide framework injectors </mat-checkbox>
|
||||
<div class="options">
|
||||
<mat-checkbox (change)="toggleHideInjectorsWithNoProviders()">
|
||||
Hide injectors with no providers
|
||||
</mat-checkbox>
|
||||
<mat-checkbox (change)="toggleHideAngularInjectors()">
|
||||
Hide framework injectors
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
</as-split-area>
|
||||
<as-split-area>
|
||||
<as-split unit="percent" direction="horizontal" [gutterSize]="9">
|
||||
|
|
@ -23,7 +27,7 @@
|
|||
<as-split-area size="35">
|
||||
<div class="injector-hierarchy">
|
||||
<h2>
|
||||
Environment Hierarchy
|
||||
<span>Environment Hierarchy</span>
|
||||
<a
|
||||
class="hierarchy-ref"
|
||||
href="https://angular.dev/guide/di/hierarchical-dependency-injection#types-of-injector-hierarchies"
|
||||
|
|
@ -42,7 +46,7 @@
|
|||
<as-split-area size="65">
|
||||
<div class="injector-hierarchy">
|
||||
<h2>
|
||||
Element Hierarchy
|
||||
<span>Element Hierarchy</span>
|
||||
<a
|
||||
class="hierarchy-ref"
|
||||
href="https://angular.dev/guide/di/hierarchical-dependency-injection#types-of-injector-hierarchies"
|
||||
|
|
|
|||
|
|
@ -133,6 +133,30 @@ as-split-area {
|
|||
}
|
||||
}
|
||||
|
||||
.options {
|
||||
padding: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2rem;
|
||||
|
||||
mat-checkbox {
|
||||
$checkbox-size: 14px;
|
||||
--mdc-checkbox-state-layer-size: 2rem;
|
||||
--mat-checkbox-label-text-size: 0.8rem;
|
||||
|
||||
::ng-deep .mdc-checkbox {
|
||||
width: $checkbox-size;
|
||||
height: $checkbox-size;
|
||||
flex: 0 0 $checkbox-size;
|
||||
|
||||
&__background {
|
||||
width: $checkbox-size;
|
||||
height: $checkbox-size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.deps {
|
||||
display: flex;
|
||||
overflow: auto;
|
||||
|
|
@ -178,12 +202,22 @@ as-split-area {
|
|||
overflow: hidden;
|
||||
|
||||
h2 {
|
||||
padding: 4px 16px;
|
||||
padding: 0.5rem 1rem;
|
||||
margin: 0;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #777777;
|
||||
font-size: 0.875rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.375rem;
|
||||
|
||||
mat-icon {
|
||||
font-size: 1rem;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ import {
|
|||
InjectorTreeVisualizer,
|
||||
} from '../dependency-injection/injector-tree-visualizer';
|
||||
|
||||
import {InjectorProvidersComponent} from './injector-providers.component';
|
||||
import {InjectorProvidersComponent} from './injector-providers/injector-providers.component';
|
||||
import {
|
||||
filterOutAngularInjectors,
|
||||
filterOutInjectorsWithNoProviders,
|
||||
|
|
|
|||
Loading…
Reference in a new issue