Skip to content

Commit af3cdf6

Browse files
Merge pull request #44 from boostcampwm-2024/refactor/useMarkdownGrammer_훅_모듈화
Refactor/useMarkdownGrammer 훅 모듈화
2 parents a348f8b + fd3d230 commit af3cdf6

File tree

12 files changed

+848
-2
lines changed

12 files changed

+848
-2
lines changed

client/src/features/editor/Editor.tsx

+14-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { useSocketStore } from "@src/stores/useSocketStore.ts";
1010
import { setCaretPosition, getAbsoluteCaretPosition } from "@src/utils/caretUtils.ts";
1111
import { editorContainer, addNewBlockButton } from "./Editor.style";
1212
import { Block } from "./components/block/Block";
13+
import { useMarkdownGrammer2 } from "./hooks/markdowngrammer/index.ts";
1314
import { useBlockDragAndDrop } from "./hooks/useBlockDragAndDrop";
1415
import { useBlockOperation } from "./hooks/useBlockOperation.ts";
1516
import { useBlockOptionSelect } from "./hooks/useBlockOption";
@@ -101,9 +102,20 @@ export const Editor = memo(({ testKey, pageId, serializedEditorData }: EditorPro
101102
sendCharInsertOperation,
102103
});
103104

104-
const { handleKeyDown: onKeyDown, handleInput: handleHrInput } = useMarkdownGrammer({
105+
// const { handleKeyDown: onKeyDown, handleInput: handleHrInput } = useMarkdownGrammer({
106+
// editorCRDT: editorCRDT.current,
107+
// editorState,
108+
// setEditorState,
109+
// pageId,
110+
// clientId,
111+
// sendBlockInsertOperation,
112+
// sendBlockDeleteOperation,
113+
// sendBlockUpdateOperation,
114+
// sendCharDeleteOperation,
115+
// sendCharInsertOperation,
116+
// });
117+
const { handleKeyDown: onKeyDown, handleInput: handleHrInput } = useMarkdownGrammer2({
105118
editorCRDT: editorCRDT.current,
106-
editorState,
107119
setEditorState,
108120
pageId,
109121
clientId,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import { getAbsoluteCaretPosition, setCaretPosition } from "@src/utils/caretUtils";
2+
import { KeyHandlerContext } from "../types";
3+
import { findBlockByIndex, isEditableBlock } from "../utils";
4+
5+
export const handleArrowKey = (e: React.KeyboardEvent<HTMLDivElement>, ctx: KeyHandlerContext) => {
6+
const { editorCRDT, pageId } = ctx;
7+
8+
const { currentBlock } = editorCRDT;
9+
if (!currentBlock || e.nativeEvent.isComposing) return;
10+
11+
const currentIndex = editorCRDT.LinkedList.spread().findIndex((block) =>
12+
block.id.equals(currentBlock.id),
13+
);
14+
15+
const caretPosition = getAbsoluteCaretPosition(e.currentTarget);
16+
const textLength = currentBlock.crdt.read().length;
17+
18+
const moveToBlock = (targetBlockIndex: number, caretPos: number) => {
19+
const targetBlock = findBlockByIndex(editorCRDT, targetBlockIndex);
20+
if (targetBlock && isEditableBlock(targetBlock)) {
21+
targetBlock.crdt.currentCaret = Math.min(caretPos, targetBlock.crdt.read().length);
22+
editorCRDT.currentBlock = targetBlock;
23+
setCaretPosition({
24+
blockId: targetBlock.id,
25+
position: targetBlock.crdt.currentCaret,
26+
pageId,
27+
});
28+
}
29+
};
30+
31+
switch (e.key) {
32+
case "ArrowUp": {
33+
const hasPrev = currentIndex > 0;
34+
if (!hasPrev) {
35+
e.preventDefault();
36+
return;
37+
}
38+
39+
let targetIndex = currentIndex - 1;
40+
let targetBlock = findBlockByIndex(editorCRDT, targetIndex);
41+
42+
while (targetBlock && targetBlock.type === "hr") {
43+
targetIndex -= 1;
44+
targetBlock = findBlockByIndex(editorCRDT, targetIndex);
45+
}
46+
47+
if (!targetBlock || targetBlock.type === "hr") return;
48+
49+
e.preventDefault();
50+
moveToBlock(targetIndex, caretPosition);
51+
break;
52+
}
53+
54+
case "ArrowDown": {
55+
const hasNext = currentIndex < editorCRDT.LinkedList.spread().length - 1;
56+
if (!hasNext) {
57+
e.preventDefault();
58+
return;
59+
}
60+
61+
let targetIndex = currentIndex + 1;
62+
let targetBlock = findBlockByIndex(editorCRDT, targetIndex);
63+
64+
while (targetBlock && targetBlock.type === "hr") {
65+
targetIndex += 1;
66+
targetBlock = findBlockByIndex(editorCRDT, targetIndex);
67+
}
68+
69+
if (!targetBlock || targetBlock.type === "hr") return;
70+
71+
e.preventDefault();
72+
moveToBlock(targetIndex, caretPosition);
73+
break;
74+
}
75+
76+
case "ArrowLeft": {
77+
if (caretPosition === 0 && currentIndex > 0) {
78+
e.preventDefault();
79+
80+
let targetIndex = currentIndex - 1;
81+
let targetBlock = findBlockByIndex(editorCRDT, targetIndex);
82+
83+
while (targetBlock && targetBlock.type === "hr") {
84+
targetIndex -= 1;
85+
targetBlock = findBlockByIndex(editorCRDT, targetIndex);
86+
}
87+
88+
if (targetBlock && targetBlock.type !== "hr") {
89+
targetBlock.crdt.currentCaret = targetBlock.crdt.read().length;
90+
editorCRDT.currentBlock = targetBlock;
91+
setCaretPosition({
92+
blockId: targetBlock.id,
93+
position: targetBlock.crdt.read().length,
94+
pageId,
95+
});
96+
}
97+
break;
98+
} else {
99+
currentBlock.crdt.currentCaret = Math.max(0, caretPosition - 1);
100+
}
101+
break;
102+
}
103+
104+
case "ArrowRight": {
105+
if (
106+
caretPosition === textLength &&
107+
currentIndex < editorCRDT.LinkedList.spread().length - 1
108+
) {
109+
e.preventDefault();
110+
111+
let targetIndex = currentIndex + 1;
112+
let targetBlock = findBlockByIndex(editorCRDT, targetIndex);
113+
114+
while (targetBlock && targetBlock.type === "hr") {
115+
targetIndex += 1;
116+
targetBlock = findBlockByIndex(editorCRDT, targetIndex);
117+
}
118+
119+
if (targetBlock && targetBlock.type !== "hr") {
120+
targetBlock.crdt.currentCaret = 0;
121+
editorCRDT.currentBlock = targetBlock;
122+
setCaretPosition({
123+
blockId: targetBlock.id,
124+
position: 0,
125+
pageId,
126+
});
127+
}
128+
break;
129+
} else {
130+
currentBlock.crdt.currentCaret = Math.min(textLength, caretPosition + 1);
131+
}
132+
break;
133+
}
134+
}
135+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
import { setCaretPosition } from "@src/utils/caretUtils";
2+
import { KeyHandlerContext } from "../types";
3+
import { updateEditorState, decreaseIndent, findBlockByIndex } from "../utils";
4+
5+
export const handleBackspaceKey = (
6+
e: React.KeyboardEvent<HTMLDivElement>,
7+
ctx: KeyHandlerContext,
8+
) => {
9+
const {
10+
editorCRDT,
11+
pageId,
12+
clientId,
13+
setEditorState,
14+
sendBlockDeleteOperation,
15+
sendBlockUpdateOperation,
16+
sendCharInsertOperation,
17+
sendCharDeleteOperation,
18+
} = ctx;
19+
20+
const { currentBlock } = editorCRDT;
21+
if (!currentBlock) return;
22+
23+
const currentContent = currentBlock.crdt.read();
24+
const currentCharNodes = currentBlock.crdt.spread();
25+
const currentIndex = editorCRDT.LinkedList.spread().findIndex((block) =>
26+
block.id.equals(currentBlock.id),
27+
);
28+
29+
const update = () => updateEditorState(editorCRDT, setEditorState);
30+
31+
if (currentContent === "") {
32+
e.preventDefault();
33+
34+
if (currentBlock.indent > 0) {
35+
decreaseIndent(editorCRDT, currentBlock, pageId, setEditorState, sendBlockUpdateOperation);
36+
return;
37+
}
38+
39+
if (currentBlock.type === "p") {
40+
const prevBlock = currentBlock.prev ? editorCRDT.LinkedList.getNode(currentBlock.prev) : null;
41+
const nextBlock = currentBlock.next ? editorCRDT.LinkedList.getNode(currentBlock.next) : null;
42+
43+
if (prevBlock?.type === "ol" && nextBlock?.type === "ol") {
44+
sendBlockDeleteOperation(editorCRDT.localDelete(currentIndex, undefined, pageId));
45+
editorCRDT.LinkedList.updateAllOrderedListIndices();
46+
47+
editorCRDT.currentBlock = prevBlock;
48+
prevBlock.crdt.currentCaret = prevBlock.crdt.read().length;
49+
update();
50+
return;
51+
}
52+
}
53+
54+
if (currentBlock.type !== "p") {
55+
const wasOrderedList = currentBlock.type === "ol";
56+
currentBlock.type = "p";
57+
sendBlockUpdateOperation(editorCRDT.localUpdate(currentBlock, pageId));
58+
editorCRDT.currentBlock = currentBlock;
59+
60+
if (wasOrderedList) {
61+
editorCRDT.LinkedList.updateAllOrderedListIndices();
62+
}
63+
update();
64+
return;
65+
}
66+
67+
const prevBlock = currentIndex > 0 ? editorCRDT.LinkedList.findByIndex(currentIndex - 1) : null;
68+
69+
if (prevBlock) {
70+
sendBlockDeleteOperation(editorCRDT.localDelete(currentIndex, undefined, pageId));
71+
72+
let targetIndex = currentIndex - 1;
73+
let targetBlock = findBlockByIndex(editorCRDT, targetIndex);
74+
75+
while (targetBlock && targetBlock.type === "hr") {
76+
targetIndex -= 1;
77+
targetBlock = findBlockByIndex(editorCRDT, targetIndex);
78+
}
79+
80+
if (targetBlock && targetBlock.type !== "hr") {
81+
targetBlock.crdt.currentCaret = targetBlock.crdt.read().length;
82+
editorCRDT.currentBlock = targetBlock;
83+
setCaretPosition({
84+
blockId: targetBlock.id,
85+
position: targetBlock.crdt.read().length,
86+
pageId,
87+
});
88+
}
89+
90+
update();
91+
}
92+
93+
return;
94+
}
95+
96+
const { currentCaret } = currentBlock.crdt;
97+
if (currentCaret === 0) {
98+
if (currentBlock.indent > 0) {
99+
decreaseIndent(editorCRDT, currentBlock, pageId, setEditorState, sendBlockUpdateOperation);
100+
update();
101+
return;
102+
}
103+
104+
if (currentBlock.type !== "p") {
105+
const wasOrderedList = currentBlock.type === "ol";
106+
currentBlock.type = "p";
107+
sendBlockUpdateOperation(editorCRDT.localUpdate(currentBlock, pageId));
108+
editorCRDT.currentBlock = currentBlock;
109+
if (wasOrderedList) {
110+
editorCRDT.LinkedList.updateAllOrderedListIndices();
111+
}
112+
update();
113+
return;
114+
}
115+
116+
const prevBlock = currentIndex > 0 ? editorCRDT.LinkedList.findByIndex(currentIndex - 1) : null;
117+
const nextBlock =
118+
currentIndex < editorCRDT.LinkedList.spread().length - 1
119+
? editorCRDT.LinkedList.findByIndex(currentIndex + 1)
120+
: null;
121+
122+
if (prevBlock) {
123+
let targetIndex = currentIndex - 1;
124+
let targetBlock = findBlockByIndex(editorCRDT, targetIndex);
125+
126+
while (targetBlock && targetBlock.type === "hr") {
127+
targetIndex -= 1;
128+
targetBlock = findBlockByIndex(editorCRDT, targetIndex);
129+
}
130+
131+
if (targetBlock && prevBlock.type === "hr") {
132+
editorCRDT.currentBlock = targetBlock;
133+
editorCRDT.currentBlock.crdt.currentCaret = targetBlock.crdt.read().length;
134+
update();
135+
return;
136+
}
137+
138+
const prevBlockEndCaret = prevBlock.crdt.read().length;
139+
140+
for (let i = 0; i < currentContent.length; i++) {
141+
const currentCharNode = currentCharNodes[i];
142+
sendCharInsertOperation(
143+
prevBlock.crdt.localInsert(
144+
prevBlockEndCaret + i,
145+
currentContent[i],
146+
prevBlock.id,
147+
pageId,
148+
clientId,
149+
currentCharNode.style,
150+
currentCharNode.color,
151+
currentCharNode.backgroundColor,
152+
),
153+
);
154+
}
155+
156+
currentContent.split("").forEach(() => {
157+
sendCharDeleteOperation(currentBlock.crdt.localDelete(0, currentBlock.id, pageId));
158+
});
159+
160+
editorCRDT.currentBlock = prevBlock;
161+
prevBlock.crdt.currentCaret = prevBlockEndCaret;
162+
sendBlockDeleteOperation(editorCRDT.localDelete(currentIndex, undefined, pageId));
163+
update();
164+
165+
if (prevBlock.type === "ol" && nextBlock?.type === "ol") {
166+
editorCRDT.LinkedList.updateAllOrderedListIndices();
167+
}
168+
e.preventDefault();
169+
}
170+
}
171+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { KeyHandlerContext } from "../types";
2+
import { updateEditorState } from "../utils";
3+
4+
export const handleDeleteKey = (e: React.KeyboardEvent<HTMLDivElement>, ctx: KeyHandlerContext) => {
5+
if (e.nativeEvent.isComposing) return;
6+
const { editorCRDT, pageId, sendCharDeleteOperation, setEditorState } = ctx;
7+
8+
const { currentBlock } = editorCRDT;
9+
if (!currentBlock) return;
10+
11+
const currentContent = currentBlock.crdt.read();
12+
13+
if (!currentBlock.next || currentContent) return;
14+
15+
const nextBlock = editorCRDT.LinkedList.getNode(currentBlock.next);
16+
if (!nextBlock) return;
17+
18+
sendCharDeleteOperation(
19+
currentBlock.crdt.localDelete(
20+
editorCRDT.LinkedList.spread().findIndex((b) => b.id.equals(currentBlock.id)) + 1,
21+
currentBlock.id,
22+
pageId,
23+
),
24+
);
25+
26+
updateEditorState(editorCRDT, setEditorState);
27+
};

0 commit comments

Comments
 (0)