Compare commits

...

463 Commits

Author SHA1 Message Date
Vladimir Enchev
ccbf7b24cf Version updated 2025-02-26 09:17:56 +02:00
nielsNocore
57f1417d4c Fixed datagrid sort issues due to duplicate sortDescriptor lists. (#1996)
* refactor datagrid sorting. to only have one ObservableCollection<SortDescriptor instead of 2

* Revert "refactor datagrid sorting. to only have one ObservableCollection<SortDescriptor instead of 2"

This reverts commit 02ef67073c.

* added sort changes without formatting

* added new unit test.

* added datagrid test with load data args.
2025-02-26 09:15:42 +02:00
Atanas Korchev
d5318e6e88 Scheduler does not show some appointments. Fixes #1995. 2025-02-26 08:51:43 +02:00
Vladimir Enchev
978e3fc610 version updated 2025-02-25 10:42:59 +02:00
Vladimir Enchev
dcb5eef814 DropDown/DropDownDataGrid case-insensitive filter cannot be translated by PG SQL provider 2025-02-25 10:42:30 +02:00
Atanas Korchev
518ad838e7 Add Select extension method for IQueryable. 2025-02-24 17:42:33 +02:00
Vladimir Enchev
ba6ced577e minor version updated 2025-02-24 16:22:37 +02:00
Vladimir Enchev
138253e728 Version updated 2025-02-24 16:21:02 +02:00
yordanov
ffd299962b Fix alternating rows color in Fluent themes 2025-02-24 16:16:52 +02:00
Vladimir Enchev
354a147155 GetBodyColumnClass and GetHeaderColumnClass renamed and made internal 2025-02-24 16:00:04 +02:00
Pedro Constantino
a623017325 [IMPROVEMENT] - Adding HeaderWhiteSpaceText and WhiteSpaceText properties to RadzenDataGridColumn. (#1999)
* [IMPROVEMENT] - Adding HeaderWhiteSpaceText and WhiteSpaceText properties to RadzenDataGridColumn.

* [IMPROVEMENT] - Improving the css and readjusting the themes folder.

* [IMPROVEMENT] - Adjusting enumerator documentation.

* [IMPROVEMENT] - Adjusting component documentation

* [IMPROVMENT] - Renaming WhiteSpace and HeaderWhiteSpace properties, renaming WhiteSpace enumerator and removing identation from csproj.

---------

Co-authored-by: Pedro Constantino <pedro.constantino@enerwatt.com.br>
2025-02-24 15:54:27 +02:00
Vladimir Enchev
654bd80abd missing cast added 2025-02-22 12:29:20 +02:00
Vladimir Enchev
3249591351 EnumerableAsString() will handle single values 2025-02-22 11:41:01 +02:00
Vladimir Enchev
40f934d7a6 version updated 2025-02-21 20:07:25 +02:00
Vladimir Enchev
53717dcc46 DataGrid CheckBoxList filter list cast exception fixed
Fix #1998
2025-02-21 20:06:59 +02:00
Vladimir Enchev
f16a00fe2e Fixed exception with DataGrid sorting and property name equal to System Type name
Obsolete code related to Dynamic LINQ deleted
2025-02-21 12:55:18 +02:00
Vladimir Enchev
ab1af0ce28 Version updated 2025-02-21 08:37:58 +02:00
Vladimir Enchev
be444bbbc4 ToFilterString() should export Boolean arrays as valid C# 2025-02-21 08:37:33 +02:00
Vladimir Enchev
e161872604 Fixed Tree is breaking after collapse
Fix ##1983
2025-02-20 18:27:34 +02:00
Vladimir Enchev
e21fa015bf version updated 2025-02-20 12:19:46 +02:00
Vladimir Enchev
fb3275d89f Fixed OrderBy() with complex lambdas 2025-02-20 12:18:05 +02:00
Monsieurvor
f42e4ffc71 fix linear scale step (#1989) 2025-02-20 11:24:39 +02:00
Vladimir Enchev
7b15c190d3 Revert "Fix OnSideClose being called on open (#1977)"
This reverts commit 75da4667b7.
2025-02-20 11:12:05 +02:00
Vladimir Enchev
73c2d27620 Saved DataGrid filter with CheckBoxList filter type will raise exception on settings load 2025-02-20 11:00:53 +02:00
nielsNocore
288db125ce added GetSelectedSources and GetSelectedTargets in the picklist to get wich items are selected. Or to get if any item is selected. (#1982) 2025-02-20 05:01:52 +02:00
Victor Ureta
0effaae435 Update RadzenDropDownDataGrid.razor.cs (#1985) 2025-02-20 05:00:34 +02:00
Vladimir Enchev
a71f1dd3c6 Version updated 2025-02-19 18:42:31 +02:00
Vladimir Enchev
c08843d587 Revert "Update RadzenDatePicker.razor.cs (#1980)"
This reverts commit b16f95a4ba.
2025-02-19 18:42:09 +02:00
Vladimir Enchev
44094cd4f4 comments fixed 2025-02-19 18:09:44 +02:00
Vladimir Enchev
a4bd9d518c Version updated 2025-02-19 17:58:41 +02:00
Vladimir Enchev
a739a6e24a Generic OrderBy<T>() method by string selector fixed to support lambda 2025-02-19 17:58:20 +02:00
Vladimir Enchev
955b52d54b duplicate test removed 2025-02-19 15:25:48 +02:00
Atanas Korchev
004293008c ExpressionParser supports typed arrays. 2025-02-19 15:23:28 +02:00
Vladimir Enchev
9f26a0b512 Should_SupportNullableCollection test added 2025-02-19 15:07:39 +02:00
CreateCode
b16f95a4ba Update RadzenDatePicker.razor.cs (#1980)
In some cultures, such as Portuguese (Portugal), the name of the day of the week appears in full, overlapping one another. This change forces the abbreviation of the name of the day of the week.
2025-02-19 15:03:41 +02:00
Vladimir Enchev
c079531d2e Fixed DataGrid CheckBoxFilter values not sorted 2025-02-19 15:00:24 +02:00
Atanas Korchev
773f2dc074 ExpressionParser supports Enumerable extension methods with IEnumerable parameters. 2025-02-19 11:35:56 +02:00
Vladimir Enchev
64d783179b Should_SupportNestedLambdasWithComplexMethod test added 2025-02-19 10:48:58 +02:00
Vladimir Enchev
3dafd6c277 Version updated 2025-02-19 09:34:05 +02:00
Vladimir Enchev
fb2fdb10b2 OrderBy() with string lambda support fixed 2025-02-19 09:33:49 +02:00
Vladimir Enchev
50dedf84d5 Where() parameters conversion between string and actual type fixed 2025-02-19 08:56:30 +02:00
Vladimir Enchev
d7b96a7dca Where() with Guid parameter fixed 2025-02-19 08:04:43 +02:00
Vladimir Enchev
1d1acdf7c8 Select improved 2025-02-18 23:14:12 +02:00
Vladimir Enchev
440bc76f08 Version updated 2025-02-18 23:10:49 +02:00
Vladimir Enchev
d2a598dbf5 Fixed DataGrid sub properties invalid expressions 2025-02-18 23:10:17 +02:00
Atanas Korchev
931f9dab6f ExpressionParser supports nested property name initializers. 2025-02-18 20:39:29 +02:00
Atanas Korchev
10ec2af398 ExpressionParser supports conditional expressions with different type. 2025-02-18 20:11:13 +02:00
Atanas Korchev
46382ef89f ExpressionParser supports DateTimeOffset.Parse. 2025-02-18 19:57:10 +02:00
Atanas Korchev
c4116feda4 Expression parsing supports the ?? operator. 2025-02-18 19:42:05 +02:00
Vladimir Enchev
4c18342cf4 Version updated 2025-02-18 17:45:21 +02:00
Atanas Korchev
92f103c1dd Support nested conditional access. 2025-02-18 17:19:02 +02:00
Vladimir Enchev
8c9374e00a Version updated 2025-02-18 11:36:53 +02:00
Vladimir Enchev
870f3d2ef9 Select expression should not create resulting type with dot in the property name 2025-02-18 11:34:51 +02:00
Vladimir Enchev
d75b3596aa ToFilterString() should convert enumerable first 2025-02-18 10:38:52 +02:00
Vladimir Enchev
c388d84f57 Version updated 2025-02-18 09:57:45 +02:00
Vladimir Enchev
782f6c9655 DataGrid FilterProperty should be used instead Property only if Property is not enumerable 2025-02-18 09:56:20 +02:00
Vladimir Enchev
078d12f2a1 DataGrid Guid column type with ToFilterStrng() support added 2025-02-18 09:42:33 +02:00
Vladimir Enchev
271e1aa26a DataGrid TimeOnly column type with ToFilterStrng() support added 2025-02-18 09:34:01 +02:00
Vladimir Enchev
c9c11c7ff7 Version updated 2025-02-18 07:51:22 +02:00
Caleb Weeks
75da4667b7 Fix OnSideClose being called on open (#1977)
Co-authored-by: Caleb Weeks <seth.weeks@carrier.com>
2025-02-18 07:50:37 +02:00
Vladimir Enchev
a2591f7f77 DataGrid FilterProperty should be used instead Property if defined 2025-02-17 19:38:13 +02:00
Vladimir Enchev
72b4e8bd39 Fixed DataGrid filtering on nullable string column will throw ReferenceNullException
Fix #1975
2025-02-17 19:05:06 +02:00
Vladimir Enchev
740b0bc3b1 Version updated 2025-02-17 16:58:30 +02:00
Vladimir Enchev
3e48b15db0 RadzenDataGridColumn FilterOperator="FilterOperator.Equals" in case of Type="typeof(string)"
Fix #1974
2025-02-17 16:56:36 +02:00
Vladimir Enchev
f1b8a22cc8 Fixed DataGrid CheckBox filtering with dynamic data 2025-02-17 16:36:03 +02:00
Pedro Constantino
02b75fcb68 [IMPROVEMENT] - Adding new option to disable radzen link, css and test added. (#1970)
* [IMPROVEMENT] - Adding new option to disable radzen link, css and test added.

* [IMPROVEMENT] - Leaving the title with lowercase letter.

* [IMPROVEMENT] - Removing version items.

---------

Co-authored-by: Pedro Constantino <pedro.constantino@enerwatt.com.br>
2025-02-17 15:10:06 +02:00
Vladimir Enchev
9b2541975f comment fixed 2025-02-17 14:38:29 +02:00
Vladimir Enchev
d3ff9a5b7c Version updated 2025-02-17 13:33:05 +02:00
Atanas Korchev
75acf7d132 Support prefix ! operator in expressions. 2025-02-17 13:27:59 +02:00
Vladimir Enchev
d6a9430c20 PrefixUnaryExpressionSyntax test added 2025-02-17 13:22:14 +02:00
Vladimir Enchev
5a89720f59 DropDown filtering by non string property fixed 2025-02-17 13:16:14 +02:00
Vladimir Enchev
bee7133e81 Fixed exception with DropDown filtering by non strong property 2025-02-17 13:01:28 +02:00
Vladimir Enchev
8522f88d66 Fixed Dropdown Filter Not Working When Binding List to Struct Class
Fix #1973
2025-02-17 12:56:57 +02:00
Vladimir Enchev
10ecc5c75d Fixed RadzenDataGrid in simple filter mode shows the date filter value at the bottom of the screen
Fix #1971
2025-02-17 12:24:43 +02:00
Atanas Korchev
cdd722c975 Refactor the ExpressionParser. 2025-02-17 11:56:01 +02:00
Vladimir Enchev
7fd9b258aa InnerException added as well 2025-02-17 11:15:20 +02:00
Vladimir Enchev
5368d398fe Where() and Select() entire exception exposed 2025-02-17 11:14:00 +02:00
Vladimir Enchev
30cc8c8711 missing comments added 2025-02-17 10:49:50 +02:00
Vladimir Enchev
f7d1fea2af Version updated 2025-02-17 10:45:30 +02:00
Vladimir Enchev
ee5674bb6d Where() and Select() methods from strings expression parsing improved (#1972)
* Add ExpressionParser and tests.

* Should_SupportNullableShortParsing test added

* Support conversion for binary operations

* Should_SupportNullablePropertiesWithArray test added

* Should_SupportDateTimeWithArray added

* code fixed

* Compilation moved to Select() only

* DataGrid columns filtering temporary switched to string expressions

* typeLocator added

* Convert arguments if needed.

* Should_SupportNestedLambdasWithEnums added

* locator improved for nested types

* ToFilterString() will not cast enums

* expression fixed

* Revert "demo reworked without strings"

This reverts commit 5e1fa61c55.

* ToFilterString() improved

* Support projections.

* ExpressionParser.ParseProjection added

* code improved

* Select by string.

* Support conditional access expressions.

* null condition added

* should add item instance name only to non indexer properties

* Array and dictionary access.

* Add editorconfig.

* Dynamic property еxpression updated to cast

* ToFilterString() removed

* Where() and Select() exception handling improved

---------

Co-authored-by: Atanas Korchev <akorchev@gmail.com>
2025-02-17 10:42:58 +02:00
Vladimir Enchev
5e1fa61c55 demo reworked without strings 2025-02-15 18:34:36 +02:00
Vladimir Enchev
92f968f933 obsolete code deleted 2025-02-15 11:07:53 +02:00
Vladimir Enchev
9eabd75864 Fixed other items not collapsed with Tree SingleExpand=true 2025-02-15 11:02:17 +02:00
Vladimir Enchev
df121e68b4 OrderBy() should return IOrderedQueryable<T> 2025-02-14 19:01:30 +02:00
Vladimir Enchev
be0721fa5d version updated 2025-02-14 18:33:36 +02:00
Vladimir Enchev
d670872d73 ToFIlterString() should output bool with proper casing 2025-02-14 18:20:40 +02:00
Vladimir Enchev
68eb9162a9 Fixed DataGrid Custom Column Filter Error With FilterCaseSensitivity="FilterCaseSensitivity.CaseInsensitive"
Fix #1969
2025-02-14 17:58:04 +02:00
Vladimir Enchev
a65ce23a10 version updated 2025-02-14 16:04:31 +02:00
Vladimir Enchev
bea9bca891 Fixed DataGrid filtering by Contains() using second filter with or 2025-02-14 16:03:31 +02:00
Vladimir Enchev
9f2c6fe8cc Fixed Empty DataGrid with FilterOperator="FilterOperator.In" and Type="typeof(IEnumerable<Enum>)">
Fix #1968
2025-02-14 15:58:06 +02:00
Vladimir Enchev
07e4276190 Accordion Refresh() method made public. 2025-02-14 15:24:41 +02:00
Vladimir Enchev
3db15c168d Version updated 2025-02-14 14:51:36 +02:00
Vladimir Enchev
7e9f66ec61 Fixed exception with filtering by string columns with null values 2025-02-14 14:51:09 +02:00
Vladimir Enchev
c7ce4ec4e3 Fixed DataGrid filtering with CustomFilterExpression column expression 2025-02-14 14:38:12 +02:00
Vladimir Enchev
ad32aed204 Fixed DataGrid grouping broken for structs
Fix #1967
2025-02-14 13:08:23 +02:00
Nopke
2df9235516 fix a test that fails or succeeds depending on the user's culture (#1963) 2025-02-14 12:09:31 +02:00
Vladimir Enchev
a0efb0b9a4 Select() should handle nested types 2025-02-14 11:38:12 +02:00
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
Vladimir Enchev
c1797cc215 Version updated 2024-12-27 04:16:26 +02:00
jakubiszon
00eb31cd88 refactor(demos): simplify DataGridColumnPicking example (#1883) 2024-12-27 07:18:58 +07:00
AndreikaKanareika
bd4b1e485b Added ShowHeader parameter for RadzenDataGrid component (#1882) 2024-12-26 17:28:48 +07:00
Vladimir Enchev
387eacf5ff Steps aria-label attribute fixed
Fix #1877
2024-12-25 03:39:42 +02:00
Vladimir Enchev
5d00e79e0b Catched possible sporadic exception invoking 'RadzenTooltip.CloseTooltip'
Fixed #1871
2024-12-20 12:21:05 +02:00
yordanov
3aac6a785d End-of-year promotion CHEERS2025 2024-12-20 09:11:00 +02:00
Christian Storb
3506256dff Prevent decimal insertion in integer-only fields (#1868)
Added a condition to check if the value is not an integer before allowing the insertion of a decimal separator when the 'NumpadDecimal' key is pressed. This ensures that decimal points are not inserted in fields restricted to integer values, maintaining their integrity.

Co-authored-by: Christian Storb <c.storb@wibutler.io>
2024-12-19 18:05:31 +02:00
Atanas Korchev
5c49ab32e5 Sort appointments by length greater than one day. Fixes #1864. 2024-12-19 11:24:39 +02:00
Vladimir Enchev
f128209c3a Version updated 2024-12-19 09:28:40 +02:00
Nenad Kovačević
4a4254847a Fixed a typo in cookie name in ThemeServicePage.razor (#1865) 2024-12-19 09:28:04 +02:00
Vladimir Enchev
d21697367c Fixed DataGrid CheckBoxList filtering with LoadData for enums 2024-12-19 09:24:23 +02:00
Vladimir Enchev
1ceaab2788 Accordion item Disabled property added
Close #1861
2024-12-18 15:51:39 +02:00
Paul Ruston
0c56f20f55 New Property - RadzenMultiDayView->AdvanceDays (#1855) 2024-12-17 11:06:08 +02:00
Vladimir Enchev
261339c7e9 Fixed DataGrid CheckBoxList filtering with LoadData and In/NotIn operator 2024-12-16 10:02:10 +02:00
Vladimir Enchev
1d1335fed5 Version updated 2024-12-16 09:11:53 +02:00
Josh
c9b5a53be3 Introduce ItemComparer parameter to DropDownBase, for IEqualityComparer support (#1854)
* Introduce ItemComparer parameter to DropDownBase. Use HashSet to track selectedItems.

* include summary for ItemComparer Parameter
2024-12-16 09:09:53 +02:00
Reinhard
11a037aeec Add customizable RadzenTreeItem css classes (#1856)
- Add ContentCssClass to RadzenTreeItem
- Add IconCssClass to RadzenTreeItem
- Add LabelCssClass to RadzenTreeItem
- Add ItemContentCssClass to RadzenTree
- Add ItemIconCssClass to RadzenTree
- Add ItemLabelCssClass to RadzenTree
2024-12-16 09:02:05 +02:00
yordanov
0743bb5f54 Fix Scheduler Planner and Timeline view z-index. Resolves #1815 2024-12-13 16:39:13 +02:00
yordanov
9fe5163d86 Add responsive utility classes for sizing, overflow, and alignment in flexbox. Resolves #1756 2024-12-13 16:16:57 +02:00
Paul Ruston
acd107b1a2 Scheduler New View - MultiDay (#1852)
* Scheduler New View - MultiDay

* Clean up. Remove comment marker
2024-12-13 14:47:17 +02:00
kerajel
1ff96df224 RadzenDataFilter.DrawNumericFilter to use ValueChanged (#1850)
Co-authored-by: Dmitrii Botov <dmitrii.botov@rokolabs.com>
2024-12-12 09:09:27 +02:00
Vladimir Enchev
fb537c46d4 Fixed Scheduler exception when navigating away 2024-12-11 18:32:41 +02:00
Vladimir Enchev
a588b454ae Version updated 2024-12-11 17:00:15 +02:00
Vladimir Enchev
0f145800fa DataGrid will close column filter after open in case of hidden column runtime 2024-12-11 16:59:08 +02:00
Vladimir Enchev
44dfa72f16 PickList SelectAllText added
Fix #1832
2024-12-11 09:00:40 +02:00
Vladimir Enchev
19152f498c Version updated 2024-12-10 14:05:14 +02:00
Vladimir Enchev
056a61c9fe Numeric stringValue set for OnChange as well 2024-12-10 10:56:57 +02:00
Vladimir Enchev
a0cfc5d267 Fixed Numeric component input lost in some cases 2024-12-10 10:11:24 +02:00
Vladimir Enchev
eb6dbf0c67 DataFilter numeric input errors handled 2024-12-09 16:43:44 +02:00
Vladimir Enchev
af2083120e DataFilter oninput added for numeric filtering (#1846)
code improved

ApplyFilter removed
2024-12-09 16:34:18 +02:00
Vladimir Enchev
95d4c0e992 DataGrid column should not be visible if all child columns are not visible 2024-12-09 13:33:07 +02:00
Vladimir Enchev
b14f67685a Version updated 2024-12-09 11:48:42 +02:00
Vladimir Enchev
b82fa04aec DataGrid non numeric advanced filtering fixed
Fix #1844
2024-12-09 09:16:34 +02:00
Vladimir Enchev
93d1e8604a PickList ItemRender event added 2024-12-06 10:36:26 +02:00
Atanas Korchev
53d32dbcc2 Rounded corners in stacked bar and column chart sometimes do not display. Closes #1675. 2024-12-05 17:35:40 +02:00
Vladimir Enchev
da43d91b5a PickList AllowMoveAll, AllowMoveAllSourceToTarget, AllowMoveAllTargetToSource properties added 2024-12-05 15:39:01 +02:00
Jaap-Jan de Wit
0694ab0777 Fix null ref exception when column is null on GroupsCollectionChanged (#1836) 2024-12-05 15:14:37 +02:00
Vladimir Enchev
207940426d DatePicker DateRender will not apply class attribute 2024-12-05 15:08:56 +02:00
Vladimir Enchev
58a204ed56 Version updated 2024-12-05 10:07:23 +02:00
Vladimir Enchev
f68a2719f4 DataGrid cell editing validation fixed 2024-12-05 10:07:03 +02:00
kerajel
4311b019f4 RadzenDataGridHeaderCell to handle nullable types in ApplyFilter (#1834)
* RadzenDataGridHeaderCell to handle nullable types in ApplyFilter

* code clean up

* code clean up

---------

Co-authored-by: Dmitrii Botov <dmitrii.botov@rokolabs.com>
2024-12-04 09:08:22 +02:00
Vladimir Enchev
c1b7ce0bcc Version updated 2024-12-03 15:39:45 +02:00
yordanov
fe0ca5403c Remove Cyber Monday promo 2024-12-03 11:55:58 +02:00
Vladimir Enchev
c5c33ebfeb Check if TextProperty is set when attempting to use it for filter 2024-12-03 11:38:52 +02:00
Atanas Korchev
2c24843e4d Scheduler does not render some events in month view. Closes #1828. 2024-12-03 10:48:01 +02:00
Vladimir Enchev
b0e17e572c Fixed DataGrid advanced numeric filter input issues caused by Blazor server latency 2024-12-03 09:17:20 +02:00
yordanov
7cd5727ccc Add info for Radzen Blazor for Visual Studio 2024-12-02 17:22:33 +02:00
Dimi Catrysse
d02bafc7a6 Update RadzenPager.razor.cs (#1827)
Adjust default value of `FirstPageTitle` to have consistent value with other titles (no dot).
2024-12-02 15:42:28 +02:00
Vladimir Enchev
ca4bdad32a Missing DateOnly check added
Close #1826
2024-12-02 11:28:53 +02:00
yordanov
4fab7f9180 Add Cyber Monday promo 2024-12-01 11:12:40 +02:00
yordanov
224f54c676 Add UI Blocks intro video 2024-12-01 11:11:48 +02:00
Vladimir Enchev
fbc1dbb443 Fixed DataGrid advanced numeric filter second input Issue
Fix #1824
2024-12-01 10:38:25 +02:00
Atanas Korchev
4da4e532c5 RadzenColorPicker is not fully integrated with the EditContext API.
Closes #1822.
2024-11-30 18:20:59 +02:00
Vladimir Enchev
a16447faa2 Version updated 2024-11-30 15:31:08 +02:00
Vladimir Enchev
a883b26dda DataGrid CheckBoxList filter for numerics and enums fixed.
Fix #1823
2024-11-30 15:30:48 +02:00
Vladimir Enchev
b9937ee6ca Fixed parameter is not of type node with AutoComplete 2024-11-29 16:04:35 +02:00
simonlübker
40ed1d5841 adds missing non-nullable DateOnly typecheck (#1820)
Co-authored-by: Simon Lübker <simon.luebker@equicon.de>
2024-11-29 14:30:22 +02:00
Vladimir Enchev
0d3b86d06b Fixed Tabs OnKeyPress out of range exception 2024-11-29 14:09:26 +02:00
Vladimir Enchev
f8464d6f23 Version updated 2024-11-29 10:19:54 +02:00
Vladimir Enchev
d2995e3b6c employeeID should be set to null on clear 2024-11-29 09:28:26 +02:00
Vladimir Enchev
eda37027d5 Force DataGrid advanced numeric filter value on apply only for default UI 2024-11-29 09:27:11 +02:00
Vladimir Enchev
11f9e5cfa4 DataGrid advanced numeric filter second value fixed
Fix #1817
2024-11-29 09:13:06 +02:00
Vladimir Enchev
a59a02a6a7 Fixed DataGrid, advanced filter with Settings on column with a Enum type
Fix #1816
2024-11-29 09:01:07 +02:00
Vladimir Enchev
26db867bd5 Filtering of DropDownBase will be able to handle null values 2024-11-28 13:40:09 +02:00
Vladimir Enchev
18a92cbaca Version updated 2024-11-28 09:59:52 +02:00
Chenxiang
62ac151847 Update RadzenDatePicker.razor (#1814)
fix: button text
2024-11-28 09:58:20 +02:00
Vladimir Enchev
0b3628e085 DataGrid numeric advanced filter input improved 2024-11-28 09:56:57 +02:00
Vladimir Enchev
ffb23e7f34 demo updated 2024-11-27 09:12:34 +02:00
Vladimir Enchev
678d861e22 DataGrid filtering sub properties should handle null values 2024-11-25 22:33:46 +02:00
Vladimir Enchev
652e08ebbe Attempt to fix possible bug with numeric filter not submitting value caused by Blazor server lag 2024-11-25 09:37:04 +02:00
Vladimir Enchev
95672569c5 Version updated 2024-11-22 16:49:09 +02:00
Vladimir Enchev
c9bedc9315 Tooltip generates console error when RadzenTooltip.CloseTooltip called
Fix #1808
2024-11-22 16:48:49 +02:00
Vladimir Enchev
dbaeb39564 Version updated 2024-11-22 09:50:36 +02:00
yordanov
1e9801b0d4 Remove duplicate rz-layout styles from the demos 2024-11-22 09:19:18 +02:00
Vasil Yordanov
eed82f2b6e UI Blocks (#1806)
Add UI Blocks
2024-11-21 16:56:17 +02:00
Vladimir Enchev
a0f1545421 Tooltip position updated when forced to change 2024-11-21 14:47:42 +02:00
Atanas Korchev
a69909ff23 Add another info message in the getting started. 2024-11-21 14:00:59 +02:00
Atanas Korchev
65c99d3fbd Add .net 9 to getting started. 2024-11-21 13:23:17 +02:00
Atanas Korchev
687094fec0 Revert "Remove unsupported .NET versions from the getting started."
This reverts commit 6d84bafa2e.
2024-11-21 13:23:17 +02:00
simonlübker
757debaecc fixes missing DateOnly Type check in if statement (#1803)
Co-authored-by: Simon Lübker <simon.luebker@equicon.de>
2024-11-21 08:52:24 +02:00
Vladimir Enchev
6d5dda80ee Version updated 2024-11-20 15:56:18 +02:00
Vladimir Enchev
b9d9e965d5 DropDownDataGrid should not request items on clear if not needed 2024-11-20 15:48:51 +02:00
Vladimir Enchev
e845df2a10 DropDownDataGrid should not request items on clear 2024-11-20 15:43:01 +02:00
Vladimir Enchev
93cfc0e15d obsolete code deleted 2024-11-20 14:34:28 +02:00
Vladimir Enchev
7a35bc2340 Tooltip should call openTooltip only once 2024-11-20 14:24:47 +02:00
Vladimir Enchev
dc7eebb8ae DataGrid advanced filter popup position fixed 2024-11-20 14:17:23 +02:00
Vladimir Enchev
1dfc967900 Slider step not working properly when equal to min 2024-11-20 14:08:21 +02:00
Vladimir Enchev
bb5217948d Tooltip top/left position fixed 2024-11-20 14:00:11 +02:00
Vladimir Enchev
c2269711c5 SplitButton popup overlaps the button in some cases 2024-11-20 11:50:58 +02:00
Vladimir Enchev
e707b99860 demo marked as new 2024-11-19 16:02:44 +02:00
Vladimir Enchev
ec43241ccc Version updated 2024-11-19 16:00:13 +02:00
Vladimir Enchev
c45436dcf4 DataGrid FilterOperators column property added, FilterValueTemplate enabled for SimpleWithMenu and CheckBoxList filter modes (#1800)
* DataGrid FilterValueTemplate support for SimpleWithMenu FilterMode

* demo updated
2024-11-19 15:44:04 +02:00
Vladimir Enchev
d41c3ee72b Popup positioning improved 2024-11-19 15:41:54 +02:00
yordanov
0058d0aa56 Update responsive classes to reduce white space on smaller screens. Resolves #1798 2024-11-19 10:17:26 +02:00
Vladimir Enchev
868638f031 Version updated 2024-11-18 13:02:00 +02:00
Marat Chiraev
a8250f17c3 Fix missing element reference for RadzenSplitterPane (#1796) 2024-11-18 12:50:40 +02:00
yordanov
c3bbc7e31c BlackFriday2024 2024-11-18 12:43:02 +02:00
Vladimir Enchev
5d5565b832 DataGrid IsValid property added 2024-11-18 11:19:15 +02:00
Vladimir Enchev
5da0ff61b2 Version updated 2024-11-18 10:34:22 +02:00
Maks
79efa87d39 Fix FilterString generation for the FilterOperator.In and FilterOperator.NotIn (#1794)
Co-authored-by: Sacred <s>
2024-11-18 10:30:43 +02:00
Vladimir Enchev
a56981bc70 RadzenDataFilterProperty FilterProperty property added similar to DataGrid columns to be able to filter collection sub property
Fix #1792
2024-11-18 10:28:53 +02:00
Vladimir Enchev
24f30bf18f RadzenDropZoneItemEventArgs DataTransfer property added to handle files drop 2024-11-18 09:45:54 +02:00
Vladimir Enchev
0fa843fd78 Fixed touch not working properly on page with Slider component 2024-11-18 09:26:21 +02:00
Vladimir Enchev
aecee331a4 Version updated 2024-11-15 15:55:13 +02:00
Vladimir Enchev
c1b7cc6d65 Fixed popup can cause scroll in some cases 2024-11-15 15:54:40 +02:00
Atanas Korchev
b8ddfa6537 Check if document.body is available to prevent JS errors when Radzen.Blazor.js is incorrectly included in the <head>. 2024-11-15 14:54:38 +02:00
Atanas Korchev
fd4bd631e5 Update the NumericRangeValidator demo to mention numeric suffixes that hint the Blazor parser what type to infer. 2024-11-15 14:53:02 +02:00
Atanas Korchev
96b8901cde Allow the developer to set the Fill of RadzenSeriesAnnotation. Closes #1789. 2024-11-15 14:40:14 +02:00
Vladimir Enchev
124033d122 DataGrid column numeric advanced filter improved 2024-11-15 09:11:50 +02:00
Vladimir Enchev
cbb292b828 Fixed disabled SplitButton tabindex not correct
Fix #1788
2024-11-14 18:16:30 +02:00
Vladimir Enchev
22b5fc2452 DataGrid advanced filter FilterValueTemplate demo added with Numeric 2024-11-14 10:46:29 +02:00
yordanov
d549a314bc Set dynamic viewport height to RadzenLayout on screen widths less than 768px 2024-11-13 18:33:38 +02:00
Vladimir Enchev
f32fe71dbc test fixed 2024-11-13 17:44:10 +02:00
Vladimir Enchev
169cf14fa6 bunit.web updated 2024-11-13 17:30:01 +02:00
Atanas Korchev
3befe007fd Update ci.yml 2024-11-13 17:22:04 +02:00
Atanas Korchev
6d84bafa2e Remove unsupported .NET versions from the getting started. 2024-11-13 17:20:15 +02:00
Vladimir Enchev
f3badc0ad4 DataGrid OData demo fixed
Fix #1786
2024-11-13 15:55:46 +02:00
Vladimir Enchev
df9887b629 Edit/run of demos fixed 2024-11-13 11:29:21 +02:00
Vladimir Enchev
cbecf5b954 Version updated 2024-11-13 09:04:43 +02:00
Vladimir Enchev
040cccebe2 NET9 support added (#1785) 2024-11-12 22:21:55 +02:00
Vladimir Enchev
efffdf54c3 Vertical slider added (#1781)
* Slider Orientation property added

Vertical range slider keyboard navigation fixed

* Update slider styles

* Vertical slider rendering fixed

* Add Updated badge to Slider

---------

Co-authored-by: yordanov <vasil@yordanov.info>
2024-11-11 10:29:38 +02:00
Atanas Korchev
c3cb0c8968 Update the getting started instructions for WASM standalone. 2024-11-09 11:13:29 +02:00
yordanov
d676c67b50 Add demo for filled icons 2024-11-08 14:16:25 +02:00
Vladimir Enchev
9316170c89 Fixed DataGrid CheckBoxList filter exception with Enum sub property 2024-11-08 08:55:51 +02:00
Vladimir Enchev
9101614d35 Version updated 2024-11-06 18:14:36 +02:00
Vladimir Enchev
7312b2859d GoogleMap UpdateMap() fixed 2024-11-06 18:14:17 +02:00
Vladimir Enchev
bf7d99ccba Fixed Carousel Auto=true does not repeat cycle
Fix #1774
2024-11-06 10:08:29 +02:00
Vladimir Enchev
fb0d588cbf Fixed DataGrid will not show EmptyTemplate when AllowVirtualization=true 2024-11-06 10:05:33 +02:00
Vladimir Enchev
8e7bd6323a Version updated 2024-11-05 12:21:20 +02:00
Vladimir Enchev
8bebd1933d Fixed "The ParameterView instance can no longer be read because it has expired." error with virtualized DropDownDataGrid 2024-11-05 11:57:35 +02:00
Vladimir Enchev
2429b2c027 Missing summary added 2024-11-05 11:23:05 +02:00
Vladimir Enchev
769820792f Fixed DataGrid Object must implement IConvertible with DateOnly filtering for OData
Fix #1772
2024-11-05 10:52:32 +02:00
Jake Mauch
36f39b5023 Add callback for Open and Close to RadzenDropDown (#1771)
Co-authored-by: jmauch <jmauch@webstaurantstore.com>
2024-11-05 09:51:28 +02:00
Vladimir Enchev
bb675ee040 Fixed Carousel out of range exception when navigating to out of range index
Close #1769 #1770
2024-11-05 09:45:48 +02:00
Vladimir Enchev
9b4aff7a3f Fixed DataGrid LoadColumnFilterData paging arguments not passed correctly 2024-11-05 08:57:53 +02:00
Vladimir Enchev
e312d71286 Carousel swipe should not prevent touch of other nested components 2024-11-04 18:21:57 +02:00
Atanas Korchev
a397c686b8 Avoid using new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase). Should fix #1705. 2024-11-04 09:21:04 +02:00
Atanas Korchev
9b92005810 Load assemblies in parallel. 2024-11-04 09:21:04 +02:00
Atanas Korchev
d4e0c8c3bb Compilation error when trying to edint the realtime data example. 2024-11-04 09:21:04 +02:00
Vladimir Enchev
ae1ec9aaa2 Version updated 2024-11-04 09:16:34 +02:00
Vladimir Enchev
e811bb9213 DataGrid will close column filter after open in case of hidden column with FilterPopupRenderMode="PopupRenderMode.OnDemand" 2024-11-01 19:49:08 +02:00
Vladimir Enchev
7de5d90c82 DataGrid Reset() should reset to original columns order 2024-11-01 19:43:45 +02:00
Vladimir Enchev
bf5265c15b Fixed boolean fields in advanced filter auto-apply without pressing 'Apply'
Fix #1766
2024-11-01 09:07:49 +02:00
Vladimir Enchev
49b111a0e9 Fixed RadzenCarousel Navigation will skip one item forward on manual navigation with Auto=true
Fix #1764
2024-11-01 08:53:34 +02:00
Vladimir Enchev
85964d9f17 Version updated 2024-10-31 10:27:58 +02:00
Vladimir Enchev
4f9075fa77 Demo updated to reinitialize DataGrid columns when switching between filtering modes 2024-10-31 10:24:03 +02:00
Vladimir Enchev
0856c9005c Fixed the ParameterView instance can no longer be read because it has expired with virtualized DropDownDataGrid 2024-10-31 10:01:13 +02:00
Vladimir Enchev
31d8bccfcf version updated 2024-10-29 11:21:09 +02:00
Vladimir Enchev
9213e3d3ef Fixed DataGrid column FilterOperator will always default to what set initially 2024-10-29 11:20:54 +02:00
Vladimir Enchev
3665079cc9 Version updated 2024-10-29 09:47:31 +02:00
Vladimir Enchev
44881e521c Changing Carousel Auto from false to true will not restart the timer properly 2024-10-29 09:47:02 +02:00
yordanov
c136d8050b Update premium themes 2024-10-29 09:41:00 +02:00
Vladimir Enchev
a5cc04b46f carousel demos fixed 2024-10-29 09:40:26 +02:00
Vladimir Enchev
a9a8b6314b Carousel swipe support fixed 2024-10-29 09:36:00 +02:00
Jan Biehl
5c7be3bcfc Update DataAnnotationValidatorConfig.razor (#1759)
The proper full width class should be rz-w-100 instead of w-100
2024-10-29 09:20:50 +02:00
yordanov
e3893198af Update Chart label rotation demos 2024-10-29 07:42:35 +02:00
yordanov
6a4e6dc5a1 Update premium themes 2024-10-29 07:41:40 +02:00
Atanas Korchev
643916f733 Chart label rotation. 2024-10-28 16:57:28 +02:00
yordanov
e964851d53 Update premium themes 2024-10-28 10:11:26 +02:00
Vladimir Enchev
79e495dc14 Version updated 2024-10-28 10:07:36 +02:00
Maxim Becker
13326879f0 Render chart tooltips in the same way as standard tooltips (#1745)
* Render chart tooltips in the same way as standard tooltips

* Move OpenChartTooltip method to TooltipService to avoid code duplications

* Avoid partially hiding of chart tooltip near top of page

* Make some of the types internal.

---------

Co-authored-by: Atanas Korchev <akorchev@gmail.com>
2024-10-28 10:01:13 +02:00
Vladimir Enchev
293a871db4 DropDownDataGrid sorting when virtualized fixed 2024-10-28 08:48:30 +02:00
Vladimir Enchev
4c9dc6350d demo source code fixed 2024-10-28 07:22:32 +02:00
Vladimir Enchev
4c54afdc55 PickList Placeholder, SourcePlaceholder and TargetPlaceholder added 2024-10-26 11:42:43 +03:00
Vasil Yordanov
d60841b040 Add CardGroup component. Resolves #1658 (#1753)
Add RadzenCardGroup component
2024-10-25 16:17:47 +03:00
yordanov
7e640f47a5 Hide icons until icon font is loaded. Resolves #1752 2024-10-25 16:00:27 +03:00
Vladimir Enchev
f3d1b273f1 Real-time data demo added for the DataGrid 2024-10-24 15:54:26 +03:00
Vladimir Enchev
e80f5b85e9 ApiKey defined for updateMap method 2024-10-24 13:35:31 +03:00
Atanas Korchev
1b5725caee Use wasm files to avoid firewall issues. 2024-10-23 11:02:58 +03:00
Vladimir Enchev
820e8f8323 unpkg.com added to script-src 2024-10-23 09:49:11 +03:00
mcgovern-ellsworth
30672e52bb added autocomplete parameters to RadzenDropDown. (#1748)
* added autocomplete parameters to RadzenDropDown.

* removed the InputAutoCompleteType setting since that input control is hidden but the outer div.
2024-10-22 18:37:24 +03:00
Vladimir Enchev
ac9796f50d demo updated 2024-10-22 16:40:43 +03:00
Vladimir Enchev
66b7b4cad0 Version updated 2024-10-22 09:50:36 +03:00
Vladimir Enchev
f417b9cc73 PreserveRowSelectionOnPageing renamed to PreserveRowSelectionOnPaging 2024-10-22 09:50:21 +03:00
Greg-MM
a304d4643c Fixed bug when navigating RadzenDropDownDataGrid with keyboard, down worked, but up would focus 2 rows above the selected index (#1747)
Added PreserveRowSelectionOnPageing to RadzenDropDownDataGrid which will keep the currently selected row index, defaults to false so there are no breaking changes

Co-authored-by: TSE <Administrator@TranExec.com>
2024-10-22 09:44:38 +03:00
Vladimir Enchev
8386e5a2b5 Version updated 2024-10-22 09:40:49 +03:00
Vladimir Enchev
27f57b96c0 DataGrid wrong number of rows rendered in some cases with composite columns 2024-10-21 17:43:50 +03:00
Vladimir Enchev
d9a049d523 version updated 2024-10-21 16:19:25 +03:00
Vladimir Enchev
f53f724b04 DataGrid duplicate rows fixed 2024-10-21 16:19:02 +03:00
Vladimir Enchev
a41105656a Version updated 2024-10-21 13:05:46 +03:00
Vladimir Enchev
54269abfd1 GoogleMap should respect Culture language 2024-10-21 12:51:34 +03:00
Vladimir Enchev
e812ff1337 DataGrid composite columns span not properly calculated in case of non visible columns 2024-10-21 11:48:42 +03:00
Vladimir Enchev
ae28f8f239 DataGrid group footers demo updated 2024-10-17 12:19:31 +03:00
Vladimir Enchev
470f261dc0 titles fixed 2024-10-17 11:04:30 +03:00
Vladimir Enchev
80cfda23b6 demo improved 2024-10-17 10:45:35 +03:00
Vladimir Enchev
5321c4d52b Version updated 2024-10-17 10:14:44 +03:00
Vladimir Enchev
0466912f3e Carousel Navigate() method will reset timer 2024-10-17 10:13:24 +03:00
Vladimir Enchev
3d3932e657 EmployeeTerritorysData added 2024-10-17 10:04:55 +03:00
Vladimir Enchev
d63117a17a DataGrid wrong column grouped in some cases with composite columns 2024-10-17 09:39:12 +03:00
Vladimir Enchev
ba06a72326 Carousel manual navigation should reset the auto-rotate timer
Fix #1741
2024-10-16 16:06:37 +03:00
Vladimir Enchev
2671f6ca61 Carousel pager not updated on auto rotate in some cases
Fix #1740
2024-10-16 15:26:07 +03:00
yordanov
8dddfcbc92 Remove obselete tabs styles. Resolves #1742 2024-10-16 14:58:05 +03:00
yordanov
ebc5793f36 Fix label overlapping when RadzenFileInput is in RadzenFormField. Resolves #1716 2024-10-16 11:17:59 +03:00
yordanov
4946221651 Fix rz-column-title overflow if it has a checkbox inside. Resolves #1683 2024-10-15 15:52:23 +03:00
Vladimir Enchev
de9817a97f DataGrid should not show two expand icons in case of both self-reference hierarchy and Template defined
Fix #1737
2024-10-14 16:36:08 +03:00
yordanov
1218c4f385 Disabled text color in material theme should be lighter. Resolves #1736 2024-10-14 11:07:52 +03:00
Vladimir Enchev
e9cee62d03 demo improved 2024-10-14 09:31:37 +03:00
yordanov
fa7cf37eab Update Documentation link to API reference 2024-10-14 09:25:29 +03:00
Vladimir Enchev
b9ecb7556f Version updated 2024-10-14 09:07:42 +03:00
Vladimir Enchev
661b38e1e6 DataGrid column picking demo improved with context menu option
Close #1735
2024-10-14 08:53:06 +03:00
Vladimir Enchev
769f4ba589 Tooltip smart position will take into account scrollbar width 2024-10-12 18:00:40 +03:00
Vladimir Enchev
6118253ebf DataFilter LoadData demo improved 2024-10-12 10:43:19 +03:00
Vladimir Enchev
0f7f717330 DataGrid/DataFilter ToFilterString() enumerable type discovery improved 2024-10-12 10:37:18 +03:00
yordanov
0b16f1eb97 Buttons in Tooltip position demo should wrap 2024-10-11 15:44:37 +03:00
Atanas Korchev
e733894c02 RadzenBody sometimes triggers JS exception after navigation. 2024-10-10 19:38:41 +03:00
Vladimir Enchev
c47dcfbe27 warnings fixed 2024-10-10 18:24:46 +03:00
Vladimir Enchev
8ae008ec9d Carousel component added (#1732)
* Carousel component added

* Carousel with Pager demo added

* Carousel demos improved

* Update carousel

* Carousel improved

* Carousel demos reworked with database data

* Add carousel pager overlay styles

* Update carousel demos

* Add Carousel to homepage and update icon

* Add Carousel styles to themes

* Update Carousel styles and demos

* Update Carousel markup and styles

* Update Carousel demos

* Update ExampleService

---------

Co-authored-by: yordanov <vasil@yordanov.info>
2024-10-10 18:21:22 +03:00
Vladimir Enchev
45ef0c78a3 docker updated 2024-10-10 15:33:05 +03:00
Vladimir Enchev
3ac46f239e Version updated 2024-10-10 14:56:48 +03:00
Vladimir Enchev
1e106f7ada DataPicker rz-readonly should be added only when ReadOnly=true 2024-10-10 14:56:32 +03:00
Vladimir Enchev
09738b2dd7 added version to blazor.web.js 2024-10-10 13:48:00 +03:00
Vladimir Enchev
dc67a0b0fc Version updated 2024-10-10 08:57:59 +03:00
Atanas Korchev
1258bbc770 RadzenDataAnnotationValidator supports nested properties. 2024-10-09 18:59:27 +03:00
Victor Ureta
5960318a60 Add same color to datepicker button as text (#1730)
* Update RadzenDatePicker.razor

* Update RadzenDatePicker.razor

* add ButtonClass
2024-10-09 13:33:37 +03:00
Vladimir Enchev
d4faf758f4 DataGrid will not render items when virtualized and grouped in some cases 2024-10-09 13:33:07 +03:00
Vladimir Enchev
f88216b9a0 ReadOnly Datepicker with PopupRenderMode Initial allows user to open popup and select date
Fix #1727
2024-10-09 11:41:04 +03:00
Vladimir Enchev
e21d648588 Closing tooltip should not try to restore focus 2024-10-09 09:04:33 +03:00
Vladimir Enchev
dbcbdc9bbc comment fixed
Fix #1729
2024-10-08 16:17:28 +03:00
Vladimir Enchev
c5d16e8381 FormatString added to DateTime column in Dialog demo 2024-10-08 16:13:36 +03:00
Vladimir Enchev
ac1aa6f3b1 dates updated 2024-10-08 16:09:20 +03:00
Vladimir Enchev
866c222c74 DropDown will not select item on Enter is some cases 2024-10-08 13:34:39 +03:00
Vladimir Enchev
06843477cb Index property added to DataGrid RowRenderEventArgs 2024-10-08 13:34:39 +03:00
Vladimir Enchev
9a7a6fc6ab DataGrid will group by wrong column in case of invisible columns 2024-10-08 13:34:38 +03:00
Atanas Korchev
4986da415f Some data grid grouping demos are not editable. 2024-10-08 08:10:09 +03:00
362 changed files with 11441 additions and 3935 deletions

244
.editorconfig Normal file
View File

@@ -0,0 +1,244 @@
# Remove the line below if you want to inherit .editorconfig settings from higher directories
root = true
# C# files
[*.cs]
#### Core EditorConfig Options ####
# Indentation and spacing
indent_size = 4
indent_style = space
tab_width = 4
# New line preferences
end_of_line = crlf
insert_final_newline = false
#### .NET Code Actions ####
# Type members
dotnet_hide_advanced_members = false
dotnet_member_insertion_location = with_other_members_of_the_same_kind
dotnet_property_generation_behavior = prefer_throwing_properties
# Symbol search
dotnet_search_reference_assemblies = true
#### .NET Coding Conventions ####
# Organize usings
dotnet_separate_import_directive_groups = false
dotnet_sort_system_directives_first = false
file_header_template = unset
# this. and Me. preferences
dotnet_style_qualification_for_event = false
dotnet_style_qualification_for_field = false
dotnet_style_qualification_for_method = false
dotnet_style_qualification_for_property = false
# Language keywords vs BCL types preferences
dotnet_style_predefined_type_for_locals_parameters_members = true
dotnet_style_predefined_type_for_member_access = true
# Parentheses preferences
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
dotnet_style_parentheses_in_other_operators = never_if_unnecessary
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity
# Modifier preferences
dotnet_style_require_accessibility_modifiers = for_non_interface_members
# Expression-level preferences
dotnet_prefer_system_hash_code = true
dotnet_style_coalesce_expression = true
dotnet_style_collection_initializer = true
dotnet_style_explicit_tuple_names = true
dotnet_style_namespace_match_folder = true
dotnet_style_null_propagation = true
dotnet_style_object_initializer = true
dotnet_style_operator_placement_when_wrapping = beginning_of_line
dotnet_style_prefer_auto_properties = true
dotnet_style_prefer_collection_expression = when_types_loosely_match
dotnet_style_prefer_compound_assignment = true
dotnet_style_prefer_conditional_expression_over_assignment = true
dotnet_style_prefer_conditional_expression_over_return = true
dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed
dotnet_style_prefer_inferred_anonymous_type_member_names = true
dotnet_style_prefer_inferred_tuple_names = true
dotnet_style_prefer_is_null_check_over_reference_equality_method = true
dotnet_style_prefer_simplified_boolean_expressions = true
dotnet_style_prefer_simplified_interpolation = true
# Field preferences
dotnet_style_readonly_field = true
# Parameter preferences
dotnet_code_quality_unused_parameters = all
# Suppression preferences
dotnet_remove_unnecessary_suppression_exclusions = none
# New line preferences
dotnet_style_allow_multiple_blank_lines_experimental = true
dotnet_style_allow_statement_immediately_after_block_experimental = true
#### C# Coding Conventions ####
# var preferences
csharp_style_var_elsewhere = false
csharp_style_var_for_built_in_types = false
csharp_style_var_when_type_is_apparent = false
# Expression-bodied members
csharp_style_expression_bodied_accessors = true
csharp_style_expression_bodied_constructors = false
csharp_style_expression_bodied_indexers = true
csharp_style_expression_bodied_lambdas = true
csharp_style_expression_bodied_local_functions = false
csharp_style_expression_bodied_methods = false
csharp_style_expression_bodied_operators = false
csharp_style_expression_bodied_properties = true
# Pattern matching preferences
csharp_style_pattern_matching_over_as_with_null_check = true
csharp_style_pattern_matching_over_is_with_cast_check = true
csharp_style_prefer_extended_property_pattern = true
csharp_style_prefer_not_pattern = true
csharp_style_prefer_pattern_matching = true
csharp_style_prefer_switch_expression = true
# Null-checking preferences
csharp_style_conditional_delegate_call = true
# Modifier preferences
csharp_prefer_static_anonymous_function = true
csharp_prefer_static_local_function = true
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async
csharp_style_prefer_readonly_struct = true
csharp_style_prefer_readonly_struct_member = true
# Code-block preferences
csharp_prefer_braces = true
csharp_prefer_simple_using_statement = true
csharp_prefer_system_threading_lock = true
csharp_style_namespace_declarations = block_scoped
csharp_style_prefer_method_group_conversion = true
csharp_style_prefer_primary_constructors = true
csharp_style_prefer_top_level_statements = true
# Expression-level preferences
csharp_prefer_simple_default_expression = true
csharp_style_deconstructed_variable_declaration = true
csharp_style_implicit_object_creation_when_type_is_apparent = true
csharp_style_inlined_variable_declaration = true
csharp_style_prefer_index_operator = true
csharp_style_prefer_local_over_anonymous_function = true
csharp_style_prefer_null_check_over_type_check = true
csharp_style_prefer_range_operator = true
csharp_style_prefer_tuple_swap = true
csharp_style_prefer_utf8_string_literals = true
csharp_style_throw_expression = true
csharp_style_unused_value_assignment_preference = discard_variable
csharp_style_unused_value_expression_statement_preference = discard_variable
# 'using' directive preferences
csharp_using_directive_placement = outside_namespace
# New line preferences
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true
csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true
csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true
csharp_style_allow_embedded_statements_on_same_line_experimental = true
#### C# Formatting Rules ####
# New line preferences
csharp_new_line_before_catch = true
csharp_new_line_before_else = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_open_brace = all
csharp_new_line_between_query_expression_clauses = true
# Indentation preferences
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents = true
csharp_indent_case_contents_when_block = true
csharp_indent_labels = no_change
csharp_indent_switch_labels = true
# Space preferences
csharp_space_after_cast = false
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_after_comma = true
csharp_space_after_dot = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_after_semicolon_in_for_statement = true
csharp_space_around_binary_operators = before_and_after
csharp_space_around_declaration_statements = false
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_before_comma = false
csharp_space_before_dot = false
csharp_space_before_open_square_brackets = false
csharp_space_before_semicolon_in_for_statement = false
csharp_space_between_empty_square_brackets = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_declaration_name_and_open_parenthesis = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false
# Wrapping preferences
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = true
#### Naming styles ####
# Naming rules
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
# Symbol specifications
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.required_modifiers =
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers =
# Naming styles
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case

View File

@@ -15,11 +15,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v3
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
dotnet-version: 9.0.x
- name: Build
run: dotnet build Radzen.Blazor/Radzen.Blazor.csproj
- name: Test

View File

@@ -17,7 +17,7 @@ COPY RadzenBlazorDemos.Host /app/RadzenBlazorDemos.Host
WORKDIR /app
RUN docfx DocFX/docfx.json
FROM mcr.microsoft.com/dotnet/sdk:8.0
FROM mcr.microsoft.com/dotnet/sdk:9.0
COPY --from=0 /app/RadzenBlazorDemos.Host /app/RadzenBlazorDemos.Host
COPY --from=0 /app/RadzenBlazorDemos /app/RadzenBlazorDemos

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

@@ -3,6 +3,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Bunit;
using Microsoft.Extensions.DependencyInjection;
using Radzen.Blazor.Rendering;
using Xunit;
using Xunit.Abstractions;
@@ -23,6 +24,9 @@ public class ChartTests
using var ctx = new TestContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.Setup<Rect>("Radzen.createChart", _ => true).SetResult(new Rect {Left = 0, Top = 0, Width = 200, Height = 200});
ctx.Services.AddScoped<TooltipService>();
ctx.JSInterop.SetupVoid("Radzen.openChartTooltip", _ => true);
ctx.RenderComponent<RadzenChartTooltip>();
var seriesData = Enumerable.Range(0, 5000).Select(i => new Point { X = i, Y = i });
var chart = ctx.RenderComponent<RadzenChart>(chartParameters =>
@@ -42,12 +46,12 @@ public class ChartTests
})));
var stopwatch = Stopwatch.StartNew();
foreach (var _ in Enumerable.Range(0, 10))
foreach (var invocation in Enumerable.Range(0, 10))
{
await chart.InvokeAsync(() => chart.Instance.MouseMove(100, 80));
Assert.Contains("<div class=\"rz-chart-tooltip", chart.Markup);
Assert.Equal((invocation + 1) * 2, ctx.JSInterop.Invocations.Count(x => x.Identifier == "Radzen.openChartTooltip"));
await chart.InvokeAsync(() => chart.Instance.MouseMove(0, 0));
Assert.DoesNotContain("<div class=\"rz-chart-tooltip", chart.Markup);
Assert.Equal(invocation + 1, ctx.JSInterop.Invocations.Count(x => x.Identifier == "Radzen.closeTooltip"));
}
output.WriteLine($"Time took: {stopwatch.Elapsed}");
}

View File

@@ -5,7 +5,7 @@ using Microsoft.AspNetCore.Components.Rendering;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Xunit;
namespace Radzen.Blazor.Tests
@@ -362,6 +362,82 @@ namespace Radzen.Blazor.Tests
component.Find(".rz-sortable-column").FirstElementChild.Click();
Assert.Contains(@$"rzi-sort-desc", component.Markup);
component.Find(".rz-sortable-column").FirstElementChild.Click();
Assert.DoesNotContain(@$"rzi-sort-desc", component.Markup);
Assert.DoesNotContain(@$"rzi-sort-asc", component.Markup);
}
// clear Sorting tests
[Fact]
public void DataGrid_Renders_ClearSorting()
{
using var ctx = new TestContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
{
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, Enumerable.Range(0, 100).Select(i => new { Id = i }));
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
{
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
builder.AddAttribute(1, "Property", "Id");
builder.AddAttribute(2, "Title", "Id");
builder.CloseComponent();
});
parameterBuilder.Add<bool>(p => p.AllowSorting, true);
});
component.Find(".rz-sortable-column").FirstElementChild.Click();
Assert.Contains(@$"rzi-sort-asc", component.Markup);
component.Instance.Sorts.Clear();
Assert.DoesNotContain(@$"rzi-sort-desc", component.Markup);
Assert.DoesNotContain(@$"rzi-sort-asc", component.Markup);
}
// sorting with load data event test.
[Fact]
public void DataGrid_Renders_LoadDataWithOrdering()
{
using var ctx = new TestContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
List<LoadDataArgs> loadDataArgs = [];
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
{
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, Enumerable.Range(0, 100).Select(i => new { Id = i }));
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
{
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
builder.AddAttribute(1, "Property", "Id");
builder.AddAttribute(2, "Title", "Id");
builder.CloseComponent();
});
parameterBuilder.Add<bool>(p => p.AllowSorting, true);
parameterBuilder.Add(p => p.LoadData, args => loadDataArgs.Add(args));
});
component.Find(".rz-sortable-column").FirstElementChild.Click();
Assert.Contains(@$"rzi-sort-asc", component.Markup);
component.Instance.Sorts.Clear();
component.Render();
Assert.DoesNotContain(@$"rzi-sort-desc", component.Markup);
Assert.DoesNotContain(@$"rzi-sort-asc", component.Markup);
Assert.Equal(4, loadDataArgs.Count);
Assert.Equal("", loadDataArgs[0].OrderBy);
Assert.Equal("Id asc", loadDataArgs[1].OrderBy);
Assert.Equal("Id asc", loadDataArgs[2].OrderBy);
Assert.Equal("", loadDataArgs[3].OrderBy);
}
// Paging tests
@@ -824,10 +900,14 @@ namespace Radzen.Blazor.Tests
component.SetParametersAndRender(parameters =>
{
parameters.Add<int>(p => p.PageSize, 20);
parameters.Add<LoadDataArgs>(p => p.LoadData, args => { raised = true; newArgs = args; });
});
component.SetParametersAndRender(parameters =>
{
parameters.Add<int>(p => p.PageSize, 20);
});
component.Find(".rz-pager-next").Click();
Assert.True(raised);

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

@@ -43,7 +43,7 @@ public class DollarsTypeConverter : TypeConverter
return new Dollars(d);
if (value is string s)
return decimal.TryParse(s, out var val) ? new Dollars(val) : null;
return decimal.TryParse(s, culture, out var val) ? new Dollars(val) : null;
return base.ConvertFrom(context, culture, value);
}

View File

@@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AngleSharp.Dom;
using Bunit;
@@ -13,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));
@@ -99,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));
});
@@ -114,6 +119,35 @@ namespace Radzen.Blazor.Tests
Assert.Contains("rz-state-highlight", items[0].ClassList);
}
[Fact]
public void DropDown_Respects_ItemEqualityComparer()
{
using var ctx = new TestContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
List<DataItem> boundCollection = [new() { Text = "Item 2" }];
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);
});
var selectedItems = component.FindAll(".rz-state-highlight");
Assert.Equal(1, selectedItems.Count);
Assert.Equal("Item 2", selectedItems[0].TextContent.Trim());
// select Item 1 in list
var items = component.FindAll(".rz-multiselect-item");
items[0].Click();
component.Render();
selectedItems = component.FindAll(".rz-state-highlight");
Assert.Equal(2, selectedItems.Count);
Assert.Equal("Item 1", selectedItems[0].TextContent.Trim());
}
[Fact]
public void DropDown_AppliesSelectionStyleWhenMultipleSelectionIsEnabled()
{
@@ -121,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);
});
@@ -245,5 +280,78 @@ 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>
{
public bool Equals(DataItem x, DataItem y)
{
if (ReferenceEquals(x, y)) return true;
if (x is null) return false;
if (y is null) return false;
if (x.GetType() != y.GetType()) return false;
return x.Text == y.Text;
}
public int GetHashCode(DataItem obj)
{
return obj.Text.GetHashCode();
}
public new bool Equals(object x, object y)
{
return Equals((DataItem)x, (DataItem)y);
}
public int GetHashCode(object obj)
{
return GetHashCode((DataItem)obj);
}
}
}
}
}

View File

@@ -0,0 +1,423 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using Xunit;
namespace Radzen.Blazor.Tests
{
public enum CarType
{
Sedan,
Coupe
}
public class ExpressionParserTests
{
class Person
{
public short? Age { get; set; }
public string Name { get; set; }
public bool? Famous { get; set; }
public DateTime BirthDate { get; set; }
}
public class Car
{
public CarType Type { get; set; }
}
public enum Status
{
Office,
Remote,
}
class OrderDetail
{
public Product Product { get; set; }
public int Quantity { get; set; }
public Order Order { get; set; }
public List<WorkStatus> WorkStatuses { get; set; }
public List<Status> Statuses { get; set; }
}
class Category
{
public string CategoryName { get; set; }
}
class Order
{
public DateTime OrderDate { get; set; }
}
class Product
{
public string ProductName { get; set; }
public Category Category { get; set; }
}
[Fact]
public void Should_ParseBindaryExpression()
{
var expression = ExpressionParser.ParsePredicate<Person>("p => p.Name == \"foo\"");
var func = expression.Compile();
Assert.True(func(new Person() { Name = "foo" }));
}
[Fact]
public void Should_ParseConditionalExpression()
{
var expression = ExpressionParser.ParsePredicate<OrderDetail>("it => (it.Product.ProductName == null ? \"\" : it.Product.ProductName).Contains(\"Queso\") && it.Quantity == 50");
var func = expression.Compile();
Assert.True(func(new OrderDetail() { Product = new Product { ProductName = "Queso" }, Quantity = 50 }));
}
[Fact]
public void Should_ParseNestedLogicalOperations()
{
var expression = ExpressionParser.ParsePredicate<OrderDetail>("it => (it.Product.ProductName == null ? \"\" : it.Product.ProductName).Contains(\"Queso\") && (it.Quantity == 50 || it.Quantity == 12)");
var func = expression.Compile();
Assert.True(func(new OrderDetail() { Product = new Product { ProductName = "Queso" }, Quantity = 12 }));
}
[Fact]
public void Should_SupportDateTimeParsing()
{
var expression = ExpressionParser.ParsePredicate<OrderDetail>("it => it.Order.OrderDate >= DateTime.Parse(\"2025-02-11\")");
var func = expression.Compile();
Assert.True(func(new OrderDetail { Order = new Order { OrderDate = new DateTime(2025, 2, 11) } }));
}
class ItemWithGenericProperty<T>
{
public T Value { get; set; }
}
[Fact]
public void Should_SupportDateOnlyParsing()
{
var expression = ExpressionParser.ParsePredicate<ItemWithGenericProperty<DateOnly>>("it => it.Value >= DateOnly.Parse(\"2025-02-11\")");
var func = expression.Compile();
Assert.True(func(new ItemWithGenericProperty<DateOnly> { Value = new DateOnly(2025, 2, 11) }));
}
[Fact]
public void Should_SupportTimeOnlyParsing()
{
var expression = ExpressionParser.ParsePredicate<ItemWithGenericProperty<TimeOnly>>("it => it.Value >= TimeOnly.Parse(\"12:00:00\")");
var func = expression.Compile();
Assert.True(func(new ItemWithGenericProperty<TimeOnly> { Value = new TimeOnly(12, 0, 0) }));
}
[Fact]
public void Should_SupportGuidParsing()
{
var expression = ExpressionParser.ParsePredicate<ItemWithGenericProperty<Guid>>("it => it.Value == Guid.Parse(\"f0e7e7d8-4f4d-4b5f-8b3e-3f1d1b4f5f5f\")");
var func = expression.Compile();
Assert.True(func(new ItemWithGenericProperty<Guid> { Value = Guid.Parse("f0e7e7d8-4f4d-4b5f-8b3e-3f1d1b4f5f5f") }));
}
[Fact]
public void Should_SupportDateTimeOffsetParsing()
{
var expression = ExpressionParser.ParsePredicate<ItemWithGenericProperty<DateTimeOffset>>("it => it.Value == DateTimeOffset.Parse(\"2025-02-11\")");
var func = expression.Compile();
Assert.True(func(new ItemWithGenericProperty<DateTimeOffset> { Value = DateTimeOffset.Parse("2025-02-11") }));
}
[Fact]
public void Should_SupportEnumWithCasts()
{
var typeLocator = (string type) => AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()).FirstOrDefault(t => t.FullName == type);
var expression = ExpressionParser.ParsePredicate<ItemWithGenericProperty<CarType[]>>("it => it.Value.Any(i => (new []{0}).Contains(i))", typeLocator);
var func = expression.Compile();
Assert.True(func(new ItemWithGenericProperty<CarType[]> { Value = [CarType.Sedan] }));
}
[Fact]
public void Should_SupportNullableCollection()
{
var expression = ExpressionParser.ParsePredicate<Person>("it => new bool?[]{ false }.Contains(it.Famous)");
var func = expression.Compile();
Assert.True(func(new Person { Famous = false }));
}
[Fact]
public void Should_SupportEnumCollections()
{
var typeLocator = (string type) => AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()).FirstOrDefault(t => t.FullName == type);
var expression = ExpressionParser.ParsePredicate<Car>($"it => it.Type == (Radzen.Blazor.Tests.CarType)1", typeLocator);
var func = expression.Compile();
Assert.True(func(new Car { Type = CarType.Coupe }));
}
[Fact]
public void Should_SupportCollectionLiterals()
{
var expression = ExpressionParser.ParsePredicate<OrderDetail>("it => (new []{\"Tofu\"}).Contains(it.Product.ProductName)");
var func = expression.Compile();
Assert.True(func(new OrderDetail { Product = new Product { ProductName = "Tofu" } }));
}
class WorkStatus
{
public string Name { get; set; }
}
[Fact]
public void Should_SupportNestedLambdas()
{
var expression = ExpressionParser.ParsePredicate<OrderDetail>("it => it.WorkStatuses.Any(i => (new []{\"Office\"}).Contains(i.Name))");
var func = expression.Compile();
Assert.True(func(new OrderDetail { WorkStatuses = [new() { Name = "Office" }] }));
}
[Fact]
public void Should_SupportNestedLambdasWithNot()
{
var expression = ExpressionParser.ParsePredicate<OrderDetail>("it => it.WorkStatuses.Any(i => !(new []{\"Office\"}).Contains(i.Name))");
var func = expression.Compile();
Assert.False(func(new OrderDetail { WorkStatuses = [new() { Name = "Office" }] }));
}
[Fact]
public void Should_SupportNestedLambdasWithEnums()
{
var expression = ExpressionParser.ParsePredicate<OrderDetail>("it => it.Statuses.Any(i => (new []{1}).Contains(i))");
var func = expression.Compile();
Assert.True(func(new OrderDetail { Statuses = new List<Status>() { (Status)1 } }));
}
[Fact]
public void Should_SupportNestedLambdasWithComplexMethod()
{
var expression = ExpressionParser.ParsePredicate<OrderDetail>("it => new [] { (Status)1 }.Intersect(it.Statuses).Any()", type => typeof(Status));
var func = expression.Compile();
Assert.True(func(new OrderDetail { Statuses = new List<Status>() { (Status)1 } }));
}
[Fact]
public void Should_SupportToLower()
{
var expression = ExpressionParser.ParsePredicate<Person>("it => (it.Name == null ? \"\" : it.Name).ToLower().Contains(\"na\".ToLower())");
var func = expression.Compile();
Assert.True(func(new Person { Name = "Nana" }));
}
[Fact]
public void Should_SupportNullableProperties()
{
var expression = ExpressionParser.ParsePredicate<Person>("it => it.Age == 50");
var func = expression.Compile();
Assert.True(func(new Person { Age = 50 }));
}
[Fact]
public void Should_SupportNullablePropertiesWithArray()
{
var expression = ExpressionParser.ParsePredicate<Person>("it => (new []{}).Contains(it.Famous)");
var func = expression.Compile();
Assert.False(func(new Person { Famous = null }));
}
[Fact]
public void Should_SupportDateTimeWithArray()
{
var expression = ExpressionParser.ParsePredicate<Person>("it => (new []{DateTime.Parse(\"5/5/2000 12:00:00 AM\")}).Contains(it.BirthDate)");
var func = expression.Compile();
Assert.True(func(new Person { BirthDate = DateTime.Parse("5/5/2000 12:00:00 AM") }));
}
[Fact]
public void Should_SupportNumericConversion()
{
var expression = ExpressionParser.ParsePredicate<ItemWithGenericProperty<double>>("it => it.Value == 50");
var func = expression.Compile();
Assert.True(func(new ItemWithGenericProperty<double> { Value = 50.0 }));
}
[Fact]
public void Should_SupportNullCoalescence()
{
var expression = ExpressionParser.ParsePredicate<ItemWithGenericProperty<double?>>("it => (it.Value ?? 0) == 0");
var func = expression.Compile();
Assert.True(func(new ItemWithGenericProperty<double?> { Value = null }));
}
[Fact]
public void Should_SupportNullConditionAndCoalescence()
{
var expression = ExpressionParser.ParsePredicate<Person>("it => (((it == null) ? null : it.Name) ?? \"\").Contains(\"Da\")");
var func = expression.Compile();
Assert.True(func(new Person { Name = "Dali" }));
}
[Fact]
public void Should_CreateProjection()
{
var expression = ExpressionParser.ParseLambda<OrderDetail>("it => new { ProductName = it.Product.ProductName}");
var func = expression.Compile();
var result = func.DynamicInvoke(new OrderDetail { Product = new Product { ProductName = "Queso" } });
var property = result.GetType().GetProperty("ProductName");
Assert.Equal(typeof(string), property.PropertyType);
Assert.Equal("Queso", property.GetValue(result));
}
[Fact]
public void Should_CreateProjectionFromNestedAccess()
{
var expression = ExpressionParser.ParseLambda<OrderDetail>("it => new { it.Product.Category.CategoryName }");
var func = expression.Compile();
var orderDetail = new OrderDetail { Product = new Product { Category = new Category { CategoryName = "Beverages" } } };
var x = new { orderDetail?.Product?.Category?.CategoryName };
var result = func.DynamicInvoke(orderDetail);
var property = result.GetType().GetProperty("CategoryName");
Assert.Equal(typeof(string), property.PropertyType);
Assert.Equal("Beverages", property.GetValue(result));
}
[Fact]
public void Should_CreateProjectionFromNestedConditionalAccess()
{
var expression = ExpressionParser.ParseLambda<OrderDetail>("it => new { it.Product?.Category?.CategoryName }");
var func = expression.Compile();
var orderDetail = new OrderDetail { Product = new Product { Category = new Category { CategoryName = "Beverages" } } };
var x = new { orderDetail?.Product?.Category?.CategoryName };
var result = func.DynamicInvoke(orderDetail);
var property = result.GetType().GetProperty("CategoryName");
Assert.Equal(typeof(string), property.PropertyType);
Assert.Equal("Beverages", property.GetValue(result));
}
[Fact]
public void Should_CreateProjectionFromConditionalAccess()
{
var expression = ExpressionParser.ParseLambda<OrderDetail>("it => new { ProductName = it.Product?.ProductName}");
var func = expression.Compile();
var result = func.DynamicInvoke(new OrderDetail { Product = null });
var property = result.GetType().GetProperty("ProductName");
Assert.Equal(typeof(string), property.PropertyType);
Assert.Null(property.GetValue(result));
}
[Fact]
public void Should_CreateProjectionFromNestedConditionalAccessAndAssignment()
{
var expression = ExpressionParser.ParseLambda<OrderDetail>("it => new { CategoryName = it.Product?.Category?.CategoryName}");
var func = expression.Compile();
var result = func.DynamicInvoke(new OrderDetail { Product = null });
var property = result.GetType().GetProperty("CategoryName");
Assert.Equal(typeof(string), property.PropertyType);
Assert.Null(property.GetValue(result));
}
[Fact]
public void Should_SelectByString()
{
var list = new List<OrderDetail> { new OrderDetail { Product = new Product { ProductName = "Chai" } } }.AsQueryable();
var result = DynamicExtensions.Select(list, "Product.ProductName as ProductName");
Assert.Equal("Chai", result.ElementType.GetProperty("ProductName").GetValue(result.FirstOrDefault()));
}
[Fact]
public void Should_SelectByWithUntypedIQueryableString()
{
IQueryable list = new List<OrderDetail> { new OrderDetail { Product = new Product { ProductName = "Chai" } } }.AsQueryable();
var result = DynamicExtensions.Select(list, "Product.ProductName as ProductName");
Assert.Equal("Chai", result.ElementType.GetProperty("ProductName").GetValue(result.FirstOrDefault()));
}
[Fact]
public void Should_SupportDictionaryIndexAccess()
{
var expression = ExpressionParser.ParsePredicate<Dictionary<string, object>>("it => (int)it[\"foo\"] == 1");
var func = expression.Compile();
Assert.True(func(new Dictionary<string, object> { ["foo"] = 1 }));
}
[Fact]
public void Should_SupportDictionaryIndexAccessWithNullableCast()
{
var expression = ExpressionParser.ParsePredicate<Dictionary<string, object>>("it => (Int32?)it[\"foo\"] == null");
var func = expression.Compile();
Assert.True(func(new Dictionary<string, object> { ["foo"] = null }));
}
[Fact]
public void Should_SupportArrayIndexAccess()
{
var expression = ExpressionParser.ParsePredicate<ItemWithGenericProperty<int[]>>("it => it.Value[0] == 1");
var func = expression.Compile();
Assert.True(func(new ItemWithGenericProperty<int[]> { Value = [1] }));
}
}
}

View File

@@ -78,6 +78,20 @@ namespace Radzen.Blazor.Tests
Assert.Contains(@$"target=""{target}""", component.Markup);
}
[Fact]
public void Link_Renders_DisabledParameter()
{
using var ctx = new TestContext();
var component = ctx.RenderComponent<RadzenLink>();
component.SetParametersAndRender(parameters => parameters.Add(p => p.Disabled, true));
Assert.Contains("class=\"rz-link rz-link-disabled active\"", component.Markup);
Assert.DoesNotContain("href=", component.Markup);
}
[Fact]
public void Icon_Renders_UnmatchedParameter()
{

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]
@@ -517,10 +612,10 @@ namespace Radzen.Blazor.Tests
});
});
component.Find("input").Change("13.53");
component.Find("input").Change(13.53);
var maxDollars = new Dollars(2);
Assert.Contains($" value=\"{maxDollars.ToString()}\"", component.Markup);
var maxDollars = new Dollars(maxValue);
Assert.Contains($" value=\"{maxDollars}\"", component.Markup);
Assert.Equal(component.Instance.Value, maxDollars);
}

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

@@ -1,14 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8</TargetFramework>
<TargetFramework>net9</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
<PackageReference Include="bunit.web" Version="1.2.49" />
<PackageReference Include="bunit.web" Version="1.36.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

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

@@ -101,6 +101,17 @@ namespace Radzen.Blazor
/// <value><c>true</c> if visible; otherwise, <c>false</c>.</value>
[Parameter]
public bool Visible { get; set; } = true;
/// <summary>
/// Specifies the label rotation angle in degrees. Set to <c>null</c> by default which means no rotation is applied. Has higher precedence than <see cref="LabelAutoRotation"/>.
/// </summary>
[Parameter]
public double? LabelRotation { get; set; } = null;
/// <summary>
/// Specifies the automatic label rotation angle in degrees. If set RadzenChart will automatically rotate the labels to fit the available space by the specified value. Has lower precedence than <see cref="LabelRotation"/>.
/// </summary>
[Parameter]
public double? LabelAutoRotation { get; set; } = null;
/// <inheritdoc />
protected override bool ShouldRefreshChart(ParameterView parameters)
@@ -108,6 +119,8 @@ namespace Radzen.Blazor
return DidParameterChange(parameters, nameof(Min), Min) ||
DidParameterChange(parameters, nameof(Max), Max) ||
DidParameterChange(parameters, nameof(Visible), Visible) ||
DidParameterChange(parameters, nameof(LabelRotation), LabelRotation) ||
DidParameterChange(parameters, nameof(LabelAutoRotation), LabelAutoRotation) ||
DidParameterChange(parameters, nameof(Step), Step);
}

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();
}
}
@@ -478,38 +477,30 @@ namespace Radzen.Blazor
}
/// <inheritdoc />
public virtual RenderFragment RenderTooltip(object data, double marginLeft, double marginTop, double chartHeight)
public virtual RenderFragment RenderTooltip(object data)
{
var item = (TItem)data;
var x = TooltipX(item);
var y = TooltipY(item);
return builder =>
{
if (Chart.Tooltip.Shared)
{
var category = PropertyAccess.GetValue(item, CategoryProperty);
builder.OpenComponent<ChartSharedTooltip>(0);
builder.AddAttribute(1, nameof(ChartSharedTooltip.X), x + marginLeft);
builder.AddAttribute(2, nameof(ChartSharedTooltip.Y), y + marginTop);
builder.AddAttribute(3, nameof(ChartSharedTooltip.Class), TooltipClass(item));
builder.AddAttribute(4, nameof(ChartSharedTooltip.Title), TooltipTitle(item));
builder.AddAttribute(4, nameof(ChartSharedTooltip.ChildContent), RenderSharedTooltipItems(category));
builder.AddAttribute(1, nameof(ChartSharedTooltip.Class), TooltipClass(item));
builder.AddAttribute(2, nameof(ChartSharedTooltip.Title), TooltipTitle(item));
builder.AddAttribute(3, nameof(ChartSharedTooltip.ChildContent), RenderSharedTooltipItems(category));
builder.CloseComponent();
}
else
{
builder.OpenComponent<ChartTooltip>(0);
builder.AddAttribute(1, nameof(ChartTooltip.X), x + marginLeft);
builder.AddAttribute(2, nameof(ChartTooltip.Y), y + marginTop);
builder.AddAttribute(3, nameof(ChartTooltip.ChildContent), TooltipTemplate?.Invoke(item));
builder.AddAttribute(4, nameof(ChartTooltip.Title), TooltipTitle(item));
builder.AddAttribute(5, nameof(ChartTooltip.Label), TooltipLabel(item));
builder.AddAttribute(6, nameof(ChartTooltip.Value), TooltipValue(item));
builder.AddAttribute(7, nameof(ChartTooltip.Class), TooltipClass(item));
builder.AddAttribute(8, nameof(ChartTooltip.Style), TooltipStyle(item));
builder.AddAttribute(1, nameof(ChartTooltip.ChildContent), TooltipTemplate?.Invoke(item));
builder.AddAttribute(2, nameof(ChartTooltip.Title), TooltipTitle(item));
builder.AddAttribute(3, nameof(ChartTooltip.Label), TooltipLabel(item));
builder.AddAttribute(4, nameof(ChartTooltip.Value), TooltipValue(item));
builder.AddAttribute(5, nameof(ChartTooltip.Class), TooltipClass(item));
builder.AddAttribute(6, nameof(ChartTooltip.Style), TooltipStyle(item));
builder.CloseComponent();
}
};
@@ -546,6 +537,16 @@ namespace Radzen.Blazor
};
}
/// <inheritdoc />
public Point GetTooltipPosition(object data)
{
var item = (TItem)data;
var x = TooltipX(item);
var y = TooltipY(item);
return new Point { X = x, Y = y };
}
/// <summary>
/// Gets the tooltip inline style.
/// </summary>

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;
@@ -530,6 +530,12 @@ namespace Radzen
/// Gets the dropped item.
/// </summary>
public TItem ToItem { get; internal set; }
/// <summary>
/// The data that underlies a drag-and-drop operation, known as the drag data store.
/// See <see cref="DataTransfer"/>.
/// </summary>
public DataTransfer DataTransfer { get; set; } = default!;
}
/// <summary>
@@ -551,7 +557,6 @@ namespace Radzen
/// Gets or sets a value indicating whether this item is visible.
/// </summary>
/// <value><c>true</c> if visible; otherwise, <c>false</c>.</value>
[Parameter]
public bool Visible { get; set; } = true;
/// <summary>
@@ -574,14 +579,12 @@ namespace Radzen
/// Gets or sets a value indicating whether this item is visible.
/// </summary>
/// <value><c>true</c> if visible; otherwise, <c>false</c>.</value>
[Parameter]
public bool Visible { get; set; } = true;
/// <summary>
/// Gets or sets a value indicating whether this item is visible.
/// </summary>
/// <value><c>true</c> if visible; otherwise, <c>false</c>.</value>
[Parameter]
public bool Disabled { get; set; }
/// <summary>
@@ -612,6 +615,34 @@ namespace Radzen
public RadzenListBox<TValue> ListBox { get; internal set; }
}
/// <summary>
/// Supplies information about RadzenPickList ItemRender event.
/// </summary>
public class PickListItemRenderEventArgs<TItem>
{
/// <summary>
/// Gets the data item.
/// </summary>
public TItem Item { get; internal set; }
/// <summary>
/// Gets or sets a value indicating whether this item is visible.
/// </summary>
/// <value><c>true</c> if visible; otherwise, <c>false</c>.</value>
public bool Visible { get; set; } = true;
/// <summary>
/// Gets or sets a value indicating whether this item is visible.
/// </summary>
/// <value><c>true</c> if visible; otherwise, <c>false</c>.</value>
public bool Disabled { get; set; }
/// <summary>
/// Gets or sets the row HTML attributes.
/// </summary>
public IDictionary<string, object> Attributes { get; private set; } = new Dictionary<string, object>();
}
/// <summary>
/// Supplies information about a <see cref="RadzenDatePicker{TValue}.DateRender" /> event that is being raised.
/// </summary>
@@ -758,6 +789,11 @@ namespace Radzen
/// </summary>
/// <value><c>true</c> if expandable; otherwise, <c>false</c>.</value>
public bool Expandable { get; set; }
/// <summary>
/// Gets or sets a value indicating row index.
/// </summary>
public int Index { get; set; }
}
/// <summary>
@@ -2251,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>
@@ -2289,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>
@@ -2392,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>
@@ -2866,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;
@@ -2896,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;
}
}
@@ -2917,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
{
@@ -3058,23 +3162,7 @@ namespace Radzen
/// <param name="property">The property.</param>
public static string GetProperty(string property)
{
Type type = null;
try
{
type = Type.GetType($"System.{property}");
}
catch
{
// ignore the exception and assume the property start without a type and do not need the '@' prefix
}
var propertyName = $"{(type != null ? "@" : "")}{property}";
if (propertyName.IndexOf(".") != -1)
{
return $"np({propertyName})";
}
return propertyName;
return property;
}
/// <summary>
@@ -3303,7 +3391,7 @@ namespace Radzen
var typeName = isEnum ? "Enum" : (Nullable.GetUnderlyingType(type) ?? type).Name;
var typeFunc = $@"{typeName}{(!isEnum && Nullable.GetUnderlyingType(type) != null ? "?" : "")}";
return $@"{typeFunc}(it[""{name}""])";
return $@"({typeFunc})it[""{name}""]";
}
}
@@ -3369,6 +3457,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>
@@ -3753,4 +3856,4 @@ namespace Radzen
/// </summary>
Right
}
}
}

