react-top-progress 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,100 @@
1
+ # react-top-progress
2
+
3
+ A lightweight, modern, and performant top loading progress bar for React.
4
+
5
+ Perfect for Next.js App Router, Vite, and standard single-page applications.
6
+
7
+ ## Features
8
+
9
+ - **0 dependencies**: Tiny footprint (~1kb gzipped).
10
+ - **Realistic loading feel**: Increments incrementally like real network requests.
11
+ - **Premium DX**: Controlled programmatically with simple `startProgress` / `finishProgress` API.
12
+ - **Gradient & Glow Support**: Fully customizable colors, shadow pegs, and effects.
13
+ - **Performant**: Uses `transform` hardware acceleration and `cubic-bezier` transitions.
14
+ - **TypeScript ready**: Full types included.
15
+ - **React 18+ & Next.js ready**: Contains `"use client"` where necessary.
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install react-top-progress
21
+ ```
22
+
23
+ **Or using yarn / pnpm / bun:**
24
+
25
+ ```bash
26
+ yarn add react-top-progress
27
+ pnpm add react-top-progress
28
+ bun add react-top-progress
29
+ ```
30
+
31
+ ## Quick Start
32
+
33
+ ### 1. Add the component to your root layout/app
34
+
35
+ At the top level of your application (like Next.js `app/layout.tsx` or Vite `src/App.tsx`), render the `<TopProgress />` component.
36
+
37
+ ```tsx
38
+ import { TopProgress } from "react-top-progress";
39
+
40
+ export default function RootLayout({ children }) {
41
+ return (
42
+ <html lang="en">
43
+ <body>
44
+ <TopProgress />
45
+ {children}
46
+ </body>
47
+ </html>
48
+ );
49
+ }
50
+ ```
51
+
52
+ ### 2. Control it programmatically
53
+
54
+ Import the API from anywhere in your app to trigger the progress bar manually!
55
+
56
+ ```tsx
57
+ import { startProgress, finishProgress } from "react-top-progress";
58
+
59
+ async function handleAction() {
60
+ startProgress();
61
+ try {
62
+ await someHeavyTask();
63
+ } finally {
64
+ finishProgress();
65
+ }
66
+ }
67
+ ```
68
+
69
+ ### 3. The `withProgress` Utility
70
+
71
+ For an even better Developer Experience, simply wrap your promises securely with `withProgress`!
72
+
73
+ ```tsx
74
+ import { withProgress } from "react-top-progress";
75
+
76
+ async function handleFetch() {
77
+ const data = await withProgress(fetch("/api/data"));
78
+ console.log(data);
79
+ }
80
+ ```
81
+
82
+ ## Customization API
83
+
84
+ The `TopProgress` component accepts the following props:
85
+
86
+ | Prop | Type | Default | Description |
87
+ | ----------- | --------- | ----------- | ------------------------------------------------------------------------------------------------------------ |
88
+ | `color` | `string` | `"#29D"` | Flat hex color or `linear-gradient(...)` function string |
89
+ | `height` | `number` | `3` | Height of the progress bar in pixels |
90
+ | `showGlow` | `boolean` | `true` | Whether to display the premium box-shadow drop-glow and trailing peg |
91
+ | `glowColor` | `string` | `undefined` | Optionally override the color of the glow effect. By default, it infers realistically from your main `color` |
92
+ | `zIndex` | `number` | `9999` | The z-index stacking context level. |
93
+
94
+ ## Credits & Inspiration
95
+
96
+ Designed for the modern web. Inspired by the classic `nprogress` and Next.js' `nextjs-toploader`. Ideal when you need explicit control without relying solely on framework routing events.
97
+
98
+ ## License
99
+
100
+ MIT
@@ -0,0 +1,16 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ interface TopProgressProps {
4
+ color?: string;
5
+ height?: number;
6
+ showGlow?: boolean;
7
+ glowColor?: string;
8
+ zIndex?: number;
9
+ }
10
+ declare const TopProgress: ({ color, height, showGlow, glowColor, zIndex, }: TopProgressProps) => react_jsx_runtime.JSX.Element | null;
11
+
12
+ declare const startProgress: () => void;
13
+ declare const finishProgress: () => void;
14
+ declare const withProgress: <T>(promise: Promise<T>) => Promise<T>;
15
+
16
+ export { TopProgress, finishProgress, startProgress, withProgress };
@@ -0,0 +1,16 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ interface TopProgressProps {
4
+ color?: string;
5
+ height?: number;
6
+ showGlow?: boolean;
7
+ glowColor?: string;
8
+ zIndex?: number;
9
+ }
10
+ declare const TopProgress: ({ color, height, showGlow, glowColor, zIndex, }: TopProgressProps) => react_jsx_runtime.JSX.Element | null;
11
+
12
+ declare const startProgress: () => void;
13
+ declare const finishProgress: () => void;
14
+ declare const withProgress: <T>(promise: Promise<T>) => Promise<T>;
15
+
16
+ export { TopProgress, finishProgress, startProgress, withProgress };
package/dist/index.js ADDED
@@ -0,0 +1,163 @@
1
+ "use strict";
2
+ "use client";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/index.ts
22
+ var index_exports = {};
23
+ __export(index_exports, {
24
+ TopProgress: () => TopProgress,
25
+ finishProgress: () => finishProgress,
26
+ startProgress: () => startProgress,
27
+ withProgress: () => withProgress
28
+ });
29
+ module.exports = __toCommonJS(index_exports);
30
+
31
+ // src/TopProgress.tsx
32
+ var import_react = require("react");
33
+
34
+ // src/progressStore.ts
35
+ var ProgressStore = class {
36
+ listeners = /* @__PURE__ */ new Set();
37
+ value = null;
38
+ interval = null;
39
+ hideTimer = null;
40
+ subscribe(listener) {
41
+ this.listeners.add(listener);
42
+ listener(this.value);
43
+ return () => {
44
+ this.listeners.delete(listener);
45
+ };
46
+ }
47
+ notify() {
48
+ this.listeners.forEach((listener) => listener(this.value));
49
+ }
50
+ start() {
51
+ if (this.value !== null) return;
52
+ this.cleanup();
53
+ this.value = 10;
54
+ this.notify();
55
+ this.interval = setInterval(() => {
56
+ if (this.value !== null) {
57
+ const amount = Math.max(0.5, (95 - this.value) / 10);
58
+ this.value = Math.min(this.value + amount, 95);
59
+ this.notify();
60
+ }
61
+ }, 200);
62
+ }
63
+ finish() {
64
+ this.cleanup();
65
+ this.value = 100;
66
+ this.notify();
67
+ this.hideTimer = setTimeout(() => {
68
+ this.value = null;
69
+ this.notify();
70
+ }, 400);
71
+ }
72
+ cleanup() {
73
+ if (this.interval) clearInterval(this.interval);
74
+ if (this.hideTimer) clearTimeout(this.hideTimer);
75
+ }
76
+ getValue() {
77
+ return this.value;
78
+ }
79
+ };
80
+ var progressStore = new ProgressStore();
81
+ var useProgressStore = () => progressStore;
82
+ var startProgress = () => progressStore.start();
83
+ var finishProgress = () => progressStore.finish();
84
+ var withProgress = async (promise) => {
85
+ startProgress();
86
+ try {
87
+ const result = await promise;
88
+ finishProgress();
89
+ return result;
90
+ } catch (err) {
91
+ finishProgress();
92
+ throw err;
93
+ }
94
+ };
95
+
96
+ // src/TopProgress.tsx
97
+ var import_jsx_runtime = require("react/jsx-runtime");
98
+ var TopProgress = ({
99
+ color = "#29D",
100
+ // Still the default flat color, but can now accept "linear-gradient(...)"
101
+ height = 3,
102
+ showGlow = true,
103
+ glowColor,
104
+ zIndex = 9999
105
+ }) => {
106
+ const store = useProgressStore();
107
+ const [progress, setProgress] = (0, import_react.useState)(store.getValue());
108
+ (0, import_react.useEffect)(() => {
109
+ return store.subscribe(setProgress);
110
+ }, [store]);
111
+ if (progress === null) {
112
+ return null;
113
+ }
114
+ const isGradient = color.includes("gradient(");
115
+ const backgroundStyle = isGradient ? { backgroundImage: color } : { backgroundColor: color };
116
+ const computedGlowColor = glowColor || (isGradient ? "rgba(41, 216, 255, 0.4)" : color);
117
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
118
+ "div",
119
+ {
120
+ style: {
121
+ position: "fixed",
122
+ top: 0,
123
+ left: 0,
124
+ width: `${progress}%`,
125
+ height: `${height}px`,
126
+ ...backgroundStyle,
127
+ // Box shadow generates a beautiful diffused glow underneath the progress bar
128
+ boxShadow: showGlow ? `0 0 10px ${computedGlowColor}, 0 0 5px ${computedGlowColor}` : "none",
129
+ // Smooth and performant 60fps CSS transitions
130
+ transition: "width 0.2s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.4s ease",
131
+ opacity: progress === 100 ? 0 : 1,
132
+ zIndex,
133
+ // Force hardware acceleration for paint performance
134
+ transform: "translateZ(0)",
135
+ // Don't intercept clicks
136
+ pointerEvents: "none"
137
+ },
138
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
139
+ "div",
140
+ {
141
+ style: {
142
+ display: showGlow ? "block" : "none",
143
+ position: "absolute",
144
+ right: 0,
145
+ top: 0,
146
+ width: 100,
147
+ height: "100%",
148
+ boxShadow: `0 0 10px ${computedGlowColor}, 0 0 5px ${computedGlowColor}`,
149
+ opacity: 1,
150
+ transform: "rotate(3deg) translate(0px, -4px)"
151
+ }
152
+ }
153
+ )
154
+ }
155
+ );
156
+ };
157
+ // Annotate the CommonJS export names for ESM import in node:
158
+ 0 && (module.exports = {
159
+ TopProgress,
160
+ finishProgress,
161
+ startProgress,
162
+ withProgress
163
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,134 @@
1
+ "use client";
2
+
3
+ // src/TopProgress.tsx
4
+ import { useEffect, useState } from "react";
5
+
6
+ // src/progressStore.ts
7
+ var ProgressStore = class {
8
+ listeners = /* @__PURE__ */ new Set();
9
+ value = null;
10
+ interval = null;
11
+ hideTimer = null;
12
+ subscribe(listener) {
13
+ this.listeners.add(listener);
14
+ listener(this.value);
15
+ return () => {
16
+ this.listeners.delete(listener);
17
+ };
18
+ }
19
+ notify() {
20
+ this.listeners.forEach((listener) => listener(this.value));
21
+ }
22
+ start() {
23
+ if (this.value !== null) return;
24
+ this.cleanup();
25
+ this.value = 10;
26
+ this.notify();
27
+ this.interval = setInterval(() => {
28
+ if (this.value !== null) {
29
+ const amount = Math.max(0.5, (95 - this.value) / 10);
30
+ this.value = Math.min(this.value + amount, 95);
31
+ this.notify();
32
+ }
33
+ }, 200);
34
+ }
35
+ finish() {
36
+ this.cleanup();
37
+ this.value = 100;
38
+ this.notify();
39
+ this.hideTimer = setTimeout(() => {
40
+ this.value = null;
41
+ this.notify();
42
+ }, 400);
43
+ }
44
+ cleanup() {
45
+ if (this.interval) clearInterval(this.interval);
46
+ if (this.hideTimer) clearTimeout(this.hideTimer);
47
+ }
48
+ getValue() {
49
+ return this.value;
50
+ }
51
+ };
52
+ var progressStore = new ProgressStore();
53
+ var useProgressStore = () => progressStore;
54
+ var startProgress = () => progressStore.start();
55
+ var finishProgress = () => progressStore.finish();
56
+ var withProgress = async (promise) => {
57
+ startProgress();
58
+ try {
59
+ const result = await promise;
60
+ finishProgress();
61
+ return result;
62
+ } catch (err) {
63
+ finishProgress();
64
+ throw err;
65
+ }
66
+ };
67
+
68
+ // src/TopProgress.tsx
69
+ import { jsx } from "react/jsx-runtime";
70
+ var TopProgress = ({
71
+ color = "#29D",
72
+ // Still the default flat color, but can now accept "linear-gradient(...)"
73
+ height = 3,
74
+ showGlow = true,
75
+ glowColor,
76
+ zIndex = 9999
77
+ }) => {
78
+ const store = useProgressStore();
79
+ const [progress, setProgress] = useState(store.getValue());
80
+ useEffect(() => {
81
+ return store.subscribe(setProgress);
82
+ }, [store]);
83
+ if (progress === null) {
84
+ return null;
85
+ }
86
+ const isGradient = color.includes("gradient(");
87
+ const backgroundStyle = isGradient ? { backgroundImage: color } : { backgroundColor: color };
88
+ const computedGlowColor = glowColor || (isGradient ? "rgba(41, 216, 255, 0.4)" : color);
89
+ return /* @__PURE__ */ jsx(
90
+ "div",
91
+ {
92
+ style: {
93
+ position: "fixed",
94
+ top: 0,
95
+ left: 0,
96
+ width: `${progress}%`,
97
+ height: `${height}px`,
98
+ ...backgroundStyle,
99
+ // Box shadow generates a beautiful diffused glow underneath the progress bar
100
+ boxShadow: showGlow ? `0 0 10px ${computedGlowColor}, 0 0 5px ${computedGlowColor}` : "none",
101
+ // Smooth and performant 60fps CSS transitions
102
+ transition: "width 0.2s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.4s ease",
103
+ opacity: progress === 100 ? 0 : 1,
104
+ zIndex,
105
+ // Force hardware acceleration for paint performance
106
+ transform: "translateZ(0)",
107
+ // Don't intercept clicks
108
+ pointerEvents: "none"
109
+ },
110
+ children: /* @__PURE__ */ jsx(
111
+ "div",
112
+ {
113
+ style: {
114
+ display: showGlow ? "block" : "none",
115
+ position: "absolute",
116
+ right: 0,
117
+ top: 0,
118
+ width: 100,
119
+ height: "100%",
120
+ boxShadow: `0 0 10px ${computedGlowColor}, 0 0 5px ${computedGlowColor}`,
121
+ opacity: 1,
122
+ transform: "rotate(3deg) translate(0px, -4px)"
123
+ }
124
+ }
125
+ )
126
+ }
127
+ );
128
+ };
129
+ export {
130
+ TopProgress,
131
+ finishProgress,
132
+ startProgress,
133
+ withProgress
134
+ };
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "react-top-progress",
3
+ "version": "0.1.0",
4
+ "description": "A lightweight modern top loading progress bar for React",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "keywords": [
12
+ "react",
13
+ "progress",
14
+ "loader",
15
+ "top-loader",
16
+ "route-progress"
17
+ ],
18
+ "author": "Harry Mate",
19
+ "license": "MIT",
20
+ "scripts": {
21
+ "build": "tsup src/index.ts --format cjs,esm --dts"
22
+ },
23
+ "peerDependencies": {
24
+ "react": ">=18"
25
+ },
26
+ "devDependencies": {
27
+ "@types/react": "^19.2.14",
28
+ "tsup": "^8.5.1",
29
+ "typescript": "^5.9.3"
30
+ }
31
+ }