Skip to content

Instantly share code, notes, and snippets.

@aadilmallick
Last active July 8, 2024 14:34
Show Gist options
  • Save aadilmallick/f63e4e081ed9fa87a3d7093dfdbca06f to your computer and use it in GitHub Desktop.
Save aadilmallick/f63e4e081ed9fa87a3d7093dfdbca06f to your computer and use it in GitHub Desktop.
// utility methods for DOM interaction
interface HTMLElement {
  on(
    event: string,
    handler: EventListenerOrEventListenerObject,
    options?: boolean | AddEventListenerOptions
  ): void;
  off(
    event: string,
    handler: EventListenerOrEventListenerObject,
    options?: boolean | EventListenerOptions
  ): void;
  $(selector: string): Element | null;
  $$(selector: string): NodeListOf<Element>;
}

export const $ = (selector: string): Element | null =>
  document.querySelector(selector);
export const $$ = (selector: string): NodeListOf<Element> =>
  document.querySelectorAll(selector);

export const selectWithThrow = (selector: string): Element => {
  const el = $(selector);
  if (!el) {
    throw new Error(`Element not found: ${selector}`);
  }
  return el;
};

HTMLElement.prototype.$ = function (
  this: HTMLElement,
  selector: string
): Element | null {
  return this.querySelector(selector);
};

HTMLElement.prototype.$$ = function (
  this: HTMLElement,
  selector: string
): NodeListOf<Element> {
  return this.querySelectorAll(selector);
};

export function debounce(callback: Function, delay: number) {
  let timeoutId: ReturnType<Window["setTimeout"]>;
  return (...args: any[]) => {
    if (timeoutId) {
      clearTimeout(timeoutId);
    }
    timeoutId = window.setTimeout(() => {
      callback(...args);
    }, delay);
  };
}

export function createReactiveProxy<T extends Record<keyof T, V>, V>(
  state: T,
  onSet: (state: T) => void
) {
  const singleProperty = Object.keys(state)[0] as keyof T;
  const proxy = new Proxy(state, {
    set(target, p, newValue, receiver) {
      if (p === singleProperty) {
        // @ts-ignore
        target[p] = newValue as V;
        onSet(target);
      }
      return Reflect.set(target, p, newValue, receiver);
    },
  });
  return proxy;
}

export function createReactiveFunction<T extends CallableFunction>(
  func: T,
  onCall: (argsList: any[]) => void
) {
  const proxy = new Proxy(func, {
    apply(targetFunc, thisArg, argArray) {
      onCall(argArray);
      return Reflect.apply(targetFunc, thisArg, argArray);
    },
  });
  return proxy;
}

export class ObservableStore<T extends CallableFunction> {
  private observers: Set<T> = new Set();
  notify(...args: any[]) {
    this.observers.forEach((observer) => observer(...args));
  }
  notifyAndReturn(...args: any[]) {
    const returnValues = Array.from(this.observers).map((observer) =>
      observer(...args)
    );
    return returnValues;
  }
  addObserver(observer: T) {
    this.observers.add(observer);
  }
  removeObserver(observer: T) {
    this.observers.delete(observer);
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment