Skip to content

Commit 1b8edbc

Browse files
authored
Merge pull request #69 from OCA-UFCG/feat/news-carousel
Add a News Carousel
2 parents e7cdec2 + a837c31 commit 1b8edbc

File tree

10 files changed

+358
-12
lines changed

10 files changed

+358
-12
lines changed

public/sprite.svg

Lines changed: 45 additions & 3 deletions
Loading

src/app/globalStyles.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,15 @@ export const GlobalStyles = createGlobalStyle`
7474
background-position: 100% 100%;
7575
}
7676
}
77+
78+
@keyframes expandWidth {
79+
from {
80+
width: 0%;
81+
}
82+
to {
83+
width: 100%;
84+
}
85+
}
7786
}
7887
7988

src/app/page.tsx

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,15 @@
1-
import { LogoSection, OcaImage } from "./globalStyles";
2-
31
import PublicationsSection from "@/components/Publication/Section/Publications";
42
import SponsorsSection from "@/components/Sponsor/Section/Sponsors";
53
import TeamMembersSection from "@/components/TeamMember/Section/TeamMembers";
6-
import AboutSection from "@/components/AboutSection/AboutSection";
74
import MapsSection from "@/components/MapsSection/MapsSection";
5+
import CarouselSection from "@/components/CarouselSection/CarouselSection";
86

97
import Template from "@/templates/hubTemplate";
108

119
export default function Home() {
1210
return (
1311
<Template>
14-
<LogoSection>
15-
<OcaImage id="logo-oca" />
16-
</LogoSection>
17-
<AboutSection />
12+
<CarouselSection />
1813
<MapsSection />
1914
<PublicationsSection />
2015
<SponsorsSection />
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { Section } from "@/app/globalStyles";
2+
import styled from "styled-components";
3+
4+
export const CarouselWrapperSection = styled(Section)`
5+
display: flex;
6+
flex-direction: column;
7+
justify-content: center;
8+
align-items: center;
9+
padding: 1rem;
10+
width: 100%;
11+
max-width: 1440px;
12+
height: 100%;
13+
box-sizing: border-box;
14+
margin-top: 4rem;
15+
`;
16+
17+
export const CarouselWrapper = styled.div`
18+
display: grid;
19+
background: #778c6130;
20+
gap: 1rem;
21+
width: 100%;
22+
height: auto;
23+
padding: 1rem;
24+
border-radius: 0.2rem;
25+
aspect-ratio: 16 / 8;
26+
box-sizing: border-box;
27+
28+
@media (max-width: 768px) {
29+
aspect-ratio: 10 / 8;
30+
}
31+
`;
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
"use client";
2+
3+
import NewsCarousel from "./NewsCarousel/NewsCarousel";
4+
import { CMSContext } from "@/contexts/ContentProvider";
5+
import {
6+
CarouselWrapper,
7+
CarouselWrapperSection,
8+
} from "./CarouselSection.styles";
9+
import { SectionTitle } from "@/app/globalStyles";
10+
import { useContext, useEffect, useState } from "react";
11+
import { INews } from "@/utils/interfaces";
12+
import { defaultNews } from "@/utils/constants";
13+
14+
const CarouselSection = () => {
15+
const { loadData } = useContext(CMSContext);
16+
const [newsItems, setNewsItems] = useState<INews[]>(defaultNews);
17+
const [loading, setLoading] = useState(true);
18+
19+
const loadNews = async () => {
20+
const fetchedNewsItems = await loadData("news");
21+
if (fetchedNewsItems.length > 0) {
22+
setNewsItems(fetchedNewsItems as unknown as INews[]);
23+
}
24+
setLoading(false);
25+
};
26+
27+
useEffect(() => {
28+
loadNews();
29+
}, []);
30+
31+
return (
32+
<CarouselWrapperSection>
33+
<SectionTitle>Últimas atualizações</SectionTitle>
34+
<CarouselWrapper>
35+
<NewsCarousel newsItems={newsItems} loading={loading} />
36+
</CarouselWrapper>
37+
</CarouselWrapperSection>
38+
);
39+
};
40+
41+
export default CarouselSection;
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import { Icon } from "@/components/Icon/Icon";
2+
import Image from "next/image";
3+
import styled from "styled-components";
4+
5+
export const NewsWrapper = styled.a`
6+
position: relative;
7+
box-sizing: border-box;
8+
padding: 1rem;
9+
border-radius: 0.2rem;
10+
width: 100%;
11+
height: auto;
12+
display: flex;
13+
flex-direction: column;
14+
align-items: center;
15+
justify-content: center;
16+
text-decoration: none;
17+
overflow: hidden;
18+
cursor: pointer;
19+
`;
20+
21+
export const NewsImage = styled(Image)`
22+
object-fit: cover;
23+
display: flex;
24+
width: 100%;
25+
height: 100%;
26+
border-radius: 0.2rem;
27+
position: absolute;
28+
`;
29+
30+
export const Overlay = styled.div`
31+
position: absolute;
32+
top: 50%;
33+
left: 0;
34+
width: 100%;
35+
height: 50%;
36+
background: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.7));
37+
border-radius: 0 0 0.2rem 0.2rem;
38+
pointer-events: none;
39+
`;
40+
41+
export const RoundButton = styled(Icon)`
42+
cursor: pointer;
43+
&:hover {
44+
opacity: 0.7;
45+
transform: scale(1.1);
46+
}
47+
transition: 0.3s;
48+
`;
49+
50+
export const ButtonWrapper = styled.div`
51+
display: flex;
52+
justify-content: space-between;
53+
align-items: center;
54+
width: 100%;
55+
z-index: 1;
56+
align-self: center;
57+
margin-top: auto;
58+
`;
59+
60+
export const NewsTitle = styled.h2`
61+
font-size: 1.5rem;
62+
font-weight: 500;
63+
z-index: 0;
64+
color: white;
65+
align-self: baseline;
66+
margin-top: auto;
67+
margin-bottom: 0.5rem;
68+
left: 0;
69+
`;
70+
71+
export const LoadingBar = styled.div`
72+
width: 0;
73+
height: 0.5rem;
74+
background-color: #d4f8af;
75+
z-index: 0;
76+
align-self: baseline;
77+
animation: expandWidth 7s linear forwards;
78+
position: absolute;
79+
bottom: 0;
80+
left: 0;
81+
border-radius: 0 0 0.2rem 0.2rem;
82+
`;
83+
84+
export const LoadingIcon = styled(Icon)`
85+
animation: spin 1.5s linear infinite;
86+
color: ${({ theme }) => theme.colors.green}60;
87+
font-size: 2rem;
88+
margin: 0 auto;
89+
display: flex;
90+
justify-content: center;
91+
align-items: center;
92+
height: 100%;
93+
width: 100%;
94+
max-height: 200px;
95+
max-width: 200px;
96+
border-radius: 50%;
97+
box-sizing: border-box;
98+
`;
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
"use client";
2+
3+
import { useState, useEffect } from "react";
4+
import {
5+
NewsWrapper,
6+
NewsImage,
7+
NewsTitle,
8+
ButtonWrapper,
9+
LoadingBar,
10+
RoundButton,
11+
Overlay,
12+
LoadingIcon,
13+
} from "./NewsCarousel.styles";
14+
import { INews } from "@/utils/interfaces";
15+
16+
const NewsCarousel = ({
17+
newsItems,
18+
loading,
19+
}: {
20+
newsItems: INews[];
21+
loading: boolean;
22+
}) => {
23+
const [index, setIndex] = useState(0);
24+
let handler: NodeJS.Timeout;
25+
26+
const handleNews = (
27+
updateIndex: () => number,
28+
event?: { preventDefault: () => void } | undefined,
29+
) => {
30+
event?.preventDefault();
31+
setIndex(updateIndex);
32+
};
33+
34+
const newsDebounce = () => {
35+
handler = setTimeout(() => {
36+
handleNews(() => (index + 1) % newsItems.length);
37+
}, 7000);
38+
};
39+
40+
useEffect(() => {
41+
if (newsItems.length > 1) {
42+
newsDebounce();
43+
}
44+
45+
return () => {
46+
clearTimeout(handler);
47+
};
48+
}, [index, newsItems]);
49+
50+
return (
51+
<NewsWrapper href={newsItems[index]?.fields.url} target="_blank">
52+
{loading ? (
53+
<LoadingIcon id="loading" />
54+
) : (
55+
<>
56+
<NewsImage
57+
width={1600}
58+
height={800}
59+
src={
60+
newsItems[index]?.fields.thumb.fields.file.url &&
61+
`https://${newsItems[index]?.fields.thumb.fields.file.url}`
62+
}
63+
alt="News"
64+
/>
65+
<Overlay />
66+
{newsItems.length > 1 && (
67+
<ButtonWrapper>
68+
<RoundButton
69+
size={36}
70+
id="previous-slide"
71+
onClick={(e) =>
72+
handleNews(
73+
() => (index - 1 + newsItems.length) % newsItems.length,
74+
e,
75+
)
76+
}
77+
/>
78+
<RoundButton
79+
size={36}
80+
id="next-slide"
81+
onClick={(e) =>
82+
handleNews(() => (index + 1) % newsItems.length, e)
83+
}
84+
/>
85+
</ButtonWrapper>
86+
)}
87+
<NewsTitle>
88+
{newsItems[index]?.fields.title.length > 60
89+
? `${newsItems[index]?.fields.title.substring(0, 60)}...`
90+
: newsItems[index]?.fields.title}
91+
</NewsTitle>
92+
{newsItems.length > 1 && <LoadingBar key={index} />}
93+
</>
94+
)}
95+
</NewsWrapper>
96+
);
97+
};
98+
99+
export default NewsCarousel;

src/components/Icon/Icon.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,23 @@ export const Icon = ({
33
size,
44
width,
55
height,
6+
onClick,
67
...props
78
}: {
89
id: string;
910
size?: number;
1011
width?: number;
1112
height?: number;
1213
props?: object;
14+
onClick?: (arg0: any) => void;
1315
}) => {
1416
return (
15-
<svg {...props} width={size ? size : width} height={size ? size : height}>
17+
<svg
18+
{...props}
19+
onClick={onClick}
20+
width={size ? size : width}
21+
height={size ? size : height}
22+
>
1623
<use href={`/sprite.svg#${id}`} />
1724
</svg>
1825
);

0 commit comments

Comments
 (0)