Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
9 commits
Select commit Hold shift + click to select a range
6eeaa84
feat: input ์ž…๋ ฅ์‹œ ๊ฒ€์ƒ‰ ๋ฆฌ์ŠคํŠธ๊ฐ€ ๋ณด์ด๊ณ , ์„ ํƒ ์‹œ ๋ฆฌ์ŠคํŠธ๊ฐ€ ๋ณด์ด์ง€ ์•Š๋„๋ก ๊ตฌํ˜„
codeisneverodd Jul 20, 2022
aa16184
feat: QuestionList ์ปดํฌ๋„ŒํŠธํ™” ๋ฐ ErrorReport ์˜ QuestionList ์ฃผ์„ํ™”
codeisneverodd Jul 20, 2022
fa4dfc1
feat: solution fetch ๋ฅผ ์œ„ํ•œ api ์ถ”๊ฐ€
codeisneverodd Jul 21, 2022
a21ba90
feat: ํ•œ๊ธ€ ๊ฒ€์ƒ‰์„ ์œ„ํ•œ ํ•จ์ˆ˜ ์ถ”๊ฐ€
codeisneverodd Jul 21, 2022
886bf2c
feat: ํ•ด์„ค ๋ชฉ๋ก์„ ์œ„ํ•œ ์ „์—ญ ์ƒํƒœ ์ถ”๊ฐ€ ๋ฐ localStorageEffect ์ถ”๊ฐ€
codeisneverodd Jul 21, 2022
c7bcdd6
feat: solutionList ์ƒํƒœ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•œ ์ปค์Šคํ…€ ํ›… ์ถ”๊ฐ€
codeisneverodd Jul 21, 2022
f314159
feat: ๋ฌธ์ œ ์ด๋ฆ„ ๊ฒ€์ƒ‰ ์ปดํฌ๋„ŒํŠธํ™”
codeisneverodd Jul 21, 2022
e05eedc
feat: ๋ฌธ์ œ ์ด๋ฆ„ ์ปดํฌ๋„ŒํŠธ๋กœ ๋ถ€ํ„ฐ ๊ฐ’์„ ๋ฐ›์•„์˜ฌ ์ˆ˜ ์žˆ๋Š” handleQuestionNameChange ํ•ธ๋“ค๋Ÿฌ ์ถ”๊ฐ€
codeisneverodd Jul 21, 2022
567d1c6
feat: ErrorReport ํŽ˜์ด์ง€์˜ QuestionList๋ฅผ ์ปดํฌ๋„ŒํŠธ๋กœ ๋Œ€์ฒด
codeisneverodd Jul 21, 2022
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
7 changes: 7 additions & 0 deletions src/apis/solutionList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const GET_SOLUTION_LIST_BASE_URL = `https://api.github.com/repos/codeisneverodd/programmers-coding-test/contents/`;

export const requestSolutionListByLevelAPI = async (level) => {
const url = GET_SOLUTION_LIST_BASE_URL + `level-${level}`;
const response = await fetch(url);
return await response.json();
};
98 changes: 98 additions & 0 deletions src/components/QuestionInputAndList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import styled from "styled-components";
import { TextInput } from "../style/styledComponents";
import { useEffect, useState } from "react";
import useSolutionListValue from "../hooks/solutionList/useSolutionListValue";
import { createFuzzyMatcher } from "./utils/createFuzzyMatcher";

