Skip to content

Commit c175391

Browse files
committed
feat: featured section control 추가
1 parent 39f7262 commit c175391

File tree

5 files changed

+206
-11
lines changed

5 files changed

+206
-11
lines changed

Diff for: .eslintrc.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ module.exports = {
55
project: "./tsconfig.json",
66
tsconfigRootDir: __dirname,
77
},
8-
extends: ["prettier"],
8+
extends: ["prettier", "plugin:react-hooks/recommended"],
99
plugins: [
1010
"@typescript-eslint/eslint-plugin",
1111
"react",

Diff for: .pnp.cjs

+16-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: package.json

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
"eslint-plugin-jsx-a11y": "^6.6.1",
5656
"eslint-plugin-prettier": "^4.2.1",
5757
"eslint-plugin-react": "^7.31.8",
58+
"eslint-plugin-react-hooks": "^4.6.0",
5859
"eslint-plugin-simple-import-sort": "^7.0.0",
5960
"jest": "^29.2.2",
6061
"prettier": "^2.7.1",

Diff for: src/components/FeaturedPostSection.tsx

+187-9
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,115 @@
1-
import { Box, Heading } from "@chakra-ui/react";
1+
import { Box, Flex, Heading } from "@chakra-ui/react";
22
import { AnimatePresence, motion } from "framer-motion";
3-
import { useEffect, useState } from "react";
3+
import { useCallback, useEffect, useRef, useState } from "react";
44

55
import FeaturedPostCard from "./FeaturedPostCard";
66

77
interface FeaturedPostSectionProps {
88
posts: GatsbyTypes.AllPostPageTemplateQuery["featuredPosts"]["nodes"];
99
}
1010

11+
const TIME_INTERVAL = 10000;
12+
1113
const FeaturedPostSection = ({ posts }: FeaturedPostSectionProps) => {
1214
const [featuredIndex, setFeaturedIndex] = useState(0);
15+
const [isPaused, setIsPaused] = useState(false);
16+
const intervalRef = useRef<NodeJS.Timeout>();
17+
18+
const intervalCallback = useCallback(() => {
19+
setFeaturedIndex((prev) => (prev + 1) % posts.length);
20+
}, [posts.length]);
21+
22+
const moveNextFeatured = useCallback(() => {
23+
setFeaturedIndex((prev) => (prev + 1) % posts.length);
24+
}, [posts.length]);
25+
26+
const movePrevFeatured = useCallback(() => {
27+
setFeaturedIndex((prev) => (prev - 1 + posts.length) % posts.length);
28+
}, [posts.length]);
1329

1430
useEffect(() => {
15-
const interval = setInterval(
16-
() => setFeaturedIndex((prev) => (prev + 1) % posts.length),
17-
10000,
18-
);
19-
return () => clearInterval(interval);
20-
}, []);
31+
if (isPaused) {
32+
clearInterval(intervalRef.current);
33+
} else {
34+
intervalRef.current = setInterval(intervalCallback, TIME_INTERVAL);
35+
}
36+
37+
return () => clearInterval(intervalRef.current);
38+
}, [intervalCallback, isPaused]);
2139

2240
return (
2341
<Box width="100%" maxWidth={{ base: "100%", md: "600px", lg: "100%" }}>
24-
<Heading fontStyle="italic">Featured.</Heading>
42+
<Flex width="100%" justifyContent="space-between" alignItems="flex-end">
43+
<Heading fontStyle="italic">Featured.</Heading>
44+
<Flex direction="column" justifyContent="center" alignItems="center" gap="8px">
45+
<Flex>
46+
<Box
47+
as="button"
48+
onClick={movePrevFeatured}
49+
cursor="pointer"
50+
_hover={{ color: "blue.500" }}
51+
>
52+
<LeftArrowIcon />
53+
</Box>
54+
55+
{isPaused ? (
56+
<Box
57+
as="button"
58+
onClick={() => setIsPaused(false)}
59+
cursor="pointer"
60+
_hover={{ color: "blue.500" }}
61+
>
62+
<PlayIcon />
63+
</Box>
64+
) : (
65+
<Box
66+
as="button"
67+
onClick={() => setIsPaused(true)}
68+
cursor="pointer"
69+
_hover={{ color: "blue.500" }}
70+
>
71+
<PauseIcon />
72+
</Box>
73+
)}
74+
75+
<Box
76+
as="button"
77+
onClick={moveNextFeatured}
78+
cursor="pointer"
79+
_hover={{ color: "blue.500" }}
80+
>
81+
<RightArrowIcon />
82+
</Box>
83+
</Flex>
84+
<Flex gap="6px">
85+
{posts.map((post, index) =>
86+
index === featuredIndex ? (
87+
<Box
88+
as="button"
89+
key={post.frontmatter?.slug!}
90+
w="8px"
91+
h="8px"
92+
borderRadius="50%"
93+
bg="gray.900"
94+
cursor="pointer"
95+
onClick={() => setFeaturedIndex(index)}
96+
/>
97+
) : (
98+
<Box
99+
as="button"
100+
key={post.frontmatter?.slug!}
101+
w="8px"
102+
h="8px"
103+
borderRadius="50%"
104+
border="1px solid"
105+
cursor="pointer"
106+
onClick={() => setFeaturedIndex(index)}
107+
/>
108+
),
109+
)}
110+
</Flex>
111+
</Flex>
112+
</Flex>
25113

26114
<Box pos="relative" height={{ base: "500px", md: "600px" }} marginTop="20px">
27115
<AnimatePresence>
@@ -59,4 +147,94 @@ const FeaturedPostSection = ({ posts }: FeaturedPostSectionProps) => {
59147
);
60148
};
61149

