diff --git a/src/assets/icons/cancel-filled-gray.svg b/src/assets/icons/cancel-filled-gray.svg
deleted file mode 100644
index bc49a1f5..00000000
--- 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 c2853515..9b295918 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/CategoryTabBar/index.tsx b/src/components/common/CategoryTabBar/index.tsx
index a90d075f..074a7b48 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/Chip/index.tsx b/src/components/common/Chip/index.tsx
index 4f9c1a93..25974664 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/FakeSearchBar/index.tsx b/src/components/common/FakeSearchBar/index.tsx
index 5a270939..c9af125d 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/PopularSearchItem/index.tsx b/src/components/common/PopularSearchItem/index.tsx
index 958ae8a0..1163f795 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/ProfileImage/index.tsx b/src/components/common/ProfileImage/index.tsx
index 55fec96a..6b02ecbc 100644
--- a/src/components/common/ProfileImage/index.tsx
+++ b/src/components/common/ProfileImage/index.tsx
@@ -24,7 +24,9 @@ const StyledProfileImage = styled.div<{ width: number }>`
border: 1px solid var(--color-gray-md);
background-color: var(--color-gray-lt);
- .img {
+ img {
object-fit: cover;
+ width: 100%;
+ height: 100%;
}
`;
diff --git a/src/components/common/SearchModal/Ad.tsx b/src/components/common/SearchModal/Ad.tsx
new file mode 100644
index 00000000..00ae6028
--- /dev/null
+++ b/src/components/common/SearchModal/Ad.tsx
@@ -0,0 +1,53 @@
+import styled from '@emotion/styled';
+
+import { AD_LIST } from '@/constants/search';
+import * as S from './styles';
+
+const SearchAd = () => {
+ return (
+
+
+ 요즘 뜨는 작품
+
+ 광고
+
+ {AD_LIST.map((ad, index) => (
+
+
+
+ ))}
+
+
+ );
+};
+
+export default SearchAd;
+
+const AdWrapper = styled.div`
+ display: flex;
+ gap: 12px;
+`;
+
+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.div`
+ width: 100%;
+ aspect-ratio: 1 / 1;
+ background-color: var(--color-gray-lt);
+
+ 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 0eacad97..1a6bf82e 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 c6a12a75..048b05ec 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/SearchAd.tsx b/src/components/common/SearchModal/SearchAd.tsx
deleted file mode 100644
index 8d686d8d..00000000
--- a/src/components/common/SearchModal/SearchAd.tsx
+++ /dev/null
@@ -1,71 +0,0 @@
-import { Image } from '@chakra-ui/react';
-import styled from '@emotion/styled';
-
-import { AD_LIST } from '@/constants/search';
-import * as G from '@/styles/globalStyles';
-
-const SearchAd = () => {
- return (
-
-
-
- 요즘 뜨는 작품
-
- 광고
-
-
- {AD_LIST.map((ad) => (
-
- ))}
-
-
- );
-};
-
-export default SearchAd;
-
-const Wrapper = styled.div`
- display: flex;
- flex-direction: column;
- padding: 16px;
-`;
-
-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 AdImage = styled(Image)`
- 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;
-`;
diff --git a/src/components/common/SearchModal/index.tsx b/src/components/common/SearchModal/index.tsx
index 3fea75d8..18763099 100644
--- a/src/components/common/SearchModal/index.tsx
+++ b/src/components/common/SearchModal/index.tsx
@@ -1,34 +1,37 @@
import styled from '@emotion/styled';
import SearchBar from '@/components/layouts/SearchBar';
-import { Z_INDEX } from '@/styles/constants';
-import * as G from '@/styles/globalStyles';
+import useSearchModalStore from '@/store/useSearchModalStore';
+import { HEIGHTS, Z_INDEX } from '@/styles/constants';
+import Ad from './Ad';
import PopularSearch from './PopularSearch';
import RecentSearch from './RecentSearch';
-import Ad from './SearchAd';
-interface SearchModalProps {
- modalClose: () => void;
-}
+const SearchModal = () => {
+ const { isModalOpen } = useSearchModalStore();
+ const searchSectionList: React.ReactNode[] = [, , ]; // 각 섹션을 리스트로 관리
+
+ // todo: 검색어 존재 시 자동 완성 모달 뜨게
-const SearchModal = ({ modalClose }: SearchModalProps) => {
return (
-
-
-
-
-
-
-
-
-
-
+ <>
+ {isModalOpen && (
+
+
+
+ {searchSectionList.map((section, index) => (
+ {section}
+ ))}
+
+
+ )}
+ >
);
};
export default SearchModal;
-const ModalWrapper = styled.div`
+const ModalLayout = styled.div`
position: fixed;
top: 0;
left: 0;
@@ -40,8 +43,17 @@ const ModalWrapper = styled.div`
z-index: ${Z_INDEX.Modal};
`;
-const SearchWrapper = styled.div`
- flex: 1;
+const SectionsWrapper = styled.div`
+ margin-top: ${HEIGHTS.HEADER};
+ width: 100%;
display: flex;
flex-direction: column;
+
+ & > * {
+ border-bottom: 1px solid var(--color-gray-lt);
+ }
+
+ & > *:last-child {
+ border-bottom: none;
+ }
`;
diff --git a/src/components/common/SearchModal/styles.ts b/src/components/common/SearchModal/styles.ts
new file mode 100644
index 00000000..b2a4ab40
--- /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/Header/index.tsx b/src/components/layouts/Header/index.tsx
index 1d57e557..1a448c95 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 8650a5f8..f065bf0a 100644
--- a/src/components/layouts/SearchBar/index.tsx
+++ b/src/components/layouts/SearchBar/index.tsx
@@ -1,25 +1,30 @@
import styled from '@emotion/styled';
+import { useEffect } from 'react';
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 { 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,17 +33,31 @@ 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 }) => {
const { searchWord } = data;
+
+ // 검색 기록 업데이트
const storedData = localStorage.getItem(SEARCH_ARRAY_KEY);
let searchArray = storedData ? JSON.parse(storedData) : [];
const existingIndex = searchArray.findIndex(
@@ -56,15 +75,17 @@ 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 && }
@@ -138,6 +160,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/categories.ts b/src/constants/categories.ts
index 3d50e373..ec948c19 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/constants/search.ts b/src/constants/search.ts
index 55d114e7..b2b597e5 100644
--- a/src/constants/search.ts
+++ b/src/constants/search.ts
@@ -1,35 +1,40 @@
-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;
+type Ad = {
+ title: string;
+ imageUrl: string;
+ artist: string;
+ des: string;
};
-export const AD_LIST: SearchAd[] = [
+export const AD_LIST: Ad[] = [
{
- 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',
+ 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: '',
},
{
- 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',
+ 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: '',
},
];
diff --git a/src/pages/Categories/components/CategoryItem/index.tsx b/src/pages/Categories/components/CategoryItem/index.tsx
index 07172d9c..29e80c4e 100644
--- a/src/pages/Categories/components/CategoryItem/index.tsx
+++ b/src/pages/Categories/components/CategoryItem/index.tsx
@@ -1,41 +1,44 @@
-import { Image } from '@chakra-ui/react';
import styled from '@emotion/styled';
interface CategoryItemProps {
- des: string;
+ title: string;
src: string;
}
-const CategoryItem = ({ des, src }: CategoryItemProps) => {
- des = des.replace('/', '/\n');
-
+const CategoryItem = ({ title, src }: CategoryItemProps) => {
return (
-
- {des}
+ {src && }
+ {title}
);
};
export default CategoryItem;
-const Wrapper = styled.div`
+const Wrapper = styled.li`
display: flex;
flex-direction: column;
align-items: center;
- cursor: pointer;
gap: 8px;
+ cursor: pointer;
`;
-const RoundImage = styled(Image)`
- aspect-ratio: 1/1;
- width: 80%;
+const CategoryThumbnail = styled.div`
+ width: 100%;
+ aspect-ratio: 1 / 1;
border-radius: 100%;
- object-fit: cover;
+ overflow: hidden;
background-color: var(--color-gray-lt);
+
+ img {
+ object-fit: cover;
+ width: 100%;
+ height: 100%;
+ }
`;
-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 00000000..4be4021e
--- /dev/null
+++ b/src/pages/Categories/components/CurationItem/index.tsx
@@ -0,0 +1,43 @@
+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;
+
+ &:not(:last-child) {
+ border-bottom: 1px solid var(--color-gray-lt);
+ }
+`;
+
+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 83c3737a..b3aec02d 100644
--- a/src/pages/Categories/index.tsx
+++ b/src/pages/Categories/index.tsx
@@ -1,40 +1,27 @@
-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 Category from './components/CategoryItem';
+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) => (
-
+
))}
-
-
+
+
-
- 매거진
- 숨겨진 무한의 가치를 발견하고 싶다면
-
-
-
- 아티스트 그라운드
- 내 취향대로 작가 골라보기
-
+ {CURATION_LIST.map((curation) => (
+
+ ))}
);
@@ -46,28 +33,22 @@ 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 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.div`
+const CurationWrapper = styled.ul`
height: auto;
width: 100%;
margin-bottom: 54px;
diff --git a/src/pages/Discover/index.tsx b/src/pages/Discover/index.tsx
index f483bd21..ae395b79 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 06f18500..8a049304 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) => (
{
+ return (
+ Error Status}>
+ }>
+
+
+
+ );
+};
+
const SearchResultsContent = () => {
const [selectedTab, setSelectedTab] = useState('전체');
const [searchParams] = useSearchParams();
@@ -34,8 +44,15 @@ const SearchResultsContent = () => {
const searchArtistLen = artistsData.length;
const categoryList = ['전체', '작품', '작가'];
+ const { isModalOpen, setIsModalOpen } = useSearchModalStore();
+
+ // 검색어 바꿔 새로 검색 시 검색 모달 닫음
+ useEffect(() => {
+ setIsModalOpen(false);
+ }, [searchQuery]);
+
const goBack = () => {
- navigate(RouterPath.categories);
+ navigate(-1);
};
const handleTabClick = (tab: string) => {
@@ -44,11 +61,9 @@ const SearchResultsContent = () => {
return (
-
-
-
+
+ {isModalOpen && }
-
{selectedTab === '전체' && (
@@ -89,28 +104,12 @@ const SearchResultsContent = () => {
);
};
-const SearchResults = () => {
- return (
- Error Status}>
- }>
-
-
-
- );
-};
-
export default SearchResults;
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 00000000..b547599d
--- /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;
diff --git a/src/styles/globalStyles.ts b/src/styles/globalStyles.ts
index 889c2063..96df705c 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')};
`;