Skip to content

Commit 693e6c2

Browse files
authored
Merge pull request #377 from boostcampwm-2024/feature-fe-#374
노드 색상 변경 기능 추가
2 parents 231a0f7 + 364fc66 commit 693e6c2

File tree

10 files changed

+129
-7
lines changed

10 files changed

+129
-7
lines changed

apps/frontend/src/app/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useSyncedUsers } from "@/entities/user";
22
import { useProtectedWorkspace } from "@/features/workspace";
33
import { CanvasView } from "@/widgets/CanvasView";
44
import { EditorView } from "@/widgets/EditorView";
5+
import { NodeToolsView } from "@/widgets/NodeToolsView";
56
import { PageSideBarView } from "@/widgets/PageSideBarView";
67
import { CanvasToolsView } from "@/widgets/CanvasToolsView";
78
import { SideWrapper } from "@/shared/ui";
@@ -30,6 +31,7 @@ function App() {
3031
>
3132
<PageSideBarView />
3233
<CanvasToolsView />
34+
<NodeToolsView />
3335
</SideWrapper>
3436
</div>
3537
);

apps/frontend/src/entities/node/model/nodeTypes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export type NoteNodeData = {
88
title: string;
99
id: number;
1010
emoji: string;
11+
color: string;
1112
};
1213

1314
export type NoteNodeType = FlowNode<NoteNodeData, "note">;

