Skip to content
Merged
40 changes: 40 additions & 0 deletions src/components/modal/modal-dialog.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import styled from "styled-components";
import { PrimaryButton } from "../button/button";
import BUTTON_SIZE from "../button/button-size";
import Colors from "../color/colors";

const Title = styled.h2`
margin: 0;
color: ${Colors.gray(600)};
`;

const Content = styled.p`
margin: 0;
color: ${Colors.gray(600)};
`;

const Action = styled.div`
display: flex;
justify-content: flex-end;
gap: 16px;
`;

const StyledAlertDialog = styled.div`
display: flex;
flex-direction: column;
gap: 24px;
`;

function ModalDialog({ title, content, action }) {
return (
<StyledAlertDialog>
<Title>{title}</Title>
{content && <Content>{content}</Content>}
<Action>
{action ?? <PrimaryButton size={BUTTON_SIZE.medium} title="확인" />}
</Action>
</StyledAlertDialog>
);
}

export default ModalDialog;
29 changes: 3 additions & 26 deletions src/components/modal/modal.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import styled from "styled-components";
import { useModal } from "../../hooks/use-modal";
import { PrimaryButton } from "../button/button";
import BUTTON_SIZE from "../button/button-size";
import Portal from "../portal/portal";

const Content = styled.div`
Expand Down Expand Up @@ -34,34 +31,14 @@ const ModalContainer = styled.div`
align-items: center;
`;

const ActionButton = styled.div`
cursor: pointer;
`;

function Modal({ id, action, children }) {
const { showsModal, setShowsModal } = useModal({
id: id,
type: "modal",
});

const handleClick = () => setShowsModal(true);
const handleConfirmClick = () => setShowsModal(false);

function Modal({ shows, children }) {
return (
<>
<ActionButton onClick={handleClick}>{action}</ActionButton>
{showsModal && (
{shows && (
<Portal id="modal">
<ModalContainer>
<StyledModal>
<Content>
{children}
<PrimaryButton
size={BUTTON_SIZE.medium}
title="확인"
onClick={handleConfirmClick}
/>
</Content>
<Content>{children}</Content>
</StyledModal>
</ModalContainer>
</Portal>
Expand Down
23 changes: 21 additions & 2 deletions src/features/message/components/message-card-detail.jsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import styled from "styled-components";
import { PrimaryButton } from "../../../components/button/button";
import BUTTON_SIZE from "../../../components/button/button-size";
import Colors from "../../../components/color/colors";
import { formatDate } from "../../../utils/formatter";
import MessageSender from "./message-sender";

const Header = styled.header`
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
Expand All @@ -12,6 +15,7 @@ const Header = styled.header`
`;

