[IMP] web_tour: review tour tips design

We noticed users may have difficulties noticing where the tip is in the
page, even when searching explicitly for it. This commit reviews the
design and improves the scss: the tip shape is now made of an unique
transformed element with a white border all around. The box shadow was
also made more visible. The visibility over white elements stays the
same as before but it is now way more visible over dark content (like
the website editor panel).

task-2357170

closes odoo/odoo#61731

X-original-commit: dbf966f6f012ccf5aa9b501586e2aa0fde05be5d
Related: odoo/enterprise#14735
Signed-off-by: Quentin Smetz (qsm) <qsm@odoo.com>
This commit is contained in:
qsm-odoo
2020-11-13 16:55:39 +00:00
parent 6f1405b59d
commit f8df10dd01
4 changed files with 124 additions and 195 deletions
+10 -11
View File
@@ -26,7 +26,8 @@ var Tip = Widget.extend({
* - 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
* - space [int] space in px between anchor and tip, default to 0, added to
* the natural space chosen in css
* - hidden [boolean] if true, the tip won't be visible (but the handlers will still be
* bound on the anchor, so that the tip is consumed if the user clicks on it)
* - overlay [Object] x and y values for the number of pixels the mouseout detection area
@@ -37,7 +38,7 @@ var Tip = Widget.extend({
this.info = _.defaults(info, {
position: "right",
width: 270,
space: 10,
space: 0,
overlay: {
x: 50,
y: 50,
@@ -74,9 +75,8 @@ var Tip = Widget.extend({
start() {
this.$tooltip_overlay = this.$(".o_tooltip_overlay");
this.$tooltip_content = this.$(".o_tooltip_content");
this.init_width = this.$el.innerWidth();
this.init_height = this.$el.innerHeight();
this.double_border_width = this.$el.outerWidth() - this.init_width;
this.init_width = this.$el.outerWidth();
this.init_height = this.$el.outerHeight();
this.content_width = this.$tooltip_content.outerWidth(true);
this.content_height = this.$tooltip_content.outerHeight(true);
this.$tooltip_content.html(this.info.scrollContent);
@@ -98,7 +98,7 @@ var Tip = Widget.extend({
this._updatePosition(true);
this.$el.toggleClass('d-none', !!this.info.hidden);
this.el.style.setProperty('opacity', '1', 'important');
this.el.classList.add('o_tooltip_visible');
core.bus.on("resize", this, _.debounce(function () {
if (this.tip_opened) {
this._to_bubble_mode(true);
@@ -326,8 +326,8 @@ var Tip = Widget.extend({
const offset = {top: this.$el[0].offsetTop, left: this.$el[0].offsetLeft};
this.$tooltip_overlay.css({
top: -Math.min((this.info.position === "bottom" ? this.info.space : this.info.overlay.y), offset.top),
right: -Math.min((this.info.position === positionRight ? this.info.space : this.info.overlay.x), this.$window.width() - (offset.left + this.init_width + this.double_border_width)),
bottom: -Math.min((this.info.position === "top" ? this.info.space : this.info.overlay.y), this.$window.height() - (offset.top + this.init_height + this.double_border_width)),
right: -Math.min((this.info.position === positionRight ? this.info.space : this.info.overlay.x), this.$window.width() - (offset.left + this.init_width)),
bottom: -Math.min((this.info.position === "top" ? this.info.space : this.info.overlay.y), this.$window.height() - (offset.top + this.init_height)),
left: -Math.min((this.info.position === positionLeft ? this.info.space : this.info.overlay.x), offset.left),
});
this.position = offset;
@@ -437,9 +437,9 @@ var Tip = Widget.extend({
var overflow = false;
var posVertical = (this.info.position === "top" || this.info.position === "bottom");
if (posVertical) {
overflow = (offset.left + this.content_width + this.double_border_width + this.info.overlay.x > this.$window.width());
overflow = (offset.left + this.content_width + this.info.overlay.x > this.$window.width());
} else {
overflow = (offset.top + this.content_height + this.double_border_width + this.info.overlay.y > this.$window.height());
overflow = (offset.top + this.content_height + this.info.overlay.y > this.$window.height());
}
if (posVertical && overflow || this.info.position === "left" || (_t.database.parameters.direction === 'rtl' && this.info.position == "right")) {
mbLeft -= (this.content_width - this.init_width);
@@ -482,7 +482,6 @@ var Tip = Widget.extend({
this.timerOut = undefined;
this.tip_opened = false;
this.$el.removeClass("active").addClass("o_animated");
this.el.style.setProperty('width', `${this.init_width}px`, 'important');
this.el.style.setProperty('height', `${this.init_height}px`, 'important');
@@ -1,49 +0,0 @@
@-webkit-keyframes move-left-right {
0% {
transform: translate(-3px, 0);
}
100% {
transform: translate(3px, 0);
}
}
@-moz-keyframes move-left-right {
0% {
transform: translate(-3px, 0);
}
100% {
transform: translate(3px, 0);
}
}
@keyframes move-left-right {
0% {
transform: translate(-3px, 0);
}
100% {
transform: translate(3px, 0);
}
}
@-webkit-keyframes move-bottom-top {
0% {
transform: translate(0, -3px);
}
100% {
transform: translate(0, 3px);
}
}
@-moz-keyframes move-bottom-top {
0% {
transform: translate(0px, -3px);
}
100% {
transform: translate(0, 3px);
}
}
@keyframes move-bottom-top {
0% {
transform: translate(0px, -3px);
}
100% {
transform: translate(0, 3px);
}
}
+114 -134
View File
@@ -1,3 +1,32 @@
$o-tip-width: 28px;
$o-tip-height: 38px;
$o-tip-anchor-space: 3px;
$o-tip-color: $o-enterprise-color;
$o-tip-border-width: 3px;
$o-tip-border-color: white;
$o-tip-animation-speed: 500ms;
$o-tip-arrow-size: 12px;
$o-tip-duration-in: 200ms;
$o-tip-size-duration-in: floor($o-tip-duration-in * 3 / 4);
$o-tip-size-delay-in: $o-tip-duration-in - $o-tip-size-duration-in;
@keyframes move-left-right {
0% {
transform: translate(-$o-tip-anchor-space, 0);
}
100% {
transform: translate($o-tip-anchor-space, 0);
}
}
@keyframes move-bottom-top {
0% {
transform: translate(0, -$o-tip-anchor-space);
}
100% {
transform: translate(0, $o-tip-anchor-space);
}
}
.o_tooltip_parent {
position: relative !important;
@@ -21,118 +50,82 @@
}
.o_tooltip {
$o-tip-color: $o-brand-odoo;
$o-tip-size: 24px;
$o-tip-border-width: 2px;
$o-tip-arrow-length: 10px;
$o-tip-arrow-width: 20px;
$o-tip-arrow-integration: 2px;
$o-tip-duration-in: 200ms;
$o-tip-size-duration-in: floor($o-tip-duration-in * 3 / 4);
$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: 150ms;
$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-arrow-duration-out: floor($o-tip-duration-out / 2);
$o-tip-arrow-delay-out: floor($o-tip-duration-out / 2);
box-sizing: content-box !important;
/*rtl:begin:ignore*/
position: absolute !important;
top: 50% !important;
left: 50% !important;
/*rtl:end:ignore*/
opacity: 0 !important;
width: $o-tip-size !important;
height: $o-tip-size !important;
margin: 0 !important;
border: $o-tip-border-width solid $o-tip-color !important;
border-radius: 50% !important;
padding: 0 !important; // ! Cannot be changed ! Javascript rely on this.
z-index: $zindex-tooltip !important; // See comment on 'o_tooltip_parent' class
background-color: $o-tip-color !important;
box-shadow: 0 2px 2px 1px rgba(150, 150, 150, 0.3) !important;
opacity: 0 !important;
width: $o-tip-width !important;
height: $o-tip-width !important; // the shape must be done using transform
margin: 0 !important;
padding: 0 !important;
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 !important;
transition: opacity 400ms ease 0ms !important;
&.o_animated {
animation: move-bottom-top 500ms ease-in 0ms infinite alternate !important;
animation: move-bottom-top $o-tip-animation-speed ease-in 0ms infinite alternate !important;
&.right, &.left {
animation-name: move-left-right !important;
}
}
&.o_tooltip_visible {
opacity: 1 !important;
}
&.o_tooltip_fixed {
position: fixed !important;
}
// Use the ::before element to make the tip shape: a simple filled and
// bordered square with one corner and 3 rounded corners, then transformed.
// Transform, from right to left: 1) make the arrow point up, 2) scale along
// Y axis so that the tip reach the desired height, 3) translate along the Y
// axis so that the arrow exactly points at the original square tip border
// = the border that will be against the pointed element, 4) rotate the
// the shape depending on the tip orientation.
&::before {
content: "";
border: 0 solid rgba(0, 0, 0, 0);
transition: all $o-tip-arrow-duration-out ease $o-tip-arrow-delay-out;
@include o-position-absolute(0, 0);
width: $o-tip-width; // Not 100% need to stay small and square for close transition
height: $o-tip-width;
border: $o-tip-border-width solid $o-tip-border-color;
border-radius: 0 50% 50% 50%;
background: radial-gradient(lighten($o-tip-color, 7%), $o-tip-color);
box-shadow: 0 0 40px 2px rgba(255, 255, 255, 0.5);
}
$-sqrt-2: 1.4142;
$-tip-scale: $o-tip-height / ((1 + $-sqrt-2) * $o-tip-width / 2);
$-tip-overflow: ($-sqrt-2 * $-tip-scale - 1) * $o-tip-width / 2;
$-tip-translate: $o-tip-anchor-space + $-tip-overflow;
&.top::before {
transform: rotate(180deg) translateY($-tip-translate) scaleY($-tip-scale) rotate(45deg);
}
&.right::before {
@include 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-color;
}
&.top::before {
/*rtl:begin:ignore*/
@include o-position-absolute($bottom: -$o-tip-arrow-length+$o-tip-arrow-integration, $left: ($o-tip-size - $o-tip-arrow-width) / 2);
/*rtl:end:ignore*/
border-width: $o-tip-arrow-length $o-tip-arrow-width/2 0 $o-tip-arrow-width/2;
border-top-color: $o-tip-color;
}
&.left::before {
@include 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-color;
transform: rotate(270deg) translateY($-tip-translate) scaleY($-tip-scale) rotate(45deg);
}
&.bottom::before {
/*rtl:begin:ignore*/
@include o-position-absolute($top: -$o-tip-arrow-length+$o-tip-arrow-integration, $left: ($o-tip-size - $o-tip-arrow-width) / 2);
/*rtl:end:ignore*/
border-width: 0 $o-tip-arrow-width/2 $o-tip-arrow-length $o-tip-arrow-width/2;
border-bottom-color: $o-tip-color;
transform: rotate(0deg) translateY($-tip-translate) scaleY($-tip-scale) rotate(45deg);
}
&::after {
content: "";
@include o-position-absolute(-$o-tip-border-width, -$o-tip-border-width, -$o-tip-border-width, -$o-tip-border-width);
border-color: $o-tip-color;
border-radius: 50%;
transition: margin $o-tip-size-duration-out ease $o-tip-size-delay-out, border-radius $o-tip-duration-out linear 0s;
background: radial-gradient(lighten($o-tip-color, 7%), $o-tip-color);
&.left::before {
transform: rotate(90deg) translateY($-tip-translate) scaleY($-tip-scale) rotate(45deg);
}
> .o_tooltip_overlay {
display: none;
@include o-position-absolute(0, 0, 0, 0);
margin: -$o-tip-border-width;
z-index: -1;
}
> .o_tooltip_content {
overflow: hidden;
padding: floor($o-tip-size/4) floor($o-tip-size/2);
direction: ltr;
position: relative;
padding: 7px 14px;
background-color: inherit;
color: transparent;
visibility: hidden;
transition: visibility 0s ease 0s;
color: black;
// Force style so that it does not depend on where the tooltip is attached
line-height: $line-height-base;
@@ -155,98 +148,85 @@
}
&.active {
border-radius: 0 !important;
border: $o-tip-border-width solid $o-tip-color !important;
background-color: white !important;
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 !important;
margin $o-tip-size-duration-in ease $o-tip-size-delay-in !important;
&::before {
transition: all $o-tip-arrow-duration-in ease $o-tip-arrow-delay-in;
}
&::after {
border: 0 solid rgba(0, 0, 0, 0);
width: $o-tip-arrow-size;
height: $o-tip-arrow-size;
border-color: $o-tip-color;
border-radius: 0;
background: white;
box-shadow: none;
}
> .o_tooltip_overlay {
display: block;
}
> .o_tooltip_content {
direction: ltr;
// Content background must appear immediately to hide the bottom of
// the square present to shape the bubble arrow. But text must
// appear at the very end.
color: black;
visibility: visible;
transition: visibility 0s ease $o-tip-duration-in;
transition: color 0ms ease $o-tip-duration-in;
}
$-arrow-offset: ($o-tip-width - $o-tip-arrow-size) / 2 - $o-tip-border-width;
$-tip-translate: $o-tip-anchor-space + $o-tip-arrow-size / 2;
&.right {
transform: translateX($-tip-translate) !important;
&::before {
left: -$o-tip-arrow-length;
border-right-color: $o-tip-color;
}
&::after {
@include 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;
@include o-position-absolute($left: -$o-tip-arrow-size, $top: $-arrow-offset);
transform: translateX(50%) rotate(45deg);
}
}
&.top {
transform: translateY(-$-tip-translate) !important;
&::before {
bottom: -$o-tip-arrow-length;
border-top-color: $o-tip-color;
}
&::after {
/*rtl:begin:ignore*/
@include o-position-absolute($bottom: -$o-tip-arrow-length+$o-tip-border-width*2, $left: ($o-tip-size - $o-tip-arrow-width) / 2);
@include o-position-absolute($bottom: -$o-tip-arrow-size, $left: $-arrow-offset);
/*rtl:end:ignore*/
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;
transform: translateY(-50%) rotate(45deg);
}
}
&.left {
transform: translateX(-$-tip-translate) !important;
&::before {
right: -$o-tip-arrow-length;
border-left-color: $o-tip-color;
}
&::after {
@include 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;
@include o-position-absolute($right: -$o-tip-arrow-size, $top: $-arrow-offset);
transform: translateX(-50%) rotate(45deg);
}
}
&.bottom {
&::before {
top: -$o-tip-arrow-length;
border-bottom-color: $o-tip-color;
}
&::after {
/*rtl:begin:ignore*/
@include o-position-absolute($top: -$o-tip-arrow-length+$o-tip-border-width*2, $left: ($o-tip-size - $o-tip-arrow-width) / 2);
/*rtl:end:ignore*/
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;
}
}
}
transform: translateY($-tip-translate) !important;
&.inverse {
&.left, &.right {
&::before, &.active::after {
top: auto;
bottom: ($o-tip-size - $o-tip-arrow-width) / 2;
&::before {
/*rtl:begin:ignore*/
@include o-position-absolute($top: -$o-tip-arrow-size, $left: $-arrow-offset);
/*rtl:end:ignore*/
transform: translateY(50%) rotate(45deg);
}
}
&.top, &.bottom {
&::before, &.active::after {
left: auto#{"/*rtl:ignore*/"};
right: ($o-tip-size - $o-tip-arrow-width) / 2#{"/*rtl:ignore*/"};
&.inverse {
&.left, &.right {
&::before {
top: auto;
bottom: $-arrow-offset;
}
}
&.top, &.bottom {
&::before {
left: auto#{"/*rtl:ignore*/"};
right: $-arrow-offset#{"/*rtl:ignore*/"};
}
}
}
}
-1
View File
@@ -4,7 +4,6 @@
<template id="assets_common" name="tours assets" inherit_id="web.assets_common">
<xpath expr="." position="inside">
<link rel="stylesheet" type="text/scss" href="/web_tour/static/src/scss/tip.scss"/>
<link rel="stylesheet" type="text/scss" href="/web_tour/static/src/scss/keyframes.scss"/>
<script type="text/javascript" src="/web_tour/static/src/js/tip.js"></script>
<script type="text/javascript" src="/web_tour/static/src/js/tour_utils.js"></script>
<script type="text/javascript" src="/web_tour/static/src/js/running_tour_action_helper.js"></script>