From 46e53879749be7ba3d30338d0f25c0a68a88eb3c Mon Sep 17 00:00:00 2001 From: "stefanorigano (SRI)" Date: Tue, 5 Jul 2022 12:27:28 +0000 Subject: [PATCH] [REF] website: multirange input This commit improve the design and simplify the implementation. Popovers have been removed and the overall integration with boostrap is increased allowing customization with the default boostrap variables. Variables for (multi-)range input have been exposed at "theme-level" allowing to customize these components when creating a new theme. task-2907955 Part-of: odoo/odoo#98558 --- .../scss/bootstrap_overridden_frontend.scss | 17 +++ .../lib/multirange/multirange_custom.js | 70 ++++------- .../lib/multirange/multirange_custom.scss | 112 ++++++++---------- 3 files changed, 89 insertions(+), 110 deletions(-) diff --git a/addons/web/static/src/legacy/scss/bootstrap_overridden_frontend.scss b/addons/web/static/src/legacy/scss/bootstrap_overridden_frontend.scss index 2ec7772b194..9f73810c4e4 100644 --- a/addons/web/static/src/legacy/scss/bootstrap_overridden_frontend.scss +++ b/addons/web/static/src/legacy/scss/bootstrap_overridden_frontend.scss @@ -5,11 +5,23 @@ // customization (eg. form-range) // gray-color-variables +$white: #fff !default; $gray-400: #ced4da !default; + +// color-variables +$blue: #0d6efd !default; + +// theme-color-variables +$primary: $blue !default; + // The contrast ratio to reach against white, to determine if color changes from "light" to "dark". Acceptable values for WCAG 2.0 are 3, 4.5 and 7. // See https://www.w3.org/TR/WCAG20/#visual-audio-contrast-contrast $min-contrast-ratio: 3 !default; +// Body + +$body-bg: $white !default; // BS Default + // Components // // Define common padding and border radius sizes and more. @@ -37,6 +49,11 @@ $input-transition: background-color .05s ease-in-out, border-color .05s ease-in- $form-check-input-border: $border-width solid $gray-400 !default; $form-check-input-border-radius: 0 !default; +$form-range-track-height: .1rem !default; +$form-range-thumb-border: $border-width solid $primary !default; +$form-range-thumb-bg: $body-bg !default; +$form-range-thumb-active-bg: $primary !default; + // Figures // Inherit color for figure-caption, use text-muted on top of it if you want diff --git a/addons/website/static/lib/multirange/multirange_custom.js b/addons/website/static/lib/multirange/multirange_custom.js index 55b2f791c42..3e65be64315 100644 --- a/addons/website/static/lib/multirange/multirange_custom.js +++ b/addons/website/static/lib/multirange/multirange_custom.js @@ -76,62 +76,43 @@ class Multirange { this.input.step = this.step = options.step || this.input.step || 1; this.currency = options.currency || this.input.dataset.currency || ''; this.currencyPosition = options.currencyPosition || this.input.dataset.currencyPosition || 'after'; + const inputClasses = ["multirange", "d-inline-block", "p-0", "align-top"]; /* Wrap the input and add its ghost */ this.rangeDiv = document.createElement("div"); - this.rangeDiv.classList.add("multirange-wrapper"); + this.rangeDiv.classList.add("multirange-wrapper", "position-relative", "mb-5"); + this.countersWrapper = document.createElement("small"); + this.countersWrapper.classList.add("d-flex", "justify-content-between", "mt-2"); + this.rangeDiv.appendChild(this.countersWrapper); this.input.parentNode.insertBefore(this.rangeDiv, this.input.nextSibling); this.rangeDiv.appendChild(this.input); this.ghost = this.input.cloneNode(); this.rangeDiv.appendChild(this.ghost); - this.input.classList.add("multirange", "original"); - this.ghost.classList.add("multirange", "ghost"); + this.input.classList.add("original", "position-absolute", "w-100", "m-0", ...inputClasses); + this.ghost.classList.add("ghost", "position-relative", ...inputClasses); this.input.value = values[0] || this.min; this.ghost.value = values[1] || this.max; - this.inputTipLocator = document.createElement("div"); - this.inputTipLocator.classList.add("tip-locator"); - this.ghostTipLocator = document.createElement("div"); - this.ghostTipLocator.classList.add("tip-locator"); - this.rangeDiv.insertBefore(this.ghostTipLocator, this.input.nextSibling); - this.rangeDiv.insertBefore(this.inputTipLocator, this.ghost.nextSibling); this.leftCounter = document.createElement("span"); - this.leftCounter.classList.add("multirange-min"); + this.leftCounter.classList.add("multirange-min", "position-absolute", "opacity-75", "opacity-100-hover", "mt-1"); this.rightCounter = document.createElement("span"); - this.rightCounter.classList.add("multirange-max"); - this.tipLocatorOptions = { - container: this.rangeDiv, - html: true, - }; - $(this.inputTipLocator).popover(Object.assign( - this.tipLocatorOptions, - { - placement: 'top', - content: this.leftCounter - }) - ); - $(this.ghostTipLocator).popover(Object.assign( - this.tipLocatorOptions, - { - placement: 'bottom', - content: this.rightCounter - }) - ); + this.rightCounter.classList.add("multirange-max", "position-absolute", "opacity-75", "opacity-100-hover", "mt-1"); + this.rightCounter.style.right = 0; + this.countersWrapper.append(this.leftCounter, this.rightCounter); - $(this.inputTipLocator).add($(this.ghostTipLocator)).popover('show'); /* Add the counterInput */ if (this.rangeWithInput) { this.leftInput = document.createElement("input"); this.leftInput.type = "number"; - this.leftInput.style.display = "none"; + this.leftInput.classList.add("invisible", "form-control", "form-control-sm", "w-auto", "mb-2", "mb-lg-1"); this.leftInput.min = this.min; this.leftInput.max = this.max; this.leftInput.step = this.step; this.rightInput = this.leftInput.cloneNode(); - this.leftInput.classList.add("multirange-min"); - this.rightInput.classList.add("multirange-max"); + this.leftInput.classList.add("multirange-min", "invisible"); + this.rightInput.classList.add("multirange-max", "invisible"); this.leftCounter.parentNode.appendChild(this.leftInput); this.rightCounter.parentNode.appendChild(this.rightInput); @@ -221,19 +202,9 @@ class Multirange { update() { const low = 100 * (this.input.valueLow - this.min) / (this.max - this.min); const high = 100 * (this.input.valueHigh - this.min) / (this.max - this.min); - const tipOffsetLow = 8 - (low * 0.15); - const tipOffsetHigh = 8 - (high * 0.15); + this.rangeDiv.style.setProperty("--low", low + '%'); this.rangeDiv.style.setProperty("--high", high + '%'); - $(this.inputTipLocator).css({ - 'left': `calc(${low}% + (${tipOffsetLow}px))`, - 'top': '3px' - }); - $(this.ghostTipLocator).css({ - 'left': `calc(${high}% + (${tipOffsetHigh}px))`, - 'top': '18px' - }); - $(this.inputTipLocator).add($(this.ghostTipLocator)).popover('update'); this.counterInputUpdate(); } @@ -253,16 +224,17 @@ class Multirange { counter = this.leftCounter; input = this.leftInput; } - if (counter.style.display === "none") { + + if (counter.classList.contains("invisible")) { this.input.valueLow = this.leftInput.value; this.input.valueHigh = this.rightInput.value; this.dispatchNewValueEvent(); this.update(); - counter.style.display = ""; - input.style.display = "none"; + counter.classList.remove("invisible"); + input.classList.add("invisible"); } else { - counter.style.display = "none"; - input.style.display = ""; + counter.classList.add("invisible"); + input.classList.remove("invisible"); this.saveOldValues(); // Hack because firefox: https://bugzilla.mozilla.org/show_bug.cgi?id=1057858 window.setTimeout(function () { diff --git a/addons/website/static/lib/multirange/multirange_custom.scss b/addons/website/static/lib/multirange/multirange_custom.scss index c170b0f2724..ae34ad43a2d 100644 --- a/addons/website/static/lib/multirange/multirange_custom.scss +++ b/addons/website/static/lib/multirange/multirange_custom.scss @@ -2,84 +2,74 @@ // which can be found on https://github.com/LeaVerou/multirange. // The license file can be found in the same folder as this file. - input[type="range"][multiple] { +input[type="range"][multiple] { pointer-events: none; visibility: hidden; +} - &::-webkit-slider-thumb { - pointer-events: auto; - } - - &::-moz-range-thumb { - pointer-events: auto; - } - - &.reverse { - direction: rtl; - } - - &.multirange { - width: 100%; - padding: 0; - margin: 0; - display: inline-block; - vertical-align: top; - +.multirange-wrapper { + input[type="range"][multiple] { + visibility: initial; &::-webkit-slider-thumb { - height: 16px; - width: 16px; + pointer-events: auto; } - &.original { - position: absolute; + &::-moz-range-thumb { + pointer-events: auto; + } - &::-webkit-slider-thumb { + &.reverse { + direction: rtl; + } + + &.multirange { + &::-moz-range-track { + border-color: transparent; /* needed to switch Firefox to "styleable" control */ + } + + &.original::-webkit-slider-thumb { position: relative; z-index: 2; } - &::-moz-range-thumb { - transform: scale(1); /* Firefox doesn't apply position it seems */ - z-index: 1; + &.ghost { + + // Workaround to handle Firefox's lack of support of z-index rules + // for #shadow-root elements, see '&.original::-webkit-slider-thumb' + // rules. The entire @media query could be removed once Firefox + // will support the feature. + @media all and (min--moz-device-pixel-ratio:0) and (min-resolution: .001dpcm) { + + // Helper function to retrive the 'width' segment from a 'border' shorthand rule. + @function -get-border-width($input) { + @each $part in $input { + @if type-of($part) == number { + @return $part; + } + } + @return null; + } + + $-thumb-border-width: #{-get-border-width($form-range-thumb-border)}; + $-ghostGap: calc(#{$form-range-thumb-width} + (#{$-thumb-border-width} * 2)); + + margin-inline-start: $-ghostGap; + width: calc(100% - #{$-ghostGap}); + } + + &::-webkit-slider-runnable-track { + background: linear-gradient(to right, transparent var(--low), var(--primary) var(--low) var(--high), transparent 0) no-repeat 50% / 100% 100%; + } + + &::-moz-range-track { + background: linear-gradient(to right, transparent var(--low), var(--primary) var(--low) var(--high), transparent 0) no-repeat 50% / 100% 100%; + } } } - - &::-moz-range-track { - border-color: transparent; /* needed to switch Firefox to "styleable" control */ - } - - &.ghost { - border-radius: 45%; - position: relative; - background: var(--track-background); - --track-background: linear-gradient(to right, transparent var(--low), #{theme-color('primary')} var(--low) var(--high), transparent 0) no-repeat 50% / 100% 35%; - - &::-webkit-slider-runnable-track { - background: var(--track-background); - } - - &::-moz-range-track { - background: var(--track-background); - } - } - } -} - -.multirange-wrapper { - position: relative; - margin: 3rem 0; - visibility: hidden; - - input[type="range"][multiple] { - visibility: initial; } [x-out-of-boundaries] { display: none; } } - -.tip-locator { - position: absolute; -}