import "core-js/modules/es.array.push.js";
import "core-js/modules/es.error.cause.js";
function _classPrivateMethodInitSpec(obj, privateSet) { _checkPrivateRedeclaration(obj, privateSet); privateSet.add(obj); }
function _classPrivateFieldInitSpec(obj, privateMap, value) { _checkPrivateRedeclaration(obj, privateMap); privateMap.set(obj, value); }
function _checkPrivateRedeclaration(obj, privateCollection) { if (privateCollection.has(obj)) { throw new TypeError("Cannot initialize the same private elements twice on an object"); } }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
function _classPrivateFieldGet(receiver, privateMap) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "get"); return _classApplyDescriptorGet(receiver, descriptor); }
function _classApplyDescriptorGet(receiver, descriptor) { if (descriptor.get) { return descriptor.get.call(receiver); } return descriptor.value; }
function _classPrivateFieldSet(receiver, privateMap, value) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "set"); _classApplyDescriptorSet(receiver, descriptor, value); return value; }
function _classExtractFieldDescriptor(receiver, privateMap, action) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to " + action + " private field on non-instance"); } return privateMap.get(receiver); }
function _classApplyDescriptorSet(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 _classPrivateMethodGet(receiver, privateSet, fn) { if (!privateSet.has(receiver)) { throw new TypeError("attempted to get private field on non-instance"); } return fn; }
import { BasePlugin } from "../base/index.mjs";
import DataManager from "./data/dataManager.mjs";
import CollapsingUI from "./ui/collapsing.mjs";
import HeadersUI from "./ui/headers.mjs";
import ContextMenuUI from "./ui/contextMenu.mjs";
import { error } from "../../helpers/console.mjs";
import { isArrayOfObjects } from "../../helpers/data.mjs";
import { TrimmingMap } from "../../translations/index.mjs";
import RowMoveController from "./utils/rowMoveController.mjs";
export const PLUGIN_KEY = 'nestedRows';
export 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.';

/**
 * @plugin NestedRows
 * @class NestedRows
 *
 * @description
 * Plugin responsible for displaying and operating on data sources with nested structures.
 */
var _skipRender = /*#__PURE__*/new WeakMap();
var _skipCoreAPIModifiers = /*#__PURE__*/new WeakMap();
var _onBeforeRowMove = /*#__PURE__*/new WeakSet();
var _onBeforeOnCellMouseDown = /*#__PURE__*/new WeakSet();
var _onFilterData = /*#__PURE__*/new WeakSet();
var _onAfterContextMenuDefaultOptions = /*#__PURE__*/new WeakSet();
var _onAfterGetRowHeader = /*#__PURE__*/new WeakSet();
var _onModifyRowHeaderWidth = /*#__PURE__*/new WeakSet();
var _onAfterRemoveRow = /*#__PURE__*/new WeakSet();
var _onBeforeRemoveRow = /*#__PURE__*/new WeakSet();
var _onBeforeAddChild = /*#__PURE__*/new WeakSet();
var _onAfterAddChild = /*#__PURE__*/new WeakSet();
var _onBeforeDetachChild = /*#__PURE__*/new WeakSet();
var _onAfterDetachChild = /*#__PURE__*/new WeakSet();
var _onAfterCreateRow = /*#__PURE__*/new WeakSet();
var _onAfterInit = /*#__PURE__*/new WeakSet();
var _onBeforeViewRender = /*#__PURE__*/new WeakSet();
var _onBeforeLoadData = /*#__PURE__*/new WeakSet();
export class NestedRows extends BasePlugin {
  constructor() {
    super(...arguments);
    /**
     * `beforeLoadData` hook callback.
     *
     * @param {Array} data The source data.
     */
    _classPrivateMethodInitSpec(this, _onBeforeLoadData);
    /**
     * `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.
     */
    _classPrivateMethodInitSpec(this, _onBeforeViewRender);
    /**
     * `afterInit` hook callback.
     */
    _classPrivateMethodInitSpec(this, _onAfterInit);
    /**
     * `afterCreateRow` hook callback.
     */
    _classPrivateMethodInitSpec(this, _onAfterCreateRow);
    /**
     * `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.
     */
    _classPrivateMethodInitSpec(this, _onAfterDetachChild);
    /**
     * `beforeDetachChild` hook callback.
     */
    _classPrivateMethodInitSpec(this, _onBeforeDetachChild);
    /**
     * `afterAddChild` hook callback.
     *
     * @param {object} parent Parent element.
     * @param {object} element New child element.
     */
    _classPrivateMethodInitSpec(this, _onAfterAddChild);
    /**
     * `beforeAddChild` hook callback.
     */
    _classPrivateMethodInitSpec(this, _onBeforeAddChild);
    /**
     * 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.
     */
    _classPrivateMethodInitSpec(this, _onBeforeRemoveRow);
    /**
     * `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.
     */
    _classPrivateMethodInitSpec(this, _onAfterRemoveRow);
    /**
     * `modifyRowHeaderWidth` hook callback.
     *
     * @param {number} rowHeaderWidth The initial row header width(s).
     * @returns {number}
     */
    _classPrivateMethodInitSpec(this, _onModifyRowHeaderWidth);
    /**
     * `afterGetRowHeader` hook callback.
     *
     * @param {number} row Row index.
     * @param {HTMLElement} TH Row header element.
     */
    _classPrivateMethodInitSpec(this, _onAfterGetRowHeader);
    /**
     * `afterContextMenuDefaultOptions` hook callback.
     *
     * @param {object} defaultOptions The default context menu items order.
     * @returns {boolean}
     */
    _classPrivateMethodInitSpec(this, _onAfterContextMenuDefaultOptions);
    /**
     * 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}
     */
    _classPrivateMethodInitSpec(this, _onFilterData);
    /**
     * `beforeOnCellMousedown` hook callback.
     *
     * @param {MouseEvent} event Mousedown event.
     * @param {object} coords Cell coords.
     * @param {HTMLElement} TD Clicked cell.
     */
    _classPrivateMethodInitSpec(this, _onBeforeOnCellMouseDown);
    /**
     * `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.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.md).
     * @param {boolean} movePossible Indicates if it's possible to move rows to the desired position.
     * @fires Hooks#afterRowMove
     * @returns {boolean}
     */
    _classPrivateMethodInitSpec(this, _onBeforeRowMove);
    /**
     * Reference to the DataManager instance.
     *
     * @private
     * @type {object}
     */
    _defineProperty(this, "dataManager", null);
    /**
     * Reference to the HeadersUI instance.
     *
     * @private
     * @type {object}
     */
    _defineProperty(this, "headersUI", null);
    /**
     * Map of skipped rows by plugin.
     *
     * @private
     * @type {null|TrimmingMap}
     */
    _defineProperty(this, "collapsedRowsMap", null);
    /**
     * Allows skipping the render cycle if set as `true`.
     *
     * @type {boolean}
     */
    _classPrivateFieldInitSpec(this, _skipRender, {
      writable: true,
      value: false
    });
    /**
     * Allows skipping the internal Core methods call if set as `true`.
     *
     * @type {boolean}
     */
    _classPrivateFieldInitSpec(this, _skipCoreAPIModifiers, {
      writable: true,
      value: false
    });
  }
  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() {
    var _this = this;
    if (this.enabled) {
      return;
    }
    this.collapsedRowsMap = this.hot.rowIndexMapper.registerMap('nestedRows', new TrimmingMap());
    this.dataManager = new DataManager(this, this.hot);
    this.collapsingUI = new CollapsingUI(this, this.hot);
    this.headersUI = new HeadersUI(this, this.hot);
    this.contextMenuUI = new ContextMenuUI(this, this.hot);
    this.rowMoveController = new RowMoveController(this);
    this.addHook('afterInit', function () {
      for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
        args[_key] = arguments[_key];
      }
      return _classPrivateMethodGet(_this, _onAfterInit, _onAfterInit2).call(_this, ...args);
    });
    this.addHook('beforeViewRender', function () {
      for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
        args[_key2] = arguments[_key2];
      }
      return _classPrivateMethodGet(_this, _onBeforeViewRender, _onBeforeViewRender2).call(_this, ...args);
    });
    this.addHook('modifyRowData', function () {
      return _this.onModifyRowData(...arguments);
    });
    this.addHook('modifySourceLength', function () {
      return _this.onModifySourceLength(...arguments);
    });
    this.addHook('beforeDataSplice', function () {
      return _this.onBeforeDataSplice(...arguments);
    });
    this.addHook('filterData', function () {
      for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
        args[_key3] = arguments[_key3];
      }
      return _classPrivateMethodGet(_this, _onFilterData, _onFilterData2).call(_this, ...args);
    });
    this.addHook('afterContextMenuDefaultOptions', function () {
      for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
        args[_key4] = arguments[_key4];
      }
      return _classPrivateMethodGet(_this, _onAfterContextMenuDefaultOptions, _onAfterContextMenuDefaultOptions2).call(_this, ...args);
    });
    this.addHook('afterGetRowHeader', function () {
      for (var _len5 = arguments.length, args = new Array(_len5), _key5 = 0; _key5 < _len5; _key5++) {
        args[_key5] = arguments[_key5];
      }
      return _classPrivateMethodGet(_this, _onAfterGetRowHeader, _onAfterGetRowHeader2).call(_this, ...args);
    });
    this.addHook('beforeOnCellMouseDown', function () {
      for (var _len6 = arguments.length, args = new Array(_len6), _key6 = 0; _key6 < _len6; _key6++) {
        args[_key6] = arguments[_key6];
      }
      return _classPrivateMethodGet(_this, _onBeforeOnCellMouseDown, _onBeforeOnCellMouseDown2).call(_this, ...args);
    });
    this.addHook('beforeRemoveRow', function () {
      for (var _len7 = arguments.length, args = new Array(_len7), _key7 = 0; _key7 < _len7; _key7++) {
        args[_key7] = arguments[_key7];
      }
      return _classPrivateMethodGet(_this, _onBeforeRemoveRow, _onBeforeRemoveRow2).call(_this, ...args);
    });
    this.addHook('afterRemoveRow', function () {
      for (var _len8 = arguments.length, args = new Array(_len8), _key8 = 0; _key8 < _len8; _key8++) {
        args[_key8] = arguments[_key8];
      }
      return _classPrivateMethodGet(_this, _onAfterRemoveRow, _onAfterRemoveRow2).call(_this, ...args);
    });
    this.addHook('beforeAddChild', function () {
      for (var _len9 = arguments.length, args = new Array(_len9), _key9 = 0; _key9 < _len9; _key9++) {
        args[_key9] = arguments[_key9];
      }
      return _classPrivateMethodGet(_this, _onBeforeAddChild, _onBeforeAddChild2).call(_this, ...args);
    });
    this.addHook('afterAddChild', function () {
      for (var _len10 = arguments.length, args = new Array(_len10), _key10 = 0; _key10 < _len10; _key10++) {
        args[_key10] = arguments[_key10];
      }
      return _classPrivateMethodGet(_this, _onAfterAddChild, _onAfterAddChild2).call(_this, ...args);
    });
    this.addHook('beforeDetachChild', function () {
      for (var _len11 = arguments.length, args = new Array(_len11), _key11 = 0; _key11 < _len11; _key11++) {
        args[_key11] = arguments[_key11];
      }
      return _classPrivateMethodGet(_this, _onBeforeDetachChild, _onBeforeDetachChild2).call(_this, ...args);
    });
    this.addHook('afterDetachChild', function () {
      for (var _len12 = arguments.length, args = new Array(_len12), _key12 = 0; _key12 < _len12; _key12++) {
        args[_key12] = arguments[_key12];
      }
      return _classPrivateMethodGet(_this, _onAfterDetachChild, _onAfterDetachChild2).call(_this, ...args);
    });
    this.addHook('modifyRowHeaderWidth', function () {
      for (var _len13 = arguments.length, args = new Array(_len13), _key13 = 0; _key13 < _len13; _key13++) {
        args[_key13] = arguments[_key13];
      }
      return _classPrivateMethodGet(_this, _onModifyRowHeaderWidth, _onModifyRowHeaderWidth2).call(_this, ...args);
    });
    this.addHook('afterCreateRow', function () {
      for (var _len14 = arguments.length, args = new Array(_len14), _key14 = 0; _key14 < _len14; _key14++) {
        args[_key14] = arguments[_key14];
      }
      return _classPrivateMethodGet(_this, _onAfterCreateRow, _onAfterCreateRow2).call(_this, ...args);
    });
    this.addHook('beforeRowMove', function () {
      for (var _len15 = arguments.length, args = new Array(_len15), _key15 = 0; _key15 < _len15; _key15++) {
        args[_key15] = arguments[_key15];
      }
      return _classPrivateMethodGet(_this, _onBeforeRowMove, _onBeforeRowMove2).call(_this, ...args);
    });
    this.addHook('beforeLoadData', data => _classPrivateMethodGet(this, _onBeforeLoadData, _onBeforeLoadData2).call(this, data));
    this.addHook('beforeUpdateData', data => _classPrivateMethodGet(this, _onBeforeLoadData, _onBeforeLoadData2).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.getSelectedRangeLast();
        if (highlight.col === -1 && highlight.row >= 0) {
          const row = this.collapsingUI.translateTrimmedRow(highlight.row);
          if (this.collapsingUI.areChildrenCollapsed(row)) {
            this.collapsingUI.expandChildren(row);
          } else {
            this.collapsingUI.collapseChildren(row);
          }
        }
      },
      runOnlyIf: () => {
        var _this$hot$getSelected;
        const highlight = (_this$hot$getSelected = this.hot.getSelectedRangeLast()) === null || _this$hot$getSelected === void 0 ? void 0 : _this$hot$getSelected.highlight;
        return highlight && this.hot.selection.isCellVisible(highlight) && highlight.isHeader();
      },
      group: SHORTCUTS_GROUP
    });
  }

  /**
   * 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() {
    _classPrivateFieldSet(this, _skipCoreAPIModifiers, true);
  }

  /**
   * Disable the modify hook skipping flag.
   *
   * @private
   */
  enableCoreAPIModifiers() {
    _classPrivateFieldSet(this, _skipCoreAPIModifiers, false);
  }
  /**
   * The modifyRowData hook callback.
   *
   * @param {number} row Visual row index.
   * @returns {boolean}
   */
  onModifyRowData(row) {
    if (_classPrivateFieldGet(this, _skipCoreAPIModifiers)) {
      return;
    }
    return this.dataManager.getDataObject(row);
  }

  /**
   * Modify the source data length to match the length of the nested structure.
   *
   * @returns {number}
   */
  onModifySourceLength() {
    if (_classPrivateFieldGet(this, _skipCoreAPIModifiers)) {
      return;
    }
    return this.dataManager.countAllRows();
  }

  /**
   * @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 (_classPrivateFieldGet(this, _skipCoreAPIModifiers) || this.dataManager.isRowHighestLevel(index)) {
      return true;
    }
    this.dataManager.spliceData(index, amount, element);
    return false;
  }
  /**
   * Destroys the plugin instance.
   */
  destroy() {
    super.destroy();
  }
}
function _onBeforeRowMove2(rows, finalIndex, dropIndex, movePossible) {
  return this.rowMoveController.onBeforeRowMove(rows, finalIndex, dropIndex, movePossible);
}
function _onBeforeOnCellMouseDown2(event, coords, TD) {
  this.collapsingUI.toggleState(event, coords, TD);
}
function _onFilterData2(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);
  _classPrivateFieldSet(this, _skipRender, true);
  return this.dataManager.getData().slice(); // Data contains reference sometimes.
}
function _onAfterContextMenuDefaultOptions2(defaultOptions) {
  return this.contextMenuUI.appendOptions(defaultOptions);
}
function _onAfterGetRowHeader2(row, TH) {
  this.headersUI.appendLevelIndicators(row, TH);
}
function _onModifyRowHeaderWidth2(rowHeaderWidth) {
  return Math.max(this.headersUI.rowHeaderWidthCache, rowHeaderWidth);
}
function _onAfterRemoveRow2(index, amount, logicRows, source) {
  if (source === this.pluginName) {
    return;
  }
  this.hot._registerTimeout(() => {
    _classPrivateFieldSet(this, _skipRender, false);
    this.headersUI.updateRowHeaderWidth();
    this.collapsingUI.collapsedRowsStash.applyStash();
  });
}
function _onBeforeRemoveRow2(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 _onBeforeAddChild2() {
  this.collapsingUI.collapsedRowsStash.stash();
}
function _onAfterAddChild2(parent, element) {
  this.collapsingUI.collapsedRowsStash.shiftStash(this.dataManager.getRowIndex(element));
  this.collapsingUI.collapsedRowsStash.applyStash();
  this.headersUI.updateRowHeaderWidth();
}
function _onBeforeDetachChild2() {
  this.collapsingUI.collapsedRowsStash.stash();
}
function _onAfterDetachChild2(parent, element, finalElementRowIndex) {
  this.collapsingUI.collapsedRowsStash.shiftStash(finalElementRowIndex, null, -1);
  this.collapsingUI.collapsedRowsStash.applyStash();
  this.headersUI.updateRowHeaderWidth();
}
function _onAfterCreateRow2() {
  this.dataManager.rewriteCache();
}
function _onAfterInit2() {
  this.headersUI.updateRowHeaderWidth();
}
function _onBeforeViewRender2(force, skipRender) {
  if (_classPrivateFieldGet(this, _skipRender)) {
    skipRender.skipRender = true;
  }
}
function _onBeforeLoadData2(data) {
  if (!isArrayOfObjects(data)) {
    error(WRONG_DATA_TYPE_ERROR);
    this.hot.getSettings()[PLUGIN_KEY] = false;
    this.disablePlugin();
    return;
  }
  this.dataManager.setData(data);
  this.dataManager.rewriteCache();
}