Skip to content

Commit

Permalink
chore: shared-ui로 common 컴포넌트 분리
Browse files Browse the repository at this point in the history
  • Loading branch information
bh2980 committed Jun 5, 2024
1 parent a665d1c commit 4fc2de4
Show file tree
Hide file tree
Showing 40 changed files with 481 additions and 200 deletions.
34 changes: 0 additions & 34 deletions apps/googleFormClone/src/component/common/Input.tsx

This file was deleted.

148 changes: 86 additions & 62 deletions apps/googleFormClone/src/component/editor/AnswerItemManager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);

Expand All @@ -37,26 +42,34 @@ const AnswerManager = ({ questionID }: AnswerManagerProps) => {

const chooseAnswerRef = useRef<(null | HTMLInputElement)[]>([]);

const changeAnswer = (e: React.ChangeEvent<HTMLInputElement>, { answerID, questionID }: AnswerInterface) => {
const changeAnswer = (
e: React.ChangeEvent<HTMLInputElement>,
{ 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();
}
}

if (chooseAnswerRef && chooseAnswerRef.current) {
chooseAnswerRef.current = chooseAnswerRef.current.filter((_, i) => i !== idx);
chooseAnswerRef.current = chooseAnswerRef.current.filter(
(_, i) => i !== idx
);
}

dispatch(removeAnswer(answerMap[aID]));
Expand All @@ -75,56 +88,67 @@ const AnswerManager = ({ questionID }: AnswerManagerProps) => {
return (
<fieldset
className={classMerge([
"flex flex-col gap-4",
(type === EDITOR_QUESTION_TYPE.short || type === EDITOR_QUESTION_TYPE.long) && "mx-[32px]",
'flex flex-col gap-4',
(type === EDITOR_QUESTION_TYPE.short ||
type === EDITOR_QUESTION_TYPE.long) &&
'mx-[32px]',
])}
>
{type === EDITOR_QUESTION_TYPE.short && <div className="text-gray-400">단답형 메시지</div>}
{type === EDITOR_QUESTION_TYPE.long && <div className="text-gray-400">장문형 메시지</div>}
{type !== EDITOR_QUESTION_TYPE.short && type !== EDITOR_QUESTION_TYPE.long && (
<>
<div ref={dragListContainerRef}>
{answerIDList.map((aID, idx) => {
const answerInfo = answerMap[aID];
return (
<ChooseAnswer
key={aID}
idx={idx + 1}
type={type}
value={answerInfo.content}
placeholder={`옵션 ${idx + 1}`}
onChange={(e) => changeAnswer(e, answerInfo)}
isEditing={isEditing}
deletable={answerIDList.length > 1}
onDeleteButton={() => removeAnswerItem(aID, idx)}
handleDrag={handleDrag}
innerRef={(el) => (chooseAnswerRef.current[idx] = el)}
/>
);
})}
</div>
{isEditing && (
<div className="items-center flex gap-4 mx-[32px]">
<div className="flex items-center gap-4">
{type === EDITOR_QUESTION_TYPE.radio && <Radio disabled />}
{type === EDITOR_QUESTION_TYPE.checkbox && <Checkbox disabled />}
{type === EDITOR_QUESTION_TYPE.dropdown && <div className="w-[24px]"></div>}
<span
className="w-full text-gray-500 cursor-text hover:underline decoration-gray-400"
onClick={addAnswerItem}
tabIndex={0}
>
옵션 추가
</span>
</div>
{/* <span>또는</span>
{type === EDITOR_QUESTION_TYPE.short && (
<div className="text-gray-400">단답형 메시지</div>
)}
{type === EDITOR_QUESTION_TYPE.long && (
<div className="text-gray-400">장문형 메시지</div>
)}
{type !== EDITOR_QUESTION_TYPE.short &&
type !== EDITOR_QUESTION_TYPE.long && (
<>
<div ref={dragListContainerRef}>
{answerIDList.map((aID, idx) => {
const answerInfo = answerMap[aID];
return (
<ChooseAnswer
key={aID}
idx={idx + 1}
type={type}
value={answerInfo.content}
placeholder={`옵션 ${idx + 1}`}
onChange={(e) => changeAnswer(e, answerInfo)}
isEditing={isEditing}
deletable={answerIDList.length > 1}
onDeleteButton={() => removeAnswerItem(aID, idx)}
handleDrag={handleDrag}
innerRef={(el) => (chooseAnswerRef.current[idx] = el)}
/>
);
})}
</div>
{isEditing && (
<div className="items-center flex gap-4 mx-[32px]">
<div className="flex items-center gap-4">
{type === EDITOR_QUESTION_TYPE.radio && <Radio disabled />}
{type === EDITOR_QUESTION_TYPE.checkbox && (
<Checkbox disabled />
)}
{type === EDITOR_QUESTION_TYPE.dropdown && (
<div className="w-[24px]"></div>
)}
<span
className="w-full text-gray-500 cursor-text hover:underline decoration-gray-400"
onClick={addAnswerItem}
tabIndex={0}
>
옵션 추가
</span>
</div>
{/* <span>또는</span>
<button type="button" className="text-blue-500">
'기타' 추가
</button> */}
</div>
)}
</>
)}
</div>
)}
</>
)}
</fieldset>
);
};
Expand Down
34 changes: 21 additions & 13 deletions apps/googleFormClone/src/component/editor/ChooseAnswer.tsx
Original file line number Diff line number Diff line change
@@ -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<React.ComponentPropsWithRef<"input">, "type"> {
type: EDITOR_QUESTION_TYPE.radio | EDITOR_QUESTION_TYPE.checkbox | EDITOR_QUESTION_TYPE.dropdown;
interface RadioAnswerProps
extends Omit<React.ComponentPropsWithRef<'input'>, '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;
}

Expand All @@ -37,15 +43,17 @@ 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',
])}
/>
)}
<div className="flex gap-4 px-[32px] w-full items-center">
{type === EDITOR_QUESTION_TYPE.radio && <Radio disabled />}
{type === EDITOR_QUESTION_TYPE.checkbox && <Checkbox disabled />}
{type === EDITOR_QUESTION_TYPE.dropdown && (
<div className="w-[20px] h-[20px] justify-center flex text-gray-500">{idx}</div>
<div className="w-[20px] h-[20px] justify-center flex text-gray-500">
{idx}
</div>
)}
<Input innerRef={innerRef} className="w-full" {...props} />
{deletable && isEditing && (
Expand Down
Loading

0 comments on commit 4fc2de4

Please sign in to comment.