From 89f117c230c3efc91c4f49ab0307f80a80f6c64a Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Mon, 26 Aug 2024 10:22:49 +0900 Subject: [PATCH] =?UTF-8?q?=EC=84=A0=ED=83=9D=EB=90=9C=20object=EC=97=90?= =?UTF-8?q?=20=EB=94=B0=EB=9D=BC=20=EC=98=A4=EB=A5=B8=EC=AA=BD=EB=A7=88?= =?UTF-8?q?=EC=9A=B0=EC=8A=A4=20=ED=81=B4=EB=A6=AD=20=EC=8B=9C=20contextme?= =?UTF-8?q?nu=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Roof2.jsx | 13 +++- .../common/context-menu/QLineContextMenu.jsx | 71 +++++++++++++++++++ .../context-menu/QPolygonContextMenu.jsx | 71 +++++++++++++++++++ src/hooks/useCanvasEvent.js | 27 +++++-- src/store/canvasAtom.js | 6 ++ 5 files changed, 183 insertions(+), 5 deletions(-) create mode 100644 src/components/common/context-menu/QLineContextMenu.jsx create mode 100644 src/components/common/context-menu/QPolygonContextMenu.jsx diff --git a/src/components/Roof2.jsx b/src/components/Roof2.jsx index 7467a634..2cfd6fae 100644 --- a/src/components/Roof2.jsx +++ b/src/components/Roof2.jsx @@ -11,6 +11,7 @@ import { useRecoilState, useRecoilValue } from 'recoil' import { canvasSizeState, compassState, + currentObjectState, fontSizeState, roofMaterialState, roofState, @@ -27,6 +28,8 @@ import QContextMenu from './common/context-menu/QContextMenu' import { modalContent, modalState } from '@/store/modalAtom' import SettingsModal from './SettingsModal' import { useAxios } from '@/hooks/useAxios' +import QPolygonContextMenu from '@/components/common/context-menu/QPolygonContextMenu' +import QLineContextMenu from '@/components/common/context-menu/QLineContextMenu' export default function Roof2(props) { const { name, userId, email, isLoggedIn } = props @@ -67,6 +70,7 @@ export default function Roof2(props) { const [contents, setContent] = useRecoilState(modalContent) const [scale, setScale] = useState(1) + const currentObject = useRecoilValue(currentObjectState) //canvas 썸네일 const [thumbnails, setThumbnails] = useState([]) @@ -126,6 +130,7 @@ export default function Roof2(props) { if (canvas) { const line = new QLine([50, 50, 200, 50], { stroke: 'black', + selectable: true, strokeWidth: 2, fontSize: fontSize, }) @@ -758,7 +763,13 @@ export default function Roof2(props) {
- {canvas !== undefined && } + {!canvas ? null : currentObject?.type === 'QPolygon' ? ( + + ) : currentObject?.type === 'QLine' ? ( + + ) : ( + + )}
) diff --git a/src/components/common/context-menu/QLineContextMenu.jsx b/src/components/common/context-menu/QLineContextMenu.jsx new file mode 100644 index 00000000..058c3b95 --- /dev/null +++ b/src/components/common/context-menu/QLineContextMenu.jsx @@ -0,0 +1,71 @@ +'use client' +import { useEffect, useState } from 'react' +import { useRecoilState, useRecoilValue } from 'recoil' + +export default function QLineContextMenu(props) { + const { contextRef, canvasProps } = props + + // const children = useRecoilValue(modalContent) + const [contextMenu, setContextMenu] = useState({ visible: false, x: 0, y: 0 }) + + useEffect(() => { + if (!contextRef.current) return + + const handleContextMenu = (e) => { + e.preventDefault() //기존 contextmenu 막고 + setContextMenu({ visible: true, x: e.pageX, y: e.pageY }) + canvasProps.upperCanvasEl.removeEventListener('contextmenu', handleContextMenu) //한번 노출 후 이벤트 삭제 + } + + const handleClick = (e) => { + e.preventDefault() + setContextMenu({ ...contextMenu, visible: false }) + } + + const handleOutsideClick = (e) => { + e.preventDefault() + if (contextMenu.visible && !ref.current.contains(e.target)) { + setContextMenu({ ...contextMenu, visible: false }) + } + } + + // Prevent the default context menu from appearing on the canvas + canvasProps.upperCanvasEl.addEventListener('contextmenu', handleContextMenu) + document.addEventListener('click', handleClick) + document.addEventListener('click', handleOutsideClick) + + return () => { + // canvasProps.upperCanvasEl.removeEventListener('contextmenu', handleContextMenu) + document.removeEventListener('click', handleClick) + document.removeEventListener('click', handleOutsideClick) + } + }, [contextRef, contextMenu]) + + const handleMenuClick = (option) => { + alert(`option ${option} clicked`) + setContextMenu({ ...contextMenu, visible: false }) + } + + return ( + <> + {contextMenu.visible && ( +
+
    +
  • handleMenuClick(1)}> + line +
  • +
  • handleMenuClick(1)}> + Option 1 +
  • +
  • handleMenuClick(2)}> + Option 2 +
  • +
  • handleMenuClick(3)}> + Option 3 +
  • +
