diff --git a/apps/googleFormClone/src/component/common/Input.tsx b/apps/googleFormClone/src/component/common/Input.tsx deleted file mode 100644 index d83b25c..0000000 --- a/apps/googleFormClone/src/component/common/Input.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import classMerge from "../../utils/classMerge"; - -interface InputProps extends React.ComponentPropsWithRef<"input"> { - innerRef?: React.ComponentPropsWithRef<"input">["ref"]; -} - -const Input = ({ className, innerRef, ...props }: InputProps) => { - return ( -
- -
-
- ); -}; - -export default Input; diff --git a/apps/googleFormClone/src/component/editor/AnswerItemManager.tsx b/apps/googleFormClone/src/component/editor/AnswerItemManager.tsx index 14bc0c8..a3dce40 100644 --- a/apps/googleFormClone/src/component/editor/AnswerItemManager.tsx +++ b/apps/googleFormClone/src/component/editor/AnswerItemManager.tsx @@ -3,18 +3,21 @@ * answer 상태에서 answerIDLIst를 순회하면서 answer을 뽑아 알맞게 렌더링한다 */ -import { useEffect, useRef } from "react"; -import { EDITOR_QUESTION_TYPE } from "../../constants"; -import { useAppDispatch, useAppSelector } from "../../hook/useRedux"; -import { AnswerInterface, addAnswer, editAnswer, removeAnswer } from "../../store/reducer/answerSlice"; -import ChooseAnswer from "./ChooseAnswer"; -import { v4 as uuidv4 } from "uuid"; -import classMerge from "../../utils/classMerge"; -import useDnDList from "../../hook/headless/useDnDList"; -import { editAnswerOrder } from "../../store/reducer/questionSlice"; -import useChangeEditBlockID from "../../hook/useChangeEditBlockID"; -import Checkbox from "../common/Checkbox"; -import Radio from "../common/Radio/Radio"; +import { useEffect, useRef } from 'react'; +import { EDITOR_QUESTION_TYPE } from '../../constants'; +import { useAppDispatch, useAppSelector } from '../../hook/useRedux'; +import { + AnswerInterface, + addAnswer, + editAnswer, + removeAnswer, +} from '../../store/reducer/answerSlice'; +import ChooseAnswer from './ChooseAnswer'; +import { v4 as uuidv4 } from 'uuid'; +import useDnDList from '../../hook/headless/useDnDList'; +import { editAnswerOrder } from '../../store/reducer/questionSlice'; +import useChangeEditBlockID from '../../hook/useChangeEditBlockID'; +import { Checkbox, Radio, classMerge } from '@google-form-clone/shared-ui'; interface AnswerManagerProps { questionID: string; @@ -23,7 +26,9 @@ interface AnswerManagerProps { // TODO 기타 추가 구현 const AnswerManager = ({ questionID }: AnswerManagerProps) => { const dispatch = useAppDispatch(); - const { type, answerIDList } = useAppSelector((store) => store.question[questionID]); + const { type, answerIDList } = useAppSelector( + (store) => store.question[questionID] + ); const answerMap = useAppSelector((store) => store.answer); const prevAnswerLength = useRef(answerIDList.length); @@ -37,18 +42,24 @@ const AnswerManager = ({ questionID }: AnswerManagerProps) => { const chooseAnswerRef = useRef<(null | HTMLInputElement)[]>([]); - const changeAnswer = (e: React.ChangeEvent, { answerID, questionID }: AnswerInterface) => { + const changeAnswer = ( + e: React.ChangeEvent, + { answerID, questionID }: AnswerInterface + ) => { dispatch(editAnswer({ answerID, content: e.target.value, questionID })); }; const addAnswerItem = () => { const NEW_ANSWER_ID = uuidv4(); - dispatch(addAnswer({ answerID: NEW_ANSWER_ID, content: "", questionID })); + dispatch(addAnswer({ answerID: NEW_ANSWER_ID, content: '', questionID })); }; const removeAnswerItem = (aID: string, idx: number) => { if (chooseAnswerRef && chooseAnswerRef.current) { - if (idx < chooseAnswerRef.current.length && chooseAnswerRef.current[idx + 1] instanceof HTMLInputElement) { + if ( + idx < chooseAnswerRef.current.length && + chooseAnswerRef.current[idx + 1] instanceof HTMLInputElement + ) { chooseAnswerRef.current[idx + 1]!.focus(); } else { chooseAnswerRef.current[idx - 1]!.focus(); @@ -56,7 +67,9 @@ const AnswerManager = ({ questionID }: AnswerManagerProps) => { } if (chooseAnswerRef && chooseAnswerRef.current) { - chooseAnswerRef.current = chooseAnswerRef.current.filter((_, i) => i !== idx); + chooseAnswerRef.current = chooseAnswerRef.current.filter( + (_, i) => i !== idx + ); } dispatch(removeAnswer(answerMap[aID])); @@ -75,56 +88,67 @@ const AnswerManager = ({ questionID }: AnswerManagerProps) => { return (
- {type === EDITOR_QUESTION_TYPE.short &&
단답형 메시지
} - {type === EDITOR_QUESTION_TYPE.long &&
장문형 메시지
} - {type !== EDITOR_QUESTION_TYPE.short && type !== EDITOR_QUESTION_TYPE.long && ( - <> -
- {answerIDList.map((aID, idx) => { - const answerInfo = answerMap[aID]; - return ( - changeAnswer(e, answerInfo)} - isEditing={isEditing} - deletable={answerIDList.length > 1} - onDeleteButton={() => removeAnswerItem(aID, idx)} - handleDrag={handleDrag} - innerRef={(el) => (chooseAnswerRef.current[idx] = el)} - /> - ); - })} -
- {isEditing && ( -
-
- {type === EDITOR_QUESTION_TYPE.radio && } - {type === EDITOR_QUESTION_TYPE.checkbox && } - {type === EDITOR_QUESTION_TYPE.dropdown &&
} - - 옵션 추가 - -
- {/* 또는 + {type === EDITOR_QUESTION_TYPE.short && ( +
단답형 메시지
+ )} + {type === EDITOR_QUESTION_TYPE.long && ( +
장문형 메시지
+ )} + {type !== EDITOR_QUESTION_TYPE.short && + type !== EDITOR_QUESTION_TYPE.long && ( + <> +
+ {answerIDList.map((aID, idx) => { + const answerInfo = answerMap[aID]; + return ( + changeAnswer(e, answerInfo)} + isEditing={isEditing} + deletable={answerIDList.length > 1} + onDeleteButton={() => removeAnswerItem(aID, idx)} + handleDrag={handleDrag} + innerRef={(el) => (chooseAnswerRef.current[idx] = el)} + /> + ); + })} +
+ {isEditing && ( +
+
+ {type === EDITOR_QUESTION_TYPE.radio && } + {type === EDITOR_QUESTION_TYPE.checkbox && ( + + )} + {type === EDITOR_QUESTION_TYPE.dropdown && ( +
+ )} + + 옵션 추가 + +
+ {/* 또는 */} -
- )} - - )} +
+ )} + + )}
); }; diff --git a/apps/googleFormClone/src/component/editor/ChooseAnswer.tsx b/apps/googleFormClone/src/component/editor/ChooseAnswer.tsx index 04edb12..4da5f19 100644 --- a/apps/googleFormClone/src/component/editor/ChooseAnswer.tsx +++ b/apps/googleFormClone/src/component/editor/ChooseAnswer.tsx @@ -1,19 +1,25 @@ -import { RiCloseFill, RiDraggable } from "react-icons/ri"; -import IconButton from "../common/IconButton"; -import { EDITOR_QUESTION_TYPE, ICON_CLASS } from "../../constants"; -import Checkbox from "../common/Checkbox"; -import Radio from "../common/Radio/Radio"; -import Input from "../common/Input"; -import classMerge from "../../utils/classMerge"; -import { isTouchScreen } from "../../hook/headless/useDnDList"; +import { RiCloseFill, RiDraggable } from 'react-icons/ri'; +import { EDITOR_QUESTION_TYPE, ICON_CLASS } from '../../constants'; +import { isTouchScreen } from '../../hook/headless/useDnDList'; +import { + Checkbox, + IconButton, + Input, + Radio, + classMerge, +} from '@google-form-clone/shared-ui'; -interface RadioAnswerProps extends Omit, "type"> { - type: EDITOR_QUESTION_TYPE.radio | EDITOR_QUESTION_TYPE.checkbox | EDITOR_QUESTION_TYPE.dropdown; +interface RadioAnswerProps + extends Omit, 'type'> { + type: + | EDITOR_QUESTION_TYPE.radio + | EDITOR_QUESTION_TYPE.checkbox + | EDITOR_QUESTION_TYPE.dropdown; idx?: number; deletable?: boolean; isEditing?: boolean; onDeleteButton?: (...params: unknown[]) => unknown; - innerRef?: React.ComponentPropsWithRef<"input">["ref"]; + innerRef?: React.ComponentPropsWithRef<'input'>['ref']; handleDrag?: (e: React.MouseEvent) => void; } @@ -37,7 +43,7 @@ const ChooseAnswer = ({ tabIndex={0} className={classMerge([ `absolute outline-none cursor-move ${ICON_CLASS}`, - !isTouchScreen && "hidden group-hover/item:flex", + !isTouchScreen && 'hidden group-hover/item:flex', ])} /> )} @@ -45,7 +51,9 @@ const ChooseAnswer = ({ {type === EDITOR_QUESTION_TYPE.radio && } {type === EDITOR_QUESTION_TYPE.checkbox && } {type === EDITOR_QUESTION_TYPE.dropdown && ( -
{idx}
+
+ {idx} +
)} {deletable && isEditing && ( diff --git a/apps/googleFormClone/src/component/editor/QuestionBlock.tsx b/apps/googleFormClone/src/component/editor/QuestionBlock.tsx index d899156..fe7b54f 100644 --- a/apps/googleFormClone/src/component/editor/QuestionBlock.tsx +++ b/apps/googleFormClone/src/component/editor/QuestionBlock.tsx @@ -1,28 +1,42 @@ -import { RiDeleteBin6Line, RiDraggable, RiFileCopyLine } from "react-icons/ri"; -import Block from "../common/Block"; -import Input from "../common/Input"; -import Divider from "../common/Divider"; -import { EDITOR_DROPDOWN_LIST, EDITOR_QUESTION_TYPE, ICON_CLASS } from "../../constants"; -import IconButton from "../common/IconButton"; -import Switch from "../common/Switch"; - -import { removeQuestion, editQuestion, copyQuestion } from "../../store/reducer/questionSlice"; -import { useAppDispatch, useAppSelector } from "../../hook/useRedux"; -import AnswerManager from "./AnswerItemManager"; -import { addAnswer, removeAnswer } from "../../store/reducer/answerSlice"; -import { v4 } from "uuid"; -import Dropdown from "../common/Dropdown"; -import useChangeEditBlockID from "../../hook/useChangeEditBlockID"; -import useBlockAutoFocus from "../../hook/useBlockAutoFocus"; -import classMerge from "../../utils/classMerge"; -import { isTouchScreen } from "../../hook/headless/useDnDList"; - -interface QuestionBlockProps extends React.ComponentPropsWithRef<"div"> { +import { RiDeleteBin6Line, RiDraggable, RiFileCopyLine } from 'react-icons/ri'; +import { + EDITOR_DROPDOWN_LIST, + EDITOR_QUESTION_TYPE, + ICON_CLASS, +} from '../../constants'; + +import { + removeQuestion, + editQuestion, + copyQuestion, +} from '../../store/reducer/questionSlice'; +import { useAppDispatch, useAppSelector } from '../../hook/useRedux'; +import AnswerManager from './AnswerItemManager'; +import { addAnswer, removeAnswer } from '../../store/reducer/answerSlice'; +import { v4 } from 'uuid'; +import useChangeEditBlockID from '../../hook/useChangeEditBlockID'; +import useBlockAutoFocus from '../../hook/useBlockAutoFocus'; +import { isTouchScreen } from '../../hook/headless/useDnDList'; +import { + Block, + Divider, + Dropdown, + IconButton, + Input, + Switch, + classMerge, +} from '@google-form-clone/shared-ui'; + +interface QuestionBlockProps extends React.ComponentPropsWithRef<'div'> { questionID: string; handleDrag?: (e: React.MouseEvent | React.TouchEvent) => void; } -const QuestionBlock = ({ questionID, handleDrag, ...props }: QuestionBlockProps) => { +const QuestionBlock = ({ + questionID, + handleDrag, + ...props +}: QuestionBlockProps) => { const dispatch = useAppDispatch(); const { changeEditingBlockID, isEditing } = useChangeEditBlockID(questionID); const { containerRef, questionInputRef } = useBlockAutoFocus(questionID); @@ -42,16 +56,29 @@ const QuestionBlock = ({ questionID, handleDrag, ...props }: QuestionBlockProps) const NEW_QUESTION_ID = v4(); dispatch( - copyQuestion({ ...questionInfo, questionID: NEW_QUESTION_ID, answerIDList: [], parentQuestionID: questionID }) + copyQuestion({ + ...questionInfo, + questionID: NEW_QUESTION_ID, + answerIDList: [], + parentQuestionID: questionID, + }) ); answerIDList.map((aID) => { - dispatch(addAnswer({ ...answerMap[aID], answerID: v4(), questionID: NEW_QUESTION_ID })); + dispatch( + addAnswer({ + ...answerMap[aID], + answerID: v4(), + questionID: NEW_QUESTION_ID, + }) + ); }); }; const changeQuestionType = (idx: number) => { - dispatch(editQuestion({ ...questionInfo, type: EDITOR_DROPDOWN_LIST[idx].type })); + dispatch( + editQuestion({ ...questionInfo, type: EDITOR_DROPDOWN_LIST[idx].type }) + ); if ( EDITOR_DROPDOWN_LIST[idx].type === EDITOR_QUESTION_TYPE.long || @@ -61,12 +88,14 @@ const QuestionBlock = ({ questionID, handleDrag, ...props }: QuestionBlockProps) dispatch(removeAnswer(answerMap[aID])); }); - dispatch(addAnswer({ answerID: v4(), content: "", questionID })); + dispatch(addAnswer({ answerID: v4(), content: '', questionID })); } }; const changeQuestionContent = (e: React.ChangeEvent) => { - dispatch(editQuestion({ ...questionInfo, questionContent: e.target.value })); + dispatch( + editQuestion({ ...questionInfo, questionContent: e.target.value }) + ); }; const changeRequired = () => { @@ -87,7 +116,10 @@ const QuestionBlock = ({ questionID, handleDrag, ...props }: QuestionBlockProps) onTouchStart={handleDrag} >
@@ -110,8 +142,10 @@ const QuestionBlock = ({ questionID, handleDrag, ...props }: QuestionBlockProps) ) : (
- {questionContent.length === 0 ? "질문" : questionContent} - {required && *} + {questionContent.length === 0 ? '질문' : questionContent} + {required && ( + * + )}
)}
@@ -127,7 +161,12 @@ const QuestionBlock = ({ questionID, handleDrag, ...props }: QuestionBlockProps) - + )} diff --git a/apps/googleFormClone/src/component/editor/TitleBlock.tsx b/apps/googleFormClone/src/component/editor/TitleBlock.tsx index 7a095bd..7f0242b 100644 --- a/apps/googleFormClone/src/component/editor/TitleBlock.tsx +++ b/apps/googleFormClone/src/component/editor/TitleBlock.tsx @@ -1,17 +1,16 @@ -import { useAppDispatch, useAppSelector } from "../../hook/useRedux"; -import useChangeEditBlockID from "../../hook/useChangeEditBlockID"; -import { editContent, editTitle } from "../../store/reducer/docsSlice"; -import Block from "../common/Block"; -import Input from "../common/Input"; -import TextArea from "../common/TextArea"; -import useBlockAutoFocus from "../../hook/useBlockAutoFocus"; +import { useAppDispatch, useAppSelector } from '../../hook/useRedux'; +import useChangeEditBlockID from '../../hook/useChangeEditBlockID'; +import { editContent, editTitle } from '../../store/reducer/docsSlice'; +import useBlockAutoFocus from '../../hook/useBlockAutoFocus'; +import { Block, Input, TextArea } from '@google-form-clone/shared-ui'; const TitleBlock = () => { - const TITLE_BLOCK_ID = "title"; + const TITLE_BLOCK_ID = 'title'; const dispatch = useAppDispatch(); const { title, content } = useAppSelector((store) => store.docs); - const { changeEditingBlockID, isEditing } = useChangeEditBlockID(TITLE_BLOCK_ID); + const { changeEditingBlockID, isEditing } = + useChangeEditBlockID(TITLE_BLOCK_ID); const { containerRef, questionInputRef } = useBlockAutoFocus(TITLE_BLOCK_ID); const changeTitle = (e: React.ChangeEvent) => { @@ -37,7 +36,11 @@ const TitleBlock = () => { placeholder="제목을 입력하세요" innerRef={questionInputRef} /> -