diff --git a/.babelrc b/.babelrc
new file mode 100644
index 000000000..8aa924d7c
--- /dev/null
+++ b/.babelrc
@@ -0,0 +1,3 @@
+{
+ "presets": ["@babel/preset-env"]
+}
\ No newline at end of file
diff --git a/.eslintrc.json b/.eslintrc.json
new file mode 100644
index 000000000..54b9a9b16
--- /dev/null
+++ b/.eslintrc.json
@@ -0,0 +1,18 @@
+{
+ "extends": ["airbnb", "prettier"],
+ "env": {
+ "browser": true,
+ "jest/globals": true,
+ "es2020": true
+ },
+ "parser": "@babel/eslint-parser",
+ "parserOptions": {
+ "ecmaVersion": 12,
+ "sourceType": "module"
+ },
+ "plugins": ["prettier", "jest", "@babel"],
+ "rules": {
+ "import/extensions": ["off"],
+ "lines-between-class-members": ["off"]
+ }
+}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000..448cd77c1
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+.vscode/
+node_modules/
+package.json
+package-lock.json
diff --git a/README.md b/README.md
index e97a1d649..6cd835035 100644
--- a/README.md
+++ b/README.md
@@ -1,114 +1,155 @@
# ๐ ์งํ์ฒ ๋
ธ์ ๋ ๋ฏธ์
+## ์๊ฐ
+
+๋๋ง์ ์ญ์ ๋ง๋ค๊ณ , ๋ง๋ค์ด์ง ์ญ๋ค์ ๋ฐํ์ผ๋ก ๋๋ง์ ์งํ์ฒ ๋
ธ์ ๊ณผ ์งํ์ฒ ๋
ธ์ ๋๋ฅผ ์ ์ํ ์ ์๋ ํ๋ก๊ทธ๋จ์
๋๋ค. ์ค์ ์งํ์ฒ ๋
ธ์ ๋์ ์ ์ฌํ ๋
ธ์ ๋๋ฅผ ์ ์ํ ์ ์๋๋ก ์ฌ๋ฌ ๊ธฐ๋ฅ๊ณผ ์ ์ฝ์ฌํญ์ด ์ ๊ณต๋ฉ๋๋ค.
+
+
+
## ๐ ๊ธฐ๋ฅ ์๊ตฌ์ฌํญ
+
+
### ์งํ์ฒ ์ญ ๊ด๋ จ ๊ธฐ๋ฅ
-- ์งํ์ฒ ์ญ์ ๋ฑ๋กํ๊ณ ์ญ์ ํ ์ ์๋ค. (๋จ, ๋
ธ์ ์ ๋ฑ๋ก๋ ์ญ์ ์ญ์ ํ ์ ์๋ค)
-- ์ค๋ณต๋ ์งํ์ฒ ์ญ ์ด๋ฆ์ด ๋ฑ๋ก๋ ์ ์๋ค.
-- ์งํ์ฒ ์ญ์ 2๊ธ์ ์ด์์ด์ด์ผ ํ๋ค.
-- ์งํ์ฒ ์ญ์ ๋ชฉ๋ก์ ์กฐํํ ์ ์๋ค.
+
+- ์งํ์ฒ ์ญ์ ์ถ๊ฐ (add subway station)
+ - :exclamation: ์์ธ#1 - ์ถ๊ฐํ๋ ค๋ ์ญ์ด ๋ค๋ฅธ ์ญ๊ณผ ์ค๋ณต๋ ์ด๋ฆ์ ๊ฐ์ง ๊ฒฝ์ฐ
+ - :exclamation: ์์ธ#2 - ์ญ์ ์ด๋ฆ์ด 2๊ธ์ ๋ฏธ๋ง์ผ ๊ฒฝ์ฐ(ํ๊ธ, ์์ด ๋ชจ๋)
+- ์งํ์ฒ ์ญ์ ์ญ์ (delete subway station)
+ - :exclamation: ์์ธ#3 - ์ญ์ ํ๋ ค๋ ์ญ์ด ๋
ธ์ ์ ์ถ๊ฐ๋์ด ์๋ ๊ฒฝ์ฐ
+- ์งํ์ฒ ์ญ ์ถ๊ฐ ๊ฐ๋ฅ ์ฌ๋ถ ๊ฒ์ฌ (check if the subway station can be added)
+ - :wrench: ํด๊ฒฐ(์์ธ #1,#2) - ์ถ๊ฐํ๋ ค๋ ์ญ์ ์ด๋ฆ์ด ๋ค๋ฅธ ์ญ๊ณผ ์ค๋ณต๋๋์ง, 2๊ธ์ ๋ฏธ๋ง์ธ์ง๋ฅผ ๊ฒ์ฌ
+- ์งํ์ฒ ์ญ ์ญ์ ๊ฐ๋ฅ ์ฌ๋ถ ๊ฒ์ฌ (check if the subway station can be deleted)
+ - :wrench: ํด๊ฒฐ(์์ธ #3) - ์ญ์ ํ ์ ์๋ ์ญ์ ์ญ์ ํ๋ ค๋ ๊ฒ์ธ์ง๋ฅผ ๊ฒ์ฌ
+
+
### ์งํ์ฒ ๋
ธ์ ๊ด๋ จ ๊ธฐ๋ฅ
-- ์งํ์ฒ ๋
ธ์ ์ ๋ฑ๋กํ๊ณ ์ญ์ ํ ์ ์๋ค.
-- ์ค๋ณต๋ ์งํ์ฒ ๋
ธ์ ์ด๋ฆ์ด ๋ฑ๋ก๋ ์ ์๋ค.
-- ๋
ธ์ ๋ฑ๋ก ์ ์ํ ์ข
์ ์ญ๊ณผ ํํ ์ข
์ ์ญ์ ์
๋ ฅ๋ฐ๋๋ค.
-- ์งํ์ฒ ๋
ธ์ ์ ๋ชฉ๋ก์ ์กฐํํ ์ ์๋ค.
+
+- ์งํ์ฒ ๋
ธ์ ์ ์ถ๊ฐ (add subway line)
+ - :exclamation: ์์ธ#4 - ์ถ๊ฐํ๋ ค๋ ๋
ธ์ ์ด ๋ค๋ฅธ ๋
ธ์ ๊ณผ ์ค๋ณต๋ ์ด๋ฆ์ ๊ฐ์ง ๊ฒฝ์ฐ
+ - :exclamation: ์์ธ#5 - ์ถ๊ฐํ ๋ ์ค์ ํ๋ ์ํ, ํํ ์ข
์ ์ญ์ด ๊ฐ์ ๊ฒฝ์ฐ
+- ์งํ์ฒ ๋
ธ์ ์ ์ญ์ (delete subway line)
+- ์งํ์ฒ ๋
ธ์ ์ถ๊ฐ ๊ฐ๋ฅ ์ฌ๋ถ ๊ฒ์ฌ (check if the subway line can be added)
+ - :wrench: ํด๊ฒฐ(์์ธ #4,#5) - ์ถ๊ฐํ๋ ค๋ ๋
ธ์ ์ด ๋ค๋ฅธ ๋
ธ์ ๊ณผ ์ค๋ณต๋ ์ด๋ฆ์ ๊ฐ์ง๋์ง, ํด๋น ๋
ธ์ ์ ์ํ, ํํ ์ข
์ ์ญ์ด ๊ฐ์์ง๋ฅผ ๊ฒ์ฌ
+- ์งํ์ฒ ๋
ธ์ ์ ์ํ ์ข
์ ๋ถํฐ ํํ ์ข
์ ๊น์ง ์ฐ๊ฒฐ๋ ์์๋๋ก ์ญ ๋ชฉ๋ก์ ์กฐํ (get all subway stations in subway line)
+
+
+
+### ์งํ์ฒ ์ ์ฒด ์ ๋ณด ์กฐํ ๊ธฐ๋ฅ
+
+- ์ ์ฒด ์งํ์ฒ ์ญ์ ๋ชฉ๋ก ์กฐํ (get all subway stations)
+- ์ ์ฒด ์งํ์ฒ ๋
ธ์ ์ ๋ชฉ๋ก ์กฐํ (get all subway lines)
+
+
### ์งํ์ฒ ๊ตฌ๊ฐ ์ถ๊ฐ ๊ธฐ๋ฅ
-- ์งํ์ฒ ๋
ธ์ ์ ๊ตฌ๊ฐ์ ์ถ๊ฐํ๋ ๊ธฐ๋ฅ์ ๋
ธ์ ์ ์ญ์ ์ถ๊ฐํ๋ ๊ธฐ๋ฅ์ด๋ผ๊ณ ๋ ํ ์ ์๋ค.
- - ์ญ๊ณผ ์ญ์ฌ์ด๋ฅผ ๊ตฌ๊ฐ์ด๋ผ ํ๊ณ ์ด ๊ตฌ๊ฐ๋ค์ ๋ชจ์์ด ๋
ธ์ ์ด๋ค.
-- ํ๋์ ์ญ์ ์ฌ๋ฌ๊ฐ์ ๋
ธ์ ์ ์ถ๊ฐ๋ ์ ์๋ค.
-- ์ญ๊ณผ ์ญ ์ฌ์ด์ ์๋ก์ด ์ญ์ด ์ถ๊ฐ ๋ ์ ์๋ค.
-- ๋
ธ์ ์์ ๊ฐ๋๊ธธ์ ์๊ธธ ์ ์๋ค.
+
+- ์งํ์ฒ ๋
ธ์ ์ ๊ตฌ๊ฐ์ ์ถ๊ฐ, ์ฆ ์งํ์ฒ ๋
ธ์ ์์ ๋ ์ญ ์ฌ์ด์ ์๋ก์ด ์ญ ๋ผ์๋ฃ๊ธฐ(insert subway station in subway line)
+ - :exclamation: ์์ธ#6 - ๋ผ์ ๋ฃ์ผ๋ ค๋ ์ญ์ด ์ด๋ฏธ ๋
ธ์ ์์ ์๋ ๊ฒฝ์ฐ
+ - :exclamation: ์์ธ#7 - ๋ผ์๋ฃ์ผ๋ ค๋ ์ญ์ ์์๊ฐ ์ซ์๋ก ํํ๋์ด ์์ง ์์ ๊ฒฝ์ฐ
+- ์งํ์ฒ ๋
ธ์ ๊ตฌ๊ฐ ์ถ๊ฐ ๊ฐ๋ฅ์ฑ ์ฌ๋ถ ๊ฒ์ฌ (check if the subway station can be inserted)
+ - :wrench: ํด๊ฒฐ(์์ธ #6) - ๋ผ์ ๋ฃ์ผ๋ ค๋ ์ญ์ด ์ด๋ฏธ ๋
ธ์ ์์ ์๋์ง ๊ฒ์ฌ
+ - :wrench: ํด๊ฒฐ(์์ธ #7) - ๋ผ์ ๋ฃ์ผ๋ ค๋ ์ญ์ ์์๊ฐ์ด ์ซ์์ธ์ง ๊ฒ์ฌ
+
+
+
+
### ์งํ์ฒ ๊ตฌ๊ฐ ์ญ์ ๊ธฐ๋ฅ
-- ๋
ธ์ ์ ๋ฑ๋ก๋ ์ญ์ ์ ๊ฑฐํ ์ ์๋ค.
-- ์ข
์ ์ ์ ๊ฑฐํ ๊ฒฝ์ฐ ๋ค์ ์ญ์ด ์ข
์ ์ด ๋๋ค.
-- ๋
ธ์ ์ ํฌํจ๋ ์ญ์ด ๋๊ฐ ์ดํ์ผ ๋๋ ์ญ์ ์ ๊ฑฐํ ์ ์๋ค.
-
+- ์งํ์ฒ ๋
ธ์ ์ ๊ตฌ๊ฐ์ ์ญ์ , ์ฆ ์งํ์ฒ ๋
ธ์ ์์์ ํน์ ์ญ์ ๋นผ๊ธฐ (pull out subway station in subway line)
+ - :exclamation: ์์ธ#8 - ๋
ธ์ ์ ํฌํจ๋ ์ญ์ ์ญ์ ํ๋ ค๋ ์์ ์ ๊ทธ ๋
ธ์ ์ ํฌํจ๋ ์ญ์ด ๋๊ฐ ์ดํ์ผ ๊ฒฝ์ฐ
+- ์งํ์ฒ ๋
ธ์ ๊ตฌ๊ฐ ์ญ์ ๊ฐ๋ฅ์ฑ ์ฌ๋ถ ๊ฒ์ฌ (check if the subway station can be pulled out)
+ - :wrench: ํด๊ฒฐ(์์ธ #8) - ์ญ์ ํ ๊ทธ ๋
ธ์ ์ ํฌํจ๋ ์ญ์ด ๋๊ฐ ๋ฏธ๋ง์ธ์ง๋ฅผ ๊ฒ์ฌ
+
+
+
+### ๋ก์ปฌ ์คํ ๋ฆฌ์ง ๊ธฐ๋ฅ
+
+- ์ ์ฒด ์งํ์ฒ ์ญ, ๋
ธ์ ๋ชฉ๋ก์ ํฌํจํ ์งํ์ฒ ๋
ธ์ ๋ ์ ๋ณด๋ฅผ ๋ก์ปฌ ์คํ ๋ฆฌ์ง์ ์ ์ฅ (save subway map info to local storage)
+- ์ ์ฒด ์งํ์ฒ ์ญ, ๋
ธ์ ๋ชฉ๋ก์ ํฌํจํ ์งํ์ฒ ๋
ธ์ ๋ ์ ๋ณด๋ฅผ ๋ก์ปฌ ์คํ ๋ฆฌ์ง์์ ๋ถ๋ฌ์ค๊ธฐ (load subway map info from local storage)
+
+
+
+### ์ธํฐํ์ด์ค ์ ํ ๊ธฐ๋ฅ
+
+- ๋ฒํผ ํด๋ฆญ์ผ๋ก ์ธํฐํ์ด์ค ์ ํ (convert interface with button click)
+
+### ์ธํฐํ์ด์ค ๋ฐ์ดํฐ ๋๊ธฐํ ๊ธฐ๋ฅ
+
+- ์ธํฐํ์ด์ค์ ์
๋ ฅ๋ ์ ๋ณด ์ ์ฅํ๊ธฐ (save interface data)
+- ์
๋ ฅ ์ ๋ณด๋ฅผ ๋ถ๋ฌ์ค๊ธฐ (load interface data)
+- ์
๋ ฅ ์ ๋ณด๋ฅผ ์ธํฐํ์ด์ค์ ๋๊ธฐํ ์ํค๊ธฐ(sync interface data to interface)
+
+### ์ญ๊ด๋ฆฌ ์ธํฐํ์ด์ค ๊ธฐ๋ฅ
+
+- ์ญ ์ถ๊ฐ ๊ธฐ๋ฅ์ ์ํ ์ ํจ์ฑ ๊ฒ์ฌ์ ๋ฐ๋ฅธ ์๋ด๋ฉ์์ง ์ถ๋ ฅ (validation check before 'add subway station' with alert message)
+ - ์๋ชป๋ ์
๋ ฅ์ด ๋ค์ด์์ ๋, ์ญ์ ์ถ๊ฐํ ์ ์๋ ์ด์ ๋ฅผ ์๋ ค์ฃผ์ด ์ฌ์
๋ ฅ์ ์ ๋
+- ์ญ ์ญ์ ๊ธฐ๋ฅ์ ์ํ ์ ํจ์ฑ ๊ฒ์ฌ์ ๋ฐ๋ฅธ ์๋ด๋ฉ์์ง ์ถ๋ ฅ (validation check before 'delete subway station' with alert message)
+ - ์ญ์ ํ ์ ์๋ ์ญ์ ์ญ์ ํ๋ ค ํ ๋, ์ญ์ ์ญ์ ํ ์ ์๋ ์ด์ ๋ฅผ ์๋ ค์ฃผ์ด ์ฌ์
๋ ฅ์ ์ ๋
+- ์ญ ์ถ๊ฐ & ์ญ์ ๊ฒฐ๊ณผ ๋ณด์ฌ์ฃผ๊ธฐ(show result of add & delete subway station)
+
+
+
+### ๋
ธ์ ๊ด๋ฆฌ ์ธํฐํ์ด์ค ๊ธฐ๋ฅ
-### ์งํ์ฒ ๋
ธ์ ์ ๋ฑ๋ก๋ ์ญ ์กฐํ ๊ธฐ๋ฅ
-- ๋
ธ์ ์ ์ํ ์ข
์ ๋ถํฐ ํํ ์ข
์ ๊น์ง ์ฐ๊ฒฐ๋ ์์๋๋ก ์ญ ๋ชฉ๋ก์ ์กฐํํ ์ ์๋ค.
+- ๋
ธ์ ์ถ๊ฐ ๊ธฐ๋ฅ์ ์ํ ์ ํจ์ฑ ๊ฒ์ฌ์ ๋ฐ๋ฅธ ์๋ด๋ฉ์์ง ์ถ๋ ฅ (validation check before 'add subway line' with alert message)
+ - ์๋ชป๋ ์
๋ ฅ์ด ๋ค์ด์์ ๋, ๋
ธ์ ์ ์ถ๊ฐํ ์ ์๋ ์ด์ ๋ฅผ ์๋ ค์ฃผ์ด ์ฌ์
๋ ฅ์ ์ ๋
+- ๋
ธ์ ์ถ๊ฐ & ์ญ์ ๊ฒฐ๊ณผ ๋ณด์ฌ์ฃผ๊ธฐ (show result of add & delete subway line)
+### ๊ตฌ๊ฐ๊ด๋ฆฌ ์ธํฐํ์ด์ค ๊ธฐ๋ฅ
+
+- ๊ตฌ๊ฐ ์ถ๊ฐ ๊ธฐ๋ฅ์ ์ํ ์ ํจ์ฑ ๊ฒ์ฌ์ ๋ฐ๋ฅธ ์๋ด๋ฉ์์ง ์ถ๋ ฅ (validation check before 'insert & pull out subway station in subway line' with alert message)
+ - ๊ตฌ๊ฐ์ ์ถ๊ฐํ ์ ์๋ ์ด์ ๋ฅผ ์๋ ค์ฃผ์ด ์ฌ์
๋ ฅ์ ์ ๋
+ - ์ญ์ ์ญ์ ํ ์ ์๋ ์ด์ ๋ฅผ ์๋ ค์ฃผ๋ฉด์ ๋
ธ์ ์์ฒด๋ฅผ ์ ๊ฑฐํ ๋ฐฉ๋ฒ์ ์๋ด
+- ๊ตฌ๊ฐ ์ถ๊ฐ & ์ญ์ ๊ฒฐ๊ณผ ๋ณด์ฌ์ฃผ๊ธฐ (show result of insert & pull out subway station)
+
+
+
+### ๋
ธ์ ๋ ์ถ๋ ฅ ์ธํฐํ์ด์ค ๊ธฐ๋ฅ
+
+- ์ ์ฒด ์งํ์ฒ ๋
ธ์ ์ ๋ชฉ๋ก ์กฐํ ๊ธฐ๋ฅ ํธ์ถ & ์กฐํํ ๋
ธ์ ๋ ๋ณด์ฌ์ฃผ๊ธฐ (trigger 'get all subway lines' & show subway map)
+
+
+
+### ๋ก์ปฌ์คํ ๋ฆฌ์ง ๋๊ธฐํ
+
+- ๋ซ๊ธฐ, ์๋ก๊ณ ์นจ ์ ๋ก์ปฌ์คํ ๋ฆฌ์ง์ ์ธํฐํ์ด์ค ์ ๋ณด, ๋
ธ์ ๋ ์ ๋ณด ์ ์ฅํ๊ธฐ (save interface & subway Map data to local storage before exit or reload)
+- ์ ์ ์ ์ธํฐํ์ด์ค ์ ๋ณด, ๋
ธ์ ๋ ์ ๋ณด๋ฅผ ๋ก์ปฌ์คํ ๋ฆฌ์ง๋ก๋ถํฐ ๋ฐ์ ๋๊ธฐํ ์ํค๊ธฐ (synchronize interface & subway Map data from local storage when user access)
+
+
+
+
+
+
+
## ๐ป ํ๋ก๊ทธ๋จ ์คํ ๊ฒฐ๊ณผ
+
+
### ์ญ๊ด๋ฆฌ
+
-### ๋
ธ์ ๊ด๋ฆฌ
-
+
-### ๊ตฌ๊ฐ๊ด๋ฆฌ
-
+### ๋
ธ์ ๊ด๋ฆฌ
-### ๋
ธ์ ๋ ์ถ๋ ฅ
-
+
+
-## โ
ํ๋ก๊ทธ๋๋ฐ ์๊ตฌ์ฌํญ
+### ๊ตฌ๊ฐ๊ด๋ฆฌ
-### ๋ฉ๋ด ๋ฒํผ
-- ์ญ ๊ด๋ฆฌ button ํ๊ทธ๋ `#station-manager-button` id๊ฐ์ ๊ฐ์ง๋ค.
-- ๋
ธ์ ๊ด๋ฆฌ button ํ๊ทธ๋ `#line-manager-button` id๊ฐ์ ๊ฐ์ง๋ค.
-- ๊ตฌ๊ฐ ๊ด๋ฆฌ button ํ๊ทธ๋ `#section-manager-button` id๊ฐ์ ๊ฐ์ง๋ค.
-- ์งํ์ฒ ๋
ธ์ ๋ ์ถ๋ ฅ ๊ด๋ฆฌ button ํ๊ทธ๋ `#map-print-manager-button` id๊ฐ์ ๊ฐ์ง๋ค.
+
-### ์งํ์ฒ ์ญ ๊ด๋ จ ๊ธฐ๋ฅ
-- ์งํ์ฒ ์ญ์ ์
๋ ฅํ๋ input ํ๊ทธ๋ `#station-name-input` id๊ฐ์ ๊ฐ์ง๋ค.
-- ์งํ์ฒ ์ญ์ ์ถ๊ฐํ๋ button ํ๊ทธ๋ `#station-add-button` id๊ฐ์ ๊ฐ์ง๋ค.
-- ์งํ์ฒ ์ญ์ ์ญ์ ํ๋ button ํ๊ทธ๋ `.station-delete-button` class๊ฐ์ ๊ฐ์ง๋ค.
+
-### ์งํ์ฒ ๋
ธ์ ๊ด๋ จ ๊ธฐ๋ฅ
-- ์งํ์ฒ ๋
ธ์ ์ ์ด๋ฆ์ ์
๋ ฅํ๋ input ํ๊ทธ๋ `#line-name-input` id๊ฐ์ ๊ฐ์ง๋ค.
-- ์งํ์ฒ ๋
ธ์ ์ ์ํ ์ข
์ ์ ์ ํํ๋ select ํ๊ทธ๋ `#line-start-station-selector` id๊ฐ์ ๊ฐ์ง๋ค.
-- ์งํ์ฒ ๋
ธ์ ์ ํํ ์ข
์ ์ ์ ํํ๋ select ํ๊ทธ๋ `#line-end-station-selector` id๊ฐ์ ๊ฐ์ง๋ค.
-- ์งํ์ฒ ๋
ธ์ ์ ์ถ๊ฐํ๋ button ํ๊ทธ๋ `#line-add-button` id๊ฐ์ ๊ฐ์ง๋ค.
-- ์งํ์ฒ ๋
ธ์ ์ ์ญ์ ํ๋ button ํ๊ทธ๋ `.line-delete-button` class๊ฐ์ ๊ฐ์ง๋ค.
+### ๋
ธ์ ๋ ์ถ๋ ฅ
-### ์งํ์ฒ ๊ตฌ๊ฐ ์ถ๊ฐ ๊ธฐ๋ฅ
-- ์งํ์ฒ ๋
ธ์ ์ ์ ํํ๋ button ํ๊ทธ๋ `.section-line-menu-button` class๊ฐ์ ๊ฐ์ง๋ค.
-- ์งํ์ฒ ๊ตฌ๊ฐ์ ์ค์ ํ ์ญ select ํ๊ทธ๋ `#section-station-selector` id๊ฐ์ ๊ฐ์ง๋ค.
-- ์งํ์ฒ ๊ตฌ๊ฐ์ ์์๋ฅผ ์
๋ ฅํ๋ input ํ๊ทธ๋ `#section-order-input` id๊ฐ์ ๊ฐ์ง๋ค.
-- ์งํ์ฒ ๊ตฌ๊ฐ์ ๋ฑ๋กํ๋ button ํ๊ทธ๋ `#section-add-button` id๊ฐ์ ๊ฐ์ง๋ค.
-- ์งํ์ฒ ๊ตฌ๊ฐ์ ์ ๊ฑฐํ๋ button ํ๊ทธ๋ `.section-delete-button` class๊ฐ์ ๊ฐ์ง๋ค.
-
-### ์งํ์ฒ ๋
ธ์ ๋ ์ถ๋ ฅ ๊ธฐ๋ฅ
-- ์งํ์ฒ ๋
ธ์ ๋ ์ถ๋ ฅ ๋ฒํผ์ ๋๋ฅด๋ฉด `
` ํ๊ทธ๋ฅผ ๋ง๋ค๊ณ ํด๋น ํ๊ทธ ๋ด๋ถ์ ๋
ธ์ ๋๋ฅผ ์ถ๋ ฅํ๋ค.
-
-### ๊ธฐ์กด ์๊ตฌ์ฌํญ
-
-- ์ฌ์ฉ์๊ฐ ์๋ชป๋ ์
๋ ฅ ๊ฐ์ ์์ฑํ ๊ฒฝ์ฐ `alert`์ ์ด์ฉํด ๋ฉ์์ง๋ฅผ ๋ณด์ฌ์ฃผ๊ณ , ์ฌ์
๋ ฅํ ์ ์๊ฒ ํ๋ค.
-- ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ(jQuery, Lodash ๋ฑ)๋ฅผ ์ฌ์ฉํ์ง ์๊ณ , ์์ Vanilla JS๋ก๋ง ๊ตฌํํ๋ค.
-- **์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋ ์ปจ๋ฒค์
์ ์งํค๋ฉด์ ํ๋ก๊ทธ๋๋ฐ** ํ๋ค
- - [https://google.github.io/styleguide/jsguide.html](https://google.github.io/styleguide/jsguide.html)
- - [https://ui.toast.com/fe-guide/ko_CODING-CONVENSION/](https://ui.toast.com/fe-guide/ko_CODING-CONVENTION)
-- **indent(์ธ๋ดํธ, ๋ค์ฌ์ฐ๊ธฐ) depth๋ฅผ 3์ด ๋์ง ์๋๋ก ๊ตฌํํ๋ค. 2๊น์ง๋ง ํ์ฉ**ํ๋ค.
- - ์๋ฅผ ๋ค์ด while๋ฌธ ์์ if๋ฌธ์ด ์์ผ๋ฉด ๋ค์ฌ์ฐ๊ธฐ๋ 2์ด๋ค.
- - ํํธ: indent(์ธ๋ดํธ, ๋ค์ฌ์ฐ๊ธฐ) depth๋ฅผ ์ค์ด๋ ์ข์ ๋ฐฉ๋ฒ์ ํจ์(๋๋ ๋ฉ์๋)๋ฅผ ๋ถ๋ฆฌํ๋ฉด ๋๋ค.
-- **ํจ์(๋๋ ๋ฉ์๋)์ ๊ธธ์ด๊ฐ 15๋ผ์ธ์ ๋์ด๊ฐ์ง ์๋๋ก ๊ตฌํํ๋ค.**
- - ํจ์(๋๋ ๋ฉ์๋)๊ฐ ํ ๊ฐ์ง ์ผ๋ง ์ ํ๋๋ก ๊ตฌํํ๋ค.
-- ๋ณ์ ์ ์ธ์ `var` ๋ฅผ ์ฌ์ฉํ์ง ์๋๋ค. `const` ์ `let` ์ ์ฌ์ฉํ๋ค.
- - [const](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/const)
- - [let](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/let)
-- `import` ๋ฌธ์ ์ด์ฉํด ์คํฌ๋ฆฝํธ๋ฅผ ๋ชจ๋ํํ๊ณ ๋ถ๋ฌ์ฌ ์ ์๊ฒ ๋ง๋ ๋ค.
- - [https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/import](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/import)
-- `template literal`์ ์ด์ฉํด ๋ฐ์ดํฐ์ html string์ ๊ฐ๋
์ฑ ์ข๊ฒ ํํํ๋ค.
- - [https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Template_literals](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Template_literals)
-
-### ์ถ๊ฐ๋ ์๊ตฌ์ฌํญ
-- [data](https://developer.mozilla.org/ko/docs/Learn/HTML/Howto/%EB%8D%B0%EC%9D%B4%ED%84%B0_%EC%86%8D%EC%84%B1_%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0)์์ฑ์ ํ์ฉํ์ฌ html ํ๊ทธ์ ์ญ, ๋
ธ์ , ๊ตฌ๊ฐ์ ์ ์ผํ ๋ฐ์ดํฐ ๊ฐ๋ค์ ๊ด๋ฆฌํ๋ค.
-- [localStorage](https://developer.mozilla.org/ko/docs/Web/API/Window/localStorage)๋ฅผ ์ด์ฉํ์ฌ, ์๋ก๊ณ ์นจํ๋๋ผ๋ ๊ฐ์ฅ ์ต๊ทผ์ ์์
ํ ์ ๋ณด๋ค์ ๋ถ๋ฌ์ฌ ์ ์๋๋ก ํ๋ค.
-
-
-
-## ๐ ๋ฏธ์
์ ์ฅ์ ๋ฐ ์งํ ์๊ตฌ์ฌํญ
-
-- ๋ฏธ์
์ [https://github.com/woowacourse/javascript-subway-map-precours](https://github.com/woowacourse/javascript-subway-map-precourse) ์ ์ฅ์๋ฅผ fork/cloneํด ์์ํ๋ค.
-- **๊ธฐ๋ฅ์ ๊ตฌํํ๊ธฐ ์ ์ javascript-subway-precourse/docs/README.md ํ์ผ์ ๊ตฌํํ ๊ธฐ๋ฅ ๋ชฉ๋ก**์ ์ ๋ฆฌํด ์ถ๊ฐํ๋ค.
-- **git์ commit ๋จ์๋ ์ ๋จ๊ณ์์ README.md ํ์ผ์ ์ ๋ฆฌํ ๊ธฐ๋ฅ ๋ชฉ๋ก ๋จ์๋ก ์ถ๊ฐ**ํ๋ค.
-- [ํ๋ฆฌ์ฝ์ค ๊ณผ์ ์ ์ถ](https://github.com/woowacourse/woowacourse-docs/tree/master/precourse) ๋ฌธ์ ์ ์ฐจ๋ฅผ ๋ฐ๋ผ ๋ฏธ์
์ ์ ์ถํ๋ค.
+
diff --git a/babel.config.js b/babel.config.js
new file mode 100644
index 000000000..14906bb61
--- /dev/null
+++ b/babel.config.js
@@ -0,0 +1,6 @@
+module.exports = {
+ plugins: [
+ '@babel/plugin-proposal-private-methods',
+ '@babel/plugin-proposal-class-properties',
+ ],
+};
diff --git a/index.css b/index.css
new file mode 100644
index 000000000..432019fe9
--- /dev/null
+++ b/index.css
@@ -0,0 +1,11 @@
+.content {
+ display: none;
+}
+
+#station-register {
+ display: none;
+}
+
+#registered-station-item-table {
+ display: none;
+}
diff --git a/index.html b/index.html
index fc99deac2..24eb6c15b 100644
--- a/index.html
+++ b/index.html
@@ -3,10 +3,87 @@
์งํ์ฒ ๋
ธ์ ๋ ๊ด๋ฆฌ
+
-
๐ ์งํ์ฒ ๋
ธ์ ๋ ๊ด๋ฆฌ
+ ๐ ์งํ์ฒ ๋
ธ์ ๋ ๊ด๋ฆฌ
+
+
+
+
+
์ญ ์ด๋ฆ
+
+
+
+
+
🚉์งํ์ฒ ์ญ ๋ชฉ๋ก
+
+
+ | ์ญ ์ด๋ฆ |
+ ์ค์ |
+
+
+
+
+
+
+
+
+
๋
ธ์ ์ด๋ฆ
+
+
+
+ ์ํ ์ข
์
+
+ ํํ ์ข
์
+
+
+
+
+
🚉์งํ์ฒ ๋
ธ์ ๋ชฉ๋ก
+
+
+ | ๋
ธ์ ์ด๋ฆ |
+ ์ํ ์ข
์ ์ญ |
+ ํํ ์ข
์ ์ญ |
+ ์ค์ |
+
+
+
+
+
+
+
+
+
๊ตฌ๊ฐ์ ์์ ํ ๋
ธ์ ์ ์ ํํด์ฃผ์ธ์
+
+
+
1ํธ์ ๊ด๋ฆฌ
+ ๊ตฌ๊ฐ ๋ฑ๋ก
+
+
+
+
+
+
+ | ์์ |
+ ์ด๋ฆ |
+ ์ค์ |
+
+
+
+
+
+
+
+
diff --git a/src/classes/classes.test.js b/src/classes/classes.test.js
new file mode 100644
index 000000000..eed3f5944
--- /dev/null
+++ b/src/classes/classes.test.js
@@ -0,0 +1,81 @@
+import SubWayStation from './subwayStation.js';
+import SubwayLine from './subwayLine.js';
+import SubwayMap from './subwayMap.js';
+
+describe('SubWayStation', () => {
+ const station1Name = 'station1';
+ const station2Name = 'station2';
+ const station3Name = 'station3';
+ const station1 = new SubWayStation();
+ const station2 = new SubWayStation();
+ const station3 = new SubWayStation();
+ const subwayMap = new SubwayMap();
+ subwayMap.addStation(station1, station1Name);
+ subwayMap.addStation(station2, station2Name);
+ subwayMap.addStation(station3, station3Name);
+ it('add subway station', () => {
+ expect(subwayMap.allStations).toHaveProperty(station1Name);
+ expect(subwayMap.allStations).toHaveProperty(station2Name);
+ expect(subwayMap.allStations).toHaveProperty(station3Name);
+ });
+ it('delete subway station', () => {
+ subwayMap.deleteStationByName(station3Name);
+ expect(subwayMap.allStations).toHaveProperty(station1Name);
+ expect(subwayMap.allStations).toHaveProperty(station2Name);
+ expect(subwayMap.allStations).not.toHaveProperty(station3Name);
+ });
+});
+
+describe('SubwayLine', () => {
+ const station1Name = 'station1';
+ const station2Name = 'station2';
+ const station3Name = 'station3';
+ const station1 = new SubWayStation();
+ const station2 = new SubWayStation();
+ const station3 = new SubWayStation();
+ const subwayMap = new SubwayMap();
+ subwayMap.addStation(station1, station1Name);
+ subwayMap.addStation(station2, station2Name);
+ subwayMap.addStation(station3, station3Name);
+ const line1 = new SubwayLine(station1Name, station2Name);
+ const line2 = new SubwayLine(station2Name, station3Name);
+ const line1Name = 'line1';
+ const line2Name = 'line2';
+ subwayMap.addLine(line1, line1Name);
+ subwayMap.addLine(line2, line2Name);
+ it('add subway line', () => {
+ expect(subwayMap.allLines).toHaveProperty(line1Name);
+ expect(subwayMap.allLines).toHaveProperty(line2Name);
+ });
+ it('delete subway line', () => {
+ subwayMap.deleteLineByName(line2Name);
+ expect(subwayMap.allLines).toHaveProperty(line1Name);
+ expect(subwayMap.allLines).not.toHaveProperty(line2Name);
+ });
+ it('add station to line', () => {
+ line1.addStationToLineByName(station3Name);
+ expect(line1.allStationsInLine).toContain(station3Name);
+ });
+});
+
+describe('SubwaySection', () => {
+ const station1Name = 'station1';
+ const station2Name = 'station2';
+ const station3Name = 'station3';
+ const station1 = new SubWayStation();
+ const station2 = new SubWayStation();
+ const station3 = new SubWayStation();
+ const subwayMap = new SubwayMap();
+ subwayMap.addStation(station1, station1Name);
+ subwayMap.addStation(station2, station2Name);
+ subwayMap.addStation(station3, station3Name);
+ const line1 = new SubwayLine(station1Name, station2Name);
+ it('insert subway station in subway line', () => {
+ line1.insertStationToLineByNameAndIndex(station3Name, 1);
+ expect(line1.allStationsInLine).toContain(station3Name);
+ });
+ it('pull out subway station in subway line', () => {
+ line1.pullOutStationFromLineByName(station3Name);
+ expect(line1.allStationsInLine).not.toContain(station3Name);
+ });
+});
diff --git a/src/classes/subwayLine.js b/src/classes/subwayLine.js
new file mode 100644
index 000000000..ba031aedb
--- /dev/null
+++ b/src/classes/subwayLine.js
@@ -0,0 +1,45 @@
+import { SUBWAY_STATION_IN_LINE_DELETE_LENGTH_LIMIT } from '../constants/configuration.js';
+
+export default class SubwayLine {
+ allStationsInLine = [];
+
+ static checkIsStationsSame(startStationName, endStationName) {
+ return startStationName === endStationName;
+ }
+
+ static checkIsIndexNumberCorrect(index) {
+ return typeof index === 'number' && Number.isNaN(index) === false;
+ }
+
+ constructor(startStationName, endStationName) {
+ this.allStationsInLine.push(startStationName);
+ this.allStationsInLine.push(endStationName);
+ }
+
+ addStationToLineByName(stationName) {
+ if (stationName in this.allStationsInLine === false) {
+ this.allStationsInLine.push(stationName);
+ }
+ }
+
+ insertStationToLineByNameAndIndex({ stationName, index }) {
+ if (stationName in this.allStationsInLine === false) {
+ this.allStationsInLine.splice(index, 0, stationName);
+ }
+ }
+
+ pullOutStationFromLineByIndex(stationIndex) {
+ return this.allStationsInLine.splice(stationIndex, 1);
+ }
+
+ checkIsStationNameExistInLine(stationName) {
+ return this.allStationsInLine.includes(stationName);
+ }
+
+ checkIsAllStationsInLineLengthSameAsLimit() {
+ return (
+ this.allStationsInLine.length <=
+ SUBWAY_STATION_IN_LINE_DELETE_LENGTH_LIMIT
+ );
+ }
+}
diff --git a/src/classes/subwayMap.js b/src/classes/subwayMap.js
new file mode 100644
index 000000000..d0e8d2911
--- /dev/null
+++ b/src/classes/subwayMap.js
@@ -0,0 +1,44 @@
+export default class SubwayMap {
+ #allStations = {};
+ #allLines = {};
+
+ get allStations() {
+ return this.#allStations;
+ }
+
+ get allLines() {
+ return this.#allLines;
+ }
+
+ addStation(station, stationName) {
+ if (stationName in this.#allStations === false) {
+ this.#allStations[stationName] = station;
+ }
+ }
+
+ checkIsStationNameExist(stationName) {
+ return stationName in this.#allStations;
+ }
+
+ deleteStationByName(stationName) {
+ if (stationName in this.#allStations) {
+ delete this.#allStations[stationName];
+ }
+ }
+
+ addLine(line, lineName) {
+ if (lineName in this.#allLines === false) {
+ this.#allLines[lineName] = line;
+ }
+ }
+
+ checkIsLineNameExist(lineName) {
+ return lineName in this.#allLines;
+ }
+
+ deleteLineByName(lineName) {
+ if (lineName in this.#allLines) {
+ delete this.#allLines[lineName];
+ }
+ }
+}
diff --git a/src/classes/subwayStation.js b/src/classes/subwayStation.js
new file mode 100644
index 000000000..43399e306
--- /dev/null
+++ b/src/classes/subwayStation.js
@@ -0,0 +1,24 @@
+import { SUBWAY_STATION_NAME_LENGTH_LIMIT } from '../constants/configuration.js';
+
+export default class SubwayStation {
+ static checkIsStationNameShort(stationName) {
+ return stationName.length < SUBWAY_STATION_NAME_LENGTH_LIMIT;
+ }
+
+ static checkIsStationBelongToLine(station) {
+ return station.belongingLineNames.length > 0;
+ }
+
+ constructor() {
+ this.belongingLineNames = [];
+ }
+
+ addBeloningLineByLineName(lineName) {
+ this.belongingLineNames.push(lineName);
+ }
+
+ deleteBeloningLineByLineName(lineName) {
+ const deleteLineIndex = this.belongingLineNames.indexOf(lineName);
+ this.belongingLineNames.splice(deleteLineIndex, 1);
+ }
+}
diff --git a/src/constants/configuration.js b/src/constants/configuration.js
new file mode 100644
index 000000000..966ae8585
--- /dev/null
+++ b/src/constants/configuration.js
@@ -0,0 +1,29 @@
+export const SUBWAY_STATION_NAME_LENGTH_LIMIT = 2;
+export const SUBWAY_STATION_IN_LINE_DELETE_LENGTH_LIMIT = 2;
+export const SUBWAY_MAP_ITEM_NAME = 'Subway-Map';
+export const INTERFACE_DATA_ITEM_NAME = 'Data';
+export const SHORT_STATION_NAME_ALERT_MESSAGE =
+ '์ญ์ ์ด๋ฆ์ด ๋๋ฌด ์งง์ต๋๋ค. 2๊ธ์ ์ด์์ ์ด๋ฆ์ ์
๋ ฅํด์ฃผ์ธ์';
+export const SAME_STATION_NAME_EXIST_MESSAGE =
+ '์ด๋ฏธ ๋์ผํ ์ด๋ฆ์ ์ญ์ด ๋ฑ๋ก๋์ด ์์ต๋๋ค. ๋ค๋ฅธ ์ด๋ฆ์ ์
๋ ฅํด์ฃผ์ธ์';
+export const STATION_REGISTERED_IN_LINE_MESSAGE =
+ '์ด ์ญ์ ๋
ธ์ ์ ๊ตฌ๊ฐ์ผ๋ก ๋ฑ๋ก๋์ด ์์ต๋๋ค. ์ญ์ ๋ฅผ ์ํ์ ๋ค๋ฉด ํด๋น ๋
ธ์ ๊ตฌ๊ฐ์์ ์ญ์ ๋นผ์ฃผ์ธ์.';
+export const SAME_LINE_NAME_EXIST_MESSAGE =
+ '์ด๋ฏธ ๋์ผํ ์ด๋ฆ์ ๋
ธ์ ์ด ๋ฑ๋ก๋์ด ์์ต๋๋ค. ๋ค๋ฅธ ์ด๋ฆ์ ์
๋ ฅํด์ฃผ์ธ์';
+export const END_AND_START_STATION_NAME_SAME_MESSAGE =
+ '์ํ ์ข
์ ์ญ๊ณผ ํํ ์ข
์ ์ญ์ด ๊ฐ์ต๋๋ค. ์๋ก ๋ค๋ฅธ ์ข
์ ์ญ๋ค์ ์ ํํด์ฃผ์ธ์.';
+export const SAME_STATION_REGISTER_TRY_MESSAGE =
+ '์ด ์ญ์ ์ด๋ฏธ ๋
ธ์ ์ ์ด ๋
ธ์ ์ ๋ฑ๋ก๋์ด ์์ต๋๋ค. ๋ค๋ฅธ ์ญ์ ์ ํํด์ฃผ์ธ์';
+export const LESS_THAN_DELETE_LENGTH_LIMIT_MESSAGE = `์ด ์ญ์ ๋นผ๋ฉด ๋
ธ์ ์ ๊ฐ์๊ฐ ${SUBWAY_STATION_IN_LINE_DELETE_LENGTH_LIMIT}๊ฐ ์ดํ๊ฐ ๋๋ฏ๋ก ๋บ ์ ์์ต๋๋ค`;
+export const INDEX_IS_NOT_NUMBER_MESSAGE = '์์๋ก ๋ฃ์ ๊ฐ์ด ์ซ์๋ก ๋ณด์ด์ง ์์ต๋๋ค. ๋ค์ ์
๋ ฅํด์ฃผ์ธ์'
+
+export default {
+ SUBWAY_STATION_NAME_LENGTH_LIMIT,
+ SUBWAY_STATION_IN_LINE_DELETE_LENGTH_LIMIT,
+ SUBWAY_MAP_ITEM_NAME,
+ SAME_LINE_NAME_EXIST_MESSAGE,
+ END_AND_START_STATION_NAME_SAME_MESSAGE,
+ SAME_STATION_REGISTER_TRY_MESSAGE,
+ LESS_THAN_DELETE_LENGTH_LIMIT_MESSAGE,
+ INDEX_IS_NOT_NUMBER_MESSAGE
+};
diff --git a/src/data/data.js b/src/data/data.js
new file mode 100644
index 000000000..dfaef22d0
--- /dev/null
+++ b/src/data/data.js
@@ -0,0 +1,69 @@
+import { setItemWithKey, getItemByKey } from '../utils/localStorage.js';
+import { stationNameInputElement } from '../elements/stationManager.js';
+import {
+ lineEndStationSelectorElement,
+ lineStartStationSelectorElement,
+ lineNameInputElement,
+} from '../elements/lineManager.js';
+import {
+ sectionOrderInputElement,
+ sectionStationSelectorElement,
+ sectionLineNameElement,
+} from '../elements/sectionManager.js';
+import { INTERFACE_DATA_ITEM_NAME } from '../constants/configuration.js';
+
+const data = {
+ stationNameInputValue: '',
+ lineNameInputValue: '',
+ lineStartStationSelectorValue: '',
+ lineEndStationSelectorValue: '',
+ sectionLineNameValue: '',
+ sectionStationSelectorValue: '',
+ sectionOrderInputNumberValue: '',
+};
+
+export const syncDataToLocalStorage = () => {
+ setItemWithKey(data, INTERFACE_DATA_ITEM_NAME);
+};
+
+export const loadDataToLocalStorage = () => {
+ const loadedData = getItemByKey(INTERFACE_DATA_ITEM_NAME);
+ if (loadedData) {
+ data.stationNameInputValue = loadedData.stationNameInputValue;
+ data.lineNameInputValue = loadedData.lineNameInputValue;
+ data.upTerminatingStationNameValue =
+ loadedData.upTerminatingStationNameValue;
+ data.downTerminatingStationNameValue =
+ loadedData.downTerminatingStationNameValue;
+ data.sectionLineValue = loadedData.sectionLineValue;
+ data.sectionStationSelectorValue = loadedData.sectionStationSelectorValue;
+ data.sectionOrderInputNumberValue = loadedData.sectionOrderInputNumberValue;
+ }
+};
+
+export const syncDataFromAllElements = () => {
+ data.stationNameInputValue = stationNameInputElement.value;
+ data.lineNameInputValue = lineNameInputElement.value;
+ data.lineStartStationSelectorValue = lineStartStationSelectorElement.value;
+ data.lineEndStationSelectorValue = lineEndStationSelectorElement.value;
+ data.sectionOrderInputNumberValue = sectionOrderInputElement.value;
+ data.sectionLineNameValue = sectionLineNameElement.value;
+ data.sectionStationSelectorValue = sectionStationSelectorElement.value;
+};
+
+export const syncDataToAllElements = () => {
+ stationNameInputElement.value = data.stationNameInputValue;
+ lineNameInputElement.value = data.lineNameInputValue;
+ lineStartStationSelectorElement.value = data.lineStartStationSelectorValue;
+ lineEndStationSelectorElement.value = data.lineEndStationSelectorValue;
+ sectionOrderInputElement.value = data.sectionOrderInputNumberValue;
+ sectionLineNameElement.value = data.sectionLineNameValue;
+ sectionStationSelectorElement.value = data.sectionStationSelectorValue;
+};
+
+export default {
+ syncDataToLocalStorage,
+ loadDataToLocalStorage,
+ syncDataFromAllElements,
+ syncDataToAllElements,
+};
diff --git a/src/elements/contentConvertButtons.js b/src/elements/contentConvertButtons.js
new file mode 100644
index 000000000..972fa947f
--- /dev/null
+++ b/src/elements/contentConvertButtons.js
@@ -0,0 +1,17 @@
+export const lineManagerButtonElement = document.getElementById(
+ 'line-manager-button'
+);
+
+export const sectionManagerButtonElement = document.getElementById(
+ 'section-manager-button'
+);
+
+export const mapPrintManagerButtonElement = document.getElementById(
+ 'map-print-manager-button'
+);
+
+export default {
+ lineManagerButtonElement,
+ sectionManagerButtonElement,
+ mapPrintManagerButtonElement,
+};
diff --git a/src/elements/contents.js b/src/elements/contents.js
new file mode 100644
index 000000000..c7f394331
--- /dev/null
+++ b/src/elements/contents.js
@@ -0,0 +1,5 @@
+export const contentElements = document.querySelectorAll('.content');
+
+export default {
+ contentElements,
+};
diff --git a/src/elements/lineManager.js b/src/elements/lineManager.js
new file mode 100644
index 000000000..d2d1a7311
--- /dev/null
+++ b/src/elements/lineManager.js
@@ -0,0 +1,23 @@
+export const lineNameInputElement = document.getElementById('line-name-input');
+export const lineStartStationSelectorElement = document.getElementById(
+ 'line-start-station-selector'
+);
+export const lineEndStationSelectorElement = document.getElementById(
+ 'line-end-station-selector'
+);
+export const lineAddButtonElement = document.getElementById('line-add-button');
+export const lineDeleteButtonElements = document.querySelectorAll(
+ '.line-delete-button'
+);
+export const resultLineItemsElement = document.getElementById(
+ 'result-line-items'
+);
+
+export default {
+ lineNameInputElement,
+ lineStartStationSelectorElement,
+ lineEndStationSelectorElement,
+ lineAddButtonElement,
+ lineDeleteButtonElements,
+ resultLineItemsElement,
+};
diff --git a/src/elements/mapPrint.js b/src/elements/mapPrint.js
new file mode 100644
index 000000000..4931c5570
--- /dev/null
+++ b/src/elements/mapPrint.js
@@ -0,0 +1,5 @@
+export const mapPrintWrapperElement = document.getElementById('map-print');
+
+export default {
+ mapPrintWrapperElement
+}
\ No newline at end of file
diff --git a/src/elements/sectionManager.js b/src/elements/sectionManager.js
new file mode 100644
index 000000000..2c78d13cd
--- /dev/null
+++ b/src/elements/sectionManager.js
@@ -0,0 +1,39 @@
+export const sectionLineMenuButtonListElement = document.getElementById(
+ 'section-line-menu-button-list'
+);
+export const sectionLineNameElement = document.getElementById(
+ 'section-line-name'
+);
+export const stationRegisterWrapperElement = document.getElementById(
+ 'station-register'
+);
+export const registeredStationItemTableElement = document.getElementById(
+ 'registered-station-item-table'
+);
+export const registeredStationItemsElement = document.getElementById(
+ 'registered-station-items'
+);
+export const sectionStationSelectorElement = document.getElementById(
+ 'section-station-selector'
+);
+export const sectionOrderInputElement = document.getElementById(
+ 'section-order-input'
+);
+export const sectionAddButtonElement = document.getElementById(
+ 'section-add-button'
+);
+export const sectionDeleteButtonElements = document.querySelectorAll(
+ '.section-delete-button'
+);
+
+export default {
+ sectionLineMenuButtonListElement,
+ sectionLineNameElement,
+ sectionStationSelectorElement,
+ registeredStationItemTableElement,
+ registeredStationItemsElement,
+ sectionOrderInputElement,
+ sectionAddButtonElement,
+ sectionDeleteButtonElements,
+ stationRegisterWrapperElement,
+};
diff --git a/src/elements/stationManager.js b/src/elements/stationManager.js
new file mode 100644
index 000000000..4e504dcd8
--- /dev/null
+++ b/src/elements/stationManager.js
@@ -0,0 +1,16 @@
+export const stationNameInputElement = document.getElementById(
+ 'station-name-input'
+);
+export const stationAddButtonElement = document.getElementById(
+ 'station-add-button'
+);
+export const stationDeleteButtonsElement = document.querySelectorAll(
+ '.station-delete-button'
+);
+export const resultStationItemsElement = document.getElementById('result-station-items');
+
+export default {
+ stationNameInputElement,
+ stationAddButtonElement,
+ stationDeleteButtonsElement,
+};
diff --git a/src/handlers/lineManager.js b/src/handlers/lineManager.js
new file mode 100644
index 000000000..ca76a9466
--- /dev/null
+++ b/src/handlers/lineManager.js
@@ -0,0 +1,103 @@
+import {
+ lineNameInputElement,
+ lineStartStationSelectorElement,
+ lineEndStationSelectorElement,
+ resultLineItemsElement,
+} from '../elements/lineManager.js';
+import { subwayMap } from '../store/store.js';
+import SubwayLine from '../classes/subwayLine.js';
+import {
+ SAME_LINE_NAME_EXIST_MESSAGE,
+ END_AND_START_STATION_NAME_SAME_MESSAGE,
+} from '../constants/configuration.js';
+import { getTableRowsTemplate } from '../templates/table.js';
+import { getSelectorOptionsTemplate } from '../templates/selector.js';
+
+const getAddLineAlertMessage = ({
+ lineName,
+ startStationName,
+ endStationName,
+}) => {
+ let alertMessage = '';
+ if (subwayMap.checkIsLineNameExist(lineName)) {
+ alertMessage += `${SAME_LINE_NAME_EXIST_MESSAGE}\n`;
+ }
+ if (SubwayLine.checkIsStationsSame(startStationName, endStationName)) {
+ alertMessage += END_AND_START_STATION_NAME_SAME_MESSAGE;
+ }
+
+ return alertMessage;
+};
+
+export const showLineManagerResultTable = () => {
+ const lineTableRows = [];
+ const allLineNames = Object.keys(subwayMap.allLines);
+ allLineNames.forEach((lineName) => {
+ const line = subwayMap.allLines[lineName];
+ const lineStartStation = line.allStationsInLine[0];
+ const lineEndStationIndex = line.allStationsInLine.length - 1;
+ const lineEndStation = line.allStationsInLine[lineEndStationIndex];
+ console.log(lineStartStation, lineEndStation);
+ const lineTableRow = [lineName, lineStartStation, lineEndStation];
+ lineTableRows.push(lineTableRow);
+ });
+ const deleteTargetCellIndex = 0;
+ const deleteButtonClass = 'line-delete-button';
+ resultLineItemsElement.innerHTML = getTableRowsTemplate({
+ rows: lineTableRows,
+ deleteTargetCellIndex,
+ deleteButtonClass,
+ });
+};
+
+export const onConverToLineContent = () => {
+ const allStationNames = Object.keys(subwayMap.allStations);
+ lineStartStationSelectorElement.innerHTML = getSelectorOptionsTemplate(
+ allStationNames
+ );
+ lineEndStationSelectorElement.innerHTML = getSelectorOptionsTemplate(
+ allStationNames
+ );
+};
+
+export const onAddLine = () => {
+ const lineName = lineNameInputElement.value;
+ const startStationName = lineStartStationSelectorElement.value;
+ const endStationName = lineEndStationSelectorElement.value;
+ subwayMap.allStations[startStationName].addBeloningLineByLineName(lineName);
+ subwayMap.allStations[endStationName].addBeloningLineByLineName(lineName);
+ const line = new SubwayLine(startStationName, endStationName);
+ const alertMessage = getAddLineAlertMessage({
+ lineName,
+ startStationName,
+ endStationName,
+ });
+ if (alertMessage === '') {
+ subwayMap.addLine(line, lineName);
+ showLineManagerResultTable();
+ } else {
+ alert(alertMessage);
+ }
+};
+
+export const onDeleteLine = (event) => {
+ const targetElement = event.target;
+ if (targetElement.className !== 'line-delete-button') {
+ return;
+ }
+ const deleteTargetName = targetElement.dataset.deleteTarget;
+ const targetLine = subwayMap.allLines[deleteTargetName];
+ targetLine.allStationsInLine.forEach((stationName) => {
+ const station = subwayMap.allStations[stationName];
+ station.deleteBeloningLineByLineName(deleteTargetName);
+ });
+ subwayMap.deleteLineByName(deleteTargetName);
+ showLineManagerResultTable();
+};
+
+export default {
+ onAddLine,
+ onConverToLineContent,
+ onDeleteLine,
+ showLineManagerResultTable,
+};
diff --git a/src/handlers/mapPrint.js b/src/handlers/mapPrint.js
new file mode 100644
index 000000000..6a9583516
--- /dev/null
+++ b/src/handlers/mapPrint.js
@@ -0,0 +1,11 @@
+import { mapPrintWrapperElement } from '../elements/mapPrint.js';
+import { getSubwayMapTemplate } from '../templates/list.js';
+import { subwayMap } from '../store/store.js';
+
+export const printMap = () => {
+ mapPrintWrapperElement.innerHTML = getSubwayMapTemplate(subwayMap.allLines);
+};
+
+export default {
+ printMap,
+};
diff --git a/src/handlers/sectionManager.js b/src/handlers/sectionManager.js
new file mode 100644
index 000000000..2fae8fce9
--- /dev/null
+++ b/src/handlers/sectionManager.js
@@ -0,0 +1,127 @@
+import {
+ sectionLineMenuButtonListElement,
+ sectionStationSelectorElement,
+ sectionLineNameElement,
+ stationRegisterWrapperElement,
+ registeredStationItemTableElement,
+ registeredStationItemsElement,
+ sectionOrderInputElement,
+} from '../elements/sectionManager.js';
+import { subwayMap } from '../store/store.js';
+import {
+ SAME_STATION_REGISTER_TRY_MESSAGE,
+ LESS_THAN_DELETE_LENGTH_LIMIT_MESSAGE,
+ INDEX_IS_NOT_NUMBER_MESSAGE,
+} from '../constants/configuration.js';
+import { getTableRowsTemplate } from '../templates/table.js';
+import { getSelectorOptionsTemplate } from '../templates/selector.js';
+import { getSectionLineMenuButtonsTemplate } from '../templates/button.js';
+import SubwayLine from '../classes/subwayLine.js';
+
+const getInsertStationAlertMessage = ({ line, stationName, index }) => {
+ let alertMessage = '';
+ if (line.checkIsStationNameExistInLine(stationName)) {
+ alertMessage += `${SAME_STATION_REGISTER_TRY_MESSAGE}\n`;
+ }
+ if (SubwayLine.checkIsIndexNumberCorrect(index) === false) {
+ alertMessage += INDEX_IS_NOT_NUMBER_MESSAGE;
+ }
+
+ return alertMessage;
+};
+
+const getPullOutStationAlertMessage = (line) => {
+ let alertMessage = '';
+ if (line.checkIsAllStationsInLineLengthSameAsLimit()) {
+ alertMessage += LESS_THAN_DELETE_LENGTH_LIMIT_MESSAGE;
+ }
+
+ return alertMessage;
+};
+
+export const showSectionManagerResultTable = () => {
+ const lineName = sectionLineNameElement.innerText;
+ const line = subwayMap.allLines[lineName];
+ if (line) {
+ const stationInLineRows = [];
+ line.allStationsInLine.forEach((stationName, index) => {
+ stationInLineRows.push([index, stationName]);
+ });
+ registeredStationItemsElement.innerHTML = getTableRowsTemplate({
+ rows: stationInLineRows,
+ deleteTargetCellIndex: 0,
+ deleteButtonClass: 'section-delete-button',
+ deleteButtonText: '๋
ธ์ ์์ ์ ๊ฑฐ',
+ });
+ }
+};
+
+export const onClickSectionLineButton = (event) => {
+ const targetElement = event.target;
+ if (targetElement.className === 'section-line-menu-button') {
+ const lineName = targetElement.innerText;
+ sectionLineNameElement.innerText = lineName;
+ stationRegisterWrapperElement.setAttribute('style', 'display: block;');
+ registeredStationItemTableElement.setAttribute('style', 'display: block;');
+ showSectionManagerResultTable();
+ }
+};
+
+export const onConverToSectionContent = () => {
+ const allLineNames = Object.keys(subwayMap.allLines);
+ const allStationNames = Object.keys(subwayMap.allStations);
+ sectionLineMenuButtonListElement.innerHTML = getSectionLineMenuButtonsTemplate(
+ allLineNames
+ );
+ sectionStationSelectorElement.innerHTML = getSelectorOptionsTemplate(
+ allStationNames
+ );
+};
+
+export const onInsertStation = () => {
+ const lineName = sectionLineNameElement.innerText;
+ const stationName = sectionStationSelectorElement.value;
+ const index = Number(sectionOrderInputElement.value);
+ const line = subwayMap.allLines[lineName];
+ const alertMessage = getInsertStationAlertMessage({
+ line,
+ stationName,
+ index,
+ });
+ if (alertMessage === '') {
+ const insertingStation = subwayMap.allStations[stationName];
+ insertingStation.addBeloningLineByLineName(lineName);
+ line.insertStationToLineByNameAndIndex({ stationName, index });
+ showSectionManagerResultTable(line.allStationsInLine);
+ } else {
+ alert(alertMessage);
+ }
+};
+
+export const onPullOutStation = (event) => {
+ const targetElement = event.target;
+ if (targetElement.className !== 'section-delete-button') {
+ return;
+ }
+ const lineName = sectionLineNameElement.innerText;
+ const targetIndex = targetElement.dataset.deleteTarget;
+ const line = subwayMap.allLines[lineName];
+ const alertMessage = getPullOutStationAlertMessage(line);
+ if (alertMessage === '') {
+ const pulledOutStationName = line.pullOutStationFromLineByIndex(
+ targetIndex
+ );
+ const pulledOutStation = subwayMap.allStations[pulledOutStationName];
+ pulledOutStation.deleteBeloningLineByLineName(lineName);
+ showSectionManagerResultTable(line.allStationsInLine);
+ } else {
+ alert(alertMessage);
+ }
+};
+
+export default {
+ onInsertStation,
+ onConverToSectionContent,
+ onClickSectionLineButton,
+ showSectionManagerResultTable,
+};
diff --git a/src/handlers/stationManager.js b/src/handlers/stationManager.js
new file mode 100644
index 000000000..5878475e7
--- /dev/null
+++ b/src/handlers/stationManager.js
@@ -0,0 +1,79 @@
+import {
+ stationNameInputElement,
+ resultStationItemsElement,
+} from '../elements/stationManager.js';
+import SubwayStation from '../classes/subwayStation.js';
+import { subwayMap } from '../store/store.js';
+import {
+ SAME_STATION_NAME_EXIST_MESSAGE,
+ SHORT_STATION_NAME_ALERT_MESSAGE,
+ STATION_REGISTERED_IN_LINE_MESSAGE,
+} from '../constants/configuration.js';
+import { getTableRowsTemplate } from '../templates/table.js';
+
+const getAddStationAlertMessage = (stationName) => {
+ let alertMessage = '';
+ if (subwayMap.checkIsStationNameExist(stationName)) {
+ alertMessage += `${SAME_STATION_NAME_EXIST_MESSAGE}\n`;
+ }
+ if (SubwayStation.checkIsStationNameShort(stationName)) {
+ alertMessage += SHORT_STATION_NAME_ALERT_MESSAGE;
+ }
+
+ return alertMessage;
+};
+
+const getDeleteStationAlertMessage = (stationName) => {
+ let alertMessage = '';
+ const station = subwayMap.allStations[stationName];
+ if (SubwayStation.checkIsStationBelongToLine(station)) {
+ alertMessage += STATION_REGISTERED_IN_LINE_MESSAGE;
+ }
+
+ return alertMessage;
+};
+
+export const showStationManagerResultTable = () => {
+ const registerdStationNames = Object.keys(subwayMap.allStations);
+ const resultRows = registerdStationNames.map((stationName) => [stationName]);
+ const deleteTargetCellIndex = 0;
+ const deleteButtonClass = 'station-delete-button';
+ resultStationItemsElement.innerHTML = getTableRowsTemplate({
+ rows: resultRows,
+ deleteTargetCellIndex,
+ deleteButtonClass,
+ });
+};
+
+export const onAddStation = () => {
+ const stationName = stationNameInputElement.value;
+ const alertMessage = getAddStationAlertMessage(stationName);
+ if (alertMessage === '') {
+ const station = new SubwayStation();
+ subwayMap.addStation(station, stationName);
+ showStationManagerResultTable();
+ stationNameInputElement.value = '';
+ } else {
+ alert(alertMessage);
+ }
+};
+
+export const onDeleteStation = (event) => {
+ const targetElement = event.target;
+ if (targetElement.className !== 'station-delete-button') {
+ return;
+ }
+ const deleteTargetName = targetElement.dataset.deleteTarget;
+ const alertMessage = getDeleteStationAlertMessage(deleteTargetName);
+ if (alertMessage === '') {
+ subwayMap.deleteStationByName(deleteTargetName);
+ showStationManagerResultTable();
+ } else {
+ alert(alertMessage);
+ }
+};
+
+export default {
+ onAddStation,
+ showStationManagerResultTable
+};
diff --git a/src/index.js b/src/index.js
index e69de29bb..5e62e7185 100644
--- a/src/index.js
+++ b/src/index.js
@@ -0,0 +1,19 @@
+import { setRouterWithElements } from './routes/routes.js';
+import { contentElements } from './elements/contents.js';
+import {
+ addLineMangerEventListeners,
+ addMapPrintEventListeners,
+ addSectionManagerEventListeners,
+ addStationManagerEventListeners,
+ addSyncDataBeforeOnloadEventListener,
+ addSyncDataBeforeUnloadEventListener,
+} from './listeners/listeners.js';
+
+setRouterWithElements(contentElements);
+
+addLineMangerEventListeners();
+addMapPrintEventListeners();
+addSectionManagerEventListeners();
+addStationManagerEventListeners();
+addSyncDataBeforeOnloadEventListener(contentElements, contentElements[0]);
+addSyncDataBeforeUnloadEventListener();
diff --git a/src/listeners/listeners.js b/src/listeners/listeners.js
new file mode 100644
index 000000000..c1e3f2d51
--- /dev/null
+++ b/src/listeners/listeners.js
@@ -0,0 +1,112 @@
+import {
+ stationAddButtonElement,
+ resultStationItemsElement,
+} from '../elements/stationManager.js';
+import {
+ lineAddButtonElement,
+ resultLineItemsElement,
+} from '../elements/lineManager.js';
+import {
+ sectionLineMenuButtonListElement,
+ sectionAddButtonElement,
+ registeredStationItemsElement,
+} from '../elements/sectionManager.js';
+import {
+ lineManagerButtonElement,
+ sectionManagerButtonElement,
+ mapPrintManagerButtonElement,
+} from '../elements/contentConvertButtons.js';
+
+import {
+ onAddStation,
+ onDeleteStation,
+ showStationManagerResultTable,
+} from '../handlers/stationManager.js';
+import {
+ onAddLine,
+ onConverToLineContent,
+ onDeleteLine,
+ showLineManagerResultTable,
+} from '../handlers/lineManager.js';
+import {
+ onConverToSectionContent,
+ onClickSectionLineButton,
+ onInsertStation,
+ onPullOutStation,
+ showSectionManagerResultTable,
+} from '../handlers/sectionManager.js';
+import { printMap } from '../handlers/mapPrint.js';
+import { convertContent } from '../routes/routes.js';
+import {
+ loadDataToLocalStorage,
+ syncDataToAllElements,
+ syncDataToLocalStorage,
+ syncDataFromAllElements,
+} from '../data/data.js';
+import {
+ syncSubwayMapFromLocalStorage,
+ syncSubwayMapToLocalStorage,
+} from '../store/store.js';
+
+export const addStationManagerEventListeners = () => {
+ stationAddButtonElement.addEventListener('click', onAddStation);
+ resultStationItemsElement.addEventListener('click', onDeleteStation);
+};
+
+export const addLineMangerEventListeners = () => {
+ lineAddButtonElement.addEventListener('click', onAddLine);
+ lineManagerButtonElement.addEventListener('click', onConverToLineContent);
+ resultLineItemsElement.addEventListener('click', onDeleteLine);
+};
+
+export const addSectionManagerEventListeners = () => {
+ sectionManagerButtonElement.addEventListener(
+ 'click',
+ onConverToSectionContent
+ );
+ sectionLineMenuButtonListElement.addEventListener(
+ 'click',
+ onClickSectionLineButton
+ );
+ sectionAddButtonElement.addEventListener('click', onInsertStation);
+ registeredStationItemsElement.addEventListener('click', onPullOutStation);
+};
+
+export const addMapPrintEventListeners = () => {
+ mapPrintManagerButtonElement.addEventListener('click', printMap);
+};
+
+export const addSyncDataBeforeUnloadEventListener = () => {
+ window.onbeforeunload = () => {
+ syncDataFromAllElements();
+ syncDataToLocalStorage();
+ syncSubwayMapToLocalStorage();
+ };
+};
+
+export const addSyncDataBeforeOnloadEventListener = (
+ contentElements,
+ defaultContentElement
+) => {
+ window.onload = () => {
+ const contentName = window.location.hash.substr(1);
+ if (contentName) {
+ convertContent(contentElements, contentName);
+ } else {
+ convertContent(contentElements, defaultContentElement.id);
+ }
+ loadDataToLocalStorage();
+ syncDataToAllElements();
+ syncSubwayMapFromLocalStorage();
+ showStationManagerResultTable();
+ showLineManagerResultTable();
+ showSectionManagerResultTable();
+ };
+};
+
+export default {
+ addStationManagerEventListeners,
+ addLineMangerEventListeners,
+ addSectionManagerEventListeners,
+ addMapPrintEventListeners,
+};
diff --git a/src/routes/routes.js b/src/routes/routes.js
new file mode 100644
index 000000000..342c31867
--- /dev/null
+++ b/src/routes/routes.js
@@ -0,0 +1,23 @@
+export const convertContent = (contentElements, contentName) => {
+ contentElements.forEach((element) => {
+ if (element.id === contentName) {
+ element.setAttribute('style', 'display: block;');
+ } else {
+ element.setAttribute('style', 'display: none;');
+ }
+ });
+};
+
+export const setRouterWithElements = (
+ contentElements,
+) => {
+ window.onhashchange = () => {
+ const contentName = window.location.hash.substr(1);
+ convertContent(contentElements, contentName);
+ };
+};
+
+export default {
+ setRouterWithElements,
+ convertContent
+};
diff --git a/src/store/store.js b/src/store/store.js
new file mode 100644
index 000000000..37271c88e
--- /dev/null
+++ b/src/store/store.js
@@ -0,0 +1,58 @@
+import StationMap from '../classes/subwayMap.js';
+import { getItemByKey, setItemWithKey } from '../utils/localStorage.js';
+import { SUBWAY_MAP_ITEM_NAME } from '../constants/configuration.js';
+import SubwayStation from '../classes/subwayStation.js';
+import SubwayLine from '../classes/subwayLine.js';
+
+export const subwayMap = new StationMap();
+
+export const syncSubwayMapToLocalStorage = () => {
+ const subwayMapItem = {
+ allStations: subwayMap.allStations,
+ allLines: subwayMap.allLines,
+ };
+ setItemWithKey(subwayMapItem, SUBWAY_MAP_ITEM_NAME);
+};
+
+const copyAllStations = (allStations) => {
+ Object.keys(allStations).forEach((stationName) => {
+ const loadedStation = allStations[stationName];
+ const station = new SubwayStation();
+ loadedStation.belongingLineNames.forEach((belongingLineName) => {
+ station.addBeloningLineByLineName(belongingLineName);
+ });
+ subwayMap.addStation(station, stationName);
+ });
+};
+
+const copyAllLines = (allLines) => {
+ Object.keys(allLines).forEach((lineName) => {
+ const loadedLine = allLines[lineName];
+ const loadedLineEndIndex = loadedLine.allStationsInLine.length - 1;
+ const line = new SubwayLine(
+ loadedLine.allStationsInLine[0],
+ loadedLine.allStationsInLine[loadedLineEndIndex]
+ );
+ for (let i = loadedLine.length - 2; i > 0; i -= 1) {
+ line.insertStationToLineByNameAndIndex(
+ loadedLine.allStationsInLine[i],
+ 1
+ );
+ }
+ subwayMap.addLine(line, lineName);
+ });
+};
+
+export const syncSubwayMapFromLocalStorage = () => {
+ const subwayMapItem = getItemByKey(SUBWAY_MAP_ITEM_NAME);
+ const { allStations, allLines } = subwayMapItem;
+ copyAllStations(allStations);
+ copyAllLines(allLines);
+ console.log(subwayMap);
+};
+
+export default {
+ subwayMap,
+ syncSubwayMapToLocalStorage,
+ syncSubwayMapFromLocalStorage,
+};
diff --git a/src/templates/button.js b/src/templates/button.js
new file mode 100644
index 000000000..deeeacb9d
--- /dev/null
+++ b/src/templates/button.js
@@ -0,0 +1,18 @@
+const getSectionLineMenuButtonTemplate = (lineName) => {
+ return ``;
+};
+
+export const getSectionLineMenuButtonsTemplate = (lineNames) => {
+ let targetLineSetterButtonsTemplate = '';
+ lineNames.forEach((lineName) => {
+ targetLineSetterButtonsTemplate += getSectionLineMenuButtonTemplate(
+ lineName
+ );
+ });
+
+ return targetLineSetterButtonsTemplate;
+};
+
+export default {
+ getSectionLineMenuButtonsTemplate,
+};
diff --git a/src/templates/list.js b/src/templates/list.js
new file mode 100644
index 000000000..a18db0157
--- /dev/null
+++ b/src/templates/list.js
@@ -0,0 +1,32 @@
+const getSubwayLineTemplate = (lineName, stationNamesInLine) => {
+ let stationItemsTemplate = '';
+ stationNamesInLine.forEach((stationName) => {
+ stationItemsTemplate += `${stationName}`;
+ });
+
+ return `
+ ${lineName}
+
+ ${stationItemsTemplate}
+
+ `;
+};
+
+export const getSubwayMapTemplate = (allLines) => {
+ const allLineNames = Object.keys(allLines);
+ let subwayMapTemplate = '';
+ allLineNames.forEach((lineName) => {
+ const { allStationsInLine } = allLines[lineName];
+ subwayMapTemplate += getSubwayLineTemplate(lineName, allStationsInLine);
+ });
+
+ return `
+
+ ${subwayMapTemplate}
+
+ `;
+};
+
+export default {
+ getSubwayMapTemplate,
+};
diff --git a/src/templates/selector.js b/src/templates/selector.js
new file mode 100644
index 000000000..1d776517e
--- /dev/null
+++ b/src/templates/selector.js
@@ -0,0 +1,16 @@
+const getSelectorOptionTemplate = (option) => {
+ return ``;
+};
+
+export const getSelectorOptionsTemplate = (options) => {
+ let selectorOptionsTemplate = '';
+ options.forEach((option) => {
+ selectorOptionsTemplate += getSelectorOptionTemplate(option);
+ });
+
+ return selectorOptionsTemplate;
+};
+
+export default {
+ getSelectorOptionsTemplate,
+};
diff --git a/src/templates/table.js b/src/templates/table.js
new file mode 100644
index 000000000..c7bfb36a8
--- /dev/null
+++ b/src/templates/table.js
@@ -0,0 +1,45 @@
+const getTableRowTemplate = ({
+ row,
+ deleteTarget,
+ deleteButtonClass,
+ deletebuttonText,
+}) => {
+ let tableCellsTemplate = '';
+ row.forEach((cell) => {
+ tableCellsTemplate += `${cell} | `;
+ });
+ return `
+
+ ${tableCellsTemplate}
+ |
+
+ `;
+};
+
+export const getTableRowsTemplate = ({
+ rows,
+ deleteTargetCellIndex,
+ deleteButtonClass,
+ deleteButtonText,
+}) => {
+ let rowItemsTemplate = '';
+ let defaultDeleteButtonText = '์ญ์ ';
+ if (deleteButtonText) {
+ defaultDeleteButtonText = deleteButtonText;
+ }
+ rows.forEach((row) => {
+ const deleteTarget = row[deleteTargetCellIndex];
+ rowItemsTemplate += getTableRowTemplate({
+ row,
+ deleteTarget,
+ deleteButtonClass,
+ deletebuttonText: defaultDeleteButtonText,
+ });
+ });
+
+ return rowItemsTemplate;
+};
+
+export default {
+ getTableRowsTemplate,
+};
diff --git a/src/utils/localStorage.js b/src/utils/localStorage.js
new file mode 100644
index 000000000..0987a7fe1
--- /dev/null
+++ b/src/utils/localStorage.js
@@ -0,0 +1,14 @@
+export const setItemWithKey = (item, key) => {
+ const stringifiedItem = JSON.stringify(item);
+ localStorage.setItem(key, stringifiedItem);
+};
+
+export const getItemByKey = (key) => {
+ const stringifiedItem = localStorage.getItem(key);
+ return JSON.parse(stringifiedItem);
+};
+
+export default {
+ setItemWithKey,
+ getItemByKey,
+};