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);
}
import { BaseEditor } from '../baseEditor/index.mjs';
import EventManager from '../../eventManager.mjs';
import { DropdownController } from './controllers/dropdownController.mjs';
import { SelectedItemsController } from './controllers/selectedItemsController.mjs';
import { addClass, setAttribute } from '../../helpers/dom/element.mjs';
import { isPrintableChar } from '../../helpers/unicode.mjs';
import { A11Y_LABEL, A11Y_GROUP } from '../../helpers/a11y.mjs';
import { EDITOR_EDIT_GROUP } from '../../shortcutContexts/constants.mjs';
import { getValuesIntersection, parseStringifiedValue } from './utils/utils.mjs';
export const EDITOR_TYPE = 'multiselect';
const DROPDOWN_ELEMENT_CSS_CLASSNAME = 'ht-multi-select-editor';
const DROPDOWN_ARIA_LABEL = 'Select options';
const SHORTCUTS_GROUP = 'multiselectEditor';
const EDITOR_VISIBLE_CLASS_NAME = 'ht_editor_visible';
var /**
   * Set of values that are currently checked in the dropdown.
   *
   * @private
   * @type {SelectedItemsController}
   */ _selectedItems = /*#__PURE__*/ new WeakMap(), /**
   * Container element that hosts the editor.
   *
   * @private
   * @type {HTMLDivElement}
   */ _editorContainer = /*#__PURE__*/ new WeakMap(), /**
   * Gets the source (options) for the editor.
   *
   * @returns {Array} The source (options) for the editor.
   */ _getSource = /*#__PURE__*/ new WeakSet(), /**
   * Register shortcuts responsible for handling editor.
   *
   * @private
   */ _registerShortcuts = /*#__PURE__*/ new WeakSet(), /**
   * Unregister shortcuts responsible for handling editor.
   *
   * @private
   */ _unregisterShortcuts = /*#__PURE__*/ new WeakSet(), /**
   * Shows the editable element.
   */ _showEditableElement = /*#__PURE__*/ new WeakSet(), /**
   * Hides the editable element.
   */ _hideEditableElement = /*#__PURE__*/ new WeakSet(), /**
   * Blocks new selections.
   */ _blockNewSelections = /*#__PURE__*/ new WeakSet(), /**
   * Unblocks new selections.
   */ _unblockNewSelections = /*#__PURE__*/ new WeakSet(), /**
   * Filters the dropdown entries.
   *
   * @param {string} query The value of the input.
   * @param {boolean} filterSelectedItems If true, the selected items will be filtered out of the dropdown.
   */ _filterEntries = /*#__PURE__*/ new WeakSet(), /**
   * Gets the available space for the dropdown.
   *
   * @returns {{spaceAbove: number, spaceBelow: number, cellHeight: number}} The available space.
   */ _getAvailableSpace = /*#__PURE__*/ new WeakSet(), /**
   * Saves the current selection state to the cell without closing the editor.
   *
   * @private
   */ _saveCurrentSelection = /*#__PURE__*/ new WeakSet(), /**
   * Adds a value reported by the dropdown to the selection set and saves to the cell.
   *
   * @private
   * @param {string} selectedKey Key of the selected value.
   * @param {string} selectedValue Value of the selected value.
   */ _addSelectedValue = /*#__PURE__*/ new WeakSet(), /**
   * Removes a deselected dropdown value from the selection set and saves to the cell.
   *
   * @private
   * @param {string} deselectedKey Key of the deselected value.
   * @param {string} deselectedValue Value of the deselected value.
   */ _removeSelectedValue = /*#__PURE__*/ new WeakSet(), /**
   * Synchronizes the internal selection set with data deserialized from the cell value.
   *
   * @private
   * @param {Array<*>} valuesArray Values decoded from the stored JSON string.
   */ _syncSelectedValues = /*#__PURE__*/ new WeakSet(), /**
   * Helper for reading editor-related settings directly from the cell meta.
   *
   * @private
   * @param {string} settingKey Cell meta key to read.
   * @returns {*} Returns the stored value for the provided key.
   */ _getEditorSetting = /*#__PURE__*/ new WeakSet(), /**
   * `afterSetSourceDataAtCell` hook callback.
   *
   * @private
   * @param {Array<*>} changes The changes.
   * @param {string} source The source of the change.
   */ _onAfterSetSourceDataAtCell = /*#__PURE__*/ new WeakSet();
