├── .travis.yml
├── src
├── Util
│ └── Confetti.js
├── LuckyDraw
│ ├── index.js
│ ├── Arc.js
│ ├── Wheel.js
│ ├── Style
│ │ └── LuckyDraw.styl
│ └── LuckyDraw.js
├── App.test.js
├── index.js
├── App.styl
├── btn.css
└── App.js
├── .npmignore
├── public
├── favicon.ico
├── manifest.webmanifest
└── index.html
├── .gitignore
├── sw-precache-config.js
├── config
├── jest
│ ├── fileTransform.js
│ └── cssTransform.js
├── polyfills.js
├── env.js
├── paths.js
├── webpack.config.dev.js
└── webpack.config.prod.js
├── lib
├── index.js
├── LuckyDraw.css
├── Arc.js
├── Wheel.js
└── LuckyDraw.js
├── scripts
├── test.js
├── build.js
└── start.js
├── README.md
└── package.json
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "stable"
--------------------------------------------------------------------------------
/src/Util/Confetti.js:
--------------------------------------------------------------------------------
1 | export default class Confetti {}
2 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | *.log
3 | src
4 | coverage
5 | test
6 | example
7 | karma.conf.js
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gemerz/react-luckydraw/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/src/LuckyDraw/index.js:
--------------------------------------------------------------------------------
1 | import LuckyDraw from './LuckyDraw';
2 |
3 | export default LuckyDraw;
4 |
--------------------------------------------------------------------------------
/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render(, div);
8 | });
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | node_modules
5 |
6 | # testing
7 | coverage
8 |
9 | # production
10 | build
11 | .idea
12 |
13 | # misc
14 | .DS_Store
15 | .env
16 | npm-debug.log
17 |
--------------------------------------------------------------------------------
/sw-precache-config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | stripPrefix: 'build/',
3 | staticFileGlobs: [
4 | 'build/*.html',
5 | 'build/manifest.json',
6 | 'build/static/**/!(*map*)'
7 | ],
8 | dontCacheBustUrlsMatching: /\.\w{8}\./,
9 | swFilePath: 'build/service-worker.js'
10 | };
11 |
--------------------------------------------------------------------------------
/config/jest/fileTransform.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | // This is a custom Jest transformer turning file imports into filenames.
4 | // http://facebook.github.io/jest/docs/tutorial-webpack.html
5 |
6 | module.exports = {
7 | process(src, filename) {
8 | return 'module.exports = ' + JSON.stringify(path.basename(filename)) + ';';
9 | },
10 | };
11 |
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _LuckyDraw = require('./LuckyDraw');
8 |
9 | var _LuckyDraw2 = _interopRequireDefault(_LuckyDraw);
10 |
11 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
12 |
13 | exports.default = _LuckyDraw2.default;
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(, document.getElementById('root'));
6 |
7 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
8 | window.addEventListener('load', function() {
9 | navigator.serviceWorker.register('./service-worker.js');
10 | });
11 | }
12 |
--------------------------------------------------------------------------------
/config/jest/cssTransform.js:
--------------------------------------------------------------------------------
1 | // This is a custom Jest transformer turning style imports into empty objects.
2 | // http://facebook.github.io/jest/docs/tutorial-webpack.html
3 |
4 | module.exports = {
5 | process() {
6 | return 'module.exports = {};';
7 | },
8 | getCacheKey(fileData, filename) {
9 | // The output is always the same.
10 | return 'cssTransform';
11 | },
12 | };
13 |
--------------------------------------------------------------------------------
/src/App.styl:
--------------------------------------------------------------------------------
1 | body
2 | width 100%
3 | overflow hidden
4 | margin 0
5 | padding 0
6 | color #ffffff
7 | background-color darken(#252B37, 3%);
8 | font-family -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif
9 |
10 | .App
11 | max-width 920px
12 | margin 0 auto
13 | min-height 100vh
14 | display flex
15 | flex-direction column
16 | justify-content center
17 | align-items center
18 |
19 | h1
20 | color #ffffff
21 | padding 0px 0 30px 0
22 |
23 | .luckdraw
24 | width 550px
25 | height 350px
26 |
27 | .ReactTabs__TabPanel>div
28 | margin 0 auto
29 |
--------------------------------------------------------------------------------
/config/polyfills.js:
--------------------------------------------------------------------------------
1 | if (typeof Promise === 'undefined') {
2 | // Rejection tracking prevents a common issue where React gets into an
3 | // inconsistent state due to an error, but it gets swallowed by a Promise,
4 | // and the user has no idea what causes React's erratic future behavior.
5 | require('promise/lib/rejection-tracking').enable();
6 | window.Promise = require('promise/lib/es6-extensions.js');
7 | }
8 |
9 | // fetch() polyfill for making API calls.
10 | require('whatwg-fetch');
11 |
12 | // Object.assign() is commonly used with React.
13 | // It will use the native implementation if it's present and isn't buggy.
14 | Object.assign = require('object-assign');
15 |
--------------------------------------------------------------------------------
/public/manifest.webmanifest:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-luckydraw",
3 | "short_name": "react-luckydraw",
4 | "description": "Progressive Web react-luckydraw component",
5 | "start_url": "./?app=true",
6 | "display": "fullscreen",
7 | "orientation": "portrait",
8 | "background_color": "#fff",
9 | "theme_color": "#34495f",
10 | "icons": [{
11 | "src": "./favicon-16x16.png",
12 | "sizes": "16x16",
13 | "type": "image/png"
14 | }, {
15 | "src": "./favicon-32x32.png",
16 | "sizes": "32x32",
17 | "type": "image/png"
18 | }, {
19 | "src": "./android-chrome-192x192.png",
20 | "sizes": "192x192",
21 | "type": "image/png"
22 | }, {
23 | "src": "./android-chrome-256x256.png",
24 | "sizes": "256x256",
25 | "type": "image/png"
26 | }]
27 | }
28 |
--------------------------------------------------------------------------------
/scripts/test.js:
--------------------------------------------------------------------------------
1 | process.env.NODE_ENV = 'test';
2 | process.env.PUBLIC_URL = '';
3 |
4 | // Load environment variables from .env file. Suppress warnings using silent
5 | // if this file is missing. dotenv will never modify any environment variables
6 | // that have already been set.
7 | // https://github.com/motdotla/dotenv
8 | require('dotenv').config({silent: true});
9 |
10 | const jest = require('jest');
11 | const argv = process.argv.slice(2);
12 |
13 | // Watch unless on CI or in coverage mode
14 | if (!process.env.CI && argv.indexOf('--coverage') < 0) {
15 | argv.push('--watch');
16 | }
17 |
18 | // A temporary hack to clear terminal correctly.
19 | // You can remove this after updating to Jest 18 when it's out.
20 | // https://github.com/facebook/jest/pull/2230
21 | var realWrite = process.stdout.write;
22 | var CLEAR = process.platform === 'win32' ? '\x1Bc' : '\x1B[2J\x1B[3J\x1B[H';
23 | process.stdout.write = function(chunk, encoding, callback) {
24 | if (chunk === '\x1B[2J\x1B[H') {
25 | chunk = CLEAR;
26 | }
27 | return realWrite.call(this, chunk, encoding, callback);
28 | };
29 |
30 |
31 | jest.run(argv);
32 |
--------------------------------------------------------------------------------
/config/env.js:
--------------------------------------------------------------------------------
1 | // Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
2 | // injected into the application via DefinePlugin in Webpack configuration.
3 |
4 | var REACT_APP = /^REACT_APP_/i;
5 |
6 | function getClientEnvironment(publicUrl) {
7 | var processEnv = Object
8 | .keys(process.env)
9 | .filter(key => REACT_APP.test(key))
10 | .reduce((env, key) => {
11 | env[key] = JSON.stringify(process.env[key]);
12 | return env;
13 | }, {
14 | // Useful for determining whether we’re running in production mode.
15 | // Most importantly, it switches React into the correct mode.
16 | 'NODE_ENV': JSON.stringify(
17 | process.env.NODE_ENV || 'development'
18 | ),
19 | // Useful for resolving the correct path to static assets in `public`.
20 | // For example,
.
21 | // This should only be used as an escape hatch. Normally you would put
22 | // images into the `src` and `import` them in code to get their paths.
23 | 'PUBLIC_URL': JSON.stringify(publicUrl)
24 | });
25 | return {'process.env': processEnv};
26 | }
27 |
28 | module.exports = getClientEnvironment;
29 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
16 | React LuckyDraw
17 |
18 |
19 |
23 |
24 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/config/paths.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var fs = require('fs');
3 |
4 | // Make sure any symlinks in the project folder are resolved:
5 | // https://github.com/facebookincubator/create-react-app/issues/637
6 | var appDirectory = fs.realpathSync(process.cwd());
7 | function resolveApp(relativePath) {
8 | return path.resolve(appDirectory, relativePath);
9 | }
10 |
11 | // We support resolving modules according to `NODE_PATH`.
12 | // This lets you use absolute paths in imports inside large monorepos:
13 | // https://github.com/facebookincubator/create-react-app/issues/253.
14 |
15 | // It works similar to `NODE_PATH` in Node itself:
16 | // https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders
17 |
18 | // We will export `nodePaths` as an array of absolute paths.
19 | // It will then be used by Webpack configs.
20 | // Jest doesn’t need this because it already handles `NODE_PATH` out of the box.
21 |
22 | // Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored.
23 | // Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims.
24 | // https://github.com/facebookincubator/create-react-app/issues/1023#issuecomment-265344421
25 |
26 | var nodePaths = (process.env.NODE_PATH || '')
27 | .split(process.platform === 'win32' ? ';' : ':')
28 | .filter(Boolean)
29 | .filter(folder => !path.isAbsolute(folder))
30 | .map(resolveApp);
31 |
32 | // config after eject: we're in ./config/
33 | module.exports = {
34 | appBuild: resolveApp('build'),
35 | appPublic: resolveApp('public'),
36 | appHtml: resolveApp('public/index.html'),
37 | appIndexJs: resolveApp('src/index.js'),
38 | appPackageJson: resolveApp('package.json'),
39 | appSrc: resolveApp('src'),
40 | yarnLockFile: resolveApp('yarn.lock'),
41 | testsSetup: resolveApp('src/setupTests.js'),
42 | appNodeModules: resolveApp('node_modules'),
43 | ownNodeModules: resolveApp('node_modules'),
44 | nodePaths: nodePaths,
45 | appComponent: resolveApp('dist'),
46 | appComponentIndexJs: resolveApp('src/LuckyDraw/index.js'),
47 | };
48 |
--------------------------------------------------------------------------------
/src/LuckyDraw/Arc.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { arc } from 'd3';
3 |
4 | class Arc extends Component {
5 | static propTypes = {
6 | text: React.PropTypes.oneOfType([
7 | React.PropTypes.string,
8 | React.PropTypes.number
9 | ]),
10 | startAngle: React.PropTypes.number,
11 | endAngle: React.PropTypes.number,
12 | innerRadius: React.PropTypes.number,
13 | outerRadius: React.PropTypes.number,
14 | labelTextRotate: React.PropTypes.number,
15 | stoke: React.PropTypes.number,
16 | showInnerLabels: React.PropTypes.bool,
17 | fontColor: React.PropTypes.string,
18 | fontSize: React.PropTypes.string,
19 | fontFamily: React.PropTypes.string,
20 | writingModel: React.PropTypes.string
21 | };
22 |
23 | renderInnerLabel(props, Arc) {
24 | let midAngle = props.endAngle < Math.PI
25 | ? props.startAngle / 2 + props.endAngle / 2
26 | : props.startAngle / 2 + props.endAngle / 2 + Math.PI;
27 | let textAngle = midAngle * 180 / Math.PI > 180
28 | ? midAngle * 180 / Math.PI - 180
29 | : midAngle * 180 / Math.PI;
30 | return (
31 |
43 | {props.text}
44 |
45 | );
46 | }
47 |
48 | render() {
49 | const props = this.props;
50 | const Arc = arc()
51 | .innerRadius(props.innerRadius)
52 | .outerRadius(props.outerRadius)
53 | .startAngle(props.startAngle)
54 | .endAngle(props.endAngle);
55 |
56 | return (
57 |
58 |
59 | {props.showInnerLabels ? this.renderInnerLabel(props, Arc) : null}
60 |
61 | );
62 | }
63 | }
64 |
65 | export default Arc;
66 |
--------------------------------------------------------------------------------
/src/LuckyDraw/Wheel.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { schemeCategory20, pie } from 'd3';
3 | import Arc from './Arc';
4 |
5 | class Wheel extends Component {
6 | static propTypes = {
7 | wheelSize: React.PropTypes.number.isRequired,
8 | range: React.PropTypes.number.isRequired,
9 | innerRadius: React.PropTypes.number,
10 | outerRadius: React.PropTypes.number,
11 | stoke: React.PropTypes.number,
12 | showInnerLabels: React.PropTypes.bool,
13 | textArray: React.PropTypes.array,
14 | fontColor: React.PropTypes.string,
15 | fontSize: React.PropTypes.string,
16 | writingModel: React.PropTypes.string
17 | };
18 | static defaultProps = {};
19 |
20 | _processData(range) {
21 | let array = [];
22 | for (var i = 0; i < range; i++) {
23 | array.push(100 / range);
24 | }
25 | return array;
26 | }
27 |
28 | render() {
29 | const props = this.props;
30 | const transform = `translate(${props.wheelSize / 2},${props.wheelSize / 2}) rotate(-${180 / props.range})`;
31 | const data = this._processData(props.range);
32 | const arcs = pie()(data).sort();
33 | const Pie = arcs.map((i, idx) => {
34 | let colorIdx = idx > 19 ? idx % 20 : idx;
35 | const textLabel = !props.ArabicLabel
36 | ? props.textArray[idx] ? props.textArray[idx] : idx + 1
37 | : idx + 1;
38 |
39 | return (
40 |
54 | );
55 | });
56 | return (
57 |
62 | );
63 | }
64 | }
65 |
66 | export default Wheel;
67 |
--------------------------------------------------------------------------------
/src/LuckyDraw/Style/LuckyDraw.styl:
--------------------------------------------------------------------------------
1 | backlight($x, $y, $spread, $size, $colorA, $colorB, $duration)
2 | &:after
3 | position absolute
4 | content ""
5 | top $y
6 | left $x
7 | right 0
8 | z-index 1
9 | height 100%
10 | width 100%
11 | margin 0 auto
12 | transform scale($size)
13 | filter blur($spread)
14 | background linear-gradient(270deg, $colorA, $colorB)
15 | background-size 200% 200%
16 | animation animateGlow $duration ease infinite
17 |
18 | .compass__container
19 | width 100%
20 | height 100%
21 | display inline-block
22 | animation textColor 10s ease infinite
23 | position relative
24 | box-shadow 0px 10px 50px rgba(0, 0, 0, 0.85)
25 | backlight 0, 5vw, 5vw, 0.85, #0fffc1, #7e0fff, 5s
26 | &.rolling:after
27 | animation none
28 |
29 | .compass__arrow
30 | position absolute
31 | height 50px
32 | width 100%
33 | top 0
34 | z-index 10
35 | text-align center
36 | &:before
37 | content: "";
38 | top 200%
39 | width 0
40 | height: 0
41 | border-left 20px solid transparent
42 | border-right 20px solid transparent
43 | border-top 100px solid lighten(darkred, 40%);
44 | position relative
45 | z-index 10
46 |
47 | //&:before
48 |
49 | .control__panel
50 | position relative
51 | height calc(100% - 74px)
52 | width calc(100% - 24px)
53 | margin 0 auto
54 | padding-top 50px
55 | overflow hidden
56 | border 12px solid darkorange
57 | display: flex
58 | flex-wrap nowrap
59 | flex-direction column
60 | align-items center
61 | justify-content flex-start
62 | background-color darken(#fff, 4%);
63 | z-index 2
64 | box-shadow inset 0px 2px 40px rgba(0, 0, 0, 0.45);
65 |
66 | .compass__spin
67 | width 100%
68 | height 100%
69 | z-index 2
70 | transition-property transform
71 | transition-timing-function ease-out
72 | animation-fill-mode forwards
73 |
74 | .compass__btn
75 | position absolute
76 | text-align center
77 | bottom -50px
78 | left 0
79 | z-index 100
80 | display inline-block
81 | width 100%
82 | .bttn-jelly.bttn-danger
83 | font-size 16px
84 | padding 10px 20px
85 |
86 | @keyframes animateGlow
87 | 0%
88 | background-position: 0% 50%
89 |
90 | 50%
91 | background-position: 100% 50%
92 | 100%
93 | background-position: 0% 50%
94 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | #react-luckydraw
2 |
3 | [](https://greenkeeper.io/)
4 | 
5 |
6 |
7 |
8 | ## Installation
9 |
10 | ```js
11 | $ npm install --save react-luckydraw
12 | ```
13 |
14 | ## Usage
15 | ``` javascript
16 | import React from 'react';
17 | import LuckyDraw from 'react-luckydraw';
18 |
19 | // include styles
20 | import 'react-luckydraw/lib/LuckyDraw.css';
21 |
22 | class App extends React.Component {
23 |
24 | constructor(props) {
25 | super(props);
26 | }
27 |
28 | render() {
29 | return (
30 |
31 | {
49 | console.log(drawNumber)
50 | }}
51 | onOutLimitAlert={(limit) => {
52 | if (limit) {
53 | window.alert('out of limits')
54 | }
55 | }}
56 | />
57 |
58 | )
59 | }
60 | }
61 | ```
62 |
63 | ## Props
64 |
65 | Property|Type|Default|Description
66 | ---|---|---|---
67 | width|number|500|width of luckydraw
68 | height|number|350|height of luckydraw
69 | wheelSize|number|width * 2|size of luckydraw Wheel
70 | range|number|30|range of luckydraw
71 | outerRadius|number|wheelSize/2|outerRadius of luckydraw Wheel
72 | innerRadius|number|outerRadius/2|innerRadius of luckydraw Wheel
73 | turns|number|3| Defines how many revolutions to rotate
74 | rotateSecond|number|5| Define how many seconds to turn around
75 | showInnerLabels|boolean|true|show Labels on luckydraw Wheel
76 | drawLimitSwitch|boolean|true|a switch of drawing wheel limit
77 | drawLimit|number|5|a limit for drawing times with wheel
78 | fontColor|string|'black'|set color for label on wheel
79 | fontSize|string|'18px'|set size for label on wheel
80 | drawButtonLabel|string|'start'|set text for drawing button
81 | textArray|array| - |set text array for wheel label
82 | onSuccessDrawReturn|function| - |callback function for draw success with the draw number
83 | onOutLimitAlert|function| - |callback function for limit times out
84 |
85 |
86 |
--------------------------------------------------------------------------------
/lib/LuckyDraw.css:
--------------------------------------------------------------------------------
1 | .compass__container {
2 | width: 100%;
3 | height: 100%;
4 | display: inline-block;
5 | animation: textColor 10s ease infinite;
6 | position: relative;
7 | box-shadow: 0px 10px 50px rgba(0,0,0,0.85);
8 | }
9 | .compass__container:after {
10 | position: absolute;
11 | content: "";
12 | top: 5vw;
13 | left: 0;
14 | right: 0;
15 | z-index: 1;
16 | height: 100%;
17 | width: 100%;
18 | margin: 0 auto;
19 | transform: scale(0.85);
20 | filter: blur(5vw);
21 | background: linear-gradient(270deg, #0fffc1, #7e0fff);
22 | background-size: 200% 200%;
23 | animation: animateGlow 5s ease infinite;
24 | }
25 | .compass__container.rolling:after {
26 | animation: none;
27 | }
28 | .compass__arrow {
29 | position: absolute;
30 | height: 50px;
31 | width: 100%;
32 | top: 0;
33 | left:0;
34 | z-index: 10;
35 | text-align: center;
36 | }
37 | .compass__arrow:before {
38 | content: "";
39 | top: 200%;
40 | width: 0;
41 | height: 0;
42 | border-left: 20px solid transparent;
43 | border-right: 20px solid transparent;
44 | border-top: 100px solid #ff2020;
45 | position: relative;
46 | z-index: 10;
47 | }
48 | .control__panel {
49 | position: relative;
50 | height: calc(100% - 74px);
51 | width: calc(100% - 24px);
52 | margin: 0 auto;
53 | padding-top: 50px;
54 | overflow: hidden;
55 | border: 12px solid #ff8c00;
56 | display: flex;
57 | flex-wrap: nowrap;
58 | flex-direction: column;
59 | align-items: center;
60 | justify-content: flex-start;
61 | background-color: #f5f5f5;
62 | z-index: 2;
63 | box-shadow: inset 0px 2px 40px rgba(0,0,0,0.45);
64 | }
65 | .compass__spin {
66 | width: 100%;
67 | height: 100%;
68 | z-index: 2;
69 | transition-property: transform;
70 | transition-timing-function: ease-out;
71 | animation-fill-mode: forwards;
72 | }
73 | .compass__btn {
74 | position: absolute;
75 | text-align: center;
76 | bottom: -50px;
77 | left: 0;
78 | z-index: 100;
79 | display: inline-block;
80 | width: 100%;
81 | }
82 | .compass__btn .bttn-jelly.bttn-danger {
83 | font-size: 16px;
84 | padding: 10px 20px;
85 | }
86 | @-moz-keyframes animateGlow {
87 | 0% {
88 | background-position: 0% 50%;
89 | }
90 | 50% {
91 | background-position: 100% 50%;
92 | }
93 | 100% {
94 | background-position: 0% 50%;
95 | }
96 | }
97 | @-webkit-keyframes animateGlow {
98 | 0% {
99 | background-position: 0% 50%;
100 | }
101 | 50% {
102 | background-position: 100% 50%;
103 | }
104 | 100% {
105 | background-position: 0% 50%;
106 | }
107 | }
108 | @-o-keyframes animateGlow {
109 | 0% {
110 | background-position: 0% 50%;
111 | }
112 | 50% {
113 | background-position: 100% 50%;
114 | }
115 | 100% {
116 | background-position: 0% 50%;
117 | }
118 | }
119 | @keyframes animateGlow {
120 | 0% {
121 | background-position: 0% 50%;
122 | }
123 | 50% {
124 | background-position: 100% 50%;
125 | }
126 | 100% {
127 | background-position: 0% 50%;
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-luckydraw",
3 | "version": "0.1.6",
4 | "author": "Gemer Cheung ",
5 | "description": "Simple luckydraw component for React.",
6 | "main": "lib/index.js",
7 | "homepage": "http://blog.gemer.xyz/react-luckydraw",
8 | "repository": {
9 | "type": "git",
10 | "url": "git+https://github.com/Gemerz/react-luckydraw.git"
11 | },
12 | "keywords": [
13 | "react",
14 | "luckydraw"
15 | ],
16 | "license": "ISC",
17 | "prepublish": "npm run build-lib && npm run build-css",
18 | "devDependencies": {
19 | "autoprefixer": "7.1.6",
20 | "babel-core": "6.26.0",
21 | "babel-eslint": "8.0.2",
22 | "babel-jest": "21.2.0",
23 | "babel-loader": "7.1.2",
24 | "babel-preset-react-app": "^3.1.0",
25 | "case-sensitive-paths-webpack-plugin": "2.1.1",
26 | "chalk": "2.3.0",
27 | "connect-history-api-fallback": "1.5.0",
28 | "cross-spawn": "5.1.0",
29 | "css-loader": "0.28.7",
30 | "detect-port": "1.2.2",
31 | "dotenv": "4.0.0",
32 | "eslint": "4.11.0",
33 | "eslint-config-react-app": "^2.0.1",
34 | "eslint-loader": "1.9.0",
35 | "eslint-plugin-flowtype": "2.39.1",
36 | "eslint-plugin-import": "2.8.0",
37 | "eslint-plugin-jsx-a11y": "6.0.2",
38 | "eslint-plugin-react": "7.4.0",
39 | "extract-text-webpack-plugin": "3.0.2",
40 | "file-loader": "1.1.5",
41 | "filesize": "3.5.11",
42 | "fs-extra": "4.0.2",
43 | "gh-pages": "^1.1.0",
44 | "gzip-size": "4.0.0",
45 | "highlight.js": "^9.9.0",
46 | "html-webpack-plugin": "2.30.1",
47 | "http-proxy-middleware": "0.17.4",
48 | "jest": "21.2.1",
49 | "json-loader": "0.5.7",
50 | "object-assign": "4.1.1",
51 | "path-exists": "3.0.0",
52 | "postcss-loader": "2.0.8",
53 | "prettier": "^1.8.2",
54 | "promise": "8.0.1",
55 | "react-dev-utils": "^4.2.1",
56 | "react-highlight": "^0.10.0",
57 | "recursive-readdir": "2.2.1",
58 | "strip-ansi": "4.0.0",
59 | "style-loader": "0.19.0",
60 | "stylus": "^0.54.5",
61 | "stylus-loader": "^3.0.1",
62 | "sw-precache": "^5.0.0",
63 | "url-loader": "0.6.2",
64 | "webpack": "3.8.1",
65 | "webpack-dev-server": "2.9.4",
66 | "webpack-manifest-plugin": "1.3.2",
67 | "whatwg-fetch": "2.0.3"
68 | },
69 | "dependencies": {
70 | "d3": "^4.4.0",
71 | "react": "^16.1.1",
72 | "react-dom": "^16.1.1",
73 | "react-tabs": "^2.1.1",
74 | "styled-components": "^2.2.3",
75 | "sweetalert": "^2.0.8"
76 | },
77 | "scripts": {
78 | "start": "node scripts/start.js",
79 | "build": "node scripts/build.js && npm run gen-service-worker",
80 | "format": "prettier --write 'src/**/*.{js,jsx}' '*.{js,jsx}' 'cosmos/**/*.{js,jsx}' --single-quote",
81 | "test": "node scripts/test.js --env=jsdom",
82 | "gen-service-worker": "sw-precache --config=sw-precache-config.js",
83 | "build-lib": "BABEL_ENV=production babel src/LuckyDraw -d lib --ignore spec.js,test.js",
84 | "build-css": "./node_modules/stylus/bin/stylus -w src/LuckyDraw/Style -o lib/LuckyDraw.css",
85 | "deploy": "gh-pages -d build"
86 | },
87 | "jest": {
88 | "collectCoverageFrom": [
89 | "src/**/*.{js,jsx}"
90 | ],
91 | "setupFiles": [
92 | "/config/polyfills.js"
93 | ],
94 | "testPathIgnorePatterns": [
95 | "[/\\\\](build|docs|node_modules)[/\\\\]"
96 | ],
97 | "testEnvironment": "node",
98 | "testURL": "http://localhost",
99 | "transform": {
100 | "^.+\\.(js|jsx)$": "/node_modules/babel-jest",
101 | "^.+\\.css$": "/config/jest/cssTransform.js",
102 | "^(?!.*\\.(js|jsx|css|json)$)": "/config/jest/fileTransform.js"
103 | },
104 | "transformIgnorePatterns": [
105 | "[/\\\\]node_modules[/\\\\].+\\.(js|jsx)$"
106 | ],
107 | "moduleNameMapper": {
108 | "^react-native$": "react-native-web"
109 | }
110 | },
111 | "babel": {
112 | "presets": [
113 | "react-app"
114 | ]
115 | },
116 | "eslintConfig": {
117 | "extends": "react-app"
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/src/btn.css:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 | /*!
3 | *
4 | * bttn.css - https://ganapativs.github.io/bttn.css
5 | * Version - 0.2.4
6 | * Demo: https://bttn.surge.sh
7 | *
8 | * Licensed under the MIT license - http://opensource.org/licenses/MIT
9 | *
10 | * Copyright (c) 2016 Ganapati V S (@ganapativs)
11 | *
12 | */
13 | /* standalone - .bttn-jelly */
14 | .bttn-default {
15 | color: #fff;
16 | }
17 | .bttn-primary,
18 | .bttn,
19 | .bttn-lg,
20 | .bttn-md,
21 | .bttn-sm,
22 | .bttn-xs {
23 | color: #1d89ff;
24 | }
25 | .bttn-warning {
26 | color: #feab3a;
27 | }
28 | .bttn-danger {
29 | color: #ff5964;
30 | }
31 | .bttn-success {
32 | color: #28b78d;
33 | }
34 | .bttn-royal {
35 | color: #bd2df5;
36 | }
37 | .bttn,
38 | .bttn-lg,
39 | .bttn-md,
40 | .bttn-sm,
41 | .bttn-xs {
42 | margin: 0;
43 | padding: 0;
44 | border-width: 0;
45 | border-color: transparent;
46 | background: transparent;
47 | font-weight: 400;
48 | cursor: pointer;
49 | position: relative;
50 | }
51 | .bttn-lg {
52 | padding: 8px 15px;
53 | font-size: 24px;
54 | font-family: inherit;
55 | }
56 | .bttn-md {
57 | font-size: 20px;
58 | font-family: inherit;
59 | padding: 5px 12px;
60 | }
61 | .bttn-sm {
62 | padding: 4px 10px;
63 | font-size: 16px;
64 | font-family: inherit;
65 | }
66 | .bttn-xs {
67 | padding: 3px 8px;
68 | font-size: 12px;
69 | font-family: inherit;
70 | }
71 | .bttn-jelly {
72 | margin: 0;
73 | padding: 0;
74 | border-width: 0;
75 | border-color: transparent;
76 | background: transparent;
77 | font-weight: 400;
78 | cursor: pointer;
79 | position: relative;
80 | font-size: 20px;
81 | font-family: inherit;
82 | padding: 5px 12px;
83 | overflow: hidden;
84 | border-radius: 50px;
85 | background: #fff;
86 | color: #1d89ff;
87 | -webkit-transition: all 0.2s cubic-bezier(0.02, 0.01, 0.47, 1);
88 | transition: all 0.2s cubic-bezier(0.02, 0.01, 0.47, 1);
89 | }
90 | .bttn-jelly:before {
91 | position: absolute;
92 | top: 0;
93 | left: 0;
94 | width: 100%;
95 | height: 100%;
96 | border-radius: 50px;
97 | background: currentColor;
98 | content: '';
99 | z-index: -1;
100 | opacity: 0;
101 | -webkit-transition: all 0.2s cubic-bezier(0.02, 0.01, 0.47, 1);
102 | transition: all 0.2s cubic-bezier(0.02, 0.01, 0.47, 1);
103 | -webkit-transform: scale(0.2);
104 | transform: scale(0.2);
105 | }
106 | .bttn-jelly:hover,
107 | .bttn-jelly:focus {
108 | box-shadow: 0 1px 8px rgba(58,51,53,0.4);
109 | -webkit-transition: all 0.3s cubic-bezier(0.02, 0.01, 0.47, 1);
110 | transition: all 0.3s cubic-bezier(0.02, 0.01, 0.47, 1);
111 | -webkit-transform: scale(1.1);
112 | transform: scale(1.1);
113 | }
114 | .bttn-jelly:hover:before,
115 | .bttn-jelly:focus:before {
116 | opacity: 0.15;
117 | -webkit-transition: all 0.3s cubic-bezier(0.02, 0.01, 0.47, 1);
118 | transition: all 0.3s cubic-bezier(0.02, 0.01, 0.47, 1);
119 | -webkit-transform: scale(1);
120 | transform: scale(1);
121 | }
122 | .bttn-jelly.bttn-xs {
123 | padding: 3px 8px;
124 | font-size: 12px;
125 | font-family: inherit;
126 | }
127 | .bttn-jelly.bttn-xs:hover,
128 | .bttn-jelly.bttn-xs:focus {
129 | box-shadow: 0 1px 4px rgba(58,51,53,0.4);
130 | }
131 | .bttn-jelly.bttn-sm {
132 | padding: 4px 10px;
133 | font-size: 16px;
134 | font-family: inherit;
135 | }
136 | .bttn-jelly.bttn-sm:hover,
137 | .bttn-jelly.bttn-sm:focus {
138 | box-shadow: 0 1px 6px rgba(58,51,53,0.4);
139 | }
140 | .bttn-jelly.bttn-md {
141 | font-size: 20px;
142 | font-family: inherit;
143 | padding: 5px 12px;
144 | }
145 | .bttn-jelly.bttn-md:hover,
146 | .bttn-jelly.bttn-md:focus {
147 | box-shadow: 0 1px 8px rgba(58,51,53,0.4);
148 | }
149 | .bttn-jelly.bttn-lg {
150 | padding: 8px 15px;
151 | font-size: 24px;
152 | font-family: inherit;
153 | }
154 | .bttn-jelly.bttn-lg:hover,
155 | .bttn-jelly.bttn-lg:focus {
156 | box-shadow: 0 1px 10px rgba(58,51,53,0.4);
157 | }
158 | .bttn-jelly.bttn-default {
159 | background: #fff;
160 | color: #1d89ff;
161 | }
162 | .bttn-jelly.bttn-primary {
163 | background: #1d89ff;
164 | color: #fff;
165 | }
166 | .bttn-jelly.bttn-warning {
167 | background: #feab3a;
168 | color: #fff;
169 | }
170 | .bttn-jelly.bttn-danger {
171 | background: #ff5964;
172 | color: #fff;
173 | }
174 | .bttn-jelly.bttn-success {
175 | background: #28b78d;
176 | color: #fff;
177 | }
178 | .bttn-jelly.bttn-royal {
179 | background: #bd2df5;
180 | color: #fff;
181 | }
--------------------------------------------------------------------------------
/lib/Arc.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
8 |
9 | var _react = require("react");
10 |
11 | var _react2 = _interopRequireDefault(_react);
12 |
13 | var _d = require("d3");
14 |
15 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
16 |
17 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
18 |
19 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
20 |
21 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
22 |
23 | var Arc = function (_Component) {
24 | _inherits(Arc, _Component);
25 |
26 | function Arc() {
27 | _classCallCheck(this, Arc);
28 |
29 | return _possibleConstructorReturn(this, (Arc.__proto__ || Object.getPrototypeOf(Arc)).apply(this, arguments));
30 | }
31 |
32 | _createClass(Arc, [{
33 | key: "renderInnerLabel",
34 | value: function renderInnerLabel(props, Arc) {
35 | var midAngle = props.endAngle < Math.PI ? props.startAngle / 2 + props.endAngle / 2 : props.startAngle / 2 + props.endAngle / 2 + Math.PI;
36 | var textAngle = midAngle * 180 / Math.PI > 180 ? midAngle * 180 / Math.PI - 180 : midAngle * 180 / Math.PI;
37 | return _react2.default.createElement(
38 | "text",
39 | {
40 | className: "rld-value",
41 | transform: "translate(" + Arc.centroid() + ") rotate(" + textAngle + ")",
42 | dy: ".45em",
43 | style: {
44 | shapeRendering: 'crispEdges',
45 | textAnchor: 'middle',
46 | writingMode: props.writingModel,
47 | fontSize: props.fontSize,
48 | fill: props.fontColor
49 | }
50 | },
51 | props.text
52 | );
53 | }
54 | }, {
55 | key: "render",
56 | value: function render() {
57 | var props = this.props;
58 | var Arc = (0, _d.arc)().innerRadius(props.innerRadius).outerRadius(props.outerRadius).startAngle(props.startAngle).endAngle(props.endAngle);
59 |
60 | return _react2.default.createElement(
61 | "g",
62 | { className: "rld-compass" },
63 | _react2.default.createElement("path", { d: Arc(),
64 | fill: props.fill,
65 | stroke: props.stoke
66 | }),
67 | props.showInnerLabels ? this.renderInnerLabel(props, Arc) : null
68 | );
69 | }
70 | }]);
71 |
72 | return Arc;
73 | }(_react.Component);
74 |
75 | Arc.propTypes = {
76 | text: _react2.default.PropTypes.string,
77 | startAngle: _react2.default.PropTypes.number,
78 | endAngle: _react2.default.PropTypes.number,
79 | innerRadius: _react2.default.PropTypes.number,
80 | outerRadius: _react2.default.PropTypes.number,
81 | labelTextRotate: _react2.default.PropTypes.number,
82 | stoke: _react2.default.PropTypes.number,
83 | showInnerLabels: _react2.default.PropTypes.bool,
84 | fontColor: _react2.default.PropTypes.string,
85 | fontSize: _react2.default.PropTypes.string,
86 | fontFamily: _react2.default.PropTypes.string,
87 | writingModel: _react2.default.PropTypes.string
88 | };
89 | exports.default = Arc;
--------------------------------------------------------------------------------
/lib/Wheel.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
8 |
9 | var _react = require("react");
10 |
11 | var _react2 = _interopRequireDefault(_react);
12 |
13 | var _d = require("d3");
14 |
15 | var _Arc = require("./Arc");
16 |
17 | var _Arc2 = _interopRequireDefault(_Arc);
18 |
19 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
20 |
21 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
22 |
23 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
24 |
25 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
26 |
27 | var Wheel = function (_Component) {
28 | _inherits(Wheel, _Component);
29 |
30 | function Wheel() {
31 | _classCallCheck(this, Wheel);
32 |
33 | return _possibleConstructorReturn(this, (Wheel.__proto__ || Object.getPrototypeOf(Wheel)).apply(this, arguments));
34 | }
35 |
36 | _createClass(Wheel, [{
37 | key: "_processData",
38 | value: function _processData(range) {
39 | var array = [];
40 | for (var i = 0; i < range; i++) {
41 | array.push(100 / range);
42 | }
43 | return array;
44 | }
45 | }, {
46 | key: "render",
47 | value: function render() {
48 |
49 | var props = this.props;
50 | var transform = "translate(" + props.wheelSize / 2 + "," + props.wheelSize / 2 + ") rotate(-" + 180 / props.range + ")";
51 | var data = this._processData(props.range);
52 | var arcs = (0, _d.pie)()(data).sort(null);
53 | var Pie = arcs.map(function (i, idx) {
54 | var colorIdx = idx > 19 ? idx % 20 : idx;
55 | return _react2.default.createElement(_Arc2.default, { key: idx,
56 | innerRadius: props.innerRadius,
57 | outerRadius: props.outerRadius,
58 | startAngle: i.startAngle,
59 | endAngle: i.endAngle,
60 | showInnerLabels: props.showInnerLabels,
61 | text: props.textArray[idx],
62 | fill: _d.schemeCategory20[colorIdx],
63 | stoke: props.stoke,
64 | fontColor: props.fontColor,
65 | fontSize: props.fontSize,
66 | writingModel: props.writingModel
67 | });
68 | });
69 | return _react2.default.createElement(
70 | "svg",
71 | { width: props.wheelSize, height: props.wheelSize },
72 | _react2.default.createElement(
73 | "g",
74 | { transform: transform },
75 | Pie
76 | )
77 | );
78 | }
79 | }]);
80 |
81 | return Wheel;
82 | }(_react.Component);
83 |
84 | Wheel.propTypes = {
85 | wheelSize: _react2.default.PropTypes.number.isRequired,
86 | range: _react2.default.PropTypes.number.isRequired,
87 | innerRadius: _react2.default.PropTypes.number,
88 | outerRadius: _react2.default.PropTypes.number,
89 | stoke: _react2.default.PropTypes.number,
90 | showInnerLabels: _react2.default.PropTypes.bool,
91 | textArray: _react2.default.PropTypes.array,
92 | fontColor: _react2.default.PropTypes.string,
93 | fontSize: _react2.default.PropTypes.string,
94 | writingModel: _react2.default.PropTypes.string
95 |
96 | };
97 | Wheel.defaultProps = {};
98 | exports.default = Wheel;
--------------------------------------------------------------------------------
/src/LuckyDraw/LuckyDraw.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import Wheel from './Wheel';
3 |
4 | class LuckyDraw extends Component {
5 | static propTypes = {
6 | width: React.PropTypes.number.isRequired,
7 | height: React.PropTypes.number.isRequired,
8 | range: React.PropTypes.number.isRequired,
9 | wheelSize: React.PropTypes.number,
10 | turns: React.PropTypes.number,
11 | innerRadius: React.PropTypes.number,
12 | outerRadius: React.PropTypes.number,
13 | stoke: React.PropTypes.number,
14 | showInnerLabels: React.PropTypes.bool,
15 | drawLimitSwitch: React.PropTypes.bool,
16 | drawLimit: React.PropTypes.number,
17 | textArray: React.PropTypes.array,
18 | fontColor: React.PropTypes.string,
19 | fontSize: React.PropTypes.string,
20 | writingModel: React.PropTypes.string,
21 | drawButtonLabel: React.PropTypes.string,
22 | ArabicLabel: React.PropTypes.bool,
23 | onSuccessDrawReturn: React.PropTypes.func,
24 | onOutLimitAlert: React.PropTypes.func
25 | };
26 | static defaultProps = {
27 | width: 500,
28 | height: 350,
29 | stoke: 20,
30 | range: 20,
31 | turns: 3,
32 | rotateSecond: 5,
33 | drawLimit: 3,
34 | drawLimitSwitch: false,
35 | fontColor: '#000',
36 | fontSize: '18px',
37 | writingModel: 'tb',
38 | drawButtonLabel: 'Start',
39 | ArabicLabel: false,
40 | textArray: []
41 | };
42 |
43 | constructor(props) {
44 | super(props);
45 | if (!('wheelSize' in props)) {
46 | this.props.wheelSize = this.defaultProps.width * 2;
47 | }
48 | this.state = {
49 | startDraw: false,
50 | drawTimes: 1,
51 | randomNumber: null,
52 | rolling: false
53 | };
54 | }
55 |
56 | // shouldComponentUpdate(nextProps, nextState) {
57 | // return true
58 | // }
59 |
60 | _processRandomNumber(min, max) {
61 | return Math.floor(Math.random() * (max - min + 1)) + min;
62 | }
63 |
64 | _processDrawAngle(range, turns, drawTimes, drawNumber) {
65 | const peer = 360 / range;
66 | const totalAngle = 360 * turns * drawTimes + drawNumber * peer;
67 | return totalAngle;
68 | }
69 |
70 | _processDrawing(e) {
71 | e.preventDefault();
72 | if (!this.state.rolling) {
73 | let drawTime = this.state.drawTimes;
74 | if (this.props.drawLimitSwitch && drawTime - 1 < this.props.drawLimit) {
75 | this.setState({
76 | startDraw: true,
77 | rolling: true,
78 | randomNumber: this._processRandomNumber(0, this.props.range - 1),
79 | drawTimes: this.state.drawTimes + 1
80 | });
81 | setTimeout(
82 | () => {
83 | this.setState({
84 | rolling: false
85 | });
86 | this.props.onSuccessDrawReturn(this.state.randomNumber);
87 | },
88 | this.props.rotateSecond * 1000
89 | );
90 | } else {
91 | this.props.onOutLimitAlert(true);
92 | }
93 | }
94 | }
95 |
96 | render() {
97 | const state = this.state;
98 | const props = this.props;
99 | let transformRotate = state.startDraw
100 | ? this._processDrawAngle(
101 | props.range,
102 | props.turns,
103 | state.drawTimes,
104 | state.randomNumber
105 | )
106 | : 0;
107 | return (
108 |
115 |
120 |
121 |
122 |
131 |
132 |
133 |
134 |
135 |
143 |
144 |
145 |
146 | );
147 | }
148 | }
149 |
150 | export default LuckyDraw;
151 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Tab, Tabs, TabList, TabPanel } from 'react-tabs';
3 | import sweetAlert from 'sweetalert';
4 | import LuckyDraw from './LuckyDraw';
5 | import 'sweetalert/dist/sweetalert.css';
6 | import 'highlight.js/styles/googlecode.css';
7 | import './App.styl';
8 | import '../lib/LuckyDraw.css';
9 | import './btn.css';
10 | // import Highlight from 'react-highlight';
11 |
12 |
13 | class App extends Component {
14 | constructor(porps) {
15 | super(porps);
16 | this.state = {
17 | currentTab: 0
18 | };
19 | }
20 |
21 | changeTab = function (key) {
22 | console.log(key);
23 | // this.setState({currentTab: key})
24 | };
25 |
26 | render() {
27 | const example2 = [
28 | 'Hello',
29 | 'world',
30 | 'php',
31 | 'is',
32 | 'best',
33 | 'language',
34 | 'in',
35 | 'the',
36 | ];
37 | const example3 = [
38 | 'javascript is best language',
39 | 'php is best language',
40 | 'python is best language',
41 | 'ruby is best language',
42 | 'go is best language',
43 | 'swift is best language',
44 | 'dart is best language',
45 | 'rust is best language',
46 | 'C is best language',
47 | 'C++ is best language',
48 | 'lua is best language',
49 | 'xml is best language',
50 | 'html is best language',
51 | 'Visual Basic .NET is best language',
52 | 'Delphi/Object Pascal is best language',
53 | 'C# is best language',
54 | 'Objective-C is best language',
55 | 'Assembly language is best language',
56 | 'MATLAB is best language',
57 | 'PL/SQL is best language',
58 | 'Scratch is best language',
59 | 'R is best language',
60 | 'Visual Basic is best language'
61 | ];
62 | const example4 = [
63 | 'Big Prize 1',
64 | 'Big Prize 2',
65 | 'Big Prize 3',
66 | 'Big Prize 4',
67 | 'Big Prize 5',
68 | 'Big Prize 6',
69 | 'Big Prize 7',
70 | 'Big Prize 8',
71 | 'Big Prize 9',
72 | 'Big Prize 10'
73 | ];
74 | return (
75 |
76 |
React-luckydraw
77 |
78 |
79 |
80 | based
81 | custom with charters
82 | vertical label
83 | effect with confetti
84 |
85 |
86 |
87 | based
88 | {
104 | sweetAlert('Got ' + (drawNumber + 1).toString(), 'Congratulations !', "success");
105 | }}
106 | onOutLimitAlert={limit => {
107 | if (limit) {
108 | sweetAlert("Oops...", "out of limits!!", "error");
109 | }
110 | }}
111 | />
112 |
113 |
114 |
115 | custom with charters
116 | {
132 | sweetAlert(example2[drawNumber], 'Congratulations !', "success");
133 |
134 | }}
135 | onOutLimitAlert={limit => {
136 | if (limit) {
137 | sweetAlert("Oops...", "out of limits!!", "error");
138 | }
139 | }}
140 | />
141 |
142 |
143 |
144 | vertical label
145 | {
161 | sweetAlert(example3[drawNumber], 'Congratulations !', "success");
162 |
163 | }}
164 | onOutLimitAlert={limit => {
165 | if (limit) {
166 | sweetAlert("Oops...", "out of limits!!", "error");
167 | }
168 | }}
169 | />
170 |
171 |
172 |
173 | Effect with confetti
174 |
175 | {
191 |
192 | sweetAlert(example4[drawNumber], 'Congratulations !', "success");
193 |
194 | }}
195 | onOutLimitAlert={limit => {
196 | if (limit) {
197 | sweetAlert("Oops...", "out of limits!!", "error");
198 | }
199 | }}
200 | />
201 |
202 |
203 |
204 |
205 | );
206 | }
207 | }
208 |
209 | export default App;
210 |
--------------------------------------------------------------------------------
/lib/LuckyDraw.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
8 |
9 | var _react = require("react");
10 |
11 | var _react2 = _interopRequireDefault(_react);
12 |
13 | var _Wheel = require("./Wheel");
14 |
15 | var _Wheel2 = _interopRequireDefault(_Wheel);
16 |
17 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
18 |
19 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
20 |
21 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
22 |
23 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
24 |
25 | var LuckyDraw = function (_Component) {
26 | _inherits(LuckyDraw, _Component);
27 |
28 | function LuckyDraw(props) {
29 | _classCallCheck(this, LuckyDraw);
30 |
31 | var _this = _possibleConstructorReturn(this, (LuckyDraw.__proto__ || Object.getPrototypeOf(LuckyDraw)).call(this, props));
32 |
33 | if (!('wheelSize' in props)) {
34 | _this.props.wheelSize = _this.defaultProps.width * 2;
35 | }
36 | _this.state = {
37 | startDraw: false,
38 | drawTimes: 1,
39 | randomNumber: null,
40 | rolling: false
41 | };
42 | return _this;
43 | }
44 |
45 | _createClass(LuckyDraw, [{
46 | key: "shouldComponentUpdate",
47 | value: function shouldComponentUpdate(nextProps, nextState) {
48 | return true;
49 | }
50 | }, {
51 | key: "_processRandomNumber",
52 | value: function _processRandomNumber(min, max) {
53 | return Math.floor(Math.random() * (max - min + 1)) + min;
54 | }
55 | }, {
56 | key: "_processDrawAngle",
57 | value: function _processDrawAngle(range, turns, drawTimes, drawNumber) {
58 | var peer = 360 / range;
59 | var totalAngle = 360 * turns * drawTimes + drawNumber * peer;
60 | return totalAngle;
61 | }
62 | }, {
63 | key: "_processDrawing",
64 | value: function _processDrawing(e) {
65 | var _this2 = this;
66 |
67 | e.preventDefault();
68 | if (!this.state.rolling) {
69 | var drawTime = this.state.drawTimes;
70 | if (this.props.drawLimitSwitch && drawTime - 1 < this.props.drawLimit) {
71 | this.setState({
72 | startDraw: true,
73 | rolling: true,
74 | randomNumber: this._processRandomNumber(0, this.props.range - 1),
75 | drawTimes: this.state.drawTimes + 1
76 | });
77 | setTimeout(function () {
78 | _this2.setState({
79 | rolling: false
80 | });
81 | _this2.props.onSuccessDrawReturn(_this2.state.randomNumber);
82 | }, this.props.rotateSecond * 1000);
83 | } else {
84 | this.props.onOutLimitAlert(true);
85 | }
86 | }
87 | }
88 | }, {
89 | key: "render",
90 | value: function render() {
91 | var _this3 = this;
92 |
93 | var state = this.state;
94 | var props = this.props;
95 | var transformRotate = state.startDraw ? this._processDrawAngle(props.range, props.turns, state.drawTimes, state.randomNumber) : 0;
96 | return _react2.default.createElement(
97 | "div",
98 | { className: "react_luckyDraw", style: {
99 | width: props.width,
100 | height: props.height
101 | } },
102 | _react2.default.createElement(
103 | "div",
104 | { className: state.rolling ? 'compass__container rolling' : 'compass__container' },
105 | _react2.default.createElement(
106 | "div",
107 | { className: "control__panel" },
108 | _react2.default.createElement("div", { className: "compass__arrow" }),
109 | _react2.default.createElement(
110 | "div",
111 | { className: "compass__spin",
112 | style: {
113 | width: props.wheelSize + "px",
114 | height: props.wheelSize + "px",
115 | transform: "rotate(" + -transformRotate + "deg) translate3d(0,0,-1px)",
116 | transitionDuration: props.rotateSecond + "s"
117 | } },
118 | _react2.default.createElement(_Wheel2.default, props)
119 | )
120 | ),
121 | _react2.default.createElement(
122 | "div",
123 | { className: "compass__btn" },
124 | _react2.default.createElement(
125 | "button",
126 | {
127 | className: "bttn-jelly bttn-md bttn-danger",
128 | onClick: function onClick(e) {
129 | _this3._processDrawing(e);
130 | } },
131 | props.drawButtonLabel
132 | )
133 | )
134 | )
135 | );
136 | }
137 | }]);
138 |
139 | return LuckyDraw;
140 | }(_react.Component);
141 |
142 | LuckyDraw.propTypes = {
143 | width: _react2.default.PropTypes.number.isRequired,
144 | height: _react2.default.PropTypes.number.isRequired,
145 | range: _react2.default.PropTypes.number.isRequired,
146 | wheelSize: _react2.default.PropTypes.number,
147 | turns: _react2.default.PropTypes.number,
148 | innerRadius: _react2.default.PropTypes.number,
149 | outerRadius: _react2.default.PropTypes.number,
150 | stoke: _react2.default.PropTypes.number,
151 | showInnerLabels: _react2.default.PropTypes.bool,
152 | drawLimitSwitch: _react2.default.PropTypes.bool,
153 | drawLimit: _react2.default.PropTypes.number,
154 | textArray: _react2.default.PropTypes.array,
155 | fontColor: _react2.default.PropTypes.string,
156 | fontSize: _react2.default.PropTypes.string,
157 | writingModel: _react2.default.PropTypes.string,
158 | drawButtonLabel: _react2.default.PropTypes.string,
159 | onSuccessDrawReturn: _react2.default.PropTypes.func,
160 | onOutLimitAlert: _react2.default.PropTypes.func
161 | };
162 | LuckyDraw.defaultProps = {
163 | width: 500,
164 | height: 350,
165 | stoke: 20,
166 | range: 20,
167 | turns: 3,
168 | rotateSecond: 5,
169 | drawLimit: 3,
170 | drawLimitSwitch: false,
171 | fontColor: '#000',
172 | fontSize: '18px',
173 | writingModel: 'tb',
174 | drawButtonLabel: 'Start'
175 | };
176 | exports.default = LuckyDraw;
--------------------------------------------------------------------------------
/scripts/build.js:
--------------------------------------------------------------------------------
1 | // Do this as the first thing so that any code reading it knows the right env.
2 | process.env.NODE_ENV = 'production';
3 |
4 | // Load environment variables from .env file. Suppress warnings using silent
5 | // if this file is missing. dotenv will never modify any environment variables
6 | // that have already been set.
7 | // https://github.com/motdotla/dotenv
8 | require('dotenv').config({silent: true});
9 |
10 | var chalk = require('chalk');
11 | var fs = require('fs-extra');
12 | var path = require('path');
13 | var pathExists = require('path-exists');
14 | var filesize = require('filesize');
15 | var gzipSize = require('gzip-size').sync;
16 | var webpack = require('webpack');
17 | var config = require('../config/webpack.config.prod');
18 | var paths = require('../config/paths');
19 | var checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
20 | var recursive = require('recursive-readdir');
21 | var stripAnsi = require('strip-ansi');
22 |
23 | var useYarn = pathExists.sync(paths.yarnLockFile);
24 |
25 | // Warn and crash if required files are missing
26 | if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
27 | process.exit(1);
28 | }
29 |
30 | // Input: /User/dan/app/build/static/js/main.82be8.js
31 | // Output: /static/js/main.js
32 | function removeFileNameHash(fileName) {
33 | return fileName
34 | .replace(paths.appBuild, '')
35 | .replace(/\/?(.*)(\.\w+)(\.js|\.css)/, (match, p1, p2, p3) => p1 + p3);
36 | }
37 |
38 | // Input: 1024, 2048
39 | // Output: "(+1 KB)"
40 | function getDifferenceLabel(currentSize, previousSize) {
41 | var FIFTY_KILOBYTES = 1024 * 50;
42 | var difference = currentSize - previousSize;
43 | var fileSize = !Number.isNaN(difference) ? filesize(difference) : 0;
44 | if (difference >= FIFTY_KILOBYTES) {
45 | return chalk.red('+' + fileSize);
46 | } else if (difference < FIFTY_KILOBYTES && difference > 0) {
47 | return chalk.yellow('+' + fileSize);
48 | } else if (difference < 0) {
49 | return chalk.green(fileSize);
50 | } else {
51 | return '';
52 | }
53 | }
54 |
55 | // First, read the current file sizes in build directory.
56 | // This lets us display how much they changed later.
57 | recursive(paths.appBuild, (err, fileNames) => {
58 | var previousSizeMap = (fileNames || [])
59 | .filter(fileName => /\.(js|css)$/.test(fileName))
60 | .reduce((memo, fileName) => {
61 | var contents = fs.readFileSync(fileName);
62 | var key = removeFileNameHash(fileName);
63 | memo[key] = gzipSize(contents);
64 | return memo;
65 | }, {});
66 |
67 | // Remove all content but keep the directory so that
68 | // if you're in it, you don't end up in Trash
69 | fs.emptyDirSync(paths.appBuild);
70 |
71 | // Start the webpack build
72 | build(previousSizeMap);
73 |
74 | // Merge with the public folder
75 | copyPublicFolder();
76 | });
77 |
78 | // Print a detailed summary of build files.
79 | function printFileSizes(stats, previousSizeMap) {
80 | var assets = stats.toJson().assets
81 | .filter(asset => /\.(js|css)$/.test(asset.name))
82 | .map(asset => {
83 | var fileContents = fs.readFileSync(paths.appBuild + '/' + asset.name);
84 | var size = gzipSize(fileContents);
85 | var previousSize = previousSizeMap[removeFileNameHash(asset.name)];
86 | var difference = getDifferenceLabel(size, previousSize);
87 | return {
88 | folder: path.join('build', path.dirname(asset.name)),
89 | name: path.basename(asset.name),
90 | size: size,
91 | sizeLabel: filesize(size) + (difference ? ' (' + difference + ')' : '')
92 | };
93 | });
94 | assets.sort((a, b) => b.size - a.size);
95 | var longestSizeLabelLength = Math.max.apply(null,
96 | assets.map(a => stripAnsi(a.sizeLabel).length)
97 | );
98 | assets.forEach(asset => {
99 | var sizeLabel = asset.sizeLabel;
100 | var sizeLength = stripAnsi(sizeLabel).length;
101 | if (sizeLength < longestSizeLabelLength) {
102 | var rightPadding = ' '.repeat(longestSizeLabelLength - sizeLength);
103 | sizeLabel += rightPadding;
104 | }
105 | console.log(
106 | ' ' + sizeLabel +
107 | ' ' + chalk.dim(asset.folder + path.sep) + chalk.cyan(asset.name)
108 | );
109 | });
110 | }
111 |
112 | // Print out errors
113 | function printErrors(summary, errors) {
114 | console.log(chalk.red(summary));
115 | console.log();
116 | errors.forEach(err => {
117 | console.log(err.message || err);
118 | console.log();
119 | });
120 | }
121 |
122 | // Create the production build and print the deployment instructions.
123 | function build(previousSizeMap) {
124 | console.log('Creating an optimized production build...');
125 | webpack(config).run((err, stats) => {
126 | if (err) {
127 | printErrors('Failed to compile.', [err]);
128 | process.exit(1);
129 | }
130 |
131 | if (stats.compilation.errors.length) {
132 | printErrors('Failed to compile.', stats.compilation.errors);
133 | process.exit(1);
134 | }
135 |
136 | if (process.env.CI && stats.compilation.warnings.length) {
137 | printErrors('Failed to compile.', stats.compilation.warnings);
138 | process.exit(1);
139 | }
140 |
141 | console.log(chalk.green('Compiled successfully.'));
142 | console.log();
143 |
144 | console.log('File sizes after gzip:');
145 | console.log();
146 | printFileSizes(stats, previousSizeMap);
147 | console.log();
148 |
149 | var openCommand = process.platform === 'win32' ? 'start' : 'open';
150 | var appPackage = require(paths.appPackageJson);
151 | var homepagePath = appPackage.homepage;
152 | var publicPath = config.output.publicPath;
153 | if (homepagePath && homepagePath.indexOf('.github.io/') !== -1) {
154 | // "homepage": "http://user.github.io/project"
155 | console.log('The project was built assuming it is hosted at ' + chalk.green(publicPath) + '.');
156 | console.log('You can control this with the ' + chalk.green('homepage') + ' field in your ' + chalk.cyan('package.json') + '.');
157 | console.log();
158 | console.log('The ' + chalk.cyan('build') + ' folder is ready to be deployed.');
159 | console.log('To publish it at ' + chalk.green(homepagePath) + ', run:');
160 | // If script deploy has been added to package.json, skip the instructions
161 | if (typeof appPackage.scripts.deploy === 'undefined') {
162 | console.log();
163 | if (useYarn) {
164 | console.log(' ' + chalk.cyan('yarn') + ' add --dev gh-pages');
165 | } else {
166 | console.log(' ' + chalk.cyan('npm') + ' install --save-dev gh-pages');
167 | }
168 | console.log();
169 | console.log('Add the following script in your ' + chalk.cyan('package.json') + '.');
170 | console.log();
171 | console.log(' ' + chalk.dim('// ...'));
172 | console.log(' ' + chalk.yellow('"scripts"') + ': {');
173 | console.log(' ' + chalk.dim('// ...'));
174 | console.log(' ' + chalk.yellow('"deploy"') + ': ' + chalk.yellow('"npm run build&&gh-pages -d build"'));
175 | console.log(' }');
176 | console.log();
177 | console.log('Then run:');
178 | }
179 | console.log();
180 | console.log(' ' + chalk.cyan(useYarn ? 'yarn' : 'npm') + ' run deploy');
181 | console.log();
182 | } else if (publicPath !== '/') {
183 | // "homepage": "http://mywebsite.com/project"
184 | console.log('The project was built assuming it is hosted at ' + chalk.green(publicPath) + '.');
185 | console.log('You can control this with the ' + chalk.green('homepage') + ' field in your ' + chalk.cyan('package.json') + '.');
186 | console.log();
187 | console.log('The ' + chalk.cyan('build') + ' folder is ready to be deployed.');
188 | console.log();
189 | } else {
190 | // no homepage or "homepage": "http://mywebsite.com"
191 | console.log('The project was built assuming it is hosted at the server root.');
192 | if (homepagePath) {
193 | // "homepage": "http://mywebsite.com"
194 | console.log('You can control this with the ' + chalk.green('homepage') + ' field in your ' + chalk.cyan('package.json') + '.');
195 | console.log();
196 | } else {
197 | // no homepage
198 | console.log('To override this, specify the ' + chalk.green('homepage') + ' in your ' + chalk.cyan('package.json') + '.');
199 | console.log('For example, add this to build it for GitHub Pages:')
200 | console.log();
201 | console.log(' ' + chalk.green('"homepage"') + chalk.cyan(': ') + chalk.green('"http://myname.github.io/myapp"') + chalk.cyan(','));
202 | console.log();
203 | }
204 | console.log('The ' + chalk.cyan('build') + ' folder is ready to be deployed.');
205 | console.log('You may also serve it locally with a static server:')
206 | console.log();
207 | if (useYarn) {
208 | console.log(' ' + chalk.cyan('yarn') + ' global add pushstate-server');
209 | } else {
210 | console.log(' ' + chalk.cyan('npm') + ' install -g pushstate-server');
211 | }
212 | console.log(' ' + chalk.cyan('pushstate-server') + ' build');
213 | console.log(' ' + chalk.cyan(openCommand) + ' http://localhost:9000');
214 | console.log();
215 | }
216 | });
217 | }
218 |
219 | function copyPublicFolder() {
220 | fs.copySync(paths.appPublic, paths.appBuild, {
221 | dereference: true,
222 | filter: file => file !== paths.appHtml
223 | });
224 | }
225 |
--------------------------------------------------------------------------------
/config/webpack.config.dev.js:
--------------------------------------------------------------------------------
1 | var autoprefixer = require('autoprefixer');
2 | var webpack = require('webpack');
3 | var HtmlWebpackPlugin = require('html-webpack-plugin');
4 | var CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
5 | var InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
6 | var WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
7 | var getClientEnvironment = require('./env');
8 | var paths = require('./paths');
9 |
10 |
11 | // Webpack uses `publicPath` to determine where the app is being served from.
12 | // In development, we always serve from the root. This makes config easier.
13 | var publicPath = '/';
14 | // `publicUrl` is just like `publicPath`, but we will provide it to our app
15 | // as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
16 | // Omit trailing slash as %PUBLIC_PATH%/xyz looks better than %PUBLIC_PATH%xyz.
17 | var publicUrl = '';
18 | // Get environment variables to inject into our app.
19 | var env = getClientEnvironment(publicUrl);
20 |
21 | // This is the development configuration.
22 | // It is focused on developer experience and fast rebuilds.
23 | // The production configuration is different and lives in a separate file.
24 | module.exports = {
25 | // You may want 'eval' instead if you prefer to see the compiled output in DevTools.
26 | // See the discussion in https://github.com/facebookincubator/create-react-app/issues/343.
27 | devtool: 'cheap-module-source-map',
28 | // These are the "entry points" to our application.
29 | // This means they will be the "root" imports that are included in JS bundle.
30 | // The first two entry points enable "hot" CSS and auto-refreshes for JS.
31 | entry: [
32 | // Include an alternative client for WebpackDevServer. A client's job is to
33 | // connect to WebpackDevServer by a socket and get notified about changes.
34 | // When you save a file, the client will either apply hot updates (in case
35 | // of CSS changes), or refresh the page (in case of JS changes). When you
36 | // make a syntax error, this client will display a syntax error overlay.
37 | // Note: instead of the default WebpackDevServer client, we use a custom one
38 | // to bring better experience for Create React App users. You can replace
39 | // the line below with these two lines if you prefer the stock client:
40 | // require.resolve('webpack-dev-server/client') + '?/',
41 | // require.resolve('webpack/hot/dev-server'),
42 | require.resolve('react-dev-utils/webpackHotDevClient'),
43 | // We ship a few polyfills by default:
44 | require.resolve('./polyfills'),
45 | // Finally, this is your app's code:
46 | paths.appIndexJs
47 | // We include the app code last so that if there is a runtime error during
48 | // initialization, it doesn't blow up the WebpackDevServer client, and
49 | // changing JS code would still trigger a refresh.
50 | ],
51 | output: {
52 | // Next line is not used in dev but WebpackDevServer crashes without it:
53 | path: paths.appBuild,
54 | // Add /* filename */ comments to generated require()s in the output.
55 | pathinfo: true,
56 | // This does not produce a real file. It's just the virtual path that is
57 | // served by WebpackDevServer in development. This is the JS bundle
58 | // containing code from all our entry points, and the Webpack runtime.
59 | filename: 'static/js/bundle.js',
60 | // This is the URL that app is served from. We use "/" in development.
61 | publicPath: publicPath
62 | },
63 | resolve: {
64 | // This allows you to set a fallback for where Webpack should look for modules.
65 | // We read `NODE_PATH` environment variable in `paths.js` and pass paths here.
66 | // We use `fallback` instead of `root` because we want `node_modules` to "win"
67 | // if there any conflicts. This matches Node resolution mechanism.
68 | // https://github.com/facebookincubator/create-react-app/issues/253
69 | fallback: paths.nodePaths,
70 | // These are the reasonable defaults supported by the Node ecosystem.
71 | // We also include JSX as a common component filename extension to support
72 | // some tools, although we do not recommend using it, see:
73 | // https://github.com/facebookincubator/create-react-app/issues/290
74 | extensions: ['.js', '.json', '.jsx', '.styl',''],
75 | alias: {
76 | // Support React Native Web
77 | // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
78 | 'react-native': 'react-native-web'
79 | }
80 | },
81 |
82 | module: {
83 | // First, run the linter.
84 | // It's important to do this before Babel processes the JS.
85 | preLoaders: [
86 | {
87 | test: /\.(js|jsx)$/,
88 | loader: 'eslint',
89 | include: paths.appSrc,
90 | }
91 | ],
92 | loaders: [
93 | // Default loader: load all assets that are not handled
94 | // by other loaders with the url loader.
95 | // Note: This list needs to be updated with every change of extensions
96 | // the other loaders match.
97 | // E.g., when adding a loader for a new supported file extension,
98 | // we need to add the supported extension to this loader too.
99 | // Add one new line in `exclude` for each loader.
100 | //
101 | // "file" loader makes sure those assets get served by WebpackDevServer.
102 | // When you `import` an asset, you get its (virtual) filename.
103 | // In production, they would get copied to the `build` folder.
104 | // "url" loader works like "file" loader except that it embeds assets
105 | // smaller than specified limit in bytes as data URLs to avoid requests.
106 | // A missing `test` is equivalent to a match.
107 | {
108 | exclude: [
109 | /\.html$/,
110 | /\.(js|jsx)$/,
111 | /\.css$/,
112 | /\.styl$/,
113 | /\.json$/,
114 | /\.svg$/
115 | ],
116 | loader: 'url',
117 | query: {
118 | limit: 10000,
119 | name: 'static/media/[name].[hash:8].[ext]'
120 | }
121 | },
122 | // Process JS with Babel.
123 | {
124 | test: /\.(js|jsx)$/,
125 | include: paths.appSrc,
126 | loader: 'babel',
127 | query: {
128 |
129 | // This is a feature of `babel-loader` for webpack (not Babel itself).
130 | // It enables caching results in ./node_modules/.cache/babel-loader/
131 | // directory for faster rebuilds.
132 | cacheDirectory: true
133 | }
134 | },
135 | // "postcss" loader applies autoprefixer to our CSS.
136 | // "css" loader resolves paths in CSS and adds assets as dependencies.
137 | // "style" loader turns CSS into JS modules that inject