# Conflicts:
#	src/hooks/useContextMenu.js
This commit is contained in:
김민식 2025-01-02 11:12:53 +09:00
commit e0f830b33b
16 changed files with 137 additions and 61 deletions

View File

@ -0,0 +1,24 @@
'use client'
import { createContext, useEffect, useState } from 'react'
import { useLocalStorage } from 'usehooks-ts'
export const GlobalDataContext = createContext({
managementState: {},
setManagementState: () => {},
managementStateLoaded: null,
})
const GlobalDataProvider = ({ children }) => {
const [managementState, setManagementState] = useState({})
// TODO: 임시 조치이며 개발 완료시 삭제 예정 -> 잊지말기...
const [managementStateLoaded, setManagementStateLoaded] = useLocalStorage('managementStateLoaded', null)
useEffect(() => {
setManagementStateLoaded(managementState)
}, [managementState])
return <GlobalDataContext.Provider value={{ managementState, setManagementState, managementStateLoaded }}>{children}</GlobalDataContext.Provider>
}
export default GlobalDataProvider

View File

@ -4,10 +4,11 @@ import { getSession } from '@/lib/authActions'
import RecoilRootWrapper from './RecoilWrapper' import RecoilRootWrapper from './RecoilWrapper'
import { QcastProvider } from './QcastProvider' import { QcastProvider } from './QcastProvider'
import SessionProvider from './SessionProvider'
import GlobalDataProvider from './GlobalDataProvider'
import Header from '@/components/header/Header' import Header from '@/components/header/Header'
import QModal from '@/components/common/modal/QModal' import QModal from '@/components/common/modal/QModal'
import Dimmed from '@/components/ui/Dimmed' import Dimmed from '@/components/ui/Dimmed'
import SessionProvider from './SessionProvider'
import PopupManager from '@/components/common/popupManager/PopupManager' import PopupManager from '@/components/common/popupManager/PopupManager'
import './globals.css' import './globals.css'
@ -60,26 +61,28 @@ export default async function RootLayout({ children }) {
return ( return (
<RecoilRootWrapper> <RecoilRootWrapper>
<html lang="en"> <GlobalDataProvider>
<body> <html lang="en">
{headerPathname === '/login' || headerPathname === '/join' ? ( <body>
<QcastProvider>{children}</QcastProvider> {headerPathname === '/login' || headerPathname === '/join' ? (
) : ( <QcastProvider>{children}</QcastProvider>
<QcastProvider> ) : (
<div className="wrap"> <QcastProvider>
<Header userSession={sessionProps} /> <div className="wrap">
<div className="content"> <Header userSession={sessionProps} />
<Dimmed /> <div className="content">
<SessionProvider useSession={sessionProps}>{children}</SessionProvider> <Dimmed />
<SessionProvider useSession={sessionProps}>{children}</SessionProvider>
</div>
<Footer />
</div> </div>
<Footer /> </QcastProvider>
</div> )}
</QcastProvider> <QModal />
)} <PopupManager />
<QModal /> </body>
<PopupManager /> </html>
</body> </GlobalDataProvider>
</html>
</RecoilRootWrapper> </RecoilRootWrapper>
) )
} }

View File

@ -1,20 +1,18 @@
'ues client' 'ues client'
import { createContext, useEffect, useState } from 'react' import { createContext } from 'react'
export const ManagementContext = createContext({ export const ManagementContext = createContext({})
managementState: {},
setManagementState: () => {},
})
const ManagementProvider = ({ children }) => { const ManagementProvider = ({ children }) => {
const [managementState, setManagementState] = useState({}) // const [managementState, setManagementState] = useState({})
useEffect(() => { // useEffect(() => {
console.log('🚀 ~ managementState:', managementState) // console.log('🚀 ~ managementState:', managementState)
}, [managementState]) // }, [managementState])
return <ManagementContext.Provider value={{ managementState, setManagementState }}>{children}</ManagementContext.Provider> // return <ManagementContext.Provider value={{ managementState, setManagementState }}>{children}</ManagementContext.Provider>
return <ManagementContext.Provider>{children}</ManagementContext.Provider>
} }
export default ManagementProvider export default ManagementProvider

View File

