/
githubmirror
/
angular
Обзор
Документация
Войти
/
githubmirror
/
angular
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
devtools/projects/ng-devtools/src/lib/devtools-tabs/devtools-tabs.component.ts
189 строк
6 KB
hawkgs
refactor(devtools): stop component inspector on esc press
09 июл 2026, 22:13
09 июл 2026, 22:13
3685755
Код
Авторство
О чём код?
/** * @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, effect, inject, output, signal} from '@angular/core'; import {MatIcon} from '@angular/material/icon'; import {MatMenu, MatMenuItem, MatMenuTrigger} from '@angular/material/menu'; import {MatTabLink, MatTabNav, MatTabNavPanel} from '@angular/material/tabs'; import {MatTooltip} from '@angular/material/tooltip'; import { ComponentExplorerView, Events, MessageBus, Route, SerializedInjector, SerializedProviderRecord, } from '../../../../protocol'; import {ApplicationEnvironment, Frame, TOP_LEVEL_FRAME_ID} from '../application-environment/index'; import {FrameManager} from '../application-services/frame_manager'; import {DirectiveExplorerComponent} from './directive-explorer/directive-explorer.component'; import {InjectorTreeComponent} from './injector-tree/injector-tree.component'; import {ProfilerComponent} from './profiler/profiler.component'; import {RouterTreeComponent} from './router-tree/router-tree.component'; import {TransferStateComponent} from './transfer-state/transfer-state.component'; import {TabUpdate} from './tab-update/index'; import {Settings} from '../application-services/settings'; import {DEEP_LINK_INSTANCE_ID} from '../application-providers/deep_link'; import {SUPPORTED_APIS} from '../application-providers/supported_apis'; import {ButtonComponent} from '../shared/button/button.component'; import {APP_DATA} from '../application-providers/app_data'; import {SettingsComponent} from './settings/settings.component'; type Tab = 'Components' | 'Profiler' | 'Router Tree' | 'Injector Tree' | 'Transfer State'; @Component({ selector: 'ng-devtools-tabs', templateUrl: './devtools-tabs.component.html', styleUrls: ['./devtools-tabs.component.scss'], imports: [ MatTabNav, MatTabNavPanel, MatTooltip, MatIcon, MatMenu, MatMenuItem, MatMenuTrigger, MatTabLink, DirectiveExplorerComponent, ProfilerComponent, RouterTreeComponent, InjectorTreeComponent, TransferStateComponent, SettingsComponent, ButtonComponent, ], providers: [TabUpdate], host: { '(document:keydown.Escape)': 'stopInspecting()', }, }) export class DevToolsTabsComponent { public readonly frameManager = inject(FrameManager); private readonly tabUpdate = inject(TabUpdate); private readonly settings = inject(Settings); protected readonly messageBus = inject<MessageBus<Events>>(MessageBus); protected readonly applicationEnvironment = inject(ApplicationEnvironment); protected readonly supportedApis = inject(SUPPORTED_APIS); protected readonly appData = inject(APP_DATA); private readonly deepLinkInstanceId = inject(DEEP_LINK_INSTANCE_ID); readonly frameSelected = output<Frame>(); readonly inspectorRunning = signal(false); protected readonly signalGraphEnabled = () => this.supportedApis().signals; protected readonly showCommentNodes = this.settings.showCommentNodes; protected readonly activeTab = this.settings.activeTab; protected readonly componentExplorerView = signal<ComponentExplorerView | null>(null); protected readonly providers = signal<SerializedProviderRecord[]>([]); readonly routes = signal<Route[]>([]); protected readonly tabs = computed<Tab[]>(() => { const supportedApis = this.supportedApis(); const tabs: Tab[] = ['Components']; if (supportedApis.profiler) { tabs.push('Profiler'); } if (supportedApis.dependencyInjection) { tabs.push('Injector Tree'); } if (supportedApis.routes && this.routes().length > 0) { tabs.push('Router Tree'); } if (this.appData().hydration) { tabs.push('Transfer State'); } return tabs; }); protected readonly profilingNotificationsSupported = Boolean( (window.chrome?.devtools as any)?.performance?.onProfilingStarted, ); protected readonly TOP_LEVEL_FRAME_ID = TOP_LEVEL_FRAME_ID; protected readonly extensionVersion = signal('dev-build'); protected readonly settingsOpened = signal(false); constructor() { this.messageBus.on('updateRouterTree', (routes: any[]) => { this.routes.set(routes || []); }); // Change the tab to Components, if an element is selected via the inspector. this.messageBus.on('selectComponent', () => { if (this.activeTab() !== 'Components') { this.changeTab('Components'); } }); this.messageBus.on('latestComponentExplorerView', (view: ComponentExplorerView) => { this.componentExplorerView.set(view); }); this.messageBus.on( 'latestInjectorProviders', (_: SerializedInjector, providers: SerializedProviderRecord[]) => { this.providers.set(providers); }, ); // Deep link: switch to Components tab when a deep link request arrives. effect(() => { if (this.deepLinkInstanceId() !== null) { this.changeTab('Components'); } }); if (typeof chrome !== 'undefined' && chrome.runtime !== undefined) { this.extensionVersion.set(chrome.runtime.getManifest().version); } } emitSelectedFrame(event: Event): void { const frameId = (event.target as HTMLInputElement).value; const frame = this.frameManager.frames().find((frame) => frame.id === parseInt(frameId, 10)); this.frameSelected.emit(frame!); } changeTab(tab: Tab): void { this.activeTab.set(tab); this.tabUpdate.notify(tab); if (tab === 'Router Tree') { this.messageBus.emit('getRoutes'); } } toggleInspector(): void { this.toggleInspectorState(); this.emitInspectorEvent(); } stopInspecting(): void { if (this.inspectorRunning()) { this.toggleInspector(); } } emitInspectorEvent(): void { if (this.inspectorRunning()) { this.messageBus.emit('inspectorStart'); } else { this.messageBus.emit('inspectorEnd'); this.messageBus.emit('removeHighlightOverlay'); } } toggleInspectorState(): void { this.inspectorRunning.update((state) => !state); } }