UNPKG

monphind-ui

Version:

A reactive component library built on top of the Web Components API

201 lines (200 loc) 6.89 kB
import { useElement } from "./core/element"; const name = 'm-slider'; const style = `:host { display: flex; flex-direction: column; justify-content: start; align-items: start; width: 300px; height: 40px; background-color: var(--m-slider-backgroundColor,${"#EFEFEF"}); border-radius: 12px; position: relative; overflow:hidden; cursor:pointer; } :host p.hidden-value { position: absolute; top: 0; bottom: 0; left: 0; right: 0; margin: auto; width: -moz-fit-content; width: fit-content; height: -moz-fit-content; height: fit-content; font-size: 13px; color: var(--m-slider-hiddenlabel-color,${"gray"}); pointer-events: none; display: none; } :host p.value { position: absolute; width: -moz-fit-content; width: fit-content; left: 0; bottom: 0; top: 0; right: 0; height: -moz-fit-content; height: fit-content; font-size: 13px; color: var(--m-slider-label-color,${"rgb(255,255,255)"}); margin: auto; pointer-events: none; display: none; } :host[labeled=true] p.value, :host[labeled=true] p.hidden-value { display: initial; } :host .slider { width: 0%; height: 100%; border-radius: 12px; background-color: var(--m-slider-slider-backgroundColor,${"#2EA2F9"}); position: relative; } :host .slider .slider-bar { position: absolute; right: 5px; top: 0; bottom: 0; margin: auto; width: 2px; border-radius: 100px; height: 15px; background-color: white; } :host([disabled=true]) { cursor: default; pointer-events:none; } :host([disabled=true]) .slider-bar { display: none; }`; const template = ` <div class="slider"> <p class="value"></p> <div class="slider-bar"></div> </div> <p class="hidden-value"></p>`; const props = { disabled: false, labeled: false, min: 0, max: 100, value: 0, step: 1 }; const displayValue = (displayValueEles, min, max, value, labeled) => { if (!labeled) return; const centerValue = (max - min) * 0.5 + min; displayValueEles.hiddened.textContent = `${value}/${max}`; displayValueEles.normal.textContent = `${value}/${max}`; if (value < centerValue) { displayValueEles.normal.style.display = "none"; displayValueEles.hiddened.style.display = "initial"; } else { displayValueEles.hiddened.style.display = "none"; displayValueEles.normal.style.display = "initial"; } }; export class Slider extends useElement({ name, style, template, props, syncProps: [ "disabled", "labeled", "value", "min", "max", "step" ], dispatch: { propChanged(key, value) { if (key === "value") { let v = Number(value); if (v < this.min) v = this.min; if (v > this.max) v = this.max; const rst = (v - this.min) / (this.max - this.min) * 100; const ele = this.shadowRoot?.querySelector(".slider"); ele.style.width = `${rst}%`; const displayValueEles = { hiddened: this.shadowRoot?.querySelector(".hidden-value"), normal: this.shadowRoot?.querySelector(".value"), }; displayValue(displayValueEles, this.min, this.max, v, this.labeled); if (this.value !== v) this.value = v; } } }, setup() { const touchslider = this.shadowRoot?.querySelector(".slider"); let mousedownLocationX; let is_mousedown = false; const value = this.value !== null ? Number(this.value) : this.min; touchslider.style.width = `${(value - this.min) / (this.max - this.min) * 100}%`; const displayValueEles = { hiddened: this.shadowRoot?.querySelector(".hidden-value"), normal: this.shadowRoot?.querySelector(".value"), }; setTimeout(() => { displayValue(displayValueEles, this.min, this.max, this.value, this.labeled); }); let beforeWidth; this?.addEventListener("mousedown", (e) => { mousedownLocationX = e.clientX; is_mousedown = true; beforeWidth = parseInt(getComputedStyle(touchslider).width) / this.getBoundingClientRect().width * 100; }); document?.addEventListener("mouseup", () => is_mousedown = false); document?.addEventListener("mousemove", (e) => { if (!is_mousedown) return; const { width, left } = this.getBoundingClientRect(); const realmouse = mousedownLocationX - left; const addedmouse = e.clientX - left; const added = (addedmouse - realmouse) / width * 100; let result = added + beforeWidth; if (result >= 100) result = 100; else if (result <= 0) result = 0; touchslider.style.width = `${result}%`; const _value = Number((Math.round((this.max - this.min) * result / 100 / this.step) * this.step + this.min).toFixed(2)); this.value = _value > this.max ? this.max : _value; displayValue(displayValueEles, this.min, this.max, this.value, this.labeled); this.dispatchEvent(new Event("input")); }); this?.addEventListener("touchstart", (e) => { mousedownLocationX = e.touches[0].clientX; is_mousedown = true; beforeWidth = parseInt(getComputedStyle(touchslider).width) / this.getBoundingClientRect().width * 100; }); document?.addEventListener("touchend", () => is_mousedown = false); document?.addEventListener("touchmove", (e) => { if (!is_mousedown) return; const { width, left } = this.getBoundingClientRect(); const realmouse = mousedownLocationX - left; const addedmouse = e.touches[0].clientX - left; const added = (addedmouse - realmouse) / width * 100; let result = added + beforeWidth; if (result >= 100) result = 100; else if (result <= 0) result = 0; touchslider.style.width = `${result}%`; const _value = Number((Math.round((this.max - this.min) * result / 100 / this.step) * this.step + this.min).toFixed(2)); this.value = _value > this.max ? this.max : _value; displayValue(displayValueEles, this.min, this.max, this.value, this.labeled); this.dispatchEvent(new Event("input")); }); return {}; } }) { } Slider.defineElement(); import 'vue';