diff --git a/src/features/animes/components/AnimeCard/index.tsx b/src/features/animes/components/AnimeCard/index.tsx index 368563d8..645159e1 100644 --- a/src/features/animes/components/AnimeCard/index.tsx +++ b/src/features/animes/components/AnimeCard/index.tsx @@ -1,5 +1,4 @@ import { Star } from "@phosphor-icons/react"; -import { Link } from "react-router-dom"; import { calcStarRatingAvg } from "@/utils/common"; @@ -26,6 +25,9 @@ export interface AnimeCardProps { /** 페이지 이동 */ onClick: (animeId: number, e: React.MouseEvent) => void; + + /** style: 사용되는 곳에 따라 width를 다르게 설정 */ + display?: "default" | "carousel"; } export default function AnimeCard({ @@ -34,10 +36,16 @@ export default function AnimeCard({ title, starScoreAvg, onClick, + display = "default", }: AnimeCardProps) { return ( - onClick(id, e)}> - + onClick(id, e)} + display={display} + > +
+ +
{title} diff --git a/src/features/animes/components/AnimeCard/style.ts b/src/features/animes/components/AnimeCard/style.ts index 1f0db8d7..af9c26a8 100644 --- a/src/features/animes/components/AnimeCard/style.ts +++ b/src/features/animes/components/AnimeCard/style.ts @@ -1,16 +1,54 @@ import { css } from "@emotion/react"; import styled from "@emotion/styled"; +import { AnimeCardProps } from "."; + interface ImageProps { image: string; } -export const AnimeCardContainer = styled.div` - width: 100%; +export const AnimeCardContainer = styled.div>` + width: 100%; // display === 'carousel'; + cursor: pointer; + + ${({ theme, display }) => + display === "default" && + css` + width: calc(50% - 4px); + &:nth-of-type(odd) { + margin-right: 8px; + } + ${theme.mq("sm")} { + width: calc(33% - 4px); + &:nth-of-type(odd) { + margin-right: 0; + } + &:not(:nth-of-type(3n)) { + margin-right: 8px; + } + } + + & .image-container { + position: relative; + width: 100%; + height: 0; + padding-bottom: 70%; + } + + & .image { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 100%; + height: 100%; + } + `} `; +/** .image className */ export const Image = styled.div` - height: 110px; + height: 110px; // display === 'carousel'; border-radius: 5px; ${({ image }) => css` background: @@ -28,6 +66,7 @@ export const InfoContainer = styled.div` flex-direction: column; align-items: flex-start; gap: 2px; + margin-top: 8px; `; export const Title = styled.div` diff --git a/src/features/animes/components/AnimeCardSkeleton/GridAnimeCardSkeleton.style.ts b/src/features/animes/components/AnimeCardSkeleton/GridAnimeCardSkeleton.style.ts new file mode 100644 index 00000000..a4b95eea --- /dev/null +++ b/src/features/animes/components/AnimeCardSkeleton/GridAnimeCardSkeleton.style.ts @@ -0,0 +1,18 @@ +import styled from "@emotion/styled"; + +export const GridAnimeCardSkeletonContainer = styled.div` + width: calc(50% - 4px); + &:nth-of-type(odd) { + margin-right: 8px; + } + + ${({ theme }) => theme.mq("sm")} { + width: calc(33% - 4px); + &:nth-of-type(odd) { + margin-right: 0; + } + &:not(:nth-of-type(3n)) { + margin-right: 8px; + } + } +`; diff --git a/src/features/animes/components/AnimeCardSkeleton/GridAnimeCardSkeleton.tsx b/src/features/animes/components/AnimeCardSkeleton/GridAnimeCardSkeleton.tsx new file mode 100644 index 00000000..65701463 --- /dev/null +++ b/src/features/animes/components/AnimeCardSkeleton/GridAnimeCardSkeleton.tsx @@ -0,0 +1,11 @@ +import { GridAnimeCardSkeletonContainer } from "./GridAnimeCardSkeleton.style"; + +import AnimeCardSkeleton from "."; + +export default function GridAnimeCardSkeleton() { + return ( + + + + ); +} diff --git a/src/features/animes/components/AnimeCardSkeleton/index.tsx b/src/features/animes/components/AnimeCardSkeleton/index.tsx index 0e883fc1..58cb0e8b 100644 --- a/src/features/animes/components/AnimeCardSkeleton/index.tsx +++ b/src/features/animes/components/AnimeCardSkeleton/index.tsx @@ -5,9 +5,9 @@ import { AnimeCardSkeletonContainer } from "./style"; export default function AnimeCardSkeleton() { return ( - - - + + + ); } diff --git a/src/features/animes/components/AnimeCardSkeleton/style.ts b/src/features/animes/components/AnimeCardSkeleton/style.ts index bdae9d51..7638ab6e 100644 --- a/src/features/animes/components/AnimeCardSkeleton/style.ts +++ b/src/features/animes/components/AnimeCardSkeleton/style.ts @@ -1,6 +1,7 @@ import styled from "@emotion/styled"; export const AnimeCardSkeletonContainer = styled.div` + width: 100%; display: flex; flex-direction: column; gap: 8px; diff --git a/src/features/animes/components/AnimeMainCarousel/style.ts b/src/features/animes/components/AnimeMainCarousel/style.ts index 2625efa4..5a595a8b 100644 --- a/src/features/animes/components/AnimeMainCarousel/style.ts +++ b/src/features/animes/components/AnimeMainCarousel/style.ts @@ -6,6 +6,7 @@ export const AnimeMainCarouselContainer = styled.div<{ image: string }>` width: 100%; height: 545px; overflow: hidden; + cursor: pointer; ${({ theme }) => theme.mq("md")} { & .slick-prev, diff --git a/src/features/animes/components/AnimeRanking/style.ts b/src/features/animes/components/AnimeRanking/style.ts index 3ddbfbb6..c30e791b 100644 --- a/src/features/animes/components/AnimeRanking/style.ts +++ b/src/features/animes/components/AnimeRanking/style.ts @@ -69,7 +69,7 @@ export const AnimeRankingContainer = styled.section` /* 아래쪽 캐러셀의 왼쪽 마진 */ & .slick-slider:last-child { - & .slick-track .slick-slide:first-child { + & .slick-track .slick-slide:first-of-type { margin-left: 16px; } } @@ -98,6 +98,7 @@ export const HighlightItemContainer = styled.div` padding-bottom: 46%; margin: 0 auto; margin-bottom: 4px; + cursor: pointer; `; export const HighlightItem = styled.div` @@ -144,6 +145,7 @@ export const SliderItem = styled.div` flex-direction: column; gap: 8px; flex-shrink: 0; + cursor: pointer; & > div:last-of-type { ${({ theme }) => theme.typo["body-3-r"]} diff --git a/src/features/animes/components/AnimeSlide/index.tsx b/src/features/animes/components/AnimeSlide/index.tsx index 942496a5..ab482d96 100644 --- a/src/features/animes/components/AnimeSlide/index.tsx +++ b/src/features/animes/components/AnimeSlide/index.tsx @@ -36,6 +36,7 @@ export default function AnimeSlide({ title, animes }: AnimeSlideProps) { {animes.map((anime) => ( div:nth-child(2) { + & > div:nth-of-type(2) { margin: 8px 0 4px; } - & > div:nth-child(3) { + & > div:nth-of-type(3) { margin-bottom: 6px; } diff --git a/src/features/animes/routes/List/index.tsx b/src/features/animes/routes/List/index.tsx index daa7756c..de9918a3 100644 --- a/src/features/animes/routes/List/index.tsx +++ b/src/features/animes/routes/List/index.tsx @@ -1,27 +1,21 @@ import { SlidersHorizontal } from "@phosphor-icons/react"; import { useRef } from "react"; import { useNavigate } from "react-router"; -import { v4 as uuid } from "uuid"; import Button from "@/components/Button"; import Empty from "@/components/Error/Empty"; import Head from "@/components/Head"; import Header from "@/components/Layout/Header"; -import Skeleton from "@/components/Skeleton"; import { TabItem } from "@/components/Tabs"; import AnimeCard from "@/features/animes/components/AnimeCard"; import useIntersectionObserver from "@/hooks/useIntersectionObserver"; import { AnimeSort } from "../../api/AnimeApi"; +import GridAnimeCardSkeleton from "../../components/AnimeCardSkeleton/GridAnimeCardSkeleton"; import useFilterAnimes from "../../hooks/useFilterAnimes"; import Filter from "./Filter"; -import { - AnimeListContainer, - Tabs, - Content, - AnimeSkeletonContainer, -} from "./style"; +import { AnimeListContainer, Tabs, Content } from "./style"; const TabItems: TabItem[] = [ { @@ -90,12 +84,8 @@ export default function AnimeList() { /> {(animesQuery.isLoading || animesQuery.isFetching) && - Array.from({ length: 7 }, () => ( - - - - - + Array.from({ length: 7 }, (_, i) => ( + ))} {!animesQuery.isLoading && @@ -107,14 +97,11 @@ export default function AnimeList() { {!animesQuery.isLoading && !animesQuery.isFetching && ( <> {animesQuery.data?.pages.map((item) => ( - <> - navigate(`/animes/${item.id}`)} - /> - {item.id} - + navigate(`/animes/${item.id}`)} + /> ))}
diff --git a/src/features/animes/routes/List/style.ts b/src/features/animes/routes/List/style.ts index 0f8ad89f..9777204f 100644 --- a/src/features/animes/routes/List/style.ts +++ b/src/features/animes/routes/List/style.ts @@ -21,13 +21,7 @@ export const Tabs = styled(BaseTabs)` export const Content = styled.div` width: 100%; display: flex; - flex-direction: column; - gap: 32px; + gap: 32px 0px; + flex-wrap: wrap; padding: 24px 16px; `; - -export const AnimeSkeletonContainer = styled.div` - display: flex; - flex-direction: column; - gap: 8px; -`; diff --git a/src/features/animes/routes/Search/SearchedAnimes/index.tsx b/src/features/animes/routes/Search/SearchedAnimes/index.tsx index 4021b1f2..70f4fdfb 100644 --- a/src/features/animes/routes/Search/SearchedAnimes/index.tsx +++ b/src/features/animes/routes/Search/SearchedAnimes/index.tsx @@ -6,7 +6,7 @@ import Empty from "@/components/Error/Empty"; import AnimeCard, { AnimeCardProps, } from "@/features/animes/components/AnimeCard"; -import AnimeCardSkeleton from "@/features/animes/components/AnimeCardSkeleton"; +import GridAnimeCardSkeleton from "@/features/animes/components/AnimeCardSkeleton/GridAnimeCardSkeleton"; import useIntersectionObserver from "@/hooks/useIntersectionObserver"; import { SearchedAnimesContainer } from "./style"; @@ -41,14 +41,9 @@ export default function SearchedAnimes({ return ( - - - - - - - - + {Array.from({ length: 4 }, (_, i) => ( + + ))} ); @@ -65,15 +60,14 @@ export default function SearchedAnimes({ return ( {animes.map((anime) => ( -
  • - navigate(`/animes/${anime.id}`)} - /> -
  • + navigate(`/animes/${anime.id}`)} + /> ))}
    diff --git a/src/features/animes/routes/Search/SearchedAnimes/style.ts b/src/features/animes/routes/Search/SearchedAnimes/style.ts index 20670be5..81d1cc7f 100644 --- a/src/features/animes/routes/Search/SearchedAnimes/style.ts +++ b/src/features/animes/routes/Search/SearchedAnimes/style.ts @@ -2,6 +2,6 @@ import styled from "@emotion/styled"; export const SearchedAnimesContainer = styled.ul` display: flex; - gap: 32px 8px; + gap: 32px 0; flex-wrap: wrap; `; diff --git a/src/features/animes/routes/Search/SuggestedAnimes/index.tsx b/src/features/animes/routes/Search/SuggestedAnimes/index.tsx index e03dcf10..68208e7a 100644 --- a/src/features/animes/routes/Search/SuggestedAnimes/index.tsx +++ b/src/features/animes/routes/Search/SuggestedAnimes/index.tsx @@ -3,7 +3,7 @@ import { useNavigate } from "react-router"; import AnimeCard, { AnimeCardProps, } from "@/features/animes/components/AnimeCard"; -import AnimeCardSkeleton from "@/features/animes/components/AnimeCardSkeleton"; +import GridAnimeCardSkeleton from "@/features/animes/components/AnimeCardSkeleton/GridAnimeCardSkeleton"; import { SuggestedAnimesContainer } from "./style"; @@ -24,10 +24,9 @@ export default function SuggestedAnimes({ <>

    이런 애니는 어떠세요?

    - - - - + {Array.from({ length: 4 }, (_, i) => ( + + ))} ); @@ -38,15 +37,14 @@ export default function SuggestedAnimes({

    이런 애니는 어떠세요?

    {animes.map((anime) => ( -
  • - navigate(`/animes/${anime.id}`)} - /> -
  • + navigate(`/animes/${anime.id}`)} + /> ))}
    diff --git a/src/features/animes/routes/Search/SuggestedAnimes/style.ts b/src/features/animes/routes/Search/SuggestedAnimes/style.ts index d9497399..e77545f3 100644 --- a/src/features/animes/routes/Search/SuggestedAnimes/style.ts +++ b/src/features/animes/routes/Search/SuggestedAnimes/style.ts @@ -2,6 +2,6 @@ import styled from "@emotion/styled"; export const SuggestedAnimesContainer = styled.ul` display: flex; - gap: 32px 8px; + gap: 32px 0; flex-wrap: wrap; `;