├── .babelrc ├── .eslintrc ├── .gitignore ├── .npmignore ├── LICENSE ├── Makefile ├── README.md ├── main.js ├── package-lock.json ├── package.json ├── postcss.config.js ├── site ├── css │ └── .keep ├── images │ ├── .keep │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ ├── 8.png │ ├── 9.png │ ├── album-1.png │ ├── album-2.png │ ├── album-3.png │ ├── album-4.png │ ├── album-5.png │ ├── album-6.png │ ├── album-7.png │ ├── album-8.png │ ├── album-9.png │ └── preview.png ├── index.html ├── js │ ├── main.bundle.js │ ├── main.bundle.js.br │ └── main.js ├── webpack.common.js ├── webpack.config.js ├── webpack.dev.js └── webpack.prod.js ├── src ├── Coverflow.js └── stylesheets │ └── coverflow.scss ├── test └── main.js ├── webpack.common.js ├── webpack.config.js ├── webpack.dev.js └── webpack.prod.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-0", "react"], 3 | "plugins": [ 4 | "transform-runtime", 5 | "transform-decorators-legacy", 6 | "transform-class-properties", 7 | "react-hot-loader/babel", 8 | [ 9 | "add-module-exports", 10 | { 11 | "addDefaultProperty": true 12 | } 13 | ] 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | "env": { 4 | "browser": true, 5 | "node": true, 6 | "es6": true 7 | }, 8 | "parser": "babel-eslint", 9 | "parserOptions": { 10 | "ecmaVersion": 6, 11 | "ecmaFeatures": { 12 | "jsx": true, 13 | "arrowFunctions": true, 14 | "blockBindings": true, 15 | "classes": true, 16 | "defaultParams": true, 17 | "destructuring": true, 18 | "forOf": true, 19 | "generators": false, 20 | "modules": true, 21 | "objectLiteralComputedProperties": true, 22 | "objectLiteralDuplicateProperties": false, 23 | "objectLiteralShorthandMethods": true, 24 | "objectLiteralShorthandProperties": true, 25 | "spread": true, 26 | "superInFunctions": true, 27 | "templateStrings": true 28 | } 29 | }, 30 | "rules": { 31 | /** 32 | * References 33 | * http://www.cnblogs.com/qianlegeqian/p/4728170.html 34 | * https://github.com/airbnb/javascript/blob/master/packages/eslint-config-airbnb/base/index.js 35 | */ 36 | "allowForLoopAfterthoughts": true, 37 | "no-underscore-dangle": 0, 38 | "no-console": "off", 39 | "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }], 40 | "semi": [2, "always"], // 句尾強制分號 41 | "curly": 0, 42 | "radix": 2, 43 | "brace-style": 0, 44 | "consistent-this": 0, 45 | "indent": [2, 2], 46 | "no-lonely-if": 2, 47 | "no-nested-ternary": 2, 48 | "no-use-before-define": [2, "nofunc"], 49 | "space-after-keywords": "off", 50 | "keyword-spacing": [2, { "before": true, "after": true }], 51 | "space-before-blocks": [2, "always"], // 大括號前要有空白 52 | "space-in-parens": [2, "never"], // 小括號內(前後)不得有空白 53 | "space-unary-ops": 2, 54 | "no-mixed-spaces-and-tabs": [2, false] // 禁止混用 space & tab 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | *.swp 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 19 | .grunt 20 | 21 | # node-waf configuration 22 | .lock-wscript 23 | 24 | # Compiled binary addons (http://nodejs.org/api/addons.html) 25 | build/Release 26 | 27 | # Dependency directory 28 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 29 | node_modules 30 | dist -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | site 2 | src 3 | webpack.config.js 4 | Makefile 5 | .eslintrc 6 | test 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Ramana Venkata 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | ./node_modules/.bin/mocha --reporter progress 3 | .PHONY: test -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Looking for someone to take over the project as I've recently changes jobs and had a child and am no longer capable of supporting this dependency. If anyone is interested, please feel free to take ownership of it 2 | 3 | React Coverflow 4 | === 5 | [![npm version](https://badge.fury.io/js/react-coverflow.svg)](http://badge.fury.io/js/react-coverflow) 6 | 7 | [![NPM](https://nodei.co/npm/react-coverflow.png)](https://nodei.co/npm/react-coverflow/) 8 | 9 | --- 10 | 11 | ![](https://raw.githubusercontent.com/andyyou/react-coverflow/gh-pages/images/preview.png) 12 | 13 | React Coverflow is a React component for building cover flow style carousel in a convenient way. 14 | 15 | Features of `react-coverflow` 16 | 17 | * Flexbox styles of CSS 3. 18 | * Support scroll in the component. 19 | * Support navigation buttons optional 20 | * Using css-module 21 | * Support mobile 22 | 23 | ## Getting started 24 | 25 | Install `react-coverflow` using npm. 26 | 27 | ``` 28 | $ npm install react-coverflow 29 | ``` 30 | 31 | The required stylesheet using `css-module` and include in package(js file), so you don't need include other stylesheet. 32 | 33 | ## Usage 34 | 35 | ```js 36 | import React from 'react'; 37 | import ReactDOM from 'react-dom'; 38 | import Coverflow from 'react-coverflow'; 39 | 40 | const fn = function () { 41 | /* do your action */ 42 | } 43 | 44 | ReactDOM.render( 45 | 52 |
fn()} 54 | onKeyDown={() => fn()} 55 | role="menuitem" 56 | tabIndex="0" 57 | > 58 | title or description 66 |
67 | title or description 68 | title or description 69 |
, 70 | 71 | document.querySelector('.content') 72 | ); 73 | ``` 74 | 75 | In order to pass functions to the images, you can simply wrap the `` in a `
`. You should make sure to give your img specific styling properties to confine it to the parent div. 76 | 77 | For more examples, on how to use React Coverflow, please visit http://andyyou.github.io/react-coverflow/ 78 | 79 | #### Properties 80 | | Name | Type | Default | Description | 81 | |----------------------|-----------|-------------------|----------------------------------------------| 82 | | children | node | | should be `` nodes | 83 | | infiniteScroll | boolean | false | Allows the carousel to scroll from the last image to the (upon clicking the next button) or from the first to the last (by clicking the previous button). infiniteScroll is not supported by mouse scrolling. | 84 | | displayQuantityOfSide| number | | The number of display image from center to the one side end.| 85 | | navigation | boolean | false | Enable navigation arrows. This will disable image-click navigation. | 86 | | enableHeading | boolean | true | Shows the img alt as the label for the img. | 87 | | enableScroll | boolean | true | Enable scrolling feature. | 88 | | media | object | | To support media query, if you want responsive with parent element you can ignore `width` and `height` props or set it to `auto`. | 89 | | active | number | middle child node | The index of the image you want to be active.| 90 | | clickable | boolean | true | Makes images clickable, setting to false will disable clickability of images. | 91 | | currentFigureScale | number | 1.5 | The scale factor (float) to be applied on the active image. | 92 | | otherFigureScale | number | .8 | The scale factor (float) to be applied on the inactive images.| 93 | | classes | object | | To support style changes for customization. | 94 | | className | string | | To add css class to the coverflow to customize. | 95 | 96 | #### Responsive 97 | 98 | Now, you can use `media` props to support responsive design. The syntax part is come form [Radium](http://projects.formidablelabs.com/radium/) 99 | You can reference to this [doc](https://github.com/FormidableLabs/radium/tree/master/docs/guides#media-queries). 100 | 101 | * 2016-09-13 If you need RWD you should add `` 102 | 103 | ```js 104 | ReactDOM.render( 105 | 106 | 122 | Album one 123 | Album two 124 | Album three 125 | Album four 126 | 127 | 128 | , 129 | document.querySelector('.example_2') 130 | ); 131 | ``` 132 | 133 | 134 | ## Development 135 | 136 | If you want to custom the component there are some commands that can help you. 137 | 138 | ``` 139 | # Compile component to dist/ 140 | $ npm run build-npm 141 | 142 | # Build source of site 143 | $ npm run build-site 144 | 145 | # Clean 146 | $ npm run clean 147 | 148 | # Development Server 149 | $ npm run dev 150 | 151 | # Deploy examples to gh-pages 152 | $ npm run build-npm 153 | $ npm run build-site 154 | $ git subtree push --prefix site origin gh-pages 155 | $ npm version patch 156 | $ npm publish 157 | ``` 158 | 159 | 160 | 161 | ## Todo 162 | 163 | * Write test case 164 | * Add further customization options 165 | * Add Prev/Next Arrows instead of Buttons 166 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | //module find 2 | 3 | module.exports = require('./dist/react-coverflow'); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-coverflow", 3 | "version": "0.2.20", 4 | "description": "Carousel of coverflow effect", 5 | "main": "main.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/andyyou/react-coverflow" 9 | }, 10 | "scripts": { 11 | "test": "make test", 12 | "prepublishOnly": "rm -rf dist && webpack --progress --colors -d", 13 | "clean": "rm -rf dist && rm -rf site/js/*.bundle.js", 14 | "build-npm": "rm -rf dist && webpack --progress --colors -d --config webpack.prod.js", 15 | "build:watch": "webpack --progress --colors --watch", 16 | "lint": "eslint ./src/Coverflow.js eslint ./site/js/main.js", 17 | "build-site": "rm -rf site/js/*.bundle.js && NODE_ENV=production webpack --config site/webpack.prod.js", 18 | "dev": "rm -rf site/js/*.bundle.js && NODE_ENV=react-hot webpack-dev-server --config site/webpack.dev.js --progress --colors --inline --hot --port 3000 --host 0.0.0.0 --content-base site" 19 | }, 20 | "postcss": { 21 | "parser": "sugarss", 22 | "map": false, 23 | "from": "/path/to/src.sss", 24 | "to": "/path/to/dest.css", 25 | "plugins": { 26 | "postcss-plugin": {} 27 | } 28 | }, 29 | "keywords": [ 30 | "Carousel", 31 | "Coverflow", 32 | "Image slider", 33 | "3D", 34 | "React", 35 | "react-component" 36 | ], 37 | "author": { 38 | "name": "andyyou", 39 | "email": "andyyu0920@gmail.com", 40 | "url": "http://andyyou.github.io/" 41 | }, 42 | "license": "MIT", 43 | "dependencies": { 44 | "radium": "^0.25.1" 45 | }, 46 | "peerDependencies": { 47 | "prop-types": "^15.6.2", 48 | "react": "^16.5.2" 49 | }, 50 | "url": "https://github.com/andyyou/react-coverflow.git", 51 | "devDependencies": { 52 | "babel-core": "^6.26.3", 53 | "babel-eslint": "^9.0.0", 54 | "babel-loader": "^7.1.5", 55 | "babel-plugin-add-module-exports": "^1.0.0", 56 | "babel-plugin-transform-class-properties": "^6.24.1", 57 | "babel-plugin-transform-decorators-legacy": "^1.3.5", 58 | "babel-plugin-transform-runtime": "^6.15.0", 59 | "babel-preset-es2015": "^6.14.0", 60 | "babel-preset-react": "^6.11.1", 61 | "babel-preset-stage-0": "^6.5.0", 62 | "babel-runtime": "^6.26.0", 63 | "chai": "^4.1.2", 64 | "clean-webpack-plugin": "^1.0.1", 65 | "css-loader": "^1.0.0", 66 | "eslint": "^5.6.0", 67 | "eslint-config-airbnb": "^17.1.0", 68 | "eslint-loader": "^2.1.1", 69 | "eslint-plugin-import": "^2.14.0", 70 | "eslint-plugin-jsx-a11y": "^6.1.1", 71 | "eslint-plugin-react": "^7.11.0", 72 | "extract-text-webpack-plugin": "^3.0.2", 73 | "file-loader": "^2.0.0", 74 | "glob": "^7.1.3", 75 | "html-webpack-plugin": "^3.2.0", 76 | "mocha": "^5.2.0", 77 | "node-sass": "^4.9.3", 78 | "postcss-load-config": "^2.0.0", 79 | "postcss-loader": "^3.0.0", 80 | "prop-types": "^15.6.2", 81 | "react": "^16.5.2", 82 | "react-dom": "^16.5.2", 83 | "react-hot-loader": "^4.3.11", 84 | "sass-loader": "^7.0.3", 85 | "style-loader": "^0.23.0", 86 | "uglifyjs-webpack-plugin": "^2.0.1", 87 | "webpack": "^4.19.1", 88 | "webpack-cli": "^3.1.1", 89 | "webpack-dev-server": "^3.1.14", 90 | "webpack-merge": "^4.2.1" 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: 'sugarss', 3 | plugins: { 4 | 'postcss-import': {}, 5 | 'postcss-cssnext': {}, 6 | 'cssnano': {} 7 | } 8 | } -------------------------------------------------------------------------------- /site/css/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sisu0827/React.js-CoverflowEffect/9db7fb53db14a83a0131e50596820b37b2079946/site/css/.keep -------------------------------------------------------------------------------- /site/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sisu0827/React.js-CoverflowEffect/9db7fb53db14a83a0131e50596820b37b2079946/site/images/.keep -------------------------------------------------------------------------------- /site/images/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sisu0827/React.js-CoverflowEffect/9db7fb53db14a83a0131e50596820b37b2079946/site/images/1.png -------------------------------------------------------------------------------- /site/images/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sisu0827/React.js-CoverflowEffect/9db7fb53db14a83a0131e50596820b37b2079946/site/images/2.png -------------------------------------------------------------------------------- /site/images/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sisu0827/React.js-CoverflowEffect/9db7fb53db14a83a0131e50596820b37b2079946/site/images/3.png -------------------------------------------------------------------------------- /site/images/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sisu0827/React.js-CoverflowEffect/9db7fb53db14a83a0131e50596820b37b2079946/site/images/4.png -------------------------------------------------------------------------------- /site/images/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sisu0827/React.js-CoverflowEffect/9db7fb53db14a83a0131e50596820b37b2079946/site/images/5.png -------------------------------------------------------------------------------- /site/images/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sisu0827/React.js-CoverflowEffect/9db7fb53db14a83a0131e50596820b37b2079946/site/images/6.png -------------------------------------------------------------------------------- /site/images/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sisu0827/React.js-CoverflowEffect/9db7fb53db14a83a0131e50596820b37b2079946/site/images/7.png -------------------------------------------------------------------------------- /site/images/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sisu0827/React.js-CoverflowEffect/9db7fb53db14a83a0131e50596820b37b2079946/site/images/8.png -------------------------------------------------------------------------------- /site/images/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sisu0827/React.js-CoverflowEffect/9db7fb53db14a83a0131e50596820b37b2079946/site/images/9.png -------------------------------------------------------------------------------- /site/images/album-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sisu0827/React.js-CoverflowEffect/9db7fb53db14a83a0131e50596820b37b2079946/site/images/album-1.png -------------------------------------------------------------------------------- /site/images/album-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sisu0827/React.js-CoverflowEffect/9db7fb53db14a83a0131e50596820b37b2079946/site/images/album-2.png -------------------------------------------------------------------------------- /site/images/album-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sisu0827/React.js-CoverflowEffect/9db7fb53db14a83a0131e50596820b37b2079946/site/images/album-3.png -------------------------------------------------------------------------------- /site/images/album-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sisu0827/React.js-CoverflowEffect/9db7fb53db14a83a0131e50596820b37b2079946/site/images/album-4.png -------------------------------------------------------------------------------- /site/images/album-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sisu0827/React.js-CoverflowEffect/9db7fb53db14a83a0131e50596820b37b2079946/site/images/album-5.png -------------------------------------------------------------------------------- /site/images/album-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sisu0827/React.js-CoverflowEffect/9db7fb53db14a83a0131e50596820b37b2079946/site/images/album-6.png -------------------------------------------------------------------------------- /site/images/album-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sisu0827/React.js-CoverflowEffect/9db7fb53db14a83a0131e50596820b37b2079946/site/images/album-7.png -------------------------------------------------------------------------------- /site/images/album-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sisu0827/React.js-CoverflowEffect/9db7fb53db14a83a0131e50596820b37b2079946/site/images/album-8.png -------------------------------------------------------------------------------- /site/images/album-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sisu0827/React.js-CoverflowEffect/9db7fb53db14a83a0131e50596820b37b2079946/site/images/album-9.png -------------------------------------------------------------------------------- /site/images/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sisu0827/React.js-CoverflowEffect/9db7fb53db14a83a0131e50596820b37b2079946/site/images/preview.png -------------------------------------------------------------------------------- /site/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React Coverflow 7 | 8 | 9 | 10 | 11 | 12 | 38 | 39 |
40 |
41 |
42 |
43 |

