"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.ScrollableNative = exports.ScrollableBaseNative = exports.default = void 0;

var _classnames = _interopRequireDefault(require("classnames"));

var _ScrollableNative = require("@enact/ui/Scrollable/ScrollableNative");

var _spotlight = _interopRequireWildcard(require("@enact/spotlight"));

var _target = require("@enact/spotlight/src/target");

var _util = require("@enact/core/util");

var _platform = _interopRequireDefault(require("@enact/core/platform"));

var _handle = require("@enact/core/handle");

var _propTypes = _interopRequireDefault(require("prop-types"));

var _react = _interopRequireWildcard(require("react"));

var _reactDom = _interopRequireDefault(require("react-dom"));

var _SpotlightContainerDecorator = _interopRequireDefault(require("@enact/spotlight/SpotlightContainerDecorator"));

var _$L = _interopRequireDefault(require("../internal/$L"));

var _Scrollbar = _interopRequireDefault(require("./Scrollbar"));

var _Skinnable = _interopRequireDefault(require("../Skinnable"));

var _OverscrollEffect = _interopRequireDefault(require("./OverscrollEffect.less"));

var _Scrollbar2 = _interopRequireDefault(require("./Scrollbar.less"));

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }

function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }

function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }

var epsilon = _ScrollableNative.constants.epsilon,
    isPageDown = _ScrollableNative.constants.isPageDown,
    isPageUp = _ScrollableNative.constants.isPageUp,
    overscrollTypeDone = _ScrollableNative.constants.overscrollTypeDone,
    overscrollTypeNone = _ScrollableNative.constants.overscrollTypeNone,
    overscrollTypeOnce = _ScrollableNative.constants.overscrollTypeOnce,
    paginationPageMultiplier = _ScrollableNative.constants.paginationPageMultiplier,
    scrollWheelPageMultiplierForMaxPixel = _ScrollableNative.constants.scrollWheelPageMultiplierForMaxPixel,
    overscrollRatioPrefix = '--scrollable-overscroll-ratio-',
    overscrollTimeout = 300,
    reverseDirections = {
  down: 'up',
  up: 'down'
};

var navigableFilter = function navigableFilter(elem) {
  if (!_spotlight.default.getPointerMode() && // ignore containers passed as their id
  typeof elem !== 'string' && elem.classList.contains(_Scrollbar2.default.scrollButton)) {
    return false;
  }
};

var configureSpotlightContainer = function configureSpotlightContainer(_ref) {
  var spotlightId = _ref['data-spotlight-id'],
      focusableScrollbar = _ref.focusableScrollbar;

  _spotlight.default.set(spotlightId, {
    navigableFilter: focusableScrollbar ? null : navigableFilter
  });
};
/**
 * A Moonstone-styled native component that provides horizontal and vertical scrollbars.
 *
 * @class ScrollableBaseNative
 * @memberof moonstone/ScrollableNative
 * @extends ui/Scrollable.ScrollableBaseNative
 * @ui
 * @private
 */


