@@ -38,7 +38,7 @@
Show hydration overlays
diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-explorer.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-explorer.component.ts
index 6031f8f23be0f..03fe8fe485ec5 100644
--- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-explorer.component.ts
+++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-explorer.component.ts
@@ -7,17 +7,17 @@
*/
import {
- ChangeDetectorRef,
+ afterNextRender,
Component,
ElementRef,
- EventEmitter,
inject,
Input,
- NgZone,
+ input,
OnDestroy,
- OnInit,
- Output,
+ output,
+ signal,
ViewChild,
+ viewChild,
} from '@angular/core';
import {
ComponentExplorerView,
@@ -86,57 +86,50 @@ const sameDirectives = (a: IndexedNode, b: IndexedNode) => {
FormsModule,
],
})
-export class DirectiveExplorerComponent implements OnInit, OnDestroy {
- @Input() showCommentNodes = false;
+export class DirectiveExplorerComponent implements OnDestroy {
+ readonly showCommentNodes = input(false);
@Input() isHydrationEnabled = false;
- @Output() toggleInspector = new EventEmitter
();
+ readonly toggleInspector = output();
- @ViewChild(DirectiveForestComponent) directiveForest!: DirectiveForestComponent;
- @ViewChild(BreadcrumbsComponent) breadcrumbs!: BreadcrumbsComponent;
+ readonly directiveForest = viewChild(DirectiveForestComponent);
@ViewChild(SplitComponent, {static: true, read: ElementRef}) splitElementRef!: ElementRef;
@ViewChild('directiveForestSplitArea', {static: true, read: ElementRef})
directiveForestSplitArea!: ElementRef;
- currentSelectedElement: IndexedNode | null = null;
- forest!: DevToolsNode[];
- splitDirection: 'horizontal' | 'vertical' = 'horizontal';
- parents: FlatNode[] | null = null;
- showHydrationNodeHighlights: boolean = false;
+ readonly currentSelectedElement = signal(null);
+ readonly forest = signal([]);
+ readonly splitDirection = signal<'horizontal' | 'vertical'>('horizontal');
+ readonly parents = signal(null);
+ readonly showHydrationNodeHighlights = signal(false);
- private _resizeObserver = new ResizeObserver((entries) =>
- this._ngZone.run(() => {
- this.refreshHydrationNodeHighlightsIfNeeded();
-
- const resizedEntry = entries[0];
- if (resizedEntry.target === this.splitElementRef.nativeElement) {
- this.splitDirection = resizedEntry.contentRect.width <= 500 ? 'vertical' : 'horizontal';
- }
-
- if (!this.breadcrumbs) {
- return;
- }
-
- this.breadcrumbs.updateScrollButtonVisibility();
- }),
- );
+ private _resizeObserver!: ResizeObserver;
private _clickedElement: IndexedNode | null = null;
private _refreshRetryTimeout: null | ReturnType = null;
- constructor(
- private readonly _appOperations: ApplicationOperations,
- private readonly _messageBus: MessageBus,
- private readonly _propResolver: ElementPropertyResolver,
- private readonly _cdr: ChangeDetectorRef,
- private readonly _ngZone: NgZone,
- private readonly _frameManager: FrameManager,
- ) {}
-
- ngOnInit(): void {
- this.subscribeToBackendEvents();
- this.refresh();
- this._resizeObserver.observe(this.splitElementRef.nativeElement);
- this._resizeObserver.observe(this.directiveForestSplitArea.nativeElement);
+ private readonly _appOperations = inject(ApplicationOperations);
+ private readonly _messageBus = inject>(MessageBus);
+ private readonly _propResolver = inject(ElementPropertyResolver);
+ private readonly _frameManager = inject(FrameManager);
+
+ constructor() {
+ afterNextRender(() => {
+ this._resizeObserver = new ResizeObserver((entries) => {
+ this.refreshHydrationNodeHighlightsIfNeeded();
+
+ const resizedEntry = entries[0];
+ if (resizedEntry.target === this.splitElementRef.nativeElement) {
+ this.splitDirection.set(
+ resizedEntry.contentRect.width <= 500 ? 'vertical' : 'horizontal',
+ );
+ }
+ });
+
+ this.subscribeToBackendEvents();
+ this.refresh();
+ this._resizeObserver.observe(this.splitElementRef.nativeElement);
+ this._resizeObserver.observe(this.directiveForestSplitArea.nativeElement);
+ });
}
ngOnDestroy(): void {
@@ -157,16 +150,17 @@ export class DirectiveExplorerComponent implements OnInit, OnDestroy {
this._messageBus.emit('setSelectedComponent', [node.position]);
this.refresh();
} else {
- this._clickedElement = this.currentSelectedElement = null;
+ this._clickedElement = null;
+ this.currentSelectedElement.set(null);
}
}
subscribeToBackendEvents(): void {
this._messageBus.on('latestComponentExplorerView', (view: ComponentExplorerView) => {
- this.forest = view.forest;
- this.currentSelectedElement = this._clickedElement;
- if (view.properties && this.currentSelectedElement) {
- this._propResolver.setProperties(this.currentSelectedElement, view.properties);
+ this.forest.set(view.forest);
+ this.currentSelectedElement.set(this._clickedElement);
+ if (view.properties && this._clickedElement) {
+ this._propResolver.setProperties(this._clickedElement, view.properties);
}
});
@@ -192,12 +186,10 @@ export class DirectiveExplorerComponent implements OnInit, OnDestroy {
viewSource(directiveName: string): void {
// find the index of the directive with directiveName in this.currentSelectedElement.directives
+ const selectedEl = this.currentSelectedElement();
+ if (!selectedEl) return;
- if (!this.currentSelectedElement) {
- return;
- }
-
- const directiveIndex = this.currentSelectedElement.directives.findIndex(
+ const directiveIndex = selectedEl.directives.findIndex(
(directive) => directive.name === directiveName,
);
@@ -213,7 +205,7 @@ export class DirectiveExplorerComponent implements OnInit, OnDestroy {
}
this._appOperations.viewSource(
- this.currentSelectedElement.position,
+ selectedEl.position,
directiveIndex !== -1 ? directiveIndex : undefined,
new URL(selectedFrame!.url),
);
@@ -262,8 +254,8 @@ export class DirectiveExplorerComponent implements OnInit, OnDestroy {
// set of properties which are already expanded.
if (
!this._clickedElement ||
- !this.currentSelectedElement ||
- !sameDirectives(this._clickedElement, this.currentSelectedElement)
+ !this.currentSelectedElement() ||
+ !sameDirectives(this._clickedElement, this.currentSelectedElement()!)
) {
return {
type: PropertyQueryTypes.All,
@@ -284,12 +276,11 @@ export class DirectiveExplorerComponent implements OnInit, OnDestroy {
}
handleSelect(node: FlatNode): void {
- this.directiveForest.handleSelect(node);
+ this.directiveForest()?.handleSelect(node);
}
handleSetParents(parents: FlatNode[] | null): void {
- this.parents = parents;
- this._cdr.detectChanges();
+ this.parents.set(parents);
}
inspect({
@@ -324,7 +315,7 @@ export class DirectiveExplorerComponent implements OnInit, OnDestroy {
}
refreshHydrationNodeHighlightsIfNeeded() {
- if (this.showHydrationNodeHighlights) {
+ if (this.showHydrationNodeHighlights()) {
this.removeHydrationNodesHightlights();
this.hightlightHydrationNodes();
}
diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-explorer.spec.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-explorer.spec.ts
index 9fbda282341c2..69ed06b125197 100644
--- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-explorer.spec.ts
+++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-explorer.spec.ts
@@ -19,7 +19,7 @@ import SpyObj = jasmine.SpyObj;
import {By} from '@angular/platform-browser';
import {FrameManager} from '../../frame_manager';
import {TabUpdate} from '../tab-update';
-import {Component, EventEmitter, Input, Output, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
+import {Component, CUSTOM_ELEMENTS_SCHEMA, output, input} from '@angular/core';
import {ElementPropertyResolver, FlatNode} from './property-resolver/element-property-resolver';
import {BreadcrumbsComponent} from './directive-forest/breadcrumbs/breadcrumbs.component';
import {PropertyTabComponent} from './property-tab/property-tab.component';
@@ -30,15 +30,15 @@ import {PropertyTabComponent} from './property-tab/property-tab.component';
standalone: true,
})
class MockDirectiveForestComponent {
- @Input() forest: IndexedNode[] = [];
- @Input() currentSelectedElement: IndexedNode | null = null;
- @Input() showCommentNodes = false;
- @Output() selectNode = new EventEmitter();
- @Output() selectDomElement = new EventEmitter();
- @Output() setParents = new EventEmitter();
- @Output() highlightComponent = new EventEmitter();
- @Output() removeComponentHighlight = new EventEmitter();
- @Output() toggleInspector = new EventEmitter();
+ readonly forest = input([]);
+ readonly currentSelectedElement = input(null);
+ readonly showCommentNodes = input(false);
+ readonly selectNode = output();
+ readonly selectDomElement = output();
+ readonly setParents = output();
+ readonly highlightComponent = output();
+ readonly removeComponentHighlight = output();
+ readonly toggleInspector = output();
}
@Component({
@@ -47,10 +47,10 @@ class MockDirectiveForestComponent {
standalone: true,
})
class MockBreadcrumbsComponent {
- @Input() parents: IndexedNode[] = [];
- @Output() handleSelect = new EventEmitter();
- @Output() mouseLeaveNode = new EventEmitter();
- @Output() mouseOverNode = new EventEmitter();
+ readonly parents = input([]);
+ readonly handleSelect = output();
+ readonly mouseLeaveNode = output();
+ readonly mouseOverNode = output();
}
@Component({
@@ -59,9 +59,9 @@ class MockBreadcrumbsComponent {
standalone: true,
})
class MockPropertyTabComponent {
- @Input() currentSelectedElement: IndexedNode | null = null;
- @Output() inspect = new EventEmitter<{node: FlatNode; directivePosition: DirectivePosition}>();
- @Output() viewSource = new EventEmitter();
+ readonly currentSelectedElement = input(null);
+ readonly inspect = output<{node: FlatNode; directivePosition: DirectivePosition}>();
+ readonly viewSource = output();
}
describe('DirectiveExplorerComponent', () => {
@@ -157,9 +157,9 @@ describe('DirectiveExplorerComponent', () => {
]);
currentSelectedElement.position = [0];
currentSelectedElement.children = [];
- comp.currentSelectedElement = currentSelectedElement;
+ comp.currentSelectedElement.set(currentSelectedElement);
comp.refresh();
- expect(comp.currentSelectedElement).toBeTruthy();
+ expect(comp.currentSelectedElement()).toBeTruthy();
expect(messageBusMock.emit).toHaveBeenCalledWith('getLatestComponentExplorerView', [
undefined,
]);
@@ -199,12 +199,12 @@ describe('DirectiveExplorerComponent', () => {
});
it('should show hydration slide toggle', () => {
- comp.isHydrationEnabled = true;
+ fixture.componentRef.setInput('isHydrationEnabled', true);
fixture.detectChanges();
const toggle = fixture.debugElement.query(By.css('mat-slide-toggle'));
expect(toggle).toBeTruthy();
- comp.isHydrationEnabled = false;
+ fixture.componentRef.setInput('isHydrationEnabled', false);
fixture.detectChanges();
const toggle2 = fixture.debugElement.query(By.css('mat-slide-toggle'));
expect(toggle2).toBeFalsy();
@@ -215,11 +215,11 @@ describe('DirectiveExplorerComponent', () => {
describe('view source', () => {
it('should not call application operations view source if no frames are detected', () => {
const directiveName = 'test';
- comp.currentSelectedElement = {
+ comp.currentSelectedElement.set({
directives: [{name: directiveName}],
position: [0],
children: [] as IndexedNode[],
- } as IndexedNode;
+ } as IndexedNode);
comp.viewSource(directiveName);
expect(applicationOperationsSpy.viewSource).toHaveBeenCalledTimes(0);
});
@@ -229,11 +229,11 @@ describe('DirectiveExplorerComponent', () => {
contentScriptConnected(1, 'test2', 'http://localhost:4200/url');
const directiveName = 'test';
- comp.currentSelectedElement = {
+ comp.currentSelectedElement.set({
directives: [{name: directiveName}],
position: [0],
children: [] as IndexedNode[],
- } as IndexedNode;
+ } as IndexedNode);
comp.viewSource(directiveName);
@@ -252,11 +252,11 @@ describe('DirectiveExplorerComponent', () => {
contentScriptConnected(1, 'test2', 'http://localhost:4200/url2');
const directiveName = 'test';
- comp.currentSelectedElement = {
+ comp.currentSelectedElement.set({
directives: [{name: directiveName}],
position: [0],
children: [] as IndexedNode[],
- } as IndexedNode;
+ } as IndexedNode);
comp.viewSource(directiveName);
diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/breadcrumbs/breadcrumbs.component.html b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/breadcrumbs/breadcrumbs.component.html
index e0ca0503ffb6f..9888a1425f645 100644
--- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/breadcrumbs/breadcrumbs.component.html
+++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/breadcrumbs/breadcrumbs.component.html
@@ -1,9 +1,9 @@
-
+
more_horiz
-
- @for (node of parents; track $index) {
+
+ @for (node of parents(); track $index) {
}
-
+
more_horiz
diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/breadcrumbs/breadcrumbs.component.scss b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/breadcrumbs/breadcrumbs.component.scss
index ebf9bac094c7a..50a749f1be712 100644
--- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/breadcrumbs/breadcrumbs.component.scss
+++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/breadcrumbs/breadcrumbs.component.scss
@@ -1,4 +1,4 @@
-.mdc-card.breadcrumb-card {
+.mdc-card.breadcrumb-card {
padding: 0;
width: 100%;
height: 24px;
@@ -20,7 +20,7 @@
overflow-x: auto;
white-space: nowrap;
display: inline-block;
- width: calc(100% - 50px);
+ // width: calc(100% - 50px);
height: 100%;
scroll-behavior: smooth;
diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/breadcrumbs/breadcrumbs.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/breadcrumbs/breadcrumbs.component.ts
index 83a466f92d8e0..3ec0a928a5add 100644
--- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/breadcrumbs/breadcrumbs.component.ts
+++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/breadcrumbs/breadcrumbs.component.ts
@@ -7,19 +7,15 @@
*/
import {
- AfterViewInit,
Component,
+ computed,
+ effect,
ElementRef,
- EventEmitter,
- HostListener,
- Input,
- OnChanges,
- OnInit,
- Output,
- ViewChild,
+ input,
+ output,
+ signal,
+ viewChild,
} from '@angular/core';
-import {Subject} from 'rxjs';
-import {debounceTime} from 'rxjs/operators';
import {FlatNode} from '../component-data-source';
import {MatButton} from '@angular/material/button';
@@ -33,46 +29,52 @@ import {MatCard} from '@angular/material/card';
standalone: true,
imports: [MatCard, MatIcon, MatButton],
})
-export class BreadcrumbsComponent implements OnInit, AfterViewInit, OnChanges {
- @Input({required: true}) parents!: FlatNode[];
- @Output() handleSelect = new EventEmitter();
- @Output() mouseOverNode = new EventEmitter();
- @Output() mouseLeaveNode = new EventEmitter();
+export class BreadcrumbsComponent {
+ readonly parents = input.required();
+ readonly handleSelect = output();
+ readonly mouseOverNode = output();
+ readonly mouseLeaveNode = output();
- @ViewChild('breadcrumbs') breadcrumbsScrollContent!: ElementRef;
+ readonly breadcrumbsScrollContent = viewChild.required('breadcrumbs');
- showScrollLeftButton = false;
- showScrollRightButton = false;
+ readonly showScrollLeftButton = computed(() => {
+ const value = this.breadcrumbsScrollLayout();
+ return value && value.scrollLeft > 0;
+ });
- updateScrollButtonVisibility$ = new Subject();
+ readonly showScrollRightButton = computed(() => {
+ const value = this.breadcrumbsScrollLayout();
+ if (!value) {
+ return false;
+ }
+ const {clientWidth, scrollWidth, scrollLeft} = value;
+ return scrollWidth > clientWidth && scrollLeft + clientWidth < scrollWidth;
+ });
- ngOnInit(): void {
- this.updateScrollButtonVisibility$
- .pipe(debounceTime(100))
- .subscribe(() => this.updateScrollButtonVisibility());
- }
-
- ngAfterViewInit(): void {
- this.updateScrollButtonVisibility$.next();
- }
-
- ngOnChanges(): void {
- this.updateScrollButtonVisibility$.next();
- }
+ private readonly breadcrumbsScrollLayout = signal<
+ | {
+ clientWidth: number;
+ scrollWidth: number;
+ scrollLeft: number;
+ }
+ | undefined
+ >(undefined);
- @HostListener('window:resize', ['$event'])
- onResize(): void {
- this.updateScrollButtonVisibility$.next();
+ constructor() {
+ effect((cleanup) => {
+ const observer = new ResizeObserver(() => this.updateScrollButtonVisibility());
+ observer.observe(this.breadcrumbsScrollContent().nativeElement);
+ cleanup(() => observer.disconnect());
+ });
}
scroll(pixels: number): void {
- this.breadcrumbsScrollContent.nativeElement.scrollLeft += pixels;
- this.updateScrollButtonVisibility$.next();
+ this.breadcrumbsScrollContent().nativeElement.scrollLeft += pixels;
+ this.updateScrollButtonVisibility();
}
updateScrollButtonVisibility(): void {
- const {clientWidth, scrollWidth, scrollLeft} = this.breadcrumbsScrollContent.nativeElement;
- this.showScrollLeftButton = scrollLeft > 0;
- this.showScrollRightButton = scrollLeft + clientWidth < scrollWidth;
+ const {clientWidth, scrollWidth, scrollLeft} = this.breadcrumbsScrollContent().nativeElement;
+ this.breadcrumbsScrollLayout.set({clientWidth, scrollWidth, scrollLeft});
}
}
diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/directive-forest.component.html b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/directive-forest.component.html
index dee01c775974e..7998f711e2650 100644
--- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/directive-forest.component.html
+++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/directive-forest.component.html
@@ -57,8 +57,8 @@
}
@if (
- treeControl.isExpanded(node) &&
- node.hydration?.status === 'mismatched' &&
+ treeControl.isExpanded(node) &&
+ node.hydration?.status === 'mismatched' &&
(node.hydration.expectedNodeDetails || node.hydration.actualNodeDetails)
) {
diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/directive-forest.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/directive-forest.component.ts
index 81cd5a6c69765..fe41f660fe976 100644
--- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/directive-forest.component.ts
+++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/directive-forest.component.ts
@@ -14,14 +14,16 @@ import {
import {FlatTreeControl} from '@angular/cdk/tree';
import {
ChangeDetectionStrategy,
- ChangeDetectorRef,
Component,
+ computed,
+ effect,
ElementRef,
- EventEmitter,
HostListener,
- Input,
- Output,
- ViewChild,
+ inject,
+ input,
+ output,
+ signal,
+ viewChild,
} from '@angular/core';
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
import {DevToolsNode, ElementPosition, Events, MessageBus} from 'protocol';
@@ -51,31 +53,19 @@ import {MatTooltip} from '@angular/material/tooltip';
],
})
export class DirectiveForestComponent {
- @Input()
- set forest(forest: DevToolsNode[]) {
- this._latestForest = forest;
- const result = this._updateForest(forest);
- const changed =
- result.movedItems.length || result.newItems.length || result.removedItems.length;
- if (this.currentSelectedElement && changed) {
- this._reselectNodeOnUpdate();
- }
- }
- @Input({required: true}) currentSelectedElement!: IndexedNode;
- @Input()
- set showCommentNodes(show: boolean) {
- this._showCommentNodes = show;
- this.forest = this._latestForest;
- }
+ readonly forest = input
([]);
+ readonly showCommentNodes = input(false);
+ readonly currentSelectedElement = input.required();
- @Output() selectNode = new EventEmitter();
- @Output() selectDomElement = new EventEmitter();
- @Output() setParents = new EventEmitter();
- @Output() highlightComponent = new EventEmitter();
- @Output() removeComponentHighlight = new EventEmitter();
- @Output() toggleInspector = new EventEmitter();
+ readonly selectNode = output();
+ readonly selectDomElement = output();
+ readonly setParents = output();
+ readonly highlightComponent = output();
+ readonly removeComponentHighlight = output();
+ readonly toggleInspector = output();
- @ViewChild(CdkVirtualScrollViewport) viewport!: CdkVirtualScrollViewport;
+ readonly viewport = viewChild.required(CdkVirtualScrollViewport);
+ private readonly updateForestResult = computed(() => this._updateForest(this.forest()));
filterRegex = new RegExp('.^');
currentlyMatchedIndex = -1;
@@ -83,14 +73,7 @@ export class DirectiveForestComponent {
selectedNode: FlatNode | null = null;
parents!: FlatNode[];
- private _highlightIDinTreeFromElement: number | null = null;
- private _showCommentNodes = false;
- private _latestForest!: DevToolsNode[];
-
- set highlightIDinTreeFromElement(id: number | null) {
- this._highlightIDinTreeFromElement = id;
- this._cdr.markForCheck();
- }
+ private readonly highlightIDinTreeFromElement = signal(null);
readonly treeControl = new FlatTreeControl(
(node) => node!.level,
@@ -102,28 +85,37 @@ export class DirectiveForestComponent {
private _initialized = false;
private resizeObserver: ResizeObserver;
- constructor(
- private _tabUpdate: TabUpdate,
- private _messageBus: MessageBus,
- private _cdr: ChangeDetectorRef,
- private elementRef: ElementRef,
- ) {
+ private _tabUpdate = inject(TabUpdate);
+ private _messageBus = inject>(MessageBus);
+ private elementRef = inject(ElementRef);
+
+ constructor() {
this.subscribeToInspectorEvents();
this._tabUpdate.tabUpdate$.pipe(takeUntilDestroyed()).subscribe(() => {
- if (this.viewport) {
+ if (this.viewport()) {
setTimeout(() => {
- this.viewport.scrollToIndex(0);
- this.viewport.checkViewportSize();
+ const viewport = this.viewport();
+ viewport.scrollToIndex(0);
+ viewport.checkViewportSize();
});
}
});
// In some cases there a height changes, we need to recalculate the viewport size.
this.resizeObserver = new ResizeObserver(() => {
- this.viewport.scrollToIndex(0);
- this.viewport.checkViewportSize();
+ this.viewport().scrollToIndex(0);
+ this.viewport().checkViewportSize();
});
this.resizeObserver.observe(this.elementRef.nativeElement);
+
+ effect(() => {
+ const result = this.updateForestResult();
+ const changed =
+ result.movedItems.length || result.newItems.length || result.removedItems.length;
+ if (this.currentSelectedElement() && changed) {
+ this._reselectNodeOnUpdate();
+ }
+ });
}
ngOnDestroy(): void {
@@ -138,11 +130,11 @@ export class DirectiveForestComponent {
});
this._messageBus.on('highlightComponent', (id: number) => {
- this.highlightIDinTreeFromElement = id;
+ this.highlightIDinTreeFromElement.set(id);
});
this._messageBus.on('removeComponentHighlight', () => {
- this.highlightIDinTreeFromElement = null;
+ this.highlightIDinTreeFromElement.set(null);
});
}
@@ -167,7 +159,7 @@ export class DirectiveForestComponent {
selectAndEnsureVisible(node: FlatNode): void {
this.select(node);
- const scrollParent = this.viewport.elementRef.nativeElement;
+ const scrollParent = this.viewport().elementRef.nativeElement;
// The top most point we see an element
const top = scrollParent.scrollTop;
// That's the bottom most point we currently see an element.
@@ -201,7 +193,7 @@ export class DirectiveForestComponent {
private _reselectNodeOnUpdate(): void {
const nodeThatStillExists = this.dataSource.getFlatNodeFromIndexedNode(
- this.currentSelectedElement,
+ this.currentSelectedElement(),
);
if (nodeThatStillExists) {
this.select(nodeThatStillExists);
@@ -215,7 +207,7 @@ export class DirectiveForestComponent {
movedItems: FlatNode[];
removedItems: FlatNode[];
} {
- const result = this.dataSource.update(forest, this._showCommentNodes);
+ const result = this.dataSource.update(forest, this.showCommentNodes());
if (!this._initialized && forest && forest.length) {
this.treeControl.expandAll();
this._initialized = true;
@@ -399,7 +391,7 @@ export class DirectiveForestComponent {
}
highlightNode(position: ElementPosition): void {
- this._highlightIDinTreeFromElement = null;
+ this.highlightIDinTreeFromElement.set(null);
this.highlightComponent.emit(position);
}
@@ -409,8 +401,8 @@ export class DirectiveForestComponent {
isHighlighted(node: FlatNode): boolean {
return (
- !!this._highlightIDinTreeFromElement &&
- this._highlightIDinTreeFromElement === node.original.component?.id
+ !!this.highlightIDinTreeFromElement() &&
+ this.highlightIDinTreeFromElement() === node.original.component?.id
);
}
diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/filter/filter.component.html b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/filter/filter.component.html
index 50585d253d2d5..974bebbd645b7 100644
--- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/filter/filter.component.html
+++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/filter/filter.component.html
@@ -12,7 +12,7 @@
placeholder="Search components"
/>
- @if (hasMatched) {
+ @if (hasMatched()) {