View File

@@ -65,11 +65,8 @@ namespace Radzen
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="Microsoft.AspNetCore.Components.Routing.LocationChangedEventArgs"/> instance containing the event data.</param>
private void UriHelper_OnLocationChanged(object sender, Microsoft.AspNetCore.Components.Routing.LocationChangedEventArgs e)
{
if (this.OnNavigate != null)
{
this.OnNavigate();
}
{
this.OnNavigate?.Invoke();
}
/// <summary>

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
@@ -272,7 +271,7 @@ namespace Radzen
/// <summary>
/// The selected items
/// </summary>
protected IList<object> selectedItems = new List<object>();
protected ISet<object> selectedItems = new HashSet<object>();
/// <summary>
/// The selected item
/// </summary>
@@ -288,10 +287,10 @@ namespace Radzen
return;
}
if (selectedItems.Count != View.Cast<object>().ToList().Where(i => disabledPropertyGetter != null ? disabledPropertyGetter(i) as bool? != true : true).Count())
if (selectedItems.Count != View.Cast<object>().ToList().Where(i => disabledPropertyGetter == null || disabledPropertyGetter(i) as bool? != true).Count())
{
selectedItems.Clear();
selectedItems = View.Cast<object>().ToList().Where(i => disabledPropertyGetter != null ? disabledPropertyGetter(i) as bool? != true : true).ToList();
selectedItems = View.Cast<object>().ToList().Where(i => disabledPropertyGetter == null || disabledPropertyGetter(i) as bool? != true).ToHashSet(ItemComparer);
}
else
{
@@ -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))
@@ -453,6 +458,11 @@ namespace Radzen
{
disabledPropertyGetter = GetGetter(DisabledProperty, type);
}
if (selectedItems.Count == 0)
{
selectedItems = new HashSet<object>(ItemComparer);
}
}
}
@@ -675,8 +685,10 @@ namespace Radzen
if (selectedIndex >= 0 && selectedIndex <= items.Count() - 1)
{
await JSRuntime.InvokeAsync<string>("Radzen.setInputValue", search, $"{searchText}".Trim());
var itemToSelect = items.ElementAtOrDefault(selectedIndex);
await JSRuntime.InvokeAsync<string>("Radzen.setInputValue", search, $"{searchText}".Trim());
if (itemToSelect != null)
{
await OnSelectItem(itemToSelect, true);
@@ -730,9 +742,10 @@ namespace Radzen
}
else
{
var filteredItems = Query.Where(TextProperty, args.Key, StringFilterOperator.StartsWith, FilterCaseSensitivity.CaseInsensitive)
.Cast<object>()
.ToList();
var filteredItems = (!string.IsNullOrEmpty(TextProperty) ?
Query.Where(TextProperty, args.Key, StringFilterOperator.StartsWith, FilterCaseSensitivity.CaseInsensitive) :
Query)
.Cast(Query.ElementType).Cast<dynamic>().ToList();
if (previousKey != args.Key)
@@ -986,7 +999,7 @@ namespace Radzen
{
if (Multiple)
{
return selectedItems.IndexOf(item) != -1;
return selectedItems.Contains(item);
}
else
{
@@ -1130,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)
@@ -1212,18 +1229,14 @@ namespace Radzen
}
else
{
selectedItems = selectedItems.AsQueryable().Where(DynamicLinqCustomTypeProvider.ParsingConfig, $@"!object.Equals(it.{ValueProperty},@0)", value).ToList();
selectedItems = selectedItems.AsQueryable().Where(i => !object.Equals(GetItemOrValueFromProperty(i, ValueProperty), value)).ToHashSet(ItemComparer);
}
}
else
{
if (!selectedItems.Any(i => object.Equals(i, item)))
if (!selectedItems.Add(item))
{
selectedItems.Add(item);
}
else
{
selectedItems = selectedItems.Where(i => !object.Equals(i, item)).ToList();
selectedItems.Remove(item);
}
}
}
@@ -1247,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
@@ -1264,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;
@@ -1274,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);
}
@@ -1285,7 +1316,7 @@ namespace Radzen
}
else
{
selectedItems = ((IEnumerable)values).Cast<object>().ToList();
selectedItems = values.Cast<object>().ToHashSet(ItemComparer);
}
}
@@ -1297,6 +1328,11 @@ namespace Radzen
}
}
/// <summary>
/// For lists of objects, an IEqualityComparer to control how selected items are determined
/// </summary>
[Parameter] public IEqualityComparer<object> ItemComparer { get; set; }
internal bool IsItemSelectedByValue(object v)
{
switch (internalValue)

View File

@@ -0,0 +1,126 @@
using Radzen;
using System.Linq.Expressions;
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 readonly Func<string, Type> typeLocator = type => AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a => a.GetTypes()).FirstOrDefault(t => t.FullName.Replace("+", ".") == type);
/// <summary>
/// Filters using the specified filter descriptors.
/// </summary>
public static IQueryable<T> Where<T>(
this IQueryable<T> source,
string predicate,
object[] parameters = null, object[] otherParameters = null)
{
try
{
if (parameters != null && !string.IsNullOrEmpty(predicate))
{
for (var i = 0; i < parameters.Length; i++)
{
object param = parameters[i];
string value = param switch
{
string s when s == string.Empty => @"""""",
null => "null",
string s => @$"""{s.Replace("\"", "\\\"")}""",
bool b => b.ToString().ToLower(),
Guid g => $"Guid.Parse(\"{g}\")",
DateTime dt => $"DateTime.Parse(\"{dt:yyyy-MM-ddTHH:mm:ss.fffZ}\")",
DateTimeOffset dto => $"DateTime.Parse(\"{dto.UtcDateTime:yyyy-MM-ddTHH:mm:ss.fffZ}\")",
DateOnly d => $"DateOnly.Parse(\"{d:yyy-MM-dd}\")",
TimeOnly t => $"TimeOnly.Parse(\"{t:HH:mm:ss}\")",
_ => param.ToString()
};
predicate = predicate.Replace($"@{i}", $"{value}");
}
}
predicate = (predicate == "true" ? "" : predicate)
.Replace("DateTime(", "DateTime.Parse(")
.Replace("DateTimeOffset(", "DateTimeOffset.Parse(")
.Replace("DateOnly(", "DateOnly.Parse(")
.Replace("Guid(", "Guid.Parse(")
.Replace(" = ", " == ");
return !string.IsNullOrEmpty(predicate) ?
source.Where(ExpressionParser.ParsePredicate<T>(predicate, typeLocator)) : source;
}
catch (Exception ex)
{
throw new InvalidOperationException($"Invalid predicate: {predicate}", ex);
}
}
/// <summary>
/// Sorts the elements of a sequence in ascending or descending order according to a key.
/// </summary>
public static IOrderedQueryable<T> OrderBy<T>(
this IQueryable<T> source,
string selector,
object[] parameters = null)
{
try
{
return QueryableExtension.OrderBy(source, selector);
}
catch (Exception ex)
{
throw new InvalidOperationException($"Invalid selector: {selector}.", ex);
}
}
/// <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)
{
return source.Select(selector, expression => ExpressionParser.ParseLambda<T>(expression));
}
/// <summary>
/// Projects each element of a sequence into a collection of property values.
/// </summary>
public static IQueryable Select(this IQueryable source, string selector, object[] parameters = null)
{
return source.Select(selector, expression => ExpressionParser.ParseLambda(expression, source.ElementType));
}
private static IQueryable Select(this IQueryable source, string selector, Func<string, LambdaExpression> lambdaCreator)
{
try
{
var properties = selector
.Replace("new (", "").Replace(")", "").Replace("new {", "").Replace("}", "").Trim()
.Split(",", StringSplitOptions.RemoveEmptyEntries);
selector = string.Join(", ", properties
.Select(s => (s.Contains(" as ") ? s.Split(" as ").LastOrDefault().Trim().Replace(".", "_") : s.Trim().Replace(".", "_")) +
" = " + $"it.{s.Split(" as ").FirstOrDefault().Replace(".", "?.").Trim()}"));
if (string.IsNullOrEmpty(selector))
{
return source;
}
var lambda = lambdaCreator($"it => new {{ {selector} }}");
return source.Provider.CreateQuery(Expression.Call(typeof(Queryable), nameof(Queryable.Select),
[source.ElementType, lambda.Body.Type], source.Expression, Expression.Quote(lambda)));
}
catch (Exception ex)
{
throw new InvalidOperationException($"Invalid selector: {selector}.", ex);
}
}
}
}

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

