diff --git a/README.md b/README.md index e97a1d649..46040b0eb 100644 --- a/README.md +++ b/README.md @@ -1,69 +1,153 @@ # πŸš‡ μ§€ν•˜μ²  노선도 λ―Έμ…˜ +## 🚦 디렉토리 ꡬ쑰 +``` +β”œβ”€β”€ LICENSE +β”œβ”€β”€ README.md +β”œβ”€β”€ images +β”‚ β”œβ”€β”€ line_manager.gif +β”‚ β”œβ”€β”€ map_print_manager.gif +β”‚ β”œβ”€β”€ section1.png +β”‚ β”œβ”€β”€ section2.png +β”‚ β”œβ”€β”€ section_manager.gif +β”‚ └── station_manager.gif +β”œβ”€β”€ index.html +└── src + β”œβ”€β”€ config.js + β”œβ”€β”€ core + β”‚ └── Component.js + β”œβ”€β”€ index.js + β”œβ”€β”€ pages + β”‚ β”œβ”€β”€ LineManager.js + β”‚ β”œβ”€β”€ PrintSubwayLine.js + β”‚ β”œβ”€β”€ SectionManager.js + β”‚ └── StationManager.js + └── utils + β”œβ”€β”€ errors.js + β”œβ”€β”€ events.js + └── reRenderPage.js +``` +- config: 전체 νŽ˜μ΄μ§€μ— ν•„μš”ν•œ νƒœκ·Έλ₯Ό λ”°λ‘œ κ΄€λ¦¬ν•˜κΈ° μœ„ν•΄ μ„€μ • 파일둜 뢄리 +- core: μƒνƒœλ‘œ HTML을 λ Œλ”λ§ν•˜κΈ° μœ„ν•΄ μ‚¬μš©λ˜λŠ” λ©”μ„œλ“œλ“€μ„ μ»΄ν¬λ„ŒνŠΈ 클래슀λ₯Ό μ‚¬μš©ν•˜μ—¬ μ •μ˜ +- pages: 각각의 화면듀을 ν•œ ν΄λ”μ—μ„œ κ΄€λ¦¬ν•˜κΈ° μœ„ν•΄ pages 폴더에 뢄리 +- utils: μ—λŸ¬λ©”μ‹œμ§€, 이벀트, νŽ˜μ΄μ§€λ₯Ό λ‹€μ‹œ κ·Έλ €μ£ΌλŠ” ν•¨μˆ˜λ₯Ό utils 폴더에 뢄리 + +
+ ## πŸš€ κΈ°λŠ₯ μš”κ΅¬μ‚¬ν•­ -### μ§€ν•˜μ²  μ—­ κ΄€λ ¨ κΈ°λŠ₯ -- μ§€ν•˜μ²  역을 λ“±λ‘ν•˜κ³  μ‚­μ œν•  수 μžˆλ‹€. (단, 노선에 λ“±λ‘λœ 역은 μ‚­μ œν•  수 μ—†λ‹€) -- μ€‘λ³΅λœ μ§€ν•˜μ²  μ—­ 이름이 등둝될 수 μ—†λ‹€. -- μ§€ν•˜μ²  역은 2κΈ€μž 이상이어야 ν•œλ‹€. -- μ§€ν•˜μ²  μ—­μ˜ λͺ©λ‘μ„ μ‘°νšŒν•  수 μžˆλ‹€. +### 1. μ—­ 관리 νŽ˜μ΄μ§€ -### μ§€ν•˜μ²  λ…Έμ„  κ΄€λ ¨ κΈ°λŠ₯ -- μ§€ν•˜μ²  노선을 λ“±λ‘ν•˜κ³  μ‚­μ œν•  수 μžˆλ‹€. -- μ€‘λ³΅λœ μ§€ν•˜μ²  λ…Έμ„  이름이 등둝될 수 μ—†λ‹€. -- λ…Έμ„  등둝 μ‹œ 상행 쒅점역과 ν•˜ν–‰ 쒅점역을 μž…λ ₯λ°›λŠ”λ‹€. -- μ§€ν•˜μ²  λ…Έμ„ μ˜ λͺ©λ‘μ„ μ‘°νšŒν•  수 μžˆλ‹€. +#### 1) μ—­μ˜ 이름 μœ νš¨μ„± 검사 + +- μ€‘λ³΅λœ μ—­μ˜ 이름을 등둝할 수 μ—†λ‹€. +- μ—­μ˜ 이름은 2κΈ€μž μ΄μƒμœΌλ‘œ ν•œλ‹€. +- μ—­μ˜ 이름에 곡백이 λ“€μ–΄κ°ˆ 수 μ—†λ‹€. +- μ—­μ˜ 이름은 ν•œκΈ€λ‘œλ§Œ μž‘μ„± κ°€λŠ₯ν•˜λ‹€. +- μ—­μ˜ 이름이 μ˜¬λ°”λ₯΄μ§€ μ•Šμ„ μ‹œ, `alert` λ©”μ‹œμ§€λ₯Ό 좜λ ₯ν•œλ‹€. + +#### 2) μ§€ν•˜μ²  μ—­ λͺ©λ‘ 관리 + +- μ—­ 관리 λ²„νŠΌ 클릭 μ‹œ, μ—­ 관리 νŽ˜μ΄μ§€λ₯Ό 좜λ ₯ν•œλ‹€. +- μ—­ μΆ”κ°€ λ²„νŠΌ 클릭 μ‹œ, 역을 μΆ”κ°€ν•œλ‹€. +- μ—­ μ‚­μ œ λ²„νŠΌ 클릭 μ‹œ, 역을 μ‚­μ œν•œλ‹€. +- μ—­μ˜ 좔가와 μ‚­μ œ μ‹œ, μ—­μ˜ λͺ©λ‘μ„ 좜λ ₯ν•œλ‹€. +- μ—­ 관리가 λλ‚œ μ—­μ˜ λͺ©λ‘μ„ λ‘œμ»¬μŠ€ν† λ¦¬μ§€μ— μ €μž₯ν•œλ‹€. + +### 2. λ…Έμ„  관리 νŽ˜μ΄μ§€ + +#### 1) λ…Έμ„ μ˜ 이름 μœ νš¨μ„± 검사 + +- μ€‘λ³΅λœ λ…Έμ„ μ˜ 이름을 등둝할 수 μ—†λ‹€. +- λ…Έμ„ μ˜ 이름에 곡백이 λ“€μ–΄κ°ˆ 수 μ—†λ‹€. +- λ…Έμ„ μ˜ 이름이 μ˜¬λ°”λ₯΄μ§€ μ•Šμ„ μ‹œ, `alert` λ©”μ‹œμ§€λ₯Ό 좜λ ₯ν•œλ‹€. + +#### 2) μ§€ν•˜μ²  λ…Έμ„  λͺ©λ‘ 관리 + +- λ…Έμ„  관리 λ²„νŠΌ 클릭 μ‹œ, λ…Έμ„  관리 νŽ˜μ΄μ§€λ₯Ό 좜λ ₯ν•œλ‹€. +- λ…Έμ„  μΆ”κ°€ λ²„νŠΌ 클릭 μ‹œ, 노선을 μΆ”κ°€ν•œλ‹€. +- μ‚­μ œ λ²„νŠΌ 클릭 μ‹œ, 노선을 μ‚­μ œν•œλ‹€. +- λ…Έμ„  좔가와 μ‚­μ œ μ‹œ, λ…Έμ„  λͺ©λ‘μ„ 좜λ ₯ν•˜κ³  λ‘œμ»¬μŠ€ν† λ¦¬μ§€μ— λ…Έμ„  λͺ©λ‘μ„ μ €μž₯ν•œλ‹€. + +### 3. ꡬ간 관리 νŽ˜μ΄μ§€ + +#### 1) λ…Έμ„  선택 + +- λ‘œμ»¬μŠ€ν† λ¦¬μ§€μ—μ„œ 노선을 받아와 λ…Έμ„  메뉴 λ²„νŠΌμ„ μƒμ„±ν•œλ‹€. +- 각 λ…Έμ„ μ˜ λ²„νŠΌμ„ 클릭 μ‹œ, ν•΄λ‹Ή λ…Έμ„ μ˜ 정보λ₯Ό 좜λ ₯ν•œλ‹€. + +#### 2) μ§€ν•˜μ²  ꡬ간 λͺ©λ‘ 관리 -### μ§€ν•˜μ²  ꡬ간 μΆ”κ°€ κΈ°λŠ₯ - μ§€ν•˜μ²  노선에 ꡬ간을 μΆ”κ°€ν•˜λŠ” κΈ°λŠ₯은 노선에 역을 μΆ”κ°€ν•˜λŠ” κΈ°λŠ₯이라고도 ν•  수 μžˆλ‹€. - - μ—­κ³Ό 역사이λ₯Ό ꡬ간이라 ν•˜κ³  이 κ΅¬κ°„λ“€μ˜ λͺ¨μŒμ΄ 노선이닀. +- μ—­κ³Ό 역사이λ₯Ό ꡬ간이라 ν•˜κ³  이 κ΅¬κ°„λ“€μ˜ λͺ¨μŒμ΄ 노선이닀. - ν•˜λ‚˜μ˜ 역은 μ—¬λŸ¬κ°œμ˜ 노선에 좔가될 수 μžˆλ‹€. - μ—­κ³Ό μ—­ 사이에 μƒˆλ‘œμš΄ 역이 μΆ”κ°€ 될 수 μžˆλ‹€. - λ…Έμ„ μ—μ„œ κ°ˆλž˜κΈΈμ€ 생길 수 μ—†λ‹€. +- 좔가될 μ—­μ˜ 이름과 μˆœμ„œλ₯Ό μž…λ ₯ λ°›λŠ”λ‹€. +- 등둝 λ²„νŠΌ 클릭 μ‹œ, 노선에 역을 μΆ”κ°€ν•œλ‹€. +- λ…Έμ„ μ—μ„œ 제거 λ²„νŠΌ 클릭 μ‹œ, 노선에 역을 μ œκ±°ν•œλ‹€. +- λ…Έμ„ μ˜ 등둝과 제거 μ‹œ, ꡬ간 λͺ©λ‘μ„ 좜λ ₯ν•˜κ³  λ‘œμ»¬μŠ€ν† λ¦¬μ§€μ— λ…Έμ„  λͺ©λ‘μ„ μ €μž₯ν•œλ‹€. +- 쒅점 제거 μ‹œ λ‹€μŒ 역이 쒅점이 λœλ‹€. +- 노선에 ν¬ν•¨λœ 역이 2개 μ΄ν•˜μΌ λ•Œ, 역을 μ œκ±°ν•  수 μ—†λ‹€. +- μΆ”κ°€ν•  μ—­μ˜ μˆœμ„œλ₯Ό μž…λ ₯ν•˜μ§€ μ•ŠμœΌλ©΄ `alert` λ©”μ‹œμ§€λ₯Ό 좜λ ₯ν•œλ‹€. - +### 4. μ§€ν•˜μ²  노선도 좜λ ₯ -### μ§€ν•˜μ²  ꡬ간 μ‚­μ œ κΈ°λŠ₯ -- 노선에 λ“±λ‘λœ 역을 μ œκ±°ν•  수 μžˆλ‹€. -- 쒅점을 μ œκ±°ν•  경우 λ‹€μŒ 역이 쒅점이 λœλ‹€. -- 노선에 ν¬ν•¨λœ 역이 λ‘κ°œ μ΄ν•˜μΌ λ•ŒλŠ” 역을 μ œκ±°ν•  수 μ—†λ‹€. +- λ‘œμ»¬μŠ€ν† λ¦¬μ§€μ— μ €μž₯된 노선도λ₯Ό 화면에 좜λ ₯ν•œλ‹€. + +
- +## πŸ₯Š λ―Έν‘ν–ˆλ˜ 점 & κ³ μƒν–ˆλ˜ 점 -### μ§€ν•˜μ²  노선에 λ“±λ‘λœ μ—­ 쑰회 κΈ°λŠ₯ -- λ…Έμ„ μ˜ 상행 쒅점뢀터 ν•˜ν–‰ μ’…μ κΉŒμ§€ μ—°κ²°λœ μˆœμ„œλŒ€λ‘œ μ—­ λͺ©λ‘μ„ μ‘°νšŒν•  수 μžˆλ‹€. +- [data]: ν˜„μž¬ κ΅¬μ‘°μ—μ„œ μ–΄λ–»κ²Œ [data]속성을 μ‚¬μš©ν•΄μ•Ό ν• μ§€ νŒŒμ•…μ„ ν•˜μ§€ λͺ»ν–ˆμŠ΅λ‹ˆλ‹€. +- μƒˆλ‘œμš΄ ꡬ쑰둜 코딩을 ν•˜λ‹€ λ³΄λ‹ˆ λ§‰νžˆλŠ” 뢀뢄이 λ§Žμ•˜κ³ , 그둜 인해 μ‹œκ°„μ— 쫓겨 더 λ§Žμ€ μ˜ˆμ™Έμ‚¬ν•­μ— λŒ€ν•œ 고민이 λΆ€μ‘±ν–ˆμŠ΅λ‹ˆλ‹€. +- μƒνƒœκ°€ λ³€ν•  λ•Œ λ§ˆλ‹€ 전체 화면을 λ Œλ”λ§ν•˜λŠ” ꡬ쑰둜 코딩을 ν•˜μ—¬, λ³€ν™”ν•˜μ§€ μ•ŠλŠ” 뢀뢄도 λ‹€μ‹œ κ·Έλ €μ•Ό ν–ˆμŠ΅λ‹ˆλ‹€. +- 각 νŽ˜μ΄μ§€λ§ˆλ‹€ ν•˜λ‚˜μ˜ 클래슀 μ•ˆμ— λ§Žμ€ λ©”μ„œλ“œκ°€ μ‘΄μž¬ν•˜μ—¬ 읽기 λΆˆνŽΈν•˜λ‹€κ³  생각이 λ“€μ—ˆμŠ΅λ‹ˆλ‹€. +- eslint와 prettier의 μ…‹νŒ…μ„ μ—λ””ν„°μ—μ„œλ§Œ μ„€μ •ν•˜κ³ , 파일둜 λ³Ό 수 μžˆλ„λ‘ ν•˜μ§€ λͺ»ν•œ 점이 μ•„μ‰½μŠ΅λ‹ˆλ‹€. +- 더 효율적인 μ½”λ“œλ₯Ό μž‘μ„±ν•˜μ§€ λͺ»ν•˜κ³ , λ‹¨μˆœνžˆ 15라인을 λ„˜μ§€ μ•ŠκΈ° μœ„ν•΄ μ–΅μ§€λ‘œ λΆ„λ¦¬ν•œ λ“―ν•œ 뢀뢄이 μžˆμ–΄ μ•„μ‰½μŠ΅λ‹ˆλ‹€. +- μ–΄λ– ν•œ 이유둜 μ—λŸ¬λ©”μ‹œμ§€κ°€ 화면에 뜬 건지 μƒν™©λ§ˆλ‹€ 더 λͺ…ν™•ν•˜κ²Œ μ„€λͺ…을 ν•˜μ§€ λͺ»ν–ˆμŠ΅λ‹ˆλ‹€. λ‹€λ₯Έ λΆ„μ˜ pr을 보고 try catch문을 μ‚¬μš©ν•˜λ©΄ 더 λͺ…ν™•ν•˜κ²Œ μ„€λͺ…을 ν•  수 μžˆλ‹€λŠ” 것을 배울 수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€. +- map으둜 화면을 그릴 λ•Œ, 화면에 계속 μ˜λ„μΉ˜ μ•Šμ€ ν…μŠ€νŠΈκ°€ λ°œμƒν•˜μ—¬ 원인을 μ°ΎλŠ”λ° κ½€ 였래 κ±Έλ ΈμŠ΅λ‹ˆλ‹€. 결과적으둜 배열인데 `join` 을 μ‚¬μš©ν•˜μ§€ μ•Šμ•„ λ°œμƒν•œ λ¬Έμ œμ˜€μŠ΅λ‹ˆλ‹€. +- ν…œν”Œλ¦Ώ λ¦¬ν„°λŸ΄μ„ μ‚¬μš©ν•΄ 일일히 화면을 그리닀 λ³΄λ‹ˆ νƒœκ·Έλ“€μ„ μž‘μ„±ν•˜λŠ”λ° ν—·κ°ˆλ¦¬λŠ” 뢀뢄이 λ§Žμ•„ κ³ μƒν–ˆμŠ΅λ‹ˆλ‹€. ν™•μž₯도ꡬλ₯Ό μ΄μš©ν•˜μ—¬ 더 νŽΈν•˜κ²Œ ν•  수 μžˆλ‹€λŠ” 것을 λ‚˜μ€‘μ— μ•Œκ²Œ λ˜μ—ˆμŠ΅λ‹ˆλ‹€. +- μ–΄λ–€ 값을 μƒμˆ˜λ‘œ 관리해야 ν•˜λŠ” μ§€ νŒλ‹¨ν•˜λŠ” 것이 μ–΄λ €μ› μŠ΅λ‹ˆλ‹€. ν•¨μˆ˜μ•ˆμ—μ„œλ§Œ μ‚¬μš©ν•˜κ³  μ˜λ―Έκ°€ λͺ…ν™•ν•΄ λ³΄μ΄λŠ” 값듀도 μƒμˆ˜ 처리 ν•΄μ•Ό ν•˜λŠ” μ§€ 의문이 λ“€μ—ˆμŠ΅λ‹ˆλ‹€. +- 상행쒅점과 ν•˜ν–‰μ’…μ μ˜ 값을 μ €μž₯ν•  λ•Œ ν•˜λ‚˜μ˜ 배열에 μ €μž₯ν•˜μ§€ μ•Šκ³  λ”°λ‘œ μ €μž₯ν–ˆμŠ΅λ‹ˆλ‹€. 그둜 인해 κ΅¬κ°„μ—μ„œ λͺ©λ‘μ„ μΆ”κ°€ν•˜κ³  μ‚­μ œν•  λ•Œ, 데이터 λ³€κ²½ν•˜λŠ” 뢀뢄이 λ„ˆλ¬΄ μ–΄λ €μ› μŠ΅λ‹ˆλ‹€. κ²°κ΅­ λ‹€μ‹œ λŒμ•„κ°€ 두 값듀을 ν•˜λ‚˜μ˜ λ°°μ—΄λ‘œ κ΄€λ¦¬ν•˜λ„λ‘ μˆ˜μ •ν•˜μ˜€κ³ , 문제λ₯Ό ν•΄κ²°ν•  수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€.
## πŸ’» ν”„λ‘œκ·Έλž¨ μ‹€ν–‰ κ²°κ³Ό ### 역관리 + ### 노선관리 + ### ꡬ간관리 + ### 노선도 좜λ ₯ - + ## βœ… ν”„λ‘œκ·Έλž˜λ° μš”κ΅¬μ‚¬ν•­ ### 메뉴 λ²„νŠΌ + - μ—­ 관리 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값을 κ°€μ§„λ‹€. @@ -71,6 +155,7 @@ - μ§€ν•˜μ²  노선을 μ‚­μ œν•˜λŠ” button νƒœκ·ΈλŠ” `.line-delete-button` class값을 κ°€μ§„λ‹€. ### μ§€ν•˜μ²  ꡬ간 μΆ”κ°€ κΈ°λŠ₯ + - μ§€ν•˜μ²  노선을 μ„ νƒν•˜λŠ” button νƒœκ·ΈλŠ” `.section-line-menu-button` class값을 κ°€μ§„λ‹€. - μ§€ν•˜μ²  ꡬ간을 μ„€μ •ν•  μ—­ select νƒœκ·ΈλŠ” `#section-station-selector` id값을 κ°€μ§„λ‹€. - μ§€ν•˜μ²  κ΅¬κ°„μ˜ μˆœμ„œλ₯Ό μž…λ ₯ν•˜λŠ” input νƒœκ·ΈλŠ” `#section-order-input` id값을 κ°€μ§„λ‹€. @@ -78,6 +163,7 @@ - μ§€ν•˜μ²  ꡬ간을 μ œκ±°ν•˜λŠ” button νƒœκ·ΈλŠ” `.section-delete-button` class값을 κ°€μ§„λ‹€. ### μ§€ν•˜μ²  노선도 좜λ ₯ κΈ°λŠ₯ + - μ§€ν•˜μ²  노선도 좜λ ₯ λ²„νŠΌμ„ λˆ„λ₯΄λ©΄ `
` νƒœκ·Έλ₯Ό λ§Œλ“€κ³  ν•΄λ‹Ή νƒœκ·Έ 내뢀에 노선도λ₯Ό 좜λ ₯ν•œλ‹€. ### κΈ°μ‘΄ μš”κ΅¬μ‚¬ν•­ @@ -101,7 +187,8 @@ - [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 νƒœκ·Έμ— μ—­, λ…Έμ„ , κ΅¬κ°„μ˜ μœ μΌν•œ 데이터 값듀을 κ΄€λ¦¬ν•œλ‹€. + +- [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)λ₯Ό μ΄μš©ν•˜μ—¬, μƒˆλ‘œκ³ μΉ¨ν•˜λ”λΌλ„ κ°€μž₯ μ΅œκ·Όμ— μž‘μ—…ν•œ 정보듀을 뢈러올 수 μžˆλ„λ‘ ν•œλ‹€.
diff --git a/index.html b/index.html index fc99deac2..f2318dd3c 100644 --- a/index.html +++ b/index.html @@ -3,10 +3,29 @@ μ§€ν•˜μ²  노선도 관리 + +
+

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

+ + + + +
-

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

diff --git a/src/config.js b/src/config.js new file mode 100644 index 000000000..aeb4b6135 --- /dev/null +++ b/src/config.js @@ -0,0 +1,23 @@ +import LineManager from "./pages/lineManager.js"; +import PrintSubwayLine from "./pages/PrintSubwayLine.js"; +import SectionManager from "./pages/SectionManager.js"; +import StationManager from "./pages/StationManager.js"; + +export const elementMap = [ + { + page: StationManager, + button: document.getElementById("station-manager-button"), + }, + { + page: LineManager, + button: document.getElementById("line-manager-button"), + }, + { + page: SectionManager, + button: document.getElementById("section-manager-button"), + }, + { + page: PrintSubwayLine, + button: document.getElementById("map-print-manager-button"), + }, +]; diff --git a/src/core/Component.js b/src/core/Component.js new file mode 100644 index 000000000..4fb87f321 --- /dev/null +++ b/src/core/Component.js @@ -0,0 +1,32 @@ +import { dispatchReRender } from "../utils/events.js"; + +export default class Component { + constructor() { + this.state = {}; + this.store = {}; + } + + setState(state) { + this.state = state; + dispatchReRender(); + } + + setStore(store) { + this.store = store; + localStorage.setItem("store", JSON.stringify(store)); + dispatchReRender(); + } + + create() { + const store = localStorage.getItem("store"); + if (store) { + this.store = JSON.parse(store); + } + } + + afterCreate() {} + + mount() {} + + render() {} +} diff --git a/src/index.js b/src/index.js index e69de29bb..dc01061c1 100644 --- a/src/index.js +++ b/src/index.js @@ -0,0 +1,20 @@ +import { dispatchReRender, RE_RENDER_EVENT } from "./utils/events.js"; +import { elementMap } from "./config.js"; +import reRenderPage from "./utils/reRenderPage.js"; + +function init() { + const app = document.querySelector("#app"); + let page; + + elementMap.forEach((element) => { + const managerPage = new element.page(); + element.button.addEventListener("click", () => { + page = managerPage; + dispatchReRender(); + }); + }); + + window.addEventListener(RE_RENDER_EVENT, () => reRenderPage(app, page)); +} + +init(); diff --git a/src/pages/LineManager.js b/src/pages/LineManager.js new file mode 100644 index 000000000..4425b6fa7 --- /dev/null +++ b/src/pages/LineManager.js @@ -0,0 +1,180 @@ +import Component from "../core/Component.js"; +import { ERROR } from "../utils/errors.js"; + +const elementMap = { + lineNameInput: "line-name-input", + lineStartStationSelector: "line-start-station-seletor", + lineEndStationSelector: "line-end-station-seletor", + lineAddButton: "line-add-button", + lineDeleteButton: "line-delete-button", +}; + +export default class LineManager extends Component { + constructor() { + super(); + this.state = { + lineName: "", + lineStartStation: "", + lineEndStation: "", + }; + + this.handleLineAddButton = () => { + const line = { + name: this.state.lineName, + stations: [this.state.lineStartStation, this.state.lineEndStation], + }; + const lines = this.store.lines ? [...this.store.lines, line] : [line]; + + if (isDuplicateLine(this.store.lines, this.state.lineName)) { + alert(ERROR.RE_TYPING_LINE); + } else { + this.setStore({ ...this.store, lines }); + } + }; + + this.handleLineDeleteButton = (index) => { + this.store.lines.splice(index, 1); + this.setStore({ ...this.store }); + }; + } + + afterCreate() { + const { lineStartStation, lineEndStation } = this.state; + const { stations } = this.store; + if (lineStartStation.length === 0) + this.state.lineStartStation = stations[0]; + if (lineEndStation.length === 0) this.state.lineEndStation = stations[0]; + } + + mount() { + this.mountNameInput(); + this.mountStartSelector(); + this.mountEndSelector(); + this.mountAddButton(); + this.mountDeleteButton(); + } + + mountNameInput() { + const lineNameInput = document.getElementById(elementMap.lineNameInput); + lineNameInput.addEventListener("blur", (event) => { + if (haveSpaceInLineName(event.target.value)) { + alert(ERROR.RE_TYPING_LINENAME); + return; + } + this.setState({ ...this.state, lineName: event.target.value }); + }); + } + + mountStartSelector() { + const lineStartStationSelector = document.getElementById( + elementMap.lineStartStationSelector + ); + lineStartStationSelector.addEventListener("change", (event) => { + this.setState({ ...this.state, lineStartStation: event.target.value }); + }); + } + + mountEndSelector() { + const lineEndStationSelector = document.getElementById( + elementMap.lineEndStationSelector + ); + lineEndStationSelector.addEventListener("change", (event) => { + this.setState({ ...this.state, lineEndStation: event.target.value }); + }); + } + + mountAddButton() { + const lineAddButton = document.getElementById(elementMap.lineAddButton); + lineAddButton.addEventListener("click", this.handleLineAddButton); + } + + mountDeleteButton() { + const lineDeleteButtons = document.getElementsByClassName( + elementMap.lineDeleteButton + ); + [...lineDeleteButtons].forEach((lineDeleteButton, index) => { + lineDeleteButton.addEventListener("click", () => { + this.handleLineDeleteButton(index); + }); + }); + } + + render() { + const { lines = [], stations = [] } = this.store; + const { lineName, lineStartStation, lineEndStation } = this.state; + return ` +
+

