diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..e66cbb303 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +git-commit-template.txt +node_modules +package.json +package-lock.json +.eslintrc.js \ No newline at end of file diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..0448900b1 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,61 @@ +## 기능 구현 목록 + +### 화면 출력 + +- ✔ 버튼을 누르면 버튼에 맞는 화면을 출력한다. + +### localStorage + +- ✔ localStorage를 사용해 새로고침하더라도 이전에 작업한 정보를 불러온다. + +### data + +- ✔ data속성을 활용해 역, 노선, 구간의 데이터 값을 관리한다. + +### 역 관리 + +- ✔ 역을 추가할 수 있고, 화면에 추가한다. + + - 예외처리) + - ✔ 중복 불가 + - ✔ 2글자 미만 불가 + - ✔ 한글과 숫자만 가능(기능 추가) + +- ✔ 역을 삭제할 수 있고, 화면에서 삭제한다. + - 예외처리) + - ✔ 노선에 등록된 역 삭제 불가 + +### 노선 관리 + +- ✔ 존재하는 역을 통해 상행과 하행 종점을 선택할 수 있고, 이름을 정해서 노선을 추가할 수 있으며, 화면에 추가한다. + + - 예외처리) + - ✔ 노선 이름 중복 불가 + - ✔ 한글과 숫자만 가능(기능 추가) + - ✔ 2글자 미만 불가(기능 추가) + - ✔ 한 노선에서 중복된 역 입력 불가(기능 추가) + +- ✔ 노선을 삭제할 수 있고, 화면에서 삭제한다. + +### 구간 관리 + +- ✔ 존재하는 노선을 선택할 수 있고, 선택 시 화면에 출력한다. + +- ✔ 노선에 존재하는 역을 추가할 수 있으며, 순서를 정할 수 있다. + - 예외처리) + - ✔ 같은 노선에 중복된 역 추가 불가 + - ✔ 노선 내에서 갈래길이 존재할 수 없음 + - ✔ 가장 큰 순서보다 큰 수 입력 불가 + - ✔ 0부터 입력가능 +- ✔ 이때 순서에 맞게 화면이 랜더링된다. +- ✔ 또한, 순서 0이 입력되면, 상행 종점이 된다. + +- ✔ 노선에서 역을 삭제할 수 있으며, 화면에서 삭제한다. + + - 예외처리) + - ✔ 한 노선에서 역이 두 개 이하일 때 삭제 불가 + - ✔ 이때, 종점을 삭제하면 종점 전의 역이 종점이 되고, 화면을 랜더링한다. + +### 노선도 출력 + +- ✔ 노선 별 상행 종점부터 하행 종점까지 연결된 순서대로 역 목록을 화면에 출력한다. diff --git a/index.html b/index.html index fc99deac2..bf34b9a8b 100644 --- a/index.html +++ b/index.html @@ -6,7 +6,73 @@
-

🚇 지하철 노선도 관리

+

🚇 지하철 노선도 관리

