Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(tag): 메인페이지 기능 추가 #464

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions src/api/tags/useGetTagsMainpage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useEffect, useState } from "react";
import { AxiosResponse } from "axios";
import { Tag } from "../../type/Tag";
import useApi from "../../hook/useApi";

export const useGetTagsMainpage = () => {
const [tagData, setTagData] = useState<TagType[]>([]);
const { request, Dialog } = useApi("get", "tags", {
limit: 100,
page: 0,
visibility: "public",
});

useEffect(() => {
const getMainTagRequest = (response: AxiosResponse) => {
const tags = response.data.items as Tag[];
setTagData({ list: tags });
};
request(getMainTagRequest);
}, []);

return {
tagData,
};
};
237 changes: 230 additions & 7 deletions src/asset/css/Tags.css
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@
}

.button_tag-create-box {
position: relative;
line-height: 40px;
position: flex;
line-height: 2.5rem;
font-size: large;
font-weight: bold;
text-align: center;
Expand Down Expand Up @@ -109,13 +109,236 @@
margin: 10px;
}

.button_tag {
overflow-y: auto;
max-width: 100rem;
.button_tag_modal_background_ {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.7);
z-index: 4;
}

.button_tag-image-button {
position: relative;
display: flex;
cursor: pointer;
background-color: rgba(00, 00, 00, 0);
z-index: 1;
top: 50%;
left: 50%;
}

@keyframes rotateImage {
0% {
transform: rotate(0deg);
}
50% {
transform: rotate(360deg);
width: 100%;
height: 100%;
}
100% {
width: 65%;
height: 65%;
}
}

.rotate {
animation: rotateImage 1s linear 1 forwards;
}

@keyframes shake {
0% {
transform: translate(0px, 0px) rotate(0deg);
}
25% {
transform: translate(2px, 2px) rotate(1deg);
}
50% {
transform: translate(0px, 0px) rotate(0deg);
}
75% {
transform: translate(2px, 2px) rotate(-1deg);
}
100% {
transform: translate(0px, 0px) rotate(0deg);
}
}

:root {
--base-color: rgba(255, 255, 255, 0.9);
--target-color: rgba(240, 111, 89, 0.683);
}

@keyframes colorChange {
0% {
color: var(--base-color);
}
100% {
color: var(--target-color);
}
}

.button_atcive {
animation: shake 0.1s linear infinite;
}

.button_post_error_code {
animation: colorChange 0.5s linear 1 forwards;
}

.tooltip-content {
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
z-index: 1;
}

@keyframes appearChange {
0% {
color: rgba(255, 255, 255, 0.1);
}
20% {
color: rgba(255, 255, 255, 0.9);
}
90% {
color: rgba(255, 255, 255, 0.9);
}
100% {
color: rgba(255, 255, 255, 0);
}
}

.button_post_error_message {
animation: appearChange 2s linear 1 forwards;
}

@keyframes blink {
0% {
border-color: 0;
}
20% {
border-color: transparent;
}
80% {
border-color: transparent;
}
100% {
border-color: 0;
}
}

.button_create-input:hover {
outline: none;
box-shadow: none;
}

.button_create-input:focus {
outline: none;
box-shadow: none;
border: none;
border-radius: 0;
border-left: 1px solid white;
animation: blink 2.5s linear infinite;
}

.button_create-input:focus::placeholder {
animation: none;
box-shadow: none;
}

/* main */
.main-tag {
width: 100%;
min-height: 42rem;
margin-top: 12rem;
position: relative;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}

.main-tag__wrapper {
margin: auto;
margin-top: 7rem;
margin-bottom: 10rem;
}

.main-tag-list_Background {
/* position: relative; */
width: 100%;
max-width: 90vw;
display: flex;
flex-shrink: 0;
flex-direction: column;
gap: 1rem 0;
padding: 1.5rem 0;
overflow: hidden;
}

.main_tag {
position: relative;
justify-content: center;
align-items: center;
text-align: center;
align-items: center;
margin: 1rem;
font-size: 3rem;
opacity: 1;
left: 0px;
border: none;
display: flex;
gap: 0 0.2rem;
color: rgba(255, 255, 255, 0.75);
background-color: #334155;
border-radius: 0.9rem;
padding: 0.7rem 1rem;
margin-right: 1rem;

box-shadow: 0 0.1rem 0.2rem rgb(0 0 0 / 20%), 0 0.1rem 0.5rem rgb(0 0 0 / 30%),
0 0.2rem 1.5rem rgb(0 0 0 / 40%);
}

