You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

104 lines
2.5 KiB

// @flow
import React, { PureComponent } from 'react'
import ReactDOM from 'react-dom'
import styled from 'styled-components'
import smoothscroll from 'smoothscroll-polyfill'
import Box from 'components/base/Box'
import AngleUp from 'icons/AngleUp'
import { GrowScrollContext } from './base/GrowScroll'
smoothscroll.polyfill()
const Container = styled(Box)`
position: fixed;
z-index: 10;
bottom: 100px;
right: 20px;
border-radius: 50%;
box-shadow: 0 2px 4px 0 rgba(102, 102, 102, 0.25);
cursor: pointer;
height: 36px;
width: 36px;
color: white;
background-color: #6490f1;
transition: all 0.5s;
opacity: ${p => (p.visible ? 1 : 0)};
pointer-events: ${p => (!p.visible ? 'none' : 'initial')};
&:focus {
width: 200px;
}
`
type Props = {
scrollThreshold: number,
getGrowScroll: () => { scrollContainer: ?HTMLDivElement },
}
type State = {
visible: boolean,
}
class StickyBackToTop extends PureComponent<Props, State> {
static defaultProps = {
scrollThreshold: 800,
}
state = {
visible: false,
}
componentDidMount() {
const { scrollContainer } = this.props.getGrowScroll()
if (scrollContainer) {
const listener = () => {
if (this._unmounted) return
const { scrollTop } = scrollContainer
const visible = scrollTop > this.props.scrollThreshold
this.setState(previous => {
if (previous.visible !== visible) {
return { visible }
}
return null
})
}
scrollContainer.addEventListener('scroll', listener)
this.releaseListener = () => scrollContainer.removeEventListener('scroll', listener)
}
}
componentWillUnmount() {
this._unmounted = true
this.releaseListener()
}
_unmounted = false
onClick = () => {
const { scrollContainer } = this.props.getGrowScroll()
if (scrollContainer) {
// $FlowFixMe seems to be missing in flow
scrollContainer.scrollTo({ top: 0, behavior: 'smooth' })
}
}
releaseListener = () => {}
render() {
const { visible } = this.state
const el = document.getElementById('sticky-back-to-top-root')
if (!el) return null
return ReactDOM.createPortal(
<Container align="center" justify="center" visible={visible} onClick={this.onClick}>
<AngleUp size={20} />
</Container>,
el,
)
}
}
export default (props: { scrollThreshold?: number }) => (
<GrowScrollContext.Consumer>
{getGrowScroll => <StickyBackToTop {...props} getGrowScroll={getGrowScroll} />}
</GrowScrollContext.Consumer>
)