"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.useScroll = exports.dataIndexAttribute = exports["default"] = 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");

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

function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _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(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }

function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }

function _iterableToArrayLimit(arr, i) { if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return; var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }

function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }

function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; 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 _objectWithoutPropertiesLoose(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]; } return target; }

function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }

function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(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; }

var reverseDirections = {
  down: 'up',
  up: 'down'
};
/**
 * The name of a custom attribute which indicates the index of an item in
 * [VirtualList]{@link agate/VirtualList.VirtualList} or
 * [VirtualGridList]{@link agate/VirtualList.VirtualGridList}.
 *
 * @constant dataIndexAttribute
 * @memberof agate/useScroll
 * @type {String}
 * @private
 */

var dataIndexAttribute = 'data-index';
exports.dataIndexAttribute = dataIndexAttribute;

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 = 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, ["data-spotlight-container", "data-spotlight-container-disabled", "data-spotlight-id", "focusableScrollbar", "preventBubblingOnKeyDown", "scrollDownAriaLabel", "scrollLeftAriaLabel", "scrollMode", "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; // 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)({
    animator: null,
    bounds: null,
    calculateDistanceByWheel: null,
    canScrollHorizontally: null,
    canScrollVertically: null,
    getScrollBounds: null,
    isDragging: null,
    isFlicked: null,
    isScrollAnimationTargetAccumulated: null,
    lastInputType: null,
    rtl: null,
    scrollBounds: null,
    scrollHeight: null,
    scrolling: null,
    scrollLeft: null,
    scrollPos: null,
    scrollTo: null,
    scrollToAccumulatedTarget: null,
    scrollToInfo: null,
    scrollTop: null,
    showScrollbarTrack: null,
    start: null,
    startHidingScrollbarTrack: null,
    stop: null,
    wheelDirection: null
  });

  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
  });
};

exports.useScroll = useScroll;
var _default = useScroll;
exports["default"] = _default;