import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { useCallback, useEffect, useImperativeHandle, useMemo, useState } from 'react';
import { Responsive as ResponsiveGridLayout } from 'react-grid-layout';
import styled, { css } from 'styled-components';
import Flex from '../../Flex';
import { useElement, useI18n } from '../../../hooks';
import { defaultThemeProp } from '../../../theme';
import useGridLayoutConfig, {} from './config';
import EditorBackground from './EditorBackground';
import EditorGridItem, { ResizeHandle, resizeHandleClassName } from './EditorGridItem';
import { checkLayoutForGaps, getBreakpointAndCols, getRowHeight, useGridItemSorter, useMonitoredSize } from './utils';
import ReactResizableStyles from './ReactResizableStyles';
import ReactGridLayoutStyles from './ReactGridLayoutStyles';
const StyledEditor = styled(Flex)(({ theme }) => {
    const { base: { palette: { interactive } } } = theme;
    return css `
    position: relative;

    /* Needed for the background grid to be visible */
    z-index: 0;
    block-size: 100%;
    overflow-y: auto;

    /* To minimize height triggers during dragging due to horizontal scrollbar showing up */
    overflow-x: hidden;

    .react-grid-item.react-grid-placeholder {
      background: ${interactive};
      opacity: 0.3;
    }
  `;
});
StyledEditor.defaultProps = defaultThemeProp;
/**
 * Editor component to create Grid Layouts
 * @component
 */