λ…Έμ„  이름

+ +
+
+

+ 상행 쒅점 + +

+

+ ν•˜ν–‰ 쒅점 + +

+
+
+ +
+
+

πŸš‰ μ§€ν•˜μ²  λ…Έμ„  λͺ©λ‘

+ + + + + + + + + + + ${lines + .map( + (line) => + ` + + + + + ` + ) + .join("")} + +
λ…Έμ„  이름상행 μ’…μ μ—­ν•˜ν–‰ 쒅점역섀정
${line.name}${line.stations[0]}${line.stations[1]} + +
+
+ `; + } +} + +function isDuplicateLine(lines = [], lineName) { + return lines.find((line) => line.name === lineName); +} + +function haveSpaceInLineName(lineName) { + return [...lineName].includes(" "); +} diff --git a/src/pages/PrintSubwayLine.js b/src/pages/PrintSubwayLine.js new file mode 100644 index 000000000..8113cd70f --- /dev/null +++ b/src/pages/PrintSubwayLine.js @@ -0,0 +1,33 @@ +import Component from "../core/Component.js"; + +export default class PrintSubwayLine extends Component { + constructor() { + super(); + } + + render() { + const { lines } = this.store; + return ` +
+ ${lines + .map( + (line) => ` +
+

${line.name}

+ +
+ ` + ) + .join("")} +
+ `; + } +} diff --git a/src/pages/SectionManager.js b/src/pages/SectionManager.js new file mode 100644 index 000000000..fca148a9f --- /dev/null +++ b/src/pages/SectionManager.js @@ -0,0 +1,164 @@ +import Component from "../core/Component.js"; +import { ERROR } from "../utils/errors.js"; + +const elementMap = { + sectionLineMenuButton: "section-line-menu-button", + sectionStationSelector: "section-station-selector", + sectionOrderInput: "section-order-input", + sectionAddButton: "section-add-button", + sectionDeleteButton: "section-delete-button", +}; + +export default class SectionManager extends Component { + constructor() { + super(); + this.state = { + selectedLine: null, + }; + + this.handleLineButtonClick = (index) => { + this.setState({ selectedLine: index }); + }; + + this.handleAddButtonClick = (order, station) => { + if (order.length === 0) { + alert(ERROR.RE_TYPING_ORDER); + return; + } + + const { selectedLine } = this.state; + this.store.lines[selectedLine].stations.splice(order, 0, station); + this.setStore(cloneDeep(this.store)); + }; + + this.handleDeleteButtonClick = (index) => { + const isLessThanTwoStation = stations.length < 2; + if (isLessThanTwoStation) { + alert(ERROR.NOT_DELETE); + return; + } + + const stations = this.store.lines[this.state.selectedLine].stations; + stations.splice(index, 1); + this.setStore(cloneDeep(this.store)); + }; + } + + mount() { + const sectionStationSelector = document.getElementById( + elementMap.sectionStationSelector + ); + const sectionOrderInput = document.getElementById( + elementMap.sectionOrderInput + ); + this.mountLineMenuButton(); + this.mountAddButton(sectionOrderInput, sectionStationSelector); + this.mountDeleteButton(); + } + + mountLineMenuButton() { + const sectionLineMenuButtons = document.getElementsByClassName( + elementMap.sectionLineMenuButton + ); + [...sectionLineMenuButtons].forEach((button, index) => { + button.addEventListener("click", () => { + this.handleLineButtonClick(index); + }); + }); + } + + mountAddButton(sectionOrderInput, sectionStationSelector) { + const sectionAddButton = document.getElementById( + elementMap.sectionAddButton + ); + sectionAddButton?.addEventListener("click", () => { + this.handleAddButtonClick( + sectionOrderInput.value, + sectionStationSelector.value + ); + }); + } + + mountDeleteButton() { + const sectionDeleteButtons = document.getElementsByClassName( + elementMap.sectionDeleteButton + ); + sectionDeleteButtons && + [...sectionDeleteButtons].forEach((button, index) => { + button.addEventListener("click", () => { + this.handleDeleteButtonClick(index); + }); + }); + } + + render() { + const { stations = [], lines = [] } = this.store; + const { selectedLine } = this.state; + const Section = () => ` +
+

