"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
exports.__esModule = true;
exports.CellInteractionState = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _mobx = require("mobx");
var _utils = require("./utils");
class CellInteractionState {
  constructor(commitHandler, parent) {
    (0, _defineProperty2.default)(this, "focusedCell", null);
    (0, _defineProperty2.default)(this, "editingCell", null);
    (0, _defineProperty2.default)(this, "editingValue", undefined);
    (0, _defineProperty2.default)(this, "editTrigger", null);
    (0, _defineProperty2.default)(this, "selectedCells", new Set());
    (0, _defineProperty2.default)(this, "selectionAnchor", null);
    (0, _defineProperty2.default)(this, "focusedCellElement", null);
    /** True while the user is performing a mouse sweep selection. */
    (0, _defineProperty2.default)(this, "isSelecting", false);
    (0, _defineProperty2.default)(this, "_parent", void 0);
    (0, _defineProperty2.default)(this, "_commitHandler", void 0);
    this._commitHandler = commitHandler;
    this._parent = parent;
    (0, _mobx.makeObservable)(this, {
      focusedCell: _mobx.observable.ref,
      editingCell: _mobx.observable.ref,
      editingValue: _mobx.observable.ref,
      editTrigger: _mobx.observable.ref,
      selectedCells: _mobx.observable,
      selectionAnchor: _mobx.observable.ref,
      isSelecting: _mobx.observable.ref,
      isEditing: _mobx.computed,
      selectedCellsList: _mobx.computed,
      focusCell: _mobx.action,
      clearFocus: _mobx.action,
      startEdit: _mobx.action,
      commitEdit: _mobx.action,
      cancelEdit: _mobx.action,
      setEditingValue: _mobx.action,
      selectRange: _mobx.action,
      selectAll: _mobx.action,
      moveFocus: _mobx.action,
      moveToNextCell: _mobx.action,
      moveToPreviousCell: _mobx.action,
      startSweep: _mobx.action,
      sweepTo: _mobx.action,
      stopSweep: _mobx.action
    });
  }
  get isEditing() {
    return this.editingCell !== null;
  }
  focusCell(rowKey, columnId) {
    if (this.editingCell) {
      this.commitEdit();
    }
    this.focusedCell = {
      rowKey,
      columnId
    };
    this.selectedCells.clear();
    this.selectedCells.add((0, _utils.cellKey)(rowKey, columnId));
    this.selectionAnchor = {
      rowKey,
      columnId
    };
  }
  clearFocus() {
    this.focusedCell = null;
    this.selectedCells.clear();
    this.selectionAnchor = null;
    this.cancelEdit();
  }
  startEdit(rowKey, columnId, trigger = 'enter') {
    var _this$focusedCell, _this$focusedCell2;
    const rk = rowKey ?? ((_this$focusedCell = this.focusedCell) == null ? void 0 : _this$focusedCell.rowKey);
    const cid = columnId ?? ((_this$focusedCell2 = this.focusedCell) == null ? void 0 : _this$focusedCell2.columnId);
    if (rk != null && cid != null) {
      this.editingValue = undefined;
      this.editTrigger = trigger;
      this.editingCell = {
        rowKey: rk,
        columnId: cid
      };
      this.focusedCell = {
        rowKey: rk,
        columnId: cid
      };
      // Collapse any multi-cell range selection down to the editing cell.
      // Without this, range-selected cells remain visually selected alongside
      // the editing cell, producing an invalid "two cells selected" state.
      this.selectedCells.clear();
      this.selectedCells.add((0, _utils.cellKey)(rk, cid));
      this.selectionAnchor = {
        rowKey: rk,
        columnId: cid
      };
    }
  }
  setEditingValue(value) {
    this.editingValue = value;
  }
  commitEdit() {
    try {
      if (this.editingCell) {
        this._commitHandler(this.editingValue, this.editingCell);
      }
    } finally {
      this.editingValue = undefined;
      this.editTrigger = null;
      this.editingCell = null;
    }
  }
  cancelEdit() {
    this.editingValue = undefined;
    this.editTrigger = null;
    this.editingCell = null;
  }
  selectRange(from, to) {
    const fromPos = this._parent._toIndices(from);
    const toPos = this._parent._toIndices(to);
    if (!fromPos || !toPos) {
      return;
    }
    const {
      keyedItems,
      editableColumns
    } = this._parent;
    const minRow = Math.min(fromPos.row, toPos.row);
    const maxRow = Math.max(fromPos.row, toPos.row);
    const minCol = Math.min(fromPos.col, toPos.col);
    const maxCol = Math.max(fromPos.col, toPos.col);
    const newKeys = new Set();
    for (let r = minRow; r <= maxRow; r++) {
      for (let c = minCol; c <= maxCol; c++) {
        newKeys.add((0, _utils.cellKey)(keyedItems[r].key, editableColumns[c].id));
      }
    }
    this._diffSelection(newKeys);
    this.focusedCell = to;
  }
  selectAll() {
    const {
      keyedItems,
      editableColumns
    } = this._parent;
    const newKeys = new Set();
    for (const ki of keyedItems) {
      for (const col of editableColumns) {
        newKeys.add((0, _utils.cellKey)(ki.key, col.id));
      }
    }
    this._diffSelection(newKeys);
    if (keyedItems.length > 0 && editableColumns.length > 0) {
      this.selectionAnchor = this._parent._toPosition(0, 0);
      this.focusedCell = this._parent._toPosition(keyedItems.length - 1, editableColumns.length - 1);
    }
  }
  moveFocus(direction, extend = false) {
    const pos = this.focusedCell && this._parent._toIndices(this.focusedCell);
    if (!pos) {
      return;
    }
    const {
      keyedItems,
      editableColumns
    } = this._parent;
    let {
      row,
      col
    } = pos;
    switch (direction) {
      case 'up':
        row = Math.max(0, row - 1);
        break;
      case 'down':
        row = Math.min(keyedItems.length - 1, row + 1);
        break;
      case 'left':
        col = Math.max(0, col - 1);
        break;
      case 'right':
        col = Math.min(editableColumns.length - 1, col + 1);
        break;
    }
    const target = this._parent._toPosition(row, col);
    if (extend && this.selectionAnchor) {
      this.selectRange(this.selectionAnchor, target);
    } else {
      this.focusCell(target.rowKey, target.columnId);
    }
  }
  moveToNextCell() {
    const pos = this.focusedCell && this._parent._toIndices(this.focusedCell);
    if (!pos) {
      return;
    }
    const {
      keyedItems,
      editableColumns
    } = this._parent;
    let {
      row,
      col
    } = pos;
    col++;
    if (col >= editableColumns.length) {
      col = 0;
      row++;
      if (row >= keyedItems.length) {
        row = keyedItems.length - 1;
        col = editableColumns.length - 1;
      }
    }
    this.focusCell(keyedItems[row].key, editableColumns[col].id);
  }
  moveToPreviousCell() {
    const pos = this.focusedCell && this._parent._toIndices(this.focusedCell);
    if (!pos) {
      return;
    }
    const {
      keyedItems,
      editableColumns
    } = this._parent;
    let {
      row,
      col
    } = pos;
    col--;
    if (col < 0) {
      col = editableColumns.length - 1;
      row--;
      if (row < 0) {
        row = 0;
        col = 0;
      }
    }
    this.focusCell(keyedItems[row].key, editableColumns[col].id);
  }
  startSweep(rowKey, columnId) {
    this.isSelecting = true;
    this.focusCell(rowKey, columnId);
  }
  sweepTo(rowKey, columnId) {
    if (!this.isSelecting || !this.selectionAnchor) {
      return;
    }
    this.selectRange(this.selectionAnchor, {
      rowKey,
      columnId
    });
  }
  stopSweep() {
    this.isSelecting = false;
  }
  _diffSelection(newKeys) {
    for (const key of this.selectedCells) {
      if (!newKeys.has(key)) {
        this.selectedCells.delete(key);
      }
    }
    for (const key of newKeys) {
      if (!this.selectedCells.has(key)) {
        this.selectedCells.add(key);
      }
    }
  }
  isCellFocused(rowKey, columnId) {
    var _this$focusedCell3, _this$focusedCell4;
    return ((_this$focusedCell3 = this.focusedCell) == null ? void 0 : _this$focusedCell3.rowKey) === rowKey && ((_this$focusedCell4 = this.focusedCell) == null ? void 0 : _this$focusedCell4.columnId) === columnId;
  }
  isCellEditing(rowKey, columnId) {
    var _this$editingCell, _this$editingCell2;
    return ((_this$editingCell = this.editingCell) == null ? void 0 : _this$editingCell.rowKey) === rowKey && ((_this$editingCell2 = this.editingCell) == null ? void 0 : _this$editingCell2.columnId) === columnId;
  }
  isCellSelected(rowKey, columnId) {
    return this.selectedCells.has((0, _utils.cellKey)(rowKey, columnId));
  }
  get selectedCellsList() {
    const result = [];
    for (const key of this.selectedCells) {
      const pos = (0, _utils.parseCellKey)(key);
      if (pos) {
        result.push(pos);
      }
    }
    return result;
  }
  scrollFocusedCellIntoView() {
    if (typeof requestAnimationFrame === 'undefined') {
      return;
    }
    requestAnimationFrame(() => {
      const cellEl = this.focusedCellElement;
      if (!cellEl) {
        return;
      }
      cellEl.scrollIntoView({
        block: 'nearest',
        inline: 'nearest'
      });
      this._adjustScrollForStickyElements(cellEl);
    });
  }

