/**
 * @license
 * Copyright 2023 Tyler Technologies, Inc. 
 * License: Apache-2.0
 */
import { __decorate } from "tslib";
import { coerceBoolean, coerceNumber, CustomElement, emitEvent, ensureChild, toggleClass } from '@tylertech/forge-core';
import { BaseComponent } from '../core/base/base-component';
import { ForgeRipple } from '../ripple';
import { createUserInteractionListener } from '../core/utils';
import { ICON_BUTTON_CONSTANTS } from './icon-button-constants';
/**
 * The custom element class behind the `<forge-icon-button>` element.
 *
 * @tag forge-icon-button
 */
let IconButtonComponent = class IconButtonComponent extends BaseComponent {
    constructor() {
        super();
        this._toggle = false;
        this._isOn = false;
        this._dense = false;
        this._densityLevel = 5;
    }
    static get observedAttributes() {
        return [
            ICON_BUTTON_CONSTANTS.attributes.IS_ON,
            ICON_BUTTON_CONSTANTS.attributes.DENSE,
            ICON_BUTTON_CONSTANTS.attributes.DENSITY_LEVEL,
            ICON_BUTTON_CONSTANTS.attributes.TOGGLE
        ];
    }
    connectedCallback() {
        if (this.querySelector(ICON_BUTTON_CONSTANTS.selectors.BUTTON)) {
            this._initialize();
        }
        else {
            ensureChild(this, ICON_BUTTON_CONSTANTS.selectors.BUTTON).then(() => this._initialize());
        }
    }
    disconnectedCallback() {
        if (typeof this._destroyUserInteractionListener === 'function') {
            this._destroyUserInteractionListener();
            this._destroyUserInteractionListener = undefined;
        }
        if (this._buttonAttrMutationObserver) {
            this._buttonAttrMutationObserver.disconnect();
        }
        if (this._rippleInstance) {
            this._rippleInstance.destroy();
        }
    }
    attributeChangedCallback(name, oldValue, newValue) {
        switch (name) {
            case ICON_BUTTON_CONSTANTS.attributes.IS_ON:
                this.isOn = coerceBoolean(newValue);
                break;
            case ICON_BUTTON_CONSTANTS.attributes.DENSE:
                this.dense = coerceBoolean(newValue);
                break;
            case ICON_BUTTON_CONSTANTS.attributes.DENSITY_LEVEL:
                this.densityLevel = coerceNumber(newValue);
                break;
            case ICON_BUTTON_CONSTANTS.attributes.TOGGLE:
                this.toggle = coerceBoolean(newValue);
                break;
        }
    }
    /** Gets/sets whether the button is togglable. */
    get toggle() {
        return this._toggle;
    }
    set toggle(value) {
        this._toggle = value;
        if (this._toggle) {
            this._initializeToggle();
        }
        else {
            this._destroyToggle();
        }
    }
    /** Gets/sets the toggled state of the icon button. Only applies when `toggle = true`. */
    get isOn() {
        return this._isOn;
    }
    set isOn(value) {
        if (this._isOn !== value) {
            this._isOn = value;
            this._applyToggle();
        }
    }
    /** Gets/sets whether the icon button is dense. */
    get dense() {
        return this._dense;
    }
    set dense(value) {
        if (this._dense !== value) {
            this._dense = value;
            this._applyDensity();
        }
    }
    /** Controls the density level. 1 (least dense) to 6 (most dense). */
    get densityLevel() {
        return this._densityLevel;
    }
    set densityLevel(value) {
        if (this._densityLevel !== value) {
            this._densityLevel = value;
            if (this._densityLevel <= 0) {
                this._densityLevel = 1;
            }
            else if (this._densityLevel > 6) {
                this._densityLevel = 6;
            }
            else if (typeof this._densityLevel !== 'number') {
                this._densityLevel = 5;
            }
            this._applyDensity();
        }
    }
    _initialize() {
        this._buttonElement = this.querySelector(ICON_BUTTON_CONSTANTS.selectors.BUTTON);
        if (!this._buttonElement) {
            return;
        }
        this._buttonElement.classList.add(ICON_BUTTON_CONSTANTS.classes.BUTTON);
        this._applyToggle();
        this._applyDensity();
        this._toggleHandler = () => {
            this._toggleValue();
            emitEvent(this, ICON_BUTTON_CONSTANTS.events.CHANGE, this._isOn, true);
        };
        // Watch attributes on the `<button>` element
        this._buttonAttrMutationObserver = new MutationObserver(mutationList => {
            var _a;
            if (mutationList.some(mutation => mutation.attributeName === 'disabled')) {
                if (this._buttonElement.hasAttribute('disabled')) {
                    (_a = this._rippleInstance) === null || _a === void 0 ? void 0 : _a.handleBlur();
                }
            }
        });
        this._buttonAttrMutationObserver.observe(this._buttonElement, { attributes: true, attributeFilter: ['disabled'] });
        if (this._toggle) {
            this._initializeToggle();
        }
        // We wait to initialize the ripple instance until the user interacts with the component to avoid unnecessary performance overhead
        this._deferRippleInitialization();
    }
    async _deferRippleInitialization() {
        if (typeof this._destroyUserInteractionListener === 'function') {
            this._destroyUserInteractionListener();
        }
        const { userInteraction, destroy } = createUserInteractionListener(this._buttonElement);
        this._destroyUserInteractionListener = destroy;
        const { type } = await userInteraction;
        this._destroyUserInteractionListener = undefined;
        if (!this.isConnected) {
            return;
        }
        this._rippleInstance = this._createRipple();
        if (type === 'focusin') {
            this._rippleInstance.handleFocus();
        }
    }
    _createRipple() {
        if (this._rippleInstance) {
            this._rippleInstance.destroy();
        }
        const ripple = new ForgeRipple(this._buttonElement);
        ripple.unbounded = true;
        return ripple;
    }
    _toggleValue() {
        this._isOn = !this._isOn;
        this._applyToggle();
    }
    _applyToggle() {
        if (!this._buttonElement) {
            return;
        }
        toggleClass(this._buttonElement, this._isOn, ICON_BUTTON_CONSTANTS.classes.BUTTON_ON);
        if (this._toggle) {
            this._buttonElement.setAttribute('aria-pressed', `${this._isOn}`);
        }
    }
    _applyDensity() {
        if (!this._buttonElement) {
            return;
        }
        // Remove all other density classes first
        ICON_BUTTON_CONSTANTS.classes.DENSITY.forEach(c => this._buttonElement.classList.remove(c));
        if (this._dense) {
            this.setAttribute(ICON_BUTTON_CONSTANTS.attributes.DENSE, '');
            this._buttonElement.classList.add(ICON_BUTTON_CONSTANTS.classes.BUTTON_DENSE);
            // 5 is the default density level (we apply 5 implicitly in the regular dense class)
            // Exclude 5 since its already covered by dense class
            if (this._densityLevel < 7 && this._densityLevel > 0 && this.densityLevel !== 5) {
                const densityLevelClass = ICON_BUTTON_CONSTANTS.classes.DENSITY[this._densityLevel - 1];
                this._buttonElement.classList.add(densityLevelClass);
                this.setAttribute(ICON_BUTTON_CONSTANTS.attributes.DENSITY_LEVEL, this._densityLevel.toString());
            }
        }
        else {
            this.removeAttribute(ICON_BUTTON_CONSTANTS.attributes.DENSE);
            this._buttonElement.classList.remove(ICON_BUTTON_CONSTANTS.classes.BUTTON_DENSE);
        }
        // re-layout the ripple for cases where dense was changed after initial layout
        if (this._rippleInstance) {
            this._rippleInstance.layout();
        }
    }
    _initializeToggle() {
        if (!this._buttonElement) {
            return;
        }
        const icons = Array.from(this._buttonElement.querySelectorAll(ICON_BUTTON_CONSTANTS.selectors.ICON));
        // We require two icon/image elements to be specified for the "on" and "off" states
        if (icons.length !== 2) {
            console.error('You must specify two icons, one for "on" and one for "off".');
            return;
        }
        // Add the icon class to each icon
        icons.forEach(icon => icon.classList.add(ICON_BUTTON_CONSTANTS.classes.ICON));
        // If there are no icons that specify the "on" class, then automatically choose the first icon as the "on" icon and add the class,
        // alternatively we check for the existence of a `forge-icon-button-on` attribute on any of the icons and use that.
        if (!icons.some(icon => icon.classList.contains(ICON_BUTTON_CONSTANTS.classes.ICON_ON))) {
            const requestedOnIcon = icons.find(icon => icon.hasAttribute(ICON_BUTTON_CONSTANTS.attributes.ICON_ON));
            if (requestedOnIcon) {
                requestedOnIcon.classList.add(ICON_BUTTON_CONSTANTS.classes.ICON_ON);
            }
            else {
                icons[0].classList.add(ICON_BUTTON_CONSTANTS.classes.ICON_ON);
            }
        }
        this._buttonElement.addEventListener('click', this._toggleHandler);
        // Wait a frame to ensure the value of the `on` property has been set
        window.requestAnimationFrame(() => {
            if (this._isOn) {
                this._buttonElement.classList.add(ICON_BUTTON_CONSTANTS.classes.BUTTON_ON);
                this._buttonElement.setAttribute('aria-pressed', `${this._isOn}`);
            }
        });
    }
    _destroyToggle() {
        if (!this._buttonElement) {
            return;
        }
        this._buttonElement.removeEventListener('click', this._toggleHandler);
    }
    layout() {
        if (this._rippleInstance) {
            this._rippleInstance.layout();
        }
    }
};
IconButtonComponent = __decorate([
    CustomElement({
        name: ICON_BUTTON_CONSTANTS.elementName
    })
], IconButtonComponent);
export { IconButtonComponent };
