mini challenges grid on desktop
This commit is contained in:
parent
73e7ada0c3
commit
76bdb473e9
@ -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,20 +24,20 @@
|
|||||||
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.
|
||||||
|
|
||||||
You can add webfonts, meta tags, or analytics to this file.
|
You can add webfonts, meta tags, or analytics to this file.
|
||||||
The build step will place the bundled scripts into the <body> tag.
|
The build step will place the bundled scripts into the <body> tag.
|
||||||
|
|
||||||
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>
|
||||||
|
@ -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,43 +57,43 @@ 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}`}>
|
||||||
<FlexRow margin='0 0 14px 0' gap='12px' width='100%' alignmentX='space-between'>
|
<FlexColumn as='article'>
|
||||||
<H3 as='h3' width='85%'>
|
<FlexRow margin='0 0 14px 0' gap='12px' width='100%' alignmentX='space-between'>
|
||||||
{props.title}
|
<H3 as='h3' width='85%'>
|
||||||
</H3>
|
{props.title}
|
||||||
<IconLabel type={props.type} size='30px'/>
|
</H3>
|
||||||
</FlexRow>
|
{props.type ? <IconLabel type={props.type} size='30px'/> : 'xxx'}
|
||||||
<Line/>
|
</FlexRow>
|
||||||
<Body as='p' margin='0 0 14px 0'>
|
<Line/>
|
||||||
{props.description ? renderDescription(props.description) : 'xxx'}
|
<Body as='p' margin='0 0 14px 0'>
|
||||||
</Body>
|
{props.description ? renderDescription(props.description) : 'xxx'}
|
||||||
<IconsGrid>
|
</Body>
|
||||||
<IconLabel size='24px' gap='8px' type='metric'>
|
<IconsGrid>
|
||||||
{props.metric ? props.metric : 'xxx'}
|
<IconLabel size='24px' gap='8px' type='metric'>
|
||||||
</IconLabel>
|
{props.metric ? props.metric : 'xxx'}
|
||||||
<IconLabel size='24px' gap='8px' type='bestScore'>
|
</IconLabel>
|
||||||
{props.bestScore ? props.bestScore : 'xxx'}
|
<IconLabel size='24px' gap='8px' type='bestScore'>
|
||||||
</IconLabel>
|
{props.bestScore ? props.bestScore : 'xxx'}
|
||||||
<IconLabel size='24px' gap='8px' type='deadline'>
|
</IconLabel>
|
||||||
{props.deadline ? props.deadline.slice(0, 10) : 'xxx'}
|
<IconLabel size='24px' gap='8px' type='deadline'>
|
||||||
</IconLabel>
|
{props.deadline ? props.deadline.slice(0, 10) : 'xxx'}
|
||||||
<IconLabel size='24px' gap='8px' type='baseline'>
|
</IconLabel>
|
||||||
{props.baseline ? props.baseline : 'xxx'}
|
<IconLabel size='24px' gap='8px' type='baseline'>
|
||||||
</IconLabel>
|
{props.baseline ? props.baseline : 'xxx'}
|
||||||
{props.prize ? <IconLabel size='24px' gap='8px' type='prize'>
|
</IconLabel>
|
||||||
{props.prize}
|
{props.prize ? <IconLabel size='24px' gap='8px' type='prize'>
|
||||||
</IconLabel> : ''}
|
{props.prize}
|
||||||
</IconsGrid>
|
</IconLabel> : ''}
|
||||||
<Container as={Link} to={`${CHALLENGE_PAGE}/${props.name}`}/>
|
</IconsGrid>
|
||||||
|
</FlexColumn>
|
||||||
</ChallengeStyle>
|
</ChallengeStyle>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 = () => {
|
||||||
|
@ -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,29 +76,59 @@ const Challenges = () => {
|
|||||||
setFiltersMenu(newFiltersMenu);
|
setFiltersMenu(newFiltersMenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
const mobileRender = () => {
|
||||||
<>
|
return (
|
||||||
<FiltersMenu translateX={filtersMenu ? '0' : '100vw'} opacity={filtersMenu ? '1' : '0'}
|
<>
|
||||||
toggleFiltersMenu={toggleFiltersMenu}
|
<FiltersMenu translateX={filtersMenu ? '0' : '100vw'} opacity={filtersMenu ? '1' : '0'}
|
||||||
sortByHandler={sortByHandler} statusHandler={statusHandler}
|
toggleFiltersMenu={toggleFiltersMenu}
|
||||||
challengeTypeHandler={challengeTypeHandler} commercialHandler={commercialHandler}
|
sortByHandler={sortByHandler} statusHandler={statusHandler}
|
||||||
sortBy={sortBy} status={status} challengeType={challengeType} commercial={commercial}/>
|
challengeTypeHandler={challengeTypeHandler} commercialHandler={commercialHandler}
|
||||||
|
sortBy={sortBy} status={status} challengeType={challengeType} commercial={commercial}/>
|
||||||
|
<FlexColumn as='main' alignmentY='flex-start' width='100%'
|
||||||
|
minHeight='100vh' padding='90px 0 32px 0'>
|
||||||
|
<FlexColumn alignmentX='flex-start' width='80%'>
|
||||||
|
<H1 as='h1' margin='0 0 20px 0'>
|
||||||
|
Challenges
|
||||||
|
</H1>
|
||||||
|
<Search searchQueryHandler={searchQueryHandler} toggleFiltersMenu={toggleFiltersMenu}/>
|
||||||
|
<FlexColumn width='100%'>
|
||||||
|
{renderChallenges()}
|
||||||
|
</FlexColumn>
|
||||||
|
</FlexColumn>
|
||||||
|
<Pager pageNr={pageNr} pages={calcPages()}
|
||||||
|
nextPage={nextPage} previousPage={previousPage}/>
|
||||||
|
</FlexColumn>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const desktopRender = () => {
|
||||||
|
return (
|
||||||
<FlexColumn as='main' alignmentY='flex-start' width='100%'
|
<FlexColumn as='main' alignmentY='flex-start' width='100%'
|
||||||
minHeight='100vh' padding='90px 0 32px 0'>
|
minHeight='100vh' padding='112px 0 82px 0'>
|
||||||
<FlexColumn alignmentX='flex-start' width='80%'>
|
<FlexColumn alignmentX='flex-start'>
|
||||||
<H1 as='h1' margin='0 0 20px 0'>
|
<H1 as='h1' margin='0 0 32px 0'>
|
||||||
Challenges
|
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()}
|
||||||
nextPage={nextPage} previousPage={previousPage}/>
|
nextPage={nextPage} previousPage={previousPage}/>
|
||||||
</FlexColumn>
|
</FlexColumn>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Media query={theme.mobile}>
|
||||||
|
{mobileRender()}
|
||||||
|
</Media>
|
||||||
|
<Media query={theme.desktop}>
|
||||||
|
{desktopRender()}
|
||||||
|
</Media>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,33 @@
|
|||||||
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'>
|
||||||
return (
|
{challenges.slice(n, n + ELEMENTS_PER_PAGE).map((challenge, index) => {
|
||||||
<MiniChallenge key={`challenge-${index}`} title={challenge.title} type={challenge.type}
|
return (
|
||||||
description={challenge.description} metric={challenge.mainMetric}
|
<MiniChallenge key={`challenge-${index}`} title={challenge.title} type={challenge.type}
|
||||||
bestScore={challenge.bestScore} baseline={challenge.baseline}
|
description={challenge.description} metric={challenge.mainMetric}
|
||||||
prize={challenge.prize} deadline={challenge.deadline}
|
bestScore={challenge.bestScore} baseline={challenge.baseline}
|
||||||
name={challenge.name}/>
|
prize={challenge.prize} deadline={challenge.deadline}
|
||||||
);
|
name={challenge.name}/>
|
||||||
})
|
);
|
||||||
|
})}
|
||||||
|
</ChallengesGrid>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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};
|
Loading…
Reference in New Issue
Block a user