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

@ -24,7 +24,7 @@
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`.
-->
<title>React App</title>
<title>Gonito</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>

View File

@ -4,7 +4,7 @@ import {Body, H3} from "../../utils/fonts";
import styled from "styled-components";
import IconLabel from "./IconLabel";
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)`
padding: 12px;
@ -14,10 +14,6 @@ const ChallengeStyle = styled(FlexColumn)`
position: relative;
max-width: 420px;
p {
width: 80%;
}
* {
cursor: pointer;
}
@ -26,12 +22,19 @@ const ChallengeStyle = styled(FlexColumn)`
transform: scale(1.05);
}
a {
position: absolute;
top: 0;
left: 0;
article {
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 renderDescription = (description) => {
if (description.length <= 200)
if (description.length <= MINI_DESCRIPTION_LENGTH)
return description;
return `${description.slice(0, 100)}...`
return `${description.slice(0, MINI_DESCRIPTION_LENGTH)}...`
}
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'>
<H3 as='h3' width='85%'>
{props.title}
</H3>
<IconLabel type={props.type} size='30px'/>
{props.type ? <IconLabel type={props.type} size='30px'/> : 'xxx'}
</FlexRow>
<Line/>
<Body as='p' margin='0 0 14px 0'>
@ -90,7 +93,7 @@ const MiniChallenge = (props) => {
{props.prize}
</IconLabel> : ''}
</IconsGrid>
<Container as={Link} to={`${CHALLENGE_PAGE}/${props.name}`}/>
</FlexColumn>
</ChallengeStyle>
);
}

View File

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

View File

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

View File

@ -1,6 +1,6 @@
import React from "react";
import {H1} from "../../utils/fonts";
import {FlexColumn, Grid} from "../../utils/containers";
import {FlexColumn} from "../../utils/containers";
import Search from "../../components/elements/Search";
import Pager from "../../components/elements/Pager";
import {ELEMENTS_PER_PAGE} from "../../utils/globals";
@ -8,6 +8,8 @@ import FiltersMenu from "../../components/elements/FiltersMenu";
import _searchQueryHandler from "./_searchQueryHandler";
import _challengesRequest from "./_challengesRequest";
import _renderChallenges from "./_renderChallenges";
import Media from "react-media";
import theme from "../../utils/theme";
const Challenges = () => {
const [pageNr, setPageNr] = React.useState(1);
@ -74,6 +76,7 @@ const Challenges = () => {
setFiltersMenu(newFiltersMenu);
}
const mobileRender = () => {
return (
<>
<FiltersMenu translateX={filtersMenu ? '0' : '100vw'} opacity={filtersMenu ? '1' : '0'}
@ -89,9 +92,7 @@ const Challenges = () => {
</H1>
<Search searchQueryHandler={searchQueryHandler} toggleFiltersMenu={toggleFiltersMenu}/>
<FlexColumn width='100%'>
<Grid margin='32px 0' gridGap='32px 0'>
{renderChallenges()}
</Grid>
</FlexColumn>
</FlexColumn>
<Pager pageNr={pageNr} pages={calcPages()}
@ -101,4 +102,35 @@ const Challenges = () => {
);
}
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;

View File

@ -1,10 +1,24 @@
import {ELEMENTS_PER_PAGE} from "../../utils/globals";
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 n = (pageNr - 1) * ELEMENTS_PER_PAGE;
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 (
<MiniChallenge key={`challenge-${index}`} title={challenge.title} type={challenge.type}
description={challenge.description} metric={challenge.mainMetric}
@ -12,7 +26,8 @@ const _renderChallenges = (pageNr, challenges) => {
prize={challenge.prize} deadline={challenge.deadline}
name={challenge.name}/>
);
})
})}
</ChallengesGrid>
)
}

View File

@ -1,6 +1,7 @@
const ELEMENTS_PER_PAGE = 12;
const MINI_DESCRIPTION_LENGTH = 70;
const API = 'https://gonito.net/api';
const CHALLENGES_PAGE = '/challenges';
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};