mini challenges grid on desktop

This commit is contained in:
mattyl006 2022-07-14 09:34:56 +02:00
parent 73e7ada0c3
commit 76bdb473e9
7 changed files with 169 additions and 94 deletions

View File

@ -1,20 +1,20 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8"/>
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> <link rel="icon" href="%PUBLIC_URL%/favicon.ico"/>
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta name="theme-color" content="#000000" /> <meta name="theme-color" content="#000000"/>
<meta <meta
name="description" name="description"
content="Web site created using create-react-app" content="Web site created using create-react-app"
/> />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png"/>
<!-- <!--
manifest.json provides metadata used when your web app is installed on a manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/ user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
--> -->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> <link rel="manifest" href="%PUBLIC_URL%/manifest.json"/>
<!-- <!--
Notice the use of %PUBLIC_URL% in the tags above. Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build. It will be replaced with the URL of the `public` folder during the build.
@ -24,12 +24,12 @@
work correctly both with client-side routing and a non-root public URL. work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`. Learn how to configure a non-root public URL by running `npm run build`.
--> -->
<title>React App</title> <title>Gonito</title>
</head> </head>
<body> <body>
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div> <div id="root"></div>
<!-- <!--
This HTML file is a template. This HTML file is a template.
If you open it directly in the browser, you will see an empty page. If you open it directly in the browser, you will see an empty page.
@ -38,6 +38,6 @@
To begin the development, run `npm start` or `yarn start`. To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`. To create a production bundle, use `npm run build` or `yarn build`.
--> -->
</body> </body>
</html> </html>

View File

