Skip to content

Commit bcac3b2

Browse files
authored
Merge pull request #182 from arturovt/refactor/reduce-rxjs
refactor: reduce RxJS overhead
2 parents 9f85cfc + 2d2ce67 commit bcac3b2

File tree

1 file changed

+54
-32
lines changed

1 file changed

+54
-32
lines changed

projects/ngneat/helipopper/src/lib/tippy.directive.ts

Lines changed: 54 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ import {
2222
import { isPlatformServer } from '@angular/common';
2323
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
2424
import type { Instance } from 'tippy.js';
25-
import { fromEvent, merge, Observable, Subject } from 'rxjs';
26-
import { filter, map, switchMap, takeUntil } from 'rxjs/operators';
25+
import { merge, Observable, Subject } from 'rxjs';
26+
import { switchMap, takeUntil } from 'rxjs/operators';
2727
import {
2828
Content,
2929
isComponent,
@@ -70,6 +70,9 @@ const defaultTriggerTarget: TippyProps['triggerTarget'] = null;
7070
const defaultZIndex: TippyProps['zIndex'] = 9999;
7171
const defaultAnimation: TippyProps['animation'] = 'fade';
7272

73+
// Available since Angular 20.
74+
declare const ngServerMode: boolean;
75+
7376
@Directive({
7477
// eslint-disable-next-line @angular-eslint/directive-selector
7578
selector: '[tp]',
@@ -224,7 +227,10 @@ export class TippyDirective implements OnChanges, AfterViewInit, OnInit {
224227
}
225228

226229
private destroyRef = inject(DestroyRef);
227-
private isServer = isPlatformServer(inject(PLATFORM_ID));
230+
private isServer =
231+
// Drop `isPlatformServer` once `ngServeMode` is available during compilation.
232+
(typeof ngServerMode !== 'undefined' && ngServerMode) ||
233+
isPlatformServer(inject(PLATFORM_ID));
228234
private tippyFactory = inject(TippyFactory);
229235
private destroyed = false;
230236
private created = false;
@@ -523,35 +529,52 @@ export class TippyDirective implements OnChanges, AfterViewInit, OnInit {
523529
}
524530

525531
protected handleContextMenu() {
526-
fromEvent<MouseEvent>(this.host(), 'contextmenu')
527-
.pipe(takeUntilDestroyed(this.destroyRef))
528-
.subscribe((event: MouseEvent) => {
529-
event.preventDefault();
530-
531-
this.instance.setProps({
532-
getReferenceClientRect: () =>
533-
({
534-
width: 0,
535-
height: 0,
536-
top: event.clientY,
537-
bottom: event.clientY,
538-
left: event.clientX,
539-
right: event.clientX,
540-
} as DOMRectReadOnly),
541-
});
542-
543-
this.instance.show();
532+
const host = this.host();
533+
const onContextMenu = (event: MouseEvent) => {
534+
event.preventDefault();
535+
536+
this.instance.setProps({
537+
getReferenceClientRect: () =>
538+
({
539+
width: 0,
540+
height: 0,
541+
top: event.clientY,
542+
bottom: event.clientY,
543+
left: event.clientX,
544+
right: event.clientX,
545+
} as DOMRectReadOnly),
544546
});
547+
548+
this.instance.show();
549+
};
550+
551+
host.addEventListener('contextmenu', onContextMenu);
552+
this.destroyRef.onDestroy(() =>
553+
host.removeEventListener('contextmenu', onContextMenu)
554+
);
545555
}
546556

547557
protected handleEscapeButton(): void {
548-
fromEvent<KeyboardEvent>(document.body, 'keydown')
549-
.pipe(
550-
filter(({ code }: KeyboardEvent) => code === 'Escape'),
551-
takeUntil(this.visibleInternal.pipe(filter((v) => !v))),
552-
takeUntilDestroyed(this.destroyRef)
553-
)
554-
.subscribe(() => this.hide());
558+
const onKeydown = (event: KeyboardEvent) => {
559+
if (event.code === 'Escape') {
560+
this.hide();
561+
}
562+
};
563+
564+
document.body.addEventListener('keydown', onKeydown);
565+
566+
// Remove listener when `visibleInternal` becomes false.
567+
const visibleSubscription = this.visibleInternal.subscribe((v) => {
568+
if (!v) {
569+
document.body.removeEventListener('keydown', onKeydown);
570+
visibleSubscription.unsubscribe();
571+
}
572+
});
573+
574+
this.destroyRef.onDestroy(() => {
575+
document.body.removeEventListener('keydown', onKeydown);
576+
visibleSubscription.unsubscribe();
577+
});
555578
}
556579

557580
protected checkOverflow(isElementOverflow: boolean) {
@@ -607,16 +630,15 @@ export class TippyDirective implements OnChanges, AfterViewInit, OnInit {
607630
this.contentChanged.pipe(
608631
// We need to wait for the content to be rendered before we can check if it's overflowing.
609632
switchMap(() => {
610-
return new Observable((subscriber) => {
633+
return new Observable<boolean>((subscriber) => {
611634
const id = window.requestAnimationFrame(() => {
612-
subscriber.next();
635+
subscriber.next(isElementOverflow(host));
613636
subscriber.complete();
614637
});
615638

616639
return () => cancelAnimationFrame(id);
617640
});
618-
}),
619-
map(() => isElementOverflow(host))
641+
})
620642
)
621643
);
622644
}

0 commit comments

Comments
 (0)