@@ -0,0 +1,548 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
namespace Radzen;
static class DynamicTypeFactory
{
public static Type CreateType(string typeName, string[] propertyNames, Type[] propertyTypes)
{
if (propertyNames.Length != propertyTypes.Length)
{
throw new ArgumentException("Property names and types count mismatch.");
}
var assemblyName = new AssemblyName("DynamicTypesAssembly");
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
var moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicTypesModule");
var typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.Sealed);
for (int i = 0; i < propertyNames.Length; i++)
{
var fieldBuilder = typeBuilder.DefineField("_" + propertyNames[i], propertyTypes[i], FieldAttributes.Private);
var propertyBuilder = typeBuilder.DefineProperty(propertyNames[i], PropertyAttributes.None, propertyTypes[i], null);
var getterMethod = typeBuilder.DefineMethod(
"get_" + propertyNames[i],
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
propertyTypes[i],
Type.EmptyTypes);
var getterIl = getterMethod.GetILGenerator();
getterIl.Emit(OpCodes.Ldarg_0);
getterIl.Emit(OpCodes.Ldfld, fieldBuilder);
getterIl.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(getterMethod);
var setterMethod = typeBuilder.DefineMethod(
"set_" + propertyNames[i],
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
null,
[propertyTypes[i]]);
var setterIl = setterMethod.GetILGenerator();
setterIl.Emit(OpCodes.Ldarg_0);
setterIl.Emit(OpCodes.Ldarg_1);
setterIl.Emit(OpCodes.Stfld, fieldBuilder);
setterIl.Emit(OpCodes.Ret);
propertyBuilder.SetSetMethod(setterMethod);
}
var dynamicType = typeBuilder.CreateType();
return dynamicType;
}
}
class ExpressionSyntaxVisitor : CSharpSyntaxVisitor<Expression>
{
private readonly ParameterExpression parameter;
private readonly Func<string, Type> typeLocator;
public ExpressionSyntaxVisitor(ParameterExpression parameter, Func<string, Type> typeLocator)
{
this.parameter = parameter;
this.typeLocator = typeLocator;
}
public override Expression VisitBinaryExpression(BinaryExpressionSyntax node)
{
var left = Visit(node.Left);
var right = ConvertIfNeeded(Visit(node.Right), left.Type);
return Expression.MakeBinary(ParseBinaryOperator(node.OperatorToken), left, right);
}
public override Expression VisitMemberAccessExpression(MemberAccessExpressionSyntax node)
{
var expression = Visit(node.Expression) ?? parameter;
return Expression.PropertyOrField(expression, node.Name.Identifier.Text);
}
public override Expression VisitLiteralExpression(LiteralExpressionSyntax node)
{
return Expression.Constant(ParseLiteral(node));
}
public override Expression VisitIdentifierName(IdentifierNameSyntax node)
{
if (node.Identifier.Text == parameter.Name)
{
return parameter;
}
var type = GetType(node.Identifier.Text);
if (type != null)
{
return Expression.Constant(type);
}
throw new NotSupportedException("Unsupported identifier: " + node.Identifier.Text);
}
public override Expression VisitConditionalExpression(ConditionalExpressionSyntax node)
{
var condition = Visit(node.Condition);
var whenTrue = Visit(node.WhenTrue);
var whenFalse = Visit(node.WhenFalse);
if (whenTrue.Type != whenFalse.Type)
{
if (whenTrue.Type == typeof(object))
{
whenTrue = Expression.Convert(whenTrue, whenFalse.Type);
}
else if (whenFalse.Type == typeof(object))
{
whenFalse = Expression.Convert(whenFalse, whenTrue.Type);
}
else
{
throw new NotSupportedException("Conditional expression types mismatch: " + whenTrue.Type + " and " + whenFalse.Type);
}
}
return Expression.Condition(condition, whenTrue, whenFalse);
}
public override Expression VisitParenthesizedExpression(ParenthesizedExpressionSyntax node)
{
return Visit(node.Expression);
}
private Type GetType(string typeName)
{
var nullable = typeName.EndsWith('?');
if (nullable)
{
typeName = typeName[..^1];
}
var type = typeName switch
{
nameof(Int32) => typeof(int),
nameof(Int64) => typeof(long),
nameof(Double) => typeof(double),
nameof(Single) => typeof(float),
nameof(Decimal) => typeof(decimal),
nameof(String) => typeof(string),
nameof(Boolean) => typeof(bool),
nameof(DateTime) => typeof(DateTime),
nameof(DateOnly) => typeof(DateOnly),
nameof(DateTimeOffset) => typeof(DateTimeOffset),
nameof(TimeOnly) => typeof(TimeOnly),
nameof(Guid) => typeof(Guid),
nameof(Char) => typeof(char),
"int" => typeof(int),
"long" => typeof(long),
"double" => typeof(double),
"float" => typeof(float),
"decimal" => typeof(decimal),
"string" => typeof(string),
"char" => typeof(char),
"bool" => typeof(bool),
_ => typeLocator?.Invoke(typeName)
};
if (nullable && type != null)
{
type = typeof(Nullable<>).MakeGenericType(type);
}
return type;
}
public override Expression VisitCastExpression(CastExpressionSyntax node)
{
var typeName = node.Type.ToString();
var targetType = GetType(typeName);
if (targetType == null)
{
throw new NotSupportedException("Unsupported cast type: " + node.Type);
}
var operand = Visit(node.Expression);
return Expression.Convert(operand, targetType);
}
public override Expression VisitImplicitArrayCreationExpression(ImplicitArrayCreationExpressionSyntax node)
{
var expressions = node.Initializer.Expressions.Select(Visit).ToArray();
var elementType = expressions.Length > 0 ? expressions[0].Type : typeof(object);
return Expression.NewArrayInit(elementType, expressions);
}
public override Expression VisitArrayCreationExpression(ArrayCreationExpressionSyntax node)
{
var elementType = GetType(node.Type.ElementType.ToString());
if (elementType == null)
{
throw new NotSupportedException("Unsupported array element type: " + node.Type.ElementType);
}
var expressions = node.Initializer.Expressions.Select(e => ConvertIfNeeded(Visit(e), elementType));
return Expression.NewArrayInit(elementType, expressions);
}
private static MethodCallExpression CallStaticMethod(Type type, string methodName, Expression[] arguments, Type[] argumentTypes)
{
var methodInfo = type.GetMethod(methodName, BindingFlags.Public | BindingFlags.Static, argumentTypes);
if (methodInfo != null)
{
return Expression.Call(methodInfo, arguments);
}
throw new NotSupportedException("Method not found: " + methodName);
}
public override Expression DefaultVisit(SyntaxNode node)
{
throw new NotSupportedException("Unsupported syntax: " + node.GetType().Name);
}
public override Expression VisitSimpleLambdaExpression(SimpleLambdaExpressionSyntax node)
{
var body = Visit(node.Body);
return Expression.Lambda(body, parameter);
}
private Expression VisitArgument(Expression instance, ArgumentSyntax argument)
{
if (argument.Expression is SimpleLambdaExpressionSyntax lambda)
{
var itemType = GetItemType(instance.Type);
var visitor = new ExpressionSyntaxVisitor(Expression.Parameter(itemType, lambda.Parameter.Identifier.Text), typeLocator);
return visitor.Visit(lambda);
}
return Visit(argument.Expression);
}
private static Expression ConvertIfNeeded(Expression expression, Type targetType)
{
if (expression is not LambdaExpression)
{
return expression.Type == targetType ? expression : Expression.Convert(expression, targetType);
}
return expression;
}
public override Expression VisitInvocationExpression(InvocationExpressionSyntax node)
{
if (node.Expression is MemberAccessExpressionSyntax methodCall)
{
var instance = Visit(methodCall.Expression);
var arguments = node.ArgumentList.Arguments.Select(a => VisitArgument(instance, a)).ToArray();
var argumentTypes = arguments.Select(a => a.Type).ToArray();
if (instance is ConstantExpression constant && constant.Value is Type type)
{
return CallStaticMethod(type, methodCall.Name.Identifier.Text, arguments, argumentTypes);
}
var instanceType = instance.Type;
var methodInfo = instanceType.GetMethod(methodCall.Name.Identifier.Text, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static, argumentTypes);
if (methodInfo == null)
{
methodInfo = typeof(Enumerable)
.GetMethods(BindingFlags.Public | BindingFlags.Static)
.FirstOrDefault(m => m.Name == methodCall.Name.Identifier.Text && m.GetParameters().Length == arguments.Length + 1);
if (methodInfo != null)
{
var argumentType = GetItemType(instanceType);
var genericMethod = methodInfo.MakeGenericMethod(argumentType);
var parameters = genericMethod.GetParameters();
var argumentsWithInstance = new[] { instance }.Concat(arguments).ToArray();
if (parameters.Length != argumentsWithInstance.Length)
{
throw new NotSupportedException("Unsupported method call: " + methodCall.Name.Identifier.Text);
}
return Expression.Call(genericMethod, argumentsWithInstance.Select((a, index) => ConvertIfNeeded(a, parameters[index].ParameterType)));
}
}
if (methodInfo == null)
{
throw new NotSupportedException("Unsupported method call: " + methodCall.Name.Identifier.Text);
}
return Expression.Call(instance, methodInfo, arguments);
}
throw new NotSupportedException("Unsupported invocation expression: " + node.ToString());
}
private static Type GetItemType(Type enumerableOrArray)
{
return enumerableOrArray.IsArray ? enumerableOrArray.GetElementType() : enumerableOrArray.GetGenericArguments()[0];
}
private static object ParseLiteral(LiteralExpressionSyntax literal)
{
return literal.Kind() switch
{
SyntaxKind.StringLiteralExpression => literal.Token.ValueText,
SyntaxKind.NumericLiteralExpression => literal.Token.Value,
SyntaxKind.TrueLiteralExpression => true,
SyntaxKind.FalseLiteralExpression => false,
SyntaxKind.NullLiteralExpression => null,
_ => throw new NotSupportedException("Unsupported literal: " + literal),
};
}
private static ExpressionType ParseBinaryOperator(SyntaxToken token)
{
return token.Kind() switch
{
SyntaxKind.EqualsEqualsToken => ExpressionType.Equal,
SyntaxKind.LessThanToken => ExpressionType.LessThan,
SyntaxKind.GreaterThanToken => ExpressionType.GreaterThan,
SyntaxKind.LessThanEqualsToken => ExpressionType.LessThanOrEqual,
SyntaxKind.GreaterThanEqualsToken => ExpressionType.GreaterThanOrEqual,
SyntaxKind.ExclamationEqualsToken => ExpressionType.NotEqual,
SyntaxKind.AmpersandAmpersandToken => ExpressionType.AndAlso,
SyntaxKind.BarBarToken => ExpressionType.OrElse,
SyntaxKind.QuestionQuestionToken => ExpressionType.Coalesce,
_ => throw new NotSupportedException("Unsupported operator: " + token.Text),
};
}
private static string GetPropertyNameFromInitializer(AnonymousObjectMemberDeclaratorSyntax initializer)
{
if (initializer.NameEquals != null)
{
return initializer.NameEquals.Name.Identifier.Text;
}
var expression = initializer.Expression;
if (expression is MemberAccessExpressionSyntax memberAccess)
{
expression = memberAccess.Name;
}
while (expression is ConditionalAccessExpressionSyntax conditionalAccess)
{
expression = conditionalAccess.WhenNotNull;
}
if (expression is MemberBindingExpressionSyntax memberBinding)
{
expression = memberBinding.Name;
}
if (expression is IdentifierNameSyntax identifier)
{
return identifier.Identifier.Text;
}
throw new NotSupportedException("Unsupported initializer: " + initializer.ToString());
}
public override Expression VisitAnonymousObjectCreationExpression(AnonymousObjectCreationExpressionSyntax node)
{
var properties = node.Initializers.Select(init =>
{
var name = GetPropertyNameFromInitializer(init);
var value = Visit(init.Expression);
return new { Name = name, Value = value };
}).ToList();
var propertyNames = properties.Select(p => p.Name).ToArray();
var propertyTypes = properties.Select(p => p.Value.Type).ToArray();
var dynamicType = DynamicTypeFactory.CreateType(parameter.Type.Name, propertyNames, propertyTypes);
var bindings = properties.Select(p => Expression.Bind(dynamicType.GetProperty(p.Name), p.Value));
return Expression.MemberInit(Expression.New(dynamicType), bindings);
}
private Expression instance;
public override Expression VisitConditionalAccessExpression(ConditionalAccessExpressionSyntax node)
{
var expression = Visit(node.Expression);
instance = expression;
var whenNotNull = Visit(node.WhenNotNull);
instance = null;
if (expression.Type.IsValueType && Nullable.GetUnderlyingType(expression.Type) == null)
{
throw new NotSupportedException("Conditional access is not supported on non-nullable value types: " + expression.Type);
}
if (!expression.Type.IsValueType || Nullable.GetUnderlyingType(expression.Type) != null)
{
return Expression.Condition(Expression.NotEqual(expression, Expression.Constant(null, expression.Type)),
whenNotNull, Expression.Default(whenNotNull.Type)
);
}
return whenNotNull;
}
public override Expression VisitMemberBindingExpression(MemberBindingExpressionSyntax node)
{
if (instance is Expression expression)
{
return Expression.PropertyOrField(expression, node.Name.Identifier.Text);
}
throw new NotSupportedException("Unsupported member binding: " + node.ToString());
}
public override Expression VisitElementAccessExpression(ElementAccessExpressionSyntax node)
{
var expression = Visit(node.Expression);
var arguments = node.ArgumentList.Arguments.Select(arg => Visit(arg.Expression)).ToArray();
if (expression.Type.IsArray)
{
return Expression.ArrayIndex(expression, arguments);
}
var indexer = expression.Type.GetProperties()
.FirstOrDefault(p => p.GetIndexParameters().Length == arguments.Length);
if (indexer != null)
{
return Expression.MakeIndex(expression, indexer, arguments);
}
throw new NotSupportedException("Unsupported element access: " + node.ToString());
}
public override Expression VisitPrefixUnaryExpression(PrefixUnaryExpressionSyntax node)
{
var operand = Visit(node.Operand);
return node.OperatorToken.Kind() switch
{
SyntaxKind.MinusToken => Expression.Negate(operand),
SyntaxKind.ExclamationToken => Expression.Not(operand),
_ => throw new NotSupportedException("Unsupported unary operator: " + node.OperatorToken.Text),
};
}
}
/// <summary>
/// Parse lambda expressions from strings.
/// </summary>
public static class ExpressionParser
{
/// <summary>
/// Parses a lambda expression that returns a boolean value.
/// </summary>
public static Expression<Func<T, bool>> ParsePredicate<T>(string expression, Func<string, Type> typeLocator = null)
{
return ParseLambda<T, bool>(expression, typeLocator);
}
/// <summary>
/// Parses a lambda expression that returns a typed result.
/// </summary>
public static Expression<Func<T, TResult>> ParseLambda<T, TResult>(string expression, Func<string, Type> typeLocator = null)
{
var (parameter, body) = Parse<T>(expression, typeLocator);
return Expression.Lambda<Func<T, TResult>>(body, parameter);
}
private static (ParameterExpression, Expression) Parse<T>(string expression, Func<string, Type> typeLocator)
{
var syntaxTree = CSharpSyntaxTree.ParseText(expression);
var root = syntaxTree.GetRoot();
var lambdaExpression = root.DescendantNodes().OfType<SimpleLambdaExpressionSyntax>().FirstOrDefault();
if (lambdaExpression == null)
{
throw new ArgumentException("Invalid lambda expression.");
}
var parameter = Expression.Parameter(typeof(T), lambdaExpression.Parameter.Identifier.Text);
var visitor = new ExpressionSyntaxVisitor(parameter, typeLocator);
var body = visitor.Visit(lambdaExpression.Body);
return (parameter, body);
}
/// <summary>
/// Parses a lambda expression that returns untyped result.
/// </summary>
public static LambdaExpression ParseLambda<T>(string expression, Func<string, Type> typeLocator = null)
{
var (parameter, body) = Parse<T>(expression, typeLocator);
return Expression.Lambda(body, parameter);
}
/// <summary>
/// Parses a lambda expression that returns untyped result.
/// </summary>
public static LambdaExpression ParseLambda(string expression, Type type, Func<string, Type> typeLocator = null)
{
var syntaxTree = CSharpSyntaxTree.ParseText(expression);
var root = syntaxTree.GetRoot();
var lambdaExpression = root.DescendantNodes().OfType<SimpleLambdaExpressionSyntax>().FirstOrDefault();
if (lambdaExpression == null)
{
throw new ArgumentException("Invalid lambda expression.");
}
var parameter = Expression.Parameter(type, lambdaExpression.Parameter.Identifier.Text);
var visitor = new ExpressionSyntaxVisitor(parameter, typeLocator);
var body = visitor.Visit(lambdaExpression.Body);
return Expression.Lambda(body, parameter);
}
}