@ -4,7 +4,7 @@ import {Body, H3} from "../../utils/fonts";
import styled from "styled-components"; import styled from "styled-components";
import IconLabel from "./IconLabel"; import IconLabel from "./IconLabel";
import {Link} from "react-router-dom"; import {Link} from "react-router-dom";
import {CHALLENGE_PAGE} from "../../utils/globals"; import {CHALLENGE_PAGE, MINI_DESCRIPTION_LENGTH} from "../../utils/globals";
const ChallengeStyle = styled(FlexColumn)` const ChallengeStyle = styled(FlexColumn)`
padding: 12px; padding: 12px;
@ -14,10 +14,6 @@ const ChallengeStyle = styled(FlexColumn)`
position: relative; position: relative;
max-width: 420px; max-width: 420px;
p {
width: 80%;
}
* { * {
cursor: pointer; cursor: pointer;
} }
@ -26,12 +22,19 @@ const ChallengeStyle = styled(FlexColumn)`
transform: scale(1.05); transform: scale(1.05);
} }
a { article {
position: absolute;
top: 0;
left: 0;
width: 100%; width: 100%;
height: 100%; align-items: flex-start;
p {
width: 80%;
}
}
@media (min-width: ${({theme}) => theme.overMobile}) {
width: 360px;
padding: 20px;
justify-content: flex-start;
} }
`; `;
@ -54,20 +57,20 @@ const IconsGrid = styled(Grid)`
`; `;
const MiniChallenge = (props) => { const MiniChallenge = (props) => {
const renderDescription = (description) => { const renderDescription = (description) => {
if (description.length <= 200) if (description.length <= MINI_DESCRIPTION_LENGTH)
return description; return description;
return `${description.slice(0, 100)}...` return `${description.slice(0, MINI_DESCRIPTION_LENGTH)}...`
} }
return ( return (
<ChallengeStyle as='article' alignmentX='flex-start'> <ChallengeStyle as={Link} to={`${CHALLENGE_PAGE}/${props.name}`}>
<FlexColumn as='article'>
<FlexRow margin='0 0 14px 0' gap='12px' width='100%' alignmentX='space-between'> <FlexRow margin='0 0 14px 0' gap='12px' width='100%' alignmentX='space-between'>
<H3 as='h3' width='85%'> <H3 as='h3' width='85%'>
{props.title} {props.title}
</H3> </H3>
<IconLabel type={props.type} size='30px'/> {props.type ? <IconLabel type={props.type} size='30px'/> : 'xxx'}
</FlexRow> </FlexRow>
<Line/> <Line/>
<Body as='p' margin='0 0 14px 0'> <Body as='p' margin='0 0 14px 0'>
@ -90,7 +93,7 @@ const MiniChallenge = (props) => {
{props.prize} {props.prize}
</IconLabel> : ''} </IconLabel> : ''}
</IconsGrid> </IconsGrid>
<Container as={Link} to={`${CHALLENGE_PAGE}/${props.name}`}/> </FlexColumn>
</ChallengeStyle> </ChallengeStyle>
); );
} }

View File

@ -5,26 +5,46 @@ import polygon from '../../assets/polygon.svg';
import styled from "styled-components"; import styled from "styled-components";
import theme from "../../utils/theme"; import theme from "../../utils/theme";
const PagerStyle = styled(FlexRow)`
gap: 14px;
@media (min-width: ${({theme}) => theme.overMobile}) {
gap: 20px;
}
`;
const LeftArrow = styled(Svg)` const LeftArrow = styled(Svg)`
background-color: ${({backgroundColor}) => backgroundColor}; background-color: ${({backgroundColor}) => backgroundColor};
cursor: ${({backgroundColor}) => (backgroundColor === 'transparent') ? 'auto' : 'pointer'}; cursor: ${({backgroundColor}) => (backgroundColor === 'transparent') ? 'auto' : 'pointer'};
width: 10px;
height: 10px;
@media (min-width: ${({theme}) => theme.overMobile}) {
width: 12px;
height: 12px;
}
`; `;
const RightArrow = styled(Svg)` const RightArrow = styled(Svg)`
display: ${({display}) => display}; background-color: ${({backgroundColor}) => backgroundColor};
transform: rotate(180deg); transform: rotate(180deg);
cursor: ${({backgroundColor}) => (backgroundColor === 'transparent') ? 'auto' : 'pointer'}; cursor: ${({backgroundColor}) => (backgroundColor === 'transparent') ? 'auto' : 'pointer'};
width: 10px;
height: 10px;
@media (min-width: ${({theme}) => theme.overMobile}) {
width: 12px;
height: 12px;
}
`; `;
const Pager = (props) => { const Pager = (props) => {
return ( return (
<FlexRow gap='14px'> <PagerStyle>
<LeftArrow as='button' src={polygon} onClick={props.previousPage} <LeftArrow as='button' src={polygon} onClick={props.previousPage} size='cover'
backgroundColor={(props.pageNr === 1) ? 'transparent' : theme.colors.dark}/> backgroundColor={(props.pageNr === 1) ? 'transparent' : theme.colors.dark}/>
<CircleNumber number={props.pageNr}/> <CircleNumber number={props.pageNr}/>
<RightArrow as='button' src={polygon} onClick={props.nextPage} <RightArrow as='button' src={polygon} onClick={props.nextPage} size='cover'
backgroundColor={(props.pageNr === props.pages) ? 'transparent' : theme.colors.dark}/> backgroundColor={(props.pageNr === props.pages) ? 'transparent' : theme.colors.dark}/>
</FlexRow> </PagerStyle>
); );
} }

View File

@ -19,6 +19,10 @@ const FooterStyle = styled(FlexRow)`
text-decoration: underline; text-decoration: underline;
cursor: pointer; cursor: pointer;
} }
@media (min-width: ${({theme}) => theme.overMobile}) {
height: 72px;
}
`; `;
const Footer = () => { const Footer = () => {

View File

@ -1,6 +1,6 @@
import React from "react"; import React from "react";
import {H1} from "../../utils/fonts"; import {H1} from "../../utils/fonts";
import {FlexColumn, Grid} from "../../utils/containers"; import {FlexColumn} from "../../utils/containers";
import Search from "../../components/elements/Search"; import Search from "../../components/elements/Search";
import Pager from "../../components/elements/Pager"; import Pager from "../../components/elements/Pager";
import {ELEMENTS_PER_PAGE} from "../../utils/globals"; import {ELEMENTS_PER_PAGE} from "../../utils/globals";
@ -8,6 +8,8 @@ import FiltersMenu from "../../components/elements/FiltersMenu";
import _searchQueryHandler from "./_searchQueryHandler"; import _searchQueryHandler from "./_searchQueryHandler";
import _challengesRequest from "./_challengesRequest"; import _challengesRequest from "./_challengesRequest";
import _renderChallenges from "./_renderChallenges"; import _renderChallenges from "./_renderChallenges";
import Media from "react-media";
import theme from "../../utils/theme";
const Challenges = () => { const Challenges = () => {
const [pageNr, setPageNr] = React.useState(1); const [pageNr, setPageNr] = React.useState(1);
@ -74,6 +76,7 @@ const Challenges = () => {
setFiltersMenu(newFiltersMenu); setFiltersMenu(newFiltersMenu);
} }
const mobileRender = () => {
return ( return (
<> <>
<FiltersMenu translateX={filtersMenu ? '0' : '100vw'} opacity={filtersMenu ? '1' : '0'} <FiltersMenu translateX={filtersMenu ? '0' : '100vw'} opacity={filtersMenu ? '1' : '0'}
@ -89,9 +92,7 @@ const Challenges = () => {
</H1> </H1>
<Search searchQueryHandler={searchQueryHandler} toggleFiltersMenu={toggleFiltersMenu}/> <Search searchQueryHandler={searchQueryHandler} toggleFiltersMenu={toggleFiltersMenu}/>
<FlexColumn width='100%'> <FlexColumn width='100%'>
<Grid margin='32px 0' gridGap='32px 0'>
{renderChallenges()} {renderChallenges()}
</Grid>
</FlexColumn> </FlexColumn>
</FlexColumn> </FlexColumn>
<Pager pageNr={pageNr} pages={calcPages()} <Pager pageNr={pageNr} pages={calcPages()}
@ -99,6 +100,37 @@ const Challenges = () => {
</FlexColumn> </FlexColumn>
</> </>
); );
}
const desktopRender = () => {
return (
<FlexColumn as='main' alignmentY='flex-start' width='100%'
minHeight='100vh' padding='112px 0 82px 0'>
<FlexColumn alignmentX='flex-start'>
<H1 as='h1' margin='0 0 32px 0'>
Challenges
</H1>
<Search searchQueryHandler={searchQueryHandler} toggleFiltersMenu={toggleFiltersMenu}/>
<FlexColumn width='100%'>
{renderChallenges()}
</FlexColumn>
</FlexColumn>
<Pager pageNr={pageNr} pages={calcPages()}
nextPage={nextPage} previousPage={previousPage}/>
</FlexColumn>
);
}
return (
<>
<Media query={theme.mobile}>
{mobileRender()}
</Media>
<Media query={theme.desktop}>
{desktopRender()}
</Media>
</>
);
} }
export default Challenges; export default Challenges;

View File

@ -1,10 +1,24 @@
import {ELEMENTS_PER_PAGE} from "../../utils/globals"; import {ELEMENTS_PER_PAGE} from "../../utils/globals";
import MiniChallenge from "../../components/elements/MiniChallenge"; import MiniChallenge from "../../components/elements/MiniChallenge";
import {Grid} from "../../utils/containers";
import styled from "styled-components";
const ChallengesGrid = styled(Grid)`
margin: 32px 0;
grid-gap: 32px 0;
@media (min-width: 1200px) {
margin: 96px 0;
grid-gap: 64px;
grid-template-columns: 1fr 1fr;
}
`;
const _renderChallenges = (pageNr, challenges) => { const _renderChallenges = (pageNr, challenges) => {
const n = (pageNr - 1) * ELEMENTS_PER_PAGE; const n = (pageNr - 1) * ELEMENTS_PER_PAGE;
return ( return (
challenges.slice(n, n + ELEMENTS_PER_PAGE).map((challenge, index) => { <ChallengesGrid margin='32px 0' gridGap='32px 0'>
{challenges.slice(n, n + ELEMENTS_PER_PAGE).map((challenge, index) => {
return ( return (
<MiniChallenge key={`challenge-${index}`} title={challenge.title} type={challenge.type} <MiniChallenge key={`challenge-${index}`} title={challenge.title} type={challenge.type}
description={challenge.description} metric={challenge.mainMetric} description={challenge.description} metric={challenge.mainMetric}
@ -12,7 +26,8 @@ const _renderChallenges = (pageNr, challenges) => {
prize={challenge.prize} deadline={challenge.deadline} prize={challenge.prize} deadline={challenge.deadline}
name={challenge.name}/> name={challenge.name}/>
); );
}) })}
</ChallengesGrid>
) )
} }

View File

@ -1,6 +1,7 @@
const ELEMENTS_PER_PAGE = 12; const ELEMENTS_PER_PAGE = 12;
const MINI_DESCRIPTION_LENGTH = 70;
const API = 'https://gonito.net/api'; const API = 'https://gonito.net/api';
const CHALLENGES_PAGE = '/challenges'; const CHALLENGES_PAGE = '/challenges';
const CHALLENGE_PAGE = '/challenge'; const CHALLENGE_PAGE = '/challenge';
export {ELEMENTS_PER_PAGE, API, CHALLENGES_PAGE, CHALLENGE_PAGE}; export {ELEMENTS_PER_PAGE, API, CHALLENGES_PAGE, CHALLENGE_PAGE, MINI_DESCRIPTION_LENGTH};