/*!
 * Custom elements for Nanopore-Digital Web applications
 */
import { r as registerInstance, c as createEvent, g as getElement, a as Host } from './index-BM3Om9WE.js';
import { M as Modal } from './modal-CY_3_LJ7.js';
import { l as lockBodyScrolling, u as unlockBodyScrolling } from './scroll-1nFw8CNk.js';
import { h, t as transformTag } from './renderer-DpNDfhNy.js';
import { b as blurActiveElement } from './active-element-CKJIkX0R.js';
import { H as HasSlotController } from './slot-Crs4khNK.js';
import './tabbable-BgopY-vd.js';
import './dom-dlicJTEJ.js';

const alertCss = ":host,\n*,\n*::before,\n*::after {\n  box-sizing: border-box;\n}\n[hidden] {\n  display: none !important;\n}\n@media (prefers-reduced-motion: reduce) {\n    :host,\n    *,\n    *::before,\n    *::after {\n      animation-duration: 0.01ms !important;\n      animation-iteration-count: 1 !important;\n      transition-duration: 0.01ms !important;\n      scroll-behavior: auto !important;\n    }\n  }:host,\n*,\n*::before,\n*::after {\n  box-sizing: border-box;\n}\n[hidden] {\n  display: none !important;\n}\n@media (prefers-reduced-motion: reduce) {\n    :host,\n    *,\n    *::before,\n    *::after {\n      animation-duration: 0.01ms !important;\n      animation-iteration-count: 1 !important;\n      transition-duration: 0.01ms !important;\n      scroll-behavior: auto !important;\n    }\n  }/** Typography */\n:host {\n  /**\n   * @prop --border-radius: Defaults to var(--nano-layer-border-radius);\n   * @prop --border-color: Defaults to var(--nano-layer-border-color);\n   * @prop --border-width: Defaults to var(--nano-layer-border-width);\n   * @prop --background: Defaults to var(--nano-layer-background-color);\n   * @prop --tint-color: The highlight colour (border-start, icon and heading text). Defaults to var(--nano-color-primary-1100);\n   * @prop --icon-size: Defaults to var(--nano-pictogram-size-large);\n   * @prop --scrim-color: overlay colour of alert display. Defaults to var(--nano-overlay-background-color);\n   * @prop --close-button-color: Defaults to var(--nano-color-neutral-400);\n   */\n  --border-radius: var(--nano-layer-border-radius);\n  --border-color: var(--nano-layer-border-color);\n  --border-width: var(--nano-layer-border-width);\n  --icon-size: var(--nano-pictogram-size-large);\n  --scrim-color: var(--nano-overlay-background-color);\n  --close-button-color: var(--nano-color-neutral-800);\n  --tint-color: var(--nano-color-primary-1100);\n  --background: var(--nano-color-primary-50);\n  display: block;\n  container-type: inline-size;\n}\n\n:host([color=neutral]), :host([theme=neutral]) {\n  --tint-color: var(--nano-color-neutral-1200);\n  --background: var(--nano-color-neutral-75);\n}\n\n:host([color=success]), :host([theme=success]) {\n  --tint-color: var(--nano-color-success-1100);\n  --background: var(--nano-color-success-50);\n}\n\n:host([color=warning]), :host([theme=warning]) {\n  --tint-color: var(--nano-color-warning-1100);\n  --background: var(--nano-color-warning-75);\n}\n\n:host([color=danger]), :host([theme=danger]) {\n  --tint-color: var(--nano-color-danger-1100);\n  --background: var(--nano-color-danger-100);\n}\n\n.alert {\n  position: relative;\n  background-color: var(--background);\n  border: solid var(--border-width) var(--border-color);\n  border-inline-start: solid 4px var(--tint-color);\n  border-radius: var(--border-radius);\n  opacity: 0;\n  transform: scale(0.9);\n  transition: var(--nano-transition-medium) opacity ease, var(--nano-transition-fast) transform ease;\n}\n.alert:focus {\n  outline: none;\n}\n.alert:not(.alert--showing) {\n  position: absolute;\n  inline-size: 1px;\n  block-size: 1px;\n  clip-path: inset(50%);\n  overflow: hidden;\n  pointer-events: none;\n  visibility: hidden;\n}\n.alert--open {\n  opacity: 1;\n  transform: none;\n}\n.alert--toasty, .alert--modal {\n  box-shadow: var(--nano-shadow-l2);\n  margin: var(--nano-spacing-md);\n}\n.alert--modal.alert--nodismiss {\n  animation: cannotClose 0.25s ease-in-out 1;\n}\n@keyframes cannotClose {\n  0% {\n    transform: scale(1);\n  }\n  50% {\n    transform: scale(1.08);\n  }\n  100% {\n    transform: scale(1);\n  }\n}\n\n.alert__main {\n  display: flex;\n  align-items: stretch;\n}\n\n.alert__content {\n  font-size: var(--nano-font-size-sm);\n  line-height: var(--nano-line-height-normal);\n  letter-spacing: var(--nano-letter-spacing-loose);\n  flex: 1 1 auto;\n  padding: var(--nano-spacing-md);\n  overflow: hidden;\n  display: flex;\n  flex-direction: column;\n  gap: var(--nano-spacing-sm);\n}\n\n.alert__modal-wrap {\n  position: fixed;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  inset: 0;\n  z-index: var(--nano-z-index-alert);\n}\n\n.alert__heading {\n  font-size: var(--nano-font-size-sm);\n  line-height: var(--nano-line-height-normal);\n  font-weight: var(--nano-font-weight-bold);\n  color: var(--tint-color);\n}\n\n.alert__title {\n  font-weight: var(--nano-font-weight-normal);\n  font-size: var(--nano-font-size-md);\n  line-height: var(--nano-line-height-dense);\n  text-wrap: balance;\n}\n\n.alert__close {\n  --color: var(--close-button-color);\n  flex: 0 0 auto;\n  display: flex;\n  align-items: center;\n  font-size: 1.1em;\n  padding-inline: 0 var(--nano-spacing-md);\n  padding-block: 0;\n}\n\n.alert__overlay {\n  position: fixed;\n  inset: 0;\n  background-color: var(--scrim-color);\n  opacity: 0;\n  transition: var(--nano-transition-fast) opacity;\n  z-index: var(--nano-z-index-alert);\n  pointer-events: all;\n}\n.alert__overlay--open {\n  opacity: 1;\n}\n\n.alert__icon {\n  flex: 0 0 auto;\n  display: flex;\n  align-items: start;\n  font-size: var(--icon-size);\n  padding-block: var(--nano-spacing-md);\n}\n.alert__icon ::slotted(*), .alert__icon::slotted(*) {\n  color: var(--tint-color);\n  margin-inline-start: var(--nano-spacing-md);\n}\n@container (width < 280px) {\n  .alert__icon {\n    display: none !important;\n  }\n}\n\n.alert__countdown {\n  position: absolute;\n  inset-block-end: 0;\n  inset-inline-start: 0;\n  inline-size: 100%;\n  block-size: 4px;\n  background-color: var(--nano-layer-border-color);\n  display: flex;\n  justify-content: flex-end;\n}\n\n.alert__countdown .alert__countdown-elapsed {\n  block-size: 100%;\n  inline-size: 0;\n}\n\n.alert__timer {\n  clip-path: inset(50%);\n  block-size: 1px;\n  overflow: hidden;\n  position: absolute;\n  white-space: nowrap;\n  inline-size: 1px;\n}\n\n.alert__countdown-elapsed {\n  background-color: var(--tint-color);\n}";

