"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
function _export(target, all) {
    for(var name in all)Object.defineProperty(target, name, {
        enumerable: true,
        get: Object.getOwnPropertyDescriptor(all, name).get
    });
}
_export(exports, {
    get animatedScrollToX () {
        return animatedScrollToX;
    },
    get getCalculatedOffset () {
        return getCalculatedOffset;
    },
    get getCalculatedPos () {
        return getCalculatedPos;
    },
    get getCarouselItems () {
        return getCarouselItems;
    },
    get getItemSlot () {
        return getItemSlot;
    },
    get scrollToPos () {
        return scrollToPos;
    },
    get useCarousel () {
        return useCarousel;
    },
    get useDebouncedFunction () {
        return useDebouncedFunction;
    }
});
var _react = /*#__PURE__*/ _interop_require_wildcard(require("react"));
var _lodashthrottle = /*#__PURE__*/ _interop_require_default(require("lodash.throttle"));
function _array_like_to_array(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 _array_without_holes(arr) {
    if (Array.isArray(arr)) return _array_like_to_array(arr);
}
function _interop_require_default(obj) {
    return obj && obj.__esModule ? obj : {
        default: obj
    };
}
function _getRequireWildcardCache(nodeInterop) {
    if (typeof WeakMap !== "function") return null;
    var cacheBabelInterop = new WeakMap();
    var cacheNodeInterop = new WeakMap();
    return (_getRequireWildcardCache = function(nodeInterop) {
        return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
    })(nodeInterop);
}
function _interop_require_wildcard(obj, nodeInterop) {
    if (!nodeInterop && obj && obj.__esModule) {
        return obj;
    }
    if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
        return {
            default: obj
        };
    }
    var cache = _getRequireWildcardCache(nodeInterop);
    if (cache && cache.has(obj)) {
        return cache.get(obj);
    }
    var newObj = {
        __proto__: null
    };
    var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
    for(var key in obj){
        if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
            var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
            if (desc && (desc.get || desc.set)) {
                Object.defineProperty(newObj, key, desc);
            } else {
                newObj[key] = obj[key];
            }
        }
    }
    newObj.default = obj;
    if (cache) {
        cache.set(obj, newObj);
    }
    return newObj;
}
function _iterable_to_array(iter) {
    if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
}
function _non_iterable_spread() {
    throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function _to_consumable_array(arr) {
    return _array_without_holes(arr) || _iterable_to_array(arr) || _unsupported_iterable_to_array(arr) || _non_iterable_spread();
}
function _unsupported_iterable_to_array(o, minLen) {
    if (!o) return;
    if (typeof o === "string") return _array_like_to_array(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(n);
    if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
}
var getCalculatedOffset = function getCalculatedOffset(scrollEl, trackEl, axis) {
    var paddingProp = axis === 'x' ? 'paddingLeft' : 'paddingTop';
    return parseInt(getComputedStyle(scrollEl)[paddingProp], 10) + parseInt(getComputedStyle(trackEl)[paddingProp], 10);
};
var positionModByScrollAlign = function positionModByScrollAlign(param) {
    var scrollAlign = param.scrollAlign, position = param.position, carouselSize = param.carouselSize, itemSize = param.itemSize, offset = param.offset, scrollStart = param.scrollStart, axis = param.axis;
    if (scrollAlign === 'start') {
        var inaccuracy = 1;
        var paddingOffset = axis === 'y' ? offset - itemSize / 2 + inaccuracy : 0;
        return position + paddingOffset;
    }
    if (scrollAlign === 'center') {
        return position - carouselSize / 2 + itemSize / 2;
    }
    if (scrollAlign === 'end') {
        return position - carouselSize + itemSize + offset;
    }
    if (scrollAlign === 'activeDirection') {
        if (position >= scrollStart + carouselSize - itemSize) {
            return position - carouselSize + itemSize + offset;
        }
        if (position > scrollStart) {
            return scrollStart;
        }
    }
    return position;
};
var getCalculatedPos = function getCalculatedPos(param) {
    var scrollEl = param.scrollEl, items = param.items, axis = param.axis, index = param.index, offset = param.offset, scrollAlign = param.scrollAlign;
    var position = scrollAlign === 'center' ? offset : 0;
    var carouselSize;
    var itemSize;
    var scrollStart;
    if (items.item(index) === null) {
        return position;
    }
    for(var i = 0; i < index; i++){
        if (axis === 'x') {
            var _ref;
            var _items_item;
            position += (_ref = (_items_item = items.item(i)) === null || _items_item === void 0 ? void 0 : _items_item.offsetWidth) !== null && _ref !== void 0 ? _ref : 0;
        } else {
            var _ref1;
            var _items_item1;
            position += (_ref1 = (_items_item1 = items.item(i)) === null || _items_item1 === void 0 ? void 0 : _items_item1.offsetHeight) !== null && _ref1 !== void 0 ? _ref1 : 0;
        }
    }
    if (axis === 'x') {
        var _ref2;
        var _items_item2;
        carouselSize = scrollEl.offsetWidth;
        itemSize = (_ref2 = (_items_item2 = items.item(index)) === null || _items_item2 === void 0 ? void 0 : _items_item2.offsetWidth) !== null && _ref2 !== void 0 ? _ref2 : 0;
        scrollStart = scrollEl.scrollLeft;
    } else {
        var _ref3;
        var _items_item3;
        carouselSize = scrollEl.offsetHeight;
        itemSize = (_ref3 = (_items_item3 = items.item(index)) === null || _items_item3 === void 0 ? void 0 : _items_item3.offsetHeight) !== null && _ref3 !== void 0 ? _ref3 : 0;
        scrollStart = scrollEl.scrollTop;
    }
    return positionModByScrollAlign({
        scrollAlign: scrollAlign,
        position: position,
        carouselSize: carouselSize,
        itemSize: itemSize,
        offset: offset,
        scrollStart: scrollStart,
        axis: axis
    });
};
var DEFAULT_DURATION = 300;
// https://css-tricks.com/emulating-css-timing-functions-javascript/
var tfs = {
    linear: function linear(t) {
        return t;
    },
    // eslint-disable-next-line
    easeIn: function easeIn(t) {
        return Math.pow(t, 1.675);
    },
    // eslint-disable-next-line
    easeOut: function easeOut(t) {
        return 1 - Math.pow(1 - t, 1.675);
    },
    easeInOut: function easeInOut(t) {
        return 0.5 * (Math.sin((t - 0.5) * Math.PI) + 1);
    }
};
var animatedScrollToX = function animatedScrollToX(elem, pos) {
    var duration = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : DEFAULT_DURATION, timingFunction = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : 'easeInOut';
    var startTime;
    var startX = elem.scrollLeft;
    var endX = Math.max(0, Math.min(elem.scrollWidth - elem.clientWidth, pos));
    var handleNewAnimationFrame = function handleNewAnimationFrame1() {
        startTime = startTime || Date.now();
        var timePos = Math.min(1, Math.max(1, Date.now() - startTime) / duration);
        var scrollPos = tfs[timingFunction](timePos);
        var left = startX + (endX - startX) * scrollPos;
        elem.scrollTo({
            left: left
        });
        if (timePos !== 1) window.requestAnimationFrame(handleNewAnimationFrame);
    };
    window.requestAnimationFrame(handleNewAnimationFrame);
};
var scrollToPos = function scrollToPos(param) {
    var scrollEl = param.scrollEl, pos = param.pos, axis = param.axis, animated = param.animated, duration = param.duration, timingFunction = param.timingFunction;
    if (axis === 'x' && Math.abs(pos - scrollEl.scrollLeft) > 1) {
        if (animated) {
            animatedScrollToX(scrollEl, pos, duration, timingFunction);
        } else {
            scrollEl.scrollTo({
                left: pos
            });
        }
    }
};
var round = function round(n) {
    return Math.round(n * 100) / 100;
};
var getItemSlot = function getItemSlot(itemIndex, itemEnd, itemSize, scrollStart, scrollSize, scrollAlign) {
    var prevIndex = arguments.length > 6 && arguments[6] !== void 0 ? arguments[6] : 0, offset = arguments.length > 7 && arguments[7] !== void 0 ? arguments[7] : 0;
    /**
     * Граница и центр скролла (видимой части).
     * Смещение + размер.
     */ var scrollEnd = scrollStart + scrollSize;
    var scrollCenter = scrollStart + scrollSize / 2;
    var itemCenter = itemEnd - itemSize / 2;
    if (scrollAlign === 'center') {
        return round((itemCenter - scrollCenter) / itemSize);
    }
    if (scrollAlign === 'start') {
        return round((itemEnd - itemSize - scrollStart) / itemSize);
    }
    if (scrollAlign === 'end') {
        return round((itemEnd - (scrollSize + scrollStart)) / itemSize);
    }
    if (scrollAlign === 'activeDirection') {
        var prevStart = offset + itemSize * prevIndex;
        var prevEnd = prevStart + itemSize;
        var prevVisible = prevEnd > scrollStart && prevStart < scrollEnd;
        if (!prevVisible) {
            if (prevIndex < itemIndex) {
                return round((itemEnd - (scrollSize + scrollStart)) / itemSize);
            }
            return round((itemEnd - itemSize - scrollStart) / itemSize);
        }
    }
    return null;
};
function getCarouselItems(track) {
    return track.children;
}
function useDebouncedFunction(func, delay, cleanUp) {
    var timeoutRef = (0, _react.useRef)();
    /**
     * Очистка таймера
     */ function clearTimer() {
        if (timeoutRef.current) {
            clearTimeout(timeoutRef.current);
            timeoutRef.current = undefined;
        }
    }
    /**
     * Очищаем таймер при анмаунте компонента, если cleanUp выставлен в true
     * и тем самым отменяем последний запланированный вызов
     */ (0, _react.useEffect)(function() {
        return cleanUp ? clearTimer : undefined;
    }, [
        cleanUp
    ]);
    return function() {
        for(var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++){
            args[_key] = arguments[_key];
        }
        clearTimer();
        timeoutRef.current = setTimeout(function() {
            return func.apply(void 0, _to_consumable_array(args));
        }, delay);
    };
}
var THROTTLE_DEFAULT_MS = 100;
var DEBOUNCE_DEFAULT_MS = 150;
var useCarousel = function useCarousel(param) {
    var index = param.index, _param_axis = param.axis, axis = _param_axis === void 0 ? 'x' : _param_axis, _param_detectActive = param.detectActive, detectActive = _param_detectActive === void 0 ? false : _param_detectActive, _param_detectThreshold = param.detectThreshold, detectThreshold = _param_detectThreshold === void 0 ? 0.5 : _param_detectThreshold, _param_scrollAlign = param.scrollAlign, scrollAlign = _param_scrollAlign === void 0 ? 'center' : _param_scrollAlign, scaleCallback = param.scaleCallback, scaleResetCallback = param.scaleResetCallback, onIndexChange = param.onIndexChange, onDetectActiveItem = param.onDetectActiveItem, _param_animatedScrollByIndex = param.animatedScrollByIndex, animatedScrollByIndex = _param_animatedScrollByIndex === void 0 ? false : _param_animatedScrollByIndex, _param_throttleMs = param.throttleMs, throttleMs = _param_throttleMs === void 0 ? THROTTLE_DEFAULT_MS : _param_throttleMs, _param_debounceMs = param.debounceMs, debounceMs = _param_debounceMs === void 0 ? DEBOUNCE_DEFAULT_MS : _param_debounceMs;
    var prevIndex = (0, _react.useRef)(null);
    var direction = (0, _react.useRef)(null);
    var offset = (0, _react.useRef)(0);
    var scrollRef = (0, _react.useRef)(null);
    var trackRef = (0, _react.useRef)(null);
    /**
     * Для того, чтобы не спамить изменениями индекса.
     * Задержка дебаунса слегка больше, чем у тротлинга.
     * Таким образом, событие срабатывает при завершении скролла.
     */ var debouncedOnIndexChange = useDebouncedFunction(function(i) {
        return onIndexChange === null || onIndexChange === void 0 ? void 0 : onIndexChange(i);
    }, debounceMs);
    /**
     * Вычисление центрального элемента.
     * Подсчет: от 0 до 1, какое количество ширины/высоты
     * каждого элемента находится по центру скролла.
     */ var throttledDetectActiveItem = (0, _react.useMemo)(function() {
        return (0, _lodashthrottle.default)(function() {
            if (!detectActive || scrollRef.current === null || trackRef.current === null) {
                return;
            }
            /**
             * Правая (или нижняя для Оу) граница элемента.
             */ var itemEdge = offset.current;
            /**
             * Смещение (отрицательный или положительный отступ)
             * и размер карусели (для Ox - ширина, для Oy - высота).
             */ var scrollPos = scrollRef.current[axis === 'x' ? 'scrollLeft' : 'scrollTop'];
            var scrollSize = scrollRef.current[axis === 'x' ? 'offsetWidth' : 'offsetHeight'];
            /**
             * Граница скролла (видимой части).
             * Смещение + размер.
             */ var scrollEdge = scrollPos + scrollSize;
            /**
             * Элементы перед, после и в видимой части.
             * перед [ ВИДИМЫЕ ] после
             */ var prevItems = [];
            var nextItems = [];
            var count = 0;
            var items = getCarouselItems(trackRef.current);
            /**
             * Проходим по всему списку, суммируя ширины элементов,
             * пока не найдем один элемент, чей центр будет в центре карусели.
             */ for(var itemIndex = 0; itemIndex < items.length; itemIndex++){
                var _prevIndex_current;
                var item = items.item(itemIndex);
                if (item === null) {
                    continue;
                }
                /**
                 * Для Ox - ширина, для Oy - высота.
                 */ var itemSize = item[axis === 'x' ? 'offsetWidth' : 'offsetHeight'];
                /**
                 * Все элементы правее вьюпорта выпадают из процедуры.
                 * Сравниваем по предыдущему элементу.
                 * [ ... ] ...|n| <- Левый край элемента за пределами начала видимой части
                 */ if (itemEdge > scrollEdge) {
                    if (scaleCallback && scaleResetCallback) {
                        nextItems.push(item);
                    }
                    continue;
                }
                itemEdge += itemSize;
                /**
                 * Все элементы левее вьюпорта выпадают из процедуры.
                 * Сравниваем по текущему элементу.
                 * Правый край элемента за пределами начала видимой части -> |p|... [ ... ]
                 */ if (scrollPos > itemEdge) {
                    if (scaleCallback && scaleResetCallback) {
                        prevItems.push(item);
                    }
                    continue;
                }
                var itemSlot = getItemSlot(itemIndex, itemEdge, itemSize, scrollPos, scrollSize, scrollAlign, (_prevIndex_current = prevIndex.current) !== null && _prevIndex_current !== void 0 ? _prevIndex_current : 0, offset.current);
                if (itemSlot !== null) {
                    if (detectThreshold && Math.abs(itemSlot) <= detectThreshold) {
                        onDetectActiveItem === null || onDetectActiveItem === void 0 ? void 0 : onDetectActiveItem(itemIndex);
                        debouncedOnIndexChange === null || debouncedOnIndexChange === void 0 ? void 0 : debouncedOnIndexChange(itemIndex);
                    }
                    if (scaleCallback) {
                        scaleCallback(item, itemSlot);
                        /**
                         * Количество айтемов в видимой части.
                         */ count++;
                    }
                }
            }
            if (scaleCallback && scaleResetCallback) {
                window.requestAnimationFrame(function() {
                    if (direction.current) {
                        if (nextItems.length) {
                            nextItems.splice(0, count).forEach(function(elem) {
                                return scaleCallback(elem, count);
                            });
                            if (nextItems.length) {
                                nextItems.splice(0, count).forEach(function(elem) {
                                    return scaleResetCallback(elem);
                                });
                            }
                        }
                    } else if (prevItems.length) {
                        var prItemsRev = prevItems.reverse();
                        prItemsRev.splice(0, count).forEach(function(elem) {
                            return scaleCallback(elem, count * -1);
                        });
                        if (prItemsRev.length) {
                            prItemsRev.splice(0, count).forEach(function(elem) {
                                return scaleResetCallback(elem);
                            });
                        }
                    }
                });
            }
        }, throttleMs);
    }, [
        axis,
        debouncedOnIndexChange,
        detectActive,
        detectThreshold,
        onDetectActiveItem,
        scaleCallback,
        scaleResetCallback,
        scrollAlign,
        throttleMs
    ]);
    /**
     * Прокрутка до нужной позиции индекса.
     */ var toIndex = (0, _react.useCallback)(function(i) {
        var scrollEl = scrollRef.current;
        var items = trackRef.current ? getCarouselItems(trackRef.current) : null;
        if (scrollEl && items && items.length > 0 && i >= 0) {
            scrollToPos({
                scrollEl: scrollEl,
                pos: getCalculatedPos({
                    scrollEl: scrollEl,
                    items: items,
                    axis: axis,
                    index: i,
                    offset: offset.current,
                    scrollAlign: scrollAlign
                }),
                axis: axis,
                /**
                     * Без анимации при переходе на другой конец списка
                     */ animated: animatedScrollByIndex && (prevIndex.current === null || Math.abs(i - prevIndex.current) !== items.length - 1)
            });
            prevIndex.current = i;
        }
    }, [
        animatedScrollByIndex,
        axis,
        scrollAlign
    ]);
    (0, _react.useEffect)(function() {
        if (scrollRef.current && trackRef.current) {
            offset.current = getCalculatedOffset(scrollRef.current, trackRef.current, axis);
        }
    }, [
        axis
    ]);
    /**
     * Операции на маунте/анмаунте компонента.
     * Создать слушатели событи и т.п.
     */ (0, _react.useEffect)(function() {
        var carouselElement = scrollRef.current;
        if (carouselElement) {
            carouselElement.addEventListener('scroll', throttledDetectActiveItem);
        }
        return function() {
            if (carouselElement) {
                carouselElement.removeEventListener('scroll', throttledDetectActiveItem);
            }
        };
    }, [
        throttledDetectActiveItem
    ]);
    /**
     * Нужно вызвать только при первом рендере
     */ (0, _react.useEffect)(function() {
        requestAnimationFrame(function() {
            /**
             * Прокрутка до начального индекса.
             */ toIndex(index);
            /**
             * Если на момент запуска карусель уже находится на нужной позиции,
             * событие скролла не произойдет, не сработает и определение центра,
             * необходимо вызвать его вручную.
             */ throttledDetectActiveItem();
        });
    // eslint-disable-next-line react-hooks/exhaustive-deps
    }, []);
    /**
     * Прокрутка до нужной позиции индекса, если индекс изменился.
     */ (0, _react.useEffect)(function() {
        if (index !== prevIndex.current) {
            toIndex(index);
        }
    }, [
        index,
        toIndex
    ]);
    return {
        scrollRef: scrollRef,
        trackRef: trackRef
    };
};
