UNPKG

carbon-custom-elements

Version:

A Carbon Design System variant that's as easy to use as native HTML elements, with no framework tax, no framework silo.

1 lines 17.2 kB
{"version":3,"sources":["components/slider/slider.ts"],"names":[],"mappings":"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","file":"slider.d.ts","sourcesContent":["/**\n * @license\n *\n * Copyright IBM Corp. 2019, 2020\n *\n * This source code is licensed under the Apache-2.0 license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport { classMap } from 'lit-html/directives/class-map';\nimport throttle from 'lodash-es/throttle';\nimport { html, property, query, customElement, LitElement } from 'lit-element';\nimport settings from 'carbon-components/es/globals/js/settings';\nimport on from 'carbon-components/es/globals/js/misc/on';\nimport FocusMixin from '../../globals/mixins/focus';\nimport FormMixin from '../../globals/mixins/form';\nimport HostListenerMixin from '../../globals/mixins/host-listener';\nimport HostListener from '../../globals/decorators/host-listener';\nimport ifNonEmpty from '../../globals/directives/if-non-empty';\nimport Handle from '../../globals/internal/handle';\nimport BXSliderInput from './slider-input';\nimport styles from './slider.scss';\n\nconst { prefix } = settings;\n\ninterface Cancelable {\n cancel(): void;\n}\n\n/**\n * The direction to move the thumb, associated with key symbols.\n */\nconst THUMB_DIRECTION = {\n Left: -1,\n ArrowLeft: -1,\n Up: -1,\n ArrowUp: -1,\n Right: 1,\n ArrowRight: 1,\n Down: 1,\n ArrowDown: 1,\n};\n\n/**\n * Slider.\n * @element bx-slider\n * @slot label-text - The label text.\n * @slot max-text - The text for maximum value.\n * @slot min-text - The text for minimum value.\n * @fires bx-slider-changed - The custom event fired after the value is changed by user gesture.\n */\n@customElement(`${prefix}-slider`)\nclass BXSlider extends HostListenerMixin(FormMixin(FocusMixin(LitElement))) {\n /**\n * The internal value of `max` property.\n */\n private _max = '100';\n\n /**\n * The internal value of `min` property.\n */\n private _min = '0';\n\n /**\n * The internal value of `step` property.\n */\n private _step = '1';\n\n /**\n * The internal value of `stepRatio` property.\n */\n private _stepRatio = '4';\n\n /**\n * The handle for the listener of `${prefix}-slider-input` event.\n */\n private _hChangeInput: Handle | null = null;\n\n /**\n * The handle for the throttled listener of `mousemove` event.\n */\n private _throttledHandleMousemoveImpl: (((event: MouseEvent) => void) & Cancelable) | null = null;\n\n /**\n * `true` if dragging of thumb is in progress.\n */\n private _dragging = false;\n\n /**\n * The rate of the thumb position in the track.\n * When we try to set a new value, we adjust the value considering `step` property.\n */\n private get _rate() {\n const { max, min, value } = this;\n // Copes with out-of-range value coming programmatically or from `<bx-slider-input>`\n return Math.min(Number(max), Math.max(Number(min), value)) / (Number(max) - Number(min));\n }\n\n private set _rate(rate: number) {\n const { max, min, step } = this;\n this.value =\n Number(min) + Math.round(((Number(max) - Number(min)) * Math.min(1, Math.max(0, rate))) / Number(step)) * Number(step);\n }\n\n /**\n * The DOM element of the thumb.\n */\n @query('#thumb')\n private _thumbNode!: HTMLDivElement;\n\n /**\n * The DOM element of the track.\n */\n @query('#track')\n private _trackNode!: HTMLDivElement;\n\n /**\n * Handles `click` event on the `<label>` to focus on the thumb.\n */\n _handleClickLabel() {\n this._thumbNode?.focus();\n }\n\n _handleFormdata(event: Event) {\n const { formData } = event as any; // TODO: Wait for `FormDataEvent` being available in `lib.dom.d.ts`\n const { disabled, name, value } = this;\n if (!disabled) {\n formData.append(name, String(value));\n }\n }\n\n /**\n * Handles `keydown` event on the thumb to increase/decrease the value.\n */\n private _handleKeydownThumb({ key, shiftKey }: KeyboardEvent) {\n if (!this.disabled) {\n if (key in THUMB_DIRECTION) {\n const { max: rawMax, min: rawMin, step: rawStep, stepRatio: rawStepRatio, value } = this;\n const max = Number(rawMax);\n const min = Number(rawMin);\n const step = Number(rawStep);\n const stepRatio = Number(rawStepRatio);\n const diff = (!shiftKey ? step : (max - min) / stepRatio) * THUMB_DIRECTION[key];\n const stepCount = (value + diff) / step;\n // Snaps to next\n this.value = Math.min(max, Math.max(min, (diff >= 0 ? Math.floor(stepCount) : Math.ceil(stepCount)) * step));\n this.dispatchEvent(\n new CustomEvent((this.constructor as typeof BXSlider).eventChange, {\n bubbles: true,\n composed: true,\n detail: {\n value: this.value,\n intermediate: false,\n },\n })\n );\n }\n }\n }\n\n /**\n * Handles `mousedown` event on the thumb to start dragging.\n */\n private _handleMousedownThumb() {\n this._dragging = true;\n }\n\n /**\n * Handles `mousedown` event on the track to update the thumb position and the value as necessary.\n */\n private _handleMousedownTrack(event: MouseEvent) {\n if (!this.disabled) {\n const { _trackNode: trackNode } = this;\n const isRtl = trackNode.ownerDocument!.defaultView!.getComputedStyle(trackNode).getPropertyValue('direction') === 'rtl';\n const thumbPosition = event.clientX;\n const { left: trackLeft, width: trackWidth } = trackNode.getBoundingClientRect();\n this._rate = (isRtl ? trackLeft + trackWidth - thumbPosition : thumbPosition - trackLeft) / trackWidth;\n this.dispatchEvent(\n new CustomEvent((this.constructor as typeof BXSlider).eventChange, {\n bubbles: true,\n composed: true,\n detail: {\n value: this.value,\n },\n })\n );\n }\n }\n\n /**\n * Handles `mousemove` event on the `document` to update the thumb position and the value as necessary.\n * @param event The event.\n */\n @HostListener('document:mousemove')\n // @ts-ignore: The decorator refers to this method but TS thinks this method is not referred to\n private _handleMousemove = (event: MouseEvent) => {\n const { disabled, _dragging: dragging } = this;\n if (!disabled && dragging) {\n this._throttledHandleMousemoveImpl!(event);\n }\n };\n\n /**\n * Updates thumb position and value upon user's `mousemove` gesture.\n * @param event The event.\n */\n private _handleMousemoveImpl(event: MouseEvent) {\n const { disabled, _dragging: dragging, _trackNode: trackNode } = this;\n if (!disabled && dragging) {\n const isRtl = trackNode.ownerDocument!.defaultView!.getComputedStyle(trackNode).getPropertyValue('direction') === 'rtl';\n const thumbPosition = event.clientX;\n const { left: trackLeft, width: trackWidth } = this._trackNode.getBoundingClientRect();\n this._rate = (isRtl ? trackLeft + trackWidth - thumbPosition : thumbPosition - trackLeft) / trackWidth;\n this.dispatchEvent(\n new CustomEvent((this.constructor as typeof BXSlider).eventChange, {\n bubbles: true,\n composed: true,\n detail: {\n value: this.value,\n intermediate: true,\n },\n })\n );\n }\n }\n\n /**\n * Handles `mouseup` event on the `document` to finishing dragging.\n */\n @HostListener('document:mouseup')\n // @ts-ignore: The decorator refers to this method but TS thinks this method is not referred to\n private _handleMouseup = () => {\n if (this._dragging) {\n this.dispatchEvent(\n new CustomEvent((this.constructor as typeof BXSlider).eventChange, {\n bubbles: true,\n composed: true,\n detail: {\n value: this.value,\n },\n })\n );\n this._dragging = false;\n }\n };\n\n /**\n * Handles `${prefix}-slider-input-changed` event to update the value.\n */\n private _handleChangeInput = ({ detail }: CustomEvent) => {\n const { intermediate, value } = detail;\n this.value = value;\n this.dispatchEvent(\n new CustomEvent((this.constructor as typeof BXSlider).eventChange, {\n bubbles: true,\n composed: true,\n detail: {\n value,\n intermediate,\n },\n })\n );\n };\n\n /**\n * `true` if the check box should be disabled.\n */\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n /**\n * The formatter for the text for maximum value.\n * Should be changed upon the locale the UI is rendered with.\n */\n @property({ attribute: false })\n formatMaxText = ({ max }: { max: string }) => max;\n\n /**\n * The formatter for the text for minimum value.\n * Should be changed upon the locale the UI is rendered with.\n */\n @property({ attribute: false })\n formatMinText = ({ min }: { min: string }) => min;\n\n /**\n * The label text.\n */\n @property({ attribute: 'label-text' })\n labelText = '';\n\n /**\n * The maximum value.\n */\n @property({ type: Number, reflect: true })\n get max() {\n return this._max.toString();\n }\n\n set max(value) {\n const { max: oldMax } = this;\n this._max = value;\n this.requestUpdate('max', oldMax);\n }\n\n /**\n * The minimum value.\n */\n @property({ type: Number, reflect: true })\n get min() {\n return this._min.toString();\n }\n\n set min(value) {\n const { min: oldMin } = this;\n this._min = value;\n this.requestUpdate('min', oldMin);\n }\n\n /**\n * The form name.\n */\n @property()\n name!: string;\n\n /**\n * The snapping step of the value.\n */\n @property({ type: Number, reflect: true })\n get step() {\n return this._step.toString();\n }\n\n set step(value) {\n const { step: oldStep } = this;\n this._step = value;\n this.requestUpdate('step', oldStep);\n }\n\n /**\n * A value determining how much the value should increase/decrease by Shift+arrow keys,\n * which will be `(max - min) / stepRatio`.\n */\n @property({ type: Number, reflect: true, attribute: 'step-ratio' })\n get stepRatio() {\n return this._stepRatio.toString();\n }\n\n set stepRatio(value) {\n const { stepRatio: oldStepRatio } = this;\n this._stepRatio = value;\n this.requestUpdate('stepRatio', oldStepRatio);\n }\n\n /**\n * The value.\n */\n @property({ type: Number })\n value = 50;\n\n createRenderRoot() {\n return this.attachShadow({ mode: 'open', delegatesFocus: true });\n }\n\n connectedCallback() {\n super.connectedCallback();\n if (!this._throttledHandleMousemoveImpl) {\n this._throttledHandleMousemoveImpl = throttle(this._handleMousemoveImpl, 10);\n }\n // Manually hooks the event listeners on the host element to make the event names configurable\n this._hChangeInput = on(\n this,\n (this.constructor as typeof BXSlider).eventChangeInput,\n this._handleChangeInput as EventListener\n );\n }\n\n disconnectedCallback() {\n if (this._hChangeInput) {\n this._hChangeInput = this._hChangeInput.release();\n }\n if (this._throttledHandleMousemoveImpl) {\n this._throttledHandleMousemoveImpl.cancel();\n this._throttledHandleMousemoveImpl = null;\n }\n super.disconnectedCallback();\n }\n\n shouldUpdate(changedProperties) {\n const input = this.querySelector((this.constructor as typeof BXSlider).selectorInput) as BXSliderInput;\n if (changedProperties.has('disabled')) {\n if (input) {\n input.disabled = this.disabled;\n }\n if (this.disabled) {\n this._dragging = false;\n }\n }\n if (input) {\n ['max', 'min', 'step', 'value'].forEach(name => {\n if (changedProperties.has(name)) {\n input[name] = this[name];\n }\n });\n }\n return true;\n }\n\n render() {\n const {\n disabled,\n formatMaxText,\n formatMinText,\n labelText,\n max,\n min,\n name,\n value,\n _rate: rate,\n _handleClickLabel: handleClickLabel,\n _handleKeydownThumb: handleKeydownThumb,\n _handleMousedownTrack: handleMousedownTrack,\n _handleMousedownThumb: handleMousedownThumb,\n } = this;\n const labelClasses = classMap({\n [`${prefix}--label`]: true,\n [`${prefix}--label--disabled`]: disabled,\n });\n const sliderClasses = classMap({\n [`${prefix}--slider`]: true,\n [`${prefix}--slider--disabled`]: disabled,\n });\n return html`\n <label class=\"${labelClasses}\" @click=\"${handleClickLabel}\">\n <slot name=\"label-text\">${labelText}</slot>\n </label>\n <div class=\"${prefix}--slider-container\">\n <span class=\"${prefix}--slider__range-label\">\n <slot name=\"min-text\">${formatMinText({ min })}</slot>\n </span>\n <div class=\"${sliderClasses}\" role=\"presentation\">\n <div\n id=\"thumb\"\n class=\"${prefix}--slider__thumb\"\n role=\"slider\"\n tabindex=\"0\"\n aria-valuemax=\"${max}\"\n aria-valuemin=\"${min}\"\n aria-valuenow=\"${value}\"\n style=\"left: ${rate * 100}%\"\n @keydown=\"${handleKeydownThumb}\"\n @mousedown=\"${handleMousedownThumb}\"\n ></div>\n <div id=\"track\" class=\"${prefix}--slider__track\" @mousedown=\"${handleMousedownTrack}\"></div>\n <div class=\"${prefix}-ce--slider__filled-track-container\">\n <div class=\"${prefix}--slider__filled-track\" style=\"transform: translate(0%, -50%) scaleX(${rate})\"></div>\n </div>\n <input\n class=\"${prefix}--slider__input\"\n type=\"hidden\"\n name=\"${ifNonEmpty(name)}\"\n .value=\"${value}\"\n min=\"${ifNonEmpty(min)}\"\n max=\"${ifNonEmpty(max)}\"\n />\n </div>\n <span class=\"${prefix}--slider__range-label\">\n <slot name=\"max-text\">${formatMaxText({ max })}</slot>\n </span>\n <slot></slot>\n </div>\n `;\n }\n\n /**\n * A selector that will return the `<input>` box got entering the value directly.\n */\n static get selectorInput() {\n return `${prefix}-slider-input`;\n }\n\n /**\n * The name of the custom event fired after the value is changed by user gesture.\n */\n static get eventChange() {\n return `${prefix}-slider-changed`;\n }\n\n /**\n * The name of the custom event fired after the value is changed in `<bx-slider-input>` by user gesture.\n */\n static get eventChangeInput() {\n return `${prefix}-slider-input-changed`;\n }\n\n static styles = styles;\n}\n\nexport default BXSlider;\n"]}