From 878c480480caf88a5fec542472ee0d3480a4e164 Mon Sep 17 00:00:00 2001 From: pipisebastian Date: Thu, 28 Nov 2024 03:32:59 +0900 Subject: [PATCH 01/20] =?UTF-8?q?feat:=20indicator=20=EC=8A=A4=ED=83=80?= =?UTF-8?q?=EC=9D=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #213 --- .../editor/components/block/Block.style.ts | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/client/src/features/editor/components/block/Block.style.ts b/client/src/features/editor/components/block/Block.style.ts index 943ed376..48128eb8 100644 --- a/client/src/features/editor/components/block/Block.style.ts +++ b/client/src/features/editor/components/block/Block.style.ts @@ -1,4 +1,4 @@ -import { cva } from "@styled-system/css"; +import { css, cva } from "@styled-system/css"; const baseBlockStyle = { display: "flex", @@ -141,3 +141,30 @@ export const textContainerStyle = cva({ type: "p", }, }); + +export const dropIndicatorStyle = cva({ + base: { + zIndex: "10", + position: "absolute", + height: "2px", + }, + variants: { + indent: { + first: { + left: "0", + width: "100%", + backgroundColor: "#ADADFF", + }, + second: { + left: "14px", + width: "calc(100% - 14px)", + backgroundColor: "#9B9BFF ", + }, + third: { + left: "26px", + width: "calc(100% - 26px)", + backgroundColor: "#8989FF", + }, + }, + }, +}); From 39a8e081b7687e7b6d780a944cfe190632ede6f5 Mon Sep 17 00:00:00 2001 From: pipisebastian Date: Thu, 28 Nov 2024 03:33:25 +0900 Subject: [PATCH 02/20] =?UTF-8?q?feat:=20ul=20=EC=95=84=EC=9D=B4=EC=BD=98?= =?UTF-8?q?=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 블록 크기를 넘어가지 않도록 수정 #213 --- .../src/features/editor/components/IconBlock/IconBlock.style.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/features/editor/components/IconBlock/IconBlock.style.ts b/client/src/features/editor/components/IconBlock/IconBlock.style.ts index 45076f63..04ff74f4 100644 --- a/client/src/features/editor/components/IconBlock/IconBlock.style.ts +++ b/client/src/features/editor/components/IconBlock/IconBlock.style.ts @@ -20,7 +20,7 @@ export const iconStyle = cva({ variants: { type: { ul: { - fontSize: "20px", // bullet point size + fontSize: "16px", // bullet point size }, ol: { paddingRight: "4px", From 6be21417aeb503ccd94facb0d34c232379a94ce9 Mon Sep 17 00:00:00 2001 From: pipisebastian Date: Thu, 28 Nov 2024 03:33:47 +0900 Subject: [PATCH 03/20] =?UTF-8?q?feat:=20tab=EC=9D=98=20=EA=B2=BD=EC=9A=B0?= =?UTF-8?q?=20=EB=B6=80=EB=AA=A8=EC=9A=94=EC=86=8C=EC=9D=98=20+1=20?= =?UTF-8?q?=EA=B9=8C=EC=A7=80=EB=A7=8C=20=EA=B0=80=EB=8A=A5=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #213 --- .../src/features/editor/hooks/useMarkdownGrammer.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/client/src/features/editor/hooks/useMarkdownGrammer.ts b/client/src/features/editor/hooks/useMarkdownGrammer.ts index 96b64bfa..56b92e97 100644 --- a/client/src/features/editor/hooks/useMarkdownGrammer.ts +++ b/client/src/features/editor/hooks/useMarkdownGrammer.ts @@ -342,8 +342,17 @@ export const useMarkdownGrammer = ({ updateEditorState(); } } else { - // tab: 들여쓰기 증가 - const maxIndent = 3; + if (!currentBlock.prev) return; + + let parentIndent = + editorCRDT.LinkedList.nodeMap[JSON.stringify(currentBlock.prev)].indent; + + const maxIndent = Math.min( + parentIndent + 1, // 부모 indent + 1 + 2, // 들여쓰기 최대 indent + ); + + // 현재 indent가 허용된 최대값보다 작을 때만 들여쓰기 증가 if (currentBlock.indent < maxIndent) { const isOrderedList = currentBlock.type === "ol"; currentBlock.indent += 1; From 34bf56641486eafd1dd77d7b5afa74469427e374 Mon Sep 17 00:00:00 2001 From: pipisebastian Date: Thu, 28 Nov 2024 03:47:30 +0900 Subject: [PATCH 04/20] =?UTF-8?q?feat:=20indicator=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #213 --- .../editor/components/block/Block.tsx | 153 ++++++++++-------- 1 file changed, 89 insertions(+), 64 deletions(-) diff --git a/client/src/features/editor/components/block/Block.tsx b/client/src/features/editor/components/block/Block.tsx index b7d33c45..b77dc97b 100644 --- a/client/src/features/editor/components/block/Block.tsx +++ b/client/src/features/editor/components/block/Block.tsx @@ -1,5 +1,4 @@ import { useSortable } from "@dnd-kit/sortable"; -import { CSS } from "@dnd-kit/utilities"; import { AnimationType, ElementType, @@ -20,11 +19,17 @@ import { MenuBlock } from "../MenuBlock/MenuBlock"; import { TextOptionModal } from "../TextOptionModal/TextOptionModal"; import { TypeOptionModal } from "../TypeOptionModal/TypeOptionModal"; import { blockAnimation } from "./Block.animation"; -import { textContainerStyle, blockContainerStyle, contentWrapperStyle } from "./Block.style"; +import { + textContainerStyle, + blockContainerStyle, + contentWrapperStyle, + dropIndicatorStyle, +} from "./Block.style"; interface BlockProps { id: string; block: CRDTBlock; + draggingBlock: BlockId[]; isActive: boolean; onInput: (e: React.FormEvent, block: CRDTBlock) => void; onCompositionEnd: (e: React.CompositionEvent, block: CRDTBlock) => void; @@ -64,6 +69,7 @@ export const Block: React.FC = memo( ({ id, block, + draggingBlock, isActive, onInput, onCompositionEnd, @@ -83,13 +89,24 @@ export const Block: React.FC = memo( const { isOpen, openModal, closeModal } = useModal(); const [selectedNodes, setSelectedNodes] = useState | null>(null); const { isAnimationStart } = useBlockAnimation(blockRef); - const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ - id, - data: { - type: "block", - block, - }, - }); + const { attributes, listeners, setNodeRef, isDragging, isOver, activeIndex, overIndex } = + useSortable({ + id, + data: { + type: "block", + block, + }, + }); + + // 현재 드래그 중인 부모 블록의 indent 확인 + const isChildOfDragging = draggingBlock.some( + (item) => item.clock === block.id.clock && item.client === block.id.client, + ); + + // NOTE 드롭 인디케이터 위치 계산 + // 현재 over 중인 블럭 위치 + 위/아래로 모두 인디케이터 표시 + 부모요소는 자식요소 내부로는 이동하지 못함 + const showTopIndicator = isOver && !isChildOfDragging && activeIndex >= overIndex; + const showBottomIndicator = isOver && !isChildOfDragging && activeIndex < overIndex; const [slashModalOpen, setSlashModalOpen] = useState(false); const [slashModalPosition, setSlashModalPosition] = useState({ top: 0, left: 0 }); @@ -216,6 +233,14 @@ export const Block: React.FC = memo( } }; + const Indicator = () => ( +
+ ); + useEffect(() => { if (blockRef.current) { console.log("setInnerHTML"); @@ -226,66 +251,66 @@ export const Block: React.FC = memo( return ( // TODO: eslint 규칙을 수정해야 할까? // TODO: ol일때 index 순서 처리 - +
+ {showTopIndicator && } - + + +
onKeyDown(e, blockRef.current, block)} + onInput={handleInput} + onClick={(e) => onClick(block.id, e)} + onCopy={(e) => onCopy(e, blockRef.current, block)} + onPaste={(e) => onPaste(e, blockRef.current, block)} + onMouseUp={handleMouseUp} + onCompositionEnd={(e) => onCompositionEnd(e, block)} + contentEditable={block.type !== "hr"} + spellCheck={false} + suppressContentEditableWarning + className={textContainerStyle({ + type: block.type, + })} + /> + + handleStyleSelect("bold")} + onItalicSelect={() => handleStyleSelect("italic")} + onUnderlineSelect={() => handleStyleSelect("underline")} + onStrikeSelect={() => handleStyleSelect("strikethrough")} + onTextColorSelect={handleTextColorSelect} + onTextBackgroundColorSelect={handleTextBackgroundColorSelect} /> - -
onKeyDown(e, blockRef.current, block)} - onInput={handleInput} - onClick={(e) => onClick(block.id, e)} - onCopy={(e) => onCopy(e, blockRef.current, block)} - onPaste={(e) => onPaste(e, blockRef.current, block)} - onMouseUp={handleMouseUp} - onCompositionEnd={(e) => onCompositionEnd(e, block)} - contentEditable={block.type !== "hr"} - spellCheck={false} - suppressContentEditableWarning - className={textContainerStyle({ - type: block.type, - })} + setSlashModalOpen(false)} + onTypeSelect={(type) => handleTypeSelect(type)} + position={slashModalPosition} /> - handleStyleSelect("bold")} - onItalicSelect={() => handleStyleSelect("italic")} - onUnderlineSelect={() => handleStyleSelect("underline")} - onStrikeSelect={() => handleStyleSelect("strikethrough")} - onTextColorSelect={handleTextColorSelect} - onTextBackgroundColorSelect={handleTextBackgroundColorSelect} - /> - setSlashModalOpen(false)} - onTypeSelect={(type) => handleTypeSelect(type)} - position={slashModalPosition} - /> - + {showBottomIndicator && } +
); }, ); From 7d9b68b9a91bdec930484255c1ed1b485a39aab1 Mon Sep 17 00:00:00 2001 From: pipisebastian Date: Thu, 28 Nov 2024 04:40:06 +0900 Subject: [PATCH 05/20] =?UTF-8?q?feat:=20blockId=20=EB=B0=B0=EC=97=B4?= =?UTF-8?q?=EB=8C=80=EC=8B=A0=20id=20string=20=EB=B0=B0=EC=97=B4=EB=A1=9C?= =?UTF-8?q?=20=EA=B4=80=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #213 --- client/src/features/editor/components/block/Block.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/client/src/features/editor/components/block/Block.tsx b/client/src/features/editor/components/block/Block.tsx index b77dc97b..58626df7 100644 --- a/client/src/features/editor/components/block/Block.tsx +++ b/client/src/features/editor/components/block/Block.tsx @@ -29,7 +29,7 @@ import { interface BlockProps { id: string; block: CRDTBlock; - draggingBlock: BlockId[]; + draggingBlock: String[]; isActive: boolean; onInput: (e: React.FormEvent, block: CRDTBlock) => void; onCompositionEnd: (e: React.CompositionEvent, block: CRDTBlock) => void; @@ -89,19 +89,18 @@ export const Block: React.FC = memo( const { isOpen, openModal, closeModal } = useModal(); const [selectedNodes, setSelectedNodes] = useState | null>(null); const { isAnimationStart } = useBlockAnimation(blockRef); - const { attributes, listeners, setNodeRef, isDragging, isOver, activeIndex, overIndex } = + const { attributes, listeners, setNodeRef, isDragging, isOver, activeIndex, overIndex, data } = useSortable({ id, data: { + id, type: "block", block, }, }); // 현재 드래그 중인 부모 블록의 indent 확인 - const isChildOfDragging = draggingBlock.some( - (item) => item.clock === block.id.clock && item.client === block.id.client, - ); + const isChildOfDragging = draggingBlock.some((item) => item === data.id); // NOTE 드롭 인디케이터 위치 계산 // 현재 over 중인 블럭 위치 + 위/아래로 모두 인디케이터 표시 + 부모요소는 자식요소 내부로는 이동하지 못함 From f10ae2bb098f9d78cc5ddd371a0c91da611d049f Mon Sep 17 00:00:00 2001 From: pipisebastian Date: Thu, 28 Nov 2024 04:40:41 +0900 Subject: [PATCH 06/20] =?UTF-8?q?feat:=20handleDragEnd=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 드래그놓았을때 자식요소까지 업데이트되도록 #213 --- .../editor/hooks/useBlockDragAndDrop.ts | 125 +++++++++++++++--- 1 file changed, 109 insertions(+), 16 deletions(-) diff --git a/client/src/features/editor/hooks/useBlockDragAndDrop.ts b/client/src/features/editor/hooks/useBlockDragAndDrop.ts index ae880a5b..547bec46 100644 --- a/client/src/features/editor/hooks/useBlockDragAndDrop.ts +++ b/client/src/features/editor/hooks/useBlockDragAndDrop.ts @@ -1,8 +1,13 @@ // hooks/useBlockDragAndDrop.ts import { DragEndEvent, PointerSensor, useSensor, useSensors } from "@dnd-kit/core"; import { EditorCRDT } from "@noctaCrdt/Crdt"; +import { Block } from "@noctaCrdt/Node"; import { useSocketStore } from "@src/stores/useSocketStore.ts"; import { EditorStateProps } from "../Editor"; +import { + RemoteBlockReorderOperation, + RemoteBlockUpdateOperation, +} from "node_modules/@noctaCrdt/Interfaces"; interface UseBlockDragAndDropProps { editorCRDT: EditorCRDT; @@ -25,12 +30,99 @@ export const useBlockDragAndDrop = ({ }), ); - const { sendBlockReorderOperation } = useSocketStore(); + const { sendBlockReorderOperation, sendBlockUpdateOperation } = useSocketStore(); + + const getChildBlocks = (nodes: Block[], parentIndex: number, parentIndent: number): Block[] => { + const children = []; + let i = parentIndex + 1; + + while (i < nodes.length && nodes[i].indent > parentIndent) { + children.push(nodes[i]); + i += 1; + } + + return children; + }; + + const reorderBlocksWithChildren = ( + nodes: Block[], + targetNode: Block, + beforeNode: Block | null, + afterNode: Block | null, + ) => { + const operations = []; + const targetIndex = nodes.indexOf(targetNode); + const childBlocks = getChildBlocks(nodes, targetIndex, targetNode.indent); + + // 이동할 위치의 부모 블록 indent 찾기 + let newIndent = 0; + console.log("beforeNode", beforeNode, afterNode); + if (beforeNode) { + // 앞 블록이 있는 경우, 그 블록의 indent를 기준으로 + newIndent = beforeNode.indent; + } else if (afterNode) { + // 뒤 블록이 있는 경우, 그 블록의 indent를 기준으로 + newIndent = afterNode.indent; + } + + // indent 변화량 계산 -> 추후 자식 블록들에 indentDiff만큼 적용 + const indentDiff = newIndent - targetNode.indent; + + // 타겟 블록 업데이트 + targetNode.indent = newIndent; + + // Reorder 연산 + const reorderOp = editorCRDT.localReorder({ + targetId: targetNode.id, + beforeId: beforeNode?.id || null, + afterId: afterNode?.id || null, + pageId, + }); + operations.push({ type: "reorder", operation: reorderOp }); + + // Update 연산 (indent 갱신) + const updateOp = editorCRDT.localUpdate(targetNode, pageId); + operations.push({ type: "update", operation: updateOp }); + + // 자식 블록들 처리 + let prevBlock = targetNode; + childBlocks.forEach((child) => { + const childNewIndent = Math.max(0, child.indent + indentDiff); + child.indent = childNewIndent; + + const childReorderOp = editorCRDT.localReorder({ + targetId: child.id, + beforeId: prevBlock.id, + afterId: afterNode?.id || null, + pageId, + }); + operations.push({ type: "reorder", operation: childReorderOp }); + + const childUpdateOp = editorCRDT.localUpdate(child, pageId); + operations.push({ type: "update", operation: childUpdateOp }); + + prevBlock = child; + }); + + return operations; + }; + + const handleDragEnd = ( + event: DragEndEvent, + draggingBlock: String[], + initDraggingBlock: () => void, + ) => { + // 커서 다시 원래대로 + document.body.style.cursor = "auto"; + initDraggingBlock(); - const handleDragEnd = (event: DragEndEvent) => { const { active, over } = event; + if (!over) return; - if (!over || active.id === over.id) return; + // 지금 놓으려는 블록(over)이 드래깅 중인 블록이거나, 드래깅 중인 블록의 자식 블록이면 무시 + const disableDrag = draggingBlock.some((item) => item === over.data.current?.id); + + if (disableDrag) return; try { const nodes = editorState.linkedList.spread(); @@ -53,13 +145,11 @@ export const useBlockDragAndDrop = ({ (block) => block.id.client === overInfo.client && block.id.clock === overInfo.clock, ); - if (!targetNode || !overNode) { - throw new Error("Unable to find target or destination node"); - } + if (!targetNode || !overNode) return; + // 드래그 방향에 따라 beforeNode와 afterNode 결정 const targetIndex = nodes.indexOf(targetNode); const overIndex = nodes.indexOf(overNode); - // 드래그 방향 결정 const isMoveDown = targetIndex < overIndex; @@ -76,18 +166,18 @@ export const useBlockDragAndDrop = ({ beforeNode = overIndex > 0 ? nodes[overIndex - 1] : null; afterNode = overNode; } - // EditorCRDT의 현재 상태로 작업 - const operation = editorCRDT.localReorder({ - targetId: targetNode.id, - beforeId: beforeNode?.id || null, - afterId: afterNode?.id || null, - pageId, + const operations = reorderBlocksWithChildren(nodes, targetNode, beforeNode, afterNode); + + // 각 operation type에 따라 함수 호출 (reorder + update(indent 갱신)) + operations.forEach((op) => { + if (op.type === "reorder") { + sendBlockReorderOperation(op.operation as RemoteBlockReorderOperation); + } else if (op.type === "update") { + sendBlockUpdateOperation(op.operation as RemoteBlockUpdateOperation); + } }); - sendBlockReorderOperation(operation); - - // EditorState 업데이트 setEditorState({ clock: editorCRDT.clock, linkedList: editorCRDT.LinkedList, @@ -97,8 +187,11 @@ export const useBlockDragAndDrop = ({ } }; + const handleDragStart = (block: Block) => {}; + return { sensors, handleDragEnd, + handleDragStart, }; }; From 6daeb57dcd25036c19d29f33cd0ce96efb5f86e0 Mon Sep 17 00:00:00 2001 From: pipisebastian Date: Thu, 28 Nov 2024 04:49:49 +0900 Subject: [PATCH 07/20] =?UTF-8?q?refactor:=20=EB=B3=80=EC=88=98=EB=AA=85?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=ED=95=A8=EC=88=98=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #213 --- client/src/features/editor/Editor.tsx | 16 +++++++++++++--- .../features/editor/components/block/Block.tsx | 6 +++--- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/client/src/features/editor/Editor.tsx b/client/src/features/editor/Editor.tsx index f888dc0b..7c09b277 100644 --- a/client/src/features/editor/Editor.tsx +++ b/client/src/features/editor/Editor.tsx @@ -1,4 +1,4 @@ -import { DndContext } from "@dnd-kit/core"; +import { DndContext, DragEndEvent } from "@dnd-kit/core"; import { SortableContext, verticalListSortingStrategy } from "@dnd-kit/sortable"; import { EditorCRDT } from "@noctaCrdt/Crdt"; import { BlockLinkedList } from "@noctaCrdt/LinkedList"; @@ -47,6 +47,7 @@ export const Editor = ({ onTitleChange, pageId, pageTitle, serializedEditorData const [displayTitle, setDisplayTitle] = useState( pageTitle === "새로운 페이지" || pageTitle === "" ? "" : pageTitle, ); + const [dragBlockList, setDragBlockList] = useState([]); const editorCRDTInstance = useMemo(() => { let newEditorCRDT; @@ -67,7 +68,7 @@ export const Editor = ({ onTitleChange, pageId, pageTitle, serializedEditorData linkedList: editorCRDT.current.LinkedList, }); - const { sensors, handleDragEnd } = useBlockDragAndDrop({ + const { sensors, handleDragEnd, handleDragStart } = useBlockDragAndDrop({ editorCRDT: editorCRDT.current, editorState, setEditorState, @@ -324,7 +325,15 @@ export const Editor = ({ onTitleChange, pageId, pageTitle, serializedEditorData className={editorTitle} />
- + { + handleDragEnd(event, dragBlockList, () => setDragBlockList([])); + }} + onDragStart={(event) => { + handleDragStart(event, setDragBlockList); + }} + sensors={sensors} + > ))} diff --git a/client/src/features/editor/components/block/Block.tsx b/client/src/features/editor/components/block/Block.tsx index 58626df7..9958f308 100644 --- a/client/src/features/editor/components/block/Block.tsx +++ b/client/src/features/editor/components/block/Block.tsx @@ -29,7 +29,7 @@ import { interface BlockProps { id: string; block: CRDTBlock; - draggingBlock: String[]; + dragBlockList: string[]; isActive: boolean; onInput: (e: React.FormEvent, block: CRDTBlock) => void; onCompositionEnd: (e: React.CompositionEvent, block: CRDTBlock) => void; @@ -69,7 +69,7 @@ export const Block: React.FC = memo( ({ id, block, - draggingBlock, + dragBlockList, isActive, onInput, onCompositionEnd, @@ -100,7 +100,7 @@ export const Block: React.FC = memo( }); // 현재 드래그 중인 부모 블록의 indent 확인 - const isChildOfDragging = draggingBlock.some((item) => item === data.id); + const isChildOfDragging = dragBlockList.some((item) => item === data.id); // NOTE 드롭 인디케이터 위치 계산 // 현재 over 중인 블럭 위치 + 위/아래로 모두 인디케이터 표시 + 부모요소는 자식요소 내부로는 이동하지 못함 From 8289631eab956d5f8738f694312f04ed0b18e03b Mon Sep 17 00:00:00 2001 From: pipisebastian Date: Thu, 28 Nov 2024 04:50:03 +0900 Subject: [PATCH 08/20] =?UTF-8?q?refactor:=20const=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #213 --- client/src/features/editor/hooks/useMarkdownGrammer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/features/editor/hooks/useMarkdownGrammer.ts b/client/src/features/editor/hooks/useMarkdownGrammer.ts index 56b92e97..53c6ae07 100644 --- a/client/src/features/editor/hooks/useMarkdownGrammer.ts +++ b/client/src/features/editor/hooks/useMarkdownGrammer.ts @@ -344,7 +344,7 @@ export const useMarkdownGrammer = ({ } else { if (!currentBlock.prev) return; - let parentIndent = + const parentIndent = editorCRDT.LinkedList.nodeMap[JSON.stringify(currentBlock.prev)].indent; const maxIndent = Math.min( From 06798c2264726d635877be3bf611d8e32a1c752c Mon Sep 17 00:00:00 2001 From: pipisebastian Date: Thu, 28 Nov 2024 04:57:10 +0900 Subject: [PATCH 09/20] =?UTF-8?q?feat:=20handleDragStart=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #213 --- .../editor/hooks/useBlockDragAndDrop.ts | 44 +++++++++++++++++-- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/client/src/features/editor/hooks/useBlockDragAndDrop.ts b/client/src/features/editor/hooks/useBlockDragAndDrop.ts index 547bec46..3c8eeb62 100644 --- a/client/src/features/editor/hooks/useBlockDragAndDrop.ts +++ b/client/src/features/editor/hooks/useBlockDragAndDrop.ts @@ -1,5 +1,5 @@ // hooks/useBlockDragAndDrop.ts -import { DragEndEvent, PointerSensor, useSensor, useSensors } from "@dnd-kit/core"; +import { DragEndEvent, DragStartEvent, PointerSensor, useSensor, useSensors } from "@dnd-kit/core"; import { EditorCRDT } from "@noctaCrdt/Crdt"; import { Block } from "@noctaCrdt/Node"; import { useSocketStore } from "@src/stores/useSocketStore.ts"; @@ -109,7 +109,7 @@ export const useBlockDragAndDrop = ({ const handleDragEnd = ( event: DragEndEvent, - draggingBlock: String[], + dragBlockList: string[], initDraggingBlock: () => void, ) => { // 커서 다시 원래대로 @@ -120,7 +120,7 @@ export const useBlockDragAndDrop = ({ if (!over) return; // 지금 놓으려는 블록(over)이 드래깅 중인 블록이거나, 드래깅 중인 블록의 자식 블록이면 무시 - const disableDrag = draggingBlock.some((item) => item === over.data.current?.id); + const disableDrag = dragBlockList.some((item) => item === over.data.current?.id); if (disableDrag) return; @@ -187,7 +187,43 @@ export const useBlockDragAndDrop = ({ } }; - const handleDragStart = (block: Block) => {}; + const handleDragStart = ( + event: DragStartEvent, + + setDragBlockList: React.Dispatch>, + ) => { + document.body.style.cursor = "grabbing"; + const { active } = event; + const parentId = active.data.current?.id; + const parentIndent = active.data.current?.block.indent; + + if (!parentId) return; + + const findChildBlocks = (parentId: string) => { + const blocks = editorState.linkedList.spread(); + const parentIndex = blocks.findIndex( + (block) => `${block.id.client}-${block.id.clock}` === parentId, + ); + + if (parentIndex === -1) return []; + + const childBlockId = []; + + for (let i = parentIndex + 1; i < blocks.length; i++) { + if (blocks[i].indent > parentIndent) { + childBlockId.push(`${blocks[i].id.client}-${blocks[i].id.clock}`); + } else { + break; + } + } + + return childBlockId; + }; + + const childBlockId = findChildBlocks(parentId); + + setDragBlockList([parentId, ...childBlockId]); + }; return { sensors, From 468a7b63b14659ea7af3fca84348a30be2254694 Mon Sep 17 00:00:00 2001 From: pipisebastian Date: Thu, 28 Nov 2024 04:59:04 +0900 Subject: [PATCH 10/20] =?UTF-8?q?refactor:=20type=20string=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #213 --- client/src/features/editor/hooks/useBlockDragAndDrop.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/features/editor/hooks/useBlockDragAndDrop.ts b/client/src/features/editor/hooks/useBlockDragAndDrop.ts index 3c8eeb62..a9addc70 100644 --- a/client/src/features/editor/hooks/useBlockDragAndDrop.ts +++ b/client/src/features/editor/hooks/useBlockDragAndDrop.ts @@ -109,7 +109,7 @@ export const useBlockDragAndDrop = ({ const handleDragEnd = ( event: DragEndEvent, - dragBlockList: string[], + dragBlockList: sstring[], initDraggingBlock: () => void, ) => { // 커서 다시 원래대로 From 9d3fcdcda114284f5ce7ff4cdf28dfa7b66da871 Mon Sep 17 00:00:00 2001 From: pipisebastian Date: Thu, 28 Nov 2024 04:59:16 +0900 Subject: [PATCH 11/20] =?UTF-8?q?refactor:=20type=20string=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #213 --- client/src/features/editor/hooks/useBlockDragAndDrop.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/features/editor/hooks/useBlockDragAndDrop.ts b/client/src/features/editor/hooks/useBlockDragAndDrop.ts index a9addc70..3c8eeb62 100644 --- a/client/src/features/editor/hooks/useBlockDragAndDrop.ts +++ b/client/src/features/editor/hooks/useBlockDragAndDrop.ts @@ -109,7 +109,7 @@ export const useBlockDragAndDrop = ({ const handleDragEnd = ( event: DragEndEvent, - dragBlockList: sstring[], + dragBlockList: string[], initDraggingBlock: () => void, ) => { // 커서 다시 원래대로 From ad2aa428fed0a0351b49ffc4d16d15b44a952238 Mon Sep 17 00:00:00 2001 From: pipisebastian Date: Thu, 28 Nov 2024 05:02:44 +0900 Subject: [PATCH 12/20] =?UTF-8?q?refactor:=20indicator=20=EB=8B=A4?= =?UTF-8?q?=EC=9E=90=EC=9D=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #213 --- .../src/features/editor/components/block/Block.style.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/src/features/editor/components/block/Block.style.ts b/client/src/features/editor/components/block/Block.style.ts index 48128eb8..16bec9d9 100644 --- a/client/src/features/editor/components/block/Block.style.ts +++ b/client/src/features/editor/components/block/Block.style.ts @@ -156,13 +156,13 @@ export const dropIndicatorStyle = cva({ backgroundColor: "#ADADFF", }, second: { - left: "14px", - width: "calc(100% - 14px)", + left: "10px", + width: "calc(100% - 10px)", backgroundColor: "#9B9BFF ", }, third: { - left: "26px", - width: "calc(100% - 26px)", + left: "20px", + width: "calc(100% - 20px)", backgroundColor: "#8989FF", }, }, From c6c0ae6f535ef73a07c3e06715c7fee722a36e50 Mon Sep 17 00:00:00 2001 From: pipisebastian Date: Thu, 28 Nov 2024 05:57:38 +0900 Subject: [PATCH 13/20] =?UTF-8?q?feat:=20backspace/shift=20tab=20=EB=88=84?= =?UTF-8?q?=EB=A5=BC=EC=8B=9C=20indent=20=EA=B0=90=EC=86=8C=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #213 --- .../editor/hooks/useMarkdownGrammer.ts | 56 +++++++++++++------ 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/client/src/features/editor/hooks/useMarkdownGrammer.ts b/client/src/features/editor/hooks/useMarkdownGrammer.ts index 53c6ae07..a5680dfe 100644 --- a/client/src/features/editor/hooks/useMarkdownGrammer.ts +++ b/client/src/features/editor/hooks/useMarkdownGrammer.ts @@ -64,6 +64,44 @@ export const useMarkdownGrammer = ({ return editorCRDT.LinkedList.findByIndex(index); }; + const decreaseIndent = (currentBlock: Block) => { + if (currentBlock.indent === 0) return; + + const currentIndex = editorCRDT.LinkedList.spread().findIndex((block) => + block.id.equals(currentBlock.id), + ); + + // 현재 블록의 indent 감소 + const wasOrderedList = currentBlock.type === "ol"; + const originalIndent = currentBlock.indent; + const newIndent = originalIndent - 1; + currentBlock.indent = newIndent; + sendBlockUpdateOperation(editorCRDT.localUpdate(currentBlock, pageId)); + + // 자식 블록들 찾기 및 업데이트 + const blocks = editorCRDT.LinkedList.spread(); + let i = currentIndex + 1; + + // 현재 블록의 원래 indent보다 큰 블록들만 처리 (자식 블록들만) + while (i < blocks.length && blocks[i].indent > originalIndent) { + const childBlock = blocks[i]; + + // 자식 블록의 indent도 1 감소 + childBlock.indent = Math.max(0, childBlock.indent - 1); + sendBlockUpdateOperation(editorCRDT.localUpdate(childBlock, pageId)); + + i += 1; + } + + // ordered list인 경우 인덱스 업데이트 + if (wasOrderedList) { + editorCRDT.LinkedList.updateAllOrderedListIndices(); + } + + editorCRDT.currentBlock = currentBlock; + updateEditorState(); + }; + const currentBlockId = editorCRDT.currentBlock ? editorCRDT.currentBlock.id : null; if (!currentBlockId) return; @@ -166,14 +204,7 @@ export const useMarkdownGrammer = ({ if (currentContent === "") { e.preventDefault(); if (currentBlock.indent > 0) { - const wasOrderedList = currentBlock.type === "ol"; - currentBlock.indent -= 1; - sendBlockUpdateOperation(editorCRDT.localUpdate(currentBlock, pageId)); - editorCRDT.currentBlock = currentBlock; - if (wasOrderedList) { - editorCRDT.LinkedList.updateAllOrderedListIndices(); - } - updateEditorState(); + decreaseIndent(currentBlock); break; } @@ -332,14 +363,7 @@ export const useMarkdownGrammer = ({ if (e.shiftKey) { // shift + tab: 들여쓰기 감소 if (currentBlock.indent > 0) { - const isOrderedList = currentBlock.type === "ol"; - currentBlock.indent -= 1; - sendBlockUpdateOperation(editorCRDT.localUpdate(currentBlock, pageId)); - editorCRDT.currentBlock = currentBlock; - if (isOrderedList) { - editorCRDT.LinkedList.updateAllOrderedListIndices(); - } - updateEditorState(); + decreaseIndent(currentBlock); } } else { if (!currentBlock.prev) return; From e0a2745473683edfb735d2e4d92d78b5d82e7dbc Mon Sep 17 00:00:00 2001 From: pipisebastian Date: Thu, 28 Nov 2024 06:08:11 +0900 Subject: [PATCH 14/20] =?UTF-8?q?feat:=20indent=EB=B3=84=20ul=20=EC=95=84?= =?UTF-8?q?=EC=9D=B4=EC=BD=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #213 --- .../editor/components/IconBlock/IconBlock.style.ts | 2 +- .../editor/components/IconBlock/IconBlock.tsx | 11 +++++++++-- client/src/features/editor/components/block/Block.tsx | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/client/src/features/editor/components/IconBlock/IconBlock.style.ts b/client/src/features/editor/components/IconBlock/IconBlock.style.ts index 04ff74f4..be2c8720 100644 --- a/client/src/features/editor/components/IconBlock/IconBlock.style.ts +++ b/client/src/features/editor/components/IconBlock/IconBlock.style.ts @@ -20,7 +20,7 @@ export const iconStyle = cva({ variants: { type: { ul: { - fontSize: "16px", // bullet point size + fontSize: "6px", // bullet point size }, ol: { paddingRight: "4px", diff --git a/client/src/features/editor/components/IconBlock/IconBlock.tsx b/client/src/features/editor/components/IconBlock/IconBlock.tsx index 5c369560..70c466db 100644 --- a/client/src/features/editor/components/IconBlock/IconBlock.tsx +++ b/client/src/features/editor/components/IconBlock/IconBlock.tsx @@ -4,13 +4,20 @@ import { iconContainerStyle, iconStyle } from "./IconBlock.style"; interface IconBlockProps { type: ElementType; index: number | undefined; + indent?: number; } -export const IconBlock = ({ type, index = 1 }: IconBlockProps) => { +export const IconBlock = ({ type, index = 1, indent = 0 }: IconBlockProps) => { const getIcon = () => { switch (type) { case "ul": - return ; + return ( + + {indent === 0 && "●"} + {indent === 1 && "○"} + {indent === 2 && "■"} + + ); case "ol": return {`${index}.`}; case "checkbox": diff --git a/client/src/features/editor/components/block/Block.tsx b/client/src/features/editor/components/block/Block.tsx index 9958f308..2a21b33d 100644 --- a/client/src/features/editor/components/block/Block.tsx +++ b/client/src/features/editor/components/block/Block.tsx @@ -272,7 +272,7 @@ export const Block: React.FC = memo( onCopySelect={handleCopySelect} onDeleteSelect={handleDeleteSelect} /> - +
onKeyDown(e, blockRef.current, block)} From baab9fd402ba219130ecd63634925e7bc6683e51 Mon Sep 17 00:00:00 2001 From: pipisebastian Date: Thu, 28 Nov 2024 13:31:44 +0900 Subject: [PATCH 15/20] =?UTF-8?q?feat:=20=EB=B0=B1=EC=8A=A4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=8A=A4=20=EB=82=B4=EC=9A=A9=EC=9E=88=EC=9D=84?= =?UTF-8?q?=EB=95=8C=EB=8F=84=20indent=20=EC=A4=84=EC=96=B4=EB=93=A4?= =?UTF-8?q?=EB=8F=84=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #213 --- client/src/features/editor/hooks/useMarkdownGrammer.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/client/src/features/editor/hooks/useMarkdownGrammer.ts b/client/src/features/editor/hooks/useMarkdownGrammer.ts index 74537303..e2be299a 100644 --- a/client/src/features/editor/hooks/useMarkdownGrammer.ts +++ b/client/src/features/editor/hooks/useMarkdownGrammer.ts @@ -283,10 +283,7 @@ export const useMarkdownGrammer = ({ const currentCaretPosition = currentBlock.crdt.currentCaret; if (currentCaretPosition === 0) { if (currentBlock.indent > 0) { - currentBlock.indent -= 1; - sendBlockUpdateOperation(editorCRDT.localUpdate(currentBlock, pageId)); - editorCRDT.currentBlock = currentBlock; - updateEditorState(); + decreaseIndent(currentBlock); break; } if (currentBlock.type !== "p") { From b64288f45368f6ec8bb373b818c8846486001321 Mon Sep 17 00:00:00 2001 From: pipisebastian Date: Thu, 28 Nov 2024 17:24:44 +0900 Subject: [PATCH 16/20] =?UTF-8?q?fix:=20=EB=93=9C=EB=9E=98=EA=B7=B8=20?= =?UTF-8?q?=EB=B6=80=EB=AA=A8,=EC=9E=90=EC=8B=9D=EA=B4=80=EA=B3=84?= =?UTF-8?q?=EA=B0=80=20=EC=A0=9C=EB=8C=80=EB=A1=9C=20=EC=97=85=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8=20=EC=95=88=EB=90=98=EB=8A=94=20=EB=AC=B8?= =?UTF-8?q?=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #213 --- @noctaCrdt/LinkedList.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/@noctaCrdt/LinkedList.ts b/@noctaCrdt/LinkedList.ts index de84c19a..cd8e4d49 100644 --- a/@noctaCrdt/LinkedList.ts +++ b/@noctaCrdt/LinkedList.ts @@ -300,6 +300,8 @@ export class BlockLinkedList extends LinkedList { if (targetNode.prev) { const prevNode = this.getNode(targetNode.prev); if (prevNode) prevNode.next = targetNode.next; + } else { + this.head = targetNode.next; } if (targetNode.next) { @@ -307,10 +309,6 @@ export class BlockLinkedList extends LinkedList { if (nextNode) nextNode.prev = targetNode.prev; } - if (this.head === targetId) { - this.head = targetNode.next; - } - // 2. 새로운 위치에 노드 삽입 if (!beforeId) { // 맨 앞으로 이동 From d7646d6b2766d6e238737361808120e4397d92b7 Mon Sep 17 00:00:00 2001 From: pipisebastian Date: Thu, 28 Nov 2024 17:25:06 +0900 Subject: [PATCH 17/20] =?UTF-8?q?fix:=20prevBlock=20=EC=A1=B4=EC=9E=AC?= =?UTF-8?q?=EC=97=AC=EB=B6=80=20=EA=B2=80=EC=82=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #213 --- client/src/features/editor/hooks/useEditorOperation.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/features/editor/hooks/useEditorOperation.ts b/client/src/features/editor/hooks/useEditorOperation.ts index 2917c6c5..84656b07 100644 --- a/client/src/features/editor/hooks/useEditorOperation.ts +++ b/client/src/features/editor/hooks/useEditorOperation.ts @@ -47,7 +47,7 @@ export const useEditorOperation = ({ const prevBlock = editorCRDT.current.LinkedList.nodeMap[JSON.stringify(targetBlock.prev)]; const nextBlock = editorCRDT.current.LinkedList.nodeMap[JSON.stringify(targetBlock.next)]; editorCRDT.current.remoteDelete(operation); - if (prevBlock.type === "ol" && nextBlock.type === "ol") { + if (prevBlock && prevBlock.type === "ol" && nextBlock.type === "ol") { editorCRDT.current.LinkedList.updateAllOrderedListIndices(); } setEditorState({ @@ -93,7 +93,7 @@ export const useEditorOperation = ({ if (operation.pageId !== pageId) return; const prevBlock = editorCRDT.current.LinkedList.nodeMap[JSON.stringify(operation.node.prev)]; editorCRDT.current.remoteUpdate(operation.node, operation.pageId); - if (prevBlock.type === "ol") { + if (prevBlock && prevBlock.type === "ol") { editorCRDT.current.LinkedList.updateAllOrderedListIndices(); } setEditorState({ From f67999ea5f64cc06b94fa71bdfb9ad4e13835aec Mon Sep 17 00:00:00 2001 From: pipisebastian Date: Thu, 28 Nov 2024 17:25:50 +0900 Subject: [PATCH 18/20] =?UTF-8?q?feat:=20=ED=95=98=EC=9C=84=EC=9A=94?= =?UTF-8?q?=EC=86=8C=EA=B9=8C=EC=A7=80=20=EB=93=9C=EB=9E=98=EA=B7=B8=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #213 --- .../editor/hooks/useBlockDragAndDrop.ts | 52 +++++++++---------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/client/src/features/editor/hooks/useBlockDragAndDrop.ts b/client/src/features/editor/hooks/useBlockDragAndDrop.ts index 3c8eeb62..db65a6c7 100644 --- a/client/src/features/editor/hooks/useBlockDragAndDrop.ts +++ b/client/src/features/editor/hooks/useBlockDragAndDrop.ts @@ -1,4 +1,3 @@ -// hooks/useBlockDragAndDrop.ts import { DragEndEvent, DragStartEvent, PointerSensor, useSensor, useSensors } from "@dnd-kit/core"; import { EditorCRDT } from "@noctaCrdt/Crdt"; import { Block } from "@noctaCrdt/Node"; @@ -32,16 +31,17 @@ export const useBlockDragAndDrop = ({ const { sendBlockReorderOperation, sendBlockUpdateOperation } = useSocketStore(); - const getChildBlocks = (nodes: Block[], parentIndex: number, parentIndent: number): Block[] => { - const children = []; + const getBlocksToMove = (nodes: Block[], parentIndex: number, parentIndent: number): Block[] => { + const blocksToMove = []; let i = parentIndex + 1; + // 자식 블록들 찾기 while (i < nodes.length && nodes[i].indent > parentIndent) { - children.push(nodes[i]); + blocksToMove.push(nodes[i]); i += 1; } - return children; + return blocksToMove; }; const reorderBlocksWithChildren = ( @@ -52,11 +52,10 @@ export const useBlockDragAndDrop = ({ ) => { const operations = []; const targetIndex = nodes.indexOf(targetNode); - const childBlocks = getChildBlocks(nodes, targetIndex, targetNode.indent); + const childBlocks = getBlocksToMove(nodes, targetIndex, targetNode.indent); // 이동할 위치의 부모 블록 indent 찾기 let newIndent = 0; - console.log("beforeNode", beforeNode, afterNode); if (beforeNode) { // 앞 블록이 있는 경우, 그 블록의 indent를 기준으로 newIndent = beforeNode.indent; @@ -67,41 +66,41 @@ export const useBlockDragAndDrop = ({ // indent 변화량 계산 -> 추후 자식 블록들에 indentDiff만큼 적용 const indentDiff = newIndent - targetNode.indent; - // 타겟 블록 업데이트 targetNode.indent = newIndent; - // Reorder 연산 - const reorderOp = editorCRDT.localReorder({ + // 타겟 블록의 reorder 연산 처리 + const targetReorderOp = editorCRDT.localReorder({ targetId: targetNode.id, beforeId: beforeNode?.id || null, afterId: afterNode?.id || null, pageId, }); - operations.push({ type: "reorder", operation: reorderOp }); + operations.push({ type: "reorder", operation: targetReorderOp }); // Update 연산 (indent 갱신) - const updateOp = editorCRDT.localUpdate(targetNode, pageId); - operations.push({ type: "update", operation: updateOp }); + const targetUpdateOp = editorCRDT.localUpdate(targetNode, pageId); + operations.push({ type: "update", operation: targetUpdateOp }); // 자식 블록들 처리 let prevBlock = targetNode; - childBlocks.forEach((child) => { - const childNewIndent = Math.max(0, child.indent + indentDiff); - child.indent = childNewIndent; + childBlocks.forEach((childBlock, index) => { + const childNewIndent = Math.max(0, childBlock.indent + indentDiff); + childBlock.indent = childNewIndent; + // 마지막일 경우 after Id를 afterNode 로 설정 const childReorderOp = editorCRDT.localReorder({ - targetId: child.id, + targetId: childBlock.id, beforeId: prevBlock.id, - afterId: afterNode?.id || null, + afterId: afterNode && index === childBlocks.length - 1 ? afterNode?.id : null, pageId, }); operations.push({ type: "reorder", operation: childReorderOp }); - const childUpdateOp = editorCRDT.localUpdate(child, pageId); + const childUpdateOp = editorCRDT.localUpdate(childBlock, pageId); operations.push({ type: "update", operation: childUpdateOp }); - prevBlock = child; + prevBlock = childBlock; }); return operations; @@ -121,7 +120,6 @@ export const useBlockDragAndDrop = ({ // 지금 놓으려는 블록(over)이 드래깅 중인 블록이거나, 드래깅 중인 블록의 자식 블록이면 무시 const disableDrag = dragBlockList.some((item) => item === over.data.current?.id); - if (disableDrag) return; try { @@ -189,7 +187,6 @@ export const useBlockDragAndDrop = ({ const handleDragStart = ( event: DragStartEvent, - setDragBlockList: React.Dispatch>, ) => { document.body.style.cursor = "grabbing"; @@ -207,22 +204,21 @@ export const useBlockDragAndDrop = ({ if (parentIndex === -1) return []; - const childBlockId = []; + const childBlockIds = []; for (let i = parentIndex + 1; i < blocks.length; i++) { if (blocks[i].indent > parentIndent) { - childBlockId.push(`${blocks[i].id.client}-${blocks[i].id.clock}`); + childBlockIds.push(`${blocks[i].id.client}-${blocks[i].id.clock}`); } else { break; } } - return childBlockId; + return childBlockIds; }; - const childBlockId = findChildBlocks(parentId); - - setDragBlockList([parentId, ...childBlockId]); + const childBlockIds = findChildBlocks(parentId); + setDragBlockList([parentId, ...childBlockIds]); }; return { From 5f2c92af90366ac4f6bb1c8bf55acd8397031ee5 Mon Sep 17 00:00:00 2001 From: pipisebastian Date: Thu, 28 Nov 2024 17:29:01 +0900 Subject: [PATCH 19/20] =?UTF-8?q?refactor:=20=EC=95=88=EC=93=B0=EB=8A=94?= =?UTF-8?q?=20css=20import=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #213 --- client/src/features/editor/components/block/Block.style.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/features/editor/components/block/Block.style.ts b/client/src/features/editor/components/block/Block.style.ts index 05c0d0e3..89e9dc3e 100644 --- a/client/src/features/editor/components/block/Block.style.ts +++ b/client/src/features/editor/components/block/Block.style.ts @@ -1,4 +1,4 @@ -import { css, cva } from "@styled-system/css"; +import { cva } from "@styled-system/css"; const baseBlockStyle = { display: "flex", From 9a9d7e44a6d6f73cd70aedb0d237510f015a55d7 Mon Sep 17 00:00:00 2001 From: pipisebastian Date: Thu, 28 Nov 2024 17:37:15 +0900 Subject: [PATCH 20/20] =?UTF-8?q?refactor:=20=EA=B8=B0=EC=A1=B4=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EB=B3=B5=EA=B5=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #213 --- @noctaCrdt/LinkedList.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/@noctaCrdt/LinkedList.ts b/@noctaCrdt/LinkedList.ts index cd8e4d49..759c07a0 100644 --- a/@noctaCrdt/LinkedList.ts +++ b/@noctaCrdt/LinkedList.ts @@ -309,6 +309,10 @@ export class BlockLinkedList extends LinkedList { if (nextNode) nextNode.prev = targetNode.prev; } + if (this.head === targetId) { + this.head = targetNode.next; + } + // 2. 새로운 위치에 노드 삽입 if (!beforeId) { // 맨 앞으로 이동