Skip to content

Commit e6c7d44

Browse files
alisaismailatisteph-ieffam
authored andcommitted
Merged in task/dspace-cris-2023_02_x/DSC-1874 (pull request DSpace#2113)
[DSC-1874] Improve related-items popover open/close behaviour Approved-by: Stefano Maffei
2 parents 13280ff + 1e01f38 commit e6c7d44

3 files changed

Lines changed: 127 additions & 5 deletions

File tree

src/app/shared/metadata-link-view/metadata-link-view.component.html

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,12 @@
33
[ngTemplateOutletContext]="{metadataView: metadataView}"></ng-container>
44
</div>
55
<ng-template class="d-flex" #linkToAuthority let-metadataView="metadataView">
6-
<span [ngbPopover]="popContent"
7-
[closeDelay]="500"
8-
[openDelay]="1000"
6+
<span [dsStickyPopover]="popContent"
7+
[openDelay]="100"
98
[animation]="true"
10-
[autoClose]="'outside'"
9+
[autoClose]="true"
1110
container="body"
12-
triggers="mouseenter:mouseleave">
11+
triggers="mouseenter">
1312
<a rel="noopener noreferrer" data-test="linkToAuthority"
1413
[routerLink]="['/items/' + metadataView.authority]">
1514
<span dsEntityIcon
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import {
2+
ElementRef,
3+
Directive,
4+
Input,
5+
TemplateRef,
6+
Renderer2,
7+
OnInit,
8+
OnDestroy,
9+
Injector,
10+
ViewContainerRef,
11+
NgZone,
12+
ChangeDetectorRef,
13+
ApplicationRef, Inject
14+
} from '@angular/core';
15+
import {NgbPopover, NgbPopoverConfig} from '@ng-bootstrap/ng-bootstrap';
16+
import {DOCUMENT} from '@angular/common';
17+
import {NavigationStart, Router} from '@angular/router';
18+
import {Subscription} from 'rxjs';
19+
20+
/**
21+
* Directive to create a sticky popover using NgbPopover.
22+
* The popover remains open when the mouse is over its content and closes when the mouse leaves.
23+
*/
24+
@Directive({
25+
selector: '[dsStickyPopover]'
26+
})
27+
export class StickyPopoverDirective extends NgbPopover implements OnInit, OnDestroy {
28+
/** Template for the sticky popover content */
29+
@Input() dsStickyPopover: TemplateRef<any>;
30+
31+
/** Subscriptions to manage router events */
32+
subs: Subscription[] = [];
33+
34+
/** Flag to determine if the popover can be closed */
35+
private canClosePopover: boolean;
36+
37+
/** Reference to the element the directive is applied to */
38+
private readonly _elRef;
39+
40+
/** Renderer to listen to and manipulate DOM elements */
41+
private readonly _render;
42+
43+
constructor(
44+
_elementRef: ElementRef<HTMLElement>,
45+
_renderer: Renderer2, injector: Injector,
46+
viewContainerRef: ViewContainerRef,
47+
config: NgbPopoverConfig,
48+
_ngZone: NgZone,
49+
@Inject(DOCUMENT) _document: Document,
50+
_changeDetector: ChangeDetectorRef,
51+
applicationRef: ApplicationRef,
52+
private router: Router
53+
) {
54+
super(_elementRef, _renderer, injector, viewContainerRef, config, _ngZone, document, _changeDetector, applicationRef);
55+
this._elRef = _elementRef;
56+
this._render = _renderer;
57+
this.triggers = 'manual';
58+
this.container = 'body';
59+
}
60+
61+
/**
62+
* Sets up event listeners for mouse enter, mouse leave, and click events.
63+
*/
64+
ngOnInit(): void {
65+
super.ngOnInit();
66+
this.ngbPopover = this.dsStickyPopover;
67+
68+
this._render.listen(this._elRef.nativeElement, 'mouseenter', () => {
69+
this.canClosePopover = true;
70+
this.open();
71+
});
72+
73+
this._render.listen(this._elRef.nativeElement, 'mouseleave', () => {
74+
setTimeout(() => {
75+
if (this.canClosePopover) {
76+
this.close();
77+
}
78+
}, 100);
79+
});
80+
81+
this._render.listen(this._elRef.nativeElement, 'click', () => {
82+
this.close();
83+
});
84+
85+
this.subs.push(
86+
this.router.events.subscribe((event) => {
87+
if (event instanceof NavigationStart) {
88+
this.close();
89+
}
90+
})
91+
);
92+
}
93+
94+
/**
95+
* Opens the popover and sets up event listeners for mouse over and mouse out events on the popover.
96+
*/
97+
open() {
98+
super.open();
99+
const popover = window.document.querySelector('.popover');
100+
this._render.listen(popover, 'mouseover', () => {
101+
this.canClosePopover = false;
102+
});
103+
104+
this._render.listen(popover, 'mouseout', () => {
105+
this.canClosePopover = true;
106+
setTimeout(() => {
107+
if (this.canClosePopover) {
108+
this.close();
109+
}
110+
}, 0);
111+
});
112+
}
113+
114+
/**
115+
* Unsubscribes from all subscriptions when the directive is destroyed.
116+
*/
117+
ngOnDestroy() {
118+
super.ngOnDestroy();
119+
this.subs.forEach((sub) => sub.unsubscribe());
120+
}
121+
}

src/app/shared/shared.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,7 @@ import { ThemedDefaultBrowseElementsComponent } from './browse-most-elements/def
357357
import { MetadataLinkViewPopoverComponent } from './metadata-link-view/metadata-link-view-popover/metadata-link-view-popover.component';
358358
import { MetadataLinkViewAvatarPopoverComponent } from './metadata-link-view/metadata-link-view-avatar-popover/metadata-link-view-avatar-popover.component';
359359
import { MetadataLinkViewOrcidComponent } from './metadata-link-view/metadata-link-view-orcid/metadata-link-view-orcid.component';
360+
import {StickyPopoverDirective} from './metadata-link-view/sticky-popover.directive';
360361

361362
const MODULES = [
362363
CommonModule,
@@ -623,6 +624,7 @@ const DIRECTIVES = [
623624
ContextHelpDirective,
624625
EntityIconDirective,
625626
MarkdownDirective,
627+
StickyPopoverDirective
626628
];
627629

628630
@NgModule({

0 commit comments

Comments
 (0)