+
+ )} + + ) +} diff --git a/src/components/common/context-menu/QPolygonContextMenu.jsx b/src/components/common/context-menu/QPolygonContextMenu.jsx new file mode 100644 index 00000000..b3246165 --- /dev/null +++ b/src/components/common/context-menu/QPolygonContextMenu.jsx @@ -0,0 +1,71 @@ +'use client' +import { useEffect, useState } from 'react' +import { useRecoilState, useRecoilValue } from 'recoil' + +export default function QPolygonContextMenu(props) { + const { contextRef, canvasProps } = props + + // const children = useRecoilValue(modalContent) + const [contextMenu, setContextMenu] = useState({ visible: false, x: 0, y: 0 }) + + useEffect(() => { + if (!contextRef.current) return + + const handleContextMenu = (e) => { + e.preventDefault() //기존 contextmenu 막고 + setContextMenu({ visible: true, x: e.pageX, y: e.pageY }) + canvasProps.upperCanvasEl.removeEventListener('contextmenu', handleContextMenu) //한번 노출 후 이벤트 삭제 + } + + const handleClick = (e) => { + e.preventDefault() + setContextMenu({ ...contextMenu, visible: false }) + } + + const handleOutsideClick = (e) => { + e.preventDefault() + if (contextMenu.visible && !ref.current.contains(e.target)) { + setContextMenu({ ...contextMenu, visible: false }) + } + } + + // Prevent the default context menu from appearing on the canvas + canvasProps.upperCanvasEl.addEventListener('contextmenu', handleContextMenu) + document.addEventListener('click', handleClick) + document.addEventListener('click', handleOutsideClick) + + return () => { + // canvasProps.upperCanvasEl.removeEventListener('contextmenu', handleContextMenu) + document.removeEventListener('click', handleClick) + document.removeEventListener('click', handleOutsideClick) + } + }, [contextRef, contextMenu]) + + const handleMenuClick = (option) => { + alert(`option ${option} clicked`) + setContextMenu({ ...contextMenu, visible: false }) + } + + return ( + <> + {contextMenu.visible && ( +
+
    +
  • handleMenuClick(1)}> + polygon +
  • +
  • handleMenuClick(1)}> + Option 1 +
  • +
  • handleMenuClick(2)}> + Option 2 +
  • +
  • handleMenuClick(3)}> + Option 3 +
  • +
+
+ )} + + ) +} diff --git a/src/hooks/useCanvasEvent.js b/src/hooks/useCanvasEvent.js index 1947708f..ea5256e6 100644 --- a/src/hooks/useCanvasEvent.js +++ b/src/hooks/useCanvasEvent.js @@ -1,11 +1,12 @@ import { useEffect, useState } from 'react' import { fabric } from 'fabric' -import { useRecoilValue } from 'recoil' -import { canvasSizeState, modeState } from '@/store/canvasAtom' +import { useRecoilState, useRecoilValue } from 'recoil' +import { canvasSizeState, currentObjectState, modeState } from '@/store/canvasAtom' // 캔버스에 필요한 이벤트 export function useCanvasEvent() { const [canvas, setCanvasForEvent] = useState(null) + const [currentObject, setCurrentObject] = useRecoilState(currentObjectState) const canvasSize = useRecoilValue(canvasSizeState) // 기본적인 이벤트 필요시 추가 @@ -15,11 +16,28 @@ export function useCanvasEvent() { canvas?.on('object:added', addEventOnObject) canvas?.on('object:modified', onChange) canvas?.on('object:removed', onChange) + canvas?.on('selection:cleared', selectionEvent.cleared) + canvas?.on('selection:created', selectionEvent.created) + canvas?.on('selection:updated', selectionEvent.updated) canvas?.on('object:added', () => { document.addEventListener('keydown', handleKeyDown) }) } + const selectionEvent = { + created: (e) => { + const target = e.selected[0] + setCurrentObject(target) + }, + cleared: (e) => { + setCurrentObject(null) + }, + updated: (e) => { + const target = e.selected[0] + setCurrentObject(target) + }, + } + const onChange = (e) => { const target = e.target if (target) { @@ -32,8 +50,9 @@ export function useCanvasEvent() { canvas?.off('object:modified') canvas?.off('object:removed') canvas?.off('object:added') - canvas?.off('mouse:move') - canvas?.off('mouse:down') + canvas?.off('selection:cleared') + canvas?.off('selection:created') + canvas?.off('selection:updated') } const addEventOnObject = (e) => { diff --git a/src/store/canvasAtom.js b/src/store/canvasAtom.js index 062698c0..fafe26a1 100644 --- a/src/store/canvasAtom.js +++ b/src/store/canvasAtom.js @@ -94,3 +94,9 @@ export const guideLineState = atom({ default: {}, dangerouslyAllowMutability: true, }) + +export const currentObjectState = atom({ + key: 'currentObject', + default: null, + dangerouslyAllowMutability: true, +})