44 | React Coverflow Component 45 |

46 | Star 48 | 49 | Fork 51 |

52 |

53 |

54 | A Coverflow effect component of React version made for the web. 55 |

56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | 66 |
67 |
68 |
69 |

70 | React Coverflow is a React component for building cover flow style carousel in a convenient way. 71 |

72 |

Getting started

73 |

74 | Install 75 | react-coverflow using npm. 76 |

77 | 78 |
npm install react-coverflow
79 | 80 |

81 | Then you can require it and use. 82 |

83 | 84 | 85 | 86 |

87 | For more information, please visit 88 | Github 89 |

90 |
91 |
92 | 93 |
94 |
95 |

Responsive

96 | 97 |
98 | We don't support 99 | react-responsive anymore. Now you can just use 100 | media property to support responsive ability. This example also highlights the function of the properties 101 | of navigation, enableHeading, and infiniteScroll properties. 102 | 103 |
104 |
105 | navigation renders the Previous and Next buttons for easier navigation 106 |
107 |
108 | enableHeading renders the label passed in as the alt property 109 |
110 |
111 | infiniteScroll allows for the user to scroll to continuously scroll through the imgs 112 |
113 |
114 |
115 |
116 |
117 | 118 |
119 |
120 |
121 |
122 |
123 | 124 |
125 |
126 |
127 | 128 |
129 |
130 | 131 |
132 |
133 |
134 |
135 |
136 |
137 | 138 |
139 |
140 |

