From 807fc1b06cf71b192bf323a7a47ec2b1f51fe43d Mon Sep 17 00:00:00 2001 From: joojjang Date: Mon, 18 Nov 2024 03:19:53 +0900 Subject: [PATCH 01/16] =?UTF-8?q?refactor(Categories):=20=ED=8C=A8?= =?UTF-8?q?=EB=94=A9,=20=EA=B0=AD=20=EB=93=B1=20=EB=B0=94=EB=A1=9C?= =?UTF-8?q?=EC=9E=A1=EA=B3=A0=20=EC=95=BD=EA=B0=84=EC=9D=98=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants/categories.ts | 2 +- .../Categories/components/CategoryItem/index.tsx | 16 ++++++---------- src/pages/Categories/index.tsx | 4 ++-- src/styles/globalStyles.ts | 6 +++--- 4 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/constants/categories.ts b/src/constants/categories.ts index 3d50e37..ab00852 100644 --- a/src/constants/categories.ts +++ b/src/constants/categories.ts @@ -8,7 +8,7 @@ export const CATEGORY_LIST: Category[] = [ { id: 1, src: 'https://images.unsplash.com/photo-1580136608079-72029d0de130?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8NHx8JUVCJThGJTk5JUVDJTk2JTkxJUVBJUI3JUI4JUVCJUE2JUJDfGVufDB8fDB8fHww', - des: '동양화/ 한국화', + des: '동양화/한국화', }, { id: 2, diff --git a/src/pages/Categories/components/CategoryItem/index.tsx b/src/pages/Categories/components/CategoryItem/index.tsx index 07172d9..2f16c72 100644 --- a/src/pages/Categories/components/CategoryItem/index.tsx +++ b/src/pages/Categories/components/CategoryItem/index.tsx @@ -1,17 +1,14 @@ -import { Image } from '@chakra-ui/react'; import styled from '@emotion/styled'; interface CategoryItemProps { - des: string; src: string; + des: string; } -const CategoryItem = ({ des, src }: CategoryItemProps) => { - des = des.replace('/', '/\n'); - +const CategoryItem = ({ src, des }: CategoryItemProps) => { return ( - + {des} ); @@ -19,17 +16,16 @@ const CategoryItem = ({ des, src }: CategoryItemProps) => { export default CategoryItem; -const Wrapper = styled.div` +const Wrapper = styled.button` display: flex; flex-direction: column; align-items: center; - cursor: pointer; gap: 8px; `; -const RoundImage = styled(Image)` +const CategoryThumbnail = styled.img` aspect-ratio: 1/1; - width: 80%; + width: 64px; border-radius: 100%; object-fit: cover; background-color: var(--color-gray-lt); diff --git a/src/pages/Categories/index.tsx b/src/pages/Categories/index.tsx index 83c3737..cd50ee3 100644 --- a/src/pages/Categories/index.tsx +++ b/src/pages/Categories/index.tsx @@ -6,7 +6,7 @@ import FakeSearchBar from '@/components/common/FakeSearchBar'; import SearchModal from '@/components/common/SearchModal'; import { CATEGORY_LIST } from '@/constants/categories'; import * as G from '@/styles/globalStyles'; -import Category from './components/CategoryItem'; +import CategoryItem from './components/CategoryItem'; const Categories = () => { const [isModalOpen, setIsModalOpen] = useState(false); @@ -21,7 +21,7 @@ const Categories = () => { {isModalOpen && setIsModalOpen(false)} />} {CATEGORY_LIST.map((category) => ( - + ))} diff --git a/src/styles/globalStyles.ts b/src/styles/globalStyles.ts index 889c206..96df705 100644 --- a/src/styles/globalStyles.ts +++ b/src/styles/globalStyles.ts @@ -13,8 +13,8 @@ export const HorizontalLine = styled.hr` export const Grid = styled.div<{ col: number; justifyItems?: string }>` display: grid; - grid-template-columns: ${({ col }) => (col === 2 ? 'repeat(2, 1fr)' : 'repeat(4, 1fr)')}; + grid-template-columns: ${({ col }) => `repeat(${col}, 1fr)`}; justify-items: ${({ justifyItems }) => justifyItems || 'center'}; - padding: ${({ col }) => (col === 2 ? '16px' : '24px 16px 32px 16px;')}; - gap: ${({ col }) => (col === 2 ? '8px' : '40px 12px')}; + padding: ${({ col }) => (col === 2 ? '16px' : '16px 16px 32px 16px;')}; + gap: ${({ col }) => (col === 2 ? '8px' : '24px')}; `; From 7ac77221c8ead9e847ea22a91c960c2b6ed0a0e7 Mon Sep 17 00:00:00 2001 From: joojjang Date: Mon, 18 Nov 2024 03:48:22 +0900 Subject: [PATCH 02/16] =?UTF-8?q?refactor(Categories):=20=ED=81=90?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=85=98=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81=20=EC=99=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - map 함수로 확장성 개선 - 카테고리 이미지 div로 한번 감싸기 --- src/constants/categories.ts | 43 +++++++++-------- .../components/CategoryItem/index.tsx | 23 +++++---- .../components/CurationItem/index.tsx | 39 +++++++++++++++ src/pages/Categories/index.tsx | 47 +++++-------------- 4 files changed, 88 insertions(+), 64 deletions(-) create mode 100644 src/pages/Categories/components/CurationItem/index.tsx diff --git a/src/constants/categories.ts b/src/constants/categories.ts index ab00852..ec948c1 100644 --- a/src/constants/categories.ts +++ b/src/constants/categories.ts @@ -1,53 +1,56 @@ type Category = { - id: number; src: string; - des: string; + title: string; }; export const CATEGORY_LIST: Category[] = [ { - id: 1, src: 'https://images.unsplash.com/photo-1580136608079-72029d0de130?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8NHx8JUVCJThGJTk5JUVDJTk2JTkxJUVBJUI3JUI4JUVCJUE2JUJDfGVufDB8fDB8fHww', - des: '동양화/한국화', + title: '동양화/한국화', }, { - id: 2, src: 'https://images.unsplash.com/photo-1579783928621-7a13d66a62d1?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8Mnx8JUVDJTg0JTlDJUVDJTk2JTkxJUVEJTk5JTk0fGVufDB8fDB8fHww', - des: '서양화', + title: '서양화', }, { - id: 3, src: 'https://plus.unsplash.com/premium_photo-1672287578309-2a2115000688?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MXx8JUVDJUExJUIwJUVBJUIwJTgxfGVufDB8fDB8fHww', - des: '조각', + title: '조각', }, { - id: 4, src: 'https://images.unsplash.com/photo-1590605105526-5c08f63f89aa?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8OHx8JUVCJThGJTg0JUVDJTk4JTg4fGVufDB8fDB8fHww', - des: '도예/공예', + title: '도예/공예', }, { - id: 5, src: 'https://images.unsplash.com/photo-1682159672286-40790338349b?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MXx8JUVCJTg5JUI0JUVCJUFGJUI4JUVCJTk0JTk0JUVDJTk2JUI0fGVufDB8fDB8fHww', - des: '뉴미디어', + title: '뉴미디어', }, { - id: 6, src: 'https://plus.unsplash.com/premium_photo-1663100678842-d89cb7b084ee?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MTd8fCVFQiU5NCU5NCVFQyU5RSU5MCVFQyU5RCVCOHxlbnwwfHwwfHx8MA%3D%3D', - des: '디자인', + title: '디자인', }, { - id: 7, src: 'https://plus.unsplash.com/premium_photo-1723075214781-ea091374d81c?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MXx8JUVEJThDJTkwJUVEJTk5JTk0fGVufDB8fDB8fHww', - des: '드로잉/판화', + title: '드로잉/판화', }, { - id: 8, src: 'https://images.unsplash.com/photo-1506434304575-afbb92660c28?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8Nnx8JUVDJTgyJUFDJUVDJUE3JTg0JUVDJTgyJUFDfGVufDB8fDB8fHww', - des: '사진', + title: '사진', }, { - id: 9, src: 'https://images.unsplash.com/photo-1546638008-efbe0b62c730?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8OHx8JUVCJThGJTk5JUVDJTk2JTkxJUVBJUI3JUI4JUVCJUE2JUJDfGVufDB8fDB8fHww', - des: '서예/캘리그라피', + title: '서예/캘리그라피', + }, +]; + +type Curation = { title: string; des: string }; + +export const CURATION_LIST: Curation[] = [ + { + title: '매거진', + des: '숨겨진 무한의 가치를 발견하고 싶다면', + }, + { + title: '아티스트 그라운드', + des: '내 취향대로 작가 골라보기', }, ]; diff --git a/src/pages/Categories/components/CategoryItem/index.tsx b/src/pages/Categories/components/CategoryItem/index.tsx index 2f16c72..06a65bc 100644 --- a/src/pages/Categories/components/CategoryItem/index.tsx +++ b/src/pages/Categories/components/CategoryItem/index.tsx @@ -1,15 +1,15 @@ import styled from '@emotion/styled'; interface CategoryItemProps { + title: string; src: string; - des: string; } -const CategoryItem = ({ src, des }: CategoryItemProps) => { +const CategoryItem = ({ title, src }: CategoryItemProps) => { return ( - - {des} + {src && Category thumbnail} + {title} ); }; @@ -23,15 +23,20 @@ const Wrapper = styled.button` gap: 8px; `; -const CategoryThumbnail = styled.img` - aspect-ratio: 1/1; +const CategoryThumbnail = styled.div` width: 64px; - border-radius: 100%; - object-fit: cover; + aspect-ratio: 1 / 1; + border-radius: 50px; + overflow: hidden; background-color: var(--color-gray-lt); + + img { + object-fit: cover; + width: 64px; + } `; -const DesWrapper = styled.p` +const Title = styled.p` font-size: var(--font-size-sm); display: inline; white-space: pre-wrap; diff --git a/src/pages/Categories/components/CurationItem/index.tsx b/src/pages/Categories/components/CurationItem/index.tsx new file mode 100644 index 0000000..3b76523 --- /dev/null +++ b/src/pages/Categories/components/CurationItem/index.tsx @@ -0,0 +1,39 @@ +import styled from '@emotion/styled'; + +interface CurationItemProps { + title: string; + des: string; +} + +const CurationItem = ({ title, des }: CurationItemProps) => { + return ( + + {title} + {des} + + ); +}; + +export default CurationItem; + +const Wrapper = styled.li` + display: flex; + flex-direction: row; + align-items: center; + width: 100%; + padding: 16px; + min-height: 54px; + gap: 8px; + cursor: pointer; +`; + +const Title = styled.span` + font-size: var(--font-size-md); + font-weight: 600; + line-height: 1.2; +`; + +const Des = styled.span` + font-size: var(--font-size-sm); + line-height: 1.2; +`; diff --git a/src/pages/Categories/index.tsx b/src/pages/Categories/index.tsx index cd50ee3..be46ad4 100644 --- a/src/pages/Categories/index.tsx +++ b/src/pages/Categories/index.tsx @@ -1,12 +1,12 @@ -import { Text } from '@chakra-ui/react'; import styled from '@emotion/styled'; import { useState } from 'react'; import FakeSearchBar from '@/components/common/FakeSearchBar'; import SearchModal from '@/components/common/SearchModal'; -import { CATEGORY_LIST } from '@/constants/categories'; +import { CATEGORY_LIST, CURATION_LIST } from '@/constants/categories'; import * as G from '@/styles/globalStyles'; import CategoryItem from './components/CategoryItem'; +import CurationItem from './components/CurationItem'; const Categories = () => { const [isModalOpen, setIsModalOpen] = useState(false); @@ -21,20 +21,14 @@ const Categories = () => { {isModalOpen && setIsModalOpen(false)} />} {CATEGORY_LIST.map((category) => ( - + ))} - + - - 매거진 - 숨겨진 무한의 가치를 발견하고 싶다면 - - - - 아티스트 그라운드 - 내 취향대로 작가 골라보기 - + {CURATION_LIST.map((curation) => ( + + ))} ); @@ -46,29 +40,12 @@ const Wrapper = styled.div` width: 100%; `; -const CurationItem = styled.div` - display: flex; - flex-direction: row; - align-items: center; - width: 100%; - padding: 16px; - min-height: 54px; - gap: 8px; -`; - -const Title = styled(Text)` - font-size: var(--font-size-md); - font-weight: 600; - line-height: 1.2; -`; - -const Des = styled(Text)` - font-size: var(--font-size-sm); - line-height: 1.2; -`; - -const CurationWrapper = styled.div` +const CurationWrapper = styled.ul` height: auto; width: 100%; margin-bottom: 54px; + + &:not(:last-child) { + border-bottom: 0.5px solid var(--color-gray-lt); + } `; From 4db570ca51b6bf7cb57caf2532151a3b3b7f9908 Mon Sep 17 00:00:00 2001 From: joojjang Date: Mon, 18 Nov 2024 03:48:55 +0900 Subject: [PATCH 03/16] =?UTF-8?q?fix(ProfileImage):=20html=20=ED=83=9C?= =?UTF-8?q?=EA=B7=B8=20=EC=84=A0=ED=83=9D=EC=9E=90=20=EA=B3=A0=EC=B9=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/common/ProfileImage/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/common/ProfileImage/index.tsx b/src/components/common/ProfileImage/index.tsx index 55fec96..7ee22b7 100644 --- a/src/components/common/ProfileImage/index.tsx +++ b/src/components/common/ProfileImage/index.tsx @@ -24,7 +24,7 @@ const StyledProfileImage = styled.div<{ width: number }>` border: 1px solid var(--color-gray-md); background-color: var(--color-gray-lt); - .img { + img { object-fit: cover; } `; From 7c33d9318deece5ac81ad9e59792b03677014c7c Mon Sep 17 00:00:00 2001 From: joojjang Date: Mon, 18 Nov 2024 03:54:51 +0900 Subject: [PATCH 04/16] =?UTF-8?q?fix(CurationItem):=20=EC=84=A0=ED=83=9D?= =?UTF-8?q?=EC=9E=90=20=EC=9E=98=EB=AA=BB=20=EB=8B=A8=20=EA=B3=B3=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/Categories/components/CurationItem/index.tsx | 4 ++++ src/pages/Categories/index.tsx | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/Categories/components/CurationItem/index.tsx b/src/pages/Categories/components/CurationItem/index.tsx index 3b76523..4be4021 100644 --- a/src/pages/Categories/components/CurationItem/index.tsx +++ b/src/pages/Categories/components/CurationItem/index.tsx @@ -25,6 +25,10 @@ const Wrapper = styled.li` min-height: 54px; gap: 8px; cursor: pointer; + + &:not(:last-child) { + border-bottom: 1px solid var(--color-gray-lt); + } `; const Title = styled.span` diff --git a/src/pages/Categories/index.tsx b/src/pages/Categories/index.tsx index be46ad4..6de1119 100644 --- a/src/pages/Categories/index.tsx +++ b/src/pages/Categories/index.tsx @@ -44,8 +44,4 @@ const CurationWrapper = styled.ul` height: auto; width: 100%; margin-bottom: 54px; - - &:not(:last-child) { - border-bottom: 0.5px solid var(--color-gray-lt); - } `; From eb07393c64e1f337a1945ae06818a0eece7268b8 Mon Sep 17 00:00:00 2001 From: joojjang Date: Mon, 18 Nov 2024 04:10:26 +0900 Subject: [PATCH 05/16] =?UTF-8?q?refactor(Categories):=20=EC=B9=B4?= =?UTF-8?q?=ED=85=8C=EA=B3=A0=EB=A6=AC=20=EA=B7=B8=EB=A6=AC=EB=93=9C=20htm?= =?UTF-8?q?l=20=ED=83=9C=EA=B7=B8=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20?= =?UTF-8?q?=EB=B0=98=EC=9D=91=ED=98=95=20UI=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/CategoryItem/index.tsx | 9 +++++---- src/pages/Categories/index.tsx | 19 +++++++++++++++++-- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/pages/Categories/components/CategoryItem/index.tsx b/src/pages/Categories/components/CategoryItem/index.tsx index 06a65bc..afcc9d9 100644 --- a/src/pages/Categories/components/CategoryItem/index.tsx +++ b/src/pages/Categories/components/CategoryItem/index.tsx @@ -16,23 +16,24 @@ const CategoryItem = ({ title, src }: CategoryItemProps) => { export default CategoryItem; -const Wrapper = styled.button` +const Wrapper = styled.li` display: flex; flex-direction: column; align-items: center; gap: 8px; + cursor: pointer; `; const CategoryThumbnail = styled.div` - width: 64px; + width: 100%; aspect-ratio: 1 / 1; - border-radius: 50px; + border-radius: 100%; overflow: hidden; background-color: var(--color-gray-lt); img { object-fit: cover; - width: 64px; + width: 100%; } `; diff --git a/src/pages/Categories/index.tsx b/src/pages/Categories/index.tsx index 6de1119..d6098e2 100644 --- a/src/pages/Categories/index.tsx +++ b/src/pages/Categories/index.tsx @@ -19,11 +19,11 @@ const Categories = () => { {isModalOpen && setIsModalOpen(false)} />} - + {CATEGORY_LIST.map((category) => ( ))} - + {CURATION_LIST.map((curation) => ( @@ -40,6 +40,21 @@ const Wrapper = styled.div` width: 100%; `; +const CategoryGrid = styled.ul` + display: grid; + grid-template-columns: repeat(4, 1fr); + justify-items: center; + padding: 16px 16px 32px 16px; + gap: 24px; + + @media (min-width: 480px) { + grid-template-columns: repeat(5, 1fr); + } + @media (min-width: 600px) { + grid-template-columns: repeat(6, 1fr); + } +`; + const CurationWrapper = styled.ul` height: auto; width: 100%; From ef2382dc44733df0bcec4b1155138865488476ec Mon Sep 17 00:00:00 2001 From: joojjang Date: Mon, 18 Nov 2024 04:13:17 +0900 Subject: [PATCH 06/16] =?UTF-8?q?fix(CategoryItem,=20ProfileImage):=20?= =?UTF-8?q?=EB=8F=99=EA=B7=B8=EB=9D=BC=EB=AF=B8=20=EC=9D=B4=EB=AF=B8?= =?UTF-8?q?=EC=A7=80=20=EB=82=A8=EB=8A=94=20=EB=B6=80=EB=B6=84=20=EC=97=86?= =?UTF-8?q?=EA=B2=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/common/ProfileImage/index.tsx | 2 ++ src/pages/Categories/components/CategoryItem/index.tsx | 1 + 2 files changed, 3 insertions(+) diff --git a/src/components/common/ProfileImage/index.tsx b/src/components/common/ProfileImage/index.tsx index 7ee22b7..6b02ecb 100644 --- a/src/components/common/ProfileImage/index.tsx +++ b/src/components/common/ProfileImage/index.tsx @@ -26,5 +26,7 @@ const StyledProfileImage = styled.div<{ width: number }>` img { object-fit: cover; + width: 100%; + height: 100%; } `; diff --git a/src/pages/Categories/components/CategoryItem/index.tsx b/src/pages/Categories/components/CategoryItem/index.tsx index afcc9d9..29e80c4 100644 --- a/src/pages/Categories/components/CategoryItem/index.tsx +++ b/src/pages/Categories/components/CategoryItem/index.tsx @@ -34,6 +34,7 @@ const CategoryThumbnail = styled.div` img { object-fit: cover; width: 100%; + height: 100%; } `; From ebce52aa2bf1503addb63209021c28b591d8133a Mon Sep 17 00:00:00 2001 From: joojjang Date: Mon, 18 Nov 2024 04:27:04 +0900 Subject: [PATCH 07/16] =?UTF-8?q?chore(search):=20ad=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=ED=8C=8C=EC=9D=BC=EB=AA=85=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/common/SearchModal/{SearchAd.tsx => Ad.tsx} | 0 src/components/common/SearchModal/index.tsx | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/components/common/SearchModal/{SearchAd.tsx => Ad.tsx} (100%) diff --git a/src/components/common/SearchModal/SearchAd.tsx b/src/components/common/SearchModal/Ad.tsx similarity index 100% rename from src/components/common/SearchModal/SearchAd.tsx rename to src/components/common/SearchModal/Ad.tsx diff --git a/src/components/common/SearchModal/index.tsx b/src/components/common/SearchModal/index.tsx index 3fea75d..e278d15 100644 --- a/src/components/common/SearchModal/index.tsx +++ b/src/components/common/SearchModal/index.tsx @@ -3,9 +3,9 @@ import styled from '@emotion/styled'; import SearchBar from '@/components/layouts/SearchBar'; import { Z_INDEX } from '@/styles/constants'; import * as G from '@/styles/globalStyles'; +import Ad from './Ad'; import PopularSearch from './PopularSearch'; import RecentSearch from './RecentSearch'; -import Ad from './SearchAd'; interface SearchModalProps { modalClose: () => void; From ed746c1cf6f3e662bf2d1dea7dee56f488b338e3 Mon Sep 17 00:00:00 2001 From: joojjang Date: Mon, 18 Nov 2024 04:36:03 +0900 Subject: [PATCH 08/16] =?UTF-8?q?refactor(serach):=20=EA=B2=80=EC=83=89=20?= =?UTF-8?q?=EC=84=B9=EC=85=98=20=ED=99=95=EC=9E=A5=EC=84=B1=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - map 함수로 처리 --- src/components/common/SearchModal/index.tsx | 27 +++++++++++++-------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/components/common/SearchModal/index.tsx b/src/components/common/SearchModal/index.tsx index e278d15..19822e5 100644 --- a/src/components/common/SearchModal/index.tsx +++ b/src/components/common/SearchModal/index.tsx @@ -2,7 +2,6 @@ import styled from '@emotion/styled'; import SearchBar from '@/components/layouts/SearchBar'; import { Z_INDEX } from '@/styles/constants'; -import * as G from '@/styles/globalStyles'; import Ad from './Ad'; import PopularSearch from './PopularSearch'; import RecentSearch from './RecentSearch'; @@ -12,16 +11,16 @@ interface SearchModalProps { } const SearchModal = ({ modalClose }: SearchModalProps) => { + const searchSectionList: React.ReactNode[] = [, , ]; + return ( - - - - - - - + + {searchSectionList.map((section, index) => ( +
{section}
+ ))} +
); }; @@ -40,8 +39,16 @@ const ModalWrapper = styled.div` z-index: ${Z_INDEX.Modal}; `; -const SearchWrapper = styled.div` - flex: 1; +const SectionsWrapper = styled.div` + width: 100%; display: flex; flex-direction: column; + + & > * { + border-bottom: 1px solid var(--color-gray-lt); + } + + & > *:last-child { + border-bottom: none; + } `; From 9bf7b81ef40398f29ae14e1a2672f2d07b9787dd Mon Sep 17 00:00:00 2001 From: joojjang Date: Mon, 18 Nov 2024 06:09:22 +0900 Subject: [PATCH 09/16] =?UTF-8?q?refactor(search):=20=EA=B2=80=EC=83=89=20?= =?UTF-8?q?=EB=AA=A8=EB=8B=AC=20=EC=AA=BD=20=EB=A6=AC=ED=8C=A9=ED=86=A0?= =?UTF-8?q?=EB=A7=81=20=EB=94=94=EC=9E=90=EC=9D=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/assets/icons/cancel-filled-gray.svg | 8 -- src/assets/icons/cancel-filled.svg | 4 +- src/components/common/Chip/index.tsx | 41 +++++----- .../common/PopularSearchItem/index.tsx | 6 +- src/components/common/SearchModal/Ad.tsx | 80 +++++++------------ .../common/SearchModal/PopularSearch.tsx | 68 +++++----------- .../common/SearchModal/RecentSearch.tsx | 68 ++++++---------- src/components/common/SearchModal/index.tsx | 14 ++-- src/components/common/SearchModal/styles.ts | 21 +++++ src/components/layouts/SearchBar/index.tsx | 5 +- src/constants/search.ts | 44 ++++------ 11 files changed, 143 insertions(+), 216 deletions(-) delete mode 100644 src/assets/icons/cancel-filled-gray.svg create mode 100644 src/components/common/SearchModal/styles.ts diff --git a/src/assets/icons/cancel-filled-gray.svg b/src/assets/icons/cancel-filled-gray.svg deleted file mode 100644 index bc49a1f..0000000 --- a/src/assets/icons/cancel-filled-gray.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/src/assets/icons/cancel-filled.svg b/src/assets/icons/cancel-filled.svg index c285351..9b29591 100644 --- a/src/assets/icons/cancel-filled.svg +++ b/src/assets/icons/cancel-filled.svg @@ -1,8 +1,8 @@ - + - + diff --git a/src/components/common/Chip/index.tsx b/src/components/common/Chip/index.tsx index 4f9c1a9..2597466 100644 --- a/src/components/common/Chip/index.tsx +++ b/src/components/common/Chip/index.tsx @@ -1,21 +1,23 @@ import styled from '@emotion/styled'; - -import CancelDefault from '@/assets/icons/cancel-default.svg?react'; import { useNavigate } from 'react-router-dom'; +import CancelIcon from '@/assets/icons/cancel-filled.svg?react'; +import { RouterPath } from '@/routes/path'; + interface ChipProps { tag: string; - onClick: () => void; + onDeleteClick: () => void; } -const Chip = ({ tag, onClick }: ChipProps) => { +const Chip = ({ tag, onDeleteClick }: ChipProps) => { const navigate = useNavigate(); + return ( - - - - + navigate(`/${RouterPath.results}?query=${tag}`)}>{tag} + + + ); }; @@ -26,28 +28,21 @@ const Wrapper = styled.div` border: 1px solid var(--color-gray-md); border-radius: var(--border-radius); font-size: var(--font-size-sm); - padding: 0.6rem; - gap: 0.6rem; - height: 2.3rem; + background-color: var(--color-white); + padding: 4px 8px; + gap: 6px; display: inline-flex; align-items: center; - justify-content: space-between; `; -const CancelIconButton = styled.button` - cursor: pointer; - width: 15px; - height: 15px; - background-color: var(--color-white); +const DeleteButton = styled.button` + width: 14px; + height: 14px; transition: fill 0.3s ease; - & svg { + svg { width: 100%; height: 100%; - } - - &:hover { - stroke: var(--color-black); - fill: var(--color-black); + color: var(--color-black); } `; diff --git a/src/components/common/PopularSearchItem/index.tsx b/src/components/common/PopularSearchItem/index.tsx index 958ae8a..1163f79 100644 --- a/src/components/common/PopularSearchItem/index.tsx +++ b/src/components/common/PopularSearchItem/index.tsx @@ -2,15 +2,15 @@ import styled from '@emotion/styled'; import { Text } from '@chakra-ui/react'; interface PopularSearchItemProps { - text: string; + name: string; rank: number; } -const PopularSearchItem = ({ text, rank }: PopularSearchItemProps) => { +const PopularSearchItem = ({ name, rank }: PopularSearchItemProps) => { return ( {rank} - #{text} + {name} ); }; diff --git a/src/components/common/SearchModal/Ad.tsx b/src/components/common/SearchModal/Ad.tsx index 8d686d8..8ee0996 100644 --- a/src/components/common/SearchModal/Ad.tsx +++ b/src/components/common/SearchModal/Ad.tsx @@ -1,71 +1,53 @@ -import { Image } from '@chakra-ui/react'; import styled from '@emotion/styled'; import { AD_LIST } from '@/constants/search'; -import * as G from '@/styles/globalStyles'; +import * as S from './styles'; const SearchAd = () => { return ( - - -
- 요즘 뜨는 작품 -
- 광고 -
- - {AD_LIST.map((ad) => ( - + + + 요즘 뜨는 작품 + + 광고 + + {AD_LIST.map((ad, index) => ( + + Ad image + ))} - -
+ + ); }; export default SearchAd; -const Wrapper = styled.div` +const AdWrapper = styled.div` display: flex; - flex-direction: column; - padding: 16px; + gap: 12px; `; -const TitleWrapper = styled.div` - width: 100%; - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: center; - margin-bottom: 16px; - color: var(--color-black, #020715); - font-size: var(--font-size-md); - font-weight: 700; - line-height: normal; -`; - -const RedText = styled.span` - color: var(--color-red); +const AdTag = styled.p` + background: var(--color-gray-md); + border-radius: 50px; + padding: 3px 7px; + font-size: var(--font-size-xs); + color: var(--color-white); + font-weight: 500; + position: absolute; + top: 16px; + right: 16px; `; -const AdImage = styled(Image)` +const AdImage = styled.div` width: 100%; - height: 100%; aspect-ratio: 1 / 1; - max-width: 200px; - max-height: 200px; background-color: var(--color-gray-lt); -`; -const Tab = styled.p` - background: var(--color-gray-lt); - border-radius: 50px; - padding: 3px 7px; - justify-content: center; - align-items: center; - gap: 10px; - width: 36px; - height: 20px; - font-size: var(--font-size-xs); - color: var(--white, #fff); - font-weight: 700; + img { + object-fit: cover; + width: 100%; + height: 100%; + } `; diff --git a/src/components/common/SearchModal/PopularSearch.tsx b/src/components/common/SearchModal/PopularSearch.tsx index 0eacad9..1a6bf82 100644 --- a/src/components/common/SearchModal/PopularSearch.tsx +++ b/src/components/common/SearchModal/PopularSearch.tsx @@ -1,63 +1,31 @@ -import { Text } from '@chakra-ui/react'; import styled from '@emotion/styled'; import { POPULAR_SEARCH_LIST } from '@/constants/search'; -import * as G from '@/styles/globalStyles'; import PopularSearchItem from '../PopularSearchItem'; +import * as S from './styles'; const PopularSearch = () => { - const midPoint = Math.ceil(POPULAR_SEARCH_LIST.length / 2); - return ( - - - - 인기 검색어 - - - - - {POPULAR_SEARCH_LIST.slice(0, midPoint).map((item, index) => ( - - ))} - - - {POPULAR_SEARCH_LIST.slice(midPoint).map((item, index) => ( - - ))} - - - + + + 인기 검색어 + + + {POPULAR_SEARCH_LIST.map((item, index) => ( + + ))} + + ); }; export default PopularSearch; -const Wrapper = styled.div` - display: flex; - flex-direction: column; - padding: 16px; -`; - -const TitleWrapper = styled.div` - display: flex; - flex-direction: row; - justify-content: space-between; -`; - -const TitleText = styled(Text)` - color: var(--color-black, #020715); - font-size: var(--font-size-md); - font-weight: 700; - line-height: normal; -`; - -const Column = styled.div` - display: flex; - flex-direction: column; - gap: 5px; -`; - -const RedText = styled.span` - color: var(--color-red); +const PopularSearchGrid = styled.div` + display: grid; + grid-auto-flow: column; // 세로 방향 배치 + grid-template-rows: repeat(5, 1fr); + justify-items: flex-start; + gap: 8px 12px; + font-size: var(--font-size-sm); `; diff --git a/src/components/common/SearchModal/RecentSearch.tsx b/src/components/common/SearchModal/RecentSearch.tsx index c6a12a7..048b05e 100644 --- a/src/components/common/SearchModal/RecentSearch.tsx +++ b/src/components/common/SearchModal/RecentSearch.tsx @@ -1,80 +1,58 @@ -import { Text } from '@chakra-ui/react'; import styled from '@emotion/styled'; import { useEffect, useState } from 'react'; -import Chip from '../Chip'; -export const SEARCH_ARRAY_KEY = 'searchArray'; +import { SEARCH_ARRAY_KEY } from '@/constants/search'; +import Chip from '../Chip'; +import * as S from './styles'; const RecentSearch = () => { const [searchArray, setSearchArray] = useState>([]); - const allDelete = () => { + const deleteAll = () => { setSearchArray([]); localStorage.removeItem(SEARCH_ARRAY_KEY); }; - const handleStoredData = (key: string) => { + const handleStoredKey = (key: string) => { const updatedArray = searchArray.filter((item) => item.key !== key); setSearchArray(updatedArray); localStorage.setItem(SEARCH_ARRAY_KEY, JSON.stringify(updatedArray)); }; useEffect(() => { - const storedData = localStorage.getItem(SEARCH_ARRAY_KEY); - if (storedData) { - setSearchArray(JSON.parse(storedData)); + const storedSearchArray = localStorage.getItem(SEARCH_ARRAY_KEY); + if (storedSearchArray) { + setSearchArray(JSON.parse(storedSearchArray)); } }, []); return ( - - - 최근 검색어 - {searchArray.length > 0 && 모두 삭제하기} - - + + 최근 검색어 + {searchArray.length > 0 && 모두 삭제} + {searchArray.map((item) => ( - handleStoredData(item.key)} /> + handleStoredKey(item.key)} /> ))} - - + + ); }; export default RecentSearch; -const Wrapper = styled.div` - display: flex; - flex-direction: column; - padding: 16px; - margin-top: 41px; -`; - -const ChipWrapper = styled.div` +const RecentSearchWrapper = styled.div` display: flex; flex-wrap: wrap; - gap: 8px; + gap: 6px; `; -const TitleWrapper = styled.div` - display: flex; - flex-direction: row; - justify-content: space-between; - margin-bottom: 10px; -`; - -const DelText = styled(Text)` - color: var(--color-gray-deep, #909090); - text-align: right; - font-size: var(--font-size-sm); +const DeleteAllButton = styled.button` + color: var(--color-gray-dk); + font-size: var(--font-size-xs); font-weight: 400; line-height: normal; - cursor: pointer; -`; - -const TitleText = styled(Text)` - color: var(--color-black, #020715); - font-size: var(--font-size-md); - font-weight: 700; - line-height: normal; + position: absolute; + top: 18px; + right: 16px; `; diff --git a/src/components/common/SearchModal/index.tsx b/src/components/common/SearchModal/index.tsx index 19822e5..59291ff 100644 --- a/src/components/common/SearchModal/index.tsx +++ b/src/components/common/SearchModal/index.tsx @@ -1,7 +1,7 @@ import styled from '@emotion/styled'; import SearchBar from '@/components/layouts/SearchBar'; -import { Z_INDEX } from '@/styles/constants'; +import { HEIGHTS, Z_INDEX } from '@/styles/constants'; import Ad from './Ad'; import PopularSearch from './PopularSearch'; import RecentSearch from './RecentSearch'; @@ -11,23 +11,26 @@ interface SearchModalProps { } const SearchModal = ({ modalClose }: SearchModalProps) => { - const searchSectionList: React.ReactNode[] = [, , ]; + const searchSectionList: React.ReactNode[] = [, , ]; // 각 섹션을 리스트로 관리 + + // todo: 검색어 존재 시 자동 완성 모달 뜨게 + // x 버튼 클릭 시 검색 모달 뜨게 return ( - + {searchSectionList.map((section, index) => (
{section}
))}
-
+ ); }; export default SearchModal; -const ModalWrapper = styled.div` +const ModalLayout = styled.div` position: fixed; top: 0; left: 0; @@ -40,6 +43,7 @@ const ModalWrapper = styled.div` `; const SectionsWrapper = styled.div` + margin-top: ${HEIGHTS.HEADER}; width: 100%; display: flex; flex-direction: column; diff --git a/src/components/common/SearchModal/styles.ts b/src/components/common/SearchModal/styles.ts new file mode 100644 index 0000000..b2a4ab4 --- /dev/null +++ b/src/components/common/SearchModal/styles.ts @@ -0,0 +1,21 @@ +import styled from '@emotion/styled'; + +export const SectionWrapper = styled.div` + width: 100%; + display: flex; + flex-direction: column; + padding: 16px; + position: relative; +`; + +export const SectionTitle = styled.p` + flex-direction: row; + margin-bottom: 12px; + font-size: var(--font-size-md); + font-weight: 700; + + .section-title-highlight { + font-size: inherit; + color: var(--color-red); + } +`; diff --git a/src/components/layouts/SearchBar/index.tsx b/src/components/layouts/SearchBar/index.tsx index 8650a5f..0fc8282 100644 --- a/src/components/layouts/SearchBar/index.tsx +++ b/src/components/layouts/SearchBar/index.tsx @@ -2,10 +2,10 @@ import styled from '@emotion/styled'; import { useForm } from 'react-hook-form'; import { useNavigate, useSearchParams } from 'react-router-dom'; -import CancelIcon from '@/assets/icons/cancel-filled-gray.svg?react'; +import CancelIcon from '@/assets/icons/cancel-filled.svg?react'; import SearchIcon from '@/assets/icons/search.svg?react'; import IconButton from '@/components/common/IconButton'; -import { SEARCH_ARRAY_KEY } from '@/components/common/SearchModal/RecentSearch'; +import { SEARCH_ARRAY_KEY } from '@/constants/search'; import { HEIGHTS, Z_INDEX } from '@/styles/constants'; const SEARCH_PLACEHOLDER = '작품/작가 외 검색은 #을 붙여주세요'; @@ -138,6 +138,7 @@ const CancelIconButton = styled(CancelIcon)` position: absolute; right: 8px; cursor: pointer; + color: var(--color-gray-dk); `; const ErrorMessage = styled.div` diff --git a/src/constants/search.ts b/src/constants/search.ts index 55d114e..e611e1c 100644 --- a/src/constants/search.ts +++ b/src/constants/search.ts @@ -1,35 +1,21 @@ -type PopularSearch = { - id: number; - text: string; -}; +export const SEARCH_ARRAY_KEY = 'searchArray'; -export const POPULAR_SEARCH_LIST: PopularSearch[] = [ - { id: 1, text: '모던 아트' }, - { id: 2, text: '추상화' }, - { id: 3, text: '인상파' }, - { id: 4, text: '인물화' }, - { id: 5, text: '풍경화' }, - { id: 6, text: '큐비즘' }, - { id: 7, text: '디지털 아트' }, - { id: 8, text: '팝 아트' }, - { id: 9, text: '아크릴화' }, - { id: 10, text: '수채화' }, +export const POPULAR_SEARCH_LIST: string[] = [ + '#모던 아트', + '#추상화', + '#인상파', + '#인물화', + '#풍경화', + '#큐비즘', + '#디지털 아트', + '#팝 아트', + '#아크릴화', + '#수채화', ]; // -type SearchAd = { - id: number; - src: string; -}; - -export const AD_LIST: SearchAd[] = [ - { - id: 1, - src: ' https://images.unsplash.com/photo-1579273166152-d725a4e2b755?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MTl8fCVFQSVCNyVCOCVFQiVBNiVCQ3xlbnwwfHwwfHx8MA%3D%3D', - }, - { - id: 2, - src: 'https://images.unsplash.com/photo-1577083862054-7324cd025fa6?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MTR8fCVFQyU4NCU5QyVFQyU5NiU5MSVFRCU5OSU5NHxlbnwwfHwwfHx8MA%3D%3D', - }, +export const AD_LIST: string[] = [ + 'https://images.unsplash.com/photo-1579273166152-d725a4e2b755?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MTl8fCVFQSVCNyVCOCVFQiVBNiVCQ3xlbnwwfHwwfHx8MA%3D%3D', + 'https://images.unsplash.com/photo-1577083862054-7324cd025fa6?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MTR8fCVFQyU4NCU5QyVFQyU5NiU5MSVFRCU5OSU5NHxlbnwwfHwwfHx8MA%3D%3D', ]; From 5aeb9b1ed52ce0fd9db9e4f37d811c0076132e0a Mon Sep 17 00:00:00 2001 From: joojjang Date: Mon, 18 Nov 2024 06:13:05 +0900 Subject: [PATCH 10/16] =?UTF-8?q?feat(serach=20ad):=20=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=A0(=EC=88=98=EB=8F=84=20=EC=9E=88=EB=8A=94...)=20?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 제목, 작가, 설명 --- src/components/common/SearchModal/Ad.tsx | 2 +- src/constants/search.ts | 25 +++++++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/components/common/SearchModal/Ad.tsx b/src/components/common/SearchModal/Ad.tsx index 8ee0996..00ae602 100644 --- a/src/components/common/SearchModal/Ad.tsx +++ b/src/components/common/SearchModal/Ad.tsx @@ -13,7 +13,7 @@ const SearchAd = () => { {AD_LIST.map((ad, index) => ( - Ad image + Ad image ))} diff --git a/src/constants/search.ts b/src/constants/search.ts index e611e1c..b2b597e 100644 --- a/src/constants/search.ts +++ b/src/constants/search.ts @@ -15,7 +15,26 @@ export const POPULAR_SEARCH_LIST: string[] = [ // -export const AD_LIST: string[] = [ - 'https://images.unsplash.com/photo-1579273166152-d725a4e2b755?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MTl8fCVFQSVCNyVCOCVFQiVBNiVCQ3xlbnwwfHwwfHx8MA%3D%3D', - 'https://images.unsplash.com/photo-1577083862054-7324cd025fa6?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MTR8fCVFQyU4NCU5QyVFQyU5NiU5MSVFRCU5OSU5NHxlbnwwfHwwfHx8MA%3D%3D', +type Ad = { + title: string; + imageUrl: string; + artist: string; + des: string; +}; + +export const AD_LIST: Ad[] = [ + { + title: '', + imageUrl: + 'https://images.unsplash.com/photo-1579273166152-d725a4e2b755?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MTl8fCVFQSVCNyVCOCVFQiVBNiVCQ3xlbnwwfHwwfHx8MA%3D%3D', + artist: '', + des: '', + }, + { + title: '', + imageUrl: + 'https://images.unsplash.com/photo-1577083862054-7324cd025fa6?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MTR8fCVFQyU4NCU5QyVFQyU5NiU5MSVFRCU5OSU5NHxlbnwwfHwwfHx8MA%3D%3D', + artist: '', + des: '', + }, ]; From 054b8a4c337240c827324297fb6af73874d0caab Mon Sep 17 00:00:00 2001 From: joojjang Date: Wed, 20 Nov 2024 11:19:10 +0900 Subject: [PATCH 11/16] =?UTF-8?q?refactor(SearchModal):=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=EB=AA=A8=EB=8B=AC=20=EC=83=81=ED=83=9C=20=EA=B4=80?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/CategoryTabBar/index.tsx | 11 ++++--- src/components/common/FakeSearchBar/index.tsx | 9 +++--- src/components/common/SearchModal/index.tsx | 29 ++++++++++--------- src/components/layouts/Header/index.tsx | 7 +++-- src/components/layouts/SearchBar/index.tsx | 26 ++++++++++++++--- src/pages/Categories/index.tsx | 11 ++----- src/pages/Discover/index.tsx | 4 ++- src/pages/Home/index.tsx | 11 ++----- src/pages/SearchResults/index.tsx | 27 +++++++++-------- src/store/useSearchModalStore.ts | 13 +++++++++ 10 files changed, 85 insertions(+), 63 deletions(-) create mode 100644 src/store/useSearchModalStore.ts diff --git a/src/components/common/CategoryTabBar/index.tsx b/src/components/common/CategoryTabBar/index.tsx index a90d075..074a7b4 100644 --- a/src/components/common/CategoryTabBar/index.tsx +++ b/src/components/common/CategoryTabBar/index.tsx @@ -2,6 +2,8 @@ import { Z_INDEX } from '@/styles/constants'; import styled from '@emotion/styled'; import { useEffect, useState } from 'react'; +import { HEIGHTS } from '@/styles/constants'; + type TapWrapperProps = { isActive: boolean; }; @@ -41,13 +43,12 @@ export default CategoryTabBar; const Wrapper = styled.div` z-index: ${Z_INDEX.Header}; - position: fixed; + position: sticky; + top: ${HEIGHTS.HEADER}; width: 100%; height: 41px; display: flex; flex-direction: row; - justify-content: space-between; - align-items: center; border-bottom: 1px solid var(--color-gray-md); background: var(--color-white); font-size: var(--font-size-sm); @@ -57,9 +58,11 @@ const Wrapper = styled.div` const TabWrapper = styled.div` width: 100%; height: 100%; - padding: 11px; cursor: pointer; text-align: center; + display: flex; + justify-content: center; + align-items: center; color: ${({ isActive }) => (isActive ? 'var(--color-black)' : 'var(--color-gray-dk)')}; border-bottom: ${({ isActive }) => (isActive ? '2px solid var(--color-black)' : 'none')}; diff --git a/src/components/common/FakeSearchBar/index.tsx b/src/components/common/FakeSearchBar/index.tsx index 5a27093..c9af125 100644 --- a/src/components/common/FakeSearchBar/index.tsx +++ b/src/components/common/FakeSearchBar/index.tsx @@ -2,18 +2,17 @@ import styled from '@emotion/styled'; import SearchIcon from '@/assets/icons/search.svg?react'; import IconButton from '@/components/common/IconButton'; +import useSearchModalStore from '@/store/useSearchModalStore'; import { HEIGHTS } from '@/styles/constants'; const SEARCH_PLACEHOLDER = '작품/작가 외 검색은 #을 붙여주세요'; -interface FakeSearchBarProps { - modalOpen: () => void; -} +const FakeSearchBar = () => { + const { isModalOpen, setIsModalOpen } = useSearchModalStore(); -const FakeSearchBar = ({ modalOpen }: FakeSearchBarProps) => { return ( - + setIsModalOpen(!isModalOpen)}> diff --git a/src/components/common/SearchModal/index.tsx b/src/components/common/SearchModal/index.tsx index 59291ff..1876309 100644 --- a/src/components/common/SearchModal/index.tsx +++ b/src/components/common/SearchModal/index.tsx @@ -1,30 +1,31 @@ import styled from '@emotion/styled'; import SearchBar from '@/components/layouts/SearchBar'; +import useSearchModalStore from '@/store/useSearchModalStore'; import { HEIGHTS, Z_INDEX } from '@/styles/constants'; import Ad from './Ad'; import PopularSearch from './PopularSearch'; import RecentSearch from './RecentSearch'; -interface SearchModalProps { - modalClose: () => void; -} - -const SearchModal = ({ modalClose }: SearchModalProps) => { +const SearchModal = () => { + const { isModalOpen } = useSearchModalStore(); const searchSectionList: React.ReactNode[] = [, , ]; // 각 섹션을 리스트로 관리 // todo: 검색어 존재 시 자동 완성 모달 뜨게 - // x 버튼 클릭 시 검색 모달 뜨게 return ( - - - - {searchSectionList.map((section, index) => ( -
{section}
- ))} -
-
+ <> + {isModalOpen && ( + + + + {searchSectionList.map((section, index) => ( +
{section}
+ ))} +
+
+ )} + ); }; diff --git a/src/components/layouts/Header/index.tsx b/src/components/layouts/Header/index.tsx index 1d57e55..1a448c9 100644 --- a/src/components/layouts/Header/index.tsx +++ b/src/components/layouts/Header/index.tsx @@ -5,18 +5,19 @@ import Logo from '@/assets/logo.svg?react'; import IconButton from '@/components/common/IconButton'; import { RouterPath } from '@/routes/path'; import useModeStore from '@/store/useModeStore'; +import useSearchModalStore from '@/store/useSearchModalStore'; import { HEIGHTS, Z_INDEX } from '@/styles/constants'; interface HeaderProps { title?: string; leftSideChildren?: React.ReactNode; rightSideChildren?: React.ReactNode; - modalOpen?: () => void; } -const Header = ({ title, leftSideChildren, rightSideChildren, modalOpen }: HeaderProps) => { +const Header = ({ title, leftSideChildren, rightSideChildren }: HeaderProps) => { const { pathname } = useLocation(); const { mode } = useModeStore(); + const { isModalOpen, setIsModalOpen } = useSearchModalStore(); const renderElements = () => { if (pathname === RouterPath.home) { @@ -24,7 +25,7 @@ const Header = ({ title, leftSideChildren, rightSideChildren, modalOpen }: Heade <> - + setIsModalOpen(!isModalOpen)} /> {mode === 'user' ? ( ) : ( diff --git a/src/components/layouts/SearchBar/index.tsx b/src/components/layouts/SearchBar/index.tsx index 0fc8282..7ed75ea 100644 --- a/src/components/layouts/SearchBar/index.tsx +++ b/src/components/layouts/SearchBar/index.tsx @@ -1,4 +1,5 @@ import styled from '@emotion/styled'; +import { useEffect } from 'react'; import { useForm } from 'react-hook-form'; import { useNavigate, useSearchParams } from 'react-router-dom'; @@ -6,20 +7,24 @@ import CancelIcon from '@/assets/icons/cancel-filled.svg?react'; import SearchIcon from '@/assets/icons/search.svg?react'; import IconButton from '@/components/common/IconButton'; import { SEARCH_ARRAY_KEY } from '@/constants/search'; +import { RouterPath } from '@/routes/path'; +import useSearchModalStore from '@/store/useSearchModalStore'; import { HEIGHTS, Z_INDEX } from '@/styles/constants'; const SEARCH_PLACEHOLDER = '작품/작가 외 검색은 #을 붙여주세요'; const MAX_RECENT_SEARCHES = 10; interface SearchBarProps { + includeBack?: boolean; includeFavorite?: boolean; - goBack?: () => void; + goBack?: () => void; // SearchResult에서만 전달됨 } -const SearchBar = ({ includeFavorite = false, goBack }: SearchBarProps) => { +const SearchBar = ({ includeBack = true, includeFavorite = false, goBack }: SearchBarProps) => { const navigate = useNavigate(); const [searchParams, setSearchParams] = useSearchParams(); const initialSearchWord = searchParams.get('query') || ''; + const { isModalOpen, setIsModalOpen } = useSearchModalStore(); const { register, handleSubmit, watch, setValue, formState } = useForm<{ searchWord: string }>({ defaultValues: { @@ -28,13 +33,25 @@ const SearchBar = ({ includeFavorite = false, goBack }: SearchBarProps) => { mode: 'onSubmit', }); + // test + useEffect(() => { + console.log('isModalOpen: ', isModalOpen); + }, [isModalOpen]); + const generateRandomKey = () => { return Math.random().toString(36).substr(2, 9); }; + const handleClickBack = () => { + if (goBack) goBack(); // SearchResult에서만 전달됨 // pathname 추출해서 해도 된다 생각했는데 안 됨 + setIsModalOpen(false); + }; + const handleRemoveSearchWord = (e: React.MouseEvent) => { + console.log('called'); e.preventDefault(); setValue('searchWord', ''); + setIsModalOpen(true); }; const activeEnter = (data: { searchWord: string }) => { @@ -57,14 +74,14 @@ const SearchBar = ({ includeFavorite = false, goBack }: SearchBarProps) => { localStorage.setItem(SEARCH_ARRAY_KEY, JSON.stringify(searchArray)); setSearchParams({ query: searchWord }); - navigate(`/results?query=${searchWord}`); + navigate(`/${RouterPath.results}?query=${searchWord}`); }; const nowSearchWord = watch('searchWord'); return ( - + {includeBack && } { {...register('searchWord', { validate: (value) => value.trim() !== '' || '공백만 입력할 수 없습니다.', })} + onClick={() => setIsModalOpen(true)} /> {nowSearchWord.trim().length > 0 && } diff --git a/src/pages/Categories/index.tsx b/src/pages/Categories/index.tsx index d6098e2..b3aec02 100644 --- a/src/pages/Categories/index.tsx +++ b/src/pages/Categories/index.tsx @@ -1,5 +1,4 @@ import styled from '@emotion/styled'; -import { useState } from 'react'; import FakeSearchBar from '@/components/common/FakeSearchBar'; import SearchModal from '@/components/common/SearchModal'; @@ -9,16 +8,10 @@ import CategoryItem from './components/CategoryItem'; import CurationItem from './components/CurationItem'; const Categories = () => { - const [isModalOpen, setIsModalOpen] = useState(false); - - const handleModalOpen = () => { - setIsModalOpen(true); - }; - return ( - - {isModalOpen && setIsModalOpen(false)} />} + + {CATEGORY_LIST.map((category) => ( diff --git a/src/pages/Discover/index.tsx b/src/pages/Discover/index.tsx index f483bd2..ae395b7 100644 --- a/src/pages/Discover/index.tsx +++ b/src/pages/Discover/index.tsx @@ -4,12 +4,14 @@ import { ErrorBoundary } from 'react-error-boundary'; import useGetFeed, { type Product } from '@/apis/products/useGetFeed'; import Loader from '@/components/common/Loader'; +import SearchModal from '@/components/common/SearchModal'; import SearchBar from '@/components/layouts/SearchBar'; import { HEIGHTS } from '@/styles/constants'; const Discover = () => ( - + + {/* todo: 폴백 UI 만들기 */} Error}> diff --git a/src/pages/Home/index.tsx b/src/pages/Home/index.tsx index 06f1850..8a04930 100644 --- a/src/pages/Home/index.tsx +++ b/src/pages/Home/index.tsx @@ -1,5 +1,4 @@ import styled from '@emotion/styled'; -import { useState } from 'react'; import SearchModal from '@/components/common/SearchModal'; import Footer from '@/components/layouts/Footer'; @@ -10,16 +9,10 @@ import AdBanner from './components/AdBanner'; import ArticleBanner from './components/ArticleBanner'; const Home = () => { - const [isModalOpen, setIsModalOpen] = useState(false); - - const handleModalOpen = () => { - setIsModalOpen(true); - }; - return ( - {isModalOpen && setIsModalOpen(false)} />} -
+
+ {ARTICLE_LIST.map((item) => ( { const searchArtistLen = artistsData.length; const categoryList = ['전체', '작품', '작가']; + const { isModalOpen, setIsModalOpen } = useSearchModalStore(); + + // 검색 결과로 이동 직후 검색 모달 닫음 + useEffect(() => { + setIsModalOpen(false); + }, []); + const goBack = () => { - navigate(RouterPath.categories); + navigate(-1); }; const handleTabClick = (tab: string) => { @@ -44,11 +51,9 @@ const SearchResultsContent = () => { return ( - - - + + {isModalOpen && } - {selectedTab === '전체' && ( @@ -105,12 +110,6 @@ const PageContainer = styled.div` width: 100%; `; -const HeaderSection = styled.div` - position: sticky; - height: 41px; - z-index: ${Z_INDEX.SearchHeader}; -`; - const ContentSection = styled.div` flex: 1; overflow-y: auto; diff --git a/src/store/useSearchModalStore.ts b/src/store/useSearchModalStore.ts new file mode 100644 index 0000000..b547599 --- /dev/null +++ b/src/store/useSearchModalStore.ts @@ -0,0 +1,13 @@ +import { create } from 'zustand'; + +type SearchModalState = { + isModalOpen: boolean; + setIsModalOpen: (isModalOpen: boolean) => void; +}; + +const useSearchModalStore = create((set) => ({ + isModalOpen: false, + setIsModalOpen: (isModalOpen) => set({ isModalOpen }), +})); + +export default useSearchModalStore; From 89dcfafaaaaebba09de549b0af41a3da99a918da Mon Sep 17 00:00:00 2001 From: joojjang Date: Wed, 20 Nov 2024 11:45:15 +0900 Subject: [PATCH 12/16] =?UTF-8?q?fix(SearchResults):=20=EA=B2=80=EC=83=89?= =?UTF-8?q?=20=EA=B2=B0=EA=B3=BC=20=ED=8E=98=EC=9D=B4=EC=A7=80=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EA=B2=80=EC=83=89=20=EB=AA=A8=EB=8B=AC=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=20=EA=B4=80=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/layouts/SearchBar/index.tsx | 4 ++++ src/pages/SearchResults/index.tsx | 24 +++++++++++----------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/components/layouts/SearchBar/index.tsx b/src/components/layouts/SearchBar/index.tsx index 7ed75ea..f065bf0 100644 --- a/src/components/layouts/SearchBar/index.tsx +++ b/src/components/layouts/SearchBar/index.tsx @@ -56,6 +56,8 @@ const SearchBar = ({ includeBack = true, includeFavorite = false, goBack }: Sear const activeEnter = (data: { searchWord: string }) => { const { searchWord } = data; + + // 검색 기록 업데이트 const storedData = localStorage.getItem(SEARCH_ARRAY_KEY); let searchArray = storedData ? JSON.parse(storedData) : []; const existingIndex = searchArray.findIndex( @@ -73,6 +75,8 @@ const SearchBar = ({ includeBack = true, includeFavorite = false, goBack }: Sear } localStorage.setItem(SEARCH_ARRAY_KEY, JSON.stringify(searchArray)); + + // 검색 실행 setSearchParams({ query: searchWord }); navigate(`/${RouterPath.results}?query=${searchWord}`); }; diff --git a/src/pages/SearchResults/index.tsx b/src/pages/SearchResults/index.tsx index 11d55eb..007716e 100644 --- a/src/pages/SearchResults/index.tsx +++ b/src/pages/SearchResults/index.tsx @@ -16,6 +16,16 @@ import ArtistContents from './components/ArtistContents'; import HorizontalFrame from './components/HorizontalFrame'; import MoreButton from './components/MoreButton'; +const SearchResults = () => { + return ( + Error Status}> + }> + + + + ); +}; + const SearchResultsContent = () => { const [selectedTab, setSelectedTab] = useState('전체'); const [searchParams] = useSearchParams(); @@ -36,10 +46,10 @@ const SearchResultsContent = () => { const { isModalOpen, setIsModalOpen } = useSearchModalStore(); - // 검색 결과로 이동 직후 검색 모달 닫음 + // 검색어 바꿔 새로 검색 시 검색 모달 닫음 useEffect(() => { setIsModalOpen(false); - }, []); + }, [searchQuery]); const goBack = () => { navigate(-1); @@ -94,16 +104,6 @@ const SearchResultsContent = () => { ); }; -const SearchResults = () => { - return ( - Error Status}> - }> - - - - ); -}; - export default SearchResults; const PageContainer = styled.div` From 6cbaf8884cf6cad7afb94e634ceef4d5110f46fe Mon Sep 17 00:00:00 2001 From: joojjang Date: Wed, 20 Nov 2024 11:55:05 +0900 Subject: [PATCH 13/16] =?UTF-8?q?fix(SearchBar):=20=EB=B0=B8=EB=A6=AC?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=EC=85=98=20=EB=A1=9C=EC=A7=81=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 검색어 없을 때 검색 안 되도록 --- src/components/layouts/SearchBar/index.tsx | 53 ++++++++++------------ 1 file changed, 23 insertions(+), 30 deletions(-) diff --git a/src/components/layouts/SearchBar/index.tsx b/src/components/layouts/SearchBar/index.tsx index f065bf0..105575f 100644 --- a/src/components/layouts/SearchBar/index.tsx +++ b/src/components/layouts/SearchBar/index.tsx @@ -57,28 +57,30 @@ const SearchBar = ({ includeBack = true, includeFavorite = false, goBack }: Sear const activeEnter = (data: { searchWord: string }) => { const { searchWord } = data; - // 검색 기록 업데이트 - const storedData = localStorage.getItem(SEARCH_ARRAY_KEY); - let searchArray = storedData ? JSON.parse(storedData) : []; - const existingIndex = searchArray.findIndex( - (item: { key: string; keyword: string }) => item.keyword === searchWord, - ); - - if (existingIndex !== -1) { - searchArray.splice(existingIndex, 1); + if (!formState.errors.searchWord) { + // 검색 기록 업데이트 + const storedData = localStorage.getItem(SEARCH_ARRAY_KEY); + let searchArray = storedData ? JSON.parse(storedData) : []; + const existingIndex = searchArray.findIndex( + (item: { key: string; keyword: string }) => item.keyword === searchWord, + ); + + if (existingIndex !== -1) { + searchArray.splice(existingIndex, 1); + } + + const newItem = { keyword: searchWord, key: generateRandomKey() }; + searchArray = [newItem, ...searchArray]; + if (searchArray.length > MAX_RECENT_SEARCHES) { + searchArray = searchArray.slice(0, MAX_RECENT_SEARCHES); + } + + localStorage.setItem(SEARCH_ARRAY_KEY, JSON.stringify(searchArray)); + + // 검색 실행 + setSearchParams({ query: searchWord }); + navigate(`/${RouterPath.results}?query=${searchWord}`); } - - const newItem = { keyword: searchWord, key: generateRandomKey() }; - searchArray = [newItem, ...searchArray]; - if (searchArray.length > MAX_RECENT_SEARCHES) { - searchArray = searchArray.slice(0, MAX_RECENT_SEARCHES); - } - - localStorage.setItem(SEARCH_ARRAY_KEY, JSON.stringify(searchArray)); - - // 검색 실행 - setSearchParams({ query: searchWord }); - navigate(`/${RouterPath.results}?query=${searchWord}`); }; const nowSearchWord = watch('searchWord'); @@ -98,9 +100,6 @@ const SearchBar = ({ includeBack = true, includeFavorite = false, goBack }: Sear /> {nowSearchWord.trim().length > 0 && } - {formState.errors.searchWord && ( - {formState.errors.searchWord.message} - )} {includeFavorite && } ); @@ -162,9 +161,3 @@ const CancelIconButton = styled(CancelIcon)` cursor: pointer; color: var(--color-gray-dk); `; - -const ErrorMessage = styled.div` - color: red; - font-size: var(--font-size-sm); - margin-top: 4px; -`; From b13f7fc49e40cc210269022ebc024e6fd050a2a8 Mon Sep 17 00:00:00 2001 From: joojjang Date: Wed, 20 Nov 2024 12:02:36 +0900 Subject: [PATCH 14/16] =?UTF-8?q?fix(SearchBar):=20searchWord=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=EB=A1=9C=20=EA=B4=80=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 서치 바에서 검색어를 쿼리 파람으로 관리하지 않음 --- src/components/layouts/SearchBar/index.tsx | 38 ++++++++-------------- 1 file changed, 13 insertions(+), 25 deletions(-) diff --git a/src/components/layouts/SearchBar/index.tsx b/src/components/layouts/SearchBar/index.tsx index 105575f..875ec97 100644 --- a/src/components/layouts/SearchBar/index.tsx +++ b/src/components/layouts/SearchBar/index.tsx @@ -1,5 +1,5 @@ import styled from '@emotion/styled'; -import { useEffect } from 'react'; +import { useEffect, useState } from 'react'; import { useForm } from 'react-hook-form'; import { useNavigate, useSearchParams } from 'react-router-dom'; @@ -22,25 +22,20 @@ interface SearchBarProps { const SearchBar = ({ includeBack = true, includeFavorite = false, goBack }: SearchBarProps) => { const navigate = useNavigate(); - const [searchParams, setSearchParams] = useSearchParams(); + const [searchParams] = useSearchParams(); const initialSearchWord = searchParams.get('query') || ''; - const { isModalOpen, setIsModalOpen } = useSearchModalStore(); + const { setIsModalOpen } = useSearchModalStore(); + const [searchWord, setSearchWord] = useState(initialSearchWord); - const { register, handleSubmit, watch, setValue, formState } = useForm<{ searchWord: string }>({ - defaultValues: { - searchWord: initialSearchWord, - }, + const { handleSubmit, setValue, formState } = useForm<{ searchWord: string }>({ mode: 'onSubmit', }); - // test useEffect(() => { - console.log('isModalOpen: ', isModalOpen); - }, [isModalOpen]); + setValue('searchWord', searchWord); // React Hook Form의 값도 초기화 + }, [searchWord, setValue]); - const generateRandomKey = () => { - return Math.random().toString(36).substr(2, 9); - }; + const generateRandomKey = () => Math.random().toString(36).substr(2, 9); const handleClickBack = () => { if (goBack) goBack(); // SearchResult에서만 전달됨 // pathname 추출해서 해도 된다 생각했는데 안 됨 @@ -48,15 +43,12 @@ const SearchBar = ({ includeBack = true, includeFavorite = false, goBack }: Sear }; const handleRemoveSearchWord = (e: React.MouseEvent) => { - console.log('called'); e.preventDefault(); - setValue('searchWord', ''); + setSearchWord(''); setIsModalOpen(true); }; - const activeEnter = (data: { searchWord: string }) => { - const { searchWord } = data; - + const activeEnter = () => { if (!formState.errors.searchWord) { // 검색 기록 업데이트 const storedData = localStorage.getItem(SEARCH_ARRAY_KEY); @@ -78,13 +70,10 @@ const SearchBar = ({ includeBack = true, includeFavorite = false, goBack }: Sear localStorage.setItem(SEARCH_ARRAY_KEY, JSON.stringify(searchArray)); // 검색 실행 - setSearchParams({ query: searchWord }); navigate(`/${RouterPath.results}?query=${searchWord}`); } }; - const nowSearchWord = watch('searchWord'); - return ( {includeBack && } @@ -93,12 +82,11 @@ const SearchBar = ({ includeBack = true, includeFavorite = false, goBack }: Sear value.trim() !== '' || '공백만 입력할 수 없습니다.', - })} + value={searchWord} + onChange={(e) => setSearchWord(e.target.value)} onClick={() => setIsModalOpen(true)} /> - {nowSearchWord.trim().length > 0 && } + {searchWord.trim().length > 0 && } {includeFavorite && } From 7320b8e693db80454ff3360d48202d08698b9037 Mon Sep 17 00:00:00 2001 From: joojjang Date: Wed, 20 Nov 2024 12:33:37 +0900 Subject: [PATCH 15/16] =?UTF-8?q?fix(Categories/CategoryItem):=20=EC=B9=B4?= =?UTF-8?q?=ED=85=8C=EA=B3=A0=EB=A6=AC=20=EC=8D=B8=EB=84=A4=EC=9D=BC=20?= =?UTF-8?q?=EC=82=AC=EC=9D=B4=EC=A6=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/Categories/components/CategoryItem/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/Categories/components/CategoryItem/index.tsx b/src/pages/Categories/components/CategoryItem/index.tsx index 29e80c4..197b926 100644 --- a/src/pages/Categories/components/CategoryItem/index.tsx +++ b/src/pages/Categories/components/CategoryItem/index.tsx @@ -26,6 +26,7 @@ const Wrapper = styled.li` const CategoryThumbnail = styled.div` width: 100%; + min-width: 64px; aspect-ratio: 1 / 1; border-radius: 100%; overflow: hidden; From 5c2efbc478e7e366e65367c02afc0c408e56bb63 Mon Sep 17 00:00:00 2001 From: joojjang Date: Wed, 20 Nov 2024 12:14:13 +0900 Subject: [PATCH 16/16] =?UTF-8?q?fix(getFeed):=20=EC=9D=91=EB=8B=B5=20?= =?UTF-8?q?=ED=98=95=EC=8B=9D=20=EB=B3=80=EA=B2=BD=EC=97=90=20=EB=94=B0?= =?UTF-8?q?=EB=A5=B8=20=EB=A6=AC=ED=84=B4=20=EA=B0=92=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/products/useGetFeed.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/apis/products/useGetFeed.ts b/src/apis/products/useGetFeed.ts index 9a8a79b..de2da19 100644 --- a/src/apis/products/useGetFeed.ts +++ b/src/apis/products/useGetFeed.ts @@ -20,9 +20,8 @@ type GetFeedResponse = { async function getFeed(size: number): Promise { try { const response = await fetchInstance().get(`/products/feed?size=${size}`); - // console.log('getFeed response: ', response); - return response.data; + return response.data.data; } catch (error) { if (isAxiosError(error)) { if (error.response) {