const Content = styled.div`
width: 100%;
margin-top: 16px;
font-size: 18px;
font-weight: 400;
Expand All @@ -34,16 +38,24 @@ const Content = styled.div`
}
`;

const Action = styled.div`
padding-top: 24px;
`;

const CreatedDate = styled.span`
font-size: 14px;
font-weight: 400;
line-height: 20px;
color: ${Colors.gray(400)};
`;

const StyledMessageCardDetail = styled.div``;
const StyledMessageCardDetail = styled.div`
display: flex;
flex-direction: column;
align-items: center;
`;

function MessageCardDetail({ message }) {
function MessageCardDetail({ message, onConfirm }) {
return (
<StyledMessageCardDetail>
<Header>
Expand All @@ -55,6 +67,13 @@ function MessageCardDetail({ message }) {
<CreatedDate>{formatDate(message.createdAt, ".")}</CreatedDate>
</Header>
<Content>{message.content}</Content>
<Action>
<PrimaryButton
size={BUTTON_SIZE.medium}
title="확인"
onClick={onConfirm}
/>
</Action>
</StyledMessageCardDetail>
);
}
Expand Down
17 changes: 14 additions & 3 deletions src/features/message/components/message-card.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,22 @@ const StyledMessageCard = styled.article`
border-radius: 16px;
background-color: white;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.08);
cursor: ${({ $isEditing }) => ($isEditing ? "default" : "pointer")};
`;

function MessageCard({ isEditing, message, onDelete }) {
function MessageCard({ isEditing, message, onClick, onDelete }) {
const handleClick = () => {
if (isEditing) return;
onClick(message);
};

const handleDeleteClick = (event) => {
event.stopPropagation();
onDelete(message);
};

return (
<StyledMessageCard>
<StyledMessageCard $isEditing={isEditing} onClick={handleClick}>
<Header>
<MessageSender
profileImageUrl={message.profileImageURL}
Expand All @@ -65,7 +76,7 @@ function MessageCard({ isEditing, message, onDelete }) {
<OutlinedButton
size={BUTTON_SIZE.medium}
icon={deleteImage}
onClick={onDelete}
onClick={handleDeleteClick}
/>
)}
</Header>
Expand Down
62 changes: 38 additions & 24 deletions src/features/message/components/messages-grid.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { useRef } from "react";
import { useRef, useState } from "react";
import { useNavigate, useParams } from "react-router";
import styled from "styled-components";
import Modal from "../../../components/modal/modal.jsx";
import { useIntersectionObserver } from "../../../hooks/use-intersection-observer.jsx";
import { useModal } from "../../../hooks/use-modal.jsx";
import { media } from "../../../utils/media.js";
import MessageCardAdd from "./message-card-add.jsx";
import MessageCardDetail from "./message-card-detail.jsx";
Expand All @@ -28,6 +29,10 @@ function MessagesGrid({ isEditing, messages, onDelete, onInfiniteScroll }) {
const navigate = useNavigate();
const { id } = useParams();
const infiniteScrollTargetRef = useRef();
const { showsModal, setShowsModal } = useModal({
key: "message-modal",
});
const [modalMessage, setModalMessage] = useState(null);

const observerCallback = (entry) => {
if (!entry.isIntersecting) return;
Expand All @@ -39,33 +44,42 @@ function MessagesGrid({ isEditing, messages, onDelete, onInfiniteScroll }) {
navigate(`/post/${id}/message`);
};

const handleDeleteClick = (messageId) => {
onDelete(messageId);
const handleMessageClick = (message) => {
setShowsModal(true);
setModalMessage(message);
};

const messageCard = (message) => (
<MessageCard
key={message.id}
isEditing={isEditing}
message={message}
onDelete={() => handleDeleteClick(message.id)}
/>
);
const handleDeleteClick = (message) => {
onDelete(message);
};

const handleModalConfirm = () => {
setShowsModal(false);
setModalMessage(null);
};

return (
<StyledRollingPaperMessagesGrid>
<MessageCardAdd onClick={handleAddClick} />
{messages.map((message) =>
isEditing ? (
messageCard(message)
) : (
<Modal key={message.id} id={message.id} action={messageCard(message)}>
<MessageCardDetail message={message} />
</Modal>
)
)}
<div ref={infiniteScrollTargetRef}></div>
</StyledRollingPaperMessagesGrid>
<>
<StyledRollingPaperMessagesGrid>
<MessageCardAdd onClick={handleAddClick} />
{messages.map((message) => (
<MessageCard
key={message.id}
isEditing={isEditing}
message={message}
onClick={handleMessageClick}
onDelete={handleDeleteClick}
/>
))}
<div ref={infiniteScrollTargetRef}></div>
</StyledRollingPaperMessagesGrid>
<Modal shows={showsModal && modalMessage != null}>
<MessageCardDetail
message={modalMessage}
onConfirm={handleModalConfirm}
/>
</Modal>
</>
);
}

Expand Down
41 changes: 41 additions & 0 deletions src/hooks/use-modal-dialog.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { useState } from "react";
import { useModal } from "./use-modal";

function useModalDialog() {
const { showsModal, setShowsModal } = useModal({
key: "delete-modal",
});
const [title, setTitle] = useState("");
const [content, setContent] = useState("");
const [primaryAction, setPrimaryAction] = useState(null);

const openDialog = ({ title, content, primaryAction }) => {
setShowsModal(true);
setTitle(title);
setContent(content);
setPrimaryAction(() => primaryAction);
};

const closeDialog = () => {
setShowsModal(false);
setTitle("");
setContent("");
setPrimaryAction(null);
};

const onPrimaryAction = () => {
primaryAction();
closeDialog();
};

return {
showsDialog: showsModal,
dialogTitle: title,
dialogContent: content,
openDialog,
closeDialog,
onPrimaryAction,
};
}

export { useModalDialog };
3 changes: 1 addition & 2 deletions src/hooks/use-modal.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { usePortal } from "./use-portal";

function useModal({ id, type }) {
const key = `${type}_${id}`;
function useModal({ key }) {
const { isOpen, setIsOpen } = usePortal({ key });
return { showsModal: isOpen, setShowsModal: setIsOpen };
}
Expand Down
Loading