Merge remote-tracking branch 'origin/main-dev' into main-demo
This commit is contained in:
@@ -92,7 +92,7 @@ const DashboardEditor: React.FC = () => {
|
|||||||
setSelectedBlock(null);
|
setSelectedBlock(null);
|
||||||
setSelectedElement(null);
|
setSelectedElement(null);
|
||||||
}
|
}
|
||||||
}, [editMode])
|
}, [editMode]);
|
||||||
|
|
||||||
// Helper function to send updates to backend - only sends the specific block that changed
|
// Helper function to send updates to backend - only sends the specific block that changed
|
||||||
const updateBackend = async (updatedBlock: Block) => {
|
const updateBackend = async (updatedBlock: Block) => {
|
||||||
@@ -210,12 +210,20 @@ const DashboardEditor: React.FC = () => {
|
|||||||
|
|
||||||
if (blockElement && elementToDrag) {
|
if (blockElement && elementToDrag) {
|
||||||
const blockRect = blockElement.getBoundingClientRect();
|
const blockRect = blockElement.getBoundingClientRect();
|
||||||
|
const elementRect = elementToDrag.getBoundingClientRect();
|
||||||
const newX = e.clientX - blockRect.left - elementDragOffset.x;
|
const newX = e.clientX - blockRect.left - elementDragOffset.x;
|
||||||
const newY = e.clientY - blockRect.top - elementDragOffset.y;
|
const newY = e.clientY - blockRect.top - elementDragOffset.y;
|
||||||
|
|
||||||
// Direct DOM manipulation
|
// Constrain element to stay within block bounds using actual element dimensions
|
||||||
elementToDrag.style.left = `${Math.max(0, Math.min(blockRect.width - 50, newX))}px`;
|
// Add 20px padding on all sides to match the padding used in calculateMinBlockSize
|
||||||
elementToDrag.style.top = `${Math.max(0, Math.min(blockRect.height - 30, newY))}px`;
|
const minX = 20; // Left padding
|
||||||
|
const minY = 20; // Top padding
|
||||||
|
const maxX = blockRect.width - elementRect.width - 20; // Right padding
|
||||||
|
const maxY = blockRect.height - elementRect.height - 20; // Bottom padding
|
||||||
|
|
||||||
|
// Direct DOM manipulation with padding on all sides
|
||||||
|
elementToDrag.style.left = `${Math.max(minX, Math.min(maxX, newX))}px`;
|
||||||
|
elementToDrag.style.top = `${Math.max(minY, Math.min(maxY, newY))}px`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,9 +254,28 @@ const DashboardEditor: React.FC = () => {
|
|||||||
const newWidth = Math.max(100, resizeStart.width + deltaX);
|
const newWidth = Math.max(100, resizeStart.width + deltaX);
|
||||||
const newHeight = Math.max(50, resizeStart.height + deltaY);
|
const newHeight = Math.max(50, resizeStart.height + deltaY);
|
||||||
|
|
||||||
// Direct DOM manipulation
|
// Constrain element to block bounds
|
||||||
elementToResize.style.width = `${newWidth}px`;
|
const blockElement = document.querySelector(`[data-block-id="${selectedBlock}"]`) as HTMLElement;
|
||||||
elementToResize.style.height = `${newHeight}px`;
|
if (blockElement) {
|
||||||
|
const blockRect = blockElement.getBoundingClientRect();
|
||||||
|
const elementLeft = elementToResize.offsetLeft;
|
||||||
|
const elementTop = elementToResize.offsetTop;
|
||||||
|
|
||||||
|
// Calculate max allowed dimensions (block size - element position - padding)
|
||||||
|
const maxAllowedWidth = blockRect.width - elementLeft - 20;
|
||||||
|
const maxAllowedHeight = blockRect.height - elementTop - 20;
|
||||||
|
|
||||||
|
const constrainedWidth = Math.min(newWidth, maxAllowedWidth);
|
||||||
|
const constrainedHeight = Math.min(newHeight, maxAllowedHeight);
|
||||||
|
|
||||||
|
// Direct DOM manipulation
|
||||||
|
elementToResize.style.width = `${constrainedWidth}px`;
|
||||||
|
elementToResize.style.height = `${constrainedHeight}px`;
|
||||||
|
} else {
|
||||||
|
// Fallback if block not found (unlikely)
|
||||||
|
elementToResize.style.width = `${newWidth}px`;
|
||||||
|
elementToResize.style.height = `${newHeight}px`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (resizingBlock) {
|
} else if (resizingBlock) {
|
||||||
const blockToResize = document.querySelector(`[data-block-id="${resizingBlock}"]`) as HTMLElement;
|
const blockToResize = document.querySelector(`[data-block-id="${resizingBlock}"]`) as HTMLElement;
|
||||||
@@ -257,10 +284,32 @@ const DashboardEditor: React.FC = () => {
|
|||||||
const deltaY = e.clientY - resizeStart.y;
|
const deltaY = e.clientY - resizeStart.y;
|
||||||
|
|
||||||
const currentBlock = blocks.find((b) => b.blockUuid === resizingBlock);
|
const currentBlock = blocks.find((b) => b.blockUuid === resizingBlock);
|
||||||
const minSize = currentBlock ? calculateMinBlockSize(currentBlock) : { width: 100, height: 50 };
|
|
||||||
|
|
||||||
const newWidth = Math.max(minSize.width, resizeStart.width + deltaX);
|
// Calculate minimum size based on ALL elements in the block
|
||||||
const newHeight = Math.max(minSize.height, resizeStart.height + deltaY);
|
const minSize = currentBlock ? calculateMinBlockSize(currentBlock) : { width: 100, height: 50 };
|
||||||
|
const minWidth = minSize.width;
|
||||||
|
const minHeight = minSize.height;
|
||||||
|
|
||||||
|
let newWidth = Math.max(minWidth, resizeStart.width + deltaX);
|
||||||
|
let newHeight = Math.max(minHeight, resizeStart.height + deltaY);
|
||||||
|
|
||||||
|
// Constrain block to editor bounds if it has absolute or fixed positioning
|
||||||
|
if (currentBlock && (currentBlock.positionType === "absolute" || currentBlock.positionType === "fixed")) {
|
||||||
|
const editorElement = editorRef.current;
|
||||||
|
if (editorElement) {
|
||||||
|
const editorRect = editorElement.getBoundingClientRect();
|
||||||
|
const blockX = currentBlock.position?.x || 0;
|
||||||
|
const blockY = currentBlock.position?.y || 0;
|
||||||
|
|
||||||
|
// Calculate max allowed dimensions based on position
|
||||||
|
const maxAllowedWidth = editorRect.width - blockX;
|
||||||
|
const maxAllowedHeight = editorRect.height - blockY;
|
||||||
|
|
||||||
|
// Constrain width and height to editor bounds, but not below minimum
|
||||||
|
newWidth = Math.max(minWidth, Math.min(newWidth, maxAllowedWidth));
|
||||||
|
newHeight = Math.max(minHeight, Math.min(newHeight, maxAllowedHeight));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Direct DOM manipulation
|
// Direct DOM manipulation
|
||||||
blockToResize.style.width = `${newWidth}px`;
|
blockToResize.style.width = `${newWidth}px`;
|
||||||
@@ -271,6 +320,8 @@ const DashboardEditor: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleMouseUp = async (): Promise<void> => {
|
const handleMouseUp = async (): Promise<void> => {
|
||||||
|
let blockToUpdate: Block | undefined;
|
||||||
|
|
||||||
// Update backend using peek methods, then update state from response
|
// Update backend using peek methods, then update state from response
|
||||||
if (draggingElement && selectedBlock && currentElement?.positionType === "absolute") {
|
if (draggingElement && selectedBlock && currentElement?.positionType === "absolute") {
|
||||||
const blockElement = document.querySelector(`[data-block-id="${selectedBlock}"]`) as HTMLElement;
|
const blockElement = document.querySelector(`[data-block-id="${selectedBlock}"]`) as HTMLElement;
|
||||||
@@ -283,15 +334,10 @@ const DashboardEditor: React.FC = () => {
|
|||||||
|
|
||||||
// Use peek to get updated blocks and send only the affected block to backend
|
// Use peek to get updated blocks and send only the affected block to backend
|
||||||
const updatedBlocks = peekUpdateElementPosition(selectedBlock, draggingElement, { x, y });
|
const updatedBlocks = peekUpdateElementPosition(selectedBlock, draggingElement, { x, y });
|
||||||
const updatedBlock = getBlockFromPeekedBlocks(updatedBlocks, selectedBlock);
|
blockToUpdate = getBlockFromPeekedBlocks(updatedBlocks, selectedBlock);
|
||||||
if (updatedBlock) {
|
|
||||||
await updateBackend(updatedBlock);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
} else if (draggingBlock && currentBlock?.positionType && (currentBlock.positionType === "absolute" || currentBlock.positionType === "fixed")) {
|
||||||
|
// Update backend for block drag
|
||||||
// Update backend for block drag
|
|
||||||
if (draggingBlock && currentBlock?.positionType && (currentBlock.positionType === "absolute" || currentBlock.positionType === "fixed")) {
|
|
||||||
const blockToDrag = document.querySelector(`[data-block-id="${draggingBlock}"]`) as HTMLElement;
|
const blockToDrag = document.querySelector(`[data-block-id="${draggingBlock}"]`) as HTMLElement;
|
||||||
|
|
||||||
if (blockToDrag) {
|
if (blockToDrag) {
|
||||||
@@ -301,32 +347,24 @@ const DashboardEditor: React.FC = () => {
|
|||||||
|
|
||||||
// Use peek to get updated blocks and send only the affected block to backend
|
// Use peek to get updated blocks and send only the affected block to backend
|
||||||
const updatedBlocks = peekUpdateBlockPosition(draggingBlock, { x, y });
|
const updatedBlocks = peekUpdateBlockPosition(draggingBlock, { x, y });
|
||||||
const updatedBlock = getBlockFromPeekedBlocks(updatedBlocks, draggingBlock);
|
blockToUpdate = getBlockFromPeekedBlocks(updatedBlocks, draggingBlock);
|
||||||
if (updatedBlock) {
|
|
||||||
await updateBackend(updatedBlock);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
} else if (resizingElement && selectedBlock) {
|
||||||
|
// Update backend for element resize
|
||||||
// Update backend for element resize
|
|
||||||
if (resizingElement && selectedBlock) {
|
|
||||||
const elementToResize = document.querySelector(`[data-element-id="${resizingElement}"]`) as HTMLElement;
|
const elementToResize = document.querySelector(`[data-element-id="${resizingElement}"]`) as HTMLElement;
|
||||||
if (elementToResize) {
|
const blockElement = document.querySelector(`[data-block-id="${selectedBlock}"]`) as HTMLElement;
|
||||||
|
|
||||||
|
if (elementToResize && blockElement) {
|
||||||
const computedStyle = window.getComputedStyle(elementToResize);
|
const computedStyle = window.getComputedStyle(elementToResize);
|
||||||
const width = parseFloat(computedStyle.width);
|
const width = parseFloat(computedStyle.width);
|
||||||
const height = parseFloat(computedStyle.height);
|
const height = parseFloat(computedStyle.height);
|
||||||
|
|
||||||
// Use peek to get updated blocks and send only the affected block to backend
|
// Use peek to get updated blocks
|
||||||
const updatedBlocks = peekUpdateElementSize(selectedBlock, resizingElement, { width, height });
|
const updatedBlocks = peekUpdateElementSize(selectedBlock, resizingElement, { width, height });
|
||||||
const updatedBlock = getBlockFromPeekedBlocks(updatedBlocks, selectedBlock);
|
blockToUpdate = getBlockFromPeekedBlocks(updatedBlocks, selectedBlock);
|
||||||
if (updatedBlock) {
|
|
||||||
await updateBackend(updatedBlock);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
} else if (resizingBlock) {
|
||||||
|
// Update backend for block resize
|
||||||
// Update backend for block resize
|
|
||||||
if (resizingBlock) {
|
|
||||||
const blockToResize = document.querySelector(`[data-block-id="${resizingBlock}"]`) as HTMLElement;
|
const blockToResize = document.querySelector(`[data-block-id="${resizingBlock}"]`) as HTMLElement;
|
||||||
if (blockToResize) {
|
if (blockToResize) {
|
||||||
const computedStyle = window.getComputedStyle(blockToResize);
|
const computedStyle = window.getComputedStyle(blockToResize);
|
||||||
@@ -335,19 +373,25 @@ const DashboardEditor: React.FC = () => {
|
|||||||
|
|
||||||
// Use peek to get updated blocks and send only the affected block to backend
|
// Use peek to get updated blocks and send only the affected block to backend
|
||||||
const updatedBlocks = peekUpdateBlockSize(resizingBlock, { width, height });
|
const updatedBlocks = peekUpdateBlockSize(resizingBlock, { width, height });
|
||||||
const updatedBlock = getBlockFromPeekedBlocks(updatedBlocks, resizingBlock);
|
blockToUpdate = getBlockFromPeekedBlocks(updatedBlocks, resizingBlock);
|
||||||
if (updatedBlock) {
|
|
||||||
await updateBackend(updatedBlock);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset all dragging states
|
// Reset all dragging states IMMEDIATELY to stop cursor following
|
||||||
setDraggingElement(null);
|
setDraggingElement(null);
|
||||||
setResizingElement(null);
|
setResizingElement(null);
|
||||||
setDraggingBlock(null);
|
setDraggingBlock(null);
|
||||||
setResizingBlock(null);
|
setResizingBlock(null);
|
||||||
setResizeStart(null);
|
setResizeStart(null);
|
||||||
|
|
||||||
|
// Perform updates if needed
|
||||||
|
if (blockToUpdate) {
|
||||||
|
// Optimistic update to prevent snap-back
|
||||||
|
updateBlock(blockToUpdate.blockUuid, blockToUpdate);
|
||||||
|
|
||||||
|
// Send to backend
|
||||||
|
await updateBackend(blockToUpdate);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (draggingElement || draggingBlock || resizingElement || resizingBlock) {
|
if (draggingElement || draggingBlock || resizingElement || resizingBlock) {
|
||||||
|
|||||||
@@ -45,13 +45,6 @@ const BlockComponent: React.FC<BlockComponentProps> = ({
|
|||||||
const isSelected = selectedBlock === block.blockUuid;
|
const isSelected = selectedBlock === block.blockUuid;
|
||||||
const isDraggable = editMode && (block.positionType === "absolute" || block.positionType === "fixed");
|
const isDraggable = editMode && (block.positionType === "absolute" || block.positionType === "fixed");
|
||||||
|
|
||||||
const handleMouseDown = (event: React.MouseEvent) => {
|
|
||||||
if (isDraggable) {
|
|
||||||
handleBlockDragStart(block.blockUuid, event);
|
|
||||||
}
|
|
||||||
handleBlockClick(block.blockUuid, event);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={block.blockUuid}
|
key={block.blockUuid}
|
||||||
@@ -70,7 +63,12 @@ const BlockComponent: React.FC<BlockComponentProps> = ({
|
|||||||
zIndex: block.zIndex || 1,
|
zIndex: block.zIndex || 1,
|
||||||
cursor: isDraggable ? "move" : "pointer",
|
cursor: isDraggable ? "move" : "pointer",
|
||||||
}}
|
}}
|
||||||
onClick={handleMouseDown}
|
onMouseDown={(e) => {
|
||||||
|
if (isDraggable) {
|
||||||
|
handleBlockDragStart(block.blockUuid, e);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onClick={(e) => handleBlockClick(block.blockUuid, e)}
|
||||||
>
|
>
|
||||||
{/* Add Element Button */}
|
{/* Add Element Button */}
|
||||||
{editMode && isSelected && (
|
{editMode && isSelected && (
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import type { Block } from "../../../../types/exportedTypes";
|
|||||||
export const calculateMinBlockSize = (block: Block): Size => {
|
export const calculateMinBlockSize = (block: Block): Size => {
|
||||||
let minWidth = 100;
|
let minWidth = 100;
|
||||||
let minHeight = 50;
|
let minHeight = 50;
|
||||||
|
let stackedHeight = 0; // Track cumulative height for stacked elements
|
||||||
|
|
||||||
block.elements.forEach((element) => {
|
block.elements.forEach((element) => {
|
||||||
if (element.positionType === "absolute") {
|
if (element.positionType === "absolute") {
|
||||||
@@ -11,10 +12,18 @@ export const calculateMinBlockSize = (block: Block): Size => {
|
|||||||
minWidth = Math.max(minWidth, elementRight + 20);
|
minWidth = Math.max(minWidth, elementRight + 20);
|
||||||
minHeight = Math.max(minHeight, elementBottom + 20);
|
minHeight = Math.max(minHeight, elementBottom + 20);
|
||||||
} else {
|
} else {
|
||||||
|
// For relative/static elements, they stack vertically
|
||||||
|
// Width: take the maximum
|
||||||
minWidth = Math.max(minWidth, (element.size?.width || 200) + 40);
|
minWidth = Math.max(minWidth, (element.size?.width || 200) + 40);
|
||||||
minHeight = Math.max(minHeight, (element.size?.height || 60) + 40);
|
// Height: sum them up since they stack
|
||||||
|
stackedHeight += element.size?.height || 60;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Add padding to stacked height and compare with minHeight from absolute elements
|
||||||
|
if (stackedHeight > 0) {
|
||||||
|
minHeight = Math.max(minHeight, stackedHeight + 40);
|
||||||
|
}
|
||||||
|
|
||||||
return { width: minWidth, height: minHeight };
|
return { width: minWidth, height: minHeight };
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user