"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.useScroll = exports["default"] = exports.dataIndexAttribute = void 0;
var _handle = require("@enact/core/handle");
var _platform = _interopRequireDefault(require("@enact/core/platform"));
var _spotlight = _interopRequireDefault(require("@enact/spotlight"));
var _Spottable = require("@enact/spotlight/Spottable");
var _target = require("@enact/spotlight/src/target");
var _utils = require("@enact/spotlight/src/utils");
var _useScroll = require("@enact/ui/useScroll");
var _utilDOM = _interopRequireDefault(require("@enact/ui/useScroll/utilDOM"));
var _utilEvent = _interopRequireDefault(require("@enact/ui/useScroll/utilEvent"));
var _react = require("react");
var _$L = _interopRequireDefault(require("../internal/$L"));
var _SharedStateDecorator = require("../Panels/SharedStateDecorator");
var _useThemeScrollContentHandle = require("./useThemeScrollContentHandle");
var _useEvent = require("./useEvent");
var _useScrollbar2 = _interopRequireDefault(require("./useScrollbar"));
var _useSpotlight = require("./useSpotlight");
var _excluded = ["data-spotlight-container", "data-spotlight-container-disabled", "data-spotlight-id", "focusableScrollbar", "preventBubblingOnKeyDown", "scrollDownAriaLabel", "scrollLeftAriaLabel", "scrollMode", "scrollRightAriaLabel", "scrollUpAriaLabel"];
function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var s = Object.getOwnPropertySymbols(e); for (r = 0; r < s.length; r++) o = s[r], t.includes(o) || {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (e.includes(n)) continue; t[n] = r[n]; } return t; }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } /**
 * Agate-themed scrollable hook and behaviors.
 *
 * @module agate/useScroll
 * @exports dataIndexAttribute
 * @exports useScroll
 * @private
 */
var reverseDirections = {
  down: 'up',
  up: 'down'
};

/**
 * The name of a custom attribute which indicates the index of an item in
 * {@link agate/VirtualList.VirtualList|VirtualList} or
 * {@link agate/VirtualList.VirtualGridList|VirtualGridList}.
 *
 * @constant dataIndexAttribute
 * @memberof agate/useScroll
 * @type {String}
 * @private
 */