${lines[selectedLine]?.name} 관리

+

ꡬ간 등둝

+ + + +
+ + + + + + + + + + ${ + lines[selectedLine]?.stations + .map( + (station, index) => + ` + + + + ` + ) + .join("") || "" + } + +
μˆœμ„œμ΄λ¦„μ„€μ •
${index}${station} + +
+ `; + + return ` +
+

ꡬ간을 μˆ˜μ •ν•  노선을 μ„ νƒν•΄μ£Όμ„Έμš”.

+ ${lines + .map( + (line) => + `` + ) + .join("")} +
+ ${isNull(selectedLine) ? "" : Section()} + `; + } +} + +function isNull(value) { + return String(value) === "null" || String(value) === "undefined"; +} + +function cloneDeep(value) { + return JSON.parse(JSON.stringify(value)); +} diff --git a/src/pages/StationManager.js b/src/pages/StationManager.js new file mode 100644 index 000000000..3c92f3adb --- /dev/null +++ b/src/pages/StationManager.js @@ -0,0 +1,129 @@ +import Component from "../core/Component.js"; +import { ERROR } from "../utils/errors.js"; + +const elementMap = { + stationNameInput: "station-name-input", + stationAddButton: "station-add-button", + stationDeleteButton: "station-delete-button", +}; + +export default class StationManager extends Component { + constructor() { + super(); + + this.handleAddButtonClick = (newStation) => { + if ( + isMoreThanTwoWords(newStation) && + isNotDuplicateSatationName(newStation, this.store.stations) && + hasNotSpaceInStationName(newStation) && + isOnlyWord(newStation) + ) { + const stations = this.store.stations + ? [...this.store.stations, newStation] + : [newStation]; + this.setStore({ ...this.store, stations }); + } else { + alert(ERROR.RE_TYPING_STATION); + } + }; + + this.handleDeleteButtonClick = (index) => { + this.store.stations.splice(index, 1); + this.setStore({ ...this.store }); + }; + } + + mount() { + const stationNameInput = document.getElementById( + elementMap.stationNameInput + ); + this.mountAddButton(stationNameInput); + this.mountDeleteButton(); + } + + mountAddButton(stationNameInput) { + const stationAddButton = document.getElementById( + elementMap.stationAddButton + ); + stationAddButton.addEventListener("click", () => { + this.handleAddButtonClick(stationNameInput.value); + }); + } + + mountDeleteButton() { + const stationDeleteButtons = document.getElementsByClassName( + elementMap.stationDeleteButton + ); + [...stationDeleteButtons].forEach((stationDeleteButton, index) => { + stationDeleteButton.addEventListener("click", () => { + this.handleDeleteButtonClick(index); + }); + }); + } + + render() { + const { stations = [] } = this.store; + return ` +
+

