* Remove Microsoft.CodeAnalysis. Add own C# expression parser.

* Popup dialog animations (#2118)

* Experiment with popup and dialog animations.

* Move animations to a separate _animations.scss

* Remove dialog closing animation.

* Support side dialog positions. Prefix keyframes.

* Use --rz-transition for animation function and duration

* Reset site.css.

* PanelMenu animations.

* Display none is toggled a bit late.

* RadzenPanel animations.

* More animations.

* Use transitions for panel menu.

* Remove old code.

* Accordion uses transitions.

* Panel uses transitions.

* Extract expand and collapse implementation in a separate component.

* Set initial expand state earlier to prevent two renders.

* Add open animation to notifications.

* Handle onanimationend before toggling the animation classes.

* Add menu animation.

* Experiment with tree animation.

* Add animations to fieldset.

---------

Co-authored-by: yordanov <vasil@yordanov.info>

* ExpressionSerializer and tests added (#2119)

* Fix failing tests.

* various components RequiresUnreferencedCode attribute added (#2120)

* RequiresUnreferencedCode added to ExpressionSerializer

* Update premium themes

* RequiresUnreferencedCode added to ExpressionParser

* FormComponentWithAutoComplete RequiresUnreferencedCode removed

* Revert "FormComponentWithAutoComplete RequiresUnreferencedCode removed"

This reverts commit ec900a4df8.

* Revert "RequiresUnreferencedCode added to ExpressionParser"

This reverts commit f93b3b159b.

* Revert "RequiresUnreferencedCode added to ExpressionSerializer"

This reverts commit 06fecec9a6.

* Revert "various components RequiresUnreferencedCode attribute added (#2120)"

This reverts commit 2ed1a6cac1.

* Remove RadzenHtml.

* ExpressionSerializer FormatValue updated to use InvariantCulture

* Catch potential JS interop exceptions that could occur during disposing.

* Revert "Remove RadzenHtml."

This reverts commit 319085bf49.

* SelectBar made single tab stop

* RadioButtonList and CheckBoxList made single tab stop

* SelectBar accessibility improved

* RadioButtonList accessibility improved

* CheckBoxList accessibility improved

* Update radio button focus styles

* Update checkbox list focus styles

* Update Checkbox Radio and SelectBar focus styles

* SelectBar, CheckBoxList and RadioButtonList focus state improved

* Check for Multiple added

* Use non-rendering event handlers for transitionend.

* Rename css class rz-selectbutton to rz-selectbar and improve focus states

* Fix selectbar focus outline offset

* Update premium themes

* Selectbar item focus styles should not be visible if the item is disabled.

* CheckBoxList and RadioButtonList item focus should be visible only on keyboard input

* SelectBar, CheckBoxList and RadioButtonList focus logic improved

* Update animations

* RadzenText margin-block should be 0 if it is in RadzenStack. Resolves #2134

* RadzenText margin-block should be 0 if it is first level child in RadzenStack

* CheckBoxList focused fixed

* Add toggle state classes to panel menu icon.

* Update accordion styles to reflect expander changes

* Add animation styles to expand arrow in Menu and ProfileMenu

* Use a instead of NavLink as it seems to cause performance issues.

* Set @bind-Expanded.

* Revert "Set @bind-Expanded."

This reverts commit 994107367bdf09043950f8bbe701eb9edefec676.

* Revert "Use a instead of NavLink as it seems to cause performance issues."

This reverts commit 05d5bef8f421bbeb5828ba1e9c5af6793ea3d32a.

* Reduce rendering of panel menu items.

* Add panel menu component.

* Use ChildContent to render the toggle icon of the panel menu item.

* Sync panel menu item selection in the item itself.

* Rename ExpandedInternal to expanded.

* Move filtering to the panel menu component.

* Remove the transitionend handler to avoid a second rendering pass.

* Build the assets for the net9.0 framework.

* Do not trigger render when Click is used.

* Panel menu keyboard navigation renders only when needed.

* Focus reworked to use AsNonRenderingEventHandler

* Focus the first item.

* Update Panel demo

* Use a more robust algorithm for month view event rendering that handles overlapping of events across a week.

* Use RadzenStack in RadioButtonList

* Add parsing support for `&` and `|`.

* Add parsing support for `^`, `>>` and `<<`.

* Simplify expression parsing tests.

* Use RadzenStack in RadioButtonList and CheckBoxList

* Change defaults for AlignItems and JustifyContent in RadioButtonList and CheckBoxList

* Update RadioButtonList and CheckBoxList demos

* Add --rz-input-border-block-end css variables to improve Fluent theme styles

* Removed AsNonRenderingEventHandler from RadioButtonList and HtmlEditor focus and blur

* Removed AsNonRenderingEventHandler from CheckBoxList

* Simplify RadzenTable rendering.

* Optimize memory usage of the ClassList utility.

* Refactor RadzenButton to use ClassList.

* RadzenSelectBar and RadzenSplitButton use ClassList.

* Refactor RadzenBadge and RadzenAlert to use ClassList.

* Refactor RadzenCard and RadzenFormField to use ClassList.

* Refactor RadzenCardGroup and progress components to use ClassList.

* Refactor RadzenMenu to use ClassList.

* Use ClassList in RadzenBody, RadzenLayout and editor rendering components.

* RadzenDialog uses ClassList.

* RadzenDataGrid uses ClassList.

* RadzenPager uses ClassList.

* RadzenColumn uses ClassList.

* Fix RadzenSplitButtonItem focused state.

---------

Co-authored-by: Atanas Korchev <akorchev@gmail.com>
Co-authored-by: Atanas Korchev <454726+akorchev@users.noreply.github.com>
Co-authored-by: yordanov <vasil@yordanov.info>
Co-authored-by: Quentin H <67709967+quintushr@users.noreply.github.com>
This commit is contained in:
Vladimir Enchev
2025-05-07 13:11:30 +03:00
committed by GitHub
parent 513e63329b
commit c7db9394c8
146 changed files with 6414 additions and 3277 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,304 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Xunit;
namespace Radzen.Blazor.Tests
{
class TestEntity
{
public string Name { get; set; }
public int Age { get; set; }
public double Salary { get; set; }
public float Score { get; set; }
public decimal Balance { get; set; }
public short Level { get; set; }
public long Population { get; set; }
public Status AccountStatus { get; set; }
public DateTime CreatedAt { get; set; }
public DateTimeOffset LastUpdated { get; set; }
public Guid Id { get; set; }
public TimeOnly StartTime { get; set; }
public DateOnly BirthDate { get; set; }
public int[] Scores { get; set; }
public List<string> Tags { get; set; }
public List<TestEntity> Children { get; set; }
public Address Address { get; set; }
public double[] Salaries { get; set; }
public float[] Heights { get; set; }
public decimal[] Balances { get; set; }
public short[] Levels { get; set; }
public long[] Populations { get; set; }
public string[] Names { get; set; }
public Guid[] Ids { get; set; }
public DateTime[] CreatedDates { get; set; }
public DateTimeOffset[] UpdatedDates { get; set; }
public TimeOnly[] StartTimes { get; set; }
public DateOnly[] BirthDates { get; set; }
public Status[] Statuses { get; set; }
}
enum Status
{
Active,
Inactive,
Suspended
}
class Address
{
public string City { get; set; }
public string Country { get; set; }
}
public class ExpressionSerializerTests
{
private readonly ExpressionSerializer _serializer = new ExpressionSerializer();
[Fact]
public void Serializes_SimpleBinaryExpression()
{
Expression<Func<int, bool>> expr = e => e > 10;
Assert.Equal("e => (e > 10)", _serializer.Serialize(expr));
}
[Fact]
public void Serializes_StringEquality()
{
Expression<Func<TestEntity, bool>> expr = e => e.Name == "John";
Assert.Equal("e => (e.Name == \"John\")", _serializer.Serialize(expr));
}
[Fact]
public void Serializes_IntComparison()
{
Expression<Func<TestEntity, bool>> expr = e => e.Age > 18;
Assert.Equal("e => (e.Age > 18)", _serializer.Serialize(expr));
}
[Fact]
public void Serializes_DoubleComparison()
{
Expression<Func<TestEntity, bool>> expr = e => e.Salary < 50000.50;
Assert.Equal("e => (e.Salary < 50000.5)", _serializer.Serialize(expr));
}
[Fact]
public void Serializes_FloatComparison()
{
Expression<Func<TestEntity, bool>> expr = e => e.Score >= 85.3f;
Assert.Equal("e => (e.Score >= 85.3)", _serializer.Serialize(expr));
}
[Fact]
public void Serializes_DecimalComparison()
{
Expression<Func<TestEntity, bool>> expr = e => e.Balance <= 1000.75m;
Assert.Equal("e => (e.Balance <= 1000.75)", _serializer.Serialize(expr));
}
[Fact]
public void Serializes_ShortComparison()
{
Expression<Func<TestEntity, bool>> expr = e => e.Level == 3;
Assert.Equal("e => (e.Level == 3)", _serializer.Serialize(expr));
}
[Fact]
public void Serializes_LongComparison()
{
Expression<Func<TestEntity, bool>> expr = e => e.Population > 1000000L;
Assert.Equal("e => (e.Population > 1000000)", _serializer.Serialize(expr));
}
[Fact]
public void Serializes_EnumComparison()
{
Expression<Func<TestEntity, bool>> expr = e => e.AccountStatus == Status.Inactive;
Assert.Equal("e => (e.AccountStatus == 1)", _serializer.Serialize(expr));
}
[Fact]
public void Serializes_ArrayContainsValue()
{
Expression<Func<TestEntity, bool>> expr = e => e.Scores.Contains(100);
Assert.Equal("e => e.Scores.Contains(100)", _serializer.Serialize(expr));
}
[Fact]
public void Serializes_ArrayNotContainsValue()
{
Expression<Func<TestEntity, bool>> expr = e => !e.Scores.Contains(100);
Assert.Equal("e => (!e.Scores.Contains(100))", _serializer.Serialize(expr));
}
[Fact]
public void Serializes_ArrayInValue()
{
Expression<Func<TestEntity, bool>> expr = e => e.Scores.Intersect(new [] { 100 }).Any();
Assert.Equal("e => e.Scores.Intersect(new [] { 100 }).Any()", _serializer.Serialize(expr));
}
[Fact]
public void Serializes_ArrayNotInValue()
{
Expression<Func<TestEntity, bool>> expr = e => e.Scores.Except(new[] { 100 }).Any();
Assert.Equal("e => e.Scores.Except(new [] { 100 }).Any()", _serializer.Serialize(expr));
}
[Fact]
public void Serializes_ArrayInValueOposite()
{
Expression<Func<TestEntity, bool>> expr = e => new[] { 100 }.Intersect(e.Scores).Any();
Assert.Equal("e => new [] { 100 }.Intersect(e.Scores).Any()", _serializer.Serialize(expr));
}
[Fact]
public void Serializes_ArrayNotInValueOposite()
{
Expression<Func<TestEntity, bool>> expr = e => new[] { 100 }.Except(e.Scores).Any();
Assert.Equal("e => new [] { 100 }.Except(e.Scores).Any()", _serializer.Serialize(expr));
}
[Fact]
public void Serializes_IntArrayInValueOposite()
{
Expression<Func<TestEntity, bool>> expr = e => new[] { 100 }.Intersect(e.Scores).Any();
Assert.Equal("e => new [] { 100 }.Intersect(e.Scores).Any()", _serializer.Serialize(expr));
}
[Fact]
public void Serializes_IntArrayNotInValueOposite()
{
Expression<Func<TestEntity, bool>> expr = e => !new[] { 100 }.Intersect(e.Scores).Any();
Assert.Equal("e => (!new [] { 100 }.Intersect(e.Scores).Any())", _serializer.Serialize(expr));
}
[Fact]
public void Serializes_DoubleArrayInValueOposite()
{
Expression<Func<TestEntity, bool>> expr = e => new[] { 99.99 }.Intersect(e.Salaries).Any();
Assert.Equal("e => new [] { 99.99 }.Intersect(e.Salaries).Any()", _serializer.Serialize(expr));
}
[Fact]
public void Serializes_FloatArrayInValueOposite()
{
Expression<Func<TestEntity, bool>> expr = e => new[] { 5.5f }.Intersect(e.Heights).Any();
Assert.Equal("e => new [] { 5.5 }.Intersect(e.Heights).Any()", _serializer.Serialize(expr));
}
[Fact]
public void Serializes_DecimalArrayInValueOposite()
{
Expression<Func<TestEntity, bool>> expr = e => new[] { 1000.75m }.Intersect(e.Balances).Any();
Assert.Equal("e => new [] { 1000.75 }.Intersect(e.Balances).Any()", _serializer.Serialize(expr));
}
[Fact]
public void Serializes_ShortArrayInValueOposite()
{
Expression<Func<TestEntity, bool>> expr = e => new [] { (short)3 }.Intersect(e.Levels).Any();
Assert.Equal("e => new [] { 3 }.Intersect(e.Levels).Any()", _serializer.Serialize(expr));
}
[Fact]
public void Serializes_LongArrayInValueOposite()
{
Expression<Func<TestEntity, bool>> expr = e => new [] { 1000000L }.Intersect(e.Populations).Any();
Assert.Equal("e => new [] { 1000000 }.Intersect(e.Populations).Any()", _serializer.Serialize(expr));
}
[Fact]
public void Serializes_StringArrayInValueOposite()
{
Expression<Func<TestEntity, bool>> expr = e => new[] { "Alice", "Bob" }.Intersect(e.Names).Any();
Assert.Equal("e => (new [] { \"Alice\", \"Bob\" }).Intersect(e.Names).Any()", _serializer.Serialize(expr));
}
[Fact]
public void Serializes_GuidArrayInValueOposite()
{
Expression<Func<TestEntity, bool>> expr = e => new[] { Guid.Parse("12345678-1234-1234-1234-123456789abc") }.Intersect(e.Ids).Any();
Assert.Equal("e => (new [] { Guid.Parse(\"12345678-1234-1234-1234-123456789abc\") }).Intersect(e.Ids).Any()", _serializer.Serialize(expr));
}
[Fact]
public void Serializes_DateTimeArrayInValueOposite()
{
Expression<Func<TestEntity, bool>> expr = e => new[] { DateTime.Parse("2023-01-01T00:00:00.000Z") }.Intersect(e.CreatedDates).Any();
Assert.Equal("e => (new [] { DateTime.Parse(\"2023-01-01T00:00:00.000Z\") }).Intersect(e.CreatedDates).Any()", _serializer.Serialize(expr));
}
[Fact]
public void Serializes_DateTimeOffsetArrayInValueOposite()
{
Expression<Func<TestEntity, bool>> expr = e => new[] { DateTimeOffset.Parse("2023-01-01T10:30:00.000+00:00") }.Intersect(e.UpdatedDates).Any();
Assert.Equal("e => (new [] { DateTimeOffset.Parse(\"2023-01-01T10:30:00.000+00:00\") }).Intersect(e.UpdatedDates).Any()", _serializer.Serialize(expr));
}
[Fact]
public void Serializes_TimeOnlyArrayInValueOposite()
{
Expression<Func<TestEntity, bool>> expr = e => new[] { TimeOnly.Parse("12:00:00") }.Intersect(e.StartTimes).Any();
Assert.Equal("e => (new [] { TimeOnly.Parse(\"12:00:00\") }).Intersect(e.StartTimes).Any()", _serializer.Serialize(expr));
}
[Fact]
public void Serializes_DateOnlyArrayInValueOposite()
{
Expression<Func<TestEntity, bool>> expr = e => new[] { DateOnly.Parse("2000-01-01") }.Intersect(e.BirthDates).Any();
Assert.Equal("e => (new [] { DateOnly.Parse(\"2000-01-01\") }).Intersect(e.BirthDates).Any()", _serializer.Serialize(expr));
}
[Fact]
public void Serializes_EnumArrayInValueOposite()
{
Expression<Func<TestEntity, bool>> expr = e => new[] { Status.Active, Status.Inactive }.Intersect(e.Statuses).Any();
Assert.Equal("e => (new [] { (Radzen.Blazor.Tests.Status)0, (Radzen.Blazor.Tests.Status)1 }).Intersect(e.Statuses).Any()", _serializer.Serialize(expr));
}
[Fact]
public void Serializes_ListContainsValue()
{
Expression<Func<TestEntity, bool>> expr = e => e.Tags.Contains("VIP");
Assert.Equal("e => e.Tags.Contains(\"VIP\")", _serializer.Serialize(expr));
}
[Fact]
public void Serializes_ListNotContainsValue()
{
Expression<Func<TestEntity, bool>> expr = e => !e.Tags.Contains("VIP");
Assert.Equal("e => (!e.Tags.Contains(\"VIP\"))", _serializer.Serialize(expr));
}
[Fact]
public void Serializes_ListAnyCheck()
{
Expression<Func<TestEntity, bool>> expr = e => e.Children.Any(c => c.Age > 18);
Assert.Equal("e => e.Children.Any(c => (c.Age > 18))", _serializer.Serialize(expr));
}
[Fact]
public void Serializes_ListNotAnyCheck()
{
Expression<Func<TestEntity, bool>> expr = e => !e.Children.Any(c => c.Age > 18);
Assert.Equal("e => (!e.Children.Any(c => (c.Age > 18)))", _serializer.Serialize(expr));
}
[Fact]
public void Serializes_EntitySubPropertyCheck()
{
Expression<Func<TestEntity, bool>> expr = e => e.Address.City == "New York";
Assert.Equal("e => (e.Address.City == \"New York\")", _serializer.Serialize(expr));
}
[Fact]
public void Serializes_ComplexExpressionWithProperties()
{
Expression<Func<TestEntity, bool>> expr = e => e.Age > 18 && e.Tags.Contains("Member") || e.Address.City == "London";
Assert.Equal("e => (((e.Age > 18) && e.Tags.Contains(\"Member\")) || (e.Address.City == \"London\"))", _serializer.Serialize(expr));
}
}
}

View File

@@ -184,13 +184,13 @@ namespace Radzen.Blazor.Tests
Assert.Contains("SummaryContent", component.Markup);
Assert.Equal(
"",
component.Find(".rz-fieldset-content-summary").ParentElement.Attributes.First(attr => attr.Name == "style").Value
"false",
component.Find(".rz-fieldset-content-summary").ParentElement.ParentElement.Attributes.First(attr => attr.Name == "aria-hidden").Value
);
}
[Fact]
public void Fieldset_DontRenders_SummaryWhenOpen()
public void Fieldset_DoesNotRender_SummaryWhenOpen()
{
using var ctx = new TestContext();
var component = ctx.RenderComponent<RadzenFieldset>();
@@ -210,8 +210,8 @@ namespace Radzen.Blazor.Tests
Assert.Contains("SummaryContent", component.Markup);
Assert.Equal(
"display: none",
component.Find(".rz-fieldset-content-summary").ParentElement.Attributes.First(attr => attr.Name == "style").Value
"true",
component.Find(".rz-fieldset-content-summary").ParentElement.ParentElement.Attributes.First(attr => attr.Name == "aria-hidden").Value
);
}
}

View File

@@ -198,14 +198,10 @@ namespace Radzen.Blazor.Tests
});
Assert.Contains("SummaryContent", component.Markup);
Assert.Equal(
"display: block",
component.Find(".rz-panel-content-summary").ParentElement.Attributes.First(attr => attr.Name == "style").Value
);
}
[Fact]
public void Panel_DontRenders_SummaryWhenOpen()
public void Panel_DoesNotRender_SummaryWhenOpen()
{
using var ctx = new TestContext();
var component = ctx.RenderComponent<RadzenPanel>();
@@ -225,8 +221,8 @@ namespace Radzen.Blazor.Tests
Assert.Contains("SummaryContent", component.Markup);
Assert.Equal(
"display: none",
component.Find(".rz-panel-content-summary").ParentElement.Attributes.First(attr => attr.Name == "style").Value
"true",
component.Find(".rz-panel-content-summary").ParentElement.ParentElement.Attributes.First(attr => attr.Name == "aria-hidden").Value
);
}
}

View File

@@ -0,0 +1,56 @@
using System;
using System.Reflection;
using System.Reflection.Emit;
static class DynamicTypeFactory
{
public static Type CreateType(string typeName, string[] propertyNames, Type[] propertyTypes)
{
if (propertyNames.Length != propertyTypes.Length)
{
throw new ArgumentException("Property names and types count mismatch.");
}
var assemblyName = new AssemblyName("DynamicTypesAssembly");
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
var moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicTypesModule");
var typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.Sealed);
for (int i = 0; i < propertyNames.Length; i++)
{
var fieldBuilder = typeBuilder.DefineField("_" + propertyNames[i], propertyTypes[i], FieldAttributes.Private);
var propertyBuilder = typeBuilder.DefineProperty(propertyNames[i], PropertyAttributes.None, propertyTypes[i], null);
var getterMethod = typeBuilder.DefineMethod(
"get_" + propertyNames[i],
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
propertyTypes[i],
Type.EmptyTypes);
var getterIl = getterMethod.GetILGenerator();
getterIl.Emit(OpCodes.Ldarg_0);
getterIl.Emit(OpCodes.Ldfld, fieldBuilder);
getterIl.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(getterMethod);
var setterMethod = typeBuilder.DefineMethod(
"set_" + propertyNames[i],
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
null,
[propertyTypes[i]]);
var setterIl = setterMethod.GetILGenerator();
setterIl.Emit(OpCodes.Ldarg_0);
setterIl.Emit(OpCodes.Ldarg_1);
setterIl.Emit(OpCodes.Stfld, fieldBuilder);
setterIl.Emit(OpCodes.Ret);
propertyBuilder.SetSetMethod(setterMethod);
}
var dynamicType = typeBuilder.CreateType();
return dynamicType;
}
}

View File