var ScrollableBaseNative =
/*#__PURE__*/
function (_Component) {
  _inherits(ScrollableBaseNative, _Component);

  function ScrollableBaseNative(props) {
    var _this;

    _classCallCheck(this, ScrollableBaseNative);

    _this = _possibleConstructorReturn(this, (ScrollableBaseNative.__proto__ || Object.getPrototypeOf(ScrollableBaseNative)).call(this, props));
    Object.defineProperty(_assertThisInitialized(_this), "isWheeling", {
      configurable: true,
      enumerable: true,
      writable: true,
      value: false
    });
    Object.defineProperty(_assertThisInitialized(_this), "lastScrollPositionOnFocus", {
      configurable: true,
      enumerable: true,
      writable: true,
      value: null
    });
    Object.defineProperty(_assertThisInitialized(_this), "indexToFocus", {
      configurable: true,
      enumerable: true,
      writable: true,
      value: null
    });
    Object.defineProperty(_assertThisInitialized(_this), "nodeToFocus", {
      configurable: true,
      enumerable: true,
      writable: true,
      value: null
    });
    Object.defineProperty(_assertThisInitialized(_this), "overscrollRefs", {
      configurable: true,
      enumerable: true,
      writable: true,
      value: {
        horizontal: null,
        vertical: null
      }
    });
    Object.defineProperty(_assertThisInitialized(_this), "overscrollJobs", {
      configurable: true,
      enumerable: true,
      writable: true,
      value: {
        horizontal: {
          before: null,
          after: null
        },
        vertical: {
          before: null,
          after: null
        } // browser native scrolling

      }
    });
    Object.defineProperty(_assertThisInitialized(_this), "resetPosition", {
      configurable: true,
      enumerable: true,
      writable: true,
      value: null
    });
    Object.defineProperty(_assertThisInitialized(_this), "isVoiceControl", {
      configurable: true,
      enumerable: true,
      writable: true,
      value: false
    });
    Object.defineProperty(_assertThisInitialized(_this), "voiceControlDirection", {
      configurable: true,
      enumerable: true,
      writable: true,
      value: 'vertical'
    });
    Object.defineProperty(_assertThisInitialized(_this), "onMouseDown", {
      configurable: true,
      enumerable: true,
      writable: true,
      value: function value(ev) {
        if (_this.props['data-spotlight-container-disabled']) {
          ev.preventDefault();
        } else {
          _this.childRef.setContainerDisabled(false);
        }
      }
    });
    Object.defineProperty(_assertThisInitialized(_this), "onFlick", {
      configurable: true,
      enumerable: true,
      writable: true,
      value: function value(_ref2) {
        var direction = _ref2.direction;

        var bounds = _this.uiRef.getScrollBounds();

        var focusedItem = _spotlight.default.getCurrent();

        if (focusedItem) {
          focusedItem.blur();
        }

        if ((direction === 'vertical' && _this.uiRef.canScrollVertically(bounds) || direction === 'horizontal' && _this.uiRef.canScrollHorizontally(bounds)) && !_this.props['data-spotlight-container-disabled']) {
          _this.childRef.setContainerDisabled(true);
        }
      }
    });
    Object.defineProperty(_assertThisInitialized(_this), "onMouseOver", {
      configurable: true,
      enumerable: true,
      writable: true,
      value: function value() {
        _this.resetPosition = _this.uiRef.childRef.containerRef.scrollTop;
      }
    });
    Object.defineProperty(_assertThisInitialized(_this), "onMouseMove", {
      configurable: true,
      enumerable: true,
      writable: true,
      value: function value() {
        if (_this.resetPosition !== null) {
          var childContainerRef = _this.uiRef.childRef.containerRef;
          childContainerRef.style.scrollBehavior = null;
          childContainerRef.scrollTop = _this.resetPosition;
          childContainerRef.style.scrollBehavior = 'smooth';
          _this.resetPosition = null;
        }
      }
    });
    Object.defineProperty(_assertThisInitialized(_this), "onWheel", {
      configurable: true,
      enumerable: true,
      writable: true,
      value: function value(ev) {
        var overscrollEffectRequired = _this.props.overscrollEffectOn.wheel,
            bounds = _this.uiRef.getScrollBounds(),
            canScrollHorizontally = _this.uiRef.canScrollHorizontally(bounds),
            canScrollVertically = _this.uiRef.canScrollVertically(bounds),
            eventDeltaMode = ev.deltaMode,
            eventDelta = -ev.wheelDeltaY || ev.deltaY;

        var delta = 0,
            needToHideThumb = false;

        if (typeof window !== 'undefined') {
          window.document.activeElement.blur();
        }

        _this.uiRef.showThumb(bounds); // FIXME This routine is a temporary support for horizontal wheel scroll.
        // FIXME If web engine supports horizontal wheel, this routine should be refined or removed.


        if (canScrollVertically) {
          // This routine handles wheel events on scrollbars for vertical scroll.
          if (eventDelta < 0 && _this.uiRef.scrollTop > 0 || eventDelta > 0 && _this.uiRef.scrollTop < bounds.maxTop) {
            var _this$uiRef = _this.uiRef,
                horizontalScrollbarRef = _this$uiRef.horizontalScrollbarRef,
                verticalScrollbarRef = _this$uiRef.verticalScrollbarRef;

            if (!_this.isWheeling) {
              if (!_this.props['data-spotlight-container-disabled']) {
                _this.childRef.setContainerDisabled(true);
              }

              _this.isWheeling = true;
            } // Not to check if ev.target is a descendant of a wrapped component which may have a lot of nodes in it.


            if (horizontalScrollbarRef && horizontalScrollbarRef.getContainerRef().contains(ev.target) || verticalScrollbarRef && verticalScrollbarRef.getContainerRef().contains(ev.target)) {
              delta = _this.uiRef.calculateDistanceByWheel(eventDeltaMode, eventDelta, bounds.clientHeight * scrollWheelPageMultiplierForMaxPixel);
              needToHideThumb = !delta;
            } else if (overscrollEffectRequired) {
              _this.uiRef.checkAndApplyOverscrollEffect('vertical', eventDelta > 0 ? 'after' : 'before', overscrollTypeOnce);
            }
          } else {
            if (overscrollEffectRequired && (eventDelta < 0 && _this.uiRef.scrollTop <= 0 || eventDelta > 0 && _this.uiRef.scrollTop >= bounds.maxTop)) {
              _this.uiRef.applyOverscrollEffect('vertical', eventDelta > 0 ? 'after' : 'before', overscrollTypeOnce, 1);
            }

            needToHideThumb = true;
          }
        } else if (canScrollHorizontally) {
          // this routine handles wheel events on any children for horizontal scroll.
          if (eventDelta < 0 && _this.uiRef.scrollLeft > 0 || eventDelta > 0 && _this.uiRef.scrollLeft < bounds.maxLeft) {
            if (!_this.isWheeling) {
              if (!_this.props['data-spotlight-container-disabled']) {
                _this.childRef.setContainerDisabled(true);
              }

              _this.isWheeling = true;
            }

            delta = _this.uiRef.calculateDistanceByWheel(eventDeltaMode, eventDelta, bounds.clientWidth * scrollWheelPageMultiplierForMaxPixel);
            needToHideThumb = !delta;
          } else {
            if (overscrollEffectRequired && (eventDelta < 0 && _this.uiRef.scrollLeft <= 0 || eventDelta > 0 && _this.uiRef.scrollLeft >= bounds.maxLeft)) {
              _this.uiRef.applyOverscrollEffect('horizontal', eventDelta > 0 ? 'after' : 'before', overscrollTypeOnce, 1);
            }

            needToHideThumb = true;
          }
        }

        if (delta !== 0) {
          /* prevent native scrolling feature for vertical direction */
          ev.preventDefault();
          var direction = Math.sign(delta); // Not to accumulate scroll position if wheel direction is different from hold direction

          if (direction !== _this.uiRef.wheelDirection) {
            _this.uiRef.isScrollAnimationTargetAccumulated = false;
            _this.uiRef.wheelDirection = direction;
          }

          _this.uiRef.scrollToAccumulatedTarget(delta, canScrollVertically, overscrollEffectRequired);
        }

        if (needToHideThumb) {
          _this.uiRef.startHidingThumb();
        }
      }
    });
    Object.defineProperty(_assertThisInitialized(_this), "start", {
      configurable: true,
      enumerable: true,
      writable: true,
      value: function value(animate) {
        if (!animate) {
          _this.focusOnItem();
        }
      }
    });
    Object.defineProperty(_assertThisInitialized(_this), "startScrollOnFocus", {
      configurable: true,
      enumerable: true,
      writable: true,
      value: function value(pos) {
        if (pos) {
          var top = pos.top,
              left = pos.left,
              bounds = _this.uiRef.getScrollBounds(),
              scrollHorizontally = bounds.maxLeft > 0 && Math.abs(left - _this.uiRef.scrollLeft) > epsilon,
              scrollVertically = bounds.maxTop > 0 && Math.abs(top - _this.uiRef.scrollTop) > epsilon;

          if (scrollHorizontally || scrollVertically) {
            _this.uiRef.start({
              targetX: left,
              targetY: top,
              animate: _this.animateOnFocus,
              overscrollEffect: _this.props.overscrollEffectOn[_this.uiRef.lastInputType] && (!_this.childRef.shouldPreventOverscrollEffect || !_this.childRef.shouldPreventOverscrollEffect())
            });

            _this.lastScrollPositionOnFocus = pos;
          }
        }
      }
    });
    Object.defineProperty(_assertThisInitialized(_this), "calculateAndScrollTo", {
      configurable: true,
      enumerable: true,
      writable: true,
      value: function value() {
        var spotItem = _spotlight.default.getCurrent(),
            positionFn = _this.childRef.calculatePositionOnFocus,
            containerRef = _this.uiRef.childRef.containerRef;

        if (spotItem && positionFn && containerRef && containerRef.contains(spotItem)) {
          var lastPos = _this.lastScrollPositionOnFocus;
          var pos; // If scroll animation is ongoing, we need to pass last target position to
          // determine correct scroll position.

          if (_this.uiRef.scrolling && lastPos) {
            pos = positionFn({
              item: spotItem,
              scrollPosition: _this.props.direction !== 'horizontal' ? lastPos.top : lastPos.left
            });
          } else {
            // scrollInfo passes in current `scrollHeight` and `scrollTop` before calculations
            var scrollInfo = {
              previousScrollHeight: _this.uiRef.bounds.scrollHeight,
              scrollTop: _this.uiRef.scrollTop
            };
            pos = positionFn({
              item: spotItem,
              scrollInfo: scrollInfo
            });
          }

          if (pos && (pos.left !== _this.uiRef.scrollLeft || pos.top !== _this.uiRef.scrollTop)) {
            _this.startScrollOnFocus(pos);
          } // update `scrollHeight`


          _this.uiRef.bounds.scrollHeight = _this.uiRef.getScrollBounds().scrollHeight;
        }
      }
    });
    Object.defineProperty(_assertThisInitialized(_this), "onFocus", {
      configurable: true,
      enumerable: true,
      writable: true,
      value: function value(ev) {
        var isDragging = _this.uiRef.isDragging,
            shouldPreventScrollByFocus = _this.childRef.shouldPreventScrollByFocus ? _this.childRef.shouldPreventScrollByFocus() : false;

        if (!_spotlight.default.getPointerMode()) {
          _this.alertThumb();
        }

        if (!(shouldPreventScrollByFocus || _spotlight.default.getPointerMode() || isDragging)) {
          var item = ev.target,
              positionFn = _this.childRef.calculatePositionOnFocus,
              spotItem = _spotlight.default.getCurrent();

          if (item && item === spotItem && positionFn) {
            _this.calculateAndScrollTo();
          }
        } else if (_this.childRef.setLastFocusedNode) {
          _this.childRef.setLastFocusedNode(ev.target);
        }
      }
    });
    Object.defineProperty(_assertThisInitialized(_this), "scrollByPage", {
      configurable: true,
      enumerable: true,
      writable: true,
      value: function value(direction) {
        // Only scroll by page when the vertical scrollbar is visible. Otherwise, treat the
        // scroller as a plain container
        if (!_this.uiRef.state.isVerticalScrollbarVisible) {
          return false;
        }

        var _this$uiRef2 = _this.uiRef,
            childRef = _this$uiRef2.childRef,
            containerRef = _this$uiRef2.containerRef,
            focusedItem = _spotlight.default.getCurrent();

        _this.uiRef.lastInputType = 'pageKey'; // Should skip scroll by page when focusedItem is paging control button of Scrollbar

        if (focusedItem && childRef.containerRef.contains(focusedItem)) {
          var // VirtualList and Scroller have a spotlightId on containerRef
          spotlightId = containerRef.dataset.spotlightId,
              rDirection = reverseDirections[direction],
              viewportBounds = containerRef.getBoundingClientRect(),
              focusedItemBounds = focusedItem.getBoundingClientRect(),
              endPoint = {
            x: focusedItemBounds.left + focusedItemBounds.width / 2,
            y: viewportBounds.top + (direction === 'up' ? focusedItemBounds.height / 2 - 1 : viewportBounds.height - focusedItemBounds.height / 2 + 1)
          };
          var next = null;
          /* 1. Find spottable item in viewport */

          next = (0, _target.getTargetByDirectionFromPosition)(rDirection, endPoint, spotlightId);

          if (next !== focusedItem) {
            _spotlight.default.focus(next);
            /* 2. Find spottable item out of viewport */
            // For Scroller

          } else if (_this.childRef.scrollToNextPage) {
            next = _this.childRef.scrollToNextPage({
              direction: direction,
              focusedItem: focusedItem,
              reverseDirection: rDirection,
              spotlightId: spotlightId
            });

            if (next !== null) {
              _this.animateOnFocus = false;

              _spotlight.default.focus(next);
            } // For VirtualList

          } else if (_this.childRef.scrollToNextItem) {
            _this.childRef.scrollToNextItem({
              direction: direction,
              focusedItem: focusedItem,
              reverseDirection: rDirection,
              spotlightId: spotlightId
            });
          } // Need to check whether an overscroll effect is needed


          return true;
        }

        return false;
      }
    });
    Object.defineProperty(_assertThisInitialized(_this), "onKeyDown", {
      configurable: true,
      enumerable: true,
      writable: true,
      value: function value(ev) {
        var overscrollEffectOn = _this.props.overscrollEffectOn,
            keyCode = ev.keyCode,
            repeat = ev.repeat;
        var overscrollEffectRequired = false,
            direction = null;
        (0, _handle.forward)('onKeyDown', ev, _this.props);
        _this.animateOnFocus = true;

        if (isPageUp(keyCode) || isPageDown(keyCode)) {
          ev.preventDefault();

          if (!repeat && _this.hasFocus() && _this.props.direction === 'vertical' || _this.props.direction === 'both') {
            _spotlight.default.setPointerMode(false);

            direction = isPageUp(keyCode) ? 'up' : 'down';
            overscrollEffectRequired = _this.scrollByPage(direction) && overscrollEffectOn.pageKey;
          }
        } else if (!_spotlight.default.getPointerMode() && !repeat && _this.hasFocus() && (0, _spotlight.getDirection)(keyCode)) {
          var element = _spotlight.default.getCurrent();

          direction = (0, _spotlight.getDirection)(keyCode);
          _this.uiRef.lastInputType = 'arrowKey';
          overscrollEffectRequired = overscrollEffectOn.arrowKey && !(element ? (0, _target.getTargetByDirectionFromElement)(direction, element) : null);

          if (overscrollEffectRequired) {
            var _this$uiRef3 = _this.uiRef,
                horizontalScrollbarRef = _this$uiRef3.horizontalScrollbarRef,
                verticalScrollbarRef = _this$uiRef3.verticalScrollbarRef;

            if (horizontalScrollbarRef && horizontalScrollbarRef.getContainerRef().contains(element) || verticalScrollbarRef && verticalScrollbarRef.getContainerRef().contains(element)) {
              overscrollEffectRequired = false;
            }
          }
        }

        if (direction && overscrollEffectRequired) {
          /* if the spotlight focus will not move */
          var orientation = direction === 'up' || direction === 'down' ? 'vertical' : 'horizontal',
              bounds = _this.uiRef.getScrollBounds(),
              scrollability = orientation === 'vertical' ? _this.uiRef.canScrollVertically(bounds) : _this.uiRef.canScrollHorizontally(bounds);

          if (scrollability) {
            var isRtl = _this.uiRef.state.rtl,
                edge = direction === 'up' || !isRtl && direction === 'left' || isRtl && direction === 'right' ? 'before' : 'after';

            _this.uiRef.checkAndApplyOverscrollEffect(orientation, edge, overscrollTypeOnce);
          }
        }
      }
    });
    Object.defineProperty(_assertThisInitialized(_this), "onScrollbarButtonClick", {
      configurable: true,
      enumerable: true,
      writable: true,
      value: function value(_ref3) {
        var isPreviousScrollButton = _ref3.isPreviousScrollButton,
            isVerticalScrollBar = _ref3.isVerticalScrollBar;

        var bounds = _this.uiRef.getScrollBounds(),
            direction = isPreviousScrollButton ? -1 : 1,
            pageDistance = direction * (isVerticalScrollBar ? bounds.clientHeight : bounds.clientWidth) * paginationPageMultiplier;

        _this.uiRef.lastInputType = 'scrollbarButton';

        if (direction !== _this.uiRef.wheelDirection) {
          _this.uiRef.isScrollAnimationTargetAccumulated = false;
          _this.uiRef.wheelDirection = direction;
        }

        _this.uiRef.scrollToAccumulatedTarget(pageDistance, isVerticalScrollBar, _this.props.overscrollEffectOn.scrollbarButton);
      }
    });
    Object.defineProperty(_assertThisInitialized(_this), "scrollStopOnScroll", {
      configurable: true,
      enumerable: true,
      writable: true,
      value: function value() {
        if (!_this.props['data-spotlight-container-disabled']) {
          _this.childRef.setContainerDisabled(false);
        }

        _this.focusOnItem();

        _this.lastScrollPositionOnFocus = null;
        _this.isWheeling = false;

        if (_this.isVoiceControl) {
          _this.isVoiceControl = false;

          _this.updateFocusAfterVoiceControl();
        }
      }
    });
    Object.defineProperty(_assertThisInitialized(_this), "scrollTo", {
      configurable: true,
      enumerable: true,
      writable: true,
      value: function value(opt) {
        _this.indexToFocus = opt.focus && typeof opt.index === 'number' ? opt.index : null;
        _this.nodeToFocus = opt.focus && opt.node instanceof Object && opt.node.nodeType === 1 ? opt.node : null;
      }
    });
    Object.defineProperty(_assertThisInitialized(_this), "alertThumb", {
      configurable: true,
      enumerable: true,
      writable: true,
      value: function value() {
        var bounds = _this.uiRef.getScrollBounds();

        _this.uiRef.showThumb(bounds);

        _this.uiRef.startHidingThumb();
      }
    });
    Object.defineProperty(_assertThisInitialized(_this), "alertThumbAfterRendered", {
      configurable: true,
      enumerable: true,
      writable: true,
      value: function value() {
        var spotItem = _spotlight.default.getCurrent();

        if (!_spotlight.default.getPointerMode() && spotItem && _this.uiRef && _this.uiRef.childRef.containerRef.contains(spotItem) && _this.uiRef.isUpdatedScrollThumb) {
          _this.alertThumb();
        }
      }
    });
    Object.defineProperty(_assertThisInitialized(_this), "handleScrollerUpdate", {
      configurable: true,
      enumerable: true,
      writable: true,
      value: function value() {
        if (_this.uiRef.scrollToInfo === null) {
          var scrollHeight = _this.uiRef.getScrollBounds().scrollHeight;

          if (scrollHeight !== _this.uiRef.bounds.scrollHeight) {
            _this.calculateAndScrollTo();
          }
        } // oddly, Scroller manages this.uiRef.bounds so if we don't update it here (it is also
        // updated in calculateAndScrollTo but we might not have made it to that point), it will be
        // out of date when we land back in this method next time.


        _this.uiRef.bounds.scrollHeight = _this.uiRef.getScrollBounds().scrollHeight;
      }
    });
    Object.defineProperty(_assertThisInitialized(_this), "clearOverscrollEffect", {
      configurable: true,
      enumerable: true,
      writable: true,
      value: function value(orientation, edge) {
        _this.overscrollJobs[orientation][edge].startAfter(overscrollTimeout, orientation, edge, overscrollTypeNone, 0);

        _this.uiRef.setOverscrollStatus(orientation, edge, overscrollTypeNone, 0);
      }
    });
    Object.defineProperty(_assertThisInitialized(_this), "applyOverscrollEffect", {
      configurable: true,
      enumerable: true,
      writable: true,
      value: function value(orientation, edge, type, ratio) {
        var nodeRef = _this.overscrollRefs[orientation];

        if (nodeRef) {
          nodeRef.style.setProperty(overscrollRatioPrefix + orientation + edge, ratio);

          if (type === overscrollTypeOnce) {
            _this.overscrollJobs[orientation][edge].start(orientation, edge, overscrollTypeDone, 0);
          }
        }
      }
    });
    Object.defineProperty(_assertThisInitialized(_this), "createOverscrollJob", {
      configurable: true,
      enumerable: true,
      writable: true,
      value: function value(orientation, edge) {
        if (!_this.overscrollJobs[orientation][edge]) {
          _this.overscrollJobs[orientation][edge] = new _util.Job(_this.applyOverscrollEffect.bind(_assertThisInitialized(_this)), overscrollTimeout);
        }
      }
    });
    Object.defineProperty(_assertThisInitialized(_this), "stopOverscrollJob", {
      configurable: true,
      enumerable: true,
      writable: true,
      value: function value(orientation, edge) {
        var job = _this.overscrollJobs[orientation][edge];

        if (job) {
          job.stop();
        }
      }
    });
    Object.defineProperty(_assertThisInitialized(_this), "addEventListeners", {
      configurable: true,
      enumerable: true,
      writable: true,
      value: function value(childContainerRef) {
        if (childContainerRef && childContainerRef.addEventListener) {
          childContainerRef.addEventListener('mouseover', _this.onMouseOver, {
            capture: true
          });
          childContainerRef.addEventListener('mousemove', _this.onMouseMove, {
            capture: true
          });
          childContainerRef.addEventListener('focusin', _this.onFocus);

          if (_platform.default.webos) {
            childContainerRef.addEventListener('webOSVoice', _this.onVoice);
            childContainerRef.setAttribute('data-webos-voice-intent', 'Scroll');
          }
        }
      }
    });
    Object.defineProperty(_assertThisInitialized(_this), "removeEventListeners", {
      configurable: true,
      enumerable: true,
      writable: true,
      value: function value(childContainerRef) {
        if (childContainerRef && childContainerRef.removeEventListener) {
          childContainerRef.removeEventListener('mouseover', _this.onMouseOver, {
            capture: true
          });
          childContainerRef.removeEventListener('mousemove', _this.onMouseMove, {
            capture: true
          });
          childContainerRef.removeEventListener('focusin', _this.onFocus);

          if (_platform.default.webos) {
            childContainerRef.removeEventListener('webOSVoice', _this.onVoice);
            childContainerRef.removeAttribute('data-webos-voice-intent');
          }
        }
      }
    });
    Object.defineProperty(_assertThisInitialized(_this), "initHorizontalOverscrollRef", {
      configurable: true,
      enumerable: true,
      writable: true,
      value: function value(ref) {
        if (ref) {
          _this.overscrollRefs.horizontal = _reactDom.default.findDOMNode(ref); // eslint-disable-line react/no-find-dom-node

          _this.createOverscrollJob('horizontal', 'before');

          _this.createOverscrollJob('horizontal', 'after');
        }
      }
    });
    Object.defineProperty(_assertThisInitialized(_this), "initVerticalOverscrollRef", {
      configurable: true,
      enumerable: true,
      writable: true,
      value: function value(ref) {
        if (ref) {
          _this.overscrollRefs.vertical = ref;

          _this.createOverscrollJob('vertical', 'before');

          _this.createOverscrollJob('vertical', 'after');
        }
      }
    });
    Object.defineProperty(_assertThisInitialized(_this), "initChildRef", {
      configurable: true,
      enumerable: true,
      writable: true,
      value: function value(ref) {
        if (ref) {
          _this.childRef = ref;
        }
      }
    });
    Object.defineProperty(_assertThisInitialized(_this), "initUiRef", {
      configurable: true,
      enumerable: true,
      writable: true,
      value: function value(ref) {
        if (ref) {
          _this.uiRef = ref;
        }
      }
    });
    Object.defineProperty(_assertThisInitialized(_this), "updateFocusAfterVoiceControl", {
      configurable: true,
      enumerable: true,
      writable: true,
      value: function value() {
        var spotItem = _spotlight.default.getCurrent();

        if (spotItem && _this.uiRef.containerRef.contains(spotItem)) {
          var viewportBounds = _this.uiRef.containerRef.getBoundingClientRect(),
              spotItemBounds = spotItem.getBoundingClientRect(),
              nodes = _spotlight.default.getSpottableDescendants(_this.uiRef.containerRef.dataset.spotlightId),
              first = _this.voiceControlDirection === 'vertical' ? 'top' : 'left',
              last = _this.voiceControlDirection === 'vertical' ? 'bottom' : 'right';

          if (spotItemBounds[last] < viewportBounds[first] || spotItemBounds[first] > viewportBounds[last]) {
            for (var i = 0; i < nodes.length; i++) {
              var nodeBounds = nodes[i].getBoundingClientRect();

              if (nodeBounds[first] > viewportBounds[first] && nodeBounds[last] < viewportBounds[last]) {
                _spotlight.default.focus(nodes[i]);

                break;
              }
            }
          }
        }
      }
    });
    Object.defineProperty(_assertThisInitialized(_this), "isReachedEdge", {
      configurable: true,
      enumerable: true,
      writable: true,
      value: function value(scrollPos, ltrBound, rtlBound) {
        var isRtl = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
        var bound = isRtl ? rtlBound : ltrBound;
        return bound === 0 && scrollPos === 0 || bound > 0 && scrollPos >= bound - 1;
      }
    });
    Object.defineProperty(_assertThisInitialized(_this), "onVoice", {
      configurable: true,
      enumerable: true,
      writable: true,
      value: function value(e) {
        var isHorizontal = _this.props.direction === 'horizontal',
            isRtl = _this.uiRef.state.rtl,
            _this$uiRef4 = _this.uiRef,
            scrollTop = _this$uiRef4.scrollTop,
            scrollLeft = _this$uiRef4.scrollLeft,
            _this$uiRef$getScroll = _this.uiRef.getScrollBounds(),
            maxLeft = _this$uiRef$getScroll.maxLeft,
            maxTop = _this$uiRef$getScroll.maxTop,
            verticalDirection = ['up', 'down', 'top', 'bottom'],
            horizontalDirection = isRtl ? ['right', 'left', 'rightmost', 'leftmost'] : ['left', 'right', 'leftmost', 'rightmost'],
            movement = ['previous', 'next', 'first', 'last'];

        var scroll = e && e.detail && e.detail.scroll,
            index = movement.indexOf(scroll);

        if (index > -1) {
          scroll = isHorizontal ? horizontalDirection[index] : verticalDirection[index];
        }

        _this.voiceControlDirection = verticalDirection.includes(scroll) && 'vertical' || horizontalDirection.includes(scroll) && 'horizontal' || null; // Case 1. Invalid direction

        if (_this.voiceControlDirection === null) {
          _this.isVoiceControl = false; // Case 2. Cannot scroll
        } else if (['up', 'top'].includes(scroll) && _this.isReachedEdge(scrollTop, 0) || ['down', 'bottom'].includes(scroll) && _this.isReachedEdge(scrollTop, maxTop) || ['left', 'leftmost'].includes(scroll) && _this.isReachedEdge(scrollLeft, 0, maxLeft, isRtl) || ['right', 'rightmost'].includes(scroll) && _this.isReachedEdge(scrollLeft, maxLeft, 0, isRtl)) {
          if (window.webOSVoiceReportActionResult) {
            window.webOSVoiceReportActionResult({
              voiceUi: {
                exception: 'alreadyCompleted'
              }
            });
            e.preventDefault();
          } // Case 3. Can scroll

        } else {
          _this.isVoiceControl = true;

          if (['up', 'down', 'left', 'right'].includes(scroll)) {
            var isPreviousScrollButton = scroll === 'up' || scroll === 'left' && !isRtl || scroll === 'right' && isRtl;

            _this.onScrollbarButtonClick({
              isPreviousScrollButton: isPreviousScrollButton,
              isVerticalScrollBar: verticalDirection.includes(scroll)
            });
          } else {
            // ['top', 'bottom', 'leftmost', 'rightmost'].includes(scroll)
            _this.uiRef.scrollTo({
              align: verticalDirection.includes(scroll) && scroll || (scroll === 'leftmost' && isRtl || scroll === 'rightmost' && !isRtl) && 'right' || 'left'
            });
          }

          e.preventDefault();
        }
      }
    });
    _this.scrollbarProps = {
      cbAlertThumb: _this.alertThumbAfterRendered,
      onNextScroll: _this.onScrollbarButtonClick,
      onPrevScroll: _this.onScrollbarButtonClick
    };
    configureSpotlightContainer(props);
    return _this;
  }

  _createClass(ScrollableBaseNative, [{
    key: "componentWillReceiveProps",
    value: function componentWillReceiveProps(nextProps) {
      if (this.props['data-spotlight-id'] !== nextProps['data-spotlight-id'] || this.props.focusableScrollbar !== nextProps.focusableScrollbar) {
        configureSpotlightContainer(nextProps);
      }
    }
  }, {
    key: "componentWillUnmount",
    value: function componentWillUnmount() {
      this.stopOverscrollJob('horizontal', 'before');
      this.stopOverscrollJob('horizontal', 'after');
      this.stopOverscrollJob('vertical', 'before');
      this.stopOverscrollJob('vertical', 'after');
    } // status

  }, {
    key: "hasFocus",
    value: function hasFocus() {
      var current = _spotlight.default.getCurrent();

      if (!current) {
        var spotlightId = _spotlight.default.getActiveContainer();

        current = document.querySelector("[data-spotlight-id=\"".concat(spotlightId, "\"]"));
      }

      return current && this.uiRef.containerRef.contains(current);
    }
  }, {
    key: "focusOnItem",
    value: function focusOnItem() {
      var childRef = this.childRef;

      if (this.indexToFocus !== null && typeof childRef.focusByIndex === 'function') {
        childRef.focusByIndex(this.indexToFocus);
        this.indexToFocus = null;
      }

      if (this.nodeToFocus !== null && typeof childRef.focusOnNode === 'function') {
        childRef.focusOnNode(this.nodeToFocus);
        this.nodeToFocus = null;
      }
    }
  }, {
    key: "render",
    value: function render() {
      var _this2 = this;

      var _props = this.props,
          childRenderer = _props.childRenderer,
          spotlightContainer = _props['data-spotlight-container'],
          spotlightContainerDisabled = _props['data-spotlight-container-disabled'],
          spotlightId = _props['data-spotlight-id'],
          focusableScrollbar = _props.focusableScrollbar,
          scrollDownAriaLabel = _props.scrollDownAriaLabel,
          scrollLeftAriaLabel = _props.scrollLeftAriaLabel,
          scrollRightAriaLabel = _props.scrollRightAriaLabel,
          scrollUpAriaLabel = _props.scrollUpAriaLabel,
          rest = _objectWithoutProperties(_props, ["childRenderer", 'data-spotlight-container', 'data-spotlight-container-disabled', 'data-spotlight-id', "focusableScrollbar", "scrollDownAriaLabel", "scrollLeftAriaLabel", "scrollRightAriaLabel", "scrollUpAriaLabel"]),
          downButtonAriaLabel = scrollDownAriaLabel == null ? (0, _$L.default)('scroll down') : scrollDownAriaLabel,
          upButtonAriaLabel = scrollUpAriaLabel == null ? (0, _$L.default)('scroll up') : scrollUpAriaLabel,
          rightButtonAriaLabel = scrollRightAriaLabel == null ? (0, _$L.default)('scroll right') : scrollRightAriaLabel,
          leftButtonAriaLabel = scrollLeftAriaLabel == null ? (0, _$L.default)('scroll left') : scrollLeftAriaLabel;

      delete rest.focusableScrollbar;
      return _react.default.createElement(_ScrollableNative.ScrollableBaseNative, _extends({
        noScrollByDrag: true
      }, rest, {
        addEventListeners: this.addEventListeners,
        applyOverscrollEffect: this.applyOverscrollEffect,
        clearOverscrollEffect: this.clearOverscrollEffect,
        onFlick: this.onFlick,
        onKeyDown: this.onKeyDown,
        onMouseDown: this.onMouseDown,
        onWheel: this.onWheel,
        ref: this.initUiRef,
        removeEventListeners: this.removeEventListeners,
        scrollStopOnScroll: this.scrollStopOnScroll,
        scrollTo: this.scrollTo,
        start: this.start,
        containerRenderer: function containerRenderer(_ref4) {
          var childComponentProps = _ref4.childComponentProps,
              ChildWrapper = _ref4.childWrapper,
              _ref4$childWrapperPro = _ref4.childWrapperProps,
              contentClassName = _ref4$childWrapperPro.className,
              restChildWrapperProps = _objectWithoutProperties(_ref4$childWrapperPro, ["className"]),
              className = _ref4.className,
              componentCss = _ref4.componentCss,
              horizontalScrollbarProps = _ref4.horizontalScrollbarProps,
              initUiChildRef = _ref4.initChildRef,
              initUiContainerRef = _ref4.initContainerRef,
              isHorizontalScrollbarVisible = _ref4.isHorizontalScrollbarVisible,
              isVerticalScrollbarVisible = _ref4.isVerticalScrollbarVisible,
              rtl = _ref4.rtl,
              scrollTo = _ref4.scrollTo,
              style = _ref4.style,
              verticalScrollbarProps = _ref4.verticalScrollbarProps;

          return _react.default.createElement("div", {
            className: (0, _classnames.default)(className, _OverscrollEffect.default.scrollable),
            "data-spotlight-container": spotlightContainer,
            "data-spotlight-container-disabled": spotlightContainerDisabled,
            "data-spotlight-id": spotlightId,
            ref: initUiContainerRef,
            style: style
          }, _react.default.createElement("div", {
            className: (0, _classnames.default)(componentCss.container, _OverscrollEffect.default.overscrollFrame, _OverscrollEffect.default.vertical, isHorizontalScrollbarVisible ? _OverscrollEffect.default.horizontalScrollbarVisible : null),
            ref: _this2.initVerticalOverscrollRef
          }, _react.default.createElement(ChildWrapper, _extends({
            className: (0, _classnames.default)(contentClassName, _OverscrollEffect.default.overscrollFrame, _OverscrollEffect.default.horizontal),
            ref: _this2.initHorizontalOverscrollRef
          }, restChildWrapperProps), childRenderer(_objectSpread({}, childComponentProps, {
            cbScrollTo: scrollTo,
            className: componentCss.scrollableFill,
            initUiChildRef: initUiChildRef,
            isVerticalScrollbarVisible: isVerticalScrollbarVisible,
            onUpdate: _this2.handleScrollerUpdate,
            ref: _this2.initChildRef,
            rtl: rtl,
            spotlightId: spotlightId
          }))), isVerticalScrollbarVisible ? _react.default.createElement(_Scrollbar.default, _extends({}, verticalScrollbarProps, _this2.scrollbarProps, {
            disabled: !isVerticalScrollbarVisible,
            focusableScrollButtons: focusableScrollbar,
            nextButtonAriaLabel: downButtonAriaLabel,
            previousButtonAriaLabel: upButtonAriaLabel,
            rtl: rtl
          })) : null), isHorizontalScrollbarVisible ? _react.default.createElement(_Scrollbar.default, _extends({}, horizontalScrollbarProps, _this2.scrollbarProps, {
            corner: isVerticalScrollbarVisible,
            disabled: !isHorizontalScrollbarVisible,
            focusableScrollButtons: focusableScrollbar,
            nextButtonAriaLabel: rightButtonAriaLabel,
            previousButtonAriaLabel: leftButtonAriaLabel,
            rtl: rtl
          })) : null);
        }
      }));
    }
  }]);

  return ScrollableBaseNative;
}(_react.Component);
/**
 * A Moonstone-styled component that provides horizontal and vertical scrollbars.
 *
 * @class ScrollableNative
 * @memberof moonstone/ScrollableNative
 * @mixes spotlight/SpotlightContainerDecorator
 * @extends moonstone/Scrollable.ScrollableBaseNative
 * @ui
 * @private
 */


