Compare commits

...

25 Commits

Author SHA1 Message Date
Vladimir Enchev
7ae64ba919 Version updated 2026-02-12 12:22:54 +02:00
Vladimir Enchev
6e94a7c65b DataGrid column defaults lost on clear filter 2026-02-12 12:22:18 +02:00
yordanov
44e20b2b5f Update premium themes 2026-02-12 11:49:41 +02:00
Atanas Korchev
d109c2e295 Remove NET7 preprocessor guards from Blazor components.
Assume .NET 7+ paths by default and delete legacy fallback-only event handler declarations.
2026-02-12 11:22:24 +02:00
yordanov
0feffad278 Fix secondary text button colors in dark themes 2026-02-12 11:20:39 +02:00
Vladimir Enchev
3be412643f Revert "DataGrid column initialization logic improved"
This reverts commit 547a681878.
2026-02-12 11:10:01 +02:00
Atanas Korchev
6d8c4ceb16 Remove NET6-specific conditions from Blazor library sources.
Drop net6 package references and inline NET6_0_OR_GREATER code paths now that .NET 6 support is no longer targeted.
2026-02-12 10:46:45 +02:00
Atanas Korchev
40b7d84224 Remove .NET 6 instructions from onboarding demo pages.
Keep tab navigation stable by defaulting to the first tab when legacy version routes are requested.
2026-02-12 10:46:45 +02:00
Atanas Korchev
4511c654f9 Normalize demo routes and enforce canonical URL redirects.
Use middleware to permanently redirect legacy/docs aliases and trailing-slash URLs to canonical paths, and keep only canonical @page directives on demo pages to prevent duplicate content and route drift.
2026-02-12 10:46:45 +02:00
Vladimir Enchev
547a681878 DataGrid column initialization logic improved 2026-02-12 10:38:46 +02:00
yordanov
cad80d533d Fix RadzenLogin tests 2026-02-12 10:29:50 +02:00
yordanov
f485420ba9 Fix RadzenLink sizing and RadzenLogin button styles 2026-02-12 10:23:33 +02:00
Vladimir Enchev
ac61235a48 ExpressionParser support for qualified array type fixed 2026-02-12 09:34:32 +02:00
Vladimir Enchev
61bd1db8b9 More possible DataGrid memory leaks fixed 2026-02-11 22:41:37 +02:00
Vladimir Enchev
5992a97ef9 RadzenDropDownDataGrid will not search as you type
Fix #2444
2026-02-11 22:31:08 +02:00
Vladimir Enchev
157070c922 DropDownBase selection not cleared in some cases 2026-02-11 22:03:10 +02:00
Vladimir Enchev
14e39f665f Version updated 2026-02-11 11:36:55 +02:00
Vladimir Enchev
2f77a0b849 Fixed Invalid operation exception with complex filter queries
Expression.Default support added to ExpressionSerializer
2026-02-11 11:36:35 +02:00
Vladimir Enchev
c3af020b81 Tooltip position in RTL mode improved 2026-02-11 11:16:53 +02:00
Vladimir Enchev
19460b899b Fix to possible DotNetObjectReference instance was already disposed 2026-02-11 10:50:41 +02:00
Vladimir Enchev
3cf12c82bf Version updated 2026-02-10 18:09:08 +02:00
Vladimir Enchev
adf86b2896 DataGrid CheckBoxList Filter crashes page in Firefox
Fixed null value for attributes and edge cases in Blazor HTML update under FF #2442
2026-02-10 18:08:28 +02:00
Atanas Korchev
7b96fe3cb4 Fix llms.txt generator: preserve @code blocks, fix @page regex, remove noise
- Add \b word boundary to directive regex so @page no longer matches
  @pageSizeOptions, @pageIndex etc., which was stripping attribute values
  and leaving broken Razor markup in the output.
- Stop stripping @code blocks from example files — they contain the C#
  code (methods, fields, event handlers) needed to understand examples.
- Exclude AccessibilityPage, GetStarted, and ThemeServicePage from
  generation (generic setup/WCAG content, not component documentation).
- Skip "Keyboard Navigation" sections (shortcuts rendered from C# data
  that the generator cannot extract, leaving empty placeholders).
- Skip "Radzen Blazor Studio" sections (IDE-specific, not component API).
2026-02-09 20:17:33 +02:00
Atanas Korchev
9a58c3a35a Exclude sub-component pages from llms.txt sections
Sub-component .razor files without @page directives were getting
standalone sections with rendered placeholder text instead of being
embedded as code snippets via <RadzenExample> in their parent pages.
2026-02-09 17:35:55 +02:00
Atanas Korchev
876c248a8e Cleanup llms.txt from marketing content and other noise. 2026-02-09 17:12:48 +02:00
119 changed files with 1013 additions and 775 deletions

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
@@ -1026,6 +1026,36 @@ public class ExpressionParserTests
Assert.True(func(new Person { BirthDate = DateTime.Parse("5/5/2000 12:00:00 AM") }));
}
class EmployeeWithHireDate
{
public DateTime? HireDate { get; set; }
public DateOnly? HireDateOnly { get; set; }
}
[Fact]
public void Should_SupportNullableDateTimeArrayWithSpecifyKindAndNullableProperty()
{
var predicate = "x => new System.DateTime?[] { DateTime.SpecifyKind(DateTime.Parse(\"2012-04-01\", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind), DateTimeKind.Unspecified) }.Contains((x.HireDate ?? null))";
var expression = ExpressionParser.ParsePredicate<EmployeeWithHireDate>(predicate);
var func = expression.Compile();
var hireDate = DateTime.SpecifyKind(DateTime.Parse("2012-04-01", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.RoundtripKind), DateTimeKind.Unspecified);
Assert.True(func(new EmployeeWithHireDate { HireDate = hireDate }));
Assert.False(func(new EmployeeWithHireDate { HireDate = DateTime.Parse("2013-01-01") }));
}
[Fact]
public void Should_SupportNullableDateOnlyArrayAndNullableProperty()
{
var predicate = "x => new System.DateOnly?[] { DateOnly.Parse(\"2012-04-01\") }.Contains((x.HireDateOnly ?? null))";
var expression = ExpressionParser.ParsePredicate<EmployeeWithHireDate>(predicate);
var func = expression.Compile();
var hireDate = DateOnly.Parse("2012-04-01");
Assert.True(func(new EmployeeWithHireDate { HireDateOnly = hireDate }));
Assert.False(func(new EmployeeWithHireDate { HireDateOnly = DateOnly.Parse("2013-01-01") }));
}
[Fact]
public void Should_SupportNumericConversion()
{

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
@@ -319,5 +319,35 @@ namespace Radzen.Blazor.Tests
Expression<Func<TestEntity, bool>> expr = e => !e.Tags.Contains("Member");
Assert.Equal("e => (!(e.Tags.Contains(\"Member\")))", _serializer.Serialize(expr));
}
[Fact]
public void Serializes_DefaultExpression_ReferenceType()
{
// Simulates the NullPropagate pattern: x.Address == null ? default(string) : x.Address.City
var param = Expression.Parameter(typeof(TestEntity), "x");
var address = Expression.Property(param, "Address");
var city = Expression.Property(address, "City");
var isNull = Expression.Equal(address, Expression.Constant(null, typeof(Address)));
var whenNull = Expression.Default(typeof(string));
var conditional = Expression.Condition(isNull, whenNull, city);
var lambda = Expression.Lambda<Func<TestEntity, string>>(conditional, param);
Assert.Equal("x => ((x.Address == null) ? null : x.Address.City)", _serializer.Serialize(lambda));
}
[Fact]
public void Serializes_DefaultExpression_ValueType()
{
// Simulates a conditional with a default value type
var param = Expression.Parameter(typeof(TestEntity), "x");
var address = Expression.Property(param, "Address");
var age = Expression.Property(param, "Age");
var isNull = Expression.Equal(address, Expression.Constant(null, typeof(Address)));
var whenNull = Expression.Default(typeof(int));
var conditional = Expression.Condition(isNull, whenNull, age);
var lambda = Expression.Lambda<Func<TestEntity, int>>(conditional, param);
Assert.Equal("x => ((x.Address == null) ? default(int) : x.Age)", _serializer.Serialize(lambda));
}
}
}

View File

@@ -119,7 +119,7 @@ namespace Radzen.Blazor.Tests
parameters.Add(p => p.AllowResetPassword, true);
});
Assert.Contains(@$"Forgot password?</button>", component.Markup);
Assert.Contains(@$"Forgot password?</span>", component.Markup);
}
[Fact]
@@ -134,7 +134,7 @@ namespace Radzen.Blazor.Tests
parameters.Add(p => p.ResetPasswordText, "Test");
});
Assert.Contains(@$"Test</button>", component.Markup);
Assert.Contains(@$"Test</span>", component.Markup);
}
[Fact]
@@ -195,7 +195,7 @@ namespace Radzen.Blazor.Tests
parameters.Add(p => p.Register, args => { clicked = true; });
});
component.Find(".rz-secondary").Click();
component.Find(".rz-secondary.rz-variant-flat").Click();
Assert.True(clicked);
}
@@ -215,7 +215,7 @@ namespace Radzen.Blazor.Tests
parameters.Add(p => p.ResetPassword, args => { clicked = true; });
});
component.Find("button.rz-link").Click();
component.Find(".rz-secondary.rz-variant-text").Click();
Assert.True(clicked);
}
@@ -234,7 +234,7 @@ namespace Radzen.Blazor.Tests
parameters.Add(p => p.ResetPassword, args => { clicked = true; });
});
component.Find("button.rz-link").Click();
component.Find(".rz-secondary.rz-variant-text").Click();
Assert.True(clicked);
}

View File

@@ -105,12 +105,10 @@ namespace Radzen.Blazor
throw new ArgumentException($"Property {propertyName} does not exist");
}
#if NET6_0_OR_GREATER
if(PropertyAccess.IsDateOnly(property))
{
return false;
}
#endif
return PropertyAccess.IsDate(property);
}

View File

@@ -1,4 +1,4 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.JSInterop;
using System;
@@ -1086,6 +1086,10 @@ namespace Radzen
selectedItems.Clear();
}
}
else if (internalValue == null && Multiple && selectedItems.Count > 0)
{
selectedItems.Clear();
}
SelectItemFromValue(internalValue);

View File

@@ -1,17 +0,0 @@
using System;
using Microsoft.AspNetCore.Components;
namespace Radzen;
#if NET7_0_OR_GREATER
#else
/// <summary>
/// Enables "onmouseenter" and "onmouseleave" event support in Blazor. Not for public use.
/// </summary>
[EventHandler("onmouseenter", typeof(EventArgs), true, true)]
[EventHandler("onmouseleave", typeof(EventArgs), true, true)]
public static class EventHandlers
{
}
#endif

View File

@@ -414,6 +414,11 @@ public class ExpressionParser
return ParseStaticMemberAccess(type, parameter);
}
if (TryParseQualifiedType(out var qualifiedType))
{
return ParseStaticMemberAccess(qualifiedType, parameter);
}
if (Peek(1).Type == TokenType.OpenParen)
{
Advance(1);
@@ -542,19 +547,10 @@ public class ExpressionParser
else
{
Type? elementType = null;
var nullable = false;
if (token.Type == TokenType.Identifier)
if (TryParseQualifiedArrayType(out var parsedElementType))
{
var typeName = token.Value;
elementType = GetWellKnownType(typeName);
Advance(1);
if (Peek().Type == TokenType.QuestionMark)
{
nullable = true;
Advance(1);
}
elementType = parsedElementType;
}
Expect(TokenType.OpenBracket);
@@ -585,11 +581,6 @@ public class ExpressionParser
elementType = elements.Count > 0 ? elements[0].Type : typeof(object);
}
if (nullable)
{
elementType = typeof(Nullable<>).MakeGenericType(elementType);
}
return Expression.NewArrayInit(elementType, elements.Select(e => ConvertIfNeeded(e, elementType)));
}
default:
@@ -653,7 +644,7 @@ public class ExpressionParser
{
var name = typeName.ToString();
var type = GetWellKnownType(name) ?? typeResolver?.Invoke(name) ?? throw new InvalidOperationException($"Could not resolve type: {typeName}");
var type = ResolveType(name) ?? throw new InvalidOperationException($"Could not resolve type: {typeName}");
if (nullable && type.IsValueType)
{
@@ -729,6 +720,156 @@ public class ExpressionParser
return Expression.Call(null, method, arguments);
}
/// <summary>
/// Tries to parse a qualified type name (e.g. System.DateTime or System.DateTime?) and returns the resolved type.
/// Advances position past the type name. The next token will be . for member access or [ for array.
/// Uses backtracking to find the longest resolvable type prefix (e.g. System.DateTime in System.DateTime.SpecifyKind).
/// </summary>
private bool TryParseQualifiedType(out Type type)
{
type = null!;
var token = Peek();
if (token.Type != TokenType.Identifier)
{
return false;
}
var startPosition = position;
var parts = new List<string> { token.Value };
Advance(1);
while (Peek().Type == TokenType.Dot)
{
Advance(1);
token = Peek();
if (token.Type != TokenType.Identifier)
{
return false;
}
parts.Add(token.Value);
Advance(1);
}
var nullable = false;
if (Peek().Type == TokenType.QuestionMark)
{
nullable = true;
Advance(1);
}
for (var i = parts.Count; i >= 1; i--)
{
var typeName = string.Join(".", parts.Take(i));
var resolvedType = ResolveType(typeName);
if (resolvedType != null)
{
if (nullable && resolvedType.IsValueType)
{
resolvedType = typeof(Nullable<>).MakeGenericType(resolvedType);
}
position = startPosition;
var tokensToConsume = (i * 2) - 1 + (nullable ? 1 : 0);
for (var t = 0; t < tokensToConsume; t++)
{
Advance(1);
}
type = resolvedType;
return true;
}
}
position = startPosition;
return false;
}
/// <summary>
/// Tries to parse a qualified array element type (e.g. System.DateTime? or DateTime) before [].
/// Advances position past the type name to the [. Returns the element type for the array.
/// </summary>
private bool TryParseQualifiedArrayType(out Type? elementType)
{
elementType = null;
var token = Peek();
if (token.Type != TokenType.Identifier)
{
return false;
}
var startPosition = position;
var parts = new List<string> { token.Value };
Advance(1);
while (Peek().Type == TokenType.Dot)
{
Advance(1);
token = Peek();
if (token.Type != TokenType.Identifier)
{
position = startPosition;
return false;
}
parts.Add(token.Value);
Advance(1);
}
var nullable = false;
if (Peek().Type == TokenType.QuestionMark)
{
nullable = true;
Advance(1);
}
if (Peek().Type != TokenType.OpenBracket)
{
position = startPosition;
return false;
}
for (var i = parts.Count; i >= 1; i--)
{
var typeName = string.Join(".", parts.Take(i));
var resolvedType = ResolveType(typeName);
if (resolvedType != null)
{
if (nullable && resolvedType.IsValueType)
{
resolvedType = typeof(Nullable<>).MakeGenericType(resolvedType);
}
elementType = resolvedType;
return true;
}
}
position = startPosition;
return false;
}
/// <summary>
/// Resolves a type name using well-known types, the optional type resolver, or by searching loaded assemblies.
/// This allows any type from loaded assemblies to be resolved without hardcoding.
/// </summary>
private Type? ResolveType(string typeName)
{
return GetWellKnownType(typeName)
?? typeResolver?.Invoke(typeName)
?? ResolveTypeFromAssemblies(typeName);
}
private static Type? ResolveTypeFromAssemblies(string typeName)
{
return AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a => a.GetTypes())
.FirstOrDefault(t =>
{
var fullName = t.FullName;
return fullName != null && fullName.Replace("+", ".", StringComparison.Ordinal) == typeName;
});
}
private static Type? GetWellKnownType(string typeName)
{
return typeName switch

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
@@ -272,6 +272,21 @@ public class ExpressionSerializer : ExpressionVisitor
return node;
}
/// <inheritdoc/>
protected override Expression VisitDefault(DefaultExpression node)
{
ArgumentNullException.ThrowIfNull(node);
if (!node.Type.IsValueType || Nullable.GetUnderlyingType(node.Type) != null)
{
_sb.Append("null");
}
else
{
_sb.Append(CultureInfo.InvariantCulture, $"default({node.Type.DisplayName(true).Replace("+", ".", StringComparison.Ordinal)})");
}
return node;
}
/// <summary>
/// Maps an ExpressionType to its corresponding C# operator.
/// </summary>

