"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 NestedRows () {
        return NestedRows;
    },
    get PLUGIN_KEY () {
        return PLUGIN_KEY;
    },
    get PLUGIN_PRIORITY () {
        return PLUGIN_PRIORITY;
    }
});
const _base = require("../base");
const _dataManager = /*#__PURE__*/ _interop_require_default(require("./data/dataManager"));
const _collapsing = /*#__PURE__*/ _interop_require_default(require("./ui/collapsing"));
const _headers = /*#__PURE__*/ _interop_require_default(require("./ui/headers"));
const _contextMenu = /*#__PURE__*/ _interop_require_default(require("./ui/contextMenu"));
const _isValidDataSource = require("./utils/isValidDataSource");
const _console = require("../../helpers/console");
const _translations = require("../../translations");
const _shortcutContexts = require("../../shortcutContexts");
const _rowMoveController = /*#__PURE__*/ _interop_require_default(require("./utils/rowMoveController"));
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 PLUGIN_KEY = 'nestedRows';
const PLUGIN_PRIORITY = 300;
const SHORTCUTS_GROUP = PLUGIN_KEY;
/* eslint-disable jsdoc/require-description-complete-sentence */ /**
 * Error message for the wrong data type error.
 */ const WRONG_DATA_TYPE_ERROR = 'The Nested Rows plugin requires an Array of Objects as a dataset to be' + ' provided. The plugin has been disabled.';
var /**
   * Allows skipping the render cycle if set as `true`.
   *
   * @type {boolean}
   */ _skipRender = /*#__PURE__*/ new WeakMap(), /**
   * Allows skipping the internal Core methods call if set as `true`.
   *
   * @type {boolean}
   */ _skipCoreAPIModifiers = /*#__PURE__*/ new WeakMap(), /**
   * State of the first render.
   *
   * @type {boolean}
   */ _isFirstRender = /*#__PURE__*/ new WeakMap(), /**
   * `beforeRowMove` hook callback.
   *
   * @param {Array} rows Array of visual row indexes to be moved.
   * @param {number} finalIndex Visual row index, being a start index for the moved rows. Points to where the elements
   *   will be placed after the moving action. To check the visualization of the final index, please take a look at
   *   [documentation](@/guides/rows/row-summary/row-summary.md).
   * @param {undefined|number} dropIndex Visual row index, being a drop index for the moved rows. Points to where we
   *   are going to drop the moved elements. To check visualization of drop index please take a look at
   *   [documentation](@/guides/rows/row-summary/row-summary.md).
   * @param {boolean} movePossible Indicates if it's possible to move rows to the desired position.
   * @fires Hooks#afterRowMove
   * @returns {boolean}
   */ _onBeforeRowMove = /*#__PURE__*/ new WeakSet(), /**
   * `beforeOnCellMousedown` hook callback.
   *
   * @param {MouseEvent} event Mousedown event.
   * @param {object} coords Cell coords.
   * @param {HTMLElement} TD Clicked cell.
   */ _onBeforeOnCellMouseDown = /*#__PURE__*/ new WeakSet(), /**
   * Provide custom source data filtering. It's handled by core method and replaces the native filtering.
   *
   * @param {number} index The index where the data filtering starts.
   * @param {number} amount An amount of rows which filtering applies to.
   * @param {number} physicalRows Physical row indexes.
   * @returns {Array}
   */ _onFilterData = /*#__PURE__*/ new WeakSet(), /**
   * `afterContextMenuDefaultOptions` hook callback.
   *
   * @param {object} defaultOptions The default context menu items order.
   * @returns {boolean}
   */ _onAfterContextMenuDefaultOptions = /*#__PURE__*/ new WeakSet(), /**
   * `afterGetRowHeader` hook callback.
   *
   * @param {number} row Row index.
   * @param {HTMLElement} TH Row header element.
   */ _onAfterGetRowHeader = /*#__PURE__*/ new WeakSet(), /**
   * `modifyRowHeaderWidth` hook callback.
   *
   * @param {number} rowHeaderWidth The initial row header width(s).
   * @returns {number}
   */ _onModifyRowHeaderWidth = /*#__PURE__*/ new WeakSet(), /**
   * `onAfterRemoveRow` hook callback.
   *
   * @param {number} index Removed row.
   * @param {number} amount Amount of removed rows.
   * @param {Array} logicRows An array of the removed physical rows.
   * @param {string} source Source of action.
   */ _onAfterRemoveRow = /*#__PURE__*/ new WeakSet(), /**
   * Callback for the `beforeRemoveRow` change list of removed physical indexes by reference. Removing parent node
   * has effect in removing children nodes.
   *
   * @param {number} index Visual index of starter row.
   * @param {number} amount Amount of rows to be removed.
   * @param {Array} physicalRows List of physical indexes.
   */ _onBeforeRemoveRow = /*#__PURE__*/ new WeakSet(), /**
   * `beforeAddChild` hook callback.
   */ _onBeforeAddChild = /*#__PURE__*/ new WeakSet(), /**
   * `afterAddChild` hook callback.
   *
   * @param {object} parent Parent element.
   * @param {object} element New child element.
   */ _onAfterAddChild = /*#__PURE__*/ new WeakSet(), /**
   * `beforeDetachChild` hook callback.
   */ _onBeforeDetachChild = /*#__PURE__*/ new WeakSet(), /**
   * `afterDetachChild` hook callback.
   *
   * @param {object} parent Parent element.
   * @param {object} element New child element.
   * @param {number} finalElementRowIndex The final row index of the detached element.
   */ _onAfterDetachChild = /*#__PURE__*/ new WeakSet(), /**
   * `afterCreateRow` hook callback.
   */ _onAfterCreateRow = /*#__PURE__*/ new WeakSet(), /**
   * `afterInit` hook callback.
   */ _onAfterInit = /*#__PURE__*/ new WeakSet(), /**
   * `afterRender` hook callback.
   * Recalculates table dimensions after the first render. Fixes the wtHider size being too small on initial display.
   */ _onAfterRender = /*#__PURE__*/ new WeakSet(), /**
   * `beforeViewRender` hook callback.
   *
   * @param {boolean} force Indicates if the render call was triggered by a change of settings or data.
   * @param {object} skipRender An object, holder for skipRender functionality.
   */ _onBeforeViewRender = /*#__PURE__*/ new WeakSet(), /**
   * `beforeLoadData` hook callback.
   *
   * @param {Array} data The source data.
   */ _onBeforeLoadData = /*#__PURE__*/ new WeakSet();
