Skip to content

Commit bb932b9

Browse files
committed
content size tracker;
API actualization;
1 parent 526844f commit bb932b9

File tree

4 files changed

+100
-6
lines changed

4 files changed

+100
-6
lines changed

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,8 @@ All properties and methods are in the [API Documentation](https://github.com/xob
4646
Run the example, it will install dependencies, build current component version and run local web-server listening `localhost:3000` (browser window will open automatically)
4747
```bash
4848
npm run examples
49-
```
49+
```
50+
51+
## Credits
52+
Big thanks to @malte-wessel with his [react-custom-scrollbars](https://github.com/malte-wessel/react-custom-scrollbars) which I used before writing this component.
53+
So don't be wondered that repos and code are look the same in some places, his package used as ethalon. And for the users convenience i've tried to make API's seamless as much as it possible.

docs/API.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@
66
* `defaultStyles`: _(boolean)_ Apply default inline styles _(default: false)_
77
* `thumbSizeMin`: _(number)_ Minimal size of thumb in pixels _(default: 30)_
88
* `scrollDetectionThreshold`: _(number)_ Scroll process check interval in milliseconds _(default: 100)_
9+
* `permanentScrollbars`: _(boolean)_ Display both, vertical and horizontal scrollbars permanently, in spite of scrolling possibility _(default: false)_
10+
* `permanentScrollbarVertical`: _(boolean)_ Display vertical scrollbar permanently, in spite of scrolling possibility _(default: false)_
11+
* `permanentScrollbarHorizontal`: _(boolean)_ Display horizontal scrollbar permanently, in spite of scrolling possibility _(default: false)_
12+
* `contentSizeTrack`: _(boolean)_ Automatically check content's sizes to actualize the scrollbars. Useful when dom is changed not only by react. _(default: false)_
13+
* `contentSizeTrackInterval`: _(number)_ Interval between content's size check, in milliseconds _(default: 200)_
914
* **Rendering**
1015
* `renderWrapper`: _(function)_ The element that wraps the content in order to hide browser's scrollbars
1116
* `renderContent`: _(function)_ The element where content will be rendered

docs/USAGE.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# USAGE
22

33
### Default
4-
The <Scrollbar> component works out of the box, with only need of `width` and `height` to be set, inline or via CSS;
4+
The `<Scrollbar />` component works out of the box, with only need of `width` and `height` to be set, inline or via CSS;
55
```javascript
66
import React, { Component } from 'react';
77
import Scrollbar from 'react-scrollbar-custom';
@@ -84,3 +84,20 @@ class App extends Component
8484
}
8585
}
8686
```
87+
88+
### Automatic size checks
89+
It is possible that DOM will be changed not only with React: for these cases `<Scrollbar />` has automatic content checker.
90+
```javascript
91+
import React, { Component } from 'react';
92+
import Scrollbar from 'react-scrollbar-custom';
93+
94+
class App extends Component
95+
{
96+
render() {
97+
return (
98+
<Scrollbar contentSizeTrack={true} contentSizeTrack={500}/>
99+
);
100+
}
101+
}
102+
```
103+
Now `<Scrollbar/>` will check content's sizes every 500ms and trigger `Scrollbar.update()` if they're changed.

src/Scrollbar/index.js

Lines changed: 72 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ export default class Scrollbar extends Component
1515
permanentScrollbars: PropTypes.bool,
1616
permanentScrollbarVertical: PropTypes.bool,
1717
permanentScrollbarHorizontal: PropTypes.bool,
18+
contentSizeTrack: PropTypes.bool,
19+
contentSizeTrackInterval: PropTypes.number,
1820

1921
onUpdate: PropTypes.func,
2022
onScroll: PropTypes.func,
@@ -24,8 +26,6 @@ export default class Scrollbar extends Component
2426
tagName: PropTypes.string,
2527
className: PropTypes.string,
2628

27-
renderScroller: PropTypes.func,
28-
2929
renderWrapper: PropTypes.func,
3030
renderContent: PropTypes.func,
3131
renderTrackVertical: PropTypes.func,
@@ -42,6 +42,8 @@ export default class Scrollbar extends Component
4242
permanentScrollbars: false,
4343
permanentScrollbarVertical: false,
4444
permanentScrollbarHorizontal: false,
45+
contentSizeTrack: false,
46+
contentSizeTrackInterval: 200,
4547

4648
tagName: 'div',
4749
className: 'CustomScrollbar',
@@ -76,16 +78,43 @@ export default class Scrollbar extends Component
7678
this.removeListeners();
7779

7880
raf.cancel(this.requestFrame);
79-
clearInterval(this.scrollDetect.interval);
81+
82+
if (this.scrollDetect.interval) {
83+
clearInterval(this.scrollDetect.interval);
84+
this.scrollDetect.interval = undefined;
85+
}
86+
this.contentSizeTrackStop();
8087
}
8188

8289
componentDidMount() {
8390
this.addListeners();
8491
this.update();
92+
93+
if (this.props.contentSizeTrack) {
94+
this.contentSizeTrackStart();
95+
}
8596
}
8697

8798
componentDidUpdate() {
8899
this.update();
100+
101+
const {scrollHeight = 0, scrollWidth = 0, clientHeight = 0, clientWidth = 0} = this.content;
102+
this.contentSizeTrackPreviousSize = {scrollHeight, scrollWidth, clientHeight, clientWidth};
103+
}
104+
105+
componentWillUpdate(nextProps, nextState) {
106+
if (nextProps.contentSizeTrack !== this.props.contentSizeTrack) {
107+
if (nextProps.contentSizeTrack) {
108+
this.contentSizeTrackStart();
109+
}
110+
else {
111+
this.contentSizeTrackStop();
112+
}
113+
}
114+
else if (nextProps.contentSizeTrack && nextProps.contentSizeTrackInterval !== this.props.contentSizeTrackInterval) {
115+
this.contentSizeTrackStop();
116+
this.contentSizeTrackStart();
117+
}
89118
}
90119

91120
/**
@@ -514,10 +543,49 @@ export default class Scrollbar extends Component
514543
return this;
515544
}
516545

546+
/**
547+
* @return {Scrollbar}
548+
*/
549+
contentSizeTrackStart() {
550+
if (!this.content || this.contentSizeTrackInterval) { return this; }
551+
552+
const {scrollHeight = 0, scrollWidth = 0, clientHeight = 0, clientWidth = 0} = this.content;
553+
this.contentSizeTrackPreviousSize = {scrollHeight, scrollWidth, clientHeight, clientWidth};
554+
const {contentSizeTrackInterval} = this.props;
555+
556+
this.contentSizeTrackInterval = setInterval(() => {
557+
const {scrollHeight = 0, scrollWidth = 0, clientHeight = 0, clientWidth = 0} = this.content;
558+
559+
if (this.contentSizeTrackPreviousSize.scrollHeight !== scrollHeight || this.contentSizeTrackPreviousSize.scrollWidth !== scrollWidth ||
560+
this.contentSizeTrackPreviousSize.clientHeight !== clientHeight || this.contentSizeTrackPreviousSize.clientWidth !== clientWidth) {
561+
this.update();
562+
}
563+
564+
this.contentSizeTrackPreviousSize = {scrollHeight, scrollWidth, clientHeight, clientWidth};
565+
}, contentSizeTrackInterval);
566+
567+
return this;
568+
}
569+
570+
/**
571+
* @return {Scrollbar}
572+
*/
573+
contentSizeTrackStop() {
574+
if (this.contentSizeTrackInterval) {
575+
clearInterval(this.contentSizeTrackInterval);
576+
}
577+
578+
this.contentSizeTrackInterval = undefined;
579+
this.contentSizeTrackPreviousSize = undefined;
580+
581+
return this;
582+
}
583+
517584
render() {
518585
const {
519586
style, thumbSizeMin, defaultStyles, scrollDetectionThreshold, permanentScrollbars, permanentScrollbarVertical, permanentScrollbarHorizontal,
520-
tagName, children, renderScroller, renderWrapper, renderContent, renderTrackVertical, renderTrackHorizontal, renderThumbVertical, renderThumbHorizontal,
587+
contentSizeTrack, contentSizeTrackInterval,
588+
tagName, children, renderWrapper, renderContent, renderTrackVertical, renderTrackHorizontal, renderThumbVertical, renderThumbHorizontal,
521589
onUpdate, onScroll, onScrollStart, onScrollStop,
522590
...props
523591
} = this.props;

0 commit comments

Comments
 (0)