diff --git a/docs/en/docs-params.json b/docs/en/docs-params.json
index a5665a1215..c15e60c8cb 100644
--- a/docs/en/docs-params.json
+++ b/docs/en/docs-params.json
@@ -36,6 +36,17 @@
"Tiered": "Tiered",
"Microservice": "Microservice"
}
+ },
+ {
+ "name": "BlazorUI",
+ "displayName": "Blazor UI Library",
+ "values": {
+ "Blazorise": "Blazorise",
+ "MudBlazor": "MudBlazor"
+ },
+ "dependsOn": {
+ "UI": ["Blazor", "BlazorServer", "BlazorWebApp"]
+ }
}
]
}
\ No newline at end of file
diff --git a/docs/en/framework/ui/blazor/basic-theme.md b/docs/en/framework/ui/blazor/basic-theme.md
index 33c0cb7175..f8cd0c7023 100644
--- a/docs/en/framework/ui/blazor/basic-theme.md
+++ b/docs/en/framework/ui/blazor/basic-theme.md
@@ -10,7 +10,8 @@
````json
//[doc-params]
{
- "UI": ["Blazor", "BlazorServer"]
+ "UI": ["Blazor", "BlazorServer"],
+ "BlazorUI": ["Blazorise", "MudBlazor"]
}
````
@@ -20,6 +21,18 @@ The Basic Theme is a theme implementation for the Blazor UI. It is a minimalist
> See the [Theming document](theming.md) to learn about themes.
+{{if BlazorUI == "MudBlazor"}}
+
+> **MudBlazor Variant** — When the `--blazor-ui-library mudblazor` option is used, the Basic Theme ships as a MudBlazor variant. Replace `BasicTheme` with `MudBlazorBasicTheme` everywhere in this document (package names, module type names and namespaces). The MudBlazor variant is **not** based on Bootstrap — it uses MudBlazor's Material Design layout components.
+>
+> Concrete package names you will see when using the MudBlazor variant:
+>
+> * `Volo.Abp.AspNetCore.Components.{Server,WebAssembly}.MudBlazorBasicTheme`
+> * `Volo.Abp.AspNetCore.Components.{Server,WebAssembly}.MudBlazorBasicTheme.Bundling`
+> * Module types: `Abp{...}MudBlazorBasicThemeModule`, `Abp{...}MudBlazorBasicThemeBundlingModule`
+
+{{end}}
+
## Installation
If you need to manually this theme, follow the steps below:
diff --git a/docs/en/framework/ui/blazor/components/submit-button.md b/docs/en/framework/ui/blazor/components/submit-button.md
index 832554c9b1..82dbeeeee9 100644
--- a/docs/en/framework/ui/blazor/components/submit-button.md
+++ b/docs/en/framework/ui/blazor/components/submit-button.md
@@ -1,12 +1,21 @@
+```json
+//[doc-params]
+{
+ "BlazorUI": ["Blazorise", "MudBlazor"]
+}
+```
+
```json
//[doc-seo]
{
- "Description": "Explore the `SubmitButton` component in Blazor UI, designed for easy form submissions with localization support and loading indicators."
+ "Description": "Explore the submit button component in Blazor UI, designed for easy form submissions with localization support and loading indicators."
}
```
# Blazor UI: SubmitButton Component
+{{if BlazorUI == "Blazorise"}}
+
`SubmitButton` is a simple wrapper around `Button` component. It is used to be placed inside of page Form or Modal dialogs where it can response to user actions and to be activated as a default button by pressing an ENTER key. Once clicked it will go into the `disabled` state and also it will show a small loading indicator until clicked event is finished.
## Quick Example
@@ -29,4 +38,54 @@ Notice that we didn't specify any text, like `Save Changes`. This is because `Su
@L["Save"]
-```
\ No newline at end of file
+```
+
+{{end}}
+
+{{if BlazorUI == "MudBlazor"}}
+
+The MudBlazor variant of ABP UI does not ship a dedicated `SubmitButton` wrapper. Use the standard `MudButton` together with the typical `Processing`/`Disabled` pattern to disable the button and show a progress indicator while the click handler is running.
+
+## Quick Example
+
+```razor
+
+ @if (_processing)
+ {
+
+ }
+ @L["Save"]
+
+
+@code {
+ private bool _processing;
+
+ private async Task SaveAsync()
+ {
+ _processing = true;
+ try
+ {
+ // ... your save operation
+ }
+ finally
+ {
+ _processing = false;
+ }
+ }
+}
+```
+
+## Submit on Enter
+
+When the button is placed inside a `` or a ``, pressing ENTER inside an input control submits the form. To run validation before saving, call `_form.Validate()` first. See the [Forms & Validation](../forms-validation.md) page for details.
+
+## Use Inside `AbpMudCrudPageBase`
+
+The MudBlazor CRUD page base (`AbpMudCrudPageBase`) already wires up the standard create/update buttons inside its dialogs and shows a progress indicator while the application service call is running. In most cases you don't need to author a save button by hand; override `OnCreatingEntityAsync` / `OnUpdatingEntityAsync` instead.
+
+> Check the [MudBlazor button documentation](https://mudblazor.com/components/button) for all available options.
+
+{{end}}
\ No newline at end of file
diff --git a/docs/en/framework/ui/blazor/customization-overriding-components.md b/docs/en/framework/ui/blazor/customization-overriding-components.md
index 8855ff89bd..c919ed084e 100644
--- a/docs/en/framework/ui/blazor/customization-overriding-components.md
+++ b/docs/en/framework/ui/blazor/customization-overriding-components.md
@@ -10,7 +10,8 @@
````json
//[doc-params]
{
- "UI": ["Blazor", "BlazorServer"]
+ "UI": ["Blazor", "BlazorServer"],
+ "BlazorUI": ["Blazorise", "MudBlazor"]
}
````
@@ -41,6 +42,8 @@ The next step is to create a razor component, like `MyBranding.razor`, in your a
The content of the `MyBranding.razor` is shown below:
+{{if BlazorUI == "Blazorise"}}
+
````html
@using Volo.Abp.DependencyInjection
{{if UI == "BlazorServer"}}
@@ -58,6 +61,33 @@ The content of the `MyBranding.razor` is shown below:
````
+{{end}}
+
+{{if BlazorUI == "MudBlazor"}}
+
+The MudBlazor variant uses the LeptonX-based MudBlazor theme by default. The component to override is the `Branding` component shipped by the active MudBlazor theme:
+
+````html
+@using Volo.Abp.DependencyInjection
+{{if UI == "BlazorServer"}}
+@using Volo.Abp.AspNetCore.Components.Server.MudBlazorLeptonXTheme.Themes.MudBlazorLeptonX
+{{end}}
+{{if UI == "Blazor"}}
+@using Volo.Abp.AspNetCore.Components.WebAssembly.MudBlazorLeptonXTheme.Themes.MudBlazorLeptonX
+{{end}}
+
+@inherits Branding
+@attribute [ExposeServices(typeof(Branding))]
+@attribute [Dependency(ReplaceServices = true)]
+
+
+
+````
+
+> If you are using the MudBlazor BasicTheme or a different MudBlazor theme, replace the namespace with the namespace of that theme's `Themes/` folder.
+
+{{end}}
+
Let's explain the code:
* `@inherits Branding` line inherits the Branding component defined by the [Basic Theme](basic-theme.md) (in the {{if UI == "BlazorServer"}}`Volo.Abp.AspNetCore.Components.Server.BasicTheme.Themes.Basic`{{end}} {{if UI == "Blazor"}}`Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme.Themes.Basic`{{end}} namespace).
@@ -75,6 +105,8 @@ Now, you can run the application to see the result:
If you prefer to use code-behind file for the C# code of your component, you can use the attributes in the C# side.
+{{if BlazorUI == "Blazorise"}}
+
**MyBlazor.razor**
````html
@@ -113,6 +145,50 @@ namespace MyProject.Blazor.Components
}
````
+{{end}}
+
+{{if BlazorUI == "MudBlazor"}}
+
+**MyBlazor.razor**
+
+````html
+{{if UI == "BlazorServer"}}
+@using Volo.Abp.AspNetCore.Components.Server.MudBlazorLeptonXTheme.Themes.MudBlazorLeptonX
+{{end}}
+{{if UI == "Blazor"}}
+@using Volo.Abp.AspNetCore.Components.WebAssembly.MudBlazorLeptonXTheme.Themes.MudBlazorLeptonX
+{{end}}
+@inherits Branding
+
+
+
+````
+
+**MyBlazor.razor.cs**
+
+````csharp
+{{if UI == "BlazorServer"}}
+using Volo.Abp.AspNetCore.Components.Server.MudBlazorLeptonXTheme.Themes.MudBlazorLeptonX;
+{{end}}
+{{if UI == "Blazor"}}
+using Volo.Abp.AspNetCore.Components.WebAssembly.MudBlazorLeptonXTheme.Themes.MudBlazorLeptonX;
+{{end}}
+
+using Volo.Abp.DependencyInjection;
+
+namespace MyProject.Blazor.Components
+{
+ [ExposeServices(typeof(Branding))]
+ [Dependency(ReplaceServices = true)]
+ public partial class MyBranding
+ {
+
+ }
+}
+````
+
+{{end}}
+
## Theming
The [Theming](theming.md) system allows you to build your own theme. You can create your theme from scratch or get the [Basic Theme](basic-theme.md) and change however you like.
diff --git a/docs/en/framework/ui/blazor/data-table-column-extensions.md b/docs/en/framework/ui/blazor/data-table-column-extensions.md
index b89b825201..bb6bc6eeb0 100644
--- a/docs/en/framework/ui/blazor/data-table-column-extensions.md
+++ b/docs/en/framework/ui/blazor/data-table-column-extensions.md
@@ -1,3 +1,10 @@
+```json
+//[doc-params]
+{
+ "BlazorUI": ["Blazorise", "MudBlazor"]
+}
+```
+
```json
//[doc-seo]
{
@@ -102,6 +109,8 @@ public class CustomTableColumn
Navigate to the razor file and paste the following code.
+{{if BlazorUI == "Blazorise"}}
+
```csharp
@using System
@using Volo.Abp.Identity
@@ -116,6 +125,27 @@ else
}
```
+{{end}}
+
+{{if BlazorUI == "MudBlazor"}}
+
+```razor
+@using Volo.Abp.Identity
+
+@if (Data.As().EmailConfirmed)
+{
+
+}
+else
+{
+
+}
+```
+
+> When using MudBlazor, the standard data grid in module pages is `AbpMudExtensibleDataGrid`. You can replace `Component = typeof(CustomTableColumn)` exactly the same way as in Blazorise; the column system is shared across both UI libraries.
+
+{{end}}
+
Navigate back to the `CustomizedUserManagement` class, and use `Component` property to specify the custom blazor component.
```csharp
diff --git a/docs/en/framework/ui/blazor/entity-action-extensions.md b/docs/en/framework/ui/blazor/entity-action-extensions.md
index ab3d4b3cba..78d16f7327 100644
--- a/docs/en/framework/ui/blazor/entity-action-extensions.md
+++ b/docs/en/framework/ui/blazor/entity-action-extensions.md
@@ -1,3 +1,10 @@
+```json
+//[doc-params]
+{
+ "BlazorUI": ["Blazorise", "MudBlazor"]
+}
+```
+
```json
//[doc-seo]
{
@@ -95,6 +102,8 @@ Here, the list of the properties that you use in the `EntityAction`.
#### Example
+{{if BlazorUI == "Blazorise"}}
+
```csharp
var clickMeAction = new EntityAction()
{
@@ -115,3 +124,33 @@ var clickMeAction = new EntityAction()
}
};
```
+
+{{end}}
+
+{{if BlazorUI == "MudBlazor"}}
+
+```csharp
+var clickMeAction = new EntityAction()
+{
+ Text = "Click Me!",
+ Clicked = (data) =>
+ {
+ //TODO: Write your custom code
+
+ return Task.CompletedTask;
+ },
+ Color = MudBlazor.Color.Error,
+ Icon = MudBlazor.Icons.Material.Filled.PanTool,
+ ConfirmationMessage = (data) => "Are you sure you want to click to the action?",
+ Visible = (data) =>
+ {
+ //TODO: Write your custom visibility action
+ //var selectedUser = data.As();
+ return true;
+ }
+};
+```
+
+> The MudBlazor variant uses `MudBlazor.Color` enum values (e.g. `Color.Primary`, `Color.Error`, `Color.Success`) for `Color`, and Material Icon constants (e.g. `Icons.Material.Filled.Edit`) for `Icon`. The `EntityAction` model itself is shared with Blazorise; only the values you put inside it change.
+
+{{end}}
diff --git a/docs/en/framework/ui/blazor/error-handling.md b/docs/en/framework/ui/blazor/error-handling.md
index 2fae4ff38d..211c56f25e 100644
--- a/docs/en/framework/ui/blazor/error-handling.md
+++ b/docs/en/framework/ui/blazor/error-handling.md
@@ -10,7 +10,8 @@
````json
//[doc-params]
{
- "UI": ["Blazor", "BlazorServer"]
+ "UI": ["Blazor", "BlazorServer"],
+ "BlazorUI": ["Blazorise", "MudBlazor"]
}
````
@@ -36,7 +37,9 @@ There are different type of `Exception` classes handled differently by the ABP.
**Example**
-````csharp
+{{if BlazorUI == "Blazorise"}}
+
+````razor
@page "/"
@using Volo.Abp
@@ -60,11 +63,41 @@ There are different type of `Exception` classes handled differently by the ABP.
{{end}}
+{{if BlazorUI == "MudBlazor"}}
+
+````razor
+@page "/"
+@using Volo.Abp
+
+Throw test exception
+
+@code
+{
+ private async Task TestException()
+ {
+ try
+ {
+ throw new UserFriendlyException("A user friendly error message!");
+ }
+ catch(UserFriendlyException ex)
+ {
+ await HandleErrorAsync(ex);
+ }
+ }
+}
+````
+
+{{end}}
+
+{{end}}
+
{{if UI == "Blazor"}}
**Example**
-````csharp
+{{if BlazorUI == "Blazorise"}}
+
+````razor
@page "/"
@using Volo.Abp
@@ -78,6 +111,28 @@ There are different type of `Exception` classes handled differently by the ABP.
}
}
````
+
+{{end}}
+
+{{if BlazorUI == "MudBlazor"}}
+
+````razor
+@page "/"
+@using Volo.Abp
+
+Throw test exception
+
+@code
+{
+ private void TestException()
+ {
+ throw new UserFriendlyException("A user friendly error message!");
+ }
+}
+````
+
+{{end}}
+
{{end}}
ABP automatically handle the exception and show an error message to the user:
diff --git a/docs/en/framework/ui/blazor/forms-validation.md b/docs/en/framework/ui/blazor/forms-validation.md
index 7fb1423e77..7426ab6cab 100644
--- a/docs/en/framework/ui/blazor/forms-validation.md
+++ b/docs/en/framework/ui/blazor/forms-validation.md
@@ -1,12 +1,21 @@
+```json
+//[doc-params]
+{
+ "BlazorUI": ["Blazorise", "MudBlazor"]
+}
+```
+
```json
//[doc-seo]
{
- "Description": "Learn how to implement form validation in ABP Blazor UI using Blazorise's validation infrastructure with practical examples."
+ "Description": "Learn how to implement form validation in ABP Blazor UI using Blazorise or MudBlazor with practical examples."
}
```
# Blazor UI: Forms & Validation
+{{if BlazorUI == "Blazorise"}}
+
ABP Blazor UI is based on the [Blazorise](https://blazorise.com/docs) and does not have a built-in form validation infrastructure. However, you can use the [Blazorise validation infrastructure](https://blazorise.com/docs/components/validation) to validate your forms.
## Sample
@@ -44,4 +53,91 @@ _The example is provided by official Blazorise documentation._
}
```
-> Check the [Blazorise documentation](https://blazorise.com/docs/components/validation) for more information and examples.
\ No newline at end of file
+> Check the [Blazorise documentation](https://blazorise.com/docs/components/validation) for more information and examples.
+
+{{end}}
+
+{{if BlazorUI == "MudBlazor"}}
+
+ABP Blazor UI built on top of [MudBlazor](https://mudblazor.com) uses MudBlazor's built-in form components and validation infrastructure. MudBlazor integrates with ASP.NET Core's `DataAnnotations` and supports custom validation through `IValidationRule`, `Func>`, or fluent validators.
+
+## Sample
+
+The most common pattern is wrapping inputs in a `` and binding the form's validation state through `IsValid`:
+
+> Standard MudBlazor and ABP usings (`@using MudBlazor`, `@using Volo.Abp.MudBlazorUI`, etc.) come from the project's `_Imports.razor`. The example below only adds the additional usings needed for validation.
+
+```razor
+@using System.ComponentModel.DataAnnotations
+
+
+
+
+
+
+
+ Submit
+
+
+
+@code {
+ private MudForm _form;
+ private bool _isValid;
+ private SampleModel _model = new();
+
+ private async Task SubmitAsync()
+ {
+ await _form.Validate();
+ if (_isValid)
+ {
+ // ...
+ }
+ }
+
+ public class SampleModel
+ {
+ [Required]
+ public string Name { get; set; }
+
+ [Required, EmailAddress]
+ public string Email { get; set; }
+ }
+}
+```
+
+### Inputs Used in CRUD Pages
+
+ABP's MudBlazor CRUD pages (see `AbpMudCrudPageBase`) use a `` containing a `` and standard MudBlazor inputs:
+
+* `` / `` for text and multi-line text
+* `` / `` for dropdowns
+* `` for booleans
+* `` / `` for date and time
+* `` for numbers
+
+The page base validates the entire form before calling the application service:
+
+```csharp
+protected override async Task OnCreatingEntityAsync()
+{
+ await _form.Validate();
+ if (!_isValid)
+ {
+ return;
+ }
+ // ... call AppService
+}
+```
+
+> Check the [MudBlazor documentation](https://mudblazor.com/components/form) for the full list of validation modes and the [MudBlazor inputs reference](https://mudblazor.com/components/textfield).
+
+{{end}}
\ No newline at end of file
diff --git a/docs/en/framework/ui/blazor/overall.md b/docs/en/framework/ui/blazor/overall.md
index 23077e307e..f5d5cf7b78 100644
--- a/docs/en/framework/ui/blazor/overall.md
+++ b/docs/en/framework/ui/blazor/overall.md
@@ -1,3 +1,10 @@
+```json
+//[doc-params]
+{
+ "BlazorUI": ["Blazorise", "MudBlazor"]
+}
+```
+
```json
//[doc-seo]
{
@@ -95,6 +102,8 @@ Currently, three themes are **officially provided**:
There are a set of standard libraries that comes pre-installed and supported by all the themes:
+{{if BlazorUI == "Blazorise"}}
+
* [Twitter Bootstrap](https://getbootstrap.com/) as the fundamental HTML/CSS framework.
* [Blazorise](https://github.com/stsrki/Blazorise) as a component library that supports the Bootstrap and adds extra components like Data Grid and Tree.
* [FontAwesome](https://fontawesome.com/) as the fundamental CSS font library.
@@ -106,6 +115,22 @@ These libraries are selected as the base libraries and available to the applicat
> Beginning from June, 2021, the Blazorise library has dual licenses; open source & commercial. Based on your yearly revenue, you may need to buy a commercial license. See [this post](https://blazorise.com/news/announcing-2022-blazorise-plans-and-pricing-updates) to learn more. The Blazorise license is bundled with ABP and commercial customers doesn't need to buy an extra Blazorise license.
+{{end}}
+
+{{if BlazorUI == "MudBlazor"}}
+
+* [MudBlazor](https://mudblazor.com/) as the component library, providing a complete set of Material Design components built natively for Blazor (form controls, data grid, dialogs, snackbars, dates, etc.).
+* [FontAwesome](https://fontawesome.com/) as the fundamental CSS font library.
+* [Flag Icon](https://github.com/lipis/flag-icons) as a library to show flags of countries.
+
+These libraries are selected as the base libraries and available to the applications and modules.
+
+The MudBlazor variant ships its own theming, dialog, snackbar and popover providers (see [Theming](theming.md)). The MudBlazor library is MIT-licensed and is bundled with ABP at no extra cost.
+
+> Bootstrap is **not** required when using MudBlazor; MudBlazor brings its own layout and component styles.
+
+{{end}}
+
### The Layout
The themes provide the layout. So, you have a responsive layout with the standard features already implemented. The screenshot below has taken from the layout of the [Basic Theme](basic-theme.md):
diff --git a/docs/en/framework/ui/blazor/page-header.md b/docs/en/framework/ui/blazor/page-header.md
index ed2e16903d..5c03af6aa4 100644
--- a/docs/en/framework/ui/blazor/page-header.md
+++ b/docs/en/framework/ui/blazor/page-header.md
@@ -1,3 +1,10 @@
+```json
+//[doc-params]
+{
+ "BlazorUI": ["Blazorise", "MudBlazor"]
+}
+```
+
```json
//[doc-seo]
{
@@ -30,6 +37,8 @@ Breadcrumbs can be added using the `BreadcrumbItems` property.
**Example: Add Language Management to the breadcrumb items.**
+{{if BlazorUI == "Blazorise"}}
+
Create a collection of `Volo.Abp.BlazoriseUI.BreadcrumbItem` objects and set the collection to the `BreadcrumbItems` parameter.
```csharp
@@ -44,6 +53,31 @@ public partial class Index
}
```
+{{end}}
+
+{{if BlazorUI == "MudBlazor"}}
+
+Create a collection of `MudBlazor.BreadcrumbItem` objects and set the collection to the `BreadcrumbItems` parameter. The MudBlazor `BreadcrumbItem` constructor takes `(string text, string href, bool disabled = false, string icon = null)`.
+
+```csharp
+using MudBlazor;
+
+public partial class Index
+{
+ protected List BreadcrumbItems { get; } = new();
+
+ protected override void OnInitialized()
+ {
+ BreadcrumbItems.Add(new BreadcrumbItem(
+ text: "Language Management",
+ href: null,
+ disabled: true));
+ }
+}
+```
+
+{{end}}
+
Navigate back to the razor page.
```csharp
@@ -57,19 +91,38 @@ The theme then renders the breadcrumb. An example render result can be:
* The Home icon is rendered by default. Set `BreadcrumbShowHome` to `false` to hide it.
* Breadcrumb items will be activated based on current navigation. Set `BreadcrumbShowCurrent` to `false` to disable it.
-You can add as many items as you need. `BreadcrumbItem` constructor gets three parameters:
+You can add as many items as you need.
+
+{{if BlazorUI == "Blazorise"}}
+
+The `Volo.Abp.BlazoriseUI.BreadcrumbItem` constructor gets three parameters:
* `text`: The text to show for the breadcrumb item.
* `url` (optional): A URL to navigate to, if the user clicks to the breadcrumb item.
* `icon` (optional): An icon class (like `fas fa-user-tie` for Font-Awesome) to show with the `text`.
+{{end}}
+
+{{if BlazorUI == "MudBlazor"}}
+
+The `MudBlazor.BreadcrumbItem` constructor takes:
+
+* `text`: The text to show for the breadcrumb item.
+* `href`: A URL to navigate to (use `null` for the current page).
+* `disabled` (optional): When `true`, the item is rendered as the current/non-clickable item.
+* `icon` (optional): A Material icon (e.g. `Icons.Material.Filled.Language`).
+
+{{end}}
+
## Page Toolbar
Page toolbar can be set using the `Toolbar` property.
**Example: Add a "New Item" toolbar item to the page toolbar.**
-Create a `PageToolbar` object and define toolbar items using the `AddButton` extension method.
+Create a `PageToolbar` object and define toolbar items using the `AddButton` extension method.
+
+{{if BlazorUI == "Blazorise"}}
```csharp
public partial class Index
@@ -87,6 +140,28 @@ public partial class Index
}
```
+{{end}}
+
+{{if BlazorUI == "MudBlazor"}}
+
+```csharp
+public partial class Index
+{
+ protected PageToolbar Toolbar { get; } = new();
+
+ protected override void OnInitialized()
+ {
+ Toolbar.AddButton("New Item", () =>
+ {
+ //Write your click action here
+ return Task.CompletedTask;
+ }, icon: MudBlazor.Icons.Material.Filled.Add);
+ }
+}
+```
+
+{{end}}
+
Navigate back to the razor page and set the `Toolbar` parameter.
```csharp
diff --git a/docs/en/framework/ui/blazor/page-layout.md b/docs/en/framework/ui/blazor/page-layout.md
index 75666fd17a..abd32745d0 100644
--- a/docs/en/framework/ui/blazor/page-layout.md
+++ b/docs/en/framework/ui/blazor/page-layout.md
@@ -1,3 +1,10 @@
+```json
+//[doc-params]
+{
+ "BlazorUI": ["Blazorise", "MudBlazor"]
+}
+```
+
```json
//[doc-seo]
{
@@ -36,6 +43,8 @@ Indicates current selected menu item name. Menu item name should match a unique
Menu item name can be set on runtime too.
+{{if BlazorUI == "Blazorise"}}
+
```html
@inject PageLayout PageLayout
@@ -49,6 +58,25 @@ Menu item name can be set on runtime too.
}
```
+{{end}}
+
+{{if BlazorUI == "MudBlazor"}}
+
+```razor
+@inject PageLayout PageLayout
+
+Change Menu
+
+@code{
+ protected void SetCategoriesMenuAsSelected()
+ {
+ PageLayout.MenuItemName = "MyProjectName.Categories";
+ }
+}
+```
+
+{{end}}
+

@@ -57,6 +85,9 @@ Menu item name can be set on runtime too.
## BreadCrumbs
BreadCrumbItems are used to render breadcrumbs in the PageHeader.
+
+{{if BlazorUI == "Blazorise"}}
+
```csharp
@inject PageLayout PageLayout
@@ -65,6 +96,21 @@ BreadCrumbItems are used to render breadcrumbs in the PageHeader.
}
```
+{{end}}
+
+{{if BlazorUI == "MudBlazor"}}
+
+```razor
+@using MudBlazor
+@inject PageLayout PageLayout
+
+@{
+ PageLayout.BreadcrumbItems.Add(new BreadcrumbItem("My Page", "/my-page"));
+}
+```
+
+{{end}}
+
## Toolbar
ToolbarItems are used to render action toolbar items in the PageHeader.
diff --git a/docs/en/framework/ui/blazor/page-toolbar-extensions.md b/docs/en/framework/ui/blazor/page-toolbar-extensions.md
index 9c96bf90f7..dc0917fe8a 100644
--- a/docs/en/framework/ui/blazor/page-toolbar-extensions.md
+++ b/docs/en/framework/ui/blazor/page-toolbar-extensions.md
@@ -1,3 +1,10 @@
+```json
+//[doc-params]
+{
+ "BlazorUI": ["Blazorise", "MudBlazor"]
+}
+```
+
```json
//[doc-seo]
{
@@ -27,6 +34,8 @@ We will use the [component override system](customization-overriding-components.
Here, the content of the overridden `SetToolbarItemsAsync` method.
+{{if BlazorUI == "Blazorise"}}
+
```csharp
protected override async ValueTask SetToolbarItemsAsync()
{
@@ -38,10 +47,31 @@ protected override async ValueTask SetToolbarItemsAsync()
}, "file-import", Blazorise.Color.Secondary);
}
```
+
+{{end}}
+
+{{if BlazorUI == "MudBlazor"}}
+
+```csharp
+protected override async ValueTask SetToolbarItemsAsync()
+{
+ await base.SetToolbarItemsAsync();
+ Toolbar.AddButton("Import users from excel", () =>
+ {
+ //TODO: Write your custom code
+ return Task.CompletedTask;
+ }, MudBlazor.Icons.Material.Filled.FileUpload, MudBlazor.Color.Secondary);
+}
+```
+
+{{end}}
+
> In order to use the `AddButton` extension method, you need to add a using statement for the `Volo.Abp.AspNetCore.Components.Web.Theming.PageToolbars` namespace.
Here, the entire content of the file.
+{{if BlazorUI == "Blazorise"}}
+
```csharp
using System.Threading.Tasks;
using Volo.Abp.AspNetCore.Components.Web.Theming.PageToolbars;
@@ -67,6 +97,37 @@ namespace MyCompanyName.MyProjectName.Blazor.Pages.Identity
}
```
+{{end}}
+
+{{if BlazorUI == "MudBlazor"}}
+
+```csharp
+using System.Threading.Tasks;
+using Volo.Abp.AspNetCore.Components.Web.Theming.PageToolbars;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.Identity.Blazor.Pages.Identity;
+
+namespace MyCompanyName.MyProjectName.Blazor.Pages.Identity
+{
+ [ExposeServices(typeof(UserManagement))]
+ [Dependency(ReplaceServices = true)]
+ public class CustomizedUserManagement : UserManagement
+ {
+ protected override async ValueTask SetToolbarItemsAsync()
+ {
+ await base.SetToolbarItemsAsync();
+ Toolbar.AddButton("Import users from excel", () =>
+ {
+ //TODO: Write your custom code
+ return Task.CompletedTask;
+ }, MudBlazor.Icons.Material.Filled.FileUpload, MudBlazor.Color.Secondary);
+ }
+ }
+}
+```
+
+{{end}}
+
When you run the application, you will see the button added next to the current button list. There are some other parameters of the `AddButton` method (for example, use `Order` to set the order of the button component relative to the other components).
## Advanced Use Cases
@@ -83,9 +144,21 @@ For this example, we've created a `MyToolbarComponent` component under the `/Pag
`MyToolbarComponent.razor` content:
-````csharp
+{{if BlazorUI == "Blazorise"}}
+
+````razor
````
+
+{{end}}
+
+{{if BlazorUI == "MudBlazor"}}
+
+````razor
+CLICK ME
+````
+
+{{end}}
We will leave the `MyToolbarComponent.razor.cs` file empty.
Then you can add the `MyToolbarComponent` to the user management page toolbar:
diff --git a/docs/en/framework/ui/blazor/theming.md b/docs/en/framework/ui/blazor/theming.md
index 7ded33608f..1fff0a057f 100644
--- a/docs/en/framework/ui/blazor/theming.md
+++ b/docs/en/framework/ui/blazor/theming.md
@@ -10,7 +10,8 @@
````json
//[doc-params]
{
- "UI": ["Blazor", "BlazorServer"]
+ "UI": ["Blazor", "BlazorServer"],
+ "BlazorUI": ["Blazorise", "MudBlazor"]
}
````
@@ -52,6 +53,8 @@ All the themes must depend on the [Volo.Abp.AspNetCore.Components.Server.Theming
{{end}}
+{{if BlazorUI == "Blazorise"}}
+
* [Twitter Bootstrap](https://getbootstrap.com/) as the fundamental HTML/CSS framework.
* [Blazorise](https://github.com/stsrki/Blazorise) as a component library that supports the Bootstrap and adds extra components like Data Grid and Tree.
* [FontAwesome](https://fontawesome.com/) as the fundamental CSS font library.
@@ -61,6 +64,31 @@ These libraries are selected as the base libraries and available to the applicat
> Bootstrap's JavaScript part is not used since the Blazorise library already provides the necessary functionalities to the Bootstrap components in a native way.
+{{end}}
+
+{{if BlazorUI == "MudBlazor"}}
+
+* [MudBlazor](https://mudblazor.com/) as the component library, providing a Material Design component set built natively for Blazor (form controls, data grid, dialogs, snackbars, dates, etc.).
+* [FontAwesome](https://fontawesome.com/) as the fundamental CSS font library.
+* [Flag Icon](https://github.com/lipis/flag-icons) as a library to show flags of countries.
+
+These libraries are selected as the base libraries and available to the applications and modules.
+
+A theme using the MudBlazor variant must place the four MudBlazor providers in the layout root so dialogs, snackbars and popovers work everywhere:
+
+```razor
+
+
+
+
+
+@Body
+```
+
+The provided themes (`Volo.Abp.AspNetCore.Components.Server.MudBlazorLeptonXTheme`, `Volo.Abp.AspNetCore.Components.WebAssembly.MudBlazorLeptonXTheme`, etc.) ship these providers as part of their layout templates.
+
+{{end}}
+
### The Layout
All themes must define a layout for the application. The following image shows the user management page in the [Basic Theme](basic-theme.md) application layout:
@@ -90,6 +118,8 @@ A theme is simply a Razor Class Library.
The easiest way of creating a new theme is adding [Basic Theme Source Code](https://github.com/abpframework/abp/tree/dev/modules/basic-theme) module with source codes and customizing it.
+{{if BlazorUI == "Blazorise"}}
+
{{if UI == "Blazor"}}
```bash
abp add-package Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme --with-source-code --add-to-solution-file
@@ -102,6 +132,24 @@ abp add-package Volo.Abp.AspNetCore.Components.Server.BasicTheme --with-source-c
```
{{end}}
+{{end}}
+
+{{if BlazorUI == "MudBlazor"}}
+
+{{if UI == "Blazor"}}
+```bash
+abp add-package Volo.Abp.AspNetCore.Components.WebAssembly.MudBlazorBasicTheme --with-source-code --add-to-solution-file
+```
+{{end}}
+
+{{if UI == "BlazorServer"}}
+```bash
+abp add-package Volo.Abp.AspNetCore.Components.Server.MudBlazorBasicTheme --with-source-code --add-to-solution-file
+```
+{{end}}
+
+{{end}}
+
### Global Styles / Scripts
A theme generally needs to add a global style to the page. ABP provides a system to manage the [Global Styles and Scripts](global-scripts-styles.md). A theme can implement the `IBundleContributor` to add global style or script files to the page.
diff --git a/docs/en/tutorials/book-store-with-abp-suite/part-05.md b/docs/en/tutorials/book-store-with-abp-suite/part-05.md
index 73a1d9c348..865658d186 100644
--- a/docs/en/tutorials/book-store-with-abp-suite/part-05.md
+++ b/docs/en/tutorials/book-store-with-abp-suite/part-05.md
@@ -11,7 +11,8 @@
//[doc-params]
{
"UI": ["MVC","Blazor","BlazorServer", "BlazorWebApp","NG","MAUIBlazor"],
- "DB": ["EF", "Mongo"]
+ "DB": ["EF", "Mongo"],
+ "BlazorUI": ["Blazorise", "MudBlazor"]
}
````
diff --git a/docs/en/tutorials/book-store/part-02.md b/docs/en/tutorials/book-store/part-02.md
index 1fe21c2848..5c399f9209 100644
--- a/docs/en/tutorials/book-store/part-02.md
+++ b/docs/en/tutorials/book-store/part-02.md
@@ -10,7 +10,8 @@
//[doc-params]
{
"UI": ["MVC","Blazor","BlazorServer", "BlazorWebApp", "NG", "MAUIBlazor"],
- "DB": ["EF","Mongo"]
+ "DB": ["EF","Mongo"],
+ "BlazorUI": ["Blazorise", "MudBlazor"]
}
````
````json
@@ -549,6 +550,8 @@ When you click on the Books menu item under the Book Store parent, you will be r
### Book List
+{{if BlazorUI == "Blazorise"}}
+
We will use the [Blazorise library](https://blazorise.com/) as the UI component kit. It is a very powerful library that supports major HTML/CSS frameworks, including Bootstrap.
ABP provides a generic base class - `AbpCrudPageBase<...>`, to create CRUD style pages. This base class is compatible with the `ICrudAppService` that was used to build the `IBookAppService`. So, we can inherit from the `AbpCrudPageBase` to automate the code behind for the standard CRUD stuff.
@@ -624,6 +627,81 @@ Open the `Books.razor` and replace the content as the following:
While the code above is pretty easy to understand, you can check the Blazorise [Card](https://blazorise.com/docs/components/card/) and [DataGrid](https://blazorise.com/docs/extensions/datagrid/) documents to understand them better.
+{{end}}
+
+{{if BlazorUI == "MudBlazor"}}
+
+We will use the [MudBlazor library](https://mudblazor.com/) as the UI component kit. It is a Material Design component library built natively for Blazor.
+
+ABP provides a generic base class — `AbpMudCrudPageBase<...>`, to create CRUD style pages. This base class is compatible with the `ICrudAppService` that was used to build the `IBookAppService`. So, we can inherit from the `AbpMudCrudPageBase` to automate the code behind for the standard CRUD stuff.
+
+Open the `Books.razor` and replace the content as the following:
+
+````razor
+@page "/books"
+@using Volo.Abp.Application.Dtos
+@using Acme.BookStore.Books
+@using Acme.BookStore.Localization
+@inherits AbpMudCrudPageBase
+
+
+
+
+ @L["Books"]
+
+
+
+
+
+
+
+
+ @L[$"Enum:BookType.{context.Item.Type}"]
+
+
+
+
+ @context.Item.PublishDate.ToShortDateString()
+
+
+
+
+
+ @context.Item.CreationTime.ToLongDateString()
+
+
+
+
+
+
+
+@code
+{
+ public Books() // Constructor
+ {
+ LocalizationResource = typeof(BookStoreResource);
+ }
+}
+````
+
+> If you see some syntax errors, you can ignore them if your application is properly built and running. Visual Studio still has some bugs with Blazor.
+
+* Inherited from `AbpMudCrudPageBase` which implements all the CRUD details for us.
+* `Entities`, `TotalCount`, `PageSize`, `OnDataGridReadAsync` are defined in the base class.
+* `LocalizationResource` is set to the `BookStoreResource` to localize the texts.
+* `AbpMudExtensibleDataGrid` is the ABP-extended `MudDataGrid` that supports the [data table column extension system](../../framework/ui/blazor/data-table-column-extensions.md).
+
+While the code above is pretty easy to understand, you can check the MudBlazor [Card](https://mudblazor.com/components/card) and [DataGrid](https://mudblazor.com/components/datagrid) documents to understand them better.
+
+{{end}}
+
#### About the AbpCrudPageBase
We will continue benefitting from `AbpCrudPageBase` for the books page. You could just inject the `IBookAppService` and perform all the server side calls yourself (thanks to the [Dynamic C# HTTP API Client Proxy](../../framework/api-development/dynamic-csharp-clients.md) system of the ABP). We will do it manually for the authors page to demonstrate how to call the server side HTTP APIs in your Blazor applications.
diff --git a/docs/en/tutorials/book-store/part-03.md b/docs/en/tutorials/book-store/part-03.md
index 73b2daea4e..5a4ae72eac 100644
--- a/docs/en/tutorials/book-store/part-03.md
+++ b/docs/en/tutorials/book-store/part-03.md
@@ -10,7 +10,8 @@
//[doc-params]
{
"UI": ["MVC","Blazor","BlazorServer","BlazorWebApp","NG", "MAUIBlazor"],
- "DB": ["EF","Mongo"]
+ "DB": ["EF","Mongo"],
+ "BlazorUI": ["Blazorise", "MudBlazor"]
}
````
@@ -1094,6 +1095,8 @@ In this section, you will learn how to create a new modal dialog form to create
### Add a "New Button" Button
+{{if BlazorUI == "Blazorise"}}
+
Open the `Books.razor` and replace the `` section with the following code:
````xml
@@ -1110,6 +1113,27 @@ Open the `Books.razor` and replace the `` section with the following
````
+{{end}}
+
+{{if BlazorUI == "MudBlazor"}}
+
+Open the `Books.razor` and replace the `` section with the following code:
+
+````razor
+
+
+ @L["Books"]
+
+
+ @L["NewBook"]
+
+
+````
+
+{{end}}
+
This will change the card header by adding a "New book" button to the right side:

@@ -1118,6 +1142,8 @@ Now, we can add a modal that will be opened when we click the button.
### Book Creation Modal
+{{if BlazorUI == "Blazorise"}}
+
Open the `Books.razor` and add the following code to the end of the page:
````xml
@@ -1184,6 +1210,52 @@ This code requires a service; Inject the `AbpBlazorMessageLocalizerHelper` at
* The form implements validation and the `AbpBlazorMessageLocalizerHelper` is used to simply localize the validation messages.
* The `CreateModal` object, `CloseCreateModalAsync` and `CreateEntityAsync` methods are defined by the base class. Check out the [Blazorise documentation](https://blazorise.com/docs/) if you want to understand the `Modal` and the other components.
+{{end}}
+
+{{if BlazorUI == "MudBlazor"}}
+
+Open the `Books.razor` and add the following code to the end of the page:
+
+````razor
+
+
+ @L["NewBook"]
+
+
+
+
+
+ @foreach (BookType bookTypeValue in Enum.GetValues(typeof(BookType)))
+ {
+ @L[$"Enum:BookType.{(int)bookTypeValue}"]
+ }
+
+
+
+
+
+
+ @L["Cancel"]
+ @L["Save"]
+
+
+````
+
+* The form uses `[Required]`/DataAnnotations for validation; messages are localized via the same `AbpResource` localization system.
+* The `_createDialog` field, `CloseCreateDialogAsync`, `CreateFormRef` and `CreateEntityAsync` are all defined in `AbpMudCrudPageBase`. Check the [MudBlazor documentation](https://mudblazor.com/components/dialog) if you want to understand the `MudDialog` and other components.
+
+{{end}}
+
That's all. Run the application and try to add a new book:

@@ -1194,6 +1266,8 @@ Editing a book is similar to creating a new book.
### Actions Dropdown
+{{if BlazorUI == "Blazorise"}}
+
Open the `Books.razor` and add the following `DataGridEntityActionsColumn` section inside the `DataGridColumns` as the first item:
````xml
@@ -1212,12 +1286,38 @@ Open the `Books.razor` and add the following `DataGridEntityActionsColumn` secti
The `DataGridEntityActionsColumn` component is used to show an "Actions" dropdown for each row in the `DataGrid`. The `DataGridEntityActionsColumn` shows a **single button** instead of a dropdown if there is only one available action inside it:
+{{end}}
+
+{{if BlazorUI == "MudBlazor"}}
+
+Open the `Books.razor` and add the following `MudDataGridEntityActionsColumn` as the first column inside the `` section of the `AbpMudExtensibleDataGrid`:
+
+````razor
+
+
+
+
+
+
+
+````
+
+* `OpenEditDialogAsync` is defined in the base class which takes the entity (book) to edit.
+
+The `MudDataGridEntityActionsColumn` component is used to show an "Actions" menu for each row in the data grid. It shows a **single icon button** when there is only one available action and a dropdown menu when there are multiple actions.
+
+{{end}}
+

### Edit Modal
We can now define a modal to edit the book. Add the following code to the end of the `Books.razor` page:
+{{if BlazorUI == "Blazorise"}}
+
````xml
@@ -1273,6 +1373,47 @@ We can now define a modal to edit the book. Add the following code to the end of
````
+{{end}}
+
+{{if BlazorUI == "MudBlazor"}}
+
+````razor
+
+
+ @EditingEntity.Name
+
+
+
+
+
+ @foreach (BookType bookTypeValue in Enum.GetValues(typeof(BookType)))
+ {
+ @L[$"Enum:BookType.{(int)bookTypeValue}"]
+ }
+
+
+
+
+
+
+ @L["Cancel"]
+ @L["Save"]
+
+
+````
+
+{{end}}
+
### Mapperly Configuration
The base `AbpCrudPageBase` uses the [object to object mapping](../../framework/infrastructure/object-to-object-mapping.md) system to convert an incoming `BookDto` object to a `CreateUpdateBookDto` object. So, we need to define the mapping.
@@ -1304,7 +1445,11 @@ You can now run the application and try to edit a book.
## Deleting a Book
-Open the `Books.razor` page and add the following `EntityAction` code under the "Edit" action inside `EntityActions`:
+Open the `Books.razor` page and add the following entity action code under the "Edit" action.
+
+{{if BlazorUI == "Blazorise"}}
+
+Add the following `EntityAction` code under the "Edit" action inside `EntityActions`:
````xml
````
+{{end}}
+
+{{if BlazorUI == "MudBlazor"}}
+
+Add the following `MudEntityAction` code under the "Edit" action inside `MudEntityActions`:
+
+````razor
+
+````
+
+{{end}}
+
* `DeleteEntityAsync` is defined in the base class that deletes the entity by performing a call to the server.
* `ConfirmationMessage` is a callback to show a confirmation message before executing the action.
* `GetDeleteConfirmationMessage` is defined in the base class. You can override this method (or pass another value to the `ConfirmationMessage` parameter) to customize the localization message.
@@ -1327,6 +1487,8 @@ Run the application and try to delete a book.
Here's the complete code to create the book management CRUD page, that has been developed in the last two parts:
+{{if BlazorUI == "Blazorise"}}
+
````xml
@page "/books"
@using Volo.Abp.Application.Dtos
@@ -1521,3 +1683,143 @@ Here's the complete code to create the book management CRUD page, that has been
{{end}}
+{{if BlazorUI == "MudBlazor"}}
+
+````razor
+@page "/books"
+@using Volo.Abp.Application.Dtos
+@using Acme.BookStore.Books
+@using Acme.BookStore.Localization
+@using Microsoft.Extensions.Localization
+@inherits AbpMudCrudPageBase
+
+
+
+
+ @L["Books"]
+
+
+ @L["NewBook"]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ @L[$"Enum:BookType.{(int)context.Item.Type}"]
+
+
+
+
+ @context.Item.PublishDate.ToShortDateString()
+
+
+
+
+
+ @context.Item.CreationTime.ToLongDateString()
+
+
+
+
+
+
+
+
+
+ @L["NewBook"]
+
+
+
+
+
+ @foreach (BookType bookTypeValue in Enum.GetValues(typeof(BookType)))
+ {
+ @L[$"Enum:BookType.{(int)bookTypeValue}"]
+ }
+
+
+
+
+
+
+ @L["Cancel"]
+ @L["Save"]
+
+
+
+
+
+ @EditingEntity.Name
+
+
+
+
+
+ @foreach (BookType bookTypeValue in Enum.GetValues(typeof(BookType)))
+ {
+ @L[$"Enum:BookType.{(int)bookTypeValue}"]
+ }
+
+
+
+
+
+
+ @L["Cancel"]
+ @L["Save"]
+
+
+
+@code
+{
+ public Books() // Constructor
+ {
+ LocalizationResource = typeof(BookStoreResource);
+ }
+}
+````
+
+{{end}}
+
+{{end}}
+
diff --git a/docs/en/tutorials/book-store/part-09.md b/docs/en/tutorials/book-store/part-09.md
index b70fd3e875..9f0489516a 100644
--- a/docs/en/tutorials/book-store/part-09.md
+++ b/docs/en/tutorials/book-store/part-09.md
@@ -10,7 +10,8 @@
//[doc-params]
{
"UI": ["MVC","Blazor","BlazorServer","BlazorWebApp","NG", "MAUIBlazor"],
- "DB": ["EF","Mongo"]
+ "DB": ["EF","Mongo"],
+ "BlazorUI": ["Blazorise", "MudBlazor"]
}
````
@@ -837,6 +838,8 @@ That's all! This is a fully working CRUD page, you can create, edit and delete a
Create a new Razor Component Page, `/Pages/Authors.razor`, in the {{ if UI == "BlazorServer" }}`Acme.BookStore.Blazor`{{ else if UI == "MAUIBlazor" }}`Acme.BookStore.MauiBlazor`{{ else }}`Acme.BookStore.Blazor.Client`{{ end }} project with the following content:
+{{if BlazorUI == "Blazorise"}}
+
````xml
@page "/authors"
@using Acme.BookStore.Authors
@@ -1017,11 +1020,129 @@ Create a new Razor Component Page, `/Pages/Authors.razor`, in the {{ if UI == "B
````
-* This code is similar to the `Books.razor`, except it doesn't inherit from the `AbpCrudPageBase`, but uses its own implementation.
+{{end}}
+
+{{if BlazorUI == "MudBlazor"}}
+
+````razor
+@page "/authors"
+@using Acme.BookStore.Authors
+@using Acme.BookStore.Localization
+@inherits BookStoreComponentBase
+@inject IAuthorAppService AuthorAppService
+
+
+
+
+ @L["Authors"]
+
+
+ @if (CanCreateAuthor)
+ {
+
+ @L["NewAuthor"]
+
+ }
+
+
+
+
+
+
+
+
+ @if (CanEditAuthor)
+ {
+
+ @L["Edit"]
+
+ }
+ @if (CanDeleteAuthor)
+ {
+
+ @L["Delete"]
+
+ }
+
+
+
+
+
+
+ @context.Item.BirthDate.ToShortDateString()
+
+
+
+
+
+
+
+
+
+ @L["NewAuthor"]
+
+
+
+
+
+
+
+
+
+ @L["Cancel"]
+
+ @L["Save"]
+
+
+
+
+
+
+ @EditingAuthor.Name
+
+
+
+
+
+
+
+
+
+ @L["Cancel"]
+
+ @L["Save"]
+
+
+
+````
+
+{{end}}
+
+* This code is similar to the `Books.razor`, except it doesn't inherit from the `AbpCrudPageBase`/`AbpMudCrudPageBase`, but uses its own implementation.
* Injects the `IAuthorAppService` to consume the server side HTTP APIs from the UI. We can directly inject application service interfaces and use just like regular method calls by the help of [Dynamic C# HTTP API Client Proxy System](../../framework/api-development/dynamic-csharp-clients.md), which performs REST API calls for us. See the `Authors` class below to see the usage.
Create a new code behind file, `Authors.razor.cs`, under the `Pages` folder, with the following content:
+{{if BlazorUI == "Blazorise"}}
+
````csharp
using System;
using System.Collections.Generic;
@@ -1195,6 +1316,196 @@ public partial class Authors
}
````
+{{end}}
+
+{{if BlazorUI == "MudBlazor"}}
+
+````csharp
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Acme.BookStore.Authors;
+using Acme.BookStore.Permissions;
+using Microsoft.AspNetCore.Authorization;
+using MudBlazor;
+using Volo.Abp.Application.Dtos;
+
+{{ if UI == "BlazorServer" }}namespace Acme.BookStore.Blazor.Pages;{{ else if UI == "MAUIBlazor" }}namespace Acme.BookStore.MauiBlazor.Pages;{{ else }}namespace Acme.BookStore.Blazor.Client.Pages;{{ end }}
+
+public partial class Authors
+{
+ private IReadOnlyList AuthorList { get; set; }
+
+ private int PageSize { get; } = LimitedResultRequestDto.DefaultMaxResultCount;
+ private int CurrentPage { get; set; }
+ private string CurrentSorting { get; set; }
+ private int TotalCount { get; set; }
+
+ private bool CanCreateAuthor { get; set; }
+ private bool CanEditAuthor { get; set; }
+ private bool CanDeleteAuthor { get; set; }
+
+ private CreateAuthorDto NewAuthor { get; set; }
+
+ private Guid EditingAuthorId { get; set; }
+ private UpdateAuthorDto EditingAuthor { get; set; }
+
+ private MudDialog CreateAuthorDialog { get; set; }
+ private MudDialog EditAuthorDialog { get; set; }
+
+ private MudForm CreateFormRef;
+ private MudForm EditFormRef;
+
+ // MudDatePicker requires nullable DateTime, while AuthorDto.BirthDate is non-nullable.
+ // Bind to a nullable wrapper and sync back to the DTO before saving.
+ private DateTime? NewAuthorBirthDate
+ {
+ get => NewAuthor?.BirthDate;
+ set { if (NewAuthor != null && value.HasValue) NewAuthor.BirthDate = value.Value; }
+ }
+
+ private DateTime? EditingAuthorBirthDate
+ {
+ get => EditingAuthor?.BirthDate;
+ set { if (EditingAuthor != null && value.HasValue) EditingAuthor.BirthDate = value.Value; }
+ }
+
+ public Authors()
+ {
+ NewAuthor = new CreateAuthorDto();
+ EditingAuthor = new UpdateAuthorDto();
+ }
+
+ protected override async Task OnInitializedAsync()
+ {
+ await SetPermissionsAsync();
+ await GetAuthorsAsync();
+ }
+
+ private async Task SetPermissionsAsync()
+ {
+ CanCreateAuthor = await AuthorizationService
+ .IsGrantedAsync(BookStorePermissions.Authors.Create);
+
+ CanEditAuthor = await AuthorizationService
+ .IsGrantedAsync(BookStorePermissions.Authors.Edit);
+
+ CanDeleteAuthor = await AuthorizationService
+ .IsGrantedAsync(BookStorePermissions.Authors.Delete);
+ }
+
+ private async Task GetAuthorsAsync()
+ {
+ var result = await AuthorAppService.GetListAsync(
+ new GetAuthorListDto
+ {
+ MaxResultCount = PageSize,
+ SkipCount = CurrentPage * PageSize,
+ Sorting = CurrentSorting
+ }
+ );
+
+ AuthorList = result.Items;
+ TotalCount = (int)result.TotalCount;
+ }
+
+ private async Task> OnDataGridReadAsync(GridState state)
+ {
+ CurrentSorting = state.SortDefinitions
+ .Select(s => s.SortBy + (s.Descending ? " DESC" : ""))
+ .Aggregate("", (a, b) => string.IsNullOrEmpty(a) ? b : a + "," + b);
+ CurrentPage = state.Page;
+
+ await GetAuthorsAsync();
+
+ return new GridData { Items = AuthorList, TotalItems = TotalCount };
+ }
+
+ private async Task OpenCreateAuthorDialogAsync()
+ {
+ NewAuthor = new CreateAuthorDto();
+ if (CreateFormRef != null) await CreateFormRef.ResetAsync();
+ await CreateAuthorDialog.ShowAsync();
+ }
+
+ private Task CloseCreateAuthorDialogAsync()
+ {
+ return CreateAuthorDialog.CloseAsync();
+ }
+
+ private async Task OpenEditAuthorDialogAsync(AuthorDto author)
+ {
+ EditingAuthorId = author.Id;
+ EditingAuthor = ObjectMapper.Map(author);
+ if (EditFormRef != null) await EditFormRef.ResetAsync();
+ await EditAuthorDialog.ShowAsync();
+ }
+
+ private Task CloseEditAuthorDialogAsync()
+ {
+ return EditAuthorDialog.CloseAsync();
+ }
+
+ private async Task DeleteAuthorAsync(AuthorDto author)
+ {
+ try
+ {
+ var confirmMessage = L["AuthorDeletionConfirmationMessage", author.Name];
+ if (!await Message.Confirm(confirmMessage))
+ {
+ return;
+ }
+
+ await AuthorAppService.DeleteAsync(author.Id);
+ await GetAuthorsAsync();
+ }
+ catch(Exception ex)
+ {
+ await HandleErrorAsync(ex);
+ }
+ }
+
+ private async Task CreateAuthorAsync()
+ {
+ try
+ {
+ await CreateFormRef.Validate();
+ if (CreateFormRef.IsValid)
+ {
+ await AuthorAppService.CreateAsync(NewAuthor);
+ await GetAuthorsAsync();
+ await CreateAuthorDialog.CloseAsync();
+ }
+ }
+ catch(Exception ex)
+ {
+ await HandleErrorAsync(ex);
+ }
+ }
+
+ private async Task UpdateAuthorAsync()
+ {
+ try
+ {
+ await EditFormRef.Validate();
+ if (EditFormRef.IsValid)
+ {
+ await AuthorAppService.UpdateAsync(EditingAuthorId, EditingAuthor);
+ await GetAuthorsAsync();
+ await EditAuthorDialog.CloseAsync();
+ }
+ }
+ catch(Exception ex)
+ {
+ await HandleErrorAsync(ex);
+ }
+ }
+}
+````
+
+{{end}}
+
This class typically defines the properties and methods used by the `Authors.razor` page.
### Object Mapping
diff --git a/docs/en/tutorials/book-store/part-10.md b/docs/en/tutorials/book-store/part-10.md
index e03ada5a96..f8ff85e5d5 100644
--- a/docs/en/tutorials/book-store/part-10.md
+++ b/docs/en/tutorials/book-store/part-10.md
@@ -10,7 +10,8 @@
//[doc-params]
{
"UI": ["MVC","Blazor","BlazorServer","BlazorWebApp","NG", "MAUIBlazor"],
- "DB": ["EF","Mongo"]
+ "DB": ["EF","Mongo"],
+ "BlazorUI": ["Blazorise", "MudBlazor"]
}
````
@@ -1115,7 +1116,9 @@ That's all. Just run the application and try to create or edit an author.
### The Book List
-It is very easy to show the *Author Name* in the book list. Open the `/Pages/Books.razor` file in the {{ if UI == "BlazorServer" }}`Acme.BookStore.Blazor` {{ else if UI == "MAUIBlazor" }}`Acme.BookStore.MauiBlazor` {{ else }}`Acme.BookStore.Blazor.Client`{{ end }} project and add the following `DataGridColumn` definition just after the `Name` (book name) column:
+It is very easy to show the *Author Name* in the book list. Open the `/Pages/Books.razor` file in the {{ if UI == "BlazorServer" }}`Acme.BookStore.Blazor` {{ else if UI == "MAUIBlazor" }}`Acme.BookStore.MauiBlazor` {{ else }}`Acme.BookStore.Blazor.Client`{{ end }} project and add the following column definition just after the `Name` (book name) column:
+
+{{if BlazorUI == "Blazorise"}}
````xml
````
+{{end}}
+
+{{if BlazorUI == "MudBlazor"}}
+
+````razor
+
+````
+
+{{end}}
+
When you run the application, you can see the *Author* column on the table:

@@ -1147,6 +1160,8 @@ protected override async Task OnInitializedAsync()
* It is essential to call the `base.OnInitializedAsync()` since `AbpCrudPageBase` has some initialization code to be executed.
+{{if BlazorUI == "Blazorise"}}
+
Override the `OpenCreateModalAsync` method and adding the following code:
````csharp
@@ -1199,7 +1214,67 @@ The final `@code` block should be the following:
}
````
-Finally, add the following `Field` definition into the `ModalBody` of the *Create* modal, as the first item, before the `Name` field:
+{{end}}
+
+{{if BlazorUI == "MudBlazor"}}
+
+Override the `OpenCreateDialogAsync` method and adding the following code:
+
+````csharp
+protected override async Task OpenCreateDialogAsync()
+{
+ if (!authorList.Any())
+ {
+ throw new UserFriendlyException(message: L["AnAuthorIsRequiredForCreatingBook"]);
+ }
+
+ await base.OpenCreateDialogAsync();
+ NewEntity.AuthorId = authorList.First().Id;
+}
+````
+
+The final `@code` block should be the following:
+
+````csharp
+@code
+{
+ //ADDED A NEW FIELD
+ IReadOnlyList authorList = Array.Empty();
+
+ public Books() // Constructor
+ {
+ LocalizationResource = typeof(BookStoreResource);
+
+ CreatePolicyName = BookStorePermissions.Books.Create;
+ UpdatePolicyName = BookStorePermissions.Books.Edit;
+ DeletePolicyName = BookStorePermissions.Books.Delete;
+ }
+
+ //GET AUTHORS ON INITIALIZATION
+ protected override async Task OnInitializedAsync()
+ {
+ await base.OnInitializedAsync();
+ authorList = (await AppService.GetAuthorLookupAsync()).Items;
+ }
+
+ protected override async Task OpenCreateDialogAsync()
+ {
+ if (!authorList.Any())
+ {
+ throw new UserFriendlyException(message: L["AnAuthorIsRequiredForCreatingBook"]);
+ }
+
+ await base.OpenCreateDialogAsync();
+ NewEntity.AuthorId = authorList.First().Id;
+ }
+}
+````
+
+{{end}}
+
+Finally, add the following field definition into the *Create* modal/dialog, as the first item, before the `Name` field:
+
+{{if BlazorUI == "Blazorise"}}
````xml
@@ -1215,6 +1290,21 @@ Finally, add the following `Field` definition into the `ModalBody` of the *Creat
````
+{{end}}
+
+{{if BlazorUI == "MudBlazor"}}
+
+````razor
+
+ @foreach (var author in authorList)
+ {
+ @author.Name
+ }
+
+````
+
+{{end}}
+
This requires to add a new localization key to the `en.json` file:
````js
@@ -1227,7 +1317,9 @@ You can run the application to see the *Author Selection* while creating a new b
### Edit Book Modal
-Add the following `Field` definition into the `ModalBody` of the *Edit* modal, as the first item, before the `Name` field:
+Add the following field definition into the *Edit* modal/dialog, as the first item, before the `Name` field:
+
+{{if BlazorUI == "Blazorise"}}
````xml
@@ -1243,6 +1335,21 @@ Add the following `Field` definition into the `ModalBody` of the *Edit* modal, a
````
+{{end}}
+
+{{if BlazorUI == "MudBlazor"}}
+
+````razor
+
+ @foreach (var author in authorList)
+ {
+ @author.Name
+ }
+
+````
+
+{{end}}
+
That's all. We are reusing the `authorList` defined for the *Create* modal.
{{end}}
diff --git a/docs/en/tutorials/modular-crm/part-03.md b/docs/en/tutorials/modular-crm/part-03.md
index e0db36d617..d34861a7ab 100644
--- a/docs/en/tutorials/modular-crm/part-03.md
+++ b/docs/en/tutorials/modular-crm/part-03.md
@@ -10,7 +10,8 @@
````json
//[doc-params]
{
- "UI": ["MVC", "BlazorWebApp", "NG"]
+ "UI": ["MVC", "BlazorWebApp", "NG"],
+ "BlazorUI": ["Blazorise", "MudBlazor"]
}
````
@@ -515,6 +516,8 @@ Open the `ModularCrm.Catalog` .NET solution in your IDE, and find the `Pages/Cat
Replace the `Index.razor` file with the following content:
+{{if BlazorUI == "Blazorise"}}
+
````razor
@page "/catalog"
@using System.Collections.Generic
@@ -547,6 +550,44 @@ Replace the `Index.razor` file with the following content:
}
````
+{{end}}
+
+{{if BlazorUI == "MudBlazor"}}
+
+````razor
+@page "/catalog"
+@using System.Collections.Generic
+@using System.Threading.Tasks
+@using ModularCrm.Catalog
+@inject IProductAppService ProductAppService
+
+Products
+
+
+
+
+ @foreach (var product in Products)
+ {
+
+ @product.Name (stock: @product.StockCount)
+
+ }
+
+
+
+
+@code {
+ private List Products { get; set; } = new();
+
+ protected override async Task OnInitializedAsync()
+ {
+ Products = await ProductAppService.GetListAsync();
+ }
+}
+````
+
+{{end}}
+
Here, you inject `IProductAppService`, get all products in `OnInitializedAsync`, and then render the result in a simple list.
{{end}}
diff --git a/docs/en/tutorials/modular-crm/part-05.md b/docs/en/tutorials/modular-crm/part-05.md
index 4387ff6908..4f5b4fc2b5 100644
--- a/docs/en/tutorials/modular-crm/part-05.md
+++ b/docs/en/tutorials/modular-crm/part-05.md
@@ -10,7 +10,8 @@
````json
//[doc-params]
{
- "UI": ["MVC", "BlazorWebApp", "NG"]
+ "UI": ["MVC", "BlazorWebApp", "NG"],
+ "BlazorUI": ["Blazorise", "MudBlazor"]
}
````
@@ -525,6 +526,8 @@ public class OrderingMenuContributor : IMenuContributor
Replace the `Index.razor` content in the `Pages/Ordering` folder of the `ModularCrm.Ordering.Blazor` project with the following code block:
+{{if BlazorUI == "Blazorise"}}
+
````razor
@page "/ordering"
@using System.Collections.Generic
@@ -559,6 +562,46 @@ Replace the `Index.razor` content in the `Pages/Ordering` folder of the `Modular
}
````
+{{end}}
+
+{{if BlazorUI == "MudBlazor"}}
+
+````razor
+@page "/ordering"
+@using System.Collections.Generic
+@using System.Threading.Tasks
+@using ModularCrm.Ordering
+@inject IOrderAppService OrderAppService
+
+Orders
+
+
+
+
+ @foreach (var order in Orders)
+ {
+
+ Customer: @order.CustomerName
+ Product: @order.ProductId
+ State: @order.State
+
+ }
+
+
+
+
+@code {
+ private List Orders { get; set; } = new();
+
+ protected override async Task OnInitializedAsync()
+ {
+ Orders = await OrderAppService.GetListAsync();
+ }
+}
+````
+
+{{end}}
+
This page shows a list of orders on the UI. You haven't created a UI to create new orders, and we will not do it to keep this tutorial simple. If you want to learn how to create advanced UIs with ABP, please follow the [Book Store tutorial](../book-store/index.md).
### Editing the Menu Item
diff --git a/docs/en/tutorials/modular-crm/part-06.md b/docs/en/tutorials/modular-crm/part-06.md
index 31cc0cb48c..1065e5df6e 100644
--- a/docs/en/tutorials/modular-crm/part-06.md
+++ b/docs/en/tutorials/modular-crm/part-06.md
@@ -10,7 +10,8 @@
````json
//[doc-params]
{
- "UI": ["MVC", "BlazorWebApp", "NG"]
+ "UI": ["MVC", "BlazorWebApp", "NG"],
+ "BlazorUI": ["Blazorise", "MudBlazor"]
}
````
@@ -296,6 +297,8 @@ As you can see, we can see the product names instead of product IDs.
Open the `Index.razor` file, and change the `@order.ProductId` part to `@order.ProductName` to write the product name instead of the product ID. The final `Index.razor` content should be the following:
+{{if BlazorUI == "Blazorise"}}
+
````razor
@page "/ordering"
@using System.Collections.Generic
@@ -330,6 +333,46 @@ Open the `Index.razor` file, and change the `@order.ProductId` part to `@order.P
}
````
+{{end}}
+
+{{if BlazorUI == "MudBlazor"}}
+
+````razor
+@page "/ordering"
+@using System.Collections.Generic
+@using System.Threading.Tasks
+@using ModularCrm.Ordering
+@inject IOrderAppService OrderAppService
+
+Orders
+
+
+
+
+ @foreach (var order in Orders)
+ {
+
+ Customer: @order.CustomerName
+ Product: @order.ProductName
+ State: @order.State
+
+ }
+
+
+
+
+@code {
+ private List Orders { get; set; } = new();
+
+ protected override async Task OnInitializedAsync()
+ {
+ Orders = await OrderAppService.GetListAsync();
+ }
+}
+````
+
+{{end}}
+
That's all. Now, you can graph build the main application and run it in ABP Studio to see the result:

diff --git a/docs/en/tutorials/todo/layered/index.md b/docs/en/tutorials/todo/layered/index.md
index c8d95b627c..f0616c7765 100644
--- a/docs/en/tutorials/todo/layered/index.md
+++ b/docs/en/tutorials/todo/layered/index.md
@@ -11,7 +11,8 @@
//[doc-params]
{
"UI": ["MVC", "Blazor", "BlazorServer", "BlazorWebApp" ,"NG", "MAUIBlazor"],
- "DB": ["EF", "Mongo"]
+ "DB": ["EF", "Mongo"],
+ "BlazorUI": ["Blazorise", "MudBlazor"]
}
````
@@ -637,6 +638,8 @@ See the *Dynamic C# Proxies & Auto API Controllers* section below to learn how w
Open the `Index.razor` file in the `Pages` folder of the {{if UI=="Blazor" || UI=="BlazorWebApp"}} *TodoApp.Blazor.Client* {{else if UI=="BlazorServer"}} *TodoApp.Blazor* {{else if UI=="MAUIBlazor"}} *TodoApp.MauiBlazor* {{end}} project and replace the content with the following code block:
+{{if BlazorUI == "Blazorise"}}
+
```xml
@page "/"
@inherits TodoAppComponentBase
@@ -675,6 +678,47 @@ Open the `Index.razor` file in the `Pages` folder of the {{if UI=="Blazor" || UI
```
+{{end}}
+
+{{if BlazorUI == "MudBlazor"}}
+
+```razor
+@page "/"
+@inherits TodoAppComponentBase
+
+
+
+
+
+ TODO LIST
+
+
+
+
+
+
+ Submit
+
+
+
+ @foreach (var todoItem in TodoItems)
+ {
+
+
+ @todoItem.Text
+
+ }
+
+
+
+
+```
+
+{{end}}
+
### Index.razor.css
As the final touch, open the `Index.razor.css` file in the `Pages` folder of the {{if UI=="Blazor" || UI=="BlazorWebApp"}}*TodoApp.Blazor.Client*{{else if UI=="BlazorServer"}} *TodoApp.Blazor* {{else if UI=="MAUIBlazor"}} *TodoApp.MauiBlazor* {{end}} project and add the following content:
diff --git a/docs/en/tutorials/todo/single-layer/index.md b/docs/en/tutorials/todo/single-layer/index.md
index 6a7f45ce62..b62abfb2b9 100644
--- a/docs/en/tutorials/todo/single-layer/index.md
+++ b/docs/en/tutorials/todo/single-layer/index.md
@@ -11,7 +11,8 @@
//[doc-params]
{
"UI": ["MVC", "Blazor", "BlazorServer", "NG"],
- "DB": ["EF", "Mongo"]
+ "DB": ["EF", "Mongo"],
+ "BlazorUI": ["Blazorise", "MudBlazor"]
}
````
@@ -621,6 +622,8 @@ This class uses the {{if UI=="Blazor"}}`ITodoAppService`{{else}}`TodoAppService`
Open the `Index.razor` file in the {{if UI=="BlazorServer"}}`Components/Pages`{{else}}`Pages`{{end}} folder and replace the content with the following code block:
+{{if BlazorUI == "Blazorise"}}
+
```xml
@page "/"
@inherits TodoAppComponentBase
@@ -660,6 +663,47 @@ Open the `Index.razor` file in the {{if UI=="BlazorServer"}}`Components/Pages`{{
```
+{{end}}
+
+{{if BlazorUI == "MudBlazor"}}
+
+```razor
+@page "/"
+@inherits TodoAppComponentBase
+
+
+
+
+
+ TODO LIST
+
+
+
+
+
+
+ Submit
+
+
+
+ @foreach (var todoItem in TodoItems)
+ {
+
+
+ @todoItem.Text
+
+ }
+
+
+
+
+```
+
+{{end}}
+
### Index.razor.css
As the final touch, open the `Index.razor.css` file in the {{if UI=="BlazorServer"}}`Components/Pages`{{else}}`Pages`{{end}} folder and add the following code block at the end of the file:
diff --git a/docs/en/ui-themes/basic-theme/index.md b/docs/en/ui-themes/basic-theme/index.md
index c87491d1b4..71ee739721 100644
--- a/docs/en/ui-themes/basic-theme/index.md
+++ b/docs/en/ui-themes/basic-theme/index.md
@@ -19,6 +19,8 @@ See the [Theming document](../../framework/ui/mvc-razor-pages/theming.md) to lea
The Basic Theme has implementation for the following UI types:
- [MVC UI](../../framework/ui/mvc-razor-pages/basic-theme.md)
-- [Blazor UI](../../framework/ui/blazor/basic-theme.md)
+- [Blazor UI](../../framework/ui/blazor/basic-theme.md) — available in two Blazor UI library variants:
+ - **Blazorise** (default): `Volo.Abp.AspNetCore.Components.{Server,WebAssembly}.BasicTheme`
+ - **MudBlazor**: `Volo.Abp.AspNetCore.Components.{Server,WebAssembly}.MudBlazorBasicTheme`
- [Angular UI](../../framework/ui/angular/basic-theme.md)
diff --git a/docs/en/ui-themes/index.md b/docs/en/ui-themes/index.md
index 2beaa0dc05..36df6270ae 100644
--- a/docs/en/ui-themes/index.md
+++ b/docs/en/ui-themes/index.md
@@ -33,6 +33,15 @@ See the following documents based on the UI type you are using:
- [Basic Theme - Blazor UI](../framework/ui/blazor/basic-theme.md)
- [Basic Theme - Angular UI](../framework/ui/angular/basic-theme.md)
+## Blazor UI Library
+
+When you create an ABP solution with a Blazor host (Blazor Server, Blazor WebAssembly or Blazor WebApp), you can additionally choose the underlying Blazor component library:
+
+* **Blazorise** — the original ABP default, based on Bootstrap.
+* **MudBlazor** — a Material-Design component library, available as an alternative variant. Each official theme has a MudBlazor version (e.g. `MudBlazorLeptonXTheme`, `MudBlazorLeptonXLiteTheme`, `MudBlazorBasicTheme`).
+
+The choice is made at solution creation time via the `--blazor-ui-library` option (`abp new ... -bul mudblazor`) or in the ABP Studio new-solution wizard.
+
## See Also
* [Theming - MVC UI](../framework/ui/mvc-razor-pages/theming.md)
diff --git a/docs/en/ui-themes/lepton-x-lite/blazor.md b/docs/en/ui-themes/lepton-x-lite/blazor.md
index 7190ac1fc2..6b15e1f134 100644
--- a/docs/en/ui-themes/lepton-x-lite/blazor.md
+++ b/docs/en/ui-themes/lepton-x-lite/blazor.md
@@ -10,7 +10,8 @@
````json
//[doc-params]
{
- "UI": ["Blazor", "BlazorServer"]
+ "UI": ["Blazor", "BlazorServer"],
+ "BlazorUI": ["Blazorise", "MudBlazor"]
}
````
@@ -20,6 +21,19 @@ LeptonX Lite has implementation for the ABP Blazor WebAssembly & Blazor Server.
> See the [Theming document](../../framework/ui/mvc-razor-pages/theming.md) to learn about themes.
+{{if BlazorUI == "MudBlazor"}}
+
+> **MudBlazor Variant** — When the `--blazor-ui-library mudblazor` option is used, the LeptonX Lite theme ships as a MudBlazor variant. Replace `LeptonXLiteTheme` with `MudBlazorLeptonXLiteTheme` everywhere in this document (package names, module type names and namespaces). The installation steps, layout customization API and override mechanism are the same; only the package and namespace prefix change. The component implementations use MudBlazor primitives (`MudAppBar`, `MudDrawer`, `MudNavLink`, etc.) instead of Blazorise components.
+>
+> Concrete package names you will see when using the MudBlazor variant:
+>
+> * `Volo.Abp.AspNetCore.Components.{Server,WebAssembly}.MudBlazorLeptonXLiteTheme`
+> * `Volo.Abp.AspNetCore.Components.{Server,WebAssembly}.MudBlazorLeptonXLiteTheme.Bundling`
+> * Module types: `Abp{...}MudBlazorLeptonXLiteThemeModule`, `Abp{...}MudBlazorLeptonXLiteThemeBundlingModule`
+> * Layout namespace: `Volo.Abp.AspNetCore.Components.{Server,WebAssembly}.MudBlazorLeptonXLiteTheme.Themes.MudBlazorLeptonXLite`
+
+{{end}}
+
## Installation
This theme is **already installed** when you create a new solution using the startup templates. If you are using any other template, you can install this theme by following the steps below:
diff --git a/docs/en/ui-themes/lepton-x/blazor.md b/docs/en/ui-themes/lepton-x/blazor.md
index 29e8deb6c3..19ad552f91 100644
--- a/docs/en/ui-themes/lepton-x/blazor.md
+++ b/docs/en/ui-themes/lepton-x/blazor.md
@@ -10,12 +10,26 @@
````json
//[doc-params]
{
- "UI": ["Blazor", "BlazorServer"]
+ "UI": ["Blazor", "BlazorServer"],
+ "BlazorUI": ["Blazorise", "MudBlazor"]
}
````
LeptonX theme is implemented and ready to use with ABP. No custom implementation is needed for Blazor Server & WebAssembly.
+{{if BlazorUI == "MudBlazor"}}
+
+> **MudBlazor Variant** — When the `--blazor-ui-library mudblazor` option is used, the LeptonX theme ships as a MudBlazor variant. Replace `LeptonXTheme` with `MudBlazorLeptonXTheme` everywhere in this document (package names, module type names and namespaces). The installation steps, layout customization API and override mechanism are the same; only the package and namespace prefix change. Component implementations use MudBlazor primitives (`MudAppBar`, `MudDrawer`, `MudNavLink`, `MudMenu`, etc.) instead of Blazorise components.
+>
+> Concrete package names you will see when using the MudBlazor variant:
+>
+> * `Volo.Abp.AspNetCore.Components.{Server,WebAssembly}.MudBlazorLeptonXTheme`
+> * `Volo.Abp.AspNetCore.Components.{Server,WebAssembly}.MudBlazorLeptonXTheme.Bundling`
+> * Module types: `Abp{...}MudBlazorLeptonXThemeModule`, `Abp{...}MudBlazorLeptonXThemeBundlingModule`
+> * Layout namespace: `Volo.Abp.AspNetCore.Components.{Server,WebAssembly}.MudBlazorLeptonXTheme.Themes.MudBlazorLeptonX`
+
+{{end}}
+
## Installation
{{if UI == "Blazor"}}
diff --git a/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/DocumentParameterDto.cs b/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/DocumentParameterDto.cs
index ea387b2794..26d8e6aaa8 100644
--- a/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/DocumentParameterDto.cs
+++ b/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/DocumentParameterDto.cs
@@ -9,5 +9,13 @@ namespace Volo.Docs.Documents
public string DisplayName { get; set; }
public Dictionary Values { get; set; }
+
+ ///
+ /// Conditional visibility: this parameter is shown only when the keyed parameter's
+ /// current value is one of the listed values.
+ /// Example: "DependsOn": { "UI": [ "Blazor", "BlazorServer", "BlazorWebApp" ] }
+ /// When null or empty the parameter is always shown.
+ ///
+ public Dictionary> DependsOn { get; set; }
}
}
\ No newline at end of file
diff --git a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml
index 521ba70ffd..c7a42b7e87 100644
--- a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml
+++ b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml
@@ -504,8 +504,9 @@
for (var i = 0; i < count; ++i)
{
var parameter = Model.DocumentPreferences.Parameters[i];
+ var hiddenStyle = Model.IsParameterVisible(parameter) ? null : "display:none;";
-