import { __decorate } from "tslib";
import { html } from "lit";
import { property } from "lit/decorators.js";
import { UiElement } from "../UiElement.js";
import { isDisabled, setDisabled } from "../lib/disabled.js";
/**
 * @fires transitioning - When the element is transitioning.
 * @slot - Main slot for the content
 */
export default class UiCollapse extends UiElement {
    /**
     * Whether the chip is disabled. The user can't interact with the chip when `true`.
     * @attribute
     */
    get disabled() {
        return isDisabled(this);
    }
    set disabled(value) {
        const old = isDisabled(this);
        setDisabled(this, value);
        this.requestUpdate('disabled', old);
    }
    get dimension() {
        return this.horizontal ? 'width' : 'height';
    }
    /**
     * `maxWidth` or `maxHeight`.
     * @private
     */
    get dimensionMax() {
        return this.horizontal ? 'maxWidth' : 'maxHeight';
    }
    /**
     * `max-width` or `max-height`.
     * @private
     */
    get dimensionMaxCss() {
        return this.horizontal ? 'max-width' : 'max-height';
    }
    get isAttached() {
        return !!this.parentNode;
    }
    /**
     * @return When true, the element is transitioning its open state. When false,
     * the element has finished opening/closing.
     */
    get transitioning() {
        return this.transitioningInternal;
    }
    constructor() {
        super();
        /**
         * Renders the collapse horizontally when true and vertically otherwise
         * @attribute
         */
        this.horizontal = false;
        /**
         * Set opened to true to show the collapse element and to false to hide it.
         * @attribute
         */
        this.open = false;
        this.transitioningInternal = false;
        /**
         * Stores the desired size of the collapse body.
         */
        this.desiredSize = '';
        this.addEventListener('transitionend', this.handleTransitionEnd.bind(this));
    }
    connectedCallback() {
        super.connectedCallback();
        if (!this.hasAttribute('role')) {
            this.setAttribute('role', 'group');
        }
        if (!this.hasAttribute('aria-hidden')) {
            this.setAttribute('aria-hidden', 'true');
        }
    }
    willUpdate(cp) {
        if (cp.has('open')) {
            this.handleOpenChange();
        }
        if (cp.has('horizontal')) {
            this.handleHorizontalChange();
        }
        super.willUpdate(cp);
    }
    /**
     * Toggle the opened state.
     */
    toggle() {
        this.open = !this.open;
        this.dispatchEvent(new Event('open'));
    }
    /**
     * Updates the size of the element.
     * @param size The new value for `maxWidth`/`maxHeight` as css property value, usually `auto` or `0px`.
     * @param animated if `true` updates the size with an animation, otherwise without.
     */
    updateSize(size, animated) {
        // Consider 'auto' as '', to take full size.
        let sizeValue = size === 'auto' ? '' : size;
        let willAnimate = animated && !this.noAnimation && this.isAttached;
        this.desiredSize = sizeValue;
        this.updateTransition(false);
        // If we can animate, must do some prep work.
        if (willAnimate) {
            // Animation will start at the current size.
            const startSize = this.calcSize();
            // For `auto` we must calculate what is the final size for the animation.
            // After the transition is done, _transitionEnd will set the size back to
            // `auto`.
            if (sizeValue === '') {
                this.style[this.dimensionMax] = '';
                sizeValue = this.calcSize();
            }
            // Go to startSize without animation.
            this.style[this.dimensionMax] = startSize;
            // Force layout to ensure transition will go. Set scrollTop to itself
            // so that compilers won't remove it.
            // eslint-disable-next-line no-self-assign
            this.scrollTop = this.scrollTop;
            // Enable animation.
            this.updateTransition(true);
            // If final size is the same as startSize it will not animate.
            willAnimate = (sizeValue !== startSize);
        }
        // Set the final size.
        this.style[this.dimensionMax] = sizeValue;
        // If it won't animate, call transitionEnd to set correct styles.
        if (!willAnimate) {
            this.transitionEnd();
        }
    }
    updateTransition(enabled) {
        this.style.transitionDuration = (enabled && !this.noAnimation) ? '' : '0s';
    }
    /**
     * Calculates the size of the element when opened.
     */
    calcSize() {
        const value = this.getBoundingClientRect()[this.dimension];
        return `${value}px`;
    }
    transitionEnd() {
        this.style[this.dimensionMax] = this.desiredSize;
        this.toggleAttribute('collapse-closed', !this.open);
        this.toggleAttribute('collapse-opened', this.open);
        this.updateTransition(false);
        this.notifyResize();
        this.transitioningInternal = false;
        this.notifyTransitioning();
    }
    notifyResize() {
        this.dispatchEvent(new Event('resize', { bubbles: true, composed: true, cancelable: true }));
    }
    notifyTransitioning() {
        this.dispatchEvent(new Event('transitioning', { bubbles: true, composed: true, cancelable: true }));
    }
    handleTransitionEnd(e) {
        const target = e.composedPath().find((node) => node === this);
        if (target) {
            this.transitionEnd();
        }
    }
    handleOpenChange() {
        const { open } = this;
        this.setAttribute('aria-hidden', String(!open));
        this.transitioningInternal = true;
        this.notifyTransitioning();
        this.toggleAttribute('collapse-closed', false);
        this.toggleAttribute('collapse-opened', false);
        this.updateSize(open ? 'auto' : '0px', true);
        // Focus the current collapse.
        if (open) {
            this.focus();
        }
    }
    handleHorizontalChange() {
        this.style.transitionProperty = this.dimensionMaxCss;
        const otherDimension = this.dimensionMax === 'maxWidth' ? 'maxHeight' : 'maxWidth';
        this.style[otherDimension] = '';
        this.updateSize(this.open ? 'auto' : '0px', false);
    }
    render() {
        return html `<slot></slot>`;
    }
}
__decorate([
    property({ type: Boolean, reflect: true })
], UiCollapse.prototype, "disabled", null);
__decorate([
    property({ type: Boolean, reflect: true })
], UiCollapse.prototype, "noAnimation", void 0);
__decorate([
    property({ type: Boolean })
], UiCollapse.prototype, "horizontal", void 0);
__decorate([
    property({ type: Boolean, reflect: true })
], UiCollapse.prototype, "open", void 0);
//# sourceMappingURL=UiCollapse.js.map