Merge branch 'dev' into feature/design-remake
This commit is contained in:
commit
20eccab581
@ -14,6 +14,7 @@ import { useMessage } from '@/hooks/useMessage'
|
||||
* @param {string} targetKey - value에 있는 키
|
||||
* @param {string} showKey - options 있는 키중 보여줄 키
|
||||
* @param {object} params - 추가 파라미터
|
||||
* @param {boolean} showFirstOptionWhenEmpty - value가 빈값일 때 첫 번째 옵션을 보여줄지 여부
|
||||
* @returns
|
||||
*/
|
||||
export default function QSelectBox({
|
||||
@ -27,6 +28,7 @@ export default function QSelectBox({
|
||||
showKey = '',
|
||||
params = {},
|
||||
tagTitle = '',
|
||||
showFirstOptionWhenEmpty = false,
|
||||
}) {
|
||||
const { getMessage } = useMessage()
|
||||
|
||||
@ -39,7 +41,9 @@ export default function QSelectBox({
|
||||
if (options.length === 0) return title !== '' ? title : getMessage('selectbox.title')
|
||||
if (showKey !== '' && !value) {
|
||||
//value가 없으면 showKey가 있으면 우선 보여준다
|
||||
// return options[0][showKey]
|
||||
if (showFirstOptionWhenEmpty && options.length > 0) {
|
||||
return options[0][showKey]
|
||||
}
|
||||
return title !== '' ? title : getMessage('selectbox.title')
|
||||
} else if (showKey !== '' && value) {
|
||||
//value가 있으면 sourceKey와 targetKey를 비교하여 보여준다
|
||||
@ -48,12 +52,18 @@ export default function QSelectBox({
|
||||
return option[sourceKey] === value[targetKey]
|
||||
})
|
||||
if (!option) {
|
||||
if (showFirstOptionWhenEmpty && options.length > 0) {
|
||||
return options[0][showKey]
|
||||
}
|
||||
return title !== '' ? title : getMessage('selectbox.title')
|
||||
} else {
|
||||
return option[showKey]
|
||||
}
|
||||
} else {
|
||||
//일치하는 조건이 없으면 기본값을 보여준다.
|
||||
if (showFirstOptionWhenEmpty && options.length > 0) {
|
||||
return showKey !== '' ? options[0][showKey] : options[0].name
|
||||
}
|
||||
return title !== '' ? title : getMessage('selectbox.title')
|
||||
}
|
||||
}
|
||||
@ -74,7 +84,7 @@ export default function QSelectBox({
|
||||
useEffect(() => {
|
||||
// value && handleClickSelectOption(value)
|
||||
setSelected(handleInitState())
|
||||
}, [options, value, sourceKey, targetKey, showKey])
|
||||
}, [options, value, sourceKey, targetKey, showKey, showFirstOptionWhenEmpty])
|
||||
|
||||
useOnClickOutside(ref, handleClose)
|
||||
|
||||
|
||||
@ -23,6 +23,7 @@ import { usePopup } from '@/hooks/usePopup'
|
||||
import { useSwal } from '@/hooks/useSwal'
|
||||
import { QcastContext } from '@/app/QcastProvider'
|
||||
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
|
||||
import {normalizeDigits, normalizeDecimal} from '@/util/input-utils'
|
||||
export default function Estimate({}) {
|
||||
const [uniqueData, setUniqueData] = useState([])
|
||||
const [handlePricingFlag, setHandlePricingFlag] = useState(false)
|
||||
@ -645,11 +646,14 @@ export default function Estimate({}) {
|
||||
newValue = parts[0] + '.' + parts[1].substring(0, 2)
|
||||
}
|
||||
|
||||
let pkgAsp = newValue || '0'
|
||||
let pkgAsp = normalizeDecimal(newValue || '0')
|
||||
|
||||
//현재 PKG용량값 가져오기
|
||||
let totVolKw = estimateContextState.totVolKw * 1000
|
||||
let pkgTotPrice = parseFloat(pkgAsp?.replaceAll(',', '')) * totVolKw * 1000
|
||||
// let pkgTotPrice = parseFloat(pkgAsp?.replaceAll(',', '')) * totVolKw * 1000
|
||||
|
||||
const pkgAspNumber = Number(normalizeDecimal(pkgAsp))
|
||||
const pkgTotPrice = pkgAspNumber * totVolKw * 1000
|
||||
|
||||
setEstimateContextState({
|
||||
pkgAsp: pkgAsp,
|
||||
@ -663,7 +667,7 @@ export default function Estimate({}) {
|
||||
// 수량 변경
|
||||
const onChangeAmount = (value, dispOrder, index) => {
|
||||
//itemChangeFlg = 1, partAdd = 0 셋팅
|
||||
let amount = Number(value.replace(/[^0-9]/g, '').replaceAll(',', ''))
|
||||
let amount = Number(normalizeDigits(value))
|
||||
|
||||
if (isNaN(amount)) {
|
||||
amount = '0'
|
||||
@ -701,7 +705,8 @@ export default function Estimate({}) {
|
||||
// 단가 변경
|
||||
const onChangeSalePrice = (value, dispOrder, index) => {
|
||||
//itemChangeFlg, partAdd 받아온 그대로
|
||||
let salePrice = Number(value.replace(/[^0-9]/g, '').replaceAll(',', ''))
|
||||
let salePrice = Number(normalizeDecimal(value))
|
||||
|
||||
if (isNaN(salePrice)) {
|
||||
salePrice = 0
|
||||
} else {
|
||||
@ -940,7 +945,7 @@ export default function Estimate({}) {
|
||||
delete item.showSalePrice
|
||||
delete item.showSaleTotPrice
|
||||
if (item.delFlg === '0') {
|
||||
let amount = Number(item.amount?.replace(/[^0-9]/g, '').replaceAll(',', '')) || 0
|
||||
let amount = Number(normalizeDigits(item.amount)) || 0
|
||||
let price
|
||||
if (amount === 0) {
|
||||
price = 0
|
||||
@ -975,7 +980,7 @@ export default function Estimate({}) {
|
||||
makeUniqueSpecialNoteCd(itemList)
|
||||
itemList.forEach((item) => {
|
||||
if (item.delFlg === '0') {
|
||||
let amount = Number(item.amount?.replace(/[^0-9]/g, '').replaceAll(',', '')) || 0
|
||||
let amount = Number(normalizeDigits(item.amount)) || 0
|
||||
let salePrice
|
||||
if (item.moduleFlg === '1') {
|
||||
const volKw = (item.pnowW * amount) / 1000
|
||||
@ -1009,8 +1014,8 @@ export default function Estimate({}) {
|
||||
}
|
||||
}
|
||||
})
|
||||
let pkgAsp = estimateContextState.pkgAsp ? Number(estimateContextState.pkgAsp.replaceAll(',', '')) : 0
|
||||
|
||||
//let pkgAsp = estimateContextState.pkgAsp ? Number(estimateContextState.pkgAsp.replaceAll(',', '')) : 0
|
||||
const pkgAsp = Number(normalizeDecimal(estimateContextState.pkgAsp))
|
||||
totals.pkgTotPrice = pkgAsp * totals.totVolKw * 1000
|
||||
totals.supplyPrice = totals.addSupplyPrice + totals.pkgTotPrice
|
||||
totals.vatPrice = totals.supplyPrice * 0.1
|
||||
@ -1069,7 +1074,7 @@ export default function Estimate({}) {
|
||||
let dispCableFlgCnt = 0
|
||||
estimateContextState.itemList.forEach((item) => {
|
||||
if (item.delFlg === '0') {
|
||||
let amount = Number(item.amount?.replace(/[^0-9]/g, '').replaceAll(',', '')) || 0
|
||||
let amount = Number(normalizeDigits(item.amount)) || 0
|
||||
let salePrice
|
||||
if (item.moduleFlg === '1') {
|
||||
const volKw = (item.pnowW * amount) / 1000
|
||||
@ -1102,7 +1107,7 @@ export default function Estimate({}) {
|
||||
item.showSaleTotPrice = '0'
|
||||
}
|
||||
|
||||
if (item.dispCableFlg === '1' ) {
|
||||
if (item.dispCableFlg === '1') {
|
||||
dispCableFlgCnt++
|
||||
if(item.itemTpCd === 'M12' || item.itemTpCd === 'S13') {
|
||||
setCableDbItem(item.itemId)
|
||||
@ -1118,9 +1123,10 @@ export default function Estimate({}) {
|
||||
setCableDbItem('100037')
|
||||
}
|
||||
|
||||
let pkgAsp = estimateContextState.pkgAsp ? Number(estimateContextState.pkgAsp.replaceAll(',', '')) : 0
|
||||
|
||||
// let pkgAsp = estimateContextState.pkgAsp ? Number(estimateContextState.pkgAsp.replaceAll(',', '')) : 0
|
||||
const pkgAsp = Number(normalizeDecimal(estimateContextState.pkgAsp))
|
||||
totals.pkgTotPrice = pkgAsp * totals.totVolKw * 1000
|
||||
|
||||
totals.supplyPrice = totals.addSupplyPrice + totals.pkgTotPrice
|
||||
totals.vatPrice = totals.supplyPrice * 0.1
|
||||
totals.totPrice = totals.supplyPrice + totals.vatPrice
|
||||
@ -1150,7 +1156,7 @@ export default function Estimate({}) {
|
||||
delete item.showSalePrice
|
||||
delete item.showSaleTotPrice
|
||||
if (item.delFlg === '0') {
|
||||
let amount = Number(item.amount?.replace(/[^0-9]/g, '').replaceAll(',', '')) || 0
|
||||
let amount = Number(normalizeDigits(item.amount)) || 0
|
||||
let price
|
||||
if (amount === 0) {
|
||||
price = 0
|
||||
@ -1176,10 +1182,6 @@ export default function Estimate({}) {
|
||||
|
||||
if (item.dispCableFlg === '1') {
|
||||
dispCableFlgCnt++
|
||||
}
|
||||
|
||||
if (item.dispCableFlg === '1'){
|
||||
|
||||
if(item.itemTpCd === 'M12' || item.itemTpCd === 'S13') {
|
||||
setCableDbItem(item.itemId)
|
||||
}else{
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { fabric } from 'fabric'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { getDirectionByPoint } from '@/util/canvas-util'
|
||||
import { calcLinePlaneSize } from '@/util/qpolygon-utils'
|
||||
|
||||
export const QLine = fabric.util.createClass(fabric.Line, {
|
||||
type: 'QLine',
|
||||
@ -17,7 +18,7 @@ export const QLine = fabric.util.createClass(fabric.Line, {
|
||||
initialize: function (points, options, length = 0) {
|
||||
// 소수점 전부 제거
|
||||
|
||||
points = points.map((point) => Number(point?.toFixed(1)))
|
||||
points = points.map((point) => Number(Number(point)?.toFixed(1)))
|
||||
|
||||
this.callSuper('initialize', points, { ...options, selectable: options.selectable ?? true })
|
||||
if (options.id) {
|
||||
@ -31,14 +32,16 @@ export const QLine = fabric.util.createClass(fabric.Line, {
|
||||
this.direction = options.direction ?? getDirectionByPoint({ x: this.x1, y: this.y1 }, { x: this.x2, y: this.y2 })
|
||||
this.textMode = options.textMode ?? 'plane' // plane:복시도, actual:실측, none:표시안함
|
||||
this.textVisible = options.textVisible ?? true
|
||||
if (length !== 0) {
|
||||
this.length = length
|
||||
} else {
|
||||
this.setLength()
|
||||
}
|
||||
|
||||
this.startPoint = { x: this.x1, y: this.y1 }
|
||||
this.endPoint = { x: this.x2, y: this.y2 }
|
||||
try {
|
||||
this.setLength()
|
||||
} catch (e) {
|
||||
setTimeout(() => {
|
||||
this.setLength()
|
||||
}, 100)
|
||||
}
|
||||
},
|
||||
|
||||
init: function () {
|
||||
@ -66,23 +69,7 @@ export const QLine = fabric.util.createClass(fabric.Line, {
|
||||
},
|
||||
|
||||
setLength() {
|
||||
if (this.attributes?.actualSize !== undefined && this.attributes?.planeSize !== undefined) {
|
||||
if (this.textMode === 'plane') {
|
||||
this.length = this.attributes.planeSize / 10
|
||||
} else if (this.textMode === 'actual') {
|
||||
this.length = this.attributes.actualSize / 10
|
||||
}
|
||||
} else {
|
||||
const scaleX = this.scaleX
|
||||
const scaleY = this.scaleY
|
||||
const x1 = this.left
|
||||
const y1 = this.top
|
||||
const x2 = this.left + this.width * scaleX
|
||||
const y2 = this.top + this.height * scaleY
|
||||
const dx = x2 - x1
|
||||
const dy = y2 - y1
|
||||
this.length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(1))
|
||||
}
|
||||
this.length = calcLinePlaneSize(this) / 10
|
||||
},
|
||||
|
||||
addLengthText() {
|
||||
|
||||
@ -7,6 +7,7 @@ import { useState } from 'react'
|
||||
import { currentObjectState } from '@/store/canvasAtom'
|
||||
import { useAuxiliaryDrawing } from '@/hooks/roofcover/useAuxiliaryDrawing'
|
||||
import { useSwal } from '@/hooks/useSwal'
|
||||
import { normalizeDigits } from '@/util/input-utils'
|
||||
|
||||
export default function AuxiliaryEdit(props) {
|
||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||
@ -40,15 +41,15 @@ export default function AuxiliaryEdit(props) {
|
||||
if (currentObject) {
|
||||
copy(
|
||||
currentObject,
|
||||
arrow2 ? (arrow2 === '←' ? Number(+horizonSize / 10) * -1 : Number(+horizonSize / 10)) : 0,
|
||||
arrow1 ? (arrow1 === '↑' ? Number(+verticalSize / 10) * -1 : Number(+verticalSize / 10)) : 0,
|
||||
arrow2 ? (arrow2 === '←' ? (Number(normalizeDigits(horizonSize)) / 10) * -1 : Number(normalizeDigits(horizonSize)) / 10) : 0,
|
||||
arrow1 ? (arrow1 === '↑' ? (Number(normalizeDigits(verticalSize)) / 10) * -1 : Number(normalizeDigits(verticalSize)) / 10) : 0,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
move(
|
||||
currentObject,
|
||||
arrow2 ? (arrow2 === '←' ? Number(+horizonSize / 10) * -1 : Number(+horizonSize / 10)) : 0,
|
||||
arrow1 ? (arrow1 === '↑' ? Number(+verticalSize / 10) * -1 : Number(+verticalSize / 10)) : 0,
|
||||
arrow2 ? (arrow2 === '←' ? (Number(normalizeDigits(horizonSize)) / 10) * -1 : Number(normalizeDigits(horizonSize)) / 10) : 0,
|
||||
arrow1 ? (arrow1 === '↑' ? (Number(normalizeDigits(verticalSize)) / 10) * -1 : Number(normalizeDigits(verticalSize)) / 10) : 0,
|
||||
)
|
||||
}
|
||||
|
||||
@ -65,7 +66,7 @@ export default function AuxiliaryEdit(props) {
|
||||
<p className="mb5">{getMessage('length')}</p>
|
||||
<div className="input-move-wrap mb5">
|
||||
<div className="input-move">
|
||||
<input type="text" className="input-origin" value={verticalSize} onChange={(e) => setVerticalSize(e.target.value)} />
|
||||
<input type="text" className="input-origin" value={verticalSize} onChange={(e) => setVerticalSize(normalizeDigits(e.target.value))} />
|
||||
</div>
|
||||
<span>mm</span>
|
||||
<div className="direction-move-wrap">
|
||||
@ -87,7 +88,7 @@ export default function AuxiliaryEdit(props) {
|
||||
</div>
|
||||
<div className="input-move-wrap">
|
||||
<div className="input-move">
|
||||
<input type="text" className="input-origin" value={horizonSize} onChange={(e) => setHorizonSize(e.target.value)} />
|
||||
<input type="text" className="input-origin" value={horizonSize} onChange={(e) => setHorizonSize(normalizeDigits(e.target.value))} />
|
||||
</div>
|
||||
<span>mm</span>
|
||||
<div className="direction-move-wrap">
|
||||
|
||||
@ -7,6 +7,7 @@ import { canvasState, currentObjectState } from '@/store/canvasAtom'
|
||||
import { useEffect, useState } from 'react'
|
||||
import Big from 'big.js'
|
||||
import { calcLineActualSize, calcLinePlaneSize } from '@/util/qpolygon-utils'
|
||||
import { normalizeDigits } from '@/util/input-utils'
|
||||
|
||||
export default function AuxiliarySize(props) {
|
||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||
@ -42,7 +43,8 @@ export default function AuxiliarySize(props) {
|
||||
if (checkedRadio === 2) setValue2(value)
|
||||
setSize(0)
|
||||
} else {
|
||||
value = Big(value.replace(/[^0-9]/g, ''))
|
||||
//value = Big(value.replace(/[^0-9]/g, ''))
|
||||
value = Big(normalizeDigits(value))
|
||||
if (checkedRadio === 1) setValue1(value.toNumber())
|
||||
if (checkedRadio === 2) setValue2(value.toNumber())
|
||||
setSize(value.div(10).toNumber())
|
||||
|
||||
@ -10,6 +10,7 @@ import { useDebounceValue } from 'usehooks-ts'
|
||||
import { moduleSelectionDataState } from '@/store/selectedModuleOptions'
|
||||
import { useCanvasPopupStatusController } from '@/hooks/common/useCanvasPopupStatusController'
|
||||
import { isObjectNotEmpty } from '@/util/common-utils'
|
||||
import { normalizeDecimal} from '@/util/input-utils'
|
||||
|
||||
export default function Module({ setTabNum }) {
|
||||
const { getMessage } = useMessage()
|
||||
@ -188,7 +189,7 @@ export default function Module({ setTabNum }) {
|
||||
type="text"
|
||||
className="input-origin block"
|
||||
value={inputInstallHeight}
|
||||
onChange={(e) => setInputInstallHeight(e.target.value)}
|
||||
onChange={(e) => setInputInstallHeight(normalizeDecimal(e.target.value))}
|
||||
/>
|
||||
</div>
|
||||
<span className="thin">m</span>
|
||||
@ -225,7 +226,7 @@ export default function Module({ setTabNum }) {
|
||||
type="text"
|
||||
className="input-origin block"
|
||||
value={inputVerticalSnowCover}
|
||||
onChange={(e) => setInputVerticalSnowCover(e.target.value)}
|
||||
onChange={(e) => setInputVerticalSnowCover(normalizeDecimal(e.target.value))}
|
||||
/>
|
||||
</div>
|
||||
<span className="thin">cm</span>
|
||||
|
||||
@ -8,6 +8,7 @@ import QSelectBox from '@/components/common/select/QSelectBox'
|
||||
import { roofsState } from '@/store/roofAtom'
|
||||
import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting'
|
||||
import Swal from 'sweetalert2'
|
||||
import { normalizeDecimal} from '@/util/input-utils'
|
||||
|
||||
export const Orientation = forwardRef((props, ref) => {
|
||||
const { getMessage } = useMessage()
|
||||
@ -177,9 +178,10 @@ export const Orientation = forwardRef((props, ref) => {
|
||||
setInputCompasDeg('-0')
|
||||
return
|
||||
}
|
||||
if (Number(e) >= -180 && Number(e) <= 180) {
|
||||
if (numberCheck(Number(e))) {
|
||||
setInputCompasDeg(Number(e))
|
||||
const n = Number(normalizeDecimal(e))
|
||||
if (n >= -180 && n <= 180) {
|
||||
if (numberCheck(n)) {
|
||||
setInputCompasDeg(n)
|
||||
}
|
||||
} else {
|
||||
setInputCompasDeg(compasDeg)
|
||||
@ -398,7 +400,7 @@ export const Orientation = forwardRef((props, ref) => {
|
||||
<div className="outline-form mt15">
|
||||
<span>{getMessage('modal.module.basic.setting.module.placement.area')}</span>
|
||||
<div className="input-grid mr10" style={{ width: '60px' }}>
|
||||
<input type="number" className="input-origin block" value={inputMargin} onChange={(e) => setInputMargin(e.target.value)} />
|
||||
<input type="text" className="input-origin block" value={inputMargin} onChange={(e) => setInputMargin(normalizeDecimal(e.target.value))} />
|
||||
</div>
|
||||
<span className="thin">m</span>
|
||||
</div>
|
||||
@ -427,10 +429,10 @@ export const Orientation = forwardRef((props, ref) => {
|
||||
<span>{getMessage('modal.module.basic.setting.module.fitting.height')}</span>
|
||||
<div className="input-grid mr10">
|
||||
<input
|
||||
type="number"
|
||||
type="text"
|
||||
className="input-origin block"
|
||||
value={inputInstallHeight}
|
||||
onChange={(e) => handleChangeInstallHeight(e.target.value)}
|
||||
onChange={(e) => handleChangeInstallHeight(normalizeDecimal(e.target.value))}
|
||||
/>
|
||||
</div>
|
||||
<span className="thin">m</span>
|
||||
@ -455,10 +457,10 @@ export const Orientation = forwardRef((props, ref) => {
|
||||
<span>{getMessage('modal.module.basic.setting.module.standard.snowfall.amount')}</span>
|
||||
<div className="input-grid mr10">
|
||||
<input
|
||||
type="number"
|
||||
type="text"
|
||||
className="input-origin block"
|
||||
value={inputVerticalSnowCover}
|
||||
onChange={(e) => handleChangeVerticalSnowCover(e.target.value)}
|
||||
onChange={(e) => handleChangeVerticalSnowCover(normalizeDecimal(e.target.value))}
|
||||
/>
|
||||
</div>
|
||||
<span className="thin">cm</span>
|
||||
|
||||
@ -12,6 +12,7 @@ import {
|
||||
import { useRecoilState, useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil'
|
||||
import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions'
|
||||
import { isObjectNotEmpty } from '@/util/common-utils'
|
||||
import { normalizeDigits } from '@/util/input-utils'
|
||||
import Image from 'next/image'
|
||||
|
||||
const Placement = forwardRef((props, refs) => {
|
||||
@ -155,7 +156,7 @@ const Placement = forwardRef((props, refs) => {
|
||||
newLayoutSetup[index] = {
|
||||
...newLayoutSetup[index],
|
||||
moduleId: itemId,
|
||||
[e.target.name]: Number(e.target.value),
|
||||
[e.target.name]: Number(normalizeDigits(e.target.value)),
|
||||
}
|
||||
props.setLayoutSetup(newLayoutSetup)
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedM
|
||||
import { forwardRef, useContext, useEffect, useImperativeHandle, useRef, useState } from 'react'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
import Swal from 'sweetalert2'
|
||||
import { normalizeDigits } from '@/util/input-utils'
|
||||
|
||||
const Trestle = forwardRef((props, ref) => {
|
||||
const { tabNum, setTabNum, trestleTrigger, roofs, setRoofs, moduleSelectionData, setModuleSelectionData, setRoofsStore } = props
|
||||
@ -58,12 +59,21 @@ const Trestle = forwardRef((props, ref) => {
|
||||
const { restoreModuleInstArea } = useModuleBasicSetting()
|
||||
const [flag, setFlag] = useState(false)
|
||||
const tempModuleSelectionData = useRef(null)
|
||||
const [autoSelectStep, setAutoSelectStep] = useState(null) // 'raftBase', 'trestle', 'constMthd', 'roofBase', 'construction'
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (roofs && !selectedRoof) {
|
||||
console.log("roofs:::::", roofs.length)
|
||||
setLengthBase(roofs[0].length);
|
||||
setSelectedRoof(roofs[0])
|
||||
}
|
||||
|
||||
if (selectedRoof && selectedRoof.lenAuth === "C") {
|
||||
onChangeLength(selectedRoof.length);
|
||||
}
|
||||
if (selectedRoof && ["C", "R"].includes(selectedRoof.raftAuth)) {
|
||||
onChangeRaftBase(roofs[0]);
|
||||
}
|
||||
//모듈 설치 영역 복구
|
||||
restoreModuleInstArea()
|
||||
}, [roofs])
|
||||
@ -100,41 +110,86 @@ const Trestle = forwardRef((props, ref) => {
|
||||
|
||||
useEffect(() => {
|
||||
if (trestleList.length > 0) {
|
||||
setSelectedTrestle(trestleList.find((trestle) => trestle.trestleMkrCd === trestleState?.trestleMkrCd) ?? null)
|
||||
const existingTrestle = trestleList.find(
|
||||
(trestle) => trestle.trestleMkrCd === trestleState?.trestleMkrCd
|
||||
);
|
||||
if (existingTrestle) {
|
||||
setSelectedTrestle(existingTrestle)
|
||||
} else if (autoSelectStep === 'trestle') {
|
||||
// 자동 선택: 첫 번째 가대메이커 선택
|
||||
console.log('Auto selecting first trestle:', trestleList[0])
|
||||
const firstTrestle = trestleList[0]
|
||||
onChangeTrestleMaker(firstTrestle)
|
||||
// setAutoSelectStep은 onChangeTrestleMaker 내부에서 처리됨
|
||||
}
|
||||
} else {
|
||||
setSelectedTrestle(null)
|
||||
}
|
||||
}, [trestleList])
|
||||
|
||||
useEffect(() => {
|
||||
if (roofBaseList.length > 0) {
|
||||
setSelectedRoofBase(roofBaseList.find((roofBase) => roofBase.roofBaseCd === trestleState?.roofBaseCd) ?? null)
|
||||
} else {
|
||||
setSelectedRoofBase(null)
|
||||
}
|
||||
}, [roofBaseList])
|
||||
}, [trestleList, autoSelectStep])
|
||||
|
||||
useEffect(() => {
|
||||
if (constMthdList.length > 0) {
|
||||
setSelectedConstMthd(constMthdList.find((constMthd) => constMthd.constMthdCd === trestleState?.constMthdCd) ?? null)
|
||||
const existingConstMthd = constMthdList.find((constMthd) => constMthd.constMthdCd === trestleState?.constMthdCd)
|
||||
if (existingConstMthd) {
|
||||
setSelectedConstMthd(existingConstMthd)
|
||||
} else if (autoSelectStep === 'constMthd') {
|
||||
// 자동 선택: 첫 번째 공법 선택
|
||||
const firstConstMthd = constMthdList[0]
|
||||
onChangeConstMthd(firstConstMthd)
|
||||
setAutoSelectStep('roofBase') // 다음 단계로 설정
|
||||
}
|
||||
} else {
|
||||
setSelectedConstMthd(null)
|
||||
}
|
||||
}, [constMthdList])
|
||||
}, [constMthdList, autoSelectStep])
|
||||
|
||||
useEffect(() => {
|
||||
if (roofBaseList.length > 0) {
|
||||
const existingRoofBase = roofBaseList.find((roofBase) => roofBase.roofBaseCd === trestleState?.roofBaseCd)
|
||||
if (existingRoofBase) {
|
||||
setSelectedRoofBase(existingRoofBase)
|
||||
} else if (autoSelectStep === 'roofBase') {
|
||||
// 자동 선택: 첫 번째 지붕밑바탕 선택
|
||||
const firstRoofBase = roofBaseList[0]
|
||||
onChangeRoofBase(firstRoofBase)
|
||||
setAutoSelectStep('construction') // 다음 단계로 설정
|
||||
}
|
||||
} else {
|
||||
setSelectedRoofBase(null)
|
||||
}
|
||||
}, [roofBaseList, autoSelectStep])
|
||||
|
||||
useEffect(() => {
|
||||
if (constructionList.length > 0) {
|
||||
setSelectedConstruction(constructionList.find((construction) => construction.constTp === trestleState?.construction?.constTp) ?? null)
|
||||
const existingConstruction = constructionList.find((construction) => construction.constTp === trestleState?.construction?.constTp)
|
||||
if (existingConstruction) {
|
||||
setSelectedConstruction(existingConstruction)
|
||||
} else if (autoSelectStep === 'construction') {
|
||||
// 자동 선택: 첫 번째 가능한 construction 선택
|
||||
const availableConstructions = constructionList.filter((construction) => construction.constPossYn === 'Y')
|
||||
if (availableConstructions.length > 0) {
|
||||
const firstConstruction = availableConstructions[0]
|
||||
const firstIndex = constructionList.findIndex((construction) => construction.constTp === firstConstruction.constTp)
|
||||
handleConstruction(firstIndex)
|
||||
setAutoSelectStep(null) // 자동 선택 완료
|
||||
} else {
|
||||
Swal.fire({
|
||||
title: getMessage('modal.module.basic.settting.module.error4', [selectedRoof?.nameJp]),
|
||||
icon: 'warning',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (constructionList.filter((construction) => construction.constPossYn === 'Y').length === 0) {
|
||||
Swal.fire({
|
||||
title: getMessage('modal.module.basic.settting.module.error4', [selectedRoof?.nameJp]), // 시공법법을 선택해주세요.
|
||||
title: getMessage('modal.module.basic.settting.module.error4', [selectedRoof?.nameJp]),
|
||||
icon: 'warning',
|
||||
})
|
||||
}
|
||||
} else {
|
||||
setSelectedConstruction(null)
|
||||
}
|
||||
}, [constructionList])
|
||||
}, [constructionList, autoSelectStep])
|
||||
|
||||
const getConstructionState = (index) => {
|
||||
if (constructionList && constructionList.length > 0) {
|
||||
@ -151,6 +206,13 @@ const Trestle = forwardRef((props, ref) => {
|
||||
|
||||
const onChangeLength = (e) => {
|
||||
setLengthBase(e)
|
||||
// 다음 단계들 초기화
|
||||
setSelectedRaftBase(null)
|
||||
setSelectedTrestle(null)
|
||||
setSelectedConstMthd(null)
|
||||
setSelectedRoofBase(null)
|
||||
setSelectedConstruction(null)
|
||||
|
||||
dispatch({
|
||||
type: 'SET_LENGTH',
|
||||
roof: {
|
||||
@ -160,10 +222,24 @@ const Trestle = forwardRef((props, ref) => {
|
||||
raft: selectedRaftBase?.clCode,
|
||||
},
|
||||
})
|
||||
|
||||
// 자동으로 첫 번째 서까래 간격 선택
|
||||
if (raftBaseList.length > 0) {
|
||||
|
||||
const inx = raftBaseList.findIndex((raft) => raft.clCode === selectedRoof?.raft) ?? 0
|
||||
const firstRaftBase = raftBaseList[inx]
|
||||
onChangeRaftBase(firstRaftBase)
|
||||
}
|
||||
}
|
||||
|
||||
const onChangeRaftBase = (e) => {
|
||||
setSelectedRaftBase(e)
|
||||
// 다음 단계들 초기화
|
||||
setSelectedTrestle(null)
|
||||
setSelectedConstMthd(null)
|
||||
setSelectedRoofBase(null)
|
||||
setSelectedConstruction(null)
|
||||
|
||||
dispatch({
|
||||
type: 'SET_RAFT_BASE',
|
||||
roof: {
|
||||
@ -172,10 +248,20 @@ const Trestle = forwardRef((props, ref) => {
|
||||
raft: e.clCode,
|
||||
},
|
||||
})
|
||||
|
||||
// 다음 단계(가대메이커) 자동 선택 설정 - 지연 실행
|
||||
setTimeout(() => {
|
||||
setAutoSelectStep('trestle')
|
||||
}, 500) // API 호출 완료를 위한 더 긴 지연
|
||||
}
|
||||
|
||||
const onChangeHajebichi = (e) => {
|
||||
setHajebichi(e)
|
||||
// 다음 단계들 초기화
|
||||
setSelectedTrestle(null)
|
||||
setSelectedConstMthd(null)
|
||||
setSelectedRoofBase(null)
|
||||
setSelectedConstruction(null)
|
||||
|
||||
// roofs 배열에서 selectedRoof.index와 같은 인덱스의 지붕 객체 업데이트
|
||||
if (selectedRoof && selectedRoof.index !== undefined) {
|
||||
@ -192,10 +278,20 @@ const Trestle = forwardRef((props, ref) => {
|
||||
hajebichi: e,
|
||||
},
|
||||
})
|
||||
|
||||
// 다음 단계(가대메이커) 자동 선택 설정 - 지연 실행
|
||||
setTimeout(() => {
|
||||
setAutoSelectStep('trestle')
|
||||
}, 500)
|
||||
}
|
||||
|
||||
const onChangeTrestleMaker = (e) => {
|
||||
setSelectedTrestle(e)
|
||||
// 다음 단계들 초기화
|
||||
setSelectedConstMthd(null)
|
||||
setSelectedRoofBase(null)
|
||||
setSelectedConstruction(null)
|
||||
|
||||
dispatch({
|
||||
type: 'SET_TRESTLE_MAKER',
|
||||
roof: {
|
||||
@ -205,32 +301,48 @@ const Trestle = forwardRef((props, ref) => {
|
||||
trestleMkrCd: e.trestleMkrCd,
|
||||
},
|
||||
})
|
||||
|
||||
// API 호출 완료 후 다음 단계(공법) 자동 선택 설정
|
||||
setTimeout(() => {
|
||||
setAutoSelectStep('constMthd')
|
||||
}, 300)
|
||||
}
|
||||
|
||||
const onChangeConstMthd = (e) => {
|
||||
setSelectedConstMthd(e)
|
||||
// 다음 단계 초기화
|
||||
setSelectedRoofBase(null)
|
||||
setSelectedConstruction(null)
|
||||
|
||||
dispatch({
|
||||
type: 'SET_CONST_MTHD',
|
||||
roof: {
|
||||
moduleTpCd: selectedModules.itemTp ?? '',
|
||||
roofMatlCd: selectedRoof?.roofMatlCd ?? '',
|
||||
raft: selectedRaftBase?.clCode,
|
||||
trestleMkrCd: selectedTrestle.trestleMkrCd,
|
||||
trestleMkrCd: selectedTrestle?.trestleMkrCd,
|
||||
constMthdCd: e.constMthdCd,
|
||||
},
|
||||
})
|
||||
|
||||
// API 호출 완료 후 다음 단계(지붕밑바탕) 자동 선택 설정
|
||||
setTimeout(() => {
|
||||
setAutoSelectStep('roofBase')
|
||||
}, 300)
|
||||
}
|
||||
|
||||
const onChangeRoofBase = (e) => {
|
||||
setSelectedRoofBase(e)
|
||||
setSelectedConstruction(null)
|
||||
|
||||
dispatch({
|
||||
type: 'SET_ROOF_BASE',
|
||||
roof: {
|
||||
moduleTpCd: selectedModules.itemTp ?? '',
|
||||
roofMatlCd: selectedRoof?.roofMatlCd ?? '',
|
||||
raft: selectedRaftBase?.clCode,
|
||||
trestleMkrCd: selectedTrestle.trestleMkrCd,
|
||||
constMthdCd: selectedConstMthd.constMthdCd,
|
||||
trestleMkrCd: selectedTrestle?.trestleMkrCd,
|
||||
constMthdCd: selectedConstMthd?.constMthdCd,
|
||||
roofBaseCd: e.roofBaseCd,
|
||||
illuminationTp: managementState?.surfaceTypeValue ?? '',
|
||||
instHt: managementState?.installHeight ?? '',
|
||||
@ -240,6 +352,11 @@ const Trestle = forwardRef((props, ref) => {
|
||||
roofPitch: Math.round(hajebichi ?? 0),
|
||||
},
|
||||
})
|
||||
|
||||
// API 호출 완료 후 다음 단계(construction) 자동 선택 설정
|
||||
setTimeout(() => {
|
||||
setAutoSelectStep('construction')
|
||||
}, 300)
|
||||
}
|
||||
|
||||
const handleConstruction = (index) => {
|
||||
@ -558,7 +675,19 @@ const Trestle = forwardRef((props, ref) => {
|
||||
type="text"
|
||||
className="input-origin block"
|
||||
value={lengthBase}
|
||||
onChange={(e) => onChangeLength(e.target.value)}
|
||||
onChange={(e) => {
|
||||
const v = e.target.value
|
||||
if (v === '') {
|
||||
onChangeLength('')
|
||||
return
|
||||
}
|
||||
const n = Number(normalizeDigits(v))
|
||||
if (Number.isNaN(n)) {
|
||||
onChangeLength('')
|
||||
} else {
|
||||
onChangeLength(n)
|
||||
}
|
||||
}}
|
||||
disabled={selectedRoof.lenAuth === 'R'}
|
||||
/>
|
||||
</div>
|
||||
@ -581,6 +710,8 @@ const Trestle = forwardRef((props, ref) => {
|
||||
showKey={'clCodeNm'}
|
||||
disabled={selectedRoof.raftAuth === 'R'}
|
||||
onChange={(e) => onChangeRaftBase(e)}
|
||||
showFirstOptionWhenEmpty={true}
|
||||
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@ -598,7 +729,19 @@ const Trestle = forwardRef((props, ref) => {
|
||||
type="text"
|
||||
className="input-origin block"
|
||||
disabled={selectedRoof.roofPchAuth === 'R'}
|
||||
onChange={(e) => onChangeHajebichi(e.target.value)}
|
||||
onChange={(e) => {
|
||||
const v = e.target.value
|
||||
if (v === '') {
|
||||
onChangeHajebichi('')
|
||||
return
|
||||
}
|
||||
const n = Number(normalizeDigits(v))
|
||||
if (Number.isNaN(n)) {
|
||||
onChangeHajebichi('')
|
||||
} else {
|
||||
onChangeHajebichi(n)
|
||||
}
|
||||
}}
|
||||
value={hajebichi}
|
||||
/>
|
||||
</div>
|
||||
@ -619,6 +762,7 @@ const Trestle = forwardRef((props, ref) => {
|
||||
targetKey={'trestleMkrCd'}
|
||||
showKey={'trestleMkrCdJp'}
|
||||
onChange={(e) => onChangeTrestleMaker(e)}
|
||||
showFirstOptionWhenEmpty={true}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@ -637,6 +781,7 @@ const Trestle = forwardRef((props, ref) => {
|
||||
targetKey={'constMthdCd'}
|
||||
showKey={'constMthdCdJp'}
|
||||
onChange={(e) => onChangeConstMthd(e)}
|
||||
showFirstOptionWhenEmpty={true}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@ -655,6 +800,7 @@ const Trestle = forwardRef((props, ref) => {
|
||||
showKey={'roofBaseCdJp'}
|
||||
value={selectedRoofBase}
|
||||
onChange={(e) => onChangeRoofBase(e)}
|
||||
showFirstOptionWhenEmpty={true}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@ -120,7 +120,44 @@ export default function CircuitTrestleSetting({ id }) {
|
||||
const beforeCapture = (type) => {
|
||||
setCanvasZoom(100)
|
||||
canvas.set({ zoom: 1 })
|
||||
canvas.viewportTransform = [1, 0, 0, 1, 0, 0]
|
||||
|
||||
// roof 객체들을 찾아서 중앙점 계산
|
||||
const roofs = canvas.getObjects().filter((obj) => obj.name === 'roof')
|
||||
|
||||
if (roofs.length > 0) {
|
||||
// 모든 roof의 x, y 좌표를 수집
|
||||
const allPoints = []
|
||||
roofs.forEach((roof) => {
|
||||
if (roof.getCurrentPoints()) {
|
||||
roof.getCurrentPoints().forEach((point) => {
|
||||
allPoints.push({ x: point.x, y: point.y })
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
if (allPoints.length > 0) {
|
||||
// 모든 점들의 중앙값 계산
|
||||
const minX = Math.min(...allPoints.map((p) => p.x))
|
||||
const maxX = Math.max(...allPoints.map((p) => p.x))
|
||||
const minY = Math.min(...allPoints.map((p) => p.y))
|
||||
const maxY = Math.max(...allPoints.map((p) => p.y))
|
||||
|
||||
const centerX = (minX + maxX) / 2
|
||||
const centerY = (minY + maxY) / 2
|
||||
|
||||
// 캔버스 중앙으로 이동하기 위한 오프셋 계산
|
||||
const canvasWidth = canvas.getWidth()
|
||||
const canvasHeight = canvas.getHeight()
|
||||
const offsetX = canvasWidth / 2 - centerX
|
||||
const offsetY = canvasHeight / 2 - centerY
|
||||
|
||||
canvas.viewportTransform = [1, 0, 0, 1, offsetX, offsetY]
|
||||
} else {
|
||||
canvas.viewportTransform = [1, 0, 0, 1, 0, 0]
|
||||
}
|
||||
} else {
|
||||
canvas.viewportTransform = [1, 0, 0, 1, 0, 0]
|
||||
}
|
||||
|
||||
const modules = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE)
|
||||
const circuitNumberTexts = canvas.getObjects().filter((obj) => obj.name === 'circuitNumber')
|
||||
@ -139,40 +176,9 @@ export default function CircuitTrestleSetting({ id }) {
|
||||
// roof polygon들의 중간점 계산
|
||||
const roofPolygons = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
||||
let x, y
|
||||
x = 0 //canvas.width / 2
|
||||
y = 1000 //canvas.height / 2
|
||||
|
||||
/*if (roofPolygons.length > 0) {
|
||||
let minX = Infinity,
|
||||
minY = Infinity,
|
||||
maxX = -Infinity,
|
||||
maxY = -Infinity
|
||||
|
||||
roofPolygons.forEach((obj) => {
|
||||
const boundingRect = obj.getBoundingRect()
|
||||
minX = Math.min(minX, boundingRect.left)
|
||||
minY = Math.min(minY, boundingRect.top)
|
||||
maxX = Math.max(maxX, boundingRect.left + boundingRect.width)
|
||||
maxY = Math.max(maxY, boundingRect.top + boundingRect.height)
|
||||
})
|
||||
|
||||
x = (minX + maxX) / 2
|
||||
y = (minY + maxY) / 2
|
||||
} else {
|
||||
// roof polygon이 없으면 기본 중앙점 사용
|
||||
x = canvas.width / 2
|
||||
y = canvas.height / 2
|
||||
}
|
||||
|
||||
if (x > 1600) {
|
||||
x = 0
|
||||
y = 0
|
||||
}
|
||||
if (y > 1600) {
|
||||
x = 0
|
||||
y = 0
|
||||
}*/
|
||||
|
||||
x = canvas.width / 2
|
||||
y = canvas.height / 2
|
||||
|
||||
canvas.zoomToPoint(new fabric.Point(x, y), 0.4)
|
||||
changeFontSize('lengthText', '28')
|
||||
changeFontSize('circuitNumber', '28')
|
||||
|
||||
@ -74,11 +74,9 @@ export default function PowerConditionalSelect(props) {
|
||||
]
|
||||
|
||||
useEffect(() => {
|
||||
if (makers.length === 0) {
|
||||
getPcsMakerList().then((res) => {
|
||||
setMakers(res.data)
|
||||
})
|
||||
}
|
||||
getPcsMakerList().then((res) => {
|
||||
setMakers(res.data)
|
||||
})
|
||||
}, [])
|
||||
|
||||
const onCheckSeries = (data) => {
|
||||
|
||||
@ -12,6 +12,7 @@ import { selectedModuleState } from '@/store/selectedModuleOptions'
|
||||
import { circuitNumDisplaySelector } from '@/store/settingAtom'
|
||||
import { useContext, useEffect, useState } from 'react'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
import { normalizeDigits } from '@/util/input-utils'
|
||||
|
||||
export default function PassivityCircuitAllocation(props) {
|
||||
const {
|
||||
@ -580,7 +581,20 @@ export default function PassivityCircuitAllocation(props) {
|
||||
value={circuitNumber}
|
||||
min={1}
|
||||
max={99}
|
||||
onChange={(e) => setCircuitNumber(e.target.value)}
|
||||
onChange={(e) => {
|
||||
const v = e.target.value
|
||||
if (v === '') {
|
||||
setCircuitNumber('')
|
||||
return
|
||||
}
|
||||
const n = Number(normalizeDigits(v))
|
||||
if (Number.isNaN(n)) {
|
||||
setCircuitNumber('')
|
||||
} else {
|
||||
const clamped = Math.max(1, Math.min(99, n))
|
||||
setCircuitNumber(clamped)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<button className="btn-frame roof" onClick={() => handleCircuitNumberFix()}>
|
||||
|
||||
@ -10,6 +10,7 @@ import { defaultSlope } from '@/store/commonAtom'
|
||||
import { re } from 'mathjs'
|
||||
import { basicSettingState } from '@/store/settingAtom'
|
||||
import { getChonByDegree, getDegreeByChon } from '@/util/canvas-util'
|
||||
import { normalizeDigits } from '@/util/input-utils'
|
||||
|
||||
export default function DimensionLineSetting(props) {
|
||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||
@ -144,8 +145,13 @@ export default function DimensionLineSetting(props) {
|
||||
defaultValue={''}
|
||||
value={changeLength}
|
||||
onChange={(e) => {
|
||||
console.log(e.target)
|
||||
setChangeLength(e.target.value)
|
||||
const v = e.target.value
|
||||
if (v === '') {
|
||||
setChangeLength('')
|
||||
return
|
||||
}
|
||||
const n = Number(normalizeDigits(v))
|
||||
setChangeLength(Number.isNaN(n) ? '' : n)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -3,6 +3,7 @@ import Image from 'next/image'
|
||||
import { useState } from 'react'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { ANGLE_TYPE, currentAngleTypeSelector } from '@/store/canvasAtom'
|
||||
import { normalizeDecimalLimit, normalizeDigits } from '@/util/input-utils'
|
||||
|
||||
export default function Eaves({ pitchRef, offsetRef, widthRef, radioTypeRef, pitchText }) {
|
||||
const { getMessage } = useMessage()
|
||||
@ -20,7 +21,17 @@ export default function Eaves({ pitchRef, offsetRef, widthRef, radioTypeRef, pit
|
||||
{getMessage('slope')}
|
||||
</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" defaultValue={currentAngleType === ANGLE_TYPE.SLOPE ? 4 : 21.8} ref={pitchRef} />
|
||||
<input
|
||||
type="text"
|
||||
className="input-origin block"
|
||||
defaultValue={currentAngleType === ANGLE_TYPE.SLOPE ? 4 : 21.8}
|
||||
ref={pitchRef}
|
||||
onChange={(e) => {
|
||||
const v = normalizeDecimalLimit(e.target.value, 2)
|
||||
e.target.value = v
|
||||
if (pitchRef?.current) pitchRef.current.value = v
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<span className="thin">{pitchText}</span>
|
||||
</div>
|
||||
@ -29,7 +40,17 @@ export default function Eaves({ pitchRef, offsetRef, widthRef, radioTypeRef, pit
|
||||
{getMessage('offset')}
|
||||
</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" defaultValue={500} ref={offsetRef} />
|
||||
<input
|
||||
type="text"
|
||||
className="input-origin block"
|
||||
defaultValue={500}
|
||||
ref={offsetRef}
|
||||
onChange={(e) => {
|
||||
const v = normalizeDigits(e.target.value)
|
||||
e.target.value = v
|
||||
if (offsetRef?.current) offsetRef.current.value = v
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
@ -66,7 +87,18 @@ export default function Eaves({ pitchRef, offsetRef, widthRef, radioTypeRef, pit
|
||||
<div className="eaves-keraba-th">
|
||||
<div className="outline-form">
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="number" min={0} className="input-origin block" defaultValue={500} ref={widthRef} readOnly={type === '1'} />
|
||||
<input
|
||||
type="text"
|
||||
className="input-origin block"
|
||||
defaultValue={500}
|
||||
ref={widthRef}
|
||||
readOnly={type === '1'}
|
||||
onChange={(e) => {
|
||||
const v = normalizeDigits(e.target.value)
|
||||
e.target.value = v
|
||||
if (widthRef?.current) widthRef.current.value = v
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
|
||||
@ -4,10 +4,10 @@ import { useEffect, useState } from 'react'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { canvasState } from '@/store/canvasAtom'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { onlyNumberInputChange } from '@/util/input-utils'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
|
||||
import { useSwal } from '@/hooks/useSwal'
|
||||
import { normalizeDigits } from '@/util/input-utils'
|
||||
|
||||
const TYPE = {
|
||||
DOT: 'DOT',
|
||||
@ -218,7 +218,7 @@ export default function DotLineGrid(props) {
|
||||
className="input-origin"
|
||||
name={`horizontalInterval`}
|
||||
value={copyCurrentSetting.INTERVAL.horizontalInterval}
|
||||
onChange={(e) => onlyNumberInputChange(e, changeInput)}
|
||||
onChange={(e) => changeInput(normalizeDigits(e.target.value), e)}
|
||||
/>
|
||||
</div>
|
||||
<span>mm</span>
|
||||
@ -231,7 +231,7 @@ export default function DotLineGrid(props) {
|
||||
className="input-origin"
|
||||
name={`verticalInterval`}
|
||||
value={copyCurrentSetting.INTERVAL.verticalInterval}
|
||||
onChange={(e) => onlyNumberInputChange(e, changeInput)}
|
||||
onChange={(e) => changeInput(normalizeDigits(e.target.value), e)}
|
||||
/>
|
||||
</div>
|
||||
<span>mm</span>
|
||||
@ -258,7 +258,7 @@ export default function DotLineGrid(props) {
|
||||
className="input-origin"
|
||||
name={`ratioInterval`}
|
||||
value={copyCurrentSetting.INTERVAL.ratioInterval}
|
||||
onChange={(e) => onlyNumberInputChange(e, changeInput)}
|
||||
onChange={(e) => changeInput(normalizeDigits(e.target.value), e)}
|
||||
/>
|
||||
</div>
|
||||
<span>mm</span>
|
||||
|
||||
@ -5,9 +5,9 @@ import { useRecoilValue } from 'recoil'
|
||||
import { contextPopupPositionState } from '@/store/popupAtom'
|
||||
import { useState } from 'react'
|
||||
import { canvasState, currentObjectState } from '@/store/canvasAtom'
|
||||
import { useGrid } from '@/hooks/common/useGrid'
|
||||
import { gridColorState } from '@/store/gridAtom'
|
||||
import { gridDisplaySelector } from '@/store/settingAtom'
|
||||
|
||||
const GRID_PADDING = 5
|
||||
export default function GridCopy(props) {
|
||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||
@ -25,10 +25,10 @@ export default function GridCopy(props) {
|
||||
}
|
||||
|
||||
const copy = (object, length) => {
|
||||
const lineStartX = object.direction === 'vertical' ? object.x1 + length : 0
|
||||
const lineEndX = object.direction === 'vertical' ? object.x2 + length : canvas.width
|
||||
const lineStartY = object.direction === 'vertical' ? 0 : object.y1 + length
|
||||
const lineEndY = object.direction === 'vertical' ? canvas.width : object.y1 + length
|
||||
const lineStartX = object.direction === 'vertical' ? object.x1 + length : object.x1
|
||||
const lineEndX = object.direction === 'vertical' ? object.x2 + length : object.x2
|
||||
const lineStartY = object.direction === 'vertical' ? object.y1 : object.y1 + length
|
||||
const lineEndY = object.direction === 'vertical' ? object.y2 : object.y1 + length
|
||||
|
||||
const line = new fabric.Line([lineStartX, lineStartY, lineEndX, lineEndY], {
|
||||
stroke: gridColor,
|
||||
|
||||
@ -3,11 +3,10 @@ import { useMessage } from '@/hooks/useMessage'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
import { contextPopupPositionState } from '@/store/popupAtom'
|
||||
import { useCanvas } from '@/hooks/useCanvas'
|
||||
import { canvasState, currentObjectState } from '@/store/canvasAtom'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useSwal } from '@/hooks/useSwal'
|
||||
import { set } from 'react-hook-form'
|
||||
import { normalizeDigits } from '@/util/input-utils'
|
||||
|
||||
export default function GridMove(props) {
|
||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||
@ -52,15 +51,15 @@ export default function GridMove(props) {
|
||||
.forEach((grid) => {
|
||||
move(
|
||||
grid,
|
||||
arrow2 === '←' ? (Number(horizonSize) * -1) / 10 : Number(horizonSize) / 10,
|
||||
arrow1 === '↑' ? (Number(verticalSize) * -1) / 10 : Number(verticalSize) / 10,
|
||||
arrow2 === '←' ? (Number(normalizeDigits(horizonSize)) * -1) / 10 : Number(normalizeDigits(horizonSize)) / 10,
|
||||
arrow1 === '↑' ? (Number(normalizeDigits(verticalSize)) * -1) / 10 : Number(normalizeDigits(verticalSize)) / 10,
|
||||
)
|
||||
})
|
||||
} else {
|
||||
move(
|
||||
currentObject,
|
||||
arrow2 === '←' ? (Number(horizonSize) * -1) / 10 : Number(horizonSize) / 10,
|
||||
arrow1 === '↑' ? (Number(verticalSize) * -1) / 10 : Number(verticalSize) / 10,
|
||||
arrow2 === '←' ? (Number(normalizeDigits(horizonSize)) * -1) / 10 : Number(normalizeDigits(horizonSize)) / 10,
|
||||
arrow1 === '↑' ? (Number(normalizeDigits(verticalSize)) * -1) / 10 : Number(normalizeDigits(verticalSize)) / 10,
|
||||
)
|
||||
}
|
||||
canvas.renderAll()
|
||||
@ -70,10 +69,10 @@ export default function GridMove(props) {
|
||||
const move = (object, x, y) => {
|
||||
object.set({
|
||||
...object,
|
||||
x1: object.direction === 'vertical' ? object.x1 + x : 0,
|
||||
x2: object.direction === 'vertical' ? object.x1 + x : canvas.width,
|
||||
y1: object.direction === 'vertical' ? 0 : object.y1 + y,
|
||||
y2: object.direction === 'vertical' ? canvas.height : object.y1 + y,
|
||||
x1: object.direction === 'vertical' ? object.x1 + x : object.x1,
|
||||
x2: object.direction === 'vertical' ? object.x1 + x : object.x2,
|
||||
y1: object.direction === 'vertical' ? object.y1 : object.y1 + y,
|
||||
y2: object.direction === 'vertical' ? object.y2 : object.y1 + y,
|
||||
})
|
||||
}
|
||||
|
||||
@ -99,7 +98,7 @@ export default function GridMove(props) {
|
||||
type="text"
|
||||
className="input-origin"
|
||||
value={verticalSize}
|
||||
onChange={(e) => setVerticalSize(e.target.value)}
|
||||
onChange={(e) => setVerticalSize(normalizeDigits(e.target.value))}
|
||||
readOnly={!isAll && currentObject?.direction === 'vertical'}
|
||||
/>
|
||||
</div>
|
||||
@ -127,7 +126,7 @@ export default function GridMove(props) {
|
||||
type="text"
|
||||
className="input-origin"
|
||||
value={horizonSize}
|
||||
onChange={(e) => setHorizonSize(e.target.value)}
|
||||
onChange={(e) => setHorizonSize(normalizeDigits(e.target.value))}
|
||||
readOnly={!isAll && currentObject?.direction === 'horizontal'}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import Image from 'next/image'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils'
|
||||
import { normalizeDecimalLimit, normalizeDigits } from '@/util/input-utils'
|
||||
|
||||
export default function Angle({ props }) {
|
||||
const { getMessage } = useMessage()
|
||||
@ -20,7 +20,7 @@ export default function Angle({ props }) {
|
||||
value={angle1}
|
||||
ref={angle1Ref}
|
||||
onFocus={(e) => (angle1Ref.current.value = '')}
|
||||
onChange={(e) => onlyNumberWithDotInputChange(e, setAngle1)}
|
||||
onChange={(e) => setAngle1(normalizeDecimalLimit(e.target.value, 2))}
|
||||
placeholder="45"
|
||||
/>
|
||||
</div>
|
||||
@ -40,7 +40,7 @@ export default function Angle({ props }) {
|
||||
value={length1}
|
||||
ref={length1Ref}
|
||||
onFocus={(e) => (length1Ref.current.value = '')}
|
||||
onChange={(e) => onlyNumberInputChange(e, setLength1)}
|
||||
onChange={(e) => setLength1(normalizeDigits(e.target.value))}
|
||||
placeholder="3000"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { onlyNumberInputChange } from '@/util/input-utils'
|
||||
import { normalizeDigits } from '@/util/input-utils'
|
||||
|
||||
export default function Diagonal({ props }) {
|
||||
const { getMessage } = useMessage()
|
||||
@ -36,7 +36,7 @@ export default function Diagonal({ props }) {
|
||||
value={outerLineDiagonalLength}
|
||||
ref={outerLineDiagonalLengthRef}
|
||||
onFocus={(e) => (outerLineDiagonalLengthRef.current.value = '')}
|
||||
onChange={(e) => onlyNumberInputChange(e, setOuterLineDiagonalLength)}
|
||||
onChange={(e) => setOuterLineDiagonalLength(normalizeDigits(e.target.value))}
|
||||
placeholder="3000"
|
||||
/>
|
||||
</div>
|
||||
@ -58,7 +58,7 @@ export default function Diagonal({ props }) {
|
||||
value={length1}
|
||||
ref={length1Ref}
|
||||
onFocus={(e) => (length1Ref.current.value = '')}
|
||||
onChange={(e) => onlyNumberInputChange(e, setLength1)}
|
||||
onChange={(e) => setLength1(normalizeDigits(e.target.value))}
|
||||
placeholder="3000"
|
||||
/>
|
||||
</div>
|
||||
@ -115,7 +115,7 @@ export default function Diagonal({ props }) {
|
||||
className="input-origin block"
|
||||
value={length2}
|
||||
ref={length2Ref}
|
||||
onChange={(e) => onlyNumberInputChange(e, setLength2)}
|
||||
onChange={(e) => setLength2(normalizeDigits(e.target.value))}
|
||||
readOnly={true}
|
||||
placeholder="3000"
|
||||
/>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils'
|
||||
import { normalizeDecimalLimit, normalizeDigits } from '@/util/input-utils'
|
||||
import { getDegreeByChon } from '@/util/canvas-util'
|
||||
|
||||
export default function DoublePitch({ props }) {
|
||||
@ -56,7 +56,7 @@ export default function DoublePitch({ props }) {
|
||||
value={angle1}
|
||||
ref={angle1Ref}
|
||||
onFocus={(e) => (angle1Ref.current.value = '')}
|
||||
onChange={(e) => onlyNumberWithDotInputChange(e, setAngle1)}
|
||||
onChange={(e) => setAngle1(normalizeDecimalLimit(e.target.value, 2))}
|
||||
placeholder="45"
|
||||
/>
|
||||
</div>
|
||||
@ -73,7 +73,7 @@ export default function DoublePitch({ props }) {
|
||||
value={length1}
|
||||
ref={length1Ref}
|
||||
onFocus={(e) => (length1Ref.current.value = '')}
|
||||
onChange={(e) => onlyNumberInputChange(e, setLength1)}
|
||||
onChange={(e) => setLength1(normalizeDigits(e.target.value))}
|
||||
placeholder="3000"
|
||||
/>
|
||||
</div>
|
||||
@ -132,7 +132,8 @@ export default function DoublePitch({ props }) {
|
||||
ref={angle2Ref}
|
||||
onFocus={(e) => (angle2Ref.current.value = '')}
|
||||
onChange={(e) => {
|
||||
onlyNumberWithDotInputChange(e, setAngle2)
|
||||
const v = normalizeDecimalLimit(e.target.value, 2)
|
||||
setAngle2(v)
|
||||
setLength2(getLength2())
|
||||
}}
|
||||
placeholder="45"
|
||||
@ -156,7 +157,7 @@ export default function DoublePitch({ props }) {
|
||||
value={length2}
|
||||
ref={length2Ref}
|
||||
onFocus={(e) => (length2Ref.current.value = '')}
|
||||
onChange={(e) => onlyNumberInputChange(e, setLength2)}
|
||||
onChange={(e) => setLength2(normalizeDigits(e.target.value))}
|
||||
readOnly={true}
|
||||
placeholder="3000"
|
||||
/>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { onlyNumberInputChange } from '@/util/input-utils'
|
||||
import { normalizeDigits } from '@/util/input-utils'
|
||||
|
||||
export default function OuterLineWall({ props }) {
|
||||
const { getMessage } = useMessage()
|
||||
@ -23,7 +23,7 @@ export default function OuterLineWall({ props }) {
|
||||
length1Ref.current.value = ''
|
||||
}
|
||||
}}
|
||||
onChange={(e) => onlyNumberInputChange(e, setLength1)}
|
||||
onChange={(e) => setLength1(normalizeDigits(e.target.value))}
|
||||
placeholder="3000"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { onlyNumberInputChange } from '@/util/input-utils'
|
||||
import { normalizeDigits } from '@/util/input-utils'
|
||||
|
||||
export default function RightAngle({ props }) {
|
||||
const { getMessage } = useMessage()
|
||||
@ -28,7 +28,7 @@ export default function RightAngle({ props }) {
|
||||
value={length1}
|
||||
ref={length1Ref}
|
||||
onFocus={(e) => (length1Ref.current.value = '')}
|
||||
onChange={(e) => onlyNumberInputChange(e, setLength1)}
|
||||
onChange={(e) => setLength1(normalizeDigits(e.target.value))}
|
||||
placeholder="3000"
|
||||
/>
|
||||
</div>
|
||||
@ -84,7 +84,7 @@ export default function RightAngle({ props }) {
|
||||
value={length2}
|
||||
ref={length2Ref}
|
||||
onFocus={(e) => (length2Ref.current.value = '')}
|
||||
onChange={(e) => onlyNumberInputChange(e, setLength2)}
|
||||
onChange={(e) => setLength2(normalizeDigits(e.target.value))}
|
||||
placeholder="3000"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -2,6 +2,7 @@ import { useMessage } from '@/hooks/useMessage'
|
||||
import { useState } from 'react'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { currentObjectState } from '@/store/canvasAtom'
|
||||
import { normalizeDigits } from '@/util/input-utils'
|
||||
|
||||
const UP_DOWN_TYPE = {
|
||||
UP: 'up',
|
||||
@ -22,7 +23,8 @@ export default function Updown({ UP_DOWN_REF }) {
|
||||
}
|
||||
const handleInput = (e) => {
|
||||
const value = e.target.value.replace(/^0+/, '')
|
||||
setFilledInput(value.replace(/[^0-9]/g, ''))
|
||||
//setFilledInput(value.replace(/[^0-9]/g, ''))
|
||||
setFilledInput(normalizeDigits(value))
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@ -13,6 +13,7 @@ import Shadow from '@/components/floor-plan/modal/object/type/Shadow'
|
||||
import TriangleDormer from '@/components/floor-plan/modal/object/type/TriangleDormer'
|
||||
import PentagonDormer from '@/components/floor-plan/modal/object/type/PentagonDormer'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
import { normalizeDecimalLimit, normalizeDigits } from '@/util/input-utils'
|
||||
|
||||
export default function ObjectSetting({ id, pos = { x: 50, y: 230 } }) {
|
||||
const { getMessage } = useMessage()
|
||||
@ -61,20 +62,20 @@ export default function ObjectSetting({ id, pos = { x: 50, y: 230 } }) {
|
||||
if (buttonAct === 1 || buttonAct === 2) {
|
||||
applyOpeningAndShadow(objectPlacement, buttonAct)
|
||||
} else {
|
||||
const height = dormerPlacement.heightRef.current !== null ? dormerPlacement.heightRef.current.value / 10 : 0
|
||||
const width = dormerPlacement.widthRef.current !== null ? dormerPlacement.widthRef.current.value / 10 : 0 //triangle일때는 없음
|
||||
const pitch = dormerPlacement.pitchRef.current !== null ? Number(dormerPlacement.pitchRef.current.value) : 0
|
||||
const height = dormerPlacement.heightRef.current !== null ? Number(normalizeDigits(dormerPlacement.heightRef.current.value)) / 10 : 0
|
||||
const width = dormerPlacement.widthRef.current !== null ? Number(normalizeDigits(dormerPlacement.widthRef.current.value)) / 10 : 0 //triangle일때는 없음
|
||||
const pitch = dormerPlacement.pitchRef.current !== null ? Number(normalizeDecimalLimit(dormerPlacement.pitchRef.current.value, 2)) : 0
|
||||
const offsetRef =
|
||||
dormerPlacement.offsetRef.current !== null
|
||||
? dormerPlacement.offsetRef.current.value === ''
|
||||
? 0
|
||||
: parseInt(dormerPlacement.offsetRef.current.value) / 10
|
||||
: Number(normalizeDigits(dormerPlacement.offsetRef.current.value)) / 10
|
||||
: 0
|
||||
const offsetWidthRef =
|
||||
dormerPlacement.offsetWidthRef.current !== null
|
||||
? dormerPlacement.offsetWidthRef.current.value === ''
|
||||
? 0
|
||||
: parseInt(dormerPlacement.offsetWidthRef.current.value) / 10
|
||||
: Number(normalizeDigits(dormerPlacement.offsetWidthRef.current.value)) / 10
|
||||
: 0
|
||||
const directionRef = dormerPlacement.directionRef.current
|
||||
|
||||
|
||||
@ -14,12 +14,13 @@ import { useCommonCode } from '@/hooks/common/useCommonCode'
|
||||
import QSelectBox from '@/components/common/select/QSelectBox'
|
||||
import { globalLocaleStore } from '@/store/localeAtom'
|
||||
|
||||
import { onlyNumberInputChange } from '@/util/input-utils'
|
||||
import { getChonByDegree, getDegreeByChon } from '@/util/canvas-util'
|
||||
import { usePolygon } from '@/hooks/usePolygon'
|
||||
import { canvasState } from '@/store/canvasAtom'
|
||||
import { useRoofFn } from '@/hooks/common/useRoofFn'
|
||||
import { usePlan } from '@/hooks/usePlan'
|
||||
import { normalizeDecimal, normalizeDigits } from '@/util/input-utils'
|
||||
import { logger } from '@/util/logger'
|
||||
|
||||
/**
|
||||
* 지붕 레이아웃
|
||||
@ -182,7 +183,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
|
||||
*/
|
||||
const changeInput = (value, e) => {
|
||||
const { name } = e.target
|
||||
setCurrentRoof({ ...currentRoof, [name]: Number(value) })
|
||||
setCurrentRoof({ ...currentRoof, [name]: Number(normalizeDecimal(value)) })
|
||||
}
|
||||
|
||||
/**
|
||||
@ -207,6 +208,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
|
||||
* 배치면초기설정 저장 버튼 클릭
|
||||
*/
|
||||
const handleSaveBtn = async () => {
|
||||
|
||||
const roofInfo = {
|
||||
...currentRoof,
|
||||
planNo: basicSetting.planNo,
|
||||
@ -214,7 +216,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
|
||||
width: roofRef.width.current?.value,
|
||||
length: roofRef.length.current?.value,
|
||||
hajebichi: roofRef.hajebichi.current?.value,
|
||||
raft: roofRef.rafter.current?.value,
|
||||
//raft: roofRef.rafter.current?.value,
|
||||
selected: true,
|
||||
layout: currentRoof.layout,
|
||||
index: 0,
|
||||
@ -224,7 +226,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
|
||||
newAddedRoofs[0] = { ...roofInfo }
|
||||
setAddedRoofs(newAddedRoofs)
|
||||
|
||||
console.log('save Info', {
|
||||
logger.debug('save Info', {
|
||||
...basicSetting,
|
||||
selectedRoofMaterial: {
|
||||
...newAddedRoofs[0],
|
||||
@ -325,15 +327,21 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
|
||||
</span>
|
||||
<div className="input-grid mr5">
|
||||
<input
|
||||
type="number"
|
||||
type="text"
|
||||
className="input-origin block"
|
||||
readOnly={currentRoof?.roofAngleSet !== item.value}
|
||||
value={index === 0 ? (currentRoof?.pitch || '0') : (currentRoof?.angle || '0')}
|
||||
onChange={(e) =>
|
||||
index === 0
|
||||
? setCurrentRoof({ ...currentRoof, pitch: e.target.value * 1, angle: getDegreeByChon(e.target.value * 1) })
|
||||
: setCurrentRoof({ ...currentRoof, pitch: getChonByDegree(e.target.value*1), angle: e.target.value * 1})
|
||||
}
|
||||
onChange={(e) => {
|
||||
const v = normalizeDecimal(e.target.value)
|
||||
e.target.value = v
|
||||
if (index === 0) {
|
||||
const num = v === '' ? '' : Number(v)
|
||||
setCurrentRoof({ ...currentRoof, pitch: num === '' ? '' : num, angle: num === '' ? '' : getDegreeByChon(num) })
|
||||
} else {
|
||||
const num = v === '' ? '' : Number(v)
|
||||
setCurrentRoof({ ...currentRoof, pitch: num === '' ? '' : getChonByDegree(num), angle: num === '' ? '' : num })
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<span className="thin">{index === 0 ? '寸' : '度'}</span>
|
||||
@ -381,7 +389,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
|
||||
name={`width`}
|
||||
ref={roofRef.width}
|
||||
value={parseInt(currentRoof?.width)}
|
||||
onChange={(e) => onlyNumberInputChange(e, changeInput)}
|
||||
onChange={(e) => changeInput(normalizeDigits(e.target.value), e)}
|
||||
readOnly={currentRoof?.widAuth === 'R'}
|
||||
disabled={currentRoof?.roofSizeSet === '3'}
|
||||
/>
|
||||
@ -398,7 +406,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
|
||||
name={`length`}
|
||||
ref={roofRef.length}
|
||||
value={parseInt(currentRoof?.length)}
|
||||
onChange={(e) => onlyNumberInputChange(e, changeInput)}
|
||||
onChange={(e) => changeInput(normalizeDigits(e.target.value), e)}
|
||||
readOnly={currentRoof?.lenAuth === 'R'}
|
||||
disabled={currentRoof?.roofSizeSet === '3'}
|
||||
/>
|
||||
@ -413,10 +421,9 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
|
||||
<QSelectBox
|
||||
options={raftCodes}
|
||||
title={
|
||||
raftCodes?.find((r) => r.clCode === (currentRoof?.raft === undefined ? currentRoof?.raftBaseCd : currentRoof?.raft))
|
||||
.clCodeNm
|
||||
raftCodes?.find((r) => r.clCode === ( currentRoof.raft?? currentRoof?.raftBaseCd))?.clCodeNm
|
||||
}
|
||||
value={currentRoof?.raft === undefined ? currentRoof?.raftBaseCd : currentRoof?.raft}
|
||||
value={currentRoof?.raft??currentRoof?.raftBaseCd}
|
||||
onChange={(e) => handleRafterChange(e.clCode)}
|
||||
sourceKey="clCode"
|
||||
targetKey={currentRoof?.raft ? 'raft' : 'raftBaseCd'}
|
||||
@ -437,7 +444,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
|
||||
name={`hajebichi`}
|
||||
ref={roofRef.hajebichi}
|
||||
value={parseInt(currentRoof?.hajebichi)}
|
||||
onChange={(e) => onlyNumberInputChange(e, changeInput)}
|
||||
onChange={(e) => changeInput(normalizeDigits(e.target.value), e)}
|
||||
readOnly={currentRoof?.roofPchAuth === 'R'}
|
||||
disabled={currentRoof?.roofSizeSet === '3'}
|
||||
/>
|
||||
|
||||
@ -7,6 +7,7 @@ import { useEffect, useState } from 'react'
|
||||
import { currentObjectState } from '@/store/canvasAtom'
|
||||
import { useRoofAllocationSetting } from '@/hooks/roofcover/useRoofAllocationSetting'
|
||||
import { useSwal } from '@/hooks/useSwal'
|
||||
import { normalizeDigits } from '@/util/input-utils'
|
||||
|
||||
export default function ActualSizeSetting(props) {
|
||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||
@ -87,7 +88,7 @@ export default function ActualSizeSetting(props) {
|
||||
<div className="eaves-keraba-td">
|
||||
<div className="outline-form">
|
||||
<div className="input-grid mr5">
|
||||
<input type="text" className="input-origin block" value={actualSize} onChange={(e) => setActualSize(Number(e.target.value))} />
|
||||
<input type="text" className="input-origin block" value={actualSize} onChange={(e) => setActualSize(Number(normalizeDigits(e.target.value)))} />
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
|
||||
@ -12,6 +12,7 @@ import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
|
||||
import { useCommonCode } from '@/hooks/common/useCommonCode'
|
||||
import { globalLocaleStore } from '@/store/localeAtom'
|
||||
import { currentAngleTypeSelector, pitchTextSelector } from '@/store/canvasAtom'
|
||||
import { normalizeDecimalLimit, normalizeDigits } from '@/util/input-utils'
|
||||
|
||||
export default function ContextRoofAllocationSetting(props) {
|
||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||
@ -151,6 +152,7 @@ export default function ContextRoofAllocationSetting(props) {
|
||||
defaultValue={roof.width}
|
||||
readOnly={roof.widAuth === 'R'}
|
||||
onChange={(e) => {
|
||||
e.target.value = normalizeDigits(e.target.value)
|
||||
handleChangeInput(e, 'width', index)
|
||||
}}
|
||||
/>
|
||||
@ -169,6 +171,7 @@ export default function ContextRoofAllocationSetting(props) {
|
||||
defaultValue={roof.length}
|
||||
readOnly={roof.lenAuth === 'R'}
|
||||
onChange={(e) => {
|
||||
e.target.value = normalizeDigits(e.target.value)
|
||||
handleChangeInput(e, 'length', index)
|
||||
}}
|
||||
/>
|
||||
@ -188,7 +191,10 @@ export default function ContextRoofAllocationSetting(props) {
|
||||
className="input-origin block"
|
||||
value={roof.hajebichi === '' ? '0' : roof.hajebichi}
|
||||
readOnly={roof.roofPchAuth === 'R'}
|
||||
onChange={(e) => handleChangeInput(e, 'hajebichi', index)}
|
||||
onChange={(e) => {
|
||||
e.target.value = normalizeDigits(e.target.value)
|
||||
handleChangeInput(e, 'hajebichi', index)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -202,7 +208,7 @@ export default function ContextRoofAllocationSetting(props) {
|
||||
type="text"
|
||||
className="input-origin block"
|
||||
onChange={(e) => {
|
||||
// handleChangeInput(e, currentAngleType === 'slope' ? 'pitch' : 'angle', index)
|
||||
e.target.value = normalizeDecimalLimit(e.target.value, 2)
|
||||
handleChangePitch(e, index)
|
||||
}}
|
||||
value={currentAngleType === 'slope' ? (roof.pitch || '0') : (roof.angle || '0')}
|
||||
|
||||
@ -13,7 +13,7 @@ import { globalLocaleStore } from '@/store/localeAtom'
|
||||
import { useRoofShapeSetting } from '@/hooks/roofcover/useRoofShapeSetting'
|
||||
import { currentAngleTypeSelector, pitchTextSelector } from '@/store/canvasAtom'
|
||||
import { getDegreeByChon } from '@/util/canvas-util'
|
||||
import { onlyNumberWithDotInputChange } from '@/util/input-utils'
|
||||
import { normalizeDecimalLimit, normalizeDigits } from '@/util/input-utils'
|
||||
|
||||
export default function RoofAllocationSetting(props) {
|
||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||
@ -151,7 +151,10 @@ export default function RoofAllocationSetting(props) {
|
||||
type="text"
|
||||
className="input-origin block"
|
||||
defaultValue={roof.width}
|
||||
onChange={(e) => handleChangeInput(e, 'width', index)}
|
||||
onChange={(e) => {
|
||||
e.target.value = normalizeDigits(e.target.value)
|
||||
handleChangeInput(e, 'width', index)
|
||||
}}
|
||||
readOnly={roof.widAuth === 'R'}
|
||||
/>
|
||||
</div>
|
||||
@ -167,7 +170,10 @@ export default function RoofAllocationSetting(props) {
|
||||
type="text"
|
||||
className="input-origin block"
|
||||
defaultValue={roof.length}
|
||||
onChange={(e) => handleChangeInput(e, 'length', index)}
|
||||
onChange={(e) => {
|
||||
e.target.value = normalizeDigits(e.target.value)
|
||||
handleChangeInput(e, 'length', index)
|
||||
}}
|
||||
readOnly={roof.lenAuth === 'R'}
|
||||
/>
|
||||
</div>
|
||||
@ -184,7 +190,10 @@ export default function RoofAllocationSetting(props) {
|
||||
<input
|
||||
type="text"
|
||||
className="input-origin block"
|
||||
onChange={(e) => handleChangeInput(e, 'hajebichi', index)}
|
||||
onChange={(e) => {
|
||||
e.target.value = normalizeDigits(e.target.value)
|
||||
handleChangeInput(e, 'hajebichi', index)
|
||||
}}
|
||||
value={parseInt(roof.hajebichi)}
|
||||
readOnly={roof.roofPchAuth === 'R'}
|
||||
/>
|
||||
@ -200,6 +209,7 @@ export default function RoofAllocationSetting(props) {
|
||||
type="text"
|
||||
className="input-origin block"
|
||||
onChange={(e) => {
|
||||
e.target.value = normalizeDecimalLimit(e.target.value, 2)
|
||||
handleChangePitch(e, index)
|
||||
}}
|
||||
value={currentAngleType === 'slope' ? roof.pitch : roof.angle}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils'
|
||||
import { normalizeDecimalLimit, normalizeDigits } from '@/util/input-utils'
|
||||
|
||||
export default function Direction({ pitch, setPitch, eavesOffset, setEavesOffset, gableOffset, setGableOffset, shedWidth, setShedWidth, pitchText }) {
|
||||
const { getMessage } = useMessage()
|
||||
@ -10,7 +10,12 @@ export default function Direction({ pitch, setPitch, eavesOffset, setEavesOffset
|
||||
{getMessage('slope')}
|
||||
</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" value={pitch} onChange={(e) => onlyNumberWithDotInputChange(e, setPitch)} />
|
||||
<input
|
||||
type="text"
|
||||
className="input-origin block"
|
||||
value={pitch}
|
||||
onChange={(e) => setPitch(normalizeDecimalLimit(e.target.value, 2))}
|
||||
/>
|
||||
</div>
|
||||
<span className="thin">{pitchText}</span>
|
||||
</div>
|
||||
@ -19,7 +24,12 @@ export default function Direction({ pitch, setPitch, eavesOffset, setEavesOffset
|
||||
{getMessage('eaves.offset')}
|
||||
</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" value={eavesOffset} onChange={(e) => onlyNumberInputChange(e, setEavesOffset)} />
|
||||
<input
|
||||
type="text"
|
||||
className="input-origin block"
|
||||
value={eavesOffset}
|
||||
onChange={(e) => setEavesOffset(normalizeDigits(e.target.value))}
|
||||
/>
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
@ -28,7 +38,12 @@ export default function Direction({ pitch, setPitch, eavesOffset, setEavesOffset
|
||||
{getMessage('gable.offset')}
|
||||
</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" value={gableOffset} onChange={(e) => onlyNumberInputChange(e, setGableOffset)} />
|
||||
<input
|
||||
type="text"
|
||||
className="input-origin block"
|
||||
value={gableOffset}
|
||||
onChange={(e) => setGableOffset(normalizeDigits(e.target.value))}
|
||||
/>
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
@ -37,7 +52,12 @@ export default function Direction({ pitch, setPitch, eavesOffset, setEavesOffset
|
||||
{getMessage('windage.width')}
|
||||
</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" value={shedWidth} onChange={(e) => onlyNumberInputChange(e, setShedWidth)} />
|
||||
<input
|
||||
type="text"
|
||||
className="input-origin block"
|
||||
value={shedWidth}
|
||||
onChange={(e) => setShedWidth(normalizeDigits(e.target.value))}
|
||||
/>
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils'
|
||||
import { normalizeDecimalLimit, normalizeDigits } from '@/util/input-utils'
|
||||
|
||||
export default function Pattern(props) {
|
||||
const { getMessage } = useMessage()
|
||||
@ -11,7 +11,7 @@ export default function Pattern(props) {
|
||||
{getMessage('slope')}
|
||||
</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" value={pitch} onChange={(e) => onlyNumberWithDotInputChange(e, setPitch)} />
|
||||
<input type="text" className="input-origin block" value={pitch} onChange={(e) => setPitch(normalizeDecimalLimit(e.target.value, 2))} />
|
||||
</div>
|
||||
<span className="thin"> {pitchText}</span>
|
||||
</div>
|
||||
@ -20,7 +20,7 @@ export default function Pattern(props) {
|
||||
{getMessage('eaves.offset')}
|
||||
</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" value={eavesOffset} onChange={(e) => onlyNumberInputChange(e, setEavesOffset)} />
|
||||
<input type="text" className="input-origin block" value={eavesOffset} onChange={(e) => setEavesOffset(normalizeDigits(e.target.value))} />
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
@ -29,7 +29,7 @@ export default function Pattern(props) {
|
||||
{getMessage('gable.offset')}
|
||||
</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" value={gableOffset} onChange={(e) => onlyNumberInputChange(e, setGableOffset)} />
|
||||
<input type="text" className="input-origin block" value={gableOffset} onChange={(e) => setGableOffset(normalizeDigits(e.target.value))} />
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useEffect } from 'react'
|
||||
import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils'
|
||||
import { normalizeDecimalLimit, normalizeDigits } from '@/util/input-utils'
|
||||
|
||||
export default function Ridge(props) {
|
||||
const { getMessage } = useMessage()
|
||||
@ -13,7 +13,7 @@ export default function Ridge(props) {
|
||||
{getMessage('slope')}
|
||||
</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" value={pitch} onChange={(e) => onlyNumberWithDotInputChange(e, setPitch)} />
|
||||
<input type="text" className="input-origin block" value={pitch} onChange={(e) => setPitch(normalizeDecimalLimit(e.target.value, 2))} />
|
||||
</div>
|
||||
<span className="thin">{pitchText}</span>
|
||||
</div>
|
||||
@ -22,7 +22,7 @@ export default function Ridge(props) {
|
||||
{getMessage('eaves.offset')}
|
||||
</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" value={eavesOffset} onChange={(e) => onlyNumberInputChange(e, setEavesOffset)} />
|
||||
<input type="text" className="input-origin block" value={eavesOffset} onChange={(e) => setEavesOffset(normalizeDigits(e.target.value))} />
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils'
|
||||
import { normalizeDecimalLimit, normalizeDigits } from '@/util/input-utils'
|
||||
|
||||
export default function Eaves({ pitch, setPitch, eavesOffset, setEavesOffset, pitchText }) {
|
||||
const { getMessage } = useMessage()
|
||||
@ -10,7 +10,7 @@ export default function Eaves({ pitch, setPitch, eavesOffset, setEavesOffset, pi
|
||||
{getMessage('slope')}
|
||||
</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" value={pitch} onChange={(e) => onlyNumberWithDotInputChange(e, setPitch)} />
|
||||
<input type="text" className="input-origin block" value={pitch} onChange={(e) => setPitch(normalizeDecimalLimit(e.target.value, 2))} />
|
||||
</div>
|
||||
<span className="thin">{pitchText}</span>
|
||||
</div>
|
||||
@ -19,7 +19,7 @@ export default function Eaves({ pitch, setPitch, eavesOffset, setEavesOffset, pi
|
||||
{getMessage('eaves.offset')}
|
||||
</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" value={eavesOffset} onChange={(e) => onlyNumberInputChange(e, setEavesOffset)} />
|
||||
<input type="text" className="input-origin block" value={eavesOffset} onChange={(e) => setEavesOffset(normalizeDigits(e.target.value))} />
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useEffect } from 'react'
|
||||
import { onlyNumberInputChange } from '@/util/input-utils'
|
||||
import { normalizeDigits } from '@/util/input-utils'
|
||||
|
||||
export default function Gable({ gableOffset, setGableOffset }) {
|
||||
const { getMessage } = useMessage()
|
||||
@ -10,7 +10,7 @@ export default function Gable({ gableOffset, setGableOffset }) {
|
||||
<div className="outline-form">
|
||||
<span className="mr10">{getMessage('gable.offset')}</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" value={gableOffset} onChange={(e) => onlyNumberInputChange(e, setGableOffset)} />
|
||||
<input type="text" className="input-origin block" value={gableOffset} onChange={(e) => setGableOffset(normalizeDigits(e.target.value))} />
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils'
|
||||
import { normalizeDecimalLimit, normalizeDigits } from '@/util/input-utils'
|
||||
|
||||
export default function HipAndGable({ pitch, setPitch, eavesOffset, setEavesOffset, hipAndGableWidth, setHipAndGableWidth, pitchText }) {
|
||||
const { getMessage } = useMessage()
|
||||
@ -10,7 +10,7 @@ export default function HipAndGable({ pitch, setPitch, eavesOffset, setEavesOffs
|
||||
{getMessage('slope')}
|
||||
</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" value={pitch} onChange={(e) => onlyNumberWithDotInputChange(e, setPitch)} />
|
||||
<input type="text" className="input-origin block" value={pitch} onChange={(e) => setPitch(normalizeDecimalLimit(e.target.value, 2))} />
|
||||
</div>
|
||||
<span className="thin">{pitchText}</span>
|
||||
</div>
|
||||
@ -19,7 +19,7 @@ export default function HipAndGable({ pitch, setPitch, eavesOffset, setEavesOffs
|
||||
{getMessage('eaves.offset')}
|
||||
</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" value={eavesOffset} onChange={(e) => onlyNumberInputChange(e, setEavesOffset)} />
|
||||
<input type="text" className="input-origin block" value={eavesOffset} onChange={(e) => setEavesOffset(normalizeDigits(e.target.value))} />
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
@ -32,7 +32,7 @@ export default function HipAndGable({ pitch, setPitch, eavesOffset, setEavesOffs
|
||||
type="text"
|
||||
className="input-origin block"
|
||||
value={hipAndGableWidth}
|
||||
onChange={(e) => onlyNumberInputChange(e, setHipAndGableWidth)}
|
||||
onChange={(e) => setHipAndGableWidth(normalizeDigits(e.target.value))}
|
||||
/>
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils'
|
||||
import { normalizeDecimalLimit, normalizeDigits } from '@/util/input-utils'
|
||||
|
||||
export default function Jerkinhead({
|
||||
gableOffset,
|
||||
@ -18,7 +18,7 @@ export default function Jerkinhead({
|
||||
{getMessage('gable.offset')}
|
||||
</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" value={gableOffset} onChange={(e) => onlyNumberInputChange(e, setGableOffset)} />
|
||||
<input type="text" className="input-origin block" value={gableOffset} onChange={(e) => setGableOffset(normalizeDigits(e.target.value))} />
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
@ -27,7 +27,7 @@ export default function Jerkinhead({
|
||||
{getMessage('jerkinhead.width')}
|
||||
</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" value={jerkinHeadWidth} onChange={(e) => onlyNumberInputChange(e, setJerkinHeadWidth)} />
|
||||
<input type="text" className="input-origin block" value={jerkinHeadWidth} onChange={(e) => setJerkinHeadWidth(normalizeDigits(e.target.value))} />
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
@ -40,7 +40,7 @@ export default function Jerkinhead({
|
||||
type="text"
|
||||
className="input-origin block"
|
||||
value={jerkinHeadPitch}
|
||||
onChange={(e) => onlyNumberWithDotInputChange(e, setJerkinHeadPitch)}
|
||||
onChange={(e) => setJerkinHeadPitch(normalizeDecimalLimit(e.target.value, 2))}
|
||||
/>
|
||||
</div>
|
||||
<span className="thin">{pitchText}</span>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils'
|
||||
import { normalizeDecimalLimit, normalizeDigits } from '@/util/input-utils'
|
||||
|
||||
export default function Shed({ shedWidth, setShedWidth, shedPitch, setShedPitch, pitchText }) {
|
||||
const { getMessage } = useMessage()
|
||||
@ -8,14 +8,14 @@ export default function Shed({ shedWidth, setShedWidth, shedPitch, setShedPitch,
|
||||
<div className="outline-form mb10">
|
||||
<span className="mr10">{getMessage('slope')}</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" value={shedPitch} onChange={(e) => onlyNumberWithDotInputChange(e, setShedPitch)} />
|
||||
<input type="text" className="input-origin block" value={shedPitch} onChange={(e) => setShedPitch(normalizeDecimalLimit(e.target.value, 2))} />
|
||||
</div>
|
||||
<span className="thin">{pitchText}</span>
|
||||
</div>
|
||||
<div className="outline-form">
|
||||
<span className="mr10">{getMessage('shed.width')}</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" value={shedWidth} onChange={(e) => onlyNumberInputChange(e, setShedWidth)} />
|
||||
<input type="text" className="input-origin block" value={shedWidth} onChange={(e) => setShedWidth(normalizeDigits(e.target.value))} />
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { useState } from 'react'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { onlyNumberInputChange } from '@/util/input-utils'
|
||||
import { normalizeDigits } from '@/util/input-utils'
|
||||
|
||||
export default function Wall({ sleeveOffset, setSleeveOffset, hasSleeve, setHasSleeve }) {
|
||||
const { getMessage } = useMessage()
|
||||
@ -29,7 +29,7 @@ export default function Wall({ sleeveOffset, setSleeveOffset, hasSleeve, setHasS
|
||||
type="text"
|
||||
className="input-origin block"
|
||||
value={sleeveOffset}
|
||||
onChange={(e) => onlyNumberInputChange(e, setSleeveOffset)}
|
||||
onChange={(e) => setSleeveOffset(normalizeDigits(e.target.value))}
|
||||
readOnly={hasSleeve === '0'}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -5,7 +5,7 @@ import { usePopup } from '@/hooks/usePopup'
|
||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||
import { canvasState } from '@/store/canvasAtom'
|
||||
import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
|
||||
import { onlyNumberInputChange } from '@/util/input-utils'
|
||||
import { normalizeDigits } from '@/util/input-utils'
|
||||
|
||||
export default function PlanSizeSetting(props) {
|
||||
const { setIsShow, horizon, vertical, id, pos = { x: 985, y: 180 }, settingsData, setSettingsData, settingsDataSave, setSettingsDataSave } = props
|
||||
@ -24,15 +24,15 @@ export default function PlanSizeSetting(props) {
|
||||
setPlanSizeSettingMode((prev) => {
|
||||
return {
|
||||
...prev,
|
||||
originHorizon: Number(planSizeSettingMode.originHorizon),
|
||||
originVertical: Number(planSizeSettingMode.originVertical),
|
||||
originHorizon: Number(normalizeDigits(planSizeSettingMode.originHorizon)),
|
||||
originVertical: Number(normalizeDigits(planSizeSettingMode.originVertical)),
|
||||
}
|
||||
})
|
||||
|
||||
setSettingsData({
|
||||
...settingsData,
|
||||
originHorizon: Number(planSizeSettingMode.originHorizon),
|
||||
originVertical: Number(planSizeSettingMode.originVertical),
|
||||
originHorizon: Number(normalizeDigits(planSizeSettingMode.originHorizon)),
|
||||
originVertical: Number(normalizeDigits(planSizeSettingMode.originVertical)),
|
||||
})
|
||||
|
||||
canvas.setWidth(planSizeSettingMode.originHorizon)
|
||||
@ -46,14 +46,15 @@ export default function PlanSizeSetting(props) {
|
||||
const changeInput = (value, e) => {
|
||||
const { name } = e.target
|
||||
|
||||
if (Number(value) > 100000) {
|
||||
value = 100000
|
||||
let n = Number(normalizeDigits(value))
|
||||
if (n > 100000) {
|
||||
n = 100000
|
||||
}
|
||||
|
||||
setPlanSizeSettingMode((prev) => {
|
||||
return {
|
||||
...prev,
|
||||
[name]: Number(value) / 10,
|
||||
[name]: n / 10,
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -77,7 +78,7 @@ export default function PlanSizeSetting(props) {
|
||||
className="input-origin block"
|
||||
name={`originHorizon`}
|
||||
value={planSizeSettingMode.originHorizon * 10}
|
||||
onChange={(e) => onlyNumberInputChange(e, changeInput)}
|
||||
onChange={(e) => changeInput(normalizeDigits(e.target.value), e)}
|
||||
/>
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
@ -90,7 +91,7 @@ export default function PlanSizeSetting(props) {
|
||||
className="input-origin block"
|
||||
name={`originVertical`}
|
||||
value={planSizeSettingMode.originVertical * 10}
|
||||
onChange={(e) => onlyNumberInputChange(e, changeInput)}
|
||||
onChange={(e) => changeInput(normalizeDigits(e.target.value), e)}
|
||||
/>
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
|
||||
@ -22,6 +22,7 @@ import { stuffSearchState } from '@/store/stuffAtom'
|
||||
import { QcastContext } from '@/app/QcastProvider'
|
||||
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
|
||||
import { useSwal } from '@/hooks/useSwal'
|
||||
import { sanitizeIntegerInputEvent } from '@/util/input-utils'
|
||||
|
||||
export default function StuffDetail() {
|
||||
const [stuffSearch, setStuffSearch] = useRecoilState(stuffSearchState)
|
||||
@ -1239,10 +1240,10 @@ export default function StuffDetail() {
|
||||
}
|
||||
|
||||
//주소
|
||||
if (!formData.address) {
|
||||
fieldNm = getMessage('stuff.detail.address')
|
||||
errors = fieldNm
|
||||
}
|
||||
// if (!formData.address) {
|
||||
// fieldNm = getMessage('stuff.detail.address')
|
||||
// errors = fieldNm
|
||||
// }
|
||||
|
||||
//도도부현
|
||||
if (!formData.prefId || formData.prefId === '0') {
|
||||
@ -1634,13 +1635,15 @@ export default function StuffDetail() {
|
||||
|
||||
// 숫자만 입력 가능
|
||||
const handleKeyUp = (e) => {
|
||||
let input = e.target
|
||||
input.value = input.value.replace(/[^0-9]/g, '')
|
||||
// let input = e.target
|
||||
// input.value = input.value.replace(/[^0-9]/g, '')
|
||||
sanitizeIntegerInputEvent(e)
|
||||
}
|
||||
|
||||
const handleBlur = (e) => {
|
||||
let input = e.target
|
||||
input.value = input.value.replace(/[^0-9]/g, '')
|
||||
// let input = e.target
|
||||
// input.value = input.value.replace(/[^0-9]/g, '')
|
||||
sanitizeIntegerInputEvent(e)
|
||||
}
|
||||
|
||||
// 그리드 더블 클릭 해당플랜의 도면작성 화면으로 이동
|
||||
|
||||
@ -9,6 +9,7 @@ import { useMessage } from '@/hooks/useMessage'
|
||||
import { isNotEmptyArray } from '@/util/common-utils'
|
||||
import { useSwal } from '@/hooks/useSwal'
|
||||
import { QcastContext } from '@/app/QcastProvider'
|
||||
import { sanitizeIntegerInputEvent } from '@/util/input-utils'
|
||||
export default function FindAddressPop(props) {
|
||||
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
||||
|
||||
@ -113,12 +114,12 @@ export default function FindAddressPop(props) {
|
||||
|
||||
//숫자만
|
||||
const handleKeyUp = (e) => {
|
||||
let input = e.target
|
||||
input.value = input.value.replace(/[^0-9]/g, '')
|
||||
|
||||
if (e.key === 'Enter') {
|
||||
searchPostNum()
|
||||
}
|
||||
// let input = e.target
|
||||
// input.value = input.value.replace(/[^0-9]/g, '')
|
||||
// if (e.key === 'Enter') {
|
||||
// searchPostNum()
|
||||
// }
|
||||
sanitizeIntegerInputEvent(e)
|
||||
}
|
||||
|
||||
//그리드에서 선택한 우편정보
|
||||
|
||||
@ -11,6 +11,8 @@ import { isObjectNotEmpty, queryStringFormatter } from '@/util/common-utils'
|
||||
import QPagination from '@/components/common/pagination/QPagination'
|
||||
import { useSwal } from '@/hooks/useSwal'
|
||||
import { QcastContext } from '@/app/QcastProvider'
|
||||
import { sanitizeDecimalInputEvent } from '@/util/input-utils'
|
||||
|
||||
export default function PlanRequestPop(props) {
|
||||
const [pageNo, setPageNo] = useState(1) //현재 페이지 번호
|
||||
const [pageSize, setPageSize] = useState(20) //페이지 당 게시물 개수
|
||||
@ -233,16 +235,16 @@ export default function PlanRequestPop(props) {
|
||||
|
||||
// 숫자만 입력 가능
|
||||
const handleKeyUp = (e) => {
|
||||
let input = e.target
|
||||
input.value = input.value.replace(/[^0-9]/g, '')
|
||||
sanitizeIntegerInputEvent(e)
|
||||
if (e.key === 'Enter') {
|
||||
onSubmit(pageNo, 'S')
|
||||
}
|
||||
}
|
||||
|
||||
const handleBlur = (e) => {
|
||||
let input = e.target
|
||||
input.value = input.value.replace(/[^0-9]/g, '')
|
||||
// let input = e.target
|
||||
// input.value = input.value.replace(/[^0-9]/g, '')
|
||||
sanitizeIntegerInputEvent(e)
|
||||
}
|
||||
|
||||
// 엔터 이벤트
|
||||
|
||||
@ -101,7 +101,7 @@ export function useCanvasPopupStatusController(param = 1) {
|
||||
popupType: popupType.toString(),
|
||||
// popupStatus: popupType === 1 ? arg : JSON.stringify(arg).replace(/"/g, '\"'),
|
||||
popupStatus: JSON.stringify(arg).replace(/"/g, '\"'),
|
||||
hajebichi: arg.roofConstructions?.[0]?.addRoof?.hajebichi || '',
|
||||
//hajebichi: arg.roofConstructions?.[0]?.addRoof?.hajebichi || '',
|
||||
}
|
||||
postFetcher(`/api/v1/canvas-popup-status`, params)
|
||||
},
|
||||
|
||||
@ -1,15 +1,18 @@
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { canvasState, dotLineGridSettingState } from '@/store/canvasAtom'
|
||||
import { canvasState, canvasZoomState, dotLineGridSettingState } from '@/store/canvasAtom'
|
||||
import { useEffect } from 'react'
|
||||
import { gridColorState } from '@/store/gridAtom'
|
||||
import { gridDisplaySelector } from '@/store/settingAtom'
|
||||
|
||||
const GRID_PADDING = 5
|
||||
|
||||
export function useGrid() {
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
|
||||
const dotLineGridSetting = useRecoilValue(dotLineGridSettingState)
|
||||
const gridColor = useRecoilValue(gridColorState)
|
||||
const isGridDisplay = useRecoilValue(gridDisplaySelector)
|
||||
const zoom = useRecoilValue(canvasZoomState)
|
||||
|
||||
useEffect(() => {
|
||||
if (!canvas) {
|
||||
@ -90,14 +93,32 @@ export function useGrid() {
|
||||
}
|
||||
|
||||
if (patternData.lineGridDisplay) {
|
||||
for (let i = 0; i < 5000 / patternData.gridVertical + 1; i++) {
|
||||
// 캔버스의 실제 보이는 영역 계산
|
||||
const canvasWidth = canvas.getWidth()
|
||||
const canvasHeight = canvas.getHeight()
|
||||
const currentZoom = canvas.getZoom()
|
||||
const viewportTransform = canvas.viewportTransform
|
||||
|
||||
const visibleLeft = -viewportTransform[4] / currentZoom
|
||||
const visibleTop = -viewportTransform[5] / currentZoom
|
||||
const visibleRight = visibleLeft + canvasWidth / currentZoom
|
||||
const visibleBottom = visibleTop + canvasHeight / currentZoom
|
||||
|
||||
// 여유 공간 추가
|
||||
const padding = 200
|
||||
const gridLeft = visibleLeft - padding
|
||||
const gridTop = visibleTop - padding
|
||||
const gridRight = visibleRight + padding
|
||||
const gridBottom = visibleBottom + padding
|
||||
|
||||
// 가로선 생성 (수평선)
|
||||
const horizontalGridRange = gridBottom - gridTop
|
||||
const horizontalGridCount = Math.ceil(horizontalGridRange / patternData.gridVertical) + 2
|
||||
|
||||
for (let i = 0; i < horizontalGridCount; i++) {
|
||||
const y = gridTop + i * patternData.gridVertical
|
||||
const horizontalLine = new fabric.Line(
|
||||
[
|
||||
-1500,
|
||||
-1500 + i * patternData.gridVertical - patternData.gridVertical / 2,
|
||||
3000,
|
||||
-1500 + i * patternData.gridVertical - patternData.gridVertical / 2,
|
||||
],
|
||||
[gridLeft, y, gridRight, y],
|
||||
{
|
||||
stroke: gridColor,
|
||||
strokeWidth: 1,
|
||||
@ -118,14 +139,14 @@ export function useGrid() {
|
||||
canvas.add(horizontalLine)
|
||||
}
|
||||
|
||||
for (let i = 0; i < 5000 / patternData.gridHorizon + 1; i++) {
|
||||
// 세로선 생성 (수직선)
|
||||
const verticalGridRange = gridRight - gridLeft
|
||||
const verticalGridCount = Math.ceil(verticalGridRange / patternData.gridHorizon) + 2
|
||||
|
||||
for (let i = 0; i < verticalGridCount; i++) {
|
||||
const x = gridLeft + i * patternData.gridHorizon
|
||||
const verticalLine = new fabric.Line(
|
||||
[
|
||||
-1500 + i * patternData.gridHorizon - patternData.gridHorizon / 2,
|
||||
-1500,
|
||||
-1500 + i * patternData.gridHorizon - patternData.gridHorizon / 2,
|
||||
3000,
|
||||
],
|
||||
[x, gridTop, x, gridBottom],
|
||||
{
|
||||
stroke: gridColor,
|
||||
strokeWidth: 1,
|
||||
@ -148,7 +169,7 @@ export function useGrid() {
|
||||
}
|
||||
|
||||
canvas.renderAll()
|
||||
}, [dotLineGridSetting])
|
||||
}, [dotLineGridSetting, zoom])
|
||||
|
||||
const move = (object, x, y) => {
|
||||
object.set({
|
||||
|
||||
@ -1804,7 +1804,7 @@ export const useTrestle = () => {
|
||||
const bracket = new fabric.Rect({
|
||||
left: startPointX,
|
||||
top: startPointY,
|
||||
fill: 'green',
|
||||
fill: i === 0 || i === count - 1 ? '#D70065' : 'green',
|
||||
name: TRESTLE_MATERIAL.BRACKET,
|
||||
parentId: module.id,
|
||||
surfaceId: module.surfaceId,
|
||||
|
||||
@ -1,29 +1,29 @@
|
||||
import { useEffect, useState, useRef, useContext } from 'react'
|
||||
import { useContext, useEffect, useState } from 'react'
|
||||
import { useRecoilState, useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil'
|
||||
import {
|
||||
adsorptionPointModeState,
|
||||
adsorptionRangeState,
|
||||
canvasState,
|
||||
planSizeSettingState,
|
||||
dotLineGridSettingState,
|
||||
canvasSettingState,
|
||||
canvasState,
|
||||
currentMenuState,
|
||||
dotLineGridSettingState,
|
||||
planSizeSettingState,
|
||||
} from '@/store/canvasAtom'
|
||||
import { globalLocaleStore } from '@/store/localeAtom'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
import { useSwal } from '@/hooks/useSwal'
|
||||
import {
|
||||
addedRoofsState,
|
||||
basicSettingState,
|
||||
correntObjectNoState,
|
||||
corridorDimensionSelector,
|
||||
settingModalFirstOptionsState,
|
||||
settingModalSecondOptionsState,
|
||||
settingModalGridOptionsState,
|
||||
basicSettingState,
|
||||
fetchRoofMaterialsState,
|
||||
roofMaterialsAtom,
|
||||
selectedRoofMaterialSelector,
|
||||
addedRoofsState,
|
||||
fetchRoofMaterialsState,
|
||||
settingModalFirstOptionsState,
|
||||
settingModalGridOptionsState,
|
||||
settingModalSecondOptionsState,
|
||||
} from '@/store/settingAtom'
|
||||
import { MENU, POLYGON_TYPE } from '@/common/common'
|
||||
import { globalFontAtom } from '@/store/fontAtom'
|
||||
@ -339,11 +339,11 @@ export function useCanvasSetting(executeEffect = true) {
|
||||
*/
|
||||
const fetchBasicSettings = async (planNo, openPoint) => {
|
||||
// 지붕재 데이터가 없으면 먼저 로드
|
||||
let materials = roofMaterials;
|
||||
let materials = roofMaterials
|
||||
if (!materials || materials.length === 0) {
|
||||
logger.log("Waiting for roofMaterials to be loaded...");
|
||||
materials = await addRoofMaterials();
|
||||
logger.log("roofMaterials loaded:", materials);
|
||||
logger.log('Waiting for roofMaterials to be loaded...')
|
||||
materials = await addRoofMaterials()
|
||||
logger.log('roofMaterials loaded:', materials)
|
||||
}
|
||||
|
||||
try {
|
||||
@ -527,10 +527,16 @@ export function useCanvasSetting(executeEffect = true) {
|
||||
roofGap:
|
||||
params.selectedRoofMaterial.raft === null || params.selectedRoofMaterial.raft === undefined
|
||||
? params.selectedRoofMaterial.raftBaseCd
|
||||
: params.roofsData.raft,
|
||||
: params.roofsData.roofGap,
|
||||
roofLayout: params.roofsData.roofLayout === null || params.roofsData.roofLayout === undefined ? 'P' : params.roofsData.roofLayout,
|
||||
roofPitch: params.roofsData.roofPitch === null || params.roofsData.roofPitch === undefined || params.roofsData.roofPitch === '' ? 0 : params.roofsData.roofPitch,
|
||||
roofAngle: params.roofsData.roofAngle === null || params.roofsData.roofAngle === undefined || params.roofsData.roofAngle === '' ? 0 : params.roofsData.roofAngle,
|
||||
roofPitch:
|
||||
params.roofsData.roofPitch === null || params.roofsData.roofPitch === undefined || params.roofsData.roofPitch === ''
|
||||
? 0
|
||||
: params.roofsData.roofPitch,
|
||||
roofAngle:
|
||||
params.roofsData.roofAngle === null || params.roofsData.roofAngle === undefined || params.roofsData.roofAngle === ''
|
||||
? 0
|
||||
: params.roofsData.roofAngle,
|
||||
},
|
||||
],
|
||||
}
|
||||
@ -643,7 +649,11 @@ export function useCanvasSetting(executeEffect = true) {
|
||||
setDimensionLineSettings({ ...dimensionLineSettings, pixel: res.originPixel, color: res.originColor })
|
||||
|
||||
/** 도면크기 설정 */
|
||||
setPlanSizeSettingMode({ ...planSizeSettingMode, originHorizon: res.originHorizon, originVertical: res.originVertical })
|
||||
setPlanSizeSettingMode({
|
||||
...planSizeSettingMode,
|
||||
originHorizon: res.originHorizon,
|
||||
originVertical: res.originVertical,
|
||||
})
|
||||
canvas.setWidth(res.originHorizon)
|
||||
canvas.setHeight(res.originVertical)
|
||||
canvas.renderAll()
|
||||
@ -723,7 +733,7 @@ export function useCanvasSetting(executeEffect = true) {
|
||||
/** 조회된 글꼴 데이터가 없는 경우 (데이터 초기화) */
|
||||
|
||||
/** 흡착점 ON/OFF */
|
||||
setAdsorptionPointMode(false)
|
||||
setAdsorptionPointMode(true)
|
||||
|
||||
/** 치수선 설정 */
|
||||
resetDimensionLineSettings()
|
||||
|
||||
@ -1,20 +1,10 @@
|
||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
|
||||
import {
|
||||
adsorptionPointAddModeState,
|
||||
adsorptionPointModeState,
|
||||
adsorptionRangeState,
|
||||
canvasSettingState,
|
||||
canvasState,
|
||||
dotLineIntervalSelector,
|
||||
globalPitchState,
|
||||
verticalHorizontalModeState,
|
||||
} from '@/store/canvasAtom'
|
||||
import { adsorptionPointModeState, adsorptionRangeState, canvasState, globalPitchState, verticalHorizontalModeState } from '@/store/canvasAtom'
|
||||
import { useEvent } from '@/hooks/useEvent'
|
||||
import { useMouse } from '@/hooks/useMouse'
|
||||
import { useLine } from '@/hooks/useLine'
|
||||
import { useTempGrid } from '@/hooks/useTempGrid'
|
||||
import { useEffect, useRef } from 'react'
|
||||
import { calculateIntersection, distanceBetweenPoints, findClosestPoint } from '@/util/canvas-util'
|
||||
import { distanceBetweenPoints } from '@/util/canvas-util'
|
||||
import { fabric } from 'fabric'
|
||||
import { calculateAngle } from '@/util/qpolygon-utils'
|
||||
import {
|
||||
@ -36,8 +26,6 @@ import { useSurfaceShapeBatch } from './useSurfaceShapeBatch'
|
||||
|
||||
import { roofDisplaySelector } from '@/store/settingAtom'
|
||||
import { useRoofFn } from '@/hooks/common/useRoofFn'
|
||||
import PlacementSurfaceLineProperty from '@/components/floor-plan/modal/placementShape/PlacementSurfaceLineProperty'
|
||||
import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint'
|
||||
import { useObject } from '@/hooks/useObject'
|
||||
|
||||
// 배치면 그리기
|
||||
@ -894,7 +882,7 @@ export function usePlacementShapeDrawing(id) {
|
||||
|
||||
const firstPoint = points[0]
|
||||
|
||||
points.forEach((point, idx) => {
|
||||
/*points.forEach((point, idx) => {
|
||||
if (idx === 0 || !isAllRightAngle) {
|
||||
return
|
||||
}
|
||||
@ -908,7 +896,7 @@ export function usePlacementShapeDrawing(id) {
|
||||
if (isAllRightAngle) {
|
||||
// alert('부정확한 다각형입니다.')
|
||||
return
|
||||
}
|
||||
}*/
|
||||
|
||||
setPoints((prev) => {
|
||||
return [...prev, { x: prev[0].x, y: prev[0].y }]
|
||||
|
||||
@ -26,6 +26,7 @@ export function useCanvas(id) {
|
||||
const isImageDisplay = useRecoilValue(imageDisplaySelector)
|
||||
const {} = useFont()
|
||||
const resetCanvasZoom = useResetRecoilState(canvasZoomState)
|
||||
const zoom = useRecoilValue(canvasZoomState)
|
||||
|
||||
/**
|
||||
* 처음 셋팅
|
||||
@ -50,8 +51,53 @@ export function useCanvas(id) {
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
// canvas 사이즈가 변경되면 다시
|
||||
}, [canvasSize])
|
||||
// zoom 상태가 변경될 때 tempGrid 라인들의 크기를 캔버스에 맞게 조정
|
||||
if (canvas) {
|
||||
adjustTempGridLines()
|
||||
}
|
||||
}, [zoom])
|
||||
|
||||
const adjustTempGridLines = () => {
|
||||
if (!canvas) return
|
||||
|
||||
const canvasWidth = canvas.getWidth()
|
||||
const canvasHeight = canvas.getHeight()
|
||||
const currentZoom = canvas.getZoom()
|
||||
const viewportTransform = canvas.viewportTransform
|
||||
|
||||
// 실제 보이는 캔버스 영역 계산 (zoom과 pan 고려)
|
||||
const visibleLeft = -viewportTransform[4] / currentZoom
|
||||
const visibleTop = -viewportTransform[5] / currentZoom
|
||||
const visibleRight = visibleLeft + canvasWidth / currentZoom
|
||||
const visibleBottom = visibleTop + canvasHeight / currentZoom
|
||||
|
||||
// tempGrid 라인들을 찾아서 크기 조정
|
||||
const tempGridLines = canvas.getObjects().filter((obj) => ['tempGrid', 'lineGrid', 'mouseLine'].includes(obj.name))
|
||||
|
||||
tempGridLines.forEach((line) => {
|
||||
if (line.direction === 'vertical') {
|
||||
// 세로 라인: y축을 캔버스 전체 높이로 설정
|
||||
line.set({
|
||||
x1: line.x1,
|
||||
y1: visibleTop - 100, // 여유 공간 추가
|
||||
x2: line.x1,
|
||||
y2: visibleBottom + 100, // 여유 공간 추가
|
||||
})
|
||||
} else if (line.direction === 'horizontal') {
|
||||
// 가로 라인: x축을 캔버스 전체 너비로 설정
|
||||
line.set({
|
||||
x1: visibleLeft - 100, // 여유 공간 추가
|
||||
y1: line.y1,
|
||||
x2: visibleRight + 100, // 여유 공간 추가
|
||||
y2: line.y1,
|
||||
})
|
||||
}
|
||||
|
||||
line.setCoords()
|
||||
})
|
||||
|
||||
canvas.renderAll()
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
canvas
|
||||
|
||||
@ -369,18 +369,39 @@ export function useEvent() {
|
||||
}
|
||||
|
||||
const drawMouseLine = (pointer) => {
|
||||
const horizontalLine = new fabric.Line([-2 * canvas.width, pointer.y, 2 * canvas.width, pointer.y], {
|
||||
// 캔버스의 실제 보이는 영역 계산 (zoom과 pan 고려)
|
||||
const canvasWidth = canvas.getWidth()
|
||||
const canvasHeight = canvas.getHeight()
|
||||
const currentZoom = canvas.getZoom()
|
||||
const viewportTransform = canvas.viewportTransform
|
||||
|
||||
const visibleLeft = -viewportTransform[4] / currentZoom
|
||||
const visibleTop = -viewportTransform[5] / currentZoom
|
||||
const visibleRight = visibleLeft + canvasWidth / currentZoom
|
||||
const visibleBottom = visibleTop + canvasHeight / currentZoom
|
||||
|
||||
// 여유 공간 추가
|
||||
const padding = 200
|
||||
const lineLeft = visibleLeft - padding
|
||||
const lineTop = visibleTop - padding
|
||||
const lineRight = visibleRight + padding
|
||||
const lineBottom = visibleBottom + padding
|
||||
|
||||
// 가로선 (수평선)
|
||||
const horizontalLine = new fabric.Line([lineLeft, pointer.y, lineRight, pointer.y], {
|
||||
stroke: 'red',
|
||||
strokeWidth: 1,
|
||||
selectable: false,
|
||||
direction: 'horizontal',
|
||||
name: 'mouseLine',
|
||||
})
|
||||
|
||||
// 세로선
|
||||
const verticalLine = new fabric.Line([pointer.x, -2 * canvas.height, pointer.x, 2 * canvas.height], {
|
||||
// 세로선 (수직선)
|
||||
const verticalLine = new fabric.Line([pointer.x, lineTop, pointer.x, lineBottom], {
|
||||
stroke: 'red',
|
||||
strokeWidth: 1,
|
||||
selectable: false,
|
||||
direction: 'vertical',
|
||||
name: 'mouseLine',
|
||||
})
|
||||
|
||||
|
||||
@ -230,28 +230,28 @@ export const usePolygon = () => {
|
||||
case 'south':
|
||||
// lines중 가장 아래에 있는 라인을 찾는다.
|
||||
const line = lines.reduce((acc, cur) => {
|
||||
return acc.y2 > cur.y2 ? acc : cur
|
||||
return acc.y2 + acc.y1 > cur.y2 + cur.y1 ? acc : cur
|
||||
}, lines[0])
|
||||
centerPoint = { x: (line.x2 + line.x1) / 2, y: Math.max(line.y1, line.y2) }
|
||||
break
|
||||
case 'north':
|
||||
// lines중 가장 위에 있는 라인을 찾는다.
|
||||
const line2 = lines.reduce((acc, cur) => {
|
||||
return acc.y2 < cur.y2 ? acc : cur
|
||||
return acc.y2 + acc.y1 < cur.y2 + cur.y1 ? acc : cur
|
||||
}, lines[0])
|
||||
centerPoint = { x: (line2.x2 + line2.x1) / 2, y: Math.min(line2.y1, line2.y2) }
|
||||
break
|
||||
case 'west':
|
||||
// lines중 가장 왼쪽에 있는 라인을 찾는다.
|
||||
const line3 = lines.reduce((acc, cur) => {
|
||||
return acc.x2 < cur.x2 ? acc : cur
|
||||
return acc.x2 + acc.x1 < cur.x2 + cur.x1 ? acc : cur
|
||||
}, lines[0])
|
||||
centerPoint = { x: Math.min(line3.x1, line3.x2), y: (line3.y1 + line3.y2) / 2 }
|
||||
break
|
||||
case 'east':
|
||||
// lines중 가장 오른쪽에 있는 라인을 찾는다.
|
||||
const line4 = lines.reduce((acc, cur) => {
|
||||
return acc.x2 > cur.x2 ? acc : cur
|
||||
return acc.x2 + acc.x1 > cur.x2 + cur.x1 ? acc : cur
|
||||
}, lines[0])
|
||||
centerPoint = { x: Math.max(line4.x1, line4.x2), y: (line4.y1 + line4.y2) / 2 }
|
||||
break
|
||||
|
||||
@ -1,20 +1,183 @@
|
||||
// 숫자만 입력 가능한 input onChange 함수
|
||||
export const onlyNumberInputChange = (e, callback) => {
|
||||
let value = e.target.value
|
||||
value = value.replace(/[^-0-9]/g, '')
|
||||
callback(value, e)
|
||||
// 간단한 IME 감지 함수
|
||||
function isIMEComposing(e) {
|
||||
// compositionstart ~ compositionend 사이의 입력은 IME 조합 중
|
||||
return e.nativeEvent?.isComposing || e.isComposing || false
|
||||
}
|
||||
|
||||
//소수점 둘째자리 숫자만 입력가능
|
||||
export const onlyNumberWithDotInputChange = (e, callback) => {
|
||||
const val = e.target.value
|
||||
|
||||
const pattern = /^-?(\d{1,4}([.]\d{0,2})?)?$/
|
||||
if (!pattern.test(val)) {
|
||||
// prev에서 마지막 자리 제거
|
||||
callback(val.slice(0, val.length - 1), e)
|
||||
// 숫자만 입력 가능한 input onChange 함수 (음수 포함)
|
||||
export const onlyNumberInputChange = (e, callback) => {
|
||||
// IME 조합 중이면 그대로 전달
|
||||
if (isIMEComposing(e)) {
|
||||
callback(e.target.value, e)
|
||||
return
|
||||
}
|
||||
|
||||
callback(val, e)
|
||||
let value = e.target.value
|
||||
value = value.replace(/[^-0-9]/g, '')
|
||||
// 음수 기호는 맨 앞에만 허용
|
||||
if (value.indexOf('-') > 0) {
|
||||
value = value.replace(/-/g, '')
|
||||
}
|
||||
// 연속된 음수 기호 제거
|
||||
value = value.replace(/^-+/, '-')
|
||||
callback(value, e)
|
||||
}
|
||||
|
||||
// 소수점 둘째자리 숫자만 입력가능 (음수 포함, 개선된 로직)
|
||||
export const onlyNumberWithDotInputChange = (e, callback) => {
|
||||
// IME 조합 중이면 그대로 전달
|
||||
if (isIMEComposing(e)) {
|
||||
callback(e.target.value, e)
|
||||
return
|
||||
}
|
||||
|
||||
const val = e.target.value
|
||||
|
||||
// 음수를 포함한 소수점 패턴 (최대 4자리 정수, 2자리 소수)
|
||||
const pattern = /^-?(\d{0,4}([.]\d{0,2})?)?$/
|
||||
|
||||
if (!pattern.test(val)) {
|
||||
// 패턴에 맞지 않으면 마지막 입력 문자 제거
|
||||
const correctedValue = val.slice(0, val.length - 1)
|
||||
callback(correctedValue, e)
|
||||
return
|
||||
}
|
||||
|
||||
// 음수 기호가 중간에 있으면 제거
|
||||
let correctedValue = val
|
||||
if (val.indexOf('-') > 0) {
|
||||
correctedValue = val.replace(/-/g, '')
|
||||
}
|
||||
|
||||
callback(correctedValue, e)
|
||||
}
|
||||
|
||||
// =============================
|
||||
// Number normalization utilities
|
||||
// =============================
|
||||
|
||||
// 1) Normalize any string to NFKC and keep only ASCII digits 0-9.
|
||||
export function normalizeDigits(value, allowNegative = false) {
|
||||
// 1. 전각 숫자를 반각으로 변환
|
||||
const halfWidth = fullToHalf(String(value ?? ''));
|
||||
// 2. NFKC 정규화
|
||||
const normalized = halfWidth.normalize('NFKC');
|
||||
|
||||
if (allowNegative) {
|
||||
// 음수 허용 시
|
||||
let result = normalized.replace(/[^-0-9]/g, '');
|
||||
// 음수 기호는 맨 앞에만 허용
|
||||
if (result.indexOf('-') > 0) {
|
||||
result = result.replace(/-/g, '');
|
||||
}
|
||||
// 연속된 음수 기호 제거
|
||||
result = result.replace(/^-+/, '-');
|
||||
return result;
|
||||
} else {
|
||||
// 양수만 허용
|
||||
return normalized.replace(/[^0-9]/g, '');
|
||||
}
|
||||
}
|
||||
|
||||
export function normalizeDecimal(value, allowNegative = false) {
|
||||
// 1. 전각 숫자와 기호를 반각으로 변환
|
||||
const halfWidth = fullToHalf(String(value ?? ''));
|
||||
// 2. NFKC 정규화
|
||||
const normalized = halfWidth.normalize('NFKC');
|
||||
|
||||
let result;
|
||||
|
||||
if (allowNegative) {
|
||||
// 음수와 소수점 허용
|
||||
result = normalized.replace(/[^-0-9.]/g, '');
|
||||
// 음수 기호는 맨 앞에만 허용
|
||||
if (result.indexOf('-') > 0) {
|
||||
result = result.replace(/-/g, '');
|
||||
}
|
||||
// 연속된 음수 기호 제거
|
||||
result = result.replace(/^-+/, '-');
|
||||
} else {
|
||||
// 양수만 허용 (소수점 포함)
|
||||
result = normalized.replace(/[^0-9.]/g, '');
|
||||
}
|
||||
|
||||
// 소수점은 하나만 허용
|
||||
const [head, ...rest] = result.split('.');
|
||||
return rest.length ? `${head}.${rest.join('').replace(/\./g, '')}` : head;
|
||||
}
|
||||
|
||||
// 2-1) Limit fractional digits for decimal numbers. Default to 2 digits.
|
||||
export function normalizeDecimalLimit(value, maxFractionDigits = 2, maxIntegerDigits = null, allowNegative = false) {
|
||||
const s = normalizeDecimal(value, allowNegative)
|
||||
if (!s) return s
|
||||
|
||||
const isNegative = s.startsWith('-')
|
||||
const absoluteValue = isNegative ? s.slice(1) : s
|
||||
const [intPart, fracPart] = absoluteValue.split('.')
|
||||
|
||||
// 정수 부분 자릿수 제한
|
||||
let limitedIntPart = intPart
|
||||
if (maxIntegerDigits && intPart.length > maxIntegerDigits) {
|
||||
limitedIntPart = intPart.slice(0, maxIntegerDigits)
|
||||
}
|
||||
|
||||
// 소수 부분 자릿수 제한
|
||||
let result = limitedIntPart
|
||||
if (fracPart !== undefined) {
|
||||
const limitedFracPart = fracPart.slice(0, Math.max(0, maxFractionDigits))
|
||||
if (limitedFracPart.length > 0) {
|
||||
result = `${limitedIntPart}.${limitedFracPart}`
|
||||
}
|
||||
}
|
||||
|
||||
return isNegative ? `-${result}` : result
|
||||
}
|
||||
|
||||
// 3) DOM input event helpers: mutate target.value and return normalized value
|
||||
export function sanitizeIntegerInputEvent(e, allowNegative = false) {
|
||||
if (!e?.target) return ''
|
||||
|
||||
// IME 조합 중이면 원본 값 반환
|
||||
if (isIMEComposing(e)) {
|
||||
return e.target.value
|
||||
}
|
||||
|
||||
const v = normalizeDigits(e.target.value, allowNegative)
|
||||
e.target.value = v
|
||||
return v
|
||||
}
|
||||
|
||||
export function sanitizeDecimalInputEvent(e, allowNegative = false) {
|
||||
if (!e?.target) return ''
|
||||
|
||||
// IME 조합 중이면 원본 값 반환
|
||||
if (isIMEComposing(e)) {
|
||||
return e.target.value
|
||||
}
|
||||
|
||||
const v = normalizeDecimal(e.target.value, allowNegative)
|
||||
e.target.value = v
|
||||
return v
|
||||
}
|
||||
|
||||
export function sanitizeDecimalLimitInputEvent(e, maxFractionDigits = 2, maxIntegerDigits = null, allowNegative = false) {
|
||||
if (!e?.target) return ''
|
||||
|
||||
// IME 조합 중이면 원본 값 반환
|
||||
if (isIMEComposing(e)) {
|
||||
return e.target.value
|
||||
}
|
||||
|
||||
const v = normalizeDecimalLimit(e.target.value, maxFractionDigits, maxIntegerDigits, allowNegative)
|
||||
e.target.value = v
|
||||
return v
|
||||
}
|
||||
|
||||
export function fullToHalf(str) {
|
||||
if (!str) return '';
|
||||
|
||||
// Convert full-width numbers (0-9) to half-width (0-9)
|
||||
return str.replace(/[0-9]/g, function(s) {
|
||||
return String.fromCharCode(s.charCodeAt(0) - 0xFEE0);
|
||||
});
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user