Skip to content

Commit

Permalink
feat: 선택된 텍스트의 스타일에 따라 버튼 디자인 변경
Browse files Browse the repository at this point in the history
  • Loading branch information
Ludovico7 committed Nov 25, 2024
1 parent 087720c commit 00f1bc5
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 21 deletions.
Original file line number Diff line number Diff line change
@@ -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<Char> | null;
isOpen: boolean;
onBoldSelect: () => void;
onItalicSelect: () => void;
Expand All @@ -17,6 +19,7 @@ const ModalPortal = ({ children }: { children: React.ReactNode }) => {
};

export const TextOptionModal = ({
selectedNodes,
isOpen,
onBoldSelect,
onItalicSelect,
Expand All @@ -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;
Expand Down Expand Up @@ -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 (
<ModalPortal>
{isOpen && (
<motion.div
ref={modalRef}
className={optionModal}
style={{
left: `${TextModalPosition.left}px`,
top: `${TextModalPosition.top}px`,
}}
>
<div className={modalContainer}>
<button className={optionButton} onClick={onBoldSelect}>
<motion.div
ref={modalRef}
className={optionModal}
style={{
left: `${TextModalPosition.left}px`,
top: `${TextModalPosition.top}px`,
}}
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 10 }}
>
<div className={modalContainer}>
<button
className={optionButton}
onClick={onBoldSelect}
style={{
backgroundColor: styleState.isBold ? "#e2e8f0" : "transparent",
}}
>
<span
style={{
fontWeight: "bold",
opacity: styleState.isBold ? 1 : 0.6,
}}
>
B
</button>
<button className={optionButton} onClick={onItalicSelect}>
</span>
</button>
<button
className={optionButton}
onClick={onItalicSelect}
style={{
backgroundColor: styleState.isItalic ? "#e2e8f0" : "transparent",
}}
>
<span
style={{
fontStyle: "italic",
fontWeight: "bold",
opacity: styleState.isItalic ? 1 : 0.6,
}}
>
I
</button>
<button className={optionButton} onClick={onUnderlineSelect}>
</span>
</button>
<button
className={optionButton}
onClick={onUnderlineSelect}
style={{
backgroundColor: styleState.isUnderline ? "#e2e8f0" : "transparent",
}}
>
<span
style={{
textDecoration: "underline",
fontWeight: "bold",
opacity: styleState.isUnderline ? 1 : 0.6,
}}
>
U
</button>
<button className={optionButton} onClick={onStrikeSelect}>
</span>
</button>
<button
className={optionButton}
onClick={onStrikeSelect}
style={{
backgroundColor: styleState.isStrike ? "#e2e8f0" : "transparent",
}}
>
<span
style={{
textDecoration: "line-through",
fontWeight: "bold",
opacity: styleState.isStrike ? 1 : 0.6,
}}
>
S
</button>
</div>
</motion.div>
)}
</span>
</button>
</div>
</motion.div>
</ModalPortal>
);
};
1 change: 1 addition & 0 deletions client/src/features/editor/components/block/Block.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ export const Block: React.FC<BlockProps> = memo(
/>
</motion.div>
<TextOptionModal
selectedNodes={selectedNodes}
isOpen={isOpen}
onClose={closeModal}
onBoldSelect={() => handleStyleSelect("bold")}
Expand Down

0 comments on commit 00f1bc5

Please sign in to comment.