471 lines
16 KiB
JavaScript
471 lines
16 KiB
JavaScript
'use client'
|
|
|
|
import { useEffect, useRef } from 'react'
|
|
import WithDraggable from '@/components/common/draggable/withDraggable'
|
|
import { useRecoilState, useRecoilValue } from 'recoil'
|
|
import { useMessage } from '@/hooks/useMessage'
|
|
import { useEvent } from '@/hooks/useEvent'
|
|
import { canvasState } from '@/store/canvasAtom'
|
|
import {
|
|
OUTER_LINE_TYPE,
|
|
outerLineArrow1State,
|
|
outerLineArrow2State,
|
|
outerLineLength1State,
|
|
outerLineLength2State,
|
|
outerLinePointsState,
|
|
outerLineTypeState,
|
|
} from '@/store/outerLineAtom'
|
|
import { QLine } from '@/components/fabric/QLine'
|
|
import { useLine } from '@/hooks/useLine'
|
|
|
|
export default function OuterLineWall(props) {
|
|
const { showOutlineModal, setShowOutlineModal } = props
|
|
const { getMessage } = useMessage()
|
|
const { addCanvasMouseEventListener, addDocumentEventListener, removeAllDocumentEventListeners } = useEvent()
|
|
const { addLineText, removeLineText } = useLine()
|
|
const length1Ref = useRef(null)
|
|
const length2Ref = useRef(null)
|
|
const [length1, setLength1] = useRecoilState(outerLineLength1State)
|
|
const [length2, setLength2] = useRecoilState(outerLineLength2State)
|
|
const [arrow1, setArrow1] = useRecoilState(outerLineArrow1State)
|
|
const [arrow2, setArrow2] = useRecoilState(outerLineArrow2State)
|
|
const [points, setPoints] = useRecoilState(outerLinePointsState)
|
|
const [type, setType] = useRecoilState(outerLineTypeState)
|
|
const arrow1Ref = useRef(arrow1)
|
|
const arrow2Ref = useRef(arrow2)
|
|
|
|
const canvas = useRecoilValue(canvasState)
|
|
|
|
useEffect(() => {
|
|
addCanvasMouseEventListener('mouse:down', mouseDown)
|
|
}, [])
|
|
|
|
useEffect(() => {
|
|
arrow1Ref.current = arrow1
|
|
}, [arrow1])
|
|
|
|
useEffect(() => {
|
|
arrow2Ref.current = arrow2
|
|
}, [arrow2])
|
|
|
|
useEffect(() => {
|
|
removeAllDocumentEventListeners()
|
|
addDocumentEventListener('keydown', document, keydown[type])
|
|
clear()
|
|
}, [type])
|
|
|
|
const clear = () => {
|
|
setLength1(0)
|
|
setLength2(0)
|
|
|
|
setArrow1('')
|
|
setArrow2('')
|
|
}
|
|
|
|
const mouseDown = (e) => {
|
|
const pointer = canvas.getPointer(e.e)
|
|
|
|
setPoints((prev) => [...prev, pointer])
|
|
}
|
|
|
|
useEffect(() => {
|
|
canvas
|
|
?.getObjects()
|
|
.filter((obj) => obj.name === 'outerLine')
|
|
.forEach((obj) => {
|
|
canvas?.remove(obj)
|
|
removeLineText(obj)
|
|
})
|
|
canvas?.remove(canvas?.getObjects().find((obj) => obj.name === 'startPoint'))
|
|
if (points.length === 0) {
|
|
return
|
|
}
|
|
|
|
if (points.length === 1) {
|
|
const point = new fabric.Circle({
|
|
radius: 5,
|
|
fill: 'transparent',
|
|
stroke: 'red',
|
|
left: points[0].x - 5,
|
|
top: points[0].y - 5,
|
|
selectable: false,
|
|
name: 'startPoint',
|
|
})
|
|
|
|
canvas?.add(point)
|
|
} else {
|
|
points.forEach((point, idx) => {
|
|
if (idx === 0) {
|
|
return
|
|
}
|
|
drawLine(points[idx - 1], point, idx)
|
|
})
|
|
}
|
|
}, [points])
|
|
|
|
const drawLine = (point1, point2, idx) => {
|
|
const line = new QLine([point1.x, point1.y, point2.x, point2.y], {
|
|
stroke: 'black',
|
|
strokeWidth: 1,
|
|
idx: idx,
|
|
selectable: false,
|
|
name: 'outerLine',
|
|
})
|
|
|
|
canvas?.add(line)
|
|
addLineText(line)
|
|
}
|
|
|
|
// 직각 완료될 경우 확인
|
|
const checkRightAngle = () => {
|
|
const length1Num = Number(length1Ref.current.value) / 10
|
|
const length2Num = Number(length2Ref.current.value) / 10
|
|
|
|
if (points.length === 0) {
|
|
return
|
|
}
|
|
|
|
if (length1Num === 0 || length2Num === 0 || arrow1Ref.current === '' || arrow2Ref.current === '') {
|
|
return
|
|
}
|
|
|
|
if (arrow1Ref.current === '↓' && arrow2Ref.current === '→') {
|
|
setPoints((prev) => {
|
|
if (prev.length === 0) {
|
|
return []
|
|
}
|
|
return [...prev, { x: prev[prev.length - 1].x + length2Num, y: prev[prev.length - 1].y + length1Num }]
|
|
})
|
|
} else if (arrow1Ref.current === '↓' && arrow2Ref.current === '←') {
|
|
setPoints((prev) => {
|
|
if (prev.length === 0) {
|
|
return []
|
|
}
|
|
return [...prev, { x: prev[prev.length - 1].x - length2Num, y: prev[prev.length - 1].y + length1Num }]
|
|
})
|
|
} else if (arrow1Ref.current === '↑' && arrow2Ref.current === '→') {
|
|
setPoints((prev) => {
|
|
if (prev.length === 0) {
|
|
return []
|
|
}
|
|
return [...prev, { x: prev[prev.length - 1].x + length2Num, y: prev[prev.length - 1].y - length1Num }]
|
|
})
|
|
} else if (arrow1Ref.current === '↑' && arrow2Ref.current === '←') {
|
|
setPoints((prev) => {
|
|
if (prev.length === 0) {
|
|
return []
|
|
}
|
|
return [...prev, { x: prev[prev.length - 1].x - length2Num, y: prev[prev.length - 1].y - length1Num }]
|
|
})
|
|
} else if (arrow1Ref.current === '→' && arrow2Ref.current === '↓') {
|
|
setPoints((prev) => {
|
|
if (prev.length === 0) {
|
|
return []
|
|
}
|
|
return [...prev, { x: prev[prev.length - 1].x + length1Num, y: prev[prev.length - 1].y + length2Num }]
|
|
})
|
|
} else if (arrow1Ref.current === '→' && arrow2Ref.current === '↑') {
|
|
setPoints((prev) => {
|
|
if (prev.length === 0) {
|
|
return []
|
|
}
|
|
return [...prev, { x: prev[prev.length - 1].x + length1Num, y: prev[prev.length - 1].y - length2Num }]
|
|
})
|
|
} else if (arrow1Ref.current === '←' && arrow2Ref.current === '↓') {
|
|
setPoints((prev) => {
|
|
if (prev.length === 0) {
|
|
return []
|
|
}
|
|
return [...prev, { x: prev[prev.length - 1].x - length1Num, y: prev[prev.length - 1].y + length2Num }]
|
|
})
|
|
} else if (arrow1Ref.current === '←' && arrow2Ref.current === '↑') {
|
|
setPoints((prev) => {
|
|
if (prev.length === 0) {
|
|
return []
|
|
}
|
|
return [...prev, { x: prev[prev.length - 1].x - length1Num, y: prev[prev.length - 1].y - length2Num }]
|
|
})
|
|
}
|
|
}
|
|
|
|
const keydown = {
|
|
outerLine: (e) => {
|
|
const key = e.key
|
|
|
|
if (!length1Ref.current) {
|
|
return
|
|
}
|
|
|
|
const lengthNum = Number(length1Ref.current.value) / 10
|
|
if (lengthNum === 0) {
|
|
return
|
|
}
|
|
switch (key) {
|
|
case 'Down': // IE/Edge에서 사용되는 값
|
|
case 'ArrowDown': {
|
|
setArrow1('↓')
|
|
setPoints((prev) => {
|
|
if (prev.length === 0) {
|
|
return []
|
|
}
|
|
return [...prev, { x: prev[prev.length - 1].x, y: prev[prev.length - 1].y + lengthNum }]
|
|
})
|
|
break
|
|
}
|
|
case 'Up': // IE/Edge에서 사용되는 값
|
|
case 'ArrowUp':
|
|
setArrow1('↑')
|
|
setPoints((prev) => {
|
|
if (prev.length === 0) {
|
|
return []
|
|
}
|
|
return [...prev, { x: prev[prev.length - 1].x, y: prev[prev.length - 1].y - lengthNum }]
|
|
})
|
|
break
|
|
case 'Left': // IE/Edge에서 사용되는 값
|
|
case 'ArrowLeft':
|
|
setArrow1('←')
|
|
setPoints((prev) => {
|
|
if (prev.length === 0) {
|
|
return []
|
|
}
|
|
return [...prev, { x: prev[prev.length - 1].x - lengthNum, y: prev[prev.length - 1].y }]
|
|
})
|
|
break
|
|
case 'Right': // IE/Edge에서 사용되는 값
|
|
case 'ArrowRight':
|
|
setArrow1('→')
|
|
setPoints((prev) => {
|
|
if (prev.length === 0) {
|
|
return []
|
|
}
|
|
return [...prev, { x: prev[prev.length - 1].x + lengthNum, y: prev[prev.length - 1].y }]
|
|
})
|
|
break
|
|
}
|
|
},
|
|
rightAngle: (e) => {
|
|
const key = e.key
|
|
|
|
const activeElem = document.activeElement
|
|
const length1Num = Number(length1Ref.current.value) / 10
|
|
const length2Num = Number(length2Ref.current.value) / 10
|
|
|
|
switch (key) {
|
|
case 'Down': // IE/Edge에서 사용되는 값
|
|
case 'ArrowDown': {
|
|
if (activeElem === length1Ref.current) {
|
|
setArrow1('↓')
|
|
arrow1Ref.current = '↓'
|
|
length2Ref.current.focus()
|
|
} else if (activeElem === length2Ref.current) {
|
|
if (arrow1Ref.current === '↓' || arrow1Ref.current === '↑') {
|
|
break
|
|
}
|
|
setArrow2('↓')
|
|
arrow2Ref.current = '↓'
|
|
checkRightAngle()
|
|
}
|
|
|
|
break
|
|
}
|
|
case 'Up': // IE/Edge에서 사용되는 값
|
|
case 'ArrowUp':
|
|
if (activeElem === length1Ref.current) {
|
|
setArrow1('↑')
|
|
arrow1Ref.current = '↑'
|
|
length2Ref.current.focus()
|
|
} else if (activeElem === length2Ref.current) {
|
|
if (arrow1Ref.current === '↓' || arrow1Ref.current === '↑') {
|
|
break
|
|
}
|
|
setArrow2('↑')
|
|
arrow2Ref.current = '↑'
|
|
checkRightAngle()
|
|
}
|
|
|
|
break
|
|
case 'Left': // IE/Edge에서 사용되는 값
|
|
case 'ArrowLeft':
|
|
if (activeElem === length1Ref.current) {
|
|
setArrow1('←')
|
|
arrow1Ref.current = '←'
|
|
length2Ref.current.focus()
|
|
} else if (activeElem === length2Ref.current) {
|
|
if (arrow1Ref.current === '←' || arrow1Ref.current === '→') {
|
|
break
|
|
}
|
|
setArrow2('←')
|
|
arrow2Ref.current = '←'
|
|
checkRightAngle()
|
|
}
|
|
|
|
break
|
|
case 'Right': // IE/Edge에서 사용되는 값
|
|
case 'ArrowRight':
|
|
if (activeElem === length1Ref.current) {
|
|
setArrow1('→')
|
|
arrow1Ref.current = '→'
|
|
length2Ref.current.focus()
|
|
} else if (activeElem === length2Ref.current) {
|
|
if (arrow1Ref.current === '←' || arrow1Ref.current === '→') {
|
|
break
|
|
}
|
|
setArrow2('→')
|
|
arrow2Ref.current = '→'
|
|
checkRightAngle()
|
|
}
|
|
|
|
break
|
|
}
|
|
},
|
|
leeGubae: (e) => {
|
|
console.log('leegubae')
|
|
},
|
|
angle: (e) => {
|
|
console.log('angle')
|
|
},
|
|
diagonalLine: (e) => {
|
|
console.log('diagonalLine')
|
|
},
|
|
}
|
|
|
|
/**
|
|
* 일변전으로 돌아가기
|
|
*/
|
|
const handleRollback = () => {
|
|
//points의 마지막 요소를 제거
|
|
setPoints((prev) => prev.slice(0, prev.length - 1))
|
|
}
|
|
|
|
const handleFix = () => {
|
|
setPoints((prev) => {
|
|
if (prev.length === 0) {
|
|
return []
|
|
}
|
|
return [...prev, { x: prev[0].x, y: prev[0].y }]
|
|
})
|
|
}
|
|
return (
|
|
<WithDraggable isShow={true} pos={{ x: 50, y: -1000 + 50 }}>
|
|
<div className={`modal-pop-wrap ssm ${showOutlineModal ? 'mount' : ''} `}>
|
|
<div className="modal-head">
|
|
<h1 className="title">{getMessage('modal.cover.outline.drawing')}</h1>
|
|
<button className="modal-close" onClick={() => setShowOutlineModal(false)}>
|
|
닫기
|
|
</button>
|
|
</div>
|
|
<div className="modal-body">
|
|
<div className="modal-btn-wrap">
|
|
<button
|
|
className={`btn-frame modal ${type === OUTER_LINE_TYPE.OUTER_LINE ? 'act' : ''}`}
|
|
onClick={() => setType(OUTER_LINE_TYPE.OUTER_LINE)}
|
|
>
|
|
{getMessage('modal.cover.outline')}
|
|
</button>
|
|
|
|
<button
|
|
className={`btn-frame modal ${type === OUTER_LINE_TYPE.RIGHT_ANGLE ? 'act' : ''}`}
|
|
onClick={() => setType(OUTER_LINE_TYPE.RIGHT_ANGLE)}
|
|
>
|
|
{getMessage('modal.cover.outline.right.angle')}
|
|
</button>
|
|
<button
|
|
className={`btn-frame modal ${type === OUTER_LINE_TYPE.LEE_GUBAE ? 'act' : ''}`}
|
|
onClick={() => setType(OUTER_LINE_TYPE.LEE_GUBAE)}
|
|
>
|
|
{getMessage('modal.cover.outline2')}
|
|
</button>
|
|
<button className={`btn-frame modal ${type === OUTER_LINE_TYPE.ANGLE ? 'act' : ''}`} onClick={() => setType(OUTER_LINE_TYPE.ANGLE)}>
|
|
{getMessage('modal.cover.outline.angle')}
|
|
</button>
|
|
<button
|
|
className={`btn-frame modal ${type === OUTER_LINE_TYPE.DIAGONAL_LINE ? 'act' : ''}`}
|
|
onClick={() => setType(OUTER_LINE_TYPE.DIAGONAL_LINE)}
|
|
>
|
|
{getMessage('modal.cover.outline.diagonal')}
|
|
</button>
|
|
</div>
|
|
<div className="modal-check-btn-wrap">
|
|
<h3 className="check-wrap-title">{getMessage('modal.cover.outline.setting')}</h3>
|
|
{type === OUTER_LINE_TYPE.OUTER_LINE ? (
|
|
<div className="outer-line-wrap">
|
|
<div className="form-input">
|
|
<label htmlFor="">{getMessage('modal.cover.outline.length')}</label>
|
|
<input
|
|
type="text"
|
|
className="input-origin block"
|
|
value={length1}
|
|
ref={length1Ref}
|
|
onChange={(e) => {
|
|
setLength1(e.target.value.replace(/[^-0-9]/g, ''))
|
|
}}
|
|
placeholder="3000"
|
|
/>
|
|
</div>
|
|
<div className="form-input">
|
|
<label htmlFor="">{getMessage('modal.cover.outline.arrow')}</label>
|
|
<input type="text" value={arrow1} className="input-origin block" />
|
|
</div>
|
|
</div>
|
|
) : type === OUTER_LINE_TYPE.RIGHT_ANGLE ? (
|
|
<div className="outer-line-wrap">
|
|
<div className="form-input">
|
|
<label htmlFor="">{getMessage('modal.cover.outline.length')}</label>
|
|
<input
|
|
type="text"
|
|
className="input-origin block"
|
|
value={length1}
|
|
ref={length1Ref}
|
|
onChange={(e) => {
|
|
setLength1(e.target.value.replace(/[^-0-9]/g, ''))
|
|
}}
|
|
placeholder="3000"
|
|
/>
|
|
</div>
|
|
<div className="form-input">
|
|
<label htmlFor="">{getMessage('modal.cover.outline.arrow')}</label>
|
|
<input type="text" value={arrow1} className="input-origin block" />
|
|
</div>
|
|
<div className="form-input">
|
|
<label htmlFor="">{getMessage('modal.cover.outline.length')}</label>
|
|
<input
|
|
type="text"
|
|
className="input-origin block"
|
|
value={length2}
|
|
ref={length2Ref}
|
|
onChange={(e) => {
|
|
setLength2(e.target.value.replace(/[^-0-9]/g, ''))
|
|
}}
|
|
placeholder="3000"
|
|
/>
|
|
</div>
|
|
<div className="form-input">
|
|
<label htmlFor="">{getMessage('modal.cover.outline.arrow')}</label>
|
|
<input type="text" value={arrow2} className="input-origin block" />
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<></>
|
|
)}
|
|
<div className="flex-check-box for2 btn">
|
|
<button className="arr-btn dark" onClick={handleFix}>
|
|
<span>{getMessage('modal.cover.outline.fix')}</span>
|
|
</button>
|
|
<button className="arr-btn dark act" onClick={handleRollback}>
|
|
<span>{getMessage('modal.cover.outline.rollback')}</span>
|
|
</button>
|
|
<button className="arr-btn dark">
|
|
<span>{getMessage('modal.cover.outline.remove')}</span>
|
|
</button>
|
|
<button className="arr-btn dark">
|
|
<span>{getMessage('modal.cover.outline.select.move')}</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</WithDraggable>
|
|
)
|
|
}
|