-
Notifications
You must be signed in to change notification settings - Fork 0
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
메인 화면 figma 프로토 타입 초기 퍼블리싱 / 컴포넌트 구조 설계 작업 #6
Changes from all commits
ae68405
7d33b5d
b778467
51ee499
2720067
7ec9401
c90a807
d5f5d23
62fe314
4058219
6245320
e60bb5d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,6 +30,7 @@ | |
"@tanstack/react-query-devtools": "^5.59.8", | ||
"axios": "^1.7.7", | ||
"jotai": "^2.10.0", | ||
"overlay-kit": "^1.4.1", | ||
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. 이번에 처음 알게되었습니다. 덕분에 좋은 것 하나 배웁니다. |
||
"react": "^18.3.1", | ||
"react-dom": "^18.3.1", | ||
"react-router-dom": "^6.26.2", | ||
|
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import { overlay } from 'overlay-kit'; | ||
|
||
export const overlayKit = overlay; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { OverlayProvider } from 'overlay-kit'; | ||
|
||
export const ReactOverlayProvider = ({ | ||
children, | ||
}: { | ||
children: React.ReactNode; | ||
}) => { | ||
return <OverlayProvider>{children}</OverlayProvider>; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export const FALLBACK_BACKGROUND_URL = 'images/image.jpg'; | ||
export const backgroundImagePaths: string[] = ['images/image.jpg']; |
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. 이미지 불러오는 기능 분리 좋은 것 같아요. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
interface FetchImageWithFallbackParams { | ||
imageUrl: string; | ||
onSuccess: () => void; | ||
onError: () => void; | ||
} | ||
|
||
export const fetchImageWithFallback = ({ | ||
imageUrl, | ||
onSuccess, | ||
onError, | ||
}: FetchImageWithFallbackParams) => { | ||
const img = new Image(); | ||
img.src = imageUrl; | ||
img.onload = onSuccess; | ||
img.onerror = onError; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { backgroundImagePaths } from '../model/backgroundImages'; | ||
|
||
export const getRandomImageUrl = () => { | ||
return backgroundImagePaths[ | ||
Math.floor(Math.random() * backgroundImagePaths.length) | ||
]; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import styled, { css } from 'styled-components'; | ||
import { fadeIn } from '@semo-client/ui/styles/keyframes/fades'; | ||
import { useBackgroundImageLoader } from '../hooks/useBackgroundImageLoader'; | ||
|
||
export const BackgroundImageView = () => { | ||
const { backgroundImageUrl, isImageReady } = useBackgroundImageLoader(); | ||
|
||
return ( | ||
<> | ||
<FullScreenBackground | ||
imageUrl={backgroundImageUrl} | ||
isImageReady={isImageReady} | ||
> | ||
<Overlay /> | ||
</FullScreenBackground> | ||
</> | ||
); | ||
}; | ||
|
||
const FullScreenBackground = styled.div<{ | ||
imageUrl: string; | ||
isImageReady: boolean; | ||
}>` | ||
width: 100vw; | ||
height: 100vh; | ||
|
||
background-image: ${({ imageUrl }) => `url(${imageUrl})`}; | ||
opacity: ${({ isImageReady }) => (isImageReady ? 1 : 0)}; | ||
|
||
${({ isImageReady }) => | ||
isImageReady && | ||
css` | ||
animation: ${fadeIn} 0.35s linear; | ||
`}; | ||
|
||
background-size: cover; | ||
background-position: center; | ||
background-repeat: no-repeat; | ||
|
||
position: fixed; | ||
top: 0; | ||
left: 0; | ||
z-index: -1; | ||
`; | ||
|
||
const Overlay = styled.div` | ||
width: 100%; | ||
height: 100%; | ||
background-color: rgba(0, 0, 0, 0.35); | ||
position: absolute; | ||
top: 0; | ||
left: 0; | ||
`; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { useState, useEffect } from 'react'; | ||
import { FALLBACK_BACKGROUND_URL } from '../../model/backgroundImages'; | ||
import { fetchImageWithFallback } from '../../service/fetchImageWithFallback'; | ||
import { getRandomImageUrl } from '../../service/getRandomImageUrl'; | ||
|
||
export const useBackgroundImageLoader = () => { | ||
const [backgroundImageUrl, setBackgroundImageUrl] = useState<string>(''); | ||
const [isImageReady, setIsImageReady] = useState<boolean>(false); | ||
|
||
useEffect(() => { | ||
const imageUrl = getRandomImageUrl(); | ||
setBackgroundImageUrl(imageUrl); | ||
|
||
fetchImageWithFallback({ | ||
imageUrl, | ||
onSuccess: () => setIsImageReady(true), | ||
onError: () => { | ||
setBackgroundImageUrl(FALLBACK_BACKGROUND_URL); | ||
setIsImageReady(true); | ||
}, | ||
}); | ||
}, []); | ||
Comment on lines
+14
to
+22
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. 👍👍 |
||
|
||
return { backgroundImageUrl, isImageReady }; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export const greetingMessageTemplates = [ | ||
'길동 님은 잠 안 자시나요? A+ 자신 있으시겠죠? 😊', | ||
'님! 오늘도 즐코하세요 😊', | ||
'님! 오늘도 화이팅 😊', | ||
]; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
export const formatDate = (date: Date): string => { | ||
const year = date.getFullYear(); | ||
const month = String(date.getMonth() + 1).padStart(2, '0'); | ||
const day = String(date.getDate()).padStart(2, '0'); | ||
|
||
const weekday = new Intl.DateTimeFormat('ko-KR', { weekday: 'short' }).format( | ||
date, | ||
); | ||
|
||
return `${year}년 ${month}월 ${day}일 (${weekday})`; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
export const formatTime = (date: Date): string => { | ||
const hours = String(date.getHours()).padStart(2, '0'); | ||
const minutes = String(date.getMinutes()).padStart(2, '0'); | ||
const seconds = String(date.getSeconds()).padStart(2, '0'); | ||
|
||
return `${hours} ${minutes} ${seconds}`; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { greetingMessageTemplates } from '../model/greetingMessageTemplates'; | ||
|
||
export const getRandomGreetingMessage = () => { | ||
return greetingMessageTemplates[ | ||
Math.floor(Math.random() * greetingMessageTemplates.length) | ||
]; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import styled from 'styled-components'; | ||
import { formatDate } from '../../service/formatDate'; | ||
import { formatTime } from '../../service/formatTime'; | ||
import { useTimeTick } from '../hooks/useTimeTick'; | ||
import { GreetingMessage } from './GreetingMessage'; | ||
|
||
export const Clock = () => { | ||
const { time } = useTimeTick(); | ||
|
||
return ( | ||
<ClockContainer> | ||
<DateDisplay>{formatDate(time)}</DateDisplay> | ||
<TimeDisplay>{formatTime(time)}</TimeDisplay> | ||
<GreetingMessage /> | ||
</ClockContainer> | ||
); | ||
}; | ||
|
||
const ClockContainer = styled.div` | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
flex-direction: column; | ||
|
||
margin: 88px auto 56px; | ||
`; | ||
|
||
const DateDisplay = styled.div` | ||
font-size: 24px; | ||
color: rgba(255, 255, 255, 0.7); | ||
font-weight: 400; | ||
`; | ||
|
||
const TimeDisplay = styled.div` | ||
width: 700px; | ||
text-align: center; | ||
font-size: 160px; | ||
font-weight: 100; | ||
line-height: 214.8px; | ||
color: #ffffff; | ||
`; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import styled from 'styled-components'; | ||
import { useGreetingMessage } from '../hooks/useGreetingMessage'; | ||
|
||
export const GreetingMessage = () => { | ||
const { greetingMessage } = useGreetingMessage(); | ||
return <StyledMessage>{greetingMessage}</StyledMessage>; | ||
}; | ||
|
||
const StyledMessage = styled.div` | ||
font-size: 24px; | ||
color: #fff; | ||
font-weight: 400; | ||
`; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { useRef } from 'react'; | ||
import { getRandomGreetingMessage } from '../../service/getRadomGreetingMessage'; | ||
|
||
export const useGreetingMessage = () => { | ||
const greetringMessageRef = useRef(getRandomGreetingMessage()); | ||
|
||
return { | ||
greetingMessage: greetringMessageRef.current, | ||
}; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { useEffect, useState } from 'react'; | ||
|
||
export const useTimeTick = () => { | ||
const [time, setTime] = useState(new Date()); | ||
|
||
useEffect(() => { | ||
const intervalId = setInterval(() => setTime(new Date()), 1000); | ||
return () => clearInterval(intervalId); | ||
}, []); | ||
|
||
return { time }; | ||
}; |
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.
title 깔끔하게 잘 설정된 것 같아요!