Compare commits

...

268 Commits

Author SHA1 Message Date
Vladimir Enchev
6f16f230d1 Version updated 2023-02-09 09:04:20 +02:00
Emil Nachev
f458a057e8 Last order date: From OrderByDescending take First (#801)
Last order date: 
From OrderByDescending take First item
or 
from OrderBy take Last item
2023-02-09 08:57:52 +02:00
yordanov
d013300fa3 Update example source tab text and title 2023-02-08 16:01:55 +02:00
yordanov
d6b8394044 Update Support page 2023-02-08 16:01:55 +02:00
yordanov
c1fa630602 Update Get Started page 2023-02-08 16:01:55 +02:00
Emil Nachev
28a03dadaf Update DataGridVirtualizationLoadDataPage.razor (#799) 2023-02-08 14:54:15 +02:00
Vladimir Enchev
29cfc2ea6b Fixed closing dialogs logic
Fix #791
2023-02-08 10:10:31 +02:00
Vladimir Enchev
f4c776f10e Fixed DataGrid filter popup no working correctly after column Visible change runtime 2023-02-07 14:57:15 +02:00
tecxx
04be6bc38b workaround for HTMLEditor innerHTML exception (#728) (#795)
Co-authored-by: Robert Rostek <robert@rostech.at>
2023-02-07 11:36:45 +02:00
Vladimir Enchev
4f0bbeeab0 Fixed DataGridColumn and DataFilterProperty FormatString logic 2023-02-07 11:18:53 +02:00
Vladimir Enchev
5a356a8d75 DataGrid ExpandRows() and CollapseRows() methods added 2023-02-07 09:31:20 +02:00
yordanov
7f35f46eaa Remove invalid --rz-paginator-button-size css variable. Resolves #794 2023-02-06 17:33:17 +02:00
yordanov
ea7be67d83 Update Support page 2023-02-06 16:48:05 +02:00
yordanov
8f0d65766d Update Colors page layout 2023-02-06 16:48:05 +02:00
yordanov
a140f318a2 Update MainLayout padding and remove TOC 2023-02-06 16:48:05 +02:00
yordanov
d0a2d1644f Add ComponentName parameter to RadzenExample 2023-02-06 16:48:05 +02:00
yordanov
e69b153e1c Add initial TOC styles 2023-02-06 16:48:05 +02:00
yordanov
d19d383738 Update Spacing page 2023-02-06 16:48:05 +02:00
yordanov
253f288323 Update Breakpoints page 2023-02-06 16:48:05 +02:00
yordanov
d6ce0536ca Update Button demo page 2023-02-06 16:48:05 +02:00
yordanov
0168c01915 Style RadzenExample tabs and source code editor 2023-02-06 16:48:05 +02:00
Atanas Korchev
7329fec67a Add copy button and error handling. 2023-02-06 16:48:05 +02:00
Atanas Korchev
5fea22294e Update branch name. 2023-02-06 16:48:05 +02:00
Atanas Korchev
669124a6b3 Runnable snippets. 2023-02-06 16:48:05 +02:00
Atanas Korchev
c44d141c07 Add monaco editor. 2023-02-06 16:48:05 +02:00
Atanas Korchev
0b30e00b8c Compile examples. 2023-02-06 16:48:05 +02:00
Vladimir Enchev
1e7bd1bf48 AsODataEnumerable() removed from the demo 2023-02-06 13:02:49 +02:00
Vladimir Enchev
c2c4d6aa02 Version updated 2023-02-06 11:27:07 +02:00
Vladimir Enchev
7e0c64191c DataGrid advanced filter mode reworked with form to handle submit on enter key 2023-02-06 11:24:26 +02:00
Vladimir Enchev
323e4e971a DataGrid advanced filter mode will apply filter on enter key
Fix #786
2023-02-06 10:07:50 +02:00
Vladimir Enchev
99b4f8f8fc Merge branch 'master' of https://github.com/radzenhq/radzen-blazor 2023-02-06 09:53:10 +02:00
Vladimir Enchev
7c1cf76c5c Fixed DialogOptions.CloseDialogOnEsc behavior
Fix #791
2023-02-06 09:52:53 +02:00
Atanas Korchev
305e1b7af6 Paste images as data URI if the UploadUrl property of RadzenHtmlEditor is not set. 2023-02-06 09:46:51 +02:00
Vladimir Enchev
6880a70227 Tabs component select with RenderMode Client should not set display:block
Fix #792
2023-02-06 09:41:39 +02:00
yordanov
0482969755 Add text transform utility css classes 2023-02-02 14:27:46 +02:00
Atanas Korchev
4c9b429dae Update performance demo. 2023-02-02 10:09:16 +02:00
Vladimir Enchev
60b8400e29 DataFilter OData demo should not inherit from DbContextPage 2023-02-01 15:36:01 +02:00
Vladimir Enchev
7260be98d6 Version updated 2023-02-01 15:25:07 +02:00
Vladimir Enchev
be94094de7 DataFilter OData support added 2023-02-01 14:23:06 +02:00
Vladimir Enchev
645077f39b DataGrid VirtualizationOverscanCount property added 2023-01-31 13:55:10 +02:00
Vladimir Enchev
b42940441d Version updated 2023-01-30 12:30:40 +02:00
yordanov
fc7071c04e Update main demos layouts 2023-01-27 19:01:27 +02:00
yordanov
de887a4e3f Generate display utility classes with responsive breakpoints 2023-01-27 15:59:25 +02:00
yordanov
7f629309c7 Add mixin for utility classes with responsive breakpoints 2023-01-27 15:59:25 +02:00
yordanov
fa45e209d5 Add text-align with responsive breakpoints and classes for white-space 2023-01-27 15:59:25 +02:00
Vladimir Enchev
40aec6cd0d Set FilterPopupRenderMode="PopupRenderMode.OnDemand" for various DataGrid examples 2023-01-27 13:58:35 +02:00
Vladimir Enchev
969ae7aeca DataGrid FilterPopupRenderMode Initial improved 2023-01-27 13:50:17 +02:00
Vladimir Enchev
a64bbd34f3 DataGrid advanced filtering demo updated 2023-01-27 13:42:02 +02:00
Vladimir Enchev
79acf83d6f Merge branch 'master' of https://github.com/radzenhq/radzen-blazor 2023-01-27 13:39:54 +02:00
Vladimir Enchev
786e8d0be1 FilterPopupRenderMode default value changed to Initial 2023-01-27 13:39:45 +02:00
Foitn
2757850f28 #748 Added methods to edit a range of rows and cancel a range of rows (#784)
Co-authored-by: DEMCON\MFE <marijn.feijten@demcon.com>
2023-01-27 13:36:48 +02:00
Vladimir Enchev
7c6f39f3b5 DataGrid FilterPopupRenderMode property added with OnDemand as default value 2023-01-27 12:04:00 +02:00
Vladimir Enchev
70723d0437 Fixed RadzenDataFilterProperty FilterTemplate context to be the underling Filter 2023-01-26 15:56:47 +02:00
Vladimir Enchev
bd9b0f798b Version updated 2023-01-26 10:52:35 +02:00
Bruno Silva
0c406b2ad8 Update DataGridInLineEditPage.razor (#783)
After inserting a new object the insert button was disabled.
2023-01-26 09:26:02 +02:00
Vladimir Enchev
822891541c check if event is present 2023-01-25 10:05:47 +02:00
Vladimir Enchev
bf064fd4e3 Added MarkupString support for Accordion item Text property 2023-01-24 15:55:22 +02:00
Vladimir Enchev
6c9e055d42 Added Template support for DropDown and DropDownDataGrid chips 2023-01-24 15:50:14 +02:00
Vladimir Enchev
7455d1bfb2 Fixed popups will steal focus in some cases
Fix #774
2023-01-24 10:29:09 +02:00
yordanov
7b923b6625 Add new series colors to chart's pastel color scheme 2023-01-23 09:59:34 +02:00
Vladimir Enchev
bcd18e9395 Version updated 2023-01-23 09:49:32 +02:00
yordanov
082d577834 Add more series colors 2023-01-23 09:43:33 +02:00
Vladimir Enchev
68bf4f9df3 DataGrid rz-data-row rz-state-disabled updated 2023-01-23 09:43:05 +02:00
Vladimir Enchev
9348698aac DataGrid rz-state-disabled added to rz-data-row 2023-01-23 09:40:14 +02:00
Vladimir Enchev
979025f7d9 Fixed DataGrid columns order not reset when Settings set to null 2023-01-23 09:27:20 +02:00
Vladimir Enchev
9aa09050da Fixed Autocomplete steals focus
#774
2023-01-20 16:29:00 +02:00
Vladimir Enchev
f331de4185 Fixed DataGrid AllowColumnReorder ignored when AllowGrouping is true 2023-01-20 13:38:18 +02:00
Vladimir Enchev
1ce067c854 DropDownDataGrid will render rz-state-disabled class for disabled items 2023-01-20 13:30:50 +02:00
Vladimir Enchev
e6d537f0f8 Set Gap and RowGap as px if specified just as number 2023-01-20 11:31:53 +02:00
Vladimir Enchev
6a69a8d21a Selection and more columns added to DataGrid dynamic data demo 2023-01-20 10:49:41 +02:00
Vladimir Enchev
415cc09a06 Fixed DataGrid will not populate pickable columns if AllowColumnPicking is initially false 2023-01-20 09:26:47 +02:00
Vladimir Enchev
fb482e133e DropDownBase select all should exclude disabled items 2023-01-20 09:13:05 +02:00
Vladimir Enchev
c8134ce2ec Disable select all CheckBox if AllowSelectAll is false 2023-01-20 08:59:37 +02:00
Vladimir Enchev
a59c062dda Version updated 2023-01-20 08:45:16 +02:00
Vladimir Enchev
fe604d3439 Merge branch 'master' of https://github.com/radzenhq/radzen-blazor 2023-01-20 08:44:51 +02:00
Vladimir Enchev
53ad8466ad Fixed popups positioning in old and new Edge browsers 2023-01-20 08:44:39 +02:00
yordanov
9e3c844231 Change rz-col selector specificity. Resolves #779 2023-01-19 22:35:34 +02:00
Vladimir Enchev
85a10e7c72 DataList AllowVirtualization property added 2023-01-19 17:09:59 +02:00
Vladimir Enchev
902dc5bdd1 themes updated 2023-01-19 10:27:11 +02:00
yordanov
666cbc9e47 Add --rz-chart-legend-font-size 2023-01-19 10:18:19 +02:00
yordanov
745fae3f76 Update Column and Stack demos 2023-01-19 10:00:36 +02:00
Vladimir Enchev
bb2c4d4c3f Version updated 2023-01-19 09:46:34 +02:00
Vladimir Enchev
f8c8d6725c themes updated 2023-01-19 09:46:27 +02:00
yordanov
54747dc800 Enable wrapping in Stack/Row demo configs 2023-01-18 18:43:04 +02:00
Vladimir Enchev
63b6c0cab4 code fixed 2023-01-18 18:41:27 +02:00
Vladimir Enchev
92c85a0791 demo fixed 2023-01-18 18:37:15 +02:00
Vladimir Enchev
83f47bfe6e Stack demo updated 2023-01-18 18:33:32 +02:00
Vladimir Enchev
2f034e98c0 Stack Wrap property added 2023-01-18 18:29:26 +02:00
Vladimir Enchev
cb416c3583 RadzenRow, RadzenColumn and RadzenStack components added (#777)
* Stack and RadzenRow/RadzenCol components added

* Add responsive breakpoints scss map to utilities

* Add initial layout styles for RadzenRow and RadzenCol

* Update RadzenCol breakpoints and default css class

* Rename rz-col-gap to rz-gap

* RadzenCol Size property added

* RadzenRow Gap and RowGap properties added

* Add spacing utility css classes

* Add RadzenCol offset and order styles

* Update RadzenCol css class names

* Order properties updated to string

* Remove negative margin css classes

* Add zero offset option

* RadzenCol renamed to RadzenColumn

* Fix offset map name

* Add Row and Column demos

* Rename RadzenCol to RadzenColumn

* column order fixed

* RadzenColumn reworked

* Stack Spacing changed to Gap

* stack demo updated

* Add display: none utility class

* Add Breakpoints and Spacing demos and update Layout demos

* Add rz-stack css class and set default gap for stack to --rz-gap

* Update demos

Co-authored-by: yordanov <vasil@yordanov.info>
2023-01-18 18:11:17 +02:00
Vladimir Enchev
584353a240 DataGrid simple filter with menu will not update settings on column filter operator change 2023-01-17 13:10:29 +02:00
Vladimir Enchev
729456c2a0 DataGrid simple filter demo improved 2023-01-17 09:31:43 +02:00
Vladimir Enchev
a02a2e5332 Version updated 2023-01-16 15:58:44 +02:00
Vladimir Enchev
78f0204d86 DropDown/DropDownDataGrid Chips remove button should not be triggered by Form submit on enter 2023-01-16 15:43:32 +02:00
Vladimir Enchev
b76ef5ca80 Second argument added to DataGrid SelectRow() method specifying if RowSelect event should be raised
Fix #769
2023-01-16 09:31:04 +02:00
Vladimir Enchev
788fc01cfb DataGrid expand/collapse should not select the row 2023-01-13 13:19:07 +02:00
Vladimir Enchev
4dc9360b34 DropDownDataGrid Separator property fixed 2023-01-12 18:23:07 +02:00
Vladimir Enchev
6c0e1b7f01 RadzenPanelMenu Multiple property added 2023-01-12 15:55:14 +02:00
Vladimir Enchev
27b91642f2 version updated 2023-01-12 15:08:59 +02:00
Vladimir Enchev
0c7be4b2c7 DropDownDataGrid AllowFilteringByAllStringColumns fixed 2023-01-12 15:08:42 +02:00
Vladimir Enchev
e41ba71828 version updated 2023-01-12 09:27:55 +02:00
paulo-rico
2b2b6b98f1 RadzenDropDownDataGrid - Option to search by individual keywords seperated by a space as per forum entry https://forum.radzen.com/t/dropdowndatagrid-with-multiple-columns/3076 (#768)
Co-authored-by: Paul Ruston <paul.ruston@live.co.uk>
2023-01-11 16:42:16 +02:00
Atanas Korchev
8f6b20abd1 Add Text property to RadzenAlert. 2023-01-10 19:27:45 +02:00
Dave Bagler
21a69c4a61 Add child content example to label API docs. (#766)
This change adds an additional example to show the new child content property for the label component.
2023-01-10 16:06:35 +02:00
Vladimir Enchev
e7b671283a Merge branch 'master' of https://github.com/radzenhq/radzen-blazor 2023-01-10 10:54:17 +02:00
Vladimir Enchev
d46a7a50b5 Label tests fixed 2023-01-10 10:54:09 +02:00
Atanas Korchev
dfae4e0848 The category axis of a bar chart is not visible when the Visible property of the value axis is set to false. 2023-01-10 10:44:21 +02:00
Vladimir Enchev
a0f7e924e5 More Edge checks updated 2023-01-09 11:08:42 +02:00
Vladimir Enchev
fd2f3c7dbe Version updated 2023-01-09 10:42:32 +02:00
Vladimir Enchev
da442dc02b Label ChildContent property added 2023-01-09 09:42:01 +02:00
Magnus Warvik
eaea900100 Fix side dialog width + title (#759)
* Set max-width of left/right side dialogs to 100%

* Use MarkupString for side dialog title (same as normal dialog)
2023-01-09 09:31:06 +02:00
Vladimir Enchev
449662f858 Check for Edge updated 2023-01-09 07:25:10 +02:00
Vladimir Enchev
def2219f41 DataGrid ShowCellDataAsTooltip property added 2023-01-06 11:03:04 +02:00
Vladimir Enchev
4670fc477d DataGrid AllGroupsExpanded property added 2023-01-06 09:19:40 +02:00
Vladimir Enchev
cea4572843 Version updated 2023-01-05 10:51:17 +02:00
Vladimir Enchev
b769d4644a Performance demo update with loading indicator 2023-01-05 09:54:08 +02:00
Vladimir Enchev
3d9b117a74 themes updated 2023-01-05 09:00:34 +02:00
Vladimir Enchev
3b1a5f227e Fixed DropDownBase exception if ValueProperty is named after system type 2023-01-04 15:40:33 +02:00
Vladimir Enchev
a66a2d936e Drag visual not destroyed on group 2023-01-04 15:12:45 +02:00
yordanov
8b5c334d47 Update copyright year 2023-01-04 12:52:00 +02:00
Vladimir Enchev
5597cf6753 performance demo restored to IQueryable 2023-01-04 12:31:53 +02:00
Vladimir Enchev
9d2d83d8d1 demo updated with LoadData 2023-01-04 12:24:55 +02:00
Vladimir Enchev
bc695479fa less records selected for the performance demo 2023-01-04 12:11:29 +02:00
Vladimir Enchev
c0f7f2accd less records selected for the performance demo 2023-01-04 12:04:55 +02:00
Vladimir Enchev
08309d6162 demo code improved 2023-01-04 11:55:09 +02:00
Vladimir Enchev
b217036ad0 DataGrid performance demo added 2023-01-04 11:46:43 +02:00
Vladimir Enchev
a276652cbc added page render time 2023-01-04 11:08:03 +02:00
Vladimir Enchev
39f643330a alignment fixed 2023-01-04 10:47:40 +02:00
Vladimir Enchev
2967cc917c code simplified 2023-01-04 10:47:16 +02:00
Vladimir Enchev
78d22b3165 obsolete code removed 2023-01-04 10:19:20 +02:00
yordanov
989036bdf2 Fix font-size in Scheduler slots 2023-01-03 17:31:30 +02:00
Vladimir Enchev
926e9ff92c Version updated 2023-01-03 11:50:56 +02:00
Vladimir Enchev
4696567514 DataGrid EmptyTemplate not shown when AllowVirtualization is true 2023-01-03 10:57:13 +02:00
Vladimir Enchev
e153a30186 Fixed incorrect culture handling in RadzenDataGridColumn
Fix #755
2023-01-03 10:35:39 +02:00
Vladimir Enchev
e8bbd3dace Fixed Numeric will throw exception on value change with empty string as Format 2023-01-03 10:13:00 +02:00
Vladimir Enchev
d2d1344858 Single expand PanelMenu example added 2023-01-03 09:55:00 +02:00
Vladimir Enchev
6be828079d DataGrid will save Settings on Reset() 2022-12-30 04:59:15 +02:00
Atanas Korchev
7a07a5c646 Prevent text selection during column resizing and grouping. 2022-12-28 14:21:42 +02:00
yordanov
ee649ebeb9 Add MaterialSymbols icon font to demos and update icons demo 2022-12-21 16:41:07 +02:00
yordanov
67fe2a5a67 Add support for different icon fonts via --rz-icon-font-family 2022-12-21 16:38:07 +02:00
yordanov
6ac193c139 Update Material Icons font 2022-12-21 13:34:24 +02:00
Vladimir Enchev
b7492a6dfa Version updated 2022-12-19 16:06:46 +02:00
Atanas Korchev
224e86f673 Change the Min and Max property types of RadzenNumericRangeValidator. 2022-12-19 12:20:30 +02:00
Marco Papst
f014e155d6 Bugfix/side dialog positioning (#754)
* fix side dialog does not use full height when main content is scrollable

* allow scrolling the content of the side dialog in vertical direction
2022-12-19 09:00:03 +02:00
Vladimir Enchev
7aa37cd6cb DataGrid initial column sorting ignores column SortProperty 2022-12-16 16:55:10 +02:00
Vladimir Enchev
b1cc09fcb6 DateTime column added to DataGrid dynamic data demo 2022-12-16 08:58:18 +02:00
Vladimir Enchev
4717293666 Fixed DataGrid Template and EmptyTemplate rendering with composite columns 2022-12-14 18:22:22 +02:00
Vladimir Enchev
a80cd0720f Version updated 2022-12-13 15:27:31 +02:00
Vladimir Enchev
e238676efc Image demo updated 2022-12-13 15:24:44 +02:00
Marco Papst
e1df231137 Feature: Open a Dialog on the side (#739)
* implement side dialog based on DialogService

* extend Dialog sample

* add optional Mask and RIght/Left position for side dialog

* add OpenSideAsync to dialog documentation

* mark Dialog as updated in demos

* add top and bottom position for side dialog

* extend side dialog docs with details to only one open dialog

* reduce default height of top/bottom side dialog

* fix failing test in RadzenNumeric due to NullreferenceException

Co-authored-by: Marco Papst <papst@sma.de>
2022-12-13 13:48:23 +02:00
Vladimir Enchev
f40d10b35c Merge branch 'master' of https://github.com/radzenhq/radzen-blazor 2022-12-13 12:37:43 +02:00
Vladimir Enchev
ea20e5445e Check for format in Numeric before replacing digit placeholder 2022-12-13 12:37:35 +02:00
Atanas Korchev
4a6f2dbdc3 Default theme does not load. 2022-12-13 12:30:16 +02:00
Vladimir Enchev
11ff01bc61 Added support for custom numeric formats in Numeric 2022-12-13 11:20:34 +02:00
Atanas Korchev
2a292e6a82 Avoid duplicate CSS loading. 2022-12-13 11:03:02 +02:00
Maxim Becker
dc11242b77 Optimize rendering of chart tooltip (#745)
* Optimize rendering of chart tooltip

* Add performance test
2022-12-12 17:22:48 +02:00
Vladimir Enchev
4ec8f5fc28 Version updated 2022-12-12 15:22:59 +02:00
Vladimir Enchev
690f3ed87c Fixed self-reference hierarchy does not work in LoadData
Fix #740
2022-12-12 15:20:23 +02:00
Vladimir Enchev
04e8f6f2e3 DataGrid column LogicalFilterOperator not saved in Settings
Fix #746
2022-12-12 15:07:55 +02:00
Vladimir Enchev
e96ab86198 DataGrid column HeaderCssClass not applied to header cell 2022-12-12 11:35:26 +02:00
Igor Telheiro
eae010a23b Update RadzenScheduler.razor.cs
Currently there's no easy way to get the current selectedView. This is because the selectedView that gets updated is the field, and not the public property.
The Scheduler.IsSelected is not an easy way to get it, as you need to test every view to get the current one.
Please consider updating the SelectedIndex property or making that SelectedView getter public.
2022-12-12 09:00:25 +02:00
Atanas Korchev
d9d829e37d Change a bit theme registration so RBS can pick it up. 2022-12-09 10:47:50 +02:00
Vladimir Enchev
53680fc774 Version updated 2022-12-08 10:41:21 +02:00
Vladimir Enchev
46b8378297 Fixed virtualized DataGrid sort null ref. exception with bound Settings and LoadData
Fix #733
2022-12-08 10:40:55 +02:00
Vladimir Enchev
a26b4f779e Check for null elements to focus 2022-12-08 09:32:07 +02:00
Vladimir Enchev
c2e8c87f3b Fixed DropdownBase.SelectItem Invalid Cast Exception when using Value Property
Fix #734
2022-12-08 09:28:56 +02:00
yordanov
c99e82bd8e Fix DropDown alignment in Chromium 108 2022-12-07 22:21:15 +02:00
Atanas Korchev
1398e54896 FormSubmit sometimes does not trigger when the model is a record type. 2022-12-07 19:46:10 +02:00
yordanov
2d93e5bb1c Card css variables should be exposed at :root level 2022-12-07 18:46:10 +02:00
Vladimir Enchev
6b901d42f7 Fixed disabled DatePicker can be cleared
Fix #735
2022-12-07 16:57:12 +02:00
Vladimir Enchev
0cc8a968ed Fixed exception with example source code tab 2022-12-07 16:05:03 +02:00
Vladimir Enchev
3f2914c6fa DropDown multi select item inline style removed 2022-12-07 09:41:56 +02:00
Vladimir Enchev
9e01786278 Fixed Dialog el.computedStyleMap is not a function
Fix #732
2022-12-06 14:10:54 +02:00
Vladimir Enchev
78ec625cd5 Version updated 2022-12-05 19:58:42 +02:00
Vladimir Enchev
02855e3ac4 Fixed DataGrid will not display data if EmptyText is set 2022-12-05 19:58:22 +02:00
Vladimir Enchev
2fc65f135d Version updated 2022-12-05 11:36:42 +02:00
Atanas Korchev
88085ca5a6 Change the test to be more accurate. 2022-12-05 11:05:17 +02:00
Vladimir Enchev
fe26a82d66 Fixed RadzenDataGrid EmptyText causes ParameterView instance exception in MAUI
Fixed #https://github.com/radzenhq/radzen-blazor/issues/729
2022-12-05 10:31:19 +02:00
Joseph
e98678b1f7 Allow set language for speech recognition (#730) 2022-12-05 10:17:09 +02:00
Atanas Korchev
ef2b4d785a Support members that differ only in casing. 2022-12-03 19:55:33 +02:00
yordanov
b721f3e3ce Fix nested DataGrid footer background color 2022-12-01 18:02:28 +02:00
Vladimir Enchev
13cc0fc08c Fixed DropDown item can be selected using ENTER key even if disabled
Fix #727
2022-12-01 10:46:38 +02:00
Vladimir Enchev
65fe95d464 version updated 2022-11-30 20:56:17 +02:00
Vladimir Enchev
80a887b0d0 Fixed Radzen.closeAllPopups() 'Cannot read properties of undefined' error 2022-11-30 20:56:09 +02:00
yordanov
bed1915da4 Input should not change font-size under 768px max-width 2022-11-30 18:11:54 +02:00
yordanov
65cde0af99 Move responsive styles for body, header and footer under RadzenLayout component styles. Resolves #724 2022-11-30 18:10:37 +02:00
yordanov
a0a2b8b265 Remove root-font-size variable from scheduler styles 2022-11-30 16:45:56 +02:00
EkaterinaTikhomirova
ed01254c84 Reset DropDown list after selection (#726)
Co-authored-by: ekaterina.tikhomirova <ekaterina.tikhomirova@dsr-corporation.com>
2022-11-30 14:56:39 +02:00
Vladimir Enchev
e03864ac03 Version updated 2022-11-30 12:25:51 +02:00
Vladimir Enchev
83796c5aa4 Fixed DataGrid with 'AllowColumnPicking' and 'AllowColumnReorder' always allows groupings
Fix #725
2022-11-30 12:25:32 +02:00
Atanas Korchev
0e329def1a ColorPicker throws NullReferenceException if it cannot parse its value as a valid color. 2022-11-29 19:36:15 +02:00
Maxim Becker
27859cf183 Add interpolation in RadzenLineSeries to make rendering more generic (#722)
* Add interpolation in RadzenLineSeries to make rendering of them more generic

* Added new lines at end of new files

* Add interpolation in RadzenAreaSeries too

* Revert the Are and Line chart demos. Add a new one.

Co-authored-by: Atanas Korchev <akorchev@gmail.com>
2022-11-29 16:15:51 +02:00
Vladimir Enchev
5560e7ee3d DataGrid GridLines property added (#723)
* DataGridGridLines added

* Add initial styles for GridLines

* Use child combinator to style first-level gridlines only

* Add style selectors for gridlines with composite cells

Co-authored-by: yordanov <vasil@yordanov.info>
2022-11-29 15:56:05 +02:00
Vladimir Enchev
72bab915b1 Fixed DataGrid filter popup closes on scrolling of DataGrid placed in column FIlterTemplate 2022-11-29 15:54:29 +02:00
Greg Horvath
f5fefcdd55 Implemented stand alone speech to text component (#703)
* Implemented stand alone speech to text component radzenhq#699 radzenhq#700

* Addressed radzenhq#703 PR review comments.

* radzenhq#703 Changed position of default title and aria-label attributes so caller can override them.

* radzradzenhq#703 Added unit test for verify default title and aria-label can be overridden.

* radzradzenhq#703 Added speech recognication error handling.  Minor refactor to toggleDictation js function.

* Refactor the implementation of toggleDictation. Change the class name. Use the Reference inherited from RadzenComponent. Other smaller changes.

* Check what component started the speech recording.

* Update speech to text button styles

* Remove important rule

* Update demo and add it to main menu

* Add StopIcon, Title and StopTitle properties. Remove console logging.

Co-authored-by: Atanas Korchev <akorchev@gmail.com>
Co-authored-by: yordanov <vasil@yordanov.info>
2022-11-29 15:06:37 +02:00
Atanas Korchev
160634ea38 Fix the installation article. 2022-11-29 13:59:12 +02:00
Vladimir Enchev
50fb5a96d8 DropDown enum example fixed to work in WASM 2022-11-29 13:10:54 +02:00
Vladimir Enchev
79c7ea16d8 DropDownBase will use Display attribute of Enum 2022-11-29 12:49:11 +02:00
Atanas Korchev
a166db0ea1 Revert "Add black friday banners"
This reverts commit 0c4e92ebec.
2022-11-29 08:24:58 +02:00
Vladimir Enchev
2b905615d1 test fixed 2022-11-28 20:30:06 +02:00
Vladimir Enchev
a18c1b5e0c version updated 2022-11-28 20:21:09 +02:00
Vladimir Enchev
c066043ceb Fixed Chart ValueProperty does not accept decimal
DynamicExpressionParser will be used for indexer only
2022-11-28 20:21:01 +02:00
Vladimir Enchev
41d64f1b7c Version updated 2022-11-28 13:03:44 +02:00
Vladimir Enchev
36023fdc35 Fixed DropDownBase exception with property names named after system types 2022-11-28 13:03:24 +02:00
Atanas Korchev
8437cf279d Update instructions for .NET 7. 2022-11-28 10:54:26 +02:00
Vladimir Enchev
d2e19cf3c7 DataFilter initial FilterOperator of filters added runtime fixed 2022-11-28 09:31:58 +02:00
Atanas Korchev
9a90be6e79 Create ci.yml 2022-11-26 09:02:52 +02:00
Atanas Korchev
903a4c78e2 Cannot select listbox item if ValueProperty is a string property. 2022-11-26 08:52:17 +02:00
Atanas Korchev
19049bfea8 Fix failing tests. 2022-11-26 08:51:50 +02:00
yordanov
0c4e92ebec Add black friday banners 2022-11-25 11:41:49 +02:00
Vladimir Enchev
aefe937adc DataGrid nested filter popups close fixed 2022-11-25 09:38:20 +02:00
Vladimir Enchev
63eda04815 DataGrid will display localized enum values in cells from Display attribute if present 2022-11-25 08:46:21 +02:00
Atanas Korchev
18c7783942 Reset the state during paging, sorting and filtering. 2022-11-25 06:57:28 +02:00
Vladimir Enchev
6c17e1e9c5 more broken links fixes 2022-11-24 21:51:47 +02:00
Vladimir Enchev
955355460e various broken links fixed 2022-11-24 21:43:06 +02:00
Vladimir Enchev
51a110b9a3 Fixed filtering of DataFilter with IEnumerable property 2022-11-24 15:26:14 +02:00
Atanas Korchev
43da6c3656 Compatibility with RBS. 2022-11-24 11:54:51 +02:00
Vladimir Enchev
411c916a26 RadzenLink ChildContent reworked 2022-11-24 11:31:25 +02:00
Vladimir Enchev
c0cc14ec99 Image url fixed 2022-11-24 11:23:21 +02:00
Vladimir Enchev
4f8dd8844c Link ChildContent support added 2022-11-24 11:23:10 +02:00
Vladimir Enchev
36f8f43aad Version updated 2022-11-24 10:00:24 +02:00
Zak Kohler
534d7f570e Fix typos in documentation (#718) 2022-11-23 07:26:59 +02:00
Vladimir Enchev
599e514afc DialogService.OpenAsync will not execute Radzen.openDialog() when options.ChildContent is set
Fix #717
2022-11-22 16:31:05 +02:00
Vladimir Enchev
42054827a2 seed reworked 2022-11-22 15:27:09 +02:00
Vladimir Enchev
bfe517ef75 docs links fixed 2022-11-22 10:07:02 +02:00
Vladimir Enchev
c585f3455d Component source links fixed 2022-11-22 09:56:27 +02:00
Vladimir Enchev
fe1d3dd82b Only RadzenExample Name property should be used to display source of the example page.
Source property should be used to link source code of the component.
2022-11-22 09:41:20 +02:00
yordanov
92d503335b Add scrollbar size. Resolves #675 2022-11-21 19:14:58 +02:00
Atanas Korchev
782f93169d Fix links to Dialog and Layout source. 2022-11-21 19:08:38 +02:00
Vladimir Enchev
0c12177091 Fixed focus lost with DropDown/DropDownDataGrid with AllowFiltering 2022-11-21 17:40:48 +02:00
Vladimir Enchev
cc90ff97e3 code fixed 2022-11-21 16:51:05 +02:00
Vladimir Enchev
525230cf2d RadioButtonList from HorizontalAlign enum demo added 2022-11-21 12:15:21 +02:00
Vladimir Enchev
2df9ef3fca Version updated 2022-11-21 11:55:57 +02:00
Vladimir Enchev
9de1b6d9d2 HtmlEditor not focused in dialog with AutoFocusFirstElement
Fix #714
2022-11-21 11:45:12 +02:00
yordanov
94b096259a Update docs templates and demos homepage 2022-11-18 15:22:02 +02:00
yordanov
4e7011f12b Update READMEs 2022-11-18 15:21:10 +02:00
yordanov
ff123ddcb4 Introducing Radzen Blazor Studio 2022-11-18 14:41:09 +02:00
aalex675
9d98fefdef Fix: PropertyAccess.Getter method doesn't allow access of array members. Change to use DynamicExpressionParser.ParseLambda from Linq. (#713)
Co-authored-by: Alden Alexander <aalexander@bachelorcontrols.com>
2022-11-18 10:54:40 +02:00
Atanas Korchev
573bddb1ae Cannot set the style of RadzenAccordion. 2022-11-18 08:58:07 +02:00
Vladimir Enchev
8386848e0e Fixed e.target.closest is not a function
Fix #712
2022-11-17 13:50:06 +02:00
Vladimir Enchev
5ba0220a74 Added CSS class "rz-group-row" to DataGrid group rows 2022-11-16 18:29:22 +02:00
Vladimir Enchev
d24cadcf00 Menu child items cannot be opened with click once closed when ClickToOpen=false 2022-11-16 14:18:24 +02:00
Vladimir Enchev
fbe53df424 Menu clickToOpen option removed from the demo 2022-11-16 13:28:42 +02:00
Vladimir Enchev
eb0347073e Version updated 2022-11-16 13:17:37 +02:00
for7raid
45907bdcca Split button improvements (#709) 2022-11-16 11:49:04 +02:00
Vladimir Enchev
b5b8aaf731 Menu ClickToOpen behavior fixed 2022-11-16 11:06:35 +02:00
Vladimir Enchev
d4eb749729 Version updated 2022-11-15 13:46:14 +02:00
Vladimir Enchev
3e09882b0c RadzenMenu ClickToOpen property added (#706) 2022-11-15 13:44:49 +02:00
Vladimir Enchev
a443b57ead Merge branch 'master' of https://github.com/radzenhq/radzen-blazor 2022-11-15 13:11:13 +02:00
Vladimir Enchev
3a430b3586 DataGrid load settings fixed with filter and paging 2022-11-15 13:11:05 +02:00
EkaterinaTikhomirova
f09f8ba4eb Add parameter in RadzenDropDown to clear search input after selection. (#705)
Co-authored-by: ekaterina.tikhomirova <ekaterina.tikhomirova@dsr-corporation.com>
2022-11-15 12:44:44 +02:00
Vladimir Enchev
c6a1560a0f DropDown item select in multi select changed from SPACE to ENTER key to allow entering space during filter 2022-11-15 12:42:55 +02:00
Vladimir Enchev
805f596f1f Fixed DataGrid second filter in advanced mode 2022-11-15 09:14:48 +02:00
Vladimir Enchev
bcee601d4c Exception fixed 2022-11-15 08:57:51 +02:00
Vladimir Enchev
ea009e802b Version updated 2022-11-14 17:36:58 +02:00
Vladimir Enchev
e421eaaf1a DataGrid group row expression fixed 2022-11-14 17:36:41 +02:00
Vladimir Enchev
997cd88db8 Employees images fixed 2022-11-14 16:48:13 +02:00
Vladimir Enchev
ccc8fbde05 Radzen.Blazor version updated 2022-11-14 13:19:42 +02:00
Vladimir Enchev
0fdf7721a7 readme updated 2022-11-14 13:19:09 +02:00
Vladimir Enchev
96f1863711 WebAssembly version of demos added (#704)
* Demos as WASM

* Copy the default wasm loading CSS.

* InMemory database instead SQLite

* DbContext filled with data

* Host project added

* Server renamed to Host

* Remove location.reload.

* Remove startup.

* Stop github caching to file. Use larger page size to improve load time.

* Display enum values.

* DbContext with static data

* Host DbContex added

* Examples and Sitemap added

* docker updated

* Theme changing.

* Update Dockerfile

* Set WORKDIR.

* Do not throw errors for duplicate assets.

* Do not inherit Index from DbContextPage

* Documentation works again.

* Sqlite removed from host

* DataGrid Save/Load settings fixed

* Demo source fixed

* Add loading.

* Add loading image

* Hide the loading with JS interop.

* Add the loading globally.

* Add server demos.

* Delete data files.

* Remove the db copy code from Dockerfile.

* DbContext seed fixed

* missing await added

* Separate solutions for all project, Blazor server and Blazor WebAssembly added

* tree demos data access fixed

* Avoid using Last() and First()

* Orders and OrderDetails data made smaller

* Update loading animation

* Move the loading to MainLayout.

* Update loading animation speed

* Seed() improved

* Revert "Move the loading to MainLayout."

This reverts commit f43ce050396b9483e8809af35493eb35531f254c.

* Fix homepage bg color with dark theme

* Revert "Revert "Move the loading to MainLayout.""

This reverts commit faee40d4ad92f474577434c61f31d847e6777a53.

Co-authored-by: Atanas Korchev <akorchev@gmail.com>
Co-authored-by: yordanov <vasil@yordanov.info>
2022-11-14 13:14:47 +02:00
Vladimir Enchev
ea9c7543d9 Fixed class attribute should not override default Css class for Accordion, CheckBoxList and RadioButtonList items
Fix #698
2022-11-14 10:56:21 +02:00
Vladimir Enchev
b69a02c878 Fixed showing tooltip on RadionButtonListItem and CheckBoxListItem 2022-11-14 08:52:39 +02:00
msdevcode
4d9c792a25 LayoutPage - Tab "Source" fix (#693) 2022-11-11 17:43:08 +02:00
yordanov
7a6992845a Update Layout demo 2022-11-11 14:05:51 +02:00
Vladimir Enchev
f2deb8f402 Dynamic and version updated 2022-11-10 15:11:11 +02:00
Vladimir Enchev
c067b162b9 Fixed possible exception with SelectBar in .NET 7 2022-11-10 15:10:59 +02:00
Vladimir Enchev
f1415d7583 Fixed DropDown error on KeyPress "Delete"
Fix #691
2022-11-10 14:59:39 +02:00
Atanas Korchev
14f23e79f1 Demonstrate RadzenLayout 2022-11-10 11:51:42 +02:00
Atanas Korchev
a5a55e207c The argument of the ResetPassword event is empty. 2022-11-09 18:05:44 +02:00
338 changed files with 16816 additions and 7048 deletions

26
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,26 @@
# This workflow will build a .NET project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net
name: .NET
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: 7.0.x
- name: Build
run: dotnet build Radzen.Blazor/Radzen.Blazor.csproj
- name: Test
run: dotnet test Radzen.Blazor.Tests/Radzen.Blazor.Tests.csproj

View File

@@ -19,7 +19,7 @@ You can ask your question here. Please use the [Radzen.Blazor Components](https:
### Dedicated technical support
Radzen staff provides technical support with guaranteed response time to Radzen Professional and Enterprise subscribers. The pricing options are available [here](https://www.radzen.com/pricing/).
Radzen staff provides technical support with guaranteed response time to Radzen Professional and Enterprise subscribers. The pricing options are available [here](https://www.radzen.com/blazor-studio/pricing/).
## How Can I Contribute?

View File

@@ -13,18 +13,19 @@ RUN apt-get update && apt-get install unzip wget git -y && wget -q -P /tmp https
COPY Radzen.Blazor /app/Radzen.Blazor
COPY Radzen.DocFX /app/DocFX
COPY RadzenBlazorDemos /app/RadzenBlazorDemos
COPY RadzenBlazorDemos.Host /app/RadzenBlazorDemos.Host
WORKDIR /app
RUN docfx DocFX/docfx.json
FROM mcr.microsoft.com/dotnet/sdk:7.0
COPY --from=0 /app/RadzenBlazorDemos /app
WORKDIR /app
COPY --from=0 /app/RadzenBlazorDemos.Host /app/RadzenBlazorDemos.Host
COPY --from=0 /app/RadzenBlazorDemos /app/RadzenBlazorDemos
WORKDIR /app/RadzenBlazorDemos.Host
RUN dotnet publish -c Release -o out
COPY RadzenBlazorDemos/northwind.db /app/out
COPY RadzenBlazorDemos/northwind.sql /app/out
ENV ASPNETCORE_URLS http://*:5000
WORKDIR /app/out
WORKDIR /app/RadzenBlazorDemos.Host/out
ENTRYPOINT ["dotnet", "RadzenBlazorDemos.dll"]
ENTRYPOINT ["dotnet", "RadzenBlazorDemos.Host.dll"]

View File

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

View File

@@ -41,7 +41,7 @@
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/pricing/).
Paid support is available as part of the [Radzen Professional subscription](https://www.radzen.com/blazor-studio/pricing/).
### :computer: Native
@@ -64,10 +64,10 @@ Everybody is welcome to visit the [Radzen Community forum](https://forum.radzen.
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/pricing/).
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 Studio](https://www.radzen.com/features/) provides tons of productivity features for Blazor developers:
- The first in the industry WYSIWYG Blazor design time canvas
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
@@ -93,7 +93,7 @@ Radzen Blazor components come with five free themes: Material, Standard, Default
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 before .NET 6) or `wwwroot/index.html` (Blazor WebAssembly) and include the CSS file of a theme CSS file by adding this snippet
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">
```
@@ -105,7 +105,7 @@ To include a different theme (i.e. Standard) just change the name of the CSS fil
### 4. Include Radzen.Blazor.js
Open `Pages\_Layout.cshtml` (Blazor Server .NET 6+), `Pages\_Host.cshtml` (Blazor Server before .NET 6) or `wwwroot/index.html` (Blazor WebAssembly) and include this snippet:
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>
@@ -137,3 +137,6 @@ Use any Radzen Blazor component by typing its tag name in a Blazor page e.g.
}
}
```
## 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

@@ -0,0 +1,44 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Bunit;
using Radzen.Blazor.Rendering;
using Xunit;
namespace Radzen.Blazor.Tests;
public class ChartTests
{
[Fact(Timeout = 30000)]
public async Task Chart_Tooltip_Performance()
{
using var ctx = new TestContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.Setup<Rect>("Radzen.createChart", _ => true).SetResult(new Rect {Left = 0, Top = 0, Width = 200, Height = 200});
var seriesData = Enumerable.Range(0, 5000).Select(i => new Point { X = i, Y = i });
var chart = ctx.RenderComponent<RadzenChart>(chartParameters =>
chartParameters
.AddChildContent<RadzenLineSeries<Point>>(seriesParameters =>
seriesParameters
.Add(p => p.CategoryProperty, nameof(Point.X))
.Add(p => p.ValueProperty, nameof(Point.Y))
.Add(p => p.Data, seriesData))
.AddChildContent<RadzenCategoryAxis>(axisParameters =>
axisParameters
.Add(p => p.Step, 100)
.Add(p => p.Formatter, x =>
{
Thread.Sleep(100);
return $"{x}";
})));
foreach (var _ in Enumerable.Range(0, 10))
{
await chart.InvokeAsync(() => chart.Instance.MouseMove(100, 100));
Assert.Contains("<div class=\"rz-chart-tooltip", chart.Markup);
await chart.InvokeAsync(() => chart.Instance.MouseMove(0, 0));
Assert.DoesNotContain("<div class=\"rz-chart-tooltip", chart.Markup);
}
}
}

View File

@@ -0,0 +1,16 @@
using Bunit;
using Xunit;
namespace Radzen.Blazor.Tests
{
public class ColorPickerTests
{
[Fact]
public void ColorPicker_ShouldAcceptInvalidValues()
{
using var ctx = new TestContext();
var component = ctx.RenderComponent<RadzenColorPicker>(ComponentParameter.CreateParameter("Value", "invalid"));
}
}
}

View File

@@ -74,11 +74,11 @@ namespace Radzen.Blazor.Tests
});
});
var markup = new Regex(@"\s\s+").Replace(component.Markup, "").Trim();
var data = component.FindAll(".rz-cell-data");
Assert.Contains(@$"<span class=""rz-cell-data"">1</span>", markup);
Assert.Contains(@$"<span class=""rz-cell-data"">2</span>", markup);
Assert.Contains(@$"<span class=""rz-cell-data"">3</span>", markup);
Assert.Equal("1", data[0].TextContent.Trim());
Assert.Equal("2", data[1].TextContent.Trim());
Assert.Equal("3", data[2].TextContent.Trim());
}
[Fact]
@@ -99,9 +99,8 @@ namespace Radzen.Blazor.Tests
});
});
var markup = new Regex(@"\s\s+").Replace(component.Markup, "").Trim();
Assert.Contains(@$"<span class=""rz-column-title"">MyId</span>", markup);
var title = component.Find(".rz-column-title");
Assert.Equal("MyId", title.TextContent.Trim());
}
[Fact]

View File

@@ -49,7 +49,7 @@ namespace Radzen.Blazor.Tests
var component = ctx.RenderComponent<RadzenDatePicker<DateTime>>();
component.SetParametersAndRender(parameters => {
component.SetParametersAndRender(parameters => {
parameters.Add<bool>(p => p.ShowTime, true);
parameters.Add<bool>(p => p.ShowSeconds, true);
});
@@ -93,7 +93,7 @@ namespace Radzen.Blazor.Tests
component.SetParametersAndRender(parameters => {
parameters.Add(p => p.DateFormat, format);
parameters.Add<object>(p => p.Value, DateTime.Now);
parameters.Add<object>(p => p.Value, DateTime.Now);
});
Assert.Contains(@$"value=""{string.Format("{0:" + format + "}", DateTime.Now)}""", component.Markup);
@@ -146,7 +146,7 @@ namespace Radzen.Blazor.Tests
component.SetParametersAndRender(parameters => {
parameters.Add<object>(p => p.Value, DateTime.Now);
parameters.Add<bool>(p => p.AllowClear, true);
parameters.Add<bool>(p => p.AllowClear, true);
});
Assert.Contains(@$"<i class=""rz-dropdown-clear-icon rzi rzi-times""", component.Markup);
@@ -320,12 +320,12 @@ namespace Radzen.Blazor.Tests
DateTime previousDay = DateTime.Today.AddDays(-1);
using var ctx = new TestContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
var component = ctx.RenderComponent<RadzenDatePicker<DateTime>>();
var raised = false;
object newValue = null;
object newValue = null;
component.SetParametersAndRender(parameters => {
parameters.Add(p => p.ValueChanged, args => { raised = true; newValue = args; })
@@ -378,14 +378,14 @@ namespace Radzen.Blazor.Tests
Assert.True(raised);
Assert.Null(newValue);
}
[Fact]
public void DatePicker_Respects_DateTimeMaxValue()
{
using var ctx = new TestContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var component = ctx.RenderComponent<RadzenDatePicker<DateTime>>(parameters =>
{
parameters.Add(p => p.Value, DateTime.MaxValue);

View File

@@ -0,0 +1,144 @@
using System;
using System.Threading.Tasks;
using Bunit;
using Xunit;
namespace Radzen.Blazor.Tests
{
public class DropDownTests
{
class DataItem
{
public string Text { get; set; }
public int Id { get; set; }
}
private static IRenderedComponent<RadzenDropDown<T>> DropDown<T>(TestContext ctx, Action<ComponentParameterCollectionBuilder<RadzenDropDown<T>>> configure = null)
{
var data = new [] {
new DataItem { Text = "Item 1", Id = 1 },
new DataItem { Text = "Item 2", Id = 2 },
};
var component = ctx.RenderComponent<RadzenDropDown<T>>();
component.SetParametersAndRender(parameters => {
parameters.Add(p => p.Data, data);
parameters.Add(p => p.TextProperty, nameof(DataItem.Text));
if (configure != null)
{
configure.Invoke(parameters);
}
else
{
parameters.Add(p => p.ValueProperty, nameof(DataItem.Id));
}
});
return component;
}
[Fact]
public async Task Dropdown_SelectItem_Method_Should_Not_Throw()
{
using var ctx = new TestContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
var component = DropDown<int>(ctx);
var items = component.FindAll(".rz-dropdown-item");
Assert.Equal(2, items.Count);
//this throws
await component.InvokeAsync(async () => await component.Instance.SelectItem(1));
}
[Fact]
public void DropDown_RendersItems()
{
using var ctx = new TestContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
var component = DropDown<int>(ctx);
var items = component.FindAll(".rz-dropdown-item");
Assert.Equal(2, items.Count);
}
[Fact]
public void DropDown_AppliesSelectionStyleForIntValue()
{
using var ctx = new TestContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
var component = DropDown<int>(ctx);
var items = component.FindAll(".rz-dropdown-item");
items[0].Click();
component.Render();
items = component.FindAll(".rz-dropdown-item");
Assert.Contains("rz-state-highlight", items[0].ClassList);
}
[Fact]
public void DropDown_AppliesSelectionStyleForStringValue()
{
using var ctx = new TestContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
var component = DropDown<string>(ctx, parameters => {
parameters.Add(p => p.ValueProperty, nameof(DataItem.Text));
});
var items = component.FindAll(".rz-dropdown-item");
items[0].Click();
component.Render();
items = component.FindAll(".rz-dropdown-item");
Assert.Contains("rz-state-highlight", items[0].ClassList);
}
[Fact]
public void DropDown_AppliesSelectionStyleWhenMultipleSelectionIsEnabled()
{
using var ctx = new TestContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
var component = DropDown<string>(ctx, parameters => {
parameters.Add(p => p.ValueProperty, nameof(DataItem.Text));
parameters.Add(p => p.Multiple, true);
});
var items = component.FindAll(".rz-multiselect-item");
items[0].Click();
component.Render();
items = component.FindAll(".rz-multiselect-item");
items[1].Click();
component.Render();
var selectedItems = component.FindAll(".rz-state-highlight");
Assert.Equal(2, selectedItems.Count);
}
}
}

View File

@@ -30,8 +30,10 @@ namespace Radzen.Blazor.Tests
component.SetParametersAndRender(parameters => parameters.Add(p => p.Text, text));
Assert.Contains(@$">{text}</span>", component.Markup);
Assert.Contains(@$"class=""rz-link-text""", component.Markup);
var textElement = component.Find(".rz-link-text");
Assert.NotNull(textElement);
Assert.Equal(text, textElement.TextContent.Trim());
}
[Fact]

View File

@@ -26,7 +26,12 @@ namespace Radzen.Blazor.Tests
var component = ctx.RenderComponent<RadzenLogin>();
Assert.Contains(@$"<label class=""rz-label"" for=""username"">Username</label>", component.Markup);
component.SetParametersAndRender(p => {
p.AddUnmatched("id", "login");
});
var label = component.Find($@"label[for=""login-username""]");
Assert.NotNull(label);
}
[Fact]
@@ -63,7 +68,7 @@ namespace Radzen.Blazor.Tests
component.SetParametersAndRender(parameters => {
parameters.Add(p => p.Username, "user");
parameters.Add(p => p.Password, "pwd");
parameters.Add(p => p.Login, args => { clicked = true; });
parameters.Add(p => p.Login, args => { clicked = true; });
});
component.Find("button").Click();

View File

@@ -0,0 +1,139 @@
using System;
using Bunit;
using Microsoft.AspNetCore.Components.Forms;
using Xunit;
namespace Radzen.Blazor.Tests
{
public class NumericRangeValidatorTests
{
class FormComponentTestDouble : IRadzenFormComponent
{
public bool IsBound => false;
public bool HasValue => true;
public string Name { get; set; }
public FieldIdentifier FieldIdentifier => throw new System.NotImplementedException();
public object GetValue()
{
return Value;
}
public object Value { get; set; }
}
class RadzenNumericRangeValidatorTestDouble : RadzenNumericRangeValidator
{
public bool Validate(object value)
{
return base.Validate(new FormComponentTestDouble { Value = value });
}
}
[Fact]
public void Throws_Exception_If_Min_And_Max_Are_Null()
{
var validator = new RadzenNumericRangeValidatorTestDouble();
Assert.Throws<System.ArgumentException>(() => validator.Validate(1));
}
[Fact]
public void Returns_False_If_Value_Is_Null()
{
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));
});
Assert.False(component.Instance.Validate(null));
}
[Fact]
public void Returns_True_If_Value_Is_Greater_Than_Min()
{
using var ctx = new TestContext();
var component = ctx.RenderComponent<RadzenNumericRangeValidatorTestDouble>();
component.SetParametersAndRender(parameters => parameters.Add(p => p.Min, 0));
Assert.True(component.Instance.Validate(1));
}
[Fact]
public void Returns_True_If_Value_Is_Equal_To_Min()
{
using var ctx = new TestContext();
var component = ctx.RenderComponent<RadzenNumericRangeValidatorTestDouble>();
component.SetParametersAndRender(parameters => parameters.Add(p => p.Min, 0));
Assert.True(component.Instance.Validate(0));
}
[Fact]
public void Returns_True_If_Value_Is_Less_Than_Max()
{
using var ctx = new TestContext();
var component = ctx.RenderComponent<RadzenNumericRangeValidatorTestDouble>();
component.SetParametersAndRender(parameters => parameters.Add(p => p.Max, 10));
Assert.True(component.Instance.Validate(9));
}
[Fact]
public void Returns_True_If_Value_Is_Equal_To_Max()
{
using var ctx = new TestContext();
var component = ctx.RenderComponent<RadzenNumericRangeValidatorTestDouble>();
component.SetParametersAndRender(parameters => parameters.Add(p => p.Max, 10));
Assert.True(component.Instance.Validate(10));
}
[Fact]
public void Returns_True_If_Value_Is_Between_Min_And_Max()
{
using var ctx = new TestContext();
var component = ctx.RenderComponent<RadzenNumericRangeValidatorTestDouble>();
component.SetParametersAndRender(parameters => parameters.Add(p => p.Min, 0).Add(p => p.Max, 10));
Assert.True(component.Instance.Validate(5));
}
[Fact]
public void Returns_True_If_Value_Is_Between_Min_And_Max_And_They_Are_Nullable()
{
int? min = 0;
int? max = 10;
using var ctx = new TestContext();
var component = ctx.RenderComponent<RadzenNumericRangeValidatorTestDouble>();
component.SetParametersAndRender(parameters => parameters.Add(p => p.Min, min).Add(p => p.Max, max));
Assert.True(component.Instance.Validate(5));
}
[Fact]
public void Returns_True_When_Value_Is_Of_DifferentType()
{
using var ctx = new TestContext();
var component = ctx.RenderComponent<RadzenNumericRangeValidatorTestDouble>();
component.SetParametersAndRender(parameters => parameters.Add(p => p.Min, 0m).Add(p => p.Max, 10m));
Assert.True(component.Instance.Validate(5));
}
[Fact]
public void Returns_False_If_Cannot_Conert_Value()
{
using var ctx = new TestContext();
var component = ctx.RenderComponent<RadzenNumericRangeValidatorTestDouble>();
component.SetParametersAndRender(parameters => parameters.Add(p => p.Min, 0m).Add(p => p.Max, 10m));
Assert.False(component.Instance.Validate(DateTime.Now));
}
}
}

View File

@@ -0,0 +1,120 @@
using AngleSharp.Css;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using Xunit;
namespace Radzen.Blazor.Tests
{
public class PropertyAccessTests
{
public partial class TestData
{
public string PROPERTY { get; set; }
public string Property { get; set; }
}
[Fact]
public void Getter_With_DifferentTargetType()
{
var o = new TestData { Property = "test" };
var getter = PropertyAccess.Getter<object, object>(nameof(TestData.Property), typeof(TestData));
var value = getter(o);
Assert.Equal(o.Property, value);
}
[Fact]
public void Getter_With_Members_That_Differ_Only_In_Casing()
{
var o = new TestData { PROPERTY = nameof(TestData.PROPERTY), Property = nameof(TestData.Property) };
var getter = PropertyAccess.Getter<TestData, string>(nameof(TestData.PROPERTY));
var value = getter(o);
Assert.Equal(nameof(TestData.PROPERTY), value);
}
[Fact]
public void Getter_Resolves_Property_On_Simple_Object()
{
var o = new SimpleObject() { Prop1 = "TestString" };
var getter = PropertyAccess.Getter<SimpleObject, string>("Prop1");
var value = getter(o);
Assert.Equal("TestString", value);
}
[Fact]
public void Getter_Resolves_Property_On_Simple_Object_QueryableType()
{
var _data = new List<SimpleObject>()
{
new SimpleObject() { Prop1 = "TestString" },
};
Func<object, object> getter = PropertyAccess.Getter<object, object>("Prop1", typeof(SimpleObject));
var value = getter(_data[0]);
Assert.Equal("TestString", value);
}
[Fact]
public void Getter_Resolves_Property_On_Nested_Object()
{
var o = new NestedObject() { Obj = new SimpleObject { Prop1 = "TestString" } };
var getter = PropertyAccess.Getter<NestedObject, string>("Obj.Prop1");
var value = getter(o);
Assert.Equal("TestString", value);
}
[Fact]
public void Getter_Resolves_Property_From_Array()
{
var o = new ArrayObject() { Values = new string[] { "1", "2", "3" } };
var getter = PropertyAccess.Getter<ArrayObject, string>("Values[1]");
var value = getter(o);
Assert.Equal("2", value);
}
[Fact]
public void Getter_Resolves_Property_From_Nested_Array()
{
var o = new NestedArrayObject() { Obj = new ArrayObject() { Values = new string[] { "1", "2", "3" } } };
var getter = PropertyAccess.Getter<NestedArrayObject, string>("Obj.Values[2]");
var value = getter(o);
Assert.Equal("3", value);
}
[Fact]
public void Getter_Resolves_Property_From_List()
{
var o = new ListObject() { Values = new List<string>() { "1", "2", "3" } };
var getter = PropertyAccess.Getter<ListObject, string>("Values[1]");
var value = getter(o);
Assert.Equal("2", value);
}
public class SimpleObject
{
public string Prop1 { get; set; }
}
public class NestedObject
{
public SimpleObject Obj { get; set; }
}
public class ArrayObject
{
public string[] Values { get; set; }
}
public class NestedArrayObject
{
public ArrayObject Obj { get; set; }
}
public class ListObject
{
public List<string> Values { get; set; }
}
}
}

View File

@@ -0,0 +1,25 @@
using System.Collections.Generic;
using Radzen.Blazor.Rendering;
using Xunit;
namespace Radzen.Blazor.Tests.Rendering;
public class StepGeneratorTests
{
[Fact]
public void Renders_Path_Correctly()
{
var data = new List<Point>
{
new() { X = 10, Y = 10 },
new() { X = 20, Y = 15 },
new() { X = 30, Y = 20 },
new() { X = 40, Y = 25 },
new() { X = 50, Y = 50 }
};
var path = new StepGenerator().Path(data);
Assert.Equal("10 10 H 20 V 15 H 30 V 20 H 40 V 25 H 50 V 50", path);
}
}

View File

@@ -0,0 +1,197 @@
using Bunit;
using Xunit;
namespace Radzen.Blazor.Tests
{
public class SpeechToTextButtonTests
{
[Fact]
public void SpeechToTextButton_Renders_Record_Button_When_Visible()
{
using var ctx = new TestContext();
var component = ctx.RenderComponent<RadzenSpeechToTextButton>();
component.Render();
var recordButton = component.Find("button.rz-button-icon-only.rz-speech-to-text-button");
Assert.NotNull(recordButton);
}
[Fact]
public void SpeechToTextButton_Does_Not_Renders_Record_Button_When_Visible_False()
{
using var ctx = new TestContext();
var component = ctx.RenderComponent<RadzenSpeechToTextButton>();
component.SetParametersAndRender(parameters =>
{
parameters.Add(p => p.Visible, false);
});
Assert.Throws<ElementNotFoundException>(() => component.Find("button.rz-button-icon-only.rz-speech-to-text-button"));
}
[Fact]
public void SpeechToTextButton_Renders_Additional_Css()
{
using var ctx = new TestContext();
var component =
ctx.RenderComponent<RadzenSpeechToTextButton>(ComponentParameter.CreateParameter("class", "another-class"));
var recordButton = component.Find("button.rz-button-icon-only.rz-speech-to-text-button.another-class");
Assert.NotNull(recordButton);
}
[Fact]
public void SpeechToTextButton_Can_Override_Default_Title_And_Aria_Label()
{
using var ctx = new TestContext();
var component =
ctx.RenderComponent<RadzenSpeechToTextButton>(
ComponentParameter.CreateParameter("title", "title override"),
ComponentParameter.CreateParameter("aria-label", "aria-label override"));
var recordButton = component.Find("button.rz-button-icon-only.rz-speech-to-text-button");
Assert.NotNull(recordButton);
Assert.Equal("title override", recordButton.GetAttribute("title"));
Assert.Equal("aria-label override", recordButton.GetAttribute("aria-label"));
}
[Fact]
public void SpeechToTextButton_Sets_Record_Button_Css_When_Record_Button_Clicked()
{
using var ctx = new TestContext();
var component = ctx.RenderComponent<RadzenSpeechToTextButton>();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
component.Render();
var recordButton = component.Find("button.rz-button-icon-only.rz-speech-to-text-button");
Assert.NotNull(recordButton);
recordButton.Click();
component.Render();
var blinkingRecordButton = component.Find("button.rz-button-icon-only.rz-speech-to-text-button-recording");
Assert.NotNull(blinkingRecordButton);
}
[Fact]
public void SpeechToTextButton_Sets_StopTitle_When_Record_Button_Clicked()
{
using var ctx = new TestContext();
var component = ctx.RenderComponent<RadzenSpeechToTextButton>();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
component.Render();
var recordButton = component.Find("button.rz-button-icon-only.rz-speech-to-text-button");
Assert.NotNull(recordButton);
recordButton.Click();
component.Render();
var blinkingRecordButton = component.Find("button.rz-button-icon-only.rz-speech-to-text-button-recording");
Assert.Equal(component.Instance.StopTitle, blinkingRecordButton.GetAttribute("title"));
}
[Fact]
public void SpeechToTextButton_ChangesIconWhen_When_Record_Button_Clicked()
{
using var ctx = new TestContext();
var component = ctx.RenderComponent<RadzenSpeechToTextButton>();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
component.Render();
var recordButton = component.Find("button.rz-button-icon-only.rz-speech-to-text-button");
Assert.NotNull(recordButton);
recordButton.Click();
component.Render();
var blinkingRecordButton = component.Find("button span");
Assert.Contains("stop", blinkingRecordButton.TextContent);
Assert.DoesNotContain("mic", blinkingRecordButton.TextContent);
}
[Fact]
public void SpeechToTextButton_UnSets_Record_Button_Css_When_Record_Button_Clicked_Twice()
{
using var ctx = new TestContext();
var component = ctx.RenderComponent<RadzenSpeechToTextButton>();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
component.Render();
var recordButton = component.Find("button.rz-button-icon-only.rz-speech-to-text-button");
Assert.NotNull(recordButton);
recordButton.Click();
component.Render();
const string blinkingRecordButtonSelector = "button.rz-button-icon-only.rz-speech-to-text-button-recording";
var blinkingRecordButton = component.Find(blinkingRecordButtonSelector);
Assert.NotNull(blinkingRecordButton);
blinkingRecordButton.Click();
component.Render();
Assert.Throws<ElementNotFoundException>(() => component.Find(blinkingRecordButtonSelector));
}
[Fact]
public void SpeechToTextButton_Invokes_OnResult_FromJs()
{
using var ctx = new TestContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
var component = ctx.RenderComponent<RadzenSpeechToTextButton>();
string resultsFromJs = null;
component.SetParametersAndRender(parameters => parameters.Add(p => p.Change, r => resultsFromJs = r));
var recordButton = component.Find("button.rz-button-icon-only.rz-speech-to-text-button");
Assert.NotNull(recordButton);
recordButton.Click();
const string speechResults = "results from js";
component.InvokeAsync(() => component.Instance.OnResult(speechResults));
Assert.Equal(speechResults, resultsFromJs);
}
}
}

View File

@@ -467,7 +467,7 @@ namespace Radzen.Blazor
builder.AddAttribute(1, nameof(ChartTooltip.X), x + marginLeft);
builder.AddAttribute(2, nameof(ChartTooltip.Y), y + marginTop);
builder.AddAttribute(3, nameof(ChartTooltip.ChildContent), TooltipTemplate == null ? null : TooltipTemplate(item));
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));

View File

@@ -7,7 +7,10 @@ using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Linq.Dynamic.Core.Parser;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Threading.Tasks;
@@ -78,6 +81,11 @@ namespace Radzen
/// SecondFilterOperator.
/// </summary>
public FilterOperator SecondFilterOperator { get; set; }
/// <summary>
/// LogicalFilterOperator.
/// </summary>
public LogicalFilterOperator LogicalFilterOperator { get; set; }
}
#if NET7_0_OR_GREATER
#else
@@ -666,6 +674,33 @@ namespace Radzen
Multiple
}
/// <summary>
/// Specifies the grid lines of <see cref="RadzenDataGrid{TItem}" />.
/// </summary>
public enum DataGridGridLines
{
/// <summary>
/// Theme default.
/// </summary>
Default,
/// <summary>
/// Both horizontal and vertical grid lines.
/// </summary>
Both,
/// <summary>
/// No grid lines.
/// </summary>
None,
/// <summary>
/// Horizontal grid lines.
/// </summary>
Horizontal,
/// <summary>
/// Vertical grid lines.
/// </summary>
Vertical
}
/// <summary>
/// Specifies the severity of a <see cref="RadzenNotification" />. Severity changes the visual styling of the RadzenNotification (icon and background color).
/// </summary>
@@ -824,6 +859,99 @@ namespace Radzen
Vertical
}
/// <summary>
/// Represents whether items are forced onto one line or can wrap onto multiple lines.
/// </summary>
public enum FlexWrap
{
/// <summary>
/// The items are laid out in a single line.
/// </summary>
NoWrap,
/// <summary>
/// The items break into multiple lines.
/// </summary>
Wrap,
/// <summary>
/// The items break into multiple lines reversed.
/// </summary>
WrapReverse
}
/// <summary>
/// Represents content justification of Stack items.
/// </summary>
public enum JustifyContent
{
/// <summary>
/// Normal content justification of Stack items.
/// </summary>
Normal,
/// <summary>
/// Center content justification of Stack items.
/// </summary>
Center,
/// <summary>
/// Start content justification of Stack items.
/// </summary>
Start,
/// <summary>
/// End content justification of Stack items.
/// </summary>
End,
/// <summary>
/// Left content justification of Stack items.
/// </summary>
Left,
/// <summary>
/// Right content justification of Stack items.
/// </summary>
Right,
/// <summary>
/// SpaceBetween content justification of Stack items.
/// </summary>
SpaceBetween,
/// <summary>
/// SpaceAround content justification of Stack items.
/// </summary>
SpaceAround,
/// <summary>
/// SpaceEvenly content justification of Stack items.
/// </summary>
SpaceEvenly,
/// <summary>
/// Stretch content justification of Stack items.
/// </summary>
Stretch
}
/// <summary>
/// Represents the alignment of Stack items.
/// </summary>
public enum AlignItems
{
/// <summary>
/// Normal items alignment.
/// </summary>
Normal,
/// <summary>
/// Center items alignment.
/// </summary>
Center,
/// <summary>
/// Start items alignment.
/// </summary>
Start,
/// <summary>
/// End items alignment.
/// </summary>
End,
/// <summary>
/// Stretch items alignment.
/// </summary>
Stretch
}
/// <summary>
/// Specifies the sort order in components that support sorting.
/// </summary>
@@ -1940,28 +2068,61 @@ namespace Radzen
/// <returns>A function which return the specified property by its name.</returns>
public static Func<TItem, TValue> Getter<TItem, TValue>(string propertyName, Type type = null)
{
var arg = Expression.Parameter(typeof(TItem));
Expression body = arg;
if (type != null)
if (propertyName.Contains("["))
{
body = Expression.Convert(body, type);
return DynamicExpressionParser.ParseLambda<TItem, TValue>(null, false, propertyName).Compile();
}
foreach (var member in propertyName.Split("."))
else
{
body = !body.Type.IsInterface ?
Expression.PropertyOrField(body, member) :
Expression.Property(
body,
new Type[] { body.Type }.Concat(body.Type.GetInterfaces()).FirstOrDefault(t => t.GetProperty(member) != null),
member);
var arg = Expression.Parameter(typeof(TItem));
Expression body = arg;
if (type != null)
{
body = Expression.Convert(body, type);
}
foreach (var member in propertyName.Split("."))
{
if (body.Type.IsInterface)
{
body = Expression.Property(body,
new [] { body.Type }.Concat(body.Type.GetInterfaces()).FirstOrDefault(t => t.GetProperty(member) != null),
member
);
}
else
{
try
{
body = Expression.PropertyOrField(body, member);
}
catch (AmbiguousMatchException)
{
var property = body.Type.GetProperty(member, BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy);
if (property != null)
{
body = Expression.Property(body, property);
}
else
{
var field = body.Type.GetField(member, BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy);
if (field != null)
{
body = Expression.Field(body, field);
}
}
}
}
}
body = Expression.Convert(body, typeof(TValue));
return Expression.Lambda<Func<TItem, TValue>>(body, arg).Compile();
}
body = Expression.Convert(body, typeof(TValue));
return Expression.Lambda<Func<TItem, TValue>>(body, arg).Compile();
}
/// <summary>
@@ -2326,6 +2487,52 @@ namespace Radzen
public RenderFragment ChildContent { get; set; }
}
/// <summary>
/// A base class of row/col components.
/// </summary>
public class RadzenFlexComponent : RadzenComponentWithChildren
{
/// <summary>
/// Gets or sets the content justify.
/// </summary>
/// <value>The content justify.</value>
[Parameter]
public JustifyContent JustifyContent { get; set; } = JustifyContent.Normal;
/// <summary>
/// Gets or sets the items alignment.
/// </summary>
/// <value>The items alignment.</value>
[Parameter]
public AlignItems AlignItems { get; set; } = AlignItems.Normal;
internal string GetFlexCSSClass<T>(Enum v)
{
var value = ToDashCase(Enum.GetName(typeof(T), v));
return value == "start" || value == "end" ? $"flex-{value}" : value;
}
internal string ToDashCase(string value)
{
var sb = new StringBuilder();
foreach (var ch in value)
{
if ((char.IsUpper(ch) && sb.Length > 0) || char.IsSeparator(ch))
{
sb.Append('-');
}
if (char.IsLetterOrDigit(ch))
{
sb.Append(char.ToLowerInvariant(ch));
}
}
return sb.ToString();
}
}
class Debouncer
{
System.Timers.Timer timer;

View File

@@ -80,6 +80,11 @@ namespace Radzen
{
Close();
}
if (_sideDialogTask?.Task.IsCompleted == false)
{
CloseSide();
}
}
/// <summary>
@@ -97,6 +102,16 @@ namespace Radzen
/// </summary>
public event Action<string, Type, Dictionary<string, object>, DialogOptions> OnOpen;
/// <summary>
/// Raises the Close event for the side dialog
/// </summary>
public event Action<dynamic> OnSideClose;
/// <summary>
/// Raises the Open event for the side dialog
/// </summary>
public event Action<Type, Dictionary<string, object>, SideDialogOptions> OnSideOpen;
/// <summary>
/// Opens a dialog with the specified arguments.
/// </summary>
@@ -121,6 +136,7 @@ namespace Radzen
/// The tasks
/// </summary>
protected List<TaskCompletionSource<dynamic>> tasks = new List<TaskCompletionSource<dynamic>>();
private TaskCompletionSource<dynamic> _sideDialogTask;
/// <summary>
/// Opens a dialog with the specified arguments.
@@ -140,6 +156,42 @@ namespace Radzen
return task.Task;
}
/// <summary>
/// Opens a side dialog with the specified arguments
/// </summary>
/// <typeparam name="T">The type of Blazor component which will be displayed in the side dialog.</typeparam>
/// <param name="title">The text displayed in the title bar of the side dialog.</param>
/// <param name="parameters">The dialog parameters. Passed as property values of <typeparamref name="T"/></param>
/// <param name="options">The side dialog options.</param>
/// <returns>A task that completes when the dialog is closed or a new one opened</returns>
public Task<dynamic> OpenSideAsync<T>(string title, Dictionary<string, object> parameters = null, SideDialogOptions options = null)
where T : ComponentBase
{
CloseSide();
_sideDialogTask = new TaskCompletionSource<dynamic>();
if (options == null)
{
options = new SideDialogOptions();
}
options.Title = title;
OnSideOpen?.Invoke(typeof(T), parameters ?? new Dictionary<string, object>(), options);
return _sideDialogTask.Task;
}
/// <summary>
/// Closes the side dialog
/// </summary>
/// <param name="result">The result of the Dialog</param>
public void CloseSide(dynamic result = null)
{
if (_sideDialogTask?.Task.IsCompleted == false)
{
_sideDialogTask.TrySetResult(result);
OnSideClose?.Invoke(result);
}
}
/// <summary>
/// Opens a dialog with the specified content.
/// </summary>
@@ -264,8 +316,6 @@ namespace Radzen
CssClass = options != null ? $"rz-dialog-confirm {options.CssClass}" : "rz-dialog-confirm",
};
await JSRuntime.InvokeAsync<string>("Radzen.openDialog", dialogOptions, Reference);
return await OpenAsync(title, ds =>
{
RenderFragment content = b =>
@@ -324,8 +374,6 @@ namespace Radzen
CssClass = options != null ? $"rz-dialog-alert {options.CssClass}" : "rz-dialog-alert",
};
await JSRuntime.InvokeAsync<string>("Radzen.openDialog", dialogOptions, Reference);
return await OpenAsync(title, ds =>
{
RenderFragment content = b =>
@@ -352,9 +400,9 @@ namespace Radzen
}
/// <summary>
/// Class DialogOptions.
/// Base Class for dialog options
/// </summary>
public class DialogOptions
public abstract class DialogOptionsBase
{
/// <summary>
/// Gets or sets a value indicating whether to show the title bar. Set to <c>true</c> by default.
@@ -367,7 +415,83 @@ namespace Radzen
/// </summary>
/// <value><c>true</c> if the close button is shown; otherwise, <c>false</c>.</value>
public bool ShowClose { get; set; } = true;
/// <summary>
/// Gets or sets the width of the dialog.
/// </summary>
/// <value>The width.</value>
public string Width { get; set; }
/// <summary>
/// Gets or sets the height of the dialog.
/// </summary>
/// <value>The height.</value>
public string Height { get; set; }
/// <summary>
/// Gets or sets the CSS style of the dialog
/// </summary>
/// <value>The style.</value>
public string Style { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the dialog should be closed by clicking the overlay.
/// </summary>
/// <value><c>true</c> if closeable; otherwise, <c>false</c>.</value>
public bool CloseDialogOnOverlayClick { get; set; } = false;
/// <summary>
/// Gets or sets dialog box custom class
/// </summary>
public string CssClass { get; set; }
}
/// <summary>
/// Class SideDialogOptions
/// </summary>
public class SideDialogOptions : DialogOptionsBase
{
/// <summary>
/// The title displayed on the dialog.
/// </summary>
public string Title { get; set; }
/// <summary>
/// The Position on which the dialog will be positioned
/// </summary>
public DialogPosition Position { get; set; } = DialogPosition.Right;
/// <summary>
/// Whether to show a mask on the background or not
/// </summary>
public bool ShowMask { get; set; } = true;
}
/// <summary>
/// DialogPosition enum
/// </summary>
public enum DialogPosition
{
/// <summary>
/// Dialog will be positioned on the right side
/// </summary>
Right,
/// <summary>
/// Dialog will be positioned on the left side
/// </summary>
Left,
/// <summary>
/// Dialog will be positioned on the top of the page
/// </summary>
Top,
/// <summary>
/// Dialog will be positioned at the bottom of the page
/// </summary>
Bottom
}
/// <summary>
/// Class DialogOptions.
/// </summary>
public class DialogOptions : DialogOptionsBase
{
/// <summary>
/// Gets or sets a value indicating whether the dialog is resizable. Set to <c>false</c> by default.
/// </summary>
@@ -394,21 +518,6 @@ namespace Radzen
/// <value>The bottom.</value>
public string Bottom { get; set; }
/// <summary>
/// Gets or sets the width of the dialog.
/// </summary>
/// <value>The width.</value>
public string Width { get; set; }
/// <summary>
/// Gets or sets the height of the dialog.
/// </summary>
/// <value>The height.</value>
public string Height { get; set; }
/// <summary>
/// Gets or sets the CSS style of the dialog
/// </summary>
/// <value>The style.</value>
public string Style { get; set; }
/// <summary>
/// Gets or sets the child content.
/// </summary>
/// <value>The child content.</value>
@@ -417,23 +526,11 @@ namespace Radzen
/// 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; } = true;
/// <summary>
/// Gets or sets a value indicating whether the dialog should be closed by clicking the overlay.
/// </summary>
/// <value><c>true</c> if closeable; otherwise, <c>false</c>.</value>
public bool CloseDialogOnOverlayClick { get; set; } = false;
/// <summary>
/// Gets or sets a value indicating whether the dialog should be closed on ESC key press.
/// </summary>
/// <value><c>true</c> if closeable; otherwise, <c>false</c>.</value>
public bool CloseDialogOnEsc { get; set; } = true;
/// <summary>
/// Gets or sets dialog box custom class
/// </summary>
public string CssClass { get; set; }
}
/// <summary>

View File

@@ -248,10 +248,10 @@ namespace Radzen
return;
}
if (selectedItems.Count != View.Cast<object>().Count())
if (selectedItems.Count != View.Cast<object>().ToList().Where(i => disabledPropertyGetter != null ? disabledPropertyGetter(i) as bool? != true : true).Count())
{
selectedItems.Clear();
selectedItems = View.Cast<object>().ToList();
selectedItems = View.Cast<object>().ToList().Where(i => disabledPropertyGetter != null ? disabledPropertyGetter(i) as bool? != true : true).ToList();
}
else
{
@@ -292,10 +292,13 @@ namespace Radzen
{
if (LoadData.HasDelegate && !string.IsNullOrEmpty(ValueProperty))
{
return View != null && View.Cast<object>().All(i => IsItemSelectedByValue(GetItemOrValueFromProperty(i, ValueProperty)));
return View != null && View.Cast<object>().ToList()
.Where(i => disabledPropertyGetter != null ? disabledPropertyGetter(i) as bool? != true : true)
.All(i => IsItemSelectedByValue(GetItemOrValueFromProperty(i, ValueProperty)));
}
return View != null && selectedItems.Count == View.Cast<object>().Count();
return View != null && selectedItems.Count == View.Cast<object>().ToList()
.Where(i => disabledPropertyGetter != null ? disabledPropertyGetter(i) as bool? != true : true).Count();
}
/// <summary>
@@ -362,7 +365,7 @@ namespace Radzen
selectedItem = null;
selectedItems.Clear();
}
InvokeAsync(OnDataChanged);
}
}
@@ -380,7 +383,7 @@ namespace Radzen
var type = query.ElementType;
if (type == typeof(object) && typeof(EnumerableQuery).IsAssignableFrom(query.GetType()) && query.Any())
{
{
type = query.FirstOrDefault().GetType();
}
@@ -413,17 +416,26 @@ namespace Radzen
/// <returns>System.Object.</returns>
public object GetItemOrValueFromProperty(object item, string property)
{
if (property == TextProperty && textPropertyGetter != null)
if (item != null)
{
return textPropertyGetter(item);
}
else if (property == ValueProperty && valuePropertyGetter != null)
{
return valuePropertyGetter(item);
}
else if (property == DisabledProperty && disabledPropertyGetter != null)
{
return disabledPropertyGetter(item);
if (property == TextProperty && textPropertyGetter != null)
{
return textPropertyGetter(item);
}
else if (property == ValueProperty && valuePropertyGetter != null)
{
return valuePropertyGetter(item);
}
else if (property == DisabledProperty && disabledPropertyGetter != null)
{
return disabledPropertyGetter(item);
}
var enumValue = item as Enum;
if (enumValue != null)
{
return Radzen.Blazor.EnumExtensions.GetDisplayDescription(enumValue);
}
}
return item;
@@ -595,7 +607,7 @@ namespace Radzen
//
}
}
else if (Multiple && key == "Space")
else if (Multiple && key == "Enter")
{
if (selectedIndex >= 0 && selectedIndex <= items.Count() - 1)
{
@@ -836,7 +848,7 @@ namespace Radzen
/// </summary>
/// <param name="item">The item.</param>
/// <returns><c>true</c> if the specified item is selected; otherwise, <c>false</c>.</returns>
internal bool isSelected(object item)
internal bool IsSelected(object item)
{
if (!string.IsNullOrEmpty(ValueProperty))
{
@@ -987,6 +999,11 @@ 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)
{
return;
}
if (!Multiple)
{
if (object.Equals(item, selectedItem))
@@ -995,7 +1012,7 @@ namespace Radzen
selectedItem = item;
if (!string.IsNullOrEmpty(ValueProperty))
{
internalValue = GetItemOrValueFromProperty(item, ValueProperty);
internalValue = PropertyAccess.GetItemOrValueFromProperty(item, ValueProperty);
}
else
{
@@ -1080,7 +1097,7 @@ namespace Radzen
}
else
{
selectedItems = selectedItems.AsQueryable().Where($@"!object.Equals({ValueProperty},@0)", value).ToList();
selectedItems = selectedItems.AsQueryable().Where($@"!object.Equals(it.{ValueProperty},@0)", value).ToList();
}
}
else
@@ -1146,7 +1163,7 @@ namespace Radzen
item = View.AsQueryable().Where($@"{ValueProperty} == @0", v).FirstOrDefault();
}
if (!object.Equals(item, null) && !selectedItems.AsQueryable().Where($@"object.Equals({ValueProperty},@0)", v).Any())
if (!object.Equals(item, null) && !selectedItems.AsQueryable().Where($@"object.Equals(it.{ValueProperty},@0)", v).Any())
{
selectedItems.Add(item);
}
@@ -1168,20 +1185,17 @@ namespace Radzen
internal bool IsItemSelectedByValue(object v)
{
if (internalValue != null)
switch (internalValue)
{
var values = internalValue as IEnumerable;
if (values != null)
{
return values.Cast<object>().Contains(v);
}
else
{
case string s:
return object.Equals(s, v);
case IEnumerable enumerable:
return enumerable.Cast<object>().Contains(v);
case null:
return false;
default:
return object.Equals(internalValue, v);
}
}
return false;
}
/// <inheritdoc />

View File

@@ -0,0 +1,22 @@
namespace Radzen.Blazor
{
/// <summary>
/// Specifies the interpolation mode of lines between data points. Used by <see cref="RadzenAreaSeries{TItem}"/> and <see cref="RadzenLineSeries{TItem}"/>.
/// </summary>
public enum Interpolation
{
/// <summary>
/// Points are connected by a straight line.
/// </summary>
Line,
/// <summary>
/// Points are connected by a smooth curve.
/// </summary>
Spline,
/// <summary>
/// Points are connected by horizontal and vertical lines only.
/// </summary>
Step
}
}

View File

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

View File

@@ -432,11 +432,11 @@ namespace Radzen
}
else if (PropertyAccess.IsNumeric(column.FilterPropertyType))
{
if (column.GetFilterOperator() == FilterOperator.IsNull || column.GetFilterOperator() == FilterOperator.IsNotNull)
if (columnFilterOperator == FilterOperator.IsNull || columnFilterOperator == FilterOperator.IsNotNull)
{
return $"{property} {linqOperator} null";
}
else if (column.GetFilterOperator() == FilterOperator.IsEmpty || column.GetFilterOperator() == FilterOperator.IsNotEmpty)
else if (columnFilterOperator == FilterOperator.IsEmpty || columnFilterOperator == FilterOperator.IsNotEmpty)
{
return $@"{property} {linqOperator} """"";
}
@@ -450,11 +450,11 @@ namespace Radzen
column.FilterPropertyType == typeof(DateTimeOffset) ||
column.FilterPropertyType == typeof(DateTimeOffset?))
{
if (column.GetFilterOperator() == FilterOperator.IsNull || column.GetFilterOperator() == FilterOperator.IsNotNull)
if (columnFilterOperator == FilterOperator.IsNull || columnFilterOperator == FilterOperator.IsNotNull)
{
return $"{property} {linqOperator} null";
}
else if (column.GetFilterOperator() == FilterOperator.IsEmpty || column.GetFilterOperator() == FilterOperator.IsNotEmpty)
else if (columnFilterOperator == FilterOperator.IsEmpty || columnFilterOperator == FilterOperator.IsNotEmpty)
{
return $@"{property} {linqOperator} """"";
}
@@ -474,11 +474,11 @@ namespace Radzen
}
else if (column.FilterPropertyType == typeof(Guid) || column.FilterPropertyType == typeof(Guid?))
{
if (column.GetFilterOperator() == FilterOperator.IsNull || column.GetFilterOperator() == FilterOperator.IsNotNull)
if (columnFilterOperator == FilterOperator.IsNull || columnFilterOperator == FilterOperator.IsNotNull)
{
return $"{property} {linqOperator} null";
}
else if (column.GetFilterOperator() == FilterOperator.IsEmpty || column.GetFilterOperator() == FilterOperator.IsNotEmpty)
else if (columnFilterOperator == FilterOperator.IsEmpty || columnFilterOperator == FilterOperator.IsNotEmpty)
{
return $@"{property} {linqOperator} """"";
}
@@ -1085,6 +1085,12 @@ namespace Radzen
}
else
{
if (filter.Property == null || (filter.FilterValue == null &&
filter.FilterOperator != FilterOperator.IsNull && filter.FilterOperator != FilterOperator.IsNotNull))
{
return;
}
var property = PropertyAccess.GetProperty(filter.Property);
if (property.IndexOf(".") != -1)
@@ -1159,6 +1165,137 @@ namespace Radzen
}
}
/// <summary>
/// Converts to OData filter expression.
/// </summary>
/// <param name="dataFilter">The DataFilter.</param>
/// <returns>System.String.</returns>
public static string ToODataFilterString<T>(this RadzenDataFilter<T> dataFilter)
{
Func<CompositeFilterDescriptor, bool> canFilter = (c) => dataFilter.properties.Where(col => col.Property == c.Property).FirstOrDefault()?.FilterPropertyType != null &&
(!(c.FilterValue == null || c.FilterValue as string == string.Empty)
|| c.FilterOperator == FilterOperator.IsNotNull || c.FilterOperator == FilterOperator.IsNull
|| c.FilterOperator == FilterOperator.IsEmpty || c.FilterOperator == FilterOperator.IsNotEmpty)
&& c.Property != null;
if (dataFilter.Filters.Where(canFilter).Any())
{
var filterExpressions = new List<string>();
foreach (var filter in dataFilter.Filters)
{
AddODataExpression(canFilter, filter, ref filterExpressions, dataFilter);
}
return filterExpressions.Any() ?
string.Join($" {dataFilter.LogicalFilterOperator.ToString().ToLower()} ", filterExpressions)
: "";
}
return "";
}
private static void AddODataExpression<T>(Func<CompositeFilterDescriptor, bool> canFilter, CompositeFilterDescriptor filter, ref List<string> filterExpressions, RadzenDataFilter<T> dataFilter)
{
if (filter.Filters != null)
{
var innerFilterExpressions = new List<string>();
foreach (var f in filter.Filters)
{
AddODataExpression(canFilter, f, ref innerFilterExpressions, dataFilter);
}
if (innerFilterExpressions.Any())
{
filterExpressions.Add("(" + string.Join($" {filter.LogicalFilterOperator.ToString().ToLower()} ", innerFilterExpressions) + ")");
}
}
else
{
if (filter.Property == null || (filter.FilterValue == null &&
filter.FilterOperator != FilterOperator.IsNull && filter.FilterOperator != FilterOperator.IsNotNull))
{
return;
}
var property = filter.Property.Replace('.', '/');
var column = dataFilter.properties.Where(c => c.Property == filter.Property).FirstOrDefault();
if (column == null) return;
if (dataFilter.FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive && column.FilterPropertyType == typeof(string))
{
property = $"tolower({property})";
}
if (filter.FilterOperator == FilterOperator.StartsWith || filter.FilterOperator == FilterOperator.EndsWith
|| filter.FilterOperator == FilterOperator.Contains || filter.FilterOperator == FilterOperator.DoesNotContain)
{
if (IsEnumerable(column.FilterPropertyType) && column.FilterPropertyType != typeof(string) &&
(filter.FilterOperator == FilterOperator.Contains || filter.FilterOperator == FilterOperator.DoesNotContain))
{
var enumerableValue = ((IEnumerable)(filter.FilterValue != null ? filter.FilterValue : Enumerable.Empty<object>())).AsQueryable();
var enumerableValueAsString = "(" + String.Join(",",
(enumerableValue.ElementType == typeof(string) ? enumerableValue.Cast<string>().Select(i => $@"'{i}'").Cast<object>() : enumerableValue.Cast<object>())) + ")";
if (enumerableValue.Any() && filter.FilterOperator == FilterOperator.Contains)
{
filterExpressions.Add($"{property} in {enumerableValueAsString}");
}
else if (enumerableValue.Any() && filter.FilterOperator == FilterOperator.DoesNotContain)
{
filterExpressions.Add($"not({property} in {enumerableValueAsString})");
}
}
else
{
var expression = dataFilter.FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive ?
$"{ODataFilterOperators[filter.FilterOperator]}({property}, tolower('{filter.FilterValue}'))" :
$"{ODataFilterOperators[filter.FilterOperator]}({property}, '{filter.FilterValue}')";
if (filter.FilterOperator == FilterOperator.DoesNotContain)
{
expression = $"not({expression})";
}
filterExpressions.Add(expression);
}
}
else
{
if (IsEnumerable(column.FilterPropertyType) && column.FilterPropertyType != typeof(string))
return;
var value = $"{filter.FilterValue}";
if (filter.FilterOperator == FilterOperator.IsNull || filter.FilterOperator == FilterOperator.IsNotNull)
{
value = $"null";
}
else if (filter.FilterOperator == FilterOperator.IsEmpty || filter.FilterOperator == FilterOperator.IsNotEmpty)
{
value = $"''";
}
else if (column.FilterPropertyType == typeof(string))
{
value = $"'{value}'";
}
else if (column.FilterPropertyType == typeof(DateTime) || column.FilterPropertyType == typeof(DateTime?))
{
value = $"{DateTime.Parse(value, null, System.Globalization.DateTimeStyles.RoundtripKind).ToString("yyyy-MM-ddTHH:mm:ss.fffZ")}";
}
else if (column.FilterPropertyType == typeof(bool) || column.FilterPropertyType == typeof(bool?))
{
value = $"{value?.ToLower()}";
}
filterExpressions.Add($@"{property} {ODataFilterOperators[filter.FilterOperator]} {value}");
}
}
}
/// <summary>
/// Ases the o data enumerable.
/// </summary>

View File

@@ -8,7 +8,7 @@
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/pricing/).
Paid support is available as part of the [Radzen Professional subscription](https://www.radzen.com/blazor-studio/pricing/).
### :computer: Native
@@ -31,10 +31,10 @@ Everybody is welcome to visit the [Radzen Community forum](https://forum.radzen.
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/pricing/).
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 Studio](https://www.radzen.com/features/) provides tons of productivity features for Blazor developers:
- The first in the industry WYSIWYG Blazor design time canvas
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
@@ -59,7 +59,7 @@ Radzen Blazor components come with five free themes: Material, Standard, Default
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 before .NET 6) or `wwwroot/index.html` (Blazor WebAssembly) and include the CSS file of a theme CSS file by adding this snippet
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">
```
@@ -71,7 +71,7 @@ To include a different theme (i.e. Standard) just change the name of the CSS fil
### 4. Include Radzen.Blazor.js
Open `Pages\_Layout.cshtml` (Blazor Server .NET 6+), `Pages\_Host.cshtml` (Blazor Server before .NET 6) or `wwwroot/index.html` (Blazor WebAssembly) and include this snippet:
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>

View File

@@ -8,7 +8,7 @@
<IsPackable>true</IsPackable>
<PackageId>Radzen.Blazor</PackageId>
<Product>Radzen.Blazor</Product>
<Version>4.3.1</Version>
<Version>4.6.7</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>
@@ -32,7 +32,7 @@
<PackageReference Include="Microsoft.AspNetCore.Components" Condition="'$(TargetFramework)' == 'net7.0'" Version="7.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Condition="'$(TargetFramework)' == 'net7.0'" Version="7.0.0" />
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.2.12" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.2.22" />
</ItemGroup>
<ItemGroup>

View File

@@ -10,14 +10,14 @@
}
@if (Visible)
{
<div @ref="@Element" role="tablist" @attributes="Attributes" class="@GetCssClass()" id="@GetId()">
<div @ref="@Element" role="tablist" style=@Style @attributes="Attributes" class="@GetCssClass()" id="@GetId()">
@for (var i = 0; i < items.Count; i++)
{
var item = items[i];
if (!item.Visible)
continue;
<div class="rz-accordion-header" @attributes="item.Attributes" style="@item.Style">
<div @ref="@item.Element" id="@item.GetItemId()" @attributes="item.Attributes" class="@item.GetItemCssClass()" style="@item.Style">
<a @onclick="@((args) => SelectItem(item))" href="javascript:void(0)" role="tab" tabindex="0"
id="@($"rz-accordiontab-{items.IndexOf(item)}")" aria-controls="@($"rz-accordiontab-{items.IndexOf(item)}-content")" aria-expanded="true">
@if (IsSelected(i, item))
@@ -34,7 +34,7 @@
}
@if (!string.IsNullOrEmpty(item.Text))
{
<span>@item.Text</span>
<span>@((MarkupString)item.Text)</span>
}
</a>
</div>

View File

@@ -109,5 +109,21 @@ namespace Radzen.Blazor
Accordion?.RemoveItem(this);
}
internal string GetItemId()
{
return GetId();
}
internal string GetItemCssClass()
{
return GetCssClass();
}
/// <inheritdoc />
protected override string GetComponentCssClass()
{
return "rz-accordion-header";
}
}
}

View File

@@ -8,12 +8,20 @@
<RadzenIcon Icon="@getIcon()" Class="rz-alert-icon" />
}
<div class="rz-alert-message">
@if (!string.IsNullOrEmpty(Title))
{
@if (!string.IsNullOrEmpty(Title))
{
<div class="rz-alert-title">@Title</div>
}
<div class="rz-alert-content">@ChildContent</div>
}
<div class="rz-alert-content">
@if(ChildContent != null)
{
@ChildContent
}
else
{
@Text
}
</div>
</div>
</div>
@if (AllowClose)

View File

@@ -23,7 +23,7 @@ namespace Radzen.Blazor
{
return Size == AlertSize.Medium ? "md" : Size == AlertSize.Large ? "lg" : Size == AlertSize.Small ? "sm" : "xs";
}
/// <summary>
/// Gets or sets a value indicating whether close is allowed. Set to <c>true</c> by default.
/// </summary>
@@ -45,6 +45,13 @@ namespace Radzen.Blazor
[Parameter]
public string Title { get; set; }
/// <summary>
/// Gets or sets the text of the alert. Overriden by <see cref="ChildContent" />.
/// </summary>
/// <value>The title.</value>
[Parameter]
public string Text { get; set; }
/// <summary>
/// Gets or sets the icon.
/// </summary>

View File

@@ -12,16 +12,7 @@
var value = ComposeValue(valueScale);
IPathGenerator pathGenerator;
if (Smooth)
{
pathGenerator = new SplineGenerator();
}
else
{
pathGenerator = new LineGenerator();
}
var pathGenerator = GetPathGenerator();
var data = Items.Select(item =>
{

View File

@@ -42,7 +42,17 @@ namespace Radzen.Blazor
/// Specifies whether to render a smooth line. Set to <c>false</c> by default.
/// </summary>
[Parameter]
public bool Smooth { get; set; }
public bool Smooth
{
get => Interpolation == Interpolation.Spline;
set => Interpolation = value ? Interpolation.Spline : Interpolation.Line;
}
/// <summary>
/// Specifies how to render lines between data points. Set to <see cref="Line"/> by default
/// </summary>
[Parameter]
public Interpolation Interpolation { get; set; } = Interpolation.Line;
/// <inheritdoc />
public override string Color
@@ -133,5 +143,20 @@ namespace Radzen.Blazor
{
return base.GetDataLabels(offsetX, offsetY - 16);
}
private IPathGenerator GetPathGenerator()
{
switch(Interpolation)
{
case Interpolation.Line:
return new LineGenerator();
case Interpolation.Spline:
return new SplineGenerator();
case Interpolation.Step:
return new StepGenerator();
default:
throw new NotSupportedException($"Interpolation {Interpolation} is not supported yet.");
}
}
}
}

View File

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

View File

@@ -38,10 +38,13 @@
@donut.RenderTitle(MarginLeft, MarginTop)
}
}
@if (tooltip != null)
{
@tooltip
}
<ChartTooltipContainer @ref="@chartTooltipContainer">
@if (tooltip != null)
{
@tooltip
}
</ChartTooltipContainer>
</CascadingValue>
}
</div>

View File

@@ -207,12 +207,7 @@ namespace Radzen.Blazor
ValueScale.Fit(ValueAxis.TickDistance);
CategoryScale.Fit(CategoryAxis.TickDistance);
var stateHasChanged = false;
if (!ValueScale.IsEqualTo(valueScale))
{
stateHasChanged = true;
}
var stateHasChanged = !ValueScale.IsEqualTo(valueScale);
if (!CategoryScale.IsEqualTo(categoryScale))
{
@@ -254,6 +249,7 @@ namespace Radzen.Blazor
}
}
ChartTooltipContainer chartTooltipContainer;
RenderFragment tooltip;
object tooltipData;
double mouseX;
@@ -313,7 +309,7 @@ namespace Radzen.Blazor
{
tooltipData = null;
tooltip = overlay.RenderTooltip(mouseX, mouseY, MarginLeft, MarginTop);
StateHasChanged();
chartTooltipContainer.Refresh();
await Task.Yield();
return;
@@ -328,7 +324,7 @@ namespace Radzen.Blazor
{
tooltipData = data;
tooltip = series.RenderTooltip(data, MarginLeft, MarginTop);
StateHasChanged();
chartTooltipContainer.Refresh();
await Task.Yield();
}
@@ -342,7 +338,7 @@ namespace Radzen.Blazor
tooltipData = null;
tooltip = null;
StateHasChanged();
chartTooltipContainer.Refresh();
await Task.Yield();
}
}

View File

@@ -24,7 +24,7 @@
}
@foreach (var item in allItems.Where(i => i.Visible))
{
<div class="rz-checkbox" @onclick="@(args => SelectItem(item))" @attributes="item.Attributes" style="@item.Style">
<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="@(async args => { if (args.Code == "Space") { await SelectItem(item); } })" tabindex="@(Disabled || item.Disabled ? "-1" : $"{TabIndex}")">
<div class="rz-helper-hidden-accessible">
<input type="checkbox" name="@Name" value="@item.Value" disabled="@Disabled" tabindex="-1">

View File

@@ -70,5 +70,20 @@ namespace Radzen.Blazor
{
Value = value;
}
internal string GetItemId()
{
return GetId();
}
internal string GetItemCssClass()
{
return GetCssClass();
}
/// <inheritdoc />
protected override string GetComponentCssClass()
{
return "rz-checkbox";
}
}
}

View File

@@ -366,7 +366,8 @@ namespace Radzen.Blazor
void Init()
{
var value = Value;
if (String.IsNullOrEmpty(Value))
if (String.IsNullOrEmpty(Value) || RGB.Parse(Value) == null)
{
value = "rgb(255, 255, 255)";
}

View File

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

View File

@@ -0,0 +1,245 @@
using Microsoft.AspNetCore.Components;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
namespace Radzen.Blazor
{
/// <summary>
/// RadzenColumn component.
/// </summary>
public partial class RadzenColumn : RadzenComponentWithChildren
{
/// <summary>
/// Gets or sets the size.
/// </summary>
/// <value>The size.</value>
[Parameter]
public int? Size { get; set; }
/// <summary>
/// Gets or sets the XS size.
/// </summary>
/// <value>The XS size.</value>
[Parameter]
public int? SizeXS { get; set; }
/// <summary>
/// Gets or sets the SM size.
/// </summary>
/// <value>The SM size.</value>
[Parameter]
public int? SizeSM { get; set; }
/// <summary>
/// Gets or sets the MD size.
/// </summary>
/// <value>The MD size.</value>
[Parameter]
public int? SizeMD { get; set; }
/// <summary>
/// Gets or sets the LG size.
/// </summary>
/// <value>The LG size.</value>
[Parameter]
public int? SizeLG { get; set; }
/// <summary>
/// Gets or sets the XL size.
/// </summary>
/// <value>The XL size.</value>
[Parameter]
public int? SizeXL { get; set; }
/// <summary>
/// Gets or sets the XX size.
/// </summary>
/// <value>The XX size.</value>
[Parameter]
public int? SizeXX { get; set; }
/// <summary>
/// Gets or sets the offset.
/// </summary>
/// <value>The offset.</value>
[Parameter]
public int? Offset { get; set; }
/// <summary>
/// Gets or sets the XS offset.
/// </summary>
/// <value>The XS offset.</value>
[Parameter]
public int? OffsetXS { get; set; }
/// <summary>
/// Gets or sets the SM offset.
/// </summary>
/// <value>The SM offset.</value>
[Parameter]
public int? OffsetSM { get; set; }
/// <summary>
/// Gets or sets the MD offset.
/// </summary>
/// <value>The MD offset.</value>
[Parameter]
public int? OffsetMD { get; set; }
/// <summary>
/// Gets or sets the LG offset.
/// </summary>
/// <value>The LG offset.</value>
[Parameter]
public int? OffsetLG { get; set; }
/// <summary>
/// Gets or sets the XL offset.
/// </summary>
/// <value>The XL offset.</value>
[Parameter]
public int? OffsetXL { get; set; }
/// <summary>
/// Gets or sets the XX offset.
/// </summary>
/// <value>The XX offset.</value>
[Parameter]
public int? OffsetXX { get; set; }
/// <summary>
/// Gets or sets the order.
/// </summary>
/// <value>The order.</value>
[Parameter]
public string Order { get; set; }
/// <summary>
/// Gets or sets the XS order.
/// </summary>
/// <value>The XS order.</value>
[Parameter]
public string OrderXS { get; set; }
/// <summary>
/// Gets or sets the SM order.
/// </summary>
/// <value>The SM order.</value>
[Parameter]
public string OrderSM { get; set; }
/// <summary>
/// Gets or sets the MD order.
/// </summary>
/// <value>The MD order.</value>
[Parameter]
public string OrderMD { get; set; }
/// <summary>
/// Gets or sets the LG order.
/// </summary>
/// <value>The LG order.</value>
[Parameter]
public string OrderLG { get; set; }
/// <summary>
/// Gets or sets the XL order.
/// </summary>
/// <value>The XL order.</value>
[Parameter]
public string OrderXL { get; set; }
/// <summary>
/// Gets or sets the XX order.
/// </summary>
/// <value>The XX order.</value>
[Parameter]
public string OrderXX { get; set; }
/// <summary>
/// Gets the final CSS style rendered by the component. Combines it with a <c>style</c> custom attribute.
/// </summary>
protected string GetStyle()
{
if (Attributes != null && Attributes.TryGetValue("style", out var style) && !string.IsNullOrEmpty(Convert.ToString(@style)))
{
return $"{GetComponentStyle()} {@style}";
}
return GetComponentStyle();
}
/// <summary>
/// Gets the component CSS style.
/// </summary>
protected string GetComponentStyle()
{
return $"{Style}{(!string.IsNullOrEmpty(Style) && !Style.EndsWith(";") ? ";" : "")}";
}
/// <inheritdoc />
protected override string GetComponentCssClass()
{
var list = new List<string>
{
Size != null ? $"rz-col-{GetColumnValue("Size", Size)}" : "rz-col"
};
if (Offset != null)
{
list.Add($"rz-offset-{GetColumnValue("Offset", Offset)}");
}
if (!string.IsNullOrEmpty(Order))
{
list.Add($"rz-order-{GetOrderValue("Order", Order)}");
}
var breakPoints = new string[] { "xs", "sm", "md", "lg", "xl", "xx" };
var properties = GetType().GetProperties()
.Where(p => breakPoints.Any(bp => p.Name.ToLower().EndsWith(bp)))
.Select(p => new { p.Name, BreakPoint = string.Concat(p.Name.ToLower().TakeLast(2)), Value = p.GetValue(this) });
foreach (var p in properties)
{
if (p.Value != null)
{
list.Add($"rz-{(!p.Name.StartsWith("Size") ? p.Name.ToLower().Replace(p.BreakPoint, "") + "-" : "col-")}{p.BreakPoint}-{GetColumnValue(p.Name, p.Value)}");
}
}
return string.Join(" ", list);
}
string GetColumnValue(string name, object value)
{
if (name.StartsWith("Order"))
{
return GetOrderValue(name, value.ToString());
}
if ((int)value < 0 || (int)value > 12)
{
throw new Exception($"Property {name} value should be between 0 and 12.");
}
return $"{value}";
}
string GetOrderValue(string name, string value)
{
var orders = Enumerable.Range(0, 12).Select(i => $"{i}").ToArray().Concat(new string[] { "first", "last" });
if (!orders.Contains(value))
{
throw new Exception($"Property {name} value should be between 0 and 12 or first/last.");
}
return value;
}
}
}

View File

@@ -42,7 +42,7 @@ else
@if (property.FilterTemplate != null)
{
<div class="rz-datafilter-editor" style="display:flex">
@property.FilterTemplate(property)
@property.FilterTemplate(Filter)
</div>
}
else if (PropertyAccess.IsNullableEnum(property.FilterPropertyType) || PropertyAccess.IsEnum(property.FilterPropertyType))
@@ -102,7 +102,11 @@ else
property.FilterValueChange += OnFilterValueChange;
Filter.Property = property.Property;
Filter.FilterOperator = property.GetFilterOperator();
if (!property.GetFilterOperators().Contains(Filter.FilterOperator))
{
Filter.FilterOperator = property.GetFilterOperators().FirstOrDefault();
}
var v = property.GetFilterValue();
if (v != null)
@@ -246,15 +250,7 @@ else
{
if (property != null && !string.IsNullOrEmpty(property.FormatString))
{
var formats = property.FormatString.Split(new char[] { '{', '}' }, StringSplitOptions.RemoveEmptyEntries);
if (formats.Length > 0)
{
var format = formats[0].Trim().Split(':');
if (format.Length > 1)
{
return format[1].Trim();
}
}
return property.FormatString.Replace("{0:", "").Replace("}", "");
}
return DataFilter.FilterDateFormat;

View File

@@ -130,7 +130,7 @@ namespace Radzen.Blazor
/// </summary>
/// <value>The filter template.</value>
[Parameter]
public RenderFragment<RadzenDataFilterProperty<TItem>> FilterTemplate { get; set; }
public RenderFragment<CompositeFilterDescriptor> FilterTemplate { get; set; }
/// <summary>
/// Gets or sets the data type.
@@ -293,10 +293,26 @@ 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))
&& FilterPropertyType != typeof(string))
{
return new FilterOperator[]
{
FilterOperator.Contains,
FilterOperator.DoesNotContain,
FilterOperator.Equals,
FilterOperator.NotEquals,
FilterOperator.IsNull,
FilterOperator.IsNotNull,
FilterOperator.IsEmpty,
FilterOperator.IsNotEmpty
};
}
return Enum.GetValues(typeof(FilterOperator)).Cast<FilterOperator>().Where(o => {
var isStringOperator = o == FilterOperator.Contains || o == FilterOperator.DoesNotContain
|| o == FilterOperator.StartsWith || o == FilterOperator.EndsWith || o == FilterOperator.IsEmpty || o == FilterOperator.IsNotEmpty;
return FilterPropertyType == typeof(string) || typeof(IEnumerable).IsAssignableFrom(FilterPropertyType) || typeof(IEnumerable<>).IsAssignableFrom(FilterPropertyType) ? isStringOperator
return FilterPropertyType == typeof(string) ? isStringOperator
|| o == FilterOperator.Equals || o == FilterOperator.NotEquals
|| o == FilterOperator.IsNull || o == FilterOperator.IsNotNull
: !isStringOperator;

View File

@@ -64,7 +64,7 @@
}
<div class="rz-data-grid-data">
<table class="rz-grid-table rz-grid-table-fixed @(AllowAlternatingRows ? "rz-grid-table-striped" : "") @(allColumns.Any(c => c.Parent != null) ? "rz-grid-table-composite" : "")">
<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())">
<colgroup>
@foreach(var g in Groups)
{
@@ -89,7 +89,7 @@
<span class="rz-column-title"></span>
</th>
}
@if (Template != null && ShowExpandColumn)
@if (Template != null && ShowExpandColumn && i == 0)
{
<th class="rz-col-icon rz-unselectable-text" scope="col" rowspan=@(deepestChildColumnLevel + 1)>
<span class="rz-column-title"></span>
@@ -112,7 +112,7 @@
var columnIndex = visibleColumns.IndexOf(column);
var sortableClass = AllowSorting && column.Sortable ? "rz-sortable-column" : "";
<RadzenDataGridHeaderCell RowIndex="@i" Grid="@this" Column="@column" ColumnIndex="@columnIndex" CssClass="@($"rz-unselectable-text {sortableClass} {column.HeaderCssClass} {getFrozenColumnClass(column, visibleColumns)} {(column.Columns != null || column.Parent != null ? "rz-composite-cell" : "")} {getColumnAlignClass(column)}".Trim())" Attributes="@(cellAttr)" />
<RadzenDataGridHeaderCell RowIndex="@i" Grid="@this" Column="@column" ColumnIndex="@columnIndex" CssClass="@($"rz-unselectable-text {sortableClass} {column.HeaderCssClass} {getFrozenColumnClass(column, visibleColumns)} {getCompositeCellCSSClass(column)} {getColumnAlignClass(column)}".Trim())" Attributes="@(cellAttr)" />
}
</tr>
}
@@ -133,7 +133,7 @@
}
@foreach (var column in visibleColumns)
{
<th colspan="@column.GetColSpan()" class="@($"rz-unselectable-text {getFrozenColumnClass(column, visibleColumns)}")" scope="col" style="@column.GetStyle(true, true)">
<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))
{
<div class="rz-cell-filter">
@@ -302,7 +302,7 @@
<tbody>
@if (Data != null)
{
@if (!ShowEmptyMessage || Count > 0 || !AllowPaging && LoadData.HasDelegate && Count == 0)
@if (!ShowEmptyMessage || Count > 0 && (IsVirtualizationAllowed() ? Data.Any() : true) || !AllowPaging && LoadData.HasDelegate && Count == 0)
{
if (columns.Count > 0)
{
@@ -312,7 +312,7 @@
else
{
<tr class=" rz-datatable-emptymessage-row">
<td class="rz-datatable-emptymessage" colspan="@(visibleColumns.Count + (Template != null && ShowExpandColumn ? 1 : 0))">
<td class="rz-datatable-emptymessage" colspan="@(visibleColumns.Sum(c => c.GetColSpan()) + (Template != null && ShowExpandColumn ? 1 : 0))">
@if (EmptyTemplate != null)
{
@EmptyTemplate
@@ -338,7 +338,7 @@
<span class="rz-column-title"></span>
</td>
}
@if (Template != null && ShowExpandColumn)
@if (Template != null && ShowExpandColumn && i == 0)
{
<td class="rz-col-icon rz-unselectable-text" scope="col" rowspan=@(deepestChildColumnLevel + 1)>
<span class="rz-column-title"></span>
@@ -358,7 +358,7 @@
}
<RadzenDataGridFooterCell RowIndex="@i" Grid="@this" Column="@column"
CssClass="@($"{column.FooterCssClass} {getFrozenColumnClass(column, visibleColumns)} {(column.Columns != null || column.Parent != null ? "rz-composite-cell" : "")}".Trim())"
CssClass="@($"{column.FooterCssClass} {getFrozenColumnClass(column, visibleColumns)} {getCompositeCellCSSClass(column)}".Trim())"
Attributes="@(cellAttr)" />
}
</tr>

View File

@@ -31,6 +31,19 @@ namespace Radzen.Blazor
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>
/// <value><c>true</c> if this instance is virtualized; otherwise, <c>false</c>.</value>
[Parameter]
public bool AllowVirtualization { get; set; }
/// <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>
[Parameter]
public int VirtualizationOverscanCount { get; set; }
internal void SetAllowVirtualization(bool allowVirtualization)
{
AllowVirtualization = allowVirtualization;
@@ -66,7 +79,7 @@ namespace Radzen.Blazor
var totalItemsCount = LoadData.HasDelegate ? Count : view.Count();
virtualDataItems = (LoadData.HasDelegate ? Data : itemToInsert != null ? (new[] { itemToInsert }).Concat(view.Skip(request.StartIndex).Take(top)) : view.Skip(request.StartIndex).Take(top)).ToList();
virtualDataItems = (LoadData.HasDelegate ? Data : itemToInsert != null ? (new[] { itemToInsert }).Concat(view.Skip(request.StartIndex).Take(top)) : view.Skip(request.StartIndex).Take(top))?.ToList();
return new Microsoft.AspNetCore.Components.Web.Virtualization.ItemsProviderResult<TItem>(virtualDataItems, totalItemsCount);
}
@@ -144,6 +157,11 @@ namespace Radzen.Blazor
});
}));
if(VirtualizationOverscanCount != default(int))
{
builder.AddAttribute(1, "OverscanCount", VirtualizationOverscanCount);
}
builder.AddComponentReferenceCapture(8, c => { virtualize = (Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize<TItem>)c; });
}
@@ -386,7 +404,7 @@ namespace Radzen.Blazor
var descriptor = sorts.Where(d => d.Property == column?.GetSortProperty()).FirstOrDefault();
if (descriptor == null && column.SortOrder.HasValue)
{
descriptor = new SortDescriptor() { Property = column.Property, SortOrder = column.SortOrder.Value };
descriptor = new SortDescriptor() { Property = column.GetSortProperty(), SortOrder = column.SortOrder.Value };
sorts.Add(descriptor);
}
@@ -484,15 +502,7 @@ namespace Radzen.Blazor
{
if (column != null && !string.IsNullOrEmpty(column.FormatString))
{
var formats = column.FormatString.Split(new char[] { '{', '}' }, StringSplitOptions.RemoveEmptyEntries);
if (formats.Length > 0)
{
var format = formats[0].Trim().Split(':');
if (format.Length > 1)
{
return format[1].Trim();
}
}
return column.FormatString.Replace("{0:", "").Replace("}", "");
}
return FilterDateFormat;
@@ -665,6 +675,13 @@ namespace Radzen.Blazor
[Parameter]
public EventCallback<DataGridColumnFilterEventArgs<TItem>> FilterCleared { get; set; }
/// <summary>
/// Gets or sets the render mode.
/// </summary>
/// <value>The render mode.</value>
[Parameter]
public PopupRenderMode FilterPopupRenderMode { get; set; } = PopupRenderMode.Initial;
internal async Task ClearFilter(RadzenDataGridColumn<TItem> column, bool closePopup = false)
{
if (closePopup)
@@ -995,14 +1012,7 @@ namespace Radzen.Blazor
/// <value>The empty template.</value>
[Parameter]
public RenderFragment EmptyTemplate { get; set; }
#if NET5_0_OR_GREATER
/// <summary>
/// Gets or sets a value indicating whether this instance is virtualized.
/// </summary>
/// <value><c>true</c> if this instance is virtualized; otherwise, <c>false</c>.</value>
[Parameter]
public bool AllowVirtualization { get; set; }
#endif
/// <summary>
/// Gets or sets a value indicating whether this instance loading indicator is shown.
/// </summary>
@@ -1059,6 +1069,13 @@ namespace Radzen.Blazor
[Parameter]
public bool AllowColumnPicking { get; set; }
/// <summary>
/// Gets or sets a value indicating whether cell data should be shown as tooltip.
/// </summary>
/// <value><c>true</c> if cell data is shown as tooltip; otherwise, <c>false</c>.</value>
[Parameter]
public bool ShowCellDataAsTooltip { get; set; } = true;
/// <summary>
/// Gets or sets the column picker columns showing text.
/// </summary>
@@ -1142,7 +1159,7 @@ namespace Radzen.Blazor
internal async Task EndColumnReorder(MouseEventArgs args, int columnIndex)
{
if (indexOfColumnToReoder != null)
if (indexOfColumnToReoder != null && AllowColumnReorder)
{
var visibleColumns = columns.Where(c => c.GetVisible()).ToList();
var columnToReorder = visibleColumns.ElementAtOrDefault(indexOfColumnToReoder.Value);
@@ -1213,6 +1230,60 @@ namespace Radzen.Blazor
[Parameter]
public EventCallback<DataGridColumnReorderedEventArgs<TItem>> ColumnReordered { get; set; }
IQueryable<TItem> GetSelfRefView(IQueryable<TItem> view, string orderBy)
{
if (!string.IsNullOrEmpty(orderBy))
{
if (typeof(TItem) == typeof(object))
{
var firstItem = view.FirstOrDefault();
if (firstItem != null)
{
view = view.Cast(firstItem.GetType()).AsQueryable().OrderBy(orderBy).Cast<TItem>();
}
}
else
{
view = view.OrderBy(orderBy);
}
}
var viewList = view.ToList();
var countWithChildren = viewList.Count + childData.SelectMany(d => d.Value.Data).Count();
for (int i = 0; i < countWithChildren; i++)
{
var item = viewList.ElementAtOrDefault(i);
if (item != null && childData.ContainsKey(item))
{
var level = 1;
var parentChildData = childData[item].ParentChildData;
while (parentChildData != null)
{
parentChildData = parentChildData.ParentChildData;
level++;
}
childData[item].Level = level;
var cd = childData[item].Data.AsQueryable();
if (!string.IsNullOrEmpty(orderBy))
{
cd = cd.OrderBy(orderBy);
}
viewList.InsertRange(viewList.IndexOf(item) + 1, cd);
}
}
view = viewList.AsQueryable()
.Where(i => childData.ContainsKey(i) && childData[i].Data.AsQueryable().Where<TItem>(allColumns).Any()
|| viewList.AsQueryable().Where<TItem>(allColumns).Contains(i));
return view;
}
/// <summary>
/// Gets the view - Data with sorting, filtering and paging applied.
/// </summary>
@@ -1221,66 +1292,26 @@ namespace Radzen.Blazor
{
get
{
if(LoadData.HasDelegate)
var orderBy = GetOrderBy();
if (LoadData.HasDelegate)
{
return base.View;
if (childData.Any())
{
return GetSelfRefView(base.View, orderBy);
}
else
{
return base.View;
}
}
IQueryable<TItem> view;
var orderBy = GetOrderBy();
if (childData.Any())
{
view = base.View;//.Where<TItem>(allColumns);
if (!string.IsNullOrEmpty(orderBy))
{
if (typeof(TItem) == typeof(object))
{
var firstItem = view.FirstOrDefault();
if (firstItem != null)
{
view = view.Cast(firstItem.GetType()).AsQueryable().OrderBy(orderBy).Cast<TItem>();
}
}
else
{
view = view.OrderBy(orderBy);
}
}
var viewList = view.ToList();
var countWithChildren = viewList.Count + childData.SelectMany(d => d.Value.Data).Count();
for (int i = 0; i < countWithChildren; i++)
{
var item = viewList.ElementAtOrDefault(i);
if (item != null && childData.ContainsKey(item))
{
var level = 1;
var parentChildData = childData[item].ParentChildData;
while (parentChildData != null)
{
parentChildData = parentChildData.ParentChildData;
level++;
}
childData[item].Level = level;
var cd = childData[item].Data.AsQueryable();
if (!string.IsNullOrEmpty(orderBy))
{
cd = cd.OrderBy(orderBy);
}
viewList.InsertRange(viewList.IndexOf(item) + 1, cd);
}
}
view = viewList.AsQueryable()
.Where(i => childData.ContainsKey(i) && childData[i].Data.AsQueryable().Where<TItem>(allColumns).Any()
|| viewList.AsQueryable().Where<TItem>(allColumns).Contains(i));
view = GetSelfRefView(base.View, orderBy);
}
else
{
@@ -1529,6 +1560,8 @@ namespace Radzen.Blazor
});
sorts.Clear();
}
SaveSettings();
}
/// <summary>
@@ -1667,11 +1700,30 @@ namespace Radzen.Blazor
return !collapsedGroupItems.Keys.Contains(item);
}
/// <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>
[Parameter]
public bool? AllGroupsExpanded { get; set; }
/// <summary>
/// Gets or sets the AllGroupsExpanded changed callback.
/// </summary>
/// <value>The AllGroupsExpanded changed callback.</value>
[Parameter]
public EventCallback<bool?> AllGroupsExpandedChanged { get; set; }
internal bool? allGroupsExpanded;
internal async System.Threading.Tasks.Task ExpandGroupItem(RadzenDataGridGroupRow<TItem> item, bool? expandedOnLoad)
{
if (expandedOnLoad == true)
return;
allGroupsExpanded = null;
await AllGroupsExpandedChanged.InvokeAsync(allGroupsExpanded);
if (!collapsedGroupItems.Keys.Contains(item))
{
await GroupRowCollapse.InvokeAsync(item.Group);
@@ -1733,15 +1785,20 @@ namespace Radzen.Blazor
public override async Task SetParametersAsync(ParameterView parameters)
{
var emptyTextChanged = parameters.DidParameterChange(nameof(EmptyText), EmptyText);
if (emptyTextChanged)
{
await ChangeState();
}
var allowColumnPickingChanged = parameters.DidParameterChange(nameof(AllowColumnPicking), AllowColumnPicking);
visibleChanged = parameters.DidParameterChange(nameof(Visible), Visible);
bool valueChanged = parameters.DidParameterChange(nameof(Value), Value);
var allGroupsExpandedChanged = parameters.DidParameterChange(nameof(AllGroupsExpanded), AllGroupsExpanded);
if (allGroupsExpandedChanged)
{
allGroupsExpanded = parameters.GetValueOrDefault<bool?>(nameof(AllGroupsExpanded));
}
await base.SetParametersAsync(parameters);
if (valueChanged)
@@ -1754,6 +1811,22 @@ namespace Radzen.Blazor
}
}
if (allowColumnPickingChanged || emptyTextChanged || allGroupsExpandedChanged && Groups.Any())
{
if (allGroupsExpandedChanged && Groups.Any() && allGroupsExpanded == true)
{
collapsedGroupItems.Clear();
}
if (allowColumnPickingChanged)
{
selectedColumns = allColumns.Where(c => c.Pickable && c.GetVisible()).ToList();
allPickableColumns = allColumns.Where(c => c.Pickable).ToList();
}
await ChangeState();
}
if (visibleChanged && !firstRender)
{
if (Visible == false)
@@ -1812,6 +1885,61 @@ namespace Radzen.Blazor
await ExpandItem(item);
}
/// <summary>
/// Expands a range of rows.
/// </summary>
/// <param name="items">The range of rows.</param>
public async System.Threading.Tasks.Task ExpandRows(IEnumerable<TItem> items)
{
// Only allow the functionality when multiple row expand is allowed
if (this.ExpandMode != DataGridExpandMode.Multiple) return;
foreach (TItem item in items)
{
if (!expandedItems.Keys.Contains(item))
{
expandedItems.Add(item, true);
await RowExpand.InvokeAsync(item);
var args = new DataGridLoadChildDataEventArgs<TItem>() { Item = item };
await LoadChildData.InvokeAsync(args);
if (args.Data != null && !childData.ContainsKey(item))
{
childData.Add(item, new DataGridChildData<TItem>() { Data = args.Data, ParentChildData = childData.Where(c => c.Value.Data.Contains(item)).Select(c => c.Value).FirstOrDefault() });
_view = null;
}
}
}
await InvokeAsync(StateHasChanged);
}
/// <summary>
/// Collapse a range of rows.
/// </summary>
/// <param name="items">The range of rows.</param>
public async System.Threading.Tasks.Task CollapseRows(IEnumerable<TItem> items)
{
// Only allow the functionality when multiple row expand is allowed
if (this.ExpandMode != DataGridExpandMode.Multiple) return;
foreach (TItem item in items)
{
if (expandedItems.Keys.Contains(item))
{
expandedItems.Remove(item);
await RowCollapse.InvokeAsync(item);
if (childData.ContainsKey(item))
{
childData.Remove(item);
_view = null;
}
}
}
await InvokeAsync(StateHasChanged);
}
internal async System.Threading.Tasks.Task ExpandItem(TItem item)
{
if (ExpandMode == DataGridExpandMode.Single && expandedItems.Keys.Any() && !LoadChildData.HasDelegate)
@@ -1875,6 +2003,33 @@ namespace Radzen.Blazor
[Parameter]
public bool AllowAlternatingRows { get; set; } = true;
/// <summary>
/// Gets or sets the grid lines.
/// </summary>
/// <value>The grid lines.</value>
[Parameter]
public DataGridGridLines GridLines { get; set; } = DataGridGridLines.Default;
internal bool ShowGridLines(RadzenDataGridColumn<TItem> column)
{
return column.Columns != null || column.Parent != null;
}
internal string getCompositeCellCSSClass(RadzenDataGridColumn<TItem> column)
{
return column.Columns != null || column.Parent != null ? "rz-composite-cell" : "";
}
internal string getGridLinesCSSClass()
{
if (GridLines == DataGridGridLines.Default)
{
return "";
}
return $"rz-grid-gridlines-{Enum.GetName(typeof(DataGridGridLines), GridLines).ToLower()}";
}
/// <summary>
/// Gets or sets the selection mode.
/// </summary>
@@ -1965,9 +2120,10 @@ namespace Radzen.Blazor
/// Selects the row.
/// </summary>
/// <param name="item">The item.</param>
public async System.Threading.Tasks.Task SelectRow(TItem item)
/// <param name="raiseEvent">Should raise RowSelect event.</param>
public async System.Threading.Tasks.Task SelectRow(TItem item, bool raiseEvent = true)
{
await OnRowSelect(item, true);
await OnRowSelect(item, raiseEvent);
}
internal async System.Threading.Tasks.Task OnRowDblClick(DataGridRowMouseEventArgs<TItem> args)
@@ -2040,6 +2196,30 @@ namespace Radzen.Blazor
}
}
/// <summary>
/// Edits a range of rows.
/// </summary>
/// <param name="items">The range of rows.</param>
public async System.Threading.Tasks.Task EditRows(IEnumerable<TItem> items)
{
// Only allow the functionality when multiple row edits is allowed
if (this.EditMode != DataGridEditMode.Multiple) return;
foreach (TItem item in items)
{
if (!editedItems.Keys.Contains(item))
{
editedItems.Add(item, true);
var editContext = new EditContext(item);
editContexts.Add(item, editContext);
await RowEdit.InvokeAsync(item);
}
}
StateHasChanged();
}
/// <summary>
/// Updates the row.
/// </summary>
@@ -2117,6 +2297,23 @@ namespace Radzen.Blazor
}
}
/// <summary>
/// Cancels the edit of a range of rows.
/// </summary>
/// <param name="items">The range of rows.</param>
public void CancelEditRows(IEnumerable<TItem> items)
{
foreach (TItem item in items)
{
if (editedItems.Keys.Contains(item))
{
editedItems.Remove(item);
editContexts.Remove(item);
}
}
StateHasChanged();
}
/// <summary>
/// Determines whether row in edit mode.
/// </summary>
@@ -2289,8 +2486,11 @@ namespace Radzen.Blazor
internal async Task EndColumnDropToGroup()
{
if(indexOfColumnToReoder != null)
if(indexOfColumnToReoder != null && AllowGrouping)
{
var functionName = $"Radzen['{getColumnResizerId(indexOfColumnToReoder.Value)}end']";
await JSRuntime.InvokeVoidAsync("eval", $"{functionName} && {functionName}()");
var column = columns.Where(c => c.GetVisible()).ElementAtOrDefault(indexOfColumnToReoder.Value);
if(column != null && column.Groupable && !string.IsNullOrEmpty(column.GetGroupProperty()))
@@ -2337,7 +2537,14 @@ namespace Radzen.Blazor
Data = null;
}
InvokeAsync(Reload);
if (IsVirtualizationAllowed() && LoadData.HasDelegate)
{
Debounce(() => InvokeAsync(Reload), 500);
}
else
{
InvokeAsync(Reload);
}
}
/// <summary>
@@ -2475,6 +2682,7 @@ namespace Radzen.Blazor
FilterOperator = c.GetFilterOperator(),
SecondFilterValue = c.GetSecondFilterValue(),
SecondFilterOperator = c.GetSecondFilterOperator(),
LogicalFilterOperator = c.GetLogicalFilterOperator()
}).ToList(),
CurrentPage = CurrentPage,
PageSize = PageSize,
@@ -2493,6 +2701,12 @@ namespace Radzen.Blazor
if (SettingsChanged.HasDelegate)
{
var shouldUpdateState = false;
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 ||
c.SecondFilterOperator == FilterOperator.IsEmpty || c.SecondFilterOperator == FilterOperator.IsNotEmpty);
if (settings.Columns != null)
{
@@ -2547,6 +2761,18 @@ namespace Radzen.Blazor
gridColumn.SetFilterValue(GetFilterValue(column.SecondFilterValue, gridColumn.FilterPropertyType), false);
shouldUpdateState = true;
}
if (gridColumn.GetSecondFilterOperator() != column.SecondFilterOperator)
{
gridColumn.SetSecondFilterOperator(column.SecondFilterOperator);
shouldUpdateState = true;
}
if (gridColumn.GetLogicalFilterOperator() != column.LogicalFilterOperator)
{
gridColumn.SetLogicalFilterOperator(column.LogicalFilterOperator);
shouldUpdateState = true;
}
}
}
}
@@ -2575,9 +2801,13 @@ namespace Radzen.Blazor
if (shouldUpdateState)
{
skip = CurrentPage * PageSize;
CalculatePager();
UpdateColumnsOrder();
await Reload();
if (hasFilter ? skip < View.Count() : true)
{
CalculatePager();
UpdateColumnsOrder();
await Reload();
}
}
}
}
@@ -2673,6 +2903,7 @@ namespace Radzen.Blazor
{
c.SetVisible(true);
});
columns = allColumns.Where(c => c.Parent == null).ToList();
InvokeAsync(Reload);
canSaveSettings = true;

View File

@@ -15,7 +15,7 @@ else
@foreach(var column in Grid.childColumns.Where(c => c.GetVisible() && c.Parent == Column))
{
<RadzenDataGridCell Row=@Row EditContext=EditContext RowIndex="@RowIndex" Grid="@Grid" Column="@column" Item="@Item"
Style="@column.GetStyle(true)" CssClass="@(column.CssClass + " " + Grid.getFrozenColumnClass(column, Grid.ColumnsCollection) + " " + (column.Columns != null || column.Parent != null ? "rz-composite-cell" : ""))" Attributes="@(Grid.CellAttributes(Item, column))">
Style="@column.GetStyle(true)" CssClass="@(column.CssClass + " " + Grid.getFrozenColumnClass(column, Grid.ColumnsCollection) + " " + Grid.getCompositeCellCSSClass(column))" Attributes="@(Grid.CellAttributes(Item, column))">
@if (Grid.Responsive)
{
<span class="rz-column-title">
@@ -29,7 +29,7 @@ else
}
</span>
}
<span class="rz-cell-data" title="@(column.Template == null ? column.GetValue(Item) : "")">
<span class="rz-cell-data" title="@(column.Template == null && Grid.ShowCellDataAsTooltip ? column.GetValue(Item) : "")">
@if (Item != null)
{
@if (Grid.IsRowInEditMode(Item) && column.EditTemplate != null)

View File

@@ -196,6 +196,11 @@ namespace Radzen.Blazor
if (Grid != null)
{
if (value == false)
{
Grid.GetJSRuntime().InvokeVoidAsync("Radzen.destroyPopup", $"{Grid.PopupID}{GetFilterProperty()}");
}
Grid.UpdatePickableColumn(this, _visible == true);
}
}
@@ -421,7 +426,16 @@ namespace Radzen.Blazor
{
var value = propertyValueGetter != null && !string.IsNullOrEmpty(Property) && !Property.Contains('.') ? propertyValueGetter(item) : !string.IsNullOrEmpty(Property) ? PropertyAccess.GetValue(item, Property) : "";
return !string.IsNullOrEmpty(FormatString) ? string.Format(FormatString, value, Grid?.Culture ?? CultureInfo.CurrentCulture) : Convert.ToString(value, Grid?.Culture ?? CultureInfo.CurrentCulture);
if ((PropertyAccess.IsEnum(FilterPropertyType) || PropertyAccess.IsNullableEnum(FilterPropertyType)) && value != null)
{
var enumValue = value as Enum;
if (enumValue != null)
{
value = EnumExtensions.GetDisplayDescription(enumValue);
}
}
return !string.IsNullOrEmpty(FormatString) ? string.Format(Grid?.Culture ?? CultureInfo.CurrentCulture, FormatString, value) : Convert.ToString(value, Grid?.Culture ?? CultureInfo.CurrentCulture);
}
internal object GetHeader()

View File

@@ -144,6 +144,8 @@
Column.SetFilterOperator(value);
Grid.SaveSettings();
await Grid.Filter.InvokeAsync(new DataGridColumnFilterEventArgs<TItem>()
{
Column = Column,

View File

@@ -15,7 +15,7 @@ else
@foreach(var column in Grid.childColumns.Where(c => c.GetVisible() && c.Parent == Column))
{
<RadzenDataGridFooterCell RowIndex="@RowIndex" Grid="@Grid" Column="@column"
CssClass="@($"{Column.FooterCssClass} {Grid.getFrozenColumnClass(column, Grid.ColumnsCollection.Where(c => c.GetVisible()).ToList())} {(column.Columns != null || column.Parent != null ? "rz-composite-cell" : "")}")"
CssClass="@($"{Column.FooterCssClass} {Grid.getFrozenColumnClass(column, Grid.ColumnsCollection.Where(c => c.GetVisible()).ToList())} {Grid.getCompositeCellCSSClass(column)}")"
Attributes="@(Attributes)" />
}
}

View File

@@ -3,7 +3,7 @@
@{
var rowArgs = Grid?.GroupRowAttributes(this);
}
<tr @attributes="@rowArgs.Item2">
<tr class="rz-group-row" @attributes="@rowArgs.Item2">
@if (Group.GroupDescriptor != null)
{
@for (var i = 0; i < GetLevel(); i++)
@@ -17,7 +17,7 @@
<td class="rz-col-icon">
<span class="rz-column-title"></span>
<a href="javascript:void(0)" @onclick="@(_ => Grid.ExpandGroupItem(this, rowArgs.Item1.Expanded))">
<span class="@(Grid.ExpandedGroupItemStyle(this, rowArgs.Item1.Expanded))"></span>
<span class="@(Grid.ExpandedGroupItemStyle(this, Grid.allGroupsExpanded != null ? Grid.allGroupsExpanded : rowArgs.Item1.Expanded))"></span>
</a>
</td>
<td colspan="@(Columns.Count + Grid.Groups.Count - 1 - Group.Level + (Grid.Template != null && Grid.ShowExpandColumn ? 1 : 0))">
@@ -28,14 +28,14 @@
}
else if(Group.GroupDescriptor != null)
{
@(Group.GroupDescriptor.GetTitle() + ": " + (Group.Data.Key ?? ""))
@(Group.GroupDescriptor.GetTitle() + ": " + (Group.Data.Key != null ? Group.Data.Key.ToString() : ""))
}
</span>
</td>
</tr>
@if(Grid != null)
{
if (Grid.IsGroupItemExpanded(this) && rowArgs.Item1.Expanded != false)
if (Grid.IsGroupItemExpanded(this) && rowArgs.Item1.Expanded != false && Grid.allGroupsExpanded != false)
{
@DrawDataRows()
}

View File

@@ -1,12 +1,14 @@
@typeparam TItem
@using Radzen.Blazor.Rendering
@if (RowIndex == Column.GetLevel())
{
<th rowspan="@(Column.GetRowSpan())" colspan="@(Column.GetColSpan())" @attributes="@Attributes" class="@CssClass" scope="col" style="@Column.GetStyle(true, true)" @onmouseup=@(args => Grid.EndColumnReorder(args, ColumnIndex)) >
<div @onclick='@((args) => Grid.OnSort(args, Column))' tabindex="@SortingTabIndex" @onkeydown="OnSortKeyPressed">
@if (Column.Parent == null && Column.Columns == null && (Grid.AllowColumnReorder && Column.Reorderable || Grid.AllowGrouping && Column.Groupable))
{
<span id="@Grid.getColumnResizerId(ColumnIndex)" class="rz-column-drag"
<span id="@Grid.getColumnResizerId(ColumnIndex)" class="rz-column-drag"
@onclick:preventDefault="true" @onclick:stopPropagation="true"
@onmousedown:preventDefault="true"
@onmousedown=@(args => Grid.StartColumnReorder(args, ColumnIndex))></span>
}
<span class="rz-column-title" title="@Column.Title">
@@ -18,7 +20,7 @@
{
<span class="rz-column-title-content">@Column.Title</span>
}
@if (Grid.AllowSorting && Column.Sortable)
{
@if (Column.GetSortOrder() == SortOrder.Ascending)
@@ -46,23 +48,25 @@
@if (Grid.AllowColumnResize && Column.Resizable && Column.Parent == null)
{
<div id="@Grid.getColumnResizerId(ColumnIndex)" style="cursor:col-resize;float:right;"
@onclick:preventDefault="true" @onclick:stopPropagation="true" class="rz-column-resizer"
@onclick:preventDefault="true" @onclick:stopPropagation="true" class="rz-column-resizer"
@onmousedown:preventDefault="true"
@onmousedown=@(args => Grid.StartColumnResize(args, ColumnIndex))>&nbsp;</div>
}
@if (Grid.AllowFiltering && Column.Filterable && Grid.FilterMode == FilterMode.Advanced)
{
<i @onclick:stopPropagation="true" onclick="@($"Radzen.togglePopup(this, '{getColumnPopupID()}')")"
class="@getFilterIconCss(Column)" />
<i @ref=@filterButton @onclick:stopPropagation="true" @onmousedown=@ToggleFilter
class="@getFilterIconCss(Column)" onclick=@getFilterOpen() />
<div id="@($"{getColumnPopupID()}")" class="rz-overlaypanel"
<Popup Lazy=@(Grid.FilterPopupRenderMode == PopupRenderMode.OnDemand) @ref=popup id="@($"{getColumnPopupID()}")" class="rz-overlaypanel"
style="display:none;min-width:250px;" tabindex="0">
<div class="rz-grid-filter rz-overlaypanel-content">
<div class="rz-overlaypanel-content">
@if (Column.FilterTemplate != null)
{
@Column.FilterTemplate(Column)
}
else
{
<form id="@($"{getColumnPopupID()}-form")" @onsubmit="@(args => ApplyFilter())" class="rz-grid-filter">
<RadzenLabel Text="@Grid.FilterText" class="rz-grid-filter-label" />
<RadzenDropDown @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 (PropertyAccess.IsNullableEnum(Column.FilterPropertyType) || PropertyAccess.IsEnum(Column.FilterPropertyType))
@@ -113,22 +117,23 @@
}
else if (Column.FilterPropertyType == typeof(bool) || Column.FilterPropertyType == typeof(bool?))
{
<RadzenCheckBox TriState="true" TValue="@object" Value="@Column.GetSecondFilterValue()" Change="@(args => { Column.SetFilterValue(args, false); Grid.SaveSettings(); })" />
<RadzenCheckBox TriState="true" TValue="@object" Value="@Column.GetSecondFilterValue()" Change="@(args => { Column.SetFilterValue(args, false); Grid.SaveSettings(); })" />
}
else
{
<RadzenTextBox Value="@($"{Column.GetSecondFilterValue()}")" Change="@(args => Column.SetFilterValue(args, false))" />
}
</form>
}
</div>
@if (Column.FilterTemplate == null)
{
<div class="rz-grid-filter-buttons">
<RadzenButton ButtonStyle="ButtonStyle.Secondary" Text=@Grid.ClearFilterText Click="@((args) => Grid.ClearFilter(Column, true))" />
<RadzenButton ButtonStyle="ButtonStyle.Primary" Text=@Grid.ApplyFilterText Click="@((args) => Grid.ApplyFilter(Column, true))" />
<RadzenButton ButtonStyle="ButtonStyle.Secondary" Text=@Grid.ClearFilterText Click="@ClearFilter" />
<RadzenButton form="@($"{getColumnPopupID()}-form")" ButtonType="ButtonType.Submit" ButtonStyle="ButtonStyle.Primary" Text=@Grid.ApplyFilterText Click="@ApplyFilter" />
</div>
}
</div>
</Popup>
}
</div>
</th>
@@ -137,11 +142,45 @@ else
{
@foreach(var column in Grid.childColumns.Where(c => c.GetVisible() && c.Parent == Column))
{
<RadzenDataGridHeaderCell RowIndex="@RowIndex" Grid="@Grid" Column="@column" ColumnIndex="@ColumnIndex"
CssClass="@($"rz-unselectable-text {(Grid.AllowSorting && column.Sortable ? "rz-sortable-column" : "")} {column.HeaderCssClass} {Grid.getFrozenColumnClass(column, Grid.ColumnsCollection.Where(c => c.GetVisible()).ToList())} {(column.Columns != null || column.Parent != null ? "rz-composite-cell" : "")} {Grid.getColumnAlignClass(column)}".Trim())" />
<RadzenDataGridHeaderCell RowIndex="@RowIndex" Grid="@Grid" Column="@column" ColumnIndex="@ColumnIndex"
CssClass="@($"rz-unselectable-text {(Grid.AllowSorting && column.Sortable ? "rz-sortable-column" : "")} {column.HeaderCssClass} {Grid.getFrozenColumnClass(column, Grid.ColumnsCollection.Where(c => c.GetVisible()).ToList())} {Grid.getCompositeCellCSSClass(column)} {Grid.getColumnAlignClass(column)}".Trim())" />
}
}
@code {
Radzen.Blazor.Rendering.Popup popup;
ElementReference filterButton;
string getFilterOpen()
{
return Grid.FilterPopupRenderMode == PopupRenderMode.Initial ? $"Radzen.togglePopup(this, '{getColumnPopupID()}')" : "";
}
async Task ToggleFilter()
{
if (Grid.FilterPopupRenderMode == PopupRenderMode.OnDemand)
{
await popup.ToggleAsync(filterButton);
}
}
async Task ClearFilter()
{
if (Grid.FilterPopupRenderMode == PopupRenderMode.OnDemand)
{
await popup.CloseAsync();
}
await Grid.ClearFilter(Column, true);
}
async Task ApplyFilter()
{
if (Grid.FilterPopupRenderMode == PopupRenderMode.OnDemand)
{
await popup.CloseAsync();
}
await Grid.ApplyFilter(Column, true);
}
[Parameter(CaptureUnmatchedValues = true)]
public IReadOnlyDictionary<string, object> Attributes { get; set; }
@@ -164,8 +203,8 @@ else
private string getFilterIconCss(RadzenDataGridColumn<TItem> column)
{
var additionalStyle = column.GetFilterValue() != null || column.GetSecondFilterValue() != null ||
column.GetFilterOperator() == FilterOperator.IsNotNull || column.GetFilterOperator() == FilterOperator.IsNull
var additionalStyle = column.GetFilterValue() != null || column.GetSecondFilterValue() != null ||
column.GetFilterOperator() == FilterOperator.IsNotNull || column.GetFilterOperator() == FilterOperator.IsNull
|| column.GetFilterOperator() == FilterOperator.IsEmpty || column.GetFilterOperator() == FilterOperator.IsNotEmpty
? "rz-grid-filter-active" : "";
return $"rzi rz-grid-filter-icon {additionalStyle}";

View File

@@ -18,7 +18,7 @@
<span class="rz-column-title"></span>
@if (rowArgs.Item1.Expandable)
{
<a href="javascript:void(0)" @onclick="@(_ => Grid.ExpandItem(Item))">
<a href="javascript:void(0)" @onclick="@(_ => Grid.ExpandItem(Item))" @onclick:stopPropagation>
<span class="@(Grid.ExpandedItemStyle(Item))"></span>
</a>
}
@@ -61,7 +61,7 @@
}
<RadzenDataGridCell Row=@this EditContext=EditContext RowIndex="@i" Grid="@this.Grid" Item="@Item" Column="@column"
Style="@column.GetStyle(true)" CssClass="@(column.CssClass + " " + Grid.getFrozenColumnClass(column, Columns) + " " + (column.Columns != null || column.Parent != null ? "rz-composite-cell" : ""))" Attributes="@(cellAttr)">
Style="@column.GetStyle(true)" CssClass="@(column.CssClass + " " + Grid.getFrozenColumnClass(column, Columns) + " " + Grid.getCompositeCellCSSClass(column))" Attributes="@(cellAttr)">
@if (Grid.Responsive)
{
<span class="rz-column-title">
@@ -78,7 +78,7 @@
@if (Grid.LoadChildData.HasDelegate && Grid.ShowExpandColumn && Grid.allColumns.IndexOf(column) == 0)
{
<span class="rz-cell-toggle">
<a style="@(getExpandIconStyle(rowArgs.Item1.Expandable))" href="javascript:void(0)" @onclick="@(_ => Grid.ExpandItem(Item))">
<a style="@(getExpandIconStyle(rowArgs.Item1.Expandable))" href="javascript:void(0)" @onclick="@(_ => Grid.ExpandItem(Item))" @onclick:stopPropagation>
<span class="@(Grid.ExpandedItemStyle(Item))"></span>
</a>
<span class="rz-cell-data" title="@(column.Template == null ? column.GetValue(Item) : "")">
@@ -129,7 +129,7 @@
@if (Grid.Template != null && Grid.expandedItems.Keys.Contains(Item))
{
<tr class="rz-expanded-row-content">
<td colspan="@(Columns.Count + (Grid.ShowExpandColumn ? 1 : 0) + + Grid.Groups.Count)">
<td colspan="@(Columns.Sum(c => c.GetColSpan()) + (Grid.ShowExpandColumn ? 1 : 0) + Grid.Groups.Count)">
<div class="rz-expanded-row-template" style="position:sticky">
@Grid.Template(Item)
</div>

View File

@@ -12,36 +12,12 @@
{
@if (!WrapItems)
{
@foreach (var item in LoadData.HasDelegate ? Data : PagedView)
{
<ul class="rz-datalist-data">
<li>
@if (Template != null)
{
@Template(item)
}
else
{
<span>Template</span>
}
</li>
</ul>
}
@DrawDataListRows()
}
else
{
<div class="rz-g">
@foreach (var item in LoadData.HasDelegate ? Data : PagedView)
{
@if (Template != null)
{
@Template(item)
}
else
{
<span>Template</span>
}
}
@DrawDataListRows()
</div>
}
}

View File

@@ -1,4 +1,16 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.JSInterop;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Data.Common;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Text.Json;
using System.Threading.Tasks;
namespace Radzen.Blazor
{
@@ -29,5 +41,98 @@ namespace Radzen.Blazor
/// <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.
/// </summary>
/// <value><c>true</c> if this instance is virtualized; otherwise, <c>false</c>.</value>
[Parameter]
public bool AllowVirtualization { get; set; }
internal Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize<TItem> virtualize;
/// <summary>
/// Gets Virtualize component reference.
/// </summary>
public Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize<TItem> Virtualize
{
get
{
return virtualize;
}
}
private async ValueTask<Microsoft.AspNetCore.Components.Web.Virtualization.ItemsProviderResult<TItem>> LoadItems(Microsoft.AspNetCore.Components.Web.Virtualization.ItemsProviderRequest request)
{
var view = AllowPaging ? PagedView : View;
var top = request.Count;
if(top <= 0)
{
top = PageSize;
}
await LoadData.InvokeAsync(new Radzen.LoadDataArgs()
{
Skip = request.StartIndex,
Top = top
});
var totalItemsCount = LoadData.HasDelegate ? Count : view.Count();
var virtualDataItems = (LoadData.HasDelegate ? Data : view.Skip(request.StartIndex).Take(top))?.ToList();
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>));
builder.AddAttribute(1, "ItemsProvider", new Microsoft.AspNetCore.Components.Web.Virtualization.ItemsProviderDelegate<TItem>(LoadItems));
builder.AddAttribute(2, "ChildContent", (RenderFragment<TItem>)((context) =>
{
return (RenderFragment)((b) =>
{
DrawRow(b, context);
});
}));
builder.AddComponentReferenceCapture(4, c => { virtualize = (Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize<TItem>)c; });
builder.CloseComponent();
}
else
{
DrawRows(builder);
}
#else
DrawRows(builder);
#endif
});
}
internal void DrawRows(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder builder)
{
foreach (var item in LoadData.HasDelegate ? Data : PagedView)
{
DrawRow(builder, item);
}
}
internal void DrawRow(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder builder, TItem item)
{
builder.OpenComponent<RadzenDataListRow<TItem>>(0);
builder.AddAttribute(1, "DataList", this);
builder.AddAttribute(2, "Item", item);
builder.SetKey(item);
builder.CloseComponent();
}
}
}

View File

@@ -0,0 +1,36 @@
@typeparam TItem
@if (!DataList.WrapItems)
{
<ul class="rz-datalist-data">
<li>
@if (DataList.Template != null)
{
@DataList.Template(Item)
}
else
{
<span>Template</span>
}
</li>
</ul>
}
else
{
@if (DataList.Template != null)
{
@DataList.Template(Item)
}
else
{
<span>Template</span>
}
}
@code {
[Parameter]
public RadzenDataList<TItem> DataList { get; set; }
[Parameter]
public TItem Item { get; set; }
}

View File

@@ -564,6 +564,9 @@ namespace Radzen.Blazor
async Task Clear()
{
if (Disabled || ReadOnly)
return;
Value = null;
await ValueChanged.InvokeAsync(default(TValue));

View File

@@ -8,19 +8,57 @@
<DialogContainer Dialog=@dialog ShowMask=@(dialog==dialogs.LastOrDefault()) />
}
@if (isSideDialogOpen)
{
<aside
class="@GetSideDialogCssClass()"
tabindex="@(isSideDialogOpen ? "0" : "-1")"
style="@GetSideDialogStyle()"
aria-labelledby="rz-dialog-side-label"
>
@if (sideDialogOptions.ShowTitle)
{
<div class="rz-dialog-side-titlebar">
<div class="rz-dialog-side-title" style="display: inline" id="rz-dialog-side-label">@((MarkupString)sideDialogOptions.Title)</div>
@if (sideDialogOptions.ShowClose)
{
<a href="javascript:void(0)" class="rz-dialog-side-titlebar-close" @onclick="@(_ => Service.CloseSide(null))" role="button">
<span class="rzi rzi-times"></span>
</a>
}
</div>
}
<div class="rz-dialog-side-content" style="@sideDialogOptions.Style">
@sideDialogContent
</div>
</aside>
@if (dialogs.Count == 0 && sideDialogOptions.ShowMask)
{
@if (sideDialogOptions.CloseDialogOnOverlayClick)
{
<div @onclick="@Service.CloseSide" class="rz-dialog-mask"></div>
}
else
{
<div class="rz-dialog-mask"></div>
}
}
}
@code {
[Inject]
DialogService Service { get; set; }
List<Dialog> dialogs = new List<Dialog>();
bool isSideDialogOpen = false;
RenderFragment sideDialogContent;
SideDialogOptions sideDialogOptions;
public async Task Open(string title, Type type, Dictionary<string, object> parameters, DialogOptions options)
{
dialogs.Add(new Dialog() { Title = title, Type = type, Parameters = parameters, Options = options });
await InvokeAsync(() => { StateHasChanged(); });
await JSRuntime.InvokeAsync<string>("Radzen.openDialog", options, Service.Reference);
}
public async Task Close(dynamic result)
@@ -42,12 +80,40 @@
{
Service.OnOpen -= OnOpen;
Service.OnClose -= OnClose;
Service.OnSideOpen -= OnSideOpen;
Service.OnSideClose -= OnSideClose;
}
protected override void OnInitialized()
{
Service.OnOpen += OnOpen;
Service.OnClose += OnClose;
Service.OnSideOpen += OnSideOpen;
Service.OnSideClose += OnSideClose;
}
void OnSideOpen(Type sideComponent, Dictionary<string, object> parameters, SideDialogOptions options)
{
sideDialogOptions = options;
sideDialogContent = new RenderFragment(builder =>
{
builder.OpenComponent(0, sideComponent);
foreach (var parameter in parameters)
{
builder.AddAttribute(1, parameter.Key, parameter.Value);
}
builder.CloseComponent();
});
isSideDialogOpen = true;
StateHasChanged();
}
void OnSideClose(dynamic _)
{
isSideDialogOpen = false;
StateHasChanged();
}
void OnOpen(string title, Type type, Dictionary<string, object> parameters, DialogOptions options)
@@ -59,4 +125,14 @@
{
Close(result).ConfigureAwait(false);
}
}
string GetSideDialogCssClass()
{
return $"rz-dialog-side rz-dialog-side-position-{sideDialogOptions.Position.ToString().ToLower()} {sideDialogOptions.CssClass}";
}
string GetSideDialogStyle()
{
return $"{sideDialogOptions.Style}{(sideDialogOptions.Width != null ? $" width: {sideDialogOptions.Width}" : "")}{(sideDialogOptions.Height != null ? $" height: {sideDialogOptions.Height}" : "")}";
}
}

View File

@@ -42,9 +42,16 @@
{
<div class="rz-chip">
<span class="rz-chip-text">
@GetItemOrValueFromProperty(item, TextProperty)
</span>
<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>
@if(Template != null)
{
@Template(item)
}
else
{
@GetItemOrValueFromProperty(item, TextProperty)
}
</span>
<button 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>
</div>
}
</div>

View File

@@ -38,6 +38,13 @@ namespace Radzen.Blazor
/// <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 a value indicating whether search field need to be cleared after selection. Set to <c>false</c> by default.
/// </summary>
/// <value><c>true</c> if need to be cleared; otherwise, <c>false</c>.</value>
[Parameter]
public bool ClearSearchAfterSelection { get; set; }
private async Task OnFocus(Microsoft.AspNetCore.Components.Web.FocusEventArgs args)
{
@@ -195,6 +202,12 @@ namespace Radzen.Blazor
{
await JSRuntime.InvokeVoidAsync("Radzen.closePopup", PopupID);
}
if (ClearSearchAfterSelection)
{
await JSRuntime.InvokeAsync<string>("Radzen.setInputValue", search, string.Empty);
await OnFilter(null);
}
await SelectItem(item);
}

View File

@@ -46,9 +46,16 @@
{
<div class="rz-chip">
<span class="rz-chip-text">
@PropertyAccess.GetItemOrValueFromProperty(item, TextProperty)
@if (Template != null)
{
@Template(item)
}
else
{
@PropertyAccess.GetItemOrValueFromProperty(item, TextProperty)
}
</span>
<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 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>
</div>
}
</div>
@@ -60,13 +67,13 @@
{
@if (Template == null)
{
@(string.Join(",", itemsToUse.Select(i => PropertyAccess.GetItemOrValueFromProperty(i, TextProperty))))
@(string.Join(Separator, itemsToUse.Select(i => PropertyAccess.GetItemOrValueFromProperty(i, TextProperty))))
}
else
{
foreach (var item in itemsToUse)
{
@Template(item)@(",")
@Template(item)@(Separator)
}
}
}
@@ -106,7 +113,7 @@
@if (AllowFiltering)
{
<div class="rz-lookup-search">
<input id="@SearchID" @ref="@search" tabindex="-1" placeholder="@SearchText"
<input class="rz-lookup-search-input" id="@SearchID" @ref="@search" tabindex="-1" placeholder="@SearchText"
@onchange="@((args) => OnFilter(args))" @onkeydown="@((args) => OnFilterKeyPress(args))" value="@searchText" style="@(ShowSearch ? "" : "margin-right:0px;")" />
@if (ShowSearch)
{

View File

@@ -59,6 +59,11 @@ namespace Radzen.Blazor
/// </summary>
protected virtual void OnRowRender(RowRenderEventArgs<object> args)
{
if (disabledPropertyGetter != null && disabledPropertyGetter(args.Data) as bool? == true)
{
args.Attributes.Add("class", "rz-data-row rz-state-disabled");
}
if (RowRender != null)
{
RowRender(args);
@@ -327,14 +332,41 @@ namespace Radzen.Blazor
if (AllowFilteringByAllStringColumns)
{
query = query.Where(string.Join(" || ", grid.ColumnsCollection.Where(c => c.Filterable && IsColumnFilterPropertyTypeString(c))
.Select(c => GetPropertyFilterExpression(c.GetFilterProperty(), filterCaseSensitivityOperator))),
FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive ? searchText.ToLower() : searchText);
if (AllowFilteringByWord)
{
string[] words = searchText.Split(' ');
foreach (string word in words)
{
query = query.Where(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))
.Select(c => GetPropertyFilterExpression(c.GetFilterProperty(), filterCaseSensitivityOperator))),
FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive ? searchText.ToLower() : searchText);
}
}
else
{
query = query.Where($"{GetPropertyFilterExpression(TextProperty, filterCaseSensitivityOperator)}",
FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive ? searchText.ToLower() : searchText);
if (AllowFilteringByWord)
{
string[] words = searchText.Split(' ');
foreach (string word in words)
{
query = query.Where($"{GetPropertyFilterExpression(TextProperty, filterCaseSensitivityOperator)}",
FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive ? word.ToLower() : word);
}
}
else
{
query = query.Where($"{GetPropertyFilterExpression(TextProperty, filterCaseSensitivityOperator)}",
FilterCaseSensitivity == FilterCaseSensitivity.CaseInsensitive ? searchText.ToLower() : searchText);
}
}
}
@@ -595,6 +627,13 @@ namespace Radzen.Blazor
[Parameter]
public bool AllowFilteringByAllStringColumns { get; set; }
/// <summary>
/// Gets or sets a value indicating whether filtering by each entered word in the search term, sperated by a space, is allowed.
/// </summary>
/// <value><c>true</c> if filtering by individual words is allowed; otherwise, <c>false</c>.</value>
[Parameter]
public bool AllowFilteringByWord { get; set; }
/// <summary>
/// Gets or sets a value indicating whether DataGrid row can be selected on row click.
/// </summary>

View File

@@ -7,12 +7,12 @@
@if (DropDown.Multiple)
{
<li class="@GetComponentCssClass("rz-multiselect")"
aria-label="@DropDown.GetItemOrValueFromProperty(Item, DropDown.TextProperty)" style="display: block;white-space: nowrap;"
aria-label="@DropDown.GetItemOrValueFromProperty(Item, DropDown.TextProperty)"
@onmousedown:preventDefault @onmousedown="args=>SelectItem(args,false)"
@onclick:preventDefault @onclick="args=>SelectItem(args,true)">
<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) ? "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>
</div>
<span>
@@ -69,7 +69,7 @@ else
string result = $"{prefix}-item ";
if (Disabled)
result += "rz-state-disabled ";
else if (DropDown.isSelected(Item))
else if (DropDown.IsSelected(Item))
result += "rz-state-highlight ";
return result;

View File

@@ -497,15 +497,7 @@
{
if (column != null && !string.IsNullOrEmpty(column.FormatString))
{
var formats = column.FormatString.Split(new char[] { '{', '}' }, StringSplitOptions.RemoveEmptyEntries);
if (formats.Length > 0)
{
var format = formats[0].Trim().Split(':');
if (format.Length > 1)
{
return format[1].Trim();
}
}
return column.FormatString.Replace("{0:", "").Replace("}", "");
}
return FilterDateFormat;

View File

@@ -2,5 +2,5 @@
@if (Visible)
{
<label @ref="@Element" for="@Component" style="@Style" @attributes="Attributes" class="@GetCssClass()" id="@GetId()">@Text</label>
<label @ref="@Element" for="@Component" style="@Style" @attributes="Attributes" class="@GetCssClass()" id="@GetId()">@if(ChildContent != null){@ChildContent}else{@Text}</label>
}

View File

@@ -13,6 +13,13 @@ namespace Radzen.Blazor
/// </example>
public partial class RadzenLabel : RadzenComponent
{
/// <summary>
/// Gets or sets the child content.
/// </summary>
/// <value>The child content.</value>
[Parameter]
public RenderFragment ChildContent { get; set; }
/// <summary>
/// Gets or sets the component name for the label.
/// </summary>

View File

@@ -13,16 +13,7 @@
var value = ComposeValue(valueScale);
IPathGenerator pathGenerator;
if (Smooth)
{
pathGenerator = new SplineGenerator();
}
else
{
pathGenerator = new LineGenerator();
}
var pathGenerator = GetPathGenerator();
var data = Items.Select(item =>
{

View File

@@ -1,3 +1,4 @@
using System;
using Microsoft.AspNetCore.Components;
using Radzen.Blazor.Rendering;
using System.Collections.Generic;
@@ -34,7 +35,17 @@ namespace Radzen.Blazor
/// Specifies whether to render a smooth line. Set to <c>false</c> by default.
/// </summary>
[Parameter]
public bool Smooth { get; set; }
public bool Smooth
{
get => Interpolation == Interpolation.Spline;
set => Interpolation = value ? Interpolation.Spline : Interpolation.Line;
}
/// <summary>
/// Specifies how to render lines between data points. Set to <see cref="Line"/> by default
/// </summary>
[Parameter]
public Interpolation Interpolation { get; set; } = Interpolation.Line;
/// <inheritdoc />
public override string Color
@@ -121,5 +132,20 @@ namespace Radzen.Blazor
{
return base.GetDataLabels(offsetX, offsetY - 16);
}
private IPathGenerator GetPathGenerator()
{
switch(Interpolation)
{
case Interpolation.Line:
return new LineGenerator();
case Interpolation.Spline:
return new SplineGenerator();
case Interpolation.Step:
return new StepGenerator();
default:
throw new NotSupportedException($"Interpolation {Interpolation} is not supported yet.");
}
}
}
}

View File

@@ -6,6 +6,15 @@
{
<i class="rzi">@((MarkupString)Icon)</i>
}
<span @ref="@Element" class="rz-link-text">@Text</span>
<span @ref="@Element" class="rz-link-text">
@if (ChildContent != null)
{
@ChildContent
}
else
{
@Text
}
</span>
</Microsoft.AspNetCore.Components.Routing.NavLink>
}

View File

@@ -18,6 +18,13 @@ namespace Radzen.Blazor
return "rz-link";
}
/// <summary>
/// Gets or sets the child content.
/// </summary>
/// <value>The child content.</value>
[Parameter]
public RenderFragment ChildContent { get; set; }
/// <summary>
/// Gets or sets the target.
/// </summary>

View File

@@ -4,12 +4,12 @@
@using Microsoft.JSInterop
@typeparam TValue
<li class="@(ListBox.isSelected(Item) ? "rz-multiselect-item rz-state-highlight" : "rz-multiselect-item ")" aria-label="@PropertyAccess.GetItemOrValueFromProperty(Item, ListBox.TextProperty)" @onclick="@(async () => { if (!ListBox.Disabled) { await ListBox.SelectItemInternal(Item); } })">
<li class="@(ListBox.IsSelected(Item) ? "rz-multiselect-item rz-state-highlight" : "rz-multiselect-item ")" aria-label="@PropertyAccess.GetItemOrValueFromProperty(Item, ListBox.TextProperty)" @onclick="@(async () => { if (!ListBox.Disabled) { await ListBox.SelectItemInternal(Item); } })">
@if (ListBox.Multiple)
{
<div class="rz-chkbox ">
<div class="@(ListBox.isSelected(Item) ? "rz-chkbox-box rz-state-active" : "rz-chkbox-box ")">
<span class="@(ListBox.isSelected(Item) ? "rz-chkbox-icon rzi rzi-check" : "rz-chkbox-icon ")"></span>
<div class="@(ListBox.IsSelected(Item) ? "rz-chkbox-box rz-state-active" : "rz-chkbox-box ")">
<span class="@(ListBox.IsSelected(Item) ? "rz-chkbox-icon rzi rzi-check" : "rz-chkbox-icon ")"></span>
</div>
</div>
}

View File

@@ -200,17 +200,17 @@ namespace Radzen.Blazor
{
if (parameters.DidParameterChange(nameof(Username), Username))
{
username = Username;
username = parameters.GetValueOrDefault<string>(nameof(Username));
}
if (parameters.DidParameterChange(nameof(Password), Password))
{
password = Password;
password = parameters.GetValueOrDefault<string>(nameof(Password));
}
if (parameters.DidParameterChange(nameof(RememberMe), RememberMe))
{
rememberMe = RememberMe;
rememberMe = parameters.GetValueOrDefault<bool>(nameof(RememberMe));
}
await base.SetParametersAsync(parameters);
@@ -222,7 +222,7 @@ namespace Radzen.Blazor
/// <param name="args">The <see cref="EventArgs"/> instance containing the event data.</param>
protected async Task OnReset(EventArgs args)
{
await ResetPassword.InvokeAsync(Username);
await ResetPassword.InvokeAsync(username);
}
/// <summary>

View File

@@ -25,6 +25,13 @@ namespace Radzen.Blazor
[Parameter]
public bool Responsive { get; set; } = true;
/// <summary>
/// Gets or sets a value indicating whether this <see cref="RadzenMenu"/> should open item on click or on hover.
/// </summary>
/// <value><c>true</c> if open item on click; otherwise, <c>false</c> and items will open on hover.</value>
[Parameter]
public bool ClickToOpen { get; set; } = true;
private bool IsOpen { get; set; } = false;
/// <inheritdoc />

View File

@@ -2,8 +2,8 @@
@inherits RadzenComponent
@if (Visible)
{
<li @attributes="Attributes" class="@GetCssClass()" style="@Style" @onclick="@OnClick" @onclick:stopPropagation>
<div class="rz-navigation-item-wrapper" onclick="Radzen.toggleMenuItem(this)">
<RadzenMenuItemWrapper Item="@this" @attributes="@Attributes" class="@GetCssClass()" style="@Style">
<div class="rz-navigation-item-wrapper" @attributes="getOpenEvents()">
@if (!string.IsNullOrEmpty(Path))
{
<NavLink target="@Target" class="rz-navigation-item-link" href="@Path">
@@ -61,5 +61,5 @@
@ChildContent
</ul>
}
</li>
</RadzenMenuItemWrapper>
}

View File

@@ -1,5 +1,7 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Radzen.Blazor
@@ -119,5 +121,21 @@ namespace Radzen.Blazor
}
}
}
Dictionary<string, object> getOpenEvents()
{
var events = new Dictionary<string, object>();
if (Parent.ClickToOpen || ChildContent != null)
{
events.Add("onclick", "Radzen.toggleMenuItem(this)");
}
else
{
events.Add("onclick", "Radzen.toggleMenuItem(this, event, false)");
}
return events;
}
}
}

View File

@@ -0,0 +1,34 @@
@using Microsoft.AspNetCore.Components.Routing
@inherits RadzenComponentWithChildren
@if (Item.Parent.ClickToOpen)
{
<li @attributes="@Attributes" @onclick="@Item.OnClick" @onclick:stopPropagation>
@ChildContent
</li>
}
else
{
if (Item.ChildContent != null)
{
<li @attributes="@Attributes" onmouseenter="Radzen.toggleMenuItem(this, event, true)" onmouseleave="Radzen.toggleMenuItem(this, event, false)">
@ChildContent
</li>
}
else
{
<li @attributes="@Attributes" @onclick="@Item.OnClick" @onclick:stopPropagation>
@ChildContent
</li>
}
}
@code {
/// <summary>
/// Gets or sets the menu item.
/// </summary>
/// <value>The menu item.</value>
[Parameter]
public RadzenMenuItem Item { get; set; }
}

View File

@@ -1,6 +1,5 @@
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using Radzen.Blazor.Rendering;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -220,6 +219,12 @@ namespace Radzen.Blazor
{
valueStr = value.ToString();
}
if (!string.IsNullOrEmpty(Format))
{
valueStr = valueStr.Replace(Format.Replace("#", "").Trim(), "");
}
return new string(valueStr.Where(c => char.IsDigit(c) || char.IsPunctuation(c)).ToArray()).Replace("%", "");
}

View File

@@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Components;
using System;
using Microsoft.AspNetCore.Components;
namespace Radzen.Blazor
{
@@ -10,14 +11,14 @@ namespace Radzen.Blazor
/// <code>
/// &lt;RadzenTemplateForm TItem="Model" Data=@model&gt;
/// &lt;RadzenNumeric style="display: block" Name="Quantity" @bind-Value=@model.Quantity /&gt;
/// &lt;RadzenNumericRangeValidator Component="Quantity" Min="1" Max="10" Text="Quantity should be between 1 and 10" Style="position: absolute" /&gt;
/// &lt;RadzenNumericRangeValidator Component="Quantity" Min="1" Max="10" Text="Quantity should be between 1 and 10" Style="position: absolute" /&gt;
/// &lt;/RadzenTemplateForm&gt;
/// @code {
/// class Model
/// {
/// public decimal Quantity { get; set; }
/// }
/// Model model = new Model();
/// Model model = new Model();
/// }
/// </code>
/// </example>>
@@ -33,30 +34,61 @@ namespace Radzen.Blazor
/// Specifies the minimum value. The component value should be greater than the minimum in order to be valid.
/// </summary>
[Parameter]
public dynamic Min { get; set; }
public IComparable Min { get; set; }
/// <summary>
/// Specifies the maximum value. The component value should be less than the maximum in order to be valid.
/// </summary>
[Parameter]
public dynamic Max { get; set; }
public IComparable Max { get; set; }
/// <inheritdoc />
protected override bool Validate(IRadzenFormComponent component)
{
dynamic value = component.GetValue();
if (Min == null && Max == null)
{
throw new ArgumentException("Min and Max cannot be both null");
}
if (Min != null && ((value != null && value < Min) || value == null))
object value = component.GetValue();
if (value == null)
{
return false;
}
if (Max != null && (value != null && value > Max))
if (Min != null)
{
return false;
if (!TryConvertToType(value, Min.GetType(), out var convertedValue) || Min.CompareTo(convertedValue) > 0)
{
return false;
}
}
if (Max != null)
{
if (!TryConvertToType(value, Max.GetType(), out var convertedValue) || Max.CompareTo(convertedValue) < 0)
{
return false;
}
}
return true;
}
private bool TryConvertToType(object value, Type type, out object convertedValue)
{
try
{
convertedValue = Convert.ChangeType(value, type);
return true;
}
catch (InvalidCastException)
{
convertedValue = null;
return false;
}
}
}
}

View File

@@ -1,7 +1,9 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Routing;
using System;
using System.Linq;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Radzen.Blazor
{
@@ -20,6 +22,13 @@ namespace Radzen.Blazor
/// </example>
public partial class RadzenPanelMenu : RadzenComponentWithChildren
{
/// <summary>
/// Gets or sets a value indicating whether multiple items can be expanded.
/// </summary>
/// <value><c>true</c> if multiple items can be expanded; otherwise, <c>false</c>.</value>
[Parameter]
public bool Multiple { get; set; } = true;
/// <summary>
/// Gets or sets the click callback.
/// </summary>
@@ -33,7 +42,7 @@ namespace Radzen.Blazor
[Parameter]
public NavLinkMatch Match { get; set; }
List<RadzenPanelMenuItem> items = new List<RadzenPanelMenuItem>();
internal List<RadzenPanelMenuItem> items = new List<RadzenPanelMenuItem>();
/// <summary>
/// Adds the item.
@@ -70,6 +79,23 @@ namespace Radzen.Blazor
UriHelper.LocationChanged -= UriHelper_OnLocationChanged;
}
internal void CollapseAll(IEnumerable<RadzenPanelMenuItem> itemsToSkip)
{
items.Concat(items.SelectManyRecursive(i => i.items))
.Where(i => !itemsToSkip.Contains(i)).ToList().ForEach(i => InvokeAsync(i.Collapse));
}
/// <inheritdoc />
public override async Task SetParametersAsync(ParameterView parameters)
{
if (parameters.DidParameterChange(nameof(Multiple), Multiple))
{
CollapseAll(Enumerable.Empty<RadzenPanelMenuItem>());
}
await base.SetParametersAsync(parameters);
}
bool ShouldMatch(string url)
{
if (string.IsNullOrEmpty(url))

View File

@@ -1,6 +1,7 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Radzen.Blazor
@@ -95,11 +96,34 @@ namespace Radzen.Blazor
async System.Threading.Tasks.Task Toggle()
{
if (!expanded && !Parent.Multiple)
{
var itemsToSkip = new List<RadzenPanelMenuItem>();
var p = ParentItem;
while (p != null)
{
itemsToSkip.Add(p);
p = p.ParentItem;
}
Parent.CollapseAll(itemsToSkip);
}
expanded = !expanded;
await ExpandedChanged.InvokeAsync(expanded);
StateHasChanged();
}
internal async System.Threading.Tasks.Task Collapse()
{
if (expanded)
{
expanded = false;
await ExpandedChanged.InvokeAsync(expanded);
StateHasChanged();
}
}
string getStyle()
{
string deg = expanded ? "180" : "0";
@@ -164,7 +188,7 @@ namespace Radzen.Blazor
}
}
List<RadzenPanelMenuItem> items = new List<RadzenPanelMenuItem>();
internal List<RadzenPanelMenuItem> items = new List<RadzenPanelMenuItem>();
/// <summary>
/// Adds the item.

View File

@@ -17,7 +17,7 @@
<div @ref="@Element" style="@Style" @attributes="Attributes" class="@GetCssClass()" id="@GetId()">
@foreach (var item in allItems.Where(i => i.Visible))
{
<div class="rz-radio-btn" @onclick="@(args => SelectItem(item))" @attributes="item.Attributes" style="@item.Style">
<div @ref="@item.Element" id="@item.GetItemId()" @onclick="@(args => SelectItem(item))" @attributes="item.Attributes" class="@item.GetItemCssClass()" style="@item.Style">
<div class="rz-radiobutton" @onkeypress="@(async args => { if (args.Code == "Space") { await SelectItem(item); } })" tabindex="@(Disabled || item.Disabled ? "-1" : $"{TabIndex}")">
<div class="rz-helper-hidden-accessible">
<input type="radio" disabled="@Disabled" name="@Name" value="@item.Value" tabindex="-1">

View File

@@ -98,5 +98,21 @@ namespace Radzen.Blazor
{
Visible = value;
}
internal string GetItemId()
{
return GetId();
}
internal string GetItemCssClass()
{
return GetCssClass();
}
/// <inheritdoc />
protected override string GetComponentCssClass()
{
return "rz-radio-btn";
}
}
}

View File

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

View File

@@ -0,0 +1,66 @@
using Microsoft.AspNetCore.Components;
using System;
using System.Linq;
using System.Collections.Generic;
namespace Radzen.Blazor
{
/// <summary>
/// RadzenCard component.
/// </summary>
public partial class RadzenRow : RadzenFlexComponent
{
/// <summary>
/// Gets or sets the gap.
/// </summary>
/// <value>The gap.</value>
[Parameter]
public string Gap { get; set; }
/// <summary>
/// Gets or sets the row gap.
/// </summary>
/// <value>The row gap.</value>
[Parameter]
public string RowGap { get; set; }
/// <summary>
/// Gets the final CSS style rendered by the component. Combines it with a <c>style</c> custom attribute.
/// </summary>
protected string GetStyle()
{
if (Attributes != null && Attributes.TryGetValue("style", out var style) && !string.IsNullOrEmpty(Convert.ToString(@style)))
{
return $"{GetComponentStyle()} {@style}";
}
return GetComponentStyle();
}
/// <summary>
/// Gets the component CSS style.
/// </summary>
protected string GetComponentStyle()
{
var list = new List<string>();
if (!string.IsNullOrEmpty(Gap))
{
list.Add($"--rz-gap:{(Gap.All(char.IsDigit) ? Gap + "px" : Gap)}");
}
if (!string.IsNullOrEmpty(RowGap))
{
list.Add($"--rz-row-gap:{(RowGap.All(char.IsDigit) ? RowGap + "px" : RowGap)}");
}
return $"{Style}{(!string.IsNullOrEmpty(Style) && !Style.EndsWith(";") ? ";" : "")}{string.Join(";", list)}";
}
/// <inheritdoc />
protected override string GetComponentCssClass()
{
return $"rz-display-flex rz-row rz-align-items-{GetFlexCSSClass<AlignItems>(AlignItems)} rz-justify-content-{GetFlexCSSClass<JustifyContent>(JustifyContent)}";
}
}
}

View File

@@ -206,7 +206,7 @@ namespace Radzen.Blazor
IList<ISchedulerView> Views { get; set; } = new List<ISchedulerView>();
ISchedulerView SelectedView
public ISchedulerView SelectedView
{
get
{

View File

@@ -188,7 +188,7 @@ namespace Radzen.Blazor
{
var type = typeof(TValue).IsGenericType ? typeof(TValue).GetGenericArguments()[0] : typeof(TValue);
var selectedValues = Value != null ? ((IEnumerable)Value).AsQueryable().Cast(type).AsEnumerable().ToList() : new List<dynamic>();
var selectedValues = Value != null ? QueryableExtension.ToList(((IEnumerable)Value).AsQueryable().Cast(type)) : new List<dynamic>();
if (!selectedValues.Contains(item.Value))
{

View File

@@ -0,0 +1,3 @@
@inherits RadzenComponent
<RadzenButton Visible="@Visible" id="@GetId()" title="@CurrentTitle" aria-label="@CurrentTitle" ButtonStyle="ButtonStyle" Icon="@CurrentIcon" style="@Style" @attributes="Attributes"
class="@GetCssClass()" Click="OnSpeechToTextClicked"></RadzenButton>

View File

@@ -0,0 +1,112 @@
using Microsoft.JSInterop;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
namespace Radzen.Blazor
{
/// <summary>
/// RadzenSpeechToTextButton component. Enables speech to text functionality.
/// <para>This is only supported on select browsers. See https://caniuse.com/?search=SpeechRecognition</para>
/// <example>
/// <code>
/// &lt;RadzenSpeechToTextButton Change=@(args => Console.WriteLine($"Value: {args}")) /&gt;
/// </code>
/// </example>
/// </summary>
public partial class RadzenSpeechToTextButton : RadzenComponent
{
/// <summary>
/// Gets or sets the button style.
/// </summary>
/// <value>The button style.</value>
[Parameter]
public ButtonStyle ButtonStyle { get; set; } = ButtonStyle.Light;
/// <summary>
/// Gets or sets the icon displayed while not recording.
/// </summary>
/// <value>The icon.</value>
[Parameter]
public string Icon { get; set; } = "mic";
/// <summary>
/// Gets or sets the icon displayed while recording.
/// </summary>
/// <value>The icon.</value>
[Parameter]
public string StopIcon { get; set; } = "stop";
private string CurrentIcon => recording ? StopIcon : Icon;
/// <summary>
/// Gets or sets the message displayed when user hovers the button and it is not recording.
/// </summary>
/// <value>The message.</value>
[Parameter]
public string Title { get; set; } = "Press to start speech recognition";
/// <summary>
/// Gets or sets the message displayed when user hovers the button and it is recording.
/// </summary>
/// <value>The message.</value>
[Parameter]
public string StopTitle { get; set; } = "Press to stop speech recognition";
private string CurrentTitle => recording ? StopTitle : Title;
/// <summary>
/// Callback which provides results from the speech recognition API.
/// </summary>
[Parameter]
public EventCallback<string> Change { get; set; }
/// <summary>
/// Gets or sets the icon displayed while recording.
/// </summary>
/// <value>The icon.</value>
[Parameter]
public string Language { get; set; }
private bool recording;
/// <inheritdoc />
protected override void OnAfterRender(bool firstRender)
{
base.OnAfterRender(firstRender);
}
/// <inheritdoc />
protected override string GetComponentCssClass()
{
return recording ? "rz-speech-to-text-button rz-speech-to-text-button-recording" : "rz-speech-to-text-button";
}
private async Task OnSpeechToTextClicked()
{
recording = !recording;
await JSRuntime.InvokeVoidAsync("Radzen.toggleDictation", Reference, Language);
}
/// <summary>
/// Provides interface for javascript to stop speech to text recording on this component if another component starts recording.
/// </summary>
[JSInvokable]
public void StopRecording()
{
recording = false;
StateHasChanged();
}
/// <summary>
/// Provides interface for javascript to pass speech results back to this component.
/// </summary>
/// <param name="result"></param>
[JSInvokable]
public void OnResult(string result)
{
Change.InvokeAsync(result);
}
}
}

View File

@@ -82,6 +82,13 @@ namespace Radzen.Blazor
[Parameter]
public bool Disabled { get; set; }
/// <summary>
/// Gets or sets the value indication behaviour to always open popup with item on click and not invoke <see cref="Click"/> event.
/// </summary>
/// <value><c>true</c> to alway open popup with items; othersie, <c>false</c>. Default is <c>false</c>.</value>
[Parameter]
public bool AlwaysOpenPopup { get; set; }
/// <summary>
/// Gets or sets the click callback.
/// </summary>
@@ -97,8 +104,15 @@ namespace Radzen.Blazor
{
if (!Disabled)
{
await JSRuntime.InvokeVoidAsync("Radzen.closePopup", PopupID);
await Click.InvokeAsync(null);
if (AlwaysOpenPopup)
{
await JSRuntime.InvokeVoidAsync("Radzen.togglePopup", Element, PopupID);
}
else
{
await JSRuntime.InvokeVoidAsync("Radzen.closePopup", PopupID);
await Click.InvokeAsync(null);
}
}
}

View File

@@ -1,16 +1,16 @@
@inherits RadzenComponent
@if (Visible)
{
<li class="rz-menuitem" role="menuitem" @onclick="@OnClick" @attributes="Attributes" style="@Style">
<a class="rz-menuitem-link">
@if (!string.IsNullOrEmpty(Icon))
{
<span class="rz-menuitem-icon">@((MarkupString)Icon)</span>
}
@if (!string.IsNullOrEmpty(Text))
{
<span class="rz-menuitem-text">@Text</span>
}
</a>
</li>
<li class=@ItemClassList role="menuitem" @onclick="@OnClick" @attributes="Attributes" style="@Style">
<a class="rz-menuitem-link">
@if (!string.IsNullOrEmpty(Icon))
{
<span class="rz-menuitem-icon">@((MarkupString)Icon)</span>
}
@if (!string.IsNullOrEmpty(Text))
{
<span class="rz-menuitem-text">@Text</span>
}
</a>
</li>
}

View File

@@ -1,5 +1,6 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Radzen.Blazor.Rendering;
namespace Radzen.Blazor
{
@@ -29,6 +30,13 @@ namespace Radzen.Blazor
[Parameter]
public string Value { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this <see cref="RadzenSplitButtonItem"/> is disabled.
/// </summary>
/// <value><c>true</c> if disabled; otherwise, <c>false</c>.</value>
[Parameter]
public bool Disabled { get; set; }
/// <summary>
/// Gets or sets the split button.
/// </summary>
@@ -43,11 +51,13 @@ namespace Radzen.Blazor
/// <param name="args">The <see cref="MouseEventArgs"/> instance containing the event data.</param>
public async System.Threading.Tasks.Task OnClick(MouseEventArgs args)
{
if (SplitButton != null)
if (SplitButton != null && !Disabled)
{
SplitButton.Close();
await SplitButton.Click.InvokeAsync(this);
}
}
ClassList ItemClassList => ClassList.Create("rz-menuitem").AddDisabled(Disabled);
}
}

View File

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

View File

@@ -0,0 +1,85 @@
using Microsoft.AspNetCore.Components;
using System;
using System.Linq;
using System.Text;
namespace Radzen.Blazor
{
/// <summary>
/// RadzenCard component.
/// </summary>
public partial class RadzenStack : RadzenFlexComponent
{
/// <summary>
/// Gets or sets the wrap.
/// </summary>
/// <value>The wrap.</value>
[Parameter]
public FlexWrap Wrap { get; set; } = FlexWrap.NoWrap;
/// <summary>
/// Gets or sets the orientation.
/// </summary>
/// <value>The orientation.</value>
[Parameter]
public Orientation Orientation { get; set; } = Orientation.Vertical;
/// <summary>
/// Gets or sets the spacing
/// </summary>
/// <value>The spacing.</value>
[Parameter]
public string Gap { get; set; }
/// <summary>
/// Gets or sets the reverse
/// </summary>
/// <value>The reverse.</value>
[Parameter]
public bool Reverse { get; set; }
/// <summary>
/// Gets the final CSS style rendered by the component. Combines it with a <c>style</c> custom attribute.
/// </summary>
protected string GetStyle()
{
if (Attributes != null && Attributes.TryGetValue("style", out var style) && !string.IsNullOrEmpty(Convert.ToString(@style)))
{
return $"{GetComponentStyle()} {@style}";
}
return GetComponentStyle();
}
/// <summary>
/// Gets the component CSS style.
/// </summary>
protected string GetComponentStyle()
{
var wrap = "";
if (Wrap == FlexWrap.Wrap)
{
wrap = ";flex-wrap:wrap;";
}
else if (Wrap == FlexWrap.NoWrap)
{
wrap = ";flex-wrap:nowrap;";
}
else if (Wrap == FlexWrap.WrapReverse)
{
wrap = ";flex-wrap:wrap-reverse;";
}
return $"{Style}{(!string.IsNullOrEmpty(Style) && !Style.EndsWith(";") ? ";" : "")}{(!string.IsNullOrEmpty(Gap) ? "--rz-gap:" + Gap + (Gap.All(c => Char.IsDigit(c)) ? "px;" : "") : "")}{wrap}";
}
/// <inheritdoc />
protected override string GetComponentCssClass()
{
var horizontal = Orientation == Orientation.Horizontal;
return $"rz-stack rz-display-flex rz-flex-{(horizontal ? "row" : "column")}{(Reverse ? "-reverse" : "")} rz-align-items-{GetFlexCSSClass<AlignItems>(AlignItems)} rz-justify-content-{GetFlexCSSClass<JustifyContent>(JustifyContent)}";
}
}
}

View File

@@ -251,9 +251,9 @@ namespace Radzen.Blazor
{
if (Visible)
{
if (Data != null)
if (EditContext != null)
{
builder.OpenRegion(Data.GetHashCode());
builder.OpenRegion(EditContext.GetHashCode());
}
builder.OpenElement(0, "form");
@@ -287,7 +287,7 @@ namespace Radzen.Blazor
builder.CloseElement(); // form
if (Data != null)
if (EditContext != null)
{
builder.CloseRegion();
}

View File

@@ -7,15 +7,14 @@
@code {
internal double Measure(RadzenChart chart)
{
if (Visible == false)
{
return 0;
}
if (chart.ShouldInvertAxes())
{
return AxisMeasurer.YAxis(chart.ValueScale, chart.CategoryAxis, chart.CategoryAxis.Title);
}
else if (Visible == false)
{
return 0;
}
else
{
return AxisMeasurer.YAxis(chart.ValueScale, chart.ValueAxis, Title);

View File

@@ -0,0 +1,12 @@
@ChildContent
@code {
[Parameter]
public RenderFragment ChildContent { get; set; }
internal void Refresh()
{
StateHasChanged();
}
}

View File

@@ -174,4 +174,34 @@
{
Service.OnRefresh -= OnRefresh;
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);
if (firstRender)
{
var options = Dialog.Options;
var dialogOptions = new DialogOptions()
{
Width = options != null ? !string.IsNullOrEmpty(options.Width) ? options.Width : "" : "",
Height = options != null ? options.Height : null,
Left = options != null ? options.Left : null,
Top = options != null ? options.Top : null,
Bottom = options != null ? options.Bottom : null,
ShowTitle = options != null ? options.ShowTitle : true,
ShowClose = options != null ? options.ShowClose : true,
Resizable = options != null ? options.Resizable : false,
Draggable = options != null ? options.Draggable : false,
Style = options != null ? options.Style : "",
AutoFocusFirstElement = options != null ? options.AutoFocusFirstElement : true,
CloseDialogOnOverlayClick = options != null ? options.CloseDialogOnOverlayClick : false,
CloseDialogOnEsc = options != null ? options.CloseDialogOnEsc : true,
CssClass = options != null ? options.CssClass : "",
};
await JSRuntime.InvokeAsync<string>("Radzen.openDialog", dialogOptions, Service.Reference);
}
}
}

View File

@@ -1,6 +1,6 @@
@inherits RadzenComponent
@using Microsoft.JSInterop
<div @ref=@Element @onmousedown:preventDefault @attributes=@Attributes style=@Style id=@GetId()>
<div @ref=@Element @attributes=@Attributes style=@Style id=@GetId()>
@if (open || !Lazy)
{
@ChildContent

View File

@@ -0,0 +1,39 @@
using System.Collections.Generic;
using System.Text;
namespace Radzen.Blazor.Rendering
{
/// <summary>
/// Class StepGenerator.
/// Implements the <see cref="Radzen.Blazor.Rendering.IPathGenerator" />
/// </summary>
/// <seealso cref="Radzen.Blazor.Rendering.IPathGenerator" />
public class StepGenerator : IPathGenerator
{
/// <summary>
/// Pathes the specified data.
/// </summary>
/// <param name="data">The data.</param>
/// <returns>System.String.</returns>
public string Path(IEnumerable<Point> data)
{
var path = new StringBuilder();
var start = true;
foreach (var point in data)
{
if (start)
{
path.Append($"{point.X.ToInvariantString()} {point.Y.ToInvariantString()}");
start = false;
continue;
}
path.Append($" H {point.X.ToInvariantString()}");
path.Append($" V {point.Y.ToInvariantString()}");
}
return path.ToString();
}
}
}

View File

@@ -1,5 +1,6 @@
// Blazor
@import 'components/blazor/utilities';
@import 'components/blazor/spacing';
@import 'components/blazor/typography';
@import 'components/blazor/icons';
@import 'components/blazor/common';
@@ -64,4 +65,6 @@
@import 'components/blazor/splitter';
@import 'components/blazor/layout';
@import 'components/blazor/breadcrumb';
@import 'components/blazor/alert';
@import 'components/blazor/alert';
@import 'components/blazor/speech-to-text-button';
@import 'components/blazor/stack';

View File

@@ -1,17 +1,37 @@
@mixin rz-color-css($map, $attribute) {
@mixin rz-color-css($property, $map) {
@each $token,
$value in $map {
.rz-#{$attribute}-#{$token} {
#{$attribute}: var(--rz-#{$token}) !important;
.rz-#{$property}-#{$token} {
#{$property}: var(--rz-#{$token}) !important;
}
}
}
@mixin rz-utility-css($map, $attribute) {
@mixin rz-utility-map-css($property, $map) {
@each $token,
$value in $map {
.rz-#{$token} {
#{$attribute}: var(--rz-#{$token}) !important;
#{$property}: var(--rz-#{$token}) !important;
}
}
}
@mixin rz-utility-list-css($property, $list) {
@each $value in $list {
.rz-#{$property}-#{$value} {
#{$property}: #{$value} !important;
}
}
}
@mixin rz-utility-list-breakpoints-css($property, $list, $breakpoints) {
@each $breakpoint, $breakpoint-value in $breakpoints {
@media (min-width: #{$breakpoint-value}) {
@each $value in $list {
.rz-#{$property}-#{$breakpoint}-#{$value} {
#{$property}: #{$value} !important;
}
}
}
}
}

View File

@@ -6,13 +6,15 @@ $card-heading-margin-bottom: 0.5rem !default;
// Card CSS variables
.rz-card {
:root {
--rz-card-padding: #{$card-padding};
--rz-card-background-color: #{$card-background-color};
--rz-card-shadow: #{$card-shadow};
--rz-card-border-radius: #{$card-border-radius};
--rz-card-heading-margin-bottom: #{$card-heading-margin-bottom};
}
.rz-card {
padding: var(--rz-card-padding);
border-radius: var(--rz-card-border-radius);
box-shadow: var(--rz-card-shadow);

View File

@@ -1,6 +1,7 @@
$chart-axis-color: var(--rz-base-300) !default;
$chart-axis-label-color: var(--rz-text-secondary-color) !default;
$chart-axis-font-size: 0.875rem !default;
$chart-legend-font-size: 0.875rem !default;
$chart-tooltip-background: var(--rz-base-background-color) !default;
$chart-tooltip-color: var(--rz-text-color) !default;
@@ -13,7 +14,23 @@ $chart-color-schemes: (
var(--rz-series-5),
var(--rz-series-6),
var(--rz-series-7),
var(--rz-series-8)
var(--rz-series-8),
var(--rz-series-9),
var(--rz-series-10),
var(--rz-series-11),
var(--rz-series-12),
var(--rz-series-13),
var(--rz-series-14),
var(--rz-series-15),
var(--rz-series-16),
var(--rz-series-17),
var(--rz-series-18),
var(--rz-series-19),
var(--rz-series-20),
var(--rz-series-21),
var(--rz-series-22),
var(--rz-series-23),
var(--rz-series-24)
),
palette: (
#003f5c,
@@ -54,6 +71,7 @@ $chart-color-schemes: (
--rz-chart-axis-color: #{$chart-axis-color};
--rz-chart-axis-label-color: #{$chart-axis-label-color};
--rz-chart-axis-font-size: #{$chart-axis-font-size};
--rz-chart-legend-font-size: #{$chart-legend-font-size};
--rz-chart-tooltip-background: #{$chart-tooltip-background};
--rz-chart-tooltip-color: #{$chart-tooltip-color};
}
@@ -180,6 +198,7 @@ $chart-color-schemes: (
.rz-legend {
position: absolute;
display: flex;
font-size: var(--rz-chart-legend-font-size);
}
.rz-legend-right {

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