Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
08a5c96
chore(git): gitignore 파일 추가
ddongule Dec 10, 2020
2edaa1c
docs: Readme 업데이트
ddongule Dec 10, 2020
533a9b9
docs: Readme의 예외 상황 highlight
ddongule Dec 10, 2020
7b58dec
style: 첫 화면 구성 - 모든 관리창 숨김처리
ddongule Dec 11, 2020
e86d089
chore(markup): 프로젝트를 위한 뼈대 생성
ddongule Dec 11, 2020
f935221
feat: element를 숨기고 보여주는 기능 구현
ddongule Dec 11, 2020
442c63b
feat: 화면 상단의 관리 버튼을 눌렀을 때, 해당 관리 내용을 보여주는 기능 구현
ddongule Dec 11, 2020
15c1afe
docs: Readme - 지하철 역 관리 관련 기능 예외 상황 업데이트
ddongule Dec 11, 2020
9ee2fd8
feat: Station Class 생성
ddongule Dec 11, 2020
2283f8f
feat: 지하철 역 관리 - 지하철 역 이름의 유효성 검사 기능 구현
ddongule Dec 11, 2020
abd0a13
feat: 입력을 초기화해주는 clear input 기능 구현
ddongule Dec 12, 2020
e504751
feat: HTML에 들어갈 table data를 만드는 기능 구현
ddongule Dec 12, 2020
03be7ab
feat: 유저의 입력을 받아 table에 한 행을 생성하는 기능 구현
ddongule Dec 12, 2020
ae04106
feat: 지하철 역 추가 기능 구현
ddongule Dec 12, 2020
b6f40f7
feat: localstorage에 있는 데이터를 가져와 state에 저장하는 기능 구현
ddongule Dec 12, 2020
ff73492
refactor: 파일에 필요한 constant 이동
ddongule Dec 12, 2020
20ae86c
chore(markup): 지하철 역 관리를 위한 뼈대 생성
ddongule Dec 12, 2020
8ad8280
refactor: String으로 만들던 table을 DOM을 이용해 만들도록 변경
ddongule Dec 13, 2020
3458b78
feat: localstorage에 저장하는 기능 구현
ddongule Dec 13, 2020
369bbbe
feat: 역을 삭제하고 localstorage에 업데이트하는 기능 구현
ddongule Dec 13, 2020
66a8e23
feat: 지하철 역 중복 확인 기능 구현
ddongule Dec 13, 2020
a949d49
chore(markup): 지하철 노선 관리를 위한 뼈대 생성
ddongule Dec 13, 2020
c2705a2
feat: 지하철 역을 삭제하기 전 경고창 띄우는 기능 구현
ddongule Dec 13, 2020
e40af75
refactor: show/hide 기능 대신 버튼을 누를 때 다시 rendering 될 수 있게 변경
ddongule Dec 13, 2020
dadc361
feat: 노선 관리 Select box 생성
ddongule Dec 13, 2020
b3dab97
feat: SubwayLine Class 생성
ddongule Dec 13, 2020
d555cf7
refactor: 재사용을 위해 새 행을 만드는 showNewRow 코드 수정
ddongule Dec 13, 2020
67ee16e
feat: 지하철 노선도 관리 기능 구현
ddongule Dec 13, 2020
620c2e8
feat: 지하철 구간 관리의 노선 버튼 추가
ddongule Dec 13, 2020
85e2ce0
feat: 지하철 구간 관리 페이지 렌더링 기능 구현
ddongule Dec 13, 2020
108ab4d
feat: 지하철 구간 관리의 '노선에서 삭제' 버튼 클릭 이벤트 구현
ddongule Dec 14, 2020
c161259
feat: 지하철 노선도 출력 기능 구현
ddongule Dec 14, 2020
2f7e3e7
feat: 지하철 구간 관리 - 역 삭제/등록시 구간 등록 표에 반영하는 기능 구현
ddongule Dec 14, 2020
2981b6b
feat: 지하철 노선 이름 입력의 유효성 검사 기능 구현
ddongule Dec 14, 2020
a62d980
feat: 지하철 구간 관리 순서 input의 유효성 검사 구현
ddongule Dec 14, 2020
b1a38cd
Merge branch 'minkyung' of github.com:ddongule/javascript-subway-map-…
ddongule Dec 14, 2020
5dd8b21
refactor: sectionManagerContainer 리팩토링
ddongule Dec 14, 2020
dc3bb90
docs: Readme - 지하철 구간 관리 기능의 예외 상황 업데이트
ddongule Dec 14, 2020
e5d5acd
refactor: total subway manage container 리팩토링
ddongule Dec 15, 2020
c72356b
refactor: 지하철 노선도 출력 기능 리팩토링
ddongule Dec 15, 2020
e1e0d15
refactor: 지하철 역 관리와 노선 관리에서 쓰이는 중복 모듈 분리
ddongule Dec 15, 2020
c4bae0e
refactor: 지하철 역, 노선 이름 input의 유효성 검사 중복 코드 리팩토링
ddongule Dec 15, 2020
55d8989
refactor: section-manage-container 리팩토링
ddongule Dec 15, 2020
60d63dd
chore(css): 불필요한 css 삭제
ddongule Dec 15, 2020
4f9e2d6
fix: 지하철 노선 삭제 기능 오류 해결
ddongule Dec 15, 2020
17c2ab2
docs: README에 지하철 노선도 미션(3주차) 회고 추가
ddongule Dec 15, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.vscode
.DS_Store
297 changes: 284 additions & 13 deletions README.md