View File

@@ -115,12 +115,10 @@ public static class PropertyAccess
{
return true;
}
#if NET6_0_OR_GREATER
if (type == typeof(DateOnly))
{
return true;
}
#endif
return false;
}
@@ -134,12 +132,10 @@ public static class PropertyAccess
if (source == null) return false;
var type = source.IsGenericType ? source.GetGenericArguments()[0] : source;
#if NET6_0_OR_GREATER
if (type == typeof(DateOnly))
{
return true;
}
#endif
return false;
}
@@ -151,9 +147,7 @@ public static class PropertyAccess
public static object? DateOnlyFromDateTime(DateTime source)
{
object? result = null;
#if NET6_0_OR_GREATER
result = DateOnly.FromDateTime(source);
#endif
return result;
}

View File

@@ -38,9 +38,7 @@ namespace Radzen
private readonly NavigationManager navigationManager;
private readonly ThemeService themeService;
#if NET7_0_OR_GREATER
private readonly IDisposable? registration;
#endif
private readonly QueryStringThemeServiceOptions? options;
private readonly PropertyInfo? hasAttachedJSRuntimeProperty;
@@ -74,7 +72,6 @@ namespace Radzen
themeService.ThemeChanged += OnThemeChanged;
#if NET7_0_OR_GREATER
try
{
registration = navigationManager.RegisterLocationChangingHandler(OnLocationChanging);
@@ -86,14 +83,12 @@ namespace Radzen
// avoid calling NavigateTo which would cause a 302 redirect during prerendering.
themeService.ThemeChanged -= OnThemeChanged;
}
#endif
}
private bool RequiresChange((string? theme, bool? wcag, bool? rightToLeft) state) =>
(state.theme != null && !string.Equals(themeService.Theme, state.theme, StringComparison.OrdinalIgnoreCase)) ||
themeService.Wcag != state.wcag || themeService.RightToLeft != state.rightToLeft;
#if NET7_0_OR_GREATER
private ValueTask OnLocationChanging(LocationChangingContext context)
{
var state = GetStateFromQueryString(context.TargetLocation);
@@ -107,7 +102,6 @@ namespace Radzen
return ValueTask.CompletedTask;
}
#endif
private (string? theme, bool? wcag, bool? rightToLeft) GetStateFromQueryString(string uri)
{
@@ -158,9 +152,7 @@ namespace Radzen
{
themeService.ThemeChanged -= OnThemeChanged;
#if NET7_0_OR_GREATER
registration?.Dispose();
#endif
GC.SuppressFinalize(this);
}

View File

@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
@@ -11,7 +11,7 @@
<IsPackable>true</IsPackable>
<PackageId>Radzen.Blazor</PackageId>
<Product>Radzen.Blazor</Product>
<Version>9.0.1</Version>
<Version>9.0.4</Version>
<Copyright>Radzen Ltd.</Copyright>
<Authors>Radzen Ltd.</Authors>
<Description>Radzen Blazor is the most sophisticated free UI component library for Blazor, featuring 100+ native components including DataGrid, Scheduler, Charts, and advanced theming with full support for Material Design and Fluent UI.</Description>
@@ -36,8 +36,6 @@
<ItemGroup>
<PackageReference Include="DartSassBuilder" Version="1.1.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.AspNetCore.Components" Condition="'$(TargetFramework)' == 'net6.0'" Version="6.0.25" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Condition="'$(TargetFramework)' == 'net6.0'" Version="6.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components" Condition="'$(TargetFramework)' == 'net7.0'" Version="7.0.14" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Condition="'$(TargetFramework)' == 'net7.0'" Version="7.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components" Condition="'$(TargetFramework)' == 'net8.0'" Version="8.0.0" />

View File

@@ -326,8 +326,6 @@ namespace Radzen
debouncer?.Dispose();
debouncer = null;
reference?.Dispose();
reference = null;
if (IsJSRuntimeAvailable && JSRuntime != null && !string.IsNullOrEmpty(UniqueID))
{
@@ -346,6 +344,9 @@ namespace Radzen
JSRuntime.InvokeVoid("Radzen.removeMouseLeave", UniqueID);
}
}
reference?.Dispose();
reference = null;
}
/// <summary>

View File

@@ -1,4 +1,4 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -10,9 +10,7 @@ namespace Radzen.Blazor
/// RadzenDataFilter component.
/// </summary>
/// <typeparam name="TItem">The type of the item.</typeparam>
#if NET6_0_OR_GREATER
[CascadingTypeParameter(nameof(TItem))]
#endif
public partial class RadzenDataFilter<TItem> : RadzenComponent
{
/// <inheritdoc />

View File

@@ -83,9 +83,7 @@ namespace Radzen.Blazor
/// }
/// </code>
/// </example>
#if NET6_0_OR_GREATER
[CascadingTypeParameter(nameof(TItem))]
#endif
public partial class RadzenDataGrid<TItem> : PagedDataBoundComponent<TItem> where TItem : notnull
{
private static readonly string[] DefaultGroupProperty = new string[] { "it" };
@@ -3647,6 +3645,15 @@ namespace Radzen.Blazor
_value = null;
Data = null;
_view = null;
_groupedPagedView = null;
if (Query != null)
{
Query.GetFilter = null;
Query.Filters = null;
Query.Sorts = null;
}
GC.SuppressFinalize(this);
}

View File

@@ -1,4 +1,4 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using System;
using System.Collections;
@@ -188,76 +188,80 @@ namespace Radzen.Blazor
if (Grid != null)
{
Grid.AddColumn(this);
SetColumnDefaults();
}
}
var canSetFilterPropertyType = (FilterMode ?? Grid.FilterMode) == Radzen.FilterMode.CheckBoxList && FilterTemplate == null;
private void SetColumnDefaults()
{
var canSetFilterPropertyType = (FilterMode ?? Grid.FilterMode) == Radzen.FilterMode.CheckBoxList && FilterTemplate == null;
if (canSetFilterPropertyType)
if (canSetFilterPropertyType)
{
if (Type == null)
{
if (Type == null)
{
var fp = GetFilterProperty();
var pt = !string.IsNullOrEmpty(fp) ?
PropertyAccess.GetPropertyType(typeof(TItem), fp) : typeof(object);
var fp = GetFilterProperty();
var pt = !string.IsNullOrEmpty(fp) ?
PropertyAccess.GetPropertyType(typeof(TItem), fp) : typeof(object);
_filterPropertyType = typeof(IEnumerable<>).MakeGenericType(pt!);
}
if (GetFilterOperator() == FilterOperator.Equals)
{
SetFilterOperator(FilterOperator.Contains);
}
Grid.FilterPopupRenderMode = PopupRenderMode.OnDemand;
_filterPropertyType = typeof(IEnumerable<>).MakeGenericType(pt!);
}
var property = GetFilterProperty();
if (!string.IsNullOrEmpty(property) && Type == null)
{
_propertyType = PropertyAccess.GetPropertyType(typeof(TItem), property);
}
if (!string.IsNullOrEmpty(property) && Type == null && !canSetFilterPropertyType)
{
_filterPropertyType = _propertyType;
}
if (_filterPropertyType == null)
{
_filterPropertyType = Type;
}
else if (!string.IsNullOrEmpty(Property))
{
propertyValueGetter = PropertyAccess.Getter<TItem, object>(Property);
}
if (!string.IsNullOrEmpty(Property) && (typeof(TItem).IsGenericType && typeof(IDictionary<,>).IsAssignableFrom(typeof(TItem).GetGenericTypeDefinition()) ||
typeof(IDictionary).IsAssignableFrom(typeof(TItem)) || typeof(System.Data.DataRow).IsAssignableFrom(typeof(TItem))))
{
propertyValueGetter = PropertyAccess.Getter<TItem, object>(Property);
}
if (_filterPropertyType == typeof(string) && filterOperator != FilterOperator.Custom && filterOperator == null && _filterOperator == null)
if (GetFilterOperator() == FilterOperator.Equals)
{
SetFilterOperator(FilterOperator.Contains);
}
if (!string.IsNullOrEmpty(Property) && !string.IsNullOrEmpty(FilterProperty))
UniqueID = $"{Property}.{FilterProperty}"; // To be sure the column uniqueID is unique even when filtering on sub property.
else
UniqueID = !string.IsNullOrEmpty(Property) ? Property : FilterProperty;
if (UseDisplayName && !string.IsNullOrEmpty(Property))
Grid.FilterPopupRenderMode = PopupRenderMode.OnDemand;
}
var property = GetFilterProperty();
if (!string.IsNullOrEmpty(property) && Type == null)
{
_propertyType = PropertyAccess.GetPropertyType(typeof(TItem), property);
}
if (!string.IsNullOrEmpty(property) && Type == null && !canSetFilterPropertyType)
{
_filterPropertyType = _propertyType;
}
if (_filterPropertyType == null)
{
_filterPropertyType = Type;
}
else if (!string.IsNullOrEmpty(Property))
{
propertyValueGetter = PropertyAccess.Getter<TItem, object>(Property);
}
if (!string.IsNullOrEmpty(Property) && (typeof(TItem).IsGenericType && typeof(IDictionary<,>).IsAssignableFrom(typeof(TItem).GetGenericTypeDefinition()) ||
typeof(IDictionary).IsAssignableFrom(typeof(TItem)) || typeof(System.Data.DataRow).IsAssignableFrom(typeof(TItem))))
{
propertyValueGetter = PropertyAccess.Getter<TItem, object>(Property);
}
if (_filterPropertyType == typeof(string) && filterOperator != FilterOperator.Custom && filterOperator == null && _filterOperator == null)
{
SetFilterOperator(FilterOperator.Contains);
}
if (!string.IsNullOrEmpty(Property) && !string.IsNullOrEmpty(FilterProperty))
UniqueID = $"{Property}.{FilterProperty}"; // To be sure the column uniqueID is unique even when filtering on sub property.
else
UniqueID = !string.IsNullOrEmpty(Property) ? Property : FilterProperty;
if (UseDisplayName && !string.IsNullOrEmpty(Property))
{
var propInfo = typeof(TItem).GetProperty(Property);
if (propInfo != null)
{
var propInfo = typeof(TItem).GetProperty(Property);
if (propInfo != null)
var displayAttr = propInfo.GetCustomAttributes(typeof(DisplayAttribute), true)
.FirstOrDefault() as DisplayAttribute;
if (displayAttr?.Name != null)
{
var displayAttr = propInfo.GetCustomAttributes(typeof(DisplayAttribute), true)
.FirstOrDefault() as DisplayAttribute;
if (displayAttr?.Name != null)
{
Title = displayAttr.Name;
}
Title = displayAttr.Name;
}
}
}
@@ -1353,7 +1357,7 @@ namespace Radzen.Blazor
/// </summary>
public virtual void ClearFilters()
{
var fo = FilterOperator == FilterOperator.Custom
var fo = GetFilterOperator() == FilterOperator.Custom
? FilterOperator.Custom
: typeof(System.Collections.IEnumerable).IsAssignableFrom(FilterPropertyType)
? !string.IsNullOrEmpty(FilterProperty) && FilterProperty != Property ? FilterOperator.In : FilterOperator.Contains
@@ -1373,6 +1377,7 @@ namespace Radzen.Blazor
LogicalFilterOperator = default(LogicalFilterOperator);
SetColumnDefaults();
}
FilterOperator? _filterOperator;

View File

@@ -173,7 +173,7 @@
}
else
{
@(!string.IsNullOrEmpty(Column.FormatString) ? string.Format(Column.FormatProvider ?? Grid?.Culture ?? CultureInfo.CurrentCulture, Column.FormatString, context ?? string.Empty) : Convert.ToString(context, Grid?.Culture ?? CultureInfo.CurrentCulture))
@(!string.IsNullOrEmpty(Column.FormatString) ? string.Format(Column.FormatProvider ?? Grid?.Culture ?? CultureInfo.CurrentCulture, Column.FormatString, context ?? string.Empty) : Convert.ToString(context ?? string.Empty, Grid?.Culture ?? CultureInfo.CurrentCulture))
}
</Template>
</RadzenListBox>

View File

@@ -1,4 +1,4 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components;
using System.Linq;
using System.Threading.Tasks;
@@ -35,9 +35,7 @@ namespace Radzen.Blazor
/// &lt;/RadzenDataList&gt;
/// </code>
/// </example>
#if NET6_0_OR_GREATER
[CascadingTypeParameter(nameof(TItem))]
#endif
public partial class RadzenDataList<TItem> : PagedDataBoundComponent<TItem>
{
/// <inheritdoc />

View File

@@ -1,4 +1,4 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.JSInterop;
@@ -356,13 +356,11 @@ namespace Radzen.Blazor
UpdateYearsAndMonths(Min, Max);
#if NET6_0_OR_GREATER
if (typeof(TValue) == typeof(TimeOnly) || typeof(TValue) == typeof(TimeOnly?))
{
TimeOnly = true;
ShowTime = true;
}
#endif
}
void UpdateYearsAndMonths(DateTime? min, DateTime? max)
@@ -541,7 +539,6 @@ namespace Radzen.Blazor
_value = dtEnum;
_dateTimeValue = selectedDates.LastOrDefault();
}
#if NET6_0_OR_GREATER
else if (value is IEnumerable<DateTime?> ndtEnum)
{
selectedDates = ndtEnum.Where(d => d.HasValue && d.Value != default(DateTime))
@@ -552,19 +549,6 @@ namespace Radzen.Blazor
_value = ndtEnum;
_dateTimeValue = selectedDates.LastOrDefault();
}
#else
else if (value is IEnumerable<System.Nullable<DateTime>> ndtEnum)
{
selectedDates = ndtEnum.Where(d => d.HasValue && d.Value != default(DateTime))
.Select(d => DateTime.SpecifyKind(d.Value.Date, Kind))
.Distinct()
.OrderBy(d => d)
.ToList();
_value = ndtEnum;
_dateTimeValue = selectedDates.LastOrDefault();
}
#endif
#if NET6_0_OR_GREATER
else if (value is IEnumerable<DateOnly> doEnum)
{
selectedDates = doEnum.Select(d => d.ToDateTime(System.TimeOnly.MinValue, Kind).Date)
@@ -574,7 +558,6 @@ namespace Radzen.Blazor
_value = doEnum;
_dateTimeValue = selectedDates.LastOrDefault();
}
#endif
else if (value is IEnumerable<DateTimeOffset?> dtoEnum)
{
selectedDates = dtoEnum.Where(o => o.HasValue)
@@ -600,7 +583,6 @@ namespace Radzen.Blazor
_value = dtoEnum;
_dateTimeValue = selectedDates.LastOrDefault();
}
#if NET6_0_OR_GREATER
else if (value is IEnumerable<DateOnly?> doNullableEnum)
{
selectedDates = doNullableEnum.Where(d => d.HasValue)
@@ -623,7 +605,6 @@ namespace Radzen.Blazor
_value = toNullableEnum;
_dateTimeValue = selectedDates.LastOrDefault();
}
#endif
else if (value is DateTime dt && dt != default(DateTime))
{
selectedDates = new List<DateTime> { DateTime.SpecifyKind(dt.Date, Kind) };
@@ -665,7 +646,6 @@ namespace Radzen.Blazor
{
_dateTimeValue = DateTime.SpecifyKind(dateTime, Kind);
}
#if NET6_0_OR_GREATER
else if (value is DateOnly dateOnly)
{
_dateTimeValue = dateOnly.ToDateTime(System.TimeOnly.MinValue, Kind);
@@ -675,7 +655,6 @@ namespace Radzen.Blazor
_dateTimeValue = new DateTime(CurrentDate.Year, CurrentDate.Month, CurrentDate.Day, timeOnly.Hour, timeOnly.Minute, timeOnly.Second, timeOnly.Millisecond, Kind);
_currentDate = _dateTimeValue.Value;
}
#endif
else
{
_dateTimeValue = null;
@@ -690,7 +669,6 @@ namespace Radzen.Blazor
private static object? ConvertToTValue(object? value)
{
#if NET6_0_OR_GREATER
var typeofTValue = typeof(TValue);
if (value is DateTime dt)
{
@@ -705,7 +683,6 @@ namespace Radzen.Blazor
return (TValue)value;
}
}
#endif
return value;
}
@@ -1385,18 +1362,10 @@ namespace Radzen.Blazor
}
newValue = list;
}
#if NET6_0_OR_GREATER
else if (typeof(IEnumerable<DateTime?>).IsAssignableFrom(typeofTValue))
{
newValue = selectedDates.Select(d => (DateTime?)d).ToList();
}
#else
else if (typeof(System.Collections.Generic.IEnumerable<System.Nullable<DateTime>>).IsAssignableFrom(typeofTValue))
{
newValue = selectedDates.Select(d => (DateTime?)d).ToList();
}
#endif
#if NET6_0_OR_GREATER
else if (typeof(IEnumerable<DateOnly>).IsAssignableFrom(typeofTValue))
{
newValue = selectedDates.Select(d => DateOnly.FromDateTime(d)).ToList();
@@ -1409,7 +1378,6 @@ namespace Radzen.Blazor
{
newValue = selectedDates.Select(d => (TimeOnly?)new TimeOnly(d.Hour, d.Minute, d.Second, d.Millisecond)).ToList();
}
#endif
else
{
newValue = selectedDates.ToList();

View File

@@ -127,7 +127,12 @@
}
else
{
@RenderFilter()
<div class="rz-dropdown-filter-container">
<input aria-label="@SearchAriaLabel" id="@SearchID" @ref="@search" tabindex="@(Disabled ? "-1" : $"{TabIndex}")" placeholder="@(FilterPlaceholder ?? string.Empty)" class="rz-dropdown-filter rz-inputtext" autocomplete="@FilterAutoCompleteType" aria-autocomplete="none" type="text"
@onchange="@OnFilter" @onkeydown="@OnFilterKeyPress" value="@(searchText ?? string.Empty)"
@oninput="@OnFilterInput" />
<span class="notranslate rz-dropdown-filter-icon rzi rzi-search"></span>
</div>
}
}
@if (Multiple && (AllowSelectAll || AllowFiltering || HeaderTemplate != null))
@@ -164,7 +169,7 @@
<input id="@SearchID" tabindex="@(Disabled ? "-1" : $"{TabIndex}")" class="rz-inputtext" role="textbox" type="text"
onclick="Radzen.preventDefaultAndStopPropagation(event)" aria-label="@SearchAriaLabel"
@ref="@search" @oninput="@OnFilterInput"
@onchange="@OnFilter" @onkeydown="@OnFilterKeyPress" value="@searchText" autocomplete="@FilterAutoCompleteType" />
@onchange="@OnFilter" @onkeydown="@OnFilterKeyPress" value="@(searchText ?? string.Empty)" autocomplete="@FilterAutoCompleteType" />
<span class="notranslate rz-multiselect-filter-icon rzi rzi-search"></span>
</div>
}
@@ -203,31 +208,3 @@
}
</div>
}
@code {
internal RenderFragment RenderFilter()
{
#if NET7_0_OR_GREATER
return __builder => {
<text>
<div class="rz-dropdown-filter-container">
<input aria-label="@SearchAriaLabel" id="@SearchID" @ref="@search" tabindex="@(Disabled ? "-1" : $"{TabIndex}")" placeholder="@FilterPlaceholder" class="rz-dropdown-filter rz-inputtext" autocomplete="@FilterAutoCompleteType" aria-autocomplete="none" type="text"
@onchange="@OnFilter" @onkeydown="@OnFilterKeyPress"
@bind:event="oninput" @bind:get="searchText" @bind:set="@(args => OnFilterInput(new ChangeEventArgs(){ Value = args }))" />
<span class="notranslate rz-dropdown-filter-icon rzi rzi-search"></span>
</div>
</text>
};
#else
return __builder => {
<text>
<div class="rz-dropdown-filter-container">
<input aria-label="@SearchAriaLabel" id="@SearchID" @ref="@search" tabindex="@(Disabled ? "-1" : $"{TabIndex}")" placeholder="@FilterPlaceholder" class="rz-dropdown-filter rz-inputtext" autocomplete="@FilterAutoCompleteType" aria-autocomplete="none" type="text"
@onchange="@OnFilter" @onkeydown="@OnFilterKeyPress" value="@searchText"
@oninput="@OnFilterInput" />
<span class="notranslate rz-dropdown-filter-icon rzi rzi-search"></span>
</div>
</text>
};
#endif
}
}

