diff options
| -rw-r--r-- | addons/web_tour/static/src/js/tip.js | 22 | ||||
| -rw-r--r-- | addons/web_tour/static/src/scss/keyframes.scss | 50 | ||||
| -rw-r--r-- | addons/web_tour/static/src/scss/tip.scss | 238 |
3 files changed, 121 insertions, 189 deletions
diff --git a/addons/web_tour/static/src/js/tip.js b/addons/web_tour/static/src/js/tip.js index c7900ac5dd2..fc6598a39e6 100644 --- a/addons/web_tour/static/src/js/tip.js +++ b/addons/web_tour/static/src/js/tip.js @@ -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,9 @@ 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.double_border_width = 0; // TODO remove me in master 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 +99,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 +327,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 +438,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 +483,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'); diff --git a/addons/web_tour/static/src/scss/keyframes.scss b/addons/web_tour/static/src/scss/keyframes.scss index 0e7bb598fb2..74f3bbf07b2 100644 --- a/addons/web_tour/static/src/scss/keyframes.scss +++ b/addons/web_tour/static/src/scss/keyframes.scss @@ -1,49 +1 @@ -@-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); - } -} +// TODO remove in master diff --git a/addons/web_tour/static/src/scss/tip.scss b/addons/web_tour/static/src/scss/tip.scss index 680b76229b6..14413423cd7 100644 --- a/addons/web_tour/static/src/scss/tip.scss +++ b/addons/web_tour/static/src/scss/tip.scss @@ -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*/ + z-index: $zindex-tooltip !important; // See comment on 'o_tooltip_parent' class opacity: 0 !important; - width: $o-tip-size !important; - height: $o-tip-size !important; + width: $o-tip-width !important; + height: $o-tip-width !important; // the shape must be done using transform 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; + 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; - } - &.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; + @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 { - /*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; + transform: rotate(180deg) translateY($-tip-translate) scaleY($-tip-scale) rotate(45deg); } - &.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; + &.right::before { + 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 { + transform: translateY($-tip-translate) !important; + &::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); + @include o-position-absolute($top: -$o-tip-arrow-size, $left: $-arrow-offset); /*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(50%) rotate(45deg); } } - } - - &.inverse { - &.left, &.right { - &::before, &.active::after { - top: auto; - bottom: ($o-tip-size - $o-tip-arrow-width) / 2; + &.inverse { + &.left, &.right { + &::before { + top: auto; + bottom: $-arrow-offset; + } } - } - &.top, &.bottom { - &::before, &.active::after { - left: auto#{"/*rtl:ignore*/"}; - right: ($o-tip-size - $o-tip-arrow-width) / 2#{"/*rtl:ignore*/"}; + &.top, &.bottom { + &::before { + left: auto#{"/*rtl:ignore*/"}; + right: $-arrow-offset#{"/*rtl:ignore*/"}; + } } } } |