@@ -0,0 +1,873 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq.Expressions;
using System.Text;
namespace Radzen;
class Token
{
public TokenType Type { get; set; }
public string Value { get; set; } = string.Empty;
public ValueKind ValueKind { get; set; } = ValueKind.None;
public int IntValue { get; internal set; }
public uint UintValue { get; internal set; }
public long LongValue { get; internal set; }
public ulong UlongValue { get; internal set; }
public decimal DecimalValue { get; internal set; }
public float FloatValue { get; internal set; }
public double DoubleValue { get; internal set; }
public Token(TokenType type)
{
Type = type;
}
public Token(TokenType type, string value)
{
Type = type;
Value = value;
}
public ConstantExpression ToConstantExpression()
{
return ValueKind switch
{
ValueKind.Null => Expression.Constant(null),
ValueKind.String => Expression.Constant(Value),
ValueKind.Character => Expression.Constant(Value[0]),
ValueKind.Int => Expression.Constant(IntValue),
ValueKind.UInt => Expression.Constant(UintValue),
ValueKind.Long => Expression.Constant(LongValue),
ValueKind.ULong => Expression.Constant(UlongValue),
ValueKind.Float => Expression.Constant(FloatValue),
ValueKind.Double => Expression.Constant(DoubleValue),
ValueKind.Decimal => Expression.Constant(DecimalValue),
ValueKind.True => Expression.Constant(true),
ValueKind.False => Expression.Constant(false),
_ => throw new InvalidOperationException($"Unsupported value kind: {ValueKind}")
};
}
}
enum ValueKind
{
None,
String,
Int,
Float,
Double,
Decimal,
Character,
Null,
True,
False,
Long,
UInt,
ULong,
}
enum TokenType
{
None,
Identifier,
EqualsEquals,
NotEquals,
EqualsGreaterThan,
StringLiteral,
NumericLiteral,
Dot,
OpenParen,
CloseParen,
Comma,
AmpersandAmpersand,
Ampersand,
BarBar,
Bar,
GreaterThan,
LessThan,
LessThanOrEqual,
GreaterThanOrEqual,
Plus,
Minus,
Star,
Slash,
CharacterLiteral,
QuestionMark,
QuestionMarkQuestionMark,
Colon,
QuestionDot,
New,
NullLiteral,
TrueLiteral,
FalseLiteral,
OpenBracket,
CloseBracket,
OpenBrace,
CloseBrace,
ExclamationMark,
Equals,
Caret,
GreaterThanGreaterThan,
LessThanLessThan,
}
static class TokenTypeExtensions
{
public static ExpressionType ToExpressionType(this TokenType tokenType)
{
return tokenType switch
{
TokenType.EqualsEquals => ExpressionType.Equal,
TokenType.NotEquals => ExpressionType.NotEqual,
TokenType.EqualsGreaterThan => ExpressionType.GreaterThanOrEqual,
TokenType.AmpersandAmpersand => ExpressionType.AndAlso,
TokenType.Ampersand => ExpressionType.And,
TokenType.BarBar => ExpressionType.OrElse,
TokenType.Bar => ExpressionType.Or,
TokenType.GreaterThan => ExpressionType.GreaterThan,
TokenType.LessThan => ExpressionType.LessThan,
TokenType.LessThanOrEqual => ExpressionType.LessThanOrEqual,
TokenType.GreaterThanOrEqual => ExpressionType.GreaterThanOrEqual,
TokenType.Plus => ExpressionType.Add,
TokenType.Minus => ExpressionType.Subtract,
TokenType.Star => ExpressionType.Multiply,
TokenType.Slash => ExpressionType.Divide,
TokenType.Caret => ExpressionType.ExclusiveOr,
TokenType.GreaterThanGreaterThan => ExpressionType.RightShift,
TokenType.LessThanLessThan => ExpressionType.LeftShift,
_ => throw new InvalidOperationException($"Unsupported token type: {tokenType}")
};
}
}
class ExpressionLexer(string expression)
{
private int position;
private char Peek(int offset = 0)
{
if (position + offset >= expression.Length)
{
return '\0';
}
return expression[position + offset];
}
private void Advance(int count)
{
position += count;
}
bool TryAdvance(char expected)
{
if (Peek(1) == expected)
{
Advance(1);
return true;
}
return false;
}
private void ScanTrivia()
{
while (char.IsWhiteSpace(Peek()))
{
Advance(1);
}
}
public static List<Token> Scan(string expression)
{
var lexer = new ExpressionLexer(expression);
return [.. lexer.Scan()];
}
public IEnumerable<Token> Scan()
{
while (position < expression.Length)
{
ScanTrivia();
var token = ScanToken();
if (token.Type == TokenType.None)
{
yield break;
}
yield return token;
}
yield return new Token(TokenType.None, string.Empty);
}
private Token ScanToken()
{
var ch = Peek();
switch (ch)
{
case '"':
return ScanStringLiteral();
case '\'':
return ScanCharacterLiteral();
case '=':
if (TryAdvance('='))
{
Advance(1);
return new Token(TokenType.EqualsEquals);
}
if (TryAdvance('>'))
{
Advance(1);
return new Token(TokenType.EqualsGreaterThan);
}
Advance(1);
return new Token(TokenType.Equals);
case '!':
if (TryAdvance('='))
{
Advance(1);
return new Token(TokenType.NotEquals);
}
Advance(1);
return new Token(TokenType.ExclamationMark);
case '>':
if (TryAdvance('='))
{
Advance(1);
return new Token(TokenType.GreaterThanOrEqual);
}
if (TryAdvance('>'))
{
Advance(1);
return new Token(TokenType.GreaterThanGreaterThan);
}
Advance(1);
return new Token(TokenType.GreaterThan);
case '<':
if (TryAdvance('<'))
{
Advance(1);
return new Token(TokenType.LessThanLessThan);
}
if (TryAdvance('='))
{
Advance(1);
return new Token(TokenType.LessThanOrEqual);
}
Advance(1);
return new Token(TokenType.LessThan);
case '+':
Advance(1);
return new Token(TokenType.Plus);
case '-':
Advance(1);
return new Token(TokenType.Minus);
case '*':
Advance(1);
return new Token(TokenType.Star);
case '/':
Advance(1);
return new Token(TokenType.Slash);
case '.':
Advance(1);
return new Token(TokenType.Dot);
case '(':
Advance(1);
return new Token(TokenType.OpenParen);
case ')':
Advance(1);
return new Token(TokenType.CloseParen);
case '[':
Advance(1);
return new Token(TokenType.OpenBracket);
case ']':
Advance(1);
return new Token(TokenType.CloseBracket);
case '{':
Advance(1);
return new Token(TokenType.OpenBrace);
case '}':
Advance(1);
return new Token(TokenType.CloseBrace);
case ',':
Advance(1);
return new Token(TokenType.Comma);
case '&':
if (TryAdvance('&'))
{
Advance(1);
return new Token(TokenType.AmpersandAmpersand);
}
Advance(1);
return new Token(TokenType.Ampersand);
case '|':
if (TryAdvance('|'))
{
Advance(1);
return new Token(TokenType.BarBar);
}
Advance(1);
return new Token(TokenType.Bar);
case '?':
if (TryAdvance('.'))
{
Advance(1);
return new Token(TokenType.QuestionDot);
}
if (TryAdvance('?'))
{
Advance(1);
return new Token(TokenType.QuestionMarkQuestionMark);
}
Advance(1);
return new Token(TokenType.QuestionMark);
case ':':
Advance(1);
return new Token(TokenType.Colon);
case '^':
Advance(1);
return new Token(TokenType.Caret);
case >= '0' and <= '9':
return ScanNumericLiteral();
case '_':
case (>= 'a' and <= 'z') or (>= 'A' and <= 'Z'):
var token = ScanIdentifier();
return token.Value switch
{
"null" => new Token(TokenType.NullLiteral) { ValueKind = ValueKind.Null },
"true" => new Token(TokenType.TrueLiteral) { ValueKind = ValueKind.True },
"false" => new Token(TokenType.FalseLiteral) { ValueKind = ValueKind.False },
"new" => new Token(TokenType.New),
_ => token
};
}
return new Token(TokenType.None, string.Empty);
}
private char ScanEscapeSequence()
{
var ch = Peek();
Advance(1);
switch (ch)
{
case '\'':
case '"':
case '\\':
break;
case '0':
ch = '\u0000';
break;
case 'a':
ch = '\u0007';
break;
case 'b':
ch = '\u0008';
break;
case 'f':
ch = '\u000c';
break;
case 'n':
ch = '\u000a';
break;
case 'r':
ch = '\u000d';
break;
case 't':
ch = '\u0009';
break;
case 'v':
ch = '\u000b';
break;
case 'u':
case 'U':
case 'x':
ch = ScanUnicodeEscapeSequence(ch);
break;
default:
throw new InvalidOperationException($"Invalid escape sequence '\\{ch}' at position {position}.");
}
return ch;
}
private char ScanUnicodeEscapeSequence(char ch)
{
var value = 0;
var count = ch == 'U' ? 8 : 4;
for (var i = 0; i < count; i++)
{
var digit = Peek();
int digitValue;
if (digit >= '0' && digit <= '9')
{
digitValue = digit - '0';
}
else if (digit >= 'a' && digit <= 'f')
{
digitValue = digit - 'a' + 10;
}
else if (digit >= 'A' && digit <= 'F')
{
digitValue = digit - 'A' + 10;
}
else if (ch != 'x')
{
throw new InvalidOperationException($"Invalid unicode escape sequence at position {position}.");
}
else
{
break;
}
value = (value << 4) + digitValue;
Advance(1);
}
return (char)value;
}
private Token ScanCharacterLiteral()
{
Advance(1);
var buffer = new StringBuilder();
while (true)
{
var ch = Peek();
switch (ch)
{
case '\0':
throw new InvalidOperationException($"Unexpected end of character literal at position {position}.");
case '\\':
Advance(1);
buffer.Append(ScanEscapeSequence());
break;
case '\'':
Advance(1);
return new Token(TokenType.CharacterLiteral, buffer.ToString())
{
ValueKind = ValueKind.Character
};
default:
if (buffer.Length > 0)
{
throw new InvalidOperationException($"Too many characters in character literal at position {position}.");
}
buffer.Append(ch);
Advance(1);
break;
}
}
}
private Token ScanStringLiteral()
{
Advance(1);
var buffer = new StringBuilder();
while (true)
{
var ch = Peek();
switch (ch)
{
case '\0':
throw new InvalidOperationException($"Unexpected end of string literal at position {position}.");
case '\\':
Advance(1);
buffer.Append(ScanEscapeSequence());
break;
case '"':
Advance(1);
return new Token(TokenType.StringLiteral, buffer.ToString())
{
ValueKind = ValueKind.String
};
default:
buffer.Append(ch);
Advance(1);
break;
}
}
}
private Token ScanNumericLiteral()
{
var buffer = new StringBuilder();
var hasDecimal = false;
var hasFSuffix = false;
var hasDSuffix = false;
var hasMSuffix = false;
var hasLSuffix = false;
var hasExponent = false;
var hasHex = false;
var hasUSuffix = false;
while (true)
{
var ch = Peek();
if (ch == '0')
{
var next = Peek(1);
if (next == 'x' || next == 'X')
{
hasHex = true;
Advance(2);
continue;
}
}
if (ch >= '0' && ch <= '9')
{
buffer.Append(ch);
Advance(1);
continue;
}
if (ch == '.')
{
if (hasDecimal)
{
throw new InvalidOperationException($"Unexpected character '{ch}' at position {position}.");
}
hasDecimal = true;
buffer.Append(ch);
Advance(1);
continue;
}
if (ch == 'l' || ch == 'L')
{
if (hasLSuffix)
{
throw new InvalidOperationException($"Unexpected character '{ch}' at position {position}.");
}
hasLSuffix = true;
Advance(1);
continue;
}
if (ch == 'u' || ch == 'U')
{
if (hasUSuffix)
{
throw new InvalidOperationException($"Unexpected character '{ch}' at position {position}.");
}
hasUSuffix = true;
Advance(1);
continue;
}
if (hasHex && ((ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')))
{
buffer.Append(ch);
Advance(1);
continue;
}
if (ch == 'e' || ch == 'E')
{
if (hasExponent)
{
throw new InvalidOperationException($"Unexpected character '{ch}' at position {position}.");
}
hasExponent = true;
buffer.Append(ch);
Advance(1);
// Check for optional + or - after e/E
ch = Peek();
if (ch == '+' || ch == '-')
{
buffer.Append(ch);
Advance(1);
}
// Must have at least one digit after e/E
ch = Peek();
if (ch < '0' || ch > '9')
{
throw new InvalidOperationException($"Expected digit after exponent at position {position}.");
}
continue;
}
if (hasDecimal || hasExponent)
{
switch (ch)
{
case 'F':
case 'f':
hasFSuffix = true;
Advance(1);
break;
case 'D':
case 'd':
hasDSuffix = true;
Advance(1);
break;
case 'M':
case 'm':
hasMSuffix = true;
Advance(1);
break;
}
}
break;
}
var value = new Token(TokenType.NumericLiteral);
var valueKind = ValueKind.None;
if (hasDecimal || hasExponent)
{
valueKind = ValueKind.Double;
}
if (hasFSuffix)
{
valueKind = ValueKind.Float;
}
if (hasDSuffix)
{
valueKind = ValueKind.Double;
}
if (hasMSuffix)
{
valueKind = ValueKind.Decimal;
}
switch (valueKind)
{
case ValueKind.Float:
value.ValueKind = ValueKind.Float;
value.FloatValue = GetValueFloat(buffer.ToString());
break;
case ValueKind.Double:
value.ValueKind = ValueKind.Double;
value.DoubleValue = GetValueDouble(buffer.ToString());
break;
case ValueKind.Decimal:
value.ValueKind = ValueKind.Decimal;
value.DecimalValue = GetValueDecimal(buffer.ToString());
break;
default:
var val = GetValueUInt64(buffer.ToString(), hasHex);
if (!hasUSuffix && !hasLSuffix)
{
if (val <= Int32.MaxValue)
{
value.ValueKind = ValueKind.Int;
value.IntValue = (int)val;
}
else if (val <= UInt32.MaxValue)
{
value.ValueKind = ValueKind.UInt;
value.UintValue = (uint)val;
}
else if (val <= Int64.MaxValue)
{
value.ValueKind = ValueKind.Long;
value.LongValue = (long)val;
}
else
{
value.ValueKind = ValueKind.ULong;
value.UlongValue = val;
}
}
else if (hasUSuffix && !hasLSuffix)
{
if (val <= UInt32.MaxValue)
{
value.ValueKind = ValueKind.UInt;
value.UintValue = (uint)val;
}
else
{
value.ValueKind = ValueKind.ULong;
value.UlongValue = val;
}
}
else if (!hasUSuffix & hasLSuffix)
{
if (val <= Int64.MaxValue)
{
value.ValueKind = ValueKind.Long;
value.LongValue = (long)val;
}
else
{
value.ValueKind = ValueKind.ULong;
value.UlongValue = val;
}
}
else
{
value.ValueKind = ValueKind.ULong;
value.UlongValue = val;
}
break;
}
return value;
}
private static decimal GetValueDecimal(string text)
{
if (!decimal.TryParse(text, NumberStyles.AllowDecimalPoint | NumberStyles.AllowExponent, CultureInfo.InvariantCulture, out var result))
{
throw new InvalidOperationException($"Invalid numeric literal: {text}");
}
return result;
}
private static float GetValueFloat(string text)
{
if (!float.TryParse(text, NumberStyles.AllowDecimalPoint | NumberStyles.AllowExponent, CultureInfo.InvariantCulture, out var result))
{
throw new InvalidOperationException($"Invalid numeric literal: {text}");
}
return result;
}
private static double GetValueDouble(string text)
{
if (!double.TryParse(text, NumberStyles.AllowDecimalPoint | NumberStyles.AllowExponent, CultureInfo.InvariantCulture, out var result))
{
throw new InvalidOperationException($"Invalid numeric literal: {text}");
}
return result;
}
private static ulong GetValueUInt64(string text, bool isHex)
{
if (!UInt64.TryParse(text, isHex ? NumberStyles.AllowHexSpecifier : NumberStyles.None, CultureInfo.InvariantCulture, out var result))
{
throw new InvalidOperationException($"Invalid numeric literal: {text}");
}
return result;
}
private Token ScanIdentifier()
{
var startOffset = position;
while (true)
{
if (position == expression.Length)
{
var length = position - startOffset;
return new Token(TokenType.Identifier, expression.Substring(startOffset, length));
}
switch (Peek())
{
case '\0':
case ' ':
case '\r':
case '\n':
case '\t':
case '!':
case '%':
case '(':
case ')':
case '*':
case '+':
case ',':
case '-':
case '.':
case '/':
case ':':
case ';':
case '<':
case '=':
case '>':
case '?':
case '[':
case ']':
case '^':
case '{':
case '|':
case '}':
case '~':
case '"':
case '\'':
// All of the following characters are not valid in an
// identifier. If we see any of them, then we know we're
// done.
return new Token(TokenType.Identifier, expression[startOffset..position]);
case >= '0' and <= '9':
if (position == startOffset)
{
break;
}
else
{
goto case '_';
}
case (>= 'a' and <= 'z') or (>= 'A' and <= 'Z'):
case '_':
// All of these characters are valid inside an identifier.
// consume it and keep processing.
Advance(1);
continue;
default:
throw new InvalidOperationException($"Unexpected character '{Peek()}' at position {position}.");
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,462 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
/// <summary>
/// Serializes LINQ Expression Trees into C# string representations.
/// </summary>
public class ExpressionSerializer : ExpressionVisitor
{
private readonly StringBuilder _sb = new StringBuilder();
/// <summary>
/// Serializes a given LINQ Expression into a C# string.
/// </summary>
/// <param name="expression">The expression to serialize.</param>
/// <returns>A string representation of the expression.</returns>
public string Serialize(Expression expression)
{
_sb.Clear();
Visit(expression);
return _sb.ToString();
}
/// <inheritdoc/>
protected override Expression VisitLambda<T>(Expression<T> node)
{
if (node.Parameters.Count > 1)
{
_sb.Append("(");
for (int i = 0; i < node.Parameters.Count; i++)
{
if (i > 0) _sb.Append(", ");
_sb.Append(node.Parameters[i].Name);
}
_sb.Append(") => ");
}
else
{
_sb.Append(node.Parameters[0].Name);
_sb.Append(" => ");
}
Visit(node.Body);
return node;
}
/// <inheritdoc/>
protected override Expression VisitParameter(ParameterExpression node)
{
_sb.Append(node.Name);
return node;
}
/// <inheritdoc/>
protected override Expression VisitMember(MemberExpression node)
{
if (node.Expression != null)
{
Visit(node.Expression);
_sb.Append($".{node.Member.Name}");
}
else
{
_sb.Append(node.Member.Name);
}
return node;
}
/// <inheritdoc/>
protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (node.Method.IsStatic && node.Arguments.Count > 0 &&
(node.Method.DeclaringType == typeof(Enumerable) ||
node.Method.DeclaringType == typeof(Queryable)))
{
Visit(node.Arguments[0]);
_sb.Append($".{node.Method.Name}(");
for (int i = 1; i < node.Arguments.Count; i++)
{
if (i > 1) _sb.Append(", ");
if (node.Arguments[i] is NewArrayExpression arrayExpr)
{
VisitNewArray(arrayExpr);
}
else
{
Visit(node.Arguments[i]);
}
}
_sb.Append(")");
}
else if (node.Method.IsStatic)
{
_sb.Append($"{node.Method.DeclaringType.Name}.{node.Method.Name}(");
for (int i = 0; i < node.Arguments.Count; i++)
{
if (i > 0) _sb.Append(", ");
Visit(node.Arguments[i]);
}
_sb.Append(")");
}
else
{
if (node.Object != null)
{
Visit(node.Object);
_sb.Append($".{node.Method.Name}(");
}
else
{
_sb.Append($"{node.Method.Name}(");
}
for (int i = 0; i < node.Arguments.Count; i++)
{
if (i > 0) _sb.Append(", ");
Visit(node.Arguments[i]);
}
_sb.Append(")");
}
return node;
}
/// <inheritdoc/>
protected override Expression VisitUnary(UnaryExpression node)
{
if (node.NodeType == ExpressionType.Not)
{
_sb.Append("(!");
Visit(node.Operand);
_sb.Append(")");
}
else if (node.NodeType == ExpressionType.Convert)
{
if (node.Operand is IndexExpression indexExpr)
{
_sb.Append($"({node.Type.DisplayName(true).Replace("+",".")})");
Visit(indexExpr.Object);
_sb.Append("[");
Visit(indexExpr.Arguments[0]);
_sb.Append("]");
return node;
}
Visit(node.Operand);
}
else
{
_sb.Append(node.NodeType switch
{
ExpressionType.Negate => "-",
ExpressionType.UnaryPlus => "+",
_ => throw new NotSupportedException($"Unsupported unary operator: {node.NodeType}")
});
Visit(node.Operand);
}
return node;
}
/// <inheritdoc/>
protected override Expression VisitConstant(ConstantExpression node)
{
_sb.Append(FormatValue(node.Value));
return node;
}
private string FormatValue(object value)
{
if (value == null)
return "null";
return value switch
{
string str => $"\"{str}\"",
char c => $"'{c}'",
bool b => b.ToString().ToLowerInvariant(),
DateTime dt => $"DateTime.Parse(\"{dt.ToString("yyyy-MM-ddTHH:mm:ss.fffZ", CultureInfo.InvariantCulture)}\", CultureInfo.InvariantCulture)",
DateOnly dateOnly => $"DateOnly.Parse(\"{dateOnly.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)}\", CultureInfo.InvariantCulture)",
TimeOnly timeOnly => $"TimeOnly.Parse(\"{timeOnly.ToString("HH:mm:ss", CultureInfo.InvariantCulture)}\", CultureInfo.InvariantCulture)",
Guid guid => $"Guid.Parse(\"{guid.ToString("D", CultureInfo.InvariantCulture)}\")",
IEnumerable enumerable when value is not string => FormatEnumerable(enumerable),
_ => value.GetType().IsEnum
? $"({value.GetType().FullName.Replace("+", ".")})" + Convert.ChangeType(value, Enum.GetUnderlyingType(value.GetType()), CultureInfo.InvariantCulture).ToString()
: Convert.ToString(value, CultureInfo.InvariantCulture)
};
}
private string FormatEnumerable(IEnumerable enumerable)
{
var arrayType = enumerable.AsQueryable().ElementType;
var items = enumerable.Cast<object>().Select(FormatValue);
return $"new {(Nullable.GetUnderlyingType(arrayType) != null ? arrayType.DisplayName(true).Replace("+", ".") : "")}[] {{ {string.Join(", ", items)} }}";
}
/// <inheritdoc/>
protected override Expression VisitNewArray(NewArrayExpression node)
{
bool needsParentheses = node.NodeType == ExpressionType.NewArrayInit &&
(node.Expressions.Count > 1 || node.Expressions[0].NodeType != ExpressionType.Constant);
if (needsParentheses) _sb.Append("(");
_sb.Append("new [] { ");
bool first = true;
foreach (var expr in node.Expressions)
{
if (!first) _sb.Append(", ");
first = false;
Visit(expr);
}
_sb.Append(" }");
if (needsParentheses) _sb.Append(")");
return node;
}
/// <inheritdoc/>
protected override Expression VisitBinary(BinaryExpression node)
{
_sb.Append("(");
Visit(node.Left);
_sb.Append($" {GetOperator(node.NodeType)} ");
Visit(node.Right);
_sb.Append(")");
return node;
}
/// <inheritdoc/>
protected override Expression VisitConditional(ConditionalExpression node)
{
_sb.Append("(");
Visit(node.Test);
_sb.Append(" ? ");
Visit(node.IfTrue);
_sb.Append(" : ");
Visit(node.IfFalse);
_sb.Append(")");
return node;
}
/// <summary>
/// Maps an ExpressionType to its corresponding C# operator.
/// </summary>
/// <param name="type">The ExpressionType to map.</param>
/// <returns>A string representation of the corresponding C# operator.</returns>
private static string GetOperator(ExpressionType type)
{
return type switch
{
ExpressionType.Add => "+",
ExpressionType.Subtract => "-",
ExpressionType.Multiply => "*",
ExpressionType.Divide => "/",
ExpressionType.AndAlso => "&&",
ExpressionType.OrElse => "||",
ExpressionType.Equal => "==",
ExpressionType.NotEqual => "!=",
ExpressionType.LessThan => "<",
ExpressionType.LessThanOrEqual => "<=",
ExpressionType.GreaterThan => ">",
ExpressionType.GreaterThanOrEqual => ">=",
ExpressionType.Coalesce => "??",
_ => throw new NotSupportedException($"Unsupported operator: {type}")
};
}
}
/// <summary>
/// Provides an extension method for displaying type names.
/// </summary>
public static class SharedTypeExtensions
{
private static readonly Dictionary<Type, string> BuiltInTypeNames = new()
{
{ typeof(bool), "bool" },
{ typeof(byte), "byte" },
{ typeof(char), "char" },
{ typeof(decimal), "decimal" },
{ typeof(double), "double" },
{ typeof(float), "float" },
{ typeof(int), "int" },
{ typeof(long), "long" },
{ typeof(object), "object" },
{ typeof(sbyte), "sbyte" },
{ typeof(short), "short" },
{ typeof(string), "string" },
{ typeof(uint), "uint" },
{ typeof(ulong), "ulong" },
{ typeof(ushort), "ushort" },
{ typeof(void), "void" }
};
/// <summary>
/// Unwraps nullable type.
/// </summary>
public static Type UnwrapNullableType(this Type type)
=> Nullable.GetUnderlyingType(type) ?? type;
/// <summary>
/// Returns a display name for the given type.
/// </summary>
/// <param name="type">The type to display.</param>
/// <param name="fullName">Indicates whether to use the full name.</param>
/// <param name="compilable">Indicates whether to use a compilable format.</param>
/// <returns>A string representing the type name.</returns>
public static string DisplayName(this Type type, bool fullName = true, bool compilable = false)
{
var stringBuilder = new StringBuilder();
ProcessType(stringBuilder, type, fullName, compilable);
return stringBuilder.ToString();
}
private static void ProcessType(StringBuilder builder, Type type, bool fullName, bool compilable)
{
if (type.IsGenericType)
{
var genericArguments = type.GetGenericArguments();
ProcessGenericType(builder, type, genericArguments, genericArguments.Length, fullName, compilable);
}
else if (type.IsArray)
{
ProcessArrayType(builder, type, fullName, compilable);
}
else if (BuiltInTypeNames.TryGetValue(type, out var builtInName))
{
builder.Append(builtInName);
}
else if (!type.IsGenericParameter)
{
if (compilable)
{
if (type.IsNested)
{
ProcessType(builder, type.DeclaringType!, fullName, compilable);
builder.Append('.');
}
else if (fullName)
{
builder.Append(type.Namespace).Append('.');
}
builder.Append(type.Name);
}
else
{
builder.Append(fullName ? type.FullName : type.Name);
}
}
}
private static void ProcessArrayType(StringBuilder builder, Type type, bool fullName, bool compilable)
{
var innerType = type;
while (innerType.IsArray)
{
innerType = innerType.GetElementType()!;
}
ProcessType(builder, innerType, fullName, compilable);
while (type.IsArray)
{
builder.Append('[');
builder.Append(',', type.GetArrayRank() - 1);
builder.Append(']');
type = type.GetElementType()!;
}
}
private static void ProcessGenericType(
StringBuilder builder,
Type type,
Type[] genericArguments,
int length,
bool fullName,
bool compilable)
{
if (type.IsConstructedGenericType
&& type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
ProcessType(builder, type.UnwrapNullableType(), fullName, compilable);
builder.Append('?');
return;
}
var offset = type.IsNested ? type.DeclaringType!.GetGenericArguments().Length : 0;
if (compilable)
{
if (type.IsNested)
{
ProcessType(builder, type.DeclaringType!, fullName, compilable);
builder.Append('.');
}
else if (fullName)
{
builder.Append(type.Namespace);
builder.Append('.');
}
}
else
{
if (fullName)
{
if (type.IsNested)
{
ProcessGenericType(builder, type.DeclaringType!, genericArguments, offset, fullName, compilable);
builder.Append('+');
}
else
{
builder.Append(type.Namespace);
builder.Append('.');
}
}
}
var genericPartIndex = type.Name.IndexOf('`');
if (genericPartIndex <= 0)
{
builder.Append(type.Name);
return;
}
builder.Append(type.Name, 0, genericPartIndex);
builder.Append('<');
for (var i = offset; i < length; i++)
{
ProcessType(builder, genericArguments[i], fullName, compilable);
if (i + 1 == length)
{
continue;
}
builder.Append(',');
if (!genericArguments[i + 1].IsGenericParameter)
{
builder.Append(' ');
}
}
builder.Append('>');
}
}

View File

@@ -187,7 +187,7 @@ namespace Radzen.Blazor
if (Visible)
{
JSRuntime.InvokeVoidAsync("Radzen.destroyGauge", Element);
JSRuntime.InvokeVoid("Radzen.destroyGauge", Element);
}
}

View File

@@ -0,0 +1,24 @@
using System;
using System.Threading.Tasks;
using Microsoft.JSInterop;
namespace Radzen;
static class JSRuntimeExtensions
{
public static void InvokeVoid(this IJSRuntime jsRuntime, string identifier, params object[] args)
{
_ = jsRuntime.InvokeVoidAsync(identifier, args).FireAndForget();
}
private static async ValueTask FireAndForget(this ValueTask task)
{
try
{
await task;
}
catch (Exception)
{
}
}
}

View File

@@ -0,0 +1,39 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
namespace Radzen.Blazor;
static class NonRenderingExtensions
{
public static Action AsNonRenderingEventHandler(this ComponentBase _, Action callback)
=> new SyncReceiver(callback).Invoke;
public static Action<TValue> AsNonRenderingEventHandler<TValue>(this Action<TValue> callback) => new SyncReceiver<TValue>(callback).Invoke;
public static Func<Task> AsNonRenderingEventHandler(this ComponentBase _, Func<Task> callback) => new AsyncReceiver(callback).Invoke;
public static Func<TValue, Task> AsNonRenderingEventHandler<TValue>(this ComponentBase _, Func<TValue, Task> callback) => new AsyncReceiver<TValue>(callback).Invoke;
private record SyncReceiver(Action callback) : ReceiverBase
{
public void Invoke() => callback();
}
private record SyncReceiver<T>(Action<T> callback) : ReceiverBase
{
public void Invoke(T arg) => callback(arg);
}
private record AsyncReceiver(Func<Task> callback) : ReceiverBase
{
public Task Invoke() => callback();
}
private record AsyncReceiver<T>(Func<T, Task> callback) : ReceiverBase
{
public Task Invoke(T arg) => callback(arg);
}
private record ReceiverBase : IHandleEvent
{
public Task HandleEventAsync(EventCallbackWorkItem item, object arg) => item.InvokeAsync(arg);
}
}

View File

@@ -71,7 +71,7 @@ namespace Radzen
var parameter = Expression.Parameter(source.ElementType, "x");
return GroupByMany(source,
properties.Select(p => Expression.Lambda<Func<T, object>>(Expression.Convert(GetNestedPropertyExpression(parameter, p), typeof(object)), parameter).Compile()).ToArray(),
properties.Select(p => Expression.Lambda<Func<T, object>>(Expression.Convert(GetNestedPropertyExpression(parameter, p), typeof(object)), parameter).Compile()).ToArray(),
0);
}
@@ -197,8 +197,8 @@ namespace Radzen
/// <returns>An <see cref="IQueryable"/> that contains each element of the source sequence converted to the specified type.</returns>
public static IQueryable Cast(this IQueryable source, Type type)
{
return source.Provider.CreateQuery(Expression.Call(null,
typeof(Queryable).GetTypeInfo().GetDeclaredMethods(nameof(Queryable.Cast)).FirstOrDefault(mi => mi.IsGenericMethod).MakeGenericMethod(type),
return source.Provider.CreateQuery(Expression.Call(null,
typeof(Queryable).GetTypeInfo().GetDeclaredMethods(nameof(Queryable.Cast)).FirstOrDefault(mi => mi.IsGenericMethod).MakeGenericMethod(type),
source.Expression));
}
@@ -234,7 +234,7 @@ namespace Radzen
/// <summary>
/// Filters using the specified filter descriptors.
/// </summary>
public static IQueryable<T> Where<T>(this IQueryable<T> source, IEnumerable<FilterDescriptor> filters,
public static IQueryable<T> Where<T>(this IQueryable<T> source, IEnumerable<FilterDescriptor> filters,
LogicalFilterOperator logicalFilterOperator, FilterCaseSensitivity filterCaseSensitivity)
{
if (filters == null || !filters.Any())
@@ -519,35 +519,6 @@ namespace Radzen
return (IList)genericToList.Invoke(null, new[] { query });
}
static string EnumerableAsString(IQueryable enumerableValue, string baseType, object value)
{
Func<IQueryable, IEnumerable<object>> values = (items) => {
if (items.ElementType == typeof(string))
{
return items.Cast<string>().Select(i => $@"""{i}""");
}
else if (items.ElementType == typeof(bool) || items.ElementType == typeof(bool?))
{
return items.Cast<object>().Select(i => $@"{i}".ToLower());
}
else if (PropertyAccess.IsDate(items.ElementType))
{
return items.Cast<object>().Select(i => $@"DateTime.Parse(""{i}"")");
}
else if (PropertyAccess.IsEnum(items.ElementType) || PropertyAccess.IsNullableEnum(items.ElementType))
{
return items.Cast<object>().Select(i => i != null ? Convert.ChangeType(i, typeof(int)) : null);
}
return items.Cast<object>();
};
var finalValues = value != null && !(IsEnumerable(value.GetType()) && !(value is string)) ? new object[] { value }.AsQueryable().Cast(value.GetType()) : enumerableValue;
return "new " + baseType + "[]{" + String.Join(",", values(finalValues)) + "}";
}
/// <summary>
/// Converts to filterstring.
/// </summary>
@@ -556,203 +527,78 @@ namespace Radzen
/// <returns>System.String.</returns>
public static string ToFilterString<T>(this IEnumerable<RadzenDataGridColumn<T>> columns)
{
var columnsWithFilter = GetFilterableColumns(columns);
Func<RadzenDataGridColumn<T>, bool> canFilter = (c) => c.Filterable && c.FilterPropertyType != null &&
(!(c.GetFilterValue() == null || c.GetFilterValue() as string == string.Empty)
|| c.GetFilterOperator() == FilterOperator.IsNotNull || c.GetFilterOperator() == FilterOperator.IsNull
|| c.GetFilterOperator() == FilterOperator.IsEmpty || c.GetFilterOperator() == FilterOperator.IsNotEmpty)
&& c.GetFilterProperty() != null;
if (columnsWithFilter.Any())
Func<RadzenDataGridColumn<T>, bool> canFilterCustom = (c) => c.Filterable && c.FilterPropertyType != null &&
(c.GetFilterOperator() == FilterOperator.Custom && c.GetCustomFilterExpression() != null)
&& c.GetFilterProperty() != null;
var columnsToFilter = columns.Where(canFilter);
var grid = columns.FirstOrDefault()?.Grid;
var gridLogicalFilterOperator = grid != null ? grid.LogicalFilterOperator : LogicalFilterOperator.And;
var gridFilterCaseSensitivity = grid != null ? grid.FilterCaseSensitivity : FilterCaseSensitivity.Default;
var serializer = new ExpressionSerializer();
var filterExpression = "";
if (columnsToFilter.Any())
{
var gridLogicalFilterOperator = columns.FirstOrDefault()?.Grid?.LogicalFilterOperator;
var gridBooleanOperator = gridLogicalFilterOperator == LogicalFilterOperator.And ? "&&" : "||";
var whereList = new List<string>();
foreach (var column in columnsWithFilter)
var filters = columnsToFilter.Select(c => new FilterDescriptor()
{
string value = "";
string secondValue = "";
Property = c.Property,
FilterProperty = c.FilterProperty,
Type = c.FilterPropertyType,
FilterValue = c.GetFilterValue(),
FilterOperator = c.GetFilterOperator(),
SecondFilterValue = c.GetSecondFilterValue(),
SecondFilterOperator = c.GetSecondFilterOperator(),
LogicalFilterOperator = c.GetLogicalFilterOperator()
});
var v = column.GetFilterValue();
var sv = column.GetSecondFilterValue();
if (filters.Any())
{
var parameter = Expression.Parameter(typeof(T), "x");
Expression combinedExpression = null;
if (column.GetFilterOperator() == FilterOperator.Custom)
foreach (var filter in filters)
{
var customFilterExpression = column.GetCustomFilterExpression();
if (!string.IsNullOrEmpty(customFilterExpression))
{
whereList.Add(customFilterExpression);
}
}
else if (v != null && IsEnumerable(v.GetType()) && v.GetType() != typeof(string) || IsEnumerable(column.FilterPropertyType) && column.FilterPropertyType != typeof(string))
{
var enumerableValue = ((IEnumerable)(v != null ? v : Enumerable.Empty<object>())).AsQueryable();
var enumerableSecondValue = ((IEnumerable)(sv != null ? sv : Enumerable.Empty<object>())).AsQueryable();
var expression = GetExpression<T>(parameter, filter, gridFilterCaseSensitivity, filter.Type);
if (expression == null) continue;
string baseType = column.FilterPropertyType.GetGenericArguments().Count() == 1
? column.FilterPropertyType.GetGenericArguments()[0].Name
: "";
if (column.Property != column.FilterProperty)
{
baseType = "";
}
var enumerableValueAsString = EnumerableAsString(enumerableValue, baseType, v);
var enumerableSecondValueAsString = EnumerableAsString(enumerableSecondValue, baseType, sv);
if (enumerableValue?.Cast<object>().Any() == true)
{
var columnFilterOperator = column.GetFilterOperator();
var columnSecondFilterOperator = column.GetSecondFilterOperator();
var linqOperator = LinqFilterOperators[column.GetFilterOperator()];
if (linqOperator == null)
{
linqOperator = "==";
}
var booleanOperator = column.LogicalFilterOperator == LogicalFilterOperator.And ? "&&" : "||";
var filterProperty = column.GetFilterProperty();
var itemInstanceName = !filterProperty.Contains("[") ? "it." : "";
var property = itemInstanceName + PropertyAccess.GetProperty(column.GetFilterProperty());
if (sv == null)
{
if (columnFilterOperator == FilterOperator.Contains || columnFilterOperator == FilterOperator.DoesNotContain)
{
if (column.GetFilterValue() is string && column.Property != column.FilterProperty && !string.IsNullOrEmpty(column.FilterProperty))
{
whereList.Add($@"{(columnFilterOperator == FilterOperator.DoesNotContain ? "! " : "")}{itemInstanceName + column.Property}.Any(i => {"i." + column.FilterProperty}.Contains(""" + column.GetFilterValue() + "\"))");
}
else if (IsEnumerable(column.FilterPropertyType) && column.FilterPropertyType != typeof(string) &&
IsEnumerable(column.PropertyType) && column.PropertyType != typeof(string))
{
whereList.Add($@"{(columnFilterOperator == FilterOperator.DoesNotContain ? "! " : "")}({enumerableValueAsString}).Contains({property})");
}
else if (IsEnumerable(column.FilterPropertyType) && column.FilterPropertyType != typeof(string) &&
column.Property != column.FilterProperty && !string.IsNullOrEmpty(column.FilterProperty))
{
whereList.Add($@"({property}).{(columnFilterOperator == FilterOperator.NotIn ? "Except" : "Intersect")}({enumerableValueAsString}).Any()");
}
else
{
whereList.Add($@"{(columnFilterOperator == FilterOperator.DoesNotContain ? "! " : "")}({enumerableValueAsString}).Contains({property})");
}
}
else if (columnFilterOperator == FilterOperator.In || columnFilterOperator == FilterOperator.NotIn)
{
if (IsEnumerable(column.FilterPropertyType) && column.FilterPropertyType != typeof(string) &&
IsEnumerable(column.PropertyType) && column.PropertyType != typeof(string))
{
whereList.Add($@"{(columnFilterOperator == FilterOperator.NotIn ? "!" : "")}{property}.Any(i => ({enumerableValueAsString}).Contains(i))");
}
else if (IsEnumerable(column.FilterPropertyType) && column.FilterPropertyType != typeof(string) &&
column.Property != column.FilterProperty && !string.IsNullOrEmpty(column.FilterProperty))
{
whereList.Add($@"{(columnFilterOperator == FilterOperator.NotIn ? "!" : "")}{itemInstanceName + column.Property}.Any(i => ({enumerableValueAsString}).Contains(i.{column.FilterProperty}))");
}
}
}
else
{
if ((columnFilterOperator == FilterOperator.Contains || columnFilterOperator == FilterOperator.DoesNotContain) &&
(columnSecondFilterOperator == FilterOperator.Contains || columnSecondFilterOperator == FilterOperator.DoesNotContain))
{
whereList.Add($@"{(columnFilterOperator == FilterOperator.DoesNotContain ? "!" : "")}({enumerableValueAsString}).Contains({property}) {booleanOperator} {(columnSecondFilterOperator == FilterOperator.DoesNotContain ? "!" : "")}({enumerableSecondValueAsString}).Contains({property})");
}
else if ((columnFilterOperator == FilterOperator.In || columnFilterOperator == FilterOperator.NotIn) &&
(columnSecondFilterOperator == FilterOperator.In || columnSecondFilterOperator == FilterOperator.NotIn))
{
whereList.Add($@"({property}).{(columnFilterOperator == FilterOperator.NotIn ? "Except" : "Intersect")}({enumerableValueAsString}).Any() {booleanOperator} ({property}).{(columnSecondFilterOperator == FilterOperator.NotIn ? "Except" : "Intersect")}({enumerableSecondValueAsString}).Any()");
}
}
}
}
else if (column.FilterPropertyType == typeof(TimeOnly) || column.FilterPropertyType == typeof(TimeOnly?))
{
value = v != null ? ((TimeOnly)v).ToString("HH:mm:ss") : "";
secondValue = sv != null ? ((TimeOnly)sv).ToString("HH:mm:ss") : "";
}
else if (column.FilterPropertyType == typeof(Guid) || column.FilterPropertyType == typeof(Guid?))
{
value = $"{v}";
secondValue = $"{sv}";
}
else if (PropertyAccess.IsDate(column.FilterPropertyType))
{
if (v != null)
{
value =
v is DateTime ? ((DateTime)v).ToString("yyyy-MM-ddTHH:mm:ss.fffZ", CultureInfo.InvariantCulture)
: v is DateTimeOffset ? ((DateTimeOffset)v).UtcDateTime.ToString("yyyy-MM-ddTHH:mm:ss.fffZ", CultureInfo.InvariantCulture)
:
#if NET6_0_OR_GREATER
v is DateOnly ? ((DateOnly)v).ToString("yyy-MM-dd", CultureInfo.InvariantCulture) : "";
#else
"";
#endif
}
if (sv != null)
{
secondValue =
sv is DateTime ? ((DateTime)sv).ToString("yyyy-MM-ddTHH:mm:ss.fffZ", CultureInfo.InvariantCulture)
: sv is DateTimeOffset ? ((DateTimeOffset)sv).UtcDateTime.ToString("yyyy-MM-ddTHH:mm:ss.fffZ", CultureInfo.InvariantCulture)
:
#if NET6_0_OR_GREATER
sv is DateOnly ? ((DateOnly)sv).ToString("yyy-MM-dd", CultureInfo.InvariantCulture) : "";
#else
"";
#endif
}
}
else if (!(v != null && IsEnumerable(v.GetType())) && (PropertyAccess.IsEnum(column.FilterPropertyType) || PropertyAccess.IsNullableEnum(column.FilterPropertyType)))
{
Type enumType = Enum.GetUnderlyingType(Nullable.GetUnderlyingType(column.FilterPropertyType) ?? column.FilterPropertyType);
if (v != null)
{
value = Convert.ChangeType(v, enumType).ToString();
}
if (sv != null)
{
secondValue = Convert.ChangeType(sv, enumType).ToString();
}
}
else
{
value = (string)Convert.ChangeType(column.GetFilterValue(), typeof(string), CultureInfo.InvariantCulture);
secondValue = (string)Convert.ChangeType(column.GetSecondFilterValue(), typeof(string), CultureInfo.InvariantCulture);
combinedExpression = combinedExpression == null
? expression
: gridLogicalFilterOperator == LogicalFilterOperator.And ?
Expression.AndAlso(combinedExpression, expression) :
Expression.OrElse(combinedExpression, expression);
}
if (!string.IsNullOrEmpty(value) || column.GetFilterOperator() == FilterOperator.IsNotNull
|| column.GetFilterOperator() == FilterOperator.IsNull
|| column.GetFilterOperator() == FilterOperator.IsEmpty
|| column.GetFilterOperator() == FilterOperator.IsNotEmpty)
if (combinedExpression != null)
{
var linqOperator = LinqFilterOperators[column.GetFilterOperator()];
if (linqOperator == null)
{
linqOperator = "==";
}
var booleanOperator = column.LogicalFilterOperator == LogicalFilterOperator.And ? "&&" : "||";
if (string.IsNullOrEmpty(secondValue))
{
whereList.Add(GetColumnFilter(column, value));
}
else
{
whereList.Add($"({GetColumnFilter(column, value)} {booleanOperator} {GetColumnFilter(column, secondValue, true)})");
}
filterExpression = serializer.Serialize(Expression.Lambda<Func<T, bool>>(combinedExpression, parameter));
}
}
return whereList.Any() ?
"it => " + string.Join($" {gridBooleanOperator} ", whereList.Where(i => !string.IsNullOrEmpty(i))) : "";
}
return "";
var columnsWithCustomFilter = columns.Where(canFilterCustom);
var customFilterExpression = "";
if (columnsToFilter.Any())
{
var expressions = columnsWithCustomFilter.Select(c => (c.GetCustomFilterExpression() ?? "").Replace(" or ", " || ").Replace(" and ", " && ")).Where(e => !string.IsNullOrEmpty(e)).ToList();
customFilterExpression = string.Join($"{(gridLogicalFilterOperator == LogicalFilterOperator.And ? " && " : " || ")}", expressions);
return !string.IsNullOrEmpty(filterExpression) && !string.IsNullOrEmpty(customFilterExpression) ?
$"{filterExpression} {(gridLogicalFilterOperator == LogicalFilterOperator.And ? " && " : " || ")} {customFilterExpression}" :
!string.IsNullOrEmpty(customFilterExpression) ? "it => " + customFilterExpression : filterExpression;
}
return filterExpression;
}
/// <summary>
@@ -764,377 +610,38 @@ namespace Radzen
public static string ToFilterString<T>(this RadzenDataFilter<T> dataFilter)
{
Func<CompositeFilterDescriptor, bool> canFilter = (c) => dataFilter.properties.Where(col => col.Property == c.Property).FirstOrDefault()?.FilterPropertyType != null &&
(!(c.FilterValue == null || c.FilterValue as string == string.Empty)
|| c.FilterOperator == FilterOperator.IsNotNull || c.FilterOperator == FilterOperator.IsNull
|| c.FilterOperator == FilterOperator.IsEmpty || c.FilterOperator == FilterOperator.IsNotEmpty)
&& c.Property != null;
(!(c.FilterValue == null || c.FilterValue as string == string.Empty)
|| c.FilterOperator == FilterOperator.IsNotNull || c.FilterOperator == FilterOperator.IsNull
|| c.FilterOperator == FilterOperator.IsEmpty || c.FilterOperator == FilterOperator.IsNotEmpty)
&& c.Property != null;
if (dataFilter.Filters.Concat(dataFilter.Filters.SelectManyRecursive(i => i.Filters ?? Enumerable.Empty<CompositeFilterDescriptor>())).Where(canFilter).Any())
{
return CompositeFilterToFilterString<T>(dataFilter.Filters, dataFilter, dataFilter.LogicalFilterOperator);
}
return "";
}
var serializer = new ExpressionSerializer();
/// <summary>
/// Creates a Linq-compatible filter string for a list of CompositeFilterDescriptions
/// </summary>
/// <typeparam name="T">The type that is being filtered</typeparam>
/// <param name="filters">The list if filters</param>
/// <param name="Datafilter">The RadzenDataFilter component</param>
/// <param name="filterOperator">Whether filter elements should be and-ed or or-ed</param>
/// <returns></returns>
private static string CompositeFilterToFilterString<T>(IEnumerable<CompositeFilterDescriptor> filters, RadzenDataFilter<T> Datafilter, LogicalFilterOperator filterOperator)
{
if (filters.Any())
{
var LogicalFilterOperator = filterOperator;
var BooleanOperator = LogicalFilterOperator == LogicalFilterOperator.And ? "&&" : "||";
var filterExpressions = new List<Expression>();
var whereList = new List<string>();
foreach (var column in filters)
var parameter = Expression.Parameter(typeof(T), "x");
foreach (var filter in dataFilter.Filters)
{
if (column.Filters is not null && column.Filters.Any())
{
whereList.Add($"({CompositeFilterToFilterString(column.Filters, Datafilter, column.LogicalFilterOperator)})");
}
if (column.Property is not null)
{
whereList.Add($"{GetColumnFilter(Datafilter, column, Datafilter.FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive)}");
}
AddWhereExpression<T>(parameter, filter, ref filterExpressions, dataFilter.FilterCaseSensitivity);
}
return string.Join($" {BooleanOperator} ", whereList.Where(i => !string.IsNullOrEmpty(i)));
}
Expression combinedExpression = null;
return "";
foreach (var expression in filterExpressions)
{
combinedExpression = combinedExpression == null
? expression
: dataFilter.LogicalFilterOperator == LogicalFilterOperator.And ?
Expression.AndAlso(combinedExpression, expression) :
Expression.OrElse(combinedExpression, expression);
}
}
/// <summary>
/// Create a single linq-compatible filter string for one datafilter element (includes sub-lists)
/// </summary>
/// <typeparam name="T">The type that is being filtered</typeparam>
/// <param name="dataFilter">The RadzenDataFilter component</param>
/// <param name="column">The filter elements for which to create the filter string</param>
/// <param name="caseSensitive">Whether filtering is case sensitive or not</param>
/// <returns></returns>
private static string GetColumnFilter<T>(RadzenDataFilter<T> dataFilter, CompositeFilterDescriptor column, bool caseSensitive)
{
var property = column.Property;
if (property.Contains(".", StringComparison.CurrentCulture))
{
property = $"({property})";
}
var columnInfo = dataFilter.properties.Where(c => c.Property == column.Property).FirstOrDefault();
if (columnInfo == null) return "";
var columnType = columnInfo.FilterPropertyType;
var columnFilterOperator = column.FilterOperator;
var linqOperator = LinqFilterOperators[columnFilterOperator.Value];
linqOperator ??= "==";
var value = "";
if (columnType == typeof(string))
{
value = (string)Convert.ChangeType(column.FilterValue, typeof(string));
value = value?.Replace("\"", "\\\"");
string filterCaseSensitivityOperator = caseSensitive ? ".ToLower()" : "";
if (!string.IsNullOrEmpty(value) && column.FilterOperator == FilterOperator.Contains)
if (combinedExpression != null)
{
return $@"({property} == null ? """" : {property}){filterCaseSensitivityOperator}.Contains(""{value}""{filterCaseSensitivityOperator})";
}
else if (!string.IsNullOrEmpty(value) && columnFilterOperator == FilterOperator.DoesNotContain)
{
return $@"!({property} == null ? """" : {property}){filterCaseSensitivityOperator}.Contains(""{value}""{filterCaseSensitivityOperator})";
}
else if (!string.IsNullOrEmpty(value) && columnFilterOperator == FilterOperator.StartsWith)
{
return $@"({property} == null ? """" : {property}){filterCaseSensitivityOperator}.StartsWith(""{value}""{filterCaseSensitivityOperator})";
}
else if (!string.IsNullOrEmpty(value) && columnFilterOperator == FilterOperator.EndsWith)
{
return $@"({property} == null ? """" : {property}){filterCaseSensitivityOperator}.EndsWith(""{value}""{filterCaseSensitivityOperator})";
}
else if (!string.IsNullOrEmpty(value) && columnFilterOperator == FilterOperator.Equals)
{
return $@"({property} == null ? """" : {property}){filterCaseSensitivityOperator} == ""{value}""{filterCaseSensitivityOperator}";
}
else if (!string.IsNullOrEmpty(value) && columnFilterOperator == FilterOperator.NotEquals)
{
return $@"({property} == null ? """" : {property}){filterCaseSensitivityOperator} != ""{value}""{filterCaseSensitivityOperator}";
}
else if (columnFilterOperator == FilterOperator.IsNull)
{
return property + " == null";
}
else if (columnFilterOperator == FilterOperator.IsEmpty)
{
return property + @" == """"";
}
else if (columnFilterOperator == FilterOperator.IsNotEmpty)
{
return property + @" != """"";
}
else if (columnFilterOperator == FilterOperator.IsNotNull)
{
return property + @" != null";
}
}
else if (PropertyAccess.IsNumeric(columnType))
{
value = (string)Convert.ChangeType(column.FilterValue, typeof(string));
if (columnFilterOperator == FilterOperator.IsNull || columnFilterOperator == FilterOperator.IsNotNull)
{
return $"{property} {linqOperator} null";
}
else if (columnFilterOperator == FilterOperator.IsEmpty || columnFilterOperator == FilterOperator.IsNotEmpty)
{
return $@"{property} {linqOperator} """"";
}
else
{
return $"{property} {linqOperator} {value}";
}
}
else if (columnType == typeof(bool) || columnType == typeof(bool?))
{
value = (string)Convert.ChangeType(column.FilterValue, typeof(string));
return $"{property} {linqOperator} {(columnFilterOperator == FilterOperator.IsNull || columnFilterOperator == FilterOperator.IsNotNull ? "null" : value)}";
}
else if (PropertyAccess.IsDate(columnType))
{
var v = column.FilterValue;
if (v != null)
{
value = $@"DateTime(""{(v is DateTime time ? time.ToString("yyyy-MM-ddTHH:mm:ss.fffZ", CultureInfo.InvariantCulture) : v is DateTimeOffset offset ? offset.UtcDateTime.ToString("yyyy-MM-ddTHH:mm:ss.fffZ", CultureInfo.InvariantCulture) : "")}"")";
}
}
else if (PropertyAccess.IsEnum(columnType) || PropertyAccess.IsNullableEnum(columnType))
{
var v = column.FilterValue;
if (v != null)
{
value = ((int)v).ToString();
}
}
else if (IsEnumerable(columnType) && columnType != typeof(string))
{
var v = column.FilterValue;
var enumerableValue = ((IEnumerable)(v ?? Enumerable.Empty<object>())).AsQueryable();
var enumerableValueAsString = "(" + String.Join(",",
(enumerableValue.ElementType == typeof(string) ?
enumerableValue.Cast<string>().Select(i => $@"""{i}""").Cast<object>()
: PropertyAccess.IsDate(enumerableValue.ElementType) ?
enumerableValue.Cast<object>().Select(i => $@"DateTime(""{i}"")").Cast<object>()
: enumerableValue.Cast<object>())) + ")";
if (enumerableValue?.Cast<object>().Any() == true)
{
if (property.Contains("."))
{
property = $"({property})";
}
if (columnFilterOperator == FilterOperator.Contains || columnFilterOperator == FilterOperator.DoesNotContain)
{
return $@"{property} {(columnFilterOperator == FilterOperator.DoesNotContain ? "not " : "in")} {enumerableValueAsString}";
}
else if (columnFilterOperator == FilterOperator.In || columnFilterOperator == FilterOperator.NotIn)
{
return $@"({property}).{(columnFilterOperator == FilterOperator.NotIn ? "Except" : "Intersect")}({enumerableValueAsString}).Any()";
}
}
}
else
{
value = (string)Convert.ChangeType(column.FilterValue, typeof(string), CultureInfo.InvariantCulture);
}
if (!string.IsNullOrEmpty(value) || column.FilterOperator == FilterOperator.IsNotNull
|| column.FilterOperator == FilterOperator.IsNull
|| column.FilterOperator == FilterOperator.IsEmpty
|| column.FilterOperator == FilterOperator.IsNotEmpty)
{
return $"({property} {linqOperator} {(columnFilterOperator == FilterOperator.IsNull || columnFilterOperator == FilterOperator.IsNotNull ? "null" : value)})";
}
return "";
}
/// <summary>
/// Gets the column filter.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="column">The column.</param>
/// <param name="value">The value.</param>
/// <param name="second">if set to <c>true</c> [second].</param>
/// <returns>System.String.</returns>
private static string GetColumnFilter<T>(RadzenDataGridColumn<T> column, string value, bool second = false)
{
var filterProperty = column.GetFilterProperty();
var itemInstanceName = !filterProperty.Contains("[") ? "it." : "";
var property = itemInstanceName + PropertyAccess.GetProperty(filterProperty);
var propertyType = !string.IsNullOrEmpty(property) ? PropertyAccess.GetPropertyType(typeof(T), property) : null;
string npProperty = (propertyType != null ? Nullable.GetUnderlyingType(propertyType) != null : true) ? $@"({property} ?? null)" : property;
var columnFilterOperator = !second ? column.GetFilterOperator() : column.GetSecondFilterOperator();
if (columnFilterOperator == FilterOperator.Custom)
{
return "";
}
var linqOperator = LinqFilterOperators[columnFilterOperator];
if (linqOperator == null)
{
linqOperator = "==";
}
bool isDateOnly = false;
#if NET6_0_OR_GREATER
if (column.FilterPropertyType == typeof(DateOnly) || column.FilterPropertyType == typeof(DateOnly?))
{
isDateOnly = true;
}
#endif
if (column.FilterPropertyType == typeof(string))
{
string filterCaseSensitivityOperator = column.Grid.FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive ? ".ToLower()" : "";
value = value?.Replace("\"", "\\\"");
if (!string.IsNullOrEmpty(value) && columnFilterOperator == FilterOperator.Contains)
{
return $@"({property} == null ? """" : {property}){filterCaseSensitivityOperator}.Contains(""{value}""{filterCaseSensitivityOperator})";
}
else if (!string.IsNullOrEmpty(value) && columnFilterOperator == FilterOperator.DoesNotContain)
{
return $@"!({property} == null ? """" : {property}){filterCaseSensitivityOperator}.Contains(""{value}""{filterCaseSensitivityOperator})";
}
else if (!string.IsNullOrEmpty(value) && columnFilterOperator == FilterOperator.StartsWith)
{
return $@"({property} == null ? """" : {property}){filterCaseSensitivityOperator}.StartsWith(""{value}""{filterCaseSensitivityOperator})";
}
else if (!string.IsNullOrEmpty(value) && columnFilterOperator == FilterOperator.EndsWith)
{
return $@"({property} == null ? """" : {property}){filterCaseSensitivityOperator}.EndsWith(""{value}""{filterCaseSensitivityOperator})";
}
else if (!string.IsNullOrEmpty(value) && columnFilterOperator == FilterOperator.Equals)
{
return $@"({property} == null ? """" : {property}){filterCaseSensitivityOperator} == ""{value}""{filterCaseSensitivityOperator}";
}
else if (!string.IsNullOrEmpty(value) && columnFilterOperator == FilterOperator.NotEquals)
{
return $@"({property} == null ? """" : {property}){filterCaseSensitivityOperator} != ""{value}""{filterCaseSensitivityOperator}";
}
else if (columnFilterOperator == FilterOperator.IsNull)
{
return npProperty + " == null";
}
else if (columnFilterOperator == FilterOperator.IsEmpty)
{
return npProperty + @" == """"";
}
else if (columnFilterOperator == FilterOperator.IsNotEmpty)
{
return npProperty + @" != """"";
}
else if (columnFilterOperator == FilterOperator.IsNotNull)
{
return npProperty + @" != null";
}
}
else if (PropertyAccess.IsEnum(column.FilterPropertyType) || PropertyAccess.IsNullableEnum(column.FilterPropertyType))
{
if (columnFilterOperator == FilterOperator.IsNull || columnFilterOperator == FilterOperator.IsNotNull)
{
return $"{property} {linqOperator} null";
}
else
{
return $"{property} {linqOperator} {value}";
}
}
else if (PropertyAccess.IsNumeric(column.FilterPropertyType))
{
if (columnFilterOperator == FilterOperator.IsNull || columnFilterOperator == FilterOperator.IsNotNull)
{
return $"{property} {linqOperator} null";
}
else if (columnFilterOperator == FilterOperator.IsEmpty || columnFilterOperator == FilterOperator.IsNotEmpty)
{
return $@"{property} {linqOperator} """"";
}
else
{
return $"{property} {linqOperator} {value}";
}
}
else if (column.FilterPropertyType == typeof(DateTime) ||
column.FilterPropertyType == typeof(DateTime?) ||
column.FilterPropertyType == typeof(DateTimeOffset) ||
column.FilterPropertyType == typeof(DateTimeOffset?) || isDateOnly)
{
if (columnFilterOperator == FilterOperator.IsNull || columnFilterOperator == FilterOperator.IsNotNull)
{
return $"{property} {linqOperator} null";
}
else if (columnFilterOperator == FilterOperator.IsEmpty || columnFilterOperator == FilterOperator.IsNotEmpty)
{
return $@"{property} {linqOperator} """"";
}
else
{
var dateTimeValue = DateTime.Parse(value, CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.RoundtripKind);
var finalDate = dateTimeValue.TimeOfDay == TimeSpan.Zero ? dateTimeValue.Date : dateTimeValue;
var dateFormat = dateTimeValue.TimeOfDay == TimeSpan.Zero ? "yyyy-MM-dd" : "yyyy-MM-ddTHH:mm:ss.fffZ";
string dateFunction = "DateTime"; //fallback to datetime, if it's an offset or dateonly use that
if (column.FilterPropertyType == typeof(DateTimeOffset) || column.FilterPropertyType == typeof(DateTimeOffset?))
dateFunction = "DateTimeOffset";
else
{
#if NET6_0_OR_GREATER
if (column.FilterPropertyType == typeof(DateOnly) || column.FilterPropertyType == typeof(DateOnly?))
dateFunction = "DateOnly";
#endif
}
return $@"{property} {linqOperator} {dateFunction}(""{finalDate.ToString(dateFormat, CultureInfo.InvariantCulture)}"")";
}
}
else if (column.FilterPropertyType == typeof(bool) || column.FilterPropertyType == typeof(bool?))
{
value = $"{value}".ToLower();
return $"{property} {linqOperator} {(columnFilterOperator == FilterOperator.IsNull || columnFilterOperator == FilterOperator.IsNotNull ? "null" : value)}";
}
else if (column.FilterPropertyType == typeof(Guid) || column.FilterPropertyType == typeof(Guid?))
{
if (columnFilterOperator == FilterOperator.IsNull || columnFilterOperator == FilterOperator.IsNotNull)
{
return $"{property} {linqOperator} null";
}
else if (columnFilterOperator == FilterOperator.IsEmpty || columnFilterOperator == FilterOperator.IsNotEmpty)
{
return $@"{property} {linqOperator} """"";
}
else
{
return $@"{property} {linqOperator} Guid(""{value}"")";
return serializer.Serialize(Expression.Lambda<Func<T, bool>>(combinedExpression, parameter));
}
}
@@ -1159,7 +666,7 @@ namespace Radzen
? null
: (string)Convert.ChangeType(filterValue is DateTimeOffset ?
((DateTimeOffset)filterValue).UtcDateTime : filterValue is DateOnly ?
((DateOnly)filterValue).ToString("yyy-MM-dd", CultureInfo.InvariantCulture) :
((DateOnly)filterValue).ToString("yyy-MM-dd", CultureInfo.InvariantCulture) :
filterValue, typeof(string), CultureInfo.InvariantCulture);
if (column.Grid.FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive && column.FilterPropertyType == typeof(string))
@@ -1276,7 +783,7 @@ namespace Radzen
column.FilterPropertyType == typeof(DateTime?) ||
column.FilterPropertyType == typeof(DateTimeOffset) ||
column.FilterPropertyType == typeof(DateTimeOffset?) ||
column.FilterPropertyType == typeof(DateOnly) ||
column.FilterPropertyType == typeof(DateOnly) ||
column.FilterPropertyType == typeof(DateOnly?))
{
if (columnFilterOperator == FilterOperator.IsNull || columnFilterOperator == FilterOperator.IsNotNull)
@@ -1394,16 +901,16 @@ namespace Radzen
if (columnsToFilter.Any())
{
source = source.Where(columnsToFilter.Select(c => new FilterDescriptor()
{
Property = c.Property,
FilterProperty = c.FilterProperty,
Type = c.FilterPropertyType,
FilterValue = c.GetFilterValue(),
FilterOperator = c.GetFilterOperator(),
SecondFilterValue = c.GetSecondFilterValue(),
SecondFilterOperator = c.GetSecondFilterOperator(),
LogicalFilterOperator = c.GetLogicalFilterOperator()
}), gridLogicalFilterOperator, gridFilterCaseSensitivity);
{
Property = c.Property,
FilterProperty = c.FilterProperty,
Type = c.FilterPropertyType,
FilterValue = c.GetFilterValue(),
FilterOperator = c.GetFilterOperator(),
SecondFilterValue = c.GetSecondFilterValue(),
SecondFilterOperator = c.GetSecondFilterOperator(),
LogicalFilterOperator = c.GetLogicalFilterOperator()
}), gridLogicalFilterOperator, gridFilterCaseSensitivity);
}
var columnsWithCustomFilter = columns.Where(canFilterCustom);
@@ -1411,7 +918,7 @@ namespace Radzen
if (columnsToFilter.Any())
{
var expressions = columnsWithCustomFilter.Select(c => (c.GetCustomFilterExpression() ?? "").Replace(" or ", " || ").Replace(" and ", " && ")).Where(e => !string.IsNullOrEmpty(e)).ToList();
source = expressions.Any() ?
source = expressions.Any() ?
System.Linq.Dynamic.Core.DynamicExtensions.Where(source, "it => " + string.Join($"{(gridLogicalFilterOperator == LogicalFilterOperator.And ? " && " : " || ")}", expressions)) : source;
}
@@ -1472,7 +979,7 @@ namespace Radzen
/// <returns>IQueryable&lt;T&gt;.</returns>
public static IQueryable<T> Where<T>(this IQueryable<T> source, IEnumerable<CompositeFilterDescriptor> filters, LogicalFilterOperator logicalFilterOperator, FilterCaseSensitivity filterCaseSensitivity)
{
Func<CompositeFilterDescriptor, bool> canFilter = (c) =>
Func<CompositeFilterDescriptor, bool> canFilter = (c) =>
(!(c.FilterValue == null || c.FilterValue as string == string.Empty)
|| c.FilterOperator == FilterOperator.IsNotNull || c.FilterOperator == FilterOperator.IsNull
|| c.FilterOperator == FilterOperator.IsEmpty || c.FilterOperator == FilterOperator.IsNotEmpty)
@@ -1820,4 +1327,4 @@ namespace Radzen
.ToList();
}
}
}
}

View File

@@ -25,7 +25,6 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" />
<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" />
@@ -56,10 +55,10 @@
</PropertyGroup>
<ItemGroup>
<Sass Include="$(MSBuildProjectDirectory)/themes/*.scss" Exclude="$(MSBuildProjectDirectory)/themes/_*.scss" Condition="'$(TargetFramework)' == 'net8.0'" />
<Sass Include="$(MSBuildProjectDirectory)/themes/*.scss" Exclude="$(MSBuildProjectDirectory)/themes/_*.scss" Condition="'$(TargetFramework)' == 'net9.0'" />
</ItemGroup>
<Target Name="Sass" BeforeTargets="BeforeBuild" Condition="'$(TargetFramework)' == 'net8.0'">
<Target Name="Sass" BeforeTargets="BeforeBuild" Condition="'$(TargetFramework)' == 'net9.0'">
<PropertyGroup>
<_SassFileList>@(Sass->'&quot;%(FullPath)&quot;', ' ')</_SassFileList>
<DartSassBuilderArgs>files $(_SassFileList) --outputstyle $(DartSassOutputStyle) --level $(DartSassOutputLevel)</DartSassBuilderArgs>
@@ -68,14 +67,14 @@
<Message Text="Converted SassFile list to argument" Importance="$(DartSassMessageLevel)" />
</Target>
<Target Name="MoveCss" AfterTargets="AfterCompile" Condition="'$(TargetFramework)' == 'net8.0'">
<Target Name="MoveCss" AfterTargets="AfterCompile" Condition="'$(TargetFramework)' == 'net9.0'">
<ItemGroup>
<CssFile Include="$(MSBuildProjectDirectory)/themes/*.css" />
</ItemGroup>
<Move SourceFiles="@(CssFile)" DestinationFolder="$(MSBuildProjectDirectory)/wwwroot/css/" />
</Target>
<Target Name="MinifyTest" BeforeTargets="Build" Condition="'$(TargetFramework)' == 'net8.0'">
<Target Name="MinifyTest" BeforeTargets="Build" Condition="'$(TargetFramework)' == 'net9.0'">
<TerserMinify InputFile="wwwroot\Radzen.Blazor.js" OutputFile="wwwroot\Radzen.Blazor.min.js" />
</Target>

View File

@@ -1,4 +1,5 @@
@using System.Linq
@using Radzen.Blazor.Rendering
@inherits RadzenComponent
@@ -21,14 +22,7 @@
<div @ref="@item.Element" id="@item.GetItemId()" @attributes="item.Attributes" class="@item.GetItemCssClass()" style="@item.Style" @onkeydown:stopPropagation>
<a @onclick="@((args) => SelectItem(item))" aria-label="@ItemAriaLabel(i, item)" title="@ItemTitle(i, item)" @onclick:preventDefault="true" role="tab"
id="@($"rz-accordiontab-{items.IndexOf(item)}")" aria-controls="@($"rz-accordiontab-{items.IndexOf(item)}-content")" aria-expanded="true">
@if (IsSelected(i, item))
{
<span class="notranslate rz-accordion-toggle-icon rzi rzi-chevron-down"></span>
}
else
{
<span class="notranslate rz-accordion-toggle-icon rzi rzi-chevron-right"></span>
}
<span class="@ToggleIconClass(item)">keyboard_arrow_down</span>
@if (!string.IsNullOrEmpty(item.Icon))
{
<i class="notranslate rzi" style="@(!string.IsNullOrEmpty(item.IconColor) ? $"color:{item.IconColor}" : null)">@item.Icon</i>
@@ -43,15 +37,12 @@
}
</a>
</div>
@if (IsSelected(i, item))
{
<div class="rz-accordion-content-wrapper" role="tabpanel"
id="@($"rz-accordiontab-{items.IndexOf(item)}-content")" aria-hidden="false" aria-labelledby="@($"rz-accordiontab-{items.IndexOf(item)}")">
<div class="rz-accordion-content" @onkeydown:stopPropagation>
@item.ChildContent
</div>
<Expander Expanded=@item.GetSelected() role="tabpanel"
id="@($"rz-accordiontab-{items.IndexOf(item)}-content")" aria-labelledby="@($"rz-accordiontab-{items.IndexOf(item)}")">
<div class="rz-accordion-content" @onkeydown:stopPropagation>
@item.ChildContent
</div>
}
</Expander>
}
</div>
}

View File

@@ -1,5 +1,6 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Radzen.Blazor.Rendering;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -110,6 +111,11 @@ namespace Radzen.Blazor
}
}
string ToggleIconClass(RadzenAccordionItem item) => ClassList.Create("notranslate rz-accordion-toggle-icon rzi")
.Add("rz-state-expanded", item.GetSelected())
.Add("rz-state-collapsed", !item.GetSelected())
.ToString();
/// <summary>
/// Refreshes this instance.
/// </summary>

