import { jsx as _jsx } from "react/jsx-runtime";
import { useCallback, useMemo } from 'react';
import { Responsive } from 'react-grid-layout';
import { useLayoutEffect, useState, forwardRef } from 'react';
import styled from 'styled-components';
import { debounce, hasProp } from '../../../utils';
import Flex, {} from '../../Flex';
/**
 * Returns a sorter function to apply on Grid children to ensure intuitive tabbing order
 * @param currentLayout - Layout objects at the current breakpoint
 * @param numColumns - Number of columns at the current breakpoint
 * @returns A sorter function to apply on Grid children
 */
export const useGridItemSorter = (currentLayout, numColumns) => {
    // Elements are expected to be navigated/tabbed in a row-major order.
    // This order should be based on the top-left {x,y} coordinates of each element.
    // Setting up weighted values based on each item's location, to ensure the row-major order.
    const keyWiseValues = useMemo(() => {
        // Can be undefined on load and on breakpoint changes.
        if (currentLayout === undefined ||
            currentLayout === null ||
            numColumns === undefined ||
            numColumns === null)
            return {};
        const returnObj = {};
        currentLayout.forEach(item => {
            returnObj[item.i] = item.x + numColumns * item.y;
        });
        return returnObj;
    }, [currentLayout, numColumns]);
    // Function to be used in `Array.sort()`
    return useCallback((a, b) => {
        // Safety check. By default, we expect `key`s to be present on all children.
        if (a.key === undefined || a.key === null || b.key === undefined || b.key === null)
            return 0;
        // In case the element is just added to the layout or if the grid just moved to a different breakpoint.
        if (keyWiseValues[a.key] === undefined ||
            keyWiseValues[a.key] === null ||
            keyWiseValues[b.key] === undefined ||
            keyWiseValues[b.key] === null)
            return 0;
        return keyWiseValues[a.key] - keyWiseValues[b.key];
    }, [keyWiseValues]);
};
/**
 * Determines the current breakpoint and numColumns based on the given width.
 * Uses the same mechanism as React-grid-layout.
 * @param width - Container width
 * @param breakpoints - Breakpoint configuration
 * @param cols - Columns configured at different breakpoints
 * @returns Current breakpoint and # of columns
 */
export const getBreakpointAndCols = (width, breakpoints, cols) => {
    // Reading it this way due to: https://github.com/DefinitelyTyped/DefinitelyTyped/issues/35370
    const { getBreakpointFromWidth } = Responsive.utils;
    const breakpoint = getBreakpointFromWidth(breakpoints, width);
    const numColumns = cols[breakpoint];
    return { breakpoint, numColumns };
};
/**
 * Determines the rowHeight to use based on auto-fit configuration.
 * Returns a static rowHeight & `rowsPerPage: null` if no auto-fit configuration specified for the current breakpoint.
 * @param breakpoint - Current breakpoint
 * @param parentHeight - Height of the container element
 * @param gap - Gutter size in pixels
 * @param rows - Rows configuration
 * @param layoutModel - Breakpoint-wise layout model
 * @returns Row height in pixels
 */
export const getRowHeight = (breakpoint, parentHeight, gap, rows, layoutModel) => {
    // If no rows configuration specified at the current breakpoint, return static values.
    if (!rows[breakpoint]) {
        return { rowHeight: 80, numRowsPerPage: null };
    }
    // If a fixed height is specified, use that
    if ('rowHeight' in rows[breakpoint]) {
        return { rowHeight: rows[breakpoint].rowHeight, numRowsPerPage: null };
    }
    // If rowsPerPage is specified, apply AUTO-FIT
    const numRowsPerPage = rows[breakpoint].rowsPerPage;
    const marginY = gap;
    // AUTO-FIT CALC: numRowsPerPage * (rowHeight + marginY) = parentHeight
    const rowHeight = parentHeight / numRowsPerPage - marginY;
    // When single-row items are present, ensure a minimum of 80px for those items
    // Else, ensure a minimum of 60px to keep the content from getting squished too much
    const hasSingleRowItems = layoutModel?.[breakpoint]?.some(item => item.h === 1);
    const minRowHeight = hasSingleRowItems ? 80 : 60;
    return { rowHeight: Math.max(rowHeight, minRowHeight), numRowsPerPage };
};
/**
 * Checks the current layouts for any gaps between/around grid items.
 * @param layout - Layout objects at a given breakpoint
 * @param numColumns - # of columns at the breakpoint
 * @returns A flag indicating whether there are gaps
 */
