From 320f6f9e1fd63a019907a91d4c944e836314ccff Mon Sep 17 00:00:00 2001 From: minsik Date: Mon, 30 Sep 2024 09:45:14 +0900 Subject: [PATCH 01/11] chore: withDraggable -> WithDraggable change file name --- .../common/draggable/{withDraggable.jsx => WithDraggable.jsx} | 0 src/components/floor-plan/FloorPlan.jsx | 2 ++ src/components/floor-plan/modal/grid/DotLineGrid.jsx | 2 +- src/components/floor-plan/modal/grid/GridCopy.jsx | 2 +- src/components/floor-plan/modal/grid/GridMove.jsx | 2 +- .../floor-plan/modal/outerlinesetting/WallLineSetting.jsx | 2 +- src/components/floor-plan/modal/setting01/SettingModal01.jsx | 2 +- 7 files changed, 7 insertions(+), 5 deletions(-) rename src/components/common/draggable/{withDraggable.jsx => WithDraggable.jsx} (100%) diff --git a/src/components/common/draggable/withDraggable.jsx b/src/components/common/draggable/WithDraggable.jsx similarity index 100% rename from src/components/common/draggable/withDraggable.jsx rename to src/components/common/draggable/WithDraggable.jsx diff --git a/src/components/floor-plan/FloorPlan.jsx b/src/components/floor-plan/FloorPlan.jsx index 699c96c5..513f870a 100644 --- a/src/components/floor-plan/FloorPlan.jsx +++ b/src/components/floor-plan/FloorPlan.jsx @@ -11,6 +11,7 @@ import SettingModal01 from '@/components/floor-plan/modal/setting01/SettingModal import CanvasLayout from '@/components/floor-plan/CanvasLayout' import DotLineGrid from '@/components/floor-plan/modal/grid/DotLineGrid' import WallLineSetting from '@/components/floor-plan/modal/outerlinesetting/WallLineSetting' +import PropertiesSetting from '@/components/floor-plan/modal/outerlinesetting/PropertiesSetting' export default function FloorPlan() { const [showCanvasSettingModal, setShowCanvasSettingModal] = useState(false) @@ -87,6 +88,7 @@ export default function FloorPlan() { {/*{showOutlineModal && }*/} {showOutlineModal && } {showDotLineGridModal && } + diff --git a/src/components/floor-plan/modal/grid/DotLineGrid.jsx b/src/components/floor-plan/modal/grid/DotLineGrid.jsx index db700207..1609f5a0 100644 --- a/src/components/floor-plan/modal/grid/DotLineGrid.jsx +++ b/src/components/floor-plan/modal/grid/DotLineGrid.jsx @@ -1,4 +1,4 @@ -import WithDraggable from '@/components/common/draggable/withDraggable' +import WithDraggable from '@/components/common/draggable/WithDraggable' import QSelectBox from '@/components/common/select/QSelectBox' import { useState } from 'react' import { useMessage } from '@/hooks/useMessage' diff --git a/src/components/floor-plan/modal/grid/GridCopy.jsx b/src/components/floor-plan/modal/grid/GridCopy.jsx index 92609d80..a6709d09 100644 --- a/src/components/floor-plan/modal/grid/GridCopy.jsx +++ b/src/components/floor-plan/modal/grid/GridCopy.jsx @@ -1,4 +1,4 @@ -import WithDraggable from '@/components/common/draggable/withDraggable' +import WithDraggable from '@/components/common/draggable/WithDraggable' import { useMessage } from '@/hooks/useMessage' export default function GridCopy(props) { diff --git a/src/components/floor-plan/modal/grid/GridMove.jsx b/src/components/floor-plan/modal/grid/GridMove.jsx index 1da8b6fa..db226872 100644 --- a/src/components/floor-plan/modal/grid/GridMove.jsx +++ b/src/components/floor-plan/modal/grid/GridMove.jsx @@ -1,4 +1,4 @@ -import WithDraggable from '@/components/common/draggable/withDraggable' +import WithDraggable from '@/components/common/draggable/WithDraggable' import { useMessage } from '@/hooks/useMessage' export default function GridMove(props) { diff --git a/src/components/floor-plan/modal/outerlinesetting/WallLineSetting.jsx b/src/components/floor-plan/modal/outerlinesetting/WallLineSetting.jsx index 7e90bfb9..cff90c61 100644 --- a/src/components/floor-plan/modal/outerlinesetting/WallLineSetting.jsx +++ b/src/components/floor-plan/modal/outerlinesetting/WallLineSetting.jsx @@ -1,6 +1,6 @@ 'use client' -import WithDraggable from '@/components/common/draggable/withDraggable' +import WithDraggable from '@/components/common/draggable/WithDraggable' import { useMessage } from '@/hooks/useMessage' import { OUTER_LINE_TYPE } from '@/store/outerLineAtom' import { useOuterLineWall } from '@/hooks/roofcover/useOuterLineWall' diff --git a/src/components/floor-plan/modal/setting01/SettingModal01.jsx b/src/components/floor-plan/modal/setting01/SettingModal01.jsx index 293d29df..263fc924 100644 --- a/src/components/floor-plan/modal/setting01/SettingModal01.jsx +++ b/src/components/floor-plan/modal/setting01/SettingModal01.jsx @@ -2,7 +2,7 @@ import { useState } from 'react' import FirstOption from './FirstOption' -import WithDraggable from '@/components/common/draggable/withDraggable' +import WithDraggable from '@/components/common/draggable/WithDraggable' import SecondOption from '@/components/floor-plan/modal/setting01/SecondOption' import { useMessage } from '@/hooks/useMessage' import GridOption from '@/components/floor-plan/modal/setting01/GridOption' From 202e91811fdf0b2a3008effdd18118b2592a6cac Mon Sep 17 00:00:00 2001 From: minsik Date: Mon, 30 Sep 2024 09:45:56 +0900 Subject: [PATCH 02/11] feat: PropertiesSetting --- .../outerlinesetting/PropertiesSetting.js | 30 + src/locales/ja.json | 6 + src/locales/ko.json | 6 + src/styles/_modal.scss | 969 ++++++++++-------- 4 files changed, 602 insertions(+), 409 deletions(-) create mode 100644 src/components/floor-plan/modal/outerlinesetting/PropertiesSetting.js diff --git a/src/components/floor-plan/modal/outerlinesetting/PropertiesSetting.js b/src/components/floor-plan/modal/outerlinesetting/PropertiesSetting.js new file mode 100644 index 00000000..1ff975a7 --- /dev/null +++ b/src/components/floor-plan/modal/outerlinesetting/PropertiesSetting.js @@ -0,0 +1,30 @@ +import WithDraggable from '@/components/common/draggable/WithDraggable' +import { useMessage } from '@/hooks/useMessage' + +export default function PropertiesSetting() { + const { getMessage } = useMessage() + return ( + +
+
+

{getMessage('modal.canvas.setting.wallline.properties.setting')}

