"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
function _export(target, all) {
    for(var name in all)Object.defineProperty(target, name, {
        enumerable: true,
        get: Object.getOwnPropertyDescriptor(all, name).get
    });
}
_export(exports, {
    get Dialog () {
        return Dialog;
    },
    get PLUGIN_KEY () {
        return PLUGIN_KEY;
    },
    get PLUGIN_PRIORITY () {
        return PLUGIN_PRIORITY;
    }
});
const _base = require("../base");
const _errors = require("../../helpers/errors");
const _ui = require("./ui");
const _object = require("../../helpers/object");
const _constants = /*#__PURE__*/ _interop_require_wildcard(require("../../i18n/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 _getRequireWildcardCache(nodeInterop) {
    if (typeof WeakMap !== "function") return null;
    var cacheBabelInterop = new WeakMap();
    var cacheNodeInterop = new WeakMap();
    return (_getRequireWildcardCache = function(nodeInterop) {
        return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
    })(nodeInterop);
}
function _interop_require_wildcard(obj, nodeInterop) {
    if (!nodeInterop && obj && obj.__esModule) {
        return obj;
    }
    if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
        return {
            default: obj
        };
    }
    var cache = _getRequireWildcardCache(nodeInterop);
    if (cache && cache.has(obj)) {
        return cache.get(obj);
    }
    var newObj = {
        __proto__: null
    };
    var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
    for(var key in obj){
        if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
            var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
            if (desc && (desc.get || desc.set)) {
                Object.defineProperty(newObj, key, desc);
            } else {
                newObj[key] = obj[key];
            }
        }
    }
    newObj.default = obj;
    if (cache) {
        cache.set(obj, newObj);
    }
    return newObj;
}
const PLUGIN_KEY = 'dialog';
const PLUGIN_PRIORITY = 360;
const SHORTCUTS_GROUP = PLUGIN_KEY;
const SHORTCUTS_CONTEXT_NAME = `plugin:${PLUGIN_KEY}`;
var /**
   * UI instance of the dialog plugin.
   *
   * @type {DialogUI}
   */ _ui1 = /*#__PURE__*/ new WeakMap(), /**
   * Flag indicating if dialog is currently visible.
   *
   * @type {boolean}
   */ _isVisible = /*#__PURE__*/ new WeakMap(), /**
   * Keeps the selection state that will be restored after the dialog is closed.
   *
   * @type {SelectionState | null}
   */ _selectionState = /*#__PURE__*/ new WeakMap(), /**
   * Register shortcuts responsible for closing the dialog and navigating through the dialog.
   */ _registerShortcuts = /*#__PURE__*/ new WeakSet(), /**
   * Unregister shortcuts responsible for closing the dialog and navigating through the dialog.
   */ _unregisterShortcuts = /*#__PURE__*/ new WeakSet(), /**
   * Registers the focus scope for the dialog plugin.
   */ _registerFocusScope = /*#__PURE__*/ new WeakSet(), /**
   * Unregisters the focus scope for the dialog plugin.
   */ _unregisterFocusScope = /*#__PURE__*/ new WeakSet(), /**
   * Called after the rendering of the table is completed. It updates the width and
   * height of the dialog container to the same size as the table.
   */ _onAfterViewRender = /*#__PURE__*/ new WeakSet();
