function Y(N) {
  const { totalRows: e, viewportHeight: t, scrollTop: r, rowHeight: a, overscan: l } = N, c = Math.ceil(t / a);
  let d = Math.floor(r / a) - l;
  d < 0 && (d = 0);
  let i = d + c + l * 2;
  return i > e && (i = e), i === e && d > 0 && (d = Math.max(0, i - c - l * 2)), {
    start: d,
    end: i,
    offsetY: d * a,
    totalHeight: e * a
  };
}
function G(N, e) {
  return N <= e;
}
const q = '<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>', $ = {
  expand: "▶",
  collapse: "▼",
  sortAsc: "▲",
  sortDesc: "▼",
  sortNone: "⇅",
  submenuArrow: "▶",
  dragHandle: "⋮⋮",
  toolPanel: "☰",
  filter: q,
  filterActive: q,
  print: "🖨️"
};
class K {
  /**
   * 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 r = new CustomEvent(e, { detail: t, bubbles: !0, cancelable: !0 });
    return this.grid?.dispatchEvent?.(r), r.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 { ...$, ...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(), r = parseInt(t, 10);
      if (!isNaN(r)) return r;
    }
    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 W(N) {
  return N.meta?.utility === !0;
}
function j(N, e, t = !1) {
  const r = N[e.field];
  if (e.operator === "blank")
    return r == null || r === "";
  if (e.operator === "notBlank")
    return r != null && r !== "";
  if (r == null) return !1;
  const a = String(r), l = t ? a : a.toLowerCase(), c = t ? String(e.value) : String(e.value).toLowerCase();
  switch (e.operator) {
    // Text operators
    case "contains":
      return l.includes(c);
    case "notContains":
      return !l.includes(c);
    case "equals":
      return l === c;
    case "notEquals":
      return l !== c;
    case "startsWith":
      return l.startsWith(c);
    case "endsWith":
      return l.endsWith(c);
    // Number/Date operators (use raw numeric values)
    case "lessThan":
      return Number(r) < Number(e.value);
    case "lessThanOrEqual":
      return Number(r) <= Number(e.value);
    case "greaterThan":
      return Number(r) > Number(e.value);
    case "greaterThanOrEqual":
      return Number(r) >= Number(e.value);
    case "between":
      return Number(r) >= Number(e.value) && Number(r) <= Number(e.valueTo);
    // Set operators
    case "in":
      return Array.isArray(e.value) && e.value.includes(r);
    case "notIn":
      return Array.isArray(e.value) && !e.value.includes(r);
    default:
      return !0;
  }
}
function U(N, e, t = !1) {
  return e.length ? N.filter((r) => e.every((a) => j(r, a, t))) : N;
}
function J(N) {
  return JSON.stringify(
    N.map((e) => ({
      field: e.field,
      operator: e.operator,
      value: e.value,
      valueTo: e.valueTo
    }))
  );
}
function B(N, e) {
  const t = /* @__PURE__ */ new Set();
  for (const r of N) {
    const a = r[e];
    a != null && t.add(a);
  }
  return [...t].sort((r, a) => typeof r == "number" && typeof a == "number" ? r - a : String(r).localeCompare(String(a)));
}
const Q = '@layer tbw-plugins{tbw-grid .tbw-quick-filter-input{flex:1;max-width:300px;height:var(--tbw-input-height, 1.75rem);padding:var(--tbw-input-padding, 0 .5rem);border:1px solid var(--tbw-color-border);border-radius:var(--tbw-border-radius);background:var(--tbw-color-bg);color:var(--tbw-color-fg);font-size:var(--tbw-font-size-sm, .8125rem)}tbw-grid .tbw-quick-filter-input:focus{outline:none;border-color:var(--tbw-color-accent)}tbw-grid .header-cell.filtered:before{content:"";position:absolute;top:var(--tbw-spacing-xs, .25rem);right:var(--tbw-spacing-xs, .25rem);width:var(--tbw-indicator-size, .375rem);height:var(--tbw-indicator-size, .375rem);background:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));border-radius:50%}tbw-grid .tbw-filter-btn{display:var(--tbw-filter-btn-display, inline-flex);visibility:var(--tbw-filter-btn-visibility, visible);align-items:center;justify-content:center;background:transparent;border:none;cursor:pointer;padding:2px;margin-left:var(--tbw-spacing-xs, .25rem);opacity:.4;transition:opacity .15s,visibility 0s,display 0s allow-discrete;color:inherit;vertical-align:middle;transition-behavior:allow-discrete}tbw-grid .tbw-filter-btn:hover,tbw-grid .tbw-filter-btn.active{opacity:1;visibility:visible;display:inline-flex}tbw-grid .tbw-filter-btn.active{color:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6))}tbw-grid .header-row .cell:hover .tbw-filter-btn,tbw-grid .header-row .cell.filtered .tbw-filter-btn{display:inline-flex;visibility:visible}}', X = "@layer tbw-plugins{.tbw-filter-panel{position:fixed;background:var(--tbw-filter-panel-bg, var(--tbw-color-panel-bg, light-dark(#eeeeee, #222222)));color:var(--tbw-filter-panel-fg, var(--tbw-color-fg, light-dark(#222222, #eeeeee)));border:1px solid var(--tbw-filter-panel-border, var(--tbw-color-border, light-dark(#d0d0d4, #454545)));border-radius:var(--tbw-filter-panel-radius, var(--tbw-border-radius, .25rem));box-shadow:0 4px 16px var(--tbw-filter-panel-shadow, var(--tbw-color-shadow, light-dark(rgba(0, 0, 0, .1), rgba(0, 0, 0, .3))));padding:var(--tbw-panel-padding, var(--tbw-spacing-lg, .75rem));z-index:10000;min-width:200px;max-width:280px;max-height:350px;display:flex;flex-direction:column;font-family:var(--tbw-font-family, system-ui, sans-serif);font-size:var(--tbw-font-size, .8125rem);transform-origin:top center}.tbw-filter-panel.tbw-filter-panel-above{transform-origin:bottom center}.tbw-filter-panel.tbw-filter-panel-animated{animation:tbw-filter-panel-enter var(--tbw-animation-duration, .15s) var(--tbw-animation-easing, ease-out)}.tbw-filter-panel.tbw-filter-panel-above.tbw-filter-panel-animated{animation:tbw-filter-panel-enter-above var(--tbw-animation-duration, .15s) var(--tbw-animation-easing, ease-out)}@keyframes tbw-filter-panel-enter{0%{opacity:0;transform:scaleY(.3) translateY(-10px)}to{opacity:1;transform:scaleY(1) translateY(0)}}@keyframes tbw-filter-panel-enter-above{0%{opacity:0;transform:scaleY(.3) translateY(10px)}to{opacity:1;transform:scaleY(1) translateY(0)}}@supports (anchor-name: --test){.tbw-filter-panel{position-anchor:--tbw-filter-anchor;top:anchor(bottom);left:anchor(left);margin-top:4px;position-try-fallbacks:flip-inline,flip-block,flip-block flip-inline}}.tbw-filter-search{margin-bottom:var(--tbw-panel-gap, var(--tbw-spacing-md, .5rem));min-height:var(--tbw-filter-item-height, 28px)}.tbw-filter-search-input{height:var(--tbw-filter-item-height, 28px);width:100%;padding:var(--tbw-filter-search-padding, var(--tbw-spacing-sm, .375rem) var(--tbw-spacing-md, .5rem));background:var(--tbw-filter-input-bg, var(--tbw-color-bg, transparent));color:inherit;border:1px solid var(--tbw-filter-input-border, var(--tbw-color-border, light-dark(#d0d0d4, #454545)));border-radius:var(--tbw-filter-input-radius, var(--tbw-border-radius, .25rem));font-size:inherit;box-sizing:border-box}.tbw-filter-search-input:focus{outline:none;border-color:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));box-shadow:0 0 0 2px rgba(from var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6)) r g b / 15%)}.tbw-filter-actions{display:flex;padding:var(--tbw-button-padding-sm, .25rem .125rem);margin-bottom:var(--tbw-panel-gap, var(--tbw-spacing-md, .5rem));border-bottom:1px solid var(--tbw-filter-divider, var(--tbw-color-border, light-dark(#d0d0d4, #454545)));min-height:var(--tbw-filter-item-height, 28px)}.tbw-filter-action-btn{background:transparent;border:none;color:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));cursor:pointer;font-size:var(--tbw-font-size-xs, .75rem);padding:2px 0}.tbw-filter-action-btn:hover{text-decoration:underline}.tbw-filter-values{flex:1;overflow-y:auto;margin-bottom:var(--tbw-panel-gap, var(--tbw-spacing-md, .5rem));max-height:180px;position:relative}.tbw-filter-values-spacer{width:1px}.tbw-filter-values-content{position:absolute;top:0;left:0;right:0}.tbw-filter-value-item{display:flex;align-items:center;gap:var(--tbw-panel-gap, var(--tbw-spacing-md, .5rem));padding:var(--tbw-button-padding-sm, .25rem .125rem);cursor:pointer;border-radius:3px;height:var(--tbw-filter-item-height, 28px)}.tbw-filter-value-item:hover{background:var(--tbw-filter-hover, var(--tbw-color-row-hover, light-dark(#f0f6ff, #1c1c1c)))}.tbw-filter-checkbox{margin:0;cursor:pointer;accent-color:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6))}.tbw-filter-no-match{color:var(--tbw-filter-muted, var(--tbw-color-fg-muted, light-dark(#555555, #aaaaaa)));padding:var(--tbw-panel-gap, var(--tbw-spacing-md, .5rem)) 0;text-align:center;font-style:italic}.tbw-filter-buttons{display:flex;gap:var(--tbw-panel-gap, var(--tbw-spacing-md, .5rem));padding-top:var(--tbw-panel-gap, var(--tbw-spacing-md, .5rem));border-top:1px solid var(--tbw-filter-divider, var(--tbw-color-border, light-dark(#d0d0d4, #454545)))}.tbw-filter-apply-btn{flex:1;padding:var(--tbw-filter-btn-padding, var(--tbw-button-padding, .375rem .75rem));background:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));color:var(--tbw-filter-accent-fg, var(--tbw-color-accent-fg, light-dark(#ffffff, #000000)));border:none;border-radius:var(--tbw-border-radius, .25rem);cursor:pointer;font-size:var(--tbw-font-size-sm, .8125rem);font-weight:var(--tbw-filter-btn-font-weight, 500);min-height:var(--tbw-filter-btn-min-height, auto)}.tbw-filter-apply-btn:hover{filter:brightness(.9)}.tbw-filter-clear-btn{flex:1;padding:var(--tbw-filter-btn-padding, var(--tbw-button-padding, .375rem .75rem));background:transparent;color:var(--tbw-filter-muted, var(--tbw-color-fg-muted, light-dark(#555555, #aaaaaa)));border:1px solid var(--tbw-filter-input-border, var(--tbw-color-border, light-dark(#d0d0d4, #454545)));border-radius:var(--tbw-border-radius, .25rem);cursor:pointer;font-size:var(--tbw-font-size-sm, .8125rem);font-weight:var(--tbw-filter-btn-font-weight, 500);min-height:var(--tbw-filter-btn-min-height, auto)}.tbw-filter-clear-btn:hover{background:var(--tbw-filter-hover, var(--tbw-color-row-hover, light-dark(#f0f6ff, #1c1c1c)))}.tbw-filter-range-inputs,.tbw-filter-date-range{display:flex;align-items:flex-end;gap:var(--tbw-spacing-sm, .375rem);margin-bottom:var(--tbw-panel-gap, var(--tbw-spacing-md, .5rem))}.tbw-filter-range-group,.tbw-filter-date-group{display:flex;flex-direction:column;gap:var(--tbw-spacing-xs, .25rem);flex:1}.tbw-filter-range-label{font-size:var(--tbw-font-size-xs, .75rem);color:var(--tbw-filter-muted, var(--tbw-color-fg-muted, light-dark(#555555, #aaaaaa)))}.tbw-filter-range-input,.tbw-filter-date-input{width:100%;height:var(--tbw-filter-item-height, 28px);padding:var(--tbw-spacing-xs, .25rem) var(--tbw-spacing-sm, .375rem);background:var(--tbw-filter-input-bg, var(--tbw-color-bg, transparent));color:inherit;border:1px solid var(--tbw-filter-input-border, var(--tbw-color-border, light-dark(#d0d0d4, #454545)));border-radius:var(--tbw-filter-input-radius, var(--tbw-border-radius, .25rem));font-size:inherit;box-sizing:border-box}.tbw-filter-range-input:focus,.tbw-filter-date-input:focus{outline:none;border-color:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));box-shadow:0 0 0 2px rgba(from var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6)) r g b / 15%)}.tbw-filter-range-separator{color:var(--tbw-filter-muted, var(--tbw-color-fg-muted, light-dark(#555555, #aaaaaa)));padding-bottom:var(--tbw-spacing-xs, .25rem)}.tbw-filter-range-slider{position:relative;height:24px;margin:var(--tbw-spacing-md, .5rem) 0 var(--tbw-panel-gap, var(--tbw-spacing-md, .5rem))}.tbw-filter-range-track{position:absolute;top:50%;left:0;right:0;height:4px;background:var(--tbw-filter-input-border, var(--tbw-color-border, light-dark(#d0d0d4, #454545)));border-radius:2px;transform:translateY(-50%)}.tbw-filter-range-fill{position:absolute;top:50%;height:4px;background:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));border-radius:2px;transform:translateY(-50%)}.tbw-filter-range-thumb{position:absolute;top:0;width:100%;height:100%;background:none;pointer-events:none;-webkit-appearance:none;appearance:none}.tbw-filter-range-thumb::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:16px;height:16px;background:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));border:2px solid var(--tbw-filter-panel-bg, var(--tbw-color-panel-bg, light-dark(#eeeeee, #222222)));border-radius:50%;cursor:pointer;pointer-events:all;box-shadow:0 1px 3px #0003}.tbw-filter-range-thumb::-moz-range-thumb{width:16px;height:16px;background:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));border:2px solid var(--tbw-filter-panel-bg, var(--tbw-color-panel-bg, light-dark(#eeeeee, #222222)));border-radius:50%;cursor:pointer;pointer-events:all;box-shadow:0 1px 3px #0003}.tbw-filter-range-thumb::-webkit-slider-thumb:hover{transform:scale(1.1)}.tbw-filter-range-thumb::-moz-range-thumb:hover{transform:scale(1.1)}}";
class I extends K {
  /** @internal */
  name = "filtering";
  /** @internal */
  styles = Q;
  /** @internal */
  get defaultConfig() {
    return {
      debounceMs: 300,
      caseSensitive: !1,
      trimInput: !0,
      useWorker: !0
    };
  }
  // #region Helpers
  /**
   * Check if filtering is enabled at the grid level.
   * Grid-wide `filterable: false` disables filtering for all columns.
   */
  isFilteringEnabled() {
    return this.grid.effectiveConfig?.filterable !== !1;
  }
  /**
   * Check if a specific column is filterable, respecting both grid-level and column-level settings.
   */
  isColumnFilterable(e) {
    return this.isFilteringEnabled() ? e.filterable !== !1 : !1;
  }
  // #endregion
  // #region Internal State
  filters = /* @__PURE__ */ new Map();
  cachedResult = null;
  cacheKey = null;
  openPanelField = null;
  panelElement = null;
  panelAnchorElement = null;
  // For CSS anchor positioning cleanup
  searchText = /* @__PURE__ */ new Map();
  excludedValues = /* @__PURE__ */ new Map();
  panelAbortController = null;
  // For panel-scoped listeners
  globalStylesInjected = !1;
  // Virtualization constants for filter value list
  static DEFAULT_LIST_ITEM_HEIGHT = 28;
  static LIST_OVERSCAN = 3;
  static LIST_BYPASS_THRESHOLD = 50;
  // Don't virtualize if < 50 items
  /**
   * Get the item height from CSS variable or fallback to default.
   * Reads --tbw-filter-item-height from the panel element.
   */
  getListItemHeight() {
    if (this.panelElement) {
      const e = getComputedStyle(this.panelElement).getPropertyValue("--tbw-filter-item-height");
      if (e && e.trim()) {
        const t = parseFloat(e);
        if (!isNaN(t) && t > 0)
          return t;
      }
    }
    return I.DEFAULT_LIST_ITEM_HEIGHT;
  }
  /**
   * Sync excludedValues map from a filter model (for set filters).
   */
  syncExcludedValues(e, t) {
    t ? t.type === "set" && t.operator === "notIn" && Array.isArray(t.value) ? this.excludedValues.set(e, new Set(t.value)) : t.type === "set" && this.excludedValues.delete(e) : this.excludedValues.delete(e);
  }
  // #endregion
  // #region Lifecycle
  /** @internal */
  attach(e) {
    super.attach(e), this.injectGlobalStyles();
  }
  /** @internal */
  detach() {
    this.filters.clear(), this.cachedResult = null, this.cacheKey = null, this.openPanelField = null, this.panelElement && (this.panelElement.remove(), this.panelElement = null), this.searchText.clear(), this.excludedValues.clear(), this.panelAbortController?.abort(), this.panelAbortController = null;
  }
  // #endregion
  // #region Hooks
  /** @internal */
  processRows(e) {
    const t = [...this.filters.values()];
    if (!t.length) return [...e];
    if (this.config.filterHandler)
      return this.cachedResult ? this.cachedResult : [...e];
    const r = J(t);
    if (this.cacheKey === r && this.cachedResult)
      return this.cachedResult;
    const a = U([...e], t, this.config.caseSensitive);
    return this.cachedResult = a, this.cacheKey = r, a;
  }
  /** @internal */
  afterRender() {
    const e = this.gridElement;
    if (!e) return;
    e.querySelectorAll('[part~="header-cell"]').forEach((r) => {
      const a = r.getAttribute("data-col");
      if (a === null) return;
      const l = this.visibleColumns[parseInt(a, 10)];
      if (!l || !this.isColumnFilterable(l) || W(l)) return;
      const c = l.field;
      if (!c) return;
      const d = this.filters.has(c);
      let i = r.querySelector(".tbw-filter-btn");
      if (i) {
        const w = i.classList.contains("active");
        if (i.classList.toggle("active", d), r.classList.toggle("filtered", d), w !== d) {
          const u = d ? "filterActive" : "filter";
          this.setIcon(i, this.resolveIcon(u));
        }
        return;
      }
      i = document.createElement("button"), i.className = "tbw-filter-btn", i.setAttribute("aria-label", `Filter ${l.header ?? c}`);
      const m = d ? "filterActive" : "filter";
      this.setIcon(i, this.resolveIcon(m)), d && (i.classList.add("active"), r.classList.add("filtered")), i.addEventListener("click", (w) => {
        w.stopPropagation(), this.toggleFilterPanel(c, l, i);
      });
      const k = r.querySelector(".resize-handle");
      k ? r.insertBefore(i, k) : r.appendChild(i);
    });
  }
  // #endregion
  // #region Public API
  /**
   * Set a filter on a specific field.
   * Pass null to remove the filter.
   */
  setFilter(e, t) {
    if (t === null)
      this.filters.delete(e), this.syncExcludedValues(e, null);
    else {
      const r = { ...t, field: e };
      this.filters.set(e, r), this.syncExcludedValues(e, r);
    }
    this.cachedResult = null, this.cacheKey = null, this.emit("filter-change", {
      filters: [...this.filters.values()],
      filteredRowCount: 0
      // Will be accurate after processRows
    }), this.requestRender();
  }
  /**
   * Get the current filter for a field.
   */
  getFilter(e) {
    return this.filters.get(e);
  }
  /**
   * Get all active filters.
   */
  getFilters() {
    return [...this.filters.values()];
  }
  /**
   * Alias for getFilters() to match functional API naming.
   */
  getFilterModel() {
    return this.getFilters();
  }
  /**
   * Set filters from an array (replaces all existing filters).
   */
  setFilterModel(e) {
    this.filters.clear(), this.excludedValues.clear();
    for (const t of e)
      this.filters.set(t.field, t), this.syncExcludedValues(t.field, t);
    this.cachedResult = null, this.cacheKey = null, this.emit("filter-change", {
      filters: [...this.filters.values()],
      filteredRowCount: 0
    }), this.requestRender();
  }
  /**
   * Clear all filters.
   */
  clearAllFilters() {
    this.filters.clear(), this.excludedValues.clear(), this.searchText.clear(), this.applyFiltersInternal();
  }
  /**
   * Clear filter for a specific field.
   */
  clearFieldFilter(e) {
    this.filters.delete(e), this.excludedValues.delete(e), this.searchText.delete(e), this.applyFiltersInternal();
  }
  /**
   * Check if a field has an active filter.
   */
  isFieldFiltered(e) {
    return this.filters.has(e);
  }
  /**
   * Get the count of filtered rows (from cache).
   */
  getFilteredRowCount() {
    return this.cachedResult?.length ?? this.rows.length;
  }
  /**
   * Get all active filters (alias for getFilters).
   */
  getActiveFilters() {
    return this.getFilters();
  }
  /**
   * Get unique values for a field (for set filter dropdowns).
   * Uses sourceRows to include all values regardless of current filter.
   */
  getUniqueValues(e) {
    return B(this.sourceRows, e);
  }
  // #endregion
  // #region Private Methods
  /**
   * Copy CSS classes and data attributes from grid to filter panel.
   * This ensures theme classes (e.g., .eds-theme) cascade to the panel.
   */
  copyGridThemeContext(e) {
    const t = this.gridElement;
    if (!t) return;
    for (const a of t.classList)
      a.startsWith("tbw-") || a === "selecting" || e.classList.add(a);
    const r = t.dataset.theme;
    r && (e.dataset.theme = r);
  }
  /**
   * Inject global styles for filter panel (rendered in document.body)
   */
  injectGlobalStyles() {
    if (this.globalStylesInjected) return;
    if (document.getElementById("tbw-filter-panel-styles")) {
      this.globalStylesInjected = !0;
      return;
    }
    const e = document.createElement("style");
    e.id = "tbw-filter-panel-styles", e.textContent = X, document.head.appendChild(e), this.globalStylesInjected = !0;
  }
  /**
   * Toggle the filter panel for a field
   */
  toggleFilterPanel(e, t, r) {
    if (this.openPanelField === e) {
      this.closeFilterPanel();
      return;
    }
    this.closeFilterPanel();
    const a = document.createElement("div");
    if (a.className = "tbw-filter-panel", this.copyGridThemeContext(a), this.isAnimationEnabled && a.classList.add("tbw-filter-panel-animated"), this.panelElement = a, this.openPanelField = e, this.config.valuesHandler) {
      a.innerHTML = '<div class="tbw-filter-loading">Loading...</div>', document.body.appendChild(a), this.positionPanel(a, r), this.setupPanelCloseHandler(a, r), this.config.valuesHandler(e, t).then((c) => {
        this.openPanelField !== e || !this.panelElement || (a.innerHTML = "", this.renderPanelContent(e, t, a, c));
      });
      return;
    }
    const l = B(this.sourceRows, e);
    document.body.appendChild(a), this.positionPanel(a, r), this.renderPanelContent(e, t, a, l), this.setupPanelCloseHandler(a, r);
  }
  /**
   * Render filter panel content with given values
   */
  renderPanelContent(e, t, r, a) {
    let l = this.excludedValues.get(e);
    l || (l = /* @__PURE__ */ new Set(), this.excludedValues.set(e, l));
    const c = this.searchText.get(e) ?? "", d = {
      field: e,
      column: t,
      uniqueValues: a,
      excludedValues: l,
      searchText: c,
      applySetFilter: (m) => {
        this.applySetFilter(e, m), this.closeFilterPanel();
      },
      applyTextFilter: (m, k, w) => {
        this.applyTextFilter(e, m, k, w), this.closeFilterPanel();
      },
      clearFilter: () => {
        this.clearFieldFilter(e), this.closeFilterPanel();
      },
      closePanel: () => this.closeFilterPanel()
    };
    let i = !1;
    if (this.config.filterPanelRenderer && (this.config.filterPanelRenderer(r, d), i = r.children.length > 0), !i && t.type) {
      const m = this.grid.effectiveConfig.typeDefaults?.[t.type];
      m?.filterPanelRenderer && (m.filterPanelRenderer(r, d), i = r.children.length > 0);
    }
    if (!i) {
      const m = t.type;
      m === "number" ? this.renderNumberFilterPanel(r, d, a) : m === "date" ? this.renderDateFilterPanel(r, d, a) : this.renderDefaultFilterPanel(r, d, a, l);
    }
  }
  /**
   * Setup click-outside handler to close the panel
   */
  setupPanelCloseHandler(e, t) {
    this.panelAbortController = new AbortController(), setTimeout(() => {
      document.addEventListener(
        "click",
        (r) => {
          !e.contains(r.target) && r.target !== t && this.closeFilterPanel();
        },
        { signal: this.panelAbortController?.signal }
      );
    }, 0);
  }
  /**
   * Close the filter panel
   */
  closeFilterPanel() {
    const e = this.panelElement;
    e && (e.remove(), this.panelElement = null), this.panelAnchorElement && (this.panelAnchorElement.style.anchorName = "", this.panelAnchorElement = null), this.openPanelField = null, this.panelAbortController?.abort(), this.panelAbortController = null;
  }
  /** Cache for CSS anchor positioning support check */
  static supportsAnchorPositioning = null;
  /**
   * Check if browser supports CSS Anchor Positioning
   */
  static checkAnchorPositioningSupport() {
    return I.supportsAnchorPositioning === null && (I.supportsAnchorPositioning = CSS.supports("anchor-name", "--test")), I.supportsAnchorPositioning;
  }
  /**
   * Position the panel below the header cell
   * Uses CSS Anchor Positioning if supported, falls back to JS positioning
   */
  positionPanel(e, t) {
    const a = t.closest(".cell") ?? t;
    if (a.style.anchorName = "--tbw-filter-anchor", this.panelAnchorElement = a, I.checkAnchorPositioningSupport()) {
      requestAnimationFrame(() => {
        const c = e.getBoundingClientRect(), d = a.getBoundingClientRect();
        c.top < d.top && e.classList.add("tbw-filter-panel-above");
      });
      return;
    }
    const l = a.getBoundingClientRect();
    e.style.position = "fixed", e.style.top = `${l.bottom + 4}px`, e.style.left = `${l.left}px`, requestAnimationFrame(() => {
      const c = e.getBoundingClientRect();
      c.right > window.innerWidth - 8 && (e.style.left = `${l.right - c.width}px`), c.bottom > window.innerHeight - 8 && (e.style.top = `${l.top - c.height - 4}px`, e.classList.add("tbw-filter-panel-above"));
    });
  }
  /**
   * Render the default filter panel content
   */
  renderDefaultFilterPanel(e, t, r, a) {
    const { field: l } = t, c = this.getListItemHeight(), d = document.createElement("div");
    d.className = "tbw-filter-search";
    const i = document.createElement("input");
    i.type = "text", i.placeholder = "Search...", i.className = "tbw-filter-search-input", i.value = this.searchText.get(l) ?? "", d.appendChild(i), e.appendChild(d);
    const m = document.createElement("div");
    m.className = "tbw-filter-actions";
    const k = document.createElement("label");
    k.className = "tbw-filter-value-item", k.style.padding = "0", k.style.margin = "0";
    const w = document.createElement("input");
    w.type = "checkbox", w.className = "tbw-filter-checkbox";
    const u = document.createElement("span");
    u.textContent = "Select All", k.appendChild(w), k.appendChild(u), m.appendChild(k);
    const v = () => {
      const o = [...S.values()], b = o.every((n) => n), y = o.every((n) => !n);
      w.checked = b, w.indeterminate = !b && !y;
    };
    w.addEventListener("change", () => {
      const o = w.checked;
      for (const b of S.keys())
        S.set(b, o);
      v(), L();
    }), e.appendChild(m);
    const E = document.createElement("div");
    E.className = "tbw-filter-values";
    const g = document.createElement("div");
    g.className = "tbw-filter-values-spacer", E.appendChild(g);
    const x = document.createElement("div");
    x.className = "tbw-filter-values-content", E.appendChild(x);
    const S = /* @__PURE__ */ new Map();
    r.forEach((o) => {
      const b = o == null ? "__null__" : String(o);
      S.set(b, !a.has(o));
    }), v();
    let C = [];
    const R = (o, b) => {
      const y = o == null ? "(Blank)" : String(o), n = o == null ? "__null__" : String(o), s = document.createElement("label");
      s.className = "tbw-filter-value-item", s.style.position = "absolute", s.style.top = `calc(var(--tbw-filter-item-height, 28px) * ${b})`, s.style.left = "0", s.style.right = "0", s.style.boxSizing = "border-box";
      const F = document.createElement("input");
      F.type = "checkbox", F.className = "tbw-filter-checkbox", F.checked = S.get(n) ?? !0, F.dataset.value = n, F.addEventListener("change", () => {
        S.set(n, F.checked), v();
      });
      const M = document.createElement("span");
      return M.textContent = y, s.appendChild(F), s.appendChild(M), s;
    }, L = () => {
      const o = C.length, b = E.clientHeight, y = E.scrollTop;
      if (g.style.height = `${o * c}px`, G(o, I.LIST_BYPASS_THRESHOLD / 3)) {
        x.innerHTML = "", x.style.transform = "translateY(0px)", C.forEach((s, F) => {
          x.appendChild(R(s, F));
        });
        return;
      }
      const n = Y({
        totalRows: o,
        viewportHeight: b,
        scrollTop: y,
        rowHeight: c,
        overscan: I.LIST_OVERSCAN
      });
      x.style.transform = `translateY(${n.offsetY}px)`, x.innerHTML = "";
      for (let s = n.start; s < n.end; s++)
        x.appendChild(R(C[s], s - n.start));
    }, f = (o) => {
      const b = this.config.caseSensitive ?? !1, y = b ? o : o.toLowerCase();
      if (C = r.filter((n) => {
        const s = n == null ? "(Blank)" : String(n), F = b ? s : s.toLowerCase();
        return !o || F.includes(y);
      }), C.length === 0) {
        g.style.height = "0px", x.innerHTML = "";
        const n = document.createElement("div");
        n.className = "tbw-filter-no-match", n.textContent = "No matching values", x.appendChild(n);
        return;
      }
      L();
    };
    E.addEventListener(
      "scroll",
      () => {
        C.length > 0 && L();
      },
      { passive: !0 }
    ), f(i.value), e.appendChild(E);
    let P;
    i.addEventListener("input", () => {
      clearTimeout(P), P = setTimeout(() => {
        this.searchText.set(l, i.value), f(i.value);
      }, this.config.debounceMs ?? 150);
    });
    const T = document.createElement("div");
    T.className = "tbw-filter-buttons";
    const A = document.createElement("button");
    A.className = "tbw-filter-apply-btn", A.textContent = "Apply", A.addEventListener("click", () => {
      const o = [];
      for (const [b, y] of S)
        if (!y)
          if (b === "__null__")
            o.push(null);
          else {
            const n = r.find((s) => String(s) === b);
            o.push(n !== void 0 ? n : b);
          }
      t.applySetFilter(o);
    }), T.appendChild(A);
    const p = document.createElement("button");
    p.className = "tbw-filter-clear-btn", p.textContent = "Clear Filter", p.addEventListener("click", () => {
      t.clearFilter();
    }), T.appendChild(p), e.appendChild(T);
  }
  /**
   * Render a number range filter panel with min/max inputs and slider
   */
  renderNumberFilterPanel(e, t, r) {
    const { field: a, column: l } = t, c = l.filterParams, d = l.editorParams, i = (h, V) => {
      if (typeof h == "number") return h;
      if (typeof h == "string") {
        const H = parseFloat(h);
        return isNaN(H) ? V : H;
      }
      return V;
    }, m = r.filter((h) => typeof h == "number" && !isNaN(h)), k = m.length > 0 ? Math.min(...m) : 0, w = m.length > 0 ? Math.max(...m) : 100, u = i(c?.min ?? d?.min, k), v = i(c?.max ?? d?.max, w), E = c?.step ?? d?.step ?? 1, g = this.filters.get(a);
    let x = u, S = v;
    g?.operator === "between" ? (x = i(g.value, u), S = i(g.valueTo, v)) : g?.operator === "greaterThanOrEqual" ? x = i(g.value, u) : g?.operator === "lessThanOrEqual" && (S = i(g.value, v));
    const C = document.createElement("div");
    C.className = "tbw-filter-range-inputs";
    const R = document.createElement("div");
    R.className = "tbw-filter-range-group";
    const L = document.createElement("label");
    L.textContent = "Min", L.className = "tbw-filter-range-label";
    const f = document.createElement("input");
    f.type = "number", f.className = "tbw-filter-range-input", f.min = String(u), f.max = String(v), f.step = String(E), f.value = String(x), R.appendChild(L), R.appendChild(f), C.appendChild(R);
    const P = document.createElement("span");
    P.className = "tbw-filter-range-separator", P.textContent = "–", C.appendChild(P);
    const T = document.createElement("div");
    T.className = "tbw-filter-range-group";
    const A = document.createElement("label");
    A.textContent = "Max", A.className = "tbw-filter-range-label";
    const p = document.createElement("input");
    p.type = "number", p.className = "tbw-filter-range-input", p.min = String(u), p.max = String(v), p.step = String(E), p.value = String(S), T.appendChild(A), T.appendChild(p), C.appendChild(T), e.appendChild(C);
    const o = document.createElement("div");
    o.className = "tbw-filter-range-slider";
    const b = document.createElement("div");
    b.className = "tbw-filter-range-track";
    const y = document.createElement("div");
    y.className = "tbw-filter-range-fill";
    const n = document.createElement("input");
    n.type = "range", n.className = "tbw-filter-range-thumb tbw-filter-range-thumb-min", n.min = String(u), n.max = String(v), n.step = String(E), n.value = String(x);
    const s = document.createElement("input");
    s.type = "range", s.className = "tbw-filter-range-thumb tbw-filter-range-thumb-max", s.min = String(u), s.max = String(v), s.step = String(E), s.value = String(S), o.appendChild(b), o.appendChild(y), o.appendChild(n), o.appendChild(s), e.appendChild(o);
    const F = () => {
      const h = parseFloat(n.value), V = parseFloat(s.value), H = v - u, D = (h - u) / H * 100, O = (V - u) / H * 100;
      y.style.left = `${D}%`, y.style.width = `${O - D}%`;
    };
    n.addEventListener("input", () => {
      const h = Math.min(parseFloat(n.value), parseFloat(s.value));
      n.value = String(h), f.value = String(h), F();
    }), s.addEventListener("input", () => {
      const h = Math.max(parseFloat(s.value), parseFloat(n.value));
      s.value = String(h), p.value = String(h), F();
    }), f.addEventListener("input", () => {
      let h = parseFloat(f.value) || u;
      h = Math.max(u, Math.min(h, parseFloat(p.value))), n.value = String(h), F();
    }), p.addEventListener("input", () => {
      let h = parseFloat(p.value) || v;
      h = Math.min(v, Math.max(h, parseFloat(f.value))), s.value = String(h), F();
    }), F();
    const M = document.createElement("div");
    M.className = "tbw-filter-buttons";
    const _ = document.createElement("button");
    _.className = "tbw-filter-apply-btn", _.textContent = "Apply", _.addEventListener("click", () => {
      const h = parseFloat(f.value), V = parseFloat(p.value);
      t.applyTextFilter("between", h, V);
    }), M.appendChild(_);
    const z = document.createElement("button");
    z.className = "tbw-filter-clear-btn", z.textContent = "Clear Filter", z.addEventListener("click", () => {
      t.clearFilter();
    }), M.appendChild(z), e.appendChild(M);
  }
  /**
   * Render a date range filter panel with from/to date inputs
   */
  renderDateFilterPanel(e, t, r) {
    const { field: a, column: l } = t, c = l.filterParams, d = l.editorParams, i = r.filter((n) => n instanceof Date || typeof n == "string" && !isNaN(Date.parse(n))).map((n) => n instanceof Date ? n : new Date(n)).filter((n) => !isNaN(n.getTime())), m = i.length > 0 ? new Date(Math.min(...i.map((n) => n.getTime()))) : null, k = i.length > 0 ? new Date(Math.max(...i.map((n) => n.getTime()))) : null, w = (n) => n ? n.toISOString().split("T")[0] : "", u = (n) => n ? typeof n == "string" ? n : typeof n == "number" ? w(new Date(n)) : "" : "", v = u(c?.min) || u(d?.min) || w(m), E = u(c?.max) || u(d?.max) || w(k), g = this.filters.get(a);
    let x = "", S = "";
    g?.operator === "between" ? (x = u(g.value) || "", S = u(g.valueTo) || "") : g?.operator === "greaterThanOrEqual" ? x = u(g.value) || "" : g?.operator === "lessThanOrEqual" && (S = u(g.value) || "");
    const C = document.createElement("div");
    C.className = "tbw-filter-date-range";
    const R = document.createElement("div");
    R.className = "tbw-filter-date-group";
    const L = document.createElement("label");
    L.textContent = "From", L.className = "tbw-filter-range-label";
    const f = document.createElement("input");
    f.type = "date", f.className = "tbw-filter-date-input", v && (f.min = v), E && (f.max = E), f.value = x, R.appendChild(L), R.appendChild(f), C.appendChild(R);
    const P = document.createElement("span");
    P.className = "tbw-filter-range-separator", P.textContent = "–", C.appendChild(P);
    const T = document.createElement("div");
    T.className = "tbw-filter-date-group";
    const A = document.createElement("label");
    A.textContent = "To", A.className = "tbw-filter-range-label";
    const p = document.createElement("input");
    p.type = "date", p.className = "tbw-filter-date-input", v && (p.min = v), E && (p.max = E), p.value = S, T.appendChild(A), T.appendChild(p), C.appendChild(T), e.appendChild(C);
    const o = document.createElement("div");
    o.className = "tbw-filter-buttons";
    const b = document.createElement("button");
    b.className = "tbw-filter-apply-btn", b.textContent = "Apply", b.addEventListener("click", () => {
      const n = f.value, s = p.value;
      n && s ? t.applyTextFilter("between", n, s) : n ? t.applyTextFilter("greaterThanOrEqual", n) : s ? t.applyTextFilter("lessThanOrEqual", s) : t.clearFilter();
    }), o.appendChild(b);
    const y = document.createElement("button");
    y.className = "tbw-filter-clear-btn", y.textContent = "Clear Filter", y.addEventListener("click", () => {
      t.clearFilter();
    }), o.appendChild(y), e.appendChild(o);
  }
  /**
   * Apply a set filter (exclude values)
   */
  applySetFilter(e, t) {
    this.excludedValues.set(e, new Set(t)), t.length === 0 ? this.filters.delete(e) : this.filters.set(e, {
      field: e,
      type: "set",
      operator: "notIn",
      value: t
    }), this.applyFiltersInternal();
  }
  /**
   * Apply a text/number/date filter
   */
  applyTextFilter(e, t, r, a) {
    this.filters.set(e, {
      field: e,
      type: "text",
      operator: t,
      value: r,
      valueTo: a
    }), this.applyFiltersInternal();
  }
  /**
   * Internal method to apply filters (sync or async based on config)
   */
  applyFiltersInternal() {
    this.cachedResult = null, this.cacheKey = null;
    const e = [...this.filters.values()];
    if (this.config.filterHandler) {
      const t = this.grid;
      t.setAttribute("aria-busy", "true");
      const r = this.config.filterHandler(e, this.sourceRows), a = (l) => {
        t.removeAttribute("aria-busy"), this.cachedResult = l, this.grid.rows = l, this.emit("filter-change", {
          filters: e,
          filteredRowCount: l.length
        }), this.requestRender();
      };
      r && typeof r.then == "function" ? r.then(a) : a(r);
      return;
    }
    this.emit("filter-change", {
      filters: e,
      filteredRowCount: 0
    }), this.requestRender();
  }
  // #endregion
  // #region Column State Hooks
  /**
   * Return filter state for a column if it has an active filter.
   * @internal
   */
  getColumnState(e) {
    const t = this.filters.get(e);
    if (t)
      return {
        filter: {
          type: t.type,
          operator: t.operator,
          value: t.value,
          valueTo: t.valueTo
        }
      };
  }
  /**
   * Apply filter state from column state.
   * @internal
   */
  applyColumnState(e, t) {
    if (!t.filter) {
      this.filters.delete(e);
      return;
    }
    const r = {
      field: e,
      type: t.filter.type,
      operator: t.filter.operator,
      value: t.filter.value,
      valueTo: t.filter.valueTo
    };
    this.filters.set(e, r), this.cachedResult = null, this.cacheKey = null;
  }
  // #endregion
}
export {
  I as FilteringPlugin
};
//# sourceMappingURL=index.js.map