const QuestionInputAndList = ({ onQuestionNameChange = () => {} }) => {
const [questionName, setQuestionName] = useState("");
const [questionList, setQuestionList] = useState([]);
const [isQuestionListVisible, setIsQuestionListVisible] = useState(false);
const solutionList = useSolutionListValue();

useEffect(() => {
setQuestionList(solutionList);
}, []);
useEffect(() => {
onQuestionNameChange(questionName);
}, [questionName]);

function handleQuestionNameInput(e) {
const inputValue = e.target.value;
setQuestionName(inputValue);
const findMatchedNameRegex = createFuzzyMatcher(inputValue);
setQuestionList(
solutionList.filter((solution) =>
findMatchedNameRegex.test(solution.name)
)
);
if (!isQuestionListVisible) setIsQuestionListVisible(true);
}
function handleQuestionNameBlur(e) {
setIsQuestionListVisible(false);
}
function handleQuestionNameFocus(e) {
setIsQuestionListVisible(true);
}
function handleQuestionClick(e) {
setIsQuestionListVisible(false);
setQuestionName(e.target.dataset.value);
}

return (
<>
<TextInput
id="questionNameInput"
placeholder="๋ฌธ์ œ ์ด๋ฆ„์„ ๊ฒ€์ƒ‰ํ•˜์„ธ์š”."
defaultValue={questionName}
value={questionName}
onFocus={handleQuestionNameFocus}
onInput={handleQuestionNameInput}
/>
{isQuestionListVisible && (
<QuestionList id="questionsList">
{questionList.map((value, index) => (
<QuestionItem key={value.name + index}>
<QuestionBtn
onClick={handleQuestionClick}
data-value={value.name}
>
{value.name} / Level {value.level}
</QuestionBtn>
</QuestionItem>
))}
</QuestionList>
)}
</>
);
};
export default QuestionInputAndList;

export const QuestionList = styled.ul`
//display: none;
position: absolute;
top: 20rem;
left: 0;
width: 100%;
height: 33.2rem;
background-color: ${(props) => props.theme.searchBg};
overflow: scroll;
z-index: 10;
`;
export const QuestionItem = styled.li``;

export const QuestionBtn = styled.div`
width: 100%;
height: 9rem;
text-align: left;
line-height: 9rem;
text-indent: 2rem;
background-color: transparent;
font-size: 3.1rem;
color: ${(props) => props.theme.basicWhite};
border-bottom: 1px solid ${(props) => props.theme.notSelectedTab};
cursor: pointer;
&:hover {
background-color: ${(props) => props.theme.programmersBlue};
}
`;
38 changes: 38 additions & 0 deletions src/components/utils/createFuzzyMatcher.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
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);
};
35 changes: 35 additions & 0 deletions src/hooks/solutionList/useFetchSolutionList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import useSetSolutionList from "./useSetSolutionList";
import { requestSolutionListByLevelAPI } from "../../apis/solutionList";

const useFetchSolutionList = () => {
const setSolutionList = useSetSolutionList();
const POSSIBLE_LEVEL = [1, 2, 3, 4, 5];

const fetchSolutionList = async () => {
setSolutionList([]);
POSSIBLE_LEVEL.forEach((level) => {
requestSolutionListByLevelAPI(level).then((response) => {
const newSolutionList = response
.filter((solution) => solution.name !== "00-ํ•ด๋‹ต-์˜ˆ์‹œ.js")
.map((solution) => ({
name: formattedFileName(solution.name),
level,
}));

setSolutionList((solutionList) => [
...solutionList,
...newSolutionList,
]);
});
});
setSolutionList((solutionList) =>
solutionList.sort((a, b) => a.level - b.level)
);
};

return fetchSolutionList;
};
export default useFetchSolutionList;
function formattedFileName(fileName) {
return fileName.replace(/-/g, " ").replace(".js", "");
}
7 changes: 7 additions & 0 deletions src/hooks/solutionList/useSetSolutionList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { useSetRecoilState } from "recoil";
import solutionListState from "../../state/solutionList";

const useSetSolutionList = () => {
return useSetRecoilState(solutionListState);
};
export default useSetSolutionList;
7 changes: 7 additions & 0 deletions src/hooks/solutionList/useSolutionListValue.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { useRecoilValue } from "recoil";
import solutionListState from "../../state/solutionList";

const useSolutionListValue = () => {
return useRecoilValue(solutionListState);
};
export default useSolutionListValue;
82 changes: 42 additions & 40 deletions src/pages/errorReportPage/ErrorReport.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,15 @@ import { useState } from "react";
import styled from "styled-components";
import Header from "../../components/Header";
import {
ThanksMsg,
OtherReportBtn,
InputLabel,
MainContetnWrapper,
OtherReportBtn,
StepByStepInputItem,
InputLabel,
TextInput,
QuestionList,
QuestionItem,
QuestionBtn,
TextArea,
SubmitBtn,
TextArea,
ThanksMsg,
} from "../../style/styledComponents";
import QuestionInputAndList from "../../components/QuestionInputAndList";