const GridLayoutEditor = ({ handle, children, layoutModel, rows, onModelChange, itemLabelMap, ...restProps }) => {
    // Track width changes: https://github.com/react-grid-layout/react-grid-layout#providing-grid-width
    const [wrapperEl, setWrapperEl] = useElement();
    const size = useMonitoredSize(wrapperEl);
    const width = size?.width;
    const height = size?.height;
    const commonGridConfig = useGridLayoutConfig();
    const t = useI18n();
    const { breakpoint, numColumns } = useMemo(() => getBreakpointAndCols(width || 0, restProps.breakpoints, restProps.cols), [width, restProps.breakpoints, restProps.cols]);
    const { rowHeight, numRowsPerPage } = useMemo(() => getRowHeight(breakpoint, height || 0, commonGridConfig.margin[0], rows, layoutModel), [breakpoint, commonGridConfig.margin, height, rows, layoutModel]);
    const currentLayout = useMemo(() => layoutModel?.[breakpoint], [breakpoint, layoutModel]);
    const onLayoutChange = useCallback((curLayout, allLayouts) => {
        onModelChange(!children || children.length === 0 ? undefined : allLayouts);
    }, [children, onModelChange]);
    // Callback to sort child DOM nodes to ensure an intuitive tabbing order
    const gridItemSorter = useGridItemSorter(currentLayout, numColumns);
    // Set up keyboard accessibility
    const [listEl, setListEl] = useElement();
    useEffect(() => {
        if (listEl) {
            listEl.setAttribute('role', 'list');
            listEl.setAttribute('tabindex', '0');
            listEl.setAttribute('aria-label', t('configurable_layout_instructions'));
        }
    }, [listEl, t]);
    const moveItem = useCallback((itemKey, deltaX, deltaY) => {
        // Should be initialized by this point
        if (!currentLayout)
            return;
        const itemIndex = currentLayout.findIndex(item => item.i === itemKey);
        // Should be present on the currentLayout by this point.
        if (itemIndex === -1)
            return;
        const oldItem = currentLayout[itemIndex];
        const newItem = {
            ...oldItem,
            // `Math.max` needed to avoid a glitch when moving left beyond the left edge
            x: Math.max(0, oldItem.x + deltaX),
            y: Math.max(0, oldItem.y + deltaY)
        };
        const newLayout = [
            ...currentLayout.slice(0, itemIndex),
            newItem,
            ...currentLayout.slice(itemIndex + 1)
        ];
        const newModel = { ...layoutModel, [breakpoint]: newLayout };
        onModelChange(!children || children.length === 0 ? undefined : newModel);
    }, [breakpoint, children, currentLayout, layoutModel, onModelChange]);
    const addOrSubtractItemSize = useCallback((itemKey, deltaW, deltaH) => {
        // Should be initialized by this point
        if (!currentLayout)
            return;
        const itemIndex = currentLayout.findIndex(item => item.i === itemKey);
        // Should be present on the currentLayout by this point.
        if (itemIndex === -1)
            return;
        const oldItem = currentLayout[itemIndex];
        const newItem = {
            ...oldItem,
            // `Math.max` needed to avoid a glitch when moving left beyond the left edge
            w: Math.max(oldItem.minW || 2, oldItem.w + deltaW),
            h: Math.max(oldItem.minH || 2, oldItem.h + deltaH)
        };
        const newLayout = [
            ...currentLayout.slice(0, itemIndex),
            newItem,
            ...currentLayout.slice(itemIndex + 1)
        ];
        const newModel = { ...layoutModel, [breakpoint]: newLayout };
        onModelChange(!children || children.length === 0 ? undefined : newModel);
    }, [breakpoint, children, currentLayout, layoutModel, onModelChange]);
    // Provide a way for the parent component to get layout-model on demand
    const [highlightedBG, setHighlightedBG] = useState(false);
    useImperativeHandle(handle, () => ({
        getLayoutModel: () => {
            // Without this check, we'll end up with the dummy model set up down below for this case
            if (!children || children.length === 0)
                return undefined;
            return layoutModel;
        },
        checkForGaps: () => {
            // On load when no items are added.
            if (!currentLayout)
                return true;
            const hasGaps = checkLayoutForGaps(currentLayout, numColumns);
            setHighlightedBG(hasGaps);
            return hasGaps;
        }
    }), [children, layoutModel, currentLayout, numColumns]);
    return (
    // Editor is always in LTR. At runtime, the layout is mirrored depending on locale.
    _jsxs(StyledEditor, { ref: setWrapperEl, dir: 'ltr', children: [_jsx(ReactResizableStyles, {}), _jsx(ReactGridLayoutStyles, {}), !!width && !!height && (_jsxs(_Fragment, { children: [_jsx(EditorBackground, { "data-testid": 'editor', layout: currentLayout, numColumns: numColumns, rowHeight: rowHeight, rowsPerPage: numRowsPerPage, highlighted: highlightedBG }), _jsxs(ResponsiveGridLayout, { ...commonGridConfig, width: width, rowHeight: rowHeight, onLayoutChange: onLayoutChange, resizeHandles: ['s', 'e', 'n', 'w', 'sw', 'nw', 'se', 'ne'], resizeHandle: _jsx(ResizeHandle, {}), 
                        // To prevent drag event on the resize handles
                        draggableCancel: `.${resizeHandleClassName}`, innerRef: setListEl, layouts: layoutModel, ...restProps, children: [children?.sort(gridItemSorter).map(child => {
                                const gridLayoutModel = layoutModel?.[breakpoint]?.filter(layout => layout.i === child.key);
                                return (_jsx(EditorGridItem, { itemKey: child.key, "data-grid": gridLayoutModel && gridLayoutModel[0], itemLabel: itemLabelMap[child.key] || '', moveItem: moveItem, addOrSubtractItemSize: addOrSubtractItemSize, children: child }, child.key));
                            }), (!children || children.length === 0) && (
                            // Placeholder when no children. Needed to avoid exceptions:
                            // https://github.com/react-grid-layout/react-grid-layout/issues/216
                            _jsx("div", { "data-grid": { x: 0, y: 0, w: 1, h: 1, resizeHandles: [] } }, '--'))] })] }))] }));
};
export default GridLayoutEditor;
//# sourceMappingURL=GridLayoutEditor.js.map