"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
Object.defineProperty(exports, "NotificationUI", {
    enumerable: true,
    get: function() {
        return NotificationUI;
    }
});
const _element = require("../../helpers/dom/element");
const _string = require("../../helpers/string");
const _constants = require("./constants");
function _check_private_redeclaration(obj, privateCollection) {
    if (privateCollection.has(obj)) {
        throw new TypeError("Cannot initialize the same private elements twice on an object");
    }
}
function _class_apply_descriptor_get(receiver, descriptor) {
    if (descriptor.get) {
        return descriptor.get.call(receiver);
    }
    return descriptor.value;
}
function _class_apply_descriptor_set(receiver, descriptor, value) {
    if (descriptor.set) {
        descriptor.set.call(receiver, value);
    } else {
        if (!descriptor.writable) {
            throw new TypeError("attempted to set read only private field");
        }
        descriptor.value = value;
    }
}
function _class_extract_field_descriptor(receiver, privateMap, action) {
    if (!privateMap.has(receiver)) {
        throw new TypeError("attempted to " + action + " private field on non-instance");
    }
    return privateMap.get(receiver);
}
function _class_private_field_get(receiver, privateMap) {
    var descriptor = _class_extract_field_descriptor(receiver, privateMap, "get");
    return _class_apply_descriptor_get(receiver, descriptor);
}
function _class_private_field_init(obj, privateMap, value) {
    _check_private_redeclaration(obj, privateMap);
    privateMap.set(obj, value);
}
function _class_private_field_set(receiver, privateMap, value) {
    var descriptor = _class_extract_field_descriptor(receiver, privateMap, "set");
    _class_apply_descriptor_set(receiver, descriptor, value);
    return value;
}
var /**
   * @type {HTMLElement}
   */ _rootElement = /*#__PURE__*/ new WeakMap(), /**
   * @type {function(string, string): string | undefined}
   */ _sanitizer = /*#__PURE__*/ new WeakMap(), /**
   * @type {boolean}
   */ _isRtl = /*#__PURE__*/ new WeakMap(), /**
   * @type {HTMLElement | null}
   */ _host = /*#__PURE__*/ new WeakMap(), /**
   * @type {Map<string, HTMLElement>}
   */ _stacks = /*#__PURE__*/ new WeakMap();
