diff --git a/blocks/form/components/range/range.css b/blocks/form/components/range/range.css new file mode 100644 index 0000000..00671e1 --- /dev/null +++ b/blocks/form/components/range/range.css @@ -0,0 +1,51 @@ +/** styling for range */ +main .form .range-wrapper.decorated input[type="range"] { + margin: unset; + padding: unset; + appearance: none; + height: 5px; + border-radius: 5px; + border: none; + background-image: linear-gradient(to right, var(--button-primary-color) calc(100% * var(--current-steps)/var(--total-steps)), #C5C5C5 calc(100% * var(--current-steps)/var(--total-steps))); +} + +main .form .range-wrapper.decorated input[type="range"]:focus { + outline: none; +} + +.range-wrapper.decorated input[type="range"]::-webkit-slider-thumb { + appearance: none; + width: 25px; + height: 25px; + border-radius: 50%; + background: #fff; + border: 3px solid var(--button-primary-color); + cursor: pointer; + outline: 3px solid #fff; + } + +.range-wrapper.decorated input[type="range"]:focus::-webkit-slider-thumb { + border-color: var(--button-primary-color); +} + +.range-wrapper.decorated .range-bubble { + color: #17252e; + font-size: 20px; + line-height: 28px; + position: relative; + display: inline-block; + padding-bottom: 12px; +} + +.range-wrapper.decorated .range-min, +.range-wrapper.decorated .range-max { + font-size: 14px; + line-height: 22px; + color: #494f50; + margin-top: 16px; + display: inline-block; +} + +.range-wrapper.decorated .range-max { + float: right; +} \ No newline at end of file diff --git a/blocks/form/components/range/range.js b/blocks/form/components/range/range.js new file mode 100644 index 0000000..a691d15 --- /dev/null +++ b/blocks/form/components/range/range.js @@ -0,0 +1,56 @@ +import { loadCSS } from "../../../../scripts/aem"; + + +function updateBubble(input, element) { + const step = input.step || 1; + const max = input.max || 0; + const min = input.min || 1; + const value = input.value || 1; + const current = Math.ceil((value - min) / step); + const total = Math.ceil((max - min) / step); + const bubble = element.querySelector('.range-bubble'); + // during initial render the width is 0. Hence using a default here. + const bubbleWidth = bubble.getBoundingClientRect().width || 31; + const left = `${(current / total) * 100}% - ${(current / total) * bubbleWidth}px`; + bubble.innerText = `${value}`; + const steps = { + '--total-steps': Math.ceil((max - min) / step), + '--current-steps': Math.ceil((value - min) / step), + }; + const style = Object.entries(steps).map(([varName, varValue]) => `${varName}:${varValue}`).join(';'); + bubble.style.left = `calc(${left})`; + element.setAttribute('style', style); +} + + +export default function decorateRange(fieldDiv, field) { + loadCSS('/blocks/form/components/range/range.css'); + const input = fieldDiv.querySelector('input'); + // modify the type in case it is not range. + input.type = 'range'; + input.min = input.min || 1; + // create a wrapper div to provide the min/max and current value + const div = document.createElement('div'); + div.className = 'range-widget-wrapper'; + input.after(div); + const hover = document.createElement('span'); + hover.className = 'range-bubble'; + const rangeMinEl = document.createElement('span'); + rangeMinEl.className = 'range-min'; + const rangeMaxEl = document.createElement('span'); + rangeMaxEl.className = 'range-max'; + rangeMinEl.innerText = `${input.min || 1}`; + rangeMaxEl.innerText = `${input.max}`; + div.appendChild(hover); + // move the input element within the wrapper div + div.appendChild(input); + div.appendChild(rangeMinEl); + div.appendChild(rangeMaxEl); + input.addEventListener('input', (e) => { + updateBubble(e.target, div); + }); + updateBubble(input, div); + // as a best practice add a custom css class to apply custom styling + fieldDiv.classList.add('decorated'); + return fieldDiv; +} diff --git a/blocks/form/mappings.js b/blocks/form/mappings.js index c161ea3..c252572 100644 --- a/blocks/form/mappings.js +++ b/blocks/form/mappings.js @@ -1,4 +1,3 @@ -import decorateRange from './components/range/range.js'; /** * returns a decorator to decorate the field definition @@ -15,7 +14,8 @@ export default async function componentDecorator(fd) { return module.default; } if (fd.properties?.edsType === 'range') { - return decorateRange; + const module = await import('./components/range/range.js'); + return module.default; } return null; } diff --git a/styles/styles.css b/styles/styles.css index 5341913..d6eca7b 100644 --- a/styles/styles.css +++ b/styles/styles.css @@ -10,8 +10,6 @@ * governing permissions and limitations under the License. */ - @import url('/blocks/form/components/range/range.css'); - :root { /* colors */ --link-color: #035fe6;