function b(s) {
  s && s.querySelectorAll(".cell-focus").forEach((t) => t.classList.remove("cell-focus"));
}
const m = 'input,select,textarea,[contenteditable="true"],[contenteditable=""],[tabindex]:not([tabindex="-1"])', R = document.createElement("template");
R.innerHTML = '<div class="cell" role="gridcell" part="cell"></div>';
const y = document.createElement("template");
y.innerHTML = '<div class="data-grid-row" role="row" part="row"></div>';
function u(s, t) {
  if (s._virtualization?.enabled) {
    const { rowHeight: r, container: o, viewportEl: l } = s._virtualization, d = o, h = l?.clientHeight ?? d?.clientHeight ?? 0;
    if (d && h > 0) {
      const c = s._focusRow * r;
      c < d.scrollTop ? d.scrollTop = c : c + r > d.scrollTop + h && (d.scrollTop = c - h + r);
    }
  }
  const e = s._activeEditRows !== void 0 && s._activeEditRows !== -1;
  e || s.refreshVirtualWindow(!1), b(s._bodyEl), Array.from(s._bodyEl.querySelectorAll('[aria-selected="true"]')).forEach((r) => {
    r.setAttribute("aria-selected", "false");
  });
  const i = s._focusRow, n = s._virtualization.start ?? 0, a = s._virtualization.end ?? s._rows.length;
  if (i >= n && i < a) {
    const r = s._bodyEl.querySelectorAll(".data-grid-row")[i - n];
    let o = r?.children[s._focusCol];
    if ((!o || !o.classList?.contains("cell")) && (o = r?.querySelector(`.cell[data-col="${s._focusCol}"]`) ?? r?.querySelector(".cell[data-col]")), o) {
      o.classList.add("cell-focus"), o.setAttribute("aria-selected", "true");
      const l = s.querySelector(".tbw-scroll-area");
      if (l && o && !e) {
        const d = s._getHorizontalScrollOffsets?.(r ?? void 0, o) ?? { left: 0, right: 0 };
        if (!d.skipScroll) {
          const h = o.getBoundingClientRect(), c = l.getBoundingClientRect(), g = h.left - c.left + l.scrollLeft, f = g + h.width, w = l.scrollLeft + d.left, v = l.scrollLeft + l.clientWidth - d.right;
          g < w ? l.scrollLeft = g - d.left : f > v && (l.scrollLeft = f - l.clientWidth + d.right);
        }
      }
      if (s._activeEditRows !== void 0 && s._activeEditRows !== -1 && o.classList.contains("editing")) {
        const d = o.querySelector(m);
        if (d && document.activeElement !== d)
          try {
            d.focus({ preventScroll: !0 });
          } catch {
          }
      } else if (!o.contains(document.activeElement)) {
        o.hasAttribute("tabindex") || o.setAttribute("tabindex", "-1");
        try {
          o.focus({ preventScroll: !0 });
        } catch {
        }
      }
    }
  }
}
const p = '<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>', C = {
  expand: "▶",
  collapse: "▼",
  sortAsc: "▲",
  sortDesc: "▼",
  sortNone: "⇅",
  submenuArrow: "▶",
  dragHandle: "⋮⋮",
  toolPanel: "☰",
  filter: p,
  filterActive: p,
  print: "🖨️"
};
class _ {
  /**
   * 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(t = {}) {
    this.userConfig = t;
  }
  /**
   * 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(t) {
    this.#e?.abort(), this.#e = new AbortController(), 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.
   *
   * @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(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 }));
  }
  /**
   * Emit a cancelable custom event from the grid.
   * @returns `true` if the event was cancelled (preventDefault called), `false` otherwise
   */
  emitCancelable(t, e) {
    const i = new CustomEvent(t, { detail: e, bubbles: !0, cancelable: !0 });
    return this.grid?.dispatchEvent?.(i), i.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 t = this.grid?.gridConfig?.icons ?? {};
    return { ...C, ...t };
  }
  // #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 t = this.grid?.effectiveConfig?.animation?.mode ?? "reduced-motion";
    if (t === !1 || t === "off") return !1;
    if (t === !0 || t === "on") return !0;
    const e = this.gridElement;
    return e ? getComputedStyle(e).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 t = this.gridElement;
    if (t) {
      const e = getComputedStyle(t).getPropertyValue("--tbw-animation-duration").trim(), i = parseInt(e, 10);
      if (!isNaN(i)) return i;
    }
    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(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
}
const E = 'tbw-grid[data-responsive-animate] .data-grid-row,tbw-grid[data-responsive-animate] .data-grid-row>.cell{transition:opacity var(--tbw-responsive-duration, .2s) ease-out,transform var(--tbw-responsive-duration, .2s) ease-out}tbw-grid[data-responsive][data-responsive-animate] .data-grid-row{animation:responsive-card-enter var(--tbw-responsive-duration, .2s) ease-out}@keyframes responsive-card-enter{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}tbw-grid[data-responsive] .header{display:none!important}tbw-grid[data-responsive] .footer-row{display:none}tbw-grid[data-responsive] .tbw-scroll-area{overflow-x:hidden;min-width:0!important}tbw-grid[data-responsive] .rows-body-wrapper{min-width:0!important}tbw-grid[data-responsive] .data-grid-row:not(.group-row){display:block!important;grid-template-columns:none!important;padding:var(--tbw-cell-padding);padding-left:var(--tbw-spacing-xl);border-bottom:1px solid var(--tbw-color-border);min-height:auto!important;height:auto!important;contain:none!important;content-visibility:visible!important;background:var(--tbw-color-bg);position:relative}tbw-grid[data-responsive] .data-grid-row:not(.group-row):nth-child(2n){background:var(--tbw-color-row-alt)}tbw-grid[data-responsive] .data-grid-row:not(.group-row):hover{background:var(--tbw-color-row-hover)}tbw-grid[data-responsive] .data-grid-row:not(.group-row)[aria-selected=true]{background:var(--tbw-color-selection)}tbw-grid[data-responsive] .data-grid-row:not(.group-row)[aria-selected=true]:before{content:"";position:absolute;left:0;top:0;bottom:0;width:4px;background:var(--tbw-color-accent)}tbw-grid[data-responsive] .data-grid-row:not(.group-row)>.cell{display:flex!important;justify-content:space-between;align-items:center;padding:var(--tbw-spacing-xs) var(--tbw-spacing-md);width:100%!important;min-width:0!important;min-height:auto!important;height:auto!important;line-height:1.5!important;position:static!important;left:auto!important;right:auto!important;border:none!important;border-bottom:none!important;border-right:none!important;background:transparent!important;white-space:normal!important;overflow:visible!important}tbw-grid[data-responsive] .data-grid-row:not(.group-row)>.cell:before{content:attr(data-header) ": ";font-weight:600;color:var(--tbw-color-header-fg);flex-shrink:0;margin-right:var(--tbw-spacing-md);min-width:100px}tbw-grid[data-responsive] .data-grid-row:not(.group-row)>.cell:after{content:none}tbw-grid[data-responsive] .cell[data-utility]{display:none!important}tbw-grid[data-responsive] .cell[data-responsive-hidden]{display:none!important}tbw-grid[data-responsive] .cell[data-responsive-value-only]{justify-content:flex-start!important;font-weight:500}tbw-grid[data-responsive] .cell[data-responsive-value-only]:before{display:none!important}tbw-grid:not([data-responsive]) .cell[data-responsive-hidden]{display:none!important}tbw-grid[data-responsive] .tbw-footer,tbw-grid[data-responsive] .tbw-pinned-rows,tbw-grid[data-responsive] .tbw-aggregation-rows{display:none!important}tbw-grid[data-responsive] .tbw-pinned-rows,tbw-grid[data-responsive] .tbw-aggregation-rows,tbw-grid[data-responsive] .tbw-aggregation-row{min-width:0!important}tbw-grid[data-responsive] .data-grid-row.responsive-card{display:block!important;padding:var(--tbw-cell-padding);border-bottom:1px solid var(--tbw-color-border)}tbw-grid[data-responsive] .data-grid-row.responsive-card>*{width:100%}tbw-grid[data-responsive] .data-grid-row.responsive-card .cell:before{display:none}';
class A extends _ {
  name = "responsive";
  version = "1.0.0";
  styles = E;
  /**
   * Plugin manifest declaring incompatibilities with other plugins.
   */
  static manifest = {
    incompatibleWith: [
      {
        name: "groupingRows",
        reason: "Responsive card layout does not yet support row grouping. The variable row heights (cards vs group headers) cause scroll calculation issues."
      }
    ]
  };
  #e;
  #t = !1;
  #n;
  #w = !1;
  #a = 0;
  /** Set of column fields to completely hide */
  #s = /* @__PURE__ */ new Set();
  /** Set of column fields to show value only (no header label) */
  #d = /* @__PURE__ */ new Set();
  /** Currently active breakpoint, or null if none */
  #l = null;
  /** Sorted breakpoints from largest to smallest */
  #c = [];
  /**
   * Check if currently in responsive mode.
   * @returns `true` if the grid is in card layout mode
   */
  isResponsive() {
    return this.#t;
  }
  /**
   * Force responsive mode regardless of width.
   * Useful for testing or manual control.
   * @param enabled - Whether to enable responsive mode
   */
  setResponsive(t) {
    t !== this.#t && (this.#t = t, this.#u(), this.emit("responsive-change", {
      isResponsive: t,
      width: this.#a,
      breakpoint: this.config.breakpoint ?? 0
    }));
  }
  /**
   * Update breakpoint dynamically.
   * @param width - New breakpoint width in pixels
   */
  setBreakpoint(t) {
    this.config.breakpoint = t, this.#v(this.#a);
  }
  /**
   * Set a custom card renderer.
   * This allows framework adapters to provide template-based renderers at runtime.
   * @param renderer - The card renderer function, or undefined to use default
   */
  setCardRenderer(t) {
    this.config.cardRenderer = t, this.#t && this.requestRender();
  }
  /**
   * Get current grid width.
   * @returns Width of the grid element in pixels
   */
  getWidth() {
    return this.#a;
  }
  /**
   * Get the currently active breakpoint config (multi-breakpoint mode only).
   * @returns The active BreakpointConfig, or null if no breakpoint is active
   */
  getActiveBreakpoint() {
    return this.#l;
  }
  attach(t) {
    super.attach(t), this.#h(this.config.hiddenColumns), this.config.breakpoints?.length && (this.#c = [...this.config.breakpoints].sort((e, i) => i.maxWidth - e.maxWidth)), this.#e = new ResizeObserver((e) => {
      const i = e[0]?.contentRect.width ?? 0;
      this.#a = i, clearTimeout(this.#n), this.#n = setTimeout(() => {
        this.#v(i);
      }, this.config.debounceMs ?? 100);
    }), this.#e.observe(this.gridElement);
  }
  /**
   * Build the hidden and value-only column sets from config.
   */
  #h(t) {
    if (this.#s.clear(), this.#d.clear(), !!t)
      for (const e of t)
        typeof e == "string" ? this.#s.add(e) : e.showValue ? this.#d.add(e.field) : this.#s.add(e.field);
  }
  detach() {
    this.#e?.disconnect(), this.#e = void 0, clearTimeout(this.#n), this.#n = void 0, this.gridElement && this.gridElement.removeAttribute("data-responsive"), super.detach();
  }
  /**
   * Apply hidden and value-only columns.
   * In legacy mode (single breakpoint), only applies when in responsive mode.
   * In multi-breakpoint mode, applies whenever there's an active breakpoint.
   */
  afterRender() {
    if (this.#_(), !(this.#c.length > 0 ? this.#l !== null : this.#t))
      return;
    const e = this.#s.size > 0, i = this.#d.size > 0;
    if (!e && !i)
      return;
    const n = this.gridElement.querySelectorAll(".cell[data-field]");
    for (const a of n) {
      const r = a.getAttribute("data-field");
      r && (this.#s.has(r) ? (a.setAttribute("data-responsive-hidden", ""), a.removeAttribute("data-responsive-value-only")) : this.#d.has(r) ? (a.setAttribute("data-responsive-value-only", ""), a.removeAttribute("data-responsive-hidden")) : (a.removeAttribute("data-responsive-hidden"), a.removeAttribute("data-responsive-value-only")));
    }
  }
  /**
   * Check if width has crossed any breakpoint threshold.
   * Handles both single breakpoint (legacy) and multi-breakpoint modes.
   */
  #v(t) {
    if (this.#c.length > 0) {
      this.#R(t);
      return;
    }
    const e = this.config.breakpoint ?? 0;
    e === 0 && !this.#w && (this.#w = !0, console.warn(
      "[tbw-grid:ResponsivePlugin] No breakpoint configured. Responsive mode is disabled. Set a breakpoint based on your grid's column count."
    ));
    const i = e > 0 && t < e;
    i !== this.#t && (this.#t = i, this.#u(), this.emit("responsive-change", {
      isResponsive: i,
      width: t,
      breakpoint: e
    }), this.requestRender());
  }
  /**
   * Check breakpoints in multi-breakpoint mode.
   * Evaluates breakpoints from largest to smallest, applying the first match.
   */
  #R(t) {
    let e = null;
    for (const n of this.#c)
      t <= n.maxWidth && (e = n);
    if (e !== this.#l) {
      this.#l = e, e?.hiddenColumns ? this.#h(e.hiddenColumns) : this.#h(this.config.hiddenColumns);
      const n = e?.cardLayout === !0;
      n !== this.#t && (this.#t = n, this.#u()), this.emit("responsive-change", {
        isResponsive: this.#t,
        width: t,
        breakpoint: e?.maxWidth ?? 0
      }), this.requestRender();
    }
  }
  /** Original row height before entering responsive mode, for restoration on exit */
  #i;
  /**
   * Apply the responsive state to the grid element.
   * Handles scroll reset when entering responsive mode and row height restoration on exit.
   */
  #u() {
    this.gridElement.toggleAttribute("data-responsive", this.#t);
    const t = this.config.animate !== !1;
    this.gridElement.toggleAttribute("data-responsive-animate", t), this.config.animationDuration && this.gridElement.style.setProperty("--tbw-responsive-duration", `${this.config.animationDuration}ms`);
    const e = this.grid;
    if (this.#t) {
      e._virtualization && (this.#i = e._virtualization.rowHeight);
      const i = this.gridElement.querySelector(".tbw-scroll-area");
      i && (i.scrollLeft = 0);
    } else {
      const i = this.gridElement.querySelectorAll(".data-grid-row");
      for (const n of i)
        n.style.height = "", n.classList.remove("responsive-card");
      this.#i && this.#i > 0 && e._virtualization && (e._virtualization.rowHeight = this.#i, this.#i = void 0), this.#r = void 0, this.#o = void 0, this.#g = void 0;
    }
  }
  /**
   * Custom row rendering when cardRenderer is provided and in responsive mode.
   *
   * When a cardRenderer is configured, this hook takes over row rendering to display
   * the custom card layout instead of the default cell structure.
   *
   * @param row - The row data object
   * @param rowEl - The row DOM element to render into
   * @param rowIndex - The index of the row in the data array
   * @returns `true` if rendered (prevents default), `void` for default rendering
   */
  renderRow(t, e, i) {
    if (!this.#t || !this.config.cardRenderer || t.__isGroupRow)
      return;
    e.replaceChildren();
    const n = this.config.cardRenderer(t, i);
    e.className = "data-grid-row responsive-card";
    const a = this.config.cardRowHeight ?? "auto";
    return a !== "auto" ? e.style.height = `${a}px` : e.style.height = "auto", e.appendChild(n), !0;
  }
  /**
   * Handle keyboard navigation in responsive mode.
   *
   * In responsive mode, the visual layout is inverted:
   * - Cells are stacked vertically within each "card" (row)
   * - DOWN/UP visually moves within the card (between fields)
   * - Page Down/Page Up or Ctrl+Down/Up moves between cards
   *
   * For custom cardRenderers, keyboard navigation is disabled entirely
   * since the implementor controls the card content and should handle
   * navigation via their own event handlers.
   *
   * @returns `true` if the event was handled and default behavior should be prevented
   */
  onKeyDown(t) {
    if (!this.#t || this.config.cardRenderer && ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(t.key))
      return !1;
    const e = this.rows.length - 1, i = this.visibleColumns.length - 1;
    switch (t.key) {
      case "ArrowDown":
        if (this.grid._focusCol < i)
          return this.grid._focusCol += 1, t.preventDefault(), u(this.grid), !0;
        if (this.grid._focusRow < e)
          return this.grid._focusRow += 1, this.grid._focusCol = 0, t.preventDefault(), u(this.grid), !0;
        break;
      case "ArrowUp":
        if (this.grid._focusCol > 0)
          return this.grid._focusCol -= 1, t.preventDefault(), u(this.grid), !0;
        if (this.grid._focusRow > 0)
          return this.grid._focusRow -= 1, this.grid._focusCol = i, t.preventDefault(), u(this.grid), !0;
        break;
      case "ArrowRight":
        if (this.grid._focusRow < e)
          return this.grid._focusRow += 1, t.preventDefault(), u(this.grid), !0;
        break;
      case "ArrowLeft":
        if (this.grid._focusRow > 0)
          return this.grid._focusRow -= 1, t.preventDefault(), u(this.grid), !0;
        break;
    }
    return !1;
  }
  // ============================================
  // Variable Height Support for Mixed Row Types
  // ============================================
  /** Measured card height from DOM for virtualization calculations */
  #r;
  /** Measured group row height from DOM for virtualization calculations */
  #o;
  /** Last known card row count for detecting changes (e.g., group expand/collapse) */
  #g;
  /**
   * Get the effective card height for virtualization calculations.
   * Prioritizes DOM-measured height (actual rendered size) over config,
   * since content can overflow the configured height.
   */
  #b() {
    if (this.#r && this.#r > 0)
      return this.#r;
    const t = this.config.cardRowHeight;
    return typeof t == "number" && t > 0 ? t : 80;
  }
  /**
   * Get the effective group row height for virtualization calculations.
   * Uses DOM-measured height, falling back to original row height.
   */
  #m() {
    return this.#o && this.#o > 0 ? this.#o : this.#i ?? 28;
  }
  /**
   * Check if there are any group rows in the current dataset.
   * Used to determine if we have mixed row heights.
   */
  #f() {
    for (const t of this.rows)
      if (t.__isGroupRow)
        return !0;
    return !1;
  }
  /**
   * Count group rows and card rows in the current dataset.
   */
  #y() {
    let t = 0, e = 0;
    for (const i of this.rows)
      i.__isGroupRow ? t++ : e++;
    return { groupCount: t, cardCount: e };
  }
  /**
   * Return total extra height contributed by mixed row heights.
   * This is called by the grid's virtualization system to adjust scrollbar height.
   *
   * The grid calculates: totalRows * baseRowHeight + pluginExtraHeight
   *
   * For mixed layouts (groups + cards), we need to report the difference between
   * actual heights and what the base calculation assumes:
   * - Extra for groups: groupCount * (groupHeight - baseHeight)
   * - Extra for cards: cardCount * (cardHeight - baseHeight)
   */
  getExtraHeight() {
    if (!this.#t || !this.config.cardRenderer || !this.#f())
      return 0;
    const t = this.#i ?? 28, e = this.#m(), i = this.#b(), { groupCount: n, cardCount: a } = this.#y(), r = n * Math.max(0, e - t), o = a * Math.max(0, i - t);
    return r + o;
  }
  /**
   * Return extra height that appears before a given row index.
   * Used by virtualization to correctly calculate scroll positions.
   *
   * Like getExtraHeight, this accounts for both group and card row heights.
   */
  getExtraHeightBefore(t) {
    if (!this.#t || !this.config.cardRenderer || !this.#f())
      return 0;
    const e = this.#i ?? 28, i = this.#m(), n = this.#b(), a = Math.max(0, i - e), r = Math.max(0, n - e);
    let o = 0, l = 0;
    const d = this.rows, h = Math.min(t, d.length);
    for (let c = 0; c < h; c++)
      d[c].__isGroupRow ? o++ : l++;
    return o * a + l * r;
  }
  /**
   * Count the number of card rows (non-group rows) in the current dataset.
   */
  #C() {
    let t = 0;
    for (const e of this.rows)
      e.__isGroupRow || t++;
    return t;
  }
  /** Pending refresh scheduled via microtask */
  #p = !1;
  /**
   * Measure card height from DOM after render and detect row count changes.
   * Called in afterRender to ensure scroll calculations are accurate.
   *
   * This handles two scenarios:
   * 1. Card height changes (content overflow, dynamic sizing)
   * 2. Card row count changes (group expand/collapse)
   * 3. Group row height changes
   *
   * For uniform card layouts (no groups), we update the virtualization row height
   * directly to the card height. For mixed layouts (groups + cards), we use the
   * getExtraHeight mechanism to report height differences.
   *
   * The refresh is deferred via microtask to avoid nested render cycles.
   */
  #_() {
    if (!this.#t || !this.config.cardRenderer)
      return;
    let t = !1;
    const e = this.grid, i = this.#f(), n = this.#C();
    if (n !== this.#g && (this.#g = n, t = !0), i) {
      const r = this.gridElement.querySelector(".data-grid-row.group-row");
      if (r) {
        const o = r.getBoundingClientRect().height;
        o > 0 && o !== this.#o && (this.#o = o, t = !0);
      }
    }
    const a = this.gridElement.querySelector(".data-grid-row.responsive-card");
    if (a) {
      const r = a.getBoundingClientRect().height;
      r > 0 && r !== this.#r && (this.#r = r, t = !0, !i && e._virtualization && (e._virtualization.rowHeight = r));
    }
    t && !this.#p && (this.#p = !0, queueMicrotask(() => {
      this.#p = !1, this.grid && this.#t && this.grid.refreshVirtualWindow?.(!0, !0);
    }));
  }
}
export {
  A as ResponsivePlugin
};
//# sourceMappingURL=index.js.map