export default function ErrorReport() {
const [submitted, setSubmitted] = useState(false);
Expand Down Expand Up @@ -51,7 +48,9 @@ export default function ErrorReport() {
function handleSubmitBtnClick() {
setSubmitted(true);
}

function handleQuestionNameChange(e) {
setQuestionName(e);
}
return (
<>
<Header />
Expand Down Expand Up @@ -110,38 +109,41 @@ export default function ErrorReport() {
{isQuestionNameVisible && (
<StepByStepInputItem>
<InputLabel>๋ฌธ์ œ ์ด๋ฆ„</InputLabel>
<TextInput
id="questionNameInput"
placeholder="๋ฌธ์ œ ์ด๋ฆ„์„ ๊ฒ€์ƒ‰ํ•˜์„ธ์š”."
defaultValue={questionName}
onInput={handleQuestionNameInput}
<QuestionInputAndList
onQuestionNameChange={handleQuestionNameChange}
/>
<QuestionList id="questionsList">
<QuestionItem>
<QuestionBtn>1๋ฒˆ๋ฌธ์ œ</QuestionBtn>
</QuestionItem>
<QuestionItem>
<QuestionBtn>2๋ฒˆ๋ฌธ์ œ</QuestionBtn>
</QuestionItem>
<QuestionItem>
<QuestionBtn>3๋ฒˆ๋ฌธ์ œ</QuestionBtn>
</QuestionItem>
<QuestionItem>
<QuestionBtn>4๋ฒˆ๋ฌธ์ œ</QuestionBtn>
</QuestionItem>
<QuestionItem>
<QuestionBtn>5๋ฒˆ๋ฌธ์ œ</QuestionBtn>
</QuestionItem>
<QuestionItem>
<QuestionBtn>6๋ฒˆ๋ฌธ์ œ</QuestionBtn>
</QuestionItem>
<QuestionItem>
<QuestionBtn>7๋ฒˆ๋ฌธ์ œ</QuestionBtn>
</QuestionItem>
<QuestionItem>
<QuestionBtn>8๋ฒˆ๋ฌธ์ œ</QuestionBtn>
</QuestionItem>
</QuestionList>
{/*<TextInput*/}
{/* id="questionNameInput"*/}
{/* placeholder="๋ฌธ์ œ ์ด๋ฆ„์„ ๊ฒ€์ƒ‰ํ•˜์„ธ์š”."*/}
{/* defaultValue={questionName}*/}
{/* onInput={handleQuestionNameInput}*/}
{/*/>*/}
{/*<QuestionInputAndList id="questionsList">*/}
{/* <QuestionItem>*/}
{/* <QuestionBtn>1๋ฒˆ๋ฌธ์ œ</QuestionBtn>*/}
{/* </QuestionItem>*/}
{/* <QuestionItem>*/}
{/* <QuestionBtn>2๋ฒˆ๋ฌธ์ œ</QuestionBtn>*/}
{/* </QuestionItem>*/}
{/* <QuestionItem>*/}
{/* <QuestionBtn>3๋ฒˆ๋ฌธ์ œ</QuestionBtn>*/}
{/* </QuestionItem>*/}
{/* <QuestionItem>*/}
{/* <QuestionBtn>4๋ฒˆ๋ฌธ์ œ</QuestionBtn>*/}
{/* </QuestionItem>*/}
{/* <QuestionItem>*/}
{/* <QuestionBtn>5๋ฒˆ๋ฌธ์ œ</QuestionBtn>*/}
{/* </QuestionItem>*/}
{/* <QuestionItem>*/}
{/* <QuestionBtn>6๋ฒˆ๋ฌธ์ œ</QuestionBtn>*/}
{/* </QuestionItem>*/}
{/* <QuestionItem>*/}
{/* <QuestionBtn>7๋ฒˆ๋ฌธ์ œ</QuestionBtn>*/}
{/* </QuestionItem>*/}
{/* <QuestionItem>*/}
{/* <QuestionBtn>8๋ฒˆ๋ฌธ์ œ</QuestionBtn>*/}
{/* </QuestionItem>*/}
{/*</QuestionInputAndList>*/}
</StepByStepInputItem>
)}

Expand Down
Loading