diff --git a/src/assets/images/pattern/pattern_01.svg b/src/assets/images/pattern/pattern_01.svg new file mode 100644 index 0000000..b5f3574 --- /dev/null +++ b/src/assets/images/pattern/pattern_01.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/assets/images/pattern/pattern_02.svg b/src/assets/images/pattern/pattern_02.svg new file mode 100644 index 0000000..6df6079 --- /dev/null +++ b/src/assets/images/pattern/pattern_02.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/pattern/pattern_03.svg b/src/assets/images/pattern/pattern_03.svg new file mode 100644 index 0000000..61d29e5 --- /dev/null +++ b/src/assets/images/pattern/pattern_03.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/pattern/pattern_04.svg b/src/assets/images/pattern/pattern_04.svg new file mode 100644 index 0000000..6b66040 --- /dev/null +++ b/src/assets/images/pattern/pattern_04.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/components/domain/rollingpaper/Card/CreateAtCardList.jsx b/src/components/domain/rollingpaper/Card/CreateAtCardList.jsx index 62c7661..a50aa23 100644 --- a/src/components/domain/rollingpaper/Card/CreateAtCardList.jsx +++ b/src/components/domain/rollingpaper/Card/CreateAtCardList.jsx @@ -1,21 +1,45 @@ import "../../../../styles/GlobalStyles"; import { textStyle } from "../../../../styles/textStyle"; -import styled from "styled-components"; +import styled, { css } from "styled-components"; import { useEffect, useState } from "react"; import recipientsService from "../../../../api/services/recipientsService"; import ArrowButton from "../../../common/Button/ArrowButton"; import { useNavigate } from "react-router-dom"; + +// ★ 패턴 SVG import +import pattern01 from "../../../../assets/images/pattern/pattern_01.svg"; +import pattern02 from "../../../../assets/images/pattern/pattern_02.svg"; +import pattern03 from "../../../../assets/images/pattern/pattern_03.svg"; +import pattern04 from "../../../../assets/images/pattern/pattern_04.svg"; + +// 색상-코드 매핑 +const colorMap = { + beige: "#FFE2AD", + purple: "#ECD9FF", + blue: "#B1E4FF", + green: "#D0F5C3", +}; + +// 색상-패턴 매핑 +const patternMap = { + purple: pattern01, + beige: pattern02, + blue: pattern03, + green: pattern04, +}; + +// ============================ Styled Components ============================ const BoneWrap = styled.div` width: 1160px; position: relative; overflow: visible; height: 260px; @media (max-width: 1199px) { - width: 100%; + width: 100%; } @media (max-width: 767px) { - height: 232px; - } + height: 232px; + } `; const BoneContainer = styled.div` @@ -25,13 +49,14 @@ const BoneContainer = styled.div` width: 100%; overflow: hidden; margin-bottom: 50px; - @media (max-width: 1199px) { - overflow: auto; + + @media (max-width: 1199px) { + overflow: auto; scrollbar-width: none; - -ms-overflow-style: none; - padding:0 20px; + -ms-overflow-style: none; + padding: 0 20px; &::-webkit-scrollbar { - display: none; + display: none; } } `; @@ -43,44 +68,53 @@ const Bone = styled.div` transition: transform 0.5s ease; transform: translateX(${(props) => props.scrollPosition}px); position: relative; - `; const BackgroundWrap = styled.div.withConfig({ shouldForwardProp: (prop) => - !["bgColor", "backgroundImageURL"].includes(prop), + !["isImageCard", "bgColor", "patternUrl", "backgroundImageUrl"].includes(prop), })` - background-color: ${({ bgColor }) => bgColor || "#FFFFFF"}; - background-image: ${({ backgroundImageURL }) => - backgroundImageURL ? `url(${backgroundImageURL})` : "none"}; - background-size: cover; - background-position: center; - background-repeat: no-repeat; width: 275px; height: 260px; padding: 30px 24px; border-radius: 1rem; - color: ${({ backgroundImageURL }) => - backgroundImageURL ? "#ffffff;" : "#000000"}; position: relative; - transition: transform 0.3s ease, box-shadow 0.3s ease, - background-color 0.3s ease; + transition: transform 0.3s ease, box-shadow 0.3s ease, background-color 0.3s ease; + cursor: pointer; + + /* 이미지 카드 or 패턴 카드 분기 */ + ${({ isImageCard, bgColor, patternUrl, backgroundImageUrl }) => + isImageCard + ? css` + /* 배경 이미지 카드 */ + background: url(${backgroundImageUrl}) center/cover no-repeat; + color: #fff; + ` + : css` + /* 배경 색 + 패턴 카드 */ + background-color: ${bgColor}; + background-image: ${patternUrl ? `url("${patternUrl}")` : "none"}; + background-repeat: no-repeat; + background-position: right bottom; + background-size: auto 150px; /* 여기서 패턴 크기 조절 가능 */ + color: #000; + `} &:hover { transform: scale(1); box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.2); - cursor: pointer; } - &:active { transform: scale(0.98); box-shadow: 0px 4px 16px rgba(0, 0, 0, 0.3); } + @media (max-width: 767px) { width: 208px; height: 232px; - } + } `; + const TextDisplay = styled.div` display: flex; height: 36px; @@ -91,6 +125,7 @@ const ToText = styled.div` ${(props) => textStyle(24, 700)(props)} margin-bottom: 0.75rem; height: 2.625rem; + @media (max-width: 767px) { ${(props) => textStyle(18, 700)(props)} } @@ -152,8 +187,8 @@ const ArrowButtonDisplay = styled.div` z-index: 2; transform: matrix(1, 0, 0, 1, 0, 108); transition: opacity 0.3s ease; -} `; + const LeftArrowButtonDisplay = styled(ArrowButtonDisplay)` display: ${({ show }) => (show ? "block" : "none")}; position: absolute; @@ -161,19 +196,21 @@ const LeftArrowButtonDisplay = styled(ArrowButtonDisplay)` top: 30%; transform: translateY(42%); z-index: 1; - @media (max-width: 1199px) { + + @media (max-width: 1199px) { display: none; } `; const RightArrowButtonDisplay = styled(ArrowButtonDisplay)` - display: ${({ show }) => (show ? "block" : "none")}; + display: ${({ show }) => (show ? "block" : "none")}; position: absolute; right: -24px; top: 30%; transform: translateY(42%); z-index: 1; - @media (max-width: 1199px) { + + @media (max-width: 1199px) { display: none; } `; @@ -205,6 +242,7 @@ const TopEmojiItem = styled.div` height: 32px; } `; + const EmojiWrapper = styled.div` position: absolute; bottom: 0px; @@ -226,6 +264,7 @@ const EmojiDiv = styled.div` width: 100%; box-sizing: border-box; `; + const EmojiImage = styled.span` font-size: 20px; `; @@ -235,28 +274,23 @@ const EmojiCount = styled.span` font-weight: bold; `; +// ============================ 메인 컴포넌트 ============================ function CreateAtCardList() { const [selectedRecipients, setSelectedRecipients] = useState([]); const [scrollPosition, setScrollPosition] = useState(0); const cardWidth = 295; const navigate = useNavigate(); - const colorMap = { - beige: "#FFE2AD", - purple: "#ECD9FF", - blue: "#B1E4FF", - green: "#D0F5C3", - }; useEffect(() => { const loadRecipients = async () => { try { - const response = await recipientsService.getRecipients( - "/14-8/recipients/" - ); + // createAt 기준 내림차순 정렬 + const response = await recipientsService.getRecipients("/14-8/recipients/"); const sortedRecipients = response.data.results.sort( (a, b) => b.createAt - a.createAt ); + // 프로필 이미지 상위 3개 추려내기 const updatedRecipients = sortedRecipients.map((recipient) => { const images = recipient.recentMessages?.slice(0, 3) || []; const imageUrls = images @@ -278,6 +312,7 @@ function CreateAtCardList() { loadRecipients(); }, []); + // 좌/우 화살표 버튼 핸들러 const handleNext = () => { const maxScroll = -(selectedRecipients.length * cardWidth - 1160); setScrollPosition((prev) => @@ -290,37 +325,42 @@ function CreateAtCardList() { }; const handleCardClick = (id) => { - // 클릭 시 해당 id로 페이지 이동 navigate(`/post/${id}`); }; - // 좌측 버튼 숨기기 조건 + // 좌우 버튼 노출 조건 const showLeftButton = scrollPosition !== 0; - // 우측 버튼 숨기기 조건 const showRightButton = scrollPosition > -(selectedRecipients.length * cardWidth - 1160); return ( <> - - - + + + - {selectedRecipients.map((recipient, index) => ( - handleCardClick(recipient.id)} // 클릭 시 해당 id로 이동 - > - + {selectedRecipients.map((recipient, index) => { + // backgroundImageURL이 있으면 이미지 카드, 없으면 색+패턴 카드 + const isImageCard = !!recipient.backgroundImageURL; + const bgColor = colorMap[recipient.backgroundColor] || "#FFFFFF"; + const patternUrl = patternMap[recipient.backgroundColor] || null; + + return ( + handleCardClick(recipient.id)} + > To. @@ -329,6 +369,7 @@ function CreateAtCardList() { : recipient.name} + {recipient.profileImages?.slice(0, 3).map((url, i) => ( @@ -339,42 +380,43 @@ function CreateAtCardList() { +{recipient.messageCount - 3} )} + {recipient.messageCount} 명이 작성했어요! -
- - - {/* 이모티콘 상위 3개 표시 */} - - {recipient.topReactions.map(({ emoji, count }, i) => ( - - {emoji} - {count} - - ))} - - + + + + + {recipient.topReactions.map(({ emoji, count }, i) => ( + + {emoji} + {count} + + ))} + + -
-
- ))} +
+ ); + })}
+ - - + +
- ); } -export default CreateAtCardList; \ No newline at end of file +export default CreateAtCardList; diff --git a/src/components/domain/rollingpaper/Card/PopularCardList.jsx b/src/components/domain/rollingpaper/Card/PopularCardList.jsx index 7516c89..1b598de 100644 --- a/src/components/domain/rollingpaper/Card/PopularCardList.jsx +++ b/src/components/domain/rollingpaper/Card/PopularCardList.jsx @@ -1,10 +1,30 @@ import "../../../../styles/GlobalStyles"; import { textStyle } from "../../../../styles/textStyle"; -import styled from "styled-components"; +import styled, { css } from "styled-components"; import { useEffect, useState } from "react"; import recipientsService from "../../../../api/services/recipientsService"; import ArrowButton from "../../../common/Button/ArrowButton"; import { useNavigate } from "react-router-dom"; +import pattern01 from '../../../../assets/images/pattern/pattern_01.svg'; +import pattern02 from '../../../../assets/images/pattern/pattern_02.svg'; +import pattern03 from '../../../../assets/images/pattern/pattern_03.svg'; +import pattern04 from '../../../../assets/images/pattern/pattern_04.svg'; + +// 색상과 패턴 매핑 +const colorMap = { + beige: "#FFE2AD", + purple: "#ECD9FF", + blue: "#B1E4FF", + green: "#D0F5C3", +}; + +const patternMap = { + purple: pattern01, + beige: pattern02, + blue: pattern03, + green: pattern04, +}; +// ------------------- styled-components ------------------- const BoneWrap = styled.div` width: 1160px; position: relative; @@ -12,13 +32,12 @@ const BoneWrap = styled.div` height: 260px; margin-bottom: 50px; @media (max-width: 1199px) { - width: 100% ; - + width: 100%; } @media (max-width: 767px) { - height: 232px; - margin-bottom: 74px; - } + height: 232px; + margin-bottom: 74px; + } `; const BoneContainer = styled.div` @@ -28,13 +47,13 @@ const BoneContainer = styled.div` width: 100%; overflow: hidden; margin-bottom: 50px; - @media (max-width: 1199px) { - overflow: auto; + @media (max-width: 1199px) { + overflow: auto; scrollbar-width: none; - padding:0 20px; - -ms-overflow-style: none; + padding: 0 20px; + -ms-overflow-style: none; &::-webkit-scrollbar { - display: none; + display: none; } } `; @@ -46,44 +65,62 @@ const Bone = styled.div` transition: transform 0.5s ease; transform: translateX(${(props) => props.scrollPosition}px); position: relative; - `; const BackgroundWrap = styled.div.withConfig({ shouldForwardProp: (prop) => - !["bgColor", "backgroundImageURL"].includes(prop), + ![ + "isImageCard", + "bgColor", + "patternUrl", + "backgroundImageUrl", + "color", + ].includes(prop), })` - background-color: ${({ bgColor }) => bgColor || "#FFFFFF"}; - background-image: ${({ backgroundImageURL }) => - backgroundImageURL ? `url(${backgroundImageURL})` : "none"}; - background-size: cover; - background-position: center; - background-repeat: no-repeat; width: 275px; height: 260px; padding: 30px 24px; border-radius: 1rem; - color: ${({ backgroundImageURL }) => - backgroundImageURL ? "#ffffff;" : "#000000"}; position: relative; - transition: transform 0.3s ease, box-shadow 0.3s ease, - background-color 0.3s ease; + transition: transform 0.3s ease, box-shadow 0.3s ease, background-color 0.3s ease; + + /* 카드 클릭 시 포인터 */ + cursor: pointer; + + /* isImageCard에 따라 스타일 분기 */ + ${({ isImageCard, bgColor, patternUrl, backgroundImageUrl }) => + isImageCard + ? css` + /* 배경 이미지 카드 */ + background: url(${backgroundImageUrl}) center/cover no-repeat; + color: #fff; /* 이미지가 들어올 땐 텍스트 색상 지정 (예시) */ + ` + : css` + /* 배경색 + 패턴 카드 */ + background-color: ${bgColor}; + background-image: ${patternUrl ? `url("${patternUrl}")` : "none"}; + background-repeat: no-repeat; + background-position: right bottom; + background-size: auto 140px; + color: #000; /* 배경색일 때 텍스트 색상 지정 (예시) */ + `} &:hover { transform: scale(1); box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.2); - cursor: pointer; } &:active { transform: scale(0.98); box-shadow: 0px 4px 16px rgba(0, 0, 0, 0.3); } + @media (max-width: 767px) { width: 208px; height: 232px; - } + } `; + const TextDisplay = styled.div` display: flex; height: 36px; @@ -155,8 +192,8 @@ const ArrowButtonDisplay = styled.div` z-index: 2; transform: matrix(1, 0, 0, 1, 0, 108); transition: opacity 0.3s ease; -} `; + const LeftArrowButtonDisplay = styled(ArrowButtonDisplay)` display: ${({ show }) => (show ? "block" : "none")}; position: absolute; @@ -164,19 +201,19 @@ const LeftArrowButtonDisplay = styled(ArrowButtonDisplay)` top: 30%; transform: translateY(42%); z-index: 1; - @media (max-width: 1199px) { + @media (max-width: 1199px) { display: none; } `; const RightArrowButtonDisplay = styled(ArrowButtonDisplay)` - display: ${({ show }) => (show ? "block" : "none")}; + display: ${({ show }) => (show ? "block" : "none")}; position: absolute; right: -24px; top: 30%; transform: translateY(42%); z-index: 1; - @media (max-width: 1199px) { + @media (max-width: 1199px) { display: none; } `; @@ -208,6 +245,7 @@ const TopEmojiItem = styled.div` height: 32px; } `; + const EmojiWrapper = styled.div` position: absolute; bottom: 0px; @@ -229,6 +267,7 @@ const EmojiDiv = styled.div` width: 100%; box-sizing: border-box; `; + const EmojiImage = styled.span` font-size: 20px; `; @@ -238,24 +277,18 @@ const EmojiCount = styled.span` font-weight: bold; `; +// ------------------- 메인 컴포넌트 ------------------- function PopularCardList() { const [selectedRecipients, setSelectedRecipients] = useState([]); const [scrollPosition, setScrollPosition] = useState(0); const cardWidth = 295; const navigate = useNavigate(); - const colorMap = { - beige: "#FFE2AD", - purple: "#ECD9FF", - blue: "#B1E4FF", - green: "#D0F5C3", - }; useEffect(() => { const loadRecipients = async () => { try { - const response = await recipientsService.getRecipients( - "/14-8/recipients/" - ); + // recipientsService.getRecipients("/14-8/recipients/") 호출 예시 + const response = await recipientsService.getRecipients("/14-8/recipients/"); const sortedRecipients = response.data.results.sort( (a, b) => b.messageCount - a.messageCount ); @@ -305,25 +338,32 @@ function PopularCardList() { return ( <> - - - + + + - {selectedRecipients.map((recipient, index) => ( - handleCardClick(recipient.id)} // 클릭 시 해당 id로 이동 - > - + {selectedRecipients.map((recipient, index) => { + // 이미지 URL 유무에 따라 이미지 카드 / 색상+패턴 카드 결정 + const isImageCard = !!recipient.backgroundImageURL; + const bgColor = colorMap[recipient.backgroundColor] || "#FFFFFF"; + const patternUrl = patternMap[recipient.backgroundColor] || null; + + return ( + handleCardClick(recipient.id)} + > To. @@ -332,6 +372,7 @@ function PopularCardList() { : recipient.name} + {recipient.profileImages?.slice(0, 3).map((url, i) => ( @@ -342,40 +383,41 @@ function PopularCardList() { +{recipient.messageCount - 3} )} + {recipient.messageCount} 명이 작성했어요! -
- - - {/* 이모티콘 상위 3개 표시 */} - - {recipient.topReactions.map(({ emoji, count }, i) => ( - - {emoji} - {count} - - ))} - - + + {/* 이모지 정보 */} + + + + {recipient.topReactions.map(({ emoji, count }, i) => ( + + {emoji} + {count} + + ))} + + -
-
- ))} +
+ ); + })}
+ - - + +
- ); }