export const checkLayoutForGaps = (layout, numColumns) => {
    // Since the grid items don't overlap,
    // we can simply count how many cells are occupied by the grid-items
    // and then see if the full grid has more cells than the filled count.
    // Y-coordinate for the lowermost row (dummy row)
    let lastRowY = 0;
    let numFilledCells = 0;
    layout.forEach(item => {
        lastRowY = Math.max(lastRowY, item.y + item.h);
        numFilledCells += item.w * item.h;
    });
    return numFilledCells < numColumns * lastRowY;
};
/**
 * Sets up a ResizeObserver on the given element and returns a new size whenever the element is resized.
 * NOTE: The input element and the output can be null on load when the corresponding ref is yet to be initialized.
 * @param element - Element to monitor size
 * @param delay - Debounce delay for the observer
 * @returns The current size object for the element
 */
export const useMonitoredSize = (element, delay = 250) => {
    const [size, setSize] = useState(null);
    useLayoutEffect(() => {
        if (!element)
            return; // If not yet initialized
        const resizeObserver = new ResizeObserver(debounce((entries) => {
            setSize(entries[0].contentRect);
        }, delay));
        resizeObserver.observe(element);
        return () => resizeObserver.disconnect();
    }, [element, delay]);
    return size;
};
/**
 * Creates an array from the range specified by the args. Supports both ascending and descending ranges
 * @param left - Left bound for the range
 * @param right - Right bound for the range
 * @param inclusive - Specifies whether the right bound should be included in the range
 * @returns - A new array
 */
export const getArrayFromRange = (left, right, inclusive) => {
    const range = [];
    const ascending = left < right;
    const increment = ascending ? 1 : -1;
    const end = !inclusive ? right : right + increment;
    for (let i = left; ascending ? i < end : i > end; i += increment) {
        range.push(i);
    }
    return range;
};
/**
 * Check for a non-null existence of the prop.
 * @param obj - Target object.
 * @param prop - Target property.
 * @returns A boolean flag indicating the non-null existence of the property.
 */
export const hasNonNullProp = (obj, prop
// eslint-disable-next-line @typescript-eslint/ban-types
) => {
    return hasProp(obj, prop) && obj[prop] !== null && obj[prop] !== undefined;
};
const StyledContent = styled(Flex) `
  /* https://stackoverflow.com/a/36247448 */
  min-height: 0;
  min-width: 0;
`;
/**
 * Wraps the children around a Flex item,
 * that grows exactly into the remaining parent height/width (depending on flex direction),
 * regardless of the child content.
 * @component
 */
export const ParentFitFlexItem = forwardRef(({ children, item, ...restProps }, ref) => {
    // TODO Do we need basis: '0'? Everything seems to work fine without it
    return (_jsx(StyledContent, { ref: ref, item: { grow: 1, basis: '0', ...item }, ...restProps, children: children }));
});
/**
 * Returns last items in layout, vertically
 * @param layout - Grid layout
 * @returns Last items in layout
 */
export const getLastItemsInLayout = (layout) => {
    if (!layout)
        return [];
    // To store the last elements
    const lastElements = [];
    layout.forEach(({ x, y, w, h, i }) => {
        const bottomEdge = y + h;
        const isLast = !layout.some(({ x: x2, y: y2, w: w2, i: i2 }) => 
        // Check if another item is below the current one
        i2 !== i && x2 < x + w && x2 + w2 > x && y2 >= bottomEdge);
        if (isLast) {
            lastElements.push(i);
        }
    });
    return lastElements;
};
/**
 * This method converts the layout model from ltr to rtl
 * @param layout - current layoutModel
 * @param cols - numColumns on the Grid for each breakpoint
 * @returns rtl layout
 */
export const convertLtrToRtl = (layout, cols) => {
    const newLayout = {};
    // Updating the x positions for RTL by calculating the mirrored x value
    Object.keys(layout).forEach(breakpoint => {
        const totalWidth = cols[breakpoint];
        newLayout[breakpoint] = layout[breakpoint]?.map((item) => {
            return {
                ...item,
                x: totalWidth ? totalWidth - item.x - item.w : item.x
            };
        });
    });
    return newLayout;
};
//# sourceMappingURL=utils.js.map