├── .gitignore ├── README.md ├── examples ├── app.css ├── assets │ ├── ghostface.jpg │ ├── montserrat-hairline-webfont.woff │ └── montserrat-regular-webfont.woff ├── index.html ├── index.jsx ├── views │ └── home │ │ ├── examples │ │ ├── both-scrollbar.jsx │ │ ├── custom-scrollbar.css │ │ ├── custom-scrollbar.jsx │ │ ├── horizontal-scrollbar.css │ │ ├── horizontal-scrollbar.jsx │ │ ├── vertical-scrollbar.css │ │ └── vertical-scrollbar.jsx │ │ ├── home-view.css │ │ ├── home-view.jsx │ │ └── index.jsx └── webpack.config.js ├── lib ├── components │ ├── button.js │ ├── scrollbar-wrapper.js │ └── scrollbar.js ├── index.js └── mixins │ └── scrollbar.js ├── package.json ├── src ├── components │ ├── button.jsx │ ├── scrollbar-wrapper.jsx │ └── scrollbar.jsx ├── index.jsx ├── mixins │ └── scrollbar.jsx └── utils │ └── offsets.js └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | bundle.js 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Installation 2 | 3 | ``` 4 | npm install react-scrollbars --save 5 | ``` 6 | 7 | ## Basic usage 8 | 9 | ``` 10 | var React = require('react'); 11 | var ScrollbarWrapper = require('react-scrollbars').ScrollbarWrapper; 12 | 13 | var Component = React.createClass({ 14 | render: function() { 15 | return ( 16 | 17 |
18 | content 19 |
20 |
21 | ); 22 | } 23 | }); 24 | 25 | module.exports = Component; 26 | ``` 27 | 28 | ## Properties 29 | 30 | ``` 31 | vertical={true} 32 | ``` 33 | 34 | **values**: `true`, `false` 35 | 36 | Force hide the scrollbars on the y-axis, regardless of what `overflow` value is set in the CSS. Works best with `overflow-y: hidden`. 37 | 38 | ``` 39 | horizontal={true} 40 | ``` 41 | 42 | **values**: `true`, `false` 43 | 44 | Force hide the scrollbars on the x-axis, regardless of what `overflow` value is set in the CSS. Works best with `overflow-x: hidden`. 45 | 46 | ``` 47 | offest={2} 48 | ``` 49 | 50 | **values**: any integer 51 | 52 | Changes the space (margin) around the scrollbar component. 53 | 54 | ``` 55 | scrollbarThickness={10} 56 | ``` 57 | 58 | **values**: any integer 59 | 60 | Controls the `width` (in y-axis) or `height` (in x-axis) of the scrollbar. 61 | 62 | 63 | ## Styles 64 | 65 | For optimized behavior add the following definitions to your stylesheets: 66 | 67 | ``` 68 | .ScrollbarContainer--scrolling { 69 | -webkit-touch-callout: none; 70 | user-select: none; 71 | } 72 | 73 | .ScrollbarContainer>div::-webkit-scrollbar { 74 | width: 0; 75 | height: 0; 76 | } 77 | 78 | .ScrollbarContainer>div::scrollbar { 79 | width: 0; 80 | height: 0; 81 | } 82 | ``` 83 | -------------------------------------------------------------------------------- /examples/app.css: -------------------------------------------------------------------------------- 1 | html { 2 | background: #ecf0f1; 3 | color: #1ABC9C; 4 | font-family: "montserrat", Helvetica, sans-serif; 5 | font-weight: lighter; 6 | } 7 | 8 | .ScrollbarContainer--scrolling { 9 | -webkit-touch-callout: none; 10 | user-select: none; 11 | } 12 | 13 | .ScrollbarContainer>div::-webkit-scrollbar { 14 | width: 0; 15 | height: 0; 16 | } 17 | 18 | .ScrollbarContainer>div::scrollbar { 19 | width: 0; 20 | height: 0; 21 | } 22 | 23 | @font-face { 24 | font-family: 'montserrat'; 25 | src: url('./assets/montserrat-hairline-webfont.woff') format('woff'); 26 | font-weight: lighter; 27 | font-style: normal; 28 | } 29 | 30 | @font-face { 31 | font-family: 'montserrat'; 32 | src: url('./assets/montserrat-regular-webfont.woff') format('woff'); 33 | font-weight: normal; 34 | font-style: normal; 35 | } -------------------------------------------------------------------------------- /examples/assets/ghostface.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojame/react-scrollbars/55e176185141938d140ca51e64dee410737d5c55/examples/assets/ghostface.jpg -------------------------------------------------------------------------------- /examples/assets/montserrat-hairline-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojame/react-scrollbars/55e176185141938d140ca51e64dee410737d5c55/examples/assets/montserrat-hairline-webfont.woff -------------------------------------------------------------------------------- /examples/assets/montserrat-regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojame/react-scrollbars/55e176185141938d140ca51e64dee410737d5c55/examples/assets/montserrat-regular-webfont.woff -------------------------------------------------------------------------------- /examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | React Scrollbars 5 | 6 | 7 |
8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/index.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 'use strict' 3 | var React = require('react'); 4 | 5 | var Home = require('./views/home/index.jsx'); 6 | 7 | require('normalize.css/normalize.css'); 8 | require('./app.css'); 9 | 10 | var App = React.createClass({ 11 | render: function () { 12 | return ( 13 |
14 |
15 |
16 | 17 |
18 | 19 |
20 |
21 | ); 22 | } 23 | }); 24 | 25 | React.render(, document.body); 26 | -------------------------------------------------------------------------------- /examples/views/home/examples/both-scrollbar.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var ScrollbarWrapper = require('react-scrollbar').ScrollbarWrapper; 3 | require('./vertical-scrollbar.css'); 4 | 5 | var BothScrollbar = React.createClass({ 6 | render: function() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | }); 14 | 15 | module.exports = BothScrollbar; 16 | 17 | -------------------------------------------------------------------------------- /examples/views/home/examples/custom-scrollbar.css: -------------------------------------------------------------------------------- 1 | .ScrollbarContent--custom { 2 | width: 100%; 3 | height: 300px; 4 | overflow: auto; 5 | background: rgb(253, 253, 253); 6 | } 7 | 8 | .ScrollbarContent--custom:hover .Scrollbar { 9 | opacity: 1 !important; 10 | } 11 | 12 | .ScrollbarContent--custom .Scrollbar { 13 | background: #3498db !important; 14 | opacity: 0 !important; 15 | transition: opacity 0.1s ease; 16 | } 17 | 18 | .ScrollbarContent--custom .Scrollbar-stick { 19 | background: #2c3e50 !important; 20 | } -------------------------------------------------------------------------------- /examples/views/home/examples/custom-scrollbar.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var ScrollbarWrapper = require('react-scrollbar').ScrollbarWrapper; 3 | require('./custom-scrollbar.css'); 4 | 5 | var VerticalScrollbar = React.createClass({ 6 | render: function() { 7 | return ( 8 | 9 |
10 |

Dennis Coles (born May 9, 1970), better known by his stage name Ghostface Killah, is an American rapper and prominent member of the Wu-Tang Clan. After the group achieved breakthrough success in the aftermath of Enter the Wu-Tang (36 Chambers), the members went on to pursue solo careers to varying levels of success. Ghostface Killah debuted his solo-career with Ironman in 1996, which was well received by music critics. He has continued his success over the following years with critically acclaimed albums such as Supreme Clientele (2000) and FishScale (2006). His stage name was taken from one of the characters in the 1979 kung fu film Mystery of Chessboxing. He is the founder of his own label Starks Enterprises.

11 |

Ghostface Killah is critically acclaimed for his loud, fast-paced flow, and his emotional stream-of-consciousness narratives containing cryptic slang and non-sequiturs. In 2006, MTV included him on their honorable mention list of The Greatest MCs of All Time, while the editors of About.com placed him on their list of the Top 50 MCs of Our Time (1987–2007), calling him "one of the most imaginative storytellers of our time." Q magazine called him "rap's finest storyteller."[11] Pitchfork Media stated that, "Ghostface has unparalleled storytelling instincts; he might be the best, most colorful storyteller rap has ever seen." NPR called him "a compulsive storyteller", and asserted, "His fiction is painterly."

12 |
13 |
14 | ); 15 | } 16 | }); 17 | 18 | module.exports = VerticalScrollbar; 19 | -------------------------------------------------------------------------------- /examples/views/home/examples/horizontal-scrollbar.css: -------------------------------------------------------------------------------- 1 | .ScrollbarContent--horizontal { 2 | width: 100%; 3 | height: 300px; 4 | overflow-x: auto; 5 | overflow-y: hidden; 6 | background: rgb(253, 253, 253); 7 | } 8 | 9 | .ScrollbarContent--horizontal-columnContainer { 10 | white-space: nowrap; 11 | } 12 | 13 | .ScrollbarContent--horizontal-column { 14 | border-radius: 5px; 15 | font-size: 18px; 16 | background: #3498db; 17 | display: inline-block; 18 | width: 300px; 19 | margin: 1em; 20 | padding: 100px 0; 21 | text-align: center; 22 | } -------------------------------------------------------------------------------- /examples/views/home/examples/horizontal-scrollbar.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var ScrollbarWrapper = require('react-scrollbar').ScrollbarWrapper; 3 | require('./horizontal-scrollbar.css'); 4 | 5 | var HorizontalScrollbar = React.createClass({ 6 | render: function() { 7 | return ( 8 | 9 |
10 |
11 | Love 12 |
13 | 14 |
15 | Don`t 16 |
17 | 18 |
19 | Live 20 |
21 | 22 |
23 | Here 24 |
25 | 26 |
27 | No 28 |
29 | 30 |
31 | More 32 |
33 |
34 |
35 | ); 36 | } 37 | }); 38 | 39 | module.exports = HorizontalScrollbar; 40 | 41 | -------------------------------------------------------------------------------- /examples/views/home/examples/vertical-scrollbar.css: -------------------------------------------------------------------------------- 1 | .ScrollbarContent--vertical { 2 | width: 100%; 3 | height: 300px; 4 | overflow: auto; 5 | background: rgb(253, 253, 253); 6 | } -------------------------------------------------------------------------------- /examples/views/home/examples/vertical-scrollbar.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var ScrollbarWrapper = require('react-scrollbar').ScrollbarWrapper; 3 | require('./vertical-scrollbar.css'); 4 | 5 | var VerticalScrollbar = React.createClass({ 6 | render: function() { 7 | return ( 8 | 9 |
10 |

Dennis Coles (born May 9, 1970), better known by his stage name Ghostface Killah, is an American rapper and prominent member of the Wu-Tang Clan. After the group achieved breakthrough success in the aftermath of Enter the Wu-Tang (36 Chambers), the members went on to pursue solo careers to varying levels of success. Ghostface Killah debuted his solo-career with Ironman in 1996, which was well received by music critics. He has continued his success over the following years with critically acclaimed albums such as Supreme Clientele (2000) and FishScale (2006). His stage name was taken from one of the characters in the 1979 kung fu film Mystery of Chessboxing. He is the founder of his own label Starks Enterprises.

11 |

Ghostface Killah is critically acclaimed for his loud, fast-paced flow, and his emotional stream-of-consciousness narratives containing cryptic slang and non-sequiturs. In 2006, MTV included him on their honorable mention list of The Greatest MCs of All Time, while the editors of About.com placed him on their list of the Top 50 MCs of Our Time (1987–2007), calling him "one of the most imaginative storytellers of our time." Q magazine called him "rap's finest storyteller."[11] Pitchfork Media stated that, "Ghostface has unparalleled storytelling instincts; he might be the best, most colorful storyteller rap has ever seen." NPR called him "a compulsive storyteller", and asserted, "His fiction is painterly."

12 |
13 |
14 | ); 15 | } 16 | }); 17 | 18 | module.exports = VerticalScrollbar; 19 | -------------------------------------------------------------------------------- /examples/views/home/home-view.css: -------------------------------------------------------------------------------- 1 | .Home { 2 | color: #2c3e50; 3 | } 4 | 5 | .Home-header { 6 | padding: 1em; 7 | background: #3498db; 8 | overflow: hidden; 9 | } 10 | 11 | .Home-header h1 { 12 | font-size: 18px; 13 | text-align: left; 14 | margin: 0; 15 | float: left; 16 | font-weight: normal; 17 | } 18 | 19 | .Home-header h2 { 20 | font-size: 18px; 21 | text-align: right; 22 | margin: 0; 23 | float: right; 24 | } 25 | 26 | .Home-header h2 a:hover { 27 | color: inherit; 28 | text-decoration: none; 29 | } 30 | 31 | .Home-container { 32 | width: 80%; 33 | margin: 3em auto; 34 | max-width: 1200px; 35 | overflow: hidden; 36 | } 37 | 38 | .Home-navigation { 39 | float: left; 40 | width: 30%; 41 | margin-right: 5%; 42 | } 43 | 44 | p { 45 | padding: 0 0 1em 0; 46 | } 47 | 48 | nav { 49 | border-top: 1px solid #bdc3c7; 50 | padding-top: 1em; 51 | } 52 | 53 | ul { 54 | margin: 0; 55 | padding: 0; 56 | } 57 | 58 | li { 59 | list-style: none; 60 | margin: 0.5em 0; 61 | } 62 | 63 | .Home-content { 64 | float: left; 65 | width: 65%; 66 | } 67 | 68 | .Home-content-block { 69 | margin: 0 0 6em 0; 70 | } 71 | 72 | h3 { 73 | font-size: 25px; 74 | border-bottom: 1px solid #bdc3c7; 75 | padding: 0 0 0.5em 0; 76 | } 77 | 78 | .Home-footer { 79 | text-align: center; 80 | padding: 1em 0; 81 | } 82 | 83 | .Home-footer a { 84 | text-decoration: none; 85 | } 86 | 87 | pre { 88 | background: #fff; 89 | border-radius: 5px; 90 | padding: 1em; 91 | display: block; 92 | margin-top: 2em; 93 | } 94 | 95 | .markdown h2:first-child { 96 | margin-top: 0; 97 | } 98 | 99 | .markdown h2 { 100 | font-size: 20px; 101 | margin-top: 3em; 102 | } 103 | 104 | a, a:visited { 105 | color: #2c3e50; 106 | } 107 | 108 | a:hover { 109 | color: #3498db; 110 | } -------------------------------------------------------------------------------- /examples/views/home/home-view.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | var React = require('react'); 4 | var BothScrollbar = require('./examples/both-scrollbar.jsx'); 5 | var HorizontalScrollbar = require('./examples/horizontal-scrollbar.jsx'); 6 | var VerticalScrollbar = require('./examples/vertical-scrollbar.jsx'); 7 | var CustomScrollbar = require('./examples/custom-scrollbar.jsx'); 8 | 9 | require('./home-view.css'); 10 | 11 | module.exports = React.createClass({ 12 | 13 | getInitialState: function() { 14 | return { 15 | }; 16 | }, 17 | 18 | render: function () { 19 | return ( 20 |
21 |
22 |

React-Scrollbars

23 |

view on github

24 |
25 | 26 |
27 | 40 | 41 |
42 |
43 |

Vertical Scrollbars

44 | 45 |
46 | 47 |
48 |

Horizontal Scrollbars

49 | 50 |
51 | 52 |
53 |

Horizontal and Vertical Scrollbars

54 | 55 |
56 | 57 |
58 |

Custom Scrollbars

59 | 60 |
61 | 62 |
63 |

Implementation

64 |
65 |
66 |
67 |
68 |
69 |
70 | 71 | 74 | 75 |
76 | ); 77 | } 78 | }); -------------------------------------------------------------------------------- /examples/views/home/index.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | module.exports = require('./home-view.jsx'); 3 | -------------------------------------------------------------------------------- /examples/webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | 3 | module.exports = { 4 | entry: './', 5 | output: { 6 | filename: 'bundle.js', 7 | }, 8 | resolve: { 9 | extensions: ['', '.js', '.jsx', '.scss', '.css'] 10 | }, 11 | plugins: [ 12 | new webpack.NormalModuleReplacementPlugin( // allow examples to include react-scrollbar 13 | /^react-scrollbar$/, 14 | __dirname + '/../src' 15 | ) 16 | ], 17 | module: { 18 | loaders: [ 19 | { test: /\.jsx$/, loaders: [ 20 | 'react-hot', 21 | 'jsx', 22 | ] 23 | }, 24 | { test: /\.css$/, loaders: [ 25 | 'style', 26 | 'css', 27 | 'autoprefixer', 28 | ] 29 | }, 30 | { test: /\.(png|woff)$/, loader: 'url?limit=100000' }, 31 | { test: /\.md$/, loader: "html!markdown" } 32 | ] 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /lib/components/button.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | 3 | var style = { 4 | background: 'transparent', 5 | border: '2px solid #1ABC9C', 6 | borderRadius: 2, 7 | textYransform: 'uppercase', 8 | padding: '0.5em 1em' 9 | }; 10 | 11 | var Button = React.createClass({displayName: "Button", 12 | render: function() { 13 | return ( 14 | React.createElement("button", {style: style, type: this.props.type, className: "Button"}, this.props.children) 15 | ); 16 | } 17 | }); 18 | 19 | module.exports = Button; 20 | -------------------------------------------------------------------------------- /lib/components/scrollbar-wrapper.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var React = require('react'); 3 | 4 | var ScrollbarMixin = require('../mixins/scrollbar'); 5 | var Scrollbar = require('./scrollbar'); 6 | 7 | var ScrollbarWrapper = React.createClass({displayName: "ScrollbarWrapper", 8 | mixins: [ScrollbarMixin], 9 | 10 | componentDidMount: function() { 11 | window.addEventListener('message', this.handleReceive, false); 12 | }, 13 | 14 | handleReceive: function(event) { 15 | var data = event.data; 16 | 17 | if (typeof(this[data.func]) === 'function') { 18 | this[data.func](); 19 | } 20 | }, 21 | 22 | onResize: function() { 23 | this.handleContentResize(); 24 | }, 25 | 26 | render: function() { 27 | return ( 28 | React.createElement("div", {style: this.scrollbarContainerStyle(), className: this.containerClass()}, 29 | React.createElement("div", {ref: "scrollableContent", style: this.scrollbarContentStyle(), onScroll: this.handleScroll, className: this.props.className + ' ScrollbarContent'}, 30 | React.createElement("div", {className: "ScrollbarChildren", style: {position: 'relative'}}, 31 | this.props.children, 32 | 33 | React.createElement("iframe", {style: {width: '100%', height: '100%', position: 'absolute', top: '-100%', left: '-100%'}, frameBorder: "0", src: "javascript:window.onresize=function(){parent.postMessage({'func': 'onResize'}, '*')}"}) 34 | ), 35 | 36 | React.createElement(Scrollbar, Object.assign({}, 37 | this.props, 38 | this.getScrollbarProps())) 39 | ) 40 | ) 41 | ); 42 | } 43 | }); 44 | 45 | module.exports = ScrollbarWrapper; -------------------------------------------------------------------------------- /lib/components/scrollbar.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var _ = require('lodash'); 3 | 4 | var Scrollbar = React.createClass({displayName: "Scrollbar", 5 | getDefaultProps: function() { 6 | return { 7 | offset: 2, 8 | scrollbarThickness: 10, 9 | stickLength: { 10 | horizontal: 100, 11 | vertical: 100 12 | }, 13 | stickPosition: { 14 | horizontal: 0, 15 | vertical: 0 16 | }, 17 | scrollbarLength: { 18 | horizontal: 100, 19 | vertical: 100 20 | }, 21 | showScrollbar: { 22 | horizontal: true, 23 | vertical: false 24 | }, 25 | vertical: true, 26 | horizontal: true 27 | }; 28 | }, 29 | 30 | verticalScrollbar: function(style, stickStyle) { 31 | if (this.props.vertical && this.props.showScrollbar.vertical) { 32 | return ( 33 | React.createElement("div", {className: "Scrollbar", style: style}, 34 | React.createElement("div", {className: "Scrollbar-stick", style: stickStyle, onMouseDown: this.props.onMouseDown.bind(null, 'y')}) 35 | ) 36 | ); 37 | } else { 38 | return null; 39 | } 40 | }, 41 | 42 | horizontalScrollbar: function(style, stickStyle) { 43 | if (this.props.horizontal && this.props.showScrollbar.horizontal) { 44 | return ( 45 | React.createElement("div", {style: style}, 46 | React.createElement("div", {style: stickStyle, onMouseDown: this.props.onMouseDown.bind(null, 'x')}) 47 | ) 48 | ); 49 | } else { 50 | return null; 51 | } 52 | }, 53 | 54 | render: function() { 55 | if (!this.props.render) { 56 | return React.createElement("div", null); 57 | } 58 | 59 | var verticalScrollbarHeight; 60 | var horizontalScrollbarWidth; 61 | 62 | if (this.props.scrollbarLength.vertical) { 63 | verticalScrollbarHeight = this.props.scrollbarLength.vertical; 64 | } 65 | 66 | if (this.props.scrollbarLength.horizontal) { 67 | horizontalScrollbarWidth = this.props.scrollbarLength.horizontal; 68 | } 69 | 70 | var scrollbarStyle = { 71 | borderRadius: 4, 72 | background: 'rgba(0, 0, 0, 0.5)', 73 | position: 'absolute', 74 | opacity: 1 75 | }; 76 | 77 | var stickStyle = { 78 | background: 'rgba(255, 255, 255, 0.7)', 79 | position: 'absolute', 80 | borderRadius: 4 81 | }; 82 | 83 | // TODO: clean this junk UP 84 | 85 | var scrollbarStyleVertical = _.extend({ 86 | width: this.props.scrollbarThickness, 87 | top: this.props.offset, 88 | height: verticalScrollbarHeight || 'auto', 89 | bottom: verticalScrollbarHeight ? 'auto' : this.props.offset, 90 | right: this.props.offset, 91 | }, scrollbarStyle); 92 | 93 | var scrollbarStyleHorizontal = _.extend({ 94 | left: this.props.offset, 95 | bottom: this.props.offset, 96 | width: horizontalScrollbarWidth || 'auto', 97 | right: horizontalScrollbarWidth ? 'auto' : this.props.offset, 98 | height: this.props.scrollbarThickness 99 | }, scrollbarStyle); 100 | 101 | var scrollbarStickStyleVertical = _.extend({ 102 | width: this.props.scrollbarThickness, 103 | height: this.props.stickLength.vertical, 104 | right: 0, 105 | top: this.props.stickPosition.vertical 106 | }, stickStyle); 107 | 108 | var scrollbarStickStyleHorizontal = _.extend({ 109 | height: this.props.scrollbarThickness, 110 | width: this.props.stickLength.horizontal, 111 | left: this.props.stickPosition.horizontal 112 | }, stickStyle); 113 | 114 | return ( 115 | React.createElement("div", {className: "Scrollbar-wrapper"}, 116 | this.verticalScrollbar(scrollbarStyleVertical, scrollbarStickStyleVertical), 117 | this.horizontalScrollbar(scrollbarStyleHorizontal, scrollbarStickStyleHorizontal) 118 | ) 119 | ); 120 | } 121 | }); 122 | 123 | module.exports = Scrollbar; 124 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | Scrollbar: require('./components/scrollbar'), 3 | ScrollbarWrapper: require('./components/scrollbar-wrapper'), 4 | Mixin: require('./mixins/scrollbar') 5 | }; 6 | -------------------------------------------------------------------------------- /lib/mixins/scrollbar.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var _ = require('lodash'); 3 | 4 | var ScrollbarMixin = { 5 | getInitialState: function() { 6 | return { 7 | stickPosition: { 8 | horizontal: 0, 9 | vertical: 0 10 | }, 11 | initialScroll: { 12 | left: 0, 13 | top: 0 14 | }, 15 | initialPosition: { 16 | x: 0, 17 | y: 0 18 | }, 19 | axis: null, 20 | initialMovement: false, 21 | scrolling: false, 22 | nativeScrollbarWidth: 0, 23 | firstRender: null 24 | }; 25 | }, 26 | 27 | getDefaultProps: function() { 28 | return { 29 | scrollbarOffset: 2, 30 | overflowTolerance: 3 31 | }; 32 | }, 33 | 34 | componentDidMount: function() { 35 | var scrollbarElement = document.createElement('div'); 36 | scrollbarElement.style.width = '100px'; 37 | scrollbarElement.style.height = '100px'; 38 | scrollbarElement.style.overflow = 'scroll'; 39 | scrollbarElement.style.position = 'absolute'; 40 | scrollbarElement.style.top = '-100%'; 41 | scrollbarElement.style.left = '-100%'; 42 | document.body.appendChild(scrollbarElement); 43 | 44 | this.setState({ 45 | nativeScrollbarWidth: scrollbarElement.offsetWidth - scrollbarElement.clientWidth, 46 | firstRender: true 47 | }, function() { 48 | document.body.removeChild(scrollbarElement); 49 | }); 50 | }, 51 | 52 | componentDidUpdate: function() { 53 | if (this.state.firstRender) { 54 | this.setState({ 55 | firstRender: false 56 | }); 57 | } 58 | }, 59 | 60 | getRatio: function() { 61 | if (!this.refs.scrollableContent) { 62 | return {}; 63 | } 64 | 65 | var element = this.refs.scrollableContent; 66 | 67 | return { 68 | horizontal: this.getContentDimensions().width / element.scrollWidth, 69 | vertical: this.getContentDimensions().height / element.scrollHeight 70 | }; 71 | }, 72 | 73 | getStickLength: function() { 74 | if (!this.refs.scrollableContent) { 75 | return {}; 76 | } 77 | 78 | var scrollbarLength = this.getScrollbarLength(); 79 | var horizontal = scrollbarLength.horizontal * this.getRatio().horizontal; 80 | var vertical = scrollbarLength.vertical * this.getRatio().vertical; 81 | 82 | return { 83 | horizontal: horizontal, 84 | vertical: vertical 85 | }; 86 | }, 87 | 88 | getContentDimensions: function() { 89 | if (!this.refs.scrollableContent) { 90 | return {}; 91 | } 92 | 93 | var element = this.refs.scrollableContent; 94 | 95 | return { 96 | height: element.clientHeight, 97 | scrollHeight: element.scrollHeight, 98 | scrollWidth: element.scrollWidth, 99 | width: element.clientWidth, 100 | }; 101 | }, 102 | 103 | getScrollbarLength: function() { 104 | var horizontal = this.getContentDimensions().width - (this.props.scrollbarOffset * 2); 105 | var vertical = this.getContentDimensions().height - (this.props.scrollbarOffset * 2); 106 | 107 | 108 | if (this.scrollbarRequired().both) { 109 | horizontal = horizontal - this.state.nativeScrollbarWidth; 110 | vertical = vertical - this.state.nativeScrollbarWidth; 111 | } 112 | 113 | return { 114 | horizontal: horizontal, 115 | vertical: vertical 116 | }; 117 | }, 118 | 119 | calculateStickPosition: function(left, top) { 120 | var scrollbarRatioWidth = this.getScrollbarLength().horizontal / this.getContentDimensions().scrollWidth; 121 | var scrollbarRatioHeight = this.getScrollbarLength().vertical / this.getContentDimensions().scrollHeight; 122 | 123 | return { 124 | horizontal: left * scrollbarRatioWidth, 125 | vertical: top * scrollbarRatioHeight 126 | }; 127 | }, 128 | 129 | scrollbarRequired: function() { 130 | if (!this.refs.scrollableContent) { 131 | return {}; 132 | } 133 | 134 | var dimensions = this.getContentDimensions(); 135 | var horizontalRequired = dimensions.scrollWidth - this.props.overflowTolerance > dimensions.width; 136 | var verticalRequired = dimensions.scrollHeight - this.props.overflowTolerance > dimensions.height; 137 | 138 | return { 139 | horizontal: horizontalRequired, 140 | vertical: verticalRequired, 141 | both: horizontalRequired && verticalRequired 142 | }; 143 | }, 144 | 145 | handleScroll: function(event) { 146 | this.setState({ 147 | stickPosition: this.calculateStickPosition(event.target.scrollLeft, event.target.scrollTop) 148 | }); 149 | }, 150 | 151 | handleMouseDown: function(axis, event) { 152 | event.preventDefault(); 153 | 154 | this.setState({ 155 | axis: axis, 156 | initialPosition: { 157 | x: event.pageX, 158 | y: event.pageY 159 | }, 160 | initialMovement: true, 161 | scrolling: true 162 | }); 163 | 164 | document.addEventListener('mousemove', this.handleStickDrag); 165 | document.addEventListener('mouseup', this.handleMouseUp); 166 | }, 167 | 168 | handleMouseUp: function() { 169 | this.setState({ 170 | scrolling: false 171 | }); 172 | 173 | document.removeEventListener('mousemove', this.handleStickDrag); 174 | document.removeEventListener('mouseup', this.handleMouseUp); 175 | }, 176 | 177 | handleStickDrag: function(event) { 178 | // TODO: this needs refactoring 179 | var origin = this.state.axis === 'x' ? 'left' : 'top'; 180 | 181 | var initialScrollPosition = this.state.initialScroll[origin]; 182 | 183 | if (this.state.initialMovement) { 184 | initialScrollPosition = origin === 'left' ? this.refs.scrollableContent.scrollLeft : this.refs.scrollableContent.scrollTop; 185 | var initialScroll = _.extend({}, this.state.initialScroll); 186 | initialScroll[origin] = initialScrollPosition; 187 | 188 | this.setState({ 189 | initialScroll: initialScroll, 190 | initialMovement: false 191 | }); 192 | } 193 | 194 | var movement = { 195 | x: (this.state.initialPosition.x - event.pageX) * -1, 196 | y: (this.state.initialPosition.y - event.pageY) * -1 197 | }; 198 | 199 | var scaledMovement = { 200 | x: movement.x / this.getRatio().horizontal, 201 | y: movement.y / this.getRatio().vertical 202 | }; 203 | 204 | if (this.state.axis === 'x') { 205 | this.refs.scrollableContent.scrollLeft = initialScrollPosition + scaledMovement.x; 206 | } else { 207 | this.refs.scrollableContent.scrollTop = initialScrollPosition + scaledMovement.y; 208 | } 209 | }, 210 | 211 | handleContentResize: function() { 212 | this.forceUpdate(); 213 | }, 214 | 215 | getScrollbarProps: function() { 216 | if (this.state.firstRender !== false) { 217 | return { 218 | render: false 219 | }; 220 | } 221 | 222 | return { 223 | render: true, 224 | stickLength: this.getStickLength(), 225 | scrollbarLength: this.getScrollbarLength(), 226 | stickPosition: this.state.stickPosition, 227 | onMouseDown: this.handleMouseDown, 228 | showScrollbar: this.scrollbarRequired(), 229 | offset: this.props.scrollbarOffset 230 | }; 231 | }, 232 | 233 | containerClass: function() { // TODO: rename getStyle or something 234 | return [ 235 | 'ScrollbarContainer', 236 | this.state.scrolling ? 'ScrollbarContainer--scrolling' : '' 237 | ].join(' '); 238 | }, 239 | 240 | scrollbarContainerStyle: function() { 241 | return { 242 | position: 'relative', 243 | overflow: 'hidden' 244 | }; 245 | }, 246 | 247 | scrollbarContentStyle: function() { 248 | var style = {}; 249 | 250 | if (this.scrollbarRequired().vertical) { 251 | style['paddingRight'] = this.state.nativeScrollbarWidth; 252 | } 253 | 254 | if (this.scrollbarRequired().horizontal) { 255 | style['marginBottom'] = this.state.nativeScrollbarWidth * -1; 256 | } 257 | 258 | return style; 259 | } 260 | }; 261 | 262 | module.exports = ScrollbarMixin; 263 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-scrollbars", 3 | "version": "0.0.5", 4 | "description": "Stateful scrollbar component for React", 5 | "keywords": [ 6 | "react-component", 7 | "scrollbars" 8 | ], 9 | "scripts": { 10 | "start": "cd examples && webpack-dev-server --port 9090 --d --hot --inline --progress --colors", 11 | "build": "./node_modules/jsx-loader/node_modules/.bin/jsx --harmony --no-cache-dir -x jsx src lib", 12 | "build-www": "cd examples && webpack" 13 | }, 14 | "main": "lib/index.js", 15 | "author": "James Coleman", 16 | "license": "ISC", 17 | "bugs": { 18 | "url": "https://github.com/ojame/react-scrollbars/issues" 19 | }, 20 | "homepage": "https://github.com/ojame/react-scrollbars", 21 | "devDependencies": { 22 | "autoprefixer-loader": "^3.1.0", 23 | "css-loader": "^0.17.0", 24 | "html-loader": "^0.3.0", 25 | "jsx-loader": ">=0.12.2", 26 | "markdown-loader": "^0.1.2", 27 | "normalize.css": "^3.0.2", 28 | "style-loader": "^0.12.3", 29 | "url-loader": "^0.5.5", 30 | "webpack": "^1.5.3", 31 | "webpack-dev-server": "^1.7.0", 32 | "react-hot-loader": "^1.1.1" 33 | }, 34 | "peerDependencies": { 35 | "react": ">=0.12.2" 36 | }, 37 | "dependencies": { 38 | "lodash": "^3.10.1" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/components/button.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | 3 | var style = { 4 | background: 'transparent', 5 | border: '2px solid #1ABC9C', 6 | borderRadius: 2, 7 | textYransform: 'uppercase', 8 | padding: '0.5em 1em' 9 | }; 10 | 11 | var Button = React.createClass({ 12 | render: function() { 13 | return ( 14 | 15 | ); 16 | } 17 | }); 18 | 19 | module.exports = Button; 20 | -------------------------------------------------------------------------------- /src/components/scrollbar-wrapper.jsx: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var React = require('react'); 3 | 4 | var ScrollbarMixin = require('../mixins/scrollbar'); 5 | var Scrollbar = require('./scrollbar'); 6 | 7 | var ScrollbarWrapper = React.createClass({ 8 | mixins: [ScrollbarMixin], 9 | 10 | componentDidMount: function() { 11 | window.addEventListener('message', this.handleReceive, false); 12 | }, 13 | 14 | handleReceive: function(event) { 15 | var data = event.data; 16 | 17 | if (typeof(this[data.func]) === 'function') { 18 | this[data.func](); 19 | } 20 | }, 21 | 22 | onResize: function() { 23 | this.handleContentResize(); 24 | }, 25 | 26 | render: function() { 27 | return ( 28 |
29 |
30 |
31 | {this.props.children} 32 | 33 | 34 |
35 | 36 | 39 |
40 |
41 | ); 42 | } 43 | }); 44 | 45 | module.exports = ScrollbarWrapper; -------------------------------------------------------------------------------- /src/components/scrollbar.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var _ = require('lodash-node'); 3 | 4 | var Scrollbar = React.createClass({ 5 | getDefaultProps: function() { 6 | return { 7 | offset: 2, 8 | scrollbarThickness: 10, 9 | stickLength: { 10 | horizontal: 100, 11 | vertical: 100 12 | }, 13 | stickPosition: { 14 | horizontal: 0, 15 | vertical: 0 16 | }, 17 | scrollbarLength: { 18 | horizontal: 100, 19 | vertical: 100 20 | }, 21 | showScrollbar: { 22 | horizontal: true, 23 | vertical: false 24 | }, 25 | vertical: true, 26 | horizontal: true 27 | }; 28 | }, 29 | 30 | verticalScrollbar: function(style, stickStyle) { 31 | if (this.props.vertical && this.props.showScrollbar.vertical) { 32 | return ( 33 |
34 |
35 |
36 | ); 37 | } else { 38 | return null; 39 | } 40 | }, 41 | 42 | horizontalScrollbar: function(style, stickStyle) { 43 | if (this.props.horizontal && this.props.showScrollbar.horizontal) { 44 | return ( 45 |
46 |
47 |
48 | ); 49 | } else { 50 | return null; 51 | } 52 | }, 53 | 54 | render: function() { 55 | if (!this.props.render) { 56 | return
; 57 | } 58 | 59 | var verticalScrollbarHeight; 60 | var horizontalScrollbarWidth; 61 | 62 | if (this.props.scrollbarLength.vertical) { 63 | verticalScrollbarHeight = this.props.scrollbarLength.vertical; 64 | } 65 | 66 | if (this.props.scrollbarLength.horizontal) { 67 | horizontalScrollbarWidth = this.props.scrollbarLength.horizontal; 68 | } 69 | 70 | var scrollbarStyle = { 71 | borderRadius: 4, 72 | background: 'rgba(0, 0, 0, 0.5)', 73 | position: 'absolute', 74 | opacity: 1 75 | }; 76 | 77 | var stickStyle = { 78 | background: 'rgba(255, 255, 255, 0.7)', 79 | position: 'absolute', 80 | borderRadius: 4 81 | }; 82 | 83 | // TODO: clean this junk UP 84 | 85 | var scrollbarStyleVertical = _.extend({ 86 | width: this.props.scrollbarThickness, 87 | top: this.props.offset, 88 | height: verticalScrollbarHeight || 'auto', 89 | bottom: verticalScrollbarHeight ? 'auto' : this.props.offset, 90 | right: this.props.offset, 91 | }, scrollbarStyle); 92 | 93 | var scrollbarStyleHorizontal = _.extend({ 94 | left: this.props.offset, 95 | bottom: this.props.offset, 96 | width: horizontalScrollbarWidth || 'auto', 97 | right: horizontalScrollbarWidth ? 'auto' : this.props.offset, 98 | height: this.props.scrollbarThickness 99 | }, scrollbarStyle); 100 | 101 | var scrollbarStickStyleVertical = _.extend({ 102 | width: this.props.scrollbarThickness, 103 | height: this.props.stickLength.vertical, 104 | right: 0, 105 | top: this.props.stickPosition.vertical 106 | }, stickStyle); 107 | 108 | var scrollbarStickStyleHorizontal = _.extend({ 109 | height: this.props.scrollbarThickness, 110 | width: this.props.stickLength.horizontal, 111 | left: this.props.stickPosition.horizontal 112 | }, stickStyle); 113 | 114 | return ( 115 |
116 | {this.verticalScrollbar(scrollbarStyleVertical, scrollbarStickStyleVertical)} 117 | {this.horizontalScrollbar(scrollbarStyleHorizontal, scrollbarStickStyleHorizontal)} 118 |
119 | ); 120 | } 121 | }); 122 | 123 | module.exports = Scrollbar; 124 | -------------------------------------------------------------------------------- /src/index.jsx: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | Scrollbar: require('./components/scrollbar'), 3 | ScrollbarWrapper: require('./components/scrollbar-wrapper'), 4 | Mixin: require('./mixins/scrollbar') 5 | }; 6 | -------------------------------------------------------------------------------- /src/mixins/scrollbar.jsx: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var React = require('react'); 3 | var _ = require('lodash-node'); 4 | 5 | var ScrollbarMixin = { 6 | getInitialState: function() { 7 | return { 8 | stickPosition: { 9 | horizontal: 0, 10 | vertical: 0 11 | }, 12 | initialScroll: { 13 | left: 0, 14 | top: 0 15 | }, 16 | initialPosition: { 17 | x: 0, 18 | y: 0 19 | }, 20 | axis: null, 21 | initialMovement: false, 22 | scrolling: false, 23 | nativeScrollbarWidth: 0, 24 | firstRender: null 25 | }; 26 | }, 27 | 28 | getDefaultProps: function() { 29 | return { 30 | scrollbarOffset: 2, 31 | overflowTolerance: 3 32 | }; 33 | }, 34 | 35 | componentDidMount: function() { 36 | var scrollbarElement = document.createElement('div'); 37 | scrollbarElement.style.width = '100px'; 38 | scrollbarElement.style.height = '100px'; 39 | scrollbarElement.style.overflow = 'scroll'; 40 | scrollbarElement.style.position = 'absolute'; 41 | scrollbarElement.style.top = '-100%'; 42 | scrollbarElement.style.left = '-100%'; 43 | document.body.appendChild(scrollbarElement); 44 | 45 | this.setState({ 46 | nativeScrollbarWidth: scrollbarElement.offsetWidth - scrollbarElement.clientWidth, 47 | firstRender: true 48 | }, function() { 49 | document.body.removeChild(scrollbarElement); 50 | }); 51 | }, 52 | 53 | componentDidUpdate: function() { 54 | if (this.state.firstRender) { 55 | this.setState({ 56 | firstRender: false 57 | }); 58 | } 59 | }, 60 | 61 | getRatio: function() { 62 | if (!this.refs.scrollableContent) { 63 | return {}; 64 | } 65 | 66 | var element = this.refs.scrollableContent; 67 | 68 | return { 69 | horizontal: this.getContentDimensions().width / element.scrollWidth, 70 | vertical: this.getContentDimensions().height / element.scrollHeight 71 | }; 72 | }, 73 | 74 | getStickLength: function() { 75 | if (!this.refs.scrollableContent) { 76 | return {}; 77 | } 78 | 79 | var scrollbarLength = this.getScrollbarLength(); 80 | var horizontal = scrollbarLength.horizontal * this.getRatio().horizontal; 81 | var vertical = scrollbarLength.vertical * this.getRatio().vertical; 82 | 83 | return { 84 | horizontal: horizontal, 85 | vertical: vertical 86 | }; 87 | }, 88 | 89 | getContentDimensions: function() { 90 | if (!this.refs.scrollableContent) { 91 | return {}; 92 | } 93 | 94 | var element = this.refs.scrollableContent; 95 | 96 | return { 97 | height: element.clientHeight, 98 | scrollHeight: element.scrollHeight, 99 | scrollWidth: element.scrollWidth, 100 | width: element.clientWidth, 101 | }; 102 | }, 103 | 104 | getScrollbarLength: function() { 105 | var horizontal = this.getContentDimensions().width - (this.props.scrollbarOffset * 2); 106 | var vertical = this.getContentDimensions().height - (this.props.scrollbarOffset * 2); 107 | 108 | 109 | if (this.scrollbarRequired().both) { 110 | horizontal = horizontal - this.state.nativeScrollbarWidth; 111 | vertical = vertical - this.state.nativeScrollbarWidth; 112 | } 113 | 114 | return { 115 | horizontal: horizontal, 116 | vertical: vertical 117 | }; 118 | }, 119 | 120 | calculateStickPosition: function(left, top) { 121 | var scrollbarRatioWidth = this.getScrollbarLength().horizontal / this.getContentDimensions().scrollWidth; 122 | var scrollbarRatioHeight = this.getScrollbarLength().vertical / this.getContentDimensions().scrollHeight; 123 | 124 | return { 125 | horizontal: left * scrollbarRatioWidth, 126 | vertical: top * scrollbarRatioHeight 127 | }; 128 | }, 129 | 130 | scrollbarRequired: function() { 131 | if (!this.refs.scrollableContent) { 132 | return {}; 133 | } 134 | 135 | var dimensions = this.getContentDimensions(); 136 | var horizontalRequired = dimensions.scrollWidth - this.props.overflowTolerance > dimensions.width; 137 | var verticalRequired = dimensions.scrollHeight - this.props.overflowTolerance > dimensions.height; 138 | 139 | return { 140 | horizontal: horizontalRequired, 141 | vertical: verticalRequired, 142 | both: horizontalRequired && verticalRequired 143 | }; 144 | }, 145 | 146 | handleScroll: function(event) { 147 | this.setState({ 148 | stickPosition: this.calculateStickPosition(event.target.scrollLeft, event.target.scrollTop) 149 | }); 150 | }, 151 | 152 | handleMouseDown: function(axis, event) { 153 | event.preventDefault(); 154 | 155 | this.setState({ 156 | axis: axis, 157 | initialPosition: { 158 | x: event.pageX, 159 | y: event.pageY 160 | }, 161 | initialMovement: true, 162 | scrolling: true 163 | }); 164 | 165 | document.addEventListener('mousemove', this.handleStickDrag); 166 | document.addEventListener('mouseup', this.handleMouseUp); 167 | }, 168 | 169 | handleMouseUp: function() { 170 | this.setState({ 171 | scrolling: false 172 | }); 173 | 174 | document.removeEventListener('mousemove', this.handleStickDrag); 175 | document.removeEventListener('mouseup', this.handleMouseUp); 176 | }, 177 | 178 | handleStickDrag: function(event) { 179 | // TODO: this needs refactoring 180 | var origin = this.state.axis === 'x' ? 'left' : 'top'; 181 | 182 | var initialScrollPosition = this.state.initialScroll[origin]; 183 | 184 | if (this.state.initialMovement) { 185 | initialScrollPosition = origin === 'left' ? this.refs.scrollableContent.scrollLeft : this.refs.scrollableContent.scrollTop; 186 | var initialScroll = _.extend({}, this.state.initialScroll); 187 | initialScroll[origin] = initialScrollPosition; 188 | 189 | this.setState({ 190 | initialScroll: initialScroll, 191 | initialMovement: false 192 | }); 193 | } 194 | 195 | var movement = { 196 | x: (this.state.initialPosition.x - event.pageX) * -1, 197 | y: (this.state.initialPosition.y - event.pageY) * -1 198 | }; 199 | 200 | var scaledMovement = { 201 | x: movement.x / this.getRatio().horizontal, 202 | y: movement.y / this.getRatio().vertical 203 | }; 204 | 205 | if (this.state.axis === 'x') { 206 | this.refs.scrollableContent.scrollLeft = initialScrollPosition + scaledMovement.x; 207 | } else { 208 | this.refs.scrollableContent.scrollTop = initialScrollPosition + scaledMovement.y; 209 | } 210 | }, 211 | 212 | handleContentResize: function() { 213 | this.forceUpdate(); 214 | }, 215 | 216 | getScrollbarProps: function() { 217 | if (this.state.firstRender !== false) { 218 | return { 219 | render: false 220 | }; 221 | } 222 | 223 | return { 224 | render: true, 225 | stickLength: this.getStickLength(), 226 | scrollbarLength: this.getScrollbarLength(), 227 | stickPosition: this.state.stickPosition, 228 | onMouseDown: this.handleMouseDown, 229 | showScrollbar: this.scrollbarRequired(), 230 | offset: this.props.scrollbarOffset 231 | }; 232 | }, 233 | 234 | containerClass: function() { // TODO: rename getStyle or something 235 | return [ 236 | 'ScrollbarContainer', 237 | this.state.scrolling ? 'ScrollbarContainer--scrolling' : '' 238 | ].join(' '); 239 | }, 240 | 241 | scrollbarContainerStyle: function() { 242 | return { 243 | position: 'relative', 244 | overflow: 'hidden' 245 | }; 246 | }, 247 | 248 | scrollbarContentStyle: function() { 249 | var style = {}; 250 | 251 | if (this.scrollbarRequired().vertical) { 252 | style['paddingRight'] = this.state.nativeScrollbarWidth; 253 | } 254 | 255 | if (this.scrollbarRequired().horizontal) { 256 | style['marginBottom'] = this.state.nativeScrollbarWidth * -1; 257 | } 258 | 259 | return style; 260 | } 261 | }; 262 | 263 | module.exports = ScrollbarMixin; 264 | -------------------------------------------------------------------------------- /src/utils/offsets.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojame/react-scrollbars/55e176185141938d140ca51e64dee410737d5c55/src/utils/offsets.js -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | 3 | module.exports = { 4 | entry: './src', 5 | module: { 6 | loaders: [ 7 | { test: /\.js$/, loader: 'jsx-loader?harmony' } 8 | ] 9 | }, 10 | output: { 11 | filename: 'build/react.scrollbar.min.js', 12 | libraryTarget: 'umd', 13 | library: 'ReactScrollbar' 14 | }, 15 | plugins: [ 16 | new webpack.optimize.OccurenceOrderPlugin(), 17 | new webpack.DefinePlugin({ 18 | 'process.env': { 19 | 'NODE_ENV': JSON.stringify('production') 20 | } 21 | }), 22 | new webpack.optimize.UglifyJsPlugin({ 23 | compressor: { 24 | warnings: false 25 | } 26 | }) 27 | ] 28 | }; 29 | --------------------------------------------------------------------------------