+ + + + + + + +
diff --git a/src/Controller/line-control.js b/src/Controller/line-control.js new file mode 100644 index 000000000..e7685d7d3 --- /dev/null +++ b/src/Controller/line-control.js @@ -0,0 +1,81 @@ +import { + $lineNameInput, + $upStreamSelector, + $downStreamSelector, +} from '../View/element.js'; +import { + addLineScreen, + addMapPrint, + addSectionButton, + addSectionScreen, + addSelectorOption, +} from '../View/add-screen.js'; +import { + removeLineScreen, + removeSectionButton, + removeSectionScreen, + removeMapPrint, +} from '../View/remove-screen.js'; +import {hideSectionEditContainer} from '../View/hide-screen.js'; +import { + setLocalStorage, + getLocalStorage, + removeLocalStorage, +} from './local-storage.js'; +import {stationInstance, lineInstance} from '../index.js'; +import { + setLineButtonDeleteEvent, + setSectionButtonLoadEvent, + setSectionButtonDeleteEvent, +} from './set-button-event.js'; +import {isLineInputValid} from './valid.js'; +import {KEY, TEXT} from '../constant.js'; + +export const onAddLine = () => { + const lineValue = getLineValue(); + if (isLineInputValid(lineValue, lineInstance.lines)) { + setLocalStorage(KEY.LINE, lineValue); + lineInstance.addLine(lineValue); + addLineScreen(lineValue); + addSectionButton(lineValue.lineName); + addSectionScreen(lineValue); + addMapPrint([lineValue]); + setSectionButtonDeleteEvent(lineValue); + setLineButtonDeleteEvent(lineValue.lineName); + setSectionButtonLoadEvent(lineValue.lineName); + } + $lineNameInput.value = ''; +}; + +export const onRemoveLine = (e) => { + const removeConfirm = confirm(TEXT.CONFIRM_DELETE); + if (removeConfirm) { + removeLocalStorage(KEY.LINE, e.target.dataset.lineName); + lineInstance.removeLine(e.target.dataset.lineName); + removeLineScreen(e.target); + removeSectionButton(e.target.dataset.lineName); + removeSectionScreen(e.target.dataset.lineName); + removeMapPrint(e.target.dataset.lineName); + hideSectionEditContainer(); + } +}; + +export const loadLine = () => { + const lines = getLocalStorage(KEY.LINE); + lineInstance.loadLine(lines); + stationInstance.stations.forEach((station) => { + addSelectorOption($upStreamSelector, station); + addSelectorOption($downStreamSelector, station); + }); + lineInstance.lines.forEach((line) => { + addLineScreen(line); + setLineButtonDeleteEvent(line.lineName); + }); +}; + +const getLineValue = () => { + return { + lineName: $lineNameInput.value, + station: [$upStreamSelector.value, $downStreamSelector.value], + }; +}; diff --git a/src/Controller/local-storage.js b/src/Controller/local-storage.js new file mode 100644 index 000000000..3b0f52136 --- /dev/null +++ b/src/Controller/local-storage.js @@ -0,0 +1,59 @@ +import {KEY} from '../constant.js'; + +export const addSectionOnLocalStorage = (key, value) => { + const localStorageValue = getLocalStorage(key); + const sectionIndex = localStorageValue.findIndex( + (line) => line.lineName === value.lineName, + ); + localStorageValue[sectionIndex].station.splice( + value.number, + 0, + value.sectionName, + ); + + return localStorage.setItem(key, JSON.stringify([...localStorageValue])); +}; + +export const setLocalStorage = (key, value) => { + const localStorageValue = getLocalStorage(key); + if (localStorageValue === null) { + return localStorage.setItem(key, JSON.stringify([value])); + } + + return localStorage.setItem( + key, + JSON.stringify([...localStorageValue, value]), + ); +}; + +export const getLocalStorage = (key) => { + const localStorageValue = JSON.parse(localStorage.getItem(key)); + + return localStorageValue; +}; + +export const removeSectionOnLocalStorage = (key, value) => { + const localStorageValue = getLocalStorage(key); + const sectionIndex = localStorageValue.findIndex( + (line) => line.lineName === value.lineName, + ); + localStorageValue[sectionIndex].station = localStorageValue[ + sectionIndex + ].station.filter((station) => station !== value.station); + + return localStorage.setItem(key, JSON.stringify([...localStorageValue])); +}; + +export const removeLocalStorage = (key, value) => { + const localStorageValue = getLocalStorage(key); + const filteredStorage = localStorageValue.filter((storage) => { + if (key === KEY.STATION) { + return storage !== value; + } + if (key === KEY.LINE) { + return storage.lineName !== value; + } + }); + + return localStorage.setItem(key, JSON.stringify(filteredStorage)); +}; diff --git a/src/Controller/map-print-control.js b/src/Controller/map-print-control.js new file mode 100644 index 000000000..545600dc5 --- /dev/null +++ b/src/Controller/map-print-control.js @@ -0,0 +1,7 @@ +import {addMapPrint} from '../View/add-screen.js'; +import {lineInstance} from '../index.js'; + +export const loadMapPrint = () => { + lineInstance.loadLine(); + addMapPrint(lineInstance.lines); +}; diff --git a/src/Controller/section-control.js b/src/Controller/section-control.js new file mode 100644 index 000000000..034e01118 --- /dev/null +++ b/src/Controller/section-control.js @@ -0,0 +1,117 @@ +import { + $sectionAddButton, + $sectionOrderInput, + $sectionSelector, + $sectionEditContainer, + $lineContainer, +} from '../View/element.js'; +import { + addLineTitle, + addSelectorOption, + addSectionScreen, + addSectionButton, + addLineScreen, + addMapPrint, +} from '../View/add-screen.js'; +import {removeAllMapPrint, removeTableScreen} from '../View/remove-screen.js'; +import {showSectionScreen} from '../View/show-screen.js'; +import {hideSectionLine} from '../View/hide-screen.js'; +import {isSectionValid, isMoreThanTwoStation} from './valid.js'; +import { + addSectionOnLocalStorage, + removeSectionOnLocalStorage, +} from './local-storage.js'; +import {stationInstance, lineInstance} from '../index.js'; +import { + setLineButtonDeleteEvent, + setSectionButtonDeleteEvent, + setSectionButtonLoadEvent, +} from './set-button-event.js'; +import {KEY, TEXT} from '../constant.js'; + +export const onLoadSection = (e) => { + hideSectionLine(); + addLineTitle(e.target.dataset.lineName); + showSectionScreen(e.target.dataset.lineName); + $sectionAddButton.dataset.lineName = e.target.dataset.lineName; +}; + +export const onAddSection = (e) => { + const sectionValue = getSectionValue(e.target.dataset.lineName); + const selectedSection = getSelectedSection(sectionValue.lineName); + if (isSectionValid(sectionValue, selectedSection.station)) { + addSectionOnLocalStorage(KEY.LINE, sectionValue); + lineInstance.updateAddLine(selectedSection, sectionValue); + updateSectionTable(); + showSectionScreen(e.target.dataset.lineName); + } + $sectionOrderInput.value = ''; +}; + +export const onRemoveSection = (e) => { + const removeConfirm = confirm(TEXT.CONFIRM_DELETE); + const parsedData = JSON.parse(e.target.dataset.sectionLine); + const removedData = getRemovedData(parsedData); + const selectedSection = getSelectedSection(removedData.lineName); + if (removeConfirm && isMoreThanTwoStation(selectedSection.station)) { + removeSectionOnLocalStorage(KEY.LINE, removedData); + lineInstance.updateRemoveLine(selectedSection, parsedData.station); + updateSectionTable(); + showSectionScreen(parsedData.lineName); + } +}; + +export const loadSectionTable = () => { + lineInstance.loadLine(); + stationInstance.stations.forEach((station) => + addSelectorOption($sectionSelector, station), + ); + lineInstance.lines.forEach((line) => { + addSectionScreen(line); + setSectionButtonLoadEvent(line.lineName); + setSectionButtonDeleteEvent(line); + }); +}; + +export const loadSectionButton = () => { + lineInstance.loadLine(); + lineInstance.lines.forEach((line) => { + addSectionButton(line.lineName); + }); +}; + +export const updateSectionTable = () => { + removeTableScreen($sectionEditContainer); + removeTableScreen($lineContainer); + removeAllMapPrint(); + lineInstance.lines.forEach((line) => { + addLineScreen(line); + addSectionScreen(line); + setLineButtonDeleteEvent(line.lineName); + setSectionButtonDeleteEvent(line); + }); + addMapPrint(lineInstance.lines); +}; + +const getRemovedData = (data) => { + return { + lineName: data.lineName, + station: data.station, + }; +}; + +const getSectionValue = (lineName) => { + return { + lineName, + sectionName: $sectionSelector.value, + number: $sectionOrderInput.value, + }; +}; + +const getSelectedSection = (lineName) => { + const sectionIndex = lineInstance.lines.findIndex( + (line) => line.lineName === lineName, + ); + + return lineInstance.lines[sectionIndex]; +}; diff --git a/src/Controller/set-button-event.js b/src/Controller/set-button-event.js new file mode 100644 index 000000000..f960ca7c5 --- /dev/null +++ b/src/Controller/set-button-event.js @@ -0,0 +1,38 @@ +import { + $stationTbody, + $lineTbody, + $sectionEditButtonContainer, + $sectionTbody, +} from '../View/element.js'; +import {onRemoveStation} from './station-control.js'; +import {onRemoveLine} from './line-control.js'; +import {onLoadSection, onRemoveSection} from './section-control.js'; + +export const setStationButtonDeleteEvent = (stationName) => { + const $button = $stationTbody.querySelector( + `[data-station-name='${stationName}']`, + ); + $button.addEventListener('click', onRemoveStation); +}; + +export const setLineButtonDeleteEvent = (lineName) => { + const $button = $lineTbody.querySelector(`[data-line-name='${lineName}']`); + $button.addEventListener('click', onRemoveLine); +}; + +export const setSectionButtonLoadEvent = (lineName) => { + const $button = $sectionEditButtonContainer.querySelector( + `[data-line-name='${lineName}']`, + ); + $button.addEventListener('click', onLoadSection); +}; + +export const setSectionButtonDeleteEvent = (line) => { + const $lineAllTr = $sectionTbody.querySelectorAll( + `[data-line-name='${line.lineName}']`, + ); + + return $lineAllTr.forEach(($tr) => { + $tr.querySelector('button').addEventListener('click', onRemoveSection); + }); +}; diff --git a/src/Controller/station-control.js b/src/Controller/station-control.js new file mode 100644 index 000000000..4b1f700bf --- /dev/null +++ b/src/Controller/station-control.js @@ -0,0 +1,57 @@ +import { + $stationNameInput, + $upStreamSelector, + $downStreamSelector, + $sectionSelector, +} from '../View/element.js'; +import {addStationScreen, addSelectorOption} from '../View/add-screen.js'; +import { + removeStationScreen, + removeSelectorOption, +} from '../View/remove-screen.js'; +import { + setLocalStorage, + getLocalStorage, + removeLocalStorage, +} from './local-storage.js'; +import {isNotLineHaved, isStationInputVaild} from './valid.js'; +import {stationInstance, lineInstance} from '../index.js'; +import {setStationButtonDeleteEvent} from './set-button-event.js'; +import {KEY, TEXT} from '../constant.js'; + +export const loadStation = () => { + const stations = getLocalStorage(KEY.STATION); + stationInstance.loadStation(stations); + stationInstance.stations.forEach((stationName) => { + addStationScreen(stationName); + setStationButtonDeleteEvent(stationName); + }); +}; + +export const onAddStation = () => { + if (isStationInputVaild($stationNameInput.value, stationInstance.stations)) { + setLocalStorage(KEY.STATION, $stationNameInput.value); + stationInstance.addStation($stationNameInput.value); + addStationScreen($stationNameInput.value); + addSelectorOption($upStreamSelector, $stationNameInput.value); + addSelectorOption($downStreamSelector, $stationNameInput.value); + addSelectorOption($sectionSelector, $stationNameInput.value); + setStationButtonDeleteEvent($stationNameInput.value); + } + $stationNameInput.value = ''; +}; + +export const onRemoveStation = (e) => { + const removeConfirm = confirm(TEXT.CONFIRM_DELETE); + if ( + removeConfirm && + isNotLineHaved(e.target.dataset.stationName, lineInstance.lines) + ) { + removeLocalStorage(KEY.STATION, e.target.dataset.stationName); + stationInstance.removeStation(e.target.dataset.stationName); + removeStationScreen(e.target); + removeSelectorOption($upStreamSelector, e.target.dataset.stationName); + removeSelectorOption($downStreamSelector, e.target.dataset.stationName); + removeSelectorOption($sectionSelector, e.target.dataset.stationName); + } +}; diff --git a/src/Controller/valid.js b/src/Controller/valid.js new file mode 100644 index 000000000..ba5e85660 --- /dev/null +++ b/src/Controller/valid.js @@ -0,0 +1,115 @@ +import {ERROR_MESSAGE, MIN_LETTER, MIN_LINE_LENGTH} from '../constant.js'; + +export const isStationInputVaild = (station, exStation) => { + if (!isMatched(station)) { + return false; + } + if (!isAllDifferentStation(station, exStation)) { + return false; + } + + return true; +}; + +export const isLineInputValid = (userLine, exLine) => { + if (!isMatched(userLine.lineName)) { + return false; + } + if (!isAllDifferentLine(userLine.lineName, exLine)) { + return false; + } + if (!isLastStopDifferent(userLine.station)) { + return false; + } + + return true; +}; + +export const isSectionValid = (userSection, exSection) => { + if (!isNumber(userSection.number)) { + return false; + } + if (!isAllDifferentSection(userSection, exSection)) { + return false; + } + if (!isMoreThanMaxNumber(userSection, exSection)) { + return false; + } + + return true; +}; + +export const isNotLineHaved = (station, lines) => { + if (lines.find((line) => line.station.includes(station))) { + return alert(ERROR_MESSAGE.LINE_HAVE_SAME_STATION); + } + + return true; +}; + +export const isMoreThanTwoStation = (line) => { + if (line.length <= MIN_LINE_LENGTH) { + return alert(ERROR_MESSAGE.LESS_THAN_TWO_STATION); + } + + return true; +}; + +const isMatched = (value) => { + if (value.match(/[^가-힣0-9]/)) { + return alert(ERROR_MESSAGE.WRONG_INPUT); + } + if (value.length < MIN_LETTER) { + return alert(ERROR_MESSAGE.LESS_THAN_ONE_LETTER); + } + + return true; +}; + +const isAllDifferentStation = (station, allStations) => { + if (allStations && allStations.includes(station)) { + return alert(ERROR_MESSAGE.SAME_STATION); + } + + return true; +}; + +const isAllDifferentLine = (userLine, allLines) => { + if (allLines.find((line) => line.lineName === userLine)) { + return alert(ERROR_MESSAGE.SAME_LINE); + } + + return true; +}; + +const isAllDifferentSection = (userSection, allSection) => { + if (allSection.find((station) => station === userSection.sectionName)) { + return alert(ERROR_MESSAGE.ALREADY_INCLUDE_STATION); + } + + return true; +}; + +const isLastStopDifferent = (station) => { + if (station[0] === station[station.length - 1]) { + return alert(ERROR_MESSAGE.SAME_LAST_STOP); + } + + return true; +}; + +const isNumber = (number) => { + if (number.match(/\D/) || number === '') { + return alert(ERROR_MESSAGE.NOT_NUMBER); + } + + return true; +}; + +const isMoreThanMaxNumber = (userSection, section) => { + if (section.length < userSection.number) { + return alert(`❌ ${section.length}${ERROR_MESSAGE.LENGTH_LIMIT}`); + } + + return true; +}; diff --git a/src/Model/line.js b/src/Model/line.js new file mode 100644 index 000000000..8eec3bacb --- /dev/null +++ b/src/Model/line.js @@ -0,0 +1,39 @@ +export default class Line { + constructor() { + this.lines = []; + } + + loadLine(lines) { + if (lines) { + return (this.lines = lines); + } + } + + addLine(line) { + return this.lines.push(line); + } + + removeLine(lineName) { + const removedLineIndex = this.lines.findIndex( + (v) => v.lineName === lineName, + ); + + return this.lines.splice(removedLineIndex, 1); + } + + updateAddLine(selectedLine, lineValue) { + return selectedLine.station.splice( + lineValue.number, + 0, + lineValue.sectionName, + ); + } + + updateRemoveLine(selectedLine, removedSectionName) { + const removedSectionIndex = selectedLine.station.indexOf( + removedSectionName, + ); + + return selectedLine.station.splice(removedSectionIndex, 1); + } +} diff --git a/src/Model/station.js b/src/Model/station.js new file mode 100644 index 000000000..add85cc11 --- /dev/null +++ b/src/Model/station.js @@ -0,0 +1,21 @@ +export default class Station { + constructor() { + this.stations = []; + } + + loadStation(stations) { + if (stations) { + return (this.stations = stations); + } + } + + addStation(stationName) { + return this.stations.push(stationName); + } + + removeStation(stationName) { + const removedStationIndex = this.stations.indexOf(stationName); + + return this.stations.splice(removedStationIndex, 1); + } +} diff --git a/src/View/add-screen.js b/src/View/add-screen.js new file mode 100644 index 000000000..9af1ad441 --- /dev/null +++ b/src/View/add-screen.js @@ -0,0 +1,103 @@ +import { + $stationTbody, + $lineTbody, + $sectionEditButtonContainer, + $sectionTbody, + $sectionEditContainer, + $mapContainer, +} from './element.js'; +import {TEXT, ADD_BUTTON_CLASS} from '../constant.js'; + +const { + STATION_DELETE, + LINE_DELETE, + SECTION_DELETE, + SECTION_LINE_MENU, +} = ADD_BUTTON_CLASS; + +const {DELETE, LINE_MANAGEMENT, DELETE_FROM_LINE} = TEXT; + +export const addStationScreen = (stationName) => { + const $stationTr = document.createElement('tr'); + $stationTr.innerHTML = ` + ${stationName} + + `; + $stationTr.querySelector('button').dataset.stationName = stationName; + $stationTbody.appendChild($stationTr); +}; + +export const addSelectorOption = ($selector, stationName) => { + const $optionLastStopStation = document.createElement('option'); + $optionLastStopStation.textContent = stationName; + $selector.appendChild($optionLastStopStation); +}; + +export const addLineScreen = (line) => { + const $lineTr = document.createElement('tr'); + $lineTr.innerHTML = ` + ${line.lineName} + ${line.station[0]} + ${line.station[line.station.length - 1]} + + `; + $lineTr.querySelector('button').dataset.lineName = line.lineName; + $lineTbody.appendChild($lineTr); +}; + +export const addLineTitle = (lineName) => { + const $lineTitle = $sectionEditContainer.querySelector('h2'); + $lineTitle.textContent = `${lineName} ${LINE_MANAGEMENT}`; +}; + +export const addSectionScreen = (line) => { + for (let i = 0; i < line.station.length; i++) { + const $sectionTr = document.createElement('tr'); + $sectionTr.innerHTML = ` + ${i} + ${line.station[i]} + + `; + const sectionData = {lineName: line.lineName, station: line.station[i]}; + setSectionData($sectionTr, sectionData); + $sectionTbody.appendChild($sectionTr); + } +}; + +const setSectionData = ($sectionTr, sectionData) => { + $sectionTr.querySelector('button').dataset.sectionLine = JSON.stringify( + sectionData, + ); + $sectionTr.dataset.lineName = sectionData.lineName; + $sectionTr.style.display = 'none'; +}; + +export const addSectionButton = (lineName) => { + const $sectionButton = document.createElement('button'); + $sectionButton.className = SECTION_LINE_MENU; + $sectionButton.textContent = lineName; + $sectionButton.dataset.lineName = lineName; + $sectionEditButtonContainer.appendChild($sectionButton); +}; + +export const addMapPrint = (lines) => { + for (let i = 0; i < lines.length; i++) { + const $lineMap = document.createElement('div'); + const $lineTitle = document.createElement('h3'); + const $lineUl = document.createElement('ul'); + $lineMap.dataset.lineName = lines[i].lineName; + $lineTitle.textContent = lines[i].lineName; + addMapStation($lineUl, lines[i].station); + $lineMap.appendChild($lineTitle); + $lineMap.appendChild($lineUl); + $mapContainer.appendChild($lineMap); + } +}; + +const addMapStation = ($ul, stations) => { + stations.forEach((stationName) => { + const $stationLi = document.createElement('li'); + $stationLi.textContent = stationName; + $ul.appendChild($stationLi); + }); +}; diff --git a/src/View/element.js b/src/View/element.js new file mode 100644 index 000000000..776e463b4 --- /dev/null +++ b/src/View/element.js @@ -0,0 +1,41 @@ +export const $screenAllButton = document.body.querySelectorAll('#app > button'); + +export const $stationContainer = document.body.querySelector('#station'); +export const $stationTbody = $stationContainer.querySelector('table > tbody'); +export const $stationNameInput = $stationContainer.querySelector( + '#station-name-input', +); +export const $stationAddButton = $stationContainer.querySelector( + '#station-add-button', +); + +export const $lineContainer = document.body.querySelector('#line'); +export const $lineTbody = $lineContainer.querySelector('table > tbody'); +export const $upStreamSelector = $lineContainer.querySelector( + '#line-start-station-selector', +); +export const $downStreamSelector = $lineContainer.querySelector( + '#line-end-station-selector', +); +export const $lineAddButton = $lineContainer.querySelector('#line-add-button'); +export const $lineNameInput = $lineContainer.querySelector('#line-name-input'); + +export const $sectionContainer = document.body.querySelector('#subway-section'); +export const $sectionTbody = $sectionContainer.querySelector('table > tbody'); +export const $sectionEditButtonContainer = $sectionContainer.querySelector( + '#section-select-button', +); +export const $sectionEditContainer = $sectionContainer.querySelector( + '#subway-section-edit', +); +export const $sectionAddButton = $sectionContainer.querySelector( + '#section-add-button', +); +export const $sectionOrderInput = $sectionContainer.querySelector( + '#section-order-input', +); +export const $sectionSelector = $sectionContainer.querySelector( + '#section-station-selector', +); + +export const $mapContainer = document.body.querySelector('#map-section'); diff --git a/src/View/hide-screen.js b/src/View/hide-screen.js new file mode 100644 index 000000000..0ed44ff1e --- /dev/null +++ b/src/View/hide-screen.js @@ -0,0 +1,17 @@ +import {$sectionContainer, $sectionEditContainer} from './element.js'; + +export const hideScreen = () => { + const $allSection = document.body.querySelectorAll('section'); + $allSection.forEach((section) => (section.style.display = 'none')); +}; + +export const hideSectionLine = () => { + const $sectionLines = $sectionContainer.querySelectorAll('tr'); + for (let i = 1; i < $sectionLines.length; i++) { + $sectionLines[i].style.display = 'none'; + } +}; + +export const hideSectionEditContainer = () => { + $sectionEditContainer.style.display = 'none'; +}; diff --git a/src/View/remove-screen.js b/src/View/remove-screen.js new file mode 100644 index 000000000..2ce68533d --- /dev/null +++ b/src/View/remove-screen.js @@ -0,0 +1,68 @@ +import { + $stationTbody, + $lineTbody, + $sectionEditButtonContainer, + $mapContainer, + $sectionTbody, +} from './element.js'; + +export const removeStationScreen = (button) => { + const $stationTr = button.parentElement.parentElement; + $stationTbody.removeChild($stationTr); +}; + +export const removeSelectorOption = ($stationSelector, stationName) => { + const $stationSelectorOptions = $stationSelector.querySelectorAll('option'); + const removedStationOption = Array.from($stationSelectorOptions).find( + (option) => option.value === stationName, + ); + $stationSelector.removeChild(removedStationOption); +}; + +export const removeLineScreen = (button) => { + const $lineTr = button.parentElement.parentElement; + $lineTbody.removeChild($lineTr); +}; + +export const removeTableScreen = ($container) => { + const $tbody = $container.querySelector('table > tbody'); + const $tr = $container.querySelectorAll('tr'); + for (let i = 1; i < $tr.length; i++) { + $tbody.removeChild($tr[i]); + } +}; + +export const removeSectionScreen = (lineName) => { + const $allSectionTr = $sectionTbody.querySelectorAll('tr'); + const $allDeletedSectionTr = Array.from($allSectionTr).filter( + (tr) => tr.dataset.lineName === lineName, + ); + + for (let i = 0; i < $allDeletedSectionTr.length; i++) { + $sectionTbody.removeChild($allDeletedSectionTr[i]); + } +}; + +export const removeSectionButton = (lineName) => { + const $sectionAllLineButton = $sectionEditButtonContainer.querySelectorAll( + 'button', + ); + const $removedSectionButton = Array.from($sectionAllLineButton).find( + (button) => button.textContent === lineName, + ); + $sectionEditButtonContainer.removeChild($removedSectionButton); +}; + +export const removeMapPrint = (lineName) => { + const $mapAllLines = $mapContainer.querySelectorAll('div'); + const $removedMapLine = Array.from($mapAllLines).find( + (map) => map.dataset.lineName === lineName, + ); + $mapContainer.removeChild($removedMapLine); +}; + +export const removeAllMapPrint = () => { + while ($mapContainer.firstChild) { + $mapContainer.removeChild($mapContainer.firstChild); + } +}; diff --git a/src/View/show-screen.js b/src/View/show-screen.js new file mode 100644 index 000000000..ee07d152d --- /dev/null +++ b/src/View/show-screen.js @@ -0,0 +1,34 @@ +import { + $stationContainer, + $lineContainer, + $sectionContainer, + $mapContainer, + $sectionEditContainer, +} from './element.js'; +import {BUTTON_MANAGEMENT_ID} from '../constant.js'; + +export const showScreen = (e) => { + const {STATION, LINE, SECTION, MAP} = BUTTON_MANAGEMENT_ID; + if (e.target.id === STATION) { + return ($stationContainer.style.display = 'block'); + } + if (e.target.id === LINE) { + return ($lineContainer.style.display = 'block'); + } + if (e.target.id === SECTION) { + return ($sectionContainer.style.display = 'block'); + } + if (e.target.id === MAP) { + return ($mapContainer.style.display = 'block'); + } +}; + +export const showSectionScreen = (lineName) => { + const $allSectionTr = $sectionEditContainer.querySelectorAll('tr'); + $sectionEditContainer.style.display = 'block'; + $allSectionTr.forEach((tr) => { + if (tr.dataset.lineName === lineName) { + tr.style.display = 'table-row'; + } + }); +}; diff --git a/src/constant.js b/src/constant.js new file mode 100644 index 000000000..d43f31b80 --- /dev/null +++ b/src/constant.js @@ -0,0 +1,41 @@ +export const BUTTON_MANAGEMENT_ID = { + STATION: 'station-manager-button', + LINE: 'line-manager-button', + SECTION: 'section-manager-button', + MAP: 'map-print-manager-button', +}; + +export const ADD_BUTTON_CLASS = { + STATION_DELETE: 'station-delete-button', + LINE_DELETE: 'line-delete-button', + SECTION_DELETE: 'section-delete-button', + SECTION_LINE_MENU: 'section-line-menu-button', +}; + +export const TEXT = { + LINE_MANAGEMENT: '관리', + DELETE_FROM_LINE: '노선에서 삭제', + DELETE: '삭제', + CONFIRM_DELETE: '❗ 정말로 삭제하시겠습니까?', +}; + +export const KEY = { + STATION: 'station', + LINE: 'line', +}; + +export const ERROR_MESSAGE = { + LINE_HAVE_SAME_STATION: '❌ 노선에 존재하는 역은 삭제할 수 없습니다.', + LESS_THAN_TWO_STATION: '❌ 노선에 역이 2개 이하이면 삭제할 수 없습니다.', + WRONG_INPUT: '❌ 띄어쓰기 없이 한글과 숫자만 입력해주세요.', + LESS_THAN_ONE_LETTER: '❌ 2글자 이상으로 입력해주세요.', + SAME_STATION: '❌ 같은 이름의 역이 존재합니다.', + SAME_LINE: '❌ 같은 이름의 노선이 존재합니다.', + ALREADY_INCLUDE_STATION: '❌ 이미 역이 추가되어 있습니다.', + SAME_LAST_STOP: '❌ 상행과 하행이 같은 역이 될 수 없습니다.', + NOT_NUMBER: '❌ 0이상의 숫자를 입력해주세요.', + LENGTH_LIMIT: '이하의 수를 입력해주세요.', +}; + +export const MIN_LETTER = 2; +export const MIN_LINE_LENGTH = 2; diff --git a/src/index.js b/src/index.js index e69de29bb..033302c42 100644 --- a/src/index.js +++ b/src/index.js @@ -0,0 +1,41 @@ +import Station from './Model/station.js'; +import Line from './Model/line.js'; +import { + $screenAllButton, + $stationAddButton, + $lineAddButton, + $sectionAddButton, +} from './View/element.js'; +import {hideScreen} from './View/hide-screen.js'; +import {showScreen} from './View/show-screen.js'; +import {onAddStation, loadStation} from './Controller/station-control.js'; +import {onAddLine, loadLine} from './Controller/line-control.js'; +import { + onAddSection, + loadSectionButton, + loadSectionTable, +} from './Controller/section-control.js'; +import {loadMapPrint} from './Controller/map-print-control.js'; + +export const stationInstance = new Station(); +export const lineInstance = new Line(); + +const onChangeScreen = (e) => { + hideScreen(); + showScreen(e); +}; + +(function gameStart() { + $screenAllButton.forEach((button) => + button.addEventListener('click', onChangeScreen), + ); + $stationAddButton.addEventListener('click', onAddStation); + $lineAddButton.addEventListener('click', onAddLine); + $sectionAddButton.addEventListener('click', onAddSection); + + loadStation(); + loadLine(); + loadSectionButton(); + loadSectionTable(); + loadMapPrint(); +})();