react-scroll-sync
Advanced tools
Comparing version
@@ -130,22 +130,26 @@ (function webpackUniversalModuleDefinition(root, factory) { | ||
return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref = ScrollSync.__proto__ || Object.getPrototypeOf(ScrollSync)).call.apply(_ref, [this].concat(args))), _this), _this.panes = {}, _this.registerPane = function (node, group) { | ||
if (!_this.panes[group]) { | ||
_this.panes[group] = []; | ||
} | ||
return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref = ScrollSync.__proto__ || Object.getPrototypeOf(ScrollSync)).call.apply(_ref, [this].concat(args))), _this), _this.panes = {}, _this.registerPane = function (node, groups) { | ||
groups.forEach(function (group) { | ||
if (!_this.panes[group]) { | ||
_this.panes[group] = []; | ||
} | ||
if (!_this.findPane(node, group)) { | ||
if (_this.panes[group].length > 0) { | ||
_this.syncScrollPosition(_this.panes[group][0], node); | ||
if (!_this.findPane(node, group)) { | ||
if (_this.panes[group].length > 0) { | ||
_this.syncScrollPosition(_this.panes[group][0], node); | ||
} | ||
_this.panes[group].push(node); | ||
} | ||
_this.addEvents(node, group); | ||
_this.panes[group].push(node); | ||
} | ||
}, _this.unregisterPane = function (node, group) { | ||
if (_this.findPane(node, group)) { | ||
_this.removeEvents(node); | ||
_this.panes[group].splice(_this.panes[group].indexOf(node), 1); | ||
} | ||
}, _this.addEvents = function (node, group) { | ||
}); | ||
_this.addEvents(node, groups); | ||
}, _this.unregisterPane = function (node, groups) { | ||
groups.forEach(function (group) { | ||
if (_this.findPane(node, group)) { | ||
_this.removeEvents(node); | ||
_this.panes[group].splice(_this.panes[group].indexOf(node), 1); | ||
} | ||
}); | ||
}, _this.addEvents = function (node, groups) { | ||
/* For some reason element.addEventListener doesnt work with document.body */ | ||
node.onscroll = _this.handlePaneScroll.bind(_this, node, group); // eslint-disable-line | ||
node.onscroll = _this.handlePaneScroll.bind(_this, node, groups); // eslint-disable-line | ||
}, _this.removeEvents = function (node) { | ||
@@ -162,3 +166,3 @@ /* For some reason element.removeEventListener doesnt work with document.body */ | ||
}); | ||
}, _this.handlePaneScroll = function (node, group) { | ||
}, _this.handlePaneScroll = function (node, groups) { | ||
if (!_this.props.enabled) { | ||
@@ -169,16 +173,18 @@ return; | ||
window.requestAnimationFrame(function () { | ||
_this.syncScrollPositions(node, group); | ||
_this.syncScrollPositions(node, groups); | ||
}); | ||
}, _this.syncScrollPositions = function (scrolledPane, group) { | ||
_this.panes[group].forEach(function (pane) { | ||
/* For all panes beside the currently scrolling one */ | ||
if (scrolledPane !== pane) { | ||
/* Remove event listeners from the node that we'll manipulate */ | ||
_this.removeEvents(pane, group); | ||
_this.syncScrollPosition(scrolledPane, pane); | ||
/* Re-attach event listeners after we're done scrolling */ | ||
window.requestAnimationFrame(function () { | ||
_this.addEvents(pane, group); | ||
}); | ||
} | ||
}, _this.syncScrollPositions = function (scrolledPane, groups) { | ||
groups.forEach(function (group) { | ||
_this.panes[group].forEach(function (pane) { | ||
/* For all panes beside the currently scrolling one */ | ||
if (scrolledPane !== pane) { | ||
/* Remove event listeners from the node that we'll manipulate */ | ||
_this.removeEvents(pane, group); | ||
_this.syncScrollPosition(scrolledPane, pane); | ||
/* Re-attach event listeners after we're done scrolling */ | ||
window.requestAnimationFrame(function () { | ||
_this.addEvents(pane, groups); | ||
}); | ||
} | ||
}); | ||
}); | ||
@@ -1380,5 +1386,15 @@ }, _temp), _possibleConstructorReturn(_this, _ret); | ||
function ScrollSyncPane() { | ||
var _ref; | ||
var _temp, _this, _ret; | ||
_classCallCheck(this, ScrollSyncPane); | ||
return _possibleConstructorReturn(this, (ScrollSyncPane.__proto__ || Object.getPrototypeOf(ScrollSyncPane)).apply(this, arguments)); | ||
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref = ScrollSyncPane.__proto__ || Object.getPrototypeOf(ScrollSyncPane)).call.apply(_ref, [this].concat(args))), _this), _this.toArray = function (groups) { | ||
return [].concat(groups); | ||
}, _temp), _possibleConstructorReturn(_this, _ret); | ||
} | ||
@@ -1389,4 +1405,6 @@ | ||
value: function componentDidMount() { | ||
this.node = this.props.attachTo || _reactDom2.default.findDOMNode(this); | ||
this.context.registerPane(this.node, this.props.group); | ||
if (this.props.enabled) { | ||
this.node = this.props.attachTo || _reactDom2.default.findDOMNode(this); | ||
this.context.registerPane(this.node, this.toArray(this.props.group)); | ||
} | ||
} | ||
@@ -1396,5 +1414,5 @@ }, { | ||
value: function componentWillReceiveProps(nextProps) { | ||
if (this.props.group !== nextProps.group) { | ||
this.context.unregisterPane(this.node, this.props.group); | ||
this.context.registerPane(this.node, nextProps.group); | ||
if (this.props.enabled && this.props.group !== nextProps.group) { | ||
this.context.unregisterPane(this.node, this.toArray(this.props.group)); | ||
this.context.registerPane(this.node, this.toArray(nextProps.group)); | ||
} | ||
@@ -1405,3 +1423,5 @@ } | ||
value: function componentWillUnmount() { | ||
this.context.unregisterPane(this.node, this.props.group); | ||
if (this.props.enabled) { | ||
this.context.unregisterPane(this.node, this.toArray(this.props.group)); | ||
} | ||
} | ||
@@ -1421,10 +1441,12 @@ }, { | ||
attachTo: _propTypes2.default.object, | ||
group: _propTypes2.default.string | ||
group: _propTypes2.default.oneOfType([_propTypes2.default.string, _propTypes2.default.arrayOf(_propTypes2.default.string)]), | ||
enabled: _propTypes2.default.bool | ||
}; | ||
ScrollSyncPane.defaultProps = { | ||
group: 'default' | ||
group: 'default', | ||
enabled: true | ||
}; | ||
ScrollSyncPane.contextTypes = { | ||
registerPane: _propTypes2.default.func.isRequired, | ||
unregisterPane: _propTypes2.default.func.isRequired | ||
registerPane: _propTypes2.default.func, | ||
unregisterPane: _propTypes2.default.func | ||
}; | ||
@@ -1431,0 +1453,0 @@ exports.default = ScrollSyncPane; |
{ | ||
"name": "react-scroll-sync", | ||
"version": "0.6.0", | ||
"version": "0.7.0", | ||
"description": "Syncronize scroll positions across multiple scrollable containers", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
To use ScrollSync you have to wrap your scrollable content (ensure that you have `overflow: auto` | ||
in CSS) in `ScrollSyncPane` and then wrap everything in `ScrollSync`. | ||
If you want to provide a toggle for users to turn the scroll syncing on and off, you can use the `enabled={false}` setting on the main `ScrollSync` element. Note that this disables the scroll syncing for all groups. | ||
If you want to provide a toggle for users to turn the scroll syncing on and off, you can use the `enabled={false}` setting on the `ScrollSync` or `ScrollSyncPane` elements. Note that `<ScrollSync enabled={false}>` disables the scroll syncing for all groups. | ||
@@ -108,1 +108,133 @@ ``` | ||
In some situations, it is also useful for a `ScrollSyncPane` to belong to multiple groups. In these cases, provide an array of group names to the `group` prop. | ||
``` | ||
const cellStyle = { minWidth: 200, padding: '.5em 1em', textAlign: 'left', borderLeft: '1px solid white', borderBottom: '1px solid white'}; | ||
<ScrollSync> | ||
<div style={{ display: 'flex', position: 'relative', height: 300 }}> | ||
<table style={{ minWidth: 200, borderCollapse: 'collapse' }}> | ||
<thead style={{ display: 'block', minWidth: 200, overflow: 'auto', color: 'white', background: 'grey' }}> | ||
<tr> | ||
<th style={cellStyle}>Fixed Column Header</th> | ||
</tr> | ||
</thead> | ||
<ScrollSyncPane group="vertical"> | ||
<tbody style={{ display: 'block', minWidth: 200, height: 200, overflowY: 'auto', background: 'lightblue' }}> | ||
<tr> | ||
<td style={cellStyle}>Fixed Column, Row 1</td> | ||
</tr> | ||
<tr> | ||
<td style={cellStyle}>Fixed Column, Row 2</td> | ||
</tr> | ||
<tr> | ||
<td style={cellStyle}>Fixed Column, Row 3</td> | ||
</tr> | ||
<tr> | ||
<td style={cellStyle}>Fixed Column, Row 4</td> | ||
</tr> | ||
<tr> | ||
<td style={cellStyle}>Fixed Column, Row 5</td> | ||
</tr> | ||
<tr> | ||
<td style={cellStyle}>Fixed Column, Row 6</td> | ||
</tr> | ||
<tr> | ||
<td style={cellStyle}>Fixed Column, Row 7</td> | ||
</tr> | ||
<tr> | ||
<td style={cellStyle}>Fixed Column, Row 8</td> | ||
</tr> | ||
<tr> | ||
<td style={cellStyle}>Fixed Column, Row 9</td> | ||
</tr> | ||
<tr> | ||
<td style={cellStyle}>Fixed Column, Row 10</td> | ||
</tr> | ||
<tr> | ||
<td style={cellStyle}>Fixed Column, Row 11</td> | ||
</tr> | ||
<tr> | ||
<td style={cellStyle}>Fixed Column, Row 12</td> | ||
</tr> | ||
</tbody> | ||
</ScrollSyncPane> | ||
</table> | ||
<table style={{ width: 400, borderCollapse: 'collapse' }}> | ||
<ScrollSyncPane group="horizontal"> | ||
<thead style={{ display: 'block', width: 400, overflow: 'auto', color: 'white', background: 'black' }}> | ||
<tr> | ||
<th style={cellStyle}>Table 2 - Header 1</th> | ||
<th style={cellStyle}>Table 2 - Header 2</th> | ||
<th style={cellStyle}>Table 2 - Header 3</th> | ||
</tr> | ||
</thead> | ||
</ScrollSyncPane> | ||
<ScrollSyncPane group={["horizontal", "vertical"]}> | ||
<tbody style={{ display: 'block', width: 400, height: 200, overflow: 'auto', background: 'pink' }}> | ||
<tr> | ||
<td style={cellStyle}>Cell 1, Row 1</td> | ||
<td style={cellStyle}>Cell 2, Row 1</td> | ||
<td style={cellStyle}>Cell 3, Row 1</td> | ||
</tr> | ||
<tr> | ||
<td style={cellStyle}>Cell 1, Row 2</td> | ||
<td style={cellStyle}>Cell 2, Row 2</td> | ||
<td style={cellStyle}>Cell 3, Row 2</td> | ||
</tr> | ||
<tr> | ||
<td style={cellStyle}>Cell 1, Row 3</td> | ||
<td style={cellStyle}>Cell 2, Row 3</td> | ||
<td style={cellStyle}>Cell 3, Row 3</td> | ||
</tr> | ||
<tr> | ||
<td style={cellStyle}>Cell 1, Row 4</td> | ||
<td style={cellStyle}>Cell 2, Row 4</td> | ||
<td style={cellStyle}>Cell 3, Row 4</td> | ||
</tr> | ||
<tr> | ||
<td style={cellStyle}>Cell 1, Row 5</td> | ||
<td style={cellStyle}>Cell 2, Row 5</td> | ||
<td style={cellStyle}>Cell 3, Row 5</td> | ||
</tr> | ||
<tr> | ||
<td style={cellStyle}>Cell 1, Row 6</td> | ||
<td style={cellStyle}>Cell 2, Row 6</td> | ||
<td style={cellStyle}>Cell 3, Row 6</td> | ||
</tr> | ||
<tr> | ||
<td style={cellStyle}>Cell 1, Row 7</td> | ||
<td style={cellStyle}>Cell 2, Row 7</td> | ||
<td style={cellStyle}>Cell 3, Row 7</td> | ||
</tr> | ||
<tr> | ||
<td style={cellStyle}>Cell 1, Row 8</td> | ||
<td style={cellStyle}>Cell 2, Row 8</td> | ||
<td style={cellStyle}>Cell 3, Row 8</td> | ||
</tr> | ||
<tr> | ||
<td style={cellStyle}>Cell 1, Row 9</td> | ||
<td style={cellStyle}>Cell 2, Row 9</td> | ||
<td style={cellStyle}>Cell 3, Row 9</td> | ||
</tr> | ||
<tr> | ||
<td style={cellStyle}>Cell 1, Row 10</td> | ||
<td style={cellStyle}>Cell 2, Row 10</td> | ||
<td style={cellStyle}>Cell 3, Row 10</td> | ||
</tr> | ||
<tr> | ||
<td style={cellStyle}>Cell 1, Row 11</td> | ||
<td style={cellStyle}>Cell 2, Row 11</td> | ||
<td style={cellStyle}>Cell 3, Row 11</td> | ||
</tr> | ||
<tr> | ||
<td style={cellStyle}>Cell 1, Row 12</td> | ||
<td style={cellStyle}>Cell 2, Row 12</td> | ||
<td style={cellStyle}>Cell 3, Row 12</td> | ||
</tr> | ||
</tbody> | ||
</ScrollSyncPane> | ||
</table> | ||
</div> | ||
</ScrollSync> | ||
``` |
@@ -40,26 +40,30 @@ import React, { Component } from 'react' | ||
registerPane = (node, group) => { | ||
if (!this.panes[group]) { | ||
this.panes[group] = [] | ||
} | ||
registerPane = (node, groups) => { | ||
groups.forEach((group) => { | ||
if (!this.panes[group]) { | ||
this.panes[group] = [] | ||
} | ||
if (!this.findPane(node, group)) { | ||
if (this.panes[group].length > 0) { | ||
this.syncScrollPosition(this.panes[group][0], node) | ||
if (!this.findPane(node, group)) { | ||
if (this.panes[group].length > 0) { | ||
this.syncScrollPosition(this.panes[group][0], node) | ||
} | ||
this.panes[group].push(node) | ||
} | ||
this.addEvents(node, group) | ||
this.panes[group].push(node) | ||
} | ||
}) | ||
this.addEvents(node, groups) | ||
} | ||
unregisterPane = (node, group) => { | ||
if (this.findPane(node, group)) { | ||
this.removeEvents(node) | ||
this.panes[group].splice(this.panes[group].indexOf(node), 1) | ||
} | ||
unregisterPane = (node, groups) => { | ||
groups.forEach((group) => { | ||
if (this.findPane(node, group)) { | ||
this.removeEvents(node) | ||
this.panes[group].splice(this.panes[group].indexOf(node), 1) | ||
} | ||
}) | ||
} | ||
addEvents = (node, group) => { | ||
addEvents = (node, groups) => { | ||
/* For some reason element.addEventListener doesnt work with document.body */ | ||
node.onscroll = this.handlePaneScroll.bind(this, node, group) // eslint-disable-line | ||
node.onscroll = this.handlePaneScroll.bind(this, node, groups) // eslint-disable-line | ||
} | ||
@@ -80,3 +84,3 @@ | ||
handlePaneScroll = (node, group) => { | ||
handlePaneScroll = (node, groups) => { | ||
if (!this.props.enabled) { | ||
@@ -87,3 +91,3 @@ return | ||
window.requestAnimationFrame(() => { | ||
this.syncScrollPositions(node, group) | ||
this.syncScrollPositions(node, groups) | ||
}) | ||
@@ -119,14 +123,16 @@ } | ||
syncScrollPositions = (scrolledPane, group) => { | ||
this.panes[group].forEach((pane) => { | ||
/* For all panes beside the currently scrolling one */ | ||
if (scrolledPane !== pane) { | ||
/* Remove event listeners from the node that we'll manipulate */ | ||
this.removeEvents(pane, group) | ||
this.syncScrollPosition(scrolledPane, pane) | ||
/* Re-attach event listeners after we're done scrolling */ | ||
window.requestAnimationFrame(() => { | ||
this.addEvents(pane, group) | ||
}) | ||
} | ||
syncScrollPositions = (scrolledPane, groups) => { | ||
groups.forEach((group) => { | ||
this.panes[group].forEach((pane) => { | ||
/* For all panes beside the currently scrolling one */ | ||
if (scrolledPane !== pane) { | ||
/* Remove event listeners from the node that we'll manipulate */ | ||
this.removeEvents(pane, group) | ||
this.syncScrollPosition(scrolledPane, pane) | ||
/* Re-attach event listeners after we're done scrolling */ | ||
window.requestAnimationFrame(() => { | ||
this.addEvents(pane, groups) | ||
}) | ||
} | ||
}) | ||
}) | ||
@@ -133,0 +139,0 @@ } |
@@ -21,23 +21,27 @@ /* eslint react/no-find-dom-node: 0 */ | ||
attachTo: PropTypes.object, | ||
group: PropTypes.string | ||
group: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]), | ||
enabled: PropTypes.bool | ||
} | ||
static defaultProps = { | ||
group: 'default' | ||
group: 'default', | ||
enabled: true | ||
} | ||
static contextTypes = { | ||
registerPane: PropTypes.func.isRequired, | ||
unregisterPane: PropTypes.func.isRequired | ||
}; | ||
registerPane: PropTypes.func, | ||
unregisterPane: PropTypes.func | ||
} | ||
componentDidMount() { | ||
this.node = this.props.attachTo || ReactDOM.findDOMNode(this) | ||
this.context.registerPane(this.node, this.props.group) | ||
if (this.props.enabled) { | ||
this.node = this.props.attachTo || ReactDOM.findDOMNode(this) | ||
this.context.registerPane(this.node, this.toArray(this.props.group)) | ||
} | ||
} | ||
componentWillReceiveProps(nextProps) { | ||
if (this.props.group !== nextProps.group) { | ||
this.context.unregisterPane(this.node, this.props.group) | ||
this.context.registerPane(this.node, nextProps.group) | ||
if (this.props.enabled && this.props.group !== nextProps.group) { | ||
this.context.unregisterPane(this.node, this.toArray(this.props.group)) | ||
this.context.registerPane(this.node, this.toArray(nextProps.group)) | ||
} | ||
@@ -47,5 +51,9 @@ } | ||
componentWillUnmount() { | ||
this.context.unregisterPane(this.node, this.props.group) | ||
if (this.props.enabled) { | ||
this.context.unregisterPane(this.node, this.toArray(this.props.group)) | ||
} | ||
} | ||
toArray = groups => [].concat(groups) | ||
render() { | ||
@@ -52,0 +60,0 @@ return this.props.children |
Sorry, the diff of this file is not supported yet
126664
6.01%1469
2.23%