const g = {
  expand: "▶",
  collapse: "▼",
  sortAsc: "▲",
  sortDesc: "▼",
  sortNone: "⇅",
  submenuArrow: "▶",
  dragHandle: "⋮⋮",
  toolPanel: "☰"
};
class a {
  /** 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, r) {
    this.grid?.dispatchEvent?.(new CustomEvent(e, { detail: r, 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 { ...g, ...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, r) {
    return r !== void 0 ? r : 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, r) {
    typeof r == "string" ? e.innerHTML = r : r instanceof HTMLElement && (e.innerHTML = "", e.appendChild(r.cloneNode(!0)));
  }
  /**
   * Log a warning message.
   */
  warn(e) {
    console.warn(`[tbw-grid:${this.name}] ${e}`);
  }
  // #endregion
}
function h(l) {
  if (!l.length) return [];
  const e = /* @__PURE__ */ new Map(), r = [], t = (s, i) => {
    if (!i.length) return;
    const n = r[r.length - 1];
    if (n && n.implicit && n.firstIndex + n.columns.length === s) {
      n.columns.push(...i);
      return;
    }
    r.push({
      id: "__implicit__" + s,
      label: void 0,
      columns: i,
      firstIndex: s,
      implicit: !0
    });
  };
  let u = [], o = 0;
  return l.forEach((s, i) => {
    const n = s.group;
    if (!n) {
      u.length === 0 && (o = i), u.push(s);
      return;
    }
    u.length && (t(o, u.slice()), u = []);
    const c = typeof n == "string" ? n : n.id;
    let d = e.get(c);
    d || (d = {
      id: c,
      label: typeof n == "string" ? void 0 : n.label,
      columns: [],
      firstIndex: i
    }, e.set(c, d), r.push(d)), d.columns.push(s);
  }), u.length && t(o, u), r.length === 1 && r[0].implicit && r[0].columns.length === l.length ? [] : r;
}
function p(l, e, r) {
  if (!e.length || !l) return;
  const t = /* @__PURE__ */ new Map();
  for (const o of e)
    for (const s of o.columns)
      s?.field && t.set(s.field, o.id);
  const u = Array.from(l.querySelectorAll(".cell[data-field]"));
  u.forEach((o) => {
    const s = o.getAttribute("data-field") || "", i = t.get(s);
    i && (o.classList.add("grouped"), o.getAttribute("data-group") || o.setAttribute("data-group", i));
  });
  for (const o of e) {
    const s = o.columns[o.columns.length - 1], i = u.find((n) => n.getAttribute("data-field") === s.field);
    i && i.classList.add("group-end");
  }
}
function f(l, e) {
  if (l.length === 0) return null;
  const r = document.createElement("div");
  r.className = "header-group-row", r.setAttribute("role", "row");
  for (const t of l) {
    const u = t.firstIndex != null ? t.firstIndex : e.findIndex((n) => t.columns.includes(n)), o = String(t.id).startsWith("__implicit__"), s = o ? "" : t.label || t.id, i = document.createElement("div");
    i.className = "cell header-group-cell", o && i.classList.add("implicit-group"), i.setAttribute("data-group", String(t.id)), i.style.gridColumn = `${u + 1} / span ${t.columns.length}`, i.textContent = s, r.appendChild(i);
  }
  return r;
}
function m(l) {
  return l.some((e) => e.group != null);
}
const w = ".header-group-row{display:grid;grid-auto-flow:column;background:var(--tbw-grouping-columns-header-bg, var(--tbw-color-header-bg));border-bottom:1px solid var(--tbw-grouping-columns-border, var(--tbw-color-border))}.header-group-cell{display:flex;align-items:center;justify-content:center;padding:4px 8px;font-weight:600;font-size:.9em;text-transform:uppercase;letter-spacing:.5px;border-right:1px solid var(--tbw-grouping-columns-border, var(--tbw-color-border))}.header-group-cell:last-child{border-right:none}.header-row .cell.grouped{border-top:none}.header-row .cell.group-end{border-right:2px solid var(--tbw-grouping-columns-separator, var(--tbw-color-border-strong))}";
class b extends a {
  name = "groupingColumns";
  version = "1.0.0";
  get defaultConfig() {
    return {
      showGroupBorders: !0
    };
  }
  // #region Internal State
  groups = [];
  isActive = !1;
  // #endregion
  // #region Lifecycle
  detach() {
    this.groups = [], this.isActive = !1;
  }
  // #endregion
  // #region Static Detection
  /**
   * Auto-detect column groups from column configuration.
   */
  static detect(e, r) {
    const t = r?.columns;
    return Array.isArray(t) ? m(t) : !1;
  }
  // #endregion
  // #region Hooks
  processColumns(e) {
    const r = h(e);
    return r.length === 0 ? (this.isActive = !1, this.groups = [], [...e]) : (this.isActive = !0, this.groups = r, [...e]);
  }
  afterRender() {
    if (!this.isActive || this.groups.length === 0) {
      const s = this.shadowRoot?.querySelector(".header")?.querySelector(".header-group-row");
      s && s.remove();
      return;
    }
    const e = this.shadowRoot?.querySelector(".header");
    if (!e) return;
    const r = e.querySelector(".header-group-row");
    r && r.remove();
    const t = f(this.groups, this.columns);
    if (t) {
      const o = e.querySelector(".header-row");
      o ? e.insertBefore(t, o) : e.appendChild(t);
    }
    const u = e.querySelector(".header-row");
    u && p(u, this.groups, this.columns);
  }
  // #endregion
  // #region Public API
  /**
   * Check if column groups are active.
   * @returns Whether grouping is active
   */
  isGroupingActive() {
    return this.isActive;
  }
  /**
   * Get the computed column groups.
   * @returns Array of column groups
   */
  getGroups() {
    return this.groups;
  }
  /**
   * Get columns in a specific group.
   * @param groupId - The group ID to find
   * @returns Array of columns in the group
   */
  getGroupColumns(e) {
    const r = this.groups.find((t) => t.id === e);
    return r ? r.columns : [];
  }
  /**
   * Refresh column groups (recompute from current columns).
   */
  refresh() {
    this.requestRender();
  }
  // #endregion
  // #region Styles
  styles = w;
  // #endregion
}
export {
  b as GroupingColumnsPlugin
};
//# sourceMappingURL=index.js.map
