mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
116404: Replaced ViewChild logic with a CSS selector
This change avoids triggering an additional change detection cycle
(cherry picked from commit f7bb83013a
)
This commit is contained in:

committed by
github-actions[bot]
![github-actions[bot]](/assets/img/avatar_default.png)
parent
daf2f50b00
commit
a984957af9
@@ -1,4 +1,4 @@
|
|||||||
<div #expandableNavbarSectionContainer class="ds-menu-item-wrapper text-md-center"
|
<div class="ds-menu-item-wrapper text-md-center"
|
||||||
[id]="'expandable-navbar-section-' + section.id"
|
[id]="'expandable-navbar-section-' + section.id"
|
||||||
(mouseenter)="onMouseEnter($event)"
|
(mouseenter)="onMouseEnter($event)"
|
||||||
(mouseleave)="onMouseLeave($event)"
|
(mouseleave)="onMouseLeave($event)"
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
</a>
|
</a>
|
||||||
<div *ngIf="(active$ | async).valueOf() === true" (click)="deactivateSection($event)"
|
<div *ngIf="(active$ | async).valueOf() === true" (click)="deactivateSection($event)"
|
||||||
[id]="expandableNavbarSectionId()"
|
[id]="expandableNavbarSectionId()"
|
||||||
[dsHoverOutsideOfElement]="expandableNavbarSection"
|
[dsHoverOutsideOfParentSelector]="'#expandable-navbar-section-' + section.id"
|
||||||
(dsHoverOutside)="deactivateSection($event, false)"
|
(dsHoverOutside)="deactivateSection($event, false)"
|
||||||
role="menu"
|
role="menu"
|
||||||
class="dropdown-menu show nav-dropdown-menu m-0 shadow-none border-top-0 px-3 px-md-0 pt-0 pt-md-1">
|
class="dropdown-menu show nav-dropdown-menu m-0 shadow-none border-top-0 px-3 px-md-0 pt-0 pt-md-1">
|
||||||
|
@@ -12,7 +12,7 @@ import { HostWindowService } from '../../shared/host-window.service';
|
|||||||
import { MenuService } from '../../shared/menu/menu.service';
|
import { MenuService } from '../../shared/menu/menu.service';
|
||||||
import { HostWindowServiceStub } from '../../shared/testing/host-window-service.stub';
|
import { HostWindowServiceStub } from '../../shared/testing/host-window-service.stub';
|
||||||
import { MenuServiceStub } from '../../shared/testing/menu-service.stub';
|
import { MenuServiceStub } from '../../shared/testing/menu-service.stub';
|
||||||
import { VarDirective } from '../../shared/utils/var.directive';
|
import { HoverOutsideDirective } from '../../shared/utils/hover-outside.directive';
|
||||||
import { ExpandableNavbarSectionComponent } from './expandable-navbar-section.component';
|
import { ExpandableNavbarSectionComponent } from './expandable-navbar-section.component';
|
||||||
|
|
||||||
describe('ExpandableNavbarSectionComponent', () => {
|
describe('ExpandableNavbarSectionComponent', () => {
|
||||||
@@ -23,7 +23,12 @@ describe('ExpandableNavbarSectionComponent', () => {
|
|||||||
describe('on larger screens', () => {
|
describe('on larger screens', () => {
|
||||||
beforeEach(waitForAsync(() => {
|
beforeEach(waitForAsync(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [NoopAnimationsModule, ExpandableNavbarSectionComponent, TestComponent, VarDirective],
|
imports: [
|
||||||
|
ExpandableNavbarSectionComponent,
|
||||||
|
HoverOutsideDirective,
|
||||||
|
NoopAnimationsModule,
|
||||||
|
TestComponent,
|
||||||
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: 'sectionDataProvider', useValue: {} },
|
{ provide: 'sectionDataProvider', useValue: {} },
|
||||||
{ provide: MenuService, useValue: menuService },
|
{ provide: MenuService, useValue: menuService },
|
||||||
@@ -41,10 +46,6 @@ describe('ExpandableNavbarSectionComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when the mouse enters the section header (while inactive)', () => {
|
describe('when the mouse enters the section header (while inactive)', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyOn(component, 'onMouseEnter').and.callThrough();
|
spyOn(component, 'onMouseEnter').and.callThrough();
|
||||||
@@ -184,7 +185,12 @@ describe('ExpandableNavbarSectionComponent', () => {
|
|||||||
describe('on smaller, mobile screens', () => {
|
describe('on smaller, mobile screens', () => {
|
||||||
beforeEach(waitForAsync(() => {
|
beforeEach(waitForAsync(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [NoopAnimationsModule, ExpandableNavbarSectionComponent, TestComponent, VarDirective],
|
imports: [
|
||||||
|
ExpandableNavbarSectionComponent,
|
||||||
|
HoverOutsideDirective,
|
||||||
|
NoopAnimationsModule,
|
||||||
|
TestComponent,
|
||||||
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: 'sectionDataProvider', useValue: {} },
|
{ provide: 'sectionDataProvider', useValue: {} },
|
||||||
{ provide: MenuService, useValue: menuService },
|
{ provide: MenuService, useValue: menuService },
|
||||||
|
@@ -7,12 +7,11 @@ import {
|
|||||||
import {
|
import {
|
||||||
AfterViewChecked,
|
AfterViewChecked,
|
||||||
Component,
|
Component,
|
||||||
ElementRef,
|
|
||||||
HostListener,
|
HostListener,
|
||||||
Inject,
|
Inject,
|
||||||
Injector,
|
Injector,
|
||||||
|
OnDestroy,
|
||||||
OnInit,
|
OnInit,
|
||||||
ViewChild,
|
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { RouterLinkActive } from '@angular/router';
|
import { RouterLinkActive } from '@angular/router';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
@@ -24,7 +23,6 @@ import { MenuService } from '../../shared/menu/menu.service';
|
|||||||
import { MenuID } from '../../shared/menu/menu-id.model';
|
import { MenuID } from '../../shared/menu/menu-id.model';
|
||||||
import { MenuSection } from '../../shared/menu/menu-section.model';
|
import { MenuSection } from '../../shared/menu/menu-section.model';
|
||||||
import { HoverOutsideDirective } from '../../shared/utils/hover-outside.directive';
|
import { HoverOutsideDirective } from '../../shared/utils/hover-outside.directive';
|
||||||
import { VarDirective } from '../../shared/utils/var.directive';
|
|
||||||
import { NavbarSectionComponent } from '../navbar-section/navbar-section.component';
|
import { NavbarSectionComponent } from '../navbar-section/navbar-section.component';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -43,12 +41,9 @@ import { NavbarSectionComponent } from '../navbar-section/navbar-section.compone
|
|||||||
NgFor,
|
NgFor,
|
||||||
NgIf,
|
NgIf,
|
||||||
RouterLinkActive,
|
RouterLinkActive,
|
||||||
VarDirective,
|
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class ExpandableNavbarSectionComponent extends NavbarSectionComponent implements AfterViewChecked, OnInit {
|
export class ExpandableNavbarSectionComponent extends NavbarSectionComponent implements AfterViewChecked, OnInit, OnDestroy {
|
||||||
|
|
||||||
@ViewChild('expandableNavbarSectionContainer') expandableNavbarSection: ElementRef;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This section resides in the Public Navbar
|
* This section resides in the Public Navbar
|
||||||
@@ -77,6 +72,11 @@ export class ExpandableNavbarSectionComponent extends NavbarSectionComponent imp
|
|||||||
*/
|
*/
|
||||||
addArrowEventListeners = false;
|
addArrowEventListeners = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of current dropdown items who have event listeners
|
||||||
|
*/
|
||||||
|
private dropdownItems: NodeListOf<HTMLElement>;
|
||||||
|
|
||||||
@HostListener('window:resize', ['$event'])
|
@HostListener('window:resize', ['$event'])
|
||||||
onResize() {
|
onResize() {
|
||||||
this.isMobile$.pipe(
|
this.isMobile$.pipe(
|
||||||
@@ -106,23 +106,43 @@ export class ExpandableNavbarSectionComponent extends NavbarSectionComponent imp
|
|||||||
this.subs.push(this.active$.subscribe((active: boolean) => {
|
this.subs.push(this.active$.subscribe((active: boolean) => {
|
||||||
if (active === true) {
|
if (active === true) {
|
||||||
this.addArrowEventListeners = true;
|
this.addArrowEventListeners = true;
|
||||||
|
} else {
|
||||||
|
this.unsubscribeFromEventListeners();
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewChecked(): void {
|
ngAfterViewChecked(): void {
|
||||||
if (this.addArrowEventListeners) {
|
if (this.addArrowEventListeners) {
|
||||||
const dropdownItems: NodeListOf<HTMLElement> = document.querySelectorAll(`#${this.expandableNavbarSectionId()} *[role="menuitem"]`);
|
this.dropdownItems = document.querySelectorAll(`#${this.expandableNavbarSectionId()} *[role="menuitem"]`);
|
||||||
dropdownItems.forEach((item: HTMLElement) => {
|
this.dropdownItems.forEach((item: HTMLElement) => {
|
||||||
item.addEventListener('keydown', this.navigateDropdown.bind(this));
|
item.addEventListener('keydown', this.navigateDropdown.bind(this));
|
||||||
});
|
});
|
||||||
if (dropdownItems.length > 0) {
|
if (this.dropdownItems.length > 0) {
|
||||||
dropdownItems.item(0).focus();
|
this.dropdownItems.item(0).focus();
|
||||||
}
|
}
|
||||||
this.addArrowEventListeners = false;
|
this.addArrowEventListeners = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
super.ngOnDestroy();
|
||||||
|
this.unsubscribeFromEventListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all the current event listeners on the dropdown items (called when the menu is closed & on component
|
||||||
|
* destruction)
|
||||||
|
*/
|
||||||
|
unsubscribeFromEventListeners(): void {
|
||||||
|
if (this.dropdownItems) {
|
||||||
|
this.dropdownItems.forEach((item: HTMLElement) => {
|
||||||
|
item.removeEventListener('keydown', this.navigateDropdown.bind(this));
|
||||||
|
});
|
||||||
|
this.dropdownItems = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When the mouse enters the section toggler activate the menu section
|
* When the mouse enters the section toggler activate the menu section
|
||||||
* @param $event
|
* @param $event
|
||||||
|
@@ -8,10 +8,11 @@ import {
|
|||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Directive to detect when the user hovers outside of the element the directive was put on
|
* Directive to detect when the user hovers outside the element the directive was put on
|
||||||
*
|
*
|
||||||
* BEWARE: it's probably not good for performance to use this excessively (on {@link ExpandableNavbarSectionComponent}
|
* **Performance Consideration**: it's probably not good for performance to use this excessively (on
|
||||||
* for example, a workaround for this problem was to add an `*ngIf` to prevent this Directive from always being active)
|
* {@link ExpandableNavbarSectionComponent} for example, a workaround for this problem was to add an `*ngIf` to prevent
|
||||||
|
* this Directive from always being active)
|
||||||
*/
|
*/
|
||||||
@Directive({
|
@Directive({
|
||||||
selector: '[dsHoverOutside]',
|
selector: '[dsHoverOutside]',
|
||||||
@@ -26,22 +27,23 @@ export class HoverOutsideDirective {
|
|||||||
public dsHoverOutside = new EventEmitter();
|
public dsHoverOutside = new EventEmitter();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The {@link ElementRef} for which this directive should emit when the mouse leaves it. By default this will be the
|
* CSS selector for the parent element to monitor. If set, the directive will use this
|
||||||
* element the directive was put on.
|
* selector to determine if the hover event originated within the selected parent element.
|
||||||
|
* If left unset, the directive will monitor mouseover hover events for the element it is applied to.
|
||||||
*/
|
*/
|
||||||
@Input()
|
@Input()
|
||||||
public dsHoverOutsideOfElement: ElementRef;
|
public dsHoverOutsideOfParentSelector: string;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private elementRef: ElementRef,
|
private elementRef: ElementRef,
|
||||||
) {
|
) {
|
||||||
this.dsHoverOutsideOfElement = this.elementRef;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@HostListener('document:mouseover', ['$event'])
|
@HostListener('document:mouseover', ['$event'])
|
||||||
public onMouseOver(event: MouseEvent): void {
|
public onMouseOver(event: MouseEvent): void {
|
||||||
const targetElement: HTMLElement = event.target as HTMLElement;
|
const targetElement: HTMLElement = event.target as HTMLElement;
|
||||||
const hoveredInside = this.dsHoverOutsideOfElement.nativeElement.contains(targetElement);
|
const element: Element = document.querySelector(this.dsHoverOutsideOfParentSelector);
|
||||||
|
const hoveredInside = (element ? new ElementRef(element) : this.elementRef).nativeElement.contains(targetElement);
|
||||||
|
|
||||||
if (!hoveredInside) {
|
if (!hoveredInside) {
|
||||||
this.dsHoverOutside.emit(null);
|
this.dsHoverOutside.emit(null);
|
||||||
|
@@ -10,7 +10,6 @@ import { RouterLinkActive } from '@angular/router';
|
|||||||
import { ExpandableNavbarSectionComponent as BaseComponent } from '../../../../../app/navbar/expandable-navbar-section/expandable-navbar-section.component';
|
import { ExpandableNavbarSectionComponent as BaseComponent } from '../../../../../app/navbar/expandable-navbar-section/expandable-navbar-section.component';
|
||||||
import { slide } from '../../../../../app/shared/animations/slide';
|
import { slide } from '../../../../../app/shared/animations/slide';
|
||||||
import { HoverOutsideDirective } from '../../../../../app/shared/utils/hover-outside.directive';
|
import { HoverOutsideDirective } from '../../../../../app/shared/utils/hover-outside.directive';
|
||||||
import { VarDirective } from '../../../../../app/shared/utils/var.directive';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-themed-expandable-navbar-section',
|
selector: 'ds-themed-expandable-navbar-section',
|
||||||
@@ -27,7 +26,6 @@ import { VarDirective } from '../../../../../app/shared/utils/var.directive';
|
|||||||
NgFor,
|
NgFor,
|
||||||
NgIf,
|
NgIf,
|
||||||
RouterLinkActive,
|
RouterLinkActive,
|
||||||
VarDirective,
|
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class ExpandableNavbarSectionComponent extends BaseComponent {
|
export class ExpandableNavbarSectionComponent extends BaseComponent {
|
||||||
|
Reference in New Issue
Block a user