Skip to content

Commit b187192

Browse files
Merge pull request #256 from boostcampwm-2024/feature-shared-#211
자동으로 노드 정렬해주는 기능
2 parents 7987757 + b7e9fba commit b187192

File tree

3 files changed

+65
-4
lines changed

3 files changed

+65
-4
lines changed

apps/frontend/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"axios": "^1.7.7",
3232
"class-variance-authority": "^0.7.0",
3333
"clsx": "^2.1.1",
34+
"elkjs": "^0.9.3",
3435
"emoji-mart": "^5.6.0",
3536
"fast-diff": "^1.3.0",
3637
"framer-motion": "^11.11.11",
@@ -46,6 +47,7 @@
4647
"tailwindcss-animate": "^1.0.7",
4748
"tailwindest": "^2.3.6",
4849
"use-debounce": "^10.0.4",
50+
"web-worker": "^1.3.0",
4951
"y-prosemirror": "^1.2.12",
5052
"y-socket.io": "^1.1.3",
5153
"yjs": "^13.6.20",

apps/frontend/src/components/canvas/index.tsx

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,22 @@ import {
1717
ReactFlowProvider,
1818
} from "@xyflow/react";
1919
import "@xyflow/react/dist/style.css";
20-
import { usePages } from "@/hooks/usePages";
21-
import { NoteNode } from "./NoteNode";
2220
import * as Y from "yjs";
21+
import ELK from "elkjs";
2322
import { SocketIOProvider } from "y-socket.io";
24-
import { cn } from "@/lib/utils";
2523
import { useQueryClient } from "@tanstack/react-query";
24+
25+
import { CollaborativeCursors } from "../CursorView";
26+
import { NoteNode } from "./NoteNode";
27+
28+
import { usePages } from "@/hooks/usePages";
29+
import { cn } from "@/lib/utils";
2630
import useYDocStore from "@/store/useYDocStore";
2731
import { useCollaborativeCursors } from "@/hooks/useCursor";
28-
import { CollaborativeCursors } from "../CursorView";
2932
import { calculateBestHandles } from "@/lib/calculateBestHandles";
3033

34+
const elk = new ELK();
35+
3136
const proOptions = { hideAttribution: true };
3237

3338
export interface YNode extends Node {
@@ -196,6 +201,47 @@ function Flow({ className }: CanvasProps) {
196201
});
197202
}, [pages, ydoc]);
198203

204+
const performLayout = async () => {
205+
const graph = {
206+
id: "root",
207+
layoutOptions: {
208+
"elk.algorithm": "force",
209+
},
210+
children: nodes.map((node) => ({
211+
id: node.id,
212+
width: 160, // 실제 노드 너비로 변경하기 (node.width)
213+
height: 40, // 실제 노드 높이로 변경하기 (node.height)
214+
})),
215+
edges: edges.map((edge) => ({
216+
id: edge.id,
217+
sources: [edge.source],
218+
targets: [edge.target],
219+
})),
220+
};
221+
222+
const layout = await elk.layout(graph);
223+
224+
const updatedNodes = nodes.map((node) => {
225+
const layoutNode = layout!.children!.find((n) => n.id === node.id);
226+
227+
return {
228+
...node,
229+
position: {
230+
x: layoutNode!.x as number,
231+
y: layoutNode!.y as number,
232+
},
233+
};
234+
});
235+
236+
const nodesMap = ydoc.getMap("nodes");
237+
238+
updatedNodes.forEach((updateNode) => {
239+
nodesMap.set(updateNode.id, updateNode);
240+
});
241+
242+
setNodes(updatedNodes);
243+
};
244+
199245
const handleNodesChange = useCallback(
200246
(changes: NodeChange[]) => {
201247
if (!ydoc) return;
@@ -335,6 +381,9 @@ function Flow({ className }: CanvasProps) {
335381
selectNodesOnDrag={false}
336382
>
337383
<Controls />
384+
<div className="fixed bottom-5 left-16 z-30 h-4 w-4 text-neutral-50 hover:cursor-pointer">
385+
<button onClick={performLayout}>Sort</button>
386+
</div>
338387
<MiniMap />
339388
<Background variant={BackgroundVariant.Dots} gap={12} size={1} />
340389
<CollaborativeCursors cursors={cursors} />

yarn.lock

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5253,6 +5253,11 @@ electron-to-chromium@^1.5.41:
52535253
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.63.tgz#69444d592fbbe628d129866c2355691ea93eda3e"
52545254
integrity sha512-ddeXKuY9BHo/mw145axlyWjlJ1UBt4WK3AlvkT7W2AbqfRQoacVoRUCF6wL3uIx/8wT9oLKXzI+rFqHHscByaA==
52555255

5256+
elkjs@^0.9.3:
5257+
version "0.9.3"
5258+
resolved "https://registry.yarnpkg.com/elkjs/-/elkjs-0.9.3.tgz#16711f8ceb09f1b12b99e971b138a8384a529161"
5259+
integrity sha512-f/ZeWvW/BCXbhGEf1Ujp29EASo/lk1FDnETgNKwJrsVvGZhUWCZyg3xLJjAsxfOmt8KjswHmI5EwCQcPMpOYhQ==
5260+
52565261
emittery@^0.13.1:
52575262
version "0.13.1"
52585263
resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad"
@@ -10454,6 +10459,11 @@ wcwidth@^1.0.1:
1045410459
dependencies:
1045510460
defaults "^1.0.3"
1045610461

10462+
web-worker@^1.3.0:
10463+
version "1.3.0"
10464+
resolved "https://registry.yarnpkg.com/web-worker/-/web-worker-1.3.0.tgz#e5f2df5c7fe356755a5fb8f8410d4312627e6776"
10465+
integrity sha512-BSR9wyRsy/KOValMgd5kMyr3JzpdeoR9KVId8u5GVlTTAtNChlsE4yTxeY7zMdNSyOmoKBv8NH2qeRY9Tg+IaA==
10466+
1045710467
webidl-conversions@^3.0.0:
1045810468
version "3.0.1"
1045910469
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"

0 commit comments

Comments
 (0)