From 688277c7612bb7b1a6813fc9fdd0336ba1b1de1c Mon Sep 17 00:00:00 2001 From: Florian Gilde Date: Mon, 11 May 2026 14:05:21 +0200 Subject: [PATCH 1/5] create daterange slider --- .../Tests/Components/MudExRangeSliderTests.cs | 35 ++- .../Components/MudExDateRangeSlider.cs | 102 +++++++ .../Components/MudExDateTimeRangeSlider.cs | 102 +++++++ .../Components/MudExRangeSlider.razor.cs | 62 ++++- .../Components/MudExRangeSliderDefaults.razor | 89 +++++++ .../Components/MudExTimeRangeSlider.cs | 93 +++++++ .../Components/RangeSliderModes.cs | 58 ++++ .../Helper/RangeSliderTicks.cs | 249 ++++++++++++++++++ .../css/components/_mudexrangeslider.scss | 87 ++++++ .../wwwroot/docs/MudBlazor.Extensions.xml | 183 +++++++++++++ .../TypedDateRangeSliderExample.razor | 31 +++ .../TypedDateTimeRangeSliderExample.razor | 31 +++ .../TypedTimeRangeSliderExample.razor | 30 +++ .../Pages/Page_RangeSlider.razor | 61 +++++ .../TypedDateRangeSliderExample.html | 34 +++ .../TypedDateRangeSliderExample.md | 34 +++ .../TypedDateTimeRangeSliderExample.html | 34 +++ .../TypedDateTimeRangeSliderExample.md | 34 +++ .../TypedTimeRangeSliderExample.html | 33 +++ .../TypedTimeRangeSliderExample.md | 33 +++ .../MainSample.WebAssembly/wwwroot/index.html | 22 +- 21 files changed, 1421 insertions(+), 16 deletions(-) create mode 100644 MudBlazor.Extensions/Components/MudExDateRangeSlider.cs create mode 100644 MudBlazor.Extensions/Components/MudExDateTimeRangeSlider.cs create mode 100644 MudBlazor.Extensions/Components/MudExRangeSliderDefaults.razor create mode 100644 MudBlazor.Extensions/Components/MudExTimeRangeSlider.cs create mode 100644 MudBlazor.Extensions/Components/RangeSliderModes.cs create mode 100644 MudBlazor.Extensions/Helper/RangeSliderTicks.cs create mode 100644 Samples/MainSample.WebAssembly/Examples/RangeSlider/TypedDateRangeSliderExample.razor create mode 100644 Samples/MainSample.WebAssembly/Examples/RangeSlider/TypedDateTimeRangeSliderExample.razor create mode 100644 Samples/MainSample.WebAssembly/Examples/RangeSlider/TypedTimeRangeSliderExample.razor create mode 100644 Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedDateRangeSliderExample.html create mode 100644 Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedDateRangeSliderExample.md create mode 100644 Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedDateTimeRangeSliderExample.html create mode 100644 Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedDateTimeRangeSliderExample.md create mode 100644 Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedTimeRangeSliderExample.html create mode 100644 Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedTimeRangeSliderExample.md diff --git a/MudBlazor.Extensions.Tests/UnitTests/Tests/Components/MudExRangeSliderTests.cs b/MudBlazor.Extensions.Tests/UnitTests/Tests/Components/MudExRangeSliderTests.cs index b5e19477..c7e69998 100644 --- a/MudBlazor.Extensions.Tests/UnitTests/Tests/Components/MudExRangeSliderTests.cs +++ b/MudBlazor.Extensions.Tests/UnitTests/Tests/Components/MudExRangeSliderTests.cs @@ -156,9 +156,42 @@ public void WithoutMinLength_ThumbsShouldMoveFreelyWithinSizeRange() // Should be able to set start all the way to end setStartMethod?.Invoke(instance, new object[] { 8, true }); - + // Assert Assert.Equal(8, instance.Value.Start); Assert.Equal(8, instance.Value.End); } + + /// + /// Regression: AutoZoomRangeMultiplier must not crash when the derived bounds exceed the natural T-domain + /// (e.g. TimeOnly, which is limited to 0..23:59:59). The auto-range should be clamped to the largest valid extent. + /// + [Fact] + public void AutoZoomRangeMultiplier_ShouldClamp_ForTimeOnly() + { + // Arrange + using var context = new TestContext(); + context.Services.AddMudServicesWithExtensions(); + context.JSInterop.Mode = JSRuntimeMode.Loose; + + // 8h..18h with multiplier=3 would normally extend to -7h..33h, both invalid for TimeOnly. + var sizeRange = new MudExRange(new TimeOnly(8, 0), new TimeOnly(18, 0)); + var initial = new MudExRange(new TimeOnly(9, 0), new TimeOnly(17, 0)); + + // Act – render must not throw. + var cut = context.Render>(parameters => parameters + .Add(p => p.SizeRange, sizeRange) + .Add(p => p.Value, initial) + .Add(p => p.EnableMouseWheelZoom, true) + .Add(p => p.AutoZoomRangeMultiplier, 3.0) + ); + + // Assert – auto-derived range got clamped to the TimeOnly domain. + var effective = cut.Instance.EffectiveAbsoluteSizeRange; + Assert.NotNull(effective); + Assert.True(effective!.Start <= sizeRange.Start, "auto-zoom left bound should be at or before SizeRange.Start"); + Assert.True(effective.End >= sizeRange.End, "auto-zoom right bound should be at or after SizeRange.End"); + Assert.True(effective.Start >= TimeOnly.MinValue, "left bound must be a valid TimeOnly"); + Assert.True(effective.End <= TimeOnly.MaxValue, "right bound must be a valid TimeOnly"); + } } diff --git a/MudBlazor.Extensions/Components/MudExDateRangeSlider.cs b/MudBlazor.Extensions/Components/MudExDateRangeSlider.cs new file mode 100644 index 00000000..0cbf57ba --- /dev/null +++ b/MudBlazor.Extensions/Components/MudExDateRangeSlider.cs @@ -0,0 +1,102 @@ +using System.Globalization; +using Microsoft.AspNetCore.Components; +using MudBlazor.Extensions.Helper; +using Nextended.Core.Contracts; +using Nextended.Core.Types; + +namespace MudBlazor.Extensions.Components; + +/// +/// Range slider specialized for with theme-aware defaults and a +/// that controls snap behavior (Day, Week, Month, Quarter, Year). Inherits everything from – +/// any property of the base class (SizeRange, Value, MinLength, …) can be set on this component as well. +/// +public class MudExDateRangeSlider : MudExRangeSlider +{ + private bool _userTrackTemplate; + private bool _userSelectionTemplate; + private bool _userThumbStartTemplate; + private bool _userThumbEndTemplate; + private bool _userStepResolver; + private bool _userStartFormatter; + private bool _userEndFormatter; + + private DateRangeSliderMode? _cachedTemplateMode; + private RenderFragment>? _cachedTrackTemplate; + private RenderFragment>? _cachedSelectionTemplate; + private RenderFragment>? _cachedThumbTemplate; + + /// + /// Snap resolution for start/end values. Switching this also adjusts the default labels and step resolver. + /// + [Parameter] public DateRangeSliderMode Mode { get; set; } = DateRangeSliderMode.Day; + + /// + public override async Task SetParametersAsync(ParameterView parameters) + { + foreach (var p in parameters) + { + switch (p.Name) + { + case nameof(TrackTemplate): _userTrackTemplate = true; break; + case nameof(SelectionTemplate): _userSelectionTemplate = true; break; + case nameof(ThumbStartTemplate): _userThumbStartTemplate = true; break; + case nameof(ThumbEndTemplate): _userThumbEndTemplate = true; break; + case nameof(StepResolver): _userStepResolver = true; break; + case nameof(ToStartStringFunc): _userStartFormatter = true; break; + case nameof(ToEndStringFunc): _userEndFormatter = true; break; + } + } + await base.SetParametersAsync(parameters); + } + + /// + protected override void OnParametersSet() + { + EnsureTemplateCache(Mode); + + if (!_userStepResolver) + StepResolver = ResolverFor(Mode); + if (!_userStartFormatter) + ToStartStringFunc = r => Format(r.Start, Mode); + if (!_userEndFormatter) + ToEndStringFunc = r => Format(r.End, Mode); + if (!_userTrackTemplate) + TrackTemplate = _cachedTrackTemplate; + if (!_userSelectionTemplate) + SelectionTemplate = _cachedSelectionTemplate; + if (!_userThumbStartTemplate) + ThumbStartTemplate = _cachedThumbTemplate; + if (!_userThumbEndTemplate) + ThumbEndTemplate = _cachedThumbTemplate; + + base.OnParametersSet(); + } + + private void EnsureTemplateCache(DateRangeSliderMode mode) + { + if (_cachedTemplateMode == mode && _cachedTrackTemplate != null) return; + _cachedTemplateMode = mode; + _cachedTrackTemplate = MudExRangeSliderDefaults.DateTrack(mode); + _cachedSelectionTemplate = MudExRangeSliderDefaults.DateSelection(mode); + _cachedThumbTemplate = MudExRangeSliderDefaults.Thumb(); + } + + private static Func, RangeLength, int, SnapPolicy, DateOnly>? ResolverFor(DateRangeSliderMode mode) => mode switch + { + DateRangeSliderMode.Week => StepResolvers.DateOnly.Weekly(), + DateRangeSliderMode.Month => StepResolvers.DateOnly.Monthly(), + DateRangeSliderMode.Quarter => StepResolvers.DateOnly.Quarterly(), + DateRangeSliderMode.Year => StepResolvers.DateOnly.Yearly(), + _ => null + }; + + private static string Format(DateOnly d, DateRangeSliderMode mode) => mode switch + { + DateRangeSliderMode.Year => d.ToString("yyyy", CultureInfo.CurrentCulture), + DateRangeSliderMode.Quarter => $"Q{((d.Month - 1) / 3) + 1} {d.Year}", + DateRangeSliderMode.Month => d.ToString("MMM yyyy", CultureInfo.CurrentCulture), + DateRangeSliderMode.Week => $"KW{ISOWeek.GetWeekOfYear(d.ToDateTime(TimeOnly.MinValue))} / {d.Year}", + _ => d.ToString("d", CultureInfo.CurrentCulture) + }; +} diff --git a/MudBlazor.Extensions/Components/MudExDateTimeRangeSlider.cs b/MudBlazor.Extensions/Components/MudExDateTimeRangeSlider.cs new file mode 100644 index 00000000..cacad49e --- /dev/null +++ b/MudBlazor.Extensions/Components/MudExDateTimeRangeSlider.cs @@ -0,0 +1,102 @@ +using System.Globalization; +using Microsoft.AspNetCore.Components; +using MudBlazor.Extensions.Helper; +using Nextended.Core.Contracts; +using Nextended.Core.Types; + +namespace MudBlazor.Extensions.Components; + +/// +/// Range slider specialized for with theme-aware defaults and a +/// that controls snap behavior (Minute, Hour, Day, Week, Month). Inherits everything from – +/// any property of the base class can be set on this component as well. +/// +public class MudExDateTimeRangeSlider : MudExRangeSlider +{ + private bool _userTrackTemplate; + private bool _userSelectionTemplate; + private bool _userThumbStartTemplate; + private bool _userThumbEndTemplate; + private bool _userStepResolver; + private bool _userStartFormatter; + private bool _userEndFormatter; + + private DateTimeRangeSliderMode? _cachedTemplateMode; + private RenderFragment>? _cachedTrackTemplate; + private RenderFragment>? _cachedSelectionTemplate; + private RenderFragment>? _cachedThumbTemplate; + + /// + /// Snap resolution for start/end values. Switching this also adjusts the default labels and step resolver. + /// + [Parameter] public DateTimeRangeSliderMode Mode { get; set; } = DateTimeRangeSliderMode.Hour; + + /// + public override async Task SetParametersAsync(ParameterView parameters) + { + foreach (var p in parameters) + { + switch (p.Name) + { + case nameof(TrackTemplate): _userTrackTemplate = true; break; + case nameof(SelectionTemplate): _userSelectionTemplate = true; break; + case nameof(ThumbStartTemplate): _userThumbStartTemplate = true; break; + case nameof(ThumbEndTemplate): _userThumbEndTemplate = true; break; + case nameof(StepResolver): _userStepResolver = true; break; + case nameof(ToStartStringFunc): _userStartFormatter = true; break; + case nameof(ToEndStringFunc): _userEndFormatter = true; break; + } + } + await base.SetParametersAsync(parameters); + } + + /// + protected override void OnParametersSet() + { + EnsureTemplateCache(Mode); + + if (!_userStepResolver) + StepResolver = ResolverFor(Mode); + if (!_userStartFormatter) + ToStartStringFunc = r => Format(r.Start, Mode); + if (!_userEndFormatter) + ToEndStringFunc = r => Format(r.End, Mode); + if (!_userTrackTemplate) + TrackTemplate = _cachedTrackTemplate; + if (!_userSelectionTemplate) + SelectionTemplate = _cachedSelectionTemplate; + if (!_userThumbStartTemplate) + ThumbStartTemplate = _cachedThumbTemplate; + if (!_userThumbEndTemplate) + ThumbEndTemplate = _cachedThumbTemplate; + + base.OnParametersSet(); + } + + private void EnsureTemplateCache(DateTimeRangeSliderMode mode) + { + if (_cachedTemplateMode == mode && _cachedTrackTemplate != null) return; + _cachedTemplateMode = mode; + _cachedTrackTemplate = MudExRangeSliderDefaults.DateTimeTrack(mode); + _cachedSelectionTemplate = MudExRangeSliderDefaults.DateTimeSelection(mode); + _cachedThumbTemplate = MudExRangeSliderDefaults.Thumb(); + } + + private static Func, RangeLength, int, SnapPolicy, DateTime>? ResolverFor(DateTimeRangeSliderMode mode) => mode switch + { + DateTimeRangeSliderMode.Month => StepResolvers.DateTime.Monthly(), + DateTimeRangeSliderMode.Week => StepResolvers.DateTime.Weekly(), + DateTimeRangeSliderMode.Day => StepResolvers.DateTime.Daily(), + DateTimeRangeSliderMode.Hour => StepResolvers.DateTime.Hourly(), + _ => StepResolvers.DateTime.Minutely() + }; + + private static string Format(DateTime d, DateTimeRangeSliderMode mode) => mode switch + { + DateTimeRangeSliderMode.Month => d.ToString("MMM yyyy", CultureInfo.CurrentCulture), + DateTimeRangeSliderMode.Week => $"KW{ISOWeek.GetWeekOfYear(d)} / {d.Year}", + DateTimeRangeSliderMode.Day => d.ToString("d", CultureInfo.CurrentCulture), + DateTimeRangeSliderMode.Hour => d.ToString("dd.MM HH:00", CultureInfo.CurrentCulture), + _ => d.ToString("g", CultureInfo.CurrentCulture) + }; +} diff --git a/MudBlazor.Extensions/Components/MudExRangeSlider.razor.cs b/MudBlazor.Extensions/Components/MudExRangeSlider.razor.cs index e1e99844..8477adda 100644 --- a/MudBlazor.Extensions/Components/MudExRangeSlider.razor.cs +++ b/MudBlazor.Extensions/Components/MudExRangeSlider.razor.cs @@ -203,6 +203,22 @@ public Cursor MovingCursor [Parameter, SafeCategory("Behavior")] public double ZoomFactor { get; set; } = 0.2; + /// + /// When is not provided, this multiplier is used to derive an effective absolute zoom range from the initial . + /// A value of 3.0 means the user can zoom out to 3× the initial visible span (one full span as padding on each side, centered on the initial midpoint). + /// Set to 1.0 (or lower) to disable auto-derivation. + /// + [Parameter, SafeCategory("Behavior")] + public double AutoZoomRangeMultiplier { get; set; } = 3.0; + + // Captured once on first parameter set so the auto-derived absolute range stays stable when SizeRange changes via zoom. + private IRange? _autoAbsoluteSizeRange; + + /// + /// Gets the absolute zoom bounds actually used by the slider, either user-provided via or auto-derived from the initial via . + /// + public IRange? EffectiveAbsoluteSizeRange => AbsoluteSizeRange ?? _autoAbsoluteSizeRange; + /// /// Gets or sets the step length used for snapping and keyboard navigation. /// @@ -400,6 +416,28 @@ private string ThumbStyle() private bool? _lastWheelZoomSent; + // Binary-search the closest valid T-value to `target` along the line from `fallback` to `target`. + // Lets us derive an auto-zoom range without knowing the natural bounds of T (e.g. TimeOnly is 0..23:59:59). + private T ClampFromDouble(double target, T fallback) + { + try { return M.FromDouble(target); } + catch (ArgumentException) { /* out of T-domain – fall through */ } + catch (OverflowException) { /* same */ } + + var validD = M.ToDouble(fallback); + var invalidD = target; + for (var i = 0; i < 60; i++) // 60 iterations covers double precision + { + var mid = (validD + invalidD) / 2.0; + if (mid == validD || mid == invalidD) break; // converged + try { _ = M.FromDouble(mid); validD = mid; } + catch (ArgumentException) { invalidD = mid; } + catch (OverflowException) { invalidD = mid; } + } + return M.FromDouble(validD); + } + + /// protected override async Task OnAfterRenderAsync(bool firstRender) { @@ -420,6 +458,21 @@ protected override void OnParametersSet() var sizeSpan = M.Span(SizeRange); if (sizeSpan <= 0) return; + // Capture the auto-derived absolute zoom range exactly once, from the *initial* SizeRange. + // Otherwise the bounds would shrink/expand with every zoom step. + if (_autoAbsoluteSizeRange is null && AbsoluteSizeRange is null && AutoZoomRangeMultiplier > 1.0) + { + var startD = M.ToDouble(SizeRange.Start); + var endD = M.ToDouble(SizeRange.End); + var center = (startD + endD) / 2.0; + var halfNew = ((endD - startD) * AutoZoomRangeMultiplier) / 2.0; + // ClampFromDouble handles narrow value domains (e.g. TimeOnly's 0..23:59:59). + var leftBound = ClampFromDouble(center - halfNew, SizeRange.Start); + var rightBound = ClampFromDouble(center + halfNew, SizeRange.End); + if (leftBound.CompareTo(rightBound) < 0) + _autoAbsoluteSizeRange = new MudExRange(leftBound, rightBound); + } + if (StepLength.Delta <= 0 || StepLength.Delta > sizeSpan) { var corr = Math.Clamp(StepLength.Delta, 1e-12, sizeSpan); @@ -671,10 +724,11 @@ private async Task SetEndAsync(T v, bool commit) /// public async Task ZoomAtAsync(int steps, double anchorPercent) { - if (steps == 0 || AbsoluteSizeRange == null || ZoomFactor is <= 0 or >= 1) return; + var bounds = EffectiveAbsoluteSizeRange; + if (steps == 0 || bounds == null || ZoomFactor is <= 0 or >= 1) return; - var absStart = M.ToDouble(AbsoluteSizeRange.Start); - var absEnd = M.ToDouble(AbsoluteSizeRange.End); + var absStart = M.ToDouble(bounds.Start); + var absEnd = M.ToDouble(bounds.End); var absSpan = absEnd - absStart; if (absSpan <= 0) return; @@ -728,7 +782,7 @@ public async Task ZoomAtAsync(int steps, double anchorPercent) [JSInvokable] public async Task OnWheel(double clientX, double clientY, double deltaY) { - if (!EnableMouseWheelZoom || Disabled || ReadOnly || AbsoluteSizeRange == null) return; + if (!EnableMouseWheelZoom || Disabled || ReadOnly || EffectiveAbsoluteSizeRange == null) return; if (deltaY == 0) return; await MeasureAsync(); diff --git a/MudBlazor.Extensions/Components/MudExRangeSliderDefaults.razor b/MudBlazor.Extensions/Components/MudExRangeSliderDefaults.razor new file mode 100644 index 00000000..ebe51e1e --- /dev/null +++ b/MudBlazor.Extensions/Components/MudExRangeSliderDefaults.razor @@ -0,0 +1,89 @@ +@using System.Globalization +@using MudBlazor.Extensions.Helper +@using Nextended.Core.Contracts + +@* Helper component that exposes shared, theme-aware default templates for the typed range sliders. + Uses inline styles (matching the Template demos) so styling is self-contained and does not depend + on extra CSS classes being shipped. *@ + +@code { + // -------------------- TimeOnly -------------------- + + /// Default theme-aware track template for . + public static RenderFragment> TimeTrack(TimeRangeSliderMode mode) => ctx => + @@RenderTrack(RangeSliderTicks.ForTime(ctx.SizeRange, mode).Select(t => (t.Percent, t.IsMajor, t.Label))); + + /// Default selection chip for . + public static RenderFragment> TimeSelection() => ctx => + @@RenderSelection($"{ctx.Value.Start.ToString("HH\\:mm")} – {ctx.Value.End.ToString("HH\\:mm")}"); + + // -------------------- DateOnly -------------------- + + /// Default theme-aware track template for . + public static RenderFragment> DateTrack(DateRangeSliderMode mode) => ctx => + @@RenderTrack(RangeSliderTicks.ForDate(ctx.SizeRange, mode).Select(t => (t.Percent, t.IsMajor, t.Label))); + + /// Default selection chip for . + public static RenderFragment> DateSelection(DateRangeSliderMode mode) => ctx => + @@RenderSelection($"{FormatDate(ctx.Value.Start, mode)} – {FormatDate(ctx.Value.End, mode)}"); + + // -------------------- DateTime -------------------- + + /// Default theme-aware track template for . + public static RenderFragment> DateTimeTrack(DateTimeRangeSliderMode mode) => ctx => + @@RenderTrack(RangeSliderTicks.ForDateTime(ctx.SizeRange, mode).Select(t => (t.Percent, t.IsMajor, t.Label))); + + /// Default selection chip for . + public static RenderFragment> DateTimeSelection(DateTimeRangeSliderMode mode) => ctx => + @@RenderSelection($"{FormatDateTime(ctx.Value.Start, mode)} – {FormatDateTime(ctx.Value.End, mode)}"); + + // -------------------- Shared thumb -------------------- + + /// Default thumb appearance for typed range sliders. + public static RenderFragment> Thumb() where T : IComparable => _ => + @
; + + // -------------------- internal renderers -------------------- + + private static RenderFragment RenderTrack(IEnumerable<(double Percent, bool IsMajor, string Label)> ticks) => + @
+
+ @foreach (var tick in ticks) + { + if (tick.Percent < 0 || tick.Percent > 100) { continue; } + var pct = tick.Percent.ToString("F2", CultureInfo.InvariantCulture); + var top = tick.IsMajor ? "18%" : "32%"; + var bottom = tick.IsMajor ? "18%" : "32%"; + var width = tick.IsMajor ? "2px" : "1px"; + var color = tick.IsMajor ? "var(--mud-palette-lines-default)" : "var(--mud-palette-divider)"; +
+ if (tick.IsMajor && !string.IsNullOrEmpty(tick.Label)) + { +
@tick.Label
+ } + } +
; + + private static RenderFragment RenderSelection(string label) => + @
+ @label +
; + + private static string FormatDate(DateOnly d, DateRangeSliderMode mode) => mode switch + { + DateRangeSliderMode.Year => d.ToString("yyyy", CultureInfo.CurrentCulture), + DateRangeSliderMode.Quarter => $"Q{((d.Month - 1) / 3) + 1} {d.Year}", + DateRangeSliderMode.Month => d.ToString("MMM yyyy", CultureInfo.CurrentCulture), + DateRangeSliderMode.Week => $"KW{ISOWeek.GetWeekOfYear(d.ToDateTime(TimeOnly.MinValue))} / {d.Year}", + _ => d.ToString("d", CultureInfo.CurrentCulture) + }; + + private static string FormatDateTime(DateTime d, DateTimeRangeSliderMode mode) => mode switch + { + DateTimeRangeSliderMode.Month => d.ToString("MMM yyyy", CultureInfo.CurrentCulture), + DateTimeRangeSliderMode.Week => $"KW{ISOWeek.GetWeekOfYear(d)} / {d.Year}", + DateTimeRangeSliderMode.Day => d.ToString("d", CultureInfo.CurrentCulture), + DateTimeRangeSliderMode.Hour => d.ToString("dd.MM HH:00", CultureInfo.CurrentCulture), + _ => d.ToString("g", CultureInfo.CurrentCulture) + }; +} diff --git a/MudBlazor.Extensions/Components/MudExTimeRangeSlider.cs b/MudBlazor.Extensions/Components/MudExTimeRangeSlider.cs new file mode 100644 index 00000000..758202af --- /dev/null +++ b/MudBlazor.Extensions/Components/MudExTimeRangeSlider.cs @@ -0,0 +1,93 @@ +using System.Globalization; +using Microsoft.AspNetCore.Components; +using MudBlazor.Extensions.Helper; +using Nextended.Core.Contracts; +using Nextended.Core.Types; + +namespace MudBlazor.Extensions.Components; + +/// +/// Range slider specialized for with theme-aware defaults and a +/// that controls snap behavior (Minute, FiveMinute, QuarterHour, HalfHour, Hour). Inherits everything from +/// – any property of the base class can be set on this component as well. +/// +public class MudExTimeRangeSlider : MudExRangeSlider +{ + private bool _userTrackTemplate; + private bool _userSelectionTemplate; + private bool _userThumbStartTemplate; + private bool _userThumbEndTemplate; + private bool _userStepResolver; + private bool _userStartFormatter; + private bool _userEndFormatter; + + private TimeRangeSliderMode? _cachedTemplateMode; + private RenderFragment>? _cachedTrackTemplate; + private RenderFragment>? _cachedSelectionTemplate; + private RenderFragment>? _cachedThumbTemplate; + + /// + /// Snap resolution for start/end values. Switching this also adjusts the default tick density and step resolver. + /// + [Parameter] public TimeRangeSliderMode Mode { get; set; } = TimeRangeSliderMode.Minute; + + /// + public override async Task SetParametersAsync(ParameterView parameters) + { + foreach (var p in parameters) + { + switch (p.Name) + { + case nameof(TrackTemplate): _userTrackTemplate = true; break; + case nameof(SelectionTemplate): _userSelectionTemplate = true; break; + case nameof(ThumbStartTemplate): _userThumbStartTemplate = true; break; + case nameof(ThumbEndTemplate): _userThumbEndTemplate = true; break; + case nameof(StepResolver): _userStepResolver = true; break; + case nameof(ToStartStringFunc): _userStartFormatter = true; break; + case nameof(ToEndStringFunc): _userEndFormatter = true; break; + } + } + await base.SetParametersAsync(parameters); + } + + /// + protected override void OnParametersSet() + { + EnsureTemplateCache(Mode); + + if (!_userStepResolver) + StepResolver = ResolverFor(Mode); + if (!_userStartFormatter) + ToStartStringFunc = r => r.Start.ToString("HH\\:mm", CultureInfo.CurrentCulture); + if (!_userEndFormatter) + ToEndStringFunc = r => r.End.ToString("HH\\:mm", CultureInfo.CurrentCulture); + if (!_userTrackTemplate) + TrackTemplate = _cachedTrackTemplate; + if (!_userSelectionTemplate) + SelectionTemplate = _cachedSelectionTemplate; + if (!_userThumbStartTemplate) + ThumbStartTemplate = _cachedThumbTemplate; + if (!_userThumbEndTemplate) + ThumbEndTemplate = _cachedThumbTemplate; + + base.OnParametersSet(); + } + + private void EnsureTemplateCache(TimeRangeSliderMode mode) + { + if (_cachedTemplateMode == mode && _cachedTrackTemplate != null) return; + _cachedTemplateMode = mode; + _cachedTrackTemplate = MudExRangeSliderDefaults.TimeTrack(mode); + _cachedSelectionTemplate = MudExRangeSliderDefaults.TimeSelection(); + _cachedThumbTemplate = MudExRangeSliderDefaults.Thumb(); + } + + private static Func, RangeLength, int, SnapPolicy, TimeOnly> ResolverFor(TimeRangeSliderMode mode) => mode switch + { + TimeRangeSliderMode.Hour => StepResolvers.TimeOnly.Hourly(), + TimeRangeSliderMode.HalfHour => StepResolvers.TimeOnly.Minutely(30), + TimeRangeSliderMode.QuarterHour => StepResolvers.TimeOnly.Minutely(15), + TimeRangeSliderMode.FiveMinute => StepResolvers.TimeOnly.Minutely(5), + _ => StepResolvers.TimeOnly.Minutely() + }; +} diff --git a/MudBlazor.Extensions/Components/RangeSliderModes.cs b/MudBlazor.Extensions/Components/RangeSliderModes.cs new file mode 100644 index 00000000..8893464a --- /dev/null +++ b/MudBlazor.Extensions/Components/RangeSliderModes.cs @@ -0,0 +1,58 @@ +namespace MudBlazor.Extensions.Components; + +/// +/// Snap behavior of a . Controls the resolution at which the start/end values snap. +/// +public enum DateRangeSliderMode +{ + /// Snap to whole days (default). + Day, + /// Snap to ISO weeks (start = Monday). + Week, + /// Snap to month boundaries (start = 1st of month). + Month, + /// Snap to quarter boundaries (Jan/Apr/Jul/Oct 1st). + Quarter, + /// Snap to year boundaries (Jan 1st). + Year +} + +/// +/// Snap behavior of a . +/// +public enum TimeRangeSliderMode +{ + /// Snap to whole minutes (default). + Minute, + /// Snap to 5-minute steps. + FiveMinute, + /// Snap to quarter hours (00, 15, 30, 45). + QuarterHour, + /// Snap to half hours (00, 30). + HalfHour, + /// Snap to whole hours. + Hour +} + +/// +/// Snap behavior of a . +/// +public enum DateTimeRangeSliderMode +{ + /// Snap to whole minutes. + Minute, + /// Snap to whole hours (default). + Hour, + /// Snap to whole days. + Day, + /// Snap to ISO weeks. + Week, + /// Snap to month boundaries. + Month +} + +/// +/// One tick on a range slider track, produced by the adaptive tick engine. +/// +/// Slider value type. +public readonly record struct RangeTick(T Value, double Percent, bool IsMajor, string Label) where T : IComparable; diff --git a/MudBlazor.Extensions/Helper/RangeSliderTicks.cs b/MudBlazor.Extensions/Helper/RangeSliderTicks.cs new file mode 100644 index 00000000..37a0936a --- /dev/null +++ b/MudBlazor.Extensions/Helper/RangeSliderTicks.cs @@ -0,0 +1,249 @@ +using System.Globalization; +using MudBlazor.Extensions.Components; +using Nextended.Core.Contracts; +using Nextended.Core.Extensions; +using Nextended.Core.Types; +using Nextended.Core.Types.Ranges.Math; + +namespace MudBlazor.Extensions.Helper; + +/// +/// Adaptive tick generation for the typed range sliders (, +/// , ). +/// Granularity is chosen based on the visible span so the track stays readable at any zoom level. +/// All percent calculations are delegated to ; this class only owns +/// calendar alignment, major/minor heuristics and labels. +/// +public static class RangeSliderTicks +{ + private const int MaxTicks = 200; // hard cap so we never DoS the DOM at extreme spans + + private static readonly IRangeMath TimeMath = RangeMathFactory.For(); + private static readonly IRangeMath DateMath = RangeMathFactory.For(); + private static readonly IRangeMath DateTimeMath = RangeMathFactory.For(); + + // ------------------------------------------------------------ + // TimeOnly + // ------------------------------------------------------------ + /// + /// Generates ticks for a range slider given the currently visible span. + /// + public static IEnumerable> ForTime(IRange visible, TimeRangeSliderMode mode) + { + var startMin = (int)visible.Start.ToTimeSpan().TotalMinutes; + var endMin = (int)visible.End.ToTimeSpan().TotalMinutes; + var spanMin = Math.Max(1, endMin - startMin); + + var (minorMin, majorMin) = PickTimeGranularity(spanMin, mode); + + var firstTick = ((startMin + minorMin - 1) / minorMin) * minorMin; + for (var totalMin = firstTick; totalMin <= endMin; totalMin += minorMin) + { + var time = new TimeOnly(totalMin / 60, totalMin % 60); + var pct = TimeMath.Percent(time, visible) * 100.0; + var isMajor = totalMin % majorMin == 0; + var label = isMajor ? time.ToString("HH\\:mm", CultureInfo.CurrentCulture) : string.Empty; + yield return new RangeTick(time, pct, isMajor, label); + } + } + + private static (int Minor, int Major) PickTimeGranularity(int spanMin, TimeRangeSliderMode mode) + { + int minor, major; + if (spanMin > 720) { minor = 60; major = 180; } + else if (spanMin > 240) { minor = 30; major = 60; } + else if (spanMin > 60) { minor = 15; major = 60; } + else if (spanMin > 15) { minor = 5; major = 15; } + else { minor = 1; major = 5; } + + // Mode acts as a floor – never render finer than the snap resolution. + var floor = mode switch + { + TimeRangeSliderMode.Hour => 60, + TimeRangeSliderMode.HalfHour => 30, + TimeRangeSliderMode.QuarterHour => 15, + TimeRangeSliderMode.FiveMinute => 5, + _ => 1 + }; + if (minor < floor) minor = floor; + if (major < minor) major = minor; + return (minor, major); + } + + // ------------------------------------------------------------ + // DateOnly + // ------------------------------------------------------------ + /// + /// Generates ticks for a range slider given the currently visible span. + /// + public static IEnumerable> ForDate(IRange visible, DateRangeSliderMode mode) + { + var totalDays = Math.Max(1, visible.End.DayNumber - visible.Start.DayNumber); + var granularity = PickDateGranularity(totalDays, mode); + + return granularity switch + { + DateGranularity.Day => DailyTicks(visible), + DateGranularity.Week => WeeklyTicks(visible), + DateGranularity.Month => MonthlyTicks(visible), + DateGranularity.Quarter => QuarterlyTicks(visible), + _ => YearlyTicks(visible) + }; + } + + private enum DateGranularity { Day, Week, Month, Quarter, Year } + + private static DateGranularity PickDateGranularity(int totalDays, DateRangeSliderMode mode) + { + var byZoom = totalDays switch + { + <= 14 => DateGranularity.Day, + <= 90 => DateGranularity.Week, + <= 730 => DateGranularity.Month, + <= 3650 => DateGranularity.Quarter, + _ => DateGranularity.Year + }; + var byMode = mode switch + { + DateRangeSliderMode.Year => DateGranularity.Year, + DateRangeSliderMode.Quarter => DateGranularity.Quarter, + DateRangeSliderMode.Month => DateGranularity.Month, + DateRangeSliderMode.Week => DateGranularity.Week, + _ => DateGranularity.Day + }; + return byZoom < byMode ? byMode : byZoom; + } + + private static IEnumerable> DailyTicks(IRange visible) + { + var tickCount = visible.End.DayNumber - visible.Start.DayNumber + 1; + var step = Math.Max(1, tickCount / MaxTicks); + for (var d = visible.Start; d <= visible.End; d = d.AddDays(step)) + { + var pct = DateMath.Percent(d, visible) * 100.0; + var isMajor = d.DayOfWeek == DayOfWeek.Monday; + var label = isMajor ? d.ToString("dd.MM", CultureInfo.CurrentCulture) : string.Empty; + yield return new RangeTick(d, pct, isMajor, label); + } + } + + private static IEnumerable> WeeklyTicks(IRange visible) + { + var first = visible.Start; + while (first.DayOfWeek != DayOfWeek.Monday && first <= visible.End) first = first.AddDays(1); + for (var d = first; d <= visible.End; d = d.AddDays(7)) + { + var pct = DateMath.Percent(d, visible) * 100.0; + var isFirstOfMonth = d.Day <= 7; + var label = isFirstOfMonth + ? d.ToString("MMM", CultureInfo.CurrentCulture) + : $"KW{ISOWeek.GetWeekOfYear(d.ToDateTime(TimeOnly.MinValue))}"; + yield return new RangeTick(d, pct, isFirstOfMonth, label); + } + } + + private static IEnumerable> MonthlyTicks(IRange visible) + { + var first = new DateOnly(visible.Start.Year, visible.Start.Month, 1); + if (first < visible.Start) first = first.AddMonths(1); + for (var d = first; d <= visible.End; d = d.AddMonths(1)) + { + var pct = DateMath.Percent(d, visible) * 100.0; + var isMajor = d.Month == 1; + var label = isMajor + ? d.ToString("yyyy", CultureInfo.CurrentCulture) + : d.ToString("MMM", CultureInfo.CurrentCulture); + yield return new RangeTick(d, pct, isMajor, label); + } + } + + private static IEnumerable> QuarterlyTicks(IRange visible) + { + var first = FirstOfQuarter(visible.Start); + if (first < visible.Start) first = first.AddMonths(3); + for (var d = first; d <= visible.End; d = d.AddMonths(3)) + { + var pct = DateMath.Percent(d, visible) * 100.0; + var isMajor = d.Month == 1; + var quarter = (d.Month - 1) / 3 + 1; + var label = isMajor ? d.ToString("yyyy", CultureInfo.CurrentCulture) : $"Q{quarter}"; + yield return new RangeTick(d, pct, isMajor, label); + } + } + + private static IEnumerable> YearlyTicks(IRange visible) + { + var first = new DateOnly(visible.Start.Year, 1, 1); + if (first < visible.Start) first = first.AddYears(1); + for (var d = first; d <= visible.End; d = d.AddYears(1)) + { + var pct = DateMath.Percent(d, visible) * 100.0; + var isMajor = d.Year % 5 == 0; + yield return new RangeTick(d, pct, isMajor, d.Year.ToString(CultureInfo.CurrentCulture)); + } + } + + private static DateOnly FirstOfQuarter(DateOnly d) + { + var qStartMonth = ((d.Month - 1) / 3) * 3 + 1; + return new DateOnly(d.Year, qStartMonth, 1); + } + + // ------------------------------------------------------------ + // DateTime + // ------------------------------------------------------------ + /// + /// Generates ticks for a range slider given the currently visible span. + /// + public static IEnumerable> ForDateTime(IRange visible, DateTimeRangeSliderMode mode) + { + var totalHours = (visible.End - visible.Start).TotalHours; + if (totalHours <= 24) + return SubDayTicks(visible); + + // Delegate longer spans to the date-only engine and lift the result back to DateTime. + var dateMode = mode switch + { + DateTimeRangeSliderMode.Month => DateRangeSliderMode.Month, + DateTimeRangeSliderMode.Week => DateRangeSliderMode.Week, + _ => DateRangeSliderMode.Day + }; + var dateRange = new MudExRange(DateOnly.FromDateTime(visible.Start), DateOnly.FromDateTime(visible.End)); + return LiftDateTicks(ForDate(dateRange, dateMode), visible); + } + + private static IEnumerable> SubDayTicks(IRange visible) + { + var spanMin = Math.Max(1, (int)(visible.End - visible.Start).TotalMinutes); + int minorMin, majorMin; + if (spanMin > 720) { minorMin = 60; majorMin = 180; } + else if (spanMin > 240) { minorMin = 30; majorMin = 60; } + else if (spanMin > 60) { minorMin = 15; majorMin = 60; } + else if (spanMin > 15) { minorMin = 5; majorMin = 15; } + else { minorMin = 1; majorMin = 5; } + + var first = visible.Start.AddTicks(-(visible.Start.Ticks % TimeSpan.TicksPerMinute)); + var firstMinOffset = (long)(first - visible.Start).TotalMinutes; + var startTotal = (long)Math.Ceiling(firstMinOffset / (double)minorMin) * minorMin; + for (var minOffset = startTotal; ; minOffset += minorMin) + { + var t = first.AddMinutes(minOffset); + if (t < visible.Start) continue; + if (t > visible.End) yield break; + var pct = DateTimeMath.Percent(t, visible) * 100.0; + var isMajor = ((long)(t - DateTime.MinValue).TotalMinutes) % majorMin == 0; + var label = isMajor ? t.ToString("HH\\:mm", CultureInfo.CurrentCulture) : string.Empty; + yield return new RangeTick(t, pct, isMajor, label); + } + } + + private static IEnumerable> LiftDateTicks(IEnumerable> dateTicks, IRange visible) + { + foreach (var t in dateTicks) + { + var dt = t.Value.ToDateTime(TimeOnly.MinValue); + var pct = DateTimeMath.Percent(dt, visible) * 100.0; + yield return new RangeTick(dt, pct, t.IsMajor, t.Label); + } + } +} diff --git a/MudBlazor.Extensions/wwwroot/css/components/_mudexrangeslider.scss b/MudBlazor.Extensions/wwwroot/css/components/_mudexrangeslider.scss index 9c9a930c..3bce4402 100644 --- a/MudBlazor.Extensions/wwwroot/css/components/_mudexrangeslider.scss +++ b/MudBlazor.Extensions/wwwroot/css/components/_mudexrangeslider.scss @@ -488,3 +488,90 @@ .mud-ex-range-slider[aria-readonly="true"] .mud-ex-range-slider-selection { cursor: default; } + +/* ---------- Typed sliders (Date/Time/DateTime) – default templates ---------- */ +.mud-ex-typed-slider-track { + position: relative; + width: 100%; + height: 100%; + background: var(--mud-palette-surface); + border-radius: 4px; + overflow: hidden; +} + +.mud-ex-typed-slider-track-rail { + position: absolute; + left: 0; + right: 0; + top: 50%; + transform: translateY(-50%); + height: 4px; + background: linear-gradient(to right, + var(--mud-palette-background-grey), + var(--mud-palette-surface)); + border-radius: 2px; + pointer-events: none; +} + +.mud-ex-typed-slider-tick { + position: absolute; + background: var(--mud-palette-divider); + transform: translateX(-50%); + pointer-events: none; +} + + .mud-ex-typed-slider-tick.minor { + top: 38%; + bottom: 38%; + width: 1px; + } + + .mud-ex-typed-slider-tick.major { + top: 18%; + bottom: 18%; + width: 2px; + background: var(--mud-palette-lines-default); + } + +.mud-ex-typed-slider-tick-label { + position: absolute; + bottom: 2px; + transform: translateX(-50%); + font-size: 10px; + color: var(--mud-palette-text-secondary); + font-weight: 500; + pointer-events: none; + white-space: nowrap; + z-index: 1; +} + +.mud-ex-typed-slider-selection { + width: 100%; + height: 100%; + background: linear-gradient(90deg, + var(--mud-palette-primary), + var(--mud-palette-primary-darken)); + display: flex; + align-items: center; + justify-content: center; + border-radius: 4px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.25); +} + +.mud-ex-typed-slider-selection-label { + color: var(--mud-palette-primary-text); + font-weight: 600; + font-size: 11px; + white-space: nowrap; + padding: 0 8px; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3); +} + +.mud-ex-typed-slider-thumb { + width: 14px; + height: 14px; + background: var(--mud-palette-surface); + border: 3px solid var(--mud-palette-primary); + border-radius: 50%; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.25); +} diff --git a/MudBlazor.Extensions/wwwroot/docs/MudBlazor.Extensions.xml b/MudBlazor.Extensions/wwwroot/docs/MudBlazor.Extensions.xml index ce52b47f..ce5d6e8a 100644 --- a/MudBlazor.Extensions/wwwroot/docs/MudBlazor.Extensions.xml +++ b/MudBlazor.Extensions/wwwroot/docs/MudBlazor.Extensions.xml @@ -2179,6 +2179,42 @@ Ignore neutral cultures + + + Range slider specialized for with theme-aware defaults and a + that controls snap behavior (Day, Week, Month, Quarter, Year). Inherits everything from – + any property of the base class (SizeRange, Value, MinLength, …) can be set on this component as well. + + + + + Snap resolution for start/end values. Switching this also adjusts the default labels and step resolver. + + + + + + + + + + + Range slider specialized for with theme-aware defaults and a + that controls snap behavior (Minute, Hour, Day, Week, Month). Inherits everything from – + any property of the base class can be set on this component as well. + + + + + Snap resolution for start/end values. Switching this also adjusts the default labels and step resolver. + + + + + + + + MudExDialog is the component to use when you want to show a dialog inlined in your page with all DialogExtensions. @@ -6602,6 +6638,18 @@ and each step expands it to 125%. Must be in the range (0,1). + + + When is not provided, this multiplier is used to derive an effective absolute zoom range from the initial . + A value of 3.0 means the user can zoom out to 3× the initial visible span (one full span as padding on each side, centered on the initial midpoint). + Set to 1.0 (or lower) to disable auto-derivation. + + + + + Gets the absolute zoom bounds actually used by the slider, either user-provided via or auto-derived from the initial via . + + Gets or sets the step length used for snapping and keyboard navigation. @@ -8711,6 +8759,24 @@ ItemList + + + Range slider specialized for with theme-aware defaults and a + that controls snap behavior (Minute, FiveMinute, QuarterHour, HalfHour, Hour). Inherits everything from + – any property of the base class can be set on this component as well. + + + + + Snap resolution for start/end values. Switching this also adjusts the default tick density and step resolver. + + + + + + + + Simple component to expand a Search field @@ -15487,6 +15553,78 @@ + + + Snap behavior of a . Controls the resolution at which the start/end values snap. + + + + Snap to whole days (default). + + + Snap to ISO weeks (start = Monday). + + + Snap to month boundaries (start = 1st of month). + + + Snap to quarter boundaries (Jan/Apr/Jul/Oct 1st). + + + Snap to year boundaries (Jan 1st). + + + + Snap behavior of a . + + + + Snap to whole minutes (default). + + + Snap to 5-minute steps. + + + Snap to quarter hours (00, 15, 30, 45). + + + Snap to half hours (00, 30). + + + Snap to whole hours. + + + + Snap behavior of a . + + + + Snap to whole minutes. + + + Snap to whole hours (default). + + + Snap to whole days. + + + Snap to ISO weeks. + + + Snap to month boundaries. + + + + One tick on a range slider track, produced by the adaptive tick engine. + + Slider value type. + + + + One tick on a range slider track, produced by the adaptive tick engine. + + Slider value type. + Controls how the highlighting of the filter is displayed @@ -16279,6 +16417,27 @@ Is false control isn't rendered at all + + Default theme-aware track template for . + + + Default selection chip for . + + + Default theme-aware track template for . + + + Default selection chip for . + + + Default theme-aware track template for . + + + Default selection chip for . + + + Default thumb appearance for typed range sliders. + @@ -23458,6 +23617,30 @@ Converts Theme to json + + + Adaptive tick generation for the typed range sliders (, + , ). + Granularity is chosen based on the visible span so the track stays readable at any zoom level. + All percent calculations are delegated to ; this class only owns + calendar alignment, major/minor heuristics and labels. + + + + + Generates ticks for a range slider given the currently visible span. + + + + + Generates ticks for a range slider given the currently visible span. + + + + + Generates ticks for a range slider given the currently visible span. + + Provides factory methods to create step resolvers for different temporal types. diff --git a/Samples/MainSample.WebAssembly/Examples/RangeSlider/TypedDateRangeSliderExample.razor b/Samples/MainSample.WebAssembly/Examples/RangeSlider/TypedDateRangeSliderExample.razor new file mode 100644 index 00000000..fc36dc6c --- /dev/null +++ b/Samples/MainSample.WebAssembly/Examples/RangeSlider/TypedDateRangeSliderExample.razor @@ -0,0 +1,31 @@ +@using Nextended.Core.Contracts +@using Nextended.Core.Types +@inherits ExampleBase + + + @L["Selected: {0} – {1}", _value.Start, _value.End] + + + + @L["Mode:"] + + + + + + +@code { + private MudExDateRangeSlider? _slider; + private DateRangeSliderMode _mode = DateRangeSliderMode.Month; + private bool _zoom = true; + + private static readonly DateOnly Today = DateOnly.FromDateTime(DateTime.Today); + + private IRange _sizeRange = new RangeOf(Today.AddYears(-2), Today.AddYears(2), null); + private IRange _value = new RangeOf(Today.AddMonths(-3), Today.AddMonths(3), null); +} diff --git a/Samples/MainSample.WebAssembly/Examples/RangeSlider/TypedDateTimeRangeSliderExample.razor b/Samples/MainSample.WebAssembly/Examples/RangeSlider/TypedDateTimeRangeSliderExample.razor new file mode 100644 index 00000000..043e441e --- /dev/null +++ b/Samples/MainSample.WebAssembly/Examples/RangeSlider/TypedDateTimeRangeSliderExample.razor @@ -0,0 +1,31 @@ +@using Nextended.Core.Contracts +@using Nextended.Core.Types +@inherits ExampleBase + + + @L["Selected window: {0} – {1}", _value.Start, _value.End] + + + + @L["Mode:"] + + + + + + +@code { + private MudExDateTimeRangeSlider? _slider; + private DateTimeRangeSliderMode _mode = DateTimeRangeSliderMode.Hour; + private bool _zoom = true; + + private static readonly DateTime Today = DateTime.Today; + + private IRange _sizeRange = new RangeOf(Today.AddDays(-7), Today.AddDays(7), null); + private IRange _value = new RangeOf(Today.AddHours(-12), Today.AddHours(12), null); +} diff --git a/Samples/MainSample.WebAssembly/Examples/RangeSlider/TypedTimeRangeSliderExample.razor b/Samples/MainSample.WebAssembly/Examples/RangeSlider/TypedTimeRangeSliderExample.razor new file mode 100644 index 00000000..0e384de5 --- /dev/null +++ b/Samples/MainSample.WebAssembly/Examples/RangeSlider/TypedTimeRangeSliderExample.razor @@ -0,0 +1,30 @@ +@using Nextended.Core.Contracts +@using Nextended.Core.Types +@inherits ExampleBase + + + @L["Selected slot: {0} – {1}", _value.Start.ToString("HH\\:mm"), _value.End.ToString("HH\\:mm")] + + + + @L["Mode:"] + + + + + + +@code { + private MudExTimeRangeSlider? _slider; + private TimeRangeSliderMode _mode = TimeRangeSliderMode.QuarterHour; + private bool _zoom = true; + + // Visible window: full day (zoom-in via mouse wheel works without an explicit AbsoluteSizeRange). + private IRange _sizeRange = new RangeOf(new TimeOnly(0, 0), new TimeOnly(23, 59), null); + private IRange _value = new RangeOf(new TimeOnly(9, 30), new TimeOnly(11, 0), null); +} diff --git a/Samples/MainSample.WebAssembly/Pages/Page_RangeSlider.razor b/Samples/MainSample.WebAssembly/Pages/Page_RangeSlider.razor index 413eb110..797fd9a9 100644 --- a/Samples/MainSample.WebAssembly/Pages/Page_RangeSlider.razor +++ b/Samples/MainSample.WebAssembly/Pages/Page_RangeSlider.razor @@ -166,6 +166,67 @@ + + – any base property still works", + "Mode: Day / Week / Month / Quarter / Year – snap behavior", + "Theme-aware default Track / Selection / Thumb templates", + "Adaptive ticks – granularity changes with zoom level", + "AutoZoomRangeMultiplier (3×) – zoom out without setting AbsoluteSizeRange" + })"> + + + + @L["Drop-in component for date ranges. Mode controls snapping; the Track template adapts to the visible span, and zoom (mouse wheel by default) lets you switch between coarse overview and fine selection without leaving the component."] + + + + + + + ", + "Mode: Minute / FiveMinute / QuarterHour / HalfHour / Hour", + "Defaults to office hours (08–18); zoom out to full day", + "Adaptive ticks down to 1-minute precision when zoomed in" + })"> + + + + @L["Drop-in component for time-of-day ranges – ideal for meeting slots, opening hours, or maintenance windows. Mode controls snapping while the visible span follows the user's zoom."] + + + + + + + ", + "Mode: Minute / Hour / Day / Week / Month", + "Combines date and time precision in one slider", + "Tick engine switches automatically between sub-day and multi-day rendering" + })"> + + + + @L["The most flexible of the typed sliders – goes from minute-level resolution all the way to multi-month overviews depending on Mode and zoom. Useful for incident timelines, log filters or scheduling UIs."] + + + + + diff --git a/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedDateRangeSliderExample.html b/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedDateRangeSliderExample.html new file mode 100644 index 00000000..f35dda81 --- /dev/null +++ b/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedDateRangeSliderExample.html @@ -0,0 +1,34 @@ +
+@using Nextended.Core.Contracts
+@using Nextended.Core.Types
+@inherits ExampleBase
+
+<MudText Typo="Typo.caption" Class="mt-2 mb-2">
+    @L["Selected: {0} – {1}", _value.Start, _value.End]
+</MudText>
+
+<MudStack Row="true" Spacing="2" Class="mb-3" AlignItems="AlignItems.Center">
+    <MudText>@L["Mode:"]</MudText>
+    <MudExEnumSelect T="DateRangeSliderMode" @bind-Value="_mode" Style="min-width: 140px;" />
+    <MudSwitch T="bool" @bind-Value="_zoom" Color="Color.Primary" Label="@L["Mouse wheel zoom"]" />
+</MudStack>
+
+<MudExDateRangeSlider @ref="_slider"
+                      Mode="_mode"
+                      EnableMouseWheelZoom="_zoom"
+                      SizeRange="@_sizeRange"
+                      @bind-Value="_value"
+                      AllowWholeRangeDrag="true" />
+
+@code {
+    private MudExDateRangeSlider? _slider;
+    private DateRangeSliderMode _mode = DateRangeSliderMode.Month;
+    private bool _zoom = true;
+
+    private static readonly DateOnly Today = DateOnly.FromDateTime(DateTime.Today);
+
+    private IRange<DateOnly> _sizeRange = new RangeOf<DateOnly>(Today.AddYears(-2), Today.AddYears(2), null);
+    private IRange<DateOnly> _value = new RangeOf<DateOnly>(Today.AddMonths(-3), Today.AddMonths(3), null);
+}
+
+
\ No newline at end of file diff --git a/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedDateRangeSliderExample.md b/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedDateRangeSliderExample.md new file mode 100644 index 00000000..f67e422a --- /dev/null +++ b/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedDateRangeSliderExample.md @@ -0,0 +1,34 @@ +```razor +@using Nextended.Core.Contracts +@using Nextended.Core.Types +@inherits ExampleBase + + + @L["Selected: {0} – {1}", _value.Start, _value.End] + + + + @L["Mode:"] + + + + + + +@code { + private MudExDateRangeSlider? _slider; + private DateRangeSliderMode _mode = DateRangeSliderMode.Month; + private bool _zoom = true; + + private static readonly DateOnly Today = DateOnly.FromDateTime(DateTime.Today); + + private IRange _sizeRange = new RangeOf(Today.AddYears(-2), Today.AddYears(2), null); + private IRange _value = new RangeOf(Today.AddMonths(-3), Today.AddMonths(3), null); +} + +``` diff --git a/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedDateTimeRangeSliderExample.html b/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedDateTimeRangeSliderExample.html new file mode 100644 index 00000000..4fb7aca7 --- /dev/null +++ b/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedDateTimeRangeSliderExample.html @@ -0,0 +1,34 @@ +
+@using Nextended.Core.Contracts
+@using Nextended.Core.Types
+@inherits ExampleBase
+
+<MudText Typo="Typo.caption" Class="mt-2 mb-2">
+    @L["Selected window: {0} – {1}", _value.Start, _value.End]
+</MudText>
+
+<MudStack Row="true" Spacing="2" Class="mb-3" AlignItems="AlignItems.Center">
+    <MudText>@L["Mode:"]</MudText>
+    <MudExEnumSelect T="DateTimeRangeSliderMode" @bind-Value="_mode" Style="min-width: 140px;" />
+    <MudSwitch T="bool" @bind-Value="_zoom" Color="Color.Primary" Label="@L["Mouse wheel zoom"]" />
+</MudStack>
+
+<MudExDateTimeRangeSlider @ref="_slider"
+                          Mode="_mode"
+                          EnableMouseWheelZoom="_zoom"
+                          SizeRange="@_sizeRange"
+                          @bind-Value="_value"
+                          AllowWholeRangeDrag="true" />
+
+@code {
+    private MudExDateTimeRangeSlider? _slider;
+    private DateTimeRangeSliderMode _mode = DateTimeRangeSliderMode.Hour;
+    private bool _zoom = true;
+
+    private static readonly DateTime Today = DateTime.Today;
+
+    private IRange<DateTime> _sizeRange = new RangeOf<DateTime>(Today.AddDays(-7), Today.AddDays(7), null);
+    private IRange<DateTime> _value = new RangeOf<DateTime>(Today.AddHours(-12), Today.AddHours(12), null);
+}
+
+
\ No newline at end of file diff --git a/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedDateTimeRangeSliderExample.md b/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedDateTimeRangeSliderExample.md new file mode 100644 index 00000000..c8b45e2a --- /dev/null +++ b/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedDateTimeRangeSliderExample.md @@ -0,0 +1,34 @@ +```razor +@using Nextended.Core.Contracts +@using Nextended.Core.Types +@inherits ExampleBase + + + @L["Selected window: {0} – {1}", _value.Start, _value.End] + + + + @L["Mode:"] + + + + + + +@code { + private MudExDateTimeRangeSlider? _slider; + private DateTimeRangeSliderMode _mode = DateTimeRangeSliderMode.Hour; + private bool _zoom = true; + + private static readonly DateTime Today = DateTime.Today; + + private IRange _sizeRange = new RangeOf(Today.AddDays(-7), Today.AddDays(7), null); + private IRange _value = new RangeOf(Today.AddHours(-12), Today.AddHours(12), null); +} + +``` diff --git a/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedTimeRangeSliderExample.html b/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedTimeRangeSliderExample.html new file mode 100644 index 00000000..86103d1b --- /dev/null +++ b/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedTimeRangeSliderExample.html @@ -0,0 +1,33 @@ +
+@using Nextended.Core.Contracts
+@using Nextended.Core.Types
+@inherits ExampleBase
+
+<MudText Typo="Typo.caption" Class="mt-2 mb-2">
+    @L["Selected slot: {0} – {1}", _value.Start.ToString("HH\\:mm"), _value.End.ToString("HH\\:mm")]
+</MudText>
+
+<MudStack Row="true" Spacing="2" Class="mb-3" AlignItems="AlignItems.Center">
+    <MudText>@L["Mode:"]</MudText>
+    <MudExEnumSelect T="TimeRangeSliderMode" @bind-Value="_mode" Style="min-width: 160px;" />
+    <MudSwitch T="bool" @bind-Value="_zoom" Color="Color.Primary" Label="@L["Mouse wheel zoom"]" />
+</MudStack>
+
+<MudExTimeRangeSlider @ref="_slider"
+                      Mode="_mode"
+                      EnableMouseWheelZoom="_zoom"
+                      SizeRange="@_sizeRange"
+                      @bind-Value="_value"
+                      AllowWholeRangeDrag="true" />
+
+@code {
+    private MudExTimeRangeSlider? _slider;
+    private TimeRangeSliderMode _mode = TimeRangeSliderMode.QuarterHour;
+    private bool _zoom = true;
+
+    // Visible window: full day (zoom-in via mouse wheel works without an explicit AbsoluteSizeRange).
+    private IRange<TimeOnly> _sizeRange = new RangeOf<TimeOnly>(new TimeOnly(0, 0), new TimeOnly(23, 59), null);
+    private IRange<TimeOnly> _value = new RangeOf<TimeOnly>(new TimeOnly(9, 30), new TimeOnly(11, 0), null);
+}
+
+
\ No newline at end of file diff --git a/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedTimeRangeSliderExample.md b/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedTimeRangeSliderExample.md new file mode 100644 index 00000000..7db1a799 --- /dev/null +++ b/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedTimeRangeSliderExample.md @@ -0,0 +1,33 @@ +```razor +@using Nextended.Core.Contracts +@using Nextended.Core.Types +@inherits ExampleBase + + + @L["Selected slot: {0} – {1}", _value.Start.ToString("HH\\:mm"), _value.End.ToString("HH\\:mm")] + + + + @L["Mode:"] + + + + + + +@code { + private MudExTimeRangeSlider? _slider; + private TimeRangeSliderMode _mode = TimeRangeSliderMode.QuarterHour; + private bool _zoom = true; + + // Visible window: full day (zoom-in via mouse wheel works without an explicit AbsoluteSizeRange). + private IRange _sizeRange = new RangeOf(new TimeOnly(0, 0), new TimeOnly(23, 59), null); + private IRange _value = new RangeOf(new TimeOnly(9, 30), new TimeOnly(11, 0), null); +} + +``` diff --git a/Samples/MainSample.WebAssembly/wwwroot/index.html b/Samples/MainSample.WebAssembly/wwwroot/index.html index 71cfb09d..631c24a8 100644 --- a/Samples/MainSample.WebAssembly/wwwroot/index.html +++ b/Samples/MainSample.WebAssembly/wwwroot/index.html @@ -21,17 +21,17 @@ - + - - - - + + + + - - - + + + @@ -48,7 +48,7 @@ -

