diff --git a/src/app/page.tsx b/src/app/page.tsx index f0ebe30e..783ee479 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -7,7 +7,6 @@ import Flex from '@shared/flex/Flex'; import Header from '@shared/header/Header'; import ProductArticle from '@shared/product-article/ProductArticle'; import Radio from '@shared/radio/Radio'; -import SearchBar from '@shared/search-bar/SearchBar'; import Spacing from '@shared/spacing/Spacing'; import Text from '@shared/text/Text'; @@ -134,7 +133,6 @@ export default function Home() {
-
diff --git a/src/app/search/layout.tsx b/src/app/search/layout.tsx new file mode 100644 index 00000000..60f80764 --- /dev/null +++ b/src/app/search/layout.tsx @@ -0,0 +1,7 @@ +function SearchLayout({ children }: { children: React.ReactNode }) { + return ( +
{children}
+ ); +} + +export default SearchLayout; diff --git a/src/app/search/page.module.scss b/src/app/search/page.module.scss new file mode 100644 index 00000000..b1196138 --- /dev/null +++ b/src/app/search/page.module.scss @@ -0,0 +1,14 @@ +.mainContainer { + .filterWrapper { + display: flex; + align-items: center; + justify-content: space-between; + margin: 13px 0 24px; + } + + .productArticleContainer { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 12px; + } +} diff --git a/src/app/search/page.tsx b/src/app/search/page.tsx new file mode 100644 index 00000000..22294edb --- /dev/null +++ b/src/app/search/page.tsx @@ -0,0 +1,99 @@ +'use client'; + +import { useState, useRef } from 'react'; + +import classNames from 'classnames/bind'; + +import Text from '@/components/shared/text/Text'; +import Drawer from '@shared/drawer/Drawer'; +import Dropdown from '@shared/dropdown/Dropdown'; +import Header from '@shared/header/Header'; +import ProductArticle from '@shared/product-article/ProductArticle'; +import SearchBar from '@shared/search-bar/SearchBar'; +import Spacing from '@shared/spacing/Spacing'; + +import styles from './page.module.scss'; + +const cx = classNames.bind(styles); +const productArticleData = [ + { + brand: '카믹스', + category: '코팅제', + id: 1, + img: '/assets/profile.JPG', + name: '아머올 세차용품 스피드 왁스 스프레이 500ml스피드 왁스 스프레이 500ml', + warningLevel: 'warning', + }, + { + brand: '카믹스', + category: '코팅제', + id: 2, + img: '/assets/profile.JPG', + name: '아머올 세차용품 스피드 왁스 스프레이 500ml스피드 왁스 스프레이 500ml', + warningLevel: 'warning', + }, + { + brand: '카믹스', + category: '코팅제', + id: 3, + img: '/assets/profile.JPG', + name: '아머올 세차용품 스피드 왁스 스프레이 500ml스피드 왁스 스프레이 500ml', + warningLevel: 'warning', + }, + { + brand: '카믹스', + category: '코팅제', + id: 4, + img: '/assets/profile.JPG', + name: '아머올 세차용품 스피드 왁스 스프레이 500ml스피드 왁스 스프레이 500ml', + warningLevel: 'warning', + }, +]; + +const options = [ + { label: '조회순', value: '1' }, + { label: '위반제품순', value: '2' }, + { label: '최신순', value: '3' }, + { label: '추천순', value: '4' }, +]; + +function SearchPage() { + const dropdownRef = useRef(null); + + const [selectedLabel, setSelectedLabel] = useState(options[0].label); + const [isOpenFilterDrawer, setIsOpenFilterDrawer] = useState(false); + + const handleFilterClick = () => { + setIsOpenFilterDrawer((prev) => { return !prev; }); + }; + + return ( + <> +
+ +
+ +
+ {`총 ${productArticleData.length}개`} + +
+
+ {productArticleData.map((item) => { + return ; + })} +
+
+ { setIsOpenFilterDrawer(false); }}> + {/* 필터 내용 아코디언 */} +

Filter Content

+
+ + ); +} +export default SearchPage; diff --git a/src/components/icons/Filter.tsx b/src/components/icons/Filter.tsx index 7d85b03e..7f994615 100644 --- a/src/components/icons/Filter.tsx +++ b/src/components/icons/Filter.tsx @@ -4,21 +4,17 @@ interface FilterProps { width?: number height?: number color?: Colors + onClick?: () => void } -function Filter({ width = 18, height = 16, color = 'gray200' }: FilterProps) { +function Filter({ + width = 18, height = 16, color = 'gray400', onClick, +}: FilterProps) { return ( - - - - - - - - - - + + + ); } diff --git a/src/components/icons/Search.tsx b/src/components/icons/Search.tsx index f241c80b..d6c24af0 100644 --- a/src/components/icons/Search.tsx +++ b/src/components/icons/Search.tsx @@ -5,7 +5,7 @@ interface SearchProps { color?: Colors } -function Search({ size = 18, color = 'black' }: SearchProps) { +function Search({ size = 17.5, color = 'tertiary400' }: SearchProps) { return ( diff --git a/src/components/shared/dropdown/Dropdown.module.scss b/src/components/shared/dropdown/Dropdown.module.scss index aba14c71..14de3f17 100644 --- a/src/components/shared/dropdown/Dropdown.module.scss +++ b/src/components/shared/dropdown/Dropdown.module.scss @@ -12,7 +12,7 @@ &.favorite { width: 80px; - padding: 4px 4px 4px 8px; + padding: 4px 8px; border-radius: 8px; background-color: var(--tertiary-200); } diff --git a/src/components/shared/dropdown/Dropdown.stories.tsx b/src/components/shared/dropdown/Dropdown.stories.tsx index f0065d4a..4f6f0dd5 100644 --- a/src/components/shared/dropdown/Dropdown.stories.tsx +++ b/src/components/shared/dropdown/Dropdown.stories.tsx @@ -18,7 +18,7 @@ type Story = StoryObj; export const YoutubeVideo: Story = { args: { type: 'favorite', - label: '최신순', + selectedLabel: '최신순', options: [ { label: '최신순', value: 'latest' }, { label: '오래된순', value: 'oldest' }, @@ -27,3 +27,18 @@ export const YoutubeVideo: Story = { value: 'latest', }, }; + +export const SearchProduct: Story = { + args: { + type: 'favorite', + selectedLabel: '조회순', + options: [ + { label: '조회순', value: 'Viewed' }, + { label: '위반제품순', value: 'Violated' }, + { label: '최신순', value: 'latest' }, + { label: '추천순', value: 'Recommend' }, + ], + placeholder: '조회순', + value: 'Viewed', + }, +}; diff --git a/src/components/shared/dropdown/Dropdown.tsx b/src/components/shared/dropdown/Dropdown.tsx index 9d31bb72..b6065497 100644 --- a/src/components/shared/dropdown/Dropdown.tsx +++ b/src/components/shared/dropdown/Dropdown.tsx @@ -19,16 +19,18 @@ interface Option { interface DropdownProps extends InputHTMLAttributes { options: Option[] - label: string | number - value: string | number + selectedLabel: string | number + value?: string | number type: 'favorite' + setSelectedLabel: React.Dispatch> } const Dropdown = forwardRef(({ - label, + selectedLabel, type, options, value, + setSelectedLabel, ...props }, ref) => { const [isOpen, setIsOpen] = useState(false); @@ -41,12 +43,17 @@ const Dropdown = forwardRef(({ setIsOpen(false); }; + const handleLabelClick = (newLabel: string) => { + setSelectedLabel(() => { return newLabel; }); + closeDropdownMenu(); + }; + const containerRef = useOutsideClick(closeDropdownMenu); return (
{isOpen && ( @@ -54,8 +61,21 @@ const Dropdown = forwardRef(({ {options.map((option) => { return (
  • - -
  • diff --git a/src/components/shared/header/Header.tsx b/src/components/shared/header/Header.tsx index bc69f31d..47b8a510 100644 --- a/src/components/shared/header/Header.tsx +++ b/src/components/shared/header/Header.tsx @@ -7,7 +7,7 @@ import { HeaderProps } from './types/headerType'; export default function Header({ isDisplayLogo = true, className, - children, isTransparent = false, displayRightIconType, + children, isTransparent = false, displayRightIconType, onFilterClick, }:HeaderProps) { const cx = classNames.bind(styles); @@ -18,6 +18,7 @@ export default function Header({ diff --git a/src/components/shared/header/headerItems/RightIcon.tsx b/src/components/shared/header/headerItems/RightIcon.tsx index 3aa43409..82b1ea9b 100644 --- a/src/components/shared/header/headerItems/RightIcon.tsx +++ b/src/components/shared/header/headerItems/RightIcon.tsx @@ -5,6 +5,7 @@ import { useState } from 'react'; import Link from 'next/link'; import FilledHeart from '@components/icons/FilledHeart'; +import Filter from '@components/icons/Filter'; import Heart from '@components/icons/Heart'; import Search from '@components/icons/Search'; import Share from '@components/icons/Share'; @@ -13,7 +14,7 @@ import { RightIconProps } from '../types/headerType'; function RightIcon({ className, - displayRightIconType, + displayRightIconType, onFilterClick, }: RightIconProps) { const [isSaved, setIsSaved] = useState(false); @@ -21,6 +22,15 @@ function RightIcon({ setIsSaved((prev) => { return !prev; }); }; + if (displayRightIconType === 'filter') { + return ( +
  • + {/* 필터가 적용됐을 때만 필터 아이콘이 바뀜 */} + +
  • + ); + } + if (displayRightIconType === 'search') { return (
  • diff --git a/src/components/shared/header/types/headerType.ts b/src/components/shared/header/types/headerType.ts index 9e06c145..c058278a 100644 --- a/src/components/shared/header/types/headerType.ts +++ b/src/components/shared/header/types/headerType.ts @@ -6,12 +6,13 @@ interface LeftIconProps { interface RightIconProps { className?: string; displayRightIconType?:string; - + onFilterClick?: () => void; } interface HeaderProps extends LeftIconProps { isTransparent?: boolean; displayRightIconType?:string; + onFilterClick?: () => void; } export type { HeaderProps, LeftIconProps, RightIconProps }; diff --git a/src/components/shared/product-article/ProductArticle.module.scss b/src/components/shared/product-article/ProductArticle.module.scss index 56f56274..1fa48716 100644 --- a/src/components/shared/product-article/ProductArticle.module.scss +++ b/src/components/shared/product-article/ProductArticle.module.scss @@ -4,8 +4,9 @@ flex-direction: column; width: 100%; overflow: hidden; + border: 1px solid var(--gray); border-radius: 15px; - box-shadow: 0 0 0 1px var(--gray); + box-shadow: 0 4px 7px 1px rgba($color: #000, $alpha: 5%); .imgBox { height: 100px; @@ -16,8 +17,8 @@ .icon { position: absolute; - top: 10px; - left: 10px; + top: 8px; + right: 8px; } } diff --git a/src/components/shared/search-bar/SearchBar.module.scss b/src/components/shared/search-bar/SearchBar.module.scss index 45926617..7e43d01e 100644 --- a/src/components/shared/search-bar/SearchBar.module.scss +++ b/src/components/shared/search-bar/SearchBar.module.scss @@ -3,14 +3,10 @@ box-sizing: border-box; align-items: center; width: 100%; - padding: 13px; + padding: 15px 19px; border-radius: 10px; - gap: 10px; - box-shadow: 1px 3px 4px 0 rgba($color: #000, $alpha: 3%); - - &.dark { - background-color: var(--gray); - } + gap: 11.5px; + background-color: var(--tertiary-200); input { flex-grow: 1; @@ -21,18 +17,17 @@ outline: inherit; background-color: inherit; color: var(--black); - font-size: 14px; + font-size: 16px; &::placeholder { - color: var(--gray-200); - font-size: 12px; - font-weight: 400; + color: var(--tertiary-400); } } button { - padding: 3px; - padding-bottom: 0; + svg { + vertical-align: top; + } } /* input[type:search] x 버튼 없애기 */ diff --git a/src/components/shared/search-bar/SearchBar.tsx b/src/components/shared/search-bar/SearchBar.tsx index 39b28c8c..71d84a57 100644 --- a/src/components/shared/search-bar/SearchBar.tsx +++ b/src/components/shared/search-bar/SearchBar.tsx @@ -5,26 +5,29 @@ import { useState } from 'react'; import classNames from 'classnames/bind'; import Delete from '@components/icons/Delete'; -import Filter from '@components/icons/Filter'; +// import Filter from '@components/icons/Filter'; import Search from '@components/icons/Search'; import styles from './SearchBar.module.scss'; const cx = classNames.bind(styles); -function SearchBar({ isMainPage = true }: { isMainPage?: boolean }) { +function SearchBar() { const [keyword, setKeyword] = useState(''); const handleSearch = () => { - // console.log('검색완료'); - }; - - const handleFilterDrawer = () => { - // console.log('FilterDrawer 열기'); + // eslint-disable-next-line no-console + console.log('검색완료'); }; return ( -
    { return e.preventDefault(); }}> + { + e.preventDefault(); + handleSearch(); + }} + > )} - ); }