From 00f1bc5c9bc33db60e136a1a7aee832f5bcb2995 Mon Sep 17 00:00:00 2001 From: Ludovico7 Date: Mon, 25 Nov 2024 16:39:44 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=84=A0=ED=83=9D=EB=90=9C=20=ED=85=8D?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=EC=9D=98=20=EC=8A=A4=ED=83=80=EC=9D=BC?= =?UTF-8?q?=EC=97=90=20=EB=94=B0=EB=9D=BC=20=EB=B2=84=ED=8A=BC=20=EB=94=94?= =?UTF-8?q?=EC=9E=90=EC=9D=B8=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TextOptionModal/TextOptionModal.tsx | 154 +++++++++++++++--- .../editor/components/block/Block.tsx | 1 + 2 files changed, 134 insertions(+), 21 deletions(-) diff --git a/client/src/features/editor/components/TextOptionModal/TextOptionModal.tsx b/client/src/features/editor/components/TextOptionModal/TextOptionModal.tsx index 30026afe..3312dda4 100644 --- a/client/src/features/editor/components/TextOptionModal/TextOptionModal.tsx +++ b/client/src/features/editor/components/TextOptionModal/TextOptionModal.tsx @@ -1,9 +1,11 @@ +import { Char } from "@noctaCrdt/Node"; import { motion } from "framer-motion"; import { useEffect, useRef, useState } from "react"; import { createPortal } from "react-dom"; import { modalContainer, optionButton, optionModal } from "./TextOptionModal.style"; interface SelectionModalProps { + selectedNodes: Array | null; isOpen: boolean; onBoldSelect: () => void; onItalicSelect: () => void; @@ -17,6 +19,7 @@ const ModalPortal = ({ children }: { children: React.ReactNode }) => { }; export const TextOptionModal = ({ + selectedNodes, isOpen, onBoldSelect, onItalicSelect, @@ -29,6 +32,12 @@ export const TextOptionModal = ({ top: 0, left: 0, }); + const [styleState, setStyleState] = useState({ + isBold: false, + isItalic: false, + isUnderline: false, + isStrike: false, + }); useEffect(() => { if (!isOpen) return; @@ -60,38 +69,141 @@ export const TextOptionModal = ({ return () => { document.removeEventListener("mousedown", handleClickOutside); + setStyleState({ + isBold: false, + isItalic: false, + isUnderline: false, + isStrike: false, + }); }; }, [isOpen, onClose]); + useEffect(() => { + if (!selectedNodes || selectedNodes.length === 0) { + setStyleState({ + isBold: false, + isItalic: false, + isUnderline: false, + isStrike: false, + }); + return; + } + + // 각 스타일의 출현 횟수를 계산 + const styleCounts = { + bold: 0, + italic: 0, + underline: 0, + strike: 0, + }; + + // 각 노드의 스타일을 확인하고 카운트 + selectedNodes.forEach((node) => { + // node.style이 undefined인 경우 빈 배열로 처리 + const nodeStyles = Array.isArray(node.style) ? node.style : node.style ? [node.style] : []; + + if (nodeStyles.includes("bold")) styleCounts.bold += 1; + if (nodeStyles.includes("italic")) styleCounts.italic += 1; + if (nodeStyles.includes("underline")) styleCounts.underline += 1; + if (nodeStyles.includes("strike")) styleCounts.strike += 1; + }); + + const totalNodes = selectedNodes.length; + + // 모든 노드가 해당 스타일을 가지고 있는 경우에만 true + setStyleState({ + isBold: styleCounts.bold === totalNodes, + isItalic: styleCounts.italic === totalNodes, + isUnderline: styleCounts.underline === totalNodes, + isStrike: styleCounts.strike === totalNodes, + }); + }, [selectedNodes]); + if (!isOpen) return; return ( - {isOpen && ( - -
- - + - + - + -
-
- )} + + + +
); }; diff --git a/client/src/features/editor/components/block/Block.tsx b/client/src/features/editor/components/block/Block.tsx index c2419ede..78cc5c07 100644 --- a/client/src/features/editor/components/block/Block.tsx +++ b/client/src/features/editor/components/block/Block.tsx @@ -217,6 +217,7 @@ export const Block: React.FC = memo( /> handleStyleSelect("bold")}