Skip to content
ts
import { h } from '@stencil/core';
import type { PluginProviders, PluginBaseComponent, CustomEvent } from '@revolist/revogrid';


export type WatchConfig = { immediate: boolean };

/**
 * Explanation how base layer for plugins works in revogrid
 * Provide minimal starting core for plugins to work
 * Extend this class to create plugin
 */
export class BasePlugin implements PluginBaseComponent {
  readonly h = h;
  readonly subscriptions: Record<string, (...args: any[]) => void> = {};
  constructor(public revogrid: HTMLRevoGridElement, public providers: PluginProviders) {}
  /**
   *
   * @param eventName - event name to subscribe to in revo-grid component (e.g. 'beforeheaderclick')
   * @param callback - callback function for event
   */
  addEventListener<K extends keyof HTMLRevoGridElementEventMap>(
    eventName: K,
    callback: (this: BasePlugin, e: CustomEvent<HTMLRevoGridElementEventMap[K]>) => void,
  ) {
    this.revogrid.addEventListener(eventName as string, callback);
    this.subscriptions[eventName as string] = callback;
  }

  /**
   * Subscribe to property change in revo-grid component
   * You can return false in callback to prevent default value set
   *
   * @param prop - property name
   * @param callback - callback function
   * @param immediate - trigger callback immediately with current value
   */
  watch<T extends any>(
    prop: string,
    callback: (arg: T) => boolean | void,
    { immediate }: Partial<WatchConfig> = { immediate: false },
  ) {
    const nativeValueDesc =
      Object.getOwnPropertyDescriptor(this.revogrid, prop) ||
      Object.getOwnPropertyDescriptor(this.revogrid.constructor.prototype, prop);

    // Overwrite property descriptor for this instance
    Object.defineProperty(this.revogrid, prop, {
      set(val: T) {
        const keepDefault = callback(val);
        if (keepDefault === false) {
          return;
        }
        // Continue with native behavior
        return nativeValueDesc?.set?.call(this, val);
      },
      get() {
        // Continue with native behavior
        return nativeValueDesc?.get?.call(this);
      },
    });
    if (immediate) {
      callback(nativeValueDesc?.value);
    }
  }

  /**
   * Remove event listener
   * @param eventName
   */
  removeEventListener(eventName: string) {
    this.revogrid.removeEventListener(eventName, this.subscriptions[eventName]);
    delete this.subscriptions[eventName];
  }

  /**
   * Emit event from revo-grid component
   * Event can be cancelled by calling event.preventDefault() in callback
   */
  emit<T = any>(eventName: string, detail?: T) {
    const event = new CustomEvent<T>(eventName, { detail, cancelable: true });
    this.revogrid.dispatchEvent(event);
    return event;
  }

  /**
   * Clear all subscriptions
   */
  clearSubscriptions() {
    for (let type in this.subscriptions) {
      this.removeEventListener(type);
    }
  }

  /**
   * Destroy plugin and clear all subscriptions
   */
  destroy() {
    this.clearSubscriptions();
  }
}

export type GridPlugin = (typeof BasePlugin);