Support set active outside

141 | 142 |

143 | You can use `state` to specify active now. 144 |

145 |
146 |
147 | 148 |
149 |
150 |
151 |
152 |
153 |
154 | 155 |
156 |
157 |
158 | 159 |
160 |
161 | 162 | 163 | 164 | 176 | 177 | 178 | 179 | -------------------------------------------------------------------------------- /site/js/main.bundle.js.br: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sisu0827/React.js-CoverflowEffect/9db7fb53db14a83a0131e50596820b37b2079946/site/js/main.bundle.js.br -------------------------------------------------------------------------------- /site/js/main.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | import React, { Component } from 'react'; 3 | import { render } from 'react-dom'; 4 | import { StyleRoot } from 'radium'; 5 | import Coverflow from '../../src/Coverflow'; 6 | 7 | const fn = () => { 8 | console.log('Album one'); 9 | }; 10 | 11 | render( 12 | 20 |
fn()} onKeyDown={() => fn()} role="menuitem" tabIndex="0"> 21 | Album one 22 |
23 | Album two 24 | Album three 25 | Album four 26 | Album five 27 | Album six 28 | Album seven 29 | Album one 30 | Album two 31 | Album three 32 | Album four 33 | Album five 34 | Album six 35 | Album seven 36 | Album one 37 | Album two 38 | Album three 39 | Album four 40 | Album five 41 | Album six 42 | Album seven 43 | Album one 44 | Album two 45 | Album three 46 | Album four 47 | Album five 48 | Album six 49 | Album seven 50 |
, 51 | 52 | document.querySelector('.example_1'), 53 | ); 54 | 55 | render( 56 | 57 | 73 | Album one 78 | Album two 79 | Album three 80 | Album four 81 | 82 | , 83 | document.querySelector('.example_2'), 84 | ); 85 | 86 | class Container extends Component { 87 | constructor(props) { 88 | super(props); 89 | 90 | this.state = { 91 | active: 0, 92 | }; 93 | } 94 | 95 | _handleClick = () => { 96 | console.log('go'); 97 | const num = ~~(Math.random() * 10); 98 | this.setState({ 99 | active: num, 100 | }); 101 | }; 102 | 103 | render() { 104 | return ( 105 |
106 |
107 | 121 |
122 | 130 |
fn()} onKeyDown={() => fn()} role="menuitem" tabIndex="0"> 131 | Album one 136 |
137 | Album two 138 | Album three 139 | Album four 140 | Album five 141 | Album six 142 | Album seven 143 | Album one 148 | Album two 149 | Album three 150 | Album four 151 |
152 |
153 | ); 154 | } 155 | } 156 | 157 | render(, document.querySelector('.example_4')); 158 | -------------------------------------------------------------------------------- /site/webpack.common.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const glob = require('glob'); 3 | const webpack = require('webpack'); 4 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); 5 | 6 | const plugins = [ 7 | new webpack.LoaderOptionsPlugin({ 8 | debug: true, 9 | test: /\.js$/, 10 | options: { 11 | eslint: { 12 | configFile: path.resolve(__dirname, '.eslintrc'), 13 | cache: false, 14 | }, 15 | }, 16 | }), 17 | ]; 18 | 19 | const entry = {}; 20 | const mainEntryPoints = glob.sync( 21 | // Ignore compile filename with `.bundle.js` 22 | path.join(__dirname, './js/!(*.bundle).js'), 23 | ); 24 | entry.main = mainEntryPoints; 25 | 26 | const config = { 27 | context: __dirname, 28 | entry, 29 | output: { 30 | path: `${__dirname}/js`, 31 | filename: '[name].bundle.js', 32 | publicPath: 'js/', 33 | }, 34 | optimization: { 35 | minimizer: [ 36 | new UglifyJsPlugin({ 37 | cache: true, 38 | sourceMap: true, 39 | parallel: true, 40 | uglifyOptions: { 41 | compress: { 42 | inline: false, 43 | }, 44 | ecma: 6, 45 | mangle: true, 46 | }, 47 | }), 48 | ], 49 | runtimeChunk: false, 50 | splitChunks: { 51 | cacheGroups: { 52 | default: false, 53 | commons: { 54 | test: /[\\/]node_modules[\\/]/, 55 | name: 'vendor_app', 56 | chunks: 'all', 57 | minChunks: 2, 58 | }, 59 | }, 60 | }, 61 | }, 62 | resolve: { 63 | extensions: ['.js', '.jsx', '.json', '.scss', '.css'], 64 | alias: { 65 | radium: require.resolve('radium/index'), 66 | }, 67 | }, 68 | module: { 69 | rules: [ 70 | { 71 | test: /\.js$/, 72 | exclude: /node_modules/, 73 | loader: 'babel-loader', 74 | }, 75 | { 76 | test: /\.(css|scss)$/, 77 | loaders: [ 78 | 'style-loader', 79 | 'css-loader?modules&importLoaders=1&localIdentName=[name]__[local]__[hash:base64:5]', 80 | { loader: 'sass-loader', options: { minimize: true } }, 81 | ], 82 | }, 83 | ], 84 | }, 85 | plugins, 86 | }; 87 | 88 | module.exports = config; 89 | -------------------------------------------------------------------------------- /site/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const glob = require('glob'); 3 | const webpack = require('webpack'); 4 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); 5 | const BrotliPlugin = require('brotli-webpack-plugin'); 6 | 7 | const plugins = [ 8 | new webpack.LoaderOptionsPlugin({ 9 | debug: true, 10 | test: /\.js$/, 11 | options: { 12 | eslint: { 13 | configFile: path.resolve(__dirname, '.eslintrc'), 14 | cache: false, 15 | }, 16 | }, 17 | }), 18 | new BrotliPlugin({ 19 | asset: '[path].br[query]', 20 | test: /\.(js|css|html|svg)$/, 21 | threshold: 10240, 22 | minRatio: 0.8, 23 | }), 24 | new webpack.DefinePlugin({ 25 | 'process.env': { 26 | NODE_ENV: JSON.stringify('production'), 27 | }, 28 | }), 29 | ]; 30 | 31 | const entry = {}; 32 | const mainEntryPoints = glob.sync( 33 | // Ignore compile filename with `.bundle.js` 34 | path.join(__dirname, './js/!(*.bundle).js'), 35 | ); 36 | entry.main = mainEntryPoints; 37 | 38 | const config = { 39 | mode: 'production', 40 | context: __dirname, 41 | entry, 42 | output: { 43 | path: `${__dirname}/js`, 44 | filename: '[name].bundle.js', 45 | publicPath: 'js/', 46 | }, 47 | devtool: 'eval-source-map', 48 | resolve: { 49 | extensions: ['.js', '.jsx', '.json', '.scss', '.css'], 50 | alias: { 51 | radium: require.resolve('radium/index'), 52 | }, 53 | }, 54 | optimization: { 55 | minimize: true, 56 | concatenateModules: true, 57 | minimizer: [ 58 | new UglifyJsPlugin({ 59 | sourceMap: true, 60 | uglifyOptions: { 61 | compress: { 62 | inline: false, 63 | }, 64 | ecma: 6, 65 | mangle: true, 66 | }, 67 | }), 68 | ], 69 | runtimeChunk: false, 70 | splitChunks: { 71 | cacheGroups: { 72 | default: false, 73 | commons: { 74 | test: /[\\/]node_modules[\\/]/, 75 | name: 'vendor_app', 76 | chunks: 'all', 77 | minChunks: 2, 78 | }, 79 | }, 80 | }, 81 | }, 82 | module: { 83 | rules: [ 84 | { 85 | test: /\.js$/, 86 | exclude: /node_modules/, 87 | loader: 'babel-loader', 88 | }, 89 | { 90 | test: /\.(css|scss)$/, 91 | loaders: [ 92 | 'style-loader', 93 | // { loader: 'css-loader', options: { minimize: true } }, 94 | 'css-loader?modules&importLoaders=1&localIdentName=[name]__[local]__[hash:base64:5]', 95 | { loader: 'sass-loader', options: { minimize: true } }, 96 | ], 97 | }, 98 | ], 99 | }, 100 | plugins, 101 | }; 102 | 103 | module.exports = config; 104 | -------------------------------------------------------------------------------- /site/webpack.dev.js: -------------------------------------------------------------------------------- 1 | const merge = require('webpack-merge'); 2 | const common = require('./webpack.common.js'); 3 | 4 | module.exports = merge(common, { 5 | mode: 'development', 6 | devtool: 'inline-source-map', 7 | }); 8 | -------------------------------------------------------------------------------- /site/webpack.prod.js: -------------------------------------------------------------------------------- 1 | const merge = require('webpack-merge'); 2 | const common = require('./webpack.common.js'); 3 | 4 | module.exports = merge(common, { 5 | mode: 'production', 6 | devtool: 'eval-source-map', 7 | }); 8 | -------------------------------------------------------------------------------- /src/Coverflow.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /** 3 | * React Coverflow 4 | * 5 | * Author: andyyou & asalem1 6 | */ 7 | import React, { Component, createRef } from 'react'; 8 | import PropTypes from 'prop-types'; 9 | import Radium, { StyleRoot } from 'radium'; 10 | import styles from './stylesheets/coverflow.scss'; 11 | 12 | const TOUCH = { 13 | move: false, 14 | lastX: 0, 15 | sign: 0, 16 | lastMove: 0, 17 | }; 18 | 19 | const TRANSITIONS = [ 20 | 'transitionend', 21 | 'oTransitionEnd', 22 | 'otransitionend', 23 | 'MSTransitionEnd', 24 | 'webkitTransitionEnd', 25 | ]; 26 | 27 | const HandleAnimationState = function() { 28 | this._removePointerEvents(); 29 | }; 30 | 31 | class Coverflow extends Component { 32 | /** 33 | * Life cycle events 34 | */ 35 | refNode = createRef(); 36 | 37 | static propTypes = { 38 | children: PropTypes.node.isRequired, 39 | displayQuantityOfSide: PropTypes.number.isRequired, 40 | navigation: PropTypes.bool, 41 | enableHeading: PropTypes.bool, 42 | enableScroll: PropTypes.bool, 43 | clickable: PropTypes.bool, 44 | currentFigureScale: PropTypes.number, 45 | otherFigureScale: PropTypes.number, 46 | active: PropTypes.number, 47 | media: PropTypes.any, 48 | classes: PropTypes.object, 49 | className: PropTypes.string, 50 | infiniteScroll: PropTypes.bool, 51 | width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 52 | height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 53 | }; 54 | 55 | static defaultProps = { 56 | navigation: false, 57 | enableHeading: true, 58 | enableScroll: true, 59 | clickable: true, 60 | classes: {}, 61 | className: '', 62 | currentFigureScale: 1.5, 63 | otherFigureScale: 0.8, 64 | active: 0, 65 | media: {}, 66 | infiniteScroll: false, 67 | width: 'auto', 68 | height: 'auto', 69 | }; 70 | 71 | state = { 72 | current: ~~(React.Children.count(this.props.children) / 2), 73 | move: 0, 74 | width: this.props.width, 75 | height: this.props.height, 76 | }; 77 | 78 | componentDidMount() { 79 | this.updateDimensions(this.props.active); 80 | const length = React.Children.count(this.props.children); 81 | 82 | TRANSITIONS.forEach(event => { 83 | for (let i = 0; i < length; i++) { 84 | const figureID = `figure_${i}`; 85 | this.refs[figureID].addEventListener(event, HandleAnimationState.bind(this)); 86 | } 87 | }); 88 | 89 | const eventListener = window && window.addEventListener; 90 | 91 | if (eventListener) { 92 | window.addEventListener('resize', this.updateDimensions.bind(this)); 93 | } 94 | } 95 | 96 | componentDidUpdate(prevProps) { 97 | if (this.props.active !== prevProps.active) { 98 | this.updateDimensions(this.props.active); 99 | } 100 | } 101 | 102 | componentWillUnmount() { 103 | const length = React.Children.count(this.props.children); 104 | 105 | TRANSITIONS.forEach(event => { 106 | for (let i = 0; i < length; i++) { 107 | const figureID = `figure_${i}`; 108 | this.refs[figureID].removeEventListener(event, HandleAnimationState.bind(this)); 109 | } 110 | }); 111 | 112 | // const removeListener = window && window.removeEventListener; 113 | 114 | // if(removeListener) { 115 | // window.removeEventListener('resize', this.updateDimensions.bind(this)); 116 | // } 117 | } 118 | 119 | updateDimensions(active) { 120 | const { displayQuantityOfSide } = this.props; 121 | const length = React.Children.count(this.props.children); 122 | const center = this._center(); 123 | let state = { 124 | width: this.refNode.current.offsetWidth, 125 | height: this.refNode.current.offsetHeight, 126 | }; 127 | const baseWidth = state.width / (displayQuantityOfSide * 2 + 1); 128 | let activeImg = typeof active === 'number' ? active : this.props.active; 129 | if (typeof active === 'number' && ~~active < length) { 130 | activeImg = ~~active; 131 | let move = 0; 132 | move = baseWidth * (center - activeImg); 133 | 134 | state = Object.assign({}, state, { 135 | current: active, 136 | move, 137 | }); 138 | } 139 | this.setState(state); 140 | } 141 | 142 | render() { 143 | const { enableScroll, navigation, className, classes, infiniteScroll, media } = this.props; 144 | const { width, height, current } = this.state; 145 | const renderPrevBtn = infiniteScroll ? true : current > 0; 146 | const renderNextBtn = infiniteScroll ? true : current < this.props.children.length - 1; 147 | return ( 148 |
151 | 152 |
163 |
164 |
165 |
166 | {navigation && ( 167 |
171 | {renderPrevBtn && ( 172 |
this._handlePrevFigure(e)} 174 | className={`${styles.arrow} ${styles.left}`} 175 | > 176 | 177 |
178 | )} 179 | {this._renderFigureNodes()} 180 | {renderNextBtn && ( 181 |
this._handleNextFigure(e)} 183 | className={`${styles.arrow} ${styles.right}`} 184 | > 185 | 186 |
187 | )} 188 |
189 | )} 190 | {!navigation && this._renderFigureNodes()} 191 |
192 |
193 |
194 | 195 |
196 | ); 197 | } 198 | 199 | /** 200 | * Private methods 201 | */ 202 | _center() { 203 | const length = React.Children.count(this.props.children); 204 | return ~~(length / 2); 205 | } 206 | 207 | _keyDown(e) { 208 | if (e.keyCode === 37) { 209 | this._handlePrevFigure(); 210 | } else if (e.keyCode === 39) { 211 | this._handleNextFigure(); 212 | } 213 | } 214 | 215 | _handleFigureStyle(index, current) { 216 | const { displayQuantityOfSide, navigation } = this.props; 217 | const { width } = this.state; 218 | const style = {}; 219 | const baseWidth = width / (displayQuantityOfSide * 2 + 1); 220 | const length = React.Children.count(this.props.children); 221 | const offset = length % 2 === 0 ? -width / 10 : 0; 222 | // Handle opacity 223 | const depth = displayQuantityOfSide - Math.abs(current - index); 224 | let opacity = depth === 1 ? 0.95 : 0.5; 225 | opacity = depth === 2 ? 0.92 : opacity; 226 | opacity = depth === 3 ? 0.9 : opacity; 227 | opacity = current === index ? 1 : opacity; 228 | // Handle translateX 229 | if (index === current) { 230 | style.width = `${baseWidth}px`; 231 | style.transform = `translateX(${this.state.move + offset}px) scale(${ 232 | this.props.currentFigureScale 233 | }`; 234 | style.zIndex = `${10 - depth}`; 235 | style.opacity = opacity; 236 | } else if (index < current) { 237 | // Left side 238 | style.width = `${baseWidth}px`; 239 | style.transform = `translateX(${this.state.move + offset}px) rotateY(40deg) scale(${ 240 | this.props.otherFigureScale 241 | }`; 242 | style.zIndex = `${10 - depth}`; 243 | style.opacity = opacity; 244 | if (navigation) { 245 | style.pointerEvents = 'none'; 246 | } 247 | } else if (index > current) { 248 | // Right side 249 | style.width = `${baseWidth}px`; 250 | style.transform = ` translateX(${this.state.move + offset}px) rotateY(-40deg) scale(${ 251 | this.props.otherFigureScale 252 | })`; 253 | style.zIndex = `${10 - depth}`; 254 | style.opacity = opacity; 255 | if (navigation) { 256 | style.pointerEvents = 'none'; 257 | } 258 | } 259 | return style; 260 | } 261 | 262 | _handleFigureClick = (index, action, e) => { 263 | if (!this.props.clickable) { 264 | e.preventDefault(); 265 | return; 266 | } 267 | if (this.state.current === index) { 268 | // If on the active figure 269 | if (typeof action === 'string') { 270 | // If action is a URL (string), follow the link 271 | window.open(action, '_blank'); 272 | } 273 | 274 | this._removePointerEvents(); 275 | } else { 276 | // Move to the selected figure 277 | e.preventDefault(); 278 | const { displayQuantityOfSide } = this.props; 279 | const { width } = this.state; 280 | const baseWidth = width / (displayQuantityOfSide * 2 + 1); 281 | const distance = this._center() - index; 282 | const move = distance * baseWidth; 283 | this.setState({ current: index, move }); 284 | } 285 | }; 286 | 287 | _renderFigureNodes = () => { 288 | const { enableHeading } = this.props; 289 | const { current } = this.state; 290 | const figureNodes = React.Children.map(this.props.children, (child, index) => { 291 | const figureElement = React.cloneElement(child, { 292 | className: styles.cover, 293 | }); 294 | const style = this._handleFigureStyle(index, current); 295 | return ( 296 |
this._handleFigureClick(index, figureElement.props['data-action'], e)} 300 | style={style} 301 | ref={`figure_${index}`} 302 | > 303 | {figureElement} 304 | {enableHeading &&
{figureElement.props.alt}
} 305 |
306 | ); 307 | }); 308 | return figureNodes; 309 | }; 310 | 311 | _removePointerEvents() { 312 | this.refs.stage.style.pointerEvents = 'auto'; 313 | } 314 | 315 | _hasPrevFigure = () => this.state.current - 1 >= 0; 316 | 317 | _hasNextFigure = () => this.state.current + 1 < this.props.children.length; 318 | 319 | _handlePrevFigure = (e) => { 320 | const { displayQuantityOfSide, infiniteScroll } = this.props; 321 | const { width } = this.state; 322 | const { current } = this.state; 323 | const baseWidth = width / (displayQuantityOfSide * 2 + 1); 324 | const distance = 325 | this._center() - (current - 1 < 0 ? this.props.children.length - 1 : current - 1); 326 | const move = distance * baseWidth; 327 | 328 | if (current - 1 >= 0) { 329 | this.setState({ current: current - 1, move }); 330 | TOUCH.lastMove = move; 331 | } 332 | if (current - 1 < 0 && infiniteScroll) { 333 | this.setState({ current: this.props.children.length - 1, move }); 334 | TOUCH.lastMove = move; 335 | } 336 | }; 337 | 338 | _handleNextFigure = (e) => { 339 | const { displayQuantityOfSide, infiniteScroll } = this.props; 340 | const { width } = this.state; 341 | const { current } = this.state; 342 | const baseWidth = width / (displayQuantityOfSide * 2 + 1); 343 | const distance = this._center() - (current + 1 >= this.props.children.length ? 0 : current + 1); 344 | const move = distance * baseWidth; 345 | 346 | if (current + 1 < this.props.children.length) { 347 | this.setState({ current: current + 1, move }); 348 | TOUCH.lastMove = move; 349 | } 350 | if (current + 1 >= this.props.children.length && infiniteScroll) { 351 | this.setState({ current: 0, move }); 352 | TOUCH.lastMove = move; 353 | } 354 | }; 355 | 356 | _handleWheel(e) { 357 | const delta = Math.abs(e.deltaY) === 125 ? e.deltaY * -120 : e.deltaY < 0 ? -600000 : 600000; 358 | const count = Math.ceil(Math.abs(delta) / 120); 359 | 360 | if (count > 0) { 361 | const sign = Math.abs(delta) / delta; 362 | let func = null; 363 | 364 | if (sign > 0 && this._hasPrevFigure()) { 365 | e.preventDefault(); 366 | func = this._handlePrevFigure(); 367 | } else if (sign < 0 && this._hasNextFigure()) { 368 | e.preventDefault(); 369 | func = this._handleNextFigure(); 370 | } 371 | 372 | if (typeof func === 'function') { 373 | for (let i = 0; i < count; i++) func(); 374 | } 375 | } 376 | } 377 | 378 | _handleTouchStart(e) { 379 | TOUCH.lastX = e.nativeEvent.touches[0].clientX; 380 | TOUCH.lastMove = this.state.move; 381 | } 382 | 383 | _handleTouchMove(e) { 384 | e.preventDefault(); 385 | const { displayQuantityOfSide } = this.props; 386 | const { width } = this.state; 387 | 388 | const clientX = e.nativeEvent.touches[0].clientX; 389 | const lastX = TOUCH.lastX; 390 | const baseWidth = width / (displayQuantityOfSide * 2 + 1); 391 | const move = clientX - lastX; 392 | const totalMove = TOUCH.lastMove - move; 393 | const sign = Math.abs(move) / move; 394 | 395 | if (Math.abs(totalMove) >= baseWidth) { 396 | let fn = null; 397 | if (sign > 0) { 398 | fn = this._handlePrevFigure(); 399 | } else if (sign < 0) { 400 | fn = this._handleNextFigure(); 401 | } 402 | if (typeof fn === 'function') { 403 | fn(); 404 | } 405 | } 406 | } 407 | } 408 | 409 | Coverflow.displayName = 'Coverflow'; 410 | 411 | export default Radium(Coverflow); 412 | -------------------------------------------------------------------------------- /src/stylesheets/coverflow.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | position: relative; 3 | background: rgba(0, 0, 0, .1); 4 | margin: 0 auto; 5 | padding: 0; 6 | overflow: hidden; 7 | outline: none; 8 | } 9 | 10 | .coverflow { 11 | position: relative; 12 | width: 100%; 13 | height: 100%; 14 | margin: 0; 15 | } 16 | 17 | .stage { 18 | height: 100%; 19 | width: 100%; 20 | display: flex; 21 | justify-content: center; 22 | margin: 0; 23 | transform-style: preserve-3d; 24 | perspective: 500px; 25 | } 26 | 27 | .figure { 28 | display: block; 29 | position: relative; 30 | margin: 0; 31 | padding: 0; 32 | flex: 0 0 auto; 33 | cursor: pointer; 34 | transition: transform 600ms ease; 35 | backface-visibility: hidden; 36 | z-index: 9; 37 | align-self: center; 38 | box-shadow: 0 50px 70px rgba(0,0,0,0.5); 39 | // TODO: box-reflect crashing the app. Resolve issue 40 | -webkit-box-reflect: below 1px -webkit-linear-gradient(bottom, rgba(black, .6) , rgba(black, .1) 20%, rgba(black, 0) 30%, rgba(black, 0)); 41 | } 42 | 43 | .cover { 44 | display: block; 45 | width: 100%; 46 | box-shadow:2px 2px 5px rgba(0,0,0,.5); 47 | } 48 | 49 | .preloader { 50 | display: hidden; 51 | } 52 | 53 | .text { 54 | position: absolute; 55 | bottom: 0; 56 | left: 0; 57 | right: 0; 58 | text-align: center; 59 | font-size: .9em; 60 | color: white; 61 | padding: 5px; 62 | overflow: hidden; 63 | background: rgba(0, 0, 0, .6); 64 | } 65 | 66 | @keyframes prevent { 67 | 0% { pointer-events: none; } 68 | 100% { pointer-events: auto; } 69 | } 70 | 71 | .arrowWrapper { 72 | display: flex; 73 | align-items: center; 74 | } 75 | 76 | #arrow1 .arrow { 77 | $size: 50px; 78 | $width: 2px; 79 | z-index: 999; 80 | opacity: 1 !important; 81 | 82 | cursor: pointer; 83 | display: block; 84 | width: $size / 1.414; 85 | height: $size / 1.414 * 2; 86 | // width: $size; 87 | // height: $size; 88 | position: relative; 89 | @each $direction in 'left' 'right' { 90 | &.#{$direction} { 91 | #{$direction}: $size; 92 | } 93 | } 94 | 95 | & span, 96 | &:before, 97 | &:after { 98 | background: #fff; 99 | box-shadow:2px 2px 8px rgba(0,0,0,.5); 100 | content: ''; 101 | display: block; 102 | width: $size; 103 | height: $width; 104 | position: absolute; 105 | top: calc(50% - (#{$width} / 2)); 106 | } 107 | 108 | &:before { 109 | transform: rotate(-45deg); 110 | } 111 | &:after { 112 | transform: rotate(45deg); 113 | } 114 | 115 | & span { 116 | width: 0; 117 | } 118 | &:hover span { 119 | width: $size * 1.414; 120 | } 121 | 122 | @each $direction in 'left' 'right' { 123 | &.#{$direction} { 124 | & span, 125 | &:before, 126 | &:after { 127 | margin: 0 $size / 4; 128 | #{$direction}: 0; 129 | transform-origin: #{$direction} 50%; 130 | } 131 | 132 | &:before, 133 | &:after { 134 | transition: #{$direction} 0.3s 0.05s; 135 | } 136 | 137 | & span { 138 | transition: width 0.3s, #{$direction} 0.3s 0.05s; 139 | } 140 | 141 | &:hover { 142 | & span, 143 | &:before, 144 | &:after { 145 | #{$direction}: $size / 1.414 * -1; 146 | } 147 | } 148 | } 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /test/main.js: -------------------------------------------------------------------------------- 1 | var should = require('chai').should(); 2 | 3 | describe('#test case', function () { 4 | /* TEST CASE */ 5 | }); 6 | 7 | -------------------------------------------------------------------------------- /webpack.common.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const glob = require('glob'); 3 | const webpack = require('webpack'); 4 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); 5 | 6 | const entry = {}; 7 | const mainEntryPoints = glob.sync(path.join(__dirname, './src/*.js')); 8 | 9 | entry['react-coverflow'] = mainEntryPoints; 10 | 11 | module.exports = { 12 | entry, 13 | plugins: [ 14 | new webpack.LoaderOptionsPlugin({ 15 | debug: true, 16 | test: /\.js$/, 17 | options: { 18 | eslint: { 19 | configFile: path.resolve(__dirname, '.eslintrc'), 20 | cache: false, 21 | }, 22 | }, 23 | }), 24 | ], 25 | output: { 26 | path: path.resolve(__dirname, 'dist'), 27 | filename: '[name].js', 28 | libraryTarget: 'umd', 29 | library: 'react-converflow', 30 | }, 31 | externals: [ 32 | { 33 | react: { 34 | root: 'React', 35 | commonjs2: 'react', 36 | commonjs: 'react', 37 | amd: 'react', 38 | }, 39 | }, 40 | { 41 | 'react-dom': { 42 | root: 'ReactDOM', 43 | commonjs2: 'react-dom', 44 | commonjs: 'react-dom', 45 | amd: 'react-dom', 46 | }, 47 | }, 48 | ], 49 | optimization: { 50 | minimizer: [ 51 | new UglifyJsPlugin({ 52 | cache: true, 53 | sourceMap: true, 54 | parallel: true, 55 | uglifyOptions: { 56 | compress: { 57 | inline: false, 58 | }, 59 | ecma: 6, 60 | mangle: true, 61 | }, 62 | }), 63 | ], 64 | runtimeChunk: false, 65 | splitChunks: { 66 | cacheGroups: { 67 | default: false, 68 | commons: { 69 | test: /[\\/]node_modules[\\/]/, 70 | name: 'vendor_app', 71 | chunks: 'all', 72 | minChunks: 2, 73 | }, 74 | }, 75 | }, 76 | }, 77 | resolve: { 78 | extensions: ['.js', '.jsx', '.json', '.scss', '.css'], 79 | alias: { 80 | radium: require.resolve('radium/index'), 81 | }, 82 | }, 83 | module: { 84 | rules: [ 85 | { 86 | test: /\.js$/, 87 | exclude: /node_modules/, 88 | loader: 'babel-loader', 89 | }, 90 | { 91 | test: /\.(css|scss)$/, 92 | loaders: [ 93 | 'style-loader', 94 | 'css-loader?modules&importLoaders=1&localIdentName=[name]__[local]__[hash:base64:5]', 95 | { 96 | loader: 'sass-loader', 97 | options: { 98 | minimize: true, 99 | }, 100 | }, 101 | ], 102 | }, 103 | ], 104 | }, 105 | }; 106 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const glob = require('glob'); 3 | const webpack = require('webpack'); 4 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); 5 | 6 | const plugins = [ 7 | new webpack.LoaderOptionsPlugin({ 8 | debug: true, 9 | test: /\.js$/, 10 | options: { 11 | eslint: { 12 | configFile: path.resolve(__dirname, '.eslintrc'), 13 | cache: false, 14 | }, 15 | }, 16 | }), 17 | new webpack.DefinePlugin({ 18 | 'process.env': { 19 | NODE_ENV: JSON.stringify('production'), 20 | }, 21 | }), 22 | ]; 23 | 24 | const entry = {}; 25 | const mainEntryPoints = glob.sync(path.join(__dirname, './src/*.js')); 26 | 27 | entry['react-coverflow'] = mainEntryPoints; 28 | 29 | module.exports = { 30 | mode: 'production', 31 | entry, 32 | output: { 33 | path: path.resolve(__dirname, 'dist'), 34 | filename: '[name].js', 35 | libraryTarget: 'umd', 36 | library: 'react-converflow', 37 | }, 38 | externals: [ 39 | { 40 | react: { 41 | root: 'React', 42 | commonjs2: 'react', 43 | commonjs: 'react', 44 | amd: 'react', 45 | }, 46 | }, 47 | { 48 | 'react-dom': { 49 | root: 'ReactDOM', 50 | commonjs2: 'react-dom', 51 | commonjs: 'react-dom', 52 | amd: 'react-dom', 53 | }, 54 | }, 55 | ], 56 | devtool: 'eval-source-map', 57 | resolve: { 58 | extensions: ['.js', '.jsx', '.json', '.scss', '.css'], 59 | alias: { 60 | radium: require.resolve('radium/index'), 61 | }, 62 | }, 63 | optimization: { 64 | nodeEnv: 'production', 65 | minimize: true, 66 | concatenateModules: true, 67 | minimizer: [ 68 | new UglifyJsPlugin({ 69 | cache: true, 70 | sourceMap: true, 71 | parallel: true, 72 | uglifyOptions: { 73 | compress: { 74 | inline: false, 75 | }, 76 | ecma: 6, 77 | mangle: true, 78 | }, 79 | }), 80 | ], 81 | runtimeChunk: false, 82 | splitChunks: { 83 | cacheGroups: { 84 | default: false, 85 | commons: { 86 | test: /[\\/]node_modules[\\/]/, 87 | name: 'vendor_app', 88 | chunks: 'all', 89 | minChunks: 2, 90 | }, 91 | }, 92 | }, 93 | }, 94 | module: { 95 | rules: [ 96 | { 97 | test: /\.js$/, 98 | exclude: /node_modules/, 99 | loader: 'babel-loader', 100 | }, 101 | { 102 | test: /\.(css|scss)$/, 103 | loaders: [ 104 | 'style-loader', 105 | 'css-loader?modules&importLoaders=1&localIdentName=[name]__[local]__[hash:base64:5]', 106 | { 107 | loader: 'sass-loader', 108 | options: { 109 | minimize: true, 110 | }, 111 | }, 112 | ], 113 | }, 114 | ], 115 | }, 116 | plugins, 117 | }; 118 | -------------------------------------------------------------------------------- /webpack.dev.js: -------------------------------------------------------------------------------- 1 | const merge = require('webpack-merge'); 2 | const common = require('./webpack.common.js'); 3 | 4 | module.exports = merge(common, { 5 | mode: 'development', 6 | devtool: 'inline-source-map', 7 | }); 8 | -------------------------------------------------------------------------------- /webpack.prod.js: -------------------------------------------------------------------------------- 1 | const merge = require('webpack-merge'); 2 | const common = require('./webpack.common.js'); 3 | 4 | module.exports = merge(common, { 5 | mode: 'production', 6 | devtool: 'eval-source-map', 7 | }); 8 | --------------------------------------------------------------------------------