apps/frontend/src/entities/node/ui/NoteNode/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export function NoteNode({
2020
return (
2121
<div
2222
className={`h-24 w-48 rounded-lg border-[1px] ${isClicked ? "border-[#8dbaef]" : "border-[#eaeaea]"} bg-white p-3 shadow-sm`}
23+
style={{ backgroundColor: data.color }}
2324
onClick={handleNodeClick}
2425
>
2526
<Handle

apps/frontend/src/features/canvas/model/useCanvas.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,12 @@ export const useCanvas = () => {
7878
const newNode: YNode = {
7979
id: existingNode.id,
8080
type: "note",
81-
data: { title: value, id: pageId, emoji: existingNode.data.emoji },
81+
data: {
82+
title: value,
83+
id: pageId,
84+
emoji: existingNode.data.emoji,
85+
color: existingNode.data.color,
86+
},
8287
position: existingNode.position,
8388
selected: false,
8489
isHolding: false,
@@ -99,7 +104,12 @@ export const useCanvas = () => {
99104
const newNode: YNode = {
100105
id: pageId,
101106
type: "note",
102-
data: { title: existingNode.data.title, id: pageId, emoji: value },
107+
data: {
108+
title: existingNode.data.title,
109+
id: pageId,
110+
emoji: value,
111+
color: existingNode.data.color,
112+
},
103113
position: existingNode.position,
104114
selected: false,
105115
isHolding: false,
@@ -108,6 +118,7 @@ export const useCanvas = () => {
108118
nodesMap.set(pageId, newNode);
109119
});
110120
}, [ydoc]);
121+
111122
useEffect(() => {
112123
if (!ydoc) return;
113124

@@ -131,7 +142,7 @@ export const useCanvas = () => {
131142
event.changes.keys.forEach((change, key) => {
132143
const nodeId = key;
133144
if (change.action === "add" || change.action === "update") {
134-
const updatedNode = nodesMap.get(nodeId) as Node;
145+
const updatedNode = nodesMap.get(nodeId) as YNode;
135146
setNodes((nds) => {
136147
const index = nds.findIndex((n) => n.id === nodeId);
137148
if (index === -1) {
Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
export { CursorButton } from "./ui/CursorButton";
2-
export { CursorPreview } from "./ui/CursorPreview";
3-
export { ProfileForm } from "./ui/ProfileForm";
4-
export { ProfilePanel } from "./ui/ProfilePanel";
52
export { NewNodePanel } from "./ui/NewNodePanel";
3+
export { NodePanel } from "./ui/NodePanel";
4+
export { ProfilePanel } from "./ui/ProfilePanel";
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { usePopover } from "@/shared/model";
2+
3+
interface NodeFormProps {
4+
changeNodeColor: (color: string) => void;
5+
}
6+
7+
export function NodeForm({ changeNodeColor }: NodeFormProps) {
8+
const { close } = usePopover();
9+
10+
const nodeBackgroundColors = {
11+
white: "#FFFFFF",
12+
grey: "#F1F1EF",
13+
brown: "#F4EEEE",
14+
orange: "#FBEBDD",
15+
yellow: "#FCF3DB",
16+
green: "#EDF3ED",
17+
blue: "#E7F3F8",
18+
purple: "#F7F3F8",
19+
pink: "#FBF2F5",
20+
red: "#FDEBEC",
21+
};
22+
23+
const handleButtonClick = (color: string) => {
24+
changeNodeColor(color);
25+
close();
26+
};
27+
28+
return (
29+
<div className="flex flex-col gap-2">
30+
<p className="text-sm text-neutral-500">Background color</p>
31+
<div className="grid grid-cols-5 gap-4">
32+
{Object.entries(nodeBackgroundColors).map(([key, color]) => {
33+
return (
34+
<button
35+
key={key}
36+
onClick={() => handleButtonClick(color)}
37+
className="h-7 w-7 rounded-md border-[1px] border-neutral-300 hover:cursor-pointer"
38+
style={{ backgroundColor: color }}
39+
/>
40+
);
41+
})}
42+
</div>
43+
</div>
44+
);
45+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { useEffect, useState } from "react";
2+
3+
import { NodeForm } from "../NodeForm";
4+
import { Node } from "@/entities/node";
5+
import { useYDocStore } from "@/shared/model";
6+
import { Popover } from "@/shared/ui";
7+
8+
interface NodePanelProps {
9+
currentPage: string;
10+
}
11+
12+
export function NodePanel({ currentPage }: NodePanelProps) {
13+
const [currentColor, setCurrentColor] = useState("#ffffff");
14+
const { ydoc } = useYDocStore();
15+
16+
const changeNodeColor = (color: string) => {
17+
const nodesMap = ydoc.getMap("nodes");
18+
const id = currentPage.toString();
19+
20+
const existingNode = nodesMap.get(id) as Node;
21+
nodesMap.set(id, {
22+
...existingNode,
23+
data: { ...existingNode.data, color },
24+
});
25+
setCurrentColor(color);
26+
};
27+
28+
useEffect(() => {
29+
const nodesMap = ydoc.getMap("nodes");
30+
const currentNode = nodesMap.get(currentPage.toString()) as Node;
31+
setCurrentColor(currentNode.data.color as string);
32+
}, [currentPage]);
33+
34+
return (
35+
<Popover placement="bottom" align="start" offset={{ x: -11, y: 16 }}>
36+
<Popover.Trigger>
37+
<button
38+
className="flex h-7 w-7 items-center justify-center rounded-md border-[1px] border-neutral-300"
39+
style={{ backgroundColor: currentColor }}
40+
/>
41+
</Popover.Trigger>
42+
<Popover.Content className="rounded-lg border bg-white p-3 shadow-md">
43+
<NodeForm changeNodeColor={changeNodeColor} />
44+
</Popover.Content>
45+
</Popover>
46+
);
47+
}

apps/frontend/src/features/canvasTools/ui/ProfilePanel/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { CursorPreview, ProfileForm } from "@/features/canvasTools";
1+
import { CursorPreview } from "../CursorPreview";
2+
import { ProfileForm } from "../ProfileForm";
23
import { usePopover } from "@/shared/model";
34

45
interface ProfilePanelProps {
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { NodeToolsView } from "./ui";
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { usePageStore } from "@/entities/page";
2+
import { NodePanel } from "@/features/canvasTools/ui/NodePanel";
3+
4+
export function NodeToolsView() {
5+
const { currentPage } = usePageStore();
6+
7+
if (!currentPage) return null;
8+
9+
return (
10+
<div className="z-10 flex flex-row rounded-xl border-[1px] border-neutral-200 bg-white p-2.5 text-black shadow-md">
11+
<NodePanel currentPage={currentPage.toString()} />
12+
</div>
13+
);
14+
}

0 commit comments

Comments
 (0)