refactor(devtools): convert all inputs to .ng-input (#62376)

Use the newly introduced input design for all inputs across the app.

PR Close #62376
This commit is contained in:
hawkgs 2025-06-30 14:31:45 +03:00 committed by Jessica Janiuk
parent 35032a6f6a
commit e7ff105646
13 changed files with 334 additions and 352 deletions

View file

@ -8,6 +8,9 @@ package(default_visibility = ["//visibility:public"])
sass_binary(
name = "filter_component_styles",
src = "filter.component.scss",
deps = [
"//devtools/projects/ng-devtools/src/styles:typography",
],
)
ng_project(

View file

@ -1,3 +1,5 @@
@use '../../../../../styles/typography';
.filter {
display: flex;
padding: 0px;
@ -23,6 +25,7 @@
}
.filter-input {
@extend %body-01;
border: none;
padding: 1px;
width: 100%;

View file

@ -26,6 +26,7 @@ ng_project(
],
deps = [
"//:node_modules/@angular/material",
"//devtools/projects/ng-devtools/src/lib/shared/button:button_rjs",
"//devtools/projects/protocol:protocol_rjs",
],
)

View file

@ -1,69 +1,83 @@
<h2 class="providers-title">Providers for {{ injector().name }}</h2>
@if (injector()) {
<div class="injector-providers">
<div class="filter">
<mat-form-field class="token-search" appearance="outline">
<mat-label>Search by token</mat-label>
<input
type="text"
matInput
placeholder="Provider token"
(input)="searchToken.set($any($event.target).value)"
[value]="searchToken()"
/>
<mat-icon matSuffix (click)="searchToken.set('')">close</mat-icon>
</mat-form-field>
<mat-form-field class="type-filter" 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 class="filter">
<div class="token-filter">
<label for="search-by-token">Search by token</label>
<input
id="search-by-token"
type="text"
class="ng-input"
placeholder="Provider token"
(input)="searchToken.set($any($event.target).value)"
[value]="searchToken()"
/>
@if (searchToken().length) {
<button
class="clear-filter"
ng-button
btnType="icon"
(click)="searchToken.set('')"
title="Clear provider token filter"
>
<mat-icon>close</mat-icon>
</button>
}
</div>
<div class="type-filter">
<label for="search-by-type">Search by type</label>
<select
id="search-by-type"
#filterTypeSelect
(change)="searchType.set(filterTypeSelect.value !== 'all' ? filterTypeSelect.value : '')"
class="ng-select"
>
<option value="all">All</option>
@for (type of providerTypes; track type) {
<option [value]="type">{{ $any(providerTypeToLabel)[type] }}</option>
}
</select>
</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" class="token-cell" [attr.title]="provider.token">
{{ 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)"
>code</mat-icon
>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table>
} @else {
<p class="no-providers-label">No such providers</p>
}
</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" class="token-cell" [attr.title]="provider.token">
{{ 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)"
>code</mat-icon
>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table>
} @else {
<p class="no-providers-label">No such providers</p>
}
}

View file

@ -1,58 +1,89 @@
@use '../../../../styles/typography';
h2 {
@extend %heading-400;
}
.select {
cursor: pointer;
}
:host {
display: block;
padding: 16px;
}
padding: 1rem;
.filter {
display: flex;
gap: 0.5rem;
h2 {
@extend %heading-400;
margin-top: 0;
}
mat-form-field {
&.token-search {
.filter {
display: flex;
gap: 0.75rem;
margin-bottom: 0.5rem;
label,
select,
input {
display: block;
width: 100%;
}
label {
padding-left: 0.75rem;
margin-bottom: 0.25rem;
color: var(--quaternary-contrast);
}
.token-filter {
position: relative;
flex: 1;
input {
padding-right: 2rem;
}
.clear-filter {
position: absolute;
right: 0.75rem;
bottom: 0.15rem;
mat-icon {
$icon-size: 16px;
font-size: $icon-size;
width: $icon-size;
height: $icon-size;
}
}
}
&.type-filter {
width: 10rem;
.type-filter {
width: 9rem;
}
}
.no-providers-label {
padding: 0.5rem 0;
text-align: center;
}
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;
td {
@extend %body-01;
}
.select {
cursor: pointer;
}
.column-title {
@extend %body-bold-01;
margin: 0;
}
.token-cell {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;
max-width: 0;
}
}
}
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;
td {
@extend %body-01;
}
}
.column-title {
@extend %body-bold-01;
margin: 0;
}
.token-cell {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;
max-width: 0;
}
.no-providers-label {
padding: 0.5rem 0;
text-align: center;
}

View file

@ -7,11 +7,7 @@
*/
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 {
@ -20,21 +16,13 @@ import {
SerializedInjector,
SerializedProviderRecord,
} from '../../../../../../protocol';
import {ButtonComponent} from '../../../shared/button/button.component';
@Component({
selector: 'ng-injector-providers',
templateUrl: './injector-providers.component.html',
styleUrl: './injector-providers.component.scss',
imports: [
MatTableModule,
MatIcon,
MatTooltip,
MatInput,
MatSelect,
MatFormField,
MatLabel,
MatOption,
],
imports: [MatTableModule, MatIcon, MatTooltip, ButtonComponent],
})
export class InjectorProvidersComponent {
readonly injector = input.required<SerializedInjector>();

View file

@ -5,12 +5,18 @@
} @else {
<div class="wrapper">
<div class="options">
<mat-checkbox (change)="toggleHideInjectorsWithNoProviders()">
Hide injectors with no providers
</mat-checkbox>
<mat-checkbox (change)="toggleHideAngularInjectors()">
Hide framework injectors
</mat-checkbox>
<div class="option">
<input
id="hide-injectors-no-providers"
type="checkbox"
(change)="toggleHideInjectorsWithNoProviders()"
/>
<label for="hide-injectors-no-providers">Hide injectors with no providers</label>
</div>
<div class="option">
<input id="hide-fw-injectors" type="checkbox" (change)="toggleHideAngularInjectors()" />
<label for="hide-fw-injectors">Hide framework injectors</label>
</div>
</div>
<as-split class="trees" unit="percent" direction="horizontal" [gutterSize]="9">
<as-split-area size="60">

View file

@ -21,27 +21,16 @@
}
.options {
padding: 0.25rem;
padding: 0.75rem;
display: flex;
align-items: center;
gap: 2rem;
border-bottom: 1px solid var(--color-separator);
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;
}
}
.option {
display: flex;
align-items: center;
gap: 0.5rem;
}
}

View file

@ -18,7 +18,6 @@ import {
untracked,
viewChild,
} from '@angular/core';
import {MatCheckbox} from '@angular/material/checkbox';
import {MatIcon} from '@angular/material/icon';
import {MatTooltip} from '@angular/material/tooltip';
import {
@ -66,7 +65,6 @@ const HIERARCHY_HOR_SIZE = 50;
TreeVisualizerHostComponent,
MatIcon,
MatTooltip,
MatCheckbox,
ResponsiveSplitDirective,
],
templateUrl: `./injector-tree.component.html`,

View file

@ -1,135 +1,113 @@
<div class="router-tree-wrapper">
<as-split unit="pixel" direction="vertical" [gutterSize]="9" [disabled]="true">
<as-split-area size="50">
<div class="filter">
<input (input)="searchRoutes($event)" placeholder="Search routes" class="ng-input filter-input" />
<div class="show-full-path">
<input id="show-full-path" type="checkbox" (change)="togglePathSettings()" />
<label for="show-full-path">Show full path</label>
</div>
</div>
<as-split unit="percent" direction="horizontal" [gutterSize]="9" class="visualization">
<as-split-area size="70">
<as-split unit="percent" direction="vertical" [gutterSize]="9">
<as-split-area size="100">
<ng-tree-visualizer-host #routerTree />
<section #routerTree class="router-tree router-graph">
<svg #routerTreeSvgContainer>
<g #routerTreeMainGroup></g>
</svg>
</section>
</as-split-area>
</as-split>
</as-split-area>
@let route = selectedRoute();
@if (route && route.data) {
<as-split-area size="30" minSize="20">
<div>
<input
matInput
(input)="searchRoutes($event)"
placeholder="Search routes"
class="filter-input"
/>
<mat-checkbox (change)="togglePathSettings()">Show Full Path</mat-checkbox>
<div class="selected-node-title">
<h2 class="router-title">Routes Details</h2>
<mat-icon
class="close-icon"
fontIcon="close"
(click)="selectedRoute.set(null)"
></mat-icon>
</div>
<table class="route-details-table">
<tr
ng-route-details-row
label="Path"
type="chip"
[data]="route.data.path"
(click)="navigateRoute(route)"
></tr>
<tr
ng-route-details-row
label="Component"
type="chip"
[data]="route.data.component"
(click)="viewComponentSource(route.data.component)"
></tr>
@if (route.data.pathMatch) {
<tr ng-route-details-row label="Path Match" [data]="route.data.pathMatch"></tr>
}
@if (route?.data?.data?.length > 0) {
<tr ng-route-details-row label="Data" [data]="route.data.data | json"></tr>
}
@if (route.data.canActivateGuards && route.data.canActivateGuards.length > 0) {
<tr
ng-route-details-row
label="Can Activate Guards"
type="list"
[data]="route.data.canActivateGuards"
(click)="viewSourceFromRouter($event, 'canActivate')"
></tr>
}
@if (route.data.canActivateChildGuards && route.data.canActivateChildGuards.length > 0) {
<tr
ng-route-details-row
label="Can Activate Child Guards"
type="list"
[data]="route.data.canActivateChildGuards"
(click)="viewSourceFromRouter($event, 'canActivateChild')"
></tr>
}
@if (route.data.canDeactivateGuards && route.data.canDeactivateGuards.length > 0) {
<tr
ng-route-details-row
label="Can DeActivate Guards"
type="list"
[data]="route.data.canDeactivateGuards"
(click)="viewSourceFromRouter($event, 'canDeactivate')"
></tr>
}
@if (route.data.canMatchGuards && route.data.canMatchGuards.length > 0) {
<tr
ng-route-details-row
label="Can Match Guards"
type="list"
[data]="route.data.canMatchGuards"
(click)="viewSourceFromRouter($event, 'canMatch')"
></tr>
}
@if (route.data.providers && route.data.providers.length > 0) {
<tr
ng-route-details-row
label="Providers"
type="list"
[data]="route.data.providers"
(click)="viewSourceFromRouter($event, 'providers')"
></tr>
}
@if (route.data.title) {
<tr ng-route-details-row label="Title" [data]="route.data.title"></tr>
}
<tr ng-route-details-row label="Active" type="flag" [data]="route.data.isActive"></tr>
<tr ng-route-details-row label="Auxiliary" type="flag" [data]="route.data.isAux"></tr>
<tr ng-route-details-row label="Lazy" type="flag" [data]="route.data.isLazy"></tr>
</table>
</div>
</as-split-area>
<as-split-area>
<as-split unit="percent" direction="horizontal" [gutterSize]="9">
<as-split-area size="70">
<as-split unit="percent" direction="vertical" [gutterSize]="9">
<as-split-area size="100">
<ng-tree-visualizer-host #routerTree />
<section #routerTree class="router-tree router-graph router-tree-container">
<svg #routerTreeSvgContainer>
<g #routerTreeMainGroup></g>
</svg>
</section>
</as-split-area>
</as-split>
</as-split-area>
@let route = selectedRoute();
@if (route && route.data) {
<as-split-area size="30" minSize="20">
<div>
<div class="selected-node-title">
<h2 class="router-title">Routes Details</h2>
<mat-icon
class="close-icon"
fontIcon="close"
(click)="selectedRoute.set(null)"
></mat-icon>
</div>
<table id="route-details-table">
<tr
ng-route-details-row
label="Path"
type="chip"
[data]="route.data.path"
(click)="navigateRoute(route)"
></tr>
<tr
ng-route-details-row
label="Component"
type="chip"
[data]="route.data.component"
(click)="viewComponentSource(route.data.component)"
></tr>
@if (route.data.pathMatch) {
<tr ng-route-details-row label="Path Match" [data]="route.data.pathMatch"></tr>
}
@if (route?.data?.data?.length > 0) {
<tr ng-route-details-row label="Data" [data]="route.data.data | json"></tr>
}
@if (route.data.canActivateGuards && route.data.canActivateGuards.length > 0) {
<tr
ng-route-details-row
label="Can Activate Guards"
type="list"
[data]="route.data.canActivateGuards"
(click)="viewSourceFromRouter($event, 'canActivate')"
></tr>
}
@if (
route.data.canActivateChildGuards && route.data.canActivateChildGuards.length > 0
) {
<tr
ng-route-details-row
label="Can Activate Child Guards"
type="list"
[data]="route.data.canActivateChildGuards"
(click)="viewSourceFromRouter($event, 'canActivateChild')"
></tr>
}
@if (route.data.canDeactivateGuards && route.data.canDeactivateGuards.length > 0) {
<tr
ng-route-details-row
label="Can DeActivate Guards"
type="list"
[data]="route.data.canDeactivateGuards"
(click)="viewSourceFromRouter($event, 'canDeactivate')"
></tr>
}
@if (route.data.canMatchGuards && route.data.canMatchGuards.length > 0) {
<tr
ng-route-details-row
label="Can Match Guards"
type="list"
[data]="route.data.canMatchGuards"
(click)="viewSourceFromRouter($event, 'canMatch')"
></tr>
}
@if (route.data.providers && route.data.providers.length > 0) {
<tr
ng-route-details-row
label="Providers"
type="list"
[data]="route.data.providers"
(click)="viewSourceFromRouter($event, 'providers')"
></tr>
}
@if (route.data.title) {
<tr ng-route-details-row label="Title" [data]="route.data.title"></tr>
}
<tr
ng-route-details-row
label="Active"
type="flag"
[data]="route.data.isActive"
></tr>
<tr
ng-route-details-row
label="Auxiliary"
type="flag"
[data]="route.data.isAux"
></tr>
<tr ng-route-details-row label="Lazy" type="flag" [data]="route.data.isLazy"></tr>
</table>
</div>
</as-split-area>
}
</as-split>
</as-split-area>
</as-split>
</div>
}
</as-split>

View file

@ -1,76 +1,49 @@
@use '../../../styles/theme' as theme;
.router-tree-wrapper {
width: 100%;
height: 100%;
overflow-x: auto;
padding: 10px;
}
.filter-input {
border: 1px solid var(--senary-contrast);
border-radius: 3px;
padding-left: 5px;
margin: 0 5px 0 10px;
width: 300px;
height: 28px;
outline: none;
color: rgba(0, 0, 0, 0.87);
}
.router-tree-container {
height: 100%;
}
.svg-container {
max-height: inherit;
}
.selected-node-title {
:host {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.5rem 1.5rem;
}
flex-direction: column;
height: 100%;
.router-title {
width: 100%;
display: block;
text-overflow: ellipsis;
overflow: hidden;
font-size: 0.875rem;
font-weight: 500;
margin: 0;
}
.filter {
border-bottom: 1px solid var(--color-separator);
display: flex;
gap: 1.25rem;
padding: 0.5rem;
.selected-node-title > h1 {
padding: 0px;
margin: 0px;
}
.filter-input {
width: 12rem;
}
#route-details-table {
border-collapse: collapse;
width: 100%;
font-size: 0.75rem;
}
#route-details-table td,
#route-details-table th {
border: 1px solid #ddd;
padding: 8px;
}
@include theme.dark-theme {
.row-title {
color: #ffffff;
.show-full-path {
display: flex;
align-items: center;
gap: 0.5rem;
}
}
.selected-node-title {
background-color: unset;
}
.visualization {
flex: 1;
.filter-input {
border: 1px solid rgba(255, 255, 255, 0.12);
color: rgba(255, 255, 255, 0.87);
.selected-node-title {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.5rem 1.5rem;
.router-title {
width: 100%;
display: block;
text-overflow: ellipsis;
overflow: hidden;
font-size: 0.875rem;
font-weight: 500;
margin: 0;
}
}
.route-details-table {
border-collapse: collapse;
width: 100%;
font-size: 0.75rem;
}
}
}

View file

@ -8,8 +8,6 @@
import {CommonModule} from '@angular/common';
import {afterNextRender, Component, effect, inject, input, signal, viewChild} from '@angular/core';
import {MatInputModule} from '@angular/material/input';
import {MatCheckboxModule} from '@angular/material/checkbox';
import {TreeVisualizerHostComponent} from '../../shared/tree-visualizer-host/tree-visualizer-host.component';
import {SplitAreaDirective, SplitComponent} from '../../vendor/angular-split/public_api';
import {MatIconModule} from '@angular/material/icon';
@ -36,8 +34,6 @@ const DEFAULT_FILTER = /.^/;
styleUrls: ['./router-tree.component.scss'],
imports: [
CommonModule,
MatInputModule,
MatCheckboxModule,
TreeVisualizerHostComponent,
SplitComponent,
SplitAreaDirective,

View file

@ -11,6 +11,7 @@
border: none;
padding: 0.375rem 0.75rem;
border-radius: 2rem;
box-sizing: border-box;
}
.ng-input {
@ -61,6 +62,7 @@
border-radius: 0.75rem;
}
label[for] {
label[for],
input[type='checkbox'] {
cursor: pointer;
}