View File

@@ -130,7 +130,9 @@
else
{
<div class="rz-lookup-search">
@RenderFilter()
<input aria-label="@SearchAriaLabel" class="rz-lookup-search-input" id="@SearchID" @ref="@search" tabindex="0" placeholder="@(SearchTextPlaceholder ?? string.Empty)"
@onchange="@OnFilter" @onkeydown="@OnFilterKeyPress" value="@(searchText ?? string.Empty)" style="@(ShowSearch ? "" : "margin-right:0px;")"
@oninput="@OnFilterInput" />
@if (ShowSearch)
{
<button tabindex="0" class="rz-button rz-button-md rz-button-icon-only rz-primary" type="button"
@@ -206,25 +208,3 @@
}
</div>
}
@code {
internal RenderFragment RenderFilter()
{
#if NET7_0_OR_GREATER
return __builder => {
<text>
<input aria-label="@SearchAriaLabel" class="rz-lookup-search-input" id="@SearchID" @ref="@search" tabindex="0" placeholder="@SearchTextPlaceholder"
@onchange="@OnFilter" @onkeydown="@OnFilterKeyPress" style="@(ShowSearch ? "" : "margin-right:0px;")"
@bind:event="oninput" @bind:get="searchText" @bind:set=@(args => OnFilterInput(new ChangeEventArgs(){ Value = args }))" />
</text>
};
#else
return __builder => {
<text>
<input aria-label="@SearchAriaLabel" class="rz-lookup-search-input" id="@SearchID" @ref="@search" tabindex="0" placeholder="@SearchTextPlaceholder"
@onchange="@OnFilter" @onkeydown="@OnFilterKeyPress" value="@searchText" style="@(ShowSearch ? "" : "margin-right:0px;")"
@oninput="@OnFilterInput" />
</text>
};
#endif
}
}

View File

@@ -1016,6 +1016,26 @@ namespace Radzen.Blazor
}
}
/// <inheritdoc />
protected override async Task OnFilterInput(ChangeEventArgs args)
{
ArgumentNullException.ThrowIfNull(args);
searchText = $"{args.Value}";
if (FilterAsYouType)
{
await SearchTextChanged.InvokeAsync(searchText);
if (ResetSelectedIndexOnFilter)
{
selectedIndex = -1;
}
Debounce(DebounceFilter, FilterDelay);
}
}
/// <summary>
/// Handles the filter event.
/// </summary>

View File

@@ -10,7 +10,7 @@ Disabled = itemArgs.Disabled;
{
<li class="@GetComponentCssClass("rz-multiselect")" role="option" tabindex="-1"
aria-label="@(DropDown?.GetItemOrValueFromProperty(Item!, DropDown?.TextProperty ?? string.Empty) ?? "")"
aria-selected="@(DropDown?.IsSelected(Item) == true)"
aria-selected="@(DropDown?.IsSelected(Item) == true ? "true" : "false")"
aria-disabled="@(Disabled || DropDown?.Disabled == true ? "true" : "false")"
@onmousedown:preventDefault @onmousedown="args=>SelectItem(args,false)"
@onclick:preventDefault @onclick="args=>SelectItem(args,true)"
@@ -36,7 +36,7 @@ else
{
<li role="option" class="@GetComponentCssClass("rz-dropdown")" tabindex="-1"
aria-label="@(DropDown?.GetItemOrValueFromProperty(Item, DropDown?.TextProperty ?? "") ?? "")"
aria-selected="@(DropDown?.IsSelected(Item) == true)"
aria-selected="@(DropDown?.IsSelected(Item) == true ? "true" : "false")"
aria-disabled="@(Disabled || DropDown?.Disabled == true ? "true" : "false")"
@onmousedown:preventDefault @onmousedown="args=>SelectItem(args,false)"
@onclick:preventDefault @onclick="args=>SelectItem(args,true)"

View File

@@ -1,4 +1,4 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components;
using System;
using System.Collections.Generic;
@@ -7,9 +7,7 @@ namespace Radzen.Blazor
/// <summary>
/// RadzenDropZoneContainer component.
/// </summary>
#if NET6_0_OR_GREATER
[CascadingTypeParameter(nameof(TItem))]
#endif
public partial class RadzenDropZoneContainer<TItem> : RadzenComponentWithChildren
{
/// <summary>

View File

@@ -43,7 +43,9 @@
@if (AllowFiltering)
{
<div class="rz-listbox-filter-container">
@RenderFilter()
<input id="@SearchID" @ref="@search" disabled="@Disabled" class="rz-inputtext" role="textbox" type="text" placeholder="@(Placeholder ?? string.Empty)"
@onchange="@OnFilter" @onkeydown="@OnFilterKeyPress" value="@(searchText ?? string.Empty)" @onkeydown:stopPropagation="true"
@oninput="@OnFilterInput" aria-label="@SearchAriaLabel" />
<span class="notranslate rz-listbox-filter-icon rzi rzi-search"></span>
</div>
}
@@ -60,25 +62,4 @@
}
</div>
}
@code {
internal RenderFragment RenderFilter()
{
#if NET7_0_OR_GREATER
return __builder => {
<text>
<input id="@SearchID" @ref="@search" disabled="@Disabled" class="rz-inputtext" role="textbox" type="text" placeholder="@Placeholder"
@onchange="@OnFilter" @onkeydown="@OnFilterKeyPress" @onkeydown:stopPropagation="true"
@bind:event="oninput" @bind:get="searchText" @bind:set=@(args => OnFilterInput(new ChangeEventArgs(){ Value = args }))" aria-label="@SearchAriaLabel" />
</text>
};
#else
return __builder => {
<text>
<input id="@SearchID" @ref="@search" disabled="@Disabled" class="rz-inputtext" role="textbox" type="text" placeholder="@Placeholder"
@onchange="@OnFilter" @onkeydown="@OnFilterKeyPress" value="@searchText" @onkeydown:stopPropagation="true"
@oninput="@OnFilterInput" aria-label="@SearchAriaLabel" />
</text>
};
#endif
}
}

View File

@@ -6,7 +6,7 @@
@if (itemArgs?.Visible == true)
{
Disabled = itemArgs.Disabled;
<li class="@GetComponentCssClass()" role="option" aria-selected="@(ListBox?.IsSelected(Item) == true)" tabindex="-1"
<li class="@GetComponentCssClass()" role="option" aria-selected="@(ListBox?.IsSelected(Item) == true ? "true" : "false")" tabindex="-1"
aria-label="@PropertyAccess.GetItemOrValueFromProperty(Item, ListBox?.TextProperty)" @onclick="@SelectItem"
aria-disabled="@(Disabled || ListBox?.Disabled == true ? "true" : "false")"
@attributes="@(itemArgs?.Attributes.Any() == true ? itemArgs.Attributes : Attributes)">

View File

@@ -33,18 +33,18 @@
<RadzenStack style="margin-top:1rem;" Orientation="Orientation.Horizontal" AlignItems="AlignItems.Center" JustifyContent="JustifyContent.End" Gap="0.5rem">
@if(ShowLoginButton)
{
<RadzenButton ButtonStyle="ButtonStyle.Primary" ButtonType="ButtonType.Submit" Text=@LoginText Click=@OnLogin />
<RadzenButton ButtonType="ButtonType.Submit" ButtonStyle="ButtonStyle.Primary" Text=@LoginText Click=@OnLogin />
}
@if (AllowResetPassword)
{
<button type="button" class="rz-link" @onclick=@OnReset>@ResetPasswordText</button>
<RadzenButton ButtonType="ButtonType.Button" ButtonStyle="ButtonStyle.Secondary" Variant="Variant.Text" Text=@ResetPasswordText Click=@OnReset />
}
</RadzenStack>
@if (AllowRegister)
{
<div class="rz-register">
@RegisterMessageText
<RadzenButton ButtonType="ButtonType.Button" Variant="Variant.Flat" ButtonStyle="ButtonStyle.Secondary" Shade="Shade.Lighter" Text=@RegisterText Click=@OnRegister />
<RadzenButton ButtonType="ButtonType.Button" ButtonStyle="ButtonStyle.Secondary" Variant="Variant.Flat" Shade="Shade.Lighter" Text=@RegisterText Click=@OnRegister />
</div>
}
}
@@ -80,11 +80,11 @@
<div class="rz-form-input-wrapper rz-login-buttons">
@if (ShowLoginButton)
{
<RadzenButton ButtonStyle="ButtonStyle.Primary" ButtonType="ButtonType.Submit" Text=@LoginText Click=@OnLogin />
<RadzenButton ButtonType="ButtonType.Submit" ButtonStyle="ButtonStyle.Primary" Text=@LoginText Click=@OnLogin />
}
@if (AllowResetPassword)
{
<button type="button" class="rz-link" @onclick=@OnReset>@ResetPasswordText</button>
<RadzenButton ButtonType="ButtonType.Button" ButtonStyle="ButtonStyle.Secondary" Variant="Variant.Text" Text=@ResetPasswordText Click=@OnReset />
}
</div>
</div>
@@ -93,7 +93,7 @@
{
<div class="rz-register">
@RegisterMessageText
<RadzenButton ButtonType="ButtonType.Button" Variant="Variant.Flat" ButtonStyle="ButtonStyle.Secondary" Shade="Shade.Lighter" Text=@RegisterText Click=@OnRegister />
<RadzenButton ButtonType="ButtonType.Button" ButtonStyle="ButtonStyle.Secondary" Variant="Variant.Flat" Shade="Shade.Lighter" Text=@RegisterText Click=@OnRegister />
</div>
}
}

View File

@@ -1,4 +1,4 @@
@using Radzen
@using Radzen
@using Microsoft.JSInterop
@using Microsoft.AspNetCore.Components.Forms
@using System.Globalization
@@ -8,7 +8,12 @@
@if (Visible)
{
<span @ref="@Element" style="@Style" @attributes="Attributes" class="@GetCssClass()" id="@GetId()">
@RenderInput()
<input @ref="@input" inputmode="decimal" @attributes="InputAttributes" type="text" name="@Name" disabled="@Disabled" readonly="@ReadOnly"
class="@GetInputCssClass()" tabindex="@(Disabled ? "-1" : $"{TabIndex}")" id="@Name"
placeholder="@CurrentPlaceholder" autocomplete="@AutoCompleteAttribute" aria-autocomplete="@AriaAutoCompleteAttribute" @bind:get="FormattedValue" @bind:set="SetValue"
onkeypress="Radzen.numericKeyPress(event, @IsInteger().ToString().ToLower(), '@Culture.NumberFormat.NumberDecimalSeparator')"
onblur="@GetOnInput()" onpaste="@GetOnPaste()" maxlength="@MaxLength"
@onkeydown="@(args => OnKeyPress(args))" @onkeydown:preventDefault=preventKeyPress @onkeydown:stopPropagation />
@if (ShowUpDown)
{
<button aria-label=@UpAriaLabel type="button" class="rz-numeric-button rz-numeric-up rz-button" tabindex="-1"
@@ -21,32 +26,4 @@
</button>
}
</span>
}
@code {
internal RenderFragment RenderInput()
{
#if NET7_0_OR_GREATER
return __builder => {
<text>
<input @ref="@input" inputmode="decimal" @attributes="InputAttributes" type="text" name="@Name" disabled="@Disabled" readonly="@ReadOnly"
class="@GetInputCssClass()" tabindex="@(Disabled ? "-1" : $"{TabIndex}")" id="@Name"
placeholder="@CurrentPlaceholder" autocomplete="@AutoCompleteAttribute" aria-autocomplete="@AriaAutoCompleteAttribute" @bind:get="FormattedValue" @bind:set="SetValue"
onkeypress="Radzen.numericKeyPress(event, @IsInteger().ToString().ToLower(), '@Culture.NumberFormat.NumberDecimalSeparator')"
onblur="@GetOnInput()" onpaste="@GetOnPaste()" maxlength="@MaxLength"
@onkeydown="@(args => OnKeyPress(args))" @onkeydown:preventDefault=preventKeyPress @onkeydown:stopPropagation />
</text>
};
#else
return __builder => {
<text>
<input @ref="@input" inputmode="decimal" @attributes="InputAttributes" type="text" name="@Name" disabled="@Disabled" readonly="@ReadOnly"
class="@GetInputCssClass()" tabindex="@(Disabled ? "-1" : $"{TabIndex}")" id="@Name"
placeholder="@CurrentPlaceholder" autocomplete="@AutoCompleteAttribute" aria-autocomplete="@AriaAutoCompleteAttribute" value="@FormattedValue" @onchange="@OnChange"
onkeypress="Radzen.numericKeyPress(event, @IsInteger().ToString().ToLower(), '@Culture.NumberFormat.NumberDecimalSeparator')"
onblur="@GetOnInput()" onpaste="@GetOnPaste()" maxlength="@MaxLength"
@onkeydown="@(args => OnKeyPress(args))" @onkeydown:preventDefault=preventKeyPress @onkeydown:stopPropagation />
</text>
};
#endif
}
}

