@striven-erp/striven-tooltip
Version:
Striven Tooltips UI
239 lines (203 loc) • 7.13 kB
JavaScript
import './striventooltip.css';
const INFOICON = {
viewBox: '0 0 1792 1792',
d:
'M1152 1376v-160q0-14-9-23t-23-9h-96v-512q0-14-9-23t-23-9h-320q-14 0-23 9t-9 23v160q0 14 9 23t23 9h96v320h-96q-14 0-23 9t-9 23v160q0 14 9 23t23 9h448q14 0 23-9t9-23zm-128-896v-160q0-14-9-23t-23-9h-192q-14 0-23 9t-9 23v160q0 14 9 23t23 9h192q14 0 23-9t9-23zm640 416q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z',
};
export default class StrivenTooltip {
/**
* Initialize StrivenTooltip instance
* @param {String} tip Tooltip message to display
* @param {HTMLElement} el Element to bind to the tooltip instance to
* @param {Object} config Configuration options object
*/
constructor(tip, el, config = {}) {
// this['_version'] = __VERSION__;
const defaultOptions = {
direction: 'South',
};
this.el = el;
this.tip = tip;
this.config = {...defaultOptions, ...config};
this.config.icon && this.initIcon();
this.initTooltip();
const StrivenTooltip = this;
this.el.StrivenTooltip = () => StrivenTooltip;
}
/**
* Initialize the tooltip
*/
initTooltip() {
// Construct Tooltip
const tooltip = document.createElement('div');
tooltip.classList.add('s-tooltip');
// Construct tooltip stem
const stem = document.createElement('div');
stem.classList.add('s-tooltip-stem');
this.stem = stem;
// Construct element to hold tooltip text content
const content = document.createElement('p');
content.classList.add('s-tooltip-content');
content.textContent = this.tip;
this.content = content;
// Create tooltip element
tooltip.append(stem);
tooltip.append(content);
this.tooltip = tooltip;
// Set parent element position
this.el.style.position = 'relative';
// Bind mouse enter even
this.el.onmouseenter = () => {
this.el.append(tooltip);
setTimeout(() => {
if (this.inView(this.tooltip)) {
this.setDirection(this.config.direction);
} else {
this.correctPosition();
}
this.showTooltip();
this.config.onShow && this.config.onShow();
}, 0);
};
// Bind mouse leave
this.el.onmouseleave = () => {
this.hideTooltip();
this.tooltip.setAttribute('style', null);
this.stem.setAttribute('style', null);
tooltip.remove();
setTimeout(() => this.config.onHide && this.config.onHide(), 0);
};
// Call the onInit handler
this.config.onInit && this.config.onInit(this.el);
}
/**
* Initialize an icon for the tooltip to show on hover
*/
initIcon() {
const tipIcon = this.constructSVG(INFOICON);
tipIcon.setAttribute('style', 'cursor: pointer');
tipIcon.onmouseenter = () =>
tipIcon.getElementsByTagName('path')[0].setAttribute('fill', '#2a6496');
tipIcon.onmouseleave = () =>
tipIcon.getElementsByTagName('path')[0].setAttribute('fill', '#428bca');
this.el.style.height = '16px';
this.el.style.width = '16px';
this.el.append(tipIcon);
}
/**
* Construct SVG Icon
* @param {Object} svgData viewBox and d data for the SVG Icon
* @return {HTMLElement} Returns the SVG Icon
*/
constructSVG(svgData) {
const {viewBox, d} = svgData;
const fillColor = '#428bca';
const xmlns = 'http://www.w3.org/2000/svg';
const height = '16';
const width = '16';
const icon = document.createElement('span');
const svg = `<svg width="${width}" height="${height}" viewBox="${viewBox}" xmlns="${xmlns}">`;
const path = `<path fill="${fillColor}" d="${d}"/>`;
icon.innerHTML = `${svg}${path}</svg>`;
return icon;
}
/**
* Check an element is the viewport
* @param {HTMLElement} el The element to check
* @return {Boolean} true if the element is in the viewport
*/
inView(el) {
const {left, right} = el.getBoundingClientRect();
if (left - el.offsetWidth / 2 < 0 || right > window.innerWidth) {
return false;
} else {
return true;
}
}
/**
* Sets the content of the tooltip
* @param {String} New tooltip content
*/
setTip(tip) {
this.tip = tip;
this.content.textContent = tip;
}
/**
* Show the tooltip
*/
showTooltip() {
this.tooltip.classList.add('s-tooltip-show');
}
/**
* Hide the tooltip
*/
hideTooltip() {
this.tooltip.classList.remove('s-tooltip-show');
}
/**
* Correct the tooltips direction to be in the viewport
*/
correctPosition() {
const {left, right} = this.tooltip.getBoundingClientRect();
if (left - this.tooltip.offsetWidth / 2 < 0) {
this.setDirection('East');
return true;
}
if (right > window.innerWidth) {
this.setDirection('West');
return true;
}
}
/**
* Sets the direction of the tooltip
* @param {String} Direction to set the tooltip
*/
setDirection(dir) {
switch (dir) {
case 'East':
this.tooltip.style.left = `${this.el.offsetWidth + 5}px`;
this.tooltip.style.top = '50%';
this.tooltip.style.marginTop = `-${this.tooltip.offsetHeight / 2}px`;
this.stem.style.left = '-5px';
this.stem.style.transform = `translateY(${this.tooltip.offsetHeight /
2 -
15}px) rotate(45deg)`;
this.stem.style.borderBottom = '1px solid #acacac';
this.stem.style.borderLeft = '1px solid #acacac';
break;
case 'West':
this.tooltip.style.right = `${this.el.offsetWidth + 5}px`;
this.tooltip.style.top = '50%';
this.tooltip.style.marginTop = `-${this.tooltip.offsetHeight / 2}px`;
this.stem.style.right = '-5px';
this.stem.style.transform = `translateY(${this.tooltip.offsetHeight /
2 -
15}px) rotate(45deg)`;
this.stem.style.borderTop = '1px solid #acacac';
this.stem.style.borderRight = '1px solid #acacac';
break;
case 'North':
this.tooltip.style.right = '50%';
this.tooltip.style.marginRight = `-${this.tooltip.offsetWidth / 2}px`;
this.tooltip.style.top = `-${this.tooltip.offsetHeight}px`;
this.stem.style.transform = `translateY(${this.tooltip.offsetHeight - this.stem.offsetHeight - 5}px) rotate(45deg)`;
this.stem.style.right = '50%';
this.stem.style.marginRight = '-5px';
this.stem.style.borderBottom = '1px solid #acacac';
this.stem.style.borderRight = '1px solid #acacac';
break;
case 'South':
default:
this.tooltip.style.top = `${this.el.offsetHeight + 5}px`;
this.tooltip.style.left = '50%';
this.tooltip.style.bottom = '0px';
this.tooltip.style.marginLeft = `-${this.tooltip.offsetWidth / 2}px`;
this.stem.style.transform = `translateY(-145%) rotate(45deg)`;
this.stem.style.left = '50%';
this.stem.style.marginLeft = '-5px';
this.stem.style.borderTop = '1px solid #acacac';
this.stem.style.borderLeft = '1px solid #acacac';
break;
}
}
}