qcast-front/src/components/GridSettingsModal.jsx
2024-09-02 13:52:24 +09:00

273 lines
9.2 KiB
JavaScript

import { useEffect, useRef, useState } from 'react'
import { Button, Checkbox, CheckboxGroup, RadioGroup, Radio, Input } from '@nextui-org/react'
import { useRecoilState, useRecoilValue } from 'recoil'
import { modalContent, modalState } from '@/store/modalAtom'
import { guideLineState, horiGuideLinesState, vertGuideLinesState } from '@/store/canvasAtom'
import { fabric } from 'fabric'
import { ColorPicker, useColor } from 'react-color-palette'
import 'react-color-palette/css'
export default function GridSettingsModal(props) {
const { canvasProps } = props
const [isCustomGridSetting, setIsCustomGridSetting] = useState(true)
const [gridCheckedValue, setGridCheckValue] = useState([])
const [ratioValue, setRatioValue] = useState('1')
const moduleLength = useRef(null) //모듈 mm 길이 입력
const customModuleHoriLength = useRef(null)
const customModuleVertLength = useRef(null)
const [open, setOpen] = useRecoilState(modalState)
const [guideLine, setGuideLine] = useRecoilState(guideLineState)
const [horiGuideLines, setHoriGuideLines] = useRecoilState(horiGuideLinesState)
const [vertGuideLines, setVertGuideLines] = useRecoilState(vertGuideLinesState)
const gridSettingArray = []
const [guideColor, setGuideColor] = useColor('rgb(200, 15, 15)')
const [colorPickerShow, setColorPickerShow] = useState(false)
const boxStyle = {
width: '50px',
height: '30px',
border: '1px solid black',
backgroundColor: guideColor.hex,
}
useEffect(() => {
moduleLength.current.value = 90
customModuleHoriLength.current.value = 90
customModuleVertLength.current.value = 90
}, [])
useEffect(() => {
setIsCustomGridSetting(ratioValue !== 'custom')
}, [ratioValue])
const drawGridSettings = () => {
//기존에 선택된 데이터가 있으면 그 데이터를 포함한다
if (!(Object.keys(guideLine).length === 0 && guideLine.constructor === Object)) {
gridSettingArray.push(...guideLine)
}
let moduleHoriLength = moduleLength.current.value //가로 간격
let moduleVertLength = moduleLength.current.value //새로 간격
if (ratioValue === 'custom') {
moduleHoriLength = customModuleHoriLength.current.value
moduleVertLength = customModuleVertLength.current.value
} else {
moduleHoriLength = moduleHoriLength / ratioValue
moduleVertLength = moduleVertLength / ratioValue
}
if (gridCheckedValue.includes('line')) {
const horizontalLineArray = []
const verticalLineArray = []
for (let i = 0; i < canvasProps.height / moduleVertLength + 1; i++) {
const horizontalLine = new fabric.Line(
[0, i * moduleVertLength - moduleVertLength / 2, canvasProps.width, i * moduleVertLength - moduleVertLength / 2],
{
stroke: guideColor.hex,
strokeWidth: 1,
selectable: true,
lockMovementX: true,
lockMovementY: true,
lockRotation: true,
lockScalingX: true,
lockScalingY: true,
name: 'guideLine',
strokeDashArray: [5, 2],
opacity: 0.3,
direction: 'horizontal',
},
)
canvasProps.add(horizontalLine)
horizontalLineArray.push(horizontalLine)
}
for (let i = 0; i < canvasProps.width / moduleHoriLength + 1; i++) {
const verticalLine = new fabric.Line(
[i * moduleHoriLength - moduleHoriLength / 2, 0, i * moduleHoriLength - moduleHoriLength / 2, canvasProps.height],
{
stroke: guideColor.hex,
strokeWidth: 1,
selectable: true,
lockMovementX: true,
lockMovementY: true,
lockRotation: true,
lockScalingX: true,
lockScalingY: true,
name: 'guideLine',
strokeDashArray: [5, 2],
opacity: 0.3,
direction: 'vertical',
},
)
canvasProps.add(verticalLine)
verticalLineArray.push(verticalLine)
}
canvasProps.renderAll()
const snapDistance = 10
const recoilObj = {
guideMode: 'guideLine',
horizontalLineArray,
verticalLineArray,
moduleVertLength: moduleVertLength,
moduleHoriLength: moduleHoriLength,
}
gridSettingArray.push(recoilObj)
const newHoriGuideLines = [...horiGuideLines]
horizontalLineArray.forEach((line) => {
newHoriGuideLines.push(line)
})
const newVertGuideLines = [...vertGuideLines]
verticalLineArray.forEach((line) => {
newVertGuideLines.push(line)
})
setHoriGuideLines(newHoriGuideLines)
setVertGuideLines(newVertGuideLines)
}
if (gridCheckedValue.includes('dot')) {
const circle = new fabric.Circle({
radius: 2,
fill: 'white',
stroke: guideColor.hex,
strokeWidth: 0.7,
originX: 'center',
originY: 'center',
selectable: false,
lockMovementX: true,
lockMovementY: true,
lockRotation: true,
lockScalingX: true,
lockScalingY: true,
})
const patternSourceCanvas = new fabric.StaticCanvas(null, {
width: moduleHoriLength,
height: moduleVertLength,
})
patternSourceCanvas.add(circle)
circle.set({
left: patternSourceCanvas.width / 2,
top: patternSourceCanvas.height / 2,
})
patternSourceCanvas.renderAll()
const pattern = new fabric.Pattern({
source: patternSourceCanvas.getElement(),
repeat: 'repeat',
})
const backgroundPolygon = new fabric.Polygon(
[
{ x: 0, y: 0 },
{ x: canvasProps.width, y: 0 },
{ x: canvasProps.width, y: canvasProps.height },
{ x: 0, y: canvasProps.height },
],
{
fill: pattern,
selectable: false,
name: 'guideDot',
},
)
canvasProps.add(backgroundPolygon)
backgroundPolygon.sendToBack()
canvasProps.renderAll()
const recoilObj = {
guideMode: 'guideDot',
moduleVertLength: moduleVertLength,
moduleHoriLength: moduleHoriLength,
}
gridSettingArray.push(recoilObj)
}
canvasProps.renderAll()
setGuideLine(gridSettingArray)
}
const removeGuideLines = () => {
if (!(Object.keys(guideLine).length === 0 && guideLine.constructor === Object)) {
const guideLines = canvasProps._objects.filter((obj) => obj.name === 'guideLine' || obj.name === 'guideDot')
guideLines?.forEach((item) => canvasProps.remove(item))
canvasProps.renderAll()
setGuideLine([])
setHoriGuideLines([])
setVertGuideLines([])
} else {
alert('그리드가 없습니다.')
return
}
}
return (
<>
<div>
<div className="flex w-full flex-wrap md:flex-nowrap gap-4">
<label style={{ display: 'block', marginBottom: '5px' }}>
<CheckboxGroup label="그리드 설정" value={gridCheckedValue} defaultChecked={gridCheckedValue} onValueChange={setGridCheckValue}>
<Checkbox value="dot"> 그리드</Checkbox>
<Checkbox value="line">점선 그리드</Checkbox>
</CheckboxGroup>
</label>
</div>
<div className="flex w-full flex-wrap md:flex-nowrap gap-4">
<Input type="number" label="모듈" ref={moduleLength} />
mm
</div>
<div className="flex w-full flex-wrap md:flex-nowrap gap-4">
<RadioGroup label="비율 설정" value={ratioValue} defaultValue={ratioValue} onValueChange={setRatioValue}>
<Radio value="1">원치수</Radio>
<Radio value="2">1/2</Radio>
<Radio value="4">1/4</Radio>
<Radio value="10">1/10</Radio>
<Radio value="custom">임의간격</Radio>
</RadioGroup>
</div>
<div className="flex w-full flex-wrap md:flex-nowrap gap-4">
가이드컬러 <div style={boxStyle} onClick={() => setColorPickerShow(!colorPickerShow)}></div>
</div>
{colorPickerShow && (
<ColorPicker color={guideColor} onChange={setGuideColor} hideInput={['hsv', 'rgb', 'hex']} height={100} hideAlpha={true} />
)}
<div className="flex w-full flex-wrap md:flex-nowrap gap-4">
<Checkbox value="linked" isDisabled={isCustomGridSetting}>
종횡연동
</Checkbox>
</div>
</div>
<div className="flex w-full flex-wrap md:flex-nowrap gap-4">
<Input type="number" label="가로간격" ref={customModuleHoriLength} min={0} isDisabled={isCustomGridSetting} />
mm
</div>
<div className="flex w-full flex-wrap md:flex-nowrap gap-4">
<Input type="number" label="세로간격" ref={customModuleVertLength} min={0} isDisabled={isCustomGridSetting} />
mm
</div>
<div className="flex gap-4 items-center">
<Button size="sm">초기화</Button>
<Button size="sm" color="secondary" onClick={drawGridSettings} isDisabled={gridCheckedValue.length === 0}>
저장
</Button>
<Button size="sm" onClick={() => setOpen(!open)}>
취소
</Button>
<Button size="sm" onClick={() => removeGuideLines()}>
그리드 삭제
</Button>
</div>
</>
)
}