let toastStackTr;
let toastStackTl;
let toastStackBl;
let toastStackBr;
if (globalThis['document']) {
    toastStackTr = Object.assign(document.createElement('div'), {
        className: 'nano-toast-stack nano-toast-stack--tr',
    });
    toastStackTl = Object.assign(document.createElement('div'), {
        className: 'nano-toast-stack nano-toast-stack--tl',
    });
    toastStackBl = Object.assign(document.createElement('div'), {
        className: 'nano-toast-stack nano-toast-stack--bl',
    });
    toastStackBr = Object.assign(document.createElement('div'), {
        className: 'nano-toast-stack nano-toast-stack--br',
    });
}
const Alert = class {
    constructor(hostRef) {
        registerInstance(this, hostRef);
        this.nanoShow = createEvent(this, "nanoShow", 7);
        this.nanoAfterShow = createEvent(this, "nanoAfterShow", 7);
        this.nanoHide = createEvent(this, "nanoHide", 7);
        this.nanoAfterHide = createEvent(this, "nanoAfterHide", 7);
    }
    panel;
    overlay;
    modal;
    originalTrigger;
    goingToHide = false;
    goingToShow = false;
    autoHideTimeout;
    remainingTimeInterval;
    countdownAnimation;
    countdownElement;
    slotCtrl = new HasSlotController(this, '[default]', 'heading', 'title', 'footer');
    isModal = false;
    isToast = false;
    isShowing = false;
    label;
    noDismiss = false;
    get host() { return getElement(this); }
    /** Indicates whether or not the alert is open. You can use this in lieu of the show/hide methods. */
    open = false;
    handleOpenChange() {
        if (this.open)
            this.show();
        else
            this.hide();
    }
    /** Set to true to make the alert closable. */
    closable = false;
    /**
     * The color to use from the application's color palette.
     * @deprecated Use `theme` instead.
     */
    color;
    /** The color to use from the application's color palette.  */
    theme;
    /**
     * The length of time, in milliseconds, the alert will show before closing itself. If the user interacts with the
     * alert before it closes (e.g. moves the mouse over it), the timer will restart.
     */
    duration = Infinity;
    remainingTime = this.duration;
    handleDurationChange() {
        this.restartAutoHide();
    }
    /**
     * Relocate the dialog to the root of the DOM.
     * Most useful in conjunction with `alert()` when parental elements
     * have css stacking constraints which don't allow for `position: fixed`
     */
    hoist = false;
    handleHoistChange() {
        if (!this.hoist || document.body.children[0] === this.host)
            return;
        document.body.prepend(this.host);
    }
    /**
     * Enables a countdown that indicates the remaining time the alert will be displayed.
     * Typically used to indicate the remaining time before a whole app refresh.
     */
    countdown = true;
    /** Emitted when the alert opens. Calling `event.preventDefault()` will prevent it from being opened. */
    nanoShow;
    /** Emitted after the alert opens and all transitions are complete. */
    nanoAfterShow;
    /** Emitted when the alert closes. Calling `event.preventDefault()` will prevent it from being closed. */
    nanoHide;
    /** Emitted after the alert closes and all transitions are complete. */
    nanoAfterHide;
    /**
     * Shows the alert.
     * @returns a promise that resolves after the alert is shown
     */
    async show() {
        if (this.goingToShow)
            return;
        const nanoShow = this.nanoShow.emit();
        if (nanoShow.defaultPrevented) {
            this.open = false;
            return false;
        }
        this.host.hidden = false;
        this.goingToShow = true;
        this.open = true;
        {
            requestAnimationFrame(() => {
                this.isShowing = true;
                this.goingToShow = false;
            });
        }
        if (this.duration < Infinity) {
            this.restartAutoHide();
        }
    }
    /**
     * Hides the alert
     * @returns a promise that resolves after the alert is hidden
     */
    async hide() {
        if (this.goingToHide) {
            return;
        }
        const nanoHide = this.nanoHide.emit();
        if (nanoHide.defaultPrevented) {
            this.open = true;
            return false;
        }
        this.goingToHide = true;
        this.open = false;
        blurActiveElement(this.host);
        requestAnimationFrame(() => (this.goingToHide = false));
        clearTimeout(this.autoHideTimeout);
        clearInterval(this.remainingTimeInterval);
    }
    /**
     * Displays the alert as a toast notification. This will move the alert out of its position in the DOM and, when
     * dismissed, it will be removed from the DOM completely. By storing a reference to the alert, you can reuse it by
     * calling this method again. The returned promise will resolve after the alert is hidden.
     *
     * @param position options are tr (top-right - default), tl (top-left), br (bottom-right), bl (bottom-left)
     * @returns a promise which will resolve after the alert has hidden
     */
    async toast(position = 'tr') {
        this.isToast = true;
        return new Promise((resolve) => {
            this.handleCountdownChange();
            let toastStack;
            switch (position) {
                case 'tl':
                    toastStack = toastStackTl;
                    break;
                case 'bl':
                    toastStack = toastStackBl;
                    break;
                case 'br':
                    toastStack = toastStackBr;
                    break;
                default:
                    toastStack = toastStackTr;
                    break;
            }
            if (!toastStack.isConnected) {
                document.body.appendChild(toastStack);
            }
            toastStack.appendChild(this.host);
            this.connectedCallback();
            this.show();
            const onClose = () => {
                this.host.remove();
                this.isToast = false;
                resolve();
                // Remove the toast stack from the DOM when there are no more alerts
                if (!toastStack.querySelector(transformTag('nano-alert'))) {
                    toastStack.remove();
                }
            };
            this.host.addEventListener('nanoAfterHide', onClose, { once: true });
            this.host.addEventListener('nano-after-hide', onClose, { once: true });
        });
    }
    /**
     * Displays the alert as a dialog / modal - more akin to a traditional js alert().
     * @param label a label for assistive technology
     */
    async alert(label) {
        this.isModal = true;
        this.label = label;
        return new Promise((resolve) => {
            let foundHoisted = false;
            for (const child of document.body.children) {
                if (child.tagName === 'NANO-ALERT' && child === this.host) {
                    foundHoisted = true;
                }
            }
            if (!foundHoisted) {
                document.body.append(this.host);
            }
            this.modal.activate();
            lockBodyScrolling(this.host);
            this.originalTrigger = document.activeElement;
            requestAnimationFrame(() => {
                this.show();
            });
            const onOpen = () => requestAnimationFrame(() => this.panel.focus({ preventScroll: true }));
            const onClose = () => {
                this.modal.deactivate();
                this.host.remove();
                this.label = undefined;
                this.isModal = false;
                resolve();
                // Restore focus to the original trigger
                if (this.originalTrigger &&
                    typeof this.originalTrigger.focus === 'function') {
                    setTimeout(() => this.originalTrigger.focus());
                }
            };
            this.host.addEventListener('nanoAfterShow', onOpen, { once: true });
            this.host.addEventListener('nano-after-show', onOpen, { once: true });
            this.host.addEventListener('nanoAfterHide', onClose, {
                once: true,
            });
            this.host.addEventListener('nano-after-hide', onClose, {
                once: true,
            });
        });
    }
    handleCountdownChange() {
        if (this.open && this.duration < Infinity && this.countdown) {
            const start = '100%';
            const end = '0';
            requestAnimationFrame(() => {
                if (!this.countdownElement)
                    return;
                this.countdownAnimation = this.countdownElement.animate([{ width: start }, { width: end }], {
                    duration: this.duration,
                    easing: 'linear',
                });
            });
        }
    }
    restartAutoHide() {
        this.handleCountdownChange();
        clearTimeout(this.autoHideTimeout);
        clearInterval(this.remainingTimeInterval);
        if (this.open && this.duration < Infinity) {
            this.autoHideTimeout = window.setTimeout(() => this.hide(), this.duration);
            this.remainingTime = this.duration;
            this.remainingTimeInterval = window.setInterval(() => {
                this.remainingTime -= 100;
            }, 100);
        }
    }
    handleMouseEnter = () => {
        this.countdownAnimation?.pause();
        clearTimeout(this.autoHideTimeout);
        clearInterval(this.remainingTimeInterval);
    };
    handleMouseLeave = () => {
        if (this.duration < Infinity) {
            this.autoHideTimeout = window.setTimeout(() => this.hide(), this.remainingTime);
            this.remainingTimeInterval = window.setInterval(() => {
                this.remainingTime -= 100;
            }, 100);
            this.countdownAnimation?.play();
        }
    };
    handleCloseClick = () => {
        this.hide();
    };
    handleTransitionEnd = (event) => {
        if (event.propertyName === 'opacity' &&
            (event.target === this.panel || event.target === this.overlay)) {
            this.host.hidden = !this.open;
            this.isShowing = this.open;
            if (this.open)
                this.nanoAfterShow.emit();
            else
                this.nanoAfterHide.emit();
        }
    };
    handleButtonClick = (e) => {
        if (e.defaultPrevented)
            return;
        if (e.target.tagName && e.target.tagName.toLowerCase() === 'button')
            this.hide();
    };
    connectedCallback() {
        this.modal = new Modal(this.host);
        this.handleHoistChange();
        this.host.addEventListener('click', this.handleButtonClick);
        this.isShowing = typeof this.host.getAttribute('showing') === 'string';
    }
    disconnectedCallback() {
        unlockBodyScrolling(this.host);
        this.host.removeEventListener('click', this.handleButtonClick);
    }
    componentDidLoad() {
        // Show on init if open
        if (this.open && !this.isShowing) {
            this.show();
        }
    }
    Panel = () => {
        return (h("div", { ref: (div) => (this.panel = div), part: "panel", class: {
                alert: true,
                'alert--open': this.open,
                'alert--toasty': this.isToast,
                'alert--modal': !!this.isModal,
                'alert--showing': this.isShowing,
                'alert--nodismiss': this.noDismiss,
            }, tabIndex: this.isModal ? 0 : undefined, role: this.isModal ? 'alertdialog' : 'alert', "aria-atomic": "true", "aria-hidden": this.isShowing ? 'false' : 'true', "aria-modal": this.isModal ? 'true' : undefined, "aria-label": this.label ? this.label : undefined, onMouseEnter: this.handleMouseEnter, onMouseLeave: this.handleMouseLeave, onTransitionEnd: this.handleTransitionEnd }, h("div", { class: "alert__main", part: "main" }, h("div", { part: "icon", class: "alert__icon" }, h("slot", { name: "icon" })), h("div", { part: "content", class: "alert__content", "aria-live": this.isModal ? 'assertive' : 'polite' }, this.slotCtrl.has('heading') && (h("div", { class: "alert__heading" }, h("slot", { name: "heading" }))), this.slotCtrl.has('title') && (h("div", { class: "alert__title" }, h("slot", { name: "title" }))), this.slotCtrl.has('[default]') && (h("div", { class: "alert__body" }, h("slot", null))), this.slotCtrl.has('footer') && (h("div", { part: "footer", class: "alert__footer" }, h("slot", { name: "footer" })))), this.closable && (h("div", { class: "alert__close", part: "close" }, h("nano-icon-button", { iconName: "light/xmark", label: "close menu", onClick: this.handleCloseClick })))), this.duration < Infinity && [
            h("div", { role: "timer", class: "alert__timer" }, this.remainingTime),
            this.countdown ? (h("div", { class: "alert__countdown" }, h("div", { ref: (e) => (this.countdownElement = e), class: "alert__countdown-elapsed" }))) : (''),
        ]));
    };
    render() {
        return (h(Host, { key: '026fe28d4b8aed79d75734f67de6192813be44a5', class: { 'nano-alert': true }, showing: this.isShowing }, this.isModal && [
            h("div", { key: 'ac596b1e2b216ee591e2f9b85adf86dbbe27fce1', part: "overlay", class: {
                    alert__overlay: true,
                    'alert__overlay--open': this.open,
                }, ref: (div) => (this.overlay = div) }),
            h("div", { key: '825f8cf0d80d9bfa7bff7710ef349c234c155d53', class: "alert__modal-wrap", onClick: (e) => {
                    if (!e.target.classList.contains('alert__modal-wrap'))
                        return;
                    this.noDismiss = true;
                    setTimeout(() => (this.noDismiss = false), 250);
                } }, h(this.Panel, { key: '7343702088055d21dc6d5252aa108c19c315a20a' })),
        ], !this.isModal && h(this.Panel, { key: 'c3cd08198bb5a87fcc38bea5130d5615608cb1ff' })));
    }
    static get watchers() { return {
        "open": ["handleOpenChange"],
        "duration": ["handleDurationChange"],
        "hoist": ["handleHoistChange"]
    }; }
};
Alert.style = alertCss;

export { Alert as nano_alert };
