using System.Diagnostics.CodeAnalysis; using System.Windows.Input; using Connected.Annotations; using Connected.Utilities; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Web; namespace Connected.Components; public partial class Menu : UIComponent, IActivatable { protected string Classname => new CssBuilder("mud-menu") .AddClass(Class) .Build(); protected string ActivatorClassname => new CssBuilder("mud-menu-activator") .AddClass("mud-disabled", Disabled) .Build(); private bool _isOpen; private bool _isMouseOver = false; [Parameter] [Category(CategoryTypes.Menu.Behavior)] public string Label { get; set; } /// /// User class names for the list, separated by space /// [Parameter] [Category(CategoryTypes.Menu.PopupAppearance)] public string ListClass { get; set; } /// /// User class names for the popover, separated by space /// [Parameter] [Category(CategoryTypes.Menu.PopupAppearance)] public string PopoverClass { get; set; } /// /// Icon to use if set will turn the button into a MudIconButton. /// [Parameter] [Category(CategoryTypes.Menu.Behavior)] public string Icon { get; set; } /// /// The color of the icon. It supports the theme colors. /// [Parameter] [Category(CategoryTypes.Menu.Appearance)] public ThemeColor IconColor { get; set; } = ThemeColor.Inherit; /// /// Icon placed before the text if set. /// [Parameter] [Category(CategoryTypes.Menu.Behavior)] public string StartIcon { get; set; } /// /// Icon placed after the text if set. /// [Parameter] [Category(CategoryTypes.Menu.Behavior)] public string EndIcon { get; set; } /// /// The color of the button. It supports the theme colors. /// [Parameter] [Category(CategoryTypes.Menu.Appearance)] public ThemeColor Color { get; set; } = ThemeColor.Default; /// /// The button Size of the component. /// [Parameter] [Category(CategoryTypes.Menu.Appearance)] public Size Size { get; set; } = Size.Medium; /// /// The button variant to use. /// [Parameter] [Category(CategoryTypes.Menu.Appearance)] public Variant Variant { get; set; } = Variant.Text; /// /// If true, compact vertical padding will be applied to all menu items. /// [Parameter] [Category(CategoryTypes.Menu.PopupAppearance)] public bool Dense { get; set; } /// /// If true, the list menu will be same width as the parent. /// [Parameter] [Category(CategoryTypes.Menu.PopupAppearance)] public bool FullWidth { get; set; } /// /// Sets the maxheight the menu can have when open. /// [Parameter] [Category(CategoryTypes.Menu.PopupAppearance)] public int? MaxHeight { get; set; } /// /// If true, instead of positioning the menu at the left upper corner, position at the exact cursor location. /// This makes sense for larger activators /// [Parameter] [Category(CategoryTypes.Menu.PopupBehavior)] public bool PositionAtCursor { get; set; } /// /// If true, instead of positioning the menu at the left upper corner, position at the exact cursor location. /// This makes sense for larger activators /// [Obsolete("Use PositionAtCursor instead.", true)] [Parameter] public bool PositionAtCurser { get => PositionAtCursor; set => PositionAtCursor = value; } /// /// Place a MudButton, a MudIconButton or any other component capable of acting as an activator. This will /// override the standard button and all parameters which concern it. /// [Parameter] [Category(CategoryTypes.Menu.Behavior)] public RenderFragment ActivatorContent { get; set; } /// /// Specify the activation event when ActivatorContent is set /// [Parameter] [Category(CategoryTypes.Menu.Behavior)] public MouseEvent ActivationEvent { get; set; } = MouseEvent.LeftClick; /// /// Set the anchor origin point to determen where the popover will open from. /// [Parameter] [Category(CategoryTypes.Menu.PopupAppearance)] public Origin AnchorOrigin { get; set; } = Origin.TopLeft; /// /// Sets the transform origin point for the popover. /// [Parameter] [Category(CategoryTypes.Menu.PopupAppearance)] public Origin TransformOrigin { get; set; } = Origin.TopLeft; /// /// Sets the direction the select menu will start from relative to its parent. /// [ExcludeFromCodeCoverage] [Obsolete("Use AnchorOrigin or TransformOrigin instead.", true)] [Parameter] public Direction Direction { get; set; } = Direction.Bottom; /// /// If true, the select menu will open either before or after the input depending on the direction. /// [ExcludeFromCodeCoverage] [Obsolete("Use AnchorOrigin or TransformOrigin instead.", true)] [Parameter] public bool OffsetY { get; set; } /// /// If true, the select menu will open either above or bellow the input depending on the direction. /// [ExcludeFromCodeCoverage] [Obsolete("Use AnchorOrigin or TransformOrigin instead.", true)] [Parameter] public bool OffsetX { get; set; } /// /// Set to true if you want to prevent page from scrolling when the menu is open /// [Parameter] [Category(CategoryTypes.Menu.PopupAppearance)] public bool LockScroll { get; set; } /// /// If true, menu will be disabled. /// [Parameter] [Category(CategoryTypes.Menu.Behavior)] public bool Disabled { get; set; } /// /// If true, disables ripple effect. /// [Parameter] [Category(CategoryTypes.Menu.Appearance)] public bool DisableRipple { get; set; } /// /// If true, no drop-shadow will be used. /// [Parameter] [Category(CategoryTypes.Menu.Appearance)] public bool DisableElevation { get; set; } #region Obsolete members from previous MudButtonBase inherited structure [ExcludeFromCodeCoverage] [Obsolete("Linking is not supported. MudMenu is not a MudBaseButton anymore.", true)] [Parameter] public string Link { get; set; } [ExcludeFromCodeCoverage] [Obsolete("Linking is not supported. MudMenu is not a MudBaseButton anymore.", true)] [Parameter] public string Target { get; set; } [ExcludeFromCodeCoverage] [Obsolete("MudMenu is not a MudBaseButton anymore.", true)] [Parameter] public string HtmlTag { get; set; } = "button"; [ExcludeFromCodeCoverage] [Obsolete("MudMenu is not a MudBaseButton anymore.", true)] [Parameter] public ButtonType ButtonType { get; set; } [ExcludeFromCodeCoverage] [Obsolete("MudMenu is not a MudBaseButton anymore.", true)] [Parameter] public ICommand Command { get; set; } [ExcludeFromCodeCoverage] [Obsolete("MudMenu is not a MudBaseButton anymore.", true)] [Parameter] public object CommandParameter { get; set; } #endregion /// /// Add menu items here /// [Parameter] [Category(CategoryTypes.Menu.PopupBehavior)] public RenderFragment ChildContent { get; set; } public string PopoverStyle { get; set; } public void CloseMenu() { _isOpen = false; _isMouseOver = false; PopoverStyle = null; StateHasChanged(); } public void OpenMenu(EventArgs args) { if (Disabled) return; if (PositionAtCursor) SetPopoverStyle((MouseEventArgs)args); _isOpen = true; StateHasChanged(); } // Sets the popover style ONLY when there is an activator private void SetPopoverStyle(MouseEventArgs args) { AnchorOrigin = Origin.TopLeft; PopoverStyle = $"margin-top: {args?.OffsetY.ToPx()}; margin-left: {args?.OffsetX.ToPx()};"; } public void ToggleMenu(MouseEventArgs args) { if (Disabled) return; if (ActivationEvent == MouseEvent.LeftClick && args.Button != 0 && !_isOpen) return; if (ActivationEvent == MouseEvent.RightClick && args.Button != 2 && !_isOpen) return; if (_isOpen) CloseMenu(); else OpenMenu(args); } public void ToggleMenuTouch(TouchEventArgs args) { if (Disabled) { return; } if (_isOpen) { CloseMenu(); } else { OpenMenu(args); } } /// /// Implementation of IActivatable.Activate, toggles the menu. /// /// /// public void Activate(object activator, MouseEventArgs args) { ToggleMenu(args); } public void MouseEnter(MouseEventArgs args) { _isMouseOver = true; if (ActivationEvent == MouseEvent.MouseOver) { OpenMenu(args); } } public async void MouseLeave() { _isMouseOver = false; await Task.Delay(100); if (ActivationEvent == MouseEvent.MouseOver && _isMouseOver == false) { CloseMenu(); } } }