const d = {
  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, 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 { ...d, ...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 c(s, e) {
  return Math.floor(s / e);
}
function u(s, e) {
  return {
    start: s * e,
    end: (s + 1) * e
  };
}
function h(s, e, t) {
  const o = c(s, t), i = c(e - 1, t), r = [];
  for (let n = o; n <= i; n++)
    r.push(n);
  return r;
}
async function l(s, e, t, o) {
  const i = u(e, t);
  return s.getRows({
    startRow: i.start,
    endRow: i.end,
    sortModel: o.sortModel,
    filterModel: o.filterModel
  });
}
function g(s, e, t) {
  const o = c(s, e), i = t.get(o);
  if (!i) return;
  const r = s % e;
  return i[r];
}
const f = 100;
class R extends a {
  name = "serverSide";
  version = "1.0.0";
  get defaultConfig() {
    return {
      pageSize: 100,
      cacheBlockSize: 100,
      maxConcurrentRequests: 2
    };
  }
  // #region Internal State
  dataSource = null;
  totalRowCount = 0;
  loadedBlocks = /* @__PURE__ */ new Map();
  loadingBlocks = /* @__PURE__ */ new Set();
  lastRequestId = 0;
  scrollDebounceTimer;
  // #endregion
  // #region Lifecycle
  detach() {
    this.dataSource = null, this.totalRowCount = 0, this.loadedBlocks.clear(), this.loadingBlocks.clear(), this.lastRequestId = 0, this.scrollDebounceTimer && (clearTimeout(this.scrollDebounceTimer), this.scrollDebounceTimer = void 0);
  }
  // #endregion
  // #region Private Methods
  /**
   * Check current viewport and load any missing blocks.
   */
  loadRequiredBlocks() {
    if (!this.dataSource) return;
    const e = this.grid, t = this.config.cacheBlockSize ?? 100, o = { startRow: e.virtualization.start, endRow: e.virtualization.end }, i = h(o.startRow, o.endRow, t);
    for (const r of i)
      if (!(this.loadedBlocks.has(r) || this.loadingBlocks.has(r))) {
        if (this.loadingBlocks.size >= (this.config.maxConcurrentRequests ?? 2))
          break;
        this.loadingBlocks.add(r), l(this.dataSource, r, t, {}).then((n) => {
          this.loadedBlocks.set(r, n.rows), this.totalRowCount = n.totalRowCount, this.loadingBlocks.delete(r), this.requestRender(), this.loadRequiredBlocks();
        }).catch(() => {
          this.loadingBlocks.delete(r);
        });
      }
  }
  // #endregion
  // #region Hooks
  processRows(e) {
    if (!this.dataSource) return [...e];
    const t = [];
    for (let o = 0; o < this.totalRowCount; o++) {
      const i = g(o, this.config.cacheBlockSize ?? 100, this.loadedBlocks);
      t.push(i ?? { __loading: !0, __index: o });
    }
    return t;
  }
  onScroll(e) {
    this.dataSource && (this.loadRequiredBlocks(), this.scrollDebounceTimer && clearTimeout(this.scrollDebounceTimer), this.scrollDebounceTimer = setTimeout(() => {
      this.loadRequiredBlocks();
    }, f));
  }
  // #endregion
  // #region Public API
  /**
   * Set the data source for server-side loading.
   * @param dataSource - Data source implementing the getRows method
   */
  setDataSource(e) {
    this.dataSource = e, this.loadedBlocks.clear(), this.loadingBlocks.clear();
    const t = this.config.cacheBlockSize ?? 100;
    l(e, 0, t, {}).then((o) => {
      this.loadedBlocks.set(0, o.rows), this.totalRowCount = o.totalRowCount, this.requestRender();
    });
  }
  /**
   * Refresh all data from the server.
   */
  refresh() {
    this.dataSource && (this.loadedBlocks.clear(), this.loadingBlocks.clear(), this.requestRender());
  }
  /**
   * Clear all cached data without refreshing.
   */
  purgeCache() {
    this.loadedBlocks.clear();
  }
  /**
   * Get the total row count from the server.
   */
  getTotalRowCount() {
    return this.totalRowCount;
  }
  /**
   * Check if a specific row is loaded in the cache.
   * @param rowIndex - Row index to check
   */
  isRowLoaded(e) {
    const t = this.config.cacheBlockSize ?? 100, o = c(e, t);
    return this.loadedBlocks.has(o);
  }
  /**
   * Get the number of loaded cache blocks.
   */
  getLoadedBlockCount() {
    return this.loadedBlocks.size;
  }
  // #endregion
}
export {
  R as ServerSidePlugin
};
//# sourceMappingURL=index.js.map