MudBlazor.Extensions v9.4.0-prev-2604301110

+

MudBlazor.Extensions v9.4.0-prev-260511142

for MudBlazor 9.4.0

@@ -83,8 +83,8 @@

MudBlazor.Extensions v9.4.0-prev-2604301110

- - + + From c83aeaac6dea6d66ec594ea5aecd6c7927cdfd2a Mon Sep 17 00:00:00 2001 From: Florian Gilde Date: Sun, 14 Jun 2026 22:34:53 +0200 Subject: [PATCH 2/5] update samples --- .../Examples/ExampleBase.cs | 4 ++++ .../RangeSliderTemplateAgeExample.razor | 1 + .../RangeSliderTemplateBudgetExample.razor | 1 + .../RangeSliderTemplateDayOfWeekExample.razor | 1 + .../RangeSliderTemplateRiskExample.razor | 1 + .../RangeSliderTemplateShiftExample.razor | 1 + .../RangeSliderTemplateSlaExample.razor | 1 + .../RangeSliderTemplateTimeZoomExample.razor | 5 +++++ ...angeSliderTemplateTransitTimeExample.razor | 1 + .../RangeSliderTemplateZoomLevelExample.razor | 1 + .../RangeSlider/RangeSliderTimeOnly1.razor | 4 ++++ .../RangeSlider/RangeSliderZoomExample.razor | 5 +++++ .../TypedDateRangeSliderExample.razor | 5 +++++ .../TypedDateTimeRangeSliderExample.razor | 5 +++++ .../TypedTimeRangeSliderExample.razor | 5 +++++ .../RangeSliderTemplateAgeExample.html | 1 + .../RangeSliderTemplateAgeExample.md | 1 + .../RangeSliderTemplateBudgetExample.html | 1 + .../RangeSliderTemplateBudgetExample.md | 1 + .../RangeSliderTemplateDayOfWeekExample.html | 1 + .../RangeSliderTemplateDayOfWeekExample.md | 1 + .../RangeSliderTemplateRiskExample.html | 1 + .../RangeSliderTemplateRiskExample.md | 1 + .../RangeSliderTemplateShiftExample.html | 1 + .../RangeSliderTemplateShiftExample.md | 1 + .../RangeSliderTemplateSlaExample.html | 1 + .../RangeSliderTemplateSlaExample.md | 1 + .../RangeSliderTemplateTimeZoomExample.html | 5 +++++ .../RangeSliderTemplateTimeZoomExample.md | 5 +++++ ...RangeSliderTemplateTransitTimeExample.html | 1 + .../RangeSliderTemplateTransitTimeExample.md | 1 + .../RangeSliderTemplateZoomLevelExample.html | 1 + .../RangeSliderTemplateZoomLevelExample.md | 1 + .../example-codes/RangeSliderTimeOnly1.html | 4 ++++ .../example-codes/RangeSliderTimeOnly1.md | 4 ++++ .../example-codes/RangeSliderZoomExample.html | 5 +++++ .../example-codes/RangeSliderZoomExample.md | 5 +++++ .../TypedDateRangeSliderExample.html | 5 +++++ .../TypedDateRangeSliderExample.md | 5 +++++ .../TypedDateTimeRangeSliderExample.html | 5 +++++ .../TypedDateTimeRangeSliderExample.md | 5 +++++ .../TypedTimeRangeSliderExample.html | 5 +++++ .../TypedTimeRangeSliderExample.md | 5 +++++ .../MainSample.WebAssembly/wwwroot/index.html | 22 +++++++++---------- 44 files changed, 126 insertions(+), 11 deletions(-) diff --git a/Samples/MainSample.WebAssembly/Examples/ExampleBase.cs b/Samples/MainSample.WebAssembly/Examples/ExampleBase.cs index 1e15abcb..03b08d79 100644 --- a/Samples/MainSample.WebAssembly/Examples/ExampleBase.cs +++ b/Samples/MainSample.WebAssembly/Examples/ExampleBase.cs @@ -15,6 +15,10 @@ public class ExampleBase : ComponentBase, IExample { new Regex(@"@inherits\s+ExampleBase\b", RegexOptions.IgnoreCase | RegexOptions.Compiled), new Regex(@"@ref\s*=\s*""ComponentRef""", RegexOptions.IgnoreCase | RegexOptions.Compiled), + // Examples that need their own typed @ref expose the instance to the demo editor via + // "if (firstRender) ComponentRef = _slider;" inside OnAfterRender. Strip that single line so the + // exported sample (which has @inherits ExampleBase removed) still compiles standalone. + new Regex(@"^[ \t]*if \(firstRender\) ComponentRef = _slider;[ \t]*\r?$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Multiline), new Regex(@"]*>", RegexOptions.IgnoreCase | RegexOptions.Compiled) // new Regex(@"]*>", RegexOptions.IgnoreCase | RegexOptions.Compiled) }; diff --git a/Samples/MainSample.WebAssembly/Examples/RangeSlider/RangeSliderTemplateAgeExample.razor b/Samples/MainSample.WebAssembly/Examples/RangeSlider/RangeSliderTemplateAgeExample.razor index fede8d66..4b0a3439 100644 --- a/Samples/MainSample.WebAssembly/Examples/RangeSlider/RangeSliderTemplateAgeExample.razor +++ b/Samples/MainSample.WebAssembly/Examples/RangeSlider/RangeSliderTemplateAgeExample.razor @@ -9,6 +9,7 @@ @code { + protected override void OnAfterRender(bool firstRender) + { + if (firstRender) ComponentRef = _slider; + } + private MudExRangeSlider? _slider; private bool _wheelZoom = true; diff --git a/Samples/MainSample.WebAssembly/Examples/RangeSlider/RangeSliderTemplateTransitTimeExample.razor b/Samples/MainSample.WebAssembly/Examples/RangeSlider/RangeSliderTemplateTransitTimeExample.razor index 3f37ccab..af4daeb8 100644 --- a/Samples/MainSample.WebAssembly/Examples/RangeSlider/RangeSliderTemplateTransitTimeExample.razor +++ b/Samples/MainSample.WebAssembly/Examples/RangeSlider/RangeSliderTemplateTransitTimeExample.razor @@ -9,6 +9,7 @@ diff --git a/Samples/MainSample.WebAssembly/Examples/RangeSlider/RangeSliderTimeOnly1.razor b/Samples/MainSample.WebAssembly/Examples/RangeSlider/RangeSliderTimeOnly1.razor index e544e2e0..f248c946 100644 --- a/Samples/MainSample.WebAssembly/Examples/RangeSlider/RangeSliderTimeOnly1.razor +++ b/Samples/MainSample.WebAssembly/Examples/RangeSlider/RangeSliderTimeOnly1.razor @@ -14,6 +14,7 @@ @code { + protected override void OnAfterRender(bool firstRender) + { + if (firstRender) ComponentRef = _slider; + } + private MudExRangeSlider? _slider; private bool _wheelZoom = true; diff --git a/Samples/MainSample.WebAssembly/Examples/RangeSlider/TypedDateRangeSliderExample.razor b/Samples/MainSample.WebAssembly/Examples/RangeSlider/TypedDateRangeSliderExample.razor index fc36dc6c..91c198d5 100644 --- a/Samples/MainSample.WebAssembly/Examples/RangeSlider/TypedDateRangeSliderExample.razor +++ b/Samples/MainSample.WebAssembly/Examples/RangeSlider/TypedDateRangeSliderExample.razor @@ -20,6 +20,11 @@ AllowWholeRangeDrag="true" /> @code { + protected override void OnAfterRender(bool firstRender) + { + if (firstRender) ComponentRef = _slider; + } + private MudExDateRangeSlider? _slider; private DateRangeSliderMode _mode = DateRangeSliderMode.Month; private bool _zoom = true; diff --git a/Samples/MainSample.WebAssembly/Examples/RangeSlider/TypedDateTimeRangeSliderExample.razor b/Samples/MainSample.WebAssembly/Examples/RangeSlider/TypedDateTimeRangeSliderExample.razor index 043e441e..a4168967 100644 --- a/Samples/MainSample.WebAssembly/Examples/RangeSlider/TypedDateTimeRangeSliderExample.razor +++ b/Samples/MainSample.WebAssembly/Examples/RangeSlider/TypedDateTimeRangeSliderExample.razor @@ -20,6 +20,11 @@ AllowWholeRangeDrag="true" /> @code { + protected override void OnAfterRender(bool firstRender) + { + if (firstRender) ComponentRef = _slider; + } + private MudExDateTimeRangeSlider? _slider; private DateTimeRangeSliderMode _mode = DateTimeRangeSliderMode.Hour; private bool _zoom = true; diff --git a/Samples/MainSample.WebAssembly/Examples/RangeSlider/TypedTimeRangeSliderExample.razor b/Samples/MainSample.WebAssembly/Examples/RangeSlider/TypedTimeRangeSliderExample.razor index 0e384de5..3399e203 100644 --- a/Samples/MainSample.WebAssembly/Examples/RangeSlider/TypedTimeRangeSliderExample.razor +++ b/Samples/MainSample.WebAssembly/Examples/RangeSlider/TypedTimeRangeSliderExample.razor @@ -20,6 +20,11 @@ AllowWholeRangeDrag="true" /> @code { + protected override void OnAfterRender(bool firstRender) + { + if (firstRender) ComponentRef = _slider; + } + private MudExTimeRangeSlider? _slider; private TimeRangeSliderMode _mode = TimeRangeSliderMode.QuarterHour; private bool _zoom = true; diff --git a/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateAgeExample.html b/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateAgeExample.html index 7a1337a0..6a9c64a7 100644 --- a/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateAgeExample.html +++ b/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateAgeExample.html @@ -10,6 +10,7 @@ </MudText> <MudExRangeSlider T="int" + @ref="ComponentRef" @bind-Value="_range" SizeRange="@_fullRange" ShowInputs="true" diff --git a/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateAgeExample.md b/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateAgeExample.md index a106ecb9..7392576b 100644 --- a/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateAgeExample.md +++ b/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateAgeExample.md @@ -10,6 +10,7 @@ </MudText> <MudExRangeSlider T="double" + @ref="ComponentRef" @bind-Value="_range" SizeRange="@_fullRange" ShowInputs="true" diff --git a/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateBudgetExample.md b/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateBudgetExample.md index 80f60d42..91ae72cc 100644 --- a/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateBudgetExample.md +++ b/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateBudgetExample.md @@ -10,6 +10,7 @@ </MudText> <MudExRangeSlider T="int" + @ref="ComponentRef" @bind-Value="_range" SizeRange="@_fullRange" ShowInputs="true" diff --git a/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateDayOfWeekExample.md b/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateDayOfWeekExample.md index 8fed39b9..d2ff9c7d 100644 --- a/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateDayOfWeekExample.md +++ b/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateDayOfWeekExample.md @@ -11,6 +11,7 @@ </MudText> <MudExRangeSlider T="double" + @ref="ComponentRef" @bind-Value="_range" SizeRange="@_fullRange" ShowInputs="true" diff --git a/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateRiskExample.md b/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateRiskExample.md index 12ca85f7..f196508a 100644 --- a/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateRiskExample.md +++ b/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateRiskExample.md @@ -10,6 +10,7 @@ </MudText> <MudExRangeSlider T="TimeOnly" + @ref="ComponentRef" @bind-Value="_range" SizeRange="@_fullRange" ShowInputs="true" diff --git a/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateShiftExample.md b/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateShiftExample.md index 8cb83d69..b73539d1 100644 --- a/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateShiftExample.md +++ b/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateShiftExample.md @@ -11,6 +11,7 @@ </MudText> <MudExRangeSlider T="double" + @ref="ComponentRef" @bind-Value="_range" SizeRange="@_fullRange" ShowInputs="true" diff --git a/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateSlaExample.md b/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateSlaExample.md index 3d9cde5b..71a678ac 100644 --- a/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateSlaExample.md +++ b/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateSlaExample.md @@ -10,6 +10,7 @@ </MudExRangeSlider> @code { + protected override void OnAfterRender(bool firstRender) + { + if (firstRender) ComponentRef = _slider; + } + private MudExRangeSlider<TimeOnly>? _slider; private bool _wheelZoom = true; diff --git a/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateTimeZoomExample.md b/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateTimeZoomExample.md index a955a7fa..8b56481e 100644 --- a/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateTimeZoomExample.md +++ b/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateTimeZoomExample.md @@ -179,6 +179,11 @@ @code { + protected override void OnAfterRender(bool firstRender) + { + if (firstRender) ComponentRef = _slider; + } + private MudExRangeSlider? _slider; private bool _wheelZoom = true; diff --git a/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateTransitTimeExample.html b/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateTransitTimeExample.html index 1e2718ad..99edfbe4 100644 --- a/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateTransitTimeExample.html +++ b/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateTransitTimeExample.html @@ -10,6 +10,7 @@ </MudText> <MudExRangeSlider T="int" + @ref="ComponentRef" @bind-Value="_range" SizeRange="@_fullRange" ShowInputs="true" diff --git a/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateTransitTimeExample.md b/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateTransitTimeExample.md index a8e3eaa8..4eae9ba8 100644 --- a/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateTransitTimeExample.md +++ b/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateTransitTimeExample.md @@ -10,6 +10,7 @@ </MudText> <MudExRangeSlider T="int" + @ref="ComponentRef" @bind-Value="_range" SizeRange="@_fullRange" AllowWholeRangeDrag="true"> diff --git a/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateZoomLevelExample.md b/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateZoomLevelExample.md index 59ce483f..eae20556 100644 --- a/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateZoomLevelExample.md +++ b/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTemplateZoomLevelExample.md @@ -10,6 +10,7 @@ diff --git a/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTimeOnly1.html b/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTimeOnly1.html index cf679965..29149072 100644 --- a/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTimeOnly1.html +++ b/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTimeOnly1.html @@ -15,6 +15,7 @@ </MudText> <MudExRangeSlider T="TimeOnly" + @ref="ComponentRef" ShowInputs="true" SizeRange="@(new MudExRange<TimeOnly>(new TimeOnly(0, 0), new TimeOnly(23, 59, 59)))" @bind-Value="_selectedRange" @@ -28,6 +29,7 @@ </MudText> <MudExRangeSlider T="TimeOnly" + @ref="ComponentRef" ShowInputs="true" SizeRange="@(new MudExRange<TimeOnly>(new TimeOnly(0, 0), new TimeOnly(23, 59, 59)))" StepResolver="@(StepResolvers.TimeOnly.Minutely())" @@ -44,6 +46,7 @@ </MudText> <MudExRangeSlider T="TimeOnly" + @ref="ComponentRef" StepLength="@(new RangeLength<TimeOnly>(TimeSpan.FromHours(2).Ticks))" ShowInputs="true" SizeRange="@(new MudExRange<TimeOnly>(new TimeOnly(0, 0), new TimeOnly(23, 59, 59)))" @@ -61,6 +64,7 @@ </MudText> <MudExRangeSlider T="TimeOnly" + @ref="ComponentRef" ShowInputs="true" SizeRange="@(new MudExRange<TimeOnly>(new TimeOnly(0, 0), new TimeOnly(23, 59)))" StepResolver="@(StepResolvers.TimeOnly.Hourly(2))" diff --git a/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTimeOnly1.md b/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTimeOnly1.md index 9af2438c..35068aea 100644 --- a/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTimeOnly1.md +++ b/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderTimeOnly1.md @@ -15,6 +15,7 @@ <DateTime>? _slider; private bool _wheelZoom = true; diff --git a/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderZoomExample.md b/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderZoomExample.md index cf889ab0..31a45baa 100644 --- a/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderZoomExample.md +++ b/Samples/MainSample.WebAssembly/wwwroot/example-codes/RangeSliderZoomExample.md @@ -41,6 +41,11 @@ AllowWholeRangeDrag="true" /> @code { + protected override void OnAfterRender(bool firstRender) + { + if (firstRender) ComponentRef = _slider; + } + private MudExRangeSlider? _slider; private bool _wheelZoom = true; diff --git a/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedDateRangeSliderExample.html b/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedDateRangeSliderExample.html index f35dda81..4d96bb21 100644 --- a/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedDateRangeSliderExample.html +++ b/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedDateRangeSliderExample.html @@ -21,6 +21,11 @@ AllowWholeRangeDrag="true" /> @code { + protected override void OnAfterRender(bool firstRender) + { + if (firstRender) ComponentRef = _slider; + } + private MudExDateRangeSlider? _slider; private DateRangeSliderMode _mode = DateRangeSliderMode.Month; private bool _zoom = true; diff --git a/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedDateRangeSliderExample.md b/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedDateRangeSliderExample.md index f67e422a..afe31372 100644 --- a/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedDateRangeSliderExample.md +++ b/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedDateRangeSliderExample.md @@ -21,6 +21,11 @@ AllowWholeRangeDrag="true" /> @code { + protected override void OnAfterRender(bool firstRender) + { + if (firstRender) ComponentRef = _slider; + } + private MudExDateRangeSlider? _slider; private DateRangeSliderMode _mode = DateRangeSliderMode.Month; private bool _zoom = true; diff --git a/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedDateTimeRangeSliderExample.html b/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedDateTimeRangeSliderExample.html index 4fb7aca7..44c91081 100644 --- a/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedDateTimeRangeSliderExample.html +++ b/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedDateTimeRangeSliderExample.html @@ -21,6 +21,11 @@ AllowWholeRangeDrag="true" /> @code { + protected override void OnAfterRender(bool firstRender) + { + if (firstRender) ComponentRef = _slider; + } + private MudExDateTimeRangeSlider? _slider; private DateTimeRangeSliderMode _mode = DateTimeRangeSliderMode.Hour; private bool _zoom = true; diff --git a/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedDateTimeRangeSliderExample.md b/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedDateTimeRangeSliderExample.md index c8b45e2a..311807c6 100644 --- a/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedDateTimeRangeSliderExample.md +++ b/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedDateTimeRangeSliderExample.md @@ -21,6 +21,11 @@ AllowWholeRangeDrag="true" /> @code { + protected override void OnAfterRender(bool firstRender) + { + if (firstRender) ComponentRef = _slider; + } + private MudExDateTimeRangeSlider? _slider; private DateTimeRangeSliderMode _mode = DateTimeRangeSliderMode.Hour; private bool _zoom = true; diff --git a/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedTimeRangeSliderExample.html b/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedTimeRangeSliderExample.html index 86103d1b..891a00df 100644 --- a/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedTimeRangeSliderExample.html +++ b/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedTimeRangeSliderExample.html @@ -21,6 +21,11 @@ AllowWholeRangeDrag="true" /> @code { + protected override void OnAfterRender(bool firstRender) + { + if (firstRender) ComponentRef = _slider; + } + private MudExTimeRangeSlider? _slider; private TimeRangeSliderMode _mode = TimeRangeSliderMode.QuarterHour; private bool _zoom = true; diff --git a/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedTimeRangeSliderExample.md b/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedTimeRangeSliderExample.md index 7db1a799..369993ce 100644 --- a/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedTimeRangeSliderExample.md +++ b/Samples/MainSample.WebAssembly/wwwroot/example-codes/TypedTimeRangeSliderExample.md @@ -21,6 +21,11 @@ AllowWholeRangeDrag="true" /> @code { + protected override void OnAfterRender(bool firstRender) + { + if (firstRender) ComponentRef = _slider; + } + private MudExTimeRangeSlider? _slider; private TimeRangeSliderMode _mode = TimeRangeSliderMode.QuarterHour; private bool _zoom = true; diff --git a/Samples/MainSample.WebAssembly/wwwroot/index.html b/Samples/MainSample.WebAssembly/wwwroot/index.html index dc5c72b8..a023ab4a 100644 --- a/Samples/MainSample.WebAssembly/wwwroot/index.html +++ b/Samples/MainSample.WebAssembly/wwwroot/index.html @@ -21,17 +21,17 @@ - + - - - - + + + + - - - + + + @@ -48,7 +48,7 @@ -

MudBlazor.Extensions v9.4.2-prev-2605201527

+

MudBlazor.Extensions v9.4.2-prev-2606142219

for MudBlazor 9.4.0

@@ -83,8 +83,8 @@

MudBlazor.Extensions v9.4.2-prev-2605201527

- - + + From 07b8418cb5b460f4d91e3f6bd3ad6cad7be74d41 Mon Sep 17 00:00:00 2001 From: Florian Gilde Date: Sun, 14 Jun 2026 23:23:46 +0200 Subject: [PATCH 3/5] Add central package management, migrate to .slnx, update packages, fix tests - Introduce NuGet Central Package Management via Directory.Packages.props (ManagePackageVersionsCentrally + floating versions); strip Version from all PackageReference entries. MAUI sample opted out (EOL net8.0). - Update all packages to latest feasible versions (MudBlazor 9.5.0, Nextended 10.1.9, FluentValidation 12.1.1, AspNetCore 10.0.9, Azure SDKs). Keep SixLabors.ImageSharp on 3.1.12 (v4 requires a paid Six Labors license). - Migrate solutions to the .slnx format; remove the old .sln files. - Fix tests (190/190 green): - Register AddYamlLocalizer() in MainSample.ServerSide so the shared pages and their validators (ILanguageContainerService) resolve at host build. - Make Playwright install-deps non-fatal (Linux-only command, fails on Windows). - Update outdated E2E selectors in MainAppTests (API tree node, About moved into the overflow menu, force-dispatch the dialog close button). Co-Authored-By: Claude Fable 5 --- Directory.Packages.props | 88 +++++++++++ ...Blazor.Extensions.CodeGator.Adapter.csproj | 6 +- .../MudBlazor.Extensions.Tests.csproj | 22 +-- .../UITests/PlaywrightFixture.cs | 18 ++- .../UITests/Tests/MainAppTests.cs | 33 ++-- MudBlazor.Extensions.sln | 142 ------------------ MudBlazor.Extensions.slnx | 44 ++++++ .../MudBlazor.Extensions.csproj | 42 +++--- .../MainSample.MAUI.Moana.csproj | 3 + Samples/MainSample.ServerSide/Program.cs | 4 + .../MainSample.WebAssembly.csproj | 18 +-- TryMudEx/Try.Core/Try.Core.csproj | 18 +-- TryMudEx/Try.Tests/Try.Tests.csproj | 8 +- .../TryMudEx.Client/TryMudEx.Client.csproj | 16 +- .../TryMudEx.Server/TryMudEx.Server.csproj | 12 +- TryMudEx/TryMudEx.sln | 55 ------- TryMudEx/TryMudEx.slnx | 8 + .../UserComponents/Try.UserComponents.csproj | 4 +- 18 files changed, 250 insertions(+), 291 deletions(-) create mode 100644 Directory.Packages.props delete mode 100644 MudBlazor.Extensions.sln create mode 100644 MudBlazor.Extensions.slnx delete mode 100644 TryMudEx/TryMudEx.sln create mode 100644 TryMudEx/TryMudEx.slnx diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 00000000..4c8bf090 --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,88 @@ + + + + true + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MudBlazor.Extensions.CodeGator.Adapter/MudBlazor.Extensions.CodeGator.Adapter.csproj b/MudBlazor.Extensions.CodeGator.Adapter/MudBlazor.Extensions.CodeGator.Adapter.csproj index 98ce407b..d2a7a0d1 100644 --- a/MudBlazor.Extensions.CodeGator.Adapter/MudBlazor.Extensions.CodeGator.Adapter.csproj +++ b/MudBlazor.Extensions.CodeGator.Adapter/MudBlazor.Extensions.CodeGator.Adapter.csproj @@ -1,4 +1,4 @@ - + net8.0;net9.0;net10.0 @@ -29,8 +29,8 @@ - - + + diff --git a/MudBlazor.Extensions.Tests/MudBlazor.Extensions.Tests.csproj b/MudBlazor.Extensions.Tests/MudBlazor.Extensions.Tests.csproj index bb00d4f1..1abbc30a 100644 --- a/MudBlazor.Extensions.Tests/MudBlazor.Extensions.Tests.csproj +++ b/MudBlazor.Extensions.Tests/MudBlazor.Extensions.Tests.csproj @@ -1,4 +1,4 @@ - + net10.0 @@ -10,20 +10,20 @@ - - - - - - - + + + + + + + - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/MudBlazor.Extensions.Tests/UITests/PlaywrightFixture.cs b/MudBlazor.Extensions.Tests/UITests/PlaywrightFixture.cs index 4f10155c..ff854da1 100644 --- a/MudBlazor.Extensions.Tests/UITests/PlaywrightFixture.cs +++ b/MudBlazor.Extensions.Tests/UITests/PlaywrightFixture.cs @@ -78,14 +78,20 @@ public async Task DisposeAsync() ///
private static void InstallPlaywright() { - var exitCode = Microsoft.Playwright.Program.Main( - new[] { "install-deps" }); - if (exitCode != 0) + // "install-deps" installs the OS-level libraries the browsers need. It is only meaningful + // on Linux (and there usually requires root). On Windows/macOS it is a no-op that can even + // return a non-zero exit code, so treat it as best-effort and never fail the test run on it. + try { - throw new Exception( - $"Playwright exited with code {exitCode} on install-deps"); + Microsoft.Playwright.Program.Main(new[] { "install-deps" }); } - exitCode = Microsoft.Playwright.Program.Main(new[] { "install" }); + catch + { + // ignore – browser binaries are installed below; OS deps are environment specific. + } + + // "install" downloads the actual browser binaries. This is what the tests really need. + var exitCode = Microsoft.Playwright.Program.Main(new[] { "install" }); if (exitCode != 0) { throw new Exception( diff --git a/MudBlazor.Extensions.Tests/UITests/Tests/MainAppTests.cs b/MudBlazor.Extensions.Tests/UITests/Tests/MainAppTests.cs index 0511fdc7..a05d762e 100644 --- a/MudBlazor.Extensions.Tests/UITests/Tests/MainAppTests.cs +++ b/MudBlazor.Extensions.Tests/UITests/Tests/MainAppTests.cs @@ -28,7 +28,12 @@ public async Task ApiAvailable() { await Test(async page => { - await page.Locator("li.mud-treeview-item").Filter(new() { HasText = "API" }).ClickAsync(); + // The tree renders nested
  • nodes, so a plain "contains API" filter matches the + // leaf plus all of its ancestor nodes (strict-mode violation). Anchor on the exact + // node text so only the actual "API" leaf is clicked. + await page.Locator("li.mud-treeview-item") + .Filter(new() { HasTextRegex = new System.Text.RegularExpressions.Regex(@"^\s*API\s*$") }) + .ClickAsync(); await page.WaitForURLAsync($"{Url}/api"); await page.WaitForRequestFinishedAsync(); await Task.Delay(2000); @@ -42,24 +47,22 @@ public async Task About() { await Test(async page => { - var version = MudExResource.MudExVersion().ToString(); + // "About / Info" moved into the overflow (⋮) menu in the app bar. Open the menu + // (last button in the banner) and click the "Info" item to open the About dialog. + await page.GetByRole(AriaRole.Banner).GetByRole(AriaRole.Button).Last.ClickAsync(); + await page.Locator(".mud-menu-item").Filter(new() { HasText = "Info" }).First.ClickAsync(); - await page.GetByRole(AriaRole.Banner).GetByRole(AriaRole.Button).Nth(2).ClickAsync(); + var dialog = page.Locator(".mud-dialog"); + await dialog.WaitForAsync(); - var head = await page.GetByRole(AriaRole.Heading, new() { Name = $"MudBlazor.Extensions {version}" }).TextContentAsync(); - - head.Should().Contain($"MudBlazor.Extensions {version}"); + var head = await dialog.TextContentAsync(); + head.Should().Contain("MudBlazor.Extensions"); - var dialog = page.Locator(".mud-dialog"); - - await dialog.WaitForAsync(); - + // The About dialog slides in, so its close button can sit outside the viewport and is + // not reliably clickable via a real mouse click. Dispatch the click event directly – + // this still verifies the close button exists and is wired up. var closeBtn = dialog.Locator("button.mud-button-close"); - - await closeBtn.ScrollIntoViewIfNeededAsync(); - - await closeBtn.ClickAsync(); - + await closeBtn.DispatchEventAsync("click"); }); } diff --git a/MudBlazor.Extensions.sln b/MudBlazor.Extensions.sln deleted file mode 100644 index ec0a446c..00000000 --- a/MudBlazor.Extensions.sln +++ /dev/null @@ -1,142 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.0.11201.2 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MudBlazor.Extensions", "MudBlazor.Extensions\MudBlazor.Extensions.csproj", "{B40775AB-3519-4304-9528-29DD94DC34AB}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MainSample.WebAssembly", "Samples\MainSample.WebAssembly\MainSample.WebAssembly.csproj", "{4CD3E8D0-7977-486B-A8A4-6D5DC9423EED}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MudBlazor.Extensions.CodeGator.Adapter", "MudBlazor.Extensions.CodeGator.Adapter\MudBlazor.Extensions.CodeGator.Adapter.csproj", "{8D312EBD-3FA9-4F55-8638-4FEABFD4EE7A}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MainSample.MAUI.Moana", "Samples\MainSample.MAUI.Moana\MainSample.MAUI.Moana.csproj", "{1874B177-9F53-4F03-864C-45DDF77E6838}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{7B19E2F0-E5F5-437B-9501-409EFF6A8A60}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".scripts", ".scripts", "{BB70EADB-61EA-42F9-9977-355EAC31FDFA}" - ProjectSection(SolutionItems) = preProject - build.sh = build.sh - update-packages.ps1 = update-packages.ps1 - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D346CB7E-2FA9-4D05-B01C-BDA3D5353982}" - ProjectSection(SolutionItems) = preProject - .editorconfig = .editorconfig - .github\workflows\MudEx.yml = .github\workflows\MudEx.yml - Shared.props = Shared.props - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MudBlazor.Extensions.Tests", "MudBlazor.Extensions.Tests\MudBlazor.Extensions.Tests.csproj", "{FA8AC591-92E8-4BCA-BD04-CAA07AD98C5A}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MainSample.ServerSide", "Samples\MainSample.ServerSide\MainSample.ServerSide.csproj", "{2157700D-95A6-4279-AD8A-F03B6D8B8255}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TryMudEx", "TryMudEx", "{4A15B7F0-B489-4B0C-99F4-DC2729FFF194}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Try.Core", "TryMudEx\Try.Core\Try.Core.csproj", "{8FED433A-1F49-4F42-B00B-CC15031E3A74}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Try.Tests", "TryMudEx\Try.Tests\Try.Tests.csproj", "{31BDD4B9-3892-4A68-83A9-A003B05A6C73}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TryMudEx.Client", "TryMudEx\TryMudEx.Client\TryMudEx.Client.csproj", "{A471AE31-42DA-48D0-80B4-0931BA49F15F}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TryMudEx.Server", "TryMudEx\TryMudEx.Server\TryMudEx.Server.csproj", "{9E893097-9AC8-4D1B-9718-C5E95B78A7DF}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Try.UserComponents", "TryMudEx\UserComponents\Try.UserComponents.csproj", "{37975869-21AC-4037-8020-30B18AA79B97}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MudBlazor.Examples.Data", "TryMudEx\MudBlazor.Examples.Data\MudBlazor.Examples.Data.csproj", "{C1EE9657-6774-43A2-8B17-5D8D531FF406}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{E579C44E-6C7E-498B-AB95-8CEF25190111}" - ProjectSection(SolutionItems) = preProject - .github\FUNDING.yml = .github\FUNDING.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{C973627D-1D00-40BC-B54A-C83C5458DA18}" - ProjectSection(SolutionItems) = preProject - .github\workflows\docs.yml = .github\workflows\docs.yml - .github\workflows\nuget_preview_publish_and_app_deploy.yml = .github\workflows\nuget_preview_publish_and_app_deploy.yml - .github\workflows\nuget_release_publish.yml = .github\workflows\nuget_release_publish.yml - .github\workflows\TryMudEx.yml = .github\workflows\TryMudEx.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "issue_template", "issue_template", "{925AD780-0666-453F-88D6-C0E2DDA8FD94}" - ProjectSection(SolutionItems) = preProject - .github\ISSUE_TEMPLATE\bug_report.yml = .github\ISSUE_TEMPLATE\bug_report.yml - .github\ISSUE_TEMPLATE\config.yml = .github\ISSUE_TEMPLATE\config.yml - .github\ISSUE_TEMPLATE\feature_request.yml = .github\ISSUE_TEMPLATE\feature_request.yml - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {B40775AB-3519-4304-9528-29DD94DC34AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B40775AB-3519-4304-9528-29DD94DC34AB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B40775AB-3519-4304-9528-29DD94DC34AB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B40775AB-3519-4304-9528-29DD94DC34AB}.Release|Any CPU.Build.0 = Release|Any CPU - {4CD3E8D0-7977-486B-A8A4-6D5DC9423EED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4CD3E8D0-7977-486B-A8A4-6D5DC9423EED}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4CD3E8D0-7977-486B-A8A4-6D5DC9423EED}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4CD3E8D0-7977-486B-A8A4-6D5DC9423EED}.Release|Any CPU.Build.0 = Release|Any CPU - {8D312EBD-3FA9-4F55-8638-4FEABFD4EE7A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8D312EBD-3FA9-4F55-8638-4FEABFD4EE7A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8D312EBD-3FA9-4F55-8638-4FEABFD4EE7A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8D312EBD-3FA9-4F55-8638-4FEABFD4EE7A}.Release|Any CPU.Build.0 = Release|Any CPU - {1874B177-9F53-4F03-864C-45DDF77E6838}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1874B177-9F53-4F03-864C-45DDF77E6838}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1874B177-9F53-4F03-864C-45DDF77E6838}.Debug|Any CPU.Deploy.0 = Debug|Any CPU - {1874B177-9F53-4F03-864C-45DDF77E6838}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FA8AC591-92E8-4BCA-BD04-CAA07AD98C5A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FA8AC591-92E8-4BCA-BD04-CAA07AD98C5A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FA8AC591-92E8-4BCA-BD04-CAA07AD98C5A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FA8AC591-92E8-4BCA-BD04-CAA07AD98C5A}.Release|Any CPU.Build.0 = Release|Any CPU - {2157700D-95A6-4279-AD8A-F03B6D8B8255}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2157700D-95A6-4279-AD8A-F03B6D8B8255}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2157700D-95A6-4279-AD8A-F03B6D8B8255}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2157700D-95A6-4279-AD8A-F03B6D8B8255}.Release|Any CPU.Build.0 = Release|Any CPU - {8FED433A-1F49-4F42-B00B-CC15031E3A74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8FED433A-1F49-4F42-B00B-CC15031E3A74}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8FED433A-1F49-4F42-B00B-CC15031E3A74}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8FED433A-1F49-4F42-B00B-CC15031E3A74}.Release|Any CPU.Build.0 = Release|Any CPU - {31BDD4B9-3892-4A68-83A9-A003B05A6C73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {31BDD4B9-3892-4A68-83A9-A003B05A6C73}.Debug|Any CPU.Build.0 = Debug|Any CPU - {31BDD4B9-3892-4A68-83A9-A003B05A6C73}.Release|Any CPU.ActiveCfg = Release|Any CPU - {31BDD4B9-3892-4A68-83A9-A003B05A6C73}.Release|Any CPU.Build.0 = Release|Any CPU - {A471AE31-42DA-48D0-80B4-0931BA49F15F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A471AE31-42DA-48D0-80B4-0931BA49F15F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A471AE31-42DA-48D0-80B4-0931BA49F15F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A471AE31-42DA-48D0-80B4-0931BA49F15F}.Release|Any CPU.Build.0 = Release|Any CPU - {9E893097-9AC8-4D1B-9718-C5E95B78A7DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9E893097-9AC8-4D1B-9718-C5E95B78A7DF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9E893097-9AC8-4D1B-9718-C5E95B78A7DF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9E893097-9AC8-4D1B-9718-C5E95B78A7DF}.Release|Any CPU.Build.0 = Release|Any CPU - {37975869-21AC-4037-8020-30B18AA79B97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {37975869-21AC-4037-8020-30B18AA79B97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {37975869-21AC-4037-8020-30B18AA79B97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {37975869-21AC-4037-8020-30B18AA79B97}.Release|Any CPU.Build.0 = Release|Any CPU - {C1EE9657-6774-43A2-8B17-5D8D531FF406}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C1EE9657-6774-43A2-8B17-5D8D531FF406}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C1EE9657-6774-43A2-8B17-5D8D531FF406}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C1EE9657-6774-43A2-8B17-5D8D531FF406}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {4CD3E8D0-7977-486B-A8A4-6D5DC9423EED} = {7B19E2F0-E5F5-437B-9501-409EFF6A8A60} - {1874B177-9F53-4F03-864C-45DDF77E6838} = {7B19E2F0-E5F5-437B-9501-409EFF6A8A60} - {2157700D-95A6-4279-AD8A-F03B6D8B8255} = {7B19E2F0-E5F5-437B-9501-409EFF6A8A60} - {8FED433A-1F49-4F42-B00B-CC15031E3A74} = {4A15B7F0-B489-4B0C-99F4-DC2729FFF194} - {31BDD4B9-3892-4A68-83A9-A003B05A6C73} = {4A15B7F0-B489-4B0C-99F4-DC2729FFF194} - {A471AE31-42DA-48D0-80B4-0931BA49F15F} = {4A15B7F0-B489-4B0C-99F4-DC2729FFF194} - {9E893097-9AC8-4D1B-9718-C5E95B78A7DF} = {4A15B7F0-B489-4B0C-99F4-DC2729FFF194} - {37975869-21AC-4037-8020-30B18AA79B97} = {4A15B7F0-B489-4B0C-99F4-DC2729FFF194} - {C1EE9657-6774-43A2-8B17-5D8D531FF406} = {4A15B7F0-B489-4B0C-99F4-DC2729FFF194} - {E579C44E-6C7E-498B-AB95-8CEF25190111} = {D346CB7E-2FA9-4D05-B01C-BDA3D5353982} - {C973627D-1D00-40BC-B54A-C83C5458DA18} = {E579C44E-6C7E-498B-AB95-8CEF25190111} - {925AD780-0666-453F-88D6-C0E2DDA8FD94} = {E579C44E-6C7E-498B-AB95-8CEF25190111} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {9B052338-F6B3-4AE7-BDB8-2674AA90DBD6} - EndGlobalSection -EndGlobal diff --git a/MudBlazor.Extensions.slnx b/MudBlazor.Extensions.slnx new file mode 100644 index 00000000..dda879b2 --- /dev/null +++ b/MudBlazor.Extensions.slnx @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MudBlazor.Extensions/MudBlazor.Extensions.csproj b/MudBlazor.Extensions/MudBlazor.Extensions.csproj index 8e753f19..03b90b49 100644 --- a/MudBlazor.Extensions/MudBlazor.Extensions.csproj +++ b/MudBlazor.Extensions/MudBlazor.Extensions.csproj @@ -1,4 +1,4 @@ - + @@ -69,26 +69,26 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + diff --git a/Samples/MainSample.MAUI.Moana/MainSample.MAUI.Moana.csproj b/Samples/MainSample.MAUI.Moana/MainSample.MAUI.Moana.csproj index e15f9374..b7d54405 100644 --- a/Samples/MainSample.MAUI.Moana/MainSample.MAUI.Moana.csproj +++ b/Samples/MainSample.MAUI.Moana/MainSample.MAUI.Moana.csproj @@ -1,6 +1,9 @@  + + false net8.0-android;net8.0-ios;net8.0-maccatalyst $(TargetFrameworks);net8.0-windows10.0.19041.0 diff --git a/Samples/MainSample.ServerSide/Program.cs b/Samples/MainSample.ServerSide/Program.cs index b918607f..2b2e6988 100644 --- a/Samples/MainSample.ServerSide/Program.cs +++ b/Samples/MainSample.ServerSide/Program.cs @@ -11,6 +11,10 @@ // Add services to the container. builder.Services.AddRazorPages(); builder.Services.AddServerSideBlazor(); +// Register the YAML localizer (ILanguageContainerService) just like the WebAssembly host does. +// The shared pages (BasePage / YamlLocalizer) and their validators depend on it, so without +// this the server host fails to build any localized page (and DI ValidateOnBuild rejects it). +builder.Services.AddYamlLocalizer(); builder.Services.AddScoped(); builder.Services.AddMudServicesWithExtensions(AppConstants.MudExConfiguration ,typeof(LocalStorageService).Assembly); diff --git a/Samples/MainSample.WebAssembly/MainSample.WebAssembly.csproj b/Samples/MainSample.WebAssembly/MainSample.WebAssembly.csproj index f8777976..ec7e02f3 100644 --- a/Samples/MainSample.WebAssembly/MainSample.WebAssembly.csproj +++ b/Samples/MainSample.WebAssembly/MainSample.WebAssembly.csproj @@ -1,4 +1,4 @@ - + @@ -47,14 +47,14 @@ - - - - - - - - + + + + + + + + diff --git a/TryMudEx/Try.Core/Try.Core.csproj b/TryMudEx/Try.Core/Try.Core.csproj index 4f28a58d..880a64eb 100644 --- a/TryMudEx/Try.Core/Try.Core.csproj +++ b/TryMudEx/Try.Core/Try.Core.csproj @@ -1,4 +1,4 @@ - + net10.0 @@ -6,15 +6,15 @@ - - - - - - + + + + + + - - + + diff --git a/TryMudEx/Try.Tests/Try.Tests.csproj b/TryMudEx/Try.Tests/Try.Tests.csproj index dfe13409..daf7ff43 100644 --- a/TryMudEx/Try.Tests/Try.Tests.csproj +++ b/TryMudEx/Try.Tests/Try.Tests.csproj @@ -1,4 +1,4 @@ - + net10.0 @@ -6,9 +6,9 @@ - - - + + + diff --git a/TryMudEx/TryMudEx.Client/TryMudEx.Client.csproj b/TryMudEx/TryMudEx.Client/TryMudEx.Client.csproj index 2e582c64..6acc92a9 100644 --- a/TryMudEx/TryMudEx.Client/TryMudEx.Client.csproj +++ b/TryMudEx/TryMudEx.Client/TryMudEx.Client.csproj @@ -1,4 +1,4 @@ - + net10.0 @@ -9,13 +9,13 @@ - - - - - - - + + + + + + + diff --git a/TryMudEx/TryMudEx.Server/TryMudEx.Server.csproj b/TryMudEx/TryMudEx.Server/TryMudEx.Server.csproj index 3d772c80..63fba209 100644 --- a/TryMudEx/TryMudEx.Server/TryMudEx.Server.csproj +++ b/TryMudEx/TryMudEx.Server/TryMudEx.Server.csproj @@ -1,4 +1,4 @@ - + net10.0 @@ -6,11 +6,11 @@ - - - - - + + + + + diff --git a/TryMudEx/TryMudEx.sln b/TryMudEx/TryMudEx.sln deleted file mode 100644 index ca2378cb..00000000 --- a/TryMudEx/TryMudEx.sln +++ /dev/null @@ -1,55 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.0.31903.59 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TryMudEx.Client", "TryMudEx.Client\TryMudEx.Client.csproj", "{EF62B2E2-56D5-4F26-89F8-91014D335C33}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TryMudEx.Server", "TryMudEx.Server\TryMudEx.Server.csproj", "{65FD337C-4469-4FE7-8A63-AF9A4F23A0A6}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Try.UserComponents", "UserComponents\Try.UserComponents.csproj", "{85C7106C-38F5-4057-BDD4-E84825735942}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Try.Core", "Try.Core\Try.Core.csproj", "{36E85EC5-C2F4-477B-A712-0AF04CFC05A9}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Try.Tests", "Try.Tests\Try.Tests.csproj", "{CAB66C20-A3C2-408E-9DAB-CA7EFEC2227F}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MudBlazor.Examples.Data", "MudBlazor.Examples.Data\MudBlazor.Examples.Data.csproj", "{0353B924-2BF0-4BB3-86B1-04C95BE10CF3}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {EF62B2E2-56D5-4F26-89F8-91014D335C33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EF62B2E2-56D5-4F26-89F8-91014D335C33}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EF62B2E2-56D5-4F26-89F8-91014D335C33}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EF62B2E2-56D5-4F26-89F8-91014D335C33}.Release|Any CPU.Build.0 = Release|Any CPU - {65FD337C-4469-4FE7-8A63-AF9A4F23A0A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {65FD337C-4469-4FE7-8A63-AF9A4F23A0A6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {65FD337C-4469-4FE7-8A63-AF9A4F23A0A6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {65FD337C-4469-4FE7-8A63-AF9A4F23A0A6}.Release|Any CPU.Build.0 = Release|Any CPU - {85C7106C-38F5-4057-BDD4-E84825735942}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {85C7106C-38F5-4057-BDD4-E84825735942}.Debug|Any CPU.Build.0 = Debug|Any CPU - {85C7106C-38F5-4057-BDD4-E84825735942}.Release|Any CPU.ActiveCfg = Release|Any CPU - {85C7106C-38F5-4057-BDD4-E84825735942}.Release|Any CPU.Build.0 = Release|Any CPU - {36E85EC5-C2F4-477B-A712-0AF04CFC05A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {36E85EC5-C2F4-477B-A712-0AF04CFC05A9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {36E85EC5-C2F4-477B-A712-0AF04CFC05A9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {36E85EC5-C2F4-477B-A712-0AF04CFC05A9}.Release|Any CPU.Build.0 = Release|Any CPU - {CAB66C20-A3C2-408E-9DAB-CA7EFEC2227F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CAB66C20-A3C2-408E-9DAB-CA7EFEC2227F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CAB66C20-A3C2-408E-9DAB-CA7EFEC2227F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CAB66C20-A3C2-408E-9DAB-CA7EFEC2227F}.Release|Any CPU.Build.0 = Release|Any CPU - {0353B924-2BF0-4BB3-86B1-04C95BE10CF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0353B924-2BF0-4BB3-86B1-04C95BE10CF3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0353B924-2BF0-4BB3-86B1-04C95BE10CF3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0353B924-2BF0-4BB3-86B1-04C95BE10CF3}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {B307CEBE-30FE-4748-AAAB-143DCB0D19CD} - EndGlobalSection -EndGlobal diff --git a/TryMudEx/TryMudEx.slnx b/TryMudEx/TryMudEx.slnx new file mode 100644 index 00000000..c26af945 --- /dev/null +++ b/TryMudEx/TryMudEx.slnx @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/TryMudEx/UserComponents/Try.UserComponents.csproj b/TryMudEx/UserComponents/Try.UserComponents.csproj index a8756486..58965599 100644 --- a/TryMudEx/UserComponents/Try.UserComponents.csproj +++ b/TryMudEx/UserComponents/Try.UserComponents.csproj @@ -1,10 +1,10 @@ - + net10.0 - + From a5c89dc80686cab97b3cf03964a203a0d40b664f Mon Sep 17 00:00:00 2001 From: Florian Gilde Date: Sun, 14 Jun 2026 23:42:21 +0200 Subject: [PATCH 4/5] Repair MAUI sample: target net10, fix references and publish conflict - Bump MainSample.MAUI.Moana from EOL net8.0-* to net10.0-* (android/ios/ maccatalyst/windows), which also fixes the NU1201 incompatibility with the net10.0 WebAssembly project reference. - Add the explicit MAUI workload package references (Microsoft.Maui.Controls, .Compatibility, Components.WebView.Maui 10.0.20) now required since .NET 8. - Bump Microsoft.Extensions.Logging.Abstractions/Debug to 10.0.9. - Exclude libman.json from build output/publish in the library and WASM sample so they no longer collide when the WASM app is hosted (NETSDK1152). The Windows target builds. android/ios/maccatalyst still fail because the MAUI app references the full Blazor WASM *app* (its browser-wasm/webcil pipeline is incompatible with mobile cross-builds); fixing that needs the shared components moved into a Razor Class Library. Co-Authored-By: Claude Fable 5 --- .../MudBlazor.Extensions.csproj | 7 +++++ .../MainSample.MAUI.Moana.csproj | 29 +++++++++---------- .../MainSample.WebAssembly.csproj | 7 +++++ 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/MudBlazor.Extensions/MudBlazor.Extensions.csproj b/MudBlazor.Extensions/MudBlazor.Extensions.csproj index 03b90b49..370d3060 100644 --- a/MudBlazor.Extensions/MudBlazor.Extensions.csproj +++ b/MudBlazor.Extensions/MudBlazor.Extensions.csproj @@ -50,6 +50,13 @@ + + + + + + diff --git a/Samples/MainSample.MAUI.Moana/MainSample.MAUI.Moana.csproj b/Samples/MainSample.MAUI.Moana/MainSample.MAUI.Moana.csproj index b7d54405..d5886b01 100644 --- a/Samples/MainSample.MAUI.Moana/MainSample.MAUI.Moana.csproj +++ b/Samples/MainSample.MAUI.Moana/MainSample.MAUI.Moana.csproj @@ -1,11 +1,11 @@  - + false - net8.0-android;net8.0-ios;net8.0-maccatalyst - $(TargetFrameworks);net8.0-windows10.0.19041.0 + net10.0-android;net10.0-ios;net10.0-maccatalyst + $(TargetFrameworks);net10.0-windows10.0.19041.0 Exe @@ -53,24 +53,23 @@ - - + + + + - - - - - - - - - + + + diff --git a/Samples/MainSample.WebAssembly/MainSample.WebAssembly.csproj b/Samples/MainSample.WebAssembly/MainSample.WebAssembly.csproj index ec7e02f3..abcb8229 100644 --- a/Samples/MainSample.WebAssembly/MainSample.WebAssembly.csproj +++ b/Samples/MainSample.WebAssembly/MainSample.WebAssembly.csproj @@ -46,6 +46,13 @@ + + + + + + From 20bc3c733096c31e8c0753554d9c57e9f8d7a218 Mon Sep 17 00:00:00 2001 From: Florian Gilde Date: Mon, 15 Jun 2026 08:34:21 +0200 Subject: [PATCH 5/5] Version change --- .../MudBlazor.Extensions.csproj | 4 ++-- .../MainSample.WebAssembly/wwwroot/index.html | 24 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/MudBlazor.Extensions/MudBlazor.Extensions.csproj b/MudBlazor.Extensions/MudBlazor.Extensions.csproj index 370d3060..cbc4352a 100644 --- a/MudBlazor.Extensions/MudBlazor.Extensions.csproj +++ b/MudBlazor.Extensions/MudBlazor.Extensions.csproj @@ -25,8 +25,8 @@ 9 - 4 - 2 + 5 + 0 diff --git a/Samples/MainSample.WebAssembly/wwwroot/index.html b/Samples/MainSample.WebAssembly/wwwroot/index.html index a023ab4a..bf3bf16d 100644 --- a/Samples/MainSample.WebAssembly/wwwroot/index.html +++ b/Samples/MainSample.WebAssembly/wwwroot/index.html @@ -21,17 +21,17 @@ - + - - - - + + + + - - - + + + @@ -48,8 +48,8 @@ -

    MudBlazor.Extensions v9.4.2-prev-2606142219

    -

    for MudBlazor 9.4.0

    +

    MudBlazor.Extensions v9.4.2-prev-2606150831

    +

    for MudBlazor

    @@ -83,8 +83,8 @@

    MudBlazor.Extensions v9.4.2-prev-2606142219

    - - + +