├── .eslintrc
├── .travis.yml
├── demo.gif
├── example
├── src
│ ├── index.css
│ ├── index.js
│ └── App.js
├── public
│ ├── manifest.json
│ └── index.html
└── package.json
├── .babelrc
├── .editorconfig
├── .gitignore
├── rollup.config.js
├── package.json
├── src
└── index.js
└── README.md
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "react-app"
3 | }
4 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 9
4 | - 8
5 |
--------------------------------------------------------------------------------
/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JovianHQ/react-top-loader/HEAD/demo.gif
--------------------------------------------------------------------------------
/example/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: sans-serif;
5 | }
6 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["env", {
4 | "modules": false
5 | }],
6 | "stage-0",
7 | "react"
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/example/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 |
4 | import './index.css'
5 | import App from './App'
6 |
7 | ReactDOM.render( , document.getElementById('root'))
8 |
--------------------------------------------------------------------------------
/example/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "react-top-loader",
3 | "name": "react-top-loader",
4 | "start_url": "./index.html",
5 | "display": "standalone",
6 | "theme_color": "#000000",
7 | "background_color": "#ffffff"
8 | }
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # See https://help.github.com/ignore-files/ for more about ignoring files.
3 |
4 | # dependencies
5 | node_modules
6 |
7 | # builds
8 | build
9 | dist
10 | .rpt2_cache
11 |
12 | # misc
13 | .DS_Store
14 | .env
15 | .env.local
16 | .env.development.local
17 | .env.test.local
18 | .env.production.local
19 |
20 | npm-debug.log*
21 | yarn-debug.log*
22 | yarn-error.log*
23 |
--------------------------------------------------------------------------------
/example/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | react-top-loader
11 |
12 |
13 |
14 |
15 | You need to enable JavaScript to run this app.
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-top-loader-example",
3 | "homepage": "https://aakashns.github.io/react-top-loader",
4 | "version": "0.0.0",
5 | "license": "MIT",
6 | "private": true,
7 | "dependencies": {
8 | "prop-types": "^15.6.2",
9 | "react": "^16.4.1",
10 | "react-dom": "^16.4.1",
11 | "react-scripts": "^1.1.4",
12 | "react-top-loader": "file:.."
13 | },
14 | "scripts": {
15 | "start": "react-scripts start",
16 | "build": "react-scripts build",
17 | "test": "react-scripts test --env=jsdom",
18 | "eject": "react-scripts eject"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import babel from 'rollup-plugin-babel'
2 | import commonjs from 'rollup-plugin-commonjs'
3 | import external from 'rollup-plugin-peer-deps-external'
4 | import postcss from 'rollup-plugin-postcss'
5 | import resolve from 'rollup-plugin-node-resolve'
6 | import url from 'rollup-plugin-url'
7 | import svgr from '@svgr/rollup'
8 |
9 | import pkg from './package.json'
10 |
11 | export default {
12 | input: 'src/index.js',
13 | output: [
14 | {
15 | file: pkg.main,
16 | format: 'cjs',
17 | sourcemap: true
18 | },
19 | {
20 | file: pkg.module,
21 | format: 'es',
22 | sourcemap: true
23 | }
24 | ],
25 | plugins: [
26 | external(),
27 | postcss({
28 | modules: true
29 | }),
30 | url(),
31 | svgr(),
32 | babel({
33 | exclude: 'node_modules/**',
34 | plugins: [ 'external-helpers' ]
35 | }),
36 | resolve(),
37 | commonjs()
38 | ]
39 | }
40 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-top-loader",
3 | "version": "1.0.0",
4 | "description": "Simple fixed-to-top progress bar / loader component for React (similar to sites like GitHub, Medium etc.)",
5 | "author": "aakashns",
6 | "license": "MIT",
7 | "repository": "aakashns/react-top-loader",
8 | "main": "dist/index.js",
9 | "module": "dist/index.es.js",
10 | "jsnext:main": "dist/index.es.js",
11 | "engines": {
12 | "node": ">=8",
13 | "npm": ">=5"
14 | },
15 | "scripts": {
16 | "test": "cross-env CI=1 react-scripts test --env=jsdom",
17 | "test:watch": "react-scripts test --env=jsdom",
18 | "build": "rollup -c",
19 | "start": "rollup -c -w",
20 | "prepare": "npm run build",
21 | "predeploy": "cd example && npm install && npm run build"
22 | },
23 | "peerDependencies": {
24 | "prop-types": "^15.5.4",
25 | "react": "^15.0.0 || ^16.0.0",
26 | "react-dom": "^15.0.0 || ^16.0.0"
27 | },
28 | "devDependencies": {
29 | "@svgr/rollup": "^2.4.1",
30 | "@typescript-eslint/eslint-plugin": "^2.20.0",
31 | "@typescript-eslint/parser": "^2.20.0",
32 | "babel-core": "^6.26.3",
33 | "babel-eslint": "^10.0.3",
34 | "babel-plugin-external-helpers": "^6.22.0",
35 | "babel-preset-env": "^1.7.0",
36 | "babel-preset-react": "^6.24.1",
37 | "babel-preset-stage-0": "^6.24.1",
38 | "cross-env": "^5.1.4",
39 | "eslint": "^6.8.0",
40 | "eslint-config-react-app": "^5.2.0",
41 | "eslint-config-standard": "^11.0.0",
42 | "eslint-config-standard-react": "^6.0.0",
43 | "eslint-plugin-flowtype": "^4.6.0",
44 | "eslint-plugin-import": "^2.20.1",
45 | "eslint-plugin-jsx-a11y": "^6.2.3",
46 | "eslint-plugin-node": "^7.0.1",
47 | "eslint-plugin-promise": "^4.0.0",
48 | "eslint-plugin-react": "^7.18.3",
49 | "eslint-plugin-react-hooks": "^2.4.0",
50 | "eslint-plugin-standard": "^3.1.0",
51 | "gh-pages": "^1.2.0",
52 | "react": "^16.4.1",
53 | "react-dom": "^16.4.1",
54 | "react-scripts": "^1.1.4",
55 | "rollup": "^0.64.1",
56 | "rollup-plugin-babel": "^3.0.7",
57 | "rollup-plugin-commonjs": "^9.1.3",
58 | "rollup-plugin-node-resolve": "^3.3.0",
59 | "rollup-plugin-peer-deps-external": "^2.2.0",
60 | "rollup-plugin-postcss": "^1.6.2",
61 | "rollup-plugin-url": "^1.4.0"
62 | },
63 | "files": [
64 | "dist"
65 | ]
66 | }
67 |
--------------------------------------------------------------------------------
/example/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import TopLoader from "react-top-loader";
3 |
4 | class Animator extends React.Component {
5 | state = {
6 | progress: 0
7 | };
8 |
9 | componentDidMount() {
10 | setInterval(
11 | () => this.setState({ progress: Math.min(1, this.state.progress + 0.1) }),
12 | this.props.loopDuration || 400
13 | );
14 | }
15 |
16 | render() {
17 | return ;
18 | }
19 | }
20 |
21 | export default class App extends Component {
22 | render() {
23 | return (
24 |
25 |
26 | react-top-loader
27 |
28 |
29 |
37 | Progress Indicator
38 |
39 |
40 |
41 |
42 |
50 | Animated
51 |
52 |
58 |
59 |
60 |
68 | Animated (no background)
69 |
70 |
71 |
72 |
73 |
81 | Indeterminate
82 |
83 |
89 |
90 |
91 |
99 | Indeterminate (no background)
100 |
101 |
102 |
103 |
104 | );
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | class DeterminateLoader extends React.Component {
4 | render() {
5 | const {
6 | fixed,
7 | backgroundColor,
8 | thickness,
9 | style,
10 | duration,
11 | progress,
12 | progressDuration,
13 | color,
14 | zIndex,
15 | ...rest
16 | } = this.props;
17 | return (
18 |
45 | );
46 | }
47 | }
48 |
49 | class IndeterminateLoader extends React.Component {
50 | state = {
51 | visible: false,
52 | loading: false
53 | };
54 |
55 | isIndeterminate = () =>
56 | this.props.progress === undefined || this.props.progerss === null;
57 |
58 | loop = () => {
59 | this.setState({ loading: true }, () =>
60 | setTimeout(() => this.setState({ loading: false }), this.props.duration)
61 | );
62 | };
63 |
64 | setupLoop = () =>
65 | setTimeout(
66 | () =>
67 | this.setState({ visible: true }, () => {
68 | this.loop();
69 | this.interval = setInterval(this.loop, this.props.duration + 100);
70 | }),
71 | this.props.delay
72 | );
73 |
74 | componentDidMount() {
75 | this.setupLoop();
76 | }
77 |
78 | componentWillUnmount() {
79 | clearInterval(this.interval);
80 | }
81 |
82 | render() {
83 | const {
84 | progress,
85 | color,
86 | backgroundColor,
87 | thickness,
88 | duration,
89 | progressDuration,
90 | fixed,
91 | style,
92 | zIndex,
93 | ...rest
94 | } = this.props;
95 | const { loading, visible } = this.state;
96 | return !visible ? null : (
97 |
136 | );
137 | }
138 | }
139 |
140 | class TopLoader extends React.Component {
141 | render() {
142 | const { show, ...rest } = this.props;
143 | if (!show) {
144 | return null;
145 | }
146 | if (rest.progress === undefined || rest.progress === null) {
147 | return ;
148 | } else {
149 | return ;
150 | }
151 | }
152 | }
153 |
154 | TopLoader.defaultProps = {
155 | show: false,
156 | duration: 1500,
157 | progressDuration: 100,
158 | thickness: 2,
159 | color: "#03a9f4",
160 | backgroundColor: "transparent",
161 | fixed: true,
162 | delay: 0,
163 | zIndex: 10000
164 | };
165 |
166 | export default TopLoader;
167 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-top-loader
2 |
3 | Simple fixed-to-top progress bar / loader component for React (similar to sites like GitHub, Medium etc.)
4 |
5 | [](https://www.npmjs.com/package/react-top-loader)
6 |
7 | 
8 |
9 | ## Installation
10 |
11 | ```
12 | npm install react-top-loader
13 | ```
14 |
15 | or
16 |
17 | ```
18 | yarn add react-top-loader
19 | ```
20 |
21 | ## Usage
22 |
23 | - Just import `TopLoader` and set the `show` prop to `true`. This will show the indeterminate
24 | loader with looping animation at the top of the page.
25 | - Use the `progress` prop to control the animation manually.
26 | - The loading bar is fixed to top by default. Set `fixed` to `false` to disable it.
27 |
28 | Here are some more examples (from the GIF above):
29 |
30 | ```javascript
31 | import TopLoader from "react-top-loader";
32 |
33 | const Examples = () => (
34 |
35 | {/* Fixed to Top with looping animation */}
36 |
37 |
38 | {/* Progress Indicator */}
39 |
40 |
41 |
42 |
43 | {/* Animated */}
44 |
52 |
53 | {/* Indeterminate */}
54 |
55 |
56 |
57 |
58 | {/* Indeterminate (no background) */}
59 |
60 |
Indeterminate (no background)
61 |
62 |
63 |
64 | );
65 |
66 | // Helper class to animate the loader
67 | class Animator extends React.Component {
68 | state = { progress: 0 };
69 |
70 | componentDidMount() {
71 | setInterval(
72 | () => this.setState({ progress: Math.min(1, this.state.progress + 0.1) }),
73 | 400
74 | );
75 | }
76 |
77 | render() {
78 | return ;
79 | }
80 | }
81 | ```
82 |
83 | ## Props
84 |
85 | | Prop | Type | Default | Required | Description |
86 | | ------------------ | ------------------------------------ | --------------- | -------- | ----------------------------------------------------------------------------------------------------------------- |
87 | | `show` | `boolean` | `false` | yes | Set this to `true` to show the loader |
88 | | `progress` | `null (or) number` (between 0 and 1) | | no | If `undefined` or `null`, indeterminate animated loader is shown. If specified, a fraction of the strip is filled |
89 | | `fixed` | `boolean` | `true` | no | If `true`, loader is shown at the top of the page (`position:fixed`). Otherwise you have to position it yourself |
90 | | `thickness` | `number` | `2` | no | Thickness (height) of the loading strip in pixels (`px`) |
91 | | `color` | `string` | `"#03a9f4"` | no | Color of the loading strip |
92 | | `backgroundColor` | `string` | `"transparent"` | no | Color of the empty region behind the loading strip (transparent by default) |
93 | | `delay` | `number` (milliseconds) | `0` | no | Show the loader after a specified delay (use this to prevent flashing of loader of very short tasks/requests) |
94 | | `duration` | `number` (milliseconds) | `1500` | no | Duration of the animation of the indeterminate loader (not applicable if `progress` is provided) |
95 | | `progressDuration` | `number` (milliseconds) | `400` | no | If you're changing the value of `progress` to animate the loader, then use this to control the speed of animation |
96 | | `zIndex` | `number` | `10000` | no | Z-Index of the top-level loader `div` |
97 | | `className` | `string` | | no | Specify a custom class for the top-level `div` |
98 | | `style` | `object` | | no | Override styles for the top-level `div` |
99 |
100 | ## Credits
101 |
102 | Developed by the [Jovian.ml](https://www.jovian.ml) team. Released under the MIT Licence. Inspired by [this Codepen example](https://codepen.io/bootpen/pen/WQQLQZ) by Shahen Algoo.
103 |
--------------------------------------------------------------------------------