"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
Object.defineProperty(exports, "DialogUI", {
    enumerable: true,
    get: function() {
        return DialogUI;
    }
});
const _templateLiteralTag = require("../../helpers/templateLiteralTag");
const _errors = require("../../helpers/errors");
const _object = require("../../helpers/object");
const _localHooks = /*#__PURE__*/ _interop_require_default(require("../../mixins/localHooks"));
const _element = require("../../helpers/dom/element");
const _a11y = require("../../helpers/a11y");
const _templates = require("./templates");
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;
}
function _class_private_method_get(receiver, privateSet, fn) {
    if (!privateSet.has(receiver)) {
        throw new TypeError("attempted to get private field on non-instance");
    }
    return fn;
}
function _class_private_method_init(obj, privateSet) {
    _check_private_redeclaration(obj, privateSet);
    privateSet.add(obj);
}
function _interop_require_default(obj) {
    return obj && obj.__esModule ? obj : {
        default: obj
    };
}
const CONTAINER_TEMPLATE = `
<div data-ref="dialogElement" class="${_constants.DIALOG_CLASS_NAME}">
  <div data-ref="dialogWrapperElement" class="${_constants.DIALOG_CLASS_NAME}__content-wrapper">
</div>
`;
var /**
   * The root element where the dialog UI will be installed.
   *
   * @type {HTMLElement}
   */ _rootElement = /*#__PURE__*/ new WeakMap(), /**
   * The references to the UI elements.
   *
   * @type {object}
   */ _refs = /*#__PURE__*/ new WeakMap(), /**
   * Indicates if the UI is in RTL mode.
   *
   * @type {boolean}
   */ _isRtl = /*#__PURE__*/ new WeakMap(), /**
   * Indicates if the animation has started.
   *
   * @type {boolean}
   */ _animationStarted = /*#__PURE__*/ new WeakMap(), /**
   * The template to use for the dialog.
   *
   * @type {function(): string}
   */ _template = /*#__PURE__*/ new WeakMap(), /**
   * The callbacks of the template buttons to trigger when the button is clicked.
   *
   * @type {Array<function(MouseEvent)>}
   */ _templateButtonCallbacks = /*#__PURE__*/ new WeakMap(), /**
   * The sanitizer to use for the dialog.
   *
   * @type {function(string): string}
   */ _sanitizer = /*#__PURE__*/ new WeakMap(), /**
   * Handles the transition end event.
   */ _onTransitionEnd = /*#__PURE__*/ new WeakSet(), /**
   * Handles the transition start event. This is used to track if the animation has started.
   */ _onTransitionStart = /*#__PURE__*/ new WeakSet();
