-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/#162 블록 옵션창 구현 #170
The head ref may contain hidden characters: "Feature/#162_\uBE14\uB85D_\uC635\uC158\uCC3D_\uAD6C\uD604"
Changes from all commits
16db53a
344d963
f80740c
f5c8906
5cf6b10
bf8a7f4
5c55f2a
f7ebfd5
c1c8111
80fa75b
1e643b1
ff7ed68
11b6ef8
f62153f
64dac06
f0c5021
cd7954b
e3980b9
75a9947
7e603eb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import { AnimationType, ElementType } from "@noctaCrdt/Interfaces"; | ||
|
||
export const OPTION_CATEGORIES = { | ||
TYPE: { | ||
id: "type", | ||
label: "전환", | ||
options: [ | ||
{ id: "p", label: "p" }, | ||
{ id: "h1", label: "h1" }, | ||
{ id: "h2", label: "h2" }, | ||
{ id: "h3", label: "h3" }, | ||
{ id: "ul", label: "ul" }, | ||
{ id: "ol", label: "ol" }, | ||
{ id: "checkbox", label: "checkbox" }, | ||
{ id: "blockquote", label: "blockquote" }, | ||
] as { id: ElementType; label: string }[], | ||
}, | ||
ANIMATION: { | ||
id: "animation", | ||
label: "애니메이션", | ||
options: [ | ||
{ id: "none", label: "없음" }, | ||
{ id: "highlight", label: "하이라이트" }, | ||
{ id: "gradation", label: "그라데이션" }, | ||
] as { id: AnimationType; label: string }[], | ||
}, | ||
DUPLICATE: { | ||
id: "duplicate", | ||
label: "복제", | ||
options: null, | ||
}, | ||
DELETE: { | ||
id: "delete", | ||
label: "삭제", | ||
options: null, | ||
}, | ||
}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good !!! 👍 |
||
|
||
export type OptionCategory = keyof typeof OPTION_CATEGORIES; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,89 @@ | ||
import { AnimationType, ElementType } from "@noctaCrdt/Interfaces"; | ||
import { useState, useRef } from "react"; | ||
import DraggableIcon from "@assets/icons/draggable.svg?url"; | ||
import { useModal } from "@src/components/modal/useModal"; | ||
import { OptionModal } from "../OptionModal/OptionModal"; | ||
import { menuBlockStyle, dragHandleIconStyle } from "./MenuBlock.style"; | ||
|
||
interface MenuBlockProps { | ||
export interface MenuBlockProps { | ||
attributes?: Record<string, any>; | ||
listeners?: Record<string, any>; | ||
onAnimationSelect: (animation: AnimationType) => void; | ||
onTypeSelect: (type: ElementType) => void; | ||
onCopySelect: () => void; | ||
onDeleteSelect: () => void; | ||
} | ||
|
||
export const MenuBlock = ({ attributes, listeners }: MenuBlockProps) => { | ||
export const MenuBlock = ({ | ||
attributes, | ||
listeners, | ||
onAnimationSelect, | ||
onTypeSelect, | ||
onCopySelect, | ||
onDeleteSelect, | ||
}: MenuBlockProps) => { | ||
const menuBlockRef = useRef<HTMLDivElement>(null); | ||
|
||
const [pressTime, setPressTime] = useState<NodeJS.Timeout | null>(null); | ||
const [isDragging, setIsDragging] = useState(false); | ||
const [menuBlockPosition, setMenuBlockPosition] = useState<{ top: number; right: number }>({ | ||
top: 0, | ||
right: 0, | ||
}); | ||
|
||
const { isOpen, openModal, closeModal } = useModal(); | ||
|
||
const handlePressStart = () => { | ||
const timer = setTimeout(() => { | ||
setIsDragging(true); | ||
}, 300); | ||
|
||
setPressTime(timer); | ||
}; | ||
|
||
const handlePressEnd = () => { | ||
if (pressTime) { | ||
clearTimeout(pressTime); | ||
setPressTime(null); | ||
} | ||
|
||
if (!isDragging) { | ||
if (menuBlockRef.current) { | ||
const { top, right } = menuBlockRef.current.getBoundingClientRect(); | ||
setMenuBlockPosition({ top, right }); | ||
} | ||
openModal(); | ||
} | ||
setIsDragging(false); | ||
}; | ||
|
||
const modifiedListeners = { | ||
...listeners, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이렇게 리스너 함수를 가져와서 이벤트를 덮어 쓸 수가 있군요..! 한수 배웠습니다 ! |
||
// dnd 이벤트 덮어쓰기 | ||
onMouseDown: (e: React.MouseEvent) => { | ||
handlePressStart(); | ||
listeners?.onMouseDown?.(e); | ||
}, | ||
onMouseUp: (e: React.MouseEvent) => { | ||
handlePressEnd(); | ||
listeners?.onMouseUp?.(e); | ||
}, | ||
}; | ||
|
||
return ( | ||
<div className={menuBlockStyle} {...attributes} {...listeners}> | ||
<div ref={menuBlockRef} className={menuBlockStyle} {...attributes} {...modifiedListeners}> | ||
<div className={dragHandleIconStyle}> | ||
<img src={DraggableIcon} alt="drag handle" width="10" height="10" /> | ||
</div> | ||
<OptionModal | ||
isOpen={isOpen} | ||
onClose={closeModal} | ||
menuBlockPosition={menuBlockPosition} | ||
onAnimationSelect={onAnimationSelect} | ||
onTypeSelect={onTypeSelect} | ||
onDeleteSelect={onDeleteSelect} | ||
onCopySelect={onCopySelect} | ||
/> | ||
</div> | ||
); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
export const modal = { | ||
initial: { | ||
opacity: 0, | ||
x: -5, | ||
}, | ||
animate: { | ||
opacity: 1, | ||
x: 0, | ||
}, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { css } from "@styled-system/css"; | ||
|
||
export const optionModal = css({ | ||
zIndex: "10000", | ||
position: "fixed", | ||
borderRadius: "8px", | ||
width: "160px", | ||
padding: "8px", | ||
background: "white", | ||
boxShadow: "md", | ||
}); | ||
|
||
export const optionButton = css({ | ||
borderRadius: "8px", | ||
width: "100%", | ||
paddingBlock: "4px", | ||
paddingInline: "8px", | ||
textAlign: "left", | ||
_hover: { | ||
backgroundColor: "gray.100/40", | ||
cursor: "pointer", | ||
}, | ||
}); | ||
|
||
export const modalContainer = css({ | ||
display: "flex", | ||
gap: "1", | ||
flexDirection: "column", | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 부분에서 id와 별개로 이름을 h1 -> 제목1, h2 -> 제목2, checkbox -> 체크박스 등으로 한글 이름을 사용하는것도 좋아보입니다!