const f = {
  expand: "▶",
  collapse: "▼",
  sortAsc: "▲",
  sortDesc: "▼",
  sortNone: "⇅",
  submenuArrow: "▶",
  dragHandle: "⋮⋮",
  toolPanel: "☰"
};
class w {
  /** Plugin version - override in subclass if needed */
  version = "1.0.0";
  /** CSS styles to inject into the grid's shadow DOM */
  styles;
  /** Custom cell renderers keyed by type name */
  cellRenderers;
  /** Custom header renderers keyed by type name */
  headerRenderers;
  /** Custom cell editors keyed by type name */
  cellEditors;
  /** The grid instance this plugin is attached to */
  grid;
  /** Plugin configuration - merged with defaults in attach() */
  config;
  /** User-provided configuration from constructor */
  userConfig;
  /**
   * Default configuration - subclasses should override this getter.
   * Note: This must be a getter (not property initializer) for proper inheritance
   * since property initializers run after parent constructor.
   */
  get defaultConfig() {
    return {};
  }
  constructor(e = {}) {
    this.userConfig = e;
  }
  /**
   * Called when the plugin is attached to a grid.
   * Override to set up event listeners, initialize state, etc.
   */
  attach(e) {
    this.grid = e, this.config = { ...this.defaultConfig, ...this.userConfig };
  }
  /**
   * Called when the plugin is detached from a grid.
   * Override to clean up event listeners, timers, etc.
   */
  detach() {
  }
  /**
   * Get another plugin instance from the same grid.
   * Use for inter-plugin communication.
   */
  getPlugin(e) {
    return this.grid?.getPlugin(e);
  }
  /**
   * Emit a custom event from the grid.
   */
  emit(e, t) {
    this.grid?.dispatchEvent?.(new CustomEvent(e, { detail: t, bubbles: !0 }));
  }
  /**
   * Request a re-render of the grid.
   */
  requestRender() {
    this.grid?.requestRender?.();
  }
  /**
   * Request a lightweight style update without rebuilding DOM.
   * Use this instead of requestRender() when only CSS classes need updating.
   */
  requestAfterRender() {
    this.grid?.requestAfterRender?.();
  }
  /**
   * Get the current rows from the grid.
   */
  get rows() {
    return this.grid?.rows ?? [];
  }
  /**
   * Get the original unfiltered/unprocessed rows from the grid.
   * Use this when you need all source data regardless of active filters.
   */
  get sourceRows() {
    return this.grid?.sourceRows ?? [];
  }
  /**
   * Get the current columns from the grid.
   */
  get columns() {
    return this.grid?.columns ?? [];
  }
  /**
   * Get only visible columns from the grid (excludes hidden).
   * Use this for rendering that needs to match the grid template.
   */
  get visibleColumns() {
    return this.grid?.visibleColumns ?? [];
  }
  /**
   * Get the shadow root of the grid.
   */
  get shadowRoot() {
    return this.grid?.shadowRoot ?? null;
  }
  /**
   * Get the disconnect signal for event listener cleanup.
   * This signal is aborted when the grid disconnects from the DOM.
   * Use this when adding event listeners that should be cleaned up automatically.
   *
   * Best for:
   * - Document/window-level listeners added in attach()
   * - Listeners on the grid element itself
   * - Any listener that should persist across renders
   *
   * Not needed for:
   * - Listeners on elements created in afterRender() (removed with element)
   *
   * @example
   * element.addEventListener('click', handler, { signal: this.disconnectSignal });
   * document.addEventListener('keydown', handler, { signal: this.disconnectSignal });
   */
  get disconnectSignal() {
    return this.grid?.disconnectSignal;
  }
  /**
   * Get the grid-level icons configuration.
   * Returns merged icons (user config + defaults).
   */
  get gridIcons() {
    const e = this.grid?.gridConfig?.icons ?? {};
    return { ...f, ...e };
  }
  /**
   * Resolve an icon value to string or HTMLElement.
   * Checks plugin config first, then grid-level icons, then defaults.
   *
   * @param iconKey - The icon key in GridIcons (e.g., 'expand', 'collapse')
   * @param pluginOverride - Optional plugin-level override
   * @returns The resolved icon value
   */
  resolveIcon(e, t) {
    return t !== void 0 ? t : this.gridIcons[e];
  }
  /**
   * Set an icon value on an element.
   * Handles both string (text/HTML) and HTMLElement values.
   *
   * @param element - The element to set the icon on
   * @param icon - The icon value (string or HTMLElement)
   */
  setIcon(e, t) {
    typeof t == "string" ? e.innerHTML = t : t instanceof HTMLElement && (e.innerHTML = "", e.appendChild(t.cloneNode(!0)));
  }
  /**
   * Log a warning message.
   */
  warn(e) {
    console.warn(`[tbw-grid:${this.name}] ${e}`);
  }
  // #endregion
}
function h(s) {
  return {
    startRow: Math.min(s.startRow, s.endRow),
    startCol: Math.min(s.startCol, s.endCol),
    endRow: Math.max(s.startRow, s.endRow),
    endCol: Math.max(s.startCol, s.endCol)
  };
}
function R(s) {
  const e = h(s);
  return {
    from: { row: e.startRow, col: e.startCol },
    to: { row: e.endRow, col: e.endCol }
  };
}
function g(s) {
  return s.map(R);
}
function C(s, e, t) {
  const r = h(t);
  return s >= r.startRow && s <= r.endRow && e >= r.startCol && e <= r.endCol;
}
function u(s, e, t) {
  return t.some((r) => C(s, e, r));
}
function m(s) {
  const e = [], t = h(s);
  for (let r = t.startRow; r <= t.endRow; r++)
    for (let o = t.startCol; o <= t.endCol; o++)
      e.push({ row: r, col: o });
  return e;
}
function A(s) {
  const e = /* @__PURE__ */ new Map();
  for (const t of s)
    for (const r of m(t))
      e.set(`${r.row},${r.col}`, r);
  return [...e.values()];
}
function d(s, e) {
  return {
    startRow: s.row,
    startCol: s.col,
    endRow: e.row,
    endCol: e.col
  };
}
const b = ':host .selecting .data-grid-row>.cell{-webkit-user-select:none;user-select:none}:host .data-grid-row.row-focus{background-color:var(--tbw-focus-background, rgba(from var(--tbw-color-accent) r g b / 12%))}:host([data-selection-mode="row"]) .cell-focus{outline:none}:host .data-grid-row>.cell.selected{background-color:var(--tbw-range-selection-bg)}:host .data-grid-row>.cell.selected.top{border-top:2px solid var(--tbw-range-border-color)}:host .data-grid-row>.cell.selected.bottom{border-bottom:2px solid var(--tbw-range-border-color)}:host .data-grid-row>.cell.selected.first{border-left:2px solid var(--tbw-range-border-color)}:host .data-grid-row>.cell.selected.last{border-right:2px solid var(--tbw-range-border-color)}';
function p(s, e, t) {
  if (s === "cell" && e.selectedCell)
    return {
      mode: s,
      ranges: [
        {
          from: { row: e.selectedCell.row, col: e.selectedCell.col },
          to: { row: e.selectedCell.row, col: e.selectedCell.col }
        }
      ]
    };
  if (s === "row" && e.selected.size > 0) {
    const r = [...e.selected].map((o) => ({
      from: { row: o, col: 0 },
      to: { row: o, col: t - 1 }
    }));
    return { mode: s, ranges: r };
  }
  return s === "range" && e.ranges.length > 0 ? { mode: s, ranges: g(e.ranges) } : { mode: s, ranges: [] };
}
class v extends w {
  name = "selection";
  version = "1.0.0";
  get defaultConfig() {
    return {
      mode: "cell"
    };
  }
  // #region Internal State
  /** Row selection state (row mode) */
  selected = /* @__PURE__ */ new Set();
  lastSelected = null;
  anchor = null;
  /** Range selection state (range mode) */
  ranges = [];
  activeRange = null;
  cellAnchor = null;
  isDragging = !1;
  /** Cell selection state (cell mode) */
  selectedCell = null;
  // #endregion
  // #region Lifecycle
  detach() {
    this.selected.clear(), this.ranges = [], this.activeRange = null, this.cellAnchor = null, this.isDragging = !1, this.selectedCell = null;
  }
  // #endregion
  // #region Event Handlers
  onCellClick(e) {
    const { rowIndex: t, colIndex: r, originalEvent: o } = e, { mode: n } = this.config;
    if (n === "cell")
      return this.selectedCell = { row: t, col: r }, this.emit("selection-change", this.#e()), this.requestAfterRender(), !1;
    if (n === "row")
      return this.selected.clear(), this.selected.add(t), this.lastSelected = t, this.emit("selection-change", this.#e()), this.requestAfterRender(), !1;
    if (n === "range") {
      const c = o.shiftKey, l = o.ctrlKey || o.metaKey;
      if (c && this.cellAnchor) {
        const i = d(this.cellAnchor, { row: t, col: r });
        l ? this.ranges.length > 0 ? this.ranges[this.ranges.length - 1] = i : this.ranges.push(i) : this.ranges = [i], this.activeRange = i;
      } else if (l) {
        const i = {
          startRow: t,
          startCol: r,
          endRow: t,
          endCol: r
        };
        this.ranges.push(i), this.activeRange = i, this.cellAnchor = { row: t, col: r };
      } else {
        const i = {
          startRow: t,
          startCol: r,
          endRow: t,
          endCol: r
        };
        this.ranges = [i], this.activeRange = i, this.cellAnchor = { row: t, col: r };
      }
      return this.emit("selection-change", this.#e()), this.requestAfterRender(), !1;
    }
    return !1;
  }
  onKeyDown(e) {
    const { mode: t } = this.config, o = ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Tab", "Home", "End", "PageUp", "PageDown"].includes(e.key);
    if (e.key === "Escape")
      return t === "cell" ? this.selectedCell = null : t === "row" ? (this.selected.clear(), this.anchor = null) : t === "range" && (this.ranges = [], this.activeRange = null, this.cellAnchor = null), this.emit("selection-change", this.#e()), this.requestAfterRender(), !0;
    if (t === "cell" && o)
      return queueMicrotask(() => {
        this.selectedCell = { row: this.grid.focusRow, col: this.grid.focusCol }, this.emit("selection-change", this.#e()), this.requestAfterRender();
      }), !1;
    if (t === "row" && (e.key === "ArrowUp" || e.key === "ArrowDown"))
      return queueMicrotask(() => {
        this.selected.clear(), this.selected.add(this.grid.focusRow), this.lastSelected = this.grid.focusRow, this.emit("selection-change", this.#e()), this.requestAfterRender();
      }), !1;
    if (t === "range" && o) {
      const n = e.shiftKey;
      return queueMicrotask(() => {
        const c = this.grid.focusRow, l = this.grid.focusCol;
        if (n) {
          this.cellAnchor || (this.cellAnchor = { row: c, col: l });
          const i = d(this.cellAnchor, { row: c, col: l });
          this.ranges = [i], this.activeRange = i;
        } else
          this.ranges = [], this.activeRange = null, this.cellAnchor = { row: c, col: l };
        this.emit("selection-change", this.#e()), this.requestAfterRender();
      }), !1;
    }
    if (t === "range" && e.key === "a" && (e.ctrlKey || e.metaKey)) {
      const n = this.rows.length, c = this.columns.length;
      if (n > 0 && c > 0) {
        const l = {
          startRow: 0,
          startCol: 0,
          endRow: n - 1,
          endCol: c - 1
        };
        return this.ranges = [l], this.activeRange = l, this.emit("selection-change", this.#e()), this.requestAfterRender(), !0;
      }
    }
    return !1;
  }
  onCellMouseDown(e) {
    if (this.config.mode !== "range" || e.rowIndex === void 0 || e.colIndex === void 0 || e.rowIndex < 0 || e.originalEvent.shiftKey && this.cellAnchor)
      return;
    this.isDragging = !0;
    const t = e.rowIndex, r = e.colIndex;
    this.cellAnchor = { row: t, col: r }, e.originalEvent.ctrlKey || e.originalEvent.metaKey || (this.ranges = []);
    const n = {
      startRow: t,
      startCol: r,
      endRow: t,
      endCol: r
    };
    return this.ranges.push(n), this.activeRange = n, this.emit("selection-change", this.#e()), this.requestAfterRender(), !0;
  }
  onCellMouseMove(e) {
    if (this.config.mode !== "range" || !this.isDragging || !this.cellAnchor || e.rowIndex === void 0 || e.colIndex === void 0 || e.rowIndex < 0) return;
    const t = d(this.cellAnchor, { row: e.rowIndex, col: e.colIndex });
    return this.ranges.length > 0 ? this.ranges[this.ranges.length - 1] = t : this.ranges.push(t), this.activeRange = t, this.emit("selection-change", this.#e()), this.requestAfterRender(), !0;
  }
  onCellMouseUp(e) {
    if (this.config.mode === "range" && this.isDragging)
      return this.isDragging = !1, !0;
  }
  /**
   * Apply selection classes to visible cells/rows.
   * Shared by afterRender and onScrollRender.
   */
  #t() {
    const e = this.shadowRoot;
    if (!e) return;
    const { mode: t } = this.config;
    e.querySelectorAll(".cell").forEach((n) => {
      n.classList.remove("selected", "top", "bottom", "first", "last");
    });
    const o = e.querySelectorAll(".data-grid-row");
    if (o.forEach((n) => {
      n.classList.remove("selected", "row-focus");
    }), t === "row" && (e.querySelectorAll(".cell-focus").forEach((n) => n.classList.remove("cell-focus")), o.forEach((n) => {
      const c = n.querySelector(".cell[data-row]"), l = parseInt(c?.getAttribute("data-row") ?? "-1", 10);
      l >= 0 && this.selected.has(l) && n.classList.add("selected", "row-focus");
    })), t === "range" && this.ranges.length > 0) {
      const n = this.activeRange ? h(this.activeRange) : null;
      e.querySelectorAll(".cell[data-row][data-col]").forEach((l) => {
        const i = parseInt(l.getAttribute("data-row") ?? "-1", 10), a = parseInt(l.getAttribute("data-col") ?? "-1", 10);
        i >= 0 && a >= 0 && u(i, a, this.ranges) && (l.classList.add("selected"), l.classList.remove("cell-focus"), n && (i === n.startRow && l.classList.add("top"), i === n.endRow && l.classList.add("bottom"), a === n.startCol && l.classList.add("first"), a === n.endCol && l.classList.add("last")));
      });
    }
    t === "cell" && this.selectedCell && e.querySelectorAll(".cell-focus").forEach((n) => n.classList.remove("cell-focus"));
  }
  afterRender() {
    const e = this.shadowRoot;
    if (!e) return;
    const t = e.children[0], { mode: r } = this.config;
    this.grid.setAttribute("data-selection-mode", r), t && t.classList.toggle("selecting", this.isDragging), this.#t();
  }
  /**
   * Called after scroll-triggered row rendering.
   * Reapplies selection classes to recycled DOM elements.
   */
  onScrollRender() {
    this.#t();
  }
  // #endregion
  // #region Public API
  /**
   * Get the selected cell (cell mode only).
   */
  getSelectedCell() {
    return this.selectedCell;
  }
  /**
   * Get all selected row indices (row mode).
   */
  getSelectedRows() {
    return [...this.selected];
  }
  /**
   * Get all selected cell ranges in public format.
   */
  getRanges() {
    return g(this.ranges);
  }
  /**
   * Get all selected cells across all ranges.
   */
  getSelectedCells() {
    return A(this.ranges);
  }
  /**
   * Check if a specific cell is in range selection.
   */
  isCellSelected(e, t) {
    return u(e, t, this.ranges);
  }
  /**
   * Clear all selection.
   */
  clearSelection() {
    this.selectedCell = null, this.selected.clear(), this.anchor = null, this.ranges = [], this.activeRange = null, this.cellAnchor = null, this.emit("selection-change", { mode: this.config.mode, ranges: [] }), this.requestAfterRender();
  }
  /**
   * Set selected ranges programmatically.
   */
  setRanges(e) {
    this.ranges = e.map((t) => ({
      startRow: t.from.row,
      startCol: t.from.col,
      endRow: t.to.row,
      endCol: t.to.col
    })), this.activeRange = this.ranges.length > 0 ? this.ranges[this.ranges.length - 1] : null, this.emit("selection-change", {
      mode: this.config.mode,
      ranges: g(this.ranges)
    }), this.requestAfterRender();
  }
  // #endregion
  // #region Private Helpers
  #e() {
    return p(
      this.config.mode,
      {
        selectedCell: this.selectedCell,
        selected: this.selected,
        ranges: this.ranges
      },
      this.columns.length
    );
  }
  // #endregion
  // #region Styles
  styles = b;
  // #endregion
}
function q(s, e, t, r) {
  const o = new Set(s.selected);
  let n = s.anchor;
  if (t === "single")
    o.clear(), o.add(e), n = e;
  else if (t === "multiple") {
    const c = r.ctrlKey || r.metaKey;
    if (r.shiftKey && s.anchor !== null) {
      const l = Math.min(s.anchor, e), i = Math.max(s.anchor, e);
      for (let a = l; a <= i; a++)
        o.add(a);
    } else c ? (o.has(e) ? o.delete(e) : o.add(e), n = e) : (o.clear(), o.add(e), n = e);
  }
  return { selected: o, lastSelected: e, anchor: n };
}
function S(s) {
  const e = /* @__PURE__ */ new Set();
  for (let t = 0; t < s; t++)
    e.add(t);
  return e;
}
function K(s, e) {
  const t = [], r = [];
  for (const o of e)
    s.has(o) || t.push(o);
  for (const o of s)
    e.has(o) || r.push(o);
  return { added: t, removed: r };
}
export {
  v as SelectionPlugin,
  K as computeSelectionDiff,
  q as handleRowClick,
  S as selectAll
};
//# sourceMappingURL=index.js.map
