diff --git a/README.md b/README.md index e97a1d649..eb2df86ff 100644 --- a/README.md +++ b/README.md @@ -1,114 +1,129 @@ # πŸš‡ μ§€ν•˜μ²  노선도 λ―Έμ…˜ +## URL + +### https://yungo1846.github.io/javascript-subway-map-precourse/ + +## 디렉토리 ꡬ쑰 + +```bash +β”‚ index.html +β”‚ LICENSE +β”‚ README.md +β”‚ +β”œβ”€images +β”‚ +└─src + β”‚ index.js + β”‚ + β”œβ”€common + β”‚ alertMessage.js + β”‚ checkInput.js + β”‚ clearInput.js + β”‚ constant.js + β”‚ + β”œβ”€event + β”‚ β”‚ menuEvent.js + β”‚ β”‚ + β”‚ β”œβ”€common + β”‚ β”‚ checkStorageItem.js + β”‚ β”‚ + β”‚ β”œβ”€line + β”‚ β”‚ addLineEvent.js + β”‚ β”‚ delLineEvent.js + β”‚ β”‚ + β”‚ β”œβ”€section + β”‚ β”‚ addSectionEvent.js + β”‚ β”‚ chooseLineEvent.js + β”‚ β”‚ delSectionEvent.js + β”‚ β”‚ + β”‚ └─station + β”‚ addStationEvent.js + β”‚ delStationEvent.js + β”‚ + └─render + render.js + renderLine.js + renderMapPrint.js + renderSection.js + renderStation.js +``` + +- common: 자주 μ‚¬μš©λ˜λŠ” μƒμˆ˜, ν•¨μˆ˜ 등을 λͺ¨μ•„놓은 디렉토리 +- event: eventHandle에 ν•„μš”ν•œ λ‘œμ§μ„ λͺ¨μ•„놓은 디렉토리 +- render: μ‚¬μš©μžμ—κ²Œ λ³΄μ΄λŠ” 화면을 κ΅¬μ„±ν•˜λŠ” λ‘œμ§μ„ λͺ¨μ•„놓은 디렉토리 + ## πŸš€ κΈ°λŠ₯ μš”κ΅¬μ‚¬ν•­ ### μ§€ν•˜μ²  μ—­ κ΄€λ ¨ κΈ°λŠ₯ -- μ§€ν•˜μ²  역을 λ“±λ‘ν•˜κ³  μ‚­μ œν•  수 μžˆλ‹€. (단, 노선에 λ“±λ‘λœ 역은 μ‚­μ œν•  수 μ—†λ‹€) -- μ€‘λ³΅λœ μ§€ν•˜μ²  μ—­ 이름이 등둝될 수 μ—†λ‹€. -- μ§€ν•˜μ²  역은 2κΈ€μž 이상이어야 ν•œλ‹€. -- μ§€ν•˜μ²  μ—­μ˜ λͺ©λ‘μ„ μ‘°νšŒν•  수 μžˆλ‹€. + +- μ§€ν•˜μ²  역을 λ“±λ‘ν•˜κ³  μ‚­μ œν•  수 μžˆλ‹€. (단, 노선에 λ“±λ‘λœ 역은 μ‚­μ œν•  수 μ—†λ‹€) (μ™„λ£Œ) +- μ€‘λ³΅λœ μ§€ν•˜μ²  μ—­ 이름이 등둝될 수 μ—†λ‹€. (μ™„λ£Œ) +- μ§€ν•˜μ²  역은 2κΈ€μž 이상이어야 ν•œλ‹€. (μ™„λ£Œ) +- μ§€ν•˜μ²  μ—­μ˜ λͺ©λ‘μ„ μ‘°νšŒν•  수 μžˆλ‹€. (μ™„λ£Œ) ### μ§€ν•˜μ²  λ…Έμ„  κ΄€λ ¨ κΈ°λŠ₯ -- μ§€ν•˜μ²  노선을 λ“±λ‘ν•˜κ³  μ‚­μ œν•  수 μžˆλ‹€. -- μ€‘λ³΅λœ μ§€ν•˜μ²  λ…Έμ„  이름이 등둝될 수 μ—†λ‹€. -- λ…Έμ„  등둝 μ‹œ 상행 쒅점역과 ν•˜ν–‰ 쒅점역을 μž…λ ₯λ°›λŠ”λ‹€. -- μ§€ν•˜μ²  λ…Έμ„ μ˜ λͺ©λ‘μ„ μ‘°νšŒν•  수 μžˆλ‹€. + +- μ§€ν•˜μ²  노선을 λ“±λ‘ν•˜κ³  μ‚­μ œν•  수 μžˆλ‹€. (μ™„λ£Œ) +- μ€‘λ³΅λœ μ§€ν•˜μ²  λ…Έμ„  이름이 등둝될 수 μ—†λ‹€. (μ™„λ£Œ) +- λ…Έμ„  이름은 2κΈ€μž 이상이어야 ν•œλ‹€. (μ™„λ£Œ) +- λ…Έμ„  등둝 μ‹œ 상행 쒅점역과 ν•˜ν–‰ 쒅점역을 μž…λ ₯λ°›λŠ”λ‹€. (μ™„λ£Œ) +- μ§€ν•˜μ²  λ…Έμ„ μ˜ λͺ©λ‘μ„ μ‘°νšŒν•  수 μžˆλ‹€. (μ™„λ£Œ) +- λ…Έμ„ μ—μ„œ κ°ˆλž˜κΈΈμ€ 생길 수 μ—†λ‹€. (상행선 ν˜Ήμ€ ν•˜ν–‰μ„ μ΄ λ‹€λ₯Έ λ…Έμ„ κ³Ό κ²ΉμΉ˜λŠ” κ²½μš°λ„ 갈래길둜 인정) (μ™„λ£Œ) ### μ§€ν•˜μ²  ꡬ간 μΆ”κ°€ κΈ°λŠ₯ -- μ§€ν•˜μ²  노선에 ꡬ간을 μΆ”κ°€ν•˜λŠ” κΈ°λŠ₯은 노선에 역을 μΆ”κ°€ν•˜λŠ” κΈ°λŠ₯이라고도 ν•  수 μžˆλ‹€. - - μ—­κ³Ό 역사이λ₯Ό ꡬ간이라 ν•˜κ³  이 κ΅¬κ°„λ“€μ˜ λͺ¨μŒμ΄ 노선이닀. -- ν•˜λ‚˜μ˜ 역은 μ—¬λŸ¬κ°œμ˜ 노선에 좔가될 수 μžˆλ‹€. -- μ—­κ³Ό μ—­ 사이에 μƒˆλ‘œμš΄ 역이 μΆ”κ°€ 될 수 μžˆλ‹€. -- λ…Έμ„ μ—μ„œ κ°ˆλž˜κΈΈμ€ 생길 수 μ—†λ‹€. + +- μ§€ν•˜μ²  노선에 ꡬ간을 μΆ”κ°€ν•˜λŠ” κΈ°λŠ₯은 노선에 역을 μΆ”κ°€ν•˜λŠ” κΈ°λŠ₯이라고도 ν•  수 μžˆλ‹€. (μ™„λ£Œ) + - μ—­κ³Ό 역사이λ₯Ό ꡬ간이라 ν•˜κ³  이 κ΅¬κ°„λ“€μ˜ λͺ¨μŒμ΄ 노선이닀. +- ν•˜λ‚˜μ˜ 역은 μ—¬λŸ¬κ°œμ˜ 노선에 좔가될 수 μžˆλ‹€. (μ™„λ£Œ) +- μ—­κ³Ό μ—­ 사이에 μƒˆλ‘œμš΄ 역이 μΆ”κ°€ 될 수 μžˆλ‹€. (μ™„λ£Œ) +- λ…Έμ„ μ—μ„œ κ°ˆλž˜κΈΈμ€ 생길 수 μ—†λ‹€. (상행선 ν˜Ήμ€ ν•˜ν–‰μ„ μ΄ λ‹€λ₯Έ λ…Έμ„ κ³Ό κ²ΉμΉ˜λŠ” κ²½μš°λ„ 갈래길둜 인정) (μ™„λ£Œ) +- ꡬ간 μˆœμ„œλŠ” μ–΄λ–€ μˆ«μžλ“  μž…λ ₯ 받을 수 μžˆλ‹€. (μ™„λ£Œ) + - 0보닀 μž‘μ€ 숫자λ₯Ό μž…λ ₯ 받은 경우, 상행선(0번 μˆœμ„œ)에 μΆ”κ°€ν•œλ‹€. + - ν•΄λ‹Ή λ…Έμ„  ν•˜ν–‰μ„  μˆœμ„œ 보닀 큰 숫자λ₯Ό μž…λ ₯ 받은 경우, ν•˜ν–‰μ„ μ— μΆ”κ°€ν•œλ‹€. ### μ§€ν•˜μ²  ꡬ간 μ‚­μ œ κΈ°λŠ₯ -- 노선에 λ“±λ‘λœ 역을 μ œκ±°ν•  수 μžˆλ‹€. -- 쒅점을 μ œκ±°ν•  경우 λ‹€μŒ 역이 쒅점이 λœλ‹€. -- 노선에 ν¬ν•¨λœ 역이 λ‘κ°œ μ΄ν•˜μΌ λ•ŒλŠ” 역을 μ œκ±°ν•  수 μ—†λ‹€. + +- 노선에 λ“±λ‘λœ 역을 μ œκ±°ν•  수 μžˆλ‹€. (μ™„λ£Œ) +- 쒅점을 μ œκ±°ν•  경우 λ‹€μŒ 역이 쒅점이 λœλ‹€. (μ™„λ£Œ) +- 노선에 ν¬ν•¨λœ 역이 λ‘κ°œ μ΄ν•˜μΌ λ•ŒλŠ” 역을 μ œκ±°ν•  수 μ—†λ‹€. (μ™„λ£Œ) ### μ§€ν•˜μ²  노선에 λ“±λ‘λœ μ—­ 쑰회 κΈ°λŠ₯ -- λ…Έμ„ μ˜ 상행 쒅점뢀터 ν•˜ν–‰ μ’…μ κΉŒμ§€ μ—°κ²°λœ μˆœμ„œλŒ€λ‘œ μ—­ λͺ©λ‘μ„ μ‘°νšŒν•  수 μžˆλ‹€. + +- λ…Έμ„ μ˜ 상행 쒅점뢀터 ν•˜ν–‰ μ’…μ κΉŒμ§€ μ—°κ²°λœ μˆœμ„œλŒ€λ‘œ μ—­ λͺ©λ‘μ„ μ‘°νšŒν•  수 μžˆλ‹€. (μ™„λ£Œ) + +## πŸ€” 고민사항 + +- μ‚¬μš©μž localStorage에 λ‹€λ₯Έ ν”„λ‘œκ·Έλž¨μ—μ„œ μ‚¬μš©ν•˜κ³  μžˆλŠ” stations와 linesκ°€ μžˆλŠ” 경우, μ–΄λ–»κ²Œ μ²˜λ¦¬ν•΄μ•Όν•˜λŠ”κ°€? + + 후보1. localStorage의 stations와 linesλ₯Ό μ΄ˆκΈ°ν™” + + => λ‹€λ₯Έ ν”„λ‘œκ·Έλž¨μ—μ„œ λ¬Έμ œκ°€ λ°œμƒν•  수 있음. λ˜ν•œ μ΄ˆκΈ°ν™” μ‹œ, 맀번 νŽ˜μ΄μ§€λ₯Ό μƒˆλ‘œκ³ μΉ¨ ν•  λ•Œλ§ˆλ‹€ μ΄ˆκΈ°ν™”κ°€ λ°œμƒν•˜κΈ° λ•Œλ¬Έμ— localStorage μ‚¬μš©μ΄μœ κ°€ 사라짐. + + 후보2. λ‹€λ₯Έ μ‚¬λžŒλ“€μ΄ μ ˆλŒ€ μ‚¬μš©ν•˜μ§€ μ•Šμ„ 것 같은 넀이밍을 μ‚¬μš©ν•œλ‹€. + + => κ°€μž₯ μ‰¬μš΄ λ°©λ²•μ΄μ§€λ§Œ μ½”λ“œμ˜ 가독성을 μœ„ν•΄ μ—¬κΈ°μ„œλŠ” μ‚¬μš©ν•˜μ§€ μ•Šμ•˜λ‹€.
## πŸ’» ν”„λ‘œκ·Έλž¨ μ‹€ν–‰ κ²°κ³Ό ### 역관리 + ### 노선관리 + ### ꡬ간관리 + ### 노선도 좜λ ₯ - - - -## βœ… ν”„λ‘œκ·Έλž˜λ° μš”κ΅¬μ‚¬ν•­ - -### 메뉴 λ²„νŠΌ -- μ—­ 관리 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/index.html b/index.html index fc99deac2..7529e6f1c 100644 --- a/index.html +++ b/index.html @@ -6,7 +6,49 @@
-

