diff --git a/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml b/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml
index fe97512b4c..cb5d5a5110 100644
--- a/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml
+++ b/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml
@@ -6682,12 +6682,14 @@
-
+
- Method called from JavaScript to get the current mouse ccordinates.
+ Method called from JavaScript to get the current mouse coordinates.
-
-
+ x-coordinate of point clicked on
+ y-coordinate of point clicked on
+ width of the screen
+ height of the screen
diff --git a/examples/Demo/Shared/wwwroot/css/site.css b/examples/Demo/Shared/wwwroot/css/site.css
index 2bd7a1b6b8..bad0befa31 100644
--- a/examples/Demo/Shared/wwwroot/css/site.css
+++ b/examples/Demo/Shared/wwwroot/css/site.css
@@ -1,4 +1,4 @@
-@import '_content/Microsoft.FluentUI.AspNetCore.Components/css/reboot.css';
+@import '/_content/Microsoft.FluentUI.AspNetCore.Components/css/reboot.css';
body {
height: 100%;
diff --git a/src/Core/Components/Menu/FluentMenu.razor.cs b/src/Core/Components/Menu/FluentMenu.razor.cs
index b1289516e9..bc667dec02 100644
--- a/src/Core/Components/Menu/FluentMenu.razor.cs
+++ b/src/Core/Components/Menu/FluentMenu.razor.cs
@@ -1,5 +1,8 @@
+// ------------------------------------------------------------------------
+// MIT License - Copyright (c) Microsoft Corporation. All rights reserved.
+// ------------------------------------------------------------------------
+
using System.Diagnostics.CodeAnalysis;
-using System.Drawing;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.FluentUI.AspNetCore.Components.Extensions;
@@ -14,12 +17,14 @@ public partial class FluentMenu : FluentComponentBase, IDisposable
private bool _opened = false;
private DotNetObjectReference? _dotNetHelper = null;
- private Point _clickedPoint = default;
+
private bool _contextMenu = false;
private readonly Dictionary items = [];
private IMenuService? _menuService = null;
private IJSObjectReference _jsModule = default!;
+ private (int top, int right, int bottom, int left) _stylePositions;
+
///
internal string? ClassValue => new CssBuilder(Class)
.Build();
@@ -35,8 +40,11 @@ public partial class FluentMenu : FluentComponentBase, IDisposable
.AddStyle("position", "fixed", () => !Anchored && !string.IsNullOrEmpty(Anchor))
.AddStyle("width", "unset", () => !Anchored)
.AddStyle("height", "unset", () => !Anchored)
- .AddStyle("left", $"{_clickedPoint.X}px", () => !Anchored && _clickedPoint.X != 0)
- .AddStyle("top", $"{_clickedPoint.Y}px", () => !Anchored && _clickedPoint.Y != 0)
+
+ .AddStyle("top", $"{_stylePositions.top}px", () => !Anchored && _stylePositions.top != 0)
+ .AddStyle("right", $"{_stylePositions.right}px", () => !Anchored && _stylePositions.right != 0)
+ .AddStyle("bottom", $"{_stylePositions.bottom}px", () => !Anchored && _stylePositions.bottom != 0)
+ .AddStyle("left", $"{_stylePositions.left}px", () => !Anchored && _stylePositions.left != 0)
.Build();
///
@@ -153,7 +161,7 @@ public bool Open
/// Gets or sets how short the space allocated to the default position has to be before the tallest area is selected for layout.
///
[Parameter]
- public int VerticalThreshold { get; set; } = 0;
+ public int VerticalThreshold { get; set; } = 200;
///
/// Gets or sets how narrow the space allocated to the default position has to be before the widest area is selected for layout.
@@ -252,23 +260,53 @@ public async Task CloseAsync()
}
///
- /// Method called from JavaScript to get the current mouse ccordinates.
+ /// Method called from JavaScript to get the current mouse coordinates.
///
- ///
- ///
+ /// x-coordinate of point clicked on
+ /// y-coordinate of point clicked on
+ /// width of the screen
+ /// height of the screen
///
[JSInvokable]
- public async Task OpenAsync(int x, int y)
+ public async Task OpenAsync(int screenWidth, int screenHeight, int x, int y)
{
- _clickedPoint = new Point(x, y);
- Open = true;
+ // Calculate the position to display the context menu using the cursor position (x, y)
+ // together with the screen width and height.
+ // The menu may need to be displayed above or left of the cursor to fit in the screen.
+ var left = 0;
+ var right = 0;
+ var top = 0;
+ var bottom = 0;
+
+ if (x + HorizontalThreshold > screenWidth)
+ {
+ right = screenWidth - x;
+ }
+ else
+ {
+ left = x;
+ }
+
+ if (y + VerticalThreshold > screenHeight)
+ {
+ bottom = screenHeight - y;
+ }
+ else
+ {
+ top = y;
+ }
+
+ _stylePositions = (top, right, bottom, left);
+
+ Open = true;
if (OpenChanged.HasDelegate)
{
await OpenChanged.InvokeAsync(Open);
}
StateHasChanged();
+
}
internal void Register(FluentMenuItem item)
@@ -286,9 +324,7 @@ private bool DrawMenuWithoutService
{
get
{
- return MenuService is not null && UseMenuService == true && !string.IsNullOrEmpty(Id) && !string.IsNullOrEmpty(Anchor) && Anchored == true
- ? false // Use the MenuService to draw the menu
- : true; // Use the default way to draw the menu
+ return MenuService is null || UseMenuService != true || string.IsNullOrEmpty(Id) || string.IsNullOrEmpty(Anchor) || Anchored != true; // Use the default way to draw the menu
}
}
@@ -328,7 +364,7 @@ internal async Task NotifyCheckedChangedAsync(FluentMenuItem fluentMenuItem)
await OnCheckedChanged.InvokeAsync(fluentMenuItem);
}
- internal async Task IsCheckedAsync (FluentMenuItem item)
+ internal async Task IsCheckedAsync(FluentMenuItem item)
{
return await _jsModule.InvokeAsync("isChecked", item.Id);
}
diff --git a/src/Core/Components/Menu/FluentMenu.razor.js b/src/Core/Components/Menu/FluentMenu.razor.js
index 8ad83197e6..e4a709d320 100644
--- a/src/Core/Components/Menu/FluentMenu.razor.js
+++ b/src/Core/Components/Menu/FluentMenu.razor.js
@@ -3,7 +3,7 @@ export function addEventLeftClick(id, dotNetHelper) {
var item = document.getElementById(id);
if (!!item) {
item.addEventListener("click", function(e) {
- dotNetHelper.invokeMethodAsync('OpenAsync', e.clientX, e.clientY);
+ dotNetHelper.invokeMethodAsync('OpenAsync', window.innerWidth, window.innerHeight, e.clientX, e.clientY);
});
}
}
@@ -14,7 +14,7 @@ export function addEventRightClick(id, dotNetHelper) {
if (!!item) {
item.addEventListener('contextmenu', function (e) {
e.preventDefault();
- dotNetHelper.invokeMethodAsync('OpenAsync', e.clientX, e.clientY);
+ dotNetHelper.invokeMethodAsync('OpenAsync', window.innerWidth, window.innerHeight, e.clientX, e.clientY);
return false;
}, false);
}