Skip to content

Commit

Permalink
🔀 Merge develop branch
Browse files Browse the repository at this point in the history
  • Loading branch information
wade3420 committed Dec 2, 2023
2 parents 12ee9a1 + 738b3bd commit dbe5dd6
Show file tree
Hide file tree
Showing 14 changed files with 307 additions and 27 deletions.
10 changes: 9 additions & 1 deletion panda.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,15 @@ export default defineConfig({

// Useful for theme customization
theme: {
extend: {},
extend: {
keyframes: {
gradient: {
'0%': { transform: 'rotate(0deg)', backgroundPositionX: '0%', backgroundPositionY: '0%' },
'50%': { backgroundPositionX: '50%', backgroundPositionY: '100%' },
'100%': { transform: 'rotate(0deg)', backgroundPositionX: '0%', backgroundPositionY: '0%' },
},
},
},
},

// The output directory for your css system
Expand Down
5 changes: 5 additions & 0 deletions public/assets/icons/left-arrow-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Metadata } from 'next';
import { css } from '@styled-system/css';
import { css } from '@/styled-system/css';

import { QueryProvider } from '../hooks/query';

Expand All @@ -23,7 +23,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
}

const containerCss = {
maxWidth: '375px',
maxWidth: '475px',
margin: '0 auto',
minHeight: '100vh',
};
47 changes: 47 additions & 0 deletions src/app/timer/TimerView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React from 'react';
import { css } from '@/styled-system/css';
import { center } from '@/styled-system/patterns';

interface Props {
isActive: boolean;
time: [string, string];
category: string;
}
export default function TimerView({ category, time, isActive }: Props) {
return (
<div className={center()}>
<div className={center(innerCss)}>
<div className={css(categoryCss)}>{category}</div>
<div
className={css(timerTextCss, {
color: isActive ? 'transparent' : '#B0B8C1',
})}
>
<span>{time[0]}</span>
<span>:</span>
<span>{time[1]}</span>
</div>
</div>
</div>
);
}

const innerCss = {
width: '312px',
height: '312px',
background: 'white',
boxShadow: '0px 10px 30px 5px rgba(18.51, 14.90, 195.38, 0.07)',
borderRadius: 9999,
flexDirection: 'column',
};

const categoryCss = { color: '#4E5968', fontSize: '18px', fontWeight: '600', lineHeight: '150%' };

const timerTextCss = {
fontSize: '70px',
fontWeight: '700',
animation: 'gradient 3s ease-in-out infinite',
backgroundSize: '150% 200%!',
'-webkit-background-clip': 'text!',
background: 'linear-gradient(108deg, #FF8C8C -1.04%, #5D8AFF 101.48%)',
};
10 changes: 10 additions & 0 deletions src/app/timer/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { type PropsWithChildren } from 'react';
import { css } from '@/styled-system/css';

export default function Layout({ children }: PropsWithChildren) {
return <div className={containerCss}>{children}</div>;
}

const containerCss = css({
minHeight: '100vh',
});
100 changes: 100 additions & 0 deletions src/app/timer/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
'use client';
import TimerView from '@/app/timer/TimerView';
import useTimer from '@/app/timer/useTimer';
import useTimerStatus from '@/app/timer/useTimerStatus';
import Header from '@/components/Layout/Header';
import { css } from '@styled-system/css';

export default function TimerPage() {
const { step, stepLabel, onNextStep } = useTimerStatus();
const { formattedTime } = useTimer(step);

const onFinish = () => {
onNextStep('stop');
alert('정말 끝내시겠습니까?');
};

return (
<div
className={css(bgCss, {
background: step === 'stop' ? '#F2F4F6' : 'linear-gradient(136deg, #FFF1F2 4.76%, #E9EFFF 89.58%)',
})}
>
<Header title={'미션 타이머'} />
<div className={css(containerCss)}>
<h1 className={titleCss}>{stepLabel.title}</h1>
<p className={descCss}>{stepLabel.desc}</p>

<TimerView category="카테고리" time={formattedTime} isActive={step !== 'stop'} />
<div className={css(buttonContainerCss)}>
{step === 'ready' && (
<button type="button" onClick={() => onNextStep('progress')}>
시작
</button>
)}
{step === 'progress' && (
<>
<button type="button" onClick={() => onNextStep('stop')}>
일시정지
</button>
<button type="button" onClick={onFinish}>
끝내기
</button>
</>
)}
{step === 'stop' && (
<>
<button type="button" onClick={() => onNextStep('progress')}>
다시 시작
</button>
<button type="button" onClick={onFinish}>
끝내기
</button>
</>
)}
</div>
</div>
</div>
);
}

const bgCss = {
minHeight: '100vh',
transition: '1s ease',
};

const containerCss = {
padding: '24px 16px',
};

