├── .babelrc ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── example ├── example.js ├── example.scss ├── index.html ├── index.js ├── shin-chan.jpg └── webpack.config.js ├── jsconfig.json ├── package-lock.json ├── package.json ├── postcss.config.js ├── src ├── index.d.ts ├── index.js ├── scrollbar.js └── styles.scss └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "env", 4 | "react", 5 | "stage-2" 6 | ], 7 | "plugins": [ 8 | "add-module-exports" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.{js,jsx,ts,html,css,less,scss,json,xml}] 13 | indent_style = space 14 | indent_size = 2 15 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | *.d.ts 2 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | 'extends': 'airbnb', 3 | 'parser': 'babel-eslint', 4 | 5 | 'rules': { 6 | 'indent': [2, 2, { 7 | SwitchCase: 1, 8 | }], 9 | 'prefer-const': 0, 10 | 'no-param-reassign': [2, { props: false }], 11 | 'no-underscore-dangle': [2, { 12 | allowAfterThis: true, 13 | allowAfterSuper: true, 14 | }], 15 | 'react/jsx-filename-extension': 0, 16 | 'react/jsx-indent': [2, 2], 17 | 'react/jsx-indent-props': [2, 2], 18 | 'class-methods-use-this': "off", 19 | // concerned 20 | 'react/forbid-prop-types': "off", 21 | 'react/no-unused-prop-types': "off", 22 | 'no-plusplus': "off", 23 | 'no-bitwise': "off", 24 | 'react/destructuring-assignment': "off" 25 | }, 26 | 27 | 'globals': { 28 | 'document': false, 29 | }, 30 | } 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | lib/ 3 | dist/ 4 | .idea/ 5 | typings/ 6 | 7 | npm-*.log 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "8" 4 | script: 5 | - npm run build 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Allen Yang 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React-Perfect-Scrollbar [![Build Status](https://travis-ci.org/goldenyz/react-perfect-scrollbar.svg?branch=master)](https://travis-ci.org/goldenyz/react-perfect-scrollbar) [![npm](https://img.shields.io/npm/v/react-perfect-scrollbar.svg?style=flat-square)](https://www.npmjs.com/package/react-perfect-scrollbar) [![npm downloads](https://img.shields.io/npm/dm/react-perfect-scrollbar.svg)](https://www.npmjs.com/package/react-perfect-scrollbar) 2 | 3 | This is a wrapper to allow use [perfect-scrollbar](https://github.com/noraesae/perfect-scrollbar) in React. 4 | 5 | ***To read documentation for versions < 1.0, please visit [`v0.2.5`](https://github.com/goldenyz/react-perfect-scrollbar/tree/v0.2.5).*** 6 | 7 | ### Usage 8 | Install the package `npm install react-perfect-scrollbar -S` 9 | Import the css file if you have loader for css files: 10 | ```js 11 | import 'react-perfect-scrollbar/dist/css/styles.css'; 12 | ``` 13 | 14 | Import the module in the place you want to use: 15 | ```js 16 | import PerfectScrollbar from 'react-perfect-scrollbar' 17 | ``` 18 | 19 | Wrap you content in this component: 20 | ```jsx 21 | 22 | ... SCROLLBAR CONTENT HERE ... 23 | 24 | ``` 25 | 26 | ### Props 27 | The following props are accepted: 28 | #### options 29 | The optional parameters used to initialize [perfect-scrollbar](https://github.com/utatti/perfect-scrollbar). 30 | For more info, please refer to https://github.com/utatti/perfect-scrollbar#options 31 | 32 | This prop previously was called "option", but has since been renamed. 33 | If you provide "option" as a prop, it will be used unless "options" is also passed. 34 | 35 | #### containerRef 36 | Return the container ref: (ref) => void; 37 | If you want to scroll to somewhere, just update scrollTop/scrollLeft by the ref: 38 | ```js 39 | // Suppose you have save the containerRef to this._scrollRef 40 | // change scroll top 41 | this._scrollRef.scrollTop = 0; 42 | 43 | // change scroll left 44 | this._scrollRef.scrollLeft = 0; 45 | ``` 46 | 47 | #### component 48 | The container component type. Default to "div". Only string is allowed. 49 | #### className 50 | The className added to container. 51 | #### style 52 | The style added to container. 53 | #### onScrollY 54 | Invoked when the y-axis is scrolled in either direction. 55 | #### onScrollX 56 | Invoked when the x-axis is scrolled in either direction. 57 | #### onScrollUp 58 | Invoked when scrolling upwards. 59 | #### onScrollDown 60 | Invoked when scrolling downwards. 61 | #### onScrollLeft 62 | Invoked when scrolling to the left. 63 | #### onScrollRight 64 | Invoked when scrolling to the right. 65 | #### onYReachStart 66 | Invoked when scrolling reaches the start of the y-axis. 67 | #### onYReachEnd 68 | Invoked when scrolling reaches the end of the y-axis (useful for infinite scroll). 69 | #### onXReachStart 70 | Invoked when scrolling reaches the start of the x-axis. 71 | #### onXReachEnd 72 | Invoked when scrolling reaches the end of the x-axis. 73 | 74 | All the callback 'onXXXX' can accept a parameter: the ref to the scrollbar container. You can get the current `scrollTop` and `scrollLeft` from it: 75 | ```jsx 76 | console.log(`scrolled to: ${container.scrollTop}.`)}> 78 | ... SCROLLBAR CONTENT HERE ... 79 | 80 | ``` 81 | 82 | #### onSync 83 | Invoked when `PerfectScrollbar` comp needs to sync the scrollbar container by invoking `ps.update()`(Basically, it is invoked in CDU lifecycle) and receive the internal `perfect-scroll` instance `ps` as parameter. 84 | 85 | It is useful when you want to customize the sync logic in some scenarios, eg: debounce the invocation of `ps.update()`. 86 | 87 | For more detail, please refer to [issue#87](https://github.com/goldenyz/react-perfect-scrollbar/issues/87) and the example directory. 88 | 89 | #### React.HTMLAttributes 90 | Any attributes defined in [React.HTMLAttributes](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react/index.d.ts#L1689) can be used for the component. 91 | 92 | ### Methods 93 | The following method can be called by the component ref: 94 | #### updateScroll 95 | Update the scrollbar(e.g. recalculate the size) manually. 96 | In the following case, the scrollbar will not update automatically, which cause the scrollbar size incorrect. 97 | ```js 98 | class Container extends Component { 99 | ... 100 | render() { 101 | return ( 102 | 103 | ... 104 | 105 | ... 106 | 107 | ); 108 | } 109 | } 110 | 111 | class ChildComponent extends Component { 112 | handleClick = () => { 113 | this.setState({ 114 | show: !this.state.show, 115 | }); 116 | } 117 | 118 | render () { 119 | return ( 120 |
121 | 82 | 83 | {this.state.items.map(e => (
{e}
))} 84 |
85 |
86 | 87 | ); 88 | } 89 | } 90 | 91 | export default Example; 92 | -------------------------------------------------------------------------------- /example/example.scss: -------------------------------------------------------------------------------- 1 | .example { 2 | width: 400px; 3 | height: 400px; 4 | 5 | .content { 6 | background: url('./shin-chan.jpg') top left no-repeat; 7 | width: 800px; 8 | height: 480px; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import Example from './example'; 4 | 5 | ReactDOM.render(, document.getElementById('root')); 6 | -------------------------------------------------------------------------------- /example/shin-chan.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goldenyz/react-perfect-scrollbar/81853fe723b3a2b28d7753cb0454fd4db960fda2/example/shin-chan.jpg -------------------------------------------------------------------------------- /example/webpack.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | const path = require('path'); 3 | const webpack = require('webpack'); 4 | 5 | module.exports = { 6 | 7 | entry: path.join(__dirname, 'index.js'), 8 | 9 | output: { 10 | path: __dirname, 11 | filename: 'bundle.js', 12 | publicPath: '/dist/', 13 | }, 14 | 15 | resolve: { 16 | alias: { 17 | 'react-perfect-scrollbar': path.join(__dirname, '../src'), 18 | }, 19 | }, 20 | 21 | module: { 22 | rules: [ 23 | { 24 | test: /\.(jsx|js)$/, 25 | exclude: /node_modules/, 26 | loader: 'babel-loader', 27 | }, 28 | { 29 | test: /\.(jpg|png|svg|ttf|eot)$/, 30 | loader: 'file-loader', 31 | options: { 32 | name: 'img/[hash].[ext]', 33 | }, 34 | }, 35 | { 36 | test: /\.scss$/, 37 | use: [ 38 | 'style-loader', 39 | 'css-loader', 40 | 'postcss-loader', 41 | 'sass-loader', 42 | ], 43 | }, 44 | { 45 | test: /\.css$/, 46 | use: [ 47 | 'style-loader', 48 | 'css-loader', 49 | ], 50 | }, 51 | ], 52 | }, 53 | 54 | devServer: { 55 | open: true, 56 | }, 57 | 58 | devtool: 'source-map', 59 | }; 60 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES6", 4 | "module": "commonjs", 5 | "allowSyntheticDefaultImports": true 6 | }, 7 | "exclude": [ 8 | "lib", 9 | "node_modules" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-perfect-scrollbar", 3 | "version": "1.5.8", 4 | "description": "A react wrapper for perfect-scrollbar", 5 | "main": "lib/index.js", 6 | "typings": "lib/index.d.ts", 7 | "scripts": { 8 | "clean": "rimraf lib && rimraf dist", 9 | "build:lib": "babel src --out-dir lib && copyfiles src/index.d.ts lib -u 1", 10 | "build:umd": "cross-env NODE_ENV=development webpack --bail", 11 | "build:umd:min": "cross-env NODE_ENV=production webpack --bail", 12 | "build": "npm run clean && npm run lint && npm run build:lib && npm run build:umd && npm run build:umd:min", 13 | "example": "webpack-dev-server --content-base example/ --config example/webpack.config.js", 14 | "lint": "eslint src", 15 | "test": "echo \"Test: TBD\"", 16 | "prepare": "npm run build", 17 | "postversion": "git push && git push --tags" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/goldenyz/react-perfect-scrollbar.git" 22 | }, 23 | "keywords": [ 24 | "react", 25 | "scrollbar", 26 | "perfect-scrollbar", 27 | "react-scrollbar" 28 | ], 29 | "files": [ 30 | "dist", 31 | "lib", 32 | "src" 33 | ], 34 | "author": "Allen Yang", 35 | "license": "MIT", 36 | "peerDependencies": { 37 | "react": ">=16.3.3", 38 | "react-dom": ">=16.3.3" 39 | }, 40 | "dependencies": { 41 | "perfect-scrollbar": "^1.5.0", 42 | "prop-types": "^15.6.1" 43 | }, 44 | "devDependencies": { 45 | "autoprefixer": "^6.6.1", 46 | "babel-cli": "^6.26.0", 47 | "babel-core": "^6.26.0", 48 | "babel-eslint": "^8.2.2", 49 | "babel-loader": "^7.1.3", 50 | "babel-plugin-add-module-exports": "^0.2.1", 51 | "babel-preset-env": "^1.6.1", 52 | "babel-preset-react": "^6.24.1", 53 | "babel-preset-stage-2": "^6.24.1", 54 | "copyfiles": "^2.0.0", 55 | "cross-env": "^5.1.4", 56 | "css-loader": "^0.28.11", 57 | "eslint": "^4.19.1", 58 | "eslint-config-airbnb": "^16.1.0", 59 | "eslint-plugin-import": "^2.10.0", 60 | "eslint-plugin-jsx-a11y": "^6.0.3", 61 | "eslint-plugin-react": "^7.7.0", 62 | "extract-text-webpack-plugin": "^3.0.2", 63 | "file-loader": "^1.1.11", 64 | "node-sass": "^4.13.1", 65 | "postcss-loader": "^2.1.3", 66 | "react": "^16.3.1", 67 | "react-dom": "^16.3.1", 68 | "rimraf": "^2.6.1", 69 | "sass-loader": "^6.0.7", 70 | "style-loader": "^0.20.3", 71 | "webpack": "^3.x", 72 | "webpack-dev-server": "^2.11.5" 73 | }, 74 | "bugs": { 75 | "url": "https://github.com/goldenyz/react-perfect-scrollbar/issues" 76 | }, 77 | "homepage": "https://github.com/goldenyz/react-perfect-scrollbar" 78 | } 79 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | module.exports = { 3 | plugins: [ 4 | require('autoprefixer')({ /* ...options */ }) 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /src/index.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | import * as React from 'react'; 5 | import PerfectScrollbar from 'perfect-scrollbar'; 6 | 7 | export interface ScrollBarProps extends React.HTMLAttributes { 8 | /** 9 | * class name on container 10 | */ 11 | className?: string; 12 | 13 | /** 14 | * style on container 15 | */ 16 | style?: React.CSSProperties; 17 | 18 | /** 19 | * perfect-scrollbar init options 20 | * 21 | * @deprecated in favor of {@link #options} 22 | */ 23 | option?: PerfectScrollbar.Options; 24 | 25 | /** 26 | * perfect-scrollbar init options 27 | */ 28 | options?: PerfectScrollbar.Options; 29 | 30 | /** 31 | * get the container ref 32 | */ 33 | containerRef?: ((container: HTMLElement) => void); 34 | 35 | /** 36 | * fires when the y-axis is scrolled in either direction. 37 | */ 38 | onScrollY?: ((container: HTMLElement) => void); 39 | 40 | /** 41 | * fires when the x-axis is scrolled in either direction. 42 | */ 43 | onScrollX?: ((container: HTMLElement) => void); 44 | 45 | /** 46 | * fires when scrolling upwards. 47 | */ 48 | onScrollUp?: ((container: HTMLElement) => void); 49 | 50 | /** 51 | * fires when scrolling downwards. 52 | */ 53 | onScrollDown?: ((container: HTMLElement) => void); 54 | 55 | /** 56 | * fires when scrolling to the left. 57 | */ 58 | onScrollLeft?: ((container: HTMLElement) => void); 59 | 60 | /** 61 | * fires when scrolling to the right. 62 | */ 63 | onScrollRight?: ((container: HTMLElement) => void); 64 | 65 | /** 66 | * fires when scrolling reaches the start of the y-axis. 67 | */ 68 | onYReachStart?: ((container: HTMLElement) => void); 69 | 70 | /** 71 | * fires when scrolling reaches the end of the y-axis (useful for infinite scroll). 72 | */ 73 | onYReachEnd?: ((container: HTMLElement) => void); 74 | 75 | /** 76 | * fires when scrolling reaches the start of the x-axis. 77 | */ 78 | onXReachStart?: ((container: HTMLElement) => void); 79 | 80 | /** 81 | * fires when scrolling reaches the end of the x-axis. 82 | */ 83 | onXReachEnd?: ((container: HTMLElement) => void); 84 | 85 | /** 86 | * component name 87 | */ 88 | component?: string; 89 | } 90 | 91 | declare class ScrollBar extends React.Component { 92 | updateScroll(): void; 93 | } 94 | 95 | export default ScrollBar; 96 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import Scrollbar from './scrollbar'; 2 | 3 | export default Scrollbar; 4 | -------------------------------------------------------------------------------- /src/scrollbar.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { PropTypes } from 'prop-types'; 3 | import PerfectScrollbar from 'perfect-scrollbar'; 4 | 5 | const handlerNameByEvent = { 6 | 'ps-scroll-y': 'onScrollY', 7 | 'ps-scroll-x': 'onScrollX', 8 | 'ps-scroll-up': 'onScrollUp', 9 | 'ps-scroll-down': 'onScrollDown', 10 | 'ps-scroll-left': 'onScrollLeft', 11 | 'ps-scroll-right': 'onScrollRight', 12 | 'ps-y-reach-start': 'onYReachStart', 13 | 'ps-y-reach-end': 'onYReachEnd', 14 | 'ps-x-reach-start': 'onXReachStart', 15 | 'ps-x-reach-end': 'onXReachEnd', 16 | }; 17 | Object.freeze(handlerNameByEvent); 18 | 19 | export default class ScrollBar extends Component { 20 | constructor(props) { 21 | super(props); 22 | 23 | this.handleRef = this.handleRef.bind(this); 24 | this._handlerByEvent = {}; 25 | } 26 | 27 | componentDidMount() { 28 | if (this.props.option) { 29 | console.warn('react-perfect-scrollbar: the "option" prop has been deprecated in favor of "options"'); 30 | } 31 | 32 | this._ps = new PerfectScrollbar(this._container, this.props.options || this.props.option); 33 | // hook up events 34 | this._updateEventHook(); 35 | this._updateClassName(); 36 | } 37 | 38 | componentDidUpdate(prevProps) { 39 | this._updateEventHook(prevProps); 40 | 41 | this.updateScroll(); 42 | 43 | if (prevProps.className !== this.props.className) { 44 | this._updateClassName(); 45 | } 46 | } 47 | 48 | componentWillUnmount() { 49 | // unhook up evens 50 | Object.keys(this._handlerByEvent).forEach((key) => { 51 | const value = this._handlerByEvent[key]; 52 | 53 | if (value) { 54 | this._container.removeEventListener(key, value, false); 55 | } 56 | }); 57 | this._handlerByEvent = {}; 58 | this._ps.destroy(); 59 | this._ps = null; 60 | } 61 | 62 | _updateEventHook(prevProps = {}) { 63 | // hook up events 64 | Object.keys(handlerNameByEvent).forEach((key) => { 65 | const callback = this.props[handlerNameByEvent[key]]; 66 | const prevCallback = prevProps[handlerNameByEvent[key]]; 67 | if (callback !== prevCallback) { 68 | if (prevCallback) { 69 | const prevHandler = this._handlerByEvent[key]; 70 | this._container.removeEventListener(key, prevHandler, false); 71 | this._handlerByEvent[key] = null; 72 | } 73 | if (callback) { 74 | const handler = () => callback(this._container); 75 | this._container.addEventListener(key, handler, false); 76 | this._handlerByEvent[key] = handler; 77 | } 78 | } 79 | }); 80 | } 81 | 82 | _updateClassName() { 83 | const { className } = this.props; 84 | 85 | const psClassNames = this._container.className.split(' ') 86 | .filter(name => name.match(/^ps([-_].+|)$/)) 87 | .join(' '); 88 | 89 | if (this._container) { 90 | this._container.className = `scrollbar-container${className ? ` ${className}` : ''}${psClassNames ? ` ${psClassNames}` : ''}`; 91 | } 92 | } 93 | 94 | updateScroll() { 95 | this.props.onSync(this._ps); 96 | } 97 | 98 | handleRef(ref) { 99 | this._container = ref; 100 | this.props.containerRef(ref); 101 | } 102 | 103 | render() { 104 | const { 105 | className, 106 | style, 107 | option, 108 | options, 109 | containerRef, 110 | onScrollY, 111 | onScrollX, 112 | onScrollUp, 113 | onScrollDown, 114 | onScrollLeft, 115 | onScrollRight, 116 | onYReachStart, 117 | onYReachEnd, 118 | onXReachStart, 119 | onXReachEnd, 120 | component, 121 | onSync, 122 | children, 123 | ...remainProps 124 | } = this.props; 125 | const Comp = component; 126 | 127 | return ( 128 | 129 | {children} 130 | 131 | ); 132 | } 133 | } 134 | 135 | ScrollBar.defaultProps = { 136 | className: '', 137 | style: undefined, 138 | option: undefined, 139 | options: undefined, 140 | containerRef: () => { }, 141 | onScrollY: undefined, 142 | onScrollX: undefined, 143 | onScrollUp: undefined, 144 | onScrollDown: undefined, 145 | onScrollLeft: undefined, 146 | onScrollRight: undefined, 147 | onYReachStart: undefined, 148 | onYReachEnd: undefined, 149 | onXReachStart: undefined, 150 | onXReachEnd: undefined, 151 | onSync: ps => ps.update(), 152 | component: 'div', 153 | }; 154 | 155 | ScrollBar.propTypes = { 156 | children: PropTypes.node.isRequired, 157 | className: PropTypes.string, 158 | style: PropTypes.object, 159 | option: PropTypes.object, 160 | options: PropTypes.object, 161 | containerRef: PropTypes.func, 162 | onScrollY: PropTypes.func, 163 | onScrollX: PropTypes.func, 164 | onScrollUp: PropTypes.func, 165 | onScrollDown: PropTypes.func, 166 | onScrollLeft: PropTypes.func, 167 | onScrollRight: PropTypes.func, 168 | onYReachStart: PropTypes.func, 169 | onYReachEnd: PropTypes.func, 170 | onXReachStart: PropTypes.func, 171 | onXReachEnd: PropTypes.func, 172 | onSync: PropTypes.func, 173 | component: PropTypes.string, 174 | }; 175 | -------------------------------------------------------------------------------- /src/styles.scss: -------------------------------------------------------------------------------- 1 | @import '../node_modules/perfect-scrollbar/css/perfect-scrollbar.css'; 2 | 3 | .scrollbar-container { 4 | position: relative; 5 | height: 100%; 6 | } 7 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | const ExtractTextPlugin = require('extract-text-webpack-plugin'); 4 | 5 | const { UglifyJsPlugin } = webpack.optimize; 6 | 7 | const env = process.env.NODE_ENV; 8 | const libraryName = '[name]'; 9 | 10 | const plugins = [ 11 | new webpack.DefinePlugin({ 12 | 'process.env.NODE_ENV': JSON.stringify(env), 13 | }), 14 | ]; 15 | 16 | let outputFile; 17 | if (env === 'production') { 18 | plugins.push(new UglifyJsPlugin({ 19 | sourceMap: true, 20 | compress: { 21 | warnings: true, 22 | }, 23 | })); 24 | plugins.push(new webpack.LoaderOptionsPlugin({ 25 | minimize: true, 26 | })); 27 | outputFile = `${libraryName}.min.js`; 28 | plugins.push(new ExtractTextPlugin({ 29 | filename: 'css/styles.min.css', 30 | })); 31 | } else { 32 | outputFile = `${libraryName}.js`; 33 | plugins.push(new ExtractTextPlugin({ 34 | filename: 'css/styles.css', 35 | })); 36 | } 37 | 38 | module.exports = { 39 | 40 | entry: { 41 | 'react-perfect-scrollbar': [ 42 | path.join(__dirname, '/src/index.js'), 43 | path.join(__dirname, '/src/styles.scss'), 44 | ], 45 | }, 46 | 47 | devtool: 'source-map', 48 | 49 | output: { 50 | path: path.join(__dirname, 'dist'), 51 | filename: outputFile, 52 | library: libraryName, 53 | libraryTarget: 'umd', 54 | umdNamedDefine: true, 55 | }, 56 | 57 | resolve: { 58 | modules: [ 59 | path.join(__dirname, './'), 60 | 'node_modules', 61 | ], 62 | 63 | extensions: ['.js', '.jsx'], 64 | }, 65 | 66 | module: { 67 | rules: [ 68 | { 69 | test: /\.(jsx|js)$/, 70 | exclude: /(node_modules|bower_components)/, 71 | use: { 72 | loader: 'babel-loader', 73 | }, 74 | }, 75 | { 76 | test: /\.scss$/, 77 | use: ExtractTextPlugin.extract({ 78 | fallback: 'style-loader', 79 | use: [ 80 | { 81 | loader: 'css-loader', 82 | options: { 83 | sourceMap: true, 84 | minimize: false, 85 | }, 86 | }, 87 | { 88 | loader: 'postcss-loader', 89 | options: { 90 | sourceMap: true, 91 | }, 92 | }, 93 | { 94 | loader: 'sass-loader', 95 | options: { 96 | sourceMap: true, 97 | }, 98 | }, 99 | ], 100 | }), 101 | }, 102 | ], 103 | }, 104 | 105 | externals: [ 106 | { 107 | react: { 108 | root: 'React', 109 | commonjs2: 'react', 110 | commonjs: 'react', 111 | amd: 'react', 112 | }, 113 | }, 114 | { 115 | 'react-dom': { 116 | root: 'ReactDOM', 117 | commonjs2: 'react-dom', 118 | commonjs: 'react-dom', 119 | amd: 'react-dom', 120 | }, 121 | }, 122 | { 123 | 'prop-types': { 124 | root: 'PropTypes', 125 | commonjs2: 'prop-types', 126 | commonjs: 'prop-types', 127 | amd: 'prop-types', 128 | }, 129 | }, 130 | ], 131 | 132 | plugins, 133 | }; 134 | --------------------------------------------------------------------------------