mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-13 21:13:07 +00:00
116404: Added navigation with arrow keys in navbar & collapsed the expandable menu when hovering outside of it
(cherry picked from commit 05232cdf2b
)
This commit is contained in:

committed by
github-actions[bot]
![github-actions[bot]](/assets/img/avatar_default.png)
parent
17e03330d0
commit
b9b1d3fd8d
@@ -5,11 +5,14 @@ import {
|
||||
NgIf,
|
||||
} from '@angular/common';
|
||||
import {
|
||||
AfterViewChecked,
|
||||
Component,
|
||||
ElementRef,
|
||||
HostListener,
|
||||
Inject,
|
||||
Injector,
|
||||
OnInit,
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import { RouterLinkActive } from '@angular/router';
|
||||
import { Observable } from 'rxjs';
|
||||
@@ -19,6 +22,8 @@ import { slide } from '../../shared/animations/slide';
|
||||
import { HostWindowService } from '../../shared/host-window.service';
|
||||
import { MenuService } from '../../shared/menu/menu.service';
|
||||
import { MenuID } from '../../shared/menu/menu-id.model';
|
||||
import { MenuSection } from '../../shared/menu/menu-section.model';
|
||||
import { HoverOutsideDirective } from '../../shared/utils/hover-outside.directive';
|
||||
import { VarDirective } from '../../shared/utils/var.directive';
|
||||
import { NavbarSectionComponent } from '../navbar-section/navbar-section.component';
|
||||
|
||||
@@ -31,9 +36,20 @@ import { NavbarSectionComponent } from '../navbar-section/navbar-section.compone
|
||||
styleUrls: ['./expandable-navbar-section.component.scss'],
|
||||
animations: [slide],
|
||||
standalone: true,
|
||||
imports: [VarDirective, RouterLinkActive, NgComponentOutlet, NgIf, NgFor, AsyncPipe],
|
||||
imports: [
|
||||
AsyncPipe,
|
||||
HoverOutsideDirective,
|
||||
NgComponentOutlet,
|
||||
NgFor,
|
||||
NgIf,
|
||||
RouterLinkActive,
|
||||
VarDirective,
|
||||
],
|
||||
})
|
||||
export class ExpandableNavbarSectionComponent extends NavbarSectionComponent implements OnInit {
|
||||
export class ExpandableNavbarSectionComponent extends NavbarSectionComponent implements AfterViewChecked, OnInit {
|
||||
|
||||
@ViewChild('expandableNavbarSectionContainer') expandableNavbarSection: ElementRef;
|
||||
|
||||
/**
|
||||
* This section resides in the Public Navbar
|
||||
*/
|
||||
@@ -54,6 +70,13 @@ export class ExpandableNavbarSectionComponent extends NavbarSectionComponent imp
|
||||
*/
|
||||
isMobile$: Observable<boolean>;
|
||||
|
||||
/**
|
||||
* Boolean used to add the event listeners to the items in the expandable menu when expanded. This is done for
|
||||
* performance reasons, there is currently an *ngIf on the menu to prevent the {@link HoverOutsideDirective} to tank
|
||||
* performance when not expanded.
|
||||
*/
|
||||
addArrowEventListeners = false;
|
||||
|
||||
@HostListener('window:resize', ['$event'])
|
||||
onResize() {
|
||||
this.isMobile$.pipe(
|
||||
@@ -68,17 +91,33 @@ export class ExpandableNavbarSectionComponent extends NavbarSectionComponent imp
|
||||
});
|
||||
}
|
||||
|
||||
constructor(@Inject('sectionDataProvider') menuSection,
|
||||
protected menuService: MenuService,
|
||||
protected injector: Injector,
|
||||
private windowService: HostWindowService,
|
||||
constructor(
|
||||
@Inject('sectionDataProvider') public section: MenuSection,
|
||||
protected menuService: MenuService,
|
||||
protected injector: Injector,
|
||||
protected windowService: HostWindowService,
|
||||
) {
|
||||
super(menuSection, menuService, injector);
|
||||
super(section, menuService, injector);
|
||||
this.isMobile$ = this.windowService.isMobile();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
super.ngOnInit();
|
||||
this.subs.push(this.active$.subscribe((active: boolean) => {
|
||||
if (active === true) {
|
||||
this.addArrowEventListeners = true;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
ngAfterViewChecked(): void {
|
||||
if (this.addArrowEventListeners) {
|
||||
const dropdownItems = document.querySelectorAll(`#${this.expandableNavbarSectionId()} *[role="menuitem"]`);
|
||||
dropdownItems.forEach(item => {
|
||||
item.addEventListener('keydown', this.navigateDropdown.bind(this));
|
||||
});
|
||||
this.addArrowEventListeners = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,9 +152,54 @@ export class ExpandableNavbarSectionComponent extends NavbarSectionComponent imp
|
||||
|
||||
/**
|
||||
* returns the ID of the DOM element representing the navbar section
|
||||
* @param sectionId
|
||||
*/
|
||||
expandableNavbarSectionId(sectionId: string) {
|
||||
return `expandable-navbar-section-${sectionId}-dropdown`;
|
||||
expandableNavbarSectionId(): string {
|
||||
return `expandable-navbar-section-${this.section.id}-dropdown`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the navigation between the menu items
|
||||
*
|
||||
* @param event
|
||||
*/
|
||||
navigateDropdown(event: KeyboardEvent): void {
|
||||
if (event.key === 'Tab') {
|
||||
this.deactivateSection(event, false);
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const items: NodeListOf<Element> = document.querySelectorAll(`#${this.expandableNavbarSectionId()} *[role="menuitem"]`);
|
||||
if (items.length === 0) {
|
||||
return;
|
||||
}
|
||||
const currentIndex: number = Array.from(items).findIndex((item: Element) => item === event.target);
|
||||
|
||||
if (event.key === 'ArrowDown') {
|
||||
(items[(currentIndex + 1) % items.length] as HTMLElement).focus();
|
||||
} else if (event.key === 'ArrowUp') {
|
||||
(items[(currentIndex - 1 + items.length) % items.length] as HTMLElement).focus();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles all the keydown events on the dropdown toggle
|
||||
*
|
||||
* @param event
|
||||
*/
|
||||
keyDown(event: KeyboardEvent): void {
|
||||
switch (event.code) {
|
||||
// Works for both Tab & Shift Tab
|
||||
case 'Tab':
|
||||
this.deactivateSection(event, false);
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
this.navigateDropdown(event);
|
||||
break;
|
||||
case 'Space':
|
||||
event.preventDefault();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user