Compare commits

...

11 Commits

12 changed files with 231 additions and 92 deletions

View File

@ -18,8 +18,9 @@ import Config from '@/config/config.export'
export default function MainPage() {
const [sessionState, setSessionState] = useRecoilState(sessionStore)
const [chagePasswordPopOpen, setChagePasswordPopOpen] = useState(false)
const [changePasswordPopOpen, setChangePasswordPopOpen] = useState(false)
//
const [isSessionLoaded, setIsSessionLoaded] = useState(false)
const router = useRouter()
const { getMessage } = useMessage()
@ -52,6 +53,14 @@ export default function MainPage() {
}
}
useEffect(() => {
if (isObjectNotEmpty(sessionState)) {
if (sessionState?.pwdInitYn !== 'Y') {
setChangePasswordPopOpen(true)
}
}
}, [sessionState])
//
const handleOnChangeRadio = (e) => {
setSearchRadioType(e.target.value)
@ -77,7 +86,7 @@ export default function MainPage() {
useEffect(() => {
if (isObjectNotEmpty(sessionState)) {
if (sessionState?.pwdInitYn !== 'Y') {
setChagePasswordPopOpen(true)
setChangePasswordPopOpen(true)
}
}
}, [sessionState])
@ -86,10 +95,25 @@ export default function MainPage() {
const [open, setOpen] = useState(false)
const [modalNoticeNo, setModalNoticeNo] = useState('')
useEffect(() => {
if (isObjectNotEmpty(sessionState)) {
if (sessionState?.pwdInitYn !== 'Y') {
setChangePasswordPopOpen(true)
} else {
// pwdInitYn 'Y' (false)
setChangePasswordPopOpen(false)
}
}
}, [sessionState])
//if (!isSessionLoaded) return null
return (
<>
{open && <BoardDetailModal noticeNo={modalNoticeNo} setOpen={setOpen} />}
{(!chagePasswordPopOpen && (
{changePasswordPopOpen ? (
<ChangePasswordPop setChangePasswordPopOpen={setChangePasswordPopOpen} />
) : (
<>
<div className="background-bord"></div>
<div className="main-contents">
@ -131,11 +155,8 @@ export default function MainPage() {
<MainContents setFaqOpen={setOpen} setFaqModalNoticeNo={setModalNoticeNo} />
</div>
</>
)) || (
<>
<ChangePasswordPop setChagePasswordPopOpen={setChagePasswordPopOpen} />
</>
)}
</>
)
}

View File

@ -1,4 +1,4 @@
import React, { useState, useRef, useEffect, forwardRef } from 'react'
import React, { useState, useRef, useEffect, forwardRef, useImperativeHandle } from 'react'
import { createCalculator } from '@/util/calc-utils'
import '@/styles/calc.scss'
@ -294,9 +294,11 @@ export const CalculatorInput = forwardRef(
} else {
calculator.currentOperand = filteredValue
setHasOperation(false)
// onChange
onChange(filteredValue)
}
onChange(filteredValue)
//onChange(filteredValue)
}
}

View File

@ -96,7 +96,7 @@ export default function QSelectBox({
title={tagTitle}
>
<p>{selected}</p>
<ul className="select-item-wrap">
<ul className="select-item-wrap" style={{ maxHeight: '200px' }}>
{options?.length > 0 &&
options?.map((option, index) => (
<li key={option.id + '_' + index} className="select-item" onClick={() => handleClickSelectOption(option)}>

View File

@ -181,8 +181,27 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
return fabric.util.transformPoint(p, matrix)
})
this.points = transformedPoints
const { left, top } = this.calcOriginCoords()
this.set('pathOffset', { x: left, y: top })
// 바운딩 박스 재계산 (width, height 업데이트 - fill 영역 수정)
const calcDim = this._calcDimensions({})
this.width = calcDim.width
this.height = calcDim.height
const newPathOffset = {
x: calcDim.left + this.width / 2,
y: calcDim.top + this.height / 2,
}
this.set('pathOffset', newPathOffset)
// 변환을 points에 적용했으므로 left, top, angle, scale 모두 리셋 (이중 변환 방지)
this.set({
left: newPathOffset.x,
top: newPathOffset.y,
angle: 0,
scaleX: 1,
scaleY: 1,
})
this.setCoords()
this.initLines()
})

View File

@ -452,7 +452,16 @@ export const Orientation = forwardRef((props, ref) => {
className="input-origin block"
value={inputCompasDeg}
readOnly={!hasAnglePassivity}
onChange={(value) => setInputCompasDeg(value)}
onChange={(value) => {
// Convert to number and ensure it's within -180 to 180 range
const numValue = parseInt(value, 10);
if (!isNaN(numValue)) {
const clampedValue = Math.min(180, Math.max(-180, numValue));
setInputCompasDeg(String(clampedValue));
} else {
setInputCompasDeg(value);
}
}}
options={{
allowNegative: true,
allowDecimal: false

View File

@ -11,9 +11,44 @@ export default function OuterLineWall({ props }) {
const { length1, setLength1, length1Ref, arrow1, setArrow1 } = props
const resetCalculatorValue = () => {
// 1. 0
setLength1(0);
if (length1Ref.current) {
// 2. input UI
length1Ref.current.focus();
length1Ref.current.value = '';
// 3. UI
setTimeout(() => {
const acButton = document.querySelector('.keypad-btn.ac, .btn-ac') ||
Array.from(document.querySelectorAll('button')).find(el => el.textContent === 'AC');
if (acButton) {
acButton.click();
} else {
// input Enter/Escape
length1Ref.current.dispatchEvent(new Event('input', { bubbles: true }));
}
// ()
length1Ref.current.focus();
}, 10);
}
}
//
useEffect(() => {
const handleKeyDown = (e) => {
if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(e.key)) {
//
setTimeout(() => {
resetCalculatorValue();
}, 0);
return;
}
//
const keypadVisible = document.querySelector('.keypad-container')
@ -88,37 +123,58 @@ export default function OuterLineWall({ props }) {
}}
/>
</div>
<button className="reset-btn" onClick={() => setLength1(0)}></button>
<button
className="reset-btn"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
resetCalculatorValue()
}}
></button>
</div>
<div className="outline-form">
<span>{getMessage('modal.cover.outline.arrow')}</span>
<div className="grid-direction">
<button
className={`direction up ${arrow1 === '↑' ? 'act' : ''}`}
onClick={() => {
onClick={(e) => {
setArrow1('↑')
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' }))
e.preventDefault();
e.stopPropagation();
resetCalculatorValue()
}}
></button>
<button
className={`direction down ${arrow1 === '↓' ? 'act' : ''}`}
onClick={() => {
onClick={(e) => {
setArrow1('↓')
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }))
e.preventDefault();
e.stopPropagation();
resetCalculatorValue()
}}
></button>
<button
className={`direction left ${arrow1 === '←' ? 'act' : ''}`}
onClick={() => {
onClick={(e) => {
setArrow1('←')
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowLeft' }))
e.preventDefault();
e.stopPropagation();
resetCalculatorValue()
}}
></button>
<button
className={`direction right ${arrow1 === '→' ? 'act' : ''}`}
onClick={() => {
onClick={(e) => {
setArrow1('→')
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight' }))
e.preventDefault();
e.stopPropagation();
resetCalculatorValue()
}}
></button>
</div>

View File

@ -114,7 +114,7 @@ export default function ChangePasswordPop(props) {
const result = { ...sessionState, pwdInitYn: 'Y' }
setSession(result)
setSessionState(result)
props.setChagePasswordPopOpen(false)
props.setChangePasswordPopOpen(false)
await login()
},
})

View File

@ -1,7 +1,19 @@
import { useRecoilValue } from 'recoil'
import { canvasState } from '@/store/canvasAtom'
import { fontSelector } from '@/store/fontAtom'
import { useEffect } from 'react'
import { useCallback, useEffect } from 'react'
/** 폰트 타입별 캔버스 오브젝트 이름 매핑 */
const FONT_TYPE_TO_OBJ_NAME = {
commonText: 'commonText',
dimensionLineText: 'dimensionLineText',
flowText: 'flowText',
lengthText: 'lengthText',
circuitNumberText: 'circuitNumber',
}
/** 캔버스 오브젝트 이름 → 폰트 타입 역매핑 */
const OBJ_NAME_TO_FONT_TYPE = Object.fromEntries(Object.entries(FONT_TYPE_TO_OBJ_NAME).map(([k, v]) => [v, k]))
export function useFont() {
const canvas = useRecoilValue(canvasState)
@ -11,85 +23,98 @@ export function useFont() {
const lengthText = useRecoilValue(fontSelector('lengthText'))
const circuitNumberText = useRecoilValue(fontSelector('circuitNumberText'))
/** 폰트 타입별 설정 매핑 */
const fontSettings = {
commonText,
dimensionLineText,
flowText,
lengthText,
circuitNumberText,
}
/**
* 타입별 폰트 설정을 캔버스 오브젝트에 적용하는 공통 함수
* @param {string} type - 폰트 타입 (commonText, dimensionLineText, flowText, lengthText, circuitNumberText)
* @param {number} delay - 적용 지연 시간 (ms), 기본값 200
*/
const changeFontByType = useCallback(
(type, delay = 200) => {
const fontSetting = fontSettings[type]
const objName = FONT_TYPE_TO_OBJ_NAME[type]
if (!fontSetting || !objName) {
console.warn(`Invalid font type: ${type}`)
return
}
setTimeout(() => {
if (canvas && fontSetting.fontWeight?.value) {
const textObjs = canvas.getObjects().filter((obj) => obj.name === objName)
textObjs.forEach((obj) => {
obj.set({
fontFamily: fontSetting.fontFamily.value,
fontWeight: fontSetting.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
fontStyle: fontSetting.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
fontSize: fontSetting.fontSize.value,
fill: fontSetting.fontColor.value,
})
})
canvas.renderAll()
}
}, delay)
},
[canvas, fontSettings],
)
const changeAllFonts = () => {
changeFontByType('commonText')
changeFontByType('dimensionLineText')
changeFontByType('flowText')
changeFontByType('lengthText')
changeFontByType('circuitNumberText')
}
/** 각 폰트 타입별 useEffect */
useEffect(() => {
if (canvas && commonText.fontWeight.value) {
const textObjs = canvas?.getObjects().filter((obj) => obj.name === 'commonText')
textObjs.forEach((obj) => {
obj.set({
fontFamily: commonText.fontFamily.value,
fontWeight: commonText.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
fontStyle: commonText.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
fontSize: commonText.fontSize.value,
fill: commonText.fontColor.value,
})
})
canvas.renderAll()
}
changeFontByType('commonText')
}, [commonText])
useEffect(() => {
if (canvas && dimensionLineText.fontWeight.value) {
const textObjs = canvas?.getObjects().filter((obj) => obj.name === 'dimensionLineText')
textObjs.forEach((obj) => {
obj.set({
fontFamily: dimensionLineText.fontFamily.value,
fontWeight: dimensionLineText.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
fontStyle: dimensionLineText.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
fontSize: dimensionLineText.fontSize.value,
fill: dimensionLineText.fontColor.value,
})
})
canvas.renderAll()
}
changeFontByType('dimensionLineText')
}, [dimensionLineText])
useEffect(() => {
if (canvas && flowText.fontWeight.value) {
const textObjs = canvas?.getObjects().filter((obj) => obj.name === 'flowText')
textObjs.forEach((obj) => {
obj.set({
fontFamily: flowText.fontFamily.value,
fontWeight: flowText.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
fontStyle: flowText.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
fontSize: flowText.fontSize.value,
fill: flowText.fontColor.value,
})
})
canvas.renderAll()
}
changeFontByType('flowText')
}, [flowText])
useEffect(() => {
if (canvas && lengthText.fontWeight.value) {
const textObjs = canvas?.getObjects().filter((obj) => obj.name === 'lengthText')
textObjs.forEach((obj) => {
obj.set({
fontFamily: lengthText.fontFamily.value,
fontWeight: lengthText.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
fontStyle: lengthText.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
fontSize: lengthText.fontSize.value,
fill: lengthText.fontColor.value,
})
})
canvas.renderAll()
}
changeFontByType('lengthText')
}, [lengthText])
useEffect(() => {
if (canvas && circuitNumberText.fontWeight.value) {
const textObjs = canvas?.getObjects().filter((obj) => obj.name === 'circuitNumber')
textObjs.forEach((obj) => {
obj.set({
fontFamily: circuitNumberText.fontFamily.value,
fontWeight: circuitNumberText.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
fontStyle: circuitNumberText.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
fontSize: circuitNumberText.fontSize.value,
fill: circuitNumberText.fontColor.value,
})
})
canvas.renderAll()
}
changeFontByType('circuitNumberText')
}, [circuitNumberText])
return {}
/** 캔버스에 텍스트 오브젝트 추가 시 자동으로 폰트 적용 */
useEffect(() => {
if (!canvas) return
const handleObjectAdded = (e) => {
const obj = e.target
if (!obj?.name) return
const fontType = OBJ_NAME_TO_FONT_TYPE[obj.name]
if (fontType) {
changeFontByType(fontType, 0)
}
}
canvas.on('object:added', handleObjectAdded)
return () => {
canvas.off('object:added', handleObjectAdded)
}
}, [canvas, changeFontByType])
return { changeFontByType, changeAllFonts }
}

View File

@ -819,7 +819,12 @@ export const useTrestle = () => {
// 발전 시뮬레이션 용 각도 재계산
const getAzimuth = (parent) => {
const { moduleCompass, surfaceCompass, direction } = parent
if (typeof parent === 'string') {
console.warn('getAzimuth: parent is string, expected object', parent);
return 0; // 또는 적절한 기본값
}
const { moduleCompass, surfaceCompass, direction } = parent || {};
if (surfaceCompass) {
return -surfaceCompass

View File

@ -812,7 +812,7 @@ export function useOuterLineWall(id, propertiesId) {
if (points.length === 0) {
return
}
enterCheck(e)
// enterCheck(e)
const key = e.key
switch (key) {
case 'Enter': {

View File

@ -815,7 +815,7 @@ export function usePlacementShapeDrawing(id) {
if (points.length === 0) {
return
}
enterCheck(e)
// enterCheck(e)
const key = e.key
switch (key) {
case 'Enter': {

View File

@ -13,6 +13,7 @@ import { basicSettingState } from '@/store/settingAtom'
import { calcLineActualSize } from '@/util/qpolygon-utils'
import { getDegreeByChon } from '@/util/canvas-util'
import { useText } from '@/hooks/useText'
import { fontSelector } from '@/store/fontAtom'
export const useLine = () => {
const canvas = useRecoilValue(canvasState)
@ -23,14 +24,15 @@ export const useLine = () => {
const angleUnit = useRecoilValue(showAngleUnitSelector)
const roofSizeSet = useRecoilValue(basicSettingState).roofSizeSet
const globalPitch = useRecoilValue(globalPitchState)
const lengthText = useRecoilValue(fontSelector('lengthText'))
const { changeCorridorDimensionText } = useText()
const addLine = (points = [], options) => {
const line = new QLine(points, {
...options,
fontSize: fontSize,
fontFamily: fontFamily,
fontSize: lengthText.fontSize.value,
fontFamily: lengthText.fontFamily.value,
})
if (line.length < 1) {