Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show a Modal using an object as modal parameters #583

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/Blazored.Modal/Blazored.Modal.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components" Version="8.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="8.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.JSInterop.WebAssembly" Version="8.0.2" />
</ItemGroup>
Expand Down
52 changes: 49 additions & 3 deletions src/Blazored.Modal/BlazoredModal.razor
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
@using System.Collections.ObjectModel
@using Blazored.Modal.Configuration
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.Extensions.Options

@inject NavigationManager NavigationManager
@inject IJSRuntime JsRuntime
Expand Down Expand Up @@ -37,6 +39,14 @@

internal event Action? OnModalClosed;

public BlazoredModal()
{
if (baseUri != null)
return;
// creates a lazy object that will determine the baseUri from the first request
baseUri = new(() => (NavigationManager.BaseUri ?? "./") + "_content/Blazored.Modal/");
}

protected override void OnInitialized()
{
if (CascadedModalService == null)
Expand All @@ -63,13 +73,49 @@
_globalModalOptions.ActivateFocusTrap = ActivateFocusTrap;
}

protected override async Task OnAfterRenderAsync(bool firstRender)
#region JavaScript path customization
// This allows the javascript to be located on a CDN or shared location for server caching
// and/or sharing between multiple apps, or that the path can be customized for the app
// automatically base on the BaseUri found in NavigationManager

[Inject]
private IOptions<Settings> Settings { get; set; } = null!;

// stores the basePath for all instances
private static string? sharedBasePath;

// default location for the JS file
private const string DefaultLocation = "./";

// a lazy object that will hold the baseUri
private static Lazy<string>? baseUri;

// gets the baseUri from the lazy object
private string? BaseUri => baseUri?.Value;

// gets the location from the settings
private string? Location => Settings.Value.JsPath;

private string JsPath
{
if (firstRender)
get
{
_styleFunctions = await JsRuntime.InvokeAsync<IJSObjectReference>("import", "./_content/Blazored.Modal/BlazoredModal.razor.js");
// if the basePath is already set, return it
var basePath = sharedBasePath;
if (basePath != null)
return basePath;
// if the location is null, use the default location
var str = Location ?? BaseUri;
return sharedBasePath = str + "BlazoredModal.razor.js";
}
}
#endregion

protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
_styleFunctions = await JsRuntime.InvokeAsync<IJSObjectReference>("import", JsPath);
}