Large diffs are not rendered by default.

Binary file added images/1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 8 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,15 @@
</head>
<body>
<div id="app">
<h1>🚇 지하철 노선도 관리 </h1>
<h1>🚇 지하철 노선도 관리</h1>
</div>
<div id="menu">
<button id="station-manager-button">1. 역 관리</button>
<button id="line-manager-button">2. 노선 관리</button>
<button id="section-manager-button">3. 구간 관리</button>
<button id="map-print-manager-button">4. 지하철 노선도 출력</button>
</div>
<div id="content"></div>
<script type="module" src="src/index.js"></script>
</body>
</html>
72 changes: 72 additions & 0 deletions src/components/line-manage-container.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import SubwayLine from "../domain/subway-line.js";
import inputNameValidator from "../utils/inputs/validator/name-validator.js";
import clearInput from "../utils/inputs/clear-input.js";
import { saveToLocalStorage } from "../index.js";
import { showNewRow, makeSelectOptions } from "../utils/display/make-elements.js";
import { getStationByName } from "../utils/global-utils.js";
import { LINE_ALERT_MESSAGES } from "../global/messages.js";
import { LINE_ARRAY_KEY, LINE_TAGS } from "../global/constant.js";

const SUBWAY_LINE_TBODY_ID = "lines";
const VALIDATION_TYPE = "LINE";

function loadLines(state) {
for (const line of state.subwayLines) {
showNewRow(SUBWAY_LINE_TBODY_ID, line, [
line.lineName,
line.stations[0].stationName,
line.stations[line.stations.length - 1].stationName
]);
}
}

function validateLineName(subwayLines, lineName, upLine, downLine, addLineInput) {
if (inputNameValidator(VALIDATION_TYPE, lineName)) {
saveLine(subwayLines, lineName, upLine, downLine);
saveToLocalStorage(LINE_ARRAY_KEY, JSON.stringify(subwayLines));
clearInput(addLineInput);
} else {
clearInput(addLineInput);
}
}

function saveLine(subwayLines, lineName, upLine, downLine) {
let lineId = 0;
if (subwayLines.length !== 0) {
lineId = subwayLines[subwayLines.length - 1].id + 1;
}

const line = new SubwayLine(
lineName,
getStationByName(upLine.value),
getStationByName(downLine.value),
lineId
);

showNewRow(SUBWAY_LINE_TBODY_ID, line, [
line.lineName,
line.stations[0].stationName,
line.stations[line.stations.length - 1].stationName
]);

subwayLines.push(line);
}

export default function lineManageContainer(state) {
const addLineInput = document.getElementById(LINE_TAGS.LINE_NAME_INPUT_ID);
const addLineSubmit = document.getElementById(LINE_TAGS.ADD_LINE_ID);
const upLine = document.getElementById(LINE_TAGS.SELECT_UP_LINE_ID);
const downLine = document.getElementById(LINE_TAGS.SELECT_DOWN_LINE_ID);
makeSelectOptions(upLine, state.stationArray);
makeSelectOptions(downLine, state.stationArray);
loadLines(state);

addLineSubmit.addEventListener("click", () => {
if (upLine.value !== downLine.value) {
const lineNameInputValue = addLineInput.value.trim();
validateLineName(state.subwayLines, lineNameInputValue, upLine, downLine, addLineInput);
} else {
alert(LINE_ALERT_MESSAGES.ERROR_UPLINE_DOWNLINE_SAME);
}
});
}
39 changes: 39 additions & 0 deletions src/components/map-print-manage-container.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { state } from "../index.js";

function printAllLineAndStations(elementToAppend, subwayLines) {
for (const line of subwayLines) {
printLineName(elementToAppend, line.lineName);
printStation(elementToAppend, line.stations);
}
}

function printLineName(elementToAppend, lineName) {
const h2 = document.createElement("h2");

h2.append(lineName);
elementToAppend.append(h2);
}