View File

@@ -1,5 +1,6 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Radzen.Blazor.Rendering;
using System;
using System.Threading.Tasks;
@@ -161,32 +162,23 @@ namespace Radzen.Blazor
}
/// <inheritdoc />
protected override string GetComponentCssClass()
{
return $"rz-alert rz-alert-{GetAlertSize()} rz-variant-{Enum.GetName(typeof(Variant), Variant).ToLowerInvariant()} rz-{Enum.GetName(typeof(AlertStyle), AlertStyle).ToLowerInvariant()} rz-shade-{Enum.GetName(typeof(Shade), Shade).ToLowerInvariant()}";
}
protected override string GetComponentCssClass() => ClassList.Create("rz-alert")
.Add($"rz-alert-{GetAlertSize()}")
.AddVariant(Variant)
.Add($"rz-{Enum.GetName(typeof(AlertStyle), AlertStyle).ToLowerInvariant()}")
.AddShade(Shade)
.ToString();
string GetIcon()
{
if (!string.IsNullOrEmpty(Icon))
{
return Icon;
}
switch (AlertStyle)
{
case AlertStyle.Success:
return "check_circle";
case AlertStyle.Danger:
return "error";
case AlertStyle.Warning:
return "warning_amber";
case AlertStyle.Info:
return "info";
default:
return "lightbulb";
}
}
string GetIcon() => !string.IsNullOrEmpty(Icon)
? Icon
: AlertStyle switch
{
AlertStyle.Success => "check_circle",
AlertStyle.Danger => "error",
AlertStyle.Warning => "warning_amber",
AlertStyle.Info => "info",
_ => "lightbulb",
};
async Task OnClose()
{
@@ -221,4 +213,4 @@ namespace Radzen.Blazor
}
}
}
}
}

