[IMP] web_tour: tip animation
* Improve the JS/LESS so that its content makes the tip grow
with a bubble effect (instead of using a popover).
* Avoid flickering when the mouse leave/enter the tip or move
between the tip and its anchor element.
* Use text-align:left instead of justify
* Set the content width in JS (to a larger width) to be customizable
* Round LESS variable to avoid values like 266.6666666666
* Set opening direction according to viewport:
- Top and bottom tips open towards the right except if they overflow
the window on this right
- Left and right tips open towards the bottom except if they overflow
the window on this bottom
* Improve the 'Skip these steps' mechanism (which is easier as we don't
use bootstrap's popover anymore)
This commit is contained in:
@@ -1,23 +1,59 @@
|
|||||||
odoo.define('web_tour.Tip', function(require) {
|
odoo.define('web_tour.Tip', function(require) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
var core = require('web.core');
|
||||||
var Widget = require('web.Widget');
|
var Widget = require('web.Widget');
|
||||||
|
|
||||||
return Widget.extend({
|
return Widget.extend({
|
||||||
template: "Tip",
|
template: "Tip",
|
||||||
|
events: {
|
||||||
|
mouseenter: "_to_info_mode",
|
||||||
|
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:
|
||||||
|
* - event [String] the event name
|
||||||
|
* - selector [String] the jQuery selector on which the event should be bound
|
||||||
|
* - handler [function] the handler
|
||||||
|
* - position [String] tip's position ('top', 'right', 'left' or 'bottom'), default 'right'
|
||||||
|
* - width [int] the width in px of the tip when opened, default 270
|
||||||
|
* - space [int] space in px between anchor and tip, default 10
|
||||||
|
* - 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, $anchor, info) {
|
||||||
this._super(parent);
|
this._super(parent);
|
||||||
this.$anchor = $anchor;
|
this.$anchor = $anchor;
|
||||||
this.info = _.defaults(info, {
|
this.info = _.defaults(info, {
|
||||||
position: 'right',
|
position: "right",
|
||||||
|
width: 270,
|
||||||
|
space: 10,
|
||||||
|
overlay: {
|
||||||
|
x: 50,
|
||||||
|
y: 50,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
this.consumed = false;
|
|
||||||
},
|
},
|
||||||
start: function() {
|
start: function() {
|
||||||
this.$breathing = this.$('.o_breathing');
|
this.init_width = this.$el.innerWidth();
|
||||||
this.$breathing.on('mouseenter', this._to_info_mode.bind(this));
|
this.init_height = this.$el.innerHeight();
|
||||||
|
this.$tooltip_overlay = this.$(".o_tooltip_overlay");
|
||||||
|
this.$tooltip_content = this.$(".o_tooltip_content");
|
||||||
|
_.each(this.info.event_handlers, (function(data) {
|
||||||
|
this.$tooltip_content.on(data.event, data.selector, data.handler);
|
||||||
|
}).bind(this));
|
||||||
this._bind_anchor_events();
|
this._bind_anchor_events();
|
||||||
this._reposition();
|
this._reposition();
|
||||||
|
core.bus.on('scroll resize', this, function() {
|
||||||
|
if (this.tip_opened) {
|
||||||
|
this._to_bubble_mode();
|
||||||
|
}
|
||||||
|
this._reposition();
|
||||||
|
});
|
||||||
|
return this._super.apply(this, arguments);
|
||||||
},
|
},
|
||||||
update: function($anchor) {
|
update: function($anchor) {
|
||||||
if (!$anchor.is(this.$anchor)) {
|
if (!$anchor.is(this.$anchor)) {
|
||||||
@@ -28,50 +64,107 @@ return Widget.extend({
|
|||||||
this._reposition();
|
this._reposition();
|
||||||
},
|
},
|
||||||
_reposition: function() {
|
_reposition: function() {
|
||||||
this.$breathing.position({
|
if (this.tip_opened) return;
|
||||||
my: this._invert_position(this.info.position),
|
this.$el.removeClass("o_animated");
|
||||||
|
this.$el.position({
|
||||||
|
my: this._get_spaced_inverted_position(this.info.position),
|
||||||
at: this.info.position,
|
at: this.info.position,
|
||||||
of: this.$anchor,
|
of: this.$anchor,
|
||||||
collision: 'fit',
|
collision: "none",
|
||||||
});
|
});
|
||||||
|
this.$tooltip_overlay.css({
|
||||||
|
top: -(this.info.position === "bottom" ? this.info.space : this.info.overlay.y),
|
||||||
|
right: -(this.info.position === "left" ? this.info.space : this.info.overlay.x),
|
||||||
|
bottom: -(this.info.position === "top" ? this.info.space : this.info.overlay.y),
|
||||||
|
left: -(this.info.position === "right" ? this.info.space : this.info.overlay.x),
|
||||||
|
});
|
||||||
|
this.$el.addClass("o_animated");
|
||||||
},
|
},
|
||||||
_bind_anchor_events: function () {
|
_bind_anchor_events: function () {
|
||||||
var self = this;
|
var consume_event = this.$anchor.is('input,textarea') ? 'change' : 'mousedown';
|
||||||
|
this.$anchor.one(consume_event, this.trigger.bind(this, 'tip_consumed'));
|
||||||
this.$anchor.on('mouseenter', this._to_info_mode.bind(this));
|
this.$anchor.on('mouseenter', this._to_info_mode.bind(this));
|
||||||
this.$anchor.on('mouseleave', this._to_bubble_mode.bind(this));
|
this.$anchor.on('mouseleave', this._to_bubble_mode.bind(this));
|
||||||
this.$anchor.on(this.$anchor.is('input,textarea') ? 'change' : 'mousedown', function () {
|
|
||||||
if (self.consumed) return;
|
|
||||||
self.consumed = true;
|
|
||||||
self.trigger('tip_consumed');
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
_invert_position: function (position) {
|
_get_spaced_inverted_position: function (position) {
|
||||||
if (position === "right") return "left";
|
if (position === "right") return "left+" + this.info.space;
|
||||||
if (position === "left") return "right";
|
if (position === "left") return "right-" + this.info.space;
|
||||||
if (position === "bottom") return "top";
|
if (position === "bottom") return "top+" + this.info.space;
|
||||||
return "bottom";
|
return "bottom-" + this.info.space;
|
||||||
},
|
},
|
||||||
_to_info_mode: function() {
|
_to_info_mode: function(force) {
|
||||||
this.$breathing.fadeOut(300);
|
if (this.timerOut !== undefined) {
|
||||||
this.$popover = this.$popover || this.$anchor.popover({
|
clearTimeout(this.timerOut);
|
||||||
content: this.info.content + (this.info.extra_content || ''),
|
this.timerOut = undefined;
|
||||||
html: true,
|
return;
|
||||||
animation: false,
|
}
|
||||||
container: this.$el,
|
|
||||||
placement: this.info.position,
|
if (force === true) {
|
||||||
});
|
this.__to_info_mode();
|
||||||
this.$popover.popover('show');
|
} else {
|
||||||
this.$('.popover').on('mouseleave.to_bubble_mode', this._to_bubble_mode.bind(this));
|
this.timerIn = setTimeout(this.__to_info_mode.bind(this), 100);
|
||||||
this.$('.popover').on('click', this.trigger.bind(this, 'popover_clicked'));
|
|
||||||
},
|
|
||||||
_to_bubble_mode: function () {
|
|
||||||
this.$breathing.fadeIn(300);
|
|
||||||
if (this.$popover) {
|
|
||||||
this.$popover.popover('hide');
|
|
||||||
this.$('.popover').off('mouseleave.to_bubble_mode');
|
|
||||||
this.$('.popover').off('click');
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
__to_info_mode: function() {
|
||||||
|
clearTimeout(this.timerIn);
|
||||||
|
this.timerIn = undefined;
|
||||||
|
|
||||||
|
var content_width = this.$tooltip_content.outerWidth();
|
||||||
|
var content_height = this.$tooltip_content.outerHeight();
|
||||||
|
|
||||||
|
this.tip_opened = true;
|
||||||
|
|
||||||
|
var offset = this.$el.offset();
|
||||||
|
var mbLeft = 0;
|
||||||
|
var mbTop = 0;
|
||||||
|
var overflow = false;
|
||||||
|
var posVertical = (this.info.position === "top" || this.info.position === "bottom");
|
||||||
|
if (posVertical) {
|
||||||
|
overflow = (offset.left + content_width + this.info.overlay.x > $(window).width());
|
||||||
|
} else {
|
||||||
|
overflow = (offset.top + content_height + this.info.overlay.y > $(window).height());
|
||||||
|
}
|
||||||
|
if (posVertical && overflow || this.info.position === "left") {
|
||||||
|
mbLeft -= (content_width - this.init_width);
|
||||||
|
}
|
||||||
|
if (!posVertical && overflow || this.info.position === "top") {
|
||||||
|
mbTop -= (content_height - this.init_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$el.toggleClass("inverse", overflow);
|
||||||
|
this.$el.removeClass("o_animated").addClass("active");
|
||||||
|
this.$el.css({
|
||||||
|
width: content_width,
|
||||||
|
height: content_height,
|
||||||
|
"margin-left": mbLeft,
|
||||||
|
"margin-top": mbTop,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
_to_bubble_mode: function (force) {
|
||||||
|
if (this.timerIn !== undefined) {
|
||||||
|
clearTimeout(this.timerIn);
|
||||||
|
this.timerIn = undefined;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (force === true) {
|
||||||
|
this.__to_bubble_mode();
|
||||||
|
} else {
|
||||||
|
this.timerOut = setTimeout(this.__to_bubble_mode.bind(this), 300);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
__to_bubble_mode: function() {
|
||||||
|
clearTimeout(this.timerOut);
|
||||||
|
this.timerOut = undefined;
|
||||||
|
|
||||||
|
this.tip_opened = false;
|
||||||
|
|
||||||
|
this.$el.removeClass("active").addClass("o_animated");
|
||||||
|
this.$el.css({
|
||||||
|
width: this.init_width,
|
||||||
|
height: this.init_height,
|
||||||
|
margin: 0,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ function getRunningKey() {
|
|||||||
|
|
||||||
return core.Class.extend({
|
return core.Class.extend({
|
||||||
init: function(consumed_tours) {
|
init: function(consumed_tours) {
|
||||||
|
this.$body = $('body');
|
||||||
this.active_tooltips = {};
|
this.active_tooltips = {};
|
||||||
this.tours = {};
|
this.tours = {};
|
||||||
this.consumed_tours = consumed_tours;
|
this.consumed_tours = consumed_tours;
|
||||||
@@ -47,11 +48,17 @@ return core.Class.extend({
|
|||||||
var tour = {
|
var tour = {
|
||||||
name: name,
|
name: name,
|
||||||
current_step: parseInt(local_storage.getItem(getStepKey(name))) || 0,
|
current_step: parseInt(local_storage.getItem(getStepKey(name))) || 0,
|
||||||
skip_enabled: options.skip_enabled,
|
|
||||||
steps: steps,
|
steps: steps,
|
||||||
url: options.url,
|
url: options.url,
|
||||||
test: options.test,
|
test: options.test,
|
||||||
};
|
};
|
||||||
|
if (options.skip_enabled) {
|
||||||
|
tour.skip_link = '<p><span class="o_skip_tour">' + _t('Skip tour') + '</span></p>';
|
||||||
|
tour.skip_handler = function (tip) {
|
||||||
|
this._deactivate_tip(tip);
|
||||||
|
this._consume_tour(name);
|
||||||
|
};
|
||||||
|
}
|
||||||
this.tours[name] = tour;
|
this.tours[name] = tour;
|
||||||
if (name === this.running_tour || (!tour.test && !_.contains(this.consumed_tours, name))) {
|
if (name === this.running_tour || (!tour.test && !_.contains(this.consumed_tours, name))) {
|
||||||
this.active_tooltips[name] = steps[tour.current_step];
|
this.active_tooltips[name] = steps[tour.current_step];
|
||||||
@@ -84,6 +91,7 @@ return core.Class.extend({
|
|||||||
* active tours otherwise). Should be called each time the DOM changes.
|
* active tours otherwise). Should be called each time the DOM changes.
|
||||||
*/
|
*/
|
||||||
update: function() {
|
update: function() {
|
||||||
|
this.in_modal = this.$body.hasClass('modal-open');
|
||||||
if (this.running_tour) {
|
if (this.running_tour) {
|
||||||
this._check_for_tooltip(this.active_tooltips[this.running_tour], this.running_tour);
|
this._check_for_tooltip(this.active_tooltips[this.running_tour], this.running_tour);
|
||||||
} else {
|
} else {
|
||||||
@@ -91,7 +99,7 @@ return core.Class.extend({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
_check_for_tooltip: function (tip, tour_name) {
|
_check_for_tooltip: function (tip, tour_name) {
|
||||||
var $trigger = $(tip.trigger).filter(':visible').first();
|
var $trigger = $((this.in_modal ? '.modal ' : '') + tip.trigger).filter(':visible').first();
|
||||||
var extra_trigger = tip.extra_trigger ? $(tip.extra_trigger).filter(':visible').length : true;
|
var extra_trigger = tip.extra_trigger ? $(tip.extra_trigger).filter(':visible').length : true;
|
||||||
var triggered = $trigger.length && extra_trigger;
|
var triggered = $trigger.length && extra_trigger;
|
||||||
if (triggered) {
|
if (triggered) {
|
||||||
@@ -105,22 +113,22 @@ return core.Class.extend({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
_activate_tip: function(tip, tour_name, $anchor) {
|
_activate_tip: function(tip, tour_name, $anchor) {
|
||||||
var skip_enabled = this.tours[tour_name].skip_enabled;
|
var tour = this.tours[tour_name];
|
||||||
if (skip_enabled && !tip.extra_content) {
|
var tip_info = tip;
|
||||||
// FIXME: not pretty but a cleaner solution will be easy when we'll stop using jquery's popover
|
if (tour.skip_link) {
|
||||||
tip.extra_content = '<br/><span class="o_skip_tour">' + _t('Skip tour') + '</span>';
|
tip_info = _.extend(_.omit(tip_info, 'content'), {
|
||||||
}
|
content: tip.content + tour.skip_link,
|
||||||
tip.widget = new Tip(this, $anchor, tip);
|
event_handlers: [{
|
||||||
tip.widget.appendTo(document.body);
|
event: 'click',
|
||||||
tip.widget.on('tip_consumed', this, this._consume_tip.bind(this, tip, tour_name));
|
selector: '.o_skip_tour',
|
||||||
if (skip_enabled) {
|
handler: tour.skip_handler.bind(this, tip),
|
||||||
tip.widget.on('popover_clicked', this, function (event) {
|
}],
|
||||||
if (event.target.className === 'o_skip_tour') {
|
|
||||||
this._deactivate_tip(tip);
|
|
||||||
this._consume_tour(tour_name);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
tip.widget = new Tip(this, $anchor, tip_info);
|
||||||
|
tip.widget.appendTo(document.body);
|
||||||
|
tip.widget.on('tip_consumed', this, this._consume_tip.bind(this, tip, tour_name));
|
||||||
|
|
||||||
if (this.running_tour === tour_name) {
|
if (this.running_tour === tour_name) {
|
||||||
clearTimeout(this.running_tour_timeout);
|
clearTimeout(this.running_tour_timeout);
|
||||||
if (tip.run) {
|
if (tip.run) {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ return session.is_bound.then(function () {
|
|||||||
var tour = new TourManager(session.web_tours);
|
var tour = new TourManager(session.web_tours);
|
||||||
|
|
||||||
// Use a MutationObserver to detect DOM changes
|
// Use a MutationObserver to detect DOM changes
|
||||||
var untracked_classnames = ['o_tooltip', 'o_breathing'];
|
var untracked_classnames = ["o_tooltip", "o_tooltip_content", "o_tooltip_overlay"];
|
||||||
var check_tooltip = _.throttle(function (records) {
|
var check_tooltip = _.throttle(function (records) {
|
||||||
var update = _.find(records, function (record) {
|
var update = _.find(records, function (record) {
|
||||||
var record_class = record.target.className;
|
var record_class = record.target.className;
|
||||||
|
|||||||
@@ -1,33 +1,49 @@
|
|||||||
@-webkit-keyframes move-left-right {
|
@-webkit-keyframes move-left-right {
|
||||||
0% {
|
0% {
|
||||||
.translate(0, 0);
|
.translate(-3px, 0);
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
.translate(5px, 0);
|
.translate(3px, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@-moz-keyframes move-left-right {
|
@-moz-keyframes move-left-right {
|
||||||
0% {
|
0% {
|
||||||
.translate(0px, 0px);
|
.translate(-3px, 0);
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
.translate(5px, 0px);
|
.translate(3px, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes move-left-right {
|
||||||
|
0% {
|
||||||
|
.translate(-3px, 0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
.translate(3px, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@-webkit-keyframes move-bottom-top {
|
@-webkit-keyframes move-bottom-top {
|
||||||
0% {
|
0% {
|
||||||
.translate(0, 0);
|
.translate(0, -3px);
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
.translate(0, 5px);
|
.translate(0, 3px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@-moz-keyframes move-bottom-top {
|
@-moz-keyframes move-bottom-top {
|
||||||
0% {
|
0% {
|
||||||
.translate(0px, 0px);
|
.translate(0px, -3px);
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
.translate(0, 5px);
|
.translate(0, 3px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes move-bottom-top {
|
||||||
|
0% {
|
||||||
|
.translate(0px, -3px);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
.translate(0, 3px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,92 +1,214 @@
|
|||||||
@o-tip-background-color: #875a7b;
|
|
||||||
|
|
||||||
.o_tooltip {
|
.o_tooltip {
|
||||||
.o_breathing {
|
@o-tip-background-color: lighten(@odoo-brand-primary, 5%);
|
||||||
position: absolute;
|
@o-tip-size: 26px;
|
||||||
width: 30px;
|
@o-tip-border-width: 2px;
|
||||||
height: 30px;
|
|
||||||
border-radius: 50%;
|
@o-tip-arrow-length: floor(@o-tip-size * 0.5);
|
||||||
z-index: @zindex-modal+1;
|
@o-tip-arrow-width: floor(@o-tip-size * 0.8);
|
||||||
background-color: @o-tip-background-color;
|
@o-tip-arrow-integration: floor(@o-tip-size * 0.1);
|
||||||
.animation-duration(0.5s);
|
|
||||||
.animation-timing-function(ease-in);
|
@o-tip-duration-in: 300ms;
|
||||||
.animation-iteration-count(infinite);
|
@o-tip-size-duration-in: floor(@o-tip-duration-in * 3 / 4);
|
||||||
.animation-direction(alternate);
|
@o-tip-size-delay-in: floor(@o-tip-duration-in / 4);
|
||||||
|
@o-tip-border-radius-duration-in: floor(@o-tip-duration-in / 2);
|
||||||
|
@o-tip-border-radius-delay-in: floor(@o-tip-duration-in * 0);
|
||||||
|
@o-tip-bg-color-duration-in: floor(@o-tip-duration-in * 1);
|
||||||
|
@o-tip-bg-color-delay-in: floor(@o-tip-duration-in * 0);
|
||||||
|
@o-tip-arrow-duration-in: floor(@o-tip-duration-in / 2);
|
||||||
|
@o-tip-arrow-delay-in: floor(@o-tip-duration-in * 0);
|
||||||
|
@o-tip-white-arrow-duration-in: floor(@o-tip-duration-in * 1);
|
||||||
|
@o-tip-white-arrow-delay-in: floor(@o-tip-duration-in * 0);
|
||||||
|
|
||||||
|
@o-tip-duration-out: 200ms;
|
||||||
|
@o-tip-size-duration-out: floor(@o-tip-duration-out * 2 / 3);
|
||||||
|
@o-tip-size-delay-out: floor(@o-tip-duration-out * 0);
|
||||||
|
@o-tip-border-radius-duration-out: floor(@o-tip-duration-out * 2 / 3);
|
||||||
|
@o-tip-border-radius-delay-out: floor(@o-tip-duration-out / 3);
|
||||||
|
@o-tip-bg-color-duration-out: floor(@o-tip-duration-out * 1);
|
||||||
|
@o-tip-bg-color-delay-out: floor(@o-tip-duration-out * 0);
|
||||||
|
@o-tip-arrow-duration-out: floor(@o-tip-duration-out / 2);
|
||||||
|
@o-tip-arrow-delay-out: floor(@o-tip-duration-out / 2);
|
||||||
|
|
||||||
|
box-sizing: content-box;
|
||||||
|
position: absolute;
|
||||||
|
width: @o-tip-size;
|
||||||
|
height: @o-tip-size;
|
||||||
|
border: @o-tip-border-width solid @odoo-brand-primary;
|
||||||
|
border-radius: 50%;
|
||||||
|
z-index: @zindex-modal+1;
|
||||||
|
background-color: @o-tip-background-color;
|
||||||
|
box-shadow: 0 0 2px 0 @odoo-brand-lightsecondary;
|
||||||
|
|
||||||
|
transition:
|
||||||
|
width @o-tip-size-duration-out ease @o-tip-size-delay-out,
|
||||||
|
height @o-tip-size-duration-out ease @o-tip-size-delay-out,
|
||||||
|
margin @o-tip-size-duration-out ease @o-tip-size-delay-out,
|
||||||
|
border-radius @o-tip-border-radius-duration-out ease @o-tip-border-radius-delay-out,
|
||||||
|
background-color @o-tip-bg-color-duration-out ease @o-tip-bg-color-delay-out;
|
||||||
|
|
||||||
|
&.o_animated {
|
||||||
|
.o-animation(move-bottom-top, 500ms, ease-in, 0ms, infinite, alternate);
|
||||||
&.right, &.left {
|
&.right, &.left {
|
||||||
.animation-name(move-left-right);
|
.animation-name(move-left-right);
|
||||||
}
|
}
|
||||||
&.top, &.bottom {
|
}
|
||||||
.animation-name(move-bottom-top);
|
|
||||||
}
|
|
||||||
|
|
||||||
&::after {
|
&::before {
|
||||||
content: '';
|
content: "";
|
||||||
width: 0;
|
border: 0 solid rgba(0, 0, 0, 0);
|
||||||
height: 0;
|
transition: all @o-tip-arrow-duration-out ease @o-tip-arrow-delay-out;
|
||||||
|
}
|
||||||
|
&.right::before {
|
||||||
|
.o-position-absolute(@left: -@o-tip-arrow-length+@o-tip-arrow-integration, @top: (@o-tip-size - @o-tip-arrow-width) / 2);
|
||||||
|
border-width: @o-tip-arrow-width/2 @o-tip-arrow-length @o-tip-arrow-width/2 0;
|
||||||
|
border-right-color: @o-tip-background-color;
|
||||||
|
}
|
||||||
|
&.top::before {
|
||||||
|
.o-position-absolute(@bottom: -@o-tip-arrow-length+@o-tip-arrow-integration, @left: (@o-tip-size - @o-tip-arrow-width) / 2);
|
||||||
|
border-width: @o-tip-arrow-length @o-tip-arrow-width/2 0 @o-tip-arrow-width/2;
|
||||||
|
border-top-color: @o-tip-background-color;
|
||||||
|
}
|
||||||
|
&.left::before {
|
||||||
|
.o-position-absolute(@right: -@o-tip-arrow-length+@o-tip-arrow-integration, @top: (@o-tip-size - @o-tip-arrow-width) / 2);
|
||||||
|
border-width: @o-tip-arrow-width/2 0 @o-tip-arrow-width/2 @o-tip-arrow-length;
|
||||||
|
border-left-color: @o-tip-background-color;
|
||||||
|
}
|
||||||
|
&.bottom::before {
|
||||||
|
.o-position-absolute(@top: -@o-tip-arrow-length+@o-tip-arrow-integration, @left: (@o-tip-size - @o-tip-arrow-width) / 2);
|
||||||
|
border-width: 0 @o-tip-arrow-width/2 @o-tip-arrow-length @o-tip-arrow-width/2;
|
||||||
|
border-bottom-color: @o-tip-background-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
@square-size: @o-tip-size*sqrt(2)/2;
|
||||||
|
@square-pos: (@o-tip-size - @square-size)/2;
|
||||||
|
.o-position-absolute(@square-pos, @square-pos, @square-pos, @square-pos);
|
||||||
|
content: "?";
|
||||||
|
line-height: @square-size;
|
||||||
|
text-align: center;
|
||||||
|
font-size: @square-size;
|
||||||
|
font-weight: 800;
|
||||||
|
border-color: @o-tip-background-color;
|
||||||
|
color: @odoo-brand-lightsecondary;
|
||||||
|
transition: margin @o-tip-size-duration-out ease @o-tip-size-delay-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .o_tooltip_overlay {
|
||||||
|
display: none;
|
||||||
|
.o-position-absolute(0, 0, 0, 0);
|
||||||
|
margin: -@o-tip-border-width;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
> .o_tooltip_content {
|
||||||
|
padding: 8px 15px;
|
||||||
|
visibility: hidden;
|
||||||
|
transition: visibility 0s ease 0s;
|
||||||
|
color: black;
|
||||||
|
|
||||||
|
.o_skip_tour {
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: gray;
|
||||||
|
&:hover {
|
||||||
|
color: darken(gray, 20%);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
&.right::after {
|
> p:last-child {
|
||||||
.o-position-absolute(@left: -4px, @top: 5px);
|
margin-bottom: 0;
|
||||||
border-right: 10px solid @o-tip-background-color;
|
|
||||||
border-top: 10px solid transparent;
|
|
||||||
border-bottom: 10px solid transparent;
|
|
||||||
}
|
|
||||||
&.top::after {
|
|
||||||
.o-position-absolute(@bottom: -4px, @left: 5px);
|
|
||||||
border-top: 10px solid @o-tip-background-color;
|
|
||||||
border-left: 10px solid transparent;
|
|
||||||
border-right: 10px solid transparent;
|
|
||||||
}
|
|
||||||
&.left::after {
|
|
||||||
.o-position-absolute(@right: -4px, @bottom: 5px);
|
|
||||||
border-left: 10px solid @o-tip-background-color;
|
|
||||||
border-top: 10px solid transparent;
|
|
||||||
border-bottom: 10px solid transparent;
|
|
||||||
}
|
|
||||||
&.bottom::after {
|
|
||||||
.o-position-absolute(@top: -4px, @right: 5px);
|
|
||||||
border-bottom: 10px solid @o-tip-background-color;
|
|
||||||
border-right: 10px solid transparent;
|
|
||||||
border-left: 10px solid transparent;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
border-radius: 0;
|
||||||
|
background-color: white;
|
||||||
|
|
||||||
|
transition:
|
||||||
|
width @o-tip-size-duration-in ease @o-tip-size-delay-in,
|
||||||
|
height @o-tip-size-duration-in ease @o-tip-size-delay-in,
|
||||||
|
margin @o-tip-size-duration-in ease @o-tip-size-delay-in,
|
||||||
|
border-radius @o-tip-border-radius-duration-in ease @o-tip-border-radius-delay-in,
|
||||||
|
background-color @o-tip-bg-color-duration-in ease @o-tip-bg-color-delay-in;
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
.o-position-absolute(@top: 10px, @left: 10px);
|
transition: all @o-tip-arrow-duration-in ease @o-tip-arrow-delay-in;
|
||||||
content: '?';
|
}
|
||||||
width: 10px;
|
&::after {
|
||||||
height: 10px;
|
content: "";
|
||||||
font-size: 18px;
|
border: 0 solid rgba(0, 0, 0, 0);
|
||||||
font-weight: 800;
|
}
|
||||||
line-height: 0.6;
|
|
||||||
color: lightgray;
|
> .o_tooltip_overlay {
|
||||||
border-radius: 50%;
|
display: block;
|
||||||
|
}
|
||||||
|
> .o_tooltip_content {
|
||||||
|
visibility: visible;
|
||||||
|
transition: visibility 0s ease @o-tip-duration-in;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.right {
|
||||||
|
&::before {
|
||||||
|
left: -@o-tip-arrow-length;
|
||||||
|
border-right-color: @odoo-brand-primary;
|
||||||
|
}
|
||||||
|
&::after {
|
||||||
|
.o-position-absolute(@left: -@o-tip-arrow-length+@o-tip-border-width*2, @top: (@o-tip-size - @o-tip-arrow-width) / 2);
|
||||||
|
border-width: @o-tip-arrow-width/2 @o-tip-arrow-length @o-tip-arrow-width/2 0;
|
||||||
|
border-right-color: white;
|
||||||
|
transition: border-right-color @o-tip-white-arrow-duration-in ease @o-tip-white-arrow-delay-in;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.top {
|
||||||
|
&::before {
|
||||||
|
bottom: -@o-tip-arrow-length;
|
||||||
|
border-top-color: @odoo-brand-primary;
|
||||||
|
}
|
||||||
|
&::after {
|
||||||
|
.o-position-absolute(@bottom: -@o-tip-arrow-length+@o-tip-border-width*2, @left: (@o-tip-size - @o-tip-arrow-width) / 2);
|
||||||
|
border-width: @o-tip-arrow-length @o-tip-arrow-width/2 0 @o-tip-arrow-width/2;
|
||||||
|
border-top-color: white;
|
||||||
|
transition: border-top-color @o-tip-white-arrow-duration-in ease @o-tip-white-arrow-delay-in;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.left {
|
||||||
|
&::before {
|
||||||
|
right: -@o-tip-arrow-length;
|
||||||
|
border-left-color: @odoo-brand-primary;
|
||||||
|
}
|
||||||
|
&::after {
|
||||||
|
.o-position-absolute(@right: -@o-tip-arrow-length+@o-tip-border-width*2, @top: (@o-tip-size - @o-tip-arrow-width) / 2);
|
||||||
|
border-width: @o-tip-arrow-width/2 0 @o-tip-arrow-width/2 @o-tip-arrow-length;
|
||||||
|
border-left-color: white;
|
||||||
|
transition: border-left-color @o-tip-white-arrow-duration-in ease @o-tip-white-arrow-delay-in;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.bottom {
|
||||||
|
&::before {
|
||||||
|
top: -@o-tip-arrow-length;
|
||||||
|
border-bottom-color: @odoo-brand-primary;
|
||||||
|
}
|
||||||
|
&::after {
|
||||||
|
.o-position-absolute(@top: -@o-tip-arrow-length+@o-tip-border-width*2, @left: (@o-tip-size - @o-tip-arrow-width) / 2);
|
||||||
|
border-width: 0 @o-tip-arrow-width/2 @o-tip-arrow-length @o-tip-arrow-width/2;
|
||||||
|
border-bottom-color: white;
|
||||||
|
transition: border-bottom-color @o-tip-white-arrow-duration-in ease @o-tip-white-arrow-delay-in;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.popover {
|
&.inverse {
|
||||||
border: 2px solid @odoo-brand-primary;
|
&.left, &.right {
|
||||||
&.right > .arrow {
|
&::before, &.active::after {
|
||||||
border-right-color: @odoo-brand-primary;
|
top: auto;
|
||||||
left: -13px;
|
bottom: (@o-tip-size - @o-tip-arrow-width) / 2;
|
||||||
&::after {
|
|
||||||
border-left-width: 2px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.bottom > .arrow {
|
&.top, &.bottom {
|
||||||
border-bottom-color: @odoo-brand-primary;
|
&::before, &.active::after {
|
||||||
top: -13px;
|
left: auto;
|
||||||
&::after {
|
right: (@o-tip-size - @o-tip-arrow-width) / 2;
|
||||||
border-top-width: 2px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.popover-content {
|
|
||||||
color: black;
|
|
||||||
text-align: justify;
|
|
||||||
.o_skip_tour {
|
|
||||||
display: inline-block;
|
|
||||||
margin-top: 5px;
|
|
||||||
cursor: pointer;
|
|
||||||
color: gray;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<templates id="template" xml:space="preserve">
|
<templates id="template" xml:space="preserve">
|
||||||
|
<div t-name="Tip" t-attf-class="o_tooltip #{widget.info.position}">
|
||||||
<t t-name="Tip">
|
<div class="o_tooltip_overlay"/>
|
||||||
<div class="o_tooltip">
|
<div class="o_tooltip_content" t-attf-style="width: #{widget.info.width}px;">
|
||||||
<div t-attf-class="o_breathing #{widget.info.position}"/>
|
<t t-raw="widget.info.content"/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</t>
|
|
||||||
|
|
||||||
</templates>
|
</templates>
|
||||||
|
|||||||
Reference in New Issue
Block a user