UNPKG

@limetech/lime-elements

Version:
501 lines (499 loc) • 16.2 kB
import { MDCSlider } from '@material/slider'; 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; /** * @exampleComponent limel-example-slider-basic * @exampleComponent limel-example-slider-multiplier * @exampleComponent limel-example-slider-multiplier-percentage-colors * @exampleComponent limel-example-slider-composite */ export class Slider { constructor() { this.renderRangeContainer = () => { return (h("div", { class: "slider__content-range-container" }, h("span", { class: "slider__content-min-label" }, this.multiplyByFactor(this.valuemin), this.unit), h("span", { class: "slider__content-max-label" }, this.multiplyByFactor(this.valuemax), this.unit))); }; this.renderSliderContainer = (inputProps) => { return (h("div", { class: { 'mdc-slider': true, 'mdc-slider--discrete': true, 'mdc-slider--disabled': this.disabled || this.readonly, } }, this.renderSliderInput(inputProps), this.renderTrack(), this.renderThumb())); }; this.renderSliderInput = (inputProps) => { return (h("input", Object.assign({ class: "mdc-slider__input", type: "range", min: this.multiplyByFactor(this.valuemin), max: this.multiplyByFactor(this.valuemax), value: this.multiplyByFactor(this.value), name: "volume", "aria-labelledby": this.labelId, "aria-describedby": this.helperText ? this.helperTextId : undefined, "aria-controls": this.helperText ? this.helperTextId : undefined }, inputProps))); }; this.renderTrack = () => { return (h("div", { class: "mdc-slider__track" }, h("div", { class: "mdc-slider__track--inactive" }), h("div", { class: "mdc-slider__track--active" }, h("div", { class: "mdc-slider__track--active_fill" })))); }; this.renderThumb = () => { return (h("div", { class: "mdc-slider__thumb" }, h("div", { class: "mdc-slider__value-indicator-container", "aria-hidden": "true" }, h("div", { class: "mdc-slider__value-indicator" }, h("span", { class: "mdc-slider__value-indicator-text" }, this.multiplyByFactor(this.value)))), h("div", { class: "mdc-slider__thumb-knob" }))); }; this.renderHelperLine = () => { if (!this.helperText) { return; } return (h("limel-helper-line", { helperText: this.helperText, helperTextId: this.helperTextId, invalid: this.invalid })); }; this.initialize = () => { const inputElement = this.getInputElement(); if (!inputElement) { return; } const value = this.getValue(); /* For some reason the input element's `value` attribute is removed (probably by Stencil) when the element is first rendered. But if the attribute is missing when MDCSlider is initialized (MDC v11.0.0), MDCSlider crashes. So we add the attribute right before initializing MDCSlider. /Ads */ inputElement.setAttribute('value', `${this.multiplyByFactor(value)}`); /* When creating the `mdcSlider` component, its important that the value set in the input field obeys the range and the step size. The MDCSlider will throw an exception unless the value in the input element is dividible by the step value and is in the provided range. If an exception occurs, this component will crash and it will be impossible to change its value. The logic below ensures that the component will render even though the provided value is wrong. This could be considered wrong, but it at least fixes so that it's possible to change the value from the UI. */ const greaterThanOrEqualMin = value >= this.valuemin; const lessThanOrEqualMax = value <= this.valuemax; if (!greaterThanOrEqualMin) { const newMin = this.multiplyByFactor(value); inputElement.setAttribute('min', `${newMin}`); } if (!lessThanOrEqualMax) { const newMax = this.multiplyByFactor(value); inputElement.setAttribute('max', `${newMax}`); } if (!this.isMultipleOfStep(value, this.step)) { inputElement.removeAttribute('step'); } this.createMDCSlider(); }; this.reCreateSliderWithStep = () => { const inputElement = this.getInputElement(); const step = `${this.multiplyByFactor(this.step)}`; inputElement.setAttribute('step', step); this.destroyMDCSlider(); this.createMDCSlider(); }; this.createMDCSlider = () => { const element = this.getRootElement(); this.mdcSlider = new MDCSlider(element); this.mdcSlider.listen('MDCSlider:change', this.changeHandler); this.mdcSlider.listen('MDCSlider:input', this.inputHandler); }; this.changeHandler = (event) => { let value = event.detail.value; const step = this.multiplyByFactor(this.step); if (!this.isMultipleOfStep(value, step)) { value = this.roundToStep(value, step); } this.change.emit(value / this.factor); }; this.inputHandler = (event) => { this.setPercentageClass(event.detail.value / this.factor); }; this.getContainerClassList = () => { return { [this.percentageClass]: true, disabled: this.disabled || this.readonly, readonly: this.readonly, }; }; this.resizeObserverCallback = () => { var _a; (_a = this.mdcSlider) === null || _a === void 0 ? void 0 : _a.layout(); }; this.updateDisabledState = () => { var _a; if (!this.mdcSlider) { return; } (_a = this.mdcSlider) === null || _a === void 0 ? void 0 : _a.setDisabled(this.disabled || this.readonly); }; 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.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.getRootElement = () => { return this.rootElement.shadowRoot.querySelector('.mdc-slider'); }; this.getInputElement = () => { const element = this.getRootElement(); if (!element) { return; } return element.querySelector('input'); }; this.isStepConfigured = () => { if (!this.step) { return true; } const input = this.getInputElement(); if (!input) { return true; } return input.hasAttribute('step'); }; this.disabled = false; this.readonly = false; this.factor = DEFAULT_FACTOR; this.label = undefined; this.helperText = undefined; this.required = false; this.invalid = false; this.unit = ''; this.value = undefined; this.valuemax = DEFAULT_MAX_VALUE; this.valuemin = DEFAULT_MIN_VALUE; this.step = undefined; this.percentageClass = undefined; this.labelId = createRandomString(); this.helperTextId = createRandomString(); } connectedCallback() { this.initialize(); this.observer = new ResizeObserver(this.resizeObserverCallback); this.observer.observe(this.rootElement); } componentWillLoad() { this.setPercentageClass(this.value); } componentDidLoad() { this.initialize(); } disconnectedCallback() { this.destroyMDCSlider(); this.observer.disconnect(); } render() { const inputProps = {}; if (this.step) { inputProps.step = this.multiplyByFactor(this.step); } if (this.disabled || this.readonly) { inputProps.disabled = true; } return (h(Host, { class: this.getContainerClassList() }, h("limel-notched-outline", { 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", { slot: "content" }, this.renderRangeContainer(), this.renderSliderContainer(inputProps))), this.renderHelperLine())); } watchDisabled() { this.updateDisabledState(); } watchReadonly() { this.updateDisabledState(); } watchValue() { if (!this.mdcSlider) { return; } const value = this.multiplyByFactor(this.getValue()); this.mdcSlider.setValue(value); if (this.isStepConfigured()) { return; } const step = this.multiplyByFactor(this.step); if (!this.isMultipleOfStep(value, step)) { return; } this.reCreateSliderWithStep(); } destroyMDCSlider() { this.mdcSlider.unlisten('MDCSlider:change', this.changeHandler); this.mdcSlider.unlisten('MDCSlider:input', this.inputHandler); this.mdcSlider.destroy(); this.mdcSlider = undefined; } 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." }, "attribute": "disabled", "reflect": true, "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." }, "attribute": "readonly", "reflect": true, "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." }, "attribute": "factor", "reflect": true, "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" }, "attribute": "label", "reflect": true }, "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" }, "attribute": "helper-text", "reflect": true }, "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." }, "attribute": "required", "reflect": true, "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." }, "attribute": "invalid", "reflect": true, "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" }, "attribute": "unit", "reflect": true, "defaultValue": "''" }, "value": { "type": "number", "mutable": false, "complexType": { "original": "number", "resolved": "number", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "The value of the input" }, "attribute": "value", "reflect": true }, "valuemax": { "type": "number", "mutable": false, "complexType": { "original": "number", "resolved": "number", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "The maximum value allowed" }, "attribute": "valuemax", "reflect": true, "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" }, "attribute": "valuemin", "reflect": true, "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" }, "attribute": "step", "reflect": true } }; } static get states() { return { "percentageClass": {} }; } 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 elementRef() { return "rootElement"; } static get watchers() { return [{ "propName": "disabled", "methodName": "watchDisabled" }, { "propName": "readonly", "methodName": "watchReadonly" }, { "propName": "value", "methodName": "watchValue" }]; } } //# sourceMappingURL=slider.js.map