class Dialog extends _base.BasePlugin {
    static get PLUGIN_KEY() {
        return PLUGIN_KEY;
    }
    static get PLUGIN_PRIORITY() {
        return PLUGIN_PRIORITY;
    }
    static get DEFAULT_SETTINGS() {
        return {
            template: null,
            content: '',
            customClassName: '',
            background: 'solid',
            contentBackground: false,
            animation: true,
            closable: false,
            a11y: {
                role: 'dialog',
                ariaLabel: 'Dialog',
                ariaLabelledby: '',
                ariaDescribedby: ''
            }
        };
    }
    static get SETTINGS_VALIDATORS() {
        return {
            template: (value)=>(0, _object.isObject)(value) && typeof [
                    'alert',
                    'confirm'
                ].includes(value.type) && typeof value.title === 'string' && (typeof value?.description === 'undefined' || typeof value?.description === 'string') && (typeof value?.buttons === 'undefined' || Array.isArray(value?.buttons) && value.buttons.every((item)=>typeof item === 'object' && typeof item.text === 'string' && [
                        'primary',
                        'secondary'
                    ].includes(item.type) && (typeof item.callback === 'undefined' || typeof item.callback === 'function'))),
            content: (value)=>typeof value === 'string' || typeof HTMLElement !== 'undefined' && value instanceof HTMLElement || typeof DocumentFragment !== 'undefined' && value instanceof DocumentFragment,
            customClassName: (value)=>typeof value === 'string',
            background: (value)=>[
                    'solid',
                    'semi-transparent'
                ].includes(value),
            contentBackground: (value)=>typeof value === 'boolean',
            animation: (value)=>typeof value === 'boolean',
            closable: (value)=>typeof value === 'boolean',
            a11y: (value)=>(0, _object.isObject)(value) && (typeof value?.role === 'undefined' || [
                    'dialog',
                    'alertdialog'
                ].includes(value?.role)) && (typeof value?.ariaLabel === 'undefined' || typeof value?.ariaLabel === 'string') && (typeof value?.ariaLabelledby === 'undefined' || typeof value?.ariaLabelledby === 'string') && (typeof value?.ariaDescribedby === 'undefined' || typeof value?.ariaDescribedby === 'string')
        };
    }
    /**
   * Check if the plugin is enabled in the handsontable settings.
   *
   * @returns {boolean}
   */ isEnabled() {
        return !!this.hot.getSettings()[PLUGIN_KEY];
    }
    /**
   * Enable plugin for this Handsontable instance.
   */ enablePlugin() {
        if (this.enabled) {
            return;
        }
        if (!_class_private_field_get(this, _ui1)) {
            _class_private_field_set(this, _ui1, new _ui.DialogUI({
                rootElement: this.hot.rootGridElement,
                sanitizer: this.hot.getSettings().sanitizer,
                isRtl: this.hot.isRtl()
            }));
        }
        _class_private_method_get(this, _registerShortcuts, registerShortcuts).call(this);
        _class_private_method_get(this, _registerFocusScope, registerFocusScope).call(this);
        this.addHook('afterViewRender', ()=>_class_private_method_get(this, _onAfterViewRender, onAfterViewRender).call(this));
        super.enablePlugin();
    }
    /**
   * Update plugin state after Handsontable settings update.
   */ updatePlugin() {
        this.disablePlugin();
        this.enablePlugin();
        super.updatePlugin();
    }
    /**
   * Disable plugin for this Handsontable instance.
   */ disablePlugin() {
        this.hide();
        _class_private_method_get(this, _unregisterShortcuts, unregisterShortcuts).call(this);
        _class_private_method_get(this, _unregisterFocusScope, unregisterFocusScope).call(this);
        super.disablePlugin();
    }
    /**
   * Check if the dialog is currently visible.
   *
   * @returns {boolean} True if the dialog is visible, false otherwise.
   */ isVisible() {
        return _class_private_field_get(this, _isVisible);
    }
    /**
   * Show dialog with given configuration.
   * Displays the dialog with the specified content and options.
   *
   * @param {object} options Dialog configuration object containing content and display options.
   * @param {object} options.template The template to use for the dialog (default: `null`). The error will be thrown when
   * the template is provided together with the `content` option.
   * @param {'confirm'} options.template.type The type of the template ('confirm').
   * @param {string} options.template.title The title of the dialog.
   * @param {string} options.template.description The description of the dialog. Default: ''.
   * @param {object[]} options.template.buttons The buttons to display in the dialog. Default: [].
   * @param {string} options.template.buttons.text The text of the button.
   * @param {'primary' | 'secondary'} options.template.buttons.type The type of the button.
   * @param {function(MouseEvent)} options.template.buttons.callback The callback to trigger when the button is clicked.
   * @param {string|HTMLElement|DocumentFragment} options.content The content to display in the dialog. Can be a string, HTMLElement, or DocumentFragment. Default: ''
   * @param {string} options.customClassName Custom CSS class name to apply to the dialog container. Default: ''
   * @param {'solid'|'semi-transparent'} options.background Dialog background variant. Default: 'solid'.
   * @param {boolean} options.contentBackground Whether to show content background. Default: false.
   * @param {boolean} options.animation Whether to enable animations when showing/hiding the dialog. Default: true.
   * @param {boolean} options.closable Whether the dialog can be closed by user interaction. Default: false.
   * @param {object} options.a11y Object with accessibility options.
   * @param {string} options.a11y.role The role of the dialog. Default: 'dialog'.
   * @param {string} options.a11y.ariaLabel The label of the dialog. Default: 'Dialog'.
   * @param {string} options.a11y.ariaLabelledby The ID of the element that labels the dialog. Default: ''.
   * @param {string} options.a11y.ariaDescribedby The ID of the element that describes the dialog. Default: ''.
   */ show(options = {}) {
        if (!this.enabled) {
            return;
        }
        if (this.isVisible()) {
            this.update(options);
            return;
        }
        this.hot.runHooks('beforeDialogShow');
        this.update(options);
        _class_private_field_get(this, _ui1).showDialog(this.getSetting('animation'));
        _class_private_field_set(this, _isVisible, true);
        this.hot.getFocusScopeManager().activateScope(PLUGIN_KEY);
        _class_private_field_set(this, _selectionState, this.hot.selection.exportSelection());
        this.hot.deselectCell();
        this.hot.runHooks('afterDialogShow');
    }
    /**
   * Hide the currently open dialog.
   * Closes the dialog and restores the focus to the table.
   */ hide() {
        if (!this.isVisible()) {
            return;
        }
        this.hot.runHooks('beforeDialogHide');
        _class_private_field_get(this, _ui1).hideDialog(this.getSetting('animation'));
        _class_private_field_set(this, _isVisible, false);
        this.hot.getFocusScopeManager().deactivateScope(PLUGIN_KEY);
        if (_class_private_field_get(this, _selectionState)?.ranges.length > 0) {
            this.hot.selection.importSelection(_class_private_field_get(this, _selectionState));
            this.hot.view.render();
            _class_private_field_set(this, _selectionState, null);
        } else {
            this.hot.view.render();
            _class_private_field_set(this, _selectionState, null);
        }
        this.hot.runHooks('afterDialogHide');
    }
    /**
   * Update the dialog configuration.
   *
   * @param {object} options Dialog configuration object containing content and display options.
   * @param {object} options.template The template to use for the dialog (default: `null`). The error will be thrown when
   * the template is provided together with the `content` option.
   * @param {'confirm'} options.template.type The type of the template ('confirm').
   * @param {string} options.template.title The title of the dialog.
   * @param {string} options.template.description The description of the dialog. Default: ''.
   * @param {object[]} options.template.buttons The buttons to display in the dialog. Default: [].
   * @param {string} options.template.buttons.text The text of the button.
   * @param {'primary' | 'secondary'} options.template.buttons.type The type of the button.
   * @param {function(MouseEvent)} options.template.buttons.callback The callback to trigger when the button is clicked.
   * @param {string|HTMLElement|DocumentFragment} options.content The content to display in the dialog. Can be a string, HTMLElement, or DocumentFragment. Default: ''
   * @param {string} options.customClassName Custom CSS class name to apply to the dialog container. Default: ''
   * @param {'solid'|'semi-transparent'} options.background Dialog background variant. Default: 'solid'.
   * @param {boolean} options.contentBackground Whether to show content background. Default: false.
   * @param {boolean} options.animation Whether to enable animations when showing/hiding the dialog. Default: true.
   * @param {boolean} options.closable Whether the dialog can be closed by user interaction. Default: false.
   * @param {object} options.a11y Object with accessibility options.
   * @param {string} options.a11y.role The role of the dialog. Default: 'dialog'.
   * @param {string} options.a11y.ariaLabel The label of the dialog. Default: 'Dialog'.
   * @param {string} options.a11y.ariaLabelledby The ID of the element that labels the dialog. Default: ''.
   * @param {string} options.a11y.ariaDescribedby The ID of the element that describes the dialog. Default: ''.
   */ update(options) {
        if (!this.enabled) {
            return;
        }
        this.updatePluginSettings(options);
        const templateValue = this.getSetting('template');
        if (templateValue !== Dialog.DEFAULT_SETTINGS.template && this.getSetting('content') !== Dialog.DEFAULT_SETTINGS.content) {
            (0, _errors.throwWithCause)('The `template` option cannot be used together with the `content` option.');
        }
        if (templateValue) {
            _class_private_field_get(this, _ui1).useTemplate(templateValue.type, {
                id: this.hot.guid,
                ...templateValue
            });
        } else {
            _class_private_field_get(this, _ui1).useDefaultTemplate();
        }
        _class_private_field_get(this, _ui1).updateDialog({
            isVisible: this.isVisible(),
            content: this.getSetting('content'),
            customClassName: this.getSetting('customClassName'),
            background: this.getSetting('background'),
            contentBackground: this.getSetting('contentBackground'),
            animation: this.getSetting('animation'),
            a11y: this.getSetting('a11y')
        });
    }
    /**
   * Displays the alert dialog with the specified content.
   *
   * @param {string | { title: string, description: string }} message The message to display in the dialog.
   * Can be a string or an object with `title` and `description` properties.
   * @param {function(MouseEvent): void} [callback] The callback to trigger when the button is clicked.
   */ showAlert(message, callback) {
        const { title = 'Alert', description } = (0, _object.isObject)(message) ? message : {
            title: message
        };
        this.show({
            template: {
                type: 'confirm',
                title,
                description,
                buttons: [
                    {
                        text: this.hot.getTranslatedPhrase(_constants.OK),
                        type: 'primary',
                        callback: (...args)=>callback?.(...args)
                    }
                ]
            },
            contentBackground: false,
            background: 'solid',
            animation: true,
            closable: false
        });
    }
    /**
   * Displays the confirm dialog with the specified content and options.
   *
   * @param {string | { title: string, description: string }} message The message to display in the dialog.
   * Can be a string or an object with `title` and `description` properties.
   * @param {function(MouseEvent): void} [onOk] The callback to trigger when the OK button is clicked.
   * @param {function(MouseEvent): void} [onCancel] The callback to trigger when the Cancel button is clicked.
   */ showConfirm(message, onOk, onCancel) {
        const { title = 'Confirm', description } = (0, _object.isObject)(message) ? message : {
            title: message
        };
        this.show({
            template: {
                type: 'confirm',
                title,
                description,
                buttons: [
                    {
                        text: this.hot.getTranslatedPhrase(_constants.CANCEL),
                        type: 'secondary',
                        callback: (...args)=>onCancel?.(...args)
                    },
                    {
                        text: this.hot.getTranslatedPhrase(_constants.OK),
                        type: 'primary',
                        callback: (...args)=>onOk?.(...args)
                    }
                ]
            },
            contentBackground: true,
            background: 'semi-transparent',
            animation: true,
            closable: false
        });
    }
    /**
   * Focus the dialog.
   */ focus() {
        _class_private_field_get(this, _ui1).focusDialog();
    }
    /**
   * Destroy dialog and reset plugin state.
   */ destroy() {
        _class_private_field_get(this, _ui1)?.destroyDialog();
        _class_private_field_set(this, _ui1, null);
        _class_private_field_set(this, _isVisible, false);
        _class_private_field_set(this, _selectionState, null);
        super.destroy();
    }
    constructor(...args){
        super(...args), _class_private_method_init(this, _registerShortcuts), _class_private_method_init(this, _unregisterShortcuts), _class_private_method_init(this, _registerFocusScope), _class_private_method_init(this, _unregisterFocusScope), _class_private_method_init(this, _onAfterViewRender), _class_private_field_init(this, _ui1, {
            writable: true,
            value: void 0
        }), _class_private_field_init(this, _isVisible, {
            writable: true,
            value: void 0
        }), _class_private_field_init(this, _selectionState, {
            writable: true,
            value: void 0
        }), _class_private_field_set(this, _ui1, null), _class_private_field_set(this, _isVisible, false), _class_private_field_set(this, _selectionState, null);
    }
}
function registerShortcuts() {
    const manager = this.hot.getShortcutManager();
    const pluginContext = manager.getContext(SHORTCUTS_CONTEXT_NAME) ?? manager.addContext(SHORTCUTS_CONTEXT_NAME);
    pluginContext.addShortcut({
        keys: [
            [
                'Escape'
            ]
        ],
        callback: ()=>{
            this.hide();
        },
        runOnlyIf: ()=>_class_private_field_get(this, _isVisible) && this.getSetting('closable'),
        group: SHORTCUTS_GROUP
    });
    pluginContext.addShortcut({
        keys: [
            [
                'Shift',
                'Tab'
            ],
            [
                'Tab'
            ]
        ],
        preventDefault: false,
        callback: (event)=>{
            this.hot._registerTimeout(()=>{
                if (event.shiftKey) {
                    this.hot.runHooks('dialogFocusPreviousElement');
                } else {
                    this.hot.runHooks('dialogFocusNextElement');
                }
            });
        },
        group: SHORTCUTS_GROUP
    });
}
function unregisterShortcuts() {
    const shortcutManager = this.hot.getShortcutManager();
    const pluginContext = shortcutManager.getContext(SHORTCUTS_CONTEXT_NAME);
    pluginContext.removeShortcutsByGroup(SHORTCUTS_GROUP);
}
function registerFocusScope() {
    this.hot.getFocusScopeManager().registerScope(PLUGIN_KEY, _class_private_field_get(this, _ui1).getContainer(), {
        shortcutsContextName: SHORTCUTS_CONTEXT_NAME,
        type: 'modal',
        runOnlyIf: ()=>this.isVisible(),
        onActivate: (focusSource)=>{
            const isListening = this.hot.isListening();
            const focusableElements = _class_private_field_get(this, _ui1).getFocusableElements();
            if (focusableElements.length > 0) {
                if (focusSource === 'tab_from_above') {
                    focusableElements.at(0).focus();
                } else if (focusSource === 'tab_from_below') {
                    focusableElements.at(-1).focus();
                }
            } else if (focusSource !== 'tab_from_above' && focusSource !== 'tab_from_below' && isListening && !_class_private_field_get(this, _ui1).getContainer().contains(this.hot.rootDocument.activeElement)) {
                _class_private_field_get(this, _ui1).getContainer().focus();
            }
            if (isListening) {
                this.hot.runHooks('afterDialogFocus', focusSource === 'unknown' ? 'show' : focusSource);
            }
        }
    });
}
function unregisterFocusScope() {
    this.hot.getFocusScopeManager().unregisterScope(PLUGIN_KEY);
}
function onAfterViewRender() {
    const { view, rootWrapperElement, rootWindow } = this.hot;
    const width = view.isHorizontallyScrollableByWindow() ? view.getTotalTableWidth() : view.getWorkspaceWidth();
    _class_private_field_get(this, _ui1).updateWidth(width);
    const dialogInfo = rootWrapperElement.querySelector('.hot-display-license-info');
    if (dialogInfo) {
        const height = dialogInfo.offsetHeight;
        const marginTop = Number.parseFloat(rootWindow.getComputedStyle(dialogInfo).marginTop);
        _class_private_field_get(this, _ui1).updateHeight(height + marginTop);
    }
}
