Add CommonHeader and SearchBar

This commit is contained in:
choibk 2025-12-16 23:31:17 +09:00
parent 7f271ec0e7
commit 5481e62a38
22 changed files with 1394 additions and 134 deletions

909
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -30,6 +30,7 @@
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24",
"globals": "^16.5.0",
"sass-embedded": "^1.97.0",
"typescript": "~5.9.3",
"typescript-eslint": "^8.46.4",
"vite": "^7.2.4"

View File

@ -1,42 +0,0 @@
#root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.react:hover {
filter: drop-shadow(0 0 2em #61dafbaa);
}
@keyframes logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@media (prefers-reduced-motion: no-preference) {
a:nth-of-type(2) .logo {
animation: logo-spin infinite 20s linear;
}
}
.card {
padding: 2em;
}
.read-the-docs {
color: #888;
}

View File

@ -1,14 +1,15 @@
import React from 'react'
// import React from 'react'
import { BrowserRouter, Routes, Route } from 'react-router-dom'
import MainPage from '@pages/index'
import AboutPage from '@pages/about'
// Page Compenents
import MainPage from '@pages/index/index'
function App() {
return (
<BrowserRouter>
<Routes>
<Route index path='/' element={<MainPage />}></Route>
<Route index path='/about' element={<AboutPage />}></Route>
</Routes>
</BrowserRouter>
)

View File

@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.5 21C16.7467 21 21 16.7467 21 11.5C21 6.25329 16.7467 2 11.5 2C6.25329 2 2 6.25329 2 11.5C2 16.7467 6.25329 21 11.5 21Z" stroke="#101828" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M22 22L20 20" stroke="#101828" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 430 B

View File

@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.5 21C16.7467 21 21 16.7467 21 11.5C21 6.25329 16.7467 2 11.5 2C6.25329 2 2 6.25329 2 11.5C2 16.7467 6.25329 21 11.5 21Z" stroke="#101828" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M22 22L20 20" stroke="#101828" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 430 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 335 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

View File

@ -0,0 +1,157 @@
// COLOR WHITE
$color-white-000: #ffffff;
$color-white-100: #ececec;
$color-white-200: #f2f4f6;
$color-white-300: #d9d9d9;
$color-white-400: #a8a8a8;
$color-white-500: #9c9c9b;
$color-white-600: #777777;
$color-white-700: #667085;
// COLOR GRAY
$color-gray-000: #fcfcfd;
$color-gray-050: #f9fafb;
$color-gray-100: #f2f4f7;
$color-gray-200: #e4e7ec;
$color-gray-300: #d0d5dd;
$color-gray-400: #98a2b3;
$color-gray-500: #667085;
$color-gray-600: #475467;
$color-gray-700: #344054;
$color-gray-800: #1d2939;
$color-gray-900: #101828;
// COLOR ERROR
$color-error-000: #fffbfa;
$color-error-050: #fef3f2;
$color-error-100: #fee4e2;
$color-error-200: #fecdca;
$color-error-300: #fda29b;
$color-error-400: #f97066;
$color-error-500: #f04438;
$color-error-600: #d92d20;
$color-error-700: #b4231b;
$color-error-800: #912018;
$color-error-900: #7a271a;
// COLOR WARNING
$color-warning-000: #fffcf5;
$color-warning-050: #fffaeb;
$color-warning-100: #fef0c7;
$color-warning-200: #fedf89;
$color-warning-300: #fec84b;
$color-warning-400: #fdb022;
$color-warning-500: #f79009;
$color-warning-600: #dc6803;
$color-warning-700: #b54708;
$color-warning-800: #93380d;
$color-warning-900: #7a2e0e;
// COLOR SUCCESS
$color-success-000: #f6fef9;
$color-success-050: #ecfdf3;
$color-success-100: #d1fadf;
$color-success-200: #a6f4c5;
$color-success-300: #6ce9a6;
$color-success-400: #32d583;
$color-success-500: #12b76a;
$color-success-600: #039855;
$color-success-700: #027a48;
$color-success-800: #05603a;
$color-success-900: #054f31;
// COLOR PINK
$color-pink-000: #fef5fb;
$color-pink-050: #fdf2fa;
$color-pink-100: #fce7f6;
$color-pink-200: #fcceee;
$color-pink-300: #faa7e0;
$color-pink-400: #f670c7;
$color-pink-500: #ee46bc;
$color-pink-600: #dd2590;
$color-pink-700: #c11574;
$color-pink-800: #9e165f;
$color-pink-900: #851651;
// COLOR ROSE
$color-rose-000: #fff5f6;
$color-rose-050: #fff1f3;
$color-rose-100: #ffe4e8;
$color-rose-200: #fecdd6;
$color-rose-300: #fea3b4;
$color-rose-400: #fd6f8e;
$color-rose-500: #f63d68;
$color-rose-600: #e31b54;
$color-rose-700: #c01048;
$color-rose-800: #a11043;
$color-rose-900: #89123e;
// COLOR ORANGE
$color-orange-000: #fffaf5;
$color-orange-050: #fff6ed;
$color-orange-100: #ffead5;
$color-orange-200: #fddcab;
$color-orange-300: #feb273;
$color-orange-400: #fd853a;
$color-orange-500: #fb6514;
$color-orange-600: #ec4a0a;
$color-orange-700: #c4320a;
$color-orange-800: #9c2a10;
$color-orange-900: #7e2410;
// COLOR BLUE LIGHT
$color-lightBlue-000: #f5fbff;
$color-lightBlue-050: #f0f9ff;
$color-lightBlue-100: #e0f2fe;
$color-lightBlue-200: #b9e6fe;
$color-lightBlue-300: #7cd4fd;
$color-lightBlue-400: #36bffa;
$color-lightBlue-500: #0ba5ec;
$color-lightBlue-600: #0086c9;
$color-lightBlue-700: #026aa2;
$color-lightBlue-800: #065986;
$color-lightBlue-900: #0b4a6f;
// COLOR BLUE
$color-blue-000: #f5faff;
$color-blue-050: #eff8ff;
$color-blue-100: #d1e9ff;
$color-blue-200: #b2ddff;
$color-blue-300: #84caff;
$color-blue-400: #53b1fd;
$color-blue-500: #2e90fa;
$color-blue-600: #1570ef;
$color-blue-700: #175cd3;
$color-blue-800: #1849a9;
$color-blue-900: #194185;
// COLOR INDIGO
$color-indigo-000: #f5f8ff;
$color-indigo-050: #eef4ff;
$color-indigo-100: #e0eaff;
$color-indigo-200: #c7d7fe;
$color-indigo-300: #a4bcfd;
$color-indigo-400: #8098f9;
$color-indigo-500: #6172f3;
$color-indigo-600: #444ce7;
$color-indigo-700: #3538cd;
$color-indigo-800: #2d31a6;
$color-indigo-900: #2d3282;
// COLOR PUPPLE
$color-pupple-000: #fafaff;
$color-pupple-050: #f4f3ff;
$color-pupple-100: #ebe9fe;
$color-pupple-200: #d9d6fe;
$color-pupple-300: #bd84fe;
$color-pupple-350: #aaa0ff;
$color-pupple-400: #9b8afb;
$color-pupple-500: #7a5af8;
$color-pupple-600: #6938ef;
$color-pupple-700: #5925dc;
$color-pupple-750: #6157b8;
$color-pupple-800: #4a1fb8;
$color-pupple-900: #3e1c96;
// COLOR BLACK
$color-black-900: #000000;

View File

@ -0,0 +1,5 @@
// font-family: 'Noto Sans KR', sans-serif;
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@100;200;300;400;500;600;700;800;900&display=swap');
// font-family: 'Public Sans', sans-serif;
@import url('https://fonts.googleapis.com/css2?family=Public+Sans:wght@100;200;300;400;500;600;700;800;900&display=swap');

View File

@ -0,0 +1,12 @@
@forward './color.scss';
@forward './font.scss';
* {
padding: 0;
margin: 0;
box-sizing: border-box;
font-family: 'Noto Sans KR', sans-serif;
font-weight: 400;
}

View File

@ -0,0 +1,59 @@
.header {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
height: 56px;
padding: 0 16px;
&__logoBox {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
gap: 8px;
cursor: pointer;
&__logo {
height: 75%;
}
&__title {
font-size: 24px;
font-weight: 600;
}
}
&__profileBox {
display: flex;
align-items: center;
justify-content: center;
gap: 16px;
&__button {
display: flex;
align-items: center;
justify-content: center;
outline: none;
cursor: pointer;
padding: 6px 10px;
border: 1px solid $color-white-300;
border-radius: 4px;
color: $color-white-600;
background-color: $color-white-000;
}
&__userName {
color: $color-white-600;
font-weight: 400;
font-size: 15px;
}
}
}

View File

@ -0,0 +1,36 @@
import { useNavigate } from 'react-router-dom'
import styles from './CommonHeader.module.scss'
import logoImage from '@/assets/images/image-logo.png'
function CommonHeader() {
const navigate = useNavigate()
// 북마크 페이지로 이동
const moveToPage = (filter: string) => {
if (filter === 'main') {
navigate('/')
} else {
navigate('/bookmark')
}
}
return (
<header className={styles.header}>
<div className={styles.header__logoBox} onClick={() => moveToPage('main')}>
<img
src={logoImage}
alt="PhotoSplash logo"
className={styles.header__logoBox__logo}
/>
<span className={styles.header__logoBox__title}>PhotoSplash</span>
</div>
<div className={styles.header__profileBox}>
<button className={styles.header__profileBox__button}></button>
<button className={styles.header__profileBox__button} onClick={() => moveToPage('bookmark')}>
</button>
<span className={styles.header__profileBox__userName}>sokuree | choibk@sokuree.com</span>
</div>
</header>
)
}
export default CommonHeader

View File

@ -0,0 +1,48 @@
.searchBar {
display: flex;
align-items: center;
gap: 28px;
&__search {
display: flex;
align-items: center;
justify-content: flex-start;
width: 50vw;
padding: 14px;
gap: 16px;
border-radius: 8px;
border: 1px solid $color-gray-300;
background-color: $color-white-000;
box-shadow: 0px 2px 8px 0px rgba(228, 231, 236, 0.7);
&:focus-within {
border-radius: 8px;
border: 1px solid #2e90fa;
background-color: $color-white-000;
box-shadow: 0px 0px 8px 0px rgba(21, 112, 239, 0.3);
.input {
color: $color-gray-800;
}
}
&__input {
outline: none;
border: none;
width: 100%;
font-size: 17px;
font-weight: 500;
line-height: 17px;
color: $color-gray-500;
}
img {
cursor: pointer;
}
}
}

View File

@ -0,0 +1,49 @@
// import { useState } from 'react'
// import { useRecoilState } from 'recoil'
import iconImage from '@/assets/images/icon-search.svg'
// import { searchState } from '@/recoil/atoms/searchState'
// import { pageState } from '@/recoil/atoms/pageState'
import styles from './CommonSearchBar.module.scss'
function CommonSearchBar() {
// const [, setSearch] = useRecoilState(searchState)
// const [, setPage] = useRecoilState(pageState)
// const [text, setText] = useState('')
// const onChange = (event: React.ChangeEvent<HTMLInputElement>) => {
// setText(event.target.value)
// }
// const onSearch = () => {
// if (text === '') {
// // input 태그 안에 빈 값으로 검색하였을 때 => searching default value
// setSearch('Korea')
// setPage(1)
// } else {
// setSearch(text) // 작성한 Input Value 값 할당
// setPage(1)
// }
// }
// const handleKeyDown = (event: React.KeyboardEvent) => {
// if (event.key === 'Enter') {
// if (text === '') {
// // input 태그 안에 빈 값으로 검색하였을 때 => searching default value
// setSearch('Korea')
// setPage(1)
// } else {
// setSearch(text) // 작성한 Input Value 값 할당
// setPage(1)
// }
// }
// }
return (
<div className={styles.searchBar}>
<div className={styles.searchBar__search}>
<input type="text" placeholder="찾으실 이미지를 검색하세요." className={styles.searchBar__search__input} /*value={text} onChange={onChange} onKeyDown={handleKeyDown} */ />
<img src={iconImage} alt="" /* onClick={onSearch} */ />
</div>
</div>
)
}
export default CommonSearchBar

View File

@ -1,68 +0,0 @@
:root {
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
}
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}

