-
Notifications
You must be signed in to change notification settings - Fork 2
design: 홈 페이지 퍼블리싱 #19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
4b6181d
04bbbea
0e3f25f
c16c1bd
5465dc5
535782f
f37f950
8b6c011
2621f47
01a3856
8957b0f
29956e2
7f0a102
59020e6
dbcc80f
1ae90fb
ef72e2e
c642d81
26ff140
e735722
3dc606d
ce5ef44
6755831
0a80cb9
d223843
e80b0e2
c1240e7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| import HomeOutlinedIcon from '@mui/icons-material/HomeOutlined'; | ||
| import { Link } from 'react-router'; | ||
|
|
||
| export default function HomeButton() { | ||
| return ( | ||
| <> | ||
| <div className="flex w-full max-w-150 justify-end pr-5 text-center"> | ||
| <Link | ||
| to="/" | ||
| className="bg-primary-3 fixed bottom-[30px] z-50 h-13 w-13 content-center rounded-full text-white transition-all duration-200 hover:scale-105 active:scale-90" | ||
| > | ||
| <HomeOutlinedIcon /> | ||
| </Link> | ||
| </div> | ||
| </> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| import { useEffect, useRef } from 'react'; | ||
| import gsap from 'gsap'; | ||
| import letter1 from '@/assets/images/letter-1.png'; | ||
| import letter2 from '@/assets/images/letter-2.png'; | ||
| import letter3 from '@/assets/images/letter-3.png'; | ||
| import letter4 from '@/assets/images/letter-4.png'; | ||
|
|
||
| const images = [letter1, letter2, letter3, letter4]; | ||
|
|
||
| const FloatingLetters = () => { | ||
| const lettersRef = useRef<HTMLImageElement[]>([]); | ||
| useEffect(() => { | ||
| if (!lettersRef.current) return; | ||
|
|
||
| lettersRef.current.forEach((letter, index) => { | ||
| gsap.to(letter, { | ||
| // x: Math.random() * 50 - 40, | ||
| y: Math.random() * 20 - 40 + 'vh', // 위아래 이동 | ||
| rotation: Math.random() * 50 - 25, // 회전 | ||
| duration: Math.random() * 3 + 2, // 지속 시간 | ||
| repeat: -1, // 무한 반복 | ||
| yoyo: true, // 왕복 | ||
| ease: 'power1.inOut', | ||
| delay: index * 1, // 편지마다 시차 | ||
| }); | ||
| }); | ||
| }, []); | ||
| return ( | ||
| <> | ||
| {images.map((src, index) => ( | ||
| <img | ||
| key={index} | ||
| src={src} | ||
| ref={(el) => { | ||
| if (el) lettersRef.current[index] = el; | ||
| }} | ||
| className="absolute w-20 opacity-90" | ||
| style={{ | ||
| left: `${index * 30 + 30}px`, // 편지지 간격 | ||
| top: '60vh', | ||
| }} | ||
| /> | ||
| ))} | ||
| </> | ||
| ); | ||
| }; | ||
|
|
||
| export default FloatingLetters; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| import { Link } from 'react-router'; | ||
| import goToLetterBoard from '@/assets/images/go-to-letter-board.png'; | ||
|
|
||
| const GoToLetterBoard = () => { | ||
| return ( | ||
| <div className="absolute bottom-48 left-[calc(var(--vh)*36)] z-9 flex w-full"> | ||
| <div className="text-left"> | ||
| <p className="text-gray-60 body-r mb-1 ml-2">게시판</p> | ||
| <Link to="/letter/board"> | ||
| <img src={goToLetterBoard} alt="go to letter board" className="w-[177px]" /> | ||
| </Link> | ||
| </div> | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| export default GoToLetterBoard; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| import { Link } from 'react-router'; | ||
| import goToLetterBoxNewLetters from '@/assets/images/go-to-letter-box-new-letters.png'; | ||
| import goToLetterBox from '@/assets/images/go-to-letter-box.png'; | ||
|
|
||
| const GoToLetterBox = () => { | ||
| //TODO : hasNewLetters 전역으로 상태 관리하기 | ||
| let hasNewLetters = true; | ||
| return ( | ||
| <div className="absolute bottom-10 left-5 z-9 flex w-fit"> | ||
| <div className="text-left"> | ||
| <p className="text-gray-60 body-r mb-1 ml-2">내 편지함</p> | ||
| <Link to="/letter/box"> | ||
| <img | ||
| src={hasNewLetters ? goToLetterBoxNewLetters : goToLetterBox} | ||
| alt="go to letter box" | ||
| className="w-[206.5px]" | ||
| /> | ||
| </Link> | ||
| </div> | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| export default GoToLetterBox; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| import { Link } from 'react-router'; | ||
| import goToRandomLetter from '@/assets/images/go-to-random-letter.png'; | ||
|
|
||
| const GoToRandomLetter = () => { | ||
| return ( | ||
| <> | ||
| <div className="z-20 h-fit w-fit"> | ||
| <p className="text-gray-60 body-r mb-1 ml-5 rotate-[-5.277deg]">고민편지 보러가기</p> | ||
| <Link to={'/letter/random'}> | ||
| <img src={goToRandomLetter} alt="go to random letter" /> | ||
| </Link> | ||
| </div> | ||
| </> | ||
| ); | ||
| }; | ||
|
|
||
| export default GoToRandomLetter; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| import { Link } from 'react-router'; | ||
| import goToWrite from '@/assets/images/go-to-write.png'; | ||
|
|
||
| const GoToWrite = () => { | ||
| return ( | ||
| <div className="h-fit w-fit pl-[87px]"> | ||
| <p className="text-gray-60 body-r mb-1 rotate-[-5.277deg]">속마음 나누기</p> | ||
| <Link to={'/letter/write'}> | ||
| <img src={goToWrite} alt="go to write" /> | ||
| </Link> | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| export default GoToWrite; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| import BackgroundImageWrapper from '@/components/BackgroundImageWrapper'; | ||
| import homeLeftMountain from '@/assets/images/home-left-mountain.png'; | ||
|
|
||
| const HomeBackgroundLeft = () => { | ||
| return ( | ||
| <BackgroundImageWrapper | ||
| as="div" | ||
| className="absolute bottom-0 left-0 z-[11] h-[calc(var(--vh)*25)] w-full min-w-[700px] -translate-x-1/3" | ||
| imageUrl={homeLeftMountain} | ||
| /> | ||
| ); | ||
| }; | ||
|
|
||
| export default HomeBackgroundLeft; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| import BackgroundImageWrapper from '@/components/BackgroundImageWrapper'; | ||
| import homeRightMountainBottom from '@/assets/images/home-right-mountain-bottom.png'; | ||
| const HomeBackgroundRightBottom = () => { | ||
| return ( | ||
| <BackgroundImageWrapper | ||
| as="div" | ||
| className="absolute bottom-0 z-[10] h-[calc(var(--vh)*20)] w-full min-w-[600px] -translate-x-1/4 overflow-hidden" | ||
| imageUrl={homeRightMountainBottom} | ||
| /> | ||
| ); | ||
| }; | ||
|
|
||
| export default HomeBackgroundRightBottom; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| import BackgroundImageWrapper from '@/components/BackgroundImageWrapper'; | ||
| import homeRightMountainTop from '@/assets/images/home-right-mountain-top.png'; | ||
| const HomeBackgroundRightTop = () => { | ||
| return ( | ||
| <BackgroundImageWrapper | ||
| as="div" | ||
| className="absolute bottom-0 z-8 h-[calc(var(--vh)*32)] w-full min-w-[760px] -translate-x-1/4 overflow-hidden" | ||
| imageUrl={homeRightMountainTop} | ||
| /> | ||
| ); | ||
| }; | ||
|
|
||
| export default HomeBackgroundRightTop; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| import { Link } from 'react-router'; | ||
|
|
||
| import { AlarmIcon, PersonIcon } from '@/assets/icons'; | ||
|
|
||
| const HomeHeader = () => { | ||
| return ( | ||
| <header className="fixed top-0 z-40 flex h-16 w-full max-w-150 items-center justify-end p-5"> | ||
| <div className="flex items-center gap-3"> | ||
| <Link to="/mypage/notifications"> | ||
| <AlarmIcon className="h-6 w-6 text-white" /> | ||
| </Link> | ||
| <Link to="/mypage"> | ||
| <PersonIcon className="h-6 w-6 text-white" /> | ||
| </Link> | ||
| </div> | ||
| </header> | ||
| ); | ||
| }; | ||
|
|
||
| export default HomeHeader; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| import RandomCheer from './RandomCheer'; | ||
| import GoToWrite from './GoToWrite'; | ||
| import GoToRandomLetter from './GoToRandomLetter'; | ||
|
|
||
| const HomeLeft = () => { | ||
| return ( | ||
| <div | ||
| style={{ top: 'calc(-240px + var(--vh) * 64)' }} | ||
| className="absolute flex w-full max-w-150 min-w-[300px] flex-shrink-0 grow snap-start flex-col space-y-[calc(var(--vh)*2)]" | ||
| > | ||
| <RandomCheer /> | ||
| <GoToWrite /> | ||
| <GoToRandomLetter /> | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| export default HomeLeft; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| import FloatingLetters from './FloatingLetters'; | ||
| import GoToLetterBoard from './GoToLetterBoard'; | ||
| import GoToLetterBox from './GoToLetterBox'; | ||
| import NewLetterModal from './NewLetterModal'; | ||
|
|
||
| const HomeRight = () => { | ||
| //TODO : hasNewLetters 전역으로 상태 관리할지 | ||
| let hasNewLetters = true; | ||
|
|
||
| return ( | ||
| <div className="flex h-screen w-full max-w-150 min-w-[300px] flex-shrink-0 grow snap-start flex-col items-center overflow-x-hidden pt-5"> | ||
| {hasNewLetters && <FloatingLetters />} | ||
| <GoToLetterBox /> | ||
| <GoToLetterBoard /> | ||
| {hasNewLetters && <NewLetterModal />} | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| export default HomeRight; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| import { useState } from 'react'; | ||
| import SendOutlinedIcon from '@mui/icons-material/SendOutlined'; | ||
| import DriveFileRenameOutlineOutlinedIcon from '@mui/icons-material/DriveFileRenameOutlineOutlined'; | ||
| import ShareOutlinedIcon from '@mui/icons-material/ShareOutlined'; | ||
| import ShowIncomingLettersModal from './ShowIncomingLettersModal'; | ||
| import ShowDraftModal from './ShowDraftModal'; | ||
| import ShowShareAccessModal from './ShowShareAccessModal'; | ||
|
Comment on lines
+1
to
+7
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오 뭔가... 린트가 반영이 안된 것 같은데 혹시 format on save 설정이 되어 있으신가요?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 되어있다고는 뜨는데 계속 import 린트가 반영이 안되는 것 같습니다 🥲 |
||
|
|
||
| const LetterActions = () => { | ||
| const [activeModal, setActiveModal] = useState< | ||
| null | 'incomingLetters' | 'draft' | 'shareAccess' | ||
| >(null); | ||
|
|
||
| const arr: { title: 'incomingLetters' | 'draft' | 'shareAccess'; icon: React.ReactNode }[] = [ | ||
| { | ||
| title: 'incomingLetters', | ||
| icon: <SendOutlinedIcon />, | ||
| }, | ||
| { | ||
| title: 'draft', | ||
| icon: <DriveFileRenameOutlineOutlinedIcon />, | ||
| }, | ||
| { | ||
| title: 'shareAccess', | ||
| icon: <ShareOutlinedIcon />, | ||
| }, | ||
| ]; | ||
| return ( | ||
| <div className="fixed top-24 z-31 mt-3 flex w-full max-w-150 justify-end pr-5"> | ||
| <div className="flex flex-col gap-y-3"> | ||
| {arr.map((item, index) => ( | ||
| <button | ||
| key={index} | ||
| onClick={() => setActiveModal(item.title)} | ||
| className="flex h-12 w-12 items-center justify-center gap-[10px] rounded-full bg-white/40 text-gray-50 shadow-[inset_0_-2px_2px_0_rgba(208,169,14,0.30),_0_0px_4px_0_rgba(199,164,29,0.30)]" | ||
| > | ||
| {item.icon} | ||
| </button> | ||
| ))} | ||
| </div> | ||
| {activeModal === 'incomingLetters' && ( | ||
| <ShowIncomingLettersModal onClose={() => setActiveModal(null)} /> | ||
| )} | ||
| {activeModal === 'draft' && <ShowDraftModal onClose={() => setActiveModal(null)} />} | ||
| {activeModal === 'shareAccess' && ( | ||
| <ShowShareAccessModal onClose={() => setActiveModal(null)} /> | ||
| )} | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| export default LetterActions; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| import React from 'react'; | ||
|
|
||
| const LetterPreview = () => { | ||
| return <div>LetterPreview</div>; | ||
| }; | ||
|
|
||
| export default LetterPreview; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| import { useState } from 'react'; | ||
|
|
||
| const NewLetterModal = () => { | ||
| const [newLetterCount, setNewLetterCount] = useState(0); | ||
|
|
||
| return ( | ||
| <p className="text-gray-60 body-b absolute top-30 mb-10 w-fit animate-pulse rounded-full bg-white px-6 py-4"> | ||
| {newLetterCount}통의 편지가 도착했어요! | ||
| </p> | ||
| ); | ||
| }; | ||
|
|
||
| export default NewLetterModal; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| import { useState } from 'react'; | ||
| import { RANDOM_CHEER_LIST } from '../constants'; | ||
| import randomCheerBird from '@/assets/images/random-cheer-bird.png'; | ||
|
|
||
| const RandomCheer = () => { | ||
| const getRandomCheer = (): string => { | ||
| const randomIndex = Math.floor(Math.random() * RANDOM_CHEER_LIST.length); | ||
| return RANDOM_CHEER_LIST[randomIndex]; | ||
| }; | ||
|
|
||
| const [randomCheer, setRandomCheer] = useState(getRandomCheer()); | ||
|
|
||
| return ( | ||
| <div className="flex flex-col items-end pr-20"> | ||
| <div | ||
| className="relative mb-3 w-fit rounded-lg border-1 border-white bg-white px-6 py-[7px] text-center" | ||
| onClick={() => setRandomCheer(getRandomCheer())} | ||
| > | ||
| <p className="caption-m">{randomCheer}</p> | ||
| <div className="absolute right-2 bottom-[-15px] -translate-x-1/2 transform border-x-[10px] border-t-[15px] border-x-transparent border-t-white"></div> | ||
| </div> | ||
| <img | ||
| src={randomCheerBird} | ||
| alt="random cheer bird" | ||
| className="h-[26.5px] w-[21px] opacity-80" | ||
| /> | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| export default RandomCheer; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
여기서 걱정이 되는게 있습니다!
지난 번에 제가 말씀드렸듯이 모바일의 vh랑 웹의 vh를 좀 다르게 다룰 필요가 있어요.
그래서 일반적인 vh를 사용하면 웹과 모바일에서 차이가 발생할 것 같아요. 물론 그 차이가 미세할 수도 있지많요!
그래서 만약 vh를 사용해야 한다면 제가 스타일 변수로 --vh를 만들어뒀는데 이걸 사용하거나 1vh
window.innerHeight * 0.01로 계산해서 하면 어떨까 해요!There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
편지 이미지들이 웹과 모바일에서 같은 높이 내에서 움직인다면, 화면 높이가 큰 웹에서는 빈 공간이 너무 많아질 것 같아요😲 그래서 움직일 수 있는 높이에 일부러 차이를 두는 게 어떨까 합니다!