const m = '<svg viewBox="0 0 16 16" width="12" height="12"><path fill="currentColor" d="M6 10.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1-.5-.5zm-2-3a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5zm-2-3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5z"/></svg>', x = {
  expand: "▶",
  collapse: "▼",
  sortAsc: "▲",
  sortDesc: "▼",
  sortNone: "⇅",
  submenuArrow: "▶",
  dragHandle: "⋮⋮",
  toolPanel: "☰",
  filter: m,
  filterActive: m,
  print: "🖨️"
};
class C {
  /**
   * Plugin dependencies - declare other plugins this one requires.
   *
   * Dependencies are validated when the plugin is attached.
   * Required dependencies throw an error if missing.
   * Optional dependencies log an info message if missing.
   *
   * @example
   * ```typescript
   * static readonly dependencies: PluginDependency[] = [
   *   { name: 'editing', required: true, reason: 'Tracks cell edits for undo/redo' },
   *   { name: 'selection', required: false, reason: 'Enables selection-based undo' },
   * ];
   * ```
   */
  static dependencies;
  /**
   * Plugin manifest - declares owned properties, config rules, and hook priorities.
   *
   * This is read by the configuration validator to:
   * - Validate that required plugins are loaded when their properties are used
   * - Execute configRules to detect invalid/conflicting settings
   * - Order hook execution based on priority
   *
   * @example
   * ```typescript
   * static override readonly manifest: PluginManifest<MyConfig> = {
   *   ownedProperties: [
   *     { property: 'myProp', level: 'column', description: 'the "myProp" column property' },
   *   ],
   *   configRules: [
   *     { id: 'myPlugin/conflict', severity: 'warn', message: '...', check: (c) => c.a && c.b },
   *   ],
   * };
   * ```
   */
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  static manifest;
  /**
   * Plugin version - defaults to grid version for built-in plugins.
   * Third-party plugins can override with their own semver.
   */
  version = typeof __GRID_VERSION__ < "u" ? __GRID_VERSION__ : "dev";
  /** 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;
  /**
   * Plugin-level AbortController for event listener cleanup.
   * Created fresh in attach(), aborted in detach().
   * This ensures event listeners are properly cleaned up when plugins are re-attached.
   */
  #e;
  /**
   * 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.
   *
   * @example
   * ```ts
   * attach(grid: GridElement): void {
   *   super.attach(grid);
   *   // Set up document-level listeners with auto-cleanup
   *   document.addEventListener('keydown', this.handleEscape, {
   *     signal: this.disconnectSignal
   *   });
   * }
   * ```
   */
  attach(e) {
    this.#e?.abort(), this.#e = new AbortController(), 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.
   *
   * @example
   * ```ts
   * detach(): void {
   *   // Clean up any state not handled by disconnectSignal
   *   this.selectedRows.clear();
   *   this.cache = null;
   * }
   * ```
   */
  detach() {
    this.#e?.abort(), this.#e = void 0;
  }
  /**
   * Get another plugin instance from the same grid.
   * Use for inter-plugin communication.
   *
   * @example
   * ```ts
   * const selection = this.getPlugin(SelectionPlugin);
   * if (selection) {
   *   const selectedRows = selection.getSelectedRows();
   * }
   * ```
   */
  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 }));
  }
  /**
   * Emit a cancelable custom event from the grid.
   * @returns `true` if the event was cancelled (preventDefault called), `false` otherwise
   */
  emitCancelable(e, t) {
    const n = new CustomEvent(e, { detail: t, bubbles: !0, cancelable: !0 });
    return this.grid?.dispatchEvent?.(n), n.defaultPrevented;
  }
  /**
   * Request a re-render of the grid.
   */
  requestRender() {
    this.grid?.requestRender?.();
  }
  /**
   * Request a re-render and restore focus styling afterward.
   * Use this when a plugin action (like expand/collapse) triggers a render
   * but needs to maintain keyboard navigation focus.
   */
  requestRenderWithFocus() {
    this.grid?.requestRenderWithFocus?.();
  }
  /**
   * 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 grid as an HTMLElement for direct DOM operations.
   * Use sparingly - prefer the typed GridElementRef API when possible.
   *
   * @example
   * ```ts
   * const width = this.gridElement.clientWidth;
   * this.gridElement.classList.add('my-plugin-active');
   * ```
   */
  get gridElement() {
    return this.grid;
  }
  /**
   * 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.#e?.signal ?? this.grid?.disconnectSignal;
  }
  /**
   * Get the grid-level icons configuration.
   * Returns merged icons (user config + defaults).
   */
  get gridIcons() {
    const e = this.grid?.gridConfig?.icons ?? {};
    return { ...x, ...e };
  }
  // #region Animation Helpers
  /**
   * Check if animations are enabled at the grid level.
   * Respects gridConfig.animation.mode and the CSS variable set by the grid.
   *
   * Plugins should use this to skip animations when:
   * - Animation mode is 'off' or `false`
   * - User prefers reduced motion and mode is 'reduced-motion' (default)
   *
   * @example
   * ```ts
   * private get animationStyle(): 'slide' | 'fade' | false {
   *   if (!this.isAnimationEnabled) return false;
   *   return this.config.animation ?? 'slide';
   * }
   * ```
   */
  get isAnimationEnabled() {
    const e = this.grid?.effectiveConfig?.animation?.mode ?? "reduced-motion";
    if (e === !1 || e === "off") return !1;
    if (e === !0 || e === "on") return !0;
    const t = this.gridElement;
    return t ? getComputedStyle(t).getPropertyValue("--tbw-animation-enabled").trim() !== "0" : !0;
  }
  /**
   * Get the animation duration in milliseconds from CSS variable.
   * Falls back to 200ms if not set.
   *
   * Plugins can use this for their animation timing to stay consistent
   * with the grid-level animation.duration setting.
   *
   * @example
   * ```ts
   * element.animate(keyframes, { duration: this.animationDuration });
   * ```
   */
  get animationDuration() {
    const e = this.gridElement;
    if (e) {
      const t = getComputedStyle(e).getPropertyValue("--tbw-animation-duration").trim(), n = parseInt(t, 10);
      if (!isNaN(n)) return n;
    }
    return 200;
  }
  // #endregion
  /**
   * 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 b(i, e = !0) {
  if (i == null) return "";
  if (i instanceof Date) return i.toISOString();
  if (typeof i == "object") return JSON.stringify(i);
  const t = String(i);
  return e && (t.includes(",") || t.includes('"') || t.includes(`
`) || t.includes("\r")) ? `"${t.replace(/"/g, '""')}"` : t;
}
function E(i, e, t, n = {}) {
  const c = n.delimiter ?? ",", l = n.newline ?? `
`, o = [], r = n.bom ? "\uFEFF" : "";
  if (t.includeHeaders !== !1) {
    const s = e.map((a) => {
      const d = a.header || a.field, u = t.processHeader ? t.processHeader(d, a.field) : d;
      return b(u);
    });
    o.push(s.join(c));
  }
  for (const s of i) {
    const a = e.map((d) => {
      let u = s[d.field];
      return t.processCell && (u = t.processCell(u, d.field, s)), b(u);
    });
    o.push(a.join(c));
  }
  return r + o.join(l);
}
function h(i, e) {
  const t = URL.createObjectURL(i), n = document.createElement("a");
  n.href = t, n.download = e, n.style.display = "none", document.body.appendChild(n), n.click(), document.body.removeChild(n), URL.revokeObjectURL(t);
}
function S(i, e) {
  const t = new Blob([i], { type: "text/csv;charset=utf-8;" });
  h(t, e);
}
function w(i) {
  return i.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
}
function y(i, e, t) {
  let n = `<?xml version="1.0" encoding="UTF-8"?>
<?mso-application progid="Excel.Sheet"?>
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
  xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet">
<Worksheet ss:Name="Sheet1">
<Table>`;
  if (t.includeHeaders !== !1) {
    n += `
<Row>`;
    for (const c of e) {
      const l = c.header || c.field, o = t.processHeader ? t.processHeader(l, c.field) : l;
      n += `<Cell><Data ss:Type="String">${w(o)}</Data></Cell>`;
    }
    n += "</Row>";
  }
  for (const c of i) {
    n += `
<Row>`;
    for (const l of e) {
      let o = c[l.field];
      t.processCell && (o = t.processCell(o, l.field, c));
      let r = "String", s = "";
      o == null ? s = "" : typeof o == "number" && !isNaN(o) ? (r = "Number", s = String(o)) : o instanceof Date ? (r = "DateTime", s = o.toISOString()) : s = w(String(o)), n += `<Cell><Data ss:Type="${r}">${s}</Data></Cell>`;
    }
    n += "</Row>";
  }
  return n += `
</Table>
</Worksheet>
</Workbook>`, n;
}
function v(i, e) {
  const t = e.endsWith(".xls") ? e : `${e}.xls`, n = new Blob([i], {
    type: "application/vnd.ms-excel;charset=utf-8;"
  });
  h(n, t);
}
class I extends C {
  /** @internal */
  name = "export";
  /** @internal */
  get defaultConfig() {
    return {
      fileName: "export",
      includeHeaders: !0,
      onlyVisible: !0,
      onlySelected: !1
    };
  }
  // #region Internal State
  isExportingFlag = !1;
  lastExportInfo = null;
  // #endregion
  // #region Private Methods
  performExport(e, t) {
    const n = this.config, c = {
      format: e,
      fileName: t?.fileName ?? n.fileName ?? "export",
      includeHeaders: t?.includeHeaders ?? n.includeHeaders,
      processCell: t?.processCell,
      processHeader: t?.processHeader,
      columns: t?.columns,
      rowIndices: t?.rowIndices
    };
    let l = [...this.columns];
    if (n.onlyVisible && (l = l.filter((s) => !s.hidden && !s.field.startsWith("__"))), t?.columns) {
      const s = new Set(t.columns);
      l = l.filter((a) => s.has(a.field));
    }
    let o = [...this.rows];
    if (n.onlySelected) {
      const s = this.getSelectionState();
      s?.selected?.size && (o = [...s.selected].sort((d, u) => d - u).map((d) => this.rows[d]).filter(Boolean));
    }
    t?.rowIndices && (o = t.rowIndices.map((s) => this.rows[s]).filter(Boolean)), this.isExportingFlag = !0;
    let r = c.fileName;
    try {
      switch (e) {
        case "csv": {
          const s = E(o, l, c, { bom: !0 });
          r = r.endsWith(".csv") ? r : `${r}.csv`, S(s, r);
          break;
        }
        case "excel": {
          const s = y(o, l, c);
          r = r.endsWith(".xls") ? r : `${r}.xls`, v(s, r);
          break;
        }
        case "json": {
          const s = o.map((u) => {
            const p = {};
            for (const f of l) {
              let g = u[f.field];
              c.processCell && (g = c.processCell(g, f.field, u)), p[f.field] = g;
            }
            return p;
          }), a = JSON.stringify(s, null, 2);
          r = r.endsWith(".json") ? r : `${r}.json`;
          const d = new Blob([a], { type: "application/json" });
          h(d, r);
          break;
        }
      }
      this.lastExportInfo = { format: e, timestamp: /* @__PURE__ */ new Date() }, this.emit("export-complete", {
        format: e,
        fileName: r,
        rowCount: o.length,
        columnCount: l.length
      });
    } finally {
      this.isExportingFlag = !1;
    }
  }
  getSelectionState() {
    try {
      return this.grid?.getPluginState?.("selection") ?? null;
    } catch {
      return null;
    }
  }
  // #endregion
  // #region Public API
  /**
   * Export data to CSV format.
   * @param params - Optional export parameters
   */
  exportCsv(e) {
    this.performExport("csv", e);
  }
  /**
   * Export data to Excel format (XML Spreadsheet).
   * @param params - Optional export parameters
   */
  exportExcel(e) {
    this.performExport("excel", e);
  }
  /**
   * Export data to JSON format.
   * @param params - Optional export parameters
   */
  exportJson(e) {
    this.performExport("json", e);
  }
  /**
   * Check if an export is currently in progress.
   * @returns Whether export is in progress
   */
  isExporting() {
    return this.isExportingFlag;
  }
  /**
   * Get information about the last export.
   * @returns Export info or null if no export has occurred
   */
  getLastExport() {
    return this.lastExportInfo;
  }
  // #endregion
}
export {
  I as ExportPlugin
};
//# sourceMappingURL=index.js.map