exports.ScrollableBaseNative = ScrollableBaseNative;
Object.defineProperty(ScrollableBaseNative, "displayName", {
  configurable: true,
  enumerable: true,
  writable: true,
  value: 'ScrollableNative'
});
Object.defineProperty(ScrollableBaseNative, "propTypes", {
  configurable: true,
  enumerable: true,
  writable: true,
  value:
  /** @lends moonstone/ScrollableNative.ScrollableNative.prototype */
  {
    /**
     * Render function.
     *
     * @type {Function}
     * @required
     * @private
     */
    childRenderer: _propTypes.default.func.isRequired,

    /**
     * This is set to `true` by SpotlightContainerDecorator
     *
     * @type {Boolean}
     * @private
     */
    'data-spotlight-container': _propTypes.default.bool,

    /**
     * `false` if the content of the list or the scroller could get focus
     *
     * @type {Boolean}
     * @default false
     * @private
     */
    'data-spotlight-container-disabled': _propTypes.default.bool,

    /**
     * This is passed onto the wrapped component to allow
     * it to customize the spotlight container for its use case.
     *
     * @type {String}
     * @private
     */
    'data-spotlight-id': _propTypes.default.string,

    /**
     * Direction of the list or the scroller.
     * `'both'` could be only used for[Scroller]{@link moonstone/Scroller.Scroller}.
     *
     * Valid values are:
     * * `'both'`,
     * * `'horizontal'`, and
     * * `'vertical'`.
     *
     * @type {String}
     * @private
     */
    direction: _propTypes.default.oneOf(['both', 'horizontal', 'vertical']),

    /**
     * Allows 5-way navigation to the scrollbar controls. By default, 5-way will
     * not move focus to the scrollbar controls.
     *
     * @type {Boolean}
     * @default false
     * @public
     */
    focusableScrollbar: _propTypes.default.bool,

    /**
     * Specifies overscroll effects shows on which type of inputs.
     *
     * @type {Object}
     * @default {
     *	arrowKey: false,
     *	drag: false,
     *	pageKey: false,
     *	scrollbarButton: false,
     *	wheel: true
     * }
     * @private
     */
    overscrollEffectOn: _propTypes.default.shape({
      arrowKey: _propTypes.default.bool,
      drag: _propTypes.default.bool,
      pageKey: _propTypes.default.bool,
      scrollbarButton: _propTypes.default.bool,
      wheel: _propTypes.default.bool
    }),

    /**
     * Sets the hint string read when focusing the next button in the vertical scroll bar.
     *
     * @type {String}
     * @default $L('scroll down')
     * @public
     */
    scrollDownAriaLabel: _propTypes.default.string,

    /**
     * Sets the hint string read when focusing the previous button in the horizontal scroll bar.
     *
     * @type {String}
     * @default $L('scroll left')
     * @public
     */
    scrollLeftAriaLabel: _propTypes.default.string,

    /**
     * Sets the hint string read when focusing the next button in the horizontal scroll bar.
     *
     * @type {String}
     * @default $L('scroll right')
     * @public
     */
    scrollRightAriaLabel: _propTypes.default.string,

    /**
     * Sets the hint string read when focusing the previous button in the vertical scroll bar.
     *
     * @type {String}
     * @default $L('scroll up')
     * @public
     */
    scrollUpAriaLabel: _propTypes.default.string
  }
});
Object.defineProperty(ScrollableBaseNative, "defaultProps", {
  configurable: true,
  enumerable: true,
  writable: true,
  value: {
    'data-spotlight-container-disabled': false,
    focusableScrollbar: false,
    overscrollEffectOn: {
      arrowKey: false,
      drag: false,
      pageKey: false,
      scrollbarButton: false,
      wheel: true
    }
  }
});
var ScrollableNative = (0, _Skinnable.default)((0, _SpotlightContainerDecorator.default)({
  overflow: true,
  preserveId: true,
  restrict: 'self-first'
}, ScrollableBaseNative));
exports.ScrollableNative = ScrollableNative;
var _default = ScrollableNative;
exports.default = _default;