πŸš‡ μ§€ν•˜μ²  노선도 관리

+

πŸš‡ μ§€ν•˜μ²  노선도 관리

+ +
+
+ + + +
diff --git a/src/common/alertMessage.js b/src/common/alertMessage.js new file mode 100644 index 000000000..403ba1ad4 --- /dev/null +++ b/src/common/alertMessage.js @@ -0,0 +1,14 @@ +import { constant } from "./constant.js"; + +export const alertMessage = { + SHORT_LENGTH_ERROR: `μ΅œμ†Œ ${constant.minLength} κΈ€μž 이상 μž…λ ₯ν•΄μ•Ό ν•©λ‹ˆλ‹€.`, + ORDERING_INPUT_NOTHING_ERROR: `μˆœμ„œλ₯Ό μž…λ ₯ν•΄μ£Όμ„Έμš”`, + DELETE_CHECK_MESSAGE: "μ •λ§λ‘œ μ‚­μ œν•˜μ‹œκ² μŠ΅λ‹ˆκΉŒ?", + DELETE_STATION_ON_LINE_MESSAGE: "이 역은 ν˜„μž¬ 노선에 λ“±λ‘λ˜μ–΄ μžˆμ–΄ μ‚­μ œκ°€ λΆˆκ°€λŠ₯ν•©λ‹ˆλ‹€.", + DELETE_STATIONS_ON_LINE_SHORTAGE_MESSAGE: `μ„ νƒν•œ λ…Έμ„ μ˜ 역이 ${constant.minStations}개 μ΄ν•˜μΈ κ²½μš°μ—λŠ” μ‚­μ œν•  수 μ—†μŠ΅λ‹ˆλ‹€.`, + SAME_DESTINATION_ERROR: `상행과 ν•˜ν–‰μ΄ κ°™μŠ΅λ‹ˆλ‹€. λ‹€μ‹œ μž…λ ₯ν•΄μ£Όμ„Έμš”.`, + SAME_LINE_EXIST_ERROR: "이미 같은 이름을 κ°€μ§„ 노선이 μžˆμŠ΅λ‹ˆλ‹€.", + SAME_STATION_EXIST_ERROR: "이미 같은 이름을 κ°€μ§„ 역이 μžˆμŠ΅λ‹ˆλ‹€.", + SAME_SECTION_EXIST_ERROR: "이미 같은 ꡬ간이 λ“±λ‘λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.", + TRANSFER_STATION_MESSAGE: `역은 λ‹€λ₯Έ 노선에 λ“±λ‘λ˜μ–΄ μžˆμ–΄ μΆ”κ°€ν•  수 μ—†μŠ΅λ‹ˆλ‹€.\n "4. μ§€ν•˜μ²  노선도 좜λ ₯"을 ν΄λ¦­ν•˜μ—¬ 노선을 ν™•μΈν•΄μ£Όμ„Έμš”.\nπŸ“Œ ꡐ차역이 없도둝 ν•˜κΈ° μœ„ν•¨μž…λ‹ˆλ‹€. πŸ“Œ`, +}; diff --git a/src/common/checkInput.js b/src/common/checkInput.js new file mode 100644 index 000000000..6a3873afe --- /dev/null +++ b/src/common/checkInput.js @@ -0,0 +1,112 @@ +import { constant } from "./constant.js"; + +export function isSatisfyLength(len) { + let result = true; + if (String(len).length < constant.minLength) { + result = false; + } + + return result; +} + +export function isSameDestination(start, end) { + let result = true; + if (start !== end) { + result = false; + } + + return result; +} + +export function isStationAlreadyExist(stations, name) { + let result = false; + for (let i = 0; i < localStorage.stations.length; i++) { + if (name === stations[i]) { + result = true; + break; + } + } + + return result; +} + +export function isLineAlreadyExist(lines, name) { + let result = false; + for (let i = 0; i < lines.length; i++) { + if (name === lines[i].name) { + result = true; + break; + } + } + + return result; +} + +export function isSectionAlreadyExist(sections, name) { + let result = false; + for (let i = 0; i < sections.length; i++) { + if (name === sections[i]) { + result = true; + break; + } + } + + return result; +} + +function _isStationOnLine(sections, name) { + let result = false; + for (let i = 0; i < sections.length; i++) { + if (name === sections[i]) { + result = true; + break; + } + } + + return result; +} + +export function isStationOnLine(lines, name) { + let result = false; + for (let i = 0; i < lines.length; i++) { + if (_isStationOnLine(lines[i].sections, name)) { + result = true; + break; + } + } + + return result; +} + +export function isSatisfyMinNumOfStations(line) { + let result = false; + if (line.sections.length > constant.minStations) { + result = true; + } + + return result; +} + +export function _isTransferStation(sections, station) { + let result = false; + for (let i = 0; i < sections.length; i++) { + if (station === sections[i]) { + result = true; + break; + } + } + + return result; +} + +export function isTransferStation(lines, station) { + let result = false; + for (let i = 0; i < lines.length; i++) { + if (_isTransferStation(lines[i].sections, station)) { + result = true; + break; + } + } + + return result; +} diff --git a/src/common/clearInput.js b/src/common/clearInput.js new file mode 100644 index 000000000..f42c5f15e --- /dev/null +++ b/src/common/clearInput.js @@ -0,0 +1,4 @@ +export function clearInput() { + const $input = document.querySelectorAll("input"); + $input.forEach((input) => (input.value = "")); +} diff --git a/src/common/constant.js b/src/common/constant.js new file mode 100644 index 000000000..3b75617b1 --- /dev/null +++ b/src/common/constant.js @@ -0,0 +1,4 @@ +export const constant = { + minLength: 2, + minStations: 2, +}; diff --git a/src/event/common/checkStorageItem.js b/src/event/common/checkStorageItem.js new file mode 100644 index 000000000..16b8caf40 --- /dev/null +++ b/src/event/common/checkStorageItem.js @@ -0,0 +1,25 @@ +export default function checkLocalStorageItem() { + if (localStorage.stations === undefined) { + localStorage.stations = JSON.stringify([ + "인천", + "λ™μΈμ²œ", + "도원", + "μ†Œμš”μ‚°", + "사당", + "μ‹œμ²­", + "신도림", + "λŒ€ν™”", + "였금", + "μ˜€μ΄λ„", + "λ‹Ήκ³ κ°œ", + ]); + } + + if (localStorage.lines === undefined) { + localStorage.lines = JSON.stringify([ + { name: "1ν˜Έμ„ ", sections: ["인천", "μ†Œμš”μ‚°"] }, + { name: "2ν˜Έμ„ ", sections: ["μ‹œμ²­", "신도림"] }, + { name: "3ν˜Έμ„ ", sections: ["λŒ€ν™”", "였금"] }, + ]); + } +} diff --git a/src/event/line/addLineEvent.js b/src/event/line/addLineEvent.js new file mode 100644 index 000000000..da24c65c8 --- /dev/null +++ b/src/event/line/addLineEvent.js @@ -0,0 +1,46 @@ +import renderLine from "../../render/renderLine.js"; +import { + isSatisfyLength, + isSameDestination, + isLineAlreadyExist, + isTransferStation, +} from "../../common/checkInput.js"; +import { alertMessage } from "../../common/alertMessage.js"; +import { clearInput } from "../../common/clearInput.js"; + +function addLine(line, start, end) { + let lines = JSON.parse(localStorage.lines); + let newLine = { name: String(line), sections: [start, end] }; + + lines.push(newLine); + localStorage.lines = JSON.stringify(lines); + + renderLine(); + clearInput(); +} + +function checkValidLine() { + const line = document.getElementById("line-name-input").value; + const start = document.getElementById("line-start-station-selector").value; + const end = document.getElementById("line-end-station-selector").value; + const lines = JSON.parse(localStorage.lines); + + if (!isSatisfyLength(line)) { + return alert(alertMessage.SHORT_LENGTH_ERROR); + } else if (isSameDestination(start, end)) { + return alert(alertMessage.SAME_DESTINATION_ERROR); + } else if (isLineAlreadyExist(lines, line)) { + return alert(alertMessage.SAME_LINE_EXIST_ERROR); + } else if (isTransferStation(lines, start)) { + return alert(`${start}` + alertMessage.TRANSFER_STATION_MESSAGE); + } else if (isTransferStation(lines, end)) { + return alert(`${end}` + alertMessage.TRANSFER_STATION_MESSAGE); + } + + addLine(line, start, end); +} + +export default function addLineEvent() { + const $addLineBtn = document.getElementById("line-add-button"); + $addLineBtn.addEventListener("click", checkValidLine); +} diff --git a/src/event/line/delLineEvent.js b/src/event/line/delLineEvent.js new file mode 100644 index 000000000..95a786add --- /dev/null +++ b/src/event/line/delLineEvent.js @@ -0,0 +1,35 @@ +import renderLine from "../../render/renderLine.js"; +import { alertMessage } from "../../common/alertMessage.js"; + +function delLine(line) { + const lines = JSON.parse(localStorage.lines); + for (let i = 0; i < lines.length; i++) { + if (line === lines[i].name) { + lines.splice(i, 1); + } + } + + localStorage.lines = JSON.stringify(lines); + + renderLine(); +} + +function findDeleteTarget(event) { + const $target = event.target; + const $lineTable = document.getElementsByClassName("line-table-row"); + const targetNumber = $target.closest("tr").dataset.number; + const line = $lineTable[targetNumber].querySelector("span").innerText; + + delLine(line); +} + +export default function delLineEvent() { + const $delLineBtn = document.querySelectorAll(".line-delete-button"); + $delLineBtn.forEach((button) => { + button.addEventListener("click", (event) => { + if (confirm(alertMessage.DELETE_CHECK_MESSAGE)) { + findDeleteTarget(event); + } + }); + }); +} diff --git a/src/event/menuEvent.js b/src/event/menuEvent.js new file mode 100644 index 000000000..1e5bbcc23 --- /dev/null +++ b/src/event/menuEvent.js @@ -0,0 +1,39 @@ +import render from "../render/render.js"; +import checkLocalStorageItem from "./common/checkStorageItem.js"; +import addStationEvent from "./station/addStationEvent.js"; +import addLineEvent from "./line/addLineEvent.js"; +import chooseLineEvent from "./section/chooseLineEvent.js"; + +function setHiddenPages() { + const $page = document.querySelectorAll(".page"); + $page.forEach((page) => (page.style.display = "none")); +} + +function changeManagerContainer({ target }) { + setHiddenPages(); + + if (target.id === "station-manager-button") { + document.getElementById("station-page").style.display = "block"; + addStationEvent(); + render("station"); + } else if (target.id === "line-manager-button") { + document.getElementById("line-page").style.display = "block"; + addLineEvent(); + render("line"); + } else if (target.id === "section-manager-button") { + document.getElementById("section-page").style.display = "block"; + render("section"); + chooseLineEvent(); + } else if (target.id === "map-print-manager-button") { + document.getElementById("map-print-page").style.display = "block"; + render("mapPrint"); + } +} + +export default function menuEvent() { + const $menuButton = document.querySelector(".menu-button"); + + checkLocalStorageItem(); + + $menuButton.addEventListener("click", changeManagerContainer); +} diff --git a/src/event/section/addSectionEvent.js b/src/event/section/addSectionEvent.js new file mode 100644 index 000000000..aa6082f6e --- /dev/null +++ b/src/event/section/addSectionEvent.js @@ -0,0 +1,52 @@ +import { renderSection } from "../../render/renderSection.js"; +import { isSectionAlreadyExist, isTransferStation } from "../../common/checkInput.js"; +import { alertMessage } from "../../common/alertMessage.js"; +import { clearInput } from "../../common/clearInput.js"; + +export function getIndexOfLine(lineName) { + const lines = JSON.parse(localStorage.lines); + let indexOfLine; + + for (let i = 0; i < lines.length; i++) { + if (lineName === lines[i].name) { + indexOfLine = i; + break; + } + } + + return indexOfLine; +} + +function addSection(indexOfLine, selectedLine, selectedStaion, stationOrder) { + let lines = JSON.parse(localStorage.lines); + lines[indexOfLine].sections = [ + ...lines[indexOfLine].sections.slice(0, stationOrder), + selectedStaion, + ...lines[indexOfLine].sections.slice(stationOrder), + ]; + localStorage.lines = JSON.stringify(lines); + + renderSection(selectedLine); + clearInput(); +} + +function checkValidSection(selectedLine) { + const selectedStation = document.getElementById("section-station-selector").value; + const stationOrder = document.getElementById("section-order-input").value; + const lines = JSON.parse(localStorage.lines); + const indexOfLine = getIndexOfLine(selectedLine); + if (isSectionAlreadyExist(lines[indexOfLine].sections, selectedStation)) { + return alert(alertMessage.SAME_SECTION_EXIST_ERROR); + } else if (stationOrder === "") { + return alert(alertMessage.ORDERING_INPUT_NOTHING_ERROR); + } else if (isTransferStation(lines, selectedStation)) { + return alert(`${selectedStation}` + alertMessage.TRANSFER_STATION_MESSAGE); + } + + addSection(indexOfLine, selectedLine, selectedStation, stationOrder); +} + +export default function addSectionEvent(selectedLine) { + const $addSectionBtn = document.getElementById("section-add-button"); + $addSectionBtn.addEventListener("click", () => checkValidSection(selectedLine)); +} diff --git a/src/event/section/chooseLineEvent.js b/src/event/section/chooseLineEvent.js new file mode 100644 index 000000000..ddfcb5c74 --- /dev/null +++ b/src/event/section/chooseLineEvent.js @@ -0,0 +1,17 @@ +import { renderSection } from "../../render/renderSection.js"; + +function onClickLine(event) { + const $target = event.target; + const targetName = $target.dataset.name; + + renderSection(targetName); +} + +export default function addSectionEvent() { + const $sectionLineMenuBtns = document.querySelectorAll(".section-line-menu-button"); + $sectionLineMenuBtns.forEach((button) => + button.addEventListener("click", (event) => { + onClickLine(event); + }) + ); +} diff --git a/src/event/section/delSectionEvent.js b/src/event/section/delSectionEvent.js new file mode 100644 index 000000000..f7cd90c67 --- /dev/null +++ b/src/event/section/delSectionEvent.js @@ -0,0 +1,54 @@ +import { renderSection } from "../../render/renderSection.js"; +import { getIndexOfLine } from "./addSectionEvent.js"; +import { isSatisfyMinNumOfStations } from "../../common/checkInput.js"; +import { alertMessage } from "../../common/alertMessage.js"; + +function getIndexOfSection(indexOfLine, section) { + const lines = JSON.parse(localStorage.lines); + let indexOfSection; + + for (let i = 0; i < lines[indexOfLine].sections.length; i++) { + if (section === lines[indexOfLine].sections[i]) { + indexOfSection = i; + break; + } + } + + return indexOfSection; +} + +function delSection(selectedLine, section) { + const indexOfLine = getIndexOfLine(selectedLine); + const indexOfSection = getIndexOfSection(indexOfLine, section); + let lines = JSON.parse(localStorage.lines); + + if (!isSatisfyMinNumOfStations(lines[indexOfLine])) { + return alert(alertMessage.DELETE_STATIONS_ON_LINE_SHORTAGE_MESSAGE); + } + + lines[indexOfLine].sections.splice(indexOfSection, 1); + localStorage.lines = JSON.stringify(lines); + + renderSection(selectedLine); +} + +function findDeleteTarget(event, selectedLine) { + const $target = event.target; + const $sectionTable = document.getElementsByClassName("section-table-row"); + const targetNumber = $target.closest("tr").dataset.number; + const section = $sectionTable[targetNumber].querySelector("span").innerText; + + delSection(selectedLine, section); +} + +export default function delSectionEvent(selectedLine) { + const $delSectionBtn = document.querySelectorAll(".section-delete-button"); + + $delSectionBtn.forEach((button) => + button.addEventListener("click", (event) => { + if (confirm(alertMessage.DELETE_CHECK_MESSAGE)) { + findDeleteTarget(event, selectedLine); + } + }) + ); +} diff --git a/src/event/station/addStationEvent.js b/src/event/station/addStationEvent.js new file mode 100644 index 000000000..283471b93 --- /dev/null +++ b/src/event/station/addStationEvent.js @@ -0,0 +1,29 @@ +import renderStation from "../../render/renderStation.js"; +import { isSatisfyLength, isStationAlreadyExist } from "../../common/checkInput.js"; +import { alertMessage } from "../../common/alertMessage.js"; +import { clearInput } from "../../common/clearInput.js"; + +function addStation(stationInput) { + let stations = JSON.parse(localStorage.stations); + stations.push(String(stationInput)); + localStorage.stations = JSON.stringify(stations); + renderStation(); + clearInput(); +} + +function checkValidStation() { + const station = document.getElementById("station-name-input").value; + let stations = JSON.parse(localStorage.stations); + + if (!isSatisfyLength(station)) { + return alert(alertMessage.SHORT_LENGTH_ERROR); + } else if (isStationAlreadyExist(stations, station)) { + return alert(alertMessage.SAME_STATION_EXIST_ERROR); + } + addStation(station); +} + +export default function addStationEvent() { + const $addStationBtn = document.getElementById("station-add-button"); + $addStationBtn.addEventListener("click", checkValidStation); +} diff --git a/src/event/station/delStationEvent.js b/src/event/station/delStationEvent.js new file mode 100644 index 000000000..12dbfae55 --- /dev/null +++ b/src/event/station/delStationEvent.js @@ -0,0 +1,38 @@ +import renderStation from "../../render/renderStation.js"; +import { isStationOnLine } from "../../common/checkInput.js"; +import { alertMessage } from "../../common/alertMessage.js"; + +function delStation(station) { + const lines = JSON.parse(localStorage.lines); + const stations = JSON.parse(localStorage.stations); + + stations.splice(stations.indexOf(station), 1); + + if (isStationOnLine(lines, station)) { + return alert(alertMessage.DELETE_STATION_ON_LINE_MESSAGE); + } + + localStorage.stations = JSON.stringify(stations); + + renderStation(); +} + +function findDeleteTarget(event) { + const $target = event.target; + const $stationTable = document.getElementsByClassName("station-table-row"); + const targetNumber = $target.closest("tr").dataset.number; + const station = $stationTable[targetNumber].querySelector("span").innerText; + + delStation(station); +} + +export default function delStationEvent() { + const $delStationBtn = document.querySelectorAll(".station-delete-button"); + $delStationBtn.forEach((button) => + button.addEventListener("click", (event) => { + if (confirm(alertMessage.DELETE_CHECK_MESSAGE)) { + findDeleteTarget(event); + } + }) + ); +} diff --git a/src/index.js b/src/index.js index e69de29bb..2b42f38e2 100644 --- a/src/index.js +++ b/src/index.js @@ -0,0 +1,7 @@ +import menuEvent from "./event/menuEvent.js"; + +export default function SubwayMap() { + menuEvent(); +} + +new SubwayMap(); diff --git a/src/render/render.js b/src/render/render.js new file mode 100644 index 000000000..739eebced --- /dev/null +++ b/src/render/render.js @@ -0,0 +1,16 @@ +import renderStation from "./renderStation.js"; +import renderLine from "./renderLine.js"; +import renderMapPrint from "./renderMapPrint.js"; +import { renderLineButton } from "./renderSection.js"; + +export default function render(name) { + if (name === "station") { + renderStation(); + } else if (name === "line") { + renderLine(); + } else if (name === "section") { + renderLineButton(); + } else if (name === "mapPrint") { + renderMapPrint(); + } +} diff --git a/src/render/renderLine.js b/src/render/renderLine.js new file mode 100644 index 000000000..5f29d7247 --- /dev/null +++ b/src/render/renderLine.js @@ -0,0 +1,71 @@ +import delLineEvent from "../event/line/delLineEvent.js"; + +function selectListTemplate() { + const stations = JSON.parse(localStorage.stations); + let newHTML = ""; + + stations.forEach((station) => (newHTML += ``)); + + return newHTML; +} + +function initSelectContainer() { + const $selectContainers = document.querySelectorAll(".line-station-selector"); + $selectContainers.forEach( + (selectContainer) => (selectContainer.innerHTML = selectListTemplate()) + ); +} + +function lineTableTemplate() { + return ` + + + + + + +
λ…Έμ„  이름상행 μ’…μ μ—­ν•˜ν–‰ 쒅점역섀정
`; +} + +function initLineListContainer() { + const $lineTableContainer = document.getElementById("line-table-container"); + $lineTableContainer.innerHTML = lineTableTemplate(); +} + +function lineListTemplate(line, lineNumber) { + return ` + + ${line.name} + + + ${line.sections[0]} + + + ${line.sections[line.sections.length - 1]} + + + + + `; +} + +function initLineList(lines) { + const $lineTable = document.querySelector(".line-table"); + let lineNumber = 0; + + lines.forEach((line) => + $lineTable.insertAdjacentHTML("beforeend", lineListTemplate(line, lineNumber++)) + ); + + delLineEvent(); +} + +export default function renderStation() { + initSelectContainer(); + initLineListContainer(); + + const lines = JSON.parse(localStorage.lines); + if (lines !== null) { + initLineList(lines); + } +} diff --git a/src/render/renderMapPrint.js b/src/render/renderMapPrint.js new file mode 100644 index 000000000..fe525c5d6 --- /dev/null +++ b/src/render/renderMapPrint.js @@ -0,0 +1,22 @@ +function mapPrintTemplate() { + const lines = JSON.parse(localStorage.lines); + let newHTML = `
`; + + lines.forEach( + (line) => + (newHTML += `

${line.name}

`) + ); + + return newHTML + "
"; +} + +function initMapPrint() { + const $mapPrintContainer = document.getElementById("map-print-container"); + $mapPrintContainer.innerHTML = mapPrintTemplate(); +} + +export default function renderMapPrint() { + initMapPrint(); +} diff --git a/src/render/renderSection.js b/src/render/renderSection.js new file mode 100644 index 000000000..3348c193c --- /dev/null +++ b/src/render/renderSection.js @@ -0,0 +1,106 @@ +import addSectionEvent from "../event/section/addSectionEvent.js"; +import delSectionEvent from "../event/section/delSectionEvent.js"; + +function lineSelectButtonTemplate() { + const lines = JSON.parse(localStorage.lines); + + return lines + .map( + (line) => + `` + ) + .join(" "); +} + +function initLineSelectButtonCotainer() { + const $lineSelectButtonContainer = document.getElementById("line-select-button-container"); + $lineSelectButtonContainer.innerHTML = lineSelectButtonTemplate(); +} + +export function renderLineButton() { + initLineSelectButtonCotainer(); +} + +function sectionInputTemplate(line) { + return `

${line}

+

ꡬ간 등둝

+
+ + + +
`; +} + +function initSectionInputContainer(selectedLine) { + const $sectionInputContainer = document.getElementById("section-input-container"); + $sectionInputContainer.innerHTML = sectionInputTemplate(selectedLine); + + addSectionEvent(selectedLine); +} + +function sectionSelectorTemplate() { + const stations = JSON.parse(localStorage.stations); + let newHTML = ""; + + stations.forEach((station) => (newHTML += ``)); + + return newHTML; +} + +function initSectionSelector() { + const $sectionStationSelector = document.getElementById("section-station-selector"); + $sectionStationSelector.innerHTML = sectionSelectorTemplate(); +} + +function sectionTableTemplate() { + return `

+ + + + + +
μˆœμ„œμ΄λ¦„μ„€μ •

`; +} + +function initSectionTableContainer() { + const $sectionTableContainer = document.getElementById("section-table-container"); + $sectionTableContainer.innerHTML = sectionTableTemplate(); +} + +function sectionListTemplate(section, sectionNumber) { + return ` + + ${sectionNumber} + + + ${section} + + + + + + `; +} + +function initSectionList(selectedLine) { + const lines = JSON.parse(localStorage.lines); + const $sectionTable = document.querySelector(".section-table"); + let sectionNumber = 0; + + for (let i = 0; i < lines.length; i++) { + if (selectedLine === lines[i].name) { + lines[i].sections.forEach((section) => + $sectionTable.insertAdjacentHTML("beforeend", sectionListTemplate(section, sectionNumber++)) + ); + } + } + + delSectionEvent(selectedLine); +} + +export function renderSection(selectedLine) { + initSectionInputContainer(selectedLine); + initSectionSelector(); + initSectionTableContainer(); + initSectionList(selectedLine); +} diff --git a/src/render/renderStation.js b/src/render/renderStation.js new file mode 100644 index 000000000..d1eca5190 --- /dev/null +++ b/src/render/renderStation.js @@ -0,0 +1,46 @@ +import delStationEvent from "../event/station/delStationEvent.js"; + +function stationTableTemplate() { + return ` + + + + +
μ—­ 이름섀정
`; +} + +function initStationListContainer() { + const $stationTableContainer = document.getElementById("station-table-container"); + $stationTableContainer.innerHTML = stationTableTemplate(); +} + +function stationListTemplate(station, stationNumber) { + return ` + + ${station} + + + + + `; +} + +function initStationList(stations) { + const $stationTable = document.querySelector(".station-table"); + let stationNumber = 0; + + stations.forEach((station) => + $stationTable.insertAdjacentHTML("beforeend", stationListTemplate(station, stationNumber++)) + ); + + delStationEvent(); +} + +export default function renderStation() { + initStationListContainer(); + + const stations = JSON.parse(localStorage.stations); + if (stations !== null) { + initStationList(stations); + } +}