biofriction-wp-theme/node_modules/foundation-sites/js/foundation.tooltip.js

457 lines
12 KiB
JavaScript

'use strict';
import $ from 'jquery';
import { GetYoDigits, ignoreMousedisappear } from './foundation.core.utils';
import { MediaQuery } from './foundation.util.mediaQuery';
import { Triggers } from './foundation.util.triggers';
import { Positionable } from './foundation.positionable';
/**
* Tooltip module.
* @module foundation.tooltip
* @requires foundation.util.box
* @requires foundation.util.mediaQuery
* @requires foundation.util.triggers
*/
class Tooltip extends Positionable {
/**
* Creates a new instance of a Tooltip.
* @class
* @name Tooltip
* @fires Tooltip#init
* @param {jQuery} element - jQuery object to attach a tooltip to.
* @param {Object} options - object to extend the default configuration.
*/
_setup(element, options) {
this.$element = element;
this.options = $.extend({}, Tooltip.defaults, this.$element.data(), options);
this.className = 'Tooltip'; // ie9 back compat
this.isActive = false;
this.isClick = false;
// Triggers init is idempotent, just need to make sure it is initialized
Triggers.init($);
this._init();
}
/**
* Initializes the tooltip by setting the creating the tip element, adding it's text, setting private variables and setting attributes on the anchor.
* @private
*/
_init() {
MediaQuery._init();
var elemId = this.$element.attr('aria-describedby') || GetYoDigits(6, 'tooltip');
this.options.tipText = this.options.tipText || this.$element.attr('title');
this.template = this.options.template ? $(this.options.template) : this._buildTemplate(elemId);
if (this.options.allowHtml) {
this.template.appendTo(document.body)
.html(this.options.tipText)
.hide();
} else {
this.template.appendTo(document.body)
.text(this.options.tipText)
.hide();
}
this.$element.attr({
'title': '',
'aria-describedby': elemId,
'data-yeti-box': elemId,
'data-toggle': elemId,
'data-resize': elemId
}).addClass(this.options.triggerClass);
super._init();
this._events();
}
_getDefaultPosition() {
// handle legacy classnames
var position = this.$element[0].className.match(/\b(top|left|right|bottom)\b/g);
return position ? position[0] : 'top';
}
_getDefaultAlignment() {
return 'center';
}
_getHOffset() {
if(this.position === 'left' || this.position === 'right') {
return this.options.hOffset + this.options.tooltipWidth;
} else {
return this.options.hOffset
}
}
_getVOffset() {
if(this.position === 'top' || this.position === 'bottom') {
return this.options.vOffset + this.options.tooltipHeight;
} else {
return this.options.vOffset
}
}
/**
* builds the tooltip element, adds attributes, and returns the template.
* @private
*/
_buildTemplate(id) {
var templateClasses = (`${this.options.tooltipClass} ${this.options.templateClasses}`).trim();
var $template = $('<div></div>').addClass(templateClasses).attr({
'role': 'tooltip',
'aria-hidden': true,
'data-is-active': false,
'data-is-focus': false,
'id': id
});
return $template;
}
/**
* sets the position class of an element and recursively calls itself until there are no more possible positions to attempt, or the tooltip element is no longer colliding.
* if the tooltip is larger than the screen width, default to full width - any user selected margin
* @private
*/
_setPosition() {
super._setPosition(this.$element, this.template);
}
/**
* reveals the tooltip, and fires an event to close any other open tooltips on the page
* @fires Tooltip#closeme
* @fires Tooltip#show
* @function
*/
show() {
if (this.options.showOn !== 'all' && !MediaQuery.is(this.options.showOn)) {
// console.error('The screen is too small to display this tooltip');
return false;
}
var _this = this;
this.template.css('visibility', 'hidden').show();
this._setPosition();
this.template.removeClass('top bottom left right').addClass(this.position)
this.template.removeClass('align-top align-bottom align-left align-right align-center').addClass('align-' + this.alignment);
/**
* Fires to close all other open tooltips on the page
* @event Closeme#tooltip
*/
this.$element.trigger('closeme.zf.tooltip', this.template.attr('id'));
this.template.attr({
'data-is-active': true,
'aria-hidden': false
});
_this.isActive = true;
// console.log(this.template);
this.template.stop().hide().css('visibility', '').fadeIn(this.options.fadeInDuration, function() {
//maybe do stuff?
});
/**
* Fires when the tooltip is shown
* @event Tooltip#show
*/
this.$element.trigger('show.zf.tooltip');
}
/**
* Hides the current tooltip, and resets the positioning class if it was changed due to collision
* @fires Tooltip#hide
* @function
*/
hide() {
// console.log('hiding', this.$element.data('yeti-box'));
var _this = this;
this.template.stop().attr({
'aria-hidden': true,
'data-is-active': false
}).fadeOut(this.options.fadeOutDuration, function() {
_this.isActive = false;
_this.isClick = false;
});
/**
* fires when the tooltip is hidden
* @event Tooltip#hide
*/
this.$element.trigger('hide.zf.tooltip');
}
/**
* adds event listeners for the tooltip and its anchor
* TODO combine some of the listeners like focus and mouseenter, etc.
* @private
*/
_events() {
var _this = this;
var $template = this.template;
var isFocus = false;
if (!this.options.disableHover) {
this.$element
.on('mouseenter.zf.tooltip', function(e) {
if (!_this.isActive) {
_this.timeout = setTimeout(function() {
_this.show();
}, _this.options.hoverDelay);
}
})
.on('mouseleave.zf.tooltip', ignoreMousedisappear(function(e) {
clearTimeout(_this.timeout);
if (!isFocus || (_this.isClick && !_this.options.clickOpen)) {
_this.hide();
}
}));
}
if (this.options.clickOpen) {
this.$element.on('mousedown.zf.tooltip', function(e) {
e.stopImmediatePropagation();
if (_this.isClick) {
//_this.hide();
// _this.isClick = false;
} else {
_this.isClick = true;
if ((_this.options.disableHover || !_this.$element.attr('tabindex')) && !_this.isActive) {
_this.show();
}
}
});
} else {
this.$element.on('mousedown.zf.tooltip', function(e) {
e.stopImmediatePropagation();
_this.isClick = true;
});
}
if (!this.options.disableForTouch) {
this.$element
.on('tap.zf.tooltip touchend.zf.tooltip', function(e) {
_this.isActive ? _this.hide() : _this.show();
});
}
this.$element.on({
// 'toggle.zf.trigger': this.toggle.bind(this),
// 'close.zf.trigger': this.hide.bind(this)
'close.zf.trigger': this.hide.bind(this)
});
this.$element
.on('focus.zf.tooltip', function(e) {
isFocus = true;
if (_this.isClick) {
// If we're not showing open on clicks, we need to pretend a click-launched focus isn't
// a real focus, otherwise on hover and come back we get bad behavior
if(!_this.options.clickOpen) { isFocus = false; }
return false;
} else {
_this.show();
}
})
.on('focusout.zf.tooltip', function(e) {
isFocus = false;
_this.isClick = false;
_this.hide();
})
.on('resizeme.zf.trigger', function() {
if (_this.isActive) {
_this._setPosition();
}
});
}
/**
* adds a toggle method, in addition to the static show() & hide() functions
* @function
*/
toggle() {
if (this.isActive) {
this.hide();
} else {
this.show();
}
}
/**
* Destroys an instance of tooltip, removes template element from the view.
* @function
*/
_destroy() {
this.$element.attr('title', this.template.text())
.off('.zf.trigger .zf.tooltip')
.removeClass(this.options.triggerClass)
.removeClass('top right left bottom')
.removeAttr('aria-describedby data-disable-hover data-resize data-toggle data-tooltip data-yeti-box');
this.template.remove();
}
}
Tooltip.defaults = {
disableForTouch: false,
/**
* Time, in ms, before a tooltip should open on hover.
* @option
* @type {number}
* @default 200
*/
hoverDelay: 200,
/**
* Time, in ms, a tooltip should take to fade into view.
* @option
* @type {number}
* @default 150
*/
fadeInDuration: 150,
/**
* Time, in ms, a tooltip should take to fade out of view.
* @option
* @type {number}
* @default 150
*/
fadeOutDuration: 150,
/**
* Disables hover events from opening the tooltip if set to true
* @option
* @type {boolean}
* @default false
*/
disableHover: false,
/**
* Optional addtional classes to apply to the tooltip template on init.
* @option
* @type {string}
* @default ''
*/
templateClasses: '',
/**
* Non-optional class added to tooltip templates. Foundation default is 'tooltip'.
* @option
* @type {string}
* @default 'tooltip'
*/
tooltipClass: 'tooltip',
/**
* Class applied to the tooltip anchor element.
* @option
* @type {string}
* @default 'has-tip'
*/
triggerClass: 'has-tip',
/**
* Minimum breakpoint size at which to open the tooltip.
* @option
* @type {string}
* @default 'small'
*/
showOn: 'small',
/**
* Custom template to be used to generate markup for tooltip.
* @option
* @type {string}
* @default ''
*/
template: '',
/**
* Text displayed in the tooltip template on open.
* @option
* @type {string}
* @default ''
*/
tipText: '',
touchCloseText: 'Tap to close.',
/**
* Allows the tooltip to remain open if triggered with a click or touch event.
* @option
* @type {boolean}
* @default true
*/
clickOpen: true,
/**
* Position of tooltip. Can be left, right, bottom, top, or auto.
* @option
* @type {string}
* @default 'auto'
*/
position: 'auto',
/**
* Alignment of tooltip relative to anchor. Can be left, right, bottom, top, center, or auto.
* @option
* @type {string}
* @default 'auto'
*/
alignment: 'auto',
/**
* Allow overlap of container/window. If false, tooltip will first try to
* position as defined by data-position and data-alignment, but reposition if
* it would cause an overflow. @option
* @type {boolean}
* @default false
*/
allowOverlap: false,
/**
* Allow overlap of only the bottom of the container. This is the most common
* behavior for dropdowns, allowing the dropdown to extend the bottom of the
* screen but not otherwise influence or break out of the container.
* Less common for tooltips.
* @option
* @type {boolean}
* @default false
*/
allowBottomOverlap: false,
/**
* Distance, in pixels, the template should push away from the anchor on the Y axis.
* @option
* @type {number}
* @default 0
*/
vOffset: 0,
/**
* Distance, in pixels, the template should push away from the anchor on the X axis
* @option
* @type {number}
* @default 0
*/
hOffset: 0,
/**
* Distance, in pixels, the template spacing auto-adjust for a vertical tooltip
* @option
* @type {number}
* @default 14
*/
tooltipHeight: 14,
/**
* Distance, in pixels, the template spacing auto-adjust for a horizontal tooltip
* @option
* @type {number}
* @default 12
*/
tooltipWidth: 12,
/**
* Allow HTML in tooltip. Warning: If you are loading user-generated content into tooltips,
* allowing HTML may open yourself up to XSS attacks.
* @option
* @type {boolean}
* @default false
*/
allowHtml: false
};
/**
* TODO utilize resize event trigger
*/
export {Tooltip};