react-selectable
react-selectable copied to clipboard
How to add horizontal autoscrolling when I select items in one long line with a horizontal scrollbar?
I have a string with elements. When there are a lot of them, a scrollbar appears. But I can't continue selecting elements outside of the visible area. How to do it?
I solved this problem for me. But I don't know how to add my solution to the original repository. My solution is:
- Install
react-selectablefrom my repository (branch:add-support-for-external-scrolling):
npm install git://github.com/danilvalov/react-selectable.git#add-support-for-external-scrolling --save
- Use the following example:
class ExampleWithHorizontalScrolling extends Component {
constructor () {
super();
this._isSelecting = false; // state of selection: active or not
this._scrollingTimer = null; // scrolling setTimeout
this._startingScrollLeft = 0; // starting container scrollLeft (when we start to select items)
this.onBeginSelection = this.onBeginSelection.bind(this);
this.onEndSelection = this.onEndSelection.bind(this);
this.startScrollingTimer = this.startScrollingTimer.bind(this);
this.stopScrollingTimer = this.stopScrollingTimer.bind(this);
this.updateScrollLeft = this.updateScrollLeft.bind(this);
this.handleSelection = this.handleSelection.bind(this);
}
scrollToDirection (direction) {
const {container, selectable} = this.refs;
if (container.scrollLeft >= container.scrollWidth) {
this.stopScrollingTimer();
return;
}
container.scrollLeft = container.scrollLeft + (5 * direction); // '5' is a scrolling step
selectable.changeScrollOffsets(container.scrollLeft - this._startingScrollLeft);
}
onBeginSelection () {
const {container} = this.refs;
this._isSelecting = true;
this._startingScrollLeft = container.scrollLeft;
}
onEndSelection () {
if (!this._scrollingTimer) {
return;
}
this._isSelecting = false;
this._startingScrollLeft = 0;
setTimeout(() => {
this.stopScrollingTimer();
});
}
startScrollingTimer (direction) {
if (!this._isSelecting) {
return;
}
this._scrollingTimer = setTimeout(() => {
requestAnimationFrame(() => {
this.startScrollingTimer(direction);
});
this.scrollToDirection(direction);
}, 50);
}
stopScrollingTimer () {
if (!this._scrollingTimer) {
return;
}
clearTimeout(this._scrollingTimer);
this._scrollingTimer = 0; // fix for IE
}
updateScrollLeft (e) {
// when we change `scrollLeftShift` in the `react-scrollable`
// we don't have `event` variable and we just need to ignore the action
if (typeof e === 'undefined') {
return;
}
// stop prevous scrolling setTimeout
this.stopScrollingTimer();
const {container} = this.refs;
const containerPosition = container.getBoundingClientRect();
const mousePositionX = e.pageX;
const canScrollLeft = container.scrollLeft > 0;
const canScrollRight = container.scrollWidth - container.clientWidth() > container.scrollLeft;
if (
(
containerPosition.left < mousePositionX || // if mouse isn't outside of container (left side)
!canScrollLeft // if we can't scroll to left (scrollLeft is not '0')
) &&
(
containerPosition.right > mousePositionX || // if mouse isn't outside of container (right side)
!canScrollRight // if we can't scroll to right
)
) {
return;
}
// detect scrolling direction
const direction =
containerPosition.right < mousePositionX &&
canScrollRight
? 1
: -1;
this.scrollToDirection(direction); // do one step to direction
this.startScrollingTimer(direction); // start scrolling timer
}
handleSelection (selectedKeys, e) {
this.updateScrollLeft(e);
// selectedKeys handling
}
render () {
return (
<div
ref='container'
style={{
width: '600px',
overflowX: 'auto',
whiteSpace: 'nowrap' // or `float: left` for every <Item>
}}
>
<SelectableGroup
ref='selectable'
onBeginSelection={this.onBeginSelection}
onEndSelection={this.onEndSelection}
onSelection={this.handleSelection}
>
{
this.props.items.map((item, index) => (
<Item
key={index}
data={item}
/>
))
}
</SelectableGroup>
</div>
);
}
}
I'll reopen the issue again. Maybe someone has the best solution.
is this auto scrolling feature added to master?