View File

@@ -50,20 +50,24 @@ namespace Radzen.Blazor
/// <param name="valueScale">The value scale.</param>
/// <returns>RenderFragment.</returns>
RenderFragment RenderOverlays(ScaleBase categoryScale, ScaleBase valueScale);
/// <summary>
/// Renders the series tooltip.
/// </summary>
/// <param name="data">The data.</param>
/// <param name="marginLeft">The left margin.</param>
/// <param name="marginTop">The right margin.</param>
/// <param name="chartHeight">Height of the whole char area.</param>
/// <returns>RenderFragment.</returns>
RenderFragment RenderTooltip(object data, double marginLeft, double marginTop, double chartHeight);
RenderFragment RenderTooltip(object data);
/// <summary>
/// Renders a tooltip item with the specified data to be displayed in a shared tooltip
/// </summary>
RenderFragment RenderSharedTooltipItem(object category);
/// <summary>
/// Get position of the series tooltip.
/// </summary>
/// <param name="data">The data.</param>
/// <returns>Position.</returns>
Point GetTooltipPosition(object data);
/// <summary>
/// Renders the legend item.
/// </summary>
/// <returns>RenderFragment.</returns>

View File

@@ -28,6 +28,12 @@ namespace Radzen.Blazor
/// <summary>
/// Renders tooltip
/// </summary>
RenderFragment RenderTooltip(double mouseX, double mouseY, double marginLeft, double marginTop);
RenderFragment RenderTooltip(double mouseX, double mouseY);
/// <summary>
/// Get position of the overlay tooltip.
/// </summary>
/// <returns>Position.</returns>
Point GetTooltipPosition(double mouseX, double mouseY);
}
}

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)
@@ -113,12 +113,12 @@ namespace Radzen.Blazor
Round = false;
}
if (step <= 0)
if (step == 0)
{
throw new ArgumentOutOfRangeException("Step must be greater than zero");
throw new ArgumentOutOfRangeException("Step must be non-zero");
}
return (start, end, step);
return (start, end, Math.Abs(step));
}
}
}

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

