const g = {
  expand: "▶",
  collapse: "▼",
  sortAsc: "▲",
  sortDesc: "▼",
  sortNone: "⇅",
  submenuArrow: "▶",
  dragHandle: "⋮⋮",
  toolPanel: "☰"
}, y = {
  /** Ask if a column can be moved. Context: ColumnConfig. Response: boolean | undefined */
  CAN_MOVE_COLUMN: "canMoveColumn"
};
class k {
  /** 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, s) {
    this.grid?.dispatchEvent?.(new CustomEvent(t, { detail: s, 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 { ...g, ...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, s) {
    return s !== void 0 ? s : 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, s) {
    typeof s == "string" ? t.innerHTML = s : s instanceof HTMLElement && (t.innerHTML = "", t.appendChild(s.cloneNode(!0)));
  }
  /**
   * Log a warning message.
   */
  warn(t) {
    console.warn(`[tbw-grid:${this.name}] ${t}`);
  }
  // #endregion
}
function p(n) {
  return n.filter((t) => t.sticky === "left");
}
function m(n) {
  return n.filter((t) => t.sticky === "right");
}
function a(n) {
  return n.some((t) => t.sticky === "left" || t.sticky === "right");
}
function h(n, t) {
  const s = n.shadowRoot;
  if (!s) return;
  const e = Array.from(s.querySelectorAll(".header-row .cell"));
  if (!e.length) return;
  const c = /* @__PURE__ */ new Map();
  t.forEach((r, o) => {
    r.field && c.set(r.field, o);
  });
  let d = 0;
  for (const r of t)
    if (r.sticky === "left") {
      const o = c.get(r.field), i = e.find((l) => l.getAttribute("data-field") === r.field);
      i && (i.classList.add("sticky-left"), i.style.position = "sticky", i.style.left = d + "px", o !== void 0 && s.querySelectorAll(`.data-grid-row .cell[data-col="${o}"]`).forEach((l) => {
        l.classList.add("sticky-left"), l.style.position = "sticky", l.style.left = d + "px";
      }), d += i.offsetWidth);
    }
  let f = 0;
  for (const r of [...t].reverse())
    if (r.sticky === "right") {
      const o = c.get(r.field), i = e.find((l) => l.getAttribute("data-field") === r.field);
      i && (i.classList.add("sticky-right"), i.style.position = "sticky", i.style.right = f + "px", o !== void 0 && s.querySelectorAll(`.data-grid-row .cell[data-col="${o}"]`).forEach((l) => {
        l.classList.add("sticky-right"), l.style.position = "sticky", l.style.right = f + "px";
      }), f += i.offsetWidth);
    }
}
function u(n) {
  const t = n.shadowRoot;
  if (!t) return;
  t.querySelectorAll(".sticky-left, .sticky-right").forEach((e) => {
    e.classList.remove("sticky-left", "sticky-right"), e.style.position = "", e.style.left = "", e.style.right = "";
  });
}
class C extends k {
  name = "pinnedColumns";
  version = "1.0.0";
  get defaultConfig() {
    return {};
  }
  // #region Internal State
  isApplied = !1;
  leftOffsets = /* @__PURE__ */ new Map();
  rightOffsets = /* @__PURE__ */ new Map();
  // #endregion
  // #region Lifecycle
  detach() {
    this.leftOffsets.clear(), this.rightOffsets.clear(), this.isApplied = !1;
  }
  // #endregion
  // #region Detection
  /**
   * Auto-detect sticky columns from column configuration.
   */
  static detect(t, s) {
    const e = s?.columns;
    return Array.isArray(e) ? a(e) : !1;
  }
  // #endregion
  // #region Hooks
  processColumns(t) {
    return this.isApplied = a([...t]), [...t];
  }
  afterRender() {
    if (!this.isApplied)
      return;
    const t = this.grid, s = [...this.columns];
    if (!a(s)) {
      u(t), this.isApplied = !1;
      return;
    }
    queueMicrotask(() => {
      h(t, s);
    });
  }
  /**
   * Handle inter-plugin queries.
   */
  onPluginQuery(t) {
    switch (t.type) {
      case y.CAN_MOVE_COLUMN: {
        const s = t.context, e = s.sticky;
        if (e === "left" || e === "right")
          return !1;
        const c = s.meta?.sticky;
        return c === "left" || c === "right" ? !1 : void 0;
      }
      default:
        return;
    }
  }
  // #endregion
  // #region Public API
  /**
   * Re-apply sticky offsets (e.g., after column resize).
   */
  refreshStickyOffsets() {
    const t = [...this.columns];
    h(this.grid, t);
  }
  /**
   * Get columns pinned to the left.
   */
  getLeftPinnedColumns() {
    const t = [...this.columns];
    return p(t);
  }
  /**
   * Get columns pinned to the right.
   */
  getRightPinnedColumns() {
    const t = [...this.columns];
    return m(t);
  }
  /**
   * Clear all sticky positioning.
   */
  clearStickyPositions() {
    u(this.grid);
  }
  /**
   * Report horizontal scroll boundary offsets for pinned columns.
   * Used by keyboard navigation to ensure focused cells aren't hidden behind sticky columns.
   */
  getHorizontalScrollOffsets(t, s) {
    if (!this.isApplied)
      return;
    let e = 0, c = 0;
    if (t) {
      const f = t.querySelectorAll(".sticky-left"), r = t.querySelectorAll(".sticky-right");
      f.forEach((o) => {
        e += o.offsetWidth;
      }), r.forEach((o) => {
        c += o.offsetWidth;
      });
    } else {
      const r = this.grid.shadowRoot;
      r && r.querySelectorAll(".header-row .cell").forEach((i) => {
        i.classList.contains("sticky-left") ? e += i.offsetWidth : i.classList.contains("sticky-right") && (c += i.offsetWidth);
      });
    }
    const d = s?.classList.contains("sticky-left") || s?.classList.contains("sticky-right");
    return { left: e, right: c, skipScroll: d };
  }
  // #endregion
}
export {
  C as PinnedColumnsPlugin
};
//# sourceMappingURL=index.js.map