150+
const LeftArrowIcon = () => {
151+
return (
152+
<svg
153+
width="24px"
154+
height="24px"
155+
viewBox="0 0 24 24"
156+
fill="none"
157+
xmlns="http://www.w3.org/2000/svg"
158+
>
159+
<path
160+
d="M17.9999 12.0001V14.6701C17.9999 17.9801 15.6499 19.3401 12.7799 17.6801L10.4699 16.3401L8.15995 15.0001C5.28995 13.3401 5.28995 10.6301 8.15995 8.97005L10.4699 7.63005L12.7799 6.29005C15.6499 4.66005 17.9999 6.01005 17.9999 9.33005V12.0001Z"
161+
stroke="currentColor"
162+
strokeWidth="1.5"
163+
strokeMiterlimit="10"
164+
strokeLinecap="round"
165+
strokeLinejoin="round"
166+
/>
167+
</svg>
168+
);
169+
};
170+
171+
const RightArrowIcon = () => {
172+
return (
173+
<svg
174+
width="24px"
175+
height="24px"
176+
viewBox="0 0 24 24"
177+
fill="none"
178+
xmlns="http://www.w3.org/2000/svg"
179+
>
180+
<path
181+
d="M6 11.9999V9.32992C6 6.01992 8.35 4.65992 11.22 6.31992L13.53 7.65992L15.84 8.99992C18.71 10.6599 18.71 13.3699 15.84 15.0299L13.53 16.3699L11.22 17.7099C8.35 19.3399 6 17.9899 6 14.6699V11.9999Z"
182+
stroke="currentColor"
183+
strokeWidth="1.5"
184+
strokeMiterlimit="10"
185+
strokeLinecap="round"
186+
strokeLinejoin="round"
187+
/>
188+
</svg>
189+
);
190+
};
191+
192+
const PauseIcon = () => {
193+
return (
194+
<svg
195+
width="24px"
196+
height="24px"
197+
viewBox="0 0 24 24"
198+
fill="none"
199+
xmlns="http://www.w3.org/2000/svg"
200+
>
201+
<path
202+
d="M9.5 15V9M14.5 15V9M22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12Z"
203+
stroke="currentColor"
204+
strokeWidth="1.5"
205+
strokeLinecap="round"
206+
strokeLinejoin="round"
207+
/>
208+
</svg>
209+
);
210+
};
211+
212+
const PlayIcon = () => {
213+
return (
214+
<svg
215+
width="24px"
216+
height="24px"
217+
viewBox="0 0 24 24"
218+
fill="none"
219+
xmlns="http://www.w3.org/2000/svg"
220+
>
221+
<path
222+
d="M11.97 22C17.4928 22 21.97 17.5228 21.97 12C21.97 6.47715 17.4928 2 11.97 2C6.44712 2 1.96997 6.47715 1.96997 12C1.96997 17.5228 6.44712 22 11.97 22Z"
223+
stroke="currentColor"
224+
strokeWidth="1.5"
225+
strokeLinecap="round"
226+
strokeLinejoin="round"
227+
/>
228+
<path
229+
d="M8.73999 12.2299V10.5599C8.73999 8.47988 10.21 7.62988 12.01 8.66988L13.46 9.50988L14.91 10.3499C16.71 11.3899 16.71 13.0899 14.91 14.1299L13.46 14.9699L12.01 15.8099C10.21 16.8499 8.73999 15.9999 8.73999 13.9199V12.2299Z"
230+
stroke="currentColor"
231+
strokeWidth="1.5"
232+
strokeMiterlimit="10"
233+
strokeLinecap="round"
234+
strokeLinejoin="round"
235+
/>
236+
</svg>
237+
);
238+
};
239+
62240
export default FeaturedPostSection;

Diff for: yarn.lock

+1
Original file line numberDiff line numberDiff line change
@@ -16451,6 +16451,7 @@ __metadata:
1645116451
eslint-plugin-jsx-a11y: ^6.6.1
1645216452
eslint-plugin-prettier: ^4.2.1
1645316453
eslint-plugin-react: ^7.31.8
16454+
eslint-plugin-react-hooks: ^4.6.0
1645416455
eslint-plugin-simple-import-sort: ^7.0.0
1645516456
framer-motion: ^7.6.2
1645616457
gatsby: ^5.0.1

0 commit comments

Comments
 (0)