var dataIndexAttribute = exports.dataIndexAttribute = 'data-index';
var isIntersecting = function isIntersecting(elem, container) {
  return elem && (0, _utils.intersects)((0, _utils.getRect)(container), (0, _utils.getRect)(elem));
};
var getIntersectingElement = function getIntersectingElement(elem, container) {
  return isIntersecting(elem, container) && elem;
};
var getTargetInViewByDirectionFromPosition = function getTargetInViewByDirectionFromPosition(direction, position, container) {
  var target = (0, _target.getTargetByDirectionFromPosition)(direction, position, _spotlight["default"].getActiveContainer());
  return getIntersectingElement(target, container);
};
var useThemeScroll = function useThemeScroll(props, instances) {
  var scrollMode = props.scrollMode;
  var themeScrollContentHandle = instances.themeScrollContentHandle,
    scrollContentRef = instances.scrollContentRef,
    scrollContainerHandle = instances.scrollContainerHandle,
    scrollContainerRef = instances.scrollContainerRef;
  var contextSharedState = (0, _react.useContext)(_SharedStateDecorator.SharedState);

  // Mutable value

  var mutableRef = (0, _react.useRef)({
    animateOnFocus: false,
    indexToFocus: null,
    isWheeling: false,
    lastScrollPositionOnFocus: null,
    nodeToFocus: null,
    pointToFocus: null
  });

  // Hooks

  var _useScrollbar = (0, _useScrollbar2["default"])(props, instances),
    alertScrollbarTrack = _useScrollbar.alertScrollbarTrack,
    isScrollButtonFocused = _useScrollbar.isScrollButtonFocused,
    scrollAndFocusScrollbarButton = _useScrollbar.scrollAndFocusScrollbarButton,
    scrollbarProps = _useScrollbar.scrollbarProps;
  (0, _useSpotlight.useSpotlightConfig)(props);
  (0, _useSpotlight.useSpotlightRestore)(props, instances);
  var _useEventWheel = (0, _useEvent.useEventWheel)(props, _objectSpread(_objectSpread({}, instances), {}, {
      spottable: mutableRef
    }), {
      isScrollButtonFocused: isScrollButtonFocused
    }),
    handleWheel = _useEventWheel.handleWheel;
  var _useEventFocus = (0, _useEvent.useEventFocus)(props, _objectSpread(_objectSpread({}, instances), {}, {
      spottable: mutableRef
    }), {
      alertScrollbarTrack: alertScrollbarTrack
    }),
    calculateAndScrollTo = _useEventFocus.calculateAndScrollTo,
    handleFocus = _useEventFocus.handleFocus,
    hasFocus = _useEventFocus.hasFocus;
  var _useEventKey = (0, _useEvent.useEventKey)(props, _objectSpread(_objectSpread({}, instances), {}, {
      spottable: mutableRef
    }), {
      hasFocus: hasFocus,
      isContent: isContent
    }),
    handleKeyDown = _useEventKey.handleKeyDown,
    lastPointer = _useEventKey.lastPointer,
    scrollByPageOnPointerMode = _useEventKey.scrollByPageOnPointerMode;
  (0, _useEvent.useEventMonitor)({}, instances, {
    lastPointer: lastPointer,
    scrollByPageOnPointerMode: scrollByPageOnPointerMode
  });
  var _useEventMouse = (0, _useEvent.useEventMouse)({}, instances, {
      isScrollButtonFocused: isScrollButtonFocused
    }),
    handleDragEnd = _useEventMouse.handleDragEnd,
    handleDragStart = _useEventMouse.handleDragStart,
    handleFlick = _useEventMouse.handleFlick,
    handleMouseDown = _useEventMouse.handleMouseDown;
  var _useEventTouch = (0, _useEvent.useEventTouch)({}, instances, {
      isScrollButtonFocused: isScrollButtonFocused
    }),
    handleTouchStart = _useEventTouch.handleTouchStart;

  // Functions

  function isContent(element) {
    return element && _utilDOM["default"].containsDangerously(scrollContentRef, element);
  }
  function scrollTo(opt) {
    mutableRef.current.indexToFocus = opt.focus && typeof opt.index === 'number' ? opt.index : null;
    mutableRef.current.nodeToFocus = opt.focus && opt.node instanceof Object && opt.node.nodeType === 1 ? opt.node : null;
  }
  function start(animate) {
    if (scrollMode === 'native' && !animate) {
      focusOnItem();
    }
  }
  function stop() {
    if (mutableRef.current.isDragging && !mutableRef.current.isFlicked) return;
    if (!props['data-spotlight-container-disabled'] || mutableRef.current.isFlicked || mutableRef.current.isWheeling) {
      themeScrollContentHandle.current.setContainerDisabled(false);
    }
    if (themeScrollContentHandle.current.pauseSpotlight) {
      themeScrollContentHandle.current.pauseSpotlight(false);
    }
    focusOnItem();
    mutableRef.current.lastScrollPositionOnFocus = null;
    mutableRef.current.isFlicked = false;
    mutableRef.current.isWheeling = false;
  }
  function scrollStopOnScroll() {
    stop();
  }
  function focusOnItem() {
    if (mutableRef.current.indexToFocus !== null && typeof themeScrollContentHandle.current.focusByIndex === 'function') {
      themeScrollContentHandle.current.focusByIndex(mutableRef.current.indexToFocus);
      mutableRef.current.indexToFocus = null;
    }
    if (mutableRef.current.nodeToFocus !== null && typeof themeScrollContentHandle.current.focusOnNode === 'function') {
      themeScrollContentHandle.current.focusOnNode(mutableRef.current.nodeToFocus);
      mutableRef.current.nodeToFocus = null;
    }
    if (mutableRef.current.pointToFocus !== null) {
      // no need to focus on pointer mode
      if (!_spotlight["default"].getPointerMode()) {
        var _mutableRef$current$p = mutableRef.current.pointToFocus,
          direction = _mutableRef$current$p.direction,
          x = _mutableRef$current$p.x,
          y = _mutableRef$current$p.y,
          position = {
            x: x,
            y: y
          },
          elemFromPoint = document.elementFromPoint(x, y),
          target = elemFromPoint && elemFromPoint.closest && getIntersectingElement(elemFromPoint.closest(".".concat(_Spottable.spottableClass)), scrollContainerRef.current) || getTargetInViewByDirectionFromPosition(direction, position, scrollContainerRef.current) || getTargetInViewByDirectionFromPosition(reverseDirections[direction], position, scrollContainerRef.current);
        if (target) {
          _spotlight["default"].focus(target);
        }
      }
      mutableRef.current.pointToFocus = null;
    }
  }
  function handleScroll(ev) {
    var x = ev.scrollLeft,
      y = ev.scrollTop,
      id = props.id;
    (0, _handle.forward)('onScroll', ev, props);
    if (id && contextSharedState && contextSharedState.set) {
      contextSharedState.set(ev, props);
      contextSharedState.set("".concat(id, ".scrollPosition"), {
        x: x,
        y: y
      });
    }
  }

  // Callback for scroller updates; calculate and, if needed, scroll to new position based on focused item.
  function handleScrollerUpdate() {
    if (scrollContainerHandle.current.scrollToInfo === null) {
      var scrollHeight = scrollContainerHandle.current.getScrollBounds().scrollHeight;
      if (scrollHeight !== scrollContainerHandle.current.bounds.scrollHeight) {
        calculateAndScrollTo();
      }
    }

    // oddly, Scroller manages scrollContainerHandle.current.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.
    scrollContainerHandle.current.bounds.scrollHeight = scrollContainerHandle.current.getScrollBounds().scrollHeight;
  }
  function handleResizeWindow() {
    var focusedItem = _spotlight["default"].getCurrent();
    if (focusedItem) {
      focusedItem.blur();
    }
  }

  // FIXME setting event handlers directly to work on the V8 snapshot.
  function addEventListeners(ref) {
    // `ref` is always `scrollContentRef`.
    (0, _utilEvent["default"])('focusin').addEventListener(ref, handleFocus);
  }

  // FIXME setting event handlers directly to work on the V8 snapshot.
  function removeEventListeners(ref) {
    // `ref` is always `scrollContentRef`.
    (0, _utilEvent["default"])('focusin').removeEventListener(ref, handleFocus);
  }

  // Return

  return {
    addEventListeners: addEventListeners,
    handleDragEnd: handleDragEnd,
    handleDragStart: handleDragStart,
    handleFlick: handleFlick,
    handleKeyDown: handleKeyDown,
    handleMouseDown: handleMouseDown,
    handleResizeWindow: handleResizeWindow,
    handleScroll: handleScroll,
    handleScrollerUpdate: handleScrollerUpdate,
    handleTouchStart: handleTouchStart,
    handleWheel: handleWheel,
    removeEventListeners: removeEventListeners,
    scrollAndFocusScrollbarButton: scrollAndFocusScrollbarButton,
    scrollbarProps: scrollbarProps,
    scrollStopOnScroll: scrollStopOnScroll,
    scrollTo: scrollTo,
    start: start,
    stop: stop
  };
};

