diff --git a/index.html b/index.html index e57d567..80aec14 100644 --- a/index.html +++ b/index.html @@ -16,6 +16,7 @@
+ diff --git a/src/app.jsx b/src/app.jsx index 7fb23d8..47815ca 100644 --- a/src/app.jsx +++ b/src/app.jsx @@ -1,18 +1,27 @@ import { BrowserRouter, Route, Routes } from "react-router"; +import ModalProvider from "./components/modal/modal-provider"; import DropdownProvider from "./components/text-field/dropdown-input/dropdown-provider"; import MessagePage from "./pages/message-list"; import TestPage from "./pages/test-page"; +function Provider({ children }) { + return ( + + {children} + + ); +} + function App() { return ( - + } /> } /> - + ); } diff --git a/src/assets/ic-person.svg b/src/assets/ic-person.svg new file mode 100644 index 0000000..9aa412e --- /dev/null +++ b/src/assets/ic-person.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/components/modal/modal-context.js b/src/components/modal/modal-context.js new file mode 100644 index 0000000..2cee28d --- /dev/null +++ b/src/components/modal/modal-context.js @@ -0,0 +1,5 @@ +import { createContext } from "react"; + +const ModalContext = createContext(); + +export default ModalContext; diff --git a/src/components/modal/modal-provider.jsx b/src/components/modal/modal-provider.jsx new file mode 100644 index 0000000..f7b6d44 --- /dev/null +++ b/src/components/modal/modal-provider.jsx @@ -0,0 +1,10 @@ +import { useState } from "react"; +import ModalContext from "./modal-context"; + +function ModalProvider({ children }) { + const [showsModal, setShowsModal] = useState(false); + const value = { showsModal, setShowsModal }; + return {children}; +} + +export default ModalProvider; diff --git a/src/components/modal/modal.jsx b/src/components/modal/modal.jsx new file mode 100644 index 0000000..d8b4aa2 --- /dev/null +++ b/src/components/modal/modal.jsx @@ -0,0 +1,208 @@ +import { createPortal } from "react-dom"; +import styled, { css } from "styled-components"; +import defaultProfileImg from "../../assets/ic-person.svg"; +import { useModal } from "../../hooks/use-modal"; +import { formatDate } from "../../utils/formatter"; +import Badge from "../badge/badge"; +import BADGE_TYPE from "../badge/badge-type"; +import { PrimaryButton } from "../button/button"; +import BUTTON_SIZE from "../button/button-size"; +import Colors from "../color/colors"; + +/* ProfileImage */ + +const profileImageStyle = css` + width: 56px; + height: 56px; + border-radius: 28px; +`; + +const UserProfileImage = styled.div` + ${profileImageStyle} + + img { + width: 100%; + height: 100%; + } +`; + +const DefaultProfileImage = styled.div` + ${profileImageStyle} + background-color: ${Colors.gray(300)}; + display: flex; + justify-content: center; + align-items: center; + + img { + width: 32px; + height: 32px; + } +`; + +function ProfileImage({ profileImg }) { + const img = 프로필 사진; + return profileImg ? ( + {img} + ) : ( + {img} + ); +} + +/* UserInfo */ + +const Name = styled.span` + font-size: 20px; + font-weight: 400; + line-height: 24px; + + span { + font-weight: 700; + } +`; + +const StyledUserInfo = styled.div` + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 6px; +`; + +function UserInfo({ name, type }) { + return ( + + + From.{` ${name}`} + + + + ); +} + +/* UserProfile */ + +const StyledUserProfile = styled.div` + display: flex; + align-items: center; + gap: 16px; +`; + +function UserProfile({ profileImg, name }) { + return ( + + + + + ); +} + +/* Header */ + +const Date = styled.span` + font-size: 14px; + font-weight: 400; + line-height: 20px; + color: ${Colors.gray(400)}; +`; + +const StyledHeader = styled.div` + width: 100%; + border-bottom: 1px solid ${Colors.gray(200)}; + + & > div { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 19px; + } +`; + +function Header({ profileImg, name, date }) { + return ( + +
+ + {formatDate(date, ".")} +
+
+ ); +} + +/* Modal */ + +const Content = styled.p` + margin: 16px 0 24px; + height: 240px; + overflow: scroll; + font-size: 18px; + font-weight: 400; + line-height: 28px; + color: #5a5a5a; + padding-right: 16px; + + &::-webkit-scrollbar { + width: 4px; + height: 0px; + } + + &::-webkit-scrollbar-thumb { + background-color: ${Colors.gray(300)}; + border-radius: 2px; + + &:hover { + background-color: ${Colors.gray(400)}; + } + } +`; + +const StyledModal = styled.div` + background-color: white; + width: 600px; + border-radius: 16px; + box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.08); + padding: 40px; + display: flex; + flex-direction: column; + align-items: center; +`; + +/* Container */ + +const ModalContainer = styled.div` + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.6); + display: flex; + justify-content: center; + align-items: center; +`; + +function Modal({ user, date, content }) { + const { setShowsModal } = useModal(); + + const ModalPortal = ({ children }) => { + return createPortal(children, document.getElementById("modal")); + }; + + const handleConfirmClick = () => setShowsModal(false); + + return ( + + + +
+ {content} + + + + + ); +} + +export default Modal; diff --git a/src/hooks/use-modal.jsx b/src/hooks/use-modal.jsx new file mode 100644 index 0000000..d215967 --- /dev/null +++ b/src/hooks/use-modal.jsx @@ -0,0 +1,9 @@ +import { useContext } from "react"; +import ModalContext from "../components/modal/modal-context"; + +function useModal() { + const { showsModal, setShowsModal } = useContext(ModalContext); + return { showsModal, setShowsModal }; +} + +export { useModal }; diff --git a/src/pages/test-page.jsx b/src/pages/test-page.jsx index f0ddc35..09c822d 100644 --- a/src/pages/test-page.jsx +++ b/src/pages/test-page.jsx @@ -12,9 +12,11 @@ import { } from "../components/button/button"; import BUTTON_SIZE from "../components/button/button-size"; import ToggleButton from "../components/button/toggle-button"; +import Modal from "../components/modal/modal"; import TextField from "../components/text-field/text-field"; import TEXT_FIELD_TYPE from "../components/text-field/text-field-type"; import Toast from "../components/toast/toast"; +import { useModal } from "../hooks/use-modal"; import { useToast } from "../hooks/use-toast"; function TestPage() { @@ -36,7 +38,11 @@ function TestPage() { const handleToastClick = () => setShowsToast(true); const handleToastDismiss = () => setShowsToast(false); - + + /* Modal */ + const { showsModal, setShowsModal } = useModal(); + const handleModalClick = () => setShowsModal(true); + return (
)}
+
+ + {showsModal && ( + + )} +
); } diff --git a/src/utils/formatter.js b/src/utils/formatter.js new file mode 100644 index 0000000..14596a1 --- /dev/null +++ b/src/utils/formatter.js @@ -0,0 +1,8 @@ +function formatDate(date, token = "-") { + const year = date.getFullYear(); + const month = `${date.getMonth() + 1}`.padStart(2, "0"); + const day = `${date.getDate()}`.padStart(2, "0"); + return `${year}${token}${month}${token}${day}`; +} + +export { formatDate };