const s = {
  expand: "▶",
  collapse: "▼",
  sortAsc: "▲",
  sortDesc: "▼",
  sortNone: "⇅",
  submenuArrow: "▶",
  dragHandle: "⋮⋮",
  toolPanel: "☰"
};
class u {
  /** 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(t = {}) {
    this.userConfig = t;
  }
  /**
   * Called when the plugin is attached to a grid.
   * Override to set up event listeners, initialize state, etc.
   */
  attach(t) {
    this.grid = t, 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(t) {
    return this.grid?.getPlugin(t);
  }
  /**
   * Emit a custom event from the grid.
   */
  emit(t, e) {
    this.grid?.dispatchEvent?.(new CustomEvent(t, { detail: e, 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 t = this.grid?.gridConfig?.icons ?? {};
    return { ...s, ...t };
  }
  /**
   * 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(t, e) {
    return e !== void 0 ? e : this.gridIcons[t];
  }
  /**
   * 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(t, e) {
    typeof e == "string" ? t.innerHTML = e : e instanceof HTMLElement && (t.innerHTML = "", t.appendChild(e.cloneNode(!0)));
  }
  /**
   * Log a warning message.
   */
  warn(t) {
    console.warn(`[tbw-grid:${this.name}] ${t}`);
  }
  // #endregion
}
function S(n, t, e) {
  const r = [...n.undoStack, t];
  for (; r.length > e; )
    r.shift();
  return {
    undoStack: r,
    redoStack: []
    // Clear redo on new action
  };
}
function a(n) {
  if (n.undoStack.length === 0)
    return { newState: n, action: null };
  const t = [...n.undoStack], e = t.pop();
  return e ? {
    newState: {
      undoStack: t,
      redoStack: [...n.redoStack, e]
    },
    action: e
  } : { newState: n, action: null };
}
function d(n) {
  if (n.redoStack.length === 0)
    return { newState: n, action: null };
  const t = [...n.redoStack], e = t.pop();
  return e ? {
    newState: {
      undoStack: [...n.undoStack, e],
      redoStack: t
    },
    action: e
  } : { newState: n, action: null };
}
function k(n) {
  return n.undoStack.length > 0;
}
function h(n) {
  return n.redoStack.length > 0;
}
function l() {
  return { undoStack: [], redoStack: [] };
}
function g(n, t, e, r) {
  return {
    type: "cell-edit",
    rowIndex: n,
    field: t,
    oldValue: e,
    newValue: r,
    timestamp: Date.now()
  };
}
class f extends u {
  name = "undoRedo";
  version = "1.0.0";
  get defaultConfig() {
    return {
      maxHistorySize: 100
    };
  }
  // State as class properties
  undoStack = [];
  redoStack = [];
  /**
   * Clean up state when plugin is detached.
   */
  detach() {
    this.undoStack = [], this.redoStack = [];
  }
  /**
   * Handle keyboard shortcuts for undo/redo.
   * - Ctrl+Z / Cmd+Z: Undo
   * - Ctrl+Y / Cmd+Y / Ctrl+Shift+Z / Cmd+Shift+Z: Redo
   */
  onKeyDown(t) {
    const e = (t.ctrlKey || t.metaKey) && t.key === "z" && !t.shiftKey, r = (t.ctrlKey || t.metaKey) && (t.key === "y" || t.key === "z" && t.shiftKey);
    if (e) {
      const o = a({ undoStack: this.undoStack, redoStack: this.redoStack });
      if (o.action) {
        const i = this.rows;
        i[o.action.rowIndex] && (i[o.action.rowIndex][o.action.field] = o.action.oldValue), this.undoStack = o.newState.undoStack, this.redoStack = o.newState.redoStack, this.emit("undo", {
          action: o.action,
          type: "undo"
        }), this.requestRender();
      }
      return !0;
    }
    if (r) {
      const o = d({ undoStack: this.undoStack, redoStack: this.redoStack });
      if (o.action) {
        const i = this.rows;
        i[o.action.rowIndex] && (i[o.action.rowIndex][o.action.field] = o.action.newValue), this.undoStack = o.newState.undoStack, this.redoStack = o.newState.redoStack, this.emit("redo", {
          action: o.action,
          type: "redo"
        }), this.requestRender();
      }
      return !0;
    }
    return !1;
  }
  // #region Public API Methods
  /**
   * Record a cell edit for undo/redo tracking.
   * Call this when a cell value changes.
   *
   * @param rowIndex - The row index where the edit occurred
   * @param field - The field (column key) that was edited
   * @param oldValue - The value before the edit
   * @param newValue - The value after the edit
   */
  recordEdit(t, e, r, o) {
    const i = g(t, e, r, o), c = S(
      { undoStack: this.undoStack, redoStack: this.redoStack },
      i,
      this.config.maxHistorySize ?? 100
    );
    this.undoStack = c.undoStack, this.redoStack = c.redoStack;
  }
  /**
   * Programmatically undo the last action.
   *
   * @returns The undone action, or null if nothing to undo
   */
  undo() {
    const t = a({ undoStack: this.undoStack, redoStack: this.redoStack });
    if (t.action) {
      const e = this.rows;
      e[t.action.rowIndex] && (e[t.action.rowIndex][t.action.field] = t.action.oldValue), this.undoStack = t.newState.undoStack, this.redoStack = t.newState.redoStack, this.requestRender();
    }
    return t.action;
  }
  /**
   * Programmatically redo the last undone action.
   *
   * @returns The redone action, or null if nothing to redo
   */
  redo() {
    const t = d({ undoStack: this.undoStack, redoStack: this.redoStack });
    if (t.action) {
      const e = this.rows;
      e[t.action.rowIndex] && (e[t.action.rowIndex][t.action.field] = t.action.newValue), this.undoStack = t.newState.undoStack, this.redoStack = t.newState.redoStack, this.requestRender();
    }
    return t.action;
  }
  /**
   * Check if there are any actions that can be undone.
   */
  canUndo() {
    return k({ undoStack: this.undoStack, redoStack: this.redoStack });
  }
  /**
   * Check if there are any actions that can be redone.
   */
  canRedo() {
    return h({ undoStack: this.undoStack, redoStack: this.redoStack });
  }
  /**
   * Clear all undo/redo history.
   */
  clearHistory() {
    const t = l();
    this.undoStack = t.undoStack, this.redoStack = t.redoStack;
  }
  /**
   * Get a copy of the current undo stack.
   */
  getUndoStack() {
    return [...this.undoStack];
  }
  /**
   * Get a copy of the current redo stack.
   */
  getRedoStack() {
    return [...this.redoStack];
  }
  // #endregion
}
export {
  f as UndoRedoPlugin
};
//# sourceMappingURL=index.js.map
