diff options
| -rw-r--r-- | app/components/CustomScrollbars.css | 24 | ||||
| -rw-r--r-- | app/components/CustomScrollbars.js | 130 | ||||
| -rw-r--r-- | package.json | 1 | ||||
| -rw-r--r-- | yarn.lock | 42 |
4 files changed, 144 insertions, 53 deletions
diff --git a/app/components/CustomScrollbars.css b/app/components/CustomScrollbars.css index 24788230b8..074c081209 100644 --- a/app/components/CustomScrollbars.css +++ b/app/components/CustomScrollbars.css @@ -1,4 +1,22 @@ -.custom-scrollbars__thumb-vertical { - background-color: rgba(255, 255, 255, 0.2); - border-radius: 3px; +.custom-scrollbars { + display: flex; + flex-direction: column; + position: relative; +} + +.custom-scrollbars__scrollable { + width: 100%; + height: 100%; } + +.custom-scrollbars__scrollable::-webkit-scrollbar { + display: none; +} + +.custom-scrollbars__thumb { + background-color: rgba(255, 255, 255, 0.2); + border-radius: 4px; + width: 8px; + transition: height 0.25s ease-in-out, opacity 0.25s ease-in-out; + pointer-events: none; +}
\ No newline at end of file diff --git a/app/components/CustomScrollbars.js b/app/components/CustomScrollbars.js index 4a0899c1be..6d0ce96daa 100644 --- a/app/components/CustomScrollbars.js +++ b/app/components/CustomScrollbars.js @@ -1,19 +1,133 @@ // @flow import React, { Component } from 'react'; -import { Scrollbars } from 'react-custom-scrollbars'; + +type ScrollbarUpdateContext = { + size: boolean, + position: boolean, +}; export default class CustomScrollbars extends Component { props: { - children: ?React.Element<*> + thumbInset: { x: number, y: number }, + children: ?React.Element<*>, + }; + + static defaultProps = { + thumbInset: { x: 2, y: 2 }, + }; + + _scrollableElement: ?HTMLElement; + _thumbElement: ?HTMLElement; + + componentDidMount() { + this._updateScrollbarsHelper({ + position: true, + size: true + }); } - render(): React.Element<*> { + componentDidUpdate() { + this._updateScrollbarsHelper({ + position: true, + size: true + }); + } + + render() { return ( - <Scrollbars - { ...this.props } - renderThumbVertical={ () => <div className="custom-scrollbars__thumb-vertical"/> }> - { this.props.children } - </Scrollbars> + <div className="custom-scrollbars"> + <div className="custom-scrollbars__thumb" + style={{ position: 'absolute', top: 0, right: 0 }} + ref={ this._onThumbRef }></div> + <div className="custom-scrollbars__scrollable" + style={{ overflow: 'auto' }} + onScroll={ this._onScroll } + ref={ this._onScrollableRef }> + { this.props.children } + </div> + </div> ); } + + + _onScrollableRef = (ref) => { + this._scrollableElement = ref; + } + + _onThumbRef = (ref) => { + this._thumbElement = ref; + } + + _onScroll = () => { + this._updateScrollbarsHelper({ position: true }); + } + + _computeThumbPosition(scrollable: HTMLElement, thumb: HTMLElement) { + // the content height of the scroll view + const scrollHeight = scrollable.scrollHeight; + + // the visible height of the scroll view + const visibleHeight = scrollable.offsetHeight; + + // scroll offset + const scrollTop = scrollable.scrollTop; + + // lowest point of scrollTop + const maxScrollTop = scrollHeight - visibleHeight; + + // calculate scroll position within 0..1 range + const scrollPosition = scrollHeight > 0 ? scrollTop / maxScrollTop : 0; + + const thumbHeight = thumb.clientHeight; + + // calculate the thumb boundary to make sure that the visual appearance of + // a thumb at lowest point matches the bottom of scrollable view + const thumbBoundary = visibleHeight - thumbHeight - (this.props.thumbInset.y * 2); + + // calculate thumb position based on scroll progress and thumb boundary + // adding vertical inset to adjust the thumb's appearance + const thumbPosition = (thumbBoundary * scrollPosition) + this.props.thumbInset.y; + + return { + x: -this.props.thumbInset.x, + y: thumbPosition, + }; + } + + _computeThumbHeight(scrollable: HTMLElement) { + const scrollHeight = scrollable.scrollHeight; + const visibleHeight = scrollable.offsetHeight; + + const thumbHeight = (visibleHeight / scrollHeight) * visibleHeight; + + // ensure that the scroll thumb doesn't shrink to nano size + return Math.max(thumbHeight, 8); + } + + _updateScrollbarsHelper(updateFlags: $Shape<ScrollbarUpdateContext>) { + const scrollable = this._scrollableElement; + const thumb = this._thumbElement; + if(scrollable && thumb) { + this._updateScrollbars(scrollable, thumb, updateFlags); + } + } + + _updateScrollbars(scrollable: HTMLElement, thumb: HTMLElement, context: $Shape<ScrollbarUpdateContext>) { + if(context.size) { + const thumbHeight = this._computeThumbHeight(scrollable); + thumb.style.setProperty('height', thumbHeight + 'px'); + + // hide thumb when there is nothing to scroll + if(thumbHeight < scrollable.offsetHeight) { + thumb.style.setProperty('opacity', '1'); + } else { + thumb.style.setProperty('opacity', '0'); + } + } + + if(context.position) { + const { x, y } = this._computeThumbPosition(scrollable, thumb); + thumb.style.setProperty('transform', `translate(${x}px, ${y}px)`); + } + } } diff --git a/package.json b/package.json index 421d637605..9c08ea1d29 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,6 @@ "moment": "^2.17.1", "prop-types": "^15.5.10", "react": "^16.0.0", - "react-custom-scrollbars": "^4.2.1", "react-dom": "^16.0.0", "react-if": "^2.2.1", "react-mapbox-gl": "^2.1.0", @@ -128,10 +128,6 @@ acorn@^5.0.0, acorn@^5.1.0, acorn@^5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.2.1.tgz#317ac7821826c22c702d66189ab8359675f135d7" -add-px-to-style@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/add-px-to-style/-/add-px-to-style-1.0.0.tgz#d0c135441fa8014a8137904531096f67f28f263a" - after@0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/after/-/after-0.8.1.tgz#ab5d4fb883f596816d3515f8f791c0af486dd627" @@ -2469,14 +2465,6 @@ doctrine@^2.0.0, doctrine@^2.0.2: dependencies: esutils "^2.0.2" -dom-css@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/dom-css/-/dom-css-2.1.0.tgz#fdbc2d5a015d0a3e1872e11472bbd0e7b9e6a202" - dependencies: - add-px-to-style "1.0.0" - prefix-style "2.0.1" - to-camel-case "1.0.0" - dom-helpers@^3.2.0: version "3.2.1" resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.2.1.tgz#3203e07fed217bd1f424b019735582fc37b2825a" @@ -6098,10 +6086,6 @@ prebuild-install@^2.3.0: tunnel-agent "^0.6.0" xtend "4.0.1" -prefix-style@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/prefix-style/-/prefix-style-2.0.1.tgz#66bba9a870cfda308a5dc20e85e9120932c95a06" - prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" @@ -6263,7 +6247,7 @@ rabin-bindings@~1.7.4: nan "^2.8.0" prebuild-install "^2.3.0" -raf@^3.1.0, raf@^3.4.0: +raf@^3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.0.tgz#a28876881b4bc2ca9117d4138163ddb80f781575" dependencies: @@ -6320,14 +6304,6 @@ react-clone-referenced-element@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/react-clone-referenced-element/-/react-clone-referenced-element-1.0.1.tgz#2bba8c69404c5e4a944398600bcc4c941f860682" -react-custom-scrollbars@^4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/react-custom-scrollbars/-/react-custom-scrollbars-4.2.1.tgz#830fd9502927e97e8a78c2086813899b2a8b66db" - dependencies: - dom-css "^2.0.0" - prop-types "^15.5.10" - raf "^3.1.0" - react-deep-force-update@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/react-deep-force-update/-/react-deep-force-update-1.1.1.tgz#bcd31478027b64b3339f108921ab520b4313dc2c" @@ -7823,26 +7799,10 @@ to-array@0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890" -to-camel-case@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/to-camel-case/-/to-camel-case-1.0.0.tgz#1a56054b2f9d696298ce66a60897322b6f423e46" - dependencies: - to-space-case "^1.0.0" - to-fast-properties@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" -to-no-case@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/to-no-case/-/to-no-case-1.0.2.tgz#c722907164ef6b178132c8e69930212d1b4aa16a" - -to-space-case@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/to-space-case/-/to-space-case-1.0.0.tgz#b052daafb1b2b29dc770cea0163e5ec0ebc9fc17" - dependencies: - to-no-case "^1.0.0" - to-utf8@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/to-utf8/-/to-utf8-0.0.1.tgz#d17aea72ff2fba39b9e43601be7b3ff72e089852" |
