const m = {
  expand: "▶",
  collapse: "▼",
  sortAsc: "▲",
  sortDesc: "▼",
  sortNone: "⇅",
  submenuArrow: "▶",
  dragHandle: "⋮⋮",
  toolPanel: "☰"
};
class _ {
  /** 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 { ...m, ...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 w(r, e, t) {
  return r.id !== void 0 ? String(r.id) : t ? `${t}-${e}` : String(e);
}
function K(r, e, t, n = null, s = 0) {
  const o = e.childrenField ?? "children", i = [];
  for (let c = 0; c < r.length; c++) {
    const l = r[c], a = w(l, c, n), h = l[o], f = Array.isArray(h) && h.length > 0, p = t.has(a);
    if (i.push({
      key: a,
      data: l,
      depth: s,
      hasChildren: f,
      isExpanded: p,
      parentKey: n
    }), f && p) {
      const y = K(h, e, t, a, s + 1);
      i.push(...y);
    }
  }
  return i;
}
function R(r, e) {
  const t = new Set(r);
  return t.has(e) ? t.delete(e) : t.add(e), t;
}
function x(r, e, t = null, n = 0) {
  const s = e.childrenField ?? "children", o = /* @__PURE__ */ new Set();
  for (let i = 0; i < r.length; i++) {
    const c = r[i], l = w(c, i, t), a = c[s];
    if (Array.isArray(a) && a.length > 0) {
      o.add(l);
      const h = x(a, e, l, n + 1);
      for (const f of h) o.add(f);
    }
  }
  return o;
}
function v() {
  return /* @__PURE__ */ new Set();
}
function C(r, e, t, n = null, s = 0) {
  const o = t.childrenField ?? "children";
  for (let i = 0; i < r.length; i++) {
    const c = r[i], l = w(c, i, n);
    if (l === e)
      return [l];
    const a = c[o];
    if (Array.isArray(a) && a.length > 0) {
      const h = C(a, e, t, l, s + 1);
      if (h)
        return [l, ...h];
    }
  }
  return null;
}
function b(r, e, t, n) {
  const s = C(r, e, t);
  if (!s) return n;
  const o = new Set(n);
  for (let i = 0; i < s.length - 1; i++)
    o.add(s[i]);
  return o;
}
function A(r, e = "children") {
  if (!Array.isArray(r) || r.length === 0) return !1;
  for (const t of r)
    if (t && Array.isArray(t[e]) && t[e].length > 0)
      return !0;
  return !1;
}
function S(r) {
  if (!Array.isArray(r) || r.length === 0) return null;
  const e = ["children", "items", "nodes", "subRows", "nested"];
  for (const t of r)
    if (!(!t || typeof t != "object")) {
      for (const n of e)
        if (Array.isArray(t[n]) && t[n].length > 0)
          return n;
    }
  return null;
}
function I(r, e = "children", t = 0) {
  if (!Array.isArray(r) || r.length === 0) return t;
  let n = t;
  for (const s of r) {
    if (!s) continue;
    const o = s[e];
    if (Array.isArray(o) && o.length > 0) {
      const i = I(o, e, t + 1);
      i > n && (n = i);
    }
  }
  return n;
}
function D(r, e = "children") {
  if (!Array.isArray(r)) return 0;
  let t = 0;
  for (const n of r) {
    if (!n) continue;
    t++;
    const s = n[e];
    Array.isArray(s) && (t += D(s, e));
  }
  return t;
}
const F = ".tree-toggle{cursor:pointer;-webkit-user-select:none;user-select:none;transition:transform .2s}.tree-toggle:hover{color:var(--tbw-tree-accent, var(--tbw-color-accent))}";
class M extends _ {
  name = "tree";
  version = "1.0.0";
  get defaultConfig() {
    return {
      childrenField: "children",
      autoDetect: !0,
      defaultExpanded: !1,
      indentWidth: 20,
      showExpandIcons: !0
    };
  }
  // #region Internal State
  /** Set of expanded row keys */
  expandedKeys = /* @__PURE__ */ new Set();
  /** Whether initial expansion (based on defaultExpanded config) has been applied */
  initialExpansionDone = !1;
  /** Flattened tree rows for rendering */
  flattenedRows = [];
  /** Map from key to flattened row for quick lookup */
  rowKeyMap = /* @__PURE__ */ new Map();
  // #endregion
  // #region Lifecycle
  detach() {
    this.expandedKeys.clear(), this.initialExpansionDone = !1, this.flattenedRows = [], this.rowKeyMap.clear();
  }
  // #endregion
  // #region Auto-Detection
  /**
   * Detects if tree functionality should be enabled based on data structure.
   * Called by the grid during plugin initialization.
   */
  detect(e) {
    if (!this.config.autoDetect) return !1;
    const t = this.config.childrenField ?? S(e) ?? "children";
    return A(e, t);
  }
  // #endregion
  // #region Data Processing
  processRows(e) {
    const t = this.config.childrenField ?? "children";
    if (!A(e, t))
      return this.flattenedRows = [], this.rowKeyMap.clear(), [...e];
    this.config.defaultExpanded && !this.initialExpansionDone && (this.expandedKeys = x(e, this.config), this.initialExpansionDone = !0), this.flattenedRows = K(e, this.config, this.expandedKeys), this.rowKeyMap.clear();
    for (const n of this.flattenedRows)
      this.rowKeyMap.set(n.key, n);
    return this.flattenedRows.map((n) => ({
      ...n.data,
      __treeKey: n.key,
      __treeDepth: n.depth,
      __treeHasChildren: n.hasChildren,
      __treeExpanded: n.isExpanded
    }));
  }
  processColumns(e) {
    if (this.flattenedRows.length === 0) return [...e];
    const t = this.config.indentWidth ?? 20, n = this.config.showExpandIcons ?? !0, s = [...e];
    if (s.length > 0) {
      const o = { ...s[0] }, i = o.viewRenderer;
      if (i?.__treeWrapped)
        return s;
      const c = (l) => {
        const { value: a, row: h, column: f } = l, p = h.__treeDepth ?? 0, y = h.__treeHasChildren ?? !1, E = h.__treeExpanded ?? !1, u = document.createElement("span");
        if (u.style.display = "flex", u.style.alignItems = "center", u.style.paddingLeft = `${p * t}px`, y && n) {
          const d = document.createElement("span");
          d.className = "tree-toggle", this.setIcon(d, this.resolveIcon(E ? "collapse" : "expand")), d.style.cursor = "pointer", d.style.marginRight = "4px", d.style.fontSize = "10px", d.setAttribute("data-tree-key", h.__treeKey), u.appendChild(d);
        } else if (n) {
          const d = document.createElement("span");
          d.style.width = "14px", d.style.display = "inline-block", u.appendChild(d);
        }
        const g = document.createElement("span");
        if (i) {
          const d = i(l);
          d instanceof Node ? g.appendChild(d) : g.textContent = String(d ?? a ?? "");
        } else
          g.textContent = String(a ?? "");
        return u.appendChild(g), u;
      };
      c.__treeWrapped = !0, o.viewRenderer = c, s[0] = o;
    }
    return s;
  }
  // #endregion
  // #region Event Handlers
  onCellClick(e) {
    const t = e.originalEvent?.target;
    if (!t?.classList.contains("tree-toggle")) return !1;
    const n = t.getAttribute("data-tree-key");
    if (!n) return !1;
    const s = this.rowKeyMap.get(n);
    return s ? (this.expandedKeys = R(this.expandedKeys, n), this.emit("tree-expand", {
      key: n,
      row: s.data,
      expanded: this.expandedKeys.has(n),
      depth: s.depth
    }), this.requestRender(), !0) : !1;
  }
  // #endregion
  // #region Public API
  /**
   * Expand a specific node by key.
   */
  expand(e) {
    this.expandedKeys.add(e), this.requestRender();
  }
  /**
   * Collapse a specific node by key.
   */
  collapse(e) {
    this.expandedKeys.delete(e), this.requestRender();
  }
  /**
   * Toggle the expansion state of a node.
   */
  toggle(e) {
    this.expandedKeys = R(this.expandedKeys, e), this.requestRender();
  }
  /**
   * Expand all nodes in the tree.
   */
  expandAll() {
    this.expandedKeys = x(this.rows, this.config), this.requestRender();
  }
  /**
   * Collapse all nodes in the tree.
   */
  collapseAll() {
    this.expandedKeys = v(), this.requestRender();
  }
  /**
   * Check if a node is currently expanded.
   */
  isExpanded(e) {
    return this.expandedKeys.has(e);
  }
  /**
   * Get all currently expanded keys.
   */
  getExpandedKeys() {
    return [...this.expandedKeys];
  }
  /**
   * Get the flattened tree rows with metadata.
   */
  getFlattenedRows() {
    return [...this.flattenedRows];
  }
  /**
   * Get a row's original data by its key.
   */
  getRowByKey(e) {
    return this.rowKeyMap.get(e)?.data;
  }
  /**
   * Expand all ancestors of a node to make it visible.
   */
  expandToKey(e) {
    this.expandedKeys = b(this.rows, e, this.config, this.expandedKeys), this.requestRender();
  }
  // #endregion
  // #region Styles
  styles = F;
  // #endregion
}
export {
  M as TreePlugin,
  D as countNodes,
  A as detectTreeStructure,
  I as getMaxDepth,
  S as inferChildrenField
};
//# sourceMappingURL=index.js.map
