Skip to content

Commit 36f9b0f

Browse files
committed
[#128] fix: caret 버그 수정 및 리팩토링
1 parent 7b80dd1 commit 36f9b0f

File tree

1 file changed

+35
-97
lines changed

1 file changed

+35
-97
lines changed

frontend/src/components/atoms/BlockContent/BlockContent.tsx

+35-97
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,10 @@
11
/** @jsx jsx */
22
/** @jsxRuntime classic */
33
import { jsx, css } from '@emotion/react';
4-
import React, {
5-
useEffect,
6-
useRef,
7-
FormEvent,
8-
KeyboardEvent,
9-
useState,
10-
} from 'react';
11-
import { useRecoilState, useRecoilValue } from 'recoil';
4+
import React, { useEffect, useRef, KeyboardEvent, useState } from 'react';
5+
import { useRecoilValue } from 'recoil';
126

13-
import {
14-
blockMapState,
15-
blockRefState,
16-
draggingBlockState,
17-
throttleState,
18-
} from '@/stores';
7+
import { blockRefState, draggingBlockState, throttleState } from '@/stores';
198
import { Block, BlockType } from '@/schemes';
209
import {
2110
regex,
@@ -25,7 +14,7 @@ import {
2514
} from '@utils/blockContent';
2615
import { useCommand, useManager } from '@/hooks';
2716
import { focusState } from '@/stores/page';
28-
import { moveBlock, updateBlock } from '@/utils';
17+
import { moveBlock, debounce } from '@/utils';
2918

3019
const isGridOrColumn = (block: Block): boolean =>
3120
block.type === BlockType.GRID || block.type === BlockType.COLUMN;
@@ -72,14 +61,14 @@ const dragOverCss = () => css`
7261

7362
function BlockContent(blockDTO: Block) {
7463
const contentEditableRef = useRef(null);
75-
const [blockMap, setBlockMap] = useRecoilState(blockMapState);
7664
const focusId = useRecoilValue(focusState);
77-
const caretRef = useRef(0);
7865
const listCnt = useRef(1);
7966
const [Dispatcher] = useCommand();
80-
const [isBlur, setIsBlur] = useState(false);
67+
const [
68+
{ blockIndex, prevSiblings },
69+
{ commitTransaction, startTransaction, setBlock, setCaretOffset },
70+
] = useManager(blockDTO.id);
8171
const draggingBlock = useRecoilValue(draggingBlockState);
82-
const [{ blockIndex }] = useManager(blockDTO.id);
8372
const [dragOverToggle, setDragOverToggle] = useState(false);
8473

8574
useEffect(() => {
@@ -93,23 +82,7 @@ function BlockContent(blockDTO: Block) {
9382
if (focusId === blockDTO.id) contentEditableRef.current.focus();
9483
}, [focusId]);
9584

96-
useEffect(() => {
97-
const selection = window.getSelection();
98-
const nodeLength = selection.focusNode?.nodeValue?.length ?? 0;
99-
if (caretRef.current > nodeLength) {
100-
caretRef.current = nodeLength;
101-
}
102-
selection.collapse(selection.focusNode, caretRef.current);
103-
}, [blockDTO.value]);
104-
105-
const indexInSibling: number = blockMap[
106-
blockDTO.parentId
107-
].childIdList.findIndex((childId: string) => childId === blockDTO.id);
108-
109-
const upperBlocks: Array<Block> = blockMap[blockDTO.parentId].childIdList
110-
.map((childId: any) => blockMap[childId])
111-
.slice(0, indexInSibling)
112-
.reverse();
85+
const upperBlocks: Array<Block> = prevSiblings.reverse();
11386

11487
const isUpperBlockEqualToNumberList = (): boolean => {
11588
if (upperBlocks.length) {
@@ -129,27 +102,27 @@ function BlockContent(blockDTO: Block) {
129102

130103
const FIRST_LIST_NUMBER = '1';
131104

132-
const handleBlock = (value: string, type?: BlockType) =>
133-
type
134-
? setBlockMap({
135-
...blockMap,
136-
[blockDTO.id]: { ...blockDTO, value, type },
137-
})
138-
: setBlockMap({
139-
...blockMap,
140-
[blockDTO.id]: { ...blockDTO, value },
141-
});
105+
const handleBlock = (value: string, type?: BlockType, caretOffset = -1) => {
106+
const { focusOffset } = window.getSelection();
107+
startTransaction();
108+
setBlock(blockDTO.id, { value, type: type || blockDTO.type });
109+
contentEditableRef.current.blur();
110+
setTimeout(() => {
111+
setCaretOffset(caretOffset === -1 ? focusOffset : caretOffset);
112+
});
113+
commitTransaction();
114+
};
142115

143-
const handleValue = (event: FormEvent<HTMLDivElement>) => {
144-
const content = event.currentTarget.textContent;
116+
const handleValue = () => {
117+
const content = contentEditableRef.current.textContent;
145118
const newType = Object.entries(regex).find((testRegex) =>
146119
testRegex[1].test(content),
147120
);
148121

149122
if (newType) {
150123
if (newType[0] === BlockType.NUMBERED_LIST) {
151-
if (!indexInSibling && content[0] !== FIRST_LIST_NUMBER) return;
152-
if (indexInSibling) {
124+
if (!blockIndex && content[0] !== FIRST_LIST_NUMBER) return;
125+
if (blockIndex) {
153126
const numberListUpperBlock = isUpperBlockEqualToNumberList();
154127
if (!numberListUpperBlock && content[0] !== FIRST_LIST_NUMBER) return;
155128
if (
@@ -164,28 +137,11 @@ function BlockContent(blockDTO: Block) {
164137
content.slice(content.indexOf(' ') + 1, content.length),
165138
newType[0] as BlockType,
166139
);
167-
caretRef.current = 0;
168140
return;
169141
}
170142
handleBlock(content);
171-
const selection = window.getSelection();
172-
caretRef.current = selection.focusOffset;
173-
};
174-
175-
const handleKeyUp = (event: KeyboardEvent<HTMLDivElement>) => {
176-
const content = event.currentTarget.textContent;
177-
if (
178-
event.key === 'Backspace' &&
179-
(!blockDTO.value || !window.getSelection().focusOffset)
180-
) {
181-
handleBlock(content, BlockType.TEXT);
182-
}
183-
184-
if (event.key === 'Enter' && event.shiftKey) {
185-
handleBlock(content);
186-
caretRef.current = window.getSelection().focusOffset;
187-
}
188143
};
144+
const updateValue = useRef(debounce(handleValue, 300)).current;
189145

190146
const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {
191147
const { focusNode, focusOffset } = window.getSelection();
@@ -211,13 +167,6 @@ function BlockContent(blockDTO: Block) {
211167
}
212168
};
213169

214-
useEffect(() => {
215-
(async () => {
216-
const { block: updatedBlock } = await updateBlock(blockDTO);
217-
setBlockMap({ ...blockMap, [blockDTO.id]: updatedBlock });
218-
})();
219-
}, [isBlur]);
220-
221170
useEffect(() => {
222171
blockRefState[blockDTO.id] = contentEditableRef;
223172
return () => {
@@ -226,32 +175,26 @@ function BlockContent(blockDTO: Block) {
226175
}, []);
227176

228177
useEffect(() => {
229-
if (focusId === blockDTO.id) contentEditableRef.current.focus();
178+
if (focusId === blockDTO.id) {
179+
contentEditableRef.current.focus();
180+
}
230181
}, [focusId]);
231182

232183
useEffect(() => {
233-
const selection = window.getSelection();
234-
const nodeLength = selection.focusNode?.nodeValue?.length ?? 0;
235-
if (caretRef.current > nodeLength) {
236-
caretRef.current = nodeLength;
237-
}
238-
selection.collapse(selection.focusNode, caretRef.current);
239-
240184
if (blockDTO.type === BlockType.NUMBERED_LIST) {
241185
const numberListUpperBlock = isUpperBlockEqualToNumberList();
242-
if (!indexInSibling || !numberListUpperBlock) {
186+
if (!blockIndex || !numberListUpperBlock) {
243187
listCnt.current = 1;
244188
return;
245189
}
246190
if (numberListUpperBlock) {
247191
listCnt.current = cntOfUpperNumberListBlock() + 1;
248192
}
249193
}
250-
}, [blockDTO.value]);
194+
}, [blockDTO.type]);
251195

252196
const dragOverHandler = (event: React.DragEvent<HTMLDivElement>) => {
253197
event.dataTransfer.dropEffect = 'move';
254-
255198
event.preventDefault();
256199
};
257200

@@ -269,14 +212,11 @@ function BlockContent(blockDTO: Block) {
269212
toId: blockDTO.parentId,
270213
index: blockIndex + 1,
271214
});
272-
setBlockMap((prev) => {
273-
const next = { ...prev };
274-
next[block.id] = block;
275-
fromBlock && (next[fromBlock.id] = fromBlock);
276-
next[to.id] = to;
277-
return next;
278-
});
279-
215+
startTransaction();
216+
setBlock(block.id, block);
217+
fromBlock && setBlock(fromBlock.id, fromBlock);
218+
setBlock(to.id, to);
219+
commitTransaction();
280220
event.preventDefault();
281221
};
282222

@@ -296,9 +236,7 @@ function BlockContent(blockDTO: Block) {
296236
onKeyDown={handleKeyDown}
297237
suppressContentEditableWarning
298238
placeholder={placeHolder[blockDTO.type]}
299-
onInput={handleValue}
300-
onKeyUp={handleKeyUp}
301-
onBlur={() => setIsBlur(!isBlur)}
239+
onInput={updateValue}
302240
>
303241
{blockDTO.value}
304242
</div>

0 commit comments

Comments
 (0)