Skip to content
Original file line number Diff line number Diff line change
@@ -1,106 +1,29 @@
import { Box } from '@codecademy/gamut';
import { useTheme } from '@emotion/react';
import cx from 'classnames';
import React, { useCallback, useEffect, useState } from 'react';
import React from 'react';
import { useWindowScroll } from 'react-use';

import { GlobalHeaderProps } from '..';
import { BasicGlobalHeader } from '../BasicGlobalHeader';
import styles from './styles.module.scss';

const defaultScrollingState = {
isInHeaderRegion: true,
isScrollingDown: true,
isScrollingDownFromHeaderRegion: true,
prevScrollPosition: typeof window === 'undefined' ? 0 : window?.pageYOffset,
};

export const AnimatedGlobalHeader: React.FC<GlobalHeaderProps> = (props) => {
const [scrollingState, setScrollingState] = useState(defaultScrollingState);

const {
isScrollingDownFromHeaderRegion,
isInHeaderRegion,
isScrollingDown,
prevScrollPosition,
} = scrollingState;

const handleScrolling = useCallback(() => {
const currentScrollPosition =
typeof window === 'undefined' ? 0 : window?.pageYOffset;
const { y } = useWindowScroll();

// handle down/up scrolling
if (currentScrollPosition > prevScrollPosition) {
setScrollingState((prevState) => {
return {
...prevState,
isScrollingDown: true,
prevScrollPosition: currentScrollPosition,
};
});
} else {
setScrollingState((prevState) => {
return {
...prevState,
isScrollingDown: false,
prevScrollPosition: currentScrollPosition,
};
});
}
const isInHeaderRegion = y === 0;

// handle static header region
if (currentScrollPosition === 0) {
setScrollingState((prevState) => ({
...prevState,
isInHeaderRegion: true,
isScrollingDownFromHeaderRegion: true,
}));
} else {
setScrollingState((prevState) => ({
...prevState,
isInHeaderRegion: false,
isScrollingDownFromHeaderRegion:
prevState.isScrollingDown &&
prevState.isScrollingDownFromHeaderRegion,
}));
}
}, [prevScrollPosition]);

useEffect(() => {
window?.addEventListener('scroll', handleScrolling, {
passive: true,
});

// returned function will be called on component unmount
return () => {
window?.removeEventListener('scroll', handleScrolling);
};
}, [handleScrolling]);
const theme = useTheme();

return (
<>
<Box height={theme.elements.headerHeight}>
<BasicGlobalHeader
{...props}
className={cx(
styles.staticHeader,
!isScrollingDownFromHeaderRegion && styles.visuallyHide
)}
/>
<BasicGlobalHeader
{...props}
className={cx(
styles.stickyHeader,
// scrolling down from top
isScrollingDownFromHeaderRegion && [styles.visuallyHide],
// scrolling down
isScrollingDown && [styles.translateUp, styles.transitionSlide],
// scrolling up
!isScrollingDown &&
!isInHeaderRegion && [
styles.translateDown,
styles.transitionSlide,
styles.visuallyShow,
],
isInHeaderRegion && [styles.transitionOpacity, styles.visuallyHide]
isInHeaderRegion && styles.transitionFadeOut
)}
{...props}
/>
</>
</Box>
);
};
Original file line number Diff line number Diff line change
@@ -1,44 +1,16 @@
@import "~@codecademy/gamut-styles/utils";

.staticHeader {
background: transparent;
border-bottom: 1px solid transparent;
position: static;
}

.stickyHeader {
border-bottom: 1px solid $color-navy;
background-color: white;
background-color: $color-white;
position: fixed;
top: 0;
z-index: 2;
width: 100%;
}

.visuallyShow {
opacity: 1;
}

.visuallyHide {
opacity: 0;
}

.translateDown {
transform: translateY(0);
}

.translateUp {
transform: translateY(-100%);
}

.transitionOpacity {
transition: opacity 0.5s ease-in-out;
}

.transitionSlide {
transition: transform 0.15s ease-in-out;
}

.transitionFade {
.transitionFadeOut {
background: transparent;
border-bottom: 1px solid transparent;
transition: background 0.5s ease-in-out, border-bottom 0.5s ease-in-out;
}