@@ -3,14 +3,14 @@
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<NoWarn>BL9993;BL0007;BL0005</NoWarn>
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
<TargetFrameworks>net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
<RazorLangVersion>7.0</RazorLangVersion>
<LangVersion>latest</LangVersion>
<OutputType>Library</OutputType>
<IsPackable>true</IsPackable>
<PackageId>Radzen.Blazor</PackageId>
<Product>Radzen.Blazor</Product>
<Version>5.2.10</Version>
<Version>6.1.2</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,14 +24,16 @@
<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" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Condition="'$(TargetFramework)' == 'net7.0'" Version="7.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components" Condition="'$(TargetFramework)' == 'net8.0'" Version="8.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Condition="'$(TargetFramework)' == 'net8.0'" Version="8.0.0" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.3.7" />
<PackageReference Include="Microsoft.AspNetCore.Components" Condition="'$(TargetFramework)' == 'net9.0'" Version="9.*-*" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Condition="'$(TargetFramework)' == 'net9.0'" Version="9.*-*" />
</ItemGroup>
<ItemGroup>
@@ -52,10 +54,10 @@
</PropertyGroup>
<ItemGroup>
<Sass Include="$(MSBuildProjectDirectory)/themes/*.scss" Exclude="$(MSBuildProjectDirectory)/themes/_*.scss" Condition="'$(TargetFramework)' == 'net6.0'" />
<Sass Include="$(MSBuildProjectDirectory)/themes/*.scss" Exclude="$(MSBuildProjectDirectory)/themes/_*.scss" Condition="'$(TargetFramework)' == 'net8.0'" />
</ItemGroup>
<Target Name="Sass" BeforeTargets="BeforeBuild" Condition="'$(TargetFramework)' == 'net6.0'">
<Target Name="Sass" BeforeTargets="BeforeBuild" Condition="'$(TargetFramework)' == 'net8.0'">
<PropertyGroup>
<_SassFileList>@(Sass->'&quot;%(FullPath)&quot;', ' ')</_SassFileList>
<DartSassBuilderArgs>files $(_SassFileList) --outputstyle $(DartSassOutputStyle) --level $(DartSassOutputLevel)</DartSassBuilderArgs>
@@ -64,7 +66,7 @@
<Message Text="Converted SassFile list to argument" Importance="$(DartSassMessageLevel)" />
</Target>
<Target Name="MoveCss" AfterTargets="AfterCompile" Condition="'$(TargetFramework)' == 'net6.0'">
<Target Name="MoveCss" AfterTargets="AfterCompile" Condition="'$(TargetFramework)' == 'net8.0'">
<ItemGroup>
<CssFile Include="$(MSBuildProjectDirectory)/themes/*.css" />
</ItemGroup>

View File

@@ -113,7 +113,7 @@ namespace Radzen.Blazor
/// <summary>
/// Refreshes this instance.
/// </summary>
internal void Refresh()
public void Refresh()
{
StateHasChanged();
}
@@ -163,6 +163,8 @@ namespace Radzen.Blazor
internal async System.Threading.Tasks.Task SelectItem(RadzenAccordionItem item, bool? value = null)
{
if(item.Disabled) return;
await CollapseAll(item);
var itemIndex = items.IndexOf(item);
@@ -178,7 +180,7 @@ namespace Radzen.Blazor
await Expand.InvokeAsync(itemIndex);
}
item.SetSelected(value ?? !selected);
await item.SetSelected(value ?? !selected);
if (!Multiple)
{
@@ -196,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,31 @@ 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.
/// </summary>
/// <value><c>true</c> if disabled; otherwise, <c>false</c>.</value>
[Parameter]
public bool Disabled { get; set; }
/// <summary>
/// Gets or sets the title attribute of the expand button.
@@ -132,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>
@@ -144,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

@@ -4,6 +4,8 @@ using Radzen.Blazor.Rendering;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Routing;
using Microsoft.JSInterop;
using System.Threading;
using System.Runtime.ExceptionServices;
namespace Radzen.Blazor
{
@@ -26,7 +28,7 @@ namespace Radzen.Blazor
{
var classList = ClassList.Create("rz-body")
.Add("rz-body-expanded", Expanded);
return classList.ToString();
}
@@ -110,7 +112,7 @@ namespace Radzen.Blazor
{
if (IsJSRuntimeAvailable && Layout != null)
{
JSRuntime.InvokeVoidAsync("eval", $"document.getElementById('{GetId()}').scrollTop = 0");
JSRuntime.InvokeVoidAsync("eval", $"try{{document.getElementById('{GetId()}').scrollTop = 0}}catch(e){{}}");
}
}

View File

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

View File

@@ -0,0 +1,35 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
namespace Radzen.Blazor
{
/// <summary>
/// RadzenCardGroup component.
/// </summary>
public partial class RadzenCardGroup : RadzenComponentWithChildren
{
/// <summary>
/// Toggles the responsive mode of the component. If set to <c>true</c> (the default) the component will be
/// expanded on larger displays and collapsed on touch devices. Set to <c>false</c> if you want to disable this behavior.
/// </summary>
[Parameter]
public bool Responsive { get; set; } = true;
/// <inheritdoc />
protected override string GetComponentCssClass()
{
var classList = new List<string>();
classList.Add("rz-card-group");
if (Responsive)
{
classList.Add("rz-card-group-responsive");
}
return string.Join(" ", classList);
}
}
}

View File

@@ -0,0 +1,43 @@
@inherits RadzenComponent
@using System.Linq
@using Microsoft.JSInterop
<CascadingValue Value=this>
@if (Visible)
{
<section @ref=@Element style=@Style @attributes=@Attributes class=@GetCssClass() id=@GetId() tabindex="0">
@if(AllowPaging && (PagerPosition == PagerPosition.Top || PagerPosition == PagerPosition.TopAndBottom))
{
<RadzenStack class="rz-carousel-pager rz-carousel-pager-top" Orientation="Orientation.Horizontal" AlignItems="AlignItems.Center" JustifyContent="JustifyContent.Center" Wrap="FlexWrap.Wrap">
@foreach (var item in items)
{
var index = items.IndexOf(item);
<a @onclick="@(args => Navigate(index))" class="rz-carousel-pager-button @(index == selectedIndex ? "rz-state-active" : "")"></a>
}
</RadzenStack>
}
@if(AllowNavigation)
{
<RadzenButton class="rz-carousel-prev" Icon="@PrevIcon" Text="@PrevText" ButtonStyle="@ButtonStyle" Size="@ButtonSize" Variant="@ButtonVariant" Shade="@ButtonShade"
Click=@Prev />
<RadzenButton class="rz-carousel-next" Icon="@NextIcon" Text="@NextText" ButtonStyle="@ButtonStyle" Size="@ButtonSize" Variant="@ButtonVariant" Shade="@ButtonShade"
Click=@Next/>
}
<ul class="rz-carousel-items"
@ontouchstart="OnTouchStart"
@ontouchend="OnTouchEnd"
@ontouchcancel="OnTouchCancel">
@Items
</ul>
@if(AllowPaging && (PagerPosition == PagerPosition.Bottom || PagerPosition == PagerPosition.TopAndBottom))
{
<RadzenStack class="rz-carousel-pager rz-carousel-pager-bottom" Orientation="Orientation.Horizontal" AlignItems="AlignItems.Center" JustifyContent="JustifyContent.Center" Wrap="FlexWrap.Wrap">
@foreach (var item in items)
{
var index = items.IndexOf(item);
<a @onclick="@(args => Navigate(index))" class="rz-carousel-pager-button @(index == selectedIndex ? "rz-state-active" : "")"></a>
}
</RadzenStack>
}
</section>
}
</CascadingValue>

View File

@@ -0,0 +1,367 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.JSInterop;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
namespace Radzen.Blazor
{
/// <summary>
/// RadzenCarousel component.
/// </summary>
/// <example>
/// <code>
/// &lt;RadzenCarousel Change=@(args => Console.WriteLine($"Selected index is: {args}"))&gt;
/// &lt;Items&gt;
/// &lt;RadzenCarouselItem&gt;
/// Details for Orders
/// &lt;/RadzenCarouselItem&gt;
/// &lt;RadzenCarousel&gt;
/// Details for Employees
/// &lt;/RadzenCarouselItem&gt;
/// &lt;/Items&gt;
/// &lt;/RadzenCarousel&gt;
/// </code>
/// </example>
public partial class RadzenCarousel : RadzenComponent
{
/// <summary>
/// Gets or sets the items.
/// </summary>
/// <value>The items.</value>
[Parameter]
public RenderFragment Items { get; set; }
internal List<RadzenCarouselItem> items = new List<RadzenCarouselItem>();
/// <summary>
/// Adds the item.
/// </summary>
/// <param name="item">The item.</param>
public void AddItem(RadzenCarouselItem item)
{
if (!items.Contains(item))
{
items.Add(item);
StateHasChanged();
}
}
/// <summary>
/// Removes the item.
/// </summary>
/// <param name="item">The item.</param>
public void RemoveItem(RadzenCarouselItem item)
{
if (items.Contains(item))
{
items.Remove(item);
if (!disposed)
{
try { InvokeAsync(StateHasChanged); } catch { }
}
}
}
/// <inheritdoc />
protected override string GetComponentCssClass()
{
return $"rz-carousel {(AllowNavigation ? "" : "rz-carousel-no-navigation")} {(PagerOverlay ? "rz-carousel-pager-overlay" : "")}".Trim();
}
/// <summary>
/// Navigates to specific index.
/// </summary>
public async Task Navigate(int index)
{
if (Auto)
{
await Reset();
}
await GoTo(index);
}
async Task Prev()
{
await Navigate(selectedIndex == 0 ? items.Count - 1 : selectedIndex - 1);
}
async Task Next()
{
await Navigate(selectedIndex == items.Count - 1 ? 0 : selectedIndex + 1);
}
async Task GoTo(int index)
{
if (index >= 0 && index <= items.Count - 1 && selectedIndex != index)
{
selectedIndex = index;
await SelectedIndexChanged.InvokeAsync(selectedIndex);
await Change.InvokeAsync(selectedIndex);
await JSRuntime.InvokeVoidAsync("Radzen.scrollCarouselItem", items[selectedIndex].element);
StateHasChanged();
}
}
/// <summary>
/// Stops the auto-cycle timer.
/// </summary>
public void Stop()
{
timer?.Change(Timeout.Infinite, Timeout.Infinite);
}
/// <summary>
/// Starts the auto-cycle timer.
/// </summary>
public void Start()
{
timer?.Change(TimeSpan.FromMilliseconds(Interval), TimeSpan.FromMilliseconds(Interval));
}
/// <summary>
/// Resets the auto-cycle timer.
/// </summary>
public async Task Reset()
{
Stop();
Start();
await Task.CompletedTask;
}
/// <summary>
/// Gets or sets the selected index.
/// </summary>
/// <value>The selected index.</value>
[Parameter]
public int SelectedIndex { get; set; }
private int selectedIndex;
/// <summary>
/// Gets or sets the selected index changed callback.
/// </summary>
/// <value>The selected index changed callback.</value>
[Parameter]
public EventCallback<int> SelectedIndexChanged { get; set; }
/// <summary>
/// Gets or sets the change callback.
/// </summary>
/// <value>The change callback.</value>
[Parameter]
public EventCallback<int> Change { get; set; }
/// <inheritdoc />
public override async Task SetParametersAsync(ParameterView parameters)
{
var shouldUpdate = false;
if (parameters.DidParameterChange(nameof(SelectedIndex), SelectedIndex))
{
selectedIndex = parameters.GetValueOrDefault<int>(nameof(SelectedIndex));
shouldUpdate = true;
}
if (parameters.DidParameterChange(nameof(Auto), Auto) ||
parameters.DidParameterChange(nameof(Interval), Interval))
{
if (parameters.GetValueOrDefault<bool>(nameof(Auto)))
{
await Reset();
}
else
{
Stop();
}
}
await base.SetParametersAsync(parameters);
if (shouldUpdate)
{
await JSRuntime.InvokeVoidAsync("Radzen.scrollCarouselItem", items[selectedIndex].element);
}
}
/// <summary>
/// Gets or sets a value indicating whether this <see cref="RadzenCarousel"/> cycle is automatic.
/// </summary>
/// <value><c>true</c> if cycle automatic; otherwise, <c>false</c>.</value>
[Parameter]
public bool Auto { get; set; } = true;
/// <summary>
/// Gets or sets the auto-cycle interval in milliseconds.
/// </summary>
[Parameter]
public double Interval { get; set; } = 4000;
/// <summary>
/// Gets or sets the pager position. Set to <c>PagerPosition.Bottom</c> by default.
/// </summary>
/// <value>The pager position.</value>
[Parameter]
public PagerPosition PagerPosition { get; set; } = PagerPosition.Bottom;
/// <summary>
/// Gets or sets a value indicating whether pager overlays the carousel items. Set to <c>true</c> by default.
/// </summary>
/// <value><c>true</c> if pager overlay is allowed; otherwise, <c>false</c>.</value>
[Parameter]
public bool PagerOverlay { get; set; } = true;
/// <summary>
/// Gets or sets a value indicating whether paging is allowed. Set to <c>true</c> by default.
/// </summary>
/// <value><c>true</c> if paging is allowed; otherwise, <c>false</c>.</value>
[Parameter]
public bool AllowPaging { get; set; } = true;
/// <summary>
/// Gets or sets a value indicating whether previous/next navigation is allowed. Set to <c>true</c> by default.
/// </summary>
/// <value><c>true</c> if previous/next navigation is allowed; otherwise, <c>false</c>.</value>
[Parameter]
public bool AllowNavigation { get; set; } = true;
/// <summary>
/// Gets or sets the buttons style
/// </summary>
/// <value>The buttons style.</value>
[Parameter]
public ButtonStyle ButtonStyle { get; set; } = ButtonStyle.Base;
/// <summary>
/// Gets or sets the design variant of the buttons.
/// </summary>
/// <value>The variant of the buttons.</value>
[Parameter]
public Variant ButtonVariant { get; set; } = Variant.Text;
/// <summary>
/// Gets or sets the color shade of the buttons.
/// </summary>
/// <value>The color shade of the buttons.</value>
[Parameter]
public Shade ButtonShade { get; set; } = Shade.Lighter;
/// <summary>
/// Gets or sets the buttons size.
/// </summary>
/// <value>The buttons size.</value>
[Parameter]
public ButtonSize ButtonSize { get; set; } = ButtonSize.Large;
/// <summary>
/// Gets or sets the next button text.
/// </summary>
/// <value>The next button text.</value>
[Parameter]
public string NextText { get; set; } = "";
/// <summary>
/// Gets or sets the previous button text.
/// </summary>
/// <value>The previous button text.</value>
[Parameter]
public string PrevText { get; set; } = "";
/// <summary>
/// Gets or sets the next button icon.
/// </summary>
/// <value>The next button icon.</value>
[Parameter]
public string NextIcon { get; set; } = "arrow_forward_ios";
/// <summary>
/// Gets or sets the previous button icon.
/// </summary>
/// <value>The previous button icon.</value>
[Parameter]
public string PrevIcon { get; set; } = "arrow_back_ios_new";
System.Threading.Timer timer;
/// <inheritdoc />
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);
if (firstRender)
{
var ts = TimeSpan.FromMilliseconds(Interval);
timer = new System.Threading.Timer(state => InvokeAsync(Next),
null, Auto ? ts : Timeout.InfiniteTimeSpan, ts);
}
}
/// <inheritdoc />
public override void Dispose()
{
base.Dispose();
if (timer != null)
{
timer.Dispose();
timer = null;
}
}
double? x;
double? y;
void OnTouchStart(TouchEventArgs args)
{
x = args.Touches[0].ClientX;
y = args.Touches[0].ClientY;
}
async Task OnTouchEnd(TouchEventArgs args)
{
if (x == null || y == null)
{
return;
}
var xDiff = x.Value - args.ChangedTouches[0].ClientX;
var yDiff = y.Value - args.ChangedTouches[0].ClientY;
if (Math.Abs(xDiff) < 100 && Math.Abs(yDiff) < 100)
{
x = null;
y = null;
return;
}
if (Math.Abs(xDiff) > Math.Abs(yDiff))
{
if (xDiff > 0)
{
await Next();
}
else
{
await Prev();
}
}
x = null;
y = null;
}
void OnTouchCancel(TouchEventArgs args)
{
x = null;
y = null;
}
}
}

View File

@@ -0,0 +1,8 @@
@using Microsoft.JSInterop
@implements IDisposable
@inject IJSRuntime JSRuntime
<li @ref="element" @attributes=@Attributes class=@ClassList tabindex="0">
@ChildContent
<div class="rz-carousel-snapper"></div>
</li>

View File

@@ -0,0 +1,63 @@
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using Radzen.Blazor.Rendering;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Radzen.Blazor
{
/// <summary>
/// RadzenCarouselItem component.
/// </summary>
public partial class RadzenCarouselItem : IDisposable
{
/// <summary>
/// Gets the class list.
/// </summary>
/// <value>The class list.</value>
ClassList ClassList => ClassList.Create()
.Add("rz-carousel-item")
.Add(Attributes);
/// <summary>
/// Gets or sets the arbitrary attributes.
/// </summary>
/// <value>The arbitrary attributes.</value>
[Parameter(CaptureUnmatchedValues = true)]
public IDictionary<string, object> Attributes { get; set; }
/// <summary>
/// Gets or sets the child content.
/// </summary>
/// <value>The child content.</value>
[Parameter]
public RenderFragment ChildContent { get; set; }
/// <summary>
/// Gets or sets the tabs.
/// </summary>
/// <value>The tabs.</value>
[CascadingParameter]
public RadzenCarousel Carousel { get; set; }
/// <inheritdoc />
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
Carousel.AddItem(this);
itemIndex = Carousel.items.IndexOf(this);
}
/// <inheritdoc />
public void Dispose()
{
Carousel?.RemoveItem(this);
}
int itemIndex;
internal ElementReference element;
}
}

View File

@@ -13,7 +13,7 @@
{
<CascadingValue Value="@this">
<Legend />
<svg style="width: 100%; height: 100%">
<svg style="width: 100%; height: 100%; overflow: visible;">
<g transform="@($"translate({MarginLeft.ToInvariantString()}, {MarginTop.ToInvariantString()})")">
<ClipPath />
@if(ShouldRenderAxes())
@@ -38,13 +38,7 @@
@donut.RenderTitle(MarginLeft, MarginTop)
}
}
<ChartTooltipContainer @ref="@chartTooltipContainer">
@if (tooltip != null)
{
@tooltip
}
</ChartTooltipContainer>
</CascadingValue>
}
</div>

View File

@@ -52,6 +52,9 @@ namespace Radzen.Blazor
/// </summary>
[Parameter]
public EventCallback<LegendClickEventArgs> LegendClick { get; set; }
[Inject]
TooltipService TooltipService { get; set; }
/// <summary>
/// Gets the runtime width of the chart.
@@ -276,7 +279,6 @@ namespace Radzen.Blazor
}
}
ChartTooltipContainer chartTooltipContainer;
RenderFragment tooltip;
object tooltipData;
double mouseX;
@@ -367,11 +369,15 @@ namespace Radzen.Blazor
{
foreach (var overlay in series.Overlays.Reverse())
{
if (overlay.Visible && overlay.Contains(mouseX - MarginLeft, mouseY - MarginTop, TooltipTolerance))
if (overlay.Visible && overlay.Contains(queryX, queryY, TooltipTolerance))
{
tooltipData = null;
tooltip = overlay.RenderTooltip(mouseX, mouseY, MarginLeft, MarginTop);
chartTooltipContainer.Refresh();
tooltip = overlay.RenderTooltip(queryX, queryY);
var tooltipPosition = overlay.GetTooltipPosition(queryX, queryY);
TooltipService.OpenChartTooltip(Element, tooltipPosition.X + MarginLeft, tooltipPosition.Y + MarginTop, _ => tooltip, new ChartTooltipOptions
{
ColorScheme = ColorScheme
});
await Task.Yield();
return;
@@ -399,21 +405,25 @@ namespace Radzen.Blazor
if (closestSeriesData != tooltipData)
{
tooltipData = closestSeriesData;
tooltip = closestSeries.RenderTooltip(closestSeriesData, MarginLeft, MarginTop, Height ?? 0);
chartTooltipContainer.Refresh();
tooltip = closestSeries.RenderTooltip(closestSeriesData);
var tooltipPosition = closestSeries.GetTooltipPosition(closestSeriesData);
TooltipService.OpenChartTooltip(Element, tooltipPosition.X + MarginLeft, tooltipPosition.Y + MarginTop, _ => tooltip, new ChartTooltipOptions
{
ColorScheme = ColorScheme
});
await Task.Yield();
}
return;
}
}
if (tooltip != null)
{
tooltipData = null;
tooltip = null;
if (tooltip != null)
{
tooltipData = null;
tooltip = null;
chartTooltipContainer.Refresh();
await Task.Yield();
}
TooltipService.Close();
await Task.Yield();
}
}

View File

@@ -0,0 +1,184 @@
@implements IAsyncDisposable
@using Microsoft.JSInterop
@inject IJSRuntime JSRuntime
@foreach (var tooltip in tooltips)
{
<div id="@UniqueID"
style="display: none; top: 0; left: 0; z-index: 1001; position: absolute;"
class="@($"rz-scheme-{tooltip.Options.ColorScheme.ToString().ToLowerInvariant()}")">
@tooltip.Options.ChildContent(Service)
</div>
}
@code {
public string UniqueID { get; set; }
[Inject] private TooltipService Service { get; set; }
List<ChartTooltip> tooltips = new List<ChartTooltip>();
async Task Open(ElementReference chart, double x, double y, ChartTooltipOptions options)
{
tooltips.Clear();
tooltips.Add(new ChartTooltip { Options = options, Chart = chart, X = x, Y = y });
await InvokeAsync(StateHasChanged);
var tooltip = tooltips.LastOrDefault();
if (tooltip != null)
{
await JSRuntime.InvokeVoidAsync("Radzen.openChartTooltip",
tooltip.Chart,
x,
y,
UniqueID);
}
}
bool IsJSRuntimeAvailable { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
IsJSRuntimeAvailable = true;
var tooltip = tooltips.LastOrDefault();
if (tooltip != null)
{
await JSRuntime.InvokeVoidAsync("Radzen.openChartTooltip",
tooltip.Chart,
tooltip.X,
tooltip.Y,
UniqueID,
Reference,
"RadzenChartTooltip.CloseTooltip");
}
}
private DotNetObjectReference<RadzenChartTooltip> reference;
/// <summary>
/// Gets the reference for the current component.
/// </summary>
/// <value>The reference.</value>
protected DotNetObjectReference<RadzenChartTooltip> Reference
{
get
{
if (reference == null)
{
reference = DotNetObjectReference.Create(this);
}
return reference;
}
}
/// <summary>
/// Closes this instance.
/// </summary>
[JSInvokable("RadzenChartTooltip.CloseTooltip")]
public void CloseTooltip()
{
Service.Close();
}
public async Task Close()
{
var lastTooltip = tooltips.LastOrDefault();
if (lastTooltip != null)
{
if (IsJSRuntimeAvailable)
{
try
{
tooltips.Remove(lastTooltip);
await JSRuntime.InvokeVoidAsync("Radzen.closeTooltip", UniqueID);
}
catch
{
// ignored
}
}
}
await InvokeAsync(StateHasChanged);
}
public async ValueTask DisposeAsync()
{
while (tooltips.Count != 0)
{
await Close();
}
reference?.Dispose();
reference = null;
if (IsJSRuntimeAvailable)
{
try
{
await JSRuntime.InvokeVoidAsync("Radzen.destroyPopup", UniqueID);
}
catch
{
// ignored
}
}
Service.OnOpenChartTooltip -= OnOpen;
Service.OnClose -= OnClose;
Service.OnNavigate -= OnNavigate;
}
protected override void OnInitialized()
{
UniqueID = Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Replace("/", "-").Replace("+", "-").Substring(0, 10);
Service.OnOpenChartTooltip += OnOpen;
Service.OnClose += OnClose;
Service.OnNavigate += OnNavigate;
}
void OnOpen(ElementReference element, double x, double y, ChartTooltipOptions options)
{
Open(element, x, y, options).ConfigureAwait(false);
}
void OnClose()
{
Close().ConfigureAwait(false);
}
void OnNavigate()
{
JSRuntime.InvokeVoidAsync("Radzen.closePopup", UniqueID);
}
private sealed class ChartTooltip
{
/// <summary>
/// Gets or sets the owning chart.
/// </summary>
/// <value>The chart.</value>
public ElementReference Chart { get; set; }
/// <summary>
/// Gets or sets the horizontal position of the tooltip.
/// </summary>
/// <value>The position.</value>
public double X { get; set; }
/// <summary>
/// Gets or sets the vertical position of the tooltip.
/// </summary>
/// <value>The position.</value>
public double Y { get; set; }
/// <summary>
/// Gets or sets the options.
/// </summary>
/// <value>The options.</value>
public ChartTooltipOptions Options { get; set; }
}
}

View File

