UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

580 lines (577 loc) • 22 kB
/** * DevExtreme (cjs/__internal/ui/slider/m_slider.js) * Version: 24.2.6 * Build date: Mon Mar 17 2025 * * Copyright (c) 2012 - 2025 Developer Express Inc. ALL RIGHTS RESERVED * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/ */ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _click = require("../../../common/core/events/click"); var _emitter = require("../../../common/core/events/core/emitter.feedback"); var _events_engine = _interopRequireDefault(require("../../../common/core/events/core/events_engine")); var _swipeable = _interopRequireDefault(require("../../../common/core/events/gesture/swipeable")); var _pointer = _interopRequireDefault(require("../../../common/core/events/pointer")); var _index = require("../../../common/core/events/utils/index"); var _number = _interopRequireDefault(require("../../../common/core/localization/number")); var _component_registrator = _interopRequireDefault(require("../../../core/component_registrator")); var _devices = _interopRequireDefault(require("../../../core/devices")); var _renderer = _interopRequireDefault(require("../../../core/renderer")); var _common = require("../../../core/utils/common"); var _deferred = require("../../../core/utils/deferred"); var _math = require("../../../core/utils/math"); var _size = require("../../../core/utils/size"); var _themes = require("../../../ui/themes"); var _utils = require("../../../ui/widget/utils.ink_ripple"); var _m_track_bar = _interopRequireDefault(require("../m_track_bar")); var _m_slider_handle = _interopRequireDefault(require("./m_slider_handle")); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e } } function _extends() { return _extends = Object.assign ? Object.assign.bind() : function(n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) { ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]) } } return n }, _extends.apply(null, arguments) } const SLIDER_CLASS = "dx-slider"; const SLIDER_WRAPPER_CLASS = "dx-slider-wrapper"; const SLIDER_HANDLE_SELECTOR = ".dx-slider-handle"; const SLIDER_BAR_CLASS = "dx-slider-bar"; const SLIDER_RANGE_CLASS = "dx-slider-range"; const SLIDER_RANGE_VISIBLE_CLASS = "dx-slider-range-visible"; const SLIDER_LABEL_CLASS = "dx-slider-label"; const SLIDER_LABEL_POSITION_CLASS_PREFIX = "dx-slider-label-position-"; const SLIDER_TOOLTIP_POSITION_CLASS_PREFIX = "dx-slider-tooltip-position-"; const INVALID_MESSAGE_VISIBLE_CLASS = "dx-invalid-message-visible"; const SLIDER_VALIDATION_NAMESPACE = "Validation"; class Slider extends _m_track_bar.default { _supportedKeys() { const { rtlEnabled: rtlEnabled } = this.option(); const roundedValue = (offset, isLeftDirection) => { offset = this._valueStep(offset); const { step: step, value: value, min: min, max: max } = this.option(); const currentPosition = value - min; const remainder = (0, _math.getRemainderByDivision)(currentPosition, step, this._getValueExponentLength()); let result = isLeftDirection ? value - offset + (remainder ? step - remainder : 0) : value + offset - remainder; if (result < min) { result = min } else if (result > max) { result = max } return this._roundToExponentLength(result) }; const moveHandleRight = offset => { this.option("value", roundedValue(offset, rtlEnabled)) }; const moveHandleLeft = offset => { this.option("value", roundedValue(offset, !rtlEnabled)) }; return _extends({}, super._supportedKeys(), { leftArrow(e) { this._processKeyboardEvent(e); moveHandleLeft(this.option("step")) }, rightArrow(e) { this._processKeyboardEvent(e); moveHandleRight(this.option("step")) }, pageUp(e) { this._processKeyboardEvent(e); moveHandleRight(this.option("step") * this.option("keyStep")) }, pageDown(e) { this._processKeyboardEvent(e); moveHandleLeft(this.option("step") * this.option("keyStep")) }, home(e) { this._processKeyboardEvent(e); const min = this.option("min"); this.option("value", min) }, end(e) { this._processKeyboardEvent(e); const max = this.option("max"); this.option("value", max) } }) } _processKeyboardEvent(e) { e.preventDefault(); e.stopPropagation(); this._saveValueChangeEvent(e) } _getDefaultOptions() { return _extends({}, super._getDefaultOptions(), { value: 50, hoverStateEnabled: true, activeStateEnabled: true, step: 1, showRange: true, tooltip: { enabled: false, format: value => value, position: "top", showMode: "onHover" }, label: { visible: false, position: "bottom", format: value => value }, keyStep: 1, useInkRipple: false, validationMessageOffset: (0, _themes.isMaterial)() ? { h: 18, v: 0 } : { h: 7, v: 4 }, focusStateEnabled: true, valueChangeMode: "onHandleMove" }) } _init() { super._init(); this._activeStateUnit = ".dx-slider-handle" } _toggleValidationMessage(visible) { if (!this.option("isValid")) { this.$element().toggleClass("dx-invalid-message-visible", visible) } } _defaultOptionsRules() { return super._defaultOptionsRules().concat([{ device: () => "desktop" === _devices.default.real().deviceType && !_devices.default.isSimulator(), options: { focusStateEnabled: true } }, { device() { const themeName = (0, _themes.current)(); return (0, _themes.isMaterial)(themeName) }, options: { useInkRipple: true } }]) } _initMarkup() { this.$element().addClass("dx-slider"); this._renderSubmitElement(); this.option("useInkRipple") && this._renderInkRipple(); super._initMarkup(); this._renderLabels(); this._renderStartHandler(); this._renderAriaMinAndMax() } _attachFocusEvents() { super._attachFocusEvents(); const namespace = this.NAME + "Validation"; const focusInEvent = (0, _index.addNamespace)("focusin", namespace); const focusOutEvent = (0, _index.addNamespace)("focusout", namespace); const $focusTarget = this._focusTarget(); _events_engine.default.on($focusTarget, focusInEvent, this._toggleValidationMessage.bind(this, true)); _events_engine.default.on($focusTarget, focusOutEvent, this._toggleValidationMessage.bind(this, false)) } _detachFocusEvents() { super._detachFocusEvents(); const $focusTarget = this._focusTarget(); this._toggleValidationMessage(false); _events_engine.default.off($focusTarget, this.NAME + "Validation") } _render() { super._render(); this._repaintHandle() } _renderSubmitElement() { this._$submitElement = (0, _renderer.default)("<input>").attr("type", "hidden").appendTo(this.$element()) } _getSubmitElement() { return this._$submitElement } _renderInkRipple() { this._inkRipple = (0, _utils.render)({ waveSizeCoefficient: .7, isCentered: true, wavesNumber: 2, useHoldAnimation: false }) } _renderInkWave(element, dxEvent, doRender, waveIndex) { if (!this._inkRipple) { return } const config = { element: element, event: dxEvent, wave: waveIndex }; if (doRender) { this._inkRipple.showWave(config) } else { this._inkRipple.hideWave(config) } } _visibilityChanged() { this.repaint() } _renderWrapper() { super._renderWrapper(); this._$wrapper.addClass("dx-slider-wrapper"); this._createComponent(this._$wrapper, _swipeable.default, { rtlEnabled: false, elastic: false, immediate: true, immediateTimeout: 0, onStart: this._swipeStartHandler.bind(this), onUpdated: this._swipeUpdateHandler.bind(this), onEnd: this._swipeEndHandler.bind(this), itemSizeFunc: this._itemWidthFunc.bind(this) }) } _renderContainer() { super._renderContainer(); this._$bar.addClass("dx-slider-bar") } _renderRange() { super._renderRange(); this._$range.addClass("dx-slider-range"); this._renderHandle(); this._renderRangeVisibility() } _renderRangeVisibility() { this._$range.toggleClass("dx-slider-range-visible", Boolean(this.option("showRange"))) } _renderHandle() { const { value: value } = this.option(); this._$handle = this._renderHandleImpl(value, this._$handle) } _renderHandleImpl(value, $element) { const $handle = $element || (0, _renderer.default)("<div>").appendTo(this._$range); const { tooltip: tooltip } = this.option(); this.$element().toggleClass("dx-slider-tooltip-position-bottom", (null === tooltip || void 0 === tooltip ? void 0 : tooltip.enabled) && "bottom" === (null === tooltip || void 0 === tooltip ? void 0 : tooltip.position)).toggleClass("dx-slider-tooltip-position-top", (null === tooltip || void 0 === tooltip ? void 0 : tooltip.enabled) && "top" === (null === tooltip || void 0 === tooltip ? void 0 : tooltip.position)); this._createComponent($handle, _m_slider_handle.default, { value: value, tooltip: tooltip }); return $handle } _renderAriaMinAndMax() { this.setAria({ valuemin: this.option("min"), valuemax: this.option("max") }, this._$handle) } _toggleActiveState($element, value) { super._toggleActiveState($element, value); this._renderInkWave($element, null, !!value, 1) } _toggleFocusClass(isFocused, $element) { super._toggleFocusClass(isFocused, $element); if (this._disposed) { return } const $focusTarget = (0, _renderer.default)($element || this._focusTarget()); this._renderInkWave($focusTarget, null, isFocused, 0) } _renderLabels() { this.$element().removeClass("dx-slider-label-position-bottom").removeClass("dx-slider-label-position-top"); if (this.option("label.visible")) { const { min: min, max: max } = this.option(); const position = this.option("label.position"); const labelFormat = this.option("label.format"); if (!this._$minLabel) { this._$minLabel = (0, _renderer.default)("<div>").addClass("dx-slider-label").appendTo(this._$wrapper) } this._$minLabel.text(_number.default.format(min, labelFormat)); if (!this._$maxLabel) { this._$maxLabel = (0, _renderer.default)("<div>").addClass("dx-slider-label").appendTo(this._$wrapper) } this._$maxLabel.text(_number.default.format(max, labelFormat)); this.$element().addClass("dx-slider-label-position-" + position) } else { if (this._$minLabel) { this._$minLabel.remove(); delete this._$minLabel } if (this._$maxLabel) { this._$maxLabel.remove(); delete this._$maxLabel } } } _renderStartHandler() { const pointerDownEventName = (0, _index.addNamespace)(_pointer.default.down, this.NAME); const clickEventName = (0, _index.addNamespace)(_click.name, this.NAME); const startAction = this._createAction(this._startHandler.bind(this)); const $element = this.$element(); _events_engine.default.off($element, pointerDownEventName); _events_engine.default.on($element, pointerDownEventName, (e => { if ((0, _index.isMouseEvent)(e)) { startAction({ event: e }) } })); _events_engine.default.off($element, clickEventName); _events_engine.default.on($element, clickEventName, (e => { const $handle = this._activeHandle(); if ($handle) { _events_engine.default.trigger($handle, "focusin"); _events_engine.default.trigger($handle, "focus") } startAction({ event: e }); const { valueChangeMode: valueChangeMode } = this.option(); if ("onHandleRelease" === valueChangeMode) { this.option("value", this._getActualValue()); this._actualValue = void 0 } })) } _itemWidthFunc() { return this._itemWidthRatio } _swipeStartHandler(e) { const rtlEnabled = this.option("rtlEnabled"); if ((0, _index.isTouchEvent)(e.event)) { this._createAction(this._startHandler.bind(this))({ event: e.event }) } this._feedbackDeferred = (0, _deferred.Deferred)(); (0, _emitter.lock)(this._feedbackDeferred); const { activeStateEnabled: activeStateEnabled } = this.option(); this._toggleActiveState(this._activeHandle(), activeStateEnabled); this._startOffset = this._currentRatio; const startOffset = this._startOffset * this._swipePixelRatio(); const endOffset = (1 - this._startOffset) * this._swipePixelRatio(); e.event.maxLeftOffset = rtlEnabled ? endOffset : startOffset; e.event.maxRightOffset = rtlEnabled ? startOffset : endOffset; this._itemWidthRatio = (0, _size.getWidth)(this.$element()) / this._swipePixelRatio(); this._needPreventAnimation = true } _swipeEndHandler(e) { var _this$_feedbackDeferr; if (this._isSingleValuePossible()) { return } null === (_this$_feedbackDeferr = this._feedbackDeferred) || void 0 === _this$_feedbackDeferr || _this$_feedbackDeferr.resolve(); this._toggleActiveState(this._activeHandle(), false); const offsetDirection = this.option("rtlEnabled") ? -1 : 1; const ratio = this._startOffset + offsetDirection * e.event.targetOffset / this._swipePixelRatio(); delete this._needPreventAnimation; this._saveValueChangeEvent(e.event); this._changeValueOnSwipe(ratio); const { valueChangeMode: valueChangeMode } = this.option(); if ("onHandleRelease" === valueChangeMode) { this.option("value", this._getActualValue()) } this._actualValue = void 0; delete this._startOffset; this._renderValue() } _activeHandle() { return this._$handle } _swipeUpdateHandler(e) { if (this._isSingleValuePossible()) { return } this._saveValueChangeEvent(e.event); this._updateHandlePosition(e) } _updateHandlePosition(e) { const offsetDirection = this.option("rtlEnabled") ? -1 : 1; const newRatio = Math.min(this._startOffset + offsetDirection * e.event.offset / this._swipePixelRatio(), 1); (0, _size.setWidth)(this._$range, 100 * newRatio + "%"); _m_slider_handle.default.getInstance(this._activeHandle()).fitTooltipPosition; this._changeValueOnSwipe(newRatio) } _swipePixelRatio() { const { min: min, max: max } = this.option(); const step = this._valueStep(this.option("step")); return (max - min) / step } _valueStep(step) { if (!step || isNaN(step)) { step = 1 } return step } _getValueExponentLength() { const { step: step, min: min } = this.option(); return Math.max((0, _math.getExponentLength)(step), (0, _math.getExponentLength)(min)) } _roundToExponentLength(value) { const valueExponentLength = this._getValueExponentLength(); return (0, _math.roundFloatPart)(value, valueExponentLength) } _changeValueOnSwipe(ratio) { const { min: min, max: max } = this.option(); const step = this._valueStep(this.option("step")); const newChange = ratio * (max - min); let newValue = min + newChange; if (step < 0) { return } if (newValue === max || newValue === min) { this._setValueOnSwipe(newValue) } else { const stepCount = Math.round((newValue - min) / step); newValue = this._roundToExponentLength(stepCount * step + min); this._setValueOnSwipe(Math.max(Math.min(newValue, max), min)) } } _setValueOnSwipe(value) { this._actualValue = value; const { valueChangeMode: valueChangeMode } = this.option(); if ("onHandleRelease" === valueChangeMode) { _m_slider_handle.default.getInstance(this._activeHandle()).option("value", value) } else { this.option("value", value); this._saveValueChangeEvent(void 0) } } _getActualValue() { const { value: value } = this.option(); return this._actualValue ?? value } _isSingleValuePossible() { const { min: min, max: max } = this.option(); return min === max } _startHandler(args) { if (this._isSingleValuePossible()) { return } const e = args.event; this._currentRatio = ((0, _index.eventData)(e).x - this._$bar.offset().left) / (0, _size.getWidth)(this._$bar); if (this.option("rtlEnabled")) { this._currentRatio = 1 - this._currentRatio } this._saveValueChangeEvent(e); this._changeValueOnSwipe(this._currentRatio) } _renderValue() { super._renderValue(); const value = this._getActualValue(); this._getSubmitElement().val((0, _common.applyServerDecimalSeparator)(value)); _m_slider_handle.default.getInstance(this._activeHandle()).option("value", value) } _setRangeStyles(options) { options && this._$range.css(options) } _callHandlerMethod(name, args) { _m_slider_handle.default.getInstance(this._$handle)[name](args) } _repaintHandle() { this._callHandlerMethod("repaint") } _fitTooltip() { this._callHandlerMethod("updateTooltipPosition") } _optionChanged(args) { switch (args.name) { case "visible": super._optionChanged(args); this._renderHandle(); this._repaintHandle(); break; case "min": case "max": this._renderValue(); super._optionChanged(args); this._renderLabels(); this._renderAriaMinAndMax(); this._fitTooltip(); break; case "step": this._renderValue(); break; case "keyStep": case "valueChangeMode": break; case "showRange": this._renderRangeVisibility(); break; case "tooltip": this._renderHandle(); break; case "label": this._renderLabels(); break; case "useInkRipple": this._invalidate(); break; default: super._optionChanged(args) } } _refresh() { const { rtlEnabled: rtlEnabled } = this.option(); this._toggleRTLDirection(rtlEnabled); this._renderDimensions(); this._renderValue(); this._renderHandle(); this._repaintHandle() } _clean() { delete this._inkRipple; delete this._actualValue; super._clean() } }(0, _component_registrator.default)("dxSlider", Slider); var _default = exports.default = Slider;