UNPKG

mdc-react

Version:

Material Components for the web implemented in React

139 lines (120 loc) 4.46 kB
import { forwardRef, useRef, useState, useEffect, useImperativeHandle, Children } from 'react'; import PropTypes from 'prop-types'; import classnames from 'classnames'; import { Clone } from '../component'; import IconButton from '../icon-button'; import { cssClasses, Align } from './constants'; import TopAppBarRow from './TopAppBarRow'; import TopAppBarSection from './TopAppBarSection'; const TopAppBar = forwardRef(({ title, navigationIcon, actionItems, fixed = false, sticky = false, dense = false, prominent = false, short = false, collapsed = false, raised = false, onNavigationIconClick = Function.prototype, element = 'header', component: Element = element, className, children, ...props }, ref) => { const rootRef = useRef(); const [scrolled, setScrolled] = useState(false); const [hidden, setHidden] = useState(false); useImperativeHandle(ref, () => rootRef.current); useEffect(() => { let lastScrollPosition = 0; function handleScroll() { const scrollValue = window.pageYOffset; if (fixed) { setScrolled(scrollValue > 0); } else if (sticky) { const currentScrollPosition = Math.max(scrollValue, 0); const diff = currentScrollPosition - lastScrollPosition; const shouldHide = diff > 0; lastScrollPosition = currentScrollPosition; setHidden(shouldHide); } } window.addEventListener('scroll', handleScroll); return () => { window.removeEventListener('scroll', handleScroll); }; }, [fixed, sticky]); const classNames = classnames(cssClasses.ROOT, { [cssClasses.FIXED]: fixed, [cssClasses.STICKY]: sticky, [cssClasses.DENSE]: dense, [cssClasses.PROMINENT]: prominent, [cssClasses.SHORT]: short, [cssClasses.SHORT_COLLAPSED]: short && collapsed, [cssClasses.FIXED_SCROLLED]: fixed && scrolled, [cssClasses.STICKY_HIDDEN]: sticky && hidden, [cssClasses.STICKY_SHOWN]: sticky && !hidden, [cssClasses.RAISED]: raised }, className); return ( <Element ref={rootRef} className={classNames} {...props} > {children || <TopAppBarRow> {(title || navigationIcon) && <TopAppBarSection align={Align.START}> {navigationIcon && <Clone component={navigationIcon} fallback={IconButton} className={cssClasses.NAVIGATION_ICON} onClick={onNavigationIconClick} /> } {title && <Clone component={title} fallback="span" className={cssClasses.TITLE} /> } </TopAppBarSection> } {actionItems && <TopAppBarSection align={Align.END}> {Children.map(actionItems, item => <Clone component={item} className={cssClasses.ACTION_ITEM} /> )} </TopAppBarSection> } </TopAppBarRow> } </Element> ); }); TopAppBar.displayName = 'MDCTopAppBar'; TopAppBar.propTypes = { title: PropTypes.node, navigationIcon: PropTypes.node, actionItems: PropTypes.oneOfType([ PropTypes.element, PropTypes.arrayOf(PropTypes.element), ]), fixed: PropTypes.bool, sticky: PropTypes.bool, dense: PropTypes.bool, prominent: PropTypes.bool, short: PropTypes.bool, collapsed: PropTypes.bool, raised: PropTypes.bool }; export default TopAppBar;