@progress/kendo-angular-tooltip
Version:
Kendo UI Tooltip for Angular - A highly customizable and easily themeable tooltip from the creators developers trust for professional Angular components.
193 lines (192 loc) • 8.13 kB
JavaScript
/**-----------------------------------------------------------------------------------------
* Copyright © 2025 Progress Software Corporation. All rights reserved.
* Licensed under commercial license. See LICENSE.md in the project root for more information
*-------------------------------------------------------------------------------------------*/
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Directive, ElementRef, Input, NgZone, Renderer2 } from "@angular/core";
import { closest } from "@progress/kendo-angular-common";
import { PopupService } from "@progress/kendo-angular-popup";
import { filter, take } from "rxjs/operators";
import { closestBySelector } from "../utils";
import { PopoverDirectivesBase } from './directives-base';
import { PopoverService } from "./popover.service";
import * as i0 from "@angular/core";
import * as i1 from "@progress/kendo-angular-popup";
import * as i2 from "./popover.service";
/**
* Represents the [`kendoPopoverContainer`](slug:configuration_popover#toc-popover-container) directive.
* It is used to filter and target multiple elements, which should display a popover on interaction.
*
* @example
* ```ts-no-run
* <div kendoPopoverContainer [popover]="myPopover" filter=".has-popover">
* <button class="has-popover">Show Popover</button>
* <button>Button Without Popover</button>
* <button class="has-popover">Show Popover</button>
* </div>
* ```
*/
export class PopoverContainerDirective extends PopoverDirectivesBase {
wrapperEl;
ngZone;
popupService;
renderer;
popoverService;
/**
* Specifies a selector for the elements that should display a popover.
*
* The possible values include any valid query selector.
* [See example](slug:configuration_popover#toc-popover-container)
*/
filter;
constructor(wrapperEl, ngZone, popupService, renderer, popoverService) {
super(ngZone, popupService, renderer);
this.wrapperEl = wrapperEl;
this.ngZone = ngZone;
this.popupService = popupService;
this.renderer = renderer;
this.popoverService = popoverService;
this._popoverService = this.popoverService;
}
/**
* Shows the Popover.
*
* @param anchor—Specifies the element that will be used as an anchor. The Popover opens relative to that element. [See example]({% slug programmaticcontrol_popover %})
*/
show(anchor) {
if (this.popupRef) {
return;
}
this.ngZone.run(() => {
this.openPopup(anchor);
});
this.popupRef.popupAnchorViewportLeave
.pipe(take(1))
.subscribe(() => this.hide());
}
/**
* Toggles the visibility of the Popover. [See example]({% slug programmaticcontrol_popover %})
*
* @param anchor—Specifies the element that will be used as an anchor. The Popover opens relative to that element.
*/
toggle(anchor) {
const previousAnchor = this.popupRef && this.popupRef.content.instance.anchor;
if (this.popupRef) {
this.hide();
if (previousAnchor !== anchor) {
this.show(anchor);
}
}
else {
this.show(anchor);
}
}
subscribeClick() {
if (this.disposeClickListener) {
this.disposeClickListener();
}
this.disposeClickListener = this.renderer.listen(document, 'click', (e) => {
const filterElement = closestBySelector(e.target, this.filter);
this.clickHandler(filterElement, e);
});
}
mouseenterHandler = (anchor) => {
this.controlVisibility(anchor, true);
};
mouseleaveHandler = (args) => {
const anchor = args.anchor;
if (this.isPrevented(anchor, false)) {
return;
}
if (!this._hideSub) {
this._hideSub = this.popoverService.hidePopover.subscribe((val) => {
const [isPopoverHovered, , isOriginAnchor, currentAnchor] = val;
if (!isPopoverHovered && !isOriginAnchor) {
this.hide();
if (!isOriginAnchor && currentAnchor) {
this.show(currentAnchor);
}
}
});
}
};
focusHandler = (anchor) => {
this.controlVisibility(anchor, true);
};
blurHandler = (args) => {
const anchor = args.anchor;
const event = args.domEvent;
if (this.isPrevented(anchor, false)) {
return;
}
// from anchor to popup focus check
const isFocusInside = !!closest(event.relatedTarget, (node) => node.classList && node.classList.contains('k-popover'));
if (!isFocusInside) {
this.hide();
}
if (!this._focusInsideSub) {
// inside popup focus check
this._focusInsideSub = this.popoverService.isFocusInsidePopover.pipe(filter(v => v !== null)).subscribe((val) => {
if (!val && !isFocusInside) {
this.hide();
}
});
}
};
subscribeToShowEvents(arr) {
const filteredElements = Array.from(document.querySelectorAll(this.filter));
filteredElements.forEach((el) => {
this.subs.add(this.renderer.listen(el, arr[0].name, () => {
this.popoverService.emitAnchorState(true, el);
arr[0].handler(el);
}));
this.subs.add(this.renderer.listen(el, arr[1].name, (e) => {
this.popoverService.emitAnchorState(false, null);
arr[1].handler({ anchor: el, domEvent: e });
}));
});
}
clickHandler(anchor, event) {
const isInsidePopup = !!closest(event.target, (node) => node.classList && node.classList.contains('k-popup'));
const popupRefAnchor = this.popupRef && this.popupRef.content.instance.anchor;
const isOriginAnchor = !!closest(event.target, (node) => node === (popupRefAnchor ? popupRefAnchor : anchor));
if (this.showOn !== 'click' || isInsidePopup || (this.popupRef && isOriginAnchor)) {
return;
}
if (!anchor && this.popupRef) {
this.controlVisibility(anchor, false);
return;
}
if (isOriginAnchor) {
this.controlVisibility(anchor, true);
}
else if (this.popupRef) {
this.controlVisibility(anchor, false);
this.controlVisibility(anchor, true);
}
}
controlVisibility(anchor, show) {
if (this.isPrevented(anchor, show)) {
return;
}
if (show) {
this.show(anchor);
}
else {
this.hide();
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PopoverContainerDirective, deps: [{ token: i0.ElementRef }, { token: i0.NgZone }, { token: i1.PopupService }, { token: i0.Renderer2 }, { token: i2.PopoverService }], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: PopoverContainerDirective, isStandalone: true, selector: "[kendoPopoverContainer]", inputs: { filter: "filter" }, providers: [PopoverService], exportAs: ["kendoPopoverContainer"], usesInheritance: true, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PopoverContainerDirective, decorators: [{
type: Directive,
args: [{
selector: '[kendoPopoverContainer]',
exportAs: 'kendoPopoverContainer',
providers: [PopoverService],
standalone: true
}]
}], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.NgZone }, { type: i1.PopupService }, { type: i0.Renderer2 }, { type: i2.PopoverService }]; }, propDecorators: { filter: [{
type: Input
}] } });