var __rest = (this && this.__rest) || function (s, e) {
    var t = {};
    for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
        t[p] = s[p];
    if (s != null && typeof Object.getOwnPropertySymbols === "function")
        for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
            if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
                t[p[i]] = s[p[i]];
        }
    return t;
};
/* https://github.com/mui/material-ui/blob/master/packages/mui-base/src/TextareaAutosize/TextareaAutosize.tsx */
import React, { forwardRef, useEffect, useRef, useState } from "react";
import ReactDOM from "react-dom";
import { debounce, useClientLayoutEffect } from "../../../utils-external/index.js";
import { ownerWindow } from "../../helpers/index.js";
import { useMergeRefs } from "../../hooks/index.js";
const checkState = (prevState, newState, renders) => {
    const { outerHeightStyle, overflow } = newState;
    // Need a large enough difference to update the height.
    // This prevents infinite rendering loop.
    if (renders.current < 20 &&
        ((outerHeightStyle > 0 &&
            Math.abs((prevState.outerHeightStyle || 0) - outerHeightStyle) > 1) ||
            prevState.overflow !== overflow)) {
        renders.current += 1;
        return newState;
    }
    if (process.env.NODE_ENV !== "production" && renders.current === 20) {
        console.error("Textarea: Too many re-renders. The layout is unstable. TextareaAutosize limits the number of renders to prevent an infinite loop.");
    }
    return prevState;
};
function getStyleValue(value) {
    return parseInt(value, 10) || 0;
}
const TextareaAutosize = forwardRef((_a, ref) => {
    var _b, _c;
    var { className, onChange, maxRows, minRows = 1, autoScrollbar, style, value } = _a, other = __rest(_a, ["className", "onChange", "maxRows", "minRows", "autoScrollbar", "style", "value"]);
    const { current: isControlled } = useRef(value != null);
    const inputRef = useRef(null);
    const handleRef = useMergeRefs(inputRef, ref);
    const shadowRef = useRef(null);
    const renders = useRef(0);
    const [state, setState] = useState({ outerHeightStyle: 0 });
    const getUpdatedState = React.useCallback((input) => {
        const containerWindow = ownerWindow(input);
        const computedStyle = containerWindow.getComputedStyle(input);
        // If input's width is shrunk and it's not visible, don't sync height.
        if (computedStyle.width === "0px") {
            return { outerHeightStyle: 0 };
        }
        const inputShallow = shadowRef.current;
        inputShallow.style.width = computedStyle.width;
        inputShallow.value = input.value || other.placeholder || "x";
        if (inputShallow.value.slice(-1) === "\n") {
            // Certain fonts which overflow the line height will cause the textarea
            // to report a different scrollHeight depending on whether the last line
            // is empty. Make it non-empty to avoid this issue.
            inputShallow.value += " ";
        }
        const boxSizing = computedStyle.boxSizing;
        const padding = getStyleValue(computedStyle.paddingBottom) +
            getStyleValue(computedStyle.paddingTop);
        const border = getStyleValue(computedStyle.borderBottomWidth) +
            getStyleValue(computedStyle.borderTopWidth);
        // The height of the inner content
        const innerHeight = inputShallow.scrollHeight - padding;
        // Measure height of a textarea with a single row
        inputShallow.value = "x";
        const singleRowHeight = inputShallow.scrollHeight - padding;
        // The height of the outer content
        let outerHeight = innerHeight;
        if (minRows) {
            outerHeight = Math.max(Number(minRows) * singleRowHeight, outerHeight);
        }
        if (maxRows) {
            outerHeight = Math.min(Number(maxRows) * singleRowHeight, outerHeight);
        }
        outerHeight = Math.max(outerHeight, singleRowHeight);
        // Take the box sizing into account for applying this value as a style.
        const outerHeightStyle = outerHeight + (boxSizing === "border-box" ? padding + border : 0);
        const overflow = Math.abs(outerHeight - innerHeight) <= 1;
        return { outerHeightStyle, overflow };
    }, [maxRows, minRows, other.placeholder]);
    const syncHeight = () => {
        const input = inputRef.current;
        if (!input) {
            return;
        }
        const newState = getUpdatedState(input);
        if (isEmpty(newState)) {
            return;
        }
        setState((prevState) => checkState(prevState, newState, renders));
    };
    useClientLayoutEffect(() => {
        const syncHeightWithFlushSync = () => {
            const inputElement = inputRef.current;
            if (!inputElement) {
                return;
            }
            const newState = getUpdatedState(inputElement);
            if (isEmpty(newState)) {
                return;
            }
            // In React 18, state updates in a ResizeObserver's callback are happening after
            // the paint, this leads to an infinite rendering.
            // Using flushSync ensures that the state is updated before the next paint.
            // Related issue - https://github.com/facebook/react/issues/24331
            ReactDOM.flushSync(() => {
                setState((prevState) => checkState(prevState, newState, renders));
            });
        };
        const handleResize = debounce(() => {
            var _a, _b, _c;
            renders.current = 0;
            if (((_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.style.height) || ((_b = inputRef.current) === null || _b === void 0 ? void 0 : _b.style.width)) {
                // User has resized manually
                if (((_c = inputRef.current) === null || _c === void 0 ? void 0 : _c.style.overflow) === "hidden") {
                    setState((oldState) => (Object.assign(Object.assign({}, oldState), { overflow: false }))); // The state update isn't important, we just need to trigger a rerender
                }
                return;
            }
            syncHeightWithFlushSync();
        }, 166, true);
        const input = inputRef.current;
        const containerWindow = ownerWindow(input);
        containerWindow.addEventListener("resize", handleResize);
        let resizeObserver;
        if (typeof ResizeObserver !== "undefined") {
            resizeObserver = new ResizeObserver(handleResize);
            resizeObserver.observe(input);
        }
        return () => {
            handleResize.clear();
            containerWindow.removeEventListener("resize", handleResize);
            if (resizeObserver) {
                resizeObserver.disconnect();
            }
        };
    }, [getUpdatedState]);
    useClientLayoutEffect(() => {
        syncHeight();
    });
    // biome-ignore lint/correctness/useExhaustiveDependencies: Since value is an external prop, we want to reset the renders on every time it changes, even when it is undefined or empty.
    useEffect(() => {
        renders.current = 0;
    }, [value]);
    const handleChange = (event) => {
        renders.current = 0;
        if (!isControlled) {
            syncHeight();
        }
        if (onChange) {
            onChange(event);
        }
    };
    const mainStyle = Object.assign({ "--__axc-textarea-height": state.outerHeightStyle
            ? state.outerHeightStyle + "px"
            : "auto", 
        // Need a large enough difference to allow scrolling.
        // This prevents infinite rendering loop.
        overflow: state.overflow &&
            !autoScrollbar &&
            !((_b = inputRef.current) === null || _b === void 0 ? void 0 : _b.style.height) &&
            !((_c = inputRef.current) === null || _c === void 0 ? void 0 : _c.style.width)
            ? "hidden"
            : undefined }, style);
    return (React.createElement(React.Fragment, null,
        React.createElement("textarea", Object.assign({ value: value, onChange: handleChange, ref: handleRef, 
            // Apply the rows prop to get a "correct" first SSR paint
            rows: minRows, style: mainStyle }, other, { className: className })),
        React.createElement("textarea", { "aria-hidden": true, className: className, readOnly: true, ref: shadowRef, tabIndex: -1, style: Object.assign({ 
                // Visibility needed to hide the extra text area on iPads
                visibility: "hidden", 
                // Remove from the content flow
                position: "absolute", 
                // Ignore the scrollbar width
                overflow: "hidden", height: 0, top: 0, left: 0, 
                // Create a new layer, increase the isolation of the computed values
                transform: "translateZ(0)" }, style) })));
});
function isEmpty(obj) {
    return (obj === undefined ||
        obj === null ||
        Object.keys(obj).length === 0 ||
        (obj.outerHeightStyle === 0 && !obj.overflow));
}
export default TextareaAutosize;
//# sourceMappingURL=TextareaAutoSize.js.map