@@ -2,7 +2,6 @@ using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Radzen.Blazor.Rendering;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.JSInterop;
@@ -167,7 +166,7 @@ namespace Radzen.Blazor
}
}
void UpdateColorUsingHsvHandles()
async Task UpdateColorUsingHsvHandles()
{
var hsv = new HSV {
Hue = HueHandleLeft,
@@ -175,45 +174,59 @@ namespace Radzen.Blazor
Value = 1 - SaturationHandleTop,
Alpha = AlphaHandleLeft
};
Color = hsv.ToRGB().ToCSS();
TriggerChange();
await TriggerChange();
}
Rect lastHslRect;
void OnSaturationMove(DraggableEventArgs args)
async Task OnSaturationMove(DraggableEventArgs args)
{
lastHslRect = args.Rect; ;
SaturationHandleLeft = Math.Clamp((args.ClientX - args.Rect.Left) / args.Rect.Width, 0, 1);
SaturationHandleTop = Math.Clamp((args.ClientY - args.Rect.Top) / args.Rect.Height, 0, 1);
UpdateColorUsingHsvHandles();
await UpdateColorUsingHsvHandles();
}
void TriggerChange()
async Task TriggerChange()
{
if (!ShowButton)
{
ValueChanged.InvokeAsync(Color);
Change.InvokeAsync(Color);
await OnChanged();
}
StateHasChanged();
}
void ChangeRGB(object value)
async Task OnChanged()
{
await ValueChanged.InvokeAsync(Color);
if (FieldIdentifier.FieldName != null)
{
EditContext?.NotifyFieldChanged(FieldIdentifier);
}
await Change.InvokeAsync(Color);
}
async Task ChangeRGB(object value)
{
var rgb = RGB.Parse(value as string);
if (rgb != null)
{
rgb.Alpha = AlphaHandleLeft;
UpdateColor(rgb);
await UpdateColor(rgb);
}
}
internal async Task SelectColor(string value)
{
UpdateColor(RGB.Parse(value));
await UpdateColor(RGB.Parse(value));
if (!ShowButton)
{
@@ -221,7 +234,7 @@ namespace Radzen.Blazor
}
}
void UpdateColor(RGB rgb)
async Task UpdateColor(RGB rgb)
{
Color = rgb.ToCSS();
@@ -232,10 +245,10 @@ namespace Radzen.Blazor
HueHandleLeft = hsv.Hue;
AlphaHandleLeft = hsv.Alpha;
TriggerChange();
await TriggerChange();
}
void ChangeAlpha(double value)
async Task ChangeAlpha(double value)
{
if (value >= 0 && value <= 100)
{
@@ -244,19 +257,19 @@ namespace Radzen.Blazor
Color = rgb.ToCSS();
TriggerChange();
await TriggerChange();
}
}
void ChangeAlpha(object alpha)
async Task ChangeAlpha(object alpha)
{
if (Double.TryParse((string)alpha, out var value))
{
ChangeAlpha(value);
await ChangeAlpha(value);
}
}
void ChangeColor(double value, Action<RGB, double> update)
async Task ChangeColor(double value, Action<RGB, double> update)
{
if (value >= 0 && value <= 255)
{
@@ -264,42 +277,42 @@ namespace Radzen.Blazor
update(rgb, value);
UpdateColor(rgb);
await UpdateColor(rgb);
}
}
void ChangeColor(object color, Action<RGB, double> update)
async Task ChangeColor(object color, Action<RGB, double> update)
{
if (Double.TryParse((string)color, out var value))
{
ChangeColor(value, update);
await ChangeColor(value, update);
}
}
Rect lastAlphaRect;
void OnAlphaMove(DraggableEventArgs args)
async Task OnAlphaMove(DraggableEventArgs args)
{
lastAlphaRect = args.Rect;
AlphaHandleLeft = Math.Round(Math.Clamp((args.ClientX - args.Rect.Left) / args.Rect.Width, 0, 1), 2);
UpdateColorUsingHsvHandles();
await UpdateColorUsingHsvHandles();
}
Rect lastHueRect;
void OnHueMove(DraggableEventArgs args)
async Task OnHueMove(DraggableEventArgs args)
{
lastHueRect = args.Rect;
HueHandleLeft = Math.Clamp((args.ClientX - args.Rect.Left) / args.Rect.Width, 0, 1);
UpdateColorUsingHsvHandles();
await UpdateColorUsingHsvHandles();
}
async Task OnClick()
{
await ValueChanged.InvokeAsync(Color);
await Change.InvokeAsync(Color);
await OnChanged();
await Popup.CloseAsync();
}
@@ -309,6 +322,7 @@ namespace Radzen.Blazor
{
SetInitialValue();
}
await Close.InvokeAsync(null);
}
@@ -370,14 +384,7 @@ namespace Radzen.Blazor
/// <inheritdoc />
protected override string GetComponentCssClass()
{
var classList = new List<string>() { "rz-colorpicker" };
if (Disabled)
{
classList.Add("rz-state-disabled");
}
return string.Join(" ", classList);
return GetClassList("rz-colorpicker").ToString();
}
/// <inheritdoc />
@@ -435,7 +442,7 @@ namespace Radzen.Blazor
lastHueRect = await JSRuntime.InvokeAsync<Rect>("Radzen.clientRect", (GetId() + "hue"));
}
OnHueMove(new DraggableEventArgs() { Rect = lastHueRect, ClientX = lastHueRect.Left + lastHueRect.Width * HueHandleLeft + (key == "ArrowLeft" ? -1 : 1) });
await OnHueMove(new DraggableEventArgs() { Rect = lastHueRect, ClientX = lastHueRect.Left + lastHueRect.Width * HueHandleLeft + (key == "ArrowLeft" ? -1 : 1) });
}
else if (key == "Escape")
{
@@ -460,7 +467,7 @@ namespace Radzen.Blazor
lastAlphaRect = await JSRuntime.InvokeAsync<Rect>("Radzen.clientRect", (GetId() + "alpha"));
}
OnAlphaMove(new DraggableEventArgs() { Rect = lastAlphaRect, ClientX = lastAlphaRect.Left + lastAlphaRect.Width * AlphaHandleLeft + (key == "ArrowLeft" ? -3 : 3) });
await OnAlphaMove(new DraggableEventArgs() { Rect = lastAlphaRect, ClientX = lastAlphaRect.Left + lastAlphaRect.Width * AlphaHandleLeft + (key == "ArrowLeft" ? -3 : 3) });
}
else if (key == "Escape")
{
@@ -485,9 +492,9 @@ namespace Radzen.Blazor
{
preventKeyPress = true;
OnSaturationMove(new DraggableEventArgs()
{
Rect = lastHslRect,
await OnSaturationMove(new DraggableEventArgs()
{
Rect = lastHslRect,
ClientX = lastHslRect.Left + lastHslRect.Width * SaturationHandleLeft + (key == "ArrowLeft" ? -1 : key == "ArrowRight" ? 1 : 0),
ClientY = lastHslRect.Top + lastHslRect.Height * SaturationHandleTop + (key == "ArrowUp" ? -1 : key == "ArrowDown" ? 1 : 0)
});

View File

@@ -2,6 +2,7 @@
<RadzenNotification @attributes="Attributes" />
<RadzenContextMenu @attributes="Attributes" />
<RadzenTooltip @attributes="Attributes" />
<RadzenChartTooltip @attributes="Attributes" />
@code {
/// <summary>

View File

@@ -48,11 +48,13 @@ namespace Radzen.Blazor
{
var validationResults = new List<ValidationResult>();
var getter = PropertyAccess.Getter<object>(EditContext.Model, component.FieldIdentifier.FieldName);
var model = component.FieldIdentifier.Model;
var value = getter(EditContext.Model);
var getter = PropertyAccess.Getter<object>(model, component.FieldIdentifier.FieldName);
var validationContext = new ValidationContext(EditContext.Model)
var value = getter(model);
var validationContext = new ValidationContext(model)
{
MemberName = component.FieldIdentifier.FieldName
};

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)
{
@@ -289,7 +291,7 @@ else
var eventCallbackGenericCreate = typeof(NumericFilterEventCallback).GetMethod("Create").MakeGenericMethod(type);
var eventCallbackGenericAction = typeof(NumericFilterEventCallback).GetMethod("Action").MakeGenericMethod(type);
builder.AddAttribute(3, "Change", eventCallbackGenericCreate.Invoke(this,
builder.AddAttribute(4, "ValueChanged", eventCallbackGenericCreate.Invoke(this,
new object[] { this, eventCallbackGenericAction.Invoke(this, new object[] { action }) }));
builder.CloseComponent();

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
@@ -118,6 +115,13 @@ namespace Radzen.Blazor
[Parameter]
public string Property { get; set; }
/// <summary>
/// Gets or sets the filter property name.
/// </summary>
/// <value>The filter property name.</value>
[Parameter]
public string FilterProperty { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this property is selected in the filter.
/// </summary>
@@ -340,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

@@ -21,7 +21,7 @@
@if (AllowGrouping || AllowColumnPicking)
{
<div class="rz-group-header" @onmouseup=@(args => EndColumnDropToGroup())>
@if (@HeaderTemplate != null)
@if (@HeaderTemplate != null && ShowHeader)
{
<div class="rz-custom-header" @onkeydown:stopPropagation>
@HeaderTemplate
@@ -67,7 +67,7 @@
}
else
{
@if (@HeaderTemplate != null)
@if (@HeaderTemplate != null && ShowHeader)
{
<div class="rz-group-header">
<div class="rz-custom-header" @onkeydown:stopPropagation>
@@ -168,167 +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" />
<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))
{
<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" />
}
@(DrawNumericFilter(column))
}
else if (column.FilterPropertyType == typeof(bool) || column.FilterPropertyType == typeof(bool?))
{
<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" />
}
<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>
}
@@ -338,7 +180,7 @@
<tbody>
@if (Data != null)
{
@if (!ShowEmptyMessage || Count > 0 && (IsVirtualizationAllowed() ? Count > 0 : true) || LoadData.HasDelegate && Data.Count() > 0)
@if (!ShowEmptyMessage || Count > 0 && (IsVirtualizationAllowed() ? Count > 0 && Data.Count() > 0 : true) || LoadData.HasDelegate && Data.Count() > 0)
{
if (columns.Count > 0)
{
@@ -454,6 +296,214 @@
}
</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>
<span>
@{ 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>
}
</span>
<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" ? " " : ";";
@@ -547,7 +597,7 @@
<a id="@(GetId() + "exp")" aria-label="@ExpandChildItemAriaLabel" class="@(getExpandIconCssClass(this, Item))" style="@(getExpandIconStyle(this, Item, rowArgs.Item1.Expandable))" @onclick:preventDefault="true" @onclick="_ => this.ExpandItem(Item)" @onclick:stopPropagation="true">
<span class="@(this.ExpandedItemStyle(Item))"></span>
</a>
<span class="rz-cell-data" @attributes="@spanAttributes">
<span class="@column.GetCellClass()" @attributes="@spanAttributes">
@if (Item != null)
{
@if (this.IsRowInEditMode(Item) && column.EditTemplate != null)
@@ -568,12 +618,29 @@
}
else
{
<span class="rz-cell-data" @attributes="@spanAttributes">
<span class="@column.GetCellClass()" @attributes="@spanAttributes">
@if (Item != null)
{
@if ((column.IsInEditMode(column.Property, Item) || this.IsRowInEditMode(Item)) && column.EditTemplate is not null)
@if (this.IsRowInEditMode(Item) && column.EditTemplate != null)
{
@column.EditTemplate(Item)
var isDefault = column.IsInEditMode == RadzenDataGridColumn<TItem>.DefaultIsInEditMode;
@if(isDefault)
{
@column.EditTemplate(Item)
}
else if (column.IsInEditMode(column.Property, Item))
{
@column.EditTemplate(Item)
}
else if (column.Template != null)
{
@column.Template(Item)
}
else
{
@column.GetValue(Item)
}
}
else if (column.Template != null)
{

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;
@@ -34,6 +32,23 @@ namespace Radzen.Blazor
#endif
public partial class RadzenDataGrid<TItem> : PagedDataBoundComponent<TItem>
{
/// <summary>
/// Returns the validity of the DataGrid.
/// </summary>
/// <value><c>true</c> if all validators in the DataGrid a valid; otherwise, <c>false</c>.</value>
public bool IsValid
{
get
{
if (!editContexts.Any())
{
return true;
}
return editContexts.All(c => !c.Value.GetValidationMessages().Any());
}
}
/// <summary>
/// Gets or sets a value indicating whether this instance is virtualized.
/// </summary>
@@ -140,13 +155,13 @@ 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 = 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());
}
_view = Enumerable.Empty<TItem>().AsQueryable();
_view = view;
return new Microsoft.AspNetCore.Components.Web.Virtualization.ItemsProviderResult<GroupResult>(_groupedPagedView.Any() ? _groupedPagedView.Skip(request.StartIndex).Take(top) : _groupedPagedView, totalItemsCount);
}
@@ -305,11 +320,20 @@ namespace Radzen.Blazor
/// <value><c>true</c> if DataGrid data cells will follow the header cells structure in composite columns; otherwise, <c>false</c>.</value>
[Parameter]
public bool AllowCompositeDataCells { get; set; } = false;
/// <summary>
/// Gets or sets a value indicating whether DataGrid data body show empty message.
/// </summary>
[Parameter]
public bool ShowEmptyMessage { get; set; } = true;
/// <summary>
/// Gets or sets value if headers are shown.
/// </summary>
/// <value>If headers are shown value.</value>
[Parameter]
public bool ShowHeader { get; set; } = true;
/// <summary>
/// Gets or sets a value indicating whether DataGrid is responsive.
/// </summary>
@@ -339,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;
}
@@ -654,11 +678,11 @@ namespace Radzen.Blazor
}
}
var descriptor = sorts.Where(d => d.Property == column?.GetSortProperty()).FirstOrDefault();
var descriptor = Sorts.Where(d => d.Property == column?.GetSortProperty()).FirstOrDefault();
if (descriptor == null && column.SortOrder.HasValue)
{
descriptor = new SortDescriptor() { Property = column.GetSortProperty(), SortOrder = column.SortOrder.Value };
sorts.Add(descriptor);
Sorts.Add(descriptor);
}
if (!allColumns.Contains(column))
@@ -760,7 +784,7 @@ namespace Radzen.Blazor
[Parameter]
public EventCallback<DataGridPickedColumnsChangedEventArgs<TItem>> PickedColumnsChanged { get; set; }
string getFilterInputId(RadzenDataGridColumn<TItem> column)
internal string getFilterInputId(RadzenDataGridColumn<TItem> column)
{
return string.Join("", $"{UniqueID}".Split('.')) + column.GetFilterProperty();
}
@@ -790,6 +814,7 @@ namespace Radzen.Blazor
builder.AddAttribute(2, "ShowUpDown", column.ShowUpDownForNumericFilter());
builder.AddAttribute(3, "Style", "width:100%");
builder.AddAttribute(4, "InputAttributes", new Dictionary<string,object>(){ { "aria-label", column.Title + $"{(!isFirst ? " second " : " ")}filter value " + (isFirst ? column.GetFilterValue() : column.GetSecondFilterValue()) } });
builder.AddAttribute(5, "id", getFilterInputId(column) + (isFirst ? "f" : "s"));
Action<object> action;
if (force)
@@ -807,34 +832,7 @@ namespace Radzen.Blazor
builder.AddAttribute(3, "Change", eventCallbackGenericCreate.Invoke(this,
new object[] { this, eventCallbackGenericAction.Invoke(this, new object[] { action }) }));
if (FilterMode == FilterMode.Advanced)
{
builder.AddAttribute(4, "oninput", EventCallback.Factory.Create<ChangeEventArgs>(this, args =>
{
var value = $"{args.Value}";
object filterValue = null;
if (!string.IsNullOrWhiteSpace(value))
{
try
{
filterValue = Convert.ChangeType(value, Nullable.GetUnderlyingType(type));
}
catch (Exception)
{
filterValue = null;
}
}
column.SetFilterValue(filterValue, isFirst);
SaveSettings();
}));
builder.AddAttribute(5, "Disabled", !column.CanSetFilterValue());
}
else if (FilterMode == FilterMode.SimpleWithMenu)
{
builder.AddAttribute(4, "Disabled", !column.CanSetFilterValue());
}
builder.AddAttribute(4, "Disabled", !column.CanSetFilterValue());
builder.CloseComponent();
});
@@ -1253,6 +1251,20 @@ namespace Radzen.Blazor
[Parameter]
public string DoesNotContainText { get; set; } = "Does not contain";
/// <summary>
/// Gets or sets the in operator text.
/// </summary>
/// <value>The in operator text.</value>
[Parameter]
public string InText { get; set; } = "In";
/// <summary>
/// Gets or sets the not in operator text.
/// </summary>
/// <value>The not in operator text.</value>
[Parameter]
public string NotInText { get; set; } = "Not in";
/// <summary>
/// Gets or sets the starts with text.
/// </summary>
@@ -1288,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)
@@ -1587,10 +1606,12 @@ namespace Radzen.Blazor
}
int? indexOfColumnToReoder;
string uniqueIDOfColumnToReoder;
internal async Task StartColumnReorder(MouseEventArgs args, int columnIndex)
internal async Task StartColumnReorder(MouseEventArgs args, int columnIndex, string uniqueID)
{
indexOfColumnToReoder = columnIndex;
uniqueIDOfColumnToReoder = uniqueID;
await JSRuntime.InvokeVoidAsync("Radzen.startColumnReorder", getColumnUniqueId(columnIndex));
}
@@ -1664,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>
@@ -1697,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);
}
}
@@ -1728,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);
@@ -1783,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
@@ -2040,7 +2071,8 @@ namespace Radzen.Blazor
c.SetVisible(null);
});
selectedColumns = allColumns.Where(c => c.Pickable && c.GetVisible()).ToList();
sorts.Clear();
Sorts.Clear();
columns = allColumns.Where(c => c.Parent == null).ToList();
}
}
@@ -2152,7 +2184,7 @@ namespace Radzen.Blazor
.ToList();
Query.Filters = filters;
Query.Sorts = sorts;
Query.Sorts = Sorts.ToList();
if (LoadData.HasDelegate)
{
await LoadData.InvokeAsync(new Radzen.LoadDataArgs()
@@ -2162,7 +2194,7 @@ namespace Radzen.Blazor
OrderBy = orderBy,
Filter = IsOData() ? allColumns.ToList().ToODataFilterString<TItem>() : filterString,
Filters = filters,
Sorts = sorts
Sorts = Sorts.ToList()
});
}
}
@@ -2176,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>();
@@ -2278,9 +2310,9 @@ namespace Radzen.Blazor
return (RowSelect.HasDelegate || ValueChanged.HasDelegate || SelectionMode == DataGridSelectionMode.Multiple) && selectedItems.Keys.Any(i => ItemEquals(i, item)) ? $"rz-state-highlight rz-data-row {isInEditMode} " : $"rz-data-row {isInEditMode} ";
}
internal Tuple<Radzen.RowRenderEventArgs<TItem>, IReadOnlyDictionary<string, object>> RowAttributes(TItem item)
internal Tuple<Radzen.RowRenderEventArgs<TItem>, IReadOnlyDictionary<string, object>> RowAttributes(TItem item, int index)
{
var args = new Radzen.RowRenderEventArgs<TItem>() { Data = item, Expandable = Template != null || LoadChildData.HasDelegate };
var args = new Radzen.RowRenderEventArgs<TItem>() { Data = item, Index = index, Expandable = Template != null || LoadChildData.HasDelegate };
if (RowRender != null)
{
@@ -2390,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)
{
@@ -2934,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();
}
@@ -2972,48 +3032,46 @@ namespace Radzen.Blazor
return isOData != null ? isOData.Value : false;
}
internal List<SortDescriptor> sorts = new List<SortDescriptor>();
internal void SetColumnSortOrder(RadzenDataGridColumn<TItem> column)
{
var CurrentSortDescriptor = Sorts.FirstOrDefault(d => d.Property == column?.GetSortProperty());
if (!AllowMultiColumnSorting)
{
foreach (var c in allColumns.ToList().Where(c => c != column))
{
c.SetSortOrderInternal(null);
}
sorts.Clear();
Sorts.Clear();
}
var descriptor = sorts.Where(d => d.Property == column?.GetSortProperty()).FirstOrDefault();
if (descriptor == null)
if (CurrentSortDescriptor == null)
{
descriptor = new SortDescriptor() { Property = column.GetSortProperty() };
CurrentSortDescriptor = new SortDescriptor() { Property = column.GetSortProperty() };
}
if (column.GetSortOrder() == null)
if (CurrentSortDescriptor.SortOrder == null)
{
column.SetSortOrderInternal(SortOrder.Ascending);
descriptor.SortOrder = SortOrder.Ascending;
CurrentSortDescriptor.SortOrder = SortOrder.Ascending;
}
else if (column.GetSortOrder() == SortOrder.Ascending)
else if (CurrentSortDescriptor.SortOrder == SortOrder.Ascending)
{
column.SetSortOrderInternal(SortOrder.Descending);
descriptor.SortOrder = SortOrder.Descending;
CurrentSortDescriptor.SortOrder = SortOrder.Descending;
}
else if (column.GetSortOrder() == SortOrder.Descending)
else if (CurrentSortDescriptor.SortOrder == SortOrder.Descending)
{
column.SetSortOrderInternal(null);
if (sorts.Where(d => d.Property == column?.GetSortProperty()).Any())
if (Sorts.Any(d => d.Property == column?.GetSortProperty()))
{
sorts.Remove(descriptor);
Sorts.Remove(CurrentSortDescriptor);
}
descriptor = null;
CurrentSortDescriptor = null;
}
if (descriptor != null && !sorts.Where(d => d.Property == column?.GetSortProperty()).Any())
if (CurrentSortDescriptor != null && !Sorts.Any(d => d.Property == column?.GetSortProperty()))
{
sorts.Add(descriptor);
Sorts.Add(CurrentSortDescriptor);
}
}
@@ -3022,14 +3080,14 @@ namespace Radzen.Blazor
if (args.Action == NotifyCollectionChangedAction.Add)
{
RadzenDataGridColumn<TItem> column;
column = columns.Where(c => c.GetGroupProperty() == ((GroupDescriptor)args.NewItems[0]).Property).FirstOrDefault();
column = columns.FirstOrDefault(c => c.GetGroupProperty() == ((GroupDescriptor)args.NewItems[0]).Property);
if(column == null)
{
column = allColumns.Where(c => c.GetGroupProperty() == ((GroupDescriptor)args.NewItems[0]).Property).FirstOrDefault();
column = allColumns.FirstOrDefault(c => c.GetGroupProperty() == ((GroupDescriptor)args.NewItems[0]).Property);
}
if (HideGroupedColumn)
if (column != null && HideGroupedColumn)
{
column.SetVisible(false);
if (!groupedColumns.Contains(column))
@@ -3041,14 +3099,14 @@ namespace Radzen.Blazor
else if (args.Action == NotifyCollectionChangedAction.Remove)
{
RadzenDataGridColumn<TItem> column;
column = columns.Where(c => c.GetGroupProperty() == ((GroupDescriptor)args.OldItems[0]).Property).FirstOrDefault();
column = columns.FirstOrDefault(c => c.GetGroupProperty() == ((GroupDescriptor)args.OldItems[0]).Property);
if (column == null)
{
column = allColumns.Where(c => c.GetGroupProperty() == ((GroupDescriptor)args.OldItems[0]).Property).FirstOrDefault();
column = allColumns.FirstOrDefault(c => c.GetGroupProperty() == ((GroupDescriptor)args.OldItems[0]).Property);
}
if (HideGroupedColumn)
if (column != null && HideGroupedColumn)
{
column.SetVisible(true);
if (groupedColumns.Contains(column))
@@ -3099,12 +3157,12 @@ namespace Radzen.Blazor
internal async Task EndColumnDropToGroup()
{
if(indexOfColumnToReoder != null && AllowGrouping)
if(indexOfColumnToReoder != null && uniqueIDOfColumnToReoder != null && AllowGrouping)
{
var functionName = $"Radzen['{getColumnUniqueId(indexOfColumnToReoder.Value)}end']";
await JSRuntime.InvokeVoidAsync("eval", $"{functionName} && {functionName}()");
var column = allColumns.ElementAtOrDefault(indexOfColumnToReoder.Value);
var column = allColumns.Where(c => (c.UniqueID ?? c.Property) == uniqueIDOfColumnToReoder).FirstOrDefault();
if (column != null && column.Groupable && !string.IsNullOrEmpty(column.GetGroupProperty()))
{
@@ -3125,6 +3183,7 @@ namespace Radzen.Blazor
}
indexOfColumnToReoder = null;
uniqueIDOfColumnToReoder = null;
}
}

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
@@ -75,6 +74,8 @@ namespace Radzen.Blazor
}
}
internal IEnumerable<RadzenDataGridColumn<TItem>> VisibleColumns => ColumnsCollection.Where(c => c.GetVisible());
internal int GetLevel()
{
int i = 0;
@@ -93,11 +94,11 @@ namespace Radzen.Blazor
if (!Grid.AllowCompositeDataCells && isDataCell || Columns == null)
return 1;
var span = ColumnsCollection.Concat(ColumnsCollection.SelectManyRecursive(c => c.ColumnsCollection)).Sum(c => c.ColumnsCollection.Count())
- ColumnsCollection.SelectManyRecursive(c => c.ColumnsCollection).Count(c => c.ColumnsCollection.Any())
+ ColumnsCollection.Where(c => c.ColumnsCollection.Count() == 0).Count();
var span = VisibleColumns.Concat(VisibleColumns.SelectManyRecursive(c => c.VisibleColumns)).Sum(c => c.VisibleColumns.Count())
- VisibleColumns.SelectManyRecursive(c => c.VisibleColumns).Count(c => c.VisibleColumns.Any())
+ VisibleColumns.Where(c => c.VisibleColumns.Count() == 0).Count();
return span != 0 ? span : ColumnsCollection.Count;
return span != 0 ? span : VisibleColumns.Count();
}
internal int GetRowSpan(bool isDataCell = false)
@@ -139,7 +140,11 @@ namespace Radzen.Blazor
{
if (Type == null)
{
_filterPropertyType = typeof(IEnumerable<object>);
var fp = GetFilterProperty();
var pt = !string.IsNullOrEmpty(fp) ?
PropertyAccess.GetPropertyType(typeof(TItem), fp) : typeof(object);
_filterPropertyType = typeof(IEnumerable<>).MakeGenericType(pt);
}
if (GetFilterOperator() == FilterOperator.Equals)
@@ -171,13 +176,13 @@ namespace Radzen.Blazor
propertyValueGetter = PropertyAccess.Getter<TItem, object>(Property);
}
if (_filterPropertyType == typeof(string) && filterOperator != FilterOperator.Custom && filterOperator == null)
if (_filterPropertyType == typeof(string) && filterOperator != FilterOperator.Custom && filterOperator == null && _filterOperator == null)
{
SetFilterOperator(FilterOperator.Contains);
}
}
}
int? orderIndex;
/// <summary>
@@ -242,6 +247,11 @@ namespace Radzen.Blazor
/// <returns>System.Boolean.</returns>
public bool GetVisible()
{
if (Columns != null && ColumnsCollection.Any() && VisibleColumns.Count() == 0)
{
return false;
}
return _visible ?? Visible;
}
@@ -423,6 +433,18 @@ namespace Radzen.Blazor
[Parameter]
public string GroupFooterCssClass { get; set; }
/// <summary>
/// Gets or sets the header white space style.
/// </summary>
[Parameter]
public WhiteSpace HeaderWhiteSpace { get; set; } = WhiteSpace.Truncate;
/// <summary>
/// Gets or sets the white space style.
/// </summary>
[Parameter]
public WhiteSpace WhiteSpace { get; set; } = WhiteSpace.Truncate;
/// <summary>
/// Gets or sets a value indicating whether this <see cref="RadzenDataGridColumn{TItem}"/> is filterable.
/// </summary>
@@ -504,7 +526,9 @@ namespace Radzen.Blazor
/// Allows the column to override whether or not this column's the <see cref="EditTemplate" /> is visible at runtime.
/// </summary>
[Parameter]
public Func<string, TItem, bool> IsInEditMode { get; set; } = (property, item) => false;
public Func<string, TItem, bool> IsInEditMode { get; set; } = DefaultIsInEditMode;
internal static Func<string, TItem, bool> DefaultIsInEditMode { get; set; } = (property, item) => false;
/// <summary>
/// Gets or sets the header template.
@@ -725,18 +749,9 @@ 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();
var descriptor = Grid.Sorts.Where(d => d.Property == GetSortProperty()).FirstOrDefault();
if (descriptor == null)
{
descriptor = new SortDescriptor() { Property = GetSortProperty() };
@@ -750,16 +765,16 @@ namespace Radzen.Blazor
else
{
SetSortOrderInternal(null);
if (Grid.sorts.Where(d => d.Property == GetSortProperty()).Any())
if (Grid.Sorts.Where(d => d.Property == GetSortProperty()).Any())
{
Grid.sorts.Remove(descriptor);
Grid.Sorts.Remove(descriptor);
}
descriptor = null;
}
if (descriptor != null && !Grid.sorts.Where(d => d.Property == GetSortProperty()).Any())
if (descriptor != null && !Grid.Sorts.Where(d => d.Property == GetSortProperty()).Any())
{
Grid.sorts.Add(descriptor);
Grid.Sorts.Add(descriptor);
}
sortOrder = new SortOrder?[] { order };
@@ -879,10 +894,10 @@ namespace Radzen.Blazor
if (Grid != null)
{
var descriptor = Grid.sorts.Where(d => d.Property == GetSortProperty()).FirstOrDefault();
var descriptor = Grid.Sorts.Where(d => d.Property == GetSortProperty()).FirstOrDefault();
if (descriptor == null)
{
Grid.sorts.Add(new SortDescriptor() { Property = GetSortProperty(), SortOrder = sortOrder.FirstOrDefault() });
Grid.Sorts.Add(new SortDescriptor() { Property = GetSortProperty(), SortOrder = sortOrder.FirstOrDefault() });
Grid._view = null;
}
}
@@ -892,7 +907,7 @@ namespace Radzen.Blazor
{
filterValue = parameters.GetValueOrDefault<object>(nameof(FilterValue));
if (FilterTemplate != null)
if (FilterTemplate != null || FilterValueTemplate != null)
{
FilterValue = filterValue;
Grid.SaveSettings();
@@ -916,7 +931,7 @@ namespace Radzen.Blazor
{
secondFilterValue = parameters.GetValueOrDefault<object>(nameof(SecondFilterValue));
if (FilterTemplate != null)
if (FilterTemplate != null || SecondFilterValueTemplate != null)
{
SecondFilterValue = secondFilterValue;
Grid.SaveSettings();
@@ -960,7 +975,7 @@ namespace Radzen.Blazor
}
}
if (parameters.DidParameterChange(nameof(FilterOperator), FilterOperator) || _filterOperator != null)
if (filterOperator == null && (parameters.DidParameterChange(nameof(FilterOperator), FilterOperator) || _filterOperator != null))
{
filterOperator = _filterOperator ?? parameters.GetValueOrDefault<FilterOperator>(nameof(FilterOperator));
}
@@ -1039,6 +1054,24 @@ namespace Radzen.Blazor
return logicalFilterOperator ?? LogicalFilterOperator;
}
/// <summary>
/// Get body column class.
/// </summary>
/// <returns></returns>
internal string GetCellClass()
{
return $"rz-cell-data rz-text-{Enum.GetName(typeof(WhiteSpace), WhiteSpace).ToLower()}";
}
/// <summary>
/// Get column header class.
/// </summary>
/// <returns></returns>
internal string GetHeaderClass()
{
return $"rz-column-title-content rz-text-{Enum.GetName(typeof(WhiteSpace), HeaderWhiteSpace).ToLower()}";
}
/// <summary>
/// Set column filter value.
/// </summary>
@@ -1050,7 +1083,19 @@ namespace Radzen.Blazor
value = offset;
}
if (PropertyAccess.IsEnum(FilterPropertyType) || (PropertyAccess.IsNullableEnum(FilterPropertyType)))
if ((FilterPropertyType == typeof(TimeOnly) || FilterPropertyType == typeof(TimeOnly?)) && value != null && value is string)
{
var v = TimeOnly.Parse($"{value}");
value = FilterPropertyType == typeof(TimeOnly) ? v : (TimeOnly?)v;
}
if ((FilterPropertyType == typeof(Guid) || FilterPropertyType == typeof(Guid?)) && value != null && value is string)
{
var v = Guid.Parse($"{value}");
value = FilterPropertyType == typeof(Guid) ? v : (Guid?)v;
}
if (!QueryableExtension.IsEnumerable(value?.GetType() ?? typeof(object)) && (PropertyAccess.IsEnum(FilterPropertyType) || (PropertyAccess.IsNullableEnum(FilterPropertyType))))
{
Type enumType = Enum.GetUnderlyingType(Nullable.GetUnderlyingType(FilterPropertyType) ?? FilterPropertyType);
value = value is not null ? Convert.ChangeType(value, enumType) : null;
@@ -1058,7 +1103,7 @@ namespace Radzen.Blazor
if (isFirst)
{
filterValue = CanSetCurrentValue(value) ? value :
filterValue = CanSetCurrentValue(value) ? value :
GetFilterOperator() == FilterOperator.IsEmpty || GetFilterOperator() == FilterOperator.IsNotEmpty ? string.Empty : null;
}
else
@@ -1163,7 +1208,7 @@ namespace Radzen.Blazor
/// </summary>
/// <value>The filter operator.</value>
[Parameter]
public FilterOperator FilterOperator
public FilterOperator FilterOperator
{
get
{
@@ -1175,6 +1220,24 @@ namespace Radzen.Blazor
}
}
IEnumerable<FilterOperator> _filterOperators;
/// <summary>
/// Gets or sets the filter operators.
/// </summary>
/// <value>The filter operators.</value>
[Parameter]
public IEnumerable<FilterOperator> FilterOperators
{
get
{
return _filterOperators;
}
set
{
_filterOperators = value;
}
}
/// <summary>
/// Gets or sets the second filter operator.
/// </summary>
@@ -1266,6 +1329,8 @@ namespace Radzen.Blazor
/// </summary>
public virtual IEnumerable<FilterOperator> GetFilterOperators()
{
if (FilterOperators != null) return FilterOperators;
if (PropertyAccess.IsEnum(FilterPropertyType) || FilterPropertyType == typeof(bool))
return new FilterOperator[] { FilterOperator.Equals, FilterOperator.NotEquals };
@@ -1294,6 +1359,8 @@ namespace Radzen.Blazor
{
switch (filterOperator)
{
case FilterOperator.Custom:
return Grid?.CustomText;
case FilterOperator.Contains:
return Grid?.ContainsText;
case FilterOperator.DoesNotContain:
@@ -1360,6 +1427,10 @@ namespace Radzen.Blazor
return "= ''";
case FilterOperator.IsNotEmpty:
return "≠ ''";
case FilterOperator.In:
return "∈";
case FilterOperator.NotIn:
return "∉";
default:
return $"{filterOperator}";
}
@@ -1421,10 +1492,10 @@ namespace Radzen.Blazor
/// </summary>
public int? GetSortIndex()
{
var descriptor = Grid.sorts.Where(s => s.Property == GetSortProperty()).FirstOrDefault();
var descriptor = Grid.Sorts.Where(s => s.Property == GetSortProperty()).FirstOrDefault();
if (descriptor != null)
{
return Grid.sorts.IndexOf(descriptor);
return Grid.Sorts.IndexOf(descriptor);
}
return null;

View File

@@ -17,7 +17,7 @@
style="display:none;" tabindex="0">
<div class="rz-overlaypanel-content">
<ul class="rz-listbox-list">
@if (Column.FilterPropertyType == typeof(string))
@if (QueryableExtension.IsEnumerable(Column.FilterPropertyType))
{
@if (Column.GetFilterOperators().Contains(FilterOperator.Contains))
{
@@ -31,25 +31,37 @@
<span class="rz-filter-menu-symbol"><s>@Column.GetFilterOperatorSymbol(FilterOperator.DoesNotContain)</s></span><span>@Grid.DoesNotContainText</span>
</li>
}
@if (Column.GetFilterOperators().Contains(FilterOperator.StartsWith))
@if (Column.GetFilterOperators().Contains(FilterOperator.In))
{
<li class="@(FilterOperatorStyle(Column, FilterOperator.In))" @onclick="@(args => ApplyFilter(FilterOperator.In))" style="display: block;">
<span class="rz-filter-menu-symbol">@Column.GetFilterOperatorSymbol(FilterOperator.In)</span><span>@Grid.InText</span>
</li>
}
@if (Column.GetFilterOperators().Contains(FilterOperator.NotIn))
{
<li class="@(FilterOperatorStyle(Column, FilterOperator.NotIn))" @onclick="@(args => ApplyFilter(FilterOperator.NotIn))" style="display: block;">
<span class="rz-filter-menu-symbol"><s>@Column.GetFilterOperatorSymbol(FilterOperator.NotIn)</s></span><span>@Grid.NotInText</span>
</li>
}
@if (Column.GetFilterOperators().Contains(FilterOperator.StartsWith) && Column.FilterPropertyType == typeof(string))
{
<li class="@(FilterOperatorStyle(Column, FilterOperator.StartsWith))" @onclick="@(args => ApplyFilter(FilterOperator.StartsWith))" style="display: block;">
<span class="rz-filter-menu-symbol">@Column.GetFilterOperatorSymbol(FilterOperator.StartsWith)</span><span>@Grid.StartsWithText</span>
</li>
}
@if (Column.GetFilterOperators().Contains(FilterOperator.EndsWith))
@if (Column.GetFilterOperators().Contains(FilterOperator.EndsWith) && Column.FilterPropertyType == typeof(string))
{
<li class="@(FilterOperatorStyle(Column, FilterOperator.EndsWith))" @onclick="@(args => ApplyFilter(FilterOperator.EndsWith))" style="display: block;">
<span class="rz-filter-menu-symbol">@Column.GetFilterOperatorSymbol(FilterOperator.EndsWith)</span><span>@Grid.EndsWithText</span>
</li>
}
@if (Column.GetFilterOperators().Contains(FilterOperator.IsEmpty))
@if (Column.GetFilterOperators().Contains(FilterOperator.IsEmpty) && Column.FilterPropertyType == typeof(string))
{
<li class="@(FilterOperatorStyle(Column, FilterOperator.IsEmpty))" @onclick="@(args => ApplyFilter(FilterOperator.IsEmpty))" style="display: block;">
<span class="rz-filter-menu-symbol">@Column.GetFilterOperatorSymbol(FilterOperator.IsEmpty)</span><span>@Grid.IsEmptyText</span>
</li>
}
@if (Column.GetFilterOperators().Contains(FilterOperator.IsNotEmpty))
@if (Column.GetFilterOperators().Contains(FilterOperator.IsNotEmpty) && Column.FilterPropertyType == typeof(string))
{
<li class="@(FilterOperatorStyle(Column, FilterOperator.IsNotEmpty))" @onclick="@(args => ApplyFilter(FilterOperator.IsNotEmpty))" style="display: block;">
<span class="rz-filter-menu-symbol">@Column.GetFilterOperatorSymbol(FilterOperator.IsNotEmpty)</span><span>@Grid.IsNotEmptyText</span>

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,184 +2,195 @@
@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))></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="@Column.GetHeaderClass()" @onkeydown:stopPropagation>@Column.HeaderTemplate</span>
}
else
{
<span class="@Column.GetHeaderClass()">@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(); InvokeAsync(() => Grid.Reload()); })" />
}
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
{
<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>
@if (Column.FilterValueTemplate != null)
{
@Column.FilterValueTemplate(Column)
}
else
{
<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 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
{
@@ -188,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 {
@@ -198,33 +209,28 @@ 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 propertyType = PropertyAccess.GetPropertyType(typeof(TItem), property) ?? Column.FilterPropertyType;
var loadDataArgsString = $"{loadDataArgs.Skip}|{loadDataArgs.Top}{loadDataArgs.Filter}";
if (Column.Grid.LoadColumnFilterData.HasDelegate)
{
var args = new DataGridLoadColumnFilterDataEventArgs<TItem>() { Column = Column, Filter = loadDataArgs.Filter };
var args = new DataGridLoadColumnFilterDataEventArgs<TItem>() { Column = Column, Filter = loadDataArgs.Filter, Skip = loadDataArgs.Skip, Top = loadDataArgs.Top };
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)
@@ -235,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
{
@@ -245,35 +251,35 @@ 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().Cast<object>().OrderBy(i => i);
propertyType = query.ElementType;
}
else
{
query = query.Select(DynamicLinqCustomTypeProvider.ParsingConfig, property).Distinct().OrderBy("it").Cast(propertyType ?? typeof(object));
query = query.Select(property).Distinct().Cast<object>().OrderBy(i => i);
}
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)
@@ -299,6 +305,11 @@ else
propertyType = PropertyAccess.GetPropertyType(typeof(TItem), Column.GetFilterProperty());
}
if (propertyType == null)
{
propertyType = Column.FilterPropertyType;
}
Column.SetFilterValue(enumerable.Cast<object>().Any() ? propertyType != null ? enumerable.AsQueryable().Cast(propertyType) : enumerable : null);
}
@@ -314,7 +325,7 @@ else
await popup.ToggleAsync(filterButton);
Grid.allColumns
.Where(c => c != Column && c.headerCell != null)
.Where(c => c.GetVisible() && c.UniqueID != Column.UniqueID)
.Select(c => c.headerCell).ToList()
.ForEach(cell => InvokeAsync(cell.CloseFilter));
}
@@ -329,8 +340,43 @@ else
await Grid.ClearFilter(Column, true);
}
string getBlur()
{
return IsAdvancedNumeric() ? "Radzen.blur(this, event)" : null;
}
bool IsAdvancedNumeric()
{
return (Column.FilterMode ?? Grid.FilterMode) == FilterMode.Advanced &&
PropertyAccess.IsNumeric(Column.FilterPropertyType) &&
!(PropertyAccess.IsEnum(Column.FilterPropertyType) || PropertyAccess.IsNullableEnum(Column.FilterPropertyType));
}
async Task ApplyFilter()
{
if (IsAdvancedNumeric())
{
var targetType = Nullable.GetUnderlyingType(Column.FilterPropertyType) ?? Column.FilterPropertyType;
if (Column.FilterValueTemplate == null)
{
var firstInputValue = await Grid.GetJSRuntime().InvokeAsync<string>("Radzen.getNumericValue", Grid.getFilterInputId(Column) + "f");
if (!object.Equals($"{Column.GetFilterValue()}", $"{firstInputValue}"))
{
Column.SetFilterValue(!string.IsNullOrEmpty(firstInputValue) ? Convert.ChangeType(firstInputValue, targetType) : null);
}
}
if (Column.SecondFilterValueTemplate == null)
{
var secondInputValue = await Grid.GetJSRuntime().InvokeAsync<string>("Radzen.getNumericValue", Grid.getFilterInputId(Column) + "s");
if (!object.Equals($"{Column.GetSecondFilterValue()}", $"{secondInputValue}"))
{
Column.SetFilterValue(!string.IsNullOrEmpty(secondInputValue) ? Convert.ChangeType(secondInputValue, targetType) : null, false);
}
}
}
if (Grid.FilterPopupRenderMode == PopupRenderMode.OnDemand)
{
await popup.CloseAsync();

View File

@@ -3,9 +3,9 @@
@implements IRadzenForm
<CascadingValue Value=@EditContext>
<CascadingValue Value=this>
@{var rowArgs = Grid?.RowAttributes(Item); }
@{var rowArgs = Grid?.RowAttributes(Item, Index); }
@{var firstLevel = Grid.AllowCompositeDataCells ? 0 : Grid.deepestChildColumnLevel; }
@for(var i = firstLevel; i < Grid.deepestChildColumnLevel + 1; i++)
@for(var i = firstLevel; Grid.AllowCompositeDataCells ? i <= Grid.deepestChildColumnLevel + 1 : i < Grid.deepestChildColumnLevel + 1; i++)
{
<tr class="@(Grid.RowStyle(Item, Index))" @attributes="@rowArgs.Item2">
@if (Grid.ShowGroupExpandColumn)
@@ -25,7 +25,7 @@
{
<td class="rz-col-icon" rowspan="@(Grid.AllowCompositeDataCells ? (Grid.deepestChildColumnLevel + 1) : 1)">
<span class="rz-column-title"></span>
@if (rowArgs.Item1.Expandable)
@if (rowArgs.Item1.Expandable && !Grid.LoadChildData.HasDelegate)
{
<a id="@(Grid.GridId() + Item.GetHashCode())" aria-label="@Grid.ExpandChildItemAriaLabel" @onclick:preventDefault="true" @onclick="@(_ => Grid.ExpandItem(Item))" @onclick:stopPropagation>
<span class="@(Grid.ExpandedItemStyle(Item))"></span>

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,24 +1,20 @@
@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 @(IsReadonly ? "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" : "")}")" tabindex="-1" type="button">
<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">
<span aria-hidden="true" class="@ButtonClasses"></span><span class="rz-button-text"></span>
</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")
@@ -146,7 +142,7 @@
@if (ShowTimeOkButton)
{
<button aria-label="@OkAriaLabel" type="button" class="rz-button rz-button-md rz-secondary" tabindex="0" @onclick="@(args => OkClick())">
<span class="rz-button-text">Ok</span>
<span class="rz-button-text">@OkAriaLabel</span>
</button>
}
</div>

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;
@@ -316,6 +315,12 @@ namespace Radzen.Blazor
/// <value>The input CSS class.</value>
[Parameter]
public string InputClass { get; set; }
/// <summary>
/// Gets or sets the button CSS class.
/// </summary>
/// <value>The button CSS class.</value>
[Parameter]
public string ButtonClass { get; set; }
/// <summary>
/// Gets or sets the Minimum Selectable Date.
@@ -744,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>
@@ -911,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>
@@ -971,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)
@@ -1051,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);
@@ -1143,14 +1167,20 @@ namespace Radzen.Blazor
string GetDayCssClass(DateTime date, DateRenderEventArgs dateArgs, bool forCell = true)
{
return ClassList.Create()
var list = ClassList.Create()
.Add("rz-state-default", !forCell)
.Add("rz-calendar-other-month", CurrentDate.Month != date.Month)
.Add("rz-state-active", !forCell && DateTimeValue.HasValue && DateTimeValue.Value.Date.CompareTo(date.Date) == 0)
.Add("rz-calendar-today", !forCell && DateTime.Now.Date.CompareTo(date.Date) == 0)
.Add("rz-state-focused", !forCell && FocusedDate.Date.CompareTo(date.Date) == 0)
.Add("rz-state-disabled", !forCell && dateArgs.Disabled)
.ToString();
.Add("rz-state-disabled", !forCell && dateArgs.Disabled);
if (dateArgs.Attributes != null && dateArgs.Attributes.TryGetValue("class", out var @class) && !string.IsNullOrEmpty(Convert.ToString(@class)))
{
list.Add($"{@class}", true);
}
return list.ToString();
}
async Task OnCalendarKeyPress(KeyboardEventArgs args)
{

View File

@@ -152,7 +152,7 @@
<input id="@SearchID" tabindex="@(Disabled ? "-1" : $"{TabIndex}")" class="rz-inputtext" role="textbox" type="text"
onclick="Radzen.preventDefaultAndStopPropagation(event)" aria-label="@SearchAriaLabel"
@ref="@search" @oninput=@(args => { searchText = $"{args.Value}"; SearchTextChanged.InvokeAsync(searchText);})
@onchange="@((args) => OnFilter(args))" @onkeydown="@((args) => OnFilterKeyPress(args))" value="@searchText" />
@onchange="@((args) => OnFilter(args))" @onkeydown="@((args) => OnFilterKeyPress(args))" value="@searchText" autocomplete="@FilterAutoCompleteType" />
<span class="notranslate rz-multiselect-filter-icon rzi rzi-search"></span>
</div>
}
@@ -191,7 +191,7 @@
return __builder => {
<text>
<div class="rz-dropdown-filter-container">
<input aria-label="@SearchAriaLabel" id="@SearchID" @ref="@search" tabindex="@(Disabled ? "-1" : $"{TabIndex}")" placeholder="@FilterPlaceholder" class="rz-dropdown-filter rz-inputtext" autocomplete="off" aria-autocomplete="none" type="text"
<input aria-label="@SearchAriaLabel" id="@SearchID" @ref="@search" tabindex="@(Disabled ? "-1" : $"{TabIndex}")" placeholder="@FilterPlaceholder" class="rz-dropdown-filter rz-inputtext" autocomplete="@FilterAutoCompleteType" aria-autocomplete="none" type="text"
@onchange="@OnFilter" @onkeydown="@OnFilterKeyPress"
@bind:event="oninput" @bind:get="searchText" @bind:set="@(args => { searchText = $"{args}"; SearchTextChanged.InvokeAsync(searchText);})" />
<span class="notranslate rz-dropdown-filter-icon rzi rzi-search"></span>
@@ -202,7 +202,7 @@
return __builder => {
<text>
<div class="rz-dropdown-filter-container">
<input aria-label="@SearchAriaLabel" id="@SearchID" @ref="@search" tabindex="@(Disabled ? "-1" : $"{TabIndex}")" placeholder="@FilterPlaceholder" class="rz-dropdown-filter rz-inputtext" autocomplete="off" aria-autocomplete="none" type="text"
<input aria-label="@SearchAriaLabel" id="@SearchID" @ref="@search" tabindex="@(Disabled ? "-1" : $"{TabIndex}")" placeholder="@FilterPlaceholder" class="rz-dropdown-filter rz-inputtext" autocomplete="@FilterAutoCompleteType" aria-autocomplete="none" type="text"
@onchange="@OnFilter" @onkeydown="@OnFilterKeyPress" value="@searchText"
@oninput="@((ChangeEventArgs args) => { searchText = $"{args.Value}"; SearchTextChanged.InvokeAsync(searchText);})" />
<span class="notranslate rz-dropdown-filter-icon rzi rzi-search"></span>

View File

@@ -19,6 +19,7 @@ namespace Radzen.Blazor
/// </example>
public partial class RadzenDropDown<TValue> : DropDownBase<TValue>
{
bool isOpen;
/// <summary>
/// Specifies additional custom attributes that will be rendered by the input.
/// </summary>
@@ -68,6 +69,13 @@ namespace Radzen.Blazor
[Parameter]
public string FilterPlaceholder { get; set; } = string.Empty;
/// <summary>
/// Gets or Sets the filter autocomplete type.
/// </summary>
/// <value>The filter autocomplete type. Default: Off</value>
[Parameter]
public AutoCompleteType FilterAutoCompleteType { get; set; } = AutoCompleteType.Off;
/// <summary>
/// Gets or sets the row render callback. Use it to set row attributes.
/// </summary>
@@ -110,8 +118,8 @@ namespace Radzen.Blazor
of = OpenOnFocus;
OpenOnFocus = false;
}
await JSRuntime.InvokeVoidAsync("Radzen.closePopup", PopupID);
isOpen = false;
await JSRuntime.InvokeVoidAsync("Radzen.closePopup", PopupID, Reference, nameof(OnClose));
if (key == "Enter")
{
@@ -131,7 +139,20 @@ namespace Radzen.Blazor
if (Disabled)
return;
await JSRuntime.InvokeVoidAsync(OpenOnFocus ? "Radzen.openPopup" : "Radzen.togglePopup", Element, PopupID, true);
if (!isOpen)
{
await Open.InvokeAsync(null);
}
isOpen = true;
if (OpenOnFocus)
{
await JSRuntime.InvokeVoidAsync("Radzen.openPopup", Element, PopupID, true, null, null, null, Reference, nameof(OnClose));
}
else
{
await JSRuntime.InvokeVoidAsync("Radzen.togglePopup", Element, PopupID, true, Reference, nameof(OnClose));
}
await JSRuntime.InvokeVoidAsync("Radzen.focusElement", isFilter ? UniqueID : SearchID);
if (list != null && selectedIndex != -1)
@@ -191,6 +212,18 @@ namespace Radzen.Blazor
[Parameter]
public string SelectAllText { get; set; }
/// <summary>
/// Callback for when a dropdown is opened.
/// </summary>
[Parameter]
public EventCallback Open { get; set; }
/// <summary>
/// Callback for when a dropdown is closed.
/// </summary>
[Parameter]
public EventCallback Close { get; set; }
private bool visibleChanged = false;
private bool disabledChanged = false;
private bool firstRender = true;
@@ -277,7 +310,8 @@ namespace Radzen.Blazor
{
if (!Multiple && !isFromKey)
{
await JSRuntime.InvokeVoidAsync("Radzen.closePopup", PopupID);
isOpen = false;
await JSRuntime.InvokeVoidAsync("Radzen.closePopup", PopupID, Reference, nameof(OnClose));
}
if (ClearSearchAfterSelection)
@@ -330,9 +364,20 @@ namespace Radzen.Blazor
}
}
/// <summary>
/// Called when popup is closed.
/// </summary>
[JSInvokable]
public async Task OnClose()
{
isOpen = false;
await Close.InvokeAsync();
}
internal async Task PopupClose()
{
await JSRuntime.InvokeVoidAsync("Radzen.closePopup", PopupID);
isOpen = false;
await JSRuntime.InvokeVoidAsync("Radzen.closePopup", PopupID, Reference, nameof(OnClose));
}
}
}

View File

@@ -37,7 +37,7 @@
}
else if ((selectedItems.Count > 0 || SelectedValue is IEnumerable && !(SelectedValue is string)) && Multiple)
{
var itemsToUse = SelectedValue is IEnumerable && !(SelectedValue is string) ? ((IEnumerable)SelectedValue).Cast<object>().ToList() : selectedItems;
var itemsToUse = SelectedValue is IEnumerable && !(SelectedValue is string) ? ((IEnumerable)SelectedValue).Cast<object>().ToHashSet() : selectedItems;
@if (itemsToUse.Count < MaxSelectedLabels && Chips)
{
<div class="rz-dropdown-chips-wrapper">

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
@@ -202,6 +201,13 @@ namespace Radzen.Blazor
[Parameter]
public bool ShowAdd { get; set; } = false;
/// <summary>
/// Gets or sets preserving the selected row index on pageing.
/// </summary>
/// <value>Row selection preservation on pageing.</value>
[Parameter]
public bool PreserveRowSelectionOnPaging { get; set; } = false;
/// <summary>
/// Gets or sets the page numbers count.
/// </summary>
@@ -347,6 +353,7 @@ namespace Radzen.Blazor
/// </summary>
protected ElementReference popup;
bool isFirstRender;
/// <summary>
/// Called when [after render asynchronous].
/// </summary>
@@ -354,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)
@@ -390,7 +399,7 @@ namespace Radzen.Blazor
if (!LoadData.HasDelegate)
{
searchText = null;
await OnLoadData(new Radzen.LoadDataArgs() { Skip = 0, Top = PageSize });
await OnLoadData(new Radzen.LoadDataArgs() { Skip = 0, Top = PageSize, OrderBy = "" });
}
}
@@ -405,7 +414,7 @@ namespace Radzen.Blazor
if (!string.IsNullOrEmpty(searchText) && !LoadData.HasDelegate)
{
await OnLoadData(new Radzen.LoadDataArgs() { Skip = skip, Top = PageSize });
await OnLoadData(new Radzen.LoadDataArgs() { Skip = skip, Top = PageSize, OrderBy = "" });
}
}
@@ -415,7 +424,7 @@ namespace Radzen.Blazor
public async Task Reload()
{
searchText = null;
await OnLoadData(new Radzen.LoadDataArgs() { Skip = 0, Top = PageSize });
await OnLoadData(new Radzen.LoadDataArgs() { Skip = 0, Top = PageSize, OrderBy = "" });
}
private string GetPropertyFilterExpression(string property, string filterCaseSensitivityOperator)
@@ -440,6 +449,7 @@ namespace Radzen.Blazor
}
string prevSearch;
string prevOrder = "";
int? skip;
async Task OnLoadData(LoadDataArgs args)
{
@@ -458,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)
@@ -470,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
@@ -490,33 +503,48 @@ 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;
if (prevOrder != args.OrderBy)
{
prevOrder = args.OrderBy;
await JSRuntime.InvokeVoidAsync("eval");
}
}
else
{
await LoadData.InvokeAsync(new Radzen.LoadDataArgs() { Skip = skip, Top = args.Top, OrderBy = args.OrderBy, Filter = searchText });
}
if(PreserveRowSelectionOnPaging && selectedIndex != -1)
{
var items = (LoadData.HasDelegate ? Data != null ? Data : Enumerable.Empty<object>() : (pagedData != null ? pagedData : Enumerable.Empty<object>())).OfType<object>().ToList();
selectedIndex = Math.Clamp(selectedIndex, 0, items.Count - 1);
await JSRuntime.InvokeAsync<int[]>("Radzen.focusTableRow", grid.GridId(), "ArrowDown", selectedIndex - 1, null);
await grid.OnRowSelect(items[selectedIndex], false);
}
}
IEnumerable _internalView = Enumerable.Empty<object>();
@@ -559,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;
@@ -580,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
@@ -592,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);
}
@@ -603,10 +648,7 @@ namespace Radzen.Blazor
{
foreach (object v in valueList)
{
if (selectedItems.IndexOf(v) == -1)
{
selectedItems.Add(v);
}
selectedItems.Add(v);
}
}
@@ -629,6 +671,8 @@ namespace Radzen.Blazor
if (Disabled)
return;
var canRequest = searchText != null;
searchText = null;
internalValue = default(TValue);
selectedItem = null;
@@ -644,7 +688,10 @@ namespace Radzen.Blazor
await grid.SelectRow(null);
}
await OnLoadData(new Radzen.LoadDataArgs() { Skip = 0, Top = PageSize });
if (canRequest)
{
await OnLoadData(new Radzen.LoadDataArgs() { Skip = 0, Top = PageSize, OrderBy = "" });
}
StateHasChanged();
}
@@ -669,7 +716,7 @@ namespace Radzen.Blazor
if (shouldChange)
{
selectedIndex = newSelectedIndex;
await JSRuntime.InvokeAsync<int[]>("Radzen.focusTableRow", grid.GridId(), key, selectedIndex - 1, null);
await JSRuntime.InvokeAsync<int[]>("Radzen.focusTableRow", grid.GridId(), key, selectedIndex + (key == "ArrowUp" ? 1 : -1), null);
await grid.OnRowSelect(items[selectedIndex], false);
}
@@ -896,8 +943,11 @@ namespace Radzen.Blazor
await SelectItem(item);
}
}
async Task CloseAndFocus()
/// <summary>
/// Closes the dropdown popup and sets focus to the input element.
/// </summary>
/// <returns>A task that represents the asynchronous operation.</returns>
public async Task CloseAndFocus()
{
if (!Disabled && !Multiple)
{

View File

@@ -18,18 +18,29 @@ namespace Radzen.Blazor
[CascadingParameter]
RadzenDropZoneContainer<TItem> Container { get; set; }
void OnDragStart()
void EnsurePayload(DragEventArgs args = null)
{
dragCssClass = "rz-dragging";
Container.Payload = new RadzenDropZoneItemEventArgs<TItem>()
{
FromZone = Zone,
Item = Item
Item = Item,
DataTransfer = args?.DataTransfer
};
}
void OnDragStart()
{
dragCssClass = "rz-dragging";
EnsurePayload();
}
void OnDragOver(DragEventArgs args)
{
if (Container.Payload == null)
{
EnsurePayload(args);
}
Container.Payload.ToItem = Item;
var canDrop = Zone.CanDrop();
@@ -51,6 +62,10 @@ namespace Radzen.Blazor
async Task OnDrop(DragEventArgs args)
{
if (Container.Payload == null)
{
EnsurePayload(args);
}
cssClass = "";
Container.Payload.ToItem = Item;
await Zone.OnDropInternal();

View File

@@ -130,7 +130,7 @@ namespace Radzen.Blazor
{
if (!firstRender)
{
await JSRuntime.InvokeVoidAsync("Radzen.updateMap", UniqueID, Zoom, Center);
await JSRuntime.InvokeVoidAsync("Radzen.updateMap", UniqueID, ApiKey, Zoom, Center);
}
}
@@ -207,12 +207,12 @@ namespace Radzen.Blazor
if (firstRender)
{
await JSRuntime.InvokeVoidAsync("Radzen.createMap", Element, Reference, UniqueID, ApiKey, MapId, Zoom, Center,
data.Select(m => new { Title = m.Title, Label = m.Label, Position = m.Position }), Options, FitBoundsToMarkersOnUpdate);
data.Select(m => new { Title = m.Title, Label = m.Label, Position = m.Position }), Options, FitBoundsToMarkersOnUpdate, Culture.TwoLetterISOLanguageName);
}
else
{
await JSRuntime.InvokeVoidAsync("Radzen.updateMap", UniqueID, null, null,
data.Select(m => new { Title = m.Title, Label = m.Label, Position = m.Position }), Options, FitBoundsToMarkersOnUpdate);
await JSRuntime.InvokeVoidAsync("Radzen.updateMap", UniqueID, ApiKey, null, null,
data.Select(m => new { Title = m.Title, Label = m.Label, Position = m.Position }), Options, FitBoundsToMarkersOnUpdate, Culture.TwoLetterISOLanguageName);
}
}

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

@@ -2,7 +2,7 @@
@inherits RadzenComponent
@if (Visible)
{
<NavLink style="@Style" href="@Path" @attributes="Attributes" class="@GetCssClass()" target="@Target" id="@GetId()" Match="@Match">
<NavLink style="@Style" href="@GetPath()" @attributes="Attributes" class="@GetCssClass()" target="@GetTarget()" id="@GetId()" Match="@Match" >
@if (!string.IsNullOrEmpty(Icon))
{
<i class="notranslate rzi" style="@(!string.IsNullOrEmpty(IconColor) ? $"color:{IconColor}" : null)">@((MarkupString)Icon)</i>

View File

@@ -13,12 +13,6 @@ namespace Radzen.Blazor
/// </example>
public partial class RadzenLink : RadzenComponent
{
/// <inheritdoc />
protected override string GetComponentCssClass()
{
return "rz-link";
}
/// <summary>
/// Gets or sets the text.
/// </summary>
@@ -81,5 +75,35 @@ namespace Radzen.Blazor
/// <value>The navigation link match.</value>
[Parameter]
public NavLinkMatch Match { get; set; } = NavLinkMatch.Prefix;
/// <summary>
/// Gets or sets whether the link is disabled.
/// </summary>
[Parameter]
public bool Disabled { get; set; }
/// <inheritdoc />
protected override string GetComponentCssClass()
{
return Disabled ? "rz-link rz-link-disabled" : "rz-link";
}
/// <summary>
/// Gets the path.
/// </summary>
/// <returns></returns>
protected string GetPath()
{
return !Disabled ? Path : null;
}
/// <summary>
/// Gets the target.
/// </summary>
/// <returns></returns>
protected string GetTarget()
{
return !Disabled ? Target : null;
}
}
}

View File

@@ -0,0 +1,15 @@
@using Radzen.Blazor
@using Radzen.Blazor.Rendering
@inherits SchedulerViewBase
@code {
public override RenderFragment Render()
{
var appointments = Scheduler.GetAppointmentsInRange(StartDate, EndDate).ToList();
return @<CascadingValue Value=@Scheduler>
<WeekView HeaderFormat=@HeaderFormat MinutesPerSlot=@MinutesPerSlot StartDate=@StartDate EndDate=@EndDate StartTime=@StartTime EndTime=@EndTime Appointments=@appointments TimeFormat="@TimeFormat" AppointmentMove=OnAppointmentMove />
</CascadingValue>;
}
}

View File

@@ -0,0 +1,119 @@
using Microsoft.AspNetCore.Components;
using Radzen.Blazor.Rendering;
using System;
namespace Radzen.Blazor
{
/// <summary>
/// Displays the appointments in a multi-day view in <see cref="RadzenScheduler{TItem}" />
/// </summary>
/// <code>
/// &lt;RadzenScheduler Data="@appointments"&gt;
/// &lt;RadzenMultiDayView /&gt;
/// &lt;/RadzenScheduler&gt;
/// </code>
public partial class RadzenMultiDayView : SchedulerViewBase
{
/// <inheritdoc />
public override string Icon => "width_normal";
/// <inheritdoc />
[Parameter]
public override string Text { get; set; } = "Multi-Day";
/// <summary>
/// Gets or sets the time format.
/// </summary>
/// <value>The time format. Set to <c>h tt</c> by default.</value>
[Parameter]
public string TimeFormat { get; set; } = "h tt";
/// <summary>
/// Gets or sets the format used to display the header text.
/// </summary>
/// <value>The header text format. Set to <c>ddd</c> by default.</value>
[Parameter]
public string HeaderFormat { get; set; } = "ddd";
/// <summary>
/// Gets or sets the start time.
/// </summary>
/// <value>The start time.</value>
[Parameter]
public TimeSpan StartTime { get; set; } = TimeSpan.FromHours(8);
/// <summary>
/// Gets or sets the end time.
/// </summary>
/// <value>The end time.</value>
[Parameter]
public TimeSpan EndTime { get; set; } = TimeSpan.FromHours(24);
/// <summary>
/// Gets or sets slot size in minutes. Set to <c>30</c> by default.
/// </summary>
/// <value>The slot size in minutes.</value>
[Parameter]
public int MinutesPerSlot { get; set; } = 30;
/// <summary>
/// Gets or sets number of days to view. Set to <c>2</c> by default.
/// </summary>
/// <value>The number of days.</value>
[Parameter]
public int NumberOfDays { get; set; } = 2;
/// <summary>
/// Gets or sets number of days to advance when using prev / next. Set to <c>1</c> by default.
/// </summary>
/// <value>The number of days to advance.</value>
[Parameter]
public int AdvanceDays { get; set; } = 1;
/// <inheritdoc />
public override DateTime StartDate
{
get
{
return Scheduler.CurrentDate.Date;
}
}
/// <inheritdoc />
public override DateTime EndDate
{
get
{
return StartDate.AddDays(NumberOfDays);
}
}
/// <inheritdoc />
public override string Title
{
get
{
if (StartDate == EndDate.AddDays(-1))
{
return $"{StartDate.ToString(Scheduler.Culture.DateTimeFormat.ShortDatePattern)}";
}
else
{
return $"{StartDate.ToString(Scheduler.Culture.DateTimeFormat.ShortDatePattern)} - {EndDate.AddDays(-1).ToString(Scheduler.Culture.DateTimeFormat.ShortDatePattern)}";
}
}
}
/// <inheritdoc />
public override DateTime Next()
{
return Scheduler.CurrentDate.Date.AddDays(AdvanceDays);
}
/// <inheritdoc />
public override DateTime Prev()
{
return Scheduler.CurrentDate.Date.AddDays(-AdvanceDays);
}
}
}

View File

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

View File

@@ -1,5 +1,6 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.Extensions.Primitives;
using Microsoft.JSInterop;
using System;
using System.Collections.Generic;
@@ -215,6 +216,8 @@ namespace Radzen.Blazor
{
_value = value;
}
stringValue = $"{value}";
}
}
@@ -226,22 +229,22 @@ namespace Radzen.Blazor
{
get
{
if (Value != null)
if (_value != null)
{
if (Format != null)
{
if (Value is IFormattable formattable)
if (_value is IFormattable formattable)
{
return formattable.ToString(Format, Culture);
}
decimal decimalValue = ConvertToDecimal(Value);
decimal decimalValue = ConvertToDecimal(_value);
return decimalValue.ToString(Format, Culture);
}
return Value.ToString();
return _value.ToString();
}
else
{
return "";
return stringValue;
}
}
set
@@ -323,9 +326,17 @@ namespace Radzen.Blazor
/// <param name="args">The <see cref="ChangeEventArgs"/> instance containing the event data.</param>
protected async System.Threading.Tasks.Task OnChange(ChangeEventArgs args)
{
stringValue = $"{args.Value}";
await InternalValueChanged(args.Value);
}
string stringValue;
async Task SetValue(string value)
{
stringValue = value;
await InternalValueChanged(value);
}
private string RemoveNonNumericCharacters(object value)
{
string valueStr = value as string;
@@ -385,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);
@@ -448,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)
@@ -463,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

@@ -43,7 +43,7 @@ namespace Radzen.Blazor
/// Gets or sets the pager's first page button's title attribute.
/// </summary>
[Parameter]
public string FirstPageTitle { get; set; } = "First page.";
public string FirstPageTitle { get; set; } = "First page";
/// <summary>
/// Gets or sets the pager's first page button's aria-label attribute.

View File

@@ -13,20 +13,20 @@
}
<RadzenListBox @bind-Value=@selectedSourceItems Data="@source" Multiple="@Multiple" @bind-SearchText=@sourceSearchText
FilterAsYouType=true FilterCaseSensitivity=FilterCaseSensitivity.CaseInsensitive
TextProperty="@TextProperty" AllowFiltering=@AllowFiltering
Template=@ListBoxTemplate
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))
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()))
ButtonStyle="@ButtonStyle" Size="@ButtonSize" Variant="@ButtonVariant" Shade="@ButtonShade" />
<RadzenButton Icon="keyboard_double_arrow_left" title="@TargetToSourceTitle" Click="@TargetToSource" Disabled=@(Disabled || target == null|| (target != null && !target.Any()))
<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="@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="@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">
@if (ShowHeader && TargetHeader != null)
@@ -35,8 +35,8 @@
}
<RadzenListBox @bind-Value=@selectedTargetItems Data="@target" Multiple="@Multiple" @bind-SearchText=@targetSearchText
FilterAsYouType=true FilterCaseSensitivity=FilterCaseSensitivity.CaseInsensitive
TextProperty="@TextProperty" AllowFiltering=@AllowFiltering
Template=@ListBoxTemplate
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