/**
 * A custom hook that passes Agate-themed scrollable behavior information as its render prop.
 *
 * @class
 * @memberof agate/useScroll
 * @ui
 * @private
 */
var useScroll = exports.useScroll = function useScroll(props) {
  var spotlightContainer = props['data-spotlight-container'],
    spotlightContainerDisabled = props['data-spotlight-container-disabled'],
    spotlightId = props['data-spotlight-id'],
    focusableScrollbar = props.focusableScrollbar,
    preventBubblingOnKeyDown = props.preventBubblingOnKeyDown,
    scrollDownAriaLabel = props.scrollDownAriaLabel,
    scrollLeftAriaLabel = props.scrollLeftAriaLabel,
    scrollMode = props.scrollMode,
    scrollRightAriaLabel = props.scrollRightAriaLabel,
    scrollUpAriaLabel = props.scrollUpAriaLabel,
    rest = _objectWithoutProperties(props, _excluded),
    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;

  // Mutable value

  var scrollContainerRef = (0, _react.useRef)();
  var scrollContentHandle = (0, _react.useRef)();
  var scrollContentRef = (0, _react.useRef)();
  var itemRefs = (0, _react.useRef)([]);
  var horizontalScrollbarHandle = (0, _react.useRef)();
  var verticalScrollbarHandle = (0, _react.useRef)();

  // Handles

  var _useThemeScrollConten = (0, _useThemeScrollContentHandle.useThemeScrollContentHandle)(),
    _useThemeScrollConten2 = _slicedToArray(_useThemeScrollConten, 2),
    themeScrollContentHandle = _useThemeScrollConten2[0],
    setThemeScrollContentHandle = _useThemeScrollConten2[1];
  var scrollContainerHandle = (0, _react.useRef)({}); // To prevent referencing errors before calling `setScrollContainerHandle`, an empty object is provided as a default.
  /* Properties in `scrollContainerHandle` provided by `setScrollContainerHandle`
  	animator
  	bounds
  	calculateDistanceByWheel
  	canScrollHorizontally
  	canScrollVertically
  	getScrollBounds
  	isDragging
  	isFlicked
  	isScrollAnimationTargetAccumulated
  	lastInputType
  	rtl
  	scrollBounds
  	scrollHeight
  	scrolling
  	scrollLeft
  	scrollTo
  	scrollToAccumulatedTarget
  	scrollToInfo
  	scrollTop
  	showScrollbarTrack
  	start
  	startHidingScrollbarTrack
  	stop
  	wheelDirection
  */

  var setScrollContainerHandle = function setScrollContainerHandle(handle) {
    scrollContainerHandle.current = handle;
  };

  // Hooks

  var instance = {
    // Ref
    scrollContainerRef: scrollContainerRef,
    scrollContentRef: scrollContentRef,
    // Handle
    themeScrollContentHandle: themeScrollContentHandle,
    scrollContainerHandle: scrollContainerHandle,
    scrollContentHandle: scrollContentHandle,
    horizontalScrollbarHandle: horizontalScrollbarHandle,
    verticalScrollbarHandle: verticalScrollbarHandle
  };
  var collectionOfProperties = {},
    assignProperties = (0, _useScroll.assignPropertiesOf)(collectionOfProperties),
    scrollProps = {};
  var _useThemeScroll = useThemeScroll(props, instance),
    addEventListeners = _useThemeScroll.addEventListeners,
    handleDragEnd = _useThemeScroll.handleDragEnd,
    handleDragStart = _useThemeScroll.handleDragStart,
    handleFlick = _useThemeScroll.handleFlick,
    handleKeyDown = _useThemeScroll.handleKeyDown,
    handleMouseDown = _useThemeScroll.handleMouseDown,
    handleResizeWindow = _useThemeScroll.handleResizeWindow,
    handleScroll = _useThemeScroll.handleScroll,
    handleScrollerUpdate = _useThemeScroll.handleScrollerUpdate,
    handleTouchStart = _useThemeScroll.handleTouchStart,
    handleWheel = _useThemeScroll.handleWheel,
    removeEventListeners = _useThemeScroll.removeEventListeners,
    scrollAndFocusScrollbarButton = _useThemeScroll.scrollAndFocusScrollbarButton,
    scrollbarProps = _useThemeScroll.scrollbarProps,
    scrollStopOnScroll = _useThemeScroll.scrollStopOnScroll,
    scrollTo = _useThemeScroll.scrollTo,
    start = _useThemeScroll.start,
    stop = _useThemeScroll.stop;

  // Render

  if (scrollMode === 'translate') {
    scrollProps.stop = stop;
  } else {
    scrollProps.scrollStopOnScroll = scrollStopOnScroll;
    scrollProps.start = start;
  }
  var _useScrollBase = (0, _useScroll.useScrollBase)(_objectSpread(_objectSpread(_objectSpread({}, rest), scrollProps), {}, {
      assignProperties: assignProperties,
      noScrollByDrag: !_platform["default"].touchScreen,
      addEventListeners: addEventListeners,
      handleResizeWindow: handleResizeWindow,
      horizontalScrollbarHandle: horizontalScrollbarHandle,
      onDragEnd: handleDragEnd,
      onDragStart: handleDragStart,
      onFlick: handleFlick,
      onKeyDown: handleKeyDown,
      onMouseDown: handleMouseDown,
      onScroll: handleScroll,
      onWheel: handleWheel,
      removeEventListeners: removeEventListeners,
      scrollTo: scrollTo,
      setScrollContainerHandle: setScrollContainerHandle,
      scrollMode: scrollMode,
      scrollContentHandle: scrollContentHandle,
      scrollContentRef: scrollContentRef,
      scrollContainerRef: scrollContainerRef,
      spotlightContainerDisabled: spotlightContainerDisabled,
      verticalScrollbarHandle: verticalScrollbarHandle
    })),
    scrollContentWrapper = _useScrollBase.scrollContentWrapper,
    isHorizontalScrollbarVisible = _useScrollBase.isHorizontalScrollbarVisible,
    isVerticalScrollbarVisible = _useScrollBase.isVerticalScrollbarVisible;
  assignProperties('scrollContainerProps', {
    'data-spotlight-container': spotlightContainer,
    'data-spotlight-container-disabled': spotlightContainerDisabled,
    'data-spotlight-id': spotlightId,
    onTouchStart: handleTouchStart,
    ref: scrollContainerRef
  });
  assignProperties('scrollContentProps', _objectSpread(_objectSpread({}, props.itemRenderer ? {
    itemRefs: itemRefs
  } : {}), {}, {
    onUpdate: handleScrollerUpdate,
    scrollContainerRef: scrollContainerRef,
    scrollAndFocusScrollbarButton: scrollAndFocusScrollbarButton,
    setThemeScrollContentHandle: setThemeScrollContentHandle,
    spotlightId: spotlightId,
    scrollContainerHandle: scrollContainerHandle,
    scrollContentHandle: scrollContentHandle,
    scrollContentRef: scrollContentRef
  }));
  assignProperties('verticalScrollbarProps', _objectSpread(_objectSpread({}, scrollbarProps), {}, {
    focusableScrollButtons: focusableScrollbar,
    nextButtonAriaLabel: downButtonAriaLabel,
    onKeyDownButton: handleKeyDown,
    preventBubblingOnKeyDown: preventBubblingOnKeyDown,
    previousButtonAriaLabel: upButtonAriaLabel,
    scrollbarHandle: verticalScrollbarHandle
  }));
  assignProperties('horizontalScrollbarProps', _objectSpread(_objectSpread({}, scrollbarProps), {}, {
    focusableScrollButtons: focusableScrollbar,
    nextButtonAriaLabel: rightButtonAriaLabel,
    onKeyDownButton: handleKeyDown,
    preventBubblingOnKeyDown: preventBubblingOnKeyDown,
    previousButtonAriaLabel: leftButtonAriaLabel,
    scrollbarHandle: horizontalScrollbarHandle
  }));
  return _objectSpread(_objectSpread({}, collectionOfProperties), {}, {
    scrollContentWrapper: scrollContentWrapper,
    scrollContentHandle: scrollContentHandle,
    isHorizontalScrollbarVisible: isHorizontalScrollbarVisible,
    isVerticalScrollbarVisible: isVerticalScrollbarVisible
  });
};
var _default = exports["default"] = useScroll;