From fe30cab457640c23b1e18338b86b0be894559631 Mon Sep 17 00:00:00 2001 From: Kim DoKyun <51306225+jasper200207@users.noreply.github.com> Date: Thu, 16 May 2024 19:02:16 +0900 Subject: [PATCH] =?UTF-8?q?Feature/#187=20=ED=95=99=EC=8A=B5=EC=9E=90?= =?UTF-8?q?=EB=A3=8C=20=EC=83=9D=EC=84=B1=EB=AA=A8=EB=8B=AC=20(#212)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat : 제목, 소개, 공개범위 #187 * feat : 파일 삽입 기능 추가 #187 * design : 추가하기 버튼 그림자 추가 #187 * design : divider 위치 변경 #187 * design : minH : 40px 추가 #187 --- src/components/IconBox/index.tsx | 2 +- src/containers/study/DocumentModal/index.tsx | 165 +++++++++++++++++++ src/containers/study/DocumentModal/type.ts | 15 ++ 3 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 src/containers/study/DocumentModal/index.tsx create mode 100644 src/containers/study/DocumentModal/type.ts diff --git a/src/components/IconBox/index.tsx b/src/components/IconBox/index.tsx index 12791e9e..279f742c 100644 --- a/src/components/IconBox/index.tsx +++ b/src/components/IconBox/index.tsx @@ -4,7 +4,7 @@ import { IconBoxProps } from './type'; const IconBox = ({ leftIcon, content, rightIcon, handleClick }: IconBoxProps) => { return ( - <Flex align="center" gap="1" w="100%" h="40px" color="white" bg="orange_light" borderRadius="2xl"> + <Flex align="center" gap="1" w="100%" h="40px" minH="40px" color="white" bg="orange_light" borderRadius="2xl"> <IconButton as="div" flexShrink="0" aria-label="" icon={leftIcon} size="icon_md" variant="transparent" /> <Text textStyle="bold_md" flex="auto" cursor="default" isTruncated> {content} diff --git a/src/containers/study/DocumentModal/index.tsx b/src/containers/study/DocumentModal/index.tsx new file mode 100644 index 00000000..d5dacda2 --- /dev/null +++ b/src/containers/study/DocumentModal/index.tsx @@ -0,0 +1,165 @@ +'use client'; + +import { Divider, Flex, Input, Text, Textarea, Button } from '@chakra-ui/react'; +import { ChangeEvent, useRef, useState } from 'react'; +import { BiFile, BiImage, BiTrash } from 'react-icons/bi'; +import { BsLink45Deg } from 'react-icons/bs'; + +import IconBox from '@/components/IconBox'; +import ActionModal from '@/components/Modal/ActionModal'; +import StyledRadio from '@/components/StyledRadio'; +import StyledRadioGroup from '@/components/StyledRadioGroup'; +import color from '@/constants/color'; +import { DocumentModalProps, DocumentType, DocumentList } from '@/containers/study/DocumentModal/type'; + +const DocumentBoxIcon = { + img: <BiImage />, + file: <BiFile />, + url: <BsLink45Deg />, +}; + +const DocumentModal = ({ isOpen, onClose }: DocumentModalProps) => { + const [doctype, setDocType] = useState<DocumentType>('img'); + const [docList, setDocList] = useState<DocumentList>({ + img: [], + file: [], + url: [], + }); + const imgInputRef = useRef<HTMLInputElement>(null); + const fileInputRef = useRef<HTMLInputElement>(null); + const urlInputRef = useRef<HTMLInputElement>(null); + + const onConfirmButtonClick = () => { + onClose(); + }; + + const handleGetDoc = { + img: (e: ChangeEvent<HTMLInputElement>) => { + const imgs = Array.from(e.target.files || []); + setDocList((prev) => ({ + ...prev, + img: [ + ...prev.img, + ...imgs.map((img) => ({ + name: img.name, + content: img, + })), + ], + })); + }, + file: (e: ChangeEvent<HTMLInputElement>) => { + const files = Array.from(e.target.files || []); + setDocList((prev) => ({ + ...prev, + file: [ + ...prev.file, + ...files.map((file) => ({ + name: file.name, + content: file, + })), + ], + })); + }, + url: () => { + if (urlInputRef.current?.value) { + const url = urlInputRef.current.value; + setDocList((prev) => ({ + ...prev, + url: [ + ...prev.url, + { + name: url, + content: url, + }, + ], + })); + urlInputRef.current.value = ''; + } + }, + }; + + const handleAddDoc = { + img: () => { + imgInputRef.current?.click(); + }, + file: () => { + fileInputRef.current?.click(); + }, + url: () => { + handleGetDoc.url(); + }, + }; + + const handleRemoveDoc = (index: number) => { + setDocList((prev) => { + const newList = { ...prev }; + newList[doctype] = newList[doctype].filter((_, idx) => idx !== index); + return newList; + }); + }; + + return ( + <ActionModal + isOpen={isOpen} + size="xl" + onClose={onClose} + title="학습자료 등록" + subButtonText="취소" + onSubButtonClick={onClose} + mainButtonText="등록" + onMainButtonClick={onConfirmButtonClick} + > + <Flex direction="column" gap="4"> + <Text textStyle="bold_xl">학습자료 제목</Text> + <Input placeholder="학습자료 제목을 입력해주세요." /> + <Text textStyle="bold_xl">학습자료 소개</Text> + <Textarea placeholder="학습자료 소개를 입력해주세요." /> + <StyledRadioGroup title="파일 유형" value={doctype} onChange={(v) => setDocType(v as DocumentType)}> + <StyledRadio value="img">이미지</StyledRadio> + <StyledRadio value="file">파일</StyledRadio> + <StyledRadio value="url">URL 링크</StyledRadio> + </StyledRadioGroup> + <Flex justify="end" direction="row" gap="4" shrink="0"> + <Input + ref={urlInputRef} + flex="1" + h="7" + shadow="md" + hidden={doctype !== 'url'} + placeholder="URL 링크를 입력해주세요." + /> + <Button w="28" h="7" shadow="md" onClick={() => handleAddDoc[doctype]()} variant="orange"> + 추가하기 + </Button> + </Flex> + <Divider borderWidth="2px" borderColor={color.orange_dark} /> + <input + hidden + type="file" + multiple + accept="image/jpg,image/png,image/jpeg,image/gif" + ref={imgInputRef} + onChange={handleGetDoc.img} + /> + <input hidden type="file" multiple ref={fileInputRef} onChange={handleGetDoc.file} /> + <Flex direction="column" gap="4" overflow="scroll" maxH="52" shrink="0"> + {docList[doctype].map((doc, index) => ( + <IconBox + key={`${doc}`} + leftIcon={DocumentBoxIcon[doctype]} + content={doc.name} + rightIcon={<BiTrash />} + handleClick={() => handleRemoveDoc(index)} + /> + ))} + </Flex> + <StyledRadioGroup title="공개 범위" defaultValue="all"> + <StyledRadio value="all">전체 공개</StyledRadio> + <StyledRadio value="only_study">스터디 공개</StyledRadio> + </StyledRadioGroup> + </Flex> + </ActionModal> + ); +}; + +export default DocumentModal; diff --git a/src/containers/study/DocumentModal/type.ts b/src/containers/study/DocumentModal/type.ts new file mode 100644 index 00000000..b29df881 --- /dev/null +++ b/src/containers/study/DocumentModal/type.ts @@ -0,0 +1,15 @@ +export interface DocumentModalProps { + isOpen: boolean; + onClose: () => void; +} + +export type DocumentType = 'img' | 'file' | 'url'; + +interface Document { + name: string; + content: string | File; +} + +export type DocumentList = { + [key in DocumentType]: Array<Document>; +};