"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
Object.defineProperty(exports, "FocusGridManager", {
    enumerable: true,
    get: function() {
        return FocusGridManager;
    }
});
const _console = require("../helpers/console");
const _element = require("../helpers/dom/element");
const _function = require("../helpers/function");
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);
}
/**
 * Possible focus modes.
 * - CELL - The browser's focus stays on the lastly selected cell element.
 * - MIXED - The browser's focus switches from the lastly selected cell element to the currently active editor's
 * `TEXTAREA` element after a delay defined in the manager.
 *
 * @type {{CELL: string, MIXED: string}}
 */ const FOCUS_MODES = Object.freeze({
    CELL: 'cell',
    MIXED: 'mixed'
});
var /**
   * The Handsontable instance.
   */ _hot = /*#__PURE__*/ new WeakMap(), /**
   * The currently enabled focus mode.
   * Can be either:
   *
   * - 'cell' - The browser's focus stays on the lastly selected cell element.
   * - 'mixed' - The browser's focus switches from the lastly selected cell element to the currently active editor's
   * `TEXTAREA` element after a delay defined in the manager.
   *
   * @type {'cell' | 'mixed'}
   */ _focusMode = /*#__PURE__*/ new WeakMap(), /**
   * The delay after which the focus switches from the lastly selected cell to the active editor's `TEXTAREA`
   * element if the focus mode is set to 'mixed'.
   *
   * @type {number}
   */ _refocusDelay = /*#__PURE__*/ new WeakMap(), /**
   * Getter function for the element to be used when refocusing the browser after a delay. If `null`, the active
   * editor's `TEXTAREA` element will be used.
   *
   * @type {null|Function}
   */ _refocusElementGetter = /*#__PURE__*/ new WeakMap(), /**
   * Map of the debounced `select` functions.
   *
   * @type {Map<number, Function>}
   */ _debouncedSelect = /*#__PURE__*/ new WeakMap(), /**
   * Flag to indicate if the selection has changed.
   *
   * @type {boolean}
   */ _hasSelectionChange = /*#__PURE__*/ new WeakMap(), /**
   * Get and return the currently selected and highlighted cell/header element.
   *
   * @param {Function} callback Callback function to be called after the cell element is retrieved.
   */ _getSelectedCell = /*#__PURE__*/ new WeakSet(), /**
   * Manage the browser's focus after each cell selection change.
   */ _focusCell = /*#__PURE__*/ new WeakSet(), /**
   * Manage the browser's focus after cell selection end.
   */ _focusEditorElement = /*#__PURE__*/ new WeakSet(), /**
   * Handle the after selection change event.
   */ _onAfterSelectionChange = /*#__PURE__*/ new WeakSet(), /**
   * Focuses the cell after the render event when the selection has changed.
   */ _onAfterRender = /*#__PURE__*/ new WeakSet(), /**
   * Update the manager configuration after calling `updateSettings`.
   *
   * @param {object} newSettings The new settings passed to the `updateSettings` method.
   */ _onUpdateSettings = /*#__PURE__*/ new WeakSet();
