Files
frontend-hideyoshi.com/src/app/shared/directive/clicked-outside/clicked-outside.directive.ts
2023-10-15 02:07:29 -03:00

116 lines
3.0 KiB
TypeScript

import { DOCUMENT } from '@angular/common';
import {
AfterViewInit,
Directive,
ElementRef,
EventEmitter,
Inject,
Input,
OnDestroy,
Output,
ViewChild,
} from '@angular/core';
import {
combineLatest,
combineLatestWith,
filter,
fromEvent,
merge,
Subscription,
} from 'rxjs';
@Directive({
selector: '[appClickedOutside]',
})
export class ClickedOutsideDirective implements AfterViewInit, OnDestroy {
@Input()
ignoreElementList!: HTMLDivElement[];
@Input()
includeClickedOutside!: HTMLDivElement[];
@Input()
clickOutsideStopWatching: boolean = false;
@Output()
clickOutside: EventEmitter<void> = new EventEmitter();
eventListener!: Subscription;
constructor(
private element: ElementRef,
@Inject(DOCUMENT) private document: Document,
) {}
ngAfterViewInit(): void {
const clickListener$ = fromEvent(this.document, 'click');
this.eventListener = clickListener$
.pipe(
filter((click) => {
return (
(this.isOutside(click.target as HTMLElement) ||
this.isInIncludedList(
click.target as HTMLElement,
)) &&
this.notInIgnoredList(click.target as HTMLElement)
);
}),
)
.subscribe(() => {
!this.clickOutsideStopWatching && this.clickOutside.emit();
});
}
ngOnDestroy(): void {
this.eventListener?.unsubscribe();
}
private isOutside(elementToCheck: HTMLElement): boolean {
let status = true;
if (
this.element.nativeElement === elementToCheck ||
this.element.nativeElement.contains(elementToCheck)
) {
status = false;
}
return status;
}
private notInIgnoredList(elementToCheck: HTMLElement): boolean {
if (!this.ignoreElementList || this.ignoreElementList.length === 0) {
return false;
}
let validateIsIgnored = (ignoreElement: HTMLDivElement): boolean => {
return (
ignoreElement === elementToCheck ||
ignoreElement.contains(elementToCheck) ||
elementToCheck.contains(ignoreElement)
);
};
return !this.ignoreElementList.some(validateIsIgnored);
}
private isInIncludedList(elementToCheck: HTMLElement): boolean {
if (
!this.includeClickedOutside ||
this.includeClickedOutside.length === 0
) {
return false;
}
let validateIsIncluded = (includedElement: HTMLDivElement): boolean => {
return (
includedElement === elementToCheck ||
includedElement.contains(elementToCheck) ||
elementToCheck.contains(includedElement)
);
};
return !this.includeClickedOutside.some(validateIsIncluded);
}
}