View File

@@ -14,7 +14,7 @@
<textarea @ref="@search" @attributes="InputAttributes" @onkeydown="@OnFilterKeyPress" value="@Value" disabled="@Disabled"
oninput="@OpenScript()" tabindex="@(Disabled ? "-1" : $"{TabIndex}")" @onchange="@OnChange" onfocus="@(OpenOnFocus ? OpenScript() : null)"
aria-autocomplete="list" aria-haspopup="true" autocomplete="off" role="combobox"
class="@InputClassList" onblur="Radzen.activeElement = null"
class=@InputClass onblur="Radzen.activeElement = null"
id="@Name" aria-expanded="true" placeholder="@CurrentPlaceholder" maxlength="@MaxLength" />
}
else
@@ -22,7 +22,7 @@
<input @ref="@search" @attributes="InputAttributes" @onkeydown="@OnFilterKeyPress" value="@Value" disabled="@Disabled"
oninput="@OpenScript()" tabindex="@(Disabled ? "-1" : $"{TabIndex}")" @onchange="@OnChange" onfocus="@(OpenOnFocus ? OpenScript() : null)"
aria-autocomplete="list" aria-haspopup="true" autocomplete="off" role="combobox"
class="@InputClassList" onblur="Radzen.activeElement = null"
class=@InputClass onblur="Radzen.activeElement = null"
type="@InputType" id="@Name" aria-expanded="true" placeholder="@CurrentPlaceholder" maxlength="@MaxLength" />
}
<div id="@PopupID" class="rz-autocomplete-panel" style="@PopupStyle">

View File

@@ -279,8 +279,9 @@ namespace Radzen.Blazor
StateHasChanged();
}
ClassList InputClassList => ClassList.Create("rz-inputtext rz-autocomplete-input")
.AddDisabled(Disabled);
string InputClass => ClassList.Create("rz-inputtext rz-autocomplete-input")
.AddDisabled(Disabled)
.ToString();
private string OpenScript()
{
@@ -293,10 +294,7 @@ namespace Radzen.Blazor
}
/// <inheritdoc />
protected override string GetComponentCssClass()
{
return GetClassList("rz-autocomplete").ToString();
}
protected override string GetComponentCssClass() => GetClassList("rz-autocomplete").ToString();
/// <inheritdoc />
public override void Dispose()
@@ -305,7 +303,7 @@ namespace Radzen.Blazor
if (IsJSRuntimeAvailable)
{
JSRuntime.InvokeVoidAsync("Radzen.destroyPopup", PopupID);
JSRuntime.InvokeVoid("Radzen.destroyPopup", PopupID);
}
}
@@ -364,4 +362,4 @@ namespace Radzen.Blazor
await search.FocusAsync();
}
}
}
}

View File

@@ -1,5 +1,5 @@
using Microsoft.AspNetCore.Components;
using System.Collections.Generic;
using Radzen.Blazor.Rendering;
namespace Radzen.Blazor
{
@@ -14,22 +14,12 @@ namespace Radzen.Blazor
public partial class RadzenBadge : RadzenComponent
{
/// <inheritdoc />
protected override string GetComponentCssClass()
{
var classList = new List<string>();
classList.Add("rz-badge");
classList.Add($"rz-badge-{BadgeStyle.ToString().ToLowerInvariant()}");
classList.Add($"rz-variant-{Variant.ToString().ToLowerInvariant()}");
classList.Add($"rz-shade-{Shade.ToString().ToLowerInvariant()}");
if (IsPill)
{
classList.Add("rz-badge-pill");
}
return string.Join(" ", classList);
}
protected override string GetComponentCssClass() => ClassList.Create("rz-badge")
.Add($"rz-badge-{BadgeStyle.ToString().ToLowerInvariant()}")
.AddVariant(Variant)
.AddShade(Shade)
.Add("rz-badge-pill", IsPill)
.ToString();
/// <summary>
/// Gets or sets the child content.
@@ -73,4 +63,4 @@ namespace Radzen.Blazor
[Parameter]
public bool IsPill { get; set; }
}
}
}

View File

@@ -24,13 +24,9 @@ namespace Radzen.Blazor
public override string Style { get; set; } = DefaultStyle;
/// <inheritdoc />
protected override string GetComponentCssClass()
{
var classList = ClassList.Create("rz-body")
.Add("rz-body-expanded", Expanded);
return classList.ToString();
}
protected override string GetComponentCssClass() => ClassList.Create("rz-body")
.Add("rz-body-expanded", Expanded)
.ToString();
/// <summary>
/// Toggles this instance width and left margin.

View File

@@ -1,5 +1,6 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Radzen.Blazor.Rendering;
using System;
using System.Threading.Tasks;
@@ -15,11 +16,6 @@ namespace Radzen.Blazor
/// </example>
public partial class RadzenButton : RadzenComponent
{
internal string getButtonSize()
{
return Size == ButtonSize.Medium ? "md" : Size == ButtonSize.Large ? "lg" : Size == ButtonSize.Small ? "sm" : "xs";
}
/// <summary>
/// Gets or sets the child content.
/// </summary>
@@ -164,9 +160,13 @@ namespace Radzen.Blazor
}
/// <inheritdoc />
protected override string GetComponentCssClass()
{
return $"rz-button rz-button-{getButtonSize()} rz-variant-{Enum.GetName(typeof(Variant), Variant).ToLowerInvariant()} rz-{Enum.GetName(typeof(ButtonStyle), ButtonStyle).ToLowerInvariant()} rz-shade-{Enum.GetName(typeof(Shade), Shade).ToLowerInvariant()}{(IsDisabled ? " rz-state-disabled" : "")}{(string.IsNullOrEmpty(Text) && !string.IsNullOrEmpty(Icon) ? " rz-button-icon-only" : "")}";
}
protected override string GetComponentCssClass() => ClassList.Create("rz-button")
.AddButtonSize(Size)
.AddVariant(Variant)
.AddButtonStyle(ButtonStyle)
.AddDisabled(IsDisabled)
.AddShade(Shade)
.Add($"rz-button-icon-only", string.IsNullOrEmpty(Text) && !string.IsNullOrEmpty(Icon))
.ToString();
}
}

View File

@@ -1,8 +1,5 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using Radzen.Blazor.Rendering;
namespace Radzen.Blazor
{
@@ -12,14 +9,9 @@ namespace Radzen.Blazor
public partial class RadzenCard : RadzenComponentWithChildren
{
/// <inheritdoc />
protected override string GetComponentCssClass()
{
var classList = new List<string>();
classList.Add("rz-card");
classList.Add($"rz-variant-{Variant.ToString().ToLowerInvariant()}");
return string.Join(" ", classList);
}
protected override string GetComponentCssClass() => ClassList.Create("rz-card")
.AddVariant(Variant)
.ToString();
/// <summary>
/// Gets or sets the card variant.

View File

@@ -1,8 +1,5 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using Radzen.Blazor.Rendering;
namespace Radzen.Blazor
{
@@ -19,17 +16,8 @@ namespace Radzen.Blazor
public bool Responsive { get; set; } = true;
/// <inheritdoc />
protected override string GetComponentCssClass()
{
var classList = new List<string>();
classList.Add("rz-card-group");
if (Responsive)
{
classList.Add("rz-card-group-responsive");
}
return string.Join(" ", classList);
}
protected override string GetComponentCssClass() => ClassList.Create("rz-card-group")
.Add("rz-card-group-responsive", Responsive)
.ToString();
}
}

View File

@@ -2,7 +2,7 @@
@implements IDisposable
@inject IJSRuntime JSRuntime
<li @ref="element" @attributes=@Attributes class=@ClassList tabindex="0">
<li @ref="element" @attributes=@Attributes class=@Class tabindex="0">
@ChildContent
<div class="rz-carousel-snapper"></div>
</li>

View File

@@ -1,5 +1,4 @@
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using Radzen.Blazor.Rendering;
using System;
using System.Collections.Generic;
@@ -16,9 +15,9 @@ namespace Radzen.Blazor
/// Gets the class list.
/// </summary>
/// <value>The class list.</value>
ClassList ClassList => ClassList.Create()
.Add("rz-carousel-item")
.Add(Attributes);
string Class => ClassList.Create("rz-carousel-item")
.Add(Attributes)
.ToString();
/// <summary>
/// Gets or sets the arbitrary attributes.

View File

@@ -586,7 +586,7 @@ namespace Radzen.Blazor
if (Visible && IsJSRuntimeAvailable)
{
JSRuntime.InvokeVoidAsync("Radzen.destroyChart", Element);
JSRuntime.InvokeVoid("Radzen.destroyChart", Element);
}
}

View File

@@ -6,12 +6,12 @@
@if (Visible)
{
<div @ref="@Element" @attributes="Attributes" class="@GetCssClass()" @onkeypress=@OnKeyPress @onkeypress:preventDefault style="@Style" tabindex="@(Disabled || ReadOnly ? "-1" : $"{TabIndex}")" id="@GetId()">
<div class="rz-helper-hidden-accessible">
<input type="checkbox" @onchange=@Toggle value=@CheckBoxValue name=@Name id=@Name checked=@CheckBoxChecked aria-checked="@((Value as bool? == true).ToString().ToLowerInvariant())" @attributes="InputAttributes"
tabindex="-1" readonly="@ReadOnly">
<div class="rz-helper-hidden-accessible">
<input type="checkbox" @onchange=@Toggle value=@CheckBoxValue name=@Name id=@Name checked=@CheckBoxChecked aria-checked="@((Value as bool? == true).ToString().ToLowerInvariant())" @attributes="InputAttributes"
tabindex="-1" readonly="@ReadOnly">
</div>
<div class=@BoxClass @onclick=@Toggle @onclick:preventDefault>
<span class=@IconClass></span>
</div>
</div>
<div class=@BoxClassList @onclick=@Toggle @onclick:preventDefault>
<span class=@IconClassList></span>
</div>
</div>
}

View File

@@ -31,23 +31,22 @@ namespace Radzen.Blazor
[Parameter]
public bool TriState { get; set; } = false;
ClassList BoxClassList => ClassList.Create("rz-chkbox-box")
.Add("rz-state-active", !object.Equals(Value, false))
.AddDisabled(Disabled);
string BoxClass => ClassList.Create("rz-chkbox-box")
.Add("rz-state-active", !object.Equals(Value, false))
.AddDisabled(Disabled)
.ToString();
ClassList IconClassList => ClassList.Create("notranslate rz-chkbox-icon")
.Add("rzi rzi-check", object.Equals(Value, true))
.Add("rzi rzi-times", object.Equals(Value, null));
string IconClass => ClassList.Create("notranslate rz-chkbox-icon")
.Add("rzi rzi-check", object.Equals(Value, true))
.Add("rzi rzi-times", object.Equals(Value, null))
.ToString();
string CheckBoxValue => CheckBoxChecked ? "true" : "false";
bool CheckBoxChecked => object.Equals(Value, true);
/// <inheritdoc />
protected override string GetComponentCssClass()
{
return GetClassList("rz-chkbox").ToString();
}
protected override string GetComponentCssClass() => GetClassList("rz-chkbox").ToString();
async Task OnKeyPress(KeyboardEventArgs args)
{

View File

@@ -14,7 +14,9 @@
}
@if (Visible)
{
<div @ref="@Element" style="@Style" @attributes="Attributes" class="@GetCssClass()" id="@GetId()">
<div @ref="@Element" style="@Style" @attributes="Attributes" class="@GetCssClass()" id="@GetId()"
role="checkboxgroup" @onfocus=@OnFocus @onblur=@OnBlur
tabindex="@(Disabled ? "-1" : $"{TabIndex}")" @onkeydown="@(args => OnKeyPress(args))" @onkeydown:preventDefault=preventKeyPress @onkeydown:stopPropagation>
@if (AllowSelectAll)
{
<div class="rz-multiselect-header rz-helper-clearfix" @onclick:preventDefault>
@@ -23,15 +25,16 @@
<RadzenLabel Component="SelectAll" Text="@SelectAllText" class="rz-chkbox-label" />
</div>
}
<RadzenStack Orientation="@Orientation" JustifyContent="@JustifyContent" AlignItems="@AlignItems" Gap="@Gap" Wrap="@Wrap">
@foreach (var item in allItems.Where(i => i.Visible))
{
<div @ref="@item.Element" id="@item.GetItemId()" @onclick="@(args => SelectItem(item))" @attributes="item.Attributes" class="@item.GetItemCssClass()" style="@item.Style">
<div class="rz-chkbox" @onkeypress="@(args => OnKeyPress(args, SelectItem(item)))" @onkeypress:preventDefault=preventKeyPress @onkeypress:stopPropagation tabindex="@(Disabled || ReadOnly || item.Disabled || item.ReadOnly ? "-1" : $"{TabIndex}")">
<div @ref="@item.Element" id="@item.GetItemId()" @onclick="@(args => SelectItem(item))" @attributes="item.Attributes" class="@item.GetItemCssClass()" style="@item.Style" role="checkbox" aria-checked=@(IsSelected(item)? "true" : "false") aria-label="@item.Text">
<div class="rz-chkbox">
<div class="rz-helper-hidden-accessible">
<input type="checkbox" name="@Name" value="@item.Value" disabled="@Disabled" readonly="@ReadOnly" tabindex="-1" aria-label="@(item.Text + " " + item.Value)" aria-checked="@(IsSelected(item).ToString().ToLowerInvariant())">
</div>
<div class=@ItemClassList(item)>
<span class=@IconClassList(item)></span>
<div class=@ItemClass(item)>
<span class=@IconClass(item)></span>
</div>
</div>
@if (item.Template != null)
@@ -46,5 +49,6 @@
}
</div>
}
</RadzenStack>
</div>
}

View File

@@ -25,12 +25,15 @@ namespace Radzen.Blazor
/// </example>
public partial class RadzenCheckBoxList<TValue> : FormComponent<IEnumerable<TValue>>
{
ClassList ItemClassList(RadzenCheckBoxListItem<TValue> item) => ClassList.Create("rz-chkbox-box")
.Add("rz-state-active", IsSelected(item))
.AddDisabled(Disabled || item.Disabled);
string ItemClass(RadzenCheckBoxListItem<TValue> item) => ClassList.Create("rz-chkbox-box")
.Add("rz-state-active", IsSelected(item))
.Add("rz-state-focused", IsFocused(item) && focused)
.AddDisabled(Disabled || item.Disabled)
.ToString();
ClassList IconClassList(RadzenCheckBoxListItem<TValue> item) => ClassList.Create("rz-chkbox-icon")
.Add("notranslate rzi rzi-check", IsSelected(item));
string IconClass(RadzenCheckBoxListItem<TValue> item) => ClassList.Create("rz-chkbox-icon")
.Add("notranslate rzi rzi-check", IsSelected(item))
.ToString();
/// <summary>
/// Gets or sets the value property.
@@ -46,6 +49,34 @@ namespace Radzen.Blazor
[Parameter]
public string TextProperty { get; set; }
/// <summary>
/// Gets or sets the content justify.
/// </summary>
/// <value>The content justify.</value>
[Parameter]
public JustifyContent JustifyContent { get; set; } = JustifyContent.Start;
/// <summary>
/// Gets or sets the items alignment.
/// </summary>
/// <value>The items alignment.</value>
[Parameter]
public AlignItems AlignItems { get; set; } = AlignItems.Start;
/// <summary>
/// Gets or sets the spacing between items
/// </summary>
/// <value>The spacing between items.</value>
[Parameter]
public string Gap { get; set; }
/// <summary>
/// Gets or sets the wrap.
/// </summary>
/// <value>The wrap.</value>
[Parameter]
public FlexWrap Wrap { get; set; } = FlexWrap.Wrap;
/// <summary>
/// Gets or sets the disabled property.
/// </summary>
@@ -60,31 +91,37 @@ namespace Radzen.Blazor
[Parameter]
public string ReadOnlyProperty { get; set; }
IEnumerable<RadzenCheckBoxListItem<TValue>> allItems
void UpdateAllItems()
{
get
allItems = items.Concat((Data != null ? Data.Cast<object>() : Enumerable.Empty<object>()).Select(i =>
{
return items.Concat((Data != null ? Data.Cast<object>() : Enumerable.Empty<object>()).Select(i =>
var item = new RadzenCheckBoxListItem<TValue>();
item.SetText((string)PropertyAccess.GetItemOrValueFromProperty(i, TextProperty));
item.SetValue((TValue)PropertyAccess.GetItemOrValueFromProperty(i, ValueProperty));
if (DisabledProperty != null && PropertyAccess.TryGetItemOrValueFromProperty<bool>(i, DisabledProperty, out var disabledResult))
{
var item = new RadzenCheckBoxListItem<TValue>();
item.SetText((string)PropertyAccess.GetItemOrValueFromProperty(i, TextProperty));
item.SetValue((TValue)PropertyAccess.GetItemOrValueFromProperty(i, ValueProperty));
item.SetDisabled(disabledResult);
}
if (DisabledProperty != null && PropertyAccess.TryGetItemOrValueFromProperty<bool>(i, DisabledProperty, out var disabledResult))
{
item.SetDisabled(disabledResult);
}
if (ReadOnlyProperty != null && PropertyAccess.TryGetItemOrValueFromProperty<bool>(i, ReadOnlyProperty, out var readOnlyResult))
{
item.SetReadOnly(readOnlyResult);
}
if (ReadOnlyProperty != null && PropertyAccess.TryGetItemOrValueFromProperty<bool>(i, ReadOnlyProperty, out var readOnlyResult))
{
item.SetReadOnly(readOnlyResult);
}
return item;
}));
}
return item;
})).ToList();
}
/// <inheritdoc />
protected override void OnParametersSet()
{
base.OnParametersSet();
UpdateAllItems();
}
List<RadzenCheckBoxListItem<TValue>> allItems;
/// <summary>
/// Gets or sets a value indicating whether the user can select all values. Set to <c>false</c> by default.
/// </summary>
@@ -171,7 +208,7 @@ namespace Radzen.Blazor
/// <inheritdoc />
protected override string GetComponentCssClass()
{
return GetClassList(Orientation == Orientation.Horizontal ? "rz-checkbox-list-horizontal" : "rz-checkbox-list-vertical").ToString();
return GetClassList("rz-checkbox-list").Add(Orientation == Orientation.Horizontal ? "rz-checkbox-list-horizontal" : "rz-checkbox-list-vertical").ToString();
}
/// <summary>
@@ -211,6 +248,7 @@ namespace Radzen.Blazor
if (items.IndexOf(item) == -1)
{
items.Add(item);
UpdateAllItems();
StateHasChanged();
}
}
@@ -224,6 +262,7 @@ namespace Radzen.Blazor
if (items.Contains(item))
{
items.Remove(item);
UpdateAllItems();
if (!disposed)
{
try { InvokeAsync(StateHasChanged); } catch { }
@@ -250,6 +289,8 @@ namespace Radzen.Blazor
if (Disabled || item.Disabled || ReadOnly || item.ReadOnly)
return;
focusedIndex = allItems.IndexOf(item);
List<TValue> selectedValues = new List<TValue>(Value != null ? Value : Enumerable.Empty<TValue>());
if (!selectedValues.Contains(item.Value))
@@ -270,21 +311,68 @@ namespace Radzen.Blazor
StateHasChanged();
}
bool focused;
int focusedIndex = -1;
bool preventKeyPress = true;
async Task OnKeyPress(KeyboardEventArgs args, Task task)
async Task OnKeyPress(KeyboardEventArgs args)
{
var key = args.Code != null ? args.Code : args.Key;
if (key == "Space" || key == "Enter")
var item = allItems.ElementAtOrDefault(focusedIndex) ?? allItems.FirstOrDefault();
if (item == null) return;
if ((Orientation == Orientation.Horizontal && (key == "ArrowLeft" || key == "ArrowRight")) ||
(Orientation == Orientation.Vertical && (key == "ArrowUp" || key == "ArrowDown")))
{
preventKeyPress = true;
var direction = key == "ArrowLeft" || key == "ArrowUp" ? -1 : 1;
focusedIndex = Math.Clamp(focusedIndex + direction, 0, allItems.FindLastIndex(t => t.Visible && !t.Disabled));
while (allItems.ElementAtOrDefault(focusedIndex)?.Disabled == true)
{
focusedIndex = focusedIndex + direction;
}
}
else if (key == "Home" || key == "End")
{
preventKeyPress = true;
await task;
focusedIndex = key == "Home" ? 0 : allItems.Where(t => HasInvisibleBefore(item) ? true : t.Visible).Count() - 1;
}
else if (key == "Space" || key == "Enter")
{
preventKeyPress = true;
if (focusedIndex >= 0 && focusedIndex < allItems.Where(t => HasInvisibleBefore(item) ? true : t.Visible).Count())
{
await SelectItem(allItems.Where(t => HasInvisibleBefore(item) ? true : t.Visible).ToList()[focusedIndex]);
}
}
else
{
preventKeyPress = false;
}
}
bool HasInvisibleBefore(RadzenCheckBoxListItem<TValue> item)
{
return allItems.Take(allItems.IndexOf(item)).Any(t => !t.Visible && !t.Disabled);
}
bool IsFocused(RadzenCheckBoxListItem<TValue> item)
{
return allItems.IndexOf(item) == focusedIndex;
}
void OnFocus()
{
focusedIndex = focusedIndex == -1 ? 0 : focusedIndex;
focused = true;
}
void OnBlur()
{
focused = false;
}
}
}

