diff --git a/src/main/bundles/dev.bundle b/src/main/bundles/dev.bundle index 0a446ca..87e07d2 100644 Binary files a/src/main/bundles/dev.bundle and b/src/main/bundles/dev.bundle differ diff --git a/src/main/frontend/invoice-generator/profile-invoice-generator.js b/src/main/frontend/invoice-generator/profile-invoice-generator.js index 2bb3fa1..dc399d4 100644 --- a/src/main/frontend/invoice-generator/profile-invoice-generator.js +++ b/src/main/frontend/invoice-generator/profile-invoice-generator.js @@ -310,12 +310,68 @@ window.initProfileInvoiceGenerator = function() { return x >= ex && x <= ex + ew && y >= ey && y <= ey + eh; } + // Resizing state + var isResizing = false; + var resizeHandle = -1; + var resizeStart = { x: 0, y: 0, width: 0, height: 0 }; + + // Helper function to check if a point is on a resize handle + function getResizeHandle(x, y, el) { + if (!el || el.isStatic) return -1; + + var ex = pageX + (el.x * zoomFactor); + var ey = pageY + (el.y * zoomFactor); + var ew = (el.width || 100) * zoomFactor; + var eh = (el.height || 30) * zoomFactor; + var hs = Math.max(6, 8 * zoomFactor); + var halfHs = hs / 2; + + // Handle positions: 0=TL, 1=TC, 2=TR, 3=ML, 4=MR, 5=BL, 6=BC, 7=BR + var handles = [ + { x: ex - halfHs, y: ey - halfHs }, + { x: ex + ew/2 - halfHs, y: ey - halfHs }, + { x: ex + ew - halfHs, y: ey - halfHs }, + { x: ex - halfHs, y: ey + eh/2 - halfHs }, + { x: ex + ew - halfHs, y: ey + eh/2 - halfHs }, + { x: ex - halfHs, y: ey + eh - halfHs }, + { x: ex + ew/2 - halfHs, y: ey + eh - halfHs }, + { x: ex + ew - halfHs, y: ey + eh - halfHs } + ]; + + for (var i = 0; i < handles.length; i++) { + if (x >= handles[i].x && x <= handles[i].x + hs && + y >= handles[i].y && y <= handles[i].y + hs) { + return i; + } + } + return -1; + } + // Mouse events canvas.addEventListener('mousedown', function(e) { var rect = canvas.getBoundingClientRect(); var x = e.clientX - rect.left; var y = e.clientY - rect.top; + // Check if clicking on a resize handle of selected element + if (selectedElement) { + var handle = getResizeHandle(x, y, selectedElement); + if (handle >= 0) { + isResizing = true; + resizeHandle = handle; + resizeStart = { + x: x, + y: y, + width: selectedElement.width || 100, + height: selectedElement.height || 30, + elemX: selectedElement.x, + elemY: selectedElement.y + }; + e.preventDefault(); + return; + } + } + // Check if clicking on an element var clickedElement = null; for (var i = elements.length - 1; i >= 0; i--) { @@ -345,7 +401,62 @@ window.initProfileInvoiceGenerator = function() { var x = e.clientX - rect.left; var y = e.clientY - rect.top; - if (isDragging && selectedElement) { + if (isResizing && selectedElement) { + // Calculate delta in base coordinates + var dx = (x - resizeStart.x) / zoomFactor; + var dy = (y - resizeStart.y) / zoomFactor; + + var newWidth = resizeStart.width; + var newHeight = resizeStart.height; + var newX = resizeStart.elemX; + var newY = resizeStart.elemY; + + // Handle indices: 0=TL, 1=TC, 2=TR, 3=ML, 4=MR, 5=BL, 6=BC, 7=BR + switch(resizeHandle) { + case 0: // Top-left + newWidth = Math.max(20, resizeStart.width - dx); + newHeight = Math.max(20, resizeStart.height - dy); + newX = resizeStart.elemX + (resizeStart.width - newWidth); + newY = resizeStart.elemY + (resizeStart.height - newHeight); + break; + case 1: // Top-center + newHeight = Math.max(20, resizeStart.height - dy); + newY = resizeStart.elemY + (resizeStart.height - newHeight); + break; + case 2: // Top-right + newWidth = Math.max(20, resizeStart.width + dx); + newHeight = Math.max(20, resizeStart.height - dy); + newY = resizeStart.elemY + (resizeStart.height - newHeight); + break; + case 3: // Middle-left + newWidth = Math.max(20, resizeStart.width - dx); + newX = resizeStart.elemX + (resizeStart.width - newWidth); + break; + case 4: // Middle-right + newWidth = Math.max(20, resizeStart.width + dx); + break; + case 5: // Bottom-left + newWidth = Math.max(20, resizeStart.width - dx); + newHeight = Math.max(20, resizeStart.height + dy); + newX = resizeStart.elemX + (resizeStart.width - newWidth); + break; + case 6: // Bottom-center + newHeight = Math.max(20, resizeStart.height + dy); + break; + case 7: // Bottom-right + newWidth = Math.max(20, resizeStart.width + dx); + newHeight = Math.max(20, resizeStart.height + dy); + break; + } + + selectedElement.width = snapToGrid(newWidth); + selectedElement.height = snapToGrid(newHeight); + selectedElement.x = snapToGrid(newX); + selectedElement.y = snapToGrid(newY); + + draw(); + notifyElementSelected(selectedElement); + } else if (isDragging && selectedElement) { // Adjust mouse movement by zoom factor to get stored coordinates var dx = (x - dragStart.x) / zoomFactor; var dy = (y - dragStart.y) / zoomFactor; @@ -364,25 +475,47 @@ window.initProfileInvoiceGenerator = function() { draw(); notifyElementSelected(selectedElement); } else { - // Update cursor - var hovering = false; - for (var i = elements.length - 1; i >= 0; i--) { - if (hitTest(x, y, elements[i])) { - hovering = true; - break; + // Update cursor based on resize handles or element hover + if (selectedElement && !selectedElement.isStatic) { + var handle = getResizeHandle(x, y, selectedElement); + if (handle >= 0) { + // Set cursor based on handle type + var cursors = ['nwse-resize', 'ns-resize', 'nesw-resize', 'ew-resize', 'ew-resize', 'nesw-resize', 'ns-resize', 'nwse-resize']; + canvas.style.cursor = cursors[handle]; + } else { + var hovering = false; + for (var i = elements.length - 1; i >= 0; i--) { + if (hitTest(x, y, elements[i])) { + hovering = true; + break; + } + } + canvas.style.cursor = hovering ? 'move' : 'default'; } + } else { + var hovering = false; + for (var i = elements.length - 1; i >= 0; i--) { + if (hitTest(x, y, elements[i])) { + hovering = true; + break; + } + } + canvas.style.cursor = hovering ? 'move' : 'default'; } - canvas.style.cursor = hovering ? 'move' : 'default'; } }); canvas.addEventListener('mouseup', function() { isDragging = false; + isResizing = false; + resizeHandle = -1; canvas.style.cursor = 'default'; }); canvas.addEventListener('mouseleave', function() { isDragging = false; + isResizing = false; + resizeHandle = -1; canvas.style.cursor = 'default'; });