@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.
2,543 lines • 97.3 kB
JavaScript
/**-----------------------------------------------------------------------------------------
* Copyright © 2025 Progress Software Corporation. All rights reserved.
* Licensed under commercial license. See LICENSE.md in the project root for more information
*-------------------------------------------------------------------------------------------*/
import * as i0 from '@angular/core';
import { forwardRef, Directive, Input, Optional, isDevMode, EventEmitter, Component, HostBinding, Output, ViewChild, ContentChild, ElementRef, Injectable, InjectionToken, Inject, NgModule } from '@angular/core';
import * as i1 from '@progress/kendo-angular-l10n';
import { ComponentMessages, LocalizationService, L10N_PREFIX } from '@progress/kendo-angular-l10n';
import { PreventableEvent, focusableSelector, Keys, isDocumentAvailable, closest, hasObservers, ResizeBatchService } from '@progress/kendo-angular-common';
import * as i1$1 from '@progress/kendo-angular-popup';
import { PopupService } from '@progress/kendo-angular-popup';
import { take, auditTime, filter } from 'rxjs/operators';
import { Subscription, BehaviorSubject, Subject, combineLatest, fromEvent } from 'rxjs';
import { validatePackage } from '@progress/kendo-licensing';
import { NgIf, NgStyle, NgClass, NgTemplateOutlet } from '@angular/common';
import { xIcon } from '@progress/kendo-svg-icons';
import { IconWrapperComponent, IconsService } from '@progress/kendo-angular-icons';
/**
* @hidden
*/
class LocalizedMessagesDirective extends ComponentMessages {
service;
/**
* The title of the close button.
*/
closeTitle;
constructor(service) {
super();
this.service = service;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: LocalizedMessagesDirective, deps: [{ token: i1.LocalizationService }], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: LocalizedMessagesDirective, isStandalone: true, selector: "[kendoTooltipLocalizedMessages]", inputs: { closeTitle: "closeTitle" }, providers: [
{
provide: ComponentMessages,
useExisting: forwardRef(() => LocalizedMessagesDirective)
}
], usesInheritance: true, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: LocalizedMessagesDirective, decorators: [{
type: Directive,
args: [{
providers: [
{
provide: ComponentMessages,
useExisting: forwardRef(() => LocalizedMessagesDirective)
}
],
selector: `[kendoTooltipLocalizedMessages]`,
standalone: true
}]
}], ctorParameters: function () { return [{ type: i1.LocalizationService }]; }, propDecorators: { closeTitle: [{
type: Input
}] } });
/**
* @hidden
*/
const ERRORS = {
popover: `Invalid value provided for the 'popover' property. The accepted data types are 'PopoverComponent' or 'PopoverFn'.`,
templateData: `templateData must be a function, but received`,
showOn: `Invalid value provided for the 'showOn' property. The available options are 'click', 'hover', 'focus' or 'none'.`
};
/**
* Arguments for the `show` event. The `show` event fires when a popover is about
* to be opened. If you cancel the event, the opening is prevented.
*/
class PopoverShowEvent extends PreventableEvent {
/**
* The host element related to the popover.
*/
anchor;
/**
* @hidden
* Constructs the event arguments for the `show` event.
* @param anchor - The host element related to the popover.
*/
constructor(anchor) {
super();
this.anchor = anchor;
}
}
/**
* Arguments for the `hide` event. The `hide` event fires when a popover is about
* to be closed. If you cancel the event, the popover stays open.
*/
class PopoverHideEvent extends PreventableEvent {
/**
* The host element related to the popover.
*/
anchor;
/**
* The popover element.
*/
popover;
/**
* @hidden
* Constructs the event arguments for the `hide` event.
* @param anchor - The host element related to the popover.
* @param popover - The popover element.
*/
constructor(anchor, popover) {
super();
this.anchor = anchor;
this.popover = popover;
}
}
/**
* Arguments for the `shown` event. The `shown` event fires after the popover has opened and its opening animation has finished.
*/
class PopoverShownEvent {
/**
* The host element related to the popover.
*/
anchor;
/**
* The popover element.
*/
popover;
/**
* @hidden
* Constructs the event arguments for the `shown` event.
* @param anchor - The host element related to the popover.
* @param popover - The popover element.
*/
constructor(anchor, popover) {
this.anchor = anchor;
this.popover = popover;
}
}
/**
* Arguments for the `hidden` event. The `hidden` event fires after the popover has closed and its closing animation has finished.
*/
class PopoverHiddenEvent {
/**
* The host element related to the popover.
*/
anchor;
/**
* @hidden
* Constructs the event arguments for the `hidden` event.
* @param anchor - The host element related to the popover.
*/
constructor(anchor) {
this.anchor = anchor;
}
}
/**
* @hidden
*/
let idx = 0;
/**
* @hidden
*/
let popoverTitleIdx = 0;
/**
* @hidden
*/
let popoverBodyIdx = 0;
/**
* @hidden
*/
const getId = (prefix, idSource) => {
switch (idSource) {
case 'popoverTitle':
return `${prefix}-${++popoverTitleIdx}`;
case 'popoverBody':
return `${prefix}-${++popoverBodyIdx}`;
default:
return `${prefix}-${++idx}`;
}
};
/**
* @hidden
*/
function align(position, offset) {
let anchorAlign = {};
let popupAlign = {};
let popupMargin = {};
switch (position) {
case 'top':
anchorAlign = { horizontal: 'center', vertical: 'top' };
popupAlign = { horizontal: 'center', vertical: 'bottom' };
popupMargin = { horizontal: 0, vertical: offset };
break;
case 'bottom':
anchorAlign = { horizontal: 'center', vertical: 'bottom' };
popupAlign = { horizontal: 'center', vertical: 'top' };
popupMargin = { horizontal: 0, vertical: offset };
break;
case 'right':
anchorAlign = { horizontal: 'right', vertical: 'center' };
popupAlign = { horizontal: 'left', vertical: 'center' };
popupMargin = { horizontal: offset, vertical: 0 };
break;
case 'left':
anchorAlign = { horizontal: 'left', vertical: 'center' };
popupAlign = { horizontal: 'right', vertical: 'center' };
popupMargin = { horizontal: offset, vertical: 0 };
break;
default: break;
}
return {
anchorAlign,
popupAlign,
popupMargin
};
}
/**
* @hidden
*/
function collision(inputcollision, position) {
if (inputcollision) {
return inputcollision;
}
if (position === 'top' || position === 'bottom') {
return { horizontal: 'fit', vertical: 'flip' };
}
return { horizontal: 'flip', vertical: 'fit' };
}
function isDocumentNode(container) {
return container.nodeType === 9;
}
/**
* @hidden
*/
function closestBySelector(element, selector) {
if (element.closest) {
return element.closest(selector);
}
const matches = Element.prototype.matches ?
(el, sel) => el.matches(sel)
: (el, sel) => el.msMatchesSelector(sel);
let node = element;
while (node && !isDocumentNode(node)) {
if (matches(node, selector)) {
return node;
}
node = node.parentNode;
}
}
/**
* @hidden
*/
function contains(container, child) {
if (!container) {
return false;
}
if (isDocumentNode(container)) {
return false;
}
if (container.contains) {
return container.contains(child);
}
if (container.compareDocumentPosition) {
return !!(container.compareDocumentPosition(child) & Node.DOCUMENT_POSITION_CONTAINED_BY);
}
}
/**
* @hidden
*/
const hasParent = (node, parent) => {
while (node && node !== parent) {
node = node.parentNode;
}
return node;
};
/**
* @hidden
*/
function getCenterOffset(item, dir, size) {
const rect = item.getBoundingClientRect();
return rect[dir] + (rect[size] / 2);
}
/**
* @hidden
*/
function containsItem(collection, item) {
return collection.indexOf(item) !== -1;
}
/**
* @hidden
*/
function getAllFocusableChildren(parent) {
return parent.querySelectorAll(focusableSelector);
}
/**
* @hidden
*/
function getFirstAndLastFocusable(parent) {
const all = getAllFocusableChildren(parent);
const firstFocusable = all.length > 0 ? all[0] : parent;
const lastFocusable = all.length > 0 ? all[all.length - 1] : parent;
return [firstFocusable, lastFocusable];
}
/**
* @hidden
*/
const packageMetadata = {
name: '@progress/kendo-angular-tooltip',
productName: 'Kendo UI for Angular',
productCode: 'KENDOUIANGULAR',
productCodes: ['KENDOUIANGULAR'],
publishDate: 1743579699,
version: '18.4.0',
licensingDocsUrl: 'https://www.telerik.com/kendo-angular-ui/my-license/'
};
/**
* Represents a template that defines the content of the Popover title.
*
* To define the template, nest an `<ng-template>` tag
* with the `kendoPopoverTitleTemplate` directive inside the `<kendo-popover>` tag.
*/
class PopoverTitleTemplateDirective {
templateRef;
constructor(templateRef) {
this.templateRef = templateRef;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PopoverTitleTemplateDirective, deps: [{ token: i0.TemplateRef, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: PopoverTitleTemplateDirective, isStandalone: true, selector: "[kendoPopoverTitleTemplate]", ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PopoverTitleTemplateDirective, decorators: [{
type: Directive,
args: [{
selector: '[kendoPopoverTitleTemplate]',
standalone: true
}]
}], ctorParameters: function () { return [{ type: i0.TemplateRef, decorators: [{
type: Optional
}] }]; } });
/**
* Represents a template that defines the content of the Popover body.
*
* To define the template, nest an `<ng-template>` tag
* with the `kendoPopoverBodyTemplate` directive inside the `<kendo-popover>` tag.
*/
class PopoverBodyTemplateDirective {
templateRef;
constructor(templateRef) {
this.templateRef = templateRef;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PopoverBodyTemplateDirective, deps: [{ token: i0.TemplateRef, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: PopoverBodyTemplateDirective, isStandalone: true, selector: "[kendoPopoverBodyTemplate]", ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PopoverBodyTemplateDirective, decorators: [{
type: Directive,
args: [{
selector: '[kendoPopoverBodyTemplate]',
standalone: true
}]
}], ctorParameters: function () { return [{ type: i0.TemplateRef, decorators: [{
type: Optional
}] }]; } });
/**
* Represents a template that defines the content of the Popover actions.
*
* To define the template, nest an `<ng-template>` tag
* with the `kendoPopoverActionsTemplate` directive inside the `<kendo-popover>` tag.
*/
class PopoverActionsTemplateDirective {
templateRef;
constructor(templateRef) {
this.templateRef = templateRef;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PopoverActionsTemplateDirective, deps: [{ token: i0.TemplateRef, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: PopoverActionsTemplateDirective, isStandalone: true, selector: "[kendoPopoverActionsTemplate]", ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PopoverActionsTemplateDirective, decorators: [{
type: Directive,
args: [{
selector: '[kendoPopoverActionsTemplate]',
standalone: true
}]
}], ctorParameters: function () { return [{ type: i0.TemplateRef, decorators: [{
type: Optional
}] }]; } });
/* eslint-disable @typescript-eslint/no-explicit-any */
/**
* Represents the [Kendo UI Popover component for Angular]({% slug overview_popover %}).
* Used to display additional information that is related to a target element.
*
* @example
* ```ts-no-run
* <kendo-popover>
* <ng-template kendoPopoverTitleTemplate>Foo Title</ng-template>
* <ng-template kendoPopoverBodyTemplate>Foo Body</ng-template>
* <ng-template kendoPopoverActionsTemplate>Foo Actions</ng-template>
* </kendo-popover>
* ```
*/
class PopoverComponent {
localization;
renderer;
element;
zone;
/**
* @hidden
*/
anchor;
/**
* Specifies the position of the Popover in relation to its anchor element. [See example]({% slug positioning_popover %})
*
* The possible options are:
* `top`
* `bottom`
* `right` (Default)
* `left`
*/
position = 'right';
/**
* Specifies the distance from the Popover to its anchor element in pixels.
*
* @default `6`
*/
set offset(value) {
this._offset = value;
}
get offset() {
const calloutBuffer = 14;
return this.callout
? calloutBuffer + this._offset
: this._offset;
}
/**
* Determines the width of the popover. Numeric values are treated as pixels.
* @default 'auto'
*/
set width(value) {
this._width = typeof value === 'number' ? `${value}px` : value;
}
get width() {
return this._width;
}
/**
* Determines the height of the popover. Numeric values are treated as pixels.
* @default 'auto'
*/
set height(value) {
this._height = typeof value === 'number' ? `${value}px` : value;
}
get height() {
return this._height;
}
/**
* @hidden
*/
direction;
/**
* Specifies the main header text of the Popover.
*
* If a `titleTemplate` is provided it would take precedence over the title.
*/
title;
/**
* @hidden
* Specifies the secondary header text of the Popover.
*
* If a `titleTemplate` is provided it would take precedence over the subtitle.
*/
subtitle;
/**
* Represents the text that will be rendered in the Popover body section.
*
* If a `bodyTemplate` is provided it would take precedence over this text.
*/
body;
/**
* Determines whether a callout will be rendered along the Popover. [See example]({% slug callout_popover %})
*
* @default true
*/
callout = true;
/**
* Enables and configures the Popover animation. [See example]({% slug animations_popover %})
*
* The possible options are:
*
* * `boolean`—Enables the default animation
* * `PopoverAnimation`—A configuration object which allows setting the `direction`, `duration` and `type` of the animation.
*
* @default false
*/
animation = false;
/**
* Defines a callback function which returns custom data passed to the Popover templates.
* It exposes the current anchor element as an argument. [See example](slug:templates_popover#toc-passing-data-to-templates)
*/
set templateData(fn) {
if (isDevMode && typeof fn !== 'function') {
throw new Error(`${ERRORS.templateData} ${JSON.stringify(fn)}.`);
}
this._templateData = fn;
}
get templateData() {
return this._templateData;
}
/**
* @hidden
* Determines the visibility of the Popover.
*/
visible = false;
/**
* @hidden
*/
get isHidden() {
return !this.visible;
}
/**
* @hidden
*/
get hasAttributeHidden() {
return !this.visible;
}
/**
* Fires before the Popover is about to be shown ([see example]({% slug events_popover %})).
* The event is preventable. If canceled, the Popover will not be displayed. [See example]({% slug events_popover %})
*/
show = new EventEmitter();
/**
* Fires after the Popover has been shown and the animation has ended. [See example]({% slug events_popover %})
*/
shown = new EventEmitter();
/**
* Fires when the Popover is about to be hidden ([see example]({% slug events_popover %})).
* The event is preventable. If canceled, the Popover will remain visible.
*/
hide = new EventEmitter();
/**
* Fires after the Popover has been hidden and the animation has ended. [See example]({% slug events_popover %})
*/
hidden = new EventEmitter();
/**
* @hidden
*/
closeOnKeyDown = new EventEmitter();
/**
* @hidden
*/
popoverWrapper;
/**
* @hidden
*/
titleTemplateWrapper;
/**
* @hidden
*/
bodyTemplateWrapper;
/**
* @hidden
*/
titleTemplate;
/**
* @hidden
*/
bodyTemplate;
/**
* @hidden
*/
actionsTemplate;
/**
* @hidden
*/
contextData;
/**
* @hidden
*/
_width = 'auto';
/**
* @hidden
*/
_height = 'auto';
/**
* @hidden
*/
popoverId = '';
_offset = 6;
subs = new Subscription();
constructor(localization, renderer, element, zone) {
this.localization = localization;
this.renderer = renderer;
this.element = element;
this.zone = zone;
validatePackage(packageMetadata);
}
ngOnInit() {
this.popoverId = getId('k-popover');
this.subs.add(this.localization.changes.subscribe(({ rtl }) => { this.direction = rtl ? 'rtl' : 'ltr'; }));
this.subs.add(this.renderer.listen(this.element.nativeElement, 'keydown', event => this.onKeyDown(event)));
}
ngAfterViewInit() {
this.zone.onStable.pipe(take(1)).subscribe(() => {
if (this.visible) {
const wrapper = this.popoverWrapper.nativeElement;
const focusablePopoverChildren = getAllFocusableChildren(wrapper);
if (focusablePopoverChildren.length > 0) {
focusablePopoverChildren[0].focus();
}
this.setAriaAttributes(wrapper, focusablePopoverChildren);
}
});
}
ngOnDestroy() {
this.subs.unsubscribe();
}
/**
* @hidden
*/
getCalloutPosition() {
switch (this.position) {
case 'top': return { 'k-callout-s': true };
case 'bottom': return { 'k-callout-n': true };
case 'left': return { 'k-callout-e': true };
case 'right': return { 'k-callout-w': true };
default: return { 'k-callout-s': true };
}
}
/**
* @hidden
*/
onKeyDown(event) {
const keyCode = event.keyCode;
const target = event.target;
if (keyCode === Keys.Tab) {
this.keepFocusWithinComponent(target, event);
}
if (keyCode === Keys.Escape) {
this.closeOnKeyDown.emit();
}
}
_templateData = () => null;
keepFocusWithinComponent(target, event) {
const wrapper = this.popoverWrapper.nativeElement;
const [firstFocusable, lastFocusable] = getFirstAndLastFocusable(wrapper);
const tabAfterLastFocusable = !event.shiftKey && target === lastFocusable;
const shiftTabAfterFirstFocusable = event.shiftKey && target === firstFocusable;
if (tabAfterLastFocusable) {
event.preventDefault();
firstFocusable.focus();
}
if (shiftTabAfterFirstFocusable) {
event.preventDefault();
lastFocusable.focus();
}
}
setAriaAttributes(wrapper, focusablePopoverChildren) {
if (this.titleTemplate) {
const titleRef = this.titleTemplateWrapper.nativeElement;
const focusableHeaderChildren = getAllFocusableChildren(titleRef).length > 0;
if (focusableHeaderChildren) {
const headerId = getId('k-popover-header', 'popoverTitle');
this.renderer.setAttribute(titleRef, 'id', headerId);
this.renderer.setAttribute(wrapper, 'aria-labelledby', headerId);
}
}
if (this.bodyTemplate) {
const bodyRef = this.bodyTemplateWrapper.nativeElement;
const focusableBodyChildren = getAllFocusableChildren(bodyRef).length > 0;
if (focusableBodyChildren) {
const bodyId = getId('k-popover-body', 'popoverBody');
this.renderer.setAttribute(bodyRef, 'id', bodyId);
this.renderer.setAttribute(wrapper, 'aria-describedby', bodyId);
}
}
this.renderer.setAttribute(wrapper, 'id', this.popoverId);
this.renderer.setAttribute(wrapper, 'role', focusablePopoverChildren.length > 0 ? 'dialog' : 'tooltip');
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PopoverComponent, deps: [{ token: i1.LocalizationService }, { token: i0.Renderer2 }, { token: i0.ElementRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: PopoverComponent, isStandalone: true, selector: "kendo-popover", inputs: { position: "position", offset: "offset", width: "width", height: "height", title: "title", subtitle: "subtitle", body: "body", callout: "callout", animation: "animation", templateData: "templateData" }, outputs: { show: "show", shown: "shown", hide: "hide", hidden: "hidden", closeOnKeyDown: "closeOnKeyDown" }, host: { properties: { "attr.dir": "this.direction", "class.k-hidden": "this.isHidden", "attr.aria-hidden": "this.hasAttributeHidden", "style.width": "this._width", "style.height": "this._height" } }, providers: [
LocalizationService,
{
provide: L10N_PREFIX,
useValue: 'kendo.popover'
}
], queries: [{ propertyName: "titleTemplate", first: true, predicate: PopoverTitleTemplateDirective, descendants: true }, { propertyName: "bodyTemplate", first: true, predicate: PopoverBodyTemplateDirective, descendants: true }, { propertyName: "actionsTemplate", first: true, predicate: PopoverActionsTemplateDirective, descendants: true }], viewQueries: [{ propertyName: "popoverWrapper", first: true, predicate: ["popoverWrapper"], descendants: true }, { propertyName: "titleTemplateWrapper", first: true, predicate: ["titleTemplateWrapper"], descendants: true }, { propertyName: "bodyTemplateWrapper", first: true, predicate: ["bodyTemplateWrapper"], descendants: true }], ngImport: i0, template: `
<div #popoverWrapper *ngIf="visible" class="k-popover k-popup" [ngStyle]="{'width': width, 'height': height}">
<div class="k-popover-callout" [ngClass]="getCalloutPosition()" *ngIf="callout"></div>
<div class="k-popover-inner" *ngIf="callout; else noCallout">
<ng-container *ngTemplateOutlet="noCallout"></ng-container>
</div>
<ng-template #noCallout>
<div #titleTemplateWrapper *ngIf="titleTemplate || title" class="k-popover-header">
<ng-template *ngIf="titleTemplate"
[ngTemplateOutlet]="titleTemplate?.templateRef"
[ngTemplateOutletContext]="{ $implicit: anchor, data: contextData }">
</ng-template>
<ng-container *ngIf="title && !titleTemplate">
{{ title }}
</ng-container>
</div>
<div #bodyTemplateWrapper *ngIf="bodyTemplate || body" class="k-popover-body">
<ng-template *ngIf="bodyTemplate"
[ngTemplateOutlet]="bodyTemplate?.templateRef"
[ngTemplateOutletContext]="{ $implicit: anchor, data: contextData }">
</ng-template>
<ng-container *ngIf="body && !bodyTemplate">
{{ body }}
</ng-container>
</div>
<div *ngIf="actionsTemplate" class="k-popover-actions k-actions k-actions-stretched k-actions-horizontal">
<ng-template *ngIf="actionsTemplate"
[ngTemplateOutlet]="actionsTemplate?.templateRef"
[ngTemplateOutletContext]="{ $implicit: anchor, data: contextData }">
</ng-template>
</div>
</ng-template>
</div>
`, isInline: true, dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PopoverComponent, decorators: [{
type: Component,
args: [{
selector: 'kendo-popover',
providers: [
LocalizationService,
{
provide: L10N_PREFIX,
useValue: 'kendo.popover'
}
],
template: `
<div #popoverWrapper *ngIf="visible" class="k-popover k-popup" [ngStyle]="{'width': width, 'height': height}">
<div class="k-popover-callout" [ngClass]="getCalloutPosition()" *ngIf="callout"></div>
<div class="k-popover-inner" *ngIf="callout; else noCallout">
<ng-container *ngTemplateOutlet="noCallout"></ng-container>
</div>
<ng-template #noCallout>
<div #titleTemplateWrapper *ngIf="titleTemplate || title" class="k-popover-header">
<ng-template *ngIf="titleTemplate"
[ngTemplateOutlet]="titleTemplate?.templateRef"
[ngTemplateOutletContext]="{ $implicit: anchor, data: contextData }">
</ng-template>
<ng-container *ngIf="title && !titleTemplate">
{{ title }}
</ng-container>
</div>
<div #bodyTemplateWrapper *ngIf="bodyTemplate || body" class="k-popover-body">
<ng-template *ngIf="bodyTemplate"
[ngTemplateOutlet]="bodyTemplate?.templateRef"
[ngTemplateOutletContext]="{ $implicit: anchor, data: contextData }">
</ng-template>
<ng-container *ngIf="body && !bodyTemplate">
{{ body }}
</ng-container>
</div>
<div *ngIf="actionsTemplate" class="k-popover-actions k-actions k-actions-stretched k-actions-horizontal">
<ng-template *ngIf="actionsTemplate"
[ngTemplateOutlet]="actionsTemplate?.templateRef"
[ngTemplateOutletContext]="{ $implicit: anchor, data: contextData }">
</ng-template>
</div>
</ng-template>
</div>
`,
standalone: true,
imports: [NgIf, NgStyle, NgClass, NgTemplateOutlet]
}]
}], ctorParameters: function () { return [{ type: i1.LocalizationService }, { type: i0.Renderer2 }, { type: i0.ElementRef }, { type: i0.NgZone }]; }, propDecorators: { position: [{
type: Input
}], offset: [{
type: Input
}], width: [{
type: Input
}], height: [{
type: Input
}], direction: [{
type: HostBinding,
args: ['attr.dir']
}], title: [{
type: Input
}], subtitle: [{
type: Input
}], body: [{
type: Input
}], callout: [{
type: Input
}], animation: [{
type: Input
}], templateData: [{
type: Input
}], isHidden: [{
type: HostBinding,
args: ['class.k-hidden']
}], hasAttributeHidden: [{
type: HostBinding,
args: ['attr.aria-hidden']
}], show: [{
type: Output
}], shown: [{
type: Output
}], hide: [{
type: Output
}], hidden: [{
type: Output
}], closeOnKeyDown: [{
type: Output
}], popoverWrapper: [{
type: ViewChild,
args: ['popoverWrapper']
}], titleTemplateWrapper: [{
type: ViewChild,
args: ['titleTemplateWrapper']
}], bodyTemplateWrapper: [{
type: ViewChild,
args: ['bodyTemplateWrapper']
}], titleTemplate: [{
type: ContentChild,
args: [PopoverTitleTemplateDirective, { static: false }]
}], bodyTemplate: [{
type: ContentChild,
args: [PopoverBodyTemplateDirective, { static: false }]
}], actionsTemplate: [{
type: ContentChild,
args: [PopoverActionsTemplateDirective, { static: false }]
}], _width: [{
type: HostBinding,
args: ['style.width']
}], _height: [{
type: HostBinding,
args: ['style.height']
}] } });
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable @typescript-eslint/no-explicit-any */
const validShowOptions = ['hover', 'click', 'none', 'focus'];
/**
* @hidden
*/
class PopoverDirectivesBase {
ngZone;
popupService;
renderer;
/**
* Specifies the popover instance that will be rendered.
* Accepts a [`PopoverComponent`]({% slug api_tooltip_popovercomponent %}) instance or
* a [`PopoverFn`]({% slug api_tooltip_popoverfn %}) callback which returns a [`PopoverComponent`]({% slug api_tooltip_popovercomponent %}) instance
* depending on the current anchor element.
*
* [See example](slug:templates_popover#toc-passing-data-to-templates)
*/
set popover(value) {
if (value instanceof PopoverComponent || typeof value === `function`) {
this._popover = value;
}
else {
if (isDevMode) {
throw new Error(ERRORS.popover);
}
}
}
get popover() {
return this._popover;
}
/**
* The event on which the Popover will be shown
*
* The supported values are:
* - `click` (default) —The Popover will be shown when its `anchor` element is clicked.
* - `hover`—The Popover will be shown when its `anchor` element is hovered.
* - `focus`—The Popover will be shown when its `anchor` element is focused.
* - `none`—The Popover will not be shown on user interaction. It could be rendered via the Popover API methods.
*/
set showOn(value) {
if (isDevMode && !containsItem(validShowOptions, value)) {
throw new Error(ERRORS.showOn);
}
this._showOn = value;
}
get showOn() {
return this._showOn;
}
/**
* @hidden
*/
anchor = null;
popupRef;
disposeHoverOverListener;
disposeHoverOutListener;
disposeClickListener;
disposePopupHoverOutListener;
disposePopupHoverInListener;
disposePopupFocusOutListener;
subs = new Subscription();
_popoverService;
_hideSub;
_focusInsideSub;
_popover;
_showOn = 'click';
_popupOpenSub;
_popupCloseSub;
_popupSubs;
constructor(ngZone, popupService, renderer) {
this.ngZone = ngZone;
this.popupService = popupService;
this.renderer = renderer;
}
ngAfterViewInit() {
if (!isDocumentAvailable()) {
return;
}
this.manageEvents();
}
ngOnDestroy() {
this.closePopup();
this.subs.unsubscribe();
this._popupSubs && this._popupSubs.unsubscribe();
if (this.disposeHoverOverListener) {
this.disposeHoverOverListener();
}
if (this.disposeHoverOutListener) {
this.disposeHoverOutListener();
}
if (this.disposeClickListener) {
this.disposeClickListener();
}
if (this._focusInsideSub) {
this._focusInsideSub.unsubscribe();
}
if (this._hideSub) {
this._hideSub.unsubscribe();
}
if (this._popupOpenSub) {
this._popupOpenSub.unsubscribe();
}
if (this._popupCloseSub) {
this._popupCloseSub.unsubscribe();
}
}
/**
* Hides the Popover ([See example]({% slug programmaticcontrol_popover %})).
*/
hide() {
this.closePopup();
}
/**
* @hidden
*/
closePopup() {
if (this.popupRef) {
if (this.anchor) {
this.renderer.removeAttribute(this.anchor, 'aria-describedby');
}
this.popupRef.close();
this.popupRef = null;
if (this.disposePopupHoverOutListener) {
this.disposePopupHoverOutListener();
}
if (this.disposePopupHoverInListener) {
this.disposePopupHoverInListener();
}
if (this.disposePopupFocusOutListener) {
this.disposePopupFocusOutListener();
}
this._popupSubs.unsubscribe();
}
}
/**
* @hidden
*/
openPopup(anchor) {
this.anchor = anchor instanceof ElementRef ? anchor.nativeElement : anchor;
const popoverComp = this.popover instanceof PopoverComponent ? this.popover : this.popover(this.anchor);
const alignSettings = align(popoverComp.position, popoverComp.offset);
const anchorAlign = alignSettings.anchorAlign;
const popupAlign = alignSettings.popupAlign;
const popupMargin = alignSettings.popupMargin;
const _animation = popoverComp.animation;
this.popupRef = this.popupService.open({
anchor: { nativeElement: this.anchor },
animate: _animation,
content: PopoverComponent,
popupAlign,
anchorAlign,
margin: popupMargin,
collision: { horizontal: 'fit', vertical: 'fit' }
});
const popupInstance = this.popupRef.content.instance;
this._popupSubs = new Subscription();
if (anchor) {
this._popupSubs.add(this.renderer.listen(this.anchor, 'keydown', event => this.onKeyDown(event)));
this.renderer.setAttribute(this.anchor, 'aria-describedby', popupInstance.popoverId);
}
this._popupSubs.add(popupInstance.closeOnKeyDown.subscribe(() => {
this.anchor.focus();
this.hide();
}));
this.applySettings(this.popupRef.content, popoverComp);
this.monitorPopup();
this.initializeCompletionEvents(popoverComp, this.anchor);
}
/**
* @hidden
*/
isPrevented(anchorElement, show) {
const popoverComp = this.popover instanceof PopoverComponent ? this.popover : this.popover(anchorElement);
let eventArgs;
// eslint-disable-next-line prefer-const
eventArgs = this.initializeEvents(popoverComp, eventArgs, show, anchorElement);
return eventArgs.isDefaultPrevented();
}
/**
* @hidden
*/
monitorPopup() {
if (this.showOn === 'hover') {
this.ngZone.runOutsideAngular(() => {
const popup = this.popupRef.popupElement;
this.disposePopupHoverInListener = this.renderer.listen(popup, 'mouseenter', _ => {
this.ngZone.run(_ => this._popoverService.emitPopoverState(true));
});
this.disposePopupHoverOutListener = this.renderer.listen(popup, 'mouseleave', _ => {
this.ngZone.run(_ => this._popoverService.emitPopoverState(false));
});
});
}
if (this.showOn === 'focus') {
this.ngZone.runOutsideAngular(() => {
const popup = this.popupRef.popupElement;
this.disposePopupFocusOutListener = this.renderer.listen(popup, 'focusout', (e) => {
const isInsidePopover = closest(e.relatedTarget, (node) => node.classList && node.classList.contains('k-popover'));
if (!isInsidePopover) {
this.ngZone.run(_ => this._popoverService.emitFocusInsidePopover(false));
}
});
});
}
}
applySettings(contentComponent, popover) {
const content = contentComponent.instance;
content.visible = true;
content.anchor = this.anchor;
content.position = popover.position;
content.offset = popover.offset;
content.width = popover.width;
content.height = popover.height;
content.title = popover.title;
content.body = popover.body;
content.callout = popover.callout;
content.animation = popover.animation;
content.contextData = popover.templateData(this.anchor);
content.titleTemplate = popover.titleTemplate;
content.bodyTemplate = popover.bodyTemplate;
content.actionsTemplate = popover.actionsTemplate;
this.popupRef.content.changeDetectorRef.detectChanges();
}
manageEvents() {
this.ngZone.runOutsideAngular(() => {
switch (this.showOn) {
case 'hover':
this.subscribeToShowEvents([{
name: 'mouseenter', handler: this.mouseenterHandler
}, {
name: 'mouseleave', handler: this.mouseleaveHandler
}]);
break;
case 'focus':
this.subscribeToShowEvents([{
name: 'focus', handler: this.focusHandler
}, {
name: 'blur', handler: this.blurHandler
}]);
break;
case 'click':
this.subscribeClick();
break;
default:
break;
}
});
}
/**
* @hidden
*/
initializeEvents(popoverComp, eventArgs, show, anchorElement) {
if (show) {
eventArgs = new PopoverShowEvent(anchorElement);
if (this.shouldEmitEvent(!!this.popupRef, 'show', popoverComp)) {
this.ngZone.run(() => popoverComp.show.emit(eventArgs));
}
}
else {
eventArgs = new PopoverHideEvent(anchorElement, this.popupRef);
if (this.shouldEmitEvent(!!this.popupRef, 'hide', popoverComp)) {
this.ngZone.run(() => popoverComp.hide.emit(eventArgs));
}
}
return eventArgs;
}
onKeyDown(event) {
const keyCode = event.keyCode;
if (keyCode === Keys.Escape) {
this.hide();
}
}
initializeCompletionEvents(popoverComp, _anchor) {
if (this.shouldEmitCompletionEvents('shown', popoverComp)) {
this.popupRef.popupOpen.subscribe(() => {
const eventArgs = new PopoverShownEvent(_anchor, this.popupRef);
popoverComp.shown.emit(eventArgs);
});
}
if (this.shouldEmitCompletionEvents('hidden', popoverComp)) {
this.popupRef.popupClose.subscribe(() => {
this.ngZone.run(_ => {
const eventArgs = new PopoverHiddenEvent(_anchor);
popoverComp.hidden.emit(eventArgs);
});
});
}
}
shouldEmitEvent(hasPopup, event, popoverComp) {
if ((event === 'show' && !hasPopup && hasObservers(popoverComp[event]))
|| (event === 'hide' && hasPopup && hasObservers(popoverComp[event]))) {
return true;
}
return false;
}
shouldEmitCompletionEvents(event, popoverComp) {
if ((hasObservers(popoverComp[event]) && !this._popupOpenSub)
|| (hasObservers(popoverComp[event]) && !this._popupCloseSub)) {
return true;
}
return false;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PopoverDirectivesBase, deps: [{ token: i0.NgZone }, { token: i1$1.PopupService }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: PopoverDirectivesBase, inputs: { popover: "popover", showOn: "showOn" }, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PopoverDirectivesBase, decorators: [{
type: Directive,
args: [{}]
}], ctorParameters: function () { return [{ type: i0.NgZone }, { type: i1$1.PopupService }, { type: i0.Renderer2 }]; }, propDecorators: { popover: [{
type: Input
}], showOn: [{
type: Input
}] } });
/**
* @hidden
*/
class PopoverService {
ngZone;
_pointerOverPopup = new BehaviorSubject(null);
_pointerOverAnchor = new BehaviorSubject(null);
_focusInsidePopover = new BehaviorSubject(null);
_hidePopover = new Subject();
_isOrigin;
originAnchor;
currentAnchor;
subs = new Subscription();
constructor(ngZone) {
this.ngZone = ngZone;
this.monitor();
}
ngOnDestroy() {
this.subs.unsubscribe();
}
get isPopoverHovered() {
return this._pointerOverPopup.asObservable();
}
emitPopoverState(isHovered) {
this.ngZone.run(_ => this._pointerOverPopup.next(isHovered));
}
get isAnchorHovered() {
return this._pointerOverAnchor.asObservable();
}
emitAnchorState(isHovered, anchor) {
this._isOrigin = this.originAnchor === anchor;
this.currentAnchor = anchor;
if (isHovered) {
this.originAnchor = anchor;
}
this.ngZone.run(_ => this._pointerOverAnchor.next(isHovered));
}
get isFocusInsidePopover() {
return this._focusInsidePopover.asObservable();
}
emitFocusInsidePopover(isFocused) {
this.ngZone.run(_ => this._focusInsidePopover.next(isFocused));
this._focusInsidePopover.next(null);
}
get hidePopover() {
return this._hidePopover.asObservable();
}
monitor() {
this.subs.add(combineLatest(this.isPopoverHovered, this.isAnchorHovered).pipe(
// `auditTime` is used because the `mouseleave` event is emitted before `mouseenter`
// i.e. there is a millisecond in which the pointer leaves the first target (e.g. anchor) and hasn't reached the second one (e.g. popup)
// resulting in both observables emitting `false`
auditTime(20)).subscribe(val => {
const [isPopoverHovered, isAnchorHovered] = val;
this._hidePopover.next([isPopoverHovered, isAnchorHovered, this._isOrigin, this.currentAnchor]);
}));
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PopoverService, deps: [{ token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PopoverService });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PopoverService, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: i0.NgZone }]; } });
/* eslint-disable @typescript-eslint/no-explicit-any */
/**
* Represents the [`kendoPopoverAnchor`](slug:configuration_popover#toc-popover-anchor) directive.
* It is used to target an element, which should display a popover on interaction.
*
* @example
* ```ts-no-run
* <button kendoPopoverAnchor [popover]="myPopover">Show Popover</button>
* ```
*/
class PopoverAnchorDirective extends PopoverDirectivesBase {
hostEl;
ngZone;
popupService;
renderer;
popoverService;
constructor(hostEl, ngZone, popupService, renderer, popoverService) {
super(ngZone, popupService, renderer);
this.hostEl = hostEl;
this.ngZone = ngZone;
this.popupService = popupService;
this.renderer = renderer;
this.popoverService = popoverService;
this._popoverService = this.popoverService;
}
ngOnChanges(changes) {
if (changes['showOn'] && !changes['showOn'].isFirstChange()) {
this.subs.unsubscribe();
if (this.disposeClickListener) {
this.disposeClickListener();
}
this.subs = new Subscription();
this.manageEvents();
}
}
/**
* Shows the Popover. [See example]({% slug programmaticcontrol_popover %})
*/
show() {
if (this.popupRef) {
return;
}
this.ngZone.run(() => {
this.openPopup(this.hostEl);
});
this.popupRef.popupAnchorViewportLeave
.pipe(take(1))
.subscribe(() => this.hide());
}
/**
* Toggles the visibility of the Popover. [See example]({% slug programmaticcontrol_popover %})
*/
toggle() {
if (this.popupRef) {
this.hide();
}
else {
this.show();
}
}
subscribeToShowEvents(arr) {
const hostEl = this.hostEl.nativeElement;
this.subs.add(this.renderer.listen(hostEl, arr[0].name, () => {
this.popoverService.emitAnchorState(true, hostEl);
arr[0].handler();
}));
this.subs.add(this.renderer.listen(hostEl, arr[1].name, (e) => {
this.popoverService.emitAnchorState(false, null);
arr[1].handler({ domEvent: e });
}));
}
subscribeClick() {
if (this.disposeClickListener) {
this.disposeClickListener();
}
this.disposeClickListener = this.renderer.listen(document, 'click', (e) => {
this.onClick(e);
});
}
mouseenterHandler = () => {
this.controlVisibility(this.hostEl.nativeElement, true);
};
mouseleaveHandler = () => {
if (this.isPrevented(this.hostEl.nativeElement, false)) {
return;
}
if (!this._hideSub) {
this._hideSub = this.popoverService.hidePopover.subscribe((val) => {
const [isPopoverHovered, isAnchorHovered] = val;
if (!isPopoverHovered && !isAnchorHovered) {
this.hide();
}
});
}
};
focusHandler = () => {
this.controlVisibility(this.hostEl.nativeElement, true);
};
blurHandler = (args) => {
const event = args.domEvent;
if (this.isPrevented(this.hostEl.nativeElement, 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) {
this.hide();
}
});
}
};
/**
* @hidden
*/
onClick(event) {
const isInsidePopup = !!closest(event.target, (node) => node.classList && node.classList.contains('k-popup'));
const isAnchor = !!closest(event.target, (node) => node === this.hostEl.nativeElement);
if (isInsidePopup || (this.popupRef && isAnchor)) {
return;
}
if (isAnchor) {
// on opening
this.controlVisibility(this.hostEl.nativeElement, true);
}
else {
// on closing
this.controlVisibility(this.hostEl.nativeElement, false);
}
}
controlVisibility(anchor, show) {
if (this.isPrevented(anchor, show)) {
return;
}
if (show) {
this.show();
}
else {
this.hide();
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PopoverAnchorDirective, deps: [{ token: i0.ElementRef }, { token: i0.NgZone }, { token: i1$1.PopupService }, { token: i0.Renderer2 }, { token: PopoverService }], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: PopoverAnchorDirective, isStandalone: true, selector: "[kendoPopoverAnchor]", providers: [PopoverService], exportAs: ["kendoPopoverAnchor"], usesInheritance: true, usesOnChanges: true, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PopoverAnchorDirective, decorators: [{
type: Directive,
args: [{
selector: '[kendoPopoverAnchor]',
exportAs: 'kendoPopoverAnchor',
providers: [PopoverService],
standalone: true
}]
}], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.NgZone }, { type: i1$1.PopupService }, { type: i0.Renderer2 }, { type: PopoverService }]; } });
/* eslint-disable @typescript-eslint/no-explicit-any */
/**
* 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>
* ```
*/
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$1.PopupService }, { token: i0.Renderer2 }, { token: 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$1.PopupService }, { type: i0.Renderer2 }, { type: PopoverService }]; }, propDecorators: { filter: [{
type: Input
}] } });
/**
* @hidden
*/
class TooltipContentComponent {
content;
localizationService;
/**
* @hidden
*/
xIcon = xIcon;
/**
* @hidden
*/
direction;
close = new EventEmitter();
get cssClasses() {
return 'k-tooltip';
}
hostRole = 'tooltip';
get hostId() {
return this.tooltipId;
}
get className() {
return this.closable;
}
get cssPosition() {
return 'relative';
}
tooltipWidth = null;
tooltipHeight = null;
titleTemplate;
anchor;
closable;
templateRef;
templateString;
closeTitle;
callout = true;
position;
/**
* @hidden
*/
tooltipId;
dynamicRTLSubscription;
constructor(content, localizationService) {
this.content = content;
this.localizationService = localizationService;
this.direction = localizationService.rtl ? 'rtl' : 'ltr';
}
ngOnInit() {
this.tooltipId = getId('tooltip');
this.dynamicRTLSubscription = this.localizationService.changes
.subscribe(({ rtl }) => this.direction = rtl ? 'rtl' : 'ltr');
}
ngOnDestroy() {
if (this.dynamicRTLSubscription) {
this.dynamicRTLSubscription.unsubscribe();
}
}
get closeButtonTitle() {
return this.closeTitle || this.localizationService.get('closeTitle');
}
calloutPositionClass() {
return {
'top': 'k-callout-s',
'left': 'k-callout-e',
'bottom': 'k-callout-n',
'right': 'k-callout-w'
}[this.position];
}
onCloseClick(event) {
event.preventDefault();
this.close.emit();
}
updateCalloutPosition(position, isFlip) {
if (!this.callout) {
return;
}
const callout = this.content.nativeElement.querySelector('.k-callout');
const isVertical = position === 'top' || position === 'bottom';
const size = isVertical ? 'width' : 'height';
const dir = isVertical ? 'left' : 'top';
const offsetProperty = isVertical ? 'marginLeft' : 'marginTop';
const calloutSize = callout.getBoundingClientRect()[size];
const anchorCenter = getCenterOffset(this.anchor.nativeElement, dir, size);
const contentCenter = getCenterOffset(this.content.nativeElement, dir, size);
const diff = Math.abs(contentCenter - anchorCenter);
if (diff > 1 || diff === 0 || Math.round(diff) === 0) {
const newMargin = contentCenter - anchorCenter + (calloutSize / 2);
callout.style[offsetProperty] = `${-newMargin}px`;
}
const calloutStyles = this.calloutStyles(position, calloutSize, isFlip);
Object.keys(calloutStyles).forEach((style) => {
callout.style[style] = calloutStyles[style];
});
}
calloutStyles = (position, calloutSize, isFlip) => {
const styles = {};
const isVertical = position === 'top' || position === 'bottom';
const flipDeg = '180deg';
const zeroDeg = '0deg';
if (!isFlip) {
styles.transform = isVertical ? `rotateX(${zeroDeg})` : `rotateY(${zeroDeg})`;
return styles;
}
if (position === 'top') {
styles.bottom = 'unset';
}
else if (position === 'bottom') {
styles.top = 'unset';
}
else if (position === 'left') {
styles.right = 'unset';
}
else if (position === 'right') {
styles.left = 'unset';
}
styles[position] = `${-calloutSize}px`;
styles.transform = isVertical ? `rotateX(${flipDeg})` : `rotateY(${flipDeg})`;
return styles;
};
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: TooltipContentComponent, deps: [{ token: i0.ElementRef }, { token: i1.LocalizationService }], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: TooltipContentComponent, isStandalone: true, selector: "kendo-tooltip", inputs: { tooltipWidth: "tooltipWidth", tooltipHeight: "tooltipHeight", titleTemplate: "titleTemplate", anchor: "anchor", closable: "closable", templateRef: "templateRef", templateString: "templateString" }, outputs: { close: "close" }, host: { properties: { "attr.dir": "this.direction", "class": "this.cssClasses", "attr.role": "this.hostRole", "attr.id": "this.hostId", "class.k-tooltip-closable": "this.className", "style.position": "this.cssPosition", "style.width.px": "this.tooltipWidth", "style.height.px": "this.tooltipHeight" } }, providers: [
LocalizationService,
{
provide: L10N_PREFIX,
useValue: 'kendo.tooltip'
}
], ngImport: i0, template: `
<ng-container kendoTooltipLocalizedMessages
i18n-closeTitle="kendo.tooltip.closeTitle|The title of the close button"
closeTitle="Close"
>
</ng-container>
<div class="k-tooltip-content">
<div class="k-tooltip-title" *ngIf="titleTemplate">
<ng-template
[ngIf]="titleTemplate"
[ngTemplateOutlet]="titleTemplate"
[ngTemplateOutletContext]="{ $implicit: anchor, anchor: anchor }">
</ng-template>
</div>
<ng-template
[ngIf]="templateRef"
[ngTemplateOutlet]="templateRef"
[ngTemplateOutletContext]="{ $implicit: anchor, anchor: anchor }">
</ng-template>
<ng-template
[ngIf]="templateString">
{{ templateString }}
</ng-template>
</div>
<div *ngIf="closable" [attr.aria-hidden]="true" class="k-tooltip-button" (click)="onCloseClick($event)">
<a href="#" [attr.title]="closeButtonTitle" class="k-icon">
<kendo-icon-wrapper
name="x"
[svgIcon]="xIcon">
</kendo-icon-wrapper>
</a>
</div>
<div class="k-callout" *ngIf="callout" [ngClass]="calloutPositionClass()"></div>
`, isInline: true, dependencies: [{ kind: "directive", type: LocalizedMessagesDirective, selector: "[kendoTooltipLocalizedMessages]", inputs: ["closeTitle"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: IconWrapperComponent, selector: "kendo-icon-wrapper", inputs: ["name", "svgIcon", "innerCssClass", "customFontClass", "size"], exportAs: ["kendoIconWrapper"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: TooltipContentComponent, decorators: [{
type: Component,
args: [{
selector: 'kendo-tooltip',
template: `
<ng-container kendoTooltipLocalizedMessages
i18n-closeTitle="kendo.tooltip.closeTitle|The title of the close button"
closeTitle="Close"
>
</ng-container>
<div class="k-tooltip-content">
<div class="k-tooltip-title" *ngIf="titleTemplate">
<ng-template
[ngIf]="titleTemplate"
[ngTemplateOutlet]="titleTemplate"
[ngTemplateOutletContext]="{ $implicit: anchor, anchor: anchor }">
</ng-template>
</div>
<ng-template
[ngIf]="templateRef"
[ngTemplateOutlet]="templateRef"
[ngTemplateOutletContext]="{ $implicit: anchor, anchor: anchor }">
</ng-template>
<ng-template
[ngIf]="templateString">
{{ templateString }}
</ng-template>
</div>
<div *ngIf="closable" [attr.aria-hidden]="true" class="k-tooltip-button" (click)="onCloseClick($event)">
<a href="#" [attr.title]="closeButtonTitle" class="k-icon">
<kendo-icon-wrapper
name="x"
[svgIcon]="xIcon">
</kendo-icon-wrapper>
</a>
</div>
<div class="k-callout" *ngIf="callout" [ngClass]="calloutPositionClass()"></div>
`,
providers: [
LocalizationService,
{
provide: L10N_PREFIX,
useValue: 'kendo.tooltip'
}
],
standalone: true,
imports: [LocalizedMessagesDirective, NgIf, NgTemplateOutlet, IconWrapperComponent, NgClass]
}]
}], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i1.LocalizationService }]; }, propDecorators: { direction: [{
type: HostBinding,
args: ['attr.dir']
}], close: [{
type: Output
}], cssClasses: [{
type: HostBinding,
args: ['class']
}], hostRole: [{
type: HostBinding,
args: ['attr.role']
}], hostId: [{
type: HostBinding,
args: ['attr.id']
}], className: [{
type: HostBinding,
args: ['class.k-tooltip-closable']
}], cssPosition: [{
type: HostBinding,
args: ['style.position']
}], tooltipWidth: [{
type: HostBinding,
args: ['style.width.px']
}, {
type: Input
}], tooltipHeight: [{
type: HostBinding,
args: ['style.height.px']
}, {
type: Input
}], titleTemplate: [{
type: Input
}], anchor: [{
type: Input
}], closable: [{
type: Input
}], templateRef: [{
type: Input
}], templateString: [{
type: Input
}] } });
/**
* Obsolete. Provide the TooltipSettings class instead.
*
* @hidden
*/
const TOOLTIP_SETTINGS = new InjectionToken('kendo-ui-tooltip-settings');
/**
* Provides a global configuration for the Kendo UI Tooltip. Once injected through
* the `AppComponent` constructor, the configuration properties can be overridden.
*
* @example
* ```ts-no-run
* import { TooltipSettings } from '@progress/kendo-angular-tooltip';
*
* _@Component({
* selector: 'my-app',
* template: `
* <div kendoTooltip>
* <button title="Saves the current document">Save</button>
* </div>`,
* providers: [{
* provide: TooltipSettings,
* useFactory: (): TooltipSettings => ({
* // Override default values of tooltips if wanted
* position: 'right'
* })
* }]
* })
* export class AppComponent { }
* ```
*/
class TooltipSettings {
/**
* Specifies if the Тooltip will display a callout arrow.
*
* The possible values are:
* * `true` (default)
* * `false`
*/
callout;
/**
* Specifies the title of the close button.
*/
closeTitle;
/**
* Specifies the position of the Tooltip that is
* relative to the anchor element.
*
* The possible values are:
* * `top` (default)
* * `bottom`
* * `left`
* * `right`
*/
position;
/**
* Specifies when the Тooltip will be rendered.
*
* The possible values are:
* * `hover` (default)
* * `click`
* * `none`
*/
showOn;
/**
* Specifies the delay in milliseconds before the Tooltip is shown.
* * `100` (default) milliseconds.
*/
showAfter;
/**
* @hidden
*/
// eslint-disable-next-line @typescript-eslint/no-empty-function
constructor() { }
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: TooltipSettings, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: TooltipSettings });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: TooltipSettings, decorators: [{
type: Injectable
}], ctorParameters: function () { return []; } });
/**
* Represents the [Kendo UI Tooltip directive for Angular]({% slug overview_tooltip %}).
* Used to display additional information that is related to an element.
*
* @example
* ```ts-no-run
* <div kendoTooltip>
* <a title="Tooltip title" href="foo">foo</a>
* </div>
* ```
*/
class TooltipDirective {
tooltipWrapper;
ngZone;
renderer;
popupService;
/**
* Specifies a selector for elements within a container which will display a tooltip
* ([see example]({% slug anchorelements_tooltip %})). The possible values include any
* DOM `selector`. The default value is `[title]`.
*/
filter = '[title]';
/**
* Specifies the position of the Tooltip that is relative to the
* anchor element ([see example]({% slug positioning_tooltip %})).
*
* The possible values are:
* * `top` (default)
* * `bottom`
* * `left`
* * `right`
*/
position = 'top';
/**
* Renders the passed template as a header title of the Tooltip
* ([see example]({% slug anchorelements_tooltip %})).
*/
titleTemplate;
/**
* Specifies when the Тooltip will be rendered
* ([see example]({% slug programmaticopening_tooltip %})).
*
* The possible values are:
* * `hover` (default)
* * `click`
* * `none`
*/
showOn;
/**
* Specifies the delay in milliseconds before the Tooltip is shown.
* * `100` (default) milliseconds.
*/
showAfter = 100;
/**
* Specifies if the Тooltip will display a callout arrow.
*
* The possible values are:
* * `true` (default)
* * `false`
*/
callout = true;
/**
* Specifies if the Тooltip will display a **Close** button
* ([see example]({% slug closable_tooltip %})).
*
* The possible values are:
* * `true`
* * `false`
*/
closable = false;
/**
* Specifies the offset in pixels between the Tooltip and the anchor. Defaults to `6` pixels.
* If the `callout` property is set to `true`, the offset is rendered from the callout arrow.
* If the `callout` property is set to `false`, the offset is rendered from the content of the Tooltip.
*/
offset = 6;
/**
* Specifies the width of the Тooltip ([see example]({% slug anchorelements_tooltip %})).
*/
tooltipWidth;
/**
* Specifies the height of the Тooltip.
*/
tooltipHeight;
/**
* Specifies a CSS class that will be added to the Tooltip.
*/
tooltipClass;
/**
* @hidden
* Specifies a CSS class that will be added to the kendo-tooltip element.
*/
tooltipContentClass;
/**
* Provides screen boundary detection when the Тooltip is shown.
*/
collision;
/**
* Specifies the title of the close button.
*/
closeTitle;
/**
* Sets the content of the Tooltip as a template reference
* ([see example]({% slug templates_tooltip %})).
*/
set tooltipTemplate(value) {
this.template = value;
}
get tooltipTemplate() {
return this.template;
}
popupRef;
template;
showTimeout;
anchor = null;
mouseOverSubscription;
mouseOutSubscription;
mouseClickSubscription;
anchorTitleSubscription;
popupPositionChangeSubscription;
popupMouseOutSubscription;
keyboardNavigationSubscription = new Subscription();
closeClickSubscription;
validPositions = ['top', 'bottom', 'right', 'left'];
validShowOptions = ['hover', 'click', 'none'];
constructor(tooltipWrapper, ngZone, renderer, popupService, settings, legacySettings) {
this.tooltipWrapper = tooltipWrapper;
this.ngZone = ngZone;
this.renderer = renderer;
this.popupService = popupService;
validatePackage(packageMetadata);
Object.assign(this, settings, legacySettings);
this.ngZone.runOutsideAngular(() => {
const wrapper = this.tooltipWrapper.nativeElement;
this.anchorTitleSubscription = fromEvent(wrapper, 'mouseover')
.pipe(filter(() => this.filter !== ''))
.subscribe((e) => {
const filterElement = closestBySelector(e.target, this.filter);
if (filterElement) {
this.hideElementTitle({ nativeElement: filterElement });
}
});
this.mouseOverSubscription = fromEvent(wrapper, 'mouseover')
.pipe(filter(() => this.filter !== ''))
.subscribe(e => this.onMouseOver(e));
this.mouseOutSubscription = fromEvent(wrapper, 'mouseout')
.subscribe(e => this.onMouseOut(e));
});
}
/**
* Shows the Tooltip.
* @param anchor— ElementRef|Element.
* Specifies the element that will be used as an anchor. The Tooltip opens relative to that element.
*/
show(anchor) {
if (this.popupRef) {
return;
}
if (anchor instanceof Element) {
anchor = { nativeElement: anchor };
}
this.anchor = anchor;
if (this.showOn === 'hover') {
if (this.popupRef) {
return;
}
clearTimeout(this.showTimeout);
this.showTimeout = setTimeout(() => this.showContent(this.anchor), this.showAfter);
}
else {
this.hideElementTitle(this.anchor);
this.showContent(this.anchor);
}
}
/**
* Hides the Tooltip.
*/
hide() {
clearTimeout(this.showTimeout);
const anchor = this.anchor && this.anchor.nativeElement;
if (anchor && anchor.getAttribute('data-title')) {
if (!anchor.getAttribute('title') && anchor.hasAttribute('title')) {
anchor.setAttribute('title', anchor.getAttribute('data-title'));
}
anchor.setAttribute('data-title', '');
}
if (this.popupMouseOutSubscription) {
this.popupMouseOutSubscription.unsubscribe();
}
if (this.closeClickSubscription) {
this.closeClickSubscription.unsubscribe();
}
this.closePopup();
}
/**
* Toggle visibility of the Tooltip.
*
* @param anchor— ElementRef|Element. Specifies the element that will be used as an anchor.
* @param show— Optional. Boolean. Specifies if the Tooltip will be rendered.
*/
toggle(anchor, show) {
const previousAnchor = this.anchor && this.anchor.nativeElement;
if (anchor instanceof Element) {
anchor = { nativeElement: anchor };
}
if (previousAnchor !== anchor.nativeElement) {
this.hide();
}
if (previousAnchor === anchor.nativeElement && this.showOn === 'click') {
this.hide();
}
if (typeof show === 'undefined') {
show = !this.popupRef;
}
if (show) {
this.show(anchor);
}
else {
this.hide();
}
}
ngOnInit() {
if (this.showOn === undefined) {
this.showOn = 'hover';
}
this.keyboardNavigationSubscription.add(this.renderer.listen(this.tooltipWrapper.nativeElement, 'keydown', event => this.onKeyDown(event)));
this.verifyProperties();
}
ngOnChanges(changes) {
if (changes.showOn && isDocumentAvailable()) {
this.subscribeClick();
}
}
ngAfterViewChecked() {
if (!this.popupRef) {
return;
}
if (this.anchor &&
!hasParent(this.anchor.nativeElement || this.anchor, this.tooltipWrapper.nativeElement)) {
this.anchor = null;
this.hide();
}
}
ngOnDestroy() {
this.hide();
this.template = null;
this.anchorTitleSubscription.unsubscribe();
this.mouseOverSubscription.unsubscribe();
this.mouseOutSubscription.unsubscribe();
this.keyboardNavigationSubscription.unsubscribe();
if (this.mouseClickSubscription) {
this.mouseClickSubscription.unsubscribe();
}
if (this.popupPositionChangeSubscription) {
this.popupPositionChangeSubscription.unsubscribe();
}
if (this.popupMouseOutSubscription) {
this.popupMouseOutSubscription.unsubscribe();
}
}
showContent(anchorRef) {
if (!anchorRef.nativeElement.getAttribute('data-title') && !this.template) {
return;
}
this.ngZone.run(() => {
this.openPopup(anchorRef);
this.bindContent(this.popupRef.content, anchorRef);
});
this.popupRef.popupAnchorViewportLeave
.pipe(take(1))
.subscribe(() => this.hide());
}
bindContent(contentComponent, anchorRef) {
const content = contentComponent.instance;
this.closeClickSubscription = content.close
.subscribe(() => {
this.hide();
});
if (!this.template) {
content.templateString = this.anchor.nativeElement.getAttribute('data-title');
}
else {
content.templateRef = this.template;
}
if (this.titleTemplate) {
content.titleTemplate = this.titleTemplate;
}
content.closeTitle = this.closeTitle;
content.anchor = anchorRef;
content.callout = this.callout;
content.closable = this.closable;
content.position = this.position;
content.tooltipWidth = this.tooltipWidth;
content.tooltipHeight = this.tooltipHeight;
this.popupRef.content.changeDetectorRef.detectChanges();
}
hideElementTitle(elementRef) {
const element = elementRef.nativeElement;
if (element.getAttribute('title')) {
element.setAttribute('data-title', element.getAttribute('title'));
element.setAttribute('title', '');
}
}
openPopup(anchorRef) {
const alignSettings = align(this.position, this.offset);
const anchorAlign = alignSettings.anchorAlign;
const popupAlign = alignSettings.popupAlign;
const popupMargin = alignSettings.popupMargin;
this.popupRef = this.popupService.open({
anchor: anchorRef,
anchorAlign,
animate: false,
content: TooltipContentComponent,
collision: collision(this.collision, this.position),
margin: popupMargin,
popupAlign,
popupClass: 'k-popup-transparent'
});
if (this.tooltipClass) {
this.renderer.addClass(this.popupRef.popupElement, this.tooltipClass);
}
if (this.tooltipContentClass) {
this.renderer.addClass(this.popupRef.content.instance['content'].nativeElement, this.tooltipContentClass);
}
const popupInstance = this.popupRef.content.instance;
if (anchorRef) {
this.renderer.setAttribute(anchorRef.nativeElement, 'aria-labelledby', popupInstance.tooltipId);
}
if (popupInstance.callout) {
this.popupPositionChangeSubscription = this.popupRef.popupPositionChange
.subscribe(({ flip }) => {
const isFlip = flip.horizontal === true || flip.vertical === true;
popupInstance.updateCalloutPosition(this.position, isFlip);
});
}
if (this.showOn === 'hover') {
this.ngZone.runOutsideAngular(() => {
const popup = this.popupRef.popupElement;
this.popupMouseOutSubscription = fromEvent(popup, 'mouseout')
.subscribe((e) => this.onMouseOut(e));
});
}
}
closePopup() {
if (this.popupRef) {
if (this.anchor) {
this.renderer.removeAttribute(this.anchor.nativeElement, 'aria-labelledby');
}
this.popupRef.close();
this.popupRef = null;
}
if (this.popupPositionChangeSubscription) {
this.popupPositionChangeSubscription.unsubscribe();
}
}
subscribeClick() {
if (this.mouseClickSubscription) {
this.mouseClickSubscription.unsubscribe();
}
if (this.showOn === 'click') {
this.mouseClickSubscription = fromEvent(document, 'click')
.pipe(filter(() => this.filter !== ''))
.subscribe(e => this.onMouseClick(e, this.tooltipWrapper.nativeElement));
}
}
onMouseClick(e, wrapper) {
const target = e.target;
const filterElement = closestBySelector(target, this.filter);
const popup = this.popupRef && this.popupRef.popupElement;
if (popup) {
if (popup.contains(target)) {
return;
}
if (this.closable) {
return;
}
}
if (wrapper.contains(target) && filterElement) {
this.toggle(filterElement, true);
}
else if (popup) {
this.hide();
}
}
onKeyDown(event) {
const keyCode = event.keyCode;
const target = event.target;
if (this.popupRef) {
const tooltipId = this.popupRef.content.location.nativeElement.getAttribute('id');
const anchorLabelledBy = target.getAttribute('aria-labelledby');
if (keyCode === Keys.Escape && this.canCloseTooltip(target, tooltipId, anchorLabelledBy)) {
this.closePopup();
}
}
}
canCloseTooltip(target, tooltipId, anchorLabelledBy) {
const isIdEqualsLabel = tooltipId === anchorLabelledBy;
const filterElement = closestBySelector(target, this.filter);
const isTargetFocused = target === document.activeElement;
const isTargetInsideWrapper = this.tooltipWrapper.nativeElement.contains(target);
return isTargetInsideWrapper && filterElement && isTargetFocused && isIdEqualsLabel;
}
onMouseOver(e) {
const filterElement = closestBySelector(e.target, this.filter);
if (this.showOn !== 'hover') {
return;
}
if (filterElement) {
this.toggle(filterElement, true);
}
}
onMouseOut(e) {
if (this.showOn !== 'hover') {
return;
}
if (this.closable) {
return;
}
const popup = this.popupRef && this.popupRef.popupElement;
const relatedTarget = e.relatedTarget;
if (relatedTarget && this.anchor && contains(this.anchor.nativeElement, relatedTarget)) {
return;
}
if (relatedTarget && contains(popup, relatedTarget)) {
return;
}
this.hide();
}
verifyProperties() {
if (!isDevMode()) {
return;
}
if (!containsItem(this.validPositions, this.position)) {
throw new Error(`Invalid value provided for position property.The available options are 'top', 'bottom', 'left', or 'right'.`);
}
if (!containsItem(this.validShowOptions, this.showOn)) {
throw new Error(`Invalid value provided for showOn property.The available options are 'hover' or 'none'.`);
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: TooltipDirective, deps: [{ token: i0.ElementRef }, { token: i0.NgZone }, { token: i0.Renderer2 }, { token: i1$1.PopupService }, { token: TooltipSettings, optional: true }, { token: TOOLTIP_SETTINGS, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: TooltipDirective, isStandalone: true, selector: "[kendoTooltip]", inputs: { filter: "filter", position: "position", titleTemplate: "titleTemplate", showOn: "showOn", showAfter: "showAfter", callout: "callout", closable: "closable", offset: "offset", tooltipWidth: "tooltipWidth", tooltipHeight: "tooltipHeight", tooltipClass: "tooltipClass", tooltipContentClass: "tooltipContentClass", collision: "collision", closeTitle: "closeTitle", tooltipTemplate: "tooltipTemplate" }, exportAs: ["kendoTooltip"], usesOnChanges: true, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: TooltipDirective, decorators: [{
type: Directive,
args: [{
selector: '[kendoTooltip]',
exportAs: 'kendoTooltip',
standalone: true
}]
}], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.NgZone }, { type: i0.Renderer2 }, { type: i1$1.PopupService }, { type: TooltipSettings, decorators: [{
type: Optional
}] }, { type: TooltipSettings, decorators: [{
type: Optional
}, {
type: Inject,
args: [TOOLTIP_SETTINGS]
}] }]; }, propDecorators: { filter: [{
type: Input
}], position: [{
type: Input
}], titleTemplate: [{
type: Input
}], showOn: [{
type: Input
}], showAfter: [{
type: Input
}], callout: [{
type: Input
}], closable: [{
type: Input
}], offset: [{
type: Input
}], tooltipWidth: [{
type: Input
}], tooltipHeight: [{
type: Input
}], tooltipClass: [{
type: Input
}], tooltipContentClass: [{
type: Input
}], collision: [{
type: Input
}], closeTitle: [{
type: Input
}], tooltipTemplate: [{
type: Input
}] } });
/**
* Utility array that contains all `Tooltip` related components and directives
*/
const KENDO_TOOLTIP = [
TooltipDirective,
TooltipContentComponent,
LocalizedMessagesDirective
];
/**
* Utility array that contains all `Popover` related components and directives
*/
const KENDO_POPOVER = [
PopoverComponent,
PopoverActionsTemplateDirective,
PopoverBodyTemplateDirective,
PopoverTitleTemplateDirective,
PopoverAnchorDirective,
PopoverContainerDirective
];
/**
* Utility array that contains all `@progress/kendo-angular-tooltip` related components and directives
*/
const KENDO_TOOLTIPS = [
...KENDO_TOOLTIP,
...KENDO_POPOVER
];
// IMPORTANT: NgModule export kept for backwards compatibility
/**
* Represents the [NgModule](link:site.data.urls.angular['ngmoduleapi'])
* definition for the Tooltip component.
*
* The package exports:
* - `KendoTooltipDirective`—The Tooltip directive class.
*
* @example
*
* ```ts-no-run
* // Import the Tooltip module
* import { TooltipModule } from '@progress/kendo-angular-tooltip';
*
* // The browser platform with a compiler
* import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
*
* import { NgModule } from '@angular/core';
*
* // Import the app component
* _@NgModule{{
* declarations: [AppComponent], // declare app component
* imports: [BrowserModule, TooltipModule], // import TooltipModule module
* bootstrap: [AppComponent]
* }}
* export class AppModule {}
*
* // Compile and launch the module
* platformBrowserDynamic().bootstrapModule(AppModule);
* ```
*/
class TooltipModule {
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: TooltipModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "16.2.12", ngImport: i0, type: TooltipModule, imports: [TooltipDirective, TooltipContentComponent, LocalizedMessagesDirective], exports: [TooltipDirective, TooltipContentComponent, LocalizedMessagesDirective] });
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: TooltipModule, providers: [PopupService, ResizeBatchService, IconsService], imports: [TooltipContentComponent] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: TooltipModule, decorators: [{
type: NgModule,
args: [{
imports: [...KENDO_TOOLTIP],
exports: [...KENDO_TOOLTIP],
providers: [PopupService, ResizeBatchService, IconsService]
}]
}] });
// IMPORTANT: NgModule export kept for backwards compatibility
/**
* Represents the [NgModule](link:site.data.urls.angular['ngmoduleapi'])
* definition for the Popover component.
*
* @example
*
* ```ts-no-run
* import { PopoverModule } from '@progress/kendo-angular-tooltip';
*
* import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
* import { NgModule } from '@angular/core';
*
* import { AppComponent } from './app.component';
*
* _@NgModule({
* declarations: [AppComponent],
* imports: [BrowserModule, PopoverModule],
* bootstrap: [AppComponent]
* })
* export class AppModule {}
*
* platformBrowserDynamic().bootstrapModule(AppModule);
*
* ```
*/
class PopoverModule {
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PopoverModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "16.2.12", ngImport: i0, type: PopoverModule, imports: [PopoverComponent, PopoverActionsTemplateDirective, PopoverBodyTemplateDirective, PopoverTitleTemplateDirective, PopoverAnchorDirective, PopoverContainerDirective], exports: [PopoverComponent, PopoverActionsTemplateDirective, PopoverBodyTemplateDirective, PopoverTitleTemplateDirective, PopoverAnchorDirective, PopoverContainerDirective] });
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PopoverModule, providers: [PopupService, ResizeBatchService] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PopoverModule, decorators: [{
type: NgModule,
args: [{
exports: [...KENDO_POPOVER],
imports: [...KENDO_POPOVER],
providers: [PopupService, ResizeBatchService]
}]
}] });
// IMPORTANT: NgModule export kept for backwards compatibility
/**
* Represents the [NgModule](link:site.data.urls.angular['ngmoduleapi'])
* definition for the Tooltips components.
*
* @example
*
* ```ts-no-run
* import { TooltipsModule } from '@progress/kendo-angular-tooltip';
*
* // The browser platform with a compiler
* import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
*
* import { NgModule } from '@angular/core';
*
* // Import the app component
* _@NgModule{{
* declarations: [AppComponent], // declare app component
* imports: [BrowserModule, TooltipsModule], // import TooltipsModule module
* bootstrap: [AppComponent]
* }}
* export class AppModule {}
*
* // Compile and launch the module
* platformBrowserDynamic().bootstrapModule(AppModule);
* ```
*/
class TooltipsModule {
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: TooltipsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "16.2.12", ngImport: i0, type: TooltipsModule, imports: [TooltipDirective, TooltipContentComponent, LocalizedMessagesDirective, PopoverComponent, PopoverActionsTemplateDirective, PopoverBodyTemplateDirective, PopoverTitleTemplateDirective, PopoverAnchorDirective, PopoverContainerDirective], exports: [TooltipDirective, TooltipContentComponent, LocalizedMessagesDirective, PopoverComponent, PopoverActionsTemplateDirective, PopoverBodyTemplateDirective, PopoverTitleTemplateDirective, PopoverAnchorDirective, PopoverContainerDirective] });
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: TooltipsModule, providers: [PopupService, ResizeBatchService, IconsService], imports: [TooltipContentComponent] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: TooltipsModule, decorators: [{
type: NgModule,
args: [{
imports: [...KENDO_TOOLTIPS],
exports: [...KENDO_TOOLTIPS],
providers: [PopupService, ResizeBatchService, IconsService]
}]
}] });
// Standalone Directives Arrays
/**
* Generated bundle index. Do not edit.
*/
export { KENDO_POPOVER, KENDO_TOOLTIP, KENDO_TOOLTIPS, LocalizedMessagesDirective, PopoverActionsTemplateDirective, PopoverAnchorDirective, PopoverBodyTemplateDirective, PopoverComponent, PopoverContainerDirective, PopoverHiddenEvent, PopoverHideEvent, PopoverModule, PopoverShowEvent, PopoverShownEvent, PopoverTitleTemplateDirective, TOOLTIP_SETTINGS, TooltipContentComponent, TooltipDirective, TooltipModule, TooltipSettings, TooltipsModule };