diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..54d714d Binary files /dev/null and b/.DS_Store differ diff --git a/README.md b/README.md index fbbc6dd..c321945 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,22 @@ # 프로그래머스 해설 사이트 + ## ✋ 개발 전 필수 확인! + 1. `npm install`을 통해 개발 종속 패키지들 설치 2. PR 시 project, milestone, reviewer, assignee 설정 필수 3. PR 시 변경 사항 설명 필수 - ## 📐 커밋 컨벤션 + ### 커밋 예시 + - `feat: 검색 시 초성 검색이 가능하도록 구현` - `refactor: 검색 결과 배열 반환 함수를 utils 폴더로 분리` -| 커밋 헤더 | 설명 | 추가 설명 | -|----------| --------------------- | ------------------------------------------------------------ | -| feat | 기능추가/수정/삭제 | 제품 코드 수정 발생 | -| refactor | 리팩토링 | 제품 코드 수정 발생 | -| fix | 버그 수정 | 제품 코드 수정 발생 | -| docs | 문서 추가, 수정, 삭제 | 코드 수정 없음 | -| etc | 그 외 모든 수정 | 유형이 혼재된 경우, 되도록이면 커밋 분리. 분리가 어려울 경우 위 순서 상 상위 항목의 유형으로 작성 | - +| 커밋 헤더 | 설명 | 추가 설명 | +| --------- | --------------------- | ------------------------------------------------------------------------------------------------- | +| feat | 기능추가/수정/삭제 | 제품 코드 수정 발생 | +| refactor | 리팩토링 | 제품 코드 수정 발생 | +| fix | 버그 수정 | 제품 코드 수정 발생 | +| docs | 문서 추가, 수정, 삭제 | 코드 수정 없음 | +| etc | 그 외 모든 수정 | 유형이 혼재된 경우, 되도록이면 커밋 분리. 분리가 어려울 경우 위 순서 상 상위 항목의 유형으로 작성 | diff --git a/Section/Footer/footer.js b/Section/Footer/footer.js deleted file mode 100644 index ef4d0db..0000000 --- a/Section/Footer/footer.js +++ /dev/null @@ -1,36 +0,0 @@ -export default function Footer() { - const footer = document.querySelector("#footerBox"); - - let template = ` - - - `; - - template = template.replaceAll("{{__레포지토리주소__}}", "https://github.com/codeisneverodd/programmers-coding-test") - footer.innerHTML = template; - - } \ No newline at end of file diff --git a/Section/SearchBox/components/SearchInput.js b/Section/SearchBox/components/SearchInput.js deleted file mode 100644 index 1f205ee..0000000 --- a/Section/SearchBox/components/SearchInput.js +++ /dev/null @@ -1,24 +0,0 @@ -import { CreateFuzzyMatcher } from '../utils/koreanFuzzy.js'; - -export function SearchInput() { - this.render = () => { - const $searchInput = document.querySelector('.searchInput'); - $searchInput.innerHTML = ` - - `; - const $fileListItem = document.querySelectorAll('.file-list-item'); - - $searchInput.addEventListener('input', event => { - const $searchInputBox = document.getElementById('searchInput'); - const query = $searchInputBox.value; - const regex = CreateFuzzyMatcher(query.toLowerCase()); - for (let i = 0; i < $fileListItem.length; i++) { - if (regex.test($fileListItem[i].textContent.toLowerCase())) { - $fileListItem[i].classList.remove('is-hidden'); - } else { - $fileListItem[i].classList.add('is-hidden'); - } - } - }); - }; -} diff --git a/Section/SearchBox/components/SearchableList.js b/Section/SearchBox/components/SearchableList.js deleted file mode 100644 index 43898eb..0000000 --- a/Section/SearchBox/components/SearchableList.js +++ /dev/null @@ -1,49 +0,0 @@ -//TODO: api 함수 및 상수 분리 -import SearchResult from '../../SearchResult/components/index.js'; -import { getFileList } from '../utils/api.js'; - -const POSSIBLE_LEVELS = [1, 2, 3, 4, 5]; -export function SearchableList() { - const $searchableList = document.querySelector('.searchableList'); - this.render = async () => { - $searchableList.innerHTML = ` -
- `; - const $fileListContainer = document.querySelector('.file-list-container'); - await fillList(); - async function fillList() { - const fileList = {}; - for (const level of POSSIBLE_LEVELS) { - fileList[level] = await getFileList(level); - delete fileList[level][0]; - } - $fileListContainer.innerHTML = ` - ${POSSIBLE_LEVELS.map( - level => ` - `, - ).join('')} - `; - } - - $fileListContainer.addEventListener('click', e => { - if (e.target.tagName !== 'LI') return; - const level = e.target.parentNode.classList[1].slice(-1); - const fileName = e.target.classList[1]; - const $searchResult = new SearchResult({ - level, - fileName, - }); - $searchResult.render(); - }); - }; -} diff --git a/Section/SearchBox/components/index.js b/Section/SearchBox/components/index.js deleted file mode 100644 index d46ae2c..0000000 --- a/Section/SearchBox/components/index.js +++ /dev/null @@ -1,18 +0,0 @@ -import { SearchInput } from './SearchInput.js'; -import { SearchableList } from './SearchableList.js'; - -export default function SearchBox() { - const $searchBox = document.querySelector('.searchBox'); - this.render = async () => { - $searchBox.innerHTML = ` -
-
-
찾는 문제가 없으신가요?
Repository에 풀이를 제보해주세요
- `; - const searchInput = new SearchInput(); - const searchableList = new SearchableList(); - - await searchableList.render(); - searchInput.render(); - }; -} diff --git a/Section/SearchBox/utils/api.js b/Section/SearchBox/utils/api.js deleted file mode 100644 index ae7e4c6..0000000 --- a/Section/SearchBox/utils/api.js +++ /dev/null @@ -1,6 +0,0 @@ -const GET_FILE_LIST_BASE_URL = `https://api.github.com/repos/codeisneverodd/programmers-coding-test/contents/`; -export const getFileList = async level => { - const url = GET_FILE_LIST_BASE_URL + `level-${level}`; - const response = await fetch(url); - return await response.json(); -}; diff --git a/Section/SearchBox/utils/koreanFuzzy.js b/Section/SearchBox/utils/koreanFuzzy.js deleted file mode 100644 index 8a65ceb..0000000 --- a/Section/SearchBox/utils/koreanFuzzy.js +++ /dev/null @@ -1,38 +0,0 @@ -const escapeRegExp = str => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - -const ch2pattern = ch => { - const offset = 44032; - if (/[가-힣]/.test(ch)) { - const chCode = ch.charCodeAt(0) - offset; - if (chCode % 28 > 0) { - return ch; - } - const begin = Math.floor(chCode / 28) * 28 + offset; - const end = begin + 27; - return `[\\u${begin.toString(16)}-\\u${end.toString(16)}]`; - } - if (/[ㄱ-ㅎ]/.test(ch)) { - const con2syl = { - ㄱ: '가'.charCodeAt(0), - ㄲ: '까'.charCodeAt(0), - ㄴ: '나'.charCodeAt(0), - ㄷ: '다'.charCodeAt(0), - ㄸ: '따'.charCodeAt(0), - ㄹ: '라'.charCodeAt(0), - ㅁ: '마'.charCodeAt(0), - ㅂ: '바'.charCodeAt(0), - ㅃ: '빠'.charCodeAt(0), - ㅅ: '사'.charCodeAt(0), - }; - const begin = - con2syl[ch] || (ch.charCodeAt(0) - 12613) * 588 + con2syl['ㅅ']; - const end = begin + 587; - return `[${ch}\\u${begin.toString(16)}-\\u${end.toString(16)}]`; - } - return escapeRegExp(ch); -}; - -export const CreateFuzzyMatcher = input => { - const pattern = input.split('').map(ch2pattern).join('.*?'); - return new RegExp(pattern); -}; diff --git a/Section/SearchResult/components/index.js b/Section/SearchResult/components/index.js deleted file mode 100644 index 9201722..0000000 --- a/Section/SearchResult/components/index.js +++ /dev/null @@ -1,84 +0,0 @@ -import { getFileContent } from "../utils/api.js"; -import { formattedFileName, splitCodeToSolutions } from "../utils/format.js"; -import { copyText } from "../utils/copyText.js"; - -export default function SearchResult({ level, fileName }) { - let page = 0; - this.render = async () => { - const $searchResult = document.querySelector('.searchResult'); - $searchResult.innerHTML = ` -
- - -
-
-
-

