UNPKG

@striven-erp/striven-tooltip

Version:
239 lines (203 loc) 7.13 kB
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; } } }