From 18a31704e8b4a93a28b9ce9996a4335ba489046c Mon Sep 17 00:00:00 2001 From: James Ketrenos Date: Sun, 14 Sep 2025 14:47:05 -0700 Subject: [PATCH] Fixed Moveable --- client/src/MediaControl.css | 48 +++--- client/src/MediaControl.tsx | 320 ++++++++++++++++++++---------------- 2 files changed, 201 insertions(+), 167 deletions(-) diff --git a/client/src/MediaControl.css b/client/src/MediaControl.css index 51fc3d3..2754912 100644 --- a/client/src/MediaControl.css +++ b/client/src/MediaControl.css @@ -7,6 +7,13 @@ opacity: 1; } +.MediaControlContainer { + position: relative; /* CRITICAL: This creates the positioning context */ + display: inline-block; + width: max-content; /* Ensure container sizes to content */ + height: max-content; +} + .MediaControlSpacer { display: flex; position: relative; @@ -30,25 +37,34 @@ .MediaControl { display: flex; - position: absolute; - align-items: center; + position: absolute; /* Out of flow */ + top: 0; /* Start at top of container */ + left: 0; /* Start at left of container */ width: 5rem; height: 3.75rem; min-width: 5rem; min-height: 3.75rem; z-index: 50000; border-radius: 0.25rem; + border: 1px solid green; } .MediaControl .Video { - position: relative; + display: flex; width: 100%; height: 100%; + position: relative; + top: 0; + left: 0; background-color: #444; border-radius: 0.25rem; border: 1px solid black; } +video { + border: 5px solid purple; +} + .MediaControl.Medium { width: 11.5em; height: 8.625em; @@ -56,37 +72,25 @@ min-height: 8.625em; } -.MediaControl > div { - display: flex; - position: absolute; - top: 0; - left: 0; - display: flex; - flex-direction: column; - align-items: center; - margin-right: 0.25rem; -} - .MediaControl .Controls { display: flex; position: absolute; - left: 0.5em; - bottom: 0.5em; - justify-content: flex-end; + gap: 0; + left: 0; + bottom: 0; + flex-direction: column; z-index: 1; + align-items: center; + justify-content: center } .MediaControl.Small .Controls { - left: 0; - bottom: unset; justify-content: center; } .MediaControl .Controls > div { - display: flex; border-radius: 0.25em; cursor: pointer; - padding: 0.25em; } .MediaControl .Controls > div:hover { @@ -100,4 +104,4 @@ .moveable-control-box .moveable-direction { border: none !important; -} \ No newline at end of file +} diff --git a/client/src/MediaControl.tsx b/client/src/MediaControl.tsx index 8f2356e..c5f4913 100644 --- a/client/src/MediaControl.tsx +++ b/client/src/MediaControl.tsx @@ -1308,6 +1308,7 @@ const MediaControl: React.FC = ({ isSelf, peer, className }) width?: number; height?: number; }>({ translate: [0, 0] }); + const containerRef = useRef(null); const targetRef = useRef(null); const spacerRef = useRef(null); const moveableRef = useRef(null); @@ -1319,6 +1320,15 @@ const MediaControl: React.FC = ({ isSelf, peer, className }) setVideoOn(peer.video_on); }, [peer]); + // Initialize size to match spacer + useEffect(() => { + if (spacerRef.current && targetRef.current && !frame.width) { + const spacerRect = spacerRef.current.getBoundingClientRect(); + targetRef.current.style.width = `${spacerRect.width}px`; + targetRef.current.style.height = `${spacerRect.height}px`; + } + }, [frame.width]); + useEffect(() => { if (!peer || peer.dead || !peer.attributes?.srcObject) { console.log( @@ -1457,171 +1467,191 @@ const MediaControl: React.FC = ({ isSelf, peer, className }) const colorVideo = isValid ? "primary" : "disabled"; return ( -
- {/* Drop target / spacer that stays in place */} +
- {isDragging && ( -
- Drop here -
- )} -
- - {/* Moveable element - now absolute positioned */} -
-
- {isSelf ? ( -
- {muted ? : } -
- ) : ( -
- {muted ? : } + {/* Drop target spacer */} +
+ {isDragging && ( +
+ Drop here
)} -
- {videoOn ? : } -
- {isValid ? ( - peer.attributes?.srcObject && ( - - - ) - ) : ( - -
Waiting for media…
- + + {/* Moveable element - positioned absolute relative to container */} +
+ + {isSelf ? ( + // onTouchStart={toggleMute} +
{muted ? : }
+ ) : ( +
+ {muted ? : } +
+ )} +
+ {videoOn ? : } +
- )} -
+ {isValid ? ( + peer.attributes?.srcObject && ( + <> +
- { - setIsDragging(true); - }} - onDrag={(e) => { - if (targetRef.current) { - targetRef.current.style.transform = e.transform; - } + { + setIsDragging(true); + }} + onDrag={(e) => { + if (targetRef.current) { + targetRef.current.style.transform = e.transform; + } - // Check for snap-back - const matrix = new DOMMatrix(e.transform); - const shouldSnap = checkSnapBack(matrix.m41, matrix.m42); + // Check for snap-back + const matrix = new DOMMatrix(e.transform); + const shouldSnap = checkSnapBack(matrix.m41, matrix.m42); - if (shouldSnap && spacerRef.current) { - // Add visual feedback for snap zone - spacerRef.current.style.borderColor = "#0088ff"; - } else if (spacerRef.current) { - spacerRef.current.style.borderColor = "#666"; - } - }} - onDragEnd={(e) => { - setIsDragging(false); + if (shouldSnap && spacerRef.current) { + // Add visual feedback for snap zone + spacerRef.current.style.borderColor = "#0088ff"; + } else if (spacerRef.current) { + spacerRef.current.style.borderColor = "#666"; + } + }} + onDragEnd={(e) => { + setIsDragging(false); - if (targetRef.current) { - const computedStyle = getComputedStyle(targetRef.current); - const transform = computedStyle.transform; + if (targetRef.current) { + const computedStyle = getComputedStyle(targetRef.current); + const transform = computedStyle.transform; - if (transform && transform !== "none") { - const matrix = new DOMMatrix(transform); - const shouldSnap = checkSnapBack(matrix.m41, matrix.m42); + if (transform && transform !== "none") { + const matrix = new DOMMatrix(transform); + const shouldSnap = checkSnapBack(matrix.m41, matrix.m42); - if (shouldSnap) { - // Snap back to origin - targetRef.current.style.transform = "translate(0px, 0px)"; - setFrame({ translate: [0, 0], width: frame.width, height: frame.height }); + if (shouldSnap) { + // Snap back to origin + targetRef.current.style.transform = "translate(0px, 0px)"; + setFrame({ translate: [0, 0], width: frame.width, height: frame.height }); - // Reset size if needed - if (spacerRef.current) { - const spacerRect = spacerRef.current.getBoundingClientRect(); - targetRef.current.style.width = `${spacerRect.width}px`; - targetRef.current.style.height = `${spacerRect.height}px`; - setFrame({ translate: [0, 0] }); + // Reset size if needed + if (spacerRef.current) { + const spacerRect = spacerRef.current.getBoundingClientRect(); + targetRef.current.style.width = `${spacerRect.width}px`; + targetRef.current.style.height = `${spacerRect.height}px`; + setFrame({ translate: [0, 0] }); + } + } else { + setFrame({ + translate: [matrix.m41, matrix.m42], + width: frame.width, + height: frame.height, + }); } } else { - setFrame({ - translate: [matrix.m41, matrix.m42], - width: frame.width, - height: frame.height, - }); + setFrame({ translate: [0, 0], width: frame.width, height: frame.height }); } - } else { - setFrame({ translate: [0, 0], width: frame.width, height: frame.height }); } - } - // Reset spacer border color - if (spacerRef.current) { - spacerRef.current.style.borderColor = "#666"; - } - }} - onResizeStart={(e) => { - e.setOrigin(["%", "%"]); - setIsDragging(true); - }} - onResize={(e) => { - e.target.style.width = `${e.width}px`; - e.target.style.height = `${e.height}px`; - setFrame({ - ...frame, - width: e.width, - height: e.height, - }); - }} - onResizeEnd={() => { - setIsDragging(false); - }} - /> -
+ // Reset spacer border color + if (spacerRef.current) { + spacerRef.current.style.borderColor = "#666"; + } + }} + onResizeStart={(e) => { + e.setOrigin(["%", "%"]); + setIsDragging(true); + }} + onResize={(e) => { + e.target.style.width = `${e.width}px`; + e.target.style.height = `${e.height}px`; + setFrame({ + ...frame, + width: e.width, + height: e.height, + }); + }} + onResizeEnd={() => { + setIsDragging(false); + }} + /> +
+
); };