μ—­ 이름

+
+ + +
+
+

πŸš‰ μ§€ν•˜μ²  μ—­ λͺ©λ‘

+ + + + + + + + + ${stations + .map( + (station) => + ` + + + + ` + ) + .join("")} + +
μ—­ 이름섀정
${station} + +
+
+
+ `; + } +} + +function isMoreThanTwoWords(stationName) { + return stationName.length > 1; +} + +function isNotDuplicateSatationName(stationName, stations = []) { + return !stations.includes(stationName); +} + +function hasNotSpaceInStationName(stationName) { + return ![...stationName].includes(" "); +} + +function isOnlyWord(stationName) { + const check_num = /[0-9]/; + const check_eng = /[a-zA-Z]/; + const check_spc = /[~!@#$%^&*()_+|<>?:{}]/; + const check_kor = /[γ„±-γ…Ž|ㅏ-γ…£|κ°€-힣]/; + + return ( + check_kor.test(stationName) && + !check_eng.test(stationName) && + !check_num.test(stationName) && + !check_spc.test(stationName) + ); +} diff --git a/src/utils/errors.js b/src/utils/errors.js new file mode 100644 index 000000000..e1000f94b --- /dev/null +++ b/src/utils/errors.js @@ -0,0 +1,7 @@ +export const ERROR = { + RE_TYPING_STATION: "μ—­ 이름을 λ‹€μ‹œ μž…λ ₯ν•΄ μ£Όμ„Έμš”", + RE_TYPING_LINE: "μ€‘λ³΅λœ 노선이 μ‘΄μž¬ν•©λ‹ˆλ‹€", + RE_TYPING_LINENAME: "λ…Έμ„  이름에 곡백이 μ‘΄μž¬ν•©λ‹ˆλ‹€", + RE_TYPING_ORDER: "μˆœμ„œλ₯Ό μž…λ ₯ν•˜μ„Έμš”", + NOT_DELETE: "2개 μ΄ν•˜λŠ” μ§€μšΈ 수 μ—†μŠ΅λ‹ˆλ‹€", +}; diff --git a/src/utils/events.js b/src/utils/events.js new file mode 100644 index 000000000..94eb7023b --- /dev/null +++ b/src/utils/events.js @@ -0,0 +1,5 @@ +export { dispatchReRender, RE_RENDER_EVENT, reRenderEvent }; + +const RE_RENDER_EVENT = "customRender"; +const reRenderEvent = new CustomEvent(RE_RENDER_EVENT); +const dispatchReRender = () => window.dispatchEvent(reRenderEvent); diff --git a/src/utils/reRenderPage.js b/src/utils/reRenderPage.js new file mode 100644 index 000000000..b7fead00d --- /dev/null +++ b/src/utils/reRenderPage.js @@ -0,0 +1,6 @@ +export default function reRenderPage(app, page) { + page.create(); + page.afterCreate(); + app.innerHTML = page.render(); + page.mount(); +}