const font24Css = {
fontSize: '24px',
fontFamily: 'Pretendard',
fontWeight: '700',
lineHeight: '150%',
wordWrap: 'break-word',
};

const font14Css = {
fontSize: '14px',
fontFamily: 'Pretendard',
fontWeight: '400',
lineHeight: '150%',
};

const titleCss = css(font24Css, { color: '#333D4B' });
const descCss = css(font14Css, { color: '#6B7684', marginBottom: '84px' });

const buttonContainerCss = {
margin: '28px auto',
display: 'flex',
justifyContent: 'center',
gap: '12px',

'& button': {
backgroundColor: 'white',
borderRadius: '30px',
padding: '16px 24px',
boxSizing: '0px 4px 20px 0px rgba(18, 23, 41, 0.10)',
},
};
34 changes: 34 additions & 0 deletions src/app/timer/useTimer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { useEffect, useState } from 'react';
import { type StepType } from '@/app/timer/useTimerStatus';

export default function useTimer(status: StepType, initSeconds = 600) {
const [second, setSecond] = useState(initSeconds); // 남은 시간 (단위: 초)

const formattedTime = formatMMSS(second);

useEffect(() => {
let timer: NodeJS.Timeout;

if (second <= 0) {
return;
}

if (status === 'progress') {
timer = setInterval(() => {
setSecond((prev) => prev - 1);
}, 1000);
}
return () => clearInterval(timer);
}, [second, status]);

return { formattedTime };
}

const formatMMSS = (second: number): [string, string] => {
const minutes = Math.floor(second / 60); // 분 계산
const seconds = second % 60; // 초 계산
const formattedMinutes = String(minutes).padStart(2, '0'); // 두 자리로 변환
const formattedSeconds = String(seconds).padStart(2, '0'); // 두 자리로 변환

return [formattedMinutes, formattedSeconds];
};
32 changes: 32 additions & 0 deletions src/app/timer/useTimerStatus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { useState } from 'react';

export type StepType = 'ready' | 'progress' | 'stop';

const TIMER_STATUS = {
ready: {
title: '준비 되셨나요?',
desc: '타이머를 눌러서 10분의 미션을 완성해 주세요!',
},
progress: {
title: '시작!',
desc: '10분 동안 최선을 다해주세요!',
},
stop: {
title: '잠시 멈췄어요',
desc: '준비가 되면 타이머를 다시 시작해주세요!',
},
} as const;

function useTimerStatus() {
const [step, setStep] = useState<StepType>('ready');

const stepLabel = TIMER_STATUS[step];

const onNextStep = (nextStep: StepType) => {
setStep(nextStep);
};

return { step, onNextStep, stepLabel };
}

export default useTimerStatus;
16 changes: 0 additions & 16 deletions src/components/Header.tsx

This file was deleted.

44 changes: 44 additions & 0 deletions src/components/Layout/Header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import Image from 'next/image';
import { flex } from '@/styled-system/patterns';
import { css } from '@styled-system/css';

interface Props {
title?: string;
}

function Header({ title }: Props) {
return (
<>
<header className={wrapperCss}>
<button type="button">
<Image src="/assets/icons/left-arrow-icon.svg" alt="Left Arrow Icon" width={20} height={20} />
</button>
<h2 className={headingCss}>{title}</h2>
</header>
<div className={css(headerBlankCss)} />
</>
);
}

const headerBlankCss = {
height: '42px;',
width: '100%',
};

const wrapperCss = flex({
padding: '10px 16px',
gap: '6px',
background: 'transparent',
position: 'fixed',
margin: '0 auto',
zIndex: 100,
});

const headingCss = css({
color: '#6B7684',
fontSize: '16px',
fontFamily: 'Pretendard',
fontWeight: '600',
});

export default Header;
16 changes: 16 additions & 0 deletions src/styled-system/tokens/keyframes.css
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,20 @@
animation-timing-function: cubic-bezier(0,0,0.2,1)
}
}
@keyframes gradient {
0% {
transform: rotate(0deg);
background-position-x: 0%;
background-position-y: 0%
}
50% {
background-position-x: 50%;
background-position-y: 100%
}
100% {
transform: rotate(0deg);
background-position-x: 0%;
background-position-y: 0%
}
}
}
2 changes: 1 addition & 1 deletion src/styled-system/tokens/tokens.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -928,7 +928,7 @@ export type AnimationToken = 'spin' | 'ping' | 'pulse' | 'bounce';

export type BreakpointToken = 'sm' | 'md' | 'lg' | 'xl' | '2xl';

export type AnimationName = 'spin' | 'ping' | 'pulse' | 'bounce';
export type AnimationName = 'spin' | 'ping' | 'pulse' | 'bounce' | 'gradient';

export type Tokens = {
borders: BorderToken;
Expand Down
Loading

0 comments on commit dbe5dd6

Please sign in to comment.