/* @oracle/oraclejet-preact: undefined */
'use strict';

var hooks = require('preact/hooks');
var useResizeObserver = require('./useResizeObserver-1597395f.js');
var tabBarUtils = require('./tabBarUtils-f6828fc8.js');

//The minimum width of the overflowtabbaritem, it can be rendered with width greater than this based on available width
const MORE_ITEM_WIDTH = 48;
const LABEL_MIN_WIDTH = 96;
const TAB_BORDER = 6;
//This is the minWidth a tab will have if it has only a label.
const TAB_MIN_WIDTH = LABEL_MIN_WIDTH + TAB_BORDER;
/**
 * Implements overflow detection and calculation
 */
function useDetectHorizontalOverflow({ rootRef, keysArray, isTruncationDisabled = true }) {
    const [visibleItemKeys, setVisibleItemKeys] = hooks.useState();
    const [overflowItemKeys, setOverflowingItemKeys] = hooks.useState();
    const trailingContentWidths = hooks.useRef(new Map());
    const [tabMaxWidth, setTabMaxWidth] = hooks.useState();
    // This state var is to only re-trigger the layout effect. Using it locally will not give the latest updated value hence we use current suffix vars
    const [tabBarWidth, setTabBarWidth] = hooks.useState(0);
    const isAllItemsTruncatedMax = hooks.useRef(false);
    const isAllItemsTruncatedFinal = hooks.useRef(false);
    const tabDimensions = hooks.useRef();
    hooks.useLayoutEffect(() => {
        // Store locally to avoid calling rootRef everytime within layouteffect
        const tabBarWidthCurrent = rootRef?.current?.clientWidth;
        const tabBarScrollWidthCurrent = rootRef?.current?.scrollWidth;
        if (rootRef.current &&
            keysArray &&
            keysArray.length > 0 &&
            tabBarWidthCurrent &&
            tabBarWidthCurrent > 0) {
            //Set tabBarWidth if not the current value
            if (tabBarWidth !== tabBarWidthCurrent) {
                setTabBarWidth(tabBarWidthCurrent);
            }
            if (tabMaxWidth === undefined) {
                const isOverflow = tabBarWidthCurrent > 0 && tabBarWidthCurrent < tabBarScrollWidthCurrent;
                //If there is no overflow maxWidth should be its prevState, prevState may also be undefined if truncation is disabled.
                if (!isOverflow) {
                    setTabMaxWidth((prevState) => {
                        return prevState;
                    });
                }
                else {
                    //We need to calculate maxWidth only if truncation threshold limit is not reached
                    if (!isAllItemsTruncatedMax.current) {
                        const tabWidths = tabBarUtils.getTabWidths(rootRef.current, trailingContentWidths, isTruncationDisabled, tabBarWidthCurrent, isAllItemsTruncatedMax?.current, keysArray);
                        //If all items have reached max treshold of truncation then we set it to true
                        if (tabWidths.every((item) => item.hasReachedLimit)) {
                            isAllItemsTruncatedMax.current = true;
                        }
                        tabDimensions.current = tabWidths;
                    }
                    //If all items have reached max treshold of truncation then we do not have update tabDimensions are maxWidth cannot go lesser than 96px.
                    else {
                        tabDimensions.current = tabDimensions.current?.map((item) => ({
                            ...item
                        }));
                    }
                }
                //Set calculated maxWidths
                if (!isTruncationDisabled) {
                    const tabMaxWidthMap = new Map(tabDimensions.current?.map((tab) => [tab.key, (tab.maxWidth + 'px')]));
                    setTabMaxWidth(tabMaxWidthMap);
                }
                //Overflow calculation
                if (tabBarWidthCurrent < tabBarScrollWidthCurrent) {
                    const arr = tabDimensions.current?.map((item) => item.cumulativeWidth);
                    //In addition to scrollWidth we check if the cumulative width of last item is greater than rootRef clientwidth
                    if (arr && tabBarWidthCurrent < arr[arr.length - 1]) {
                        const { closestEntry, closestIndex } = searchClosest(arr, tabBarWidthCurrent);
                        // Check for not null, because even for '0' value of closestIndex we want to slice in such cases only overflow item will be rendered
                        if (closestEntry && closestIndex != null) {
                            setVisibleItemKeys(keysArray.slice(0, closestIndex));
                            setOverflowingItemKeys(keysArray?.slice(closestIndex));
                            //Once OverflowTabBarItem is rendered, i.e overflowItemKeys move into overflow menu then space is available, which can be re-dsitributed to truncated visible items. We also check if these visible items have already gone through this calculation otherwise we skip it to avoid infinite loop.
                            if (closestIndex > 0 && !isAllItemsTruncatedFinal.current && !isTruncationDisabled) {
                                const tabWidthsAfterOverflow = tabBarUtils.getTabWidths(rootRef.current, trailingContentWidths, isTruncationDisabled, tabBarWidthCurrent, isAllItemsTruncatedMax?.current);
                                const tabMaxWidthMapAfterOverflow = new Map(tabWidthsAfterOverflow.map((tab) => [tab.key, (tab.maxWidth + 'px')]));
                                //Flag to indicate that final truncation calculation has occured
                                isAllItemsTruncatedFinal.current = true;
                                setTabMaxWidth(tabMaxWidthMapAfterOverflow);
                            }
                        }
                    }
                }
            }
        }
    }, [keysArray, rootRef, tabBarWidth, tabMaxWidth, isTruncationDisabled]);
    const updateTabBarWidth = hooks.useCallback((entry) => {
        const rectWidth = Math.ceil(entry.contentRect.width);
        // Sometimes entry.contentRect.width maybe a decimal value so we need to get absolute value
        if (Math.abs(tabBarWidth - entry.contentRect.width) > 1) {
            setTabBarWidth(entry.contentRect.width);
            if (tabMaxWidth !== undefined && !isAllItemsTruncatedMax.current) {
                setTabMaxWidth(undefined);
            }
            //Once there is change in the container width and there is no overflow we render all items as visible.
            if (rectWidth >= rootRef.current?.scrollWidth) {
                setVisibleItemKeys(keysArray);
                setOverflowingItemKeys([]);
            }
            isAllItemsTruncatedFinal.current = false;
        }
    }, [tabBarWidth, tabMaxWidth, rootRef, keysArray]);
    useResizeObserver.useResizeObserver(rootRef, updateTabBarWidth);
    return shouldRenderAllTabs(visibleItemKeys, overflowItemKeys, keysArray)
        ? {
            visibleItemKeys: keysArray,
            overflowItemKeys,
            maxWidths: tabMaxWidth
        }
        : {
            visibleItemKeys,
            overflowItemKeys,
            maxWidths: tabMaxWidth
        };
}
//If the overflow + visible keys or their quantity are not the same as keysArray then we first render all the keys as visible
const shouldRenderAllTabs = (visibleItemKeys, overflowItemKeys, keysArray) => {
    if (visibleItemKeys == null)
        return true;
    else if (visibleItemKeys && overflowItemKeys && keysArray) {
        const combinedArray = [...visibleItemKeys, ...overflowItemKeys];
        const isNotEqual = combinedArray.length !== keysArray.length ||
            JSON.stringify(keysArray) !== JSON.stringify(combinedArray);
        return isNotEqual;
    }
    else
        return false;
};
//Searches for a value closest and smaller than search value from the cumulative array
const searchClosest = (arr, searchValue) => {
    {
        if (arr != null && searchValue != null) {
            let left = 0;
            let right = arr.length - 1;
            let closestEntry = null;
            let closestIndex = null;
            while (left <= right && left < arr.length && right > -1) {
                const mid = Math.floor((left + right) / 2);
                const midEntry = arr[mid];
                if (midEntry === searchValue) {
                    return { closestEntry: midEntry, closestIndex: mid };
                }
                else if (midEntry < searchValue) {
                    closestEntry = midEntry;
                    closestIndex = mid;
                    left = mid + 1;
                }
                else {
                    right = mid - 1;
                }
                //If the searchValue is greater than closest entry and their difference is greater more item width then we have space to fit more item. Otherwise we return the index with the closest and small value when compared to searchvalue
                if (closestEntry && searchValue > closestEntry) {
                    //NOTE: Do not update closestEntry to midEntry here because value from const midEntry = arr[mid], midEntry maybe greater than searchValue and below it may end up updating closestIndex to mid +1 due to abs check
                    //Sometimes closest entry may be much smaller than searchvalue so in such cases we maybe able to accomodate more items. The difference between searchValue and closestEntry should be greater than the minWidth needed for any item so that it can be rendered. If the difference is lesser than item min width we then check if space is available to render overflowitem alone else we reduce the closestIndex.
                    closestIndex =
                        Math.abs(searchValue - closestEntry) > TAB_MIN_WIDTH && mid + 1 <= arr.length - 2
                            ? mid + 1
                            : Math.abs(searchValue - closestEntry) > MORE_ITEM_WIDTH
                                ? mid
                                : mid - 1 >= 0
                                    ? mid - 1
                                    : 0;
                }
                //If the searchValue is lesser than closestEntry we return a value lesser than mid, this happens when all the values in the array are lesser than searchValue, i.e screensize is extremely small
                else {
                    closestIndex = mid - 1 >= 0 ? mid - 1 : 0;
                }
            }
            return { closestEntry: closestEntry, closestIndex: closestIndex };
        }
        else
            return { closestEntry: null, closestIndex: null };
    }
};

exports.useDetectHorizontalOverflow = useDetectHorizontalOverflow;
//# sourceMappingURL=useDetectHorizontalOverflow-978848a8.js.map