View File

@ -1,6 +1,5 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
// import './index.css'
import App from './App.tsx'
createRoot(document.getElementById('root')!).render(

View File

@ -1,9 +0,0 @@
import React from 'react'
function index() {
return (
<div>about .</div>
)
}
export default index

View File

@ -1,9 +0,0 @@
import React from 'react'
function index() {
return (
<div> .</div>
)
}
export default index

30
src/pages/index/index.tsx Normal file
View File

@ -0,0 +1,30 @@
import CommonHeader from "@/components/common/header/CommonHeader"
import styles from "./styles/index.module.scss"
import CommonSearchBar from "@/components/common/searchBar/CommonSearchBar"
function index() {
return (
<div className={styles.page}>
{/*공통 헤더 UI 부분*/}
<CommonHeader />
{/*공통 네비게이션 UI 부분*/}
<div className={styles.page__contents}>
<div className={styles.page__contents__introBox}>
<div className={styles.wrapper}>
<span className={styles.wrapper__title}>PhotoSplash</span>
<span className={styles.wrapper__desc}>
. <br />
.
</span>
{/*검색창 UI 부분*/}
<CommonSearchBar />
</div>
</div>
<div className={styles.page__contents__imageBox}></div>
</div>
{/* 공통 푸터 UI 부분 */}
</div>
)
}
export default index

View File

@ -0,0 +1,72 @@
$HEADER-HEIGHT: 50px;
$FOOTER-HEIGHT: 50px;
$NAVIGATION-HEIGHT: 50px;
.page {
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
width: 100%;
height: 100vh;
border-radius: 8px;
&__contents {
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
width: 100%;
height: calc(100vh - $HEADER-HEIGHT - $NAVIGATION-HEIGHT - $FOOTER-HEIGHT);
&__introBox {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 40%;
background-image: url("/src/assets/images/image-intro.jpg");
background-size: cover;
background-position: 100% 15%;
.wrapper {
display: flex;
flex-direction: column;
&__title {
margin-bottom: 4px;
color: $color-white-000;
font-size: 3rem;
font-weight: 700;
}
&__desc {
color: $color-white-000;
margin-bottom: 32px;
}
}
}
&__imageBox {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 60%;
flex-wrap: wrap;
padding: 16px 60px;
gap: 16px;
overflow-y: scroll;
}
}
}

View File

@ -30,7 +30,9 @@ export default defineConfig({
css: {
preprocessorOptions: {
scss: {
additionalData: '@import "./src/assets/styles/main.scss";'
additionalData: `@use "${fileURLToPath(
new URL('./src/assets/styles/main.scss', import.meta.url)
)}" as *;`,
},
},
},