diff --git a/src/components/range/style.scss b/src/components/range/style.scss index 7fbc05e8..ffe0d54e 100644 --- a/src/components/range/style.scss +++ b/src/components/range/style.scss @@ -43,7 +43,6 @@ color: #000; } - // Remove the number controls -moz-appearance: textfield; diff --git a/src/custom-els/RangeInput/index.ts b/src/custom-els/RangeInput/index.ts index 58ed63b5..5759bedb 100644 --- a/src/custom-els/RangeInput/index.ts +++ b/src/custom-els/RangeInput/index.ts @@ -1,5 +1,5 @@ import { bind } from '../../lib/initial-util'; -import './styles.css'; +import * as style from './styles.css'; const RETARGETED_EVENTS = ['focus', 'blur']; const UPDATE_EVENTS = ['input', 'change']; @@ -7,9 +7,8 @@ const REFLECTED_PROPERTIES = ['name', 'min', 'max', 'step', 'value', 'disabled'] const REFLECTED_ATTRIBUTES = ['name', 'min', 'max', 'step', 'value', 'disabled']; class RangeInputElement extends HTMLElement { - private _input = document.createElement('input'); - private _valueDisplayWrapper = document.createElement('div'); - private _valueDisplay = document.createElement('span'); + private _input: HTMLInputElement; + private _valueDisplay?: HTMLDivElement; private _ignoreChange = false; static get observedAttributes() { @@ -18,7 +17,9 @@ class RangeInputElement extends HTMLElement { constructor() { super(); + this._input = document.createElement('input'); this._input.type = 'range'; + this._input.className = style.input; for (const event of RETARGETED_EVENTS) { this._input.addEventListener(event, this._retargetEvent, true); @@ -29,6 +30,18 @@ class RangeInputElement extends HTMLElement { } } + connectedCallback() { + if (this.contains(this._input)) return; + this.innerHTML = + `
` + + `
` + + `
` + + '
'; + + this.insertBefore(this._input, this.firstChild); + this._valueDisplay = this.querySelector('.' + style.valueDisplay) as HTMLDivElement; + } + get labelPrecision(): string { return this.getAttribute('label-precision') || ''; } @@ -37,14 +50,6 @@ class RangeInputElement extends HTMLElement { this.setAttribute('label-precision', precision); } - connectedCallback() { - if (this._input.parentNode !== this) { - this.appendChild(this._input); - this._valueDisplayWrapper.appendChild(this._valueDisplay); - this.appendChild(this._valueDisplayWrapper); - } - } - attributeChangedCallback(name: string, oldValue: string, newValue: string | null) { if (this._ignoreChange) return; if (newValue === null) { @@ -65,15 +70,15 @@ class RangeInputElement extends HTMLElement { @bind private _update() { - const value = parseFloat(this.value || '0'); - const min = parseFloat(this.min || '0'); - const max = parseFloat(this.max || '100'); - const labelPrecision = parseFloat(this.labelPrecision || '0'); + const value = Number(this.value) || 0; + const min = Number(this.min) || 0; + const max = Number(this.max) || 100; + const labelPrecision = Number(this.labelPrecision) || 0; const percent = 100 * (value - min) / (max - min); const displayValue = labelPrecision ? value.toPrecision(labelPrecision) : Math.round(value).toString(); - this._valueDisplay.textContent = displayValue; + this._valueDisplay!.textContent = displayValue; this.style.setProperty('--value-percent', percent + '%'); this.style.setProperty('--value-width', '' + displayValue.length); } diff --git a/src/custom-els/RangeInput/styles.css b/src/custom-els/RangeInput/styles.css index c0397d80..f48483b3 100644 --- a/src/custom-els/RangeInput/styles.css +++ b/src/custom-els/RangeInput/styles.css @@ -14,41 +14,6 @@ range-input[disabled] { filter: grayscale(1); } -/* Reversed Variant */ -range-input[reversed] input, -range-input[reversed]::before, -range-input[reversed] > div { - transform: scaleX(-1); -} -range-input[reversed] > div > span { - transform: scaleX(-1) scale(.2); -} -range-input[reversed] input:focus + div span { - transform: scaleX(-1) scale(1); -} - - -range-input input { - position: relative; - flex: 1; - vertical-align: middle; - width: 100%; - padding: 0 !important; - margin: 0 !important; - background: none; - appearance: none; - -webkit-appearance: none; - outline: none; -} -range-input input::-webkit-slider-runnable-track, -range-input input::-moz-range-track, -range-input input::-ms-track { - appearance: none; - -ms-appearance: none; - -moz-appearance: none; - -webkit-appearance: none; -} - range-input::before { content: ''; display: block; @@ -57,32 +22,33 @@ range-input::before { left: 0; width: 100%; height: 2px; - outline: none; - border: none; - background: #eee; border-radius: 1px; box-shadow: 0 -.5px 0 rgba(0,0,0,0.3), inset 0 .5px 0 rgba(255,255,255,0.2), 0 .5px 0 rgba(255,255,255,0.3); background: linear-gradient(#34B9EB, #218ab1) 0/ var(--value-percent, 0%) 100% no-repeat #eee; } -range-input input::-webkit-slider-thumb { - appearance: none; - -webkit-appearance: none; - background: url('data:image/svg+xml,') center no-repeat #34B9EB; - box-sizing: content-box; +.input { + position: relative; + width: 100%; + padding: 0; + margin: 0; + opacity: 0; +} + +.thumb { + pointer-events: none; + position: absolute; + bottom: 3px; + left: var(--value-percent, 0%); + margin-left: -6px; + background: url('data:image/svg+xml,') center no-repeat #34B9EB; border-radius: 50%; width: 12px; height: 12px; - border: none; box-shadow: 0 0.5px 2px rgba(0,0,0,0.3); - outline: none; } -range-input input:focus::-webkit-slider-thumb { - box-shadow: 0 1px 3px rgba(0,0,0,0.5); -} - -range-input > div { +.thumb-wrapper { position: absolute; left: 6px; right: 6px; @@ -91,8 +57,8 @@ range-input > div { overflow: visible; } -range-input > div > span { - background: url('data:image/svg+xml,') top center no-repeat; +.value-display { + background: url('data:image/svg+xml,') top center no-repeat; position: absolute; box-sizing: border-box; left: var(--value-percent, 0%); @@ -111,12 +77,18 @@ range-input > div > span { font-size: calc(100% - var(--value-width, 3) / 5 * .2em); text-overflow: clip; text-shadow: 0 -.5px 0 rgba(0,0,0,0.4); - transition: transform 200ms ease, opacity 200ms ease; + transition: all 200ms ease; + transition-property: opacity, transform; will-change: transform; pointer-events: none; overflow: hidden; } -range-input input:focus + div span { + +.input:active + .thumb-wrapper .value-display { opacity: 1; transform: scale(1); } + +.input:active + .thumb-wrapper .thumb { + box-shadow: 0 1px 3px rgba(0,0,0,0.5); +}