Skip to content

Commit

Permalink
feat: indicator ๊ตฌํ˜„
Browse files Browse the repository at this point in the history
  • Loading branch information
pipisebastian committed Nov 27, 2024
1 parent 6be2141 commit 34bf566
Showing 1 changed file with 89 additions and 64 deletions.
153 changes: 89 additions & 64 deletions client/src/features/editor/components/block/Block.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import {
AnimationType,
ElementType,
Expand All @@ -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<HTMLDivElement>, block: CRDTBlock) => void;
onCompositionEnd: (e: React.CompositionEvent<HTMLDivElement>, block: CRDTBlock) => void;
Expand Down Expand Up @@ -64,6 +69,7 @@ export const Block: React.FC<BlockProps> = memo(
({
id,
block,
draggingBlock,
isActive,
onInput,
onCompositionEnd,
Expand All @@ -83,13 +89,24 @@ export const Block: React.FC<BlockProps> = memo(
const { isOpen, openModal, closeModal } = useModal();
const [selectedNodes, setSelectedNodes] = useState<Array<Char> | 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 });
Expand Down Expand Up @@ -216,6 +233,14 @@ export const Block: React.FC<BlockProps> = memo(
}
};

const Indicator = () => (
<div
className={dropIndicatorStyle({
indent: block.indent === 0 ? "first" : block.indent === 1 ? "second" : "third",
})}
/>
);

useEffect(() => {
if (blockRef.current) {
console.log("setInnerHTML");
Expand All @@ -226,66 +251,66 @@ export const Block: React.FC<BlockProps> = memo(
return (
// TODO: eslint ๊ทœ์น™์„ ์ˆ˜์ •ํ•ด์•ผ ํ• ๊นŒ?
// TODO: ol์ผ๋•Œ index ์ˆœ์„œ ์ฒ˜๋ฆฌ
<motion.div
ref={setNodeRef}
className={blockContainerStyle({ isActive })}
style={{
transform: CSS.Transform.toString(transform),
transition,
opacity: isDragging ? 0.5 : undefined,
}}
initial={blockAnimation[block.animation || "none"].initial}
animate={isAnimationStart && blockAnimation[block.animation || "none"].animate}
data-group
>
<div style={{ position: "relative" }}>
{showTopIndicator && <Indicator />}
<motion.div
className={contentWrapperStyle()}
style={{ paddingLeft: `${block.indent * 12}px` }}
ref={setNodeRef}
className={blockContainerStyle({ isActive })}
style={{ opacity: isDragging || isChildOfDragging ? 0.3 : undefined }}
initial={blockAnimation[block.animation || "none"].initial}
animate={isAnimationStart && blockAnimation[block.animation || "none"].animate}
data-group
>
<MenuBlock
attributes={attributes}
listeners={listeners}
onAnimationSelect={handleAnimationSelect}
onTypeSelect={handleTypeSelect}
onCopySelect={handleCopySelect}
onDeleteSelect={handleDeleteSelect}
<motion.div
className={contentWrapperStyle()}
style={{ paddingLeft: `${block.indent * 12}px` }}
>
<MenuBlock
attributes={attributes}
listeners={listeners}
onAnimationSelect={handleAnimationSelect}
onTypeSelect={handleTypeSelect}
onCopySelect={handleCopySelect}
onDeleteSelect={handleDeleteSelect}
/>
<IconBlock type={block.type} index={block.listIndex} />
<div
ref={blockRef}
onKeyDown={(e) => 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,
})}
/>
</motion.div>
<TextOptionModal
selectedNodes={selectedNodes}
isOpen={isOpen}
onClose={closeModal}
onBoldSelect={() => handleStyleSelect("bold")}
onItalicSelect={() => handleStyleSelect("italic")}
onUnderlineSelect={() => handleStyleSelect("underline")}
onStrikeSelect={() => handleStyleSelect("strikethrough")}
onTextColorSelect={handleTextColorSelect}
onTextBackgroundColorSelect={handleTextBackgroundColorSelect}
/>
<IconBlock type={block.type} index={block.listIndex} />
<div
ref={blockRef}
onKeyDown={(e) => 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,
})}
<TypeOptionModal
isOpen={slashModalOpen}
onClose={() => setSlashModalOpen(false)}
onTypeSelect={(type) => handleTypeSelect(type)}
position={slashModalPosition}
/>
</motion.div>
<TextOptionModal
selectedNodes={selectedNodes}
isOpen={isOpen}
onClose={closeModal}
onBoldSelect={() => handleStyleSelect("bold")}
onItalicSelect={() => handleStyleSelect("italic")}
onUnderlineSelect={() => handleStyleSelect("underline")}
onStrikeSelect={() => handleStyleSelect("strikethrough")}
onTextColorSelect={handleTextColorSelect}
onTextBackgroundColorSelect={handleTextBackgroundColorSelect}
/>
<TypeOptionModal
isOpen={slashModalOpen}
onClose={() => setSlashModalOpen(false)}
onTypeSelect={(type) => handleTypeSelect(type)}
position={slashModalPosition}
/>
</motion.div>
{showBottomIndicator && <Indicator />}
</div>
);
},
);
Expand Down

0 comments on commit 34bf566

Please sign in to comment.