const h = {
  expand: "▶",
  collapse: "▼",
  sortAsc: "▲",
  sortDesc: "▼",
  sortNone: "⇅",
  submenuArrow: "▶",
  dragHandle: "⋮⋮",
  toolPanel: "☰"
};
class m {
  /** 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 { ...h, ...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 p(r, t, e) {
  return t.length ? [...r].sort((s, o) => {
    for (const i of t) {
      const d = e.find((l) => l.field === i.field)?.sortComparator ?? x, a = s[i.field], u = o[i.field], n = d(a, u, s, o);
      if (n !== 0)
        return i.direction === "asc" ? n : -n;
    }
    return 0;
  }) : [...r];
}
function x(r, t) {
  return r == null && t == null ? 0 : r == null ? 1 : t == null ? -1 : typeof r == "number" && typeof t == "number" ? r - t : r instanceof Date && t instanceof Date ? r.getTime() - t.getTime() : typeof r == "boolean" && typeof t == "boolean" ? r === t ? 0 : r ? -1 : 1 : String(r).localeCompare(String(t));
}
function M(r, t, e, s) {
  const o = r.find((i) => i.field === t);
  return e ? o ? o.direction === "asc" ? r.map((i) => i.field === t ? { ...i, direction: "desc" } : i) : r.filter((i) => i.field !== t) : r.length < s ? [...r, { field: t, direction: "asc" }] : r : o?.direction === "asc" ? [{ field: t, direction: "desc" }] : o?.direction === "desc" ? [] : [{ field: t, direction: "asc" }];
}
function f(r, t) {
  const e = r.findIndex((s) => s.field === t);
  return e >= 0 ? e + 1 : void 0;
}
function g(r, t) {
  return r.find((e) => e.field === t)?.direction;
}
const y = '.header-cell[data-sort=asc]:after{content:"↑";margin-left:4px;opacity:.8}.header-cell[data-sort=desc]:after{content:"↓";margin-left:4px;opacity:.8}.sort-index{font-size:10px;background:var(--tbw-multi-sort-badge-bg, var(--tbw-color-panel-bg));color:var(--tbw-multi-sort-badge-color, var(--tbw-color-fg));border-radius:50%;width:14px;height:14px;display:inline-flex;align-items:center;justify-content:center;margin-left:2px;font-weight:600}';
class C extends m {
  name = "multiSort";
  version = "1.0.0";
  get defaultConfig() {
    return {
      maxSortColumns: 3,
      showSortIndex: !0
    };
  }
  // #region Internal State
  sortModel = [];
  // #endregion
  // #region Lifecycle
  detach() {
    this.sortModel = [];
  }
  // #endregion
  // #region Hooks
  processRows(t) {
    return this.sortModel.length === 0 ? [...t] : p([...t], this.sortModel, [...this.columns]);
  }
  onHeaderClick(t) {
    if (!this.columns.find((i) => i.field === t.field)?.sortable) return !1;
    const s = t.originalEvent.shiftKey, o = this.config.maxSortColumns ?? 3;
    return this.sortModel = M(this.sortModel, t.field, s, o), this.emit("sort-change", { sortModel: [...this.sortModel] }), this.requestRender(), !0;
  }
  afterRender() {
    const t = this.shadowRoot;
    if (!t) return;
    const e = this.config.showSortIndex !== !1;
    t.querySelectorAll(".header-row .cell[data-field]").forEach((o) => {
      const i = o.getAttribute("data-field");
      if (!i) return;
      const c = f(this.sortModel, i), d = g(this.sortModel, i);
      if (o.querySelector(".sort-index")?.remove(), d) {
        o.querySelector('[part~="sort-indicator"], .sort-indicator')?.remove(), o.setAttribute("data-sort", d);
        const n = document.createElement("span");
        if (n.className = "sort-indicator", n.style.marginLeft = "4px", n.style.opacity = "0.8", this.setIcon(n, this.resolveIcon(d === "asc" ? "sortAsc" : "sortDesc")), o.appendChild(n), e && this.sortModel.length > 1 && c !== void 0) {
          const l = document.createElement("span");
          l.className = "sort-index", l.textContent = String(c), o.appendChild(l);
        }
      } else
        o.removeAttribute("data-sort");
    });
  }
  // #endregion
  // #region Public API
  /**
   * Get the current sort model.
   * @returns Copy of the current sort model
   */
  getSortModel() {
    return [...this.sortModel];
  }
  /**
   * Set the sort model programmatically.
   * @param model - New sort model to apply
   */
  setSortModel(t) {
    this.sortModel = [...t], this.emit("sort-change", { sortModel: [...t] }), this.requestRender();
  }
  /**
   * Clear all sorting.
   */
  clearSort() {
    this.sortModel = [], this.emit("sort-change", { sortModel: [] }), this.requestRender();
  }
  /**
   * Get the sort index (1-based) for a specific field.
   * @param field - Field to check
   * @returns 1-based index or undefined if not sorted
   */
  getSortIndex(t) {
    return f(this.sortModel, t);
  }
  /**
   * Get the sort direction for a specific field.
   * @param field - Field to check
   * @returns Sort direction or undefined if not sorted
   */
  getSortDirection(t) {
    return g(this.sortModel, t);
  }
  // #endregion
  // #region Column State Hooks
  /**
   * Return sort state for a column if it's in the sort model.
   */
  getColumnState(t) {
    const e = this.sortModel.findIndex((o) => o.field === t);
    return e === -1 ? void 0 : {
      sort: {
        direction: this.sortModel[e].direction,
        priority: e
      }
    };
  }
  /**
   * Apply sort state from column state.
   * Rebuilds the sort model from all column states.
   */
  applyColumnState(t, e) {
    if (!e.sort) {
      this.sortModel = this.sortModel.filter((i) => i.field !== t);
      return;
    }
    const s = this.sortModel.findIndex((i) => i.field === t), o = {
      field: t,
      direction: e.sort.direction
    };
    s !== -1 ? this.sortModel[s] = o : this.sortModel.splice(e.sort.priority, 0, o);
  }
  // #endregion
  // #region Styles
  styles = y;
  // #endregion
}
export {
  C as MultiSortPlugin
};
//# sourceMappingURL=index.js.map