class NestedRows extends _base.BasePlugin {
    static get PLUGIN_KEY() {
        return PLUGIN_KEY;
    }
    static get PLUGIN_PRIORITY() {
        return PLUGIN_PRIORITY;
    }
    /**
   * Checks if the plugin is enabled in the handsontable settings. This method is executed in {@link Hooks#beforeInit}
   * hook and if it returns `true` then the {@link NestedRows#enablePlugin} method is called.
   *
   * @returns {boolean}
   */ isEnabled() {
        return !!this.hot.getSettings()[PLUGIN_KEY];
    }
    /**
   * Enables the plugin functionality for this Handsontable instance.
   */ enablePlugin() {
        if (this.enabled) {
            return;
        }
        this.collapsedRowsMap = this.hot.rowIndexMapper.registerMap('nestedRows', new _translations.TrimmingMap());
        _class_private_field_set(this, _isFirstRender, true);
        this.dataManager = new _dataManager.default(this, this.hot);
        this.collapsingUI = new _collapsing.default(this, this.hot);
        this.headersUI = new _headers.default(this, this.hot);
        this.contextMenuUI = new _contextMenu.default(this, this.hot);
        this.rowMoveController = new _rowMoveController.default(this);
        this.addHook('afterInit', (...args)=>_class_private_method_get(this, _onAfterInit, onAfterInit).call(this, ...args));
        this.addHook('afterRender', (...args)=>_class_private_method_get(this, _onAfterRender, onAfterRender).call(this, ...args));
        this.addHook('beforeViewRender', (...args)=>_class_private_method_get(this, _onBeforeViewRender, onBeforeViewRender).call(this, ...args));
        this.addHook('modifyRowData', (...args)=>this.onModifyRowData(...args));
        this.addHook('modifySourceLength', (...args)=>this.onModifySourceLength(...args));
        this.addHook('beforeDataSplice', (...args)=>this.onBeforeDataSplice(...args));
        this.addHook('filterData', (...args)=>_class_private_method_get(this, _onFilterData, onFilterData).call(this, ...args));
        this.addHook('afterContextMenuDefaultOptions', (...args)=>_class_private_method_get(this, _onAfterContextMenuDefaultOptions, onAfterContextMenuDefaultOptions).call(this, ...args));
        this.addHook('afterGetRowHeader', (...args)=>_class_private_method_get(this, _onAfterGetRowHeader, onAfterGetRowHeader).call(this, ...args));
        this.addHook('beforeOnCellMouseDown', (...args)=>_class_private_method_get(this, _onBeforeOnCellMouseDown, onBeforeOnCellMouseDown).call(this, ...args));
        this.addHook('beforeRemoveRow', (...args)=>_class_private_method_get(this, _onBeforeRemoveRow, onBeforeRemoveRow).call(this, ...args));
        this.addHook('afterRemoveRow', (...args)=>_class_private_method_get(this, _onAfterRemoveRow, onAfterRemoveRow).call(this, ...args));
        this.addHook('beforeAddChild', (...args)=>_class_private_method_get(this, _onBeforeAddChild, onBeforeAddChild).call(this, ...args));
        this.addHook('afterAddChild', (...args)=>_class_private_method_get(this, _onAfterAddChild, onAfterAddChild).call(this, ...args));
        this.addHook('beforeDetachChild', (...args)=>_class_private_method_get(this, _onBeforeDetachChild, onBeforeDetachChild).call(this, ...args));
        this.addHook('afterDetachChild', (...args)=>_class_private_method_get(this, _onAfterDetachChild, onAfterDetachChild).call(this, ...args));
        this.addHook('modifyRowHeaderWidth', (...args)=>_class_private_method_get(this, _onModifyRowHeaderWidth, onModifyRowHeaderWidth).call(this, ...args));
        this.addHook('afterCreateRow', (...args)=>_class_private_method_get(this, _onAfterCreateRow, onAfterCreateRow).call(this, ...args));
        this.addHook('beforeRowMove', (...args)=>_class_private_method_get(this, _onBeforeRowMove, onBeforeRowMove).call(this, ...args));
        this.addHook('beforeLoadData', (data)=>_class_private_method_get(this, _onBeforeLoadData, onBeforeLoadData).call(this, data));
        this.addHook('beforeUpdateData', (data)=>_class_private_method_get(this, _onBeforeLoadData, onBeforeLoadData).call(this, data));
        this.registerShortcuts();
        super.enablePlugin();
    }
    /**
   * Disables the plugin functionality for this Handsontable instance.
   */ disablePlugin() {
        this.hot.rowIndexMapper.unregisterMap('nestedRows');
        this.unregisterShortcuts();
        super.disablePlugin();
    }
    /**
   * Updates the plugin's state.
   *
   * This method is executed when [`updateSettings()`](@/api/core.md#updatesettings) is invoked with any of the following configuration options:
   *  - [`nestedRows`](@/api/options.md#nestedrows)
   */ updatePlugin() {
        this.disablePlugin();
        // We store a state of the data manager.
        const currentSourceData = this.dataManager.getData();
        this.enablePlugin();
        // After enabling plugin previously stored data is restored.
        this.dataManager.updateWithData(currentSourceData);
        super.updatePlugin();
    }
    /**
   * Register shortcuts responsible for toggling collapsible columns.
   *
   * @private
   */ registerShortcuts() {
        this.hot.getShortcutManager().getContext('grid').addShortcut({
            keys: [
                [
                    'Enter'
                ]
            ],
            callback: ()=>{
                const { highlight } = this.hot.getSelectedRangeActive();
                const row = this.collapsingUI.translateTrimmedRow(highlight.row);
                if (this.collapsingUI.areChildrenCollapsed(row)) {
                    this.collapsingUI.expandChildren(row);
                } else {
                    this.collapsingUI.collapseChildren(row);
                }
                // prevent default Enter behavior (move to the next row within a selection range)
                return false;
            },
            runOnlyIf: ()=>{
                const highlight = this.hot.getSelectedRangeActive()?.highlight;
                return highlight && this.hot.getSelectedRangeActive()?.isSingle() && this.hot.selection.isCellVisible(highlight) && highlight.col === -1 && highlight.row >= 0;
            },
            group: SHORTCUTS_GROUP,
            relativeToGroup: _shortcutContexts.EDITOR_EDIT_GROUP,
            position: 'before'
        });
    }
    /**
   * Unregister shortcuts responsible for toggling collapsible columns.
   *
   * @private
   */ unregisterShortcuts() {
        this.hot.getShortcutManager().getContext('grid').removeShortcutsByGroup(SHORTCUTS_GROUP);
    }
    /**
   * Enable the modify hook skipping flag - allows retrieving the data from Handsontable without this plugin's
   * modifications.
   *
   * @private
   */ disableCoreAPIModifiers() {
        _class_private_field_set(this, _skipCoreAPIModifiers, true);
    }
    /**
   * Disable the modify hook skipping flag.
   *
   * @private
   */ enableCoreAPIModifiers() {
        _class_private_field_set(this, _skipCoreAPIModifiers, false);
    }
    /**
   * The modifyRowData hook callback.
   *
   * @private
   * @param {number} row Visual row index.
   * @returns {boolean}
   */ onModifyRowData(row) {
        if (_class_private_field_get(this, _skipCoreAPIModifiers)) {
            return;
        }
        return this.dataManager.getDataObject(row);
    }
    /**
   * Modify the source data length to match the length of the nested structure.
   *
   * @private
   * @returns {number}
   */ onModifySourceLength() {
        if (_class_private_field_get(this, _skipCoreAPIModifiers)) {
            return;
        }
        return this.dataManager.countAllRows();
    }
    /**
   * @private
   * @param {number} index The index where the data was spliced.
   * @param {number} amount An amount of items to remove.
   * @param {object} element An element to add.
   * @returns {boolean}
   */ onBeforeDataSplice(index, amount, element) {
        if (_class_private_field_get(this, _skipCoreAPIModifiers) || this.dataManager.isRowHighestLevel(index)) {
            return true;
        }
        this.dataManager.spliceData(index, amount, element);
        return false;
    }
    /**
   * Destroys the plugin instance.
   */ destroy() {
        super.destroy();
    }
    constructor(...args){
        super(...args), _class_private_method_init(this, _onBeforeRowMove), _class_private_method_init(this, _onBeforeOnCellMouseDown), _class_private_method_init(this, _onFilterData), _class_private_method_init(this, _onAfterContextMenuDefaultOptions), _class_private_method_init(this, _onAfterGetRowHeader), _class_private_method_init(this, _onModifyRowHeaderWidth), _class_private_method_init(this, _onAfterRemoveRow), _class_private_method_init(this, _onBeforeRemoveRow), _class_private_method_init(this, _onBeforeAddChild), _class_private_method_init(this, _onAfterAddChild), _class_private_method_init(this, _onBeforeDetachChild), _class_private_method_init(this, _onAfterDetachChild), _class_private_method_init(this, _onAfterCreateRow), _class_private_method_init(this, _onAfterInit), _class_private_method_init(this, _onAfterRender), _class_private_method_init(this, _onBeforeViewRender), _class_private_method_init(this, _onBeforeLoadData), _class_private_field_init(this, _skipRender, {
            writable: true,
            value: void 0
        }), _class_private_field_init(this, _skipCoreAPIModifiers, {
            writable: true,
            value: void 0
        }), _class_private_field_init(this, _isFirstRender, {
            writable: true,
            value: void 0
        }), /**
   * Reference to the DataManager instance.
   *
   * @private
   * @type {object}
   */ this.dataManager = null, /**
   * Reference to the HeadersUI instance.
   *
   * @private
   * @type {object}
   */ this.headersUI = null, /**
   * Map of skipped rows by plugin.
   *
   * @private
   * @type {null|TrimmingMap}
   */ this.collapsedRowsMap = null, _class_private_field_set(this, _skipRender, false), _class_private_field_set(this, _skipCoreAPIModifiers, false), _class_private_field_set(this, _isFirstRender, true);
    }
}
function onBeforeRowMove(rows, finalIndex, dropIndex, movePossible) {
    return this.rowMoveController.onBeforeRowMove(rows, finalIndex, dropIndex, movePossible);
}
function onBeforeOnCellMouseDown(event, coords, TD) {
    this.collapsingUI.toggleState(event, coords, TD);
}
function onFilterData(index, amount, physicalRows) {
    this.collapsingUI.collapsedRowsStash.stash();
    this.collapsingUI.collapsedRowsStash.trimStash(physicalRows[0], amount);
    this.collapsingUI.collapsedRowsStash.shiftStash(physicalRows[0], null, -1 * amount);
    this.dataManager.filterData(index, amount, physicalRows);
    _class_private_field_set(this, _skipRender, true);
    return this.dataManager.getData().slice(); // Data contains reference sometimes.
}
function onAfterContextMenuDefaultOptions(defaultOptions) {
    return this.contextMenuUI.appendOptions(defaultOptions);
}
function onAfterGetRowHeader(row, TH) {
    this.headersUI.appendLevelIndicators(row, TH);
}
function onModifyRowHeaderWidth(rowHeaderWidth) {
    return Math.max(this.headersUI.rowHeaderWidthCache, rowHeaderWidth);
}
function onAfterRemoveRow(index, amount, logicRows, source) {
    if (source === this.pluginName) {
        return;
    }
    this.hot._registerTimeout(()=>{
        _class_private_field_set(this, _skipRender, false);
        this.headersUI.updateRowHeaderWidth();
        this.collapsingUI.collapsedRowsStash.applyStash();
    });
}
function onBeforeRemoveRow(index, amount, physicalRows) {
    const modifiedPhysicalRows = Array.from(physicalRows.reduce((removedRows, physicalIndex)=>{
        if (this.dataManager.isParent(physicalIndex)) {
            const children = this.dataManager.getDataObject(physicalIndex).__children;
            // Preserve a parent in the list of removed rows.
            removedRows.add(physicalIndex);
            if (Array.isArray(children)) {
                // Add a children to the list of removed rows.
                children.forEach((child)=>removedRows.add(this.dataManager.getRowIndex(child)));
            }
            return removedRows;
        }
        // Don't modify list of removed rows when already checked element isn't a parent.
        return removedRows.add(physicalIndex);
    }, new Set()));
    // Modifying hook's argument by the reference.
    physicalRows.length = 0;
    physicalRows.push(...modifiedPhysicalRows);
}
function onBeforeAddChild() {
    this.collapsingUI.collapsedRowsStash.stash();
}
function onAfterAddChild(parent, element) {
    this.collapsingUI.collapsedRowsStash.shiftStash(this.dataManager.getRowIndex(element));
    this.collapsingUI.collapsedRowsStash.applyStash();
    this.headersUI.updateRowHeaderWidth();
}
function onBeforeDetachChild() {
    this.collapsingUI.collapsedRowsStash.stash();
}
function onAfterDetachChild(parent, element, finalElementRowIndex) {
    this.collapsingUI.collapsedRowsStash.shiftStash(finalElementRowIndex, null, -1);
    this.collapsingUI.collapsedRowsStash.applyStash();
    this.headersUI.updateRowHeaderWidth();
}
function onAfterCreateRow() {
    this.dataManager.rewriteCache();
}
function onAfterInit() {
    this.headersUI.updateRowHeaderWidth();
}
function onAfterRender() {
    if (_class_private_field_get(this, _isFirstRender) && this.hot.view) {
        _class_private_field_set(this, _isFirstRender, false);
        this.hot.rootWindow.requestAnimationFrame(()=>{
            if (this.hot && this.hot.view && !this.hot.isDestroyed) {
                this.hot.view.adjustElementsSize(true);
            }
        });
    }
}
function onBeforeViewRender(force, skipRender) {
    if (_class_private_field_get(this, _skipRender)) {
        skipRender.skipRender = true;
    }
}
function onBeforeLoadData(data) {
    if (!(0, _isValidDataSource.isValidDataSource)(data)) {
        (0, _console.error)(WRONG_DATA_TYPE_ERROR);
        this.hot.getSettings()[PLUGIN_KEY] = false;
        this.disablePlugin();
        return;
    }
    this.dataManager.setData(data);
    this.dataManager.rewriteCache();
}