internal async Task CloseInstance(ModalReference? modal, ModalResult result)
{
Expand Down
4 changes: 4 additions & 0 deletions src/Blazored.Modal/BlazoredModalInstance.razor
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ else
{
<h3 class="bm-title">@Title</h3>
}
@if (HeaderContent != null)
{
@HeaderContent
}
@if (!HideCloseButton)
{
<button type="button" class="bm-close" aria-label="close" @onclick="() => CancelAsync()" @attributes="@_closeBtnAttributes">
Expand Down
14 changes: 14 additions & 0 deletions src/Blazored.Modal/BlazoredModalInstance.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,19 @@ public partial class BlazoredModalInstance : IDisposable
public bool UseCustomLayout { get; set; }
public FocusTrap? FocusTrap { get; set; }

/// <summary>
/// Get
/// </summary>
public RenderFragment? HeaderContent
{
get => _headerContent;
set
{
_headerContent = value;
FocusTrap?.Refresh();
}
}


[SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "This is assigned in Razor code and isn't currently picked up by the tooling.")]
private ElementReference _modalReference;
Expand All @@ -35,6 +48,7 @@ public partial class BlazoredModalInstance : IDisposable

// Temporarily add a tabindex of -1 to the close button so it doesn't get selected as the first element by activateFocusTrap
private readonly Dictionary<string, object> _closeBtnAttributes = new() { { "tabindex", "-1" } };
private RenderFragment? _headerContent;

protected override bool ShouldRender()
{
Expand Down
13 changes: 10 additions & 3 deletions src/Blazored.Modal/BlazoredModalInstance.razor.css
Original file line number Diff line number Diff line change
Expand Up @@ -56,24 +56,31 @@

.bm-header {
display: flex;
align-items: flex-start;
align-items: center;
justify-content: space-between;
padding: 0 0 2rem 0;
}

.bm-title {
margin-bottom: 0;
flex-grow: 1;
}

.bm-close {
padding: 1rem;
margin: -1rem -1rem -1rem auto;
margin: 0px !important;
padding: 0px !important;
background-color: transparent;
border: 0;
-webkit-appearance: none;
cursor: pointer;
font-size: 1.5rem;
font-weight: bold;
width: 2rem;
}

.bm-close > * {
position: relative;
top: 1px;
}

.position-topleft .blazored-modal {
Expand Down
21 changes: 21 additions & 0 deletions src/Blazored.Modal/Configuration/Settings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Blazored.Modal.Configuration
{
public class Settings
{
public Settings()
{

}

/// <summary>
/// Gets or sets the default JavaScript path where to locate BlazoredModal.razor.js, without including the name of the file.
/// </summary>
public string? JsPath { get; set; }
}
}
15 changes: 14 additions & 1 deletion src/Blazored.Modal/FocusTrap.razor
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,15 @@
[Parameter] public RenderFragment ChildContent { get; set; } = default!;
[Parameter] public bool IsActive { get; set; }

private bool _shouldRender = false;

protected override bool ShouldRender()
=> false;
{
var result = _shouldRender;
if (_shouldRender)
_shouldRender = false;
return result;
}

protected override async Task OnAfterRenderAsync(bool firstRender)
{
Expand All @@ -28,6 +35,12 @@
}
}

internal void Refresh()
{
_shouldRender = true;
StateHasChanged();
}

internal async Task SetFocus()
=> await _startFirst.FocusAsync();

Expand Down
23 changes: 23 additions & 0 deletions src/Blazored.Modal/ModalHeader.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
@code {

[Parameter]
public string? Title { get; set; }

[Parameter]
public RenderFragment? ChildContent { get; set; }

[CascadingParameter]
public BlazoredModalInstance? ModalInstance { get; set; }

protected override void OnInitialized()
{
base.OnInitialized();
if (ModalInstance != null && !string.IsNullOrEmpty(Title))
ModalInstance.Title = Title;

if (ChildContent!=null && ModalInstance != null)
{
ModalInstance.HeaderContent = ChildContent;
}
}
}
25 changes: 22 additions & 3 deletions src/Blazored.Modal/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,29 @@
using Blazored.Modal.Services;
using Blazored.Modal.Configuration;
using Blazored.Modal.Services;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;

namespace Blazored.Modal;

public static class ServiceCollectionExtensions
{
public static IServiceCollection AddBlazoredModal(this IServiceCollection services)
public static IServiceCollection AddBlazoredModal2(this IServiceCollection services)
=> services.AddScoped<IModalService, ModalService>();
}

public static IServiceCollection AddBlazoredModal(this IServiceCollection services, Action<Settings>? configure = null)
{
services.AddScoped<IModalService, ModalService>();
OptionsBuilder<Settings> optionsBuilder = services.AddOptions<Settings>();
optionsBuilder.Configure(c => c.JsPath = null);
if (configure != null)
optionsBuilder.Configure(configure);
optionsBuilder.PostConfigure<IConfiguration>((settings, configuration) =>
{
configuration.GetSection("BlazorFluentUI").Bind(settings);
});
return services;
}

}

9 changes: 9 additions & 0 deletions src/Blazored.Modal/Services/IModalService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,15 @@ public interface IModalService
/// <param name="options">Options to configure the modal.</param>
IModalReference Show<TComponent>(string title, ModalParameters parameters, ModalOptions options) where TComponent : IComponent;

/// <summary>
/// Shows a modal containing a <typeparamref name="TComponent"/> with the specified <paramref name="title"/>,
/// <paramref name="parameters"/> and <paramref name="options"/>.
/// </summary>
/// <param name="title">Modal title.</param>
/// <param name="parameters">Key/Value collection of parameters to pass to component being displayed.</param>
/// <param name="options">Options to configure the modal.</param>
IModalReference Show<TComponent>(string title, object? parameters, ModalOptions? options = null) where TComponent : IComponent;

/// <summary>
/// Shows a modal containing a <paramref name="component"/>.
/// </summary>
Expand Down
27 changes: 27 additions & 0 deletions src/Blazored.Modal/Services/ModalService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,33 @@ public IModalReference Show<T>(string title, ModalParameters parameters) where T
public IModalReference Show<T>(string title, ModalParameters parameters, ModalOptions options) where T : IComponent
=> Show(typeof(T), title, parameters, options);

/// <summary>
/// Shows the modal with the component type using the specified <paramref name="title"/>,
/// passing the specified <paramref name="parameters"/> and setting a custom CSS style.
/// </summary>
/// <param name="title">Modal title.</param>
/// <param name="parameters">Key/Value collection of parameters to pass to component being displayed.</param>
/// <param name="options">Options to configure the modal.</param>
public IModalReference Show<T>(string title, object? parameters, ModalOptions? options = null) where T : IComponent
=> Show(typeof(T), title, GetParameters(parameters), options ?? new ModalOptions());

private ModalParameters GetParameters(object? parameters)
{
var modalParameters = new ModalParameters();
if (parameters == null)
return modalParameters;

var properties = parameters.GetType().GetProperties();
foreach (var property in properties)
{
var value = property.GetValue(parameters);
if (value != null)
modalParameters.Add(property.Name, value);
}

return modalParameters;
}

/// <summary>
/// Shows the modal with the specific component type.
/// </summary>
Expand Down