diff --git a/web/src/index.css b/web/src/index.css
index 68351848f..b290221b3 100644
--- a/web/src/index.css
+++ b/web/src/index.css
@@ -171,4 +171,10 @@
.react-resizable-handle {
z-index: 30;
+ background-image: none !important;
+}
+
+.react-grid-item.react-grid-placeholder {
+ border: 3px solid #a00000 !important;
+ opacity: 0.5 !important;
}
diff --git a/web/src/views/live/DraggableGridLayout.tsx b/web/src/views/live/DraggableGridLayout.tsx
index a65591923..0aa9459b6 100644
--- a/web/src/views/live/DraggableGridLayout.tsx
+++ b/web/src/views/live/DraggableGridLayout.tsx
@@ -94,6 +94,7 @@ export default function DraggableGridLayout({
// editing
const [editGroup, setEditGroup] = useState(false);
+ const [showCircles, setShowCircles] = useState(true);
useEffect(() => {
setEditGroup(false);
@@ -115,6 +116,7 @@ export default function DraggableGridLayout({
}
// save layout to idb
setGridLayout(currentLayout);
+ setShowCircles(true);
},
[setGridLayout, isGridLayoutLoaded, gridLayout, currentGridLayout],
);
@@ -321,13 +323,37 @@ export default function DraggableGridLayout({
const heightDiff = layoutItem.h - oldLayoutItem.h;
const widthDiff = layoutItem.w - oldLayoutItem.w;
const changeCoef = oldLayoutItem.w / oldLayoutItem.h;
- if (Math.abs(heightDiff) < Math.abs(widthDiff) || layoutItem.w == 12) {
- layoutItem.h = layoutItem.w / changeCoef;
- placeholder.h = layoutItem.w / changeCoef;
+
+ let newWidth, newHeight;
+
+ if (Math.abs(heightDiff) < Math.abs(widthDiff)) {
+ newHeight = Math.round(layoutItem.w / changeCoef);
+ newWidth = Math.round(newHeight * changeCoef);
} else {
- layoutItem.w = layoutItem.h * changeCoef;
- placeholder.w = layoutItem.h * changeCoef;
+ newWidth = Math.round(layoutItem.h * changeCoef);
+ newHeight = Math.round(newWidth / changeCoef);
}
+
+ // Ensure dimensions maintain aspect ratio and fit within the grid
+ if (layoutItem.x + newWidth > 12) {
+ newWidth = 12 - layoutItem.x;
+ newHeight = Math.round(newWidth / changeCoef);
+ }
+
+ if (changeCoef == 0.5) {
+ // portrait
+ newHeight = Math.ceil(newHeight / 2) * 2;
+ } else if (changeCoef == 2) {
+ // pano/wide
+ newHeight = Math.ceil(newHeight * 2) / 2;
+ }
+
+ newWidth = Math.round(newHeight * changeCoef);
+
+ layoutItem.w = newWidth;
+ layoutItem.h = newHeight;
+ placeholder.w = layoutItem.w;
+ placeholder.h = layoutItem.h;
};
return (
@@ -378,6 +404,7 @@ export default function DraggableGridLayout({
resizeHandles={isEditMode ? ["sw", "nw", "se", "ne"] : []}
onDragStop={handleLayoutChange}
onResize={handleResize}
+ onResizeStart={() => setShowCircles(false)}
onResizeStop={handleLayoutChange}
>
{includeBirdseye && birdseyeConfig?.enabled && (
@@ -385,13 +412,14 @@ export default function DraggableGridLayout({
key="birdseye"
className={cn(
isEditMode &&
+ showCircles &&
"outline outline-2 outline-muted-foreground hover:cursor-grab hover:outline-4 active:cursor-grabbing",
)}
birdseyeConfig={birdseyeConfig}
liveMode={birdseyeConfig.restream ? "mse" : "jsmpeg"}
onClick={() => onSelectCamera("birdseye")}
>
- {isEditMode && }
+ {isEditMode && showCircles && }
)}
{cameras.map((camera) => {
@@ -412,6 +440,7 @@ export default function DraggableGridLayout({
"rounded-lg bg-black md:rounded-2xl",
grow,
isEditMode &&
+ showCircles &&
"outline-2 outline-muted-foreground hover:cursor-grab hover:outline-4 active:cursor-grabbing",
)}
windowVisible={
@@ -423,7 +452,7 @@ export default function DraggableGridLayout({
!isEditMode && onSelectCamera(camera.name);
}}
>
- {isEditMode && }
+ {isEditMode && showCircles && }
);
})}