@@ -14,6 +14,27 @@ namespace Radzen.Blazor
/// </summary>
public partial class RadzenPickList<TItem> : RadzenComponent
{
/// <summary>
/// Gets or sets a value indicating whether it is allowed to move all items.
/// </summary>
/// <value><c>true</c> if c allowed to move all items; otherwise, <c>false</c>.</value>
[Parameter]
public bool AllowMoveAll { get; set; } = true;
/// <summary>
/// Gets or sets a value indicating whether it is allowed to move all items from source to target.
/// </summary>
/// <value><c>true</c> if c allowed to move all items from source to target; otherwise, <c>false</c>.</value>
[Parameter]
public bool AllowMoveAllSourceToTarget { get; set; } = true;
/// <summary>
/// Gets or sets a value indicating whether it is allowed to move all items from target to source.
/// </summary>
/// <value><c>true</c> if c allowed to move all items from target to source; otherwise, <c>false</c>.</value>
[Parameter]
public bool AllowMoveAllTargetToSource { get; set; } = true;
/// <summary>
/// Gets or sets a value indicating whether multiple selection is allowed.
/// </summary>
@@ -21,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>
@@ -42,6 +70,27 @@ namespace Radzen.Blazor
[Parameter]
public RenderFragment TargetHeader { get; set; }
/// <summary>
/// Gets or sets the common placeholder
/// </summary>
/// <value>The common placeholder.</value>
[Parameter]
public string Placeholder { get; set; }
/// <summary>
/// Gets or sets the source placeholder
/// </summary>
/// <value>The source placeholder.</value>
[Parameter]
public string SourcePlaceholder { get; set; }
/// <summary>
/// Gets or sets the target placeholder
/// </summary>
/// <value>The target placeholder.</value>
[Parameter]
public string TargetPlaceholder { get; set; }
/// <summary>
/// Gets or sets the text property
/// </summary>
@@ -49,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>
@@ -56,6 +112,46 @@ namespace Radzen.Blazor
[Parameter]
public RenderFragment<TItem> Template { get; set; }
/// <summary>
/// Gets or sets the select all text.
/// </summary>
/// <value>The select all text.</value>
[Parameter]
public string SelectAllText { get; set; }
/// <summary>
/// Gets or sets the row render callback. Use it to set row attributes.
/// </summary>
/// <value>The row render callback.</value>
[Parameter]
public Action<PickListItemRenderEventArgs<TItem>> ItemRender { get; set; }
void OnSourceItemRender(ListBoxItemRenderEventArgs<object> args)
{
if (ItemRender != null)
{
var newArgs = new PickListItemRenderEventArgs<TItem>();
newArgs.Item = args.Item != null ? (TItem)args.Item : default(TItem);
ItemRender(newArgs);
newArgs.Attributes.ToList().ForEach(k => args.Attributes.Add(k.Key, k.Value));
args.Visible = newArgs.Visible;
args.Disabled = newArgs.Disabled;
}
}
void OnTargetItemRender(ListBoxItemRenderEventArgs<object> args)
{
if (ItemRender != null)
{
var newArgs = new PickListItemRenderEventArgs<TItem>();
newArgs.Item = args.Item != null ? (TItem)args.Item : default(TItem);
ItemRender(newArgs);
newArgs.Attributes.ToList().ForEach(k => args.Attributes.Add(k.Key, k.Value));
args.Visible = newArgs.Visible;
args.Disabled = newArgs.Disabled;
}
}
private RenderFragment<dynamic> ListBoxTemplate => Template != null ? item => Template((TItem)item) : null;
/// <summary>
@@ -149,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>
@@ -234,7 +358,25 @@ namespace Radzen.Blazor
await base.SetParametersAsync(parameters);
}
async Task Update(bool sourceToTarget, IEnumerable<TItem> items)
/// <summary>
/// Returns a collection of TItem that are selected in the source list.
/// </summary>
/// <returns></returns>
public IEnumerable<TItem> GetSelectedSources()
{
return Multiple ? (selectedSourceItems as IEnumerable)?.Cast<TItem>() : [(TItem)selectedSourceItems];
}
/// <summary>
/// Returns a collection of TItem that are selected in the target list.
/// </summary>
/// <returns></returns>
public IEnumerable<TItem> GetSelectedTargets()
{
return Multiple ? (selectedTargetItems as IEnumerable)?.Cast<TItem>() : [(TItem)selectedTargetItems];
}
private async Task Update(bool sourceToTarget, IEnumerable<TItem> items)
{
if (sourceToTarget)
{
@@ -281,24 +423,12 @@ namespace Radzen.Blazor
StateHasChanged();
}
async Task SourceToTarget()
{
await Update(true, null);
}
private async Task SourceToTarget() => await Update(true, null);
async Task SelectedSourceToTarget()
{
await Update(true, Multiple ? (selectedSourceItems as IEnumerable)?.Cast<TItem>() : new List<TItem>() { (TItem)selectedSourceItems } );
}
private async Task SelectedSourceToTarget() => await Update(true, GetSelectedSources());
async Task TargetToSource()
{
await Update(false, null);
}
private async Task TargetToSource() => await Update(false, null);
async Task SelectedTargetToSource()
{
await Update(false, Multiple ? (selectedTargetItems as IEnumerable)?.Cast<TItem>() : new List<TItem>() { (TItem)selectedTargetItems });
}
private async Task SelectedTargetToSource() => await Update(false, GetSelectedTargets());
}
}

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 });
}
}
@@ -600,7 +630,7 @@ namespace Radzen.Blazor
{
if (Data == null)
{
return Array.Empty<AppointmentData>();
return [];
}
if (start == rangeStart && end == rangeEnd && appointments != null)
@@ -611,10 +641,11 @@ 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 { Property = EndProperty, FilterValue = start, FilterOperator = FilterOperator.GreaterThanOrEquals },
new FilterDescriptor { Property = StartProperty, 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

@@ -52,6 +52,10 @@ namespace Radzen.Blazor
[Parameter]
public double OffsetY { get; set; }
/// <summary> The color of the annotation text. </summary>
[Parameter]
public string Fill { get; set; }
/// <summary> Determines whether the annotation is visible. Set to <c>true</c> by default.</summary>
[Parameter]
public bool Visible { get; set; } = true;
@@ -130,9 +134,10 @@ namespace Radzen.Blazor
{
builder.OpenElement(0, "g");
builder.OpenComponent<Text>(1);
builder.AddAttribute(2, "Value", Text);
builder.AddAttribute(3, "Position", new Point{ X = x, Y = y });
builder.AddAttribute(4, "TextAnchor", textAnchor);
builder.AddAttribute(2, nameof(Rendering.Text.Value), Text);
builder.AddAttribute(3, nameof(Rendering.Text.Position), new Point{ X = x, Y = y });
builder.AddAttribute(4, nameof(Rendering.Text.TextAnchor), textAnchor);
builder.AddAttribute(5, nameof(Rendering.Text.Fill), Fill);
builder.SetKey($"{Text}-{Chart.Series.IndexOf(series)}");
builder.CloseComponent();
builder.CloseElement();
@@ -152,11 +157,17 @@ namespace Radzen.Blazor
}
/// <inheritdoc/>
public RenderFragment RenderTooltip(double mouseX, double mouseY, double marginLeft, double marginTop)
public RenderFragment RenderTooltip(double mouseX, double mouseY)
{
return null;
}
/// <inheritdoc />
public Point GetTooltipPosition(double mouseX, double mouseY)
{
return new Point { X = mouseX, Y = mouseY };
}
/// <inheritdoc/>
public void Dispose() => series?.Overlays.Remove(this);
}

View File

@@ -54,10 +54,16 @@
return false;
}
public RenderFragment RenderTooltip(double mouseX, double mouseY, double marginLeft, double marginTop)
public RenderFragment RenderTooltip(double mouseX, double mouseY)
{
return null;
}
/// <inheritdoc />
public Point GetTooltipPosition(double mouseX, double mouseY)
{
return new Point { X = mouseX, Y = mouseY };
}
public void Dispose() => series?.Overlays.Remove(this);
}