  /**
   * After scrollIntoView, the cell may be hidden behind sticky columns
   * or the sticky header. If so, scroll a bit more to fully reveal it.
   */
  _adjustScrollForStickyElements(cellEl) {
    const td = cellEl.parentElement;
    if (!td) {
      return;
    }

    // Horizontal: check if cell is behind a sticky column.
    // Sticky columns stay in place while other columns scroll, so their
    // bounding rect's right edge can overlap the focused cell's left edge.
    const row = td.parentElement;
    const cellLeft = td.getBoundingClientRect().left;
    if (row) {
      let maxPrevRight = 0;
      for (const child of Array.from(row.children)) {
        if (child === td) {
          break;
        }
        const right = child.getBoundingClientRect().right;
        if (right > maxPrevRight) {
          maxPrevRight = right;
        }
      }
      if (cellLeft < maxPrevRight) {
        var _cellEl$closest;
        const scrollParent = (_cellEl$closest = cellEl.closest('table')) == null ? void 0 : _cellEl$closest.parentElement;
        if (scrollParent) {
          scrollParent.scrollLeft -= maxPrevRight - cellLeft;
        }
      }
    }

    // Vertical: same pattern — check if cell is behind the sticky header
    // or below the visible area of the page-level scroll container.
    const headerEl = this._parent.tableState.headerElement;
    const scrollEl = this._parent.collection.scrollableContent;
    if (scrollEl) {
      const cellRect = td.getBoundingClientRect();
      const scrollRect = scrollEl.getBoundingClientRect();
      const effectiveTop = headerEl ? headerEl.getBoundingClientRect().bottom : scrollRect.top;
      if (cellRect.top < effectiveTop) {
        scrollEl.scrollTop -= effectiveTop - cellRect.top + 2;
      } else if (cellRect.bottom > scrollRect.bottom) {
        scrollEl.scrollTop += cellRect.bottom - scrollRect.bottom + 2;
      }
    }
  }
  getSelectionBorder(rowKey, columnId) {
    if (!this.isCellSelected(rowKey, columnId)) {
      return _utils.noBorder;
    }
    const pos = this._parent._toIndices({
      rowKey,
      columnId
    });
    if (!pos) {
      return _utils.noBorder;
    }
    const isNeighborSelected = (row, col) => {
      const neighbor = this._parent._toPosition(row, col);
      return neighbor !== null && this.selectedCells.has((0, _utils.cellKey)(neighbor.rowKey, neighbor.columnId));
    };
    return {
      top: !isNeighborSelected(pos.row - 1, pos.col),
      bottom: !isNeighborSelected(pos.row + 1, pos.col),
      left: !isNeighborSelected(pos.row, pos.col - 1),
      right: !isNeighborSelected(pos.row, pos.col + 1)
    };
  }
}
exports.CellInteractionState = CellInteractionState;
//# sourceMappingURL=CellInteractionState.js.map