/**
 * @private
 * @class MultiSelectEditor
 */ export class MultiSelectEditor extends BaseEditor {
    /**
   * Returns the editor type.
   *
   * @returns {string} The editor type.
   */ static get EDITOR_TYPE() {
        return EDITOR_TYPE;
    }
    /**
   * Creates an editor's elements and adds necessary CSS classnames.
   */ createElements() {
        const { rootDocument } = this.hot;
        _class_private_field_set(this, _editorContainer, rootDocument.createElement('div'));
        _class_private_field_get(this, _editorContainer).style.display = 'none';
        addClass(_class_private_field_get(this, _editorContainer), 'handsontableEditor');
        this.dropdownContainerElement = this.hot.rootDocument.createElement('div');
        addClass(this.dropdownContainerElement, `${DROPDOWN_ELEMENT_CSS_CLASSNAME} handsontableEditor`);
        setAttribute(this.dropdownContainerElement, [
            A11Y_LABEL(DROPDOWN_ARIA_LABEL),
            A11Y_GROUP()
        ]);
        _class_private_field_get(this, _editorContainer).appendChild(this.dropdownContainerElement);
        this.hot.rootElement.appendChild(_class_private_field_get(this, _editorContainer));
        this.dropdownController = new DropdownController(this.dropdownContainerElement, this.hot.guid);
    }
    /**
   * Prepares editor's meta data.
   *
   * @param {number} row The visual row index.
   * @param {number} col The visual column index.
   * @param {number|string} prop The column property (passed when datasource is an array of objects).
   * @param {HTMLTableCellElement} td The rendered cell element.
   * @param {*} value The rendered value.
   * @param {object} cellProperties The cell meta object (see {@link Core#getCellMeta}).
   */ prepare(row, col, prop, td, value, cellProperties) {
        super.prepare(row, col, prop, td, value, cellProperties);
        const parsedValue = parseStringifiedValue(value);
        const valuesArray = Array.isArray(parsedValue) ? parsedValue : [
            parsedValue
        ];
        const valuesIntersection = getValuesIntersection(valuesArray, _class_private_method_get(this, _getSource, getSource).call(this));
        this.dropdownController.reset();
        if (valuesIntersection.length >= _class_private_method_get(this, _getEditorSetting, getEditorSetting).call(this, 'maxSelections')) {
            _class_private_method_get(this, _blockNewSelections, blockNewSelections).call(this);
        }
        _class_private_method_get(this, _syncSelectedValues, syncSelectedValues).call(this, valuesIntersection);
        this.dropdownController.setSourceSortFunction(this.cellProperties.sourceSortFunction);
        this.dropdownController.fillDropdown(_class_private_method_get(this, _getSource, getSource).call(this), valuesIntersection);
        this.dropdownController.setVisibleRowsNumberSetting(_class_private_method_get(this, _getEditorSetting, getEditorSetting).call(this, 'visibleRows'));
        this.dropdownController.setSearchInputVisibility(_class_private_method_get(this, _getEditorSetting, getEditorSetting).call(this, 'searchInput'));
        if (cellProperties.maxSelections !== undefined) {
            _class_private_field_get(this, _selectedItems).setMaxSelectionCount(cellProperties.maxSelections);
        }
    }
    /**
   * Finishes editing and start saving or restoring process for editing cell.
   *
   * @param {boolean} restoreOriginalValue If true, then closes editor without saving value from the editor into a cell.
   * @param {boolean} ctrlDown If true, then saveValue will save editor's value to each cell in the last selected range.
   * @param {Function} callback The callback function, fired after editor closing.
   */ finishEditing(restoreOriginalValue, ctrlDown, callback) {
        super.finishEditing(restoreOriginalValue, ctrlDown, callback);
    }
    /**
   * Binds events to the editor.
   *
   * @private
   */ bindEvents() {
        this.dropdownController.addLocalHook('afterDropdownItemChecked', (selectedKey, selectedValue)=>{
            _class_private_method_get(this, _addSelectedValue, addSelectedValue).call(this, selectedKey, selectedValue);
            if (_class_private_field_get(this, _selectedItems).getSize() >= this.cellProperties.maxSelections) {
                _class_private_method_get(this, _blockNewSelections, blockNewSelections).call(this);
            }
        });
        this.dropdownController.addLocalHook('afterDropdownItemUnchecked', (deselectedKey, deselectedValue)=>{
            _class_private_method_get(this, _removeSelectedValue, removeSelectedValue).call(this, deselectedKey, deselectedValue);
            if (_class_private_field_get(this, _selectedItems).getSize() < this.cellProperties.maxSelections) {
                _class_private_method_get(this, _unblockNewSelections, unblockNewSelections).call(this);
            }
        });
        this.addHook('afterDestroy', ()=>this.destroy());
        this.addHook('afterSetSourceDataAtCell', (...args)=>_class_private_method_get(this, _onAfterSetSourceDataAtCell, onAfterSetSourceDataAtCell).call(this, ...args));
        this.addHook('afterScrollHorizontally', ()=>this.refreshDimensions());
        this.addHook('afterScrollVertically', ()=>this.refreshDimensions());
        this.dropdownController.getInputController().addLocalHook('triggerFilter', (value)=>_class_private_method_get(this, _filterEntries, filterEntries).call(this, value));
    }
    /**
   * Opens the editor.
   *
   * @param {Event} event The event object.
   */ open(event) {
        if (isPrintableChar(event?.keyCode)) {
            const character = event.key.length === 1 ? event.key : String.fromCharCode(event.keyCode);
            if (this.dropdownController.getInputController().enabled) {
                this.dropdownController.getInputController().setValue(character);
                _class_private_method_get(this, _filterEntries, filterEntries).call(this, character);
            }
            event.preventDefault();
            this.enableFullEditMode();
        }
        _class_private_method_get(this, _showEditableElement, showEditableElement).call(this);
        this.refreshDimensions();
        this.hot.getShortcutManager().setActiveContextName('editor');
        _class_private_method_get(this, _registerShortcuts, registerShortcuts).call(this);
        this.dropdownController.getInputController().listen();
        this.dropdownController.updateDimensions(_class_private_method_get(this, _getAvailableSpace, getAvailableSpace).call(this));
    }
    /**
   * Closes the editor.
   */ close() {
        _class_private_method_get(this, _hideEditableElement, hideEditableElement).call(this);
        _class_private_method_get(this, _unregisterShortcuts, unregisterShortcuts).call(this);
        this.dropdownController.getInputController().unlisten();
    }
    /**
   * Returns the editor's value.
   *
   * @returns {string} The editor's value.
   */ getValue() {
        return _class_private_field_get(this, _selectedItems).getItemsArray();
    }
    /**
   * Sets the editor's value.
   */ setValue() {
    // Currently not implemented.
    //
    // As the MultiSelectEditor saves data after every change, there's no need to set the value when
    // the editor is closed.
    //
    // TODO: discuss this behavior and consider implementing an option for the data-saving strategy.
    }
    /**
   * Refreshes the editor's dimensions.
   */ refreshDimensions() {
        // TD is outside of the viewport.
        if (!this.getEditedCell()) {
            this.close();
            return;
        }
        const { top, start, height } = this.getEditedCellRect();
        const editorStyle = _class_private_field_get(this, _editorContainer).style;
        editorStyle.top = `${top + height}px`;
        editorStyle[this.hot.isRtl() ? 'right' : 'left'] = `${start}px`;
        addClass(_class_private_field_get(this, _editorContainer), EDITOR_VISIBLE_CLASS_NAME);
    }
    /**
   * Focuses the editor.
   */ focus() {
        if (_class_private_method_get(this, _getEditorSetting, getEditorSetting).call(this, 'searchInput') === false) {
            this.dropdownController.focusFirstItem();
        } else {
            this.dropdownController.focusSearchInput();
        }
    }
    /**
   * Gets the input element.
   *
   * @returns {HTMLInputElement} The input element.
   */ getInputElement() {
        return this.dropdownController.getInputController().getInputElement();
    }
    /**
   * Called when the editor is destroyed.
   *
   * @private
   */ destroy() {
        this.close();
        this.dropdownController.reset();
    }
    /**
   * @param {Core} hotInstance The Handsontable instance.
   */ constructor(hotInstance){
        super(hotInstance), _class_private_method_init(this, _getSource), _class_private_method_init(this, _registerShortcuts), _class_private_method_init(this, _unregisterShortcuts), _class_private_method_init(this, _showEditableElement), _class_private_method_init(this, _hideEditableElement), _class_private_method_init(this, _blockNewSelections), _class_private_method_init(this, _unblockNewSelections), _class_private_method_init(this, _filterEntries), _class_private_method_init(this, _getAvailableSpace), _class_private_method_init(this, _saveCurrentSelection), _class_private_method_init(this, _addSelectedValue), _class_private_method_init(this, _removeSelectedValue), _class_private_method_init(this, _syncSelectedValues), _class_private_method_init(this, _getEditorSetting), _class_private_method_init(this, _onAfterSetSourceDataAtCell), _class_private_field_init(this, _selectedItems, {
            writable: true,
            value: void 0
        }), _class_private_field_init(this, _editorContainer, {
            writable: true,
            value: void 0
        }), /**
   * Prevent the editor from closing after data change.
   *
   * @type {boolean}
   */ this._closeAfterDataChange = false, _class_private_field_set(this, _selectedItems, new SelectedItemsController()), _class_private_field_set(this, _editorContainer, null), /**
   * Container element that hosts the dropdown with checkbox options.
   *
   * @private
   * @type {HTMLDivElement}
   */ this.dropdownContainerElement = null, /**
   * Dropdown controller responsible for rendering and syncing option states.
   *
   * @private
   * @type {DropdownController|null}
   */ this.dropdownController = this.dropdownController ?? null;
        this.eventManager = new EventManager(this);
        this.createElements();
        this.bindEvents();
    }
}
function getSource() {
    return this.cellProperties.source ?? [];
}
function registerShortcuts() {
    const shortcutManager = this.hot.getShortcutManager();
    const editorContext = shortcutManager.getContext('editor');
    editorContext.addShortcuts([
        {
            keys: [
                [
                    'ArrowUp'
                ]
            ],
            callback: ()=>{
                this.dropdownController.focusPreviousItem();
            }
        },
        {
            keys: [
                [
                    'ArrowDown'
                ]
            ],
            callback: ()=>{
                if (this.hot.rootDocument.activeElement === this.getInputElement()) {
                    this.dropdownController.focusFirstItem();
                } else {
                    this.dropdownController.focusNextItem();
                }
            }
        }
    ], {
        group: SHORTCUTS_GROUP
    });
    editorContext.addShortcuts([
        {
            keys: [
                [
                    'enter'
                ],
                [
                    'shift',
                    'enter'
                ],
                [
                    'control/meta',
                    'enter'
                ],
                [
                    'control/meta',
                    'shift',
                    'enter'
                ]
            ],
            runOnlyIf: ()=>!_class_private_method_get(this, _getEditorSetting, getEditorSetting).call(this, 'enterCommits'),
            callback: (event)=>{
                const activeElement = this.hot.rootDocument.activeElement;
                if (activeElement.tagName === 'INPUT' && activeElement.type === 'checkbox') {
                    activeElement.checked = !activeElement.checked;
                    activeElement.dispatchEvent(new Event('change'));
                }
                event.preventDefault();
                return false;
            }
        },
        {
            keys: [
                [
                    'space'
                ]
            ],
            runOnlyIf: ()=>!_class_private_method_get(this, _getEditorSetting, getEditorSetting).call(this, 'enterCommits'),
            callback: (event)=>{
                event.preventDefault();
            }
        }
    ], {
        group: SHORTCUTS_GROUP,
        relativeToGroup: EDITOR_EDIT_GROUP,
        position: 'before'
    });
}
function unregisterShortcuts() {
    const shortcutManager = this.hot.getShortcutManager();
    const editorContext = shortcutManager.getContext('editor');
    editorContext.removeShortcutsByGroup(SHORTCUTS_GROUP);
}
function showEditableElement() {
    _class_private_field_get(this, _editorContainer).style.display = '';
}
function hideEditableElement() {
    _class_private_field_get(this, _editorContainer).style.display = 'none';
}
function blockNewSelections() {
    this.dropdownController.disableCheckboxes();
}
function unblockNewSelections() {
    this.dropdownController.enableCheckboxes();
}
function filterEntries(query, filterSelectedItems = _class_private_method_get(this, _getEditorSetting, getEditorSetting).call(this, 'filterSelectedItems') ?? true) {
    const filteredItems = _class_private_method_get(this, _getSource, getSource).call(this).filter((item)=>{
        const value = item?.value ?? item;
        if (!filterSelectedItems && _class_private_field_get(this, _selectedItems).has(item)) {
            return true;
        }
        if (this.cellProperties.filteringCaseSensitive) {
            return value.includes(query);
        }
        return value.toLowerCase().includes(query.toLowerCase());
    });
    this.dropdownController.fillDropdown(filteredItems, _class_private_field_get(this, _selectedItems).getItemsArray());
    this.dropdownController.updateDimensions(_class_private_method_get(this, _getAvailableSpace, getAvailableSpace).call(this), true);
}
function getAvailableSpace() {
    const cellRect = this.getEditedCellRect();
    const isVerticallyScrollableByWindow = this.hot.view.isVerticallyScrollableByWindow();
    const workspaceHeight = this.hot.view.getWorkspaceHeight();
    let spaceAbove = cellRect.top;
    if (isVerticallyScrollableByWindow) {
        const topOffset = this.hot.view.getTableOffset().top - this.hot.rootWindow.scrollY;
        spaceAbove = Math.max(spaceAbove + topOffset, 0);
    }
    const spaceBelow = workspaceHeight - spaceAbove - cellRect.height;
    return {
        spaceAbove,
        spaceBelow,
        cellHeight: cellRect.height
    };
}
function saveCurrentSelection() {
    const value = _class_private_field_get(this, _selectedItems).getItemsArray();
    this.saveValue([
        [
            value
        ]
    ]);
}
function addSelectedValue(selectedKey, selectedValue) {
    if (selectedKey) {
        _class_private_field_get(this, _selectedItems).add({
            key: selectedKey,
            value: selectedValue
        });
    } else {
        _class_private_field_get(this, _selectedItems).add(selectedValue);
    }
    _class_private_method_get(this, _saveCurrentSelection, saveCurrentSelection).call(this);
    this.refreshDimensions();
}
function removeSelectedValue(deselectedKey, deselectedValue) {
    if (deselectedKey) {
        _class_private_field_get(this, _selectedItems).remove({
            key: deselectedKey,
            value: deselectedValue
        });
    } else {
        _class_private_field_get(this, _selectedItems).remove(deselectedValue);
    }
    _class_private_method_get(this, _saveCurrentSelection, saveCurrentSelection).call(this);
    this.refreshDimensions();
}
function syncSelectedValues(valuesArray) {
    if (valuesArray.length > 0) {
        _class_private_field_set(this, _selectedItems, new SelectedItemsController(valuesArray));
    } else {
        _class_private_field_set(this, _selectedItems, new SelectedItemsController());
    }
}
function getEditorSetting(settingKey) {
    return this.cellProperties[settingKey];
}
function onAfterSetSourceDataAtCell(changes, source) {
    if (this.isOpened() && source === `${EDITOR_TYPE}-renderer` && parseInt(changes[0][0], 10) === this.cellProperties.visualRow && parseInt(changes[0][1], 10) === this.cellProperties.visualCol) {
        _class_private_method_get(this, _syncSelectedValues, syncSelectedValues).call(this, changes[0][3]);
        this.dropdownController.fillDropdown(_class_private_method_get(this, _getSource, getSource).call(this), _class_private_field_get(this, _selectedItems).getItemsArray());
        this.dropdownController.focusItem(0);
        if (_class_private_field_get(this, _selectedItems).getSize() >= this.cellProperties.maxSelections) {
            _class_private_method_get(this, _blockNewSelections, blockNewSelections).call(this);
        } else {
            _class_private_method_get(this, _unblockNewSelections, unblockNewSelections).call(this);
        }
    }
}
