"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Collapse = exports.AnimationStates = void 0;
const tslib_1 = require("tslib");
const jsx_runtime_1 = require("react/jsx-runtime");
/*
 * Copyright 2015 Palantir Technologies, Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
const classnames_1 = tslib_1.__importDefault(require("classnames"));
const react_1 = require("react");
const common_1 = require("../../common");
const props_1 = require("../../common/props");
/**
 * `Collapse` can be in one of six states, enumerated here.
 * When changing the `isOpen` prop, the following happens to the states:
 * isOpen={true}  : CLOSED -> OPEN_START -> OPENING -> OPEN
 * isOpen={false} : OPEN -> CLOSING_START -> CLOSING -> CLOSED
 */
var AnimationStates;
(function (AnimationStates) {
    /**
     * The body is re-rendered, height is set to the measured body height and
     * the body Y is set to 0.
     */
    AnimationStates[AnimationStates["OPEN_START"] = 0] = "OPEN_START";
    /**
     * Animation begins, height is set to auto. This is all animated, and on
     * complete, the state changes to OPEN.
     */
    AnimationStates[AnimationStates["OPENING"] = 1] = "OPENING";
    /**
     * The collapse height is set to auto, and the body Y is set to 0 (so the
     * element can be seen as normal).
     */
    AnimationStates[AnimationStates["OPEN"] = 2] = "OPEN";
    /**
     * Height has been changed from auto to the measured height of the body to
     * prepare for the closing animation in CLOSING.
     */
    AnimationStates[AnimationStates["CLOSING_START"] = 3] = "CLOSING_START";
    /**
     * Height is set to 0 and the body Y is at -height. Both of these properties
     * are transformed, and then after the animation is complete, the state
     * changes to CLOSED.
     */
    AnimationStates[AnimationStates["CLOSING"] = 4] = "CLOSING";
    /**
     * The contents of the collapse is not rendered, the collapse height is 0,
     * and the body Y is at -height (so that the bottom of the body is at Y=0).
     */
    AnimationStates[AnimationStates["CLOSED"] = 5] = "CLOSED";
})(AnimationStates || (exports.AnimationStates = AnimationStates = {}));
/**
 * Collapse component.
 *
 * @see https://blueprintjs.com/docs/#core/components/collapse
 */
const Collapse = ({ children, className, component = "div", isOpen = false, keepChildrenMounted = false, transitionDuration = 200, }) => {
    const [animationState, setAnimationState] = (0, react_1.useState)(isOpen ? AnimationStates.OPEN : AnimationStates.CLOSED);
    const [height, setHeight] = (0, react_1.useState)(isOpen ? "auto" : "0px");
    const [heightWhenOpen, setHeightWhenOpen] = (0, react_1.useState)(undefined);
    const [prevIsOpen, setPrevIsOpen] = (0, react_1.useState)(isOpen);
    const isOpenRef = (0, react_1.useRef)(isOpen);
    isOpenRef.current = isOpen;
    const contents = (0, react_1.useRef)(null);
    const animationStateRef = (0, react_1.useRef)(animationState);
    animationStateRef.current = animationState;
    const delayedTimerRef = (0, react_1.useRef)();
    const onDelayedStateChange = (0, react_1.useCallback)(() => {
        switch (animationStateRef.current) {
            case AnimationStates.OPENING:
                setAnimationState(AnimationStates.OPEN);
                setHeight("auto");
                break;
            case AnimationStates.CLOSING:
                setAnimationState(AnimationStates.CLOSED);
                break;
            default:
                break;
        }
    }, []);
    // Synchronize animationState with the isOpen prop during render.
    if (isOpen !== prevIsOpen) {
        setPrevIsOpen(isOpen);
        if (isOpen) {
            switch (animationState) {
                case AnimationStates.OPEN:
                case AnimationStates.OPENING:
                    break;
                default:
                    setAnimationState(AnimationStates.OPEN_START);
            }
        }
        else {
            switch (animationState) {
                case AnimationStates.CLOSED:
                case AnimationStates.CLOSING:
                    break;
                default:
                    setAnimationState(AnimationStates.CLOSING_START);
                    setHeight(`${heightWhenOpen}px`);
            }
        }
    }
    // Clean up delayed timer on unmount only (mirrors AbstractPureComponent.setTimeout behavior)
    (0, react_1.useEffect)(() => {
        return () => {
            if (delayedTimerRef.current != null) {
                clearTimeout(delayedTimerRef.current);
            }
        };
    }, []);
    // Handle animation state transitions
    (0, react_1.useEffect)(() => {
        if (!contents.current)
            return undefined;
        switch (animationState) {
            case AnimationStates.OPEN_START: {
                const clientHeight = contents.current.clientHeight;
                setAnimationState(AnimationStates.OPENING);
                setHeight(`${clientHeight}px`);
                setHeightWhenOpen(clientHeight);
                clearTimeout(delayedTimerRef.current);
                delayedTimerRef.current = setTimeout(onDelayedStateChange, transitionDuration);
                break;
            }
            case AnimationStates.CLOSING_START: {
                const clientHeight = contents.current.clientHeight;
                const immediateTimer = setTimeout(() => {
                    setAnimationState(AnimationStates.CLOSING);
                    setHeight("0px");
                    setHeightWhenOpen(clientHeight);
                });
                clearTimeout(delayedTimerRef.current);
                delayedTimerRef.current = setTimeout(onDelayedStateChange, transitionDuration);
                return () => clearTimeout(immediateTimer);
            }
            default:
                break;
        }
        return undefined;
    }, [animationState, transitionDuration, onDelayedStateChange]);
    const contentsRefHandler = (0, react_1.useCallback)((element) => {
        contents.current = element;
        if (contents.current != null) {
            const contentHeight = contents.current.clientHeight;
            setAnimationState(isOpenRef.current ? AnimationStates.OPEN : AnimationStates.CLOSED);
            setHeight(contentHeight === 0 ? undefined : `${contentHeight}px`);
            setHeightWhenOpen(contentHeight === 0 ? undefined : contentHeight);
        }
    }, []);
    const isContentVisible = animationState !== AnimationStates.CLOSED;
    const shouldRenderChildren = isContentVisible || keepChildrenMounted;
    const displayWithTransform = isContentVisible && animationState !== AnimationStates.CLOSING;
    const isAutoHeight = height === "auto";
    const containerStyle = {
        height: isContentVisible ? height : undefined,
        overflowY: isAutoHeight ? "visible" : undefined,
        // transitions don't work with height: auto
        transition: isAutoHeight ? "none" : undefined,
    };
    const contentsStyle = {
        // only use heightWhenOpen while closing
        transform: displayWithTransform ? "translateY(0)" : `translateY(-${heightWhenOpen}px)`,
        // transitions don't work with height: auto
        transition: isAutoHeight ? "none" : undefined,
    };
    return (0, react_1.createElement)(component, {
        className: (0, classnames_1.default)(common_1.Classes.COLLAPSE, className),
        style: containerStyle,
    }, (0, jsx_runtime_1.jsx)("div", { className: common_1.Classes.COLLAPSE_BODY, ref: contentsRefHandler, style: contentsStyle, "aria-hidden": !isContentVisible, children: shouldRenderChildren ? children : null }));
};
exports.Collapse = Collapse;
exports.Collapse.displayName = `${props_1.DISPLAYNAME_PREFIX}.Collapse`;
//# sourceMappingURL=collapse.js.map