Add progress indicator on header

This commit is contained in:
Joachim Robert 2019-02-03 23:34:32 +01:00
parent 5a35f675a6
commit 1d99bc4739
5 changed files with 101 additions and 11 deletions

View file

@ -1,16 +1,24 @@
// @flow
import * as React from 'react';
import Link from 'next/link';
import { Container, ButtonRowContainer, Label } from './style';
import {
Container,
ButtonRowContainer,
Label,
Progression,
ProgressBar } from './style';
import { PrimaryButton, GhostButton } from '../Button';
import Logo from './Logo';
type Props = {
showHeaderShadow: boolean,
displayProgress: boolean,
totalItemsCount: number,
currentCount: number,
};
export default function Header(props: Props) {
const { showHeaderShadow } = props;
const { showHeaderShadow, totalItemsCount, currentCount, displayProgress } = props;
return (
<Container showHeaderShadow={showHeaderShadow} data-cy="header">
@ -21,6 +29,11 @@ export default function Header(props: Props) {
</a>
</Link>
<Progression isHidden={!displayProgress}>
{currentCount} of {totalItemsCount} completed
<ProgressBar />
</Progression>
<ButtonRowContainer>
<Link href="/about">
<a>

View file

@ -4,8 +4,8 @@ import { theme } from '../theme';
export const Container = styled.div`
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-areas: 'logo actions';
grid-template-columns: 1fr 1fr 1fr;
grid-template-areas: 'logo progression actions';
padding: 16px;
position: fixed;
top: 0;
@ -25,7 +25,7 @@ export const Container = styled.div`
@media (max-width: 968px) {
padding: 8px 16px;
grid-template-columns: 1fr 1fr;
grid-template-areas: 'logo actions';
grid-template-areas: "logo actions" "progression progression";
}
`;
@ -36,6 +36,34 @@ export const Logo = styled.h1`
color: ${theme.text.default};
`;
export const Progression = styled.div`
grid-area: progression;
text-align: center;
display: ${props =>
props.isHidden ? 'none' : 'block'};
`;
export const ProgressBar = styled.div`
height: 8px;
margin: 6px 0;
position: relative;
overflow: hidden;
border-radius: 4px;
background-image: linear-gradient(to left, #a913de, #6ac9ff);
&:after {
content: '';
position: absolute;
height: 100%;
right: 0;
width: 100%;
background: ${theme.border.default};
max-width: var(--progress);
transition: max-width ${theme.animations.default};
}
`;
export const ButtonRowContainer = styled.div`
display: flex;
justify-content: flex-end;

View file

@ -17,18 +17,25 @@ import {
ScrollToTop,
} from './style';
import * as gtag from '../../lib/gtag';
import { getLocalStorageLength } from '../../lib/localStorage';
import data from '../../config/data';
export { SectionHeading, Heading, Subheading };
type Props = {
children: Node,
displayProgress: boolean,
};
const totalItemsCount = Object.keys(data).length;
export default function Page(props: Props) {
const { children } = props;
const { children, displayProgress } = props;
const [lastTrackedPageview, setLastTrackedPageview] = useState(null);
const [showHeaderShadow, setHeaderShadow] = useState(false);
const [scrollToTopVisible, setScrollToTopVisible] = useState(false);
const [progress, setProgress] = useState(0);
const [currentCount, setCurrentCount] = useState(0);
function handleScroll() {
const headerShadowState = window && window.pageYOffset > 0;
@ -39,6 +46,14 @@ export default function Page(props: Props) {
const throttledScroll = throttle(300, handleScroll);
function updateProgress() {
console.log("called")
const checkedItemsCount = getLocalStorageLength();
const progressPercentage = checkedItemsCount * 100 / totalItemsCount;
setProgress(progressPercentage);
setCurrentCount(checkedItemsCount);
};
const scrollToTop = () => {
if (window) {
window.scrollTo(0, 0);
@ -46,6 +61,8 @@ export default function Page(props: Props) {
};
useEffect(() => {
updateProgress();
if (window) {
window.addEventListener('scroll', throttledScroll);
}
@ -56,7 +73,18 @@ export default function Page(props: Props) {
setLastTrackedPageview(null);
}
};
}, []);
}, [progress]);
useEffect(() => {
if (window && displayProgress) {
window.addEventListener('storage:updated', updateProgress );
}
return () => {
if (window && displayProgress) {
window.removeEventListener('storage:updated', updateProgress );
}
};
});
useEffect(() => {
if (document) {
@ -71,7 +99,18 @@ export default function Page(props: Props) {
return (
<ThemeProvider theme={theme}>
<Container>
<Header showHeaderShadow={showHeaderShadow} />
<style>{`
:root {
--progress: ${100 - progress}%;
}
`}</style>
<Header
showHeaderShadow={showHeaderShadow}
displayProgress={displayProgress}
totalItemsCount={totalItemsCount}
currentCount={currentCount}
/>
<InnerContainer>{children}</InnerContainer>
<Footer />
<ScrollToTop isVisible={scrollToTopVisible} onClick={scrollToTop}>

View file

@ -10,7 +10,8 @@ export const storeItem = (key, value) => {
if (!localStorage) return;
try {
return localStorage.setItem(key, JSON.stringify(value));
localStorage.setItem(key, JSON.stringify(value));
window.dispatchEvent(new Event('storage:updated'));
} catch (err) {}
};
@ -18,6 +19,15 @@ export const removeItemFromStorage = key => {
if (!localStorage) return;
try {
return localStorage.removeItem(key);
localStorage.removeItem(key);
window.dispatchEvent(new Event('storage:updated'));
} catch (err) {}
};
export const getLocalStorageLength = () => {
if (!localStorage) return;
try {
return localStorage.length;
} catch (err) {}
}

View file

@ -19,7 +19,7 @@ class Index extends React.Component<{}> {
render() {
return (
<Page>
<Page displayProgress={true}>
<SectionHeading>
<Heading>Be safe on the internet.</Heading>
<Subheading>