From f185912c293661a0838d12f39f71d024809f3adb Mon Sep 17 00:00:00 2001 From: Jaeyoung Lee Date: Fri, 13 Sep 2024 14:15:44 +0900 Subject: [PATCH 01/25] =?UTF-8?q?=EC=A7=80=EB=B6=95=20=EA=B7=B8=EB=A6=AC?= =?UTF-8?q?=EA=B8=B0=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Roof2.jsx | 11 +- src/hooks/useMode.js | 466 ++++++++++++++++----------------------- 2 files changed, 206 insertions(+), 271 deletions(-) diff --git a/src/components/Roof2.jsx b/src/components/Roof2.jsx index d8b4c25e..e9da0e7a 100644 --- a/src/components/Roof2.jsx +++ b/src/components/Roof2.jsx @@ -405,6 +405,15 @@ export default function Roof2(props) { { x: 600, y: 100 }, ] + const type1C = [ + { x: 100, y: 100 }, + { x: 850, y: 100 }, + { x: 850, y: 800 }, + { x: 550, y: 800 }, + { x: 500, y: 400 }, + { x: 100, y: 400 }, + ] + const types = [type1, type2, type3, type4, type1A, type1B, eightPoint, eightPoint2, eightPoint3, eightPoint4, twelvePoint] const newP = [ { x: 450, y: 450 }, @@ -413,7 +422,7 @@ export default function Roof2(props) { { x: 450, y: 850 }, ] - const polygon = new QPolygon(twelvePoint, { + const polygon = new QPolygon(type1C, { fill: 'transparent', stroke: 'green', strokeWidth: 1, diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index d427b548..bc650216 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -1263,7 +1263,8 @@ export function useMode() { historyPoints.current = [] const wall = makePolygon(null, sort) - wall.set({ name: 'wall' }) + wall.name = 'wall' + // wall.set({ name: 'wall' }) return wall } @@ -1471,7 +1472,8 @@ export function useMode() { *벽 지붕 외곽선 생성 polygon을 입력받아 만들기 */ const handleOuterlinesTest2 = (polygon, offset = 50) => { - const offsetPoints = offsetPolygon(polygon.points, offset) + drawRoofPolygon(polygon, offset) + /*const offsetPoints = offsetPolygon(polygon.points, offset) const roof = makePolygon( offsetPoints.map((point) => { @@ -1484,287 +1486,211 @@ export function useMode() { setWall(polygon) roof.drawHelpLine() - roof.divideLine() + roof.divideLine()*/ } const drawRoofPolygon = (wall, offset = 50) => { - console.log(wall) - let points = wall.points, - expandedPoints = [] - - let minX = points[0].x, - minY = points[0].y, - maxX = points[0].x, - maxY = points[0].y - - points.forEach((point) => { - if (point.x < minX) minX = point.x - if (point.y < minY) minY = point.y - if (point.x > maxX) maxX = point.x - if (point.y > maxY) maxY = point.y - }) - - console.log(points) - points.forEach((point, index) => { - const prevIndex = index === 0 ? points.length - 1 : index - 1 - const nextIndex = index === points.length - 1 ? 0 : index + 1 - point.direction = getDirectionByPoint(point, points[nextIndex]) - point.length = Math.abs(point.x - points[nextIndex].x) === 0 ? Math.abs(point.y - points[nextIndex].y) : Math.abs(point.x - points[nextIndex].x) - // point.degree = Math.round(getDegreeBetweenTwoLines(points[prevIndex], point, points[nextIndex])) - }) - console.log('points : ', points) - - points.forEach((currentWall, index) => { - let prevWall = points[index === 0 ? points.length - 1 : index - 1] - let nextWall = points[index === points.length - 1 ? 0 : index + 1] - let isStartPointIn = minX < currentWall.x && currentWall.x < maxX && minY < currentWall.y && currentWall.y < maxY - // let isEndPointIn = minX < currentWall.x2 && currentWall.x2 < maxX && minY < currentWall.y2 && currentWall.y2 < maxY - - if (prevWall.direction !== nextWall.direction) { - if (currentWall.direction === 'top') { - console.log('prevWall degree : ', 45) - if (prevWall.direction === 'right') { - let addLength = getLineOffsetPoint(prevWall, currentWall, nextWall, offset) - expandedPoints.push({ - x: currentWall.x + addLength, - y: currentWall.y + addLength, - }) - } - if (prevWall.direction === 'left') { - console.log('prevWall degree : ', 225) - expandedPoints.push({ - x: currentWall.x - offset, - y: currentWall.y + offset, - }) - } - } - if (currentWall.direction === 'bottom') { - if (prevWall.direction === 'right') { - console.log('prevWall degree : ', 45) - let addLength = getLineOffsetPoint(prevWall, currentWall, nextWall, offset) - console.log(currentWall.x, '-', offset, '+', addLength) - console.log('addLength : ', addLength) - expandedPoints.push({ - x: currentWall.x + addLength, - y: currentWall.y - addLength, - }) - } - if (prevWall.direction === 'left') { - console.log('prevWall degree : ', 315) - let addLength = getLineOffsetPoint(prevWall, currentWall, nextWall, offset) - console.log(currentWall.x, '-', offset, '+', addLength) - console.log('addLength : ', addLength) - expandedPoints.push({ - x: currentWall.x - offset, - y: currentWall.y - offset, - }) - } - } - if (currentWall.direction === 'right') { - if (prevWall.direction === 'top') { - if (isStartPointIn) { - console.log('prevWall degree : ', 135) - expandedPoints.push({ - x: currentWall.x + offset, - y: currentWall.y + offset, - }) - } else { - console.log('prevWall degree : ', 315) - expandedPoints.push({ - x: currentWall.x - offset, - y: currentWall.y - offset, - }) - } - } - if (prevWall.direction === 'bottom') { - if (isStartPointIn) { - console.log('prevWall degree : ', 45) - - expandedPoints.push({ - x: currentWall.x + offset, - y: currentWall.y - offset, - }) - } else { - console.log('prevWall degree : ', 225) - let addLength = getLineOffsetPoint(prevWall, currentWall, nextWall, offset) - console.log('addLength : ', addLength) - expandedPoints.push({ - x: currentWall.x - offset, - y: currentWall.y + offset, - }) - } - } - } - if (currentWall.direction === 'left') { - if (prevWall.direction === 'top') { - if (isStartPointIn) { - console.log('prevWall degree : ', 225) - let addLength = getLineOffsetPoint(prevWall, currentWall, nextWall, offset) - console.log('addLength : ', addLength) - expandedPoints.push({ - x: currentWall.x - offset, - y: currentWall.y + offset, - }) - } else { - console.log('prevWall degree : ', 45) - - let addLength = getLineOffsetPoint(prevWall, currentWall, nextWall, offset) - - expandedPoints.push({ - x: currentWall.x + offset, - y: currentWall.y - offset, - }) - } - } - if (prevWall.direction === 'bottom') { - if (isStartPointIn) { - console.log('prevWall degree : ', 315) - expandedPoints.push({ - x: currentWall.x - offset, - y: currentWall.y - offset, - }) - } else { - console.log('prevWall degree : ', 135) - expandedPoints.push({ - x: currentWall.x + offset, - y: currentWall.y + offset, - }) - } - } + let walls = wall.lines + walls.forEach((wall, index) => { + if (index === 0) { + wall.attributes = { + type: 'gable', + width: 30, } } else { - console.log('else :::: ') - if (currentWall.direction === 'top') { - if (prevWall.direction === 'right') { - if (isStartPointIn) { - console.log('prevWall degree : ', 315) - expandedPoints.push({ - x: currentWall.x - offset, - y: currentWall.y - offset, - }) - } else { - console.log('prevWall degree : ', 135) - expandedPoints.push({ - x: currentWall.x + offset, - y: currentWall.y + offset, - }) - } - } - if (prevWall.direction === 'left') { - if (isStartPointIn) { - console.log('prevWall degree : ', 45) - expandedPoints.push({ - x: currentWall.x + offset, - y: currentWall.y - offset, - }) - } else { - console.log('prevWall degree : ', 225) - expandedPoints.push({ - x: currentWall.x - offset, - y: currentWall.y + offset, - }) - } - } - } - if (currentWall.direction === 'bottom') { - if (prevWall.direction === 'right') { - if (isStartPointIn) { - console.log('prevWall degree : ', 225) - expandedPoints.push({ - x: currentWall.x - offset, - y: currentWall.y + offset, - }) - } else { - console.log('prevWall degree : ', 45) - expandedPoints.push({ - x: currentWall.x + offset, - y: currentWall.y - offset, - }) - } - } - } - if (currentWall.direction === 'right') { - if (prevWall.direction === 'top') { - if (isStartPointIn) { - console.log('prevWall degree : ', 135) - expandedPoints.push({ - x: currentWall.x + offset, - y: currentWall.y + offset, - }) - } else { - console.log('prevWall degree : ', 315) - expandedPoints.push({ - x: currentWall.x - offset, - y: currentWall.y - offset, - }) - } - } - if (prevWall.direction === 'bottom') { - if (isStartPointIn) { - console.log('prevWall degree : ', 225) - expandedPoints.push({ - x: currentWall.x - offset, - y: currentWall.y + offset, - }) - } else { - console.log('prevWall degree : ', 45) - expandedPoints.push({ - x: currentWall.x + offset, - y: currentWall.y - offset, - }) - } - } - } - if (currentWall.direction === 'left') { - if (prevWall.direction === 'top') { - if (isStartPointIn) { - console.log('prevWall degree : ', 225) - expandedPoints.push({ - x: currentWall.x - offset, - y: currentWall.y + offset, - }) - } else { - console.log('prevWall degree : ', 45) - expandedPoints.push({ - x: currentWall.x + offset, - y: currentWall.y - offset, - }) - } - } - if (prevWall.direction === 'bottom') { - if (isStartPointIn) { - console.log('prevWall degree : ', 135) - expandedPoints.push({ - x: currentWall.x + offset, - y: currentWall.y + offset, - }) - } else { - console.log('prevWall degree : ', 315) - expandedPoints.push({ - x: currentWall.x - offset, - y: currentWall.y - offset, - }) - } - } + wall.attributes = { + type: 'hip', + width: 50, } } }) - console.log('expandedPoints : ', expandedPoints) + console.log('walls', walls) - /*const roof = new fabric.Polygon(expandedPoints, { - fill: 'transparent', - stroke: 'red', - strokeWidth: 1, - selectable: true, - fontSize: fontSize, - name: 'QPolygon1', + walls.forEach((currentWall, index) => { + let prevWall, nextWall + if (index === 0) { + prevWall = walls[walls.length - 1] + nextWall = walls[index + 1] + } else if (index === walls.length - 1) { + prevWall = walls[index - 1] + nextWall = walls[0] + } else { + prevWall = walls[index - 1] + nextWall = walls[index + 1] + } + + let p1 = [ + { x: currentWall.x1, y: currentWall.y1 }, + { x: currentWall.x2, y: currentWall.y2 }, + { x: prevWall.x1, y: prevWall.y1 }, + { x: prevWall.x2, y: prevWall.y2 }, + ], + p2 = [ + { x: currentWall.x1, y: currentWall.y1 }, + { x: currentWall.x2, y: currentWall.y2 }, + { x: nextWall.x1, y: nextWall.y1 }, + { x: nextWall.x2, y: nextWall.y2 }, + ] + + p1 = Array.from(new Set(p1.map((point) => JSON.stringify(point)))).map((point) => JSON.parse(point)) + p2 = Array.from(new Set(p2.map((point) => JSON.stringify(point)))).map((point) => JSON.parse(point)) + + let p1Lengths = [] + p1.forEach((point1, index) => { + let point2 + if (p1.length - 1 === index) { + point2 = p1[0] + } else { + point2 = p1[index + 1] + } + const dx = Math.abs(point2.x - point1.x) + const dy = Math.abs(point2.y - point1.y) + p1Lengths.push({ + length: Math.round(Math.sqrt(dx ** 2 + dy ** 2)), + coords: [ + { x: point1.x, y: point1.y }, + { x: point2.x, y: point2.y }, + ], + }) + }) + + let p2Lengths = [] + p2.forEach((point1, index) => { + let point2 + if (p2.length - 1 === index) { + point2 = p2[0] + } else { + point2 = p2[index + 1] + } + const dx = Math.abs(point2.x - point1.x) + const dy = Math.abs(point2.y - point1.y) + p2Lengths.push({ + length: Math.round(Math.sqrt(dx ** 2 + dy ** 2)), + coords: [ + { x: point1.x, y: point1.y }, + { x: point2.x, y: point2.y }, + ], + }) + }) + + let x1 = 0, + y1 = 0, + x2 = 0, + y2 = 0, + offsetX1 = 0, + offsetY1 = 0, + offsetX2 = 0, + offsetY2 = 0 + + let alfa = Math.round(Math.sqrt(Math.abs(currentWall.x1 - currentWall.x2) ** 2 + Math.abs(currentWall.y1 - currentWall.y2) ** 2)), + bravo = Math.round(Math.sqrt(Math.abs(prevWall.x1 - prevWall.x2) ** 2 + Math.abs(prevWall.y1 - prevWall.y2) ** 2)), + charlie = p1Lengths.filter((length) => length.length !== alfa && length.length !== bravo)[0], + alpha = Math.round(Math.acos((alfa ** 2 + charlie.length ** 2 - bravo ** 2) / (2 * alfa * charlie.length)) * (180 / Math.PI) * 100) / 100, + beta = Math.round(Math.acos((bravo ** 2 + charlie.length ** 2 - alfa ** 2) / (2 * bravo * charlie.length)) * (180 / Math.PI) * 100) / 100, + gamma = Math.round(Math.acos((alfa ** 2 + bravo ** 2 - charlie.length ** 2) / (2 * alfa * bravo)) * (180 / Math.PI) * 100) / 100, + isValley = checkValley(wall, currentWall, prevWall) + console.log('현재 라인 : ', alfa) + gamma = isValley ? Math.round(((360 - gamma) / 2) * 100) / 100 : Math.round((gamma / 2) * 100) / 100 + + console.log('이전 라인과의 각도', gamma) + + if (currentWall.x1 === currentWall.x2) { + offsetY1 = currentWall.attributes.width + offsetX1 = prevWall.attributes.width + x1 = Math.min(currentWall.x1, currentWall.x2) + offsetX1 + y1 = currentWall.y1 + offsetY1 + } else if (currentWall.y1 === currentWall.y2) { + offsetX1 = currentWall.attributes.width + offsetY1 = prevWall.attributes.width + x1 = currentWall.x1 + offsetX1 + y1 = Math.min(currentWall.y1, currentWall.y2) + offsetY1 + } else { + } + + bravo = Math.round(Math.sqrt(Math.abs(nextWall.x1 - nextWall.x2) ** 2 + Math.abs(nextWall.y1 - nextWall.y2) ** 2)) + charlie = p2Lengths.filter((length) => length.length !== alfa && length.length !== bravo)[0] + + // alpha = Math.round(Math.acos((alfa ** 2 + charlie ** 2 - bravo ** 2) / (2 * alfa * charlie)) * (180 / Math.PI)) + // betta = Math.round(Math.acos((bravo ** 2 + charlie ** 2 - alfa ** 2) / (2 * bravo * charlie)) * (180 / Math.PI)) + gamma = Math.round(Math.acos((alfa ** 2 + bravo ** 2 - charlie.length ** 2) / (2 * alfa * bravo)) * (180 / Math.PI) * 100) / 100 + isValley = checkValley(wall, currentWall, nextWall) + gamma = isValley ? Math.round(((360 - gamma) / 2) * 100) / 100 : Math.round((gamma / 2) * 100) / 100 + + console.log('다음 라인과의 각도', gamma) + + /*let offsetX = 0, + offsetY = 0 + let x1 = 0, + y1 = 0, + x2 = 0, + y2 = 0 + if (prevWall.direction !== nextWall.direction) { + if (currentWall.x1 === currentWall.x2) { + offsetX = prevWall.x1 < currentWall.x1 ? currentWall.attributes.width : -currentWall.attributes.width + x1 = currentWall.x1 + x2 = currentWall.x2 + y1 = Math.min(currentWall.y1, currentWall.y2) - Math.min(prevWall.attributes.width, nextWall.attributes.width) + y2 = Math.max(currentWall.y1, currentWall.y2) + Math.max(prevWall.attributes.width, nextWall.attributes.width) + } else { + offsetY = prevWall.y1 < currentWall.y1 ? currentWall.attributes.width : -currentWall.attributes.width + x1 = Math.min(currentWall.x1, currentWall.x2) - Math.min(prevWall.attributes.width, nextWall.attributes.width) + x2 = Math.max(currentWall.x1, currentWall.x2) + Math.max(prevWall.attributes.width, nextWall.attributes.width) + y1 = currentWall.y1 + y2 = currentWall.y2 + } + } else { + } + + x1 = x1 + offsetX + x2 = x2 + offsetX + y1 = y1 + offsetY + y2 = y2 + offsetY + + const roof = new QLine([x1, y1, x2, y2], { + fontSize: fontSize, + stroke: 'black', + strokeWidth: 1, + name: 'roofLine', + }) + canvas?.add(roof) + canvas?.renderAll()*/ }) + } - roof.wall = wall - canvas?.add(roof)*/ + /** + * 라인 사이가 지붕골 인지 확인. + * @param polygon + * @param line1 + * @param line2 + * @returns {boolean} + */ + const checkValley = (polygon, line1, line2) => { + let points = [ + { x: line1.x1, y: line1.y1 }, + { x: line1.x2, y: line1.y2 }, + { x: line2.x1, y: line2.y1 }, + { x: line2.x2, y: line2.y2 }, + ] + points = Array.from(new Set(points.map((point) => JSON.stringify(point)))).map((point) => JSON.parse(point)) + const centroidX = points.reduce((acc, point) => acc + point.x, 0) / points.length + const centroidY = points.reduce((acc, point) => acc + point.y, 0) / points.length - // roof.drawHelpLine() + let isValley = true + const pPoints = polygon.points + pPoints.forEach((point, index) => { + if (index < pPoints.length - 1) { + let j = index + 1 + let xi = pPoints[index].x + polygon.left, + yi = pPoints[index].y + polygon.top + let xj = pPoints[j].x + polygon.left, + yj = pPoints[j].y + polygon.top + + let intersect = yi > centroidY !== yj > centroidY && centroidX < ((xj - xi) * (centroidY - yi)) / (yj - yi) + xi + if (intersect) isValley = !isValley + } + }) + return isValley } /** From d6d295867efee445c653048e6a9d9f4a2ba80b16 Mon Sep 17 00:00:00 2001 From: Jaeyoung Lee Date: Fri, 20 Sep 2024 09:44:36 +0900 Subject: [PATCH 02/25] =?UTF-8?q?=EC=A7=80=EB=B6=95=20=EB=9D=BC=EC=9D=B8?= =?UTF-8?q?=EB=B3=84=20offset=20=EC=A0=81=EC=9A=A9=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Roof2.jsx | 4 +- src/hooks/useMode.js | 414 ++++++++++++++++++------------------- src/util/qpolygon-utils.js | 4 - 3 files changed, 198 insertions(+), 224 deletions(-) diff --git a/src/components/Roof2.jsx b/src/components/Roof2.jsx index e9da0e7a..f57f62ae 100644 --- a/src/components/Roof2.jsx +++ b/src/components/Roof2.jsx @@ -140,10 +140,10 @@ export default function Roof2(props) { useEffect(() => { get({ url: `/api/canvas-management/canvas-statuses/by-object/test123240822001` }).then((res) => { - console.log(res) + // console.log(res) const arrangeData = res.map((item) => { - console.log(item.canvasStatus.replace(/##/g, '"').replace(/\\/g, '')) + // console.log(item.canvasStatus.replace(/##/g, '"').replace(/\\/g, '')) const test = item.canvasStatus.replace(/##/g, '"').replace(/\\/g, '') const test2 = test.substring(1, test.length - 1) return { diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index bc650216..0ea0c065 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -1472,11 +1472,203 @@ export function useMode() { *벽 지붕 외곽선 생성 polygon을 입력받아 만들기 */ const handleOuterlinesTest2 = (polygon, offset = 50) => { - drawRoofPolygon(polygon, offset) - /*const offsetPoints = offsetPolygon(polygon.points, offset) + const roof = drawRoofPolygon(polygon) + roof.drawHelpLine() + roof.divideLine() + } + + function inwardEdgeNormal(vertex1, vertex2) { + // Assuming that polygon vertices are in clockwise order + const dx = vertex2.x - vertex1.x + const dy = vertex2.y - vertex1.y + const edgeLength = Math.sqrt(dx * dx + dy * dy) + + return { + x: -dy / edgeLength, + y: dx / edgeLength, + } + } + + function outwardEdgeNormal(vertex1, vertex2) { + const n = inwardEdgeNormal(vertex1, vertex2) + + return { + x: -n.x, + y: -n.y, + } + } + + function createRoofPolygon(vertices) { + const edges = [] + let minX = vertices.length > 0 ? vertices[0].x : undefined + let minY = vertices.length > 0 ? vertices[0].y : undefined + let maxX = minX + let maxY = minY + + for (let i = 0; i < vertices.length; i++) { + const vertex1 = vertices[i] + const vertex2 = vertices[(i + 1) % vertices.length] + + const outwardNormal = outwardEdgeNormal(vertex1, vertex2) + + const inwardNormal = inwardEdgeNormal(vertex1, vertex2) + + const edge = { + vertex1, + vertex2, + index: i, + outwardNormal, + inwardNormal, + } + + edges.push(edge) + + const x = vertices[i].x + const y = vertices[i].y + minX = Math.min(x, minX) + minY = Math.min(y, minY) + maxX = Math.max(x, maxX) + maxY = Math.max(y, maxY) + } + + return { + vertices, + edges, + minX, + minY, + maxX, + maxY, + } + } + + function createOffsetEdge(edge, dx, dy) { + return { + vertex1: { + x: edge.vertex1.x + dx, + y: edge.vertex1.y + dy, + }, + vertex2: { + x: edge.vertex2.x + dx, + y: edge.vertex2.y + dy, + }, + } + } + + function edgesIntersection(edgeA, edgeB) { + const den = + (edgeB.vertex2.y - edgeB.vertex1.y) * (edgeA.vertex2.x - edgeA.vertex1.x) - + (edgeB.vertex2.x - edgeB.vertex1.x) * (edgeA.vertex2.y - edgeA.vertex1.y) + + if (den === 0) { + return null // lines are parallel or coincident + } + + const ua = + ((edgeB.vertex2.x - edgeB.vertex1.x) * (edgeA.vertex1.y - edgeB.vertex1.y) - + (edgeB.vertex2.y - edgeB.vertex1.y) * (edgeA.vertex1.x - edgeB.vertex1.x)) / + den + + const ub = + ((edgeA.vertex2.x - edgeA.vertex1.x) * (edgeA.vertex1.y - edgeB.vertex1.y) - + (edgeA.vertex2.y - edgeA.vertex1.y) * (edgeA.vertex1.x - edgeB.vertex1.x)) / + den + + // Edges are not intersecting but the lines defined by them are + const isIntersectionOutside = ua < 0 || ub < 0 || ua > 1 || ub > 1 + + return { + x: edgeA.vertex1.x + ua * (edgeA.vertex2.x - edgeA.vertex1.x), + y: edgeA.vertex1.y + ua * (edgeA.vertex2.y - edgeA.vertex1.y), + isIntersectionOutside, + } + } + + function createMarginPolygon(polygon, lines, arcSegments = 0) { + const offsetEdges = [] + + polygon.edges.forEach((edge, i) => { + const offset = lines[i % lines.length].attributes.offset + const dx = edge.outwardNormal.x * offset + const dy = edge.outwardNormal.y * offset + offsetEdges.push(createOffsetEdge(edge, dx, dy)) + }) + + const vertices = [] + + offsetEdges.forEach((thisEdge, i) => { + const prevEdge = offsetEdges[(i + offsetEdges.length - 1) % offsetEdges.length] + const vertex = edgesIntersection(prevEdge, thisEdge) + if (vertex && (!vertex.isIntersectionOutside || arcSegments < 1)) { + vertices.push({ + x: vertex.x, + y: vertex.y, + }) + } + }) + + const marginPolygon = createRoofPolygon(vertices) + marginPolygon.offsetEdges = offsetEdges + return marginPolygon + } + + function createPaddingPolygon(polygon, lines, arcSegments = 0) { + const offsetEdges = [] + + polygon.edges.forEach((edge, i) => { + const offset = lines[i % lines.length].attributes.offset + const dx = edge.inwardNormal.x * offset + const dy = edge.inwardNormal.y * offset + offsetEdges.push(createOffsetEdge(edge, dx, dy)) + }) + + const vertices = [] + + offsetEdges.forEach((thisEdge, i) => { + const prevEdge = offsetEdges[(i + offsetEdges.length - 1) % offsetEdges.length] + const vertex = edgesIntersection(prevEdge, thisEdge) + if (vertex && (!vertex.isIntersectionOutside || arcSegments < 1)) { + vertices.push({ + x: vertex.x, + y: vertex.y, + }) + } + }) + + const paddingPolygon = createRoofPolygon(vertices) + paddingPolygon.offsetEdges = offsetEdges + return paddingPolygon + } + + const drawRoofPolygon = (wall) => { + let walls = wall.lines + walls.forEach((wall, index) => { + if (index === walls.length - 1 || index === 3) { + wall.attributes = { + type: 'gable', + offset: 30, + } + } else { + wall.attributes = { + type: 'hip', + offset: 50, + } + } + }) + const polygon = createRoofPolygon(wall.points) + const originPolygon = new QPolygon(wall.points, { fontSize: 0 }) + let offsetPolygon + + let result = createMarginPolygon(polygon, wall.lines).vertices + const allPointsOutside = result.every((point) => !originPolygon.inPolygon(point)) + + if (allPointsOutside) { + offsetPolygon = createMarginPolygon(polygon, wall.lines).vertices + } else { + offsetPolygon = createPaddingPolygon(polygon, wall.lines).vertices + } const roof = makePolygon( - offsetPoints.map((point) => { + offsetPolygon.map((point) => { return { x1: point.x, y1: point.y } }), ) @@ -1485,177 +1677,7 @@ export function useMode() { setRoof(roof) setWall(polygon) - roof.drawHelpLine() - roof.divideLine()*/ - } - - const drawRoofPolygon = (wall, offset = 50) => { - let walls = wall.lines - walls.forEach((wall, index) => { - if (index === 0) { - wall.attributes = { - type: 'gable', - width: 30, - } - } else { - wall.attributes = { - type: 'hip', - width: 50, - } - } - }) - - console.log('walls', walls) - - walls.forEach((currentWall, index) => { - let prevWall, nextWall - if (index === 0) { - prevWall = walls[walls.length - 1] - nextWall = walls[index + 1] - } else if (index === walls.length - 1) { - prevWall = walls[index - 1] - nextWall = walls[0] - } else { - prevWall = walls[index - 1] - nextWall = walls[index + 1] - } - - let p1 = [ - { x: currentWall.x1, y: currentWall.y1 }, - { x: currentWall.x2, y: currentWall.y2 }, - { x: prevWall.x1, y: prevWall.y1 }, - { x: prevWall.x2, y: prevWall.y2 }, - ], - p2 = [ - { x: currentWall.x1, y: currentWall.y1 }, - { x: currentWall.x2, y: currentWall.y2 }, - { x: nextWall.x1, y: nextWall.y1 }, - { x: nextWall.x2, y: nextWall.y2 }, - ] - - p1 = Array.from(new Set(p1.map((point) => JSON.stringify(point)))).map((point) => JSON.parse(point)) - p2 = Array.from(new Set(p2.map((point) => JSON.stringify(point)))).map((point) => JSON.parse(point)) - - let p1Lengths = [] - p1.forEach((point1, index) => { - let point2 - if (p1.length - 1 === index) { - point2 = p1[0] - } else { - point2 = p1[index + 1] - } - const dx = Math.abs(point2.x - point1.x) - const dy = Math.abs(point2.y - point1.y) - p1Lengths.push({ - length: Math.round(Math.sqrt(dx ** 2 + dy ** 2)), - coords: [ - { x: point1.x, y: point1.y }, - { x: point2.x, y: point2.y }, - ], - }) - }) - - let p2Lengths = [] - p2.forEach((point1, index) => { - let point2 - if (p2.length - 1 === index) { - point2 = p2[0] - } else { - point2 = p2[index + 1] - } - const dx = Math.abs(point2.x - point1.x) - const dy = Math.abs(point2.y - point1.y) - p2Lengths.push({ - length: Math.round(Math.sqrt(dx ** 2 + dy ** 2)), - coords: [ - { x: point1.x, y: point1.y }, - { x: point2.x, y: point2.y }, - ], - }) - }) - - let x1 = 0, - y1 = 0, - x2 = 0, - y2 = 0, - offsetX1 = 0, - offsetY1 = 0, - offsetX2 = 0, - offsetY2 = 0 - - let alfa = Math.round(Math.sqrt(Math.abs(currentWall.x1 - currentWall.x2) ** 2 + Math.abs(currentWall.y1 - currentWall.y2) ** 2)), - bravo = Math.round(Math.sqrt(Math.abs(prevWall.x1 - prevWall.x2) ** 2 + Math.abs(prevWall.y1 - prevWall.y2) ** 2)), - charlie = p1Lengths.filter((length) => length.length !== alfa && length.length !== bravo)[0], - alpha = Math.round(Math.acos((alfa ** 2 + charlie.length ** 2 - bravo ** 2) / (2 * alfa * charlie.length)) * (180 / Math.PI) * 100) / 100, - beta = Math.round(Math.acos((bravo ** 2 + charlie.length ** 2 - alfa ** 2) / (2 * bravo * charlie.length)) * (180 / Math.PI) * 100) / 100, - gamma = Math.round(Math.acos((alfa ** 2 + bravo ** 2 - charlie.length ** 2) / (2 * alfa * bravo)) * (180 / Math.PI) * 100) / 100, - isValley = checkValley(wall, currentWall, prevWall) - console.log('현재 라인 : ', alfa) - gamma = isValley ? Math.round(((360 - gamma) / 2) * 100) / 100 : Math.round((gamma / 2) * 100) / 100 - - console.log('이전 라인과의 각도', gamma) - - if (currentWall.x1 === currentWall.x2) { - offsetY1 = currentWall.attributes.width - offsetX1 = prevWall.attributes.width - x1 = Math.min(currentWall.x1, currentWall.x2) + offsetX1 - y1 = currentWall.y1 + offsetY1 - } else if (currentWall.y1 === currentWall.y2) { - offsetX1 = currentWall.attributes.width - offsetY1 = prevWall.attributes.width - x1 = currentWall.x1 + offsetX1 - y1 = Math.min(currentWall.y1, currentWall.y2) + offsetY1 - } else { - } - - bravo = Math.round(Math.sqrt(Math.abs(nextWall.x1 - nextWall.x2) ** 2 + Math.abs(nextWall.y1 - nextWall.y2) ** 2)) - charlie = p2Lengths.filter((length) => length.length !== alfa && length.length !== bravo)[0] - - // alpha = Math.round(Math.acos((alfa ** 2 + charlie ** 2 - bravo ** 2) / (2 * alfa * charlie)) * (180 / Math.PI)) - // betta = Math.round(Math.acos((bravo ** 2 + charlie ** 2 - alfa ** 2) / (2 * bravo * charlie)) * (180 / Math.PI)) - gamma = Math.round(Math.acos((alfa ** 2 + bravo ** 2 - charlie.length ** 2) / (2 * alfa * bravo)) * (180 / Math.PI) * 100) / 100 - isValley = checkValley(wall, currentWall, nextWall) - gamma = isValley ? Math.round(((360 - gamma) / 2) * 100) / 100 : Math.round((gamma / 2) * 100) / 100 - - console.log('다음 라인과의 각도', gamma) - - /*let offsetX = 0, - offsetY = 0 - let x1 = 0, - y1 = 0, - x2 = 0, - y2 = 0 - if (prevWall.direction !== nextWall.direction) { - if (currentWall.x1 === currentWall.x2) { - offsetX = prevWall.x1 < currentWall.x1 ? currentWall.attributes.width : -currentWall.attributes.width - x1 = currentWall.x1 - x2 = currentWall.x2 - y1 = Math.min(currentWall.y1, currentWall.y2) - Math.min(prevWall.attributes.width, nextWall.attributes.width) - y2 = Math.max(currentWall.y1, currentWall.y2) + Math.max(prevWall.attributes.width, nextWall.attributes.width) - } else { - offsetY = prevWall.y1 < currentWall.y1 ? currentWall.attributes.width : -currentWall.attributes.width - x1 = Math.min(currentWall.x1, currentWall.x2) - Math.min(prevWall.attributes.width, nextWall.attributes.width) - x2 = Math.max(currentWall.x1, currentWall.x2) + Math.max(prevWall.attributes.width, nextWall.attributes.width) - y1 = currentWall.y1 - y2 = currentWall.y2 - } - } else { - } - - x1 = x1 + offsetX - x2 = x2 + offsetX - y1 = y1 + offsetY - y2 = y2 + offsetY - - const roof = new QLine([x1, y1, x2, y2], { - fontSize: fontSize, - stroke: 'black', - strokeWidth: 1, - name: 'roofLine', - }) - canvas?.add(roof) - canvas?.renderAll()*/ - }) + return roof } /** @@ -1693,50 +1715,6 @@ export function useMode() { return isValley } - /** - * 구하려는 라인의 x1,y1좌표가 기준 - * @param line1 이전 라인 - * @param line2 현재 라인 - * @param line3 다음 라인 - * @param offset - * @returns {number} - */ - const getLineOffsetPoint = (line1, line2, line3, offset) => { - //밑변 - let a = Math.abs(line1.x - line2.x) - //빗변 - let c = Math.sqrt(Math.abs(line1.x - line2.x) ** 2 + Math.abs(line1.y - line2.y) ** 2) - - console.log(a, c) - //밑변과 빗변사이의 각도 - let alphaDegree = getDegreeBetweenTwoLines(line1, line2, line3) - alphaDegree = alphaDegree <= 90 ? alphaDegree : 180 - alphaDegree - alphaDegree = 90 - alphaDegree - console.log('alphaDegree : ', alphaDegree) - - // console.log('Math.tan(alphaDegree * (Math.PI / 180)) : ', Math.tan(alphaDegree * (Math.PI / 180))) - console.log(Math.round(offset * Math.tan(alphaDegree * (Math.PI / 180)))) - - const angle = getDegreeBetweenTwoLines(line1, line2, line3) - const side1 = line1.length - const side2 = line2.length - const side3 = Math.sqrt(side1 ** 2 + side2 ** 2 - 2 * side1 * side2 * Math.cos(angle * (Math.PI / 180))) - const beta = Math.round(Math.asin((side2 * Math.sin(angle * (Math.PI / 180))) / side3) * (180 / Math.PI) * 10) / 10 - const alpha = 180 - angle - beta - - console.log('angle : ', angle, 'alpha : ', alpha, 'beta : ', beta) - - const h = side2 * Math.sin(alpha * (Math.PI / 180)) - const h_new = h + offset - console.log('빗변까지 길이 : ', h, 'offset 적용된 빗변까지 길이 : ', h_new) - const newAdjacent = h_new / Math.sin(angle * (Math.PI / 180)) - console.log('offset 적용된 밑변 : ', newAdjacent) - - // offset = Math.sqrt(diffAdjacent ** 2 + diffAdjacent ** 2) / 2 - console.log('offset : ', offset) - return offset - } - /** * 구하려는 라인의 x1,y1좌표가 기준 * @param line1 이전 라인 diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index 1599905d..032ad5be 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -1206,10 +1206,6 @@ export const drawHippedRoof = (polygon, chon) => { } const drawRoofRidge = (polygon, chon) => { - const points = [] - polygon.wall.points.forEach((point) => points.push({ x: point.x, y: point.y })) - console.log('points : ', points) - const walls = polygon.wall.lines // 외벽의 라인 const roofs = polygon.lines // 지붕의 라인 let ridgeWall = [] From 112ebea1c1a71b5281aec8492c6ded3f0c22ff6b Mon Sep 17 00:00:00 2001 From: Jaeyoung Lee Date: Fri, 27 Sep 2024 16:23:04 +0900 Subject: [PATCH 03/25] =?UTF-8?q?=EB=B3=80=EB=B3=84=20=EC=86=8D=EC=84=B1?= =?UTF-8?q?=20=EC=9E=91=EC=97=85=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Roof2.jsx | 11 +- src/hooks/useMode.js | 24 ++- src/util/qpolygon-utils.js | 406 ++++++++++++++++++------------------- 3 files changed, 222 insertions(+), 219 deletions(-) diff --git a/src/components/Roof2.jsx b/src/components/Roof2.jsx index 8e326c7f..3cfff65c 100644 --- a/src/components/Roof2.jsx +++ b/src/components/Roof2.jsx @@ -39,7 +39,7 @@ import QEmptyContextMenu from '@/components/common/context-menu/QEmptyContextMen import InitSettingsModal from './InitSettingsModal' import GridSettingsModal from './GridSettingsModal' import { SurfaceShapeModal } from '@/components/ui/SurfaceShape' -import { changeAllGableRoof, drawDirectionStringToArrow } from '@/util/qpolygon-utils' +import { changeAllHipAndGableRoof, drawDirectionStringToArrow } from '@/util/qpolygon-utils' import ThumbnailList from '@/components/ui/ThumbnailLIst' import ObjectPlacement from '@/components/ui/ObjectPlacement' import { globalLocaleStore } from '@/store/localeAtom' @@ -425,7 +425,7 @@ export default function Roof2(props) { { x: 450, y: 850 }, ] - const polygon = new QPolygon(type1C, { + const polygon = new QPolygon(twelvePoint, { fill: 'transparent', stroke: 'green', strokeWidth: 1, @@ -640,13 +640,14 @@ export default function Roof2(props) { drawDirectionStringToArrow(canvas, globalCampass) } - const setAllGableRoof = () => { + // 팔작지붕, 합각지붕 + const setAllHipAndGableRoof = () => { let offset = Number(prompt('gable roof offset', '50')) if (!isNaN(offset) && offset > 0) { const polygon = canvas?.getObjects() console.log('gable roof offset : ', offset) console.log('polygon : ', polygon) - changeAllGableRoof(polygon, offset, canvas) + changeAllHipAndGableRoof(polygon, offset, canvas) } else { alert('offset 은 0 보다 커야 함') } @@ -777,7 +778,7 @@ export default function Roof2(props) { )} - */} - {/* - */} + {/* 퍼블시작 */} +
+
+
+

물건목록

+
    +
  • + 전체 + {gridCount} +
  • +
  • + 선택 + {selectedRowDataCount} +
  • +
+
+
+
+ +
+
+ +
+
-
- +
+
+ +
페이징 컴포넌트예정
+
+ {/* 퍼블종료 */} ) } diff --git a/src/components/management/StuffDetail.jsx b/src/components/management/StuffDetail.jsx index 66baf8e3..898b4176 100644 --- a/src/components/management/StuffDetail.jsx +++ b/src/components/management/StuffDetail.jsx @@ -4,16 +4,20 @@ import React, { useState, useEffect } from 'react' import { useRouter, useSearchParams } from 'next/navigation' import { Input, RadioGroup, Radio, Button, Autocomplete, AutocompleteItem, Select, SelectItem, Checkbox, Textarea, button } from '@nextui-org/react' import Link from 'next/link' -import { del, get, post } from '@/lib/Axios' +import { useAxios } from '@/hooks/useAxios' +import { globalLocaleStore } from '@/store/localeAtom' import { queryStringFormatter, isEmptyArray } from '@/util/common-utils' import dayjs from 'dayjs' import { useMessage } from '@/hooks/useMessage' import { useForm } from 'react-hook-form' +import { useRecoilState, useRecoilValue } from 'recoil' export default function StuffDetail() { const router = useRouter() const searchParams = useSearchParams() const { getMessage } = useMessage() + const globalLocaleState = useRecoilValue(globalLocaleStore) + const { get, post, del } = useAxios(globalLocaleState) //form const formInitValue = { // 물건번호 T...(임시) R...(진짜) @@ -63,7 +67,7 @@ export default function StuffDetail() { useEffect(() => { if (objectNo) { - console.log('수정화면') + //console.log('수정화면') setEditMode('EDIT') if (objectNo.substring(0, 1) === 'R') { @@ -90,7 +94,7 @@ export default function StuffDetail() { //1차점 : X167 get({ url: `/api/object/saleStore/X167/list` }).then((res) => { if (!isEmptyArray(res)) { - console.log('판매점 결과:::::', res) + // console.log('판매점 결과:::::', res) setSaleStoreList(res) //1차 판매점 자동완성 값 셋팅 form.setValue('saleStoreId', res[0].saleStoreId) @@ -99,9 +103,6 @@ export default function StuffDetail() { setOtherSaleStoreList([]) } }) - } else { - alert('삭제된 물건입니다') - router.push('/management/stuff') } }) } else { @@ -385,6 +386,312 @@ export default function StuffDetail() { return ( <> {(editMode === 'NEW' && ( +
+
+
+ * 필수 입력항목 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ 담당자 * + +
+ +
+
+ 물건구분/물건명 * + +
+
+ + +
+
+ + +
+
+ +
+
+ +
+
+
물건명 후리가나 +
+ +
+
+
+
+ 1차 판매점명 / ID + * +
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+ 2차 판매점명 / ID * +
+
+
+
+
+
+ +
+
+
+ +
+
+ 우편번호 * + +
+
+ +
+ +
*우편번호 7자리를 입력한 후, 주소검색 버튼을 클릭해 주십시오
+
+
+ 도도부현 / 주소 * + +
+
+ {/* {prefCodeList?.length > 0 && ( + + )} */} +
+
+ +
+
+
+ 발전량시뮬레이션지역 * + +
+
+ 기준풍속 * + +
+
+ +
+ m/s이하 +
+
+ 수직적설량 * + +
+
+ cm +
+ + +
+
+
+ 면조도구분 * + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ 설치높이 * + +
+
+ +
+ m +
+
계약조건 +
+
+ + +
+
+ + +
+
+
메모 +
+ +
+
+
+
+ {!isFormValid ? ( + + ) : ( + + )} + + + +
+
+
+ )) || ( + <> + {objectNo.substring(0, 1) === 'R' ? ( + <> + + + + + + + ) : ( + <> + + + + + + )} + + )} + {/* {(editMode === 'NEW' && (
(*필수 입력항목)
@@ -610,7 +917,7 @@ export default function StuffDetail() { )} - )} + )} */} ) } diff --git a/src/components/management/StuffHeader.jsx b/src/components/management/StuffHeader.jsx new file mode 100644 index 00000000..fb4d831c --- /dev/null +++ b/src/components/management/StuffHeader.jsx @@ -0,0 +1,51 @@ +'use client' + +import React, { useState, useEffect } from 'react' +import { useAxios } from '@/hooks/useAxios' +import { useRouter, useSearchParams } from 'next/navigation' +import { globalLocaleStore } from '@/store/localeAtom' +import { useRecoilValue } from 'recoil' +export default function StuffHeader() { + const router = useRouter() + const searchParams = useSearchParams() + const objectNo = searchParams.get('objectNo') //url에서 물건번호 꺼내서 바로 set + const globalLocaleState = useRecoilValue(globalLocaleStore) + const { get } = useAxios(globalLocaleState) + const [headerData, setHeaderData] = useState({}) + + useEffect(() => { + get({ url: `/api/object/${objectNo}/detail` }).then((res) => { + //console.log('res::', res) + if (res != null && res != '') { + console.log('헤더상세::::::::::', res) + setHeaderData(res) + } else { + alert('삭제된 물건입니다') + router.push('/management/stuff') + } + }) + }, [objectNo]) + + return ( +
+
+
물건번호
+
+ {headerData.objectNo} +
+
+
+
사양확정일
+
{headerData.specificationConfirmDate}
+
+
+
갱신일시
+
{headerData.lastEditDatetime}
+
+
+
등록일
+
{headerData.createDatetime}
+
+
+ ) +} diff --git a/src/components/management/StuffSearchCondition.jsx b/src/components/management/StuffSearchCondition.jsx index 58aa4ce9..420b2145 100644 --- a/src/components/management/StuffSearchCondition.jsx +++ b/src/components/management/StuffSearchCondition.jsx @@ -2,25 +2,36 @@ import React, { useEffect } from 'react' import { useState } from 'react' -import { Input, RadioGroup, Radio, Button, Autocomplete, AutocompleteItem } from '@nextui-org/react' +import { Autocomplete, AutocompleteItem } from '@nextui-org/react' +import { useAxios } from '@/hooks/useAxios' +import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil' +import { globalLocaleStore } from '@/store/localeAtom' + import RangeDatePicker from '@/components/common/datepicker/RangeDatePicker' -import { useRecoilState, useResetRecoilState } from 'recoil' import { stuffSearchState } from '@/store/stuffAtom' import { isEmptyArray } from '@/util/common-utils' -import { get } from '@/lib/Axios' import dayjs from 'dayjs' import isLeapYear from 'dayjs/plugin/isLeapYear' // 윤년 판단 플러그인 dayjs.extend(isLeapYear) import Link from 'next/link' +import SingleDatePicker from '../common/datepicker/SingleDatePicker' export default function StuffSearchCondition() { - //달력 props 관련 날짜 셋팅 - const [dateRange, setDateRange] = useState([dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'), dayjs(new Date()).format('YYYY-MM-DD')]) - const [startRangeDate, endRangeDate] = dateRange + const globalLocaleState = useRecoilValue(globalLocaleStore) - const rangeDatePickerProps = { - startRangeDate, //시작일 - endRangeDate, //종료일 - setDateRange, + const { get } = useAxios(globalLocaleState) + + //달력 props 관련 날짜 셋팅 + const [startDate, setStartDate] = useState(dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD')) + const [endDate, setEndDate] = useState(dayjs(new Date()).format('YYYY-MM-DD')) + + const rangeDatePickerProps1 = { + startDate, //시작일 + setStartDate, + } + + const rangeDatePickerProps2 = { + startDate: endDate, //종료일 + setStartDate: setEndDate, } //여기서 선택한 검색조건들을 recoil로 관리 @@ -40,7 +51,7 @@ export default function StuffSearchCondition() { const [schSelSaleStoreList, setSchSelSaleStoreList] = useState([]) //판매대리점 자동완성 SELECT // 조회 const onSubmit = () => { - let diff = dayjs(endRangeDate).diff(startRangeDate, 'day') + let diff = dayjs(endDate).diff(startDate, 'day') if (diff > 366) { return alert('최대1년 조회 가능합니다.') } @@ -55,8 +66,8 @@ export default function StuffSearchCondition() { schReceiveUser: stuffSearch?.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser, schDispCompanyName: stuffSearch?.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName, schDateType: stuffSearch?.schDateType ? stuffSearch.schDateType : dateType, - schFromDt: dayjs(startRangeDate).format('YYYY-MM-DD'), - schToDt: dayjs(endRangeDate).format('YYYY-MM-DD'), + schFromDt: dayjs(startDate).format('YYYY-MM-DD'), + schToDt: dayjs(endDate).format('YYYY-MM-DD'), code: 'E', schSelSaleStoreId: stuffSearch?.schSelSaleStoreId ? stuffSearch.schSelSaleStoreId : schSelSaleStoreId, startRow: stuffSearch?.startRow ? stuffSearch.startRow : 1, @@ -76,7 +87,8 @@ export default function StuffSearchCondition() { setReceiveUser('') setDispCompanyName('') setDateType('U') - setDateRange([dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'), dayjs(new Date()).format('YYYY-MM-DD')]) + setStartDate(dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD')) + setEndDate(dayjs(new Date()).format('YYYY-MM-DD')) setSchSelSaleStoreId('') resetStuffRecoil() } @@ -102,23 +114,207 @@ export default function StuffSearchCondition() { } } - //x로 날짜 비웠을때 기본값으로 셋팅 useEffect(() => { - if (!startRangeDate && !endRangeDate) { - setDateRange([dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'), dayjs(new Date()).format('YYYY-MM-DD')]) - } - }, [startRangeDate, endRangeDate]) - - useEffect(() => { - setDateRange([ - stuffSearch?.schFromDt ? stuffSearch.schFromDt : dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'), - stuffSearch?.schToDt ? stuffSearch.schToDt : dayjs(new Date()).format('YYYY-MM-DD'), - ]) + setStartDate(stuffSearch?.schFromDt ? stuffSearch.schFromDt : dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD')) + setEndDate(stuffSearch?.schToDt ? stuffSearch.schToDt : dayjs(new Date()).format('YYYY-MM-DD')) }, [stuffSearch]) return ( <> -
+ {/* 퍼블적용시작 */} +
+
+
+

물건현황

+
+
+ + + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
물건번호 +
+ { + setObjectNo(e.target.value) + setStuffSearch({ ...stuffSearch, code: 'S', schObjectNo: e.target.value }) + }} + /> +
+
판매대리점명 +
+ { + setSaleStoreId(e.target.value) + setStuffSearch({ ...stuffSearch, code: 'S', schSaleStoreId: e.target.value }) + }} + /> +
+
물건주소 +
+ { + setAddress(e.target.value) + setStuffSearch({ ...stuffSearch, code: 'S', schAddress: e.target.value }) + }} + /> +
+
물건명 +
+ { + setobjectName(e.target.value) + setStuffSearch({ ...stuffSearch, code: 'S', schObjectName: e.target.value }) + }} + /> +
+
견적처 +
+ { + setDispCompanyName(e.target.value) + setStuffSearch({ ...stuffSearch, code: 'S', schDispCompanyName: e.target.value }) + }} + /> +
+
판매대리점 선택 + {/*
+ +
*/} + {schSelSaleStoreList?.length > 0 && ( + + {(option) => {option.saleStoreName}} + + )} +
담당자 +
+ { + setReceiveUser(e.target.value) + setStuffSearch({ ...stuffSearch, code: 'S', schReceiveUser: e.target.value }) + }} + /> +
+
기간검색 +
+
+
+ { + setDateType(e.target.value) + setStuffSearch({ ...stuffSearch, code: 'S', schDateType: e.target.value }) + }} + /> + +
+
+ { + setDateType(e.target.value) + setStuffSearch({ ...stuffSearch, code: 'S', schDateType: e.target.value }) + }} + /> + +
+
+
+
+ +
+ ~ +
+ +
+
+
+
+
+
+ {/* 퍼블적용끝 */} + {/*
@@ -300,7 +496,7 @@ export default function StuffSearchCondition() { ) } })} -
+
*/} ) } From 8f6d30e7b52bd1f6347ae9565c385bd6b23f175e Mon Sep 17 00:00:00 2001 From: basssy Date: Sun, 29 Sep 2024 11:25:43 +0900 Subject: [PATCH 05/25] =?UTF-8?q?=EB=AC=BC=EA=B1=B4=ED=99=94=EB=A9=B4=20sh?= =?UTF-8?q?owdatacount=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/management/Stuff.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/management/Stuff.jsx b/src/components/management/Stuff.jsx index c46bd504..638126ee 100644 --- a/src/components/management/Stuff.jsx +++ b/src/components/management/Stuff.jsx @@ -390,7 +390,7 @@ export default function Stuff() {
- From 320f6f9e1fd63a019907a91d4c944e836314ccff Mon Sep 17 00:00:00 2001 From: minsik Date: Mon, 30 Sep 2024 09:45:14 +0900 Subject: [PATCH 06/25] 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 07/25] 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 08/25] =?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 09/25] =?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 11/25] =?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 12/25] =?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 13/25] =?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 14/25] =?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 15/25] =?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 16/25] =?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) From 533b39f0ecdcbc4612baa78bf7b4c94fec017b06 Mon Sep 17 00:00:00 2001 From: yoosangwook Date: Wed, 2 Oct 2024 11:33:49 +0900 Subject: [PATCH 17/25] refactor: Add modal handle --- src/components/common/draggable/WithDraggable.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/common/draggable/WithDraggable.jsx b/src/components/common/draggable/WithDraggable.jsx index 23195fbe..0e19704c 100644 --- a/src/components/common/draggable/WithDraggable.jsx +++ b/src/components/common/draggable/WithDraggable.jsx @@ -17,7 +17,7 @@ export default function WithDraggable({ isShow, children, pos }) { return ( <> {isShow && ( - handleOnDrag(e, data)}> + handleOnDrag(e, data)} handle=".modal-head"> {children} )} From d6641369098ea714b8a12b7379c62d9f03422c27 Mon Sep 17 00:00:00 2001 From: yoosangwook Date: Wed, 2 Oct 2024 13:18:04 +0900 Subject: [PATCH 18/25] fix: Control modal handle props --- src/components/common/draggable/WithDraggable.jsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/common/draggable/WithDraggable.jsx b/src/components/common/draggable/WithDraggable.jsx index 0e19704c..29952981 100644 --- a/src/components/common/draggable/WithDraggable.jsx +++ b/src/components/common/draggable/WithDraggable.jsx @@ -3,7 +3,7 @@ import { useEffect, useState } from 'react' import Draggable from 'react-draggable' -export default function WithDraggable({ isShow, children, pos }) { +export default function WithDraggable({ isShow, children, pos, handle = '' }) { const [position, setPosition] = useState({ x: 0, y: 0 }) const handleOnDrag = (e, data) => { @@ -17,7 +17,11 @@ export default function WithDraggable({ isShow, children, pos }) { return ( <> {isShow && ( - handleOnDrag(e, data)} handle=".modal-head"> + handleOnDrag(e, data)} + handle={handle === '' ? '.modal-head' : handle} + > {children} )} From f7310b53d5276f0622d7204c51889bb28020d33e Mon Sep 17 00:00:00 2001 From: minsik Date: Wed, 2 Oct 2024 13:51:31 +0900 Subject: [PATCH 19/25] =?UTF-8?q?-=20=EB=B0=B0=EC=B9=98=EB=A9=B4=20?= =?UTF-8?q?=EC=B4=88=EA=B8=B0=20=EC=84=A4=EC=A0=95=20modal=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/floor-plan/CanvasMenu.jsx | 16 ++- src/components/floor-plan/FloorPlan.jsx | 5 +- src/components/floor-plan/MenuDepth01.jsx | 3 +- .../outerlinesetting/WallLineSetting.jsx | 2 +- .../modal/placementShape/MaterialGuide.jsx | 17 +++ .../placementShape/PlacementShapeSetting.js | 120 ++++++++++++++++++ .../modal/placementShape/SizeGuide.jsx | 39 ++++++ .../modal/setting01/SettingModal01.jsx | 2 +- src/locales/ja.json | 17 +++ src/locales/ko.json | 17 +++ src/styles/_modal.scss | 2 - 11 files changed, 233 insertions(+), 7 deletions(-) create mode 100644 src/components/floor-plan/modal/placementShape/MaterialGuide.jsx create mode 100644 src/components/floor-plan/modal/placementShape/PlacementShapeSetting.js create mode 100644 src/components/floor-plan/modal/placementShape/SizeGuide.jsx diff --git a/src/components/floor-plan/CanvasMenu.jsx b/src/components/floor-plan/CanvasMenu.jsx index 99b3e048..bda5a3ca 100644 --- a/src/components/floor-plan/CanvasMenu.jsx +++ b/src/components/floor-plan/CanvasMenu.jsx @@ -30,7 +30,7 @@ const canvasMenus = [ ] export default function CanvasMenu(props) { - const { setShowCanvasSettingModal, showOutlineModal, setShowOutlineModal } = props + const { setShowCanvasSettingModal, showOutlineModal, setShowOutlineModal, setShowPlaceShapeModal } = props const [menuNumber, setMenuNumber] = useState(null) const [type, setType] = useState('') @@ -54,6 +54,10 @@ export default function CanvasMenu(props) { setCurrentMenu(menu.title) switch (menu.index) { + case 1: + setType('placementShape') + onClickPlacementInitialMenu() + break case 2: setType('outline') break @@ -67,12 +71,16 @@ export default function CanvasMenu(props) { } const menuProps = { setShowOutlineModal, + setShowPlaceShapeModal, type, } const settingsModalOptions = useRecoilState(settingModalFirstOptionsState) useEffect(() => { + if (menuNumber === 1) { + onClickPlacementInitialMenu() + } if (menuNumber !== 2 && showOutlineModal) setShowOutlineModal(false) }, [menuNumber, type]) @@ -81,6 +89,12 @@ export default function CanvasMenu(props) { saveCanvas(sessionState.userId) } + const onClickPlacementInitialMenu = () => { + setShowOutlineModal(false) + setShowCanvasSettingModal(false) + setShowPlaceShapeModal(true) + } + const handleClear = () => { setPoints([]) canvas?.clear() diff --git a/src/components/floor-plan/FloorPlan.jsx b/src/components/floor-plan/FloorPlan.jsx index 9f7c6641..50ed672e 100644 --- a/src/components/floor-plan/FloorPlan.jsx +++ b/src/components/floor-plan/FloorPlan.jsx @@ -12,10 +12,12 @@ 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' +import PlacementShapeSetting from '@/components/floor-plan/modal/placementShape/PlacementShapeSetting' export default function FloorPlan() { const [showCanvasSettingModal, setShowCanvasSettingModal] = useState(false) const [showOutlineModal, setShowOutlineModal] = useState(false) + const [showPlaceShapeModal, setShowPlaceShapeModal] = useState(false) const [showPropertiesSettingModal, setShowPropertiesSettingModal] = useState(false) const globalLocaleState = useRecoilValue(globalLocaleStore) const { get } = useAxios(globalLocaleState) @@ -40,6 +42,7 @@ export default function FloorPlan() { setShowCanvasSettingModal, showOutlineModal, setShowOutlineModal, + setShowPlaceShapeModal, } useEffect(() => { @@ -90,10 +93,10 @@ export default function FloorPlan() {
{showCanvasSettingModal && } - {/*{showOutlineModal && }*/} {showOutlineModal && } {showDotLineGridModal && } {showPropertiesSettingModal && } + {showPlaceShapeModal && }
diff --git a/src/components/floor-plan/MenuDepth01.jsx b/src/components/floor-plan/MenuDepth01.jsx index a6b12877..925978e6 100644 --- a/src/components/floor-plan/MenuDepth01.jsx +++ b/src/components/floor-plan/MenuDepth01.jsx @@ -7,7 +7,7 @@ import { currentMenuState } from '@/store/canvasAtom' import { useSetRecoilState } from 'recoil' export default function MenuDepth01(props) { - const { setShowOutlineModal, type } = props + const { setShowOutlineModal, type, setShowPlaceShapeModal } = props const { getMessage } = useMessage() const [activeMenu, setActiveMenu] = useState() const setCurrentMenu = useSetRecoilState(currentMenuState) @@ -16,6 +16,7 @@ export default function MenuDepth01(props) { setShowOutlineModal(menu === MENU.ROOF_COVERING.EXTERIOR_WALL_LINE) setCurrentMenu(menu) if (type === 'outline') { + setShowPlaceShapeModal(false) setShowOutlineModal(id === 0) } } diff --git a/src/components/floor-plan/modal/outerlinesetting/WallLineSetting.jsx b/src/components/floor-plan/modal/outerlinesetting/WallLineSetting.jsx index 0cba88c2..d4b05967 100644 --- a/src/components/floor-plan/modal/outerlinesetting/WallLineSetting.jsx +++ b/src/components/floor-plan/modal/outerlinesetting/WallLineSetting.jsx @@ -16,7 +16,7 @@ export default function WallLineSetting(props) { const { type, setType, handleFix, handleRollback } = useOuterLineWall() return ( - +

{getMessage('modal.cover.outline.drawing')}

diff --git a/src/components/floor-plan/modal/placementShape/MaterialGuide.jsx b/src/components/floor-plan/modal/placementShape/MaterialGuide.jsx new file mode 100644 index 00000000..eff09305 --- /dev/null +++ b/src/components/floor-plan/modal/placementShape/MaterialGuide.jsx @@ -0,0 +1,17 @@ +import { useMessage } from '@/hooks/useMessage' + +export default function MaterialGuide({ setShowMaterialGuidModal }) { + const { getMessage } = useMessage() + return ( +
+
+ +
+
+
{getMessage('modal.placement.initial.setting.roof.material.info')}
+
+
+ ) +} diff --git a/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.js b/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.js new file mode 100644 index 00000000..606f932d --- /dev/null +++ b/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.js @@ -0,0 +1,120 @@ +import SizeGuide from '@/components/floor-plan/modal/placementShape/SizeGuide' +import MaterialGuide from '@/components/floor-plan/modal/placementShape/MaterialGuide' +import WithDraggable from '@/components/common/draggable/WithDraggable' +import { useState } from 'react' +import { useMessage } from '@/hooks/useMessage' + +export default function PlacementShapeSetting({ setShowPlaceShapeModal }) { + const [showSizeGuideModal, setShowSizeGuidModal] = useState(false) + const [showMaterialGuideModal, setShowMaterialGuidModal] = useState(false) + const { getMessage } = useMessage() + return ( + +
+
+

{getMessage('plan.menu.placement.surface.initial.setting')}

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + +
{getMessage('modal.placement.initial.setting.plan.drawing')}{getMessage('modal.placement.initial.setting.plan.drawing.size.stuff')}
+ {getMessage('modal.placement.initial.setting.size')} + + +
+
+ + +
+
+ + +
+
+ + +
+
+
{getMessage('modal.placement.initial.setting.roof.angle.setting')} +
+
+ + +
+
+ + +
+
+
+ {getMessage('modal.placement.initial.setting.roof.material')} + + +
+
+ +
+
+ W +
+ +
+
+
+ L +
+ +
+
+
+ {getMessage('modal.placement.initial.setting.rafter')} +
+ +
+
+
+
+
+
+ +
+
+ {showSizeGuideModal && } + {showMaterialGuideModal && } +
+
+ ) +} diff --git a/src/components/floor-plan/modal/placementShape/SizeGuide.jsx b/src/components/floor-plan/modal/placementShape/SizeGuide.jsx new file mode 100644 index 00000000..1f9d2770 --- /dev/null +++ b/src/components/floor-plan/modal/placementShape/SizeGuide.jsx @@ -0,0 +1,39 @@ +import { useMessage } from '@/hooks/useMessage' + +export default function SizeGuide({ setShowSizeGuidModal }) { + const { getMessage } = useMessage() + return ( +
+
+ +
+
+
{getMessage('modal.placement.initial.setting.size.info')}
+
+ + + + + + + + + + + + + + + + + + + +
{getMessage('modal.placement.initial.setting.size.roof')}{getMessage('modal.placement.initial.setting.size.roof.info')}
{getMessage('modal.placement.initial.setting.size.actual')}{getMessage('modal.placement.initial.setting.size.actual.info')}
{getMessage('modal.placement.initial.setting.size.none.pitch')}{getMessage('modal.placement.initial.setting.size.none.pitch.info')}
+
+
+
+ ) +} diff --git a/src/components/floor-plan/modal/setting01/SettingModal01.jsx b/src/components/floor-plan/modal/setting01/SettingModal01.jsx index 263fc924..9207bd76 100644 --- a/src/components/floor-plan/modal/setting01/SettingModal01.jsx +++ b/src/components/floor-plan/modal/setting01/SettingModal01.jsx @@ -13,7 +13,7 @@ export default function SettingModal01(props) { const { getMessage } = useMessage() return ( - +

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

diff --git a/src/locales/ja.json b/src/locales/ja.json index 06429bc6..a1285410 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -15,6 +15,22 @@ "header.stem": "ステム", "plan.menu.plan.drawing": "도면작성", "plan.menu.placement.surface.initial.setting": "配置面 初期設定", + "modal.placement.initial.setting.plan.drawing.size.stuff": "寸法入力による物品作成", + "modal.placement.initial.setting.plan.": "図面の作成方法", + "modal.placement.initial.setting.size": "寸法入力方法", + "modal.placement.initial.setting.size.info": "寸法入力方法案内", + "modal.placement.initial.setting.size.roof": "複視図入力", + "modal.placement.initial.setting.size.roof.info": "平面の外壁線と立面の屋根勾配に基づいて作画する場合に選択", + "modal.placement.initial.setting.size.actual": "実測値入力", + "modal.placement.initial.setting.size.actual.info": "現地屋根の外周寸法を入力して作画する場合選択", + "modal.placement.initial.setting.size.none.pitch": "陸上屋根", + "modal.placement.initial.setting.size.none.pitch.info": "傾斜のない平面形状の屋根にパネルを配置する場合に選択", + "modal.placement.initial.setting.roof.angle.setting": "屋根角度設定", + "modal.placement.initial.setting.roof.pitch": "傾斜", + "modal.placement.initial.setting.roof.angle": "角度", + "modal.placement.initial.setting.roof.material": "屋根材の選択(単位:mm)", + "modal.placement.initial.setting.roof.material.info": "対応可能な屋根材や足場は限定されますので、必ず事前マニュアルをご確認ください。", + "modal.placement.initial.setting.rafter": "垂木の間隔", "plan.menu.roof.cover": "지붕덮개", "plan.menu.roof.cover.outline.drawing": "外壁線を描", "plan.menu.roof.cover.roof.shape.setting": "屋根形状設定", @@ -84,6 +100,7 @@ "modal.grid.copy.info": "間隔を設定し、コピー方向を選択します", "modal.grid.copy.length": "長さ", "modal.grid.copy.save": "保存", + "modal.common.save": "保存", "modal.canvas.setting.font.plan.edit": "フォントとサイズの変更", "modal.canvas.setting.font.plan.edit.word": "文字フォントの変更", "modal.canvas.setting.font.plan.edit.flow": "フロー方向フォントの変更", diff --git a/src/locales/ko.json b/src/locales/ko.json index b3e2decd..cc84b9d7 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -15,6 +15,22 @@ "header.stem": "Stem", "plan.menu.plan.drawing": "도면작성", "plan.menu.placement.surface.initial.setting": "배치면 초기 설정", + "modal.placement.initial.setting.plan.drawing": "도면 작성방법", + "modal.placement.initial.setting.plan.drawing.size.stuff": "치수 입력에 의한 물건 작성", + "modal.placement.initial.setting.size": "치수 입력방법", + "modal.placement.initial.setting.size.info": "치수 입력방법 안내", + "modal.placement.initial.setting.size.roof": "복시도 입력", + "modal.placement.initial.setting.size.roof.info": "평면의 외벽선과 입면의 지붕 구배를 바탕으로 작화할 경우 선택", + "modal.placement.initial.setting.size.actual": "실측값 입력", + "modal.placement.initial.setting.size.actual.info": "현지 지붕의 외주 치수를 입력하여 작화하는 경우 선택", + "modal.placement.initial.setting.size.none.pitch": "육지붕", + "modal.placement.initial.setting.size.none.pitch.info": "경사가 없는 평면 형태의 지붕에 패널을 배치할 경우 선택", + "modal.placement.initial.setting.roof.angle.setting": "지붕각도 설정", + "modal.placement.initial.setting.roof.pitch": "경사", + "modal.placement.initial.setting.roof.angle": "각도", + "modal.placement.initial.setting.roof.material": "지붕재 선택(단위: mm)", + "modal.placement.initial.setting.roof.material.info": "대응 가능한 지붕재 및 발판은 한정되므로 반드시 사전 매뉴얼을 확인하십시오.", + "modal.placement.initial.setting.rafter": "서까래", "plan.menu.roof.cover": "지붕덮개", "plan.menu.roof.cover.outline.drawing": "외벽선 그리기", "plan.menu.roof.cover.roof.shape.setting": "지붕형상 설정", @@ -85,6 +101,7 @@ "modal.grid.copy.info": "간격을 설정하고 복사 방향을 선택하십시오", "modal.grid.copy.length": "길이", "modal.grid.copy.save": "저장", + "modal.common.save": "저장", "modal.canvas.setting.font.plan.edit": "글꼴 및 크기 변경", "modal.canvas.setting.font.plan.edit.word": "문자 글꼴 변경", "modal.canvas.setting.font.plan.edit.flow": "흐름 방향 글꼴 변경", diff --git a/src/styles/_modal.scss b/src/styles/_modal.scss index 28456176..88b63229 100644 --- a/src/styles/_modal.scss +++ b/src/styles/_modal.scss @@ -28,8 +28,6 @@ $alert-color: #101010; .modal-pop-wrap { position: fixed; - top: 200px; - right: 100px; width: 100%; min-width: 300px; height: -webkit-fit-content; From fcfdd6547c2aa318f444a8e4ae63eb516854627f Mon Sep 17 00:00:00 2001 From: yoosangwook Date: Wed, 2 Oct 2024 14:05:33 +0900 Subject: [PATCH 20/25] feat: Add grid color setting modal --- src/components/floor-plan/FloorPlan.jsx | 8 ++++++ .../modal/grid/GridColorSetting.jsx | 27 +++++++++++++++++++ .../floor-plan/modal/setting01/GridOption.jsx | 6 +++-- .../modal/setting01/SettingModal01.jsx | 4 +-- 4 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 src/components/floor-plan/modal/grid/GridColorSetting.jsx diff --git a/src/components/floor-plan/FloorPlan.jsx b/src/components/floor-plan/FloorPlan.jsx index 55b700c1..19dfe66a 100644 --- a/src/components/floor-plan/FloorPlan.jsx +++ b/src/components/floor-plan/FloorPlan.jsx @@ -12,6 +12,7 @@ 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' +import GridColorSetting from './modal/grid/GridColorSetting' export default function FloorPlan() { const [showCanvasSettingModal, setShowCanvasSettingModal] = useState(false) @@ -27,9 +28,11 @@ export default function FloorPlan() { const [showDotLineGridModal, setShowDotLineGridModal] = useState(false) const [showGridCopyModal, setShowGridCopyModal] = useState(false) const [showGridMoveModal, setShowGridMoveModal] = useState(false) + const [showColorPickerModal, setShowColorPickerModal] = useState(false) const canvasSettingProps = { setShowCanvasSettingModal, setShowDotLineGridModal, + setShowColorPickerModal, } const outlineProps = { @@ -78,6 +81,10 @@ export default function FloorPlan() { setShowDotLineGridModal, } + const gridColorProps = { + setShowColorPickerModal, + } + const propertiesSettingProps = { setShowPropertiesSettingModal, } @@ -94,6 +101,7 @@ export default function FloorPlan() { {/*{showOutlineModal && }*/} {showOutlineModal && } {showDotLineGridModal && } + {showColorPickerModal && } {showPropertiesSettingModal && }
diff --git a/src/components/floor-plan/modal/grid/GridColorSetting.jsx b/src/components/floor-plan/modal/grid/GridColorSetting.jsx new file mode 100644 index 00000000..21d2a210 --- /dev/null +++ b/src/components/floor-plan/modal/grid/GridColorSetting.jsx @@ -0,0 +1,27 @@ +import WithDraggable from '@/components/common/draggable/WithDraggable' +import ColorPicker from '@/components/common/color-picker/ColorPicker' +import { useRecoilState } from 'recoil' +import { gridColorState } from '@/store/gridAtom' +import { useMessage } from '@/hooks/useMessage' + +export default function GridColorSetting(props) { + const { setShowColorPickerModal } = props + const [color, setColor] = useRecoilState(gridColorState) + const { getMessage } = useMessage() + + return ( + +
+
+

{getMessage('modal.canvas.setting.grid.color.setting')}

+ +
+
+ +
+
+
+ ) +} diff --git a/src/components/floor-plan/modal/setting01/GridOption.jsx b/src/components/floor-plan/modal/setting01/GridOption.jsx index edf1e802..1b157b75 100644 --- a/src/components/floor-plan/modal/setting01/GridOption.jsx +++ b/src/components/floor-plan/modal/setting01/GridOption.jsx @@ -8,7 +8,7 @@ import { gridColorState } from '@/store/gridAtom' import { useColor } from 'react-color-palette' export default function GridOption(props) { - const { setShowDotLineGridModal } = props + const { setShowDotLineGridModal, setShowColorPickerModal } = props const [gridOptions, setGridOptions] = useRecoilState(settingModalGridOptionsState) const [gridColor, setGridColor] = useRecoilState(gridColorState) const [adsorptionPointAddMode, setAdsorptionPointAddMode] = useRecoilState(adsorptionPointAddModeState) @@ -55,7 +55,9 @@ export default function GridOption(props) { if (option.id === 4) { // 그리드 색 설정 if (option.selected) { - setColorPickerShow(true) + setShowColorPickerModal(true) + } else { + setShowColorPickerModal(false) } } diff --git a/src/components/floor-plan/modal/setting01/SettingModal01.jsx b/src/components/floor-plan/modal/setting01/SettingModal01.jsx index 263fc924..356b90c7 100644 --- a/src/components/floor-plan/modal/setting01/SettingModal01.jsx +++ b/src/components/floor-plan/modal/setting01/SettingModal01.jsx @@ -8,7 +8,7 @@ import { useMessage } from '@/hooks/useMessage' import GridOption from '@/components/floor-plan/modal/setting01/GridOption' export default function SettingModal01(props) { - const { setShowCanvasSettingModal, setShowDotLineGridModal } = props + const { setShowCanvasSettingModal, setShowDotLineGridModal, setShowColorPickerModal } = props const [buttonAct, setButtonAct] = useState(1) const { getMessage } = useMessage() @@ -36,7 +36,7 @@ export default function SettingModal01(props) {
{buttonAct === 1 && } {buttonAct === 2 && } - {buttonAct === 3 && } + {buttonAct === 3 && }
From 2e52a05f3ed7af27a9f898195fa51015da98c15c Mon Sep 17 00:00:00 2001 From: yjnoh Date: Wed, 2 Oct 2024 14:07:40 +0900 Subject: [PATCH 21/25] =?UTF-8?q?=EC=88=98=EB=8F=99=20=EC=85=80=20?= =?UTF-8?q?=EC=9E=91=EC=97=85=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 | 174 ++++++++++++++++++++++++++++++------------- 1 file changed, 122 insertions(+), 52 deletions(-) diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index 6778bdba..ee110a5e 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -4871,9 +4871,11 @@ export function useMode() { let turfPolygon let manualDrawCells = drewRoofCells // 앞에서 자동으로 했을때 추가됨 let direction + canvas.on('mouse:move', (e) => { //마우스 이벤트 삭제 후 재추가 const mousePoint = canvas.getPointer(e.e) + for (let i = 0; i < trestlePolygons.length; i++) { turfPolygon = polygonToTurfPolygon(trestlePolygons[i]) direction = trestlePolygons[i].direction //도형의 방향 @@ -4890,27 +4892,28 @@ export function useMode() { const turfPoints = coordToTurfPolygon(points) if (turf.booleanWithin(turfPoints, turfPolygon)) { - //turf에 보면 폴리곤안에 포인트가 있는지 함수가 있다 - - // 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')) //움직일때 일단 지워가면서 움직임 - 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 축 크기 조정 잠금 + fabricPolygon = new fabric.Rect({ + fill: 'white', + stroke: 'black', + strokeWidth: 1, + width: width, + height: height, + left: mousePoint.x - width / 2, + top: mousePoint.y - height / 2, + selectable: false, + lockMovementX: true, + lockMovementY: true, + lockRotation: true, + lockScalingX: true, + lockScalingY: true, opacity: 0.8, - parentId: trestlePolygons[i].parentId, name: 'tmpCell', + parentId: trestlePolygons[i].parentId, }) canvas?.add(fabricPolygon) //움직여가면서 추가됨 @@ -4918,67 +4921,127 @@ export function useMode() { /** * 스냅기능 */ - let snapDistance = 20 + let snapDistance = 10 + let cellSnapDistance = 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 trestleLeft = trestlePolygons[i].left + const trestleTop = trestlePolygons[i].top + const trestleRight = trestleLeft + trestlePolygons[i].width * trestlePolygons[i].scaleX + const trestleBottom = trestleTop + trestlePolygons[i].height * trestlePolygons[i].scaleY + const bigCenterY = (trestleTop + trestleTop + 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 + const smallCenterX = smallLeft + (fabricPolygon.width * fabricPolygon.scaleX) / 2 + const smallCenterY = smallTop + (fabricPolygon.height * fabricPolygon.scaleX) / 2 + + /** + * 미리 깔아놓은 셀이 있을때 셀에 흡착됨 + */ + if (manualDrawCells) { + manualDrawCells.forEach((cell) => { + const holdCellLeft = cell.left + const holdCellTop = cell.top + const holdCellRight = holdCellLeft + cell.width * cell.scaleX + const holdCellBottom = holdCellTop + cell.height * cell.scaleY + const holdCellCenterX = holdCellLeft + (cell.width * cell.scaleX) / 2 + const holdCellCenterY = holdCellTop + (cell.height * cell.scaleY) / 2 + + //설치된 셀에 좌측에 스냅 + if (Math.abs(smallRight - holdCellLeft) < snapDistance) { + fabricPolygon.left = holdCellLeft - width - 0.5 + } + + //설치된 셀에 우측에 스냅 + if (Math.abs(smallLeft - holdCellRight) < snapDistance) { + fabricPolygon.left = holdCellRight + 0.5 + } + + //설치된 셀에 위쪽에 스냅 + if (Math.abs(smallBottom - holdCellTop) < snapDistance) { + fabricPolygon.top = holdCellTop - height - 0.5 + } + + //설치된 셀에 밑쪽에 스냅 + if (Math.abs(smallTop - holdCellBottom) < snapDistance) { + fabricPolygon.top = holdCellBottom + 0.5 + } + //가운데 -> 가운데 + if (Math.abs(smallCenterX - holdCellCenterX) < cellSnapDistance) { + fabricPolygon.left = holdCellCenterX - width / 2 + } + //왼쪽 -> 가운데 + if (Math.abs(smallLeft - holdCellCenterX) < cellSnapDistance) { + fabricPolygon.left = holdCellCenterX + } + // 오른쪽 -> 가운데 + if (Math.abs(smallRight - holdCellCenterX) < cellSnapDistance) { + fabricPolygon.left = holdCellCenterX - width + } + //세로 가운데 -> 가운데 + if (Math.abs(smallCenterY - holdCellCenterY) < cellSnapDistance) { + fabricPolygon.top = holdCellCenterY - height / 2 + } + //위쪽 -> 가운데 + if (Math.abs(smallTop - holdCellCenterY) < cellSnapDistance) { + fabricPolygon.top = holdCellCenterY + } + //아랫쪽 -> 가운데 + if (Math.abs(smallBottom - holdCellCenterY) < cellSnapDistance) { + fabricPolygon.top = holdCellCenterY - height + } + }) + } // 위쪽 변에 스냅 - if (Math.abs(smallTop - bigTop) < snapDistance) { - fabricPolygon.top = bigTop + if (Math.abs(smallTop - trestleTop) < snapDistance) { + fabricPolygon.top = trestleTop } // 아래쪽 변에 스냅 - 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(smallTop + fabricPolygon.height * fabricPolygon.scaleY - (trestleTop + trestlePolygons[i].height)) < snapDistance) { + fabricPolygon.top = trestleTop + trestlePolygons[i].height - fabricPolygon.height * fabricPolygon.scaleY } // 왼쪽변에 스냅 - if (Math.abs(smallLeft - bigLeft) < snapDistance) { - fabricPolygon.left = bigLeft + if (Math.abs(smallLeft - trestleLeft) < snapDistance) { + fabricPolygon.left = trestleLeft } //오른쪽 변에 스냅 - if (Math.abs(smallRight - bigRight) < snapDistance) { - fabricPolygon.left = bigRight - fabricPolygon.width * fabricPolygon.scaleX + if (Math.abs(smallRight - trestleRight) < snapDistance) { + fabricPolygon.left = trestleRight - 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(smallLeft - (trestleLeft + trestlePolygons[i].width / 2)) < snapDistance) { + fabricPolygon.left = trestleLeft + 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(smallCenterX - (trestleLeft + trestlePolygons[i].width / 2)) < snapDistance) { + fabricPolygon.left = trestleLeft + 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 + if (Math.abs(smallRight - (trestleLeft + trestlePolygons[i].width / 2)) < snapDistance) { + fabricPolygon.left = trestleLeft + 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 + fabricPolygon.height / 2 - bigCenterY) < snapDistance) { + fabricPolygon.top = bigCenterY - fabricPolygon.height / 2 } - if (Math.abs(smallTop - (bigTop + trestlePolygons[i].height / 2)) < snapDistance) { - fabricPolygon.top = bigTop + trestlePolygons[i].height / 2 + if (Math.abs(smallTop - (trestleTop + trestlePolygons[i].height / 2)) < snapDistance) { + fabricPolygon.top = trestleTop + 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 + if (Math.abs(smallBottom - (trestleTop + trestlePolygons[i].height / 2)) < snapDistance) { + fabricPolygon.top = trestleTop + trestlePolygons[i].height / 2 - fabricPolygon.height * fabricPolygon.scaleY } } @@ -4999,27 +5062,34 @@ export function useMode() { canvas?.on('mouse:up', (e) => { if (!inside) return if (fabricPolygon) { - const turfCellPolygon = polygonToTurfPolygon(fabricPolygon) + const rectPoints = [ + { x: fabricPolygon.left + 0.1, y: fabricPolygon.top + 0.1 }, + { x: fabricPolygon.left + 0.1 + fabricPolygon.width * fabricPolygon.scaleX, y: fabricPolygon.top + 0.1 }, + { + x: fabricPolygon.left + fabricPolygon.width * fabricPolygon.scaleX + 0.1, + y: fabricPolygon.top + fabricPolygon.height * fabricPolygon.scaleY + 0.1, + }, + { x: fabricPolygon.left + 0.1, y: fabricPolygon.top + fabricPolygon.height * fabricPolygon.scaleY + 0.1 }, + ] + + fabricPolygon.set({ points: rectPoints }) + const tempTurfModule = polygonToTurfPolygon(fabricPolygon) + fabricPolygon.setCoords() //좌표 재정렬 - if (turf.booleanWithin(turfCellPolygon, turfPolygon)) { + if (turf.booleanWithin(tempTurfModule, 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))) + const isOverlap = manualDrawCells.some((cell) => turf.booleanOverlap(tempTurfModule, polygonToTurfPolygon(cell))) if (!isOverlap) { //안겹치면 넣는다 fabricPolygon.setCoords() - fabricPolygon.set({ name: 'cell' }) + fabricPolygon.set({ name: 'cell', fill: '#BFFD9F' }) manualDrawCells.push(fabricPolygon) } else { alert('셀끼리 겹치면 안되죠?') } - // } else { - // alert('나갔으요!!') + } else { + alert('나갔으요!!') } setDrewRoofCells(manualDrawCells) } From 1bf8bff0aabb26a7273770b7e4d6e815a6d4a246 Mon Sep 17 00:00:00 2001 From: minsik Date: Wed, 2 Oct 2024 15:00:19 +0900 Subject: [PATCH 22/25] =?UTF-8?q?-=20dot.line=20=EB=AA=A8=EB=8B=AC=20?= =?UTF-8?q?=EC=9C=84=EC=B9=98=20=EC=88=98=EC=A0=95=20-=20grid=20color=20?= =?UTF-8?q?=EB=AA=A8=EB=8B=AC=20=EC=9C=84=EC=B9=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/floor-plan/modal/grid/DotLineGrid.jsx | 2 +- src/components/floor-plan/modal/grid/GridColorSetting.jsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/floor-plan/modal/grid/DotLineGrid.jsx b/src/components/floor-plan/modal/grid/DotLineGrid.jsx index 1609f5a0..86584959 100644 --- a/src/components/floor-plan/modal/grid/DotLineGrid.jsx +++ b/src/components/floor-plan/modal/grid/DotLineGrid.jsx @@ -220,7 +220,7 @@ export default function DotLineGrid(props) { } return ( - +

{getMessage('modal.canvas.setting.grid.dot.line.setting')}

diff --git a/src/components/floor-plan/modal/grid/GridColorSetting.jsx b/src/components/floor-plan/modal/grid/GridColorSetting.jsx index 21d2a210..ed213250 100644 --- a/src/components/floor-plan/modal/grid/GridColorSetting.jsx +++ b/src/components/floor-plan/modal/grid/GridColorSetting.jsx @@ -10,7 +10,7 @@ export default function GridColorSetting(props) { const { getMessage } = useMessage() return ( - +

{getMessage('modal.canvas.setting.grid.color.setting')}

From 4857c31d5a5c95b112a227e4ca6b4dfb32fafc61 Mon Sep 17 00:00:00 2001 From: yjnoh Date: Wed, 2 Oct 2024 15:07:13 +0900 Subject: [PATCH 23/25] =?UTF-8?q?=EC=85=80=20=EB=8F=84=EB=A8=B8=EC=98=81?= =?UTF-8?q?=EC=97=AD=20=EC=A0=9C=EC=99=B8=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useMode.js | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index ee110a5e..dbe3753b 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -4865,6 +4865,8 @@ export function useMode() { const drawCellManualInTrestle = () => { const trestlePolygons = canvas?.getObjects().filter((obj) => obj.name === 'trestle') //가대를 가져옴 + const dormerTrestlePolygons = canvas?.getObjects().filter((obj) => obj.name === 'dormerTrestle') //도머 객체 + if (trestlePolygons.length !== 0) { let fabricPolygon = null let inside = false @@ -5060,22 +5062,39 @@ export function useMode() { }) canvas?.on('mouse:up', (e) => { + let isIntersection = true if (!inside) return if (fabricPolygon) { const rectPoints = [ - { x: fabricPolygon.left + 0.1, y: fabricPolygon.top + 0.1 }, - { x: fabricPolygon.left + 0.1 + fabricPolygon.width * fabricPolygon.scaleX, y: fabricPolygon.top + 0.1 }, + { x: fabricPolygon.left + 0.5, y: fabricPolygon.top + 0.5 }, + { x: fabricPolygon.left + 0.5 + fabricPolygon.width * fabricPolygon.scaleX, y: fabricPolygon.top + 0.5 }, { - x: fabricPolygon.left + fabricPolygon.width * fabricPolygon.scaleX + 0.1, - y: fabricPolygon.top + fabricPolygon.height * fabricPolygon.scaleY + 0.1, + x: fabricPolygon.left + fabricPolygon.width * fabricPolygon.scaleX + 0.5, + y: fabricPolygon.top + fabricPolygon.height * fabricPolygon.scaleY + 0.5, }, - { x: fabricPolygon.left + 0.1, y: fabricPolygon.top + fabricPolygon.height * fabricPolygon.scaleY + 0.1 }, + { x: fabricPolygon.left + 0.5, y: fabricPolygon.top + fabricPolygon.height * fabricPolygon.scaleY + 0.5 }, ] fabricPolygon.set({ points: rectPoints }) const tempTurfModule = polygonToTurfPolygon(fabricPolygon) + if (dormerTrestlePolygons) { + dormerTrestlePolygons.forEach((dormerTrestle) => { + const dormerTurfPolygon = polygonToTurfPolygon(dormerTrestle) + + const intersection = turf.intersect(turf.featureCollection([dormerTurfPolygon, tempTurfModule])) + + if (intersection) { + alert('도머위에 모듈을 올릴 수 없습니다.') + isIntersection = false + } + }) + } + + if (!isIntersection) return + fabricPolygon.setCoords() //좌표 재정렬 + if (turf.booleanWithin(tempTurfModule, turfPolygon)) { //마우스 클릭시 set으로 해당 위치에 셀을 넣음 @@ -5164,8 +5183,6 @@ export function useMode() { const cols = Math.floor((bbox[2] - bbox[0]) / width) const rows = Math.floor((bbox[3] - bbox[1]) / height) - // console.log('bbox', bbox) - for (let col = 0; col <= cols; col++) { for (let row = 0; row <= rows; row++) { let x = 0, From 0eaf9735176bb9dc8e4926194e901aaeda5113a9 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Wed, 2 Oct 2024 15:41:22 +0900 Subject: [PATCH 24/25] =?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=EC=99=84=EB=A3=8C=20=ED=9B=84=20offset=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/GridSettingsModal.jsx | 7 +- src/components/fabric/QLine.js | 4 +- src/components/fabric/QPolygon.js | 9 +- src/components/floor-plan/CanvasMenu.jsx | 2 +- .../floor-plan/modal/grid/DotLineGrid.jsx | 19 ++- .../modal/grid/GridColorSetting.jsx | 14 +- .../modal/outerlinesetting/OuterLineWall.jsx | 1 + .../outerlinesetting/PropertiesSetting.jsx | 2 +- .../floor-plan/modal/setting01/GridOption.jsx | 1 - .../modal/setting01/SettingModal01.jsx | 19 ++- src/hooks/roofcover/useOuterLineWall.js | 20 ++- src/hooks/roofcover/usePropertiesSetting.js | 135 +++++++++++------- src/hooks/useEvent.js | 9 +- src/hooks/useLine.js | 7 +- src/hooks/useMode.js | 4 +- src/hooks/usePolygon.js | 15 +- src/store/canvasAtom.js | 16 +++ src/store/gridAtom.js | 2 +- src/store/outerLineAtom.js | 5 + src/store/settingAtom.js | 2 +- 20 files changed, 204 insertions(+), 89 deletions(-) diff --git a/src/components/GridSettingsModal.jsx b/src/components/GridSettingsModal.jsx index 35bb1793..92b2e618 100644 --- a/src/components/GridSettingsModal.jsx +++ b/src/components/GridSettingsModal.jsx @@ -6,6 +6,7 @@ import { guideLineState, horiGuideLinesState, vertGuideLinesState } from '@/stor import { fabric } from 'fabric' import { ColorPicker, useColor } from 'react-color-palette' import 'react-color-palette/css' +import { gridColorState } from '@/store/gridAtom' export default function GridSettingsModal(props) { const { canvasProps } = props @@ -23,7 +24,7 @@ export default function GridSettingsModal(props) { const gridSettingArray = [] - const [guideColor, setGuideColor] = useColor('rgb(200, 15, 15)') + const gridColor = useRecoilValue(gridColorState) const [colorPickerShow, setColorPickerShow] = useState(false) const boxStyle = { @@ -67,7 +68,7 @@ export default function GridSettingsModal(props) { const horizontalLine = new fabric.Line( [0, i * moduleVertLength - moduleVertLength / 2, canvasProps.width, i * moduleVertLength - moduleVertLength / 2], { - stroke: guideColor.hex, + stroke: gridColor, strokeWidth: 1, selectable: true, lockMovementX: true, @@ -89,7 +90,7 @@ export default function GridSettingsModal(props) { const verticalLine = new fabric.Line( [i * moduleHoriLength - moduleHoriLength / 2, 0, i * moduleHoriLength - moduleHoriLength / 2, canvasProps.height], { - stroke: guideColor.hex, + stroke: gridColor, strokeWidth: 1, selectable: true, lockMovementX: true, diff --git a/src/components/fabric/QLine.js b/src/components/fabric/QLine.js index da141287..c73e442f 100644 --- a/src/components/fabric/QLine.js +++ b/src/components/fabric/QLine.js @@ -13,7 +13,7 @@ export const QLine = fabric.util.createClass(fabric.Line, { area: 0, children: [], initialize: function (points, options, canvas) { - this.callSuper('initialize', points, { ...options, selectable: options.selectable ?? false }) + this.callSuper('initialize', points, { ...options, selectable: options.selectable ?? true }) if (options.id) { this.id = options.id } else { @@ -75,7 +75,7 @@ export const QLine = fabric.util.createClass(fabric.Line, { const y2 = this.top + this.height * scaleY const dx = x2 - x1 const dy = y2 - y1 - this.length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(0)) + this.length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(1)) * 10 }, addLengthText() { diff --git a/src/components/fabric/QPolygon.js b/src/components/fabric/QPolygon.js index 84e57974..fec9e227 100644 --- a/src/components/fabric/QPolygon.js +++ b/src/components/fabric/QPolygon.js @@ -27,6 +27,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { point.x = Math.round(point.x) point.y = Math.round(point.y) }) + options.selectable = options.selectable ?? true options.sort = options.sort ?? true options.parentId = options.parentId ?? null @@ -51,6 +52,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { } this.callSuper('initialize', points, options) + if (options.id) { this.id = options.id } else { @@ -164,6 +166,9 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { stroke: this.stroke, strokeWidth: this.strokeWidth, fontSize: this.fontSize, + attributes: { + offset: 0, + }, direction: getDirectionByPoint(point, nextPoint), idx: i, }) @@ -193,14 +198,14 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { const end = points[(i + 1) % points.length] const dx = end.x - start.x const dy = end.y - start.y - const length = Math.sqrt(dx * dx + dy * dy) + const length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(1)) * 10 const midPoint = new fabric.Point((start.x + end.x) / 2, (start.y + end.y) / 2) const degree = (Math.atan2(dy, dx) * 180) / Math.PI // Create new text object if it doesn't exist - const text = new fabric.IText(length.toFixed(0), { + const text = new fabric.Text(length.toFixed(0), { left: midPoint.x, top: midPoint.y, fontSize: this.fontSize, diff --git a/src/components/floor-plan/CanvasMenu.jsx b/src/components/floor-plan/CanvasMenu.jsx index 7276805a..975c944b 100644 --- a/src/components/floor-plan/CanvasMenu.jsx +++ b/src/components/floor-plan/CanvasMenu.jsx @@ -149,7 +149,7 @@ export default function CanvasMenu(props) {
}
- +
diff --git a/src/components/floor-plan/modal/grid/DotLineGrid.jsx b/src/components/floor-plan/modal/grid/DotLineGrid.jsx index 86584959..54780ab5 100644 --- a/src/components/floor-plan/modal/grid/DotLineGrid.jsx +++ b/src/components/floor-plan/modal/grid/DotLineGrid.jsx @@ -1,12 +1,13 @@ import WithDraggable from '@/components/common/draggable/WithDraggable' import QSelectBox from '@/components/common/select/QSelectBox' -import { useState } from 'react' +import { useEffect, useState } from 'react' import { useMessage } from '@/hooks/useMessage' import { canvasState, dotLineGridSettingState, dotLineIntervalSelector } from '@/store/canvasAtom' -import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil' +import { useRecoilState, useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil' import { onlyNumberInputChange } from '@/util/input-utils' import { fabric } from 'fabric' import { gridColorState } from '@/store/gridAtom' +import { settingModalGridOptionsState } from '@/store/settingAtom' const TYPE = { DOT: 'DOT', @@ -17,6 +18,7 @@ export default function DotLineGrid(props) { // const [modalOption, setModalOption] = useRecoilState(modalState); //modal 열림닫힘 state const [close, setClose] = useState(false) const { setShowDotLineGridModal } = props + const setSettingModalGridOptions = useSetRecoilState(settingModalGridOptionsState) const gridColor = useRecoilValue(gridColorState) const canvas = useRecoilValue(canvasState) @@ -25,6 +27,17 @@ export default function DotLineGrid(props) { const interval = useRecoilValue(dotLineIntervalSelector) const { getMessage } = useMessage() + + useEffect(() => { + return () => { + setSettingModalGridOptions((prev) => { + const newSettingOptions = [...prev] + newSettingOptions[1].selected = false + return [...newSettingOptions] + }) + } + }, []) + const SelectOption = [ { id: 1, name: getMessage('modal.canvas.setting.grid.dot.line.setting.line.origin'), value: 1 }, { id: 2, name: '1/2', value: 1 / 2 }, @@ -144,7 +157,7 @@ export default function DotLineGrid(props) { const verticalLine = new fabric.Line( [i * horizontalInterval - horizontalInterval / 2, 0, i * horizontalInterval - horizontalInterval / 2, canvas.height], { - stroke: 'black', + stroke: gridColor, strokeWidth: 1, selectable: true, lockMovementX: true, diff --git a/src/components/floor-plan/modal/grid/GridColorSetting.jsx b/src/components/floor-plan/modal/grid/GridColorSetting.jsx index ed213250..5b7b2ff6 100644 --- a/src/components/floor-plan/modal/grid/GridColorSetting.jsx +++ b/src/components/floor-plan/modal/grid/GridColorSetting.jsx @@ -1,14 +1,26 @@ import WithDraggable from '@/components/common/draggable/WithDraggable' import ColorPicker from '@/components/common/color-picker/ColorPicker' -import { useRecoilState } from 'recoil' +import { useRecoilState, useSetRecoilState } from 'recoil' import { gridColorState } from '@/store/gridAtom' import { useMessage } from '@/hooks/useMessage' +import { useEffect } from 'react' +import { settingModalGridOptionsState } from '@/store/settingAtom' export default function GridColorSetting(props) { const { setShowColorPickerModal } = props const [color, setColor] = useRecoilState(gridColorState) + const setSettingModalGridOptions = useSetRecoilState(settingModalGridOptionsState) const { getMessage } = useMessage() + useEffect(() => { + return () => { + setSettingModalGridOptions((prev) => { + const newSettingOptions = [...prev] + newSettingOptions[3].selected = false + return [...newSettingOptions] + }) + } + }, []) return (
diff --git a/src/components/floor-plan/modal/outerlinesetting/OuterLineWall.jsx b/src/components/floor-plan/modal/outerlinesetting/OuterLineWall.jsx index a3493f17..ffa5e0e7 100644 --- a/src/components/floor-plan/modal/outerlinesetting/OuterLineWall.jsx +++ b/src/components/floor-plan/modal/outerlinesetting/OuterLineWall.jsx @@ -2,6 +2,7 @@ import { useMessage } from '@/hooks/useMessage' import { onlyNumberInputChange } from '@/util/input-utils' +import GridMove from '@/components/floor-plan/modal/grid/GridMove' export default function OuterLineWall({ props }) { const { getMessage } = useMessage() diff --git a/src/components/floor-plan/modal/outerlinesetting/PropertiesSetting.jsx b/src/components/floor-plan/modal/outerlinesetting/PropertiesSetting.jsx index cd2973e7..248567e3 100644 --- a/src/components/floor-plan/modal/outerlinesetting/PropertiesSetting.jsx +++ b/src/components/floor-plan/modal/outerlinesetting/PropertiesSetting.jsx @@ -9,7 +9,7 @@ export default function PropertiesSetting(props) { const { handleSetEaves, handleSetGable, handleRollback, handleFix, closeModal } = usePropertiesSetting() return ( - +

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

diff --git a/src/components/floor-plan/modal/setting01/GridOption.jsx b/src/components/floor-plan/modal/setting01/GridOption.jsx index 1b157b75..d9301afc 100644 --- a/src/components/floor-plan/modal/setting01/GridOption.jsx +++ b/src/components/floor-plan/modal/setting01/GridOption.jsx @@ -18,7 +18,6 @@ export default function GridOption(props) { const [color, setColor] = useColor(gridColor) useEffect(() => { - console.log(color) setGridColor(color.hex) }, [color]) diff --git a/src/components/floor-plan/modal/setting01/SettingModal01.jsx b/src/components/floor-plan/modal/setting01/SettingModal01.jsx index b08a2578..78511548 100644 --- a/src/components/floor-plan/modal/setting01/SettingModal01.jsx +++ b/src/components/floor-plan/modal/setting01/SettingModal01.jsx @@ -6,11 +6,18 @@ 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' +import { canGridOptionSeletor } from '@/store/canvasAtom' +import { useRecoilValue } from 'recoil' export default function SettingModal01(props) { const { setShowCanvasSettingModal, setShowDotLineGridModal, setShowColorPickerModal } = props const [buttonAct, setButtonAct] = useState(1) const { getMessage } = useMessage() + const canGridOptionSeletorValue = useRecoilValue(canGridOptionSeletor) + + const handleBtnClick = (num) => { + setButtonAct(num) + } return ( @@ -23,16 +30,18 @@ export default function SettingModal01(props) {
- - - + {canGridOptionSeletorValue && ( + + )}
{buttonAct === 1 && } {buttonAct === 2 && } diff --git a/src/hooks/roofcover/useOuterLineWall.js b/src/hooks/roofcover/useOuterLineWall.js index 0f1e4f41..130593a0 100644 --- a/src/hooks/roofcover/useOuterLineWall.js +++ b/src/hooks/roofcover/useOuterLineWall.js @@ -1,6 +1,6 @@ import { useEffect, useRef } from 'react' import { distanceBetweenPoints, getDegreeByChon } from '@/util/canvas-util' -import { useRecoilState, useRecoilValue } from 'recoil' +import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' import { adsorptionPointAddModeState, adsorptionPointModeState, @@ -20,6 +20,7 @@ import { outerLineArrow1State, outerLineArrow2State, outerLineDiagonalState, + outerLineFixState, outerLineLength1State, outerLineLength2State, outerLinePointsState, @@ -59,6 +60,8 @@ export function useOuterLineWall() { const arrow1Ref = useRef(arrow1) const arrow2Ref = useRef(arrow2) + const setOuterLineFix = useSetRecoilState(outerLineFixState) + const outerLineDiagonalLengthRef = useRef(null) const isFix = useRef(false) @@ -69,6 +72,15 @@ export function useOuterLineWall() { if (adsorptionPointAddMode || tempGridMode) { return } + + if (points.length === 0) { + // 만약 포인트가 없다면 모든 라인과 텍스트를 삭제 후 outerLines에서 point를 뽑아 points에 넣어준다. + const lengthTxts = canvas?.getObjects().filter((obj) => obj.name === 'lengthTxt') + lengthTxts.forEach((txt) => { + canvas?.remove(txt) + }) + } + addCanvasMouseEventListener('mouse:down', mouseDown) clear() }, [verticalHorizontalMode, points, adsorptionPointAddMode, adsorptionPointMode, adsorptionRange, interval, tempGridMode]) @@ -153,6 +165,7 @@ export function useOuterLineWall() { canvas?.remove(canvas?.getObjects().find((obj) => obj.name === 'startPoint')) if (points.length === 0) { + setOuterLineFix(true) removeAllDocumentEventListeners() return } @@ -172,6 +185,7 @@ export function useOuterLineWall() { canvas?.add(point) } else { + setOuterLineFix(false) points.forEach((point, idx) => { if (idx === 0) { return @@ -183,10 +197,10 @@ export function useOuterLineWall() { const firstPoint = points[0] if (isFix.current) { - canvas?.renderAll() - closeModalFn.current(false) removeAllMouseEventListeners() removeAllDocumentEventListeners() + canvas?.renderAll() + closeModalFn.current(false) } if (points.length < 3) { diff --git a/src/hooks/roofcover/usePropertiesSetting.js b/src/hooks/roofcover/usePropertiesSetting.js index 9df385ed..d478a1bc 100644 --- a/src/hooks/roofcover/usePropertiesSetting.js +++ b/src/hooks/roofcover/usePropertiesSetting.js @@ -1,91 +1,116 @@ import { useEffect, useRef } from 'react' import { LINE_TYPE } from '@/common/common' import { useRecoilValue } from 'recoil' -import { canvasState } from '@/store/canvasAtom' +import { canvasState, currentObjectState } from '@/store/canvasAtom' +import { useMode } from '@/hooks/useMode' +import { usePolygon } from '@/hooks/usePolygon' +import { useLine } from '@/hooks/useLine' export function usePropertiesSetting() { - const currentLine = useRef(null) - const currentIdx = useRef(-1) const canvas = useRecoilValue(canvasState) + const currentObject = useRecoilValue(currentObjectState) + + const { drawRoofPolygon } = useMode() + + const { addPolygonByLines } = usePolygon() + const { removeLine } = useLine() + useEffect(() => { - selectNextLine() - }, []) + if (!currentObject) { + return + } + if (currentObject.name !== 'outerLine') { + return + } + + const type = currentObject.attributes?.type + + const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') + + lines.forEach((line) => { + const lineType = line.attributes?.type + if (!lineType) { + line.set({ + stroke: '#000000', + strokeWidth: 4, + }) + } + }) + + if (!type) { + currentObject.set({ + stroke: '#EA10AC', + strokeWidth: 4, + }) + } + }, [currentObject]) + + const history = useRef([]) const handleSetEaves = () => { - currentLine.current.set({ + const selectedLine = canvas?.getActiveObject() + if (!selectedLine) { + return + } + selectedLine.set({ stroke: '#45CD7D', strokeWidth: 4, attributes: { - offset: 500, + offset: 50, type: LINE_TYPE.WALLLINE.EAVES, pitch: 4, }, }) + history.current.push(selectedLine) + nextLineFocus(selectedLine) canvas.renderAll() - selectNextLine() } const handleSetGable = () => { - currentLine.current.set({ + const selectedLine = canvas?.getActiveObject() + if (!selectedLine) { + return + } + selectedLine.set({ stroke: '#3FBAE6', strokeWidth: 4, attributes: { - offset: 300, + offset: 30, type: LINE_TYPE.WALLLINE.GABLE, }, }) - canvas.renderAll() - selectNextLine() - } - - 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, - }) - + history.current.push(selectedLine) + nextLineFocus(selectedLine) canvas.renderAll() } - const selectPrevLine = () => { + const nextLineFocus = (selectedLine) => { const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') + const index = lines.findIndex((line) => line === selectedLine) - currentIdx.current-- - - if (currentIdx.current <= -1) { - currentIdx.current = -1 - selectNextLine() - return + const nextLine = lines[index + 1] || lines[0] + if (!nextLine.attributes?.type) { + canvas.setActiveObject(nextLine) } else { - lines.forEach((line, index) => { - if (index >= currentIdx.current) { - delete line.attributes - line.set({ - stroke: '#000000', - strokeWidth: 4, - }) - } - currentIdx.current-- - canvas.renderAll() - selectNextLine() - }) + //activeObject 해제 + canvas.discardActiveObject() } } const handleRollback = () => { - selectPrevLine() + if (history.current.length === 0) { + return + } + const lastLine = history.current.pop() + + lastLine.set({ + stroke: '#000000', + strokeWidth: 4, + }) + + canvas.setActiveObject(lastLine) + canvas.renderAll() } const handleFix = () => { @@ -100,8 +125,15 @@ export function usePropertiesSetting() { stroke: '#000000', strokeWidth: 4, }) + removeLine(line) }) + const wall = addPolygonByLines(lines, { name: 'WallLine', fill: 'transparent', stroke: 'black' }) + + wall.lines = [...lines] + + drawRoofPolygon(wall) + canvas.renderAll() } @@ -121,7 +153,6 @@ export function usePropertiesSetting() { }) canvas.renderAll() - fn(false) } diff --git a/src/hooks/useEvent.js b/src/hooks/useEvent.js index 67ff8c44..f9203dc1 100644 --- a/src/hooks/useEvent.js +++ b/src/hooks/useEvent.js @@ -1,6 +1,6 @@ import { useEffect, useRef } from 'react' -import { useRecoilState, useRecoilValue } from 'recoil' -import { canvasState, canvasZoomState, currentMenuState } from '@/store/canvasAtom' +import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' +import { canvasState, canvasZoomState, currentMenuState, textModeState } from '@/store/canvasAtom' import { fabric } from 'fabric' import { calculateDistance, distanceBetweenPoints, findClosestPoint } from '@/util/canvas-util' import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint' @@ -12,12 +12,14 @@ export function useEvent() { const currentMenu = useRecoilValue(currentMenuState) const documentEventListeners = useRef([]) const mouseEventListeners = useRef([]) - const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState) + const setCanvasZoom = useSetRecoilState(canvasZoomState) const { adsorptionPointAddMode, adsorptionPointMode, adsorptionRange, getAdsorptionPoints, adsorptionPointAddModeStateEvent } = useAdsorptionPoint() const { dotLineGridSetting, interval, getClosestLineGrid } = useDotLineGrid() const { tempGridModeStateLeftClickEvent, tempGridMode, tempGridRightClickEvent } = useTempGrid() + const textMode = useRecoilValue(textModeState) + useEffect(() => { if (!canvas) { return @@ -38,6 +40,7 @@ export function useEvent() { addCanvasMouseEventListener('mouse:move', defaultMouseMoveEvent) addCanvasMouseEventListener('mouse:out', defaultMouseOutEvent) addDocumentEventListener('keydown', document, defaultKeyboardEvent) + addDocumentEventListener('contextmenu', document, defaultContextMenuEvent) if (adsorptionPointAddMode) { addCanvasMouseEventListener('mouse:down', adsorptionPointAddModeStateEvent) } diff --git a/src/hooks/useLine.js b/src/hooks/useLine.js index d42af418..b6c8a532 100644 --- a/src/hooks/useLine.js +++ b/src/hooks/useLine.js @@ -1,5 +1,6 @@ import { useRecoilValue } from 'recoil' import { canvasState, fontFamilyState, fontSizeState } from '@/store/canvasAtom' +import { QLine } from '@/components/fabric/QLine' export const useLine = () => { const canvas = useRecoilValue(canvasState) @@ -7,13 +8,13 @@ export const useLine = () => { const fontFamily = useRecoilValue(fontFamilyState) const addLine = (points = [], options) => { - const line = new fabric.Line(points, { + const line = new QLine(points, { ...options, - selectable: options.selectable ?? false, + fontSize: fontSize, + fontFamily: fontFamily, }) canvas?.add(line) - addLineText(line) return line } diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index ee110a5e..0c353864 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -1678,7 +1678,7 @@ export function useMode() { const drawRoofPolygon = (wall) => { // TODO [ljyoung] : offset 입력 처리 후 제거 해야함. - wall.lines.forEach((line, index) => { + /*wall.lines.forEach((line, index) => { if (index === wall.lines.length - 1 || index === 3) { line.attributes = { type: 'gable', @@ -1690,7 +1690,7 @@ export function useMode() { offset: 50, } } - }) + })*/ const polygon = createRoofPolygon(wall.points) const originPolygon = new QPolygon(wall.points, { fontSize: 0 }) diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js index b364bf86..13b12fe1 100644 --- a/src/hooks/usePolygon.js +++ b/src/hooks/usePolygon.js @@ -2,6 +2,7 @@ import { canvasState, fontFamilyState, fontSizeState } from '@/store/canvasAtom' import { useRecoilValue } from 'recoil' import { fabric } from 'fabric' import { getDirectionByPoint } from '@/util/canvas-util' +import { QPolygon } from '@/components/fabric/QPolygon' export const usePolygon = () => { const canvas = useRecoilValue(canvasState) @@ -9,19 +10,22 @@ export const usePolygon = () => { const fontFamily = useRecoilValue(fontFamilyState) const addPolygon = (points, options) => { - const polygon = new fabric.Polygon(points, { + const polygon = new QPolygon(points, { ...options, - selectable: options.selectable ?? false, + fontSize: fontSize, + fontFamily: fontFamily, + selectable: true, }) canvas?.add(polygon) - addLengthText(polygon) + + return polygon } const addPolygonByLines = (lines, options) => { const points = createPolygonPointsFromOuterLines(lines) - addPolygon(points, { + return addPolygon(points, { ...options, }) } @@ -39,10 +43,11 @@ export const usePolygon = () => { const degree = (Math.atan2(dy, dx) * 180) / Math.PI // Create new text object if it doesn't exist - const text = new fabric.IText(length.toString(), { + const text = new fabric.Text(length.toString(), { left: midPoint.x, top: midPoint.y, fontSize: fontSize, + fontFamily: fontFamily, parentId: polygon.id, minX: Math.min(start.x, end.x), maxX: Math.max(start.x, end.x), diff --git a/src/store/canvasAtom.js b/src/store/canvasAtom.js index aa400a46..9fb440c6 100644 --- a/src/store/canvasAtom.js +++ b/src/store/canvasAtom.js @@ -1,5 +1,6 @@ import { atom, selector } from 'recoil' import { MENU } from '@/common/common' +import { outerLineFixState, outerLinePointsState } from '@/store/outerLineAtom' export const canvasState = atom({ key: 'canvasState', @@ -267,3 +268,18 @@ export const tempGridModeState = atom({ key: 'tempGridModeState', default: false, }) + +export const textModeState = atom({ + key: 'textModeState', + default: false, +}) + +export const canGridOptionSeletor = selector({ + key: 'canGridOptionSeletor', + get: ({ get }) => { + const points = get(outerLinePointsState) + const currentMenu = get(currentMenuState) + const outerLineFix = get(outerLineFixState) + return points.length === 0 || outerLineFix + }, +}) diff --git a/src/store/gridAtom.js b/src/store/gridAtom.js index 9b21a9f5..a4b4b15b 100644 --- a/src/store/gridAtom.js +++ b/src/store/gridAtom.js @@ -2,5 +2,5 @@ import { atom } from 'recoil' export const gridColorState = atom({ key: 'gridColorState', - default: '#000000', + default: '#FF0000', }) diff --git a/src/store/outerLineAtom.js b/src/store/outerLineAtom.js index 2fcc5f9d..361bc526 100644 --- a/src/store/outerLineAtom.js +++ b/src/store/outerLineAtom.js @@ -63,3 +63,8 @@ export const outerLinePointsState = atom({ key: 'outerLinePointsState', default: [], }) + +export const outerLineFixState = atom({ + key: 'outerLineFixState', + default: false, +}) diff --git a/src/store/settingAtom.js b/src/store/settingAtom.js index 7c637c2f..80fa9c4c 100644 --- a/src/store/settingAtom.js +++ b/src/store/settingAtom.js @@ -1,4 +1,4 @@ -import { atom } from 'recoil' +import { atom, selector } from 'recoil' export const settingModalFirstOptionsState = atom({ key: 'settingModalFirstOptions', From 3505064c96386addbebfcbda63ba6957fe30b8d7 Mon Sep 17 00:00:00 2001 From: minsik Date: Wed, 2 Oct 2024 17:08:50 +0900 Subject: [PATCH 25/25] =?UTF-8?q?-=20=EC=A7=80=EB=B6=95=ED=98=95=EC=83=81?= =?UTF-8?q?=20=EC=84=A4=EC=A0=95=20=EB=AA=A8=EB=8B=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../images/canvas/allocation_delete.svg | 4 + .../static/images/canvas/allocation_edit.svg | 4 + .../images/canvas/allocation_icon01_black.svg | 6 + .../images/canvas/allocation_icon01_white.svg | 6 + .../images/canvas/allocation_icon02_black.svg | 6 + .../images/canvas/allocation_icon02_white.svg | 6 + public/static/images/canvas/eaves_icon01.svg | 10 + public/static/images/canvas/eaves_icon02.svg | 10 + public/static/images/canvas/eaves_icon03.svg | 15 + public/static/images/canvas/eaves_icon04.svg | 9 + public/static/images/canvas/eaves_icon05.svg | 10 + public/static/images/canvas/eaves_icon06.svg | 18 + public/static/images/canvas/eaves_icon07.svg | 23 + public/static/images/canvas/eaves_icon08.svg | 15 + public/static/images/canvas/shape_menu01.svg | 34 + public/static/images/canvas/shape_menu02.svg | 31 + public/static/images/canvas/shape_menu03.svg | 31 + public/static/images/canvas/shape_menu04.svg | 30 + public/static/images/canvas/shape_menu05.svg | 29 + public/static/images/canvas/shape_menu06.svg | 29 + public/static/images/canvas/shape_menu07.svg | 29 + public/static/images/canvas/shape_menu08.svg | 29 + src/components/floor-plan/CanvasMenu.jsx | 5 +- src/components/floor-plan/FloorPlan.jsx | 4 + src/components/floor-plan/MenuDepth01.jsx | 3 +- ...peSetting.js => PlacementShapeSetting.jsx} | 0 .../modal/roofShape/RoofShapeSetting.jsx | 58 + .../modal/roofShape/type/Direction.jsx | 45 + .../modal/roofShape/type/Pattern.jsx | 36 + .../floor-plan/modal/roofShape/type/Ridge.jsx | 27 + .../floor-plan/modal/roofShape/type/Side.jsx | 46 + .../modal/roofShape/type/option/Eaves.jsx | 27 + .../modal/roofShape/type/option/Gable.jsx | 16 + .../roofShape/type/option/HipAndGable.jsx | 36 + .../roofShape/type/option/Jerkinhead.jsx | 36 + .../modal/roofShape/type/option/Shed.jsx | 16 + .../modal/roofShape/type/option/Wall.jsx | 38 + src/locales/ja.json | 29 +- src/locales/ko.json | 30 +- src/styles/_modal.scss | 312 +++- src/styles/_reset.scss | 1267 ++++++++--------- 41 files changed, 1699 insertions(+), 716 deletions(-) create mode 100644 public/static/images/canvas/allocation_delete.svg create mode 100644 public/static/images/canvas/allocation_edit.svg create mode 100644 public/static/images/canvas/allocation_icon01_black.svg create mode 100644 public/static/images/canvas/allocation_icon01_white.svg create mode 100644 public/static/images/canvas/allocation_icon02_black.svg create mode 100644 public/static/images/canvas/allocation_icon02_white.svg create mode 100644 public/static/images/canvas/eaves_icon01.svg create mode 100644 public/static/images/canvas/eaves_icon02.svg create mode 100644 public/static/images/canvas/eaves_icon03.svg create mode 100644 public/static/images/canvas/eaves_icon04.svg create mode 100644 public/static/images/canvas/eaves_icon05.svg create mode 100644 public/static/images/canvas/eaves_icon06.svg create mode 100644 public/static/images/canvas/eaves_icon07.svg create mode 100644 public/static/images/canvas/eaves_icon08.svg create mode 100644 public/static/images/canvas/shape_menu01.svg create mode 100644 public/static/images/canvas/shape_menu02.svg create mode 100644 public/static/images/canvas/shape_menu03.svg create mode 100644 public/static/images/canvas/shape_menu04.svg create mode 100644 public/static/images/canvas/shape_menu05.svg create mode 100644 public/static/images/canvas/shape_menu06.svg create mode 100644 public/static/images/canvas/shape_menu07.svg create mode 100644 public/static/images/canvas/shape_menu08.svg rename src/components/floor-plan/modal/placementShape/{PlacementShapeSetting.js => PlacementShapeSetting.jsx} (100%) create mode 100644 src/components/floor-plan/modal/roofShape/RoofShapeSetting.jsx create mode 100644 src/components/floor-plan/modal/roofShape/type/Direction.jsx create mode 100644 src/components/floor-plan/modal/roofShape/type/Pattern.jsx create mode 100644 src/components/floor-plan/modal/roofShape/type/Ridge.jsx create mode 100644 src/components/floor-plan/modal/roofShape/type/Side.jsx create mode 100644 src/components/floor-plan/modal/roofShape/type/option/Eaves.jsx create mode 100644 src/components/floor-plan/modal/roofShape/type/option/Gable.jsx create mode 100644 src/components/floor-plan/modal/roofShape/type/option/HipAndGable.jsx create mode 100644 src/components/floor-plan/modal/roofShape/type/option/Jerkinhead.jsx create mode 100644 src/components/floor-plan/modal/roofShape/type/option/Shed.jsx create mode 100644 src/components/floor-plan/modal/roofShape/type/option/Wall.jsx diff --git a/public/static/images/canvas/allocation_delete.svg b/public/static/images/canvas/allocation_delete.svg new file mode 100644 index 00000000..711a241e --- /dev/null +++ b/public/static/images/canvas/allocation_delete.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/static/images/canvas/allocation_edit.svg b/public/static/images/canvas/allocation_edit.svg new file mode 100644 index 00000000..795d10f9 --- /dev/null +++ b/public/static/images/canvas/allocation_edit.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/static/images/canvas/allocation_icon01_black.svg b/public/static/images/canvas/allocation_icon01_black.svg new file mode 100644 index 00000000..01829a32 --- /dev/null +++ b/public/static/images/canvas/allocation_icon01_black.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/static/images/canvas/allocation_icon01_white.svg b/public/static/images/canvas/allocation_icon01_white.svg new file mode 100644 index 00000000..5618848e --- /dev/null +++ b/public/static/images/canvas/allocation_icon01_white.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/static/images/canvas/allocation_icon02_black.svg b/public/static/images/canvas/allocation_icon02_black.svg new file mode 100644 index 00000000..57c6173b --- /dev/null +++ b/public/static/images/canvas/allocation_icon02_black.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/static/images/canvas/allocation_icon02_white.svg b/public/static/images/canvas/allocation_icon02_white.svg new file mode 100644 index 00000000..17211b04 --- /dev/null +++ b/public/static/images/canvas/allocation_icon02_white.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/static/images/canvas/eaves_icon01.svg b/public/static/images/canvas/eaves_icon01.svg new file mode 100644 index 00000000..da520069 --- /dev/null +++ b/public/static/images/canvas/eaves_icon01.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/static/images/canvas/eaves_icon02.svg b/public/static/images/canvas/eaves_icon02.svg new file mode 100644 index 00000000..0addbc04 --- /dev/null +++ b/public/static/images/canvas/eaves_icon02.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/static/images/canvas/eaves_icon03.svg b/public/static/images/canvas/eaves_icon03.svg new file mode 100644 index 00000000..b95b49fc --- /dev/null +++ b/public/static/images/canvas/eaves_icon03.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/public/static/images/canvas/eaves_icon04.svg b/public/static/images/canvas/eaves_icon04.svg new file mode 100644 index 00000000..ac08ce05 --- /dev/null +++ b/public/static/images/canvas/eaves_icon04.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/static/images/canvas/eaves_icon05.svg b/public/static/images/canvas/eaves_icon05.svg new file mode 100644 index 00000000..649a6058 --- /dev/null +++ b/public/static/images/canvas/eaves_icon05.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/static/images/canvas/eaves_icon06.svg b/public/static/images/canvas/eaves_icon06.svg new file mode 100644 index 00000000..a2f17801 --- /dev/null +++ b/public/static/images/canvas/eaves_icon06.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/public/static/images/canvas/eaves_icon07.svg b/public/static/images/canvas/eaves_icon07.svg new file mode 100644 index 00000000..1f101d56 --- /dev/null +++ b/public/static/images/canvas/eaves_icon07.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/static/images/canvas/eaves_icon08.svg b/public/static/images/canvas/eaves_icon08.svg new file mode 100644 index 00000000..8a2529de --- /dev/null +++ b/public/static/images/canvas/eaves_icon08.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/public/static/images/canvas/shape_menu01.svg b/public/static/images/canvas/shape_menu01.svg new file mode 100644 index 00000000..6fbe44cc --- /dev/null +++ b/public/static/images/canvas/shape_menu01.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/static/images/canvas/shape_menu02.svg b/public/static/images/canvas/shape_menu02.svg new file mode 100644 index 00000000..36a5eb41 --- /dev/null +++ b/public/static/images/canvas/shape_menu02.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/static/images/canvas/shape_menu03.svg b/public/static/images/canvas/shape_menu03.svg new file mode 100644 index 00000000..ae7701c3 --- /dev/null +++ b/public/static/images/canvas/shape_menu03.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/static/images/canvas/shape_menu04.svg b/public/static/images/canvas/shape_menu04.svg new file mode 100644 index 00000000..e23282c5 --- /dev/null +++ b/public/static/images/canvas/shape_menu04.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/static/images/canvas/shape_menu05.svg b/public/static/images/canvas/shape_menu05.svg new file mode 100644 index 00000000..500d6bc6 --- /dev/null +++ b/public/static/images/canvas/shape_menu05.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/static/images/canvas/shape_menu06.svg b/public/static/images/canvas/shape_menu06.svg new file mode 100644 index 00000000..a13e1b6c --- /dev/null +++ b/public/static/images/canvas/shape_menu06.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/static/images/canvas/shape_menu07.svg b/public/static/images/canvas/shape_menu07.svg new file mode 100644 index 00000000..08119e5e --- /dev/null +++ b/public/static/images/canvas/shape_menu07.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/static/images/canvas/shape_menu08.svg b/public/static/images/canvas/shape_menu08.svg new file mode 100644 index 00000000..4588b3bb --- /dev/null +++ b/public/static/images/canvas/shape_menu08.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/floor-plan/CanvasMenu.jsx b/src/components/floor-plan/CanvasMenu.jsx index 7276805a..b6d2c269 100644 --- a/src/components/floor-plan/CanvasMenu.jsx +++ b/src/components/floor-plan/CanvasMenu.jsx @@ -10,7 +10,7 @@ import QSelectBox from '@/components/common/select/QSelectBox' import { useMessage } from '@/hooks/useMessage' import { usePlan } from '@/hooks/usePlan' import { useSwal } from '@/hooks/useSwal' -import { canvasState, canvasZoomState, currentMenuState, currentCanvasPlanState, verticalHorizontalModeState } from '@/store/canvasAtom' +import { canvasState, canvasZoomState, currentCanvasPlanState, currentMenuState, verticalHorizontalModeState } from '@/store/canvasAtom' import { sessionStore } from '@/store/commonAtom' import { outerLinePointsState } from '@/store/outerLineAtom' import { appMessageStore, globalLocaleStore } from '@/store/localeAtom' @@ -31,7 +31,7 @@ const canvasMenus = [ ] export default function CanvasMenu(props) { - const { setShowCanvasSettingModal, showOutlineModal, setShowOutlineModal, setShowPlaceShapeModal } = props + const { setShowCanvasSettingModal, showOutlineModal, setShowOutlineModal, setShowPlaceShapeModal, setShowRoofShapeSettingModal } = props const [menuNumber, setMenuNumber] = useState(null) const [type, setType] = useState('') @@ -75,6 +75,7 @@ export default function CanvasMenu(props) { const menuProps = { setShowOutlineModal, setShowPlaceShapeModal, + setShowRoofShapeSettingModal, type, } diff --git a/src/components/floor-plan/FloorPlan.jsx b/src/components/floor-plan/FloorPlan.jsx index ee27cbcd..38d31f9d 100644 --- a/src/components/floor-plan/FloorPlan.jsx +++ b/src/components/floor-plan/FloorPlan.jsx @@ -14,12 +14,14 @@ import WallLineSetting from '@/components/floor-plan/modal/outerlinesetting/Wall import PropertiesSetting from '@/components/floor-plan/modal/outerlinesetting/PropertiesSetting' import PlacementShapeSetting from '@/components/floor-plan/modal/placementShape/PlacementShapeSetting' import GridColorSetting from './modal/grid/GridColorSetting' +import RoofShapeSetting from '@/components/floor-plan/modal/roofShape/RoofShapeSetting' export default function FloorPlan() { const [showCanvasSettingModal, setShowCanvasSettingModal] = useState(false) const [showOutlineModal, setShowOutlineModal] = useState(false) const [showPlaceShapeModal, setShowPlaceShapeModal] = useState(false) const [showPropertiesSettingModal, setShowPropertiesSettingModal] = useState(false) + const [showRoofShapeSettingModal, setShowRoofShapeSettingModal] = useState(false) const globalLocaleState = useRecoilValue(globalLocaleStore) const { get } = useAxios(globalLocaleState) @@ -47,6 +49,7 @@ export default function FloorPlan() { showOutlineModal, setShowOutlineModal, setShowPlaceShapeModal, + setShowRoofShapeSettingModal, } useEffect(() => { @@ -106,6 +109,7 @@ export default function FloorPlan() { {showColorPickerModal && } {showPropertiesSettingModal && } {showPlaceShapeModal && } + {showRoofShapeSettingModal && }
diff --git a/src/components/floor-plan/MenuDepth01.jsx b/src/components/floor-plan/MenuDepth01.jsx index 925978e6..43a059f0 100644 --- a/src/components/floor-plan/MenuDepth01.jsx +++ b/src/components/floor-plan/MenuDepth01.jsx @@ -7,7 +7,7 @@ import { currentMenuState } from '@/store/canvasAtom' import { useSetRecoilState } from 'recoil' export default function MenuDepth01(props) { - const { setShowOutlineModal, type, setShowPlaceShapeModal } = props + const { setShowOutlineModal, type, setShowPlaceShapeModal, setShowRoofShapeSettingModal } = props const { getMessage } = useMessage() const [activeMenu, setActiveMenu] = useState() const setCurrentMenu = useSetRecoilState(currentMenuState) @@ -18,6 +18,7 @@ export default function MenuDepth01(props) { if (type === 'outline') { setShowPlaceShapeModal(false) setShowOutlineModal(id === 0) + setShowRoofShapeSettingModal(id === 1) } } diff --git a/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.js b/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx similarity index 100% rename from src/components/floor-plan/modal/placementShape/PlacementShapeSetting.js rename to src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx diff --git a/src/components/floor-plan/modal/roofShape/RoofShapeSetting.jsx b/src/components/floor-plan/modal/roofShape/RoofShapeSetting.jsx new file mode 100644 index 00000000..517a7ba3 --- /dev/null +++ b/src/components/floor-plan/modal/roofShape/RoofShapeSetting.jsx @@ -0,0 +1,58 @@ +import { useMessage } from '@/hooks/useMessage' +import WithDraggable from '@/components/common/draggable/WithDraggable' +import Ridge from '@/components/floor-plan/modal/roofShape/type/Ridge' +import Pattern from '@/components/floor-plan/modal/roofShape/type/Pattern' +import Side from '@/components/floor-plan/modal/roofShape/type/Side' +import { useState } from 'react' +import Image from 'next/image' +import Direction from '@/components/floor-plan/modal/roofShape/type/Direction' + +export default function RoofShapeSetting({ setShowRoofShapeSettingModal }) { + const { getMessage } = useMessage() + const [shapeNum, setShapeNum] = useState(1) + const shapeMenu = [ + { id: 1, name: getMessage('modal.roof.shape.setting.ridge') }, // 용마루 + { id: 2, name: getMessage('modal.roof.shape.setting.patten.a') }, // 패턴A + { id: 3, name: getMessage('modal.roof.shape.setting.patten.b') }, // 패턴B + { id: 4, name: getMessage('modal.roof.shape.setting.side') }, // 변별로 설정 + { id: 5, name: getMessage('commons.west') }, // 서 + { id: 6, name: getMessage('commons.east') }, // 서 + { id: 7, name: getMessage('commons.south') }, // 서 + { id: 8, name: getMessage('commons.north') }, // 북 + ] + + return ( + +
+
+

{getMessage('modal.roof.shape.setting')}

+ +
+
+
+ {shapeMenu.map((item) => ( + + ))} +
+
+
{getMessage('setting')}
+ {shapeNum === 1 && } + {(shapeNum === 2 || shapeNum === 3) && } + {shapeNum === 4 && } + {(shapeNum === 5 || shapeNum === 6 || shapeNum === 7 || shapeNum === 8) && } +
+
+ +
+
+
+
+ ) +} diff --git a/src/components/floor-plan/modal/roofShape/type/Direction.jsx b/src/components/floor-plan/modal/roofShape/type/Direction.jsx new file mode 100644 index 00000000..a02258f9 --- /dev/null +++ b/src/components/floor-plan/modal/roofShape/type/Direction.jsx @@ -0,0 +1,45 @@ +import { useMessage } from '@/hooks/useMessage' + +export default function Direction() { + const { getMessage } = useMessage() + return ( +
+
+ + {getMessage('slope')} + +
+ +
+ {getMessage('size')} +
+
+ + {getMessage('eaves.offset')} + +
+ +
+ mm +
+
+ + {getMessage('gable.offset')} + +
+ +
+ mm +
+
+ + {getMessage('windage.width')} + +
+ +
+ mm +
+
+ ) +} diff --git a/src/components/floor-plan/modal/roofShape/type/Pattern.jsx b/src/components/floor-plan/modal/roofShape/type/Pattern.jsx new file mode 100644 index 00000000..606c154d --- /dev/null +++ b/src/components/floor-plan/modal/roofShape/type/Pattern.jsx @@ -0,0 +1,36 @@ +import { useMessage } from '@/hooks/useMessage' + +export default function Pattern() { + const { getMessage } = useMessage() + return ( +
+
+ + {getMessage('slope')} + +
+ +
+ {getMessage('size')} +
+
+ + {getMessage('eaves.offset')} + +
+ +
+ mm +
+
+ + {getMessage('gable.offset')} + +
+ +
+ mm +
+
+ ) +} diff --git a/src/components/floor-plan/modal/roofShape/type/Ridge.jsx b/src/components/floor-plan/modal/roofShape/type/Ridge.jsx new file mode 100644 index 00000000..7897046c --- /dev/null +++ b/src/components/floor-plan/modal/roofShape/type/Ridge.jsx @@ -0,0 +1,27 @@ +import { useMessage } from '@/hooks/useMessage' + +export default function Ridge() { + const { getMessage } = useMessage() + return ( +
+
+ + {getMessage('slope')} + +
+ +
+ {getMessage('size')} +
+
+ + {getMessage('eaves.offset')} + +
+ +
+ mm +
+
+ ) +} diff --git a/src/components/floor-plan/modal/roofShape/type/Side.jsx b/src/components/floor-plan/modal/roofShape/type/Side.jsx new file mode 100644 index 00000000..3fc1737c --- /dev/null +++ b/src/components/floor-plan/modal/roofShape/type/Side.jsx @@ -0,0 +1,46 @@ +import { useState } from 'react' +import { useMessage } from '@/hooks/useMessage' +import Eaves from '@/components/floor-plan/modal/roofShape/type/option/Eaves' +import Gable from '@/components/floor-plan/modal/roofShape/type/option/Gable' +import HipAndGable from '@/components/floor-plan/modal/roofShape/type/option/HipAndGable' +import Wall from '@/components/floor-plan/modal/roofShape/type/option/Wall' +import Jerkinhead from '@/components/floor-plan/modal/roofShape/type/option/Jerkinhead' +import Shed from '@/components/floor-plan/modal/roofShape/type/option/Shed' + +export default function Side() { + const [buttonAct, setButtonAct] = useState(1) + const { getMessage } = useMessage() + const buttonMenu = [ + { id: 1, name: getMessage('eaves') }, + { id: 2, name: getMessage('gable') }, + { id: 3, name: getMessage('wall') }, + { id: 4, name: getMessage('hipandgable') }, + { id: 5, name: getMessage('jerkinhead') }, + { id: 6, name: getMessage('shed') }, + ] + return ( +
+
+
+ {buttonMenu.map((item) => ( + + ))} +
+
+
+ {buttonAct === 1 && } + {buttonAct === 2 && } + {buttonAct === 3 && } + {buttonAct === 4 && } + {buttonAct === 5 && } + {buttonAct === 6 && } +
+
+ + +
+
+ ) +} diff --git a/src/components/floor-plan/modal/roofShape/type/option/Eaves.jsx b/src/components/floor-plan/modal/roofShape/type/option/Eaves.jsx new file mode 100644 index 00000000..303dad55 --- /dev/null +++ b/src/components/floor-plan/modal/roofShape/type/option/Eaves.jsx @@ -0,0 +1,27 @@ +import { useMessage } from '@/hooks/useMessage' + +export default function Eaves() { + const { getMessage } = useMessage() + return ( + <> +
+ + {getMessage('slope')} + +
+ +
+ {getMessage('size')} +
+
+ + {getMessage('eaves.offset')} + +
+ +
+ mm +
+ + ) +} diff --git a/src/components/floor-plan/modal/roofShape/type/option/Gable.jsx b/src/components/floor-plan/modal/roofShape/type/option/Gable.jsx new file mode 100644 index 00000000..63092550 --- /dev/null +++ b/src/components/floor-plan/modal/roofShape/type/option/Gable.jsx @@ -0,0 +1,16 @@ +import { useMessage } from '@/hooks/useMessage' + +export default function Gable() { + const { getMessage } = useMessage() + return ( + <> +
+ {getMessage('gable.offset')} +
+ +
+ mm +
+ + ) +} diff --git a/src/components/floor-plan/modal/roofShape/type/option/HipAndGable.jsx b/src/components/floor-plan/modal/roofShape/type/option/HipAndGable.jsx new file mode 100644 index 00000000..ed2e1b60 --- /dev/null +++ b/src/components/floor-plan/modal/roofShape/type/option/HipAndGable.jsx @@ -0,0 +1,36 @@ +import { useMessage } from '@/hooks/useMessage' + +export default function HipAndGable() { + const { getMessage } = useMessage() + return ( + <> +
+ + {getMessage('slope')} + +
+ +
+ {getMessage('size')} +
+
+ + {getMessage('eaves.offset')} + +
+ +
+ mm +
+
+ + {getMessage('gable.offset')} + +
+ +
+ mm +
+ + ) +} diff --git a/src/components/floor-plan/modal/roofShape/type/option/Jerkinhead.jsx b/src/components/floor-plan/modal/roofShape/type/option/Jerkinhead.jsx new file mode 100644 index 00000000..d5737470 --- /dev/null +++ b/src/components/floor-plan/modal/roofShape/type/option/Jerkinhead.jsx @@ -0,0 +1,36 @@ +import { useMessage } from '@/hooks/useMessage' + +export default function Jerkinhead() { + const { getMessage } = useMessage() + return ( + <> +
+ + {getMessage('gable.offset')} + +
+ +
+ mm +
+
+ + {getMessage('jerkinhead.width')} + +
+ +
+ mm +
+
+ + {getMessage('jerkinhead.slope')} + +
+ +
+ {getMessage('size')} +
+ + ) +} diff --git a/src/components/floor-plan/modal/roofShape/type/option/Shed.jsx b/src/components/floor-plan/modal/roofShape/type/option/Shed.jsx new file mode 100644 index 00000000..c4fd4333 --- /dev/null +++ b/src/components/floor-plan/modal/roofShape/type/option/Shed.jsx @@ -0,0 +1,16 @@ +import { useMessage } from '@/hooks/useMessage' + +export default function Shed() { + const { getMessage } = useMessage() + return ( + <> +
+ {getMessage('shed.width')} +
+ +
+ mm +
+ + ) +} diff --git a/src/components/floor-plan/modal/roofShape/type/option/Wall.jsx b/src/components/floor-plan/modal/roofShape/type/option/Wall.jsx new file mode 100644 index 00000000..6b716892 --- /dev/null +++ b/src/components/floor-plan/modal/roofShape/type/option/Wall.jsx @@ -0,0 +1,38 @@ +import { useState } from 'react' +import { useMessage } from '@/hooks/useMessage' + +export default function Wall() { + const [hasSleeve, setHasSleeve] = useState('0') + const { getMessage } = useMessage() + return ( + <> + {hasSleeve} +
+
+
+
+ setHasSleeve(e.target.value)} /> + +
+
+
+
+
+
+ setHasSleeve(e.target.value)} /> + +
+
+
+
+
+ +
+ mm +
+
+
+
+ + ) +} diff --git a/src/locales/ja.json b/src/locales/ja.json index a1285410..71c6fb8d 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -31,6 +31,11 @@ "modal.placement.initial.setting.roof.material": "屋根材の選択(単位:mm)", "modal.placement.initial.setting.roof.material.info": "対応可能な屋根材や足場は限定されますので、必ず事前マニュアルをご確認ください。", "modal.placement.initial.setting.rafter": "垂木の間隔", + "modal.roof.shape.setting": "屋根形状の設定", + "modal.roof.shape.setting.ridge": "龍丸屋根", + "modal.roof.shape.setting.patten.a": "Aパターン", + "modal.roof.shape.setting.patten.b": "Bパターン", + "modal.roof.shape.setting.side": "別に設定", "plan.menu.roof.cover": "지붕덮개", "plan.menu.roof.cover.outline.drawing": "外壁線を描", "plan.menu.roof.cover.roof.shape.setting": "屋根形状設定", @@ -49,6 +54,7 @@ "modal.cover.outline.fix": "外壁線確定", "modal.cover.outline.rollback": "一変戦に戻る", "modal.cover.outline.finish": "設定完了", + "common.setting.finish": "設定完了", "modal.cover.outline.remove": "外壁の削除", "modal.cover.outline.select.move": "外壁の選択、移動", "plan.menu.roof.cover.roof.setting": "屋根形状設定", @@ -226,6 +232,10 @@ "common.message.password.init.success": "パスワード [{0}] に初期化されました。", "common.message.no.edit.save": "この文書は変更できません。", "common.require": "필수", + "commons.west": "立つ", + "commons.east": "ドン", + "commons.south": "M", + "commons.north": "北", "site.name": "Q.CAST III", "site.sub_name": "태양광 발전 시스템 도면관리 사이트", "login": "로그인", @@ -284,5 +294,22 @@ "stuff.gridHeader.dispCompanyName": "견적처", "stuff.gridHeader.receiveUser": "담당자", "stuff.gridHeader.specDate": "사양확인", - "stuff.gridHeader.createDatetime": "등록일" + "stuff.gridHeader.createDatetime": "등록일", + "slope": "傾斜", + "eaves.offset": "軒の", + "gable.offset": "ケラバ出幅", + "size": "寸", + "eaves": "軒", + "gable": "ケラバ", + "wall": "壁", + "hipandgable": "八作屋根", + "jerkinhead": "半折", + "shed": "片側の流れ", + "apply": "適用", + "has.sleeve": "袖あり", + "has.not.sleeve": "袖なし", + "jerkinhead.width": "半折先幅", + "jerkinhead.slope": "半折先傾斜", + "shed.width": "片流幅", + "windage.width": "漂流の出幅" } diff --git a/src/locales/ko.json b/src/locales/ko.json index cc84b9d7..572c1eef 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -31,6 +31,11 @@ "modal.placement.initial.setting.roof.material": "지붕재 선택(단위: mm)", "modal.placement.initial.setting.roof.material.info": "대응 가능한 지붕재 및 발판은 한정되므로 반드시 사전 매뉴얼을 확인하십시오.", "modal.placement.initial.setting.rafter": "서까래", + "modal.roof.shape.setting": "지붕형상 설정", + "modal.roof.shape.setting.ridge": "용마루", + "modal.roof.shape.setting.patten.a": "A 패턴", + "modal.roof.shape.setting.patten.b": "A 패턴", + "modal.roof.shape.setting.side": "변별로 설정", "plan.menu.roof.cover": "지붕덮개", "plan.menu.roof.cover.outline.drawing": "외벽선 그리기", "plan.menu.roof.cover.roof.shape.setting": "지붕형상 설정", @@ -53,6 +58,8 @@ "modal.cover.outline.fix": "외벽선 확정", "modal.cover.outline.rollback": "일변전으로 돌아가기", "modal.cover.outline.finish": "설정완료", + "common.setting.finish": "설정완료", + "common.setting.rollback": "일변전으로 돌아가기", "modal.cover.outline.remove": "외벽 제거", "modal.cover.outline.select.move": "외벽 선택, 이동", "plan.menu.placement.surface": "배치면", @@ -227,6 +234,10 @@ "common.message.password.init.success": "비밀번호 [{0}]로 초기화 되었습니다.", "common.message.no.edit.save": "This document cannot be changed.", "common.require": "필수", + "commons.west": "서", + "commons.east": "동", + "commons.south": "남", + "commons.north": "북", "site.name": "Q.CAST III", "site.sub_name": "태양광 발전 시스템 도면관리 사이트", "login": "로그인", @@ -285,5 +296,22 @@ "stuff.gridHeader.dispCompanyName": "견적처", "stuff.gridHeader.receiveUser": "담당자", "stuff.gridHeader.specDate": "사양확인", - "stuff.gridHeader.createDatetime": "등록일" + "stuff.gridHeader.createDatetime": "등록일", + "slope": "경사", + "eaves.offset": "처마 출폭", + "gable.offset": "케라바 출폭", + "size": "치수", + "eaves": "처마", + "gable": "케라바", + "wall": "벽", + "hipandgable": "팔작지붕", + "jerkinhead": "반절처", + "shed": "한쪽흐름", + "apply": "적용", + "has.sleeve": "소매 있음", + "has.not.sleeve": "소매 없음", + "jerkinhead.width": "반절처 폭", + "jerkinhead.slope": "반절처 경사", + "shed.width": "한쪽흐름 폭", + "windage.width": "편류의 출폭" } diff --git a/src/styles/_modal.scss b/src/styles/_modal.scss index 88b63229..30e19173 100644 --- a/src/styles/_modal.scss +++ b/src/styles/_modal.scss @@ -29,7 +29,6 @@ $alert-color: #101010; .modal-pop-wrap { position: fixed; width: 100%; - min-width: 300px; height: -webkit-fit-content; height: -moz-fit-content; height: fit-content; @@ -37,7 +36,26 @@ $alert-color: #101010; border-radius: 4px; background-color: #272727; z-index: 9999999; - overflow: hidden; + + &.xxxm { + width: 230px; + } + + &.xxm { + width: 270px; + } + + &.xm { + width: 300px; + } + + &.ssm { + width: 380px; + } + + &.sm { + width: 580px; + } &.r { width: 400px; @@ -47,16 +65,12 @@ $alert-color: #101010; width: 440px; } - &.sm { - width: 580px; + &.ml { + width: 530px; } - &.ssm { - width: 380px; - } - - &.xm { - width: 300px; + &.l-2 { + width: 640px; } &.l { @@ -112,7 +126,7 @@ $alert-color: #101010; align-items: center; padding: 10px 24px; background-color: #000; - + // overflow: hidden; h1.title { font-size: 13px; color: $pop-color; @@ -121,8 +135,8 @@ $alert-color: #101010; .modal-close { margin-left: auto; - color: $pop-color; - text-indent: -999999999px; + color: transparent; + font-size: 0; width: 10px; height: 10px; background: url(../../public/static/images/canvas/modal_close.svg) no-repeat center; @@ -146,6 +160,8 @@ $alert-color: #101010; flex: 1 1 auto; padding: 0; } + + margin-bottom: 14px; } } @@ -253,21 +269,18 @@ $alert-color: #101010; 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; + background-color: transparent; + border: 1px solid #3D3D3D; border-radius: 2px; - padding: 10px; + padding: 15px 10px; gap: 20px; - margin-bottom: 5px; + margin-bottom: 10px; .grid-input-form { display: flex; @@ -301,6 +314,29 @@ $alert-color: #101010; .sort-select { width: 100%; background-color: #313131; + min-width: auto; + font-size: 12px; + border: none; + + p { + font-size: 12px; + } + + > ul { + border: none; + } + } + + &.right { + p { + text-align: right; + } + + ul { + li { + justify-content: flex-end; + } + } } } @@ -309,7 +345,7 @@ $alert-color: #101010; text-align: right; button { - padding: 0 20px; + padding: 0 10px; } } @@ -319,7 +355,7 @@ $alert-color: #101010; color: $pop-color; font-weight: $pop-normal-weight; padding-bottom: 15px; - border-bottom: 1px solid #3C3C3C; + } .grid-direction { @@ -501,7 +537,6 @@ $alert-color: #101010; .outline-wrap { padding: 24px 0; border-top: 1px solid #424242; - border-bottom: 1px solid #424242; .outline-inner { display: flex; @@ -513,15 +548,19 @@ $alert-color: #101010; } .outline-form { - width: 50%; + // width: 50%; + margin-right: 15px; } } + + &:last-child { + border-bottom: 1px solid #424242; + } } .outline-form { display: flex; align-items: center; - margin-right: 15px; span { width: 60px; @@ -532,7 +571,9 @@ $alert-color: #101010; margin-right: 10px; &.thin { - font-weight: & $pop-normal-weight; + width: auto; + font-weight: $pop-normal-weight; + margin-right: 0; } } @@ -591,18 +632,18 @@ $alert-color: #101010; margin-bottom: 14px; } +.setting-tit { + font-size: 13px; + color: $pop-color; + font-weight: $pop-bold-weight; + margin-bottom: 10px; +} + .properties-setting-wrap { &.outer { margin-top: 24px; } - .setting-tit { - font-size: 13px; - color: $pop-color; - font-weight: $pop-bold-weight; - margin-bottom: 10px; - } - .setting-btn-wrap { display: flex; align-items: center; @@ -692,9 +733,212 @@ $alert-color: #101010; border-bottom: 1px solid #424242; } +.padding-form { + padding-left: 23px; +} + .discrimination-box { padding: 16px 12px; border: 1px solid #3D3D3D; border-radius: 2px; - margin-top: 14px; +} + +.modal-bottom-border-bx { + margin-top: 24px; + padding-bottom: 14px; + border-bottom: 1px solid #424242; +} + +// 처마∙케라바 변경 +.eaves-keraba-table { + display: table; + border-collapse: collapse; + + .eaves-keraba-item { + display: table-row; + + .eaves-keraba-th, + .eaves-keraba-td { + font-size: $pop-normal-size; + color: $pop-color; + font-weight: $pop-normal-weight; + display: table-cell; + vertical-align: middle; + padding-bottom: 14px; + } + + .eaves-keraba-td { + padding-left: 15px; + } + + .eaves-keraba-ico { + display: flex; + align-items: center; + justify-content: center; + padding: 5px; + background-color: #3D3D3D; + border: 1px solid #3D3D3D; + border-radius: 2px; + + &.act { + border: 1px solid #ED0004; + } + } + + &:last-child { + .eaves-keraba-th, + .eaves-keraba-td { + padding-bottom: 0; + } + } + } +} + +.guide { + font-size: $pop-normal-size; + font-weight: $pop-normal-weight; + color: $pop-color; + margin-bottom: 24px; + + &.sm { + margin-bottom: 15px; + } +} + +// 지붕면 할당 +.allocation-select-wrap { + display: flex; + align-items: center; + padding-bottom: 14px; + border-bottom: 1px solid #424242; + margin-bottom: 14px; + + span { + font-size: $pop-normal-size; + color: $pop-color; + font-weight: $pop-bold-weight; + margin-right: 10px; + } + + .allocation-edit { + display: flex; + align-items: center; + height: 30px; + padding: 0 10px; + margin-left: 5px; + font-size: $pop-normal-size; + color: $pop-color; + font-weight: $pop-normal-weight; + border: 1px solid #484848; + background-color: #323234; + + i { + display: block; + width: 12px; + height: 12px; + margin-right: 5px; + background: url(../../public/static/images/canvas/allocation_edit.svg) no-repeat center; + background-size: cover; + } + } +} + +.block-box { + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 10px; + + .flex-ment { + gap: 10px; + + .dec { + text-decoration: underline; + } + + .delete { + display: block; + width: 15px; + height: 15px; + background: url(../../public/static/images/canvas/allocation_delete.svg) no-repeat center; + background-size: cover; + } + } + + &:last-child { + margin-bottom: 0; + } +} + +.icon-btn-wrap { + flex: 1; + display: flex; + align-items: center; + gap: 5px; + + button { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 30px; + font-size: $pop-normal-size; + font-weight: $pop-normal-weight; + color: $pop-color; + border: 1px solid #646464; + border-radius: 2px; + padding: 0 10px; + transition: all .15s ease-in-out; + + i { + height: 15px; + display: block; + margin-left: 10px; + background-repeat: no-repeat; + background-position: center; + background-size: cover; + transition: all .15s ease-in-out; + + &.allocation01 { + background-image: url(../../public/static/images/canvas/allocation_icon01_white.svg); + width: 15px; + } + + &.allocation02 { + background-image: url(../../public/static/images/canvas/allocation_icon02_white.svg); + width: 18px; + } + } + + &.act, + &:hover { + color: #101010; + border: 1px solid #101010; + background-color: #fff; + + i { + &.allocation01 { + background-image: url(../../public/static/images/canvas/allocation_icon01_black.svg); + } + + &.allocation02 { + background-image: url(../../public/static/images/canvas/allocation_icon02_black.svg); + } + } + } + } +} + +// 경사설정 +.slope-wrap { + padding-bottom: 24px; + border-bottom: 1px solid #424242; +} + +// 면형상 배치 +.roof-shape-menu { + &.plane { + grid-template-columns: 1fr 1fr 1fr 1fr 1fr; + grid-template-rows: 1fr 1fr 1fr; + } } \ No newline at end of file diff --git a/src/styles/_reset.scss b/src/styles/_reset.scss index dcf0c0d2..6d899458 100644 --- a/src/styles/_reset.scss +++ b/src/styles/_reset.scss @@ -1,21 +1,18 @@ * { - -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, @@ -25,777 +22,693 @@ 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; -} - -body:first-of-type caption { - display: none; + line-height: 1.4; } +body:first-of-type caption { display:none;} ol, ul { - list-style: none; + list-style: none; } - blockquote, q { - quotes: none; + quotes: none; } - blockquote:before, blockquote:after, q:before, q:after { - content: ''; - content: none; + content: ''; + content: none; } - table { - width: 100%; - border-collapse: separate; - border-spacing: 0; - border: 0 none; + width: 100%; + border-collapse: separate; + border-spacing:0; + border:0 none; } - caption, th, td { - text-align: left; - font-weight: normal; - border: 0; + text-align:left; + font-weight: normal; + border:0; } -a { - cursor: pointer; - color: #000; +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; -} +.mt5{margin-top: 5px !important;} +.mt10{margin-top: 10px !important;} +.mt15{margin-top: 15px !important;} +.mb5{margin-bottom: 5px !important;} +.mb10{margin-bottom: 10px !important;} +.mb15{margin-bottom: 15px !important;} +.mr5{margin-right: 5px !important;} +.mr10{margin-right: 10px !important;} +.mr15{margin-right: 15px !important;} +.ml5{margin-left: 5px !important;} +.ml10{margin-left: 10px !important;} +.ml15{margin-left: 15px !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; - } - } - - &.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; +.btn-frame{ + display: inline-block; + padding: 0 7px; + height: 34px; + line-height: 34px; + border-radius: 2px; color: #fff; - font-weight: 500; - } - - &.block { - display: block; - width: 100%; - } - - &.ico-flx { - display: flex; - align-items: center; - - .ico { - margin-right: 10px; + 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; + } + } + &.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 { - font-weight: 400; + &.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; +.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; + &.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; - 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; +.sort-select{ + position: relative; + display: inline-block; + min-width: 100px; + height: 30px; + line-height: 30px; + padding: 0 25px 0 10px; 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; + border-top-left-radius: 2px; + color: #fff; + cursor: pointer; + p{ + font-size: 13px; color: #fff; - line-height: 1.4; - } - - &:hover { - background-color: #2C2C2C; - } + height: 100%; } - } - - &::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; + .select-item-wrap{ + position: absolute; + top: 100%; + left: -1px; + clip-path:inset(0 0 100% 0); + width: calc(100% + 2px); + padding: 8px 0; + max-height: 100px; + overflow-y: auto; + background-color: #373737; + border: 1px solid #3F3F3F; + border-radius: 2px; + transition: all 0.17s ease-in-out; + visibility: hidden; + z-index: 999; + .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; + } + } + &::-webkit-scrollbar { + width: 2px; + background-color: transparent; + + } + &::-webkit-scrollbar-thumb { + background-color: #5a5a5a; + border-radius: 10px; + } + &::-webkit-scrollbar-track { + background-color: transparent; + } } - - &:after { - transform: translateY(-50%) rotate(-180deg); + &::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); + } } - } } -.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; - } +.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; +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%; + } + &:read-only{ + color: #AAA; + } } - - &.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; + } } - } - - &.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; - } - - .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; +.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 { - color: #101010; - font-weight: 600; + .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; + } } - } } // 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: #1083E3; - border: 1px solid #1083E3; + &.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; + } } - } } // radio .d-check-radio, -.d-check-box { - line-height: 1.1; - cursor: pointer; - - input[type=checkbox], - input[type=radio] { - position: static; - margin-left: 0; +.d-check-box{ + line-height: 1.1; cursor: pointer; - 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; + input[type=checkbox], + input[type=radio]{ + position: static; + margin-left: 0; + cursor: pointer; + opacity: 0; + z-index: 1; + flex: 0 0 auto; } - } - - &.no-text { - label { - padding-left: 0; + 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; + } } - } } .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; + 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; + } } - - &::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; + &.light{ + label{ + &:before{ + border-color: #D6D6D7; + } + &:after{ + background-color: #697C8F; + } + } } - } - - &.light { - label { - &:before { - border-color: #D6D6D7; - } - - &:after { - background-color: #697C8F; - } + input[type=radio]:checked + label::after{ + opacity: 1; + visibility: visible; } - } - - 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; - } + &.pop{ + label{ + font-size: 12px; + &: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; +.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; + } } - - &: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; + 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); } - } - - 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; + &.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