Merge pull request #77 from kevinsqi/typescript
Convert project to Typescript and improve demo setup
19
.eslintrc
@ -1,19 +0,0 @@
|
||||
{
|
||||
"extends": "airbnb",
|
||||
"rules": {
|
||||
"arrow-parens": ["error", "always"],
|
||||
"comma-dangle": [
|
||||
"error",
|
||||
{
|
||||
"arrays": "always-multiline",
|
||||
"objects": "always-multiline"
|
||||
}
|
||||
],
|
||||
"max-len": 0,
|
||||
"react/jsx-filename-extension": 0
|
||||
},
|
||||
"env": {
|
||||
"browser": true,
|
||||
"mocha": true
|
||||
}
|
||||
}
|
||||
17
.gitignore
vendored
@ -1,6 +1,15 @@
|
||||
node_modules
|
||||
npm-debug.log
|
||||
# dependencies
|
||||
/node_modules
|
||||
|
||||
# distribution folder
|
||||
/dist
|
||||
|
||||
# coverage
|
||||
/coverage
|
||||
|
||||
.DS_Store
|
||||
.cache
|
||||
*.iml
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.idea
|
||||
|
||||
@ -1,8 +1,4 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "node"
|
||||
- "7"
|
||||
- "8"
|
||||
script:
|
||||
- npm run lint
|
||||
- npm test
|
||||
- node
|
||||
- lts/*
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
# Contributing
|
||||
## Contributing
|
||||
|
||||
To run demo locally on localhost:8080:
|
||||
Thanks for your interest in helping out! If you'd like to make a change to react-circular-progressbar master, it's preferable to first validate your change by writing what you plan to do in a new [Github issue](https://github.com/kevinsqi/react-circular-progressbar/issues).
|
||||
|
||||
```bash
|
||||
yarn install
|
||||
yarn start
|
||||
```
|
||||
To make your own changes:
|
||||
|
||||
Keep tests passing by running `yarn test` and `yarn run lint`.
|
||||
1. Fork the repo.
|
||||
2. Go to `/demo` and view the README to see how to run the demo.
|
||||
3. Make changes to `/src` and they'll be applied immediately to the running demo. You can use this to prototype, but you probably don't need to commit the changes to `/demo` when creating a PR.
|
||||
4. Add tests for your new changes and make sure they're passing with `yarn test`.
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
|
||||
A circular progressbar component, built with SVG and extensively customizable. [**Try it out on CodeSandbox**](https://codesandbox.io/s/vymm4oln6y).
|
||||
|
||||
<a href="https://codesandbox.io/s/vymm4oln6y"><img height="120" src="/docs/animated-progressbar.gif?raw=true" alt="animated progressbar" /></a> <a href="https://codesandbox.io/s/vymm4oln6y"><img height="120" src="/docs/circular-progressbar-examples.png?raw=true" alt="progressbar examples" /></a>
|
||||
<a href="https://codesandbox.io/s/vymm4oln6y"><img height="120" src="/demo/public/images/animated-progressbar.gif?raw=true" alt="animated progressbar" /></a> <a href="https://codesandbox.io/s/vymm4oln6y"><img height="120" src="/demo/public/images/circular-progressbar-examples.png?raw=true" alt="progressbar examples" /></a>
|
||||
|
||||
## Installation
|
||||
|
||||
@ -139,7 +139,7 @@ If you want to add images or multiple lines of text within the progressbar, the
|
||||
|
||||
[**Here's a Codesandbox demo**](https://codesandbox.io/s/qlr7w0rm29)
|
||||
|
||||
<a href="https://codesandbox.io/s/qlr7w0rm29"><img src="/docs/custom-content-progressbar.png?raw=true" alt="custom content progressbar" /></a>
|
||||
<a href="https://codesandbox.io/s/qlr7w0rm29"><img src="/demo/public/images/custom-content-progressbar.png?raw=true" alt="custom content progressbar" /></a>
|
||||
|
||||
## Customizing animation and animating text
|
||||
|
||||
|
||||
5
demo/.env
Normal file
@ -0,0 +1,5 @@
|
||||
# This is used to skip the error where
|
||||
# e.g. babel-jest is a sub-dependency in the parent dir
|
||||
# but is also used by react-scripts:
|
||||
# "There might be a problem with the project dependency tree"
|
||||
SKIP_PREFLIGHT_CHECK=true
|
||||
23
demo/.gitignore
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
27
demo/README.md
Normal file
@ -0,0 +1,27 @@
|
||||
# react-circular-progressbar demo site
|
||||
|
||||
## Developing
|
||||
|
||||
In the parent react-circular-progressbar directory, run:
|
||||
|
||||
```
|
||||
yarn link
|
||||
yarn start
|
||||
```
|
||||
|
||||
In this repo, run:
|
||||
|
||||
```
|
||||
yarn link react-circular-progressbar
|
||||
yarn start
|
||||
```
|
||||
|
||||
The demo site will be running at [localhost:3000](http://localhost:3000). Now, any changes that are made to react-circular-progressbar source will be live-reloaded on the demo site.
|
||||
|
||||
## Deploying
|
||||
|
||||
This site is hosted on github pages at https://www.kevinqi.com/react-circular-progressbar. Deploy new updates by running in this directory:
|
||||
|
||||
```
|
||||
yarn run deploy
|
||||
```
|
||||
35
demo/package.json
Normal file
@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "demo",
|
||||
"homepage": "https://www.kevinqi.com/react-circular-progressbar",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@types/jest": "24.0.11",
|
||||
"@types/node": "11.13.6",
|
||||
"@types/react": "16.8.14",
|
||||
"@types/react-dom": "16.8.4",
|
||||
"gh-pages": "^2.0.1",
|
||||
"react": "^16.8.6",
|
||||
"react-circular-progressbar": "file:./../",
|
||||
"react-dom": "^16.8.6",
|
||||
"react-scripts": "2.1.8",
|
||||
"typescript": "3.4.4"
|
||||
},
|
||||
"scripts": {
|
||||
"predeploy": "npm run build",
|
||||
"deploy": "gh-pages -d build",
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app"
|
||||
},
|
||||
"browserslist": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not ie <= 11",
|
||||
"not op_mini all"
|
||||
]
|
||||
}
|
||||
BIN
demo/public/favicon.ico
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
43
demo/public/index.html
Normal file
@ -0,0 +1,43 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>react-circular-progressbar: a circular progress indicator component</title>
|
||||
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
|
||||
/>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
15
demo/public/manifest.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
25
demo/src/App.css
Normal file
@ -0,0 +1,25 @@
|
||||
.bg-yellow {
|
||||
background-color: #f8e8d5;
|
||||
}
|
||||
|
||||
/* bootstrap overrides */
|
||||
a {
|
||||
color: #3e98c7;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* demo style overrides */
|
||||
.CircularProgressbar.incomplete .CircularProgressbar-path {
|
||||
stroke: #f66;
|
||||
}
|
||||
.CircularProgressbar.incomplete .CircularProgressbar-text {
|
||||
fill: #f66;
|
||||
}
|
||||
.CircularProgressbar.complete .CircularProgressbar-path {
|
||||
stroke: #99f;
|
||||
}
|
||||
.CircularProgressbar.complete .CircularProgressbar-text {
|
||||
fill: #99f;
|
||||
}
|
||||
9
demo/src/App.test.tsx
Normal file
@ -0,0 +1,9 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './App';
|
||||
|
||||
it('renders without crashing', () => {
|
||||
const div = document.createElement('div');
|
||||
ReactDOM.render(<App />, div);
|
||||
ReactDOM.unmountComponentAtNode(div);
|
||||
});
|
||||
19
demo/src/App.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
import React, { Component } from 'react';
|
||||
import CircularProgressbar from 'react-circular-progressbar';
|
||||
import Demo from './Demo';
|
||||
|
||||
// Stylesheets
|
||||
import 'react-circular-progressbar/dist/styles.css';
|
||||
import './App.css';
|
||||
|
||||
class App extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Demo />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default App;
|
||||
71
demo/src/ChangingProgressbar.tsx
Normal file
@ -0,0 +1,71 @@
|
||||
import React from 'react';
|
||||
import CircularProgressbar from 'react-circular-progressbar';
|
||||
|
||||
type CircularProgressbarProps = {
|
||||
counterClockwise?: boolean;
|
||||
strokeWidth?: number;
|
||||
};
|
||||
|
||||
type Props = CircularProgressbarProps &
|
||||
typeof ChangingProgressbar.defaultProps & {
|
||||
percentages: number[];
|
||||
classForPercentage?: (percentage: number) => string;
|
||||
stylesForPercentage?: (percentage: number) => {};
|
||||
textForPercentage?: (percentage: number) => string;
|
||||
};
|
||||
|
||||
type State = {
|
||||
currentPercentageIndex: number;
|
||||
};
|
||||
|
||||
class ChangingProgressbar extends React.Component<Props, State> {
|
||||
static defaultProps = {
|
||||
interval: 1000,
|
||||
classForPercentage: (percentage: number) => '',
|
||||
stylesForPercentage: (percentage: number) => ({}),
|
||||
textForPercentage: (percentage: number) => `${percentage}%`,
|
||||
};
|
||||
|
||||
state = {
|
||||
currentPercentageIndex: 0,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
setInterval(() => {
|
||||
this.setState({
|
||||
currentPercentageIndex:
|
||||
(this.state.currentPercentageIndex + 1) % this.props.percentages.length,
|
||||
});
|
||||
}, this.props.interval);
|
||||
}
|
||||
|
||||
getCurrentPercentage() {
|
||||
return this.props.percentages[this.state.currentPercentageIndex];
|
||||
}
|
||||
|
||||
getClassName() {
|
||||
return this.props.classForPercentage(this.getCurrentPercentage());
|
||||
}
|
||||
|
||||
getStyles() {
|
||||
return this.props.stylesForPercentage(this.getCurrentPercentage());
|
||||
}
|
||||
|
||||
getText() {
|
||||
return this.props.textForPercentage(this.getCurrentPercentage());
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<CircularProgressbar
|
||||
{...this.props}
|
||||
className={this.getClassName()}
|
||||
percentage={this.getCurrentPercentage()}
|
||||
text={this.getText()}
|
||||
styles={this.getStyles()}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ChangingProgressbar;
|
||||
123
demo/src/Demo.tsx
Normal file
@ -0,0 +1,123 @@
|
||||
import React from 'react';
|
||||
import CircularProgressbar from 'react-circular-progressbar';
|
||||
import ChangingProgressbar from './ChangingProgressbar';
|
||||
|
||||
const githubURL = 'https://github.com/kevinsqi/react-circular-progressbar';
|
||||
|
||||
const Example: React.FunctionComponent<{ description: string }> = ({ description, children }) => (
|
||||
<div className="col-12 col-sm-6 col-md-3">
|
||||
<div className="row mb-1">
|
||||
<div className="col-6 offset-3">{children}</div>
|
||||
</div>
|
||||
<p className="text-center">{description}</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
function Demo() {
|
||||
return (
|
||||
<div className="container">
|
||||
<div className="row mt-5">
|
||||
<div className="col-12">
|
||||
<div className="text-center">
|
||||
<h1 className="mb-3">react-circular-progressbar</h1>
|
||||
<p>A circular progress indicator component</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="row mt-5 mb-5">
|
||||
<div className="col-6 offset-3 col-md-2 offset-md-5">
|
||||
<ChangingProgressbar
|
||||
percentages={[0, 20, 40, 60, 80, 100]}
|
||||
stylesForPercentage={(percentage: number) => {
|
||||
const alpha = (100 + percentage) / 200;
|
||||
return {
|
||||
path: {
|
||||
stroke: `rgba(62, 152, 199, ${alpha})`,
|
||||
},
|
||||
};
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
<div className="row mt-5">
|
||||
<div className="col-12 mb-5">
|
||||
<h2 className="text-center">Built with SVG and styled with plain CSS.</h2>
|
||||
</div>
|
||||
|
||||
<Example description="Customize text and styling dynamically based on percentage.">
|
||||
<ChangingProgressbar
|
||||
percentages={[75, 100]}
|
||||
classForPercentage={(percentage: number) => {
|
||||
return percentage === 100 ? 'complete' : 'incomplete';
|
||||
}}
|
||||
textForPercentage={(percentage: number) => {
|
||||
return percentage === 100 ? `${percentage}!!` : `${percentage}`;
|
||||
}}
|
||||
/>
|
||||
</Example>
|
||||
|
||||
<Example description="Customize stroke width and make it go counterclockwise.">
|
||||
<ChangingProgressbar percentages={[0, 20, 80]} strokeWidth={5} counterClockwise />
|
||||
</Example>
|
||||
|
||||
<Example description="Add a background color for that inverted look.">
|
||||
<CircularProgressbar
|
||||
className="CircularProgressbar-inverted"
|
||||
background
|
||||
backgroundPadding={5}
|
||||
strokeWidth={6}
|
||||
percentage={66}
|
||||
text={`${66}%`}
|
||||
classes={{
|
||||
root: 'CircularProgressbar',
|
||||
trail: 'CircularProgressbar-trail',
|
||||
path: 'CircularProgressbar-path',
|
||||
text: 'CircularProgressbar-text some-additional-test-class',
|
||||
background: 'CircularProgressbar-background',
|
||||
}}
|
||||
styles={{
|
||||
background: {
|
||||
fill: '#3e98c7',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Example>
|
||||
|
||||
<Example description="With SVG and CSS you can do whatever you want.">
|
||||
<div style={{ position: 'relative', width: '100%', height: '100%' }}>
|
||||
<div style={{ position: 'absolute', width: '100%' }}>
|
||||
<CircularProgressbar percentage={50} />
|
||||
</div>
|
||||
<div style={{ width: '100%', padding: '10%' }}>
|
||||
<img style={{ width: '100%' }} src="https://i.imgur.com/b9NyUGm.png" alt="doge" />
|
||||
</div>
|
||||
</div>
|
||||
</Example>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
<div className="mt-5 mb-5">
|
||||
<h2 className="text-center">Installation</h2>
|
||||
<div className="text-center mt-5">
|
||||
<p>Install with yarn or npm:</p>
|
||||
<p className="mb-5">
|
||||
<code className="p-2 text-dark bg-yellow">yarn add react-circular-progressbar</code>
|
||||
</p>
|
||||
<a className="btn btn-info btn-lg" href={githubURL}>
|
||||
View docs on Github
|
||||
</a>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="mt-5">
|
||||
Built by <a href="https://www.kevinqi.com/">@kevinsqi</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Demo;
|
||||
14
demo/src/index.css
Normal file
@ -0,0 +1,14 @@
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
}
|
||||
12
demo/src/index.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
import * as serviceWorker from './serviceWorker';
|
||||
|
||||
ReactDOM.render(<App />, document.getElementById('root'));
|
||||
|
||||
// If you want your app to work offline and load faster, you can change
|
||||
// unregister() to register() below. Note this comes with some pitfalls.
|
||||
// Learn more about service workers: https://bit.ly/CRA-PWA
|
||||
serviceWorker.unregister();
|
||||
1
demo/src/react-app-env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
/// <reference types="react-scripts" />
|
||||
143
demo/src/serviceWorker.ts
Normal file
@ -0,0 +1,143 @@
|
||||
// This optional code is used to register a service worker.
|
||||
// register() is not called by default.
|
||||
|
||||
// This lets the app load faster on subsequent visits in production, and gives
|
||||
// it offline capabilities. However, it also means that developers (and users)
|
||||
// will only see deployed updates on subsequent visits to a page, after all the
|
||||
// existing tabs open on the page have been closed, since previously cached
|
||||
// resources are updated in the background.
|
||||
|
||||
// To learn more about the benefits of this model and instructions on how to
|
||||
// opt-in, read https://bit.ly/CRA-PWA
|
||||
|
||||
const isLocalhost = Boolean(
|
||||
window.location.hostname === 'localhost' ||
|
||||
// [::1] is the IPv6 localhost address.
|
||||
window.location.hostname === '[::1]' ||
|
||||
// 127.0.0.1/8 is considered localhost for IPv4.
|
||||
window.location.hostname.match(
|
||||
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
|
||||
)
|
||||
);
|
||||
|
||||
type Config = {
|
||||
onSuccess?: (registration: ServiceWorkerRegistration) => void;
|
||||
onUpdate?: (registration: ServiceWorkerRegistration) => void;
|
||||
};
|
||||
|
||||
export function register(config?: Config) {
|
||||
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
|
||||
// The URL constructor is available in all browsers that support SW.
|
||||
const publicUrl = new URL(
|
||||
(process as { env: { [key: string]: string } }).env.PUBLIC_URL,
|
||||
window.location.href
|
||||
);
|
||||
if (publicUrl.origin !== window.location.origin) {
|
||||
// Our service worker won't work if PUBLIC_URL is on a different origin
|
||||
// from what our page is served on. This might happen if a CDN is used to
|
||||
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
|
||||
return;
|
||||
}
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
|
||||
|
||||
if (isLocalhost) {
|
||||
// This is running on localhost. Let's check if a service worker still exists or not.
|
||||
checkValidServiceWorker(swUrl, config);
|
||||
|
||||
// Add some additional logging to localhost, pointing developers to the
|
||||
// service worker/PWA documentation.
|
||||
navigator.serviceWorker.ready.then(() => {
|
||||
console.log(
|
||||
'This web app is being served cache-first by a service ' +
|
||||
'worker. To learn more, visit https://bit.ly/CRA-PWA'
|
||||
);
|
||||
});
|
||||
} else {
|
||||
// Is not localhost. Just register service worker
|
||||
registerValidSW(swUrl, config);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function registerValidSW(swUrl: string, config?: Config) {
|
||||
navigator.serviceWorker
|
||||
.register(swUrl)
|
||||
.then(registration => {
|
||||
registration.onupdatefound = () => {
|
||||
const installingWorker = registration.installing;
|
||||
if (installingWorker == null) {
|
||||
return;
|
||||
}
|
||||
installingWorker.onstatechange = () => {
|
||||
if (installingWorker.state === 'installed') {
|
||||
if (navigator.serviceWorker.controller) {
|
||||
// At this point, the updated precached content has been fetched,
|
||||
// but the previous service worker will still serve the older
|
||||
// content until all client tabs are closed.
|
||||
console.log(
|
||||
'New content is available and will be used when all ' +
|
||||
'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
|
||||
);
|
||||
|
||||
// Execute callback
|
||||
if (config && config.onUpdate) {
|
||||
config.onUpdate(registration);
|
||||
}
|
||||
} else {
|
||||
// At this point, everything has been precached.
|
||||
// It's the perfect time to display a
|
||||
// "Content is cached for offline use." message.
|
||||
console.log('Content is cached for offline use.');
|
||||
|
||||
// Execute callback
|
||||
if (config && config.onSuccess) {
|
||||
config.onSuccess(registration);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error during service worker registration:', error);
|
||||
});
|
||||
}
|
||||
|
||||
function checkValidServiceWorker(swUrl: string, config?: Config) {
|
||||
// Check if the service worker can be found. If it can't reload the page.
|
||||
fetch(swUrl)
|
||||
.then(response => {
|
||||
// Ensure service worker exists, and that we really are getting a JS file.
|
||||
const contentType = response.headers.get('content-type');
|
||||
if (
|
||||
response.status === 404 ||
|
||||
(contentType != null && contentType.indexOf('javascript') === -1)
|
||||
) {
|
||||
// No service worker found. Probably a different app. Reload the page.
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
registration.unregister().then(() => {
|
||||
window.location.reload();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// Service worker found. Proceed as normal.
|
||||
registerValidSW(swUrl, config);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
console.log(
|
||||
'No internet connection found. App is running in offline mode.'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export function unregister() {
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
registration.unregister();
|
||||
});
|
||||
}
|
||||
}
|
||||
25
demo/tsconfig.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "preserve"
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
}
|
||||
10720
demo/yarn.lock
Executable file
1
dist/index.js
vendored
61
dist/styles.css
vendored
@ -1,61 +0,0 @@
|
||||
/*
|
||||
* react-circular-progressbar styles
|
||||
*
|
||||
* All of the styles in this file are optional and configurable!
|
||||
*/
|
||||
|
||||
.CircularProgressbar {
|
||||
/*
|
||||
* This fixes an issue where the CircularProgressbar svg has
|
||||
* 0 width inside a "display: flex" container, and thus not visible.
|
||||
*
|
||||
* If you're not using "display: flex", you can remove this style.
|
||||
*/
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.CircularProgressbar .CircularProgressbar-path {
|
||||
stroke: #3e98c7;
|
||||
stroke-linecap: round;
|
||||
transition: stroke-dashoffset 0.5s ease 0s;
|
||||
}
|
||||
|
||||
.CircularProgressbar .CircularProgressbar-trail {
|
||||
stroke: #d6d6d6;
|
||||
}
|
||||
|
||||
.CircularProgressbar .CircularProgressbar-text {
|
||||
fill: #3e98c7;
|
||||
font-size: 20px;
|
||||
dominant-baseline: middle;
|
||||
text-anchor: middle;
|
||||
}
|
||||
|
||||
.CircularProgressbar .CircularProgressbar-background {
|
||||
fill: #d6d6d6;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sample background styles. Use these with e.g.:
|
||||
*
|
||||
* <CircularProgressbar
|
||||
* className="CircularProgressbar-inverted"
|
||||
* background
|
||||
* percentage={50}
|
||||
* />
|
||||
*/
|
||||
.CircularProgressbar.CircularProgressbar-inverted .CircularProgressbar-background {
|
||||
fill: #3e98c7;
|
||||
}
|
||||
|
||||
.CircularProgressbar.CircularProgressbar-inverted .CircularProgressbar-text {
|
||||
fill: #fff;
|
||||
}
|
||||
|
||||
.CircularProgressbar.CircularProgressbar-inverted .CircularProgressbar-path {
|
||||
stroke: #fff;
|
||||
}
|
||||
|
||||
.CircularProgressbar.CircularProgressbar-inverted .CircularProgressbar-trail {
|
||||
stroke: transparent;
|
||||
}
|
||||
@ -1,53 +0,0 @@
|
||||
import React from 'react';
|
||||
import CircularProgressbar from '../src';
|
||||
|
||||
class ChangingProgressbar extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
currentPercentageIndex: 0,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
setInterval(() => {
|
||||
this.setState({
|
||||
currentPercentageIndex: (this.state.currentPercentageIndex + 1) % this.props.percentages.length
|
||||
});
|
||||
}, this.props.interval);
|
||||
}
|
||||
|
||||
getStyles() {
|
||||
return this.props.stylesForPercentage ? (
|
||||
this.props.stylesForPercentage(this.getCurrentPercentage())
|
||||
) : {};
|
||||
}
|
||||
|
||||
getCurrentPercentage() {
|
||||
return this.props.percentages[this.state.currentPercentageIndex];
|
||||
}
|
||||
|
||||
getText() {
|
||||
return this.props.textForPercentage
|
||||
? this.props.textForPercentage(this.getCurrentPercentage())
|
||||
: `${this.getCurrentPercentage()}%`;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<CircularProgressbar
|
||||
{...this.props}
|
||||
percentage={this.getCurrentPercentage()}
|
||||
text={this.getText()}
|
||||
styles={this.getStyles()}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ChangingProgressbar.defaultProps = {
|
||||
interval: 1000,
|
||||
}
|
||||
|
||||
export default ChangingProgressbar;
|
||||
139
docs/demo.jsx
@ -1,139 +0,0 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import CircularProgressbar from '../src';
|
||||
import ChangingProgressbar from './ChangingProgressbar';
|
||||
|
||||
const githubURL = 'https://github.com/kevinsqi/react-circular-progressbar';
|
||||
|
||||
const Example = ({ description, children }) => (
|
||||
<div className="col-xs-12 col-sm-6 col-md-3">
|
||||
<div className="row mb-1">
|
||||
<div className="col-xs-6 offset-xs-3">{children}</div>
|
||||
</div>
|
||||
<p className="text-xs-center">{description}</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
class Demo extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div className="container">
|
||||
<div className="row mt-3">
|
||||
<div className="col-xs-12">
|
||||
<div className="text-xs-center">
|
||||
<h1 className="mb-2">react-circular-progressbar</h1>
|
||||
<p>A circular progress indicator component</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="row mt-3 mb-3">
|
||||
<div className="col-xs-6 offset-xs-3 col-md-2 offset-md-5">
|
||||
<ChangingProgressbar
|
||||
percentages={[0, 20, 40, 60, 80, 100]}
|
||||
stylesForPercentage={(percentage) => {
|
||||
const alpha = (100 + percentage) / 200;
|
||||
return {
|
||||
path: {
|
||||
stroke: `rgba(62, 152, 199, ${alpha})`,
|
||||
},
|
||||
};
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
<div className="row mt-3">
|
||||
<h2 className="text-xs-center mb-3">Built with SVG and styled with plain CSS.</h2>
|
||||
|
||||
<Example description="Customize text and styling dynamically based on percentage.">
|
||||
<ChangingProgressbar
|
||||
percentages={[75, 100]}
|
||||
classForPercentage={(percentage) => {
|
||||
return percentage === 100 ? 'complete' : 'incomplete';
|
||||
}}
|
||||
textForPercentage={(percentage) => {
|
||||
return percentage === 100 ? `${percentage}!!` : `${percentage}`;
|
||||
}}
|
||||
/>
|
||||
</Example>
|
||||
|
||||
<Example description="Customize stroke width and make it go counterclockwise.">
|
||||
<ChangingProgressbar percentages={[0, 20, 80]} strokeWidth={5} counterClockwise />
|
||||
</Example>
|
||||
|
||||
<Example description="Add a background color for that inverted look.">
|
||||
<CircularProgressbar
|
||||
className="CircularProgressbar-inverted"
|
||||
background
|
||||
backgroundPadding={5}
|
||||
strokeWidth={6}
|
||||
percentage={66}
|
||||
text={`${66}%`}
|
||||
/>
|
||||
</Example>
|
||||
|
||||
<Example description="With SVG and CSS you can do whatever you want.">
|
||||
<div style={{ position: 'relative', width: '100%', height: '100%' }}>
|
||||
<div style={{ position: 'absolute', width: '100%' }}>
|
||||
<CircularProgressbar percentage={50} />
|
||||
</div>
|
||||
<div style={{ width: '100%', padding: '10%' }}>
|
||||
<img style={{ width: '100%' }} src="https://i.imgur.com/b9NyUGm.png" alt="doge" />
|
||||
</div>
|
||||
</div>
|
||||
</Example>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
<div className="mt-3 mb-3">
|
||||
<h2 className="text-xs-center">Installation</h2>
|
||||
<div className="text-xs-center mt-3">
|
||||
<p>Install with yarn or npm:</p>
|
||||
<p className="mb-3">
|
||||
<code>yarn add react-circular-progressbar</code>
|
||||
</p>
|
||||
<a className="btn btn-info btn-lg" href={githubURL}>
|
||||
View docs on Github
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
<div className="mt-3 mb-3">
|
||||
<h2 className="text-xs-center">Try it out</h2>
|
||||
<div className="row mt-3">
|
||||
<div className="col-md-10 offset-md-1">
|
||||
<iframe
|
||||
src="https://codesandbox.io/embed/vymm4oln6y"
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '500px',
|
||||
border: 0,
|
||||
borderRadius: '4px',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
<div className="my-3">
|
||||
<div className="text-xs-center">
|
||||
<a className="btn btn-info btn-lg" href={githubURL}>
|
||||
View docs on Github
|
||||
</a>
|
||||
<div className="mt-3">
|
||||
Built by <a href="http://www.kevinqi.com/">Kevin Qi</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.render(React.createElement(Demo), document.getElementById('demo'));
|
||||
@ -1,41 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge">
|
||||
|
||||
<title>react-circular-progressbar</title>
|
||||
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.5/css/bootstrap.min.css" />
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
|
||||
<style>
|
||||
/* bootstrap overrides */
|
||||
a {
|
||||
color: #3e98c7;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* demo style overrides */
|
||||
.CircularProgressbar.incomplete .CircularProgressbar-path {
|
||||
stroke: #f66;
|
||||
}
|
||||
.CircularProgressbar.incomplete .CircularProgressbar-text {
|
||||
fill: #f66;
|
||||
}
|
||||
.CircularProgressbar.complete .CircularProgressbar-path {
|
||||
stroke: #99f;
|
||||
}
|
||||
.CircularProgressbar.complete .CircularProgressbar-text {
|
||||
fill: #99f;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="demo"></div>
|
||||
<script src="index.js" type="text/javascript"></script>
|
||||
</body>
|
||||
</html>
|
||||
22620
docs/index.js
@ -1,61 +0,0 @@
|
||||
/*
|
||||
* react-circular-progressbar styles
|
||||
*
|
||||
* All of the styles in this file are optional and configurable!
|
||||
*/
|
||||
|
||||
.CircularProgressbar {
|
||||
/*
|
||||
* This fixes an issue where the CircularProgressbar svg has
|
||||
* 0 width inside a "display: flex" container, and thus not visible.
|
||||
*
|
||||
* If you're not using "display: flex", you can remove this style.
|
||||
*/
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.CircularProgressbar .CircularProgressbar-path {
|
||||
stroke: #3e98c7;
|
||||
stroke-linecap: round;
|
||||
transition: stroke-dashoffset 0.5s ease 0s;
|
||||
}
|
||||
|
||||
.CircularProgressbar .CircularProgressbar-trail {
|
||||
stroke: #d6d6d6;
|
||||
}
|
||||
|
||||
.CircularProgressbar .CircularProgressbar-text {
|
||||
fill: #3e98c7;
|
||||
font-size: 20px;
|
||||
dominant-baseline: middle;
|
||||
text-anchor: middle;
|
||||
}
|
||||
|
||||
.CircularProgressbar .CircularProgressbar-background {
|
||||
fill: #d6d6d6;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sample background styles. Use these with e.g.:
|
||||
*
|
||||
* <CircularProgressbar
|
||||
* className="CircularProgressbar-inverted"
|
||||
* background
|
||||
* percentage={50}
|
||||
* />
|
||||
*/
|
||||
.CircularProgressbar.CircularProgressbar-inverted .CircularProgressbar-background {
|
||||
fill: #3e98c7;
|
||||
}
|
||||
|
||||
.CircularProgressbar.CircularProgressbar-inverted .CircularProgressbar-text {
|
||||
fill: #fff;
|
||||
}
|
||||
|
||||
.CircularProgressbar.CircularProgressbar-inverted .CircularProgressbar-path {
|
||||
stroke: #fff;
|
||||
}
|
||||
|
||||
.CircularProgressbar.CircularProgressbar-inverted .CircularProgressbar-trail {
|
||||
stroke: transparent;
|
||||
}
|
||||
10
jest.config.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"rootDir": "test",
|
||||
"transform": {
|
||||
"^.+\\.(t|j)sx?$": "ts-jest"
|
||||
},
|
||||
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
|
||||
"moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json", "node"],
|
||||
"coverageDirectory": "<rootDir>/../coverage",
|
||||
"setupFilesAfterEnv": ["<rootDir>/setupTests.ts"]
|
||||
}
|
||||
63
package.json
@ -3,7 +3,12 @@
|
||||
"version": "1.0.0",
|
||||
"description": "A circular progress indicator component",
|
||||
"author": "Kevin Qi <iqnivek@gmail.com>",
|
||||
"main": "./dist/index.js",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"style": "dist/styles.css",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"repository": "https://github.com/kevinsqi/react-circular-progressbar.git",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
@ -12,43 +17,35 @@
|
||||
"react-component",
|
||||
"svg"
|
||||
],
|
||||
"style": "dist/styles.css",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "NODE_ENV=production webpack && npm run build:demo && npm run build:css",
|
||||
"build:css": "mkdir -p dist && cp ./src/styles.css ./dist/styles.css && cp ./src/styles.css ./docs/styles.css",
|
||||
"build:demo": "NODE_ENV=demo webpack",
|
||||
"build": "npm-run-all clean build:css build:js",
|
||||
"build:css": "postcss src/styles.css --use autoprefixer -d dist/ --no-map",
|
||||
"build:js": "tsc",
|
||||
"clean": "rimraf dist",
|
||||
"lint": "eslint src test",
|
||||
"prepare": "npm run clean && npm run build",
|
||||
"test": "mocha --compilers js:babel-register --recursive --require ./test/setup.js",
|
||||
"test:watch": "npm test -- --watch",
|
||||
"start": "webpack-dev-server --progress --inline"
|
||||
"format": "prettier --write 'src/**/*' 'demo/src/**/*'",
|
||||
"prepare": "npm-run-all clean build",
|
||||
"start": "npm-run-all --parallel start:css start:js",
|
||||
"start:css": "postcss src/styles.css --use autoprefixer -d dist/ --no-map --watch",
|
||||
"start:js": "tsc -w",
|
||||
"test": "jest --config jest.config.json --coverage"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-core": "^6.3.15",
|
||||
"babel-eslint": "^7.2.3",
|
||||
"babel-loader": "^7.0.0",
|
||||
"babel-preset-env": "^1.6.0",
|
||||
"babel-preset-react": "^6.3.13",
|
||||
"babel-register": "^6.8.0",
|
||||
"chai": "^3.5.0",
|
||||
"enzyme": "^2.3.0",
|
||||
"eslint": "^3.11.0",
|
||||
"eslint-config-airbnb": "^14.1.0",
|
||||
"eslint-plugin-import": "^2.2.0",
|
||||
"eslint-plugin-jsx-a11y": "^4.0.0",
|
||||
"eslint-plugin-react": "^6.0.0",
|
||||
"jsdom": "^9.0.0",
|
||||
"mocha": "^3.3.0",
|
||||
"react": "^15.4.0",
|
||||
"react-addons-test-utils": "^15.0.2",
|
||||
"react-dom": "^15.4.0",
|
||||
"@types/enzyme": "^3.9.1",
|
||||
"@types/enzyme-adapter-react-16": "^1.0.5",
|
||||
"@types/jest": "^24.0.11",
|
||||
"@types/react": "^16.8.14",
|
||||
"autoprefixer": "^9.5.1",
|
||||
"enzyme": "^3.9.0",
|
||||
"enzyme-adapter-react-16": "^1.12.1",
|
||||
"jest": "^24.7.1",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"postcss-cli": "^6.1.2",
|
||||
"prettier": "^1.17.0",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6",
|
||||
"rimraf": "^2.3.4",
|
||||
"webpack": "^2.5.1",
|
||||
"webpack-dev-server": "^2.4.5"
|
||||
"ts-jest": "^24.0.2",
|
||||
"typescript": "^3.4.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"prop-types": "^15.5.10"
|
||||
|
||||
@ -9,8 +9,70 @@ const FULL_RADIUS = 50;
|
||||
const CENTER_X = 50;
|
||||
const CENTER_Y = 50;
|
||||
|
||||
class CircularProgressbar extends React.Component {
|
||||
constructor(props) {
|
||||
type CircularProgressbarDefaultProps = {
|
||||
strokeWidth: number;
|
||||
className: string;
|
||||
text: string;
|
||||
background: boolean;
|
||||
backgroundPadding: number;
|
||||
initialAnimation: boolean;
|
||||
counterClockwise: boolean;
|
||||
classes: {
|
||||
root: string;
|
||||
trail: string;
|
||||
path: string;
|
||||
text: string;
|
||||
background: string;
|
||||
};
|
||||
styles: {
|
||||
root?: object;
|
||||
trail?: object;
|
||||
path?: object;
|
||||
text?: object;
|
||||
background?: object;
|
||||
};
|
||||
};
|
||||
|
||||
type CircularProgressbarProps = CircularProgressbarDefaultProps & {
|
||||
percentage: number;
|
||||
};
|
||||
|
||||
type CircularProgressbarState = {
|
||||
percentage: number;
|
||||
};
|
||||
|
||||
class CircularProgressbar extends React.Component<
|
||||
CircularProgressbarProps,
|
||||
CircularProgressbarState
|
||||
> {
|
||||
initialTimeout: number | undefined = undefined;
|
||||
requestAnimationFrame: number | undefined = undefined;
|
||||
|
||||
static defaultProps: CircularProgressbarDefaultProps = {
|
||||
strokeWidth: 8,
|
||||
className: '',
|
||||
text: '',
|
||||
background: false,
|
||||
backgroundPadding: 0,
|
||||
initialAnimation: false,
|
||||
counterClockwise: false,
|
||||
classes: {
|
||||
root: 'CircularProgressbar',
|
||||
trail: 'CircularProgressbar-trail',
|
||||
path: 'CircularProgressbar-path',
|
||||
text: 'CircularProgressbar-text',
|
||||
background: 'CircularProgressbar-background',
|
||||
},
|
||||
styles: {
|
||||
root: {},
|
||||
trail: {},
|
||||
path: {},
|
||||
text: {},
|
||||
background: {},
|
||||
},
|
||||
};
|
||||
|
||||
constructor(props: CircularProgressbarProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
@ -20,7 +82,7 @@ class CircularProgressbar extends React.Component {
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.initialAnimation) {
|
||||
this.initialTimeout = setTimeout(() => {
|
||||
this.initialTimeout = window.setTimeout(() => {
|
||||
this.requestAnimationFrame = window.requestAnimationFrame(() => {
|
||||
this.setState({
|
||||
percentage: this.props.percentage,
|
||||
@ -30,7 +92,7 @@ class CircularProgressbar extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
componentWillReceiveProps(nextProps: CircularProgressbarProps) {
|
||||
this.setState({
|
||||
percentage: nextProps.percentage,
|
||||
});
|
||||
@ -38,7 +100,9 @@ class CircularProgressbar extends React.Component {
|
||||
|
||||
componentWillUnmount() {
|
||||
clearTimeout(this.initialTimeout);
|
||||
window.cancelAnimationFrame(this.requestAnimationFrame);
|
||||
if (this.requestAnimationFrame) {
|
||||
window.cancelAnimationFrame(this.requestAnimationFrame);
|
||||
}
|
||||
}
|
||||
|
||||
getBackgroundPadding() {
|
||||
@ -72,7 +136,10 @@ class CircularProgressbar extends React.Component {
|
||||
|
||||
getPathStyles() {
|
||||
const diameter = Math.PI * 2 * this.getPathRadius();
|
||||
const truncatedPercentage = Math.min(Math.max(this.state.percentage, MIN_PERCENTAGE), MAX_PERCENTAGE);
|
||||
const truncatedPercentage = Math.min(
|
||||
Math.max(this.state.percentage, MIN_PERCENTAGE),
|
||||
MAX_PERCENTAGE,
|
||||
);
|
||||
const dashoffset = ((100 - truncatedPercentage) / 100) * diameter;
|
||||
|
||||
return {
|
||||
@ -84,18 +151,11 @@ class CircularProgressbar extends React.Component {
|
||||
getPathRadius() {
|
||||
// the radius of the path is defined to be in the middle, so in order for the path to
|
||||
// fit perfectly inside the 100x100 viewBox, need to subtract half the strokeWidth
|
||||
return FULL_RADIUS - (this.props.strokeWidth / 2) - this.getBackgroundPadding();
|
||||
return FULL_RADIUS - this.props.strokeWidth / 2 - this.getBackgroundPadding();
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
percentage,
|
||||
className,
|
||||
classes,
|
||||
styles,
|
||||
strokeWidth,
|
||||
text,
|
||||
} = this.props;
|
||||
const { percentage, className, classes, styles, strokeWidth, text } = this.props;
|
||||
const pathDescription = this.getPathDescription();
|
||||
|
||||
return (
|
||||
@ -104,17 +164,15 @@ class CircularProgressbar extends React.Component {
|
||||
style={styles.root}
|
||||
viewBox={`0 0 ${MAX_X} ${MAX_Y}`}
|
||||
>
|
||||
{
|
||||
this.props.background ? (
|
||||
<circle
|
||||
className={classes.background}
|
||||
style={styles.background}
|
||||
cx={CENTER_X}
|
||||
cy={CENTER_Y}
|
||||
r={FULL_RADIUS}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
{this.props.background ? (
|
||||
<circle
|
||||
className={classes.background}
|
||||
style={styles.background}
|
||||
cx={CENTER_X}
|
||||
cy={CENTER_Y}
|
||||
r={FULL_RADIUS}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<path
|
||||
className={classes.trail}
|
||||
@ -132,58 +190,14 @@ class CircularProgressbar extends React.Component {
|
||||
style={Object.assign({}, styles.path, this.getPathStyles())}
|
||||
/>
|
||||
|
||||
{
|
||||
text ? (
|
||||
<text
|
||||
className={classes.text}
|
||||
style={styles.text}
|
||||
x={CENTER_X}
|
||||
y={CENTER_Y}
|
||||
>
|
||||
{text}
|
||||
</text>
|
||||
) : null
|
||||
}
|
||||
{text ? (
|
||||
<text className={classes.text} style={styles.text} x={CENTER_X} y={CENTER_Y}>
|
||||
{text}
|
||||
</text>
|
||||
) : null}
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
CircularProgressbar.propTypes = {
|
||||
percentage: PropTypes.number.isRequired,
|
||||
className: PropTypes.string,
|
||||
text: PropTypes.string,
|
||||
classes: PropTypes.objectOf(PropTypes.string),
|
||||
styles: PropTypes.objectOf(PropTypes.object),
|
||||
strokeWidth: PropTypes.number,
|
||||
background: PropTypes.bool,
|
||||
backgroundPadding: PropTypes.number,
|
||||
initialAnimation: PropTypes.bool,
|
||||
counterClockwise: PropTypes.bool,
|
||||
};
|
||||
|
||||
CircularProgressbar.defaultProps = {
|
||||
strokeWidth: 8,
|
||||
className: '',
|
||||
text: null,
|
||||
classes: {
|
||||
root: 'CircularProgressbar',
|
||||
trail: 'CircularProgressbar-trail',
|
||||
path: 'CircularProgressbar-path',
|
||||
text: 'CircularProgressbar-text',
|
||||
background: 'CircularProgressbar-background',
|
||||
},
|
||||
styles: {
|
||||
root: {},
|
||||
trail: {},
|
||||
path: {},
|
||||
text: {},
|
||||
background: {},
|
||||
},
|
||||
background: false,
|
||||
backgroundPadding: null,
|
||||
initialAnimation: false,
|
||||
counterClockwise: false,
|
||||
};
|
||||
|
||||
export default CircularProgressbar;
|
||||
141
test/CircularProgressbar.test.tsx
Normal file
@ -0,0 +1,141 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import CircularProgressbar from '../src/index';
|
||||
|
||||
describe('<CircularProgressbar />', () => {
|
||||
test('SVG rendered to DOM', () => {
|
||||
const wrapper = shallow(<CircularProgressbar percentage={50} />);
|
||||
expect(wrapper.find('svg').length).toBe(1);
|
||||
});
|
||||
describe('props.strokeWidth', () => {
|
||||
test('Applies to path', () => {
|
||||
const wrapper = shallow(<CircularProgressbar percentage={50} strokeWidth={2} />);
|
||||
expect(wrapper.find('.CircularProgressbar-path').prop('strokeWidth')).toEqual(2);
|
||||
});
|
||||
});
|
||||
describe('props.className', () => {
|
||||
test('Applies to SVG', () => {
|
||||
const wrapper = shallow(<CircularProgressbar percentage={50} className="my-custom-class" />);
|
||||
expect(wrapper.find('svg').prop('className')).toContain('my-custom-class');
|
||||
});
|
||||
});
|
||||
describe('props.text', () => {
|
||||
test('Does not render when blank', () => {
|
||||
const wrapper = shallow(<CircularProgressbar percentage={50} />);
|
||||
expect(wrapper.find('.CircularProgressbar-text').exists()).toEqual(false);
|
||||
});
|
||||
test('Renders the correct string', () => {
|
||||
const percentage = 50;
|
||||
const wrapper = shallow(
|
||||
<CircularProgressbar percentage={percentage} text={`${percentage}%`} />,
|
||||
);
|
||||
expect(wrapper.find('.CircularProgressbar-text').text()).toEqual('50%');
|
||||
});
|
||||
});
|
||||
describe('props.percentage', () => {
|
||||
test('Renders correct path', () => {
|
||||
const percentage = 30;
|
||||
const wrapper = shallow(
|
||||
<CircularProgressbar percentage={percentage} strokeWidth={0} className="my-custom-class" />,
|
||||
);
|
||||
|
||||
const dashoffset = wrapper.find('.CircularProgressbar-path').prop('style')!.strokeDashoffset;
|
||||
const expectedRadius = 50;
|
||||
const expectedDiameter = 2 * expectedRadius * Math.PI;
|
||||
const expectedOffset = ((100 - percentage) / 100) * expectedDiameter;
|
||||
expect(dashoffset).toEqual(`${expectedOffset}px`);
|
||||
|
||||
const expectedArcto = `a ${expectedRadius},${expectedRadius}`;
|
||||
expect(wrapper.find('.CircularProgressbar-path').prop('d')).toContain(expectedArcto);
|
||||
});
|
||||
});
|
||||
describe('props.counterClockwise', () => {
|
||||
test('Reverses dashoffset', () => {
|
||||
const clockwise = shallow(<CircularProgressbar percentage={50} />);
|
||||
const counterClockwise = shallow(<CircularProgressbar percentage={50} counterClockwise />);
|
||||
|
||||
// Counterclockwise should have the negative dashoffset of clockwise
|
||||
expect(
|
||||
`-${clockwise.find('.CircularProgressbar-path').prop('style')!.strokeDashoffset}`,
|
||||
).toEqual(counterClockwise.find('.CircularProgressbar-path').prop('style')!.strokeDashoffset);
|
||||
});
|
||||
});
|
||||
describe('props.styles', () => {
|
||||
test('Style customizations applied to all subcomponents', () => {
|
||||
const percentage = 50;
|
||||
const wrapper = shallow(
|
||||
<CircularProgressbar
|
||||
percentage={percentage}
|
||||
text={`${percentage}%`}
|
||||
background
|
||||
styles={{
|
||||
root: { stroke: '#000000' },
|
||||
trail: { stroke: '#111111' },
|
||||
path: { stroke: '#222222' },
|
||||
text: { stroke: '#333333' },
|
||||
background: { stroke: '#444444' },
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
expect(wrapper.find('.CircularProgressbar').prop('style')!.stroke).toEqual('#000000');
|
||||
expect(wrapper.find('.CircularProgressbar-trail').prop('style')!.stroke).toEqual('#111111');
|
||||
expect(wrapper.find('.CircularProgressbar-path').prop('style')!.stroke).toEqual('#222222');
|
||||
expect(wrapper.find('.CircularProgressbar-text').prop('style')!.stroke).toEqual('#333333');
|
||||
expect(wrapper.find('.CircularProgressbar-background').prop('style')!.stroke).toEqual(
|
||||
'#444444',
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('props.background', () => {
|
||||
test('Background does not render when prop is false', () => {
|
||||
const wrapper = shallow(<CircularProgressbar percentage={50} background={false} />);
|
||||
expect(wrapper.find('.CircularProgressbar-background').exists()).toEqual(false);
|
||||
});
|
||||
test('Renders a <circle> with correct radius', () => {
|
||||
const wrapper = shallow(<CircularProgressbar percentage={50} background />);
|
||||
expect(wrapper.find('.CircularProgressbar-background').exists()).toBe(true);
|
||||
expect(wrapper.find('.CircularProgressbar-background').type()).toEqual('circle');
|
||||
expect(wrapper.find('.CircularProgressbar-background').prop('r')).toEqual(50);
|
||||
});
|
||||
});
|
||||
describe('props.classes', () => {
|
||||
test('Has default values', () => {
|
||||
const wrapper = shallow(<CircularProgressbar percentage={50} text="50" />);
|
||||
expect(wrapper.find('.CircularProgressbar').type()).toEqual('svg');
|
||||
expect(wrapper.find('.CircularProgressbar-path').type()).toEqual('path');
|
||||
expect(wrapper.find('.CircularProgressbar-trail').type()).toEqual('path');
|
||||
expect(wrapper.find('.CircularProgressbar-text').type()).toEqual('text');
|
||||
});
|
||||
test('Prop overrides work', () => {
|
||||
const wrapper = shallow(
|
||||
<CircularProgressbar
|
||||
percentage={50}
|
||||
text="50"
|
||||
background
|
||||
classes={{
|
||||
root: 'someRootClass',
|
||||
path: 'somePathClass',
|
||||
trail: 'someTrailClass',
|
||||
text: 'someTextClass',
|
||||
background: 'someBackgroundClass',
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
|
||||
// Assert default classes don't exist
|
||||
expect(wrapper.find('.CircularProgressbar').exists()).toBe(false);
|
||||
expect(wrapper.find('.CircularProgressbar-path').exists()).toBe(false);
|
||||
expect(wrapper.find('.CircularProgressbar-trail').exists()).toBe(false);
|
||||
expect(wrapper.find('.CircularProgressbar-text').exists()).toBe(false);
|
||||
expect(wrapper.find('.CircularProgressbar-background').exists()).toBe(false);
|
||||
|
||||
// Assert override classes do exist
|
||||
expect(wrapper.find('.someRootClass').type()).toEqual('svg');
|
||||
expect(wrapper.find('.somePathClass').type()).toEqual('path');
|
||||
expect(wrapper.find('.someTrailClass').type()).toEqual('path');
|
||||
expect(wrapper.find('.someTextClass').type()).toEqual('text');
|
||||
expect(wrapper.find('.someBackgroundClass').type()).toEqual('circle');
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,197 +0,0 @@
|
||||
import React from 'react';
|
||||
import { assert } from 'chai';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import CircularProgressbar from '../../src';
|
||||
|
||||
describe('CircularProgressbar', () => {
|
||||
it('should not throw exceptions in base case', () => {
|
||||
assert.doesNotThrow(() => <CircularProgressbar percentage={50} />);
|
||||
});
|
||||
|
||||
it('should render as an svg', () => {
|
||||
const wrapper = shallow(
|
||||
<CircularProgressbar percentage={50} />
|
||||
);
|
||||
assert.equal(1, wrapper.find('svg').length);
|
||||
});
|
||||
});
|
||||
|
||||
describe('CircularProgressbar props', () => {
|
||||
it('strokeWidth', () => {
|
||||
const wrapper = shallow(
|
||||
<CircularProgressbar
|
||||
percentage={50}
|
||||
strokeWidth={2}
|
||||
/>
|
||||
);
|
||||
assert.equal(2, wrapper.find('.CircularProgressbar-path').prop('strokeWidth'));
|
||||
});
|
||||
|
||||
it('className', () => {
|
||||
const wrapper = shallow(
|
||||
<CircularProgressbar
|
||||
percentage={50}
|
||||
className="my-custom-class"
|
||||
/>
|
||||
);
|
||||
assert(wrapper.find('svg').prop('className').includes('my-custom-class'));
|
||||
});
|
||||
|
||||
it('text does not render when null', () => {
|
||||
const wrapper = shallow(
|
||||
<CircularProgressbar
|
||||
percentage={50}
|
||||
/>
|
||||
);
|
||||
assert(!wrapper.find('.CircularProgressbar-text').exists());
|
||||
});
|
||||
|
||||
it('text', () => {
|
||||
const percentage = 50;
|
||||
const wrapper = shallow(
|
||||
<CircularProgressbar
|
||||
percentage={percentage}
|
||||
text={`${percentage}%`}
|
||||
/>
|
||||
);
|
||||
assert.equal(wrapper.find('.CircularProgressbar-text').text(), '50%');
|
||||
});
|
||||
|
||||
it('percentage', () => {
|
||||
const percentage = 30;
|
||||
const wrapper = shallow(
|
||||
<CircularProgressbar
|
||||
percentage={percentage}
|
||||
strokeWidth={0}
|
||||
className="my-custom-class"
|
||||
/>
|
||||
);
|
||||
|
||||
const dashoffset = wrapper.find('.CircularProgressbar-path').prop('style').strokeDashoffset;
|
||||
const expectedRadius = 50;
|
||||
const expectedDiameter = 2 * expectedRadius * Math.PI;
|
||||
const expectedOffset = ((100 - percentage) / 100) * expectedDiameter;
|
||||
assert.equal(dashoffset, `${expectedOffset}px`);
|
||||
|
||||
const expectedArcto = `a ${expectedRadius},${expectedRadius}`;
|
||||
assert(wrapper.find('.CircularProgressbar-path').prop('d').includes(expectedArcto));
|
||||
});
|
||||
|
||||
it('counterClockwise', () => {
|
||||
const clockwise = shallow(
|
||||
<CircularProgressbar percentage={50} />
|
||||
);
|
||||
const counterClockwise = shallow(
|
||||
<CircularProgressbar percentage={50} counterClockwise />
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
`-${clockwise.find('.CircularProgressbar-path').prop('style').strokeDashoffset}`,
|
||||
counterClockwise.find('.CircularProgressbar-path').prop('style').strokeDashoffset,
|
||||
'counterclockwise should have the negative dashoffset of clockwise',
|
||||
);
|
||||
});
|
||||
|
||||
it('styles', () => {
|
||||
const percentage = 50;
|
||||
const wrapper = shallow(
|
||||
<CircularProgressbar
|
||||
percentage={percentage}
|
||||
text={`${percentage}%`}
|
||||
background
|
||||
styles={{
|
||||
root: { stroke: '#000000' },
|
||||
trail: { stroke: '#111111' },
|
||||
path: { stroke: '#222222' },
|
||||
text: { stroke: '#333333' },
|
||||
background: { stroke: '#444444' },
|
||||
}}
|
||||
/>
|
||||
);
|
||||
assert.equal(
|
||||
wrapper.find('.CircularProgressbar').prop('style').stroke,
|
||||
'#000000',
|
||||
);
|
||||
assert.equal(
|
||||
wrapper.find('.CircularProgressbar-trail').prop('style').stroke,
|
||||
'#111111',
|
||||
);
|
||||
assert.equal(
|
||||
wrapper.find('.CircularProgressbar-path').prop('style').stroke,
|
||||
'#222222',
|
||||
);
|
||||
assert.equal(
|
||||
wrapper.find('.CircularProgressbar-text').prop('style').stroke,
|
||||
'#333333',
|
||||
);
|
||||
assert.equal(
|
||||
wrapper.find('.CircularProgressbar-background').prop('style').stroke,
|
||||
'#444444',
|
||||
);
|
||||
});
|
||||
|
||||
it('background does not render when null', () => {
|
||||
const wrapper = shallow(
|
||||
<CircularProgressbar
|
||||
percentage={50}
|
||||
/>
|
||||
);
|
||||
assert(!wrapper.find('.CircularProgressbar-background').exists());
|
||||
});
|
||||
|
||||
it('background', () => {
|
||||
const wrapper = shallow(
|
||||
<CircularProgressbar
|
||||
percentage={50}
|
||||
background
|
||||
/>
|
||||
);
|
||||
assert(wrapper.find('.CircularProgressbar-background').exists());
|
||||
assert.equal(wrapper.find('.CircularProgressbar-background').type(), 'circle');
|
||||
assert.equal(wrapper.find('.CircularProgressbar-background').prop('r'), 50);
|
||||
});
|
||||
|
||||
it('classes defaults', () => {
|
||||
const wrapper = shallow(
|
||||
<CircularProgressbar
|
||||
percentage={50}
|
||||
text="50"
|
||||
/>
|
||||
);
|
||||
assert.equal(wrapper.find('.CircularProgressbar').type(), 'svg');
|
||||
assert.equal(wrapper.find('.CircularProgressbar-path').type(), 'path');
|
||||
assert.equal(wrapper.find('.CircularProgressbar-trail').type(), 'path');
|
||||
assert.equal(wrapper.find('.CircularProgressbar-text').type(), 'text');
|
||||
});
|
||||
|
||||
it('classes', () => {
|
||||
const wrapper = shallow(
|
||||
<CircularProgressbar
|
||||
percentage={50}
|
||||
text="50"
|
||||
background
|
||||
classes={{
|
||||
root: 'someRootClass',
|
||||
path: 'somePathClass',
|
||||
trail: 'someTrailClass',
|
||||
text: 'someTextClass',
|
||||
background: 'someBackgroundClass',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
// Assert default classes don't exist
|
||||
assert(!wrapper.find('.CircularProgressbar').exists());
|
||||
assert(!wrapper.find('.CircularProgressbar-path').exists());
|
||||
assert(!wrapper.find('.CircularProgressbar-trail').exists());
|
||||
assert(!wrapper.find('.CircularProgressbar-text').exists());
|
||||
assert(!wrapper.find('.CircularProgressbar-background').exists());
|
||||
// Assert override classes do exist
|
||||
assert.equal(wrapper.find('.someRootClass').type(), 'svg');
|
||||
assert.equal(wrapper.find('.somePathClass').type(), 'path');
|
||||
assert.equal(wrapper.find('.someTrailClass').type(), 'path');
|
||||
assert.equal(wrapper.find('.someTextClass').type(), 'text');
|
||||
assert.equal(wrapper.find('.someBackgroundClass').type(), 'circle');
|
||||
});
|
||||
});
|
||||
@ -1,20 +0,0 @@
|
||||
/* global document */
|
||||
|
||||
// https://github.com/airbnb/enzyme/blob/master/docs/guides/jsdom.md#using-enzyme-with-jsdom
|
||||
|
||||
const jsdom = require('jsdom').jsdom;
|
||||
|
||||
const exposedProperties = ['window', 'navigator', 'document'];
|
||||
|
||||
global.document = jsdom('');
|
||||
global.window = document.defaultView;
|
||||
Object.keys(document.defaultView).forEach((property) => {
|
||||
if (typeof global[property] === 'undefined') {
|
||||
exposedProperties.push(property);
|
||||
global[property] = document.defaultView[property];
|
||||
}
|
||||
});
|
||||
|
||||
global.navigator = {
|
||||
userAgent: 'node.js',
|
||||
};
|
||||
4
test/setupTests.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import Enzyme from 'enzyme';
|
||||
import Adapter from 'enzyme-adapter-react-16';
|
||||
|
||||
Enzyme.configure({ adapter: new Adapter() });
|
||||
18
tsconfig.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"declaration": true,
|
||||
"declarationDir": "dist",
|
||||
"esModuleInterop": true,
|
||||
"jsx": "react",
|
||||
"lib": ["es2015", "dom"],
|
||||
"module": "commonjs",
|
||||
"noImplicitAny": true,
|
||||
"outDir": "./dist",
|
||||
"removeComments": true,
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"target": "es5"
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
@ -1,72 +0,0 @@
|
||||
const webpack = require('webpack');
|
||||
const path = require('path');
|
||||
|
||||
const nodeEnv = process.env.NODE_ENV || 'development';
|
||||
const demoDir = 'docs';
|
||||
|
||||
const webpackConfig = {
|
||||
context: __dirname,
|
||||
entry: {
|
||||
'react-circular-progressbar': [
|
||||
path.resolve(__dirname, 'src', 'index.jsx')
|
||||
]
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(__dirname),
|
||||
filename: 'index.js',
|
||||
library: 'CircularProgressbar',
|
||||
libraryTarget: 'umd'
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.js', '.jsx'],
|
||||
modules: ['node_modules']
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.jsx?$/,
|
||||
exclude: /(node_modules)/,
|
||||
loader: 'babel-loader'
|
||||
}
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.NODE_ENV': JSON.stringify(nodeEnv)
|
||||
}),
|
||||
new webpack.LoaderOptionsPlugin({
|
||||
debug: true
|
||||
})
|
||||
]
|
||||
};
|
||||
|
||||
if (nodeEnv === 'development') {
|
||||
webpackConfig.devtool = 'source-map';
|
||||
webpackConfig.devServer = { contentBase: path.resolve(__dirname, demoDir) };
|
||||
webpackConfig.entry['react-circular-progressbar'].unshift('webpack-dev-server/client?http://0.0.0.0:8080/');
|
||||
webpackConfig.entry['react-circular-progressbar'].push(path.resolve(__dirname, demoDir, 'demo.jsx'));
|
||||
webpackConfig.output.publicPath = '/';
|
||||
}
|
||||
|
||||
if (nodeEnv === 'demo') {
|
||||
webpackConfig.entry['react-circular-progressbar'].push(path.resolve(__dirname, demoDir, 'demo.jsx'));
|
||||
webpackConfig.output.path = path.resolve(__dirname, demoDir);
|
||||
}
|
||||
|
||||
if (nodeEnv === 'production') {
|
||||
webpackConfig.externals = {
|
||||
'react': {
|
||||
root: 'React',
|
||||
commonjs2: 'react',
|
||||
commonjs: 'react',
|
||||
amd: 'react'
|
||||
}
|
||||
};
|
||||
webpackConfig.output.path = path.resolve(__dirname, 'dist');
|
||||
webpackConfig.plugins.push(new webpack.optimize.UglifyJsPlugin({
|
||||
compress: { warnings: false },
|
||||
sourceMap: false
|
||||
}));
|
||||
}
|
||||
|
||||
module.exports = webpackConfig;
|
||||