@controllable-ui/ngx-tooltip
Version:
> ✨ Check out the demo [here](https://ciprian-anin.github.io/controllable-ui-angular/).
806 lines • 64.5 kB
JavaScript
import * as i1 from '@angular/common';
import { CommonModule } from '@angular/common';
import * as i0 from '@angular/core';
import { input, ViewEncapsulation, ChangeDetectionStrategy, Component, computed, output, viewChild, signal, effect } from '@angular/core';
class NgxTooltipArrowComponent {
arrowSize = input();
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: NgxTooltipArrowComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.0.4", type: NgxTooltipArrowComponent, isStandalone: true, selector: "ngx-tooltip-arrow", inputs: { arrowSize: { classPropertyName: "arrowSize", publicName: "arrowSize", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div\n class=\"ngxTooltip-arrow\"\n [ngStyle]=\"{ 'width.px': arrowSize(), 'height.px': arrowSize() }\"\n>\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 -16 32 32\">\n <path d=\"M16 0l16 16H0z\"></path>\n </svg>\n</div>\n", styles: [".ngxTooltip-dialog-with-bridge{--arrow-offset: 16px}.ngxTooltip-placement-top-start .ngxTooltip-arrow{margin-left:min(max(var(--relative-x) - var(--dialog-x) + var(--arrow-offset) / 2,var(--arrow-offset)),var(--dialog-width) - var(--arrow-offset));transform:rotate(-180deg)}.ngxTooltip-placement-top .ngxTooltip-arrow{margin-left:min(max(var(--relative-x) + var(--relative-width) / 2 - (var(--dialog-x) + var(--dialog-width) / 2) + var(--dialog-width) / 2,var(--arrow-offset)),var(--dialog-width) - var(--arrow-offset));transform:rotate(-180deg) translate(50%)}.ngxTooltip-placement-top-end .ngxTooltip-arrow{margin-left:min(max(var(--relative-x) + var(--relative-width) - var(--dialog-x) - var(--arrow-offset) / 2,var(--arrow-offset)),var(--dialog-width) - var(--arrow-offset));transform:rotate(-180deg) translate(100%)}.ngxTooltip-placement-top-start .ngxTooltip-animated-inner-dialog-with-bridge .ngxTooltip-arrow,.ngxTooltip-placement-top .ngxTooltip-animated-inner-dialog-with-bridge .ngxTooltip-arrow,.ngxTooltip-placement-top-end .ngxTooltip-animated-inner-dialog-with-bridge .ngxTooltip-arrow{bottom:0;margin-bottom:var(--dialog-offset)}.ngxTooltip-placement-bottom-start .ngxTooltip-arrow{margin-left:min(max(var(--relative-x) - var(--dialog-x) + var(--arrow-offset) / 2,var(--arrow-offset) / 2),var(--dialog-width) - var(--arrow-offset) / 2)}.ngxTooltip-placement-bottom .ngxTooltip-arrow{margin-left:min(max(var(--relative-x) + var(--relative-width) / 2 - (var(--dialog-x) + var(--dialog-width) / 2) + var(--dialog-width) / 2,var(--arrow-offset)),var(--dialog-width) - var(--arrow-offset));transform:translate(-50%)}.ngxTooltip-placement-bottom-end .ngxTooltip-arrow{margin-left:min(max(var(--relative-x) + var(--relative-width) - (var(--dialog-x)) - var(--arrow-offset) / 2,var(--arrow-offset) / 2),var(--dialog-width) - var(--arrow-offset) / 2);transform:translate(-100%)}.ngxTooltip-placement-bottom-start .ngxTooltip-animated-inner-dialog-with-bridge .ngxTooltip-arrow,.ngxTooltip-placement-bottom .ngxTooltip-animated-inner-dialog-with-bridge .ngxTooltip-arrow,.ngxTooltip-placement-bottom-end .ngxTooltip-animated-inner-dialog-with-bridge .ngxTooltip-arrow{top:0;margin-top:var(--dialog-offset)}.ngxTooltip-placement-left-start .ngxTooltip-arrow{margin-bottom:min(max(var(--dialog-height) - (var(--relative-y) - var(--dialog-y)),var(--arrow-size) + 3px),var(--dialog-height) - 3px);transform:rotate(90deg) translate(100%)}.ngxTooltip-placement-left .ngxTooltip-arrow{margin-bottom:min(max(var(--dialog-height) - (var(--relative-y) + var(--relative-height) / 2 - (var(--dialog-y) + var(--dialog-height) / 2) + var(--dialog-height) / 2),var(--arrow-size) - 3px),var(--dialog-height) - var(--arrow-size) + 3px);transform:rotate(90deg) translate(50%)}.ngxTooltip-placement-left-end .ngxTooltip-arrow{margin-bottom:min(max(var(--dialog-y) + var(--dialog-height) - (var(--relative-y) + var(--relative-height)) + 3px,3px),var(--dialog-height) - var(--arrow-size) - 3px);transform:rotate(90deg);transform-origin:center}.ngxTooltip-placement-left-start .ngxTooltip-animated-inner-dialog-with-bridge .ngxTooltip-arrow,.ngxTooltip-placement-left .ngxTooltip-animated-inner-dialog-with-bridge .ngxTooltip-arrow,.ngxTooltip-placement-left-end .ngxTooltip-animated-inner-dialog-with-bridge .ngxTooltip-arrow{right:0;margin-right:var(--dialog-offset)}.ngxTooltip-placement-right-start .ngxTooltip-arrow{margin-bottom:min(max(var(--dialog-height) - (var(--relative-y) - var(--dialog-y)),var(--arrow-size) + 3px),var(--dialog-height) - 3px);transform:rotate(-90deg) translate(-100%)}.ngxTooltip-placement-right .ngxTooltip-arrow{margin-bottom:min(max(var(--dialog-height) - (var(--relative-y) + var(--relative-height) / 2 - (var(--dialog-y) + var(--dialog-height) / 2) + var(--dialog-height) / 2),var(--arrow-size) - 3px),var(--dialog-height) - var(--arrow-size) + 3px);transform:rotate(-90deg) translate(-50%)}.ngxTooltip-placement-right-end .ngxTooltip-arrow{margin-bottom:min(max(var(--dialog-y) + var(--dialog-height) - (var(--relative-y) + var(--relative-height)) + 3px,3px),var(--dialog-height) - var(--arrow-size) - 3px);transform:rotate(-90deg);transform-origin:center}.ngxTooltip-placement-right-start .ngxTooltip-animated-inner-dialog-with-bridge .ngxTooltip-arrow,.ngxTooltip-placement-right .ngxTooltip-animated-inner-dialog-with-bridge .ngxTooltip-arrow,.ngxTooltip-placement-right-end .ngxTooltip-animated-inner-dialog-with-bridge .ngxTooltip-arrow{left:0;margin-left:var(--dialog-offset)}.ngxTooltip-arrow{padding:0;margin:0;color:#616161eb;width:var(--arrow-offset);height:var(--arrow-offset);line-height:0;position:absolute;bottom:0;transform-origin:center}.ngxTooltip-arrow svg{fill:currentColor;filter:drop-shadow(0 -3px 3px rgba(0,0,0,.16));height:auto}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: NgxTooltipArrowComponent, decorators: [{
type: Component,
args: [{ selector: 'ngx-tooltip-arrow', imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<div\n class=\"ngxTooltip-arrow\"\n [ngStyle]=\"{ 'width.px': arrowSize(), 'height.px': arrowSize() }\"\n>\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 -16 32 32\">\n <path d=\"M16 0l16 16H0z\"></path>\n </svg>\n</div>\n", styles: [".ngxTooltip-dialog-with-bridge{--arrow-offset: 16px}.ngxTooltip-placement-top-start .ngxTooltip-arrow{margin-left:min(max(var(--relative-x) - var(--dialog-x) + var(--arrow-offset) / 2,var(--arrow-offset)),var(--dialog-width) - var(--arrow-offset));transform:rotate(-180deg)}.ngxTooltip-placement-top .ngxTooltip-arrow{margin-left:min(max(var(--relative-x) + var(--relative-width) / 2 - (var(--dialog-x) + var(--dialog-width) / 2) + var(--dialog-width) / 2,var(--arrow-offset)),var(--dialog-width) - var(--arrow-offset));transform:rotate(-180deg) translate(50%)}.ngxTooltip-placement-top-end .ngxTooltip-arrow{margin-left:min(max(var(--relative-x) + var(--relative-width) - var(--dialog-x) - var(--arrow-offset) / 2,var(--arrow-offset)),var(--dialog-width) - var(--arrow-offset));transform:rotate(-180deg) translate(100%)}.ngxTooltip-placement-top-start .ngxTooltip-animated-inner-dialog-with-bridge .ngxTooltip-arrow,.ngxTooltip-placement-top .ngxTooltip-animated-inner-dialog-with-bridge .ngxTooltip-arrow,.ngxTooltip-placement-top-end .ngxTooltip-animated-inner-dialog-with-bridge .ngxTooltip-arrow{bottom:0;margin-bottom:var(--dialog-offset)}.ngxTooltip-placement-bottom-start .ngxTooltip-arrow{margin-left:min(max(var(--relative-x) - var(--dialog-x) + var(--arrow-offset) / 2,var(--arrow-offset) / 2),var(--dialog-width) - var(--arrow-offset) / 2)}.ngxTooltip-placement-bottom .ngxTooltip-arrow{margin-left:min(max(var(--relative-x) + var(--relative-width) / 2 - (var(--dialog-x) + var(--dialog-width) / 2) + var(--dialog-width) / 2,var(--arrow-offset)),var(--dialog-width) - var(--arrow-offset));transform:translate(-50%)}.ngxTooltip-placement-bottom-end .ngxTooltip-arrow{margin-left:min(max(var(--relative-x) + var(--relative-width) - (var(--dialog-x)) - var(--arrow-offset) / 2,var(--arrow-offset) / 2),var(--dialog-width) - var(--arrow-offset) / 2);transform:translate(-100%)}.ngxTooltip-placement-bottom-start .ngxTooltip-animated-inner-dialog-with-bridge .ngxTooltip-arrow,.ngxTooltip-placement-bottom .ngxTooltip-animated-inner-dialog-with-bridge .ngxTooltip-arrow,.ngxTooltip-placement-bottom-end .ngxTooltip-animated-inner-dialog-with-bridge .ngxTooltip-arrow{top:0;margin-top:var(--dialog-offset)}.ngxTooltip-placement-left-start .ngxTooltip-arrow{margin-bottom:min(max(var(--dialog-height) - (var(--relative-y) - var(--dialog-y)),var(--arrow-size) + 3px),var(--dialog-height) - 3px);transform:rotate(90deg) translate(100%)}.ngxTooltip-placement-left .ngxTooltip-arrow{margin-bottom:min(max(var(--dialog-height) - (var(--relative-y) + var(--relative-height) / 2 - (var(--dialog-y) + var(--dialog-height) / 2) + var(--dialog-height) / 2),var(--arrow-size) - 3px),var(--dialog-height) - var(--arrow-size) + 3px);transform:rotate(90deg) translate(50%)}.ngxTooltip-placement-left-end .ngxTooltip-arrow{margin-bottom:min(max(var(--dialog-y) + var(--dialog-height) - (var(--relative-y) + var(--relative-height)) + 3px,3px),var(--dialog-height) - var(--arrow-size) - 3px);transform:rotate(90deg);transform-origin:center}.ngxTooltip-placement-left-start .ngxTooltip-animated-inner-dialog-with-bridge .ngxTooltip-arrow,.ngxTooltip-placement-left .ngxTooltip-animated-inner-dialog-with-bridge .ngxTooltip-arrow,.ngxTooltip-placement-left-end .ngxTooltip-animated-inner-dialog-with-bridge .ngxTooltip-arrow{right:0;margin-right:var(--dialog-offset)}.ngxTooltip-placement-right-start .ngxTooltip-arrow{margin-bottom:min(max(var(--dialog-height) - (var(--relative-y) - var(--dialog-y)),var(--arrow-size) + 3px),var(--dialog-height) - 3px);transform:rotate(-90deg) translate(-100%)}.ngxTooltip-placement-right .ngxTooltip-arrow{margin-bottom:min(max(var(--dialog-height) - (var(--relative-y) + var(--relative-height) / 2 - (var(--dialog-y) + var(--dialog-height) / 2) + var(--dialog-height) / 2),var(--arrow-size) - 3px),var(--dialog-height) - var(--arrow-size) + 3px);transform:rotate(-90deg) translate(-50%)}.ngxTooltip-placement-right-end .ngxTooltip-arrow{margin-bottom:min(max(var(--dialog-y) + var(--dialog-height) - (var(--relative-y) + var(--relative-height)) + 3px,3px),var(--dialog-height) - var(--arrow-size) - 3px);transform:rotate(-90deg);transform-origin:center}.ngxTooltip-placement-right-start .ngxTooltip-animated-inner-dialog-with-bridge .ngxTooltip-arrow,.ngxTooltip-placement-right .ngxTooltip-animated-inner-dialog-with-bridge .ngxTooltip-arrow,.ngxTooltip-placement-right-end .ngxTooltip-animated-inner-dialog-with-bridge .ngxTooltip-arrow{left:0;margin-left:var(--dialog-offset)}.ngxTooltip-arrow{padding:0;margin:0;color:#616161eb;width:var(--arrow-offset);height:var(--arrow-offset);line-height:0;position:absolute;bottom:0;transform-origin:center}.ngxTooltip-arrow svg{fill:currentColor;filter:drop-shadow(0 -3px 3px rgba(0,0,0,.16));height:auto}\n"] }]
}] });
const generateRandomNumber = () => {
const seed = new Date().getTime(); // Get current timestamp
const random = Math.abs(Math.sin(seed) * 1000000); // Use sine function for randomness and scale it
return Math.floor(random); // Return a positive integer
};
const getDocumentWidth = () => document.documentElement.clientWidth;
const getDocumentHeight = () => document.documentElement.clientHeight;
const nextTickRender = () => new Promise((resolve) => setTimeout(resolve));
const delay = (milliseconds, timeoutIDSignal) => new Promise((resolve) => setTimeout(resolve, milliseconds));
function getScrollableContainer(scrollableContainer) {
return scrollableContainer ?? document.documentElement;
}
/**
* In order to avoid the annoying flickering from top to bottom or from left to right
* in case one side size has mare space than other, after we already have dialog placed on a side,
* we should try to keep dialog on current placement until min size is hit.
*
* When it is first opened it should be positioned were is the most space and
* it should remain there, decreasing height as much as we can
* (until we hit min-height / min-width) and after trying to position it
* on opposed side if there is more space (even if it needs a small decrease of height
* on the opposed side, than the default height)
* * (basically trying all placements in the order defined in placementsToBeTried)
*
*/
function getDialogAvailablePositionConsideringKeepingCurrentPlacement({ placementsToBeTried, currentPlacement, dialogElement, relativeElement, dialogMaxHeight = Infinity, dialogMinHeight = 0, dialogMaxWidth = Infinity, dialogMinWidth = 0, }) {
switch (currentPlacement) {
case 'top-start':
case 'top':
case 'top-end': {
// decrease height to fit on top; if min-height was hit try to place again on placementsToBeTried
const maxAvailableSpaceOnTop = getMaxAvailableSpaceOnTop(relativeElement);
return maxAvailableSpaceOnTop < dialogMinHeight
? getAvailablePlacementFromTheOnesToBeTried(placementsToBeTried, dialogElement, relativeElement)
: maxAvailableSpaceOnTop > dialogMaxHeight
? {
type: 'full-size-available',
placement: currentPlacement,
}
: {
type: 'partial-size-available',
placement: currentPlacement,
availableSize: maxAvailableSpaceOnTop,
};
}
case 'bottom-start':
case 'bottom':
case 'bottom-end': {
// decrease height to fit on bottom; if min-height was hit try to place again on placementsToBeTried
const maxAvailableSpaceOnBottom = getMaxAvailableSpaceOnBottom(relativeElement);
return maxAvailableSpaceOnBottom < dialogMinHeight
? getAvailablePlacementFromTheOnesToBeTried(placementsToBeTried, dialogElement, relativeElement)
: maxAvailableSpaceOnBottom > dialogMaxHeight
? {
type: 'full-size-available',
placement: currentPlacement,
}
: {
type: 'partial-size-available',
placement: currentPlacement,
availableSize: maxAvailableSpaceOnBottom,
};
}
case 'left-start':
case 'left':
case 'left-end': {
// decrease height to fit on left; if min-width was hit try to place again on placementsToBeTried
const maxAvailableSpaceOnLeft = getMaxAvailableSpaceOnLeft(relativeElement);
return maxAvailableSpaceOnLeft < dialogMinWidth
? getAvailablePlacementFromTheOnesToBeTried(placementsToBeTried, dialogElement, relativeElement)
: maxAvailableSpaceOnLeft > dialogMaxWidth
? {
type: 'full-size-available',
placement: currentPlacement,
}
: {
type: 'partial-size-available',
placement: currentPlacement,
availableSize: maxAvailableSpaceOnLeft,
};
}
case 'right-start':
case 'right':
case 'right-end': {
// decrease height to fit on right; if min-width was hit try to place again on placementsToBeTried
const maxAvailableSpaceOnRight = getMaxAvailableSpaceOnRight(relativeElement);
return maxAvailableSpaceOnRight < dialogMinWidth
? getAvailablePlacementFromTheOnesToBeTried(placementsToBeTried, dialogElement, relativeElement)
: maxAvailableSpaceOnRight > dialogMaxWidth
? {
type: 'full-size-available',
placement: currentPlacement,
}
: {
type: 'partial-size-available',
placement: currentPlacement,
availableSize: maxAvailableSpaceOnRight,
};
}
default: {
return getAvailablePlacementFromTheOnesToBeTried(placementsToBeTried, dialogElement, relativeElement);
}
}
}
function getAvailablePlacementFromTheOnesToBeTried(placementsToBeTried, dialogElement, relativeElement) {
const dialogElementRect = dialogElement.getBoundingClientRect();
const relativeElementRect = relativeElement.getBoundingClientRect();
const placementWithFullAvailableSpace = placementsToBeTried.find((placementToBeTried) => {
switch (placementToBeTried) {
case 'top-start':
case 'top':
case 'top-end': {
return relativeElementRect.top - dialogElementRect.height >= 0;
}
case 'bottom-start':
case 'bottom':
case 'bottom-end': {
return (relativeElementRect.bottom + dialogElementRect.height <=
getDocumentHeight());
}
case 'left-start':
case 'left':
case 'left-end': {
return relativeElementRect.left - dialogElementRect.width >= 0;
}
case 'right-start':
case 'right':
case 'right-end': {
return (relativeElementRect.right + dialogElementRect.width <=
getDocumentWidth());
}
}
});
if (placementWithFullAvailableSpace) {
return {
type: 'full-size-available',
placement: placementWithFullAvailableSpace,
};
}
else {
return {
type: 'partial-size-available',
...getMaxPartialAvailableSpace(placementsToBeTried, relativeElement),
};
}
}
function getMaxPartialAvailableSpace(placementsToBeTried, relativeElement) {
const availableSizeOfPlacementsToBeTried = placementsToBeTried.map((currentPlacementToBeTried) => ({
placement: currentPlacementToBeTried,
availableSize: computeMaxAvailableSizeOfDialogForPlacement(currentPlacementToBeTried, relativeElement),
}));
const placementWithMaxAvailableSpace = availableSizeOfPlacementsToBeTried.reduce((acc, value) => {
return acc.availableSize >= value.availableSize ? acc : value;
}, availableSizeOfPlacementsToBeTried[0]);
return placementWithMaxAvailableSpace;
}
function getMaxAvailableSpaceOnTop(relativeElement) {
const relativeElementRect = relativeElement.getBoundingClientRect();
return relativeElementRect.top;
}
function getMaxAvailableSpaceOnBottom(relativeElement) {
const relativeElementRect = relativeElement.getBoundingClientRect();
return getDocumentHeight() - relativeElementRect.bottom;
}
function getMaxAvailableSpaceOnLeft(relativeElement) {
const relativeElementRect = relativeElement.getBoundingClientRect();
return relativeElementRect.left;
}
function getMaxAvailableSpaceOnRight(relativeElement) {
const relativeElementRect = relativeElement.getBoundingClientRect();
return getDocumentWidth() - relativeElementRect.right;
}
function computeMaxAvailableSizeOfDialogForPlacement(placement, relativeElement) {
switch (placement) {
case 'top-start':
case 'top':
case 'top-end': {
return getMaxAvailableSpaceOnTop(relativeElement);
}
case 'bottom-start':
case 'bottom':
case 'bottom-end': {
return getMaxAvailableSpaceOnBottom(relativeElement);
}
case 'left-start':
case 'left':
case 'left-end': {
return getMaxAvailableSpaceOnLeft(relativeElement);
}
case 'right-start':
case 'right':
case 'right-end': {
return getMaxAvailableSpaceOnRight(relativeElement);
}
}
}
function getBottomEndPositionStyle({ relativeElement, dialogElement, scrollableContainer, }) {
const { right, bottom } = relativeElement.getBoundingClientRect();
const dialogElementRect = dialogElement.getBoundingClientRect();
return {
top: `${ensureAdjustBottomToBeAtMostAtTheBottomEdgeOfScrollableContainer(bottom, scrollableContainer)}px`,
left: `${ensureAdjustXToHaveWidthInsideView(right - dialogElementRect.width, dialogElementRect.width, scrollableContainer)}px`,
right: 'auto',
bottom: 'auto',
};
}
function getBottomPositionStyle({ relativeElement, dialogElement, scrollableContainer, }) {
const { x, bottom, width } = relativeElement.getBoundingClientRect();
const dialogElementRect = dialogElement.getBoundingClientRect();
return {
top: `${ensureAdjustBottomToBeAtMostAtTheBottomEdgeOfScrollableContainer(bottom, scrollableContainer)}px`,
left: `${ensureAdjustXToHaveWidthInsideView(x + width / 2 - dialogElementRect.width / 2, dialogElementRect.width, scrollableContainer)}px`,
right: 'auto',
bottom: 'auto',
};
}
function getBottomStartPositionStyle({ relativeElement, dialogElement, scrollableContainer, }) {
const { x, bottom } = relativeElement.getBoundingClientRect();
const dialogElementRect = dialogElement.getBoundingClientRect();
return {
top: `${ensureAdjustBottomToBeAtMostAtTheBottomEdgeOfScrollableContainer(bottom, scrollableContainer)}px`,
left: `${ensureAdjustXToHaveWidthInsideView(x, dialogElementRect.width, scrollableContainer)}px`,
right: 'auto',
bottom: 'auto',
};
}
function getRightEndPositionStyle({ relativeElement, dialogElement, scrollableContainer, }) {
const { right, bottom } = relativeElement.getBoundingClientRect();
const dialogElementRect = dialogElement.getBoundingClientRect();
return {
top: `${ensureAdjustYToHaveHeightInsideView(bottom - dialogElementRect.height, dialogElementRect.height, scrollableContainer)}px`,
left: `${ensureAdjustRightToBeAtMostAtTheRightEdgeOfScrollableContainer(right, scrollableContainer)}px`,
right: 'auto',
bottom: 'auto',
};
}
function getRightPositionStyle({ relativeElement, dialogElement, scrollableContainer, }) {
const { right, y, height } = relativeElement.getBoundingClientRect();
const dialogElementRect = dialogElement.getBoundingClientRect();
return {
top: `${ensureAdjustYToHaveHeightInsideView(y + height / 2 - dialogElementRect.height / 2, dialogElementRect.height, scrollableContainer)}px`,
left: `${ensureAdjustRightToBeAtMostAtTheRightEdgeOfScrollableContainer(right, scrollableContainer)}px`,
right: 'auto',
bottom: 'auto',
};
}
function getRightStartPositionStyle({ relativeElement, dialogElement, scrollableContainer, }) {
const { right, y } = relativeElement.getBoundingClientRect();
const dialogElementRect = dialogElement.getBoundingClientRect();
return {
top: `${ensureAdjustYToHaveHeightInsideView(y, dialogElementRect.height, scrollableContainer)}px`,
left: `${ensureAdjustRightToBeAtMostAtTheRightEdgeOfScrollableContainer(right, scrollableContainer)}px`,
right: 'auto',
bottom: 'auto',
};
}
function getLeftEndPositionStyle({ relativeElement, dialogElement, scrollableContainer, }) {
const { x, bottom } = relativeElement.getBoundingClientRect();
const dialogElementRect = dialogElement.getBoundingClientRect();
return {
top: `${ensureAdjustYToHaveHeightInsideView(bottom - dialogElementRect.height, dialogElementRect.height, scrollableContainer)}px`,
left: `${ensureAdjustXToBeAtMostAtTheLeftEdgeOfScrollableContainer(x, scrollableContainer) - dialogElementRect.width}px`,
right: 'auto',
bottom: 'auto',
};
}
function getLeftPositionStyle({ relativeElement, dialogElement, scrollableContainer, }) {
const { x, y, height } = relativeElement.getBoundingClientRect();
const dialogElementRect = dialogElement.getBoundingClientRect();
return {
top: `${ensureAdjustYToHaveHeightInsideView(y + height / 2 - dialogElementRect.height / 2, dialogElementRect.height, scrollableContainer)}px`,
left: `${ensureAdjustXToBeAtMostAtTheLeftEdgeOfScrollableContainer(x, scrollableContainer) - dialogElementRect.width}px`,
right: 'auto',
bottom: 'auto',
};
}
function getLeftStartPositionStyle({ relativeElement, dialogElement, scrollableContainer, }) {
const { x, y } = relativeElement.getBoundingClientRect();
const dialogElementRect = dialogElement.getBoundingClientRect();
return {
top: `${ensureAdjustYToHaveHeightInsideView(y, dialogElementRect.height, scrollableContainer)}px`,
left: `${ensureAdjustXToBeAtMostAtTheLeftEdgeOfScrollableContainer(x, scrollableContainer) - dialogElementRect.width}px`,
right: 'auto',
bottom: 'auto',
};
}
function getTopEndPositionStyle({ relativeElement, dialogElement, scrollableContainer, }) {
const { y, right } = relativeElement.getBoundingClientRect();
const dialogElementRect = dialogElement.getBoundingClientRect();
return {
top: `${ensureAdjustYToBeAtMostAtTheTopEdgeOfScrollableContainer(y, scrollableContainer) - dialogElementRect.height}px`,
left: `${ensureAdjustXToHaveWidthInsideView(right - dialogElementRect.width, dialogElementRect.width, scrollableContainer)}px`,
right: 'auto',
bottom: 'auto',
};
}
function ensureAdjustXToHaveWidthInsideView(x, dialogWidth, scrollableContainer) {
const rightEdgeOfView = scrollableContainer
? Math.min(getDocumentWidth(), scrollableContainer.getBoundingClientRect().right)
: getDocumentWidth();
const getLeftEdgeOfView = () => scrollableContainer
? Math.max(0, scrollableContainer.getBoundingClientRect().left)
: 0;
const sizeOutsideRightSide = x + dialogWidth - rightEdgeOfView;
const adjustedX = sizeOutsideRightSide > 0
? Math.max(x - sizeOutsideRightSide, 0)
: Math.max(x, getLeftEdgeOfView());
return Math.round(adjustedX);
}
function ensureAdjustYToBeAtMostAtTheTopEdgeOfScrollableContainer(y, scrollableContainer) {
return scrollableContainer
? Math.max(y, scrollableContainer?.getBoundingClientRect().y)
: y;
}
function ensureAdjustBottomToBeAtMostAtTheBottomEdgeOfScrollableContainer(bottom, scrollableContainer) {
return scrollableContainer
? Math.min(bottom, scrollableContainer.getBoundingClientRect().bottom)
: bottom;
}
function ensureAdjustXToBeAtMostAtTheLeftEdgeOfScrollableContainer(x, scrollableContainer) {
return scrollableContainer
? Math.max(x, scrollableContainer.getBoundingClientRect().x)
: x;
}
function ensureAdjustRightToBeAtMostAtTheRightEdgeOfScrollableContainer(right, scrollableContainer) {
return scrollableContainer
? Math.min(right, scrollableContainer.getBoundingClientRect().right)
: right;
}
function ensureAdjustYToHaveHeightInsideView(y, dialogHeight, scrollableContainer) {
const bottomEdgeOfView = scrollableContainer
? Math.min(getDocumentHeight(), scrollableContainer.getBoundingClientRect().bottom)
: getDocumentHeight();
const getTopEdgeOfView = () => scrollableContainer
? Math.max(0, scrollableContainer.getBoundingClientRect().top)
: 0;
const sizeOutside = y + dialogHeight - bottomEdgeOfView;
const adjustedY = sizeOutside > 0
? Math.max(y - sizeOutside, 0)
: Math.max(y, getTopEdgeOfView());
return adjustedY;
}
function getTopStartPositionStyle({ dialogElement, relativeElement, scrollableContainer, }) {
const { x, y } = relativeElement.getBoundingClientRect();
const dialogElementRect = dialogElement.getBoundingClientRect();
return {
top: `${ensureAdjustYToBeAtMostAtTheTopEdgeOfScrollableContainer(y, scrollableContainer) - dialogElementRect.height}px`,
left: `${ensureAdjustXToHaveWidthInsideView(x, dialogElementRect.width, scrollableContainer)}px`,
right: 'auto',
bottom: 'auto',
};
}
function getTopPositionStyle({ dialogElement, relativeElement, scrollableContainer, }) {
const { x, y, width } = relativeElement.getBoundingClientRect();
const dialogElementRect = dialogElement.getBoundingClientRect();
return {
top: `${ensureAdjustYToBeAtMostAtTheTopEdgeOfScrollableContainer(y, scrollableContainer) - dialogElementRect.height}px`,
left: `${ensureAdjustXToHaveWidthInsideView(x + width / 2 - dialogElementRect.width / 2, dialogElementRect.width, scrollableContainer)}px`,
right: 'auto',
bottom: 'auto',
};
}
function getDialogPositionStyle(availablePosition, dialogElement, relativeElement, scrollableContainer) {
switch (availablePosition.placement) {
case 'top-start':
return getTopStartPositionStyle({
dialogElement,
relativeElement,
scrollableContainer,
});
case 'top':
return getTopPositionStyle({
dialogElement,
relativeElement,
scrollableContainer,
});
case 'top-end':
return getTopEndPositionStyle({
relativeElement,
dialogElement,
scrollableContainer,
});
case 'bottom-start':
return getBottomStartPositionStyle({
dialogElement,
relativeElement,
scrollableContainer,
});
case 'bottom':
return getBottomPositionStyle({
dialogElement,
relativeElement,
scrollableContainer,
});
case 'bottom-end':
return getBottomEndPositionStyle({
dialogElement,
relativeElement,
scrollableContainer,
});
case 'left-start':
return getLeftStartPositionStyle({
dialogElement,
relativeElement,
scrollableContainer,
});
case 'left':
return getLeftPositionStyle({
dialogElement,
relativeElement,
scrollableContainer,
});
case 'left-end':
return getLeftEndPositionStyle({
dialogElement,
relativeElement,
scrollableContainer,
});
case 'right-start':
return getRightStartPositionStyle({
dialogElement,
relativeElement,
scrollableContainer,
});
case 'right':
return getRightPositionStyle({
dialogElement,
relativeElement,
scrollableContainer,
});
case 'right-end':
return getRightEndPositionStyle({
dialogElement,
relativeElement,
scrollableContainer,
});
}
}
const defaultOrderOfPlacementsToBeTried = {
'top-start': ['top-start', 'bottom-start', 'left', 'right'],
top: ['top', 'bottom', 'left', 'right'],
'top-end': ['top-end', 'bottom-end', 'left', 'right'],
'left-start': ['left-start', 'right-start', 'top', 'bottom'],
left: ['left', 'right', 'top', 'bottom'],
'left-end': ['left-end', 'right-end', 'top', 'bottom'],
'right-start': ['right-start', 'left-start', 'top', 'bottom'],
right: ['right', 'left', 'top', 'bottom'],
'right-end': ['right-end', 'left-end', 'top', 'bottom'],
'bottom-start': ['bottom-start', 'top-start', 'left', 'right'],
bottom: ['bottom', 'top', 'left', 'right'],
'bottom-end': ['bottom-end', 'top-end', 'left', 'right'],
};
class NgxTooltipComponent {
open = input();
preferredPlacement = input('bottom-start');
orderOfPlacementsToBeTried = input();
orderOfPlacementsToBeTriedComputed = computed(() => this.orderOfPlacementsToBeTried() ||
defaultOrderOfPlacementsToBeTried[this.preferredPlacement()]);
enterDelay = input(100);
leaveDelay = input(150);
triggerActions = input(['hover', 'focus']);
arrow = input(false);
arrowSize = computed(() => (this.arrow() ? 12 : 0));
dialogOffset = input(5);
computedDialogOffset = computed(() => this.arrow()
? this.dialogOffset() - this.arrowSize() / 2
: this.dialogOffset());
bridgeSize = computed(() => this.computedDialogOffset() + this.arrowSize());
scrollableContainer = input();
tooltipRootClass = input();
tooltipClass = input();
/**
* default - Dialog placement will be recomputed immediately after we remain
* without necessary space for dialog on current placement.
*
* considerKeepingCurrentPlacement - Keep current placement of dialog as time as it remains in
* min & max sizes boundaries.
*/
placementStrategy = input('default');
/**
*
* `dialogMinMaxSizes`:
* > is used when placementStrategy has value considerKeepingCurrentPlacement
* > In case we need to keep current position, we will use maximum & minimum sizes
* > of dialog to check if it fits in current placement, without going over its minimum sizes.
* > In case we don't have minimum size available for current placement,
* > than will be tried next place from `orderOfPlacementsToBeTried`.
* * > Maximum size is used to make sure to not have a bigger maximum size on dialog popover.
* > (we make sure to override the maximum size in case the available space is smaller than the dialog size)
*/
dialogMinMaxSizes = input();
onOpen$ = output();
onClose$ = output();
tooltipId = generateRandomNumber();
relativeElementRef = viewChild('relativeElementRef');
dialogWithBridgeRef = viewChild('dialogWithBridgeRef');
dialogRef = viewChild('dialogRef');
dialogIsOpenLocalState = signal(this.open());
dialogPositionStyle = signal({
currentPlacement: undefined,
value: {},
maxHeight: '',
maxWidth: '',
});
closeDialogTimeoutID = signal(undefined);
openDialogTimeoutID = signal(undefined);
dialogAnimationState = signal('initial');
constructor() {
effect((onCleanup) => {
if (this.dialogIsOpenLocalState() &&
this.triggerActions()?.includes('click')) {
window.addEventListener('click', this.handleClickOutsideClose);
onCleanup(() => {
window.removeEventListener('click', this.handleClickOutsideClose);
});
}
});
effect((onCleanup) => {
if (this.dialogIsOpenLocalState()) {
const scrollableContainer = getScrollableContainer(this.scrollableContainer()?.nativeElement);
scrollableContainer.addEventListener('scroll', this.positionDialog);
onCleanup(() => {
scrollableContainer.removeEventListener('scroll', this.positionDialog);
});
}
});
}
positionDialog = async () => {
if (this.dialogWithBridgeRef() &&
this.dialogRef() &&
this.relativeElementRef()) {
// compute placement of dialog using the dialogRef which doesn't include bridge,
// which is unknown before knowing the next dialog placement.
// We use dialogOffset to properly take the bridge into account when computing the available space & placement
const availablePosition = this.placementStrategy() === 'considerKeepingCurrentPlacement'
? getDialogAvailablePositionConsideringKeepingCurrentPlacement({
placementsToBeTried: this.orderOfPlacementsToBeTriedComputed(),
dialogElement: this.dialogRef().nativeElement,
relativeElement: this.relativeElementRef().nativeElement,
currentPlacement: this.dialogPositionStyle().currentPlacement,
...this.dialogMinMaxSizes?.(),
})
: getAvailablePlacementFromTheOnesToBeTried(this.orderOfPlacementsToBeTriedComputed(), this.dialogRef().nativeElement, this.relativeElementRef().nativeElement);
if (availablePosition.type === 'partial-size-available') {
// in case of partial size available to be displayed,
// apply the new height or width on element before computing
// its position for available placement location
// (for ex. if placement is left this will make sure to always
// place on left center based on new dialog height obtained
// after reducing the width of it)
switch (availablePosition.placement) {
case 'top-start':
case 'top':
case 'top-end':
case 'bottom-start':
case 'bottom':
case 'bottom-end':
// Note: maxHeight will be set on .ngxTooltip-tooltip
this.dialogPositionStyle.set({
...this.dialogPositionStyle(),
maxHeight: `${availablePosition.availableSize - this.bridgeSize()}px`,
});
break;
case 'left-start':
case 'left':
case 'left-end':
case 'right-start':
case 'right':
case 'right-end':
// Note: maxWidth will be set on .ngxTooltip-dialog-with-bridge
this.dialogPositionStyle.set({
...this.dialogPositionStyle(),
maxWidth: `${availablePosition.availableSize}px`,
});
break;
}
await nextTickRender(); // wait for new width/height to be rendered,
// to be able to compute properly, the position of dialog for
// available placement location
}
this.dialogPositionStyle.set({
...this.dialogPositionStyle(),
currentPlacement: availablePosition.placement,
});
await nextTickRender(); // wait for the placement class to be rendered,
// in order to have the bridge (padding) applied
const dialogPositionStyleProps = getDialogPositionStyle(availablePosition, this.dialogWithBridgeRef().nativeElement, // dialogWithBridgeRef have the bridge in place in this point
// so we can properly compute the position, taking into account the bridge
this.relativeElementRef().nativeElement, this.scrollableContainer()?.nativeElement);
this.dialogPositionStyle.set({
...this.dialogPositionStyle(),
value: {
...this.dialogPositionStyle().value,
...dialogPositionStyleProps,
'--scrollbar-height': `${window.innerHeight - document.documentElement.clientHeight}px`,
// visibility: "visible",
},
});
await nextTickRender(); // wait for the dialog to be rendered on the computed placement
this.dialogPositionStyle.set({
...this.dialogPositionStyle(),
value: {
...this.dialogPositionStyle().value,
...(this.arrow()
? {
...(() => {
const { x, y, width, height, right, bottom } = this.relativeElementRef()?.nativeElement.getBoundingClientRect() ??
{};
const scrollableContainerRect = getScrollableContainer(this.scrollableContainer()?.nativeElement).getBoundingClientRect();
return {
'--relative-x': `${Math.max(x, scrollableContainerRect.x)}px`,
'--relative-y': `${Math.max(y, scrollableContainerRect.y)}px`,
'--relative-width': `${Math.min(width, Math.max(0, width -
Math.max(0, right - scrollableContainerRect.right) -
Math.max(0, scrollableContainerRect.x - x)))}px`,
'--relative-height': `${Math.min(height, Math.max(0, height -
Math.max(0, bottom - scrollableContainerRect.bottom) -
Math.max(0, scrollableContainerRect.y - y)))}px`,
};
})(),
...(() => {
const { x, y, width, height } = this.dialogRef()?.nativeElement.getBoundingClientRect() ??
{};
return {
'--dialog-x': `${x}px`,
'--dialog-y': `${y}px`,
'--dialog-width': `${width}px`,
'--dialog-height': `${height}px`,
};
})(),
}
: {}),
},
});
}
};
scheduleDialogOpen = async () => {
// ! check again to make sure we open dialog just if external
// ! state specify now that the dialog should be opened
// This check is needed because due to asynchronously downloading
// the function, it might be downloaded later than scheduleDialogClose
// an in that case close would take place before scheduleDialogOpen,
// and this will end up, having dialog open, even if external state
// specify that it should be closed
if (this.open()) {
this.dialogPositionStyle.set({
...this.dialogPositionStyle(),
value: {
...this.dialogPositionStyle().value,
visibility: 'hidden',
},
});
this.openDialogTimeoutID.set(setTimeout(async () => {
// ! check again to make sure we open dialog just if external
// ! state specify now that the dialog should be opened
if (this.open()) {
await nextTickRender(); // wait for dialog to have `visibility: hidden` set
// before showing it
// This is important in order to avoid the display of it on a position
// inappropriate with requested/available placement
// * (at this moment we don't have the dialog sizes,
// * and it is not positioned on requested/available placement)
const positionDialogAndMakeItVisible = new ResizeObserver(async () => {
await this.positionDialog();
await this.positionDialog(); // call a second time to make sure that the size of dialog is computed properly
// (the first time when we call positionDialog the browser doesn't compute the height/width of dialog properly)
this.dialogPositionStyle.set({
...this.dialogPositionStyle(),
value: {
...this.dialogPositionStyle().value,
visibility: 'visible',
},
});
this.dialogAnimationState.set('show');
positionDialogAndMakeItVisible.disconnect();
});
if (this.dialogRef()) {
positionDialogAndMakeItVisible.observe(this.dialogRef().nativeElement);
this.dialogWithBridgeRef()?.nativeElement.showPopover();
this.dialogIsOpenLocalState.set(true);
}
}
}, this.enterDelay()));
}
};
closeDialog = () => {
// ! check again to make sure we close dialog just if external
// ! state specify now that the dialog should be closed
if (!this.open()) {
this.dialogWithBridgeRef()?.nativeElement.hidePopover();
this.dialogIsOpenLocalState.set(false);
this.dialogPositionStyle.set({
...this.dialogPositionStyle(),
currentPlacement: undefined,
value: {},
maxHeight: '',
maxWidth: '',
});
const scrollableContainer = getScrollableContainer(this.scrollableContainer()?.nativeElement);
scrollableContainer.removeEventListener('scroll', this.positionDialog);
}
};
scheduleDialogClose = () => {
// check again to make sure we close dialog just if external
// state specify now that the dialog should be closed
if (!this.open()) {
this.dialogAnimationState.set('hide');
this.closeDialogTimeoutID.set(setTimeout(() => {
this.closeDialog();
}, this.leaveDelay()));
}
};
cancelDialogOpen = () => {
clearTimeout(this.openDialogTimeoutID());
this.openDialogTimeoutID.set(undefined);
this.dialogAnimationState.set('hide');
};
cancelDialogClose = () => {
clearTimeout(this.closeDialogTimeoutID());
this.closeDialogTimeoutID.set(undefined);
this.dialogAnimationState.set('show');
};
handleClickOutsideClose = async (event) => {
if (this.dialogWithBridgeRef()?.nativeElement !== event.target &&
!this.dialogWithBridgeRef()?.nativeElement.contains(event.target) &&
this.relativeElementRef()?.nativeElement !== event.target &&
!this.relativeElementRef()?.nativeElement.contains(event.target)) {
this.onClose$?.emit();
}
};
handleOpenAction = () => {
this.onOpen$?.emit();
};
async ngOnChanges(changes) {
if ('open' in changes) {
await this.toggleVisibility();
}
}
toggleVisibility = async () => {
const shouldDialogOpen = this.open();
if (shouldDialogOpen) {
this.cancelDialogClose();
await this.scheduleDialogOpen();
}
else {
this.cancelDialogOpen();
if (this.dialogIsOpenLocalState()) {
this.scheduleDialogClose();
}
}
};
handleRelativeElementMouseOrFocusLeave = (event) => {
if (this.dialogWithBridgeRef()?.nativeElement !== event.relatedTarget &&
(!(event.relatedTarget instanceof Node) ||
!this.dialogWithBridgeRef()?.nativeElement.contains(event.relatedTarget))) {
this.onClose$.emit();
}
};
handleMouseOrFocusLeaveDialog = async (event) => {
if (this.relativeElementRef()?.nativeElement !== event.relatedTarget &&
(!(event.relatedTarget instanceof Node) ||
!this.relativeElementRef()?.nativeElement.contains(event.relatedTarget))) {
this.onClose$.emit();
}
};
handleMouseEnter = async () => {
this.onOpen$?.emit(); // emit open to parent to make sure it will have open state also
await this.cancelDialogClose();
};
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: NgxTooltipComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: NgxTooltipComponent, isStandalone: true, selector: "ngx-tooltip", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, preferredPlacement: { classPropertyName: "preferredPlacement", publicName: "preferredPlacement", isSignal: true, isRequired: false, transformFunction: null }, orderOfPlacementsToBeTried: { classPropertyName: "orderOfPlacementsToBeTried", publicName: "orderOfPlacementsToBeTried", isSignal: true, isRequired: false, transformFunction: null }, enterDelay: { classPropertyName: "enterDelay", publicName: "enterDelay", isSignal: true, isRequired: false, transformFunction: null }, leaveDelay: { classPropertyName: "leaveDelay", publicName: "leaveDelay", isSignal: true, isRequired: false, transformFunction: null }, triggerActions: { classPropertyName: "triggerActions", publicName: "triggerActions", isSignal: true, isRequired: false, transformFunction: null }, arrow: { classPropertyName: "arrow", publicName: "arrow", isSignal: true, isRequired: false, transformFunction: null }, dialogOffset: { classPropertyName: "dialogOffset", publicName: "dialogOffset", isSignal: true, isRequired: false, transformFunction: null }, scrollableContainer: { classPropertyName: "scrollableContainer", publicName: "scrollableContainer", isSignal: true, isRequired: false, transformFunction: null }, tooltipRootClass: { classPropertyName: "tooltipRootClass", publicName: "tooltipRootClass", isSignal: true, isRequired: false, transformFunction: null }, tooltipClass: { classPropertyName: "tooltipClass", publicName: "tooltipClass", isSignal: true, isRequired: false, transformFunction: null }, placementStrategy: { classPropertyName: "placementStrategy", publicName: "placementStrategy", isSignal: true, isRequired: false, transformFunction: null }, dialogMinMaxSizes: { classPropertyName: "dialogMinMaxSizes", publicName: "dialogMinMaxSizes", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onOpen$: "onOpen$", onClose$: "onClose$" }, viewQueries: [{ propertyName: "relativeElementRef", first: true, predicate: ["relativeElementRef"], descendants: true, isSignal: true }, { propertyName: "dialogWithBridgeRef", first: true, predicate: ["dialogWithBridgeRef"], descendants: true, isSignal: true }, { propertyName: "dialogRef", first: true, predicate: ["dialogRef"], descendants: true, isSignal: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"ngxTooltip-root-container\">\n <div\n #relativeElementRef\n class=\"ngxTooltip-relative-element\"\n [attr.popovertarget]=\"tooltipId\"\n (mouseenter)=\"\n triggerActions().includes('hover') ? handleOpenAction() : undefined\n \"\n (mouseleave)=\"\n triggerActions().includes('hover')\n ? handleRelativeElementMouseOrFocusLeave($event)\n : undefined\n \"\n (focusin)=\"\n triggerActions().includes('focus') ? handleOpenAction() : undefined\n \"\n (focusout)=\"\n triggerActions().includes('focus')\n ? handleRelativeElementMouseOrFocusLeave($event)\n : undefined\n \"\n (click)=\"\n triggerActions().includes('click') ? handleOpenAction() : undefined\n \"\n >\n <ng-content select=\".relative-element\"></ng-content>\n </div>\n\n <div\n class=\"ngxTooltip-dialog-with-bridge {{ tooltipRootClass() }}\"\n [ngClass]=\"[\n dialogPositionStyle().currentPlacement\n ? 'ngxTooltip-placement-' + dialogPositionStyle().currentPlacement\n : '',\n dialogAnimationState() === 'initial' ? 'ngxTooltip-initial' : '',\n dialogAnimationState() === 'show' ? 'ngxTooltip-show' : '',\n dialogAnimationState() === 'hide' ? 'ngxTooltip-hide' : ''\n ]\"\n #dialogWithBridgeRef\n popover=\"manual\"\n [attr.id]=\"tooltipId\"\n role=\"tooltip\"\n [attr.data-dialog-placement]=\"preferredPlacement()\"\n [ngStyle]=\"{\n 'visibility': dialogPositionStyle().value['visibility'] ?? '',\n 'bottom': dialogPositionStyle().value['bottom'] ?? '',\n 'top': dialogPositionStyle().value['top'] ?? '',\n 'left': dialogPositionStyle().value['left'] ?? '',\n 'right': dialogPositionStyle().value['right'] ?? '',\n '--scrollbar-height': dialogPositionStyle().value['--scrollbar-height'] ?? '',\n '--relative-x': dialogPositionStyle().value['--relative-x'] ?? '',\n '--relative-y': dialogPositionStyle().value['--relative-y'] ?? '',\n '--relative-width': dialogPositionStyle().value['--relative-width'] ?? '',\n '--relative-height': dialogPositionStyle().value['--relative-height'] ?? '',\n '--dialog-x': dialogPositionStyle().value['--dialog-x'] ?? '',\n '--dialog-y': dialogPositionStyle().value['--dialog-y'] ?? '',\n '--dialog-width': dialogPositionStyle().value['--dialog-width'] ?? '',\n '--dialog-height': dialogPositionStyle().value['--dialog-height'] ?? '',\n 'max-width': dialogPositionStyle().maxWidth,\n '--dialog-offset.px': (computedDialogOffset()),\n '--close-timeout.ms': leaveDelay(),\n '--arrow-size.px': arrow() ? arrowSize() : 0,\n }\"\n (mouseenter)=\"\n triggerActions().includes('hover') ? handleMouseEnter() : undefined\n \"\n (mouseleave)=\"\n triggerActions().includes('hover')\n ? handleMouseOrFocusLeaveDialog($event)\n : undefined\n \"\n >\n <div\n class=\"ngxTooltip-inner-dialog-with-bridge\"\n #dialogRef\n (focusout)=\"\n triggerActions().includes('focus')\n ? handleMouseOrFocusLeaveDialog($event)\n : undefined\n \"\n >\n <div class=\"ngxTooltip-animated-inner-dialog-with-bridge\">\n <div\n class=\"ngxTooltip-tooltip tooltipClass\"\n [ngStyle]=\"{\n 'max-height':\n dialogPositionStyle().maxHeight !== ''\n ? dialogPositionStyle().maxHeight\n : dialogMinMaxSizes()?.dialogMaxHeight !== undefined\n ? dialogMinMaxSizes()?.dialogMaxHeight + 'px'\n : ''\n }\"\n >\n <ng-content select=\".message\"></ng-content>\n </div>\n\n @if (arrow()) {\n <ngx-tooltip-arrow [arrowSize]=\"arrowSize()\" />\n }\n </div>\n </div>\n </div>\n</div>\n", styles: [":host{width:auto;display:block}.ngxTooltip-root-container{display:flex;width:100%}.ngxTooltip-dialog-with-bridge,.ngxTooltip-inner-dialog-with-bridge{border:unset;padding:unset;background:transparent;overflow:hidden}.ngxTooltip-show .ngxTooltip-animated-inner-dialog-with-bridge{animation:ngxTooltip-show .15s cubic-bezier(0,0,.2,1) forwards}.ngxTooltip-hide .ngxTooltip-animated-inner-dialog-with-bridge{animation:ngxTooltip-hide .15s cubic-bezier(0,0,.2,1) forwards;animation-delay:calc(var(--close-timeout) - .15s)}.ngxTooltip-tooltip{display:flex;width:fit-content;max-width:600px;max-height:calc(100vh - 1px - var(--scrollbar-height));height:100%;padding:4px 8px;border:unset;border-radius:4px;margin:0;overflow-wrap:break-word;font-size:12px;color:#fff;background-color:#616161eb;transform-origin:center top}.ngxTooltip-tooltip:first-child{overflow:auto}.ngxTooltip-placement-top-start .ngxTooltip-animated-inner-dialog-with-bridge,.ngxTooltip-placement-top .ngxTooltip-animated-inner-dialog-with-bridge,.ngxTooltip-placement-top-end .ngxTooltip-animated-inner-dialog-with-bridge{padding-bottom:calc(var(--dialog-offset) + var(--arrow-size))}.ngxTooltip-placement-bottom-start .ngxTooltip-animated-inner-dialog-with-bridge,.ngxTooltip-placement-bottom .ngxTooltip-animated-inner-dialog-with-bridge,.ngxTooltip-placement-bottom-end .ngxTooltip-animated-inner-dialog-with-bridge{padding-top:calc(var(--dialog-offset) + var(--arrow-size))}.ngxTooltip-placement-left-start .ngxTooltip-animated-inner-dialog-with-bridge,.ngxTooltip-placement-left .ngxTooltip-animated-inner-dialog-with-bridge,.ngxTooltip-placement-left-end .ngxTooltip-animated-inner-dialog-with-bridge{padding-right:calc(var(--dialog-offset) + var(--arrow-size))}.ngxTooltip-placement-right-start .ngxTooltip-animated-inner-dialog-with-bridge,.ngxTooltip-placement-right .ngxTooltip-animated-inner-dialog-with-bridge,.ngxTooltip-placement-right-end .ngxTooltip-animated-inner-dialog-with-bridge{padding-left:calc(var(--dialog-offset) + var(--arrow-size))}.ngxTooltip-relative-element{display:inline-block;width:100%;height:100%}@keyframes ngxTooltip-show{0%{opacity:0;transform:scale(.8)}to{opacity:1;transform:scale(1)}}@keyframes ngxTooltip-hide{0%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.8)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: NgxTooltipArrowComponent, selector: "ngx-tooltip-arrow", inputs: ["arrowSize"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: NgxTooltipComponent, decorators: [{
type: Component,
args: [{ selector: 'ngx-tooltip', imports: [CommonModule, NgxTooltipArrowComponent], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<div class=\"ngxTooltip-root-container\">\n <div\n #relativeElementRef\n class=\"ngxTooltip-relative-element\"\n [attr.popovertarget]=\"tooltipId\"\n (mouseenter)=\"\n triggerActions().includes('hover') ? handleOpenAction() : undefined\n \"\n (mouseleave)=\"\n triggerActions().includes('hover')\n ? handleRelativeElementMouseOrFocusLeave($event)\n : undefined\n \"\n (focusin)=\"\n triggerActions().includes('focus') ? handleOpenAction() : undefined\n \"\n (focusout)=\"\n triggerActions().includes('focus')\n ? handleRelativeElementMouseOrFocusLeave($event)\n : undefined\n \"\n (click)=\"\n triggerActions().includes('click') ? handleOpenAction() : undefined\n \"\n >\n <ng-content select=\".relative-element\"></ng-content>\n </div>\n\n <div\n class=\"ngxTooltip-dialog-with-bridge {{ tooltipRootClass() }}\"\n [ngClass]=\"[\n dialogPositionStyle().currentPlacement\n ? 'ngxTooltip-placement-' + dialogPositionStyle().currentPlacement\n : '',\n dialogAnimationState() === 'initial' ? 'ngxTooltip-initial' : '',\n dialogAnimationState() === 'show' ? 'ngxTooltip-show' : '',\n dialogAnimationState() === 'hide' ? 'ngxTooltip-hide' : ''\n ]\"\n #dialogWithBridgeRef\n popover=\"manual\"\n [attr.id]=\"tooltipId\"\n role=\"tooltip\"\n [attr.data-dialog-placement]=\"preferredPlacement()\"\n [ngStyle]=\"{\n 'visibility': dialogPositionStyle().value['visibility'] ?? '',\n 'bottom': dialogPositionStyle().value['bottom'] ?? '',\n 'top': dialogPositionStyle().value['top'] ?? '',\n 'left': dialogPositionStyle().value['left'] ?? '',\n 'right': dialogPositionStyle().value['right'] ?? '',\n '--scrollbar-height': dialogPositionStyle().value['--scrollbar-height'] ?? '',\n '--relative-x': dialogPositionStyle().value['--relative-x'] ?? '',\n '--relative-y': dialogPositionStyle().value['--relative-y'] ?? '',\n '--relative-width': dialogPositionStyle().value['--relative-width'] ?? '',\n '--relative-height': dialogPositionStyle().value['--relative-height'] ?? '',\n '--dialog-x': dialogPositionStyle().value['--dialog-x'] ?? '',\n '--dialog-y': dialogPositionStyle().value['--dialog-y'] ?? '',\n '--dialog-width': dialogPositionStyle().value['--dialog-width'] ?? '',\n '--dialog-height': dialogPositionStyle().value['--dialog-height'] ?? '',\n 'max-width': dialogPositionStyle().maxWidth,\n '--dialog-offset.px': (computedDialogOffset()),\n '--close-timeout.ms': leaveDelay(),\n '--arrow-size.px': arrow() ? arrowSize() : 0,\n }\"\n (mouseenter)=\"\n triggerActions().includes('hover') ? handleMouseEnter() : undefined\n \"\n (mouseleave)=\"\n triggerActions().includes('hover')\n ? handleMouseOrFocusLeaveDialog($event)\n : undefined\n \"\n >\n <div\n class=\"ngxTooltip-inner-dialog-with-bridge\"\n #dialogRef\n (focusout)=\"\n triggerActions().includes('focus')\n ? handleMouseOrFocusLeaveDialog($event)\n : undefined\n \"\n >\n <div class=\"ngxTooltip-animated-inner-dialog-with-bridge\">\n <div\n class=\"ngxTooltip-tooltip tooltipClass\"\n [ngStyle]=\"{\n 'max-height':\n dialogPositionStyle().maxHeight !== ''\n ? dialogPositionStyle().maxHeight\n : dialogMinMaxSizes()?.dialogMaxHeight !== undefined\n ? dialogMinMaxSizes()?.dialogMaxHeight + 'px'\n : ''\n }\"\n >\n <ng-content select=\".message\"></ng-content>\n </div>\n\n @if (arrow()) {\n <ngx-tooltip-arrow [arrowSize]=\"arrowSize()\" />\n }\n </div>\n </div>\n </div>\n</div>\n", styles: [":host{width:auto;display:block}.ngxTooltip-root-container{display:flex;width:100%}.ngxTooltip-dialog-with-bridge,.ngxTooltip-inner-dialog-with-bridge{border:unset;padding:unset;background:transparent;overflow:hidden}.ngxTooltip-show .ngxTooltip-animated-inner-dialog-with-bridge{animation:ngxTooltip-show .15s cubic-bezier(0,0,.2,1) forwards}.ngxTooltip-hide .ngxTooltip-animated-inner-dialog-with-bridge{animation:ngxTooltip-hide .15s cubic-bezier(0,0,.2,1) forwards;animation-delay:calc(var(--close-timeout) - .15s)}.ngxTooltip-tooltip{display:flex;width:fit-content;max-width:600px;max-height:calc(100vh - 1px - var(--scrollbar-height));height:100%;padding:4px 8px;border:unset;border-radius:4px;margin:0;overflow-wrap:break-word;font-size:12px;color:#fff;background-color:#616161eb;transform-origin:center top}.ngxTooltip-tooltip:first-child{overflow:auto}.ngxTooltip-placement-top-start .ngxTooltip-animated-inner-dialog-with-bridge,.ngxTooltip-placement-top .ngxTooltip-animated-inner-dialog-with-bridge,.ngxTooltip-placement-top-end .ngxTooltip-animated-inner-dialog-with-bridge{padding-bottom:calc(var(--dialog-offset) + var(--arrow-size))}.ngxTooltip-placement-bottom-start .ngxTooltip-animated-inner-dialog-with-bridge,.ngxTooltip-placement-bottom .ngxTooltip-animated-inner-dialog-with-bridge,.ngxTooltip-placement-bottom-end .ngxTooltip-animated-inner-dialog-with-bridge{padding-top:calc(var(--dialog-offset) + var(--arrow-size))}.ngxTooltip-placement-left-start .ngxTooltip-animated-inner-dialog-with-bridge,.ngxTooltip-placement-left .ngxTooltip-animated-inner-dialog-with-bridge,.ngxTooltip-placement-left-end .ngxTooltip-animated-inner-dialog-with-bridge{padding-right:calc(var(--dialog-offset) + var(--arrow-size))}.ngxTooltip-placement-right-start .ngxTooltip-animated-inner-dialog-with-bridge,.ngxTooltip-placement-right .ngxTooltip-animated-inner-dialog-with-bridge,.ngxTooltip-placement-right-end .ngxTooltip-animated-inner-dialog-with-bridge{padding-left:calc(var(--dialog-offset) + var(--arrow-size))}.ngxTooltip-relative-element{display:inline-block;width:100%;height:100%}@keyframes ngxTooltip-show{0%{opacity:0;transform:scale(.8)}to{opacity:1;transform:scale(1)}}@keyframes ngxTooltip-hide{0%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.8)}}\n"] }]
}], ctorParameters: () => [] });
/*
* Public API Surface of ngx-tooltip
*/
/**
* Generated bundle index. Do not edit.
*/
export { NgxTooltipComponent, defaultOrderOfPlacementsToBeTried };
//# sourceMappingURL=controllable-ui-ngx-tooltip.mjs.map