diff --git a/src/assets/tutorial/01.svg b/src/assets/tutorial/01.svg
new file mode 100644
index 0000000..af012b6
--- /dev/null
+++ b/src/assets/tutorial/01.svg
@@ -0,0 +1,14 @@
+
diff --git a/src/assets/tutorial/02.svg b/src/assets/tutorial/02.svg
new file mode 100644
index 0000000..590699f
--- /dev/null
+++ b/src/assets/tutorial/02.svg
@@ -0,0 +1,9 @@
+
diff --git a/src/assets/tutorial/03.svg b/src/assets/tutorial/03.svg
new file mode 100644
index 0000000..b10fb2e
--- /dev/null
+++ b/src/assets/tutorial/03.svg
@@ -0,0 +1,15 @@
+
diff --git a/src/assets/tutorial/04.svg b/src/assets/tutorial/04.svg
new file mode 100644
index 0000000..7d80141
--- /dev/null
+++ b/src/assets/tutorial/04.svg
@@ -0,0 +1,15 @@
+
diff --git a/src/assets/tutorial/05.svg b/src/assets/tutorial/05.svg
new file mode 100644
index 0000000..32610c1
--- /dev/null
+++ b/src/assets/tutorial/05.svg
@@ -0,0 +1,15 @@
+
diff --git a/src/components/BlockContainer.jsx b/src/components/BlockContainer.jsx
index 6c83333..786e123 100644
--- a/src/components/BlockContainer.jsx
+++ b/src/components/BlockContainer.jsx
@@ -1,31 +1,45 @@
import { useState, useEffect } from "react";
-
import useStore from "../store";
-
import InputBlock from "./common/InputBlock";
import MethodBlock from "./common/MethodBlock";
-
import { fetchBlocks } from "../services/blocks";
-
import {
+ Content,
+ Tab,
+ TabList,
BlockList,
InputBlockList,
MethodBlockList,
} from "../style/BlockContainerStyle";
-import { Header, Section, Content } from "../style/CommonStyle";
+import { Header, Section } from "../style/CommonStyle";
const BlockContainer = () => {
const { handleBlockDragStart, handleBlockDragOver, handleDeleteBlock } =
useStore();
const [inputBlocks, setInputBlocks] = useState([]);
const [methodBlocks, setMethodBlocks] = useState([]);
+ const [activeTab, setActiveTab] = useState("All");
+ const [actions, setActions] = useState(["All"]);
useEffect(() => {
const renderBlocks = async () => {
try {
const blocks = await fetchBlocks();
+
setInputBlocks(blocks.inputBlocks);
setMethodBlocks(blocks.methodBlocks);
+
+ const allActions = new Set(["All"]);
+
+ const updateAction = (allBlocks) => {
+ allBlocks.forEach((block) =>
+ block.actions.forEach((action) => allActions.add(action)),
+ );
+ };
+
+ [blocks.inputBlocks, blocks.methodBlocks].forEach(updateAction);
+
+ setActions([...allActions]);
} catch (error) {
console.error("Fetch Error");
}
@@ -33,15 +47,46 @@ const BlockContainer = () => {
renderBlocks();
}, []);
+ const filterBlocksByAction = (blocks, action) => {
+ return blocks.filter((block) => block.actions.includes(action));
+ };
+
+ const filterBlocks = (action) => {
+ if (action === "All") {
+ return { inputBlocks, methodBlocks };
+ }
+
+ return {
+ inputBlocks: filterBlocksByAction(inputBlocks, action),
+ methodBlocks: filterBlocksByAction(methodBlocks, action),
+ };
+ };
+
+ const {
+ inputBlocks: filteredInputBlocks,
+ methodBlocks: filteredMethodBlocks,
+ } = filterBlocks(activeTab);
+
return (
+
+ {actions.map((action) => (
+ setActiveTab(action)}
+ >
+ {action}
+
+ ))}
+
- {inputBlocks.map((inputBlock) => (
+ {filteredInputBlocks.map((inputBlock) => (
{
))}
- {methodBlocks.map((methodBlock) => (
+ {filteredMethodBlocks.map((methodBlock) => (
{
+ const [currentIndex, setCurrentIndex] = useState(0);
+
+ const handleNextButton = () => {
+ if (currentIndex < tutorials.length - 1) {
+ setCurrentIndex(currentIndex + 1);
+ } else {
+ onClose();
+ }
+ };
+
+ const handlePreviousButton = () => {
+ if (currentIndex > 0) {
+ setCurrentIndex(currentIndex - 1);
+ }
+ };
+
+ const currentTutorial = tutorials[currentIndex];
+
+ return (
+
+
+
+
+
+
+
+
+ {currentTutorial.content.split(".").join(".\n")}
+
+
+
+
+
+
+
+
+ );
+};
+
+export default TutorialModal;
diff --git a/src/pages/Main.jsx b/src/pages/Main.jsx
index 567dee5..4c8c472 100644
--- a/src/pages/Main.jsx
+++ b/src/pages/Main.jsx
@@ -1,17 +1,56 @@
+import { useState } from "react";
+
import useStore from "../store";
import BlockContainer from "../components/BlockContainer";
import BlockDashboard from "../components/BlockDashboard";
import TestCodeDashboard from "../components/TestCodeDashboard";
+import Tutorial from "../components/common/Tutorial";
+
+import firstStep from "../assets/tutorial/01.svg";
+import secondStep from "../assets/tutorial/02.svg";
+import thirdStep from "../assets/tutorial/03.svg";
+import fourthStep from "../assets/tutorial/04.svg";
+import fifthStep from "../assets/tutorial/05.svg";
const Main = () => {
const { handleKeyDown } = useStore();
+ const [showTutorial, setShowTutorial] = useState(true);
+
+ const tutorials = [
+ {
+ image: firstStep,
+ content:
+ "왼쪽의 회색 블록에는 사용자가 원하는 값을 입력할 수 있습니다.오른쪽의 남색 블록으로 사용자가 원하는 동작을 선택할 수 있습니다.",
+ },
+ {
+ image: secondStep,
+ content: "Block Dashboard로 블록을 드래그할 수 있습니다.",
+ },
+ {
+ image: thirdStep,
+ content:
+ "하나의 라인 블록에는 최소 하나의 남색 블록이 포함되어야 합니다.",
+ },
+ { image: fourthStep, content: "reset 버튼을 누르면 블록이 초기화됩니다." },
+ {
+ image: fifthStep,
+ content: "create 버튼을 누르면 테스트 코드가 생성됩니다.",
+ },
+ ];
return (
+ {showTutorial && (
+ setShowTutorial(false)}
+ />
+ )}
);
};
diff --git a/src/style/BlockContainerStyle.jsx b/src/style/BlockContainerStyle.jsx
index eae3144..064c6e9 100644
--- a/src/style/BlockContainerStyle.jsx
+++ b/src/style/BlockContainerStyle.jsx
@@ -1,9 +1,56 @@
import styled from "styled-components";
+const Content = styled.div`
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ position: relative;
+ height: 100vh;
+ overflow: scroll;
+ overflow-x: auto;
+`;
+
+const TabList = styled.nav`
+ display: flex;
+ flex-direction: column;
+ position: absolute;
+ top: 0;
+ left: 0;
+ height: 100%;
+ padding: 2rem 0.5rem;
+ background-color: transparent;
+ gap: 2rem;
+
+ @media (max-width: 1120px) {
+ position: static;
+ top: unset;
+ left: unset;
+ }
+`;
+
+const Tab = styled.button`
+ border: none;
+ background: ${({ theme }) => theme.color.lightGrayColor};
+ color: ${({ theme, "data-active": active }) =>
+ active ? theme.color.whiteColor : theme.color.blackColor};
+ font-size: ${({ theme }) => theme.fontSize.xsmall};
+ text-align: left;
+ cursor: pointer;
+
+ &:hover {
+ background-color: ${({ theme }) => theme.color.grayColor};
+ }
+
+ &:focus {
+ outline: none;
+ }
+`;
+
const BlockList = styled.ul`
display: flex;
+ flex-direction: column;
gap: 2rem;
- margin: 4rem;
+ padding: 2rem;
`;
const InputBlockList = styled.ul`
@@ -18,4 +65,4 @@ const MethodBlockList = styled.ul`
gap: 1rem;
`;
-export { BlockList, InputBlockList, MethodBlockList };
+export { Content, Tab, TabList, BlockList, InputBlockList, MethodBlockList };
diff --git a/src/style/TutorialStyle.jsx b/src/style/TutorialStyle.jsx
new file mode 100644
index 0000000..b79fae7
--- /dev/null
+++ b/src/style/TutorialStyle.jsx
@@ -0,0 +1,20 @@
+import styled from "styled-components";
+
+const ImageContainer = styled.div`
+ display: flex;
+ gap: 4rem;
+ width: 50rem;
+ height: 20rem;
+
+ img {
+ width: 50rem;
+ }
+`;
+
+const ContentContainer = styled.pre`
+ display: flex;
+ line-height: 2rem;
+ font-weight: bold;
+`;
+
+export { ImageContainer, ContentContainer };