UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

475 lines (472 loc) • 17.9 kB
/** * DevExtreme (cjs/__internal/ui/scroll_view/m_scrollable.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 _events_engine = _interopRequireDefault(require("../../../common/core/events/core/events_engine")); var _emitterGesture = _interopRequireDefault(require("../../../common/core/events/gesture/emitter.gesture.scroll")); var _index = require("../../../common/core/events/utils/index"); var _component_registrator = _interopRequireDefault(require("../../../core/component_registrator")); var _devices = _interopRequireDefault(require("../../../core/devices")); var _element = require("../../../core/element"); var _renderer = _interopRequireDefault(require("../../../core/renderer")); var _browser = _interopRequireDefault(require("../../../core/utils/browser")); var _common = require("../../../core/utils/common"); var _deferred = require("../../../core/utils/deferred"); var _size = require("../../../core/utils/size"); var _type = require("../../../core/utils/type"); var _window = require("../../../core/utils/window"); var _dom_component = _interopRequireDefault(require("../../core/widget/dom_component")); var _get_element_location_internal = require("../../ui/scroll_view/utils/get_element_location_internal"); var _m_support = _interopRequireDefault(require("../../core/utils/m_support")); var _m_scrollable = require("./m_scrollable.device"); var _m_scrollable2 = _interopRequireDefault(require("./m_scrollable.native")); var _m_scrollable3 = require("./m_scrollable.simulated"); 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 SCROLLABLE = "dxScrollable"; const SCROLLABLE_STRATEGY = "dxScrollableStrategy"; const SCROLLABLE_CLASS = "dx-scrollable"; const SCROLLABLE_DISABLED_CLASS = "dx-scrollable-disabled"; const SCROLLABLE_CONTAINER_CLASS = "dx-scrollable-container"; const SCROLLABLE_WRAPPER_CLASS = "dx-scrollable-wrapper"; const SCROLLABLE_CONTENT_CLASS = "dx-scrollable-content"; const VERTICAL = "vertical"; const HORIZONTAL = "horizontal"; const BOTH = "both"; class Scrollable extends _dom_component.default { _getDefaultOptions() { return _extends({}, super._getDefaultOptions(), { disabled: false, onScroll: null, direction: VERTICAL, showScrollbar: "onScroll", useNative: true, bounceEnabled: true, scrollByContent: true, scrollByThumb: false, onUpdated: null, onStart: null, onEnd: null, onBounce: null, useSimulatedScrollbar: false, useKeyboard: true, inertiaEnabled: true, updateManually: false, _onVisibilityChanged: _common.noop }) } _defaultOptionsRules() { return super._defaultOptionsRules().concat((0, _m_scrollable.deviceDependentOptions)(), [{ device: () => _m_support.default.nativeScrolling && "android" === _devices.default.real().platform && !_browser.default.mozilla, options: { useSimulatedScrollbar: true } }]) } _initOptions(options) { super._initOptions(options); if (!("useSimulatedScrollbar" in options)) { this._setUseSimulatedScrollbar() } } _setUseSimulatedScrollbar() { if (!this.initialOption("useSimulatedScrollbar")) { this.option("useSimulatedScrollbar", !this.option("useNative")) } } _init() { super._init(); this._initScrollableMarkup(); this._locked = false } _visibilityChanged(visible) { if (visible) { this.update(); this._updateRtlPosition(); this._savedScrollOffset && this.scrollTo(this._savedScrollOffset); delete this._savedScrollOffset; const { _onVisibilityChanged: onVisibilityChanged } = this.option(); null === onVisibilityChanged || void 0 === onVisibilityChanged || onVisibilityChanged(this) } else { this._savedScrollOffset = this.scrollOffset() } } _initScrollableMarkup() { const $element = this.$element().addClass("dx-scrollable"); const $container = (0, _renderer.default)("<div>").addClass("dx-scrollable-container"); const $wrapper = (0, _renderer.default)("<div>").addClass("dx-scrollable-wrapper"); const $content = (0, _renderer.default)("<div>").addClass("dx-scrollable-content"); this._$container = $container; this._$wrapper = $wrapper; this._$content = $content; $content.append($element.contents()).appendTo($container); $container.appendTo($wrapper); $wrapper.appendTo($element) } _dimensionChanged() { this.update(); this._updateRtlPosition() } _initMarkup() { super._initMarkup(); this._renderDirection() } _render() { this._renderStrategy(); this._attachEventHandlers(); this._renderDisabledState(); this._createActions(); this.update(); super._render(); this._updateRtlPosition(true) } _updateRtlPosition(needInitializeRtlConfig) { this._strategy.updateRtlPosition(needInitializeRtlConfig) } _getMaxOffset() { const { scrollWidth: scrollWidth, clientWidth: clientWidth, scrollHeight: scrollHeight, clientHeight: clientHeight } = (0, _renderer.default)(this.container()).get(0); return { left: scrollWidth - clientWidth, top: scrollHeight - clientHeight } } _attachEventHandlers() { const strategy = this._strategy; const initEventData = { getDirection: strategy.getDirection.bind(strategy), validate: this._validate.bind(this), isNative: this.option("useNative"), scrollTarget: this._$container }; _events_engine.default.off(this._$wrapper, `.${SCROLLABLE}`); _events_engine.default.on(this._$wrapper, (0, _index.addNamespace)(_emitterGesture.default.init, SCROLLABLE), initEventData, this._initHandler.bind(this)); _events_engine.default.on(this._$wrapper, (0, _index.addNamespace)(_emitterGesture.default.start, SCROLLABLE), strategy.handleStart.bind(strategy)); _events_engine.default.on(this._$wrapper, (0, _index.addNamespace)(_emitterGesture.default.move, SCROLLABLE), strategy.handleMove.bind(strategy)); _events_engine.default.on(this._$wrapper, (0, _index.addNamespace)(_emitterGesture.default.end, SCROLLABLE), strategy.handleEnd.bind(strategy)); _events_engine.default.on(this._$wrapper, (0, _index.addNamespace)(_emitterGesture.default.cancel, SCROLLABLE), strategy.handleCancel.bind(strategy)); _events_engine.default.on(this._$wrapper, (0, _index.addNamespace)(_emitterGesture.default.stop, SCROLLABLE), strategy.handleStop.bind(strategy)); _events_engine.default.off(this._$container, `.${SCROLLABLE}`); _events_engine.default.on(this._$container, (0, _index.addNamespace)("scroll", SCROLLABLE), strategy.handleScroll.bind(strategy)) } _validate(e) { if (this._isLocked()) { return false } this._updateIfNeed(); return this._moveIsAllowed(e) } _moveIsAllowed(e) { return this._strategy.validate(e) } handleMove(e) { this._strategy.handleMove(e) } _prepareDirections(value) { this._strategy._prepareDirections(value) } _initHandler() { const strategy = this._strategy; strategy.handleInit.apply(strategy, arguments) } _renderDisabledState() { const { disabled: disabled } = this.option(); this.$element().toggleClass("dx-scrollable-disabled", disabled); if (this.option("disabled")) { this._lock() } else { this._unlock() } } _renderDirection() { const { direction: direction } = this.option(); this.$element().removeClass(`dx-scrollable-${HORIZONTAL}`).removeClass(`dx-scrollable-${VERTICAL}`).removeClass(`dx-scrollable-${BOTH}`).addClass(`dx-scrollable-${direction}`) } _renderStrategy() { this._createStrategy(); this._strategy.render(); this.$element().data(SCROLLABLE_STRATEGY, this._strategy) } _createStrategy() { this._strategy = this.option("useNative") ? new _m_scrollable2.default(this) : new _m_scrollable3.SimulatedStrategy(this) } _createActions() { var _this$_strategy; null === (_this$_strategy = this._strategy) || void 0 === _this$_strategy || _this$_strategy.createActions() } _clean() { var _this$_strategy2; null === (_this$_strategy2 = this._strategy) || void 0 === _this$_strategy2 || _this$_strategy2.dispose() } _optionChanged(args) { var _this$_strategy3; switch (args.name) { case "onStart": case "onEnd": case "onUpdated": case "onScroll": case "onBounce": this._createActions(); break; case "direction": this._resetInactiveDirection(); this._invalidate(); break; case "useNative": this._setUseSimulatedScrollbar(); this._invalidate(); break; case "inertiaEnabled": case "scrollByThumb": case "bounceEnabled": case "useKeyboard": case "showScrollbar": case "useSimulatedScrollbar": this._invalidate(); break; case "disabled": this._renderDisabledState(); null === (_this$_strategy3 = this._strategy) || void 0 === _this$_strategy3 || _this$_strategy3.disabledChanged(); break; case "updateManually": case "scrollByContent": case "_onVisibilityChanged": break; case "width": super._optionChanged(args); this._updateRtlPosition(); break; default: super._optionChanged(args) } } _resetInactiveDirection() { const inactiveProp = this._getInactiveProp(); if (!inactiveProp || !(0, _window.hasWindow)()) { return } const scrollOffset = this.scrollOffset(); scrollOffset[inactiveProp] = 0; this.scrollTo(scrollOffset) } _getInactiveProp() { const { direction: direction } = this.option(); if (direction === VERTICAL) { return "left" } if (direction === HORIZONTAL) { return "top" } } _location() { return this._strategy.location() } _normalizeLocation(location) { if ((0, _type.isPlainObject)(location)) { const left = (0, _common.ensureDefined)(location.left, location.x); const top = (0, _common.ensureDefined)(location.top, location.y); return { left: (0, _type.isDefined)(left) ? -left : void 0, top: (0, _type.isDefined)(top) ? -top : void 0 } } const { direction: direction } = this.option(); return { left: direction !== VERTICAL ? -location : void 0, top: direction !== HORIZONTAL ? -location : void 0 } } _isLocked() { return this._locked } _lock() { this._locked = true } _unlock() { if (!this.option("disabled")) { this._locked = false } } _isDirection(direction) { const { direction: current } = this.option(); if (direction === VERTICAL) { return current !== HORIZONTAL } if (direction === HORIZONTAL) { return current !== VERTICAL } return current === direction } _updateAllowedDirection() { const allowedDirections = this._strategy._allowedDirections(); if (this._isDirection(BOTH) && allowedDirections.vertical && allowedDirections.horizontal) { this._allowedDirectionValue = BOTH } else if (this._isDirection(HORIZONTAL) && allowedDirections.horizontal) { this._allowedDirectionValue = HORIZONTAL } else if (this._isDirection(VERTICAL) && allowedDirections.vertical) { this._allowedDirectionValue = VERTICAL } else { this._allowedDirectionValue = null } } _allowedDirection() { return this._allowedDirectionValue } $content() { return this._$content } content() { return (0, _element.getPublicElement)(this._$content) } container() { return (0, _element.getPublicElement)(this._$container) } scrollOffset() { return this._strategy._getScrollOffset() } _isRtlNativeStrategy() { const { useNative: useNative, rtlEnabled: rtlEnabled } = this.option(); return useNative && rtlEnabled } scrollTop() { return this.scrollOffset().top } scrollLeft() { return this.scrollOffset().left } clientHeight() { return (0, _size.getHeight)(this._$container) } scrollHeight() { return (0, _size.getOuterHeight)(this.$content()) } clientWidth() { return (0, _size.getWidth)(this._$container) } scrollWidth() { return (0, _size.getOuterWidth)(this.$content()) } update() { if (!this._strategy) { return } return (0, _deferred.when)(this._strategy.update()).done((() => { this._updateAllowedDirection() })) } scrollBy(distance) { distance = this._normalizeLocation(distance); if (!distance.top && !distance.left) { return } this._updateIfNeed(); this._strategy.scrollBy(distance) } scrollTo(targetLocation) { if (!(0, _window.hasWindow)()) { return } targetLocation = this._normalizeLocation(targetLocation); this._updateIfNeed(); let location = this._location(); const { useNative: useNative } = this.option(); if (!useNative) { const strategy = this._strategy; targetLocation = strategy._applyScaleRatio(targetLocation); location = strategy._applyScaleRatio(location) } if (this._isRtlNativeStrategy()) { location.left -= this._getMaxOffset().left } const distance = this._normalizeLocation({ left: location.left - (0, _common.ensureDefined)(targetLocation.left, location.left), top: location.top - (0, _common.ensureDefined)(targetLocation.top, location.top) }); if (!distance.top && !distance.left) { return } this._strategy.scrollBy(distance) } scrollToElement(element, offset) { const $element = (0, _renderer.default)(element); const elementInsideContent = this.$content().find(element).length; const elementIsInsideContent = $element.parents(".dx-scrollable").length - $element.parents(".dx-scrollable-content").length === 0; if (!elementInsideContent || !elementIsInsideContent) { return } const scrollPosition = { top: 0, left: 0 }; const { direction: direction } = this.option(); if (direction !== VERTICAL) { scrollPosition.left = this.getScrollElementPosition($element, HORIZONTAL, offset) } if (direction !== HORIZONTAL) { scrollPosition.top = this.getScrollElementPosition($element, VERTICAL, offset) } this.scrollTo(scrollPosition) } getScrollElementPosition($element, direction, offset) { const scrollOffset = this.scrollOffset(); return (0, _get_element_location_internal.getElementLocationInternal)($element.get(0), direction, (0, _renderer.default)(this.container()).get(0), scrollOffset, offset) } _updateIfNeed() { if (!this.option("updateManually")) { this.update() } } _useTemplates() { return false } isRenovated() { return !!Scrollable.IS_RENOVATED_WIDGET } }(0, _component_registrator.default)(SCROLLABLE, Scrollable); var _default = exports.default = Scrollable;