View File

@@ -118,7 +118,6 @@ namespace Radzen.Blazor
_ => false
};
#if NET7_0_OR_GREATER
private TNum SumFloating<TNum>(TNum value1, TNum value2)
{
ArgumentNullException.ThrowIfNull(value1);
@@ -174,7 +173,6 @@ namespace Radzen.Blazor
return newValue;
}
#endif
async System.Threading.Tasks.Task UpdateValueWithStep(bool stepUp)
{
@@ -186,7 +184,6 @@ namespace Radzen.Blazor
var step = string.IsNullOrEmpty(Step) || Step == "any" ? 1 : decimal.Parse(Step.Replace(",", ".", StringComparison.Ordinal), System.Globalization.CultureInfo.InvariantCulture);
TValue? newValue;
#if NET7_0_OR_GREATER
if (IsNumericType(Value))
{
// cannot call UpdateValueWithStepNumeric directly because TValue is not value type constrained
@@ -196,7 +193,6 @@ namespace Radzen.Blazor
newValue = dynamicWrapper(Value, stepUp, step);
}
else
#endif
{
var valueToUpdate = ConvertToDecimal(Value);

View File

@@ -38,9 +38,7 @@ namespace Radzen.Blazor
/// &lt;/RadzenPivotDataGrid&gt;
/// </code>
/// </example>
#if NET6_0_OR_GREATER
[CascadingTypeParameter(nameof(TItem))]
#endif
public partial class RadzenPivotDataGrid<TItem> : PagedDataBoundComponent<TItem>
{
private class RowHeaderCell

View File

@@ -29,9 +29,7 @@ namespace Radzen.Blazor
/// }
/// </code>
/// </example>
#if NET6_0_OR_GREATER
[CascadingTypeParameter(nameof(TItem))]
#endif
public class RadzenTemplateForm<TItem> : RadzenComponent, IRadzenForm
{
/// <summary>

View File

@@ -1,32 +1,14 @@
@using Radzen
@using Microsoft.AspNetCore.Components.Forms
@inherits FormComponent<string>
@if (Visible)
{
if (Immediate)
{
@RenderImmediateTextArea()
<textarea @ref="@Element" id="@GetId()" disabled="@Disabled" readonly="@ReadOnly" name="@Name" rows="@Rows" cols="@Cols" style="@Style" @attributes="Attributes" class="@GetCssClass()"
placeholder="@CurrentPlaceholder" maxlength="@MaxLength" @bind:get="@Value" @bind:set="@SetValue" @bind:event="oninput" tabindex="@(Disabled ? "-1" : $"{TabIndex}")"></textarea>
}
else
{
<textarea @ref="@Element" id="@GetId()" disabled="@Disabled" readonly="@ReadOnly" name="@Name" rows="@Rows" cols="@Cols" style="@Style" @attributes="Attributes" class="@GetCssClass()"
placeholder="@CurrentPlaceholder" maxlength="@MaxLength" value="@Value" @onchange="@OnChange" tabindex="@(Disabled ? "-1" : $"{TabIndex}")"></textarea>
placeholder="@CurrentPlaceholder" maxlength="@MaxLength" value="@Value" @onchange="@OnChange" tabindex="@(Disabled ? "-1" : $"{TabIndex}")"></textarea>
}
}
@code {
internal RenderFragment RenderImmediateTextArea()
{
#if NET7_0_OR_GREATER
return __builder => {
<textarea @ref="@Element" id="@GetId()" disabled="@Disabled" readonly="@ReadOnly" name="@Name" rows="@Rows" cols="@Cols" style="@Style" @attributes="Attributes" class="@GetCssClass()"
placeholder="@CurrentPlaceholder" maxlength="@MaxLength" @bind:get="@Value" @bind:set="@SetValue" @bind:event="oninput" tabindex="@(Disabled ? "-1" : $"{TabIndex}")"></textarea>
};
#else
return __builder => {
<textarea @ref="@Element" id="@GetId()" disabled="@Disabled" readonly="@ReadOnly" name="@Name" rows="@Rows" cols="@Cols" style="@Style" @attributes="Attributes" class="@GetCssClass()"
placeholder="@CurrentPlaceholder" maxlength="@MaxLength" value="@Value" @oninput="@OnChange" tabindex="@(Disabled ? "-1" : $"{TabIndex}")"></textarea>
};
#endif
}
}
}

View File

@@ -1,30 +1,14 @@
@inherits FormComponentWithAutoComplete<string>
@inherits FormComponentWithAutoComplete<string>
@if (Visible)
{
if (Immediate)
{
@RenderImmediateInput()
<input @ref="@Element" id="@GetId()" disabled="@Disabled" readonly="@ReadOnly" name="@Name" style="@Style" @attributes="Attributes" class="@GetCssClass()" tabindex="@(Disabled ? "-1" : $"{TabIndex}")"
placeholder="@CurrentPlaceholder" maxlength="@MaxLength" autocomplete="@AutoCompleteAttribute" aria-autocomplete="@AriaAutoCompleteAttribute" @bind:get="@Value" @bind:set="@SetValue" @bind:event="oninput"/>
}
else
{
<input @ref="@Element" id="@GetId()" disabled="@Disabled" readonly="@ReadOnly" name="@Name" style="@Style" @attributes="Attributes" class="@GetCssClass()" tabindex="@(Disabled ? "-1" : $"{TabIndex}")"
placeholder="@CurrentPlaceholder" maxlength="@MaxLength" autocomplete="@AutoCompleteAttribute" aria-autocomplete="@AriaAutoCompleteAttribute" value="@Value" @onchange="@OnChange" />
}
}
@code {
internal RenderFragment RenderImmediateInput()
{
#if NET7_0_OR_GREATER
return __builder => {
<input @ref="@Element" id="@GetId()" disabled="@Disabled" readonly="@ReadOnly" name="@Name" style="@Style" @attributes="Attributes" class="@GetCssClass()" tabindex="@(Disabled ? "-1" : $"{TabIndex}")"
placeholder="@CurrentPlaceholder" maxlength="@MaxLength" autocomplete="@AutoCompleteAttribute" aria-autocomplete="@AriaAutoCompleteAttribute" @bind:get="@Value" @bind:set="@SetValue" @bind:event="oninput"/>
};
#else
return __builder => {
<input @ref="@Element" id="@GetId()" disabled="@Disabled" readonly="@ReadOnly" name="@Name" style="@Style" @attributes="Attributes" class="@GetCssClass()" tabindex="@(Disabled ? "-1" : $"{TabIndex}")"
placeholder="@CurrentPlaceholder" maxlength="@MaxLength" autocomplete="@AutoCompleteAttribute" aria-autocomplete="@AriaAutoCompleteAttribute" value="@Value" @oninput="@OnChange" />
};
#endif
}
}

View File

@@ -1,4 +1,4 @@
@using Radzen
@using Radzen
@using Radzen.Blazor.Rendering
@using Microsoft.AspNetCore.Components.Forms
@using System.Linq.Expressions
@@ -88,11 +88,9 @@
@RenderUnitField(TimeSpanUnit.Millisecond, MillisecondsUnitText, "ms", UnconfirmedValue.Milliseconds, MillisecondsStep ?? "", "000")
@{
#if NET7_0_OR_GREATER
{
@RenderUnitField(TimeSpanUnit.Microsecond, MicrosecondsUnitText, "us", UnconfirmedValue.Microseconds, MicrosecondsStep ?? "", "000")
}
#endif
}
</div>

View File

@@ -1,4 +1,4 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.JSInterop;
@@ -240,13 +240,11 @@ namespace Radzen.Blazor
[Parameter]
public string? MillisecondsStep { get; set; }
#if NET7_0_OR_GREATER
/// <summary>
/// Specifies the step of the microseconds field in the picker panel.
/// </summary>
[Parameter]
public string? MicrosecondsStep { get; set; }
#endif
#endregion
#region Parameters: panel texts
@@ -310,13 +308,11 @@ namespace Radzen.Blazor
[Parameter]
public string MillisecondsUnitText { get; set; } = "Milliseconds";
#if NET7_0_OR_GREATER
/// <summary>
/// Specifies the microseconds label text.
/// </summary>
[Parameter]
public string MicrosecondsUnitText { get; set; } = "Microseconds";
#endif
#endregion
#region Parameters: other config
@@ -764,9 +760,7 @@ namespace Radzen.Blazor
TimeSpanUnit.Minute => TimeSpan.FromMinutes(value),
TimeSpanUnit.Second => TimeSpan.FromSeconds(value),
TimeSpanUnit.Millisecond => TimeSpan.FromMilliseconds(value),
#if NET7_0_OR_GREATER
TimeSpanUnit.Microsecond => TimeSpan.FromMicroseconds(value),
#endif
_ => TimeSpan.Zero,
};
private static int GetTimeSpanUnitValue(TimeSpanUnit unit, TimeSpan timeSpan)
@@ -777,9 +771,7 @@ namespace Radzen.Blazor
TimeSpanUnit.Minute => timeSpan.Minutes,
TimeSpanUnit.Second => timeSpan.Seconds,
TimeSpanUnit.Millisecond => timeSpan.Milliseconds,
#if NET7_0_OR_GREATER
TimeSpanUnit.Microsecond => timeSpan.Microseconds,
#endif
_ => 0,
};
private static readonly Dictionary<TimeSpanUnit, int> _timeUnitMaxAbsoluteValues = new()
@@ -788,10 +780,8 @@ namespace Radzen.Blazor
{ TimeSpanUnit.Hour, 23 },
{ TimeSpanUnit.Minute, 59 },
{ TimeSpanUnit.Second, 59 },
{ TimeSpanUnit.Millisecond, 999 }
#if NET7_0_OR_GREATER
, { TimeSpanUnit.Microsecond, 999 }
#endif
{ TimeSpanUnit.Millisecond, 999 },
{ TimeSpanUnit.Microsecond, 999 }
};
private static readonly Dictionary<TimeSpanUnit, int> _timeUnitZeroValues = Enum
.GetValues<TimeSpanUnit>()
@@ -853,14 +843,12 @@ namespace Radzen.Blazor
}
timeUnitMaxValues[TimeSpanUnit.Millisecond] = 0;
#if NET7_0_OR_GREATER
if (boundary.Microseconds != 0)
{
timeUnitMaxValues[TimeSpanUnit.Microsecond] = Math.Abs(boundary.Microseconds);
return timeUnitMaxValues;
}
timeUnitMaxValues[TimeSpanUnit.Microsecond] = 0;
#endif
return timeUnitMaxValues;
}

View File

@@ -28,13 +28,10 @@ public enum TimeSpanUnit
/// <summary>
/// Millisecond.
/// </summary>
Millisecond = 4
#if NET7_0_OR_GREATER
,
Millisecond = 4,
/// <summary>
/// Microsecond.
/// </summary>
Microsecond = 5
#endif
}

View File

