304 lines
8.0 KiB
JavaScript
304 lines
8.0 KiB
JavaScript
(function($, anim) {
|
|
'use strict';
|
|
|
|
let _defaults = {
|
|
exitDelay: 200,
|
|
enterDelay: 0,
|
|
html: null,
|
|
margin: 5,
|
|
inDuration: 250,
|
|
outDuration: 200,
|
|
position: 'bottom',
|
|
transitionMovement: 10
|
|
};
|
|
|
|
/**
|
|
* @class
|
|
*
|
|
*/
|
|
class Tooltip extends Component {
|
|
/**
|
|
* Construct Tooltip instance
|
|
* @constructor
|
|
* @param {Element} el
|
|
* @param {Object} options
|
|
*/
|
|
constructor(el, options) {
|
|
super(Tooltip, el, options);
|
|
|
|
this.el.M_Tooltip = this;
|
|
this.options = $.extend({}, Tooltip.defaults, options);
|
|
|
|
this.isOpen = false;
|
|
this.isHovered = false;
|
|
this.isFocused = false;
|
|
this._appendTooltipEl();
|
|
this._setupEventHandlers();
|
|
}
|
|
|
|
static get defaults() {
|
|
return _defaults;
|
|
}
|
|
|
|
static init(els, options) {
|
|
return super.init(this, els, options);
|
|
}
|
|
|
|
/**
|
|
* Get Instance
|
|
*/
|
|
static getInstance(el) {
|
|
let domElem = !!el.jquery ? el[0] : el;
|
|
return domElem.M_Tooltip;
|
|
}
|
|
|
|
/**
|
|
* Teardown component
|
|
*/
|
|
destroy() {
|
|
$(this.tooltipEl).remove();
|
|
this._removeEventHandlers();
|
|
this.el.M_Tooltip = undefined;
|
|
}
|
|
|
|
_appendTooltipEl() {
|
|
let tooltipEl = document.createElement('div');
|
|
tooltipEl.classList.add('material-tooltip');
|
|
this.tooltipEl = tooltipEl;
|
|
|
|
let tooltipContentEl = document.createElement('div');
|
|
tooltipContentEl.classList.add('tooltip-content');
|
|
tooltipContentEl.innerHTML = this.options.html;
|
|
tooltipEl.appendChild(tooltipContentEl);
|
|
document.body.appendChild(tooltipEl);
|
|
}
|
|
|
|
_updateTooltipContent() {
|
|
this.tooltipEl.querySelector('.tooltip-content').innerHTML = this.options.html;
|
|
}
|
|
|
|
_setupEventHandlers() {
|
|
this._handleMouseEnterBound = this._handleMouseEnter.bind(this);
|
|
this._handleMouseLeaveBound = this._handleMouseLeave.bind(this);
|
|
this._handleFocusBound = this._handleFocus.bind(this);
|
|
this._handleBlurBound = this._handleBlur.bind(this);
|
|
this.el.addEventListener('mouseenter', this._handleMouseEnterBound);
|
|
this.el.addEventListener('mouseleave', this._handleMouseLeaveBound);
|
|
this.el.addEventListener('focus', this._handleFocusBound, true);
|
|
this.el.addEventListener('blur', this._handleBlurBound, true);
|
|
}
|
|
|
|
_removeEventHandlers() {
|
|
this.el.removeEventListener('mouseenter', this._handleMouseEnterBound);
|
|
this.el.removeEventListener('mouseleave', this._handleMouseLeaveBound);
|
|
this.el.removeEventListener('focus', this._handleFocusBound, true);
|
|
this.el.removeEventListener('blur', this._handleBlurBound, true);
|
|
}
|
|
|
|
open(isManual) {
|
|
if (this.isOpen) {
|
|
return;
|
|
}
|
|
isManual = isManual === undefined ? true : undefined; // Default value true
|
|
this.isOpen = true;
|
|
// Update tooltip content with HTML attribute options
|
|
this.options = $.extend({}, this.options, this._getAttributeOptions());
|
|
this._updateTooltipContent();
|
|
this._setEnterDelayTimeout(isManual);
|
|
}
|
|
|
|
close() {
|
|
if (!this.isOpen) {
|
|
return;
|
|
}
|
|
|
|
this.isHovered = false;
|
|
this.isFocused = false;
|
|
this.isOpen = false;
|
|
this._setExitDelayTimeout();
|
|
}
|
|
|
|
/**
|
|
* Create timeout which delays when the tooltip closes
|
|
*/
|
|
_setExitDelayTimeout() {
|
|
clearTimeout(this._exitDelayTimeout);
|
|
|
|
this._exitDelayTimeout = setTimeout(() => {
|
|
if (this.isHovered || this.isFocused) {
|
|
return;
|
|
}
|
|
|
|
this._animateOut();
|
|
}, this.options.exitDelay);
|
|
}
|
|
|
|
/**
|
|
* Create timeout which delays when the toast closes
|
|
*/
|
|
_setEnterDelayTimeout(isManual) {
|
|
clearTimeout(this._enterDelayTimeout);
|
|
|
|
this._enterDelayTimeout = setTimeout(() => {
|
|
if (!this.isHovered && !this.isFocused && !isManual) {
|
|
return;
|
|
}
|
|
|
|
this._animateIn();
|
|
}, this.options.enterDelay);
|
|
}
|
|
|
|
_positionTooltip() {
|
|
let origin = this.el,
|
|
tooltip = this.tooltipEl,
|
|
originHeight = origin.offsetHeight,
|
|
originWidth = origin.offsetWidth,
|
|
tooltipHeight = tooltip.offsetHeight,
|
|
tooltipWidth = tooltip.offsetWidth,
|
|
newCoordinates,
|
|
margin = this.options.margin,
|
|
targetTop,
|
|
targetLeft;
|
|
|
|
(this.xMovement = 0), (this.yMovement = 0);
|
|
|
|
targetTop = origin.getBoundingClientRect().top + M.getDocumentScrollTop();
|
|
targetLeft = origin.getBoundingClientRect().left + M.getDocumentScrollLeft();
|
|
|
|
if (this.options.position === 'top') {
|
|
targetTop += -tooltipHeight - margin;
|
|
targetLeft += originWidth / 2 - tooltipWidth / 2;
|
|
this.yMovement = -this.options.transitionMovement;
|
|
} else if (this.options.position === 'right') {
|
|
targetTop += originHeight / 2 - tooltipHeight / 2;
|
|
targetLeft += originWidth + margin;
|
|
this.xMovement = this.options.transitionMovement;
|
|
} else if (this.options.position === 'left') {
|
|
targetTop += originHeight / 2 - tooltipHeight / 2;
|
|
targetLeft += -tooltipWidth - margin;
|
|
this.xMovement = -this.options.transitionMovement;
|
|
} else {
|
|
targetTop += originHeight + margin;
|
|
targetLeft += originWidth / 2 - tooltipWidth / 2;
|
|
this.yMovement = this.options.transitionMovement;
|
|
}
|
|
|
|
newCoordinates = this._repositionWithinScreen(
|
|
targetLeft,
|
|
targetTop,
|
|
tooltipWidth,
|
|
tooltipHeight
|
|
);
|
|
$(tooltip).css({
|
|
top: newCoordinates.y + 'px',
|
|
left: newCoordinates.x + 'px'
|
|
});
|
|
}
|
|
|
|
_repositionWithinScreen(x, y, width, height) {
|
|
let scrollLeft = M.getDocumentScrollLeft();
|
|
let scrollTop = M.getDocumentScrollTop();
|
|
let newX = x - scrollLeft;
|
|
let newY = y - scrollTop;
|
|
|
|
let bounding = {
|
|
left: newX,
|
|
top: newY,
|
|
width: width,
|
|
height: height
|
|
};
|
|
|
|
let offset = this.options.margin + this.options.transitionMovement;
|
|
let edges = M.checkWithinContainer(document.body, bounding, offset);
|
|
|
|
if (edges.left) {
|
|
newX = offset;
|
|
} else if (edges.right) {
|
|
newX -= newX + width - window.innerWidth;
|
|
}
|
|
|
|
if (edges.top) {
|
|
newY = offset;
|
|
} else if (edges.bottom) {
|
|
newY -= newY + height - window.innerHeight;
|
|
}
|
|
|
|
return {
|
|
x: newX + scrollLeft,
|
|
y: newY + scrollTop
|
|
};
|
|
}
|
|
|
|
_animateIn() {
|
|
this._positionTooltip();
|
|
this.tooltipEl.style.visibility = 'visible';
|
|
anim.remove(this.tooltipEl);
|
|
anim({
|
|
targets: this.tooltipEl,
|
|
opacity: 1,
|
|
translateX: this.xMovement,
|
|
translateY: this.yMovement,
|
|
duration: this.options.inDuration,
|
|
easing: 'easeOutCubic'
|
|
});
|
|
}
|
|
|
|
_animateOut() {
|
|
anim.remove(this.tooltipEl);
|
|
anim({
|
|
targets: this.tooltipEl,
|
|
opacity: 0,
|
|
translateX: 0,
|
|
translateY: 0,
|
|
duration: this.options.outDuration,
|
|
easing: 'easeOutCubic'
|
|
});
|
|
}
|
|
|
|
_handleMouseEnter() {
|
|
this.isHovered = true;
|
|
this.isFocused = false; // Allows close of tooltip when opened by focus.
|
|
this.open(false);
|
|
}
|
|
|
|
_handleMouseLeave() {
|
|
this.isHovered = false;
|
|
this.isFocused = false; // Allows close of tooltip when opened by focus.
|
|
this.close();
|
|
}
|
|
|
|
_handleFocus() {
|
|
if (M.tabPressed) {
|
|
this.isFocused = true;
|
|
this.open(false);
|
|
}
|
|
}
|
|
|
|
_handleBlur() {
|
|
this.isFocused = false;
|
|
this.close();
|
|
}
|
|
|
|
_getAttributeOptions() {
|
|
let attributeOptions = {};
|
|
let tooltipTextOption = this.el.getAttribute('data-tooltip');
|
|
let positionOption = this.el.getAttribute('data-position');
|
|
|
|
if (tooltipTextOption) {
|
|
attributeOptions.html = tooltipTextOption;
|
|
}
|
|
|
|
if (positionOption) {
|
|
attributeOptions.position = positionOption;
|
|
}
|
|
return attributeOptions;
|
|
}
|
|
}
|
|
|
|
M.Tooltip = Tooltip;
|
|
|
|
if (M.jQueryLoaded) {
|
|
M.initializeJqueryWrapper(Tooltip, 'tooltip', 'M_Tooltip');
|
|
}
|
|
})(cash, M.anime);
|