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 @@
-