class NotificationUI {
    /**
   * Creates the overlay host and four corner stack elements after `rootGridElement`.
   */ install() {
        if (_class_private_field_get(this, _host)) {
            return;
        }
        const doc = _class_private_field_get(this, _rootElement).ownerDocument;
        const host = doc.createElement('div');
        host.className = `${_constants.NOTIFICATION_CLASS_NAME} handsontable`;
        host.setAttribute('dir', _class_private_field_get(this, _isRtl) ? 'rtl' : 'ltr');
        _constants.NOTIFICATION_POSITIONS.forEach((position)=>{
            const stack = doc.createElement('div');
            stack.className = `${_constants.NOTIFICATION_CLASS_NAME}__stack ${_constants.NOTIFICATION_CLASS_NAME}__stack--${position}`;
            stack.dataset.htNotificationPosition = position;
            /* Scrollable overflow makes stacks tab-focusable in some browsers; keep them out of the tab order. */ stack.tabIndex = -1;
            host.appendChild(stack);
            _class_private_field_get(this, _stacks).set(position, stack);
        });
        _class_private_field_get(this, _rootElement).after(host);
        _class_private_field_set(this, _host, host);
    }
    /**
   * @returns {HTMLElement | null}
   */ getHost() {
        return _class_private_field_get(this, _host);
    }
    /**
   * @param {string} position Stack key.
   * @returns {HTMLElement | undefined}
   */ getStack(position) {
        return _class_private_field_get(this, _stacks).get(position);
    }
    /**
   * Updates RTL direction on the host.
   *
   * @param {boolean} isRtl Whether the grid uses RTL layout.
   */ setRtl(isRtl) {
        _class_private_field_set(this, _isRtl, isRtl);
        if (_class_private_field_get(this, _host)) {
            _class_private_field_get(this, _host).setAttribute('dir', isRtl ? 'rtl' : 'ltr');
        }
    }
    /**
   * Updates the HTML sanitizer used for string notification messages.
   *
   * @param {function(string, string): string | undefined} sanitizer Sanitizer from Handsontable settings, if any.
   */ setSanitizer(sanitizer) {
        _class_private_field_set(this, _sanitizer, sanitizer);
    }
    /**
   * Builds a toast element from normalized options.
   *
   * @param {object} options Normalized notification options (id, variant, title, message, closable, actions).
   * @param {string} closeLabel Translated label for the close control.
   * @param {boolean} animation Whether enter animation is enabled.
   * @returns {{ element: HTMLElement }}
   */ createToastElement(options, closeLabel, animation) {
        const doc = _class_private_field_get(this, _rootElement).ownerDocument;
        const element = doc.createElement('div');
        const isError = options.variant === 'error';
        element.className = `${_constants.NOTIFICATION_CLASS_NAME}__toast ${_constants.NOTIFICATION_CLASS_NAME}__toast--${options.variant}`;
        element.dataset.htNotificationId = options.id;
        element.setAttribute('role', isError ? 'alert' : 'status');
        element.setAttribute('aria-live', isError ? 'assertive' : 'polite');
        element.setAttribute('aria-atomic', 'true');
        if (animation) {
            element.classList.add(`${_constants.NOTIFICATION_CLASS_NAME}__toast--animate`);
            element.classList.add(options.position.startsWith('top') ? `${_constants.NOTIFICATION_CLASS_NAME}__toast--stack-top` : `${_constants.NOTIFICATION_CLASS_NAME}__toast--stack-bottom`);
        }
        const inner = doc.createElement('div');
        inner.className = `${_constants.NOTIFICATION_CLASS_NAME}__toast-inner`;
        const accent = doc.createElement('div');
        accent.className = `${_constants.NOTIFICATION_CLASS_NAME}__accent`;
        accent.setAttribute('aria-hidden', 'true');
        inner.appendChild(accent);
        const body = doc.createElement('div');
        body.className = `${_constants.NOTIFICATION_CLASS_NAME}__body`;
        if (options.title) {
            const titleEl = doc.createElement('div');
            titleEl.className = `${_constants.NOTIFICATION_CLASS_NAME}__title`;
            titleEl.textContent = (0, _string.stripTags)(options.title);
            body.appendChild(titleEl);
        }
        const messageEl = doc.createElement('div');
        messageEl.className = `${_constants.NOTIFICATION_CLASS_NAME}__message`;
        if (typeof options.message === 'string') {
            (0, _element.fastInnerHTML)(messageEl, options.message, _class_private_field_get(this, _sanitizer));
        } else {
            messageEl.appendChild(options.message);
        }
        body.appendChild(messageEl);
        if (options.actions?.length) {
            const actionsRow = doc.createElement('div');
            actionsRow.className = `${_constants.NOTIFICATION_CLASS_NAME}__actions`;
            options.actions.forEach((action, index)=>{
                const btn = doc.createElement('button');
                btn.type = 'button';
                btn.className = `ht-button ht-button--${action.type === 'primary' ? 'primary' : 'secondary'}`;
                btn.textContent = (0, _string.stripTags)(action.label);
                btn.dataset.htNotificationAction = String(index);
                actionsRow.appendChild(btn);
            });
            body.appendChild(actionsRow);
        }
        inner.appendChild(body);
        if (options.closable) {
            const closeBtn = doc.createElement('button');
            closeBtn.type = 'button';
            closeBtn.className = `${_constants.NOTIFICATION_CLASS_NAME}__close`;
            closeBtn.setAttribute('aria-label', closeLabel);
            inner.appendChild(closeBtn);
        }
        element.appendChild(inner);
        element.tabIndex = -1;
        inner.querySelectorAll('button').forEach((btn)=>{
            btn.tabIndex = -1;
        });
        const win = _class_private_field_get(this, _rootElement).ownerDocument.defaultView;
        if (animation) {
            /* Double rAF: first frame paints opacity/transform start; second adds --visible so transition runs. */ win.requestAnimationFrame(()=>{
                win.requestAnimationFrame(()=>{
                    element.classList.add(`${_constants.NOTIFICATION_CLASS_NAME}__toast--visible`);
                });
            });
        } else {
            element.classList.add(`${_constants.NOTIFICATION_CLASS_NAME}__toast--visible`);
        }
        return {
            element
        };
    }
    /**
   * @param {HTMLElement} toastEl Toast root element.
   * @returns {HTMLElement[]}
   */ static getFocusables(toastEl) {
        const buttons = Array.from(toastEl.querySelectorAll('button')).filter((btn)=>!btn.disabled);
        if (buttons.length > 0) {
            return buttons;
        }
        return [
            toastEl
        ];
    }
    /**
   * Enables or disables sequential keyboard focus for notification controls. When disabled, controls stay
   * out of the tab order so opening a toast does not move focus; screen readers still receive `aria-live` updates.
   *
   * @param {HTMLElement | null} host Notification plugin host element.
   * @param {boolean} enabled Whether controls participate in tab navigation.
   */ static setSequentialFocusWithinHost(host, enabled) {
        if (!host) {
            return;
        }
        const tabIndex = enabled ? 0 : -1;
        host.querySelectorAll(`.${_constants.NOTIFICATION_CLASS_NAME}__toast`).forEach((toast)=>{
            const buttons = Array.from(toast.querySelectorAll('button')).filter((btn)=>!btn.disabled);
            buttons.forEach((btn)=>{
                btn.tabIndex = tabIndex;
            });
            if (buttons.length === 0) {
                toast.tabIndex = tabIndex;
            } else {
                toast.tabIndex = -1;
            }
        });
    }
    /**
   * Detaches the host from the DOM and clears internal stack references.
   */ destroy() {
        _class_private_field_get(this, _host)?.remove();
        _class_private_field_set(this, _host, null);
        _class_private_field_get(this, _stacks).clear();
    }
    /**
   * @param {object} params Constructor parameters.
   * @param {HTMLElement} params.rootElement Handsontable root grid element.
   * @param {function(string, string): string | undefined} params.sanitizer Sanitizer for HTML strings.
   * @param {boolean} params.isRtl Whether the grid uses RTL layout.
   */ constructor({ rootElement, sanitizer, isRtl }){
        _class_private_field_init(this, _rootElement, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _sanitizer, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _isRtl, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _host, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _stacks, {
            writable: true,
            value: void 0
        });
        _class_private_field_set(this, _isRtl, false);
        _class_private_field_set(this, _host, null);
        _class_private_field_set(this, _stacks, new Map());
        _class_private_field_set(this, _rootElement, rootElement);
        _class_private_field_set(this, _sanitizer, sanitizer);
        _class_private_field_set(this, _isRtl, isRtl);
    }
}
