mirror of
https://github.com/radzenhq/radzen-blazor.git
synced 2026-02-04 05:35:44 +00:00
446 lines
13 KiB
C#
446 lines
13 KiB
C#
using Microsoft.AspNetCore.Components;
|
|
using Microsoft.AspNetCore.Components.Routing;
|
|
using Microsoft.AspNetCore.Components.Web;
|
|
using Radzen.Blazor.Rendering;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Radzen.Blazor
|
|
{
|
|
/// <summary>
|
|
/// RadzenPanelMenuItem component.
|
|
/// </summary>
|
|
public partial class RadzenPanelMenuItem : RadzenComponent
|
|
{
|
|
/// <inheritdoc />
|
|
protected override string GetComponentCssClass() => ClassList.Create("rz-navigation-item")
|
|
.Add("rz-state-focused", Parent?.IsFocused(this) == true)
|
|
.AddDisabled(Disabled)
|
|
.ToString();
|
|
|
|
/// <summary>
|
|
/// Gets or sets the target.
|
|
/// </summary>
|
|
/// <value>The target.</value>
|
|
[Parameter]
|
|
public string? Target { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the expanded changed callback.
|
|
/// </summary>
|
|
/// <value>The expanded changed callback.</value>
|
|
[Parameter]
|
|
public EventCallback<bool> ExpandedChanged { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the text.
|
|
/// </summary>
|
|
/// <value>The text.</value>
|
|
[Parameter]
|
|
public string ImageAlternateText { get; set; } = "image";
|
|
|
|
/// <summary>
|
|
/// Gets or sets the text.
|
|
/// </summary>
|
|
/// <value>The text.</value>
|
|
[Parameter]
|
|
public string? Text { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the value.
|
|
/// </summary>
|
|
/// <value>The value.</value>
|
|
[Parameter]
|
|
public object? Value { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the path.
|
|
/// </summary>
|
|
/// <value>The path.</value>
|
|
[Parameter]
|
|
public string? Path { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the navigation link match.
|
|
/// </summary>
|
|
/// <value>The navigation link match.</value>
|
|
[Parameter]
|
|
public NavLinkMatch Match { get; set; } = NavLinkMatch.Prefix;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the icon.
|
|
/// </summary>
|
|
/// <value>The icon.</value>
|
|
[Parameter]
|
|
public string? Icon { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the icon color.
|
|
/// </summary>
|
|
/// <value>The icon color.</value>
|
|
[Parameter]
|
|
public string? IconColor { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the image.
|
|
/// </summary>
|
|
/// <value>The image.</value>
|
|
[Parameter]
|
|
public string? Image { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the template.
|
|
/// </summary>
|
|
/// <value>The template.</value>
|
|
[Parameter]
|
|
public RenderFragment? Template { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether this <see cref="RadzenPanelMenuItem"/> is expanded.
|
|
/// </summary>
|
|
/// <value><c>true</c> if expanded; otherwise, <c>false</c>.</value>
|
|
[Parameter]
|
|
public bool Expanded { get; set; }
|
|
|
|
private bool expanded;
|
|
|
|
internal bool IsExpanded => expanded;
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether this <see cref="RadzenPanelMenuItem"/> is selected.
|
|
/// </summary>
|
|
/// <value><c>true</c> if selected; otherwise, <c>false</c>.</value>
|
|
[Parameter]
|
|
public bool Selected { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the child content.
|
|
/// </summary>
|
|
/// <value>The child content.</value>
|
|
[Parameter]
|
|
public RenderFragment? ChildContent { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether this <see cref="RadzenPanelMenuItem"/> is disabled.
|
|
/// </summary>
|
|
/// <value><c>true</c> if disabled; otherwise, <c>false</c>.</value>
|
|
[Parameter]
|
|
public bool Disabled { get; set; }
|
|
|
|
internal async Task Toggle()
|
|
{
|
|
if (!expanded && Parent?.Multiple != true)
|
|
{
|
|
var itemsToSkip = new List<RadzenPanelMenuItem>();
|
|
|
|
var p = ParentItem;
|
|
|
|
while (p != null)
|
|
{
|
|
itemsToSkip.Add(p);
|
|
|
|
p = p.ParentItem;
|
|
}
|
|
|
|
if (Parent != null)
|
|
{
|
|
await Parent.CollapseAllAsync(itemsToSkip);
|
|
}
|
|
}
|
|
|
|
expanded = !expanded;
|
|
|
|
await ExpandedChanged.InvokeAsync(expanded);
|
|
}
|
|
|
|
internal async Task CollapseAsync()
|
|
{
|
|
if (expanded)
|
|
{
|
|
expanded = false;
|
|
|
|
await ExpandedChanged.InvokeAsync(expanded);
|
|
}
|
|
}
|
|
|
|
string ToggleClass => ClassList.Create("notranslate rzi rz-navigation-item-icon-children")
|
|
.Add("rz-state-expanded", expanded)
|
|
.Add("rz-state-collapsed", !expanded)
|
|
.ToString();
|
|
|
|
string getIconStyle()
|
|
{
|
|
return $"{(Parent?.DisplayStyle == MenuItemDisplayStyle.Icon ? "margin-inline-end:0px;" : "")}{(!string.IsNullOrEmpty(IconColor) ? $"color:{IconColor}" : "")}";
|
|
}
|
|
|
|
void Expand()
|
|
{
|
|
expanded = true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the click callback.
|
|
/// </summary>
|
|
/// <value>The click callback.</value>
|
|
[Parameter]
|
|
public EventCallback<MenuItemEventArgs> Click { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the parent item.
|
|
/// </summary>
|
|
/// <value>The parent item.</value>
|
|
[CascadingParameter]
|
|
public RadzenPanelMenuItem? ParentItem { get; set;}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the parent.
|
|
/// </summary>
|
|
/// <value>The parent.</value>
|
|
[CascadingParameter]
|
|
public RadzenPanelMenu? Parent { get; set; }
|
|
|
|
internal List<RadzenPanelMenuItem> items = new List<RadzenPanelMenuItem>();
|
|
|
|
/// <summary>
|
|
/// Adds the item.
|
|
/// </summary>
|
|
/// <param name="item">The item.</param>
|
|
public void AddItem(RadzenPanelMenuItem item)
|
|
{
|
|
if (!items.Contains(item))
|
|
{
|
|
items.Add(item);
|
|
}
|
|
}
|
|
|
|
void EnsureVisible()
|
|
{
|
|
if (selected)
|
|
{
|
|
var parent = ParentItem;
|
|
|
|
while (parent != null)
|
|
{
|
|
parent.Expand();
|
|
|
|
if (parent.ParentItem == null)
|
|
{
|
|
parent.StateHasChanged();
|
|
}
|
|
|
|
parent = parent.ParentItem;
|
|
}
|
|
}
|
|
}
|
|
|
|
[Inject]
|
|
NavigationManager? NavigationManager { get; set; }
|
|
|
|
/// <inheritdoc />
|
|
protected override void OnInitialized()
|
|
{
|
|
base.OnInitialized();
|
|
|
|
expanded = Expanded;
|
|
|
|
selected = Selected;
|
|
|
|
if (NavigationManager != null)
|
|
{
|
|
NavigationManager.LocationChanged += OnLocationChanged;
|
|
}
|
|
|
|
if (ParentItem != null)
|
|
{
|
|
ParentItem.AddItem(this);
|
|
}
|
|
else if (Parent != null)
|
|
{
|
|
Parent.AddItem(this);
|
|
}
|
|
|
|
SyncWithNavigationManager();
|
|
}
|
|
|
|
string WrapperClass => ClassList.Create("rz-navigation-item-wrapper")
|
|
.Add("rz-navigation-item-wrapper-active", selected)
|
|
.ToString();
|
|
|
|
string LinkClass => ClassList.Create("rz-navigation-item-link")
|
|
.Add("rz-navigation-item-link-active", selected)
|
|
.ToString();
|
|
|
|
private bool selected;
|
|
|
|
private void OnLocationChanged(object? sender, LocationChangedEventArgs e)
|
|
{
|
|
SyncWithNavigationManager();
|
|
}
|
|
|
|
private void SyncWithNavigationManager()
|
|
{
|
|
var matches = ShouldMatch();
|
|
|
|
if (matches != selected)
|
|
{
|
|
selected = matches;
|
|
|
|
EnsureVisible();
|
|
|
|
StateHasChanged();
|
|
}
|
|
}
|
|
|
|
bool ShouldMatch()
|
|
{
|
|
if (string.IsNullOrEmpty(Path))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var currentAbsoluteUrl = NavigationManager?.ToAbsoluteUri(NavigationManager.Uri)?.AbsoluteUri ?? string.Empty;
|
|
var absoluteUrl = NavigationManager?.ToAbsoluteUri(Path)?.AbsoluteUri ?? string.Empty;
|
|
|
|
if (EqualsHrefExactlyOrIfTrailingSlashAdded(absoluteUrl, currentAbsoluteUrl))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (Path == "/")
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var match = Match != NavLinkMatch.Prefix ? Match : Parent?.Match ?? NavLinkMatch.Prefix;
|
|
|
|
if (match == NavLinkMatch.Prefix && IsStrictlyPrefixWithSeparator(currentAbsoluteUrl, absoluteUrl))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private static bool EqualsHrefExactlyOrIfTrailingSlashAdded(string absoluteUrl, string currentAbsoluteUrl)
|
|
{
|
|
if (string.Equals(currentAbsoluteUrl, absoluteUrl, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (currentAbsoluteUrl.Length == absoluteUrl.Length - 1)
|
|
{
|
|
if (absoluteUrl[^1] == '/' && absoluteUrl.StartsWith(currentAbsoluteUrl, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private static bool IsSeparator(char c)
|
|
{
|
|
return c == '?' || c == '/' || c == '#';
|
|
}
|
|
|
|
private static bool IsStrictlyPrefixWithSeparator(string value, string prefix)
|
|
{
|
|
var prefixLength = prefix.Length;
|
|
if (value.Length > prefixLength)
|
|
{
|
|
return value.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)
|
|
&& (
|
|
prefixLength == 0
|
|
|| IsSeparator(prefix[prefixLength - 1])
|
|
|| IsSeparator(value[prefixLength])
|
|
);
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override async Task SetParametersAsync(ParameterView parameters)
|
|
{
|
|
var expandedChanged = parameters.DidParameterChange(nameof(Expanded), Expanded);
|
|
|
|
var selectedChanged = parameters.DidParameterChange(nameof(Selected), Selected);
|
|
|
|
await base.SetParametersAsync(parameters);
|
|
|
|
if (expandedChanged)
|
|
{
|
|
expanded = Expanded;
|
|
}
|
|
|
|
if (selectedChanged)
|
|
{
|
|
selected = Selected;
|
|
|
|
if (selected)
|
|
{
|
|
EnsureVisible();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handles the Click event.
|
|
/// </summary>
|
|
/// <param name="args">The <see cref="MouseEventArgs"/> instance containing the event data.</param>
|
|
public async Task OnClick(MouseEventArgs args)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(args);
|
|
if (Parent != null)
|
|
{
|
|
var eventArgs = new MenuItemEventArgs
|
|
{
|
|
Text = Text,
|
|
Path = Path,
|
|
Value = Value,
|
|
AltKey = args.AltKey,
|
|
Button = args.Button,
|
|
Buttons = args.Buttons,
|
|
ClientX = args.ClientX,
|
|
ClientY = args.ClientY,
|
|
CtrlKey = args.CtrlKey,
|
|
Detail = args.Detail,
|
|
MetaKey = args.MetaKey,
|
|
ScreenX = args.ScreenX,
|
|
ScreenY = args.ScreenY,
|
|
ShiftKey = args.ShiftKey,
|
|
Type = args.Type,
|
|
};
|
|
await Parent.Click.InvokeAsync(eventArgs);
|
|
|
|
if (Click.HasDelegate)
|
|
{
|
|
await Click.InvokeAsync(eventArgs);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void Dispose()
|
|
{
|
|
base.Dispose();
|
|
|
|
if (NavigationManager != null)
|
|
{
|
|
NavigationManager.LocationChanged -= OnLocationChanged;
|
|
}
|
|
|
|
if (Parent != null)
|
|
{
|
|
Parent.RemoveItem(this);
|
|
}
|
|
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
}
|
|
}
|