@shopware-ag/meteor-component-library
Version:
The meteor component library is a Vue component library developed by Shopware. It is based on the [Meteor Design System](https://shopware.design/).
371 lines (370 loc) • 12.7 kB
JavaScript
"use strict";
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
const id = require("./id-8e80f112.js");
const availableTooltipPlacements = ["top", "right", "bottom", "left"];
const tooltipRegistry = /* @__PURE__ */ new Map();
class Tooltip {
constructor({
id: id$1 = id.createId(),
placement = "top",
message,
width = 200,
element,
showDelay = 100,
hideDelay = showDelay,
disabled = false,
appearance = "dark",
showOnDisabledElements = false,
zIndex = null
}) {
__publicField(this, "_id");
__publicField(this, "_placement");
__publicField(this, "_message");
__publicField(this, "_width");
__publicField(this, "_parentDOMElement");
__publicField(this, "_showDelay");
__publicField(this, "_hideDelay");
__publicField(this, "_disabled");
__publicField(this, "_appearance");
__publicField(this, "_showOnDisabledElements");
__publicField(this, "_zIndex");
__publicField(this, "_isShown");
__publicField(this, "_state");
__publicField(this, "_DOMElement");
__publicField(this, "_parentDOMElementWrapper");
__publicField(this, "_actualTooltipPlacement");
__publicField(this, "_timeout");
this._id = id$1;
this._placement = Tooltip.validatePlacement(placement);
this._message = Tooltip.validateMessage(message);
this._width = Tooltip.validateWidth(width);
this._parentDOMElement = element;
this._showDelay = showDelay ?? 100;
this._hideDelay = hideDelay ?? 100;
this._disabled = disabled;
this._appearance = appearance;
this._showOnDisabledElements = showOnDisabledElements;
this._zIndex = zIndex;
this._isShown = false;
this._state = false;
this._DOMElement = null;
this._parentDOMElementWrapper = null;
this._actualTooltipPlacement = null;
}
get id() {
return this._id;
}
/**
* Initializes the tooltip.
* Needs to be called after the parent DOM Element is inserted to the DOM.
*/
init() {
this._DOMElement = this.createDOMElement();
if (this._showOnDisabledElements) {
this._parentDOMElementWrapper = this.createParentDOMElementWrapper();
}
this.registerEvents();
}
/**
* Updates the styles and/or text of the tooltip
*/
update({
message,
placement,
width,
showDelay,
hideDelay,
disabled,
appearance,
showOnDisabledElements,
zIndex
}) {
if (message && this._message !== message) {
this._message = Tooltip.validateMessage(message);
if (this._DOMElement) {
this._DOMElement.innerHTML = this._message;
}
this.registerEvents();
}
if (width && this._width !== width) {
this._width = Tooltip.validateWidth(width);
this._DOMElement.style.width = `${this._width}px`;
}
if (placement && this._placement !== placement) {
this._placement = Tooltip.validatePlacement(placement);
this._placeTooltip();
}
if (showDelay && this._showDelay !== showDelay) {
this._showDelay = showDelay;
}
if (hideDelay && this._hideDelay !== hideDelay) {
this._hideDelay = hideDelay;
}
if (disabled !== void 0 && this._disabled !== disabled) {
this._disabled = disabled;
}
if (appearance && this._appearance !== appearance) {
this._DOMElement.classList.remove(`mt-tooltip--${this._appearance}`);
this._appearance = appearance;
this._DOMElement.classList.add(`mt-tooltip--${this._appearance}`);
}
if (showOnDisabledElements !== void 0 && this._showOnDisabledElements !== showOnDisabledElements) {
this._showOnDisabledElements = showOnDisabledElements;
}
if (zIndex !== this._zIndex && zIndex !== void 0) {
this._zIndex = zIndex;
}
}
/**
* Creates a wrapper around the original DOMElement.
* This is needed because a disabled input field does not fire any mouse events and prevents the tooltip
* therefore from working.
* @returns {HTMLElement}
*/
createParentDOMElementWrapper() {
const element = document.createElement("div");
element.classList.add("mt-tooltip--wrapper");
this._parentDOMElement.parentNode.insertBefore(element, this._parentDOMElement);
element.appendChild(this._parentDOMElement);
return element;
}
createDOMElement() {
const element = document.createElement("div");
element.innerHTML = this._message;
element.style.width = `${this._width}px`;
element.setAttribute("aria-hidden", "false");
element.setAttribute("aria-role", "tooltip");
element.setAttribute("aria-label", "currently-opened-tooltip");
element.classList.add("mt-tooltip");
element.classList.add(`mt-tooltip--${this._appearance}`);
if (this._zIndex !== null) {
element.style.zIndex = this._zIndex.toFixed(0);
}
return element;
}
registerEvents() {
if (this._parentDOMElementWrapper) {
this._parentDOMElementWrapper.addEventListener("mouseenter", this.onMouseToggle.bind(this));
this._parentDOMElementWrapper.addEventListener("mouseleave", this.onMouseToggle.bind(this));
} else {
this._parentDOMElement.addEventListener("mouseenter", this.onMouseToggle.bind(this));
this._parentDOMElement.addEventListener("mouseleave", this.onMouseToggle.bind(this));
}
this._DOMElement.addEventListener("mouseenter", this.onMouseToggle.bind(this));
this._DOMElement.addEventListener("mouseleave", this.onMouseToggle.bind(this));
}
/**
* Sets the state and triggers the toggle.
*/
onMouseToggle(event) {
this._state = event.type === "mouseenter";
if (this._timeout) {
clearTimeout(this._timeout);
}
this._timeout = setTimeout(
this._toggle.bind(this),
this._state ? this._showDelay : this._hideDelay
);
}
_toggle() {
if (this._state && !this._isShown && this._doesParentExist()) {
this.showTooltip();
return;
}
if (!this._state && this._isShown) {
this.hideTooltip();
}
}
/**
* Gets the parent element by tag name and tooltip id and returns true or false whether the element exists.
* @returns {boolean}
* @private
*/
_doesParentExist() {
const tooltipIdOfParentElement = this._parentDOMElement.getAttribute("tooltip-id") ?? "";
const htmlTagOfParentElement = this._parentDOMElement.tagName.toLowerCase();
return !!document.querySelector(
`${htmlTagOfParentElement}[tooltip-id="${tooltipIdOfParentElement}"]`
);
}
/**
* Appends the tooltip to the DOM and sets a suitable position
*/
showTooltip() {
if (this._disabled) {
return;
}
document.body.appendChild(this._DOMElement);
this._placeTooltip();
this._isShown = true;
}
/**
* Removes the tooltip from the DOM
*/
hideTooltip() {
if (this._disabled) {
return;
}
this._DOMElement.remove();
this._isShown = false;
}
_placeTooltip() {
let possiblePlacements = availableTooltipPlacements;
let placement = this._placement;
possiblePlacements = possiblePlacements.filter((pos) => pos !== placement);
this._DOMElement.classList.remove(`mt-tooltip--${this._actualTooltipPlacement}`);
this._setDOMElementPosition(this._calculateTooltipPosition(placement ?? "top"));
this._actualTooltipPlacement = placement ?? null;
while (!this._isElementInViewport(this._DOMElement)) {
if (possiblePlacements.length < 1) {
this._actualTooltipPlacement = this._placement ?? null;
this._setDOMElementPosition(this._calculateTooltipPosition(this._placement ?? "top"));
break;
}
placement = possiblePlacements.shift();
this._setDOMElementPosition(this._calculateTooltipPosition(placement ?? "top"));
this._actualTooltipPlacement = placement ?? null;
}
this._DOMElement.classList.add(`mt-tooltip--${this._actualTooltipPlacement ?? ""}`);
}
_setDOMElementPosition({ top, left }) {
this._DOMElement.style.position = "fixed";
this._DOMElement.style.top = top;
this._DOMElement.style.left = left;
}
_calculateTooltipPosition(placement) {
const boundingBox = this._parentDOMElement.getBoundingClientRect();
const secureOffset = 10;
let top;
let left;
switch (placement) {
case "bottom":
top = `${boundingBox.top + boundingBox.height + secureOffset}px`;
left = `${boundingBox.left + boundingBox.width / 2 - this._DOMElement.offsetWidth / 2}px`;
break;
case "left":
top = `${boundingBox.top + boundingBox.height / 2 - this._DOMElement.offsetHeight / 2}px`;
left = `${boundingBox.left - secureOffset - this._DOMElement.offsetWidth}px`;
break;
case "right":
top = `${boundingBox.top + boundingBox.height / 2 - this._DOMElement.offsetHeight / 2}px`;
left = `${boundingBox.right + secureOffset}px`;
break;
case "top":
default:
top = `${boundingBox.top - this._DOMElement.offsetHeight - secureOffset}px`;
left = `${boundingBox.left + boundingBox.width / 2 - this._DOMElement.offsetWidth / 2}px`;
}
return { top, left };
}
_isElementInViewport(element) {
const boundingClientRect = element.getBoundingClientRect();
const windowHeight = window.innerHeight || document.documentElement.clientHeight;
const windowWidth = window.innerWidth || document.documentElement.clientWidth;
const visibleBorders = {
top: boundingClientRect.top > 0,
right: boundingClientRect.right < windowWidth,
bottom: boundingClientRect.bottom < windowHeight,
left: boundingClientRect.left > 0
};
return visibleBorders.top && visibleBorders.right && visibleBorders.bottom && visibleBorders.left;
}
static validatePlacement(placement) {
if (!availableTooltipPlacements.includes(placement)) {
console.warn(
"Tooltip Directive",
`The modifier has to be one of these "${availableTooltipPlacements.join(",")}"`
);
return "top";
}
return placement;
}
static validateMessage(message) {
if (typeof message !== "string") {
console.warn("Tooltip Directive", "The tooltip needs a message with type string");
}
return message ?? "";
}
static validateWidth(width) {
if (width === "auto") {
return width;
}
if (typeof width !== "number" || width < 1) {
console.warn("Tooltip Directive", "The tooltip width has to be a number greater 0");
return 200;
}
return width;
}
static validateDelay(delay) {
if (typeof delay !== "number" || delay < 1) {
console.warn("Tooltip Directive", "The tooltip delay has to be a number greater 0");
return 100;
}
return delay;
}
}
function createOrUpdateTooltip(el, {
value,
modifiers
}) {
let message = typeof value === "string" ? value : value.message;
message = message ? message.trim() : "";
const placement = value.position || Object.keys(modifiers)[0];
const showDelay = value.showDelay;
const hideDelay = value.hideDelay;
const disabled = value.disabled;
const appearance = value.appearance;
const width = value.width;
const showOnDisabledElements = value.showOnDisabledElements;
const zIndex = value.zIndex;
const configuration = {
element: el,
message,
placement,
width,
showDelay,
hideDelay,
disabled,
appearance,
showOnDisabledElements,
zIndex
};
if (el.hasAttribute("tooltip-id")) {
const tooltip2 = tooltipRegistry.get(el.getAttribute("tooltip-id"));
tooltip2.update(configuration);
return;
}
const tooltip = new Tooltip(configuration);
tooltipRegistry.set(tooltip.id ?? "", tooltip);
el.setAttribute("tooltip-id", tooltip.id);
}
const vTooltip = {
beforeMount: (el, binding) => {
createOrUpdateTooltip(el, binding);
},
unmounted: (el) => {
if (el.hasAttribute("tooltip-id")) {
const tooltip = tooltipRegistry.get(el.getAttribute("tooltip-id"));
tooltip.hideTooltip();
}
},
updated: (el, binding) => {
createOrUpdateTooltip(el, binding);
},
/**
* Initialize the tooltip once it has been inserted to the DOM.
*/
mounted: (el) => {
if (el.hasAttribute("tooltip-id")) {
const tooltip = tooltipRegistry.get(el.getAttribute("tooltip-id"));
tooltip.init();
}
}
};
exports.vTooltip = vTooltip;
//# sourceMappingURL=tooltip.directive-7b51326d.js.map