[IMP] web_tour: tip location
Choosing where to place a tip is a difficult problem. If placed in the body element, the tip have to be repositionned on each scroll event which is performance-consuming and induces bugs (firefox scroll event, tips which is over an element while the anchor is under, ...). If placed in the anchor parent which scrolls, all the above problems are solved but when the tip opens it cannot overflow its parent boundaries, which is not possible in many contexts (modals, ...). The solution here is to change the tip location when it opens/closes. When closed, it is placed in the anchor parent which scrolls. This allows to not have to reposition the tip on each scroll. The tip is also naturally hidden when its anchor is not visible on the screen. When opened, the tip is placed in the body, where it can be placed on the top of anything in the DOM. The transition from one state to another is not noticable as the position does not have to be computed when the tip is moved in the DOM and the animation is purely CSS. Note: however, this commit does not solve the firefox bug of closing the tip when the window scrolls. Here, the temporary solution is to let the tip be wrongly position if the user scrolls when a tip is opened (this only occurs when using the mouse wheel anyway).
This commit is contained in:
@@ -11,7 +11,6 @@ return Widget.extend({
|
||||
mouseleave: "_to_bubble_mode",
|
||||
},
|
||||
/**
|
||||
* @param {$anchor} [JQuery] the node on which the tip should be placed
|
||||
* @param {info} [Object] description of the tip, containing the following keys:
|
||||
* - content [String] the html content of the tip
|
||||
* - event_handlers [Object] description of optional event handlers to bind to the tip:
|
||||
@@ -24,9 +23,8 @@ return Widget.extend({
|
||||
* - overlay [Object] x and y values for the number of pixels the mouseout detection area
|
||||
* overlaps the opened tip, default {x: 50, y: 50}
|
||||
*/
|
||||
init: function(parent, $anchor, info) {
|
||||
init: function(parent, info) {
|
||||
this._super(parent);
|
||||
this.$anchor = $anchor;
|
||||
this.info = _.defaults(info, {
|
||||
position: "right",
|
||||
width: 270,
|
||||
@@ -36,6 +34,24 @@ return Widget.extend({
|
||||
y: 50,
|
||||
},
|
||||
});
|
||||
this.position = {
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
};
|
||||
},
|
||||
/**
|
||||
* @param {$anchor} [JQuery] the node on which the tip should be placed
|
||||
*/
|
||||
attach_to: function ($anchor) {
|
||||
this.$anchor = $anchor;
|
||||
this.$ideal_location = this._get_ideal_location();
|
||||
|
||||
var position = this.$ideal_location.css("position");
|
||||
if (position === "static") {
|
||||
this.$ideal_location.addClass("o_tooltip_parent");
|
||||
}
|
||||
|
||||
return this.appendTo(this.$ideal_location);
|
||||
},
|
||||
start: function() {
|
||||
this.$tooltip_overlay = this.$(".o_tooltip_overlay");
|
||||
@@ -59,30 +75,58 @@ return Widget.extend({
|
||||
|
||||
this._reposition();
|
||||
this.$el.css("opacity", 1);
|
||||
core.bus.on('scroll resize', this, function() {
|
||||
core.bus.on("resize", this, function () {
|
||||
if (this.tip_opened) {
|
||||
this._to_bubble_mode(true);
|
||||
}
|
||||
this._reposition();
|
||||
});
|
||||
|
||||
this.$el.on("transitionend oTransitionEnd webkitTransitionEnd", (function () {
|
||||
if (!this.tip_opened && this.$el.parent()[0] === document.body) {
|
||||
this.$el.detach();
|
||||
this.$el.css(this.position);
|
||||
this.$el.appendTo(this.$ideal_location);
|
||||
}
|
||||
}).bind(this));
|
||||
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
destroy: function () {
|
||||
this._unbind_anchor_events();
|
||||
clearTimeout(this.timerIn);
|
||||
clearTimeout(this.timerOut);
|
||||
|
||||
// Do not remove the parent class if it contains other tooltips
|
||||
if (this.$ideal_location.children(".o_tooltip").not(this.$el[0]).length === 0) {
|
||||
this.$ideal_location.removeClass("o_tooltip_parent");
|
||||
}
|
||||
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
update: function($anchor) {
|
||||
update: function ($anchor) {
|
||||
if (!$anchor.is(this.$anchor)) {
|
||||
this._unbind_anchor_events();
|
||||
this.$anchor = $anchor;
|
||||
this.$ideal_location = this._get_ideal_location();
|
||||
if (this.$el.parent()[0] !== document.body) {
|
||||
this.$el.appendTo(this.$ideal_location);
|
||||
}
|
||||
this._bind_anchor_events();
|
||||
}
|
||||
this._reposition();
|
||||
},
|
||||
_reposition: function() {
|
||||
_get_ideal_location: function () {
|
||||
var $location = this.$anchor;
|
||||
var o;
|
||||
do {
|
||||
$location = $location.parent();
|
||||
o = $location.css("overflow");
|
||||
} while ((o === "visible" || o === "hidden") && $location[0] !== document.body);
|
||||
|
||||
return $location;
|
||||
},
|
||||
_reposition: function () {
|
||||
if (this.tip_opened) return;
|
||||
this.$el.removeClass("o_animated");
|
||||
|
||||
@@ -101,6 +145,8 @@ return Widget.extend({
|
||||
left: -Math.min((this.info.position === "right" ? this.info.space : this.info.overlay.x), offset.left),
|
||||
});
|
||||
|
||||
this.position = this.$el.position();
|
||||
|
||||
this.$el.addClass("o_animated");
|
||||
},
|
||||
_bind_anchor_events: function () {
|
||||
@@ -135,6 +181,9 @@ return Widget.extend({
|
||||
this.timerOut = undefined;
|
||||
return;
|
||||
}
|
||||
if (this.tip_opened) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (force === true) {
|
||||
this._build_info_mode();
|
||||
@@ -149,6 +198,13 @@ return Widget.extend({
|
||||
this.tip_opened = true;
|
||||
|
||||
var offset = this.$el.offset();
|
||||
|
||||
if (this.$el.parent()[0] !== document.body) {
|
||||
this.$el.detach();
|
||||
this.$el.css(offset);
|
||||
this.$el.appendTo(document.body);
|
||||
}
|
||||
|
||||
var mbLeft = 0;
|
||||
var mbTop = 0;
|
||||
var overflow = false;
|
||||
@@ -180,6 +236,9 @@ return Widget.extend({
|
||||
this.timerIn = undefined;
|
||||
return;
|
||||
}
|
||||
if (!this.tip_opened) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (force === true) {
|
||||
this._build_bubble_mode();
|
||||
|
||||
@@ -125,9 +125,9 @@ return core.Class.extend({
|
||||
}],
|
||||
});
|
||||
}
|
||||
tip.widget = new Tip(this, $anchor, tip_info);
|
||||
tip.widget.appendTo(document.body);
|
||||
tip.widget = new Tip(this, tip_info);
|
||||
tip.widget.on('tip_consumed', this, this._consume_tip.bind(this, tip, tour_name));
|
||||
tip.widget.attach_to($anchor);
|
||||
|
||||
if (this.running_tour === tour_name) {
|
||||
clearTimeout(this.running_tour_timeout);
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
|
||||
.o_tooltip_parent {
|
||||
position: relative!important;
|
||||
}
|
||||
|
||||
.o_tooltip {
|
||||
@o-tip-background-color: lighten(@odoo-brand-primary, 5%);
|
||||
@o-tip-size: 26px;
|
||||
|
||||
Reference in New Issue
Block a user