UNPKG

@suyouwanggang/p-ui

Version:

`p-ui`是一套使用原生`Web Components`规范开发的跨框架UI组件库,基于`lit-elment`库开发。 [github项目地址](https://github.com/suyouwanggang/p-ui)

295 lines (288 loc) 10.7 kB
import { css, customElement, html, LitElement, property } from 'lit-element'; import ResizeObserver from 'resize-observer-polyfill'; type lineSize = '' | 'mid' | 'large'; @customElement('p-slider') class PSlider extends LitElement { public get input(): HTMLInputElement | unknown { return this; } @property({ type: String, reflect: true }) name: string; @property({ type: Number, reflect: true }) value: number = 0; @property({ type: Boolean, reflect: true }) vertical: boolean = false; @property({ type: Boolean, reflect: true }) alwaysTip: boolean = false; @property({ type: Boolean, reflect: true }) showtips: boolean = true; @property({ type: Boolean, reflect: true }) required: boolean = false; @property({ type: Boolean, reflect: true }) disabled: boolean = false; @property({ type: String, reflect: true }) suffix: string = undefined; @property({ type: String, reflect: true }) prefix: string = undefined; @property({ type: String, reflect: true }) lineSize: lineSize = undefined; @property({ type: String, reflect: true }) lineColor: string = undefined; @property({ type: Number, reflect: true }) min: number = 0; @property({ type: Number, reflect: true }) max: number = undefined; @property({ type: Number, reflect: true }) step: number = 1; static get styles() { return css` :host{ box-sizing:border-box; display:flex; padding:0 5px; --lineSize:2px; --lineBorder:2px; --trackSize:10px; } :host([vertical]){ height:300px; } :host([disabled]){ opacity:var(--disabled-opaticity,0.6); --themeColor:#999; cursor:not-allowed; } :host([disabled]) #slider{ pointer-events:none; opacity:var(--disabled-opaticity,0.6); } #slider-con{ display:flex; padding:5px 0; width:100%; margin: auto; } ::-moz-focus-inner,::-moz-focus-outer{ border:0; outline : 0; } :host([showtips]){ pointer-events:all; } #slider{ pointer-events:all; margin:0 -5px; width: calc( 100% + 10px ); -webkit-appearance: none; outline : 0; height: 12px; background:none; border-radius:2px; } :host([linesize='mid']){ --lineSize:4px; --lineBorder:3px; --trackSize:14px; } :host([linesize='larger']){ --lineSize:8px; --lineBorder:4px; --trackSize:22px; } #slider::-webkit-slider-runnable-track{ display: flex; align-items: center; position: relative; height:var(--lineSize) ; border-radius:var(--lineBorder) ; background:linear-gradient(to right, var(--themeColor,#42b983) calc( 100% * var(--percent) ), rgba(0,0,0,.1) 0% ); } #slider::-moz-range-progress { display: flex; align-items: center; position: relative; height:var(--lineSize); border-radius: var(--lineBorder); outline : 0; background:var(--themeColor,#42b983) } #slider::-moz-range-track{ height: var(--lineSize) ; background: rgba(0,0,0,.1); } #slider::-webkit-slider-thumb{ -webkit-appearance: none; border: var(--lineSize) solid var(--themeColor,#42b983); position: relative; width: var(--trackSize) ; height: var(--trackSize) ; border-radius: 50%; background:var(--themeColor,#42b983); transition:.2s cubic-bezier(.12, .4, .29, 1.46); } #slider::-moz-range-thumb{ box-sizing:border-box; pointer-events:none; border:2px solid var(--themeColor,#42b983); position: relative; width: var(--trackSize) ; height: var(--trackSize) ; border-radius: 50%; background:var(--themeColor,#42b983); transition:.2s cubic-bezier(.12, .4, .29, 1.46); } #slider:focus{ z-index:2; } #slider::-webkit-slider-thumb:active, #slider:focus::-webkit-slider-thumb{ transform:scale(1.2); box-shadow: 0 0 10px rgba(0,0,0,0.1); background: #fff; } #slider::-moz-range-thumb:active, #slider:focus::-moz-range-thumb{ transform:scale(1.2); box-shadow: 0 0 10px rgba(0,0,0,0.1); background: #fff; } :host([vertical]) #slider-con{ position: absolute; top: 50%; left: 50%; transform:translate(-50%, -50%) rotate(-90deg); width:calc( var(--h,300px) - 10px ); } :host([vertical]) #slider-con::before{ writing-mode: vertical-lr; padding: 10px 6px; } :host([vertical]){ display:inline-flex; position:relative; width:20px; } :host([vertical]) p-tips::before,:host([vertical]) p-tips::after{ left: calc( var(--percent ,.5) * 100% + 5px ); } :host(:focus-within) #slider-con,:host(:hover) #slider-con{ z-index:10 } `; } get slider(): HTMLInputElement { return this.renderRoot.querySelector('#slider'); } focus() { this.slider!.focus(); } reset() { this.value = 0; } get form(): HTMLFormElement { return this.closest('form,p-form'); } private _resizeObserver: ResizeObserver = null; firstUpdated(_changedProperties: Map<string | number | symbol, unknown>) { super.firstUpdated(_changedProperties); const sliderCon: HTMLElement = this.renderRoot.querySelector('#slider-con'); this.addEventListener('wheel', (ev: WheelEvent) => { if (this.disabled) { return; } ev.preventDefault(); if (ev.deltaY < 0 && !this.vertical || ev.deltaY > 0 && this.vertical) { const newValue = this.value - this.step * 5; this.value = Math.max(this.min, newValue); } else { const newValue = this.value + this.step * 5; this.value = Math.min(this.max, newValue); } this.dispatchEvent(new CustomEvent('change', { detail: { value: this.value } })); }, true); this._initResizeObserver(); } _initResizeObserver() { if (this.vertical) { if (!this._resizeObserver) { const sliderCon: HTMLElement = this.renderRoot.querySelector('#slider-con'); this._resizeObserver = new ResizeObserver((entries: ResizeObserverEntry[]) => { for (const entry of entries) { const { height } = entry.contentRect; // this.height = height + 'px'; this.style.setProperty('--h', height + 'px'); } }); this._resizeObserver.observe(this); } } else { if (this._resizeObserver) { this._resizeObserver.unobserve(this); this._resizeObserver = null; } } } get tipContent() { let tip = this.prefix !== undefined ? this.prefix : ''; tip += this.value; tip += this.suffix !== undefined ? this.suffix : ''; return tip; } get percent() { return (this.value - this.min) / (this.max - this.min); } render() { return html`<p-tips id='slider-con' dir=${this.vertical ? 'right' : 'top'} style="--percent: ${this.percent}; " .show=${this.alwaysTip ? 'true' : ''} .tips=${this.alwaysTip || (this.showtips && !this.disabled) ? this.tipContent : ''} > <input type='range' id='slider' .value=${String(this.value)} @input=${this.inputHander} @change=${this.changeHander} min=${this.min} max=${this.max} step=${this.step} ?disabled=${this.disabled} /> </p-tips> `; } inputHander(event: InputEvent) { this.value = Number(this.slider.value); event.stopPropagation(); this.dispatchEvent(new CustomEvent('input', { detail: { value: this.value } })); } changeHander(event: InputEvent) { this.value = Number(this.slider.value); this.dispatchEvent(new CustomEvent('change', { detail: { value: this.value } })); } disconnectedCallback() { super.disconnectedCallback(); if (this._resizeObserver != null) { this._resizeObserver.unobserve(this); this._resizeObserver = null; } } update(_changedProperties: Map<string | number | symbol, unknown>) { super.update(_changedProperties); if (_changedProperties.has('value') &&this.max!=undefined&& this.value > this.max) { this.value = this.max; } if (_changedProperties.has('value') && this.value < this.min) { this.value = this.min; } if (_changedProperties.has('max')) { if (this.value > this.max) { this.value = this.max; } } if (_changedProperties.has('min')) { if (this.min < 0) { this.min = 0; } if (this.value < this.min) { this.value = this.min; } } if (_changedProperties.has('vertical')) { this._initResizeObserver(); } if (_changedProperties.has('lineColor')) { if (this.lineColor) { this.style.setProperty('--themeColor', this.lineColor); } else { this.style.removeProperty('--themeColor'); } } } }