Bläddra i källkod

Keep file context menu inside viewport

Toby Chui 1 månad sedan
förälder
incheckning
b513a835d2

+ 2 - 0
src/web/SystemAO/file_system/file_explorer.css

@@ -1017,6 +1017,8 @@ body.fmResizing *{
     -moz-box-shadow: 0px 8px 28px var(--fs-shadow);
     box-shadow: 0px 8px 28px var(--fs-shadow);
     z-index: 999;
+    max-height: calc(100vh - 16px);
+    overflow-y: auto;
 }
 
 .contextmenu.visible{

+ 46 - 69
src/web/SystemAO/file_system/js/explorer/selection.js

@@ -134,51 +134,54 @@ function bindFileObjectEvents(){
         }
     });
 
-    //This function calculate and offset the context menu to not go out of the window area
+    /*
+        Place the context menu next to the cursor, keeping the whole menu
+        inside the window.
+    */
+    const contextMenuMargin = 8;
+
     function calculateContextMenuOffsets(evt){
-        var defaultLeftPost = evt.pageX + "px";
-        var defaultTopPost =evt.pageY + "px";
-        
-        if (evt.pageX > window.innerWidth / 2){
-            defaultLeftPost = evt.pageX - $("#contextmenu").width();
-            
-            if (defaultLeftPost < 0){
-                //over the left boundary
-                defaultLeftPost = 0;
-            }
-            defaultLeftPost = defaultLeftPost + "px";
-        }else{
+        let menu = $("#contextmenu");
+        let menuWidth = menu.outerWidth();
+        let menuHeight = menu.outerHeight();
+        let viewWidth = window.innerWidth;
+        let viewHeight = window.innerHeight;
+
+        //Viewport coordinates of the click, with a fallback for synthetic events
+        let clickX = (evt.clientX == undefined)?evt.pageX:evt.clientX;
+        let clickY = (evt.clientY == undefined)?evt.pageY:evt.clientY;
+
+        //Open towards the side of the cursor with more room
+        let left = clickX;
+        if (clickX > viewWidth / 2){
+            left = clickX - menuWidth;
+        }
 
-            if (evt.pageX + $("#contextmenu").width() > window.innerWidth){
-                //Over the right boundary
-                defaultLeftPost = window.innerWidth - $("#contextmenu").width();
-                defaultLeftPost = defaultLeftPost + "px";
-            }
+        let top = clickY;
+        if (clickY > viewHeight / 2){
+            top = clickY - menuHeight;
         }
 
-        if (evt.pageY > window.innerHeight / 2){
-            defaultTopPost = evt.pageY - $("#contextmenu").height();
+        //Pull the menu back inside the window if it still overflows any edge
+        if (left + menuWidth > viewWidth - contextMenuMargin){
+            left = viewWidth - menuWidth - contextMenuMargin;
+        }
+        if (left < contextMenuMargin){
+            left = contextMenuMargin;
+        }
 
-            if (defaultTopPost < 0){
-                //over the top boundary
-                defaultTopPost = 0;
-            }
-            defaultTopPost = defaultTopPost + "px"
-            
-        }else{
-            if (evt.pageY + $("#contextmenu").height() > window.innerHeight){
-                //Over the lower boundary
-                defaultTopPost =  window.innerHeight - $("#contextmenu").height();
-                defaultTopPost = defaultTopPost + "px"
-            }
+        if (top + menuHeight > viewHeight - contextMenuMargin){
+            top = viewHeight - menuHeight - contextMenuMargin;
+        }
+        if (top < contextMenuMargin){
+            top = contextMenuMargin;
         }
-        
-        $("#contextmenu").css({
-            left: defaultLeftPost,
-            top: defaultTopPost
-        });
 
-    }  
+        menu.css({
+            left: left + "px",
+            top: top + "px"
+        });
+    }
 
     //Rightclick on a file object
     $(".fileObject").off("contextmenu").on("contextmenu", function(evt){
@@ -260,12 +263,6 @@ function bindFileObjectEvents(){
         }
 
         $("#contextmenu").addClass("visible");
-        //Handle CSS offset of the contextmenu
-        if ($("#contextmenu").offset().top < 0){
-            $("#contextmenu").css("top","0px");
-        }else if($("#contextmenu").offset().top + $("#contextmenu").height() > window.innerHeight){
-            $("#contextmenu").css("top",window.innerHeight - $("#contextmenu").height() + "px");
-        }
 
         if (isMobile){
             $("#contextmenu").find(".mobileonly").show();
@@ -305,17 +302,9 @@ function bindFileObjectEvents(){
             $("#contextmenu").find(".vroothide").show();
             $("#contextmenu").find(".zipFileOnly").hide();
 
-            //Calculate the position of the context menu
-            calculateContextMenuOffsets(e);
-
-            //Show context menu
+            //Show context menu, then place it (it must be rendered to be measured)
             $("#contextmenu").addClass("visible");
-            //Handle CSS offset of the contextmenu
-            if ($("#contextmenu").offset().top < 0){
-                $("#contextmenu").css("top","0px");
-            }else if($("#contextmenu").offset().top + $("#contextmenu").height() > window.innerHeight){
-                $("#contextmenu").css("top",window.innerHeight - $("#contextmenu").height() + "px");
-            }
+            calculateContextMenuOffsets(e);
         }
     });
 
@@ -357,13 +346,8 @@ function bindFileObjectEvents(){
             $("#contextmenu").find(".divider").hide();
             $("#contextmenu").find(".specialviewonly").show();
 
-            calculateContextMenuOffsets(e);
             $("#contextmenu").addClass("visible");
-            if ($("#contextmenu").offset().top < 0){
-                $("#contextmenu").css("top","0px");
-            }else if($("#contextmenu").offset().top + $("#contextmenu").height() > window.innerHeight){
-                $("#contextmenu").css("top",window.innerHeight - $("#contextmenu").height() + "px");
-            }
+            calculateContextMenuOffsets(e);
             return;
         }
 
@@ -377,16 +361,9 @@ function bindFileObjectEvents(){
             $("#contextmenu").find(".specialviewonly").hide();
             $("#contextmenu").find(".vrootonly").show();
 
-            //Show context menu
-            calculateContextMenuOffsets(e);
-
+            //Show context menu, then place it (it must be rendered to be measured)
             $("#contextmenu").addClass("visible");
-            //Handle CSS offset of the contextmenu
-            if ($("#contextmenu").offset().top < 0){
-                $("#contextmenu").css("top","0px");
-            }else if($("#contextmenu").offset().top + $("#contextmenu").height() > window.innerHeight){
-                $("#contextmenu").css("top",window.innerHeight - $("#contextmenu").height() + "px");
-            }
+            calculateContextMenuOffsets(e);
         }
     });