View File

@@ -1,12 +1,80 @@
using Microsoft.AspNetCore.Components;
using Radzen.Blazor.Rendering;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
namespace Radzen.Blazor
{
static class ClassListExtensions
{
private static int GetSize(string name, int value)
{
if (value < 0 || value > 12)
{
throw new ArgumentOutOfRangeException($"Property {name} value should be between 0 and 12.");
}
return value;
}
private static string GetOrder(string name, string value)
{
if (int.TryParse(value, out int result))
{
if (result >= 0 && result <= 12)
{
return value;
}
}
if (value == "first" || value == "last")
{
return value;
}
throw new ArgumentOutOfRangeException($"Property {name} value should be between 0 and 12 or first/last.");
}
public static ClassList AddSize(this ClassList classList, string prefix, string name, string size, int? value)
{
if (value.HasValue)
{
classList.Add($"rz-{prefix}-{size}-{GetSize(name, value.Value)}");
}
return classList;
}
public static ClassList AddSize(this ClassList classList, string prefix, string name, int? value)
{
if (value.HasValue)
{
classList.Add($"rz-{prefix}-{GetSize(name, value.Value)}");
}
return classList;
}
public static ClassList AddOrder(this ClassList classList, string prefix, string name, string size, string value)
{
if (!string.IsNullOrEmpty(value))
{
classList.Add($"rz-{prefix}-{size}-{GetOrder(name, value)}");
}
return classList;
}
public static ClassList AddOrder(this ClassList classList, string prefix, string name, string value)
{
if (!string.IsNullOrEmpty(value))
{
classList.Add($"rz-{prefix}-{GetOrder(name, value)}");
}
return classList;
}
}
/// <summary>
/// RadzenColumn component.
/// </summary>
@@ -181,65 +249,29 @@ namespace Radzen.Blazor
}
/// <inheritdoc />
protected override string GetComponentCssClass()
{
var list = new List<string>
{
Size != null ? $"rz-col-{GetColumnValue("Size", Size)}" : "rz-col"
};
if (Offset != null)
{
list.Add($"rz-offset-{GetColumnValue("Offset", Offset)}");
}
if (!string.IsNullOrEmpty(Order))
{
list.Add($"rz-order-{GetOrderValue("Order", Order)}");
}
var breakPoints = new string[] { "xs", "sm", "md", "lg", "xl", "xx" };
var properties = GetType().GetProperties()
.Where(p => breakPoints.Any(bp => p.Name.ToLower().EndsWith(bp)))
.Select(p => new { p.Name, BreakPoint = string.Concat(p.Name.ToLower().TakeLast(2)), Value = p.GetValue(this) });
foreach (var p in properties)
{
if (p.Value != null)
{
list.Add($"rz-{(!p.Name.StartsWith("Size") ? p.Name.ToLower().Replace(p.BreakPoint, "") + "-" : "col-")}{p.BreakPoint}-{GetColumnValue(p.Name, p.Value)}");
}
}
return string.Join(" ", list);
}
string GetColumnValue(string name, object value)
{
if (name.StartsWith("Order"))
{
return GetOrderValue(name, value.ToString());
}
if ((int)value < 0 || (int)value > 12)
{
throw new Exception($"Property {name} value should be between 0 and 12.");
}
return $"{value}";
}
string GetOrderValue(string name, string value)
{
var orders = Enumerable.Range(0, 12).Select(i => $"{i}").ToArray().Concat(new string[] { "first", "last" });
if (!orders.Contains(value))
{
throw new Exception($"Property {name} value should be between 0 and 12 or first/last.");
}
return value;
}
protected override string GetComponentCssClass() => ClassList.Create()
.Add("rz-col", Size == null)
.AddSize("col", nameof(Size), Size)
.AddSize("col", nameof(SizeXS), "xs", SizeXS)
.AddSize("col", nameof(SizeSM), "sm", SizeSM)
.AddSize("col", nameof(SizeMD), "md", SizeMD)
.AddSize("col", nameof(SizeLG), "lg", SizeLG)
.AddSize("col", nameof(SizeXL), "xl", SizeXL)
.AddSize("col", nameof(SizeXX), "xx", SizeXX)
.AddSize("offset", nameof(Offset), Offset)
.AddSize("offset", nameof(OffsetXS), "xs", OffsetXS)
.AddSize("offset", nameof(OffsetSM), "sm", OffsetSM)
.AddSize("offset", nameof(OffsetMD), "md", OffsetMD)
.AddSize("offset", nameof(OffsetLG), "lg", OffsetLG)
.AddSize("offset", nameof(OffsetXL), "xl", OffsetXL)
.AddSize("offset", nameof(OffsetXX), "xx", OffsetXX)
.AddOrder("order", nameof(Order), Order)
.AddOrder("order", nameof(OrderXS), "xs", OrderXS)
.AddOrder("order", nameof(OrderSM), "sm", OrderSM)
.AddOrder("order", nameof(OrderMD), "md", OrderMD)
.AddOrder("order", nameof(OrderLG), "lg", OrderLG)
.AddOrder("order", nameof(OrderXL), "xl", OrderXL)
.AddOrder("order", nameof(OrderXX), "xx", OrderXX)
.ToString();
}
}
}

View File

@@ -302,17 +302,17 @@ namespace Radzen
{
if (ContextMenu.HasDelegate)
{
JSRuntime.InvokeVoidAsync("Radzen.removeContextMenu", UniqueID);
JSRuntime.InvokeVoid("Radzen.removeContextMenu", UniqueID);
}
if (MouseEnter.HasDelegate)
{
JSRuntime.InvokeVoidAsync("Radzen.removeMouseEnter", UniqueID);
JSRuntime.InvokeVoid("Radzen.removeMouseEnter", UniqueID);
}
if (MouseLeave.HasDelegate)
{
JSRuntime.InvokeVoidAsync("Radzen.removeMouseLeave", UniqueID);
JSRuntime.InvokeVoid("Radzen.removeMouseLeave", UniqueID);
}
}
}

View File

@@ -174,7 +174,7 @@ namespace Radzen.Blazor
void OnNavigate()
{
JSRuntime.InvokeVoidAsync("Radzen.closePopup", UniqueID);
JSRuntime.InvokeVoid("Radzen.closePopup", UniqueID);
}
}
}

View File

@@ -2,6 +2,7 @@
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.JSInterop;
using Radzen.Blazor.Rendering;
using System;
using System.Collections;
using System.Collections.Generic;
@@ -3303,32 +3304,12 @@ namespace Radzen.Blazor
}
/// <inheritdoc />
protected override string GetComponentCssClass()
{
var additionalClasses = new List<string>();
if (CurrentStyle.ContainsKey("height"))
{
additionalClasses.Add("rz-has-height");
}
if (RowSelect.HasDelegate || ValueChanged.HasDelegate || SelectionMode == DataGridSelectionMode.Multiple)
{
additionalClasses.Add("rz-selectable");
}
if (Responsive)
{
additionalClasses.Add("rz-datatable-reflow");
}
if (Density == Density.Compact)
{
additionalClasses.Add("rz-density-compact");
}
return $"rz-has-pager rz-datatable rz-datatable-scrollable {String.Join(" ", additionalClasses)}";
}
protected override string GetComponentCssClass() => ClassList.Create("rz-has-pager rz-datatable rz-datatable-scrollable")
.Add("rz-has-height", CurrentStyle.ContainsKey("height"))
.Add("rz-datatable-reflow", Responsive)
.Add("rz-density-compact", Density == Density.Compact)
.Add("rz-selectable", RowSelect.HasDelegate || ValueChanged.HasDelegate || SelectionMode == DataGridSelectionMode.Multiple)
.ToString();
internal string getHeaderStyle()
{
@@ -3364,7 +3345,7 @@ namespace Radzen.Blazor
{
foreach (var column in allColumns.ToList().Where(c => c.GetVisible()))
{
JSRuntime.InvokeVoidAsync("Radzen.destroyPopup", $"{PopupID}{column.GetFilterProperty()}");
JSRuntime.InvokeVoid("Radzen.destroyPopup", $"{PopupID}{column.GetFilterProperty()}");
}
}
}

View File

@@ -1161,8 +1161,8 @@ namespace Radzen.Blazor
if (IsJSRuntimeAvailable)
{
JSRuntime.InvokeVoidAsync("Radzen.destroyPopup", PopupID);
JSRuntime.InvokeVoidAsync("Radzen.destroyDatePicker", UniqueID, Element);
JSRuntime.InvokeVoid("Radzen.destroyPopup", PopupID);
JSRuntime.InvokeVoid("Radzen.destroyDatePicker", UniqueID, Element);
}
}

View File

@@ -126,10 +126,28 @@
StateHasChanged();
}
bool sideDialogClosing = false;
async Task OnSideCloseAsync()
{
sideDialogClosing = true;
StateHasChanged();
await Task.Delay(300);
isSideDialogOpen = false;
sideDialogClosing = false;
StateHasChanged();
}
void OnSideClose(dynamic _)
{
isSideDialogOpen = false;
StateHasChanged();
if (isSideDialogOpen)
{
InvokeAsync(OnSideCloseAsync);
}
}
void OnOpen(string title, Type type, Dictionary<string, object> parameters, DialogOptions options)
@@ -142,10 +160,12 @@
Close(result).ConfigureAwait(false);
}
string GetSideDialogCssClass()
{
return $"rz-dialog-side rz-dialog-side-position-{sideDialogOptions.Position.ToString().ToLower()} {sideDialogOptions.CssClass}";
}
string GetSideDialogCssClass() => ClassList.Create("rz-dialog-side")
.Add($"rz-dialog-side-position-{sideDialogOptions.Position.ToString().ToLower()}")
.Add(sideDialogOptions.CssClass)
.Add("rz-open", !sideDialogClosing)
.Add("rz-close", sideDialogClosing)
.ToString();
string GetSideDialogStyle()
{

View File

@@ -9,7 +9,7 @@
@if (Visible)
{
<div @ref="@Element" @attributes="Attributes" class="@GetCssClass()" onmousedown="Radzen.activeElement = null" @onclick="@(args => OpenPopup("ArrowDown", false, true))" @onclick:preventDefault @onclick:stopPropagation style="@Style" tabindex="@(Disabled ? "-1" : $"{TabIndex}")"
@onkeydown="@((args) => OnKeyPress(args))" @onkeydown:preventDefault="@preventKeydown" id="@GetId()" @onfocus="@((args) => OnFocus(args))">
@onkeydown="@((args) => OnKeyPress(args))" @onkeydown:preventDefault="@preventKeydown" id="@GetId()" @onfocus=@this.AsNonRenderingEventHandler(OnFocus)>
<div class="rz-helper-hidden-accessible">
<input disabled="@Disabled" aria-haspopup="listbox" readonly="" type="text" tabindex="-1"
name="@Name" value="@(internalValue != null ? internalValue : "")" id="@Name"

View File

@@ -102,7 +102,7 @@ namespace Radzen.Blazor
return args;
}
private async Task OnFocus(Microsoft.AspNetCore.Components.Web.FocusEventArgs args)
private async Task OnFocus()
{
if (OpenOnFocus)
{
@@ -360,7 +360,7 @@ namespace Radzen.Blazor
if (IsJSRuntimeAvailable)
{
JSRuntime.InvokeVoidAsync("Radzen.destroyPopup", PopupID);
JSRuntime.InvokeVoid("Radzen.destroyPopup", PopupID);
}
}

View File

@@ -8,7 +8,7 @@
@if (Visible)
{
<div @ref="@Element" @attributes="Attributes" class="@GetCssClass()" @onfocus="@((args) => OnFocus(args))"
<div @ref="@Element" @attributes="Attributes" class="@GetCssClass()" @onfocus=@this.AsNonRenderingEventHandler(OnFocus)
style="@Style" tabindex="@(Disabled ? "-1" : $"{TabIndex}")" id="@GetId()" @onclick="@(args => OpenPopup("ArrowDown", false, true))" @onclick:preventDefault @onclick:stopPropagation
@onkeydown="@((args) => OnKeyPress(args))" @onkeydown:preventDefault="@preventKeydown">
<div class="rz-helper-hidden-accessible">

View File

@@ -97,7 +97,7 @@ namespace Radzen.Blazor
[Parameter]
public bool OpenOnFocus { get; set; }
private async Task OnFocus(Microsoft.AspNetCore.Components.Web.FocusEventArgs args)
private async Task OnFocus()
{
if (OpenOnFocus)
{
@@ -994,7 +994,7 @@ namespace Radzen.Blazor
if (IsJSRuntimeAvailable)
{
JSRuntime.InvokeVoidAsync("Radzen.destroyPopup", PopupID);
JSRuntime.InvokeVoid("Radzen.destroyPopup", PopupID);
}
}

View File

@@ -1,4 +1,5 @@
@inherits RadzenComponent
@using Radzen.Blazor.Rendering
@inherits RadzenComponent
@if (Visible)
{
@@ -6,7 +7,6 @@
@if(AllowCollapse || !string.IsNullOrEmpty(Text) || !string.IsNullOrEmpty(Icon) || HeaderTemplate != null)
{
<legend class="rz-fieldset-legend" style="white-space:nowrap">
@if (AllowCollapse)
{
<a id="@(GetId() + "expc")" title="@TitleAttribute()" aria-label="@AriaLabelAttribute()" @onclick:preventDefault="true"
@@ -46,18 +46,18 @@
}
</legend>
}
<div class="rz-fieldset-content-wrapper" role="region" id="rz-fieldset-0-content" aria-hidden="false" style="@contentStyle">
<Expander Expanded=@(!collapsed) CssClass="rz-fieldset-content-wrapper" id="rz-fieldset-0-content">
<div class="rz-fieldset-content">
@ChildContent
</div>
</div>
</Expander>
@if (SummaryTemplate != null)
{
<div class="rz-fieldset-content-wrapper" role="region" aria-hidden="false" style="@summaryContentStyle">
<div class="rz-fieldset-content rz-fieldset-content-summary">
@SummaryTemplate
</div>
<Expander Expanded=@collapsed CssClass="rz-fieldset-content-wrapper">
<div class="rz-fieldset-content rz-fieldset-content-summary">
@SummaryTemplate
</div>
</Expander>
}
</fieldset>
}
}

View File

@@ -1,5 +1,6 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Radzen.Blazor.Rendering;
using System;
using System.Threading.Tasks;
@@ -26,10 +27,10 @@ namespace Radzen.Blazor
public partial class RadzenFieldset : RadzenComponent
{
/// <inheritdoc />
protected override string GetComponentCssClass()
{
return AllowCollapse ? "rz-fieldset rz-fieldset-toggleable" : "rz-fieldset";
}
protected override string GetComponentCssClass() =>
ClassList.Create("rz-fieldset")
.Add("rz-fieldset-toggleable", AllowCollapse)
.ToString();
/// <summary>
/// Gets or sets a value indicating whether collapsing is allowed. Set to <c>false</c> by default.
@@ -131,14 +132,9 @@ namespace Radzen.Blazor
[Parameter]
public EventCallback Collapse { get; set; }
string contentStyle = "";
string summaryContentStyle = "display: none";
async System.Threading.Tasks.Task Toggle(EventArgs args)
async Task Toggle(EventArgs args)
{
collapsed = !collapsed;
contentStyle = collapsed ? "display: none;" : "";
summaryContentStyle = !collapsed ? "display: none" : "";
if (collapsed)
{
@@ -188,15 +184,6 @@ namespace Radzen.Blazor
await base.SetParametersAsync(parameters);
}
/// <inheritdoc />
protected override Task OnParametersSetAsync()
{
contentStyle = collapsed ? "display: none;" : "";
summaryContentStyle = !collapsed ? "display: none" : "";
return base.OnParametersSetAsync();
}
bool preventKeyPress = false;
async Task OnKeyPress(KeyboardEventArgs args, Task task)
{

View File

@@ -9,7 +9,7 @@
{
<div @ref="@Element" style="@Style" @attributes="Attributes" class="@GetCssClass()" id="@GetId()">
<div class="rz-fileupload-buttonbar">
<span class="@ChooseClassList" tabindex="@(Disabled ? "-1" : $"{TabIndex}")" onkeydown="if(event.keyCode == 32 || event.keyCode == 13){event.preventDefault();this.firstElementChild.click();}">
<span class=@ChooseClass tabindex="@(Disabled ? "-1" : $"{TabIndex}")" onkeydown="if(event.keyCode == 32 || event.keyCode == 13){event.preventDefault();this.firstElementChild.click();}">
<input id="@(Name ?? GetId())" @attributes="InputAttributes" tabindex="-1" disabled="@Disabled" @ref="@fileUpload" name="@Name" type="file" accept="@Accept" onkeydown="event.stopPropagation()"
onchange="Radzen.uploadInputChange(event, null, false, false, true)" />
<span class="rz-button-text">@ChooseText</span>
@@ -43,7 +43,7 @@
}
</div>
<div>
<button disabled="@Disabled" type="button" class="@ButtonClassList" @onclick="@Remove" title="@DeleteText" tabindex="@(Disabled ? "-1" : $"{TabIndex}")">
<button disabled="@Disabled" type="button" class=@ButtonClass @onclick="@Remove" title="@DeleteText" tabindex="@(Disabled ? "-1" : $"{TabIndex}")">
<span class="rz-button-icon-left rz-icon-trash" style="display:block"></span>
</button>
</div>

View File

@@ -55,24 +55,15 @@ namespace Radzen.Blazor
[Parameter]
public string Title { get; set; }
/// <summary>
/// Gets the choose class list.
/// </summary>
/// <value>The choose class list.</value>
ClassList ChooseClassList => ClassList.Create("rz-fileupload-choose rz-button rz-secondary")
.AddDisabled(Disabled);
/// <summary>
/// Gets the button class list.
/// </summary>
/// <value>The button class list.</value>
ClassList ButtonClassList => ClassList.Create("rz-button rz-button-icon-only rz-base rz-shade-default")
.AddDisabled(Disabled);
string ChooseClass => ClassList.Create("rz-fileupload-choose rz-button rz-secondary")
.AddDisabled(Disabled)
.ToString();
string ButtonClass => ClassList.Create("rz-button rz-button-icon-only rz-base rz-shade-default")
.AddDisabled(Disabled)
.ToString();
/// <inheritdoc />
protected override string GetComponentCssClass()
{
return GetClassList("rz-fileupload").ToString();
}
protected override string GetComponentCssClass() => GetClassList("rz-fileupload").ToString();
/// <summary>
/// Gets file input reference.

View File

@@ -167,9 +167,10 @@ namespace Radzen.Blazor
}
/// <inheritdoc />
protected override string GetComponentCssClass()
{
return ClassList.Create($"rz-form-field rz-variant-{Enum.GetName(typeof(Variant), Variant).ToLowerInvariant()}").AddDisabled(disabled).Add("rz-floating-label", AllowFloatingLabel == true).ToString();
}
protected override string GetComponentCssClass() => ClassList.Create("rz-form-field")
.AddVariant(Variant)
.AddDisabled(disabled)
.Add("rz-floating-label", AllowFloatingLabel)
.ToString();
}
}
}

View File

@@ -223,7 +223,7 @@ namespace Radzen.Blazor
if (IsJSRuntimeAvailable)
{
JSRuntime.InvokeVoidAsync("Radzen.destroyMap", UniqueID);
JSRuntime.InvokeVoid("Radzen.destroyMap", UniqueID);
}
}
}

View File

@@ -432,7 +432,7 @@ namespace Radzen.Blazor
if (Visible && IsJSRuntimeAvailable)
{
JSRuntime.InvokeVoidAsync("Radzen.destroyEditor", ContentEditable);
JSRuntime.InvokeVoid("Radzen.destroyEditor", ContentEditable);
}
}

View File

@@ -34,16 +34,8 @@ namespace Radzen.Blazor
}
/// <inheritdoc />
protected override string GetComponentCssClass()
{
var classList = ClassList.Create("rz-layout");
if (themeService != null)
{
classList.Add($"rz-{themeService.Theme}");
}
return classList.ToString();
}
protected override string GetComponentCssClass() => ClassList.Create("rz-layout")
.Add($"rz-{themeService.Theme}", themeService != null)
.ToString();
}
}

View File

@@ -4,7 +4,7 @@
{
<ul @ref=@Element style=@Style @attributes=@Attributes class=@GetCssClass() id=@GetId()
tabindex="0" @onkeydown="@OnKeyPress" @onkeydown:preventDefault=preventKeyPress @onkeydown:stopPropagation
@onfocus=@(args => {focusedIndex = focusedIndex == -1 ? 0: focusedIndex; currentItems = items.Where(i => i.Visible && !i.Disabled).ToList();})>
@onfocus=@this.AsNonRenderingEventHandler(OnFocus)>
@if (Responsive)
{
<li class="rz-menu-toggle-item">

View File

@@ -1,6 +1,6 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.JSInterop;
using Radzen.Blazor.Rendering;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -40,26 +40,10 @@ namespace Radzen.Blazor
private bool IsOpen { get; set; } = false;
/// <inheritdoc />
protected override string GetComponentCssClass()
{
var classList = new List<string>();
classList.Add("rz-menu");
if (Responsive)
{
if (IsOpen)
{
classList.Add("rz-menu-open");
}
else
{
classList.Add("rz-menu-closed");
}
}
return string.Join(" ", classList);
}
protected override string GetComponentCssClass() => ClassList.Create("rz-menu")
.Add("rz-menu-open", Responsive && IsOpen)
.Add("rz-menu-closed", Responsive && !IsOpen)
.ToString();
void OnToggle()
{
@@ -73,7 +57,6 @@ namespace Radzen.Blazor
[Parameter]
public EventCallback<MenuItemEventArgs> Click { get; set; }
[Inject]
NavigationManager NavigationManager { get; set; }
@@ -244,5 +227,10 @@ namespace Radzen.Blazor
base.Dispose();
NavigationManager.LocationChanged -= OnLocationChanged;
}
void OnFocus()
{
focusedIndex = focusedIndex == -1 ? 0 : focusedIndex;
}
}
}
}

View File

