diff --git a/client/src/App.tsx b/client/src/App.tsx index 2cad04c..bc77f35 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -10,10 +10,9 @@ import Menubar from '@container/Menubar'; import Main from '@page/Main'; import Todos from '@page/Todos'; import DiagramPage from '@page/DiagramPage'; -import OverLay from '@components/OverLay'; import TodoController from '@container/TodoController'; -import { TutorialImage } from '@components/tutorial/TutorialImage'; +import { TutorialVideo } from '@components/tutorial/TutorialVideo'; import { isTutorialAtom } from '@util/GlobalState'; import { PRIMARY_COLORS } from '@util/Constants'; @@ -82,7 +81,7 @@ const App = (): ReactElement => { - {isShow && } + {isShow && } {isTutorial && ( diff --git a/client/src/components/tutorial/Dots.tsx b/client/src/components/tutorial/Dots.tsx index 4ee661a..b3e2e45 100644 --- a/client/src/components/tutorial/Dots.tsx +++ b/client/src/components/tutorial/Dots.tsx @@ -2,34 +2,88 @@ import { ReactElement } from 'react'; import styled from 'styled-components'; import { PRIMARY_COLORS } from '@util/Constants'; -const Dot = styled.span<{ active: boolean }>` - padding: 5px; - margin-right: 10px; - cursor: none; +const Dot = styled.li<{ active: boolean }>` + position: relative; + width: 40px; + height: 40px; border-radius: 50%; - background: ${(props) => (props.active ? `${PRIMARY_COLORS.lightGray}` : `${PRIMARY_COLORS.darkGray}`)}}; + margin: 0 8px; + cursor: pointer; + text-align: -webkit-match-parent; + + &::before { + content: ''; + width: 20px; + height: 20px; + background-color: ${(props) => (props.active ? `${PRIMARY_COLORS.red}` : `${PRIMARY_COLORS.blue}`)}; + border-radius: 50%; + display: flex; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + } +`; + +const Svg = styled.svg` + width: 40px; + height: 40px; + fill: none; `; -const Wrapper = styled.div` - position: absolute; +const Circle = styled.circle<{ duration: number }>` + opacity: 1; + stroke-linecap: round; + transform: rotate(-90deg); + transform-origin: 50% 50%; + stroke-width: 3px; + stroke: ${PRIMARY_COLORS.red}; + stroke-dasharray: 330; + stroke-dashoffset: 60; + animation: progress ${(props) => `${props.duration}s`} linear; + @keyframes progress { + 0% { + stroke-dashoffset: 330; + } + 100% { + stroke-dashoffset: 60; + } + } +`; + +const Wrapper = styled.ul` display: flex; justify-content: center; align-items: center; - bottom: 25px; width: 100%; + + padding: 0; + margin: 15px 0 0; + list-style: none; `; interface DotsProps { slides: string[]; currentIndex: number; + setCurrentIndex: React.Dispatch>; + duration: number; } -export const Dots = ({ slides, currentIndex }: DotsProps): ReactElement => { +export const Dots = ({ slides, currentIndex, setCurrentIndex, duration }: DotsProps): ReactElement => { return ( - {slides.map((slide, i) => ( - - ))} + {slides.map((slide, i) => { + if (currentIndex === i) { + return ( + setCurrentIndex(i)}> + + + + + ); + } + return setCurrentIndex(i)} />; + })} ); }; diff --git a/client/src/components/tutorial/TutorialImage.tsx b/client/src/components/tutorial/TutorialImage.tsx deleted file mode 100644 index be41588..0000000 --- a/client/src/components/tutorial/TutorialImage.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import { ReactElement, useEffect, useState, useRef } from 'react'; -import styled from 'styled-components'; -import { Dots } from './Dots'; - -import { PrevIcon, NextIcon, CancelIcon } from './Icons'; -import { PRIMARY_COLORS } from '@util/Constants'; -import Button from '@components/Button'; - -import Tutorial1 from '@images/tutorial/tutorial-page-1.svg'; -import Tutorial2 from '@images/tutorial/tutorial-page-2.svg'; -import Tutorial3 from '@images/tutorial/tutorial-page-3.svg'; -import Tutorial5 from '@images/tutorial/tutorial-page-5.svg'; - -const StyledOverlay = styled.div` - position: relative; - display: flex; - justify-content: center; - align-items: center; - background-color: ${PRIMARY_COLORS.black}; - width: 100vw; - height: 100vh; - position: fixed; - top: 0; - left: 0; - bottom: 0; - right: 0; - z-index: 10; - - & > img { - width: 80%; - height: 80%; - } -`; - -const imgSrcArray: string[] = [Tutorial1, Tutorial2, Tutorial3, Tutorial5]; - -const StyledButton = styled(Button)` - background-color: transparent; -`; - -const StyledCancelButton = styled(Button)` - position: absolute; - background-color: transparent; - top: 0px; - right: 0px; -`; - -export const TutorialImage = ({ - isTutorial, - setIsOver, -}: { - isTutorial: boolean; - setIsOver: React.Dispatch>; -}): ReactElement => { - const [currentIndex, setCurrentIndex] = useState(0); - - const autoPlayRef = useRef<() => void>(); - - useEffect(() => { - autoPlayRef.current = nextSlide; - }); - - useEffect(() => { - const play = (): void => { - if (autoPlayRef.current !== undefined) autoPlayRef.current(); - }; - - const interval = setInterval(play, 10 * 1000); - - return () => { - clearInterval(interval); - }; - }, [currentIndex]); - - const prevSlide = (): void => { - setCurrentIndex(currentIndex === 0 ? imgSrcArray.length - 1 : currentIndex - 1); - }; - - const nextSlide = (): void => { - setCurrentIndex(currentIndex === imgSrcArray.length - 1 ? 0 : currentIndex + 1); - }; - - const exit = (): void => { - setIsOver(true); - }; - return ( - - } onClick={exit} /> - } onClick={prevSlide} /> - - } onClick={nextSlide} /> - - - ); -}; diff --git a/client/src/components/tutorial/TutorialVideo.tsx b/client/src/components/tutorial/TutorialVideo.tsx new file mode 100644 index 0000000..d68f511 --- /dev/null +++ b/client/src/components/tutorial/TutorialVideo.tsx @@ -0,0 +1,86 @@ +import { ReactElement, useState, useRef } from 'react'; +import styled from 'styled-components'; + +import { CancelIcon } from './Icons'; +import { PRIMARY_COLORS } from '@util/Constants'; +import Button from '@components/Button'; +import { Dots } from './Dots'; + +import Main from '@images/tutorial/main.mp4'; +import Table from '@images/tutorial/table.mp4'; +import Diagram from '@images/tutorial/diagram.mp4'; + +const videoSrcArray: string[] = [Main, Table, Diagram]; +const titleArray: string[] = ['메인 페이지', '테이블 뷰', '다이어그램 뷰']; + +const Title = styled.h1` + font-family: 'Roboto'; + color: ${PRIMARY_COLORS.red}; +`; + +const StyledOverlay = styled.div` + position: relative; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + background-color: ${PRIMARY_COLORS.white}; + width: 100vw; + height: 100vh; + position: fixed; + top: 0; + left: 0; + bottom: 0; + right: 0; + z-index: 10; + + & > video { + width: 80%; + height: 80%; + } +`; + +const StyledCancelButton = styled(Button)` + position: absolute; + background-color: transparent; + top: 0px; + right: 0px; +`; + +export const TutorialVideo = ({ + setIsOver, +}: { + setIsOver: React.Dispatch>; +}): ReactElement => { + const ref = useRef(null); + const [currentIndex, setCurrentIndex] = useState(0); + const [duration, setDuration] = useState(0); + + const handleCancel = (): void => { + setIsOver(true); + }; + + const onEnded = (): void => { + setCurrentIndex(currentIndex === videoSrcArray.length - 1 ? 0 : currentIndex + 1); + }; + + const onLoadedMetadata = (): void => { + if (ref.current === null) return; + setDuration(ref.current.duration); + }; + + return ( + + } onClick={handleCancel} /> + {titleArray[currentIndex]} + + ); +}; diff --git a/client/src/container/Header.tsx b/client/src/container/Header.tsx index 60f370a..352653a 100644 --- a/client/src/container/Header.tsx +++ b/client/src/container/Header.tsx @@ -24,6 +24,7 @@ const Wrapper = styled.div` const Header = (): ReactElement => { const [isTutorial, setIsTutorial] = useAtom(isTutorialAtom); + const url: string = isTutorial ? '/tutorials' : '/'; const [, changeIndexedDBtoMemory] = useAtom(changeIndexedDBtoMemoryAtom); const [, changeMemorytoIndexedDB] = useAtom(changeMemorytoIndexedDBAtom); const startTutorial = (): void => { @@ -36,7 +37,7 @@ const Header = (): ReactElement => { }; return ( - + {isTutorial ? ( diff --git a/client/src/images/tutorial/diagram.mp4 b/client/src/images/tutorial/diagram.mp4 new file mode 100644 index 0000000..18b2bb1 Binary files /dev/null and b/client/src/images/tutorial/diagram.mp4 differ diff --git a/client/src/images/tutorial/main.mp4 b/client/src/images/tutorial/main.mp4 new file mode 100644 index 0000000..c101584 Binary files /dev/null and b/client/src/images/tutorial/main.mp4 differ diff --git a/client/src/images/tutorial/table.mp4 b/client/src/images/tutorial/table.mp4 new file mode 100644 index 0000000..d5cde35 Binary files /dev/null and b/client/src/images/tutorial/table.mp4 differ diff --git a/client/src/images/tutorial/tutorial-page-1.svg b/client/src/images/tutorial/tutorial-page-1.svg deleted file mode 100755 index 0e7e17f..0000000 --- a/client/src/images/tutorial/tutorial-page-1.svg +++ /dev/null @@ -1,136 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/client/src/images/tutorial/tutorial-page-2.svg b/client/src/images/tutorial/tutorial-page-2.svg deleted file mode 100755 index 8757e9a..0000000 --- a/client/src/images/tutorial/tutorial-page-2.svg +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/client/src/images/tutorial/tutorial-page-3.svg b/client/src/images/tutorial/tutorial-page-3.svg deleted file mode 100755 index c04a47d..0000000 --- a/client/src/images/tutorial/tutorial-page-3.svg +++ /dev/null @@ -1,285 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/client/src/images/tutorial/tutorial-page-5.svg b/client/src/images/tutorial/tutorial-page-5.svg deleted file mode 100755 index 5b290de..0000000 --- a/client/src/images/tutorial/tutorial-page-5.svg +++ /dev/null @@ -1,281 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -