diff --git a/src/components/atoms/CommentBox.tsx b/src/components/atoms/CommentBox.tsx index 3846d4f6..d0870721 100644 --- a/src/components/atoms/CommentBox.tsx +++ b/src/components/atoms/CommentBox.tsx @@ -3,16 +3,13 @@ import React from "react"; import styled from "styled-components"; import { font } from "@/styles/font"; import Emoji from "@/global/assets/svgs/Emoji"; +import useEmoji from "@/hooks/useEmoji"; import AnonymousBox from "@/components/atoms/AnonymousBox"; -import EmojiModal from "../common/Modal/EmojiModal"; +import EmojiModal from "@/components/common/Modal/EmojiModal"; const CommentBox = () => { const [isAnonymous, setIsAnonymous] = React.useState(false); - const [isOpenEmojiModal, setIsOpenEmojiModal] = React.useState(false); - - const handleEmojiButtonClick = () => { - setIsOpenEmojiModal(true); - }; + const { openEmoji, closeEmoji, visible } = useEmoji(); return ( @@ -20,15 +17,10 @@ const CommentBox = () => { - {isOpenEmojiModal && ( - <> - - - - setIsOpenEmojiModal(false)} /> - + {visible && ( + )} - + @@ -94,19 +86,4 @@ const CommentUploadButton = styled.button` } `; -const EmojiBox = styled.div` - position: absolute; - top: -24%; - right: 84%; - z-index: 10; -`; - -const ModalBackground = styled.div` - width: 100vw; - height: 100vh; - position: fixed; - top: 0; - left: 0; -`; - export default CommentBox; diff --git a/src/components/atoms/CustomEditor.tsx b/src/components/atoms/CustomEditor.tsx index 5cd2e946..a3555f3a 100644 --- a/src/components/atoms/CustomEditor.tsx +++ b/src/components/atoms/CustomEditor.tsx @@ -2,6 +2,7 @@ import React from "react"; import { Editor as TinymcEditor } from "@tinymce/tinymce-react"; import { font } from "@/styles/font"; import styled from "styled-components"; +import useEmoji from "@/hooks/useEmoji"; import EmojiModal from "../common/Modal/EmojiModal"; interface IBlobInfo { @@ -16,11 +17,7 @@ interface IBlobInfo { const CustomEditor = () => { const [content, setContent] = React.useState(""); - const [isOpenEmojiModal, setIsOpenEmojiModal] = React.useState(false); - - const handleEmojiButtonClick = () => { - setIsOpenEmojiModal(true); - }; + const { openEmoji, closeEmoji, visible } = useEmoji(); const imagesUploadHandler = async (blobInfo: IBlobInfo): Promise => { return new Promise(() => { @@ -31,14 +28,7 @@ const CustomEditor = () => { return ( - {isOpenEmojiModal && ( - <> - - - - setIsOpenEmojiModal(false)} /> - - )} + {visible && } { setup: (tinymceEditor) => { tinymceEditor.ui.registry.addButton("emoticon", { icon: "emoji", - onAction: handleEmojiButtonClick, + onAction: openEmoji, }); }, relative_urls: false, @@ -114,19 +104,4 @@ const Container = styled.div` position: relative; `; -const EmojiBox = styled.div` - position: absolute; - left: 54%; - top: 14%; - z-index: 10; -`; - -const ModalBackground = styled.div` - width: 100vw; - height: 100vh; - position: fixed; - top: 0; - left: 0; -`; - export default CustomEditor; diff --git a/src/components/common/Modal/EmojiModal/ModalHeader.tsx b/src/components/common/Modal/EmojiModal/ModalHeader.tsx index ec9569da..45788cad 100644 --- a/src/components/common/Modal/EmojiModal/ModalHeader.tsx +++ b/src/components/common/Modal/EmojiModal/ModalHeader.tsx @@ -1,20 +1,29 @@ +import XIcon from "@/global/assets/svgs/XIcon"; import HoldingBackTears from "@/global/assets/svgs/emojis/HoldingBackTears"; +import color from "@/styles/color"; import { font } from "@/styles/font"; import React from "react"; import styled from "styled-components"; -const ModalHeader = () => { +interface IModalHeaderProps { + handleClickCloseButton: () => void; +} + +const ModalHeader = ({ handleClickCloseButton }: IModalHeaderProps) => { return ( - + + <CloseButton onClick={handleClickCloseButton}> + <XIcon width={10} height={10} /> + </CloseButton> </Container> ); }; const Container = styled.header` width: 100%; - padding: 8px 0 8px 12px; + padding: 10px 0 10px 14px; display: flex; gap: 6px; align-items: center; @@ -22,11 +31,17 @@ const Container = styled.header` `; const Title = styled.span` - ${font.H6}; + ${font.p3}; + font-weight: 600; + color: ${color.black}; &:after { - content: "이모지"; + content: "이모티콘"; } `; +const CloseButton = styled.button` + margin: 0 20px 0 auto; +`; + export default ModalHeader; diff --git a/src/components/common/Modal/EmojiModal/index.tsx b/src/components/common/Modal/EmojiModal/index.tsx index c0e740df..00b04ac3 100644 --- a/src/components/common/Modal/EmojiModal/index.tsx +++ b/src/components/common/Modal/EmojiModal/index.tsx @@ -1,25 +1,60 @@ import color from "@/styles/color"; import React from "react"; -import styled from "styled-components"; -import ModalHeader from "./ModalHeader"; +import styled, { css } from "styled-components"; +import useModal from "@/hooks/useModal"; import ModalList from "./ModalList"; +import ModalHeader from "./ModalHeader"; + +interface IEmojiModalProps { + top?: string; + right?: string; + bottom?: string; + left?: string; + onClose: () => void; +} + +const EmojiModal = (direction: IEmojiModalProps) => { + const { onClose } = direction; -const EmojiModal = () => { return ( - <Container> - <ModalHeader /> - <ModalList /> - </Container> + <> + <Container {...direction}> + <ModalHeader handleClickCloseButton={onClose} /> + <ModalList /> + </Container> + <ModalBackground onClick={onClose} /> + </> ); }; -const Container = styled.div` +const Container = styled.div<{ + top?: string; + right?: string; + bottom?: string; + left?: string; +}>` + position: absolute; + z-index: 10; width: 30vw; height: fit-content; display: flex; flex-direction: column; background-color: ${color.white}; box-shadow: 2px 2px 10px 0 rgba(0, 0, 0, 0.1); + ${({ top, right, bottom, left }) => css` + top: ${top}; + right: ${right}; + bottom: ${bottom}; + left: ${left}; + `} +`; + +const ModalBackground = styled.div` + width: 100vw; + height: 100vh; + position: fixed; + top: 0; + left: 0; `; export default EmojiModal; diff --git a/src/global/assets/svgs/XIcon.tsx b/src/global/assets/svgs/XIcon.tsx new file mode 100644 index 00000000..c64eac5b --- /dev/null +++ b/src/global/assets/svgs/XIcon.tsx @@ -0,0 +1,21 @@ +import React from "react"; + +const XIcon = ({ ...props }: React.SVGAttributes<HTMLOrSVGElement>) => { + return ( + <svg + width="14" + height="14" + {...props} + viewBox="0 0 9 9" + fill="none" + xmlns="http://www.w3.org/2000/svg" + > + <path + d="M1.04983 0.228055C1.1194 0.158391 1.20201 0.103125 1.29296 0.0654173C1.3839 0.0277098 1.48138 0.00830078 1.57983 0.00830078C1.67828 0.00830078 1.77577 0.0277098 1.86671 0.0654173C1.95765 0.103125 2.04027 0.158391 2.10983 0.228055L4.82983 2.94706L7.54983 0.228055C7.61943 0.158455 7.70206 0.103244 7.793 0.0655768C7.88394 0.0279092 7.9814 0.00852204 8.07983 0.00852203C8.17826 0.00852203 8.27573 0.0279092 8.36667 0.0655768C8.4576 0.103244 8.54023 0.158455 8.60983 0.228055C8.67943 0.297656 8.73464 0.380284 8.77231 0.471221C8.80998 0.562159 8.82937 0.659625 8.82937 0.758055C8.82937 0.856485 8.80998 0.953952 8.77231 1.04489C8.73464 1.13583 8.67943 1.21845 8.60983 1.28806L5.89083 4.00806L8.60983 6.72806C8.7504 6.86862 8.82937 7.05927 8.82937 7.25806C8.82937 7.45684 8.7504 7.64749 8.60983 7.78805C8.46927 7.92862 8.27862 8.00759 8.07983 8.00759C7.88104 8.00759 7.6904 7.92862 7.54983 7.78805L4.82983 5.06906L2.10983 7.78805C1.96927 7.92862 1.77862 8.00759 1.57983 8.00759C1.38104 8.00759 1.1904 7.92862 1.04983 7.78805C0.909268 7.64749 0.830299 7.45684 0.830299 7.25806C0.830299 7.05927 0.909268 6.86862 1.04983 6.72806L3.76883 4.00806L1.04983 1.28806C0.980168 1.21849 0.924902 1.13587 0.887195 1.04493C0.849487 0.953989 0.830078 0.856505 0.830078 0.758055C0.830078 0.659605 0.849487 0.562121 0.887195 0.471179C0.924902 0.380237 0.980168 0.29762 1.04983 0.228055Z" + fill="black" + /> + </svg> + ); +}; + +export default XIcon; diff --git a/src/global/types/emoji.type.ts b/src/global/types/emoji.type.ts new file mode 100644 index 00000000..1d3a2cc9 --- /dev/null +++ b/src/global/types/emoji.type.ts @@ -0,0 +1,3 @@ +export default interface IEmojiState { + visible: boolean; +} diff --git a/src/hooks/useEmoji.ts b/src/hooks/useEmoji.ts new file mode 100644 index 00000000..0fab24b6 --- /dev/null +++ b/src/hooks/useEmoji.ts @@ -0,0 +1,19 @@ +import emojiStore from "@/store/emoji.store"; +import { useCallback } from "react"; +import { useRecoilState } from "recoil"; + +const useEmoji = () => { + const [emoji, setEmoji] = useRecoilState(emojiStore); + + const openEmoji = useCallback(() => { + setEmoji({ visible: true }); + }, [setEmoji]); + + const closeEmoji = useCallback(() => { + setEmoji({ visible: false }); + }, [setEmoji]); + + return { openEmoji, closeEmoji, visible: emoji.visible }; +}; + +export default useEmoji; diff --git a/src/page/calender/layouts/ScheduleBox.tsx b/src/page/calender/layouts/ScheduleBox.tsx index 64d3ad0b..2331a422 100644 --- a/src/page/calender/layouts/ScheduleBox.tsx +++ b/src/page/calender/layouts/ScheduleBox.tsx @@ -16,7 +16,7 @@ const ScheduleBox = () => { }; const Container = styled.main` - width: 76%; + width: 67%; background-color: ${color.white}; border-radius: 5px; display: flex; diff --git a/src/store/emoji.store.ts b/src/store/emoji.store.ts new file mode 100644 index 00000000..4d6a53d7 --- /dev/null +++ b/src/store/emoji.store.ts @@ -0,0 +1,11 @@ +import IEmojiState from "@/global/types/emoji.type"; +import { atom } from "recoil"; + +const emojiStore = atom<IEmojiState>({ + key: "emojiStore", + default: { + visible: false, + }, +}); + +export default emojiStore;