78 lines
1.7 KiB
JavaScript
78 lines
1.7 KiB
JavaScript
/**
|
|
* External dependencies
|
|
*/
|
|
import { Component, createRef, Fragment } from 'react';
|
|
|
|
/**
|
|
* Internal dependencies
|
|
*/
|
|
import './style.scss';
|
|
|
|
/**
|
|
* HOC that provides a function to scroll to the top of the component.
|
|
*
|
|
* @param {Function} OriginalComponent Component being wrapped.
|
|
*/
|
|
const withScrollToTop = ( OriginalComponent ) => {
|
|
class WrappedComponent extends Component {
|
|
constructor() {
|
|
super();
|
|
|
|
this.scrollPointRef = createRef();
|
|
}
|
|
|
|
scrollToTopIfNeeded = () => {
|
|
const scrollPointRefYPosition = this.scrollPointRef.current.getBoundingClientRect()
|
|
.bottom;
|
|
const isScrollPointRefVisible =
|
|
scrollPointRefYPosition >= 0 &&
|
|
scrollPointRefYPosition <= window.innerHeight;
|
|
if ( ! isScrollPointRefVisible ) {
|
|
this.scrollPointRef.current.scrollIntoView();
|
|
}
|
|
};
|
|
|
|
moveFocusToTop = ( focusableSelector ) => {
|
|
const focusableElements = this.scrollPointRef.current.parentElement.querySelectorAll(
|
|
focusableSelector
|
|
);
|
|
if ( focusableElements.length ) {
|
|
focusableElements[ 0 ].focus();
|
|
}
|
|
};
|
|
|
|
scrollToTop = ( args ) => {
|
|
if ( ! window || ! Number.isFinite( window.innerHeight ) ) {
|
|
return;
|
|
}
|
|
|
|
this.scrollToTopIfNeeded();
|
|
if ( args && args.focusableSelector ) {
|
|
this.moveFocusToTop( args.focusableSelector );
|
|
}
|
|
};
|
|
|
|
render() {
|
|
return (
|
|
<Fragment>
|
|
<div
|
|
className="with-scroll-to-top__scroll-point"
|
|
ref={ this.scrollPointRef }
|
|
aria-hidden
|
|
/>
|
|
<OriginalComponent
|
|
{ ...this.props }
|
|
scrollToTop={ this.scrollToTop }
|
|
/>
|
|
</Fragment>
|
|
);
|
|
}
|
|
}
|
|
|
|
WrappedComponent.displayName = 'withScrollToTop';
|
|
|
|
return WrappedComponent;
|
|
};
|
|
|
|
export default withScrollToTop;
|