UNPKG

@vime/core

Version:

Customizable, extensible, accessible and framework agnostic media player.

262 lines (261 loc) 7.64 kB
import { Component, Element, Event, h, Prop, } from '@stencil/core'; import { withComponentRegistry } from '../../core/player/withComponentRegistry'; /** * A custom styled and ARIA friendly `input[type="range"]` component for inputting numeric values. * In addition, there are optimizations made for improved touch support (more information can be * found at https://github.com/sampotts/rangetouch). */ export class Slider { constructor() { /** * A number that specifies the granularity that the value must adhere to. */ this.step = 1; /** * The lowest value in the range of permitted values. */ this.min = 0; /** * The greatest permitted value. */ this.max = 10; /** * The current value. */ this.value = 5; withComponentRegistry(this); } getPercentage() { return `${(this.value / this.max) * 100}%`; } onValueChange(event) { var _a; const value = parseFloat((_a = event.target) === null || _a === void 0 ? void 0 : _a.value); this.vmValueChange.emit(value); } calcTouchedValue(event) { const input = event.target; const touch = event.changedTouches[0]; const min = parseFloat(input.getAttribute('min')); const max = parseFloat(input.getAttribute('max')); const step = parseFloat(input.getAttribute('step')); const delta = max - min; // Calculate percentage. let percent; const clientRect = input.getBoundingClientRect(); const sliderThumbWidth = parseFloat(window .getComputedStyle(this.host) .getPropertyValue('--vm-slider-thumb-width')); const thumbWidth = ((100 / clientRect.width) * (sliderThumbWidth / 2)) / 100; percent = (100 / clientRect.width) * (touch.clientX - clientRect.left); // Don't allow outside bounds. percent = Math.max(0, Math.min(percent, 100)); // Factor in the thumb offset. if (percent < 50) { percent -= (100 - percent * 2) * thumbWidth; } else if (percent > 50) { percent += (percent - 50) * 2 * thumbWidth; } const position = delta * (percent / 100); if (step >= 1) { return min + Math.round(position / step) * step; } /** * This part differs from original implementation to save space. Only supports 2 decimal * places (0.01) as the step. */ return min + parseFloat(position.toFixed(2)); } /** * Basically input[range="type"] on touch devices sucks (particularly iOS), so this helps make it * better. * * @see https://github.com/sampotts/rangetouch */ onTouch(event) { const input = event.target; if (input.disabled) return; event.preventDefault(); this.value = this.calcTouchedValue(event); this.vmValueChange.emit(this.value); input.dispatchEvent(new window.Event(event.type === 'touchend' ? 'change' : 'input', { bubbles: true, })); } render() { var _a; return (h("div", { class: "slider", style: { '--vm-value': this.getPercentage(), } }, h("input", { type: "range", step: this.step, min: this.min, max: this.max, value: this.value, autocomplete: "off", "aria-label": this.label, "aria-valuemin": this.min, "aria-valuemax": this.max, "aria-valuenow": this.value, "aria-valuetext": (_a = this.valueText) !== null && _a !== void 0 ? _a : this.getPercentage(), "aria-orientation": "horizontal", onInput: this.onValueChange.bind(this), onFocus: () => { this.vmFocus.emit(); }, onBlur: () => { this.vmBlur.emit(); }, onTouchStart: this.onTouch.bind(this), onTouchMove: this.onTouch.bind(this), onTouchEnd: this.onTouch.bind(this) }))); } static get is() { return "vm-slider"; } static get encapsulation() { return "shadow"; } static get originalStyleUrls() { return { "$": ["slider.css"] }; } static get styleUrls() { return { "$": ["slider.css"] }; } static get properties() { return { "step": { "type": "number", "mutable": false, "complexType": { "original": "number", "resolved": "number", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "A number that specifies the granularity that the value must adhere to." }, "attribute": "step", "reflect": false, "defaultValue": "1" }, "min": { "type": "number", "mutable": false, "complexType": { "original": "number", "resolved": "number", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "The lowest value in the range of permitted values." }, "attribute": "min", "reflect": false, "defaultValue": "0" }, "max": { "type": "number", "mutable": false, "complexType": { "original": "number", "resolved": "number", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "The greatest permitted value." }, "attribute": "max", "reflect": false, "defaultValue": "10" }, "value": { "type": "number", "mutable": false, "complexType": { "original": "number", "resolved": "number", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "The current value." }, "attribute": "value", "reflect": false, "defaultValue": "5" }, "valueText": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string | undefined", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "Human-readable text alternative for the current value. Defaults to `value:max` percentage." }, "attribute": "value-text", "reflect": false }, "label": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string | undefined", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "A human-readable label for the purpose of the slider." }, "attribute": "label", "reflect": false } }; } static get events() { return [{ "method": "vmValueChange", "name": "vmValueChange", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "Emitted when the value of the underlying `input` field changes." }, "complexType": { "original": "number", "resolved": "number", "references": {} } }, { "method": "vmFocus", "name": "vmFocus", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "Emitted when the slider receives focus." }, "complexType": { "original": "void", "resolved": "void", "references": {} } }, { "method": "vmBlur", "name": "vmBlur", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "Emitted when the slider loses focus." }, "complexType": { "original": "void", "resolved": "void", "references": {} } }]; } static get elementRef() { return "host"; } }