react-top-progress 0.1.1 → 1.0.1

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,22 +1,19 @@
1
1
  # react-top-progress
2
2
 
3
- ![Demo of react-top-progress](demo_react_top_progress.png)
3
+ **<a href="https://react-test-progress.vercel.app/" target="_blank">👉 View Live Demo</a>**
4
4
 
5
- **[👉 View Live Demo](https://react-test-progress.vercel.app/)**
5
+ A lightweight, modern, and performant top loading progress bar for React. Built for seamless user experiences with zero dependencies!
6
6
 
7
- A lightweight, modern, and performant top loading progress bar for React.
8
-
9
- Perfect for Next.js App Router, Vite, and standard single-page applications.
7
+ Perfect for Next.js App Router, Vite, and standard SPA architectures.
10
8
 
11
9
  ## Features
12
10
 
13
- - **0 dependencies**: Tiny footprint (~1kb gzipped).
14
- - **Realistic loading feel**: Increments incrementally like real network requests.
15
- - **Premium DX**: Controlled programmatically with simple `startProgress` / `finishProgress` API.
16
- - **Gradient & Glow Support**: Fully customizable colors, shadow pegs, and effects.
17
- - **Performant**: Uses `transform` hardware acceleration and `cubic-bezier` transitions.
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.
18
16
  - **TypeScript ready**: Full types included.
19
- - **React 18+ & Next.js ready**: Contains `"use client"` where necessary.
20
17
 
21
18
  ## Installation
22
19
 
@@ -34,9 +31,7 @@ bun add react-top-progress
34
31
 
35
32
  ## Quick Start
36
33
 
37
- ### 1. Add the component to your root layout/app
38
-
39
- At the top level of your application (like Next.js `app/layout.tsx` or Vite `src/App.tsx`), render the `<TopProgress />` component.
34
+ At the top level of your application (like Next.js `app/layout.tsx` or Vite `src/App.tsx`), render the component.
40
35
 
41
36
  ```tsx
42
37
  import { TopProgress } from "react-top-progress";
@@ -53,52 +48,71 @@ export default function RootLayout({ children }) {
53
48
  }
54
49
  ```
55
50
 
56
- ### 2. Control it programmatically
51
+ ## Built-in Methods
57
52
 
58
- Import the API from anywhere in your app to trigger the progress bar manually!
53
+ You can trigger the loader from anywhere in your codebase by importing the functions globally, or via a React `ref` assigned to `<TopProgress ref={myRef} />`.
59
54
 
60
- ```tsx
61
- import { startProgress, finishProgress } from "react-top-progress";
55
+ | Method | Parameters | Description |
56
+ | ----------------------------------------------- | -------------------------- | -------------------------------------------------------------------------------------------------------------------- |
57
+ | `start(loaderType?)` | `"continuous" \| "static"` | Starts the loading indicator. Default is continuous. |
58
+ | `continuousStart(startingValue?, refreshRate?)` | `number`, `number` | Starts with a random value (20-30), then slowly ticks up (2-10) repetitively. Reaches max 90%. |
59
+ | `staticStart(startingValue?)` | `number` | Starts the loader with a random static value between 30-50, waiting for completion. |
60
+ | `complete()` / `finishProgress()` | | Instantly pushes the indicator to 100% and gracefully fades it out. |
61
+ | `increase(value)` | `number` | Manually increments the loader by a specific amount. |
62
+ | `decrease(value)` | `number` | Manually decrements the loader by a specific amount. |
63
+ | `setProgress(value)` | `number` | Hardsets to a specific progress percentage. |
64
+ | `getProgress()` | | Returns the current progress percentage (0 - 100). |
65
+ | `withProgress(promise)` | `Promise<T>` | Wraps an asynchronous action. `startProgress()` executes automatically before and completes automatically afterward. |
62
66
 
63
- async function handleAction() {
64
- startProgress();
67
+ ### Global Usage Example
68
+
69
+ ```tsx
70
+ import {
71
+ startProgress,
72
+ finishProgress,
73
+ withProgress,
74
+ } from "react-top-progress";
75
+
76
+ async function fetchData() {
77
+ startProgress("continuous");
65
78
  try {
66
- await someHeavyTask();
79
+ await apiCall();
67
80
  } finally {
68
- finishProgress();
81
+ finishProgress(); // alias of complete()
69
82
  }
70
83
  }
71
- ```
72
84
 
73
- ### 3. The `withProgress` Utility
74
-
75
- For an even better Developer Experience, simply wrap your promises securely with `withProgress`!
76
-
77
- ```tsx
78
- import { withProgress } from "react-top-progress";
79
-
80
- async function handleFetch() {
81
- const data = await withProgress(fetch("/api/data"));
82
- console.log(data);
83
- }
85
+ // Or functionally wrap promises for ultimate DX:
86
+ const loadProfile = () => withProgress(fetch("/api/profile"));
84
87
  ```
85
88
 
86
- ## Customization API
89
+ ## Component Properties
87
90
 
88
- The `TopProgress` component accepts the following props:
91
+ You can customize `<TopProgress />` passing any of the following props:
89
92
 
90
- | Prop | Type | Default | Description |
91
- | ----------- | --------- | ----------- | ------------------------------------------------------------------------------------------------------------ |
92
- | `color` | `string` | `"#29D"` | Flat hex color or `linear-gradient(...)` function string |
93
- | `height` | `number` | `3` | Height of the progress bar in pixels |
94
- | `showGlow` | `boolean` | `true` | Whether to display the premium box-shadow drop-glow and trailing peg |
95
- | `glowColor` | `string` | `undefined` | Optionally override the color of the glow effect. By default, it infers realistically from your main `color` |
96
- | `zIndex` | `number` | `9999` | The z-index stacking context level. |
93
+ | Property | Type | Default | Description |
94
+ | -------------------- | --------------- | --------------- | ------------------------------------------------------------------------------------------------- |
95
+ | `progress` | `number` | | Hardset the progress (0-100) if you want to bypass the internal store and control state yourself. |
96
+ | `color` | `string` | `"red"` | Background color of the bar. Accepts `linear-gradient(...)` or hex codes. |
97
+ | `height` | `number` | `2` | Height of the progress bar in pixels. |
98
+ | `shadow` | `boolean` | `true` | Appends a glorious trailing drop-glow peg matching the bar's color tracking its head. |
99
+ | `background` | `string` | `"transparent"` | Fills the container div's background layer under the progress element. |
100
+ | `transitionTime` | `number` | `300` | Fade transition time (ms) for the fade-out completion sequence. |
101
+ | `loaderSpeed` | `number` | `500` | Loader width transition speed (ms). |
102
+ | `waitingTime` | `number` | `1000` | The delay time (ms) loader waits at 100% width before fading entirely out. |
103
+ | `style` | `CSSProperties` | | Inject custom JSX styles directly onto the loader element. |
104
+ | `className` | `string` | | Apply a specific custom CSS class to the loader element. |
105
+ | `containerStyle` | `CSSProperties` | | Configure inline styles for the fixed `<div />` wrapper container. |
106
+ | `containerClassName` | `string` | | Custom CSS class applied onto the wrapper container. |
107
+ | `onLoaderFinished` | `() => void` | | A callback function executing precisely when the loader fully hits 100% max width. |
97
108
 
98
- ## Credits & Inspiration
109
+ ## Credits
99
110
 
100
111
  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.
101
112
 
113
+ Built with ❤️ by **[Harry Mate](https://github.com/harrymate22)**.
114
+ 🌟 If you find this library helpful, consider dropping a **Star** on GitHub and **[following me (@harrymate22)](https://github.com/harrymate22)** for more open-source tools!
115
+
102
116
  ## License
103
117
 
104
- MIT
118
+ MIT © [harrymate22](https://github.com/harrymate22)
Binary file
package/dist/index.d.mts CHANGED
@@ -1,16 +1,45 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
1
+ import React, { CSSProperties } from 'react';
2
2
 
3
3
  interface TopProgressProps {
4
+ progress?: number;
4
5
  color?: string;
6
+ shadow?: boolean;
5
7
  height?: number;
8
+ background?: string;
9
+ style?: CSSProperties;
10
+ containerStyle?: CSSProperties;
11
+ shadowStyle?: CSSProperties;
12
+ transitionTime?: number;
13
+ loaderSpeed?: number;
14
+ waitingTime?: number;
15
+ className?: string;
16
+ containerClassName?: string;
17
+ onLoaderFinished?: () => void;
6
18
  showGlow?: boolean;
7
19
  glowColor?: string;
8
20
  zIndex?: number;
9
21
  }
10
- declare const TopProgress: ({ color, height, showGlow, glowColor, zIndex, }: TopProgressProps) => react_jsx_runtime.JSX.Element | null;
22
+ interface TopProgressRef {
23
+ start: (loaderType?: "continuous" | "static") => void;
24
+ continuousStart: (startingValue?: number, refreshRate?: number) => void;
25
+ staticStart: (startingValue?: number) => void;
26
+ complete: () => void;
27
+ increase: (value: number) => void;
28
+ decrease: (value: number) => void;
29
+ getProgress: () => number | null;
30
+ set: (value: number) => void;
31
+ }
32
+ declare const TopProgress: React.ForwardRefExoticComponent<TopProgressProps & React.RefAttributes<TopProgressRef>>;
11
33
 
12
- declare const startProgress: () => void;
34
+ declare const startProgress: (type?: "continuous" | "static") => void;
35
+ declare const continuousStart: (startingValue?: number, refreshRate?: number) => void;
36
+ declare const staticStart: (startingValue?: number) => void;
13
37
  declare const finishProgress: () => void;
38
+ declare const completeProgress: () => void;
39
+ declare const increaseProgress: (value: number) => void;
40
+ declare const decreaseProgress: (value: number) => void;
41
+ declare const getProgress: () => number | null;
42
+ declare const setProgress: (value: number) => void;
14
43
  declare const withProgress: <T>(promise: Promise<T>) => Promise<T>;
15
44
 
16
- export { TopProgress, finishProgress, startProgress, withProgress };
45
+ export { TopProgress, type TopProgressProps, type TopProgressRef, completeProgress, continuousStart, decreaseProgress, finishProgress, getProgress, increaseProgress, setProgress, startProgress, staticStart, withProgress };
package/dist/index.d.ts CHANGED
@@ -1,16 +1,45 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
1
+ import React, { CSSProperties } from 'react';
2
2
 
3
3
  interface TopProgressProps {
4
+ progress?: number;
4
5
  color?: string;
6
+ shadow?: boolean;
5
7
  height?: number;
8
+ background?: string;
9
+ style?: CSSProperties;
10
+ containerStyle?: CSSProperties;
11
+ shadowStyle?: CSSProperties;
12
+ transitionTime?: number;
13
+ loaderSpeed?: number;
14
+ waitingTime?: number;
15
+ className?: string;
16
+ containerClassName?: string;
17
+ onLoaderFinished?: () => void;
6
18
  showGlow?: boolean;
7
19
  glowColor?: string;
8
20
  zIndex?: number;
9
21
  }
10
- declare const TopProgress: ({ color, height, showGlow, glowColor, zIndex, }: TopProgressProps) => react_jsx_runtime.JSX.Element | null;
22
+ interface TopProgressRef {
23
+ start: (loaderType?: "continuous" | "static") => void;
24
+ continuousStart: (startingValue?: number, refreshRate?: number) => void;
25
+ staticStart: (startingValue?: number) => void;
26
+ complete: () => void;
27
+ increase: (value: number) => void;
28
+ decrease: (value: number) => void;
29
+ getProgress: () => number | null;
30
+ set: (value: number) => void;
31
+ }
32
+ declare const TopProgress: React.ForwardRefExoticComponent<TopProgressProps & React.RefAttributes<TopProgressRef>>;
11
33
 
12
- declare const startProgress: () => void;
34
+ declare const startProgress: (type?: "continuous" | "static") => void;
35
+ declare const continuousStart: (startingValue?: number, refreshRate?: number) => void;
36
+ declare const staticStart: (startingValue?: number) => void;
13
37
  declare const finishProgress: () => void;
38
+ declare const completeProgress: () => void;
39
+ declare const increaseProgress: (value: number) => void;
40
+ declare const decreaseProgress: (value: number) => void;
41
+ declare const getProgress: () => number | null;
42
+ declare const setProgress: (value: number) => void;
14
43
  declare const withProgress: <T>(promise: Promise<T>) => Promise<T>;
15
44
 
16
- export { TopProgress, finishProgress, startProgress, withProgress };
45
+ export { TopProgress, type TopProgressProps, type TopProgressRef, completeProgress, continuousStart, decreaseProgress, finishProgress, getProgress, increaseProgress, setProgress, startProgress, staticStart, withProgress };
package/dist/index.js CHANGED
@@ -22,8 +22,15 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
22
22
  var index_exports = {};
23
23
  __export(index_exports, {
24
24
  TopProgress: () => TopProgress,
25
+ completeProgress: () => completeProgress,
26
+ continuousStart: () => continuousStart,
27
+ decreaseProgress: () => decreaseProgress,
25
28
  finishProgress: () => finishProgress,
29
+ getProgress: () => getProgress,
30
+ increaseProgress: () => increaseProgress,
31
+ setProgress: () => setProgress,
26
32
  startProgress: () => startProgress,
33
+ staticStart: () => staticStart,
27
34
  withProgress: () => withProgress
28
35
  });
29
36
  module.exports = __toCommonJS(index_exports);
@@ -37,6 +44,11 @@ var ProgressStore = class {
37
44
  value = null;
38
45
  interval = null;
39
46
  hideTimer = null;
47
+ config = {
48
+ transitionTime: 300,
49
+ loaderSpeed: 500,
50
+ waitingTime: 1e3
51
+ };
40
52
  subscribe(listener) {
41
53
  this.listeners.add(listener);
42
54
  listener(this.value);
@@ -47,27 +59,63 @@ var ProgressStore = class {
47
59
  notify() {
48
60
  this.listeners.forEach((listener) => listener(this.value));
49
61
  }
50
- start() {
51
- if (this.value !== null) return;
62
+ start(loaderType = "continuous") {
63
+ if (loaderType === "static") {
64
+ this.staticStart();
65
+ } else {
66
+ this.continuousStart();
67
+ }
68
+ }
69
+ continuousStart(startingValue, refreshRate) {
52
70
  this.cleanup();
53
- this.value = 10;
71
+ this.value = startingValue !== void 0 ? startingValue : Math.floor(Math.random() * 10) + 20;
54
72
  this.notify();
55
73
  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);
74
+ if (this.value !== null && this.value < 90) {
75
+ const inc = Math.floor(Math.random() * 8) + 2;
76
+ this.value = Math.min(this.value + inc, 90);
59
77
  this.notify();
60
78
  }
61
- }, 200);
79
+ }, refreshRate || this.config.loaderSpeed);
80
+ }
81
+ staticStart(startingValue) {
82
+ this.cleanup();
83
+ this.value = startingValue !== void 0 ? startingValue : Math.floor(Math.random() * 20) + 30;
84
+ this.notify();
85
+ }
86
+ complete() {
87
+ this.finish();
62
88
  }
63
89
  finish() {
64
90
  this.cleanup();
91
+ if (this.value === null) return;
65
92
  this.value = 100;
66
93
  this.notify();
67
94
  this.hideTimer = setTimeout(() => {
68
95
  this.value = null;
69
96
  this.notify();
70
- }, 400);
97
+ }, this.config.waitingTime);
98
+ }
99
+ set(value) {
100
+ this.cleanup();
101
+ this.value = value;
102
+ this.notify();
103
+ if (value >= 100) {
104
+ this.finish();
105
+ }
106
+ }
107
+ increase(amount) {
108
+ if (this.value !== null) {
109
+ this.value = Math.min(this.value + amount, 100);
110
+ this.notify();
111
+ if (this.value === 100) this.finish();
112
+ }
113
+ }
114
+ decrease(amount) {
115
+ if (this.value !== null) {
116
+ this.value = Math.max(0, this.value - amount);
117
+ this.notify();
118
+ }
71
119
  }
72
120
  cleanup() {
73
121
  if (this.interval) clearInterval(this.interval);
@@ -76,11 +124,21 @@ var ProgressStore = class {
76
124
  getValue() {
77
125
  return this.value;
78
126
  }
127
+ getProgress() {
128
+ return this.value;
129
+ }
79
130
  };
80
131
  var progressStore = new ProgressStore();
81
132
  var useProgressStore = () => progressStore;
82
- var startProgress = () => progressStore.start();
133
+ var startProgress = (type) => progressStore.start(type);
134
+ var continuousStart = (startingValue, refreshRate) => progressStore.continuousStart(startingValue, refreshRate);
135
+ var staticStart = (startingValue) => progressStore.staticStart(startingValue);
83
136
  var finishProgress = () => progressStore.finish();
137
+ var completeProgress = () => progressStore.complete();
138
+ var increaseProgress = (value) => progressStore.increase(value);
139
+ var decreaseProgress = (value) => progressStore.decrease(value);
140
+ var getProgress = () => progressStore.getProgress();
141
+ var setProgress = (value) => progressStore.set(value);
84
142
  var withProgress = async (promise) => {
85
143
  startProgress();
86
144
  try {
@@ -95,69 +153,124 @@ var withProgress = async (promise) => {
95
153
 
96
154
  // src/TopProgress.tsx
97
155
  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
- )
156
+ var TopProgress = (0, import_react.forwardRef)(
157
+ ({
158
+ progress: controlledProgress,
159
+ color = "red",
160
+ shadow = true,
161
+ height = 2,
162
+ background = "transparent",
163
+ style,
164
+ containerStyle,
165
+ shadowStyle,
166
+ transitionTime = 300,
167
+ loaderSpeed = 500,
168
+ waitingTime = 1e3,
169
+ className,
170
+ containerClassName,
171
+ onLoaderFinished,
172
+ showGlow,
173
+ glowColor,
174
+ zIndex = 9999
175
+ }, ref) => {
176
+ const store = useProgressStore();
177
+ const [internalProgress, setInternalProgress] = (0, import_react.useState)(
178
+ store.getValue()
179
+ );
180
+ (0, import_react.useImperativeHandle)(ref, () => ({
181
+ start: (type) => store.start(type),
182
+ continuousStart: (s, r) => store.continuousStart(s, r),
183
+ staticStart: (s) => store.staticStart(s),
184
+ complete: () => store.complete(),
185
+ increase: (v) => store.increase(v),
186
+ decrease: (v) => store.decrease(v),
187
+ getProgress: () => store.getProgress(),
188
+ set: (v) => store.set(v)
189
+ }));
190
+ (0, import_react.useEffect)(() => {
191
+ store.config.transitionTime = transitionTime;
192
+ store.config.loaderSpeed = loaderSpeed;
193
+ store.config.waitingTime = waitingTime;
194
+ }, [transitionTime, loaderSpeed, waitingTime, store]);
195
+ (0, import_react.useEffect)(() => {
196
+ return store.subscribe(setInternalProgress);
197
+ }, [store]);
198
+ const progress = controlledProgress !== void 0 ? controlledProgress : internalProgress;
199
+ (0, import_react.useEffect)(() => {
200
+ if (progress === 100 && onLoaderFinished) {
201
+ onLoaderFinished();
202
+ }
203
+ }, [progress, onLoaderFinished]);
204
+ if (progress === null) {
205
+ return null;
154
206
  }
155
- );
156
- };
207
+ const isGradient = color.includes("gradient(");
208
+ const backgroundStyle = isGradient ? { backgroundImage: color } : { backgroundColor: color };
209
+ const useShadow = showGlow !== void 0 ? showGlow : shadow;
210
+ const computedGlowColor = glowColor || (isGradient ? "rgba(41, 216, 255, 0.4)" : color);
211
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
212
+ "div",
213
+ {
214
+ className: containerClassName,
215
+ style: {
216
+ position: "fixed",
217
+ top: 0,
218
+ left: 0,
219
+ width: "100%",
220
+ height: `${height}px`,
221
+ backgroundColor: background,
222
+ zIndex,
223
+ pointerEvents: "none",
224
+ ...containerStyle
225
+ },
226
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
227
+ "div",
228
+ {
229
+ className,
230
+ style: {
231
+ width: `${progress}%`,
232
+ height: "100%",
233
+ ...backgroundStyle,
234
+ transition: `width ${loaderSpeed}ms cubic-bezier(0.4, 0, 0.2, 1), opacity ${transitionTime}ms ease`,
235
+ opacity: progress === 100 ? 0 : 1,
236
+ transform: "translateZ(0)",
237
+ ...style
238
+ },
239
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
240
+ "div",
241
+ {
242
+ style: {
243
+ display: useShadow ? "block" : "none",
244
+ position: "absolute",
245
+ right: 0,
246
+ top: 0,
247
+ width: 100,
248
+ height: "100%",
249
+ boxShadow: `0 0 10px ${computedGlowColor}, 0 0 5px ${computedGlowColor}`,
250
+ opacity: 1,
251
+ transform: "rotate(3deg) translate(0px, -4px)",
252
+ ...shadowStyle
253
+ }
254
+ }
255
+ )
256
+ }
257
+ )
258
+ }
259
+ );
260
+ }
261
+ );
262
+ TopProgress.displayName = "TopProgress";
157
263
  // Annotate the CommonJS export names for ESM import in node:
158
264
  0 && (module.exports = {
159
265
  TopProgress,
266
+ completeProgress,
267
+ continuousStart,
268
+ decreaseProgress,
160
269
  finishProgress,
270
+ getProgress,
271
+ increaseProgress,
272
+ setProgress,
161
273
  startProgress,
274
+ staticStart,
162
275
  withProgress
163
276
  });
package/dist/index.mjs CHANGED
@@ -1,7 +1,12 @@
1
1
  "use client";
2
2
 
3
3
  // src/TopProgress.tsx
4
- import { useEffect, useState } from "react";
4
+ import {
5
+ useEffect,
6
+ useState,
7
+ forwardRef,
8
+ useImperativeHandle
9
+ } from "react";
5
10
 
6
11
  // src/progressStore.ts
7
12
  var ProgressStore = class {
@@ -9,6 +14,11 @@ var ProgressStore = class {
9
14
  value = null;
10
15
  interval = null;
11
16
  hideTimer = null;
17
+ config = {
18
+ transitionTime: 300,
19
+ loaderSpeed: 500,
20
+ waitingTime: 1e3
21
+ };
12
22
  subscribe(listener) {
13
23
  this.listeners.add(listener);
14
24
  listener(this.value);
@@ -19,27 +29,63 @@ var ProgressStore = class {
19
29
  notify() {
20
30
  this.listeners.forEach((listener) => listener(this.value));
21
31
  }
22
- start() {
23
- if (this.value !== null) return;
32
+ start(loaderType = "continuous") {
33
+ if (loaderType === "static") {
34
+ this.staticStart();
35
+ } else {
36
+ this.continuousStart();
37
+ }
38
+ }
39
+ continuousStart(startingValue, refreshRate) {
24
40
  this.cleanup();
25
- this.value = 10;
41
+ this.value = startingValue !== void 0 ? startingValue : Math.floor(Math.random() * 10) + 20;
26
42
  this.notify();
27
43
  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);
44
+ if (this.value !== null && this.value < 90) {
45
+ const inc = Math.floor(Math.random() * 8) + 2;
46
+ this.value = Math.min(this.value + inc, 90);
31
47
  this.notify();
32
48
  }
33
- }, 200);
49
+ }, refreshRate || this.config.loaderSpeed);
50
+ }
51
+ staticStart(startingValue) {
52
+ this.cleanup();
53
+ this.value = startingValue !== void 0 ? startingValue : Math.floor(Math.random() * 20) + 30;
54
+ this.notify();
55
+ }
56
+ complete() {
57
+ this.finish();
34
58
  }
35
59
  finish() {
36
60
  this.cleanup();
61
+ if (this.value === null) return;
37
62
  this.value = 100;
38
63
  this.notify();
39
64
  this.hideTimer = setTimeout(() => {
40
65
  this.value = null;
41
66
  this.notify();
42
- }, 400);
67
+ }, this.config.waitingTime);
68
+ }
69
+ set(value) {
70
+ this.cleanup();
71
+ this.value = value;
72
+ this.notify();
73
+ if (value >= 100) {
74
+ this.finish();
75
+ }
76
+ }
77
+ increase(amount) {
78
+ if (this.value !== null) {
79
+ this.value = Math.min(this.value + amount, 100);
80
+ this.notify();
81
+ if (this.value === 100) this.finish();
82
+ }
83
+ }
84
+ decrease(amount) {
85
+ if (this.value !== null) {
86
+ this.value = Math.max(0, this.value - amount);
87
+ this.notify();
88
+ }
43
89
  }
44
90
  cleanup() {
45
91
  if (this.interval) clearInterval(this.interval);
@@ -48,11 +94,21 @@ var ProgressStore = class {
48
94
  getValue() {
49
95
  return this.value;
50
96
  }
97
+ getProgress() {
98
+ return this.value;
99
+ }
51
100
  };
52
101
  var progressStore = new ProgressStore();
53
102
  var useProgressStore = () => progressStore;
54
- var startProgress = () => progressStore.start();
103
+ var startProgress = (type) => progressStore.start(type);
104
+ var continuousStart = (startingValue, refreshRate) => progressStore.continuousStart(startingValue, refreshRate);
105
+ var staticStart = (startingValue) => progressStore.staticStart(startingValue);
55
106
  var finishProgress = () => progressStore.finish();
107
+ var completeProgress = () => progressStore.complete();
108
+ var increaseProgress = (value) => progressStore.increase(value);
109
+ var decreaseProgress = (value) => progressStore.decrease(value);
110
+ var getProgress = () => progressStore.getProgress();
111
+ var setProgress = (value) => progressStore.set(value);
56
112
  var withProgress = async (promise) => {
57
113
  startProgress();
58
114
  try {
@@ -67,68 +123,123 @@ var withProgress = async (promise) => {
67
123
 
68
124
  // src/TopProgress.tsx
69
125
  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
+ var TopProgress = forwardRef(
127
+ ({
128
+ progress: controlledProgress,
129
+ color = "red",
130
+ shadow = true,
131
+ height = 2,
132
+ background = "transparent",
133
+ style,
134
+ containerStyle,
135
+ shadowStyle,
136
+ transitionTime = 300,
137
+ loaderSpeed = 500,
138
+ waitingTime = 1e3,
139
+ className,
140
+ containerClassName,
141
+ onLoaderFinished,
142
+ showGlow,
143
+ glowColor,
144
+ zIndex = 9999
145
+ }, ref) => {
146
+ const store = useProgressStore();
147
+ const [internalProgress, setInternalProgress] = useState(
148
+ store.getValue()
149
+ );
150
+ useImperativeHandle(ref, () => ({
151
+ start: (type) => store.start(type),
152
+ continuousStart: (s, r) => store.continuousStart(s, r),
153
+ staticStart: (s) => store.staticStart(s),
154
+ complete: () => store.complete(),
155
+ increase: (v) => store.increase(v),
156
+ decrease: (v) => store.decrease(v),
157
+ getProgress: () => store.getProgress(),
158
+ set: (v) => store.set(v)
159
+ }));
160
+ useEffect(() => {
161
+ store.config.transitionTime = transitionTime;
162
+ store.config.loaderSpeed = loaderSpeed;
163
+ store.config.waitingTime = waitingTime;
164
+ }, [transitionTime, loaderSpeed, waitingTime, store]);
165
+ useEffect(() => {
166
+ return store.subscribe(setInternalProgress);
167
+ }, [store]);
168
+ const progress = controlledProgress !== void 0 ? controlledProgress : internalProgress;
169
+ useEffect(() => {
170
+ if (progress === 100 && onLoaderFinished) {
171
+ onLoaderFinished();
172
+ }
173
+ }, [progress, onLoaderFinished]);
174
+ if (progress === null) {
175
+ return null;
126
176
  }
127
- );
128
- };
177
+ const isGradient = color.includes("gradient(");
178
+ const backgroundStyle = isGradient ? { backgroundImage: color } : { backgroundColor: color };
179
+ const useShadow = showGlow !== void 0 ? showGlow : shadow;
180
+ const computedGlowColor = glowColor || (isGradient ? "rgba(41, 216, 255, 0.4)" : color);
181
+ return /* @__PURE__ */ jsx(
182
+ "div",
183
+ {
184
+ className: containerClassName,
185
+ style: {
186
+ position: "fixed",
187
+ top: 0,
188
+ left: 0,
189
+ width: "100%",
190
+ height: `${height}px`,
191
+ backgroundColor: background,
192
+ zIndex,
193
+ pointerEvents: "none",
194
+ ...containerStyle
195
+ },
196
+ children: /* @__PURE__ */ jsx(
197
+ "div",
198
+ {
199
+ className,
200
+ style: {
201
+ width: `${progress}%`,
202
+ height: "100%",
203
+ ...backgroundStyle,
204
+ transition: `width ${loaderSpeed}ms cubic-bezier(0.4, 0, 0.2, 1), opacity ${transitionTime}ms ease`,
205
+ opacity: progress === 100 ? 0 : 1,
206
+ transform: "translateZ(0)",
207
+ ...style
208
+ },
209
+ children: /* @__PURE__ */ jsx(
210
+ "div",
211
+ {
212
+ style: {
213
+ display: useShadow ? "block" : "none",
214
+ position: "absolute",
215
+ right: 0,
216
+ top: 0,
217
+ width: 100,
218
+ height: "100%",
219
+ boxShadow: `0 0 10px ${computedGlowColor}, 0 0 5px ${computedGlowColor}`,
220
+ opacity: 1,
221
+ transform: "rotate(3deg) translate(0px, -4px)",
222
+ ...shadowStyle
223
+ }
224
+ }
225
+ )
226
+ }
227
+ )
228
+ }
229
+ );
230
+ }
231
+ );
232
+ TopProgress.displayName = "TopProgress";
129
233
  export {
130
234
  TopProgress,
235
+ completeProgress,
236
+ continuousStart,
237
+ decreaseProgress,
131
238
  finishProgress,
239
+ getProgress,
240
+ increaseProgress,
241
+ setProgress,
132
242
  startProgress,
243
+ staticStart,
133
244
  withProgress
134
245
  };
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "react-top-progress",
3
- "version": "0.1.1",
3
+ "version": "1.0.1",
4
4
  "description": "A lightweight modern top loading progress bar for React",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
7
7
  "types": "dist/index.d.ts",
8
8
  "files": [
9
9
  "dist",
10
- "demo_react_top_progress.png"
10
+ "demo_react_top_progress.webp"
11
11
  ],
12
12
  "keywords": [
13
13
  "react",
Binary file