-        
-      
- `; - const $fileTitle = document.querySelector('.file-title'); - const $code = document.querySelector('.code'); - const solutions = splitCodeToSolutions( - await getFileContent(level, fileName), - ); - setCurrentResult(); - addCopyEvent(); - addNavEvent(); - - function setCurrentResult() { - $fileTitle.innerHTML = formattedFileName(fileName); - $code.innerHTML = solutions[page]; - if(solutions.length > 1){ - const $btnNextSolution = document.querySelector('.btnNextSolution-inactive') - $btnNextSolution.className = 'btnNextSolution' - } - } - - function addCopyEvent() { - const $copyBtn = document.querySelector('.btn-copy'); - $copyBtn.addEventListener('click', e => { - const src = e.target.previousElementSibling; - copyText(src); - const isCopied = document.querySelector('.isCopied'); - isCopied.textContent = ' 📋 클립보드에 복사됨!'; - setTimeout(() => { - isCopied.textContent = ''; - }, 1000); - }); - } - - function addNavEvent() { - const $navigator = document.querySelector('.solutionNavigator'); - const btnPrevSolution = document.querySelector( - '.btnPrevSolution-inactive', - ); - const btnNextSolution = document.querySelector('.btnNextSolution'); - if (page === 0) { - btnPrevSolution.className = 'btnPrevSolution-inactive'; - } - $navigator.addEventListener('click', e => { - const $clickedButton = e.target.closest('button'); - - if ($clickedButton.className.includes('btnPrevSolution')) { - if (page > 0) { - page -= 1; - $code.innerHTML = solutions[page]; - } - } - if ($clickedButton.className.includes('btnNextSolution')) { - if (page < solutions.length - 1) { - page += 1; - $code.innerHTML = solutions[page]; - } - } - page !== 0 - ? (btnPrevSolution.className = 'btnPrevSolution') - : (btnPrevSolution.className = 'btnPrevSolution-inactive'); - page === solutions.length - 1 - ? (btnNextSolution.className = 'btnNextSolution-inactive') - : (btnNextSolution.className = 'btnNextSolution'); - }); - } - }; -} diff --git a/Section/SearchResult/utils/api.js b/Section/SearchResult/utils/api.js deleted file mode 100644 index e2cdee0..0000000 --- a/Section/SearchResult/utils/api.js +++ /dev/null @@ -1,6 +0,0 @@ -const GET_FILE_CONTENT_BASE_URL = `https://raw.githubusercontent.com/codeisneverodd/programmers-coding-test/main/`; -export const getFileContent = async (level, fileName) => { - const url = GET_FILE_CONTENT_BASE_URL + `/level-${level}` + `/${fileName}`; - const response = await fetch(url); - return await response.text(); -}; diff --git a/Section/SearchResult/utils/copyText.js b/Section/SearchResult/utils/copyText.js deleted file mode 100644 index 67a9637..0000000 --- a/Section/SearchResult/utils/copyText.js +++ /dev/null @@ -1,11 +0,0 @@ -export function copyText(src) { - const t = document.createElement('textarea'); - document.body.appendChild(t); - t.value = src.innerHTML - .replace(/</g, '<') - .replace(/>/g, '>') - .replace(/"/g, '"'); - t.select(); - document.execCommand('copy'); - document.body.removeChild(t); -} diff --git a/app.js b/app.js deleted file mode 100644 index 1335594..0000000 --- a/app.js +++ /dev/null @@ -1,28 +0,0 @@ -import SearchBox from './Section/SearchBox/components/index.js'; -import FooterBox from './Section/Footer/footer.js'; - -const $app = document.querySelector('.app'); -const $loading = document.querySelector('.loading'); - -const init = async () => { - $app.innerHTML = ` - -
- - `; - const $searchBox = new SearchBox(); - await $searchBox.render(); -}; - -const loading = async () => { - $loading.innerHTML = ` - - - Loading… - `; - await init(); - FooterBox(); - $loading.style.display = 'none'; -}; - -loading(); \ No newline at end of file diff --git a/index.html b/index.html deleted file mode 100644 index 2b57603..0000000 --- a/index.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - 프로그래머스 해설 은행 - - - - - - -

🔍 프로그래머스 해설 은행

-
-
- - - diff --git a/public/index.html b/public/index.html index 0f5f6bc..f3efa95 100644 --- a/public/index.html +++ b/public/index.html @@ -3,24 +3,19 @@ - + 프로그래머스 해설 은행 - + diff --git a/src/.DS_Store b/src/.DS_Store new file mode 100644 index 0000000..1ccffbb Binary files /dev/null and b/src/.DS_Store differ diff --git a/src/App.js b/src/App.js index 43764b9..2681bb5 100644 --- a/src/App.js +++ b/src/App.js @@ -1,7 +1,16 @@ -function App() { +import SearchBox from "./SearchBox/SearchBox"; +import SearchResult from "./SearchResult/SearchResult"; +import Footer from "./Footer/Footer"; +import Loading from "./Loading"; + +export default function App() { return ( -
+
+

🔍 프로그래머스 해설 은행

+ + + +
); } - -export default App; diff --git a/src/Footer/footer.js b/src/Footer/footer.js index ef4d0db..e09633d 100644 --- a/src/Footer/footer.js +++ b/src/Footer/footer.js @@ -1,36 +1,63 @@ -export default function Footer() { - const footer = document.querySelector("#footerBox"); +import gitHubLogoPath from "../images/github.png"; +import programmersLogoPath from "../images/programmers.png"; - let template = ` -