Merge branch 'dev' into dev-yj

This commit is contained in:
yjnoh 2024-09-20 09:20:56 +09:00
commit 357a5ad569
16 changed files with 314 additions and 76 deletions

Binary file not shown.

View File

@ -10,10 +10,10 @@ import { ToastContainer } from 'react-toastify'
import Header from '@/components/header/Header' import Header from '@/components/header/Header'
import QModal from '@/components/common/modal/QModal' import QModal from '@/components/common/modal/QModal'
import { QcastProvider } from './QcastProvider'
import './globals.css' import './globals.css'
import '../styles/style.scss' import '../styles/style.scss'
import { QcastProvider } from './QcastProvider'
const inter = Inter({ subsets: ['latin'] }) const inter = Inter({ subsets: ['latin'] })

View File

@ -42,6 +42,7 @@ import { SurfaceShapeModal } from '@/components/ui/SurfaceShape'
import { changeAllGableRoof, drawDirectionStringToArrow } from '@/util/qpolygon-utils' import { changeAllGableRoof, drawDirectionStringToArrow } from '@/util/qpolygon-utils'
import ThumbnailList from '@/components/ui/ThumbnailLIst' import ThumbnailList from '@/components/ui/ThumbnailLIst'
import ObjectPlacement from '@/components/ui/ObjectPlacement' import ObjectPlacement from '@/components/ui/ObjectPlacement'
import { globalLocaleStore } from '@/store/localeAtom'
export default function Roof2(props) { export default function Roof2(props) {
const { name, userId, email, isLoggedIn } = props const { name, userId, email, isLoggedIn } = props
@ -58,7 +59,9 @@ export default function Roof2(props) {
setBackImg, setBackImg,
} = useCanvas('canvas') } = useCanvas('canvas')
const { get } = useAxios() const globalLocaleState = useRecoilValue(globalLocaleStore)
const { get } = useAxios(globalLocaleState)
const canvasRef = useRef(null) const canvasRef = useRef(null)

View File

@ -11,10 +11,9 @@ import { useRecoilState } from 'recoil'
import { globalLocaleStore } from '@/store/localeAtom' import { globalLocaleStore } from '@/store/localeAtom'
import { modalContent, modalState } from '@/store/modalAtom' import { modalContent, modalState } from '@/store/modalAtom'
export default function Login(props) { export default function Login() {
const { patch } = useAxios() const { patch } = useAxios()
const { currentLocale } = props
const { getMessage } = useMessage() const { getMessage } = useMessage()
const [globalLocaleState, setGlbalLocaleState] = useRecoilState(globalLocaleStore) const [globalLocaleState, setGlbalLocaleState] = useRecoilState(globalLocaleStore)
const [isSelected, setIsSelected] = useState(globalLocaleState === 'ko' ? true : false) const [isSelected, setIsSelected] = useState(globalLocaleState === 'ko' ? true : false)

View File

@ -1,15 +1,15 @@
'use client' 'use client'
import { useState } from 'react' import { useEffect, useState } from 'react'
import CanvasFrame from './CanvasFrame' import CanvasFrame from './CanvasFrame'
import { useRecoilState, useRecoilValue } from 'recoil' import { useRecoilState, useRecoilValue } from 'recoil'
import { currentMenuState, stepState } from '@/store/canvasAtom' import { currentMenuState, stepState } from '@/store/canvasAtom'
export default function CanvasLayout() { export default function CanvasLayout() {
const [plans, setPlans] = useState([ const [plans, setPlans] = useState([
{ id: 0, name: 'Plan 1' }, { id: 0, name: 'Plan 1', isCurrent: false },
{ id: 1, name: 'Plan 2' }, { id: 1, name: 'Plan 2', isCurrent: false },
{ id: 2, name: 'Plan 3' }, { id: 2, name: 'Plan 3', isCurrent: false },
]) ])
const [idxNum, setIdxNum] = useState(null) const [idxNum, setIdxNum] = useState(null)
@ -26,6 +26,12 @@ export default function CanvasLayout() {
setPlans([...plans, { id: plans.length, name: `Plan ${plans.length + 1}` }]) setPlans([...plans, { id: plans.length, name: `Plan ${plans.length + 1}` }])
} }
useEffect(() => {
if (plans.length === 1) {
setPlans([{ id: 0, name: 'Plan 1', isCurrent: false }])
}
}, [])
return ( return (
<div className="canvas-layout"> <div className="canvas-layout">
<div className="canvas-page-list"> <div className="canvas-page-list">

View File

@ -1,19 +1,23 @@
'use client' 'use client'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import MenuDepth01 from './MenuDepth01' import MenuDepth01 from './MenuDepth01'
import QSelectBox from '@/components/common/select/QSelectBox' import QSelectBox from '@/components/common/select/QSelectBox'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { post } from '@/lib/Axios' import { post } from '@/lib/Axios'
import { useRecoilState } from 'recoil' import { useRecoilState, useRecoilValue } from 'recoil'
import { settingModalFirstOptionsState } from '@/store/settingAtom' import { settingModalFirstOptionsState, settingModalSecondOptionsState } from '@/store/settingAtom'
import { canvasZoomState, verticalHorizontalModeState } from '@/store/canvasAtom'
export default function CanvasMenu(props) { export default function CanvasMenu(props) {
const [objectNo] = useState('test123240912001') const [objectNo] = useState('test123240912001') //
const { setShowCanvasSettingModal, showOutlineModal, setShowOutlineModal } = props const { setShowCanvasSettingModal, showOutlineModal, setShowOutlineModal } = props
const [menuNumber, setMenuNumber] = useState(null) const [menuNumber, setMenuNumber] = useState(null)
const [verticalHorizontalMode, setVerticalHorizontalMode] = useRecoilState(verticalHorizontalModeState)
const [vertical, setVertical] = useState(true) const [vertical, setVertical] = useState(true)
const [type, setType] = useState('') const [type, setType] = useState('')
const { getMessage } = useMessage() const { getMessage } = useMessage()
const canvasZoom = useRecoilValue(canvasZoomState)
const SelectOption = [{ name: '瓦53A' }, { name: '瓦53A' }] const SelectOption = [{ name: '瓦53A' }, { name: '瓦53A' }]
const onClickNav = (number) => { const onClickNav = (number) => {
setMenuNumber(number) setMenuNumber(number)
@ -26,7 +30,8 @@ export default function CanvasMenu(props) {
type, type,
} }
const settingsModalOptions = useRecoilState(settingModalFirstOptionsState) const firstOptions = useRecoilState(settingModalFirstOptionsState)
const secondOptions = useRecoilState(settingModalSecondOptionsState)
useEffect(() => {}, [menuNumber, type]) useEffect(() => {}, [menuNumber, type])
@ -35,11 +40,19 @@ export default function CanvasMenu(props) {
try { try {
// //
const dataToSend = { const dataToSend = {
option1: settingsModalOptions[0].option1.map((item) => ({ firstOption1: firstOptions[0].option1.map((item) => ({
column: item.column, column: item.column,
selected: item.selected, selected: item.selected,
})), })),
option2: settingsModalOptions[0].option2.map((item) => ({ firstOption2: firstOptions[0].option2.map((item) => ({
column: item.column,
selected: item.selected,
})),
// secondOption1: secondOptions[0].option1.map((item) => ({
// name: item.name,
// //
// })),
secondOption2: secondOptions[0].option2.map((item) => ({
column: item.column, column: item.column,
selected: item.selected, selected: item.selected,
})), })),
@ -47,20 +60,24 @@ export default function CanvasMenu(props) {
const patternData = { const patternData = {
objectNo, objectNo,
assignDisplay: dataToSend.option1[0].selected, assignDisplay: dataToSend.firstOption1[0].selected,
drawDisplay: dataToSend.option1[1].selected, drawDisplay: dataToSend.firstOption1[1].selected,
gridDisplay: dataToSend.option1[2].selected, gridDisplay: dataToSend.firstOption1[2].selected,
charDisplay: dataToSend.option1[3].selected, charDisplay: dataToSend.firstOption1[3].selected,
flowDisplay: dataToSend.option1[4].selected, flowDisplay: dataToSend.firstOption1[4].selected,
hallwayDimenDisplay: dataToSend.option1[5].selected, hallwayDimenDisplay: dataToSend.firstOption1[5].selected,
actualDimenDisplay: dataToSend.option1[6].selected, actualDimenDisplay: dataToSend.firstOption1[6].selected,
noDimenDisplay: dataToSend.option1[7].selected, noDimenDisplay: dataToSend.firstOption1[7].selected,
trestleDisplay: dataToSend.option1[8].selected, trestleDisplay: dataToSend.firstOption1[8].selected,
coordiDisplay: dataToSend.option1[9].selected, coordiDisplay: dataToSend.firstOption1[9].selected,
drawConverDisplay: dataToSend.option1[10].selected, drawConverDisplay: dataToSend.firstOption1[10].selected,
onlyBorder: dataToSend.option2[0].selected, onlyBorder: dataToSend.firstOption2[0].selected,
lineHatch: dataToSend.option2[1].selected, lineHatch: dataToSend.firstOption2[1].selected,
allPainted: dataToSend.option2[2].selected, allPainted: dataToSend.firstOption2[2].selected,
adsorpRangeSmall: dataToSend.secondOption2[0].selected,
adsorpRangeSmallSemi: dataToSend.secondOption2[1].selected,
adsorpRangeMedium: dataToSend.secondOption2[2].selected,
adsorpRangeLarge: dataToSend.secondOption2[3].selected,
} }
// HTTP POST // HTTP POST
@ -135,9 +152,9 @@ export default function CanvasMenu(props) {
<button className="btn03 "></button> <button className="btn03 "></button>
</div> </div>
{menuNumber !== 4 && ( {menuNumber !== 4 && (
<div className={`vertical-horizontal ${vertical ? 'on' : ''}`}> <div className={`vertical-horizontal ${verticalHorizontalMode ? 'on' : ''}`}>
<span>{getMessage('plan.mode.vertical.horizontal')}</span> <span>{getMessage('plan.mode.vertical.horizontal')}</span>
<button onClick={() => setVertical(!vertical)}>{vertical ? 'ON' : 'OFF'}</button> <button onClick={() => setVerticalHorizontalMode(!verticalHorizontalMode)}>{verticalHorizontalMode ? 'ON' : 'OFF'}</button>
</div> </div>
)} )}
<div className="select-box"> <div className="select-box">
@ -150,7 +167,7 @@ export default function CanvasMenu(props) {
</div> </div>
<div className="size-control"> <div className="size-control">
<button className="control-btn minus"></button> <button className="control-btn minus"></button>
<span>100%</span> <span>{canvasZoom}%</span>
<button className="control-btn plus"></button> <button className="control-btn plus"></button>
</div> </div>
<div className="btn-from"> <div className="btn-from">

View File

@ -6,10 +6,15 @@ import CanvasLayout from '@/components/floor-plan/CanvasLayout'
import '@/styles/contents.scss' import '@/styles/contents.scss'
import OuterLineWall from '@/components/floor-plan/modal/outerlinesetting/OuterLineWall' import OuterLineWall from '@/components/floor-plan/modal/outerlinesetting/OuterLineWall'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { globalLocaleStore } from '@/store/localeAtom'
import { useRecoilValue } from 'recoil'
import { useAxios } from '@/hooks/useAxios'
export default function FloorPlan() { export default function FloorPlan() {
const [showCanvasSettingModal, setShowCanvasSettingModal] = useState(false) const [showCanvasSettingModal, setShowCanvasSettingModal] = useState(false)
const [showOutlineModal, setShowOutlineModal] = useState(false) const [showOutlineModal, setShowOutlineModal] = useState(false)
const globalLocaleState = useRecoilValue(globalLocaleStore)
const { get } = useAxios(globalLocaleState)
const canvasSettingProps = { const canvasSettingProps = {
setShowCanvasSettingModal, setShowCanvasSettingModal,

View File

@ -5,7 +5,7 @@ import WithDraggable from '@/components/common/draggable/withDraggable'
import { useRecoilState, useRecoilValue } from 'recoil' import { useRecoilState, useRecoilValue } from 'recoil'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useEvent } from '@/hooks/useEvent' import { useEvent } from '@/hooks/useEvent'
import { canvasState } from '@/store/canvasAtom' import { canvasState, verticalHorizontalModeState } from '@/store/canvasAtom'
import { import {
OUTER_LINE_TYPE, OUTER_LINE_TYPE,
outerLineArrow1State, outerLineArrow1State,
@ -17,12 +17,15 @@ import {
} from '@/store/outerLineAtom' } from '@/store/outerLineAtom'
import { QLine } from '@/components/fabric/QLine' import { QLine } from '@/components/fabric/QLine'
import { useLine } from '@/hooks/useLine' import { useLine } from '@/hooks/useLine'
import { distanceBetweenPoints } from '@/util/canvas-util'
export default function OuterLineWall(props) { export default function OuterLineWall(props) {
const { setShowOutlineModal } = props const { setShowOutlineModal } = props
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { addCanvasMouseEventListener, addDocumentEventListener, removeAllDocumentEventListeners } = useEvent() const { addCanvasMouseEventListener, addDocumentEventListener, removeAllMouseEventListeners, removeAllDocumentEventListeners } = useEvent()
const { addLineText, removeLineText } = useLine() const { addLineText, removeLineText } = useLine()
const verticalHorizontalMode = useRecoilValue(verticalHorizontalModeState)
const length1Ref = useRef(null) const length1Ref = useRef(null)
const length2Ref = useRef(null) const length2Ref = useRef(null)
const [length1, setLength1] = useRecoilState(outerLineLength1State) const [length1, setLength1] = useRecoilState(outerLineLength1State)
@ -37,8 +40,9 @@ export default function OuterLineWall(props) {
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
useEffect(() => { useEffect(() => {
removeAllMouseEventListeners()
addCanvasMouseEventListener('mouse:down', mouseDown) addCanvasMouseEventListener('mouse:down', mouseDown)
}, []) }, [verticalHorizontalMode, points])
useEffect(() => { useEffect(() => {
arrow1Ref.current = arrow1 arrow1Ref.current = arrow1
@ -64,8 +68,44 @@ export default function OuterLineWall(props) {
const mouseDown = (e) => { const mouseDown = (e) => {
const pointer = canvas.getPointer(e.e) const pointer = canvas.getPointer(e.e)
if (points.length === 0) {
setPoints((prev) => [...prev, pointer])
} else {
const lastPoint = points[points.length - 1]
let newPoint = { x: pointer.x, y: pointer.y }
const length = distanceBetweenPoints(lastPoint, newPoint)
if (verticalHorizontalMode) {
const vector = {
x: pointer.x - points[points.length - 1].x,
y: pointer.y - points[points.length - 1].y,
}
const slope = Math.abs(vector.y / vector.x) //
setPoints((prev) => [...prev, pointer]) let scaledVector
if (slope >= 1) {
// 1 x
scaledVector = {
x: 0,
y: vector.y >= 0 ? Number(length) : -Number(length),
}
} else {
// 1 y
scaledVector = {
x: vector.x >= 0 ? Number(length) : -Number(length),
y: 0,
}
}
const verticalLength = scaledVector.y
const horizontalLength = scaledVector.x
newPoint = {
x: lastPoint.x + horizontalLength,
y: lastPoint.y + verticalLength,
}
}
setPoints((prev) => [...prev, newPoint])
}
} }
useEffect(() => { useEffect(() => {
@ -76,7 +116,18 @@ export default function OuterLineWall(props) {
canvas?.remove(obj) canvas?.remove(obj)
removeLineText(obj) removeLineText(obj)
}) })
canvas
?.getObjects()
.filter((obj) => obj.name === 'helpGuideLine' || (obj.name === 'lengthTxt' && obj.parent?.name === 'helpGuideLine'))
.forEach((obj) => canvas?.remove(obj))
canvas?.remove(canvas?.getObjects().find((obj) => obj.name === 'startPoint')) canvas?.remove(canvas?.getObjects().find((obj) => obj.name === 'startPoint'))
// point
removeAllDocumentEventListeners()
addDocumentEventListener('keydown', document, keydown[type])
if (points.length === 0) { if (points.length === 0) {
return return
} }
@ -100,13 +151,58 @@ export default function OuterLineWall(props) {
} }
drawLine(points[idx - 1], point, idx) drawLine(points[idx - 1], point, idx)
}) })
const lastPoint = points[points.length - 1]
const firstPoint = points[0]
if (points.length < 3) {
return
}
if (lastPoint.x === firstPoint.x && lastPoint.y === firstPoint.y) {
return
}
if (lastPoint.x === firstPoint.x || lastPoint.y === firstPoint.y) {
const line = new QLine([lastPoint.x, lastPoint.y, firstPoint.x, firstPoint.y], {
stroke: 'grey',
strokeWidth: 1,
selectable: false,
name: 'helpGuideLine',
})
canvas?.add(line)
addLineText(line)
} else {
const guideLine1 = new QLine([lastPoint.x, lastPoint.y, lastPoint.x, firstPoint.y], {
stroke: 'grey',
strokeWidth: 1,
strokeDashArray: [1, 1, 1],
name: 'helpGuideLine',
})
const guideLine2 = new QLine([guideLine1.x2, guideLine1.y2, firstPoint.x, firstPoint.y], {
stroke: 'grey',
strokeWidth: 1,
strokeDashArray: [1, 1, 1],
name: 'helpGuideLine',
})
if (guideLine1.length > 0) {
canvas?.add(guideLine1)
addLineText(guideLine1)
}
canvas?.add(guideLine2)
addLineText(guideLine2)
}
} }
}, [points]) }, [points])
const drawLine = (point1, point2, idx) => { const drawLine = (point1, point2, idx) => {
const line = new QLine([point1.x, point1.y, point2.x, point2.y], { const line = new QLine([point1.x, point1.y, point2.x, point2.y], {
stroke: 'black', stroke: 'black',
strokeWidth: 1, strokeWidth: 3,
idx: idx, idx: idx,
selectable: false, selectable: false,
name: 'outerLine', name: 'outerLine',
@ -190,6 +286,9 @@ export default function OuterLineWall(props) {
const keydown = { const keydown = {
outerLine: (e) => { outerLine: (e) => {
if (points.length === 0) {
return
}
const key = e.key const key = e.key
if (!length1Ref.current) { if (!length1Ref.current) {
@ -245,11 +344,12 @@ export default function OuterLineWall(props) {
} }
}, },
rightAngle: (e) => { rightAngle: (e) => {
if (points.length === 0) {
return
}
const key = e.key const key = e.key
const activeElem = document.activeElement const activeElem = document.activeElement
const length1Num = Number(length1Ref.current.value) / 10
const length2Num = Number(length2Ref.current.value) / 10
switch (key) { switch (key) {
case 'Down': // IE/Edge case 'Down': // IE/Edge
@ -339,6 +439,9 @@ export default function OuterLineWall(props) {
} }
const handleFix = () => { const handleFix = () => {
if (points.length < 3) {
return
}
setPoints((prev) => { setPoints((prev) => {
if (prev.length === 0) { if (prev.length === 0) {
return [] return []
@ -449,10 +552,10 @@ export default function OuterLineWall(props) {
<></> <></>
)} )}
<div className="flex-check-box for2 btn"> <div className="flex-check-box for2 btn">
<button className="arr-btn dark" onClick={handleFix}> <button className={`arr-btn dark ${points.length >= 3 ? 'act' : ''}`} onClick={handleFix}>
<span>{getMessage('modal.cover.outline.fix')}</span> <span>{getMessage('modal.cover.outline.fix')}</span>
</button> </button>
<button className="arr-btn dark act" onClick={handleRollback}> <button className={`arr-btn dark ${points.length >= 1 ? 'act' : ''}`} onClick={handleRollback}>
<span>{getMessage('modal.cover.outline.rollback')}</span> <span>{getMessage('modal.cover.outline.rollback')}</span>
</button> </button>
<button className="arr-btn dark"> <button className="arr-btn dark">

View File

@ -5,24 +5,27 @@ import React, { useEffect, useState } from 'react'
import { get } from '@/lib/Axios' import { get } from '@/lib/Axios'
export default function FirstOption() { export default function FirstOption() {
const [objectNo] = useState('test123240912001') const [objectNo] = useState('test123240912001') //
const [settingsModalOptions, setSettingModalOptions] = useRecoilState(settingModalFirstOptionsState) const [settingsModalOptions, setSettingModalOptions] = useRecoilState(settingModalFirstOptionsState)
const { option1, option2 } = settingsModalOptions const { option1, option2 } = settingsModalOptions
const { getMessage } = useMessage() const { getMessage } = useMessage()
// const [isFetched, setIsFetched] = useState(false) //
//
useEffect(() => { useEffect(() => {
console.log('useEffect 실행') console.log('FirstOption useEffect 실행')
fetchSettings() if (!isFetched) {
}, []) // fetchSettings
fetchSettings()
}
}, [isFetched]) // isFetched
// Canvas Setting // Canvas Setting
const fetchSettings = async () => { const fetchSettings = async () => {
try { try {
const res = await get({ url: `/api/canvas-management/canvas-settings/by-object/${objectNo}` }) const res = await get({ url: `/api/canvas-management/canvas-settings/by-object/${objectNo}` })
// console.log(res)
const options1 = [ const options1 = [
'assignDisplay', 'assignDisplay',
'drawDisplay', 'drawDisplay',
@ -45,9 +48,9 @@ export default function FirstOption() {
setSettingModalOptions({ setSettingModalOptions({
option1, option1,
option2, option2,
// rangeSetting: data.rangeSetting,
// gridSettings: data.gridSettings,
}) })
setIsFetched(true) // isFetched true
} catch (error) { } catch (error) {
console.error('Data fetching error:', error) console.error('Data fetching error:', error)
} }

View File

@ -1,23 +1,61 @@
import { useRecoilState } from 'recoil' import { useRecoilState } from 'recoil'
import { settingModalSecondOptionsState } from '@/store/settingAtom' import { settingModalSecondOptionsState } from '@/store/settingAtom'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import React, { useEffect, useState } from 'react'
import { get } from '@/lib/Axios'
export default function SecondOption() { export default function SecondOption() {
const [objectNo] = useState('test123240912001') //
const [settingsModalOptions, setSettingModalOptions] = useRecoilState(settingModalSecondOptionsState) const [settingsModalOptions, setSettingModalOptions] = useRecoilState(settingModalSecondOptionsState)
const { option1, option2 } = settingsModalOptions const { option1, option2 } = settingsModalOptions
const { getMessage } = useMessage() const { getMessage } = useMessage()
const onClickOption = (option) => {
option.selected = !option.selected
setSettingModalOptions({ option1: option1, option2: option2 }) const [isFetched, setIsFetched] = useState(false) //
//
useEffect(() => {
console.log('SecondOption useEffect 실행')
if (!isFetched) {
// fetchSettings
fetchSettings()
}
}, [isFetched]) // isFetched
const fetchSettings = async () => {
try {
const res = await get({ url: `/api/canvas-management/canvas-settings/by-object/${objectNo}` })
//const options1 = ['1', '2', '3', '4']
//const option1 = settingsModalOptions.option1.map((item, index) => ({ ...item, selected: res[options1[index]] }))
const options2 = ['adsorpRangeSmall', 'adsorpRangeSmallSemi', 'adsorpRangeMedium', 'adsorpRangeLarge']
const option2 = settingsModalOptions.option2.map((item, index) => ({ ...item, selected: res[options2[index]] }))
//
setSettingModalOptions({
option1,
option2,
})
setIsFetched(true) // isFetched true
} catch (error) {
console.error('Data fetching error:', error)
}
}
const onClickOption = (option) => {
// option2
const updatedOption2 = option2.map((item) => (item.id === option.id ? { ...item, selected: true } : { ...item, selected: false }))
setSettingModalOptions({ option1, option2: updatedOption2 })
} }
return ( return (
<> <>
<div className="modal-check-btn-wrap"> <div className="modal-check-btn-wrap">
<h3 className="check-wrap-title">{getMessage('modal.canvas.setting.font.plan.edit')}</h3> <h3 className="check-wrap-title">{getMessage('modal.canvas.setting.font.plan.edit')}</h3>
<div className="flex-check-box for2"> <div className="flex-check-box for2">
{settingsModalOptions.option1.map((item) => ( {settingsModalOptions.option1.map((item, index) => (
<button className="arr-btn"> <button key={item.id || index} className="arr-btn">
<span>{getMessage(item.name)}</span> <span>{getMessage(item.name)}</span>
</button> </button>
))} ))}
@ -26,8 +64,8 @@ export default function SecondOption() {
<div className="modal-check-btn-wrap"> <div className="modal-check-btn-wrap">
<h3 className="check-wrap-title">{getMessage('modal.canvas.setting.font.plan.absorption')}</h3> <h3 className="check-wrap-title">{getMessage('modal.canvas.setting.font.plan.absorption')}</h3>
<div className="flex-check-box for-line"> <div className="flex-check-box for-line">
{settingsModalOptions.option2.map((item) => ( {settingsModalOptions.option2.map((item, index) => (
<button key={item.id} className={`check-btn ${item.selected ? 'act' : ''}`} onClick={() => onClickOption(item)}> <button key={item.id || index} className={`check-btn ${item.selected ? 'act' : ''}`} onClick={() => onClickOption(item)}>
<span className="check-area"></span> <span className="check-area"></span>
<span className="title-area">{getMessage(item.name)}</span> <span className="title-area">{getMessage(item.name)}</span>
</button> </button>

View File

@ -5,7 +5,7 @@ const AxiosType = {
EXTERNAL: 'External', EXTERNAL: 'External',
} }
export function useAxios() { export function useAxios(lang = '') {
const getInstances = (url) => { const getInstances = (url) => {
let type = AxiosType.INTERNAL let type = AxiosType.INTERNAL
url.startsWith('http') ? (type = AxiosType.EXTERNAL) : '' url.startsWith('http') ? (type = AxiosType.EXTERNAL) : ''
@ -14,6 +14,7 @@ export function useAxios() {
baseURL: type === AxiosType.INTERNAL ? process.env.NEXT_PUBLIC_API_SERVER_PATH : '', baseURL: type === AxiosType.INTERNAL ? process.env.NEXT_PUBLIC_API_SERVER_PATH : '',
headers: { headers: {
Accept: 'application/json', Accept: 'application/json',
lang,
}, },
}) })
} }

View File

@ -1,6 +1,6 @@
import { useEffect, useRef } from 'react' import { useEffect, useRef } from 'react'
import { useRecoilValue } from 'recoil' import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
import { canvasState, currentMenuState } from '@/store/canvasAtom' import { canvasState, canvasZoomState, currentMenuState } from '@/store/canvasAtom'
import { fabric } from 'fabric' import { fabric } from 'fabric'
export function useEvent() { export function useEvent() {
@ -8,16 +8,12 @@ export function useEvent() {
const currentMenu = useRecoilValue(currentMenuState) const currentMenu = useRecoilValue(currentMenuState)
const keyboardEventListeners = useRef([]) const keyboardEventListeners = useRef([])
const mouseEventListeners = useRef([]) const mouseEventListeners = useRef([])
const setCanvasZoom = useSetRecoilState(canvasZoomState)
useEffect(() => { useEffect(() => {
if (!canvas) { if (!canvas) {
return return
} }
Object.keys(canvas.__eventListeners).forEach((key) => {
if (key.indexOf('mouse') > -1) {
canvas.off(key)
}
})
removeAllMouseEventListeners() removeAllMouseEventListeners()
removeAllDocumentEventListeners() removeAllDocumentEventListeners()
addDefaultEvent() addDefaultEvent()
@ -27,9 +23,30 @@ export function useEvent() {
//default Event 추가 //default Event 추가
addCanvasMouseEventListener('mouse:move', defaultMouseMoveEvent) addCanvasMouseEventListener('mouse:move', defaultMouseMoveEvent)
addCanvasMouseEventListener('mouse:out', defaultMouseOutEvent) addCanvasMouseEventListener('mouse:out', defaultMouseOutEvent)
addCanvasMouseEventListener('mouse:wheel', wheelEvent)
addDocumentEventListener('keydown', document, defaultKeyboardEvent) addDocumentEventListener('keydown', document, defaultKeyboardEvent)
} }
const wheelEvent = (opt) => {
const delta = opt.e.deltaY // 휠 이동 값 (양수면 축소, 음수면 확대)
let zoom = canvas.getZoom() // 현재 줌 값
zoom += delta > 0 ? -0.1 : 0.1
// 줌 값 제한 (최소 0.5배, 최대 5배)
if (zoom > 5) zoom = 5
if (zoom < 0.5) zoom = 0.5
setCanvasZoom(Number((zoom * 100).toFixed(0)))
// 마우스 위치 기준으로 확대/축소
canvas.zoomToPoint({ x: opt.e.offsetX, y: opt.e.offsetY }, zoom)
// 이벤트의 기본 동작 방지 (스크롤 방지)
opt.e.preventDefault()
opt.e.stopPropagation()
}
const defaultMouseOutEvent = (e) => { const defaultMouseOutEvent = (e) => {
removeMouseLine() removeMouseLine()
} }
@ -38,7 +55,7 @@ export function useEvent() {
removeMouseLine() removeMouseLine()
// 가로선 // 가로선
const pointer = canvas.getPointer(e.e) const pointer = canvas.getPointer(e.e)
const horizontalLine = new fabric.Line([0, pointer.y, 2 * canvas.width, pointer.y], { const horizontalLine = new fabric.Line([-1 * canvas.width, pointer.y, 2 * canvas.width, pointer.y], {
stroke: 'red', stroke: 'red',
strokeWidth: 1, strokeWidth: 1,
selectable: false, selectable: false,
@ -46,7 +63,7 @@ export function useEvent() {
}) })
// 세로선 // 세로선
const verticalLine = new fabric.Line([pointer.x, 0, pointer.x, 2 * canvas.height], { const verticalLine = new fabric.Line([pointer.x, -1 * canvas.height, pointer.x, 2 * canvas.height], {
stroke: 'red', stroke: 'red',
strokeWidth: 1, strokeWidth: 1,
selectable: false, selectable: false,
@ -112,6 +129,7 @@ export function useEvent() {
return { return {
addDocumentEventListener, addDocumentEventListener,
addCanvasMouseEventListener, addCanvasMouseEventListener,
removeAllMouseEventListeners,
removeAllDocumentEventListeners, removeAllDocumentEventListeners,
} }
} }

View File

@ -1,22 +1,35 @@
import { useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
import { canvasState, fontSizeState } from '@/store/canvasAtom' import { canvasState, fontFamilyState, fontSizeState } from '@/store/canvasAtom'
export const useLine = () => { export const useLine = () => {
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
const fontSize = useRecoilValue(fontSizeState) const fontSize = useRecoilValue(fontSizeState)
const fontFamily = useRecoilValue(fontFamilyState)
const addLineText = (line) => { const addLine = (points = [], options) => {
const line = new fabric.Line(points, {
...options,
selectable: options.selectable ?? false,
})
canvas?.add(line)
return line
}
const addLineText = (line, length = getLengthByLine(line)) => {
removeLineText(line) removeLineText(line)
const text = new fabric.Text(getLengthByLine(line).toFixed(0), { const text = new fabric.Text(length.toFixed(0), {
left: (line.x2 + line.x1) / 2, left: (line.x2 + line.x1) / 2,
top: (line.y2 + line.y1) / 2, top: (line.y2 + line.y1) / 2,
parent: line, parent: line,
name: 'lengthTxt', name: 'lengthTxt',
fontSize: fontSize, fontSize: fontSize,
fontFamily: fontFamily,
}) })
canvas?.add(text) canvas?.add(text)
return text
} }
const removeLineText = (line) => { const removeLineText = (line) => {
@ -33,7 +46,7 @@ export const useLine = () => {
const dx = x2 - x1 const dx = x2 - x1
const dy = y2 - y1 const dy = y2 - y1
return Number(Math.sqrt(dx * dx + dy * dy).toFixed(0)) * 10 return Number(Math.sqrt(dx * dx + dy * dy).toFixed(1)) * 10
} }
return { return {

View File

@ -1,8 +1,24 @@
import { canvasState } from '@/store/canvasAtom' import { canvasState, fontFamilyState, fontSizeState } from '@/store/canvasAtom'
import { useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
export const usePolygon = () => { export const usePolygon = () => {
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
const fontSize = useRecoilValue(fontSizeState)
const fontFamily = useRecoilValue(fontFamilyState)
return {} const addPolygon = (points, options) => {
const polygon = new fabric.Polygon(points, {
...options,
selectable: options.selectable ?? false,
})
canvas?.add(polygon)
}
const addPolygonByLines = (lines) => {}
return {
addPolygon,
addPolygonByLines,
}
} }

View File

@ -12,6 +12,11 @@ export const textState = atom({
default: 'test text', default: 'test text',
}) })
export const canvasZoomState = atom({
key: 'canvasZoomState',
default: 100,
})
export const modeState = atom({ export const modeState = atom({
key: 'modeState', key: 'modeState',
default: 'default', default: 'default',
@ -22,6 +27,11 @@ export const guideModeLineState = atom({
default: false, default: false,
}) })
export const fontFamilyState = atom({
key: 'fontFamilyState',
default: 'Noto Sans KR',
})
export const fontSizeState = atom({ export const fontSizeState = atom({
key: 'fontSizeState', key: 'fontSizeState',
default: 16, default: 16,
@ -185,3 +195,9 @@ export const currentMenuState = atom({
key: 'currentMenu', key: 'currentMenu',
default: MENU.INITIAL_CANVAS_SETTING, default: MENU.INITIAL_CANVAS_SETTING,
}) })
// 수직 수평 모드
export const verticalHorizontalModeState = atom({
key: 'verticalHorizontalMode',
default: true,
})

View File

@ -35,10 +35,10 @@ export const settingModalSecondOptionsState = atom({
{ id: 4, name: 'modal.canvas.setting.font.plan.edit.circuit.num' }, { id: 4, name: 'modal.canvas.setting.font.plan.edit.circuit.num' },
], ],
option2: [ option2: [
{ id: 1, name: 'modal.canvas.setting.font.plan.absorption.small', selected: false }, { id: 1, column: 'adsorpRangeSmall', name: 'modal.canvas.setting.font.plan.absorption.small', selected: true },
{ id: 2, name: 'modal.canvas.setting.font.plan.absorption.small.semi', selected: false }, { id: 2, column: 'adsorpRangeSmallSemi', name: 'modal.canvas.setting.font.plan.absorption.small.semi', selected: false },
{ id: 3, name: 'modal.canvas.setting.font.plan.absorption.medium', selected: false }, { id: 3, column: 'adsorpRangeMedium', name: 'modal.canvas.setting.font.plan.absorption.medium', selected: false },
{ id: 4, name: 'modal.canvas.setting.font.plan.absorption.large', selected: false }, { id: 4, column: 'adsorpRangeLarge', name: 'modal.canvas.setting.font.plan.absorption.large', selected: false },
], ],
}, },
dangerouslyAllowMutability: true, dangerouslyAllowMutability: true,