/**
 * Copyright Aquera Inc 2023
 *
 * This source code is licensed under the BSD-3-Clause license found in the
 * LICENSE file in the root directory of this source tree.
 */
import { __decorate } from "tslib";
import { styles } from './nile-dialog.css';
import '../nile-icon-button/nile-icon-button';
import { animateTo, stopAnimations } from '../internal/animate';
import { classMap } from 'lit/directives/class-map.js';
import { customElement, property, query } from 'lit/decorators.js';
import { getAnimation, setDefaultAnimation } from '../utilities/animation-registry';
import { HasSlotController } from '../internal/slot';
import { html, nothing } from 'lit';
import { ifDefined } from 'lit/directives/if-defined.js';
import { lockBodyScrolling, unlockBodyScrolling } from '../internal/scroll';
import { waitForEvent } from '../internal/event';
import { watch } from '../internal/watch';
import Modal from '../internal/modal';
import NileElement from '../internal/nile-element';
/**
 * Nile icon component.
 *
 * @tag nile-modal
 *
 */
let NileDialog = class NileDialog extends NileElement {
    constructor() {
        super(...arguments);
        this.hasSlotController = new HasSlotController(this, 'footer', 'sublabel', 'label');
        /**
         * Indicates whether or not the dialog is open. You can toggle this attribute to show and hide the dialog, or you can
         * use the `show()` and `hide()` methods and this attribute will reflect the dialog's open state.
         */
        this.open = false;
        /**
         * The dialog's label as displayed in the header. You should always include a relevant label even when using
         * `no-header`, as it is required for proper accessibility. If you need to display HTML, use the `label` slot instead.
         */
        this.label = '';
        this.sublabel = '';
        /**
         * Disables the header. This will also remove the default close button, so please ensure you provide an easy,
         * accessible way for users to dismiss the dialog.
         */
        this.noHeader = false;
        this.preventOverlayClose = false;
        /**
         *  Added to resolve conflicts between Angular Material's mat-drawer components.
         */
        this.drawerStyle = `
      <style id="drawer-style">
        .mat-drawer.mat-drawer-side { z-index: 1; }
      </style>
  `;
    }
    connectedCallback() {
        super.connectedCallback();
        this.handleDocumentKeyDown = this.handleDocumentKeyDown.bind(this);
        this.modal = new Modal(this);
    }
    firstUpdated() {
        this.dialog.hidden = !this.open;
        if (this.open) {
            this.addOpenListeners();
            this.modal.activate();
            lockBodyScrolling(this);
            document.head.insertAdjacentHTML('beforeend', this.drawerStyle);
        }
    }
    disconnectedCallback() {
        super.disconnectedCallback();
        unlockBodyScrolling(this);
    }
    requestClose(source) {
        const nileRequestClose = this.emit('nile-request-close', {
            cancelable: true,
            detail: { source },
        });
        if (source === 'close-button') {
            this.hide();
            return;
        }
        if (nileRequestClose.defaultPrevented || this.preventOverlayClose) {
            const animation = getAnimation(this, 'dialog.denyClose', {
                dir: 'right',
            });
            animateTo(this.panel, animation.keyframes, animation.options);
            return;
        }
        this.hide();
    }
    addOpenListeners() {
        document.addEventListener('keydown', this.handleDocumentKeyDown);
    }
    removeOpenListeners() {
        document.removeEventListener('keydown', this.handleDocumentKeyDown);
    }
    handleDocumentKeyDown(event) {
        if (this.open && event.key === 'Escape') {
            event.stopPropagation();
            this.requestClose('keyboard');
        }
    }
    async handleOpenChange() {
        if (this.open) {
            this.emit('nile-show');
            this.addOpenListeners();
            this.originalTrigger = document.activeElement;
            this.modal.activate();
            document.head.insertAdjacentHTML('beforeend', this.drawerStyle);
            lockBodyScrolling(this);
            const autoFocusTarget = this.querySelector('[autofocus]');
            if (autoFocusTarget) {
                autoFocusTarget.removeAttribute('autofocus');
            }
            await Promise.all([
                stopAnimations(this.dialog),
                stopAnimations(this.overlay),
            ]);
            this.dialog.hidden = false;
            // Set initial focus
            requestAnimationFrame(() => {
                const nileInitialFocus = this.emit('nile-initial-focus', {
                    cancelable: true,
                });
                if (!nileInitialFocus.defaultPrevented) {
                    // Set focus to the autofocus target and restore the attribute
                    if (autoFocusTarget) {
                        autoFocusTarget.focus({
                            preventScroll: true,
                        });
                    }
                    else {
                        this.panel.focus({ preventScroll: true });
                    }
                }
                // Restore the autofocus attribute
                if (autoFocusTarget) {
                    autoFocusTarget.setAttribute('autofocus', '');
                }
            });
            const panelAnimation = getAnimation(this, 'dialog.show', {
                dir: 'right',
            });
            const overlayAnimation = getAnimation(this, 'dialog.overlay.show', {
                dir: 'right',
            });
            await Promise.all([
                animateTo(this.panel, panelAnimation.keyframes, panelAnimation.options),
                animateTo(this.overlay, overlayAnimation.keyframes, overlayAnimation.options),
            ]);
            this.emit('nile-after-show');
        }
        else {
            // Hide
            this.emit('nile-hide');
            this.removeOpenListeners();
            this.modal.deactivate();
            await Promise.all([
                stopAnimations(this.dialog),
                stopAnimations(this.overlay),
            ]);
            const panelAnimation = getAnimation(this, 'dialog.hide', {
                dir: 'right',
            });
            const overlayAnimation = getAnimation(this, 'dialog.overlay.hide', {
                dir: 'right',
            });
            const styleTag = document.getElementById('drawer-style');
            if (styleTag) {
                document.head.removeChild(styleTag);
            }
            // Animate the overlay and the panel at the same time. Because animation durations might be different, we need to
            // hide each one individually when the animation finishes, otherwise the first one that finishes will reappear
            // unexpectedly. We'll unhide them after all animations have completed.
            await Promise.all([
                animateTo(this.overlay, overlayAnimation.keyframes, overlayAnimation.options).then(() => {
                    this.overlay.hidden = true;
                }),
                animateTo(this.panel, panelAnimation.keyframes, panelAnimation.options).then(() => {
                    this.panel.hidden = true;
                }),
            ]);
            this.dialog.hidden = true;
            // Now that the dialog is hidden, restore the overlay and panel for next time
            this.overlay.hidden = false;
            this.panel.hidden = false;
            unlockBodyScrolling(this);
            // Restore focus to the original trigger
            const trigger = this.originalTrigger;
            if (typeof trigger?.focus === 'function') {
                setTimeout(() => trigger.focus());
            }
            this.emit('nile-after-hide');
        }
    }
    /** Shows the dialog. */
    async show() {
        if (this.open) {
            return undefined;
        }
        this.open = true;
        return waitForEvent(this, 'nile-after-show');
    }
    /** Hides the dialog */
    async hide() {
        if (!this.open) {
            return undefined;
        }
        this.open = false;
        return waitForEvent(this, 'nile-after-hide');
    }
    render() {
        return html `
      <div
        part="base"
        class=${classMap({
            dialog: true,
            'dialog--open': this.open,
            'dialog--has-footer': this.hasSlotController.test('footer'),
        })}
      >
        <div
          part="overlay"
          class="dialog__overlay"
          @click=${() => this.requestClose('overlay')}
          tabindex="-1"
        ></div>

        <div
          part="panel"
          class="dialog__panel"
          role="dialog"
          aria-modal="true"
          aria-hidden=${this.open ? 'false' : 'true'}
          aria-label=${ifDefined(this.noHeader ? this.label : undefined)}
          aria-labelledby=${ifDefined(!this.noHeader ? 'title' : undefined)}
          tabindex="0"
        >
          ${!this.noHeader
            ? html `
                <header part="header" class="dialog__header">
                  <h2 part="title" class="dialog__header__text" id="title">
                    <slot name="label">
                      ${this.label?.length
                ? this.label
                : nothing}
                    </slot>
                    ${this.sublabel || this.hasSlotController.test('sublabel') ? html `
                      <slot name="sublabel">
                        ${this.sublabel?.length
                ? this.sublabel
                : nothing}
                      </slot>
                      ` : ''}
                  </h2>
                  <div part="header-actions" class="dialog__header-actions">
                    <slot name="header-actions"></slot>
                    <nile-icon
                      part="close-button"
                      exportparts="base:close-button__base"
                      class="dialog__close"
                      name="var(--nile-icon-close, var(--ng-icon-x-close))"
                      method="var(--nile-svg-method-fill, var(--ng-svg-method-stroke))"
                      label="close"
                      color="var(--nile-dialog-remove-icon-color, var(--ng-colors-fg-quaternary-400))"
                      @click="${() => this.requestClose('close-button')}"
                    ></nile-icon>
                  </div>
                </header>
              `
            : ''}

          <slot
            part="body"
            class="dialog__body  ${this.noHeader
            ? 'dialog__body-noheader'
            : ''}"
          ></slot>

          <footer part="footer" class="dialog__footer">
            <slot name="footer"></slot>
          </footer>
        </div>
      </div>
    `;
    }
};
NileDialog.styles = styles;
__decorate([
    query('.dialog')
], NileDialog.prototype, "dialog", void 0);
__decorate([
    query('.dialog__panel')
], NileDialog.prototype, "panel", void 0);
__decorate([
    query('.dialog__overlay')
], NileDialog.prototype, "overlay", void 0);
__decorate([
    property({ type: Boolean, reflect: true })
], NileDialog.prototype, "open", void 0);
__decorate([
    property({ reflect: true })
], NileDialog.prototype, "label", void 0);
__decorate([
    property({ reflect: true })
], NileDialog.prototype, "sublabel", void 0);
__decorate([
    property({ type: Boolean, reflect: true })
], NileDialog.prototype, "noHeader", void 0);
__decorate([
    property({ type: Boolean, reflect: true })
], NileDialog.prototype, "preventOverlayClose", void 0);
__decorate([
    watch('open', { waitUntilFirstUpdate: true })
], NileDialog.prototype, "handleOpenChange", null);
NileDialog = __decorate([
    customElement('nile-dialog')
], NileDialog);
export { NileDialog };
setDefaultAnimation('dialog.show', {
    keyframes: [
        { opacity: 0, scale: 0.8 },
        { opacity: 1, scale: 1 }
    ],
    options: { duration: 250, easing: 'ease' }
});
setDefaultAnimation('dialog.hide', {
    keyframes: [
        { opacity: 1, scale: 1 },
        { opacity: 0, scale: 0.8 }
    ],
    options: { duration: 250, easing: 'ease' }
});
setDefaultAnimation('dialog.denyClose', {
    keyframes: [{ scale: 1 }, { scale: 1.02 }, { scale: 1 }],
    options: { duration: 250 }
});
setDefaultAnimation('dialog.overlay.show', {
    keyframes: [{ opacity: 0 }, { opacity: 1 }],
    options: { duration: 250 }
});
setDefaultAnimation('dialog.overlay.hide', {
    keyframes: [{ opacity: 1 }, { opacity: 0 }],
    options: { duration: 250 }
});
export default NileDialog;
//# sourceMappingURL=nile-dialog.js.map