class DialogUI {
    /**
   * Uses the specified template for the dialog.
   *
   * @param {string} templateName The name of the template to use.
   * @param {object} templateVars The variables to use for the template.
   */ useTemplate(templateName, templateVars = {}) {
        if (!_templates.TEMPLATES.has(templateName) || templateName === 'base') {
            const validTemplates = Array.from(_templates.TEMPLATES.keys()).filter((template)=>template !== 'base').join(', ');
            (0, _errors.throwWithCause)((0, _templateLiteralTag.toSingleLine)`Invalid template: ${templateName}.\x20
        Valid templates are: ${validTemplates}.`);
        }
        _class_private_field_set(this, _template, _templates.TEMPLATES.get(templateName)(templateVars));
        _class_private_field_set(this, _templateButtonCallbacks, (templateVars.buttons ?? []).map((button)=>button.callback));
    }
    /**
   * Uses the default template for the dialog for the `content` option.
   */ useDefaultTemplate() {
        _class_private_field_set(this, _template, _templates.TEMPLATES.get('base')());
        _class_private_field_set(this, _templateButtonCallbacks, []);
    }
    /**
   * Creates the dialog UI elements and sets up the structure.
   */ install() {
        if (_class_private_field_get(this, _refs)?.dialogElement) {
            return;
        }
        const elements = (0, _templateLiteralTag.html)`${CONTAINER_TEMPLATE}`;
        _class_private_field_set(this, _refs, elements.refs);
        const { dialogElement } = _class_private_field_get(this, _refs);
        // Set ARIA attributes
        (0, _element.setAttribute)(dialogElement, [
            (0, _a11y.A11Y_MODAL)(),
            [
                'dir',
                _class_private_field_get(this, _isRtl) ? 'rtl' : 'ltr'
            ]
        ]);
        dialogElement.addEventListener('transitionstart', ()=>_class_private_method_get(this, _onTransitionStart, onTransitionStart).call(this));
        dialogElement.addEventListener('transitionend', ()=>_class_private_method_get(this, _onTransitionEnd, onTransitionEnd).call(this));
        // Append to Handsontable after table grid element
        _class_private_field_get(this, _rootElement).after(elements.fragment);
    }
    /**
   * Returns the dialog element.
   *
   * @returns {HTMLElement} The dialog element.
   */ getContainer() {
        return _class_private_field_get(this, _refs).dialogElement;
    }
    /**
   * Gets the focusable elements.
   *
   * @returns {HTMLElement[]} The focusable elements.
   */ getFocusableElements() {
        return _class_private_field_get(this, _template).focusableElements();
    }
    /**
   * Updates the dialog content and class name.
   *
   * @param {object} options - Class name update options.
   * @param {boolean} options.isVisible - Whether the dialog is visible.
   * @param {string|HTMLElement} options.content - The content to render in the dialog.
   * @param {string} options.customClassName - The custom class name to add to the dialog.
   * @param {string} options.background - The background to add to the dialog.
   * @param {boolean} options.contentBackground - Whether to show content background.
   * @param {boolean} options.animation - Whether to add the animation class to the dialog.
   * @param {object} options.a11y - The accessibility options for the dialog.
   *
   * @returns {DialogUI} The instance of the DialogUI.
   */ updateDialog({ isVisible, content, customClassName, background, contentBackground, animation, a11y }) {
        const elements = _class_private_field_get(this, _template).compile();
        const { dialogElement, dialogWrapperElement } = _class_private_field_get(this, _refs);
        dialogWrapperElement.innerHTML = '';
        dialogWrapperElement.appendChild(elements.fragment);
        Object.assign(_class_private_field_get(this, _refs), elements.refs);
        const { contentElement, buttonsContainer } = _class_private_field_get(this, _refs);
        if (_class_private_field_get(this, _template).TEMPLATE_NAME !== 'base') {
            Object.assign(a11y, _class_private_field_get(this, _template).dialogA11YOptions());
        }
        // Dialog class name
        const customClass = customClassName ? ` ${customClassName}` : '';
        const backgroundClass = background ? ` ${_constants.DIALOG_CLASS_NAME}--background-${background}` : '';
        const animationClass = animation ? ` ${_constants.DIALOG_CLASS_NAME}--animation` : '';
        const showClass = isVisible ? ` ${_constants.DIALOG_CLASS_NAME}--show` : '';
        // Update dialog class name
        dialogElement.className = [
            _constants.DIALOG_CLASS_NAME,
            `${_constants.DIALOG_CLASS_NAME}--${_class_private_field_get(this, _template).TEMPLATE_NAME}`,
            'handsontable',
            customClass,
            backgroundClass,
            animationClass,
            showClass
        ].join(' ');
        (0, _element.setAttribute)(dialogElement, [
            _class_private_field_get(this, _template).TEMPLATE_NAME === 'base' ? (0, _a11y.A11Y_TABINDEX)(-1) : undefined
        ]);
        // Dialog aria attributes
        (0, _element.setAttribute)(dialogElement, [
            a11y.role === 'alertdialog' ? (0, _a11y.A11Y_ALERTDIALOG)() : (0, _a11y.A11Y_DIALOG)()
        ]);
        if (a11y.ariaLabel && !a11y.ariaLabelledby) {
            (0, _element.setAttribute)(dialogElement, [
                a11y.ariaLabel ? (0, _a11y.A11Y_LABEL)(a11y.ariaLabel) : undefined
            ]);
        } else {
            (0, _element.removeAttribute)(dialogElement, 'aria-label');
        }
        if (a11y.ariaLabelledby) {
            (0, _element.setAttribute)(dialogElement, [
                (0, _a11y.A11Y_LABELED_BY)(a11y.ariaLabelledby)
            ]);
        } else {
            (0, _element.removeAttribute)(dialogElement, 'aria-labelledby');
        }
        if (a11y.ariaDescribedby) {
            (0, _element.setAttribute)(dialogElement, [
                (0, _a11y.A11Y_DESCRIBED_BY)(a11y.ariaDescribedby)
            ]);
        } else {
            (0, _element.removeAttribute)(dialogElement, 'aria-describedby');
        }
        // Dialog content class name
        const contentBackgroundClass = contentBackground ? ` ${_constants.DIALOG_CLASS_NAME}__content--background` : '';
        // Update content class name
        (0, _element.addClass)(contentElement, `${_constants.DIALOG_CLASS_NAME}__content${contentBackgroundClass}`);
        if (_class_private_field_get(this, _template).TEMPLATE_NAME === 'base') {
            // Clear existing dialog content
            contentElement.innerHTML = '';
            // Render new dialog content
            if (typeof content === 'string') {
                (0, _element.fastInnerHTML)(contentElement, content, _class_private_field_get(this, _sanitizer));
            } else if (content instanceof HTMLElement || content instanceof DocumentFragment) {
                contentElement.appendChild(content);
            }
        } else if (buttonsContainer) {
            Array.from(buttonsContainer.children).forEach((button, index)=>{
                const callback = _class_private_field_get(this, _templateButtonCallbacks)[index];
                if (callback) {
                    button.addEventListener('click', callback);
                }
            });
        }
        return this;
    }
    /**
   * Shows the dialog with optional animation.
   *
   * @param {boolean} animation - Whether to add the animation class to the dialog.
   * @returns {DialogUI} The instance of the DialogUI.
   */ showDialog(animation) {
        const { dialogElement } = _class_private_field_get(this, _refs);
        dialogElement.style.display = 'block';
        if (animation) {
            // Triggers style and layout recalculation, so the display: block is fully committed before adding
            // the class ht-dialog--show.
            // eslint-disable-next-line no-unused-expressions
            dialogElement.offsetHeight;
        }
        (0, _element.addClass)(dialogElement, `${_constants.DIALOG_CLASS_NAME}--show`);
        _class_private_field_set(this, _animationStarted, false);
        return this;
    }
    /**
   * Hides the dialog with optional animation.
   *
   * @param {boolean} animation - Whether to add the animation class to the dialog.
   * @returns {DialogUI} The instance of the DialogUI.
   */ hideDialog(animation) {
        const { dialogElement } = _class_private_field_get(this, _refs);
        (0, _element.removeClass)(dialogElement, `${_constants.DIALOG_CLASS_NAME}--show`);
        if (animation && !_class_private_field_get(this, _animationStarted) || !animation) {
            dialogElement.style.display = 'none';
        }
        _class_private_field_set(this, _animationStarted, false);
        return this;
    }
    /**
   * Focuses the dialog element.
   */ focusDialog() {
        _class_private_field_get(this, _refs).dialogElement.focus();
    }
    /**
   * Updates the width of the dialog container to the same size as the table.
   *
   * @param {number} width - The width of the table.
   * @returns {DialogUI} The instance of the DialogUI.
   */ updateWidth(width) {
        _class_private_field_get(this, _refs).dialogElement.style.width = `${width}px`;
        return this;
    }
    /**
   * Updates the height of the dialog container.
   *
   * @param {number} licenseInfoHeight - The height of the license info.
   * @returns {DialogUI} The instance of the DialogUI.
   */ updateHeight(licenseInfoHeight) {
        _class_private_field_get(this, _refs).dialogElement.style.height = `calc(100% - ${licenseInfoHeight}px)`;
        return this;
    }
    /**
   * Removes the dialog UI elements from the DOM and clears the refs.
   */ destroyDialog() {
        _class_private_field_get(this, _refs)?.dialogElement.remove();
        _class_private_field_set(this, _refs, null);
    }
    constructor({ rootElement, isRtl, sanitizer }){
        _class_private_method_init(this, _onTransitionEnd);
        _class_private_method_init(this, _onTransitionStart);
        _class_private_field_init(this, _rootElement, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _refs, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _isRtl, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _animationStarted, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _template, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _templateButtonCallbacks, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _sanitizer, {
            writable: true,
            value: void 0
        });
        _class_private_field_set(this, _isRtl, false);
        _class_private_field_set(this, _animationStarted, false);
        _class_private_field_set(this, _template, _templates.TEMPLATES.get('base'));
        _class_private_field_set(this, _templateButtonCallbacks, []);
        _class_private_field_set(this, _rootElement, rootElement);
        _class_private_field_set(this, _isRtl, isRtl);
        _class_private_field_set(this, _sanitizer, sanitizer);
        this.install();
    }
}
function onTransitionEnd() {
    const { dialogElement } = _class_private_field_get(this, _refs);
    if (!(0, _element.hasClass)(dialogElement, `${_constants.DIALOG_CLASS_NAME}--show`)) {
        dialogElement.style.display = 'none';
    }
}
function onTransitionStart() {
    _class_private_field_set(this, _animationStarted, true);
}
(0, _object.mixin)(DialogUI, _localHooks.default);