class FocusGridManager {
    init() {
        const hotSettings = _class_private_field_get(this, _hot).getSettings();
        _class_private_field_set(this, _focusMode, hotSettings.imeFastEdit ? FOCUS_MODES.MIXED : FOCUS_MODES.CELL);
        _class_private_field_get(this, _hot).addHook('afterUpdateSettings', (...args)=>_class_private_method_get(this, _onUpdateSettings, onUpdateSettings).call(this, ...args));
        _class_private_field_get(this, _hot).addHook('afterSelection', (...args)=>_class_private_method_get(this, _onAfterSelectionChange, onAfterSelectionChange).call(this, ...args));
        _class_private_field_get(this, _hot).addHook('afterSelectionFocusSet', (...args)=>_class_private_method_get(this, _onAfterSelectionChange, onAfterSelectionChange).call(this, ...args));
        _class_private_field_get(this, _hot).addHook('afterSelectionEnd', (...args)=>_class_private_method_get(this, _focusEditorElement, focusEditorElement).call(this, ...args));
        _class_private_field_get(this, _hot).addHook('afterRender', (...args)=>_class_private_method_get(this, _onAfterRender, onAfterRender).call(this, ...args));
    }
    /**
   * Get the current focus mode.
   *
   * @returns {'cell' | 'mixed'}
   */ getFocusMode() {
        return _class_private_field_get(this, _focusMode);
    }
    /**
   * Set the focus mode.
   *
   * @param {'cell' | 'mixed'} focusMode The new focus mode.
   */ setFocusMode(focusMode) {
        if (Object.values(FOCUS_MODES).includes(focusMode)) {
            _class_private_field_set(this, _focusMode, focusMode);
        } else {
            (0, _console.warn)(`"${focusMode}" is not a valid focus mode.`);
        }
    }
    /**
   * Get the delay after which the focus will change from the cell elements to the active editor's `TEXTAREA`
   * element if the focus mode is set to 'mixed'.
   *
   * @returns {number} Delay in milliseconds.
   */ getRefocusDelay() {
        return _class_private_field_get(this, _refocusDelay);
    }
    /**
   * Set the delay after which the focus will change from the cell elements to the active editor's `TEXTAREA`
   * element if the focus mode is set to 'mixed'.
   *
   * @param {number} delay Delay in milliseconds.
   */ setRefocusDelay(delay) {
        _class_private_field_set(this, _refocusDelay, delay);
    }
    /**
   * Set the function to be used as the "refocus element" getter. It should return a focusable HTML element.
   *
   * @param {Function} getRefocusElementFunction The refocus element getter.
   */ setRefocusElementGetter(getRefocusElementFunction) {
        _class_private_field_set(this, _refocusElementGetter, getRefocusElementFunction);
    }
    /**
   * Get the element to be used when refocusing the browser after a delay in case of the focus mode being 'mixed'.
   *
   * @returns {HTMLTextAreaElement|HTMLElement|undefined}
   */ getRefocusElement() {
        if (typeof _class_private_field_get(this, _refocusElementGetter) === 'function') {
            return _class_private_field_get(this, _refocusElementGetter).call(this);
        }
        return _class_private_field_get(this, _hot).getActiveEditor()?.TEXTAREA;
    }
    /**
   * Moves the browser focus to a connected `HTMLElement`. Plugins should call this instead of
   * `element.focus()` so programmatic focus stays in the focus manager layer.
   *
   * @param {HTMLElement} element The element to focus.
   * @param {FocusOptions} [focusOptions] Optional arguments for the native `focus()` call.
   * @returns {boolean} `true` if `focus()` was called, `false` if `element` is not a connected `HTMLElement`.
   */ focusElement(element, focusOptions) {
        if (!(0, _element.isHTMLElement)(element) || !element.isConnected) {
            return false;
        }
        element.focus(focusOptions);
        return true;
    }
    /**
   * Set the browser's focus to the highlighted cell of the last selection.
   *
   * @param {HTMLTableCellElement} [selectedCell] The highlighted cell/header element.
   */ focusOnHighlightedCell(selectedCell) {
        const focusElement = (element)=>{
            const currentHighlightCoords = _class_private_field_get(this, _hot).getSelectedRangeActive()?.highlight;
            if (!currentHighlightCoords) {
                return;
            }
            let elementToBeFocused = _class_private_field_get(this, _hot).runHooks('modifyFocusedElement', currentHighlightCoords.row, currentHighlightCoords.col, element);
            if (!(0, _element.isHTMLElement)(elementToBeFocused)) {
                elementToBeFocused = element;
            }
            if (elementToBeFocused && !_class_private_field_get(this, _hot).getActiveEditor()?.isOpened()) {
                this.focusElement(elementToBeFocused, {
                    preventScroll: true
                });
            }
        };
        if (selectedCell) {
            focusElement(selectedCell);
        } else {
            _class_private_method_get(this, _getSelectedCell, getSelectedCell).call(this, (element)=>focusElement(element));
        }
    }
    /**
   * Set the focus to the active editor's `TEXTAREA` element after the provided delay. If no delay is provided, it
   * will be taken from the manager's configuration.
   *
   * @param {number} [delay] Delay in milliseconds.
   */ refocusToEditorTextarea(delay = _class_private_field_get(this, _refocusDelay)) {
        // Re-focus on the editor's `TEXTAREA` element (or a predefined element) if the `imeFastEdit` option is enabled.
        if (_class_private_field_get(this, _hot).getSettings().imeFastEdit && !_class_private_field_get(this, _hot).getActiveEditor()?.isOpened()) {
            _class_private_field_get(this, _hot).getActiveEditor()?.refreshValue?.();
            if (!_class_private_field_get(this, _debouncedSelect).has(delay)) {
                _class_private_field_get(this, _debouncedSelect).set(delay, (0, _function.debounce)(()=>{
                    if (!_class_private_field_get(this, _hot).isDestroyed) {
                        this.getRefocusElement()?.select();
                    }
                }, delay));
            }
            _class_private_field_get(this, _debouncedSelect).get(delay)();
        }
    }
    constructor(hotInstance){
        _class_private_method_init(this, _getSelectedCell);
        _class_private_method_init(this, _focusCell);
        _class_private_method_init(this, _focusEditorElement);
        _class_private_method_init(this, _onAfterSelectionChange);
        _class_private_method_init(this, _onAfterRender);
        _class_private_method_init(this, _onUpdateSettings);
        _class_private_field_init(this, _hot, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _focusMode, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _refocusDelay, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _refocusElementGetter, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _debouncedSelect, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _hasSelectionChange, {
            writable: true,
            value: void 0
        });
        _class_private_field_set(this, _refocusDelay, 1);
        _class_private_field_set(this, _refocusElementGetter, null);
        _class_private_field_set(this, _debouncedSelect, new Map());
        _class_private_field_set(this, _hasSelectionChange, false);
        _class_private_field_set(this, _hot, hotInstance);
    }
}
function getSelectedCell(callback) {
    const highlight = _class_private_field_get(this, _hot).getSelectedRangeActive()?.highlight;
    if (!highlight || !_class_private_field_get(this, _hot).selection.isCellVisible(highlight)) {
        callback(null);
        return;
    }
    const cell = _class_private_field_get(this, _hot).getCell(highlight.row, highlight.col, true);
    if (cell === null) {
        _class_private_field_get(this, _hot).addHookOnce('afterScroll', ()=>{
            callback(_class_private_field_get(this, _hot).getCell(highlight.row, highlight.col, true));
        });
    } else {
        callback(cell);
    }
}
function focusCell() {
    _class_private_method_get(this, _getSelectedCell, getSelectedCell).call(this, (selectedCell)=>{
        const { activeElement } = _class_private_field_get(this, _hot).rootDocument;
        // Blurring the `activeElement` removes the unwanted border around the focusable element (#6877)
        // and resets the `document.activeElement` property. The blurring should happen only when the
        // previously selected input element has not belonged to the Handsontable editor. If blurring is
        // triggered for all elements, there is a problem with the disappearing IME editor (#9672).
        if (activeElement && (0, _element.isOutsideInput)(activeElement)) {
            activeElement.blur();
        }
        this.focusOnHighlightedCell(selectedCell);
    });
}
function focusEditorElement() {
    _class_private_method_get(this, _getSelectedCell, getSelectedCell).call(this, (selectedCell)=>{
        if (this.getFocusMode() === FOCUS_MODES.MIXED && selectedCell?.nodeName === 'TD') {
            this.refocusToEditorTextarea();
        }
    });
}
function onAfterSelectionChange() {
    _class_private_field_set(this, _hasSelectionChange, true);
}
function onAfterRender() {
    if (_class_private_field_get(this, _hasSelectionChange)) {
        _class_private_field_set(this, _hasSelectionChange, false);
        _class_private_method_get(this, _focusCell, focusCell).call(this);
    }
}
function onUpdateSettings(newSettings) {
    if (typeof newSettings.imeFastEdit === 'boolean') {
        this.setFocusMode(newSettings.imeFastEdit ? FOCUS_MODES.MIXED : FOCUS_MODES.CELL);
    }
}
