Compare commits

...

483 Commits

Author SHA1 Message Date
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
Vladimir Enchev
cdbd20b794 Version updated 2024-10-07 10:34:43 +03:00
Vladimir Enchev
49567ade65 DataGrid CheckBoxList filter values ordered by default 2024-10-06 10:23:28 +03:00
Vladimir Enchev
4d857449d2 DataGrid CheckBoxList filter boolean localization example added 2024-10-04 11:36:10 +03:00
Vladimir Enchev
dc03621a14 DataGrid column FormatString can be used to localize boolean in CheckBoxList filter 2024-10-04 09:03:16 +03:00
Vladimir Enchev
b83cca17e2 Fixed DropDown will focus disabled items in some cases 2024-10-03 15:06:49 +03:00
Vladimir Enchev
343afffd40 Fixed DatePicker can select disabled date if focused 2024-10-03 11:44:59 +03:00
Vladimir Enchev
163eac9511 Version updated 2024-10-03 10:11:32 +03:00
Vladimir Enchev
167bfcb0b3 Tabs focus logic with Visible=false tabs fixed
Fix #1722
2024-10-03 09:48:19 +03:00
Vladimir Enchev
f0f94eb84c Popup with DataGrid demo improved 2024-10-03 09:14:52 +03:00
Vladimir Enchev
7123c9ca12 SplitButton PopupID fixed 2024-10-03 09:01:23 +03:00
Vladimir Enchev
41e12d8e54 More focusTableRow improvements 2024-10-02 22:00:34 +03:00
Vladimir Enchev
473ab14ae6 focusTableRow improved 2024-10-02 21:57:43 +03:00
Vladimir Enchev
127ffd451c Keyboard navigation added to Popup with DataGrid demo 2024-10-02 18:16:53 +03:00
Vladimir Enchev
32bb43b66f DataFilter InText and NotInText added
Fix #1721
2024-10-02 17:53:31 +03:00
Vladimir Enchev
765f5acbcc Upload should not raise change if file is not selected 2024-10-01 16:54:22 +03:00
Vladimir Enchev
8ac137f22d Fixed FileInput error when canceled
Fix #1720
2024-10-01 16:51:07 +03:00
Vladimir Enchev
10bbf6b157 Version updated 2024-10-01 16:17:41 +03:00
ax-meyer
390dc0605b Use ToString() on property to filter in DataGrid CheckBoxList to check for empty strings (#1718)
The change in 35c9f55c41 broke filtering when the property to be filtered was not a string.
2024-10-01 16:17:06 +03:00
Vladimir Enchev
6fda9f0691 Version updated 2024-10-01 14:47:38 +03:00
Vladimir Enchev
0a1c5830d2 Fixed DataGrid enum filtering in simple mode 2024-10-01 14:47:21 +03:00
Vladimir Enchev
33efa2316d Version updated 2024-10-01 11:12:48 +03:00
Vladimir Enchev
b3de9374a7 Popup AutoFocusFirstElement property added
Popup demos updated.
2024-10-01 11:12:15 +03:00
Victor Ureta
1d869e8d2b Added AdditionalContent Parameter to Scheduler (#1717)
* feat: add AdditionalContent parameter for rendering custom content next to year, month, and day buttons in RadzenScheduler

* Change name variable
2024-10-01 08:49:10 +03:00
Atanas Korchev
56156dd5ff RadzenHtmlEditor applies valid and invalid classes during validation. 2024-09-30 20:01:23 +03:00
Vladimir Enchev
35c9f55c41 DataGrid CheckBoxList filter should use null instead empty string 2024-09-30 16:26:01 +03:00
Vladimir Enchev
e70e3559eb tests fixed 2024-09-30 10:26:58 +03:00
Vladimir Enchev
5957439475 Version updated 2024-09-30 10:13:44 +03:00
yordanov
322ed552ed Update premium themes 2024-09-30 09:37:59 +03:00
Antoine Gagné
bf848d5ef8 Fixed input width in RadzenFormFields (#1715) 2024-09-30 09:03:35 +03:00
Vladimir Enchev
285057a9df Filtering by nullable enum fixed 2024-09-30 07:58:15 +03:00
fallen576
4de62ffa55 Enum filtering using underlying types other than Int32 is now supported. (#1714) 2024-09-30 07:43:25 +03:00
Vladimir Enchev
f97fde1387 Update DataGridMixedAdvancedFilterPage.razor 2024-09-28 08:46:49 +03:00
Vladimir Enchev
8a5bba625c DataGrid will group wrong column in case of Visible=false columns 2024-09-27 08:07:04 +03:00
yordanov
108a209ac8 Update buttons' hover state in material3-dark. Resolves #1655 2024-09-26 17:10:34 +03:00
yordanov
8390a4aba9 Use forced-colors media query instead of -ms-high-contrast. Resolves #1712 2024-09-26 16:28:27 +03:00
yordanov
9740416e56 Hide unwanted focused outline around the table in RadzenDataGrid 2024-09-26 15:57:46 +03:00
yordanov
8cc82642f9 Update Popup demo 2024-09-26 14:40:30 +03:00
Vladimir Enchev
5f4804f4cf maps.googleapis.com added to font and styles csp exceptions 2024-09-25 13:35:44 +03:00
Vladimir Enchev
24dbee5d3d googleapis added to csp 2024-09-25 13:26:55 +03:00
Vladimir Enchev
298f84d25e DataGrid simple DateTime filter popup button will show active filter state 2024-09-25 10:02:46 +03:00
Vladimir Enchev
182c66f0a9 Added debug symbols to nuget package and updated the version 2024-09-24 15:44:41 +03:00
Vladimir Enchev
fd11c34646 Version updated 2024-09-24 15:20:16 +03:00
Vladimir Enchev
6bcc7388bb RadzenNumeric prevents tab out when up/down key pressed before
Fix #1711
2024-09-24 15:15:14 +03:00
Vladimir Enchev
60da0c0082 Added 'notranslate' CSS class to all icons 2024-09-24 15:11:09 +03:00
Vladimir Enchev
2b2b48bec4 DataGrid column filters not cleared properly 2024-09-24 13:24:01 +03:00
Vladimir Enchev
d579f79399 DatePicker with Inline=true should not close or toggle popup 2024-09-23 11:43:17 +03:00
Vladimir Enchev
bf85df68b3 DatePicker as calendar, disappears when press escape key
Fix #1706
2024-09-23 08:56:40 +03:00
Vladimir Enchev
e6e7ec49df Mask component value not updated on change in Safari 2024-09-20 09:34:27 +03:00
Vladimir Enchev
e7801e1222 Version updated 2024-09-19 10:11:28 +03:00
Vladimir Enchev
3327254022 DataGrid filter aria label properties misspelled 2024-09-19 10:10:49 +03:00
Vladimir Enchev
d53d09e358 Version updated 2024-09-19 08:54:23 +03:00
Vladimir Enchev
34c02ac391 Upload will unable to upload empty file
Fix #1702
2024-09-19 08:51:26 +03:00
Atanas Korchev
cf4eb0c9cd Chart series annotations are sometimes duplicated. 2024-09-18 16:22:42 +03:00
Atanas Korchev
ee6220b1fe DataGrid row drag and drop works in day and month view. 2024-09-18 14:43:51 +03:00
Vladimir Enchev
a264638726 demo improved 2024-09-18 10:47:32 +03:00
Atanas Korchev
3e6a10a57d DataAnnotationValidator throws exception when used with double properties. Fixes #1689. 2024-09-18 09:55:55 +03:00
Vladimir Enchev
5258816770 SecurityCode input not focused after count change
Close #1700
2024-09-18 09:27:41 +03:00
Vladimir Enchev
fb4457f9a1 Dialog closes on ESC even if CloseDialogOnEsc = false in some cases
Fix #1699
2024-09-17 20:01:25 +03:00
Vladimir Enchev
627a74038c demos improved 2024-09-17 14:59:47 +03:00
Tcsaba66
54c297e898 create optional parameters for radzenhtmleritorimage (#1691) 2024-09-17 14:22:08 +03:00
Vladimir Enchev
a4d7201f7d DataFilter ToFilterString() should traverse filters exactly like Where() extension method 2024-09-17 09:51:15 +03:00
Atanas Korchev
b04cf69b90 Change the documentation link to point to the API reference. 2024-09-16 17:26:56 +03:00
Atanas Korchev
ea654f0b8d Remove the guides and redirect to the demos. 2024-09-16 17:14:03 +03:00
Vladimir Enchev
9b16aaf9e4 Version updated 2024-09-16 16:16:50 +03:00
yordanov
87672de761 Add utility css classes for display, overflow, sizing and borders 2024-09-16 16:13:02 +03:00
Vladimir Enchev
171a8def6d DataGrid KeyDown event added 2024-09-16 16:07:32 +03:00
Equitis
f09179f4cd Insert NumpadDecimal at cursor position. (#1693)
Co-authored-by: Patrick Fuhr <p.fuhr@solutit.de>
2024-09-16 16:01:54 +03:00
Vladimir Enchev
03f62068b3 DatePicker year DropDown should respect Culture 2024-09-16 14:59:51 +03:00
Atanas Korchev
828b4230e3 Required validation triggers unnecessarily for RadzenHtmlEditor. 2024-09-16 14:45:55 +03:00
Vladimir Enchev
2b23e5d472 demo fixed 2024-09-16 12:56:40 +03:00
Vladimir Enchev
17dbe70460 Pager will focus clicked button if not disabled 2024-09-13 16:38:45 +03:00
Atanas Korchev
587c3a0647 Remove outdated getting started instructions from the documentation. 2024-09-13 13:49:08 +03:00
Vladimir Enchev
83f0879b11 demo update to use Change instead onblur event 2024-09-13 10:53:38 +03:00
Vladimir Enchev
212b741828 demo updated 2024-09-13 10:26:23 +03:00
Vladimir Enchev
ee81b608f9 AutoComplete property deleted. All cases will be supported trough custom attribute 2024-09-12 16:06:23 +03:00
Vladimir Enchev
ad17080ee8 AutoComplete parameter type changed from bool to object to comply with .NET9
In .NET 9 it's not longer possible to have custom attribute and parameter with similar names - casing is ignored.
Details: https://github.com/dotnet/razor/issues/8854
2024-09-12 15:01:51 +03:00
Atanas Korchev
6a8611c348 Implement UploadComplete event in RadzenHtmlEditor. 2024-09-12 13:06:47 +03:00
Vladimir Enchev
3ba3610b21 DataGrid CheckBoxList column filter not refreshed on search when AllowCheckBoxListVirtualization="false" 2024-09-12 10:36:02 +03:00
Vladimir Enchev
8deed23e01 PropertyAccess.GetDynamicPropertyExpression() method added 2024-09-11 17:04:11 +03:00
Monsieurvor
356106263b NumericRangeValidator: AllowNull (#1686)
* AllowNull range validator

* Change comment

* Add AllowNull test case

* Fix test assertion

* Update RadzenNumericRangeValidator.cs
2024-09-11 15:25:10 +03:00
Vladimir Enchev
0566244b61 CSP tab added 2024-09-11 11:02:47 +03:00
Vladimir Enchev
17d76b4e6f Version updated 2024-09-11 09:57:55 +03:00
Vladimir Enchev
9c3490f275 Content-Security-Policy added to demos 2024-09-11 09:56:24 +03:00
Vladimir Enchev
b1b6499d7c DataGrid column AllowCheckBoxListVirtualization property added 2024-09-10 18:07:51 +03:00
Vladimir Enchev
a36a74091f Fixed RadzenDataGrid Grouping very slow on the first render
Fix #1684
2024-09-10 17:16:12 +03:00
Vladimir Enchev
4dfbd676af Fixed DatePicker logic for HoursStep, MinutesStep and SecondsStep 2024-09-10 08:24:19 +03:00
Vladimir Enchev
f72f8b3e39 Version updated 2024-09-09 12:36:34 +03:00
Vladimir Enchev
45cc6880f9 DataGrid should close other columns filter popups when FilterPopupRenderMode == PopupRenderMode.OnDemand 2024-09-09 11:04:35 +03:00
Vladimir Enchev
17281cd3b1 Numeric inputmode attribute can be defined using InputAttributes 2024-09-09 10:28:47 +03:00
Vladimir Enchev
332b452b56 Numeric inputmode attribute support fixed 2024-09-09 09:32:44 +03:00
Vladimir Enchev
c647515186 DataFilter IsEmoty/IsNotEmpty operators logic fixed 2024-09-08 15:15:54 +03:00
Vladimir Enchev
6faf0ed9b9 DataFilter 'Is Null' & 'Is Not Null' filter options do not produce correct filter via ToFilterString()
Fix #1682
2024-09-08 15:08:53 +03:00
Lynkle
fad024d9bb fix(FileInput): Modified FileInput.razor to handle rendering the preview of an image correctly when using byte[] as the TValue instead of string. Added a corresponding demo to highlight this. (#1680) 2024-09-08 08:29:45 +03:00
Vladimir Enchev
67718df278 Version updated 2024-09-06 09:26:34 +03:00
Christoph
273c3283c2 Add Reload method to RadzenTree (#1678)
* Added demo to illustrate the refresh problem with dynamic loading.

* Added RadzenTree.Reload public API.

* Documentation added.
2024-09-06 09:24:01 +03:00
Vladimir Enchev
aa7306ebd1 DropDownDataGrid OpenOnFocus logic fixed 2024-09-06 09:16:27 +03:00
Vladimir Enchev
56cb173ad5 DataGrid will not render colgroup if there are child columns
Fix #1674
2024-09-05 14:50:06 +03:00
Vladimir Enchev
174f0a9393 demo updated 2024-09-05 09:26:50 +03:00
Vladimir Enchev
a1dbc1a6fb DataGrid exception when trying to filter DateTimeOffset 2024-09-04 09:02:39 +03:00
Vladimir Enchev
d9de2ee0bc Version updated 2024-09-03 17:45:31 +03:00
Vladimir Enchev
cf1e0eda27 Fixed DataGrid string column will always apply Contains for initial filter 2024-09-03 17:40:37 +03:00
Vladimir Enchev
ccc82e5a61 DropDownDataGrid @bind-SelectedItem can cause endless loop in some cases 2024-09-02 14:08:43 +03:00
Vladimir Enchev
0859f939dd DataGrid CheckBoxList filter breaks cells value from enum description 2024-09-02 10:25:11 +03:00
Atanas Korchev
519c133c0c Update the descriptions and content of the RadzenHtmlEditor demos. 2024-09-02 10:22:48 +03:00
Atanas Korchev
9f62860821 The user can insert images and links in RadzenHtmlEditor by pressing Enter. 2024-09-02 09:51:24 +03:00
Atanas Korchev
238ed1ac2c The user can select an image in RadzenHtmlEditor by clicking it. Then use the Insert image tool to set its attributes. Closes #1209. 2024-09-02 09:51:24 +03:00
Vladimir Enchev
cd5903b358 Version updated 2024-09-02 09:08:33 +03:00
Vladimir Enchev
f78125c4fb DropDownDataGrid focus of selected item fixed 2024-09-02 09:06:54 +03:00
Atanas Korchev
6394241b9f Shared tooltips do not work in some cases. 2024-09-01 20:54:57 +03:00
Vladimir Enchev
d4dfc9bbab Fixed Slider with Min="1" change value with click 2024-08-31 09:32:27 +03:00
Vladimir Enchev
cf668be964 Version updated 2024-08-30 18:41:03 +03:00
Vladimir Enchev
6ef443d724 DropDownDataGrid Cannot read properties of undefined (reading 'classList') error fixed 2024-08-30 18:40:36 +03:00
Vladimir Enchev
75af217864 demo title fixed 2024-08-30 11:46:48 +03:00
Vladimir Enchev
7a7343c3c8 demo updated 2024-08-30 11:39:59 +03:00
Vladimir Enchev
18bfa8f562 DataGrid will allow mixed Simple and SimpleWithMenu modes per column 2024-08-30 11:38:46 +03:00
Vladimir Enchev
a76a0815a0 DataGrid column FilterMode property added
Fix #1672
2024-08-30 11:07:18 +03:00
Vladimir Enchev
332f384a65 DropDown selected item not highlighted on initial popup open 2024-08-29 09:52:38 +03:00
Vladimir Enchev
3c5e1216c8 Version updated 2024-08-29 09:17:42 +03:00
Vladimir Enchev
c7c11a1c1e DropDownDataGrid keyboard navigation fixed 2024-08-29 09:17:23 +03:00
Vladimir Enchev
0dd993c1d0 version updated 2024-08-28 10:09:36 +03:00
Vladimir Enchev
c977e9b59c DropDownDataGrid row not selected on initial selection 2024-08-28 10:09:16 +03:00
Vladimir Enchev
81055b5500 DataGrid composite column GroupFooterTemplate fixed 2024-08-28 09:53:00 +03:00
Vladimir Enchev
c8df446846 demo fixed 2024-08-28 09:15:36 +03:00
Vladimir Enchev
97ac4621b9 DataList ShowEmptyMessage disabled by default 2024-08-26 13:07:38 +03:00
Vladimir Enchev
9346c474cf DataGrid simple filter input autocomplete disabled 2024-08-26 13:06:24 +03:00
Vladimir Enchev
d9a55965d3 DataList EmptyMessage, EmptyTemplate and ShowEmptyMessage added
Fix #1666
2024-08-26 13:03:10 +03:00
Vladimir Enchev
50306343a8 Version updated 2024-08-23 11:48:55 +03:00
Vladimir Enchev
08d7457620 Fixed Numeric floating-point value loss of precision
Fix #1667
2024-08-23 11:32:02 +03:00
Vladimir Enchev
b80a382d2f Fixed DropDown collapse when inside Popup 2024-08-23 09:58:38 +03:00
Vladimir Enchev
ab066bb441 DataGrid column FooterTemplate not rendered when only for composite columns 2024-08-23 09:20:03 +03:00
Vladimir Enchev
4a64260d6e Version updated 2024-08-22 07:51:39 +03:00
Vladimir Enchev
9390077007 DropDownDataGrid HeaderTemplate duplicated
Fix #1664
2024-08-22 07:44:34 +03:00
yordanov
baf5386e34 Hide RadzenButton's hover state on touchscreen devices 2024-08-21 10:27:31 +03:00
Vladimir Enchev
86e72b2f7f PanelMenu will use PanelMenuItem.Match if not default (#1662) 2024-08-21 10:06:35 +03:00
Vladimir Enchev
70bc02b12e DataGrid Bool and Bool? filters not handled correctly
Fix #1663
2024-08-21 10:05:56 +03:00
Vladimir Enchev
6bce8b0195 RadzenTreeItem rendering fixed
Close #1660
2024-08-20 16:31:14 +03:00
yordanov
dd4afb1d0a Fix DropDown chips padding within a flat or filled FormField. Resolves #1654 2024-08-16 08:56:29 +03:00
Vladimir Enchev
95493fe157 version updated 2024-08-16 07:54:09 +03:00
Vladimir Enchev
42b9ef3956 CheckBoxListItem does not update aria-checked selected
Fix #1653
2024-08-16 07:51:19 +03:00
Vladimir Enchev
97ee7b7a7a DatePicker not closed when date is picked without time 2024-08-16 07:26:33 +03:00
Atanas Korchev
230860f773 Preload the icon font to test loading. 2024-08-15 13:04:37 +03:00
Atanas Korchev
5f7f6d975e Update the Sparkline demo. 2024-08-15 12:58:17 +03:00
yordanov
e7c95fdcc1 Components are now more than 90 2024-08-15 11:24:13 +03:00
Vladimir Enchev
933367a685 Version updated 2024-08-15 10:03:51 +03:00
yordanov
4857307533 Update demos homepage 2024-08-15 09:53:06 +03:00
Atanas Korchev
1ee50e42ca Raise the Paste event when an image is pasted. Closes #1652. 2024-08-15 09:50:26 +03:00
Vladimir Enchev
5bbc108331 DropDown OpenOnFocus behavior improved 2024-08-15 09:05:50 +03:00
Vladimir Enchev
11d62516d9 DataGrid column FormatProvider property added
Fix #1649
2024-08-15 08:39:14 +03:00
yordanov
ef714a8bfc Update premium themes 2024-08-14 19:13:14 +03:00
yordanov
c6b8441a79 Update demos homepage 2024-08-14 19:13:14 +03:00
Atanas Korchev
200ad86575 Exception is thrown when using bar series with empty data. 2024-08-14 12:29:12 +03:00
Vasil Yordanov
54fe2f260f New Shared chart tooltips and Sparkline component (#1651)
* Implement shared tooltips.

* Style Chart tooltips.

* Initial Sparkline implementation.

* Support axes in the Sparkline.

* Add Sparkline demo.

---------

Co-authored-by: Atanas Korchev <akorchev@gmail.com>
2024-08-14 12:20:06 +03:00
Vasil Yordanov
e056cf743d Fix progressbar value color. Resolves #1641 (#1650)
* Fix progressbar value color
* Fix failing ProgressBar tests
2024-08-14 12:01:19 +03:00
yordanov
8dbc747928 Update icons on homepage 2024-08-13 17:40:14 +03:00
Vladimir Enchev
969b37e4e9 Version updated 2024-08-12 08:31:35 +03:00
Vladimir Enchev
79a111668e Fixed Splitter exception when all panes Resizable=false 2024-08-12 08:25:45 +03:00
yordanov
9c90e3110b Fix padding-inline-end spacing utility classes 2024-08-09 17:49:03 +03:00
yordanov
59237fb9c6 Improve links' focus state on homepage 2024-08-09 16:29:14 +03:00
yordanov
fc3705f019 Update demos homepage, icons and links 2024-08-09 16:00:27 +03:00
Vladimir Enchev
15adbc0823 Fixed DataFilter ToFilterString() wrong expression for dates
Fix #1642
2024-08-09 10:04:40 +03:00
Vladimir Enchev
ca6472f5f2 DropDownDataGrid HeaderTemplate and FooterTemplate support added
Fix #1643
2024-08-09 08:53:39 +03:00
Vladimir Enchev
de38322f34 DataGrid ColumnReordering event added
Fix #875
2024-08-08 18:54:26 +03:00
Vladimir Enchev
ed45d30639 Version updated 2024-08-08 14:06:19 +03:00
Vladimir Enchev
f99fc6f15f SideDialogOptions AutoFocusFirstElement added 2024-08-08 11:49:53 +03:00
Vladimir Enchev
56aaa082da DataGrid custom filtering demo fixed
Fix #1614
2024-08-08 10:51:02 +03:00
Vladimir Enchev
0aaa2f20bd ProfileMenu keyboard navigation improved
Fix #1636
2024-08-08 10:30:51 +03:00
Vladimir Enchev
bac589e3ec DataGrid column null propagation for filtering will be used only for nullable types
Fix #1633
2024-08-08 10:02:29 +03:00
Vladimir Enchev
1dc7b02a3c SplitButton not cleared from DOM after disposed
Fix #1630
2024-08-08 09:43:08 +03:00
Vladimir Enchev
83965afe68 DataGrid CheckBoxList Filter is not showing all values
Fix #1640
2024-08-07 17:57:50 +03:00
Wolfowy
0e9b110023 Check fireChangeEvent before invoking Change event in RadzenUpload (#1638) 2024-08-07 17:42:27 +03:00
yordanov
44f01a77fd Fix SplitButton spacing issue between icon and text. Resolves #1639 2024-08-07 17:18:27 +03:00
yordanov
58552b7a50 Fix Panel's toggle icon colors in Standard Dark theme 2024-08-07 16:43:07 +03:00
Vladimir Enchev
ddf3feac51 Version updated 2024-08-06 12:33:02 +03:00
Vladimir Enchev
c2d59fcee1 DatePicker should not close the popup when setting the day if ShowTimeOkButton is true 2024-08-06 12:31:41 +03:00
yordanov
ee3b2592aa Disable rz-ripple pointer events 2024-08-05 15:59:52 +03:00
yordanov
ed79dfb220 Fix TextAlign css class not properly applied to rz-numeric 2024-08-05 13:06:21 +03:00
yordanov
6fbb942ad9 Fix TextAlign not applied in RadzenNumeric 2024-08-05 12:47:13 +03:00
yordanov
94a9340ee3 Fix #1631 IconColor property not applied in RadzenSplitButtonItem 2024-08-02 19:02:34 +03:00
yordanov
ef6d2b7d2b Restrict box-sizing to .rz- selectors and their children, and disable box-sizing inheritance. Resolves #1632 2024-08-02 09:58:59 +03:00
Vladimir Enchev
7f0b603f60 version updated 2024-08-01 17:48:29 +03:00
Vladimir Enchev
04ec31ea5c GoogleMap custom marker fixed 2024-08-01 17:48:07 +03:00
Vladimir Enchev
d1ff36c44a . 2024-08-01 17:29:36 +03:00
Vladimir Enchev
d703443469 . 2024-08-01 16:48:53 +03:00
Vladimir Enchev
5455a02c4d version updated 2024-08-01 16:11:44 +03:00
yordanov
4bbb186f63 Fix disabled state of AutoComplete and DatePicker in FormField 2024-08-01 15:22:10 +03:00
Vladimir Enchev
b62e8bf976 DropDownBase should not raise ContextMenu event if disabled 2024-08-01 11:04:38 +03:00
Vladimir Enchev
7ccc4fe33a GoogleMap will use default marker label if not set custom 2024-08-01 08:08:33 +03:00
Vladimir Enchev
b52d1cf1e2 ApiKey and MapId info added 2024-08-01 07:58:44 +03:00
Vladimir Enchev
6ee9db0eb8 DataGrid CheckBoxList filtering support improved for collection sub properties 2024-07-29 16:42:16 +03:00
Vladimir Enchev
36ac2a5bfb Missing Include("Customer") added 2024-07-29 13:33:59 +03:00
Vladimir Enchev
e1841c652a Version updated 2024-07-29 10:04:01 +03:00
Vladimir Enchev
6fc2c88482 sorting disabled 2024-07-29 10:03:14 +03:00
Vladimir Enchev
b8a13a5ba4 Filtering sub properties demo added 2024-07-29 09:45:49 +03:00
Vladimir Enchev
d36623c1fb Added DataGrid Contains/DoesNotContain support for collection sub properties 2024-07-29 09:01:21 +03:00
Adam Hewitt
06ab88556c Set 'disabled' attribute on RadzenPager links when on the first/last page (#1627)
* Set 'disabled' attribute on RadzenPager buttons

* add tests

* fix pager test

---------

Co-authored-by: Adam Hewitt <ahewitt@glasswall.com>
2024-07-29 07:42:35 +03:00
Vladimir Enchev
b552790035 Added marker label content for GoogleMap demo
Fix #1624
2024-07-29 07:42:06 +03:00
Atanas Korchev
6610dd22df Show how to use CookieThemeService in .NET 6 & 7. 2024-07-26 19:22:45 +03:00
Atanas Korchev
04dddf92d8 Remove @code{} from the heading. 2024-07-26 17:49:10 +03:00
Atanas Korchev
bedd9c97c2 Add missing step for theme persistence. 2024-07-26 17:33:26 +03:00
Atanas Korchev
81c859ba31 Fix typo in cookie service registration. 2024-07-26 16:51:13 +03:00
Vladimir Enchev
041e662e07 Version updated 2024-07-26 16:14:18 +03:00
yordanov
65c7c66b50 Update premium themes 2024-07-26 16:07:47 +03:00
yordanov
287f2762f9 Update ThemeService demo 2024-07-26 11:39:53 +03:00
yordanov
3750e94ff5 Update icon demos 2024-07-26 10:34:26 +03:00
Atanas Korchev
e127d60345 Append the assembly version to CSS file url to prevent caching of old themes. 2024-07-26 10:21:17 +03:00
Atanas Korchev
5189298884 Add ThemeService demo. 2024-07-26 09:48:20 +03:00
Vladimir Enchev
5a9c370b29 Class changed to class 2024-07-26 08:50:50 +03:00
yordanov
ec4f370f29 Update changelog 2024-07-25 11:18:55 +03:00
yordanov
da194dd88d Update premium themes 2024-07-25 11:06:55 +03:00
yordanov
91e6b86eb3 Update sample dashboard layout 2024-07-25 10:55:58 +03:00
Cosmatevs
550e2d59b4 DataGrid: fix textbox and checkbox indents in simple filters (#1620)
* DataGrid: fix textbox and checkbox indents in simple filters

* use :has(.rz-filter-button) only to change elements other than .rz-filter-button
2024-07-25 10:15:51 +03:00
Vladimir Enchev
80b7ef08c8 TextArea demo updated with auto-resize example
Fix #1501
2024-07-24 10:15:41 +03:00
Vladimir Enchev
7c9ddf0c3f TextArea demo updated with char count example
Fix #1604
2024-07-24 09:53:22 +03:00
Vladimir Enchev
9abdb1d47b Readme updated 2024-07-24 09:16:29 +03:00
Vladimir Enchev
a5171dd9b4 Version updated 2024-07-24 08:31:50 +03:00
Vladimir Enchev
4e7c04efbe Warnings fixed 2024-07-24 08:31:31 +03:00
Vladimir Enchev
d3c66dc750 NET5_0_OR_GREATER preprocessor removed 2024-07-24 08:28:35 +03:00
Vladimir Enchev
7e14357902 Dialog Drag event fixed
Close #1612
2024-07-24 08:11:35 +03:00
Cosmatevs
92f6c2448f RadzenMask: fix string duplication on typing when a character allowed by the pattern (e.g. 2 is in [0-9]) is in the mask (e.g. "+22 *** *** ***") (#1618) 2024-07-24 07:38:35 +03:00
Vladimir Enchev
b05ebe8bdf GoogleMap MapId property added 2024-07-23 18:34:34 +03:00
Vladimir Enchev
0b62390a54 DataGrid CheckBoxList Filter Values repeat values when scroll down
Fix #1616
2024-07-23 18:17:39 +03:00
mayoismyfavoritespice
8c9b5f2a18 added disableSmartPosition option support to enforce style 'top' properties from being corrected automatically (#1613) 2024-07-23 07:21:17 +03:00
Vladimir Enchev
960894c329 Version updated 2024-07-22 18:23:25 +03:00
Vladimir Enchev
a5786ecccf DataGrid cannot insert new row when bound to empty collection
Fix #1610
2024-07-22 18:23:00 +03:00
Atanas Korchev
33a83928e7 Include getting-started as a URL. 2024-07-22 11:25:51 +03:00
Vladimir Enchev
149c6271b3 Made Dialog client-draggable to avoid unwanted content rendering during dragging.
Close #1590
2024-07-22 10:10:59 +03:00
Eric McGaha
031d23df0c HtmlEditor exception when editing previously uploaded image (#1605) (#1606) 2024-07-22 09:04:23 +03:00
Maks
30134bf3b5 Fixed the issue where the DatePicker popup not appeared in RadzenDatePicker when the ShowButton parameter was set to false. (#1607) 2024-07-22 08:46:54 +03:00
yordanov
7907150f17 Fix #1579 Right sidebar does not show on smaller screens and ignores Responsive attribute 2024-07-19 16:23:51 +03:00
yordanov
e5f9639d1e Remove unused css variables in RadzenPager. Resolves #1351 2024-07-19 12:01:01 +03:00
yordanov
47bad16b54 Update to latest Material Symbols Outlined font 2024-07-18 18:58:21 +03:00
yordanov
efa944ef8c Update drop style in DataGridRowDragScheduler demo 2024-07-18 18:00:43 +03:00
Atanas Korchev
68d4cfff16 Update the changelog. 2024-07-18 18:00:34 +03:00
Atanas Korchev
c9c6c4c5ea Remove hash parameters before parsing the query string. 2024-07-18 17:52:56 +03:00
Atanas Korchev
792b3a1267 Tell that AppearanceToggle requires RadzenTheme. 2024-07-18 17:50:01 +03:00
Atanas Korchev
5b64cf6925 Avoid NavigationException on first redirect. 2024-07-18 17:29:51 +03:00
Atanas Korchev
097878b4b2 Query string parsing does not work on Windows. 2024-07-18 17:11:49 +03:00
yordanov
32eca9aabf Update spacing on Get Started page 2024-07-18 16:45:57 +03:00
Vladimir Enchev
7a27432885 Version updated 2024-07-18 16:25:41 +03:00
Vladimir Enchev
92e4249eec 5.0 (#1603)
* Initial commit

Update spacing css classes in demos

Update spacing css classes in demos

Improve Notification layout and styles

Fix Notification with custom position in RTL mode

Update spacing css classes in demos

Add box-sizing: border-box to components

Remove bootstrap css from App.razor

Fix text-align in tabs demos

Rename CSS classes and update markup in RadzenDatePicker

Remove bootstrap scss

Tabs anchor element should inherit the text color

Update margins in typography

Remove bootstrap instructions from get started page

Remove bootstrap css from demos

Update premium themes

Add variable fonts

Remove legacy color tokens and token maps

Remove --rz-form-group-margin-bottom as it is not used

Add base color tokens

Add Base style values

Remove -styles scss map and use  and  styles maps instead

Remove -styles scss map and use  and  styles maps instead

Remove -styles scss map and use  and  styles maps instead

Remove -styles scss map and use  and  styles maps instead

Format button's styles

Reset body margin to 0

Splitter icons should use --rz-icon-font-family

Add styles for image and svg

Replace MaterialIcons font with MaterialSymbols variable font

Update RadzenButton's demos

Update RadzenBadge's demos

Update Colors demo page

Update RadzenProgressBars' demos

Use Base instead of Secondary button style in Confirm Dialog

Update DataGrid Hierarchy demos

Unify DataGrid filter buttons

Replace light with base button styles

Replace secondary with base button style

RadzenTheme, RadzenThemeSwitch and ThemeService.

Reorder base color variables

Add rtl mode animation to ProgressBar

Update SpeechToTextButton default button style and animation

Add CookieThemeService.

Fix base color variable value in software theme

Add Software Dark and Humanistic Dark themes

Update RadzenThemeSwitch

Default base text button should inherit color from its parent element

Update ColoPicker background for transparent color preview

Add Material Dark theme

Material Dark theme is now free

Remove ambiguous method name.

Update demos configurator

Update premium themes

Update the services to follow the options pattern for DI registration.

Improve RadzenThemeSwitch to use the current theme.

Rename ThemeSwitch to AppearanceToggle and add a demo page

Update SplitButton's dropdown icon demo

Create getting-started.html

Update getting-started.html

Update getting-started.html

Add --rz-grid-group-header-item-color and fix column footer color

Fix demos code snippet colors

Add changelog and update demo status badges

Update Changelog

Persist the current theme.

Update getting-started.html

Rename getting-started.html to md.

Render the theme CSS class at RadzenLayout level.

Hide the right sidebar by default.

Isolate CSS variables in a single rule

Revamp the getting started help article.

Remove nested README.md. Link getting started instructions.

Add Scheduler highlight background color css variable

Sidebar border right should be inline-end

Inputs should inherit font-family

Buttons in code viewer and event console should use base button style

Add Standard Dark theme

Update Changelog

Sidebar border right in themes should be inline-end

Render RadzenTheme only when needed.

Add cursor pointer to SidebarToggle

Fix AppearanceToggle margin

Update default theme colors

Update standard theme colors

Fix filter color in humanistic dark theme

Update software dark theme colors

Add humanistic dark wcag theme

Add software dark wcag theme

Add standard dark wcag theme

Buttons for add and remove now use base button style

Update Get Started styling

Update Dark WCAG theme colors

Update SideBar transition styles

Remove theme name css class

Add premium themes

code fixed

more code fixes

* Update Icon demo page content

* Fix --rz-grid-filter-buttons-background-color in Standard theme

* Remove obsolete fonts

* tests fixed

---------

Co-authored-by: yordanov <vasil@yordanov.info>
2024-07-18 16:24:20 +03:00
Vladimir Enchev
6fac0aa0f5 DataGrid filter popup should be always lazy if FilterMode == FilterMode.CheckBoxList 2024-07-18 14:28:05 +03:00
Vladimir Enchev
cd2ede5ce8 DataGrid CheckBoxList filter type filtering fixed 2024-07-18 13:32:08 +03:00
Vladimir Enchev
ac407375ab Dialog Resize and Drag events added 2024-07-18 10:32:18 +03:00
Vladimir Enchev
1730effc55 Version updated 2024-07-17 11:31:14 +03:00
Vladimir Enchev
21c063ac53 Fixed DropDown/ListBox select as you type selects second item first 2024-07-17 11:19:47 +03:00
Vladimir Enchev
2ba2ef9617 Version updated 2024-07-17 10:59:59 +03:00
Vladimir Enchev
cfe2424cff DataGrid LoadColumnFilterData event added to load column filter data for DataGrid FilterMode.CheckBoxList filter mode (#1600)
* DataGrid LoadColumnFilterData event added

* CheckBoxList with OData support added
2024-07-17 10:58:55 +03:00
Vladimir Enchev
397fe2a0c8 DataGrid EnumFilterTranslationFunc not applied to second filter
Fix #1599
2024-07-17 08:45:52 +03:00
TGasimov
e846cc532c RadzenCompareValidator fails when validated RadzenDatePicker value cleared (#1598)
Co-authored-by: Gasimov, Teymur <GasimovT@bv.com>
2024-07-16 16:47:05 +03:00
Vladimir Enchev
d273f99940 DataGrid column GetSortIndex() method added
Fix #1595
2024-07-16 14:07:29 +03:00
Vladimir Enchev
f0763c2b67 Version updated 2024-07-15 17:47:34 +03:00
Justyna Sienkiewicz
48d6293ab6 Fixed inserting to CurrentItems (#1596) 2024-07-15 17:46:37 +03:00
yordanov
0e270595df Add responsive styles to DataGrid simple filter mode with menu. Resolves #1586 2024-07-15 16:43:38 +03:00
yordanov
db6770159e Fix ability to set width and height of a SplitButton 2024-07-15 14:33:47 +03:00
Vladimir Enchev
e6bf1ed6f7 DataGrid CheckBoxList filter demo FilterPopupRenderMode set to PopupRenderMode.OnDemand 2024-07-12 10:53:06 +03:00
Vladimir Enchev
996dd11e24 Version updated 2024-07-11 11:12:04 +03:00
Krystian Szatan
4ea1f20022 RadzenSteps - Added AllowStepSelect parameter (#1591) 2024-07-11 11:10:16 +03:00
Vladimir Enchev
a56cfc5688 DropDown, DropDownDataGrid and ListBox code updated (#1593) 2024-07-11 10:58:33 +03:00
Paul Ruston
d71d05374f MonthSelect event on all Year Views (#1587)
* MonthSelect event on all Year Views in rendering

* Resolve initial PR oversights

* Resolve initial PR oversights 2
2024-07-10 17:59:29 +03:00
Barry
99b82b24d0 Add RadzenDataAnnotationValidator to Support Data Annotation Validation (#1588)
* implement data annotation validator

* Rebase to latest `master` branch. Update the demo. Use PropertyAccess.Getter instead of reflection.

---------

Co-authored-by: Atanas Korchev <akorchev@gmail.com>
2024-07-10 17:58:40 +03:00
Vladimir Enchev
5cfe624e25 Dialog focus trap will cycle between first and last focusable elements
Fix #1585
2024-07-10 11:29:40 +03:00
Vladimir Enchev
62066e04e3 PickList Disabled property added 2024-07-10 10:59:44 +03:00
Vincent Schmandt
5478c014f1 Add PackageLicenseExpression (#1578)
* Add PackageLicenseExpression

* Remove PackageLicenseFile as only PackageLicenseFile or PackageLicenseExpression can be provided

See also https://github.com/microsoft/vstest/issues/4816
2024-07-10 10:07:55 +03:00
Vladimir Enchev
aea3147454 DataGrid column col element width not reset properly
Fix #1582
2024-07-09 13:41:17 +03:00
Daniel Jonsson
e466de4d0e Fix a spelling error in StepsCanChange.razor (#1581) 2024-07-05 13:39:44 +01:00
Vladimir Enchev
0de3cf72b8 demo updated 2024-07-03 11:15:51 +03:00
Vladimir Enchev
41fa97994e Version updated 2024-07-03 10:21:05 +03:00
Vladimir Enchev
d03a563e0d Merge branch 'master' of https://github.com/radzenhq/radzen-blazor 2024-07-03 10:10:45 +03:00
Vladimir Enchev
0f89c0b112 Tree Checkable delegate fixed
Fix #1580
2024-07-03 10:10:42 +03:00
Atanas Korchev
07ae096af2 Ignore chart series radius if it exceeds the series size. Fixes #1576. 2024-07-01 18:52:59 +03:00
Zagidin Selimov
11e4d482f4 fix (#1575)
Co-authored-by: Zagidin Selimov <zselimov@adeptik.com>
2024-07-01 18:49:47 +03:00
Vladimir Enchev
f959e3b869 version updated 2024-07-01 17:33:16 +03:00
Vladimir Enchev
ec19bfb556 Dialog AutoFocusFirstElement will always focus first HtmlEditor 2024-07-01 17:31:10 +03:00
Vladimir Enchev
fa4edf9845 Drag from DataGrid to Scheduler demo added
Drag & Drop demos combined in separate category
2024-06-28 14:53:06 +03:00
Vladimir Enchev
ab52430846 DataGrid should clear filter values for other columns on ClearFilter 2024-06-28 10:50:50 +03:00
Vladimir Enchev
6045869260 Version updated 2024-06-28 10:18:04 +03:00
Vladimir Enchev
c08a598658 DataGrid CheckBoxList filter values collection will respect filters applied to other columns 2024-06-28 10:16:50 +03:00
Vladimir Enchev
340483bf7f DataGrid CheckBoxList filter should clear the column filter if no values are checked 2024-06-28 10:01:56 +03:00
Vladimir Enchev
87395e0ec4 Fixed DataGrid wrong column/rowspan in some cases with composite columns 2024-06-28 09:52:15 +03:00
Atanas Korchev
694194dd01 Allow developers to specify drag-and-drop events via SlotRender. 2024-06-28 09:23:38 +03:00
Vladimir Enchev
aa6935820d Version updated 2024-06-27 12:01:25 +03:00
Vladimir Enchev
57c038dca6 Added Checkable/CheckableProperty similar to Text/TextProperty in the RadzenTreeItem/RadzenTreeLevel 2024-06-27 12:00:57 +03:00
Vladimir Enchev
fe69b1419f Version updated 2024-06-27 11:36:21 +03:00
Vladimir Enchev
0cf7365755 Menu cannot open sub item with ClickToOpen=true 2024-06-27 11:35:54 +03:00
efinder2
7e96f776ff Changes aria-live regions for better screen reader (#1571)
* changed aria for screen reader

* fix div

* reverted RadzenAlert
2024-06-27 11:34:18 +03:00
Vladimir Enchev
e5ce3d46de Version updated 2024-06-26 10:15:11 +03:00
Vladimir Enchev
f097cb3c3a @nameof() used to set various property names across all demos 2024-06-26 10:13:54 +03:00
Vladimir Enchev
23aea7b794 Fixed DataGrid column picker filtering exception with columns without Title 2024-06-25 10:35:57 +03:00
Vladimir Enchev
c2839fdc29 Menu items not reachable in some case with ClickToOpen=false 2024-06-25 09:28:20 +03:00
Vladimir Enchev
3fbd64338c Fixed DataGrid UpdateRow() method for items with overridden Equals/GetHashCode() 2024-06-24 18:10:07 +03:00
Vladimir Enchev
36a2917b17 Version updated 2024-06-24 16:20:35 +03:00
Vladimir Enchev
9f3fc43791 DataGrid should not request all IQueryable data when virtualization is enabled in some cases 2024-06-24 16:20:10 +03:00
Vladimir Enchev
4774d2d1fd Version updated 2024-06-24 09:32:31 +03:00
Vladimir Enchev
91de8a5a8f Numeric tests fixed 2024-06-24 09:10:36 +03:00
Vladimir Enchev
652005264b Fixed DataGrid composite column wrong colspan when child column has no children
Fix #1568
2024-06-24 08:36:42 +03:00
Simon Pawlowski
98967410cb prevent numeric overflow on step up/down (#1570) 2024-06-24 08:16:03 +03:00
Vladimir Enchev
55b2e3f1ff Fixed Numeric overflow exception with byte as TValue
Fix #1567
2024-06-21 07:45:29 +03:00
Vladimir Enchev
cf8ddc98af DropDown and ListBox should not select items with SelectAll() if ReadOnly
Fix #1566
2024-06-21 07:33:58 +03:00
Atanas Korchev
f259fa85a5 Hide the right sidebar by default. 2024-06-19 16:24:12 +03:00
Atanas Korchev
5ea6c7739f Enable properly automatic layout of the Monaco editor. 2024-06-18 17:43:08 +03:00
Atanas Korchev
eac071af9d RadzenArcGauge does not use the Max scale value in some cases. 2024-06-18 17:37:03 +03:00
Vladimir Enchev
2b0f424af6 Version updated 2024-06-17 07:36:57 +03:00
Vladimir Enchev
fcbfd28025 DataGrid composite columns column span fixed in some cases 2024-06-17 07:36:36 +03:00
yordanov
37982fe416 Fix focus states in Scheduler day view 2024-06-14 12:10:49 +03:00
Vladimir Enchev
6824c85820 Fixed DataGrid advanced filter with empty/not empty operators 2024-06-14 09:02:32 +03:00
Vladimir Enchev
bd9f76222a DataGrid Conditional Columns render demo added 2024-06-13 11:32:44 +03:00
Paul Ruston
349e771f74 Fix DaySlotEvents rendering (#1531)
* Fix DaySlotEvents rendering

* Revert "Fix DaySlotEvents rendering"

This reverts commit 4982cd14bf.

* Day Rendering Process

* Updated comments regarding algorithm

* Changes as per https://github.com/radzenhq/radzen-blazor/pull/1531#issuecomment-2160029819
2024-06-13 11:23:43 +03:00
Vladimir Enchev
83c2993cef Version updated 2024-06-12 08:21:24 +03:00
Vladimir Enchev
dbb91fa140 AutoComplete OpenOnFocus added
Fix #1560
2024-06-12 08:09:11 +03:00
Vladimir Enchev
849ca6803d DataGrid should not set rowspan/colspan less than 1 2024-06-12 07:57:36 +03:00
bert-algoet
f41288f441 When using multiselect dropdown with chips, stop dropdown van repositioning when there is enough room below the dropdown. (#1559) 2024-06-12 07:55:20 +03:00
Robert McLaws
9e974d5c30 Adds CalculatedCssClass Func to RadzenDataGridColumn to allow users to style the cell based on properties of the underlying object. (#1561) 2024-06-12 07:55:02 +03:00
Vladimir Enchev
62d25ee917 DataGrid composite columns column span fixed 2024-06-11 19:13:53 +03:00
Milo Davis
652a5dd27d Add support for proper OData filtering of numeric data grid columns with null or not null options (#1558)
Co-authored-by: Milo Davis <DavisM@steelers.nfl.com>
2024-06-11 08:45:34 +03:00
Rudolf de Schipper
1051bfa661 Rename linq filtering data filter (#1557)
* Update QueryableExtension.cs to add ToLinqFilterString

Add ToLinqFilterString extension method plus 2 supporting methods, allowing RadzenDataFilter to create Linq-compatible filter strings

* Update QueryableExtension.cs - rename ToLinqFilterString

rename ToLinqFilterString into ToFilterString, to align with common practice.
2024-06-11 08:44:35 +03:00
Robert McLaws
3a3c481e3b [DataGrid] Enhanced DataGridColumn EditMode control (#1550)
* Adding RadzenGridColumn.IsInEditMode property, updating render check in RadzenDataGrid, and updating associated demo.

* Removed unnecessary usings.
2024-06-10 17:56:08 +03:00
Vladimir Enchev
9642c58d94 DataGrid CheckBoxList filter will check if Property/FilterProperty is defined 2024-06-10 17:55:06 +03:00
Sean Martz
72d37ab218 Add ability for RadzenDataGrid.Query to filter DateOnly datatypes (#1548)
Co-authored-by: seanmartz <smartz@smart-union.org>
2024-06-10 17:40:57 +03:00
Atanas Korchev
a2047f9498 RadzenHtmlEditor does not update the EditorContext state when its value changes. 2024-06-10 12:06:45 +03:00
Atanas Korchev
22a94130f2 Version updated 2024-06-07 15:04:43 +03:00
Vladimir Enchev
6a907ff0c2 Revert "Add filter capabilities for DateOnly in RadzenDataGrid (#1545)" (#1546)
This reverts commit 25572a9d57.
2024-06-07 14:43:45 +03:00
Vladimir Enchev
11e0ca29d4 Version updated 2024-06-07 09:05:08 +03:00
Vladimir Enchev
47cd4ef790 DataGrid CheckBoxList filtering improved 2024-06-07 09:04:43 +03:00
Sean Martz
25572a9d57 Add filter capabilities for DateOnly in RadzenDataGrid (#1545)
* Add DateOnly functionality

* Undo mass formatting

---------

Co-authored-by: seanmartz <smartz@smart-union.org>
2024-06-07 08:53:55 +03:00
Vladimir Enchev
02b94e5672 CheckBoxList should not select disabled items on select all 2024-06-07 07:41:04 +03:00
Vladimir Enchev
23790fefd6 DataGridCheckBoxListFilter demo updated 2024-06-06 16:48:13 +03:00
Vladimir Enchev
3c8e4a24c6 more obsolete code deleted 2024-06-06 16:39:22 +03:00
Vladimir Enchev
0f754055ab obsolete method removed 2024-06-06 16:37:52 +03:00
Vladimir Enchev
ac7c2612b2 DataGrid InCell Editing demo added
Fix #1543
2024-06-06 16:28:29 +03:00
Vladimir Enchev
7b54ff2046 DataGrid cannot expand/collapse group items when virtualized 2024-06-06 11:04:57 +03:00
Vladimir Enchev
c7743ffdbe DropDown grouping with multiple select demo imroved 2024-06-06 10:17:56 +03:00
Vladimir Enchev
7a604d39bd Version updated 2024-06-04 09:30:01 +03:00
nitrouscookies
7c67f8bba1 Add FilterOperator null check so SetParametersAsync doesn't override initial values (#1542)
Co-authored-by: dstillwell <dstillwell@moorheadschools.org>
2024-06-04 08:53:34 +03:00
Vladimir Enchev
6f2ca41eb6 Fix DropDownDataGrid Chips render to use ValueTemplate/Template if defined exactly like DropDown
Close #1539
2024-06-03 10:21:34 +03:00
Vladimir Enchev
b26ac16201 RadzenDropDownDataGrid Reset() does not work when Multiple= true
Fix #1540
2024-06-03 09:37:54 +03:00
Vladimir Enchev
71b7ba70d1 SecurityCode will use autocomplete="one-time-code" 2024-06-03 08:55:44 +03:00
bert-algoet
96bd62e0cc Check if SelectItem is null before calling disabledPropertyGetter (#1538) 2024-06-03 08:51:58 +03:00
Atanas Korchev
c2ce598d0f Update the SSRSViewer documentation. 2024-05-31 11:37:55 +03:00
Vladimir Enchev
cf56069804 Version updated 2024-05-31 10:54:25 +03:00
Vladimir Enchev
7834b535d6 DatePicker cannot set hour to 0 when bound to TimeOnly 2024-05-31 10:11:10 +03:00
Vladimir Enchev
4b440199a2 Demo style updated
Fix #1535
2024-05-30 14:51:43 +03:00
Vladimir Enchev
4facfa5c7a Added custom style for drag over DataGrid row 2024-05-30 09:59:16 +03:00
Vladimir Enchev
99ab247c06 Version updated 2024-05-29 15:50:39 +03:00
Kasun Jalitha
5cf12fced5 Modified the RadzenSplitButton to allow customizing its dropdown icon. (#1530)
* Modified the RadzenSplitButton to allow customizing its dropdown icon.

* Changed the text in demo page.
2024-05-29 15:49:41 +03:00
Vladimir Enchev
5abc92308a DatePicker DateTime Kind not set properly in some cases 2024-05-29 10:15:33 +03:00
Vladimir Enchev
ed50ca5b53 RadzenMask caret position kept during input
Fix #1529
2024-05-28 10:10:34 +03:00
Atanas Korchev
3e8eb1a6eb Implement TodaySelect event in RadzenScheduler 2024-05-26 10:54:03 +03:00
Vladimir Enchev
d360c584c1 Version updated 2024-05-24 12:34:58 +03:00
Vladimir Enchev
55482bf28a DataGrid CheckBoxList filtering fixed to work properly with enums and FilterTemplate 2024-05-24 12:33:59 +03:00
Vladimir Enchev
bcf5b4c1b2 unused demos deleted 2024-05-24 10:49:55 +03:00
Vladimir Enchev
7098e28532 code updated 2024-05-23 15:15:03 +03:00
Vladimir Enchev
ab8aa2e1ae demo fixed 2024-05-23 15:13:50 +03:00
Vladimir Enchev
97fc9a8b37 more demo updates 2024-05-23 15:12:42 +03:00
Vladimir Enchev
5f91a4561b DropDown demos group items start margin defined 2024-05-23 15:09:41 +03:00
Vladimir Enchev
b5dd56f187 ValueTemplate improved 2024-05-23 14:47:20 +03:00
Vladimir Enchev
819f0b7d7e Version updated 2024-05-23 13:11:20 +03:00
Vladimir Enchev
16399d4512 HeaderTemplate added to DropDownDataGrid, HeaderTemplate and ItemRender added to DropDown and ListBox
DropDown grouping demos improved, ListBox item Disabled fixed
2024-05-23 13:10:41 +03:00
Vladimir Enchev
86df184463 Cannot read properties of null (reading 'querySelector') at Object.createDatePicker
Fix #1523
2024-05-22 08:37:16 +03:00
Vladimir Enchev
a4a2d66b85 Version updated 2024-05-21 11:05:53 +03:00
Vladimir Enchev
1fb972cf2a DatePicker will not select with PopupRenderMode="PopupRenderMode.OnDemand"
Fix #1473
2024-05-21 09:35:28 +03:00
Krystian Szatan
f1ab0e3449 Added missing ContentCssClass set in DialogService (#1521)
* Dialog - custom container class

New parameter ContentCssClass

* Update DialogContainer.razor

* Revert rz-dialog-titlebar

* Update DialogService.cs

Added ContentCssClass
2024-05-21 09:33:02 +03:00
Vladimir Enchev
5032646090 Version updated 2024-05-20 15:17:58 +03:00
Atanas Korchev
2facce9633 Include text measurement data for cyrillic characters. Closes #1505. 2024-05-20 13:47:42 +03:00
Krystian Szatan
bce50b991e Dialog - custom container class (#1518)
* Dialog - custom container class

New parameter ContentCssClass

* Update DialogContainer.razor

* Revert rz-dialog-titlebar
2024-05-20 12:57:23 +03:00
Atanas Korchev
bd1a5ef046 Provide parsing config when using Dynamic Linq methods in order to improve initial performance. 2024-05-20 10:55:21 +03:00
stlufred
47e9b4b587 Fix exception if TreeLevel has no Text nor TextProperty (#1515)
This can happen when using template.
2024-05-19 17:21:16 +03:00
Vladimir Enchev
676e6127cd Version updated 2024-05-16 16:45:50 +03:00
Vladimir Enchev
7b192a4e14 demo updated 2024-05-16 16:15:22 +03:00
Vladimir Enchev
e3239ed0b3 Tree ItemContextMenu event added 2024-05-15 11:03:03 +03:00
Marco Papst
1423454191 Add a Footer template to RadzenDropZone (#1503)
* Add a Footer template to RadzenDropZone

* Add header over the Footer Template Demo

* rename FooterTemplate to Footer
2024-05-15 09:53:08 +03:00
Vladimir Enchev
285b6367ed RadzenDataGrid EmptyTemplate alignment/colspan is incorrect when Data Grid uses Grouping
Fix #1504
2024-05-15 09:52:02 +03:00
Vladimir Enchev
2907403f82 Version updated 2024-05-13 18:06:06 +03:00
Vladimir Enchev
9a735639d4 DataGrid crosstab example added 2024-05-13 12:00:10 +03:00
Vladimir Enchev
375e3c4636 SecurityCode backspace support added 2024-05-13 09:41:31 +03:00
Vladimir Enchev
ab3d7078bf demo updated 2024-05-10 16:09:06 +03:00
Vladimir Enchev
3b191b917b Tree drag & drop demo added 2024-05-10 15:46:57 +03:00
Vladimir Enchev
4be129917a DataGrid group should not be collapsed if set Expanded = true in GroupRowRender 2024-05-09 16:18:22 +03:00
Vladimir Enchev
507f508497 demo fixed 2024-05-09 15:20:41 +03:00
Vladimir Enchev
4b063b3493 Drag row between two DataGrids demo added 2024-05-09 10:43:46 +03:00
Vladimir Enchev
8e457a20bc DataGrid rows reorder demo updated 2024-05-08 18:28:31 +03:00
Vladimir Enchev
b9716cf2a3 DataGrid rows reorder demo added 2024-05-08 17:10:24 +03:00
Vladimir Enchev
b05a4ab83a version updated 2024-05-08 09:32:41 +03:00
Vladimir Enchev
ca75eb651d Fixed DropDownDataGrid steal focus in some cases 2024-05-08 09:32:17 +03:00
Vladimir Enchev
a6329fd3c0 Numeric and Android SecurityCode input improved 2024-05-07 12:11:30 +03:00
Atanas Korchev
89e33e984b Use the Culture set for RadzenScheduler when displaying the month name in year view. 2024-05-07 10:19:55 +03:00
Atanas Korchev
40c0b85a10 Use the Culture set for RadzenScheduler when displaying the title of the "More" dialog in month view. 2024-05-07 10:13:25 +03:00
Vladimir Enchev
7cdb199855 MaxFileCount default value changed to 10 2024-05-07 10:05:39 +03:00
Vladimir Enchev
8c165dc17f Upload MaxFileCount property added 2024-05-07 09:55:55 +03:00
Vladimir Enchev
43fe21a87d version updated 2024-05-06 17:50:19 +03:00
Vladimir Enchev
4b7559f2ec Various button title and aria-label attributes added 2024-05-06 16:52:29 +03:00
Vladimir Enchev
9e026034d7 DropDownBase EmptyAriaLabel property added 2024-05-06 16:24:10 +03:00
Vladimir Enchev
9466ccb12a Fixed "Anchor element found with no link content and no name and/or ID attribute." accessibility error 2024-05-06 11:09:57 +03:00
Vladimir Enchev
f53f314f0b Title set to DataGrid advanced filter apply and clear buttons 2024-05-06 09:59:30 +03:00
Vladimir Enchev
9b53a3a052 various input attributes added 2024-05-06 09:32:14 +03:00
Vladimir Enchev
7f8a95646a Fixed error in RadzenDataGrid FilterMode="FilterMode.CheckBoxList"
Fix #1498
2024-05-06 09:21:58 +03:00
Paul Ruston
88d452a9fa Don't set filterOperator if Custom (#1497) 2024-05-03 17:45:55 +03:00
Vladimir Enchev
ee2e1412d6 Version updated 2024-05-02 08:42:54 +03:00
melfon
f2ea6af1c3 Uses pointer event instead of mouse event to track splitter movements (#1496)
Co-authored-by: Tomas Wiell <tomas.wiell@scientaomicron.com>
2024-05-02 08:41:47 +03:00
Vladimir Enchev
76bd5d7518 DropDownBase indexer property binding imporved 2024-05-02 08:33:46 +03:00
Vladimir Enchev
70626ccb79 DropDownDataGrid should check column Type 2024-05-02 08:07:10 +03:00
Vladimir Enchev
124fca6d1f DropDownDataGrid selected row not cleared on value clear 2024-05-01 09:31:56 +03:00
Vladimir Enchev
37154cc1ef Version updated 2024-04-30 19:53:19 +03:00
Vladimir Enchev
9f42ad5746 DropDownDataGrid binding to dynamic data demo added 2024-04-30 19:53:00 +03:00
Vladimir Enchev
94c19c8724 DataGrid SetFilterValue for enums fixed
Fix #1493
2024-04-30 19:06:26 +03:00
Atanas Korchev
c3dd9a5f14 Uploading an image when UploadUrl is not set would insert the image as base64 encoded data. 2024-04-30 15:06:38 +03:00
yordanov
8f2a6acb7b Update DropZone demos 2024-04-30 09:37:19 +03:00
Vladimir Enchev
821817fc89 DropZone component added (#1492)
* DropZone component added

* DropZone and DropZoneItem rendering improved

* rz-can-drop/rz-no-drop css classes added

* Update DropZone styles and demo

* dragCssClass added

* Update DropZone styles and demo

* Update premium themes

* Update navigation items

* DropZoneCanDropNoDropStyles  demo added

* item styles added

* Allow drop over zone no matter if has items

* Update styles in DropDzone demos

* Use dragover instead dragenter to apply css class

---------

Co-authored-by: yordanov <vasil@yordanov.info>
2024-04-30 09:08:46 +03:00
Vladimir Enchev
0f3097590f Version updated 2024-04-29 16:18:04 +03:00
Vladimir Enchev
20826fff54 Drag and drop files to upload demo added 2024-04-29 14:39:31 +03:00
Vladimir Enchev
9067e1d8d1 Fixed Cast() exception with DataGrid FilterMode CheckBoxList 2024-04-29 13:32:52 +03:00
Vladimir Enchev
dbf983e287 DataGrid should not render <col> element for group expand column when ShowGroupExpandColumn=false
Fix #1490
2024-04-29 10:16:38 +03:00
Vladimir Enchev
f81043133a RadzenCompareValidator ValidateOnValueChange property added
Fix #1487
2024-04-29 10:04:02 +03:00
Vladimir Enchev
cab9b0bd1e Fixed DataGrid initial enum filter not selected in the filtering DropDown
Fix #1489
2024-04-29 09:28:08 +03:00
Vladimir Enchev
d9b42016dc Improved DataGrid grouping performance
Fix #1486
2024-04-26 09:30:59 +03:00
Vladimir Enchev
5ee5a23b29 Version updated 2024-04-25 10:06:07 +03:00
Vladimir Enchev
a1f33d93c1 Chart CartesianSeries DateOnly exception fixed
Fix #1484
2024-04-25 10:05:04 +03:00
Vladimir Enchev
1f9a19c37a Fixed virtualized DropDown null ref. exception on ENTER key press
Fix #1483
2024-04-24 19:23:51 +03:00
Vladimir Enchev
9e9d371a3f Version updated 2024-04-24 11:25:09 +03:00
Vladimir Enchev
b97136a606 Issue drag scrolling Dialog content on mobile after moving dialog
Fix #1480
2024-04-24 11:23:43 +03:00
Vladimir Enchev
f1df463a07 RadzenDropDown SelectedItemChanged keeps repeating forever
Fix #1479
2024-04-24 10:58:45 +03:00
Vladimir Enchev
e59c335cac DataGrid dynamic data demo updated with enum 2024-04-24 10:50:32 +03:00
Vladimir Enchev
a9280edf97 Version updated 2024-04-23 19:03:43 +03:00
Vladimir Enchev
68eca70a31 version updated 2024-04-23 19:01:39 +03:00
Vladimir Enchev
dd68c35295 DataGrid ungrouping in virtualized mode throws error 2024-04-23 19:01:16 +03:00
Vladimir Enchev
57530af792 DateOnly supported added to DataGrid component
Fix #1478
2024-04-23 10:32:32 +03:00
Vladimir Enchev
4947fa91ef Column FormatString used for Excel filter 2024-04-22 18:51:51 +03:00
Vladimir Enchev
c605cc77d5 GetFilterValues() improved 2024-04-22 18:51:29 +03:00
Vladimir Enchev
6fe6b681ac DataGrid FilterMode.CheckBoxList (Excel like) filtering added 2024-04-22 18:51:03 +03:00
Vladimir Enchev
08884af624 Version updated 2024-04-22 17:15:39 +03:00
Vladimir Enchev
2adfbb5169 Revert "Implement OpenPopup and ClosePopup Events for Radzen Dropdown (#1471)" (#1477)
This reverts commit f6e05a27d8.
2024-04-22 14:10:47 +03:00
Atanas Korchev
831c3c4fa4 Column and bar radius is not applied in some cases. Fixes #1464. 2024-04-22 12:52:19 +03:00
Vladimir Enchev
df87b51e63 ClosePopup() renamed to PopupClose 2024-04-22 11:03:41 +03:00
Atanas Korchev
a17fceac39 Stacked bar and column series do not work with negative values. Closes #1475. 2024-04-22 10:58:24 +03:00
luis507pty
f6e05a27d8 Implement OpenPopup and ClosePopup Events for Radzen Dropdown (#1471)
* Add Parameters OnOpenPopup and OnClosePopup to handler events

* Add CheckAndTriggerPopupStateChange method

* Add RadzenDropDown Action Methods OnOpenPopup And  OnClosePopup.

* Radzen.closePopup send DotNetObjectReference

* -Use EventCallBack
- Change property name OnOpenPopu to OpenPopupCallback
- Change property name OnClosePopup to ClosePopupCallback

* validate and invoke delegate

* -Change name the internal method OpenPopup to TogglePopup

---------

Co-authored-by: Desarrollador 04 04 <develop04@smrey.com>
2024-04-22 10:27:19 +03:00
Atanas Korchev
19cb4ee7a4 Stacked area series do not support negative values. 2024-04-22 10:23:52 +03:00
Vladimir Enchev
8d4f853432 Pager aria-current attribute added for current page 2024-04-19 11:17:15 +03:00
nielsNocore
76eaac5d31 add ability to add custom button content to a split button (#1470) 2024-04-19 09:46:43 +03:00
Atanas Korchev
5c02943ab3 Clicking a link in RadzenHtmlEditor sometimes navigates. Closes #1467. 2024-04-19 09:43:13 +03:00
Atanas Korchev
09a38e2dcd Exception is thrown when updating the data of pie or donut series and a tooltip is shown. 2024-04-19 09:29:20 +03:00
Vladimir Enchev
4a772953dd DropDownBase SelectedItemChanged changed from Action to EventCallback 2024-04-18 15:07:47 +03:00
Vladimir Enchev
be544e09cf Version updated 2024-04-18 11:52:24 +03:00
Vladimir Enchev
8a817980db Draggable requires single touch to start drag after first drag end
Fix #1469
2024-04-18 11:49:56 +03:00
Vladimir Enchev
1dbfbe5d5f Tab cannot be selected in some cases when TabRenderMode.Client after add/remove
Fix #1466
2024-04-18 10:47:55 +03:00
Vladimir Enchev
53ed0dc12d RadzenDropDownDataGrid is not displaying empty text
Fix #1465
2024-04-18 09:57:52 +03:00
nielsNocore
1bbff65f5d add method to collapse all items, which allow collapse all rows in a datagrid, whithout iterating all items in the page. (#1468)
small refactoring duplicate code to correctly collapse an aitem.
2024-04-18 09:49:44 +03:00
Vladimir Enchev
8e5e9ab517 CascadingTypeParameter added for DataFilter, DataList, DataGrid and TemplateForm components 2024-04-17 15:41:15 +03:00
yordanov
c2d37932e1 Replace padding in Scheduler's view header with auto scroll width 2024-04-16 17:33:00 +03:00
Vladimir Enchev
7e7bbd4591 Pager navigate to page with ENTER/SPACE index fixed 2024-04-15 14:28:05 +03:00
Vladimir Enchev
6d2d3c4c3b Version updated 2024-04-15 13:19:40 +03:00
yordanov
a1ba7f9e5f Pager focus styles should be visible only when needed 2024-04-12 12:57:49 +03:00
yordanov
b89949e361 Fix disabled state of DatePicker in FormField 2024-04-11 14:52:40 +03:00
yordanov
72ad111198 Remove redundant border property 2024-04-11 14:28:35 +03:00
yordanov
00b4e9a9b4 Fix disabled state of buttons in Upload and FileInput 2024-04-11 12:55:06 +03:00
Vladimir Enchev
a460fe12a1 DropDownBase should not loose focus on select all 2024-04-10 16:15:58 +03:00
yordanov
7e4fdcd9a3 Update Scheduler responsive styles. Adds container media queries and resolves #1445 2024-04-10 16:11:12 +03:00
Vladimir Enchev
0f4d06b4d4 Version updated 2024-04-10 10:26:39 +03:00
Marat Chiraev
bfbf6edc87 Fixed a bug where it was possible to apply a filter on an empty string (#1458) 2024-04-10 10:21:15 +03:00
Vladimir Enchev
5027e2f03a TextBox, TextArea and Password components will unable to show ContextMenu if Name is set 2024-04-10 10:20:25 +03:00
yordanov
70ea05c0ca Hide clear icon in disabled DropDown and DatePicker and unify disabled color of trigger icon 2024-04-09 18:06:15 +03:00
yordanov
5e9d9abe10 Fix DropDown and DatePicker icon and text colors in disabled and hover states 2024-04-09 17:36:19 +03:00
Vladimir Enchev
128495dd91 Version updated 2024-04-09 16:43:08 +03:00
Stefan
314a8c0741 Make MaxLength a parameter (#1456) 2024-04-09 08:59:08 +03:00
Vladimir Enchev
8c6ca6f8f7 PickList invalid cast exception fixed 2024-04-09 08:58:33 +03:00
yordanov
421bb3b701 Fix layout of SelectBar bind value demo 2024-04-08 15:47:59 +03:00
Vladimir Enchev
a8e9ef49f4 Fixed RadzenDropDown EnterKey ArgumentOutOfRangeException 2024-04-08 15:35:53 +03:00
Vladimir Enchev
2474ad8d1d Version updated 2024-04-08 15:15:51 +03:00
Vladimir Enchev
dd58ac01ed DataGrid simple filter mode by string properties fixed 2024-04-08 15:15:40 +03:00
Vladimir Enchev
fd4fc377a1 Version updated 2024-04-08 11:26:20 +03:00
Vladimir Enchev
3b31cec3e7 DataGrid will clear column filters on escape and will not allow set or filter value if empty operator type is selected
Close #1454
2024-04-08 10:08:49 +03:00
Vladimir Enchev
28fc95dd1f DataGrid Reset() should not call SaveSettings() 2024-04-08 09:28:16 +03:00
1013 changed files with 27153 additions and 38504 deletions

View File

@@ -1,39 +1,13 @@
![Radzen Blazor Components](RadzenBlazorDemos/wwwroot/images/radzen-blazor-components.png)
![Radzen Blazor Components](https://raw.githubusercontent.com/radzenhq/radzen-blazor/master/RadzenBlazorDemos/wwwroot/images/radzen-blazor-components.png)
<h1 align="center">
Radzen Blazor Components
</h1>
Radzen Blazor Components
========================
<p align="center">
A set of <strong>70+ free and open source</strong> native Blazor UI controls.
</p>
A set of **90+ free and open source** native Blazor UI controls.
<div align="center">
See Online Demos or Read the Docs
[See Online Demos](https://blazor.radzen.com) or [Read the Docs](https://blazor.radzen.com/docs/)
</div>
---
<p align="center">
<a href="https://github.com/radzenhq/radzen-blazor/blob/master/LICENSE">
<img alt="License - MIT" src="https://img.shields.io/github/license/radzenhq/radzen-blazor?logo=github&style=for-the-badge" />
</a>
<a href="https://www.nuget.org/packages/Radzen.Blazor">
<img alt="NuGet Downloads" src="https://img.shields.io/nuget/dt/Radzen.Blazor?color=%232694F9&label=nuget%20downloads&logo=nuget&style=for-the-badge" />
</a>
<img alt="Last Commit" src="https://img.shields.io/github/last-commit/radzenhq/radzen-blazor?logo=github&style=for-the-badge" />
<a href="https://github.com/radzenhq/radzen-blazor/graphs/contributors">
<img alt="Github Contributors" src="https://img.shields.io/github/contributors/radzenhq/radzen-blazor?logo=github&style=for-the-badge" />
</a>
<a href="https://blazor.radzen.com">
<img alt="Radzen Blazor Components - Online Demos" src="https://img.shields.io/badge/demos-online-brightgreen?color=%232694F9&logo=blazor&style=for-the-badge" />
</a>
<a href="https://blazor.radzen.com/docs">
<img alt="Radzen Blazor Components - Documentation" src="https://img.shields.io/badge/docs-online-brightgreen?color=%232694F9&logo=blazor&style=for-the-badge" />
</a>
</p>
[![License - MIT](https://img.shields.io/github/license/radzenhq/radzen-blazor?logo=github&style=for-the-badge)](https://github.com/radzenhq/radzen-blazor/blob/master/LICENSE)[![NuGet Downloads](https://img.shields.io/nuget/dt/Radzen.Blazor?color=%232694F9&label=nuget%20downloads&logo=nuget&style=for-the-badge) ](https://www.nuget.org/packages/Radzen.Blazor)![Last Commit](https://img.shields.io/github/last-commit/radzenhq/radzen-blazor?logo=github&style=for-the-badge) [![Github Contributors](https://img.shields.io/github/contributors/radzenhq/radzen-blazor?logo=github&style=for-the-badge) ](https://github.com/radzenhq/radzen-blazor/graphs/contributors)[![Radzen Blazor Components - Online Demos](https://img.shields.io/badge/demos-online-brightgreen?color=%232694F9&logo=blazor&style=for-the-badge) ](https://blazor.radzen.com)[![Radzen Blazor Components - Documentation](https://img.shields.io/badge/docs-online-brightgreen?color=%232694F9&logo=blazor&style=for-the-badge)](https://blazor.radzen.com/docs)
## Why choose Radzen Blazor Components?
@@ -76,67 +50,8 @@ Our flagship product [Radzen Blazor Studio](https://www.radzen.com/blazor-studio
## Get started with Radzen Blazor Components
### 1. Install
Check the [getting started](https://blazor.radzen.com/getting-started) instructions to start making awesome Blazor applications.
Radzen Blazor Components are distributed as a [Radzen.Blazor NuGet package](https://www.nuget.org/packages/Radzen.Blazor). You can add them to your project in one of the following ways
- Install the package from command line by running `dotnet add package Radzen.Blazor`
- Add the project from the Visual NuGet Package Manager
- Manually edit the .csproj file and add a project reference
### 2. Import the namespace
Open the `_Imports.razor` file of your Blazor application and add this line `@using Radzen.Blazor`.
### 3. Include a theme
Radzen Blazor components come with five free themes: Material, Standard, Default, Dark, Software and Humanistic.
To use a theme
1. Pick a theme. The [online demos](https://blazor.radzen.com/colors) allow you to preview the available options via the theme dropdown located in the header. The Material theme is currently selected by default.
1. Include the theme CSS file in your Blazor application. Open `Pages\_Layout.cshtml` (Blazor Server .NET 6), `Pages\_Host.cshtml` (Blazor Server .NET 7) or `wwwroot/index.html` (Blazor WebAssembly) and include a theme CSS file by adding this snippet
```html
<link rel="stylesheet" href="_content/Radzen.Blazor/css/material-base.css">
```
To include a different theme (i.e. Standard) just change the name of the CSS file:
```
<link rel="stylesheet" href="_content/Radzen.Blazor/css/standard-base.css">
```
### 4. Include Radzen.Blazor.js
Open `Pages\_Layout.cshtml` (Blazor Server .NET 6), `Pages\_Host.cshtml` (Blazor Server .NET 7) or `wwwroot/index.html` (Blazor WebAssembly) and include this snippet:
```html
<script src="_content/Radzen.Blazor/Radzen.Blazor.js"></script>
```
### 5. Use a component
Use any Radzen Blazor component by typing its tag name in a Blazor page e.g.
```html
<RadzenButton Text="Hi"></RadzenButton>
```
#### Data-binding a property
```razor
<RadzenButton Text=@text />
<RadzenTextBox @bind-Value=@text />
@code {
string text = "Hi";
}
```
#### Handing events
```razor
<RadzenButton Click="@ButtonClicked" Text="Hi"></RadzenButton>
@code {
void ButtonClicked()
{
}
}
```
## Run demos locally
Use Radzen.Server.sln to open and run demos as Blazor server application or Radzen.WebAssembly.sln to open and run demos as Blazor WebAssembly application. Radzen.sln has reference to all projects including tests.

View File

@@ -30,7 +30,7 @@ namespace Radzen.Blazor.Tests
component.SetParametersAndRender(parameters => parameters.Add(p => p.Icon, icon));
Assert.Contains(@$"<i class=""rz-button-icon-left rzi"">{icon}</i>", component.Markup);
Assert.Contains(@$"<i class=""notranslate rz-button-icon-left rzi"">{icon}</i>", component.Markup);
}
[Fact]
@@ -48,7 +48,7 @@ namespace Radzen.Blazor.Tests
);
// does not render the actual icon when busy
Assert.DoesNotContain(@$"<i class=""rz-button-icon-left rzi"">{icon}</i>", component.Markup);
Assert.DoesNotContain(@$"<i class=""notranslate rz-button-icon-left rzi"">{icon}</i>", component.Markup);
// renders the icon with busy spin animation
Assert.Contains(@"<i style=""animation: rotation", component.Markup);
@@ -71,7 +71,7 @@ namespace Radzen.Blazor.Tests
parameters.Add(p => p.Icon, icon);
});
Assert.Contains(@$"<i class=""rz-button-icon-left rzi"">{icon}</i>", component.Markup);
Assert.Contains(@$"<i class=""notranslate rz-button-icon-left rzi"">{icon}</i>", component.Markup);
Assert.Contains(@$"<span class=""rz-button-text"">{text}</span>", component.Markup);
}
@@ -86,7 +86,7 @@ namespace Radzen.Blazor.Tests
component.SetParametersAndRender(parameters => parameters.Add(p => p.Image, image));
Assert.Contains(@$"<img class=""rz-button-icon-left rzi"" src=""{image}"" alt=""button"" />", component.Markup);
Assert.Contains(@$"<img class=""notranslate rz-button-icon-left rzi"" src=""{image}"" alt=""button"" />", component.Markup);
}
[Fact]
@@ -106,7 +106,7 @@ namespace Radzen.Blazor.Tests
parameters.Add(p => p.ImageAlternateText, text);
});
Assert.Contains(@$"<img class=""rz-button-icon-left rzi"" src=""{image}"" alt=""{text}"" />", component.Markup);
Assert.Contains(@$"<img class=""notranslate rz-button-icon-left rzi"" src=""{image}"" alt=""{text}"" />", component.Markup);
Assert.Contains(@$"<span class=""rz-button-text"">{text}</span>", component.Markup);
}

View File

@@ -20,39 +20,29 @@ namespace Radzen.Blazor.Tests
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var component = ctx.RenderComponent<RadzenGrid<dynamic>>(parameterBuilder =>
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
{
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, new[] { new { Id = 1 }, new { Id = 2 }, new { Id = 3 } });
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
{
builder.OpenComponent(0, typeof(RadzenGridColumn<dynamic>));
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
builder.AddAttribute(1, "Property", "Id");
builder.CloseComponent();
});
});
// Main
Assert.Contains(@$"rz-datatable-scrollable-wrapper", component.Markup);
Assert.Contains(@$"rz-datatable-scrollable-view", component.Markup);
Assert.Contains(@$"rz-data-grid", component.Markup);
Assert.Contains(@$"rz-datatable", component.Markup);
Assert.Contains(@$"rz-datatable-scrollable", component.Markup);
// Header
Assert.Contains(@$"rz-datatable-scrollable-header", component.Markup);
Assert.Contains(@$"rz-datatable-scrollable-header-box", component.Markup);
Assert.Contains(@$"rz-datatable-thead", component.Markup);
Assert.Contains(@$"rz-datatable-scrollable-colgroup", component.Markup);
// Data
Assert.Contains(@$"rz-data-grid-data", component.Markup);
//Body
Assert.Contains(@$"rz-datatable-scrollable-body", component.Markup);
Assert.Contains(@$"rz-datatable-scrollable-table-wrapper", component.Markup);
Assert.Contains(@$"rz-datatable-data", component.Markup);
Assert.Contains(@$"rz-datatable-hoverable-rows", component.Markup);
// Footer
Assert.DoesNotContain(@$"rz-datatable-scrollable-footer", component.Markup);
Assert.DoesNotContain(@$"rz-datatable-scrollable-footer-box", component.Markup);
//Columns
Assert.DoesNotContain(@$"rz-sortable-column", component.Markup);
// Table
Assert.Contains(@$"rz-grid-table", component.Markup);
Assert.Contains(@$"rz-grid-table-fixed", component.Markup);
Assert.Contains(@$"rz-grid-table-striped", component.Markup);
}
// Columns tests
@@ -63,12 +53,12 @@ namespace Radzen.Blazor.Tests
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var component = ctx.RenderComponent<RadzenGrid<dynamic>>(parameterBuilder =>
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
{
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, new[] { new { Id = 1 }, new { Id = 2 }, new { Id = 3 } });
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
{
builder.OpenComponent(0, typeof(RadzenGridColumn<dynamic>));
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
builder.AddAttribute(1, "Property", "Id");
builder.CloseComponent();
});
@@ -88,12 +78,12 @@ namespace Radzen.Blazor.Tests
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var component = ctx.RenderComponent<RadzenGrid<dynamic>>(parameterBuilder =>
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
{
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, new[] { new { Id = 1 }, new { Id = 2 }, new { Id = 3 } });
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
{
builder.OpenComponent(0, typeof(RadzenGridColumn<dynamic>));
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
builder.AddAttribute(1, "Title", "MyId");
builder.CloseComponent();
});
@@ -158,12 +148,12 @@ namespace Radzen.Blazor.Tests
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var component = ctx.RenderComponent<RadzenGrid<dynamic>>(parameterBuilder =>
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
{
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, new[] { new { Id = 1 }, new { Id = 2 }, new { Id = 3 } });
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
{
builder.OpenComponent(0, typeof(RadzenGridColumn<dynamic>));
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
builder.AddAttribute(1, "Property", "Id");
builder.AddAttribute(2, "Title", "Id");
builder.CloseComponent();
@@ -188,12 +178,12 @@ namespace Radzen.Blazor.Tests
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var component = ctx.RenderComponent<RadzenGrid<dynamic>>(parameterBuilder =>
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
{
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, new[] { new { Id = 1 }, new { Id = 2 }, new { Id = 3 } });
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
{
builder.OpenComponent(0, typeof(RadzenGridColumn<dynamic>));
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
builder.AddAttribute(1, "Property", "Id");
builder.AddAttribute(2, "Title", "Id");
builder.AddAttribute(3, "Sortable", false);
@@ -212,12 +202,12 @@ namespace Radzen.Blazor.Tests
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var component = ctx.RenderComponent<RadzenGrid<dynamic>>(parameterBuilder =>
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
{
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, new[] { new { Id = 1 }, new { Id = 2 }, new { Id = 3 } });
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
{
builder.OpenComponent(0, typeof(RadzenGridColumn<dynamic>));
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
builder.AddAttribute(1, "Property", "Id");
builder.AddAttribute(2, "Title", "Id");
builder.CloseComponent();
@@ -225,14 +215,14 @@ namespace Radzen.Blazor.Tests
parameterBuilder.Add<bool>(p => p.AllowFiltering, true);
});
Assert.Contains(@$"rz-cell-filter", component.Markup);
Assert.Contains(@$"rz-grid-filter-icon", component.Markup);
component.SetParametersAndRender(parameters =>
{
parameters.Add<bool>(p => p.AllowFiltering, false);
});
Assert.DoesNotContain(@$"rz-cell-filter", component.Markup);
Assert.DoesNotContain(@$"rz-grid-filter-icon", component.Markup);
}
[Fact]
@@ -242,12 +232,12 @@ namespace Radzen.Blazor.Tests
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var component = ctx.RenderComponent<RadzenGrid<dynamic>>(parameterBuilder =>
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
{
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, new[] { new { Id = 1 }, new { Id = 2 }, new { Id = 3 } });
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
{
builder.OpenComponent(0, typeof(RadzenGridColumn<dynamic>));
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
builder.AddAttribute(1, "Property", "Id");
builder.AddAttribute(2, "Title", "Id");
builder.AddAttribute(3, "Filterable", false);
@@ -266,12 +256,12 @@ namespace Radzen.Blazor.Tests
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var component = ctx.RenderComponent<RadzenGrid<dynamic>>(parameterBuilder =>
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
{
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, new[] { new { Id = 1 }, new { Id = 2 }, new { Id = 3 } });
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
{
builder.OpenComponent(0, typeof(RadzenGridColumn<dynamic>));
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
builder.AddAttribute(1, "Property", "Id");
builder.AddAttribute(2, "Title", "Id");
builder.CloseComponent();
@@ -297,12 +287,12 @@ namespace Radzen.Blazor.Tests
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var component = ctx.RenderComponent<RadzenGrid<int>>(parameterBuilder =>
var component = ctx.RenderComponent<RadzenDataGrid<int>>(parameterBuilder =>
{
parameterBuilder.Add<IEnumerable<int>>(p => p.Data, new[] { 1, 2, 3 });
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
{
builder.OpenComponent(0, typeof(RadzenGridColumn<int>));
builder.OpenComponent(0, typeof(RadzenDataGridColumn<int>));
builder.AddAttribute(1, "HeaderTemplate", (RenderFragment)delegate (RenderTreeBuilder b)
{
@@ -323,12 +313,12 @@ namespace Radzen.Blazor.Tests
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var component = ctx.RenderComponent<RadzenGrid<int>>(parameterBuilder =>
var component = ctx.RenderComponent<RadzenDataGrid<int>>(parameterBuilder =>
{
parameterBuilder.Add<IEnumerable<int>>(p => p.Data, new[] { 1, 2, 3 });
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
{
builder.OpenComponent(0, typeof(RadzenGridColumn<int>));
builder.OpenComponent(0, typeof(RadzenDataGridColumn<int>));
builder.AddAttribute(1, "FooterTemplate", (RenderFragment)delegate (RenderTreeBuilder b)
{
@@ -339,8 +329,8 @@ namespace Radzen.Blazor.Tests
});
});
Assert.Contains(@$"rz-datatable-scrollable-footer", component.Markup);
Assert.Contains(@$"rz-datatable-scrollable-footer-box", component.Markup);
Assert.Contains(@$"rz-datatable-tfoot", component.Markup);
Assert.Contains(@$"rz-column-footer", component.Markup);
Assert.Contains(@$"Footer", component.Markup);
}
@@ -352,12 +342,12 @@ namespace Radzen.Blazor.Tests
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var component = ctx.RenderComponent<RadzenGrid<dynamic>>(parameterBuilder =>
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(RadzenGridColumn<dynamic>));
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
builder.AddAttribute(1, "Property", "Id");
builder.AddAttribute(2, "Title", "Id");
builder.CloseComponent();
@@ -382,11 +372,20 @@ namespace Radzen.Blazor.Tests
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var component = ctx.RenderComponent<RadzenGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Enumerable.Range(0, 100)));
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.AllowPaging, true);
});
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AllowPaging, true));
Assert.Contains(@$"rz-paginator-bottom", component.Markup);
Assert.Contains(@$"rz-pager", component.Markup);
}
[Fact]
@@ -396,16 +395,21 @@ namespace Radzen.Blazor.Tests
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var component = ctx.RenderComponent<RadzenGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Enumerable.Range(0, 100)));
component.SetParametersAndRender(parameters =>
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
{
parameters.Add<bool>(p => p.AllowPaging, true);
parameters.Add<PagerPosition>(p => p.PagerPosition, PagerPosition.Top);
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.AllowPaging, true);
parameterBuilder.Add<PagerPosition>(p => p.PagerPosition, PagerPosition.Top);
});
Assert.Contains(@$"rz-paginator", component.Markup);
Assert.DoesNotContain(@$"rz-paginator-bottom", component.Markup);
Assert.Contains(@$"rz-pager", component.Markup);
}
[Fact]
@@ -415,16 +419,21 @@ namespace Radzen.Blazor.Tests
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var component = ctx.RenderComponent<RadzenGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Enumerable.Range(0, 100)));
component.SetParametersAndRender(parameters =>
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
{
parameters.Add<bool>(p => p.AllowPaging, true);
parameters.Add<PagerPosition>(p => p.PagerPosition, PagerPosition.TopAndBottom);
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.AllowPaging, true);
parameterBuilder.Add<PagerPosition>(p => p.PagerPosition, PagerPosition.TopAndBottom);
});
Assert.Contains(@$"rz-paginator", component.Markup);
Assert.Contains(@$"rz-paginator-bottom", component.Markup);
Assert.Contains(@$"rz-pager", component.Markup);
}
[Fact]
@@ -434,7 +443,7 @@ namespace Radzen.Blazor.Tests
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var component = ctx.RenderComponent<RadzenGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Enumerable.Range(0, 100)));
var component = ctx.RenderComponent<RadzenDataGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Enumerable.Range(0, 100)));
component.SetParametersAndRender(parameters =>
{
@@ -453,7 +462,7 @@ namespace Radzen.Blazor.Tests
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var component = ctx.RenderComponent<RadzenGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Enumerable.Range(0, 100)));
var component = ctx.RenderComponent<RadzenDataGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Enumerable.Range(0, 100)));
component.SetParametersAndRender(parameters =>
{
@@ -472,7 +481,7 @@ namespace Radzen.Blazor.Tests
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var component = ctx.RenderComponent<RadzenGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Array.Empty<int>()));
var component = ctx.RenderComponent<RadzenDataGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Array.Empty<int>()));
component.Render();
Assert.Contains("No records to display.", component.Markup);
@@ -486,7 +495,7 @@ namespace Radzen.Blazor.Tests
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var component = ctx.RenderComponent<RadzenGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Array.Empty<int>()));
var component = ctx.RenderComponent<RadzenDataGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Array.Empty<int>()));
component.SetParametersAndRender(parameters =>
{
parameters.Add(p => p.EmptyText, emptyText);
@@ -502,7 +511,7 @@ namespace Radzen.Blazor.Tests
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var component = ctx.RenderComponent<RadzenGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Array.Empty<int>()));
var component = ctx.RenderComponent<RadzenDataGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Array.Empty<int>()));
component.SetParametersAndRender(parameters =>
{
parameters.Add<RenderFragment>(p => p.EmptyTemplate, builder =>
@@ -523,18 +532,28 @@ namespace Radzen.Blazor.Tests
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var component = ctx.RenderComponent<RadzenGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Enumerable.Range(0, 100)));
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.AllowPaging, true);
});
var raised = false;
LoadDataArgs newArgs = null;
component.SetParametersAndRender(parameters =>
{
parameters.Add<bool>(p => p.AllowPaging, true);
parameters.Add<LoadDataArgs>(p => p.LoadData, args => { raised = true; newArgs = args; });
});
component.Find(".rz-paginator-next").Click();
component.Find(".rz-pager-next").Click();
Assert.True(raised);
Assert.True(newArgs.Skip == 10);
@@ -548,18 +567,28 @@ namespace Radzen.Blazor.Tests
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var component = ctx.RenderComponent<RadzenGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Enumerable.Range(0, 100)));
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.AllowPaging, true);
});
var raised = false;
LoadDataArgs newArgs = null;
component.SetParametersAndRender(parameters =>
{
parameters.Add<bool>(p => p.AllowPaging, true);
parameters.Add<LoadDataArgs>(p => p.LoadData, args => { raised = true; newArgs = args; });
});
component.Find(".rz-paginator-last").Click();
component.Find(".rz-pager-last").Click();
Assert.True(raised);
Assert.True(newArgs.Skip == 90);
@@ -573,19 +602,29 @@ namespace Radzen.Blazor.Tests
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var component = ctx.RenderComponent<RadzenGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Enumerable.Range(0, 100)));
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.AllowPaging, true);
});
var raised = false;
LoadDataArgs newArgs = null;
component.SetParametersAndRender(parameters =>
{
parameters.Add<bool>(p => p.AllowPaging, true);
parameters.Add<LoadDataArgs>(p => p.LoadData, args => { raised = true; newArgs = args; });
});
component.Find(".rz-paginator-next").Click();
component.Find(".rz-paginator-prev").Click();
component.Find(".rz-pager-next").Click();
component.Find(".rz-pager-prev").Click();
Assert.True(raised);
Assert.True(newArgs.Skip == 0);
@@ -599,19 +638,29 @@ namespace Radzen.Blazor.Tests
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var component = ctx.RenderComponent<RadzenGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Enumerable.Range(0, 100)));
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.AllowPaging, true);
});
var raised = false;
LoadDataArgs newArgs = null;
component.SetParametersAndRender(parameters =>
{
parameters.Add<bool>(p => p.AllowPaging, true);
parameters.Add<LoadDataArgs>(p => p.LoadData, args => { raised = true; newArgs = args; });
});
component.Find(".rz-paginator-next").Click();
component.Find(".rz-paginator-first").Click();
component.Find(".rz-pager-next").Click();
component.Find(".rz-pager-first").Click();
Assert.True(raised);
Assert.True(newArgs.Skip == 0);
@@ -625,17 +674,27 @@ namespace Radzen.Blazor.Tests
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var component = ctx.RenderComponent<RadzenGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Enumerable.Range(0, 100)));
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.AllowPaging, true);
});
var raised = false;
component.SetParametersAndRender(parameters =>
{
parameters.Add<bool>(p => p.AllowPaging, true);
parameters.Add<LoadDataArgs>(p => p.LoadData, args => { raised = true; });
});
component.Find(".rz-paginator-first").Click();
component.Find(".rz-pager-first").Click();
Assert.False(raised);
}
@@ -647,17 +706,27 @@ namespace Radzen.Blazor.Tests
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var component = ctx.RenderComponent<RadzenGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Enumerable.Range(0, 100)));
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.AllowPaging, true);
});
var raised = false;
component.SetParametersAndRender(parameters =>
{
parameters.Add<bool>(p => p.AllowPaging, true);
parameters.Add<LoadDataArgs>(p => p.LoadData, args => { raised = true; });
});
component.Find(".rz-paginator-prev").Click();
component.Find(".rz-pager-prev").Click();
Assert.False(raised);
}
@@ -669,23 +738,29 @@ namespace Radzen.Blazor.Tests
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var component = ctx.RenderComponent<RadzenGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Enumerable.Range(0, 100)));
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.AllowPaging, true);
});
var raised = false;
component.SetParametersAndRender(parameters =>
{
parameters.Add<bool>(p => p.AllowPaging, true);
});
component.Find(".rz-paginator-last").Click();
component.Find(".rz-pager-last").Click();
component.SetParametersAndRender(parameters =>
{
parameters.Add<LoadDataArgs>(p => p.LoadData, args => { raised = true; });
});
component.Find(".rz-paginator-last").Click();
component.Find(".rz-pager-last").Click();
Assert.False(raised);
}
@@ -697,23 +772,29 @@ namespace Radzen.Blazor.Tests
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var component = ctx.RenderComponent<RadzenGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Enumerable.Range(0, 100)));
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.AllowPaging, true);
});
var raised = false;
component.SetParametersAndRender(parameters =>
{
parameters.Add<bool>(p => p.AllowPaging, true);
});
component.Find(".rz-paginator-last").Click();
component.Find(".rz-pager-last").Click();
component.SetParametersAndRender(parameters =>
{
parameters.Add<LoadDataArgs>(p => p.LoadData, args => { raised = true; });
});
component.Find(".rz-paginator-next").Click();
component.Find(".rz-pager-next").Click();
Assert.False(raised);
}
@@ -725,19 +806,29 @@ namespace Radzen.Blazor.Tests
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var component = ctx.RenderComponent<RadzenGrid<int>>(parameterBuilder => parameterBuilder.Add<IEnumerable<int>>(p => p.Data, Enumerable.Range(0, 100)));
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.AllowPaging, true);
});
var raised = false;
LoadDataArgs newArgs = null;
component.SetParametersAndRender(parameters =>
{
parameters.Add<bool>(p => p.AllowPaging, true);
parameters.Add<int>(p => p.PageSize, 20);
parameters.Add<LoadDataArgs>(p => p.LoadData, args => { raised = true; newArgs = args; });
});
component.Find(".rz-paginator-next").Click();
component.Find(".rz-pager-next").Click();
Assert.True(raised);
Assert.True(newArgs.Skip == 20);

View File

@@ -29,7 +29,7 @@ namespace Radzen.Blazor.Tests
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AllowPaging, true));
Assert.Contains(@$"rz-paginator-bottom", component.Markup);
Assert.Contains(@$"rz-pager-bottom", component.Markup);
}
[Fact]
@@ -44,8 +44,8 @@ namespace Radzen.Blazor.Tests
parameters.Add<PagerPosition>(p => p.PagerPosition, PagerPosition.Top);
});
Assert.Contains(@$"rz-paginator", component.Markup);
Assert.DoesNotContain(@$"rz-paginator-bottom", component.Markup);
Assert.Contains(@$"rz-pager", component.Markup);
Assert.DoesNotContain(@$"rz-pager-bottom", component.Markup);
}
[Fact]
@@ -60,8 +60,8 @@ namespace Radzen.Blazor.Tests
parameters.Add<PagerPosition>(p => p.PagerPosition, PagerPosition.TopAndBottom);
});
Assert.Contains(@$"rz-paginator", component.Markup);
Assert.Contains(@$"rz-paginator-bottom", component.Markup);
Assert.Contains(@$"rz-pager", component.Markup);
Assert.Contains(@$"rz-pager-bottom", component.Markup);
}
[Fact]
@@ -127,7 +127,7 @@ namespace Radzen.Blazor.Tests
parameters.Add<LoadDataArgs>(p => p.LoadData, args => { raised = true; newArgs = args; });
});
component.Find(".rz-paginator-next").Click();
component.Find(".rz-pager-next").Click();
Assert.True(raised);
Assert.True(newArgs.Skip == 10);
@@ -149,7 +149,7 @@ namespace Radzen.Blazor.Tests
parameters.Add<LoadDataArgs>(p => p.LoadData, args => { raised = true; newArgs = args; });
});
component.Find(".rz-paginator-last").Click();
component.Find(".rz-pager-last").Click();
Assert.True(raised);
Assert.True(newArgs.Skip == 90);
@@ -171,8 +171,8 @@ namespace Radzen.Blazor.Tests
parameters.Add<LoadDataArgs>(p => p.LoadData, args => { raised = true; newArgs = args; });
});
component.Find(".rz-paginator-next").Click();
component.Find(".rz-paginator-prev").Click();
component.Find(".rz-pager-next").Click();
component.Find(".rz-pager-prev").Click();
Assert.True(raised);
Assert.True(newArgs.Skip == 0);
@@ -194,8 +194,8 @@ namespace Radzen.Blazor.Tests
parameters.Add<LoadDataArgs>(p => p.LoadData, args => { raised = true; newArgs = args; });
});
component.Find(".rz-paginator-next").Click();
component.Find(".rz-paginator-first").Click();
component.Find(".rz-pager-next").Click();
component.Find(".rz-pager-first").Click();
Assert.True(raised);
Assert.True(newArgs.Skip == 0);
@@ -216,7 +216,7 @@ namespace Radzen.Blazor.Tests
parameters.Add<LoadDataArgs>(p => p.LoadData, args => { raised = true; });
});
component.Find(".rz-paginator-first").Click();
component.Find(".rz-pager-first").Click();
Assert.False(raised);
}
@@ -235,7 +235,7 @@ namespace Radzen.Blazor.Tests
parameters.Add<LoadDataArgs>(p => p.LoadData, args => { raised = true; });
});
component.Find(".rz-paginator-prev").Click();
component.Find(".rz-pager-prev").Click();
Assert.False(raised);
}
@@ -253,13 +253,13 @@ namespace Radzen.Blazor.Tests
parameters.Add<bool>(p => p.AllowPaging, true);
});
component.Find(".rz-paginator-last").Click();
component.Find(".rz-pager-last").Click();
component.SetParametersAndRender(parameters => {
parameters.Add<LoadDataArgs>(p => p.LoadData, args => { raised = true; });
});
component.Find(".rz-paginator-last").Click();
component.Find(".rz-pager-last").Click();
Assert.False(raised);
}
@@ -277,13 +277,13 @@ namespace Radzen.Blazor.Tests
parameters.Add<bool>(p => p.AllowPaging, true);
});
component.Find(".rz-paginator-last").Click();
component.Find(".rz-pager-last").Click();
component.SetParametersAndRender(parameters => {
parameters.Add<LoadDataArgs>(p => p.LoadData, args => { raised = true; });
});
component.Find(".rz-paginator-next").Click();
component.Find(".rz-pager-next").Click();
Assert.False(raised);
}
@@ -304,7 +304,7 @@ namespace Radzen.Blazor.Tests
parameters.Add<LoadDataArgs>(p => p.LoadData, args => { raised = true; newArgs = args; });
});
component.Find(".rz-paginator-next").Click();
component.Find(".rz-pager-next").Click();
Assert.True(raised);
Assert.True(newArgs.Skip == 20);

View File

@@ -18,10 +18,10 @@ namespace Radzen.Blazor.Tests
var component = ctx.RenderComponent<RadzenDatePicker<DateTime>>();
Assert.Contains(@$"rz-datepicker", component.Markup);
Assert.Contains(@$"rz-calendar", component.Markup);
Assert.Contains(@$"rz-datepicker-group", component.Markup);
Assert.Contains(@$"rz-datepicker-header", component.Markup);
Assert.Contains(@$"rz-datepicker-calendar", component.Markup);
Assert.Contains(@$"rz-calendar-header", component.Markup);
Assert.Contains(@$"rz-calendar-view", component.Markup);
}
[Fact]
@@ -137,7 +137,7 @@ namespace Radzen.Blazor.Tests
parameters.Add<bool>(p => p.TimeOnly, true);
});
Assert.DoesNotContain(@$"rz-datepicker-header", component.Markup);
Assert.DoesNotContain(@$"rz-calendar-header", component.Markup);
}
[Fact]
@@ -155,7 +155,7 @@ namespace Radzen.Blazor.Tests
parameters.Add<bool>(p => p.AllowClear, true);
});
Assert.Contains(@$"<i class=""rz-dropdown-clear-icon rzi rzi-times""", component.Markup);
Assert.Contains(@$"<i class=""notranslate rz-dropdown-clear-icon rzi rzi-times""", component.Markup);
}
[Fact]
@@ -218,7 +218,7 @@ namespace Radzen.Blazor.Tests
component.SetParametersAndRender(parameters => parameters.Add(p => p.Style, value));
Assert.Contains(@$"style=""display: inline-block;{value}""", component.Markup);
Assert.Contains(@$"style=""{value}""", component.Markup);
}
[Fact]
@@ -252,7 +252,7 @@ namespace Radzen.Blazor.Tests
parameters.Add(p => p.Change, args => { raised = true; newValue = args; });
});
component.Find(".rz-datepicker-next-icon").Click();
component.Find(".rz-calendar-next-icon").Click();
Assert.False(raised);
}
@@ -274,7 +274,7 @@ namespace Radzen.Blazor.Tests
parameters.Add(p => p.ValueChanged, args => { raised = true; newValue = args; });
});
component.Find(".rz-datepicker-next-icon").Click();
component.Find(".rz-calendar-next-icon").Click();
Assert.False(raised);
}
@@ -296,7 +296,7 @@ namespace Radzen.Blazor.Tests
parameters.Add(p => p.Change, args => { raised = true; newValue = args; });
});
component.Find(".rz-datepicker-prev-icon").Click();
component.Find(".rz-calendar-prev-icon").Click();
Assert.False(raised);
}
@@ -318,7 +318,7 @@ namespace Radzen.Blazor.Tests
parameters.Add(p => p.ValueChanged, args => { raised = true; newValue = args; });
});
component.Find(".rz-datepicker-prev-icon").Click();
component.Find(".rz-calendar-prev-icon").Click();
Assert.False(raised);
}
@@ -471,7 +471,7 @@ namespace Radzen.Blazor.Tests
Assert.Contains(DateTime.MaxValue.ToString(component.Instance.DateFormat), component.Markup);
var exception = Record.Exception(() => component.Find(".rz-datepicker-next-icon")
var exception = Record.Exception(() => component.Find(".rz-calendar-next-icon")
.Click());
Assert.Null(exception);
}
@@ -500,7 +500,7 @@ namespace Radzen.Blazor.Tests
parameters.Add(p => p.Change, args => { raised = true; newValue = args; });
});
component.Find(".rz-datepicker-next-icon").Click();
component.Find(".rz-calendar-next-icon").Click();
component.FindAll(".rz-button-text").First(x => x.TextContent == "Ok").Click();
Assert.True(raised);
@@ -665,11 +665,11 @@ namespace Radzen.Blazor.Tests
parameter.Add(p => p.ShowCalendarWeek, true);
});
Assert.Contains(@$"rz-datepicker-week-number", component.Markup);
Assert.Equal(8, component.FindAll(".rz-datepicker-calendar th").Count());
Assert.Contains(@$"rz-calendar-week-number", component.Markup);
Assert.Equal(8, component.FindAll(".rz-calendar-view th").Count());
// check header and week number column
Assert.Single(component.FindAll("th.rz-datepicker-week-number"));
Assert.Equal(6, component.FindAll("td.rz-datepicker-week-number").Count());
Assert.Equal(6, component.FindAll("td.rz-calendar-week-number").Count());
}
[Fact]
@@ -684,8 +684,8 @@ namespace Radzen.Blazor.Tests
parameter.Add(p => p.ShowCalendarWeek, false);
});
Assert.DoesNotContain(@$"rz-datepicker-week-number", component.Markup);
Assert.Equal(7, component.FindAll(".rz-datepicker-calendar th").Count());
Assert.DoesNotContain(@$"rz-calendar-week-number", component.Markup);
Assert.Equal(7, component.FindAll(".rz-calendar-view th").Count());
}
[Fact]
@@ -701,7 +701,7 @@ namespace Radzen.Blazor.Tests
parameter.Add(p => p.CalendarWeekTitle, "Wk");
});
var weekNumberHeader = component.Find(".rz-datepicker-calendar th.rz-datepicker-week-number");
var weekNumberHeader = component.Find(".rz-calendar-view th.rz-datepicker-week-number");
Assert.Contains("Wk", weekNumberHeader.InnerHtml);
}
}

View File

@@ -46,7 +46,7 @@ namespace Radzen.Blazor.Tests
component.SetParametersAndRender(parameters => parameters.Add(p => p.Icon, value));
Assert.Contains(@$"<i class=""rzi"">{value}</i>", component.Markup);
Assert.Contains(@$"<i class=""notranslate rzi"">{value}</i>", component.Markup);
}
[Fact]
@@ -104,11 +104,11 @@ namespace Radzen.Blazor.Tests
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AllowCollapse, true));
Assert.Contains(@"<span class=""rz-fieldset-toggler rzi rzi-w rzi-minus""></span>", component.Markup);
Assert.Contains(@"<span class=""notranslate rz-fieldset-toggler rzi rzi-w rzi-minus""></span>", component.Markup);
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.Collapsed, true));
Assert.Contains(@"<span class=""rz-fieldset-toggler rzi rzi-w rzi-plus""></span>", component.Markup);
Assert.Contains(@"<span class=""notranslate rz-fieldset-toggler rzi rzi-w rzi-plus""></span>", component.Markup);
}
[Fact]

View File

@@ -17,7 +17,7 @@ namespace Radzen.Blazor.Tests
component.SetParametersAndRender(parameters => parameters.Add(p => p.Icon, icon));
Assert.Contains(@$">{icon}</i>", component.Markup);
Assert.Contains(@$"class=""rzi""", component.Markup);
Assert.Contains(@$"class=""notranslate rzi""", component.Markup);
}
[Fact]

View File

@@ -47,7 +47,7 @@ namespace Radzen.Blazor.Tests
component.SetParametersAndRender(parameters => parameters.Add(p => p.Icon, icon));
Assert.Contains(@$"<i class=""rzi"">{icon}</i>", component.Markup);
Assert.Contains(@$"<i class=""notranslate rzi"">{icon}</i>", component.Markup);
}
[Fact]

View File

@@ -119,12 +119,12 @@ namespace Radzen.Blazor.Tests
var component = ctx.RenderComponent<RadzenMask>();
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, false));
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", false));
Assert.Contains(@$"autocomplete=""off""", component.Markup);
Assert.Contains(@$"aria-autocomplete=""none""", component.Markup);
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, true));
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
Assert.Contains(@$"autocomplete=""on""", component.Markup);
Assert.DoesNotContain(@$"aria-autocomplete", component.Markup);
@@ -135,7 +135,7 @@ namespace Radzen.Blazor.Tests
Assert.DoesNotContain(@$"aria-autocomplete", component.Markup);
component.Instance.DefaultAutoCompleteAttribute = "autocomplete-custom";
component.SetParametersAndRender(parameters => parameters.Add(p => p.AutoComplete, false));
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", false));
Assert.Contains(@$"autocomplete=""autocomplete-custom""", component.Markup);
Assert.Contains(@$"aria-autocomplete=""none""", component.Markup);
@@ -148,22 +148,22 @@ namespace Radzen.Blazor.Tests
var component = ctx.RenderComponent<RadzenMask>();
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, false));
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", false));
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.On));
Assert.Contains(@$"autocomplete=""off""", component.Markup);
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, true));
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.Off));
Assert.Contains(@$"autocomplete=""off""", component.Markup);
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, true));
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.AdditionalName));
Assert.Contains(@$"autocomplete=""{AutoCompleteType.AdditionalName.GetAutoCompleteValue()}""", component.Markup);
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, true));
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.Email));
Assert.Contains(@$"autocomplete=""{AutoCompleteType.Email.GetAutoCompleteValue()}""", component.Markup);

View File

@@ -60,6 +60,19 @@ namespace Radzen.Blazor.Tests
Assert.False(component.Instance.Validate(null));
}
[Fact]
public void Returns_True_If_Value_Is_Null_And_AllowNull_Is_True()
{
using var ctx = new TestContext();
var component = ctx.RenderComponent<RadzenNumericRangeValidatorTestDouble>();
component.SetParametersAndRender(parameters =>
{
component.SetParametersAndRender(parameters => parameters.Add(p => p.Min, 0).Add(p => p.Max, 10).Add(p => p.AllowNull, true));
});
Assert.True(component.Instance.Validate(null));
}
[Fact]
public void Returns_False_If_Value_Overflows()

View File

@@ -16,9 +16,9 @@ namespace Radzen.Blazor.Tests
component.Render();
Assert.Contains(@$"rz-spinner", component.Markup);
Assert.Contains(@$"rz-spinner-up", component.Markup);
Assert.Contains(@$"rz-spinner-down", component.Markup);
Assert.Contains(@$"rz-numeric", component.Markup);
Assert.Contains(@$"rz-numeric-up", component.Markup);
Assert.Contains(@$"rz-numeric-down", component.Markup);
}
[Fact]
@@ -54,7 +54,7 @@ namespace Radzen.Blazor.Tests
parameters.Add<decimal?>(p => p.Min, minValue);
});
component.Find(".rz-spinner-down").Click();
component.Find(".rz-numeric-down").Click();
Assert.False(raised, $"Numeric value should Change event if value is less than min value.");
}
@@ -108,7 +108,7 @@ namespace Radzen.Blazor.Tests
parameters.Add<decimal?>(p => p.Max, maxValue);
});
component.Find(".rz-spinner-up").Click();
component.Find(".rz-numeric-up").Click();
Assert.False(raised, $"Numeric value should Change event if value is less than min value.");
}
@@ -216,12 +216,12 @@ namespace Radzen.Blazor.Tests
var component = ctx.RenderComponent<RadzenNumeric<double>>();
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, false));
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", false));
Assert.Contains(@$"autocomplete=""off""", component.Markup);
Assert.Contains(@$"aria-autocomplete=""none""", component.Markup);
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, true));
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
Assert.Contains(@$"autocomplete=""on""", component.Markup);
Assert.DoesNotContain(@$"aria-autocomplete", component.Markup);
@@ -232,7 +232,7 @@ namespace Radzen.Blazor.Tests
Assert.DoesNotContain(@$"aria-autocomplete", component.Markup);
component.Instance.DefaultAutoCompleteAttribute = "autocomplete-custom";
component.SetParametersAndRender(parameters => parameters.Add(p => p.AutoComplete, false));
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", false));
Assert.Contains(@$"autocomplete=""autocomplete-custom""", component.Markup);
Assert.Contains(@$"aria-autocomplete=""none""", component.Markup);
@@ -245,22 +245,22 @@ namespace Radzen.Blazor.Tests
var component = ctx.RenderComponent<RadzenNumeric<double>>();
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, false));
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", false));
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.On));
Assert.Contains(@$"autocomplete=""off""", component.Markup);
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, true));
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.Off));
Assert.Contains(@$"autocomplete=""off""", component.Markup);
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, true));
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.BdayMonth));
Assert.Contains(@$"autocomplete=""{AutoCompleteType.BdayMonth.GetAutoCompleteValue()}""", component.Markup);
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, true));
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.BdayYear));
Assert.Contains(@$"autocomplete=""{AutoCompleteType.BdayYear.GetAutoCompleteValue()}""", component.Markup);
@@ -329,7 +329,7 @@ namespace Radzen.Blazor.Tests
parameters.Add(p => p.Change, args => { raised = true; newValue = args; });
});
component.Find(".rz-spinner-up").Click();
component.Find(".rz-numeric-up").Click();
Assert.True(raised, "Numeric Change should be raised on step up");
Assert.True(object.Equals(expectedValue, newValue), $"Numeric value should be incremented on step up. Expected value: {expectedValue}, value: {newValue}");
@@ -338,7 +338,7 @@ namespace Radzen.Blazor.Tests
component.SetParametersAndRender(parameters => parameters.Add(p => p.ValueChanged, args => { raised = true; }));
component.Find(".rz-spinner-up").Click();
component.Find(".rz-numeric-up").Click();
Assert.True(raised, "Numeric ValueChanged should be raised on step up");
}
@@ -364,7 +364,7 @@ namespace Radzen.Blazor.Tests
parameters.Add(p => p.Change, args => { raised = true; newValue = args; });
});
component.Find(".rz-spinner-down").Click();
component.Find(".rz-numeric-down").Click();
Assert.True(raised, "Numeric Change should be raised on step up");
Assert.True(object.Equals(expectedValue, newValue), $"Numeric value should be incremented on step up. Expected value: {expectedValue}, value: {newValue}");
@@ -373,7 +373,7 @@ namespace Radzen.Blazor.Tests
component.SetParametersAndRender(parameters => parameters.Add(p => p.ValueChanged, args => { raised = true; }));
component.Find(".rz-spinner-down").Click();
component.Find(".rz-numeric-down").Click();
Assert.True(raised, "Numeric ValueChanged should be raised on step up");
}
@@ -387,9 +387,9 @@ namespace Radzen.Blazor.Tests
component.Render();
Assert.Contains(@$"rz-spinner-button-icon", component.Markup);
Assert.Contains(@$"rz-spinner-up", component.Markup);
Assert.Contains(@$"rz-spinner-down", component.Markup);
Assert.Contains(@$"rz-numeric-button-icon", component.Markup);
Assert.Contains(@$"rz-numeric-up", component.Markup);
Assert.Contains(@$"rz-numeric-down", component.Markup);
}
[Fact]
@@ -401,9 +401,9 @@ namespace Radzen.Blazor.Tests
component.Render();
Assert.DoesNotContain(@$"rz-spinner-button-icon", component.Markup);
Assert.DoesNotContain(@$"rz-spinner-up", component.Markup);
Assert.DoesNotContain(@$"rz-spinner-down", component.Markup);
Assert.DoesNotContain(@$"rz-numeric-button-icon", component.Markup);
Assert.DoesNotContain(@$"rz-numeric-up", component.Markup);
Assert.DoesNotContain(@$"rz-numeric-down", component.Markup);
}
[Fact]

View File

@@ -23,14 +23,14 @@ namespace Radzen.Blazor.Tests
component.Render();
Assert.Contains(@$"rz-paginator", component.Markup);
Assert.Contains(@$"rz-pager", component.Markup);
component.SetParametersAndRender(parameters =>
{
parameters.Add<int>(p => p.PageSize, 101);
parameters.Add<int>(p => p.Count, 100);
});
Assert.DoesNotContain(@$"rz-paginator", component.Markup);
Assert.DoesNotContain(@$"rz-pager", component.Markup);
}
[Fact]
@@ -49,7 +49,7 @@ namespace Radzen.Blazor.Tests
component.Render();
Assert.Contains(@$"rz-paginator", component.Markup);
Assert.Contains(@$"rz-pager", component.Markup);
Assert.Contains(@$"rz-dropdown-trigger", component.Markup);
}
@@ -67,13 +67,13 @@ namespace Radzen.Blazor.Tests
await component.Instance.GoToPage(2);
component.Render();
Assert.Contains(@$"rz-paginator-summary", component.Markup);
Assert.Contains(@$"rz-pager-summary", component.Markup);
Assert.Contains(@$"Page 3 of 10 (100 items)", component.Markup);
component.SetParametersAndRender(parameters => {
parameters.Add<bool>(p => p.ShowPagingSummary, false);
});
Assert.DoesNotContain(@$"rz-paginator-summary", component.Markup);
Assert.DoesNotContain(@$"rz-pager-summary", component.Markup);
}
[Fact]
@@ -109,5 +109,51 @@ namespace Radzen.Blazor.Tests
Assert.Contains(@$"rz-density-compact", component.Markup);
}
[Fact]
public async void RadzenPager_First_And_Prev_Buttons_Are_Disabled_When_On_The_First_Page()
{
using var ctx = new TestContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var component = ctx.RenderComponent<RadzenPager>(parameters => {
parameters.Add<int>(p => p.PageSize, 10);
parameters.Add<int>(p => p.Count, 100);
parameters.Add<bool>(p => p.ShowPagingSummary, true);
});
await component.Instance.GoToPage(0);
component.Render();
var firstPageButton = component.Find("a.rz-pager-first");
Assert.True(firstPageButton.HasAttribute("disabled"));
var prevPageButton = component.Find("a.rz-pager-prev");
Assert.True(prevPageButton.HasAttribute("disabled"));
}
[Fact]
public async void RadzenPager_Last_And_Next_Buttons_Are_Disabled_When_On_The_Last_Page()
{
using var ctx = new TestContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var component = ctx.RenderComponent<RadzenPager>(parameters => {
parameters.Add<int>(p => p.PageSize, 10);
parameters.Add<int>(p => p.Count, 100);
parameters.Add<bool>(p => p.ShowPagingSummary, true);
});
await component.Instance.GoToPage(9);
component.Render();
var lastPageButton = component.Find("a.rz-pager-last");
Assert.True(lastPageButton.HasAttribute("disabled"));
var nextPageButton = component.Find("a.rz-pager-next");
Assert.True(nextPageButton.HasAttribute("disabled"));
}
}
}

View File

@@ -47,7 +47,7 @@ namespace Radzen.Blazor.Tests
component.SetParametersAndRender(parameters => parameters.Add(p => p.Icon, value));
Assert.Contains(@$"<i class=""rzi"">{value}</i>", component.Markup);
Assert.Contains(@$"<i class=""notranslate rzi"">{value}</i>", component.Markup);
}
[Fact]
@@ -119,11 +119,11 @@ namespace Radzen.Blazor.Tests
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AllowCollapse, true));
Assert.Contains(@"<span class=""rzi rzi-minus""></span>", component.Markup);
Assert.Contains(@"<span class=""notranslate rzi rzi-minus""></span>", component.Markup);
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.Collapsed, true));
Assert.Contains(@"<span class=""rzi rzi-plus""></span>", component.Markup);
Assert.Contains(@"<span class=""notranslate rzi rzi-plus""></span>", component.Markup);
}
[Fact]

View File

@@ -119,11 +119,11 @@ namespace Radzen.Blazor.Tests
var component = ctx.RenderComponent<RadzenPassword>();
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, false));
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", false));
Assert.Contains(@$"autocomplete=""new-password""", component.Markup);
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, true));
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
Assert.Contains(@$"autocomplete=""on""", component.Markup);
@@ -139,22 +139,22 @@ namespace Radzen.Blazor.Tests
var component = ctx.RenderComponent<RadzenPassword>();
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, false));
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", false));
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.On));
Assert.Contains(@$"autocomplete=""new-password""", component.Markup);
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, true));
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.Off));
Assert.Contains(@$"autocomplete=""off""", component.Markup);
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, true));
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.CurrentPassword));
Assert.Contains(@$"autocomplete=""{AutoCompleteType.CurrentPassword.GetAutoCompleteValue()}""", component.Markup);
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, true));
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.NewPassword));
Assert.Contains(@$"autocomplete=""{AutoCompleteType.NewPassword.GetAutoCompleteValue()}""", component.Markup);

View File

@@ -72,7 +72,7 @@ namespace Radzen.Blazor.Tests
component.SetParametersAndRender(parameters => parameters.Add(p => p.Style, value));
Assert.Contains(@$"style=""{value}""", component.Markup);
Assert.Contains(@$"style=""--rz-progressbar-value: 0%;{value}""", component.Markup);
}
[Fact]
@@ -118,7 +118,7 @@ namespace Radzen.Blazor.Tests
parameters.Add<double>(p => p.Max, max);
});
Assert.Contains(@$"style=""width: {Math.Min(value / max * 100, 100).ToInvariantString()}%;""", component.Markup);
Assert.Contains(@$"style=""--rz-progressbar-value: {Math.Min(value / max * 100, 100).ToInvariantString()}%;""", component.Markup);
}
[Fact]

View File

@@ -39,7 +39,7 @@ namespace Radzen.Blazor.Tests
component.SetParametersAndRender(parameters => parameters.Add<int>(p => p.Value, value));
Assert.Contains(@$"style=""width: {Math.Round((value / max * 100)).ToInvariantString()}%;""", component.Markup);
Assert.Contains(@$"style=""left: {Math.Round((value / max * 100)).ToInvariantString()}%;""", component.Markup);
Assert.Contains(@$"style=""inset-inline-start: {Math.Round((value / max * 100)).ToInvariantString()}%;""", component.Markup);
}
[Fact]
@@ -55,9 +55,9 @@ namespace Radzen.Blazor.Tests
parameters.Add<IEnumerable<int>>(p => p.Value, new int[] { 4, 30 });
});
Assert.Contains(@$"left: 4%", component.Markup);
Assert.Contains(@$"left: 30%", component.Markup);
Assert.Contains(@$"left: 4%; width: 26%;", component.Markup);
Assert.Contains(@$"inset-inline-start: 4%", component.Markup);
Assert.Contains(@$"inset-inline-start: 30%", component.Markup);
Assert.Contains(@$"inset-inline-start: 4%; width: 26%;", component.Markup);
}
[Fact]

View File

@@ -1,4 +1,5 @@
using Bunit;
using Microsoft.AspNetCore.Components;
using Xunit;
namespace Radzen.Blazor.Tests
@@ -44,7 +45,7 @@ namespace Radzen.Blazor.Tests
component.SetParametersAndRender(parameters => parameters.Add(p => p.Icon, icon));
Assert.Contains(@$"<i class=""rz-button-icon-left rzi"">{icon}</i>", component.Markup);
Assert.Contains(@$"<i class=""notranslate rz-button-icon-left rzi"">{icon}</i>", component.Markup);
}
[Fact]
@@ -59,10 +60,10 @@ namespace Radzen.Blazor.Tests
component.SetParametersAndRender(parameters => {
parameters.Add(p => p.Text, text);
parameters.Add(p => p.Icon, icon);
parameters.Add(p => p.Icon, icon);
});
Assert.Contains(@$"<i class=""rz-button-icon-left rzi"">{icon}</i>", component.Markup);
Assert.Contains(@$"<i class=""notranslate rz-button-icon-left rzi"">{icon}</i>", component.Markup);
Assert.Contains(@$"<span class=""rz-button-text"">{text}</span>", component.Markup);
}
@@ -77,7 +78,7 @@ namespace Radzen.Blazor.Tests
component.SetParametersAndRender(parameters => parameters.Add(p => p.Image, image));
Assert.Contains(@$"<img class=""rz-button-icon-left rzi"" src=""{image}"" alt=""image"" />", component.Markup);
Assert.Contains(@$"<img class=""notranslate rz-button-icon-left rzi"" src=""{image}"" alt=""image"" />", component.Markup);
}
[Fact]
@@ -96,10 +97,26 @@ namespace Radzen.Blazor.Tests
parameters.Add(p => p.ImageAlternateText, text);
});
Assert.Contains(@$"<img class=""rz-button-icon-left rzi"" src=""{image}"" alt=""{text}"" />", component.Markup);
Assert.Contains(@$"<img class=""notranslate rz-button-icon-left rzi"" src=""{image}"" alt=""{text}"" />", component.Markup);
Assert.Contains(@$"<span class=""rz-button-text"">{text}</span>", component.Markup);
}
[Fact]
public void SplitButton_Renders_ButtonContent()
{
using var ctx = new TestContext();
RenderFragment buttonContent = (builder) => builder.AddMarkupContent(0, "<strong>Custom button content</strong>");
var text = "Test";
var component = ctx.RenderComponent<RadzenSplitButton>(parameters => parameters
.Add(p => p.ButtonContent, buttonContent)
.Add(p => p.Text, text));
Assert.Contains(@$"<strong>Custom button content</strong>", component.Markup);
Assert.DoesNotContain(@$"<span class=""rz-button-text"">{text}</span>", component.Markup);
}
[Fact]
public void SplitButton_Renders_DisabledParameter()
{

View File

@@ -119,12 +119,12 @@ namespace Radzen.Blazor.Tests
var component = ctx.RenderComponent<RadzenTextBox>();
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, false));
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", false));
Assert.Contains(@$"autocomplete=""off""", component.Markup);
Assert.Contains(@$"aria-autocomplete=""none""", component.Markup);
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, true));
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
Assert.Contains(@$"autocomplete=""on""", component.Markup);
Assert.DoesNotContain(@$"aria-autocomplete", component.Markup);
@@ -135,7 +135,7 @@ namespace Radzen.Blazor.Tests
Assert.DoesNotContain(@$"aria-autocomplete", component.Markup);
component.Instance.DefaultAutoCompleteAttribute = "autocomplete-custom";
component.SetParametersAndRender(parameters => parameters.Add(p => p.AutoComplete, false));
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", false));
Assert.Contains(@$"autocomplete=""autocomplete-custom""", component.Markup);
Assert.Contains(@$"aria-autocomplete=""none""", component.Markup);
@@ -148,22 +148,22 @@ namespace Radzen.Blazor.Tests
var component = ctx.RenderComponent<RadzenTextBox>();
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, false));
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", false));
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.On));
Assert.Contains(@$"autocomplete=""off""", component.Markup);
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, true));
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.Off));
Assert.Contains(@$"autocomplete=""off""", component.Markup);
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, true));
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.AdditionalName));
Assert.Contains(@$"autocomplete=""{AutoCompleteType.AdditionalName.GetAutoCompleteValue()}""", component.Markup);
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, true));
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.FamilyName));
Assert.Contains(@$"autocomplete=""{AutoCompleteType.FamilyName.GetAutoCompleteValue()}""", component.Markup);

View File

@@ -5,7 +5,7 @@ using System.Linq;
using System.Linq.Dynamic.Core;
using Radzen.Blazor.Rendering;
using System.Threading.Tasks;
using System.Collections;
using System.Net.Mime;
using Microsoft.AspNetCore.Components.Rendering;
namespace Radzen.Blazor
@@ -92,6 +92,12 @@ namespace Radzen.Blazor
throw new ArgumentException($"Property {propertyName} does not exist");
}
#if NET6_0_OR_GREATER
if(PropertyAccess.IsDateOnly(property))
{
return false;
}
#endif
return PropertyAccess.IsDate(property);
}
@@ -404,7 +410,7 @@ namespace Radzen.Blazor
if (IsDate(CategoryProperty) || IsNumeric(CategoryProperty))
{
Items = Items.AsQueryable().OrderBy(CategoryProperty).ToList();
Items = Items.AsQueryable().OrderBy(DynamicLinqCustomTypeProvider.ParsingConfig, CategoryProperty).ToList();
}
}
@@ -481,18 +487,62 @@ namespace Radzen.Blazor
return builder =>
{
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));
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.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.CloseComponent();
}
};
}
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.CloseComponent();
private RenderFragment RenderSharedTooltipItems(object category)
{
return builder =>
{
var visibleSeries = Chart.Series.Where(s => s.Visible).ToList();
foreach (var series in visibleSeries)
{
builder.AddContent(1, series.RenderSharedTooltipItem(category));
}
};
}
/// <inheritdoc />
public virtual RenderFragment RenderSharedTooltipItem(object category)
{
return builder =>
{
var item = Items.FirstOrDefault(i => object.Equals(PropertyAccess.GetValue(i, CategoryProperty), category));
if (item != null)
{
builder.OpenComponent<ChartSharedTooltipItem>(0);
builder.AddAttribute(1, nameof(ChartSharedTooltipItem.Value), TooltipValue(item));
builder.AddAttribute(2, nameof(ChartSharedTooltipItem.ChildContent), TooltipTemplate?.Invoke(item));
builder.AddAttribute(3, nameof(ChartSharedTooltipItem.LegendItem), RenderLegendItem(false));
builder.CloseComponent();
}
};
}
@@ -516,6 +566,14 @@ namespace Radzen.Blazor
/// <inheritdoc />
public virtual RenderFragment RenderLegendItem()
{
return RenderLegendItem(true);
}
/// <summary>
/// Renders the legend item for this series.
/// </summary>
protected virtual RenderFragment RenderLegendItem(bool clickable)
{
var style = new List<string>();
@@ -534,6 +592,7 @@ namespace Radzen.Blazor
builder.AddAttribute(5, nameof(LegendItem.MarkerSize), MarkerSize);
builder.AddAttribute(6, nameof(LegendItem.Text), GetTitle());
builder.AddAttribute(7, nameof(LegendItem.Click), EventCallback.Factory.Create(this, OnLegendItemClick));
builder.AddAttribute(8, nameof(LegendItem.Clickable), clickable);
builder.CloseComponent();
};
}

View File

@@ -291,6 +291,7 @@ namespace Radzen
services.AddScoped<NotificationService>();
services.AddScoped<TooltipService>();
services.AddScoped<ContextMenuService>();
services.AddScoped<ThemeService>();
return services;
}
@@ -505,6 +506,112 @@ namespace Radzen
void Refresh();
}
/// <summary>
/// Supplies information about RadzenDropZoneContainer CanDrop function and RadzenDropZone Drop event.
/// </summary>
public class RadzenDropZoneItemEventArgs<TItem>
{
/// <summary>
/// Gets the dragged item zone.
/// </summary>
public RadzenDropZone<TItem> FromZone { get; internal set; }
/// <summary>
/// Gets the drop zone.
/// </summary>
public RadzenDropZone<TItem> ToZone { get; internal set; }
/// <summary>
/// Gets the dragged item.
/// </summary>
public TItem Item { get; internal set; }
/// <summary>
/// Gets the dropped item.
/// </summary>
public TItem ToItem { get; internal set; }
}
/// <summary>
/// Supplies information about RadzenDropZoneContainer ItemRender event.
/// </summary>
public class RadzenDropZoneItemRenderEventArgs<TItem>
{
/// <summary>
/// Gets the drop zone.
/// </summary>
public RadzenDropZone<TItem> Zone { get; internal set; }
/// <summary>
/// Gets the dragged 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>
[Parameter]
public bool Visible { get; set; } = true;
/// <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 RadzenDropDown ItemRender event.
/// </summary>
public class DropDownBaseItemRenderEventArgs<TValue>
{
/// <summary>
/// Gets the data item.
/// </summary>
public object 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>
[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>
/// Gets or sets the row HTML attributes.
/// </summary>
public IDictionary<string, object> Attributes { get; private set; } = new Dictionary<string, object>();
}
/// <summary>
/// Supplies information about RadzenDropDown ItemRender event.
/// </summary>
public class DropDownItemRenderEventArgs<TValue> : DropDownBaseItemRenderEventArgs<TValue>
{
/// <summary>
/// Gets the DropDown.
/// </summary>
public RadzenDropDown<TValue> DropDown { get; internal set; }
}
/// <summary>
/// Supplies information about RadzenDropDown ItemRender event.
/// </summary>
public class ListBoxItemRenderEventArgs<TValue> : DropDownBaseItemRenderEventArgs<TValue>
{
/// <summary>
/// Gets the DropDown.
/// </summary>
public RadzenListBox<TValue> ListBox { get; internal set; }
}
/// <summary>
/// Supplies information about a <see cref="RadzenDatePicker{TValue}.DateRender" /> event that is being raised.
/// </summary>
@@ -591,12 +698,12 @@ namespace Radzen
{
/// <summary>
/// Gets or sets the appointment data.
/// </summary>
/// </summary>
public AppointmentData Appointment { get; set; }
/// <summary>
/// Gets or sets the time span.
/// </summary>
/// </summary>
public TimeSpan TimeSpan { get; set; }
}
@@ -651,6 +758,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>
@@ -681,23 +793,6 @@ namespace Radzen
public bool FirstRender { get; internal set; }
}
/// <summary>
/// Supplies information about a <see cref="RadzenGrid{TItem}.Render" /> event that is being raised.
/// </summary>
/// <typeparam name="T"></typeparam>
public class GridRenderEventArgs<T>
{
/// <summary>
/// Gets the instance of the RadzenGrid component which has rendered.
/// </summary>
public RadzenGrid<T> Grid { get; internal set; }
/// <summary>
/// Gets a value indicating whether this is the first time the RadzenGrid has rendered.
/// </summary>
/// <value><c>true</c> if this is the first time; otherwise, <c>false</c>.</value>
public bool FirstRender { get; internal set; }
}
/// <summary>
/// Supplies information about a <see cref="RadzenDataGrid{TItem}.Render" /> event that is being raised.
/// </summary>
@@ -726,18 +821,6 @@ namespace Radzen
public DataGridSettings Settings { get; set; }
}
/// <summary>
/// Supplies information about a <see cref="RadzenGrid{TItem}.CellRender" /> event that is being raised.
/// </summary>
/// <typeparam name="T"></typeparam>
public class CellRenderEventArgs<T> : RowRenderEventArgs<T>
{
/// <summary>
/// Gets the RadzenGridColumn which this cells represents.
/// </summary>
public RadzenGridColumn<T> Column { get; internal set; }
}
/// <summary>
/// Supplies information about a <see cref="RadzenDataGrid{TItem}.CellRender" /> event that is being raised.
/// </summary>
@@ -767,6 +850,22 @@ namespace Radzen
public RadzenDataGridColumn<T> Column { get; internal set; }
}
/// <summary>
/// Supplies information about a <see cref="RadzenTree.ItemContextMenu" /> event that is being raised.
/// </summary>
public class TreeItemContextMenuEventArgs : Microsoft.AspNetCore.Components.Web.MouseEventArgs
{
/// <summary>
/// Gets the tree item text.
/// </summary>
public string Text { get; internal set; }
/// <summary>
/// Gets the tree item value.
/// </summary>
public object Value { get; internal set; }
}
/// <summary>
/// Supplies information about a <see cref="RadzenDataGrid{TItem}.RowClick" /> or <see cref="RadzenDataGrid{TItem}.RowDoubleClick" /> event that is being raised.
/// </summary>
@@ -841,12 +940,8 @@ namespace Radzen
/// <summary>
/// Represents a file which the user selects for upload via <see cref="RadzenUpload" />.
/// </summary>
public class FileInfo
#if NET5_0_OR_GREATER
: IBrowserFile
#endif
public class FileInfo : IBrowserFile
{
#if NET5_0_OR_GREATER
/// <summary>
/// Creates FileInfo.
/// </summary>
@@ -863,20 +958,16 @@ namespace Radzen
{
this.source = source;
}
#endif
string _name;
/// <summary>
/// Gets the name of the selected file.
/// </summary>
public string Name
public string Name
{
get
{
#if NET5_0_OR_GREATER
return _name ?? source.Name;
#else
return _name;
#endif
}
set
{
@@ -892,11 +983,7 @@ namespace Radzen
{
get
{
#if NET5_0_OR_GREATER
return _size != default(long) ? _size : source.Size;
#else
return _size;
#endif
return _size != default(long) ? _size : source != null ? source.Size : 0;
}
set
{
@@ -904,7 +991,6 @@ namespace Radzen
}
}
#if NET5_0_OR_GREATER
/// <summary>
/// Gets the IBrowserFile.
/// </summary>
@@ -927,7 +1013,6 @@ namespace Radzen
{
return source.OpenReadStream(maxAllowedSize, cancellationToken);
}
#endif
}
/// <summary>
@@ -1283,11 +1368,11 @@ namespace Radzen
/// </summary>
Light,
/// <summary>
/// Dark styling. Similar to dark buttons.
/// Base styling. Similar to base buttons.
/// </summary>
Base,
/// <summary>
/// The default styling.
/// Dark styling. Similar to dark buttons.
/// </summary>
Dark,
/// <summary>
@@ -1372,11 +1457,11 @@ namespace Radzen
/// </summary>
Light,
/// <summary>
/// Dark styling. Similar to dark buttons.
/// Base styling. Similar to base buttons.
/// </summary>
Base,
/// <summary>
/// The default styling.
/// Dark styling. Similar to dark buttons.
/// </summary>
Dark,
/// <summary>
@@ -1430,6 +1515,10 @@ namespace Radzen
/// </summary>
Light,
/// <summary>
/// Base styling. Similar to base buttons.
/// </summary>
Base,
/// <summary>
/// Dark styling. Similar to dark buttons.
/// </summary>
Dark,
@@ -1657,6 +1746,10 @@ namespace Radzen
/// </summary>
Light,
/// <summary>
/// The base UI styling.
/// </summary>
Base,
/// <summary>
/// A button with dark styling.
/// </summary>
Dark,
@@ -1744,7 +1837,11 @@ namespace Radzen
/// <summary>
/// The component displays a popup filtering UI and allows you to pick filtering operator and or filter by multiple values.
/// </summary>
Advanced
Advanced,
/// <summary>
/// The component displays a popup filtering UI and allows you to pick multiple values from list of all values.
/// </summary>
CheckBoxList
}
/// <summary>
@@ -1939,6 +2036,10 @@ namespace Radzen
/// </summary>
Light,
/// <summary>
/// Base styling. Similar to base buttons.
/// </summary>
Base,
/// <summary>
/// Dark styling. Similar to dark buttons.
/// </summary>
Dark,
@@ -1978,6 +2079,10 @@ namespace Radzen
/// </summary>
Light,
/// <summary>
/// Base styling. Similar to base buttons.
/// </summary>
Base,
/// <summary>
/// Dark styling. Similar to dark buttons.
/// </summary>
Dark,
@@ -2099,6 +2204,28 @@ namespace Radzen
public double Width { get; internal set; }
}
/// <summary>
/// Supplies information about a <see cref="RadzenDataGrid{TItem}.ColumnReordering" /> event that is being raised.
/// </summary>
/// <typeparam name="T"></typeparam>
public class DataGridColumnReorderingEventArgs<T>
{
/// <summary>
/// Gets the reordered RadzenDataGridColumn.
/// </summary>
public RadzenDataGridColumn<T> Column { get; internal set; }
/// <summary>
/// Gets the reordered to RadzenDataGridColumn.
/// </summary>
public RadzenDataGridColumn<T> ToColumn { get; internal set; }
/// <summary>
/// Gets or sets a value which will cancel the event.
/// </summary>
/// <value><c>true</c> to cancel the event; otherwise, <c>false</c>.</value>
public bool Cancel { get; set; }
}
/// <summary>
/// Supplies information about a <see cref="RadzenDataGrid{TItem}.ColumnReordered" /> event that is being raised.
/// </summary>
@@ -2119,22 +2246,6 @@ namespace Radzen
public int NewIndex { get; internal set; }
}
/// <summary>
/// Supplies information about a <see cref="RadzenGrid{TItem}.ColumnResized" /> event that is being raised.
/// </summary>
/// <typeparam name="T"></typeparam>
public class ColumnResizedEventArgs<T>
{
/// <summary>
/// Gets the resized RadzenGridColumn.
/// </summary>
public RadzenGridColumn<T> Column { get; internal set; }
/// <summary>
/// Gets the new width of the column.
/// </summary>
public double Width { get; internal set; }
}
/// <summary>
/// Represents a filter in a component that supports filtering.
/// </summary>
@@ -2345,6 +2456,46 @@ namespace Radzen
internal IEnumerable<T> Data { get; set; }
}
/// <summary>
/// Supplies information about a <see cref="RadzenDataGrid{TItem}.LoadColumnFilterData" /> event that is being raised.
/// </summary>
public class DataGridLoadColumnFilterDataEventArgs<T>
{
/// <summary>
/// Gets or sets the data.
/// </summary>
/// <value>The data.</value>
public IEnumerable Data { get; set; }
/// <summary>
/// Gets or sets the total data count.
/// </summary>
/// <value>The total data count.</value>
public int Count { get; set; }
/// <summary>
/// Gets how many items to skip. Related to paging and the current page. Usually used with the <see cref="Enumerable.Skip{TSource}(IEnumerable{TSource}, int)"/> LINQ method.
/// </summary>
public int? Skip { get; set; }
/// <summary>
/// Gets how many items to take. Related to paging and the current page size. Usually used with the <see cref="Enumerable.Take{TSource}(IEnumerable{TSource}, int)"/> LINQ method.
/// </summary>
/// <value>The top.</value>
public int? Top { get; set; }
/// <summary>
/// Gets the filter expression as a string.
/// </summary>
/// <value>The filter.</value>
public string Filter { get; internal set; }
/// <summary>
/// Gets the column.
/// </summary>
/// <value>The column.</value>
public RadzenDataGridColumn<T> Column { get; internal set; }
}
/// <summary>
/// Supplies information about a <see cref="RadzenPager.PageChanged" /> event that is being raised.
/// </summary>
@@ -2614,10 +2765,18 @@ namespace Radzen
/// </summary>
public Func<object, string> Text { get; set; }
/// <summary>
/// Gets or sets the function which returns a value for the <see cref="RadzenTreeItem.Checkable" /> of a child item.
/// </summary>
public Func<object, bool> Checkable { get; set; }
/// <summary>
/// Gets or sets the name of the property which provides the value for the <see cref="RadzenTreeItem.Text" /> of a child item.
/// </summary>
public string TextProperty { get; set; }
/// <summary>
/// Gets or sets the name of the property which provides the value for the <see cref="RadzenTreeItem.Checkable" /> of a child item.
/// </summary>
public string CheckableProperty { get; set; }
/// <summary>
/// Gets or sets a function which returns whether a child item has children of its own. Called with an item from <see cref="Data" />.
/// By default all items are considered to have children.
/// </summary>
@@ -2645,7 +2804,7 @@ namespace Radzen
public class TreeItemRenderEventArgs
{
/// <summary>
/// Gets or sets the item HTML attributes.
/// Gets or sets the item HTML attributes.
/// </summary>
public IDictionary<string, object> Attributes { get; private set; } = new Dictionary<string, object>();
@@ -2660,7 +2819,7 @@ namespace Radzen
/// Gets or sets a value indicating whether this item is checked.
/// </summary>
/// <value><c>true</c> if expanded; otherwise, <c>false</c>.</value>
public bool? Checked
public bool? Checked
{
get
{
@@ -2832,10 +2991,48 @@ namespace Radzen
{
return true;
}
#if NET6_0_OR_GREATER
if (type == typeof(DateOnly))
{
return true;
}
#endif
return false;
}
/// <summary>
/// Determines whether the specified type is a DateOnly.
/// </summary>
/// <param name="source">The source.</param>
/// <returns><c>true</c> if the specified type is a DateOnly instance or nullable DateOnly; otherwise, <c>false</c>.</returns>
public static bool IsDateOnly(Type source)
{
if (source == null) return false;
var type = source.IsGenericType ? source.GetGenericArguments()[0] : source;
#if NET6_0_OR_GREATER
if (type == typeof(DateOnly))
{
return true;
}
#endif
return false;
}
/// <summary>
/// Determines whether the specified type is a DateOnly.
/// </summary>
/// <param name="source">The source.</param>
/// <returns><c>true</c> if the specified type is a DateOnly instance or nullable DateOnly; otherwise, <c>false</c>.</returns>
public static object DateOnlyFromDateTime(DateTime source)
{
object result = null;
#if NET6_0_OR_GREATER
result = DateOnly.FromDateTime(source);
#endif
return result;
}
/// <summary>
/// Gets the type of the element of a collection time.
/// </summary>
@@ -2921,7 +3118,7 @@ namespace Radzen
{
var type = data.GetType();
var arg = Expression.Parameter(typeof(object));
var body = Expression.Property(Expression.Convert(arg, type), propertyName);
var body = Expression.Convert(Expression.Property(Expression.Convert(arg, type), propertyName), typeof(T));
return Expression.Lambda<Func<object, T>>(body, arg).Compile();
}
@@ -3098,6 +3295,21 @@ namespace Radzen
return null;
}
/// <summary>
/// Gets the dynamic property expression when binding to IDictionary.
/// </summary>
/// <param name="name">The property name.</param>
/// <param name="type">The property type.</param>
/// <returns>Dynamic property expression.</returns>
public static string GetDynamicPropertyExpression(string name, Type type)
{
var isEnum = type.IsEnum || Nullable.GetUnderlyingType(type)?.IsEnum == true;
var typeName = isEnum ? "Enum" : (Nullable.GetUnderlyingType(type) ?? type).Name;
var typeFunc = $@"{typeName}{(!isEnum && Nullable.GetUnderlyingType(type) != null ? "?" : "")}";
return $@"{typeFunc}(it[""{name}""])";
}
}
/// <summary>
@@ -3157,12 +3369,11 @@ namespace Radzen
/// </summary>
/// <value>The field identifier.</value>
FieldIdentifier FieldIdentifier { get; }
#if NET5_0_OR_GREATER
/// <summary>
/// Sets the focus.
/// </summary>
ValueTask FocusAsync();
#endif
}
/// <summary>

View File

@@ -0,0 +1,110 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.JSInterop;
namespace Radzen
{
/// <summary>
/// Options for the <see cref="CookieThemeService" />.
/// </summary>
public class CookieThemeServiceOptions
{
/// <summary>
/// Gets or sets the cookie name.
/// </summary>
public string Name { get; set; } = "Theme";
/// <summary>
/// Gets or sets the cookie duration.
/// </summary>
public TimeSpan Duration { get; set; } = TimeSpan.FromDays(365);
}
/// <summary>
/// Persist the current theme in a cookie. Requires <see cref="ThemeService" /> to be registered in the DI container.
/// </summary>
public class CookieThemeService
{
private readonly CookieThemeServiceOptions options;
private readonly IJSRuntime jsRuntime;
private readonly ThemeService themeService;
/// <summary>
/// Initializes a new instance of the <see cref="CookieThemeService" /> class.
/// </summary>
public CookieThemeService(IJSRuntime jsRuntime, ThemeService themeService, IOptions<CookieThemeServiceOptions> options)
{
this.jsRuntime = jsRuntime;
this.themeService = themeService;
this.options = options.Value;
themeService.ThemeChanged += OnThemeChanged;
_ = InitializeAsync();
}
private async Task InitializeAsync()
{
try
{
var cookies = await jsRuntime.InvokeAsync<string>("eval", "document.cookie");
var themeCookie = cookies?.Split("; ").Select(x =>
{
var parts = x.Split("=");
return (Key: parts[0], Value: parts[1]);
})
.FirstOrDefault(x => x.Key == options.Name);
var theme = themeCookie?.Value;
if (!string.IsNullOrEmpty(theme) && themeService.Theme != theme)
{
themeService.SetTheme(theme);
}
}
catch (InvalidOperationException)
{
}
}
private void OnThemeChanged()
{
var expiration = DateTime.Now.Add(options.Duration);
_ = jsRuntime.InvokeVoidAsync("eval", $"document.cookie = \"{options.Name}={themeService.Theme}; expires={expiration:R}; path=/\"");
}
}
/// <summary>
/// Extension methods to register the <see cref="CookieThemeService" />.
/// </summary>
public static class CookieThemeServiceCollectionExtensions
{
/// <summary>
/// Adds the <see cref="CookieThemeService" /> to the service collection.
/// </summary>
public static IServiceCollection AddRadzenCookieThemeService(this IServiceCollection services)
{
services.AddOptions<CookieThemeServiceOptions>();
services.AddScoped<CookieThemeService>();
return services;
}
/// <summary>
/// Adds the <see cref="CookieThemeService" /> to the service collection with the specified configuration.
/// </summary>
public static IServiceCollection AddRadzenCookieThemeService(this IServiceCollection services, Action<CookieThemeServiceOptions> configure)
{
services.Configure(configure);
services.AddScoped<CookieThemeService>();
return services;
}
}
}

View File

@@ -1,6 +1,6 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Web;
using Radzen.Blazor;
using Radzen.Blazor.Rendering;
@@ -278,7 +278,7 @@ namespace Radzen
query.Add($"{Enum.GetName(typeof(StringFilterOperator), FilterOperator)}(@0)");
_view = Query.Where(String.Join(".", query), ignoreCase ? searchText.ToLower() : searchText);
_view = Query.Where(DynamicLinqCustomTypeProvider.ParsingConfig, string.Join(".", query), ignoreCase ? searchText.ToLower() : searchText);
}
else
{
@@ -392,13 +392,12 @@ namespace Radzen
.AddDisabled(Disabled)
.Add(FieldIdentifier, EditContext)
.Add("rz-state-empty", !HasValue);
#if NET5_0_OR_GREATER
/// <inheritdoc/>
public virtual async ValueTask FocusAsync()
{
await Element.FocusAsync();
}
#endif
/// <summary> Provides support for RadzenFormField integration. </summary>
[CascadingParameter]
@@ -406,5 +405,20 @@ namespace Radzen
/// <summary> Gets the current placeholder. Returns empty string if this component is inside a RadzenFormField.</summary>
protected string CurrentPlaceholder => FormFieldContext?.AllowFloatingLabel == true ? " " : Placeholder;
/// <summary>
/// Handles the <see cref="E:ContextMenu" /> event.
/// </summary>
/// <param name="args">The <see cref="MouseEventArgs"/> instance containing the event data.</param>
/// <returns>Task.</returns>
public override Task OnContextMenu(MouseEventArgs args)
{
if (!Disabled)
{
return base.OnContextMenu(args);
}
return Task.CompletedTask;
}
}
}

View File

@@ -2,6 +2,7 @@
using Microsoft.JSInterop;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Threading.Tasks;
@@ -24,7 +25,7 @@ namespace Radzen
/// &lt;div class="row"&gt;
/// &lt;div class="col-md-12"&gt;
/// &lt;RadzenButton Text="Ok" Click="() =&gt; ds.Close(true)" Style="margin-bottom: 10px; width: 150px" /&gt;
/// &lt;RadzenButton Text="Cancel" Click="() =&gt; ds.Close(false)" ButtonStyle="ButtonStyle.Secondary" Style="margin-bottom: 10px; width: 150px"/&gt;
/// &lt;RadzenButton Text="Cancel" Click="() =&gt; ds.Close(false)" ButtonStyle="ButtonStyle.Base" Style="margin-bottom: 10px; width: 150px"/&gt;
/// &lt;RadzenButton Text="Refresh" Click="(() =&gt; { orderID = 10249; ds.Refresh(); })" ButtonStyle="ButtonStyle.Info" Style="margin-bottom: 10px; width: 150px"/&gt;
/// Order ID: @orderID
/// &lt;/div&gt;
@@ -279,6 +280,9 @@ namespace Radzen
CssClass = options != null ? options.CssClass : "",
WrapperCssClass = options != null ? options.WrapperCssClass : "",
CloseTabIndex = options != null ? options.CloseTabIndex : 0,
ContentCssClass = options != null ? options.ContentCssClass : "",
Resize = options?.Resize,
Drag = options?.Drag
});
}
@@ -364,7 +368,7 @@ namespace Radzen
b.OpenComponent<Blazor.RadzenButton>(i++);
b.AddAttribute(i++, "Text", options != null ? options.CancelButtonText : "Cancel");
b.AddAttribute(i++, "ButtonStyle", ButtonStyle.Secondary);
b.AddAttribute(i++, "ButtonStyle", ButtonStyle.Base);
b.AddAttribute(i++, "Click", EventCallback.Factory.Create<Microsoft.AspNetCore.Components.Web.MouseEventArgs>(this, () => ds.Close(false)));
b.CloseComponent();
@@ -401,6 +405,7 @@ namespace Radzen
CloseDialogOnEsc = options != null ? options.CloseDialogOnEsc : true,
CssClass = options != null ? $"rz-dialog-alert {options.CssClass}" : "rz-dialog-alert",
WrapperCssClass = options != null ? $"rz-dialog-wrapper {options.WrapperCssClass}" : "rz-dialog-wrapper",
ContentCssClass = options != null ? $"rz-dialog-content {options.ContentCssClass}" : "rz-dialog-content",
CloseTabIndex = options != null ? options.CloseTabIndex : 0,
};
@@ -476,7 +481,12 @@ namespace Radzen
/// Gets or sets the CSS classes added to the dialog's wrapper element.
/// </summary>
public string WrapperCssClass { get; set; }
/// <summary>
/// Gets or sets the CSS classes added to the dialog's content element.
/// </summary>
public string ContentCssClass { get; set; }
/// <summary>
/// Gets or sets a value the dialog escape tabindex. Set to <c>0</c> by default.
/// </summary>
@@ -502,6 +512,11 @@ namespace Radzen
/// Whether to show a mask on the background or not
/// </summary>
public bool ShowMask { get; set; } = true;
/// <summary>
/// Gets or sets a value indicating whether to focus the first focusable HTML element. Set to <c>true</c> by default.
/// </summary>
public bool AutoFocusFirstElement { get; set; } = false;
}
/// <summary>
@@ -537,11 +552,25 @@ namespace Radzen
/// </summary>
/// <value><c>true</c> if resizable; otherwise, <c>false</c>.</value>
public bool Resizable { get; set; } = false;
/// <summary>
/// Gets or sets the change.
/// </summary>
/// <value>The change.</value>
public Action<Size> Resize { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the dialog is draggable. Set to <c>false</c> by default.
/// </summary>
/// <value><c>true</c> if draggable; otherwise, <c>false</c>.</value>
public bool Draggable { get; set; } = false;
/// <summary>
/// Gets or sets the change.
/// </summary>
/// <value>The change.</value>
public Action<Point> Drag { get; set; }
/// <summary>
/// Gets or sets the X coordinate of the dialog. Maps to the <c>left</c> CSS attribute.
/// </summary>

View File

@@ -17,7 +17,6 @@ namespace Radzen
/// <typeparam name="T"></typeparam>
public class DropDownBase<T> : DataBoundFormComponent<T>
{
#if NET5_0_OR_GREATER
/// <summary>
/// Gets or sets a value that determines how many additional items will be rendered before and after the visible region. This help to reduce the frequency of rendering during scrolling. However, higher values mean that more elements will be present in the page.
/// </summary>
@@ -46,7 +45,7 @@ namespace Radzen
var totalItemsCount = LoadData.HasDelegate ? Count : view.Count();
var top = request.Count;
if(top <= 0)
if (top <= 0)
{
top = PageSize;
}
@@ -78,27 +77,19 @@ namespace Radzen
/// </summary>
[Parameter]
public int PageSize { get; set; } = 5;
#endif
/// <summary>
/// Determines whether virtualization is allowed.
/// </summary>
/// <returns><c>true</c> if virtualization is allowed; otherwise, <c>false</c>.</returns>
internal bool IsVirtualizationAllowed()
{
#if NET5_0_OR_GREATER
return AllowVirtualization;
#else
return false;
#endif
}
internal int GetVirtualizationOverscanCount()
{
#if NET5_0_OR_GREATER
return VirtualizationOverscanCount;
#else
return 0;
#endif
}
/// <summary>
@@ -109,7 +100,6 @@ namespace Radzen
{
return new RenderFragment(builder =>
{
#if NET5_0_OR_GREATER
if (AllowVirtualization)
{
builder.OpenComponent(0, typeof(Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize<object>));
@@ -122,7 +112,7 @@ namespace Radzen
});
}));
if(VirtualizationOverscanCount != default(int))
if (VirtualizationOverscanCount != default(int))
{
builder.AddAttribute(3, "OverscanCount", VirtualizationOverscanCount);
}
@@ -138,12 +128,6 @@ namespace Radzen
RenderItem(builder, item);
}
}
#else
foreach (var item in LoadData.HasDelegate ? Data : View)
{
RenderItem(builder, item);
}
#endif
});
}
@@ -194,6 +178,13 @@ namespace Radzen
}
}
/// <summary>
/// Gets or sets the header template.
/// </summary>
/// <value>The header template.</value>
[Parameter]
public RenderFragment HeaderTemplate { get; set; }
/// <summary>
/// Gets or sets a value indicating whether filtering is allowed. Set to <c>false</c> by default.
/// </summary>
@@ -264,12 +255,19 @@ namespace Radzen
[Parameter]
public string SearchAriaLabel { get; set; } = "Search";
/// <summary>
/// Gets or sets the empty value aria label text.
/// </summary>
/// <value>The empty value aria label text.</value>
[Parameter]
public string EmptyAriaLabel { get; set; } = "Empty";
/// <summary>
/// Gets or sets the selected item changed.
/// </summary>
/// <value>The selected item changed.</value>
[Parameter]
public Action<object> SelectedItemChanged { get; set; }
public EventCallback<object> SelectedItemChanged { get; set; }
/// <summary>
/// The selected items
@@ -283,7 +281,7 @@ namespace Radzen
/// <summary>
/// Selects all.
/// </summary>
protected async System.Threading.Tasks.Task SelectAll()
protected virtual async System.Threading.Tasks.Task SelectAll()
{
if (Disabled)
{
@@ -337,6 +335,8 @@ namespace Radzen
await Change.InvokeAsync(internalValue);
StateHasChanged();
await JSRuntime.InvokeVoidAsync("Radzen.focusElement", GetId());
}
internal bool IsAllSelected()
@@ -441,21 +441,34 @@ namespace Radzen
if (!string.IsNullOrEmpty(ValueProperty))
{
valuePropertyGetter = PropertyAccess.Getter<object, object>(ValueProperty, type);
valuePropertyGetter = GetGetter(ValueProperty, type);
}
if (!string.IsNullOrEmpty(TextProperty))
{
textPropertyGetter = PropertyAccess.Getter<object, object>(TextProperty, type);
textPropertyGetter = GetGetter(TextProperty, type);
}
if (!string.IsNullOrEmpty(DisabledProperty))
{
disabledPropertyGetter = PropertyAccess.Getter<object, object>(DisabledProperty, type);
disabledPropertyGetter = GetGetter(DisabledProperty, type);
}
}
}
Func<object, object> GetGetter(string propertyName, Type type)
{
if (propertyName?.Contains("[") == true)
{
var getter = typeof(PropertyAccess).GetMethod("Getter", [typeof(string), typeof(Type)]);
var getterMethod = getter.MakeGenericMethod([type, typeof(object)]);
return (i) => getterMethod.Invoke(i, [propertyName, type]);
}
return PropertyAccess.Getter<object, object>(propertyName, type);
}
internal Func<object, object> valuePropertyGetter;
internal Func<object, object> textPropertyGetter;
internal Func<object, object> disabledPropertyGetter;
@@ -493,7 +506,6 @@ namespace Radzen
return item;
}
#if NET5_0_OR_GREATER
/// <inheritdoc/>
protected override async Task OnDataChanged()
{
@@ -504,7 +516,6 @@ namespace Radzen
await InvokeAsync(Virtualize.RefreshDataAsync);
}
}
#endif
/// <summary>
/// Gets the popup identifier.
@@ -522,7 +533,7 @@ namespace Radzen
/// Gets the search identifier.
/// </summary>
/// <value>The search identifier.</value>
protected string SearchID
public string SearchID
{
get
{
@@ -624,9 +635,7 @@ namespace Radzen
{
if (IsVirtualizationAllowed())
{
#if NET5_0_OR_GREATER
items = virtualItems;
#endif
items = virtualItems ?? Enumerable.Empty<object>().ToList();
}
else
{
@@ -648,7 +657,11 @@ namespace Radzen
if (!Multiple && !popupOpened && shouldSelectOnChange != false)
{
await OnSelectItem(items.ElementAt(selectedIndex), true);
var itemToSelect = items.ElementAtOrDefault(selectedIndex);
if (itemToSelect != null)
{
await OnSelectItem(itemToSelect, true);
}
}
}
catch (Exception)
@@ -662,8 +675,14 @@ namespace Radzen
if (selectedIndex >= 0 && selectedIndex <= items.Count() - 1)
{
var itemToSelect = items.ElementAtOrDefault(selectedIndex);
await JSRuntime.InvokeAsync<string>("Radzen.setInputValue", search, $"{searchText}".Trim());
await OnSelectItem(items.ElementAt(selectedIndex), true);
if (itemToSelect != null)
{
await OnSelectItem(itemToSelect, true);
}
}
var popupOpened = await JSRuntime.InvokeAsync<bool>("Radzen.popupOpened", PopupID);
@@ -676,7 +695,7 @@ namespace Radzen
{
if (!Multiple)
{
await JSRuntime.InvokeVoidAsync("Radzen.closePopup", PopupID);
await ClosePopup(key);
}
}
}
@@ -688,7 +707,7 @@ namespace Radzen
}
else if (key == "Escape" || key == "Tab")
{
await JSRuntime.InvokeVoidAsync("Radzen.closePopup", PopupID);
await ClosePopup(key);
}
else if (key == "Delete" && AllowClear)
{
@@ -711,12 +730,9 @@ namespace Radzen
Debounce(DebounceFilter, FilterDelay);
}
else
else
{
var filteredItems = GetView(items.AsQueryable(),
args.Key,
StringFilterOperator.StartsWith,
FilterCaseSensitivity.CaseInsensitive)
var filteredItems = Query.Where(TextProperty, args.Key, StringFilterOperator.StartsWith, FilterCaseSensitivity.CaseInsensitive)
.Cast<object>()
.ToList();
@@ -724,7 +740,7 @@ namespace Radzen
if (previousKey != args.Key)
{
previousKey = args.Key;
itemIndex = 0;
itemIndex = -1;
}
itemIndex = itemIndex + 1 >= filteredItems.Count() ? 0 : itemIndex + 1;
@@ -745,13 +761,18 @@ namespace Radzen
selectedIndex = result.Index;
}
await JSRuntime.InvokeVoidAsync("Radzen.selectListItem", list, list, result.Index);
}
}
}
preventKeydown = false;
}
}
internal virtual async Task ClosePopup(string key)
{
await JSRuntime.InvokeVoidAsync("Radzen.closePopup", PopupID);
}
int itemIndex;
string previousKey;
@@ -774,13 +795,11 @@ namespace Radzen
_view = null;
if (IsVirtualizationAllowed())
{
#if NET5_0_OR_GREATER
if (virtualize != null)
{
await virtualize.RefreshDataAsync();
}
await InvokeAsync(() => { StateHasChanged(); });
#endif
}
else
{
@@ -791,7 +810,6 @@ namespace Radzen
{
if (IsVirtualizationAllowed())
{
#if NET5_0_OR_GREATER
if (virtualize != null)
{
await InvokeAsync(virtualize.RefreshDataAsync);
@@ -801,7 +819,6 @@ namespace Radzen
await LoadData.InvokeAsync(await GetLoadDataArgs());
}
await InvokeAsync(() => { StateHasChanged(); });
#endif
}
else
{
@@ -851,7 +868,6 @@ namespace Radzen
/// <returns>LoadDataArgs.</returns>
internal virtual async System.Threading.Tasks.Task<LoadDataArgs> GetLoadDataArgs()
{
#if NET5_0_OR_GREATER
if (AllowVirtualization)
{
return await Task.FromResult(new Radzen.LoadDataArgs() { Skip = 0, Top = PageSize, Filter = searchText });
@@ -860,9 +876,6 @@ namespace Radzen
{
return await Task.FromResult(new Radzen.LoadDataArgs() { Filter = searchText });
}
#else
return await Task.FromResult(new Radzen.LoadDataArgs() { Filter = searchText });
#endif
}
/// <summary>
@@ -897,13 +910,12 @@ namespace Radzen
/// <returns>A Task representing the asynchronous operation.</returns>
public override async Task SetParametersAsync(ParameterView parameters)
{
#if NET5_0_OR_GREATER
var pageSize = parameters.GetValueOrDefault<int>(nameof(PageSize));
if(pageSize != default(int))
if (pageSize != default(int))
{
PageSize = pageSize;
}
#endif
var selectedItemChanged = parameters.DidParameterChange(nameof(SelectedItem), SelectedItem);
if (selectedItemChanged)
{
@@ -941,7 +953,7 @@ namespace Radzen
if (valueAsEnumerable != null)
{
if (!valueAsEnumerable.Cast<object>().SequenceEqual(selectedItems.Select(i => GetItemOrValueFromProperty(i, ValueProperty))))
if (!valueAsEnumerable.Cast<object>().SequenceEqual(selectedItems.Select(i => string.IsNullOrEmpty(ValueProperty) ? i : GetItemOrValueFromProperty(i, ValueProperty))))
{
selectedItems.Clear();
}
@@ -1024,71 +1036,6 @@ namespace Radzen
}
}
IQueryable GetView(IQueryable source, string value, StringFilterOperator? op = null, FilterCaseSensitivity? cs = null)
{
IQueryable result;
if (!string.IsNullOrEmpty(value))
{
var ignoreCase = (cs ?? FilterCaseSensitivity) == FilterCaseSensitivity.CaseInsensitive;
var query = new List<string>();
if (!string.IsNullOrEmpty(TextProperty))
{
query.Add(TextProperty);
}
if (typeof(EnumerableQuery).IsAssignableFrom(source.GetType()))
{
query.Add("ToString()");
}
if (ignoreCase)
{
query.Add("ToLower()");
}
query.Add($"{Enum.GetName(typeof(StringFilterOperator), op ?? FilterOperator)}(@0)");
var search = ignoreCase ? value.ToLower() : value;
if (source.ElementType == typeof(Enum))
{
result = source.Cast<Enum>()
.Where((Func<Enum, bool>)(i =>
{
var v = ignoreCase ? i.GetDisplayDescription().ToLower() : i.GetDisplayDescription();
if (FilterOperator == StringFilterOperator.Contains)
{
return v.Contains(search);
}
else if (FilterOperator == StringFilterOperator.StartsWith)
{
return v.StartsWith(search);
}
else if (FilterOperator == StringFilterOperator.EndsWith)
{
return v.EndsWith(search);
}
return v == search;
})).AsQueryable();
}
else
{
result = source.Where(String.Join(".", query), search);
}
}
else
{
result = source;
}
return result;
}
/// <summary>
/// Gets the view.
/// </summary>
@@ -1099,7 +1046,7 @@ namespace Radzen
{
if (_view == null && Query != null)
{
_view = GetView(Query, searchText);
_view = Query.Where(TextProperty, searchText, FilterOperator, FilterCaseSensitivity);
}
return _view;
@@ -1147,7 +1094,7 @@ namespace Radzen
/// <param name="raiseChange">if set to <c>true</c> [raise change].</param>
public async System.Threading.Tasks.Task SelectItem(object item, bool raiseChange = true)
{
if (disabledPropertyGetter != null && disabledPropertyGetter(item) as bool? == true)
if (disabledPropertyGetter != null && item != null && disabledPropertyGetter(item) as bool? == true)
{
return;
}
@@ -1169,7 +1116,7 @@ namespace Radzen
SetSelectedIndexFromSelectedItem();
SelectedItemChanged?.Invoke(selectedItem);
await SelectedItemChanged.InvokeAsync(selectedItem);
}
else
{
@@ -1267,7 +1214,7 @@ namespace Radzen
}
else
{
selectedItems = selectedItems.AsQueryable().Where($@"!object.Equals(it.{ValueProperty},@0)", value).ToList();
selectedItems = selectedItems.AsQueryable().Where(DynamicLinqCustomTypeProvider.ParsingConfig, $@"!object.Equals(it.{ValueProperty},@0)", value).ToList();
}
}
else
@@ -1302,7 +1249,7 @@ namespace Radzen
}
else
{
SelectedItem = view.AsQueryable().Where($@"{ValueProperty} == @0", value).FirstOrDefault();
SelectedItem = view.AsQueryable().Where(DynamicLinqCustomTypeProvider.ParsingConfig, $@"{ValueProperty} == @0", value).FirstOrDefault();
}
}
else
@@ -1311,8 +1258,6 @@ namespace Radzen
}
SetSelectedIndexFromSelectedItem();
SelectedItemChanged?.Invoke(selectedItem);
}
else
{
@@ -1331,10 +1276,10 @@ namespace Radzen
}
else
{
item = view.AsQueryable().Where($@"{ValueProperty} == @0", v).FirstOrDefault();
item = view.AsQueryable().Where(DynamicLinqCustomTypeProvider.ParsingConfig, $@"{ValueProperty} == @0", v).FirstOrDefault();
}
if (!object.Equals(item, null) && !selectedItems.AsQueryable().Where($@"object.Equals(it.{ValueProperty},@0)", v).Any())
if (!object.Equals(item, null) && !selectedItems.AsQueryable().Where(DynamicLinqCustomTypeProvider.ParsingConfig, $@"object.Equals(it.{ValueProperty},@0)", v).Any())
{
selectedItems.Add(item);
}

View File

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

@@ -35,7 +35,8 @@ namespace Radzen.Blazor
/// </summary>
public static IEnumerable<object> EnumAsKeyValuePair(Type enumType, Func<string, string> translationFunction = null)
{
return Enum.GetValues(enumType).Cast<Enum>().Distinct().Select(val => new { Value = Convert.ToInt32(val), Text = val.GetDisplayDescription(translationFunction) });
Type underlyingType = Enum.GetUnderlyingType(enumType);
return Enum.GetValues(enumType).Cast<Enum>().Distinct().Select(val => new { Value = Convert.ChangeType(val, underlyingType), Text = val.GetDisplayDescription(translationFunction) });
}
/// <summary>

View File

@@ -16,13 +16,6 @@ namespace Radzen
/// </summary>
public class FormComponentWithAutoComplete<T> : FormComponent<T>
{
/// <summary>
/// Gets or sets a value indicating the browser built-in autocomplete is enabled.
/// </summary>
/// <value><c>true</c> if input automatic complete is enabled; otherwise, <c>false</c>.</value>
[Parameter]
public virtual bool AutoComplete { get; set; } = true;
/// <summary>
/// Gets or sets a value indicating the type of built-in autocomplete
/// the browser should use.
@@ -44,8 +37,8 @@ namespace Radzen
/// AutoCompleteType.</value>
public virtual string AutoCompleteAttribute
{
get => !AutoComplete ? DefaultAutoCompleteAttribute :
autoComplete as string ?? AutoCompleteType.GetAutoCompleteValue();
get => Attributes != null && Attributes.ContainsKey("AutoComplete") && $"{Attributes["AutoComplete"]}".ToLower() == "false" ? DefaultAutoCompleteAttribute :
Attributes != null && Attributes.ContainsKey("AutoComplete") ? Attributes["AutoComplete"] as string ?? AutoCompleteType.GetAutoCompleteValue() : AutoCompleteType.GetAutoCompleteValue();
}
/// <summary>
@@ -53,18 +46,11 @@ namespace Radzen
/// </summary>
public virtual string DefaultAutoCompleteAttribute { get; set; } = "off";
object autoComplete;
object ariaAutoComplete;
/// <inheritdoc />
public override async Task SetParametersAsync(ParameterView parameters)
{
parameters = parameters.TryGetValue(nameof(AutoComplete).ToLower(), out autoComplete) ?
ParameterView.FromDictionary(parameters
.ToDictionary().Where(i => i.Key != nameof(AutoComplete).ToLower()).ToDictionary(i => i.Key, i => i.Value)
.ToDictionary(i => i.Key, i => i.Value))
: parameters;
parameters = parameters.TryGetValue("aria-autocomplete", out ariaAutoComplete) ?
ParameterView.FromDictionary(parameters
.ToDictionary().Where(i => i.Key != "aria-autocomplete").ToDictionary(i => i.Key, i => i.Value)
@@ -326,12 +312,11 @@ namespace Radzen
/// <summary> Gets the current placeholder. Returns empty string if this component is inside a RadzenFormField.</summary>
protected string CurrentPlaceholder => FormFieldContext?.AllowFloatingLabel == true ? " " : Placeholder;
#if NET5_0_OR_GREATER
/// <inheritdoc/>
public virtual async ValueTask FocusAsync()
{
await Element.FocusAsync();
}
#endif
}
}

View File

@@ -60,6 +60,10 @@ namespace Radzen.Blazor
/// <returns>RenderFragment.</returns>
RenderFragment RenderTooltip(object data, double marginLeft, double marginTop, double chartHeight);
/// <summary>
/// Renders a tooltip item with the specified data to be displayed in a shared tooltip
/// </summary>
RenderFragment RenderSharedTooltipItem(object category);
/// <summary>
/// Renders the legend item.
/// </summary>
/// <returns>RenderFragment.</returns>

View File

@@ -16,5 +16,11 @@ namespace Radzen.Blazor
/// Gets the values for category.
/// </summary>
IEnumerable<double> ValuesForCategory(double category);
/// <summary>
/// Gets the items for category.
/// </summary>
/// <param name="category"></param>
/// <returns></returns>
IEnumerable<object> ItemsForCategory(double category);
}
}

View File

@@ -18,6 +18,13 @@ namespace Radzen.Blazor
/// </summary>
IEnumerable<double> ValuesForCategory(double category);
/// <summary>
/// Gets the items for category.
/// </summary>
/// <param name="category"></param>
/// <returns></returns>
IEnumerable<object> ItemsForCategory(double category);
/// <summary>
/// Gets the value at the specified index.
/// </summary>

View File

@@ -71,6 +71,12 @@ namespace Radzen.Blazor
/// <param name="appointments">The appointments for this range.</param>
Task<bool> SelectSlot(DateTime start, DateTime end, IEnumerable<AppointmentData> appointments);
/// <summary>
/// Selects the specified month.
/// </summary>
/// <param name="monthStart">The start of the month.</param>
/// <param name="appointments">The appointments for this range.</param>
Task SelectMonth(DateTime monthStart, IEnumerable<AppointmentData> appointments);
/// <summary>
/// Selects the specified more link.
/// </summary>
/// <param name="start">The start.</param>

View File

@@ -88,11 +88,6 @@ namespace Radzen.Blazor
if (Step is IConvertible)
{
step = Convert.ToDouble(Step);
if (step <= 0)
{
throw new ArgumentOutOfRangeException("Step must be greater than zero");
}
}
}
@@ -102,7 +97,7 @@ namespace Radzen.Blazor
end = Math.Ceiling(end / step) * step;
}
if (!Double.IsFinite(Input.Start) && !Double.IsFinite(Input.End))
if (!double.IsFinite(Input.Start) || !double.IsFinite(Input.End))
{
Input.Start = start = 0;
Input.End = end = 2;
@@ -110,7 +105,7 @@ namespace Radzen.Blazor
Round = false;
}
if (!Double.IsFinite(start) && !Double.IsFinite(end))
if (!double.IsFinite(start) || !double.IsFinite(end))
{
Input.Start = start = 0;
Input.End = end = 2;
@@ -118,6 +113,11 @@ namespace Radzen.Blazor
Round = false;
}
if (step <= 0)
{
throw new ArgumentOutOfRangeException("Step must be greater than zero");
}
return (start, end, step);
}
}

View File

@@ -231,9 +231,10 @@ namespace Radzen
/// <param name="orderby">The orderby.</param>
/// <param name="expand">The expand.</param>
/// <param name="select">The select.</param>
/// <param name="apply">The apply.</param>
/// <param name="count">if set to <c>true</c> [count].</param>
/// <returns>Uri.</returns>
public static Uri GetODataUri(this Uri uri, string filter = null, int? top = null, int? skip = null, string orderby = null, string expand = null, string select = null, bool? count = null)
public static Uri GetODataUri(this Uri uri, string filter = null, int? top = null, int? skip = null, string orderby = null, string expand = null, string select = null, string apply = null, bool? count = null)
{
var uriBuilder = new UriBuilder(uri);
var queryString = HttpUtility.ParseQueryString(uriBuilder.Query);
@@ -268,6 +269,11 @@ namespace Radzen
queryString["$select"] = $"{select}";
}
if (!string.IsNullOrEmpty(apply))
{
queryString["$apply"] = $"{apply}";
}
if (count != null)
{
queryString["$count"] = $"{count}".ToLower();

View File

@@ -0,0 +1,189 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading.Tasks;
using System.Web;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Routing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace Radzen
{
/// <summary>
/// Options for the <see cref="QueryStringThemeService" />.
/// </summary>
public class QueryStringThemeServiceOptions
{
/// <summary>
/// Gets or sets the query string parameter for the theme.
/// </summary>
public string ThemeParameter { get; set; } = "theme";
/// <summary>
/// Gets or sets the query string parameter for the wcag compatible color theme.
/// </summary>
public string WcagParameter { get; set; } = "wcag";
/// <summary>
/// Gets or sets the query string parameter for the right to left theme.
/// </summary>
public string RightToLeftParameter { get; set; } = "rtl";
}
/// <summary>
/// Persist the current theme in the query string. Requires <see cref="ThemeService" /> to be registered in the DI container.
/// </summary>
public class QueryStringThemeService : IDisposable
{
private readonly NavigationManager navigationManager;
private readonly ThemeService themeService;
#if NET7_0_OR_GREATER
private readonly IDisposable registration;
#endif
private readonly QueryStringThemeServiceOptions options;
private readonly PropertyInfo hasAttachedJSRuntimeProperty;
/// <summary>
/// Initializes a new instance of the <see cref="QueryStringThemeService" /> class.
/// </summary>
public QueryStringThemeService(NavigationManager navigationManager, ThemeService themeService, IOptions<QueryStringThemeServiceOptions> options)
{
this.navigationManager = navigationManager;
this.themeService = themeService;
this.options = options.Value;
hasAttachedJSRuntimeProperty = navigationManager.GetType().GetProperty("HasAttachedJSRuntime");
var state = GetStateFromQueryString(navigationManager.Uri);
if (state.theme != null && RequiresChange(state))
{
themeService.SetTheme(new ThemeOptions
{
Theme = state.theme,
Wcag = state.wcag,
RightToLeft = state.rightToLeft,
TriggerChange = true
});
}
themeService.ThemeChanged += OnThemeChanged;
#if NET7_0_OR_GREATER
try
{
registration = navigationManager.RegisterLocationChangingHandler(OnLocationChanging);
}
catch (NotSupportedException)
{
// HttpNavigationManager does not support that
}
#endif
}
private bool RequiresChange((string theme, bool? wcag, bool? rightToLeft) state) =>
(state.theme != null && !string.Equals(themeService.Theme, state.theme, StringComparison.OrdinalIgnoreCase)) ||
themeService.Wcag != state.wcag || themeService.RightToLeft != state.rightToLeft;
#if NET7_0_OR_GREATER
private ValueTask OnLocationChanging(LocationChangingContext context)
{
var state = GetStateFromQueryString(context.TargetLocation);
if (RequiresChange(state))
{
context.PreventNavigation();
navigationManager.NavigateTo(GetUriWithStateQueryParameters(context.TargetLocation), replace: true);
}
return ValueTask.CompletedTask;
}
#endif
private (string theme, bool? wcag, bool? rightToLeft) GetStateFromQueryString(string uri)
{
var queryString = uri.Contains('?') ? uri[(uri.IndexOf('?') + 1)..] : string.Empty;
var query = HttpUtility.ParseQueryString(queryString.Contains('#') ? queryString[..queryString.IndexOf('#')] : queryString);
bool? wcag = query.Get(options.WcagParameter) != null ? query.Get(options.WcagParameter) == "true" : null;
bool? rtl = query.Get(options.RightToLeftParameter) != null ? query.Get(options.RightToLeftParameter) == "true" : null;
return (query.Get(options.ThemeParameter), wcag, rtl);
}
private string GetUriWithStateQueryParameters(string uri)
{
var parameters = new Dictionary<string, object>
{
{ options.ThemeParameter, themeService.Theme.ToLowerInvariant() },
};
if (themeService.Wcag.HasValue)
{
parameters.Add(options.WcagParameter, themeService.Wcag.Value ? "true" : "false");
}
if (themeService.RightToLeft.HasValue)
{
parameters.Add(options.RightToLeftParameter, themeService.RightToLeft.Value ? "true" : "false");
}
return navigationManager.GetUriWithQueryParameters(uri, parameters);
}
private void OnThemeChanged()
{
if (hasAttachedJSRuntimeProperty is null || hasAttachedJSRuntimeProperty.GetValue(navigationManager) is true)
{
var state = GetStateFromQueryString(navigationManager.Uri);
navigationManager.NavigateTo(GetUriWithStateQueryParameters(navigationManager.Uri),
forceLoad: state.rightToLeft != themeService.RightToLeft);
}
}
/// <inheritdoc />
public void Dispose()
{
themeService.ThemeChanged -= OnThemeChanged;
#if NET7_0_OR_GREATER
registration?.Dispose();
#endif
}
}
/// <summary>
/// Extension methods to register the <see cref="QueryStringThemeService" />.
/// </summary>
public static class QueryStringThemeServiceCollectionExtensions
{
/// <summary>
/// Adds the <see cref="QueryStringThemeService" /> to the service collection.
/// </summary>
public static IServiceCollection AddRadzenQueryStringThemeService(this IServiceCollection services)
{
services.AddOptions<QueryStringThemeServiceOptions>();
services.AddScoped<QueryStringThemeService>();
return services;
}
/// <summary>
/// Adds the <see cref="QueryStringThemeService" /> to the service collection with the specified condiguration.
/// </summary>
public static IServiceCollection AddRadzenQueryStringThemeService(this IServiceCollection services, Action<QueryStringThemeServiceOptions> configure)
{
services.Configure(configure);
services.AddScoped<QueryStringThemeService>();
return services;
}
}
}

View File

@@ -90,60 +90,6 @@ namespace Radzen
return (IList)genericToList.Invoke(null, new[] { query });
}
/// <summary>
/// Converts to filterstring.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="columns">The columns.</param>
/// <returns>System.String.</returns>
public static string ToFilterString<T>(this IEnumerable<RadzenGridColumn<T>> columns)
{
Func<RadzenGridColumn<T>, bool> canFilter = (c) => c.Filterable && !string.IsNullOrEmpty(c.Type) &&
!(c.FilterValue == null || c.FilterValue as string == string.Empty) && c.GetFilterProperty() != null;
var columnsWithFilter = columns.Where(canFilter).ToList();
if (columnsWithFilter.Any())
{
var gridLogicalFilterOperator = columns.FirstOrDefault()?.Grid?.LogicalFilterOperator;
var gridBooleanOperator = gridLogicalFilterOperator == LogicalFilterOperator.And ? "and" : "or";
var whereList = new List<string>();
foreach (var column in columnsWithFilter)
{
var value = (string)Convert.ChangeType(column.FilterValue, typeof(string));
var secondValue = (string)Convert.ChangeType(column.SecondFilterValue, typeof(string));
var columnType = column.Type;
var columnFormat = column.Format;
if (!string.IsNullOrEmpty(columnType) && !string.IsNullOrEmpty(value))
{
var linqOperator = FilterOperators[column.FilterOperator];
if (linqOperator == null)
{
linqOperator = "==";
}
var booleanOperator = column.LogicalFilterOperator == LogicalFilterOperator.And ? "and" : "or";
if (string.IsNullOrEmpty(secondValue))
{
whereList.Add(GetColumnFilter(column));
}
else
{
whereList.Add($"({GetColumnFilter(column)} {booleanOperator} {GetColumnFilter(column, true)})");
}
}
}
return string.Join($" {gridBooleanOperator} ", whereList.Where(i => !string.IsNullOrEmpty(i)));
}
return "";
}
/// <summary>
/// Converts to filterstring.
/// </summary>
@@ -180,22 +126,40 @@ namespace Radzen
{
if (v != null)
{
value = v is DateTime ? ((DateTime)v).ToString("yyyy-MM-ddTHH:mm:ss.fffZ", CultureInfo.InvariantCulture) : v is DateTimeOffset ? ((DateTimeOffset)v).UtcDateTime.ToString("yyyy-MM-ddTHH:mm:ss.fffZ", CultureInfo.InvariantCulture) : "";
value =
v is DateTime ? ((DateTime) v).ToString("yyyy-MM-ddTHH:mm:ss.fffZ", CultureInfo.InvariantCulture)
: v is DateTimeOffset ? ((DateTimeOffset) v).UtcDateTime.ToString("yyyy-MM-ddTHH:mm:ss.fffZ", CultureInfo.InvariantCulture)
:
#if NET6_0_OR_GREATER
v is DateOnly ? ((DateOnly) v).ToString("yyy-MM-dd", CultureInfo.InvariantCulture) : "";
#else
"";
#endif
}
if (sv != null)
{
secondValue = sv is DateTime ? ((DateTime)sv).ToString("yyyy-MM-ddTHH:mm:ss.fffZ", CultureInfo.InvariantCulture) : sv is DateTimeOffset ? ((DateTimeOffset)sv).UtcDateTime.ToString("yyyy-MM-ddTHH:mm:ss.fffZ", CultureInfo.InvariantCulture) : "";
secondValue =
sv is DateTime ? ((DateTime)sv).ToString("yyyy-MM-ddTHH:mm:ss.fffZ", CultureInfo.InvariantCulture)
: sv is DateTimeOffset ? ((DateTimeOffset)sv).UtcDateTime.ToString("yyyy-MM-ddTHH:mm:ss.fffZ", CultureInfo.InvariantCulture)
:
#if NET6_0_OR_GREATER
sv is DateOnly ? ((DateOnly) sv).ToString("yyy-MM-dd", CultureInfo.InvariantCulture) : "";
#else
"";
#endif
}
}
else if (PropertyAccess.IsEnum(column.FilterPropertyType) || PropertyAccess.IsNullableEnum(column.FilterPropertyType))
{
Type enumType = Enum.GetUnderlyingType(Nullable.GetUnderlyingType(column.FilterPropertyType) ?? column.FilterPropertyType);
if (v != null)
{
value = ((int)v).ToString();
value = Convert.ChangeType(v, enumType).ToString();
}
if (sv != null)
{
secondValue = ((int)sv).ToString();
secondValue = Convert.ChangeType(sv, enumType).ToString();
}
}
else if (IsEnumerable(column.FilterPropertyType) && column.FilterPropertyType != typeof(string))
@@ -293,93 +257,216 @@ namespace Radzen
}
/// <summary>
/// Gets the column filter.
/// Converts a RadzenDataFilter to a Linq-compatibly filter string
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="column">The column.</param>
/// <param name="second">if set to <c>true</c> [second].</param>
/// <returns>System.String.</returns>
private static string GetColumnFilter<T>(RadzenGridColumn<T> column, bool second = false)
/// <typeparam name="T">The type that is being filtered</typeparam>
/// <param name="dataFilter">The RadzenDataFilter component</param>
/// <returns>A Linq-compatible filter string</returns>
public static string ToFilterString<T>(this RadzenDataFilter<T> dataFilter)
{
var property = PropertyAccess.GetProperty(column.GetFilterProperty());
Func<CompositeFilterDescriptor, bool> canFilter = (c) => dataFilter.properties.Where(col => col.Property == c.Property).FirstOrDefault()?.FilterPropertyType != null &&
(!(c.FilterValue == null || c.FilterValue as string == string.Empty)
|| c.FilterOperator == FilterOperator.IsNotNull || c.FilterOperator == FilterOperator.IsNull
|| c.FilterOperator == FilterOperator.IsEmpty || c.FilterOperator == FilterOperator.IsNotEmpty)
&& c.Property != null;
if (property.IndexOf(".") != -1)
if (dataFilter.Filters.Concat(dataFilter.Filters.SelectManyRecursive(i => i.Filters ?? Enumerable.Empty<CompositeFilterDescriptor>())).Where(canFilter).Any())
{
return CompositeFilterToFilterString<T>(dataFilter.Filters, dataFilter, dataFilter.LogicalFilterOperator);
}
return "";
}
/// <summary>
/// Creates a Linq-compatible filter string for a list of CompositeFilterDescriptions
/// </summary>
/// <typeparam name="T">The type that is being filtered</typeparam>
/// <param name="filters">The list if filters</param>
/// <param name="Datafilter">The RadzenDataFilter component</param>
/// <param name="filterOperator">Whether filter elements should be and-ed or or-ed</param>
/// <returns></returns>
private static string CompositeFilterToFilterString<T>(IEnumerable<CompositeFilterDescriptor> filters, RadzenDataFilter<T> Datafilter, LogicalFilterOperator filterOperator)
{
if (filters.Any())
{
var LogicalFilterOperator = filterOperator;
var BooleanOperator = LogicalFilterOperator == LogicalFilterOperator.And ? "&&" : "||";
var whereList = new List<string>();
foreach (var column in filters)
{
if (column.Filters is not null && column.Filters.Any())
{
whereList.Add($"({CompositeFilterToFilterString(column.Filters, Datafilter, column.LogicalFilterOperator)})");
}
if (column.Property is not null)
{
whereList.Add($"{GetColumnFilter(Datafilter, column, Datafilter.FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive)}");
}
}
return string.Join($" {BooleanOperator} ", whereList.Where(i => !string.IsNullOrEmpty(i)));
}
return "";
}
/// <summary>
/// Create a single linq-compatible filter string for one datafilter element (includes sub-lists)
/// </summary>
/// <typeparam name="T">The type that is being filtered</typeparam>
/// <param name="dataFilter">The RadzenDataFilter component</param>
/// <param name="column">The filter elements for which to create the filter string</param>
/// <param name="caseSensitive">Whether filtering is case sensitive or not</param>
/// <returns></returns>
private static string GetColumnFilter<T>(RadzenDataFilter<T> dataFilter, CompositeFilterDescriptor column, bool caseSensitive)
{
var property = column.Property;
if (property.Contains(".", StringComparison.CurrentCulture))
{
property = $"({property})";
}
if (column.Type == "string" && string.IsNullOrEmpty(column.Format))
var columnInfo = dataFilter.properties.Where(c => c.Property == column.Property).FirstOrDefault();
if (columnInfo == null) return "";
var columnType = columnInfo.FilterPropertyType;
var columnFilterOperator = column.FilterOperator;
var linqOperator = LinqFilterOperators[columnFilterOperator.Value];
linqOperator ??= "==";
var value = "";
if (columnType == typeof(string))
{
property = $@"({property} == null ? """" : {property})";
}
value = (string)Convert.ChangeType(column.FilterValue, typeof(string));
value = value?.Replace("\"", "\\\"");
var columnFilterOperator = !second ? column.FilterOperator : column.SecondFilterOperator;
string filterCaseSensitivityOperator = caseSensitive ? ".ToLower()" : "";
var linqOperator = FilterOperators[columnFilterOperator];
if (linqOperator == null)
{
linqOperator = "==";
}
var value = !second ? (string)Convert.ChangeType(column.FilterValue, typeof(string)) :
(string)Convert.ChangeType(column.SecondFilterValue, typeof(string));
value = value?.Replace("\"", "\\\"");
var columnType = column.Type;
var columnFormat = column.Format;
if (columnType == "string")
{
string filterCaseSensitivityOperator = column.Grid.FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive ? ".ToLower()" : "";
if (columnFormat == "date-time" || columnFormat == "date")
{
var dateTimeValue = DateTime.Parse(value, CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.RoundtripKind);
var finalDate = dateTimeValue.TimeOfDay == TimeSpan.Zero ? dateTimeValue.Date : dateTimeValue;
var dateFormat = dateTimeValue.TimeOfDay == TimeSpan.Zero ? "yyyy-MM-dd" : "yyyy-MM-ddTHH:mm:ss.fffZ";
return $@"{property} {linqOperator} DateTime(""{finalDate.ToString(dateFormat, CultureInfo.InvariantCulture)}"")";
}
else if (columnFormat == "time")
{
return $"{property} {linqOperator} duration'{value}'";
}
else if (columnFormat == "uuid")
{
return $@"{property} {linqOperator} Guid(""{value}"")";
}
else if (!string.IsNullOrEmpty(value) && columnFilterOperator == "contains")
if (!string.IsNullOrEmpty(value) && column.FilterOperator == FilterOperator.Contains)
{
return $@"({property} == null ? """" : {property}){filterCaseSensitivityOperator}.Contains(""{value}""{filterCaseSensitivityOperator})";
}
else if (!string.IsNullOrEmpty(value) && columnFilterOperator == "DoesNotContain")
else if (!string.IsNullOrEmpty(value) && columnFilterOperator == FilterOperator.DoesNotContain)
{
return $@"({property} == null ? """" : !{property}){filterCaseSensitivityOperator}.Contains(""{value}""{filterCaseSensitivityOperator})";
}
else if (!string.IsNullOrEmpty(value) && columnFilterOperator == "startswith")
else if (!string.IsNullOrEmpty(value) && columnFilterOperator == FilterOperator.StartsWith)
{
return $@"({property} == null ? """" : {property}){filterCaseSensitivityOperator}.StartsWith(""{value}""{filterCaseSensitivityOperator})";
}
else if (!string.IsNullOrEmpty(value) && columnFilterOperator == "endswith")
else if (!string.IsNullOrEmpty(value) && columnFilterOperator == FilterOperator.EndsWith)
{
return $@"({property} == null ? """" : {property}){filterCaseSensitivityOperator}.EndsWith(""{value}""{filterCaseSensitivityOperator})";
}
else if (!string.IsNullOrEmpty(value) && columnFilterOperator == "eq")
else if (!string.IsNullOrEmpty(value) && columnFilterOperator == FilterOperator.Equals)
{
return $@"({property} == null ? """" : {property}){filterCaseSensitivityOperator} == ""{value}""{filterCaseSensitivityOperator}";
}
else if (!string.IsNullOrEmpty(value) && columnFilterOperator == "ne")
else if (!string.IsNullOrEmpty(value) && columnFilterOperator == FilterOperator.NotEquals)
{
return $@"({property} == null ? """" : {property}){filterCaseSensitivityOperator} != ""{value}""{filterCaseSensitivityOperator}";
}
else if (columnFilterOperator == FilterOperator.IsNull)
{
return property + " == null";
}
else if (columnFilterOperator == FilterOperator.IsEmpty)
{
return property + @" == """"";
}
else if (columnFilterOperator == FilterOperator.IsNotEmpty)
{
return property + @" != """"";
}
else if (columnFilterOperator == FilterOperator.IsNotNull)
{
return property + @" != null";
}
}
else if (columnType == "number" || columnType == "integer")
else if (PropertyAccess.IsNumeric(columnType))
{
return $"{property} {linqOperator} {value}";
value = (string)Convert.ChangeType(column.FilterValue, typeof(string));
if (columnFilterOperator == FilterOperator.IsNull || columnFilterOperator == FilterOperator.IsNotNull)
{
return $"{property} {linqOperator} null";
}
else if (columnFilterOperator == FilterOperator.IsEmpty || columnFilterOperator == FilterOperator.IsNotEmpty)
{
return $@"{property} {linqOperator} """"";
}
else
{
return $"{property} {linqOperator} {value}";
}
}
else if (columnType == "boolean")
else if (columnType == typeof(bool) || columnType == typeof(bool?))
{
return $"{property} == {value}";
value = (string)Convert.ChangeType(column.FilterValue, typeof(string));
return $"{property} {linqOperator} {(columnFilterOperator == FilterOperator.IsNull || columnFilterOperator == FilterOperator.IsNotNull ? "null" : value)}";
}
else if (PropertyAccess.IsDate(columnType))
{
var v = column.FilterValue;
if (v != null)
{
value = $@"DateTime(""{(v is DateTime time ? time.ToString("yyyy-MM-ddTHH:mm:ss.fffZ", CultureInfo.InvariantCulture) : v is DateTimeOffset offset ? offset.UtcDateTime.ToString("yyyy-MM-ddTHH:mm:ss.fffZ", CultureInfo.InvariantCulture) : "")}"")";
}
}
else if (PropertyAccess.IsEnum(columnType) || PropertyAccess.IsNullableEnum(columnType))
{
var v = column.FilterValue;
if (v != null)
{
value = ((int)v).ToString();
}
}
else if (IsEnumerable(columnType) && columnType != typeof(string))
{
var v = column.FilterValue;
var enumerableValue = ((IEnumerable)(v ?? Enumerable.Empty<object>())).AsQueryable();
string baseType = columnType.GetGenericArguments().Count() == 1 ? columnType.GetGenericArguments()[0].Name : "";
var enumerableValueAsString = "new " + baseType + "[]{" + String.Join(",",
(enumerableValue.ElementType == typeof(string) ? enumerableValue.Cast<string>().Select(i => $@"""{i}""").Cast<object>() : enumerableValue.Cast<object>())) + "}";
if (enumerableValue?.Any() == true)
{
if (property.Contains("."))
{
property = $"({property})";
}
if (columnFilterOperator == FilterOperator.Contains || columnFilterOperator == FilterOperator.DoesNotContain)
{
return $@"{(columnFilterOperator == FilterOperator.DoesNotContain ? "!" : "")}({enumerableValueAsString}).Contains({property})";
}
else if (columnFilterOperator == FilterOperator.In || columnFilterOperator == FilterOperator.NotIn)
{
return $@"({property}).{(columnFilterOperator == FilterOperator.NotIn ? "Except" : "Intersect")}({enumerableValueAsString}).Any()";
}
}
}
else
{
value = (string)Convert.ChangeType(column.FilterValue, typeof(string), CultureInfo.InvariantCulture);
}
if (!string.IsNullOrEmpty(value) || column.FilterOperator == FilterOperator.IsNotNull
|| column.FilterOperator == FilterOperator.IsNull
|| column.FilterOperator == FilterOperator.IsEmpty
|| column.FilterOperator == FilterOperator.IsNotEmpty)
{
return $"({property} {linqOperator} {(columnFilterOperator == FilterOperator.IsNull || columnFilterOperator == FilterOperator.IsNotNull ? "null" : value)})";
}
return "";
@@ -396,13 +483,14 @@ namespace Radzen
private static string GetColumnFilter<T>(RadzenDataGridColumn<T> column, string value, bool second = false)
{
var property = PropertyAccess.GetProperty(column.GetFilterProperty());
var propertyType = !string.IsNullOrEmpty(property) ? PropertyAccess.GetPropertyType(typeof(T), property) : null;
if (property.IndexOf(".") != -1)
{
property = $"({property})";
}
bool hasNp = property.Contains("np(");
string npProperty = hasNp ? property : $@"np({property})";
string npProperty = hasNp ? property : (propertyType != null ? Nullable.GetUnderlyingType(propertyType) != null : true) ? $@"np({property})" : property;
var columnFilterOperator = !second ? column.GetFilterOperator() : column.GetSecondFilterOperator();
@@ -416,12 +504,20 @@ namespace Radzen
{
linqOperator = "==";
}
bool isDateOnly = false;
#if NET6_0_OR_GREATER
if (column.FilterPropertyType == typeof(DateOnly) || column.FilterPropertyType == typeof(DateOnly?))
{
isDateOnly = true;
}
#endif
if (column.FilterPropertyType == typeof(string))
{
string filterCaseSensitivityOperator = column.Grid.FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive ? ".ToLower()" : "";
value = value?.Replace("\"", "\\\"");
if (!string.IsNullOrEmpty(value) && columnFilterOperator == FilterOperator.Contains)
{
return $@"({property} == null ? """" : {property}){filterCaseSensitivityOperator}.Contains(""{value}""{filterCaseSensitivityOperator})";
@@ -481,7 +577,7 @@ namespace Radzen
else if (column.FilterPropertyType == typeof(DateTime) ||
column.FilterPropertyType == typeof(DateTime?) ||
column.FilterPropertyType == typeof(DateTimeOffset) ||
column.FilterPropertyType == typeof(DateTimeOffset?))
column.FilterPropertyType == typeof(DateTimeOffset?) || isDateOnly)
{
if (columnFilterOperator == FilterOperator.IsNull || columnFilterOperator == FilterOperator.IsNotNull)
{
@@ -496,14 +592,25 @@ namespace Radzen
var dateTimeValue = DateTime.Parse(value, CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.RoundtripKind);
var finalDate = dateTimeValue.TimeOfDay == TimeSpan.Zero ? dateTimeValue.Date : dateTimeValue;
var dateFormat = dateTimeValue.TimeOfDay == TimeSpan.Zero ? "yyyy-MM-dd" : "yyyy-MM-ddTHH:mm:ss.fffZ";
var dateFunction = column.FilterPropertyType == typeof(DateTimeOffset) || column.FilterPropertyType == typeof(DateTimeOffset?) ? "DateTimeOffset" : "DateTime";
string dateFunction = "DateTime"; //fallback to datetime, if it's an offset or dateonly use that
if (column.FilterPropertyType == typeof(DateTimeOffset) || column.FilterPropertyType == typeof(DateTimeOffset?))
dateFunction = "DateTimeOffset";
else
{
#if NET6_0_OR_GREATER
if (column.FilterPropertyType == typeof(DateOnly) || column.FilterPropertyType == typeof(DateOnly?))
dateFunction = "DateOnly";
#endif
}
return $@"{property} {linqOperator} {dateFunction}(""{finalDate.ToString(dateFormat, CultureInfo.InvariantCulture)}"")";
}
}
else if (column.FilterPropertyType == typeof(bool) || column.FilterPropertyType == typeof(bool?))
{
return $"{property} == {value}";
return $"{property} {linqOperator} {(columnFilterOperator == FilterOperator.IsNull || columnFilterOperator == FilterOperator.IsNotNull ? "null" : value)}";
}
else if (column.FilterPropertyType == typeof(Guid) || column.FilterPropertyType == typeof(Guid?))
{
@@ -524,93 +631,6 @@ namespace Radzen
return "";
}
/// <summary>
/// Gets the column o data filter.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="column">The column.</param>
/// <param name="second">if set to <c>true</c> [second].</param>
/// <returns>System.String.</returns>
private static string GetColumnODataFilter<T>(RadzenGridColumn<T> column, bool second = false)
{
var columnType = column.Type;
var columnFormat = column.Format;
var property = column.GetFilterProperty().Replace('.', '/');
var columnFilterOperator = !second ? column.FilterOperator : column.SecondFilterOperator;
var value = !second ? (string)Convert.ChangeType(column.FilterValue, typeof(string)) :
(string)Convert.ChangeType(column.SecondFilterValue, typeof(string));
if (column.Grid.FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive && columnType == "string" && string.IsNullOrEmpty(columnFormat))
{
property = $"tolower({property})";
}
if (columnType == "string")
{
if (columnFormat == "date-time" || columnFormat == "date")
{
return $"{property} {columnFilterOperator} {DateTime.Parse(value, CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.RoundtripKind).ToString("yyyy-MM-ddTHH:mm:ss.fffZ", CultureInfo.InvariantCulture)}";
}
else if (columnFormat == "time")
{
return $"{property} {columnFilterOperator} duration'{value}'";
}
else if (columnFormat == "uuid")
{
return $"{property} {columnFilterOperator} {value}";
}
else if (!string.IsNullOrEmpty(value) && columnFilterOperator == "contains")
{
return column.Grid.FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive ?
$"contains({property}, tolower('{value}'))" :
$"contains({property}, '{value}')";
}
else if (!string.IsNullOrEmpty(value) && columnFilterOperator == "DoesNotContain")
{
return column.Grid.FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive ?
$"not(contains({property}, tolower('{value}')))" :
$"not(contains({property}, '{value}'))";
}
else if (!string.IsNullOrEmpty(value) && columnFilterOperator == "startswith")
{
return column.Grid.FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive ?
$"startswith({property}, tolower('{value}'))" :
$"startswith({property}, '{value}')";
}
else if (!string.IsNullOrEmpty(value) && columnFilterOperator == "endswith")
{
return column.Grid.FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive ?
$"endswith({property}, tolower('{value}'))" :
$"endswith({property}, '{value}')";
}
else if (!string.IsNullOrEmpty(value) && columnFilterOperator == "eq")
{
return column.Grid.FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive ?
$"{property} eq tolower('{value}')" :
$"{property} eq '{value}'";
}
else if (!string.IsNullOrEmpty(value) && columnFilterOperator == "ne")
{
return column.Grid.FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive ?
$"{property} ne tolower('{value}')" :
$"{property} ne '{value}'";
}
}
else if (columnType == "number" || columnType == "integer")
{
return $"{property} {columnFilterOperator} {value}";
}
else if (columnType == "boolean")
{
return $"{property} eq {value.ToLower()}";
}
return "";
}
/// <summary>
/// Gets the column o data filter.
/// </summary>
@@ -627,7 +647,8 @@ namespace Radzen
var value = IsEnumerable(column.FilterPropertyType) && column.FilterPropertyType != typeof(string)
? null
: (string)Convert.ChangeType(filterValue, typeof(string), CultureInfo.InvariantCulture);
: (string)Convert.ChangeType(filterValue is DateTimeOffset ?
((DateTimeOffset)filterValue).UtcDateTime : filterValue, typeof(string), CultureInfo.InvariantCulture);
if (column.Grid.FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive && column.FilterPropertyType == typeof(string))
{
@@ -715,7 +736,14 @@ namespace Radzen
}
else if (PropertyAccess.IsNumeric(column.FilterPropertyType))
{
return $"{property} {odataFilterOperator} {value}";
if (columnFilterOperator == FilterOperator.IsNull || columnFilterOperator == FilterOperator.IsNotNull)
{
return $"{property} {odataFilterOperator} null";
}
else
{
return $"{property} {odataFilterOperator} {value}";
}
}
else if (column.FilterPropertyType == typeof(bool) || column.FilterPropertyType == typeof(bool?))
{
@@ -758,62 +786,6 @@ namespace Radzen
return "";
}
/// <summary>
/// Converts to odatafilterstring.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="columns">The columns.</param>
/// <returns>System.String.</returns>
public static string ToODataFilterString<T>(this IEnumerable<RadzenGridColumn<T>> columns)
{
Func<RadzenGridColumn<T>, bool> canFilter = (c) => c.Filterable && !string.IsNullOrEmpty(c.Type) &&
!(c.FilterValue == null || c.FilterValue as string == string.Empty) && c.GetFilterProperty() != null;
var columnsWithFilter = columns.Where(canFilter).ToList();
if (columnsWithFilter.Any())
{
var gridLogicalFilterOperator = columns.FirstOrDefault()?.Grid?.LogicalFilterOperator;
var gridBooleanOperator = gridLogicalFilterOperator == LogicalFilterOperator.And ? "and" : "or";
var whereList = new List<string>();
foreach (var column in columnsWithFilter)
{
var property = column.GetFilterProperty().Replace('.', '/');
var value = (string)Convert.ChangeType(column.FilterValue, typeof(string));
var secondValue = (string)Convert.ChangeType(column.SecondFilterValue, typeof(string));
var columnType = column.Type;
var columnFormat = column.Format;
if (!string.IsNullOrEmpty(columnType) && !string.IsNullOrEmpty(value))
{
var linqOperator = FilterOperators[column.FilterOperator];
if (linqOperator == null)
{
linqOperator = "==";
}
var booleanOperator = column.LogicalFilterOperator == LogicalFilterOperator.And ? "and" : "or";
if (string.IsNullOrEmpty(secondValue))
{
whereList.Add(GetColumnODataFilter(column));
}
else
{
whereList.Add($"({GetColumnODataFilter(column)} {booleanOperator} {GetColumnODataFilter(column, true)})");
}
}
}
return string.Join($" {gridBooleanOperator} ", whereList.Where(i => !string.IsNullOrEmpty(i)));
}
return "";
}
/// <summary>
/// Converts to odatafilterstring.
/// </summary>
@@ -873,89 +845,6 @@ namespace Radzen
return "";
}
/// <summary>
/// Wheres the specified columns.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source">The source.</param>
/// <param name="columns">The columns.</param>
/// <returns>IQueryable&lt;T&gt;.</returns>
public static IQueryable<T> Where<T>(this IQueryable<T> source, IEnumerable<RadzenGridColumn<T>> columns)
{
Func<RadzenGridColumn<T>, bool> canFilter = (c) => c.Filterable && !string.IsNullOrEmpty(c.Type) &&
!(c.FilterValue == null || c.FilterValue as string == string.Empty) && c.GetFilterProperty() != null;
if (columns.Where(canFilter).Any())
{
var gridLogicalFilterOperator = columns.FirstOrDefault()?.Grid?.LogicalFilterOperator;
var gridBooleanOperator = gridLogicalFilterOperator == LogicalFilterOperator.And ? "and" : "or";
var index = 0;
var whereList = new Dictionary<string, IEnumerable<object>>();
foreach (var column in columns.Where(canFilter))
{
var property = PropertyAccess.GetProperty(column.GetFilterProperty());
if (property.IndexOf(".") != -1)
{
property = $"({property})";
}
if (column.Type == "string" && string.IsNullOrEmpty(column.Format))
{
property = $@"({property} == null ? """" : {property})";
}
string filterCaseSensitivityOperator = column.Type == "string" && string.IsNullOrEmpty(column.Format) &&
column.Grid.FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive ? ".ToLower()" : "";
var comparison = FilterOperators[column.FilterOperator];
var booleanOperator = column.LogicalFilterOperator == LogicalFilterOperator.And ? "and" : "or";
if (column.SecondFilterValue == null)
{
if (comparison == "StartsWith" || comparison == "EndsWith" || comparison == "Contains")
{
whereList.Add($@"{property}{filterCaseSensitivityOperator}.{comparison}(@{index}{filterCaseSensitivityOperator})", new object[] { column.FilterValue });
index++;
}
else if (comparison == "DoesNotContain")
{
whereList.Add($@"!{property}{filterCaseSensitivityOperator}.Contains(@{index}{filterCaseSensitivityOperator})", new object[] { column.FilterValue });
index++;
}
else
{
whereList.Add($@"{property}{filterCaseSensitivityOperator} {comparison} @{index}{filterCaseSensitivityOperator}", new object[] { column.FilterValue });
index++;
}
}
else
{
var firstFilter = comparison == "StartsWith" || comparison == "EndsWith" || comparison == "Contains" ?
$@"{property}{filterCaseSensitivityOperator}.{comparison}(@{index}{filterCaseSensitivityOperator})" :
comparison == "DoesNotContain" ? $@"!{property}{filterCaseSensitivityOperator}.Contains(@{index}{filterCaseSensitivityOperator})" :
$@"{property}{filterCaseSensitivityOperator} {comparison} @{index}{filterCaseSensitivityOperator}";
index++;
var secondComparison = FilterOperators[column.SecondFilterOperator];
var secondFilter = secondComparison == "StartsWith" || secondComparison == "EndsWith" || secondComparison == "Contains" ?
$@"{property}{filterCaseSensitivityOperator}.{secondComparison}(@{index}{filterCaseSensitivityOperator})" :
secondComparison == "DoesNotContain" ? $@"!{property}{filterCaseSensitivityOperator}.Contains(@{index}{filterCaseSensitivityOperator})" :
$@"{property}{filterCaseSensitivityOperator} {secondComparison} @{index}{filterCaseSensitivityOperator}";
index++;
whereList.Add($@"({firstFilter} {booleanOperator} {secondFilter})", new object[] { column.FilterValue, column.SecondFilterValue });
}
}
return source.Where(string.Join($" {gridBooleanOperator} ", whereList.Keys), whereList.Values.SelectMany(i => i.ToArray()).ToArray());
}
return source;
}
/// <summary>
/// Gets if type is IEnumerable.
/// </summary>
@@ -1029,11 +918,25 @@ namespace Radzen
{
if (IsEnumerable(column.FilterPropertyType) && column.FilterPropertyType != typeof(string) && comparison == "Contains")
{
whereList.Add($@"(@{index}).Contains({property})", new object[] { column.GetFilterValue() });
if (column.Property != column.FilterProperty && !string.IsNullOrEmpty(column.FilterProperty))
{
whereList.Add($@"{column.Property}.Any(i => i.{column.FilterProperty}{filterCaseSensitivityOperator}.Contains(@{index}{filterCaseSensitivityOperator}))", new object[] { column.GetFilterValue() });
}
else
{
whereList.Add($@"(@{index}).Contains({property})", new object[] { column.GetFilterValue() });
}
}
else
{
whereList.Add($@"{property}{filterCaseSensitivityOperator}.{comparison}(@{index}{filterCaseSensitivityOperator})", new object[] { column.GetFilterValue() });
if (IsEnumerable(column.FilterPropertyType) && column.FilterPropertyType != typeof(string) && column.Property != column.FilterProperty && !string.IsNullOrEmpty(column.FilterProperty))
{
whereList.Add($@"{column.Property}.Any(i => i.{column.FilterProperty}{filterCaseSensitivityOperator}.{comparison}(@{index}{filterCaseSensitivityOperator}))", new object[] { column.GetFilterValue() });
}
else
{
whereList.Add($@"{property}{filterCaseSensitivityOperator}.{comparison}(@{index}{filterCaseSensitivityOperator})", new object[] { column.GetFilterValue() });
}
}
index++;
@@ -1042,7 +945,14 @@ namespace Radzen
{
if (IsEnumerable(column.FilterPropertyType) && column.FilterPropertyType != typeof(string) && comparison == "DoesNotContain")
{
whereList.Add($@"!(@{index}).Contains({property})", new object[] { column.GetFilterValue() });
if (column.Property != column.FilterProperty && !string.IsNullOrEmpty(column.FilterProperty))
{
whereList.Add($@"!{column.Property}.Any(i => i.{column.FilterProperty}{filterCaseSensitivityOperator}.Contains(@{index}{filterCaseSensitivityOperator}))", new object[] { column.GetFilterValue() });
}
else
{
whereList.Add($@"!(@{index}).Contains({property})", new object[] { column.GetFilterValue() });
}
}
else
{
@@ -1053,19 +963,23 @@ namespace Radzen
}
else if (comparison == "In" || comparison == "NotIn")
{
if (IsEnumerable(column.FilterPropertyType) && column.FilterPropertyType != typeof(string) &&
if (IsEnumerable(column.FilterPropertyType) && column.FilterPropertyType != typeof(string) &&
IsEnumerable(column.PropertyType) && column.PropertyType != typeof(string))
{
whereList.Add($@"{(comparison == "NotIn" ? "!" : "")}{property}.Any(i => i in @{index})", new object[] { column.GetFilterValue() });
index++;
}
else if (IsEnumerable(column.FilterPropertyType) && column.FilterPropertyType != typeof(string) &&
else if (IsEnumerable(column.FilterPropertyType) && column.FilterPropertyType != typeof(string) &&
column.Property != column.FilterProperty && !string.IsNullOrEmpty(column.FilterProperty))
{
whereList.Add($@"{(comparison == "NotIn" ? "!" : "")}{column.Property}.Any(i => i.{column.FilterProperty} in @{index})", new object[] { column.GetFilterValue() });
index++;
}
}
else if (IsEnumerable(column.FilterPropertyType) && column.FilterPropertyType != typeof(string) && column.Property != column.FilterProperty && !string.IsNullOrEmpty(column.FilterProperty))
{
whereList.Add($@"{column.Property}.Any(i => i.{column.FilterProperty}{filterCaseSensitivityOperator} {comparison} @{index}{filterCaseSensitivityOperator})", new object[] { column.GetFilterValue() });
}
else if (!(IsEnumerable(column.FilterPropertyType) && column.FilterPropertyType != typeof(string)))
{
whereList.Add($@"{property}{filterCaseSensitivityOperator} {comparison} @{index}{filterCaseSensitivityOperator}", new object[] { column.GetFilterValue() });
@@ -1109,7 +1023,7 @@ namespace Radzen
}
return whereList.Keys.Any() ?
source.Where(string.Join($" {gridBooleanOperator} ", whereList.Keys), whereList.Values.SelectMany(i => i.ToArray()).ToArray())
source.Where(DynamicLinqCustomTypeProvider.ParsingConfig, string.Join($" {gridBooleanOperator} ", whereList.Keys), whereList.Values.SelectMany(i => i.ToArray()).ToArray())
: source;
}
@@ -1143,7 +1057,7 @@ namespace Radzen
}
return filterExpressions.Any() ?
source.Where(string.Join($" {dataFilter.LogicalFilterOperator.ToString().ToLower()} ", filterExpressions), filterValues.SelectMany(i => i.ToArray()).ToArray())
source.Where(DynamicLinqCustomTypeProvider.ParsingConfig, string.Join($" {dataFilter.LogicalFilterOperator.ToString().ToLower()} ", filterExpressions), filterValues.SelectMany(i => i.ToArray()).ToArray())
: source;
}
@@ -1169,7 +1083,8 @@ namespace Radzen
else
{
if (filter.Property == null || filter.FilterOperator == null || (filter.FilterValue == null &&
filter.FilterOperator != FilterOperator.IsNull && filter.FilterOperator != FilterOperator.IsNotNull))
filter.FilterOperator != FilterOperator.IsNull && filter.FilterOperator != FilterOperator.IsNotNull &&
filter.FilterOperator != FilterOperator.IsEmpty && filter.FilterOperator != FilterOperator.IsNotEmpty))
{
return;
}
@@ -1265,6 +1180,80 @@ namespace Radzen
}
}
/// <summary>
/// Wheres the specified filters.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="property">The property.</param>
/// <param name="value">The value.</param>
/// <param name="op">The StringFilterOperator.</param>
/// <param name="cs">The FilterCaseSensitivity.</param>
/// <returns>IQueryable&lt;T&gt;.</returns>
public static IQueryable Where(this IQueryable source, string property, string value, StringFilterOperator op, FilterCaseSensitivity cs)
{
IQueryable result;
if (!string.IsNullOrEmpty(value))
{
var ignoreCase = cs == FilterCaseSensitivity.CaseInsensitive;
var query = new List<string>();
if (!string.IsNullOrEmpty(property))
{
query.Add(property);
}
if (typeof(EnumerableQuery).IsAssignableFrom(source.GetType()))
{
query.Add("ToString()");
}
if (ignoreCase)
{
query.Add("ToLower()");
}
query.Add($"{Enum.GetName(typeof(StringFilterOperator), op)}(@0)");
var search = ignoreCase ? value.ToLower() : value;
if (source.ElementType == typeof(Enum))
{
result = source.Cast<Enum>()
.Where((Func<Enum, bool>)(i =>
{
var v = ignoreCase ? i.GetDisplayDescription().ToLower() : i.GetDisplayDescription();
if (op == StringFilterOperator.Contains)
{
return v.Contains(search);
}
else if (op == StringFilterOperator.StartsWith)
{
return v.StartsWith(search);
}
else if (op == StringFilterOperator.EndsWith)
{
return v.EndsWith(search);
}
return v == search;
})).AsQueryable();
}
else
{
result = source.Where(DynamicLinqCustomTypeProvider.ParsingConfig, string.Join(".", query), search);
}
}
else
{
result = source;
}
return result;
}
/// <summary>
/// Converts to OData filter expression.
@@ -1440,7 +1429,7 @@ namespace Radzen
.Where(c => c.Filterable
&& c.FilterPropertyType != null
&& (!(c.GetFilterValue() == null || c.GetFilterValue() as string == string.Empty)
|| c.CanSetFilterValue()
|| !c.CanSetFilterValue()
|| c.HasCustomFilter())
&& c.GetFilterProperty() != null)
.ToList();

View File

@@ -1,105 +0,0 @@
## Radzen Blazor is a set of 70+ free native Blazor UI controls packed with DataGrid, Scheduler, Charts and robust theming including Material design and Fluent UI.
![Radzen Blazor Components](https://raw.githubusercontent.com/radzenhq/radzen-blazor/master/RadzenBlazorDemos/wwwroot/images/radzen-blazor-components.png)
## Why choose Radzen Blazor Components?
### :sparkles: Free
Radzen Blazor Components are open source and free for commercial use. You can install them from [nuget](https://www.nuget.org/packages/Radzen.Blazor) or build your own copy from source.
Paid support is available as part of the [Radzen Professional subscription](https://www.radzen.com/blazor-studio/pricing/).
### :computer: Native
The components are implemented in C# and take full advantage of the Blazor framework. They do not depend on or wrap existing JavaScript frameworks or libraries.
Blazor Server and Blazor WebAssembly are fully supported.
### :seedling: Growing
We add new components and features on a regular basis.
Short development cycle. We release as soon as new stuff is available. No more quarterly releases.
## Support exceeding your expectations
### :speech_balloon: Community Support
Everybody is welcome to visit the [Radzen Community forum](https://forum.radzen.com/). Join the growing community and participate in the discussions!
### :dart: Dedicated Support
The Radzen team monitors the forum threads, but does not guarantee a response to every question. For guaranteed responses you may consider the dedicated support option.
Dedicated support for the Radzen Blazor Components is available as part of the [Radzen Professional subscription](https://www.radzen.com/blazor-studio/pricing/).
Our flagship product [Radzen Blazor Studio](https://www.radzen.com/blazor-studio/) provides tons of productivity features for Blazor developers:
- An industry-leading WYSIWYG Blazor design time canvas
- Scaffolding a complete CRUD applications from a database
- Built-in security - authentication and authorization
- Visual Studio Code and Professional support
- Deployment to IIS and Azure
- Dedicated support with 24 hour guaranteed response time
## Get started with Radzen Blazor Components
### 1. Install
Radzen Blazor Components are distributed as a [Radzen.Blazor nuget package](https://www.nuget.org/packages/Radzen.Blazor). You can add them to your project in one of the following ways
- Install the package from command line by running `dotnet add package Radzen.Blazor`
- Add the project from the Visual Nuget Package Manager
- Manually edit the .csproj file and add a project reference
### 2. Import the namespace
Open the `_Imports.razor` file of your Blazor application and add this line `@using Radzen.Blazor`.
### 3. Include a theme
Radzen Blazor components come with five free themes: Material, Standard, Default, Dark, Software and Humanistic.
To use a theme
1. Pick a theme. The [online demos](https://blazor.radzen.com/colors) allow you to preview the available options via the theme dropdown located in the header. The Material theme is currently selected by default.
1. Include the theme CSS file in your Blazor application. Open `Pages\_Layout.cshtml` (Blazor Server .NET 6), `Pages\_Host.cshtml` (Blazor Server .NET 7) or `wwwroot/index.html` (Blazor WebAssembly) and include a theme CSS file by adding this snippet
```html
<link rel="stylesheet" href="_content/Radzen.Blazor/css/material-base.css">
```
To include a different theme (i.e. Standard) just change the name of the CSS file:
```
<link rel="stylesheet" href="_content/Radzen.Blazor/css/standard-base.css">
```
### 4. Include Radzen.Blazor.js
Open `Pages\_Layout.cshtml` (Blazor Server .NET 6), `Pages\_Host.cshtml` (Blazor Server .NET 7) or `wwwroot/index.html` (Blazor WebAssembly) and include this snippet:
```html
<script src="_content/Radzen.Blazor/Radzen.Blazor.js?v=@(typeof(Radzen.Colors).Assembly.GetName().Version)"></script>
```
### 5. Use a component
Use any Radzen Blazor component by typing its tag name in a Blazor page e.g.
```html
<RadzenButton Text="Hi"></RadzenButton>
```
#### Data-binding a property
```razor
<RadzenButton Text=@text />
<RadzenTextBox @bind-Value=@text />
@code {
string text = "Hi";
}
```
#### Handing events
```razor
<RadzenButton Click="@ButtonClicked" Text="Hi"></RadzenButton>
@code {
void ButtonClicked()
{
}
}
```

View File

@@ -1,46 +1,43 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<NoWarn>BL9993;BL0007;BL0005</NoWarn>
<TargetFrameworks>netstandard2.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
<RazorLangVersion>3.0</RazorLangVersion>
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
<RazorLangVersion>7.0</RazorLangVersion>
<LangVersion>latest</LangVersion>
<OutputType>Library</OutputType>
<IsPackable>true</IsPackable>
<PackageId>Radzen.Blazor</PackageId>
<Product>Radzen.Blazor</Product>
<Version>4.29.2</Version>
<Version>5.2.11</Version>
<Copyright>Radzen Ltd.</Copyright>
<Authors>Radzen Ltd.</Authors>
<Description>Radzen Blazor is a set of 70+ free native Blazor UI controls packed with DataGrid, Scheduler, Charts and robust theming including Material design and Fluent UI.</Description>
<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>
<PackageTags>blazor material design fluent fluentui components datagrid scheduler charts</PackageTags>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageProjectUrl>https://www.radzen.com</PackageProjectUrl>
<PackageIcon>icon.png</PackageIcon>
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<Title>Radzen Components for Blazor</Title>
<RepositoryUrl>https://github.com/radzenhq/radzen-blazor</RepositoryUrl>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DartSassBuilder" Version="1.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components" Condition="'$(TargetFramework)' == 'netstandard2.1'" Version="3.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Condition="'$(TargetFramework)' == 'netstandard2.1'" Version="3.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Components" Condition="'$(TargetFramework)' == 'net5.0'" Version="5.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Condition="'$(TargetFramework)' == 'net5.0'" Version="5.0.0" />
<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="Microsoft.CSharp" Version="4.7.0" Condition="'$(TargetFramework)' == 'netstandard2.1'" />
<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" />
</ItemGroup>
<ItemGroup>
<None Include="LICENSE.txt" Pack="true" PackagePath="" />
<None Include="icon.png" Pack="true" PackagePath="" />
<None Include="README.md" Pack="true" PackagePath="\" />
<None Include="..\README.md" Pack="true" PackagePath="\" />
</ItemGroup>
<ItemGroup>
@@ -74,4 +71,4 @@
<Move SourceFiles="@(CssFile)" DestinationFolder="$(MSBuildProjectDirectory)/wwwroot/css/" />
</Target>
</Project>
</Project>

View File

@@ -24,15 +24,15 @@
id="@($"rz-accordiontab-{items.IndexOf(item)}")" aria-controls="@($"rz-accordiontab-{items.IndexOf(item)}-content")" aria-expanded="true">
@if (IsSelected(i, item))
{
<span class="rz-accordion-toggle-icon rzi rzi-chevron-down"></span>
<span class="notranslate rz-accordion-toggle-icon rzi rzi-chevron-down"></span>
}
else
{
<span class="rz-accordion-toggle-icon rzi rzi-chevron-right"></span>
<span class="notranslate rz-accordion-toggle-icon rzi rzi-chevron-right"></span>
}
@if (!string.IsNullOrEmpty(item.Icon))
{
<i class="rzi" style="@(!string.IsNullOrEmpty(item.IconColor) ? $"color:{item.IconColor}" : null)">@((MarkupString)item.Icon)</i>
<i class="notranslate rzi" style="@(!string.IsNullOrEmpty(item.IconColor) ? $"color:{item.IconColor}" : null)">@((MarkupString)item.Icon)</i>
}
@if (item.Template != null)
{

View File

@@ -5,7 +5,7 @@
<div class="rz-alert-item">
@if (ShowIcon)
{
<RadzenIcon Icon="@GetIcon()" IconColor="@IconColor" Class="rz-alert-icon" />
<RadzenIcon Icon="@GetIcon()" IconColor="@IconColor" class="rz-alert-icon" />
}
<div class="rz-alert-message">
@if (!string.IsNullOrEmpty(Title))

View File

@@ -176,15 +176,15 @@ namespace Radzen.Blazor
switch (AlertStyle)
{
case AlertStyle.Success:
return "check_circle_outline";
return "check_circle";
case AlertStyle.Danger:
return "error_outline";
return "error";
case AlertStyle.Warning:
return "warning_amber";
case AlertStyle.Info:
return "info_outline";
return "info";
default:
return "lightbulb_outline";
return "lightbulb";
}
}

View File

@@ -0,0 +1,2 @@
@inherits RadzenComponent
<RadzenToggleButton Visible=@Visible @attributes=@Attributes Icon=@Icon Change=@OnChange Value=@value Variant="Variant" ButtonStyle="ButtonStyle" ToggleButtonStyle="ToggleButtonStyle" ToggleShade="ToggleShade" />

View File

@@ -0,0 +1,112 @@
using System;
using System.Diagnostics;
using Microsoft.AspNetCore.Components;
namespace Radzen.Blazor
{
/// <summary>
/// Dark or light theme switch. Requires <see cref="ThemeService" /> to be registered in the DI container.
/// </summary>
public partial class RadzenAppearanceToggle : RadzenComponent
{
[Inject]
private ThemeService ThemeService { get; set; }
/// <summary>
/// Gets or sets the switch button variant.
/// </summary>
/// <value>The switch button variant.</value>
[Parameter]
public Variant Variant { get; set; } = Variant.Text;
/// <summary>
/// Gets or sets the switch button style.
/// </summary>
/// <value>The switch button style.</value>
[Parameter]
public ButtonStyle ButtonStyle { get; set; } = ButtonStyle.Base;
/// <summary>
/// Gets or sets the switch button toggled shade.
/// </summary>
/// <value>The switch button toggled shade.</value>
[Parameter]
public Shade ToggleShade { get; set; } = Shade.Default;
/// <summary>
/// Gets or sets the switch button toggled style.
/// </summary>
/// <value>The switch button toggled style.</value>
[Parameter]
public ButtonStyle ToggleButtonStyle { get; set; } = ButtonStyle.Base;
/// <summary>
/// Gets or sets the light theme. Not set by default - the component uses the light version of the current theme.
/// </summary>
[Parameter]
public string LightTheme { get; set; }
/// <summary>
/// Gets or sets the dark theme. Not set by default - the component uses the dark version of the current theme.
/// </summary>
[Parameter]
public string DarkTheme { get; set; }
private string CurrentLightTheme => LightTheme ?? ThemeService.Theme?.ToLowerInvariant() switch
{
"dark" => "default",
"material-dark" => "material",
"fluent-dark" => "fluent",
"material3-dark" => "material3",
"software-dark" => "software",
"humanistic-dark" => "humanistic",
"standard-dark" => "standard",
_ => ThemeService.Theme,
};
private string CurrentDarkTheme => DarkTheme ?? ThemeService.Theme?.ToLowerInvariant() switch
{
"default" => "dark",
"material" => "material-dark",
"fluent" => "fluent-dark",
"material3" => "material3-dark",
"software" => "software-dark",
"humanistic" => "humanistic-dark",
"standard" => "standard-dark",
_ => ThemeService.Theme,
};
private bool value;
/// <inheritdoc />
protected override void OnInitialized()
{
base.OnInitialized();
ThemeService.ThemeChanged += OnThemeChanged;
value = ThemeService.Theme != CurrentDarkTheme;
}
private void OnThemeChanged()
{
value = ThemeService.Theme != CurrentDarkTheme;
StateHasChanged();
}
void OnChange(bool value)
{
ThemeService.SetTheme(value ? CurrentLightTheme : CurrentDarkTheme);
}
private string Icon => value ? "dark_mode" : "light_mode";
/// <inheritdoc />
public override void Dispose()
{
base.Dispose();
ThemeService.ThemeChanged -= OnThemeChanged;
}
}
}

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
@@ -13,7 +12,7 @@
@if (Multiline)
{
<textarea @ref="@search" @attributes="InputAttributes" @onkeydown="@OnFilterKeyPress" value="@Value" disabled="@Disabled"
oninput="@OpenScript()" tabindex="@(Disabled ? "-1" : $"{TabIndex}")" @onchange="@OnChange"
oninput="@OpenScript()" tabindex="@(Disabled ? "-1" : $"{TabIndex}")" @onchange="@OnChange" onfocus="@(OpenOnFocus ? OpenScript() : null)"
aria-autocomplete="list" aria-haspopup="true" autocomplete="off" role="combobox"
class="@InputClassList" onblur="Radzen.activeElement = null"
id="@Name" aria-expanded="true" placeholder="@CurrentPlaceholder" maxlength="@MaxLength" />
@@ -21,7 +20,7 @@
else
{
<input @ref="@search" @attributes="InputAttributes" @onkeydown="@OnFilterKeyPress" value="@Value" disabled="@Disabled"
oninput="@OpenScript()" tabindex="@(Disabled ? "-1" : $"{TabIndex}")" @onchange="@OnChange"
oninput="@OpenScript()" tabindex="@(Disabled ? "-1" : $"{TabIndex}")" @onchange="@OnChange" onfocus="@(OpenOnFocus ? OpenScript() : null)"
aria-autocomplete="list" aria-haspopup="true" autocomplete="off" role="combobox"
class="@InputClassList" onblur="Radzen.activeElement = null"
type="@InputType" id="@Name" aria-expanded="true" placeholder="@CurrentPlaceholder" maxlength="@MaxLength" />

View File

@@ -37,6 +37,13 @@ namespace Radzen.Blazor
[Parameter]
public bool Multiline { get; set; }
/// <summary>
/// Gets or sets a value indicating whether popup should open on focus. Set to <c>false</c> by default.
/// </summary>
/// <value><c>true</c> if popup should open on focus; otherwise, <c>false</c>.</value>
[Parameter]
public bool OpenOnFocus { get; set; }
/// <summary>
/// Gets or sets the Popup height.
/// </summary>
@@ -79,6 +86,7 @@ namespace Radzen.Blazor
/// Gets or sets the underlying max length.
/// </summary>
/// <value>The max length value.</value>
[Parameter]
public long? MaxLength { get; set; }
/// <summary>
@@ -144,6 +152,8 @@ namespace Radzen.Blazor
{
var value = await JSRuntime.InvokeAsync<string>("Radzen.getInputValue", search);
value = $"{value}";
if (value.Length < MinLength)
{
await JSRuntime.InvokeVoidAsync("Radzen.closePopup", PopupID);
@@ -203,7 +213,7 @@ namespace Radzen.Blazor
string textProperty = string.IsNullOrEmpty(TextProperty) ? string.Empty : $".{TextProperty}";
return Query.Where($"o=>o{textProperty}{filterCaseSensitivityOperator}.{Enum.GetName(typeof(StringFilterOperator), FilterOperator)}(@0)",
return Query.Where(DynamicLinqCustomTypeProvider.ParsingConfig, $"o=>o{textProperty}{filterCaseSensitivityOperator}.{Enum.GetName(typeof(StringFilterOperator), FilterOperator)}(@0)",
FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive ? searchText.ToLower() : searchText);
}
@@ -305,7 +315,6 @@ namespace Radzen.Blazor
}
}
#if NET5_0_OR_GREATER
/// <summary>
/// Sets the focus on the input element.
/// </summary>
@@ -313,6 +322,5 @@ namespace Radzen.Blazor
{
await search.FocusAsync();
}
#endif
}
}

View File

@@ -32,10 +32,11 @@
{
var y = category(data) - barHeight / 2 + index * height + index * padding;
var x = value(data);
var itemValue = Value(data);
var itemValue = Value(data);
var radius = Chart.BarOptions.Radius;
var width = Math.Abs(x0 - x);
if (radius > height / 2)
if (radius > height / 2 || radius > width)
{
radius = 0;
}

View File

@@ -25,11 +25,11 @@
{
@if (!string.IsNullOrEmpty(@Icon))
{
<i class="rz-button-icon-left rzi" style="@(!string.IsNullOrEmpty(IconColor) ? $"color:{IconColor}" : null)">@((MarkupString)Icon)</i>
<i class="notranslate rz-button-icon-left rzi" style="@(!string.IsNullOrEmpty(IconColor) ? $"color:{IconColor}" : null)">@((MarkupString)Icon)</i>
}
@if (!string.IsNullOrEmpty(Image))
{
<img class="rz-button-icon-left rzi" src="@Image" alt="@ImageAlternateText" />
<img class="notranslate rz-button-icon-left rzi" src="@Image" alt="@ImageAlternateText" />
}
@if (!string.IsNullOrEmpty(Text))
{

View File

@@ -52,17 +52,36 @@ namespace Radzen.Blazor
/// </summary>
[Parameter]
public EventCallback<LegendClickEventArgs> LegendClick { get; set; }
double? Width { get; set; }
double? Height { get; set; }
/// <summary>
/// Gets the runtime width of the chart.
/// </summary>
protected double? Width { get; set; }
double MarginTop { get; set; }
/// <summary>
/// Gets the runtime height of the chart.
/// </summary>
protected double? Height { get; set; }
double MarginLeft { get; set; }
/// <summary>
/// Gets or sets the top margin of the plot area.
/// </summary>
protected double MarginTop { get; set; }
double MarginRight { get; set; }
/// <summary>
/// Gets or sets the left margin of the plot area.
/// </summary>
protected double MarginLeft { get; set; }
double MarginBottom { get; set; }
/// <summary>
/// Gets or sets the right margin of the plot area.
/// </summary>
protected double MarginRight { get; set; }
/// <summary>
/// Gets or sets the bottom margin of the plot area.
/// </summary>
protected double MarginBottom { get; set; }
/// <summary>
/// Gets or sets the child content. Used to specify series and other configuration.
@@ -93,7 +112,11 @@ namespace Radzen.Blazor
Series.Remove(series);
}
private bool ShouldRenderAxes()
/// <summary>
/// Returns whether the chart should render axes.
/// </summary>
/// <returns></returns>
protected bool ShouldRenderAxes()
{
var pieType = typeof(RadzenPieSeries<>);
var donutType = typeof(RadzenDonutSeries<>);
@@ -111,7 +134,11 @@ namespace Radzen.Blazor
return Series.Count > 0 && Series.All(series => series is IChartBarSeries);
}
private bool UpdateScales()
/// <summary>
/// Updates the scales based on the configuration.
/// </summary>
/// <returns></returns>
protected virtual bool UpdateScales()
{
var valueScale = ValueScale;
var categoryScale = CategoryScale;
@@ -309,7 +336,7 @@ namespace Radzen.Blazor
if (squaredDistance < closestSeriesDistanceSquared)
{
closestSeries = series;
closestSeriesData = seriesData;
closestSeriesData = seriesData;
closestSeriesDistanceSquared = squaredDistance;
}
}
@@ -360,7 +387,7 @@ namespace Radzen.Blazor
if (squaredDistance < closestSeriesDistanceSquared)
{
closestSeries = series;
closestSeriesData = seriesData;
closestSeriesData = seriesData;
closestSeriesDistanceSquared = squaredDistance;
}
}
@@ -370,7 +397,7 @@ namespace Radzen.Blazor
if (closestSeriesData != null)
{
if (closestSeriesData != tooltipData)
{
{
tooltipData = closestSeriesData;
tooltip = closestSeries.RenderTooltip(closestSeriesData, MarginLeft, MarginTop, Height ?? 0);
chartTooltipContainer.Refresh();

View File

@@ -8,7 +8,7 @@ namespace Radzen.Blazor
public partial class RadzenChartTooltipOptions : RadzenChartComponentBase
{
/// <summary>
/// Gets or sets a value indicating whether to show tooltips. By defaults RadzenChart displays tooltips.
/// Gets or sets a value indicating whether to show tooltips. By default RadzenChart displays tooltips.
/// </summary>
/// <value><c>true</c> to display tooltips; otherwise, <c>false</c>.</value>
[Parameter]
@@ -21,6 +21,12 @@ namespace Radzen.Blazor
[Parameter]
public string Style { get; set; }
/// <summary>
/// Enable or disable shared tooltips (one tooltip displaying data for all values for the same category). By default set to false (a separate tooltip is shown for each point in the category).
/// </summary>
[Parameter]
public bool Shared { get; set; }
/// <inheritdoc />
protected override void Initialize()
{

View File

@@ -35,7 +35,7 @@ namespace Radzen.Blazor
.Add("rz-state-active", !object.Equals(Value, false))
.AddDisabled(Disabled);
ClassList IconClassList => ClassList.Create("rz-chkbox-icon")
ClassList IconClassList => ClassList.Create("notranslate rz-chkbox-icon")
.Add("rzi rzi-check", object.Equals(Value, true))
.Add("rzi rzi-times", object.Equals(Value, null));

View File

@@ -28,7 +28,7 @@
<div @ref="@item.Element" id="@item.GetItemId()" @onclick="@(args => SelectItem(item))" @attributes="item.Attributes" class="@item.GetItemCssClass()" style="@item.Style">
<div class="rz-chkbox" @onkeypress="@(args => OnKeyPress(args, SelectItem(item)))" @onkeypress:preventDefault=preventKeyPress @onkeypress:stopPropagation tabindex="@(Disabled || ReadOnly || item.Disabled || item.ReadOnly ? "-1" : $"{TabIndex}")">
<div class="rz-helper-hidden-accessible">
<input type="checkbox" name="@Name" value="@item.Value" disabled="@Disabled" readonly="@ReadOnly" tabindex="-1" aria-label="@(item.Text + " " + item.Value)" aria-checked="@((item.Value as bool? == true).ToString().ToLowerInvariant())">
<input type="checkbox" name="@Name" value="@item.Value" disabled="@Disabled" readonly="@ReadOnly" tabindex="-1" aria-label="@(item.Text + " " + item.Value)" aria-checked="@(IsSelected(item).ToString().ToLowerInvariant())">
</div>
<div class=@ItemClassList(item)>
<span class=@IconClassList(item)></span>

View File

@@ -30,7 +30,7 @@ namespace Radzen.Blazor
.AddDisabled(Disabled || item.Disabled);
ClassList IconClassList(RadzenCheckBoxListItem<TValue> item) => ClassList.Create("rz-chkbox-icon")
.Add("rzi rzi-check", IsSelected(item));
.Add("notranslate rzi rzi-check", IsSelected(item));
/// <summary>
/// Gets or sets the value property.
@@ -108,7 +108,7 @@ namespace Radzen.Blazor
if (value == true)
{
Value = allItems.Select(i => i.Value);
Value = allItems.Where(i => !i.Disabled).Select(i => i.Value);
}
else if (value == false)
{

View File

@@ -8,10 +8,10 @@
<div @ref=@Element style=@Style @onclick=@Toggle @attributes=@Attributes class=@GetCssClass() id=@GetId() tabindex="@(Disabled ? -1 : TabIndex)" @onkeypress="@(args => OnKeyPress(args, Toggle()))" @onkeypress:preventDefault=preventKeyPress @onkeypress:stopPropagation>
@if (Icon != null)
{
<i class="rzi" style="@(!string.IsNullOrEmpty(IconColor) ? $"color:{IconColor}" : null)">@Icon</i>
<i class="notranslate rzi" style="@(!string.IsNullOrEmpty(IconColor) ? $"color:{IconColor}" : null)">@Icon</i>
}
<div class="rz-colorpicker-value" style="background-color: @Value" ></div>
<button type="button" tabindex="-1" class="rz-colorpicker-trigger" disabled=@Disabled @onclick:preventDefault><i class="rzi" /></button>
<button aria-label="@ToggleAriaLabel" type="button" tabindex="-1" class="rz-colorpicker-trigger" disabled=@Disabled @onclick:preventDefault><i class="notranslate rzi" /></button>
<Popup Lazy=@(PopupRenderMode == PopupRenderMode.OnDemand) @ref=@Popup class="rz-colorpicker-popup" Close=@OnClosePopup Open=@Open>
@if (ShowHSV)
{

View File

@@ -18,6 +18,13 @@ namespace Radzen.Blazor
/// </example>
public partial class RadzenColorPicker : FormComponent<string>
{
/// <summary>
/// Gets or sets the toggle popup aria label text.
/// </summary>
/// <value>The toggle popup aria label text.</value>
[Parameter]
public string ToggleAriaLabel { get; set; } = "Toggle";
/// <summary>
/// Gets or sets the open callback.
/// </summary>

View File

@@ -32,8 +32,9 @@
var y = value(data);
var itemValue = Value(data);
var radius = Chart.ColumnOptions.Radius;
var height = Math.Abs(y0 - y);
if (radius > width / 2)
if (radius > width / 2 || radius > height)
{
radius = 0;
}

View File

@@ -78,6 +78,13 @@ namespace Radzen.Blazor
[Parameter]
public CompareOperator Operator { get; set; } = CompareOperator.Equal;
/// <summary>
/// Gets or sets a value indicating whether this <see cref="RadzenCompareValidator"/> should be validated on value change of the specified Component.
/// </summary>
/// <value><c>true</c> if should be validated; otherwise, <c>false</c>.</value>
[Parameter]
public virtual bool ValidateOnComponentValueChange { get; set; } = true;
private int Compare(object componentValue)
{
switch (componentValue)
@@ -94,13 +101,26 @@ namespace Radzen.Blazor
/// <inheritdoc />
public override async Task SetParametersAsync(ParameterView parameters)
{
var shouldValidate = parameters.DidParameterChange(nameof(Value), Value);
var valueChanged = parameters.DidParameterChange(nameof(Value), Value);
await base.SetParametersAsync(parameters);
if (shouldValidate && !firstRender)
if (ValidateOnComponentValueChange && valueChanged && !firstRender && Visible)
{
EditContext.Validate();
var component = Form.FindComponent(Component);
if (component != null && component.FieldIdentifier.FieldName != null)
{
IsValid = Validate(component);
messages?.Clear(component.FieldIdentifier);
if (!IsValid)
{
messages?.Add(component.FieldIdentifier, Text);
}
EditContext?.NotifyValidationStateChanged();
}
}
}

View File

@@ -119,7 +119,7 @@ namespace Radzen
/// Gets the unique identifier.
/// </summary>
/// <returns>Returns the <c>id</c> attribute (if specified) or generates a random one.</returns>
protected string GetId()
protected virtual string GetId()
{
if (Attributes != null && Attributes.TryGetValue("id", out var id) && !string.IsNullOrEmpty(Convert.ToString(@id)))
{

View File

@@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Components;
#if NET5_0_OR_GREATER
namespace Radzen
{
/// <summary>
@@ -74,5 +73,4 @@ namespace Radzen
return (IComponent)Activator.CreateInstance(componentType);
}
}
}
#endif
}

View File

@@ -0,0 +1,73 @@
using Microsoft.AspNetCore.Components;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
namespace Radzen.Blazor
{
/// <summary>
/// A validator component which validates a component value using the data annotations
/// defined on the corresponding model property.
/// Must be placed inside a <see cref="RadzenTemplateForm{TItem}" />
/// </summary>
/// <example>
/// <code>
/// &lt;RadzenTemplateForm TItem="UserModel" Data=@user&gt;
/// &lt;RadzenTextBox style="display: block" Name="Name" @bind-Value=@user.Name /&gt;
/// &lt;RadzenFieldValidator Component="Name" /&gt;
/// &lt;/RadzenTemplateForm&gt;
/// @code {
/// class UserModel
/// {
/// [Required(ErrorMessage = "Name is required.")]
/// [StringLength(50, ErrorMessage = "Name must be less than 50 characters.")]
/// public string Name { get; set; }
/// }
/// UserModel user = new UserModel();
/// }
/// </code>
/// </example>
public class RadzenDataAnnotationValidator : ValidatorBase
{
/// <summary>
/// Gets or sets the message displayed when the component is invalid.
/// The message is generated from the data annotation attributes applied to the model property.
/// </summary>
[Parameter]
public override string Text { get; set; }
/// <summary>
/// Gets or sets the separator used to join multiple validation messages.
/// </summary>
[Parameter]
public string MessageSeparator { get; set; } = " and ";
/// <inheritdoc />
protected override bool Validate(IRadzenFormComponent component)
{
var validationResults = new List<ValidationResult>();
var model = component.FieldIdentifier.Model;
var getter = PropertyAccess.Getter<object>(model, component.FieldIdentifier.FieldName);
var value = getter(model);
var validationContext = new ValidationContext(model)
{
MemberName = component.FieldIdentifier.FieldName
};
var isValid = Validator.TryValidateProperty(value, validationContext, validationResults);
if (!isValid)
{
Text = string.Join(MessageSeparator, validationResults.Select(vr => vr.ErrorMessage));
}
return isValid;
}
}
}

View File

@@ -18,7 +18,7 @@
<RadzenSelectBarItem Text="@OrOperatorText" Value="LogicalFilterOperator.Or" title="@OrOperatorText" />
</Items>
</RadzenSelectBar>
<RadzenButton title="@ClearFilterText" class="rz-datafilter-item-clear rz-datafilter-all-items-clear" Icon="clear" Click="@(args => ClearFilters())" Visible=@(Filters.Any()) Variant="Variant.Text" Size="ButtonSize.Small" ButtonStyle="ButtonStyle.Dark" />
<RadzenButton title="@ClearFilterText" class="rz-datafilter-item-clear rz-datafilter-all-items-clear" Icon="clear" Click="@(args => ClearFilters())" Visible=@(Filters.Any()) Variant="Variant.Text" Size="ButtonSize.Small" ButtonStyle="ButtonStyle.Base" />
<ul class="rz-datafilter-group">
@foreach(var filter in Filters)
@@ -28,7 +28,7 @@
</li>
}
<li class="rz-datafilter-item rz-datafilter-bar">
<RadzenSplitButton Icon="add" Click="@(args => AddFilter(args?.Value == "group"))" Size="ButtonSize.Small" Variant="Variant.Flat" ButtonStyle="ButtonStyle.Primary" Shade="Shade.Lighter">
<RadzenSplitButton Icon="add" Click="@(args => AddFilter(args?.Value == "group"))" Size="ButtonSize.Small" Variant="Variant.Flat" ButtonStyle="ButtonStyle.Base">
<RadzenSplitButtonItem Icon="add" Text="@AddFilterText" />
<RadzenSplitButtonItem Icon="playlist_add" Value="group" Text="@AddFilterGroupText" />
</RadzenSplitButton>

View File

@@ -10,6 +10,9 @@ namespace Radzen.Blazor
/// RadzenDataFilter component.
/// </summary>
/// <typeparam name="TItem">The type of the item.</typeparam>
#if NET6_0_OR_GREATER
[CascadingTypeParameter(nameof(TItem))]
#endif
public partial class RadzenDataFilter<TItem> : RadzenComponent
{
/// <inheritdoc />
@@ -224,6 +227,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>

View File

@@ -299,10 +299,10 @@ namespace Radzen.Blazor
if (PropertyAccess.IsNullableEnum(FilterPropertyType))
return new FilterOperator[] { FilterOperator.Equals, FilterOperator.NotEquals, FilterOperator.IsNull, FilterOperator.IsNotNull };
if ((typeof(IEnumerable).IsAssignableFrom(FilterPropertyType) || typeof(IEnumerable<>).IsAssignableFrom(FilterPropertyType))
if ((typeof(IEnumerable).IsAssignableFrom(FilterPropertyType) || typeof(IEnumerable<>).IsAssignableFrom(FilterPropertyType))
&& FilterPropertyType != typeof(string))
{
var operators = new FilterOperator[]
var operators = new FilterOperator[]
{
FilterOperator.Contains,
FilterOperator.DoesNotContain,
@@ -344,6 +344,10 @@ namespace Radzen.Blazor
return DataFilter?.ContainsText;
case FilterOperator.DoesNotContain:
return DataFilter?.DoesNotContainText;
case FilterOperator.In:
return DataFilter?.InText;
case FilterOperator.NotIn:
return DataFilter?.NotInText;
case FilterOperator.EndsWith:
return DataFilter?.EndsWithText;
case FilterOperator.Equals:

View File

@@ -1,4 +1,3 @@
@using System.Linq.Dynamic.Core
@using Microsoft.JSInterop
@using Microsoft.AspNetCore.Components.Forms
@using Radzen
@@ -37,8 +36,8 @@
{
<div class="rz-group-header-item">
<span class="rz-group-header-item-title">@gd.GetTitle()</span>
<a aria-label="@RemoveGroupArialLabel" @onclick:preventDefault="true" @onclick=@(args => RemoveGroupAsync(gd)) role="button" class="rz-dialog-titlebar-icon rz-dialog-titlebar-close">
<span class="rzi rzi-times"></span>
<a id="@(GetId() + "rg")" aria-label="@RemoveGroupAriaLabel" @onclick:preventDefault="true" @onclick=@(args => RemoveGroupAsync(gd)) role="button" class="rz-dialog-titlebar-icon rz-dialog-titlebar-close">
<span class="notranslate rzi rzi-times"></span>
</a>
</div>
}
@@ -54,7 +53,7 @@
{
<div class="rz-column-picker">
<RadzenDropDown SelectAllText="@AllColumnsText" AllowSelectAll="@AllowPickAllColumns"
MaxSelectedLabels="@ColumnsPickerMaxSelectedLabels" InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", SelectVisibleColumnsArialLabel }})"
MaxSelectedLabels="@ColumnsPickerMaxSelectedLabels" InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", SelectVisibleColumnsAriaLabel }})"
SelectedItemsText="@ColumnsShowingText" Change=@ToggleColumns
@bind-Value="@selectedColumns" FilterCaseSensitivity=FilterCaseSensitivity.CaseInsensitive
Multiple="true" AllowFiltering="@ColumnsPickerAllowFiltering"
@@ -84,10 +83,15 @@
<div class="rz-data-grid-data" tabindex="-1">
<table class="rz-grid-table rz-grid-table-fixed @(AllowAlternatingRows ? "rz-grid-table-striped" : "") @(allColumns.Any(c => c.Parent != null) ? "rz-grid-table-composite" : "") @(getGridLinesCSSClass())">
@if(allColumns.All(c => c.Parent == null))
{
<colgroup>
@foreach (var g in Groups)
@if (ShowGroupExpandColumn)
{
<col>
@foreach (var g in Groups)
{
<col>
}
}
@if (Template != null && ShowExpandColumn)
{
@@ -98,6 +102,7 @@
<col id=@(getColumnUniqueId(visibleColumns.IndexOf(column)) + "-col") style="@column.GetStyle(false, false, true)">
}
</colgroup>
}
<thead>
@for (var i = 0; i < deepestChildColumnLevel + 1; i++)
{
@@ -141,7 +146,7 @@
}
</tr>
}
@if (AllowFiltering && (FilterMode == FilterMode.Simple || FilterMode == FilterMode.SimpleWithMenu) && columns.Where(column => column.Filterable && (!string.IsNullOrEmpty(column.GetFilterProperty()) || column.FilterTemplate != null)).Any())
@if (AllowFiltering && (visibleColumns.Any(c => c.FilterMode == FilterMode.Simple || c.FilterMode == FilterMode.SimpleWithMenu) || FilterMode == FilterMode.Simple || FilterMode == FilterMode.SimpleWithMenu) && columns.Where(column => column.Filterable && (!string.IsNullOrEmpty(column.GetFilterProperty()) || column.FilterTemplate != null)).Any())
{
<tr @onkeydown:stopPropagation>
@if (ShowGroupExpandColumn)
@@ -161,6 +166,7 @@
}
@foreach (var column in visibleColumns)
{
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))
{
@@ -175,25 +181,25 @@
<span class="rz-cell-filter-label" style="height:35px; width:100%;" onclick="event.preventDefault()">
@if (PropertyAccess.IsDate(column.FilterPropertyType))
{
if (FilterMode == FilterMode.Simple)
if (filterMode == FilterMode.Simple)
{
<button class="rz-button rz-button-md rz-button-icon-only rz-variant-flat rz-light" onclick="@($"Radzen.togglePopup(this.parentNode, '{PopupID}{column.GetFilterProperty()}')")">
<i class="rzi">date_range</i>
<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="rzi rz-cell-filter-clear">close</i>
<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="rzi rz-cell-filter-clear">close</i>
<i @onclick="@((args) => ClearFilter(column))" class="notranslate rzi rz-cell-filter-clear">close</i>
}
<div id="@($"{PopupID}{column.GetFilterProperty()}")" class="rz-overlaypanel"
style="display:none;width:550px;" tabindex="0">
<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">
@@ -265,18 +271,14 @@
</div>
</div>
<RadzenDatePicker TValue="@object" AllowInput=@(AllowFilterDateInput) InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", column.Title + FilterValueArialLabel + column.GetFilterValue() }})"
<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(args.Value); SaveSettings(); })" />
Value="@column.GetFilterValue()" Change="@(args => { column.SetFilterValue(PropertyAccess.IsDateOnly(column.FilterPropertyType) ? PropertyAccess.DateOnlyFromDateTime(args.Value) : args.Value); SaveSettings(); })" />
</div>
<div class="rz-date-filter-buttons">
<button class="rz-button rz-clear-filter" @onclick="@((args) => ClearFilter(column, true))">
@ClearFilterText
</button>
<button class="rz-button rz-apply-filter" @onclick="@((args) => ApplyFilter(column, true))">
@ApplyFilterText
</button>
<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>
@@ -285,21 +287,21 @@
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 + FilterValueArialLabel + column.GetFilterValue() }})"
<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(args.Value); InvokeAsync(() => ApplyFilter(column, true));} })" />
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 = -1, Text = EnumNullFilterText}} : Enumerable.Empty<object>()).Concat(EnumExtensions.EnumAsKeyValuePair(Nullable.GetUnderlyingType(column.FilterPropertyType) ?? column.FilterPropertyType)))
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)
if (filterMode == FilterMode.SimpleWithMenu)
{
<RadzenDataGridFilterMenu Grid="@this" Column="@column" />
}
@@ -313,14 +315,14 @@
}
else
{
if (FilterMode == FilterMode.SimpleWithMenu)
if (filterMode == FilterMode.SimpleWithMenu)
{
<RadzenDataGridFilterMenu Grid="@this" Column="@column" />
}
<input aria-label=@(column.Title + FilterValueArialLabel + 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%;" />
<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="rzi rz-cell-filter-clear" style="position:absolute;right:10px;">close</i>
<i @onclick="@((args) => ClearFilter(column))" class="notranslate rzi rz-cell-filter-clear" style="position:absolute;inset-inline-end:10px;">close</i>
}
}
</span>
@@ -336,17 +338,32 @@
<tbody>
@if (Data != null)
{
@if (!ShowEmptyMessage || Count > 0 && (IsVirtualizationAllowed() ? Data.Any() : true) || LoadData.HasDelegate && Data != null && Data.Any())
@if (!ShowEmptyMessage || Count > 0 && (IsVirtualizationAllowed() ? Count > 0 : true) || LoadData.HasDelegate && Data.Count() > 0)
{
if (columns.Count > 0)
{
@DrawRows(visibleColumns)
}
else
{
<tr class=" rz-datatable-emptymessage-row" @onkeydown:stopPropagation>
<td class="rz-datatable-emptymessage" colspan="@(visibleColumns.Sum(c => c.GetColSpan()) + (Template != null && ShowExpandColumn ? 1 : 0))">
@if (EmptyTemplate != null)
{
@EmptyTemplate
}
else
{
<span style="white-space: normal">@EmptyText</span>
}
</td>
</tr>
}
}
else
{
<tr class=" rz-datatable-emptymessage-row" @onkeydown:stopPropagation>
<td class="rz-datatable-emptymessage" colspan="@(visibleColumns.Sum(c => c.GetColSpan()) + (Template != null && ShowExpandColumn ? 1 : 0))">
<td class="rz-datatable-emptymessage" colspan="@(visibleColumns.Sum(c => c.GetColSpan()) + (Template != null && ShowExpandColumn ? 1 : 0) + Groups.Count)">
@if (EmptyTemplate != null)
{
@EmptyTemplate
@@ -360,12 +377,12 @@
}
}
</tbody>
@if (visibleColumns.Where(c => c.FooterTemplate != null).Any())
@if (allColumns.Where(c => c.Visible && c.FooterTemplate != null).Any())
{
<tfoot class="rz-datatable-tfoot" @onkeydown:stopPropagation>
@for (var i = 0; i < deepestChildColumnLevel + 1; i++)
{
<tr class="">
<tr>
@if (i == 0) // Only add the th elements for the first row
{
@if (ShowGroupExpandColumn)
@@ -417,7 +434,7 @@
}
else
{
<i class="rzi-circle-o-notch"></i>
<i class="notranslate rzi-circle-o-notch"></i>
}
</div>
}
@@ -459,13 +476,13 @@
var cellAttributes = new Dictionary<string, object>(Attributes);
var rowspan = column.GetRowSpan(true);
if(rowspan != 1)
if(rowspan > 1)
{
SetAttribute(cellAttributes, "rowspan", rowspan);
}
var colspan = column.GetColSpan(true);
if(colspan != 1)
if(colspan > 1)
{
SetAttribute(cellAttributes, "colspan", colspan);
}
@@ -476,7 +493,7 @@
SetAttribute(cellAttributes, "style", cellStyle);
}
var cellClass = GetCellCssClass(column, Attributes);
var cellClass = GetCellCssClass(column, Item, Attributes);
if (!string.IsNullOrEmpty(cellClass))
{
SetAttribute(cellAttributes, "class", cellClass);
@@ -510,7 +527,7 @@
<text>
@if (this.AllowCompositeDataCells ? RowIndex == column.GetLevel() : (column.Parent != null && RowIndex == column.GetLevel() || column.Columns == null))
{
<td @attributes="@CellAttributes" @oncontextmenu:preventDefault="@this.CellContextMenu.HasDelegate" @onkeydown:stopPropagation>
<td @attributes="@CellAttributes" @oncontextmenu:preventDefault="@this.CellContextMenu.HasDelegate" @onkeydown:stopPropagation="true">
@if (this.Responsive)
{
<span class="rz-column-title">
@@ -527,7 +544,7 @@
@if (this.LoadChildData.HasDelegate && this.ShowExpandColumn && this.allColumns.IndexOf(column) == 0)
{
<span class="rz-cell-toggle">
<a aria-label="@ExpandChildItemAriaLabel" class="@(getExpandIconCssClass(this, Item))" style="@(getExpandIconStyle(this, Item, rowArgs.Item1.Expandable))" @onclick:preventDefault="true" @onclick="_ => this.ExpandItem(Item)" @onclick:stopPropagation>
<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">
@@ -554,7 +571,7 @@
<span class="rz-cell-data" @attributes="@spanAttributes">
@if (Item != null)
{
@if (this.IsRowInEditMode(Item) && column.EditTemplate != null)
@if ((column.IsInEditMode(column.Property, Item) || this.IsRowInEditMode(Item)) && column.EditTemplate is not null)
{
@column.EditTemplate(Item)
}
@@ -578,7 +595,7 @@
@RenderCell(c, Item, this.CellAttributes(Item, c), rowArgs, RowIndex)
}
}
</text>
</text>
};
}
@@ -594,22 +611,26 @@
return columnStyle;
}
string GetCellCssClass(RadzenDataGridColumn<TItem> column, IReadOnlyDictionary<string, object> Attributes)
string GetCellCssClass(RadzenDataGridColumn<TItem> column, TItem item, IReadOnlyDictionary<string, object> Attributes)
{
var CssClass = column.CssClass + " " + getFrozenColumnClass(column, columns.Where(c => c.GetVisible()).ToList()) + " " + getCompositeCellCSSClass(column);
if (Attributes != null && Attributes.TryGetValue("class", out var @class) && !string.IsNullOrEmpty(Convert.ToString(@class)))
List<string> classes = [
column.CalculatedCssClass?.Invoke(column, item) ?? string.Empty,
column.CssClass,
getFrozenColumnClass(column, columns.Where(c => c.GetVisible()).ToList()),
getCompositeCellCSSClass(column),
];
if (Attributes?.TryGetValue("class", out var attributeClass) is true)
{
return $"{CssClass} {@class}".Trim();
classes.Add(Convert.ToString(attributeClass));
}
return String.IsNullOrWhiteSpace(CssClass) ? null : CssClass;
var result = string.Join(" ", classes.Where(c => !string.IsNullOrWhiteSpace(c)));
return !String.IsNullOrWhiteSpace(result) ? result : null;
}
async Task OnContextMenu(RadzenDataGridColumn<TItem> Column, TItem Item, MouseEventArgs args)
{
#if NET5_0_OR_GREATER
await OnCellContextMenu(new DataGridCellMouseEventArgs<TItem>
{
Data = Item,
@@ -629,25 +650,6 @@
Type = args.Type,
Column = Column
});
#else
await OnCellContextMenu(new DataGridCellMouseEventArgs<TItem>
{
Data = Item,
AltKey = args.AltKey,
Button = args.Button,
Buttons = args.Buttons,
ClientX = args.ClientX,
ClientY = args.ClientY,
CtrlKey = args.CtrlKey,
Detail = args.Detail,
MetaKey = args.MetaKey,
ScreenX = args.ScreenX,
ScreenY = args.ScreenY,
ShiftKey = args.ShiftKey,
Type = args.Type,
Column = Column
});
#endif
}
bool clicking;
@@ -660,7 +662,6 @@
try
{
clicking = true;
#if NET5_0_OR_GREATER
await OnCellClick(new DataGridCellMouseEventArgs<TItem>
{
Data = Item,
@@ -680,26 +681,7 @@
Type = args.Type,
Column = Column
});
#else
await OnCellClick(new DataGridCellMouseEventArgs<TItem>
{
Data = Item,
AltKey = args.AltKey,
Button = args.Button,
Buttons = args.Buttons,
ClientX = args.ClientX,
ClientY = args.ClientY,
CtrlKey = args.CtrlKey,
Detail = args.Detail,
MetaKey = args.MetaKey,
ScreenX = args.ScreenX,
ScreenY = args.ScreenY,
ShiftKey = args.ShiftKey,
Type = args.Type,
Column = Column
});
#endif
#if NET5_0_OR_GREATER
await OnRowClick(new DataGridRowMouseEventArgs<TItem>
{
Data = Item,
@@ -718,24 +700,7 @@
ShiftKey = args.ShiftKey,
Type = args.Type
});
#else
await OnRowClick(new DataGridRowMouseEventArgs<TItem>
{
Data = Item,
AltKey = args.AltKey,
Button = args.Button,
Buttons = args.Buttons,
ClientX = args.ClientX,
ClientY = args.ClientY,
CtrlKey = args.CtrlKey,
Detail = args.Detail,
MetaKey = args.MetaKey,
ScreenX = args.ScreenX,
ScreenY = args.ScreenY,
ShiftKey = args.ShiftKey,
Type = args.Type
});
#endif
}
finally
{
@@ -745,7 +710,6 @@
async Task OnDblClick(RadzenDataGridColumn<TItem> Column, TItem Item, MouseEventArgs args)
{
#if NET5_0_OR_GREATER
await OnCellDblClick(new DataGridCellMouseEventArgs<TItem>
{
Data = Item,
@@ -765,26 +729,7 @@
Type = args.Type,
Column = Column
});
#else
await OnCellDblClick(new DataGridCellMouseEventArgs<TItem>
{
Data = Item,
AltKey = args.AltKey,
Button = args.Button,
Buttons = args.Buttons,
ClientX = args.ClientX,
ClientY = args.ClientY,
CtrlKey = args.CtrlKey,
Detail = args.Detail,
MetaKey = args.MetaKey,
ScreenX = args.ScreenX,
ScreenY = args.ScreenY,
ShiftKey = args.ShiftKey,
Type = args.Type,
Column = Column
});
#endif
#if NET5_0_OR_GREATER
await OnRowDblClick(new DataGridRowMouseEventArgs<TItem>
{
Data = Item,
@@ -803,24 +748,6 @@
ShiftKey = args.ShiftKey,
Type = args.Type
});
#else
await OnRowDblClick(new DataGridRowMouseEventArgs<TItem>
{
Data = Item,
AltKey = args.AltKey,
Button = args.Button,
Buttons = args.Buttons,
ClientX = args.ClientX,
ClientY = args.ClientY,
CtrlKey = args.CtrlKey,
Detail = args.Detail,
MetaKey = args.MetaKey,
ScreenX = args.ScreenX,
ScreenY = args.ScreenY,
ShiftKey = args.ShiftKey,
Type = args.Type
});
#endif
}
static string getExpandIconStyle(RadzenDataGrid<TItem> Grid, TItem Item, bool expandable)

View File

@@ -29,9 +29,11 @@ namespace Radzen.Blazor
/// &lt;/RadzenDataGrid&gt;
/// </code>
/// </example>
#if NET6_0_OR_GREATER
[CascadingTypeParameter(nameof(TItem))]
#endif
public partial class RadzenDataGrid<TItem> : PagedDataBoundComponent<TItem>
{
#if NET5_0_OR_GREATER
/// <summary>
/// Gets or sets a value indicating whether this instance is virtualized.
/// </summary>
@@ -103,7 +105,7 @@ namespace Radzen.Blazor
top = PageSize;
}
var filter = isOData == true ?
var filter = isOData == true ?
allColumns.ToList().ToODataFilterString<TItem>() : allColumns.ToList().ToFilterString<TItem>();
var loadDataArgs = $"{request.StartIndex}|{top}{GetOrderBy()}{filter}";
@@ -131,20 +133,28 @@ namespace Radzen.Blazor
}
var view = AllowPaging ? PagedView : View;
var query = view.AsQueryable().OrderBy(Groups.Any() ? string.Join(',', Groups.Select(g => $"{(typeof(TItem) == typeof(object) ? g.Property : "np(" + g.Property + ")")}")) : "it");
_groupedPagedView = query.GroupByMany(Groups.Any() ? Groups.Select(g => $"{(typeof(TItem) == typeof(object) ? g.Property : "np(" + g.Property + ")")}").ToArray() : new string[] { "it" }).ToList();
var totalItemsCount = await Task.FromResult(_groupedPagedView.Count());
var query = Enumerable.Empty<TItem>().AsQueryable();
var totalItemsCount = 0;
_groupedPagedView = Enumerable.Empty<GroupResult>();
return new Microsoft.AspNetCore.Components.Web.Virtualization.ItemsProviderResult<GroupResult>(_groupedPagedView.Skip(request.StartIndex).Take(top), totalItemsCount);
if (Groups.Any())
{
query = view.AsQueryable().OrderBy(DynamicLinqCustomTypeProvider.ParsingConfig, Groups.Any() ? string.Join(',', Groups.Select(g => $"{(typeof(TItem) == typeof(object) ? g.Property : "np(" + g.Property + ")")}")) : "it");
_groupedPagedView = await Task.FromResult(query.GroupByMany(DynamicLinqCustomTypeProvider.ParsingConfig, Groups.Any() ? Groups.Select(g => $"{(typeof(TItem) == typeof(object) ? g.Property : "np(" + g.Property + ")")}").ToArray() : new string[] { "it" }).ToList());
totalItemsCount = await Task.FromResult(_groupedPagedView.Count());
}
_view = view;
return new Microsoft.AspNetCore.Components.Web.Virtualization.ItemsProviderResult<GroupResult>(_groupedPagedView.Any() ? _groupedPagedView.Skip(request.StartIndex).Take(top) : _groupedPagedView, totalItemsCount);
}
#endif
RenderFragment DrawRows(IList<RadzenDataGridColumn<TItem>> visibleColumns)
{
return new RenderFragment(builder =>
{
#if NET5_0_OR_GREATER
if (AllowVirtualization)
{
if(AllowGrouping && Groups.Any() && !LoadData.HasDelegate)
@@ -172,7 +182,7 @@ namespace Radzen.Blazor
{
builder.OpenComponent(0, typeof(Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize<TItem>));
builder.AddAttribute(1, "ItemsProvider", new Microsoft.AspNetCore.Components.Web.Virtualization.ItemsProviderDelegate<TItem>(LoadItems));
builder.AddAttribute(2, "ChildContent", (RenderFragment<TItem>)((context) =>
{
return (RenderFragment)((b) =>
@@ -210,9 +220,6 @@ namespace Radzen.Blazor
{
DrawGroupOrDataRows(builder, visibleColumns);
}
#else
DrawGroupOrDataRows(builder, visibleColumns);
#endif
});
}
@@ -256,6 +263,14 @@ namespace Radzen.Blazor
}
}
/// <summary>
/// Gets or sets the callback used to load column filter data for DataGrid FilterMode.CheckBoxList filter mode.
/// </summary>
/// <value>The load filter data event callback.</value>
[Parameter]
public EventCallback<DataGridLoadColumnFilterDataEventArgs<TItem>> LoadColumnFilterData { get; set; }
/// <summary>
/// Gets or sets the load child data callback.
/// </summary>
@@ -277,6 +292,13 @@ namespace Radzen.Blazor
[Parameter]
public string ExpandGroupAriaLabel { get; set; } = "Expand group";
/// <summary>
/// Gets or sets the date simple filter toggle aria label text.
/// </summary>
/// <value>The date simple filter toggle aria label text.</value>
[Parameter]
public string FilterToggleAriaLabel { get; set; } = "Toggle";
/// <summary>
/// Gets or sets a value indicating whether DataGrid data cells will follow the header cells structure in composite columns.
/// </summary>
@@ -296,7 +318,7 @@ namespace Radzen.Blazor
public bool Responsive { get; set; }
/// <summary>
/// Allows to define a custom function for enums DisplayAttribute Description property value translation in datagrid
/// Allows to define a custom function for enums DisplayAttribute Description property value translation in datagrid
/// Enum filters.
/// </summary>
[Parameter]
@@ -317,9 +339,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(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(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 v = (AllowPaging && !LoadData.HasDelegate ? query.Skip(skip).Take(PageSize) : query).ToList().AsQueryable();
_groupedPagedView = v.GroupByMany(Groups.Select(g => $"{(typeof(TItem) == typeof(object) ? g.Property : "np(" + g.Property + ")")}").ToArray()).ToList();
_groupedPagedView = v.GroupByMany(DynamicLinqCustomTypeProvider.ParsingConfig, Groups.Select(g => $"{(typeof(TItem) == typeof(object) ? g.Property : "np(" + g.Property + ")")}").ToArray()).ToList();
}
return _groupedPagedView;
}
@@ -327,7 +349,7 @@ namespace Radzen.Blazor
internal async Task RemoveGroupAsync(GroupDescriptor gd)
{
Groups.Remove(gd);
Groups.Remove(gd);
_groupedPagedView = null;
var column = columns.Where(c => c.GetGroupProperty() == gd.Property).FirstOrDefault();
@@ -369,7 +391,7 @@ namespace Radzen.Blazor
// https://stackoverflow.com/questions/25308823/targeting-positionsticky-elements-that-are-currently-in-a-stuck-state
// https://codepen.io/TomAnthony/pen/qBqgErK
// It seemed too complicated, so left + right frozen columns problme has been solved by following css classes:
// - rz-frozen-cell-left all of the "left frozen columns" get this class
// - rz-frozen-cell-left all of the "left frozen columns" get this class
// - rz-frozen-cell-left-end the most right column of the "left frozen columns" get this class to draw the shadow for it
// - rz-frozen-cell-left-inner all of the "left inner frozen columns" get this class
// - rz-frozen-cell-right all of the "right frozen columns" get this class
@@ -461,12 +483,21 @@ namespace Radzen.Blazor
}
}
/// <summary>
/// Gets or sets key down callback.
/// </summary>
/// <value>The key down callback.</value>
[Parameter]
public EventCallback<KeyboardEventArgs> KeyDown { get; set; }
/// <summary>
/// Handles the <see cref="E:KeyDown" /> event.
/// </summary>
/// <param name="args">The <see cref="KeyboardEventArgs"/> instance containing the event data.</param>
protected virtual async Task OnKeyDown(KeyboardEventArgs args)
{
await KeyDown.InvokeAsync(args);
var key = args.Code != null ? args.Code : args.Key;
if (key == "ArrowDown" || key == "ArrowUp" || key == "ArrowLeft" || key == "ArrowRight")
@@ -569,7 +600,7 @@ namespace Radzen.Blazor
/// Gives the grid a custom header, allowing the adding of components to create custom tool bars in addtion to column grouping and column picker
/// </summary>
[Parameter]
public RenderFragment HeaderTemplate { get; set; }
public RenderFragment HeaderTemplate { get; set; }
/// <summary>
/// Gives the grid a custom footer, allowing the adding of components to create custom tool bars or custom pagination
@@ -659,7 +690,7 @@ namespace Radzen.Blazor
columnsList.Add(column);
}
}
else
else
{
if (columnsList.Contains(column))
{
@@ -798,10 +829,11 @@ namespace Radzen.Blazor
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();
@@ -836,15 +868,21 @@ namespace Radzen.Blazor
LogicalFilterOperator = column.GetLogicalFilterOperator()
});
if (FilterMode == FilterMode.CheckBoxList)
{
allColumns.ToList().ForEach(c =>
{
c.ClearFilterValues();
});
}
SaveSettings();
if (LoadData.HasDelegate && IsVirtualizationAllowed())
{
isOData = Data != null && typeof(ODataEnumerable<TItem>).IsAssignableFrom(Data.GetType());
Data = null;
#if NET5_0_OR_GREATER
ResetLoadData();
#endif
}
await InvokeAsync(ReloadInternal);
@@ -897,9 +935,7 @@ namespace Radzen.Blazor
if (LoadData.HasDelegate && IsVirtualizationAllowed())
{
Data = null;
#if NET5_0_OR_GREATER
ResetLoadData();
#endif
}
await InvokeAsync(ReloadInternal);
@@ -951,14 +987,22 @@ namespace Radzen.Blazor
column.ClearFilters();
if (FilterMode == FilterMode.CheckBoxList)
{
allColumns.ToList().ForEach(c =>
{
c.ClearFilterValues();
});
}
skip = 0;
CurrentPage = 0;
SaveSettings();
await FilterCleared.InvokeAsync(new DataGridColumnFilterEventArgs<TItem>()
{
Column = column,
await FilterCleared.InvokeAsync(new DataGridColumnFilterEventArgs<TItem>()
{
Column = column,
FilterValue = column.GetFilterValue(),
SecondFilterValue = column.GetSecondFilterValue(),
FilterOperator = column.GetFilterOperator(),
@@ -969,9 +1013,7 @@ namespace Radzen.Blazor
if (LoadData.HasDelegate && IsVirtualizationAllowed() && shouldReload)
{
Data = null;
#if NET5_0_OR_GREATER
ResetLoadData();
#endif
}
if (closePopup)
@@ -1231,14 +1273,14 @@ namespace Radzen.Blazor
/// <value>The null text.</value>
[Parameter]
public string IsNullText { get; set; } = "Is null";
/// <summary>
/// Gets or sets the is empty text.
/// </summary>
/// <value>The empty text.</value>
[Parameter]
public string IsEmptyText { get; set; } = "Is empty";
/// <summary>
/// Gets or sets the is not empty text.
/// </summary>
@@ -1429,49 +1471,49 @@ namespace Radzen.Blazor
/// </summary>
/// <value>The remove group button aria label text.</value>
[Parameter]
public string RemoveGroupArialLabel { get; set; } = "Remove group";
public string RemoveGroupAriaLabel { get; set; } = "Remove group";
/// <summary>
/// Gets or sets the select visible columns aria label text.
/// </summary>
/// <value>The select visible columns aria label text.</value>
[Parameter]
public string SelectVisibleColumnsArialLabel { get; set; } = "select visible columns";
public string SelectVisibleColumnsAriaLabel { get; set; } = "select visible columns";
/// <summary>
/// Gets or sets the column logical filter value aria label text.
/// </summary>
/// <value>The the column logical filter value aria label text.</value>
[Parameter]
public string LogicalOperatorArialLabel { get; set; } = " logical filter operator ";
public string LogicalOperatorAriaLabel { get; set; } = " logical filter operator ";
/// <summary>
/// Gets or sets the column filter value aria label text.
/// </summary>
/// <value>The the column filter value aria label text.</value>
[Parameter]
public string FilterOperatorArialLabel { get; set; } = " filter operator ";
public string FilterOperatorAriaLabel { get; set; } = " filter operator ";
/// <summary>
/// Gets or sets the column filter value aria label text.
/// </summary>
/// <value>The the column filter value aria label text.</value>
[Parameter]
public string SecondFilterOperatorArialLabel { get; set; } = " second filter operator ";
public string SecondFilterOperatorAriaLabel { get; set; } = " second filter operator ";
/// <summary>
/// Gets or sets the column filter value aria label text.
/// </summary>
/// <value>The the column filter value aria label text.</value>
[Parameter]
public string FilterValueArialLabel { get; set; } = " filter value ";
public string FilterValueAriaLabel { get; set; } = " filter value ";
/// <summary>
/// Gets or sets the column filter value aria label text.
/// </summary>
/// <value>The the column filter value aria label text.</value>
[Parameter]
public string SecondFilterValueArialLabel { get; set; } = " second filter value ";
public string SecondFilterValueAriaLabel { get; set; } = " second filter value ";
/// <summary>
/// Gets or sets a value indicating whether user can pick all columns in column picker.
@@ -1564,6 +1606,21 @@ namespace Radzen.Blazor
{
var actualColumnIndexFrom = columns.IndexOf(columnToReorder);
var actualColumnIndexTo = columns.IndexOf(columnToReorderTo);
var reorderingArgs = new DataGridColumnReorderingEventArgs<TItem>
{
Column = columnToReorder,
ToColumn = columnToReorderTo
};
await ColumnReordering.InvokeAsync(reorderingArgs);
if(reorderingArgs.Cancel)
{
indexOfColumnToReoder = null;
return;
}
columns.Remove(columnToReorder);
columns.Insert(actualColumnIndexTo, columnToReorder);
@@ -1617,6 +1674,13 @@ namespace Radzen.Blazor
[Parameter]
public EventCallback<DataGridColumnResizedEventArgs<TItem>> ColumnResized { get; set; }
/// <summary>
/// Gets or sets the column reordering callback.
/// </summary>
/// <value>The column reordering callback.</value>
[Parameter]
public EventCallback<DataGridColumnReorderingEventArgs<TItem>> ColumnReordering { get; set; }
/// <summary>
/// Gets or sets the column reordered callback.
/// </summary>
@@ -1759,11 +1823,7 @@ namespace Radzen.Blazor
internal bool IsVirtualizationAllowed()
{
#if NET5_0_OR_GREATER
return AllowVirtualization;
#else
return false;
#endif
}
IList<TItem> _value;
@@ -1971,8 +2031,8 @@ namespace Radzen.Blazor
if (resetColumnState)
{
allColumns.ToList().ForEach(c =>
{
allColumns.ToList().ForEach(c =>
{
c.ClearFilters();
c.ResetSortOrder();
c.SetOrderIndex(null);
@@ -1982,11 +2042,6 @@ namespace Radzen.Blazor
selectedColumns = allColumns.Where(c => c.Pickable && c.GetVisible()).ToList();
sorts.Clear();
}
if (!LoadData.HasDelegate)
{
SaveSettings();
}
}
/// <summary>
@@ -1994,9 +2049,7 @@ namespace Radzen.Blazor
/// </summary>
public async override Task Reload()
{
#if NET5_0_OR_GREATER
ResetLoadData();
#endif
await ReloadInternal();
}
@@ -2009,7 +2062,7 @@ namespace Radzen.Blazor
{
Count = 1;
}
#if NET5_0_OR_GREATER
if (AllowVirtualization)
{
if (!LoadData.HasDelegate)
@@ -2029,7 +2082,7 @@ namespace Radzen.Blazor
Data = null;
}
}
#endif
if (!IsVirtualizationAllowed())
{
await InvokeLoadData(skip, PageSize);
@@ -2043,7 +2096,6 @@ namespace Radzen.Blazor
}
else
{
#if NET5_0_OR_GREATER
if (AllowVirtualization)
{
if (virtualize != null)
@@ -2056,7 +2108,6 @@ namespace Radzen.Blazor
await groupVirtualize.RefreshDataAsync();
}
}
#endif
}
if (LoadData.HasDelegate && View.Count() == 0 && Count > 0)
@@ -2143,7 +2194,7 @@ namespace Radzen.Blazor
internal string ExpandedGroupItemStyle(RadzenDataGridGroupRow<TItem> item, bool? expandedOnLoad)
{
return collapsedGroupItems.Keys.Contains(item) || expandedOnLoad == false ? "rz-row-toggler rzi-grid-sort rzi-chevron-circle-right" : "rz-row-toggler rzi-grid-sort rzi-chevron-circle-down";
return collapsedGroupItems.Keys.Contains(item) || expandedOnLoad == false ? "notranslate rz-row-toggler rzi-grid-sort rzi-chevron-circle-right" : "rz-row-toggler rzi-grid-sort rzi-chevron-circle-down";
}
internal bool IsGroupItemExpanded(RadzenDataGridGroupRow<TItem> item)
@@ -2154,7 +2205,7 @@ namespace Radzen.Blazor
/// <summary>
/// Gets or sets a value indicating whether all groups should be expanded when DataGrid is grouped.
/// </summary>
/// <value><c>true</c> if groups are expanded; otherwise, <c>false</c>.</value>
/// <value><c>true</c> if groups are expanded; otherwise, <c>false</c>.</value>
[Parameter]
public bool? AllGroupsExpanded { get; set; }
@@ -2215,7 +2266,7 @@ namespace Radzen.Blazor
internal string ExpandedItemStyle(TItem item)
{
return expandedItems.Keys.Any(i => ItemEquals(i, item)) ? "rz-row-toggler rzi-chevron-circle-down" : "rz-row-toggler rzi-chevron-circle-right";
return expandedItems.Keys.Any(i => ItemEquals(i, item)) ? "notranslate rz-row-toggler rzi-chevron-circle-down" : "rz-row-toggler rzi-chevron-circle-right";
}
internal Dictionary<TItem, bool> selectedItems = new Dictionary<TItem, bool>();
@@ -2227,9 +2278,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)
{
@@ -2433,6 +2484,19 @@ namespace Radzen.Blazor
await InvokeAsync(StateHasChanged);
}
/// <summary>
/// Collapse all rows that are expanded
/// </summary>
/// <returns></returns>
public async System.Threading.Tasks.Task CollapseAll()
{
foreach(var item in expandedItems.Keys.ToList())
{
await CollapseItem(item);
}
}
/// <summary>
/// Collapse a range of rows.
/// </summary>
@@ -2442,23 +2506,25 @@ namespace Radzen.Blazor
// Only allow the functionality when multiple row expand is allowed
if (this.ExpandMode != DataGridExpandMode.Multiple) return;
foreach (TItem item in items)
foreach (TItem item in items.Where(x=> expandedItems.Keys.Any(i => ItemEquals(i, x))))
{
if (expandedItems.Keys.Any(i => ItemEquals(i, item)))
{
expandedItems.Remove(item);
await RowCollapse.InvokeAsync(item);
if (childData.ContainsKey(item))
{
childData.Remove(item);
_view = null;
}
}
await CollapseItem(item);
}
await InvokeAsync(StateHasChanged);
}
private async Task CollapseItem(TItem item)
{
expandedItems.Remove(item);
await RowCollapse.InvokeAsync(item);
if (childData.ContainsKey(item))
{
childData.Remove(item);
_view = null;
}
}
internal async System.Threading.Tasks.Task ExpandItem(TItem item)
{
if (ExpandMode == DataGridExpandMode.Single && expandedItems.Keys.Any() && !LoadChildData.HasDelegate)
@@ -2493,14 +2559,7 @@ namespace Radzen.Blazor
}
else
{
expandedItems.Remove(item);
await RowCollapse.InvokeAsync(item);
if (childData.ContainsKey(item))
{
childData.Remove(item);
_view = null;
}
await CollapseItem(item);
}
await InvokeAsync(StateHasChanged);
@@ -2776,12 +2835,12 @@ namespace Radzen.Blazor
{
if (editedItems.Keys.Any(i => ItemEquals(i, item)))
{
var editContext = editContexts[item];
var editContext = editContexts.FirstOrDefault(i => ItemEquals(i.Key, item)).Value;
if (editContext.Validate())
if (editContext?.Validate() == true)
{
editedItems.Remove(item);
editContexts.Remove(item);
editedItems = editedItems.Where(i => !ItemEquals(i.Key, item)).ToDictionary(i => i.Key, i => i.Value);
editContexts = editContexts.Where(i => !ItemEquals(i.Key, item)).ToDictionary(i => i.Key, i => i.Value);
if (itemsToInsert.Contains(item))
{
@@ -2817,7 +2876,6 @@ namespace Radzen.Blazor
}
else
{
#if NET5_0_OR_GREATER
itemsToInsert.Remove(item);
if(virtualize != null)
{
@@ -2828,7 +2886,6 @@ namespace Radzen.Blazor
{
groupVirtualize.RefreshDataAsync();
}
#endif
}
}
else
@@ -2888,7 +2945,6 @@ namespace Radzen.Blazor
}
else
{
#if NET5_0_OR_GREATER
if(virtualize != null)
{
await virtualize.RefreshDataAsync();
@@ -2898,7 +2954,6 @@ namespace Radzen.Blazor
{
await groupVirtualize.RefreshDataAsync();
}
#endif
}
await EditRowInternal(item);
@@ -3021,8 +3076,8 @@ namespace Radzen.Blazor
/// Gets or sets the group descriptors.
/// </summary>
/// <value>The groups.</value>
public ObservableCollection<GroupDescriptor> Groups
{
public ObservableCollection<GroupDescriptor> Groups
{
get
{
if (groups == null)
@@ -3049,13 +3104,7 @@ namespace Radzen.Blazor
var functionName = $"Radzen['{getColumnUniqueId(indexOfColumnToReoder.Value)}end']";
await JSRuntime.InvokeVoidAsync("eval", $"{functionName} && {functionName}()");
RadzenDataGridColumn<TItem> column;
column = columns.Where(c => c.GetVisible()).ElementAtOrDefault(indexOfColumnToReoder.Value);
//may be its a child column
if (column == null)
column = allColumns.Where(c => c.GetVisible()).ElementAtOrDefault(indexOfColumnToReoder.Value);
var column = allColumns.Where(c => c.Visible).ToList().ElementAtOrDefault(indexOfColumnToReoder.Value);
if (column != null && column.Groupable && !string.IsNullOrEmpty(column.GetGroupProperty()))
{
@@ -3076,7 +3125,7 @@ namespace Radzen.Blazor
}
indexOfColumnToReoder = null;
}
}
}
/// <summary>
@@ -3156,9 +3205,7 @@ namespace Radzen.Blazor
if (LoadData.HasDelegate && IsVirtualizationAllowed())
{
Data = null;
#if NET5_0_OR_GREATER
ResetLoadData();
#endif
}
InvokeAsync(ReloadInternal);
@@ -3186,9 +3233,7 @@ namespace Radzen.Blazor
if (LoadData.HasDelegate && IsVirtualizationAllowed())
{
Data = null;
#if NET5_0_OR_GREATER
ResetLoadData();
#endif
}
InvokeAsync(ReloadInternal);
@@ -3219,7 +3264,7 @@ namespace Radzen.Blazor
additionalClasses.Add("rz-density-compact");
}
return $"rz-has-paginator rz-datatable rz-datatable-scrollable {String.Join(" ", additionalClasses)}";
return $"rz-has-pager rz-datatable rz-datatable-scrollable {String.Join(" ", additionalClasses)}";
}
internal string getHeaderStyle()
@@ -3298,7 +3343,7 @@ namespace Radzen.Blazor
Visible = c.GetVisible(),
OrderIndex = c.GetOrderIndex(),
SortOrder = c.GetSortOrder(),
SortIndex = c.getSortIndex(),
SortIndex = c.GetSortIndex(),
FilterValue = c.GetFilterValue(),
FilterOperator = c.GetFilterOperator(),
SecondFilterValue = c.GetSecondFilterValue(),
@@ -3323,8 +3368,8 @@ namespace Radzen.Blazor
if (SettingsChanged.HasDelegate)
{
var shouldUpdateState = false;
var hasFilter = settings.Columns != null && settings.Columns.Any(c =>
c.FilterValue != null || c.SecondFilterValue != null ||
var hasFilter = settings.Columns != null && settings.Columns.Any(c =>
c.FilterValue != null || c.SecondFilterValue != null ||
c.FilterOperator == FilterOperator.IsNull || c.FilterOperator == FilterOperator.IsNotNull ||
c.FilterOperator == FilterOperator.IsEmpty || c.FilterOperator == FilterOperator.IsNotEmpty ||
c.SecondFilterOperator == FilterOperator.IsNull || c.SecondFilterOperator == FilterOperator.IsNotNull ||

View File

@@ -1,6 +1,7 @@
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
@@ -37,6 +38,19 @@ namespace Radzen.Blazor
[CascadingParameter]
public RadzenDataGridColumn<TItem> Parent { get; set; }
/// <summary>
/// Specifies wether CheckBoxList filter list virtualization is enabled. Set to <c>true</c> by default.
/// </summary>
[Parameter]
public bool AllowCheckBoxListVirtualization { get; set; } = true;
/// <summary>
/// Gets or sets the column filter mode.
/// </summary>
/// <value>The column filter mode.</value>
[Parameter]
public FilterMode? FilterMode { get; set; }
internal void RemoveColumn(RadzenDataGridColumn<TItem> column)
{
if (Grid.childColumns.Contains(column))
@@ -76,17 +90,14 @@ namespace Radzen.Blazor
internal int GetColSpan(bool isDataCell = false)
{
if (!Grid.AllowCompositeDataCells && isDataCell)
if (!Grid.AllowCompositeDataCells && isDataCell || Columns == null)
return 1;
var directChildColumns = Grid.childColumns.Where(c => c.GetVisible() && c.Parent == this);
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();
if (Parent == null)
{
return Columns == null ? 1 : directChildColumns.Sum(c => c.GetColSpan());
}
return Columns == null ? 1 : directChildColumns.Count();
return span != 0 ? span : ColumnsCollection.Count;
}
internal int GetRowSpan(bool isDataCell = false)
@@ -97,7 +108,7 @@ namespace Radzen.Blazor
if (Columns == null && Parent != null)
{
var level = this.GetLevel();
return level == Grid.deepestChildColumnLevel ? 1 : level + 1;
return level == Grid.deepestChildColumnLevel ? 1 : level < Grid.deepestChildColumnLevel ? Grid.deepestChildColumnLevel : level + 1;
}
return Columns == null && Parent == null ? Grid.deepestChildColumnLevel + 1 : 1;
@@ -122,6 +133,23 @@ namespace Radzen.Blazor
{
Grid.AddColumn(this);
var canSetFilterPropertyType = (FilterMode ?? Grid.FilterMode) == Radzen.FilterMode.CheckBoxList && FilterTemplate == null;
if (canSetFilterPropertyType)
{
if (Type == null)
{
_filterPropertyType = typeof(IEnumerable<object>);
}
if (GetFilterOperator() == FilterOperator.Equals)
{
SetFilterOperator(FilterOperator.Contains);
}
Grid.FilterPopupRenderMode = PopupRenderMode.OnDemand;
}
var property = GetFilterProperty();
if (!string.IsNullOrEmpty(property))
@@ -129,7 +157,7 @@ namespace Radzen.Blazor
_propertyType = PropertyAccess.GetPropertyType(typeof(TItem), property);
}
if (!string.IsNullOrEmpty(property) && Type == null)
if (!string.IsNullOrEmpty(property) && Type == null && !canSetFilterPropertyType)
{
_filterPropertyType = _propertyType;
}
@@ -138,18 +166,18 @@ namespace Radzen.Blazor
{
_filterPropertyType = Type;
}
else
else if(!string.IsNullOrEmpty(Property))
{
propertyValueGetter = PropertyAccess.Getter<TItem, object>(Property);
}
if (_filterPropertyType == typeof(string))
if (_filterPropertyType == typeof(string) && filterOperator != FilterOperator.Custom && filterOperator == null)
{
FilterOperator = FilterOperator.Contains;
SetFilterOperator(FilterOperator.Contains);
}
}
}
int? orderIndex;
/// <summary>
@@ -268,7 +296,7 @@ namespace Radzen.Blazor
[Parameter]
public string ColumnPickerTitle
{
get => _columnPickerTitle ?? Title;
get => _columnPickerTitle ?? Title ?? string.Empty;
set => _columnPickerTitle = value;
}
@@ -331,7 +359,7 @@ namespace Radzen.Blazor
{
return FilterPlaceholder ?? string.Empty;
}
/// <summary>
/// Gets or sets the second filter value.
/// </summary>
@@ -367,6 +395,13 @@ namespace Radzen.Blazor
[Parameter]
public string CssClass { get; set; }
/// <summary>
/// Gets or sets a function that calculates the CSS class based on the <typeparamref name="TItem"/> value.
/// </summary>
/// <value>The dynamic CSS class applied to data cells.</value>
[Parameter]
public Func<RadzenDataGridColumn<TItem>, TItem, string> CalculatedCssClass { get; set; }
/// <summary>
/// Gets or sets the header CSS class applied to header cell.
/// </summary>
@@ -465,6 +500,12 @@ namespace Radzen.Blazor
[Parameter]
public RenderFragment<TItem> EditTemplate { get; set; }
/// <summary>
/// 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;
/// <summary>
/// Gets or sets the header template.
/// </summary>
@@ -521,6 +562,13 @@ namespace Radzen.Blazor
[Parameter]
public Type Type { get; set; }
/// <summary>
/// Gets or sets the IFormatProvider used for FormatString.
/// </summary>
/// <value>The IFormatProvider.</value>
[Parameter]
public IFormatProvider FormatProvider { get; set; }
Func<TItem, object> propertyValueGetter;
/// <summary>
@@ -532,7 +580,9 @@ namespace Radzen.Blazor
{
var value = propertyValueGetter != null && !string.IsNullOrEmpty(Property) && !Property.Contains('.') ? propertyValueGetter(item) : !string.IsNullOrEmpty(Property) ? PropertyAccess.GetValue(item, Property) : "";
if ((PropertyAccess.IsEnum(FilterPropertyType) || PropertyAccess.IsNullableEnum(FilterPropertyType)) && value != null)
if ((PropertyAccess.IsEnum(FilterPropertyType) || PropertyAccess.IsNullableEnum(FilterPropertyType) ||
((FilterMode ?? Grid.FilterMode) == Radzen.FilterMode.CheckBoxList && (value as Enum) != null)) && value != null)
{
var enumValue = value as Enum;
if (enumValue != null)
@@ -541,7 +591,7 @@ namespace Radzen.Blazor
}
}
return !string.IsNullOrEmpty(FormatString) ? string.Format(Grid?.Culture ?? CultureInfo.CurrentCulture, FormatString, value) : Convert.ToString(value, Grid?.Culture ?? CultureInfo.CurrentCulture);
return !string.IsNullOrEmpty(FormatString) ? string.Format(FormatProvider ?? Grid?.Culture ?? CultureInfo.CurrentCulture, FormatString, value) : Convert.ToString(value, FormatProvider ?? Grid?.Culture ?? CultureInfo.CurrentCulture);
}
internal object GetHeader()
@@ -573,7 +623,7 @@ namespace Radzen.Blazor
var width = GetWidthOrGridSetting()?.Trim();
if (!string.IsNullOrEmpty(width) && !isForCol)
if (!string.IsNullOrEmpty(width))
{
style.Add($"width:{width}");
}
@@ -608,13 +658,13 @@ namespace Radzen.Blazor
{
var stackColumns = visibleFrozenColumns.Where((c, i) => visibleFrozenColumns.IndexOf(this) > i);
return GetStackedStyleForFrozen(stackColumns, "left");
return GetStackedStyleForFrozen(stackColumns, "inset-inline-start");
}
else
{
var stackColumns = visibleFrozenColumns.Where((c, i) => visibleFrozenColumns.IndexOf(this) < i);
return GetStackedStyleForFrozen(stackColumns, "right");
return GetStackedStyleForFrozen(stackColumns, "inset-inline-end");
}
}
@@ -848,12 +898,10 @@ namespace Radzen.Blazor
Grid.SaveSettings();
if (Grid.IsVirtualizationAllowed())
{
#if NET5_0_OR_GREATER
if (Grid.virtualize != null)
{
await Grid.virtualize.RefreshDataAsync();
}
#endif
}
else
{
@@ -874,12 +922,10 @@ namespace Radzen.Blazor
Grid.SaveSettings();
if (Grid.IsVirtualizationAllowed())
{
#if NET5_0_OR_GREATER
if (Grid.virtualize != null)
{
await Grid.virtualize.RefreshDataAsync();
}
#endif
}
else
{
@@ -900,12 +946,10 @@ namespace Radzen.Blazor
Grid.SaveSettings();
if (Grid.IsVirtualizationAllowed())
{
#if NET5_0_OR_GREATER
if (Grid.virtualize != null)
{
await Grid.virtualize.RefreshDataAsync();
}
#endif
}
else
{
@@ -915,10 +959,10 @@ namespace Radzen.Blazor
return;
}
}
if (parameters.DidParameterChange(nameof(FilterOperator), FilterOperator))
if (parameters.DidParameterChange(nameof(FilterOperator), FilterOperator) || _filterOperator != null)
{
filterOperator = parameters.GetValueOrDefault<FilterOperator>(nameof(FilterOperator));
filterOperator = _filterOperator ?? parameters.GetValueOrDefault<FilterOperator>(nameof(FilterOperator));
}
if (parameters.DidParameterChange(nameof(SecondFilterValue), SecondFilterValue))
@@ -955,6 +999,14 @@ namespace Radzen.Blazor
return filterValue ?? FilterValue;
}
internal void ClearFilterValues()
{
if (headerCell != null)
{
headerCell.filterValues = null;
}
}
/// <summary>
/// Get column filter operator.
/// </summary>
@@ -998,13 +1050,21 @@ namespace Radzen.Blazor
value = offset;
}
if (PropertyAccess.IsEnum(FilterPropertyType) || (PropertyAccess.IsNullableEnum(FilterPropertyType)))
{
Type enumType = Enum.GetUnderlyingType(Nullable.GetUnderlyingType(FilterPropertyType) ?? FilterPropertyType);
value = value is not null ? Convert.ChangeType(value, enumType) : null;
}
if (isFirst)
{
filterValue = value;
filterValue = CanSetCurrentValue(value) ? value :
GetFilterOperator() == FilterOperator.IsEmpty || GetFilterOperator() == FilterOperator.IsNotEmpty ? string.Empty : null;
}
else
{
secondFilterValue = value;
secondFilterValue = CanSetCurrentValue(value, false) ? value :
GetSecondFilterOperator() == FilterOperator.IsEmpty || GetSecondFilterOperator() == FilterOperator.IsNotEmpty ? string.Empty : null;
}
}
@@ -1020,12 +1080,18 @@ namespace Radzen.Blazor
await Grid.FirstPage(true);
}
internal bool CanSetFilterValue()
internal bool CanSetFilterValue(bool isFirst = true)
{
return GetFilterOperator() == FilterOperator.IsNull
|| GetFilterOperator() == FilterOperator.IsNotNull
|| GetFilterOperator() == FilterOperator.IsEmpty
|| GetFilterOperator() == FilterOperator.IsNotEmpty;
var fo = isFirst ? GetFilterOperator() : GetSecondFilterOperator();
return fo != FilterOperator.IsNull
&& fo != FilterOperator.IsNotNull
&& fo != FilterOperator.IsEmpty
&& fo != FilterOperator.IsNotEmpty;
}
internal bool CanSetCurrentValue(object value, bool isFirst = true)
{
return CanSetFilterValue(isFirst) ? !string.IsNullOrEmpty(value?.ToString()) : false;
}
internal bool HasCustomFilter()
@@ -1037,7 +1103,7 @@ namespace Radzen.Blazor
{
return GetFilterValue() != null
|| GetSecondFilterValue() != null
|| CanSetFilterValue()
|| !CanSetFilterValue()
|| HasCustomFilter();
}
@@ -1073,28 +1139,41 @@ namespace Radzen.Blazor
/// </summary>
public void ClearFilters()
{
SetFilterValue(null);
SetFilterValue(null, false);
SetFilterOperator(null);
SetSecondFilterOperator(null);
FilterValue = null;
SecondFilterValue = null;
FilterOperator = FilterOperator == FilterOperator.Custom
var fo = FilterOperator == FilterOperator.Custom
? FilterOperator.Custom
: typeof(System.Collections.IEnumerable).IsAssignableFrom(FilterPropertyType)
? !string.IsNullOrEmpty(FilterProperty) && FilterProperty != Property ? FilterOperator.In : FilterOperator.Contains
: default(FilterOperator);
SecondFilterOperator = default(FilterOperator);
SetFilterOperator(fo);
SetSecondFilterOperator(null);
filterValue = null;
secondFilterValue = null;
ClearFilterValues();
LogicalFilterOperator = default(LogicalFilterOperator);
}
FilterOperator? _filterOperator;
/// <summary>
/// Gets or sets the filter operator.
/// </summary>
/// <value>The filter operator.</value>
[Parameter]
public FilterOperator FilterOperator { get; set; }
public FilterOperator FilterOperator
{
get
{
return _filterOperator ?? FilterOperator.Equals;
}
set
{
_filterOperator = value;
}
}
/// <summary>
/// Gets or sets the second filter operator.
@@ -1187,10 +1266,10 @@ namespace Radzen.Blazor
/// </summary>
public virtual IEnumerable<FilterOperator> GetFilterOperators()
{
if (PropertyAccess.IsEnum(FilterPropertyType))
if (PropertyAccess.IsEnum(FilterPropertyType) || FilterPropertyType == typeof(bool))
return new FilterOperator[] { FilterOperator.Equals, FilterOperator.NotEquals };
if (PropertyAccess.IsNullableEnum(FilterPropertyType))
if (PropertyAccess.IsNullableEnum(FilterPropertyType) || FilterPropertyType == typeof(bool?))
return new FilterOperator[] { FilterOperator.Equals, FilterOperator.NotEquals, FilterOperator.IsNull, FilterOperator.IsNotNull };
return Enum.GetValues(typeof(FilterOperator)).Cast<FilterOperator>().Where(o =>
@@ -1198,7 +1277,7 @@ namespace Radzen.Blazor
var isStringOperator = o == FilterOperator.Contains || o == FilterOperator.DoesNotContain
|| o == FilterOperator.StartsWith || o == FilterOperator.EndsWith || o == FilterOperator.IsEmpty || o == FilterOperator.IsNotEmpty;
if ((FilterPropertyType == typeof(string) || !QueryableExtension.IsEnumerable(FilterPropertyType)) &&
if ((FilterPropertyType == typeof(string) || !QueryableExtension.IsEnumerable(FilterPropertyType)) &&
(o == FilterOperator.In || o == FilterOperator.NotIn)) return false;
return FilterPropertyType == typeof(string) || QueryableExtension.IsEnumerable(FilterPropertyType) ? isStringOperator
@@ -1291,6 +1370,12 @@ namespace Radzen.Blazor
/// </summary>
public virtual bool ShowTimeForDateTimeFilter()
{
#if NET6_0_OR_GREATER
if (FilterPropertyType == typeof(DateOnly))
{
return false;
}
#endif
return true;
}
@@ -1331,7 +1416,10 @@ namespace Radzen.Blazor
Grid?.RemoveColumn(this);
}
internal int? getSortIndex()
/// <summary>
/// Gets the column sort descriptor index indicating order of applied column sort in case of multiple sorting.
/// </summary>
public int? GetSortIndex()
{
var descriptor = Grid.sorts.Where(s => s.Property == GetSortProperty()).FirstOrDefault();
if (descriptor != null)
@@ -1344,8 +1432,8 @@ namespace Radzen.Blazor
internal string getSortIndexAsString()
{
var index = getSortIndex();
return index != null ? $"{getSortIndex() + 1}" : "";
var index = GetSortIndex();
return index != null ? $"{GetSortIndex() + 1}" : "";
}
internal RadzenDataGridHeaderCell<TItem> headerCell;

View File

@@ -3,7 +3,7 @@
@using Microsoft.JSInterop
@inject IJSRuntime JSRuntime
<button title=@Column.GetFilterOperatorText(Column.GetFilterOperator()) class="@FilterIconStyle()" onclick="@($"Radzen.togglePopup(this.parentNode, '{Grid.PopupID}{Column.GetFilterProperty()}')")">
<i class="rzi">@Grid.FilterIcon</i>
<i class="notranslate rzi">@Grid.FilterIcon</i>
@if (Column.GetFilterOperator() == FilterOperator.DoesNotContain)
{
<s>@Column.GetFilterOperatorSymbol(Column.GetFilterOperator())</s>
@@ -128,7 +128,7 @@
{
var additionalStyle = Column.HasActiveFilter() ? "rz-grid-filter-active" : "";
return $"rz-filter-button rz-button rz-button-md rz-button-icon-only rz-variant-flat rz-light {additionalStyle}";
return $"rz-filter-button rz-button rz-button-md rz-button-icon-only rz-variant-flat rz-base rz-shade-default {additionalStyle}";
}
protected async Task ApplyFilter(FilterOperator value)

View File

@@ -0,0 +1,61 @@
@typeparam TItem
@if (RowIndex == Column.GetLevel())
{
<td rowspan="@(Column.GetRowSpan())" colspan="@(Column.GetColSpan())" @attributes="@Attributes" class="@CssClass" scope="col" style="@GetStyle()">
<span class="rz-column-footer">
@if (Column.GroupFooterTemplate != null)
{
@Column.GroupFooterTemplate(Group)
}
</span>
</td>
}
else
{
@foreach(var column in Grid.childColumns.Where(c => c.GetVisible() && c.Parent == Column))
{
<RadzenDataGridGroupFooterCell Group="@Group" RowIndex="@RowIndex" Grid="@Grid" Column="@column" Style="@column.GetStyle(true, true)"
CssClass="@($"{Column.GroupFooterCssClass} {Grid.getFrozenColumnClass(column, Grid.ColumnsCollection.Where(c => c.GetVisible()).ToList())} {Grid.getCompositeCellCSSClass(column)}")"
Attributes="@(Attributes)" />
}
}
@code {
[Parameter(CaptureUnmatchedValues = true)]
public IReadOnlyDictionary<string, object> Attributes { get; set; }
[Parameter]
public Group Group { get; set; }
[Parameter]
public RadzenDataGridColumn<TItem> Column { get; set; }
[Parameter]
public int RowIndex { get; set; }
[Parameter]
public RadzenDataGrid<TItem> Grid { get; set; }
[Parameter]
public string CssClass { get; set; }
[Parameter]
public string Style { get; set; }
private string GetStyle()
{
var styles = new List<string>() { Column.GetStyle(true, true), Style };
if (Attributes?.TryGetValue("style", out var styleAttribute) == true)
{
styles.Add(Convert.ToString(styleAttribute));
}
var finalStyle = string.Join(";",
styles
.Select(x => x?.Trim().TrimEnd(';'))
.Where(x => !string.IsNullOrEmpty(x))
);
return finalStyle;
}
}

View File

@@ -1,41 +1,41 @@
@typeparam TItem
@using System.Linq.Dynamic.Core
<tr>
@if (Grid.Template != null && Grid.ShowExpandColumn)
{
@if (Grid.ShowGroupExpandColumn)
{
<th class="rz-col-icon rz-unselectable-text" scope="col">
<span class="rz-column-title"></span>
</th>
}
}
@for(var i = 0; i < GetLevel(); i++)
{
<td class="rz-col-icon">
<span class="rz-column-title"></span>
</td>
}
@foreach (var column in Columns)
{
<td class="@($" {column.GroupFooterCssClass} {Grid.getFrozenColumnClass(column, Columns)}".Trim())" style="@column.GetStyle(true, true)">
<span class="rz-column-footer">
@if (column.GroupFooterTemplate != null)
@for (var i = 0; i < Grid.deepestChildColumnLevel + 1; i++)
{
<tr>
@if (i == 0) // Only add the th elements for the first row
{
@if (Grid.Template != null && Grid.ShowExpandColumn)
{
@if (Grid.ShowGroupExpandColumn)
{
@column.GroupFooterTemplate(Group)
<th class="rz-col-icon rz-unselectable-text" scope="col">
<span class="rz-column-title"></span>
</th>
}
</span>
</td>
}
</tr>
}
}
@for (var j = 0; j < GetLevel(); j++)
{
<td class="rz-col-icon">
<span class="rz-column-title"></span>
</td>
}
@foreach (var column in Columns)
{
<RadzenDataGridGroupFooterCell Group="@Group" RowIndex="@i" Grid="@Grid" Column="@column" CssClass="@($"{column.GroupFooterCssClass} {Grid.getFrozenColumnClass(column, Columns)} {Grid.getCompositeCellCSSClass(column)}".Trim())" />
}
</tr>
}
@code {
[Parameter]
public IList<RadzenDataGridColumn<TItem>> Columns { get; set; }
GroupResult _groupResult;
[Parameter]
public GroupResult GroupResult
{
public GroupResult GroupResult
{
get
{
return _groupResult;

View File

@@ -3,6 +3,14 @@
@using System.Globalization
@{
var rowArgs = Grid?.GroupRowAttributes(this);
if (Grid != null)
{
if (rowArgs.Item1.Expanded == true && Grid.collapsedGroupItems.ContainsKey(this))
{
Grid.collapsedGroupItems.Remove(this);
}
}
}
<tr class="rz-group-row" @attributes="@rowArgs.Item2">
@if (Grid.ShowGroupExpandColumn)
@@ -19,7 +27,7 @@
}
<td class="rz-col-icon">
<span class="rz-column-title"></span>
<a aria-label=@Grid.ExpandGroupAriaLabel @onclick:preventDefault="true" @onclick="@(_ => Grid.ExpandGroupItem(this, rowArgs.Item1.Expanded))">
<a id="@(Grid.GridId() + Group.GetHashCode())" aria-label=@Grid.ExpandGroupAriaLabel @onclick:preventDefault="true" @onclick="@(_ => Grid.ExpandGroupItem(this, rowArgs.Item1.Expanded))">
<span class="@(Grid.ExpandedGroupItemStyle(this, Grid.allGroupsExpanded != null ? Grid.allGroupsExpanded : rowArgs.Item1.Expanded))"></span>
</a>
</td>

View File

@@ -1,6 +1,11 @@
@typeparam TItem
@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))>
@@ -26,7 +31,7 @@
{
@if (Column.GetSortOrder() == SortOrder.Ascending)
{
<span class="rz-sortable-column-icon rzi-grid-sort rzi-sort rzi-sort-asc"></span>
<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()" />
@@ -34,7 +39,7 @@
}
else if (Column.GetSortOrder() == SortOrder.Descending)
{
<span class="rz-sortable-column-icon rzi-grid-sort rzi-sort rzi-sort-desc"></span>
<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()" />
@@ -42,7 +47,7 @@
}
else
{
<span class="rz-sortable-column-icon rzi-grid-sort rzi-sort"></span>
<span class="notranslate rz-sortable-column-icon rzi-grid-sort rzi-sort"></span>
}
}
</span>
@@ -53,7 +58,8 @@
@onmousedown:preventDefault="true"
@onmousedown=@StartColumnResize @onmouseup=@StopColumnResize>&nbsp;</div>
}
@if (Grid.AllowFiltering && Column.Filterable && Grid.FilterMode == FilterMode.Advanced)
@{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">
@@ -70,8 +76,10 @@
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.FilterOperatorArialLabel + Column.GetFilterOperatorText(Column.GetSecondFilterOperator()) }})"
<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)
{
@@ -79,10 +87,10 @@
}
else if (PropertyAccess.IsNullableEnum(Column.FilterPropertyType) || PropertyAccess.IsEnum(Column.FilterPropertyType))
{
<RadzenDropDown AllowClear="false" AllowFiltering="false" TValue="@object"
<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.FilterValueArialLabel + Column.GetFilterValue() }})" />
InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", Column.Title + Grid.FilterValueAriaLabel + Column.GetFilterValue() }})" />
}
else if (PropertyAccess.IsNumeric(Column.FilterPropertyType))
{
@@ -90,32 +98,32 @@
}
else if (PropertyAccess.IsDate(Column.FilterPropertyType))
{
<RadzenDatePicker TValue="@object" ShowTime="@Column.ShowTimeForDateTimeFilter()" ShowTimeOkButton="true" DateFormat="@Grid.getFilterDateFormat(Column)"
Value="@Column.GetFilterValue()" Change="@(args => Column.SetFilterValue(args.Value))" AllowInput=@(Grid.AllowFilterDateInput)
InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", Column.Title + Grid.FilterValueArialLabel + Column.GetFilterValue() }})" />
<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 InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", Column.Title + Grid.FilterValueArialLabel + Column.GetFilterValue() }})" TriState="true" TValue="@object" Value="@Column.GetFilterValue()" Change="@(args => { Column.SetFilterValue(null); Column.SetFilterValue(args); Grid.SaveSettings(); InvokeAsync(() => Grid.Reload()); })" />
<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 id="@($"{getColumnPopupID()}-sf")" aria-label=@(Column.Title + Grid.FilterValueArialLabel + Column.GetFilterValue()) Value="@($"{Column.GetFilterValue()}")" Change="@(args => Column.SetFilterValue(args))" />
<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.LogicalOperatorArialLabel + (Column.LogicalFilterOperator == LogicalFilterOperator.And ? Grid.AndOperatorText : Grid.OrOperatorText) }})"
<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.SecondFilterOperatorArialLabel + 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))" />
<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 AllowClear="false" AllowFiltering="false" TValue="@object" InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", Column.Title + Grid.SecondFilterValueArialLabel }})"
Value=@Column.GetSecondFilterValue() Multiple="false" Placeholder="@Grid.EnumFilterSelectText" TextProperty="Text" ValueProperty="Value" Data=@EnumExtensions.EnumAsKeyValuePair(Nullable.GetUnderlyingType(Column.FilterPropertyType) ?? 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))
@@ -124,27 +132,48 @@
}
else if (PropertyAccess.IsDate(Column.FilterPropertyType))
{
<RadzenDatePicker TValue="@object" InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", Column.Title + Grid.SecondFilterValueArialLabel + Column.GetSecondFilterValue() }})"
<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(args.Value, false))" AllowInput=@(Grid.AllowFilterDateInput) />
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 InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", Column.Title + Grid.SecondFilterValueArialLabel + Column.GetSecondFilterValue() }})" TriState="true" TValue="@object" Value="@Column.GetSecondFilterValue()" Change="@(args => { Column.SetFilterValue(args, false); Grid.SaveSettings(); })" />
<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 id="@($"{getColumnPopupID()}-sf2")" aria-label=@(Column.Title + Grid.SecondFilterValueArialLabel + Column.GetSecondFilterValue()) Value="@($"{Column.GetSecondFilterValue()}")" Change="@(args => Column.SetFilterValue(args, false))" />
<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))" />
}
}
</form>
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>
}
</form>
}
</div>
@if (Column.FilterTemplate == null)
{
<div class="rz-grid-filter-buttons">
<RadzenButton ButtonStyle="ButtonStyle.Secondary" Text=@Grid.ClearFilterText Click="@ClearFilter" />
<RadzenButton form="@($"{getColumnPopupID()}-form")" ButtonType="ButtonType.Submit" ButtonStyle="ButtonStyle.Primary" Text=@Grid.ApplyFilterText />
<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>
@@ -166,6 +195,113 @@ else
Radzen.Blazor.Rendering.Popup popup;
ElementReference filterButton;
int filterValuesCount;
internal IEnumerable filterValues;
string loadDataArgsString;
async Task LoadFilterValues(LoadDataArgs loadDataArgs)
{
var property = Column.Property != Column.FilterProperty && !string.IsNullOrEmpty(Column.FilterProperty) ? Column.Property :
Column.GetFilterProperty();
var propertyType = PropertyAccess.GetPropertyType(typeof(TItem), property);
if (property.IndexOf(".") != -1)
{
property = $"np({property})";
}
if (propertyType == typeof(string))
{
property = $@"({property} == null ? """" : {property})";
}
var loadDataArgsString = $"{loadDataArgs.Skip}|{loadDataArgs.Top}{loadDataArgs.Filter}";
if (Column.Grid.LoadColumnFilterData.HasDelegate)
{
var args = new DataGridLoadColumnFilterDataEventArgs<TItem>() { Column = Column, Filter = loadDataArgs.Filter };
await Column.Grid.LoadColumnFilterData.InvokeAsync(args);
filterValues = args.Data.AsQueryable().Select(DynamicLinqCustomTypeProvider.ParsingConfig, property).Distinct().Cast(propertyType ?? typeof(object));
filterValuesCount = args.Count;
}
else if(!string.IsNullOrEmpty(property) && (filterValues == null || this.loadDataArgsString != loadDataArgsString) && Column.Grid.Data != null)
{
this.loadDataArgsString = loadDataArgsString;
IQueryable query;
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);
}
else
{
query = Column.Grid.Data.AsQueryable().Where<TItem>(Column.Grid.allColumns.Where(c => c != Column));
}
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));
}
else
{
query = query.Select(DynamicLinqCustomTypeProvider.ParsingConfig, property).Distinct().OrderBy("it").Cast(propertyType ?? typeof(object));
}
filterValuesCount = query.Count();
if(loadDataArgs.Skip != null)
{
query = query.Skip(loadDataArgs.Skip.Value);
}
if(loadDataArgs.Top != null)
{
query = query.Take(loadDataArgs.Top.Value);
}
filterValues = query.Select("it => it.ToString() == string.Empty ? null : it");
if (!Column.AllowCheckBoxListVirtualization)
{
await InvokeAsync(StateHasChanged);
}
}
}
void ListBoxChange(object args)
{
var enumerable = args as IEnumerable;
Type propertyType = null;
if (Column.Property != Column.FilterProperty && !string.IsNullOrEmpty(Column.FilterProperty))
{
var collectionType = PropertyAccess.GetPropertyType(typeof(TItem), Column.Property);
if (collectionType != null && collectionType.IsGenericType)
{
var itemType = collectionType.GenericTypeArguments.FirstOrDefault();
if(itemType != null)
{
propertyType = PropertyAccess.GetPropertyType(itemType, Column.FilterProperty);
}
}
}
else
{
propertyType = PropertyAccess.GetPropertyType(typeof(TItem), Column.GetFilterProperty());
}
Column.SetFilterValue(enumerable.Cast<object>().Any() ? propertyType != null ? enumerable.AsQueryable().Cast(propertyType) : enumerable : null);
}
string getFilterOpen()
{
return Grid.FilterPopupRenderMode == PopupRenderMode.Initial ? $"Radzen.togglePopup(this, '{getColumnPopupID()}', false, null, null, true, true)" : "";
@@ -176,6 +312,11 @@ else
if (Grid.FilterPopupRenderMode == PopupRenderMode.OnDemand)
{
await popup.ToggleAsync(filterButton);
Grid.allColumns
.Where(c => c != Column && c.headerCell != null)
.Select(c => c.headerCell).ToList()
.ForEach(cell => InvokeAsync(cell.CloseFilter));
}
}
@@ -239,7 +380,7 @@ else
private string getFilterIconCss(RadzenDataGridColumn<TItem> column)
{
var additionalStyle = Column.HasActiveFilter() ? "rz-grid-filter-active" : "";
return $"rzi rz-grid-filter-icon {additionalStyle}";
return $"notranslate rzi rz-grid-filter-icon {additionalStyle}";
}
private Task OnSortKeyPressed(KeyboardEventArgs args)
@@ -311,6 +452,7 @@ else
if (key == "Escape")
{
await CloseFilter();
await ClearFilter();
await Grid.GetJSRuntime().InvokeVoidAsync("Radzen.focusElement", Grid.GridId());
}
}

View File

@@ -3,7 +3,7 @@
@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++)
{
@@ -27,7 +27,7 @@
<span class="rz-column-title"></span>
@if (rowArgs.Item1.Expandable)
{
<a aria-label="@Grid.ExpandChildItemAriaLabel" @onclick:preventDefault="true" @onclick="@(_ => Grid.ExpandItem(Item))" @onclick:stopPropagation>
<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>
</a>
}

View File

@@ -10,15 +10,29 @@
}
@if (Data != null)
{
@if (!WrapItems)
{
@DrawDataListRows()
}
else
@if (!ShowEmptyMessage || Count > 0 && (AllowVirtualization ? Count > 0 : true) || LoadData.HasDelegate && Data.Count() > 0)
{
@if (!WrapItems)
{
@DrawDataListRows()
}
else
{
<div class="rz-g">
@DrawDataListRows()
</div>
}
}
else
{
@if (EmptyTemplate != null)
{
@EmptyTemplate
}
else
{
<span style="white-space: normal">@EmptyText</span>
}
}
}
@if (IsLoading)
@@ -31,13 +45,13 @@
}
else
{
<i class="rzi-circle-o-notch"></i>
<i class="notranslate rzi-circle-o-notch"></i>
}
</div>
}
@if (AllowPaging && PagerPosition.HasFlag(PagerPosition.Bottom))
{
<RadzenPager HorizontalAlign="@PagerHorizontalAlign" AlwaysVisible="@PagerAlwaysVisible" @ref="bottomPager" Count="@Count" PageSize="@PageSize" PageNumbersCount="@PageNumbersCount" PageChanged="@OnPageChanged" PageSizeChanged="@OnPageSizeChanged" PageSizeOptions="@PageSizeOptions" ShowPagingSummary="@ShowPagingSummary" PagingSummaryFormat="@PagingSummaryFormat" PageSizeText="@PageSizeText" class="rz-paginator-bottom" Density="@Density" FirstPageTitle="@FirstPageTitle" FirstPageAriaLabel="@FirstPageAriaLabel" PrevPageAriaLabel="@PrevPageAriaLabel" PrevPageTitle="@PrevPageTitle" NextPageAriaLabel="@NextPageAriaLabel" NextPageTitle="@NextPageTitle" LastPageAriaLabel="@LastPageAriaLabel" LastPageTitle="@LastPageTitle" PageAriaLabelFormat="@PageAriaLabelFormat" PageTitleFormat="@PageTitleFormat" PrevPageLabel="@PrevPageLabel" NextPageLabel="@NextPageLabel" />
<RadzenPager HorizontalAlign="@PagerHorizontalAlign" AlwaysVisible="@PagerAlwaysVisible" @ref="bottomPager" Count="@Count" PageSize="@PageSize" PageNumbersCount="@PageNumbersCount" PageChanged="@OnPageChanged" PageSizeChanged="@OnPageSizeChanged" PageSizeOptions="@PageSizeOptions" ShowPagingSummary="@ShowPagingSummary" PagingSummaryFormat="@PagingSummaryFormat" PageSizeText="@PageSizeText" class="rz-pager-bottom" Density="@Density" FirstPageTitle="@FirstPageTitle" FirstPageAriaLabel="@FirstPageAriaLabel" PrevPageAriaLabel="@PrevPageAriaLabel" PrevPageTitle="@PrevPageTitle" NextPageAriaLabel="@NextPageAriaLabel" NextPageTitle="@NextPageTitle" LastPageAriaLabel="@LastPageAriaLabel" LastPageTitle="@LastPageTitle" PageAriaLabelFormat="@PageAriaLabelFormat" PageTitleFormat="@PageTitleFormat" PrevPageLabel="@PrevPageLabel" NextPageLabel="@NextPageLabel" />
}
</div>
}

View File

@@ -27,6 +27,9 @@ namespace Radzen.Blazor
/// &lt;/RadzenDataList&gt;
/// </code>
/// </example>
#if NET6_0_OR_GREATER
[CascadingTypeParameter(nameof(TItem))]
#endif
public partial class RadzenDataList<TItem> : PagedDataBoundComponent<TItem>
{
/// <inheritdoc />
@@ -35,13 +38,43 @@ namespace Radzen.Blazor
return "rz-datalist-content";
}
/// <summary>
/// Gets or sets a value indicating whether DataList should show empty message.
/// </summary>
[Parameter]
public bool ShowEmptyMessage { get; set; }
private string _emptyText = "No records to display.";
/// <summary>
/// Gets or sets the empty text shown when Data is empty collection.
/// </summary>
/// <value>The empty text.</value>
[Parameter]
public string EmptyText
{
get { return _emptyText; }
set
{
if (value != _emptyText)
{
_emptyText = value;
}
}
}
/// <summary>
/// Gets or sets the empty template shown when Data is empty collection.
/// </summary>
/// <value>The empty template.</value>
[Parameter]
public RenderFragment EmptyTemplate { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to wrap items.
/// </summary>
/// <value><c>true</c> if wrap items; otherwise, <c>false</c>.</value>
[Parameter]
public bool WrapItems { get; set; }
#if NET5_0_OR_GREATER
/// <summary>
/// Gets or sets a value indicating whether this instance is virtualized.
@@ -85,12 +118,10 @@ namespace Radzen.Blazor
return new Microsoft.AspNetCore.Components.Web.Virtualization.ItemsProviderResult<TItem>(virtualDataItems, totalItemsCount);
}
#endif
RenderFragment DrawDataListRows()
{
return new RenderFragment(builder =>
{
#if NET5_0_OR_GREATER
if (AllowVirtualization)
{
builder.OpenComponent(0, typeof(Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize<TItem>));
@@ -112,9 +143,6 @@ namespace Radzen.Blazor
{
DrawRows(builder);
}
#else
DrawRows(builder);
#endif
});
}
@@ -124,12 +152,11 @@ namespace Radzen.Blazor
public async override Task Reload()
{
await base.Reload();
#if NET5_0_OR_GREATER
if (virtualize != null)
{
await virtualize.RefreshDataAsync();
}
#endif
}
internal void DrawRows(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder builder)

View File

@@ -10,50 +10,52 @@
@implements IRadzenFormComponent
@if (Visible)
{
<div @ref="@Element" @attributes="Attributes" class="@GetCssClass()" style="@getStyle()" id="@GetId()">
<div @ref="@Element" @attributes="Attributes" class="@($"rz-datepicker{(Disabled ? " rz-state-disabled" : "")}") @GetCssClass()" style="@getStyle()" id="@GetId()">
@if (!Inline)
{
<span class="@($"rz-calendar rz-calendar-w-btn{(Disabled ? " rz-state-disabled" : "")}")" style="width:100%">
<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" : "")" id="@Name" placeholder="@Placeholder" />
@if (ShowButton)
{
<button @onmousedown=@OnToggle class="@($"rz-datepicker-trigger rz-calendar-button rz-button rz-button-icon-only{(Disabled ? " rz-state-disabled" : "")}")" tabindex="-1" type="button">
<span aria-hidden="true" class="@ButtonClasses"></span><span class="rz-button-text"></span>
</button>
}
@if (AllowClear && HasValue)
{
<i class="rz-dropdown-clear-icon rzi rzi-times" @onclick="@Clear" @onclick:stopPropagation="true"></i>
}
</span>
<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" />
@if (ShowButton)
{
<button aria-label="@ToggleAriaLabel" @onmousedown=@OnToggle class="@($"rz-datepicker-trigger rz-button rz-button-icon-only{(Disabled ? " rz-state-disabled" : "")} {ButtonClass}")" tabindex="-1" type="button">
<span aria-hidden="true" class="@ButtonClasses"></span><span class="rz-button-text"></span>
</button>
}
@if (AllowClear && HasValue)
{
<i class="notranslate rz-dropdown-clear-icon rzi rzi-times" @onclick="@Clear" @onclick:stopPropagation="true"></i>
}
}
<Popup id="@PopupID" Lazy=@(PopupRenderMode == PopupRenderMode.OnDemand) @ref=@popup style=@PopupStyle class="@($"{(Inline ? "rz-datepicker-inline " : "")}rz-datepicker")">
<div class="rz-datepicker-group" @onkeydown="@OnPopupKeyDown">
<Popup id="@PopupID" Lazy=@(PopupRenderMode == PopupRenderMode.OnDemand) @ref=@popup style=@PopupStyle class="@($"{(Inline ? "rz-datepicker-inline-container " : "rz-datepicker-popup-container ")}")">
<div class="rz-calendar" @onkeydown="@OnPopupKeyDown">
@if (!TimeOnly)
{
<div class="rz-datepicker-header">
<a tabindex="-1" aria-label="@PrevMonthAriaLabel" @onclick:preventDefault="true" class="rz-datepicker-prev" @onclick="@(async () => { if (!Disabled) { try { if(CurrentDate.AddMonths(-1).Year >= YearFrom) {CurrentDate = CurrentDate.AddMonths(-1);}} catch (ArgumentOutOfRangeException) {}} })">
<span class="rz-datepicker-prev-icon rzi rzi-chevron-left"></span>
<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) {}} })">
<span class="notranslate rzi rz-calendar-prev-icon"></span>
</a>
<a tabindex="-1" aria-label="@NextMonthAriaLabel" @onclick:preventDefault="true" class="rz-datepicker-next" @onclick="@(async () => { if (!Disabled) { try { if(CurrentDate.AddMonths(1).Year <= YearTo) {CurrentDate = CurrentDate.AddMonths(1);}} catch (ArgumentOutOfRangeException) {} } })">
<span class="rz-datepicker-next-icon rzi rzi-chevron-right"></span>
<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) {} } })">
<span class="notranslate rzi rz-calendar-next-icon"></span>
</a>
<div class="rz-datepicker-title">
<RadzenDropDown @ref="monthDropDown" Style="height:auto;width:120px;margin-top:5px;text-align:left;" TabIndex="@TabIndex"
TValue="int" Value="@CurrentDate.Month" Disabled="@Disabled" Data="@months" TextProperty="Name" ValueProperty="Value"
Change="@((args) => { SetMonth(int.Parse(args.ToString())); })"/>
<RadzenDropDown @ref="yearDropDown" Style="height:auto;width:80px;margin-top:5px;text-align:left;" TabIndex="@TabIndex"
TValue="int" Value="@CurrentDate.Year" Disabled="@Disabled" Data="@years" TextProperty="Name" ValueProperty="Value"
Change="@((args) => { SetYear(int.Parse(args.ToString())); })"/>
<div class="rz-calendar-title">
<RadzenDropDown @ref="monthDropDown" class="rz-calendar-month-dropdown" TabIndex="@TabIndex"
TValue="int" Value="@CurrentDate.Month" Disabled="@Disabled" Data="@months" TextProperty="Name" ValueProperty="Value"
Change="@((args) => { SetMonth(int.Parse(args.ToString())); })"/>
<RadzenDropDown @ref="yearDropDown" class="rz-calendar-year-dropdown" TabIndex="@TabIndex"
TValue="int" Value="@CurrentDate.Year" Disabled="@Disabled" Data="@years" TextProperty="Name" ValueProperty="Value"
Change="@((args) => { SetYear(int.Parse(args.ToString())); })">
<Template>
@Culture.Calendar.GetYear(new DateTime(context.Value,1,1))
</Template>
</RadzenDropDown>
</div>
</div>
@if(ShowDays)
{
<div class="rz-datepicker-calendar-container" tabindex="@(Disabled ? "-1" : $"{TabIndex}")" @onkeydown="@(args => OnCalendarKeyPress(args))" @onkeydown:preventDefault=preventKeyPress @onkeydown:stopPropagation>
<table class="rz-datepicker-calendar" style="@(Inline ? "" : "width:100%")">
<div class="rz-calendar-view-container" tabindex="@(Disabled ? "-1" : $"{TabIndex}")" @onkeydown="@(args => OnCalendarKeyPress(args))" @onkeydown:preventDefault=preventKeyPress @onkeydown:stopPropagation>
<table class="rz-calendar-view rz-calendar-month-view" style="@(Inline ? "" : "width:100%")">
<thead>
<tr>
@if (ShowCalendarWeek)
@@ -79,7 +81,7 @@
<tr>
@if (ShowCalendarWeek)
{
<td class="rz-datepicker-other-month rz-datepicker-week-number">@Culture.Calendar.GetWeekOfYear(new DateTime(CurrentDate.Year, CurrentDate.Month, 1).AddDays(i * 7), Culture.DateTimeFormat.CalendarWeekRule, Culture.DateTimeFormat.FirstDayOfWeek)</td>
<td class="rz-calendar-other-month rz-calendar-week-number">@Culture.Calendar.GetWeekOfYear(new DateTime(CurrentDate.Year, CurrentDate.Month, 1).AddDays(i * 7), Culture.DateTimeFormat.CalendarWeekRule, Culture.DateTimeFormat.FirstDayOfWeek)</td>
}
@for (int j = 0; j < 7; j++)
{
@@ -91,7 +93,7 @@
DateTime date = StartDate.AddDays(dayNumber++);
var dateArgs = DateAttributes(date);
<td @attributes="@(dateArgs.Attributes)" class="@GetDayCssClass(date, dateArgs)" @onmouseup=@Close
<td @attributes="@(dateArgs.Attributes)" class="@GetDayCssClass(date, dateArgs)"
@onclick="@(async () => { if (!Disabled && !dateArgs.Disabled) { await SetDay(date); } })">
<span class=@GetDayCssClass(date, dateArgs, false)>@date.Day</span>
</td>
@@ -132,18 +134,18 @@
@if (HourFormat == "12")
{
<div class="rz-ampm-picker">
<a tabindex="@(Disabled ? "-1" : $"{TabIndex}")"aria-label="@ToggleAmPmAriaLabel" @onclick:preventDefault="true" @onclick="@ToggleAmPm">
<span class="rzi rzi-chevron-up"></span>
<a id="@(GetId() + "ampmup")" tabindex="@(Disabled ? "-1" : $"{TabIndex}")" aria-label="@ToggleAmPmAriaLabel" @onclick:preventDefault="true" @onclick="@ToggleAmPm">
<span class="notranslate rzi rzi-chevron-up"></span>
</a>
<span>@CurrentDate.ToString("tt")</span>
<a tabindex="@(Disabled ? "-1" : $"{TabIndex}")" aria-label="@ToggleAmPmAriaLabel" @onclick:preventDefault="true" @onclick="@ToggleAmPm">
<span class="rzi rzi-chevron-down"></span>
<a id="@(GetId() + "ampmdown")" tabindex="@(Disabled ? "-1" : $"{TabIndex}")" aria-label="@ToggleAmPmAriaLabel" @onclick:preventDefault="true" @onclick="@ToggleAmPm">
<span class="notranslate rzi rzi-chevron-down"></span>
</a>
</div>
}
@if (ShowTimeOkButton)
{
<button type="button" class="rz-button rz-button-md rz-secondary" tabindex="0" @onclick="@OkClick">
<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>
</button>
}

View File

@@ -39,6 +39,20 @@ namespace Radzen.Blazor
[Parameter]
public string CalendarWeekTitle { get; set; } = "#";
/// <summary>
/// Gets or sets the toggle popup aria label text.
/// </summary>
/// <value>The toggle popup aria label text.</value>
[Parameter]
public string ToggleAriaLabel { get; set; } = "Toggle";
/// <summary>
/// Gets or sets the OK button aria label text.
/// </summary>
/// <value>The OK button aria label text.</value>
[Parameter]
public string OkAriaLabel { get; set; } = "Ok";
/// <summary>
/// Gets or sets the previous month aria label text.
/// </summary>
@@ -137,6 +151,7 @@ namespace Radzen.Blazor
{
if (ShowTimeOkButton)
{
DateTimeValue = newValue;
CurrentDate = newValue;
}
else
@@ -155,9 +170,9 @@ namespace Radzen.Blazor
if (v < 0)
{
newHour = 23;
newMinute = 59;
newSecond = 59;
newHour = string.IsNullOrEmpty(HoursStep) ? 23 : 0;
newMinute = string.IsNullOrEmpty(MinutesStep) ? 59 : 0;
newSecond = string.IsNullOrEmpty(SecondsStep) ? 59 : 0;
}
var newValue = new DateTime(CurrentDate.Year, CurrentDate.Month, CurrentDate.Day, newHour > 23 || newHour < 0 ? 0 : newHour, newMinute, newSecond);
@@ -182,9 +197,12 @@ namespace Radzen.Blazor
await UpdateValueFromTime(newValue);
}
async Task OkClick()
async Task OkClick(bool shouldClose = true)
{
Close();
if (shouldClose)
{
Close();
}
if(Min.HasValue && CurrentDate < Min.Value || Max.HasValue && CurrentDate > Max.Value)
{
@@ -216,12 +234,12 @@ namespace Radzen.Blazor
if (monthDropDown != null)
{
await monthDropDown.ClosePopup();
await monthDropDown.PopupClose();
}
if (yearDropDown != null)
{
await yearDropDown.ClosePopup();
await yearDropDown.PopupClose();
}
}
}
@@ -298,6 +316,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.
@@ -679,7 +703,7 @@ namespace Radzen.Blazor
private string ButtonClasses
{
get => $"rz-button-icon-left rzi rzi-{(TimeOnly ? "time" : "calendar")}";
get => $"notranslate rz-button-icon-left rzi rzi-{(TimeOnly ? "time" : "calendar")}";
}
/// <summary>
@@ -890,7 +914,7 @@ namespace Radzen.Blazor
private string getStyle()
{
return $"display: inline-block;{(Inline ? "overflow:auto;" : "")}{(Style != null ? Style : "")}";
return $"{(Inline ? "overflow:auto;" : "")}{(Style != null ? Style : "")}";
}
/// <summary>
@@ -920,11 +944,11 @@ namespace Radzen.Blazor
{
if (Inline)
{
return "white-space: nowrap";
return "";
}
else
{
return $"width: 320px; {contentStyle}";
return $"{contentStyle}";
}
}
}
@@ -936,6 +960,10 @@ namespace Radzen.Blazor
DateTimeOffset? offset = DateTime.SpecifyKind((DateTime)Value, Kind);
await ValueChanged.InvokeAsync((TValue)(object)offset);
}
else if ((typeof(TValue) == typeof(DateTime) || typeof(TValue) == typeof(DateTime?)) && Value != null)
{
await ValueChanged.InvokeAsync((TValue)(object)DateTime.SpecifyKind((DateTime)Value, Kind));
}
else
{
await ValueChanged.InvokeAsync((TValue)Value);
@@ -950,7 +978,7 @@ namespace Radzen.Blazor
protected override string GetComponentCssClass()
{
return ClassList.Create()
.Add("rz-calendar-inline", Inline)
.Add("rz-datepicker-inline", Inline)
.Add(FieldIdentifier, EditContext)
.ToString();
}
@@ -960,7 +988,7 @@ namespace Radzen.Blazor
if (ShowTimeOkButton)
{
CurrentDate = new DateTime(newValue.Year, newValue.Month, newValue.Day, CurrentDate.Hour, CurrentDate.Minute, CurrentDate.Second);
await OkClick();
await OkClick(!ShowTime);
}
else
{
@@ -972,9 +1000,7 @@ namespace Radzen.Blazor
Close();
}
}
#if NET5_0_OR_GREATER
await FocusAsync();
#endif
}
private void SetMonth(int month)
@@ -1116,9 +1142,7 @@ namespace Radzen.Blazor
if (PopupRenderMode == PopupRenderMode.OnDemand && !Disabled && !ReadOnly && !Inline)
{
await popup.ToggleAsync(Element);
#if NET5_0_OR_GREATER
await FocusAsync();
#endif
}
}
DateTime FocusedDate { get; set; } = DateTime.Now;
@@ -1127,9 +1151,9 @@ namespace Radzen.Blazor
{
return ClassList.Create()
.Add("rz-state-default", !forCell)
.Add("rz-datepicker-other-month", CurrentDate.Month != date.Month)
.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-datepicker-today", !forCell && DateTime.Now.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();
@@ -1156,30 +1180,27 @@ namespace Radzen.Blazor
{
preventKeyPress = true;
await SetDay(FocusedDate);
if (!DateAttributes(FocusedDate).Disabled)
{
await SetDay(FocusedDate);
await ClosePopup();
#if NET5_0_OR_GREATER
await FocusAsync();
#endif
await ClosePopup();
await FocusAsync();
}
}
else if (key == "Escape")
{
preventKeyPress = false;
await ClosePopup();
#if NET5_0_OR_GREATER
await FocusAsync();
#endif
}
else if (key == "Tab")
{
preventKeyPress = false;
await ClosePopup();
#if NET5_0_OR_GREATER
await FocusAsync();
#endif
}
else
{
@@ -1195,9 +1216,7 @@ namespace Radzen.Blazor
preventKeyPress = false;
await ClosePopup();
#if NET5_0_OR_GREATER
await FocusAsync();
#endif
}
}
@@ -1230,9 +1249,7 @@ namespace Radzen.Blazor
preventKeyPress = false;
await ClosePopup();
#if NET5_0_OR_GREATER
await FocusAsync();
#endif
}
else
{
@@ -1242,6 +1259,8 @@ namespace Radzen.Blazor
internal async Task TogglePopup()
{
if (Inline) return;
if (PopupRenderMode == PopupRenderMode.Initial)
{
await JSRuntime.InvokeVoidAsync("Radzen.togglePopup", Element, PopupID, false, null, null, true, true);
@@ -1254,6 +1273,8 @@ namespace Radzen.Blazor
async Task ClosePopup()
{
if (Inline) return;
if (PopupRenderMode == PopupRenderMode.Initial)
{
await JSRuntime.InvokeVoidAsync("Radzen.closePopup", PopupID);
@@ -1265,7 +1286,7 @@ namespace Radzen.Blazor
}
bool preventKeyPress = false;
#if NET5_0_OR_GREATER
/// <inheritdoc/>
public async ValueTask FocusAsync()
{
@@ -1276,6 +1297,5 @@ namespace Radzen.Blazor
catch
{}
}
#endif
}
}

View File

@@ -22,8 +22,8 @@
<div class="rz-dialog-side-title" style="display: inline" id="rz-dialog-side-label">@((MarkupString)sideDialogOptions.Title)</div>
@if (sideDialogOptions.ShowClose)
{
<a aria-label="@CloseSideDialogAriaLabel" @onclick:preventDefault="true" class="rz-dialog-side-titlebar-close" @onclick="@(_ => Service.CloseSide(null))" role="button" tabindex="@sideDialogOptions.CloseTabIndex">
<span class="rzi rzi-times"></span>
<a id="@(sideDialogOptions.GetHashCode() + "cl")" aria-label="@CloseSideDialogAriaLabel" @onclick:preventDefault="true" class="rz-dialog-side-titlebar-close" @onclick="@(_ => Service.CloseSide(null))" role="button" tabindex="@sideDialogOptions.CloseTabIndex">
<span class="notranslate rzi rzi-times"></span>
</a>
}
</div>
@@ -145,4 +145,14 @@
return $"{widthStyle}{heightStyle}{sideDialogOptions.Style}";
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);
if (isSideDialogOpen)
{
await JSRuntime.InvokeAsync<string>("Radzen.openSideDialog", sideDialogOptions);
}
}
}

View File

@@ -36,15 +36,16 @@
var y = CenterY;
var radius = CurrentRadius;
var innerRadius = InnerRadius ?? radius / 2;
var items = PositiveItems;
return
@<g>
<g class="@className">
@if (PositiveItems.Any())
@if (items.Any())
{
var sum = PositiveItems.Sum(Value);
var sum = items.Sum(Value);
var startAngle = StartAngle;
@foreach (var data in PositiveItems)
@foreach (var data in items)
{
var value = Value(data);
var sweepAngle = (value / sum) * TotalAngle;

View File

@@ -1,6 +1,5 @@
@using Radzen
@using System.Linq
@using System.Linq.Dynamic.Core
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.JSInterop
@using Microsoft.AspNetCore.Components.Rendering
@@ -14,7 +13,7 @@
<div class="rz-helper-hidden-accessible">
<input disabled="@Disabled" aria-haspopup="listbox" readonly="" type="text" tabindex="-1"
name="@Name" value="@(internalValue != null ? internalValue : "")" id="@Name"
aria-label="@(!Multiple && internalValue != null ? internalValue : "")" @attributes="InputAttributes"/>
aria-label="@(!Multiple && internalValue != null ? internalValue : EmptyAriaLabel)" @attributes="InputAttributes"/>
</div>
@if (ValueTemplate != null && selectedItem != null)
@@ -55,7 +54,7 @@
@GetItemOrValueFromProperty(item, TextProperty)
}
</span>
<button tabindex="0" title="@(RemoveChipTitle + " " + GetItemOrValueFromProperty(item, TextProperty))" type=button class="rz-button rz-button-sm rz-button-icon-only rz-light @(Disabled ?"rz-state-disabled":"")" @onclick:preventDefault @onclick:stopPropagation @onclick="() => OnChipRemove(item)"><RadzenIcon Icon="close" /></button>
<button tabindex="0" title="@(RemoveChipTitle + " " + GetItemOrValueFromProperty(item, TextProperty))" type=button class="rz-button rz-button-sm rz-button-icon-only rz-base rz-shade-default @(Disabled ?"rz-state-disabled":"")" @onclick:preventDefault @onclick:stopPropagation @onclick="() => OnChipRemove(item)"><RadzenIcon Icon="close" /></button>
</div>
}
</div>
@@ -107,22 +106,30 @@
}
<div class="rz-dropdown-trigger rz-corner-right">
<span class="rz-dropdown-trigger-icon rzi rzi-chevron-down"></span>
<span class="notranslate rz-dropdown-trigger-icon rzi rzi-chevron-down"></span>
</div>
<div id="@PopupID" class="@(Multiple ? "rz-multiselect-panel " : "rz-dropdown-panel ")"
<div id="@PopupID" class="@(Multiple ? "rz-multiselect-panel" : "rz-dropdown-panel")"
style="display:none; box-sizing: border-box">
@if (AllowFiltering && !Multiple)
@if(!Multiple && (AllowFiltering || HeaderTemplate != null))
{
<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"
@onchange="@((ChangeEventArgs args) => OnFilter(args))" @onkeydown="@((args) => OnFilterKeyPress(args))" value="@searchText"
@oninput=@(args => { searchText = $"{args.Value}"; SearchTextChanged.InvokeAsync(searchText);}) />
<span class="rz-dropdown-filter-icon rzi rzi-search"></span>
</div>
@if (HeaderTemplate != null)
{
@HeaderTemplate
}
else
{
@RenderFilter()
}
}
@if (Multiple && (AllowSelectAll || AllowFiltering))
@if (Multiple && (AllowSelectAll || AllowFiltering || HeaderTemplate != null))
{
@if (HeaderTemplate != null)
{
@HeaderTemplate
}
else
{
<div class="rz-multiselect-header rz-helper-clearfix" @onclick:preventDefault>
@if(AllowSelectAll && Data != null && Data.Cast<object>().Any())
{
@@ -130,8 +137,8 @@
<div class="rz-helper-hidden-accessible">
<input readonly="readonly" type="checkbox" id="@($"{(Name ?? UniqueID + "sa")}")" aria-label="@SearchAriaLabel" aria-checked="@(IsAllSelected().ToString().ToLowerInvariant())">
</div>
<div class="@(IsAllSelected() ? "rz-chkbox-box rz-state-active" : "rz-chkbox-box")">
<span class="@(IsAllSelected() ? "rz-chkbox-icon rzi rzi-check" : "rz-chkbox-icon")"></span>
<div class="@(IsAllSelected() ? "notranslate rz-chkbox-box rz-state-active" : "notranslate rz-chkbox-box")">
<span class="@(IsAllSelected() ? "notranslate rz-chkbox-icon rzi rzi-check" : "notranslate rz-chkbox-icon")"></span>
</div>
</div>
}
@@ -146,13 +153,14 @@
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" />
<span class="rz-multiselect-filter-icon rzi rzi-search"></span>
<span class="notranslate rz-multiselect-filter-icon rzi rzi-search"></span>
</div>
}
<a class="rz-multiselect-close " @onclick="@ClearAll" @onclick:stopPropagation="true">
<span class="rzi rzi-times"></span>
<a id="@(GetId() + "clear")" class="rz-multiselect-close " @onclick="@ClearAll" @onclick:stopPropagation="true">
<span class="notranslate rzi rzi-times"></span>
</a>
</div>
}
}
<div class="@(Multiple ? "rz-multiselect-items-wrapper" : "rz-dropdown-items-wrapper")" style="@PopupStyle">
@if (Data != null && Data.Cast<object>().Any())
@@ -172,7 +180,35 @@
</div>
@if (AllowClear && (!Multiple && HasValue || Multiple && selectedItems.Count > 0))
{
<i class="rz-dropdown-clear-icon rzi rzi-times" @onclick="@ClearAll" @onclick:stopPropagation="true"></i>
<i class="notranslate rz-dropdown-clear-icon rzi rzi-times" @onclick="@ClearAll" @onclick:stopPropagation="true"></i>
}
</div>
}
@code {
internal RenderFragment RenderFilter()
{
#if NET7_0_OR_GREATER
return __builder => {
<text>
<div class="rz-dropdown-filter-container">
<input aria-label="@SearchAriaLabel" id="@SearchID" @ref="@search" tabindex="@(Disabled ? "-1" : $"{TabIndex}")" placeholder="@FilterPlaceholder" class="rz-dropdown-filter rz-inputtext" autocomplete="off" 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>
</div>
</text>
};
#else
return __builder => {
<text>
<div class="rz-dropdown-filter-container">
<input aria-label="@SearchAriaLabel" id="@SearchID" @ref="@search" tabindex="@(Disabled ? "-1" : $"{TabIndex}")" placeholder="@FilterPlaceholder" class="rz-dropdown-filter rz-inputtext" autocomplete="off" 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>
</div>
</text>
};
#endif
}
}

View File

@@ -4,6 +4,7 @@ using Microsoft.JSInterop;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Web;
using System.Collections.Generic;
using System;
namespace Radzen.Blazor
{
@@ -67,6 +68,32 @@ namespace Radzen.Blazor
[Parameter]
public string FilterPlaceholder { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the row render callback. Use it to set row attributes.
/// </summary>
/// <value>The row render callback.</value>
[Parameter]
public Action<DropDownItemRenderEventArgs<TValue>> ItemRender { get; set; }
internal DropDownItemRenderEventArgs<TValue> ItemAttributes(RadzenDropDownItem<TValue> item)
{
var disabled = !string.IsNullOrEmpty(DisabledProperty) ? GetItemOrValueFromProperty(item.Item, DisabledProperty) : false;
var args = new DropDownItemRenderEventArgs<TValue>()
{
DropDown = this,
Item = item.Item,
Disabled = disabled is bool ? (bool)disabled : false,
};
if (ItemRender != null)
{
ItemRender(args);
}
return args;
}
private async Task OnFocus(Microsoft.AspNetCore.Components.Web.FocusEventArgs args)
{
if (OpenOnFocus)
@@ -74,6 +101,24 @@ namespace Radzen.Blazor
await OpenPopup("Enter", false);
}
}
internal override async Task ClosePopup(string key)
{
bool of = false;
if (key == "Enter")
{
of = OpenOnFocus;
OpenOnFocus = false;
}
await JSRuntime.InvokeVoidAsync("Radzen.closePopup", PopupID);
if (key == "Enter")
{
await JSRuntime.InvokeVoidAsync("Radzen.focusElement", UniqueID);
OpenOnFocus = of;
}
}
/// <summary>
/// Opens the popup.
@@ -89,7 +134,7 @@ namespace Radzen.Blazor
await JSRuntime.InvokeVoidAsync(OpenOnFocus ? "Radzen.openPopup" : "Radzen.togglePopup", Element, PopupID, true);
await JSRuntime.InvokeVoidAsync("Radzen.focusElement", isFilter ? UniqueID : SearchID);
if (list != null)
if (list != null && selectedIndex != -1)
{
await JSRuntime.InvokeVoidAsync("Radzen.selectListItem", search, list, selectedIndex);
}
@@ -150,6 +195,17 @@ namespace Radzen.Blazor
private bool disabledChanged = false;
private bool firstRender = true;
/// <inheritdoc />
protected override Task SelectAll()
{
if (ReadOnly)
{
return Task.CompletedTask;
}
return base.SelectAll();
}
/// <inheritdoc />
public override async Task SetParametersAsync(ParameterView parameters)
{
@@ -274,7 +330,7 @@ namespace Radzen.Blazor
}
}
internal async Task ClosePopup()
internal async Task PopupClose()
{
await JSRuntime.InvokeVoidAsync("Radzen.closePopup", PopupID);
}

View File

@@ -1,7 +1,6 @@
@using Radzen
@using System.Collections
@using System.Collections.Generic
@using System.Linq.Dynamic.Core
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.JSInterop
@typeparam TValue
@@ -14,7 +13,7 @@
@onkeydown="@((args) => OnKeyPress(args))" @onkeydown:preventDefault="@preventKeydown">
<div class="rz-helper-hidden-accessible">
<input tabindex="-1" disabled="@Disabled" style="width:100%" aria-haspopup="listbox" readonly="" type="text" id="@Name"
name="@Name" aria-label="@(!Multiple && internalValue != null ? PropertyAccess.GetItemOrValueFromProperty(internalValue, TextProperty) : "")" @attributes="InputAttributes"/>
name="@Name" aria-label="@(!Multiple && internalValue != null ? PropertyAccess.GetItemOrValueFromProperty(internalValue, TextProperty) : EmptyAriaLabel)" @attributes="InputAttributes" />
</div>
@@ -39,23 +38,27 @@
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;
@if (itemsToUse.Count < MaxSelectedLabels && Template == null && Chips)
@if (itemsToUse.Count < MaxSelectedLabels && Chips)
{
<div class="rz-dropdown-chips-wrapper">
@foreach (var item in itemsToUse)
{
<div class="rz-chip">
<span class="rz-chip-text" @onkeydown:stopPropagation>
@if (Template != null)
@if (ValueTemplate != null)
{
@ValueTemplate(item)
}
else if (Template != null)
{
@Template(item)
}
else
{
@PropertyAccess.GetItemOrValueFromProperty(item, TextProperty)
@GetItemOrValueFromProperty(item, TextProperty)
}
</span>
<button tabindex="0" title="@(RemoveChipTitle + " " + GetItemOrValueFromProperty(item, TextProperty))" type="button" class="rz-button rz-button-sm rz-button-icon-only rz-light @(Disabled ?"rz-state-disabled":"")" @onclick:preventDefault @onclick:stopPropagation @onclick="() => OnChipRemove(item)"><RadzenIcon Icon="close" /></button>
<button tabindex="0" title="@(RemoveChipTitle + " " + GetItemOrValueFromProperty(item, TextProperty))" type="button" class="rz-button rz-button-sm rz-button-icon-only rz-base rz-shade-default @(Disabled ?"rz-state-disabled":"")" @onclick:preventDefault @onclick:stopPropagation @onclick="() => OnChipRemove(item)"><RadzenIcon Icon="close" /></button>
</div>
}
</div>
@@ -105,35 +108,40 @@
}
<div class="rz-dropdown-trigger rz-corner-right">
<span class="rz-dropdown-trigger-icon rzi rzi-chevron-down"></span>
<span class="notranslate rz-dropdown-trigger-icon rzi rzi-chevron-down"></span>
</div>
<div id="@PopupID" class="@(Multiple ? "rz-multiselect-panel" : "rz-dropdown-panel")"
style="display:none;min-width:400px;padding:0px;">
<div class="rz-lookup-panel" @onkeydown="@CloseOnEscape">
@if (AllowFiltering)
@if (AllowFiltering || HeaderTemplate != null)
{
@if (HeaderTemplate != null)
{
@HeaderTemplate
}
else
{
<div class="rz-lookup-search">
<input aria-label="@SearchAriaLabel" class="rz-lookup-search-input" id="@SearchID" @ref="@search" tabindex="0" placeholder="@SearchTextPlaceholder"
@onchange="@((args) => OnFilter(args))" @onkeydown="@((args) => OnFilterKeyPress(args))" value="@searchText" style="@(ShowSearch ? "" : "margin-right:0px;")"
@oninput=@(args => { searchText = $"{args.Value}"; SearchTextChanged.InvokeAsync(searchText);}) />
@RenderFilter()
@if (ShowSearch)
{
<button tabindex="0" class="rz-button rz-button-md rz-button-icon-only rz-primary" type="button" title="" @onclick="@((args) => OnFilter(new ChangeEventArgs()))">
<i class="rz-button-icon-left rzi">search</i>
<button tabindex="0" class="rz-button rz-button-md rz-button-icon-only rz-primary" type="button" title="@SearchTextPlaceholder" @onclick="@((args) => OnFilter(new ChangeEventArgs()))">
<i class="notranslate rz-button-icon-left rzi">search</i>
</button>
}
@if (ShowAdd)
{
<button tabindex="0" class="rz-button rz-button-md rz-button-icon-only rz-primary" style="margin-left: 5px" type="button" title="" @onclick="@OnAddClick">
<i class="rz-button-icon-left rzi">add</i>
<button tabindex="0" class="rz-button rz-button-md rz-button-icon-only rz-primary" style="margin-left: 5px" type="button" title="@AddAriaLabel" @onclick="@OnAddClick">
<i class="notranslate rz-button-icon-left rzi">add</i>
</button>
}
</div>
}
}
@if (Template != null)
{
<RadzenDataGrid AllowVirtualization="@IsVirtualizationAllowed()" VirtualizationOverscanCount="@GetVirtualizationOverscanCount()" AllowRowSelectOnRowClick="@AllowRowSelectOnRowClick" PagerHorizontalAlign="@PagerHorizontalAlign" PagerAlwaysVisible="@PagerAlwaysVisible" Responsive="@Responsive" AllowColumnResize="@AllowColumnResize" ColumnWidth="@ColumnWidth" @ref="grid" Data="@(LoadData.HasDelegate ? (Data != null ? Data.Cast<object>() : Enumerable.Empty<object>()) : pagedData)" Count="@(LoadData.HasDelegate ? Count : count)" LoadData="@OnLoadData"
<RadzenDataGrid EmptyTemplate=@EmptyTemplate FooterTemplate=@FooterTemplate AllowVirtualization="@IsVirtualizationAllowed()" VirtualizationOverscanCount="@GetVirtualizationOverscanCount()" AllowRowSelectOnRowClick="@AllowRowSelectOnRowClick" PagerHorizontalAlign="@PagerHorizontalAlign" PagerAlwaysVisible="@PagerAlwaysVisible" Responsive="@Responsive" AllowColumnResize="@AllowColumnResize" ColumnWidth="@ColumnWidth" @ref="grid" Data="@(LoadData.HasDelegate ? (Data != null ? Data.Cast<object>() : Enumerable.Empty<object>()) : pagedData)" Count="@(LoadData.HasDelegate ? Count : count)" LoadData="@OnLoadData"
TItem="object" CellRender="@OnCellRender" RowRender="@OnRowRender" ShowPagingSummary="@ShowPagingSummary" PagingSummaryFormat="@PagingSummaryFormat" PageSize="@PageSize" PageNumbersCount="@PageNumbersCount" AllowPaging="@(!IsVirtualizationAllowed())" AllowSorting="@AllowSorting" RowSelect="@OnRowSelect" Style="@(IsVirtualizationAllowed() ? "height:285px" : "")" Density="@Density" IsLoading="@IsLoading" FirstPageTitle="@FirstPageTitle" FirstPageAriaLabel="@FirstPageAriaLabel" PrevPageAriaLabel="@PrevPageAriaLabel" PrevPageTitle="@PrevPageTitle" NextPageAriaLabel="@NextPageAriaLabel" NextPageTitle="@NextPageTitle" LastPageAriaLabel="@LastPageAriaLabel" LastPageTitle="@LastPageTitle" PageAriaLabelFormat="@PageAriaLabelFormat" PageTitleFormat="@PageTitleFormat">
<Columns>
<RadzenDataGridColumn TItem="object" Property="@TextProperty" Title="@TextProperty" Type="typeof(string)">
@@ -142,16 +150,13 @@
</Template>
</RadzenDataGridColumn>
</Columns>
<EmptyTemplate>
@EmptyTemplate
</EmptyTemplate>
</RadzenDataGrid>
}
else
{
if (!string.IsNullOrEmpty(TextProperty) || Columns != null)
{
<RadzenDataGrid AllowVirtualization="@IsVirtualizationAllowed()" VirtualizationOverscanCount="@GetVirtualizationOverscanCount()" AllowRowSelectOnRowClick="@AllowRowSelectOnRowClick" PagerHorizontalAlign="@PagerHorizontalAlign" PagerAlwaysVisible="@PagerAlwaysVisible" Responsive="@Responsive" AllowColumnResize="@AllowColumnResize" ColumnWidth="@ColumnWidth" EmptyText="@EmptyText" @ref="grid" Data="@(LoadData.HasDelegate ? (Data != null ? Data.Cast<object>() : Enumerable.Empty<object>()) : pagedData)" Count="@(LoadData.HasDelegate ? Count : count)" LoadData="@OnLoadData"
<RadzenDataGrid EmptyTemplate=@EmptyTemplate FooterTemplate=@FooterTemplate AllowVirtualization="@IsVirtualizationAllowed()" VirtualizationOverscanCount="@GetVirtualizationOverscanCount()" AllowRowSelectOnRowClick="@AllowRowSelectOnRowClick" PagerHorizontalAlign="@PagerHorizontalAlign" PagerAlwaysVisible="@PagerAlwaysVisible" Responsive="@Responsive" AllowColumnResize="@AllowColumnResize" ColumnWidth="@ColumnWidth" EmptyText="@EmptyText" @ref="grid" Data="@(LoadData.HasDelegate ? (Data != null ? Data.Cast<object>() : Enumerable.Empty<object>()) : pagedData)" Count="@(LoadData.HasDelegate ? Count : count)" LoadData="@OnLoadData"
TItem="object" CellRender="@OnCellRender" RowRender="@OnRowRender" ShowPagingSummary="@ShowPagingSummary" PagingSummaryFormat="@PagingSummaryFormat" PageSize="@PageSize" PageNumbersCount="@PageNumbersCount" AllowPaging="@(!IsVirtualizationAllowed())" AllowSorting="@AllowSorting" RowSelect="@OnRowSelect" Style="@(IsVirtualizationAllowed() ? "height:285px" : "")" Density="@Density" IsLoading="@IsLoading" FirstPageTitle="@FirstPageTitle" FirstPageAriaLabel="@FirstPageAriaLabel" PrevPageAriaLabel="@PrevPageAriaLabel" PrevPageTitle="@PrevPageTitle" NextPageAriaLabel="@NextPageAriaLabel" NextPageTitle="@NextPageTitle" LastPageAriaLabel="@LastPageAriaLabel" LastPageTitle="@LastPageTitle" PageAriaLabelFormat="@PageAriaLabelFormat" PageTitleFormat="@PageTitleFormat">
<Columns>
@if (Columns != null)
@@ -163,14 +168,11 @@
<RadzenDataGridColumn TItem="object" Property="@TextProperty" Title="@TextProperty" Type="typeof(string)" />
}
</Columns>
<EmptyTemplate>
@EmptyTemplate
</EmptyTemplate>
</RadzenDataGrid>
}
else
{
<RadzenDataGrid AllowVirtualization="@IsVirtualizationAllowed()" VirtualizationOverscanCount="@GetVirtualizationOverscanCount()" AllowRowSelectOnRowClick="@AllowRowSelectOnRowClick" PagerHorizontalAlign="@PagerHorizontalAlign" PagerAlwaysVisible="@PagerAlwaysVisible" Responsive="@Responsive" AllowColumnResize="@AllowColumnResize" ColumnWidth="@ColumnWidth" EmptyText="@EmptyText" @ref="grid" Data="@(LoadData.HasDelegate ? (Data != null ? Data.Cast<object>() : Enumerable.Empty<object>()) : pagedData)" Count="@(LoadData.HasDelegate ? Count : count)" LoadData="@OnLoadData"
<RadzenDataGrid EmptyTemplate=@EmptyTemplate FooterTemplate=@FooterTemplate AllowVirtualization="@IsVirtualizationAllowed()" VirtualizationOverscanCount="@GetVirtualizationOverscanCount()" AllowRowSelectOnRowClick="@AllowRowSelectOnRowClick" PagerHorizontalAlign="@PagerHorizontalAlign" PagerAlwaysVisible="@PagerAlwaysVisible" Responsive="@Responsive" AllowColumnResize="@AllowColumnResize" ColumnWidth="@ColumnWidth" EmptyText="@EmptyText" @ref="grid" Data="@(LoadData.HasDelegate ? (Data != null ? Data.Cast<object>() : Enumerable.Empty<object>()) : pagedData)" Count="@(LoadData.HasDelegate ? Count : count)" LoadData="@OnLoadData"
TItem="object" CellRender="@OnCellRender" RowRender="@OnRowRender" ShowPagingSummary="@ShowPagingSummary" PagingSummaryFormat="@PagingSummaryFormat" PageSize="@PageSize" PageNumbersCount="@PageNumbersCount" AllowPaging="@(!IsVirtualizationAllowed())" AllowSorting="@AllowSorting" RowSelect="@OnRowSelect" Style="@(IsVirtualizationAllowed() ? "height:285px" : "")" Density="@Density" IsLoading="@IsLoading" FirstPageTitle="@FirstPageTitle" FirstPageAriaLabel="@FirstPageAriaLabel" PrevPageAriaLabel="@PrevPageAriaLabel" PrevPageTitle="@PrevPageTitle" NextPageAriaLabel="@NextPageAriaLabel" NextPageTitle="@NextPageTitle" LastPageAriaLabel="@LastPageAriaLabel" LastPageTitle="@LastPageTitle" PageAriaLabelFormat="@PageAriaLabelFormat" PageTitleFormat="@PageTitleFormat">
<Columns>
<RadzenDataGridColumn TItem="object" Property="it" Title="Item" Type="typeof(string)">
@@ -179,9 +181,6 @@
</Template>
</RadzenDataGridColumn>
</Columns>
<EmptyTemplate>
@EmptyTemplate
</EmptyTemplate>
</RadzenDataGrid>
}
}
@@ -190,7 +189,29 @@
@if (AllowClear && (!Multiple && HasValue || Multiple && (selectedItems.Count > 0 || SelectedValue is IEnumerable)))
{
<i class="rz-dropdown-clear-icon rzi rzi-times" @onclick="@Clear" @onclick:stopPropagation="true"></i>
<i class="notranslate rz-dropdown-clear-icon rzi rzi-times" @onclick="@Clear" @onclick:stopPropagation="true"></i>
}
</div>
}
@code {
internal RenderFragment RenderFilter()
{
#if NET7_0_OR_GREATER
return __builder => {
<text>
<input aria-label="@SearchAriaLabel" class="rz-lookup-search-input" id="@SearchID" @ref="@search" tabindex="0" placeholder="@SearchTextPlaceholder"
@onchange="@OnFilter" @onkeydown="@OnFilterKeyPress" style="@(ShowSearch ? "" : "margin-right:0px;")"
@bind:event="oninput" @bind:get="searchText" @bind:set=@(args => { searchText = $"{args}"; SearchTextChanged.InvokeAsync(searchText);}) />
</text>
};
#else
return __builder => {
<text>
<input aria-label="@SearchAriaLabel" class="rz-lookup-search-input" id="@SearchID" @ref="@search" tabindex="0" placeholder="@SearchTextPlaceholder"
@onchange="@OnFilter" @onkeydown="@OnFilterKeyPress" value="@searchText" style="@(ShowSearch ? "" : "margin-right:0px;")"
@oninput=@((ChangeEventArgs args) => { searchText = $"{args.Value}"; SearchTextChanged.InvokeAsync(searchText);}) />
</text>
};
#endif
}
}

View File

@@ -42,9 +42,16 @@ namespace Radzen.Blazor
[Parameter]
public Action<DataGridCellRenderEventArgs<object>> CellRender { get; set; }
/// <summary>
/// Gets or sets the footer template.
/// </summary>
/// <value>The footer template.</value>
[Parameter]
public RenderFragment FooterTemplate { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the selected items will be displayed as chips. Set to <c>false</c> by default.
/// Requires <see cref="DropDownBase{T}.Multiple" /> to be set to <c>true</c>.
/// Requires <see cref="DropDownBase{T}.Multiple" /> to be set to <c>true</c>.
/// </summary>
/// <value><c>true</c> to display the selected items as chips; otherwise, <c>false</c>.</value>
[Parameter]
@@ -102,12 +109,12 @@ namespace Radzen.Blazor
{
if (Disabled)
return;
#if NET5_0_OR_GREATER
if (IsVirtualizationAllowed())
{
await grid.RefreshDataAsync();
}
#endif
await JSRuntime.InvokeVoidAsync(OpenOnFocus ? "Radzen.openPopup" : "Radzen.togglePopup", Element, PopupID, true);
if (FocusFilterOnPopup)
@@ -263,19 +270,19 @@ namespace Radzen.Blazor
/// </summary>
[Parameter]
public string NextPageAriaLabel { get; set; } = "Go to next page.";
/// <summary>
/// Gets or sets the pager's numeric page number buttons' title attributes.
/// </summary>
[Parameter]
public string PageTitleFormat { get; set; } = "Page {0}";
/// <summary>
/// Gets or sets the pager's numeric page number buttons' aria-label attributes.
/// </summary>
[Parameter]
public string PageAriaLabelFormat { get; set; } = "Go to page {0}.";
/// <summary>
/// Gets or sets the empty text.
/// </summary>
@@ -290,6 +297,12 @@ namespace Radzen.Blazor
[Parameter]
public string SearchTextPlaceholder { get; set; } = "Search...";
/// <summary>
/// Gets or sets the add button aria-label attribute.
/// </summary>
[Parameter]
public string AddAriaLabel { get; set; } = "Add";
/// <summary>
/// Gets or sets the selected value.
/// </summary>
@@ -315,22 +328,6 @@ namespace Radzen.Blazor
[Parameter]
public int MaxSelectedLabels { get; set; } = 4;
#if !NET5_0_OR_GREATER
/// <summary>
/// Gets or sets the page size.
/// </summary>
/// <value>The page size.</value>
[Parameter]
public int PageSize { get; set; } = 5;
/// <summary>
/// Gets or sets the total items count.
/// </summary>
/// <value>The total items count.</value>
[Parameter]
public int Count { get; set; }
#endif
/// <summary>
/// Gets or sets the selected items text.
/// </summary>
@@ -355,19 +352,32 @@ namespace Radzen.Blazor
/// </summary>
/// <param name="firstRender">if set to <c>true</c> [first render].</param>
/// <returns>Task.</returns>
protected override Task OnAfterRenderAsync(bool firstRender)
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
{
if(Visible && LoadData.HasDelegate && Data == null)
{
LoadData.InvokeAsync(new Radzen.LoadDataArgs() { Skip = 0, Top = PageSize, Filter = searchText });
await LoadData.InvokeAsync(new Radzen.LoadDataArgs() { Skip = 0, Top = PageSize, Filter = searchText });
}
StateHasChanged();
if (!Multiple && grid != null && SelectedItem != null)
{
var items = (LoadData.HasDelegate ? Data != null ? Data : Enumerable.Empty<object>() : (pagedData != null ? pagedData : Enumerable.Empty<object>())).OfType<object>().ToList();
if (items.Any())
{
selectedIndex = items.IndexOf(SelectedItem);
if (selectedIndex >= 0)
{
await JSRuntime.InvokeAsync<int[]>("Radzen.focusTableRow", grid.GridId(), "ArrowDown", selectedIndex - 1, null);
}
}
}
}
return base.OnAfterRenderAsync(firstRender);
await base.OnAfterRenderAsync(firstRender);
}
/// <summary>
@@ -420,6 +430,8 @@ namespace Radzen.Blazor
private bool IsColumnFilterPropertyTypeString(RadzenDataGridColumn<object> column)
{
if (column.Type == typeof(string)) return true;
var property = column.GetFilterProperty();
var itemType = Data != null ? Data.AsQueryable().ElementType : typeof(object);
var type = PropertyAccess.GetPropertyType(itemType, property);
@@ -458,16 +470,16 @@ namespace Radzen.Blazor
foreach (string word in words)
{
query = query.Where(string.Join(" || ", grid.ColumnsCollection.Where(c => c.Filterable && IsColumnFilterPropertyTypeString(c))
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);
}
}
else
{
query = query.Where(string.Join(" || ", grid.ColumnsCollection.Where(c => c.Filterable && IsColumnFilterPropertyTypeString(c))
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);
FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive ? searchText.ToLower() : searchText);
}
}
else
@@ -478,13 +490,13 @@ namespace Radzen.Blazor
foreach (string word in words)
{
query = query.Where($"{GetPropertyFilterExpression(TextProperty, filterCaseSensitivityOperator)}",
query = query.Where(DynamicLinqCustomTypeProvider.ParsingConfig, $"{GetPropertyFilterExpression(TextProperty, filterCaseSensitivityOperator)}",
FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive ? word.ToLower() : word);
}
}
else
{
query = query.Where($"{GetPropertyFilterExpression(TextProperty, filterCaseSensitivityOperator)}",
query = query.Where(DynamicLinqCustomTypeProvider.ParsingConfig, $"{GetPropertyFilterExpression(TextProperty, filterCaseSensitivityOperator)}",
FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive ? searchText.ToLower() : searchText);
}
}
@@ -492,7 +504,7 @@ namespace Radzen.Blazor
if (!string.IsNullOrEmpty(args.OrderBy))
{
query = query.OrderBy(args.OrderBy);
query = query.OrderBy(DynamicLinqCustomTypeProvider.ParsingConfig, args.OrderBy);
}
count = await Task.FromResult(query.Count());
@@ -543,21 +555,32 @@ namespace Radzen.Blazor
{
if (!Multiple)
{
bool raiseChange = false;
if (!string.IsNullOrEmpty(ValueProperty))
{
var item = Query.Where($@"{ValueProperty} == @0", value).FirstOrDefault();
if (item != null)
var item = Query.Where(DynamicLinqCustomTypeProvider.ParsingConfig, $@"{ValueProperty} == @0", value).FirstOrDefault();
if (item != null && SelectedItem != item)
{
SelectedItem = item;
raiseChange = true;
}
}
else
{
SelectedItem = internalValue;
if (SelectedItem != internalValue)
{
SelectedItem = internalValue;
raiseChange = true;
}
}
if (raiseChange)
{
SelectedItemChanged.InvokeAsync(SelectedItem);
selectedItems.Clear();
selectedItems.Add(SelectedItem);
}
SelectedItemChanged?.Invoke(SelectedItem);
selectedItems.Clear();
selectedItems.Add(SelectedItem);
}
else
{
@@ -569,8 +592,8 @@ namespace Radzen.Blazor
{
foreach (object v in valueList)
{
var item = Query.Where($@"{ValueProperty} == @0", v).FirstOrDefault();
if (item != null && !selectedItems.AsQueryable().Where($@"object.Equals(it.{ValueProperty},@0)", v).Any())
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())
{
selectedItems.Add(item);
}
@@ -594,6 +617,10 @@ namespace Radzen.Blazor
{
selectedItem = null;
selectedItems.Clear();
if (grid != null)
{
grid.selectedItems.Clear();
}
}
}
@@ -667,7 +694,7 @@ namespace Radzen.Blazor
{
await grid.PrevPage();
}
else
else
{
await grid.NextPage();
}
@@ -692,10 +719,7 @@ namespace Radzen.Blazor
}
}
if (!Multiple)
{
await JSRuntime.InvokeVoidAsync("Radzen.closePopup", PopupID);
}
await CloseAndFocus();
}
}
else if (args.AltKey && key == "ArrowDown")
@@ -760,16 +784,15 @@ namespace Radzen.Blazor
async Task CloseOnEscape(KeyboardEventArgs args)
{
var key = args.Code != null ? args.Code : args.Key;
if (key == "Escape")
var key = args.Code != null ? args.Code : args.Key;
if (key == "Escape")
{
await JSRuntime.InvokeVoidAsync("Radzen.closePopup", PopupID);
await JSRuntime.InvokeVoidAsync("Radzen.closePopup", PopupID);
}
}
async Task RefreshAfterFilter()
{
#if NET5_0_OR_GREATER
if (IsVirtualizationAllowed() && grid != null)
{
if(string.IsNullOrEmpty(searchText))
@@ -802,7 +825,7 @@ namespace Radzen.Blazor
}
}
}
#endif
StateHasChanged();
if (!IsVirtualizationAllowed())
@@ -866,18 +889,27 @@ namespace Radzen.Blazor
async Task OnRowSelect(object item)
{
if (!Disabled && !Multiple)
{
await JSRuntime.InvokeVoidAsync("Radzen.closePopup", PopupID);
}
await JSRuntime.InvokeVoidAsync("Radzen.focusElement", UniqueID);
await CloseAndFocus();
if (AllowRowSelectOnRowClick)
{
await SelectItem(item);
}
}
async Task CloseAndFocus()
{
if (!Disabled && !Multiple)
{
await JSRuntime.InvokeVoidAsync("Radzen.closePopup", PopupID);
}
var of = OpenOnFocus;
OpenOnFocus = false;
await JSRuntime.InvokeVoidAsync("Radzen.focusElement", UniqueID);
OpenOnFocus = of;
}
private async Task OnChipRemove(object item)
@@ -945,7 +977,11 @@ namespace Radzen.Blazor
/// </summary>
public new async Task Reset() {
base.Reset();
await grid.SelectRow(null);
if (!Multiple)
{
await grid.SelectRow(null);
}
}
}
}

View File

@@ -1,18 +1,21 @@
@using Radzen
@using System.Linq.Dynamic.Core
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.JSInterop
@typeparam TValue
@{var itemArgs = DropDown?.ItemAttributes(this); }
@if (itemArgs.Visible)
{
Disabled = itemArgs.Disabled;
@if (DropDown.Multiple)
{
<li class="@GetComponentCssClass("rz-multiselect")"
aria-label="@DropDown.GetItemOrValueFromProperty(Item, DropDown.TextProperty)"
@onmousedown:preventDefault @onmousedown="args=>SelectItem(args,false)"
@onclick:preventDefault @onclick="args=>SelectItem(args,true)">
@onmousedown:preventDefault @onmousedown="args=>SelectItem(args,false)"
@onclick:preventDefault @onclick="args=>SelectItem(args,true)"
@attributes="@(itemArgs.Attributes.Any() ? itemArgs.Attributes : Attributes)">
<div class="rz-chkbox ">
<div class="@(DropDown.IsSelected(Item) ? "rz-chkbox-box rz-state-active" : "rz-chkbox-box") @(Disabled ? " rz-state-disabled " : "")">
<span class="@(DropDown.IsSelected(Item) ? "rz-chkbox-icon rzi rzi-check" : "rz-chkbox-icon")"></span>
<div class="@(DropDown.IsSelected(Item) ? "notranslate rz-chkbox-box rz-state-active" : "notranslate rz-chkbox-box") @(Disabled ? " rz-state-disabled " : "")">
<span class="@(DropDown.IsSelected(Item) ? "notranslate rz-chkbox-icon rzi rzi-check" : "notranslate rz-chkbox-icon")"></span>
</div>
</div>
<span class="rz-multiselect-item-content">
@@ -31,7 +34,8 @@ else
{
<li role="option" class="@GetComponentCssClass("rz-dropdown")" aria-label="@DropDown.GetItemOrValueFromProperty(Item, DropDown.TextProperty)"
@onmousedown:preventDefault @onmousedown="args=>SelectItem(args,false)"
@onclick:preventDefault @onclick="args=>SelectItem(args,true)">
@onclick:preventDefault @onclick="args=>SelectItem(args,true)"
@attributes="@(itemArgs.Attributes.Any() ? itemArgs.Attributes : Attributes)">
<span>
@if (DropDown.Template != null)
{
@@ -44,7 +48,11 @@ else
</span>
</li>
}
}
@code {
[Parameter(CaptureUnmatchedValues = true)]
public IReadOnlyDictionary<string, object> Attributes { get; set; }
[Parameter]
public RadzenDropDown<TValue> DropDown { get; set; }

View File

@@ -0,0 +1,19 @@
@inherits RadzenComponentWithChildren
@typeparam TItem
@if(Visible)
{
<div @ref="@Element" style="@Style" @attributes="Attributes" class="@GetCssClass()" id="@GetId()" @ondrop="OnDrop" @ondragover="OnDragOver" @ondragleave="OnDragLeave">
@ChildContent
@foreach (var item in Items)
{
var result = ItemAttributes(item);
<CascadingValue Value=@item>
<CascadingValue Value=this>
<RadzenDropZoneItem TItem="TItem" Visible="@result.Item1.Visible" Attributes="@result.Item2" />
</CascadingValue>
</CascadingValue>
}
@Footer
</div>
}

View File

@@ -0,0 +1,117 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.JSInterop;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
namespace Radzen.Blazor
{
/// <summary>
/// RadzenDropZone component.
/// </summary>
public partial class RadzenDropZone<TItem> : RadzenComponentWithChildren
{
/// <summary>
/// Gets or sets the zone value used to compare items in container Selector function.
/// </summary>
/// <value>The zone value used to compare items in container Selector function.</value>
[Parameter]
public object Value { get; set; }
/// <summary>
/// Gets or sets the Footer Templated
/// The Footer Template is rendered below the items in the <see cref="RadzenDropZone{TItem}" />
/// </summary>
[Parameter]
public RenderFragment Footer { get; set; }
[CascadingParameter]
RadzenDropZoneContainer<TItem> Container { get; set; }
IEnumerable<TItem> Items
{
get
{
return Container.ItemSelector != null ? Container.Data.Where(i => Container.ItemSelector(i, this)) : Enumerable.Empty<TItem>();
}
}
internal bool CanDrop()
{
if (Container.Payload != null)
{
Container.Payload.ToZone = this;
Container.Payload.FromZone = Container.Payload.FromZone;
Container.Payload.Item = Container.Payload.Item;
}
var canDrop = Container.CanDrop != null && Container.Payload != null ? Container.CanDrop(Container.Payload) : true;
return canDrop;
}
internal void OnDragOver(DragEventArgs args)
{
var canDrop = CanDrop();
args.DataTransfer.DropEffect = canDrop ? "move" : "none";
cssClass = canDrop ? "rz-can-drop" : "rz-no-drop";
}
void OnDragLeave(DragEventArgs args)
{
cssClass = "";
}
async Task OnDrop(DragEventArgs args)
{
cssClass = "";
await OnDropInternal();
}
internal async Task OnDropInternal()
{
if (CanDrop())
{
await Container.Drop.InvokeAsync(Container.Payload);
}
}
/// <inheritdoc />
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);
if (Visible)
{
await JSRuntime.InvokeVoidAsync("Radzen.prepareDrag", Element);
}
}
string cssClass;
/// <inheritdoc />
protected override string GetComponentCssClass()
{
return $"rz-dropzone {cssClass}".Trim();
}
Tuple<RadzenDropZoneItemRenderEventArgs<TItem>, IReadOnlyDictionary<string, object>> ItemAttributes(TItem item)
{
var args = new RadzenDropZoneItemRenderEventArgs<TItem>()
{
Zone = this,
Item = item
};
if (Container.ItemRender != null)
{
Container.ItemRender(args);
}
return new Tuple<RadzenDropZoneItemRenderEventArgs<TItem>, IReadOnlyDictionary<string, object>>(args, new ReadOnlyDictionary<string, object>(args.Attributes));
}
}
}

View File

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

View File

@@ -0,0 +1,65 @@
using Microsoft.AspNetCore.Components;
using System;
using System.Collections.Generic;
namespace Radzen.Blazor
{
/// <summary>
/// RadzenDropZoneContainer component.
/// </summary>
#if NET6_0_OR_GREATER
[CascadingTypeParameter(nameof(TItem))]
#endif
public partial class RadzenDropZoneContainer<TItem> : RadzenComponentWithChildren
{
/// <summary>
/// Gets or sets the data.
/// </summary>
/// <value>The data.</value>
[Parameter]
public IEnumerable<TItem> Data { get; set; }
/// <summary>
/// Gets or sets the selector function for zone items.
/// </summary>
/// <value>The selector function for zone items.</value>
[Parameter]
public Func<TItem, RadzenDropZone<TItem>, bool> ItemSelector { get; set; }
/// <summary>
/// Gets or sets the function that checks if the item can be dropped in specific zone or item.
/// </summary>
/// <value>The function that checks if the item can be dropped in specific zone.</value>
[Parameter]
public Func<RadzenDropZoneItemEventArgs<TItem>, bool> CanDrop { 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<RadzenDropZoneItemRenderEventArgs<TItem>> ItemRender { get; set; }
/// <summary>
/// Gets or sets the template for zone items.
/// </summary>
/// <value>The template for zone items.</value>
[Parameter]
public RenderFragment<TItem> Template { get; set; }
/// <summary>
/// The event callback raised on item drop.
/// </summary>
/// <value>The event callback raised on item drop.</value>
[Parameter]
public EventCallback<RadzenDropZoneItemEventArgs<TItem>> Drop { get; set; }
internal RadzenDropZoneItemEventArgs<TItem> Payload { get; set; }
/// <inheritdoc />
protected override string GetComponentCssClass()
{
return "rz-dropzone-container";
}
}
}

View File

@@ -0,0 +1,17 @@
@inherits RadzenComponent
@typeparam TItem
@if(Visible)
{
<div @ref="@Element" style="@Style" @attributes="Attributes" class="@GetCssClass()" id="@GetId()"
draggable="@(Attributes.ContainsKey("draggable") ? Attributes["draggable"] : "true")"
@ondragstart="@OnDragStart" @ondragover="OnDragOver" @ondragleave="OnDragLeave" @ondragend="OnDragEnd" @ondrop="OnDrop">
@if (Container.Template != null)
{
@Container.Template(Item)
}
else
{
@Item
}
</div>
}

View File

@@ -0,0 +1,68 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using System.Threading.Tasks;
namespace Radzen.Blazor
{
/// <summary>
/// RadzenDropZoneItem component.
/// </summary>
public partial class RadzenDropZoneItem<TItem> : RadzenComponent
{
[CascadingParameter]
TItem Item { get; set; }
[CascadingParameter]
RadzenDropZone<TItem> Zone { get; set; }
[CascadingParameter]
RadzenDropZoneContainer<TItem> Container { get; set; }
void OnDragStart()
{
dragCssClass = "rz-dragging";
Container.Payload = new RadzenDropZoneItemEventArgs<TItem>()
{
FromZone = Zone,
Item = Item
};
}
void OnDragOver(DragEventArgs args)
{
Container.Payload.ToItem = Item;
var canDrop = Zone.CanDrop();
args.DataTransfer.DropEffect = canDrop ? "move" : "none";
cssClass = canDrop ? "rz-can-drop" : "rz-no-drop";
Zone.OnDragOver(args);
}
void OnDragLeave(DragEventArgs args)
{
cssClass = "";
}
void OnDragEnd(DragEventArgs args)
{
dragCssClass = "";
}
async Task OnDrop(DragEventArgs args)
{
cssClass = "";
Container.Payload.ToItem = Item;
await Zone.OnDropInternal();
}
string cssClass;
string dragCssClass;
/// <inheritdoc />
protected override string GetComponentCssClass()
{
return $"rz-dropzone-item {cssClass} {dragCssClass}".Trim();
}
}
}

View File

@@ -9,21 +9,21 @@
@if (AllowCollapse)
{
<a title="@TitleAttribute()" aria-label="@AriaLabelAttribute()" @onclick:preventDefault="true"
<a id="@(GetId() + "expc")" title="@TitleAttribute()" aria-label="@AriaLabelAttribute()" @onclick:preventDefault="true"
aria-controls="rz-fieldset-0-content" aria-expanded="@(collapsed ? "false" : "true")" @onclick=@Toggle
tabindex="0" @onkeypress="@(args => OnKeyPress(args, Toggle(new EventArgs())))" @onkeypress:preventDefault=preventKeyPress @onkeypress:stopPropagation>
@if (collapsed)
{
<span class="rz-fieldset-toggler rzi rzi-w rzi-plus"></span>
<span class="notranslate rz-fieldset-toggler rzi rzi-w rzi-plus"></span>
}
else
{
<span class="rz-fieldset-toggler rzi rzi-w rzi-minus"></span>
<span class="notranslate rz-fieldset-toggler rzi rzi-w rzi-minus"></span>
}
@if (!string.IsNullOrEmpty(Icon))
{
<i class="rzi" style="@(!string.IsNullOrEmpty(IconColor) ? $"color:{IconColor}" : null)">@((MarkupString)Icon)</i><span>@Text</span>
<i class="notranslate rzi" style="@(!string.IsNullOrEmpty(IconColor) ? $"color:{IconColor}" : null)">@((MarkupString)Icon)</i><span>@Text</span>
}
else
{
@@ -36,7 +36,7 @@
{
@if (!string.IsNullOrEmpty(Icon))
{
<i class="rzi" style="@(!string.IsNullOrEmpty(IconColor) ? $"color:{IconColor}" : null)">@((MarkupString)Icon)</i><span>@Text</span>
<i class="notranslate rzi" style="@(!string.IsNullOrEmpty(IconColor) ? $"color:{IconColor}" : null)">@((MarkupString)Icon)</i><span>@Text</span>
}
else
{

View File

@@ -23,7 +23,7 @@
<div>
@if (IsImage)
{
<img style="@ImageStyle" src="@Value" @onclick="@OnImageClick" alt="@ImageAlternateText" />
<img style="@ImageStyle" src="@ImageValue" @onclick="@OnImageClick" alt="@ImageAlternateText" />
}
</div>
<div>

View File

@@ -65,7 +65,7 @@ namespace Radzen.Blazor
/// Gets the button class list.
/// </summary>
/// <value>The button class list.</value>
ClassList ButtonClassList => ClassList.Create("rz-button rz-button-icon-only rz-light")
ClassList ButtonClassList => ClassList.Create("rz-button rz-button-icon-only rz-base rz-shade-default")
.AddDisabled(Disabled);
/// <inheritdoc />
@@ -100,6 +100,23 @@ namespace Radzen.Blazor
}
}
private string ImageValue
{
get
{
if (Value == null)
{
return string.Empty;
}
else if (Value is byte[] bytes)
{
return System.Text.Encoding.Default.GetString(bytes);
}
return Value.ToString();
}
}
async Task OnChange()
{
string uploadValue;
@@ -136,6 +153,11 @@ namespace Radzen.Blazor
[JSInvokable("RadzenUpload.OnChange")]
public async System.Threading.Tasks.Task OnChange(IEnumerable<PreviewFileInfo> files)
{
if(files == null || !files.Any())
{
return;
}
var file = files.FirstOrDefault();
FileSize = file.Size;

View File

@@ -60,6 +60,13 @@ namespace Radzen.Blazor
[Parameter]
public string ApiKey { get; set; }
/// <summary>
/// Gets or sets the Google Map Id.
/// </summary>
/// <value>The Google Map Id.</value>
[Parameter]
public string MapId { get; set; }
/// <summary>
/// Gets or sets the Google map options: https://developers.google.com/maps/documentation/javascript/reference/map#MapOptions.
/// </summary>
@@ -199,7 +206,7 @@ namespace Radzen.Blazor
if (firstRender)
{
await JSRuntime.InvokeVoidAsync("Radzen.createMap", Element, Reference, UniqueID, ApiKey, Zoom, Center,
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);
}
else

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +0,0 @@
namespace Radzen.Blazor
{
/// <summary>
/// RadzenGrid.
/// </summary>
public partial class RadzenGrid<TItem>
{
}
}

View File

@@ -1,51 +0,0 @@
@typeparam TItem
<td style="@Style" @attributes="@Attributes" class="@GetCssClass()" @onclick="@OnClick" @ondblclick="@OnDblClick" >
<CascadingValue Value=this>
@ChildContent
</CascadingValue>
</td>
@code {
[Parameter(CaptureUnmatchedValues = true)]
public IReadOnlyDictionary<string, object> Attributes { get; set; }
[Parameter]
public RenderFragment ChildContent { get; set; }
[Parameter]
public string CssClass { get; set; }
[Parameter]
public string Style { get; set; }
[Parameter]
public TItem Item { get; set; }
[Parameter]
public RadzenGrid<TItem> Grid { get; set; }
async Task OnClick()
{
if (Grid != null)
{
await Grid.OnRowClick(Item);
}
}
async Task OnDblClick()
{
if (Grid != null)
{
await Grid.OnRowDblClick(Item);
}
}
string GetCssClass()
{
if (Attributes != null && Attributes.TryGetValue("class", out var @class) && !string.IsNullOrEmpty(Convert.ToString(@class)))
{
return $"{CssClass} {@class}".Trim();
}
return CssClass;
}
}

View File

@@ -1,341 +0,0 @@
@using Radzen
@using Radzen.Blazor
@implements IDisposable
@typeparam TItem
@code {
public override async Task SetParametersAsync(ParameterView parameters)
{
if (parameters.DidParameterChange(nameof(Visible), Visible) || parameters.DidParameterChange(nameof(Title), Title))
{
if (Grid != null)
{
await Grid.ChangeState();
}
}
await base.SetParametersAsync(parameters);
}
string _title;
[Parameter]
public string Title
{
get
{
return _title;
}
set
{
if (_title != value)
{
_title = value;
}
}
}
[Parameter]
public string Property { get; set; }
[Parameter]
public string SortProperty { get; set; }
[Parameter]
public string FilterProperty { get; set; }
[Parameter]
public object FilterValue { get; set; }
[Parameter]
public object SecondFilterValue { get; set; }
string _filterOperator;
[Parameter]
public string FilterOperator
{
get
{
if (string.IsNullOrEmpty(_filterOperator))
{
_filterOperator = Type == "string" && string.IsNullOrEmpty(Format) ? "contains" : "eq";
}
return _filterOperator;
}
set
{
if (_filterOperator != value)
{
_filterOperator = value;
}
}
}
string _secondFilterOperator;
[Parameter]
public string SecondFilterOperator
{
get
{
if (string.IsNullOrEmpty(_secondFilterOperator))
{
_secondFilterOperator = Type == "string" && string.IsNullOrEmpty(Format) ? "contains" : "eq";
}
return _secondFilterOperator;
}
set
{
if (_secondFilterOperator != value)
{
_secondFilterOperator = value;
}
}
}
[Parameter]
public LogicalFilterOperator LogicalFilterOperator { get; set; } = LogicalFilterOperator.And;
Dictionary<string, string> _filterOperators;
public Dictionary<string, string> FilterOperators
{
get
{
if (_filterOperators == null)
{
_filterOperators = new Dictionary<string, string>();
if (Type == "string" && string.IsNullOrEmpty(Format))
{
_filterOperators.Add("startswith", this.Grid != null ? this.Grid.StartsWithText : "Starts with");
_filterOperators.Add("contains", this.Grid != null ? this.Grid.ContainsText : "Contains");
_filterOperators.Add("endswith", this.Grid != null ? this.Grid.EndsWithText : "Ends with");
}
_filterOperators.Add("eq", this.Grid != null ? this.Grid.EqualsText : "Equals");
_filterOperators.Add("ne", this.Grid != null ? this.Grid.NotEqualsText : "Not equals");
if (Type != "string" || (Type == "string" && !string.IsNullOrEmpty(Format)))
{
_filterOperators.Add("lt", this.Grid != null ? this.Grid.LessThanText : "Less than");
_filterOperators.Add("le", this.Grid != null ? this.Grid.LessThanOrEqualsText : "Less than or equals");
_filterOperators.Add("gt", this.Grid != null ? this.Grid.GreaterThanText : "Greater than");
_filterOperators.Add("ge", this.Grid != null ? this.Grid.GreaterThanOrEqualsText : "Greater than or equals");
}
}
return _filterOperators;
}
}
[Parameter]
public string Width { get; set; }
[Parameter]
public string Type { get; set; } = "string";
[Parameter]
public string Format { get; set; }
[Parameter]
public string FormatString { get; set; }
[Parameter]
public string CssClass { get; set; }
[Parameter]
public string HeaderCssClass { get; set; }
[Parameter]
public string FooterCssClass { get; set; }
[Parameter]
public bool Filterable { get; set; } = true;
[Parameter]
public bool Sortable { get; set; } = true;
[Parameter]
public bool Bubble { get; set; } = true;
[Parameter]
public bool Visible { get; set; } = true;
[Parameter]
public TextAlign TextAlign { get; set; } = TextAlign.Left;
[Parameter]
public RenderFragment<TItem> Template { get; set; }
[Parameter]
public RenderFragment<TItem> EditTemplate { get; set; }
[Parameter]
public RenderFragment HeaderTemplate { get; set; }
[Parameter]
public RenderFragment FooterTemplate { get; set; }
[Parameter]
public RenderFragment<RadzenGridColumn<TItem>> FilterTemplate { get; set; }
RadzenGrid<TItem> _grid;
[CascadingParameter]
public RadzenGrid<TItem> Grid
{
get
{
return _grid;
}
set
{
if (_grid != value)
{
_grid = value;
_grid.AddColumn(this);
var property = GetFilterProperty();
if (!string.IsNullOrEmpty(property))
{
_filterPropertyType = PropertyAccess.GetPropertyType(typeof(TItem), property);
if (_filterPropertyType != null)
{
if (PropertyAccess.IsNumeric(_filterPropertyType))
{
Type = "number";
}
else if (_filterPropertyType == typeof(bool) || _filterPropertyType == typeof(bool?))
{
Type = "boolean";
}
else if (_filterPropertyType == typeof(DateTime) || _filterPropertyType == typeof(DateTime?))
{
Type = "string";
Format = "date-time";
}
else if (_filterPropertyType == typeof(DateTimeOffset) || _filterPropertyType == typeof(DateTimeOffset?))
{
Type = "string";
Format = "date-time-offset";
}
else if (_filterPropertyType == typeof(Guid) || _filterPropertyType == typeof(Guid?))
{
Type = "string";
Format = "uuid";
}
}
}
}
}
}
Type _filterPropertyType;
internal Type FilterPropertyType
{
get
{
return _filterPropertyType;
}
}
public string GetStyle(bool forCell = false)
{
var style = new List<string>();
var width = GetWidth();
if (width != null)
{
style.Add($"width:{width}");
}
else if (Grid != null && Grid.ColumnWidth != null)
{
style.Add($"width:{Grid.ColumnWidth}");
}
if (forCell && TextAlign != TextAlign.Left)
{
style.Add($"text-align:{Enum.GetName(typeof(TextAlign), TextAlign).ToLower()};");
}
return string.Join(";", style);
}
public string GetSortProperty()
{
if (!string.IsNullOrEmpty(SortProperty))
{
return SortProperty;
}
else
{
return Property;
}
}
internal SortOrder? SortOrder { get; set; }
public string GetFilterProperty()
{
if (!string.IsNullOrEmpty(FilterProperty))
{
return FilterProperty;
}
else
{
return Property;
}
}
internal void SetFilterValue(object value, bool isFirst = true)
{
if (Format == "date-time-offset" && value != null && value is DateTime?)
{
DateTimeOffset? offset = DateTime.SpecifyKind((DateTime)value, DateTimeKind.Utc);
value = offset;
}
if (isFirst)
{
FilterValue = value;
}
else
{
SecondFilterValue = value;
}
}
internal void SetFilterOperator(string value)
{
FilterOperator = value;
}
internal void SetSecondFilterOperator(string value)
{
SecondFilterOperator = value;
}
internal void SetLogicalFilterOperator(LogicalFilterOperator value)
{
LogicalFilterOperator = value;
}
string runtimeWidth;
internal void SetWidth(string value)
{
runtimeWidth = value;
}
internal string GetWidth()
{
return !string.IsNullOrEmpty(runtimeWidth) ? runtimeWidth : Width;
}
public void Dispose()
{
Grid?.RemoveColumn(this);
}
}

View File

@@ -120,7 +120,6 @@ namespace Radzen.Blazor
ElementReference ContentEditable { get; set; }
RadzenTextArea TextArea { get; set; }
#if NET5_0_OR_GREATER
/// <summary>
/// Focuses the editor.
/// </summary>
@@ -136,7 +135,6 @@ namespace Radzen.Blazor
return TextArea.Element.FocusAsync();
}
}
#endif
internal RadzenHtmlEditorCommandState State { get; set; } = new RadzenHtmlEditorCommandState();
@@ -192,9 +190,17 @@ namespace Radzen.Blazor
public async Task ExecuteCommandAsync(string name, string value = null)
{
State = await JSRuntime.InvokeAsync<RadzenHtmlEditorCommandState>("Radzen.execCommand", ContentEditable, name, value);
await OnExecuteAsync(name);
Html = State.Html;
await OnChange();
if (Html != State.Html)
{
Html = State.Html;
htmlChanged = true;
await OnChange();
}
}
/// <summary>
@@ -213,7 +219,11 @@ namespace Radzen.Blazor
private async Task SourceChanged(string html)
{
Html = html;
if (Html != html)
{
Html = html;
htmlChanged = true;
}
await JSRuntime.InvokeVoidAsync("Radzen.innerHTML", ContentEditable, Html);
await OnChange();
StateHasChanged();
@@ -221,8 +231,21 @@ namespace Radzen.Blazor
async Task OnChange()
{
await ValueChanged.InvokeAsync(Html);
await Change.InvokeAsync(Html);
if (htmlChanged)
{
htmlChanged = false;
_value = Html;
await ValueChanged.InvokeAsync(Html);
if (FieldIdentifier.FieldName != null)
{
EditContext?.NotifyFieldChanged(FieldIdentifier);
}
await Change.InvokeAsync(Html);
}
}
internal async Task OnExecuteAsync(string name)
@@ -260,6 +283,8 @@ namespace Radzen.Blazor
await OnChange();
}
bool htmlChanged = false;
bool visibleChanged = false;
bool firstRender = true;
@@ -330,7 +355,11 @@ namespace Radzen.Blazor
[JSInvokable]
public void OnChange(string html)
{
Html = html;
if (Html != html)
{
Html = html;
htmlChanged = true;
}
Input.InvokeAsync(html);
}
@@ -376,7 +405,7 @@ namespace Radzen.Blazor
/// <inheritdoc />
protected override string GetComponentCssClass()
{
return "rz-html-editor";
return GetClassList("rz-html-editor").ToString();
}
/// <inheritdoc />
@@ -389,5 +418,41 @@ namespace Radzen.Blazor
JSRuntime.InvokeVoidAsync("Radzen.destroyEditor", ContentEditable);
}
}
/// <summary>
/// Gets or sets the callback which when a file is uploaded.
/// </summary>
/// <value>The complete callback.</value>
[Parameter]
public EventCallback<UploadCompleteEventArgs> UploadComplete { get; set; }
internal async Task RaiseUploadComplete(UploadCompleteEventArgs args)
{
await UploadComplete.InvokeAsync(args);
}
/// <summary>
/// Invoked by interop when the upload is complete.
/// </summary>
[JSInvokable("OnUploadComplete")]
public async Task OnUploadComplete(string response)
{
System.Text.Json.JsonDocument doc = null;
if (!string.IsNullOrEmpty(response))
{
try
{
doc = System.Text.Json.JsonDocument.Parse(response);
}
catch (System.Text.Json.JsonException)
{
//
}
}
await UploadComplete.InvokeAsync(new UploadCompleteEventArgs() { RawResponse = response, JsonResponse = doc });
}
}
}

View File

@@ -15,39 +15,57 @@
Attributes = await Editor.GetSelectionAttributes<ImageAttributes>("img", new[] {"src", "alt", "width", "height"});
var result = await DialogService.OpenAsync(Title, ds =>
@<div class="rz-html-editor-dialog">
<div class="rz-html-editor-dialog-item">
<label>@SelectText</label>
<RadzenUpload ChooseText=@UploadChooseText @ref=@FileUpload Url=@Editor.UploadUrl Auto="false" Accept="image/*"
style="width: 100%" Complete="OnUploadComplete" Error="OnUploadError">
@foreach (var header in uploadHeaders)
{
<RadzenUploadHeader Name=@header.Key Value=@header.Value />
}
var result = await DialogService.OpenAsync(Title, ds => @<div class="rz-html-editor-dialog">
<RadzenTemplateForm TItem="ImageAttributes" Data="@Attributes" Submit="OnSubmit">
<div class="rz-html-editor-dialog-item">
<label>@SelectText</label>
@if (Editor.UploadUrl != null)
{
<RadzenUpload ChooseText=@UploadChooseText @ref=@FileUpload Url=@Editor.UploadUrl Auto="false" Accept="image/*"
style="width: 100%" Complete="OnUploadComplete" Error="OnUploadError">
@foreach (var header in uploadHeaders)
{
<RadzenUploadHeader Name=@header.Key Value=@header.Value />
}
</RadzenUpload>
</div>
<div class="rz-html-editor-dialog-item">
<label>@WidthText</label>
<RadzenTextBox @bind-Value=@Attributes.Width style="width: 100%" />
</div>
<div class="rz-html-editor-dialog-item">
<label>@HeightText</label>
<RadzenTextBox @bind-Value=@Attributes.Height style="width: 100%" />
</div>
<div class="rz-html-editor-dialog-item">
<label>@UrlText</label>
<RadzenTextBox @bind-Value=@Attributes.Src style="width: 100%" />
</div>
<div class="rz-html-editor-dialog-item">
<label>@AltText</label>
<RadzenTextBox @bind-Value=@Attributes.Alt style="width: 100%" />
</div>
<div class="rz-html-editor-dialog-buttons">
<RadzenButton Text=@OkText Click="OnSubmit" />
<RadzenButton Text=@CancelText Click="()=> ds.Close(false)" ButtonStyle="ButtonStyle.Secondary" />
</div>
} else {
<RadzenFileInput Accept="image/*" ChooseText=@UploadChooseText @bind-Value=@Attributes.Src style="width: 100%" />
}
</div>
@if (ShowWidth)
{
<div class="rz-html-editor-dialog-item">
<label>@WidthText</label>
<RadzenTextBox @bind-Value=@Attributes.Width style="width: 100%" />
</div>
}
@if (ShowHeight)
{
<div class="rz-html-editor-dialog-item">
<label>@HeightText</label>
<RadzenTextBox @bind-Value=@Attributes.Height style="width: 100%" />
</div>
}
@if (ShowSrc)
{
<div class="rz-html-editor-dialog-item">
<label>@UrlText</label>
<RadzenTextBox @bind-Value=@Attributes.Src style="width: 100%" />
</div>
}
@if (ShowAlt)
{
<div class="rz-html-editor-dialog-item">
<label>@AltText</label>
<RadzenTextBox @bind-Value=@Attributes.Alt style="width: 100%" />
</div>
}
<div class="rz-html-editor-dialog-buttons">
<RadzenButton Text=@OkText ButtonType="ButtonType.Submit" />
<RadzenButton Text=@CancelText Click="()=> ds.Close(false)" ButtonStyle="ButtonStyle.Secondary" />
</div>
</RadzenTemplateForm>
</div>
);
);
}
}

View File

@@ -81,12 +81,36 @@ namespace Radzen.Blazor
[Parameter]
public string HeightText { get; set; } = "Image Height";
/// <summary>
/// Specifies whether to show the image width section. Set it to false to hide it. Default value is true.
/// </summary>
[Parameter]
public bool ShowWidth { get; set; } = true;
/// <summary>
/// Specifies whether to show the image height section. Set it to false to hide it. Default value is true.
/// </summary>
[Parameter]
public bool ShowHeight { get; set; } = true;
/// <summary>
/// Specifies whether to show the web address section. Set it to false to hide it. Default value is true.
/// </summary>
[Parameter]
public bool ShowSrc { get; set; } = true;
/// <summary>
/// Specifies whether to show the alternative text section. Set it to false to hide it. Default value is true.
/// </summary>
[Parameter]
public bool ShowAlt { get; set; } = true;
ImageAttributes Attributes { get; set; }
RadzenUpload FileUpload { get; set; }
async Task OnSubmit()
{
if (FileUpload.HasValue)
if (FileUpload?.HasValue == true)
{
await FileUpload.Upload();
}
@@ -107,6 +131,8 @@ namespace Radzen.Blazor
{
DialogService.Close(true);
}
await Editor.RaiseUploadComplete(args);
}
async Task OnUploadError(UploadErrorEventArgs args)

View File

@@ -20,28 +20,29 @@
blank = true;
}
var result = await DialogService.OpenAsync(Title, ds =>
@<div class="rz-html-editor-dialog">
<div class="rz-html-editor-dialog-item">
<label>@UrlText</label>
<RadzenTextBox @bind-Value=@attributes.Href style="width: 100%" />
</div>
@if (string.IsNullOrWhiteSpace(attributes.InnerHtml) || attributes.InnerHtml == "<br>")
{
<div class="rz-html-editor-dialog-item">
<label>@LinkText</label>
<RadzenTextBox @bind-Value=@attributes.InnerText style="width: 100%" />
</div>
}
<div class="rz-html-editor-dialog-item">
<RadzenCheckBox @bind-Value=@blank />
<label>@OpenInNewWindowText</label>
</div>
<div class="rz-html-editor-dialog-buttons">
<RadzenButton Text=@OkText Click="()=> ds.Close(true)" />
<RadzenButton Text=@CancelText Click="()=> ds.Close(false)" ButtonStyle="ButtonStyle.Secondary" />
</div>
</div>);
var result = await DialogService.OpenAsync(Title, ds => @<div class="rz-html-editor-dialog">
<RadzenTemplateForm TItem="LinkAttributes" Data="@attributes" Submit="() => ds.Close(true)">
<div class="rz-html-editor-dialog-item">
<label>@UrlText</label>
<RadzenTextBox @bind-Value=@attributes.Href style="width: 100%" />
</div>
@if (string.IsNullOrWhiteSpace(attributes.InnerHtml) || attributes.InnerHtml == "<br>")
{
<div class="rz-html-editor-dialog-item">
<label>@LinkText</label>
<RadzenTextBox @bind-Value=@attributes.InnerText style="width: 100%" />
</div>
}
<div class="rz-html-editor-dialog-item">
<RadzenCheckBox @bind-Value=@blank />
<label>@OpenInNewWindowText</label>
</div>
<div class="rz-html-editor-dialog-buttons">
<RadzenButton Text=@OkText ButtonType="ButtonType.Submit" />
<RadzenButton Text=@CancelText Click="()=> ds.Close(false)" ButtonStyle="ButtonStyle.Secondary" />
</div>
</RadzenTemplateForm>
</div>);
await Editor.RestoreSelectionAsync();
@@ -58,6 +59,5 @@
await Editor.ExecuteCommandAsync("insertHTML", html.ToString());
}
}
}

View File

@@ -1,4 +1,4 @@
@using Radzen.Blazor.Rendering
@inherits RadzenHtmlEditorButtonBase
<EditorButton Title=@Title Shortcut=@Shortcut Click=@OnClick Icon="format_underline" Selected=@Editor.State.Underline />
<EditorButton Title=@Title Shortcut=@Shortcut Click=@OnClick Icon="format_underlined" Selected=@Editor.State.Underline />

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