.main_tag-box-super {
background-color: rgba(38, 70, 83, 1);
color: rgba(255, 255, 255, 0.75);
}

.loop-slider {
.inner {
display: flex;
width: fit-content;
animation-name: loop;
animation-timing-function: linear;
animation-iteration-count: infinite;
animation-direction: var(--direction);
animation-duration: var(--duration);
}
}

.fade {
pointer-events: none;
background: linear-gradient(
90deg,
rgba(38, 70, 83, 0.2),
transparent 50%,
transparent 50%,
rgba(38, 70, 83, 0.2)
);
position: absolute;
inset: 0;
}

@keyframes loop {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-50%);
}
}

.button_tag-modal {
Expand Down
2 changes: 2 additions & 0 deletions src/component/main/Main.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import MainBanner from "./MainBanner";
import MainNew from "./MainNew";
import MainPopular from "./MainPopular";
import MainTag from "./MainTag";
import "../../asset/css/Main.css";
import useDialog from "../../hook/useDialog";

Expand All @@ -10,6 +11,7 @@ const Main = () => {
<main className="main-wrapper">
<Dialog />
<MainBanner />
<MainTag setOpenTitleAndMessage={setOpenTitleAndMessage} />
<MainNew setOpenTitleAndMessage={setOpenTitleAndMessage} />
<MainPopular setOpenTitleAndMessage={setOpenTitleAndMessage} />
</main>
Expand Down
30 changes: 30 additions & 0 deletions src/component/main/MainTag.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { useGetTagsMainpage } from "../../api/tags/useGetTagsMainpage";
import SubTitle from "../utils/SubTitle";
import MainTagList from "./MainTagList";
import "../../asset/css/Tags.css";

const MainTag = () => {
const { tagData } = useGetTagsMainpage();
const tags = Array.isArray(tagData?.list)
? tagData.list
.filter(tag => tag.visibility === "public")
.map(tag => tag.content)
: [];

return (
<section className="main-tag">
<div className="main-tag__wrapper">
<SubTitle
subTitle="책들에 달린 태그를 확인해보세요."
description="태그를 클릭하면 태그가 달려있는 책들이 검색 됩니다."
alignItems="center"
/>
</div>
<div className="main-tag__wrapper">
<MainTagList tags={tags} />
</div>
</section>
);
};

export default MainTag;
73 changes: 73 additions & 0 deletions src/component/main/MainTagList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { useState } from "react";
import { useNavigate } from "react-router-dom";

const DURATION: number = 50000;
const ROWS: number = 10;
const TAGS_PER_ROW: number = 10;

const random = (min: number, max: number) => Math.floor(Math.random() * (max - min)) + min;
const shuffle = arr => [...arr].sort(() => 0.5 - Math.random());

const InfiniteLoopSlider = ({ children, duration, reverse = false }) => {
return (
<div
className="loop-slider"
style={{
"--duration": `${duration}ms`,
"--direction": reverse ? "reverse" : "normal",
}}
>
<div className="inner">
{children}
{children}
</div>
</div>
);
};

const hashtagSpan = {
fontSize: "2.7rem",
color: "rgba(255, 255, 255, 0.7)",
};

const Tag = ({ text, tagClick }) => (
<div className="main_tag" onClick={() => tagClick(text)}>
<span style={hashtagSpan}>#</span> {text}
</div>
);

const MainTagList = ({ tags }: string[]) => {
const [content, setContent] = useState("");
const navigate = useNavigate();

const tagClick = text => {
setContent(text);
const encodedURIContent = encodeURIComponent(text);
try {
navigate(`/search?search=${encodedURIContent}`);
} catch (e) {
console.error(e);
}
};

return (
<div className="main-tag-list_Background">
{[...new Array(ROWS)].map((_, i) => (
<InfiniteLoopSlider
key={i}
duration={random(DURATION - 5000, DURATION + 5000)}
reverse={i % 2}
>
{shuffle(tags)
.slice(0, TAGS_PER_ROW)
.map(tag => (
<Tag text={tag} tagClick={tagClick} />
))}
</InfiniteLoopSlider>
))}
<div className="fade" />
</div>
);
};

export default MainTagList;