Skip to content

TS rewrite #253

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
kharrat opened this issue Mar 16, 2025 · 0 comments
Open

TS rewrite #253

kharrat opened this issue Mar 16, 2025 · 0 comments

Comments

@kharrat
Copy link

kharrat commented Mar 16, 2025

Hello,

I wrote a typescript rewrite to the main file if any one interested

/**
 * NProgress is a lightweight progress bar for JS applications.
 * It provides a simple way to visualize loading states with a slim progress bar at the top of the page.
 *
 * This is implemented as a singleton, so you should access it via NProgress.getInstance() or use the default export.
 *
 * @example
 * // Start the progress bar
 * NProgress.start();
 *
 * // Set progress to 50%
 * NProgress.set(0.5);
 *
 * // Increment the progress bar
 * NProgress.inc();
 *
 * // Complete the progress
 * NProgress.done();
 *
 * // Configure options
 * NProgress.configure({ showSpinner: false });
 */

type NProgressSettings = {
  minimum: number;
  easing?: string;
  positionUsing?: string;
  speed?: number;
  trickle?: boolean;
  trickleSpeed?: number;
  showSpinner?: boolean;
  barSelector: string;
  spinnerSelector?: string;
  parent: string;
  template: string;
};

export class NProgress {
  private static instance: NProgress;
  private status: number | null = null;

  // Default settings
  private settings: NProgressSettings = {
    minimum: 0.08,
    easing: "linear",
    positionUsing: "",
    speed: 200,
    trickle: true,
    trickleSpeed: 200,
    showSpinner: true,
    barSelector: '[role="bar"]',
    spinnerSelector: '[role="spinner"]',
    parent: "body",
    template:
      '<div class="bar" role="bar"><div class="peg"></div></div>' +
      '<div class="spinner" role="spinner"><div class="spinner-icon"></div></div>',
  };

  private constructor() {}

  public static getInstance() {
    if (!NProgress.instance) {
      NProgress.instance = new NProgress();
    }
    return NProgress.instance;
  }

  public configure(options: Partial<NProgressSettings>) {
    Object.assign(this.settings, options);
    return this;
  }

  public start(): NProgress {
    if (!this.status) this.set(0);

    const work = () => {
      setTimeout(() => {
        if (!this.status) return;
        this.trickle();
        work();
      }, this.settings.trickleSpeed);
    };

    if (this.settings.trickle) work();
    return this;
  }

  public set(n: number): NProgress {
    const started = this.isStarted();
    n = this.clamp(n, this.settings.minimum || 0.08, 1);
    this.status = n === 1 ? null : n;

    const progress = this.render(!started);
    const bar = progress.querySelector<HTMLElement>(this.settings.barSelector);
    const speed = this.settings.speed || 200;
    const ease = this.settings.easing || "linear";

    if (bar) {
      this.applyCss(bar, this.barPositionCSS(n, speed, ease));
    }

    if (n === 1) {
      setTimeout(() => {
        this.remove();
      }, speed);
    }

    return this;
  }

  public done(force = false): NProgress {
    if (!force && !this.status) return this;
    return this.inc(0.3 + 0.5 * Math.random()).set(1);
  }

  public inc(amount?: number): NProgress {
    let n = this.status ?? 0;

    if (n >= 1) return this;
    amount =
      amount ?? (n < 0.2 ? 0.1 : n < 0.5 ? 0.04 : n < 0.8 ? 0.02 : 0.005);

    return this.set(this.clamp(n + amount, 0, 0.994));
  }

  public trickle(): NProgress {
    return this.inc();
  }

  private isStarted(): boolean {
    return typeof this.status === "number";
  }

  private render(fromStart = false): HTMLElement {
    if (this.isRendered()) return document.getElementById("nprogress")!;

    const progress = document.createElement("div");
    progress.id = "nprogress";
    progress.innerHTML = this.settings.template;

    document.querySelector(this.settings.parent)!.appendChild(progress);
    return progress;
  }

  private remove(): void {
    const progress = document.getElementById("nprogress");
    progress?.parentNode?.removeChild(progress);
  }

  private isRendered(): boolean {
    return !!document.getElementById("nprogress");
  }

  private clamp(n: number, min: number, max: number): number {
    return Math.min(Math.max(n, min), max);
  }

  private barPositionCSS(
    n: number,
    speed: number,
    ease: string
  ): Record<string, string> {
    return {
      transform: `translate3d(${(-1 + n) * 100}%,0,0)`,
      transition: `all ${speed}ms ${ease}`,
    };
  }

  private applyCss(element: HTMLElement, styles: Record<string, string>): void {
    Object.assign(element.style, styles);
  }
}

export default NProgress.getInstance();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant