UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

980 lines (978 loc) • 38.2 kB
/** * DevExtreme (cjs/__internal/ui/scroll_view/m_scrollable.simulated.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.SimulatedStrategy = exports.Scroller = void 0; var _translator = require("../../../common/core/animation/translator"); var _events_engine = _interopRequireDefault(require("../../../common/core/events/core/events_engine")); var _index = require("../../../common/core/events/utils/index"); var _class = _interopRequireDefault(require("../../../core/class")); var _dom_adapter = _interopRequireDefault(require("../../../core/dom_adapter")); var _renderer = _interopRequireDefault(require("../../../core/renderer")); var _common = require("../../../core/utils/common"); var _deferred = require("../../../core/utils/deferred"); var _extend = require("../../../core/utils/extend"); var _inflector = require("../../../core/utils/inflector"); var _iterator = require("../../../core/utils/iterator"); var _position = require("../../../core/utils/position"); var _size = require("../../../core/utils/size"); var _type = require("../../../core/utils/type"); var _window = require("../../../core/utils/window"); var _m_animator = _interopRequireDefault(require("./m_animator")); var _m_scrollbar = _interopRequireDefault(require("./m_scrollbar")); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e } } const SCROLLABLE_SIMULATED = "dxSimulatedScrollable"; const SCROLLABLE_STRATEGY = "dxScrollableStrategy"; const SCROLLABLE_SIMULATED_CURSOR = `${SCROLLABLE_SIMULATED}Cursor`; const SCROLLABLE_SIMULATED_KEYBOARD = `${SCROLLABLE_SIMULATED}Keyboard`; const SCROLLABLE_SIMULATED_CLASS = "dx-scrollable-simulated"; const SCROLLABLE_SCROLLBARS_ALWAYSVISIBLE = "dx-scrollable-scrollbars-alwaysvisible"; const SCROLLABLE_SCROLLBAR_CLASS = "dx-scrollable-scrollbar"; const VERTICAL = "vertical"; const HORIZONTAL = "horizontal"; const ACCELERATION = .92; const OUT_BOUNDS_ACCELERATION = .5; const MIN_VELOCITY_LIMIT = 1; const FRAME_DURATION = Math.round(1e3 / 60); const SCROLL_LINE_HEIGHT = 40; const VALIDATE_WHEEL_TIMEOUT = 500; const BOUNCE_MIN_VELOCITY_LIMIT = .2; const BOUNCE_DURATION = 400; const BOUNCE_FRAMES = 400 / FRAME_DURATION; const BOUNCE_ACCELERATION_SUM = (1 - .92 ** BOUNCE_FRAMES) / (1 - .92); const KEY_CODES = { PAGE_UP: "pageUp", PAGE_DOWN: "pageDown", END: "end", HOME: "home", LEFT: "leftArrow", UP: "upArrow", RIGHT: "rightArrow", DOWN: "downArrow", TAB: "tab" }; class InertiaAnimator extends _m_animator.default { constructor() { super(...arguments); this.VELOCITY_LIMIT = 1 } ctor(scroller) { super.ctor(); this.scroller = scroller } _isFinished() { return Math.abs(this.scroller._velocity) <= this.VELOCITY_LIMIT } _step() { this.scroller._scrollStep(this.scroller._velocity); this.scroller._velocity *= this._acceleration() } _acceleration() { return this.scroller._inBounds() ? .92 : .5 } _complete() { this.scroller._scrollComplete() } } class BounceAnimator extends InertiaAnimator { constructor() { super(...arguments); this.VELOCITY_LIMIT = .2 } _isFinished() { return this.scroller._crossBoundOnNextStep() || super._isFinished() } _acceleration() { return .92 } _complete() { this.scroller._move(this.scroller._bounceLocation); super._complete() } } class Scroller extends(_class.default.inherit({})) { ctor(options) { this._initOptions(options); this._initAnimators(); this._initScrollbar() } _initOptions(options) { this._location = 0; this._topReached = false; this._bottomReached = false; this._axis = options.direction === HORIZONTAL ? "x" : "y"; this._prop = options.direction === HORIZONTAL ? "left" : "top"; this._dimension = options.direction === HORIZONTAL ? "width" : "height"; this._scrollProp = options.direction === HORIZONTAL ? "scrollLeft" : "scrollTop"; (0, _iterator.each)(options, ((optionName, optionValue) => { this[`_${optionName}`] = optionValue })) } _initAnimators() { this._inertiaAnimator = new InertiaAnimator(this); this._bounceAnimator = new BounceAnimator(this) } _initScrollbar() { this._scrollbar = new _m_scrollbar.default((0, _renderer.default)("<div>").appendTo(this._$container), { direction: this._direction, visible: this._scrollByThumb, visibilityMode: this._visibilityModeNormalize(this._scrollbarVisible), expandable: this._scrollByThumb }); this._$scrollbar = this._scrollbar.$element() } _visibilityModeNormalize(mode) { return true === mode ? "onScroll" : false === mode ? "never" : mode } _scrollStep(delta) { const prevLocation = this._location; this._location += delta; this._suppressBounce(); this._move(); if (Math.abs(prevLocation - this._location) < 1) { return } _events_engine.default.triggerHandler(this._$container, { type: "scroll" }) } _suppressBounce() { if (this._bounceEnabled || this._inBounds(this._location)) { return } this._velocity = 0; this._location = this._boundLocation() } _boundLocation(location) { location = void 0 !== location ? location : this._location; return Math.max(Math.min(location, this._maxOffset), this._minOffset) } _move(location) { this._location = void 0 !== location ? location * this._getScaleRatio() : this._location; this._moveContent(); this._moveScrollbar() } _moveContent() { const location = this._location; this._$container[this._scrollProp](-location / this._getScaleRatio()); this._moveContentByTranslator(location) } _getScaleRatio() { if ((0, _window.hasWindow)() && !this._scaleRatio) { const element = this._$element.get(0); const realDimension = this._getRealDimension(element, this._dimension); const baseDimension = this._getBaseDimension(element, this._dimension); this._scaleRatio = Math.round(realDimension / baseDimension * 100) / 100 } return this._scaleRatio || 1 } _getRealDimension(element, dimension) { return Math.round((0, _position.getBoundingRect)(element)[dimension]) } _getBaseDimension(element, dimension) { const dimensionName = `offset${(0,_inflector.titleize)(dimension)}`; return element[dimensionName] } _moveContentByTranslator(location) { let translateOffset; const minOffset = -this._maxScrollPropValue; if (location > 0) { translateOffset = location } if (location <= minOffset) { translateOffset = location - minOffset } if (this._translateOffset === translateOffset) { return } const targetLocation = {}; targetLocation[this._prop] = translateOffset; this._translateOffset = translateOffset; if (!translateOffset) { (0, _translator.resetPosition)(this._$content); return }(0, _translator.move)(this._$content, targetLocation) } _moveScrollbar() { this._scrollbar.moveTo(this._location) } _scrollComplete() { if (this._inBounds()) { this._hideScrollbar(); if (this._completeDeferred) { this._completeDeferred.resolve() } } this._scrollToBounds() } _scrollToBounds() { var _this$_bounceAction; if (this._inBounds()) { return } null === (_this$_bounceAction = this._bounceAction) || void 0 === _this$_bounceAction || _this$_bounceAction.call(this); this._setupBounce(); this._bounceAnimator.start() } _setupBounce() { const boundLocation = this._bounceLocation = this._boundLocation(); const bounceDistance = boundLocation - this._location; this._velocity = bounceDistance / BOUNCE_ACCELERATION_SUM } _inBounds(location) { location = void 0 !== location ? location : this._location; return this._boundLocation(location) === location } _crossBoundOnNextStep() { const location = this._location; const nextLocation = location + this._velocity; return location < this._minOffset && nextLocation >= this._minOffset || location > this._maxOffset && nextLocation <= this._maxOffset } _initHandler(e) { this._stopScrolling(); this._prepareThumbScrolling(e) } _stopScrolling() { (0, _common.deferRenderer)((() => { this._hideScrollbar(); this._inertiaAnimator.stop(); this._bounceAnimator.stop() }))() } _prepareThumbScrolling(e) { if ((0, _index.isDxMouseWheelEvent)(e.originalEvent)) { return } const $target = (0, _renderer.default)(e.originalEvent.target); const scrollbarClicked = this._isScrollbar($target); if (scrollbarClicked) { this._moveToMouseLocation(e) } this._thumbScrolling = scrollbarClicked || this._isThumb($target); this._crossThumbScrolling = !this._thumbScrolling && this._isAnyThumbScrolling($target); if (this._thumbScrolling) { this._scrollbar.feedbackOn() } } _isThumbScrollingHandler($target) { return this._isThumb($target) } _moveToMouseLocation(e) { const mouseLocation = e[`page${this._axis.toUpperCase()}`] - this._$element.offset()[this._prop]; const location = this._location + mouseLocation / this._containerToContentRatio() - (0, _size.getHeight)(this._$container) / 2; this._scrollStep(-Math.round(location)) } _startHandler() { this._showScrollbar() } _moveHandler(delta) { if (this._crossThumbScrolling) { return } if (this._thumbScrolling) { delta[this._axis] = -Math.round(delta[this._axis] / this._containerToContentRatio()) } this._scrollBy(delta) } _scrollBy(delta) { delta = delta[this._axis]; if (!this._inBounds()) { delta *= .5 } this._scrollStep(delta) } _scrollByHandler(delta) { if (!delta.x && !delta.y) { return } this._scrollBy(delta); this._scrollComplete() } _containerToContentRatio() { return this._scrollbar.containerToContentRatio() } _endHandler(velocity) { this._completeDeferred = (0, _deferred.Deferred)(); this._velocity = velocity[this._axis]; this._inertiaHandler(); this._resetThumbScrolling(); return this._completeDeferred.promise() } _inertiaHandler() { this._suppressInertia(); this._inertiaAnimator.start() } _suppressInertia() { if (!this._inertiaEnabled || this._thumbScrolling) { this._velocity = 0 } } _resetThumbScrolling() { this._thumbScrolling = false; this._crossThumbScrolling = false } _stopHandler() { if (this._thumbScrolling) { this._scrollComplete() } this._resetThumbScrolling(); this._scrollToBounds() } _disposeHandler() { this._stopScrolling(); this._$scrollbar.remove() } _updateHandler() { this._update(); this._moveToBounds() } _update() { this._stopScrolling(); return (0, _common.deferUpdate)((() => { this._resetScaleRatio(); this._updateLocation(); this._updateBounds(); this._updateScrollbar(); (0, _common.deferRender)((() => { this._moveScrollbar(); this._scrollbar.update() })) })) } _resetScaleRatio() { this._scaleRatio = null } _updateLocation() { this._location = ((0, _translator.locate)(this._$content)[this._prop] - this._$container[this._scrollProp]()) * this._getScaleRatio() } _updateBounds() { this._maxOffset = this._getMaxOffset(); this._minOffset = this._getMinOffset() } _getMaxOffset() { return 0 } _getMinOffset() { this._maxScrollPropValue = Math.max(this._contentSize() - this._containerSize(), 0); return -this._maxScrollPropValue } _updateScrollbar() { (0, _common.deferUpdater)((() => { const containerSize = this._containerSize(); const contentSize = this._contentSize(); const baseContainerSize = this._getBaseDimension(this._$container.get(0), this._dimension); const baseContentSize = this._getBaseDimension(this._$content.get(0), this._dimension); (0, _common.deferRender)((() => { this._scrollbar.option({ containerSize: containerSize, contentSize: contentSize, baseContainerSize: baseContainerSize, baseContentSize: baseContentSize, scaleRatio: this._getScaleRatio() }) })) }))() } _moveToBounds() { (0, _common.deferRenderer)((0, _common.deferUpdater)((0, _common.deferRenderer)((() => { const location = this._boundLocation(); const locationChanged = location !== this._location; this._location = location; this._move(); if (locationChanged) { var _this$_scrollAction; null === (_this$_scrollAction = this._scrollAction) || void 0 === _this$_scrollAction || _this$_scrollAction.call(this) } }))))() } _createActionsHandler(actions) { this._scrollAction = actions.scroll; this._bounceAction = actions.bounce } _showScrollbar() { this._scrollbar.option("visible", true) } _hideScrollbar() { this._scrollbar.option("visible", false) } _containerSize() { return this._getRealDimension(this._$container.get(0), this._dimension) } _contentSize() { const isOverflowHidden = "hidden" === this._$content.css(`overflow${this._axis.toUpperCase()}`); let contentSize = this._getRealDimension(this._$content.get(0), this._dimension); if (!isOverflowHidden) { const containerScrollSize = this._$content[0][`scroll${(0,_inflector.titleize)(this._dimension)}`] * this._getScaleRatio(); contentSize = Math.max(containerScrollSize, contentSize) } return contentSize } _validateEvent(e) { const $target = (0, _renderer.default)(e.originalEvent.target); return this._isThumb($target) || this._isScrollbar($target) } _isThumb($element) { return this._scrollByThumb && this._scrollbar.isThumb($element) } _isScrollbar($element) { return this._scrollByThumb && (null === $element || void 0 === $element ? void 0 : $element.is(this._$scrollbar)) } _reachedMin() { return Math.round(this._location - this._minOffset) <= 0 } _reachedMax() { return Math.round(this._location - this._maxOffset) >= 0 } _cursorEnterHandler() { this._resetScaleRatio(); this._updateScrollbar(); this._scrollbar.cursorEnter() } _cursorLeaveHandler() { this._scrollbar.cursorLeave() } dispose() {} } exports.Scroller = Scroller; let hoveredScrollable; let activeScrollable; class SimulatedStrategy extends(_class.default.inherit({})) { ctor(scrollable) { this._init(scrollable) } _init(scrollable) { this._component = scrollable; this._$element = scrollable.$element(); this._$container = (0, _renderer.default)(scrollable.container()); this._$wrapper = scrollable._$wrapper; this._$content = scrollable.$content(); this.option = scrollable.option.bind(scrollable); this._createActionByOption = scrollable._createActionByOption.bind(scrollable); this._isLocked = scrollable._isLocked.bind(scrollable); this._isDirection = scrollable._isDirection.bind(scrollable); this._allowedDirection = scrollable._allowedDirection.bind(scrollable); this._getMaxOffset = scrollable._getMaxOffset.bind(scrollable) } render() { this._$element.addClass("dx-scrollable-simulated"); this._createScrollers(); if (this.option("useKeyboard")) { this._$container.prop("tabIndex", 0) } this._attachKeyboardHandler(); this._attachCursorHandlers() } _createScrollers() { this._scrollers = {}; if (this._isDirection(HORIZONTAL)) { this._createScroller(HORIZONTAL) } if (this._isDirection(VERTICAL)) { this._createScroller(VERTICAL) } this._$element.toggleClass(SCROLLABLE_SCROLLBARS_ALWAYSVISIBLE, "always" === this.option("showScrollbar")) } _createScroller(direction) { this._scrollers[direction] = new Scroller(this._scrollerOptions(direction)) } _scrollerOptions(direction) { return { direction: direction, $content: this._$content, $container: this._$container, $wrapper: this._$wrapper, $element: this._$element, scrollByThumb: this.option("scrollByThumb"), scrollbarVisible: this.option("showScrollbar"), bounceEnabled: this.option("bounceEnabled"), inertiaEnabled: this.option("inertiaEnabled"), isAnyThumbScrolling: this._isAnyThumbScrolling.bind(this) } } _applyScaleRatio(targetLocation) { for (const direction in this._scrollers) { const prop = this._getPropByDirection(direction); if ((0, _type.isDefined)(targetLocation[prop])) { const scroller = this._scrollers[direction]; targetLocation[prop] *= scroller._getScaleRatio() } } return targetLocation } _isAnyThumbScrolling($target) { let result = false; this._eventHandler("isThumbScrolling", $target).done(((isThumbScrollingVertical, isThumbScrollingHorizontal) => { result = isThumbScrollingVertical || isThumbScrollingHorizontal })); return result } handleInit(e) { this._suppressDirections(e); this._eventForUserAction = e; this._eventHandler("init", e) } _suppressDirections(e) { if ((0, _index.isDxMouseWheelEvent)(e.originalEvent)) { this._prepareDirections(true); return } this._prepareDirections(); this._eachScroller((function(scroller, direction) { const $target = (0, _renderer.default)(e.originalEvent.target); const isValid = scroller._validateEvent(e) || this.option("scrollByContent") && this._isContent($target); this._validDirections[direction] = isValid })) } _isContent($element) { return !!$element.closest(this._$element).length } _prepareDirections(value) { value = value || false; this._validDirections = {}; this._validDirections[HORIZONTAL] = value; this._validDirections[VERTICAL] = value } _eachScroller(callback) { callback = callback.bind(this); (0, _iterator.each)(this._scrollers, ((direction, scroller) => { callback(scroller, direction) })) } handleStart(e) { this._eventForUserAction = e; this._eventHandler("start").done(this._startAction) } _saveActive() { activeScrollable = this } _resetActive() { if (activeScrollable === this) { activeScrollable = null } } handleMove(e) { var _e$preventDefault; if (this._isLocked()) { e.cancel = true; this._resetActive(); return } this._saveActive(); null === (_e$preventDefault = e.preventDefault) || void 0 === _e$preventDefault || _e$preventDefault.call(e); this._adjustDistance(e, e.delta); this._eventForUserAction = e; this._eventHandler("move", e.delta) } _adjustDistance(e, distance) { distance.x *= this._validDirections[HORIZONTAL]; distance.y *= this._validDirections[VERTICAL]; const devicePixelRatio = this._tryGetDevicePixelRatio(); if (devicePixelRatio && (0, _index.isDxMouseWheelEvent)(e.originalEvent)) { distance.x = Math.round(distance.x / devicePixelRatio * 100) / 100; distance.y = Math.round(distance.y / devicePixelRatio * 100) / 100 } } _tryGetDevicePixelRatio() { if ((0, _window.hasWindow)()) { return (0, _window.getWindow)().devicePixelRatio } } handleEnd(e) { var _e$originalEvent; this._resetActive(); this._refreshCursorState(null === (_e$originalEvent = e.originalEvent) || void 0 === _e$originalEvent ? void 0 : _e$originalEvent.target); this._adjustDistance(e, e.velocity); this._eventForUserAction = e; return this._eventHandler("end", e.velocity).done(this._endAction) } handleCancel(e) { this._resetActive(); this._eventForUserAction = e; return this._eventHandler("end", { x: 0, y: 0 }) } handleStop() { this._resetActive(); this._eventHandler("stop") } handleScroll() { var _this$_scrollAction2; this._updateRtlConfig(); null === (_this$_scrollAction2 = this._scrollAction) || void 0 === _this$_scrollAction2 || _this$_scrollAction2.call(this) } _attachKeyboardHandler() { _events_engine.default.off(this._$element, `.${SCROLLABLE_SIMULATED_KEYBOARD}`); if (!this.option("disabled") && this.option("useKeyboard")) { _events_engine.default.on(this._$element, (0, _index.addNamespace)("keydown", SCROLLABLE_SIMULATED_KEYBOARD), this._keyDownHandler.bind(this)) } } _keyDownHandler(e) { clearTimeout(this._updateHandlerTimeout); this._updateHandlerTimeout = setTimeout((() => { if ((0, _index.normalizeKeyName)(e) === KEY_CODES.TAB) { this._eachScroller((scroller => { scroller._updateHandler() })) } })); if (!this._$container.is(_dom_adapter.default.getActiveElement(this._$container.get(0)))) { return } let handled = true; switch ((0, _index.normalizeKeyName)(e)) { case KEY_CODES.DOWN: this._scrollByLine({ y: 1 }); break; case KEY_CODES.UP: this._scrollByLine({ y: -1 }); break; case KEY_CODES.RIGHT: this._scrollByLine({ x: 1 }); break; case KEY_CODES.LEFT: this._scrollByLine({ x: -1 }); break; case KEY_CODES.PAGE_DOWN: this._scrollByPage(1); break; case KEY_CODES.PAGE_UP: this._scrollByPage(-1); break; case KEY_CODES.HOME: this._scrollToHome(); break; case KEY_CODES.END: this._scrollToEnd(); break; default: handled = false } if (handled) { e.stopPropagation(); e.preventDefault() } } _scrollByLine(lines) { const devicePixelRatio = this._tryGetDevicePixelRatio(); let scrollOffset = 40; if (devicePixelRatio) { scrollOffset = Math.abs(scrollOffset / devicePixelRatio * 100) / 100 } this.scrollBy({ top: (lines.y || 0) * -scrollOffset, left: (lines.x || 0) * -scrollOffset }) } _scrollByPage(page) { const prop = this._wheelProp(); const dimension = this._dimensionByProp(prop); const distance = {}; const getter = "width" === dimension ? _size.getWidth : _size.getHeight; distance[prop] = page * -getter(this._$container); this.scrollBy(distance) } _dimensionByProp(prop) { return "left" === prop ? "width" : "height" } _getPropByDirection(direction) { return direction === HORIZONTAL ? "left" : "top" } _scrollToHome() { const prop = this._wheelProp(); const distance = {}; distance[prop] = 0; this._component.scrollTo(distance) } _scrollToEnd() { const prop = this._wheelProp(); const dimension = this._dimensionByProp(prop); const distance = {}; const getter = "width" === dimension ? _size.getWidth : _size.getHeight; distance[prop] = getter(this._$content) - getter(this._$container); this._component.scrollTo(distance) } createActions() { this._startAction = this._createActionHandler("onStart"); this._endAction = this._createActionHandler("onEnd"); this._updateAction = this._createActionHandler("onUpdated"); this._createScrollerActions() } _createScrollerActions() { this._scrollAction = this._createActionHandler("onScroll"); this._bounceAction = this._createActionHandler("onBounce"); this._eventHandler("createActions", { scroll: this._scrollAction, bounce: this._bounceAction }) } _createActionHandler(optionName) { const actionHandler = this._createActionByOption(optionName); return () => { actionHandler((0, _extend.extend)(this._createActionArgs(), arguments)) } } _createActionArgs() { const { horizontal: scrollerX, vertical: scrollerY } = this._scrollers; const offset = this._getScrollOffset(); this._scrollOffset = { top: scrollerY && offset.top, left: scrollerX && offset.left }; return { event: this._eventForUserAction, scrollOffset: this._scrollOffset, reachedLeft: null === scrollerX || void 0 === scrollerX ? void 0 : scrollerX._reachedMax(), reachedRight: null === scrollerX || void 0 === scrollerX ? void 0 : scrollerX._reachedMin(), reachedTop: null === scrollerY || void 0 === scrollerY ? void 0 : scrollerY._reachedMax(), reachedBottom: null === scrollerY || void 0 === scrollerY ? void 0 : scrollerY._reachedMin() } } _getScrollOffset() { return { top: -this.location().top, left: -this.location().left } } _eventHandler(eventName, location) { const args = [].slice.call(arguments).slice(1); const deferreds = (0, _iterator.map)(this._scrollers, (scroller => scroller[`_${eventName}Handler`].apply(scroller, args))); return _deferred.when.apply(_renderer.default, deferreds).promise() } location() { const location = (0, _translator.locate)(this._$content); location.top -= this._$container.scrollTop(); location.left -= this._$container.scrollLeft(); return location } disabledChanged() { this._attachCursorHandlers() } _attachCursorHandlers() { _events_engine.default.off(this._$element, `.${SCROLLABLE_SIMULATED_CURSOR}`); if (!this.option("disabled") && this._isHoverMode()) { _events_engine.default.on(this._$element, (0, _index.addNamespace)("mouseenter", SCROLLABLE_SIMULATED_CURSOR), this._cursorEnterHandler.bind(this)); _events_engine.default.on(this._$element, (0, _index.addNamespace)("mouseleave", SCROLLABLE_SIMULATED_CURSOR), this._cursorLeaveHandler.bind(this)) } } _isHoverMode() { return "onHover" === this.option("showScrollbar") } _cursorEnterHandler(e) { e = e || {}; e.originalEvent = e.originalEvent || {}; if (activeScrollable || e.originalEvent._hoverHandled) { return } if (hoveredScrollable) { hoveredScrollable._cursorLeaveHandler() } hoveredScrollable = this; this._eventHandler("cursorEnter"); e.originalEvent._hoverHandled = true } _cursorLeaveHandler(e) { if (hoveredScrollable !== this || activeScrollable === hoveredScrollable) { return } this._eventHandler("cursorLeave"); hoveredScrollable = null; this._refreshCursorState(null === e || void 0 === e ? void 0 : e.relatedTarget) } _refreshCursorState(target) { if (!this._isHoverMode() && (!target || activeScrollable)) { return } const $target = (0, _renderer.default)(target); const $scrollable = $target.closest(".dx-scrollable-simulated:not(.dx-state-disabled)"); const targetScrollable = $scrollable.length && $scrollable.data(SCROLLABLE_STRATEGY); if (hoveredScrollable && hoveredScrollable !== targetScrollable) { hoveredScrollable._cursorLeaveHandler() } if (targetScrollable) { targetScrollable._cursorEnterHandler() } } update() { const result = this._eventHandler("update").done(this._updateAction); return (0, _deferred.when)(result, (0, _common.deferUpdate)((() => { const allowedDirections = this._allowedDirections(); (0, _common.deferRender)((() => { let touchDirection = allowedDirections.vertical ? "pan-x" : ""; touchDirection = allowedDirections.horizontal ? "pan-y" : touchDirection; touchDirection = allowedDirections.vertical && allowedDirections.horizontal ? "none" : touchDirection; this._$container.css("touchAction", touchDirection) })); return (0, _deferred.when)().promise() }))) } _allowedDirections() { const bounceEnabled = this.option("bounceEnabled"); const verticalScroller = this._scrollers[VERTICAL]; const horizontalScroller = this._scrollers[HORIZONTAL]; return { vertical: verticalScroller && (verticalScroller._minOffset < 0 || bounceEnabled), horizontal: horizontalScroller && (horizontalScroller._minOffset < 0 || bounceEnabled) } } _updateBounds() { var _this$_scrollers$HORI; null === (_this$_scrollers$HORI = this._scrollers[HORIZONTAL]) || void 0 === _this$_scrollers$HORI || _this$_scrollers$HORI._updateBounds() } _isHorizontalAndRtlEnabled() { return this.option("rtlEnabled") && this.option("direction") !== VERTICAL } updateRtlPosition(needInitializeRtlConfig) { if (needInitializeRtlConfig) { this._rtlConfig = { scrollRight: 0, clientWidth: this._$container.get(0).clientWidth, windowPixelRatio: this._getWindowDevicePixelRatio() } } this._updateBounds(); if (this._isHorizontalAndRtlEnabled()) { let scrollLeft = this._getMaxOffset().left - this._rtlConfig.scrollRight; if (scrollLeft <= 0) { scrollLeft = 0; this._rtlConfig.scrollRight = this._getMaxOffset().left } if (this._getScrollOffset().left !== scrollLeft) { this._rtlConfig.skipUpdating = true; this._component.scrollTo({ left: scrollLeft }); this._rtlConfig.skipUpdating = false } } } _updateRtlConfig() { if (this._isHorizontalAndRtlEnabled() && !this._rtlConfig.skipUpdating) { const { clientWidth: clientWidth, scrollLeft: scrollLeft } = this._$container.get(0); const windowPixelRatio = this._getWindowDevicePixelRatio(); if (this._rtlConfig.windowPixelRatio === windowPixelRatio && this._rtlConfig.clientWidth === clientWidth) { this._rtlConfig.scrollRight = this._getMaxOffset().left - scrollLeft } this._rtlConfig.clientWidth = clientWidth; this._rtlConfig.windowPixelRatio = windowPixelRatio } } _getWindowDevicePixelRatio() { return (0, _window.hasWindow)() ? (0, _window.getWindow)().devicePixelRatio : 1 } scrollBy(distance) { var _this$_startAction, _this$_endAction; const verticalScroller = this._scrollers[VERTICAL]; const horizontalScroller = this._scrollers[HORIZONTAL]; if (verticalScroller) { distance.top = verticalScroller._boundLocation(distance.top + verticalScroller._location) - verticalScroller._location } if (horizontalScroller) { distance.left = horizontalScroller._boundLocation(distance.left + horizontalScroller._location) - horizontalScroller._location } this._prepareDirections(true); null === (_this$_startAction = this._startAction) || void 0 === _this$_startAction || _this$_startAction.call(this); this._eventHandler("scrollBy", { x: distance.left, y: distance.top }); null === (_this$_endAction = this._endAction) || void 0 === _this$_endAction || _this$_endAction.call(this); this._updateRtlConfig() } validate(e) { if ((0, _index.isDxMouseWheelEvent)(e) && (0, _index.isCommandKeyPressed)(e)) { return false } if (this.option("disabled")) { return false } if (this.option("bounceEnabled")) { return true } return (0, _index.isDxMouseWheelEvent)(e) ? this._validateWheel(e) : this._validateMove(e) } _validateWheel(e) { const scroller = this._scrollers[this._wheelDirection(e)]; const reachedMin = scroller._reachedMin(); const reachedMax = scroller._reachedMax(); const contentGreaterThanContainer = !reachedMin || !reachedMax; const locatedNotAtBound = !reachedMin && !reachedMax; const scrollFromMin = reachedMin && e.delta > 0; const scrollFromMax = reachedMax && e.delta < 0; let validated = contentGreaterThanContainer && (locatedNotAtBound || scrollFromMin || scrollFromMax); validated = validated || void 0 !== this._validateWheelTimer; if (validated) { clearTimeout(this._validateWheelTimer); this._validateWheelTimer = setTimeout((() => { this._validateWheelTimer = void 0 }), 500) } return validated } _validateMove(e) { if (!this.option("scrollByContent") && !(0, _renderer.default)(e.target).closest(".dx-scrollable-scrollbar").length) { return false } return this._allowedDirection() } getDirection(e) { return (0, _index.isDxMouseWheelEvent)(e) ? this._wheelDirection(e) : this._allowedDirection() } _wheelProp() { return this._wheelDirection() === HORIZONTAL ? "left" : "top" } _wheelDirection(e) { switch (this.option("direction")) { case HORIZONTAL: return HORIZONTAL; case VERTICAL: return VERTICAL; default: return null !== e && void 0 !== e && e.shiftKey ? HORIZONTAL : VERTICAL } } dispose() { this._resetActive(); if (hoveredScrollable === this) { hoveredScrollable = null } this._eventHandler("dispose"); this._detachEventHandlers(); this._$element.removeClass("dx-scrollable-simulated"); this._eventForUserAction = null; clearTimeout(this._validateWheelTimer); clearTimeout(this._updateHandlerTimeout) } _detachEventHandlers() { _events_engine.default.off(this._$element, `.${SCROLLABLE_SIMULATED_CURSOR}`); _events_engine.default.off(this._$container, `.${SCROLLABLE_SIMULATED_KEYBOARD}`) } } exports.SimulatedStrategy = SimulatedStrategy;