function printStation(elementToAppend, stationArray) {
for (const station of stationArray) {
const stationsInLine = document.createElement("li");
stationsInLine.append(station.stationName);
elementToAppend.append(stationsInLine);
}
}

export default function mapPrintManageContainer() {
const parent = document.getElementById("manage-map-print");
const div = document.createElement("div");

if (state.subwayLines.length) {
printAllLineAndStations(div, state.subwayLines);
} else {
const title = `<h2>노선이 없습니다.<br />노선에 아래의 역들을 등록해 주세요.</h2>`;
const titleElement = new DOMParser().parseFromString(title, "text/html").firstElementChild;
div.append(titleElement);
printStation(div, state.stationArray);
}

parent.append(div);
}
184 changes: 184 additions & 0 deletions src/components/section-manage-container.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
import { state, saveToLocalStorage } from "../index.js";
import { LINE_ARRAY_KEY, SECTION_TAGS } from "../global/constant.js";
import { CONFIRM_MESSAGES } from "../global/messages.js";
import { SECTION_HTML, makeSectionHtml } from "../global/innerHtml.js";
import { getStationByName } from "../utils/global-utils.js";
import {
makeNewElementWithInnerHtml,
makeNewTdWithElement
} from "../utils/display/make-elements.js";
import sectionInputValidator, {
checkZero
} from "../utils/inputs/validator/section-input-validator.js";

import clearInput from "../utils/inputs/clear-input.js";

function removeAllChild(element) {
while (element.firstChild) {
element.removeChild(element.firstChild);
}
}

function loadEditSectionLines() {
const parentDiv = document.getElementById(SECTION_TAGS.PARENT_SECTION_ID);

for (const line of state.subwayLines) {
const lineManageButton = makeNewElementWithInnerHtml("button", line.lineName);
lineManageButton.classList.add(SECTION_TAGS.LINE_MENU_BUTTON_CLASS);
lineManageButton.dataset.id = `manage-${line.id}`;

parentDiv.append(lineManageButton);
}
}

function indexOfNewElement(sectionInput, stationsInLine) {
if (sectionInput < stationsInLine.length) {
return sectionInput;
}
return stationsInLine.length;
}

function addSectionInput(input, option, line, tableBody) {
const sectionInputValue = checkZero(input.value);

if (sectionInputValidator(sectionInputValue, option.value, line.stations)) {
const newStation = getStationByName(option.value);
const newIndex = indexOfNewElement(sectionInputValue, line.stations);
const tr = makeOneRow(newStation, newIndex, line, tableBody);
line.stations.splice(parseInt(sectionInputValue), 0, newStation);

updateAddedIndex(newIndex, tableBody.children);
tableBody.insertBefore(tr, tableBody.children[newIndex]);
saveToLocalStorage(LINE_ARRAY_KEY, JSON.stringify(state.subwayLines));
clearInput(input);
} else {
clearInput(input);
}
}

function makeSelectOptions(stationArray) {
const selectWrapper = document.createElement("select");
selectWrapper.id = SECTION_TAGS.STATION_SELECTOR_ID;

for (const station of stationArray) {
const option = `<option value="${station.stationName}">${station.stationName}</option>`;
appendHtmlToParent(selectWrapper, option);
}
return selectWrapper;
}

function makeSectionManager(stationArray, wrapper, parent) {
const selectWrapper = makeSelectOptions(stationArray);

appendHtmlToParent(wrapper, SECTION_HTML.SECTION_TABLE_HTML);
wrapper.append(selectWrapper);
appendHtmlToParent(wrapper, SECTION_HTML.SECTION_INPUT_HTML);
parent.append(wrapper);
}

export default function sectionManageContainer() {
loadEditSectionLines();
const parentDiv = document.getElementById(SECTION_TAGS.PARENT_SECTION_ID);
const lineWrapperDiv = document.createElement("div");
lineWrapperDiv.id = "manage-wrapper";

for (const line of state.subwayLines) {
const manage = document.querySelector(`[data-id="manage-${line.id}"]`);
manage.addEventListener("click", () => {
removeAllChild(lineWrapperDiv);
appendHtmlToParent(lineWrapperDiv, makeSectionHtml(line.lineName));
makeSectionManager(state.stationArray, lineWrapperDiv, parentDiv);

const table = document.getElementById("section-table");
const tableBody = document.getElementById("section-tbody");

const selectedOption = document.getElementById(SECTION_TAGS.STATION_SELECTOR_ID);
const sectionInput = document.getElementById(SECTION_TAGS.ORDER_INPUT_ID);
const addSectionInputButton = document.getElementById(SECTION_TAGS.ADD_BUTTON_ID);

addSectionInputButton.addEventListener("click", () => {
addSectionInput(sectionInput, selectedOption, line, tableBody);
});

showTableItems(line, tableBody);
table.append(tableBody);
lineWrapperDiv.append(table);
});
}
}