@ -1,6 +1,6 @@
'use client' 'use client'
import { useRef, useState, useEffect } from 'react' import { useRef, useState, useEffect, useContext } from 'react'
import { useRecoilState } from 'recoil' import { useRecoilState } from 'recoil'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { FaAnglesUp } from 'react-icons/fa6' import { FaAnglesUp } from 'react-icons/fa6'
@ -26,6 +26,7 @@ import QPagination from './common/pagination/QPagination'
import { trestleRequestModels, constructionRequestModels, trestleDetailRequestModels } from '@/models/apiModels' import { trestleRequestModels, constructionRequestModels, trestleDetailRequestModels } from '@/models/apiModels'
import QSelectBox from './common/select/QSelectBox' import QSelectBox from './common/select/QSelectBox'
import SampleReducer from './sample/SampleReducer' import SampleReducer from './sample/SampleReducer'
import { GlobalDataContext } from '@/app/GlobalDataProvider'
export default function Playground() { export default function Playground() {
const [useCadFile, setUseCadFile] = useRecoilState(useCadFileState) const [useCadFile, setUseCadFile] = useRecoilState(useCadFileState)
@ -52,6 +53,8 @@ export default function Playground() {
const [users, setUsers] = useState([]) const [users, setUsers] = useState([])
const { managementState, setManagementState, managementStateLoaded } = useContext(GlobalDataContext)
useEffect(() => { useEffect(() => {
console.log('textInput:', textInput) console.log('textInput:', textInput)
}, [textInput]) }, [textInput])
@ -508,6 +511,20 @@ export default function Playground() {
<div className="my-2"> <div className="my-2">
<SampleReducer /> <SampleReducer />
</div> </div>
<div className="my-2">
<Button onClick={() => setManagementState({ ...managementState, objectNo: '1234567890' })}>GlobalDataProvider 테스트</Button>
</div>
<div className="my-2">
<Button onClick={() => setManagementState({})}>GlobalDataProvider 초기화</Button>
</div>
<div className="my-2">
<p>{managementStateLoaded?.objectNo}</p>
</div>
<div className="my-2">
<Button onClick={() => swalFire({ text: 'alert 테스트입니다.', type: 'alert', confirmFn: () => console.log('Alert!!!') })}>
Sweetalert - alert
</Button>
</div>
</div> </div>
</> </>
) )

View File

@ -35,10 +35,6 @@ export const QLine = fabric.util.createClass(fabric.Line, {
this.startPoint = { x: this.x1, y: this.y1 } this.startPoint = { x: this.x1, y: this.y1 }
this.endPoint = { x: this.x2, y: this.y2 } this.endPoint = { x: this.x2, y: this.y2 }
if (canvas) {
this.canvas = canvas
}
}, },
init: function () { init: function () {

View File

@ -79,7 +79,7 @@ export default function CanvasMenu(props) {
const { floorPlanState, setFloorPlanState } = useContext(FloorPlanContext) const { floorPlanState, setFloorPlanState } = useContext(FloorPlanContext)
const { restoreModuleInstArea } = useModuleBasicSetting() const { restoreModuleInstArea } = useModuleBasicSetting()
const addedRoofs = useRecoilValue(addedRoofsState) const [addedRoofs, setAddedRoofsState] = useRecoilState(addedRoofsState)
const [basicSetting, setBasicSetting] = useRecoilState(basicSettingState) const [basicSetting, setBasicSetting] = useRecoilState(basicSettingState)
const selectedRoofMaterial = useRecoilValue(selectedRoofMaterialSelector) const selectedRoofMaterial = useRecoilValue(selectedRoofMaterialSelector)
@ -118,6 +118,16 @@ export default function CanvasMenu(props) {
const changeSelectedRoofMaterial = (e) => { const changeSelectedRoofMaterial = (e) => {
setBasicSetting({ ...basicSetting, selectedRoofMaterial: e }) setBasicSetting({ ...basicSetting, selectedRoofMaterial: e })
const newAddedRoofs = addedRoofs.map((roof) => {
if (roof.index === e.index) {
return { ...roof, selected: true }
} else {
return { ...roof, selected: false }
}
})
setAddedRoofsState(newAddedRoofs)
} }
const settingsModalOptions = useRecoilState(settingModalFirstOptionsState) const settingsModalOptions = useRecoilState(settingModalFirstOptionsState)

View File

@ -238,15 +238,17 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, set
<div className="placement-option"> <div className="placement-option">
<div className="grid-select no-flx" style={{ width: '171px' }}> <div className="grid-select no-flx" style={{ width: '171px' }}>
<QSelectBox <QSelectBox
title={basicSetting.roofSizeSet === '3' ? getMessage('modal.placement.initial.setting.size.none.pitch') : ''}
ref={roofRef.roofCd} ref={roofRef.roofCd}
options={roofMaterials.map((roof, index) => { options={roofMaterials.map((roof, index) => {
return { ...roof, name: globalLocale === 'ko' ? roof.roofMatlNm : roof.roofMatlNmJp } return { ...roof, name: globalLocale === 'ko' ? roof.roofMatlNm : roof.roofMatlNmJp }
})} })}
value={currentRoofMaterial} value={basicSetting.roofSizeSet === '3' ? null : currentRoofMaterial}
onChange={(e) => handleRoofTypeChange(e.roofMatlCd)} onChange={(e) => handleRoofTypeChange(e.roofMatlCd)}
sourceKey="id" sourceKey="id"
targetKey="id" targetKey="id"
showKey="name" showKey="name"
disabled={basicSetting.roofSizeSet === '3'}
/> />
{/* <select {/* <select
className="select-light dark" className="select-light dark"
@ -324,6 +326,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, set
sourceKey="clCode" sourceKey="clCode"
targetKey={currentRoofMaterial.raft ? 'raft' : 'raftBaseCd'} targetKey={currentRoofMaterial.raft ? 'raft' : 'raftBaseCd'}
showKey="clCodeNm" showKey="clCodeNm"
disabled={basicSetting.roofSizeSet === '3'}
/> />
{/* <select className="select-light dark" name="roofGap" ref={roofRef.rafter}> {/* <select className="select-light dark" name="roofGap" ref={roofRef.rafter}>
{raftCodes.map((raft, index) => { {raftCodes.map((raft, index) => {

View File

@ -18,7 +18,8 @@ import WindSelectPop from './popup/WindSelectPop'
import { useCommonCode } from '@/hooks/common/useCommonCode' import { useCommonCode } from '@/hooks/common/useCommonCode'
import StuffPlanQGrid from './StuffPlanQGrid' import StuffPlanQGrid from './StuffPlanQGrid'
import { floorPlanObjectState } from '@/store/floorPlanObjectAtom' import { floorPlanObjectState } from '@/store/floorPlanObjectAtom'
import { ManagementContext } from '@/app/management/ManagementProvider' import { GlobalDataContext } from '@/app/GlobalDataProvider'
// import { ManagementContext } from '@/app/management/ManagementProvider'
import DocDownOptionPop from '../estimate/popup/DocDownOptionPop' import DocDownOptionPop from '../estimate/popup/DocDownOptionPop'
import { stuffSearchState } from '@/store/stuffAtom' import { stuffSearchState } from '@/store/stuffAtom'
import { QcastContext } from '@/app/QcastProvider' import { QcastContext } from '@/app/QcastProvider'
@ -106,7 +107,7 @@ export default function StuffDetail() {
const objectNo = searchParams.get('objectNo') //url set const objectNo = searchParams.get('objectNo') //url set
const [editMode, setEditMode] = useState('NEW') const [editMode, setEditMode] = useState('NEW')
const { managementState, setManagementState } = useContext(ManagementContext) const { managementState, setManagementState } = useContext(GlobalDataContext)
const [planGridProps, setPlanGridProps] = useState({ const [planGridProps, setPlanGridProps] = useState({
planGridData: [], planGridData: [],
isPageable: false, isPageable: false,

View File

@ -3,11 +3,12 @@
import { useContext } from 'react' import { useContext } from 'react'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { ManagementContext } from '@/app/management/ManagementProvider' import { GlobalDataContext } from '@/app/GlobalDataProvider'
// import { ManagementContext } from '@/app/management/ManagementProvider'
export default function StuffHeader() { export default function StuffHeader() {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { managementState } = useContext(ManagementContext) const { managementState } = useContext(GlobalDataContext)
// //
const copyObjectNo = async (objectNo) => { const copyObjectNo = async (objectNo) => {

View File

@ -13,7 +13,8 @@ import { useMessage } from '@/hooks/useMessage'
import { floorPlanObjectState } from '@/store/floorPlanObjectAtom' import { floorPlanObjectState } from '@/store/floorPlanObjectAtom'
import { isObjectNotEmpty, queryStringFormatter } from '@/util/common-utils' import { isObjectNotEmpty, queryStringFormatter } from '@/util/common-utils'
import { ManagementContext } from '@/app/management/ManagementProvider' import { GlobalDataContext } from '@/app/GlobalDataProvider'
// import { ManagementContext } from '@/app/management/ManagementProvider'
import { SessionContext } from '@/app/SessionProvider' import { SessionContext } from '@/app/SessionProvider'
export default function StuffSubHeader({ type }) { export default function StuffSubHeader({ type }) {
@ -25,7 +26,7 @@ export default function StuffSubHeader({ type }) {
const { isGlobalLoading } = useContext(QcastContext) const { isGlobalLoading } = useContext(QcastContext)
const { managementState } = useContext(ManagementContext) const { managementState } = useContext(GlobalDataContext)
const [buttonStyle, setButtonStyle] = useState('') const [buttonStyle, setButtonStyle] = useState('')
useEffect(() => { useEffect(() => {

View File

@ -54,7 +54,7 @@ export default function SampleReducer() {
return t return t
} }
}) })
setSampleState({ ...sampleState, tabs: newTabs }) setSampleState({ tabs: newTabs })
} }
useEffect(() => { useEffect(() => {

View File

@ -1,11 +1,18 @@
import { useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
import { canvasState } from '@/store/canvasAtom' import { canvasState, currentObjectState } from '@/store/canvasAtom'
import { selectedRoofMaterialSelector } from '@/store/settingAtom' import { selectedRoofMaterialSelector } from '@/store/settingAtom'
import { ROOF_MATERIAL_LAYOUT } from '@/components/floor-plan/modal/placementShape/PlacementShapeSetting' import { ROOF_MATERIAL_LAYOUT } from '@/components/floor-plan/modal/placementShape/PlacementShapeSetting'
import { POLYGON_TYPE } from '@/common/common'
const ROOF_COLOR = {
0: 'rgb(199,240,213)',
1: 'rgb(178,238,255)',
2: 'rgb(187,204,255)',
3: 'rgb(228,202,255)',
}
export function useRoofFn() { export function useRoofFn() {
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
const selectedRoofMaterial = useRecoilValue(selectedRoofMaterialSelector) const selectedRoofMaterial = useRecoilValue(selectedRoofMaterialSelector)
const currentObject = useRecoilValue(currentObjectState)
//면형상 선택 클릭시 지붕 패턴 입히기 //면형상 선택 클릭시 지붕 패턴 입히기
function setSurfaceShapePattern(polygon, mode = 'onlyBorder', trestleMode = false, roofMaterial = selectedRoofMaterial) { function setSurfaceShapePattern(polygon, mode = 'onlyBorder', trestleMode = false, roofMaterial = selectedRoofMaterial) {
@ -14,6 +21,7 @@ export function useRoofFn() {
let width = (roofMaterial.width ?? 226) / 10 let width = (roofMaterial.width ?? 226) / 10
let height = (roofMaterial.length ?? 158) / 10 let height = (roofMaterial.length ?? 158) / 10
const index = roofMaterial.index ?? 0
let roofStyle = 2 let roofStyle = 2
const inputPatternSize = { width: width, height: height } //임시 사이즈 const inputPatternSize = { width: width, height: height } //임시 사이즈
const patternSize = { ...inputPatternSize } // 입력된 값을 뒤집기 위해 const patternSize = { ...inputPatternSize } // 입력된 값을 뒤집기 위해
@ -33,7 +41,7 @@ export function useRoofFn() {
const rows = Math.floor(patternSourceCanvas.height / patternSize.height) const rows = Math.floor(patternSourceCanvas.height / patternSize.height)
const cols = Math.floor(patternSourceCanvas.width / patternSize.width) const cols = Math.floor(patternSourceCanvas.width / patternSize.width)
ctx.strokeStyle = mode === 'allPainted' ? 'black' : 'blue' ctx.strokeStyle = mode === 'allPainted' ? 'black' : ROOF_COLOR[index]
ctx.lineWidth = mode === 'allPainted' ? 1 : 0.4 ctx.lineWidth = mode === 'allPainted' ? 1 : 0.4
ctx.fillStyle = mode === 'allPainted' ? 'rgba(0, 159, 64, 0.7)' : 'white' ctx.fillStyle = mode === 'allPainted' ? 'rgba(0, 159, 64, 0.7)' : 'white'
@ -141,5 +149,22 @@ export function useRoofFn() {
polygon.roofMaterial = roofMaterial polygon.roofMaterial = roofMaterial
polygon.canvas?.renderAll() polygon.canvas?.renderAll()
} }
return { setSurfaceShapePattern }
function removeRoofMaterial(roof = currentObject) {
if (roof === null || roof.name !== POLYGON_TYPE.ROOF) {
return
}
roof.set('fill', null)
roof.roofMaterial = null
canvas?.renderAll()
}
function removeAllRoofMaterial() {
const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
roofBases.forEach((roofBase) => {
removeRoofMaterial(roofBase)
})
}
return { setSurfaceShapePattern, removeRoofMaterial, removeAllRoofMaterial }
} }

View File

@ -88,10 +88,10 @@ export function useRoofShapePassivitySetting(id) {
const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
let stroke, strokeWidth let stroke, strokeWidth
lines.forEach((line) => { lines.forEach((line) => {
if (line.attributes.type === LINE_TYPE.WALLLINE.EAVES || line.attributes.type === LINE_TYPE.WALLLINE.HIPANDGABLE) { if (line.attributes?.type === LINE_TYPE.WALLLINE.EAVES || line.attributes?.type === LINE_TYPE.WALLLINE.HIPANDGABLE) {
stroke = '#45CD7D' stroke = '#45CD7D'
strokeWidth = 4 strokeWidth = 4
} else if (line.attributes.type === LINE_TYPE.WALLLINE.GABLE || line.attributes.type === LINE_TYPE.WALLLINE.JERKINHEAD) { } else if (line.attributes?.type === LINE_TYPE.WALLLINE.GABLE || line.attributes?.type === LINE_TYPE.WALLLINE.JERKINHEAD) {
stroke = '#3FBAE6' stroke = '#3FBAE6'
strokeWidth = 4 strokeWidth = 4
} else { } else {
@ -136,7 +136,7 @@ export function useRoofShapePassivitySetting(id) {
const index = lines.findIndex((line) => line === selectedLine) const index = lines.findIndex((line) => line === selectedLine)
const nextLine = lines[index + 1] || lines[0] const nextLine = lines[index + 1] || lines[0]
if (nextLine.attributes.isFixed) { if (nextLine.attributes?.isFixed) {
canvas.discardActiveObject() canvas.discardActiveObject()
return return
} }

View File

@ -39,6 +39,7 @@ import ContextRoofAllocationSetting from '@/components/floor-plan/modal/roofAllo
import { useCanvasSetting } from './option/useCanvasSetting' import { useCanvasSetting } from './option/useCanvasSetting'
import { useGrid } from './common/useGrid' import { useGrid } from './common/useGrid'
import { useAdsorptionPoint } from './useAdsorptionPoint' import { useAdsorptionPoint } from './useAdsorptionPoint'
import { useRoofFn } from '@/hooks/common/useRoofFn'
export function useContextMenu() { export function useContextMenu() {
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
@ -65,6 +66,7 @@ export function useContextMenu() {
const commonTextFont = useRecoilValue(fontSelector('commonText')) const commonTextFont = useRecoilValue(fontSelector('commonText'))
const { settingsData, setSettingsDataSave } = useCanvasSetting() const { settingsData, setSettingsDataSave } = useCanvasSetting()
const { swalFire } = useSwal() const { swalFire } = useSwal()
const { removeRoofMaterial, removeAllRoofMaterial } = useRoofFn()
const currentMenuSetting = () => { const currentMenuSetting = () => {
switch (currentMenu) { switch (currentMenu) {
@ -124,14 +126,17 @@ export function useContextMenu() {
{ {
id: 'roofMaterialPlacement', id: 'roofMaterialPlacement',
name: getMessage('contextmenu.roof.material.placement'), name: getMessage('contextmenu.roof.material.placement'),
component: <RoofAllocationSetting id={popupId} />,
}, },
{ {
id: 'roofMaterialRemove', id: 'roofMaterialRemove',
name: getMessage('contextmenu.roof.material.remove'), name: getMessage('contextmenu.roof.material.remove'),
fn: () => removeRoofMaterial(),
}, },
{ {
id: 'roofMaterialRemoveAll', id: 'roofMaterialRemoveAll',
name: getMessage('contextmenu.roof.material.remove.all'), name: getMessage('contextmenu.roof.material.remove.all'),
fn: () => removeAllRoofMaterial(),
}, },
{ {
id: 'selectMove', id: 'selectMove',

View File

@ -21,6 +21,8 @@ export const useSwal = () => {
text, text,
icon: icon === '' ? 'success' : icon, icon: icon === '' ? 'success' : icon,
confirmButtonText: '확인', confirmButtonText: '확인',
}).then(() => {
confirmFn()
}) })
} else if (type === 'confirm') { } else if (type === 'confirm') {
MySwal.fire({ MySwal.fire({

View File

@ -239,16 +239,6 @@ export const roofMaterialsSelector = selector({
}, },
}) })
// 지붕면 할당에서 추가된 지붕재 목록
export const addedRoofsSelector = selector({
key: 'addedRoofsSelector',
get: ({ get }) => {
const basicSetting = get(basicSettingState)
return basicSetting.roofs
},
dangerouslyAllowMutability: true,
})
/** /**
* 현재 선택된 물건 번호 * 현재 선택된 물건 번호
*/ */