Compare commits

...

142 Commits

Author SHA1 Message Date
Vladimir Enchev
a244741640 version updated 2025-02-14 11:26:13 +02:00
Vladimir Enchev
e3f5360a7f ToFilterString() should not use in operator 2025-02-14 11:25:51 +02:00
Vladimir Enchev
c280bae14f ToFilterString() should cast enum value to enum type 2025-02-14 11:11:35 +02:00
Vladimir Enchev
e99671ba81 Where() should handle nested types 2025-02-14 10:49:33 +02:00
Vladimir Enchev
73338462e5 Where() should handle bools and old single = expressions 2025-02-14 10:36:12 +02:00
Vladimir Enchev
1912e69375 OrderBy() variable reference ignored if present 2025-02-14 10:28:35 +02:00
Vladimir Enchev
60a3789e9c DateTime, DateTimeOffset, DateOnly and Guid filtering by string fixed 2025-02-14 09:53:37 +02:00
Vladimir Enchev
a89bc5a5ce version updated 2025-02-14 07:16:30 +02:00
Vladimir Enchev
08f3126f46 Fixed DataGrid "Translation of method 'string.ToLowerInvariant' failed
Fix #1964
2025-02-14 07:16:14 +02:00
Vladimir Enchev
53e746146b DartSassBuilder PrivateAssets="All" added 2025-02-13 16:56:02 +02:00
Vladimir Enchev
1e7cc0d655 Version updated 2025-02-13 14:57:11 +02:00
Vladimir Enchev
b37a81b297 DataGrid filtering null reference exception fixed in some cases 2025-02-13 14:53:45 +02:00
Vladimir Enchev
6aec8118f2 DataGrid sort by dynamic type fxed 2025-02-13 14:33:53 +02:00
Vladimir Enchev
756732803f Array and List<> member access fixed 2025-02-13 14:28:08 +02:00
Vladimir Enchev
a6b8abb9ce version updated 2025-02-13 13:39:04 +02:00
Vladimir Enchev
38feb54237 Various components reworked without dynamic LINQ (#1960) 2025-02-13 13:35:41 +02:00
Vladimir Enchev
ee828de086 version updated 2025-02-12 18:05:26 +02:00
Vladimir Enchev
bc82638fea DataGrid ReloadSettings() method added 2025-02-12 18:04:20 +02:00
Lynkle
edb6f58f66 Allow AutoComplete with OpenOnFocus to show items even without user input (#1957)
* fix(autocomplete): OpenOnFocus now lets you view items in the dropdown even if you have not provided any searchText. This is for the case where you want the autocomplete but the user does/may not know what it is they want to complete.

* fix(autocomplete): Adjusted it so that removing search text back to null/empty string will reset autocomplete suggestions.
2025-02-11 18:19:59 +02:00
nielsNocore
23b9a6e7bf Fix IsAllSelectedInDropdown is correct when disabled items exists (#1953)
added unit tests for it.
2025-02-10 12:03:52 +02:00
Vladimir Enchev
0b3c6058e2 Fixed Tree recursion error during expand with SingleExpand=true 2025-02-07 14:47:12 +02:00
yordanov
cb6572980e Add demo video for Radzen Blazor for Visual Studio 2025-02-06 11:18:11 +02:00
Vladimir Enchev
78e5bbb9dc version updated 2025-02-06 10:58:16 +02:00
Vladimir Enchev
12b52b3e79 ShowVideoDialog updated 2025-02-06 10:57:41 +02:00
yordanov
e226749388 Add input width: 100%; for rz-helper-hidden-accessible class 2025-02-06 10:47:44 +02:00
yordanov
001cd0cdcd Remove input width: 0; for rz-helper-hidden-accessible class as it breaks default DropDown width 2025-02-06 10:00:52 +02:00
Vladimir Enchev
c3806348a0 Fixed FileInput breaks if both MaxWidth and MaxHeight is set and a non image is selected
Fix #1922
2025-02-06 09:28:56 +02:00
nielsNocore
c6a8d80d45 add DisabledProperty to radzenPickList (#1946)
* add DisabledProperty to radzenPickList

* moved picklist icons to be properties, to be able to custimze the icons in the picklist
2025-02-05 17:23:29 +02:00
nielsNocore
2babacd15a moved picklist icons to be properties, to be able to custimze the icons in the picklist (#1947) 2025-02-05 17:22:51 +02:00
nielsNocore
456ab29a34 added parameter to set "AllowSelectAll" in de radzenPicklist (#1945)
* added parameter to set "AllowSelectAll" in de radzenPicklist

* added default value to radzenPickList.AllowSelectAll to be true
2025-02-05 15:00:25 +02:00
Vladimir Enchev
1de201841a DataGrid CheckBoxList filter mode with EF Core data source crashes application when using keyboard
Fix #1943
2025-02-05 09:14:36 +02:00
yordanov
a5586fd4ea Add demo of RadzenIcon using FontAwesome icons 2025-02-04 17:30:01 +02:00
Vladimir Enchev
a14ac78957 Dynamic and version updated 2025-02-03 14:36:04 +02:00
yordanov
1193ede1ca Update FormField demos 2025-02-03 14:23:47 +02:00
yordanov
8ad75a1773 Update premium themes 2025-02-03 13:58:41 +02:00
rklfss
9ea1b44e64 RadzenDatePicker floating label and RadzenFormField support (#1933)
* Added support to use RadzenDatePicker in RadzenFormField

- added rz-state-empty css class to RadzenDatePicker if there is no value
- added css to handle floating labels for any form field component within RadzenFormField

* RadzenDatePicker minor code fixes and improvements

* RadzenDatePicker Demo show time part
2025-02-03 13:39:40 +02:00
Josh
b92ef7922d Add Notify overload that accepts a Timespan for duration, for improved clarity (#1936) 2025-02-03 13:20:26 +02:00
Vasil Yordanov
3d6eff639f Unify styles and behavior of forms components when invalid css class is present (#1938) 2025-02-03 10:25:13 +02:00
Atanas Korchev
011e761d7f RadzenTheme could throw a null reference exception during dispose in rare cases. 2025-02-03 09:31:52 +02:00
Atanas Korchev
a2fdb687a1 Setting the Value of RadzenHtmlEditorBackground makes it impossible to pick a color. 2025-01-31 17:21:53 +02:00
jamesalainheffer
ff846e0cc1 Added a parameter called "Size" to the "RadzenGravatar" component. (#1932)
* Added a parameter called "Size" to the "RadzenGravatar" component. This will allow for users to adjust the size of the gravatar image coming in, so that they are not stuck with just a 32px image.

* Corrected the default value from 32 to 36, and remove the width variable in favor of the parametised Size variable. (As per suggestion from akorchev - thanks)

* Corrected a typo where the "value" attribute was inside the summary, it is now outside, where it should be.
2025-01-31 09:57:22 +02:00
Vladimir Enchev
df0ad0d9f1 DropDownDataGrid item not selected after change outside of the component. 2025-01-30 16:20:35 +02:00
Vladimir Enchev
716d1a6cba Version updated 2025-01-29 08:58:01 +02:00
Frank
3e7468177d Added ReadOnly parameter for RadzenSwitch component (#1927)
* Added ReadOnly parameter for RadzenSwitch component

* Formatting
2025-01-29 08:57:29 +02:00
Vladimir Enchev
d4113e6715 Dynamic LINQ ParsingConfig initialization moved to RadzenComponent 2025-01-29 08:56:35 +02:00
Vladimir Enchev
7f7ce06d0e Version updated 2025-01-28 06:51:39 +02:00
nielsNocore
7cfea596fd fixed InvokeAsync(Reload); not awaited in datagrid en pagedDataBoundComponent (#1926) 2025-01-28 06:49:29 +02:00
Vladimir Enchev
7eae5d0f6e DataGrid sorting with dynamic data fixed 2025-01-28 06:48:29 +02:00
Vladimir Enchev
a390658ad9 DynamicLinqCustomTypeProvider RestrictOrderByToPropertyOrField set to false 2025-01-27 18:20:03 +02:00
Vladimir Enchev
ecc403cbbc Dynamic LINQ settings improved 2025-01-27 15:59:15 +02:00
Paul Ruston
e870ca856c Add DaySelect event to Day, Week and Month Views (#1910)
* Add DaySelect event to Day, Week and Month Views

* Resolution of comments

* Resolve incorrect code example. Also anomolies in Month example

* Changed AppointmentsInSlot call as per comment
2025-01-27 10:12:27 +02:00
Vladimir Enchev
e7b8fec063 version updated 2025-01-27 08:02:07 +02:00
Vladimir Enchev
7767878c4c System.Linq.Dynamic.Core updated 2025-01-27 07:54:17 +02:00
Vladimir Enchev
63de20fe36 DropDownBase multiple selection fixed 2025-01-26 19:55:06 +02:00
Vladimir Enchev
6114a9a8ed version updated 2025-01-26 15:37:56 +02:00
Vladimir Enchev
d5514ca0cd Dynamic LINQ AllowEqualsAndToStringMethodsOnObject set to true by default 2025-01-26 15:37:38 +02:00
Vladimir Enchev
8cf0565211 System.Linq.Dynamic.Core updated to latest preview 2025-01-26 15:14:10 +02:00
Vladimir Enchev
32246b1d69 code fixed 2025-01-26 15:06:31 +02:00
Vladimir Enchev
418dbf09ec System.Linq.Dynamic.Core updated 2025-01-24 22:45:47 +02:00
Vladimir Enchev
a198d09b71 System.Linq.Dynamic.Core updated to preview 2025-01-24 13:53:00 +02:00
yordanov
f3bb0e7b90 Timeline point icon size should not impact ChildContent icon size. Resolves #1919 2025-01-24 11:24:40 +02:00
Vladimir Enchev
b3917e19a2 Avoid using ToString() and Equals() methods with Dynamic LINQ 2025-01-24 11:09:18 +02:00
yordanov
e7d8b7454f Update Material theme secondary color value to meet WCAG AA contrast ratio criteria. Resolves #1923 2025-01-24 10:54:57 +02:00
yordanov
4d4660eed9 Update Material Symbols fonts 2025-01-23 13:53:39 +02:00
Vladimir Enchev
b80e47672d Version updated 2025-01-23 10:54:45 +02:00
Vladimir Enchev
3b370841cc System.Linq.Dynamic.Core updated 2025-01-23 10:43:02 +02:00
Atanas Korchev
7ec2a9a0a5 Update table demo descriptions. 2025-01-23 10:39:05 +02:00
Vladimir Enchev
e340a376ef code updated 2025-01-23 10:35:10 +02:00
Vladimir Enchev
a2709afc5c text fixed 2025-01-23 10:25:58 +02:00
Vladimir Enchev
9c7ed95f23 Table demos descriptions added 2025-01-23 10:21:59 +02:00
Vladimir Enchev
524f1f99cb missing comment added 2025-01-23 10:11:20 +02:00
yordanov
2ac6d7f2bb Add Table component to the demos homepage and Data section in main nav 2025-01-23 09:43:18 +02:00
Vladimir Enchev
6b3fd9efae Table with merged cells demo added 2025-01-23 09:40:19 +02:00
Vladimir Enchev
d99daa6538 demo updated 2025-01-23 09:27:41 +02:00
Vladimir Enchev
13e0bba379 Table demos reworked 2025-01-23 09:25:53 +02:00
Vladimir Enchev
ed7c5bc537 various Table fixes 2025-01-23 09:04:38 +02:00
Josh
03b7968b99 Expose Visible and IFormFieldContext on IRadzenFormComponent (#1920) 2025-01-23 08:55:32 +02:00
Vladimir Enchev
274d64246b Fixed DataGrid Enum Column Filter with Filtermode CheckBoxList not working
Fix #1918
2025-01-22 19:15:59 +02:00
Vladimir Enchev
4450053fd3 CustomText property added to localize DataGrid and DataFilter Custom filter operator 2025-01-22 19:01:07 +02:00
Vladimir Enchev
8c321f18e1 demos keyboard navigation details reworked with RadzenTable 2025-01-22 10:26:38 +02:00
Vladimir Enchev
e015807edc method name improved 2025-01-22 10:17:03 +02:00
Vladimir Enchev
4ebcd2f214 RadzenTable improved 2025-01-22 10:15:21 +02:00
Vladimir Enchev
67cd8b7a61 Demos keyboard support updated with RadzenTable 2025-01-21 17:55:48 +02:00
Vladimir Enchev
0a802ca73b RadzenTableHeader, RadzenTableHeaderRow and RadzenTableBody added 2025-01-21 15:31:18 +02:00
Vladimir Enchev
097f37bfbc Table GridLines and AllowAlternatingRows added 2025-01-21 15:12:38 +02:00
Vladimir Enchev
524e42980a RadzenTable component added 2025-01-21 11:09:08 +02:00
Vladimir Enchev
34eebc9406 version updated 2025-01-20 17:49:41 +02:00
Atanas Korchev
65f20c232c Linking to an anchor does not scroll to the anchor. 2025-01-20 17:46:25 +02:00
Andrey Dmitrienko
d742fdae86 Text shifting fix for RadzenDropDown (#1915)
* Text shifting fix for RadzenDropDown
- Add an input width: 0; configuration for rz-helper-hidden-accessible class

---------

Co-authored-by: Andrey Dmitrienko <andrey.dmitrienko@flexbricks.com>
2025-01-20 11:02:04 +02:00
Vladimir Enchev
397e2baf6c Fixed RadzenAutoComplete Value is not displayed in the input field when using Value and Change event
Fix #1914
2025-01-20 09:39:54 +02:00
Vladimir Enchev
4b1d951083 Version updated 2025-01-17 22:05:24 +02:00
ivan-rosales-rieloff
ab65a5a975 Add empty string support, culture based cast (#1912)
* add is valid decimal value validation  on ConvertToDecimal to avoid errors when decimal format is invalid, ie. double comma

* Support culture to convert strings, add support to empty string as zero value

* Culture specific number conversion test

* Add Specific culture, and default cuture numeric test

---------

Co-authored-by: Ivan Rosales <kanibal68@hotmail.com>
2025-01-17 22:04:45 +02:00
Vladimir Enchev
9947cbf47b RadzenDropDown with filtering on Android closes on open
Fix #1913
2025-01-17 10:10:09 +02:00
Vladimir Enchev
f781ba0c6a Version updated 2025-01-16 12:25:27 +02:00
Vladimir Enchev
e3c605a7a9 Version updated 2025-01-16 12:23:51 +02:00
Vladimir Enchev
849e6761b8 Revert "add is valid decimal value validation on ConvertToDecimal to avoid errors when decimal format is invalid, ie. double comma (#1911)"
This reverts commit fef5ceb36d.
2025-01-16 12:21:27 +02:00
Vladimir Enchev
407ef36b70 Version updaed 2025-01-16 11:26:33 +02:00
ivan-rosales-rieloff
fef5ceb36d add is valid decimal value validation on ConvertToDecimal to avoid errors when decimal format is invalid, ie. double comma (#1911)
Co-authored-by: Ivan Rosales <kanibal68@hotmail.com>
2025-01-16 11:23:37 +02:00
Atanas Korchev
2e22f556d8 Update DartSassBuilder. 2025-01-16 11:15:32 +02:00
Atanas Korchev
95b833402f Exception is thrown when all series values are 0 in certain value axis configuration. 2025-01-16 08:53:37 +02:00
Vladimir Enchev
c0e7418e7c RadzenAutoComplete SelectedItem property added 2025-01-15 19:52:03 +02:00
Vladimir Enchev
4d72ef1efe comment fixed 2025-01-15 16:04:58 +02:00
Vladimir Enchev
a00bce399f DialogService OpenSide() method added 2025-01-15 16:03:17 +02:00
yordanov
2b8658b233 Add info block for Radzen Blazor for Visual Studio 2025-01-15 15:31:34 +02:00
Vladimir Enchev
fc2784450b version updated 2025-01-14 17:11:52 +02:00
Vladimir Enchev
896d9bd3ae various warnings fixed 2025-01-14 17:11:42 +02:00
yordanov
9199096f69 Fix RadzenDatePicker input padding when trigger button is hidden 2025-01-14 11:44:45 +02:00
yordanov
bdb2694734 Make RadzenRating hover color more prominent and add focus outlines 2025-01-14 10:49:34 +02:00
Vladimir Enchev
98ab3cd3d0 Accordion dynamic items demo added 2025-01-14 10:33:43 +02:00
Vladimir Enchev
22f7d3f9f4 Accordion item Selected logic improved 2025-01-14 09:21:52 +02:00
Nopke
7f27dafe32 rating: (#1909)
* make stars filled when they're selected
* update hover styles to make them different from selected styles and similar to focus styles
2025-01-14 08:58:27 +02:00
Vladimir Enchev
37a1e6d4b5 Version updated 2025-01-13 15:05:37 +02:00
Pierluigi Mari
91d5473bf2 Bug fix present in RadzenDropDownDataGrid from version 5.7.0 (#1907)
When RadzenDropDownDataGrid is set to allow multi-selection and virtualization with LoadData, it doesn't keep all items selected as you scroll through the list. Optimized the initialization of selectedItems in DropDownBase, so that selectedItems is reinitialized only if empty, thus preserving existing items.

Co-authored-by: Pierluigi.Mari <pierluigi.mari@iqera.it>
2025-01-13 15:02:21 +02:00
yordanov
b883cccb30 Update premium themes 2025-01-13 14:58:14 +02:00
Nopke
2ca7b3ba5f Select bar orientation parameter (#1905)
Select bar:
* add 'orientation' param
* update styles
* reimplement item border duplication prevention (instead of negative margins just remove borders)
* add 'orientation' param example
* add 'updated' annotation
* add whitespace in .razor file to make it more comprehensible
2025-01-13 14:51:27 +02:00
Josh
faada7ae7b adds Disabled, from RadzenFormComponent<T>, to the IRadzenFormComponent interface, for better integration with cross-cutting form behavior (#1903) 2025-01-13 10:52:48 +02:00
Vladimir Enchev
3910fb778f All popups will be closed on window resize 2025-01-13 10:46:26 +02:00
Vladimir Enchev
4577d063cc Fixed Accordion cannot be collapsed using Selected property of the item 2025-01-13 10:37:50 +02:00
Vladimir Enchev
1f5c70166e Fixed RadzenDataFilter.ToFilterString() returns invalid filter for string does not contain
Fix #1906
2025-01-13 10:27:17 +02:00
Vladimir Enchev
f920f9f08d demo updated 2025-01-10 14:15:37 +02:00
Vladimir Enchev
e6272e88e6 text fixed 2025-01-10 11:27:01 +02:00
Vladimir Enchev
4be24abf7f Version updated 2025-01-10 10:56:33 +02:00
Vladimir Enchev
85cc05a144 Added Simple and SimpleWithMenu FilterMode support for DataGrid composite columns 2025-01-10 10:55:53 +02:00
yordanov
7f726c4e2a Fix RadzenTree icon margin should not propagate to treenode label's child content 2025-01-09 12:16:07 +02:00
Paul Ruston
5c86ffd2ac Add SelectedView to SchedulerLoadEventArgs (#1900) 2025-01-09 10:44:56 +02:00
Vladimir Enchev
257444b640 DataGrid sort order not set properly when SortProperty is different from Property 2025-01-09 09:39:08 +02:00
yordanov
c61d453de4 Remove end-of-year promo 2025-01-07 10:43:26 +02:00
Atanas Korchev
4b88b18e78 RadzenChart throws an exception when all series data is zero. Fixes #1885. 2025-01-06 11:49:09 +02:00
Vladimir Enchev
1125894e7a version updated 2025-01-06 10:29:24 +02:00
Vladimir Enchev
e8894360fa DataGrid CheckBoxList filter loading indicator added 2025-01-06 10:29:10 +02:00
David Kohout
168c071ac3 Support for inserting after specific row in DataGrid (#1894)
* Support to insert new row after specific row

* Demo page updated to show insert after row
2025-01-06 10:03:14 +02:00
zjelev
d6dd67951e keep your enums values in English & export in excel their description (#1895) 2025-01-06 09:39:01 +02:00
Bendegúz Török
c7e4470a60 Include link to excess appointments in the month check (as in YearPlannerView) (#1888) 2025-01-04 16:51:48 +02:00
Paul Ruston
cb6789504a Ability to Show / Hide Scheduler Header (#1891) 2025-01-04 15:30:58 +02:00
Vladimir Enchev
314664c4e2 Fixed Nullable Integer RadzenNumeric allows decimals
Fix #1880
2025-01-04 08:42:43 +02:00
Vladimir Enchev
58794f806c Numeric input not updated when Value is set to null 2025-01-04 08:28:32 +02:00
Nopke
6a470a6448 Standard dark theme base flag fix (#1889)
* Standard dark theme: remove incorrect 'base' flag

* Standard dark base theme: add missing 'base' flag
2025-01-02 18:59:12 +02:00
yordanov
27bf1713a0 Fix background colors of right inline frozen columns 2025-01-02 17:01:59 +02:00
Atanas Korchev
c1e428dd3e RadzenTree throws collection modified exception when SingleExpand is set to true. 2025-01-02 16:58:46 +02:00
yordanov
661e5b50f8 Update copyright year 2025-01-02 15:25:56 +02:00
Paul Ruston
749c2cf600 Update Scheduler Title when StartMonth changes in Year Views (#1873)
* Update Scheduler Title when StartMonth changes in Year Views

* Update to the Demo page

* Slight update to XML comment on SchedulerYearViewBase->StartMonth
2024-12-27 16:27:34 +02:00
Atanas Korchev
153e9e01bc Fix broken link. Closes #1881. 2024-12-27 16:26:14 +02:00
210 changed files with 4471 additions and 2618 deletions

View File

@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2018-2024 Radzen Ltd
Copyright (c) 2018-2025 Radzen Ltd
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -174,6 +174,34 @@ namespace Radzen.Blazor.Tests
Assert.Contains(@$"tabindex=""{value}""", component.Markup);
}
[Fact]
public void DatePicker_Renders_EmptyCssClass_WhenValueIsEmpty()
{
using var ctx = new TestContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var component = ctx.RenderComponent<RadzenDatePicker<DateTime>>();
component.SetParametersAndRender(parameters => parameters.Add(p => p.Value, null));
Assert.Contains(@$"rz-state-empty", component.Markup);
}
[Fact]
public void DatePicker_DoesNotRender_EmptyCssClass_WhenValueIsNotEmpty()
{
using var ctx = new TestContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var component = ctx.RenderComponent<RadzenDatePicker<DateTime>>();
component.SetParametersAndRender(parameters => parameters.Add(p => p.Value, DateTime.Now));
Assert.DoesNotContain(@$"rz-state-empty", component.Markup);
}
[Fact]
public void DatePicker_Renders_DisabledParameter()
{

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AngleSharp.Dom;
using Bunit;
@@ -14,18 +15,20 @@ namespace Radzen.Blazor.Tests
{
public string Text { get; set; }
public int Id { get; set; }
public bool Disabled { get; set; } = false;
}
private static IRenderedComponent<RadzenDropDown<T>> DropDown<T>(TestContext ctx, Action<ComponentParameterCollectionBuilder<RadzenDropDown<T>>> configure = null)
{
var data = new [] {
var data = new[] {
new DataItem { Text = "Item 1", Id = 1 },
new DataItem { Text = "Item 2", Id = 2 },
};
var component = ctx.RenderComponent<RadzenDropDown<T>>();
component.SetParametersAndRender(parameters => {
component.SetParametersAndRender(parameters =>
{
parameters.Add(p => p.Data, data);
parameters.Add(p => p.TextProperty, nameof(DataItem.Text));
@@ -100,7 +103,8 @@ namespace Radzen.Blazor.Tests
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
var component = DropDown<string>(ctx, parameters => {
var component = DropDown<string>(ctx, parameters =>
{
parameters.Add(p => p.ValueProperty, nameof(DataItem.Text));
});
@@ -123,7 +127,8 @@ namespace Radzen.Blazor.Tests
List<DataItem> boundCollection = [new() { Text = "Item 2" }];
var component = DropDown<string>(ctx, parameters => {
var component = DropDown<string>(ctx, parameters =>
{
parameters.Add(p => p.ItemComparer, new DataItemComparer());
parameters.Add(p => p.Multiple, true);
parameters.Add(p => p.Value, boundCollection);
@@ -150,7 +155,8 @@ namespace Radzen.Blazor.Tests
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
var component = DropDown<string>(ctx, parameters => {
var component = DropDown<string>(ctx, parameters =>
{
parameters.Add(p => p.ValueProperty, nameof(DataItem.Text));
parameters.Add(p => p.Multiple, true);
});
@@ -275,6 +281,50 @@ namespace Radzen.Blazor.Tests
Assert.Collection(selectedItems, item => Assert.Contains("value: Item 1", item.Text()), item => Assert.Contains("value: Item 2", item.Text()));
}
[Theory]
[InlineData(false, true, false, true, "false")]
[InlineData(true, false, true, false, "true")]
[InlineData(true, false, false, false, "false")]
[InlineData(true, false, false, true, "true")]
[InlineData(false, false, false, true, "false")]
public void DropDown_AllSelectedFalseIfListIsAllDisabled(bool item1Selected, bool item1Disabled, bool item2Selected, bool item2Disabled, string expectedAriaCheckedValue)
{
using var ctx = new TestContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
var data = new[] {
new DataItem { Text = "Item 1", Id = 1, Disabled = item1Disabled },
new DataItem { Text = "Item 2", Id = 2, Disabled = item2Disabled },
};
List<int> selectedValues = [];
if (item1Selected)
{
selectedValues.Add(data[0].Id);
}
if (item2Selected)
{
selectedValues.Add(data[1].Id);
}
var component = ctx.RenderComponent<RadzenDropDown<DataItem>>(parameters => parameters
.Add(p => p.Data, data)
.Add(p => p.Value, selectedValues)
.Add(p => p.Multiple, true)
.Add(p => p.AllowSelectAll, true)
.Add(p => p.TextProperty, nameof(DataItem.Text))
.Add(p => p.DisabledProperty, nameof(DataItem.Disabled))
.Add(p => p.ValueProperty, nameof(DataItem.Id)));
Assert.NotNull(component);
var highlightedItems = component.FindAll(".rz-state-highlight");
Assert.Equal(selectedValues.Count, highlightedItems.Count);
var selectAllCheckBox = component.Find(".rz-multiselect-header input[type='checkbox']");
Assert.Equal(expectedAriaCheckedValue, selectAllCheckBox.GetAttribute("aria-checked"));
}
class DataItemComparer : IEqualityComparer<DataItem>, IEqualityComparer<object>
{
@@ -292,7 +342,7 @@ namespace Radzen.Blazor.Tests
return obj.Text.GetHashCode();
}
public bool Equals(object x, object y)
public new bool Equals(object x, object y)
{
return Equals((DataItem)x, (DataItem)y);
}

View File

@@ -28,6 +28,10 @@ namespace Radzen.Blazor.Tests
throw new NotImplementedException();
}
public bool Disabled { get; set; }
public bool Visible { get; set; }
public IFormFieldContext FormFieldContext => null;
public object Value { get; set; }
}
@@ -169,4 +173,4 @@ namespace Radzen.Blazor.Tests
Assert.False(component.Instance.Validate(DateTime.Now));
}
}
}
}

View File

@@ -463,10 +463,11 @@ namespace Radzen.Blazor.Tests
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var value = new Dollars(11m);
Dollars? ConvertFunc(string s) => decimal.TryParse(s, out var val) ? new Dollars(val) : null;
Dollars? ConvertFunc(string s) => decimal.TryParse(s, System.Globalization.CultureInfo.InvariantCulture, out var val) ? new Dollars(val) : null;
var component = ctx.RenderComponent<RadzenNumeric<Dollars?>>(
ComponentParameter.CreateParameter(nameof(RadzenNumeric<Dollars?>.ConvertValue), (Func<string, Dollars?>)ConvertFunc),
ComponentParameter.CreateParameter(nameof(RadzenNumeric<Dollars?>.Value), value)
ComponentParameter.CreateParameter(nameof(RadzenNumeric<Dollars?>.Value), value),
ComponentParameter.CreateParameter(nameof(RadzenNumeric<Dollars>.Culture), System.Globalization.CultureInfo.InvariantCulture)
);
component.Render();
@@ -494,7 +495,101 @@ namespace Radzen.Blazor.Tests
component.Render();
Assert.Contains($" value=\"{valueToTest.ToString(format)}\"", component.Markup);
Assert.Contains($" value=\"{valueToTest.ToString(format, System.Globalization.CultureInfo.CurrentCulture)}\"", component.Markup);
}
[Fact]
public void Numeric_Supports_TypeConverterWithCulture()
{
using var ctx = new TestContext();
var valueToTest = new Dollars(100.234m);
string format = "0.00";
var component = ctx.RenderComponent<RadzenNumeric<Dollars>>(
ComponentParameter.CreateParameter(nameof(RadzenNumeric<Dollars>.Format), format),
ComponentParameter.CreateParameter(nameof(RadzenNumeric<Dollars>.Value), valueToTest),
ComponentParameter.CreateParameter(nameof(RadzenNumeric<Dollars>.Culture), System.Globalization.CultureInfo.InvariantCulture)
);
component.Render();
Assert.Contains($" value=\"{valueToTest.ToString(format, System.Globalization.CultureInfo.InvariantCulture)}\"", component.Markup);
}
[Fact]
public void Numeric_Supports_EmptyString()
{
using var ctx = new TestContext();
var valueToTest = "";
string format = "0.00";
var component = ctx.RenderComponent<RadzenNumeric<string>>(
ComponentParameter.CreateParameter(nameof(RadzenNumeric<string>.Format), format),
ComponentParameter.CreateParameter(nameof(RadzenNumeric<string>.Value), valueToTest),
ComponentParameter.CreateParameter(nameof(RadzenNumeric<Dollars>.Culture), System.Globalization.CultureInfo.InvariantCulture)
);
component.Render();
Assert.Contains($" value=\"0.00\"", component.Markup);
}
[Fact]
public void Numeric_Supports_ValueString()
{
using var ctx = new TestContext();
var valueToTest = "12.50";
string format = "0.00";
var component = ctx.RenderComponent<RadzenNumeric<string>>(
ComponentParameter.CreateParameter(nameof(RadzenNumeric<string>.Format), format),
ComponentParameter.CreateParameter(nameof(RadzenNumeric<string>.Value), valueToTest),
ComponentParameter.CreateParameter(nameof(RadzenNumeric<Dollars>.Culture), System.Globalization.CultureInfo.InvariantCulture)
);
component.Render();
Assert.Contains($" value=\"{valueToTest}\"", component.Markup);
}
[Fact]
public void Numeric_Supports_ValueStringEsCLCulture()
{
using var ctx = new TestContext();
var valueToTest = "12,50";
string format = "0.00";
var component = ctx.RenderComponent<RadzenNumeric<string>>(
ComponentParameter.CreateParameter(nameof(RadzenNumeric<string>.Format), format),
ComponentParameter.CreateParameter(nameof(RadzenNumeric<string>.Value), valueToTest),
ComponentParameter.CreateParameter(nameof(RadzenNumeric<Dollars>.Culture), System.Globalization.CultureInfo.GetCultureInfo("es-CL"))
);
component.Render();
Assert.Contains($" value=\"{valueToTest}\"", component.Markup);
}
[Fact]
public void Numeric_Supports_ValueStringEnUSCulture()
{
using var ctx = new TestContext();
var valueToTest = "12.50";
string format = "0.00";
var component = ctx.RenderComponent<RadzenNumeric<string>>(
ComponentParameter.CreateParameter(nameof(RadzenNumeric<string>.Format), format),
ComponentParameter.CreateParameter(nameof(RadzenNumeric<string>.Value), valueToTest),
ComponentParameter.CreateParameter(nameof(RadzenNumeric<Dollars>.Culture), System.Globalization.CultureInfo.GetCultureInfo("en-US"))
);
component.Render();
Assert.Contains($" value=\"{valueToTest}\"", component.Markup);
}
[Fact]

View File

@@ -1,8 +1,5 @@
using AngleSharp.Css;
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using Xunit;
namespace Radzen.Blazor.Tests

View File

@@ -134,5 +134,18 @@ namespace Radzen.Blazor.Tests
Assert.True(raised);
Assert.True(object.Equals(value, !(bool)newValue));
}
[Fact]
public void Switch_Renders_ReadOnlyParameter()
{
using var ctx = new TestContext();
var component = ctx.RenderComponent<RadzenSwitch>();
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.ReadOnly, true));
Assert.Contains(@$"rz-readonly", component.Markup);
}
}
}

View File

@@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Components;
using System.Linq;
using System.Linq.Dynamic.Core;
using Radzen.Blazor.Rendering;
using System.Threading.Tasks;
using System.Net.Mime;
@@ -410,7 +409,7 @@ namespace Radzen.Blazor
if (IsDate(CategoryProperty) || IsNumeric(CategoryProperty))
{
Items = Items.AsQueryable().OrderBy(DynamicLinqCustomTypeProvider.ParsingConfig, CategoryProperty).ToList();
Items = Items.AsQueryable().OrderBy(CategoryProperty).ToList();
}
}

View File

@@ -7,11 +7,11 @@ using Radzen.Blazor.Rendering;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Linq.Dynamic.Core.Parser;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Metadata;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
@@ -2287,6 +2287,18 @@ namespace Radzen
/// </summary>
/// <value>The property.</value>
public string Property { get; set; }
/// <summary>
/// Gets or sets the property type.
/// </summary>
/// <value>The property type.</value>
public Type Type { get; set; }
/// <summary>
/// Gets or sets the name of the filtered property.
/// </summary>
/// <value>The property.</value>
public string FilterProperty { get; set; }
/// <summary>
/// Gets or sets the value to filter by.
/// </summary>
@@ -2325,6 +2337,18 @@ namespace Radzen
/// <value>The property.</value>
public string Property { get; set; }
/// <summary>
/// Gets or sets the property type.
/// </summary>
/// <value>The property type.</value>
public Type Type { get; set; }
/// <summary>
/// Gets or sets the name of the filtered property.
/// </summary>
/// <value>The property.</value>
public string FilterProperty { get; set; }
/// <summary>
/// Gets or sets the value to filter by.
/// </summary>
@@ -2428,6 +2452,43 @@ namespace Radzen
public int Level { get; set; }
}
/// <summary>
/// The result of a call to a <see cref="QueryableExtension"/>.GroupByMany() overload.
/// </summary>
public class GroupResult
{
/// <summary>
/// The key value of the group.
/// </summary>
public dynamic Key { get; internal set; } = null!;
/// <summary>
/// The number of resulting elements in the group.
/// </summary>
public int Count { get; internal set; }
/// <summary>
/// The resulting elements in the group.
/// </summary>
public IEnumerable Items { get; internal set; }
/// <summary>
/// The resulting subgroups in the group.
/// </summary>
public IEnumerable<GroupResult> Subgroups { get; internal set; }
/// <summary>
/// Returns a <see cref="System.String" /> showing the key of the group and the number of items in the group.
/// </summary>
/// <returns>
/// A <see cref="System.String" /> that represents this instance.
/// </returns>
public override string ToString()
{
return string.Format(CultureInfo.CurrentCulture, "{0} ({1})", ((object)Key).ToString(), Count);
}
}
/// <summary>
/// Supplies information about a <see cref="PagedDataBoundComponent{TItem}.LoadData" /> event that is being raised.
/// </summary>
@@ -2902,9 +2963,14 @@ namespace Radzen
/// </summary>
/// <param name="value">The value.</param>
/// <param name="type">The type.</param>
/// <param name="culture">The culture.</param>
/// <returns>System.Object</returns>
public static object ChangeType(object value, Type type)
public static object ChangeType(object value, Type type, CultureInfo culture = null)
{
if (culture == null)
{
culture = CultureInfo.CurrentCulture;
}
if (value == null && Nullable.GetUnderlyingType(type) != null)
{
return value;
@@ -2932,7 +2998,7 @@ namespace Radzen
}
return value is IConvertible ? Convert.ChangeType(value, Nullable.GetUnderlyingType(type) ?? type) : value;
return value is IConvertible ? Convert.ChangeType(value, Nullable.GetUnderlyingType(type) ?? type, culture) : value;
}
}
@@ -2953,7 +3019,9 @@ namespace Radzen
{
if (propertyName.Contains("["))
{
return DynamicExpressionParser.ParseLambda<TItem, TValue>(null, false, propertyName).Compile();
var arg = Expression.Parameter(typeof(TItem));
return Expression.Lambda<Func<TItem, TValue>>(QueryableExtension.GetNestedPropertyExpression(arg, propertyName, type), arg).Compile();
}
else
{
@@ -3105,11 +3173,6 @@ namespace Radzen
}
var propertyName = $"{(type != null ? "@" : "")}{property}";
if (propertyName.IndexOf(".") != -1)
{
return $"np({propertyName})";
}
return propertyName;
}
@@ -3405,6 +3468,21 @@ namespace Radzen
/// Sets the focus.
/// </summary>
ValueTask FocusAsync();
/// <summary>
/// Sets the Disabled state of the component
/// </summary>
bool Disabled { get; set; }
/// <summary>
/// Sets the Visible state of the component
/// </summary>
bool Visible { get; set; }
/// <summary>
/// Sets the FormFieldContext of the component
/// </summary>
IFormFieldContext FormFieldContext { get; }
}
/// <summary>
@@ -3789,4 +3867,4 @@ namespace Radzen
/// </summary>
Right
}
}
}

View File

@@ -3,12 +3,9 @@ using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Web;
using Radzen.Blazor;
using Radzen.Blazor.Rendering;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Linq.Expressions;
using System.Threading.Tasks;
@@ -262,23 +259,7 @@ namespace Radzen
{
if (!string.IsNullOrEmpty(searchText))
{
var ignoreCase = FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive;
var query = new List<string>();
if (!string.IsNullOrEmpty(TextProperty))
{
query.Add(TextProperty);
}
if (ignoreCase)
{
query.Add("ToLower()");
}
query.Add($"{Enum.GetName(typeof(StringFilterOperator), FilterOperator)}(@0)");
_view = Query.Where(DynamicLinqCustomTypeProvider.ParsingConfig, string.Join(".", query), ignoreCase ? searchText.ToLower() : searchText);
_view = Query.Where(TextProperty, searchText, FilterOperator, FilterCaseSensitivity);
}
else
{

View File

@@ -180,6 +180,27 @@ namespace Radzen
return _sideDialogTask.Task;
}
/// <summary>
/// Opens a side dialog with the specified arguments
/// </summary>
/// <typeparam name="T">The type of Blazor component which will be displayed in the side dialog.</typeparam>
/// <param name="title">The text displayed in the title bar of the side dialog.</param>
/// <param name="parameters">The dialog parameters. Passed as property values of <typeparamref name="T"/></param>
/// <param name="options">The side dialog options.</param>
public void OpenSide<T>(string title, Dictionary<string, object> parameters = null, SideDialogOptions options = null)
where T : ComponentBase
{
CloseSide();
if (options == null)
{
options = new SideDialogOptions();
}
options.Title = title;
OnSideOpen?.Invoke(typeof(T), parameters ?? new Dictionary<string, object>(), options);
}
/// <summary>
/// Closes the side dialog
/// </summary>
@@ -189,8 +210,9 @@ namespace Radzen
if (_sideDialogTask?.Task.IsCompleted == false)
{
_sideDialogTask.TrySetResult(result);
OnSideClose?.Invoke(result);
}
OnSideClose?.Invoke(result);
}
/// <summary>

View File

@@ -6,7 +6,6 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;
namespace Radzen
@@ -341,15 +340,17 @@ namespace Radzen
internal bool IsAllSelected()
{
List<object> notDisabledItemsInList = View.Cast<object>().ToList()
.Where(i => disabledPropertyGetter == null || disabledPropertyGetter(i) as bool? != true)
.ToList();
if (LoadData.HasDelegate && !string.IsNullOrEmpty(ValueProperty))
{
return View != null && View.Cast<object>().ToList()
.Where(i => disabledPropertyGetter != null ? disabledPropertyGetter(i) as bool? != true : true)
return View != null && notDisabledItemsInList.Count > 0 && notDisabledItemsInList
.All(i => IsItemSelectedByValue(GetItemOrValueFromProperty(i, ValueProperty)));
}
return View != null && selectedItems.Count == View.Cast<object>().ToList()
.Where(i => disabledPropertyGetter != null ? disabledPropertyGetter(i) as bool? != true : true).Count();
return View != null && notDisabledItemsInList.Count > 0 && selectedItems.Count == notDisabledItemsInList.Count;
}
/// <summary>
@@ -434,9 +435,13 @@ namespace Radzen
var type = query.ElementType;
if (type == typeof(object) && typeof(EnumerableQuery).IsAssignableFrom(query.GetType()) && query.Any())
if (type == typeof(object) && typeof(EnumerableQuery).IsAssignableFrom(query.GetType()) && query.Cast<object>().Any())
{
type = query.FirstOrDefault().GetType();
var firstElement = query.Cast<object>().FirstOrDefault(i => i != null);
if (firstElement != null)
{
type = firstElement.GetType();
}
}
if (!string.IsNullOrEmpty(ValueProperty))
@@ -454,7 +459,10 @@ namespace Radzen
disabledPropertyGetter = GetGetter(DisabledProperty, type);
}
selectedItems = new HashSet<object>(ItemComparer);
if (selectedItems.Count == 0)
{
selectedItems = new HashSet<object>(ItemComparer);
}
}
}
@@ -737,8 +745,7 @@ namespace Radzen
var filteredItems = (!string.IsNullOrEmpty(TextProperty) ?
Query.Where(TextProperty, args.Key, StringFilterOperator.StartsWith, FilterCaseSensitivity.CaseInsensitive) :
Query)
.Cast<object>()
.ToList();
.Cast(Query.ElementType).Cast<dynamic>().ToList();
if (previousKey != args.Key)
@@ -1136,9 +1143,13 @@ namespace Radzen
var query = Data.AsQueryable();
var elementType = query.ElementType;
if (elementType == typeof(object) && typeof(EnumerableQuery).IsAssignableFrom(query.GetType()) && query.Any())
if (elementType == typeof(object) && typeof(EnumerableQuery).IsAssignableFrom(query.GetType()) && query.Cast<object>().Any())
{
elementType = query.FirstOrDefault().GetType();
var firstElement = query.Cast<object>().FirstOrDefault(i => i != null);
if (firstElement != null)
{
elementType = firstElement.GetType();
}
}
if (elementType != null)
@@ -1218,7 +1229,7 @@ namespace Radzen
}
else
{
selectedItems = selectedItems.AsQueryable().Where(DynamicLinqCustomTypeProvider.ParsingConfig, $@"!object.Equals(it.{ValueProperty},@0)", value).ToHashSet(ItemComparer);
selectedItems = selectedItems.AsQueryable().Where(i => !object.Equals(GetItemOrValueFromProperty(i, ValueProperty), value)).ToHashSet(ItemComparer);
}
}
else
@@ -1249,7 +1260,16 @@ namespace Radzen
}
else
{
SelectedItem = view.AsQueryable().Where(DynamicLinqCustomTypeProvider.ParsingConfig, $@"{ValueProperty} == @0", value).FirstOrDefault();
SelectedItem = view.AsQueryable().Where(new FilterDescriptor[]
{
new FilterDescriptor()
{
Property = ValueProperty,
FilterValue = value
}
},
LogicalFilterOperator.And,
FilterCaseSensitivity.Default).FirstOrDefault();
}
}
else
@@ -1266,7 +1286,7 @@ namespace Radzen
{
if (!string.IsNullOrEmpty(ValueProperty))
{
foreach (object v in values.ToDynamicList())
foreach (object v in values.Cast<dynamic>().ToList())
{
dynamic item;
@@ -1276,10 +1296,19 @@ namespace Radzen
}
else
{
item = view.AsQueryable().Where(DynamicLinqCustomTypeProvider.ParsingConfig, $@"{ValueProperty} == @0", v).FirstOrDefault();
item = view.AsQueryable().Where(new FilterDescriptor[]
{
new FilterDescriptor()
{
Property = ValueProperty,
FilterValue = v
}
},
LogicalFilterOperator.And,
FilterCaseSensitivity.Default).FirstOrDefault();
}
if (!object.Equals(item, null) && !selectedItems.AsQueryable().Where(DynamicLinqCustomTypeProvider.ParsingConfig, $@"object.Equals(it.{ValueProperty},@0)", v).Any())
if (!object.Equals(item, null) && !selectedItems.AsQueryable().Where(i => object.Equals(GetItemOrValueFromProperty(i, ValueProperty), v)).Any())
{
selectedItems.Add(item);
}

View File

@@ -0,0 +1,753 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Radzen;
using Radzen.Blazor;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.Common;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Reflection.Metadata;
using System.Runtime.CompilerServices;
using System.Runtime.Loader;
using System.Text;
using System.Text.RegularExpressions;
namespace System.Linq.Dynamic.Core
{
/// <summary>
/// Class DynamicExtensions used to replace System.Linq.Dynamic.Core library.
/// </summary>
public static class DynamicExtensions
{
static string rtPath = Path.GetDirectoryName(typeof(object).Assembly.Location);
static CSharpCompilation Compilation = CSharpCompilation.Create(Guid.NewGuid().ToString())
.WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
.WithOptimizationLevel(OptimizationLevel.Debug))
.AddReferences(MetadataReference.CreateFromFile(Path.Combine(rtPath, "System.Runtime.dll")))
.AddReferences(MetadataReference.CreateFromFile(Path.Combine(rtPath, "System.Collections.dll")))
.AddReferences(MetadataReference.CreateFromFile(typeof(object).Assembly.Location))
.AddReferences(MetadataReference.CreateFromFile(typeof(Expression).Assembly.Location))
.AddReferences(MetadataReference.CreateFromFile(typeof(Queryable).Assembly.Location))
.AddReferences(MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location));
private static Assembly Compile(CSharpCompilation compilation, string code, AssemblyLoadContext context)
{
var errors = compilation.GetDiagnostics().Where(d => d.Severity == DiagnosticSeverity.Error);
if (errors.Any())
{
var message = string.Join(Environment.NewLine, errors.Select(e => e.GetMessage()));
throw new InvalidOperationException($"Compilation of {code} failed: {message}");
}
using var projectStream = new MemoryStream();
var result = compilation.Emit(projectStream);
if (!result.Success)
{
errors = result.Diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error);
var message = string.Join(Environment.NewLine, errors.Select(e => e.GetMessage()));
throw new InvalidOperationException(message);
}
projectStream.Seek(0, SeekOrigin.Begin);
return context.LoadFromStream(projectStream);
}
/// <summary>
/// Filters using the specified filter descriptors.
/// </summary>
public static IQueryable<T> Where<T>(
this IQueryable<T> source,
string selector,
object[] parameters = null, object[] otherParameters = null)
{
try
{
if (parameters != null)
{
for (var i = 0; i < parameters.Length; i++)
{
var value = object.Equals(parameters[i], string.Empty) ? @"""""" :
parameters[i] == null ? @"null" :
parameters[i] is string ? @$"""{parameters[i].ToString().Replace("\"", "\\\"")}""" :
parameters[i] is bool ? $"{parameters[i]}".ToLower() : parameters[i];
selector = selector.Replace($"@{i}", $"{value}");
}
}
var code = $@"
using System;
using System.Linq;
using System.Linq.Expressions;
namespace Dynamic;
public static class Linq
{{
public static Expression<Func<{typeof(T).FullName.Replace("+", ".")}, bool>> where = {(selector == "true" ? "i => true" : selector).Replace("DateTime", "DateTime.Parse").Replace("DateTimeOffset", "DateTimeOffset.Parse").Replace("DateOnly", "DateOnly.Parse").Replace("Guid", "Guid.Parse").Replace(" = "," == ")};
}}";
var assembly = Compile(Compilation
.AddReferences(MetadataReference.CreateFromFile(typeof(T).Assembly.Location))
.AddSyntaxTrees(CSharpSyntaxTree.ParseText(code, new CSharpParseOptions(LanguageVersion.Latest))),
code, new AssemblyLoadContext("RadzenALC", true));
Expression<Func<T, bool>> whereMethod = (Expression<Func<T, bool>>)assembly
.GetType("Dynamic.Linq").GetFields().FirstOrDefault().GetValue(null);
return source.Where(whereMethod);
}
catch
{
throw new InvalidOperationException($"Invalid Where selector");
}
}
/// <summary>
/// Sorts the elements of a sequence in ascending or descending order according to a key.
/// </summary>
public static IQueryable<T> OrderBy<T>(
this IQueryable<T> source,
string selector,
object[] parameters = null)
{
return Radzen.QueryableExtension.OrderBy(source, selector);
}
/// <summary>
/// Projects each element of a sequence into a collection of property values.
/// </summary>
public static IQueryable Select<T>(
this IQueryable<T> source,
string selector,
object[] parameters = null)
{
try
{
var parameter = Expression.Parameter(typeof(T), "it");
var className = $"Class{Guid.NewGuid()}".Replace("-", "");
var properties = selector.Replace("new (", "").Replace(")", "").Trim().Split(",", StringSplitOptions.RemoveEmptyEntries);
var declaredProperties = string.Join(Environment.NewLine, properties
.Select(s =>
{
var original = s.Split(" as ").FirstOrDefault().Trim();
var property = QueryableExtension.GetNestedPropertyExpression(parameter, original);
var name = s.Contains(" as ") ? s.Split(" as ").LastOrDefault().Trim() : s.Trim();
return $@"public {property.Type.DisplayName(fullName: false)} {name} {{ get; set; }}";
}));
selector = string.Join(", ", properties
.Select(s => (s.Contains(" as ") ? s.Split(" as ").LastOrDefault().Trim() : s.Trim()) + " = " + $"it.{s.Split(" as ").FirstOrDefault().Replace(".", "?.").Trim()}"));
var code = $@"
using System;
using System.Linq;
using System.Linq.Expressions;
namespace Dynamic;
public class {className}
{{
{declaredProperties}
}}
public static class Linq
{{
public static IQueryable Select(IQueryable source)
{{
var list = System.Linq.Enumerable.ToList(System.Linq.Queryable.Cast<{typeof(T).FullName}>(source));
return System.Linq.Queryable.AsQueryable(list.Select(it => new {className}() {{ {selector} }}));
}}
}}";
var assembly = Compile(Compilation
.AddReferences(MetadataReference.CreateFromFile(typeof(T).Assembly.Location))
.AddSyntaxTrees(CSharpSyntaxTree.ParseText(code, new CSharpParseOptions(LanguageVersion.Latest))),
code, new AssemblyLoadContext("RadzenALC", true));
return (IQueryable)assembly.GetType("Dynamic.Linq").GetMethods().FirstOrDefault().Invoke(null, new object[] { source });
}
catch
{
throw new InvalidOperationException($"Invalid selector");
}
}
}
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" }
};
public static Type UnwrapNullableType(this Type type)
=> Nullable.GetUnderlyingType(type) ?? type;
public static bool IsNullableValueType(this Type type)
=> type.IsConstructedGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
public static bool IsNullableType(this Type type)
=> !type.IsValueType || type.IsNullableValueType();
public static bool IsValidEntityType(this Type type)
=> type.IsClass
&& !type.IsArray;
public static bool IsPropertyBagType(this Type type)
{
if (type.IsGenericTypeDefinition)
{
return false;
}
var types = GetGenericTypeImplementations(type, typeof(IDictionary<,>));
return types.Any(
t => t.GetGenericArguments()[0] == typeof(string)
&& t.GetGenericArguments()[1] == typeof(object));
}
public static Type MakeNullable(this Type type, bool nullable = true)
=> type.IsNullableType() == nullable
? type
: nullable
? typeof(Nullable<>).MakeGenericType(type)
: type.UnwrapNullableType();
public static bool IsNumeric(this Type type)
{
type = type.UnwrapNullableType();
return type.IsInteger()
|| type == typeof(decimal)
|| type == typeof(float)
|| type == typeof(double);
}
public static bool IsInteger(this Type type)
{
type = type.UnwrapNullableType();
return type == typeof(int)
|| type == typeof(long)
|| type == typeof(short)
|| type == typeof(byte)
|| type == typeof(uint)
|| type == typeof(ulong)
|| type == typeof(ushort)
|| type == typeof(sbyte)
|| type == typeof(char);
}
public static bool IsSignedInteger(this Type type)
=> type == typeof(int)
|| type == typeof(long)
|| type == typeof(short)
|| type == typeof(sbyte);
public static bool IsAnonymousType(this Type type)
=> type.Name.StartsWith("<>", StringComparison.Ordinal)
&& type.GetCustomAttributes(typeof(CompilerGeneratedAttribute), inherit: false).Length > 0
&& type.Name.Contains("AnonymousType");
public static PropertyInfo GetAnyProperty(this Type type, string name)
{
var props = type.GetRuntimeProperties().Where(p => p.Name == name).ToList();
if (props.Count > 1)
{
throw new AmbiguousMatchException();
}
return props.SingleOrDefault();
}
public static bool IsInstantiable(this Type type)
=> !type.IsAbstract
&& !type.IsInterface
&& (!type.IsGenericType || !type.IsGenericTypeDefinition);
public static Type UnwrapEnumType(this Type type)
{
var isNullable = type.IsNullableType();
var underlyingNonNullableType = isNullable ? type.UnwrapNullableType() : type;
if (!underlyingNonNullableType.IsEnum)
{
return type;
}
var underlyingEnumType = Enum.GetUnderlyingType(underlyingNonNullableType);
return isNullable ? MakeNullable(underlyingEnumType) : underlyingEnumType;
}
public static Type GetSequenceType(this Type type)
{
var sequenceType = TryGetSequenceType(type);
if (sequenceType == null)
{
throw new ArgumentException($"The type {type.Name} does not represent a sequence");
}
return sequenceType;
}
public static Type TryGetSequenceType(this Type type)
=> type.TryGetElementType(typeof(IEnumerable<>))
?? type.TryGetElementType(typeof(IAsyncEnumerable<>));
public static Type TryGetElementType(this Type type, Type interfaceOrBaseType)
{
if (type.IsGenericTypeDefinition)
{
return null;
}
var types = GetGenericTypeImplementations(type, interfaceOrBaseType);
Type singleImplementation = null;
foreach (var implementation in types)
{
if (singleImplementation == null)
{
singleImplementation = implementation;
}
else
{
singleImplementation = null;
break;
}
}
return singleImplementation?.GenericTypeArguments.FirstOrDefault();
}
public static bool IsCompatibleWith(this Type propertyType, Type fieldType)
{
if (propertyType.IsAssignableFrom(fieldType)
|| fieldType.IsAssignableFrom(propertyType))
{
return true;
}
var propertyElementType = propertyType.TryGetSequenceType();
var fieldElementType = fieldType.TryGetSequenceType();
return propertyElementType != null
&& fieldElementType != null
&& IsCompatibleWith(propertyElementType, fieldElementType);
}
public static IEnumerable<Type> GetGenericTypeImplementations(this Type type, Type interfaceOrBaseType)
{
var typeInfo = type.GetTypeInfo();
if (!typeInfo.IsGenericTypeDefinition)
{
var baseTypes = interfaceOrBaseType.GetTypeInfo().IsInterface
? typeInfo.ImplementedInterfaces
: type.GetBaseTypes();
foreach (var baseType in baseTypes)
{
if (baseType.IsGenericType
&& baseType.GetGenericTypeDefinition() == interfaceOrBaseType)
{
yield return baseType;
}
}
if (type.IsGenericType
&& type.GetGenericTypeDefinition() == interfaceOrBaseType)
{
yield return type;
}
}
}
public static IEnumerable<Type> GetBaseTypes(this Type type)
{
var currentType = type.BaseType;
while (currentType != null)
{
yield return currentType;
currentType = currentType.BaseType;
}
}
public static List<Type> GetBaseTypesAndInterfacesInclusive(this Type type)
{
var baseTypes = new List<Type>();
var typesToProcess = new Queue<Type>();
typesToProcess.Enqueue(type);
while (typesToProcess.Count > 0)
{
type = typesToProcess.Dequeue();
baseTypes.Add(type);
if (type.IsNullableValueType())
{
typesToProcess.Enqueue(Nullable.GetUnderlyingType(type)!);
}
if (type.IsConstructedGenericType)
{
typesToProcess.Enqueue(type.GetGenericTypeDefinition());
}
if (!type.IsGenericTypeDefinition
&& !type.IsInterface)
{
if (type.BaseType != null)
{
typesToProcess.Enqueue(type.BaseType);
}
foreach (var @interface in GetDeclaredInterfaces(type))
{
typesToProcess.Enqueue(@interface);
}
}
}
return baseTypes;
}
public static IEnumerable<Type> GetTypesInHierarchy(this Type type)
{
var currentType = type;
while (currentType != null)
{
yield return currentType;
currentType = currentType.BaseType;
}
}
public static IEnumerable<Type> GetDeclaredInterfaces(this Type type)
{
var interfaces = type.GetInterfaces();
if (type.BaseType == typeof(object)
|| type.BaseType == null)
{
return interfaces;
}
return interfaces.Except(type.BaseType.GetInterfaces());
}
public static ConstructorInfo GetDeclaredConstructor(this Type type, Type[] types)
{
types ??= Array.Empty<Type>();
return type.GetTypeInfo().DeclaredConstructors
.SingleOrDefault(
c => !c.IsStatic
&& c.GetParameters().Select(p => p.ParameterType).SequenceEqual(types))!;
}
public static IEnumerable<PropertyInfo> GetPropertiesInHierarchy(this Type type, string name)
{
var currentType = type;
do
{
var typeInfo = currentType.GetTypeInfo();
foreach (var propertyInfo in typeInfo.DeclaredProperties)
{
if (propertyInfo.Name.Equals(name, StringComparison.Ordinal)
&& !(propertyInfo.GetMethod ?? propertyInfo.SetMethod)!.IsStatic)
{
yield return propertyInfo;
}
}
currentType = typeInfo.BaseType;
}
while (currentType != null);
}
// Looking up the members through the whole hierarchy allows to find inherited private members.
public static IEnumerable<MemberInfo> GetMembersInHierarchy(this Type type)
{
var currentType = type;
do
{
// Do the whole hierarchy for properties first since looking for fields is slower.
foreach (var propertyInfo in currentType.GetRuntimeProperties().Where(pi => !(pi.GetMethod ?? pi.SetMethod)!.IsStatic))
{
yield return propertyInfo;
}
foreach (var fieldInfo in currentType.GetRuntimeFields().Where(f => !f.IsStatic))
{
yield return fieldInfo;
}
currentType = currentType.BaseType;
}
while (currentType != null);
}
public static IEnumerable<MemberInfo> GetMembersInHierarchy(this Type type, string name)
=> type.GetMembersInHierarchy().Where(m => m.Name == name);
private static readonly Dictionary<Type, object> CommonTypeDictionary = new()
{
#pragma warning disable IDE0034 // Simplify 'default' expression - default causes default(object)
{ typeof(int), default(int) },
{ typeof(Guid), default(Guid) },
{ typeof(DateOnly), default(DateOnly) },
{ typeof(DateTime), default(DateTime) },
{ typeof(DateTimeOffset), default(DateTimeOffset) },
{ typeof(TimeOnly), default(TimeOnly) },
{ typeof(long), default(long) },
{ typeof(bool), default(bool) },
{ typeof(double), default(double) },
{ typeof(short), default(short) },
{ typeof(float), default(float) },
{ typeof(byte), default(byte) },
{ typeof(char), default(char) },
{ typeof(uint), default(uint) },
{ typeof(ushort), default(ushort) },
{ typeof(ulong), default(ulong) },
{ typeof(sbyte), default(sbyte) }
#pragma warning restore IDE0034 // Simplify 'default' expression
};
public static object GetDefaultValue(this Type type)
{
if (!type.IsValueType)
{
return null;
}
// A bit of perf code to avoid calling Activator.CreateInstance for common types and
// to avoid boxing on every call. This is about 50% faster than just calling CreateInstance
// for all value types.
return CommonTypeDictionary.TryGetValue(type, out var value)
? value
: Activator.CreateInstance(type);
}
public static IEnumerable<Reflection.TypeInfo> GetConstructibleTypes(this Assembly assembly)
=> assembly.GetLoadableDefinedTypes().Where(
t => !t.IsAbstract
&& !t.IsGenericTypeDefinition);
public static IEnumerable<Reflection.TypeInfo> GetLoadableDefinedTypes(this Assembly assembly)
{
try
{
return assembly.DefinedTypes;
}
catch (ReflectionTypeLoadException ex)
{
return ex.Types.Where(t => t != null).Select(IntrospectionExtensions.GetTypeInfo!);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
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('>');
}
public static IEnumerable<string> GetNamespaces(this Type type)
{
if (BuiltInTypeNames.ContainsKey(type))
{
yield break;
}
yield return type.Namespace!;
if (type.IsGenericType)
{
foreach (var typeArgument in type.GenericTypeArguments)
{
foreach (var ns in typeArgument.GetNamespaces())
{
yield return ns;
}
}
}
}
public static ConstantExpression GetDefaultValueConstant(this Type type)
=> (ConstantExpression)GenerateDefaultValueConstantMethod
.MakeGenericMethod(type).Invoke(null, Array.Empty<object>())!;
private static readonly MethodInfo GenerateDefaultValueConstantMethod =
typeof(SharedTypeExtensions).GetTypeInfo().GetDeclaredMethod(nameof(GenerateDefaultValueConstant))!;
private static ConstantExpression GenerateDefaultValueConstant<TDefault>()
=> Expression.Constant(default(TDefault), typeof(TDefault));
}
}

View File

@@ -1,18 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq.Dynamic.Core;
using System.Linq.Dynamic.Core.CustomTypeProviders;
using System.Reflection;
namespace Radzen.Blazor
{
class DynamicLinqCustomTypeProvider : IDynamicLinkCustomTypeProvider
{
static readonly HashSet<Type> empty = [];
public HashSet<Type> GetCustomTypes() => empty;
public Dictionary<Type, List<MethodInfo>> GetExtensionMethods() => throw new NotSupportedException();
public Type ResolveType(string typeName) => throw new NotSupportedException();
public Type ResolveTypeBySimpleName(string simpleTypeName) => throw new NotSupportedException();
public static ParsingConfig ParsingConfig = new() { CustomTypeProvider = new DynamicLinqCustomTypeProvider() };
}
}

View File

@@ -77,6 +77,12 @@ namespace Radzen.Blazor
/// <param name="appointments">The appointments for this range.</param>
Task SelectMonth(DateTime monthStart, IEnumerable<AppointmentData> appointments);
/// <summary>
/// Selects the specified day.
/// </summary>
/// <param name="day">The selected day.</param>
/// <param name="appointments">The appointments for this range.</param>
Task SelectDay(DateTime day, IEnumerable<AppointmentData> appointments);
/// <summary>
/// Selects the specified more link.
/// </summary>
/// <param name="start">The start.</param>

View File

@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2018-2024 Radzen Ltd
Copyright (c) 2018-2025 Radzen Ltd
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -53,7 +53,7 @@ namespace Radzen.Blazor
protected virtual double CalculateTickCount(int distance)
{
return Math.Ceiling(Math.Abs(Output.End - Output.Start) / distance);
return Math.Max(1, Math.Ceiling(Math.Abs(Output.End - Output.Start) / distance));
}
public override (double Start, double End, double Step) Ticks(int distance)

View File

@@ -3,5 +3,4 @@
<assembly fullname="System.Core">
<type fullname="System.Linq.Queryable" preserve="all" />
</assembly>
<assembly fullname="System.Linq.Dynamic.Core" />
</linker>

View File

@@ -8,7 +8,7 @@ using System.Text;
namespace Radzen
{
/// <summary>
/// Class NotificationService. Contains various methods with options to open notifications.
/// Class NotificationService. Contains various methods with options to open notifications.
/// Should be added as scoped service in the application services and RadzenNotification should be added in application main layout.
/// </summary>
/// <example>
@@ -50,6 +50,20 @@ namespace Radzen
/// <param name="detail">The detail.</param>
/// <param name="duration">The duration.</param>
/// <param name="click">The click event.</param>
public void Notify(NotificationSeverity severity, string summary,
string detail, TimeSpan duration, Action<NotificationMessage> click = null)
{
Notify(severity, summary, detail, duration.TotalMilliseconds, click);
}
/// <summary>
/// Notifies the specified severity.
/// </summary>
/// <param name="severity">The severity.</param>
/// <param name="summary">The summary.</param>
/// <param name="detail">The detail.</param>
/// <param name="duration">The duration, default of 3 seconds.</param>
/// <param name="click">The click event.</param>
/// <param name="closeOnClick">If true, then the notification will be closed when clicked on.</param>
/// <param name="payload">Used to store a custom payload that can be retreived later in the click event handler.</param>
/// <param name="close">Action to be executed on close.</param>
@@ -108,7 +122,7 @@ namespace Radzen
/// Gets or sets the click event.
/// </summary>
/// <value>This event handler is called when the notification is clicked on.</value>
public Action<NotificationMessage> Click { get; set; }
public Action<NotificationMessage> Click { get; set; }
/// <summary>
/// Get or set the event for when the notification is closed
/// </summary>
@@ -146,12 +160,12 @@ namespace Radzen
public bool Equals(NotificationMessage other)
{
if(other == null) return false;
if(object.ReferenceEquals(this, other)) return true;
return this.Severity == other.Severity
&& this.Summary == other.Summary
&& this.Detail == other.Detail
return this.Severity == other.Severity
&& this.Summary == other.Summary
&& this.Detail == other.Detail
&& this.Duration == other.Duration
&& this.Style == other.Style
&& this.Click == other.Click

View File

@@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using Radzen.Blazor;
@@ -354,18 +353,18 @@ namespace Radzen
/// Called when [parameters set asynchronous].
/// </summary>
/// <returns>Task.</returns>
protected override Task OnParametersSetAsync()
protected override async Task OnParametersSetAsync()
{
if (Visible && !LoadData.HasDelegate)
{
InvokeAsync(Reload);
await InvokeAsync(Reload);
}
else
{
CalculatePager();
}
return base.OnParametersSetAsync();
await base.OnParametersSetAsync();
}
/// <summary>

File diff suppressed because it is too large Load Diff

View File

@@ -10,7 +10,7 @@
<IsPackable>true</IsPackable>
<PackageId>Radzen.Blazor</PackageId>
<Product>Radzen.Blazor</Product>
<Version>5.7.2</Version>
<Version>6.0.3</Version>
<Copyright>Radzen Ltd.</Copyright>
<Authors>Radzen Ltd.</Authors>
<Description>Radzen Blazor is a set of 90+ free native Blazor UI controls packed with DataGrid, Scheduler, Charts and robust theming including Material design and Fluent UI.</Description>
@@ -24,7 +24,8 @@
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DartSassBuilder" Version="1.0.0" />
<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" />
<PackageReference Include="Microsoft.AspNetCore.Components" Condition="'$(TargetFramework)' == 'net7.0'" Version="7.0.14" />
@@ -33,7 +34,6 @@
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Condition="'$(TargetFramework)' == 'net8.0'" Version="8.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components" Condition="'$(TargetFramework)' == 'net9.0'" Version="9.*-*" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Condition="'$(TargetFramework)' == 'net9.0'" Version="9.*-*" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.3.7" />
</ItemGroup>
<ItemGroup>

View File

@@ -180,7 +180,7 @@ namespace Radzen.Blazor
await Expand.InvokeAsync(itemIndex);
}
item.SetSelected(value ?? !selected);
await item.SetSelected(value ?? !selected);
if (!Multiple)
{
@@ -198,7 +198,7 @@ namespace Radzen.Blazor
{
if (i.GetSelected())
{
i.SetSelected(false);
await i.SetSelected(false);
await Collapse.InvokeAsync(items.IndexOf(i));
}
}

View File

@@ -34,7 +34,24 @@ namespace Radzen.Blazor
/// </summary>
/// <value><c>true</c> if selected; otherwise, <c>false</c>.</value>
[Parameter]
public bool Selected { get; set; }
public bool Selected
{
get
{
return selected != null ? selected.Value : false;
}
set
{
selected = value;
}
}
/// <summary>
/// Gets or sets the value changed.
/// </summary>
/// <value>The value changed.</value>
[Parameter]
public EventCallback<bool> SelectedChanged { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this <see cref="RadzenAccordionItem"/> is disabled.
@@ -139,9 +156,11 @@ namespace Radzen.Blazor
return selected ?? Selected;
}
internal void SetSelected(bool? value)
internal async Task SetSelected(bool? value)
{
selected = value;
await SelectedChanged.InvokeAsync(Selected);
}
/// <summary>
@@ -151,12 +170,19 @@ namespace Radzen.Blazor
/// <returns>A Task representing the asynchronous operation.</returns>
public override async Task SetParametersAsync(ParameterView parameters)
{
bool shouldRefresh = false;
if (parameters.DidParameterChange(nameof(Selected), Selected))
{
Accordion?.SelectItem(this, parameters.GetValueOrDefault<bool>(nameof(Selected)));
selected = parameters.GetValueOrDefault<bool>(nameof(Selected));
shouldRefresh = true;
}
await base.SetParametersAsync(parameters);
if (shouldRefresh)
{
Accordion.Refresh();
}
}
/// <summary>

View File

@@ -27,7 +27,7 @@
}
<div id="@PopupID" class="rz-autocomplete-panel" style="@PopupStyle">
<ul @ref="@list" class="rz-autocomplete-items rz-autocomplete-list" role="listbox">
@if (!string.IsNullOrEmpty(searchText) || !string.IsNullOrEmpty(customSearchText))
@if (OpenOnFocus || (!string.IsNullOrEmpty(searchText) || !string.IsNullOrEmpty(customSearchText)))
{
@foreach (var item in LoadData.HasDelegate ? Data != null ? Data : Enumerable.Empty<object>() : (View != null ? View : Enumerable.Empty<object>()))
{

View File

@@ -1,7 +1,6 @@
using Radzen;
using Radzen.Blazor.Rendering;
using System.Collections;
using System.Linq.Dynamic.Core;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.JSInterop;
using Microsoft.AspNetCore.Components;
@@ -23,6 +22,35 @@ namespace Radzen.Blazor
/// </example>
public partial class RadzenAutoComplete : DataBoundFormComponent<string>
{
object selectedItem = null;
/// <summary>
/// Gets or sets the selected item.
/// </summary>
/// <value>The selected item.</value>
[Parameter]
public object SelectedItem
{
get
{
return selectedItem;
}
set
{
if (selectedItem != value)
{
selectedItem = object.Equals(value, "null") ? null : value;
}
}
}
/// <summary>
/// Gets or sets the selected item changed.
/// </summary>
/// <value>The selected item changed.</value>
[Parameter]
public EventCallback<object> SelectedItemChanged { get; set; }
/// <summary>
/// Specifies additional custom attributes that will be rendered by the input.
/// </summary>
@@ -153,8 +181,8 @@ namespace Radzen.Blazor
var value = await JSRuntime.InvokeAsync<string>("Radzen.getInputValue", search);
value = $"{value}";
if (value.Length < MinLength)
if (value.Length < MinLength && !OpenOnFocus)
{
await JSRuntime.InvokeVoidAsync("Radzen.closePopup", PopupID);
return;
@@ -195,7 +223,7 @@ namespace Radzen.Blazor
{
get
{
return Data != null && !string.IsNullOrEmpty(searchText) ? Data.AsQueryable() : null;
return Data != null && (OpenOnFocus || !string.IsNullOrEmpty(searchText)) ? Data.AsQueryable() : null;
}
}
@@ -209,12 +237,7 @@ namespace Radzen.Blazor
{
if (Query != null)
{
string filterCaseSensitivityOperator = FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive ? ".ToLower()" : "";
string textProperty = string.IsNullOrEmpty(TextProperty) ? string.Empty : $".{TextProperty}";
return Query.Where(DynamicLinqCustomTypeProvider.ParsingConfig, $"o=>o{textProperty}{filterCaseSensitivityOperator}.{Enum.GetName(typeof(StringFilterOperator), FilterOperator)}(@0)",
FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive ? searchText.ToLower() : searchText);
return Query.Where(TextProperty, searchText, FilterOperator, FilterCaseSensitivity);
}
return null;
@@ -232,6 +255,8 @@ namespace Radzen.Blazor
await ValueChanged.InvokeAsync($"{Value}");
if (FieldIdentifier.FieldName != null) { EditContext?.NotifyFieldChanged(FieldIdentifier); }
await Change.InvokeAsync(Value);
await SelectedItemChanged.InvokeAsync(null);
}
async System.Threading.Tasks.Task SelectItem(object item)
@@ -249,6 +274,8 @@ namespace Radzen.Blazor
if (FieldIdentifier.FieldName != null) { EditContext?.NotifyFieldChanged(FieldIdentifier); }
await Change.InvokeAsync(Value);
await SelectedItemChanged.InvokeAsync(item);
StateHasChanged();
}
@@ -307,8 +334,22 @@ namespace Radzen.Blazor
shouldClose = !visible;
}
if (parameters.DidParameterChange(nameof(SelectedItem), SelectedItem))
{
var item = parameters.GetValueOrDefault<object>(nameof(SelectedItem));
if (item != null)
{
await SelectItem(item);
}
}
await base.SetParametersAsync(parameters);
if (parameters.DidParameterChange(nameof(Value), Value))
{
Value = parameters.GetValueOrDefault<object>(nameof(Value));
}
if (shouldClose && !firstRender)
{
await JSRuntime.InvokeVoidAsync("Radzen.destroyPopup", PopupID);

View File

@@ -276,6 +276,13 @@ namespace Radzen.Blazor
[Parameter]
public string IsNotEmptyText { get; set; } = "Is not empty";
/// <summary>
/// Gets or sets the custom filter operator text.
/// </summary>
/// <value>The custom filter operator text.</value>
[Parameter]
public string CustomText { get; set; } = "Custom";
/// <summary>
/// Gets or sets a value indicating whether the columns can be filtered.
/// </summary>

View File

@@ -119,6 +119,8 @@ else
}
Filter.Property = property.Property;
Filter.FilterProperty = property.FilterProperty;
Filter.Type = property.FilterPropertyType;
if (Filter.FilterOperator == null)
{

View File

@@ -3,10 +3,7 @@ using Microsoft.JSInterop;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.Common;
using System.Globalization;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;
namespace Radzen.Blazor
@@ -347,6 +344,8 @@ namespace Radzen.Blazor
{
switch (filterOperator)
{
case FilterOperator.Custom:
return DataFilter?.CustomText;
case FilterOperator.Contains:
return DataFilter?.ContainsText;
case FilterOperator.DoesNotContain:

View File

@@ -168,202 +168,9 @@
{
var filterMode = column.FilterMode ?? FilterMode;
<th colspan="@column.GetColSpan()" class="@($"rz-unselectable-text {getFrozenColumnClass(column, visibleColumns)} {column.HeaderCssClass}")" scope="col" style="@column.GetStyle(true, true)">
@if (AllowFiltering && column.Filterable && column.Columns == null && (!string.IsNullOrEmpty(column.GetFilterProperty()) || column.FilterTemplate != null))
@if (allColumns.All(c => c.Parent == null) && AllowFiltering && column.Filterable && column.Columns == null && (!string.IsNullOrEmpty(column.GetFilterProperty()) || column.FilterTemplate != null))
{
<div class="rz-cell-filter">
<div class="rz-cell-filter-content">
@if (column.FilterTemplate != null)
{
@column.FilterTemplate(column)
}
else
{
<span class="rz-cell-filter-label" style="height:35px; width:100%;" onclick="event.preventDefault()">
@if (PropertyAccess.IsDate(column.FilterPropertyType))
{
if (filterMode == FilterMode.Simple)
{
<button aria-label="@FilterToggleAriaLabel" class="rz-button rz-button-md rz-button-icon-only rz-variant-flat rz-base rz-shade-default @(column.HasActiveFilter() ? "rz-grid-filter-active" : "")" onclick="@($"Radzen.togglePopup(this.parentNode, '{PopupID}{column.GetFilterProperty()}')")">
<i class="notranslate rzi">date_range</i>
</button>
var filterValue = column.GetFilterValue();
var filterOperator = column.GetFilterOperator();
@if (filterValue != null && filters.Any(d => d.Property == column.GetFilterProperty()))
{
<span class="rz-current-filter">@string.Format("{0:" + getFilterDateFormat(column) + "}", filterValue)</span>
<i @onclick="@((args) => ClearFilter(column))" class="notranslate rzi rz-cell-filter-clear">close</i>
}
else if ((filterOperator == FilterOperator.IsNull || filterOperator == FilterOperator.IsNotNull) && filters.Any(d => d.Property == column.GetFilterProperty()))
{
<span class="rz-current-filter">@column.GetFilterOperatorText(filterOperator)</span>
<i @onclick="@((args) => ClearFilter(column))" class="notranslate rzi rz-cell-filter-clear">close</i>
}
<div id="@($"{PopupID}{column.GetFilterProperty()}")" class="rz-overlaypanel rz-grid-date-filter"
style="display:none;" tabindex="0">
<div class="rz-overlaypanel-content">
<div class="rz-date-filter">
<div class="rz-listbox rz-inputtext ">
<div class="rz-listbox-list-wrapper">
<ul class="rz-listbox-list">
@if (column.GetFilterOperators().Contains(FilterOperator.Equals))
{
<li class="@(DateFilterOperatorStyle(column, FilterOperator.Equals))" @onclick="@((args) => ApplyDateFilterByFilterOperator(column, FilterOperator.Equals))" style="display: block;">
<span>@EqualsText</span>
</li>
}
@if (column.GetFilterOperators().Contains(FilterOperator.NotEquals))
{
<li class="@(DateFilterOperatorStyle(column, FilterOperator.NotEquals))" @onclick="@((args) => ApplyDateFilterByFilterOperator(column, FilterOperator.NotEquals))" style="display: block;">
<span>@NotEqualsText</span>
</li>
}
@if (column.GetFilterOperators().Contains(FilterOperator.LessThan))
{
<li class="@(DateFilterOperatorStyle(column, FilterOperator.LessThan))" @onclick="@((args) => ApplyDateFilterByFilterOperator(column, FilterOperator.LessThan))" style="display: block;">
<span>@LessThanText</span>
</li>
}
@if (column.GetFilterOperators().Contains(FilterOperator.LessThanOrEquals))
{
<li class="@(DateFilterOperatorStyle(column, FilterOperator.LessThanOrEquals))" @onclick="@((args) => ApplyDateFilterByFilterOperator(column, FilterOperator.LessThanOrEquals))" style="display: block;">
<span>@LessThanOrEqualsText</span>
</li>
}
@if (column.GetFilterOperators().Contains(FilterOperator.GreaterThan))
{
<li class="@(DateFilterOperatorStyle(column, FilterOperator.GreaterThan))" @onclick="@((args) => ApplyDateFilterByFilterOperator(column, FilterOperator.GreaterThan))" style="display: block;">
<span>@GreaterThanText</span>
</li>
}
@if (column.GetFilterOperators().Contains(FilterOperator.GreaterThanOrEquals))
{
<li class="@(DateFilterOperatorStyle(column, FilterOperator.GreaterThanOrEquals))" @onclick="@((args) => ApplyDateFilterByFilterOperator(column, FilterOperator.GreaterThanOrEquals))" style="display: block;">
<span>@GreaterThanOrEqualsText</span>
</li>
}
@if (column.GetFilterOperators().Contains(FilterOperator.IsEmpty))
{
<li class="@(DateFilterOperatorStyle(column, FilterOperator.IsEmpty))" @onclick="@((args) => ApplyDateFilterByFilterOperator(column, FilterOperator.IsEmpty))" style="display: block;">
<span>@IsEmptyText</span>
</li>
}
@if (column.GetFilterOperators().Contains(FilterOperator.IsNotEmpty))
{
<li class="@(DateFilterOperatorStyle(column, FilterOperator.IsNotEmpty))" @onclick="@((args) => ApplyDateFilterByFilterOperator(column, FilterOperator.IsNotEmpty))" style="display: block;">
<span>@IsNotEmptyText</span>
</li>
}
@if (column.GetFilterOperators().Contains(FilterOperator.IsNull))
{
<li class="@(DateFilterOperatorStyle(column, FilterOperator.IsNull))" @onclick="@((args) => ApplyDateFilterByFilterOperator(column, FilterOperator.IsNull))" style="display: block;">
<span>@IsNullText</span>
</li>
}
@if (column.GetFilterOperators().Contains(FilterOperator.IsNotNull))
{
<li class="@(DateFilterOperatorStyle(column, FilterOperator.IsNotNull))" @onclick="@((args) => ApplyDateFilterByFilterOperator(column, FilterOperator.IsNotNull))" style="display: block;">
<span>@IsNotNullText</span>
</li>
}
</ul>
</div>
</div>
<RadzenDatePicker TValue="@object" AllowInput=@(AllowFilterDateInput) InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", column.Title + FilterValueAriaLabel + column.GetFilterValue() }})"
ShowTime="@column.ShowTimeForDateTimeFilter()" ShowTimeOkButton="false" Inline="true" DateFormat="@getFilterDateFormat(column)"
Value="@column.GetFilterValue()" Change="@(args => { column.SetFilterValue(PropertyAccess.IsDateOnly(column.FilterPropertyType) ? PropertyAccess.DateOnlyFromDateTime(args.Value) : args.Value); SaveSettings(); })" />
</div>
<div class="rz-grid-filter-buttons">
<RadzenButton ButtonStyle="ButtonStyle.Base" class="rz-clear-filter" Click="@((args) => ClearFilter(column, true))" Text=@ClearFilterText title="@ClearFilterText" />
<RadzenButton ButtonStyle="ButtonStyle.Primary" class="rz-apply-filter" Click="@((args) => ApplyFilter(column, true))" Text=@ApplyFilterText title="@ApplyFilterText" />
</div>
</div>
</div>
}
else
{
<RadzenDataGridFilterMenu Grid="@this" Column="@column" />
@if (column.FilterValueTemplate != null)
{
@column.FilterValueTemplate(column)
}
else
{
<RadzenDatePicker Disabled=@(!column.CanSetFilterValue()) TValue="@object" Style="width:100%" AllowInput=@(AllowFilterDateInput) AllowClear="true" InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", column.Title + FilterValueAriaLabel + column.GetFilterValue() }})"
ShowTime="false" ShowTimeOkButton="false" DateFormat="@getFilterDateFormat(column)"
Value="@column.GetFilterValue()" Change="@(args => { if(!args.HasValue) { InvokeAsync(() => ClearFilter(column, true)); } else {column.SetFilterValue(PropertyAccess.IsDateOnly(column.FilterPropertyType) ? PropertyAccess.DateOnlyFromDateTime(args.Value) : args.Value); InvokeAsync(() => ApplyFilter(column, true));} })" />
}
}
}
else if (PropertyAccess.IsNullableEnum(column.FilterPropertyType) || PropertyAccess.IsEnum(column.FilterPropertyType))
{
@if (column.FilterValueTemplate != null)
{
@column.FilterValueTemplate(column)
}
else
{
<RadzenDropDown Style="width:100%" AllowClear="true" AllowFiltering="false" TValue="@object"
Value=@column.GetFilterValue() Multiple="false" Placeholder="@EnumFilterSelectText" TextProperty="Text" ValueProperty="Value"
Data=@((PropertyAccess.IsNullableEnum(column.FilterPropertyType) ? new object[]{ new { Value = Convert.ChangeType(-1, Enum.GetUnderlyingType(Nullable.GetUnderlyingType(column.FilterPropertyType) ?? column.FilterPropertyType)), Text = EnumNullFilterText}} : Enumerable.Empty<object>()).Concat(EnumExtensions.EnumAsKeyValuePair(Nullable.GetUnderlyingType(column.FilterPropertyType) ?? column.FilterPropertyType)))
Change="@(args => {column.SetFilterValue(args);column.SetFilterOperator(object.Equals(args, -1) ? FilterOperator.IsNull : FilterOperator.Equals);InvokeAsync(() => ApplyFilter(column, true));})" />
}
}
else if (PropertyAccess.IsNumeric(column.FilterPropertyType))
{
if (filterMode == FilterMode.SimpleWithMenu)
{
<RadzenDataGridFilterMenu Grid="@this" Column="@column" />
}
@if (column.FilterValueTemplate != null)
{
@column.FilterValueTemplate(column)
}
else
{
@(DrawNumericFilter(column))
}
}
else if (column.FilterPropertyType == typeof(bool) || column.FilterPropertyType == typeof(bool?))
{
@if (column.FilterValueTemplate != null)
{
@column.FilterValueTemplate(column)
}
else
{
<div style="@(column.TextAlign == TextAlign.Center ? "width:100%;text-align:center" : column.TextAlign == TextAlign.Right ? "width:100%;text-align:right" : "")">
<RadzenCheckBox TriState="true" TValue="@object" Value="@column.GetFilterValue()" Change="@((args) => OnFilter(new ChangeEventArgs() { Value = args }, column))" />
</div>
}
}
else
{
if (filterMode == FilterMode.SimpleWithMenu)
{
<RadzenDataGridFilterMenu Grid="@this" Column="@column" />
}
@if (column.FilterValueTemplate != null)
{
@column.FilterValueTemplate(column)
}
else
{
<input autocomplete="off" aria-label=@(column.Title + FilterValueAriaLabel + column.GetFilterValue()) disabled=@(!column.CanSetFilterValue()) id="@(getFilterInputId(column))" @onchange="@((args) => OnFilter(args, column))" @onkeydown="@((args) => OnFilterKeyPress(args, column))" value="@column.GetFilterValue()" type="text" placeholder="@column.GetFilterPlaceholder()" class="rz-textbox" style="width: 100%;" />
@if (column.GetFilterValue() != null && filters.Any(d => d.Property == column.GetFilterProperty()))
{
<i @onclick="@((args) => ClearFilter(column))" class="notranslate rzi rz-cell-filter-clear" style="position:absolute;inset-inline-end:10px;">close</i>
}
}
}
</span>
}
</div>
</div>
@RenderSimpleFilter(column, filterMode)
}
</th>
}
@@ -489,6 +296,212 @@
}
</CascadingValue>
@code {
internal RenderFragment RenderSimpleFilter(RadzenDataGridColumn<TItem> column, FilterMode filterMode)
{
return __builder =>
{
<text>
@if (AllowFiltering && column.Filterable && column.Columns == null && (!string.IsNullOrEmpty(column.GetFilterProperty()) || column.FilterTemplate != null))
{
<div class="rz-cell-filter">
<div class="rz-cell-filter-content">
@if (column.FilterTemplate != null)
{
@column.FilterTemplate(column)
}
else
{
<span class="rz-cell-filter-label" style="height:35px; width:100%;" onclick="event.preventDefault()">
@if (PropertyAccess.IsDate(column.FilterPropertyType))
{
if (filterMode == FilterMode.Simple)
{
<button aria-label="@FilterToggleAriaLabel" class="rz-button rz-button-md rz-button-icon-only rz-variant-flat rz-base rz-shade-default @(column.HasActiveFilter() ? "rz-grid-filter-active" : "")" onclick="@($"Radzen.togglePopup(this.parentNode, '{PopupID}{column.GetFilterProperty()}')")">
<i class="notranslate rzi">date_range</i>
</button>
var filterValue = column.GetFilterValue();
var filterOperator = column.GetFilterOperator();
@if (filterValue != null && filters.Any(d => d.Property == column.GetFilterProperty()))
{
<span class="rz-current-filter">@string.Format("{0:" + getFilterDateFormat(column) + "}", filterValue)</span>
<i @onclick="@((args) => ClearFilter(column))" class="notranslate rzi rz-cell-filter-clear">close</i>
}
else if ((filterOperator == FilterOperator.IsNull || filterOperator == FilterOperator.IsNotNull) && filters.Any(d => d.Property == column.GetFilterProperty()))
{
<span class="rz-current-filter">@column.GetFilterOperatorText(filterOperator)</span>
<i @onclick="@((args) => ClearFilter(column))" class="notranslate rzi rz-cell-filter-clear">close</i>
}
<div id="@($"{PopupID}{column.GetFilterProperty()}")" class="rz-overlaypanel rz-grid-date-filter"
style="display:none;" tabindex="0">
<div class="rz-overlaypanel-content">
<div class="rz-date-filter">
<div class="rz-listbox rz-inputtext ">
<div class="rz-listbox-list-wrapper">
<ul class="rz-listbox-list">
@if (column.GetFilterOperators().Contains(FilterOperator.Equals))
{
<li class="@(DateFilterOperatorStyle(column, FilterOperator.Equals))" @onclick="@((args) => ApplyDateFilterByFilterOperator(column, FilterOperator.Equals))" style="display: block;">
<span>@EqualsText</span>
</li>
}
@if (column.GetFilterOperators().Contains(FilterOperator.NotEquals))
{
<li class="@(DateFilterOperatorStyle(column, FilterOperator.NotEquals))" @onclick="@((args) => ApplyDateFilterByFilterOperator(column, FilterOperator.NotEquals))" style="display: block;">
<span>@NotEqualsText</span>
</li>
}
@if (column.GetFilterOperators().Contains(FilterOperator.LessThan))
{
<li class="@(DateFilterOperatorStyle(column, FilterOperator.LessThan))" @onclick="@((args) => ApplyDateFilterByFilterOperator(column, FilterOperator.LessThan))" style="display: block;">
<span>@LessThanText</span>
</li>
}
@if (column.GetFilterOperators().Contains(FilterOperator.LessThanOrEquals))
{
<li class="@(DateFilterOperatorStyle(column, FilterOperator.LessThanOrEquals))" @onclick="@((args) => ApplyDateFilterByFilterOperator(column, FilterOperator.LessThanOrEquals))" style="display: block;">
<span>@LessThanOrEqualsText</span>
</li>
}
@if (column.GetFilterOperators().Contains(FilterOperator.GreaterThan))
{
<li class="@(DateFilterOperatorStyle(column, FilterOperator.GreaterThan))" @onclick="@((args) => ApplyDateFilterByFilterOperator(column, FilterOperator.GreaterThan))" style="display: block;">
<span>@GreaterThanText</span>
</li>
}
@if (column.GetFilterOperators().Contains(FilterOperator.GreaterThanOrEquals))
{
<li class="@(DateFilterOperatorStyle(column, FilterOperator.GreaterThanOrEquals))" @onclick="@((args) => ApplyDateFilterByFilterOperator(column, FilterOperator.GreaterThanOrEquals))" style="display: block;">
<span>@GreaterThanOrEqualsText</span>
</li>
}
@if (column.GetFilterOperators().Contains(FilterOperator.IsEmpty))
{
<li class="@(DateFilterOperatorStyle(column, FilterOperator.IsEmpty))" @onclick="@((args) => ApplyDateFilterByFilterOperator(column, FilterOperator.IsEmpty))" style="display: block;">
<span>@IsEmptyText</span>
</li>
}
@if (column.GetFilterOperators().Contains(FilterOperator.IsNotEmpty))
{
<li class="@(DateFilterOperatorStyle(column, FilterOperator.IsNotEmpty))" @onclick="@((args) => ApplyDateFilterByFilterOperator(column, FilterOperator.IsNotEmpty))" style="display: block;">
<span>@IsNotEmptyText</span>
</li>
}
@if (column.GetFilterOperators().Contains(FilterOperator.IsNull))
{
<li class="@(DateFilterOperatorStyle(column, FilterOperator.IsNull))" @onclick="@((args) => ApplyDateFilterByFilterOperator(column, FilterOperator.IsNull))" style="display: block;">
<span>@IsNullText</span>
</li>
}
@if (column.GetFilterOperators().Contains(FilterOperator.IsNotNull))
{
<li class="@(DateFilterOperatorStyle(column, FilterOperator.IsNotNull))" @onclick="@((args) => ApplyDateFilterByFilterOperator(column, FilterOperator.IsNotNull))" style="display: block;">
<span>@IsNotNullText</span>
</li>
}
</ul>
</div>
</div>
<RadzenDatePicker TValue="@object" AllowInput=@(AllowFilterDateInput) InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", column.Title + FilterValueAriaLabel + column.GetFilterValue() }})"
ShowTime="@column.ShowTimeForDateTimeFilter()" ShowTimeOkButton="false" Inline="true" DateFormat="@getFilterDateFormat(column)"
Value="@column.GetFilterValue()" Change="@(args => { column.SetFilterValue(PropertyAccess.IsDateOnly(column.FilterPropertyType) ? PropertyAccess.DateOnlyFromDateTime(args.Value) : args.Value); SaveSettings(); })" />
</div>
<div class="rz-grid-filter-buttons">
<RadzenButton ButtonStyle="ButtonStyle.Base" class="rz-clear-filter" Click="@((args) => ClearFilter(column, true))" Text=@ClearFilterText title="@ClearFilterText" />
<RadzenButton ButtonStyle="ButtonStyle.Primary" class="rz-apply-filter" Click="@((args) => ApplyFilter(column, true))" Text=@ApplyFilterText title="@ApplyFilterText" />
</div>
</div>
</div>
}
else
{
<RadzenDataGridFilterMenu Grid="@this" Column="@column" />
@if (column.FilterValueTemplate != null)
{
@column.FilterValueTemplate(column)
}
else
{
<RadzenDatePicker Disabled=@(!column.CanSetFilterValue()) TValue="@object" Style="width:100%" AllowInput=@(AllowFilterDateInput) AllowClear="true" InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", column.Title + FilterValueAriaLabel + column.GetFilterValue() }})"
ShowTime="false" ShowTimeOkButton="false" DateFormat="@getFilterDateFormat(column)"
Value="@column.GetFilterValue()" Change="@(args => { if(!args.HasValue) { InvokeAsync(() => ClearFilter(column, true)); } else {column.SetFilterValue(PropertyAccess.IsDateOnly(column.FilterPropertyType) ? PropertyAccess.DateOnlyFromDateTime(args.Value) : args.Value); InvokeAsync(() => ApplyFilter(column, true));} })" />
}
}
}
else if (PropertyAccess.IsNullableEnum(column.FilterPropertyType) || PropertyAccess.IsEnum(column.FilterPropertyType))
{
@if (column.FilterValueTemplate != null)
{
@column.FilterValueTemplate(column)
}
else
{
<RadzenDropDown Style="width:100%" AllowClear="true" AllowFiltering="false" TValue="@object"
Value=@column.GetFilterValue() Multiple="false" Placeholder="@EnumFilterSelectText" TextProperty="Text" ValueProperty="Value"
Data=@((PropertyAccess.IsNullableEnum(column.FilterPropertyType) ? new object[]{ new { Value = Convert.ChangeType(-1, Enum.GetUnderlyingType(Nullable.GetUnderlyingType(column.FilterPropertyType) ?? column.FilterPropertyType)), Text = EnumNullFilterText}} : Enumerable.Empty<object>()).Concat(EnumExtensions.EnumAsKeyValuePair(Nullable.GetUnderlyingType(column.FilterPropertyType) ?? column.FilterPropertyType)))
Change="@(args => {column.SetFilterValue(args);column.SetFilterOperator(object.Equals(args, -1) ? FilterOperator.IsNull : FilterOperator.Equals);InvokeAsync(() => ApplyFilter(column, true));})" />
}
}
else if (PropertyAccess.IsNumeric(column.FilterPropertyType))
{
if (filterMode == FilterMode.SimpleWithMenu)
{
<RadzenDataGridFilterMenu Grid="@this" Column="@column" />
}
@if (column.FilterValueTemplate != null)
{
@column.FilterValueTemplate(column)
}
else
{
@(DrawNumericFilter(column))
}
}
else if (column.FilterPropertyType == typeof(bool) || column.FilterPropertyType == typeof(bool?))
{
@if (column.FilterValueTemplate != null)
{
@column.FilterValueTemplate(column)
}
else
{
<div style="@(column.TextAlign == TextAlign.Center ? "width:100%;text-align:center" : column.TextAlign == TextAlign.Right ? "width:100%;text-align:right" : "")">
<RadzenCheckBox TriState="true" TValue="@object" Value="@column.GetFilterValue()" Change="@((args) => OnFilter(new ChangeEventArgs() { Value = args }, column))" />
</div>
}
}
else
{
if (filterMode == FilterMode.SimpleWithMenu)
{
<RadzenDataGridFilterMenu Grid="@this" Column="@column" />
}
@if (column.FilterValueTemplate != null)
{
@column.FilterValueTemplate(column)
}
else
{
<input autocomplete="off" aria-label=@(column.Title + FilterValueAriaLabel + column.GetFilterValue()) disabled=@(!column.CanSetFilterValue()) id="@(getFilterInputId(column))" @onchange="@((args) => OnFilter(args, column))" @onkeydown="@((args) => OnFilterKeyPress(args, column))" value="@column.GetFilterValue()" type="text" placeholder="@column.GetFilterPlaceholder()" class="rz-textbox" style="width: 100%;" />
@if (column.GetFilterValue() != null && filters.Any(d => d.Property == column.GetFilterProperty()))
{
<i @onclick="@((args) => ClearFilter(column))" class="notranslate rzi rz-cell-filter-clear" style="position:absolute;inset-inline-end:10px;">close</i>
}
}
}
</span>
}
</div>
</div>
}
</text>
};
}
internal void SetAttribute(Dictionary<string, object> attributes, string attributeName, object attributeValue)
{
var separator = attributeName == "class" ? " " : ";";

View File

@@ -7,9 +7,7 @@ using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Data.Common;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Text.Json;
using System.Threading.Tasks;
@@ -157,8 +155,8 @@ namespace Radzen.Blazor
if (Groups.Any())
{
query = view.AsQueryable().OrderBy(DynamicLinqCustomTypeProvider.ParsingConfig, Groups.Any() ? string.Join(',', Groups.Select(g => $"{(typeof(TItem) == typeof(object) ? g.Property : "np(" + g.Property + ")")}")) : "it");
_groupedPagedView = await Task.FromResult(query.GroupByMany(DynamicLinqCustomTypeProvider.ParsingConfig, Groups.Any() ? Groups.Select(g => $"{(typeof(TItem) == typeof(object) ? g.Property : "np(" + g.Property + ")")}").ToArray() : new string[] { "it" }).ToList());
query = view.AsQueryable().OrderBy(Groups.Any() ? string.Join(',', Groups.Select(g => g.Property)) : null);
_groupedPagedView = await Task.FromResult(query.GroupByMany(Groups.Any() ? Groups.Select(g => g.Property).ToArray() : new string[] { "it" }).ToList());
totalItemsCount = await Task.FromResult(_groupedPagedView.Count());
}
@@ -365,9 +363,9 @@ namespace Radzen.Blazor
if (_groupedPagedView == null)
{
var orderBy = GetOrderBy();
var query = Groups.Count(g => g.SortOrder == null) == Groups.Count || !string.IsNullOrEmpty(orderBy) ? View : View.OrderBy(DynamicLinqCustomTypeProvider.ParsingConfig, string.Join(',', Groups.Select(g => $"{(typeof(TItem) == typeof(object) ? g.Property : "np(" + g.Property + ")")} {(g.SortOrder == null ? "" : g.SortOrder == SortOrder.Ascending ? " asc" : " desc")}")));
var query = Groups.Count(g => g.SortOrder == null) == Groups.Count || !string.IsNullOrEmpty(orderBy) ? View : View.OrderBy(string.Join(',', Groups.Select(g => $"{g.Property} {(g.SortOrder == null ? "" : g.SortOrder == SortOrder.Ascending ? " asc" : " desc")}")));
var v = (AllowPaging && !LoadData.HasDelegate ? query.Skip(skip).Take(PageSize) : query).ToList().AsQueryable();
_groupedPagedView = v.GroupByMany(DynamicLinqCustomTypeProvider.ParsingConfig, Groups.Select(g => $"{(typeof(TItem) == typeof(object) ? g.Property : "np(" + g.Property + ")")}").ToArray()).ToList();
_groupedPagedView = v.GroupByMany(Groups.Select(g => g.Property).ToArray()).ToList();
}
return _groupedPagedView;
}
@@ -1302,6 +1300,13 @@ namespace Radzen.Blazor
[Parameter]
public string IsNotEmptyText { get; set; } = "Is not empty";
/// <summary>
/// Gets or sets the custom filter operator text.
/// </summary>
/// <value>The custom filter operator text.</value>
[Parameter]
public string CustomText { get; set; } = "Custom";
internal class NumericFilterEventCallback
{
public static EventCallback<T> Create<T>(object receiver, Action<T> action)
@@ -1680,7 +1685,16 @@ namespace Radzen.Blazor
internal string GetOrderBy()
{
return string.Join(",", sorts.Select(d => allColumns.ToList().Where(c => c.GetSortProperty() == d.Property).FirstOrDefault()).Where(c => c != null).Select(c => c.GetSortOrderAsString(IsOData())));
return string.Join(",", sorts.Select(d => GetSortOrderAsString(d, IsOData())));
}
internal string GetSortOrderAsString(SortDescriptor d, bool isOData)
{
var property = d.Property;
if (string.IsNullOrEmpty(property))
return "";
var p = isOData ? property.Replace('.', '/') : PropertyAccess.GetProperty(property);
return $"{p} {(d.SortOrder == Radzen.SortOrder.Ascending ? "asc" : "desc")}";
}
/// <summary>
@@ -1713,12 +1727,12 @@ namespace Radzen.Blazor
var firstItem = view.FirstOrDefault();
if (firstItem != null)
{
view = view.Cast(firstItem.GetType()).AsQueryable().OrderBy(orderBy).Cast<TItem>();
view = QueryableExtension.Cast(view, firstItem.GetType()).AsQueryable().OrderBy(orderBy).Cast<TItem>();
}
}
else
{
view = view.OrderBy(orderBy);
view = view.OrderBy<TItem>(orderBy);
}
}
@@ -1744,7 +1758,7 @@ namespace Radzen.Blazor
var cd = childData[item].Data.AsQueryable();
if (!string.IsNullOrEmpty(orderBy))
{
cd = cd.OrderBy(orderBy);
cd = cd.OrderBy<TItem>(orderBy);
}
viewList.InsertRange(viewList.IndexOf(item) + 1, cd);
@@ -1799,7 +1813,8 @@ namespace Radzen.Blazor
var firstItem = view.FirstOrDefault();
if (firstItem != null)
{
view = view.Cast(firstItem.GetType()).AsQueryable().OrderBy(orderBy).Cast<TItem>();
view = QueryableExtension.Cast(view, firstItem.GetType());
view = view.OrderBy(orderBy).Cast<TItem>();
}
}
else
@@ -2193,18 +2208,18 @@ namespace Radzen.Blazor
/// Called when parameters set asynchronous.
/// </summary>
/// <returns>Task.</returns>
protected override Task OnParametersSetAsync()
protected override async Task OnParametersSetAsync()
{
if (Visible && !LoadData.HasDelegate && _view == null)
{
InvokeAsync(Reload);
await InvokeAsync(Reload);
}
else
{
CalculatePager();
}
return Task.CompletedTask;
await Task.CompletedTask;
}
internal Dictionary<RadzenDataGridGroupRow<TItem>, bool> collapsedGroupItems = new Dictionary<RadzenDataGridGroupRow<TItem>, bool>();
@@ -2407,6 +2422,17 @@ namespace Radzen.Blazor
}
}
/// <summary>
/// Force load of the DataGrid Settings.
/// </summary>
public async Task ReloadSettings()
{
if (settings != null)
{
await LoadSettingsInternal(settings);
}
}
/// <inheritdoc />
protected override async Task OnAfterRenderAsync(bool firstRender)
{
@@ -2951,23 +2977,40 @@ namespace Radzen.Blazor
/// </summary>
/// <param name="item">The item.</param>
public async System.Threading.Tasks.Task InsertRow(TItem item)
{
await InsertRowAtIndex(item);
}
/// <summary>
/// Inserts new row after specific row item.
/// </summary>
/// <param name="itemToInsert">The item.</param>
/// <param name="rowItem">Row item to insert after</param>
public async System.Threading.Tasks.Task InsertAfterRow(TItem itemToInsert, TItem rowItem)
{
var list = this.PagedView.ToList();
var index = list.IndexOf(rowItem);
await InsertRowAtIndex(itemToInsert, index + 1);
}
private async System.Threading.Tasks.Task InsertRowAtIndex(TItem item, int insertIndex = 0)
{
itemsToInsert.Add(item);
if(!IsVirtualizationAllowed())
if (!IsVirtualizationAllowed())
{
var list = this.PagedView.ToList();
list.Insert(0, item);
list.Insert(insertIndex, item);
this._view = list.AsQueryable();
this.Count++;
}
else
{
if(virtualize != null)
if (virtualize != null)
{
await virtualize.RefreshDataAsync();
}
if(groupVirtualize != null)
if (groupVirtualize != null)
{
await groupVirtualize.RefreshDataAsync();
}

View File

@@ -5,7 +5,6 @@ using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;
namespace Radzen.Blazor
@@ -738,15 +737,6 @@ namespace Radzen.Blazor
}
}
internal string GetSortOrderAsString(bool isOData)
{
var property = GetSortProperty();
if (string.IsNullOrEmpty(property))
return "";
var p = isOData ? property.Replace('.', '/') : PropertyAccess.GetProperty(property);
return $"{p} {(GetSortOrder() == Radzen.SortOrder.Ascending ? "asc" : "desc")}";
}
internal void SetSortOrder(SortOrder? order)
{
var descriptor = Grid.sorts.Where(d => d.Property == GetSortProperty()).FirstOrDefault();
@@ -1327,6 +1317,8 @@ namespace Radzen.Blazor
{
switch (filterOperator)
{
case FilterOperator.Custom:
return Grid?.CustomText;
case FilterOperator.Contains:
return Grid?.ContainsText;
case FilterOperator.DoesNotContain:

View File

@@ -1,5 +1,4 @@
@typeparam TItem
@using System.Linq.Dynamic.Core
@for (var i = 0; i < Grid.deepestChildColumnLevel + 1; i++)
{
<tr>
@@ -32,9 +31,9 @@
[Parameter]
public IList<RadzenDataGridColumn<TItem>> Columns { get; set; }
GroupResult _groupResult;
GroupResult _groupResult;
[Parameter]
public GroupResult GroupResult
public GroupResult GroupResult
{
get
{

View File

@@ -1,5 +1,4 @@
@typeparam TItem
@using System.Linq.Dynamic.Core
@using System.Globalization
@{
var rowArgs = Grid?.GroupRowAttributes(this);
@@ -86,7 +85,7 @@
GroupResult _groupResult;
[Parameter]
public GroupResult GroupResult
public GroupResult GroupResult
{
get
{

View File

@@ -2,148 +2,147 @@
@using Microsoft.JSInterop
@using Radzen.Blazor.Rendering
@using System.Globalization
@using System.Linq.Dynamic.Core
@using System.Collections
@using Radzen
@if (RowIndex == Column.GetLevel())
{
<th rowspan="@(Column.GetRowSpan())" colspan="@(Column.GetColSpan())" @attributes="@Attributes" class="@CssClass" scope="col" style="@GetStyle()" @onmouseup=@(args => Grid.EndColumnReorder(args, ColumnIndex))>
<div @onclick='@((args) => Grid.OnSort(args, Column))' @onkeydown="OnSortKeyPressed">
@if ((Grid.AllowColumnReorder && Column.Reorderable || Grid.AllowGrouping && Column.Groupable))
{
<span id="@(Grid.getColumnUniqueId(ColumnIndex) + "-drag")" class="rz-column-drag"
@onclick:preventDefault="true" @onclick:stopPropagation="true"
@onmousedown:preventDefault="true"
@onmousedown=@(args => Grid.StartColumnReorder(args, ColumnIndex, Column.UniqueID ?? Column.Property))></span>
}
<span class="rz-column-title" title="@(Grid.ShowColumnTitleAsTooltip ? @Column.GetTitle() : Column.HeaderTooltip)">
@if (Column.HeaderTemplate != null)
<th rowspan="@(Column.GetRowSpan())" colspan="@(Column.GetColSpan())" @attributes="@Attributes" class="@CssClass" scope="col" style="@GetStyle()" @onmouseup=@(args => Grid.EndColumnReorder(args, ColumnIndex))>
<div @onclick='@((args) => Grid.OnSort(args, Column))' @onkeydown="OnSortKeyPressed">
@if ((Grid.AllowColumnReorder && Column.Reorderable || Grid.AllowGrouping && Column.Groupable))
{
<span class="rz-column-title-content" @onkeydown:stopPropagation>@Column.HeaderTemplate</span>
}
else
{
<span class="rz-column-title-content">@Column.GetTitle()</span>
<span id="@(Grid.getColumnUniqueId(ColumnIndex) + "-drag")" class="rz-column-drag"
@onclick:preventDefault="true" @onclick:stopPropagation="true"
@onmousedown:preventDefault="true"
@onmousedown=@(args => Grid.StartColumnReorder(args, ColumnIndex, Column.UniqueID ?? Column.Property))></span>
}
<span class="rz-column-title" title="@(Grid.ShowColumnTitleAsTooltip ? @Column.GetTitle() : Column.HeaderTooltip)">
@if (Column.HeaderTemplate != null)
{
<span class="rz-column-title-content" @onkeydown:stopPropagation>@Column.HeaderTemplate</span>
}
else
{
<span class="rz-column-title-content">@Column.GetTitle()</span>
}
@if (Grid.AllowSorting && Column.Sortable)
{
@if (Column.GetSortOrder() == SortOrder.Ascending)
{
<span class="notranslate rz-sortable-column-icon rzi-grid-sort rzi-sort rzi-sort-asc"></span>
@if(Grid.ShowMultiColumnSortingIndex)
{
<RadzenBadge BadgeStyle="BadgeStyle.Info" Shade="Shade.Lighter" IsPill=true Text="@Column.getSortIndexAsString()" />
}
}
else if (Column.GetSortOrder() == SortOrder.Descending)
{
<span class="notranslate rz-sortable-column-icon rzi-grid-sort rzi-sort rzi-sort-desc"></span>
@if(Grid.ShowMultiColumnSortingIndex)
@if (Grid.AllowSorting && Column.Sortable)
{
@if (Column.GetSortOrder() == SortOrder.Ascending)
{
<span class="notranslate rz-sortable-column-icon rzi-grid-sort rzi-sort rzi-sort-asc"></span>
@if(Grid.ShowMultiColumnSortingIndex)
{
<RadzenBadge BadgeStyle="BadgeStyle.Info" Shade="Shade.Lighter" IsPill=true Text="@Column.getSortIndexAsString()" />
}
}
else if (Column.GetSortOrder() == SortOrder.Descending)
{
<span class="notranslate rz-sortable-column-icon rzi-grid-sort rzi-sort rzi-sort-desc"></span>
@if(Grid.ShowMultiColumnSortingIndex)
{
<RadzenBadge BadgeStyle="BadgeStyle.Info" Shade="Shade.Lighter" IsPill=true Text="@Column.getSortIndexAsString()" />
}
}
else
{
<span class="notranslate rz-sortable-column-icon rzi-grid-sort rzi-sort"></span>
}
}
}
else
</span>
@if (Grid.AllowColumnResize && Column.Resizable && Column.Parent == null)
{
<span class="notranslate rz-sortable-column-icon rzi-grid-sort rzi-sort"></span>
<div id="@(Grid.getColumnUniqueId(ColumnIndex) + "-resizer")" style="cursor:col-resize;float:right;"
@onclick:preventDefault="true" @onclick:stopPropagation="true" class="rz-column-resizer"
@onmousedown:preventDefault="true"
@onmousedown=@StartColumnResize @onmouseup=@StopColumnResize>&nbsp;</div>
}
}
</span>
@if (Grid.AllowColumnResize && Column.Resizable && Column.Parent == null)
{
<div id="@(Grid.getColumnUniqueId(ColumnIndex) + "-resizer")" style="cursor:col-resize;float:right;"
@onclick:preventDefault="true" @onclick:stopPropagation="true" class="rz-column-resizer"
@onmousedown:preventDefault="true"
@onmousedown=@StartColumnResize @onmouseup=@StopColumnResize>&nbsp;</div>
}
@{var filterMode = Column.FilterMode ?? Grid.FilterMode;}
@if (Grid.AllowFiltering && Column.Filterable && (filterMode == FilterMode.Advanced || filterMode == FilterMode.CheckBoxList))
{
@{var filterMode = Column.FilterMode ?? Grid.FilterMode;}
@if (Grid.AllowFiltering && Column.Filterable && (filterMode == FilterMode.Advanced || filterMode == FilterMode.CheckBoxList))
{
<i @ref=@filterButton @onclick:stopPropagation="true" @onmousedown=@ToggleFilter
class="@getFilterIconCss(Column)" onclick=@getFilterOpen() @onclick:preventDefault="true">
@Grid.FilterIcon
class="@getFilterIconCss(Column)" onclick=@getFilterOpen() @onclick:preventDefault="true">
@Grid.FilterIcon
</i>
<Popup Lazy=@(Grid.FilterPopupRenderMode == PopupRenderMode.OnDemand) @ref=popup id="@($"{getColumnPopupID()}")" class="rz-overlaypanel"
style="display:none;min-width:250px;" @onkeydown="OnFilterPopupKeyPressed">
<div class="rz-overlaypanel-content">
@if (Column.FilterTemplate != null)
{
@Column.FilterTemplate(Column)
}
else
{
<form id="@($"{getColumnPopupID()}-form")" @onsubmit="@(args => ApplyFilter())" class="rz-grid-filter">
style="display:none;min-width:250px;" @onkeydown="OnFilterPopupKeyPressed">
<div class="rz-overlaypanel-content">
@if (Column.FilterTemplate != null)
{
@Column.FilterTemplate(Column)
}
else
{
<form id="@($"{getColumnPopupID()}-form")" @onsubmit="@(args => ApplyFilter())" class="rz-grid-filter">
@if (filterMode == FilterMode.Advanced)
{
<span class="rz-grid-filter-label">@Grid.FilterText</span>
<RadzenDropDown InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", Grid.FilterOperatorAriaLabel + Column.GetFilterOperatorText(Column.GetSecondFilterOperator()) }})"
<span class="rz-grid-filter-label">@Grid.FilterText</span>
<RadzenDropDown InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", Grid.FilterOperatorAriaLabel + Column.GetFilterOperatorText(Column.GetSecondFilterOperator()) }})"
@onclick:preventDefault="true" Data="@(Column.GetFilterOperators().Select(t => new { Value = Column.GetFilterOperatorText(t), Key = t }))" TextProperty="Value" ValueProperty="Key" TValue="FilterOperator" Value="@Column.GetFilterOperator()" Change="@(args => Column.SetFilterOperator((FilterOperator)args))" />
@if (Column.FilterValueTemplate != null)
{
@Column.FilterValueTemplate(Column)
}
else if (PropertyAccess.IsNullableEnum(Column.FilterPropertyType) || PropertyAccess.IsEnum(Column.FilterPropertyType))
{
<RadzenDropDown Disabled="@(!Column.CanSetFilterValue())" AllowClear="false" AllowFiltering="false" TValue="@object"
Value=@Column.GetFilterValue() Multiple="false" Placeholder="@Grid.EnumFilterSelectText" TextProperty="Text" ValueProperty="Value" Data=@EnumExtensions.EnumAsKeyValuePair(Nullable.GetUnderlyingType(Column.FilterPropertyType) ?? Column.FilterPropertyType, Grid.EnumFilterTranslationFunc)
Change="@(args => Column.SetFilterValue(args))"
InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", Column.Title + Grid.FilterValueAriaLabel + Column.GetFilterValue() }})" />
}
else if (PropertyAccess.IsNumeric(Column.FilterPropertyType))
{
@(Grid.DrawNumericFilter(Column, false))
}
else if (PropertyAccess.IsDate(Column.FilterPropertyType))
{
<RadzenDatePicker Disabled="@(!Column.CanSetFilterValue())" TValue="@object" ShowTime="@Column.ShowTimeForDateTimeFilter()" ShowTimeOkButton="true" DateFormat="@Grid.getFilterDateFormat(Column)"
Value="@Column.GetFilterValue()" Change="@(args => Column.SetFilterValue(PropertyAccess.IsDateOnly(Column.FilterPropertyType) ? PropertyAccess.DateOnlyFromDateTime(args.Value) : args.Value))" AllowInput=@(Grid.AllowFilterDateInput)
InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", Column.Title + Grid.FilterValueAriaLabel + Column.GetFilterValue() }})" />
@if (Column.FilterValueTemplate != null)
{
@Column.FilterValueTemplate(Column)
}
else if (PropertyAccess.IsNullableEnum(Column.FilterPropertyType) || PropertyAccess.IsEnum(Column.FilterPropertyType))
{
<RadzenDropDown Disabled="@(!Column.CanSetFilterValue())" AllowClear="false" AllowFiltering="false" TValue="@object"
Value=@Column.GetFilterValue() Multiple="false" Placeholder="@Grid.EnumFilterSelectText" TextProperty="Text" ValueProperty="Value" Data=@EnumExtensions.EnumAsKeyValuePair(Nullable.GetUnderlyingType(Column.FilterPropertyType) ?? Column.FilterPropertyType, Grid.EnumFilterTranslationFunc)
Change="@(args => Column.SetFilterValue(args))"
InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", Column.Title + Grid.FilterValueAriaLabel + Column.GetFilterValue() }})" />
}
else if (PropertyAccess.IsNumeric(Column.FilterPropertyType))
{
@(Grid.DrawNumericFilter(Column, false))
}
else if (PropertyAccess.IsDate(Column.FilterPropertyType))
{
<RadzenDatePicker Disabled="@(!Column.CanSetFilterValue())" TValue="@object" ShowTime="@Column.ShowTimeForDateTimeFilter()" ShowTimeOkButton="true" DateFormat="@Grid.getFilterDateFormat(Column)"
Value="@Column.GetFilterValue()" Change="@(args => Column.SetFilterValue(PropertyAccess.IsDateOnly(Column.FilterPropertyType) ? PropertyAccess.DateOnlyFromDateTime(args.Value) : args.Value))" AllowInput=@(Grid.AllowFilterDateInput)
InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", Column.Title + Grid.FilterValueAriaLabel + Column.GetFilterValue() }})" />
}
else if (Column.FilterPropertyType == typeof(bool) || Column.FilterPropertyType == typeof(bool?))
{
<RadzenCheckBox Disabled="@(!Column.CanSetFilterValue())" InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", Column.Title + Grid.FilterValueAriaLabel + Column.GetFilterValue() }})" TriState="true" TValue="@object" Value="@Column.GetFilterValue()" Change="@(args => { Column.SetFilterValue(null); Column.SetFilterValue(args); Grid.SaveSettings(); })" />
}
else
{
<RadzenTextBox Disabled="@(!Column.CanSetFilterValue())" id="@($"{getColumnPopupID()}-sf")" aria-label=@(Column.Title + Grid.FilterValueAriaLabel + Column.GetFilterValue()) Value="@($"{Column.GetFilterValue()}")" Change="@(args => Column.SetFilterValue(args))" />
}
}
else if (Column.FilterPropertyType == typeof(bool) || Column.FilterPropertyType == typeof(bool?))
{
<RadzenCheckBox Disabled="@(!Column.CanSetFilterValue())" InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", Column.Title + Grid.FilterValueAriaLabel + Column.GetFilterValue() }})" TriState="true" TValue="@object" Value="@Column.GetFilterValue()" Change="@(args => { Column.SetFilterValue(null); Column.SetFilterValue(args); Grid.SaveSettings(); })" />
}
else
{
<RadzenTextBox Disabled="@(!Column.CanSetFilterValue())" id="@($"{getColumnPopupID()}-sf")" aria-label=@(Column.Title + Grid.FilterValueAriaLabel + Column.GetFilterValue()) Value="@($"{Column.GetFilterValue()}")" Change="@(args => Column.SetFilterValue(args))" />
}
<RadzenDropDown @onclick:preventDefault="true" TextProperty="Text" ValueProperty="Value" Style="width: 90px" InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", Column.Title + Grid.LogicalOperatorAriaLabel + (Column.LogicalFilterOperator == LogicalFilterOperator.And ? Grid.AndOperatorText : Grid.OrOperatorText) }})"
Data="@(Enum.GetValues(typeof(LogicalFilterOperator)).Cast<LogicalFilterOperator>().Select(t => new { Text = t == LogicalFilterOperator.And ? Grid.AndOperatorText : Grid.OrOperatorText, Value = t }))" TValue="LogicalFilterOperator" Value="@Column.LogicalFilterOperator" Change="@(args => Column.SetLogicalFilterOperator((LogicalFilterOperator)args))" />
<RadzenDropDown @onclick:preventDefault="true" TextProperty="Text" ValueProperty="Value" Style="width: 90px" InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", Column.Title + Grid.LogicalOperatorAriaLabel + (Column.LogicalFilterOperator == LogicalFilterOperator.And ? Grid.AndOperatorText : Grid.OrOperatorText) }})"
Data="@(Enum.GetValues(typeof(LogicalFilterOperator)).Cast<LogicalFilterOperator>().Select(t => new { Text = t == LogicalFilterOperator.And ? Grid.AndOperatorText : Grid.OrOperatorText, Value = t }))" TValue="LogicalFilterOperator" Value="@Column.LogicalFilterOperator" Change="@(args => Column.SetLogicalFilterOperator((LogicalFilterOperator)args))" />
<RadzenDropDown InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", Grid.SecondFilterOperatorAriaLabel + Column.GetFilterOperatorText(Column.GetSecondFilterOperator()) }})" @onclick:preventDefault="true" Data="@(Column.GetFilterOperators().Select(t => new { Value = Column.GetFilterOperatorText(t), Key = t }))" TextProperty="Value" ValueProperty="Key" TValue="FilterOperator" Value="@Column.GetSecondFilterOperator()" Change="@(args => Column.SetSecondFilterOperator((FilterOperator)args))" />
@if (Column.SecondFilterValueTemplate != null)
{
@Column.SecondFilterValueTemplate(Column)
}
else if (PropertyAccess.IsNullableEnum(Column.FilterPropertyType) || PropertyAccess.IsEnum(Column.FilterPropertyType))
{
<RadzenDropDown Disabled="@(!Column.CanSetFilterValue(false))" AllowClear="false" AllowFiltering="false" TValue="@object" InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", Column.Title + Grid.SecondFilterValueAriaLabel }})"
Value=@Column.GetSecondFilterValue() Multiple="false" Placeholder="@Grid.EnumFilterSelectText" TextProperty="Text" ValueProperty="Value" Data=@EnumExtensions.EnumAsKeyValuePair(Nullable.GetUnderlyingType(Column.FilterPropertyType) ?? Column.FilterPropertyType, Grid.EnumFilterTranslationFunc)
Change="@(args => Column.SetFilterValue(args,false))" />
}
else if (PropertyAccess.IsNumeric(Column.FilterPropertyType))
{
@(Grid.DrawNumericFilter(Column, false, false))
}
else if (PropertyAccess.IsDate(Column.FilterPropertyType))
{
<RadzenDatePicker Disabled="@(!Column.CanSetFilterValue(false))" TValue="@object" InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", Column.Title + Grid.SecondFilterValueAriaLabel + Column.GetSecondFilterValue() }})"
ShowTime="@Column.ShowTimeForDateTimeFilter()" ShowTimeOkButton="true" DateFormat="@Grid.getFilterDateFormat(Column)"
Value="@Column.GetSecondFilterValue()" Change="@(args => Column.SetFilterValue(PropertyAccess.IsDateOnly(Column.FilterPropertyType) ? PropertyAccess.DateOnlyFromDateTime(args.Value) : args.Value, false))" AllowInput=@(Grid.AllowFilterDateInput) />
<RadzenDropDown InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", Grid.SecondFilterOperatorAriaLabel + Column.GetFilterOperatorText(Column.GetSecondFilterOperator()) }})" @onclick:preventDefault="true" Data="@(Column.GetFilterOperators().Select(t => new { Value = Column.GetFilterOperatorText(t), Key = t }))" TextProperty="Value" ValueProperty="Key" TValue="FilterOperator" Value="@Column.GetSecondFilterOperator()" Change="@(args => Column.SetSecondFilterOperator((FilterOperator)args))" />
@if (Column.SecondFilterValueTemplate != null)
{
@Column.SecondFilterValueTemplate(Column)
}
else if (PropertyAccess.IsNullableEnum(Column.FilterPropertyType) || PropertyAccess.IsEnum(Column.FilterPropertyType))
{
<RadzenDropDown Disabled="@(!Column.CanSetFilterValue(false))" AllowClear="false" AllowFiltering="false" TValue="@object" InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", Column.Title + Grid.SecondFilterValueAriaLabel }})"
Value=@Column.GetSecondFilterValue() Multiple="false" Placeholder="@Grid.EnumFilterSelectText" TextProperty="Text" ValueProperty="Value" Data=@EnumExtensions.EnumAsKeyValuePair(Nullable.GetUnderlyingType(Column.FilterPropertyType) ?? Column.FilterPropertyType, Grid.EnumFilterTranslationFunc)
Change="@(args => Column.SetFilterValue(args,false))" />
}
else if (PropertyAccess.IsNumeric(Column.FilterPropertyType))
{
@(Grid.DrawNumericFilter(Column, false, false))
}
else if (PropertyAccess.IsDate(Column.FilterPropertyType))
{
<RadzenDatePicker Disabled="@(!Column.CanSetFilterValue(false))" TValue="@object" InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", Column.Title + Grid.SecondFilterValueAriaLabel + Column.GetSecondFilterValue() }})"
ShowTime="@Column.ShowTimeForDateTimeFilter()" ShowTimeOkButton="true" DateFormat="@Grid.getFilterDateFormat(Column)"
Value="@Column.GetSecondFilterValue()" Change="@(args => Column.SetFilterValue(PropertyAccess.IsDateOnly(Column.FilterPropertyType) ? PropertyAccess.DateOnlyFromDateTime(args.Value) : args.Value, false))" AllowInput=@(Grid.AllowFilterDateInput) />
}
else if (Column.FilterPropertyType == typeof(bool) || Column.FilterPropertyType == typeof(bool?))
{
<RadzenCheckBox Disabled="@(!Column.CanSetFilterValue(false))" InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", Column.Title + Grid.SecondFilterValueAriaLabel + Column.GetSecondFilterValue() }})" TriState="true" TValue="@object" Value="@Column.GetSecondFilterValue()" Change="@(args => { Column.SetFilterValue(args, false); Grid.SaveSettings(); })" />
}
else
{
<RadzenTextBox Disabled="@(!Column.CanSetFilterValue(false))" id="@($"{getColumnPopupID()}-sf2")" aria-label=@(Column.Title + Grid.SecondFilterValueAriaLabel + Column.GetSecondFilterValue()) Value="@($"{Column.GetSecondFilterValue()}")" Change="@(args => Column.SetFilterValue(args, false))" />
}
else if (Column.FilterPropertyType == typeof(bool) || Column.FilterPropertyType == typeof(bool?))
{
<RadzenCheckBox Disabled="@(!Column.CanSetFilterValue(false))" InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", Column.Title + Grid.SecondFilterValueAriaLabel + Column.GetSecondFilterValue() }})" TriState="true" TValue="@object" Value="@Column.GetSecondFilterValue()" Change="@(args => { Column.SetFilterValue(args, false); Grid.SaveSettings(); })" />
}
else
{
<RadzenTextBox Disabled="@(!Column.CanSetFilterValue(false))" id="@($"{getColumnPopupID()}-sf2")" aria-label=@(Column.Title + Grid.SecondFilterValueAriaLabel + Column.GetSecondFilterValue()) Value="@($"{Column.GetSecondFilterValue()}")" Change="@(args => Column.SetFilterValue(args, false))" />
}
}
else
@@ -154,39 +153,44 @@
}
else
{
<RadzenListBox AllowVirtualization="@Column.AllowCheckBoxListVirtualization" AllowClear="true" Multiple="true" Style="height: 300px"
TValue="IEnumerable<object>" Value=@Column.GetFilterValue() Change="@ListBoxChange"
Data=@filterValues Count=@filterValuesCount LoadData="@LoadFilterValues"
AllowFiltering="@(!string.IsNullOrEmpty(Column.GetFilterProperty()) && PropertyAccess.GetPropertyType(typeof(TItem), Column.GetFilterProperty()) == typeof(string))"
Disabled="@(!Column.CanSetFilterValue())" FilterCaseSensitivity="FilterCaseSensitivity.CaseInsensitive"
InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", Column.Title + Grid.FilterValueAriaLabel + Column.GetFilterValue() }})">
<Template>
@if (context as Enum != null)
{
@EnumExtensions.GetDisplayDescription(context as Enum)
}
else
{
@(!string.IsNullOrEmpty(Column.FormatString) ? string.Format(Column.FormatProvider ?? Grid?.Culture ?? CultureInfo.CurrentCulture, Column.FormatString, context ?? "") : Convert.ToString(context ?? "", Grid?.Culture ?? CultureInfo.CurrentCulture))
}
</Template>
</RadzenListBox>
<RadzenProgressBarCircular Style="position:absolute;width:100%;" Visible="@isLoading" Value="100" ShowValue="false" Mode="ProgressBarMode.Indeterminate" />
<RadzenListBox AllowVirtualization="@Column.AllowCheckBoxListVirtualization" AllowClear="true" Multiple="true" Style="height: 300px"
TValue="IEnumerable<object>" Value=@Column.GetFilterValue() Change="@ListBoxChange"
Data=@filterValues Count=@filterValuesCount LoadData="@LoadFilterValues"
AllowFiltering="@(!string.IsNullOrEmpty(Column.GetFilterProperty()) && PropertyAccess.GetPropertyType(typeof(TItem), Column.GetFilterProperty()) == typeof(string))"
Disabled="@(!Column.CanSetFilterValue())" FilterCaseSensitivity="FilterCaseSensitivity.CaseInsensitive"
InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", Column.Title + Grid.FilterValueAriaLabel + Column.GetFilterValue() }})">
<Template>
@if (context as Enum != null)
{
@EnumExtensions.GetDisplayDescription(context as Enum)
}
else
{
@(!string.IsNullOrEmpty(Column.FormatString) ? string.Format(Column.FormatProvider ?? Grid?.Culture ?? CultureInfo.CurrentCulture, Column.FormatString, context ?? "") : Convert.ToString(context ?? "", Grid?.Culture ?? CultureInfo.CurrentCulture))
}
</Template>
</RadzenListBox>
}
}
</form>
}
</div>
@if (Column.FilterTemplate == null)
{
<div class="rz-grid-filter-buttons">
<RadzenButton ButtonStyle="ButtonStyle.Base" class="rz-clear-filter" Click="@ClearFilter" Text=@Grid.ClearFilterText title="@Grid.ClearFilterText" />
<RadzenButton onmousedown="@getBlur()" ButtonStyle="ButtonStyle.Primary" class="rz-apply-filter" form="@($"{getColumnPopupID()}-form")" ButtonType="ButtonType.Submit" Text=@Grid.ApplyFilterText title="@Grid.ApplyFilterText" />
</div>
}
</Popup>
</div>
@if (Column.FilterTemplate == null)
{
<div class="rz-grid-filter-buttons">
<RadzenButton ButtonStyle="ButtonStyle.Base" class="rz-clear-filter" Click="@ClearFilter" Text=@Grid.ClearFilterText title="@Grid.ClearFilterText" />
<RadzenButton onmousedown="@getBlur()" ButtonStyle="ButtonStyle.Primary" class="rz-apply-filter" form="@($"{getColumnPopupID()}-form")" ButtonType="ButtonType.Submit" Text=@Grid.ApplyFilterText title="@Grid.ApplyFilterText" />
</div>
}
</Popup>
}
</div>
@if (Grid.allColumns.Any(c => c.Parent != null) && Grid.AllowFiltering && Column.Filterable && (filterMode == FilterMode.Simple || filterMode == FilterMode.SimpleWithMenu))
{
@Grid.RenderSimpleFilter(Column, filterMode)
}
</div>
</th>
</th>
}
else
{
@@ -195,7 +199,7 @@ else
ColumnIndex = Grid.allColumns.IndexOf(column);//for child must have different columnindex
<RadzenDataGridHeaderCell RowIndex="@RowIndex" Grid="@Grid" Column="@column" Style="@column.GetStyle(true, true)" ColumnIndex="@ColumnIndex"
CssClass="@($"rz-unselectable-text {(Grid.AllowSorting && column.Sortable ? "rz-sortable-column" : "")} {column.HeaderCssClass} {Grid.getFrozenColumnClass(column, Grid.ColumnsCollection.Where(c => c.GetVisible()).ToList())} {Grid.getCompositeCellCSSClass(column)} {Grid.getColumnAlignClass(column)}".Trim())" />
CssClass="@($"rz-unselectable-text {(Grid.AllowSorting && column.Sortable ? "rz-sortable-column" : "")} {column.HeaderCssClass} {Grid.getFrozenColumnClass(column, Grid.ColumnsCollection.Where(c => c.GetVisible()).ToList())} {Grid.getCompositeCellCSSClass(column)} {Grid.getColumnAlignClass(column)}".Trim())" />
}
}
@code {
@@ -205,24 +209,19 @@ else
int filterValuesCount;
internal IEnumerable filterValues;
string loadDataArgsString;
bool isLoading = false;
async Task LoadFilterValues(LoadDataArgs loadDataArgs)
{
isLoading = true;
await Task.Yield();
var property = Column.Property != Column.FilterProperty && !string.IsNullOrEmpty(Column.FilterProperty) ? Column.Property :
Column.GetFilterProperty();
var propertyType = PropertyAccess.GetPropertyType(typeof(TItem), property);
if (property.IndexOf(".") != -1)
{
property = $"np({property})";
}
if (propertyType == typeof(string))
{
property = $@"({property} == null ? """" : {property})";
}
var loadDataArgsString = $"{loadDataArgs.Skip}|{loadDataArgs.Top}{loadDataArgs.Filter}";
if (Column.Grid.LoadColumnFilterData.HasDelegate)
@@ -231,7 +230,7 @@ else
await Column.Grid.LoadColumnFilterData.InvokeAsync(args);
filterValues = args.Data.AsQueryable().Select(DynamicLinqCustomTypeProvider.ParsingConfig, property).Distinct().Cast(propertyType ?? typeof(object));
filterValues = args.Data.AsQueryable().Select(property).Distinct();
filterValuesCount = args.Count;
}
else if(!string.IsNullOrEmpty(property) && (filterValues == null || this.loadDataArgsString != loadDataArgsString) && Column.Grid.Data != null)
@@ -242,8 +241,8 @@ else
if(!string.IsNullOrEmpty(loadDataArgs.Filter))
{
query = Radzen.QueryableExtension.Where(Column.Grid.Data.AsQueryable().Where<TItem>(Column.Grid.allColumns.Where(c => c != Column)),
property, loadDataArgs.Filter, StringFilterOperator.Contains, FilterCaseSensitivity.CaseInsensitive);
query = Column.Grid.Data.AsQueryable().Where<TItem>(Column.Grid.allColumns.Where(c => c != Column))
.Where(property, loadDataArgs.Filter, StringFilterOperator.Contains, FilterCaseSensitivity.CaseInsensitive);
}
else
{
@@ -252,35 +251,34 @@ else
if (Column.Property != Column.FilterProperty && !string.IsNullOrEmpty(Column.FilterProperty))
{
query = query
.SelectMany(DynamicLinqCustomTypeProvider.ParsingConfig, property)
.Select(DynamicLinqCustomTypeProvider.ParsingConfig, Column.FilterProperty)
.Distinct().OrderBy("it").Cast(typeof(object));
query = query.SelectMany(property).Select(Column.FilterProperty).Distinct().OrderBy();
}
else
{
query = query.Select(DynamicLinqCustomTypeProvider.ParsingConfig, property).Distinct().OrderBy("it").Cast(propertyType ?? typeof(object));
query = query.Select(property).Distinct().OrderBy();
}
filterValuesCount = query.Count();
filterValuesCount = query.Cast<object>().Count();
if(loadDataArgs.Skip != null)
{
query = query.Skip(loadDataArgs.Skip.Value);
query = query.Cast<object>().Skip(loadDataArgs.Skip.Value);
}
if(loadDataArgs.Top != null)
{
query = query.Take(loadDataArgs.Top.Value);
query = query.Cast<object>().Take(loadDataArgs.Top.Value);
}
filterValues = query.Select("it => it.ToString() == string.Empty ? null : it");
filterValues = query.Cast(propertyType);
if (!Column.AllowCheckBoxListVirtualization)
{
await InvokeAsync(StateHasChanged);
}
}
isLoading = false;
}
void ListBoxChange(object args)
@@ -343,7 +341,7 @@ else
bool IsAdvancedNumeric()
{
return Grid.FilterMode == FilterMode.Advanced &&
return (Column.FilterMode ?? Grid.FilterMode) == FilterMode.Advanced &&
PropertyAccess.IsNumeric(Column.FilterPropertyType) &&
!(PropertyAccess.IsEnum(Column.FilterPropertyType) || PropertyAccess.IsNullableEnum(Column.FilterPropertyType));
}

View File

@@ -1,15 +1,5 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.JSInterop;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Data.Common;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Text.Json;
using System.Threading.Tasks;
namespace Radzen.Blazor

View File

@@ -1,21 +1,17 @@
@using Radzen
@using Radzen.Blazor.Rendering
@using Microsoft.AspNetCore.Components.Forms
@using System.Linq.Expressions
@using System.Globalization
@using Microsoft.JSInterop
@typeparam TValue
@inherits RadzenComponent
@implements IRadzenFormComponent
@if (Visible)
{
<div @ref="@Element" @attributes="Attributes" class="@($"rz-datepicker{(Disabled ? " rz-state-disabled" : "")}") @GetCssClass()" style="@getStyle()" id="@GetId()">
<div @ref="@Element" @attributes="Attributes" class="@GetCssClass()" style="@getStyle()" id="@GetId()">
@if (!Inline)
{
<input @ref="@input" @attributes="InputAttributes" disabled="@Disabled" readonly="@IsReadonly" value="@FormattedValue" tabindex="@(Disabled ? "-1" : $"{TabIndex}")"
@onchange="@ParseDate" autocomplete="off" type="text" name="@Name" @onkeydown="@(args => OnKeyPress(args))" @onkeydown:preventDefault=preventKeyPress @onkeydown:stopPropagation
class="rz-inputtext @InputClass @(ReadOnly ? "rz-readonly" : "") @(!ShowButton ? "rz-input-trigger" : "")" id="@Name" placeholder="@Placeholder" />
class="rz-inputtext @InputClass @(ReadOnly ? "rz-readonly" : "") @(!ShowButton ? "rz-input-trigger" : "")" id="@Name" placeholder="@CurrentPlaceholder" />
@if (ShowButton)
{
<button aria-label="@ToggleAriaLabel" @onmousedown=@OnToggle class="@($"rz-datepicker-trigger rz-button rz-button-icon-only{(Disabled ? " rz-state-disabled" : "")} {ButtonClass}")" tabindex="-1" type="button">
@@ -33,10 +29,10 @@
@if (!TimeOnly)
{
<div class="rz-calendar-header">
<a id="@(GetId() + "pm")" tabindex="-1" aria-label="@PrevMonthAriaLabel" @onclick:preventDefault="true" class="rz-button rz-button-md rz-variant-text rz-button-icon-only rz-secondary rz-shade-default rz-calendar-prev" @onclick="@(async () => { if (!Disabled) { try { if(CurrentDate.AddMonths(-1).Year >= YearFrom) {CurrentDate = CurrentDate.AddMonths(-1);}} catch (ArgumentOutOfRangeException) {}} })">
<a id="@(GetId() + "pm")" tabindex="-1" aria-label="@PrevMonthAriaLabel" @onclick:preventDefault="true" class="rz-button rz-button-md rz-variant-text rz-button-icon-only rz-secondary rz-shade-default rz-calendar-prev" @onclick="@(() => { if (!Disabled) { try { if(CurrentDate.AddMonths(-1).Year >= YearFrom) {CurrentDate = CurrentDate.AddMonths(-1);}} catch (ArgumentOutOfRangeException) {}} })">
<span class="notranslate rzi rz-calendar-prev-icon"></span>
</a>
<a id="@(GetId() + "nm")" tabindex="-1" aria-label="@NextMonthAriaLabel" @onclick:preventDefault="true" class="rz-button rz-button-md rz-variant-text rz-button-icon-only rz-secondary rz-shade-default rz-calendar-next" @onclick="@(async () => { if (!Disabled) { try { if(CurrentDate.AddMonths(1).Year <= YearTo) {CurrentDate = CurrentDate.AddMonths(1);}} catch (ArgumentOutOfRangeException) {} } })">
<a id="@(GetId() + "nm")" tabindex="-1" aria-label="@NextMonthAriaLabel" @onclick:preventDefault="true" class="rz-button rz-button-md rz-variant-text rz-button-icon-only rz-secondary rz-shade-default rz-calendar-next" @onclick="@(() => { if (!Disabled) { try { if(CurrentDate.AddMonths(1).Year <= YearTo) {CurrentDate = CurrentDate.AddMonths(1);}} catch (ArgumentOutOfRangeException) {} } })">
<span class="notranslate rzi rz-calendar-next-icon"></span>
</a>
<div class="rz-calendar-title">
@@ -116,19 +112,19 @@
{
<div class="rz-timepicker" @onmousedown:stopPropagation>
<RadzenNumeric InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", "hour" }})" TValue="int" Disabled="@Disabled" Value="@(HourFormat == "12" ? ((CurrentDate.Hour + 11) % 12) + 1 : CurrentDate.Hour)"
Min="@(HourFormat == "12" ? 1 : -1)" Max="@(HourFormat == "12" ? 12 : 24)" TValue="double" Step="@HoursStep"
Min="@(HourFormat == "12" ? 1 : -1)" Max="@(HourFormat == "12" ? 12 : 24)" Step="@HoursStep"
Change="@UpdateHour" class="rz-hour-picker" @oninput=@OnUpdateHourInput Format="@(PadHours ? "00" : "")" Name="@($"{UniqueID}-h")" />
<div class="rz-separator">
<span>:</span>
</div>
<RadzenNumeric InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", "minutes" }})" TValue="int" Disabled="@Disabled" Value="CurrentDate.Minute" TValue="double" Step="@MinutesStep" Min="0" Max="59"
<RadzenNumeric InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", "minutes" }})" TValue="int" Disabled="@Disabled" Value="CurrentDate.Minute" Step="@MinutesStep" Min="0" Max="59"
Change="@UpdateMinutes" class="rz-minute-picker" @oninput=@OnUpdateHourMinutes Format="@(PadMinutes ? "00" : "")" Name="@($"{UniqueID}-m")"/>
@if (ShowSeconds)
{
<div class="rz-separator">
<span>:</span>
</div>
<RadzenNumeric InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", "seconds" }})" TValue="int" Disabled="@Disabled" Value="CurrentDate.Second" TValue="double" Step="@SecondsStep" Min="0" Max="59"
<RadzenNumeric InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", "seconds" }})" TValue="int" Disabled="@Disabled" Value="CurrentDate.Second" Step="@SecondsStep" Min="0" Max="59"
Change="@UpdateSeconds" class="rz-second-picker" @oninput=@OnUpdateHourSeconds Format="@(PadSeconds ? "00" : "")" Name="@($"{UniqueID}-s")"/>
}
@if (HourFormat == "12")

View File

@@ -7,7 +7,6 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Linq.Expressions;
using System.Threading.Tasks;
@@ -750,6 +749,12 @@ namespace Radzen.Blazor
[Parameter]
public bool Disabled { get; set; }
/// <summary>
/// Gets or sets the FormFieldContext of the component
/// </summary>
[CascadingParameter]
public IFormFieldContext FormFieldContext { get; set; } = null;
/// <summary>
/// Gets or sets a value indicating whether days part is shown.
/// </summary>
@@ -917,6 +922,9 @@ namespace Radzen.Blazor
return $"{(Inline ? "overflow:auto;" : "")}{(Style != null ? Style : "")}";
}
/// <summary> Gets the current placeholder. Returns empty string if this component is inside a RadzenFormField.</summary>
protected string CurrentPlaceholder => FormFieldContext?.AllowFloatingLabel == true ? " " : Placeholder;
/// <summary>
/// Closes this instance popup.
/// </summary>
@@ -977,10 +985,13 @@ namespace Radzen.Blazor
/// <inheritdoc />
protected override string GetComponentCssClass()
{
return ClassList.Create()
return ClassList.Create("rz-datepicker")
.Add("rz-datepicker-inline", Inline)
.AddDisabled(Disabled)
.Add("rz-state-empty", !HasValue)
.Add(FieldIdentifier, EditContext)
.ToString();
}
private async Task SetDay(DateTime newValue)
@@ -1057,8 +1068,15 @@ namespace Radzen.Blazor
shouldClose = !visible;
}
var disabledChanged = parameters.DidParameterChange(nameof(Disabled), Disabled);
await base.SetParametersAsync(parameters);
if (disabledChanged)
{
FormFieldContext?.DisabledChanged(Disabled);
}
if (shouldClose && !firstRender && IsJSRuntimeAvailable)
{
await JSRuntime.InvokeVoidAsync("Radzen.destroyPopup", PopupID);

View File

@@ -5,7 +5,6 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;
namespace Radzen.Blazor
@@ -354,6 +353,7 @@ namespace Radzen.Blazor
/// </summary>
protected ElementReference popup;
bool isFirstRender;
/// <summary>
/// Called when [after render asynchronous].
/// </summary>
@@ -361,6 +361,8 @@ namespace Radzen.Blazor
/// <returns>Task.</returns>
protected override async Task OnAfterRenderAsync(bool firstRender)
{
isFirstRender = firstRender;
if (firstRender)
{
if(Visible && LoadData.HasDelegate && Data == null)
@@ -466,10 +468,13 @@ namespace Radzen.Blazor
if (query == null)
return;
var filterOperator = FilterOperator == StringFilterOperator.Contains ?
Radzen.FilterOperator.Contains :
FilterOperator == StringFilterOperator.StartsWith ? Radzen.FilterOperator.StartsWith :
FilterOperator == StringFilterOperator.EndsWith ? Radzen.FilterOperator.EndsWith : Radzen.FilterOperator.Equals;
if (!string.IsNullOrEmpty(searchText))
{
string filterCaseSensitivityOperator = FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive ? ".ToLower()" : "";
if (AllowFilteringByAllStringColumns && grid != null)
{
if (AllowFilteringByWord)
@@ -478,16 +483,16 @@ namespace Radzen.Blazor
foreach (string word in words)
{
query = query.Where(DynamicLinqCustomTypeProvider.ParsingConfig, string.Join(" || ", grid.ColumnsCollection.Where(c => c.Filterable && IsColumnFilterPropertyTypeString(c))
.Select(c => GetPropertyFilterExpression(c.GetFilterProperty(), filterCaseSensitivityOperator))),
FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive ? word.ToLower() : word);
query = query.Where(grid.ColumnsCollection.Where(c => c.Filterable && IsColumnFilterPropertyTypeString(c))
.Select(c => new FilterDescriptor() { Property = c.GetFilterProperty(), FilterValue = word, FilterOperator = filterOperator }),
LogicalFilterOperator.Or, FilterCaseSensitivity);
}
}
else
{
query = query.Where(DynamicLinqCustomTypeProvider.ParsingConfig, string.Join(" || ", grid.ColumnsCollection.Where(c => c.Filterable && IsColumnFilterPropertyTypeString(c))
.Select(c => GetPropertyFilterExpression(c.GetFilterProperty(), filterCaseSensitivityOperator))),
FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive ? searchText.ToLower() : searchText);
query = query.Where(grid.ColumnsCollection.Where(c => c.Filterable && IsColumnFilterPropertyTypeString(c))
.Select(c => new FilterDescriptor() { Property = c.GetFilterProperty(), FilterValue = searchText, FilterOperator = filterOperator }),
LogicalFilterOperator.Or, FilterCaseSensitivity);
}
}
else
@@ -498,26 +503,24 @@ namespace Radzen.Blazor
foreach (string word in words)
{
query = query.Where(DynamicLinqCustomTypeProvider.ParsingConfig, $"{GetPropertyFilterExpression(TextProperty, filterCaseSensitivityOperator)}",
FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive ? word.ToLower() : word);
query = query.Where(TextProperty, word, FilterOperator, FilterCaseSensitivity);
}
}
else
{
query = query.Where(DynamicLinqCustomTypeProvider.ParsingConfig, $"{GetPropertyFilterExpression(TextProperty, filterCaseSensitivityOperator)}",
FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive ? searchText.ToLower() : searchText);
query = query.Where(TextProperty, searchText, FilterOperator, FilterCaseSensitivity);
}
}
}
if (!string.IsNullOrEmpty(args.OrderBy))
{
query = query.OrderBy(DynamicLinqCustomTypeProvider.ParsingConfig, args.OrderBy);
query = query.OrderBy(args.OrderBy);
}
count = await Task.FromResult(query.Count());
count = await Task.FromResult(query.Cast<object>().Count());
pagedData = await Task.FromResult(QueryableExtension.ToList(query.Skip(skip.HasValue ? skip.Value : 0).Take(args.Top.HasValue ? args.Top.Value : PageSize)).Cast<object>());
pagedData = await Task.FromResult(query.Cast<object>().Skip(skip.HasValue ? skip.Value : 0).Take(args.Top.HasValue ? args.Top.Value : PageSize).ToList());
_internalView = query;
@@ -584,7 +587,11 @@ namespace Radzen.Blazor
if (!string.IsNullOrEmpty(ValueProperty))
{
var item = Query.Where(DynamicLinqCustomTypeProvider.ParsingConfig, $@"{ValueProperty} == @0", value).FirstOrDefault();
var item = Query.Where(new FilterDescriptor[]
{
new FilterDescriptor() { Property = ValueProperty, FilterValue = value }
}, LogicalFilterOperator.And, FilterCaseSensitivity.Default).FirstOrDefault();
if (item != null && SelectedItem != item)
{
SelectedItem = item;
@@ -605,6 +612,15 @@ namespace Radzen.Blazor
SelectedItemChanged.InvokeAsync(SelectedItem);
selectedItems.Clear();
selectedItems.Add(SelectedItem);
try
{
if (grid != null && !isFirstRender)
{
InvokeAsync(() => grid.SelectRow(SelectedItem, false));
JSRuntime.InvokeAsync<int[]>("Radzen.focusTableRow", grid.GridId(), "ArrowDown", Items.ToList().IndexOf(SelectedItem) - 1, null);
}
}
catch { }
}
}
else
@@ -617,8 +633,12 @@ namespace Radzen.Blazor
{
foreach (object v in valueList)
{
var item = Query.Where(DynamicLinqCustomTypeProvider.ParsingConfig, $@"{ValueProperty} == @0", v).FirstOrDefault();
if (item != null && !selectedItems.AsQueryable().Where(DynamicLinqCustomTypeProvider.ParsingConfig, $@"object.Equals(it.{ValueProperty},@0)", v).Any())
var item = Query.Where(new FilterDescriptor[]
{
new FilterDescriptor() { Property = ValueProperty, FilterValue = v }
}, LogicalFilterOperator.And, FilterCaseSensitivity.Default).FirstOrDefault();
if (item != null && !selectedItems.AsQueryable().Where(i => object.Equals(GetItemOrValueFromProperty(i, ValueProperty), v)).Any())
{
selectedItems.Add(item);
}

View File

@@ -28,6 +28,13 @@ namespace Radzen.Blazor
[Parameter]
public string AlternateText { get; set; } = "gravatar";
/// <summary>
/// Gets or sets the size. Defaulted to 36 (pixels).
/// </summary>
/// <value>The size of the image in pixels.</value>
[Parameter]
public int Size { get; set; } = 36;
/// <summary>
/// Gets gravatar URL.
/// </summary>
@@ -38,9 +45,8 @@ namespace Radzen.Blazor
var md5Email = MD5.Calculate(System.Text.Encoding.ASCII.GetBytes(Email != null ? Email : ""));
var style = "retro";
var width = "36";
return $"https://secure.gravatar.com/avatar/{md5Email}?d={style}&s={width}";
return $"https://secure.gravatar.com/avatar/{md5Email}?d={style}&s={Size}";
}
}

View File

@@ -1,7 +1,7 @@
@using Radzen.Blazor.Rendering
@inherits RadzenHtmlEditorColorBase
<EditorColorPicker Title=@Title @bind-Value=@Value Icon="opacity" Change=@OnChange ShowHSV=@ShowHSV ShowRGBA=@ShowRGBA
<EditorColorPicker Title=@Title @bind-Value=@value Icon="opacity" Change=@OnChange ShowHSV=@ShowHSV ShowRGBA=@ShowRGBA
ShowColors=@ShowColors ShowButton=@ShowButton HexText=@HexText RedText=@RedText GreenText=@GreenText
BlueText=@BlueText AlphaText=@AlphaText ButtonText=@ButtonText>
@ChildContent

View File

@@ -1,3 +1,4 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
namespace Radzen.Blazor
@@ -24,7 +25,7 @@ namespace Radzen.Blazor
/// Specifies the default background color. Set to <c>"rgb(0, 0, 255)"</c> by default;
/// </summary>
[Parameter]
public string Value { get; set; } = "rgb(0, 0, 255)";
public override string Value { get; set; } = "rgb(0, 0, 255)";
/// <summary>
/// Specifies the title (tooltip) displayed when the user hovers the tool. Set to <c>"Background color"</c> by default.
/// </summary>

View File

@@ -1,7 +1,7 @@
@using Radzen.Blazor.Rendering
@inherits RadzenHtmlEditorColorBase
<EditorColorPicker Title=@Title @bind-Value=@Value Icon="title" Change=@OnChange ShowHSV=@ShowHSV ShowRGBA=@ShowRGBA
<EditorColorPicker Title=@Title @bind-Value=@value Icon="title" Change=@OnChange ShowHSV=@ShowHSV ShowRGBA=@ShowRGBA
ShowColors=@ShowColors ShowButton=@ShowButton HexText=@HexText RedText=@RedText GreenText=@GreenText
BlueText=@BlueText AlphaText=@AlphaText ButtonText=@ButtonText>
@ChildContent

View File

@@ -24,7 +24,7 @@ namespace Radzen.Blazor
/// Specifies the default text color. Set to <c>"rgb(255, 0, 0)"</c> by default;
/// </summary>
[Parameter]
public string Value { get; set; } = "rgb(255, 0, 0)";
public override string Value { get; set; } = "rgb(255, 0, 0)";
/// <summary>
/// Specifies the title (tooltip) displayed when the user hovers the tool. Set to <c>"Text color"</c> by default.
/// </summary>

View File

@@ -84,5 +84,36 @@ namespace Radzen.Blazor
{
await Editor.ExecuteCommandAsync(CommandName, value);
}
/// <summary>
/// The default value of the color picker.
/// </summary>
public abstract string Value { get; set; }
/// <summary>
/// The internal state of the component.
/// </summary>
protected string value;
/// <inheritdoc />
protected override void OnInitialized()
{
value = Value;
base.OnInitialized();
}
/// <inheritdoc />
public override async Task SetParametersAsync(ParameterView parameters)
{
var valueChanged = parameters.DidParameterChange(nameof(Value), Value);
await base.SetParametersAsync(parameters);
if (valueChanged)
{
value = Value;
}
}
}
}

View File

@@ -216,6 +216,8 @@ namespace Radzen.Blazor
{
_value = value;
}
stringValue = $"{value}";
}
}
@@ -394,6 +396,8 @@ namespace Radzen.Blazor
newValue = ApplyMinMax(newValue);
stringValue = $"{newValue}";
if (EqualityComparer<TValue>.Default.Equals(Value, newValue))
{
await JSRuntime.InvokeAsync<string>("Radzen.setInputValue", input, FormattedValue);
@@ -457,8 +461,14 @@ namespace Radzen.Blazor
var converter = TypeDescriptor.GetConverter(typeof(TValue));
if (converter.CanConvertTo(typeof(decimal)))
return (decimal)converter.ConvertTo(null, Culture, input, typeof(decimal));
return (decimal)ConvertType.ChangeType(input, typeof(decimal));
try
{
return (decimal)ConvertType.ChangeType(input, typeof(decimal), Culture);
}
catch
{
return decimal.Zero;
}
}
private TValue ConvertFromDecimal(decimal? input)
@@ -472,7 +482,7 @@ namespace Radzen.Blazor
return (TValue)converter.ConvertFrom(null, Culture, input);
}
return (TValue)ConvertType.ChangeType(input, typeof(TValue));
return (TValue)ConvertType.ChangeType(input, typeof(TValue), Culture);
}
/// <summary>

View File

@@ -13,19 +13,19 @@
}
<RadzenListBox @bind-Value=@selectedSourceItems Data="@source" Multiple="@Multiple" @bind-SearchText=@sourceSearchText
FilterAsYouType=true FilterCaseSensitivity=FilterCaseSensitivity.CaseInsensitive
TextProperty="@TextProperty" AllowFiltering=@AllowFiltering ItemRender="@OnSourceItemRender"
Template=@ListBoxTemplate Placeholder="@(SourcePlaceholder ?? Placeholder)" SelectAllText="@SelectAllText"
TextProperty="@TextProperty" DisabledProperty="@DisabledProperty" AllowFiltering=@AllowFiltering ItemRender="@OnSourceItemRender"
Template=@ListBoxTemplate Placeholder="@(SourcePlaceholder ?? Placeholder)" SelectAllText="@SelectAllText" AllowSelectAll="@AllowSelectAll"
Disabled=@(Disabled || Source == null || (Source != null && !Source.Any()))
/>
</RadzenStack>
<RadzenStack Orientation="@(Orientation == Orientation.Vertical ? Orientation.Horizontal : Orientation.Vertical)" JustifyContent="@ButtonJustifyContent" Gap="@ButtonGap" class="rz-picklist-buttons">
<RadzenButton Icon="keyboard_arrow_right" title="@SelectedSourceToTargetTitle" Click="@SelectedSourceToTarget" Disabled=@(Disabled || selectedSourceItems == null || (Multiple && (selectedSourceItems as IEnumerable)?.Cast<object>().Any() != true))
<RadzenButton Icon="@SelectedSourceToTargetIcon" title="@SelectedSourceToTargetTitle" Click="@SelectedSourceToTarget" Disabled=@(Disabled || selectedSourceItems == null || (Multiple && (selectedSourceItems as IEnumerable)?.Cast<object>().Any() != true))
ButtonStyle="@ButtonStyle" Size="@ButtonSize" Variant="@ButtonVariant" Shade="@ButtonShade" />
<RadzenButton Icon="keyboard_arrow_left" title="@SelectedTargetToSourceTitle" Click="@SelectedTargetToSource" Disabled=@(Disabled || selectedTargetItems == null || (Multiple && (selectedTargetItems as IEnumerable)?.Cast<object>().Any() != true))
<RadzenButton Icon="@SelectedTargetToSourceIcon" title="@SelectedTargetToSourceTitle" Click="@SelectedTargetToSource" Disabled=@(Disabled || selectedTargetItems == null || (Multiple && (selectedTargetItems as IEnumerable)?.Cast<object>().Any() != true))
ButtonStyle="@ButtonStyle" Size="@ButtonSize" Variant="@ButtonVariant" Shade="@ButtonShade"/>
<RadzenButton Icon="keyboard_double_arrow_right" title="@SourceToTargetTitle" Click="@SourceToTarget" Disabled=@(Disabled || source == null || (source != null && !source.Any()))
<RadzenButton Icon="@SourceToTargetIcon" title="@SourceToTargetTitle" Click="@SourceToTarget" Disabled=@(Disabled || source == null || (source != null && !source.Any()))
ButtonStyle="@ButtonStyle" Size="@ButtonSize" Variant="@ButtonVariant" Shade="@ButtonShade" Visible=@(AllowMoveAll && AllowMoveAllSourceToTarget) />
<RadzenButton Icon="keyboard_double_arrow_left" title="@TargetToSourceTitle" Click="@TargetToSource" Disabled=@(Disabled || target == null|| (target != null && !target.Any()))
<RadzenButton Icon="@TargetToSourceIcon" title="@TargetToSourceTitle" Click="@TargetToSource" Disabled=@(Disabled || target == null|| (target != null && !target.Any()))
ButtonStyle="@ButtonStyle" Size="@ButtonSize" Variant="@ButtonVariant" Shade="@ButtonShade" Visible=@(AllowMoveAll && AllowMoveAllTargetToSource)/>
</RadzenStack>
<RadzenStack class="rz-picklist-target-wrapper">
@@ -35,8 +35,8 @@
}
<RadzenListBox @bind-Value=@selectedTargetItems Data="@target" Multiple="@Multiple" @bind-SearchText=@targetSearchText
FilterAsYouType=true FilterCaseSensitivity=FilterCaseSensitivity.CaseInsensitive
TextProperty="@TextProperty" AllowFiltering=@AllowFiltering ItemRender="@OnTargetItemRender"
Template=@ListBoxTemplate Placeholder="@(TargetPlaceholder ?? Placeholder)" SelectAllText="@SelectAllText"
TextProperty="@TextProperty" DisabledProperty="@DisabledProperty" AllowFiltering=@AllowFiltering ItemRender="@OnTargetItemRender"
Template=@ListBoxTemplate Placeholder="@(TargetPlaceholder ?? Placeholder)" SelectAllText="@SelectAllText" AllowSelectAll="@AllowSelectAll"
Disabled=@(Disabled || Target == null|| (Target != null && !Target.Any()))
/>
</RadzenStack>

View File

@@ -42,6 +42,13 @@ namespace Radzen.Blazor
[Parameter]
public bool Multiple { get; set; }
/// <summary>
/// Gets or sets a value indicating whether selecting all items is allowed.
/// </summary>
/// <value><c>true</c> if selecting all items is allowed; otherwise, <c>false</c>.</value>
[Parameter]
public bool AllowSelectAll { get; set; } = true;
/// <summary>
/// Gets or sets a value indicating whether component is disabled.
/// </summary>
@@ -91,6 +98,13 @@ namespace Radzen.Blazor
[Parameter]
public string TextProperty { get; set; }
/// <summary>
/// Gets or sets the disabled property
/// </summary>
/// <value>The disabled property.</value>
[Parameter]
public string DisabledProperty { get; set; }
/// <summary>
/// Gets or sets the source template
/// </summary>
@@ -231,6 +245,34 @@ namespace Radzen.Blazor
[Parameter]
public string SelectedTargetToSourceTitle { get; set; } = "Move selected target items to source collection";
/// <summary>
/// Gets or sets the source to target icon
/// </summary>
/// <value>The source to target icon.</value>
[Parameter]
public string SourceToTargetIcon { get; set; } = "keyboard_double_arrow_right";
/// <summary>
/// Gets or sets the selected source to target icon
/// </summary>
/// <value>The selected source to target icon.</value>
[Parameter]
public string SelectedSourceToTargetIcon { get; set; } = "keyboard_arrow_right";
/// <summary>
/// Gets or sets the target to source icon
/// </summary>
/// <value>The target to source icon.</value>
[Parameter]
public string TargetToSourceIcon { get; set; } = "keyboard_double_arrow_left";
/// <summary>
/// Gets or sets the selected target to source icon
/// </summary>
/// <value>The selected target to source icon.</value>
[Parameter]
public string SelectedTargetToSourceIcon { get; set; } = "keyboard_arrow_left";
/// <summary>
/// Gets the final CSS style rendered by the component. Combines it with a <c>style</c> custom attribute.
/// </summary>

View File

@@ -12,27 +12,30 @@
@ChildContent
</CascadingValue>
<div @ref=Element style=@Style @attributes=@Attributes class=@GetCssClass() id=@GetId()>
<div class="rz-scheduler-nav">
<div class="rz-scheduler-nav-prev-next">
<button tabindex="0" class="rz-button rz-prev" @onclick=@OnPrev title="@PrevText"><RadzenIcon Icon="chevron_left" /></button>
<button tabindex="0" class="rz-button rz-next" @onclick=@OnNext title="@NextText"><RadzenIcon Icon="chevron_right" /></button>
<button tabindex="0" class="rz-button rz-today" @onclick=@OnToday title="@TodayText">@TodayText</button>
</div>
<div class="rz-scheduler-nav-title">@SelectedView?.Title</div>
<div class="rz-scheduler-nav-views">
@if (NavigationTemplate != null)
{
@NavigationTemplate
}
else
{
@foreach (var view in Views)
{
<RadzenButton Click=@(args => OnChangeView(view)) Icon=@view.Icon Text=@view.Text class="@($"{(IsSelected(view) ? " rz-state-active" : "")}")" />
}
}
</div>
</div>
@SelectedView?.Render()
@if (ShowHeader)
{
<div class="rz-scheduler-nav">
<div class="rz-scheduler-nav-prev-next">
<button tabindex="0" class="rz-button rz-prev" @onclick=@OnPrev title="@PrevText"><RadzenIcon Icon="chevron_left" /></button>
<button tabindex="0" class="rz-button rz-next" @onclick=@OnNext title="@NextText"><RadzenIcon Icon="chevron_right" /></button>
<button tabindex="0" class="rz-button rz-today" @onclick=@OnToday title="@TodayText">@TodayText</button>
</div>
<div class="rz-scheduler-nav-title">@SelectedView?.Title</div>
<div class="rz-scheduler-nav-views">
@if (NavigationTemplate != null)
{
@NavigationTemplate
}
else
{
@foreach (var view in Views)
{
<RadzenButton Click=@(args => OnChangeView(view)) Icon=@view.Icon Text=@view.Text class="@($"{(IsSelected(view) ? " rz-state-active" : "")}")" />
}
}
</div>
</div>
}
@SelectedView?.Render()
</div>
}

View File

@@ -3,7 +3,6 @@ using Microsoft.JSInterop;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;
namespace Radzen.Blazor
@@ -145,6 +144,13 @@ namespace Radzen.Blazor
[Parameter]
public string TextProperty { get; set; }
/// <summary>
/// Specifies whether to Show or Hide the Scheduler Header. Defaults to true />.
/// </summary>
/// <value>Show / hide header</value>
[Parameter]
public bool ShowHeader { get; set; } = true;
/// <summary>
/// A callback that will be invoked when the user clicks a slot in the current view. Commonly used to add new appointments.
/// </summary>
@@ -188,9 +194,9 @@ namespace Radzen.Blazor
/// &lt;RadzenScheduler Data=@appointments MonthSelect=@OnMonthSelect&gt;
/// &lt;/RadzenScheduler&gt;
/// @code {
/// void OnMonthSelect(SchedulerTodaySelectEventArgs args)
/// void OnMonthSelect(SchedulerMonthSelectEventArgs args)
/// {
/// args.Month = DateTime.Month.AddMonth(1);
/// var selectedMonth = args.MonthStart.Month;
/// }
/// }
/// </code>
@@ -198,6 +204,24 @@ namespace Radzen.Blazor
[Parameter]
public EventCallback<SchedulerMonthSelectEventArgs> MonthSelect { get; set; }
/// <summary>
/// A callback that will be invoked when the user clicks a day header button or the day number in a MonthView.
/// </summary>
/// <example>
/// <code>
/// &lt;RadzenScheduler Data=@appointments DaySelect=@OnDaySelect&gt;
/// &lt;/RadzenScheduler&gt;
/// @code {
/// void OnDaySelect(SchedulerDaySelectEventArgs args)
/// {
/// var selectedDay = args.Day;
/// }
/// }
/// </code>
/// </example>
[Parameter]
public EventCallback<SchedulerDaySelectEventArgs> DaySelect { get; set; }
/// <summary>
/// A callback that will be invoked when the user clicks an appointment in the current view. Commonly used to edit existing appointments.
/// </summary>
@@ -387,6 +411,12 @@ namespace Radzen.Blazor
await MonthSelect.InvokeAsync(new SchedulerMonthSelectEventArgs { MonthStart = monthStart, Appointments = appointments, View = SelectedView });
}
/// <inheritdoc />
public async Task SelectDay(DateTime day, IEnumerable<AppointmentData> appointments)
{
await DaySelect.InvokeAsync(new SchedulerDaySelectEventArgs { Day = day, Appointments = appointments, View = SelectedView });
}
/// <inheritdoc />
public async Task<bool> SelectMore(DateTime start, DateTime end, IEnumerable<AppointmentData> appointments)
{
@@ -580,7 +610,7 @@ namespace Radzen.Blazor
{
if (SelectedView != null)
{
await LoadData.InvokeAsync(new SchedulerLoadDataEventArgs { Start = SelectedView.StartDate, End = SelectedView.EndDate });
await LoadData.InvokeAsync(new SchedulerLoadDataEventArgs { Start = SelectedView.StartDate, End = SelectedView.EndDate, View = SelectedView });
}
}
@@ -611,10 +641,22 @@ namespace Radzen.Blazor
rangeStart = start;
rangeEnd = end;
var predicate = $"{EndProperty} >= @0 && {StartProperty} < @1";
appointments = Data.AsQueryable()
.Where(DynamicLinqCustomTypeProvider.ParsingConfig, predicate, start, end)
.Where(
new FilterDescriptor[] {
new FilterDescriptor
{
Property = StartProperty,
FilterValue = start,
FilterOperator = FilterOperator.GreaterThanOrEquals
},
new FilterDescriptor
{
Property = EndProperty,
FilterValue = end,
FilterOperator = FilterOperator.LessThanOrEquals
}
}, LogicalFilterOperator.And, FilterCaseSensitivity.Default)
.ToList()
.Select(item => new AppointmentData { Start = startGetter(item), End = endGetter(item), Text = textGetter(item), Data = item });

View File

@@ -15,7 +15,27 @@
{
<div @ref="@Element" style="@Style" @attributes="Attributes" class="@GetCssClass()" id="@GetId()">
@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}")">@if(item.Template != null){ @item.Template(item)}else{@if (!string.IsNullOrEmpty(item.Icon)){<i class="notranslate rzi rz-navigation-item-icon" style="margin-right:2px;@(!string.IsNullOrEmpty(item.IconColor) ? $"color:{item.IconColor}" : "")">@((MarkupString)item.Icon)</i>}@if (!string.IsNullOrEmpty(item.Image)){<img class="rz-navigation-item-icon" src="@item.Image" style="@item.ImageStyle" alt=@item.ImageAlternateText/>}<span class="rz-button-text">@item.Text</span>}</div>}
{
<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}")">
@if (item.Template != null)
{
@item.Template(item)
}
else
{
@if (!string.IsNullOrEmpty(item.Icon))
{
<i class="notranslate rzi rz-navigation-item-icon" style="margin-right:2px;@(!string.IsNullOrEmpty(item.IconColor) ? $"color:{item.IconColor}" : "")">@((MarkupString)item.Icon)</i>
}
@if (!string.IsNullOrEmpty(item.Image))
{
<img class="rz-navigation-item-icon" src="@item.Image" style="@item.ImageStyle" alt=@item.ImageAlternateText/>
}
<span class="rz-button-text">@item.Text</span>
}
</div>
}
</div>
}

View File

@@ -1,12 +1,10 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.JSInterop;
using Radzen.Blazor.Rendering;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;
namespace Radzen.Blazor
@@ -34,16 +32,24 @@ namespace Radzen.Blazor
}
/// <summary>
/// Gets or sets the size.
/// Gets or sets the size. Set to <c>ButtonSize.Medium</c> by default.
/// </summary>
/// <value>The size.</value>
[Parameter]
public ButtonSize Size { get; set; } = ButtonSize.Medium;
/// <summary>
/// Gets or sets the orientation. Set to <c>Orientation.Horizontal</c> by default.
/// </summary>
/// <value>The orientation.</value>
[Parameter]
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);
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);
/// <summary>
/// Gets or sets the value property.
@@ -98,9 +104,10 @@ namespace Radzen.Blazor
/// <inheritdoc />
protected override string GetComponentCssClass()
{
return GetClassList("rz-selectbutton rz-buttonset").Add($"rz-buttonset-{items.Count}").ToString();
}
=> GetClassList("rz-selectbutton rz-buttonset")
.Add($"rz-selectbutton-{(Orientation == Orientation.Vertical ? "vertical" : "horizontal")}")
.Add($"rz-buttonset-{items.Count}")
.ToString();
/// <summary>
/// Gets or sets a value indicating whether this <see cref="RadzenSelectBar{TValue}"/> is multiple.
@@ -180,7 +187,7 @@ namespace Radzen.Blazor
/// Selects the item.
/// </summary>
/// <param name="item">The item.</param>
protected async System.Threading.Tasks.Task SelectItem(RadzenSelectBarItem item)
protected async Task SelectItem(RadzenSelectBarItem item)
{
if (Disabled || item.Disabled)
return;
@@ -189,7 +196,7 @@ namespace Radzen.Blazor
{
var type = typeof(TValue).IsGenericType ? typeof(TValue).GetGenericArguments()[0] : typeof(TValue);
var selectedValues = Value != null ? QueryableExtension.ToList(((IEnumerable)Value).AsQueryable().Cast(type)) : new List<dynamic>();
var selectedValues = Value != null ? new List<dynamic>(((IEnumerable)Value).Cast<dynamic>()) : new List<dynamic>();
if (!selectedValues.Contains(item.Value))
{

View File

@@ -5,7 +5,6 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;
namespace Radzen.Blazor

View File

@@ -8,6 +8,6 @@
<div class="rz-helper-hidden-accessible">
<input type="checkbox" name="@Name" id="@Name" checked="@Value" value="@ValueAsString" tabindex="-1" aria-checked="@(Value.ToString().ToLowerInvariant())" @attributes=@InputAttributes>
</div>
<span class="rz-switch-circle@(Disabled ? " rz-disabled" : "")"></span>
<span class="rz-switch-circle@(Disabled ? " rz-disabled" : ReadOnly ? " rz-readonly" : "")"></span>
</div>
}

View File

@@ -15,6 +15,13 @@ namespace Radzen.Blazor
/// </example>
public partial class RadzenSwitch : FormComponent<bool>
{
/// <summary>
/// Gets or sets a value indicating whether is read only.
/// </summary>
/// <value><c>true</c> if is read only; otherwise, <c>false</c>.</value>
[Parameter]
public bool ReadOnly { get; set; }
/// <inheritdoc />
protected override string GetComponentCssClass()
{
@@ -35,7 +42,7 @@ namespace Radzen.Blazor
/// </summary>
public async System.Threading.Tasks.Task Toggle()
{
if (Disabled)
if (Disabled || ReadOnly)
{
return;
}

View File

@@ -0,0 +1,13 @@
@inherits RadzenComponentWithChildren
@if (Visible)
{
<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>
</div>
</div>
}

View File

@@ -0,0 +1,57 @@
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;
namespace Radzen.Blazor
{
/// <summary>
/// RadzenTable component.
/// </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;
/// <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();
}
/// <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);
}
}
}

View File

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

View File

@@ -0,0 +1,39 @@
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();
}
}
}
}

View File

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

View File

@@ -0,0 +1,40 @@
using Microsoft.AspNetCore.Components;
using System.Collections.Generic;
namespace Radzen.Blazor
{
/// <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);
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,39 @@
using Microsoft.AspNetCore.Components;
using System.Collections.Generic;
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();
}
}
}
}

View File

@@ -0,0 +1,9 @@
@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>
}

View File

@@ -0,0 +1,34 @@
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);
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,62 @@
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();
}
}
}
}

View File

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

View File

@@ -0,0 +1,68 @@
using Microsoft.AspNetCore.Components;
using System.Collections.Generic;
namespace Radzen.Blazor
{
/// <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();
}
}
}
}

View File

@@ -130,11 +130,20 @@ namespace Radzen.Blazor
}
}
private string GetPath()
{
var uri = new Uri(NavigationManager.Uri);
var anchor = GetAnchor();
return $"{uri.PathAndQuery}#{anchor}";
}
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.OpenElement(1, "a");
builder.AddAttribute(2, "name", GetAnchor());
builder.AddAttribute(3, "href", Path);
builder.AddAttribute(2, "id", GetAnchor());
builder.AddAttribute(3, "href", GetPath());
builder.AddAttribute(4, "class", "rz-link");
builder.AddAttribute(5, "target", "_top"); // To support relative links without the Blazor router interfering
builder.AddElementReferenceCapture(6, capture => element = capture);

View File

@@ -115,7 +115,10 @@ namespace Radzen.Blazor
/// </summary>
public void Dispose()
{
ThemeService.ThemeChanged -= OnThemeChanged;
if (ThemeService != null)
{
ThemeService.ThemeChanged -= OnThemeChanged;
}
persistingSubscription.Dispose();
}
}

View File

@@ -31,7 +31,7 @@ namespace Radzen.Blazor
.Add("rzi-caret-right", !clientExpanded)
.Add(Tree.ItemIconCssClass)
.Add(IconCssClass);
private ClassList LabelClassList => ClassList.Create("rz-treenode-label")
private ClassList LabelClassList => ClassList.Create("rz-treenode-label")
.Add(Tree.ItemLabelCssClass)
.Add(LabelCssClass);
/// <summary>
@@ -105,17 +105,17 @@ namespace Radzen.Blazor
[Parameter]
public IEnumerable Data { get; set; }
/// <summary>
/// Gets or sets the CSS classes added to the content.
/// <summary>
/// Gets or sets the CSS classes added to the content.
/// </summary>
[Parameter]
public string ContentCssClass { get; set; }
public string ContentCssClass { get; set; }
/// <summary>
/// Gets or sets the CSS classes added to the icon.
/// </summary>
[Parameter]
public string IconCssClass { get; set; }
public string IconCssClass { get; set; }
/// <summary>
/// Gets or sets the CSS classes added to the label.
@@ -157,7 +157,7 @@ namespace Radzen.Blazor
bool clientExpanded;
internal async Task Toggle()
{
if (expanded)
if (expanded && !Tree.SingleExpand)
{
clientExpanded = !clientExpanded;
@@ -229,7 +229,7 @@ namespace Radzen.Blazor
if (Tree.SingleExpand)
{
var siblings = ParentItem?.items ?? Tree.items;
var siblings = (ParentItem?.items ?? Tree.items).ToList();
foreach (var sibling in siblings)
{

View File

@@ -1,7 +1,7 @@
@using Radzen.Blazor
@using Radzen.Blazor.Rendering
@inherits SchedulerViewBase
@inherits SchedulerYearViewBase
@code {
public override RenderFragment Render()

View File

@@ -16,7 +16,7 @@ namespace Radzen.Blazor
/// &lt;/RadzenScheduler&gt;
/// </code>
/// </example>
public partial class RadzenYearPlannerView : SchedulerViewBase
public partial class RadzenYearPlannerView : SchedulerYearViewBase
{
/// <inheritdoc />
public override string Icon => "view_list";
@@ -91,7 +91,7 @@ namespace Radzen.Blazor
/// </summary>
/// <value>The start month.</value>
[Parameter]
public Month StartMonth { get; set; } = Month.January;
public override Month StartMonth { get; set; } = Month.January;
/// <inheritdoc />
public override DateTime Next()

View File

@@ -1,7 +1,7 @@
@using Radzen.Blazor
@using Radzen.Blazor.Rendering
@inherits SchedulerViewBase
@inherits SchedulerYearViewBase
@code {
public override RenderFragment Render()

View File

@@ -16,7 +16,7 @@ namespace Radzen.Blazor
/// &lt;/RadzenScheduler&gt;
/// </code>
/// </example>
public partial class RadzenYearTimelineView : SchedulerViewBase
public partial class RadzenYearTimelineView : SchedulerYearViewBase
{
/// <inheritdoc />
public override string Icon => "view_timeline";
@@ -91,7 +91,7 @@ namespace Radzen.Blazor
/// </summary>
/// <value>The start month.</value>
[Parameter]
public Month StartMonth { get; set; } = Month.January;
public override Month StartMonth { get; set; } = Month.January;
/// <inheritdoc />
public override DateTime Next()

View File

@@ -1,7 +1,7 @@
@using Radzen.Blazor
@using Radzen.Blazor.Rendering
@inherits SchedulerViewBase
@inherits SchedulerYearViewBase
@code {
public override RenderFragment Render()

View File

@@ -15,7 +15,7 @@ namespace Radzen.Blazor
/// &lt;/RadzenScheduler&gt;
/// </code>
/// </example>
public partial class RadzenYearView : SchedulerViewBase
public partial class RadzenYearView : SchedulerYearViewBase
{
/// <inheritdoc />
public override string Icon => "calendar_month";
@@ -89,7 +89,7 @@ namespace Radzen.Blazor
/// </summary>
/// <value>The start month.</value>
[Parameter]
public Month StartMonth { get; set; } = Month.January;
public override Month StartMonth { get; set; } = Month.January;
/// <inheritdoc />
public override DateTime Next()

View File

@@ -2,7 +2,7 @@
@inherits DropableViewBase
<div class="rz-view rz-day-view">
<div class="rz-view-header">
<div class="rz-view-header" @onclick=@(args => OnDayClick(@StartDate))>
<div class="rz-slot-hour-header"></div>
<div class="rz-slot-header">
@StartDate.ToString("ddd", Scheduler.Culture)
@@ -64,6 +64,13 @@
await Scheduler.SelectSlot(date, date.AddMinutes(MinutesPerSlot), AppointmentsInSlot(date, date.AddMinutes(MinutesPerSlot)));
}
async Task OnDayClick(DateTime day)
{
var dayStart = day.Date.Add(StartTime);
var dayEnd = day.Date.Add(EndTime);
await Scheduler.SelectDay(day.Date, AppointmentsInSlot(dayStart, dayEnd));
}
IDictionary<string, object> Attributes(DateTime date)
{
var attributes = Scheduler.GetSlotAttributes(date, date.AddMinutes(MinutesPerSlot));
@@ -84,7 +91,9 @@
var updown = key == "ArrowUp" || key == "ArrowDown";
var leftright = key == "ArrowLeft" || key == "ArrowRight";
currentAppointments = AppointmentsInSlot(CurrentDate, CurrentDate.AddDays(1));
var dayStart = CurrentDate.Date.Add(StartTime);
var dayEnd = CurrentDate.Date.Add(EndTime);
currentAppointments = AppointmentsInSlot(dayStart, dayEnd);
if (arrow && currentAppointments.Any())
{

View File

@@ -82,7 +82,7 @@
{
var dayOfWeek = StartDate.AddDays(days++);
<div @onclick="@(args => OnSlotClick(dayOfWeek))" ondragover="event.preventDefault();" @ondrop=@(args => @OnDrop(dayOfWeek)) @attributes=@Attributes(dayOfWeek)>
<div class="rz-slot-title @(dayOfWeek == CurrentDate ? " rz-state-focused" : "")">
<div class="rz-slot-title @(dayOfWeek == CurrentDate ? " rz-state-focused" : "")" @onclick=@(args => OnDayClick(dayOfWeek)) @onclick:stopPropagation>
@dayOfWeek.Day
</div>
</div>
@@ -125,6 +125,11 @@
await Scheduler.SelectSlot(date, date.AddDays(1), AppointmentsInSlot(date, date.AddDays(1)));
}
async Task OnDayClick(DateTime day)
{
await Scheduler.SelectDay(day, AppointmentsInSlot(day, day.AddDays(1)));
}
double DetermineTop(HashSet<double> existingTops)
{
var top = 1.5;

View File

@@ -8,7 +8,8 @@
<div class="rz-slot-hour-header"></div>
@for (var day = StartDate; day < EndDate; day = day.AddDays(1))
{
<div class="rz-slot-header">
var loopDay = day;
<div class="rz-slot-header" @onclick=@(args => OnDayClick(@loopDay))>
@day.ToString(HeaderFormat, Scheduler.Culture)
</div>
}
@@ -78,6 +79,13 @@
await Scheduler.SelectSlot(date, date.AddMinutes(MinutesPerSlot), AppointmentsInSlot(date, date.AddMinutes(MinutesPerSlot)));
}
async Task OnDayClick(DateTime day)
{
var dayStart = day.Date.Add(StartTime);
var dayEnd = day.Date.Add(EndTime);
await Scheduler.SelectDay(day.Date, AppointmentsInSlot(dayStart, dayEnd));
}
IDictionary<string, object> Attributes(DateTime date)
{
var attributes = Scheduler.GetSlotAttributes(date, date.AddMinutes(MinutesPerSlot));

View File

@@ -95,7 +95,7 @@
</div>
<div class="rz-slots">
<div @attributes=@Attributes(realstart, "rz-slot", false)>
<div class="rz-slot-header" style="cursor: pointer;" @onclick=@(args => OnMonthClick(startMonth))>
<div class="rz-slot-header" @onclick=@(args => OnMonthClick(startMonth))>
@realstart.ToString("MMM", Scheduler.Culture)
</div>
</div>
@@ -126,7 +126,7 @@
}
}
<div @attributes=@Attributes(realstart, "rz-slot", false)>
<div class="rz-slot-header" style="cursor: pointer;" @onclick=@(args => OnMonthClick(startMonth))>
<div class="rz-slot-header" @onclick=@(args => OnMonthClick(startMonth))>
@realstart.ToString("MMM", Scheduler.Culture)
</div>
</div>

View File

@@ -81,22 +81,21 @@
}
}
}
@if (excessCount > 0)
{
var slotIndex = start.Subtract(date).Days;
var slotWidth = (SLOT_WIDTH / CALENDAR_WIDTH) * 100;
var left = ((slotIndex + 1) * slotWidth) + (100.0 / 85.0);
var top = ((MaxAppointmentsInSlot + 1) * 1.5) + 1.7;
var listDate = start;
<a class="rz-event-list-btn" style="top: @(top.ToInvariantString())em; left: @(left.ToInvariantString())%" @onclick=@(args => OnListClick(listDate, appointments))>@String.Format(MoreText, excessCount)</a>
@if (excessCount > 0)
{
var slotIndex = start.Subtract(date).Days;
var slotWidth = (SLOT_WIDTH / CALENDAR_WIDTH) * 100;
var left = ((slotIndex + 1) * slotWidth) + (100.0 / 85.0);
var top = ((MaxAppointmentsInSlot + 1) * 1.5) + 1.7;
var listDate = start;
<a class="rz-event-list-btn" style="top: @(top.ToInvariantString())em; left: @(left.ToInvariantString())%" @onclick=@(args => OnListClick(listDate, appointments))>@String.Format(MoreText, excessCount)</a>
}
}
}
</div>
<div class="rz-slots">
<div @attributes=@Attributes(realstart, "rz-slot", false)>
<div class="rz-slot-header" style="cursor: pointer;" @onclick=@(args => OnMonthClick(startMonth))>
<div class="rz-slot-header" @onclick=@(args => OnMonthClick(startMonth))>
@realstart.ToString("MMMM", Scheduler.Culture)
</div>
</div>

View File

@@ -32,7 +32,7 @@
tabindex="0" @onkeydown="@(args => OnKeyPress(args))" @onkeydown:preventDefault="@preventKeyPress" @onkeydown:stopPropagation
@onfocus=@(args => {CurrentDate = currentDate; CurrentMonth = currentMonth; })>
<div class="rz-slot-header rz-pb-2" style="cursor: pointer;" @onclick=@(args => OnMonthClick(startMonth))>
<div class="rz-slot-header rz-pb-2" @onclick=@(args => OnMonthClick(startMonth))>
<span class="rz-text-subtitle1">
@realstart.ToString("MMMM yyyy", Scheduler.Culture)
</span>

View File

@@ -89,6 +89,11 @@ namespace Radzen.Blazor
/// <param name="round">Wether to round.</param>
public double NiceNumber(double range, bool round)
{
if (range == 0)
{
return 1;
}
var sign = Math.Sign(range);
range = Math.Abs(range);
var exponent = Math.Floor(Math.Log10(range));

View File

@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using Radzen.Blazor;
namespace Radzen
{
/// <summary>
/// Supplies information about a <see cref="RadzenScheduler{TItem}.DaySelect" /> event that is being raised.
/// </summary>
public class SchedulerDaySelectEventArgs
{
/// <summary>
/// Selected date.
/// </summary>
public DateTime Day { get; set; }
/// <summary>
/// List of appointments.
/// </summary>
public IEnumerable<AppointmentData> Appointments { get; set; }
/// <summary>
/// Current View.
/// </summary>
public ISchedulerView View { get; set; }
}
}

View File

@@ -16,5 +16,10 @@ namespace Radzen
/// The start of the currently rendered period.
/// </summary>
public DateTime End { get; set; }
/// <summary>
/// The selected view of the scheduler.
/// </summary>
public ISchedulerView View { get; set; }
}
}

View File

@@ -10,7 +10,7 @@ namespace Radzen
public class SchedulerMonthSelectEventArgs
{
/// <summary>
/// Monthg start date. You can change this value to navigate to a different date.
/// Month start date.
/// </summary>
public DateTime MonthStart { get; set; }
/// <summary>

View File

@@ -0,0 +1,36 @@
using System;
using Radzen;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
namespace Radzen.Blazor
{
/// <summary>
/// A base class for <see cref="RadzenScheduler{TItem}" /> views.
/// </summary>
public abstract class SchedulerYearViewBase : SchedulerViewBase
{
/// <summary>
/// Gets the StartMonth of the view.
/// </summary>
/// <value>The start month.</value>
public abstract Month StartMonth { get; set; }
/// <summary>
/// Called by the Blazor runtime when parameters are set.
/// </summary>
/// <param name="parameters">The parameters.</param>
public override async Task SetParametersAsync(ParameterView parameters)
{
if (parameters.DidParameterChange(nameof(StartMonth), StartMonth))
{
if (Scheduler != null)
{
await Scheduler.Reload();
}
}
await base.SetParametersAsync(parameters);
}
}
}

View File

@@ -184,7 +184,7 @@ namespace Radzen
Text = "Material",
Value = "material",
Primary = "#4340d2",
Secondary = "#e91e63",
Secondary = "#e31c65",
Base = "#f5f5f5",
Selection = "rgba(67, 64, 210, 0.12)",
SelectionText = "#4340d2",

View File

@@ -27,6 +27,7 @@ $checkbox-tri-icon-font-size: $checkbox-icon-font-size !default;
.rz-checkbox-list-vertical {
box-sizing: border-box;
border-radius: var(--rz-border-radius);
.rz-checkbox {
display: flex;
@@ -39,6 +40,7 @@ $checkbox-tri-icon-font-size: $checkbox-icon-font-size !default;
.rz-checkbox-list-horizontal {
box-sizing: border-box;
border-radius: var(--rz-border-radius);
.rz-checkbox {
display: inline-flex;
@@ -65,6 +67,7 @@ $checkbox-tri-icon-font-size: $checkbox-icon-font-size !default;
width: var(--rz-checkbox-width);
min-width: var(--rz-checkbox-width);
height: var(--rz-checkbox-height);
border-radius: var(--rz-checkbox-border-radius);
&:focus {
outline: var(--rz-outline-normal);

View File

@@ -3,6 +3,7 @@
height: 0;
input {
width: 100%;
height: 0;
padding: 0;
margin: 0;

View File

@@ -61,6 +61,11 @@ $timepicker-border: none !default;
.rz-datepicker {
display: inline-block;
position: relative;
border-radius: var(--rz-input-border-radius);
&:has(>.rz-inputtext:not(:disabled):not(.rz-state-disabled):focus) {
outline: none; // unify .invalid styles with other forms components
}
.rz-readonly {
cursor: pointer;
@@ -70,6 +75,9 @@ $timepicker-border: none !default;
@extend %input;
width: 100%;
line-height: var(--rz-datepicker-line-height);
}
&:has(.rz-datepicker-trigger) > .rz-inputtext {
padding-inline-end: calc(1rem + var(--rz-datepicker-trigger-icon-width));
}

View File

@@ -360,7 +360,8 @@ $form-field-helper-padding: 0 0.5rem !default;
.rz-checkbox-list-horizontal ~ &,
.rz-chkbox ~ &,
.rz-fileupload ~ &,
.rz-state-empty:has(.rz-placeholder) ~ & {
.rz-state-empty:has(.rz-placeholder) ~ &,
.rz-form-field.rz-state-focused & {
inset-inline-end: auto;
inset-block-start: var(--rz-form-field-label-floating-top);
padding-block-start: 0;
@@ -383,7 +384,8 @@ $form-field-helper-padding: 0 0.5rem !default;
.rz-form-field:not(.rz-variant-outlined) .rz-checkbox-list-horizontal ~ &,
.rz-form-field:not(.rz-variant-outlined) .rz-chkbox ~ &,
.rz-form-field:not(.rz-variant-outlined) .rz-fileupload ~ &,
.rz-form-field:not(.rz-variant-outlined) .rz-state-empty:has(.rz-placeholder) ~ & {
.rz-form-field:not(.rz-variant-outlined) .rz-state-empty:has(.rz-placeholder) ~ &,
.rz-form-field:not(.rz-variant-outlined).rz-state-focused & {
background-color: inherit !important;
}
@@ -393,7 +395,8 @@ $form-field-helper-padding: 0 0.5rem !default;
.rz-state-focused &,
.rz-variant-filled.rz-state-focused &,
.rz-variant-flat.rz-state-focused & {
.rz-variant-flat.rz-state-focused &,
.rz-form-field.rz-state-focused & {
color: var(--rz-form-field-label-focus-color);
}

View File

@@ -670,7 +670,8 @@ $column-draggable-shadow: 0 8px 10px 0 rgba(0, 0, 0, 0.1) !default;
}
}
&.rz-frozen-cell-right {
&.rz-frozen-cell-right,
&.rz-frozen-cell-right-inner {
&:before {
z-index: -2;
background-color: var(--rz-grid-frozen-cell-background-color);
@@ -693,7 +694,8 @@ $column-draggable-shadow: 0 8px 10px 0 rgba(0, 0, 0, 0.1) !default;
}
}
&.rz-frozen-cell-right {
&.rz-frozen-cell-right,
&.rz-frozen-cell-right-inner {
&:after {
background-color: var(--rz-grid-selected-background-color);
}
@@ -722,7 +724,8 @@ $column-draggable-shadow: 0 8px 10px 0 rgba(0, 0, 0, 0.1) !default;
}
}
&.rz-frozen-cell-right {
&.rz-frozen-cell-right,
&.rz-frozen-cell-right-inner {
&:after {
background-color: var(--rz-grid-cell-focus-background-color);
color: var(--rz-grid-cell-focus-color);
@@ -760,7 +763,8 @@ $column-draggable-shadow: 0 8px 10px 0 rgba(0, 0, 0, 0.1) !default;
}
}
&.rz-frozen-cell-right {
&.rz-frozen-cell-right,
&.rz-frozen-cell-right-inner {
&:after {
background-color: var(--rz-grid-hover-background-color);
}

View File

@@ -85,7 +85,9 @@ input {
font-family: inherit;
font-size: var(--rz-input-font-size);
transition: var(--rz-input-transition);
outline: none;
&:not(.invalid) {
outline: none;
}
@extend %input-base;

View File

@@ -23,6 +23,7 @@ $radio-checked-border: var(--rz-input-border) !default;
.rz-radio-button-list-vertical {
box-sizing: border-box;
border-radius: var(--rz-border-radius);
.rz-radio-btn {
display: flex;
@@ -34,6 +35,7 @@ $radio-checked-border: var(--rz-input-border) !default;
.rz-radio-button-list-horizontal {
box-sizing: border-box;
border-radius: var(--rz-border-radius);
.rz-radio-btn {
display: inline-flex;

View File

@@ -13,6 +13,7 @@ $rating-ban-icon-color: $rating-color !default;
display: inline-flex;
font-size: var(--rz-rating-font-size);
height: 1em;
border-radius: var(--rz-border-radius);
&.rz-state-disabled {
.rzi {
@@ -22,6 +23,7 @@ $rating-ban-icon-color: $rating-color !default;
}
a {
display: inline-flex;
width: 1em;
height: 1em;
text-decoration: none;
@@ -46,7 +48,8 @@ $rating-ban-icon-color: $rating-color !default;
opacity: var(--rz-rating-opacity);
&:before {
content: "star_border";
content: "star";
font-variation-settings: "FILL" 0;
}
}
@@ -55,35 +58,25 @@ $rating-ban-icon-color: $rating-color !default;
&:before {
content: "star";
font-variation-settings: "FILL" 1;
}
}
&:not(.rz-state-disabled):not(.rz-state-readonly) {
a:hover {
a:hover, a:focus-visible {
cursor: pointer;
.rzi-star,
.rzi-star-o,
.rzi-ban {
color: var(--rz-rating-selected-color);
}
.rzi-star-o:before {
content: "star";
color: var(--rz-rating-focus-color);
}
}
a:focus-visible {
.rzi-star,
.rzi-star-o,
.rzi-ban {
color: var(--rz-rating-focus-color);
}
.rzi-star-o:before {
content: "star";
}
a:focus-visible > span {
outline: var(--rz-outline-focus);
border-radius: var(--rz-border-radius)
}
}
}

View File

@@ -8,6 +8,7 @@ $rz-security-code-input-line-height: 1 !default;
.rz-security-code {
box-sizing: border-box;
display: inline-flex;
border-radius: var(--rz-input-border-radius);
}
.rz-security-code-wrapper {

View File

@@ -11,6 +11,15 @@ $selectbar-sizes: xs, sm, md, lg;
.rz-selectbutton {
box-sizing: border-box;
display: inline-flex;
border-radius: var(--rz-selectbar-border-radius);
&.rz-selectbutton-horizontal {
flex-direction: row;
}
&.rz-selectbutton-vertical {
flex-direction: column;
}
.rz-button {
&:focus-visible {
@@ -19,30 +28,58 @@ $selectbar-sizes: xs, sm, md, lg;
}
}
@each $size in $selectbar-sizes { //.rz-selectbutton .rz-button.rz-button-md
.rz-selectbutton .rz-button.rz-button-#{$size} {
margin-inline-start: -1px;
display: inline-block;
background-color: var(--rz-selectbar-background-color);
color: var(--rz-selectbar-color);
border: var(--rz-selectbar-border);
border-radius: 0;
&:first-child {
margin-inline-start: 0;
border-start-start-radius: var(--rz-selectbar-border-radius);
border-end-start-radius: var(--rz-selectbar-border-radius);
@each $size in $selectbar-sizes {
.rz-selectbutton {
.rz-button.rz-button-#{$size} {
display: inline-block;
background-color: var(--rz-selectbar-background-color);
color: var(--rz-selectbar-color);
border: var(--rz-selectbar-border);
border-radius: 0;
&.rz-state-active {
background-color: var(--rz-selectbar-selected-background-color);
color: var(--rz-selectbar-selected-color);
border: var(--rz-selectbar-selected-border);
}
}
&:last-child {
border-start-end-radius: var(--rz-selectbar-border-radius);
border-end-end-radius: var(--rz-selectbar-border-radius);
&.rz-selectbutton-horizontal {
.rz-button.rz-button-#{$size} {
&:first-child {
border-start-start-radius: var(--rz-selectbar-border-radius);
border-end-start-radius: var(--rz-selectbar-border-radius);
}
&:not(:first-child) {
border-inline-start: none;
}
&:last-child {
border-start-end-radius: var(--rz-selectbar-border-radius);
border-end-end-radius: var(--rz-selectbar-border-radius);
}
}
}
&.rz-state-active {
background-color: var(--rz-selectbar-selected-background-color);
color: var(--rz-selectbar-selected-color);
border: var(--rz-selectbar-selected-border);
&.rz-selectbutton-vertical {
.rz-button.rz-button-#{$size} {
text-align: center;
&:first-child {
border-top-left-radius: var(--rz-selectbar-border-radius);
border-top-right-radius: var(--rz-selectbar-border-radius);
}
&:not(:first-child) {
border-top: none;
}
&:last-child {
border-bottom-left-radius: var(--rz-selectbar-border-radius);
border-bottom-right-radius: var(--rz-selectbar-border-radius);
}
}
}
}
}

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