View File

@@ -73,10 +73,16 @@
return false;
}
public RenderFragment RenderTooltip(double mouseX, double mouseY, double marginLeft, double marginTop)
public RenderFragment RenderTooltip(double mouseX, double mouseY)
{
return null;
}
/// <inheritdoc />
public Point GetTooltipPosition(double mouseX, double mouseY)
{
return new Point { X = mouseX, Y = mouseY };
}
public void Dispose() => series?.Overlays.Remove(this);
}

View File

@@ -88,19 +88,15 @@
}
}
public RenderFragment RenderTooltip(double mouseX, double mouseY, double marginLeft, double marginTop)
public RenderFragment RenderTooltip(double mouseX, double mouseY)
{
string text;
if (Chart.ShouldInvertAxes())
{
mouseX = Chart.CategoryScale.Scale(Value) + marginLeft;
mouseY = Math.Max(marginTop, Math.Min(Chart.ValueScale.OutputSize + marginTop, mouseY));
text = Chart.ValueAxis.Format(Chart.CategoryScale, Value);
}
else
{
mouseY = Chart.ValueScale.Scale(Value) + marginTop;
mouseX = Math.Max(marginLeft, Math.Min(Chart.CategoryScale.OutputSize + marginLeft, mouseX));
text = Chart.ValueAxis.Format(Chart.ValueScale, Value);
}
@@ -108,20 +104,35 @@
{
builder.OpenComponent<ChartTooltip>(0);
builder.AddAttribute(1, nameof(ChartTooltip.X), mouseX);
builder.AddAttribute(2, nameof(ChartTooltip.Y), mouseY);
builder.AddAttribute(1, nameof(ChartTooltip.ChildContent), TooltipTemplate?.Invoke(Value));
builder.AddAttribute(3, nameof(ChartTooltip.ChildContent), TooltipTemplate == null ? null : TooltipTemplate(Value));
builder.AddAttribute(6, nameof(ChartTooltip.Title), series.GetTitle());
builder.AddAttribute(2, nameof(ChartTooltip.Label), Name);
builder.AddAttribute(3, nameof(ChartTooltip.Value), text);
builder.AddAttribute(8, nameof(ChartTooltip.Title), series.GetTitle());
builder.AddAttribute(4, nameof(ChartTooltip.Label), Name);
builder.AddAttribute(5, nameof(ChartTooltip.Value), text);
builder.AddAttribute(6, nameof(ChartTooltip.Style), $"border: 1px solid {Stroke};");
builder.AddAttribute(7, nameof(ChartTooltip.Class), "");
builder.AddAttribute(4, nameof(ChartTooltip.Style), $"border: 1px solid {Stroke};");
builder.AddAttribute(5, nameof(ChartTooltip.Class), "");
builder.CloseComponent();
};
}
/// <inheritdoc />
public Point GetTooltipPosition(double mouseX, double mouseY)
{
if (Chart.ShouldInvertAxes())
{
mouseX = Chart.CategoryScale.Scale(Value);
mouseY = Math.Max(0, Math.Min(Chart.ValueScale.OutputSize, mouseY));
}
else
{
mouseY = Chart.ValueScale.Scale(Value);
mouseX = Math.Max(0, Math.Min(Chart.CategoryScale.OutputSize, mouseX));
}
return new Point { X = mouseX, Y = mouseY };
}
public void Dispose() => series?.Overlays.Remove(this);
}

View File

@@ -11,14 +11,14 @@
<div @ref="Element" style="@Style" @attributes="Attributes" class="@GetCssClass()" id="@GetId()">
@if (Range)
{
<span class="rz-slider-range" style="inset-inline-start: @(Left.ToInvariantString())%; width: @((SecondLeft - Left).ToInvariantString())%;"></span>
<span tabindex="@(Disabled ? -1 : 0)" @ref="minHandle" class="rz-slider-handle" style="inset-inline-start: @(Left.ToInvariantString())%; bottom: auto;" @onkeydown="@(args => OnKeyPress(args, true))" @onkeydown:preventDefault=preventKeyPress @onkeydown:stopPropagation></span>
<span tabindex="@(Disabled ? -1 : 0)" @ref="maxHandle" class="rz-slider-handle rz-slider-handle-active" style="inset-inline-start: @(SecondLeft.ToInvariantString())%; bottom: auto;" @onkeydown="@(args => OnKeyPress(args, false))" @onkeydown:preventDefault=preventKeyPress @onkeydown:stopPropagation></span>
<span class="rz-slider-range" style="@(Orientation == Orientation.Horizontal ? "inset-inline-start" : "top"): @((Orientation == Orientation.Horizontal ? Left : 100 - SecondLeft).ToInvariantString())%; @(Orientation == Orientation.Horizontal ? "width" : "height"): @((SecondLeft - Left).ToInvariantString())%;"></span>
<span tabindex="@(Disabled ? -1 : 0)" @ref="minHandle" class="rz-slider-handle @($"rz-slider-handle-{Orientation.ToString().ToLowerInvariant()}")" style="@(Orientation == Orientation.Horizontal ? "inset-inline-start" : "top"): @((Orientation == Orientation.Horizontal ? Left : 100 - Left).ToInvariantString())%; bottom: auto;" @onkeydown="@(args => OnKeyPress(args, true))" @onkeydown:preventDefault=preventKeyPress @onkeydown:stopPropagation></span>
<span tabindex="@(Disabled ? -1 : 0)" @ref="maxHandle" class="rz-slider-handle @($"rz-slider-handle-{Orientation.ToString().ToLowerInvariant()}") rz-slider-handle-active" style="@(Orientation == Orientation.Horizontal ? "inset-inline-start" : "top"): @((Orientation == Orientation.Horizontal ? SecondLeft : 100 - SecondLeft).ToInvariantString())%; bottom: auto;" @onkeydown="@(args => OnKeyPress(args, false))" @onkeydown:preventDefault=preventKeyPress @onkeydown:stopPropagation></span>
}
else
{
<span class="rz-slider-range rz-slider-range-min" style="width: @(Left.ToInvariantString())%;"></span>
<span tabindex="@(Disabled ? -1 : 0)" @ref="handle" class="rz-slider-handle" style="inset-inline-start: @(Left.ToInvariantString())%;" @onkeydown="@(args => OnKeyPress(args, false))" @onkeydown:preventDefault=preventKeyPress @onkeydown:stopPropagation></span>
<span class="rz-slider-range rz-slider-range-min" style="@(Orientation == Orientation.Horizontal ? "width" : "height"): @(Left.ToInvariantString())%;@(Orientation == Orientation.Horizontal ? "" : $"top:{(100 - Left).ToInvariantString()}%")"></span>
<span tabindex="@(Disabled ? -1 : 0)" @ref="handle" class="rz-slider-handle @($"rz-slider-handle-{Orientation.ToString().ToLowerInvariant()}")" style="@(Orientation == Orientation.Horizontal ? "inset-inline-start" : "top"): @((Orientation == Orientation.Horizontal ? Left : 100 - Left).ToInvariantString())%;" @onkeydown="@(args => OnKeyPress(args, false))" @onkeydown:preventDefault=preventKeyPress @onkeydown:stopPropagation></span>
}
</div>
}

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
@@ -91,7 +90,7 @@ namespace Radzen.Blazor
if (Visible && !Disabled)
{
await JSRuntime.InvokeVoidAsync("Radzen.createSlider", UniqueID, Reference, Element, Range, Range ? minHandle : handle, maxHandle, Min, Max, Value, Step);
await JSRuntime.InvokeVoidAsync("Radzen.createSlider", UniqueID, Reference, Element, Range, Range ? minHandle : handle, maxHandle, Min, Max, Value, Step, Orientation == Orientation.Vertical);
StateHasChanged();
}
@@ -194,9 +193,15 @@ namespace Radzen.Blazor
/// <inheritdoc />
protected override string GetComponentCssClass()
{
return $"rz-slider {(Disabled ? "rz-state-disabled " : "")}rz-slider-horizontal";
return $"rz-slider {(Disabled ? "rz-state-disabled " : "")}{(Orientation == Orientation.Vertical ? "rz-slider-vertical" : "rz-slider-horizontal")}";
}
/// <summary>
/// Specifies the orientation. Set to <c>Orientation.Horizontal</c> by default.
/// </summary>
[Parameter]
public Orientation Orientation { get; set; } = Orientation.Horizontal;
/// <summary>
/// Gets or sets the value.
/// </summary>
@@ -316,7 +321,7 @@ namespace Radzen.Blazor
{
var key = args.Code != null ? args.Code : args.Key;
if (key == "ArrowLeft" || key == "ArrowRight")
if (Orientation == Orientation.Horizontal ? key == "ArrowLeft" || key == "ArrowRight" : key == "ArrowUp" || key == "ArrowDown")
{
preventKeyPress = true;
@@ -329,13 +334,13 @@ namespace Radzen.Blazor
var oldMinValueAsDecimal = (decimal)ConvertType.ChangeType(oldMinValue, typeof(decimal));
var oldMaxValueAsDecimal = (decimal)ConvertType.ChangeType(oldMaxValue, typeof(decimal));
await OnValueChange((isMin ? oldMinValueAsDecimal : oldMaxValueAsDecimal) + (key == "ArrowLeft" ? -step : step), isMin);
await OnValueChange((isMin ? oldMinValueAsDecimal : oldMaxValueAsDecimal) + (key == "ArrowLeft" || key == "ArrowDown" ? -step : step), isMin);
}
else
{
var valueAsDecimal = Value == null ? 0 : (decimal)ConvertType.ChangeType(Value, typeof(decimal));
await OnValueChange(valueAsDecimal + (key == "ArrowLeft" ? -step : step), isMin);
await OnValueChange(valueAsDecimal + (key == "ArrowLeft" || key == "ArrowDown" ? -step : step), isMin);
}
}
else

View File

@@ -4,7 +4,7 @@
@if (Visible)
{
<div @ref="@Element" style="@Style" @attributes="Attributes" class="@GetCssClass()" id="@GetId()"
tabindex="0" @onkeydown="@OnKeyPress" @onkeydown:preventDefault=preventKeyPress @onkeydown:stopPropagation>
tabindex="@(Disabled ? -1 : TabIndex)" @onkeydown="@OnKeyPress" @onkeydown:preventDefault=preventKeyPress @onkeydown:stopPropagation>
<button aria-label="@(ButtonAriaLabel ?? Text)" tabindex="-1" disabled="@IsDisabled" class="@getButtonCss()" type="button" @onclick="@OnClick">
<span class="rz-button-box">
@if (ButtonContent != null)

View File

@@ -145,6 +145,13 @@ namespace Radzen.Blazor
[Parameter]
public string DropDownIcon { get; set; } = "arrow_drop_down";
/// <summary>
/// Gets or sets the index of the tab.
/// </summary>
/// <value>The index of the tab.</value>
[Parameter]
public int TabIndex { get; set; } = 0;
/// <summary>
/// Gets or sets the click callback.
/// </summary>

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