선택된 object에 따라 오른쪽마우스 클릭 시 contextmenu 변경
This commit is contained in:
parent
39e8dc335e
commit
89f117c230
@ -11,6 +11,7 @@ import { useRecoilState, useRecoilValue } from 'recoil'
|
|||||||
import {
|
import {
|
||||||
canvasSizeState,
|
canvasSizeState,
|
||||||
compassState,
|
compassState,
|
||||||
|
currentObjectState,
|
||||||
fontSizeState,
|
fontSizeState,
|
||||||
roofMaterialState,
|
roofMaterialState,
|
||||||
roofState,
|
roofState,
|
||||||
@ -27,6 +28,8 @@ import QContextMenu from './common/context-menu/QContextMenu'
|
|||||||
import { modalContent, modalState } from '@/store/modalAtom'
|
import { modalContent, modalState } from '@/store/modalAtom'
|
||||||
import SettingsModal from './SettingsModal'
|
import SettingsModal from './SettingsModal'
|
||||||
import { useAxios } from '@/hooks/useAxios'
|
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) {
|
export default function Roof2(props) {
|
||||||
const { name, userId, email, isLoggedIn } = props
|
const { name, userId, email, isLoggedIn } = props
|
||||||
@ -67,6 +70,7 @@ export default function Roof2(props) {
|
|||||||
const [contents, setContent] = useRecoilState(modalContent)
|
const [contents, setContent] = useRecoilState(modalContent)
|
||||||
|
|
||||||
const [scale, setScale] = useState(1)
|
const [scale, setScale] = useState(1)
|
||||||
|
const currentObject = useRecoilValue(currentObjectState)
|
||||||
|
|
||||||
//canvas 썸네일
|
//canvas 썸네일
|
||||||
const [thumbnails, setThumbnails] = useState([])
|
const [thumbnails, setThumbnails] = useState([])
|
||||||
@ -126,6 +130,7 @@ export default function Roof2(props) {
|
|||||||
if (canvas) {
|
if (canvas) {
|
||||||
const line = new QLine([50, 50, 200, 50], {
|
const line = new QLine([50, 50, 200, 50], {
|
||||||
stroke: 'black',
|
stroke: 'black',
|
||||||
|
selectable: true,
|
||||||
strokeWidth: 2,
|
strokeWidth: 2,
|
||||||
fontSize: fontSize,
|
fontSize: fontSize,
|
||||||
})
|
})
|
||||||
@ -758,7 +763,13 @@ export default function Roof2(props) {
|
|||||||
<ThumbnailList {...thumbnailProps} />
|
<ThumbnailList {...thumbnailProps} />
|
||||||
<div className="flex justify-start my-8 mx-2 w-full">
|
<div className="flex justify-start my-8 mx-2 w-full">
|
||||||
<canvas ref={canvasRef} id="canvas" style={{ border: '1px solid black' }} />
|
<canvas ref={canvasRef} id="canvas" style={{ border: '1px solid black' }} />
|
||||||
{canvas !== undefined && <QContextMenu contextRef={canvasRef} canvasProps={canvas} />}
|
{!canvas ? null : currentObject?.type === 'QPolygon' ? (
|
||||||
|
<QPolygonContextMenu contextRef={canvasRef} canvasProps={canvas} />
|
||||||
|
) : currentObject?.type === 'QLine' ? (
|
||||||
|
<QLineContextMenu contextRef={canvasRef} canvasProps={canvas} />
|
||||||
|
) : (
|
||||||
|
<QContextMenu contextRef={canvasRef} canvasProps={canvas} />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|||||||
71
src/components/common/context-menu/QLineContextMenu.jsx
Normal file
71
src/components/common/context-menu/QLineContextMenu.jsx
Normal file
@ -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 && (
|
||||||
|
<div style={{ position: 'absolute', top: contextMenu.y, left: contextMenu.x, zIndex: 2000 }}>
|
||||||
|
<ul style={{ listStyle: 'none', margin: 0, padding: '5px' }}>
|
||||||
|
<li style={{ padding: '8px 12px', cursor: 'pointer' }} onClick={() => handleMenuClick(1)}>
|
||||||
|
line
|
||||||
|
</li>
|
||||||
|
<li style={{ padding: '8px 12px', cursor: 'pointer' }} onClick={() => handleMenuClick(1)}>
|
||||||
|
Option 1
|
||||||
|
</li>
|
||||||
|
<li style={{ padding: '8px 12px', cursor: 'pointer' }} onClick={() => handleMenuClick(2)}>
|
||||||
|
Option 2
|
||||||
|
</li>
|
||||||
|
<li style={{ padding: '8px 12px', cursor: 'pointer' }} onClick={() => handleMenuClick(3)}>
|
||||||
|
Option 3
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
71
src/components/common/context-menu/QPolygonContextMenu.jsx
Normal file
71
src/components/common/context-menu/QPolygonContextMenu.jsx
Normal file
@ -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 && (
|
||||||
|
<div style={{ position: 'absolute', top: contextMenu.y, left: contextMenu.x, zIndex: 2000 }}>
|
||||||
|
<ul style={{ listStyle: 'none', margin: 0, padding: '5px' }}>
|
||||||
|
<li style={{ padding: '8px 12px', cursor: 'pointer' }} onClick={() => handleMenuClick(1)}>
|
||||||
|
polygon
|
||||||
|
</li>
|
||||||
|
<li style={{ padding: '8px 12px', cursor: 'pointer' }} onClick={() => handleMenuClick(1)}>
|
||||||
|
Option 1
|
||||||
|
</li>
|
||||||
|
<li style={{ padding: '8px 12px', cursor: 'pointer' }} onClick={() => handleMenuClick(2)}>
|
||||||
|
Option 2
|
||||||
|
</li>
|
||||||
|
<li style={{ padding: '8px 12px', cursor: 'pointer' }} onClick={() => handleMenuClick(3)}>
|
||||||
|
Option 3
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -1,11 +1,12 @@
|
|||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { fabric } from 'fabric'
|
import { fabric } from 'fabric'
|
||||||
import { useRecoilValue } from 'recoil'
|
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||||
import { canvasSizeState, modeState } from '@/store/canvasAtom'
|
import { canvasSizeState, currentObjectState, modeState } from '@/store/canvasAtom'
|
||||||
|
|
||||||
// 캔버스에 필요한 이벤트
|
// 캔버스에 필요한 이벤트
|
||||||
export function useCanvasEvent() {
|
export function useCanvasEvent() {
|
||||||
const [canvas, setCanvasForEvent] = useState(null)
|
const [canvas, setCanvasForEvent] = useState(null)
|
||||||
|
const [currentObject, setCurrentObject] = useRecoilState(currentObjectState)
|
||||||
const canvasSize = useRecoilValue(canvasSizeState)
|
const canvasSize = useRecoilValue(canvasSizeState)
|
||||||
|
|
||||||
// 기본적인 이벤트 필요시 추가
|
// 기본적인 이벤트 필요시 추가
|
||||||
@ -15,11 +16,28 @@ export function useCanvasEvent() {
|
|||||||
canvas?.on('object:added', addEventOnObject)
|
canvas?.on('object:added', addEventOnObject)
|
||||||
canvas?.on('object:modified', onChange)
|
canvas?.on('object:modified', onChange)
|
||||||
canvas?.on('object:removed', 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', () => {
|
canvas?.on('object:added', () => {
|
||||||
document.addEventListener('keydown', handleKeyDown)
|
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 onChange = (e) => {
|
||||||
const target = e.target
|
const target = e.target
|
||||||
if (target) {
|
if (target) {
|
||||||
@ -32,8 +50,9 @@ export function useCanvasEvent() {
|
|||||||
canvas?.off('object:modified')
|
canvas?.off('object:modified')
|
||||||
canvas?.off('object:removed')
|
canvas?.off('object:removed')
|
||||||
canvas?.off('object:added')
|
canvas?.off('object:added')
|
||||||
canvas?.off('mouse:move')
|
canvas?.off('selection:cleared')
|
||||||
canvas?.off('mouse:down')
|
canvas?.off('selection:created')
|
||||||
|
canvas?.off('selection:updated')
|
||||||
}
|
}
|
||||||
|
|
||||||
const addEventOnObject = (e) => {
|
const addEventOnObject = (e) => {
|
||||||
|
|||||||
@ -94,3 +94,9 @@ export const guideLineState = atom({
|
|||||||
default: {},
|
default: {},
|
||||||
dangerouslyAllowMutability: true,
|
dangerouslyAllowMutability: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const currentObjectState = atom({
|
||||||
|
key: 'currentObject',
|
||||||
|
default: null,
|
||||||
|
dangerouslyAllowMutability: true,
|
||||||
|
})
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user