react-top-progress 1.0.3 → 1.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 CHANGED
@@ -1,19 +1,23 @@
1
1
  # react-top-progress
2
2
 
3
+ ![npm version](https://img.shields.io/npm/v/react-top-progress)
4
+ ![downloads](https://img.shields.io/npm/dw/react-top-progress)
5
+ ![license](https://img.shields.io/npm/l/react-top-progress)
6
+
3
7
  **<a href="https://react-test-progress.vercel.app/" target="_blank">👉 View Live Demo</a>**
4
8
 
5
- A lightweight, modern, and performant top loading progress bar for React. Built for seamless user experiences with zero dependencies!
9
+ A lightweight, modern, and performant top loading progress bar for React. Built for seamless user experiences with **zero dependencies**!
6
10
 
7
11
  Perfect for Next.js App Router, Vite, and standard SPA architectures.
8
12
 
9
- ## Features
13
+ ## 🚀 Why react-top-progress?
10
14
 
11
- - **0 Dependencies**: Tiny footprint (~1.8kb gzipped).
12
- - **Advanced Loaders**: `continuous` and `static` start modes matching network logic.
13
- - **Premium DX**: Controlled programmatically with simple global API hooks, or direct React `ref`.
14
- - **Customizable**: Add gradients, background container colors, custom CSS classes, and glows.
15
- - **Performant**: Uses `transform: translateZ(0)` hardware acceleration and `cubic-bezier` CSS transitions.
16
- - **TypeScript ready**: Full types included.
15
+ - **SSR & App Router Safe**: Fully compatible with Next.js SSR & App Router natively
16
+ - **Framework Agnostic**: Works perfectly in React, Vite, CRA, Remix, React Router
17
+ - **0 Dependencies**: No weird transitive bloat
18
+ - **📦 Tiny Bundle Size**: ~1.9kb gzipped (Tree-shakable)
19
+ - **Premium Aesthetics**: Smooth realistic loading animation with beautiful gradients & glows
20
+ - **Promise Integrations**: Auto-wrapping utilities like `withProgress` for API fetching
17
21
 
18
22
  ## Installation
19
23
 
@@ -21,30 +25,48 @@ Perfect for Next.js App Router, Vite, and standard SPA architectures.
21
25
  npm install react-top-progress
22
26
  ```
23
27
 
24
- **Or using yarn / pnpm / bun:**
28
+ ## Quick Start (Next.js / Vite / React)
25
29
 
26
- ```bash
27
- yarn add react-top-progress
28
- pnpm add react-top-progress
29
- bun add react-top-progress
30
+ ### Method 1: The Global Provider (Recommended)
31
+
32
+ This approach automatically provides all routing configurations and is incredibly clean.
33
+
34
+ ```jsx
35
+ import { ProgressProvider, useAutoProgress } from "react-top-progress";
36
+
37
+ export default function RootLayout({ children }) {
38
+ // Use generic auto-route progress detector or just trigger global effects
39
+ // Optional but recommended for easy routing coverage!
40
+
41
+ return (
42
+ <ProgressProvider color="#29D" height={3} shadow={true}>
43
+ {children}
44
+ </ProgressProvider>
45
+ );
46
+ }
30
47
  ```
31
48
 
32
- ## Quick Start
49
+ ### Method 2: Next.js App Router Generic Auto-Routing
33
50
 
34
- At the top level of your application (like Next.js `app/layout.jsx` or Vite `src/App.jsx`), render the component.
35
- Works flawlessly in both **JSX** and **TSX**!
51
+ Because Next.js App Router removed native router events, you can quickly auto-wire progress with the `useRouteProgress` hook, which intrinsically detects all link navigation globally using zero-external dependencies!
36
52
 
37
53
  ```jsx
38
- import { TopProgress } from "react-top-progress";
54
+ "use client";
55
+
56
+ import { TopProgress, useRouteProgress } from "react-top-progress";
57
+
58
+ export default function Layout({ children }) {
59
+ useRouteProgress(); // Auto-detects clicks, routing events, and history pushState!
39
60
 
40
- export default function RootLayout({ children }) {
41
61
  return (
42
- <html lang="en">
43
- <body>
44
- <TopProgress />
45
- {children}
46
- </body>
47
- </html>
62
+ <>
63
+ <TopProgress
64
+ height={4}
65
+ color="linear-gradient(90deg, #ff5500, #ff0080)"
66
+ />
67
+ {/* Your Top Nav, Sidebar, etc. */}
68
+ {children}
69
+ </>
48
70
  );
49
71
  }
50
72
  ```
@@ -59,53 +81,52 @@ You can trigger the loader from anywhere in your codebase by importing the funct
59
81
  | `continuousStart(startingValue?, refreshRate?)` | `number`, `number` | Starts with a random value (20-30), then slowly ticks up (2-10) repetitively. Reaches max 90%. |
60
82
  | `staticStart(startingValue?)` | `number` | Starts the loader with a random static value between 30-50, waiting for completion. |
61
83
  | `complete()` / `finishProgress()` | | Instantly pushes the indicator to 100% and gracefully fades it out. |
84
+ | `resetProgress()` | | Instantly zeroes out and hides the progress bar without fading. |
62
85
  | `increase(value)` | `number` | Manually increments the loader by a specific amount. |
63
86
  | `decrease(value)` | `number` | Manually decrements the loader by a specific amount. |
64
87
  | `setProgress(value)` | `number` | Hardsets to a specific progress percentage. |
65
88
  | `getProgress()` | | Returns the current progress percentage (0 - 100). |
66
89
  | `withProgress(promise)` | `Promise<T>` | Wraps an asynchronous action. `startProgress()` executes automatically before and completes automatically afterward. |
67
90
 
68
- ### Global Usage Example
91
+ ### Promise Wrapper Example
69
92
 
70
93
  ```jsx
71
- import {
72
- startProgress,
73
- finishProgress,
74
- withProgress,
75
- } from "react-top-progress";
76
-
77
- async function fetchData() {
78
- startProgress("continuous");
79
- try {
80
- await apiCall();
81
- } finally {
82
- finishProgress(); // alias of complete()
83
- }
84
- }
94
+ import { withProgress } from "react-top-progress";
85
95
 
86
- // Or functionally wrap promises for ultimate DX:
87
- const loadProfile = () => withProgress(fetch("/api/profile"));
96
+ const data = await withProgress(fetch("/api/users"));
88
97
  ```
89
98
 
90
99
  ## Component Properties
91
100
 
92
101
  You can customize `<TopProgress />` passing any of the following props:
93
102
 
94
- | Property | Type | Default | Description |
95
- | -------------------- | --------------- | --------------- | ------------------------------------------------------------------------------------------------- |
96
- | `progress` | `number` | | Hardset the progress (0-100) if you want to bypass the internal store and control state yourself. |
97
- | `color` | `string` | `"red"` | Background color of the bar. Accepts `linear-gradient(...)` or hex codes. |
98
- | `height` | `number` | `2` | Height of the progress bar in pixels. |
99
- | `shadow` | `boolean` | `true` | Appends a glorious trailing drop-glow peg matching the bar's color tracking its head. |
100
- | `background` | `string` | `"transparent"` | Fills the container div's background layer under the progress element. |
101
- | `transitionTime` | `number` | `300` | Fade transition time (ms) for the fade-out completion sequence. |
102
- | `loaderSpeed` | `number` | `500` | Loader width transition speed (ms). |
103
- | `waitingTime` | `number` | `1000` | The delay time (ms) loader waits at 100% width before fading entirely out. |
104
- | `style` | `CSSProperties` | | Inject custom JSX styles directly onto the loader element. |
105
- | `className` | `string` | | Apply a specific custom CSS class to the loader element. |
106
- | `containerStyle` | `CSSProperties` | | Configure inline styles for the fixed `<div />` wrapper container. |
107
- | `containerClassName` | `string` | | Custom CSS class applied onto the wrapper container. |
108
- | `onLoaderFinished` | `() => void` | | A callback function executing precisely when the loader fully hits 100% max width. |
103
+ | Property | Type | Default | Description |
104
+ | -------------------- | --------------- | -------------------------------- | ------------------------------------------------------------------------------------------------- |
105
+ | `progress` | `number` | | Hardset the progress (0-100) if you want to bypass the internal store and control state yourself. |
106
+ | `color` | `string` | `"#29D"` | Background color of the bar. Accepts `linear-gradient(...)` or hex codes. |
107
+ | `height` | `number` | `3` | Height of the progress bar in pixels. |
108
+ | `shadow` | `boolean` | `true` | Appends a glorious trailing drop-glow peg matching the bar's color tracking its head. |
109
+ | `background` | `string` | `"transparent"` | Fills the container div's background layer under the progress element. |
110
+ | `transitionSpeed` | `number` | `200` | Fade transition time (ms) for the fade-out completion sequence. |
111
+ | `loaderSpeed` | `number` | `500` | Loader width transition speed (ms). |
112
+ | `waitingTime` | `number` | `1000` | The delay time (ms) loader waits at 100% width before fading entirely out. |
113
+ | `easing` | `string` | `"cubic-bezier(0.4, 0, 0.2, 1)"` | Customizable CSS animation timing path curve. |
114
+ | `style` | `CSSProperties` | | Inject custom JSX styles directly onto the loader element. |
115
+ | `className` | `string` | | Apply a specific custom CSS class to the loader element. |
116
+ | `containerStyle` | `CSSProperties` | | Configure inline styles for the fixed `<div />` wrapper container. |
117
+ | `containerClassName` | `string` | | Custom CSS class applied onto the wrapper container. |
118
+ | `onLoaderFinished` | `() => void` | | A callback function executing precisely when the loader fully hits 100% max width. |
119
+
120
+ ## Compared to Alternatives
121
+
122
+ Developers need to know why to switch. Here is the breakdown:
123
+
124
+ | Library | Dependencies | SSR Safe | Routing Bindings | Promise Wrap | Bundle Size |
125
+ | ----------------------- | ---------------------- | -------- | ----------------- | ------------ | ----------------- |
126
+ | `nprogress` | Old jQuery style logic | No | ❌ | ❌ | Larger |
127
+ | `nextjs-toploader` | Next-specific only | Yes | ✅ (Next.js) | ❌ | Medium |
128
+ | `react-top-loading-bar` | React mostly | Some | ❌ | ❌ | Medium |
129
+ | **react-top-progress** | **0 dependencies** | **Yes** | **✅ (Agnostic)** | **✅** | **Tiny (~1.9kb)** |
109
130
 
110
131
  ## Credits
111
132
 
package/dist/index.d.mts CHANGED
@@ -1,4 +1,5 @@
1
1
  import React, { CSSProperties } from 'react';
2
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
3
 
3
4
  interface TopProgressProps {
4
5
  progress?: number;
@@ -10,8 +11,10 @@ interface TopProgressProps {
10
11
  containerStyle?: CSSProperties;
11
12
  shadowStyle?: CSSProperties;
12
13
  transitionTime?: number;
14
+ transitionSpeed?: number;
13
15
  loaderSpeed?: number;
14
16
  waitingTime?: number;
17
+ easing?: string;
15
18
  className?: string;
16
19
  containerClassName?: string;
17
20
  onLoaderFinished?: () => void;
@@ -31,6 +34,14 @@ interface TopProgressRef {
31
34
  }
32
35
  declare const TopProgress: React.ForwardRefExoticComponent<TopProgressProps & React.RefAttributes<TopProgressRef>>;
33
36
 
37
+ interface ProgressProviderProps extends TopProgressProps {
38
+ children: React.ReactNode;
39
+ }
40
+ declare const ProgressProvider: ({ children, ...props }: ProgressProviderProps) => react_jsx_runtime.JSX.Element;
41
+
42
+ declare const useAutoProgress: () => void;
43
+ declare const useRouteProgress: () => void;
44
+
34
45
  declare const startProgress: (type?: "continuous" | "static") => void;
35
46
  declare const continuousStart: (startingValue?: number, refreshRate?: number) => void;
36
47
  declare const staticStart: (startingValue?: number) => void;
@@ -40,6 +51,7 @@ declare const increaseProgress: (value: number) => void;
40
51
  declare const decreaseProgress: (value: number) => void;
41
52
  declare const getProgress: () => number | null;
42
53
  declare const setProgress: (value: number) => void;
54
+ declare const resetProgress: () => void;
43
55
  declare const withProgress: <T>(promise: Promise<T>) => Promise<T>;
44
56
 
45
- export { TopProgress, type TopProgressProps, type TopProgressRef, completeProgress, continuousStart, decreaseProgress, finishProgress, getProgress, increaseProgress, setProgress, startProgress, staticStart, withProgress };
57
+ export { ProgressProvider, type ProgressProviderProps, TopProgress, type TopProgressProps, type TopProgressRef, completeProgress, continuousStart, decreaseProgress, finishProgress, getProgress, increaseProgress, resetProgress, setProgress, startProgress, staticStart, useAutoProgress, useRouteProgress, withProgress };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import React, { CSSProperties } from 'react';
2
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
3
 
3
4
  interface TopProgressProps {
4
5
  progress?: number;
@@ -10,8 +11,10 @@ interface TopProgressProps {
10
11
  containerStyle?: CSSProperties;
11
12
  shadowStyle?: CSSProperties;
12
13
  transitionTime?: number;
14
+ transitionSpeed?: number;
13
15
  loaderSpeed?: number;
14
16
  waitingTime?: number;
17
+ easing?: string;
15
18
  className?: string;
16
19
  containerClassName?: string;
17
20
  onLoaderFinished?: () => void;
@@ -31,6 +34,14 @@ interface TopProgressRef {
31
34
  }
32
35
  declare const TopProgress: React.ForwardRefExoticComponent<TopProgressProps & React.RefAttributes<TopProgressRef>>;
33
36
 
37
+ interface ProgressProviderProps extends TopProgressProps {
38
+ children: React.ReactNode;
39
+ }
40
+ declare const ProgressProvider: ({ children, ...props }: ProgressProviderProps) => react_jsx_runtime.JSX.Element;
41
+
42
+ declare const useAutoProgress: () => void;
43
+ declare const useRouteProgress: () => void;
44
+
34
45
  declare const startProgress: (type?: "continuous" | "static") => void;
35
46
  declare const continuousStart: (startingValue?: number, refreshRate?: number) => void;
36
47
  declare const staticStart: (startingValue?: number) => void;
@@ -40,6 +51,7 @@ declare const increaseProgress: (value: number) => void;
40
51
  declare const decreaseProgress: (value: number) => void;
41
52
  declare const getProgress: () => number | null;
42
53
  declare const setProgress: (value: number) => void;
54
+ declare const resetProgress: () => void;
43
55
  declare const withProgress: <T>(promise: Promise<T>) => Promise<T>;
44
56
 
45
- export { TopProgress, type TopProgressProps, type TopProgressRef, completeProgress, continuousStart, decreaseProgress, finishProgress, getProgress, increaseProgress, setProgress, startProgress, staticStart, withProgress };
57
+ export { ProgressProvider, type ProgressProviderProps, TopProgress, type TopProgressProps, type TopProgressRef, completeProgress, continuousStart, decreaseProgress, finishProgress, getProgress, increaseProgress, resetProgress, setProgress, startProgress, staticStart, useAutoProgress, useRouteProgress, withProgress };
package/dist/index.js CHANGED
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  // src/index.ts
22
22
  var index_exports = {};
23
23
  __export(index_exports, {
24
+ ProgressProvider: () => ProgressProvider,
24
25
  TopProgress: () => TopProgress,
25
26
  completeProgress: () => completeProgress,
26
27
  continuousStart: () => continuousStart,
@@ -28,9 +29,12 @@ __export(index_exports, {
28
29
  finishProgress: () => finishProgress,
29
30
  getProgress: () => getProgress,
30
31
  increaseProgress: () => increaseProgress,
32
+ resetProgress: () => resetProgress,
31
33
  setProgress: () => setProgress,
32
34
  startProgress: () => startProgress,
33
35
  staticStart: () => staticStart,
36
+ useAutoProgress: () => useAutoProgress,
37
+ useRouteProgress: () => useRouteProgress,
34
38
  withProgress: () => withProgress
35
39
  });
36
40
  module.exports = __toCommonJS(index_exports);
@@ -96,6 +100,11 @@ var ProgressStore = class {
96
100
  this.notify();
97
101
  }, this.config.waitingTime);
98
102
  }
103
+ reset() {
104
+ this.cleanup();
105
+ this.value = null;
106
+ this.notify();
107
+ }
99
108
  set(value) {
100
109
  this.cleanup();
101
110
  this.value = value;
@@ -139,6 +148,7 @@ var increaseProgress = (value) => progressStore.increase(value);
139
148
  var decreaseProgress = (value) => progressStore.decrease(value);
140
149
  var getProgress = () => progressStore.getProgress();
141
150
  var setProgress = (value) => progressStore.set(value);
151
+ var resetProgress = () => progressStore.reset();
142
152
  var withProgress = async (promise) => {
143
153
  startProgress();
144
154
  try {
@@ -156,16 +166,18 @@ var import_jsx_runtime = require("react/jsx-runtime");
156
166
  var TopProgress = (0, import_react.forwardRef)(
157
167
  ({
158
168
  progress: controlledProgress,
159
- color = "red",
169
+ color = "#29D",
160
170
  shadow = true,
161
- height = 2,
171
+ height = 3,
162
172
  background = "transparent",
163
173
  style,
164
174
  containerStyle,
165
175
  shadowStyle,
166
- transitionTime = 300,
176
+ transitionTime,
177
+ transitionSpeed = 200,
167
178
  loaderSpeed = 500,
168
179
  waitingTime = 1e3,
180
+ easing = "cubic-bezier(0.4, 0, 0.2, 1)",
169
181
  className,
170
182
  containerClassName,
171
183
  onLoaderFinished,
@@ -177,6 +189,7 @@ var TopProgress = (0, import_react.forwardRef)(
177
189
  const [internalProgress, setInternalProgress] = (0, import_react.useState)(
178
190
  store.getValue()
179
191
  );
192
+ const [mounted, setMounted] = (0, import_react.useState)(false);
180
193
  (0, import_react.useImperativeHandle)(ref, () => ({
181
194
  start: (type) => store.start(type),
182
195
  continuousStart: (s, r) => store.continuousStart(s, r),
@@ -187,12 +200,14 @@ var TopProgress = (0, import_react.forwardRef)(
187
200
  getProgress: () => store.getProgress(),
188
201
  set: (v) => store.set(v)
189
202
  }));
203
+ const fadeSpeed = transitionTime !== void 0 ? transitionTime : transitionSpeed;
190
204
  (0, import_react.useEffect)(() => {
191
- store.config.transitionTime = transitionTime;
205
+ store.config.transitionTime = fadeSpeed;
192
206
  store.config.loaderSpeed = loaderSpeed;
193
207
  store.config.waitingTime = waitingTime;
194
- }, [transitionTime, loaderSpeed, waitingTime, store]);
208
+ }, [fadeSpeed, loaderSpeed, waitingTime, store]);
195
209
  (0, import_react.useEffect)(() => {
210
+ setMounted(true);
196
211
  return store.subscribe(setInternalProgress);
197
212
  }, [store]);
198
213
  const progress = controlledProgress !== void 0 ? controlledProgress : internalProgress;
@@ -201,7 +216,7 @@ var TopProgress = (0, import_react.forwardRef)(
201
216
  onLoaderFinished();
202
217
  }
203
218
  }, [progress, onLoaderFinished]);
204
- if (progress === null) {
219
+ if (!mounted || progress === null) {
205
220
  return null;
206
221
  }
207
222
  const isGradient = color.includes("gradient(");
@@ -231,7 +246,7 @@ var TopProgress = (0, import_react.forwardRef)(
231
246
  width: `${progress}%`,
232
247
  height: "100%",
233
248
  ...backgroundStyle,
234
- transition: `width ${loaderSpeed}ms cubic-bezier(0.4, 0, 0.2, 1), opacity ${transitionTime}ms ease`,
249
+ transition: `width ${loaderSpeed}ms ${easing}, opacity ${fadeSpeed}ms ease`,
235
250
  opacity: progress === 100 ? 0 : 1,
236
251
  transform: "translateZ(0)",
237
252
  ...style
@@ -260,8 +275,84 @@ var TopProgress = (0, import_react.forwardRef)(
260
275
  }
261
276
  );
262
277
  TopProgress.displayName = "TopProgress";
278
+
279
+ // src/ProgressProvider.tsx
280
+ var import_jsx_runtime2 = require("react/jsx-runtime");
281
+ var ProgressProvider = ({
282
+ children,
283
+ ...props
284
+ }) => {
285
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
286
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(TopProgress, { ...props }),
287
+ children
288
+ ] });
289
+ };
290
+
291
+ // src/hooks.ts
292
+ var import_react2 = require("react");
293
+ var useAutoProgress = () => {
294
+ (0, import_react2.useEffect)(() => {
295
+ startProgress();
296
+ return () => {
297
+ finishProgress();
298
+ };
299
+ }, []);
300
+ };
301
+ var useRouteProgress = () => {
302
+ (0, import_react2.useEffect)(() => {
303
+ const handleAnchorClick = (event) => {
304
+ const target = event.currentTarget;
305
+ if (!target.href || target.target === "_blank" || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || target.hasAttribute("download")) {
306
+ return;
307
+ }
308
+ try {
309
+ const targetUrl = new URL(target.href);
310
+ const currentUrl = new URL(window.location.href);
311
+ if (targetUrl.origin === currentUrl.origin && targetUrl.pathname !== currentUrl.pathname) {
312
+ startProgress();
313
+ }
314
+ } catch (err) {
315
+ }
316
+ };
317
+ const handleMutation = () => {
318
+ const anchors = document.querySelectorAll("a[href]");
319
+ anchors.forEach((anchor) => {
320
+ anchor.removeEventListener("click", handleAnchorClick);
321
+ anchor.addEventListener("click", handleAnchorClick);
322
+ });
323
+ };
324
+ const mutationObserver = new MutationObserver(handleMutation);
325
+ mutationObserver.observe(document.body, { childList: true, subtree: true });
326
+ handleMutation();
327
+ const originalPushState = window.history.pushState;
328
+ const originalReplaceState = window.history.replaceState;
329
+ window.history.pushState = function(...args) {
330
+ finishProgress();
331
+ return originalPushState.apply(this, args);
332
+ };
333
+ window.history.replaceState = function(...args) {
334
+ finishProgress();
335
+ return originalReplaceState.apply(this, args);
336
+ };
337
+ const handlePopState = () => {
338
+ finishProgress();
339
+ };
340
+ window.addEventListener("popstate", handlePopState);
341
+ return () => {
342
+ mutationObserver.disconnect();
343
+ const anchors = document.querySelectorAll("a[href]");
344
+ anchors.forEach((anchor) => {
345
+ anchor.removeEventListener("click", handleAnchorClick);
346
+ });
347
+ window.history.pushState = originalPushState;
348
+ window.history.replaceState = originalReplaceState;
349
+ window.removeEventListener("popstate", handlePopState);
350
+ };
351
+ }, []);
352
+ };
263
353
  // Annotate the CommonJS export names for ESM import in node:
264
354
  0 && (module.exports = {
355
+ ProgressProvider,
265
356
  TopProgress,
266
357
  completeProgress,
267
358
  continuousStart,
@@ -269,8 +360,11 @@ TopProgress.displayName = "TopProgress";
269
360
  finishProgress,
270
361
  getProgress,
271
362
  increaseProgress,
363
+ resetProgress,
272
364
  setProgress,
273
365
  startProgress,
274
366
  staticStart,
367
+ useAutoProgress,
368
+ useRouteProgress,
275
369
  withProgress
276
370
  });
package/dist/index.mjs CHANGED
@@ -66,6 +66,11 @@ var ProgressStore = class {
66
66
  this.notify();
67
67
  }, this.config.waitingTime);
68
68
  }
69
+ reset() {
70
+ this.cleanup();
71
+ this.value = null;
72
+ this.notify();
73
+ }
69
74
  set(value) {
70
75
  this.cleanup();
71
76
  this.value = value;
@@ -109,6 +114,7 @@ var increaseProgress = (value) => progressStore.increase(value);
109
114
  var decreaseProgress = (value) => progressStore.decrease(value);
110
115
  var getProgress = () => progressStore.getProgress();
111
116
  var setProgress = (value) => progressStore.set(value);
117
+ var resetProgress = () => progressStore.reset();
112
118
  var withProgress = async (promise) => {
113
119
  startProgress();
114
120
  try {
@@ -126,16 +132,18 @@ import { jsx } from "react/jsx-runtime";
126
132
  var TopProgress = forwardRef(
127
133
  ({
128
134
  progress: controlledProgress,
129
- color = "red",
135
+ color = "#29D",
130
136
  shadow = true,
131
- height = 2,
137
+ height = 3,
132
138
  background = "transparent",
133
139
  style,
134
140
  containerStyle,
135
141
  shadowStyle,
136
- transitionTime = 300,
142
+ transitionTime,
143
+ transitionSpeed = 200,
137
144
  loaderSpeed = 500,
138
145
  waitingTime = 1e3,
146
+ easing = "cubic-bezier(0.4, 0, 0.2, 1)",
139
147
  className,
140
148
  containerClassName,
141
149
  onLoaderFinished,
@@ -147,6 +155,7 @@ var TopProgress = forwardRef(
147
155
  const [internalProgress, setInternalProgress] = useState(
148
156
  store.getValue()
149
157
  );
158
+ const [mounted, setMounted] = useState(false);
150
159
  useImperativeHandle(ref, () => ({
151
160
  start: (type) => store.start(type),
152
161
  continuousStart: (s, r) => store.continuousStart(s, r),
@@ -157,12 +166,14 @@ var TopProgress = forwardRef(
157
166
  getProgress: () => store.getProgress(),
158
167
  set: (v) => store.set(v)
159
168
  }));
169
+ const fadeSpeed = transitionTime !== void 0 ? transitionTime : transitionSpeed;
160
170
  useEffect(() => {
161
- store.config.transitionTime = transitionTime;
171
+ store.config.transitionTime = fadeSpeed;
162
172
  store.config.loaderSpeed = loaderSpeed;
163
173
  store.config.waitingTime = waitingTime;
164
- }, [transitionTime, loaderSpeed, waitingTime, store]);
174
+ }, [fadeSpeed, loaderSpeed, waitingTime, store]);
165
175
  useEffect(() => {
176
+ setMounted(true);
166
177
  return store.subscribe(setInternalProgress);
167
178
  }, [store]);
168
179
  const progress = controlledProgress !== void 0 ? controlledProgress : internalProgress;
@@ -171,7 +182,7 @@ var TopProgress = forwardRef(
171
182
  onLoaderFinished();
172
183
  }
173
184
  }, [progress, onLoaderFinished]);
174
- if (progress === null) {
185
+ if (!mounted || progress === null) {
175
186
  return null;
176
187
  }
177
188
  const isGradient = color.includes("gradient(");
@@ -201,7 +212,7 @@ var TopProgress = forwardRef(
201
212
  width: `${progress}%`,
202
213
  height: "100%",
203
214
  ...backgroundStyle,
204
- transition: `width ${loaderSpeed}ms cubic-bezier(0.4, 0, 0.2, 1), opacity ${transitionTime}ms ease`,
215
+ transition: `width ${loaderSpeed}ms ${easing}, opacity ${fadeSpeed}ms ease`,
205
216
  opacity: progress === 100 ? 0 : 1,
206
217
  transform: "translateZ(0)",
207
218
  ...style
@@ -230,7 +241,83 @@ var TopProgress = forwardRef(
230
241
  }
231
242
  );
232
243
  TopProgress.displayName = "TopProgress";
244
+
245
+ // src/ProgressProvider.tsx
246
+ import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
247
+ var ProgressProvider = ({
248
+ children,
249
+ ...props
250
+ }) => {
251
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
252
+ /* @__PURE__ */ jsx2(TopProgress, { ...props }),
253
+ children
254
+ ] });
255
+ };
256
+
257
+ // src/hooks.ts
258
+ import { useEffect as useEffect2 } from "react";
259
+ var useAutoProgress = () => {
260
+ useEffect2(() => {
261
+ startProgress();
262
+ return () => {
263
+ finishProgress();
264
+ };
265
+ }, []);
266
+ };
267
+ var useRouteProgress = () => {
268
+ useEffect2(() => {
269
+ const handleAnchorClick = (event) => {
270
+ const target = event.currentTarget;
271
+ if (!target.href || target.target === "_blank" || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || target.hasAttribute("download")) {
272
+ return;
273
+ }
274
+ try {
275
+ const targetUrl = new URL(target.href);
276
+ const currentUrl = new URL(window.location.href);
277
+ if (targetUrl.origin === currentUrl.origin && targetUrl.pathname !== currentUrl.pathname) {
278
+ startProgress();
279
+ }
280
+ } catch (err) {
281
+ }
282
+ };
283
+ const handleMutation = () => {
284
+ const anchors = document.querySelectorAll("a[href]");
285
+ anchors.forEach((anchor) => {
286
+ anchor.removeEventListener("click", handleAnchorClick);
287
+ anchor.addEventListener("click", handleAnchorClick);
288
+ });
289
+ };
290
+ const mutationObserver = new MutationObserver(handleMutation);
291
+ mutationObserver.observe(document.body, { childList: true, subtree: true });
292
+ handleMutation();
293
+ const originalPushState = window.history.pushState;
294
+ const originalReplaceState = window.history.replaceState;
295
+ window.history.pushState = function(...args) {
296
+ finishProgress();
297
+ return originalPushState.apply(this, args);
298
+ };
299
+ window.history.replaceState = function(...args) {
300
+ finishProgress();
301
+ return originalReplaceState.apply(this, args);
302
+ };
303
+ const handlePopState = () => {
304
+ finishProgress();
305
+ };
306
+ window.addEventListener("popstate", handlePopState);
307
+ return () => {
308
+ mutationObserver.disconnect();
309
+ const anchors = document.querySelectorAll("a[href]");
310
+ anchors.forEach((anchor) => {
311
+ anchor.removeEventListener("click", handleAnchorClick);
312
+ });
313
+ window.history.pushState = originalPushState;
314
+ window.history.replaceState = originalReplaceState;
315
+ window.removeEventListener("popstate", handlePopState);
316
+ };
317
+ }, []);
318
+ };
233
319
  export {
320
+ ProgressProvider,
234
321
  TopProgress,
235
322
  completeProgress,
236
323
  continuousStart,
@@ -238,8 +325,11 @@ export {
238
325
  finishProgress,
239
326
  getProgress,
240
327
  increaseProgress,
328
+ resetProgress,
241
329
  setProgress,
242
330
  startProgress,
243
331
  staticStart,
332
+ useAutoProgress,
333
+ useRouteProgress,
244
334
  withProgress
245
335
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-top-progress",
3
- "version": "1.0.3",
3
+ "version": "1.1.0",
4
4
  "description": "A lightweight modern top loading progress bar for React",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -11,10 +11,25 @@
11
11
  ],
12
12
  "keywords": [
13
13
  "react",
14
- "progress",
15
- "loader",
16
- "top-loader",
17
- "route-progress"
14
+ "nextjs",
15
+ "nextjs loader",
16
+ "ssr progress bar",
17
+ "top loading bar",
18
+ "route progress",
19
+ "react progress bar",
20
+ "nextjs app router loader",
21
+ "lightweight loader",
22
+ "modern progress bar",
23
+ "nprogress",
24
+ "vite loader",
25
+ "react top loading bar",
26
+ "loading bar",
27
+ "page transition",
28
+ "progress indicator",
29
+ "react-router loader",
30
+ "remix loader",
31
+ "next14 loader",
32
+ "next15 loader"
18
33
  ],
19
34
  "author": "Harry Mate",
20
35
  "license": "MIT",