@@ -5,7 +5,7 @@
{
var classes = GetMessageCssClasses();
<div aria-live="polite" class="rz-notification-item-wrapper @classes.Item1" style="@(Message.Click != null || Message.CloseOnClick ? "cursor: pointer;" : "") @Style">
<div aria-live="polite" class="rz-notification-item-wrapper rz-open @classes.Item1" style="@(Message.Click != null || Message.CloseOnClick ? "cursor: pointer;" : "") @Style">
<div class="rz-notification-item">
<div class="rz-notification-message-wrapper">
<span class="notranslate rzi rz-notification-icon @classes.Item2" @onclick="NotificationClicked"></span>

View File

@@ -3,7 +3,7 @@
{
<div @ref="@Element" @attributes="Attributes" class="@GetCssClass()" style="@Style" id="@GetId()"
@onkeydown="@OnKeyDown" @onkeydown:preventDefault="preventKeyDown" @onkeydown:stopPropagation="true" tabindex=0
@onfocus=@OnFocus>
@onfocus=@this.AsNonRenderingEventHandler(OnFocus)>
@if(ShowPagingSummary)
{
<span class="rz-pager-summary">

View File

@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.JSInterop;
using Radzen.Blazor.Rendering;
namespace Radzen.Blazor
{
@@ -18,26 +19,11 @@ namespace Radzen.Blazor
/// </example>
public partial class RadzenPager : RadzenComponent
{
static readonly IDictionary<HorizontalAlign, string> HorizontalAlignCssClasses = new Dictionary<HorizontalAlign, string>
{
{HorizontalAlign.Center, "rz-align-center"},
{HorizontalAlign.Left, "rz-align-left"},
{HorizontalAlign.Right, "rz-align-right"},
{HorizontalAlign.Justify, "rz-align-justify"}
};
/// <inheritdoc />
protected override string GetComponentCssClass()
{
var additionalClasses = new List<string>();
if (Density == Density.Compact)
{
additionalClasses.Add("rz-density-compact");
}
return $"rz-pager rz-unselectable-text rz-helper-clearfix {HorizontalAlignCssClasses[HorizontalAlign]} {String.Join(" ", additionalClasses)}";
}
protected override string GetComponentCssClass() => ClassList.Create("rz-pager rz-unselectable-text rz-helper-clearfix")
.Add("rz-density-compact", Density == Density.Compact)
.AddHorizontalAlign(HorizontalAlign)
.ToString();
/// <summary>
/// Gets or sets the pager's first page button's title attribute.
@@ -550,7 +536,7 @@ namespace Radzen.Blazor
bool shouldFocus;
void OnFocus(FocusEventArgs args)
void OnFocus()
{
focusedIndex = focusedIndex == -3 ? 0 : focusedIndex;

View File

@@ -1,4 +1,5 @@
@inherits RadzenComponentWithChildren
@using Radzen.Blazor.Rendering
@inherits RadzenComponentWithChildren
@if (Visible)
{
<div @ref="@Element" style="@Style" @attributes="Attributes" class="@GetCssClass()" id="@GetId()">
@@ -27,7 +28,7 @@
</a>
}
</div>
<div class="rz-panel-content-wrapper" role="region" aria-hidden="false" style="@contentStyle">
<Expander Expanded=@(!collapsed) CssClass="rz-panel-content-wrapper">
<div class="rz-panel-content">
@ChildContent
</div>
@@ -37,14 +38,14 @@
@FooterTemplate
</div>
}
</div>
</Expander>
@if (SummaryTemplate != null)
{
<div class="rz-panel-content-wrapper" role="region" aria-hidden="false" style="@summaryContentStyle">
<Expander Expanded=@collapsed>
<div class="rz-panel-content rz-panel-content-summary">
@SummaryTemplate
</div>
</div>
</Expander>
}
</div>
}

View File

@@ -1,5 +1,6 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Radzen.Blazor.Rendering;
using System.Threading.Tasks;
namespace Radzen.Blazor
@@ -130,14 +131,9 @@ namespace Radzen.Blazor
[Parameter]
public string CollapseAriaLabel { get; set; } = null;
string contentStyle = "display: block;";
string summaryContentStyle = "display: none";
async System.Threading.Tasks.Task Toggle(MouseEventArgs args)
async Task Toggle(MouseEventArgs args)
{
collapsed = !collapsed;
contentStyle = collapsed ? "display: none;" : "display: block;";
summaryContentStyle = !collapsed ? "display: none" : "display: block";
if (collapsed)
{
@@ -150,6 +146,15 @@ namespace Radzen.Blazor
StateHasChanged();
}
string ContentClass => ClassList.Create("rz-panel-content-wrapper")
.Add("rz-open", !collapsed)
.Add("rz-close", collapsed)
.ToString();
string SummaryClass => ClassList.Create("rz-panel-content-wrapper")
.Add("rz-open", collapsed)
.Add("rz-close", !collapsed)
.ToString();
/// <inheritdoc />
protected override void OnInitialized()
@@ -169,18 +174,6 @@ namespace Radzen.Blazor
await base.SetParametersAsync(parameters);
}
/// <summary>
/// Called when parameters set asynchronous.
/// </summary>
/// <returns>Task.</returns>
protected override Task OnParametersSetAsync()
{
contentStyle = collapsed ? "display: none;" : "display: block;";
summaryContentStyle = !collapsed ? "display: none" : "display: block";
return base.OnParametersSetAsync();
}
bool preventKeyPress = false;
async Task OnKeyPress(KeyboardEventArgs args, Task task)
{

View File

@@ -1,13 +1,13 @@
@inherits RadzenComponentWithChildren
@inject NavigationManager UriHelper
@if (Visible)
{
<ul @ref="@Element" style="@Style" @attributes="Attributes" class="@GetCssClass()" id="@GetId()"
tabindex="0" @onkeydown="@OnKeyPress" @onkeydown:preventDefault=preventKeyPress @onkeydown:stopPropagation
@onfocus=@(args => { focusedIndex = focusedIndex == -1 ? 0: focusedIndex; currentItems = currentItems != null ? currentItems : items.Where(i => i.Visible).ToList();})>
<ul @ref=@Element style=@Style @attributes=@Attributes class=@GetCssClass() id=@GetId() tabindex="0"
@onkeydown=@OnKeyPress
@onkeydown:preventDefault=preventKeyPress @onkeydown:stopPropagation
@onfocus=@this.AsNonRenderingEventHandler(OnFocus)>
<CascadingValue Value=this>
@ChildContent
</CascadingValue>
</ul>
}
}

View File

@@ -56,7 +56,6 @@ namespace Radzen.Blazor
[Parameter]
public bool ShowArrow { get; set; } = true;
internal List<RadzenPanelMenuItem> items = new List<RadzenPanelMenuItem>();
/// <summary>
@@ -65,36 +64,12 @@ namespace Radzen.Blazor
/// <param name="item">The item.</param>
public void AddItem(RadzenPanelMenuItem item)
{
if (items.IndexOf(item) == -1)
if (!items.Contains(item))
{
items.Add(item);
SelectItem(item);
StateHasChanged();
}
}
/// <inheritdoc />
protected override void OnInitialized()
{
UriHelper.LocationChanged += UriHelper_OnLocationChanged;
}
private void UriHelper_OnLocationChanged(object sender, Microsoft.AspNetCore.Components.Routing.LocationChangedEventArgs e)
{
var allExpandedItems = items.Concat(items.SelectManyRecursive(i => i.ExpandedInternal ? i.items : Enumerable.Empty<RadzenPanelMenuItem>()));
foreach (var item in allExpandedItems)
{
SelectItem(item);
}
}
/// <inheritdoc />
public override void Dispose()
{
base.Dispose();
UriHelper.LocationChanged -= UriHelper_OnLocationChanged;
}
internal void CollapseAll(IEnumerable<RadzenPanelMenuItem> itemsToSkip)
{
items.Concat(items.SelectManyRecursive(i => i.items))
@@ -112,85 +87,6 @@ namespace Radzen.Blazor
await base.SetParametersAsync(parameters);
}
bool ShouldMatch(RadzenPanelMenuItem item)
{
if (string.IsNullOrEmpty(item.Path))
{
return false;
}
var currentAbsoluteUrl = UriHelper.ToAbsoluteUri(UriHelper.Uri).AbsoluteUri;
var absoluteUrl = UriHelper.ToAbsoluteUri(item.Path).AbsoluteUri;
if (EqualsHrefExactlyOrIfTrailingSlashAdded(absoluteUrl, currentAbsoluteUrl))
{
return true;
}
if (item.Path == "/")
{
return false;
}
var match = item.Match != NavLinkMatch.Prefix ? item.Match : Match;
if (match == NavLinkMatch.Prefix && IsStrictlyPrefixWithSeparator(currentAbsoluteUrl, absoluteUrl))
{
return true;
}
return false;
}
private static bool EqualsHrefExactlyOrIfTrailingSlashAdded(string absoluteUrl, string currentAbsoluteUrl)
{
if (string.Equals(currentAbsoluteUrl, absoluteUrl, StringComparison.OrdinalIgnoreCase))
{
return true;
}
if (currentAbsoluteUrl.Length == absoluteUrl.Length - 1)
{
if (absoluteUrl[absoluteUrl.Length - 1] == '/'
&& absoluteUrl.StartsWith(currentAbsoluteUrl, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
private static bool IsSeparator(char c)
{
return c == '?' || c == '/' || c == '#';
}
private static bool IsStrictlyPrefixWithSeparator(string value, string prefix)
{
var prefixLength = prefix.Length;
if (value.Length > prefixLength)
{
return value.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)
&& (
prefixLength == 0
|| IsSeparator(prefix[prefixLength - 1])
|| IsSeparator(value[prefixLength])
);
}
else
{
return false;
}
}
internal void SelectItem(RadzenPanelMenuItem item)
{
var selected = ShouldMatch(item);
item.Select(selected);
}
/// <inheritdoc />
protected override string GetComponentCssClass()
{
@@ -204,14 +100,12 @@ namespace Radzen.Blazor
List<RadzenPanelMenuItem> currentItems;
bool preventKeyPress = false;
async Task OnKeyPress(KeyboardEventArgs args)
{
var key = args.Code != null ? args.Code : args.Key;
var key = args.Code ?? args.Key;
if (currentItems == null)
{
currentItems = items.Where(i => i.Visible).ToList();
}
currentItems ??= [.. items.Where(i => i.Visible)];
if (key == "ArrowUp" || key == "ArrowDown")
{
@@ -223,8 +117,8 @@ namespace Radzen.Blazor
currentItems = (parentItem.ParentItem != null ? parentItem.ParentItem.items : parentItem.Parent.items).ToList();
focusedIndex = currentItems.IndexOf(parentItem);
}
else if (key == "ArrowDown" && currentItems.ElementAtOrDefault(focusedIndex) != null &&
currentItems.ElementAtOrDefault(focusedIndex).ExpandedInternal && currentItems.ElementAtOrDefault(focusedIndex).items.Any())
else if (key == "ArrowDown" && currentItems.ElementAtOrDefault(focusedIndex) != null &&
currentItems.ElementAtOrDefault(focusedIndex).IsExpanded && currentItems.ElementAtOrDefault(focusedIndex).items.Any())
{
currentItems = currentItems.ElementAtOrDefault(focusedIndex).items.Where(i => i.Visible).ToList();
focusedIndex = 0;
@@ -236,7 +130,7 @@ namespace Radzen.Blazor
focusedIndex = parentItem != null ? currentItems.IndexOf(parentItem) + 1 : focusedIndex;
}
else if (key == "ArrowUp" && currentItems.ElementAtOrDefault(focusedIndex - 1) != null &&
currentItems.ElementAtOrDefault(focusedIndex - 1).ExpandedInternal && currentItems.ElementAtOrDefault(focusedIndex - 1).items.Any())
currentItems.ElementAtOrDefault(focusedIndex - 1).IsExpanded && currentItems.ElementAtOrDefault(focusedIndex - 1).items.Any())
{
currentItems = currentItems.ElementAtOrDefault(focusedIndex - 1).items.Where(i => i.Visible).ToList();
focusedIndex = currentItems.Count - 1;
@@ -261,15 +155,15 @@ namespace Radzen.Blazor
{
var item = currentItems[focusedIndex];
if (item.items.Any())
if (item.items.Count > 0)
{
await item.Toggle();
currentItems = (item.ExpandedInternal ?
currentItems = (item.IsExpanded ?
item.items :
item.ParentItem != null ? item.ParentItem.items : item.Parent.items).Where(i => i.Visible).ToList();
focusedIndex = item.ExpandedInternal ? 0 : currentItems.IndexOf(item);
focusedIndex = item.IsExpanded ? 0 : currentItems.IndexOf(item);
}
else
{
@@ -288,6 +182,11 @@ namespace Radzen.Blazor
{
preventKeyPress = false;
}
if (preventKeyPress)
{
StateHasChanged();
}
}
internal bool IsFocused(RadzenPanelMenuItem item)
@@ -302,5 +201,18 @@ namespace Radzen.Blazor
focusedIndex = -1;
currentItems = null;
}
void OnFocus()
{
currentItems ??= [.. items.Where(i => i.Visible)];
if (focusedIndex == -1)
{
Console.WriteLine("OnFocus");
focusedIndex = 0;
StateHasChanged();
}
}
}
}

View File

@@ -1,13 +1,13 @@
@using Microsoft.AspNetCore.Components.Routing
@inject NavigationManager UriHelper
@using Radzen.Blazor.Rendering
@using Microsoft.AspNetCore.Components.Routing
@inherits RadzenComponent
@if (Visible)
{
<li @ref="@Element" id="@GetId()" style="@Style" @attributes="Attributes" class="@GetItemCssClass()" @onclick="@OnClick" @onclick:stopPropagation>
<div class=@(Selected ? "rz-navigation-item-wrapper rz-navigation-item-wrapper-active" : "rz-navigation-item-wrapper")>
<li @ref=@Element id=@GetId() style=@Style @attributes=@Attributes class=@GetCssClass() @onclick=@(this.AsNonRenderingEventHandler<MouseEventArgs>(OnClick)) @onclick:stopPropagation>
<div class=@WrapperClass>
@if (Path != null)
{
<NavLink tabindex="-1" target="@Target" class=@(Selected ? "rz-navigation-item-link rz-navigation-item-link-active" : "rz-navigation-item-link") style="@(Parent?.DisplayStyle == MenuItemDisplayStyle.Icon?"margin-inline-end:0px;":"")" href="@Path" Match="@Match">
<NavLink tabindex="-1" target="@Target" class=@LinkClass style="@(Parent?.DisplayStyle == MenuItemDisplayStyle.Icon?"margin-inline-end:0px;":"")" href="@Path" Match="@Match">
@if (!string.IsNullOrEmpty(Icon) && (Parent?.DisplayStyle == MenuItemDisplayStyle.Icon || Parent?.DisplayStyle == MenuItemDisplayStyle.IconAndText))
{
<i class="notranslate rzi rz-navigation-item-icon" style="@getIconStyle()">@Icon</i>
@@ -24,9 +24,9 @@
{
<span class="rz-navigation-item-text" @onclick="@Toggle">@Text</span>
}
@if (items.Any() && Parent?.ShowArrow == true)
@if (ChildContent != null && Parent?.ShowArrow == true)
{
<i class="notranslate rzi rz-navigation-item-icon-children" style="@getStyle()" @onclick="@Toggle" @onclick:preventDefault>keyboard_arrow_down</i>
<i class=@ToggleClass @onclick="@Toggle" @onclick:preventDefault>keyboard_arrow_down</i>
}
</NavLink>
}
@@ -49,20 +49,22 @@
{
<span class="rz-navigation-item-text">@Text</span>
}
@if (items.Any() && Parent?.ShowArrow == true)
@if (ChildContent != null && Parent?.ShowArrow == true)
{
<i class="notranslate rzi rz-navigation-item-icon-children" style="@getStyle()">keyboard_arrow_down</i>
<i class=@ToggleClass>keyboard_arrow_down</i>
}
</div>
}
</div>
@if (ChildContent != null)
{
<ul class="rz-navigation-menu" style="@getItemStyle()">
<CascadingValue Value=this>
@ChildContent
</CascadingValue>
</ul>
<Expander Expanded=@expanded>
<ul class="rz-navigation-menu">
<CascadingValue Value=this>
@ChildContent
</CascadingValue>
</ul>
</Expander>
}
</li>
}

View File

@@ -1,9 +1,9 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Routing;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.JSInterop;
using Radzen.Blazor.Rendering;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Radzen.Blazor
@@ -14,10 +14,9 @@ namespace Radzen.Blazor
public partial class RadzenPanelMenuItem : RadzenComponent
{
/// <inheritdoc />
protected override string GetComponentCssClass()
{
return "rz-navigation-item";
}
protected override string GetComponentCssClass() => ClassList.Create("rz-navigation-item")
.Add("rz-state-focused", Parent?.IsFocused(this) == true)
.ToString();
/// <summary>
/// Gets or sets the target.
@@ -103,7 +102,9 @@ namespace Radzen.Blazor
[Parameter]
public bool Expanded { get; set; }
internal bool ExpandedInternal { get; set; }
private bool expanded;
internal bool IsExpanded => expanded;
/// <summary>
/// Gets or sets a value indicating whether this <see cref="RadzenPanelMenuItem"/> is selected.
@@ -119,9 +120,9 @@ namespace Radzen.Blazor
[Parameter]
public RenderFragment ChildContent { get; set; }
internal async System.Threading.Tasks.Task Toggle()
internal async Task Toggle()
{
if (!ExpandedInternal && !Parent.Multiple)
if (!expanded && !Parent.Multiple)
{
var itemsToSkip = new List<RadzenPanelMenuItem>();
var p = ParentItem;
@@ -134,44 +135,36 @@ namespace Radzen.Blazor
Parent.CollapseAll(itemsToSkip);
}
ExpandedInternal = !ExpandedInternal;
await ExpandedChanged.InvokeAsync(ExpandedInternal);
StateHasChanged();
expanded = !expanded;
await ExpandedChanged.InvokeAsync(expanded);
}
internal async System.Threading.Tasks.Task Collapse()
internal async Task Collapse()
{
if (ExpandedInternal)
if (expanded)
{
ExpandedInternal = false;
await ExpandedChanged.InvokeAsync(ExpandedInternal);
StateHasChanged();
expanded = false;
await ExpandedChanged.InvokeAsync(expanded);
}
}
string getStyle()
{
string deg = ExpandedInternal ? "180" : "0";
return $@"transform: rotate({deg}deg);";
}
string ToggleClass => ClassList.Create("notranslate rzi rz-navigation-item-icon-children")
.Add("rz-state-expanded", expanded)
.Add("rz-state-collapsed", !expanded)
.ToString();
string getIconStyle()
{
return $"{(Parent?.DisplayStyle == MenuItemDisplayStyle.Icon ? "margin-inline-end:0px;" : "")}{(!string.IsNullOrEmpty(IconColor) ? $"color:{IconColor}" : "")}";
}
string getItemStyle()
{
return ExpandedInternal ? "" : "display:none";
return $"{(Parent?.DisplayStyle == MenuItemDisplayStyle.Icon ? "margin-inline-end:0px;" : "")}{(!string.IsNullOrEmpty(IconColor) ? $"color:{IconColor}" : "")}";
}
void Expand()
{
ExpandedInternal = true;
expanded = true;
}
RadzenPanelMenu _parent;
/// <summary>
/// Gets or sets the click callback.
/// </summary>
@@ -179,55 +172,19 @@ namespace Radzen.Blazor
[Parameter]
public EventCallback<MenuItemEventArgs> Click { get; set; }
RadzenPanelMenuItem _parentItem;
/// <summary>
/// Gets or sets the parent item.
/// </summary>
/// <value>The parent item.</value>
[CascadingParameter]
public RadzenPanelMenuItem ParentItem
{
get
{
return _parentItem;
}
set
{
if (_parentItem != value)
{
_parentItem = value;
_parentItem.AddItem(this);
EnsureVisible();
}
}
}
public RadzenPanelMenuItem ParentItem { get; set;}
/// <summary>
/// Gets or sets the parent.
/// </summary>
/// <value>The parent.</value>
[CascadingParameter]
public RadzenPanelMenu Parent
{
get
{
return _parent;
}
set
{
if (_parent != value)
{
_parent = value;
if (ParentItem == null)
{
_parent.AddItem(this);
}
}
}
}
public RadzenPanelMenu Parent { get; set; }
internal List<RadzenPanelMenuItem> items = new List<RadzenPanelMenuItem>();
@@ -237,48 +194,179 @@ namespace Radzen.Blazor
/// <param name="item">The item.</param>
public void AddItem(RadzenPanelMenuItem item)
{
if (items.IndexOf(item) == -1)
if (!items.Contains(item))
{
items.Add(item);
Parent.SelectItem(item);
StateHasChanged();
}
}
/// <summary>
/// Selects the specified item by value.
/// </summary>
/// <param name="value">if set to <c>true</c> [value].</param>
public void Select(bool value)
{
Selected = value;
StateHasChanged();
}
void EnsureVisible()
{
if (Selected)
if (selected)
{
var parent = ParentItem;
while (parent != null)
{
parent.Expand();
if (parent.ParentItem == null)
{
parent.StateHasChanged();
}
parent = parent.ParentItem;
}
}
}
[Inject]
NavigationManager NavigationManager { get; set; }
/// <inheritdoc />
protected override void OnInitialized()
{
expanded = Expanded;
selected = Selected;
NavigationManager.LocationChanged += OnLocationChanged;
if (ParentItem != null)
{
ParentItem.AddItem(this);
}
else if (Parent != null)
{
Parent.AddItem(this);
}
SyncWithNavigationManager();
}
string WrapperClass => ClassList.Create("rz-navigation-item-wrapper")
.Add("rz-navigation-item-wrapper-active", selected)
.ToString();
string LinkClass => ClassList.Create("rz-navigation-item-link")
.Add("rz-navigation-item-link-active", selected)
.ToString();
private bool selected = false;
private void OnLocationChanged(object sender, LocationChangedEventArgs e)
{
SyncWithNavigationManager();
}
private void SyncWithNavigationManager()
{
var matches = ShouldMatch();
if (matches != selected)
{
selected = matches;
EnsureVisible();
StateHasChanged();
}
}
bool ShouldMatch()
{
if (string.IsNullOrEmpty(Path))
{
return false;
}
var currentAbsoluteUrl = NavigationManager.ToAbsoluteUri(NavigationManager.Uri).AbsoluteUri;
var absoluteUrl = NavigationManager.ToAbsoluteUri(Path).AbsoluteUri;
if (EqualsHrefExactlyOrIfTrailingSlashAdded(absoluteUrl, currentAbsoluteUrl))
{
return true;
}
if (Path == "/")
{
return false;
}
var match = Match != NavLinkMatch.Prefix ? Match : Parent.Match;
if (match == NavLinkMatch.Prefix && IsStrictlyPrefixWithSeparator(currentAbsoluteUrl, absoluteUrl))
{
return true;
}
return false;
}
private static bool EqualsHrefExactlyOrIfTrailingSlashAdded(string absoluteUrl, string currentAbsoluteUrl)
{
if (string.Equals(currentAbsoluteUrl, absoluteUrl, StringComparison.OrdinalIgnoreCase))
{
return true;
}
if (currentAbsoluteUrl.Length == absoluteUrl.Length - 1)
{
if (absoluteUrl[^1] == '/' && absoluteUrl.StartsWith(currentAbsoluteUrl, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
private static bool IsSeparator(char c)
{
return c == '?' || c == '/' || c == '#';
}
private static bool IsStrictlyPrefixWithSeparator(string value, string prefix)
{
var prefixLength = prefix.Length;
if (value.Length > prefixLength)
{
return value.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)
&& (
prefixLength == 0
|| IsSeparator(prefix[prefixLength - 1])
|| IsSeparator(value[prefixLength])
);
}
else
{
return false;
}
}
/// <inheritdoc />
public override async Task SetParametersAsync(ParameterView parameters)
{
if (parameters.DidParameterChange(nameof(Expanded), Expanded))
{
ExpandedInternal = parameters.GetValueOrDefault<bool>(nameof(Expanded));
}
var expandedChanged = parameters.DidParameterChange(nameof(Expanded), Expanded);
var selectedChanged = parameters.DidParameterChange(nameof(Selected), Selected);
await base.SetParametersAsync(parameters);
if (expandedChanged)
{
expanded = Expanded;
}
if (selectedChanged)
{
selected = Selected;
if (selected)
{
EnsureVisible();
}
}
}
/// <summary>
@@ -316,17 +404,12 @@ namespace Radzen.Blazor
}
}
internal string GetItemCssClass()
{
return $"{GetCssClass()} {(Parent.IsFocused(this) ? "rz-state-focused" : "")}".Trim();
}
/// <inheritdoc />
public override void Dispose()
{
base.Dispose();
items.Remove(this);
NavigationManager.LocationChanged -= OnLocationChanged;
if (Parent != null)
{

View File

@@ -15,7 +15,7 @@
</div>
@if (ShowIcon)
{
<i class="notranslate rzi rz-navigation-item-icon-children" style="@iconStyle">keyboard_arrow_down</i>
<i class=@ToggleClass>keyboard_arrow_down</i>
}
</div>
</div>

View File

@@ -4,6 +4,7 @@ using Microsoft.JSInterop;
using System.Collections.Generic;
using System;
using System.Threading.Tasks;
using Radzen.Blazor.Rendering;
namespace Radzen.Blazor
{
@@ -50,7 +51,6 @@ namespace Radzen.Blazor
public bool ShowIcon { get; set; } = true;
string contentStyle = "display:none;position:absolute;z-index:1;";
string iconStyle = "transform: rotate(0deg);";
/// <summary>
/// Toggles the menu open/close state.
@@ -58,18 +58,23 @@ namespace Radzen.Blazor
/// <param name="args">The <see cref="MouseEventArgs"/> instance containing the event data.</param>
public async Task Toggle(MouseEventArgs args)
{
contentStyle = contentStyle.IndexOf("display:none;") != -1 ? "display:block;" : "display:none;position:absolute;z-index:1;";
iconStyle = iconStyle.IndexOf("rotate(0deg)") != -1 ? "transform: rotate(-180deg);" : "transform: rotate(0deg);";
contentStyle = Collapsed ? "display:block;" : "display:none;position:absolute;z-index:1;";
await InvokeAsync(StateHasChanged);
}
bool Collapsed => contentStyle.Contains("display:none;", StringComparison.CurrentCulture);
string ToggleClass => ClassList.Create("notranslate rzi rz-navigation-item-icon-children")
.Add("rz-state-expanded", !Collapsed)
.Add("rz-state-collapsed", Collapsed)
.ToString();
/// <summary>
/// Closes this instance.
/// </summary>
public void Close()
{
contentStyle = "display:none;";
iconStyle = "transform: rotate(0deg);";
StateHasChanged();
}
@@ -93,7 +98,7 @@ namespace Radzen.Blazor
{
preventKeyPress = true;
if (!contentStyle.Contains("display:none;") && focusedIndex >= 0 && focusedIndex < items.Count)
if (!Collapsed && focusedIndex >= 0 && focusedIndex < items.Count)
{
var item = items[focusedIndex];
@@ -110,7 +115,7 @@ namespace Radzen.Blazor
{
await Toggle(new MouseEventArgs());
if (!contentStyle.Contains("display:none;"))
if (!Collapsed)
{
focusedIndex = focusedIndex != -1 ? focusedIndex : 0;
}

View File

@@ -1,6 +1,6 @@
using Microsoft.AspNetCore.Components;
using Radzen.Blazor.Rendering;
using System;
using System.Collections.Generic;
namespace Radzen.Blazor
{
@@ -15,28 +15,11 @@ namespace Radzen.Blazor
public partial class RadzenProgressBar
{
/// <inheritdoc />
protected override string GetComponentCssClass()
{
var classList = new List<string>()
{
"rz-progressbar"
};
switch (Mode)
{
case ProgressBarMode.Determinate:
classList.Add("rz-progressbar-determinate");
break;
case ProgressBarMode.Indeterminate:
classList.Add("rz-progressbar-indeterminate");
break;
}
classList.Add($"rz-progressbar-{ProgressBarStyle.ToString().ToLowerInvariant()}");
return string.Join(" ", classList);
}
protected override string GetComponentCssClass() => ClassList.Create("rz-progressbar")
.Add("rz-progressbar-determinate", Mode == ProgressBarMode.Determinate)
.Add("rz-progressbar-indeterminate", Mode == ProgressBarMode.Indeterminate)
.Add($"rz-progressbar-{ProgressBarStyle.ToString().ToLowerInvariant()}")
.ToString();
/// <summary>
/// Gets or sets the template.
/// </summary>
@@ -105,4 +88,4 @@ namespace Radzen.Blazor
/// </summary>
protected double NormalizedValue => Math.Min(Math.Max((Value - Min) / (Max - Min), 0), 1);
}
}
}

View File

@@ -1,5 +1,5 @@
using Microsoft.AspNetCore.Components;
using System.Collections.Generic;
using Radzen.Blazor.Rendering;
namespace Radzen.Blazor
{
@@ -14,48 +14,21 @@ namespace Radzen.Blazor
public partial class RadzenProgressBarCircular : RadzenProgressBar
{
/// <inheritdoc />
protected override string GetComponentCssClass()
protected override string GetComponentCssClass() => ClassList.Create("rz-progressbar-circular")
.Add("rz-progressbar-determinate", Mode == ProgressBarMode.Determinate)
.Add("rz-progressbar-indeterminate", Mode == ProgressBarMode.Indeterminate)
.Add($"rz-progressbar-{ProgressBarStyle.ToString().ToLowerInvariant()}")
.Add($"rz-progressbar-circular-{CircleSize}")
.ToString();
string CircleSize => Size switch
{
var classList=new List<string>()
{
"rz-progressbar-circular"
};
switch (Mode)
{
case ProgressBarMode.Determinate:
classList.Add("rz-progressbar-determinate");
break;
case ProgressBarMode.Indeterminate:
classList.Add("rz-progressbar-indeterminate");
break;
}
classList.Add($"rz-progressbar-{ProgressBarStyle.ToString().ToLowerInvariant()}");
classList.Add($"rz-progressbar-circular-{GetCircleSize()}");
return string.Join(" ", classList);
}
/// <summary>
/// Gets the circle size.
/// </summary>
protected string GetCircleSize()
{
switch (Size)
{
case ProgressBarCircularSize.Medium:
return "md";
case ProgressBarCircularSize.Large:
return "lg";
case ProgressBarCircularSize.Small:
return "sm";
case ProgressBarCircularSize.ExtraSmall:
return "xs";
default:
return string.Empty;
}
}
ProgressBarCircularSize.Medium => "md",
ProgressBarCircularSize.Large => "lg",
ProgressBarCircularSize.Small => "sm",
ProgressBarCircularSize.ExtraSmall => "xs",
_ => string.Empty,
};
/// <summary>
/// Gets or sets the size.
@@ -64,4 +37,4 @@ namespace Radzen.Blazor
[Parameter]
public ProgressBarCircularSize Size { get; set; } = ProgressBarCircularSize.Medium;
}
}
}

View File

@@ -14,16 +14,19 @@
}
@if (Visible)
{
<div @ref="@Element" style="@Style" @attributes="Attributes" class="@GetCssClass()" id="@GetId()">
<div @ref="@Element" style="@Style" @attributes="Attributes" class="@GetCssClass()" id="@GetId()"
role="radiogroup" @onfocus=@OnFocus @onblur=@OnBlur
tabindex="@(Disabled ? "-1" : $"{TabIndex}")" @onkeydown="@(args => OnKeyPress(args))" @onkeydown:preventDefault=preventKeyPress @onkeydown:stopPropagation>
<RadzenStack Orientation="@Orientation" JustifyContent="@JustifyContent" AlignItems="@AlignItems" Gap="@Gap" Wrap="@Wrap">
@foreach (var item in allItems.Where(i => i.Visible))
{
<div @ref="@item.Element" id="@item.GetItemId()" @onclick="@(args => SelectItem(item))" @attributes="item.Attributes" class="@item.GetItemCssClass()" style="@item.Style">
<div class="rz-radiobutton" @onkeypress="@(args => OnKeyPress(args, SelectItem(item)))" @onkeypress:preventDefault=preventKeyPress @onkeypress:stopPropagation tabindex="@(Disabled || item.Disabled ? "-1" : $"{TabIndex}")">
<div @ref="@item.Element" id="@item.GetItemId()" @onclick="@(args => SelectItem(item))" @attributes="item.Attributes" class="@item.GetItemCssClass()" style="@item.Style" role="radio" aria-checked=@(IsSelected(item)? "true" : "false") aria-label="@item.Text">
<div class="rz-radiobutton">
<div class="rz-helper-hidden-accessible">
<input type="radio" disabled="@Disabled" name="@Name" value="@item.Value" tabindex="-1" aria-label="@(item.Text + " " + item.Value)" @attributes="item.InputAttributes">
</div>
<div class=@ItemClassList(item)>
<span class=@IconClassList(item)></span>
<div class=@ItemClass(item)>
<span class=@IconClass(item)></span>
</div>
</div>
@if (item.Template != null)
@@ -38,5 +41,6 @@
}
</div>
}
</RadzenStack>
</div>
}

View File

@@ -1,6 +1,7 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Radzen.Blazor.Rendering;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
@@ -24,12 +25,15 @@ namespace Radzen.Blazor
/// </example>
public partial class RadzenRadioButtonList<TValue> : FormComponent<TValue>
{
ClassList ItemClassList(RadzenRadioButtonListItem<TValue> item) => ClassList.Create("rz-radiobutton-box")
string ItemClass(RadzenRadioButtonListItem<TValue> item) => ClassList.Create("rz-radiobutton-box")
.Add("rz-state-active", IsSelected(item))
.AddDisabled(Disabled || item.Disabled);
.Add("rz-state-focused", IsFocused(item) && focused)
.AddDisabled(Disabled || item.Disabled)
.ToString();
ClassList IconClassList(RadzenRadioButtonListItem<TValue> item) => ClassList.Create("rz-radiobutton-icon")
.Add("notranslate rzi rzi-circle-on", IsSelected(item));
string IconClass(RadzenRadioButtonListItem<TValue> item) => ClassList.Create("rz-radiobutton-icon")
.Add("notranslate rzi rzi-circle-on", IsSelected(item))
.ToString();
/// <summary>
/// Gets or sets the value property.
/// </summary>
@@ -44,6 +48,34 @@ namespace Radzen.Blazor
[Parameter]
public string TextProperty { get; set; }
/// <summary>
/// Gets or sets the content justify.
/// </summary>
/// <value>The content justify.</value>
[Parameter]
public JustifyContent JustifyContent { get; set; } = JustifyContent.Start;
/// <summary>
/// Gets or sets the items alignment.
/// </summary>
/// <value>The items alignment.</value>
[Parameter]
public AlignItems AlignItems { get; set; } = AlignItems.Start;
/// <summary>
/// Gets or sets the spacing between items
/// </summary>
/// <value>The spacing between items.</value>
[Parameter]
public string Gap { get; set; }
/// <summary>
/// Gets or sets the wrap.
/// </summary>
/// <value>The wrap.</value>
[Parameter]
public FlexWrap Wrap { get; set; } = FlexWrap.Wrap;
/// <summary>
/// Gets or sets the disabled property.
/// </summary>
@@ -58,29 +90,36 @@ namespace Radzen.Blazor
[Parameter]
public string VisibleProperty { get; set; }
IEnumerable<RadzenRadioButtonListItem<TValue>> allItems
List<RadzenRadioButtonListItem<TValue>> allItems;
void UpdateAllItems()
{
get
allItems = items.Concat((Data != null ? Data.Cast<object>() : Enumerable.Empty<object>()).Select(i =>
{
return items.Concat((Data != null ? Data.Cast<object>() : Enumerable.Empty<object>()).Select(i =>
var item = new RadzenRadioButtonListItem<TValue>();
item.SetText((string)PropertyAccess.GetItemOrValueFromProperty(i, TextProperty));
item.SetValue((TValue)PropertyAccess.GetItemOrValueFromProperty(i, ValueProperty));
if (DisabledProperty != null && PropertyAccess.TryGetItemOrValueFromProperty<bool>(i, DisabledProperty, out var disabledResult))
{
var item = new RadzenRadioButtonListItem<TValue>();
item.SetText((string)PropertyAccess.GetItemOrValueFromProperty(i, TextProperty));
item.SetValue((TValue)PropertyAccess.GetItemOrValueFromProperty(i, ValueProperty));
item.SetDisabled(disabledResult);
}
if (DisabledProperty != null && PropertyAccess.TryGetItemOrValueFromProperty<bool>(i, DisabledProperty, out var disabledResult))
{
item.SetDisabled(disabledResult);
}
if (VisibleProperty != null && PropertyAccess.TryGetItemOrValueFromProperty<bool>(i, VisibleProperty, out var visibleResult))
{
item.SetVisible(visibleResult);
}
if (VisibleProperty != null && PropertyAccess.TryGetItemOrValueFromProperty<bool>(i, VisibleProperty, out var visibleResult))
{
item.SetVisible(visibleResult);
}
return item;
})).ToList();
}
return item;
}));
}
/// <inheritdoc />
protected override void OnParametersSet()
{
base.OnParametersSet();
UpdateAllItems();
}
IEnumerable _data = null;
@@ -109,7 +148,10 @@ namespace Radzen.Blazor
/// <inheritdoc />
protected override string GetComponentCssClass()
{
return GetClassList(Orientation == Orientation.Horizontal ? "rz-radio-button-list-horizontal" : "rz-radio-button-list-vertical").ToString();
var horizontal = Orientation == Orientation.Horizontal;
return $"rz-radio-button-list rz-radio-button-list-{(horizontal ? "horizontal" : "vertical")}";
}
/// <summary>
@@ -137,6 +179,7 @@ namespace Radzen.Blazor
if (items.IndexOf(item) == -1)
{
items.Add(item);
UpdateAllItems();
StateHasChanged();
}
}
@@ -150,6 +193,7 @@ namespace Radzen.Blazor
if (items.Contains(item))
{
items.Remove(item);
UpdateAllItems();
try
{ InvokeAsync(StateHasChanged); }
catch { }
@@ -175,6 +219,8 @@ namespace Radzen.Blazor
if (Disabled || item.Disabled)
return;
focusedIndex = allItems.IndexOf(item);
Value = item.Value;
await ValueChanged.InvokeAsync(Value);
@@ -193,21 +239,68 @@ namespace Radzen.Blazor
StateHasChanged();
}
bool focused;
int focusedIndex = -1;
bool preventKeyPress = true;
async Task OnKeyPress(KeyboardEventArgs args, Task task)
async Task OnKeyPress(KeyboardEventArgs args)
{
var key = args.Code != null ? args.Code : args.Key;
if (key == "Space" || key == "Enter")
var item = allItems.ElementAtOrDefault(focusedIndex) ?? allItems.FirstOrDefault();
if (item == null) return;
if ((Orientation == Orientation.Horizontal && (key == "ArrowLeft" || key == "ArrowRight")) ||
(Orientation == Orientation.Vertical && (key == "ArrowUp" || key == "ArrowDown")))
{
preventKeyPress = true;
var direction = key == "ArrowLeft" || key == "ArrowUp" ? -1 : 1;
focusedIndex = Math.Clamp(focusedIndex + direction, 0, allItems.FindLastIndex(t => t.Visible && !t.Disabled));
while (allItems.ElementAtOrDefault(focusedIndex)?.Disabled == true)
{
focusedIndex = focusedIndex + direction;
}
}
else if (key == "Home" || key == "End")
{
preventKeyPress = true;
await task;
focusedIndex = key == "Home" ? 0 : allItems.Where(t => HasInvisibleBefore(item) ? true : t.Visible).Count() - 1;
}
else if (key == "Space" || key == "Enter")
{
preventKeyPress = true;
if (focusedIndex >= 0 && focusedIndex < allItems.Where(t => HasInvisibleBefore(item) ? true : t.Visible).Count())
{
await SelectItem(allItems.Where(t => HasInvisibleBefore(item) ? true : t.Visible).ToList()[focusedIndex]);
}
}
else
{
preventKeyPress = false;
}
}
bool HasInvisibleBefore(RadzenRadioButtonListItem<TValue> item)
{
return allItems.Take(allItems.IndexOf(item)).Any(t => !t.Visible && !t.Disabled);
}
bool IsFocused(RadzenRadioButtonListItem<TValue> item)
{
return allItems.IndexOf(item) == focusedIndex;
}
void OnFocus()
{
focusedIndex = focusedIndex == -1 ? 0 : focusedIndex;
focused = true;
}
void OnBlur()
{
focused = false;
}
}
}

View File

@@ -705,7 +705,7 @@ namespace Radzen.Blazor
if (IsJSRuntimeAvailable)
{
JSRuntime.InvokeVoidAsync("Radzen.destroyScheduler", Element);
JSRuntime.InvokeVoid("Radzen.destroyScheduler", Element);
}
}

View File

@@ -108,7 +108,7 @@ namespace Radzen.Blazor
if (IsJSRuntimeAvailable)
{
JSRuntime.InvokeVoidAsync("Radzen.destroySecurityCode", GetId(), Element);
JSRuntime.InvokeVoid("Radzen.destroySecurityCode", GetId(), Element);
}
}

View File

@@ -13,12 +13,14 @@
}
@if (Visible)
{
<div @ref="@Element" style="@Style" @attributes="Attributes" class="@GetCssClass()" id="@GetId()">
<div @ref="@Element" style="@Style" @attributes="Attributes" class="@GetCssClass()" id="@GetId()"
role="toolbar" aria-orientation="horizontal" @onfocus=@this.AsNonRenderingEventHandler(OnFocus) @onblur=@this.AsNonRenderingEventHandler(OnBlur)
tabindex="@(Disabled ? "-1" : $"{TabIndex}")" @onkeydown="@(args => OnKeyPress(args))" @onkeydown:preventDefault=preventKeyPress @onkeydown:stopPropagation>
@foreach (var item in allItems.Where(i => i.Visible))
{
<div @ref="@item.Element" id="@item.GetItemId()"
@onclick="@(args => SelectItem(item))" @onkeypress="@(args => OnKeyPress(args, SelectItem(item)))" @onkeypress:preventDefault=preventKeyPress @onkeypress:stopPropagation
@attributes="item.Attributes" style="@item.Style" class=@ButtonClassList(item) aria-label="@item.Text" tabindex="@(Disabled || item.Disabled ? "-1" : $"{TabIndex}")">
@onclick="@(args => SelectItem(item))"
@attributes="item.Attributes" style="@item.Style" class=@ButtonClass(item) aria-label="@item.Text" aria-selected=@(IsSelected(item)? "true" : "false")>
@if (item.Template != null)
{
@item.Template(item)

View File

@@ -26,11 +26,6 @@ namespace Radzen.Blazor
/// </example>
public partial class RadzenSelectBar<TValue> : FormComponent<TValue>, IRadzenSelectBar
{
private string getButtonSize()
{
return Size == ButtonSize.Medium ? "md" : Size == ButtonSize.Large ? "lg" : Size == ButtonSize.Small ? "sm" : "xs";
}
/// <summary>
/// Gets or sets the size. Set to <c>ButtonSize.Medium</c> by default.
/// </summary>
@@ -46,10 +41,12 @@ namespace Radzen.Blazor
public Orientation Orientation { get; set; } = Orientation.Horizontal;
ClassList ButtonClassList(RadzenSelectBarItem item)
=> ClassList.Create($"rz-button rz-button-{getButtonSize()} rz-button-text-only")
.Add("rz-state-active", IsSelected(item))
.AddDisabled(Disabled || item.Disabled);
string ButtonClass(RadzenSelectBarItem item) => ClassList.Create($"rz-button rz-button-text-only")
.AddButtonSize(Size)
.Add("rz-state-active", IsSelected(item))
.Add("rz-state-focused", IsFocused(item) && focused)
.AddDisabled(Disabled || item.Disabled)
.ToString();
/// <summary>
/// Gets or sets the value property.
@@ -65,19 +62,7 @@ namespace Radzen.Blazor
[Parameter]
public string TextProperty { get; set; }
IEnumerable<RadzenSelectBarItem> allItems
{
get
{
return items.Concat((Data != null ? Data.Cast<object>() : Enumerable.Empty<object>()).Select(i =>
{
var item = new RadzenSelectBarItem();
item.SetText((string)PropertyAccess.GetItemOrValueFromProperty(i, TextProperty));
item.SetValue(PropertyAccess.GetItemOrValueFromProperty(i, ValueProperty));
return item;
}));
}
}
List<RadzenSelectBarItem> allItems;
IEnumerable _data = null;
@@ -103,11 +88,29 @@ namespace Radzen.Blazor
}
/// <inheritdoc />
protected override string GetComponentCssClass()
=> GetClassList("rz-selectbutton rz-buttonset")
.Add($"rz-selectbutton-{(Orientation == Orientation.Vertical ? "vertical" : "horizontal")}")
.Add($"rz-buttonset-{items.Count}")
.ToString();
protected override void OnParametersSet()
{
base.OnParametersSet();
UpdateAllItems();
}
void UpdateAllItems()
{
allItems = items.Concat((Data != null ? Data.Cast<object>() : Enumerable.Empty<object>()).Select(i =>
{
var item = new RadzenSelectBarItem();
item.SetText((string)PropertyAccess.GetItemOrValueFromProperty(i, TextProperty));
item.SetValue(PropertyAccess.GetItemOrValueFromProperty(i, ValueProperty));
return item;
})).ToList();
}
/// <inheritdoc />
protected override string GetComponentCssClass() => GetClassList("rz-selectbar rz-buttonset")
.Add($"rz-selectbar-{(Orientation == Orientation.Vertical ? "vertical" : "horizontal")}")
.Add($"rz-buttonset-{allItems.Count}")
.ToString();
/// <summary>
/// Gets or sets a value indicating whether this <see cref="RadzenSelectBar{TValue}"/> is multiple.
@@ -134,6 +137,7 @@ namespace Radzen.Blazor
if (items.IndexOf(item) == -1)
{
items.Add(item);
UpdateAllItems();
StateHasChanged();
}
}
@@ -147,6 +151,7 @@ namespace Radzen.Blazor
if (items.Contains(item))
{
items.Remove(item);
UpdateAllItems();
if (!disposed)
{
try { InvokeAsync(StateHasChanged); } catch { }
@@ -192,6 +197,8 @@ namespace Radzen.Blazor
if (Disabled || item.Disabled)
return;
focusedIndex = allItems.IndexOf(item);
if (Multiple)
{
var type = typeof(TValue).IsGenericType ? typeof(TValue).GetGenericArguments()[0] : typeof(TValue);
@@ -229,21 +236,69 @@ namespace Radzen.Blazor
StateHasChanged();
}
bool focused;
int focusedIndex = -1;
bool preventKeyPress = true;
async Task OnKeyPress(KeyboardEventArgs args, Task task)
async Task OnKeyPress(KeyboardEventArgs args)
{
var key = args.Code != null ? args.Code : args.Key;
if (key == "Space" || key == "Enter")
var item = allItems.ElementAtOrDefault(focusedIndex) ?? allItems.FirstOrDefault();
if (item == null) return;
if (key == "ArrowLeft" || key == "ArrowRight")
{
preventKeyPress = true;
await task;
var direction = key == "ArrowLeft" ? -1 : 1;
focusedIndex = Math.Clamp(focusedIndex + direction, 0, allItems.FindLastIndex(t => t.Visible && !t.Disabled));
while (allItems.ElementAtOrDefault(focusedIndex)?.Disabled == true)
{
focusedIndex = focusedIndex + direction;
}
}
else if (key == "Home" || key == "End")
{
preventKeyPress = true;
focusedIndex = key == "Home" ? 0 : allItems.Where(t => HasInvisibleBefore(item) ? true : t.Visible).Count() - 1;
}
else if (key == "Space" || key == "Enter")
{
preventKeyPress = true;
if (focusedIndex >= 0 && focusedIndex < allItems.Where(t => HasInvisibleBefore(item) ? true : t.Visible).Count())
{
await SelectItem(allItems.Where(t => HasInvisibleBefore(item) ? true : t.Visible).ToList()[focusedIndex]);
}
}
else
{
preventKeyPress = false;
}
}
bool HasInvisibleBefore(RadzenSelectBarItem item)
{
return allItems.Take(allItems.IndexOf(item)).Any(t => !t.Visible && !t.Disabled);
}
bool IsFocused(RadzenSelectBarItem item)
{
return allItems.ToList().IndexOf(item) == focusedIndex;
}
void OnFocus()
{
focusedIndex = focusedIndex == -1 ? 0 : focusedIndex;
focused = true;
}
void OnBlur()
{
focused = false;
}
}
}

View File

@@ -104,7 +104,7 @@ namespace Radzen.Blazor
if (IsJSRuntimeAvailable)
{
JSRuntime.InvokeVoidAsync("Radzen.destroySlider", UniqueID, Element);
JSRuntime.InvokeVoid("Radzen.destroySlider", UniqueID, Element);
}
}

View File

@@ -5,7 +5,7 @@
{
<div @ref="@Element" style="@Style" @attributes="Attributes" class="@GetCssClass()" id="@GetId()"
tabindex="@(Disabled ? -1 : TabIndex)" @onkeydown="@OnKeyPress" @onkeydown:preventDefault=preventKeyPress @onkeydown:stopPropagation>
<button aria-label="@(ButtonAriaLabel ?? Text)" tabindex="-1" disabled="@IsDisabled" class="@getButtonCss()" type="@Enum.GetName(typeof(ButtonType), ButtonType).ToLower()" @onclick="@OnClick">
<button aria-label="@(ButtonAriaLabel ?? Text)" tabindex="-1" disabled="@IsDisabled" class=@ButtonClass type="@Enum.GetName(typeof(ButtonType), ButtonType).ToLower()" @onclick="@OnClick">
<span class="rz-button-box">
@if (ButtonContent != null)
{
@@ -43,7 +43,7 @@
}
</span>
</button>
<button tabindex="-1" disabled="@IsDisabled" onclick="@OpenPopupScript()" class="@getPopupButtonCss()" type="button" aria-label="@OpenAriaLabel">
<button tabindex="-1" disabled="@IsDisabled" onclick="@OpenPopupScript()" class=@PopupButtonClass type="button" aria-label="@OpenAriaLabel">
<RadzenIcon Icon="@DropDownIcon" />
</button>
<div id="@PopupID" class="rz-splitbutton-menu">

View File

@@ -1,6 +1,7 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.JSInterop;
using Radzen.Blazor.Rendering;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
@@ -23,11 +24,6 @@ namespace Radzen.Blazor
/// </example>
public partial class RadzenSplitButton : RadzenComponentWithChildren
{
private string getButtonSize()
{
return Size == ButtonSize.Medium ? "md" : Size == ButtonSize.Large ? "lg" : Size == ButtonSize.Small ? "sm" : "xs";
}
/// <summary>
/// Gets or sets the child content.
/// </summary>
@@ -132,7 +128,6 @@ namespace Radzen.Blazor
[Parameter]
public bool AlwaysOpenPopup { get; set; }
/// <summary>
/// Gets or sets the open button aria-label attribute.
/// </summary>
@@ -206,15 +201,22 @@ namespace Radzen.Blazor
}
}
private string getButtonCss()
{
return $"rz-button rz-button-{getButtonSize()} rz-variant-{Enum.GetName(typeof(Variant), Variant).ToLowerInvariant()} rz-{Enum.GetName(typeof(ButtonStyle), ButtonStyle).ToLowerInvariant()} rz-shade-{Enum.GetName(typeof(Shade), Shade).ToLowerInvariant()} {(IsDisabled ? " rz-state-disabled" : "")}{(string.IsNullOrEmpty(Text) && !string.IsNullOrEmpty(Icon) ? " rz-button-icon-only" : "")}";
}
string ButtonClass => ClassList.Create("rz-button")
.AddButtonSize(Size)
.AddVariant(Variant)
.AddButtonStyle(ButtonStyle)
.AddShade(Shade)
.Add("rz-button-icon-only", string.IsNullOrEmpty(Text) && !string.IsNullOrEmpty(Icon))
.AddDisabled(IsDisabled)
.ToString();
private string getPopupButtonCss()
{
return $"rz-splitbutton-menubutton rz-button rz-button-icon-only rz-button-{getButtonSize()} rz-variant-{Enum.GetName(typeof(Variant), Variant).ToLowerInvariant()} rz-{Enum.GetName(typeof(ButtonStyle), ButtonStyle).ToLowerInvariant()} rz-shade-{Enum.GetName(typeof(Shade), Shade).ToLowerInvariant()}{(IsDisabled ? " rz-state-disabled" : "")}";
}
string PopupButtonClass => ClassList.Create("rz-splitbutton-menubutton rz-button rz-button-icon-only")
.AddButtonSize(Size)
.AddVariant(Variant)
.AddButtonStyle(ButtonStyle)
.AddShade(Shade)
.AddDisabled(IsDisabled)
.ToString();
private string OpenPopupScript()
{
@@ -239,7 +241,7 @@ namespace Radzen.Blazor
if (IsJSRuntimeAvailable)
{
JSRuntime.InvokeVoidAsync("Radzen.destroyPopup", PopupID);
JSRuntime.InvokeVoid("Radzen.destroyPopup", PopupID);
}
}
@@ -332,4 +334,4 @@ namespace Radzen.Blazor
[Parameter]
public string ButtonAriaLabel { get; set; } = "Button";
}
}
}

View File

@@ -1,7 +1,7 @@
@inherits RadzenComponent
@if (Visible)
{
<li class=@ItemClassList role="menuitem" @onclick="@OnClick" @attributes="Attributes" style="@Style">
<li class=@ItemClass role="menuitem" @onclick="@OnClick" @attributes="Attributes" style="@Style">
<a id="@(SplitButton.SplitButtonId() + GetHashCode())" class="rz-menuitem-link">
@if (!string.IsNullOrEmpty(Icon))
{

View File

@@ -80,7 +80,10 @@ namespace Radzen.Blazor
}
}
ClassList ItemClassList => ClassList.Create("rz-menuitem").AddDisabled(Disabled).Add("rz-state-highlight", SplitButton.IsFocused(this));
string ItemClass => ClassList.Create("rz-menuitem")
.AddDisabled(Disabled)
.Add("rz-state-highlight", SplitButton.IsFocused(this))
.ToString();
/// <inheritdoc />
public override void Dispose()

View File

@@ -3,11 +3,7 @@
{
<div @ref="@Element" style="@Style" @attributes="Attributes" class="@GetCssClass()" id="@GetId()">
<div class="rz-data-grid-data">
<table class="@GetTableCssClass()">
<CascadingValue Value=this>
@ChildContent
</CascadingValue>
</table>
<table class="@GetTableCssClass()">@ChildContent</table>
</div>
</div>
}
}

View File

@@ -1,57 +1,38 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Routing;
using System;
using System.Linq;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.JSInterop;
using System;
using Microsoft.AspNetCore.Components;
using Radzen.Blazor.Rendering;
namespace Radzen.Blazor
namespace Radzen.Blazor;
/// <summary>
/// Display a styled table with data.
/// </summary>
public partial class RadzenTable : RadzenComponentWithChildren
{
/// <summary>
/// RadzenTable component.
/// Gets or sets the grid lines style.
/// </summary>
public partial class RadzenTable : RadzenComponentWithChildren
{
/// <summary>
/// Gets or sets the grid lines.
/// </summary>
/// <value>The grid lines.</value>
[Parameter]
public DataGridGridLines GridLines { get; set; } = DataGridGridLines.Default;
/// <value>The grid lines.</value>
[Parameter]
public DataGridGridLines GridLines { get; set; } = DataGridGridLines.Default;
/// <summary>
/// Gets or sets a value indicating whether RadzenTable should use alternating row styles.
/// </summary>
/// <value><c>true</c> if RadzenTable is using alternating row styles; otherwise, <c>false</c>.</value>
[Parameter]
public bool AllowAlternatingRows { get; set; } = true;
/// <summary>
/// Gets or sets a value indicating whether RadzenTable should use alternating row styles.
/// </summary>
/// <value><c>true</c> if RadzenTable is using alternating row styles; otherwise, <c>false</c>.</value>
[Parameter]
public bool AllowAlternatingRows { get; set; } = true;
/// <inheritdoc />
protected override string GetComponentCssClass()
{
return $"rz-data-grid rz-datatable rz-datatable-scrollable {(CurrentStyle.ContainsKey("height") ? "rz-has-height" : "")}".Trim();
}
/// <inheritdoc />
protected override string GetComponentCssClass() => ClassList.Create("rz-data-grid rz-datatable rz-datatable-scrollable")
.Add("rz-has-height", CurrentStyle.ContainsKey("height"))
.ToString();
/// <summary>
/// Gets the table CSS classes.
/// </summary>
protected virtual string GetTableCssClass()
{
var styles = new List<string>(new string[] { "rz-grid-table", "rz-grid-table-fixed" });
if (AllowAlternatingRows)
{
styles.Add("rz-grid-table-striped");
}
if (GridLines != DataGridGridLines.Default)
{
styles.Add($"rz-grid-gridlines-{Enum.GetName(typeof(DataGridGridLines), GridLines).ToLower()}");
}
return string.Join(" ", styles);
}
}
}
/// <summary>
/// Gets the table CSS classes.
/// </summary>
protected virtual string GetTableCssClass() => ClassList.Create("rz-grid-table rz-grid-table-fixed")
.Add("rz-grid-table-striped", AllowAlternatingRows)
.Add($"rz-grid-gridlines-{Enum.GetName(typeof(DataGridGridLines), GridLines).ToLowerInvariant()}", GridLines != DataGridGridLines.Default)
.ToString();
}

