Initial commit
This commit is contained in:
164
lib/assets/javascripts/builder/components/tipsy-tooltip-view.js
Executable file
164
lib/assets/javascripts/builder/components/tipsy-tooltip-view.js
Executable file
@@ -0,0 +1,164 @@
|
||||
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 '';
|
||||
};
|
||||
Reference in New Issue
Block a user