This commit is contained in:
147
node_modules/react-scroll-sync/src/ScrollSync.js
generated
vendored
Normal file
147
node_modules/react-scroll-sync/src/ScrollSync.js
generated
vendored
Normal file
@@ -0,0 +1,147 @@
|
||||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
/**
|
||||
* ScrollSync provider component
|
||||
*
|
||||
*/
|
||||
|
||||
export default class ScrollSync extends Component {
|
||||
|
||||
static propTypes = {
|
||||
/**
|
||||
* Callback to be invoked any time synchronization happens
|
||||
*
|
||||
* @param {Element} el The element that has received the scroll event
|
||||
*/
|
||||
onSync: PropTypes.func,
|
||||
children: PropTypes.element.isRequired,
|
||||
proportional: PropTypes.bool,
|
||||
vertical: PropTypes.bool,
|
||||
horizontal: PropTypes.bool,
|
||||
enabled: PropTypes.bool
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
proportional: true,
|
||||
vertical: true,
|
||||
horizontal: true,
|
||||
enabled: true
|
||||
};
|
||||
|
||||
static childContextTypes = {
|
||||
registerPane: PropTypes.func,
|
||||
unregisterPane: PropTypes.func
|
||||
}
|
||||
|
||||
getChildContext() {
|
||||
return {
|
||||
registerPane: this.registerPane,
|
||||
unregisterPane: this.unregisterPane
|
||||
}
|
||||
}
|
||||
|
||||
panes = {}
|
||||
|
||||
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)
|
||||
}
|
||||
this.panes[group].push(node)
|
||||
}
|
||||
})
|
||||
this.addEvents(node, groups)
|
||||
}
|
||||
|
||||
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, groups) => {
|
||||
/* For some reason element.addEventListener doesnt work with document.body */
|
||||
node.onscroll = this.handlePaneScroll.bind(this, node, groups) // eslint-disable-line
|
||||
}
|
||||
|
||||
removeEvents = (node) => {
|
||||
/* For some reason element.removeEventListener doesnt work with document.body */
|
||||
node.onscroll = null // eslint-disable-line
|
||||
}
|
||||
|
||||
findPane = (node, group) => {
|
||||
if (!this.panes[group]) {
|
||||
return false
|
||||
}
|
||||
|
||||
return this.panes[group].find(pane => pane === node)
|
||||
}
|
||||
|
||||
handlePaneScroll = (node, groups) => {
|
||||
if (!this.props.enabled) {
|
||||
return
|
||||
}
|
||||
|
||||
window.requestAnimationFrame(() => {
|
||||
this.syncScrollPositions(node, groups)
|
||||
})
|
||||
}
|
||||
|
||||
syncScrollPosition(scrolledPane, pane) {
|
||||
const {
|
||||
scrollTop,
|
||||
scrollHeight,
|
||||
clientHeight,
|
||||
scrollLeft,
|
||||
scrollWidth,
|
||||
clientWidth
|
||||
} = scrolledPane
|
||||
|
||||
const scrollTopOffset = scrollHeight - clientHeight
|
||||
const scrollLeftOffset = scrollWidth - clientWidth
|
||||
|
||||
const { proportional, vertical, horizontal } = this.props
|
||||
|
||||
/* Calculate the actual pane height */
|
||||
const paneHeight = pane.scrollHeight - clientHeight
|
||||
const paneWidth = pane.scrollWidth - clientWidth
|
||||
/* Adjust the scrollTop position of it accordingly */
|
||||
if (vertical && scrollTopOffset > 0) {
|
||||
pane.scrollTop = proportional ? (paneHeight * scrollTop) / scrollTopOffset : scrollTop // eslint-disable-line
|
||||
}
|
||||
if (horizontal && scrollLeftOffset > 0) {
|
||||
pane.scrollLeft = proportional ? (paneWidth * scrollLeft) / scrollLeftOffset : scrollLeft // eslint-disable-line
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
if (this.props.onSync) this.props.onSync(scrolledPane)
|
||||
}
|
||||
|
||||
render() {
|
||||
return React.Children.only(this.props.children)
|
||||
}
|
||||
}
|
||||
60
node_modules/react-scroll-sync/src/ScrollSyncPane.js
generated
vendored
Normal file
60
node_modules/react-scroll-sync/src/ScrollSyncPane.js
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
/* eslint react/no-find-dom-node: 0 */
|
||||
|
||||
import { Component } from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
/**
|
||||
* ScrollSyncPane Component
|
||||
*
|
||||
* Wrap your content in it to keep its scroll position in sync with other panes
|
||||
*
|
||||
* @example ./example.md
|
||||
*/
|
||||
|
||||
|
||||
export default class ScrollSyncPane extends Component {
|
||||
|
||||
static propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
attachTo: PropTypes.object,
|
||||
group: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
|
||||
enabled: PropTypes.bool
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
group: 'default',
|
||||
enabled: true
|
||||
}
|
||||
|
||||
static contextTypes = {
|
||||
registerPane: PropTypes.func,
|
||||
unregisterPane: PropTypes.func
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.enabled) {
|
||||
this.node = this.props.attachTo || ReactDOM.findDOMNode(this)
|
||||
this.context.registerPane(this.node, this.toArray(this.props.group))
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (this.props.enabled && this.props.group !== prevProps.group) {
|
||||
this.context.unregisterPane(this.node, this.toArray(prevProps.group))
|
||||
this.context.registerPane(this.node, this.toArray(this.props.group))
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.props.enabled) {
|
||||
this.context.unregisterPane(this.node, this.toArray(this.props.group))
|
||||
}
|
||||
}
|
||||
|
||||
toArray = groups => [].concat(groups)
|
||||
|
||||
render() {
|
||||
return this.props.children
|
||||
}
|
||||
}
|
||||
239
node_modules/react-scroll-sync/src/example.md
generated
vendored
Normal file
239
node_modules/react-scroll-sync/src/example.md
generated
vendored
Normal file
@@ -0,0 +1,239 @@
|
||||
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 `ScrollSync` or `ScrollSyncPane` elements. Note that `<ScrollSync enabled={false}>` disables the scroll syncing for all groups.
|
||||
|
||||
```
|
||||
<ScrollSync>
|
||||
<div style={{ display: 'flex', position: 'relative', height: 300 }}>
|
||||
<ScrollSyncPane>
|
||||
<div style={{overflow: 'auto'}}>
|
||||
<section style={{ height: 500 }}>
|
||||
<h1>Left Pane Content</h1>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab aperiam doloribus
|
||||
dolorum
|
||||
est eum eveniet exercitationem iste labore minus, neque nobis odit officiis omnis
|
||||
possimus quasi rerum sed soluta veritatis.</p>
|
||||
</section>
|
||||
</div>
|
||||
</ScrollSyncPane>
|
||||
|
||||
<ScrollSyncPane>
|
||||
<div style={{overflow: 'auto'}}>
|
||||
<section style={{ height: 1000 }}>
|
||||
<h1>Middle Pane Content</h1>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab aperiam doloribus
|
||||
dolorum
|
||||
est eum eveniet exercitationem iste labore minus, neque nobis odit officiis omnis
|
||||
possimus quasi rerum sed soluta veritatis.</p>
|
||||
</section>
|
||||
</div>
|
||||
</ScrollSyncPane>
|
||||
|
||||
<ScrollSyncPane>
|
||||
<div style={{overflow: 'auto'}}>
|
||||
<section style={{ height: 2000 }}>
|
||||
<h1>Right Pane Content</h1>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab aperiam doloribus
|
||||
dolorum
|
||||
est eum eveniet exercitationem iste labore minus, neque nobis odit officiis omnis
|
||||
possimus quasi rerum sed soluta veritatis.</p>
|
||||
</section>
|
||||
</div>
|
||||
</ScrollSyncPane>
|
||||
</div>
|
||||
</ScrollSync>
|
||||
```
|
||||
|
||||
Sometimes it is useful to attach the `onScroll` event listener to a different node (for example
|
||||
to a `document.body`). Use `attachTo` prop for that.
|
||||
|
||||
Additionally sometimes there is need to use few independent scroll groups inside one ScrollSync.
|
||||
Provide an arbitrary group name in the `group` prop to ScrollSyncPane components to limit synchronization to panes with that group name.
|
||||
|
||||
```
|
||||
<ScrollSync>
|
||||
<div style={{ display: 'flex', position: 'relative', height: 300 }}>
|
||||
<ScrollSyncPane group="one">
|
||||
<div style={{overflow: 'auto'}}>
|
||||
<section style={{ height: 500 }}>
|
||||
<h1>Left Pane Content</h1>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab aperiam doloribus
|
||||
dolorum
|
||||
est eum eveniet exercitationem iste labore minus, neque nobis odit officiis omnis
|
||||
possimus quasi rerum sed soluta veritatis.</p>
|
||||
</section>
|
||||
</div>
|
||||
</ScrollSyncPane>
|
||||
|
||||
<ScrollSyncPane group="two">
|
||||
<div style={{overflow: 'auto'}}>
|
||||
<section style={{ height: 1000 }}>
|
||||
<h1>Middle Pane Content</h1>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab aperiam doloribus
|
||||
dolorum
|
||||
est eum eveniet exercitationem iste labore minus, neque nobis odit officiis omnis
|
||||
possimus quasi rerum sed soluta veritatis.</p>
|
||||
</section>
|
||||
</div>
|
||||
</ScrollSyncPane>
|
||||
|
||||
<ScrollSyncPane group="one">
|
||||
<div style={{overflow: 'auto'}}>
|
||||
<section style={{ height: 2000 }}>
|
||||
<h1>Right Pane Content</h1>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab aperiam doloribus
|
||||
dolorum
|
||||
est eum eveniet exercitationem iste labore minus, neque nobis odit officiis omnis
|
||||
possimus quasi rerum sed soluta veritatis.</p>
|
||||
</section>
|
||||
</div>
|
||||
</ScrollSyncPane>
|
||||
|
||||
<ScrollSyncPane group="two">
|
||||
<div style={{overflow: 'auto'}}>
|
||||
<section style={{ height: 2000 }}>
|
||||
<h1>Right Pane Content</h1>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab aperiam doloribus
|
||||
dolorum
|
||||
est eum eveniet exercitationem iste labore minus, neque nobis odit officiis omnis
|
||||
possimus quasi rerum sed soluta veritatis.</p>
|
||||
</section>
|
||||
</div>
|
||||
</ScrollSyncPane>
|
||||
</div>
|
||||
</ScrollSync>
|
||||
```
|
||||
|
||||
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>
|
||||
```
|
||||
2
node_modules/react-scroll-sync/src/index.js
generated
vendored
Normal file
2
node_modules/react-scroll-sync/src/index.js
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
export { default as ScrollSync } from './ScrollSync'
|
||||
export { default as ScrollSyncPane } from './ScrollSyncPane'
|
||||
Reference in New Issue
Block a user