View File

@@ -1,9 +1,5 @@
@inherits RadzenComponentWithChildren
@if (Visible)
{
<tbody @ref="@Element" style="@Style" @attributes="Attributes" class="@GetCssClass()" id="@GetId()">
<CascadingValue Value=this>
@ChildContent
</CascadingValue>
</tbody>
}
<tbody @ref="@Element" style="@Style" @attributes="Attributes" class="@GetCssClass()" id="@GetId()">@ChildContent</tbody>
}

View File

@@ -1,39 +1,5 @@
using Microsoft.AspNetCore.Components;
using System.Collections.Generic;
namespace Radzen.Blazor
{
/// <summary>
/// RadzenTableBody component.
/// </summary>
public partial class RadzenTableBody : RadzenComponentWithChildren
{
List<RadzenTableRow> rows = new List<RadzenTableRow>();
/// <summary>
/// Adds the row.
/// </summary>
/// <param name="row">The row.</param>
public void AddRow(RadzenTableRow row)
{
if (rows.IndexOf(row) == -1)
{
rows.Add(row);
StateHasChanged();
}
}
/// <summary>
/// Removes the row.
/// </summary>
/// <param name="row">The row.</param>
public void RemoveRow(RadzenTableRow row)
{
if (rows.IndexOf(row) != -1)
{
rows.Remove(row);
StateHasChanged();
}
}
}
}
namespace Radzen.Blazor;
/// <summary>
/// RadzenTableBody component.
/// </summary>
public partial class RadzenTableBody : RadzenComponentWithChildren;

View File

@@ -2,10 +2,6 @@
@if (Visible)
{
<td @ref="@Element" style="@Style" @attributes="Attributes" class="@GetCssClass()" id="@GetId()">
<span class="rz-cell-data">
<CascadingValue Value=this>
@ChildContent
</CascadingValue>
</span>
<span class="rz-cell-data">@ChildContent</span>
</td>
}
}

View File

