cartodb/lib/assets/javascripts/builder/components/tipsy-tooltip-view.js
2020-06-15 10:58:47 +08:00

165 lines
4.5 KiB
JavaScript
Executable File

var _ = require('underscore');
var CoreView = require('backbone/core-view');
require('tipsy');
/**
* Tipsy tooltip view.
*
* - Needs an element to work.
* - Inits tipsy library.
* - Clean bastard tipsy bindings easily.
*
*/
module.exports = CoreView.extend({
options: {
gravity: 's',
opacity: 1,
fade: true
},
events: {
'mouseenter': '_onMouseEnter',
'mouseleave': '_onMouseLeave'
},
initialize: function (opts) {
if (!opts.el) throw new Error('Element is needed to have tipsy tooltip working');
this._mouseEnterAction = opts.mouseEnterAction;
this._mouseLeaveAction = opts.mouseLeaveAction;
this._tipsyOpenedManually = opts.trigger === 'manual';
this._initTipsy();
},
_initTipsy: function () {
var options = _.clone(this.options);
if (options.gravity === 'auto') {
options.gravity = this.getOptimalGravity(
options.gravityConfiguration && options.gravityConfiguration.preferredGravities || ['s', 'n', 'e', 'w'],
options.gravityConfiguration && options.gravityConfiguration.margin || 0,
window
);
}
if (!options.title) {
options.title = this.getTitleFromDataAttribute;
}
this.$el.tipsy(options);
this.tipsy = this.$el.data('tipsy');
},
_onMouseEnter: function () {
this._mouseEnterAction && this._mouseEnterAction();
},
_onMouseLeave: function () {
this._mouseLeaveAction && this._mouseLeaveAction();
},
setOffset: function (offset) {
this.tipsy.options.offset = offset;
},
showTipsy: function () {
this.$el.tipsy('show');
},
hideTipsy: function () {
this.$el.tipsy('hide');
},
getElement: function () {
return this.el;
},
getOptimalGravity: function (preferredGravities, margin, browserWindow) {
return function (tooltipElement) {
if (!tooltipElement) {
return preferredGravities[0] || 'n';
}
var tooltipContainer = this;
var tooltipContainerBoundingRect = tooltipContainer.getBoundingClientRect();
var gravityOptions = { n: 'bottom', s: 'top', w: 'right', e: 'left' };
var viewportBoundaries = {
top: browserWindow.pageYOffset + margin,
right: browserWindow.innerWidth + browserWindow.pageXOffset - margin,
bottom: browserWindow.innerHeight + browserWindow.pageYOffset - margin,
left: browserWindow.pageXOffset + margin
};
var tooltipBoundaries = {
top: tooltipContainerBoundingRect.top - tooltipElement.offsetHeight,
right: tooltipContainerBoundingRect.left + tooltipContainerBoundingRect.width + tooltipElement.offsetWidth,
bottom: tooltipContainerBoundingRect.top + tooltipContainerBoundingRect.height + tooltipElement.offsetHeight,
left: tooltipContainerBoundingRect.left - tooltipElement.offsetWidth
};
var tooltipBodyGravity = '';
var optimalGravity = _.find(preferredGravities, function (gravity) {
var position = gravityOptions[gravity];
var tooltipFitsPosition = isTooltipInsideViewport(position, viewportBoundaries[position], tooltipBoundaries[position]);
if (tooltipFitsPosition && (position === 'top' || position === 'bottom')) {
tooltipBodyGravity = getTooltipBodyGravity(gravityOptions, viewportBoundaries, tooltipBoundaries);
}
return tooltipFitsPosition;
});
return optimalGravity ? optimalGravity + tooltipBodyGravity : preferredGravities[0];
};
},
getTitleFromDataAttribute: function () {
return this.getAttribute('data-tooltip');
},
destroyTipsy: function () {
if (this.tipsy) {
// tipsy does not return this
this.tipsy.hide();
this.$el.unbind('mouseleave mouseenter');
}
if (this._tipsyOpenedManually) {
this.hideTipsy();
}
this.$el.removeData('tipsy');
delete this.tipsy;
},
clean: function () {
this.destroyTipsy();
}
});
var isTooltipInsideViewport = function (bound, viewportBound, tooltipBound) {
if (bound === 'top' || bound === 'left') {
return viewportBound <= tooltipBound;
}
if (bound === 'right' || bound === 'bottom') {
return viewportBound >= tooltipBound;
}
};
var getTooltipBodyGravity = function (gravityOptions, viewportBoundaries, tooltipBoundaries) {
var nonCollisioningEdges = _.filter(['e', 'w'], function (edge) {
var bound = gravityOptions[edge];
return isTooltipInsideViewport(bound, viewportBoundaries[bound], tooltipBoundaries[bound]);
});
if (nonCollisioningEdges.length === 1) {
return nonCollisioningEdges[0];
}
return '';
};