diff --git a/.gitignore b/.gitignore index 315cf207..f3b61bd7 100644 --- a/.gitignore +++ b/.gitignore @@ -41,4 +41,5 @@ next-env.d.ts #lock files yarn.lock package-lock.json +pnpm-lock.yaml certificates \ No newline at end of file diff --git a/MainLayout.codediagram b/MainLayout.codediagram new file mode 100644 index 00000000..baf0aca3 --- /dev/null +++ b/MainLayout.codediagram @@ -0,0 +1 @@ +{"id":-1,"name":"FROM_FILE","userId":-1,"createdAt":"","updatedAt":"","content":{"items":[{"uid":"jsIqfLoyaa","position":{"x":160,"y":360},"sizes":{"width":720,"height":522},"autoheight":true,"blockContent":{"content":[{"type":"filePathNode","attrs":{"pathToFile":"src/app/","version":1},"content":[{"type":"text","marks":[{"type":"bold"}],"text":"layout.js"}]},{"type":"codeBlock","attrs":{"language":"javascript","wrapCode":true},"content":[{"type":"text","text":"\n \n \n {headerPathname === '/login' || headerPathname === '/join' ? (\n {children}\n ) : (\n \n
\n
\n
\n \n {children}\n
\n
\n
\n
\n )}\n \n \n \n \n
"}]}]},"nodeType":"block"},{"uid":"yOseUHcOzU","position":{"x":1020,"y":480},"sizes":{"width":557.5,"height":189.5},"autoheight":true,"blockContent":{"content":[{"type":"filePathNode","attrs":{"pathToFile":"src/app/","version":1},"content":[{"type":"text","marks":[{"type":"bold"}],"text":"RecoilWrapper.js"}]},{"type":"codeBlock","attrs":{"language":"javascript","wrapCode":true},"content":[{"type":"text","text":"export default function RecoilRootWrapper({ children }) {\n return {children}\n}"}]}]},"nodeType":"block"},{"uid":"ohc4COfFLi","position":{"x":1680,"y":370},"sizes":{"width":840.5,"height":312},"autoheight":true,"blockContent":{"content":[{"type":"filePathNode","attrs":{"pathToFile":"src/app/","version":1},"content":[{"type":"text","marks":[{"type":"bold"}],"text":"QcastProvider.js"}]},{"type":"codeBlock","attrs":{"language":"javascript","wrapCode":true},"content":[{"type":"text","text":"<>\n {isGlobalLoading && (\n
\n \n
\n )}\n \n }>{children}\n \n"}]}]},"nodeType":"block"},{"uid":"LQIlkk7G7c","position":{"x":2700,"y":160},"sizes":{"width":400,"height":109},"autoheight":true,"blockContent":{"content":[{"type":"filePathNode","attrs":{"pathToFile":"src/components/header/","version":1},"content":[{"type":"text","marks":[{"type":"bold"}],"text":"Header.jsx"}]},{"type":"paragraph","content":[{"type":"text","text":" "}]}]},"nodeType":"block"},{"uid":"h-RKXEf7EQ","position":{"x":2710,"y":360},"sizes":{"width":792.5,"height":207},"autoheight":true,"blockContent":{"content":[{"type":"filePathNode","attrs":{"pathToFile":"src/app/","version":1},"content":[{"type":"text","marks":[{"type":"bold"}],"text":"SessionProvider.js"}]},{"type":"codeBlock","attrs":{"language":"javascript","wrapCode":true},"content":[{"type":"text","text":"export default function SessionProvider({ useSession, children }) {\n const [session, setSession] = useState(useSession)\n return {children}\n}"}]}]},"nodeType":"block"},{"uid":"eMjqDGJ7H-","position":{"x":2710,"y":660},"sizes":{"width":400,"height":109},"autoheight":true,"blockContent":{"content":[{"type":"filePathNode","attrs":{"pathToFile":"src/components/footer/","version":1},"content":[{"type":"text","marks":[{"type":"bold"}],"text":"Footer.jsx"}]},{"type":"paragraph","content":[{"type":"text","text":" "}]}]},"nodeType":"block"},{"uid":"HjtO7B4Big","position":{"x":1110,"y":730},"sizes":{"width":400,"height":312},"autoheight":true,"blockContent":{"content":[{"type":"filePathNode","attrs":{"pathToFile":"src/components/common/popupManager/","version":1},"content":[{"type":"text","marks":[{"type":"bold"}],"text":"PopupManager.jsx"}]},{"type":"codeBlock","attrs":{"language":"javascript","wrapCode":true},"content":[{"type":"text","text":"export default function PopupManager() {\n const popup = useRecoilValue(popupState)\n\n return [\n ...popup?.config.map((child) => {child.component}),\n ...popup?.other.map((child) => {child.component}),\n ]\n}"}]}]},"nodeType":"block"},{"uid":"VyJRuoccdZ","position":{"x":3620,"y":400},"sizes":{"width":190,"height":95},"autoheight":true,"blockContent":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"type":"text","marks":[{"type":"bold"},{"type":"italic"}],"text":"{children}"}]}]},"nodeType":"block"},{"uid":"BO78hOqExy","position":{"x":1750,"y":140},"sizes":{"width":400,"height":109},"autoheight":true,"blockContent":{"content":[{"type":"filePathNode","attrs":{"pathToFile":"node_modules/next/dist/client/components/","version":1},"content":[{"type":"text","marks":[{"type":"bold"}],"text":"error-boundary.d.ts"}]},{"type":"paragraph","content":[{"type":"text","text":" "}]}]},"nodeType":"block"}],"configs":{"centerX":-818,"centerY":-32,"zoomLevel":1},"arrowData":{"arrowsMap":{},"pointsMap":{},"edgesMap":{"edge-jsIqfLoyaa-jsIqfLoyaa-right-yOseUHcOzU-yOseUHcOzU-left":{"uid":"edge-jsIqfLoyaa-jsIqfLoyaa-right-yOseUHcOzU-yOseUHcOzU-left","fromNodeId":"jsIqfLoyaa","fromHandleId":"jsIqfLoyaa-right","toNodeId":"yOseUHcOzU","toHandleId":"yOseUHcOzU-left","direction":"ft","selectable":true,"type":"solid","content":{"label":""}},"edge-yOseUHcOzU-yOseUHcOzU-right-ohc4COfFLi-ohc4COfFLi-left":{"uid":"edge-yOseUHcOzU-yOseUHcOzU-right-ohc4COfFLi-ohc4COfFLi-left","fromNodeId":"yOseUHcOzU","fromHandleId":"yOseUHcOzU-right","toNodeId":"ohc4COfFLi","toHandleId":"ohc4COfFLi-left","direction":"ft","selectable":true,"type":"solid","content":{"label":""}},"edge-ohc4COfFLi-ohc4COfFLi-right-LQIlkk7G7c-LQIlkk7G7c-left":{"uid":"edge-ohc4COfFLi-ohc4COfFLi-right-LQIlkk7G7c-LQIlkk7G7c-left","fromNodeId":"ohc4COfFLi","fromHandleId":"ohc4COfFLi-right","toNodeId":"LQIlkk7G7c","toHandleId":"LQIlkk7G7c-left","direction":"ft","selectable":true,"type":"solid","content":{"label":""}},"edge-ohc4COfFLi-ohc4COfFLi-right-h-RKXEf7EQ-h-RKXEf7EQ-left":{"uid":"edge-ohc4COfFLi-ohc4COfFLi-right-h-RKXEf7EQ-h-RKXEf7EQ-left","fromNodeId":"ohc4COfFLi","fromHandleId":"ohc4COfFLi-right","toNodeId":"h-RKXEf7EQ","toHandleId":"h-RKXEf7EQ-left","direction":"ft","selectable":true,"type":"solid","content":{"label":""}},"edge-ohc4COfFLi-ohc4COfFLi-right-eMjqDGJ7H--eMjqDGJ7H--left":{"uid":"edge-ohc4COfFLi-ohc4COfFLi-right-eMjqDGJ7H--eMjqDGJ7H--left","fromNodeId":"ohc4COfFLi","fromHandleId":"ohc4COfFLi-right","toNodeId":"eMjqDGJ7H-","toHandleId":"eMjqDGJ7H--left","direction":"ft","selectable":true,"type":"solid","content":{"label":""}},"edge-yOseUHcOzU-yOseUHcOzU-bottom-HjtO7B4Big-HjtO7B4Big-top":{"uid":"edge-yOseUHcOzU-yOseUHcOzU-bottom-HjtO7B4Big-HjtO7B4Big-top","fromNodeId":"yOseUHcOzU","fromHandleId":"yOseUHcOzU-bottom","toNodeId":"HjtO7B4Big","toHandleId":"HjtO7B4Big-top","direction":"ft","selectable":true,"type":"solid","content":{"label":""}},"edge-h-RKXEf7EQ-h-RKXEf7EQ-right-VyJRuoccdZ-VyJRuoccdZ-left":{"uid":"edge-h-RKXEf7EQ-h-RKXEf7EQ-right-VyJRuoccdZ-VyJRuoccdZ-left","fromNodeId":"h-RKXEf7EQ","fromHandleId":"h-RKXEf7EQ-right","toNodeId":"VyJRuoccdZ","toHandleId":"VyJRuoccdZ-left","direction":"ft","selectable":true,"type":"solid","content":{"label":""}},"edge-BO78hOqExy-BO78hOqExy-bottom-ohc4COfFLi-ohc4COfFLi-top":{"uid":"edge-BO78hOqExy-BO78hOqExy-bottom-ohc4COfFLi-ohc4COfFLi-top","fromNodeId":"BO78hOqExy","fromHandleId":"BO78hOqExy-bottom","toNodeId":"ohc4COfFLi","toHandleId":"ohc4COfFLi-top","direction":"ft","selectable":true,"type":"solid","content":{"label":""}}}}}} \ No newline at end of file diff --git a/src/app/QcastProvider.js b/src/app/QcastProvider.js index 861ee8f9..43404308 100644 --- a/src/app/QcastProvider.js +++ b/src/app/QcastProvider.js @@ -1,6 +1,6 @@ 'use client' -import { createContext, useEffect, useState } from 'react' +import { createContext, useState } from 'react' import { ErrorBoundary } from 'next/dist/client/components/error-boundary' import { useCommonCode } from '@/hooks/common/useCommonCode' import ServerError from './error' diff --git a/src/components/Playground.jsx b/src/components/Playground.jsx index e2901061..fcc5468d 100644 --- a/src/components/Playground.jsx +++ b/src/components/Playground.jsx @@ -25,6 +25,7 @@ import QPagination from './common/pagination/QPagination' import { trestleRequestModels, constructionRequestModels, trestleDetailRequestModels } from '@/models/apiModels' import QSelectBox from './common/select/QSelectBox' +import SampleReducer from './sample/SampleReducer' export default function Playground() { const [useCadFile, setUseCadFile] = useRecoilState(useCadFileState) @@ -504,6 +505,9 @@ export default function Playground() {
+
+ +
) diff --git a/src/components/floor-plan/CanvasFrame.jsx b/src/components/floor-plan/CanvasFrame.jsx index d21c55e7..cf2e0d56 100644 --- a/src/components/floor-plan/CanvasFrame.jsx +++ b/src/components/floor-plan/CanvasFrame.jsx @@ -1,32 +1,28 @@ 'use client' -import { useContext, useEffect, useRef } from 'react' +import { useEffect, useRef } from 'react' import { useRecoilValue } from 'recoil' +import QContextMenu from '@/components/common/context-menu/QContextMenu' +import PanelBatchStatistics from '@/components/floor-plan/modal/panelBatch/PanelBatchStatistics' +import ImgLoad from '@/components/floor-plan/modal/ImgLoad' import { useCanvas } from '@/hooks/useCanvas' -import { useEvent } from '@/hooks/useEvent' import { usePlan } from '@/hooks/usePlan' import { useContextMenu } from '@/hooks/useContextMenu' -import { currentMenuState } from '@/store/canvasAtom' -import QContextMenu from '@/components/common/context-menu/QContextMenu' import { useCanvasConfigInitialize } from '@/hooks/common/useCanvasConfigInitialize' -import { MENU } from '@/common/common' -import PanelBatchStatistics from '@/components/floor-plan/modal/panelBatch/PanelBatchStatistics' +import { currentMenuState } from '@/store/canvasAtom' import { totalDisplaySelector } from '@/store/settingAtom' -import ImgLoad from '@/components/floor-plan/modal/ImgLoad' +import { MENU } from '@/common/common' export default function CanvasFrame() { const canvasRef = useRef(null) - const { canvas, handleBackImageLoadToCanvas } = useCanvas('canvas') + const { canvas } = useCanvas('canvas') const { canvasLoadInit, gridInit } = useCanvasConfigInitialize() const currentMenu = useRecoilValue(currentMenuState) const { contextMenu, handleClick } = useContextMenu() - const { selectedPlan, currentCanvasPlan } = usePlan() + const { selectedPlan } = usePlan() const totalDisplay = useRecoilValue(totalDisplaySelector) // 집계표 표시 여부 - // useEvent() - // const { initEvent } = useContext(EventContext) - // initEvent() const loadCanvas = () => { if (canvas) { diff --git a/src/components/floor-plan/CanvasLayout.jsx b/src/components/floor-plan/CanvasLayout.jsx index 70bf2bb9..ba2994c0 100644 --- a/src/components/floor-plan/CanvasLayout.jsx +++ b/src/components/floor-plan/CanvasLayout.jsx @@ -3,12 +3,12 @@ import { useContext, useEffect } from 'react' import { useRecoilValue } from 'recoil' import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider' +import { SessionContext } from '@/app/SessionProvider' import { useMessage } from '@/hooks/useMessage' import { useSwal } from '@/hooks/useSwal' import { usePlan } from '@/hooks/usePlan' -import { globalLocaleStore } from '@/store/localeAtom' -import { SessionContext } from '@/app/SessionProvider' import { useCanvasMenu } from '@/hooks/common/useCanvasMenu' +import { globalLocaleStore } from '@/store/localeAtom' export default function CanvasLayout({ children }) { // const { menuNumber } = props diff --git a/src/components/floor-plan/CanvasMenu.jsx b/src/components/floor-plan/CanvasMenu.jsx index 71fdb0bf..34a7b6c4 100644 --- a/src/components/floor-plan/CanvasMenu.jsx +++ b/src/components/floor-plan/CanvasMenu.jsx @@ -2,54 +2,47 @@ import { useContext, useEffect, useState } from 'react' +import { usePathname, useRouter } from 'next/navigation' + import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' -import { usePathname, useRouter } from 'next/navigation' +import { v4 as uuidv4 } from 'uuid' + import MenuDepth01 from './MenuDepth01' import QSelectBox from '@/components/common/select/QSelectBox' -import { v4 as uuidv4 } from 'uuid' +import SettingModal01 from '@/components/floor-plan/modal/setting01/SettingModal01' +import PlacementShapeSetting from '@/components/floor-plan/modal/placementShape/PlacementShapeSetting' +import EstimateCopyPop from '../estimate/popup/EstimateCopyPop' +import DocDownOptionPop from '../estimate/popup/DocDownOptionPop' +import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider' import { useMessage } from '@/hooks/useMessage' import { usePlan } from '@/hooks/usePlan' import { useSwal } from '@/hooks/useSwal' import { useEvent } from '@/hooks/useEvent' +import { usePopup } from '@/hooks/usePopup' +import { useCanvasEvent } from '@/hooks/useCanvasEvent' +import { useCommonUtils } from '@/hooks/common/useCommonUtils' +import useMenu from '@/hooks/common/useMenu' +import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController' +import { useAxios } from '@/hooks/useAxios' +import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting' import { canvasSettingState, canvasState, canvasZoomState, currentMenuState, verticalHorizontalModeState } from '@/store/canvasAtom' import { sessionStore } from '@/store/commonAtom' import { outerLinePointsState } from '@/store/outerLineAtom' import { appMessageStore, globalLocaleStore } from '@/store/localeAtom' -import { - addedRoofsSelector, - addedRoofsState, - basicSettingState, - roofMaterialsSelector, - selectedRoofMaterialSelector, - settingModalFirstOptionsState, -} from '@/store/settingAtom' +import { addedRoofsState, basicSettingState, selectedRoofMaterialSelector, settingModalFirstOptionsState } from '@/store/settingAtom' +import { placementShapeDrawingPointsState } from '@/store/placementShapeDrawingAtom' +import { commonUtilsState } from '@/store/commonUtilsAtom' +import { menusState, menuTypeState } from '@/store/menuAtom' +import { estimateState, floorPlanObjectState } from '@/store/floorPlanObjectAtom' +import { pwrGnrSimTypeState } from '@/store/simulatorAtom' +import { isObjectNotEmpty } from '@/util/common-utils' import KO from '@/locales/ko.json' import JA from '@/locales/ja.json' -import { useCanvasEvent } from '@/hooks/useCanvasEvent' -import SettingModal01 from '@/components/floor-plan/modal/setting01/SettingModal01' -import { usePopup } from '@/hooks/usePopup' -import { placementShapeDrawingPointsState } from '@/store/placementShapeDrawingAtom' -import PlacementShapeSetting from '@/components/floor-plan/modal/placementShape/PlacementShapeSetting' -import { useCommonUtils } from '@/hooks/common/useCommonUtils' -import { commonUtilsState } from '@/store/commonUtilsAtom' -import { menusState, menuTypeState } from '@/store/menuAtom' -import useMenu from '@/hooks/common/useMenu' import { MENU } from '@/common/common' -import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController' -import { estimateState, floorPlanObjectState } from '@/store/floorPlanObjectAtom' -import DocDownOptionPop from '../estimate/popup/DocDownOptionPop' -import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider' -import EstimateCopyPop from '../estimate/popup/EstimateCopyPop' -import { pwrGnrSimTypeState } from '@/store/simulatorAtom' -import { useAxios } from '@/hooks/useAxios' - -import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting' -import { isObjectNotEmpty } from '@/util/common-utils' - export default function CanvasMenu(props) { const { menuNumber, setMenuNumber } = props const pathname = usePathname() @@ -58,8 +51,8 @@ export default function CanvasMenu(props) { const canvasMenus = useRecoilValue(menusState) const [type, setType] = useRecoilState(menuTypeState) const [verticalHorizontalMode, setVerticalHorizontalMode] = useRecoilState(verticalHorizontalModeState) - const [appMessageState, setAppMessageState] = useRecoilState(appMessageStore) - const [currentMenu, setCurrentMenu] = useRecoilState(currentMenuState) + const setAppMessageState = useSetRecoilState(appMessageStore) + const setCurrentMenu = useSetRecoilState(currentMenuState) const setOuterLinePoints = useSetRecoilState(outerLinePointsState) const setPlacementPoints = useSetRecoilState(placementShapeDrawingPointsState) const canvasSetting = useRecoilValue(canvasSettingState) @@ -86,7 +79,7 @@ export default function CanvasMenu(props) { const { floorPlanState, setFloorPlanState } = useContext(FloorPlanContext) const { restoreModuleInstArea } = useModuleBasicSetting() - const addedRoofs = useRecoilValue(addedRoofsState) + const [addedRoofs, setAddedRoofsState] = useRecoilState(addedRoofsState) const [basicSetting, setBasicSetting] = useRecoilState(basicSettingState) const selectedRoofMaterial = useRecoilValue(selectedRoofMaterialSelector) @@ -123,15 +116,18 @@ export default function CanvasMenu(props) { if (pathname !== '/floor-plan') router.push('/floor-plan') } - useEffect(() => { - console.log('addedRoofs', addedRoofs) - }, [addedRoofs]) - useEffect(() => { - console.log('selectedRoofMaterial', selectedRoofMaterial) - }, [selectedRoofMaterial]) - const changeSelectedRoofMaterial = (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) diff --git a/src/components/floor-plan/FloorPlan.jsx b/src/components/floor-plan/FloorPlan.jsx index e2c53668..fe95d46b 100644 --- a/src/components/floor-plan/FloorPlan.jsx +++ b/src/components/floor-plan/FloorPlan.jsx @@ -1,6 +1,6 @@ 'use client' -import { useContext, useEffect } from 'react' +import { useEffect } from 'react' //import { useRecoilState } from 'recoil' import CanvasMenu from '@/components/floor-plan/CanvasMenu' import { useCanvasMenu } from '@/hooks/common/useCanvasMenu' diff --git a/src/components/floor-plan/MenuDepth01.jsx b/src/components/floor-plan/MenuDepth01.jsx index 5b1897b3..1253c491 100644 --- a/src/components/floor-plan/MenuDepth01.jsx +++ b/src/components/floor-plan/MenuDepth01.jsx @@ -1,11 +1,12 @@ 'use client' +import { useEffect } from 'react' + import { useMessage } from '@/hooks/useMessage' +import useMenu from '@/hooks/common/useMenu' import { canvasState, currentMenuState } from '@/store/canvasAtom' import { useRecoilState, useRecoilValue } from 'recoil' import { menuTypeState, subMenusState } from '@/store/menuAtom' -import useMenu from '@/hooks/common/useMenu' -import { useEffect } from 'react' export default function MenuDepth01() { const type = useRecoilValue(menuTypeState) diff --git a/src/components/floor-plan/RoofCoveringMenu.jsx b/src/components/floor-plan/RoofCoveringMenu.jsx index 26da4bda..b270a1f2 100644 --- a/src/components/floor-plan/RoofCoveringMenu.jsx +++ b/src/components/floor-plan/RoofCoveringMenu.jsx @@ -1,11 +1,12 @@ 'use client' -import { useMessage } from '@/hooks/useMessage' import { useRecoilState, useSetRecoilState } from 'recoil' -import { currentMenuState } from '@/store/canvasAtom' + import { MENU } from '@/common/common' -import { modalState } from '@/store/modalAtom' import { ToggleonMouse } from '@/components/header/Header' +import { useMessage } from '@/hooks/useMessage' +import { currentMenuState } from '@/store/canvasAtom' +import { modalState } from '@/store/modalAtom' export default function RoofCoveringMenu() { const { getMessage } = useMessage() diff --git a/src/components/floor-plan/modal/module/PanelEdit.jsx b/src/components/floor-plan/modal/module/PanelEdit.jsx index ec9d9e1b..a6ad56c1 100644 --- a/src/components/floor-plan/modal/module/PanelEdit.jsx +++ b/src/components/floor-plan/modal/module/PanelEdit.jsx @@ -9,15 +9,27 @@ import { deepCopyArray } from '@/util/common-utils' import { canvasState } from '@/store/canvasAtom' import * as turf from '@turf/turf' import { POLYGON_TYPE } from '@/common/common' +import { useModal } from '@nextui-org/react' +import { useModule } from '@/hooks/module/useModule' + +export const PANEL_EDIT_TYPE = { + MOVE: 'move', + COPY: 'copy', + COLUMN_MOVE: 'columnMove', + COLUMN_COPY: 'columnCopy', + ROW_MOVE: 'rowMove', + ROW_COPY: 'rowCopy', +} export default function PanelEdit(props) { const contextPopupPosition = useRecoilValue(contextPopupPositionState) - const { id, pos = contextPopupPosition, type = 'move', apply } = props + const { id, pos = contextPopupPosition, type = PANEL_EDIT_TYPE.MOVE, apply } = props const { closePopup } = usePopup() const [length, setLength] = useState(0) const [direction, setDirection] = useState('up') const { getMessage } = useMessage() const canvas = useRecoilValue(canvasState) + const { moduleMove, moduleCopy, moduleMultiMove, moduleMultiCopy } = useModule() useEffect(() => { if (canvas) { @@ -28,7 +40,20 @@ export default function PanelEdit(props) { //모듈 이동 적용 const handleApply = () => { - contextModuleMove(length, direction) + // const activeModuleIds = canvas.getActiveObjects().map((obj) => obj.id) + if (type === PANEL_EDIT_TYPE.MOVE) { + moduleMove(length, direction) + } else if (type === PANEL_EDIT_TYPE.COPY) { + moduleCopy(length, direction) + } else if (type === PANEL_EDIT_TYPE.COLUMN_MOVE) { + moduleMultiMove('column', length, direction) + } else if (type === PANEL_EDIT_TYPE.COLUMN_COPY) { + moduleMultiCopy('column', length, direction) + } else if (type === PANEL_EDIT_TYPE.ROW_MOVE) { + moduleMultiMove('row', length, direction) + } else if (type === PANEL_EDIT_TYPE.ROW_COPY) { + moduleMultiCopy('row', length, direction) + } closePopup(id) } diff --git a/src/components/floor-plan/modal/module/column/ColumnInsert.jsx b/src/components/floor-plan/modal/module/column/ColumnInsert.jsx index 8239dc33..56f9a66c 100644 --- a/src/components/floor-plan/modal/module/column/ColumnInsert.jsx +++ b/src/components/floor-plan/modal/module/column/ColumnInsert.jsx @@ -5,20 +5,22 @@ import { usePopup } from '@/hooks/usePopup' import { useMessage } from '@/hooks/useMessage' import { useState } from 'react' import Image from 'next/image' +import { MODULE_INSERT_TYPE, useModule } from '@/hooks/module/useModule' export default function ColumnInsert(props) { const contextPopupPosition = useRecoilValue(contextPopupPositionState) const { id, pos = contextPopupPosition, apply } = props const { closePopup } = usePopup() - const [selectedType, setSelectedType] = useState(1) + const [selectedType, setSelectedType] = useState(MODULE_INSERT_TYPE.LEFT) const { getMessage } = useMessage() + const { moduleColumnInsert } = useModule() const handleApply = () => { - if (apply) apply() + moduleColumnInsert(selectedType) closePopup(id) } - const HandleRadioChange = (e) => { - setSelectedType(Number(e.target.value)) + const handleRadioChange = (e) => { + setSelectedType(e.target.value) } return ( @@ -36,16 +38,30 @@ export default function ColumnInsert(props) {
- +
- +
- {selectedType === 1 && ( + {selectedType === MODULE_INSERT_TYPE.LEFT && ( react )} - {selectedType === 2 && ( + {selectedType === MODULE_INSERT_TYPE.RIGHT && ( react { - if (apply) apply() + // if (apply) apply() + moduleColumnRemove(selectedType) closePopup(id) } @@ -39,12 +42,12 @@ export default function ColumnRemove(props) {
{types.map((type, index) => { return ( -
+
setSelectedType(Number(e.target.value))} + onClick={(e) => setSelectedType(e.target.value)} value={type.value} checked={selectedType === type.value} /> @@ -54,7 +57,7 @@ export default function ColumnRemove(props) { })}
- {selectedType === 1 && ( + {selectedType === MODULE_REMOVE_TYPE.LEFT && ( react )} - {selectedType === 2 && ( + {selectedType === MODULE_REMOVE_TYPE.RIGHT && ( react )} - {selectedType === 3 && ( + {selectedType === MODULE_REMOVE_TYPE.HORIZONTAL_SIDE && ( react )} - {selectedType === 4 && ( + {selectedType === MODULE_REMOVE_TYPE.NONE && ( react { - if (apply) apply() + muduleRowInsert(selectedType) closePopup(id) } const HandleRadioChange = (e) => { - setSelectedType(Number(e.target.value)) + setSelectedType(e.target.value) } return ( @@ -36,16 +38,30 @@ export default function RowInsert(props) {
- +
- +
- {selectedType === 1 && ( + {selectedType === MODULE_INSERT_TYPE.TOP && ( react )} - {selectedType === 2 && ( + {selectedType === MODULE_INSERT_TYPE.BOTTOM && ( react { - if (apply) apply() + // if (apply) apply() + moduleRowRemove(selectedType) closePopup(id) } @@ -39,22 +42,22 @@ export default function RowRemove(props) {
{types.map((type, index) => { return ( -
+
setSelectedType(Number(e.target.value))} + onClick={(e) => setSelectedType(e.target.value)} value={type.value} checked={selectedType === type.value} /> - +
) })}
- {selectedType === 1 && ( + {selectedType === MODULE_REMOVE_TYPE.TOP && ( react )} - {selectedType === 2 && ( + {selectedType === MODULE_REMOVE_TYPE.BOTTOM && ( react )} - {selectedType === 3 && ( + {selectedType === MODULE_REMOVE_TYPE.VERTICAL_SIDE && ( react )} - {selectedType === 4 && ( + {selectedType === MODULE_REMOVE_TYPE.NONE && ( react {/* {raftCodes.map((raft, index) => { diff --git a/src/components/floor-plan/modal/roofAllocation/ContextRoofAllocationSetting.jsx b/src/components/floor-plan/modal/roofAllocation/ContextRoofAllocationSetting.jsx index cba18953..23993cb0 100644 --- a/src/components/floor-plan/modal/roofAllocation/ContextRoofAllocationSetting.jsx +++ b/src/components/floor-plan/modal/roofAllocation/ContextRoofAllocationSetting.jsx @@ -28,6 +28,7 @@ export default function ContextRoofAllocationSetting(props) { handleChangeRaft, handleChangeLayout, handleSaveContext, + currentRoofList, } = useRoofAllocationSetting(id) const { findCommonCode } = useCommonCode() @@ -39,7 +40,7 @@ export default function ContextRoofAllocationSetting(props) { return ( -
+

{getMessage('plan.menu.estimate.roof.alloc')}

- {roofList.map((roof, index) => { + {currentRoofList.map((roof, index) => { return (
@@ -88,78 +93,74 @@ export default function ContextRoofAllocationSetting(props) {
r.id === roof.id)} + value={roof} + showKey={'roofMatlNm'} + sourceKey={'roofMatlCd'} + targetKey={'roofMatlCd'} onChange={(e) => handleChangeRoofMaterial(e, index)} />
- {index === 0 && 基本屋根材} + {index === 0 && {getMessage('modal.roof.alloc.default.roof.material')}} {index !== 0 && }
-
- {roof.widAuth && ( -
- W -
- + {(roof.widAuth || roof.lenAuth) && ( +
+ {roof.widAuth && ( +
+ W +
+ +
- {/*
- -
*/} -
- )} - {roof.lenAuth && ( -
- L -
- + )} + {roof.lenAuth && ( +
+ L +
+ +
- {/*
- -
*/} -
- )} - {roof.raftAuth && ( -
- {getMessage('modal.placement.initial.setting.rafter')} -
- {raftCodes.length > 0 && ( - ({ name: raft.clCodeNm, value: raft.clCode }))} - onChange={(e) => handleChangeRaft(e, index)} - value={raftCodes.find((r) => r.value === roof.raft)} - /> - )} - {/* */} + )} +
+ )} + {(roof.raftAuth || roof.roofPchAuth) && ( +
+ {roof.raftAuth && ( +
+
+ {getMessage('modal.placement.initial.setting.rafter')} + {raftCodes.length > 0 && ( +
+ +
+ )} +
-
- )} - {roof.roofPchAuth && ( -
- {getMessage('hajebichi')} -
- + )} + {roof.roofPchAuth && ( +
+
+ {getMessage('hajebichi')} +
+ +
+
- {/*
- -
*/} -
- )} -
+ )} +
+ )}
- {roofList.map((roof, index) => { + {currentRoofList.map((roof, index) => { return (
@@ -136,6 +137,7 @@ export default function RoofAllocationSetting(props) { showKey={'clCodeNm'} sourceKey={'clCode'} targetKey={roof.raft ? 'raft' : 'raftBaseCd'} + onChange={(e) => handleChangeRaft(e, index)} />
)} diff --git a/src/components/sample/SampleReducer.jsx b/src/components/sample/SampleReducer.jsx new file mode 100644 index 00000000..26150fea --- /dev/null +++ b/src/components/sample/SampleReducer.jsx @@ -0,0 +1,115 @@ +import { Card, CardBody, Input, Tab, Tabs } from '@nextui-org/react' +import { useEffect, useReducer } from 'react' + +const reducer = (prevState, nextState) => { + return { ...prevState, ...nextState } +} + +const defaultData = { + commonData: 'common', + tabs: [ + { + id: 1, + name: 'tab1', + range: 10, + maker: 'maker1', + law: 'law1', + basis: 'basis1', + }, + { + id: 2, + name: 'tab2', + range: 20, + maker: 'maker2', + law: 'law2', + basis: 'basis2', + }, + { + id: 3, + name: 'tab3', + range: 30, + maker: 'maker3', + law: 'law3', + basis: 'basis3', + }, + { + id: 4, + name: 'tab4', + range: 40, + maker: 'maker4', + law: 'law4', + basis: 'basis4', + }, + ], +} + +export default function SampleReducer() { + const [sampleState, setSampleState] = useReducer(reducer, defaultData) + + const handleChangeTabsData = (newTab) => { + const newTabs = sampleState.tabs.map((t) => { + if (t.id === newTab.id) { + return newTab + } else { + return t + } + }) + setSampleState({ ...sampleState, tabs: newTabs }) + } + + useEffect(() => { + console.log('🚀 ~ SampleReducer ~ sampleState:', sampleState) + }, [sampleState]) + + return ( + <> +
공통: {sampleState.commonData}
+
+ + {sampleState.tabs.map((s) => ( + + + +
+ { + handleChangeTabsData({ ...s, range: e.target.value }) + }} + /> + { + handleChangeTabsData({ ...s, maker: e.target.value }) + }} + /> + { + handleChangeTabsData({ ...s, law: e.target.value }) + }} + /> + { + handleChangeTabsData({ ...s, basis: e.target.value }) + }} + /> +
+
+
+
+ ))} +
+
+ + ) +} diff --git a/src/hooks/common/useRoofFn.js b/src/hooks/common/useRoofFn.js index afe1c71e..eb932106 100644 --- a/src/hooks/common/useRoofFn.js +++ b/src/hooks/common/useRoofFn.js @@ -1,11 +1,13 @@ import { useRecoilValue } from 'recoil' -import { canvasState } from '@/store/canvasAtom' +import { canvasState, currentObjectState } from '@/store/canvasAtom' import { selectedRoofMaterialSelector } from '@/store/settingAtom' import { ROOF_MATERIAL_LAYOUT } from '@/components/floor-plan/modal/placementShape/PlacementShapeSetting' +import { POLYGON_TYPE } from '@/common/common' export function useRoofFn() { const canvas = useRecoilValue(canvasState) const selectedRoofMaterial = useRecoilValue(selectedRoofMaterialSelector) + const currentObject = useRecoilValue(currentObjectState) //면형상 선택 클릭시 지붕 패턴 입히기 function setSurfaceShapePattern(polygon, mode = 'onlyBorder', trestleMode = false, roofMaterial = selectedRoofMaterial) { @@ -141,5 +143,22 @@ export function useRoofFn() { polygon.roofMaterial = roofMaterial 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 } } diff --git a/src/hooks/module/useModule.js b/src/hooks/module/useModule.js new file mode 100644 index 00000000..5e494412 --- /dev/null +++ b/src/hooks/module/useModule.js @@ -0,0 +1,833 @@ +import { BATCH_TYPE, POLYGON_TYPE } from '@/common/common' +import { canvasState } from '@/store/canvasAtom' +import { isOverlap, polygonToTurfPolygon, rectToPolygon } from '@/util/canvas-util' +import { useRecoilValue } from 'recoil' +import { v4 as uuidv4 } from 'uuid' +import * as turf from '@turf/turf' +import { useSwal } from '../useSwal' +import { QPolygon } from '@/components/fabric/QPolygon' +import { useModuleBasicSetting } from './useModuleBasicSetting' + +export const MODULE_REMOVE_TYPE = { + LEFT: 'left', + RIGHT: 'right', + HORIZONTAL_SIDE: 'horizontalSide', + TOP: 'top', + BOTTOM: 'bottom', + VERTICAL_SIDE: 'verticalSide', + NONE: 'none', +} + +export const MODULE_INSERT_TYPE = { + LEFT: 'left', + RIGHT: 'right', + TOP: 'up', + BOTTOM: 'down', +} + +export function useModule() { + const canvas = useRecoilValue(canvasState) + const { swalFire } = useSwal() + const { checkModuleDisjointObjects } = useModuleBasicSetting() + + const moduleMove = (length, direction) => { + const checkModuleDisjointSurface = (squarePolygon, turfModuleSetupSurface) => { + return turf.booleanContains(turfModuleSetupSurface, squarePolygon) || turf.booleanWithin(squarePolygon, turfModuleSetupSurface) + } + + const selectedObj = canvas.getActiveObjects() //선택된 객체들을 가져옴 + const selectedIds = selectedObj.map((obj) => obj.id) // selectedObj의 ID 추출 + + canvas.discardActiveObject() //선택해제 + + const isSetupModules = canvas.getObjects().filter((obj) => obj.name === 'module' && !selectedIds.includes(obj.id)) // selectedObj에 없는 객체만 필터링 + const selectedModules = canvas.getObjects().filter((obj) => selectedIds.includes(obj.id) && obj.name === 'module') //선택했던 객체들만 가져옴 + const setupSurface = canvas + .getObjects() + .filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && obj.id === selectedModules[0].surfaceId)[0] + const isOverlapArray = [] + const isInSurfaceArray = [] + const isOverlapObjects = [] + const objects = getObjects() + + if (selectedModules) { + canvas.remove(...selectedModules) + selectedModules.forEach((module) => { + module.set({ + originCoords: { + left: module.left, + top: module.top, + }, + }) + + if (direction === 'up') { + module.set({ ...module, top: module.top - Number(length) }) + } else if (direction === 'down') { + module.set({ ...module, top: module.top + Number(length) }) + } else if (direction === 'left') { + module.set({ ...module, left: module.left - Number(length) }) + } else if (direction === 'right') { + module.set({ ...module, left: module.left + Number(length) }) + } + module.setCoords() + canvas.renderAll() + + //다른 모듈과 겹치는지 확인하는 로직 + const isOverlap = isSetupModules.some((isSetupModule) => + turf.booleanOverlap(polygonToTurfPolygon(module, true), polygonToTurfPolygon(isSetupModule, true)), + ) + isOverlapArray.push(isOverlap) + + const turfModuleSetupSurface = polygonToTurfPolygon(setupSurface, true) + const turfModule = polygonToTurfPolygon(module, true) + + //나갔는지 확인하는 로직 + const isInSurface = turf.booleanContains(turfModuleSetupSurface, turfModule) || turf.booleanWithin(turfModule, turfModuleSetupSurface) + isInSurfaceArray.push(isInSurface) + isOverlapObjects.push(!checkModuleDisjointObjects(polygonToTurfPolygon(module, true), objects)) + }) + + const isNotOverlap = isOverlapArray.some((isOverlap) => isOverlap) // true면 겹침 + const isNotOutSurface = isInSurfaceArray.every((isOutSurface) => isOutSurface) //false면 밖으로 나감 + + //안겹치고 안나갔으면 이동시킨다 아니면 원래 위치로 돌려놓는다 + if (isNotOverlap || !isNotOutSurface || isOverlapObjects.some((isOverlap) => isOverlap)) { + swalFire({ + title: isNotOverlap + ? '겹치는 모듈이 있습니다.' + : isOverlapObjects.some((isOverlap) => isOverlap) + ? '모듈이 오브젝트와 겹칩니다.' + : '영역 밖', + icon: 'error', + type: 'confirm', + confirmFn: () => { + selectedModules.forEach((module) => { + module.set({ ...module, left: module.originCoords.left, top: module.originCoords.top }) + module.setCoords() + }) + }, + }) + } + + canvas.add(...selectedModules) + canvas.renderAll() + } + } + + const moduleCopy = (length, direction) => { + if (canvas.getActiveObjects().length === 0) return + const activeModuleIds = canvas.getActiveObjects().map((obj) => obj.id) + const modules = canvas.getObjects().filter((obj) => activeModuleIds.includes(obj.id)) + const objects = getObjects() + const otherModules = canvas.getObjects().filter((obj) => obj.surfaceId === modules[0].surfaceId && obj.name === POLYGON_TYPE.MODULE) + const moduleSetupSurface = canvas + .getObjects() + .filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && obj.id === modules[0].surfaceId)[0] + let [isOverlapOtherModules, isOverlapObjects, isOutsideSurface] = [[], [], []] + canvas.discardActiveObject() //선택해제 + modules.forEach((module) => { + const { top, left } = getPosotion(module, direction, length, true) + const moduleOptions = { ...module, left, top, id: uuidv4() } + const rect = new QPolygon(module.points, moduleOptions) + canvas.add(rect) + rect.setCoords() + canvas.renderAll() + + isOverlapOtherModules.push( + otherModules.some( + (otherModule) => + turf.booleanOverlap(polygonToTurfPolygon(rect, true), polygonToTurfPolygon(otherModule, true)) || + turf.booleanWithin(polygonToTurfPolygon(rect, true), polygonToTurfPolygon(otherModule, true)), + ), + ) + + isOverlapObjects.push(!checkModuleDisjointObjects(polygonToTurfPolygon(rect, true), objects)) + + isOutsideSurface.push( + !turf.booleanContains(polygonToTurfPolygon(moduleSetupSurface, true), polygonToTurfPolygon(rect, true)) || + !turf.booleanWithin(polygonToTurfPolygon(rect, true), polygonToTurfPolygon(moduleSetupSurface, true)), + ) + + if ( + isOverlapOtherModules.some((isOverlap) => isOverlap) || + isOutsideSurface.some((isOutside) => isOutside) || + isOverlapObjects.some((isOverlap) => isOverlap) + ) { + swalFire({ + title: isOverlapOtherModules ? '겹치는 모듈이 있습니다.' : isOutsideSurface ? '모듈이 오브젝트와 겹칩니다.' : '영역 밖', + icon: 'error', + type: 'confirm', + confirmFn: () => { + canvas.remove(rect) + canvas.renderAll() + }, + }) + } + }) + } + + const moduleMultiMove = (type, length, direction) => { + if (canvas.getActiveObjects().length === 0) return + if (canvas.getActiveObjects().length > 1) { + swalFire({ + title: '여러 개의 모듈을 선택할 수 없습니다.', + icon: 'error', + type: 'alert', + }) + canvas.discardActiveObject() + return + } + const activeModule = canvas.getObjects().filter((obj) => canvas.getActiveObjects()[0].id === obj.id)[0] + const modules = type === 'row' ? getRowModules(activeModule) : getColumnModules(activeModule) + const otherModules = getOtherModules(modules) + const objects = getObjects() + console.log('🚀 ~ moduleMultiMove ~ objects:', objects) + + const moduleSetupSurface = canvas + .getObjects() + .filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && obj.id === activeModule.surfaceId)[0] + let [isOverlapOtherModules, isOverlapObjects, isOutsideSurface] = [[], [], []] + + modules.forEach((module) => { + const { top, left } = getPosotion(module, direction, length, false) + module.originPos = { + top: module.top, + left: module.left, + } + + module.set({ top, left }) + module.setCoords() + canvas.renderAll() + + if (otherModules.length > 0) { + isOverlapOtherModules.push( + otherModules.some( + (otherModule) => + turf.booleanOverlap(polygonToTurfPolygon(module, true), polygonToTurfPolygon(otherModule, true)) || + turf.booleanWithin(polygonToTurfPolygon(module, true), polygonToTurfPolygon(otherModule, true)), + ), + ) + } + + if (objects.length > 0) { + isOverlapObjects.push(!checkModuleDisjointObjects(polygonToTurfPolygon(module, true), objects)) + } + + isOutsideSurface.push( + !turf.booleanContains(polygonToTurfPolygon(moduleSetupSurface, true), polygonToTurfPolygon(module, true)) || + !turf.booleanWithin(polygonToTurfPolygon(module, true), polygonToTurfPolygon(moduleSetupSurface, true)), + ) + }) + + if ( + isOverlapOtherModules.some((isOverlap) => isOverlap) || + isOverlapObjects.some((isOverlap) => isOverlap) || + isOutsideSurface.some((isOutside) => isOutside) + ) { + swalFire({ + title: isOverlapOtherModules.some((isOverlap) => isOverlap) + ? '겹치는 모듈이 있습니다.' + : isOverlapObjects.some((isOverlap) => isOverlap) + ? '모듈이 오브젝트와 겹칩니다.' + : '영역 밖', + icon: 'error', + type: 'confirm', + confirmFn: () => { + modules.forEach((module) => { + module.set({ top: module.originPos.top, left: module.originPos.left }) + module.setCoords() + }) + canvas.renderAll() + }, + }) + } + } + + const moduleMultiCopy = (type, length, direction) => { + if (canvas.getActiveObjects().length === 0) return + if (canvas.getActiveObjects().length > 1) { + swalFire({ + title: '여러 개의 모듈을 선택할 수 없습니다.', + icon: 'error', + type: 'alert', + }) + canvas.discardActiveObject() + return + } + const activeModule = canvas.getObjects().filter((obj) => canvas.getActiveObjects()[0].id === obj.id)[0] + const modules = type === 'row' ? getRowModules(activeModule) : getColumnModules(activeModule) + const otherModules = canvas.getObjects().filter((obj) => obj.surfaceId === modules[0].surfaceId && obj.name === POLYGON_TYPE.MODULE) + const objects = getObjects() + const copyRects = [] + const moduleSetupSurface = canvas + .getObjects() + .filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && obj.id === modules[0].surfaceId)[0] + let [isOverlapOtherModules, isOverlapObjects, isOutsideSurface] = [[], [], []] + let moduleLength = 0 + if (['up', 'down'].includes(direction)) { + moduleLength = Number(modules[modules.length - 1].top) + Number(modules[modules.length - 1].height) - Number(modules[0].top) + } else if (['left', 'right'].includes(direction)) { + moduleLength = Number(modules[modules.length - 1].left) + Number(modules[modules.length - 1].width) - Number(modules[0].left) + } + + modules.forEach((module) => { + const { top, left } = getPosotion(module, direction, Number(length) + Number(moduleLength), false) + const moduleOptions = { ...module, left, top, id: uuidv4() } + const rect = new QPolygon(module.getCurrentPoints(), moduleOptions) + canvas.add(rect) + copyRects.push(rect) + module.setCoords() + + if (otherModules.length > 0) { + isOverlapOtherModules.push( + otherModules.some( + (otherModule) => + turf.booleanOverlap(polygonToTurfPolygon(rect, true), polygonToTurfPolygon(otherModule, true)) || + turf.booleanWithin(polygonToTurfPolygon(rect, true), polygonToTurfPolygon(otherModule, true)), + ), + ) + } + + if (objects.length > 0) { + isOverlapObjects.push(!checkModuleDisjointObjects(polygonToTurfPolygon(module, true), objects)) + } + + isOutsideSurface.push( + !turf.booleanContains(polygonToTurfPolygon(moduleSetupSurface, true), polygonToTurfPolygon(rect, true)) || + !turf.booleanWithin(polygonToTurfPolygon(rect, true), polygonToTurfPolygon(moduleSetupSurface, true)), + ) + }) + + if ( + isOverlapOtherModules.some((isOverlap) => isOverlap) || + isOverlapObjects.some((isOverlap) => isOverlap) || + isOutsideSurface.some((isOutside) => isOutside) + ) { + swalFire({ + title: isOverlapOtherModules.some((isOverlap) => isOverlap) + ? '겹치는 모듈이 있습니다.' + : isOverlapObjects.some((isOverlap) => isOverlap) + ? '모듈이 오브젝트와 겹칩니다.' + : '영역 밖', + icon: 'error', + type: 'confirm', + confirmFn: () => { + canvas.remove(...copyRects) + canvas.renderAll() + }, + }) + } else { + canvas.renderAll() + } + } + + const moduleColumnRemove = (type) => { + const activeModule = canvas.getObjects().filter((obj) => canvas.getActiveObjects()[0].id === obj.id)[0] + const columnModules = getColumnModules(activeModule) + const otherModules = getOtherModules(columnModules) + const objects = getObjects() + let targetModules = [] + const rightModules = otherModules.filter((module) => activeModule.left < module.left).sort((a, b) => a.left - b.left) + const leftModules = otherModules.filter((module) => activeModule.left > module.left).sort((a, b) => b.left - a.left) + let width = -1 + const moduleSetupSurface = canvas + .getObjects() + .filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && obj.id === activeModule.surfaceId)[0] + let [isOverlapOtherModules, isOverlapObjects, isOutsideSurface] = [[], [], []] + canvas.discardActiveObject() + canvas.remove(...columnModules) + canvas.renderAll() + + if (type === MODULE_REMOVE_TYPE.LEFT) { + rightModules.forEach((module) => { + module.originPos = { + left: module.left, + top: module.top, + } + if (width === -1) width = module.left - activeModule.left + module.set({ left: module.left - width }) + module.setCoords() + canvas.renderAll() + isOverlapOtherModules.push(getIsOverlapOtherModules(module, leftModules)) + isOverlapObjects.push(!checkModuleDisjointObjects(polygonToTurfPolygon(module, true), objects)) + isOutsideSurface.push(getIsOutsideSurface(module, moduleSetupSurface)) + }) + targetModules = rightModules + } else if (type === MODULE_REMOVE_TYPE.RIGHT) { + leftModules.forEach((module) => { + module.originPos = { + left: module.left, + top: module.top, + } + if (width === -1) width = activeModule.left - module.left + module.set({ left: module.left + width }) + module.setCoords() + canvas.renderAll() + isOverlapOtherModules.push(getIsOverlapOtherModules(module, rightModules)) + isOverlapObjects.push(!checkModuleDisjointObjects(polygonToTurfPolygon(module, true), objects)) + isOutsideSurface.push(getIsOutsideSurface(module, moduleSetupSurface)) + }) + targetModules = leftModules + } else if (type === MODULE_REMOVE_TYPE.HORIZONTAL_SIDE) { + const sideModules = [...leftModules, ...rightModules] + leftModules.forEach((module) => { + module.originPos = { + left: module.left, + top: module.top, + } + if (width === -1) width = activeModule.left - module.left + module.set({ left: module.left + width / 2 }) + module.setCoords() + canvas.renderAll() + }) + + rightModules.forEach((module) => { + module.originPos = { + left: module.left, + top: module.top, + } + if (width === -1) width = module.left - activeModule.left + module.set({ left: module.left - width / 2 }) + module.setCoords() + canvas.renderAll() + }) + canvas.renderAll() + sideModules.forEach((module) => { + isOverlapOtherModules.push( + getIsOverlapOtherModules( + module, + sideModules.filter((m) => m.id !== module.id), + ), + ) + isOverlapObjects.push(!checkModuleDisjointObjects(polygonToTurfPolygon(module, true), objects)) + isOutsideSurface.push(getIsOutsideSurface(module, moduleSetupSurface)) + }) + + targetModules = sideModules + } + if ( + (isOverlapOtherModules.some((isOverlap) => isOverlap) || + isOverlapObjects.some((isOverlap) => isOverlap) || + isOutsideSurface.some((isOutside) => isOutside)) && + type !== MODULE_REMOVE_TYPE.NONE + ) { + swalFire({ + title: isOverlapOtherModules.some((isOverlap) => isOverlap) + ? '겹치는 모듈이 있습니다.' + : isOverlapObjects.some((isOverlap) => isOverlap) + ? '모듈이 오브젝트와 겹칩니다.' + : '영역 밖', + icon: 'error', + type: 'confirm', + confirmFn: () => { + canvas.add(...columnModules) + targetModules.forEach((module) => { + module.set({ top: module.originPos.top, left: module.originPos.left }) + module.setCoords() + }) + }, + }) + } + canvas.renderAll() + } + + const moduleRowRemove = (type) => { + const activeModule = canvas.getObjects().filter((obj) => canvas.getActiveObjects()[0].id === obj.id)[0] + const rowModules = getRowModules(activeModule) + const otherModules = getOtherModules(rowModules) + const objects = getObjects() + let targetModules = [] + const topModules = otherModules.filter((module) => activeModule.top > module.top).sort((a, b) => b.top - a.top) + const bottomModules = otherModules.filter((module) => activeModule.top < module.top).sort((a, b) => a.top - b.top) + let height = -1 + const moduleSetupSurface = canvas + .getObjects() + .filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && obj.id === activeModule.surfaceId)[0] + let [isOverlapOtherModules, isOverlapObjects, isOutsideSurface] = [[], [], []] + + canvas.discardActiveObject() + canvas.remove(...rowModules) + canvas.renderAll() + + if (type === MODULE_REMOVE_TYPE.TOP) { + bottomModules.forEach((module) => { + module.originPos = { + left: module.left, + top: module.top, + } + if (height === -1) height = module.top - activeModule.top + module.set({ top: module.top - height }) + module.setCoords() + canvas.renderAll() + isOverlapOtherModules.push(getIsOverlapOtherModules(module, topModules)) + isOverlapObjects.push(!checkModuleDisjointObjects(polygonToTurfPolygon(module, true), objects)) + isOutsideSurface.push(getIsOutsideSurface(module, moduleSetupSurface)) + }) + targetModules = bottomModules + } else if (type === MODULE_REMOVE_TYPE.BOTTOM) { + topModules.forEach((module) => { + module.originPos = { + left: module.left, + top: module.top, + } + if (height === -1) height = activeModule.top - module.top + module.set({ top: module.top + height }) + module.setCoords() + canvas.renderAll() + isOverlapOtherModules.push(getIsOverlapOtherModules(module, bottomModules)) + isOverlapObjects.push(!checkModuleDisjointObjects(polygonToTurfPolygon(module, true), objects)) + isOutsideSurface.push(getIsOutsideSurface(module, moduleSetupSurface)) + }) + targetModules = topModules + } else if (type === MODULE_REMOVE_TYPE.VERTICAL_SIDE) { + topModules.forEach((module) => { + module.originPos = { + left: module.left, + top: module.top, + } + if (height === -1) height = activeModule.top - module.top + module.set({ top: module.top + height / 2 }) + module.setCoords() + canvas.renderAll() + }) + + bottomModules.forEach((module) => { + module.originPos = { + left: module.left, + top: module.top, + } + if (height === -1) height = module.top - activeModule.top + module.set({ top: module.top - height / 2 }) + module.setCoords() + canvas.renderAll() + }) + + const sideModules = [...topModules, ...bottomModules] + canvas.renderAll() + sideModules.forEach((module) => { + isOverlapOtherModules.push( + getIsOverlapOtherModules( + module, + sideModules.filter((m) => m.id !== module.id), + ), + ) + isOverlapObjects.push(!checkModuleDisjointObjects(polygonToTurfPolygon(module, true), objects)) + isOutsideSurface.push(getIsOutsideSurface(module, moduleSetupSurface)) + }) + targetModules = sideModules + } + + if ( + (isOverlapOtherModules.some((isOverlap) => isOverlap) || + isOverlapObjects.some((isOverlap) => isOverlap) || + isOutsideSurface.some((isOutside) => isOutside)) && + type !== MODULE_REMOVE_TYPE.NONE + ) { + swalFire({ + title: isOverlapOtherModules.some((isOverlap) => isOverlap) + ? '겹치는 모듈이 있습니다.' + : isOverlapObjects.some((isOverlap) => isOverlap) + ? '모듈이 오브젝트와 겹칩니다.' + : '영역 밖', + icon: 'error', + type: 'confirm', + confirmFn: () => { + canvas.add(...rowModules) + targetModules.forEach((module) => { + module.set({ top: module.originPos.top, left: module.originPos.left }) + module.setCoords() + }) + }, + }) + } + canvas.renderAll() + } + + const moduleColumnInsert = (type) => { + const activeModule = canvas.getObjects().filter((obj) => canvas.getActiveObjects()[0].id === obj.id)[0] + const columnModules = getColumnModules(activeModule) + let otherModules = getOtherModules(columnModules) + const targetModules = + type === MODULE_INSERT_TYPE.LEFT + ? otherModules.filter((module) => module.left < activeModule.left).sort((a, b) => a.left - b.left) + : otherModules.filter((module) => module.left > activeModule.left).sort((a, b) => b.left - a.left) + const objects = getObjects() + const copyModules = [] + const moduleSetupSurface = canvas + .getObjects() + .filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && obj.id === activeModule.surfaceId)[0] + let width = -1 + let [isOverlapOtherModules, isOverlapObjects, isOutsideSurface] = [[], [], []] + if (targetModules.length === 0) { + swalFire({ + title: '마지막 모듈입니다.', + icon: 'error', + type: 'alert', + }) + return + } + canvas.discardActiveObject() + targetModules.forEach((module) => { + if (width === -1) + width = type === MODULE_INSERT_TYPE.LEFT ? Number(activeModule.left) - Number(module.left) : Number(module.left) - Number(activeModule.left) + const { top, left } = getPosotion(module, type, width, false) + module.originPos = { + left: module.left, + top: module.top, + } + module.set({ left, top }) + canvas.renderAll() + if (objects.length > 0) { + isOverlapObjects.push(!checkModuleDisjointObjects(polygonToTurfPolygon(module, true), objects)) + } + + isOutsideSurface.push( + !turf.booleanContains(polygonToTurfPolygon(moduleSetupSurface, true), polygonToTurfPolygon(module, true)) || + !turf.booleanWithin(polygonToTurfPolygon(module, true), polygonToTurfPolygon(moduleSetupSurface, true)), + ) + module.setCoords() + }) + otherModules = getOtherModules(columnModules) + columnModules.forEach((module) => { + const { top, left } = getPosotion(module, type, width, false) + const moduleOptions = { ...module, left, top, id: uuidv4() } + const copyModule = new QPolygon(module.points, moduleOptions) + canvas.add(copyModule) + copyModules.push(copyModule) + canvas.renderAll() + + if (otherModules.length > 0) { + isOverlapOtherModules.push( + otherModules.some( + (otherModule) => + turf.booleanOverlap(polygonToTurfPolygon(copyModule, true), polygonToTurfPolygon(otherModule, true)) || + turf.booleanWithin(polygonToTurfPolygon(copyModule, true), polygonToTurfPolygon(otherModule, true)), + ), + ) + } + + if (objects.length > 0) { + isOverlapObjects.push(!checkModuleDisjointObjects(polygonToTurfPolygon(module, true), objects)) + } + + isOutsideSurface.push( + !turf.booleanContains(polygonToTurfPolygon(moduleSetupSurface, true), polygonToTurfPolygon(copyModule, true)) || + !turf.booleanWithin(polygonToTurfPolygon(copyModule, true), polygonToTurfPolygon(moduleSetupSurface, true)), + ) + module.setCoords() + }) + + if ( + isOverlapOtherModules.some((isOverlap) => isOverlap) || + isOverlapObjects.some((isOverlap) => isOverlap) || + isOutsideSurface.some((isOutside) => isOutside) + ) { + swalFire({ + title: isOverlapOtherModules.some((isOverlap) => isOverlap) + ? '겹치는 모듈이 있습니다.' + : isOverlapObjects.some((isOverlap) => isOverlap) + ? '모듈이 오브젝트와 겹칩니다.' + : '영역 밖', + icon: 'error', + type: 'confirm', + confirmFn: () => { + targetModules.forEach((module) => { + module.set({ top: module.originPos.top, left: module.originPos.left }) + module.setCoords() + }) + canvas.renderAll() + copyModules.forEach((module) => { + canvas.remove(module) + }) + canvas.renderAll() + }, + }) + } + } + + const muduleRowInsert = (type) => { + console.log('🚀 ~ muduleRowInsert ~ type:', type) + const activeModule = canvas.getObjects().filter((obj) => canvas.getActiveObjects()[0].id === obj.id)[0] + const rowModules = getRowModules(activeModule) + console.log('🚀 ~ muduleRowInsert ~ rowModules:', rowModules) + let otherModules = getOtherModules(rowModules) + const targetModules = + type === MODULE_INSERT_TYPE.TOP + ? otherModules.filter((module) => module.top < activeModule.top).sort((a, b) => b.top - a.top) + : otherModules.filter((module) => module.top > activeModule.top).sort((a, b) => a.top - b.top) + console.log('🚀 ~ muduleRowInsert ~ targetModules:', targetModules) + if (targetModules.length === 0) { + swalFire({ + title: '마지막 모듈입니다.', + icon: 'error', + type: 'alert', + }) + return + } + const objects = getObjects() + const copyModules = [] + const moduleSetupSurface = canvas + .getObjects() + .filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && obj.id === activeModule.surfaceId)[0] + let height = -1 + let [isOverlapOtherModules, isOverlapObjects, isOutsideSurface] = [[], [], []] + canvas.discardActiveObject() + targetModules.forEach((module) => { + if (height === -1) + height = type === MODULE_INSERT_TYPE.TOP ? Number(activeModule.top) - Number(module.top) : Number(module.top) - Number(activeModule.top) + const { top, left } = getPosotion(module, type, height, false) + module.originPos = { + left: module.left, + top: module.top, + } + module.set({ left, top }) + canvas.renderAll() + if (objects.length > 0) { + isOverlapObjects.push(!checkModuleDisjointObjects(polygonToTurfPolygon(module, true), objects)) + } + + isOutsideSurface.push( + !turf.booleanContains(polygonToTurfPolygon(moduleSetupSurface, true), polygonToTurfPolygon(module, true)) || + !turf.booleanWithin(polygonToTurfPolygon(module, true), polygonToTurfPolygon(moduleSetupSurface, true)), + ) + module.setCoords() + }) + otherModules = getOtherModules(rowModules) + rowModules.forEach((module) => { + const { top, left } = getPosotion(module, type, height, false) + const moduleOptions = { ...module, left, top, id: uuidv4() } + const copyModule = new QPolygon(module.points, moduleOptions) + canvas.add(copyModule) + copyModules.push(copyModule) + canvas.renderAll() + + if (otherModules.length > 0) { + isOverlapOtherModules.push( + otherModules.some( + (otherModule) => + turf.booleanOverlap(polygonToTurfPolygon(copyModule, true), polygonToTurfPolygon(otherModule, true)) || + turf.booleanWithin(polygonToTurfPolygon(copyModule, true), polygonToTurfPolygon(otherModule, true)), + ), + ) + } + + if (objects.length > 0) { + isOverlapObjects.push(!checkModuleDisjointObjects(polygonToTurfPolygon(module, true), objects)) + } + + isOutsideSurface.push( + !turf.booleanContains(polygonToTurfPolygon(moduleSetupSurface, true), polygonToTurfPolygon(copyModule, true)) || + !turf.booleanWithin(polygonToTurfPolygon(copyModule, true), polygonToTurfPolygon(moduleSetupSurface, true)), + ) + module.setCoords() + }) + + if ( + isOverlapOtherModules.some((isOverlap) => isOverlap) || + isOverlapObjects.some((isOverlap) => isOverlap) || + isOutsideSurface.some((isOutside) => isOutside) + ) { + swalFire({ + title: isOverlapOtherModules.some((isOverlap) => isOverlap) + ? '겹치는 모듈이 있습니다.' + : isOverlapObjects.some((isOverlap) => isOverlap) + ? '모듈이 오브젝트와 겹칩니다.' + : '영역 밖', + icon: 'error', + type: 'confirm', + confirmFn: () => { + targetModules.forEach((module) => { + module.set({ top: module.originPos.top, left: module.originPos.left }) + module.setCoords() + }) + canvas.renderAll() + copyModules.forEach((module) => { + canvas.remove(module) + }) + canvas.renderAll() + }, + }) + } + } + + const getRowModules = (target) => { + return canvas + .getObjects() + .filter((obj) => target.surfaceId === obj.surfaceId && obj.name === POLYGON_TYPE.MODULE && obj.top === target.top) + .sort((a, b) => a.left - b.left) + } + + const getColumnModules = (target) => { + return canvas + .getObjects() + .filter((obj) => target.surfaceId === obj.surfaceId && obj.name === POLYGON_TYPE.MODULE && obj.left === target.left) + .sort((a, b) => a.top - b.top) + } + + const getPosotion = (target, direction, length, hasMargin = false) => { + let top = target.top + let left = target.left + + if (direction === 'up') { + top = Number(target.top) - Number(length) + top = hasMargin ? top - Number(target.height) : top + } else if (direction === 'down') { + top = Number(target.top) + Number(length) + top = hasMargin ? top + Number(target.height) : top + } else if (direction === 'left') { + left = Number(target.left) - Number(length) + left = hasMargin ? left - Number(target.width) : left + } else if (direction === 'right') { + left = Number(target.left) + Number(length) + left = hasMargin ? left + Number(target.width) : left + } + return { top, left } + } + + const getOtherModules = (modules) => { + const moduleIds = modules.map((module) => module.id) + return canvas + .getObjects() + .filter((obj) => obj.surfaceId === modules[0].surfaceId && obj.name === POLYGON_TYPE.MODULE && !moduleIds.includes(obj.id)) + } + + const getObjects = () => { + return canvas + ?.getObjects() + .filter((obj) => [BATCH_TYPE.OPENING, BATCH_TYPE.TRIANGLE_DORMER, BATCH_TYPE.PENTAGON_DORMER, BATCH_TYPE.SHADOW].includes(obj.name)) + } + + const getIsOverlapOtherModules = (module, otherModules) => { + return otherModules.some( + (otherModule) => + turf.booleanOverlap(polygonToTurfPolygon(module, true), polygonToTurfPolygon(otherModule, true)) || + turf.booleanWithin(polygonToTurfPolygon(module, true), polygonToTurfPolygon(otherModule, true)), + ) + } + + const getIsOverlapObjects = (module, objects) => { + return !objects.some( + (object) => + turf.booleanOverlap(polygonToTurfPolygon(module, true), polygonToTurfPolygon(object, true)) || + turf.booleanWithin(polygonToTurfPolygon(module, true), polygonToTurfPolygon(object, true)), + ) + } + + const getIsOutsideSurface = (module, moduleSetupSurface) => { + return ( + !turf.booleanContains(polygonToTurfPolygon(moduleSetupSurface, true), polygonToTurfPolygon(module, true)) || + !turf.booleanWithin(polygonToTurfPolygon(module, true), polygonToTurfPolygon(moduleSetupSurface, true)) + ) + } + + return { + moduleMove, + moduleMultiMove, + moduleCopy, + moduleMultiCopy, + moduleColumnRemove, + moduleRowRemove, + moduleColumnInsert, + muduleRowInsert, + } +} diff --git a/src/hooks/module/useModuleBasicSetting.js b/src/hooks/module/useModuleBasicSetting.js index d9f8ae84..f654145d 100644 --- a/src/hooks/module/useModuleBasicSetting.js +++ b/src/hooks/module/useModuleBasicSetting.js @@ -428,7 +428,8 @@ export function useModuleBasicSetting() { dormerTurfPolygon = batchObjectGroupToTurfPolygon(object) } else { //개구, 그림자 - dormerTurfPolygon = polygonToTurfPolygon(rectToPolygon(object)) + object.set({ points: rectToPolygon(object) }) + dormerTurfPolygon = polygonToTurfPolygon(object) } const intersection = turf.intersect(turf.featureCollection([dormerTurfPolygon, tempTurfModule])) //겹치는지 확인 @@ -567,35 +568,35 @@ export function useModuleBasicSetting() { return containsBatchObjects } - /** - * 도머나 개구가 모듈에 걸치는지 확인하는 로직 - * @param {*} squarePolygon - * @param {*} containsBatchObjects - * @returns - */ - const checkModuleDisjointObjects = (squarePolygon, containsBatchObjects) => { - let isDisjoint = false - - if (containsBatchObjects.length > 0) { - let convertBatchObject - //도머가 있으면 적용되는 로직 - isDisjoint = containsBatchObjects.every((batchObject) => { - if (batchObject.type === 'group') { - convertBatchObject = batchObjectGroupToTurfPolygon(batchObject) - } else { - convertBatchObject = polygonToTurfPolygon(batchObject) - } - /** - * 도머가 여러개일수있으므로 겹치는게 있다면... - * 안겹치는지 확인하는 로직이라 안겹치면 true를 반환 - */ - return turf.booleanDisjoint(squarePolygon, convertBatchObject) - }) - } else { - isDisjoint = true - } - return isDisjoint - } + // /** + // * 도머나 개구가 모듈에 걸치는지 확인하는 로직 + // * @param {*} squarePolygon + // * @param {*} containsBatchObjects + // * @returns + // */ + // const checkModuleDisjointObjects = (squarePolygon, containsBatchObjects) => { + // let isDisjoint = false + // + // if (containsBatchObjects.length > 0) { + // let convertBatchObject + // //도머가 있으면 적용되는 로직 + // isDisjoint = containsBatchObjects.every((batchObject) => { + // if (batchObject.type === 'group') { + // convertBatchObject = batchObjectGroupToTurfPolygon(batchObject) + // } else { + // convertBatchObject = polygonToTurfPolygon(batchObject) + // } + // /** + // * 도머가 여러개일수있으므로 겹치는게 있다면... + // * 안겹치는지 확인하는 로직이라 안겹치면 true를 반환 + // */ + // return turf.booleanDisjoint(squarePolygon, convertBatchObject) + // }) + // } else { + // isDisjoint = true + // } + // return isDisjoint + // } /** * 배치면 안에 있는지 확인 @@ -2141,36 +2142,6 @@ export function useModuleBasicSetting() { return containsBatchObjects } - /** - * 도머나 개구가 모듈에 걸치는지 확인하는 로직 - * @param {*} squarePolygon - * @param {*} containsBatchObjects - * @returns - */ - const checkModuleDisjointObjects = (squarePolygon, containsBatchObjects) => { - let isDisjoint = false - - if (containsBatchObjects.length > 0) { - let convertBatchObject - //도머가 있으면 적용되는 로직 - isDisjoint = containsBatchObjects.every((batchObject) => { - if (batchObject.type === 'group') { - convertBatchObject = batchObjectGroupToTurfPolygon(batchObject) - } else { - convertBatchObject = polygonToTurfPolygon(batchObject) - } - /** - * 도머가 여러개일수있으므로 겹치는게 있다면... - * 안겹치는지 확인하는 로직이라 안겹치면 true를 반환 - */ - return turf.booleanDisjoint(squarePolygon, convertBatchObject) - }) - } else { - isDisjoint = true - } - return isDisjoint - } - /** * 배치면 안에 있는지 확인 * @param {*} squarePolygon @@ -2431,6 +2402,39 @@ export function useModuleBasicSetting() { //드래그 하기위해 기능 활성화 } + /** + * 도머나 개구가 모듈에 걸치는지 확인하는 로직 + * @param {*} squarePolygon + * @param {*} containsBatchObjects + * @returns + */ + const checkModuleDisjointObjects = (squarePolygon, containsBatchObjects) => { + let isDisjoint = false + + if (containsBatchObjects.length > 0) { + let convertBatchObject + //도머가 있으면 적용되는 로직 + isDisjoint = containsBatchObjects.every((batchObject) => { + if (batchObject.type === 'group') { + convertBatchObject = batchObjectGroupToTurfPolygon(batchObject) + } else { + if (!batchObject.points) { + batchObject.set({ points: rectToPolygon(batchObject) }) + } + convertBatchObject = polygonToTurfPolygon(batchObject) + } + /** + * 도머가 여러개일수있으므로 겹치는게 있다면... + * 안겹치는지 확인하는 로직이라 안겹치면 true를 반환 + */ + return turf.booleanDisjoint(squarePolygon, convertBatchObject) + }) + } else { + isDisjoint = true + } + return isDisjoint + } + return { makeModuleInstArea, manualModuleSetup, @@ -2438,5 +2442,6 @@ export function useModuleBasicSetting() { restoreModuleInstArea, manualFlatroofModuleSetup, autoFlatroofModuleSetup, + checkModuleDisjointObjects, } } diff --git a/src/hooks/option/useCanvasSetting.js b/src/hooks/option/useCanvasSetting.js index 441a06030..1c1dca34 100644 --- a/src/hooks/option/useCanvasSetting.js +++ b/src/hooks/option/useCanvasSetting.js @@ -269,17 +269,6 @@ export function useCanvasSetting() { roofSizeSet: item.roofSizeSet, roofAngleSet: item.roofAngleSet, } - - roofsArray = { - roofApply: true, - roofSeq: 1, - roofMatlCd: 'ROOF_ID_WA_53A', - roofWidth: 265, - roofHeight: 235, - roofHajebichi: 0, - roofGap: 'HEI_455', - roofLayout: 'P', - } }) } else { roofsRow = [ diff --git a/src/hooks/roofcover/useRoofAllocationSetting.js b/src/hooks/roofcover/useRoofAllocationSetting.js index a2c7b483..6c1ace5c 100644 --- a/src/hooks/roofcover/useRoofAllocationSetting.js +++ b/src/hooks/roofcover/useRoofAllocationSetting.js @@ -13,6 +13,7 @@ import useMenu from '@/hooks/common/useMenu' import { useCanvasMenu } from '@/hooks/common/useCanvasMenu' import { menuTypeState } from '@/store/menuAtom' import { useRoofFn } from '@/hooks/common/useRoofFn' +import { ROOF_MATERIAL_LAYOUT } from '@/components/floor-plan/modal/placementShape/PlacementShapeSetting' // 지붕면 할당 export function useRoofAllocationSetting(id) { @@ -32,7 +33,7 @@ export function useRoofAllocationSetting(id) { const [currentRoofMaterial, setCurrentRoofMaterial] = useState(roofMaterials[0]) // 팝업 내 기준 지붕재 const [roofList, setRoofList] = useRecoilState(addedRoofsState) // 배치면 초기설정에서 선택한 지붕재 배열 const [editingLines, setEditingLines] = useState([]) - const isFirstRef = useRef(0) + const [currentRoofList, setCurrentRoofList] = useState(roofList) const { setSurfaceShapePattern } = useRoofFn() @@ -76,20 +77,25 @@ export function useRoofAllocationSetting(id) { }, []) const onAddRoofMaterial = () => { - if (roofList.length >= 4) { + if (currentRoofList.length >= 4) { swalFire({ type: 'alert', icon: 'error', text: getMessage('지붕재는 4개까지 선택 가능합니다.') }) return } - setRoofList([...roofList, { ...currentRoofMaterial, selected: false, id: currentRoofMaterial.roofMatlCd, name: currentRoofMaterial.roofMatlNm }]) + setCurrentRoofList([ + ...currentRoofList, + { ...currentRoofMaterial, selected: false, id: currentRoofMaterial.roofMatlCd, name: currentRoofMaterial.roofMatlNm }, + ]) } const onDeleteRoofMaterial = (idx) => { - setRoofList([...roofList.filter((_, index) => index !== idx)]) + const isSelected = currentRoofList[idx].selected + const newRoofList = [...currentRoofList].filter((_, index) => index !== idx) + if (isSelected) { + newRoofList[0].selected = true + } + setCurrentRoofList(newRoofList) } - const { handleMenu } = useMenu() - const [currentMenu, setCurrentMenu] = useRecoilState(currentMenuState) - // 선택한 지붕재로 할당 const handleSave = () => { // 모두 actualSize 있으면 바로 적용 없으면 actualSize 설정 @@ -102,7 +108,7 @@ export function useRoofAllocationSetting(id) { // 지붕재 오른쪽 마우스 클릭 후 단일로 지붕재 변경 필요한 경우 const handleSaveContext = () => { - const selectedRoofMaterial = roofList.find((roof) => roof.selected) + const selectedRoofMaterial = currentRoofList.find((roof) => roof.selected) setSurfaceShapePattern(currentObject, roofDisplay.column, false, selectedRoofMaterial) closeAll() } @@ -169,11 +175,11 @@ export function useRoofAllocationSetting(id) { setBasicSetting((prev) => { return { ...prev, - selectedRoofMaterial: roofList.find((roof) => roof.selected), + selectedRoofMaterial: currentRoofList.find((roof) => roof.selected), } }) - setRoofList(roofList) + setRoofList(currentRoofList) const roofs = canvas.getObjects().filter((obj) => obj.name === 'roof') @@ -182,7 +188,12 @@ export function useRoofAllocationSetting(id) { roof.set({ isFixed: true, }) - setSurfaceShapePattern(roof, roofDisplay.column) + setSurfaceShapePattern( + roof, + roofDisplay.column, + false, + currentRoofList.find((roof) => roof.selected), + ) drawDirectionArrow(roof) }) @@ -216,54 +227,53 @@ export function useRoofAllocationSetting(id) { // 지붕재 변경 const handleChangeRoofMaterial = (value, index) => { - if (isFirstRef.current === 0) { - isFirstRef.current++ - return - } + const selectedIndex = roofMaterials.findIndex((roof) => roof.selected) const selectedRoofMaterial = roofMaterials.find((roof) => roof.roofMatlCd === value.id) - const newRoofList = roofList.map((roof, idx) => { + const newRoofList = currentRoofList.map((roof, idx) => { if (idx === index) { return { ...selectedRoofMaterial } } return roof }) - setRoofList(newRoofList) + newRoofList[selectedIndex].selected = true + + setCurrentRoofList(newRoofList) } // 기본 지붕재 radio값 변경 const handleDefaultRoofMaterial = (index) => { - const newRoofList = roofList.map((roof, idx) => { + const newRoofList = currentRoofList.map((roof, idx) => { return { ...roof, selected: idx === index } }) - setRoofList(newRoofList) + setCurrentRoofList(newRoofList) } // 서까래 변경 const handleChangeRaft = (e, index) => { - const raftValue = e.value + const raftValue = e.clCode - const newRoofList = roofList.map((roof, idx) => { + const newRoofList = currentRoofList.map((roof, idx) => { if (idx === index) { return { ...roof, raft: raftValue } } return roof }) - setRoofList(newRoofList) + setCurrentRoofList(newRoofList) } const handleChangeLayout = (layoutValue, index) => { - const newRoofList = roofList.map((roof, idx) => { + const newRoofList = currentRoofList.map((roof, idx) => { if (idx === index) { return { ...roof, layout: layoutValue } } return roof }) - setRoofList(newRoofList) + setCurrentRoofList(newRoofList) } return { @@ -278,11 +288,11 @@ export function useRoofAllocationSetting(id) { setBasicSetting, currentRoofMaterial, setCurrentRoofMaterial, - roofList, handleDefaultRoofMaterial, handleChangeRoofMaterial, handleChangeRaft, handleChangeLayout, handleSaveContext, + currentRoofList, } } diff --git a/src/hooks/useContextMenu.js b/src/hooks/useContextMenu.js index 38564f7b..e0a2cf0f 100644 --- a/src/hooks/useContextMenu.js +++ b/src/hooks/useContextMenu.js @@ -23,7 +23,7 @@ import { useCommonUtils } from './common/useCommonUtils' import { useMessage } from '@/hooks/useMessage' import { useCanvasEvent } from '@/hooks/useCanvasEvent' import { contextMenuListState, contextMenuState } from '@/store/contextMenu' -import PanelEdit from '@/components/floor-plan/modal/module/PanelEdit' +import PanelEdit, { PANEL_EDIT_TYPE } from '@/components/floor-plan/modal/module/PanelEdit' import DimensionLineSetting from '@/components/floor-plan/modal/dimensionLine/DimensionLineSetting' import ColumnRemove from '@/components/floor-plan/modal/module/column/ColumnRemove' import ColumnInsert from '@/components/floor-plan/modal/module/column/ColumnInsert' @@ -36,6 +36,7 @@ import { fontSelector, globalFontAtom } from '@/store/fontAtom' import { useLine } from '@/hooks/useLine' import { useSwal } from '@/hooks/useSwal' import ContextRoofAllocationSetting from '@/components/floor-plan/modal/roofAllocation/ContextRoofAllocationSetting' +import { useRoofFn } from '@/hooks/common/useRoofFn' export function useContextMenu() { const canvas = useRecoilValue(canvasState) @@ -59,6 +60,7 @@ export function useContextMenu() { const { addLine, removeLine } = useLine() const commonTextFont = useRecoilValue(fontSelector('commonText')) const { swalFire } = useSwal() + const { removeRoofMaterial, removeAllRoofMaterial } = useRoofFn() const currentMenuSetting = () => { switch (currentMenu) { @@ -110,10 +112,12 @@ export function useContextMenu() { { id: 'roofMaterialRemove', name: getMessage('contextmenu.roof.material.remove'), + fn: () => removeRoofMaterial(), }, { id: 'roofMaterialRemoveAll', name: getMessage('contextmenu.roof.material.remove.all'), + fn: () => removeAllRoofMaterial(), }, { id: 'selectMove', @@ -609,24 +613,24 @@ export function useContextMenu() { { id: 'move', name: getMessage('contextmenu.move'), - component: , + component: , }, { id: 'copy', name: getMessage('contextmenu.copy'), - component: , + component: , }, ], [ { id: 'columnMove', name: getMessage('contextmenu.column.move'), - component: , + component: , }, { id: 'columnCopy', name: getMessage('contextmenu.column.copy'), - component: , + component: , }, { id: 'columnRemove', @@ -643,12 +647,12 @@ export function useContextMenu() { { id: 'rowMove', name: getMessage('contextmenu.row.move'), - component: , + component: , }, { id: 'rowCopy', name: getMessage('contextmenu.row.copy'), - component: , + component: , }, { id: 'rowRemove', diff --git a/src/store/settingAtom.js b/src/store/settingAtom.js index 2350b0f5..1b837fdd 100644 --- a/src/store/settingAtom.js +++ b/src/store/settingAtom.js @@ -224,9 +224,9 @@ export const roofMaterialsAtom = atom({ export const selectedRoofMaterialSelector = selector({ key: 'selectedRoofMaterialSelector', get: ({ get }) => { - const basicSetting = get(basicSettingState) + const addedRoofs = get(addedRoofsState) - return { ...basicSetting.selectedRoofMaterial } + return addedRoofs.find((roof) => roof.selected) }, }) @@ -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, -}) - /** * 현재 선택된 물건 번호 */