mirror of
https://github.com/radzenhq/radzen-blazor.git
synced 2026-02-04 05:35:44 +00:00
193 lines
7.3 KiB
C#
193 lines
7.3 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Reflection;
|
|
using System.Threading.Tasks;
|
|
using System.Web;
|
|
using Microsoft.AspNetCore.Components;
|
|
using Microsoft.AspNetCore.Components.Routing;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Options;
|
|
|
|
namespace Radzen
|
|
{
|
|
/// <summary>
|
|
/// Options for the <see cref="QueryStringThemeService" />.
|
|
/// </summary>
|
|
public class QueryStringThemeServiceOptions
|
|
{
|
|
/// <summary>
|
|
/// Gets or sets the query string parameter for the theme.
|
|
/// </summary>
|
|
public string ThemeParameter { get; set; } = "theme";
|
|
/// <summary>
|
|
/// Gets or sets the query string parameter for the wcag compatible color theme.
|
|
/// </summary>
|
|
public string WcagParameter { get; set; } = "wcag";
|
|
|
|
/// <summary>
|
|
/// Gets or sets the query string parameter for the right to left theme.
|
|
/// </summary>
|
|
public string RightToLeftParameter { get; set; } = "rtl";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Persist the current theme in the query string. Requires <see cref="ThemeService" /> to be registered in the DI container.
|
|
/// </summary>
|
|
public class QueryStringThemeService : IDisposable
|
|
{
|
|
private readonly NavigationManager navigationManager;
|
|
private readonly ThemeService themeService;
|
|
|
|
#if NET7_0_OR_GREATER
|
|
private readonly IDisposable? registration;
|
|
#endif
|
|
private readonly QueryStringThemeServiceOptions? options;
|
|
private readonly PropertyInfo? hasAttachedJSRuntimeProperty;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="QueryStringThemeService" /> class.
|
|
/// </summary>
|
|
public QueryStringThemeService(NavigationManager navigationManager, ThemeService themeService, IOptions<QueryStringThemeServiceOptions> options)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(navigationManager);
|
|
ArgumentNullException.ThrowIfNull(themeService);
|
|
ArgumentNullException.ThrowIfNull(options);
|
|
|
|
this.navigationManager = navigationManager;
|
|
this.themeService = themeService;
|
|
this.options = options.Value;
|
|
|
|
hasAttachedJSRuntimeProperty = navigationManager.GetType().GetProperty("HasAttachedJSRuntime");
|
|
|
|
var state = GetStateFromQueryString(navigationManager.Uri);
|
|
|
|
if (state.theme != null && RequiresChange(state))
|
|
{
|
|
themeService.SetTheme(new ThemeOptions
|
|
{
|
|
Theme = state.theme,
|
|
Wcag = state.wcag,
|
|
RightToLeft = state.rightToLeft,
|
|
TriggerChange = true
|
|
});
|
|
}
|
|
|
|
themeService.ThemeChanged += OnThemeChanged;
|
|
|
|
#if NET7_0_OR_GREATER
|
|
try
|
|
{
|
|
registration = navigationManager.RegisterLocationChangingHandler(OnLocationChanging);
|
|
}
|
|
catch (NotSupportedException)
|
|
{
|
|
// HttpNavigationManager does not support that
|
|
}
|
|
#endif
|
|
}
|
|
|
|
private bool RequiresChange((string? theme, bool? wcag, bool? rightToLeft) state) =>
|
|
(state.theme != null && !string.Equals(themeService.Theme, state.theme, StringComparison.OrdinalIgnoreCase)) ||
|
|
themeService.Wcag != state.wcag || themeService.RightToLeft != state.rightToLeft;
|
|
|
|
#if NET7_0_OR_GREATER
|
|
private ValueTask OnLocationChanging(LocationChangingContext context)
|
|
{
|
|
var state = GetStateFromQueryString(context.TargetLocation);
|
|
|
|
if (RequiresChange(state))
|
|
{
|
|
context.PreventNavigation();
|
|
|
|
navigationManager.NavigateTo(GetUriWithStateQueryParameters(context.TargetLocation), replace: true);
|
|
}
|
|
|
|
return ValueTask.CompletedTask;
|
|
}
|
|
#endif
|
|
|
|
private (string? theme, bool? wcag, bool? rightToLeft) GetStateFromQueryString(string uri)
|
|
{
|
|
var queryString = uri.Contains('?', StringComparison.Ordinal) ? uri[(uri.IndexOf('?', StringComparison.Ordinal) + 1)..] : string.Empty;
|
|
|
|
var query = HttpUtility.ParseQueryString(queryString.Contains('#', StringComparison.Ordinal) ? queryString[..queryString.IndexOf('#', StringComparison.Ordinal)] : queryString);
|
|
|
|
bool? wcag = options?.WcagParameter != null ? (query.Get(options.WcagParameter) != null ? query.Get(options.WcagParameter) == "true" : null) : null;
|
|
bool? rtl = options?.RightToLeftParameter != null ? (query.Get(options.RightToLeftParameter) != null ? query.Get(options.RightToLeftParameter) == "true" : null) : null;
|
|
|
|
return (query?.Get(options?.ThemeParameter), wcag, rtl);
|
|
}
|
|
|
|
private string GetUriWithStateQueryParameters(string uri)
|
|
{
|
|
var parameters = new Dictionary<string, object?>
|
|
{
|
|
{ options?.ThemeParameter ?? string.Empty, themeService?.Theme?.ToLowerInvariant() ?? string.Empty },
|
|
};
|
|
|
|
if (themeService?.Wcag != null && options?.WcagParameter != null)
|
|
{
|
|
parameters.Add(options.WcagParameter, themeService.Wcag.Value ? "true" : "false");
|
|
}
|
|
|
|
if (themeService?.RightToLeft != null && options?.RightToLeftParameter != null)
|
|
{
|
|
parameters.Add(options.RightToLeftParameter, themeService.RightToLeft.Value ? "true" : "false");
|
|
}
|
|
|
|
return navigationManager.GetUriWithQueryParameters(uri, new Dictionary<string, object?>(parameters));
|
|
}
|
|
|
|
private void OnThemeChanged()
|
|
{
|
|
|
|
if (hasAttachedJSRuntimeProperty is null || hasAttachedJSRuntimeProperty.GetValue(navigationManager) is true)
|
|
{
|
|
var state = GetStateFromQueryString(navigationManager.Uri);
|
|
|
|
navigationManager.NavigateTo(GetUriWithStateQueryParameters(navigationManager.Uri),
|
|
forceLoad: state.rightToLeft != themeService.RightToLeft);
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void Dispose()
|
|
{
|
|
themeService.ThemeChanged -= OnThemeChanged;
|
|
|
|
#if NET7_0_OR_GREATER
|
|
registration?.Dispose();
|
|
#endif
|
|
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Extension methods to register the <see cref="QueryStringThemeService" />.
|
|
/// </summary>
|
|
public static class QueryStringThemeServiceCollectionExtensions
|
|
{
|
|
/// <summary>
|
|
/// Adds the <see cref="QueryStringThemeService" /> to the service collection.
|
|
/// </summary>
|
|
public static IServiceCollection AddRadzenQueryStringThemeService(this IServiceCollection services)
|
|
{
|
|
services.AddOptions<QueryStringThemeServiceOptions>();
|
|
services.AddScoped<QueryStringThemeService>();
|
|
|
|
return services;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds the <see cref="QueryStringThemeService" /> to the service collection with the specified condiguration.
|
|
/// </summary>
|
|
public static IServiceCollection AddRadzenQueryStringThemeService(this IServiceCollection services, Action<QueryStringThemeServiceOptions> configure)
|
|
{
|
|
services.Configure(configure);
|
|
services.AddScoped<QueryStringThemeService>();
|
|
|
|
return services;
|
|
}
|
|
}
|
|
} |