@@ -1,40 +1,10 @@
using Microsoft.AspNetCore.Components;
using System.Collections.Generic;
namespace Radzen.Blazor;
namespace Radzen.Blazor
/// <summary>
/// Represents a cell in <see cref="RadzenTable"/>
/// </summary>
public partial class RadzenTableCell : RadzenComponentWithChildren
{
/// <summary>
/// RadzenTableRow component.
/// </summary>
public partial class RadzenTableCell : RadzenComponentWithChildren
{
/// <inheritdoc />
protected override string GetComponentCssClass()
{
return "rz-data-cell";
}
RadzenTableRow _row;
/// <summary>
/// Gets or sets the row.
/// </summary>
/// <value>The row.</value>
[CascadingParameter]
public RadzenTableRow Row
{
get
{
return _row;
}
set
{
if (_row != value)
{
_row = value;
_row.AddCell(this);
}
}
}
}
}
/// <inheritdoc />
protected override string GetComponentCssClass() => "rz-data-cell";
}

View File

@@ -1,9 +1,5 @@
@inherits RadzenComponentWithChildren
@if (Visible)
{
<thead @ref="@Element" style="@Style" @attributes="Attributes" class="@GetCssClass()" id="@GetId()">
<CascadingValue Value=this>
@ChildContent
</CascadingValue>
</thead>
}
<thead @ref="@Element" style="@Style" @attributes="Attributes" class="@GetCssClass()" id="@GetId()">@ChildContent</thead>
}

View File

@@ -1,39 +1,6 @@
using Microsoft.AspNetCore.Components;
using System.Collections.Generic;
namespace Radzen.Blazor;
namespace Radzen.Blazor
{
/// <summary>
/// RadzenTableHeader component.
/// </summary>
public partial class RadzenTableHeader : RadzenComponentWithChildren
{
List<RadzenTableHeaderRow> rows = new List<RadzenTableHeaderRow>();
/// <summary>
/// Adds the row.
/// </summary>
/// <param name="row">The row.</param>
public void AddRow(RadzenTableHeaderRow row)
{
if (rows.IndexOf(row) == -1)
{
rows.Add(row);
StateHasChanged();
}
}
/// <summary>
/// Removes the row.
/// </summary>
/// <param name="row">The row.</param>
public void RemoveRow(RadzenTableHeaderRow row)
{
if (rows.IndexOf(row) != -1)
{
rows.Remove(row);
StateHasChanged();
}
}
}
}
/// <summary>
/// RadzenTableHeader component.
/// </summary>
public partial class RadzenTableHeader : RadzenComponentWithChildren;

View File

@@ -1,9 +1,5 @@
@inherits RadzenComponentWithChildren
@if (Visible)
{
<th @ref="@Element" style="padding: var(--rz-grid-cell-padding);@Style" @attributes="Attributes" class="@GetCssClass()" id="@GetId()">
<CascadingValue Value=this>
@ChildContent
</CascadingValue>
</th>
}
<th @ref="@Element" style="padding: var(--rz-grid-cell-padding);@Style" @attributes="Attributes" class="@GetCssClass()" id="@GetId()">@ChildContent</th>
}

View File

@@ -1,34 +1,5 @@
using Microsoft.AspNetCore.Components;
using System.Collections.Generic;
namespace Radzen.Blazor
{
/// <summary>
/// RadzenTableRow component.
/// </summary>
public partial class RadzenTableHeaderCell : RadzenComponentWithChildren
{
RadzenTableHeaderRow _row;
/// <summary>
/// Gets or sets the row.
/// </summary>
/// <value>The row.</value>
[CascadingParameter]
public RadzenTableHeaderRow Row
{
get
{
return _row;
}
set
{
if (_row != value)
{
_row = value;
_row.AddCell(this);
}
}
}
}
}
namespace Radzen.Blazor;
/// <summary>
/// RadzenTableRow component.
/// </summary>
public partial class RadzenTableHeaderCell : RadzenComponentWithChildren;

View File

@@ -1,9 +1,5 @@
@inherits RadzenComponentWithChildren
@if (Visible)
{
<tr @ref="@Element" style="@Style" @attributes="Attributes" class="@GetCssClass()" id="@GetId()">
<CascadingValue Value=this>
@ChildContent
</CascadingValue>
</tr>
}
<tr @ref="@Element" style="@Style" @attributes="Attributes" class="@GetCssClass()" id="@GetId()">@ChildContent</tr>
}

View File

@@ -1,62 +1,5 @@
using Microsoft.AspNetCore.Components;
using System.Collections.Generic;
namespace Radzen.Blazor
{
/// <summary>
/// RadzenTableRow component.
/// </summary>
public partial class RadzenTableHeaderRow : RadzenComponentWithChildren
{
RadzenTableHeader _header;
/// <summary>
/// Gets or sets the table body.
/// </summary>
/// <value>The table body.</value>
[CascadingParameter]
public RadzenTableHeader Header
{
get
{
return _header;
}
set
{
if (_header != value)
{
_header = value;
_header.AddRow(this);
}
}
}
List<RadzenTableHeaderCell> cells = new List<RadzenTableHeaderCell>();
/// <summary>
/// Adds the cell.
/// </summary>
/// <param name="cell">The cell.</param>
public void AddCell(RadzenTableHeaderCell cell)
{
if (cells.IndexOf(cell) == -1)
{
cells.Add(cell);
StateHasChanged();
}
}
/// <summary>
/// Removes the cell.
/// </summary>
/// <param name="cell">The cell.</param>
public void RemoveCell(RadzenTableHeaderCell cell)
{
if (cells.IndexOf(cell) != -1)
{
cells.Remove(cell);
StateHasChanged();
}
}
}
}
namespace Radzen.Blazor;
/// <summary>
/// RadzenTableRow component.
/// </summary>
public partial class RadzenTableHeaderRow : RadzenComponentWithChildren;

View File

@@ -1,9 +1,5 @@
@inherits RadzenComponentWithChildren
@if (Visible)
{
<tr @ref="@Element" style="@Style" @attributes="Attributes" class="@GetCssClass()" id="@GetId()">
<CascadingValue Value=this>
@ChildContent
</CascadingValue>
</tr>
}
<tr @ref="@Element" style="@Style" @attributes="Attributes" class="@GetCssClass()" id="@GetId()">@ChildContent</tr>
}

View File

@@ -1,68 +1,9 @@
using Microsoft.AspNetCore.Components;
using System.Collections.Generic;
namespace Radzen.Blazor
namespace Radzen.Blazor;
/// <summary>
/// RadzenTableRow component.
/// </summary>
public partial class RadzenTableRow : RadzenComponentWithChildren
{
/// <summary>
/// RadzenTableRow component.
/// </summary>
public partial class RadzenTableRow : RadzenComponentWithChildren
{
/// <inheritdoc />
protected override string GetComponentCssClass()
{
return "rz-data-row";
}
RadzenTableBody _body;
/// <summary>
/// Gets or sets the table body.
/// </summary>
/// <value>The table body.</value>
[CascadingParameter]
public RadzenTableBody Body
{
get
{
return _body;
}
set
{
if (_body != value)
{
_body = value;
_body.AddRow(this);
}
}
}
List<RadzenTableCell> cells = new List<RadzenTableCell>();
/// <summary>
/// Adds the cell.
/// </summary>
/// <param name="cell">The cell.</param>
public void AddCell(RadzenTableCell cell)
{
if (cells.IndexOf(cell) == -1)
{
cells.Add(cell);
StateHasChanged();
}
}
/// <summary>
/// Removes the cell.
/// </summary>
/// <param name="cell">The cell.</param>
public void RemoveCell(RadzenTableCell cell)
{
if (cells.IndexOf(cell) != -1)
{
cells.Remove(cell);
StateHasChanged();
}
}
}
}
/// <inheritdoc />
protected override string GetComponentCssClass() => "rz-data-row";
}

View File

@@ -2,7 +2,7 @@
@implements IDisposable
@if (Tabs.RenderMode == TabRenderMode.Server ? Visible : true)
{
<li role="presentation" @attributes=@Attributes style=@getStyle() class=@ClassList>
<li role="presentation" @attributes=@Attributes style=@getStyle() class=@Class>
<a @onclick=@OnClick role="tab" @onclick:preventDefault="true" id="@($"{Tabs.Id}-tabpanel-{Index}-label")"
aria-selected=@(IsSelected? "true" : "false") aria-controls="@($"{Tabs.Id}-tabpanel-{Index}")" @onkeydown:stopPropagation>
@if (!string.IsNullOrEmpty(Icon))

View File

@@ -79,11 +79,12 @@ namespace Radzen.Blazor
/// Gets the class list.
/// </summary>
/// <value>The class list.</value>
ClassList ClassList => ClassList.Create()
.Add("rz-tabview-selected", IsSelected)
.Add("rz-state-focused", Tabs.IsFocused(this))
.AddDisabled(Disabled)
.Add(Attributes);
string Class => ClassList.Create()
.Add("rz-tabview-selected", IsSelected)
.Add("rz-state-focused", Tabs.IsFocused(this))
.AddDisabled(Disabled)
.Add(Attributes)
.ToString();
/// <summary>
/// Gets the index.

View File

@@ -348,16 +348,17 @@ namespace Radzen.Blazor
break;
}
var classList = ClassList.Create(className)
.Add(Attributes)
.Add(alignClassName, TextAlign != TextAlign.Left);
var @class = ClassList.Create(className)
.Add(Attributes)
.Add(alignClassName, TextAlign != TextAlign.Left)
.ToString();
if (Visible)
{
builder.OpenElement(0, tagName);
builder.AddAttribute(1, "style", Style);
builder.AddMultipleAttributes(2, Attributes);
builder.AddAttribute(3, "class", classList.ToString());
builder.AddAttribute(3, "class", @class);
builder.AddAttribute(4, "id", GetId());
if (!string.IsNullOrEmpty(Text))

View File

@@ -231,15 +231,6 @@ namespace Radzen.Blazor
return Task.CompletedTask;
}
/// <summary>
/// Gets the class list.
/// </summary>
/// <param name="className">Name of the class.</param>
/// <returns>ClassList.</returns>
protected ClassList GetClassList(string className) => ClassList.Create(className)
.AddDisabled(Disabled)
.Add(FieldIdentifier, EditContext);
/// <summary> Provides support for RadzenFormField integration. </summary>
[CascadingParameter]
public IFormFieldContext FormFieldContext { get; set; }
@@ -279,22 +270,19 @@ namespace Radzen.Blazor
}
/// <inheritdoc />
protected override string GetComponentCssClass()
{
var classes = GetClassList("rz-toggle-button");
protected override string GetComponentCssClass() => ClassList.Create("rz-button rz-toggle-button")
.Add("rz-button-icon-only", string.IsNullOrEmpty(Text) && !string.IsNullOrEmpty(Icon))
.AddButtonSize(Size)
.AddVariant(Variant)
.AddButtonStyle(CurrentButtonStyle)
.AddShade(CurrentShade)
.AddDisabled(IsDisabled)
.Add(FieldIdentifier, EditContext)
.Add("rz-state-active", HasValue)
.ToString();
if (HasValue)
{
classes.Add("rz-state-active", Value);
}
var baseClasses = HasValue ?
$"rz-button rz-button-{getButtonSize()} rz-variant-{Enum.GetName(typeof(Variant), Variant).ToLowerInvariant()} rz-{Enum.GetName(typeof(ButtonStyle), ToggleButtonStyle).ToLowerInvariant()} rz-shade-{Enum.GetName(typeof(Shade), ToggleShade).ToLowerInvariant()}{(IsDisabled ? " rz-state-disabled" : "")}{(string.IsNullOrEmpty(Text) && !string.IsNullOrEmpty(Icon) ? " rz-button-icon-only" : "")}" :
$"rz-button rz-button-{getButtonSize()} rz-variant-{Enum.GetName(typeof(Variant), Variant).ToLowerInvariant()} rz-{Enum.GetName(typeof(ButtonStyle), ButtonStyle).ToLowerInvariant()} rz-shade-{Enum.GetName(typeof(Shade), Shade).ToLowerInvariant()}{(IsDisabled ? " rz-state-disabled" : "")}{(string.IsNullOrEmpty(Text) && !string.IsNullOrEmpty(Icon) ? " rz-button-icon-only" : "")}";
return $"{baseClasses} {classes.ToString()}";
}
private Shade CurrentShade => HasValue ? ToggleShade : Shade;
private ButtonStyle CurrentButtonStyle => HasValue ? ToggleButtonStyle : ButtonStyle;
/// <summary>
/// Gets or sets the ToggleButton style.

View File

@@ -4,10 +4,10 @@
@{var itemArgs = Tree?.ItemAttributes(this); }
<li class="rz-treenode" @attributes="@(itemArgs.Item1.Attributes.Any() ? itemArgs.Item1.Attributes : Attributes)"
@oncontextmenu="OnContextMenu" @oncontextmenu:preventDefault="@Tree.ItemContextMenu.HasDelegate" @oncontextmenu:stopPropagation>
<div class=@ContentClassList @onclick="@Select">
<div class=@ContentClass @onclick="@Select">
@if (ChildContent != null || HasChildren)
{
<span class=@IconClassList @onclick="@Toggle" @onclick:stopPropagation></span>
<span class=@IconClass @onclick="@Toggle" @onclick:stopPropagation></span>
}
@if(Tree != null && Tree.AllowCheckBoxes)
{
@@ -15,18 +15,20 @@
}
@if (Template != null)
{
<div class="@LabelClassList" @onkeydown:stopPropagation>@Template(this)</div>
<div class=@LabelClass @onkeydown:stopPropagation>@Template(this)</div>
} else
{
<label for="@(GetHashCode())" class="@LabelClassList">@Text</label>
<label for="@(GetHashCode())" class=@LabelClass>@Text</label>
}
</div>
@if (ChildContent != null && expanded)
{
<CascadingValue Value=this>
<ul class="rz-treenode-children" style=@(clientExpanded ? "" : "display:none;") @onkeydown:stopPropagation>
<Expander Expanded=@clientExpanded>
<ul class="rz-treenode-children" @onkeydown:stopPropagation>
@ChildContent
</ul>
</Expander>
</CascadingValue>
}
</li>

View File

@@ -21,19 +21,22 @@ namespace Radzen.Blazor
[Parameter(CaptureUnmatchedValues = true)]
public IReadOnlyDictionary<string, object> Attributes { get; set; }
ClassList ContentClassList => ClassList.Create("rz-treenode-content")
.Add("rz-treenode-content-selected", selected)
.Add("rz-state-focused", Tree.IsFocused(this))
.Add(Tree.ItemContentCssClass)
.Add(ContentCssClass);
ClassList IconClassList => ClassList.Create("notranslate rz-tree-toggler rzi")
.Add("rzi-caret-down", clientExpanded)
.Add("rzi-caret-right", !clientExpanded)
.Add(Tree.ItemIconCssClass)
.Add(IconCssClass);
private ClassList LabelClassList => ClassList.Create("rz-treenode-label")
.Add(Tree.ItemLabelCssClass)
.Add(LabelCssClass);
string ContentClass => ClassList.Create("rz-treenode-content")
.Add("rz-treenode-content-selected", selected)
.Add("rz-state-focused", Tree.IsFocused(this))
.Add(Tree.ItemContentCssClass)
.Add(ContentCssClass)
.ToString();
string IconClass => ClassList.Create("notranslate rz-tree-toggler rzi")
.Add("rzi-caret-down", clientExpanded)
.Add("rzi-caret-right", !clientExpanded)
.Add(Tree.ItemIconCssClass)
.Add(IconCssClass)
.ToString();
string LabelClass => ClassList.Create("rz-treenode-label")
.Add(Tree.ItemLabelCssClass)
.Add(LabelCssClass)
.ToString();
/// <summary>
/// Gets or sets the child content.
/// </summary>

View File

@@ -141,15 +141,17 @@ namespace Radzen.Blazor
/// Gets the choose class list.
/// </summary>
/// <value>The choose class list.</value>
ClassList ChooseClassList => ClassList.Create("rz-fileupload-choose rz-button")
.AddDisabled(Disabled);
string ChooseClassList => ClassList.Create("rz-fileupload-choose rz-button")
.AddDisabled(Disabled)
.ToString();
/// <summary>
/// Gets the button class list.
/// </summary>
/// <value>The button class list.</value>
ClassList ButtonClassList => ClassList.Create("rz-button rz-button-icon-only rz-base rz-shade-default")
.AddDisabled(Disabled);
string ButtonClassList => ClassList.Create("rz-button rz-button-icon-only rz-base rz-shade-default")
.AddDisabled(Disabled)
.ToString();
/// <summary>
/// Gets or sets the child content.

View File

@@ -3,118 +3,169 @@ using System.Collections.Generic;
using System.Text;
using Microsoft.AspNetCore.Components.Forms;
namespace Radzen.Blazor.Rendering
namespace Radzen.Blazor.Rendering;
/// <summary>
/// Utility for managing a list of CSS classes.
/// </summary>
public readonly struct ClassList
{
/// <summary>
/// Class ClassList.
/// </summary>
public class ClassList
private ClassList(string className = null)
{
/// <summary>
/// The builder
/// </summary>
private readonly StringBuilder builder = new StringBuilder();
/// <summary>
/// Initializes a new instance of the <see cref="ClassList"/> class.
/// </summary>
/// <param name="className">Name of the class.</param>
private ClassList(string className = null)
{
Add(className);
}
/// <summary>
/// Creates the specified class name.
/// </summary>
/// <param name="className">Name of the class.</param>
/// <returns>ClassList.</returns>
public static ClassList Create(string className = null) => new ClassList(className);
/// <summary>
/// Adds the specified class name.
/// </summary>
/// <param name="className">Name of the class.</param>
/// <param name="condition">if set to <c>true</c> [condition].</param>
/// <returns>ClassList.</returns>
public ClassList Add(string className, bool condition = true)
{
if (condition && !string.IsNullOrWhiteSpace(className))
{
if (builder.Length > 0)
{
builder.Append(" ");
}
builder.Append(className);
}
return this;
}
/// <summary>
/// Adds the specified attributes.
/// </summary>
/// <param name="attributes">The attributes.</param>
/// <returns>ClassList.</returns>
public ClassList Add(IDictionary<string, object> attributes)
{
if (attributes != null && attributes.TryGetValue("class", out var className) && className != null)
{
return Add(className.ToString());
}
return this;
}
/// <summary>
/// Adds the specified attributes.
/// </summary>
/// <param name="attributes">The attributes.</param>
/// <returns>ClassList.</returns>
public ClassList Add(IReadOnlyDictionary<string, object> attributes)
{
if (attributes != null && attributes.TryGetValue("class", out var className) && className != null)
{
return Add(className.ToString());
}
return this;
}
/// <summary>
/// Adds the specified field.
/// </summary>
/// <param name="field">The field.</param>
/// <param name="context">The context.</param>
/// <returns>ClassList.</returns>
public ClassList Add(FieldIdentifier field, EditContext context)
{
if (field.FieldName != null && context != null)
{
return Add(context.FieldCssClass(field));
}
return this;
}
/// <summary>
/// Adds the disabled.
/// </summary>
/// <param name="condition">if set to <c>true</c> [condition].</param>
/// <returns>ClassList.</returns>
public ClassList AddDisabled(bool condition = true)
{
return Add("rz-state-disabled", condition);
}
/// <summary>
/// Returns a <see cref="System.String" /> that represents this instance.
/// </summary>
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
public override string ToString()
{
return builder.ToString();
}
Add(className);
}
private readonly StringBuilder builder = StringBuilderCache.Acquire();
/// <summary>
/// Creates the specified class name.
/// </summary>
/// <param name="className">Name of the class.</param>
/// <returns>ClassList.</returns>
public static ClassList Create(string className = null) => new(className);
/// <summary>
/// Adds the specified class name if the condition is true.
/// </summary>
/// <param name="className">Name of the class.</param>
/// <param name="condition">if set to <c>true</c> the class name is added.</param>
/// <remarks>
/// The class name is added only if it is not null or empty.
/// </remarks>
public ClassList Add(string className, bool condition = true)
{
if (condition && !string.IsNullOrWhiteSpace(className))
{
if (builder.Length > 0)
{
builder.Append(' ');
}
builder.Append(className);
}
return this;
}
/// <summary>
/// Checks if the provided attributes contain a class name and adds it to the list.
/// </summary>
/// <param name="attributes">The attributes.</param>
public ClassList Add(IDictionary<string, object> attributes)
{
if (attributes != null && attributes.TryGetValue("class", out var value) && value is string @class)
{
return Add(@class);
}
return this;
}
/// <summary>
/// Checks if the provided attributes contain a class name and adds it to the list.
/// </summary>
/// <param name="attributes">The attributes.</param>
public ClassList Add(IReadOnlyDictionary<string, object> attributes)
{
if (attributes != null && attributes.TryGetValue("class", out var value) && value is string @class)
{
return Add(@class);
}
return this;
}
/// <summary>
/// Adds the class returned by the EditContext for the specified field identifier
/// </summary>
/// <param name="field">The field.</param>
/// <param name="context">The context.</param>
public ClassList Add(FieldIdentifier field, EditContext context)
{
if (field.FieldName != null && context != null)
{
return Add(context.FieldCssClass(field));
}
return this;
}
/// <summary>
/// Adds the disabled class if the condition is true.
/// </summary>
public ClassList AddDisabled(bool condition = true) => Add("rz-state-disabled", condition);
/// <summary>
/// Adds the specified size as button size class.
/// </summary>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public ClassList AddButtonSize(ButtonSize size) => size switch {
ButtonSize.Small => Add("rz-button-sm"),
ButtonSize.Large => Add("rz-button-lg"),
ButtonSize.Medium => Add("rz-button-md"),
ButtonSize.ExtraSmall => Add("rz-button-xs"),
_ => throw new ArgumentOutOfRangeException(nameof(size), size, null)
};
/// <summary>
/// Adds the specified variant as variant CSS class.
/// </summary>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public ClassList AddVariant(Variant variant) => variant switch {
Variant.Filled => Add("rz-variant-filled"),
Variant.Flat => Add("rz-variant-flat"),
Variant.Outlined => Add("rz-variant-outlined"),
Variant.Text => Add("rz-variant-text"),
_ => throw new ArgumentOutOfRangeException(nameof(variant), variant, null)
};
/// <summary>
/// Adds the specified button style as shade CSS class.
/// </summary>
/// <param name="style"></param>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public ClassList AddButtonStyle(ButtonStyle style) => style switch {
ButtonStyle.Primary => Add("rz-primary"),
ButtonStyle.Secondary => Add("rz-secondary"),
ButtonStyle.Light => Add("rz-light"),
ButtonStyle.Base => Add("rz-base"),
ButtonStyle.Dark => Add("rz-dark"),
ButtonStyle.Success => Add("rz-success"),
ButtonStyle.Warning => Add("rz-warning"),
ButtonStyle.Danger => Add("rz-danger"),
ButtonStyle.Info => Add("rz-info"),
_ => throw new ArgumentOutOfRangeException(nameof(style), style, null)
};
/// <summary>
/// Adds the specified shade as a CSS class.
/// </summary>
/// <param name="shade"></param>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public ClassList AddShade(Shade shade) => shade switch {
Shade.Default => Add("rz-shade-default"),
Shade.Light => Add("rz-shade-light"),
Shade.Dark => Add("rz-shade-dark"),
Shade.Lighter => Add("rz-shade-lighter"),
Shade.Darker => Add("rz-shade-darker"),
_ => throw new ArgumentOutOfRangeException(nameof(shade), shade, null)
};
/// <summary>
/// Adds the specified horizontal alignment as a CSS class.
/// </summary>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public ClassList AddHorizontalAlign(HorizontalAlign alignment) => alignment switch {
HorizontalAlign.Center => Add("rz-align-center"),
HorizontalAlign.Left => Add("rz-align-left"),
HorizontalAlign.Right => Add("rz-align-right"),
HorizontalAlign.Justify => Add("rz-align-justify"),
_ => throw new ArgumentOutOfRangeException(nameof(alignment), alignment, null)
};
/// <summary>
/// Returns a <see cref="System.String" /> that represents this instance.
/// </summary>
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
public override string ToString() => StringBuilderCache.GetStringAndRelease(builder);
}

View File

@@ -9,7 +9,7 @@
</div>
</div>
<div @ref=view class="rz-view-content" tabindex="0" @onkeydown="@(args => OnKeyPress(args))" @onkeydown:preventDefault="@preventKeyPress" @onkeydown:stopPropagation
@onfocus=@(args => {if(CurrentDate == default(DateTime)) { CurrentDate = StartDate; }})>
@onfocus=@this.AsNonRenderingEventHandler(OnFocus)>
<Hours Start=@StartTime End=@EndTime TimeFormat=@TimeFormat MinutesPerSlot=@MinutesPerSlot />
<div class="rz-slots">
<DaySlotEvents MinutesPerSlot=@MinutesPerSlot StartDate=@StartDate EndDate=@EndDate Appointments=@Appointments CurrentDate="@CurrentDate" CurrentAppointment="@currentAppointment" AppointmentDragStart=@OnAppointmentDragStart />
@@ -23,6 +23,14 @@
</div>
@code {
void OnFocus()
{
if (CurrentDate == default(DateTime))
{
CurrentDate = StartDate;
}
}
ElementReference view;
[Parameter]

View File

@@ -3,8 +3,8 @@
@using System.ComponentModel
@inject IJSRuntime JSRuntime
@implements IDisposable
<div class="@WrapperCssClass">
<div @ref="dialog" class="@CssClass" role="dialog" aria-labelledby="rz-dialog-0-label" style=@Style>
<div class=@WrapperClass>
<div @ref="dialog" class=@Class role="dialog" aria-labelledby="rz-dialog-0-label" style=@Style>
@if (Dialog.Options.ShowTitle)
{
<div class="rz-dialog-titlebar">
@@ -26,7 +26,7 @@
}
</div>
}
<div class="@ContentCssClass">
<div class=@ContentClass>
@if (Dialog.Options.ChildContent != null)
{
@Dialog.Options.ChildContent(Service)
@@ -49,7 +49,6 @@
<div class="rz-dialog-mask" style="pointer-events: none;"></div>
}
}
</div>
@code {
@@ -158,32 +157,17 @@
Service.Close();
}
string CssClass
{
get
{
var baseCss = "rz-dialog";
return string.IsNullOrEmpty(Dialog.Options.CssClass) ? baseCss : $"{baseCss} {Dialog.Options.CssClass}";
}
}
string Class => ClassList.Create("rz-dialog rz-open")
.Add(Dialog.Options.CssClass)
.ToString();
string WrapperCssClass
{
get
{
var baseCss = "rz-dialog-wrapper";
return string.IsNullOrWhiteSpace(Dialog.Options.WrapperCssClass) ? baseCss : $"{baseCss} {Dialog.Options.WrapperCssClass}";
}
}
string WrapperClass => ClassList.Create("rz-dialog-wrapper")
.Add(Dialog.Options.WrapperCssClass)
.ToString();
string ContentCssClass
{
get
{
var baseCss = "rz-dialog-content";
return string.IsNullOrEmpty(Dialog.Options.ContentCssClass) ? baseCss : $"{baseCss} {Dialog.Options.ContentCssClass}";
}
}
string ContentClass => ClassList.Create("rz-dialog-content")
.Add(Dialog.Options.ContentCssClass)
.ToString();
string Style
{

View File

@@ -52,20 +52,9 @@ else
string GetTitle() => !string.IsNullOrEmpty(Shortcut) ? $"{Title} ({Shortcut})" : Title;
string Class
{
get
{
var classList = new List<string>() { "rz-html-editor-button" };
if (Selected && !Editor.Disabled && EnabledModes.HasFlag(Editor.GetMode()))
{
classList.Add("rz-selected");
}
return string.Join(" ", classList);
}
}
string Class => ClassList.Create("rz-html-editor-button")
.Add("rz-selected", Selected && !Editor.Disabled && EnabledModes.HasFlag(Editor.GetMode()))
.ToString();
void Empty()
{

View File

@@ -75,18 +75,7 @@
}
}
string Class
{
get
{
var classList = new List<string>() { "rz-html-editor-dropdown" };
if (Editor.Disabled || !EnabledModes.HasFlag(Editor.GetMode()))
{
classList.Add("rz-disabled");
}
return string.Join(" ", classList);
}
}
string Class => ClassList.Create("rz-html-editor-dropdown")
.Add("rz-disabled", Editor.Disabled || !EnabledModes.HasFlag(Editor.GetMode()))
.ToString();
}

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