// TODO - utils로 빼기

function appendHtmlToParent(parent, html) {
const htmlDOM = new DOMParser().parseFromString(html, "text/html").body.childNodes;

for (const node of htmlDOM) {
parent.append(node);
}
}

function updateDeletedIndex(currentIndex, trDatas) {
for (let index = parseInt(currentIndex.innerHTML); index < trDatas.length; index++) {
trDatas[index].firstChild.innerHTML = parseInt(trDatas[index].firstChild.innerHTML) - 1;
}
}

function updateAddedIndex(currentIndex, trDatas) {
for (let index = parseInt(currentIndex); index < trDatas.length; index++) {
trDatas[index].firstChild.innerHTML = parseInt(trDatas[index].firstChild.innerHTML) + 1;
}
}

function deletedStationFromLine(currentStations, deleteButton) {
return currentStations.filter(
station => parseInt(deleteButton.dataset.id) !== parseInt(station.id)
);
}

function deleteStationFromLine(tr, line, tableBody) {
if (confirm(CONFIRM_MESSAGES.CONFIRM_DELETE)) {
line.stations = deletedStationFromLine(line.stations, tr.childNodes[2].firstChild);
tr.remove();

updateDeletedIndex(tr.childNodes[0], tableBody.children);
}
saveToLocalStorage(LINE_ARRAY_KEY, JSON.stringify(state.subwayLines));
}

function makeTdDeleteButton(station, tr, line, tableBody) {
const tdDeleteButton = makeNewElementWithInnerHtml("button", "노선에서 삭제");
tdDeleteButton.class = SECTION_TAGS.DELETE_BUTTON_CLASS;
tdDeleteButton.dataset.id = station.id;

tdDeleteButton.addEventListener("click", () => {
deleteStationFromLine(tr, line, tableBody);
});

return tdDeleteButton;
}

function makeTrWithDataset(station, index) {
const tr = document.createElement("tr");
tr.dataset.stationId = station.id;
tr.dataset.trId = index;

return tr;
}

function makeOneRow(station, index, line, tableBody) {
const tr = makeTrWithDataset(station, index);
const tdIndex = makeNewElementWithInnerHtml("td", index);
const tdStationName = makeNewElementWithInnerHtml("td", station.stationName);
const tdDeleteButton = makeTdDeleteButton(station, tr, line, tableBody);
const tdDelete = makeNewTdWithElement(tdDeleteButton);
tr.append(...[tdIndex, tdStationName, tdDelete]);

return tr;
}

function showTableItems(line, tableBody) {
for (const [index, station] of Object.entries(line.stations)) {
const tr = makeOneRow(station, index, line, tableBody);
tableBody.append(tr);
}
}
50 changes: 50 additions & 0 deletions src/components/station-manage-container.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import Station from "../domain/station.js";
import inputNameValidator from "../utils/inputs/validator/name-validator.js";
import clearInput from "../utils/inputs/clear-input.js";
import { saveToLocalStorage } from "../index.js";
import { showNewRow } from "../utils/display/make-elements.js";
import { STATION_ARRAY_KEY, STATION_TAGS } from "../global/constant.js";

const VALIDATION_TYPE = "STATION";

function loadStations(state) {
for (const station of state.stationArray) {
showNewRow(STATION_TAGS.STATION_TBODY_ID, station, [station.stationName]);
}
}

function makeStationId(stationArray) {
let stationId = 0;

if (stationArray.length !== 0) {
stationId = stationArray[stationArray.length - 1].id + 1;
}

return stationId;
}

function makeNewStation(stationArray, stationNameInputValue) {
const stationId = makeStationId(stationArray);
const station = new Station(stationNameInputValue, stationId);

showNewRow(STATION_TAGS.STATION_TBODY_ID, station, [station.stationName]);
stationArray.push(station);
saveToLocalStorage(STATION_ARRAY_KEY, JSON.stringify(stationArray));
}

export default function stationManageContainer(state) {
const addStationButton = document.getElementById(STATION_TAGS.ADD_BUTTON_ID);
const stationNameInput = document.getElementById(STATION_TAGS.STATION_NAME_INPUT_ID);

loadStations(state);
addStationButton.addEventListener("click", () => {
const stationNameInputValue = stationNameInput.value.trim();

if (inputNameValidator(VALIDATION_TYPE, stationNameInputValue)) {
makeNewStation(state.stationArray, stationNameInputValue);
clearInput(stationNameInput);
} else {
clearInput(stationNameInput);
}
});
}
Loading