├── .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 | 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 |
33 |
44 |
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 |
112 |
120 |
134 |
135 |
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 | [![NPM](https://img.shields.io/npm/v/react-top-loader.svg)](https://www.npmjs.com/package/react-top-loader) 6 | 7 | ![demo](./demo.gif) 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 |
45 | 51 |
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 | --------------------------------------------------------------------------------