common/types.ts

import { Subject } from "rxjs";

/**
 * Actor type 구분
 * @export
 * @enum {number}
 */
export enum AppearType {
  BASE = "base",
  ONCE = "once",
  LAZY = "lazy"
}

/**
 * 관찰자
 * @interface IStage
 * @template T
 * @property init {method} 초기화
 * @property observe {method} 관찰 대상 등록
 * @property unobserve {method} 관찰 대상 해제
 * @property dispose {method} 파기
 */
export interface IStage<T> {
  /**
   * @param [option] 초기 옵션
   */
  init(option?: StageOption): void;

  /**
   * @param actor 관찰대상
   */
  observe(actor: T): void;

  /**
   * @param actor 관찰해제 대상
   */
  unobserve(actor: T): void;

  /**
   * 파기
   */
  dispose(): void;
}

/**
 * 관찰대상
 * @interface IActor
 * @property element {ActorElement} 관찰 대상이 참조해야하는 DOM
 * @property stage {IStage<any>} 관찰 대상이 속하게 되는 스테이지(관찰자)
 * @property events {Subject<AppearEvent>} 관찰 이벤트 Observable
 * @property isAppear {boolean} 현재 관찰대상의 진입, 이탈 여부
 * @property bind {method} 관찰 대상이 속하게 되는 스테이지(관찰자) 등록.
 * @property appear {method} 스테이지 진입.
 * @property disappear {method} 스테이지 이탈.
 * @property dispose {method} 파기
 */
export interface IActor {
  element: ActorElement;
  stage: IStage<any>;
  events: Subject<AppearEvent>;
  isAppear: boolean;

  /**
   * @param stage 관찰 대상으로 등록될 때 해당 인스턴스를 관찰하는 스테이지
   */
  bind(stage: IStage<IActor>): void;

  /**
   * @param [entry] 진입 당시 관찰 정보
   */
  appear(entry?: IntersectionObserverEntry): void;

  /**
   * @param [entry] 이탈 당시 관찰 정보
   */
  disappear(entry?: IntersectionObserverEntry): void;

  dispose(): void;
}

/**
 * 관찰대상 - 한번만 형
 * @export
 * @interface IOnceActor
 */
export interface IOnceActor {}

/**
 * 관찰대상 - 한번만 느긋하게 형
 * @export
 * @interface ILazyActor
 * @property setCheckoutDelay {method} 느린 감지를 시작하기 전 대기 시간.
 * @property setAppearDelay {method} 지정된 시간 사이에 진입 후 진출을 하는 경우 진입 알림을 하지 않는 대기 시간.
 */
export interface ILazyActor {
  setCheckoutDelay?(delay: number): void;
  setAppearDelay?(delay: number): void;
}

export interface Actor extends IActor, IOnceActor, ILazyActor {}

/**
 * 관찰자, 관찰대상에서 참조되어야 하는 native element 타입
 * @typedef {HTMLElement | Element} ActorElement
 */
export type ActorElement = HTMLElement | Element;

/**
 * 스테이지 초기 옵션.
 * @interface StageOption
 * @property root {Element} mdn 참고
 * @property rootMargin {string} mdn 참고
 * @property threshold {string | array} mdn 참고
 * @extends {IntersectionObserverInit}
 * @see https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
 */
export interface StageOption extends IntersectionObserverInit {}

/**
 * 관찰대상의 이벤트
 * @implements {AppearEventData<T>}
 * @template T
 */
export class AppearEvent<T = IActor> implements AppearEventData<T> {
  /**
   * @param type 이벤트 타입
   * @param option 이벤트 데이터
   */
  constructor(type: string, option: AppearEventData<T>) {
    const { actor, entry } = option;
    this.type = type;
    this.actor = actor;
    this.entry = entry;
  }

  /**
   * 이벤트 타입 - 진입
   */
  static readonly APPEAR: string = "APPEAR";

  /**
   * 이벤트 타입 - 이탈
   */
  static readonly DISAPPEAR: string = "DISAPPEAR";

  /**
   * 이벤트 타입
   */
  type: string;

  /**
   * 참조되는 관찰대상
   */
  actor: T;

  /**
   * 인터섹션 옵저버의 진입, 이탈 당시 관찰 상태
   */
  entry: IntersectionObserverEntry;
}

/**
 * Appear 이벤트의 데이터
 * @interface AppearEventData
 * @property actor {T} 참조되는 관찰대상
 * @property entry {IntersectionObserverEntry} 인터섹션 옵저버의 진입, 이탈 당시 관찰 상태
 * @template T
 */
export interface AppearEventData<T> {
  actor: T;
  entry: IntersectionObserverEntry;
}