+ +
+
+
{getMessage('modal.canvas.setting.wallline.properties.setting.info')}
+
+
{getMessage('setting')}
+
+ + +
+
+
+ + +
+
+
+
+ ) +} diff --git a/src/locales/ja.json b/src/locales/ja.json index 8e62d630..06429bc6 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -32,6 +32,7 @@ "modal.cover.outline.arrow": "方向 (矢印)", "modal.cover.outline.fix": "外壁線確定", "modal.cover.outline.rollback": "一変戦に戻る", + "modal.cover.outline.finish": "設定完了", "modal.cover.outline.remove": "外壁の削除", "modal.cover.outline.select.move": "外壁の選択、移動", "plan.menu.roof.cover.roof.setting": "屋根形状設定", @@ -115,6 +116,11 @@ "modal.canvas.setting.first.option.border": "ボーダーのみ", "modal.canvas.setting.first.option.line": "ラインハッチ", "modal.canvas.setting.first.option.all": "All painted", + "modal.canvas.setting.wallline.properties.setting": "外壁のプロパティの設定", + "modal.canvas.setting.wallline.properties.setting.info": "※属性を変更する外壁線を選択し、軒で設定またはケラバで設定 ボタンをクリックして設定値を適用します。", + "modal.canvas.setting.wallline.properties.setting.eaves": "軒で設定", + "modal.canvas.setting.wallline.properties.setting.edge": "ケラバに設定", + "setting": "設定", "common.message.no.data": "No data", "common.message.no.dataDown": "ダウンロードするデータがありません", "common.message.noData": "表示するデータがありません", diff --git a/src/locales/ko.json b/src/locales/ko.json index 9bf624d6..b3e2decd 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -36,6 +36,7 @@ "modal.cover.outline.arrow": "방향(화살표)", "modal.cover.outline.fix": "외벽선 확정", "modal.cover.outline.rollback": "일변전으로 돌아가기", + "modal.cover.outline.finish": "설정완료", "modal.cover.outline.remove": "외벽 제거", "modal.cover.outline.select.move": "외벽 선택, 이동", "plan.menu.placement.surface": "배치면", @@ -116,6 +117,11 @@ "modal.canvas.setting.first.option.border": "테두리만", "modal.canvas.setting.first.option.line": "라인해치", "modal.canvas.setting.first.option.all": "All painted", + "modal.canvas.setting.wallline.properties.setting": "외벽선 속성 설정", + "modal.canvas.setting.wallline.properties.setting.info": "※ 속성을 변경할 외벽선을 선택하고, 처마로 설정 또는 케라바로 설정\n 버튼을 클릭하여 설정값을 적용하십시오.\n", + "modal.canvas.setting.wallline.properties.setting.eaves": "처마로 설정", + "modal.canvas.setting.wallline.properties.setting.edge": "케라바로 설정", + "setting": "설정", "common.message.no.data": "No data", "common.message.no.dataDown": "No data to download", "common.message.noData": "No data to display", diff --git a/src/styles/_modal.scss b/src/styles/_modal.scss index 6309f50e..0ad517a3 100644 --- a/src/styles/_modal.scss +++ b/src/styles/_modal.scss @@ -4,466 +4,617 @@ $pop-bold-weight: 500; $pop-normal-size: 12px; $alert-color: #101010; -@keyframes mountpop{ - from{opacity: 0; scale: 0.95;} - to{opacity: 1; scale: 1;} -} -@keyframes unmountpop{ - from{opacity: 1; scale: 1;} - to{opacity: 0; scale: 0.95;} +@keyframes mountpop { + from { + opacity: 0; + scale: 0.95; + } + to { + opacity: 1; + scale: 1; + } } -.modal-pop-wrap{ - position: fixed; - top: 200px; - right: 100px; - width: 100%; - min-width: 300px; - height: -webkit-fit-content; - height: -moz-fit-content; - height: fit-content; - border: 1px solid #000; - border-radius: 4px; - background-color: #272727; - z-index: 9999999; - overflow: hidden; - &.r{ - width: 400px; - } - &.sm{ - width: 580px; - } - &.ssm{ - width: 380px; - } - &.xm{ - width: 300px; - } - &.l{ - width: 800px; - } - &.mount{ - animation: mountpop .17s ease-in-out forwards; - } - &.unmount{ - animation: unmountpop .17s ease-in-out forwards; - } - &.alert{ - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - background-color: transparent; - border: none; - .modal-head{ - background-color: transparent; - padding: 0 0 8px; - .modal-close{ - width: 20px; - height: 20px; - background: url(../../public/static/images/canvas/alert_close.svg)no-repeat center; - } - } - .modal-body{ - background-color: #fff; - padding: 22px; - border-radius: 4px; - border: 1px solid #101010; - color: $alert-color; - .alert-title{ - font-size: 13px; - font-weight: 700; - color: $alert-color; - margin-bottom: 15px; - } - } - } +@keyframes unmountpop { + from { + opacity: 1; + scale: 1; + } + to { + opacity: 0; + scale: 0.95; + } } -.modal-head{ - display: flex; - align-items: center; - padding: 10px 24px; - background-color: #000; - h1.title{ + +.modal-pop-wrap { + position: fixed; + top: 200px; + right: 100px; + width: 100%; + min-width: 300px; + height: -webkit-fit-content; + height: -moz-fit-content; + height: fit-content; + border: 1px solid #000; + border-radius: 4px; + background-color: #272727; + z-index: 9999999; + overflow: hidden; + + &.r { + width: 400px; + } + + &.sm { + width: 580px; + } + + &.ssm { + width: 380px; + } + + &.xm { + width: 300px; + } + + &.l { + width: 800px; + } + + &.mount { + animation: mountpop .17s ease-in-out forwards; + } + + &.unmount { + animation: unmountpop .17s ease-in-out forwards; + } + + &.alert { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background-color: transparent; + border: none; + + .modal-head { + background-color: transparent; + padding: 0 0 8px; + + .modal-close { + width: 20px; + height: 20px; + background: url(../../public/static/images/canvas/alert_close.svg) no-repeat center; + } + } + + .modal-body { + background-color: #fff; + padding: 22px; + border-radius: 4px; + border: 1px solid #101010; + color: $alert-color; + + .alert-title { font-size: 13px; - color: $pop-color; font-weight: 700; - } - .modal-close{ - margin-left: auto; - color: $pop-color; - text-indent: -999999999px; - width: 10px; - height: 10px; - background: url(../../public/static/images/canvas/modal_close.svg)no-repeat center; - } -} -.modal-body{ - padding: 24px; - .modal-btn-wrap{ - display: flex; - align-items: center; - gap: 5px; - button{ - flex: 1; - } - } - .modal-check-btn-wrap{ - margin-top: 15px; - .check-wrap-title{ - font-size: $pop-normal-size; - color: $pop-color; - font-weight: 600; - &.light{ - font-weight: $pop-normal-weight; - } - } - .flex-check-box{ - display: flex; - flex-wrap: wrap; - gap: 10px; - margin-top: 15px; - &.for2{ - justify-content: flex-end; - button{ - width: calc(50% - 5px); - } - &.btn{ - gap: 5px; - button{ - width: calc(50% - 2.5px); - } - } - } - &.for-line{ - button{ - flex: 1; - } - } - } - } - .outer-line-wrap{ - border-top: 1px solid #3C3C3C; - margin-top: 10px; - padding-top: 15px; - margin-bottom: 15px; - > div{ - margin-bottom: 15px; - &:last-child{ - margin-bottom: 0; - } - } - } - .modal-guide{ - display: block; - font-size: $pop-normal-size; color: $alert-color; - font-weight: $pop-normal-weight; + margin-bottom: 15px; + } } + } } -.adsorption-point{ +.modal-head { + display: flex; + align-items: center; + padding: 10px 24px; + background-color: #000; + + h1.title { + font-size: 13px; + color: $pop-color; + font-weight: 700; + } + + .modal-close { + margin-left: auto; + color: $pop-color; + text-indent: -999999999px; + width: 10px; + height: 10px; + background: url(../../public/static/images/canvas/modal_close.svg) no-repeat center; + } +} + +.modal-body { + padding: 24px; + + .modal-btn-wrap { display: flex; align-items: center; - background-color: #3A3A3A; - border-radius: 3px; - padding-left: 11px; - overflow: hidden; - transition: all 0.17s ease-in-out; - span{ - font-size: $pop-normal-size; - color: #898989; + gap: 5px; + + button { + flex: 1; } - i{ - display: flex; - align-items: center; - padding: 0 7px; - margin-left: auto; - height: 100%; - font-size: 13px; - color: #898989; + } + + .modal-check-btn-wrap { + margin-top: 15px; + + .check-wrap-title { + font-size: $pop-normal-size; + color: $pop-color; + font-weight: 600; + + &.light { + font-weight: $pop-normal-weight; + } } - &.act{ - i{ - color: $pop-color; - background-color: #1083E3; + + .flex-check-box { + display: flex; + flex-wrap: wrap; + gap: 10px; + margin-top: 15px; + + &.for2 { + justify-content: flex-end; + + button { + width: calc(50% - 5px); } + + &.btn { + gap: 5px; + + button { + width: calc(50% - 2.5px); + } + } + } + + &.for-line { + button { + flex: 1; + } + } } + } + + .outer-line-wrap { + border-top: 1px solid #3C3C3C; + margin-top: 10px; + padding-top: 15px; + margin-bottom: 15px; + + > div { + margin-bottom: 15px; + + &:last-child { + margin-bottom: 0; + } + } + } + + .modal-guide { + display: block; + font-size: $pop-normal-size; + color: $alert-color; + font-weight: $pop-normal-weight; + } +} + +.adsorption-point { + display: flex; + align-items: center; + background-color: #3A3A3A; + border-radius: 3px; + padding-left: 11px; + overflow: hidden; + transition: all 0.17s ease-in-out; + + span { + font-size: $pop-normal-size; + color: #898989; + } + + i { + display: flex; + align-items: center; + padding: 0 7px; + margin-left: auto; + height: 100%; + font-size: 13px; + color: #898989; + } + + &.act { + i { + color: $pop-color; + background-color: #1083E3; + } + } } // grid-option -.grid-check-form{ +.grid-check-form { + display: flex; + align-items: center; + gap: 15px; + padding-bottom: 15px; + border-bottom: 1px solid #3C3C3C; +} + +.grid-option-wrap { + padding: 15px 0; + border-bottom: 1px solid #3C3C3C; + + .grid-option-box { display: flex; align-items: center; - gap: 15px; - padding-bottom: 15px; - border-bottom: 1px solid #3C3C3C; -} -.grid-option-wrap{ - padding: 15px 0; - border-bottom: 1px solid #3C3C3C; - .grid-option-box{ - display: flex; - align-items: center; - background-color: #3D3D3D; - border-radius: 2px; - padding: 10px; - gap: 20px; - margin-bottom: 5px; - .grid-input-form{ - display: flex; - align-items: center; - span{ - flex: none; - font-size: $pop-normal-size; - color: $pop-color; - font-weight: $pop-bold-weight; - } - .input-grid{ - width: 54px; - input{ - width: 100%; - } - } - } - &:last-child{ - margin-bottom: 0; + background-color: #3D3D3D; + border-radius: 2px; + padding: 10px; + gap: 20px; + margin-bottom: 5px; + + .grid-input-form { + display: flex; + align-items: center; + + span { + flex: none; + font-size: $pop-normal-size; + color: $pop-color; + font-weight: $pop-bold-weight; + } + + .input-grid { + width: 54px; + + input { + width: 100%; } + } } + + &:last-child { + margin-bottom: 0; + } + } } -.grid-select{ - flex: 1; - .sort-select{ - width: 100%; - background-color: #313131; - } + +.grid-select { + flex: 1; + + .sort-select { + width: 100%; + background-color: #313131; + } } -.grid-btn-wrap{ - padding-top: 15px; - text-align: right; - button{ - padding: 0 20px; - } + +.grid-btn-wrap { + padding-top: 15px; + text-align: right; + + button { + padding: 0 20px; + } } // grid copy -.grid-option-tit{ - font-size: $pop-normal-size; - color: $pop-color; - font-weight: $pop-normal-weight; - padding-bottom: 15px; - border-bottom: 1px solid #3C3C3C; +.grid-option-tit { + font-size: $pop-normal-size; + color: $pop-color; + font-weight: $pop-normal-weight; + padding-bottom: 15px; + border-bottom: 1px solid #3C3C3C; } -.grid-direction{ - display: flex; - align-items: center; - gap: 5px; - flex: 1; + +.grid-direction { + display: flex; + align-items: center; + gap: 5px; + flex: 1; } -.direction{ - width: 22px; - height: 22px; - background-color: #757575; - background-image: url(../../public/static/images/canvas/grid_option_arr.svg); - background-repeat: no-repeat; - background-position: center; - background-size: 16px 15px; - border-radius: 50%; - transition: all .15s ease-in-out; - opacity: 0.6; - &.down{transform: rotate(180deg);} - &.left{transform: rotate(-90deg);} - &.right{transform: rotate(90deg);} - &:hover, - &.act{ - opacity: 1; - } + +.direction { + width: 22px; + height: 22px; + background-color: #757575; + background-image: url(../../public/static/images/canvas/grid_option_arr.svg); + background-repeat: no-repeat; + background-position: center; + background-size: 16px 15px; + border-radius: 50%; + transition: all .15s ease-in-out; + opacity: 0.6; + + &.down { + transform: rotate(180deg); + } + + &.left { + transform: rotate(-90deg); + } + + &.right { + transform: rotate(90deg); + } + + &:hover, + &.act { + opacity: 1; + } } // grid-move -.move-form{ - p{ - font-size: $pop-normal-size; - color: $pop-color; - font-weight: $pop-bold-weight; - } +.move-form { + p { + font-size: $pop-normal-size; + color: $pop-color; + font-weight: $pop-bold-weight; + } } -.input-move-wrap{ - display: flex; - align-items: center; - gap: 5px; - span{ - color: $pop-color; - font-size: $pop-normal-size; - } - .input-move{ - width: 130px; - input{ - width: 100%; - } + +.input-move-wrap { + display: flex; + align-items: center; + gap: 5px; + + span { + color: $pop-color; + font-size: $pop-normal-size; + } + + .input-move { + width: 130px; + + input { + width: 100%; } + } } -.direction-move-wrap{ - flex: none; - display: grid; - grid-template-columns: 1fr 1fr; - gap: 10px; + +.direction-move-wrap { + flex: none; + display: grid; + grid-template-columns: 1fr 1fr; + gap: 10px; } // 배치면 초기 설정 -.placement-table{ - table{ - table-layout: fixed; - tr{ - th{ - display: flex; - align-items: center; - font-size: $pop-normal-size; - color: $pop-color; - font-weight: $pop-bold-weight; - padding: 18px 0; - border-bottom: 1px solid #424242; - } - td{ - font-size: $pop-normal-size; - color: $pop-color; - border-bottom: 1px solid #424242; - padding-left: 20px; - } - &:first-child{ - td, - th{ - padding-top: 0; - } - } - } - } - .tooltip{ - position: relative; - display: block; - width: 15px; - height: 15px; - margin-left: 5px; - background: url(../../public/static/images/canvas/pop_tip.svg)no-repeat center; - background-size: cover; - } - &.light{ - padding: 0; - th,td{ - color: $alert-color; - border-bottom: none; - border-top: 1px solid #EFEFEF; - } - th{ - padding: 14px 0; - } - tr{ - &:first-child{ - td, - th{ - padding-top: 14px; - } - } - &:last-child{ - td, - th{ - padding-bottom: 0px; - } - } - } - } -} +.placement-table { + table { + table-layout: fixed; -.pop-form-radio{ - display: flex; - align-items: center; - gap: 10px; -} -.placement-option{ - display: flex; - align-items: center; - gap: 20px; -} -.select-wrap{ - div{ - width: 100%; - } -} -.flex-ment{ - display: flex; - align-items: center; - gap: 5px; - span{ + tr { + th { + display: flex; + align-items: center; font-size: $pop-normal-size; color: $pop-color; - font-weight: $pop-normal-weight; + font-weight: $pop-bold-weight; + padding: 18px 0; + border-bottom: 1px solid #424242; + } + + td { + font-size: $pop-normal-size; + color: $pop-color; + border-bottom: 1px solid #424242; + padding-left: 20px; + } + + &:first-child { + td, + th { + padding-top: 0; + } + } } + } + + .tooltip { + position: relative; + display: block; + width: 15px; + height: 15px; + margin-left: 5px; + background: url(../../public/static/images/canvas/pop_tip.svg) no-repeat center; + background-size: cover; + } + + &.light { + padding: 0; + + th, td { + color: $alert-color; + border-bottom: none; + border-top: 1px solid #EFEFEF; + } + + th { + padding: 14px 0; + } + + tr { + &:first-child { + td, + th { + padding-top: 14px; + } + } + + &:last-child { + td, + th { + padding-bottom: 0px; + } + } + } + } +} + +.pop-form-radio { + display: flex; + align-items: center; + gap: 10px; +} + +.placement-option { + display: flex; + align-items: center; + gap: 20px; +} + +.select-wrap { + div { + width: 100%; + } +} + +.flex-ment { + display: flex; + align-items: center; + gap: 5px; + + span { + font-size: $pop-normal-size; + color: $pop-color; + font-weight: $pop-normal-weight; + } } // 외벽선 그리기 -.outline-wrap{ - padding: 24px 0; - border-bottom: 1px solid #424242; - .outline-inner{ - display: flex; - align-items: center; - margin-bottom: 14px; - &:last-child{ - margin-bottom: 0; - } - } -} -.outline-form{ - width: 50%; +.outline-wrap { + padding: 24px 0; + border-bottom: 1px solid #424242; + + .outline-inner { display: flex; align-items: center; - margin-right: 15px; - span{ - width: 60px; - flex: none; - font-size: $pop-normal-size; - font-weight: $pop-bold-weight; - color: $pop-color; - margin-right: 10px; - } - .reset-btn{ - flex: none; - width: 30px; - height: 30px; - background: transparent; - border: 1px solid #484848; - border-radius: 2px; - margin-left: 5px; - background-image: url(../../public/static/images/canvas/reset_ico.svg); - background-repeat: no-repeat; - background-size: 12px 12px; - background-position: center; - } - &:last-child{ - margin-right: 0; + margin-bottom: 14px; + + &:last-child { + margin-bottom: 0; } + } } -.cul-wrap{ +.outline-form { + width: 50%; + display: flex; + align-items: center; + margin-right: 15px; + + span { + width: 60px; + flex: none; + font-size: $pop-normal-size; + font-weight: $pop-bold-weight; + color: $pop-color; + margin-right: 10px; + } + + .reset-btn { + flex: none; + width: 30px; + height: 30px; + background: transparent; + border: 1px solid #484848; + border-radius: 2px; + margin-left: 5px; + background-image: url(../../public/static/images/canvas/reset_ico.svg); + background-repeat: no-repeat; + background-size: 12px 12px; + background-position: center; + } + + &:last-child { + margin-right: 0; + } +} + +.cul-wrap { + display: flex; + + .outline-box { + width: 50%; + margin-right: 15px; + + .outline-form { + width: 100%; + margin-bottom: 14px; + margin-right: 0; + + &:last-child { + margin-bottom: 0; + } + } + } + + .cul-box { display: flex; - .outline-box{ - width: 50%; - margin-right: 15px; - .outline-form{ - width: 100%; - margin-bottom: 14px; - margin-right: 0; - &:last-child{ - margin-bottom: 0; - } + align-items: center; + justify-content: center; + width: 50%; + background-color: #3D3D3D; + border-radius: 2px; + } +} + +.properties-guide { + font-size: $pop-normal-size; + color: #AAA; + font-weight: $pop-normal-weight; + margin-bottom: 14px; +} + +.properties-setting-wrap { + .setting-tit { + font-size: 13px; + color: $pop-color; + font-weight: $pop-bold-weight; + margin-bottom: 10px; + } + + .setting-btn-wrap { + display: flex; + align-items: center; + padding: 14px 0; + border-top: 1px solid #424242; + border-bottom: 1px solid #424242; + + .setting-btn { + display: block; + width: 100%; + height: 40px; + font-size: 13px; + color: #fff; + font-weight: 700; + border-radius: 2px; + transition: all .15s ease-in-out; + + &.green { + background-color: #305941; + border: 1px solid #45CD7D; + + &:hover { + background-color: #3a6b4e; } + } + + &.blue { + background-color: #2E5360; + border: 1px solid #3FBAE6; + + &:hover { + background-color: #365f6e; + } + } } - .cul-box{ - display: flex; - align-items: center; - justify-content: center; - width: 50%; - background-color: #3D3D3D; - border-radius: 2px ; - } + } } \ No newline at end of file From 1d8d46eb4a0e72104e0a1d5911000ad20129b49e Mon Sep 17 00:00:00 2001 From: minsik Date: Mon, 30 Sep 2024 13:29:22 +0900 Subject: [PATCH 03/11] =?UTF-8?q?=F0=9F=9A=A8chore:=20Sync=20Sass?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/styles/_modal.scss | 84 ++- src/styles/_reset.scss | 1262 ++++++++++++++++++++++------------------ 2 files changed, 777 insertions(+), 569 deletions(-) diff --git a/src/styles/_modal.scss b/src/styles/_modal.scss index 0ad517a3..28456176 100644 --- a/src/styles/_modal.scss +++ b/src/styles/_modal.scss @@ -45,6 +45,10 @@ $alert-color: #101010; width: 400px; } + &.lr { + width: 440px; + } + &.sm { width: 580px; } @@ -138,6 +142,13 @@ $alert-color: #101010; button { flex: 1; } + + &.sub { + button { + flex: 1 1 auto; + padding: 0; + } + } } .modal-check-btn-wrap { @@ -491,6 +502,7 @@ $alert-color: #101010; // 외벽선 그리기 .outline-wrap { padding: 24px 0; + border-top: 1px solid #424242; border-bottom: 1px solid #424242; .outline-inner { @@ -501,11 +513,14 @@ $alert-color: #101010; &:last-child { margin-bottom: 0; } + + .outline-form { + width: 50%; + } } } .outline-form { - width: 50%; display: flex; align-items: center; margin-right: 15px; @@ -517,6 +532,10 @@ $alert-color: #101010; font-weight: $pop-bold-weight; color: $pop-color; margin-right: 10px; + + &.thin { + font-weight: & $pop-normal-weight; + } } .reset-btn { @@ -566,6 +585,7 @@ $alert-color: #101010; } } +// 외벽선 속성 설정 .properties-guide { font-size: $pop-normal-size; color: #AAA; @@ -574,6 +594,10 @@ $alert-color: #101010; } .properties-setting-wrap { + &.outer { + margin-top: 24px; + } + .setting-tit { font-size: 13px; color: $pop-color; @@ -617,4 +641,62 @@ $alert-color: #101010; } } } +} + +// 지붕형상 설정 +.roof-shape-menu { + display: grid; + grid-template-columns: 1fr 1fr 1fr 1fr; + grid-template-rows: 1fr 1fr; + gap: 24px 10px; + margin-bottom: 24px; + + .shape-box { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + padding: 13px; + background-color: #3D3D3D; + transition: background .15s ease-in-out; + + img { + max-width: 100%; + } + } + + .shape-title { + font-size: $pop-normal-size; + font-weight: $pop-bold-weight; + color: $pop-color; + margin-top: 10px; + text-align: center; + transition: color .15s ease-in-out; + } + + .shape-menu-box { + &.act, + &:hover { + .shape-box { + background-color: #008BFF; + } + + .shape-title { + color: #008BFF; + } + } + } +} + +.setting-box { + padding: 14px 0; + border-top: 1px solid #424242; + border-bottom: 1px solid #424242; +} + +.discrimination-box { + padding: 16px 12px; + border: 1px solid #3D3D3D; + border-radius: 2px; + margin-top: 14px; } \ No newline at end of file diff --git a/src/styles/_reset.scss b/src/styles/_reset.scss index 2c5b1e22..dcf0c0d2 100644 --- a/src/styles/_reset.scss +++ b/src/styles/_reset.scss @@ -1,18 +1,21 @@ * { - -webkit-text-size-adjust:none; - -moz-text-size-adjust:none; - -ms-text-size-adjust:none; - text-size-adjust: none; - box-sizing: content-box + -webkit-text-size-adjust: none; + -moz-text-size-adjust: none; + -ms-text-size-adjust: none; + text-size-adjust: none; + box-sizing: content-box } + *, ::after, ::before { - box-sizing: border-box; + box-sizing: border-box; } -html, body{ - width: 100%; - height: 100%; - font-size: 16px; + +html, body { + width: 100%; + height: 100%; + font-size: 16px; } + html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, @@ -22,654 +25,777 @@ b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, -article, aside, canvas, details, embed, -figure, figcaption, footer, header, hgroup, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video { - margin: 0; - padding: 0; - border: 0; - font: inherit; - vertical-align: baseline; - font-family: 'Noto Sans JP', sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - font-smooth: never; + margin: 0; + padding: 0; + border: 0; + font: inherit; + vertical-align: baseline; + font-family: 'Noto Sans JP', sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + font-smooth: never; } + /* HTML5 display-role reset for older browsers */ -article, aside, details, figcaption, figure, +article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { - display: block; + display: block; } + body { - line-height: 1.4; + line-height: 1.4; +} + +body:first-of-type caption { + display: none; } -body:first-of-type caption { display:none;} ol, ul { - list-style: none; -} -blockquote, q { - quotes: none; -} -blockquote:before, blockquote:after, -q:before, q:after { - content: ''; - content: none; -} -table { - width: 100%; - border-collapse: separate; - border-spacing:0; - border:0 none; -} -caption, th, td { - text-align:left; - font-weight: normal; - border:0; + list-style: none; } -a { - cursor:pointer; - color:#000; +blockquote, q { + quotes: none; } + +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} + +table { + width: 100%; + border-collapse: separate; + border-spacing: 0; + border: 0 none; +} + +caption, th, td { + text-align: left; + font-weight: normal; + border: 0; +} + +a { + cursor: pointer; + color: #000; +} + a, a:hover, a:active { - text-decoration:none; - -webkit-tap-highlight-color: transparent; + text-decoration: none; + -webkit-tap-highlight-color: transparent; } + /*form_style*/ input, select, textarea, button, a, label { - -webkit-tap-highlight-color:rgba(0,0,0,0); + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } -button,input[type=text], input[type=button] { - -webkit-appearance: none; - -webkit-border-radius: 0; - -webkit-appearance:none; - appearance: none; - border-radius: 0 + +button, input[type=text], input[type=button] { + -webkit-appearance: none; + -webkit-border-radius: 0; + -webkit-appearance: none; + appearance: none; + border-radius: 0 } + input[type=checkbox], input[type=radio] { - box-sizing: border-box; - padding: 0; + box-sizing: border-box; + padding: 0; } + input, select, button { - border:0 none; - outline:none; - margin:0; + border: 0 none; + outline: none; + margin: 0; } + select { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; } + select::-ms-expand { - display: none; + display: none; } + ::-webkit-input-placeholder { - line-height:1; - font-weight:300; - font-size:0.938rem; - letter-spacing:-0.6px; - color:#8b8b8b; + line-height: 1; + font-weight: 300; + font-size: 0.938rem; + letter-spacing: -0.6px; + color: #8b8b8b; } -.log-box ::-webkit-input-placeholder{ - color:#8b8b8b; + +.log-box ::-webkit-input-placeholder { + color: #8b8b8b; } -button{ - background: transparent; - font-family: 'Noto Sans JP', sans-serif; - border: none; - padding: 0; - margin: 0; - line-height: 1.4; - color: inherit; - outline: none; - cursor: pointer; + +button { + background: transparent; + font-family: 'Noto Sans JP', sans-serif; + border: none; + padding: 0; + margin: 0; + line-height: 1.4; + color: inherit; + outline: none; + cursor: pointer; } -.pre{ - font-family: 'Pretendard', sans-serif !important; + +.pre { + font-family: 'Pretendard', sans-serif !important; } // margin -.mt5{margin-top: 5px !important;} -.mt10{margin-top: 10px !important;} -.mb5{margin-bottom: 5px !important;} -.mb10{margin-bottom: 10px !important;} -.mr5{margin-right: 5px !important;} -.mr10{margin-right: 10px !important;} -.ml5{margin-left: 5px !important;} -.ml10{margin-left: 10px !important;} - -// button -.btn-frame{ - display: inline-block; - padding: 0 7px; - height: 34px; - line-height: 34px; - border-radius: 2px; - color: #fff; - font-size: 12px; - font-weight: 400; - border: 1px solid #000; - text-align: center; - font-family: 'Pretendard', sans-serif; - transition: all .17s ease-in-out; - cursor: pointer; - &.block{ - width: 100%; - } - &.small{ - font-family: 'Noto Sans JP', sans-serif; - height: 30px; - line-height: 30px; - font-size: 13px; - } - - &.deepgray{ - background-color: #2C2C2C; - border: 1px solid #484848; - } - &.gray{ - background-color: #3C3C3C; - border: 1px solid #545454; - } - &.dark{ - background-color: #1C1C1C; - border: 1px solid #484848; - } - &.modal{ - font-family: 'Noto Sans JP', sans-serif; - background-color: #272727; - border: 1px solid #484848; - color: #aaa; - &:hover{ - background-color: #1083E3; - border: 1px solid #1083E3; - color: #fff; - font-weight: 500; - } - } - &:hover, - &.act{ - background-color: #1083E3; - border: 1px solid #1083E3; - color: #fff; - font-weight: 500; - } - &.block{ - display: block; - width: 100%; - } - &.ico-flx{ - display: flex; - align-items: center; - .ico{ - margin-right: 10px; - } - &:hover, - &.act{ - font-weight: 400; - } - } +.mt5 { + margin-top: 5px !important; } -.btn-origin{ - display: inline-block; +.mt10 { + margin-top: 10px !important; +} + +.mb5 { + margin-bottom: 5px !important; +} + +.mb10 { + margin-bottom: 10px !important; +} + +.mr5 { + margin-right: 5px !important; +} + +.mr10 { + margin-right: 10px !important; +} + +.ml5 { + margin-left: 5px !important; +} + +.ml10 { + margin-left: 10px !important; +} + +// button +.btn-frame { + display: inline-block; + padding: 0 7px; + height: 34px; + line-height: 34px; + border-radius: 2px; + color: #fff; + font-size: 12px; + font-weight: 400; + border: 1px solid #000; + text-align: center; + font-family: 'Pretendard', sans-serif; + transition: all .17s ease-in-out; + cursor: pointer; + + &.block { + width: 100%; + } + + &.small { + font-family: 'Noto Sans JP', sans-serif; height: 30px; - padding: 0 14px; - border-radius: 2px; - background-color: #101010; - color: #fff; + line-height: 30px; font-size: 13px; - font-weight: 400; - transition: all .15s ease-in-out; - &.navy{ - background-color: #304961; - &:hover{ - background-color: #1083E3; - } + } + + &.deepgray { + background-color: #2C2C2C; + border: 1px solid #484848; + } + + &.gray { + background-color: #3C3C3C; + border: 1px solid #545454; + } + + &.dark { + background-color: #1C1C1C; + border: 1px solid #484848; + } + + &.modal { + font-family: 'Noto Sans JP', sans-serif; + background-color: #272727; + border: 1px solid #484848; + color: #aaa; + + &:hover { + background-color: #1083E3; + border: 1px solid #1083E3; + color: #fff; + font-weight: 500; } - &.grey{ - background-color: #94A0AD; - &:hover{ - background-color: #607F9A; - } + } + + &.sub-tab { + height: 30px; + padding: 0 10px; + line-height: 28px; + font-family: 'Noto Sans JP', sans-serif; + background-color: #2D2D2D; + border: 1px solid #393939; + color: #aaa; + + &.act, + &:hover { + background-color: #414E6C; + border: 1px solid #414E6C; + color: #fff; + font-weight: 500; } + } + + &:hover, + &.act { + background-color: #1083E3; + border: 1px solid #1083E3; + color: #fff; + font-weight: 500; + } + + &.block { + display: block; + width: 100%; + } + + &.ico-flx { + display: flex; + align-items: center; + + .ico { + margin-right: 10px; + } + + &:hover, + &.act { + font-weight: 400; + } + } +} + +.btn-origin { + display: inline-block; + height: 30px; + padding: 0 14px; + border-radius: 2px; + background-color: #101010; + color: #fff; + font-size: 13px; + font-weight: 400; + transition: all .15s ease-in-out; + + &.navy { + background-color: #304961; + + &:hover { + background-color: #1083E3; + } + } + + &.grey { + background-color: #94A0AD; + + &:hover { + background-color: #607F9A; + } + } } // select -.sort-select{ - position: relative; - display: inline-block; - min-width: 100px; - height: 30px; - line-height: 30px; - padding: 0 10px; +.sort-select { + position: relative; + display: inline-block; + min-width: 100px; + height: 30px; + line-height: 30px; + padding: 0 10px; + background-color: #373737; + border: 1px solid #3F3F3F; + border-radius: 2px; + border-top-left-radius: 2px; + color: #fff; + cursor: pointer; + + p { + font-size: 13px; + color: #fff; + height: 100%; + } + + .select-item-wrap { + position: absolute; + top: 100%; + left: -1px; + clip-path: inset(0 0 100% 0); + width: calc(100% + 2px); + padding: 8px 0; background-color: #373737; border: 1px solid #3F3F3F; border-radius: 2px; - border-top-left-radius: 2px; - color: #fff; - cursor: pointer; - p{ - font-size: 13px; + transition: all 0.17s ease-in-out; + visibility: hidden; + + .select-item { + display: flex; + align-items: center; + padding: 8px 20px; + line-height: 1.4; + transition: all .17s ease-in-out; + + button { + font-size: 12px; color: #fff; - height: 100%; + line-height: 1.4; + } + + &:hover { + background-color: #2C2C2C; + } } - .select-item-wrap{ - position: absolute; - top: 100%; - left: -1px; - clip-path:inset(0 0 100% 0); - width: calc(100% + 2px); - padding: 8px 0; - background-color: #373737; - border: 1px solid #3F3F3F; - border-radius: 2px; - transition: all 0.17s ease-in-out; - visibility: hidden; - .select-item{ - display: flex; - align-items: center; - padding: 8px 20px; - line-height: 1.4; - transition: all .17s ease-in-out; - button{ - font-size: 12px; - color: #fff; - line-height: 1.4; - } - &:hover{ - background-color: #2C2C2C; - } - } + } + + &::after { + content: ''; + position: absolute; + top: 50%; + right: 7px; + transform: translateY(-50%); + width: 10px; + height: 6px; + background: url(/static/images/common/select-arr.svg) no-repeat center; + background-size: cover; + transition: all .17s ease-in-out; + } + + &.active { + .select-item-wrap { + clip-path: inset(0 0 0 0); + visibility: visible; } - &::after{ - content: ''; - position: absolute; - top: 50%; - right: 7px; - transform: translateY(-50%); - width: 10px; - height: 6px; - background: url(/static/images/common/select-arr.svg) no-repeat center; - background-size: cover; - transition: all .17s ease-in-out; - } - &.active{ - .select-item-wrap{ - clip-path: inset(0 0 0 0); - visibility: visible; - } - &:after{ - transform: translateY(-50%) rotate(-180deg); - } + + &:after { + transform: translateY(-50%) rotate(-180deg); } + } } -.select-light{ - position: relative; - display: block; - width: 100%; - height: 30px; - background: #FFF url(../../public/static/images/common/select_light_arr.svg) calc(100% - 11px) center no-repeat; - background-size: 10px 6px; - border: 1px solid #eee; - padding: 0 30px 0 10px; - font-size: 13px; - color: #45576F; - font-family: 'Noto Sans JP', sans-serif; - cursor: pointer; - &:disabled{ - opacity: 1; - background-color: #FAFAFA; - color: #999; - cursor: default; - } - &.black{ - color: #101010; - } - &.dark{ - background: #323234 url(../../public/static/images/common/select_dark_arr.svg) calc(100% - 11px) center no-repeat; - color: #898989; - font-size: 12px; - border-radius: 2px; - border: none; - } +.select-light { + position: relative; + display: block; + width: 100%; + height: 30px; + background: #FFF url(../../public/static/images/common/select_light_arr.svg) calc(100% - 11px) center no-repeat; + background-size: 10px 6px; + border: 1px solid #eee; + padding: 0 30px 0 10px; + font-size: 13px; + color: #45576F; + font-family: 'Noto Sans JP', sans-serif; + cursor: pointer; + + &:disabled { + opacity: 1; + background-color: #FAFAFA; + color: #999; + cursor: default; + } + + &.black { + color: #101010; + } + + &.dark { + background: #323234 url(../../public/static/images/common/select_dark_arr.svg) calc(100% - 11px) center no-repeat; + color: #898989; + font-size: 12px; + border-radius: 2px; + border: none; + } } // input -.form-input{ - label{ - display: block; - color: #aaa; - font-size: 12px; - font-weight: 500; - margin-bottom: 10px; - } -} -input[type=number], -input[type=text]{ - &.input-origin{ - display: inline-block; - height: 30px; - line-height: 30px; - border-radius: 2px; - background-color: #323234; - color: #fff; - font-size: 12px; - font-weight: 500; - font-family: 'Pretendard', sans-serif; - padding: 0 10px; - letter-spacing: 0px; - text-align: right; - &::placeholder{ - opacity: 1; - font-size: 12px; - letter-spacing: 0px; - } - &.block{ - width: 100%; - } - } - &.input-light{ - display: block; - width: 100%; - height: 30px; - padding: 0 10px; - border: 1px solid #eee; - border-radius: 2px; - background-color: #fff; - font-family: 'Noto Sans JP', sans-serif; - font-size: 13px; - color: #45576F; - font-weight: normal; - transition: border-color .17s ease-in-out; - text-align: left; - &:read-only{ - background-color: #FAFAFA; - color: #999999; - } - } +.form-input { + label { + display: block; + color: #aaa; + font-size: 12px; + font-weight: 500; + margin-bottom: 10px; + } } +input[type=number], +input[type=text] { + &.input-origin { + display: inline-block; + height: 30px; + line-height: 30px; + border-radius: 2px; + background-color: #323234; + color: #fff; + font-size: 12px; + font-weight: 500; + font-family: 'Pretendard', sans-serif; + padding: 0 10px; + letter-spacing: 0px; + text-align: right; + + &::placeholder { + opacity: 1; + font-size: 12px; + letter-spacing: 0px; + } + + &.block { + width: 100%; + } + } + + &.input-light { + display: block; + width: 100%; + height: 30px; + padding: 0 10px; + border: 1px solid #eee; + border-radius: 2px; + background-color: #fff; + font-family: 'Noto Sans JP', sans-serif; + font-size: 13px; + color: #45576F; + font-weight: normal; + transition: border-color .17s ease-in-out; + text-align: left; + + &:read-only { + background-color: #FAFAFA; + color: #999999; + } + } +} // check-btn -.check-btn{ - display: flex; - align-items: center; - height: 30px; - background-color: #3A3A3A; - border-radius: 3px; - transition: all .17s ease-in-out; - .check-area{ - flex: none; - width: 30px; - height: 100%; - border-right: 1px solid #272727; - background: url(../../public/static/images/canvas/check-grey.svg)no-repeat center; - background-size: 11px 9px; +.check-btn { + display: flex; + align-items: center; + height: 30px; + background-color: #3A3A3A; + border-radius: 3px; + transition: all .17s ease-in-out; + + .check-area { + flex: none; + width: 30px; + height: 100%; + border-right: 1px solid #272727; + background: url(../../public/static/images/canvas/check-grey.svg) no-repeat center; + background-size: 11px 9px; + } + + .title-area { + padding: 0 10px; + font-size: 12px; + color: #898989; + font-weight: 400; + } + + &.block { + width: 100%; + } + + &:hover, + &.act { + background-color: #fff; + + .check-area { + border-right: 1px solid #101010; + background: url(../../public/static/images/canvas/check-black.svg) no-repeat center; } - .title-area{ - padding: 0 10px; - font-size: 12px; - color: #898989; - font-weight: 400; - } - &.block{ - width: 100%; - } - &:hover, - &.act{ - background-color: #fff; - .check-area{ - border-right: 1px solid #101010; - background: url(../../public/static/images/canvas/check-black.svg)no-repeat center; - } - .title-area{ - color: #101010; - font-weight: 600; - } + + .title-area { + color: #101010; + font-weight: 600; } + } } // arr-btn -.arr-btn{ - display: block; - height: 30px; - border-radius: 3px; - background-color: #3A3A3A; - padding: 0 11px; - text-align: left; - transition: all .17s ease-in-out; - span{ - position: relative; - font-size: 12px; - color: #898989; - font-weight: 400; - padding-right: 15px; - &:after{ - content: ''; - position: absolute; - top: 50%; - right: 0; - transform: translateY(-50%); - width: 5px; - height: 8px; - background: url(../../public/static/images/canvas/arr_btn_ico.svg)no-repeat center; - } +.arr-btn { + display: block; + height: 30px; + border-radius: 3px; + background-color: #3A3A3A; + padding: 0 11px; + text-align: left; + transition: all .17s ease-in-out; + + span { + position: relative; + font-size: 12px; + color: #898989; + font-weight: 400; + padding-right: 15px; + + &:after { + content: ''; + position: absolute; + top: 50%; + right: 0; + transform: translateY(-50%); + width: 5px; + height: 8px; + background: url(../../public/static/images/canvas/arr_btn_ico.svg) no-repeat center; } + } + + &:hover, + &.act { + background-color: #fff; + + span { + color: #101010; + font-weight: 500; + + &:after { + background: url(../../public/static/images/canvas/arr_btn_ico_black.svg) no-repeat center; + } + } + } + + &.dark { + text-align: center; + background-color: #272727; + border: 1px solid #484848; + + span { + color: #Fff; + + &:after { + background: url(../../public/static/images/canvas/arr_btn_ico_white.svg) no-repeat center; + } + } + &:hover, - &.act{ - background-color: #fff; - span{ - color: #101010; - font-weight: 500; - &:after{ - background: url(../../public/static/images/canvas/arr_btn_ico_black.svg)no-repeat center; - } - } - } - &.dark{ - text-align: center; - background-color: #272727; - border: 1px solid #484848; - span{ - color: #Fff; - &:after{ - background: url(../../public/static/images/canvas/arr_btn_ico_white.svg)no-repeat center; - } - } - &:hover, - &.act{ - background-color: #1083E3; - border: 1px solid #1083E3; - } + &.act { + background-color: #1083E3; + border: 1px solid #1083E3; } + } } // radio .d-check-radio, -.d-check-box{ - line-height: 1.1; +.d-check-box { + line-height: 1.1; + cursor: pointer; + + input[type=checkbox], + input[type=radio] { + position: static; + margin-left: 0; cursor: pointer; - input[type=checkbox], - input[type=radio]{ - position: static; - margin-left: 0; - cursor: pointer; - opacity: 0; - z-index: 1; - flex: 0 0 auto; + opacity: 0; + z-index: 1; + flex: 0 0 auto; + } + + label { + position: relative; + padding-left: 10px; + margin-bottom: 0; + word-break: break-all; + line-height: 1.2; + display: inline; + vertical-align: top; + color: #fff; + font-size: 13px; + font-weight: 400; + cursor: pointer; + } + + &.light { + label { + color: #45576F; } - label{ - position: relative; - padding-left: 10px; - margin-bottom: 0; - word-break: break-all; - line-height: 1.2; - display: inline; - vertical-align: top; - color: #fff; - font-size: 13px; - font-weight: 400; - cursor: pointer; - } - &.light{ - label{ - color: #45576F; - } - } - &.no-text{ - label{ - padding-left: 0; - } + } + + &.no-text { + label { + padding-left: 0; } + } } .d-check-radio { - label{ - &::before{ - cursor: pointer; - content: ""; - display: inline-block; - position: absolute; - width: 17px; - height: 17px; - top:2px; - left: 0; - margin-left: -12px; - border: 1px solid #999999; - border-radius: 100%; - background-color: transparent; - text-align:center; - font-size:13px; - line-height:1.4; - transition: border 0.15s ease-in-out, color 0.15s ease-in-out; - } - &::after{ - cursor: pointer; - content: ""; - display: inline-block; - position: absolute; - width: 9px; - height: 9px; - top:6px; - left: 4px; - margin-left: -12px; - border: none; - border-radius: 100%; - background-color: #fff; - text-align:center; - font-size:13px; - line-height:1.4; - opacity: 0; - visibility: hidden; - transition: opacity 0.15s ease-in-out, color 0.15s ease-in-out; - } + label { + &::before { + cursor: pointer; + content: ""; + display: inline-block; + position: absolute; + width: 17px; + height: 17px; + top: 2px; + left: 0; + margin-left: -12px; + border: 1px solid #999999; + border-radius: 100%; + background-color: transparent; + text-align: center; + font-size: 13px; + line-height: 1.4; + transition: border 0.15s ease-in-out, color 0.15s ease-in-out; } - &.light{ - label{ - &:before{ - border-color: #D6D6D7; - } - &:after{ - background-color: #697C8F; - } - } + + &::after { + cursor: pointer; + content: ""; + display: inline-block; + position: absolute; + width: 9px; + height: 9px; + top: 6px; + left: 4px; + margin-left: -12px; + border: none; + border-radius: 100%; + background-color: #fff; + text-align: center; + font-size: 13px; + line-height: 1.4; + opacity: 0; + visibility: hidden; + transition: opacity 0.15s ease-in-out, color 0.15s ease-in-out; } - input[type=radio]:checked + label::after{ - opacity: 1; - visibility: visible; + } + + &.light { + label { + &:before { + border-color: #D6D6D7; + } + + &:after { + background-color: #697C8F; + } } - &.pop{ - label{ - &:before{ - width: 16px; - height: 16px; - border-color: #fff; - } - &:after{ - width: 8px; - height: 8px; - background-color: #fff; - } - } + } + + input[type=radio]:checked + label::after { + opacity: 1; + visibility: visible; + } + + &.pop { + label { + &:before { + width: 16px; + height: 16px; + border-color: #fff; + } + + &:after { + width: 8px; + height: 8px; + background-color: #fff; + } } + } } // check-box -.d-check-box{ - label{ - &::before{ - cursor: pointer; - content: ""; - display: inline-block; - position: absolute; - width: 17px; - height: 17px; - top: 2px; - left: 0; - margin-left: -12px; - border: 1px solid #D6D6D7; - background-color: transparent; - transition: border 0.15s ease-in-out, color 0.15s ease-in-out; - } - &:after{ - cursor: pointer; - content: ""; - display: inline-block; - position: absolute; - width: 16px; - height: 16px; - top:0; - left: 0; - margin-left: -.8rem; - transition: border 0.05s ease-in-out, color 0.05s ease-in-out; - } +.d-check-box { + label { + &::before { + cursor: pointer; + content: ""; + display: inline-block; + position: absolute; + width: 17px; + height: 17px; + top: 2px; + left: 0; + margin-left: -12px; + border: 1px solid #D6D6D7; + background-color: transparent; + transition: border 0.15s ease-in-out, color 0.15s ease-in-out; } - input[type=checkbox]:checked + label::after{ - content: ""; - display: inline-block; - position: absolute; - top: 1px; - left: -1px; - width: 5px; - height: 8px; - border: 2px solid #697C8F; - border-left: none; - border-top: none; - transform: translate(7.75px,4.5px) rotate(45deg); - -ms-transform: translate(7.75px,4.5px) rotate(45deg); + + &:after { + cursor: pointer; + content: ""; + display: inline-block; + position: absolute; + width: 16px; + height: 16px; + top: 0; + left: 0; + margin-left: -.8rem; + transition: border 0.05s ease-in-out, color 0.05s ease-in-out; } - &.pop{ - input[type=checkbox]:checked + label::after{ - border-color: #fff; - } + } + + input[type=checkbox]:checked + label::after { + content: ""; + display: inline-block; + position: absolute; + top: 1px; + left: -1px; + width: 5px; + height: 8px; + border: 2px solid #697C8F; + border-left: none; + border-top: none; + transform: translate(7.75px, 4.5px) rotate(45deg); + -ms-transform: translate(7.75px, 4.5px) rotate(45deg); + } + + &.pop { + input[type=checkbox]:checked + label::after { + border-color: #fff; } + } } // date-picker -.date-picker{ - svg{display: none;} - .react-datepicker-wrapper{ - width: 100%; - } - input[type=text]{ - display: block; - width: 100%; - height: 30px; - padding: 0 34px 0 10px; - border-radius: 2px; - border: 1px solid #eee; - font-size: 13px; - color: #45576F; - font-weight: normal; - font-family: 'Noto Sans JP', sans-serif; - background: #fff url(../../public/static/images/common/datepicker.svg) calc(100% - 11px) center no-repeat; - background-size: 14px 15px; - cursor: pointer; - } +.date-picker { + svg { + display: none; + } + + .react-datepicker-wrapper { + width: 100%; + } + + input[type=text] { + display: block; + width: 100%; + height: 30px; + padding: 0 34px 0 10px; + border-radius: 2px; + border: 1px solid #eee; + font-size: 13px; + color: #45576F; + font-weight: normal; + font-family: 'Noto Sans JP', sans-serif; + background: #fff url(../../public/static/images/common/datepicker.svg) calc(100% - 11px) center no-repeat; + background-size: 14px 15px; + cursor: pointer; + } } \ No newline at end of file From c28f798038810cd06935562c749719ce4db07dc2 Mon Sep 17 00:00:00 2001 From: minsik Date: Mon, 30 Sep 2024 13:30:13 +0900 Subject: [PATCH 04/11] =?UTF-8?q?title=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../outerlinesetting/WallLineSetting.jsx | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/components/floor-plan/modal/outerlinesetting/WallLineSetting.jsx b/src/components/floor-plan/modal/outerlinesetting/WallLineSetting.jsx index cff90c61..0cba88c2 100644 --- a/src/components/floor-plan/modal/outerlinesetting/WallLineSetting.jsx +++ b/src/components/floor-plan/modal/outerlinesetting/WallLineSetting.jsx @@ -55,19 +55,22 @@ export default function WallLineSetting(props) { {getMessage('modal.cover.outline.diagonal')} - {type === OUTER_LINE_TYPE.OUTER_LINE ? ( - - ) : type === OUTER_LINE_TYPE.RIGHT_ANGLE ? ( - - ) : type === OUTER_LINE_TYPE.DOUBLE_PITCH ? ( - - ) : type === OUTER_LINE_TYPE.ANGLE ? ( - - ) : type === OUTER_LINE_TYPE.DIAGONAL_LINE ? ( - - ) : ( - <> - )} +
+
{getMessage('modal.cover.outline.setting')}
+ {type === OUTER_LINE_TYPE.OUTER_LINE ? ( + + ) : type === OUTER_LINE_TYPE.RIGHT_ANGLE ? ( + + ) : type === OUTER_LINE_TYPE.DOUBLE_PITCH ? ( + + ) : type === OUTER_LINE_TYPE.ANGLE ? ( + + ) : type === OUTER_LINE_TYPE.DIAGONAL_LINE ? ( + + ) : ( + <> + )} +
diff --git a/src/components/floor-plan/modal/outerlinesetting/PropertiesSetting.js b/src/components/floor-plan/modal/outerlinesetting/PropertiesSetting.js index 1ff975a7..c4950666 100644 --- a/src/components/floor-plan/modal/outerlinesetting/PropertiesSetting.js +++ b/src/components/floor-plan/modal/outerlinesetting/PropertiesSetting.js @@ -1,14 +1,18 @@ import WithDraggable from '@/components/common/draggable/WithDraggable' import { useMessage } from '@/hooks/useMessage' -export default function PropertiesSetting() { +export default function PropertiesSetting(props) { const { getMessage } = useMessage() + const { setShowPropertiesSettingModal } = props + return (

{getMessage('modal.canvas.setting.wallline.properties.setting')}

- +
{getMessage('modal.canvas.setting.wallline.properties.setting.info')}
From 8f3f74b60856d8847b84746e6882bc2e0e12dbdb Mon Sep 17 00:00:00 2001 From: Daseul Kim Date: Mon, 30 Sep 2024 14:26:36 +0900 Subject: [PATCH 06/11] =?UTF-8?q?refactor:=20canvas=20status=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=ED=95=A8=EC=88=98=EC=97=90=20userId=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EB=B0=A9=EC=8B=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/floor-plan/CanvasLayout.jsx | 4 +++- src/hooks/usePlan.js | 4 +--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/floor-plan/CanvasLayout.jsx b/src/components/floor-plan/CanvasLayout.jsx index 5d3817a2..e71c01f5 100644 --- a/src/components/floor-plan/CanvasLayout.jsx +++ b/src/components/floor-plan/CanvasLayout.jsx @@ -6,6 +6,7 @@ import CanvasFrame from './CanvasFrame' import { usePlan } from '@/hooks/usePlan' import { globalLocaleStore } from '@/store/localeAtom' import { currentCanvasPlanState, initCanvasPlansState } from '@/store/canvasAtom' +import { sessionStore } from '@/store/commonAtom' export default function CanvasLayout() { const [objectNo, setObjectNo] = useState('test123240822001') // 이후 삭제 필요 @@ -14,6 +15,7 @@ export default function CanvasLayout() { const [currentCanvasPlan, setCurrentCanvasPlan] = useRecoilState(currentCanvasPlanState) const [initCanvasPlans, setInitCanvasPlans] = useRecoilState(initCanvasPlansState) const globalLocaleState = useRecoilValue(globalLocaleStore) + const sessionState = useRecoilValue(sessionStore) const { getCanvasByObjectNo } = usePlan() @@ -63,7 +65,7 @@ export default function CanvasLayout() { } useEffect(() => { - getCanvasByObjectNo(objectNo).then((res) => { + getCanvasByObjectNo(sessionState.userId, objectNo).then((res) => { console.log('canvas 목록 ', res) if (res.length > 0) { setInitCanvasPlans(res) diff --git a/src/hooks/usePlan.js b/src/hooks/usePlan.js index 55854cb7..cf566b3f 100644 --- a/src/hooks/usePlan.js +++ b/src/hooks/usePlan.js @@ -12,8 +12,6 @@ export function usePlan() { const [initCanvasPlans, setInitCanvasPlans] = useRecoilState(initCanvasPlansState) const { getMessage } = useMessage() const { get, promisePost, promisePut } = useAxios() - const [sessionState, setSessionState] = useRecoilState(sessionStore) - const [userId, setUserId] = useState(sessionState.userId) /** * 마우스 포인터의 가이드라인을 제거합니다. @@ -137,7 +135,7 @@ export function usePlan() { /** * objectNo에 해당하는 canvas 목록을 조회하는 함수 */ - const getCanvasByObjectNo = async (objectNo) => { + const getCanvasByObjectNo = async (userId, objectNo) => { return get({ url: `/api/canvas-management/canvas-statuses/by-object/${objectNo}/${userId}` }).then((res) => res.map((item) => ({ id: item.id, From 0bf1fbfe5683d1faf6a833c1d7404f7a48e1643f Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Mon, 30 Sep 2024 14:48:02 +0900 Subject: [PATCH 07/11] =?UTF-8?q?=EC=99=B8=EB=B2=BD=EC=84=A0=20=EA=B7=B8?= =?UTF-8?q?=EB=A6=AC=EA=B8=B0=20=3D>=20=EC=99=B8=EB=B2=BD=EC=84=A0=20?= =?UTF-8?q?=EC=86=8D=EC=84=B1=20=EC=84=A4=EC=A0=95=20=EC=9E=91=EC=97=85?= =?UTF-8?q?=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/common.js | 3 +- src/components/floor-plan/FloorPlan.jsx | 1 + .../modal/outerlinesetting/Angle.jsx | 39 +- .../modal/outerlinesetting/Diagonal.jsx | 128 +++++- .../modal/outerlinesetting/DoublePitch.jsx | 173 +++++++- .../modal/outerlinesetting/OuterLineWall.jsx | 40 +- ...ertiesSetting.js => PropertiesSetting.jsx} | 21 +- .../modal/outerlinesetting/RightAngle.jsx | 101 ++++- .../outerlinesetting/WallLineSetting.jsx | 115 +++++- src/hooks/roofcover/useOuterLineWall.js | 371 ++++++++++++++---- src/hooks/roofcover/usePropertiesSetting.js | 89 +++++ src/hooks/useAdsorptionPoint.js | 2 +- src/hooks/useCanvas.js | 9 +- src/hooks/useDotLineGrid.js | 1 + src/hooks/useEvent.js | 44 ++- src/hooks/useLine.js | 1 + 16 files changed, 974 insertions(+), 164 deletions(-) rename src/components/floor-plan/modal/outerlinesetting/{PropertiesSetting.js => PropertiesSetting.jsx} (56%) create mode 100644 src/hooks/roofcover/usePropertiesSetting.js diff --git a/src/common/common.js b/src/common/common.js index 84cf60a3..f9ca6fff 100644 --- a/src/common/common.js +++ b/src/common/common.js @@ -53,8 +53,9 @@ export const Mode = { export const LINE_TYPE = { WALLLINE: { /** - * 처마 / 캐라바 / 벽 / 팔작지붕 / 반절처 / 한쪽흐름 + * 없음 / 처마 / 캐라바 / 벽 / 팔작지붕 / 반절처 / 한쪽흐름 */ + DEFAULT: 'default', EAVES: 'eaves', GABLE: 'gable', WALL: 'wall', diff --git a/src/components/floor-plan/FloorPlan.jsx b/src/components/floor-plan/FloorPlan.jsx index 9f7c6641..55b700c1 100644 --- a/src/components/floor-plan/FloorPlan.jsx +++ b/src/components/floor-plan/FloorPlan.jsx @@ -34,6 +34,7 @@ export default function FloorPlan() { const outlineProps = { setShowOutlineModal, + setShowPropertiesSettingModal, } const modalProps = { diff --git a/src/components/floor-plan/modal/outerlinesetting/Angle.jsx b/src/components/floor-plan/modal/outerlinesetting/Angle.jsx index ca3ea288..83985016 100644 --- a/src/components/floor-plan/modal/outerlinesetting/Angle.jsx +++ b/src/components/floor-plan/modal/outerlinesetting/Angle.jsx @@ -1,8 +1,11 @@ import Image from 'next/image' import { useMessage } from '@/hooks/useMessage' +import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils' -export default function Angle() { +export default function Angle({ props }) { const { getMessage } = useMessage() + const { angle1, setAngle1, angle1Ref, length1, setLength1, length1Ref } = props + return ( <>
@@ -11,16 +14,40 @@ export default function Angle() {
{getMessage('modal.cover.outline.angle')}
- + onlyNumberWithDotInputChange(e, setAngle1)} + placeholder="45" + />
- +
- {getMessage('modal.cover.outline.arrow')} + {getMessage('modal.cover.outline.length')}
- + onlyNumberInputChange(e, setLength1)} + placeholder="3000" + />
- +
diff --git a/src/components/floor-plan/modal/outerlinesetting/Diagonal.jsx b/src/components/floor-plan/modal/outerlinesetting/Diagonal.jsx index 9ae9f02e..84c25329 100644 --- a/src/components/floor-plan/modal/outerlinesetting/Diagonal.jsx +++ b/src/components/floor-plan/modal/outerlinesetting/Diagonal.jsx @@ -1,7 +1,24 @@ import { useMessage } from '@/hooks/useMessage' +import { onlyNumberInputChange } from '@/util/input-utils' -export default function Diagonal() { +export default function Diagonal({ props }) { const { getMessage } = useMessage() + + const { + length1, + setLength1, + length1Ref, + length2, + setLength2, + length2Ref, + outerLineDiagonalLength, + setOuterLineDiagonalLength, + outerLineDiagonalLengthRef, + arrow1, + setArrow1, + arrow2, + setArrow2, + } = props return ( <>
@@ -13,26 +30,77 @@ export default function Diagonal() { {getMessage('modal.cover.outline.length')}
- + onlyNumberInputChange(e, setOuterLineDiagonalLength)} + placeholder="3000" + />
- +
{getMessage('modal.cover.outline.length')}
- + onlyNumberInputChange(e, setLength1)} + placeholder="3000" + />
- +
{getMessage('modal.cover.outline.arrow')}
- - - - + + + +
@@ -40,16 +108,48 @@ export default function Diagonal() {
{getMessage('modal.cover.outline.length')}
- + onlyNumberInputChange(e, setLength2)} + readOnly={true} + placeholder="3000" + />
{getMessage('modal.cover.outline.arrow')}
- - - - + + + +
diff --git a/src/components/floor-plan/modal/outerlinesetting/DoublePitch.jsx b/src/components/floor-plan/modal/outerlinesetting/DoublePitch.jsx index ea386d4b..5bfa5950 100644 --- a/src/components/floor-plan/modal/outerlinesetting/DoublePitch.jsx +++ b/src/components/floor-plan/modal/outerlinesetting/DoublePitch.jsx @@ -1,7 +1,48 @@ import { useMessage } from '@/hooks/useMessage' +import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils' +import { getDegreeByChon } from '@/util/canvas-util' -export default function DoublePitch() { +export default function DoublePitch({ props }) { const { getMessage } = useMessage() + const { + angle1, + setAngle1, + angle1Ref, + angle2, + setAngle2, + angle2Ref, + length1, + setLength1, + length1Ref, + length2, + setLength2, + length2Ref, + arrow1, + setArrow1, + arrow2, + setArrow2, + arrow1Ref, + arrow2Ref, + } = props + + const getLength2 = () => { + const angle1Value = angle1Ref.current.value + const angle2Value = angle2Ref.current.value + const length1Value = length1Ref.current.value + + const arrow1Value = arrow1Ref.current + const arrow2Value = arrow2Ref.current + + if (angle1Value !== 0 && length1Value !== 0 && angle2Value !== 0 && arrow1Value !== '') { + const radian1 = (getDegreeByChon(angle1Value) * Math.PI) / 180 + + const radian2 = (getDegreeByChon(angle2Value) * Math.PI) / 180 + return Math.floor((Math.tan(radian1) * length1Value) / Math.tan(radian2)) + } + + return 0 + } + return ( <>
@@ -9,26 +50,70 @@ export default function DoublePitch() {
{getMessage('modal.cover.outline.angle')}
- + onlyNumberWithDotInputChange(e, setAngle1)} + placeholder="45" + />
- +
{getMessage('modal.cover.outline.length')}
- + onlyNumberInputChange(e, setLength1)} + placeholder="3000" + />
- +
{getMessage('modal.cover.outline.arrow')}
- - - - + + + +
@@ -38,26 +123,80 @@ export default function DoublePitch() {
{getMessage('modal.cover.outline.angle')}
- + { + onlyNumberWithDotInputChange(e, setAngle2) + console.log(getLength2()) + setLength2(getLength2()) + }} + placeholder="45" + />
- +
{getMessage('modal.cover.outline.length')}
- + onlyNumberInputChange(e, setLength2)} + readOnly={true} + placeholder="3000" + />
- +
{getMessage('modal.cover.outline.arrow')}
- - - - + + + +
diff --git a/src/components/floor-plan/modal/outerlinesetting/OuterLineWall.jsx b/src/components/floor-plan/modal/outerlinesetting/OuterLineWall.jsx index 9dfa85c8..a3493f17 100644 --- a/src/components/floor-plan/modal/outerlinesetting/OuterLineWall.jsx +++ b/src/components/floor-plan/modal/outerlinesetting/OuterLineWall.jsx @@ -1,12 +1,12 @@ 'use client' import { useMessage } from '@/hooks/useMessage' -import { useOuterLineWall } from '@/hooks/roofcover/useOuterLineWall' import { onlyNumberInputChange } from '@/util/input-utils' -export default function OuterLineWall(props) { +export default function OuterLineWall({ props }) { const { getMessage } = useMessage() - const { length1, setLength1, length1Ref, arrow1, setArrow1 } = useOuterLineWall() + + const { length1, setLength1, length1Ref, arrow1, setArrow1 } = props return (
@@ -22,15 +22,39 @@ export default function OuterLineWall(props) { placeholder="3000" />
- +
{getMessage('modal.cover.outline.arrow')}
- - - - + + + +
diff --git a/src/components/floor-plan/modal/outerlinesetting/PropertiesSetting.js b/src/components/floor-plan/modal/outerlinesetting/PropertiesSetting.jsx similarity index 56% rename from src/components/floor-plan/modal/outerlinesetting/PropertiesSetting.js rename to src/components/floor-plan/modal/outerlinesetting/PropertiesSetting.jsx index c4950666..0120f6a4 100644 --- a/src/components/floor-plan/modal/outerlinesetting/PropertiesSetting.js +++ b/src/components/floor-plan/modal/outerlinesetting/PropertiesSetting.jsx @@ -1,10 +1,13 @@ import WithDraggable from '@/components/common/draggable/WithDraggable' import { useMessage } from '@/hooks/useMessage' +import { usePropertiesSetting } from '@/hooks/roofcover/usePropertiesSetting' export default function PropertiesSetting(props) { const { getMessage } = useMessage() const { setShowPropertiesSettingModal } = props - + + const { handleSetEaves, handleSetGable, handleRollback, handleFix } = usePropertiesSetting() + return (
@@ -19,13 +22,21 @@ export default function PropertiesSetting(props) {
{getMessage('setting')}
- - + +
- - + +
diff --git a/src/components/floor-plan/modal/outerlinesetting/RightAngle.jsx b/src/components/floor-plan/modal/outerlinesetting/RightAngle.jsx index 815ab087..306a09f0 100644 --- a/src/components/floor-plan/modal/outerlinesetting/RightAngle.jsx +++ b/src/components/floor-plan/modal/outerlinesetting/RightAngle.jsx @@ -1,24 +1,63 @@ import { useMessage } from '@/hooks/useMessage' +import { onlyNumberInputChange } from '@/util/input-utils' -export default function RightAngle() { +export default function RightAngle({ props }) { const { getMessage } = useMessage() + const { length1, setLength1, length1Ref, length2, setLength2, length2Ref, arrow1, setArrow1, arrow2, setArrow2 } = props return (
{getMessage('modal.cover.outline.length')}
- + onlyNumberInputChange(e, setLength1)} + placeholder="3000" + />
- +
{getMessage('modal.cover.outline.arrow')}
- - - - + + + +
@@ -26,17 +65,53 @@ export default function RightAngle() {
{getMessage('modal.cover.outline.length')}
- + onlyNumberInputChange(e, setLength2)} + placeholder="3000" + />
- +
{getMessage('modal.cover.outline.arrow')}
- - - - + + + +
diff --git a/src/components/floor-plan/modal/outerlinesetting/WallLineSetting.jsx b/src/components/floor-plan/modal/outerlinesetting/WallLineSetting.jsx index 0cba88c2..8a9c1979 100644 --- a/src/components/floor-plan/modal/outerlinesetting/WallLineSetting.jsx +++ b/src/components/floor-plan/modal/outerlinesetting/WallLineSetting.jsx @@ -11,9 +11,102 @@ import DoublePitch from '@/components/floor-plan/modal/outerlinesetting/DoublePi import Diagonal from '@/components/floor-plan/modal/outerlinesetting/Diagonal' export default function WallLineSetting(props) { - const { setShowOutlineModal } = props + const { setShowOutlineModal, setShowPropertiesSettingModal } = props const { getMessage } = useMessage() - const { type, setType, handleFix, handleRollback } = useOuterLineWall() + const { + length1, + setLength1, + length2, + setLength2, + length1Ref, + length2Ref, + arrow1, + setArrow1, + arrow2, + setArrow2, + angle1, + setAngle1, + angle1Ref, + angle2, + setAngle2, + angle2Ref, + type, + setType, + arrow1Ref, + arrow2Ref, + outerLineDiagonalLength, + setOuterLineDiagonalLength, + outerLineDiagonalLengthRef, + handleRollback, + handleFix, + } = useOuterLineWall() + + const outerLineProps = { + length1, + setLength1, + length1Ref, + arrow1, + setArrow1, + } + + const rightAngleProps = { + length1, + setLength1, + length1Ref, + length2, + setLength2, + length2Ref, + arrow1, + setArrow1, + arrow2, + setArrow2, + } + + const doublePitchProps = { + angle1, + setAngle1, + angle1Ref, + angle2, + setAngle2, + angle2Ref, + length1, + setLength1, + length1Ref, + length2, + setLength2, + length2Ref, + arrow1, + setArrow1, + arrow2, + setArrow2, + arrow1Ref, + arrow2Ref, + } + + const angleProps = { + angle1, + setAngle1, + angle1Ref, + length1, + setLength1, + length1Ref, + } + + const diagonalLineProps = { + length1, + setLength1, + length1Ref, + length2, + setLength2, + length2Ref, + outerLineDiagonalLength, + setOuterLineDiagonalLength, + outerLineDiagonalLengthRef, + arrow1, + setArrow1, + arrow2, + setArrow2, + } return ( @@ -58,15 +151,15 @@ export default function WallLineSetting(props) {
{getMessage('modal.cover.outline.setting')}
{type === OUTER_LINE_TYPE.OUTER_LINE ? ( - + ) : type === OUTER_LINE_TYPE.RIGHT_ANGLE ? ( - + ) : type === OUTER_LINE_TYPE.DOUBLE_PITCH ? ( - + ) : type === OUTER_LINE_TYPE.ANGLE ? ( - + ) : type === OUTER_LINE_TYPE.DIAGONAL_LINE ? ( - + ) : ( <> )} @@ -75,7 +168,13 @@ export default function WallLineSetting(props) { -
diff --git a/src/hooks/roofcover/useOuterLineWall.js b/src/hooks/roofcover/useOuterLineWall.js index 355eca16..0f1e4f41 100644 --- a/src/hooks/roofcover/useOuterLineWall.js +++ b/src/hooks/roofcover/useOuterLineWall.js @@ -1,5 +1,5 @@ import { useEffect, useRef } from 'react' -import { distanceBetweenPoints } from '@/util/canvas-util' +import { distanceBetweenPoints, getDegreeByChon } from '@/util/canvas-util' import { useRecoilState, useRecoilValue } from 'recoil' import { adsorptionPointAddModeState, @@ -16,14 +16,17 @@ import { useTempGrid } from '@/hooks/useTempGrid' import { usePolygon } from '@/hooks/usePolygon' import { outerLineAngle1State, + outerLineAngle2State, outerLineArrow1State, outerLineArrow2State, + outerLineDiagonalState, outerLineLength1State, outerLineLength2State, outerLinePointsState, outerLineTypeState, } from '@/store/outerLineAtom' import { calculateAngle } from '@/util/qpolygon-utils' +import { fabric } from 'fabric' export function useOuterLineWall() { const canvas = useRecoilValue(canvasState) @@ -33,6 +36,7 @@ export function useOuterLineWall() { const { addLine, removeLine } = useLine() const { tempGridMode } = useTempGrid() const { addPolygonByLines } = usePolygon() + const verticalHorizontalMode = useRecoilValue(verticalHorizontalModeState) const adsorptionPointAddMode = useRecoilValue(adsorptionPointAddModeState) const adsorptionPointMode = useRecoilValue(adsorptionPointModeState) @@ -42,28 +46,31 @@ export function useOuterLineWall() { const length1Ref = useRef(null) const length2Ref = useRef(null) const angle1Ref = useRef(null) + const angle2Ref = useRef(null) const [length1, setLength1] = useRecoilState(outerLineLength1State) const [length2, setLength2] = useRecoilState(outerLineLength2State) const [arrow1, setArrow1] = useRecoilState(outerLineArrow1State) const [arrow2, setArrow2] = useRecoilState(outerLineArrow2State) const [points, setPoints] = useRecoilState(outerLinePointsState) const [type, setType] = useRecoilState(outerLineTypeState) + const [angle1, setAngle1] = useRecoilState(outerLineAngle1State) + const [angle2, setAngle2] = useRecoilState(outerLineAngle2State) + const [outerLineDiagonalLength, setOuterLineDiagonalLength] = useRecoilState(outerLineDiagonalState) const arrow1Ref = useRef(arrow1) const arrow2Ref = useRef(arrow2) + const outerLineDiagonalLengthRef = useRef(null) + const isFix = useRef(false) - const [angle1, setAngle1] = useRecoilState(outerLineAngle1State) + const closeModalFn = useRef(null) + useEffect(() => { if (adsorptionPointAddMode || tempGridMode) { return } - removeMouseEvent('mouse:down', mouseDown) addCanvasMouseEventListener('mouse:down', mouseDown) clear() - return () => { - removeAllMouseEventListeners() - } }, [verticalHorizontalMode, points, adsorptionPointAddMode, adsorptionPointMode, adsorptionRange, interval, tempGridMode]) useEffect(() => { @@ -75,9 +82,8 @@ export function useOuterLineWall() { }, [arrow2]) useEffect(() => { - removeAllDocumentEventListeners() - addDocumentEventListener('keydown', document, keydown[type]) clear() + addDocumentEventListener('keydown', document, keydown[type]) }, [type]) const clear = () => { @@ -88,6 +94,9 @@ export function useOuterLineWall() { setArrow2('') setAngle1(0) + setAngle2(0) + + setOuterLineDiagonalLength(0) } const mouseDown = (e) => { @@ -143,14 +152,13 @@ export function useOuterLineWall() { canvas?.remove(canvas?.getObjects().find((obj) => obj.name === 'startPoint')) - // point가 변경 될때마다 이벤트 리스너를 제거하고 다시 등록 - removeAllDocumentEventListeners() - addDocumentEventListener('keydown', document, keydown[type]) - if (points.length === 0) { + removeAllDocumentEventListeners() return } + addDocumentEventListener('keydown', document, keydown[type]) + if (points.length === 1) { const point = new fabric.Circle({ radius: 5, @@ -174,6 +182,13 @@ export function useOuterLineWall() { const lastPoint = points[points.length - 1] const firstPoint = points[0] + if (isFix.current) { + canvas?.renderAll() + closeModalFn.current(false) + removeAllMouseEventListeners() + removeAllDocumentEventListeners() + } + if (points.length < 3) { return } @@ -241,7 +256,7 @@ export function useOuterLineWall() { stroke: 'black', strokeWidth: 3, idx: idx, - selectable: false, + selectable: true, name: 'outerLine', }) } @@ -261,7 +276,26 @@ export function useOuterLineWall() { } // 직각 완료될 경우 확인 - const checkRightAngle = () => { + const checkRightAngle = (direction) => { + const activeElem = document.activeElement + + const canDirection = + direction === '↓' || direction === '↑' + ? arrow1Ref.current === '←' || arrow1Ref.current === '→' + : arrow1Ref.current === '↓' || arrow1Ref.current === '↑' + + if (activeElem === length1Ref.current || activeElem === angle1Ref.current) { + setArrow1(direction) + arrow1Ref.current = direction + length2Ref.current.focus() + } else if (activeElem === length2Ref.current || activeElem === angle2Ref.current) { + if (!canDirection) { + return + } + setArrow2(direction) + arrow2Ref.current = direction + } + const length1Num = Number(length1Ref.current.value) / 10 const length2Num = Number(length2Ref.current.value) / 10 @@ -332,6 +366,191 @@ export function useOuterLineWall() { } } + //이구배 완료될 경우 확인 ↓, ↑, ←, → + const checkDoublePitch = (direction) => { + const activeElem = document.activeElement + + const canDirection = + direction === '↓' || direction === '↑' + ? arrow1Ref.current === '←' || arrow1Ref.current === '→' + : arrow1Ref.current === '↓' || arrow1Ref.current === '↑' + + if (activeElem === length1Ref.current || activeElem === angle1Ref.current) { + setArrow1(direction) + arrow1Ref.current = direction + angle2Ref.current.focus() + } else if (activeElem === length2Ref.current || activeElem === angle2Ref.current) { + if (!canDirection) { + return + } + setArrow2(direction) + arrow2Ref.current = direction + } + + const angle1Value = angle1Ref.current.value + const angle2Value = angle2Ref.current.value + const length1Value = length1Ref.current.value + const length2Value = length2Ref.current.value + + const arrow1Value = arrow1Ref.current + const arrow2Value = arrow2Ref.current + + if (angle1Value !== 0 && length1Value !== 0 && angle2Value !== 0 && arrow1Value !== '' && arrow2Value !== '') { + if (arrow1Value === '↓' && arrow2Value === '→') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x + length1Value / 10, y: prev[prev.length - 1].y + length2Value / 10 }] + }) + } else if (arrow1Value === '↓' && arrow2Value === '←') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x - length1Value / 10, y: prev[prev.length - 1].y + length2Value / 10 }] + }) + } else if (arrow1Value === '↑' && arrow2Value === '→') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x + length1Value / 10, y: prev[prev.length - 1].y - length2Value / 10 }] + }) + } else if (arrow1Value === '↑' && arrow2Value === '←') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x - length1Value / 10, y: prev[prev.length - 1].y - length2Value / 10 }] + }) + } else if (arrow1Value === '→' && arrow2Value === '↓') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x + length2Value / 10, y: prev[prev.length - 1].y + length1Value / 10 }] + }) + } else if (arrow1Value === '→' && arrow2Value === '↑') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x + length2Value / 10, y: prev[prev.length - 1].y - length1Value / 10 }] + }) + } else if (arrow1Value === '←' && arrow2Value === '↓') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x - length2Value / 10, y: prev[prev.length - 1].y + length1Value / 10 }] + }) + } else if (arrow1Value === '←' && arrow2Value === '↑') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x - length2Value / 10, y: prev[prev.length - 1].y - length1Value / 10 }] + }) + } + + angle1Ref.current.focus() + } + } + + //대각선 완료될 경우 확인 + const checkDiagonal = (direction) => { + const activeElem = document.activeElement + + const canDirection = + direction === '↓' || direction === '↑' + ? arrow1Ref.current === '←' || arrow1Ref.current === '→' + : arrow1Ref.current === '↓' || arrow1Ref.current === '↑' + + if (activeElem === length1Ref.current) { + setArrow1(direction) + arrow1Ref.current = direction + } else if (activeElem === length2Ref.current || activeElem === angle2Ref.current) { + if (!canDirection) { + return + } + setArrow2(direction) + arrow2Ref.current = direction + } + + const diagonalLength = outerLineDiagonalLengthRef.current.value // 대각선 길이 + + const length1Value = length1Ref.current.value + + const arrow1Value = arrow1Ref.current + const arrow2Value = arrow2Ref.current + + const getLength2 = () => { + return Math.floor(Math.sqrt(diagonalLength ** 2 - length1Value ** 2)) + } + + const length2Value = getLength2() + + console.log(length2Value) + + if (diagonalLength !== 0 && length1Value !== 0 && arrow1Value !== '') { + setLength2(getLength2()) + length2Ref.current.focus() + } + + if (length1Value !== 0 && length2Value !== 0 && arrow1Value !== '' && arrow2Value !== '') { + if (arrow1Value === '↓' && arrow2Value === '→') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x + length2Value / 10, y: prev[prev.length - 1].y + length1Value / 10 }] + }) + } else if (arrow1Value === '↓' && arrow2Value === '←') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x - length2Value / 10, y: prev[prev.length - 1].y + length1Value / 10 }] + }) + } else if (arrow1Value === '↑' && arrow2Value === '→') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x + length2Value / 10, y: prev[prev.length - 1].y - length1Value / 10 }] + }) + } else if (arrow1Value === '↑' && arrow2Value === '←') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x - length2Value / 10, y: prev[prev.length - 1].y - length1Value / 10 }] + }) + } else if (arrow1Value === '→' && arrow2Value === '↓') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x + length1Value / 10, y: prev[prev.length - 1].y + length2Value / 10 }] + }) + } else if (arrow1Value === '→' && arrow2Value === '↑') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [ + ...prev, + { + x: prev[prev.length - 1].x + length1Value / 10, + y: prev[prev.length - 1].y - length2Value / 10, + }, + ] + }) + } + } + } + const keydown = { outerLine: (e) => { if (points.length === 0) { @@ -411,75 +630,52 @@ export function useOuterLineWall() { switch (key) { case 'Down': // IE/Edge에서 사용되는 값 case 'ArrowDown': { - if (activeElem === length1Ref.current) { - setArrow1('↓') - arrow1Ref.current = '↓' - length2Ref.current.focus() - } else if (activeElem === length2Ref.current) { - if (arrow1Ref.current === '↓' || arrow1Ref.current === '↑') { - break - } - setArrow2('↓') - arrow2Ref.current = '↓' - checkRightAngle() - } - + checkRightAngle('↓') break } case 'Up': // IE/Edge에서 사용되는 값 case 'ArrowUp': - if (activeElem === length1Ref.current) { - setArrow1('↑') - arrow1Ref.current = '↑' - length2Ref.current.focus() - } else if (activeElem === length2Ref.current) { - if (arrow1Ref.current === '↓' || arrow1Ref.current === '↑') { - break - } - setArrow2('↑') - arrow2Ref.current = '↑' - checkRightAngle() - } - + checkRightAngle('↑') break case 'Left': // IE/Edge에서 사용되는 값 case 'ArrowLeft': - if (activeElem === length1Ref.current) { - setArrow1('←') - arrow1Ref.current = '←' - length2Ref.current.focus() - } else if (activeElem === length2Ref.current) { - if (arrow1Ref.current === '←' || arrow1Ref.current === '→') { - break - } - setArrow2('←') - arrow2Ref.current = '←' - checkRightAngle() - } - + checkRightAngle('←') break case 'Right': // IE/Edge에서 사용되는 값 case 'ArrowRight': - if (activeElem === length1Ref.current) { - setArrow1('→') - arrow1Ref.current = '→' - length2Ref.current.focus() - } else if (activeElem === length2Ref.current) { - if (arrow1Ref.current === '←' || arrow1Ref.current === '→') { - break - } - setArrow2('→') - arrow2Ref.current = '→' - checkRightAngle() - } - + checkRightAngle('→') break } }, - leeGubae: (e) => { - console.log('leegubae') + doublePitch: (e) => { + if (points.length === 0) { + return + } + const key = e.key + switch (key) { + case 'Down': // IE/Edge에서 사용되는 값 + case 'ArrowDown': { + checkDoublePitch('↓') + break + } + case 'Up': // IE/Edge에서 사용되는 값 + case 'ArrowUp': + checkDoublePitch('↑') + break + case 'Left': // IE/Edge에서 사용되는 값 + case 'ArrowLeft': + checkDoublePitch('←') + break + case 'Right': // IE/Edge에서 사용되는 값 + case 'ArrowRight': + checkDoublePitch('→') + break + } }, angle: (e) => { + if (points.length === 0) { + return + } const key = e.key switch (key) { case 'Enter': { @@ -501,7 +697,30 @@ export function useOuterLineWall() { } }, diagonalLine: (e) => { - console.log('diagonalLine') + if (points.length === 0) { + return + } + + const key = e.key + switch (key) { + case 'Down': // IE/Edge에서 사용되는 값 + case 'ArrowDown': { + checkDiagonal('↓') + break + } + case 'Up': // IE/Edge에서 사용되는 값 + case 'ArrowUp': + checkDiagonal('↑') + break + case 'Left': // IE/Edge에서 사용되는 값 + case 'ArrowLeft': + checkDiagonal('←') + break + case 'Right': // IE/Edge에서 사용되는 값 + case 'ArrowRight': + checkDiagonal('→') + break + } }, } @@ -513,7 +732,7 @@ export function useOuterLineWall() { setPoints((prev) => prev.slice(0, prev.length - 1)) } - const handleFix = () => { + const handleFix = (fn) => { if (points.length < 3) { return } @@ -541,6 +760,9 @@ export function useOuterLineWall() { setPoints((prev) => { return [...prev, { x: prev[0].x, y: prev[0].y }] }) + + isFix.current = true + closeModalFn.current = fn } return { @@ -558,6 +780,15 @@ export function useOuterLineWall() { setArrow2, arrow1Ref, arrow2Ref, + angle1, + setAngle1, + angle1Ref, + angle2, + setAngle2, + angle2Ref, + outerLineDiagonalLength, + setOuterLineDiagonalLength, + outerLineDiagonalLengthRef, type, setType, handleFix, diff --git a/src/hooks/roofcover/usePropertiesSetting.js b/src/hooks/roofcover/usePropertiesSetting.js new file mode 100644 index 00000000..95800dfd --- /dev/null +++ b/src/hooks/roofcover/usePropertiesSetting.js @@ -0,0 +1,89 @@ +import { useEffect, useRef } from 'react' +import { useRecoilValue } from 'recoil' +import { canvasState } from '@/store/canvasAtom' +import { LINE_TYPE } from '@/common/common' + +export function usePropertiesSetting() { + const currentLine = useRef(null) + const canvas = useRecoilValue(canvasState) + + useEffect(() => { + initLineSetting() + }, []) + + useEffect(() => { + const lines = canvas?.getObjects().filter((obj) => obj.name === 'outerLine') + lines.forEach((line) => { + line.set({ + type: line.type, + stroke: line.type === LINE_TYPE.WALLLINE.EAVES ? '#45CD7D' : line.type === LINE_TYPE.WALLLINE.GABLE ? '#3FBAE6' : '#000000', + strokeWidth: 4, + }) + }) + }, [currentLine.current]) + + const handleSetEaves = () => { + currentLine.current.set({ + stroke: '#45CD7D', + strokeWidth: 4, + attributes: { + offset: 500, + type: LINE_TYPE.WALLLINE.EAVES, + pitch: 4, + }, + }) + + nextLineSetting() + } + + const handleSetGable = () => { + currentLine.current.set({ + type: LINE_TYPE.WALLLINE.GABLE, + stroke: '#3FBAE6', + strokeWidth: 4, + attributes: { + offset: 300, + type: LINE_TYPE.WALLLINE.GABLE, + }, + }) + + nextLineSetting() + } + + const initLineSetting = () => { + currentLine.current = canvas?.getObjects().filter((obj) => obj.name === 'outerLine')[0] + + currentLine.current.set({ + stroke: '#EA10AC', + strokeWidth: 4, + }) + + canvas?.renderAll() + } + + const nextLineSetting = () => { + // currentLine.current의 값은 currentLine.current의 idx 다음 값, 없을 경우는 null + currentLine.current = canvas?.getObjects().find((obj) => obj.idx === currentLine.current.idx + 1) + + if (!currentLine.current) { + canvas?.renderAll() + handleFix() + return + } + + currentLine.current = currentLine.current.set({ + stroke: '#EA10AC', + strokeWidth: 4, + }) + + canvas?.renderAll() + } + + const currentLineSetting = () => {} + + const handleRollback = () => {} + + const handleFix = () => {} + + return { handleSetEaves, handleSetGable, handleRollback, handleFix } +} diff --git a/src/hooks/useAdsorptionPoint.js b/src/hooks/useAdsorptionPoint.js index 44e2495b..49d49714 100644 --- a/src/hooks/useAdsorptionPoint.js +++ b/src/hooks/useAdsorptionPoint.js @@ -26,7 +26,7 @@ export function useAdsorptionPoint() { top: pointer.y - 3, x: pointer.x, y: pointer.y, - selectable: false, + selectable: true, name: 'adsorptionPoint', }) diff --git a/src/hooks/useCanvas.js b/src/hooks/useCanvas.js index 37ffdc5f..0f506fd3 100644 --- a/src/hooks/useCanvas.js +++ b/src/hooks/useCanvas.js @@ -36,6 +36,7 @@ export function useCanvas(id) { setCanvas(c) setCanvasForEvent(c) + attachDefaultEventOnCanvas() return () => { // c.dispose() @@ -45,7 +46,6 @@ export function useCanvas(id) { useEffect(() => { // canvas 사이즈가 변경되면 다시 - attachDefaultEventOnCanvas() }, [canvasSize]) useEffect(() => { @@ -91,6 +91,13 @@ export function useCanvas(id) { // settings for all canvas in the app fabric.Object.prototype.transparentCorners = false fabric.Object.prototype.id = uuidv4() + fabric.Object.prototype.selectable = true + fabric.Object.prototype.lockMovementX = true + fabric.Object.prototype.lockMovementY = true + fabric.Object.prototype.lockRotation = true + fabric.Object.prototype.lockScalingX = true + fabric.Object.prototype.lockScalingY = true + fabric.Object.prototype.cornerColor = '#2BEBC8' fabric.Object.prototype.cornerStyle = 'rect' fabric.Object.prototype.cornerStrokeColor = '#2BEBC8' diff --git a/src/hooks/useDotLineGrid.js b/src/hooks/useDotLineGrid.js index 34d82fa7..0c21c030 100644 --- a/src/hooks/useDotLineGrid.js +++ b/src/hooks/useDotLineGrid.js @@ -6,6 +6,7 @@ import { gridColorState } from '@/store/gridAtom' export function useDotLineGrid() { const canvas = useRecoilValue(canvasState) + const gridColor = useRecoilValue(gridColorState) const [dotLineGridSetting, setDotLineGridSettingState] = useRecoilState(dotLineGridSettingState) const interval = useRecoilValue(dotLineIntervalSelector) // 가로 세로 간격 diff --git a/src/hooks/useEvent.js b/src/hooks/useEvent.js index 31a2cd8a..67ff8c44 100644 --- a/src/hooks/useEvent.js +++ b/src/hooks/useEvent.js @@ -1,24 +1,16 @@ import { useEffect, useRef } from 'react' import { useRecoilState, useRecoilValue } from 'recoil' -import { - adsorptionPointAddModeState, - adsorptionPointModeState, - adsorptionRangeState, - canvasState, - canvasZoomState, - currentMenuState, -} from '@/store/canvasAtom' +import { canvasState, canvasZoomState, currentMenuState } from '@/store/canvasAtom' import { fabric } from 'fabric' -import { calculateDistance, calculateIntersection, distanceBetweenPoints, findClosestPoint } from '@/util/canvas-util' +import { calculateDistance, distanceBetweenPoints, findClosestPoint } from '@/util/canvas-util' import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint' -import { useMouse } from '@/hooks/useMouse' import { useDotLineGrid } from '@/hooks/useDotLineGrid' import { useTempGrid } from '@/hooks/useTempGrid' export function useEvent() { const canvas = useRecoilValue(canvasState) const currentMenu = useRecoilValue(currentMenuState) - const keyboardEventListeners = useRef([]) + const documentEventListeners = useRef([]) const mouseEventListeners = useRef([]) const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState) @@ -30,9 +22,8 @@ export function useEvent() { if (!canvas) { return } - removeAllMouseEventListeners() removeAllDocumentEventListeners() - + removeAllMouseEventListeners() /** * wheelEvent */ @@ -193,13 +184,14 @@ export function useEvent() { } const addCanvasMouseEventListener = (eventType, handler) => { + canvas.off(eventType) canvas.on(eventType, handler) mouseEventListeners.current.push({ eventType, handler }) } const removeAllMouseEventListeners = () => { mouseEventListeners.current.forEach(({ eventType, handler }) => { - canvas.off(eventType, handler) + canvas.off(eventType) }) mouseEventListeners.current.length = 0 // 배열 초기화 } @@ -211,24 +203,36 @@ export function useEvent() { * @param handler */ const addDocumentEventListener = (eventType, element, handler) => { + removeDocumentEvent(eventType) element.addEventListener(eventType, handler) - keyboardEventListeners.current.push({ eventType, element, handler }) + documentEventListeners.current.push({ eventType, element, handler }) } /** * document에 등록되는 event 제거 */ const removeAllDocumentEventListeners = () => { - keyboardEventListeners.current.forEach(({ eventType, element, handler }) => { + documentEventListeners.current.forEach(({ eventType, element, handler }) => { element.removeEventListener(eventType, handler) }) - keyboardEventListeners.current.length = 0 // 배열 초기화 + documentEventListeners.current.length = 0 // 배열 초기화 } - const removeMouseEvent = (type, handler) => { + const removeMouseEvent = (type) => { mouseEventListeners.current = mouseEventListeners.current.filter((event) => { - if (event.type === type && event.handler === handler) { - canvas.off(type, handler) + if (event.eventType === type) { + canvas.off(type, event.handler) + return false + } + return true + }) + } + + const removeDocumentEvent = (type) => { + documentEventListeners.current = documentEventListeners.current.filter((event) => { + if (event.eventType === type) { + console.log(type) + event.element.removeEventListener(type, event.handler) return false } return true diff --git a/src/hooks/useLine.js b/src/hooks/useLine.js index e701a2ff..d42af418 100644 --- a/src/hooks/useLine.js +++ b/src/hooks/useLine.js @@ -38,6 +38,7 @@ export const useLine = () => { const removeLine = (line) => { removeLineText(line) canvas?.remove(line) + canvas?.renderAll() } const removeLineText = (line) => { From 05d2db69c376360736e920554baa8d098ab30d0c Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Mon, 30 Sep 2024 16:22:00 +0900 Subject: [PATCH 08/11] =?UTF-8?q?=EC=99=B8=EB=B2=BD=EC=84=A0=20=EC=86=8D?= =?UTF-8?q?=EC=84=B1=20=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../outerlinesetting/PropertiesSetting.jsx | 17 ++- src/hooks/roofcover/usePropertiesSetting.js | 116 ++++++++++++------ 2 files changed, 92 insertions(+), 41 deletions(-) diff --git a/src/components/floor-plan/modal/outerlinesetting/PropertiesSetting.jsx b/src/components/floor-plan/modal/outerlinesetting/PropertiesSetting.jsx index 0120f6a4..cd2973e7 100644 --- a/src/components/floor-plan/modal/outerlinesetting/PropertiesSetting.jsx +++ b/src/components/floor-plan/modal/outerlinesetting/PropertiesSetting.jsx @@ -6,14 +6,19 @@ export default function PropertiesSetting(props) { const { getMessage } = useMessage() const { setShowPropertiesSettingModal } = props - const { handleSetEaves, handleSetGable, handleRollback, handleFix } = usePropertiesSetting() + const { handleSetEaves, handleSetGable, handleRollback, handleFix, closeModal } = usePropertiesSetting() return (

{getMessage('modal.canvas.setting.wallline.properties.setting')}

-
@@ -34,7 +39,13 @@ export default function PropertiesSetting(props) { -
diff --git a/src/hooks/roofcover/usePropertiesSetting.js b/src/hooks/roofcover/usePropertiesSetting.js index 95800dfd..9df385ed 100644 --- a/src/hooks/roofcover/usePropertiesSetting.js +++ b/src/hooks/roofcover/usePropertiesSetting.js @@ -1,27 +1,17 @@ import { useEffect, useRef } from 'react' +import { LINE_TYPE } from '@/common/common' import { useRecoilValue } from 'recoil' import { canvasState } from '@/store/canvasAtom' -import { LINE_TYPE } from '@/common/common' export function usePropertiesSetting() { const currentLine = useRef(null) + const currentIdx = useRef(-1) const canvas = useRecoilValue(canvasState) useEffect(() => { - initLineSetting() + selectNextLine() }, []) - useEffect(() => { - const lines = canvas?.getObjects().filter((obj) => obj.name === 'outerLine') - lines.forEach((line) => { - line.set({ - type: line.type, - stroke: line.type === LINE_TYPE.WALLLINE.EAVES ? '#45CD7D' : line.type === LINE_TYPE.WALLLINE.GABLE ? '#3FBAE6' : '#000000', - strokeWidth: 4, - }) - }) - }, [currentLine.current]) - const handleSetEaves = () => { currentLine.current.set({ stroke: '#45CD7D', @@ -32,13 +22,12 @@ export function usePropertiesSetting() { pitch: 4, }, }) - - nextLineSetting() + canvas.renderAll() + selectNextLine() } const handleSetGable = () => { currentLine.current.set({ - type: LINE_TYPE.WALLLINE.GABLE, stroke: '#3FBAE6', strokeWidth: 4, attributes: { @@ -46,44 +35,95 @@ export function usePropertiesSetting() { type: LINE_TYPE.WALLLINE.GABLE, }, }) - - nextLineSetting() + canvas.renderAll() + selectNextLine() } - const initLineSetting = () => { - currentLine.current = canvas?.getObjects().filter((obj) => obj.name === 'outerLine')[0] + const selectNextLine = () => { + const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') + currentIdx.current++ + + if (currentIdx.current >= lines.length) { + currentIdx.current = lines.length + currentLine.current = lines[currentIdx.current - 1] + return + } + + currentLine.current = lines[currentIdx.current] currentLine.current.set({ stroke: '#EA10AC', strokeWidth: 4, }) - canvas?.renderAll() + canvas.renderAll() } - const nextLineSetting = () => { - // currentLine.current의 값은 currentLine.current의 idx 다음 값, 없을 경우는 null - currentLine.current = canvas?.getObjects().find((obj) => obj.idx === currentLine.current.idx + 1) + const selectPrevLine = () => { + const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') - if (!currentLine.current) { - canvas?.renderAll() - handleFix() + currentIdx.current-- + + if (currentIdx.current <= -1) { + currentIdx.current = -1 + selectNextLine() + return + } else { + lines.forEach((line, index) => { + if (index >= currentIdx.current) { + delete line.attributes + line.set({ + stroke: '#000000', + strokeWidth: 4, + }) + } + currentIdx.current-- + canvas.renderAll() + selectNextLine() + }) + } + } + + const handleRollback = () => { + selectPrevLine() + } + + const handleFix = () => { + if (!confirm('외벽선 속성 설정을 완료하시겠습니까?')) { + return + } + const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') + + lines.forEach((line) => { + line.set({ + attributes: line.attributes ? line.attributes : { offset: 0, type: LINE_TYPE.WALLLINE.WALL }, + stroke: '#000000', + strokeWidth: 4, + }) + }) + + canvas.renderAll() + } + + const closeModal = (fn) => { + if (!confirm('외벽선 속성 설정을 종료 하시겠습니까?')) { return } - currentLine.current = currentLine.current.set({ - stroke: '#EA10AC', - strokeWidth: 4, + const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') + + lines.forEach((line) => { + line.set({ + attributes: { offset: 0, type: LINE_TYPE.WALLLINE.WALL }, + stroke: '#000000', + strokeWidth: 4, + }) }) - canvas?.renderAll() + canvas.renderAll() + + fn(false) } - const currentLineSetting = () => {} - - const handleRollback = () => {} - - const handleFix = () => {} - - return { handleSetEaves, handleSetGable, handleRollback, handleFix } + return { handleSetEaves, handleSetGable, handleRollback, handleFix, closeModal } } From 4ee102f1b9b1bc777396efd4d6302da6127ec928 Mon Sep 17 00:00:00 2001 From: Daseul Kim Date: Mon, 30 Sep 2024 16:36:10 +0900 Subject: [PATCH 09/11] =?UTF-8?q?feat:=20canvas=20plan=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/floor-plan/CanvasLayout.jsx | 55 +++++++++++++++------- src/hooks/useAxios.js | 6 ++- src/hooks/usePlan.js | 17 ++++++- 3 files changed, 60 insertions(+), 18 deletions(-) diff --git a/src/components/floor-plan/CanvasLayout.jsx b/src/components/floor-plan/CanvasLayout.jsx index e71c01f5..3abebd85 100644 --- a/src/components/floor-plan/CanvasLayout.jsx +++ b/src/components/floor-plan/CanvasLayout.jsx @@ -3,6 +3,8 @@ import { useEffect, useState } from 'react' import { useRecoilState, useRecoilValue } from 'recoil' import CanvasFrame from './CanvasFrame' +import { useMessage } from '@/hooks/useMessage' +import { useSwal } from '@/hooks/useSwal' import { usePlan } from '@/hooks/usePlan' import { globalLocaleStore } from '@/store/localeAtom' import { currentCanvasPlanState, initCanvasPlansState } from '@/store/canvasAtom' @@ -17,7 +19,9 @@ export default function CanvasLayout() { const globalLocaleState = useRecoilValue(globalLocaleStore) const sessionState = useRecoilValue(sessionStore) - const { getCanvasByObjectNo } = usePlan() + const { getMessage } = useMessage() + const { swalFire } = useSwal() + const { getCanvasByObjectNo, delCanvasById } = usePlan() const handleCurrentPlan = (newCurrentId) => { if (!currentCanvasPlan?.id || currentCanvasPlan.id !== newCurrentId) { @@ -40,21 +44,29 @@ export default function CanvasLayout() { const handleDeletePlan = (e, id) => { e.stopPropagation() // 이벤트 버블링 방지 - // 삭제할 아이디와 다른 아이템만 남김 - const filterInitPlans = initCanvasPlans.filter((plan) => plan.id !== id) - setInitCanvasPlans(filterInitPlans) - const filterAddPlans = addCanvasPlans.filter((plan) => plan.id !== id) - setAddCanvasPlans(filterAddPlans) - - const combinedPlans = [...filterInitPlans, ...filterAddPlans] - if (combinedPlans.length === 0) { - // 모든 데이터가 삭제된 경우 - setPlanNum(0) + if (initCanvasPlans.some((plan) => plan.id === id)) { + delCanvasById(id) + .then((res) => { + swalFire({ text: getMessage('common.message.delete') }) + console.log('[DELETE] canvas-statuses res :::::::: %o', res) + setInitCanvasPlans((initCanvasPlans) => initCanvasPlans.filter((plan) => plan.id !== id)) + }) + .catch((error) => { + swalFire({ text: error.message, icon: 'error' }) + console.error('[DELETE] canvas-statuses res error :::::::: %o', error) + }) } else { - const lastPlanId = combinedPlans.at(-1).id - if (id !== lastPlanId) { - handleCurrentPlan(lastPlanId) - } + setAddCanvasPlans(addCanvasPlans.filter((plan) => plan.id !== id)) + swalFire({ text: getMessage('common.message.delete') }) + } + + // 삭제 후 last 데이터에 포커싱 + const lastPlan = [...initCanvasPlans, ...addCanvasPlans].filter((plan) => plan.id !== id).at(-1) + if (!lastPlan) { + setPlanNum(0) + setCurrentCanvasPlan(null) + } else if (id !== lastPlan.id) { + handleCurrentPlan(lastPlan.id) } } @@ -84,7 +96,18 @@ export default function CanvasLayout() { {[...initCanvasPlans, ...addCanvasPlans].map((plan) => ( ))} diff --git a/src/hooks/useAxios.js b/src/hooks/useAxios.js index a1ecda2a..1a7cd03e 100644 --- a/src/hooks/useAxios.js +++ b/src/hooks/useAxios.js @@ -79,5 +79,9 @@ export function useAxios(lang = '') { .catch(console.error) } - return { get, promiseGet, post, promisePost, put, promisePut, patch, del } + const promiseDel = async ({ url }) => { + return await getInstances(url).delete(url) + } + + return { get, promiseGet, post, promisePost, put, promisePut, patch, del, promiseDel } } diff --git a/src/hooks/usePlan.js b/src/hooks/usePlan.js index cf566b3f..0d28d94d 100644 --- a/src/hooks/usePlan.js +++ b/src/hooks/usePlan.js @@ -11,7 +11,7 @@ export function usePlan() { const [currentCanvasPlan, setcurrentCanvasPlan] = useRecoilState(currentCanvasPlanState) const [initCanvasPlans, setInitCanvasPlans] = useRecoilState(initCanvasPlansState) const { getMessage } = useMessage() - const { get, promisePost, promisePut } = useAxios() + const { get, promisePost, promisePut, promiseDel } = useAxios() /** * 마우스 포인터의 가이드라인을 제거합니다. @@ -147,11 +147,26 @@ export function usePlan() { ) } + /** + * id에 해당하는 canvas 데이터를 삭제하는 함수 + */ + const delCanvasById = (id) => { + return promiseDel({ url: `/api/canvas-management/canvas-statuses/by-id/${id}` }) + } + + /** + * objectNo에 해당하는 canvas 데이터들을 삭제하는 함수 + */ + const delCanvasByObjectNo = (objectNo) => { + return promiseDel({ url: `/api/canvas-management/canvas-statuses/by-object/${objectNo}` }) + } + return { canvas, removeMouseLines, saveCanvas, addCanvas, getCanvasByObjectNo, + delCanvasById, } } From 82e751f2ca447fb2af0c8711b5a270cd874a9d3c Mon Sep 17 00:00:00 2001 From: Daseul Kim Date: Mon, 30 Sep 2024 17:08:40 +0900 Subject: [PATCH 10/11] =?UTF-8?q?refactor:=20canvas=20plan=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=20=EC=8B=9C=20swalFire(confirm)=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/floor-plan/CanvasMenu.jsx | 13 +++++++++++-- src/hooks/usePlan.js | 13 ++++++------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/components/floor-plan/CanvasMenu.jsx b/src/components/floor-plan/CanvasMenu.jsx index 99b3e048..07b248cb 100644 --- a/src/components/floor-plan/CanvasMenu.jsx +++ b/src/components/floor-plan/CanvasMenu.jsx @@ -9,7 +9,8 @@ import QSelectBox from '@/components/common/select/QSelectBox' import { useMessage } from '@/hooks/useMessage' import { usePlan } from '@/hooks/usePlan' -import { canvasState, canvasZoomState, currentMenuState, verticalHorizontalModeState } from '@/store/canvasAtom' +import { useSwal } from '@/hooks/useSwal' +import { canvasState, canvasZoomState, currentMenuState, currentCanvasPlanState, verticalHorizontalModeState } from '@/store/canvasAtom' import { sessionStore } from '@/store/commonAtom' import { outerLinePointsState } from '@/store/outerLineAtom' import { appMessageStore, globalLocaleStore } from '@/store/localeAtom' @@ -40,6 +41,7 @@ export default function CanvasMenu(props) { const setCurrentMenu = useSetRecoilState(currentMenuState) const setPoints = useSetRecoilState(outerLinePointsState) const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState) + const [currentCanvasPlan, setcurrentCanvasPlan] = useRecoilState(currentCanvasPlanState) const globalLocale = useRecoilValue(globalLocaleStore) const canvas = useRecoilValue(canvasState) @@ -47,6 +49,7 @@ export default function CanvasMenu(props) { const { getMessage } = useMessage() const { saveCanvas } = usePlan() + const { swalFire } = useSwal() const SelectOption = [{ name: '瓦53A' }, { name: '瓦53A' }] const onClickNav = (menu) => { @@ -78,7 +81,13 @@ export default function CanvasMenu(props) { // 저장버튼(btn08) 클릭 시 호출되는 함수 const handleSaveCanvas = () => { - saveCanvas(sessionState.userId) + swalFire({ + html: getMessage('common.message.confirm.save') + `
${currentCanvasPlan.name}`, + type: 'confirm', + confirmFn: () => { + saveCanvas(sessionState.userId) + }, + }) } const handleClear = () => { diff --git a/src/hooks/usePlan.js b/src/hooks/usePlan.js index 0d28d94d..fedbb3be 100644 --- a/src/hooks/usePlan.js +++ b/src/hooks/usePlan.js @@ -2,14 +2,13 @@ import { useRecoilState } from 'recoil' import { canvasState, currentCanvasPlanState, initCanvasPlansState } from '@/store/canvasAtom' import { useAxios } from '@/hooks/useAxios' import { useMessage } from '@/hooks/useMessage' -import { toastUp } from '@/hooks/useToast' -import { sessionStore } from '@/store/commonAtom' -import { useState } from 'react' +import { useSwal } from '@/hooks/useSwal' export function usePlan() { const [canvas, setCanvas] = useRecoilState(canvasState) const [currentCanvasPlan, setcurrentCanvasPlan] = useRecoilState(currentCanvasPlanState) const [initCanvasPlans, setInitCanvasPlans] = useRecoilState(initCanvasPlansState) + const { swalFire } = useSwal() const { getMessage } = useMessage() const { get, promisePost, promisePut, promiseDel } = useAxios() @@ -101,14 +100,14 @@ export function usePlan() { await promisePut({ url: '/api/canvas-management/canvas-statuses', data: planData }) .then((res) => { - toastUp({ message: getMessage('common.message.save'), type: 'success' }) // 성공 시 메세지 없음 + swalFire({ text: getMessage('common.message.save') }) console.log('[PUT] canvas-statuses res :::::::: %o', res) setInitCanvasPlans((initCanvasPlans) => initCanvasPlans.map((plan) => (plan.id === currentCanvasPlan.id ? { ...plan, canvasStatus: canvasStatus } : plan)), ) }) .catch((error) => { - toastUp({ message: error.message, type: 'error' }) + swalFire({ text: error.message, icon: 'error' }) console.error('[PUT] canvas-statuses error :::::::: %o', error) }) } else { @@ -122,11 +121,11 @@ export function usePlan() { await promisePost({ url: '/api/canvas-management/canvas-statuses', data: planData }) .then((res) => { - toastUp({ message: getMessage('common.message.save'), type: 'success' }) // 성공 시 메세지 없음 + swalFire({ text: getMessage('common.message.save') }) console.log('[POST] canvas-statuses response :::::::: %o', res) }) .catch((error) => { - toastUp({ message: error.message, type: 'error' }) + swalFire({ text: error.message, icon: 'error' }) console.error('[POST] canvas-statuses res error :::::::: %o', error) }) } From 71e3d78c9b70b6a2c839ff4edebc542dc5fc6484 Mon Sep 17 00:00:00 2001 From: yjnoh Date: Mon, 30 Sep 2024 17:09:31 +0900 Subject: [PATCH 11/11] =?UTF-8?q?=EB=AA=A8=EB=93=88=20=ED=9D=A1=EC=B0=A9?= =?UTF-8?q?=EC=A0=90=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useMode.js | 195 +++++++++++++++++++++++++++---------------- 1 file changed, 121 insertions(+), 74 deletions(-) diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index c2b29121..91444740 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -39,6 +39,7 @@ import * as turf from '@turf/turf' import { INPUT_TYPE, Mode } from '@/common/common' import { m } from 'framer-motion' import { set } from 'react-hook-form' +import { FaWineGlassEmpty } from 'react-icons/fa6' export function useMode() { const [mode, setMode] = useRecoilState(modeState) @@ -4941,6 +4942,12 @@ export function useMode() { ) } + const coordToTurfPolygon = (points) => { + const coordinates = points.map((point) => [point.x, point.y]) + coordinates.push(coordinates[0]) + return turf.polygon([coordinates]) + } + /** * trestle에서 영역을 가져와 mouse:move 이벤트로 해당 영역에 진입했을때 booleanPointInPolygon 로 진입여부를 확인 * 확인 후 셀을 이동시킴 @@ -4949,58 +4956,125 @@ export function useMode() { const trestlePolygons = canvas?.getObjects().filter((obj) => obj.name === 'trestle') //가대를 가져옴 if (trestlePolygons.length !== 0) { - let lastPointPosition = { x: 0, y: 0 } let fabricPolygon = null let inside = false let turfPolygon - let manualDrawCells = drewRoofCells // - + let manualDrawCells = drewRoofCells // 앞에서 자동으로 했을때 추가됨 + let direction canvas.on('mouse:move', (e) => { //마우스 이벤트 삭제 후 재추가 const mousePoint = canvas.getPointer(e.e) - const turfPoint = turf.point([mousePoint.x, mousePoint.y]) - for (let i = 0; i < trestlePolygons.length; i++) { turfPolygon = polygonToTurfPolygon(trestlePolygons[i]) - if (turf.booleanPointInPolygon(turfPoint, turfPolygon)) { + direction = trestlePolygons[i].direction //도형의 방향 + let width = direction === 'south' || direction === 'north' ? 172 : 113 + let height = direction === 'south' || direction === 'north' ? 113 : 172 + + const points = [ + { x: mousePoint.x - width / 2, y: mousePoint.y - height / 2 }, + { x: mousePoint.x + width / 2, y: mousePoint.y - height / 2 }, + { x: mousePoint.x + width / 2, y: mousePoint.y + height / 2 }, + { x: mousePoint.x - width / 2, y: mousePoint.y + height / 2 }, + ] + + const turfPoints = coordToTurfPolygon(points) + + if (turf.booleanWithin(turfPoints, turfPolygon)) { //turf에 보면 폴리곤안에 포인트가 있는지 함수가 있다 - const direction = trestlePolygons[i].direction //도형의 방향 - let width = direction === 'south' || direction === 'north' ? 172.2 : 113.4 - let height = direction === 'south' || direction === 'north' ? 113.4 : 172.2 - if (Math.abs(mousePoint.x - lastPointPosition.x) >= 5 || Math.abs(mousePoint.y - lastPointPosition.y) >= 5) { - let isDrawing = false - if (isDrawing) return - canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'tmpCell')) //움직일때 일단 지워가면서 움직임 + // if (Math.abs(mousePoint.x - lastPointPosition.x) >= 5 || Math.abs(mousePoint.y - lastPointPosition.y) >= 5) { + let isDrawing = false - const points = [ - { x: mousePoint.x - width / 2, y: mousePoint.y - height / 2 }, - { x: mousePoint.x + width / 2, y: mousePoint.y - height / 2 }, - { x: mousePoint.x + width / 2, y: mousePoint.y + height / 2 }, - { x: mousePoint.x - width / 2, y: mousePoint.y + height / 2 }, - ] + if (isDrawing) return + canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'tmpCell')) //움직일때 일단 지워가면서 움직임 - fabricPolygon = new QPolygon(points, { - fill: '#BFFD9F', - stroke: 'black', - selectable: false, // 선택 가능하게 설정 - lockMovementX: true, // X 축 이동 잠금 - lockMovementY: true, // Y 축 이동 잠금 - lockRotation: true, // 회전 잠금 - lockScalingX: true, // X 축 크기 조정 잠금 - lockScalingY: true, // Y 축 크기 조정 잠금 - opacity: 0.8, - parentId: trestlePolygons[i].parentId, - name: 'tmpCell', - }) + fabricPolygon = new QPolygon(points, { + fill: '#BFFD9F', + // stroke: 'black', + // strokeWidth: 1, + selectable: false, // 선택 가능하게 설정 + lockMovementX: true, // X 축 이동 잠금 + lockMovementY: true, // Y 축 이동 잠금 + lockRotation: true, // 회전 잠금 + lockScalingX: true, // X 축 크기 조정 잠금 + lockScalingY: true, // Y 축 크기 조정 잠금 + opacity: 0.8, + parentId: trestlePolygons[i].parentId, + name: 'tmpCell', + }) - canvas?.add(fabricPolygon) //움직여가면서 추가됨 - lastPointPosition = { x: mousePoint.x, y: mousePoint.y } + canvas?.add(fabricPolygon) //움직여가면서 추가됨 + + /** + * 스냅기능 + */ + let snapDistance = 20 + + const bigLeft = trestlePolygons[i].left + const bigTop = trestlePolygons[i].top + const bigRight = bigLeft + trestlePolygons[i].width * trestlePolygons[i].scaleX + const bigBottom = bigTop + trestlePolygons[i].height * trestlePolygons[i].scaleY + const bigCenter = (bigTop + bigTop + trestlePolygons[i].height) / 2 + + // 작은 폴리곤의 경계 좌표 계산 + const smallLeft = fabricPolygon.left + const smallTop = fabricPolygon.top + const smallRight = smallLeft + fabricPolygon.width * fabricPolygon.scaleX + const smallBottom = smallTop + fabricPolygon.height * fabricPolygon.scaleY + const smallCenter = smallLeft + (fabricPolygon.width * fabricPolygon.scaleX) / 2 + + // 위쪽 변에 스냅 + if (Math.abs(smallTop - bigTop) < snapDistance) { + fabricPolygon.top = bigTop } + // 아래쪽 변에 스냅 + if (Math.abs(smallTop + fabricPolygon.height * fabricPolygon.scaleY - (bigTop + trestlePolygons[i].height)) < snapDistance) { + fabricPolygon.top = bigTop + trestlePolygons[i].height - fabricPolygon.height * fabricPolygon.scaleY + } + + // 왼쪽변에 스냅 + if (Math.abs(smallLeft - bigLeft) < snapDistance) { + fabricPolygon.left = bigLeft + } + //오른쪽 변에 스냅 + if (Math.abs(smallRight - bigRight) < snapDistance) { + fabricPolygon.left = bigRight - fabricPolygon.width * fabricPolygon.scaleX + } + + if (direction === 'south' || direction === 'north') { + // 모듈왼쪽이 세로중앙선에 붙게 스냅 + if (Math.abs(smallLeft - (bigLeft + trestlePolygons[i].width / 2)) < snapDistance) { + fabricPolygon.left = bigLeft + trestlePolygons[i].width / 2 + } + + // 모듈이 가운데가 세로중앙선에 붙게 스냅 + if (Math.abs(smallCenter - (bigLeft + trestlePolygons[i].width / 2)) < snapDistance) { + fabricPolygon.left = bigLeft + trestlePolygons[i].width / 2 - (fabricPolygon.width * fabricPolygon.scaleX) / 2 + } + + // 모듈오른쪽이 세로중앙선에 붙게 스냅 + if (Math.abs(smallRight - (bigLeft + trestlePolygons[i].width / 2)) < snapDistance) { + fabricPolygon.left = bigLeft + trestlePolygons[i].width / 2 - fabricPolygon.width * fabricPolygon.scaleX + } + } else { + // 모듈이 가로중앙선에 스냅 + if (Math.abs(smallTop + fabricPolygon.height / 2 - bigCenter) < snapDistance) { + fabricPolygon.top = bigCenter - fabricPolygon.height / 2 + } + + if (Math.abs(smallTop - (bigTop + trestlePolygons[i].height / 2)) < snapDistance) { + fabricPolygon.top = bigTop + trestlePolygons[i].height / 2 + } + // 모듈 밑면이 가로중앙선에 스냅 + if (Math.abs(smallBottom - (bigTop + trestlePolygons[i].height / 2)) < snapDistance) { + fabricPolygon.top = bigTop + trestlePolygons[i].height / 2 - fabricPolygon.height * fabricPolygon.scaleY + } + } + + fabricPolygon.setCoords() canvas?.renderAll() inside = true - break } else { inside = false } @@ -5016,20 +5090,26 @@ export function useMode() { if (!inside) return if (fabricPolygon) { const turfCellPolygon = polygonToTurfPolygon(fabricPolygon) - + fabricPolygon.setCoords() //좌표 재정렬 if (turf.booleanWithin(turfCellPolygon, turfPolygon)) { //마우스 클릭시 set으로 해당 위치에 셀을 넣음 + + manualDrawCells.forEach((cell) => { + console.log('cells', cell.points) + }) + console.log('turfCellPolygon', turfCellPolygon.geometry.coordinates) + const isOverlap = manualDrawCells.some((cell) => turf.booleanOverlap(turfCellPolygon, polygonToTurfPolygon(cell))) if (!isOverlap) { //안겹치면 넣는다 - fabricPolygon.set({ name: 'cell' }) fabricPolygon.setCoords() + fabricPolygon.set({ name: 'cell' }) manualDrawCells.push(fabricPolygon) } else { alert('셀끼리 겹치면 안되죠?') } - } else { - alert('나갔으요!!') + // } else { + // alert('나갔으요!!') } setDrewRoofCells(manualDrawCells) } @@ -5106,27 +5186,6 @@ export function useMode() { // console.log('bbox', bbox) - const boxes = [] - const installedCellsArray = [] - - for (let x = bbox[0]; x < bbox[2]; x += width) { - for (let y = bbox[1]; y < bbox[3]; y += height) { - const box = turf.polygon([ - [ - [x, y], - [x + width, y], - [x + width, y + height], - [x, y + height], - [x, y], - ], - ]) - - if (turf.booleanWithin(box, turfTrestlePolygon)) { - boxes.push(box) - } - } - } - for (let col = 0; col <= cols; col++) { for (let row = 0; row <= rows; row++) { let x = 0, @@ -5176,20 +5235,6 @@ export function useMode() { const squarePolygon = turf.polygon([square]) - // console.log('turfTrestlePolygon', turfTrestlePolygon) - // console.log('squarePolygon', squarePolygon) - - const areaSize = turf.area(turfTrestlePolygon) - - // console.log('areaSize', areaSize) - const objSize = turf.area(squarePolygon) - - // console.log('objSize', objSize) - - const maxObject = Math.floor(areaSize / objSize) - - // console.log('maxObjectSize', maxObject) - const disjointFromTrestle = turf.booleanContains(turfTrestlePolygon, squarePolygon) || turf.booleanWithin(squarePolygon, turfTrestlePolygon) if (disjointFromTrestle) { @@ -5230,6 +5275,8 @@ export function useMode() { lockScalingY: true, // Y 축 크기 조정 잠금 opacity: 0.8, parentId: trestle.parentId, + lineCol: col, + lineRow: row, }) canvas?.add(fabricPolygon) drawCellsArray.push(fabricPolygon)