react-custom-scroll
Advanced tools
Comparing version 1.9.0 to 1.10.0
@@ -1,1 +0,1 @@ | ||
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(require("react"),require("react-dom")):"function"==typeof define&&define.amd?define(["react","react-dom"],e):"object"==typeof exports?exports.ReactCustomScroll=e(require("react"),require("react-dom")):t.ReactCustomScroll=e(t.react,t["react-dom"])}(this,function(t,e){return function(t){function e(r){if(o[r])return o[r].exports;var l=o[r]={exports:{},id:r,loaded:!1};return t[r].call(l.exports,l,l.exports,e),l.loaded=!0,l.exports}var o={};return e.m=t,e.c=o,e.p="",e(0)}([function(t,e,o){t.exports=o(1)},function(t,e,o){"use strict";function r(t,e,o){return e in t?Object.defineProperty(t,e,{value:o,enumerable:!0,configurable:!0,writable:!0}):t[e]=o,t}function l(t,e,o){return e=e||0===e?e:t,o=o||0===o?o:t,e>o?(console.error("min limit is greater than max limit"),t):t<e?e:t>o?o:t}function s(t,e){var o=e.getBoundingClientRect();return t.clientX>o.left&&t.clientX<o.left+o.width&&t.clientY>o.top&&t.clientY<o.top+o.height}function n(t){var e=this.props.minScrollHandleHeight;if(t.height>=e)return t;var o=e-t.height,r=this.state.scrollPos/(this.contentHeight-this.visibleHeight),l=o*r,s=t.top-l;return{height:e,top:s}}var i=o(2),a=o(3);t.exports=i.createClass({displayName:"customScroll",propTypes:{allowOuterScroll:i.PropTypes.bool,heightRelativeToParent:i.PropTypes.string,onScroll:i.PropTypes.func,addScrolledClass:i.PropTypes.bool,freezePosition:i.PropTypes.bool,handleClass:i.PropTypes.string,minScrollHandleHeight:i.PropTypes.number,flex:i.PropTypes.string,rtl:i.PropTypes.bool,scrollTo:i.PropTypes.number},getDefaultProps:function(){return{handleClass:"inner-handle",minScrollHandleHeight:38}},getInitialState:function(){return this.scrollbarYWidth=0,{scrollPos:0,onDrag:!1}},componentDidMount:function(){this.forceUpdate()},componentDidUpdate:function(t){var e=a.findDOMNode(this),o=e.getBoundingClientRect(),r=this.getScrolledElement();this.contentHeight=r.scrollHeight,this.scrollbarYWidth=r.offsetWidth-r.clientWidth,this.visibleHeight=r.clientHeight,this.scrollRatio=this.contentHeight?this.visibleHeight/this.contentHeight:1,this.toggleScrollIfNeeded(),this.position={top:o.top+window.pageYOffset,left:o.left+window.pageXOffset},(this.props.freezePosition||t.freezePosition)&&this.adjustFreezePosition(t),"undefined"!=typeof this.props.scrollTo&&this.props.scrollTo!==t.scrollTo&&this.updateScrollPosition(this.props.scrollTo)},componentWillUnmount:function(){document.removeEventListener("mousemove",this.onHandleDrag),document.removeEventListener("mouseup",this.onHandleDragEnd)},adjustFreezePosition:function(t){var e=this.getScrolledElement(),o=this.refs.contentWrapper;this.props.freezePosition&&(o.scrollTop=this.state.scrollPos),t.freezePosition&&(e.scrollTop=this.state.scrollPos)},toggleScrollIfNeeded:function(){var t=this.contentHeight-this.visibleHeight>1;this.hasScroll!==t&&(this.hasScroll=t,this.forceUpdate())},getScrollTop:function(){return this.getScrolledElement().scrollTop},updateScrollPosition:function(t){var e=this.getScrolledElement();e.scrollTop=t,this.setState({scrollPos:t})},onClick:function(t){if(this.hasScroll&&this.isMouseEventOnCustomScrollbar(t)&&!this.isMouseEventOnScrollHandle(t)){var e=this.calculateNewScrollHandleTop(t),o=this.getScrollValueFromHandlePosition(e);this.updateScrollPosition(o)}},isMouseEventOnCustomScrollbar:function(t){var e=a.findDOMNode(this.refs.customScrollbar);return s(t,e)},isMouseEventOnScrollHandle:function(t){var e=a.findDOMNode(this.refs.scrollHandle);return s(t,e)},calculateNewScrollHandleTop:function(t){var e,o=t.pageY-this.position.top,r=this.getScrollHandleStyle().top,l=o>r+this.scrollHandleHeight;return e=l?r+Math.min(this.scrollHandleHeight,this.visibleHeight-this.scrollHandleHeight):r-Math.max(this.scrollHandleHeight,0)},getScrollValueFromHandlePosition:function(t){return t/this.scrollRatio},getScrollHandleStyle:function(){var t=this.state.scrollPos*this.scrollRatio;return this.scrollHandleHeight=this.visibleHeight*this.scrollRatio,{height:this.scrollHandleHeight,top:t}},adjustCustomScrollPosToContentPos:function(t){this.setState({scrollPos:t})},onScroll:function(t){this.props.freezePosition||(this.adjustCustomScrollPosToContentPos(t.currentTarget.scrollTop),this.props.onScroll&&this.props.onScroll(t))},getScrolledElement:function(){return this.refs.innerContainer},onMouseDown:function(t){this.hasScroll&&this.isMouseEventOnScrollHandle(t)&&(this.startDragHandlePos=this.getScrollHandleStyle().top,this.startDragMousePos=t.pageY,this.setState({onDrag:!0}),document.addEventListener("mousemove",this.onHandleDrag),document.addEventListener("mouseup",this.onHandleDragEnd))},onHandleDrag:function(t){t.preventDefault();var e=t.pageY-this.startDragMousePos,o=l(this.startDragHandlePos+e,0,this.visibleHeight-this.scrollHandleHeight),r=this.getScrollValueFromHandlePosition(o);this.updateScrollPosition(r)},onHandleDragEnd:function(t){this.setState({onDrag:!1}),t.preventDefault(),document.removeEventListener("mousemove",this.onHandleDrag),document.removeEventListener("mouseup",this.onHandleDragEnd)},blockOuterScroll:function(t){if(!this.props.allowOuterScroll){var e=t.currentTarget,o=t.currentTarget.scrollHeight,r=o-t.currentTarget.offsetHeight,l=t.deltaY%3?t.deltaY:10*t.deltaY;e.scrollTop+l<=0?(e.scrollTop=0,t.preventDefault()):e.scrollTop+l>=r&&(e.scrollTop=r,t.preventDefault()),t.stopPropagation()}},getInnerContainerClasses:function(){var t="inner-container";return this.state.scrollPos&&this.props.addScrolledClass&&(t+=" content-scrolled"),t},getScrollStyles:function(){var t,e,o=this.scrollbarYWidth||20,l=this.props.rtl?"marginLeft":"marginRight",s=(t={},r(t,l,-1*o),r(t,"height",this.props.heightRelativeToParent||this.props.flex?"100%":""),t),n=(e={},r(e,l,this.scrollbarYWidth?0:o),r(e,"height",this.props.heightRelativeToParent||this.props.flex?"100%":""),r(e,"overflowY",this.props.freezePosition?"hidden":"visible"),e);return{innerContainer:s,contentWrapper:n}},getOuterContainerStyle:function(){return{height:this.props.heightRelativeToParent||this.props.flex?"100%":""}},getRootStyles:function(){var t={};return this.props.heightRelativeToParent?t.height=this.props.heightRelativeToParent:this.props.flex&&(t.flex=this.props.flex),t},render:function(){var t=this.getScrollStyles(),e=this.getRootStyles(),o=n.call(this,this.getScrollHandleStyle());return i.createElement("div",{className:"custom-scroll "+(this.state.onDrag?"scroll-handle-dragged":""),style:e},i.createElement("div",{className:"outer-container",style:this.getOuterContainerStyle(),onMouseDown:this.onMouseDown,onClick:this.onClick},this.hasScroll?i.createElement("div",{ref:"customScrollbar",className:"custom-scrollbar"+(this.props.rtl?" custom-scrollbar-rtl":""),key:"scrollbar"},i.createElement("div",{ref:"scrollHandle",className:"custom-scroll-handle",style:o},i.createElement("div",{className:this.props.handleClass}))):null,i.createElement("div",{ref:"innerContainer",className:this.getInnerContainerClasses(),style:t.innerContainer,onScroll:this.onScroll,onWheel:this.blockOuterScroll},i.createElement("div",{className:"content-wrapper",ref:"contentWrapper",style:t.contentWrapper},this.props.children))))}})},function(e,o){e.exports=t},function(t,o){t.exports=e}])}); | ||
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(require("react"),require("react-dom")):"function"==typeof define&&define.amd?define(["react","react-dom"],e):"object"==typeof exports?exports.ReactCustomScroll=e(require("react"),require("react-dom")):t.ReactCustomScroll=e(t.react,t["react-dom"])}(this,function(t,e){return function(t){function e(r){if(o[r])return o[r].exports;var s=o[r]={exports:{},id:r,loaded:!1};return t[r].call(s.exports,s,s.exports,e),s.loaded=!0,s.exports}var o={};return e.m=t,e.c=o,e.p="",e(0)}([function(t,e,o){t.exports=o(1)},function(t,e,o){"use strict";function r(t,e,o){return e in t?Object.defineProperty(t,e,{value:o,enumerable:!0,configurable:!0,writable:!0}):t[e]=o,t}function s(t,e,o){return e=e||0===e?e:t,o=o||0===o?o:t,e>o?(console.error("min limit is greater than max limit"),t):t<e?e:t>o?o:t}function l(t,e){var o=e.getBoundingClientRect();return t.clientX>o.left&&t.clientX<o.left+o.width&&t.clientY>o.top&&t.clientY<o.top+o.height}function i(t){var e=this.props.minScrollHandleHeight;if(t.height>=e)return t;var o=e-t.height,r=this.state.scrollPos/(this.contentHeight-this.visibleHeight),s=o*r,l=t.top-s;return{height:e,top:l}}var n=o(2),a=o(3);t.exports=n.createClass({displayName:"customScroll",propTypes:{children:n.PropTypes.any,allowOuterScroll:n.PropTypes.bool,heightRelativeToParent:n.PropTypes.string,onScroll:n.PropTypes.func,addScrolledClass:n.PropTypes.bool,freezePosition:n.PropTypes.bool,handleClass:n.PropTypes.string,minScrollHandleHeight:n.PropTypes.number,flex:n.PropTypes.string,rtl:n.PropTypes.bool,scrollTo:n.PropTypes.number,keepAtBottom:n.PropTypes.bool},getDefaultProps:function(){return{handleClass:"inner-handle",minScrollHandleHeight:38}},getInitialState:function(){return this.scrollbarYWidth=0,{scrollPos:0,onDrag:!1}},componentDidMount:function(){this.forceUpdate()},componentDidUpdate:function(t,e){var o=this.contentHeight,r=this.visibleHeight,s=a.findDOMNode(this),l=s.getBoundingClientRect(),i=this.getScrolledElement(),n=e.scrollPos>=o-r;this.contentHeight=i.scrollHeight,this.scrollbarYWidth=i.offsetWidth-i.clientWidth,this.visibleHeight=i.clientHeight,this.scrollRatio=this.contentHeight?this.visibleHeight/this.contentHeight:1;var c=this.state.scrollPos>=this.contentHeight-this.visibleHeight,h=o!==this.contentHeight;this.toggleScrollIfNeeded(),this.position={top:l.top+window.pageYOffset,left:l.left+window.pageXOffset},(this.props.freezePosition||t.freezePosition)&&this.adjustFreezePosition(t),"undefined"!=typeof this.props.scrollTo&&this.props.scrollTo!==t.scrollTo?this.updateScrollPosition(this.props.scrollTo):this.props.keepAtBottom&&h&&n&&!c&&this.updateScrollPosition(this.contentHeight-this.visibleHeight)},componentWillUnmount:function(){document.removeEventListener("mousemove",this.onHandleDrag),document.removeEventListener("mouseup",this.onHandleDragEnd)},adjustFreezePosition:function(t){var e=this.getScrolledElement(),o=this.refs.contentWrapper;this.props.freezePosition&&(o.scrollTop=this.state.scrollPos),t.freezePosition&&(e.scrollTop=this.state.scrollPos)},toggleScrollIfNeeded:function(){var t=this.contentHeight-this.visibleHeight>1;this.hasScroll!==t&&(this.hasScroll=t,this.forceUpdate())},getScrollTop:function(){return this.getScrolledElement().scrollTop},updateScrollPosition:function(t){var e=this.getScrolledElement();e.scrollTop=t,this.setState({scrollPos:t})},onClick:function(t){if(this.hasScroll&&this.isMouseEventOnCustomScrollbar(t)&&!this.isMouseEventOnScrollHandle(t)){var e=this.calculateNewScrollHandleTop(t),o=this.getScrollValueFromHandlePosition(e);this.updateScrollPosition(o)}},isMouseEventOnCustomScrollbar:function(t){var e=a.findDOMNode(this.refs.customScrollbar);return l(t,e)},isMouseEventOnScrollHandle:function(t){var e=a.findDOMNode(this.refs.scrollHandle);return l(t,e)},calculateNewScrollHandleTop:function(t){var e=t.pageY-this.position.top,o=this.getScrollHandleStyle().top,r=void 0,s=e>o+this.scrollHandleHeight;return r=s?o+Math.min(this.scrollHandleHeight,this.visibleHeight-this.scrollHandleHeight):o-Math.max(this.scrollHandleHeight,0)},getScrollValueFromHandlePosition:function(t){return t/this.scrollRatio},getScrollHandleStyle:function(){var t=this.state.scrollPos*this.scrollRatio;return this.scrollHandleHeight=this.visibleHeight*this.scrollRatio,{height:this.scrollHandleHeight,top:t}},adjustCustomScrollPosToContentPos:function(t){this.setState({scrollPos:t})},onScroll:function(t){this.props.freezePosition||(this.adjustCustomScrollPosToContentPos(t.currentTarget.scrollTop),this.props.onScroll&&this.props.onScroll(t))},getScrolledElement:function(){return this.refs.innerContainer},onMouseDown:function(t){this.hasScroll&&this.isMouseEventOnScrollHandle(t)&&(this.startDragHandlePos=this.getScrollHandleStyle().top,this.startDragMousePos=t.pageY,this.setState({onDrag:!0}),document.addEventListener("mousemove",this.onHandleDrag),document.addEventListener("mouseup",this.onHandleDragEnd))},onHandleDrag:function(t){t.preventDefault();var e=t.pageY-this.startDragMousePos,o=s(this.startDragHandlePos+e,0,this.visibleHeight-this.scrollHandleHeight),r=this.getScrollValueFromHandlePosition(o);this.updateScrollPosition(r)},onHandleDragEnd:function(t){this.setState({onDrag:!1}),t.preventDefault(),document.removeEventListener("mousemove",this.onHandleDrag),document.removeEventListener("mouseup",this.onHandleDragEnd)},blockOuterScroll:function(t){if(!this.props.allowOuterScroll){var e=t.currentTarget,o=t.currentTarget.scrollHeight,r=o-t.currentTarget.offsetHeight,s=t.deltaY%3?t.deltaY:10*t.deltaY;e.scrollTop+s<=0?(e.scrollTop=0,t.preventDefault()):e.scrollTop+s>=r&&(e.scrollTop=r,t.preventDefault()),t.stopPropagation()}},getInnerContainerClasses:function(){var t="inner-container";return this.state.scrollPos&&this.props.addScrolledClass&&(t+=" content-scrolled"),t},getScrollStyles:function(){var t,e,o=this.scrollbarYWidth||20,s=this.props.rtl?"marginLeft":"marginRight",l=(t={},r(t,s,-1*o),r(t,"height",this.props.heightRelativeToParent||this.props.flex?"100%":""),t),i=(e={},r(e,s,this.scrollbarYWidth?0:o),r(e,"height",this.props.heightRelativeToParent||this.props.flex?"100%":""),r(e,"overflowY",this.props.freezePosition?"hidden":"visible"),e);return{innerContainer:l,contentWrapper:i}},getOuterContainerStyle:function(){return{height:this.props.heightRelativeToParent||this.props.flex?"100%":""}},getRootStyles:function(){var t={};return this.props.heightRelativeToParent?t.height=this.props.heightRelativeToParent:this.props.flex&&(t.flex=this.props.flex),t},render:function(){var t=this.getScrollStyles(),e=this.getRootStyles(),o=i.call(this,this.getScrollHandleStyle());return n.createElement("div",{className:"custom-scroll "+(this.state.onDrag?"scroll-handle-dragged":""),style:e},n.createElement("div",{className:"outer-container",style:this.getOuterContainerStyle(),onMouseDown:this.onMouseDown,onClick:this.onClick},this.hasScroll?n.createElement("div",{ref:"customScrollbar",className:"custom-scrollbar"+(this.props.rtl?" custom-scrollbar-rtl":""),key:"scrollbar"},n.createElement("div",{ref:"scrollHandle",className:"custom-scroll-handle",style:o},n.createElement("div",{className:this.props.handleClass}))):null,n.createElement("div",{ref:"innerContainer",className:this.getInnerContainerClasses(),style:t.innerContainer,onScroll:this.onScroll,onWheel:this.blockOuterScroll},n.createElement("div",{className:"content-wrapper",ref:"contentWrapper",style:t.contentWrapper},this.props.children))))}})},function(e,o){e.exports=t},function(t,o){t.exports=e}])}); |
@@ -21,10 +21,37 @@ define(['react', 'lodash', './demoText', './firstComp.rt'], function (React, _, demoText, template) { | ||
displayName: 'firstComp', | ||
getInitialState: function () { | ||
return { | ||
dynamicContentCounter: 4 | ||
}; | ||
}, | ||
getText: function () { | ||
return demoText.text; | ||
}, | ||
isFlexExample: function () { | ||
return getParameterByName('flex'); | ||
getDynamicContent: function () { | ||
return _.times(this.state.dynamicContentCounter, function (index) { | ||
return 'Content #' + index; | ||
}); | ||
}, | ||
addContent: function () { | ||
this.setState({ | ||
dynamicContentCounter: this.state.dynamicContentCounter + 1 | ||
}); | ||
}, | ||
removeContent: function () { | ||
this.setState({ | ||
dynamicContentCounter: Math.max(this.state.dynamicContentCounter - 1, 4) | ||
}); | ||
}, | ||
getExamplesToDisplay: function () { | ||
var isFlex = getParameterByName('flex'); | ||
var isDynamic = getParameterByName('dynamic'); | ||
return { | ||
flex: isFlex, | ||
dynamic: isDynamic, | ||
standard: !isFlex && !isDynamic | ||
}; | ||
}, | ||
render: template | ||
}); | ||
}); |
@@ -7,13 +7,20 @@ define([ | ||
'use strict'; | ||
return function () { | ||
return React.createElement('div', { 'className': 'example-wrapper' }, !this.isFlexExample() ? React.createElement('div', { | ||
function repeatContent1(exampleTypes, content, contentIndex) { | ||
return React.createElement('div', { | ||
'className': 'dynamic-content', | ||
'key': content | ||
}, content); | ||
} | ||
function scopeExampleTypes2() { | ||
var exampleTypes = this.getExamplesToDisplay(); | ||
return React.createElement('div', { 'className': 'example-wrapper' }, exampleTypes.standard ? React.createElement('div', { | ||
'key': 'native-example', | ||
'className': 'container native-scroll' | ||
}, React.createElement('label', { 'className': 'side-title' }, 'Native Scroll'), React.createElement('div', { 'className': 'panel' }, React.createElement('div', { 'className': 'panel-header' }, React.createElement('label', { 'className': 'panel-title' }, 'This is boring')), React.createElement('div', { 'className': 'panel-content-native panel-content' }, React.createElement('div', { 'className': 'content-fill' }, this.getText())))) : null, !this.isFlexExample() ? React.createElement('div', { | ||
}, React.createElement('label', { 'className': 'side-title' }, 'Native Scroll'), React.createElement('div', { 'className': 'panel' }, React.createElement('div', { 'className': 'panel-header' }, React.createElement('label', { 'className': 'panel-title' }, 'This is boring')), React.createElement('div', { 'className': 'panel-content-native panel-content' }, React.createElement('div', { 'className': 'content-fill' }, this.getText())))) : null, exampleTypes.standard ? React.createElement('div', { | ||
'key': 'cool-example', | ||
'className': 'container custom-scroll-example' | ||
}, React.createElement('label', { 'className': 'side-title' }, 'Custom Scroll'), React.createElement('div', { 'className': 'panel' }, React.createElement('div', { 'className': 'panel-header' }, React.createElement('label', { 'className': 'panel-title' }, 'Cool Scrollbar !')), React.createElement(customScroll, { 'allowOuterScroll': true }, React.createElement('div', { 'className': 'panel-content-custom panel-content' }, React.createElement('div', { 'className': 'content-fill' }, this.getText()))))) : null, !this.isFlexExample() ? React.createElement('div', { | ||
}, React.createElement('label', { 'className': 'side-title' }, 'Custom Scroll'), React.createElement('div', { 'className': 'panel' }, React.createElement('div', { 'className': 'panel-header' }, React.createElement('label', { 'className': 'panel-title' }, 'Cool Scrollbar !')), React.createElement(customScroll, { 'allowOuterScroll': true }, React.createElement('div', { 'className': 'panel-content-custom panel-content' }, React.createElement('div', { 'className': 'content-fill' }, this.getText()))))) : null, exampleTypes.standard ? React.createElement('div', { | ||
'key': 'crazy-example', | ||
'className': 'container custom-scroll-example' | ||
}, React.createElement('label', { 'className': 'side-title' }, 'Crazy Designer'), React.createElement('div', { 'className': 'panel crazy-scroll' }, React.createElement('div', { 'className': 'panel-header' }, React.createElement('label', { 'className': 'panel-title' }, 'Who designed this ???')), React.createElement(customScroll, { 'allowOuterScroll': true }, React.createElement('div', { 'className': 'panel-content-custom panel-content' }, React.createElement('div', { 'className': 'content-fill' }, this.getText()))))) : null, this.isFlexExample() ? React.createElement('div', { | ||
}, React.createElement('label', { 'className': 'side-title' }, 'Crazy Designer'), React.createElement('div', { 'className': 'panel crazy-scroll' }, React.createElement('div', { 'className': 'panel-header' }, React.createElement('label', { 'className': 'panel-title' }, 'Who designed this ???')), React.createElement(customScroll, { 'allowOuterScroll': true }, React.createElement('div', { 'className': 'panel-content-custom panel-content' }, React.createElement('div', { 'className': 'content-fill' }, this.getText()))))) : null, exampleTypes.flex ? React.createElement('div', { | ||
'key': 'flex-example', | ||
@@ -24,4 +31,25 @@ 'className': 'container example-flex-wrapper' | ||
'flex': '1' | ||
}, React.createElement('div', { 'className': 'panel-content-custom panel-content' }, React.createElement('div', { 'className': 'content-fill' }, this.getText()))))) : null, React.createElement('div', { 'className': 'scroll-creator' })); | ||
}, React.createElement('div', { 'className': 'panel-content-custom panel-content' }, React.createElement('div', { 'className': 'content-fill' }, this.getText()))))) : null, exampleTypes.dynamic ? React.createElement('div', { | ||
'key': 'dynamic-example', | ||
'className': 'container example-dynamic-wrapper' | ||
}, React.createElement('label', { 'className': 'side-title' }, 'KeepAtBottom prop'), React.createElement('div', { 'className': 'panel dynamic-scroll' }, React.createElement('div', { 'className': 'panel-header' }, React.createElement('label', { 'className': 'panel-title' }, 'DYNAMIC CONTENT!!!')), React.createElement(customScroll, { | ||
'allowOuterScroll': true, | ||
'keepAtBottom': true | ||
}, React.createElement('div', { 'className': 'panel-content-custom panel-content' }, React.createElement.apply(this, [ | ||
'div', | ||
{ 'className': 'content-fill' }, | ||
_.map(this.getDynamicContent(), repeatContent1.bind(this, exampleTypes)) | ||
])))), exampleTypes.dynamic ? React.createElement('button', { | ||
'className': 'dynamic-content-button', | ||
'key': 'addContent', | ||
'onClick': this.addContent | ||
}, 'Add Content') : null, exampleTypes.dynamic ? React.createElement('button', { | ||
'className': 'dynamic-content-button', | ||
'key': 'removeContent', | ||
'onClick': this.removeContent | ||
}, 'Remove Content') : null) : null, React.createElement('div', { 'className': 'scroll-creator' })); | ||
} | ||
return function () { | ||
return scopeExampleTypes2.apply(this, []); | ||
}; | ||
}); |
{ | ||
"name": "react-custom-scroll", | ||
"version": "1.9.0", | ||
"version": "1.10.0", | ||
"description": "An easily designable, cross browser (!!), custom scroll with ReactJS Animations and scroll rate **exactly** like native scroll", | ||
@@ -32,5 +32,6 @@ "main": "index", | ||
"css-loader": "0.23.1", | ||
"eslint": "^2.0.0", | ||
"eslint-config-wix-editor": "^0.2.0", | ||
"eslint-plugin-react": "^3.16.1", | ||
"eslint": "^3.6.11", | ||
"eslint-config-wix-editor": "^0.2.3", | ||
"eslint-plugin-react": "^6.10.0", | ||
"eslint-plugin-lodash": "2.2.5", | ||
"jasmine": "2.4.1", | ||
@@ -49,3 +50,3 @@ "jasmine-core": "2.4.1", | ||
"react-templates": "^0.4.1", | ||
"lodash": "^3.10.1", | ||
"lodash": "^4.17.4", | ||
"style-loader": "0.13.1", | ||
@@ -52,0 +53,0 @@ "webpack": "^1.12.13" |
@@ -42,13 +42,14 @@ [![NPM version][npm-image]][npm-url] | ||
- allowOuterScroll : boolean, default false. Blocks outer scroll while scrolling the content | ||
- heightRelativeToParent : string, default undefined. Content height limit is relative to parent - the value should be the height limit. | ||
- flex : number, default undefined. If present will apply to the content wrapped by the custom scroll. | ||
- **allowOuterScroll** : boolean, default false. Blocks outer scroll while scrolling the content | ||
- **heightRelativeToParent** : string, default undefined. Content height limit is relative to parent - the value should be the height limit. | ||
- **flex** : number, default undefined. If present will apply to the content wrapped by the custom scroll. | ||
This prop represents flex size. It is only relevant if the parent of customScroll has display: flex. See example below. | ||
This prop will override any value given to heightRelativeToParent when setting the height of customScroll. | ||
- onScroll - function, default undefined. Listener that will be called on each scroll. | ||
- addScrolledClass : boolean, default false. If true, will add a css class 'content-scrolled' while being scrolled. | ||
- freezePosition : boolean, default false. When true, will prevent scrolling. | ||
- minScrollHandleHeight : number, sets the mimimum height of the scroll handle. Default is 38, as in Chrome on OSX. | ||
- rtl : boolean, default false. Right to left document, will place the custom scrollbar on the left side of the content, and assume the native one is also there. | ||
- scrollTo: number, default undefined. Will scroll content to the given value. | ||
- **onScroll** - function, default undefined. Listener that will be called on each scroll. | ||
- **addScrolledClass** : boolean, default false. If true, will add a css class 'content-scrolled' while being scrolled. | ||
- **freezePosition** : boolean, default false. When true, will prevent scrolling. | ||
- **minScrollHandleHeight** : number, sets the mimimum height of the scroll handle. Default is 38, as in Chrome on OSX. | ||
- **rtl** : boolean, default false. Right to left document, will place the custom scrollbar on the left side of the content, and assume the native one is also there. | ||
- **scrollTo**: number, default undefined. Will scroll content to the given value. | ||
- **keepAtBottom**: boolean, default false. For dynamic content, will keep the scroll position at the bottom of the content, when the content changes, if the position was at the bottom before the change. [See example here](http://rommguy.github.io/react-custom-scroll/example/demo.html?dynamic=true) | ||
@@ -55,0 +56,0 @@ ##### Example for heightRelativeToParent |
'use strict'; | ||
var React = require('react'); | ||
var reactDOM = require('react-dom'); | ||
const React = require('react'); | ||
const reactDOM = require('react-dom'); | ||
@@ -49,2 +49,3 @@ function ensureWithinLimits(value, min, max) { | ||
propTypes: { | ||
children: React.PropTypes.any, | ||
allowOuterScroll: React.PropTypes.bool, | ||
@@ -59,3 +60,4 @@ heightRelativeToParent: React.PropTypes.string, | ||
rtl: React.PropTypes.bool, | ||
scrollTo: React.PropTypes.number | ||
scrollTo: React.PropTypes.number, | ||
keepAtBottom: React.PropTypes.bool | ||
}, | ||
@@ -78,11 +80,16 @@ getDefaultProps() { | ||
}, | ||
componentDidUpdate(prevProps) { | ||
var domNode = reactDOM.findDOMNode(this); | ||
var boundingRect = domNode.getBoundingClientRect(); | ||
var innerContainer = this.getScrolledElement(); | ||
componentDidUpdate(prevProps, prevState) { | ||
const prevContentHeight = this.contentHeight; | ||
const prevVisibleHeight = this.visibleHeight; | ||
const domNode = reactDOM.findDOMNode(this); | ||
const boundingRect = domNode.getBoundingClientRect(); | ||
const innerContainer = this.getScrolledElement(); | ||
const reachedBottomOnPrevRender = prevState.scrollPos >= prevContentHeight - prevVisibleHeight; | ||
this.contentHeight = innerContainer.scrollHeight; | ||
this.scrollbarYWidth = innerContainer.offsetWidth - innerContainer.clientWidth; | ||
this.visibleHeight = innerContainer.clientHeight; | ||
this.scrollRatio = this.contentHeight ? this.visibleHeight / this.contentHeight : 1; | ||
const reachedBottomOnCurrentRender = this.state.scrollPos >= this.contentHeight - this.visibleHeight; | ||
const contentResized = prevContentHeight !== this.contentHeight; | ||
@@ -101,2 +108,4 @@ this.toggleScrollIfNeeded(); | ||
this.updateScrollPosition(this.props.scrollTo); | ||
} else if (this.props.keepAtBottom && contentResized && reachedBottomOnPrevRender && !reachedBottomOnCurrentRender) { | ||
this.updateScrollPosition(this.contentHeight - this.visibleHeight); | ||
} | ||
@@ -109,4 +118,4 @@ }, | ||
adjustFreezePosition(prevProps) { | ||
var innerContainer = this.getScrolledElement(); | ||
var contentWrapper = this.refs.contentWrapper; | ||
const innerContainer = this.getScrolledElement(); | ||
const contentWrapper = this.refs.contentWrapper; | ||
@@ -122,3 +131,3 @@ if (this.props.freezePosition) { | ||
toggleScrollIfNeeded() { | ||
var shouldHaveScroll = this.contentHeight - this.visibleHeight > 1; | ||
const shouldHaveScroll = this.contentHeight - this.visibleHeight > 1; | ||
if (this.hasScroll !== shouldHaveScroll) { | ||
@@ -133,3 +142,3 @@ this.hasScroll = shouldHaveScroll; | ||
updateScrollPosition(scrollValue) { | ||
var innerContainer = this.getScrolledElement(); | ||
const innerContainer = this.getScrolledElement(); | ||
innerContainer.scrollTop = scrollValue; | ||
@@ -158,6 +167,6 @@ this.setState({ | ||
calculateNewScrollHandleTop(clickEvent) { | ||
var clickYRelativeToScrollbar = clickEvent.pageY - this.position.top; | ||
var scrollHandleTop = this.getScrollHandleStyle().top; | ||
var newScrollHandleTop; | ||
var isBelowHandle = clickYRelativeToScrollbar > (scrollHandleTop + this.scrollHandleHeight); | ||
const clickYRelativeToScrollbar = clickEvent.pageY - this.position.top; | ||
const scrollHandleTop = this.getScrollHandleStyle().top; | ||
let newScrollHandleTop; | ||
const isBelowHandle = clickYRelativeToScrollbar > (scrollHandleTop + this.scrollHandleHeight); | ||
if (isBelowHandle) { | ||
@@ -174,3 +183,3 @@ newScrollHandleTop = scrollHandleTop + Math.min(this.scrollHandleHeight, this.visibleHeight - this.scrollHandleHeight); | ||
getScrollHandleStyle() { | ||
var handlePosition = this.state.scrollPos * this.scrollRatio; | ||
const handlePosition = this.state.scrollPos * this.scrollRatio; | ||
this.scrollHandleHeight = this.visibleHeight * this.scrollRatio; | ||
@@ -214,5 +223,5 @@ return { | ||
event.preventDefault(); | ||
var mouseDeltaY = event.pageY - this.startDragMousePos; | ||
var handleTopPosition = ensureWithinLimits(this.startDragHandlePos + mouseDeltaY, 0, this.visibleHeight - this.scrollHandleHeight); | ||
var newScrollValue = this.getScrollValueFromHandlePosition(handleTopPosition); | ||
const mouseDeltaY = event.pageY - this.startDragMousePos; | ||
const handleTopPosition = ensureWithinLimits(this.startDragHandlePos + mouseDeltaY, 0, this.visibleHeight - this.scrollHandleHeight); | ||
const newScrollValue = this.getScrollValueFromHandlePosition(handleTopPosition); | ||
this.updateScrollPosition(newScrollValue); | ||
@@ -232,6 +241,6 @@ }, | ||
} | ||
var contentNode = e.currentTarget; | ||
var totalHeight = e.currentTarget.scrollHeight; | ||
var maxScroll = totalHeight - e.currentTarget.offsetHeight; | ||
var delta = e.deltaY % 3 ? (e.deltaY) : (e.deltaY * 10); | ||
const contentNode = e.currentTarget; | ||
const totalHeight = e.currentTarget.scrollHeight; | ||
const maxScroll = totalHeight - e.currentTarget.offsetHeight; | ||
const delta = e.deltaY % 3 ? (e.deltaY) : (e.deltaY * 10); | ||
if (contentNode.scrollTop + delta <= 0) { | ||
@@ -247,3 +256,3 @@ contentNode.scrollTop = 0; | ||
getInnerContainerClasses() { | ||
var res = 'inner-container'; | ||
let res = 'inner-container'; | ||
if (this.state.scrollPos && this.props.addScrolledClass) { | ||
@@ -255,9 +264,9 @@ res += ' content-scrolled'; | ||
getScrollStyles() { | ||
var scrollSize = this.scrollbarYWidth || 20; | ||
var marginKey = this.props.rtl ? 'marginLeft' : 'marginRight'; | ||
var innerContainerStyle = { | ||
const scrollSize = this.scrollbarYWidth || 20; | ||
const marginKey = this.props.rtl ? 'marginLeft' : 'marginRight'; | ||
const innerContainerStyle = { | ||
[marginKey]: (-1 * scrollSize), | ||
height: (this.props.heightRelativeToParent || this.props.flex) ? '100%' : '' | ||
}; | ||
var contentWrapperStyle = { | ||
const contentWrapperStyle = { | ||
[marginKey]: this.scrollbarYWidth ? 0 : scrollSize, | ||
@@ -279,3 +288,3 @@ height: (this.props.heightRelativeToParent || this.props.flex) ? '100%' : '', | ||
getRootStyles() { | ||
let result = {}; | ||
const result = {}; | ||
@@ -296,3 +305,3 @@ if (this.props.heightRelativeToParent) { | ||
return ( | ||
<div className={'custom-scroll ' + (this.state.onDrag ? 'scroll-handle-dragged' : '')} | ||
<div className={`custom-scroll ${ this.state.onDrag ? 'scroll-handle-dragged' : ''}`} | ||
style={rootStyle}> | ||
@@ -305,3 +314,3 @@ <div className="outer-container" | ||
<div ref="customScrollbar" | ||
className={'custom-scrollbar' + (this.props.rtl ? ' custom-scrollbar-rtl' : '')} | ||
className={`custom-scrollbar${ this.props.rtl ? ' custom-scrollbar-rtl' : ''}`} | ||
key="scrollbar"> | ||
@@ -308,0 +317,0 @@ <div ref="scrollHandle" className="custom-scroll-handle" style={scrollHandleStyle}> |
@@ -17,3 +17,3 @@ var React = require('react'); | ||
this.visibleHeight = 100; | ||
this.customScroll = createAndRenderCustomScroll(this.customScrollContainer, {}, this.visibleHeight, this.totalScrollHeight); | ||
this.customScroll = renderCustomScroll(this.customScrollContainer, {}, this.visibleHeight, this.totalScrollHeight); | ||
}); | ||
@@ -25,3 +25,3 @@ | ||
function createAndRenderCustomScroll(container, props, visibleHeight, contentHeight) { | ||
function renderCustomScroll(container, props, visibleHeight, contentHeight) { | ||
var scrolledContent = React.createElement('div', { | ||
@@ -105,3 +105,3 @@ style: { | ||
var propsOnScroll = jasmine.createSpy('onScroll'); | ||
this.customScroll = createAndRenderCustomScroll(this.customScrollContainer, { | ||
this.customScroll = renderCustomScroll(this.customScrollContainer, { | ||
onScroll: propsOnScroll | ||
@@ -141,3 +141,3 @@ }, this.visibleHeight, this.totalScrollHeight); | ||
this.visibleHeight = 200; | ||
this.customScroll = createAndRenderCustomScroll(this.customScrollContainer, {}, this.visibleHeight, this.totalScrollHeight); | ||
this.customScroll = renderCustomScroll(this.customScrollContainer, {}, this.visibleHeight, this.totalScrollHeight); | ||
}); | ||
@@ -155,3 +155,3 @@ | ||
it('should set the handle size to minimum height from props', function () { | ||
this.customScroll = createAndRenderCustomScroll(this.customScrollContainer, { | ||
this.customScroll = renderCustomScroll(this.customScrollContainer, { | ||
minScrollHandleHeight: 50 | ||
@@ -177,3 +177,3 @@ }, this.visibleHeight, this.totalScrollHeight); | ||
this.visibleHeight = 100; | ||
this.customScroll = createAndRenderCustomScroll(this.customScrollContainer, { | ||
this.customScroll = renderCustomScroll(this.customScrollContainer, { | ||
freezePosition: true | ||
@@ -196,3 +196,3 @@ }, this.visibleHeight, this.totalScrollHeight); | ||
beforeEach(function () { | ||
this.customScroll = createAndRenderCustomScroll(this.customScrollContainer, { | ||
this.customScroll = renderCustomScroll(this.customScrollContainer, { | ||
heightRelativeToParent: '70%' | ||
@@ -216,3 +216,3 @@ }, this.visibleHeight, this.totalScrollHeight); | ||
beforeEach(function () { | ||
this.customScroll = createAndRenderCustomScroll(this.customScrollContainer, {}, this.visibleHeight, this.totalScrollHeight); | ||
this.customScroll = renderCustomScroll(this.customScrollContainer, {}, this.visibleHeight, this.totalScrollHeight); | ||
}); | ||
@@ -235,3 +235,3 @@ | ||
beforeEach(function () { | ||
this.customScroll = createAndRenderCustomScroll(this.customScrollContainer, { | ||
this.customScroll = renderCustomScroll(this.customScrollContainer, { | ||
flex: '2' | ||
@@ -255,4 +255,4 @@ }, this.visibleHeight, this.totalScrollHeight); | ||
beforeEach(function () { | ||
this.customScroll = createAndRenderCustomScroll(this.customScrollContainer, { | ||
rtl: 'true' | ||
this.customScroll = renderCustomScroll(this.customScrollContainer, { | ||
rtl: true | ||
}, this.visibleHeight, this.totalScrollHeight); | ||
@@ -274,3 +274,3 @@ }); | ||
it('should replace the default class', function () { | ||
this.customScroll = createAndRenderCustomScroll(this.customScrollContainer, { | ||
this.customScroll = renderCustomScroll(this.customScrollContainer, { | ||
handleClass: 'some-custom-class' | ||
@@ -331,3 +331,3 @@ }, this.visibleHeight, this.totalScrollHeight); | ||
const visibleHeight = contentHeight + 100; | ||
this.customScroll = createAndRenderCustomScroll(this.customScrollContainer, {}, visibleHeight, contentHeight); | ||
this.customScroll = renderCustomScroll(this.customScrollContainer, {}, visibleHeight, contentHeight); | ||
@@ -400,3 +400,3 @@ const yOnHandle = this.scrollHandleLayout.top + this.scrollHandleLayout.height / 2; | ||
scrollToValue = 10; | ||
customScroll = createAndRenderCustomScroll(this.customScrollContainer, {scrollTo: scrollToValue}, this.visibleHeight, this.totalScrollHeight); | ||
customScroll = renderCustomScroll(this.customScrollContainer, {scrollTo: scrollToValue}, this.visibleHeight, this.totalScrollHeight); | ||
outerContainer = TestUtils.findRenderedDOMComponentWithClass(customScroll, 'outer-container'); | ||
@@ -433,3 +433,84 @@ }); | ||
}); | ||
describe('keepAtBottom', function () { | ||
describe('when false', function () { | ||
it('should not scroll to bottom if the scroll is at the bottom', function () { | ||
const contentContainerNode = this.customScroll.refs.innerContainer; | ||
const expectedScrollTop = this.totalScrollHeight - this.visibleHeight; | ||
// scroll to bottom | ||
renderCustomScroll(this.customScrollContainer, {scrollTo: this.totalScrollHeight}, this.visibleHeight, this.totalScrollHeight); | ||
expect(contentContainerNode.scrollTop).toEqual(expectedScrollTop); | ||
// add content | ||
renderCustomScroll(this.customScrollContainer, {}, this.visibleHeight, this.totalScrollHeight + 500); | ||
expect(contentContainerNode.scrollTop).toEqual(expectedScrollTop); | ||
}); | ||
}); | ||
describe('when true', function () { | ||
describe('when content is added', function () { | ||
it('should automatically scroll to bottom if the scroll is at the bottom', function () { | ||
const addedContentHeight = 500; | ||
const contentContainerNode = this.customScroll.refs.innerContainer; | ||
const expectedScrollTop = this.totalScrollHeight - this.visibleHeight + addedContentHeight; | ||
// scroll to bottom | ||
renderCustomScroll(this.customScrollContainer, {scrollTo: this.totalScrollHeight}, this.visibleHeight, this.totalScrollHeight); | ||
expect(contentContainerNode.scrollTop).toEqual(this.totalScrollHeight - this.visibleHeight); | ||
// add content | ||
renderCustomScroll(this.customScrollContainer, {keepAtBottom: true}, this.visibleHeight, this.totalScrollHeight + addedContentHeight); | ||
expect(contentContainerNode.scrollTop).toEqual(expectedScrollTop); | ||
}); | ||
it('should not scroll to bottom if the scroll was not at the bottom', function () { | ||
const addedContentHeight = 50; | ||
const contentContainerNode = this.customScroll.refs.innerContainer; | ||
const initialScrollTop = contentContainerNode.scrollTop; | ||
// add content | ||
renderCustomScroll(this.customScrollContainer, {keepAtBottom: true}, this.visibleHeight, this.totalScrollHeight + addedContentHeight); | ||
expect(contentContainerNode.scrollTop).toEqual(initialScrollTop); | ||
}); | ||
}); | ||
describe('when content is the same', function () { | ||
it('should not scroll to bottom if the scroll is at the bottom', function () { | ||
const contentContainerNode = this.customScroll.refs.innerContainer; | ||
const expectedScrollTop = this.totalScrollHeight - this.visibleHeight; | ||
// scroll to bottom | ||
renderCustomScroll(this.customScrollContainer, {scrollTo: this.totalScrollHeight}, this.visibleHeight, this.totalScrollHeight); | ||
expect(contentContainerNode.scrollTop).toEqual(expectedScrollTop); | ||
renderCustomScroll(this.customScrollContainer, {keepAtBottom: true}, this.visibleHeight, this.totalScrollHeight); | ||
expect(contentContainerNode.scrollTop).toEqual(expectedScrollTop); | ||
}); | ||
it('should allow regular scroll', function () { | ||
this.customScroll = renderCustomScroll(this.customScrollContainer, { | ||
keepAtBottom: true, | ||
scrollTo: this.totalScrollHeight | ||
}, this.visibleHeight, this.totalScrollHeight); | ||
var contentContainerNode = this.customScroll.refs.innerContainer; | ||
contentContainerNode.scrollTop = 0; | ||
TestUtils.Simulate.scroll(contentContainerNode); | ||
expect(contentContainerNode.scrollTop).toEqual(0); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); | ||
@@ -28,2 +28,3 @@ 'use strict'; | ||
}, | ||
// devtool: 'source-map', | ||
externals: { | ||
@@ -30,0 +31,0 @@ react: 'react', |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
128845
33
2133
134
26