UNPKG

@limetech/lime-elements

Version:
464 lines (463 loc) 18.2 kB
import { h, Host, } from "@stencil/core"; import { getPercentageClass } from "./get-percentage-class"; import { createRandomString } from "../../util/random-string"; const DEFAULT_FACTOR = 1; const DEFAULT_MAX_VALUE = 100; const DEFAULT_MIN_VALUE = 0; const MAX_VISIBLE_STEP_DOTS = 20; /** * @exampleComponent limel-example-slider-basic * @exampleComponent limel-example-slider-multiplier * @exampleComponent limel-example-slider-multiplier-percentage-colors * @exampleComponent limel-example-slider-unit * @exampleComponent limel-example-slider-composite */ export class Slider { constructor() { /** * Disables the slider when `true`, * and visually shows that the field is editable but disabled. * This tells the users that if certain requirements are met, * the slider may become interactable. */ this.disabled = false; /** * Disables the slider when `true`. This visualizes the slider slightly differently. * But shows no visual sign indicating that the slider field * is disabled or can ever become interactable. */ this.readonly = false; /** * Default value: 1. * The factor that the properties `value`, `valuemax`, `valuemin`, and * `step` are multiplied by. On `change` divides the value by the factor, * so the original format stays the same. */ this.factor = DEFAULT_FACTOR; /** * Set to `true` to indicate that the slider is required. */ this.required = false; /** * Set to `true` to indicate that the current value of the slider is invalid. */ this.invalid = false; /** * Set to `true` to display percentage-based colors on the slider. * The colors change in intervals of 10% as the value changes, * creating a color spectrum from red (low) → orange → yellow → green → teal (high). */ this.displaysPercentageColors = false; /** * Unit to display next to the value */ this.unit = ''; /** * The maximum value allowed */ this.valuemax = DEFAULT_MAX_VALUE; /** * The minimum value allowed */ this.valuemin = DEFAULT_MIN_VALUE; this.renderStepDots = (min, max) => { if (!this.step) { return; } const step = this.multiplyByFactor(this.step); const count = Math.floor((max - min) / step) + 1; if (count > MAX_VISIBLE_STEP_DOTS) { return; } return Array.from({ length: count }, () => h("span", { class: "step-dot" })); }; this.renderHelperLine = () => { if (!this.helperText) { return; } return (h("limel-helper-line", { helperText: this.helperText, helperTextId: this.helperTextId, invalid: this.invalid })); }; this.handleInput = (event) => { event.stopPropagation(); const input = event.target; const value = Number(input.value); this.displayValue = value; this.setPercentageClass(value / this.factor); }; this.handleChange = (event) => { event.stopPropagation(); const input = event.target; let value = Number(input.value); const step = this.multiplyByFactor(this.step); if (!this.isMultipleOfStep(value, step)) { value = this.roundToStep(value, step); } this.change.emit(value / this.factor); }; this.getContainerClassList = () => { if (!this.percentageClass) { return {}; } return { [this.percentageClass]: true, }; }; this.multiplyByFactor = (value) => { return Math.round(value * this.factor); }; this.getValue = () => { let value = this.value; if (!Number.isFinite(value)) { value = this.valuemin; } return value; }; this.getFraction = () => { const min = this.multiplyByFactor(this.valuemin); const max = this.multiplyByFactor(this.valuemax); if (max === min) { return 0; } return Math.max(0, Math.min(1, (this.displayValue - min) / (max - min))); }; this.setPercentageClass = (value) => { this.percentageClass = getPercentageClass((value - this.valuemin) / (this.valuemax - this.valuemin)); }; this.isMultipleOfStep = (value, step) => { if (!step) { return true; } return value % step === 0; }; this.roundToStep = (value, step) => { return Math.round(value / step) * step; }; this.labelId = createRandomString(); this.helperTextId = createRandomString(); } componentWillLoad() { this.displayValue = this.multiplyByFactor(this.getValue()); this.setPercentageClass(this.getValue()); } render() { const min = this.multiplyByFactor(this.valuemin); const max = this.multiplyByFactor(this.valuemax); const fraction = this.getFraction(); const inputProps = {}; if (this.step) { inputProps.step = this.multiplyByFactor(this.step); } if (this.disabled || this.readonly) { inputProps.disabled = true; } return (h(Host, { key: 'fa9695563f078bdab1707422cad3aaef2610e592', class: this.getContainerClassList() }, h("limel-notched-outline", { key: '8b71ef15e9544a6b346248217c2c2fa8acbba6e7', labelId: this.labelId, label: this.label, required: this.required, invalid: this.invalid, disabled: this.disabled, readonly: this.readonly, hasValue: !!this.value, hasFloatingLabel: true }, h("div", { key: '0327cf7d5295bb1d56e2e34b8dca15cfc3e15c0d', slot: "content" }, h("div", { key: '83fade77119ba9fa1d24ae8fdcd54317648918b3', class: "slider", style: { '--slider-fraction': `${fraction}` } }, h("input", Object.assign({ key: '5181430787abe75b4556923e31ebe8520df977e9', type: "range", min: min, max: max, value: this.displayValue, "aria-labelledby": this.labelId, "aria-describedby": this.helperText ? this.helperTextId : undefined, onInput: this.handleInput, onChange: this.handleChange }, inputProps)), h("div", { key: '6ee95d718f1d01e92adcf9592882ed1d32e4ba67', class: "track" }, h("div", { key: 'bba420e41501990bdeb1326e59e6eff57000848d', class: "active" }), this.renderStepDots(min, max)), h("div", { key: '46e5a45adbda4e1a650d01408c3d228f1c6be68d', class: "thumb" }, h("div", { key: 'eeba995f52ff3291e2e7818528b05c7c1dec1b32', class: "knob" }), h("div", { key: 'd0321459ed22c6870d939da54df43fe2bfef6333', class: "indicator", "aria-hidden": "true" }, this.displayValue))), h("div", { key: 'bcc4449d7605343fd64f4b89b50c8e5d665f6937', class: "range-labels" }, h("span", { key: '301c78b1640551fe814ad3b2768bb40f9b682b39', class: "min" }, this.multiplyByFactor(this.valuemin), this.unit), h("span", { key: '7f8d741f63030b6f8e88231e5215448b815bf3ca', class: "max" }, this.multiplyByFactor(this.valuemax), this.unit)))), this.renderHelperLine())); } watchValue() { this.displayValue = this.multiplyByFactor(this.getValue()); this.setPercentageClass(this.getValue()); } static get is() { return "limel-slider"; } static get encapsulation() { return "shadow"; } static get originalStyleUrls() { return { "$": ["slider.scss"] }; } static get styleUrls() { return { "$": ["slider.css"] }; } static get properties() { return { "disabled": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Disables the slider when `true`,\nand visually shows that the field is editable but disabled.\nThis tells the users that if certain requirements are met,\nthe slider may become interactable." }, "getter": false, "setter": false, "reflect": true, "attribute": "disabled", "defaultValue": "false" }, "readonly": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Disables the slider when `true`. This visualizes the slider slightly differently.\nBut shows no visual sign indicating that the slider field\nis disabled or can ever become interactable." }, "getter": false, "setter": false, "reflect": true, "attribute": "readonly", "defaultValue": "false" }, "factor": { "type": "number", "mutable": false, "complexType": { "original": "number", "resolved": "number", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Default value: 1.\nThe factor that the properties `value`, `valuemax`, `valuemin`, and\n`step` are multiplied by. On `change` divides the value by the factor,\nso the original format stays the same." }, "getter": false, "setter": false, "reflect": true, "attribute": "factor", "defaultValue": "DEFAULT_FACTOR" }, "label": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Label to display next to the input" }, "getter": false, "setter": false, "reflect": true, "attribute": "label" }, "helperText": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Optional helper text to display below the slider" }, "getter": false, "setter": false, "reflect": true, "attribute": "helper-text" }, "required": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Set to `true` to indicate that the slider is required." }, "getter": false, "setter": false, "reflect": true, "attribute": "required", "defaultValue": "false" }, "invalid": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Set to `true` to indicate that the current value of the slider is invalid." }, "getter": false, "setter": false, "reflect": true, "attribute": "invalid", "defaultValue": "false" }, "displaysPercentageColors": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Set to `true` to display percentage-based colors on the slider.\nThe colors change in intervals of 10% as the value changes,\ncreating a color spectrum from red (low) \u2192 orange \u2192 yellow \u2192 green \u2192 teal (high)." }, "getter": false, "setter": false, "reflect": true, "attribute": "displays-percentage-colors", "defaultValue": "false" }, "unit": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Unit to display next to the value" }, "getter": false, "setter": false, "reflect": true, "attribute": "unit", "defaultValue": "''" }, "value": { "type": "number", "mutable": false, "complexType": { "original": "number", "resolved": "number", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "The value of the input" }, "getter": false, "setter": false, "reflect": true, "attribute": "value" }, "valuemax": { "type": "number", "mutable": false, "complexType": { "original": "number", "resolved": "number", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "The maximum value allowed" }, "getter": false, "setter": false, "reflect": true, "attribute": "valuemax", "defaultValue": "DEFAULT_MAX_VALUE" }, "valuemin": { "type": "number", "mutable": false, "complexType": { "original": "number", "resolved": "number", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "The minimum value allowed" }, "getter": false, "setter": false, "reflect": true, "attribute": "valuemin", "defaultValue": "DEFAULT_MIN_VALUE" }, "step": { "type": "number", "mutable": false, "complexType": { "original": "number", "resolved": "number", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "The stepping interval to use when adjusting the value" }, "getter": false, "setter": false, "reflect": true, "attribute": "step" } }; } static get states() { return { "percentageClass": {}, "displayValue": {} }; } static get events() { return [{ "method": "change", "name": "change", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "Emitted when the value has been changed" }, "complexType": { "original": "number", "resolved": "number", "references": {} } }]; } static get watchers() { return [{ "propName": "value", "methodName": "watchValue" }]; } }