From 453ec15872cd33c7c72ee0aca366a20921b64f7e Mon Sep 17 00:00:00 2001 From: sryung Date: Thu, 14 Dec 2023 15:27:06 +0900 Subject: [PATCH] =?UTF-8?q?[=F0=9F=8E=80=20:=20style]=20=EC=82=AC=EC=9D=B4?= =?UTF-8?q?=EB=93=9C=20=EB=A9=94=EB=89=B4=20+=20=ED=99=88=20=EC=8A=A4?= =?UTF-8?q?=ED=83=80=EC=9D=BC=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 공통 컴포넌트 관리 및 스타일 개편 - styles 레포 정리 --- src/App.tsx | 6 +- src/assets/images/i-window1.svg | 4 + src/assets/images/i-window2.svg | 4 + src/assets/images/i-window3.svg | 5 ++ src/components/layout.tsx | 4 +- src/components/menu.tsx | 86 ++++++++++++++----- src/components/post-tweet-form.tsx | 2 +- src/components/search.tsx | 33 +++++++ src/components/timeline.tsx | 6 +- src/components/tweet.tsx | 2 +- src/components/user-timeline.tsx | 6 +- src/components/window-top.tsx | 16 ++++ src/routes/home.tsx | 8 +- src/routes/profile.tsx | 29 ++++--- src/routes/search-result.tsx | 3 + src/styles/App.ts | 10 +-- src/styles/auth.ts | 8 +- src/styles/button.ts | 2 +- src/styles/components/menu.ts | 30 ------- src/styles/layout.ts | 26 ++---- src/styles/menu.ts | 67 +++++++++++++++ src/styles/popup.ts | 47 +++++----- .../{components => }/post-tweet-form.ts | 19 ++-- src/styles/profile.ts | 8 +- src/styles/search.ts | 12 +++ src/styles/timeline.ts | 8 ++ src/styles/{components => }/tweet.ts | 5 +- src/styles/window.ts | 38 ++++++++ 28 files changed, 352 insertions(+), 142 deletions(-) create mode 100644 src/assets/images/i-window1.svg create mode 100644 src/assets/images/i-window2.svg create mode 100644 src/assets/images/i-window3.svg create mode 100644 src/components/search.tsx create mode 100644 src/components/window-top.tsx create mode 100644 src/routes/search-result.tsx delete mode 100644 src/styles/components/menu.ts create mode 100644 src/styles/menu.ts rename src/styles/{components => }/post-tweet-form.ts (88%) create mode 100644 src/styles/search.ts create mode 100644 src/styles/timeline.ts rename src/styles/{components => }/tweet.ts (86%) create mode 100644 src/styles/window.ts diff --git a/src/App.tsx b/src/App.tsx index c8d4a7f..229bf50 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -7,7 +7,7 @@ import Profile from './routes/profile.tsx'; import Auth from './routes/auth.tsx'; import Layout from './components/layout.tsx'; import LoadingScreen from './components/loading-screen.tsx'; -import * as S from './styles/App.ts'; +import * as S from './styles/app.ts'; const router = createBrowserRouter([ { @@ -44,10 +44,10 @@ function App() { init(); }, []); return ( - + <> {isLoading ? : } - + ); } diff --git a/src/assets/images/i-window1.svg b/src/assets/images/i-window1.svg new file mode 100644 index 0000000..5e4dd71 --- /dev/null +++ b/src/assets/images/i-window1.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/i-window2.svg b/src/assets/images/i-window2.svg new file mode 100644 index 0000000..61cc2e1 --- /dev/null +++ b/src/assets/images/i-window2.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/i-window3.svg b/src/assets/images/i-window3.svg new file mode 100644 index 0000000..deeaa14 --- /dev/null +++ b/src/assets/images/i-window3.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/components/layout.tsx b/src/components/layout.tsx index 613dbb6..f162be4 100644 --- a/src/components/layout.tsx +++ b/src/components/layout.tsx @@ -1,12 +1,14 @@ import { Outlet } from 'react-router-dom'; +import Wrapper from '../styles/layout.ts'; import Menu from './menu.tsx'; -import { Wrapper } from '../styles/layout.ts'; +import Search from './search.tsx'; export default function Layout() { return ( + ); } diff --git a/src/components/menu.tsx b/src/components/menu.tsx index 4902446..6ffde0b 100644 --- a/src/components/menu.tsx +++ b/src/components/menu.tsx @@ -1,35 +1,77 @@ import { Link, useNavigate } from 'react-router-dom'; +import { useEffect, useState } from 'react'; import { auth } from '../firebase.ts'; -import * as S from '../styles/components/menu.ts'; +import WindowTop from './window-top.tsx'; +import * as W from '../styles/window.ts'; +import * as S from '../styles/menu.ts'; +import * as P from '../styles/popup.ts'; +import ImageComputer from '../assets/images/logo-small.png'; import { ReactComponent as IconUser } from '../assets/images/i-user.svg'; import { ReactComponent as IconHome } from '../assets/images/i-home.svg'; import { ReactComponent as IconLogout } from '../assets/images/i-arrowleft.svg'; export default function Menu() { const navigate = useNavigate(); + const [logoutPopup, setLogoutPopup] = useState(false); + const toggleLogoutPopup = () => { + setLogoutPopup(!logoutPopup); + }; + useEffect(() => { + const handleEscKey = (e: KeyboardEvent) => { + if (e.key === 'Escape') { + setLogoutPopup(false); + } + }; + document.addEventListener('keydown', handleEscKey); + return () => { + document.removeEventListener('keydown', handleEscKey); + }; + }, [logoutPopup]); const onLogout = async () => { - // eslint-disable-next-line no-restricted-globals - const ok = confirm('로그아웃 하시겠습니까?'); - if (ok) { - await auth.signOut(); - navigate('/auth'); - } + await auth.signOut(); + navigate('/auth'); }; return ( - - - - - - - - - - - - - - - + + + + + Zwitter + + + + + + + 홈 + + + + + 프로필 + + + + + 로그아웃 + + + {logoutPopup ? ( + + + 로그아웃 하시겠습니까? + + + 예 + + + 아니요 + + + + + ) : null} + + ); } diff --git a/src/components/post-tweet-form.tsx b/src/components/post-tweet-form.tsx index 5823803..f8ce76e 100644 --- a/src/components/post-tweet-form.tsx +++ b/src/components/post-tweet-form.tsx @@ -2,7 +2,7 @@ import React, { useState } from 'react'; import { addDoc, collection, updateDoc } from 'firebase/firestore'; import { getDownloadURL, ref, uploadBytes } from 'firebase/storage'; import { auth, db, storage } from '../firebase.ts'; -import * as S from '../styles/components/post-tweet-form.ts'; +import * as S from '../styles/post-tweet-form.ts'; import { ReactComponent as IconPhoto } from '../assets/images/i-photo.svg'; export default function PostTweetForm() { diff --git a/src/components/search.tsx b/src/components/search.tsx new file mode 100644 index 0000000..11024b3 --- /dev/null +++ b/src/components/search.tsx @@ -0,0 +1,33 @@ +import React, { useState } from 'react'; +import WindowTop from './window-top.tsx'; +import * as W from '../styles/window.ts'; +import * as S from '../styles/search.ts'; +import LoadingScreen from './loading-screen.tsx'; + +export default function Search() { + const [error, setError] = useState(''); + const [isLoading, setLoading] = useState(false); + const [searchKeyword, setSearchKeyword] = useState(''); + const onChange = (e: React.ChangeEvent) => { + setSearchKeyword(e.target.value); + }; + return isLoading ? ( + + ) : ( + + + + + + {error ?

{error}

: null} +
+
+ ); +} diff --git a/src/components/timeline.tsx b/src/components/timeline.tsx index 85b08f4..17a6af5 100644 --- a/src/components/timeline.tsx +++ b/src/components/timeline.tsx @@ -9,7 +9,7 @@ import { import { Unsubscribe } from 'firebase/auth'; import { db } from '../firebase.ts'; import Tweet from './tweet.tsx'; -import * as S from '../styles/layout.ts'; +import TimelineWrapper from '../styles/timeline.ts'; import ITweet from '../interfaces/ITweet.ts'; export default function Timeline() { @@ -45,10 +45,10 @@ export default function Timeline() { }; }, []); return ( - + {tweets.map((tweet) => ( ))} - + ); } diff --git a/src/components/tweet.tsx b/src/components/tweet.tsx index 71cc14d..4749682 100644 --- a/src/components/tweet.tsx +++ b/src/components/tweet.tsx @@ -3,7 +3,7 @@ import { deleteObject, ref } from 'firebase/storage'; import { auth, db, storage } from '../firebase.ts'; import ITweet from '../interfaces/ITweet.ts'; import FormattedDate from '../utils/formattedDate.tsx'; -import * as S from '../styles/components/tweet.ts'; +import * as S from '../styles/tweet.ts'; export default function Tweet({ id, diff --git a/src/components/user-timeline.tsx b/src/components/user-timeline.tsx index 905a5cb..9034b1c 100644 --- a/src/components/user-timeline.tsx +++ b/src/components/user-timeline.tsx @@ -9,7 +9,7 @@ import { } from 'firebase/firestore'; import { auth, db } from '../firebase.ts'; import Tweet from './tweet.tsx'; -import * as S from '../styles/layout.ts'; +import TimelineWrapper from '../styles/timeline.ts'; import ITweet from '../interfaces/ITweet.ts'; export default function UserTimeline() { @@ -40,10 +40,10 @@ export default function UserTimeline() { fetchTweets(); }, []); return ( - + {tweets.map((tweet) => ( ))} - + ); } diff --git a/src/components/window-top.tsx b/src/components/window-top.tsx new file mode 100644 index 0000000..d791a5b --- /dev/null +++ b/src/components/window-top.tsx @@ -0,0 +1,16 @@ +import * as W from '../styles/window.ts'; +import { ReactComponent as IconWindow1 } from '../assets/images/i-window1.svg'; +import { ReactComponent as IconWindow2 } from '../assets/images/i-window2.svg'; +import { ReactComponent as IconWindow3 } from '../assets/images/i-window3.svg'; + +export default function WindowTop() { + return ( + + + + + + + + ); +} diff --git a/src/routes/home.tsx b/src/routes/home.tsx index 52d36f0..b794528 100644 --- a/src/routes/home.tsx +++ b/src/routes/home.tsx @@ -1,12 +1,14 @@ +import WindowTop from '../components/window-top.tsx'; import PostTweetForm from '../components/post-tweet-form.tsx'; import Timeline from '../components/timeline.tsx'; -import * as S from '../styles/layout.ts'; +import * as W from '../styles/window.ts'; export default function Home() { return ( - + + - + ); } diff --git a/src/routes/profile.tsx b/src/routes/profile.tsx index 36763fe..fe441a7 100644 --- a/src/routes/profile.tsx +++ b/src/routes/profile.tsx @@ -2,7 +2,9 @@ import React, { useState } from 'react'; import { getDownloadURL, ref, uploadBytes } from 'firebase/storage'; import { updateProfile } from 'firebase/auth'; import { auth, storage } from '../firebase.ts'; +import WindowTop from '../components/window-top.tsx'; import UserTimeline from '../components/user-timeline.tsx'; +import * as W from '../styles/window.ts'; import * as S from '../styles/profile.ts'; import { ReactComponent as IconUser } from '../assets/images/i-user.svg'; @@ -24,18 +26,21 @@ export default function Profile() { } }; return ( - - - {avatar ? : } - - - {user?.displayName ?? 'Anonymous'} + + + + + {avatar ? : } + + + {user?.displayName ?? 'Anonymous'} + - + ); } diff --git a/src/routes/search-result.tsx b/src/routes/search-result.tsx new file mode 100644 index 0000000..78cfb5b --- /dev/null +++ b/src/routes/search-result.tsx @@ -0,0 +1,3 @@ +export default function SearchResult() { + return null; +} diff --git a/src/styles/App.ts b/src/styles/App.ts index 231b248..18b7290 100644 --- a/src/styles/App.ts +++ b/src/styles/App.ts @@ -1,4 +1,4 @@ -import { createGlobalStyle, styled } from 'styled-components'; +import { createGlobalStyle } from 'styled-components'; import reset from 'styled-reset'; export const primaryColor = '#FF7AB2'; @@ -6,11 +6,8 @@ export const grayColor = '#A7A7A7'; export const blackColor = '#070707'; export const whiteColor = '#FAFAFA'; -export const Wrapper = styled.div` - display: flex; - justify-content: center; - height: 100vh; -`; +export const LogoTextShadow = (offset: number) => + `${-offset}px 0px ${blackColor}, 0px ${offset}px ${blackColor}, ${offset}px 0px ${blackColor}, 0px ${-offset}px ${blackColor}`; export const GlobalStyles = createGlobalStyle` ${reset}; @@ -34,6 +31,7 @@ export const GlobalStyles = createGlobalStyle` } button { cursor: pointer; + transition: all 0.5s ease; &:hover, &:active { opacity: 0.8; diff --git a/src/styles/auth.ts b/src/styles/auth.ts index e52aee7..ee07d99 100644 --- a/src/styles/auth.ts +++ b/src/styles/auth.ts @@ -1,5 +1,5 @@ import { styled } from 'styled-components'; -import { blackColor, primaryColor, whiteColor } from './App.ts'; +import { LogoTextShadow, blackColor, primaryColor, whiteColor } from './app.ts'; import { LineButton, SolidButton, Input } from './button.ts'; export const Wrapper = styled.div` @@ -28,11 +28,7 @@ export const Image = styled.img` export const Intro = styled.h2` font-size: 64px; color: ${whiteColor}; - text-shadow: - -3px 0px ${blackColor}, - 0px 3px ${blackColor}, - 3px 0px ${blackColor}, - 0px -3px ${blackColor}; + text-shadow: ${LogoTextShadow(3)}; span { color: ${primaryColor}; } diff --git a/src/styles/button.ts b/src/styles/button.ts index 992f853..6a00ebd 100644 --- a/src/styles/button.ts +++ b/src/styles/button.ts @@ -1,5 +1,5 @@ import { styled } from 'styled-components'; -import { blackColor, grayColor, primaryColor, whiteColor } from './App.ts'; +import { blackColor, grayColor, primaryColor, whiteColor } from './app.ts'; const Button = styled.button` width: 100%; diff --git a/src/styles/components/menu.ts b/src/styles/components/menu.ts deleted file mode 100644 index 7938bea..0000000 --- a/src/styles/components/menu.ts +++ /dev/null @@ -1,30 +0,0 @@ -import styled from 'styled-components'; -import { blackColor, primaryColor } from '../App.ts'; - -export const MenuList = styled.ul` - display: flex; - flex-direction: column; - align-items: center; - gap: 20px; -`; - -export const MenuItem = styled.li` - display: flex; - justify-content: center; - align-items: center; - width: 50px; - height: 50px; - border: 2px solid ${blackColor}; - border-radius: 50%; - cursor: pointer; - svg { - width: 30px; - stroke: ${blackColor}; - } - &.log-out { - border-color: ${primaryColor}; - svg { - stroke: ${primaryColor}; - } - } -`; diff --git a/src/styles/layout.ts b/src/styles/layout.ts index 7d78f9e..e6ee4c5 100644 --- a/src/styles/layout.ts +++ b/src/styles/layout.ts @@ -1,26 +1,12 @@ import styled from 'styled-components'; -export const Wrapper = styled.div` +const Wrapper = styled.div` display: grid; - grid-template-columns: 1fr 4fr; + grid-template-columns: minmax(200px, 1fr) 4fr minmax(200px, 1fr); gap: 20px; - width: 100%; - max-width: 860px; - height: 100%; - padding: 50px 0px; + width: calc(100% - 40px); + height: calc(100vh - 40px); + margin: 20px; `; -export const HomeCenterWrapper = styled.div` - position: relative; -`; - -export const TimeLineWrapper = styled.ul` - display: block; - height: calc(100vh - 350px); - margin-top: 250px; - overflow-y: auto; -`; - -export const UserTimeLineWrapper = styled.ul` - display: block; -`; +export default Wrapper; diff --git a/src/styles/menu.ts b/src/styles/menu.ts new file mode 100644 index 0000000..8c74d29 --- /dev/null +++ b/src/styles/menu.ts @@ -0,0 +1,67 @@ +import styled from 'styled-components'; +import { LogoTextShadow, blackColor, primaryColor, whiteColor } from './app.ts'; + +export const Logo = styled.div` + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + margin: 50px 0; +`; + +export const LogoTitle = styled.h1` + color: ${whiteColor}; + font-size: 30px; + text-shadow: ${LogoTextShadow(2)}; + span { + color: ${primaryColor}; + } +`; + +export const LogoImage = styled.img` + width: 40px; + height: 40px; +`; + +export const MenuList = styled.ul` + display: flex; + flex-direction: column; + align-items: center; + gap: 20px; +`; + +export const MenuItem = styled.li` + line-height: 30px; + cursor: pointer; + a { + font-size: 20px; + font-family: 'Ycomputer-Regular', sans-serif; + text-decoration: none; + transition: all 0.5s ease; + } + svg { + width: 24px; + stroke: ${blackColor}; + vertical-align: sub; + transition: all 0.5s ease; + } + &:hover { + a { + color: ${primaryColor}; + } + svg { + stroke: ${primaryColor}; + } + } +`; + +export const Button = styled.button` + border: 0; + background-color: transparent; + color: ${primaryColor}; + font-size: 20px; + font-family: 'Ycomputer-Regular', sans-serif; + svg { + stroke: ${primaryColor}; + } +`; diff --git a/src/styles/popup.ts b/src/styles/popup.ts index 15f0cd6..1d5714d 100644 --- a/src/styles/popup.ts +++ b/src/styles/popup.ts @@ -1,5 +1,6 @@ import { styled } from 'styled-components'; -import { blackColor, grayColor, primaryColor, whiteColor } from './App.ts'; +import { LogoTextShadow, blackColor, primaryColor, whiteColor } from './app.ts'; +import { SolidButton } from './button.ts'; export const PopupWrapper = styled.div` z-index: 100; @@ -29,6 +30,21 @@ export const Popup = styled.div` border-radius: 10px; `; +export const MiniPopup = styled.div` + position: relative; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + width: calc(100vw - 100px); + max-width: 400px; + height: auto; + padding: 30px; + background-color: ${whiteColor}; + border-radius: 6px; + border: 3px solid ${blackColor}; +`; + export const CloseButton = styled.button` position: absolute; top: 20px; @@ -60,11 +76,7 @@ export const Logo = styled.h2` color: ${whiteColor}; font-size: 40px; line-height: 48px; - text-shadow: - -2px 0px ${blackColor}, - 0px 2px ${blackColor}, - 2px 0px ${blackColor}, - 0px -2px ${blackColor}; + text-shadow: ${LogoTextShadow(2)}; span { color: ${primaryColor}; } @@ -82,23 +94,16 @@ export const Title = styled.h3` `; export const Text = styled.p` - text-align: center; - font-size: 14px; - font-weight: 700; - line-height: 30px; padding: 20px 0; + font-size: 18px; + line-height: 30px; + text-align: center; `; -export const Button = styled.button` - display: block; - border: none; - border-top: 1px solid ${grayColor}; - background-color: white; - text-align: center; - color: ${primaryColor}; +export const ButtonWrapper = styled.div` + display: flex; + gap: 10px; width: 100%; - line-height: 30px; - font-weight: 700; - margin-bottom: 10px; - padding-top: 10px; `; + +export const Button = styled(SolidButton)``; diff --git a/src/styles/components/post-tweet-form.ts b/src/styles/post-tweet-form.ts similarity index 88% rename from src/styles/components/post-tweet-form.ts rename to src/styles/post-tweet-form.ts index 1ff5b52..b6989c9 100644 --- a/src/styles/components/post-tweet-form.ts +++ b/src/styles/post-tweet-form.ts @@ -1,15 +1,24 @@ import styled from 'styled-components'; -import { blackColor, grayColor, primaryColor, whiteColor } from '../App.ts'; +import { blackColor, grayColor, primaryColor, whiteColor } from './app.ts'; export const Form = styled.form` - position: absolute; - top: 0; - left: 0; - right: 0; + position: relative; display: flex; flex-direction: column; + flex-shrink: 0; gap: 10px; width: 100%; + padding-bottom: 10px; + &::after { + content: ''; + position: absolute; + bottom: 0; + left: -10px; + right: -10px; + width: calc(100% + 20px); + height: 2px; + background-color: ${blackColor}; + } `; export const TextArea = styled.textarea` diff --git a/src/styles/profile.ts b/src/styles/profile.ts index fe68f0e..88b47db 100644 --- a/src/styles/profile.ts +++ b/src/styles/profile.ts @@ -1,11 +1,13 @@ import { styled } from 'styled-components'; -import { grayColor } from './App.ts'; +import { grayColor } from './app.ts'; -export const Wrapper = styled.div` +export const Avatar = styled.div` display: flex; - align-items: center; flex-direction: column; + justify-content: center; + align-items: center; gap: 20px; + margin: 50px 0; `; export const AvatarUpload = styled.label` diff --git a/src/styles/search.ts b/src/styles/search.ts new file mode 100644 index 0000000..b173ac1 --- /dev/null +++ b/src/styles/search.ts @@ -0,0 +1,12 @@ +import { styled } from 'styled-components'; +import { Input } from './button.ts'; + +export const Form = styled.form` + display: flex; + flex-direction: column; + gap: 10px; + width: 100%; + max-width: 500px; +`; + +export const FormInput = styled(Input)``; diff --git a/src/styles/timeline.ts b/src/styles/timeline.ts new file mode 100644 index 0000000..241ad4b --- /dev/null +++ b/src/styles/timeline.ts @@ -0,0 +1,8 @@ +import styled from 'styled-components'; + +const TimelineWrapper = styled.ul` + display: block; + overflow-y: auto; +`; + +export default TimelineWrapper; diff --git a/src/styles/components/tweet.ts b/src/styles/tweet.ts similarity index 86% rename from src/styles/components/tweet.ts rename to src/styles/tweet.ts index fc00e22..3f4bbed 100644 --- a/src/styles/components/tweet.ts +++ b/src/styles/tweet.ts @@ -1,5 +1,5 @@ import { styled } from 'styled-components'; -import { grayColor, primaryColor, whiteColor } from '../App.ts'; +import { grayColor, primaryColor, whiteColor } from './app.ts'; interface IWrapperProps { hasPhoto: boolean; @@ -10,6 +10,9 @@ export const Wrapper = styled.li` grid-template-columns: ${({ hasPhoto }) => (hasPhoto ? '3fr 1fr' : '1fr')}; padding: 20px; margin: 20px 0; + &:not(:last-child) { + border-bottom: 1px dashed ${grayColor}; + } `; export const Column = styled.div``; diff --git a/src/styles/window.ts b/src/styles/window.ts new file mode 100644 index 0000000..514637e --- /dev/null +++ b/src/styles/window.ts @@ -0,0 +1,38 @@ +import styled from 'styled-components'; +import { blackColor, grayColor, primaryColor } from './app.ts'; + +export const Window = styled.section` + position: relative; + display: flex; + flex-direction: column; + height: calc(100vh - 40px); + padding: 38px 10px 10px; + border: 3px solid ${blackColor}; + border-radius: 6px; + box-shadow: 6px 6px ${grayColor}; +`; + +export const TopBar = styled.div` + position: absolute; + top: 0; + left: 0; + right: 0; + height: 28px; + margin-bottom: 10px; + background-color: ${primaryColor}; + border-bottom: 3px solid ${blackColor}; + flex-shrink: 0; +`; + +export const IconWrapper = styled.div` + float: right; + display: flex; + align-items: center; + gap: 2px; + height: 100%; + padding-right: 2px; + svg { + width: 20px; + height: 20px; + } +`;