import { __decorate, __metadata } from "tslib";
import { h, Host, } from "@stencil/core";
import { BEM } from "../../utils/bem";
import { ListenToConfig } from "../../utils/config";
import { debounceEvent, raf, transitionEndAsync, waitForComponent } from "../../utils/helpers";
import { Logger } from "../../utils/log";
export class Accordion {
    constructor() {
        this.componentId = `bal-accordion-${accordionIds++}`;
        this.animationGeneration = 0;
        this.state = 1 /* AccordionState.Collapsed */;
        this.animated = true;
        /**
         * PUBLIC PROPERTY API
         * ------------------------------------------------------
         */
        /**
         * If `true` the accordion is open.
         */
        this.active = false;
        /**
         * Set the amount of time, in milliseconds, to wait to trigger the `balChange` event after each keystroke. This also impacts form bindings such as `ngModel` or `v-model`.
         */
        this.debounce = 0;
        /**
         * Label of the open trigger button
         */
        this.openLabel = '';
        /**
         * BalIcon of the open trigger button
         */
        this.openIcon = 'plus';
        /**
         * Label of the close trigger button
         */
        this.closeLabel = '';
        /**
         * BalIcon of the close trigger button
         */
        this.closeIcon = 'close';
        /**
         * If `true` the accordion is used on the bottom of a card
         */
        this.card = false;
        /**
         * defines the version of the component
         */
        this.version = 2;
        /**
         * PRIVATE METHODS
         * ------------------------------------------------------
         */
        this.updateState = (initialUpdate = false) => {
            if (this.active) {
                this.expand(initialUpdate);
            }
            else {
                this.collapse(initialUpdate);
            }
        };
        this.setState = (state) => {
            this.state = state;
            if (this.version === 2) {
                this.updateTriggerElement();
                this.updateDetailsElement();
                this.updateSummaryElement();
            }
        };
        this.updateDetailsElement = () => {
            const detailsElement = this.detailsElement;
            if (detailsElement) {
                detailsElement.state = this.state;
                detailsElement.active = this.active;
                detailsElement.animated = this.animated;
            }
        };
        this.updateTriggerElement = () => {
            const triggerElement = this.triggerElement;
            if (triggerElement) {
                triggerElement.state = this.state;
                triggerElement.active = this.active;
            }
        };
        this.updateSummaryElement = () => {
            const summaryElement = this.summaryElement;
            if (summaryElement) {
                summaryElement.state = this.state;
                summaryElement.active = this.active;
            }
        };
        this.cancelAnimation = () => {
            if (this.currentRaf !== undefined) {
                cancelAnimationFrame(this.currentRaf);
                this.currentRaf = undefined;
            }
            if (this.currentRafInner !== undefined) {
                cancelAnimationFrame(this.currentRafInner);
                this.currentRafInner = undefined;
            }
            this.animationGeneration++;
        };
        this.expand = (initialUpdate = false) => {
            this.active = true;
            const detailsElement = this.detailsElement;
            const detailsWrapperElement = this.detailsWrapperElement;
            if (initialUpdate || detailsElement === null || detailsWrapperElement === null) {
                this.setState(4 /* AccordionState.Expanded */);
                return this.active;
            }
            if (this.state === 4 /* AccordionState.Expanded */) {
                return this.active;
            }
            this.cancelAnimation();
            const generation = this.animationGeneration;
            if (this.shouldAnimate()) {
                this.currentRaf = raf(() => {
                    if (generation !== this.animationGeneration)
                        return;
                    this.setState(8 /* AccordionState.Expanding */);
                    this.balWillAnimate.emit(this.active);
                    this.currentRafInner = raf(async () => {
                        if (generation !== this.animationGeneration)
                            return;
                        const contentHeight = detailsWrapperElement.offsetHeight;
                        const waitForTransition = transitionEndAsync(detailsElement, 300);
                        detailsElement.style.setProperty('max-height', `${contentHeight}px`);
                        await waitForTransition;
                        if (generation !== this.animationGeneration)
                            return;
                        this.setState(4 /* AccordionState.Expanded */);
                        detailsElement.style.removeProperty('max-height');
                        this.balDidAnimate.emit(this.active);
                    });
                });
            }
            else {
                this.balWillAnimate.emit(this.active);
                this.setState(4 /* AccordionState.Expanded */);
                this.balDidAnimate.emit(this.active);
            }
            return this.active;
        };
        this.collapse = (initialUpdate = false) => {
            this.active = false;
            const detailsElement = this.detailsElement;
            if (initialUpdate || detailsElement === null) {
                this.setState(1 /* AccordionState.Collapsed */);
                return this.active;
            }
            if (this.state === 1 /* AccordionState.Collapsed */) {
                return this.active;
            }
            this.cancelAnimation();
            const generation = this.animationGeneration;
            if (this.shouldAnimate()) {
                this.currentRaf = raf(async () => {
                    if (generation !== this.animationGeneration)
                        return;
                    const contentHeight = detailsElement.offsetHeight;
                    detailsElement.style.setProperty('max-height', `${contentHeight}px`);
                    this.currentRafInner = raf(async () => {
                        if (generation !== this.animationGeneration)
                            return;
                        const waitForTransition = transitionEndAsync(detailsElement, 300);
                        this.setState(2 /* AccordionState.Collapsing */);
                        this.balWillAnimate.emit(this.active);
                        await waitForTransition;
                        if (generation !== this.animationGeneration)
                            return;
                        this.setState(1 /* AccordionState.Collapsed */);
                        detailsElement.style.removeProperty('max-height');
                        this.balDidAnimate.emit(this.active);
                    });
                });
            }
            else {
                this.balWillAnimate.emit(this.active);
                this.setState(1 /* AccordionState.Collapsed */);
                this.balDidAnimate.emit(this.active);
            }
            return this.active;
        };
        this.shouldAnimate = () => {
            if (typeof window === 'undefined') {
                return false;
            }
            return this.animated;
        };
        /**
         * EVENT BINDING
         * ------------------------------------------------------
         */
        this.onTriggerClickV1 = () => {
            this.humanToggle();
        };
    }
    createLogger(log) {
        this.log = log;
    }
    async activeChanged(newActive, oldActive) {
        if (newActive !== oldActive) {
            this.active = newActive;
            this.updateState();
        }
    }
    debounceChanged() {
        this.balChange = debounceEvent(this.balChange, this.debounce);
    }
    /**
     * LIFECYCLE
     * ------------------------------------------------------
     */
    async connectedCallback() {
        this.debounceChanged();
        await waitForComponent(this.el);
        if (this.active) {
            this.activeChanged(this.active, false);
        }
        this.updateState(true);
    }
    /**
     * LISTENERS
     * ------------------------------------------------------
     */
    /**
     * @internal define config for the component
     */
    async configChanged(state) {
        this.animated = state.animated;
    }
    /**
     * PUBLIC METHODS
     * ------------------------------------------------------
     */
    /**
     * Opens the accordion
     */
    async present() {
        return this.expand();
    }
    /**
     * Closes the accordion
     */
    async dismiss() {
        return this.collapse();
    }
    /**
     * Triggers the accordion
     */
    async toggle() {
        if (this.active) {
            return this.collapse();
        }
        else {
            return this.expand();
        }
    }
    /**
     * @internal
     */
    async humanToggle() {
        if (this.active) {
            await this.collapse();
        }
        else {
            await this.expand();
        }
        this.balChange.emit(this.active);
        return this.active;
    }
    /**
     * GETTERS
     * ------------------------------------------------------
     */
    get summaryElement() {
        var _a;
        return ((_a = this.el) === null || _a === void 0 ? void 0 : _a.querySelector(`#${this.componentId}-summary`)) || null;
    }
    get triggerElement() {
        var _a;
        return ((_a = this.el) === null || _a === void 0 ? void 0 : _a.querySelector(`#${this.componentId}-trigger`)) || null;
    }
    get detailsElement() {
        var _a;
        if (this.version === 1) {
            return this.contentEl || null;
        }
        return ((_a = this.el) === null || _a === void 0 ? void 0 : _a.querySelector(`#${this.componentId}-details`)) || null;
    }
    get detailsWrapperElement() {
        var _a;
        if (this.version === 1) {
            return this.contentElWrapper || null;
        }
        return ((_a = this.el) === null || _a === void 0 ? void 0 : _a.querySelector(`#${this.componentId}-details > div`)) || null;
    }
    /**
     * RENDER
     * ------------------------------------------------------
     */
    render() {
        return this.version === 2 ? this.renderVersion2() : this.renderVersion1();
    }
    renderVersion2() {
        const block = BEM.block('accordion');
        return (h(Host, { id: this.componentId, class: Object.assign(Object.assign(Object.assign(Object.assign({}, block.class()), block.modifier('active').class(this.active)), block.modifier('card-v2').class(this.card)), block.modifier('animated').class(this.animated)) }));
    }
    renderVersion1() {
        const label = this.active ? this.closeLabel : this.openLabel;
        const icon = this.active ? this.closeIcon : this.openIcon;
        const block = BEM.block('accordion');
        const expanded = this.state === 4 /* AccordionState.Expanded */ || this.state === 8 /* AccordionState.Expanding */;
        const buttonPart = expanded ? 'button expanded' : 'button';
        const contentPart = expanded ? 'content expanded' : 'content';
        return (h(Host, { id: this.componentId, class: Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, block.class()), block.modifier('card-v1').class(this.card)), block.modifier('active').class(this.active)), block.modifier('expanding').class(this.state === 8 /* AccordionState.Expanding */)), block.modifier('expanded').class(this.state === 4 /* AccordionState.Expanded */)), block.modifier('collapsing').class(this.state === 2 /* AccordionState.Collapsing */)), block.modifier('collapsed').class(this.state === 1 /* AccordionState.Collapsed */)), block.modifier('animated').class(this.animated)) }, h("div", { class: Object.assign({}, block.element('wrapper').class()) }, h("div", { class: Object.assign(Object.assign({}, block.element('trigger').class()), block.element('trigger').modifier('card').class(this.card)), "data-testid": "bal-accordion-summary" }, h("bal-button", { id: `${this.componentId}-button`, "aria-controls": `${this.componentId}-content`, part: buttonPart, "data-testid": "bal-accordion-trigger", expanded: true, color: 'info', icon: icon, onClick: this.onTriggerClickV1 }, label)), h("div", { id: `${this.componentId}-content`, "aria-labelledby": `${this.componentId}-button`, role: "region", part: contentPart, class: Object.assign(Object.assign(Object.assign({}, block.element('content').class()), block.element('content').modifier('card').class(this.card)), block
                .element('content')
                .modifier('overflow-hidden')
                .class(this.state !== 4 /* AccordionState.Expanded */)), ref: contentEl => (this.contentEl = contentEl) }, h("div", { id: `${this.componentId}-content-wrapper`, "data-testid": "bal-accordion-details", class: Object.assign({}, block.element('content').element('wrapper').class()), ref: contentElWrapper => (this.contentElWrapper = contentElWrapper) }, h("slot", null))))));
    }
    static get is() { return "bal-accordion"; }
    static get originalStyleUrls() {
        return {
            "$": ["bal-accordion.sass"]
        };
    }
    static get styleUrls() {
        return {
            "$": ["bal-accordion.css"]
        };
    }
    static get properties() {
        return {
            "active": {
                "type": "boolean",
                "mutable": true,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "If `true` the accordion is open."
                },
                "getter": false,
                "setter": false,
                "reflect": true,
                "attribute": "active",
                "defaultValue": "false"
            },
            "debounce": {
                "type": "number",
                "mutable": false,
                "complexType": {
                    "original": "number",
                    "resolved": "number",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Set the amount of time, in milliseconds, to wait to trigger the `balChange` event after each keystroke. This also impacts form bindings such as `ngModel` or `v-model`."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "debounce",
                "defaultValue": "0"
            },
            "openLabel": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Label of the open trigger button"
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "open-label",
                "defaultValue": "''"
            },
            "openIcon": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "BalIcon of the open trigger button"
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "open-icon",
                "defaultValue": "'plus'"
            },
            "closeLabel": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Label of the close trigger button"
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "close-label",
                "defaultValue": "''"
            },
            "closeIcon": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "BalIcon of the close trigger button"
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "close-icon",
                "defaultValue": "'close'"
            },
            "card": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "If `true` the accordion is used on the bottom of a card"
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "card",
                "defaultValue": "false"
            },
            "version": {
                "type": "number",
                "mutable": false,
                "complexType": {
                    "original": "number",
                    "resolved": "number",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "defines the version of the component"
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "version",
                "defaultValue": "2"
            }
        };
    }
    static get states() {
        return {
            "state": {},
            "animated": {}
        };
    }
    static get events() {
        return [{
                "method": "balChange",
                "name": "balChange",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Emitted when the accordion has opened or closed"
                },
                "complexType": {
                    "original": "BalEvents.BalAccordionChangeDetail",
                    "resolved": "boolean",
                    "references": {
                        "BalEvents": {
                            "location": "global",
                            "id": "global::BalEvents"
                        }
                    }
                }
            }, {
                "method": "balWillAnimate",
                "name": "balWillAnimate",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Emitted before the animation starts"
                },
                "complexType": {
                    "original": "BalEvents.BalAccordionWillAnimateDetail",
                    "resolved": "boolean",
                    "references": {
                        "BalEvents": {
                            "location": "global",
                            "id": "global::BalEvents"
                        }
                    }
                }
            }, {
                "method": "balDidAnimate",
                "name": "balDidAnimate",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Emitted after the animation has finished"
                },
                "complexType": {
                    "original": "BalEvents.BalAccordionDidAnimateDetail",
                    "resolved": "boolean",
                    "references": {
                        "BalEvents": {
                            "location": "global",
                            "id": "global::BalEvents"
                        }
                    }
                }
            }];
    }
    static get methods() {
        return {
            "configChanged": {
                "complexType": {
                    "signature": "(state: BalConfigState) => Promise<void>",
                    "parameters": [{
                            "name": "state",
                            "type": "BalConfigState",
                            "docs": ""
                        }],
                    "references": {
                        "Promise": {
                            "location": "global",
                            "id": "global::Promise"
                        },
                        "BalConfigState": {
                            "location": "import",
                            "path": "../../utils/config",
                            "id": "src/utils/config/index.ts::BalConfigState"
                        }
                    },
                    "return": "Promise<void>"
                },
                "docs": {
                    "text": "",
                    "tags": [{
                            "name": "internal",
                            "text": "define config for the component"
                        }]
                }
            },
            "present": {
                "complexType": {
                    "signature": "() => Promise<boolean>",
                    "parameters": [],
                    "references": {
                        "Promise": {
                            "location": "global",
                            "id": "global::Promise"
                        }
                    },
                    "return": "Promise<boolean>"
                },
                "docs": {
                    "text": "Opens the accordion",
                    "tags": []
                }
            },
            "dismiss": {
                "complexType": {
                    "signature": "() => Promise<boolean>",
                    "parameters": [],
                    "references": {
                        "Promise": {
                            "location": "global",
                            "id": "global::Promise"
                        }
                    },
                    "return": "Promise<boolean>"
                },
                "docs": {
                    "text": "Closes the accordion",
                    "tags": []
                }
            },
            "toggle": {
                "complexType": {
                    "signature": "() => Promise<boolean>",
                    "parameters": [],
                    "references": {
                        "Promise": {
                            "location": "global",
                            "id": "global::Promise"
                        }
                    },
                    "return": "Promise<boolean>"
                },
                "docs": {
                    "text": "Triggers the accordion",
                    "tags": []
                }
            },
            "humanToggle": {
                "complexType": {
                    "signature": "() => Promise<boolean>",
                    "parameters": [],
                    "references": {
                        "Promise": {
                            "location": "global",
                            "id": "global::Promise"
                        }
                    },
                    "return": "Promise<boolean>"
                },
                "docs": {
                    "text": "",
                    "tags": [{
                            "name": "internal",
                            "text": undefined
                        }]
                }
            }
        };
    }
    static get elementRef() { return "el"; }
    static get watchers() {
        return [{
                "propName": "active",
                "methodName": "activeChanged"
            }, {
                "propName": "debounce",
                "methodName": "debounceChanged"
            }];
    }
}
__decorate([
    Logger('bal-accordion'),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Function]),
    __metadata("design:returntype", void 0)
], Accordion.prototype, "createLogger", null);
__decorate([
    ListenToConfig(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], Accordion.prototype, "configChanged", null);
let accordionIds = 0;