@@ -251,11 +251,20 @@ $button-sizes: map-merge(
&.rz-shade-darker { box-shadow: inset 0 0 0 var(--rz-border-width) var(--rz-#{$style}-darker); color: var(--rz-#{$style}-darker); }
}
@else {
&.rz-shade-lighter { box-shadow: inset 0 0 0 var(--rz-border-width) var(--rz-#{$style}-lighter); color: var(--rz-#{$style}-light); }
&.rz-shade-light { box-shadow: inset 0 0 0 var(--rz-border-width) var(--rz-#{$style}-light); color: var(--rz-#{$style}-light); }
&.rz-shade-default { box-shadow: inset 0 0 0 var(--rz-border-width) var(--rz-#{$style}); color: var(--rz-#{$style}); }
&.rz-shade-dark { box-shadow: inset 0 0 0 var(--rz-border-width) var(--rz-#{$style}-dark); color: var(--rz-#{$style}-dark); }
&.rz-shade-darker { box-shadow: inset 0 0 0 var(--rz-border-width) var(--rz-#{$style}-darker); color: var(--rz-#{$style}-darker); }
// Fluent/Standard dark specific text color overrides for secondary only
@if ($fluent == true or $standard == true) and $theme-dark == true and $style == 'secondary' {
&.rz-shade-lighter { box-shadow: inset 0 0 0 var(--rz-border-width) var(--rz-#{$style}-lighter); color: var(--rz-#{$style}-light); }
&.rz-shade-light { box-shadow: inset 0 0 0 var(--rz-border-width) var(--rz-#{$style}-light); color: var(--rz-#{$style}-light); }
&.rz-shade-default { box-shadow: inset 0 0 0 var(--rz-border-width) var(--rz-#{$style}); color: var(--rz-on-#{$style}); }
&.rz-shade-dark { box-shadow: inset 0 0 0 var(--rz-border-width) var(--rz-#{$style}-dark); color: var(--rz-#{$style}-dark); }
&.rz-shade-darker { box-shadow: inset 0 0 0 var(--rz-border-width) var(--rz-#{$style}-darker); color: var(--rz-#{$style}-darker); }
} @else {
&.rz-shade-lighter { box-shadow: inset 0 0 0 var(--rz-border-width) var(--rz-#{$style}-lighter); color: var(--rz-#{$style}-light); }
&.rz-shade-light { box-shadow: inset 0 0 0 var(--rz-border-width) var(--rz-#{$style}-light); color: var(--rz-#{$style}-light); }
&.rz-shade-default { box-shadow: inset 0 0 0 var(--rz-border-width) var(--rz-#{$style}); color: var(--rz-#{$style}); }
&.rz-shade-dark { box-shadow: inset 0 0 0 var(--rz-border-width) var(--rz-#{$style}-dark); color: var(--rz-#{$style}-dark); }
&.rz-shade-darker { box-shadow: inset 0 0 0 var(--rz-border-width) var(--rz-#{$style}-darker); color: var(--rz-#{$style}-darker); }
}
}
background-color: transparent;
@@ -302,11 +311,20 @@ $button-sizes: map-merge(
&.rz-shade-dark { color: var(--rz-#{$style}-dark); }
&.rz-shade-darker { color: var(--rz-#{$style}-darker); }
} @else {
&.rz-shade-lighter { color: var(--rz-#{$style}-lighter); }
&.rz-shade-light { color: var(--rz-#{$style}-light); }
&.rz-shade-default { color: var(--rz-#{$style}); }
&.rz-shade-dark { color: var(--rz-#{$style}-dark); }
&.rz-shade-darker { color: var(--rz-#{$style}-darker); }
// Fluent/Standard dark specific text color overrides for secondary only
@if ($fluent == true or $standard == true) and $theme-dark == true and $style == 'secondary' {
&.rz-shade-lighter { color: var(--rz-#{$style}-lighter); }
&.rz-shade-light { color: var(--rz-#{$style}-light); }
&.rz-shade-default { color: var(--rz-on-#{$style}); }
&.rz-shade-dark { color: var(--rz-#{$style}-dark); }
&.rz-shade-darker { color: var(--rz-#{$style}-darker); }
} @else {
&.rz-shade-lighter { color: var(--rz-#{$style}-lighter); }
&.rz-shade-light { color: var(--rz-#{$style}-light); }
&.rz-shade-default { color: var(--rz-#{$style}); }
&.rz-shade-dark { color: var(--rz-#{$style}-dark); }
&.rz-shade-darker { color: var(--rz-#{$style}-darker); }
}
}
background-color: transparent;
box-shadow: none;

View File

@@ -3,10 +3,6 @@
color: var(--rz-link-color);
text-decoration: none;
cursor: pointer;
background: none;
border: none;
padding: 0;
font: inherit;
.rz-link-disabled {
pointer-events: none;

View File

@@ -92,6 +92,7 @@ window.Radzen = {
var handler = function (e) {
e.stopPropagation();
e.preventDefault();
try {
ref.invokeMethodAsync('RadzenComponent.RaiseContextMenu',
{
ClientX: e.clientX,
@@ -105,6 +106,7 @@ window.Radzen = {
Button: e.button,
Buttons: e.buttons,
});
} catch { }
return false;
};
Radzen[id + 'contextmenu'] = handler;
@@ -115,7 +117,7 @@ window.Radzen = {
var el = document.getElementById(id);
if (el) {
var handler = function (e) {
ref.invokeMethodAsync('RadzenComponent.RaiseMouseEnter');
try { ref.invokeMethodAsync('RadzenComponent.RaiseMouseEnter'); } catch { }
};
Radzen[id + 'mouseenter'] = handler;
el.addEventListener('mouseenter', handler, false);
@@ -125,7 +127,7 @@ window.Radzen = {
var el = document.getElementById(id);
if (el) {
var handler = function (e) {
ref.invokeMethodAsync('RadzenComponent.RaiseMouseLeave');;
try { ref.invokeMethodAsync('RadzenComponent.RaiseMouseLeave'); } catch { }
};
Radzen[id + 'mouseleave'] = handler;
el.addEventListener('mouseleave', handler, false);
@@ -254,9 +256,9 @@ window.Radzen = {
});
Radzen[id].instance.addListener('click', function (e) {
Radzen[id].invokeMethodAsync('RadzenGoogleMap.OnMapClick', {
try { Radzen[id].invokeMethodAsync('RadzenGoogleMap.OnMapClick', {
Position: {Lat: e.latLng.lat(), Lng: e.latLng.lng()}
});
}); } catch { }
});
Radzen.updateMap(id, apiKey, zoom, center, markers, options, fitBoundsToMarkersOnUpdate, language);
@@ -302,11 +304,11 @@ window.Radzen = {
});
marker.addListener('click', function (e) {
Radzen[id].invokeMethodAsync('RadzenGoogleMap.OnMarkerClick', {
try { Radzen[id].invokeMethodAsync('RadzenGoogleMap.OnMarkerClick', {
Title: marker.title,
Label: marker.content.innerText,
Position: marker.position
});
}); } catch { }
});
marker.setMap(Radzen[id].instance);
@@ -392,7 +394,7 @@ window.Radzen = {
var code = Radzen[id].inputs.map(i => i.value).join('').trim();
hidden.value = code;
ref.invokeMethodAsync('RadzenSecurityCode.OnValueChange', code);
try { ref.invokeMethodAsync('RadzenSecurityCode.OnValueChange', code); } catch { }
Radzen[id].inputs[Radzen[id].inputs.length - 1].focus();
}
@@ -432,7 +434,7 @@ window.Radzen = {
var value = Radzen[id].inputs.map(i => i.value).join('').trim();
hidden.value = value;
ref.invokeMethodAsync('RadzenSecurityCode.OnValueChange', value);
try { ref.invokeMethodAsync('RadzenSecurityCode.OnValueChange', value); } catch { }
var index = Radzen[id].inputs.indexOf(e.currentTarget);
if (index < Radzen[id].inputs.length - 1) {
@@ -448,7 +450,7 @@ window.Radzen = {
var value = Radzen[id].inputs.map(i => i.value).join('').trim();
hidden.value = value;
ref.invokeMethodAsync('RadzenSecurityCode.OnValueChange', value);
try { ref.invokeMethodAsync('RadzenSecurityCode.OnValueChange', value); } catch { }
var index = Radzen[id].inputs.indexOf(e.currentTarget);
if (index > 0) {
@@ -510,11 +512,11 @@ window.Radzen = {
newValue >= min &&
newValue <= max
) {
slider.invokeMethodAsync(
try { slider.invokeMethodAsync(
'RadzenSlider.OnValueChange',
newValue,
!!slider.isMin
);
); } catch { }
}
};
@@ -546,11 +548,11 @@ window.Radzen = {
var newValue = percent * (max - min) + min;
var oldValue = range ? value[slider.isMin ? 0 : 1] : value;
if (newValue >= min && newValue <= max && newValue != oldValue) {
slider.invokeMethodAsync(
try { slider.invokeMethodAsync(
'RadzenSlider.OnValueChange',
newValue,
!!slider.isMin
);
); } catch { }
}
}
};
@@ -840,7 +842,7 @@ window.Radzen = {
uploadComponent.files = Array.from(fileInput.files);
uploadComponent.localFiles = files;
uploadComponent.invokeMethodAsync('RadzenUpload.OnChange', files);
try { uploadComponent.invokeMethodAsync('RadzenUpload.OnChange', files); } catch { }
}
for (var i = 0; i < fileInput.files.length; i++) {
@@ -913,7 +915,7 @@ window.Radzen = {
Radzen.uploadComponents && Radzen.uploadComponents[fileInput.id];
if (uploadComponent) {
var progress = parseInt((e.loaded / e.total) * 100);
uploadComponent.invokeMethodAsync(
try { uploadComponent.invokeMethodAsync(
'RadzenUpload.OnProgress',
progress,
e.loaded,
@@ -925,7 +927,7 @@ window.Radzen = {
cancelled = true;
xhr.abort();
}
});
}); } catch { }
}
}
};
@@ -936,27 +938,27 @@ window.Radzen = {
Radzen.uploadComponents && Radzen.uploadComponents[fileInput.id];
if (uploadComponent) {
if (status === 0 || (status >= 200 && status < 400)) {
uploadComponent.invokeMethodAsync(
try { uploadComponent.invokeMethodAsync(
'RadzenUpload.OnComplete',
xhr.responseText,
cancelled
);
); } catch { }
} else {
uploadComponent.invokeMethodAsync(
try { uploadComponent.invokeMethodAsync(
'RadzenUpload.OnError',
xhr.responseText
);
); } catch { }
}
}
}
};
uploadComponent.invokeMethodAsync('GetHeaders').then(function (headers) {
try { uploadComponent.invokeMethodAsync('GetHeaders').then(function (headers) {
xhr.open(method, url, true);
for (var name in headers) {
xhr.setRequestHeader(name, headers[name]);
}
xhr.send(data);
});
}); } catch { }
},
getCookie: function (name) {
var value = '; ' + decodeURIComponent(document.cookie);
@@ -1236,6 +1238,12 @@ window.Radzen = {
rect.width = x ? rect.width + 20 : rect.width;
rect.height = y ? rect.height + 20 : rect.height;
var isRTL = Radzen.isRTL(popup);
if (isRTL && (position == 'bottom' || position == 'top')) {
left = parentRect.right - rect.width;
}
var smartPosition = !position || position == 'bottom';
if (smartPosition && top + rect.height > window.innerHeight && parentRect.top > rect.height) {
@@ -1276,6 +1284,24 @@ window.Radzen = {
}
}
if (smartPosition && isRTL && left < 0 && window.innerWidth > rect.width) {
left = !position ? 0 : rect.left;
if (position) {
top = y || parentRect.top;
var tooltipContent = popup.children[0];
var tooltipContentClassName = 'rz-' + position + '-tooltip-content';
if (tooltipContent.classList.contains(tooltipContentClassName)) {
tooltipContent.classList.remove(tooltipContentClassName);
tooltipContent.classList.add('rz-right-tooltip-content');
position = 'right';
if (instance && callback) {
try { instance.invokeMethodAsync(callback, position); } catch { }
}
}
}
}
if (smartPosition) {
if (position) {
top = top + 20;
@@ -1294,7 +1320,7 @@ window.Radzen = {
if (position == 'top') {
top = parentRect.top - rect.height + 5;
left = parentRect.left;
left = isRTL ? parentRect.right - rect.width : parentRect.left;
}
popup.style.zIndex = 2000;
@@ -1643,11 +1669,13 @@ window.Radzen = {
dialog.offsetWidth = e[0].target.offsetWidth;
dialog.offsetHeight = e[0].target.offsetHeight;
try {
dialog.invokeMethodAsync(
'RadzenDialog.OnResize',
e[0].target.offsetWidth,
e[0].target.offsetHeight
);
} catch { }
}
};
Radzen.dialogResizer = new ResizeObserver(dialogResize).observe(lastDialog.parentElement);
@@ -1668,7 +1696,7 @@ window.Radzen = {
lastDialog.parentElement.style.left = left + 'px';
lastDialog.parentElement.style.top = top + 'px';
dialog.invokeMethodAsync('RadzenDialog.OnDrag', top, left);
try { dialog.invokeMethodAsync('RadzenDialog.OnDrag', top, left); } catch { }
};
var stop = function () {
@@ -1775,7 +1803,7 @@ window.Radzen = {
var lastDialog = dialogs[dialogs.length - 1];
if (lastDialog && lastDialog.options && lastDialog.options.closeDialogOnEsc) {
Radzen.dialogService.invokeMethodAsync('DialogService.Close', null);
try { Radzen.dialogService.invokeMethodAsync('DialogService.Close', null); } catch { }
if (dialogs.length <= 1) {
document.removeEventListener('keydown', Radzen.closePopupOrDialog);
@@ -1962,7 +1990,7 @@ window.Radzen = {
ref.resizeHandler = function () {
var rect = ref.getBoundingClientRect();
instance.invokeMethodAsync('Resize', rect.width, rect.height);
try { instance.invokeMethodAsync('Resize', rect.width, rect.height); } catch { }
};
if (window.ResizeObserver) {
@@ -1983,7 +2011,7 @@ window.Radzen = {
var rect = ref.getBoundingClientRect();
var x = e.clientX - rect.left;
var y = e.clientY - rect.top;
instance.invokeMethodAsync('MouseMove', x, y);
try { instance.invokeMethodAsync('MouseMove', x, y); } catch { }
}
}, 100);
ref.mouseEnterHandler = function () {
@@ -1994,14 +2022,14 @@ window.Radzen = {
return;
}
inside = false;
instance.invokeMethodAsync('MouseMove', -1, -1);
try { instance.invokeMethodAsync('MouseMove', -1, -1); } catch { }
};
ref.clickHandler = function (e) {
var rect = ref.getBoundingClientRect();
var x = e.clientX - rect.left;
var y = e.clientY - rect.top;
if (!e.target.closest('.rz-marker')) {
instance.invokeMethodAsync('Click', x, y);
try { instance.invokeMethodAsync('Click', x, y); } catch { }
}
};
@@ -2025,7 +2053,7 @@ window.Radzen = {
ref.resizeHandler = function () {
var rect = ref.getBoundingClientRect();
instance.invokeMethodAsync('Resize', rect.width, rect.height);
try { instance.invokeMethodAsync('Resize', rect.width, rect.height); } catch { }
};
window.addEventListener('resize', ref.resizeHandler);
@@ -2074,7 +2102,7 @@ window.Radzen = {
mediaQuery: function(query, instance) {
if (instance) {
function callback(event) {
instance.invokeMethodAsync('OnChange', event.matches)
try { instance.invokeMethodAsync('OnChange', event.matches); } catch { }
};
var query = matchMedia(query);
this.mediaQueries[instance._id] = function() {
@@ -2092,7 +2120,7 @@ window.Radzen = {
},
createEditor: function (ref, uploadUrl, paste, instance, shortcuts) {
ref.inputListener = function () {
instance.invokeMethodAsync('OnChange', ref.innerHTML);
try { instance.invokeMethodAsync('OnChange', ref.innerHTML); } catch { }
};
ref.keydownListener = function (e) {
var key = '';
@@ -2109,7 +2137,7 @@ window.Radzen = {
if (shortcuts.includes(key)) {
e.preventDefault();
instance.invokeMethodAsync('ExecuteShortcutAsync', key);
try { instance.invokeMethodAsync('ExecuteShortcutAsync', key); } catch { }
}
};
@@ -2135,7 +2163,7 @@ window.Radzen = {
ref.selectionChangeListener = function () {
if (document.activeElement == ref) {
instance.invokeMethodAsync('OnSelectionChange');
try { instance.invokeMethodAsync('OnSelectionChange'); } catch { }
}
};
ref.handleInsert = function (e, transfer, hasDelegate) {
@@ -2163,36 +2191,36 @@ window.Radzen = {
var result = JSON.parse(xhr.responseText);
var html = '<img src="' + result.url + '">';
if (hasDelegate) {
instance.invokeMethodAsync('OnPaste', html)
try { instance.invokeMethodAsync('OnPaste', html)
.then(function (html) {
document.execCommand("insertHTML", false, html);
});
}); } catch { }
} else {
document.execCommand("insertHTML", false, '<img src="' + result.url + '">');
}
instance.invokeMethodAsync('OnUploadComplete', xhr.responseText);
try { instance.invokeMethodAsync('OnUploadComplete', xhr.responseText); } catch { }
} else {
instance.invokeMethodAsync('OnError', xhr.responseText);
try { instance.invokeMethodAsync('OnError', xhr.responseText); } catch { }
}
}
}
instance.invokeMethodAsync('GetHeaders').then(function (headers) {
try { instance.invokeMethodAsync('GetHeaders').then(function (headers) {
xhr.open('POST', uploadUrl, true);
for (var name in headers) {
xhr.setRequestHeader(name, headers[name]);
}
xhr.send(data);
});
}); } catch { }
} else {
var reader = new FileReader();
reader.onload = function (e) {
var html = '<img src="' + e.target.result + '">';
if (hasDelegate) {
instance.invokeMethodAsync('OnPaste', html)
try { instance.invokeMethodAsync('OnPaste', html)
.then(function (html) {
document.execCommand("insertHTML", false, html);
});
}); } catch { }
} else {
document.execCommand("insertHTML", false, html);
}
@@ -2217,11 +2245,11 @@ window.Radzen = {
data = data.substring(startIndex + startMarker.length, endIndex).trim();
}
instance.invokeMethodAsync('OnPaste', data)
try { instance.invokeMethodAsync('OnPaste', data)
.then(ref.focus())
.then(function (html) {
document.execCommand("insertHTML", false, html);
});
}); } catch { }
}
ref.pasteListener = function (e) {
ref.handleInsert(e, e.clipboardData, paste);
@@ -2318,11 +2346,11 @@ window.Radzen = {
return { left: 0, top: 0, width: 0, height: 0 };
}
ref.mouseMoveHandler = function (e) {
instance.invokeMethodAsync(handler, { clientX: e.clientX, clientY: e.clientY });
try { instance.invokeMethodAsync(handler, { clientX: e.clientX, clientY: e.clientY }); } catch { }
};
ref.touchMoveHandler = function (e) {
if (e.targetTouches[0] && ref.contains(e.targetTouches[0].target)) {
instance.invokeMethodAsync(handler, { clientX: e.targetTouches[0].clientX, clientY: e.targetTouches[0].clientY });
try { instance.invokeMethodAsync(handler, { clientX: e.targetTouches[0].clientX, clientY: e.targetTouches[0].clientY }); } catch { }
}
};
ref.mouseUpHandler = function (e) {
@@ -2428,11 +2456,11 @@ window.Radzen = {
var cell = el.parentNode.parentNode;
if (!cell) return;
if (Radzen[el]) {
grid.invokeMethodAsync(
try { grid.invokeMethodAsync(
'RadzenGrid.OnColumnResized',
columnIndex,
cell.getBoundingClientRect().width
);
); } catch { }
el.style.width = null;
document.removeEventListener('mousemove', Radzen[el].mouseMoveHandler);
document.removeEventListener('mouseup', Radzen[el].mouseUpHandler);
@@ -2452,11 +2480,11 @@ window.Radzen = {
width: cell.getBoundingClientRect().width,
mouseUpHandler: function (e) {
if (Radzen[el]) {
grid.invokeMethodAsync(
try { grid.invokeMethodAsync(
'RadzenGrid.OnColumnResized',
columnIndex,
cell.getBoundingClientRect().width
);
); } catch { }
el.style.width = null;
document.removeEventListener('mousemove', Radzen[el].mouseMoveHandler);
document.removeEventListener('mouseup', Radzen[el].mouseUpHandler);
@@ -2578,13 +2606,13 @@ window.Radzen = {
paneNextLength: isFinite(paneNextLength) ? paneNextLength : 0,
mouseUpHandler: function(e) {
if (Radzen[el]) {
splitter.invokeMethodAsync(
try { splitter.invokeMethodAsync(
'RadzenSplitter.OnPaneResized',
parseInt(pane.getAttribute('data-index')),
parseFloat(pane.style.flexBasis),
paneNext ? parseInt(paneNext.getAttribute('data-index')) : null,
paneNext ? parseFloat(paneNext.style.flexBasis) : null
);
); } catch { }
document.removeEventListener('pointerup', Radzen[el].mouseUpHandler);
document.removeEventListener('pointermove', Radzen[el].mouseMoveHandler);
@@ -2595,9 +2623,9 @@ window.Radzen = {
mouseMoveHandler: function(e) {
if (Radzen[el]) {
splitter.invokeMethodAsync(
try { splitter.invokeMethodAsync(
'RadzenSplitter.OnPaneResizing'
);
); } catch { }
var spacePerc = Radzen[el].panePerc + Radzen[el].paneNextPerc;
var spaceLength = Radzen[el].paneLength + Radzen[el].paneNextLength;
@@ -2712,10 +2740,10 @@ window.Radzen = {
let current = event.results[event.results.length - 1][0]
let result = current.transcript;
componentRef.invokeMethodAsync("OnResult", result);
try { componentRef.invokeMethodAsync("OnResult", result); } catch { }
};
radzenRecognition.onend = function (event) {
componentRef.invokeMethodAsync("StopRecording");
try { componentRef.invokeMethodAsync("StopRecording"); } catch { }
radzenRecognition = null;
};
radzenRecognition.start();
@@ -2825,7 +2853,7 @@ window.Radzen = {
currentSelector = match;
if (!this.selectedNavigationSelector || (match === this.selectedNavigationSelector)) {
this.navigateTo(currentSelector, false);
ref.invokeMethodAsync('ScrollIntoView', currentSelector);
try { ref.invokeMethodAsync('ScrollIntoView', currentSelector); } catch { }
}
}
// clear selected navigation selector after scroll completes
@@ -2867,7 +2895,7 @@ window.Radzen = {
createDraggable: function(element, ref, onDragStart) {
function handleDragStart(e) {
e.dataTransfer.setData('', e.target.id);
ref.invokeMethodAsync(onDragStart);
try { ref.invokeMethodAsync(onDragStart); } catch { }
}
element.draggable = true;
element.addEventListener('dragstart', handleDragStart);
@@ -2887,7 +2915,7 @@ Radzen.registerFabMenu = function(element, dotnet){
}
const handler = function(e){
if(!element.contains(e.target)){
dotnet.invokeMethodAsync('CloseAsync');
try { dotnet.invokeMethodAsync('CloseAsync'); } catch { }
}
};
element.__rzOutsideClickHandler = handler;

View File

@@ -0,0 +1,172 @@
using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
namespace RadzenBlazorDemos.Host;
/// <summary>
/// Redirects legacy and non-canonical demo routes to canonical routes.
/// </summary>
public static class CanonicalRedirectMiddleware
{
private static readonly FrozenDictionary<string, string> StaticRedirects = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
["/buttons"] = "/button",
["/docs"] = "/",
["/docs/guides/components/aichat.html"] = "/aichat",
["/docs/guides/components/accordion.html"] = "/accordion",
["/docs/guides/components/arcgauge.html"] = "/arc-gauge",
["/docs/guides/components/autocomplete.html"] = "/autocomplete",
["/docs/guides/components/badge.html"] = "/badge",
["/docs/guides/components/barcode.html"] = "/barcode",
["/docs/guides/components/breadcrumb.html"] = "/breadcrumb",
["/docs/guides/components/button.html"] = "/button",
["/docs/guides/components/carousel.html"] = "/carousel",
["/docs/guides/components/chart.html"] = "/chart-series",
["/docs/guides/components/chat.html"] = "/chat",
["/docs/guides/components/checkbox.html"] = "/checkbox",
["/docs/guides/components/checkboxlist.html"] = "/checkboxlist",
["/docs/guides/components/colorpicker.html"] = "/colorpicker",
["/docs/guides/components/comparevalidator.html"] = "/comparevalidator",
["/docs/guides/components/contextmenu.html"] = "/contextmenu",
["/docs/guides/components/datalist.html"] = "/datalist",
["/docs/guides/components/datagrid.html"] = "/datagrid",
["/docs/guides/components/datepicker.html"] = "/datepicker",
["/docs/guides/components/dialog.html"] = "/dialog",
["/docs/guides/components/dropdown.html"] = "/dropdown",
["/docs/guides/components/dropdowndatagrid.html"] = "/dropdown-datagrid",
["/docs/guides/components/emailvalidator.html"] = "/emailvalidator",
["/docs/guides/components/fieldset.html"] = "/fieldset",
["/docs/guides/components/fileinput.html"] = "/fileinput",
["/docs/guides/getting-started/context-menu.html"] = "/get-started",
["/docs/guides/getting-started/dialog.html"] = "/get-started",
["/docs/guides/getting-started/notification.html"] = "/get-started",
["/docs/guides/getting-started/tooltip.html"] = "/get-started",
["/docs/guides/getting-started/use-component.html"] = "/get-started",
["/docs/guides/components/googlemap.html"] = "/googlemap",
["/docs/guides/components/gravatar.html"] = "/gravatar",
["/docs/guides/components/htmleditor.html"] = "/html-editor",
["/docs/guides/components/icon.html"] = "/icon",
["/docs/guides/components/image.html"] = "/image",
["/docs/guides/index.html"] = "/get-started",
["/docs/guides/components/label.html"] = "/label",
["/docs/guides/components/lengthvalidator.html"] = "/lengthvalidator",
["/docs/guides/components/link.html"] = "/link",
["/docs/guides/components/listbox.html"] = "/listbox",
["/docs/guides/components/login.html"] = "/login",
["/docs/guides/components/mask.html"] = "/mask",
["/docs/guides/components/media-query.html"] = "/media-query",
["/docs/guides/components/menu.html"] = "/menu",
["/docs/guides/components/notification.html"] = "/notification",
["/docs/guides/components/numeric.html"] = "/numeric",
["/docs/guides/components/numericrangevalidator.html"] = "/numericrangevalidator",
["/docs/guides/components/panel.html"] = "/panel",
["/docs/guides/components/panelmenu.html"] = "/panelmenu",
["/docs/guides/components/pager.html"] = "/pager",
["/docs/guides/components/password.html"] = "/password",
["/docs/guides/components/profilemenu.html"] = "/profile-menu",
["/docs/guides/components/progressbar.html"] = "/progressbar",
["/docs/guides/components/qrcode.html"] = "/qrcode",
["/docs/guides/components/radiobuttonlist.html"] = "/radiobuttonlist",
["/docs/guides/components/radialgauge.html"] = "/radial-gauge",
["/docs/guides/components/rating.html"] = "/rating",
["/docs/guides/components/regexvalidator.html"] = "/regexvalidator",
["/docs/guides/components/requiredvalidator.html"] = "/requiredvalidator",
["/docs/guides/components/scheduler.html"] = "/scheduler",
["/docs/guides/components/selectbar.html"] = "/selectbar",
["/docs/guides/components/slider.html"] = "/slider",
["/docs/guides/components/splitbutton.html"] = "/splitbutton",
["/docs/guides/components/splitter.html"] = "/splitter",
["/docs/guides/components/ssrsviewer.html"] = "/ssrsviewer",
["/docs/guides/components/steps.html"] = "/steps",
["/docs/guides/components/switch.html"] = "/switch",
["/docs/guides/components/tabs.html"] = "/tabs",
["/docs/guides/components/templateform.html"] = "/templateform",
["/docs/guides/components/textarea.html"] = "/textarea",
["/docs/guides/components/textbox.html"] = "/textbox",
["/docs/guides/components/timespanpicker.html"] = "/timespanpicker",
["/docs/guides/components/tooltip.html"] = "/tooltip",
["/docs/guides/components/tree.html"] = "/tree",
["/docs/guides/components/upload.html"] = "/example-upload"
}.ToFrozenDictionary(StringComparer.OrdinalIgnoreCase);
public static IApplicationBuilder UseCanonicalRedirects(this IApplicationBuilder app)
{
app.Use(async (context, next) =>
{
var path = NormalizePath(context.Request.Path.Value ?? string.Empty);
if (TryResolveTarget(path, out var targetPath))
{
context.Response.StatusCode = StatusCodes.Status301MovedPermanently;
context.Response.Headers.Location = targetPath + context.Request.QueryString;
context.Response.Headers.CacheControl = "public, max-age=86400";
return;
}
await next();
});
return app;
}
private static bool TryResolveTarget(string path, out string targetPath)
{
if (StaticRedirects.TryGetValue(path, out targetPath))
{
return true;
}
if (TryMapOptionalSegment(path, "/getting-started", "/get-started", out targetPath))
{
return true;
}
if (TryMapOptionalSegment(path, "/docs/guides/getting-started/installation.html", "/get-started", out targetPath))
{
return true;
}
targetPath = string.Empty;
return false;
}
private static string NormalizePath(string path)
{
if (string.IsNullOrWhiteSpace(path))
{
return "/";
}
return path.TrimEnd('/') switch
{
"" => "/",
var normalized => normalized
};
}
private static bool TryMapOptionalSegment(string path, string sourceBasePath, string targetBasePath, out string targetPath)
{
if (path.Equals(sourceBasePath, StringComparison.OrdinalIgnoreCase))
{
targetPath = targetBasePath;
return true;
}
var prefix = sourceBasePath + "/";
if (path.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
{
var suffix = path[prefix.Length..];
if (!suffix.Contains('/'))
{
targetPath = targetBasePath + "/" + suffix;
return true;
}
}
targetPath = string.Empty;
return false;
}
}

View File

@@ -1,4 +1,4 @@
using System.Linq.Dynamic.Core;
using System.Linq.Dynamic.Core;
using System.Data;
using System.Globalization;
using Microsoft.EntityFrameworkCore;
@@ -229,10 +229,8 @@ namespace RadzenBlazorDemos
if (underlyingType == typeof(System.Guid) || underlyingType == typeof(System.DateTimeOffset))
return true;
#if NET6_0_OR_GREATER
if (underlyingType == typeof(System.DateOnly) || underlyingType == typeof(System.TimeOnly))
return true;
#endif
var typeCode = Type.GetTypeCode(underlyingType);
switch (typeCode)

View File

@@ -15,6 +15,7 @@ using System.IO;
using Radzen;
using RadzenBlazorDemos;
using RadzenBlazorDemos.Data;
using RadzenBlazorDemos.Host;
using RadzenBlazorDemos.Services;
using RadzenBlazorDemos.Host.Services;
using Microsoft.AspNetCore.Http;
@@ -134,6 +135,8 @@ if (!app.Environment.IsDevelopment())
}
app.UseRouting();
app.UseCanonicalRedirects();
app.UseTrailingSlashRedirect();
app.UseAntiforgery();
app.MapGet("/llms.txt", () =>
{

View File

@@ -0,0 +1,32 @@
using Microsoft.AspNetCore.Builder;
namespace RadzenBlazorDemos.Host;
/// <summary>
/// Middleware that removes trailing slashes from URLs with a 301 permanent redirect.
/// This prevents duplicate content issues where both /pricing and /pricing/ serve the same page.
/// </summary>
public static class TrailingSlashMiddleware
{
public static IApplicationBuilder UseTrailingSlashRedirect(this IApplicationBuilder app)
{
app.Use(async (context, next) =>
{
var path = context.Request.Path.Value;
if (path != "/" && path?.EndsWith('/') == true)
{
var newPath = path.TrimEnd('/') + context.Request.QueryString;
context.Response.StatusCode = 301;
context.Response.Headers.Location = newPath;
context.Response.Headers.CacheControl = "public, max-age=86400";
return;
}
await next();
});
return app;
}
}

View File

@@ -5,6 +5,44 @@ namespace RadzenBlazorDemos.Tools;
class Program
{
// Pages that are not component documentation (marketing, meta, showcases).
// These produce noisy content (testimonials, repeated CTAs, sample dashboards)
// that pollutes search results when used with a RAG system.
static readonly HashSet<string> ExcludedPages = new(StringComparer.OrdinalIgnoreCase)
{
"AccessibilityPage", // Generic WCAG/ARIA info, no component-specific content
"AI",
"Changelog",
"Dashboard",
"DashboardPage",
"GetStarted", // Installation steps, not component documentation
"Index",
"NotFound",
"Playground",
"SupportPage",
"ThemeServicePage", // Theme persistence setup steps, not component demos
"ThemesPage",
};
// Filename prefixes that match non-documentation showcase pages.
static readonly string[] ExcludedPrefixes = ["Templates", "UIBlocks"];
static bool IsExcluded(string filePath)
{
var name = Path.GetFileNameWithoutExtension(filePath);
if (ExcludedPages.Contains(name))
return true;
foreach (var prefix in ExcludedPrefixes)
{
if (name.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
return true;
}
return false;
}
static int Main(string[] args)
{
if (args.Length < 2)
@@ -41,7 +79,7 @@ class Program
static void Generate(string outputPath, string pagesPath, string servicesPath, string modelsPath)
{
var sb = new StringBuilder();
sb.AppendLine("# Radzen Blazor Components - Demo Application");
sb.AppendLine();
sb.AppendLine("This file contains all demo pages and examples from the Radzen Blazor Components demo application.");
@@ -49,16 +87,16 @@ class Program
sb.AppendLine();
sb.AppendLine("## Table of Contents");
sb.AppendLine();
// Collect all demo files
var allPages = Directory.GetFiles(pagesPath, "*.razor", SearchOption.AllDirectories)
.Where(f => !Path.GetFileName(f).StartsWith("_"))
.ToList();
// Separate main pages (with @page directive) from example components
var mainPages = new List<string>();
var examplePages = new List<string>();
foreach (var page in allPages)
{
var content = File.ReadAllText(page, Encoding.UTF8);
@@ -72,25 +110,28 @@ class Program
examplePages.Add(page);
}
}
// Sort main pages ascending by filename
mainPages = mainPages.OrderBy(p => Path.GetFileName(p)).ToList();
// Keep all pages for content generation (sorted ascending)
var pages = allPages.OrderBy(p => Path.GetFileName(p)).ToList();
// Sort main pages ascending by filename and apply exclusions
mainPages = mainPages
.Where(p => !IsExcluded(p))
.OrderBy(p => Path.GetFileName(p)).ToList();
// Only main pages (with @page directive) get their own sections.
// Example sub-components are embedded as code snippets via <RadzenExample>.
var pages = mainPages;
var pageCs = Directory.GetFiles(pagesPath, "*.cs", SearchOption.AllDirectories)
.OrderBy(f => Path.GetFileName(f))
.ToList();
var services = !string.IsNullOrEmpty(servicesPath) && Directory.Exists(servicesPath)
? Directory.GetFiles(servicesPath, "*.cs", SearchOption.AllDirectories).OrderBy(f => Path.GetFileName(f)).ToList()
: Enumerable.Empty<string>();
var models = !string.IsNullOrEmpty(modelsPath) && Directory.Exists(modelsPath)
? Directory.GetFiles(modelsPath, "*.cs", SearchOption.AllDirectories).OrderBy(f => Path.GetFileName(f)).ToList()
: Enumerable.Empty<string>();
// Generate table of contents - only include main pages
sb.AppendLine("### Demo Pages");
foreach (var page in mainPages)
@@ -99,7 +140,7 @@ class Program
var fileName = Path.GetFileNameWithoutExtension(page);
sb.AppendLine($"- [{fileName}](#{SanitizeAnchor(fileName)}) - `{relativePath}`");
}
if (pageCs.Any())
{
sb.AppendLine();
@@ -110,28 +151,36 @@ class Program
sb.AppendLine($"- [{fileName}](#{SanitizeAnchor(fileName)}-code-behind)");
}
}
sb.AppendLine();
sb.AppendLine("---");
sb.AppendLine();
// Add demo pages
sb.AppendLine("## Demo Pages");
sb.AppendLine();
foreach (var page in pages)
{
var relativePath = Path.GetRelativePath(pagesPath, page).Replace('\\', '/');
var fileName = Path.GetFileNameWithoutExtension(page);
var fileContent = File.ReadAllText(page, Encoding.UTF8);
var extractedContent = ExtractDescriptionsAndExamples(fileContent, page);
if (string.IsNullOrWhiteSpace(extractedContent))
continue;
// Add explicit HTML anchor to ensure TOC links work across all markdown parsers
var anchor = SanitizeAnchor(fileName);
sb.AppendLine($"<a id=\"{anchor}\"></a>");
// Skip sections that produce negligible content — these are typically
// sub-component pages whose rendered placeholder text leaked through
// (e.g. "km/h", "Value is:", single comma).
var textOnly = Regex.Replace(extractedContent, @"```[\s\S]*?```", ""); // strip code blocks
textOnly = Regex.Replace(textOnly, @"^#{1,6}\s+.*$", "", RegexOptions.Multiline); // strip headings
textOnly = Regex.Replace(textOnly, @"^\*\*Path:\*\*.*$", "", RegexOptions.Multiline); // strip path
textOnly = Regex.Replace(textOnly, @"^Example:$", "", RegexOptions.Multiline); // strip "Example:" labels
textOnly = textOnly.Trim();
if (textOnly.Length < 20)
continue;
sb.AppendLine($"### {fileName}");
sb.AppendLine();
sb.AppendLine($"**Path:** `{relativePath}`");
@@ -141,21 +190,19 @@ class Program
sb.AppendLine("---");
sb.AppendLine();
}
// Add C# code-behind files
if (pageCs.Any())
{
sb.AppendLine("## Code-Behind Files");
sb.AppendLine();
foreach (var csFile in pageCs)
{
var relativePath = Path.GetRelativePath(pagesPath, csFile).Replace('\\', '/');
var fileName = Path.GetFileNameWithoutExtension(csFile);
var fileContent = File.ReadAllText(csFile, Encoding.UTF8);
var codeBehindAnchor = SanitizeAnchor($"{fileName}-code-behind");
sb.AppendLine($"<a id=\"{codeBehindAnchor}\"></a>");
sb.AppendLine($"### {fileName} (Code-Behind)");
sb.AppendLine();
sb.AppendLine($"**Path:** `{relativePath}`");
@@ -168,19 +215,19 @@ class Program
sb.AppendLine();
}
}
// Add services
if (services.Any())
{
sb.AppendLine("## Services");
sb.AppendLine();
foreach (var service in services)
{
var relativePath = Path.GetRelativePath(servicesPath, service).Replace('\\', '/');
var fileName = Path.GetFileNameWithoutExtension(service);
var fileContent = File.ReadAllText(service, Encoding.UTF8);
sb.AppendLine($"### {fileName}");
sb.AppendLine();
sb.AppendLine($"**Path:** `{relativePath}`");
@@ -193,19 +240,19 @@ class Program
sb.AppendLine();
}
}
// Add models
if (models.Any())
{
sb.AppendLine("## Data Models");
sb.AppendLine();
foreach (var model in models)
{
var relativePath = Path.GetRelativePath(modelsPath, model).Replace('\\', '/');
var fileName = Path.GetFileNameWithoutExtension(model);
var fileContent = File.ReadAllText(model, Encoding.UTF8);
sb.AppendLine($"### {fileName}");
sb.AppendLine();
sb.AppendLine($"**Path:** `{relativePath}`");
@@ -218,7 +265,7 @@ class Program
sb.AppendLine();
}
}
// Write to file
var outputDir = Path.GetDirectoryName(outputPath);
if (!string.IsNullOrEmpty(outputDir))
@@ -232,13 +279,13 @@ class Program
{
if (string.IsNullOrWhiteSpace(text))
return "";
// Markdown heading anchors are generated by:
// 1. Convert to lowercase
// 2. Replace spaces and special chars with hyphens
// 3. Remove multiple consecutive hyphens
// 4. Trim hyphens from start/end
var anchor = text.ToLower()
.Replace(" ", "-")
.Replace("_", "-")
@@ -268,13 +315,13 @@ class Program
.Replace("`", "")
.Replace("'", "")
.Replace("\"", "");
// Remove multiple consecutive hyphens
anchor = Regex.Replace(anchor, @"-+", "-");
// Trim hyphens from start and end
anchor = anchor.Trim('-');
return anchor;
}
@@ -282,40 +329,56 @@ class Program
{
var result = new StringBuilder();
var pagesDirectory = Path.GetDirectoryName(pagePath) ?? "";
var seenText = new HashSet<string>(StringComparer.Ordinal);
// Remove @code blocks entirely
razorContent = Regex.Replace(razorContent,
@"@code\s*\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}",
"",
razorContent = Regex.Replace(razorContent,
@"@code\s*\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}",
"",
RegexOptions.Singleline | RegexOptions.IgnoreCase);
// Remove @page, @inject, @layout directives
razorContent = Regex.Replace(razorContent,
@"@(page|inject|layout|using|namespace|implements)[^\r\n]*",
"",
// Remove @page, @inject, @layout directives.
// Use \b word boundary so @page doesn't match @pageSizeOptions, etc.
razorContent = Regex.Replace(razorContent,
@"@(page|inject|layout|using|namespace|implements)\b[^\r\n]*",
"",
RegexOptions.IgnoreCase | RegexOptions.Multiline);
// Split content into lines to process sequentially
var lines = razorContent.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None);
var skipUntilNextHeading = false;
for (int i = 0; i < lines.Length; i++)
{
var line = lines[i];
// Check for RadzenText
if (line.Contains("<RadzenText"))
{
var textContent = ExtractRadzenTextContent(lines, ref i);
if (!string.IsNullOrWhiteSpace(textContent.Content))
if (!string.IsNullOrWhiteSpace(textContent.Content) && seenText.Add(textContent.Content))
{
// Format as heading if it's a heading style
// Skip sections that produce no useful component documentation:
// - "Keyboard Navigation": shortcuts rendered by <KeyboardNavigationDataGrid>
// from C# data, can't be extracted — leaves a generic placeholder sentence.
// - "Radzen Blazor Studio": IDE-specific content, not component API docs.
if (textContent.IsHeading && (
textContent.Content.Contains("Keyboard Navigation") ||
textContent.Content.Contains("Radzen Blazor Studio")))
{
skipUntilNextHeading = true;
continue;
}
if (textContent.IsHeading)
{
skipUntilNextHeading = false;
result.AppendLine();
result.AppendLine(textContent.Content);
result.AppendLine();
}
else
else if (!skipUntilNextHeading)
{
result.AppendLine(textContent.Content);
}
@@ -324,6 +387,9 @@ class Program
// Check for RadzenExample
else if (line.Contains("<RadzenExample"))
{
if (skipUntilNextHeading)
continue;
var exampleContent = ExtractRadzenExampleContent(lines, ref i, pagesDirectory);
if (!string.IsNullOrWhiteSpace(exampleContent))
{
@@ -336,7 +402,7 @@ class Program
}
}
}
return result.ToString().Trim();
}
@@ -344,36 +410,37 @@ class Program
{
var fullTag = new StringBuilder();
var depth = 0;
// Collect the complete RadzenText tag (may span multiple lines)
for (int i = index; i < lines.Length; i++)
{
var line = lines[i];
fullTag.AppendLine(line);
// Count opening and closing tags
var openMatches = Regex.Matches(line, @"<RadzenText", RegexOptions.IgnoreCase);
var closeMatches = Regex.Matches(line, @"</RadzenText>", RegexOptions.IgnoreCase);
depth += openMatches.Count - closeMatches.Count;
if (closeMatches.Count > 0 && depth == 0)
{
index = i;
break;
}
}
var tagContent = fullTag.ToString();
// Extract attributes - check both TextStyle and TagName for heading detection
// Only treat RadzenText as a heading when it has an explicit TextStyle.
// TagName (e.g. TagName="TagName.H2") is just an HTML rendering hint used
// for visual styling (marketing text, testimonials, CTAs) and does NOT
// indicate a semantic documentation heading.
var textStyleMatch = Regex.Match(tagContent, @"TextStyle=""TextStyle\.(H[2-6])""", RegexOptions.IgnoreCase);
var tagNameMatch = Regex.Match(tagContent, @"TagName=""TagName\.(H[1-6])""", RegexOptions.IgnoreCase);
bool isHeading = false;
int headingLevel = 0;
// Prefer TextStyle for heading level, but also check TagName
if (textStyleMatch.Success)
{
var hMatch = Regex.Match(textStyleMatch.Groups[1].Value, @"H(\d)");
@@ -383,67 +450,74 @@ class Program
isHeading = headingLevel >= 2 && headingLevel <= 6;
}
}
// If no TextStyle heading but TagName suggests heading, use that
if (!isHeading && tagNameMatch.Success)
{
var hMatch = Regex.Match(tagNameMatch.Groups[1].Value, @"H(\d)");
if (hMatch.Success)
{
var tagLevel = int.Parse(hMatch.Groups[1].Value);
if (tagLevel >= 2 && tagLevel <= 6)
{
headingLevel = tagLevel;
isHeading = true;
}
}
}
// Extract inner content
var contentMatch = Regex.Match(tagContent, @"<RadzenText[^>]*>([\s\S]*?)</RadzenText>", RegexOptions.IgnoreCase);
if (!contentMatch.Success)
return (string.Empty, false);
var content = contentMatch.Groups[1].Value.Trim();
// Convert <code> tags to markdown inline code BEFORE converting links
content = ConvertCodeTagsToMarkdown(content);
// Convert RadzenLink to markdown links BEFORE removing HTML tags
content = ConvertRadzenLinksToMarkdown(content);
// For headings, strip RadzenLink elements entirely (CTA buttons are not heading text).
// For non-headings, convert them to markdown links.
if (isHeading)
content = Regex.Replace(content, @"<RadzenLink[^>]*/\s*>|<RadzenLink[^>]*>[\s\S]*?</RadzenLink>", "", RegexOptions.IgnoreCase);
else
content = ConvertRadzenLinksToMarkdown(content);
// Remove all HTML tags (except markdown links which are already converted)
content = Regex.Replace(content, @"<[^>]+>", "");
// Remove @ expressions
content = Regex.Replace(content, @"@[A-Za-z0-9_.()]+", "");
// Remove Razor/C# expressions: @(...), @variable, $"...", ?.Member, etc.
content = Regex.Replace(content, @"@\([^)]*\)", ""); // @(...) expressions
content = Regex.Replace(content, @"@[A-Za-z0-9_.()]+", ""); // @variable references
content = Regex.Replace(content, @"\$""[^""]*""", ""); // $"..." string interpolations
content = Regex.Replace(content, @"\?\.\w+", ""); // ?.Member null-conditional access
// Clean up whitespace
content = Regex.Replace(content, @"\s+", " ").Trim();
// Format as heading if needed
// Skip content that looks like leaked C#/Razor rather than documentation text.
// Indicators: unbalanced parentheses, remaining code artifacts like => or {}.
if (content.Contains("=>") || content.Contains("FilterOperator") || content.Contains("FilterValue"))
return (string.Empty, false);
// Format as heading if needed.
// Markdown heading budget:
// # = document title
// ## = major sections (Demo Pages, Code-Behind)
// ### = page name (AccordionPage)
// #### = component heading (Accordion)
// ##### = sub-feature (Accordion with single expand)
// ###### = detail
if (isHeading && !string.IsNullOrWhiteSpace(content))
{
// Map heading levels: H2 -> ####, H4 -> ####, H5 -> #####, H6 -> ######
int markdownLevel = 4; // Default to ####
if (headingLevel == 4) markdownLevel = 4; // H4 -> ####
else if (headingLevel == 5) markdownLevel = 5; // H5 -> #####
else if (headingLevel == 6) markdownLevel = 6; // H6 -> ######
else if (headingLevel == 2) markdownLevel = 4; // H2 -> ####
int markdownLevel = headingLevel switch
{
2 => 4, // H2 -> #### component heading
3 => 5, // H3 -> ##### sub-feature
4 => 5, // H4 -> ##### sub-feature
5 => 6, // H5 -> ###### detail
6 => 6, // H6 -> ###### detail
_ => 4
};
content = new string('#', markdownLevel) + " " + content;
}
return (content, isHeading);
}
static string ConvertCodeTagsToMarkdown(string content)
{
// Match <code>...</code> tags
var codeMatches = Regex.Matches(content,
@"<code>([\s\S]*?)</code>",
var codeMatches = Regex.Matches(content,
@"<code>([\s\S]*?)</code>",
RegexOptions.IgnoreCase);
// Process in reverse to maintain indices
var matchesArray = new Match[codeMatches.Count];
for (int i = 0; i < codeMatches.Count; i++)
@@ -454,25 +528,25 @@ class Program
{
var match = matchesArray[i];
var codeContent = match.Groups[1].Value.Trim();
// Remove nested HTML tags from code content
codeContent = Regex.Replace(codeContent, @"<[^>]+>", "");
// Clean up @("@bind-Selected") to @bind-Selected (remove extra quotes and parentheses)
// Match: @("...") pattern and extract the content inside quotes
// Pattern: @("@bind-Selected") -> @bind-Selected
// Use non-verbatim string to avoid quote escaping issues
codeContent = Regex.Replace(codeContent, "@\\(\"([^\"]+)\"\\)", "$1");
codeContent = Regex.Replace(codeContent, "@\\('([^']+)'\\)", "$1");
if (!string.IsNullOrWhiteSpace(codeContent))
{
var markdownCode = $"`{codeContent}`";
content = content.Substring(0, match.Index) + markdownCode +
content = content.Substring(0, match.Index) + markdownCode +
content.Substring(match.Index + match.Length);
}
}
return content;
}
@@ -480,12 +554,12 @@ class Program
{
// Handle both self-closing and opening/closing RadzenLink tags
// Match the entire tag first, then extract attributes
// Pattern for self-closing: <RadzenLink ... />
var selfClosingPattern = @"<RadzenLink([^>]*?)\s*/>";
// Pattern for opening/closing: <RadzenLink ...>...</RadzenLink>
var openClosePattern = @"<RadzenLink([^>]*?)>([\s\S]*?)</RadzenLink>";
// Process self-closing tags
var selfClosingMatches = Regex.Matches(content, selfClosingPattern, RegexOptions.IgnoreCase);
var selfClosingArray = new Match[selfClosingMatches.Count];
@@ -498,16 +572,16 @@ class Program
var match = selfClosingArray[i];
var attributes = match.Groups[1].Value;
var (path, text) = ExtractLinkAttributes(attributes, "");
if (!string.IsNullOrWhiteSpace(path))
{
string linkText = !string.IsNullOrWhiteSpace(text) ? text : path;
var markdownLink = $"[{linkText}]({path})";
content = content.Substring(0, match.Index) + markdownLink +
content = content.Substring(0, match.Index) + markdownLink +
content.Substring(match.Index + match.Length);
}
}
// Process opening/closing tags
var linkMatches = Regex.Matches(content, openClosePattern, RegexOptions.IgnoreCase);
var matchesArray = new Match[linkMatches.Count];
@@ -521,21 +595,21 @@ class Program
var attributes = match.Groups[1].Value;
var innerContent = match.Groups[2].Value.Trim();
var (path, text) = ExtractLinkAttributes(attributes, innerContent);
if (!string.IsNullOrWhiteSpace(path))
{
string linkText = !string.IsNullOrWhiteSpace(text) ? text :
string linkText = !string.IsNullOrWhiteSpace(text) ? text :
(!string.IsNullOrWhiteSpace(innerContent) ? Regex.Replace(innerContent, @"<[^>]+>", "").Trim() : path);
if (string.IsNullOrWhiteSpace(linkText))
linkText = path;
var markdownLink = $"[{linkText}]({path})";
content = content.Substring(0, match.Index) + markdownLink +
content = content.Substring(0, match.Index) + markdownLink +
content.Substring(match.Index + match.Length);
}
}
return content;
}
@@ -543,27 +617,27 @@ class Program
{
string path = "";
string text = "";
// Extract Path attribute (can be in any order)
var pathMatch = Regex.Match(attributes, @"Path=[""]?([^""\s>]+)[""]?", RegexOptions.IgnoreCase);
if (pathMatch.Success)
{
path = pathMatch.Groups[1].Value;
}
// Extract Text attribute
var textMatch = Regex.Match(attributes, @"Text=[""]?([^""]+)[""]?", RegexOptions.IgnoreCase);
if (textMatch.Success)
{
text = textMatch.Groups[1].Value.Trim();
}
// If no Text attribute and there's inner content, use that
if (string.IsNullOrWhiteSpace(text) && !string.IsNullOrWhiteSpace(innerContent))
{
text = Regex.Replace(innerContent, @"<[^>]+>", "").Trim();
}
return (path, text);
}
@@ -571,48 +645,48 @@ class Program
{
var fullTag = new StringBuilder();
var depth = 0;
// Collect the complete RadzenExample tag
for (int i = index; i < lines.Length; i++)
{
var line = lines[i];
fullTag.AppendLine(line);
var openMatches = Regex.Matches(line, @"<RadzenExample", RegexOptions.IgnoreCase);
var closeMatches = Regex.Matches(line, @"</RadzenExample>", RegexOptions.IgnoreCase);
depth += openMatches.Count - closeMatches.Count;
if (closeMatches.Count > 0 && depth == 0)
{
index = i;
break;
}
}
var tagContent = fullTag.ToString();
// Extract Example attribute
var exampleMatch = Regex.Match(tagContent, @"Example=[""]?([^""\s>]+)[""]?", RegexOptions.IgnoreCase);
if (exampleMatch.Success)
{
var exampleName = exampleMatch.Groups[1].Value.Trim();
var exampleFilePath = Path.Combine(pagesDirectory, $"{exampleName}.razor");
if (File.Exists(exampleFilePath))
{
var exampleContent = File.ReadAllText(exampleFilePath, Encoding.UTF8);
return CleanExampleFile(exampleContent);
}
}
// Fallback: extract inline content
var inlineMatch = Regex.Match(tagContent, @"<RadzenExample[^>]*>([\s\S]*?)</RadzenExample>", RegexOptions.IgnoreCase);
if (inlineMatch.Success)
{
return CleanExampleContent(inlineMatch.Groups[1].Value);
}
return string.Empty;
}
@@ -620,21 +694,21 @@ class Program
{
if (string.IsNullOrWhiteSpace(content))
return string.Empty;
var result = content;
// Remove all HTML tags (including <strong>, <code>, <Radzen*>, etc.)
result = Regex.Replace(result, @"<[^>]+>", "");
// Remove @ expressions (like @variable, @ExampleService)
result = Regex.Replace(result, @"@[A-Za-z0-9_.()]+", "");
// Clean up multiple spaces but preserve single newlines for readability
result = Regex.Replace(result, @"[ \t]+", " ");
// Clean up multiple newlines (max 2 consecutive)
result = Regex.Replace(result, @"(\r?\n){3,}", Environment.NewLine + Environment.NewLine);
return result.Trim();
}
@@ -642,21 +716,21 @@ class Program
{
if (string.IsNullOrWhiteSpace(content))
return string.Empty;
var result = content;
// Remove nested RadzenExample tags if any
result = Regex.Replace(result,
@"<RadzenExample[^>]*>[\s\S]*?</RadzenExample>",
"",
result = Regex.Replace(result,
@"<RadzenExample[^>]*>[\s\S]*?</RadzenExample>",
"",
RegexOptions.IgnoreCase);
// Trim each line and remove empty lines
var lines = result.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None)
.Select(l => l.Trim())
.Where(l => !string.IsNullOrWhiteSpace(l))
.ToList();
return string.Join(Environment.NewLine, lines);
}
@@ -664,24 +738,20 @@ class Program
{
if (string.IsNullOrWhiteSpace(content))
return string.Empty;
var result = content;
// Remove @code blocks
result = Regex.Replace(result,
@"@code\s*\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}",
"",
RegexOptions.Singleline | RegexOptions.IgnoreCase);
// Remove @using, @inject, @page directives
result = Regex.Replace(result,
@"@(using|inject|page|layout|namespace|implements)[^\r\n]*",
"",
// Remove @using, @inject, @page directives (but NOT @code blocks — they contain
// essential C# code that users need to understand the examples).
// Use \b word boundary so @page doesn't match @pageSizeOptions, @pageIndex, etc.
result = Regex.Replace(result,
@"@(using|inject|page|layout|namespace|implements)\b[^\r\n]*",
"",
RegexOptions.IgnoreCase | RegexOptions.Multiline);
// Clean up multiple blank lines
result = Regex.Replace(result, @"(\r?\n\s*){3,}", Environment.NewLine + Environment.NewLine);
return result.Trim();
}
}

View File

@@ -1,6 +1,4 @@
@page "/aichat"
@page "/docs/guides/components/aichat.html"
@using Radzen
@using Radzen.Blazor

View File

@@ -1,5 +1,4 @@
@page "/accordion"
@page "/docs/guides/components/accordion.html"
@page "/accordion"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
Accordion

View File

@@ -1,6 +1,4 @@
@page "/arc-gauge"
@page "/docs/guides/components/arcgauge.html"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
Radzen Blazor Arc Gauge
</RadzenText>

View File

@@ -1,5 +1,4 @@
@page "/autocomplete"
@page "/docs/guides/components/autocomplete.html"
@page "/autocomplete"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
AutoComplete

View File

@@ -1,5 +1,4 @@
@page "/badge"
@page "/docs/guides/components/badge.html"
@page "/badge"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
Badge
</RadzenText>

View File

@@ -1,6 +1,4 @@
@page "/barcode"
@page "/docs/guides/components/barcode.html"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
Barcode
</RadzenText>

View File

@@ -1,5 +1,4 @@
@page "/breadcrumb"
@page "/docs/guides/components/breadcrumb.html"
@page "/breadcrumb"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
BreadCrumb
</RadzenText>

View File

@@ -1,7 +1,4 @@
@page "/buttons"
@page "/button"
@page "/docs/guides/components/button.html"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
Button
</RadzenText>

View File

@@ -1,5 +1,4 @@
@page "/carousel"
@page "/docs/guides/components/carousel.html"
@page "/carousel"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
Carousel

View File

@@ -1,6 +1,4 @@
@page "/chart-series"
@page "/docs/guides/components/chart.html"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
Radzen Blazor Chart
</RadzenText>

View File

@@ -1,6 +1,4 @@
@page "/chat"
@page "/docs/guides/components/chat.html"
@using Radzen
@using Radzen.Blazor

View File

@@ -1,5 +1,4 @@
@page "/checkboxlist"
@page "/docs/guides/components/checkboxlist.html"
@page "/checkboxlist"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
CheckBoxList

View File

@@ -1,5 +1,4 @@
@page "/checkbox"
@page "/docs/guides/components/checkbox.html"
@page "/checkbox"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
CheckBox
</RadzenText>

View File

@@ -1,5 +1,4 @@
@page "/colorpicker"
@page "/docs/guides/components/colorpicker.html"
@page "/colorpicker"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
ColorPicker
</RadzenText>

View File

@@ -1,5 +1,4 @@
@page "/comparevalidator"
@page "/docs/guides/components/comparevalidator.html"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
CompareValidator
</RadzenText>

View File

@@ -1,5 +1,4 @@
@page "/contextmenu"
@page "/docs/guides/components/contextmenu.html"
@page "/contextmenu"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
ContextMenu

View File

@@ -1,5 +1,4 @@
@page "/datagrid"
@page "/docs/guides/components/datagrid.html"
@page "/datagrid"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
DataGrid

View File

@@ -1,5 +1,4 @@
@page "/datalist"
@page "/docs/guides/components/datalist.html"
@page "/datalist"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
DataList

View File

@@ -1,5 +1,4 @@
@page "/datepicker"
@page "/docs/guides/components/datepicker.html"
@page "/datepicker"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
DatePicker

View File

@@ -1,5 +1,4 @@
@page "/dialog"
@page "/docs/guides/components/dialog.html"
@page "/dialog"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
Dialog

View File

@@ -1,5 +1,4 @@
@page "/dropdown-datagrid"
@page "/docs/guides/components/dropdowndatagrid.html"
@page "/dropdown-datagrid"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
DropDownDataGrid

View File

@@ -1,5 +1,4 @@
@page "/dropdown"
@page "/docs/guides/components/dropdown.html"
@page "/dropdown"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
DropDown

View File

@@ -1,6 +1,4 @@
@page "/emailvalidator"
@page "/docs/guides/components/emailvalidator.html"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
EmailValidator
</RadzenText>

View File

@@ -1,5 +1,4 @@
@page "/fieldset"
@page "/docs/guides/components/fieldset.html"
@page "/fieldset"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
Fieldset

View File

@@ -1,5 +1,4 @@
@page "/fileinput"
@page "/docs/guides/components/fileinput.html"
@page "/fileinput"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
FileInput

View File

@@ -1,14 +1,5 @@
@inject NavigationManager NavigationManager
@page "/get-started/{version?}"
@page "/getting-started/{version?}"
@page "/docs/guides/getting-started/installation.html/{version?}"
@page "/docs/guides/getting-started/use-component.html"
@page "/docs/guides/getting-started/context-menu.html"
@page "/docs/guides/getting-started/dialog.html"
@page "/docs/guides/getting-started/notification.html"
@page "/docs/guides/getting-started/tooltip.html"
@page "/docs/guides/index.html"
<PageTitle>Get Started | Free UI Components by Radzen</PageTitle>
<HeadContent>
<meta name="description" content="How to get started with the Radzen Blazor Component library.">
@@ -28,7 +19,7 @@ More info is available in the <RadzenLink Text="rendering mode article" Path="ht
</RadzenAlert>
<RadzenAlert AlertStyle="AlertStyle.Info" Variant="Variant.Flat" Shade="Shade.Lighter" AllowClose="false" class="rz-mb-12">
If you have upgraded your application to .NET 8 .NET 9 or .NET 10 follow the getting started instructions for the version you initially used - e.g.
<RadzenLink Path="get-started/net7-server" Text=".NET 7" /> or <RadzenLink Path="get-started/net6-server" Text=".NET 6"/>.
<RadzenLink Path="get-started/net7-server" Text=".NET 7" />.
These instructions assume the new application layout which uses rendering modes and was introduced with .NET 8.
</RadzenAlert>
@Installation(CurrentVersion)
@@ -83,26 +74,6 @@ These instructions assume the new application layout which uses rendering modes
@Additional(CurrentVersion)
</div>
</RadzenTabsItem>
<RadzenTabsItem Text=".NET 6 Server">
<div class="rz-p-0 rz-p-sm-2 rz-px-lg-12">
@Installation(CurrentVersion)
@Import(CurrentVersion)
@IncludeTheme(("_Layout.cshtml", "ServerPrerendered", CurrentVersion))
@IncludeJavaScript(("_Layout.cshtml", CurrentVersion))
@UseComponent(CurrentVersion)
@Additional(CurrentVersion)
</div>
</RadzenTabsItem>
<RadzenTabsItem Text=".NET 6 WebAssembly (hosted)">
<div class="rz-p-0 rz-p-sm-2 rz-px-lg-12">
@Installation(CurrentVersion)
@Import(CurrentVersion)
@IncludeTheme(("_Layout.cshtml", "WebAssemblyPrerendered", CurrentVersion))
@IncludeJavaScript(("_Layout.cshtml", CurrentVersion))
@UseComponent(CurrentVersion)
@Additional(CurrentVersion)
</div>
</RadzenTabsItem>
<RadzenTabsItem Text="WebAssembly (standalone)">
<div class="rz-p-0 rz-p-sm-2 rz-px-lg-12">
@Installation(CurrentVersion)
@@ -152,7 +123,7 @@ These instructions assume the new application layout which uses rendering modes
</RadzenTabs>
@code {
private static readonly string[] versions = ["net9", "net7-server", "net7-wasm", "net6-server", "net6-wasm", "wasm", "csp"];
private static readonly string[] versions = ["net9", "net7-server", "net7-wasm", "wasm", "csp"];
private int selectedIndex = 0;
@@ -166,6 +137,10 @@ These instructions assume the new application layout which uses rendering modes
base.OnParametersSet();
selectedIndex = Array.IndexOf(versions, CurrentVersion);
if (selectedIndex < 0)
{
selectedIndex = 0;
}
}
private void OnTabChange(int index)

View File

@@ -1,5 +1,4 @@
@page "/googlemap"
@page "/docs/guides/components/googlemap.html"
@page "/googlemap"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
GoogleMap

View File

@@ -1,5 +1,4 @@
@page "/gravatar"
@page "/docs/guides/components/gravatar.html"
@page "/gravatar"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
Gravatar

View File

@@ -1,5 +1,4 @@
@page "/html-editor"
@page "/docs/guides/components/htmleditor.html"
@page "/html-editor"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
HtmlEditor

View File

@@ -1,5 +1,4 @@
@page "/icon"
@page "/docs/guides/components/icon.html"
@page "/icon"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
Icon

View File

@@ -1,5 +1,4 @@
@page "/image"
@page "/docs/guides/components/image.html"
@page "/image"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
Image

View File

@@ -1,6 +1,5 @@
@layout HomeLayout
@page "/"
@page "/docs/"
@inject ExampleService ExampleService
@inject NotificationService NotificationService
@inject DialogService DialogService

View File

@@ -1,6 +1,4 @@
@page "/label"
@page "/docs/guides/components/label.html"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
Label
</RadzenText>

View File

@@ -1,6 +1,4 @@
@page "/lengthvalidator"
@page "/docs/guides/components/lengthvalidator.html"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
LengthValidator
</RadzenText>

View File

@@ -1,5 +1,4 @@
@page "/link"
@page "/docs/guides/components/link.html"
@page "/link"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
Link

View File

@@ -1,5 +1,4 @@
@page "/listbox"
@page "/docs/guides/components/listbox.html"
@page "/listbox"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
ListBox

View File

@@ -1,5 +1,4 @@
@page "/login"
@page "/docs/guides/components/login.html"
@page "/login"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
Login

View File

@@ -1,5 +1,4 @@
@page "/mask"
@page "/docs/guides/components/mask.html"
@page "/mask"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
Mask

View File

@@ -1,6 +1,4 @@
@page "/media-query"
@page "/docs/guides/components/media-query.html"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
MediaQuery
</RadzenText>

View File

@@ -1,5 +1,4 @@
@page "/menu"
@page "/docs/guides/components/menu.html"
@page "/menu"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
Menu

View File

@@ -1,5 +1,4 @@
@page "/notification"
@page "/docs/guides/components/notification.html"
@page "/notification"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
Notification

View File

@@ -1,5 +1,4 @@
@page "/numeric"
@page "/docs/guides/components/numeric.html"
@page "/numeric"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
Numeric

View File

@@ -1,6 +1,4 @@
@page "/numericrangevalidator"
@page "/docs/guides/components/numericrangevalidator.html"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
NumericRangeValidator
</RadzenText>

View File

@@ -1,5 +1,4 @@
@page "/pager"
@page "/docs/guides/components/pager.html"
@page "/pager"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
Pager

View File

@@ -1,5 +1,4 @@
@page "/panelmenu"
@page "/docs/guides/components/panelmenu.html"
@page "/panelmenu"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
PanelMenu

View File

@@ -1,5 +1,4 @@
@page "/panel"
@page "/docs/guides/components/panel.html"
@page "/panel"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
Panel

View File

@@ -1,5 +1,4 @@
@page "/password"
@page "/docs/guides/components/password.html"
@page "/password"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
Password

View File

@@ -1,5 +1,4 @@
@page "/profile-menu"
@page "/docs/guides/components/profilemenu.html"
@page "/profile-menu"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
ProfileMenu

View File

@@ -1,5 +1,4 @@
@page "/progressbar"
@page "/docs/guides/components/progressbar.html"
@page "/progressbar"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
ProgressBar

View File

@@ -1,6 +1,4 @@
@page "/qrcode"
@page "/docs/guides/components/qrcode.html"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
QRCode
</RadzenText>

View File

@@ -1,6 +1,4 @@
@page "/radial-gauge"
@page "/docs/guides/components/radialgauge.html"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
Radzen Blazor Radial Gauge
</RadzenText>

View File

@@ -1,5 +1,4 @@
@page "/radiobuttonlist"
@page "/docs/guides/components/radiobuttonlist.html"
@page "/radiobuttonlist"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
RadioButtonList

View File

@@ -1,5 +1,4 @@
@page "/rating"
@page "/docs/guides/components/rating.html"
@page "/rating"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
Rating

View File

@@ -1,6 +1,4 @@
@page "/regexvalidator"
@page "/docs/guides/components/regexvalidator.html"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
RegexValidator
</RadzenText>

View File

@@ -1,6 +1,4 @@
@page "/requiredvalidator"
@page "/docs/guides/components/requiredvalidator.html"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
RequiredValidator
</RadzenText>

View File

@@ -1,5 +1,4 @@
@page "/ssrsviewer"
@page "/docs/guides/components/ssrsviewer.html"
@page "/ssrsviewer"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
SSRS Viewer

View File

@@ -1,6 +1,4 @@
@page "/scheduler"
@page "/docs/guides/components/scheduler.html"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
Scheduler
</RadzenText>

Some files were not shown because too many files have changed in this diff Show More