import React, { forwardRef, useCallback, useEffect, useRef, useState } from "react";
import { createPortal } from "react-dom";
import { useForwardedRef } from "@bedrock-layout/use-forwarded-ref";
import { Timeout } from "oop-timers";
import { HandleEsc } from "../../utils/HandleEsc.js";
import { Content, StyledDrawer, Tint } from "./Drawer.styled.js";
const RENDER_TIMEOUT = 500;
const TRANSITION_MS = 300;
const DEFAULT_CONTENT_SCALE = 0.92;
const DEFAULT_TINT_OPACITY = 0.1;
const PARTIAL_RADIUS = "16px";
const DEFAULT_SCALE_SELECTORS = ["#__next", "#root", "#storybook-root", "body>*:first-child"];
const closedTransform = {
    bottom: "translate(0, 100%)",
    top: "translate(0, -100%)",
    left: "translate(-100%, 0)",
    right: "translate(100%, 0)",
};
const getSizeCss = (size) => {
    if (size === undefined) {
        return undefined;
    }
    if (typeof size === "string") {
        return size;
    }
    if (size >= 0) {
        return `${size}px`;
    }
    return `calc(100% + ${size}px)`;
};
const positioningPresets = {
    bottom: (size) => ({ top: "auto", height: size }),
    top: (size) => ({ bottom: "auto", height: size }),
    left: (size) => ({ right: "auto", width: size }),
    right: (size) => ({ left: "auto", width: size }),
};
const radiusPresets = {
    bottom: { borderTopLeftRadius: PARTIAL_RADIUS, borderTopRightRadius: PARTIAL_RADIUS },
    top: { borderBottomLeftRadius: PARTIAL_RADIUS, borderBottomRightRadius: PARTIAL_RADIUS },
    left: { borderTopRightRadius: PARTIAL_RADIUS, borderBottomRightRadius: PARTIAL_RADIUS },
    right: { borderTopLeftRadius: PARTIAL_RADIUS, borderBottomLeftRadius: PARTIAL_RADIUS },
};
const getStaticStyle = (from, size) => {
    const sizeCss = getSizeCss(size);
    if (sizeCss === undefined) {
        return {};
    }
    return { ...positioningPresets[from](sizeCss), ...radiusPresets[from] };
};
const findScaleTargets = (selectors, mode) => {
    for (const sel of selectors) {
        if (mode === "first") {
            const el = document.querySelector(sel);
            if (el) {
                return [el];
            }
        }
        else {
            const els = Array.from(document.querySelectorAll(sel));
            if (els.length > 0) {
                return els;
            }
        }
    }
    return [];
};
const applyScale = (target, transform) => {
    target.style.setProperty("transition", `transform ${TRANSITION_MS}ms`);
    target.style.setProperty("transform-origin", "50% 0");
    target.style.setProperty("transform", transform);
};
const Drawer = forwardRef((props, ref) => {
    var _a, _b, _c;
    const drawerRef = useForwardedRef(ref);
    const [shouldRenderWhenClosed, setShouldRenderWhenClosed] = useState(false);
    const [isVisible, setIsVisible] = useState(props.isOpen);
    const timeoutRef = useRef(null);
    useEffect(() => {
        timeoutRef.current = new Timeout(() => {
            setShouldRenderWhenClosed(false);
        }, RENDER_TIMEOUT);
        return () => {
            var _a;
            (_a = timeoutRef.current) === null || _a === void 0 ? void 0 : _a.stop();
            timeoutRef.current = null;
        };
    }, []);
    useEffect(() => {
        var _a, _b;
        if (!props.isOpen) {
            (_a = timeoutRef.current) === null || _a === void 0 ? void 0 : _a.start();
            setIsVisible(false);
            return undefined;
        }
        (_b = timeoutRef.current) === null || _b === void 0 ? void 0 : _b.stop();
        setShouldRenderWhenClosed(true);
        const id = requestAnimationFrame(() => {
            setIsVisible(true);
        });
        return () => {
            cancelAnimationFrame(id);
        };
    }, [props.isOpen]);
    const { scaleContent, scaleSelectors, scaleSelectorsMode } = props;
    useEffect(() => {
        if (!scaleContent || !props.isOpen) {
            return undefined;
        }
        const selectors = scaleSelectors === undefined
            ? DEFAULT_SCALE_SELECTORS
            : (Array.isArray(scaleSelectors) ? scaleSelectors : [scaleSelectors]);
        const mode = scaleSelectorsMode !== null && scaleSelectorsMode !== void 0 ? scaleSelectorsMode : "first";
        const targets = findScaleTargets(selectors, mode);
        if (targets.length === 0) {
            return undefined;
        }
        const scale = typeof scaleContent === "number" ? scaleContent : DEFAULT_CONTENT_SCALE;
        targets.forEach((el) => {
            applyScale(el, `scale(${scale})`);
        });
        return () => {
            targets.forEach((el) => {
                applyScale(el, "");
            });
        };
    }, [props.isOpen, scaleContent, scaleSelectors, scaleSelectorsMode]);
    const from = (_a = props.from) !== null && _a !== void 0 ? _a : "bottom";
    const style = {
        ...getStaticStyle(from, props.size),
        transform: isVisible ? "translate(0, 0)" : closedTransform[from],
        transition: props.isOpen && !isVisible ? "none" : undefined,
    };
    const shouldRender = props.isOpen || shouldRenderWhenClosed;
    const closeOnEsc = (_b = props.closeOnEsc) !== null && _b !== void 0 ? _b : true;
    const esc = closeOnEsc && React.createElement(HandleEsc, { onPress: props.onClose });
    const portal = (_c = props.portal) !== null && _c !== void 0 ? _c : true;
    const overlayClick = props.onOverlayClick === undefined ? "close" : props.onOverlayClick;
    const { onClose } = props;
    const handleOverlayClick = useCallback(() => {
        if (overlayClick === "close") {
            onClose();
            return;
        }
        if (typeof overlayClick === "function") {
            overlayClick();
        }
    }, [overlayClick, onClose]);
    const hasOverlay = props.size !== undefined || Boolean(scaleContent);
    const overlayInteractive = overlayClick !== null && props.isOpen;
    const tintStyle = {
        opacity: isVisible ? DEFAULT_TINT_OPACITY : 0,
        pointerEvents: (overlayInteractive ? "auto" : "none"),
    };
    const tree = (React.createElement(React.Fragment, null,
        hasOverlay && (React.createElement(Tint, { style: tintStyle, onClick: handleOverlayClick, "aria-hidden": true })),
        React.createElement(StyledDrawer, { className: props.className, style: style, ref: drawerRef },
            esc,
            React.createElement(Content, null, shouldRender && props.children))));
    if (portal) {
        const root = typeof portal === "boolean" ? document.body : portal;
        return createPortal(tree, root);
    }
    return tree;
});
Drawer.displayName = "Drawer";
Drawer.toString = () => StyledDrawer.toString();
const DrawerContentSelector = Content.toString();
export { Drawer, DrawerContentSelector };
//# sourceMappingURL=Drawer.js.map