From 528afe6aee692489213b2e9ca69deaeaaf2c59fb Mon Sep 17 00:00:00 2001 From: capdiem Date: Tue, 26 Dec 2023 11:19:46 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=86=95=20feat:=20test=20GetRelativeUriWit?= =?UTF-8?q?hQueryParameters?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../InteractiveTrigger/MInteractivePopup2.cs | 56 +++++++ .../MInteractiveTrigger2.razor | 24 +++ .../MInteractiveTriggerBase2.razor | 52 +++++++ .../MInteractiveTriggerBase2.razor.cs | 141 ++++++++++++++++++ .../MInteractiveTriggers2.razor | 33 ++++ .../Components/Layout/MainLayout.razor | 14 +- .../Components/Layout/MobileAppBar.razor | 2 +- .../Components/Shared/MenuableTitle.razor | 2 +- .../NavigationManagerExtensions.cs | 31 ++++ 9 files changed, 346 insertions(+), 9 deletions(-) create mode 100644 src/MASA.OfficialWebsite.WebApp/Components/InteractiveTrigger/MInteractivePopup2.cs create mode 100644 src/MASA.OfficialWebsite.WebApp/Components/InteractiveTrigger/MInteractiveTrigger2.razor create mode 100644 src/MASA.OfficialWebsite.WebApp/Components/InteractiveTrigger/MInteractiveTriggerBase2.razor create mode 100644 src/MASA.OfficialWebsite.WebApp/Components/InteractiveTrigger/MInteractiveTriggerBase2.razor.cs create mode 100644 src/MASA.OfficialWebsite.WebApp/Components/InteractiveTrigger/MInteractiveTriggers2.razor create mode 100644 src/MASA.OfficialWebsite.WebApp/NavigationManagerExtensions.cs diff --git a/src/MASA.OfficialWebsite.WebApp/Components/InteractiveTrigger/MInteractivePopup2.cs b/src/MASA.OfficialWebsite.WebApp/Components/InteractiveTrigger/MInteractivePopup2.cs new file mode 100644 index 0000000..204f0e2 --- /dev/null +++ b/src/MASA.OfficialWebsite.WebApp/Components/InteractiveTrigger/MInteractivePopup2.cs @@ -0,0 +1,56 @@ +using BlazorComponent; +using Microsoft.AspNetCore.Components; +using Microsoft.JSInterop; + +namespace Masa.Blazor; + +/// +/// A abstract class for components that use the +/// or component as a trigger. +/// +public abstract class MInteractivePopup : ComponentBase, IOutsideClickJsCallback, IAsyncDisposable +{ + [Inject] private OutsideClickJSModule OutsideClickJSModule { get; set; } = null!; + + [Inject] protected NavigationManager NavigationManager { get; set; } = null!; + + /// + /// The query name of url for trigger a interactive popup. + /// + [Parameter] public string QueryName { get; set; } = null!; + + /// + /// The activator selector. + /// + [Parameter] public string Activator { get; set; } = null!; + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + await OutsideClickJSModule.InitializeAsync(this, Activator, ".m-interactive-trigger__popup"); + } + } + + public async Task HandleOnOutsideClickAsync() + { + // TODO: https://github.com/dotnet/aspnetcore/issues/52705 + await Task.Delay(100); + NavigationManager.NavigateWithQueryParameter(QueryName, (string?)null); + } + + async ValueTask IAsyncDisposable.DisposeAsync() + { + try + { + await DisposeAsync(); + await OutsideClickJSModule.UnbindAndDisposeAsync(); + } + catch (JSDisconnectedException) + { + // ignore + } + } + + protected virtual Task DisposeAsync() => Task.CompletedTask; +} diff --git a/src/MASA.OfficialWebsite.WebApp/Components/InteractiveTrigger/MInteractiveTrigger2.razor b/src/MASA.OfficialWebsite.WebApp/Components/InteractiveTrigger/MInteractiveTrigger2.razor new file mode 100644 index 0000000..00265ba --- /dev/null +++ b/src/MASA.OfficialWebsite.WebApp/Components/InteractiveTrigger/MInteractiveTrigger2.razor @@ -0,0 +1,24 @@ +@namespace Masa.Blazor +@typeparam TValue +@using Microsoft.AspNetCore.Components.Rendering +@inherits MInteractiveTriggerBase2 + +@base.BuildRenderTree + +@code { + + protected override string ComponentName => nameof(MInteractiveTrigger); + + protected override void RenderLinkContent(RenderTreeBuilder __builder) + { + var value = IsInteractive ? default : InteractiveValue; + + @RenderSingleLink(QueryName, value, InteractiveValue) + } + + protected override bool CheckInteractive() + { + return EqualityComparer.Default.Equals(QueryValue, InteractiveValue); + } + +} diff --git a/src/MASA.OfficialWebsite.WebApp/Components/InteractiveTrigger/MInteractiveTriggerBase2.razor b/src/MASA.OfficialWebsite.WebApp/Components/InteractiveTrigger/MInteractiveTriggerBase2.razor new file mode 100644 index 0000000..95a0255 --- /dev/null +++ b/src/MASA.OfficialWebsite.WebApp/Components/InteractiveTrigger/MInteractiveTriggerBase2.razor @@ -0,0 +1,52 @@ +@namespace Masa.Blazor +@using Microsoft.AspNetCore.Components.Rendering +@inherits CssProviderComponentBase +@typeparam TValue +@typeparam TInteractiveValue +@inject NavigationManager NavigationManager + +
+ @RenderLinkContent + @RenderPopupContent +
+ +@code { + + protected abstract void RenderLinkContent(RenderTreeBuilder __builder); + + protected RenderFragment RenderSingleLink(string name, TValue? value, TValue? interactiveValue) => __builder => + { + + @ChildContent?.Invoke(interactiveValue) + + }; + + private void RenderPopupContent(RenderTreeBuilder __builder) + { + @if (WithPopup) + { +
+ @RenderInteractiveComponent +
+ } + else + { + @RenderInteractiveComponent + } + } + + private void RenderInteractiveComponent(RenderTreeBuilder __builder) + { + @if (IsInteractive) + { + + } + } + +} diff --git a/src/MASA.OfficialWebsite.WebApp/Components/InteractiveTrigger/MInteractiveTriggerBase2.razor.cs b/src/MASA.OfficialWebsite.WebApp/Components/InteractiveTrigger/MInteractiveTriggerBase2.razor.cs new file mode 100644 index 0000000..8f8a24b --- /dev/null +++ b/src/MASA.OfficialWebsite.WebApp/Components/InteractiveTrigger/MInteractiveTriggerBase2.razor.cs @@ -0,0 +1,141 @@ +using BlazorComponent; +using Microsoft.AspNetCore.Components; + +namespace Masa.Blazor; + +using BemIt; + +/// +/// A abstract class of interactive trigger component. +/// +/// +/// + +#if NET8_0_OR_GREATER +[StreamRendering] +#endif + +public abstract partial class MInteractiveTriggerBase2 : CssProviderComponentBase +{ + [Parameter] public bool DisableLinkOnInteractive { get; set; } + + /// + /// The name of query parameter. + /// + [Parameter] [EditorRequired] public string QueryName { get; set; } = null!; + + /// + /// The value of query parameter. + /// + [Parameter] public TValue? QueryValue { get; set; } + + /// + /// A value that is used to determine whether the component is interactive. + /// + [Parameter] [EditorRequired] public virtual TInteractiveValue InteractiveValue { get; set; } = default!; + + /// + /// The of interactive component. + /// + [Parameter] [EditorRequired] public Type InteractiveComponentType { get; set; } = null!; + + /// + /// The parameters of interactive component. + /// + [Parameter] public IDictionary? InteractiveComponentParameters { get; set; } + + [Parameter] public RenderFragment? ChildContent { get; set; } + + /// + /// Determines whether a built-in popup is needed to display the interactive component. + /// + [Parameter] public bool WithPopup { get; set; } + + [Parameter] public string? PopupClass { get; set; } + + [Parameter] public string? PopupStyle { get; set; } + + /// + /// The top position of popup. + /// + [Parameter] public int? Top { get; set; } + + /// + /// The right position of popup. + /// + [Parameter] public int? Right { get; set; } + + /// + /// The bottom position of popup. + /// + [Parameter] public int? Bottom { get; set; } + + /// + /// The left position of popup. + /// + [Parameter] public int? Left { get; set; } + + private bool _active; + + private string ElementId => $"_int_trigger_{QueryName}"; + + protected virtual string ComponentName => nameof(MInteractiveTriggerBase); + + protected string? Activator => $"#{ElementId} > a"; + + public bool IsInteractive { get; private set; } + + private IDictionary ComputedInteractiveComponentParameters + { + get + { + InteractiveComponentParameters ??= new Dictionary(); + + if (InteractiveComponentType.IsAssignableTo(typeof(MInteractivePopup))) + { + InteractiveComponentParameters.TryAdd(nameof(QueryName), QueryName); + InteractiveComponentParameters.TryAdd(nameof(Activator), Activator); + } + + return InteractiveComponentParameters; + } + } + + protected override async Task OnInitializedAsync() + { + QueryName.ThrowIfNull(ComponentName); + InteractiveValue.ThrowIfNull(ComponentName); + InteractiveComponentType.ThrowIfNull(ComponentName); + + IsInteractive = CheckInteractive(); + + if (IsInteractive) + { + // The html generated from the server is rendered on the page + // before a short delay. With delay and [StreamRendering], + // set active to true, there will be a short transition animation. + + await Task.Delay(1); + + _active = true; + } + } + + protected abstract bool CheckInteractive(); + + protected override void SetComponentCss() + { + CssProvider.UseBem("m-interactive-trigger") + .Element("link", + css => { css.Modifiers(m => m.Modifier("disabled", IsInteractive && DisableLinkOnInteractive)); }) + .Element("popup", css => { css.Modifiers(m => m.Modifier("active", _active).AddClass(PopupClass)); }, + style => + { + style.AddIf($"top: {Top}px", () => Top.HasValue) + .AddIf($"right: {Right}px", () => Right.HasValue) + .AddIf($"bottom: {Bottom}px", () => Bottom.HasValue) + .AddIf($"left: {Left}px", () => Left.HasValue) + .Add(PopupStyle); + }); + } +} diff --git a/src/MASA.OfficialWebsite.WebApp/Components/InteractiveTrigger/MInteractiveTriggers2.razor b/src/MASA.OfficialWebsite.WebApp/Components/InteractiveTrigger/MInteractiveTriggers2.razor new file mode 100644 index 0000000..6e1e159 --- /dev/null +++ b/src/MASA.OfficialWebsite.WebApp/Components/InteractiveTrigger/MInteractiveTriggers2.razor @@ -0,0 +1,33 @@ +@namespace Masa.Blazor +@typeparam TValue +@using Microsoft.AspNetCore.Components.Rendering +@inherits MInteractiveTriggerBase2> + +@base.BuildRenderTree + +@code { + + protected override string ComponentName => nameof(MInteractiveTriggers); + + protected override void OnInitialized() + { + base.OnInitialized(); + + InteractiveValue.ThrowIfNull(ComponentName); + } + + protected override void RenderLinkContent(RenderTreeBuilder __builder) + { + foreach (var interactiveValue in InteractiveValue!) + { + var value = EqualityComparer.Default.Equals(QueryValue, interactiveValue) ? default : interactiveValue; + @RenderSingleLink(QueryName, value, interactiveValue) + } + } + + protected override bool CheckInteractive() + { + return InteractiveValue!.Any(val => EqualityComparer.Default.Equals(QueryValue, val)); + } + +} diff --git a/src/MASA.OfficialWebsite.WebApp/Components/Layout/MainLayout.razor b/src/MASA.OfficialWebsite.WebApp/Components/Layout/MainLayout.razor index 3b0f373..d1c2580 100644 --- a/src/MASA.OfficialWebsite.WebApp/Components/Layout/MainLayout.razor +++ b/src/MASA.OfficialWebsite.WebApp/Components/Layout/MainLayout.razor @@ -128,19 +128,19 @@ 关注我们 - + - +