Merge branch 'dev' into dev-yj
This commit is contained in:
commit
d389b03577
@ -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([])
|
||||
@ -125,6 +129,7 @@ export default function Roof2(props) {
|
||||
if (canvas) {
|
||||
const line = new QLine([50, 50, 200, 50], {
|
||||
stroke: 'black',
|
||||
selectable: true,
|
||||
strokeWidth: 2,
|
||||
fontSize: fontSize,
|
||||
})
|
||||
@ -757,7 +762,13 @@ export default function Roof2(props) {
|
||||
<ThumbnailList {...thumbnailProps} />
|
||||
<div className="flex justify-start my-8 mx-2 w-full">
|
||||
<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>
|
||||
</>
|
||||
)
|
||||
|
||||
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>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -11,6 +11,7 @@ export const QLine = fabric.util.createClass(fabric.Line, {
|
||||
direction: null,
|
||||
idx: 0,
|
||||
area: 0,
|
||||
children: [],
|
||||
initialize: function (points, options, canvas) {
|
||||
this.callSuper('initialize', points, { ...options, selectable: options.selectable ?? false })
|
||||
if (options.id) {
|
||||
@ -122,6 +123,7 @@ export const QLine = fabric.util.createClass(fabric.Line, {
|
||||
lockRotation: true,
|
||||
lockScalingX: true,
|
||||
lockScalingY: true,
|
||||
parent: this,
|
||||
name: 'lengthText',
|
||||
})
|
||||
|
||||
|
||||
@ -17,6 +17,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
||||
cells: [],
|
||||
parentId: null,
|
||||
innerLines: [],
|
||||
children: [],
|
||||
initialize: function (points, options, canvas) {
|
||||
// 소수점 전부 제거
|
||||
points.forEach((point) => {
|
||||
@ -195,6 +196,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
||||
lockScalingY: true,
|
||||
idx: i,
|
||||
name: 'lengthText',
|
||||
parent: this,
|
||||
})
|
||||
|
||||
this.texts.push(text)
|
||||
@ -271,6 +273,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
||||
name: 'cell',
|
||||
idx: idx,
|
||||
parentId: this.id,
|
||||
parent: this,
|
||||
})
|
||||
|
||||
idx++
|
||||
|
||||
@ -179,25 +179,6 @@ export function useCanvas(id) {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 선택한 도형을 삭제한다.
|
||||
*/
|
||||
const handleDelete = () => {
|
||||
const targets = canvas?.getActiveObjects()
|
||||
if (targets?.length === 0) {
|
||||
alert('삭제할 대상을 선택해주세요.')
|
||||
return
|
||||
}
|
||||
|
||||
if (!confirm('정말로 삭제하시겠습니까?')) {
|
||||
return
|
||||
}
|
||||
|
||||
targets?.forEach((target) => {
|
||||
canvas?.remove(target)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 페이지 내 캔버스 저장
|
||||
* todo : 현재는 localStorage에 그냥 저장하지만 나중에 변경해야함
|
||||
@ -469,7 +450,6 @@ export function useCanvas(id) {
|
||||
handleUndo,
|
||||
handleRedo,
|
||||
handleCopy,
|
||||
handleDelete,
|
||||
handleSave,
|
||||
handlePaste,
|
||||
handleRotate,
|
||||
|
||||
@ -1,162 +1,146 @@
|
||||
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)
|
||||
|
||||
// 기본적인 이벤트 필요시 추가
|
||||
const attachDefaultEventOnCanvas = () => {
|
||||
removeEventOnCanvas()
|
||||
canvas?.on('object:added', onChange)
|
||||
canvas?.on('object:added', addEventOnObject)
|
||||
canvas?.on('object:modified', onChange)
|
||||
canvas?.on('object:removed', onChange)
|
||||
canvas?.on('object:added', objectEvent.onChange)
|
||||
canvas?.on('object:added', objectEvent.addEvent)
|
||||
canvas?.on('object:modified', objectEvent.onChange)
|
||||
canvas?.on('object:removed', objectEvent.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)
|
||||
})
|
||||
canvas?.on('object:removed', objectEvent.removed)
|
||||
}
|
||||
|
||||
const onChange = (e) => {
|
||||
const target = e.target
|
||||
if (target) {
|
||||
// settleDown(target)
|
||||
}
|
||||
const objectEvent = {
|
||||
onChange: (e) => {
|
||||
const target = e.target
|
||||
if (target) {
|
||||
// settleDown(target)
|
||||
}
|
||||
},
|
||||
addEvent: (e) => {
|
||||
const target = e.target
|
||||
|
||||
if (target.type === 'QPolygon' || target.type === 'QLine') {
|
||||
const textObjs = canvas?.getObjects().filter((obj) => obj.name === 'lengthText')
|
||||
textObjs.forEach((obj) => {
|
||||
obj.bringToFront()
|
||||
})
|
||||
}
|
||||
|
||||
if (target.name === 'cell') {
|
||||
target.on('mousedown', () => {
|
||||
if (target.get('selected')) {
|
||||
target.set({ selected: false })
|
||||
target.set({ fill: '#BFFD9F' })
|
||||
} else {
|
||||
target.set({ selected: true })
|
||||
target.set({ fill: 'red' })
|
||||
}
|
||||
canvas?.renderAll()
|
||||
})
|
||||
}
|
||||
|
||||
if (target.name === 'trestle') {
|
||||
target.on('mousedown', () => {
|
||||
if (target.defense === 'north') {
|
||||
alert('북쪽은 선택 불가합니다.')
|
||||
return
|
||||
}
|
||||
if (target.get('selected')) {
|
||||
target.set({ strokeWidth: 1 })
|
||||
target.set({ strokeDashArray: [5, 5] })
|
||||
target.set({ selected: false })
|
||||
} else {
|
||||
target.set({ strokeWidth: 5 })
|
||||
target.set({ strokeDashArray: [0, 0] })
|
||||
target.set({ selected: true })
|
||||
}
|
||||
canvas?.renderAll()
|
||||
})
|
||||
}
|
||||
|
||||
if (target.name === 'lengthText') {
|
||||
const x = target.left
|
||||
const y = target.top
|
||||
target.on('selected', (e) => {
|
||||
Object.keys(target.controls).forEach((controlKey) => {
|
||||
target.setControlVisible(controlKey, false)
|
||||
})
|
||||
})
|
||||
target.on('moving', (e) => {
|
||||
if (target.parentDirection === 'left' || target.parentDirection === 'right') {
|
||||
const minX = target.minX
|
||||
const maxX = target.maxX
|
||||
|
||||
if (target.left <= minX) {
|
||||
target.set({ left: minX, top: y })
|
||||
} else if (target.left >= maxX) {
|
||||
target.set({ left: maxX, top: y })
|
||||
} else {
|
||||
target.set({ top: y })
|
||||
}
|
||||
} else if (target.parentDirection === 'top' || target.parentDirection === 'bottom') {
|
||||
const minY = target.minY
|
||||
const maxY = target.maxY
|
||||
|
||||
if (target.top <= minY) {
|
||||
target.set({ left: x, top: minY })
|
||||
} else if (target.top >= maxY) {
|
||||
target.set({ left: x, top: maxY })
|
||||
} else {
|
||||
target.set({ left: x })
|
||||
}
|
||||
}
|
||||
canvas?.renderAll()
|
||||
})
|
||||
}
|
||||
},
|
||||
removed: (e) => {
|
||||
const whiteList = ['mouseLine', 'guideLine']
|
||||
|
||||
if (whiteList.includes(e.target.name)) {
|
||||
return
|
||||
}
|
||||
console.log('removed', e)
|
||||
},
|
||||
}
|
||||
|
||||
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 removeEventOnCanvas = () => {
|
||||
canvas?.off('object:added')
|
||||
canvas?.off('object:modified')
|
||||
canvas?.off('object:removed')
|
||||
canvas?.off('object:added')
|
||||
canvas?.off('mouse:move')
|
||||
canvas?.off('mouse:down')
|
||||
}
|
||||
|
||||
const addEventOnObject = (e) => {
|
||||
const target = e.target
|
||||
|
||||
if (target.type === 'QPolygon' || target.type === 'QLine') {
|
||||
const textObjs = canvas?.getObjects().filter((obj) => obj.name === 'lengthText')
|
||||
textObjs.forEach((obj) => {
|
||||
obj.bringToFront()
|
||||
})
|
||||
}
|
||||
|
||||
if (target.name === 'cell') {
|
||||
target.on('mousedown', () => {
|
||||
if (target.get('selected')) {
|
||||
target.set({ selected: false })
|
||||
target.set({ fill: '#BFFD9F' })
|
||||
} else {
|
||||
target.set({ selected: true })
|
||||
target.set({ fill: 'red' })
|
||||
}
|
||||
canvas?.renderAll()
|
||||
})
|
||||
}
|
||||
|
||||
if (target.name === 'trestle') {
|
||||
target.on('mousedown', () => {
|
||||
if (target.defense === 'north') {
|
||||
alert('북쪽은 선택 불가합니다.')
|
||||
return
|
||||
}
|
||||
if (target.get('selected')) {
|
||||
target.set({ strokeWidth: 1 })
|
||||
target.set({ strokeDashArray: [5, 5] })
|
||||
target.set({ selected: false })
|
||||
} else {
|
||||
target.set({ strokeWidth: 5 })
|
||||
target.set({ strokeDashArray: [0, 0] })
|
||||
target.set({ selected: true })
|
||||
}
|
||||
canvas?.renderAll()
|
||||
})
|
||||
}
|
||||
|
||||
if (target.name === 'lengthText') {
|
||||
const x = target.left
|
||||
const y = target.top
|
||||
target.on('selected', (e) => {
|
||||
Object.keys(target.controls).forEach((controlKey) => {
|
||||
target.setControlVisible(controlKey, false)
|
||||
})
|
||||
})
|
||||
target.on('moving', (e) => {
|
||||
if (target.parentDirection === 'left' || target.parentDirection === 'right') {
|
||||
const minX = target.minX
|
||||
const maxX = target.maxX
|
||||
|
||||
if (target.left <= minX) {
|
||||
target.set({ left: minX, top: y })
|
||||
} else if (target.left >= maxX) {
|
||||
target.set({ left: maxX, top: y })
|
||||
} else {
|
||||
target.set({ top: y })
|
||||
}
|
||||
} else if (target.parentDirection === 'top' || target.parentDirection === 'bottom') {
|
||||
const minY = target.minY
|
||||
const maxY = target.maxY
|
||||
|
||||
if (target.top <= minY) {
|
||||
target.set({ left: x, top: minY })
|
||||
} else if (target.top >= maxY) {
|
||||
target.set({ left: x, top: maxY })
|
||||
} else {
|
||||
target.set({ left: x })
|
||||
}
|
||||
}
|
||||
canvas?.renderAll()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 마우스 가로, 세로선 그리기
|
||||
const drawMouseLines = (e) => {
|
||||
// 현재 마우스 포인터의 위치를 가져옵니다.
|
||||
const pointer = canvas?.getPointer(e.e)
|
||||
|
||||
// 기존에 그려진 가이드라인을 제거합니다.
|
||||
removeMouseLines()
|
||||
|
||||
if (canvas?.getActiveObject()) {
|
||||
return
|
||||
}
|
||||
|
||||
// 가로선을 그립니다.
|
||||
const horizontalLine = new fabric.Line([0, pointer.y, canvasSize.horizontal, pointer.y], {
|
||||
stroke: 'black',
|
||||
strokeWidth: 1,
|
||||
selectable: false,
|
||||
name: 'mouseLine',
|
||||
})
|
||||
|
||||
// 세로선을 그립니다.
|
||||
const verticalLine = new fabric.Line([pointer.x, 0, pointer.x, canvasSize.vertical], {
|
||||
stroke: 'black',
|
||||
strokeWidth: 1,
|
||||
selectable: false,
|
||||
name: 'mouseLine',
|
||||
})
|
||||
|
||||
// 선들을 캔버스에 추가합니다.
|
||||
canvas?.add(horizontalLine, verticalLine)
|
||||
|
||||
// 캔버스를 다시 그립니다.
|
||||
canvas?.renderAll()
|
||||
}
|
||||
|
||||
//가로, 세로 선 삭제
|
||||
const removeMouseLines = () => {
|
||||
if (canvas?._objects.length > 0) {
|
||||
const mouseLines = canvas?._objects.filter((obj) => obj.name === 'mouseLine')
|
||||
mouseLines.forEach((item) => canvas?.remove(item))
|
||||
}
|
||||
canvas?.renderAll()
|
||||
canvas?.off('selection:cleared')
|
||||
canvas?.off('selection:created')
|
||||
canvas?.off('selection:updated')
|
||||
}
|
||||
|
||||
/**
|
||||
@ -197,6 +181,16 @@ export function useCanvasEvent() {
|
||||
case 'Esc': // IE/Edge에서 사용되는 값
|
||||
case 'Escape':
|
||||
break
|
||||
|
||||
case 'z':
|
||||
if (!e.ctrlKey) {
|
||||
return
|
||||
}
|
||||
|
||||
console.log('뒤로가기')
|
||||
|
||||
break
|
||||
|
||||
default:
|
||||
return // 키 이벤트를 처리하지 않는다면 종료합니다.
|
||||
}
|
||||
@ -216,6 +210,7 @@ export function useCanvasEvent() {
|
||||
}
|
||||
|
||||
targetObj.set({ top: top })
|
||||
targetObj.fire('modified')
|
||||
canvas?.renderAll()
|
||||
}
|
||||
|
||||
@ -232,6 +227,7 @@ export function useCanvasEvent() {
|
||||
}
|
||||
|
||||
targetObj.set({ top: top })
|
||||
targetObj.fire('modified')
|
||||
canvas?.renderAll()
|
||||
}
|
||||
|
||||
@ -248,6 +244,7 @@ export function useCanvasEvent() {
|
||||
}
|
||||
|
||||
targetObj.set({ left: left })
|
||||
targetObj.fire('modified')
|
||||
canvas?.renderAll()
|
||||
}
|
||||
|
||||
@ -264,9 +261,29 @@ export function useCanvasEvent() {
|
||||
}
|
||||
|
||||
targetObj.set({ left: left })
|
||||
targetObj.fire('modified')
|
||||
canvas?.renderAll()
|
||||
}
|
||||
|
||||
/**
|
||||
* 선택한 도형을 삭제한다.
|
||||
*/
|
||||
const handleDelete = () => {
|
||||
const targets = canvas?.getActiveObjects()
|
||||
if (targets?.length === 0) {
|
||||
alert('삭제할 대상을 선택해주세요.')
|
||||
return
|
||||
}
|
||||
|
||||
if (!confirm('정말로 삭제하시겠습니까?')) {
|
||||
return
|
||||
}
|
||||
|
||||
targets?.forEach((target) => {
|
||||
canvas?.remove(target)
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
setCanvasForEvent,
|
||||
attachDefaultEventOnCanvas,
|
||||
|
||||
@ -94,3 +94,9 @@ export const guideLineState = atom({
|
||||
default: {},
|
||||
dangerouslyAllowMutability: true,
|
||||
})
|
||||
|
||||
export const currentObjectState = atom({
|
||||
key: 'currentObject',
|
||||
default: null,
|
||||
dangerouslyAllowMutability: true,
|
||||
})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user