mdc-react
Version:
Material Components for the web implemented in React
139 lines (120 loc) • 4.46 kB
JSX
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;