mirror of https://github.com/abpframework/abp.git
67 changed files with 1609 additions and 314 deletions
@ -0,0 +1,271 @@ |
|||
# Implementation Plan: Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor |
|||
|
|||
## Overview |
|||
This document outlines the plan to implement all missing features from `Volo.Abp.AspNetCore.Components.Web.Theming` (Blazorise version) into `Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor` (MudBlazor version). |
|||
|
|||
## Current Status |
|||
|
|||
### ✅ Already Implemented |
|||
1. **PageHeader Component** (`Components/PageHeader.razor` + `.razor.cs`) |
|||
2. **MudPageToolbarButton** (`PageToolbars/MudPageToolbarButton.razor` + `.razor.cs`) |
|||
3. **MudBlazorPageToolbarExtensions** (`PageToolbars/MudBlazorPageToolbarExtensions.cs`) - Contains `AddMudButton` method |
|||
4. **Module Registration** (`AbpAspNetCoreComponentsWebThemingMudBlazorModule.cs`) |
|||
|
|||
### ❌ Missing Features |
|||
|
|||
## Implementation Plan |
|||
|
|||
### Phase 1: Core Components (High Priority) |
|||
|
|||
#### 1.1 DynamicLayoutComponent |
|||
**Location:** `Components/DynamicLayoutComponent.razor` + `.razor.cs` |
|||
|
|||
**Description:** Renders dynamic components configured in `AbpDynamicLayoutComponentOptions`. |
|||
|
|||
**Implementation:** |
|||
- Copy `DynamicLayoutComponent.razor` from Blazorise version (no UI-specific code, should work as-is) |
|||
- Copy `DynamicLayoutComponent.razor.cs` from Blazorise version (no UI-specific code, should work as-is) |
|||
- These components are UI-agnostic and can be directly copied |
|||
|
|||
**Files to Create:** |
|||
- `framework/src/Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor/Components/DynamicLayoutComponent.razor` |
|||
- `framework/src/Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor/Components/DynamicLayoutComponent.razor.cs` |
|||
|
|||
**Dependencies:** None (uses base `AbpAspNetCoreComponentsWebThemingModule`) |
|||
|
|||
--- |
|||
|
|||
#### 1.2 LayoutHook Component |
|||
**Location:** `Components/LayoutHooks/LayoutHook.razor` + `.razor.cs` |
|||
|
|||
**Description:** Renders layout hooks that allow injecting components at specific points in layouts. |
|||
|
|||
**Implementation:** |
|||
- Copy `LayoutHook.razor` from Blazorise version (no UI-specific code, should work as-is) |
|||
- Copy `LayoutHook.razor.cs` from Blazorise version (no UI-specific code, should work as-is) |
|||
- These components are UI-agnostic and can be directly copied |
|||
|
|||
**Files to Create:** |
|||
- `framework/src/Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor/Components/LayoutHooks/LayoutHook.razor` |
|||
- `framework/src/Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor/Components/LayoutHooks/LayoutHook.razor.cs` |
|||
|
|||
**Dependencies:** None (uses base `AbpAspNetCoreComponentsWeb.Theming` infrastructure) |
|||
|
|||
--- |
|||
|
|||
### Phase 2: Bundling Components (High Priority) |
|||
|
|||
#### 2.1 AbpStyles Component |
|||
**Location:** `Bundling/AbpStyles.razor` |
|||
|
|||
**Description:** Renders CSS stylesheets with support for prerendering and WebAssembly. |
|||
|
|||
**Implementation:** |
|||
- Copy `AbpStyles.razor` from Blazorise version |
|||
- This component is UI-agnostic and can be directly copied |
|||
- Handles: |
|||
- Prerendering support |
|||
- WebAssembly style file handling |
|||
- Bundle management via `IComponentBundleManager` |
|||
|
|||
**Files to Create:** |
|||
- `framework/src/Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor/Bundling/AbpStyles.razor` |
|||
|
|||
**Dependencies:** |
|||
- `IComponentBundleManager` (from base `Volo.Abp.AspNetCore.Components.Web.Theming`) |
|||
|
|||
--- |
|||
|
|||
#### 2.2 AbpScripts Component |
|||
**Location:** `Bundling/AbpScripts.razor` |
|||
|
|||
**Description:** Renders JavaScript files with support for prerendering and WebAssembly. |
|||
|
|||
**Implementation:** |
|||
- Copy `AbpScripts.razor` from Blazorise version |
|||
- This component is UI-agnostic and can be directly copied |
|||
- Handles: |
|||
- Prerendering support |
|||
- WebAssembly script file handling |
|||
- Bundle management via `IComponentBundleManager` |
|||
|
|||
**Files to Create:** |
|||
- `framework/src/Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor/Bundling/AbpScripts.razor` |
|||
|
|||
**Dependencies:** |
|||
- `IComponentBundleManager` (from base `Volo.Abp.AspNetCore.Components.Web.Theming`) |
|||
|
|||
--- |
|||
|
|||
### Phase 3: PageToolbar Extensions (Medium Priority) |
|||
|
|||
#### 3.1 AddComponent Extension Methods |
|||
**Location:** `PageToolbars/MudBlazorPageToolbarExtensions.cs` |
|||
|
|||
**Description:** Add `AddComponent` extension methods to `PageToolbar` for adding custom components to toolbars. |
|||
|
|||
**Current State:** Only `AddMudButton` exists. |
|||
|
|||
**Implementation:** |
|||
- Add `AddComponent<TComponent>` generic method |
|||
- Add `AddComponent(Type componentType, ...)` non-generic method |
|||
- These methods should use `SimplePageToolbarContributor` (from base project) |
|||
- Follow the same pattern as Blazorise version but without UI-specific code |
|||
|
|||
**Files to Update:** |
|||
- `framework/src/Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor/PageToolbars/MudBlazorPageToolbarExtensions.cs` |
|||
|
|||
**Example Implementation:** |
|||
```csharp |
|||
public static PageToolbar AddComponent<TComponent>( |
|||
this PageToolbar toolbar, |
|||
Dictionary<string, object?>? arguments = null, |
|||
int order = 0, |
|||
string? requiredPolicyName = null) |
|||
{ |
|||
return toolbar.AddComponent( |
|||
typeof(TComponent), |
|||
arguments, |
|||
order, |
|||
requiredPolicyName |
|||
); |
|||
} |
|||
|
|||
public static PageToolbar AddComponent( |
|||
this PageToolbar toolbar, |
|||
Type componentType, |
|||
Dictionary<string, object?>? arguments = null, |
|||
int order = 0, |
|||
string? requiredPolicyName = null) |
|||
{ |
|||
toolbar.Contributors.Add( |
|||
new SimplePageToolbarContributor( |
|||
componentType, |
|||
arguments, |
|||
order, |
|||
requiredPolicyName |
|||
) |
|||
); |
|||
|
|||
return toolbar; |
|||
} |
|||
``` |
|||
|
|||
**Dependencies:** |
|||
- `SimplePageToolbarContributor` (from base `Volo.Abp.AspNetCore.Components.Web.Theming`) |
|||
|
|||
--- |
|||
|
|||
### Phase 4: Module Configuration (Low Priority - Optional) |
|||
|
|||
#### 4.1 Module Service Configuration |
|||
**Location:** `AbpAspNetCoreComponentsWebThemingMudBlazorModule.cs` |
|||
|
|||
**Description:** Configure any MudBlazor-specific services or options. |
|||
|
|||
**Current State:** Module is empty (only depends on base modules). |
|||
|
|||
**Implementation:** |
|||
- Review if any MudBlazor-specific configuration is needed |
|||
- Currently, the base `AbpAspNetCoreComponentsWebThemingModule` handles `AbpDynamicLayoutComponentOptions` |
|||
- May need to add MudBlazor-specific component registrations if required |
|||
|
|||
**Files to Update:** |
|||
- `framework/src/Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor/AbpAspNetCoreComponentsWebThemingMudBlazorModule.cs` |
|||
|
|||
**Note:** This may not be necessary if all configuration is handled in the base module or in theme-specific packages. |
|||
|
|||
--- |
|||
|
|||
## Implementation Notes |
|||
|
|||
### UI-Agnostic Components |
|||
The following components are **UI-agnostic** and can be copied directly from the Blazorise version: |
|||
- `DynamicLayoutComponent` |
|||
- `LayoutHook` |
|||
- `AbpStyles` |
|||
- `AbpScripts` |
|||
|
|||
These components don't use any Blazorise-specific UI components and rely only on: |
|||
- Base Blazor components (`ComponentBase`, `DynamicComponent`) |
|||
- ABP infrastructure (options, services) |
|||
- Standard .NET types |
|||
|
|||
### Infrastructure Already Available |
|||
The following infrastructure is already available in the base `Volo.Abp.AspNetCore.Components.Web.Theming` project and doesn't need to be reimplemented: |
|||
- `PageToolbar`, `PageToolbarManager`, `IPageToolbarManager` |
|||
- `PageToolbarContributor`, `SimplePageToolbarContributor` |
|||
- `PageToolbarItem`, `PageToolbarItemList` |
|||
- `PageLayout`, `PageHeaderOptions` |
|||
- `StandardLayouts` |
|||
- `AbpRouterOptions`, `RouterAssemblyList` |
|||
- `IThemeManager`, `ITheme`, theme infrastructure |
|||
- `ToolbarManager`, `Toolbar`, toolbar infrastructure |
|||
- `AbpDynamicLayoutComponentOptions` |
|||
- `IComponentBundleManager` |
|||
|
|||
### Testing Checklist |
|||
After implementing each phase, verify: |
|||
1. ✅ Components compile without errors |
|||
2. ✅ Components can be used in MudBlazor layouts |
|||
3. ✅ Bundling components work with prerendering |
|||
4. ✅ Layout hooks render correctly |
|||
5. ✅ Dynamic layout components render correctly |
|||
6. ✅ PageToolbar extensions work with MudBlazor components |
|||
|
|||
--- |
|||
|
|||
## Implementation Order |
|||
|
|||
1. **Phase 1** - Core Components (DynamicLayoutComponent, LayoutHook) |
|||
- These are foundational and used by layouts |
|||
- UI-agnostic, easy to implement |
|||
|
|||
2. **Phase 2** - Bundling Components (AbpStyles, AbpScripts) |
|||
- Required for proper asset loading |
|||
- UI-agnostic, easy to implement |
|||
|
|||
3. **Phase 3** - PageToolbar Extensions |
|||
- Enhances toolbar functionality |
|||
- Straightforward implementation |
|||
|
|||
4. **Phase 4** - Module Configuration |
|||
- Optional, only if needed |
|||
|
|||
--- |
|||
|
|||
## Files Summary |
|||
|
|||
### Files to Create (7 files) |
|||
1. `Components/DynamicLayoutComponent.razor` |
|||
2. `Components/DynamicLayoutComponent.razor.cs` |
|||
3. `Components/LayoutHooks/LayoutHook.razor` |
|||
4. `Components/LayoutHooks/LayoutHook.razor.cs` |
|||
5. `Bundling/AbpStyles.razor` |
|||
6. `Bundling/AbpScripts.razor` |
|||
7. (Optional) Update `PageToolbars/MudBlazorPageToolbarExtensions.cs` |
|||
|
|||
### Files to Update (1 file) |
|||
1. `PageToolbars/MudBlazorPageToolbarExtensions.cs` - Add `AddComponent` methods |
|||
|
|||
--- |
|||
|
|||
## Estimated Effort |
|||
|
|||
- **Phase 1:** ~30 minutes (copy files, verify) |
|||
- **Phase 2:** ~30 minutes (copy files, verify) |
|||
- **Phase 3:** ~20 minutes (add extension methods) |
|||
- **Phase 4:** ~10 minutes (if needed) |
|||
|
|||
**Total:** ~1.5 hours |
|||
|
|||
--- |
|||
|
|||
## Dependencies |
|||
|
|||
All required dependencies are already available: |
|||
- Base `Volo.Abp.AspNetCore.Components.Web.Theming` project |
|||
- `Volo.Abp.MudBlazorUI` project |
|||
- Standard Blazor infrastructure |
|||
|
|||
No new NuGet packages or project references are required. |
|||
@ -0,0 +1,19 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor; |
|||
|
|||
public class AbpDynamicLayoutComponentOptions |
|||
{ |
|||
/// <summary>
|
|||
/// Used to define components that renders in the layout
|
|||
/// </summary>
|
|||
[NotNull] |
|||
public Dictionary<Type, IDictionary<string,object>?> Components { get; set; } |
|||
|
|||
public AbpDynamicLayoutComponentOptions() |
|||
{ |
|||
Components = new Dictionary<Type, IDictionary<string, object>?>(); |
|||
} |
|||
} |
|||
@ -0,0 +1,61 @@ |
|||
@implements IDisposable |
|||
@inject IComponentBundleManager BundleManager |
|||
@inject PersistentComponentState ApplicationState |
|||
@if (ScriptFiles != null) |
|||
{ |
|||
foreach (var file in ScriptFiles) |
|||
{ |
|||
var src = file; |
|||
if (!AppBasePath.IsNullOrWhiteSpace()) |
|||
{ |
|||
src = AppBasePath.EnsureEndsWith('/') + file.RemovePreFix("/"); |
|||
} |
|||
<script src="@src"></script> |
|||
} |
|||
} |
|||
|
|||
@code { |
|||
|
|||
private const string PrerenderedKey = "abp_script_prerendered"; |
|||
|
|||
[Parameter] |
|||
public List<string>? WebAssemblyScriptFiles { get; set; } |
|||
|
|||
[Parameter] |
|||
public string? BundleName { get; set; } |
|||
|
|||
[Parameter] |
|||
public string? AppBasePath { get; set; } |
|||
|
|||
private List<string>? ScriptFiles { get; set; } |
|||
|
|||
private PersistingComponentStateSubscription _persistingSubscription; |
|||
|
|||
protected override async Task OnInitializedAsync() |
|||
{ |
|||
_persistingSubscription = ApplicationState.RegisterOnPersisting(Callback); |
|||
if (!ApplicationState.TryTakeFromJson<string>(PrerenderedKey, out _)) |
|||
{ |
|||
// We are in prerendering mode |
|||
if (!BundleName.IsNullOrWhiteSpace()) |
|||
{ |
|||
ScriptFiles = (await BundleManager.GetScriptBundleFilesAsync(BundleName!)).ToList(); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
if (OperatingSystem.IsBrowser() && WebAssemblyScriptFiles != null) |
|||
{ |
|||
ScriptFiles = WebAssemblyScriptFiles; |
|||
} |
|||
} |
|||
} |
|||
|
|||
private Task Callback() |
|||
{ |
|||
ApplicationState.PersistAsJson(PrerenderedKey, PrerenderedKey); |
|||
return Task.CompletedTask; |
|||
} |
|||
|
|||
public void Dispose() => _persistingSubscription.Dispose(); |
|||
} |
|||
@ -0,0 +1,75 @@ |
|||
@implements IDisposable |
|||
@inject IComponentBundleManager BundleManager |
|||
@inject PersistentComponentState ApplicationState |
|||
@if (StyleFiles != null) |
|||
{ |
|||
foreach (var file in StyleFiles) |
|||
{ |
|||
var href = file; |
|||
if (!AppBasePath.IsNullOrWhiteSpace()) |
|||
{ |
|||
href = AppBasePath.EnsureEndsWith('/') + file.RemovePreFix("/"); |
|||
} |
|||
<link rel="stylesheet" href="@href" /> |
|||
} |
|||
} |
|||
|
|||
@code { |
|||
|
|||
private const string PrerenderedKey = "abp_style_prerendered"; |
|||
|
|||
[Parameter] |
|||
public List<string>? WebAssemblyStyleFiles { get; set; } |
|||
|
|||
[Parameter] |
|||
public string? BundleName { get; set; } |
|||
|
|||
[Parameter] |
|||
public string? AppBasePath { get; set; } |
|||
|
|||
private List<string>? StyleFiles { get; set; } |
|||
|
|||
private PersistingComponentStateSubscription _persistingSubscription; |
|||
|
|||
protected override async Task OnInitializedAsync() |
|||
{ |
|||
_persistingSubscription = ApplicationState.RegisterOnPersisting(Callback); |
|||
if (!ApplicationState.TryTakeFromJson<List<string>>(PrerenderedKey, out var scriptFiles)) |
|||
{ |
|||
// We are in prerendering mode |
|||
if (!BundleName.IsNullOrWhiteSpace()) |
|||
{ |
|||
StyleFiles = (await BundleManager.GetStyleBundleFilesAsync(BundleName!)).ToList(); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
StyleFiles = scriptFiles; |
|||
if (OperatingSystem.IsBrowser() && StyleFiles != null && WebAssemblyStyleFiles != null) |
|||
{ |
|||
StyleFiles.AddIfNotContains(WebAssemblyStyleFiles); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private bool _hasRemoveServerStyle = false; |
|||
|
|||
protected override async Task OnAfterRenderAsync(bool firstRender) |
|||
{ |
|||
if (!_hasRemoveServerStyle && OperatingSystem.IsBrowser() && WebAssemblyStyleFiles != null) |
|||
{ |
|||
_hasRemoveServerStyle = true; |
|||
await Task.Delay(3000); |
|||
StyleFiles = WebAssemblyStyleFiles; |
|||
StateHasChanged(); |
|||
} |
|||
} |
|||
|
|||
private Task Callback() |
|||
{ |
|||
ApplicationState.PersistAsJson(PrerenderedKey, StyleFiles); |
|||
return Task.CompletedTask; |
|||
} |
|||
|
|||
public void Dispose() => _persistingSubscription.Dispose(); |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.Bundling; |
|||
|
|||
public interface IComponentBundleManager |
|||
{ |
|||
Task<IReadOnlyList<string>> GetStyleBundleFilesAsync(string bundleName); |
|||
|
|||
Task<IReadOnlyList<string>> GetScriptBundleFilesAsync(string bundleName); |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
@if (AbpDynamicLayoutComponentOptions.Value.Components.Any()) |
|||
{ |
|||
foreach (var (componentType, parameters) in AbpDynamicLayoutComponentOptions.Value.Components) |
|||
{ |
|||
<DynamicComponent Type="@componentType" Parameters="@parameters" /> |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
using Microsoft.AspNetCore.Components; |
|||
using Microsoft.Extensions.Options; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.Components; |
|||
|
|||
public partial class DynamicLayoutComponent : ComponentBase |
|||
{ |
|||
[Inject] |
|||
protected IOptions<AbpDynamicLayoutComponentOptions> AbpDynamicLayoutComponentOptions { get; set; } = default!; |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
@if (LayoutHookViewModel.Hooks.Any()) |
|||
{ |
|||
foreach (var hook in LayoutHookViewModel.Hooks) |
|||
{ |
|||
<DynamicComponent Type="@hook.ComponentType" /> |
|||
} |
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Components; |
|||
using Microsoft.Extensions.Options; |
|||
using Volo.Abp.Ui.LayoutHooks; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.Components.LayoutHooks; |
|||
|
|||
public partial class LayoutHook : ComponentBase |
|||
{ |
|||
[Parameter] |
|||
public string Name { get; set; } = default!; |
|||
|
|||
[Parameter] |
|||
public string? Layout { get; set; } |
|||
|
|||
[Inject] |
|||
protected IOptions<AbpLayoutHookOptions> LayoutHookOptions { get; set; } = default!; |
|||
|
|||
protected LayoutHookViewModel LayoutHookViewModel { get; private set; } = default!; |
|||
|
|||
protected override Task OnInitializedAsync() |
|||
{ |
|||
if (LayoutHookOptions.Value.Hooks.TryGetValue(Name, out var layoutHooks)) |
|||
{ |
|||
layoutHooks = layoutHooks |
|||
.Where(x => IsComponentBase(x) && (string.IsNullOrWhiteSpace(x.Layout) || x.Layout == Layout)) |
|||
.ToList(); |
|||
} |
|||
|
|||
layoutHooks ??= new List<LayoutHookInfo>(); |
|||
|
|||
LayoutHookViewModel = new LayoutHookViewModel(layoutHooks.ToArray(), Layout); |
|||
|
|||
return Task.CompletedTask; |
|||
} |
|||
|
|||
protected virtual bool IsComponentBase(LayoutHookInfo layoutHook) |
|||
{ |
|||
return typeof(ComponentBase).IsAssignableFrom(layoutHook.ComponentType); |
|||
} |
|||
} |
|||
@ -1,65 +0,0 @@ |
|||
@using Microsoft.AspNetCore.Components.Web |
|||
@using Volo.Abp.Ui.Branding |
|||
@inject IBrandingProvider BrandingProvider |
|||
@using global::MudBlazor |
|||
|
|||
<MudGrid Class="entry-row"> |
|||
@if (PageLayout.Title.IsNullOrEmpty()) |
|||
{ |
|||
<PageTitle>@BrandingProvider.AppName</PageTitle> |
|||
} |
|||
else |
|||
{ |
|||
<PageTitle>@PageLayout.Title | @BrandingProvider.AppName</PageTitle> |
|||
} |
|||
|
|||
@if (Options.Value.RenderPageTitle) |
|||
{ |
|||
<MudItem xs="12" sm="6"> |
|||
<MudText Typo="Typo.h4" Class="content-header-title"> |
|||
@PageLayout.Title |
|||
</MudText> |
|||
</MudItem> |
|||
} |
|||
|
|||
@if (Options.Value.RenderBreadcrumbs && PageLayout.BreadcrumbItems.Any()) |
|||
{ |
|||
<MudItem xs="12" sm="6"> |
|||
<MudBreadcrumbs MaxItems="4"> |
|||
@if (BreadcrumbShowHome) |
|||
{ |
|||
<MudBreadcrumbItem Href="/"> |
|||
<MudIcon Icon="@Icons.Material.Filled.Home" /> |
|||
</MudBreadcrumbItem> |
|||
} |
|||
@foreach (var item in PageLayout.BreadcrumbItems) |
|||
{ |
|||
<MudBreadcrumbItem Href="@item.Url"> |
|||
@item.Text |
|||
</MudBreadcrumbItem> |
|||
} |
|||
</MudBreadcrumbs> |
|||
</MudItem> |
|||
} |
|||
|
|||
@if (Options.Value.RenderToolbar) |
|||
{ |
|||
<MudItem xs="12"> |
|||
<MudStack Row="true" Justify="Justify.FlexEnd" Spacing="1"> |
|||
@if (Toolbar == null) |
|||
{ |
|||
@ChildContent |
|||
} |
|||
|
|||
@foreach (var toolbarItemRender in ToolbarItemRenders) |
|||
{ |
|||
<MudStackItem> |
|||
@toolbarItemRender |
|||
</MudStackItem> |
|||
} |
|||
</MudStack> |
|||
</MudItem> |
|||
} |
|||
</MudGrid> |
|||
|
|||
|
|||
@ -1,127 +0,0 @@ |
|||
using Microsoft.AspNetCore.Components; |
|||
using Microsoft.Extensions.Options; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.AspNetCore.Components.Web.Theming.Layout; |
|||
using Volo.Abp.AspNetCore.Components.Web.Theming.PageToolbars; |
|||
using Volo.Abp.BlazoriseUI; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.Components; |
|||
|
|||
public partial class PageHeader : ComponentBase, IDisposable |
|||
{ |
|||
protected List<RenderFragment> ToolbarItemRenders { get; set; } |
|||
|
|||
[Inject] |
|||
public IPageToolbarManager PageToolbarManager { get; set; } = default!; |
|||
|
|||
[Inject] |
|||
public PageLayout PageLayout { get; private set; } = default!; |
|||
|
|||
[Inject] |
|||
public IOptions<PageHeaderOptions> Options { get; set; } = default!; |
|||
|
|||
[Parameter] |
|||
public string? Title { get => PageLayout.Title; set => PageLayout.Title = value; } |
|||
|
|||
[Parameter] |
|||
public bool BreadcrumbShowHome { get; set; } = true; |
|||
|
|||
[Parameter] |
|||
public bool BreadcrumbShowCurrent { get; set; } = true; |
|||
|
|||
[Parameter] |
|||
public RenderFragment ChildContent { get; set; } = default!; |
|||
|
|||
[Parameter] |
|||
public List<BreadcrumbItem> BreadcrumbItems |
|||
{ |
|||
get => PageLayout.BreadcrumbItems.ToList(); |
|||
set |
|||
{ |
|||
PageLayout.BreadcrumbItems.Clear(); |
|||
if (value != null) |
|||
{ |
|||
foreach (var item in value) |
|||
{ |
|||
PageLayout.BreadcrumbItems.Add(item); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
[Parameter] |
|||
public PageToolbar? Toolbar { get; set; } |
|||
|
|||
public PageHeader() |
|||
{ |
|||
ToolbarItemRenders = new List<RenderFragment>(); |
|||
} |
|||
|
|||
protected async override Task OnParametersSetAsync() |
|||
{ |
|||
await base.OnParametersSetAsync(); |
|||
if (Toolbar != null) |
|||
{ |
|||
var toolbarItems = await PageToolbarManager.GetItemsAsync(Toolbar); |
|||
|
|||
if (!ShouldRenderToolbarItems(toolbarItems)) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
ToolbarItemRenders.Clear(); |
|||
|
|||
if (!Options.Value.RenderToolbar) |
|||
{ |
|||
PageLayout.ToolbarItems.Clear(); |
|||
foreach (var item in toolbarItems) |
|||
{ |
|||
PageLayout.ToolbarItems.Add(item); |
|||
} |
|||
return; |
|||
} |
|||
|
|||
foreach (var item in toolbarItems) |
|||
{ |
|||
var sequence = 0; |
|||
ToolbarItemRenders.Add(builder => |
|||
{ |
|||
builder.OpenComponent(sequence, item.ComponentType); |
|||
if (item.Arguments != null) |
|||
{ |
|||
foreach (var argument in item.Arguments) |
|||
{ |
|||
sequence++; |
|||
builder.AddAttribute(sequence, argument.Key, argument.Value); |
|||
} |
|||
} |
|||
builder.CloseComponent(); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
|
|||
protected virtual bool ShouldRenderToolbarItems(PageToolbarItem[] items) |
|||
{ |
|||
if (items.Length != PageLayout.ToolbarItems.Count) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
return items.Where((t, i) => t.ComponentType != PageLayout.ToolbarItems[i].ComponentType).Any(); |
|||
} |
|||
|
|||
protected async override Task OnInitializedAsync() |
|||
{ |
|||
await base.OnInitializedAsync(); |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
PageLayout.Reset(); |
|||
ToolbarItemRenders.Clear(); |
|||
} |
|||
} |
|||
@ -0,0 +1,65 @@ |
|||
@using global::MudBlazor |
|||
@using Microsoft.Extensions.Options |
|||
@using Microsoft.AspNetCore.Components.Web |
|||
@using Volo.Abp.Ui.Branding |
|||
|
|||
@inject IOptions<PageHeaderOptions> Options |
|||
@inject IBrandingProvider BrandingProvider |
|||
|
|||
<MudGrid Class="entry-row"> |
|||
|
|||
@if (PageLayout.Title.IsNullOrEmpty()) |
|||
{ |
|||
<PageTitle>@BrandingProvider.AppName</PageTitle> |
|||
} |
|||
else |
|||
{ |
|||
<PageTitle>@PageLayout.Title | @BrandingProvider.AppName</PageTitle> |
|||
} |
|||
|
|||
@if(Options.Value.RenderPageTitle) |
|||
{ |
|||
<MudItem xs="12" sm="12"> |
|||
<MudText Typo="Typo.h4" Class="content-header-title">@PageLayout.Title</MudText> |
|||
</MudItem> |
|||
} |
|||
|
|||
@if (Options.Value.RenderBreadcrumbs && PageLayout.BreadcrumbItems.Any()) |
|||
{ |
|||
<MudItem xs="12" lg="12" Class="pl-lg-0"> |
|||
<MudBreadcrumbs Items="_breadcrumbItems" Class="pa-0"> |
|||
<ItemTemplate Context="item"> |
|||
<MudLink Href="@item.Href"> |
|||
@if (!string.IsNullOrEmpty(item.Icon)) |
|||
{ |
|||
<MudIcon Icon="@item.Icon" Size="Size.Small" Class="mr-1" /> |
|||
} |
|||
@item.Text |
|||
</MudLink> |
|||
</ItemTemplate> |
|||
<SeparatorTemplate> |
|||
<MudIcon Icon="@Icons.Material.Filled.ChevronRight" Size="Size.Small" /> |
|||
</SeparatorTemplate> |
|||
</MudBreadcrumbs> |
|||
</MudItem> |
|||
} |
|||
|
|||
@if(Options.Value.RenderToolbar) |
|||
{ |
|||
<MudItem xs="12" Class="d-flex justify-end flex-grow-1"> |
|||
<MudStack Row="true" Spacing="2" Class="align-center"> |
|||
@if (Toolbar == null) |
|||
{ |
|||
@ChildContent |
|||
} |
|||
|
|||
@foreach (var toolbarItemRender in ToolbarItemRenders) |
|||
{ |
|||
<MudItem Class="px-1 py-2"> |
|||
@toolbarItemRender |
|||
</MudItem> |
|||
} |
|||
</MudStack> |
|||
</MudItem> |
|||
} |
|||
</MudGrid> |
|||
@ -0,0 +1,199 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Components; |
|||
using MudBlazor; |
|||
using Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.PageToolbars; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.Layout; |
|||
|
|||
public partial class PageHeader : ComponentBase, IDisposable |
|||
{ |
|||
protected List<RenderFragment> ToolbarItemRenders { get; set; } |
|||
|
|||
private List<BreadcrumbItem> _breadcrumbItems = new(); |
|||
|
|||
public IPageToolbarManager PageToolbarManager { get; set; } = default!; |
|||
|
|||
[Inject] |
|||
public PageLayout PageLayout { get; private set; } = default!; |
|||
|
|||
[Parameter] // TODO: Consider removing this property in future and use only PageLayout.
|
|||
public string? Title { get => PageLayout.Title; set => PageLayout.Title = value; } |
|||
|
|||
[Parameter] |
|||
public bool BreadcrumbShowHome { get; set; } = true; |
|||
|
|||
[Parameter] |
|||
public bool BreadcrumbShowCurrent { get; set; } = true; |
|||
|
|||
[Parameter] |
|||
public RenderFragment ChildContent { get; set; } = default!; |
|||
|
|||
[Parameter] // TODO: Consider removing this property in future and use only PageLayout.
|
|||
public List<BreadcrumbItem> BreadcrumbItems { |
|||
get => PageLayout.BreadcrumbItems.ToList(); |
|||
set |
|||
{ |
|||
PageLayout.BreadcrumbItems.Clear(); |
|||
foreach (var item in value) |
|||
{ |
|||
PageLayout.BreadcrumbItems.Add(item); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[Parameter] |
|||
public PageToolbar? Toolbar { get; set; } |
|||
|
|||
public PageHeader() |
|||
{ |
|||
ToolbarItemRenders = new List<RenderFragment>(); |
|||
} |
|||
|
|||
protected override async Task OnParametersSetAsync() |
|||
{ |
|||
await base.OnParametersSetAsync(); |
|||
|
|||
// Build MudBlazor breadcrumb items
|
|||
BuildBreadcrumbItems(); |
|||
|
|||
if (Toolbar != null) |
|||
{ |
|||
var toolbarItems = await PageToolbarManager.GetItemsAsync(Toolbar); |
|||
|
|||
if (!ShouldRenderToolbarItems(toolbarItems)) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
ToolbarItemRenders.Clear(); |
|||
|
|||
if (!Options.Value.RenderToolbar) |
|||
{ |
|||
PageLayout.ToolbarItems.Clear(); |
|||
foreach (var item in toolbarItems) |
|||
{ |
|||
PageLayout.ToolbarItems.Add(item); |
|||
} |
|||
return; |
|||
} |
|||
|
|||
foreach (var item in toolbarItems) |
|||
{ |
|||
var sequence = 0; |
|||
ToolbarItemRenders.Add(builder => |
|||
{ |
|||
builder.OpenComponent(sequence, item.ComponentType); |
|||
if (item.Arguments != null) |
|||
{ |
|||
foreach (var argument in item.Arguments) |
|||
{ |
|||
sequence++; |
|||
builder.AddAttribute(sequence, argument.Key, argument.Value); |
|||
} |
|||
} |
|||
builder.CloseComponent(); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void BuildBreadcrumbItems() |
|||
{ |
|||
_breadcrumbItems.Clear(); |
|||
|
|||
if (BreadcrumbShowHome) |
|||
{ |
|||
_breadcrumbItems.Add(new BreadcrumbItem(string.Empty, href: "/", icon: Icons.Material.Filled.Home)); |
|||
} |
|||
|
|||
var pageItems = PageLayout.BreadcrumbItems.ToList(); |
|||
for (var i = 0; i < pageItems.Count; i++) |
|||
{ |
|||
var item = pageItems[i]; |
|||
var isLast = i == pageItems.Count - 1; |
|||
|
|||
// Convert Blazorise icon to MudBlazor icon if needed
|
|||
var mudIcon = ConvertToMudIcon(item.Icon); |
|||
|
|||
// If BreadcrumbShowCurrent is false, skip the last item or make it non-clickable
|
|||
if (isLast && !BreadcrumbShowCurrent) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
_breadcrumbItems.Add(new BreadcrumbItem( |
|||
item.Text, |
|||
href: isLast ? null : item.Href, |
|||
disabled: isLast, |
|||
icon: mudIcon)); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Converts Blazorise icon to MudBlazor icon.
|
|||
/// You may need to expand this mapping based on the icons used in your application.
|
|||
/// </summary>
|
|||
private static string? ConvertToMudIcon(object? blazoriseIcon) |
|||
{ |
|||
if (blazoriseIcon == null) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
// If it's already a string (MudBlazor icon format), return as-is
|
|||
if (blazoriseIcon is string iconString) |
|||
{ |
|||
return iconString; |
|||
} |
|||
|
|||
// Map common Blazorise icons to MudBlazor icons
|
|||
// Add more mappings as needed for your application
|
|||
var iconName = blazoriseIcon.ToString(); |
|||
|
|||
return iconName switch |
|||
{ |
|||
"Home" => Icons.Material.Filled.Home, |
|||
"Edit" => Icons.Material.Filled.Edit, |
|||
"Delete" => Icons.Material.Filled.Delete, |
|||
"Add" => Icons.Material.Filled.Add, |
|||
"Search" => Icons.Material.Filled.Search, |
|||
"Settings" => Icons.Material.Filled.Settings, |
|||
"User" => Icons.Material.Filled.Person, |
|||
"Users" => Icons.Material.Filled.People, |
|||
"File" => Icons.Material.Filled.InsertDriveFile, |
|||
"Folder" => Icons.Material.Filled.Folder, |
|||
"Dashboard" => Icons.Material.Filled.Dashboard, |
|||
"List" => Icons.Material.Filled.List, |
|||
"Check" => Icons.Material.Filled.Check, |
|||
"Times" => Icons.Material.Filled.Close, |
|||
"Warning" => Icons.Material.Filled.Warning, |
|||
"Info" => Icons.Material.Filled.Info, |
|||
"Question" => Icons.Material.Filled.Help, |
|||
_ => null // Return null for unmapped icons
|
|||
}; |
|||
} |
|||
|
|||
protected virtual bool ShouldRenderToolbarItems(PageToolbarItem[] items) |
|||
{ |
|||
if (items.Length != PageLayout.ToolbarItems.Count) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
return items.Where((t, i) => t.ComponentType != PageLayout.ToolbarItems[i].ComponentType).Any(); |
|||
} |
|||
|
|||
protected override async Task OnInitializedAsync() |
|||
{ |
|||
await base.OnInitializedAsync(); |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
PageLayout.Reset(); |
|||
ToolbarItemRenders.Clear(); |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.Layout; |
|||
|
|||
public class PageHeaderOptions |
|||
{ |
|||
public bool RenderPageTitle { get; set; } = true; |
|||
public bool RenderBreadcrumbs { get; set; } = true; |
|||
public bool RenderToolbar { get; set; } = true; |
|||
} |
|||
@ -0,0 +1,54 @@ |
|||
using System.Collections.ObjectModel; |
|||
using System.ComponentModel; |
|||
using System.Runtime.CompilerServices; |
|||
using MudBlazor; |
|||
using Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.PageToolbars; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.Layout; |
|||
|
|||
public class PageLayout : IScopedDependency, INotifyPropertyChanged |
|||
{ |
|||
private string? title; |
|||
|
|||
// TODO: Consider using this property for setting Page Title too.
|
|||
public virtual string? Title { |
|||
get => title; |
|||
set { |
|||
title = value; |
|||
OnPropertyChanged(); |
|||
} |
|||
} |
|||
|
|||
private string? menuItemName; |
|||
|
|||
public string? MenuItemName { |
|||
get => menuItemName; |
|||
set |
|||
{ |
|||
menuItemName = value; |
|||
OnPropertyChanged(); |
|||
} |
|||
} |
|||
|
|||
public bool ShowToolbar { get; set; } = true; |
|||
|
|||
public virtual ObservableCollection<BreadcrumbItem> BreadcrumbItems { get; } = new(); |
|||
|
|||
public virtual ObservableCollection<PageToolbarItem> ToolbarItems { get; } = new(); |
|||
|
|||
public event PropertyChangedEventHandler? PropertyChanged; |
|||
|
|||
protected void OnPropertyChanged([CallerMemberName] string? propertyName = null) |
|||
{ |
|||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); |
|||
} |
|||
|
|||
public void Reset() |
|||
{ |
|||
Title = string.Empty; |
|||
MenuItemName = string.Empty; |
|||
BreadcrumbItems.Clear(); |
|||
ToolbarItems.Clear(); |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.Layout; |
|||
|
|||
public static class StandardLayouts |
|||
{ |
|||
public const string Application = "Application"; |
|||
public const string Account = "Account"; |
|||
public const string Public = "Public"; |
|||
public const string Empty = "Empty"; |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.PageToolbars; |
|||
|
|||
public interface IPageToolbarContributor |
|||
{ |
|||
Task ContributeAsync(PageToolbarContributionContext context); |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.PageToolbars; |
|||
|
|||
public interface IPageToolbarManager |
|||
{ |
|||
Task<PageToolbarItem[]> GetItemsAsync(PageToolbar toolbar); |
|||
} |
|||
@ -1,34 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.AspNetCore.Components.Web.Theming.PageToolbars; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.PageToolbars; |
|||
|
|||
public static class MudBlazorPageToolbarExtensions |
|||
{ |
|||
public static PageToolbar AddMudButton( |
|||
this PageToolbar toolbar, |
|||
string text, |
|||
Func<Task> clicked, |
|||
object? icon = null, |
|||
int order = 0, |
|||
string? requiredPolicyName = null) |
|||
{ |
|||
toolbar.AddComponent( |
|||
typeof(MudPageToolbarButton), |
|||
new Dictionary<string, object?> |
|||
{ |
|||
{ nameof(MudPageToolbarButton.Text), text }, |
|||
{ nameof(MudPageToolbarButton.Disabled), false }, |
|||
{ nameof(MudPageToolbarButton.Icon), icon }, |
|||
{ nameof(MudPageToolbarButton.Clicked), clicked } |
|||
}, |
|||
order, |
|||
requiredPolicyName |
|||
); |
|||
|
|||
return toolbar; |
|||
} |
|||
} |
|||
|
|||
@ -1,13 +0,0 @@ |
|||
@using global::MudBlazor |
|||
|
|||
<MudButton Variant="@Variant.Filled" |
|||
Color="@Color.Primary" |
|||
Disabled="Disabled" |
|||
OnClick="@OnClickCallback"> |
|||
@if (Icon is string iconString && !string.IsNullOrWhiteSpace(iconString)) |
|||
{ |
|||
<MudIcon Icon="@iconString" Class="me-1" /> |
|||
} |
|||
@Text |
|||
</MudButton> |
|||
|
|||
@ -1,33 +0,0 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Components; |
|||
using Microsoft.AspNetCore.Components.Web; |
|||
using global::MudBlazor; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.PageToolbars; |
|||
|
|||
public partial class MudPageToolbarButton : ComponentBase |
|||
{ |
|||
[Parameter] |
|||
public string Text { get; set; } = string.Empty; |
|||
|
|||
[Parameter] |
|||
public bool Disabled { get; set; } |
|||
|
|||
[Parameter] |
|||
public object? Icon { get; set; } |
|||
|
|||
[Parameter] |
|||
public Func<Task>? Clicked { get; set; } |
|||
|
|||
private EventCallback<MouseEventArgs> OnClickCallback => EventCallback.Factory.Create<MouseEventArgs>(this, OnClickAsync); |
|||
|
|||
private async Task OnClickAsync(MouseEventArgs args) |
|||
{ |
|||
if (Clicked != null && !Disabled) |
|||
{ |
|||
await Clicked.Invoke(); |
|||
} |
|||
await Task.CompletedTask; |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.PageToolbars; |
|||
|
|||
public class PageToolbar |
|||
{ |
|||
public PageToolbarContributorList Contributors { get; set; } |
|||
|
|||
public PageToolbar() |
|||
{ |
|||
Contributors = new PageToolbarContributorList(); |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
using System; |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.PageToolbars; |
|||
|
|||
public class PageToolbarContributionContext |
|||
{ |
|||
[NotNull] |
|||
public IServiceProvider ServiceProvider { get; } |
|||
|
|||
[NotNull] |
|||
public PageToolbarItemList Items { get; } |
|||
|
|||
public PageToolbarContributionContext( |
|||
[NotNull] IServiceProvider serviceProvider) |
|||
{ |
|||
ServiceProvider = Check.NotNull(serviceProvider, nameof(serviceProvider)); |
|||
Items = new PageToolbarItemList(); |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.PageToolbars; |
|||
|
|||
public abstract class PageToolbarContributor : IPageToolbarContributor |
|||
{ |
|||
public abstract Task ContributeAsync(PageToolbarContributionContext context); |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.PageToolbars; |
|||
|
|||
public class PageToolbarContributorList : List<IPageToolbarContributor> |
|||
{ |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.PageToolbars; |
|||
|
|||
public class PageToolbarDictionary : Dictionary<string, PageToolbar> |
|||
{ |
|||
|
|||
} |
|||
@ -0,0 +1,69 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using MudBlazor; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.PageToolbars; |
|||
|
|||
public static class PageToolbarExtensions |
|||
{ |
|||
public static PageToolbar AddComponent<TComponent>( |
|||
this PageToolbar toolbar, |
|||
Dictionary<string, object?>? arguments = null, |
|||
int order = 0, |
|||
string? requiredPolicyName = null) |
|||
{ |
|||
return toolbar.AddComponent( |
|||
typeof(TComponent), |
|||
arguments, |
|||
order, |
|||
requiredPolicyName |
|||
); |
|||
} |
|||
|
|||
public static PageToolbar AddComponent( |
|||
this PageToolbar toolbar, |
|||
Type componentType, |
|||
Dictionary<string, object?>? arguments = null, |
|||
int order = 0, |
|||
string? requiredPolicyName = null) |
|||
{ |
|||
toolbar.Contributors.Add( |
|||
new SimplePageToolbarContributor( |
|||
componentType, |
|||
arguments, |
|||
order, |
|||
requiredPolicyName |
|||
) |
|||
); |
|||
|
|||
return toolbar; |
|||
} |
|||
|
|||
public static PageToolbar AddButton( |
|||
this PageToolbar toolbar, |
|||
string text, |
|||
Func<Task> clicked, |
|||
object? icon = null, |
|||
Color? color = null, |
|||
bool disabled = false, |
|||
int order = 0, |
|||
string? requiredPolicyName = null) |
|||
{ |
|||
// TODO@MudBlazor: Implement ToolbarButton contributor
|
|||
// toolbar.AddComponent<ToolbarButton>(
|
|||
// new Dictionary<string, object?>
|
|||
// {
|
|||
// { nameof(ToolbarButton.Color), color ?? Color.Primary},
|
|||
// { nameof(ToolbarButton.Text), text},
|
|||
// { nameof(ToolbarButton.Disabled), disabled},
|
|||
// { nameof(ToolbarButton.Icon), icon},
|
|||
// { nameof(ToolbarButton.Clicked),clicked},
|
|||
// },
|
|||
// order,
|
|||
// requiredPolicyName
|
|||
// );
|
|||
|
|||
return toolbar; |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.PageToolbars; |
|||
|
|||
public class PageToolbarItem |
|||
{ |
|||
[NotNull] |
|||
public Type ComponentType { get; } |
|||
|
|||
public Dictionary<string, object?>? Arguments { get; set; } |
|||
|
|||
public int Order { get; set; } |
|||
|
|||
public PageToolbarItem( |
|||
[NotNull] Type componentType, |
|||
Dictionary<string, object?>? arguments = null, |
|||
int order = 0) |
|||
{ |
|||
ComponentType = Check.NotNull(componentType, nameof(componentType)); |
|||
Arguments = arguments; |
|||
Order = order; |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.PageToolbars; |
|||
|
|||
public class PageToolbarItemList : List<PageToolbarItem> |
|||
{ |
|||
|
|||
} |
|||
@ -0,0 +1,38 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.PageToolbars; |
|||
|
|||
public class PageToolbarManager : IPageToolbarManager, ITransientDependency |
|||
{ |
|||
protected IServiceScopeFactory ServiceScopeFactory { get; } |
|||
|
|||
public PageToolbarManager( |
|||
IServiceScopeFactory serviceScopeFactory) |
|||
{ |
|||
ServiceScopeFactory = serviceScopeFactory; |
|||
} |
|||
|
|||
public virtual async Task<PageToolbarItem[]> GetItemsAsync(PageToolbar toolbar) |
|||
{ |
|||
if (toolbar == null || !toolbar.Contributors.Any()) |
|||
{ |
|||
return Array.Empty<PageToolbarItem>(); |
|||
} |
|||
|
|||
using (var scope = ServiceScopeFactory.CreateScope()) |
|||
{ |
|||
var context = new PageToolbarContributionContext(scope.ServiceProvider); |
|||
|
|||
foreach (var contributor in toolbar.Contributors) |
|||
{ |
|||
await contributor.ContributeAsync(context); |
|||
} |
|||
|
|||
return context.Items.OrderBy(i => i.Order).ToArray(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,58 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Authorization; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.PageToolbars; |
|||
|
|||
public class SimplePageToolbarContributor : IPageToolbarContributor |
|||
{ |
|||
public Type ComponentType { get; } |
|||
|
|||
public Dictionary<string, object?>? Arguments { get; set; } |
|||
|
|||
public int Order { get; } |
|||
|
|||
public string? RequiredPolicyName { get; } |
|||
|
|||
private bool? _shouldAddComponent; |
|||
|
|||
public SimplePageToolbarContributor( |
|||
Type componentType, |
|||
Dictionary<string, object?>? arguments = null, |
|||
int order = 0, |
|||
string? requiredPolicyName = null) |
|||
{ |
|||
ComponentType = componentType; |
|||
Arguments = arguments; |
|||
Order = order; |
|||
RequiredPolicyName = requiredPolicyName; |
|||
} |
|||
|
|||
public async Task ContributeAsync(PageToolbarContributionContext context) |
|||
{ |
|||
if (await ShouldAddComponentAsync(context)) |
|||
{ |
|||
context.Items.Add(new PageToolbarItem(ComponentType, Arguments, Order)); |
|||
} |
|||
} |
|||
|
|||
protected virtual async Task<bool> ShouldAddComponentAsync(PageToolbarContributionContext context) |
|||
{ |
|||
if (_shouldAddComponent.HasValue) |
|||
{ |
|||
return _shouldAddComponent.Value; |
|||
} |
|||
|
|||
if (RequiredPolicyName == null) |
|||
{ |
|||
_shouldAddComponent = true; |
|||
return _shouldAddComponent.Value; |
|||
} |
|||
|
|||
var authorizationService = context.ServiceProvider.GetRequiredService<IAuthorizationService>(); |
|||
_shouldAddComponent = await authorizationService.IsGrantedAsync(RequiredPolicyName); |
|||
return _shouldAddComponent.Value; |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
using System.Reflection; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.Routing; |
|||
|
|||
public class AbpRouterOptions |
|||
{ |
|||
public Assembly AppAssembly { get; set; } = default!; |
|||
|
|||
public RouterAssemblyList AdditionalAssemblies { get; } |
|||
|
|||
public AbpRouterOptions() |
|||
{ |
|||
AdditionalAssemblies = new RouterAssemblyList(); |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using System.Collections.Generic; |
|||
using System.Reflection; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.Routing; |
|||
|
|||
public class RouterAssemblyList : List<Assembly> |
|||
{ |
|||
|
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.Theming; |
|||
|
|||
public class AbpThemingOptions |
|||
{ |
|||
public ThemeDictionary Themes { get; } |
|||
|
|||
public string? DefaultThemeName { get; set; } |
|||
|
|||
public AbpThemingOptions() |
|||
{ |
|||
Themes = new ThemeDictionary(); |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
using System; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.Theming; |
|||
|
|||
public class DefaultThemeManager : IThemeManager, IScopedDependency, IServiceProviderAccessor |
|||
{ |
|||
public IServiceProvider ServiceProvider { get; } |
|||
public ITheme CurrentTheme => GetCurrentTheme(); |
|||
|
|||
private ITheme? _currentTheme; |
|||
|
|||
protected IThemeSelector ThemeSelector { get; } |
|||
|
|||
public DefaultThemeManager( |
|||
IServiceProvider serviceProvider, |
|||
IThemeSelector themeSelector) |
|||
{ |
|||
ServiceProvider = serviceProvider; |
|||
ThemeSelector = themeSelector; |
|||
} |
|||
|
|||
protected virtual ITheme GetCurrentTheme() |
|||
{ |
|||
if (_currentTheme != null) |
|||
{ |
|||
return _currentTheme; |
|||
} |
|||
|
|||
_currentTheme = (ITheme)ServiceProvider.GetRequiredService(ThemeSelector.GetCurrentThemeInfo().ThemeType); |
|||
return _currentTheme; |
|||
} |
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
using System.Linq; |
|||
using Microsoft.Extensions.Options; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.Theming; |
|||
|
|||
public class DefaultThemeSelector : IThemeSelector, ITransientDependency |
|||
{ |
|||
protected AbpThemingOptions Options { get; } |
|||
|
|||
public DefaultThemeSelector(IOptions<AbpThemingOptions> options) |
|||
{ |
|||
Options = options.Value; |
|||
} |
|||
|
|||
public virtual ThemeInfo GetCurrentThemeInfo() |
|||
{ |
|||
if (!Options.Themes.Any()) |
|||
{ |
|||
throw new AbpException($"No theme registered! Use {nameof(AbpThemingOptions)} to register themes."); |
|||
} |
|||
|
|||
if (Options.DefaultThemeName == null) |
|||
{ |
|||
return Options.Themes.Values.First(); |
|||
} |
|||
|
|||
var themeInfo = Options.Themes.Values.FirstOrDefault(t => t.Name == Options.DefaultThemeName); |
|||
if (themeInfo == null) |
|||
{ |
|||
throw new AbpException("Default theme is configured but it's not found in the registered themes: " + Options.DefaultThemeName); |
|||
} |
|||
|
|||
return themeInfo; |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
using System; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.Theming; |
|||
|
|||
public interface ITheme |
|||
{ |
|||
Type GetLayout(string name, bool fallbackToDefault = true); |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.Theming; |
|||
|
|||
public interface IThemeManager |
|||
{ |
|||
ITheme CurrentTheme { get; } |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.Theming; |
|||
|
|||
public interface IThemeSelector |
|||
{ |
|||
ThemeInfo GetCurrentThemeInfo(); |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.Theming; |
|||
|
|||
public class ThemeDictionary : Dictionary<Type, ThemeInfo> |
|||
{ |
|||
public ThemeInfo Add<TTheme>() |
|||
where TTheme : ITheme |
|||
{ |
|||
return Add(typeof(TTheme)); |
|||
} |
|||
|
|||
public ThemeInfo Add(Type themeType) |
|||
{ |
|||
if (ContainsKey(themeType)) |
|||
{ |
|||
throw new AbpException("This theme is already added before: " + themeType.AssemblyQualifiedName); |
|||
} |
|||
|
|||
return this[themeType] = new ThemeInfo(themeType); |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
using System; |
|||
using Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.Layout; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.Theming; |
|||
|
|||
public static class ThemeExtensions |
|||
{ |
|||
public static Type GetApplicationLayout(this ITheme theme, bool fallbackToDefault = true) |
|||
{ |
|||
return theme.GetLayout(StandardLayouts.Application, fallbackToDefault); |
|||
} |
|||
|
|||
public static Type GetAccountLayout(this ITheme theme, bool fallbackToDefault = true) |
|||
{ |
|||
return theme.GetLayout(StandardLayouts.Account, fallbackToDefault); |
|||
} |
|||
|
|||
public static Type GetPublicLayout(this ITheme theme, bool fallbackToDefault = true) |
|||
{ |
|||
return theme.GetLayout(StandardLayouts.Public, fallbackToDefault); |
|||
} |
|||
|
|||
public static Type GetEmptyLayout(this ITheme theme, bool fallbackToDefault = true) |
|||
{ |
|||
return theme.GetLayout(StandardLayouts.Empty, fallbackToDefault); |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
using System; |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.Theming; |
|||
|
|||
public class ThemeInfo |
|||
{ |
|||
public Type ThemeType { get; } |
|||
|
|||
public string Name { get; } |
|||
|
|||
public ThemeInfo([NotNull] Type themeType) |
|||
{ |
|||
Check.NotNull(themeType, nameof(themeType)); |
|||
|
|||
if (!typeof(ITheme).IsAssignableFrom(themeType)) |
|||
{ |
|||
throw new AbpException($"Given {nameof(themeType)} ({themeType.AssemblyQualifiedName}) should be type of {typeof(ITheme).AssemblyQualifiedName}"); |
|||
} |
|||
|
|||
ThemeType = themeType; |
|||
Name = ThemeNameAttribute.GetName(themeType); |
|||
} |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
using System; |
|||
using System.Linq; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.Theming; |
|||
|
|||
[AttributeUsage(AttributeTargets.Class)] |
|||
public class ThemeNameAttribute : Attribute |
|||
{ |
|||
public string Name { get; set; } |
|||
|
|||
public ThemeNameAttribute(string name) |
|||
{ |
|||
Name = name; |
|||
} |
|||
|
|||
public static string GetName(Type themeType) |
|||
{ |
|||
return themeType |
|||
.GetCustomAttributes(true) |
|||
.OfType<ThemeNameAttribute>() |
|||
.FirstOrDefault()?.Name ?? themeType.Name; |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
using System.Collections.Generic; |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.Toolbars; |
|||
|
|||
public class AbpToolbarOptions |
|||
{ |
|||
[NotNull] |
|||
public List<IToolbarContributor> Contributors { get; } |
|||
|
|||
public AbpToolbarOptions() |
|||
{ |
|||
Contributors = new List<IToolbarContributor>(); |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using JetBrains.Annotations; |
|||
using Microsoft.AspNetCore.Authorization; |
|||
using Microsoft.Extensions.Localization; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.Toolbars; |
|||
|
|||
public interface IToolbarConfigurationContext : IServiceProviderAccessor |
|||
{ |
|||
Toolbar Toolbar { get; } |
|||
|
|||
IAuthorizationService AuthorizationService { get; } |
|||
|
|||
IStringLocalizerFactory StringLocalizerFactory { get; } |
|||
|
|||
Task<bool> IsGrantedAsync(string policyName); |
|||
|
|||
IStringLocalizer? GetDefaultLocalizer(); |
|||
|
|||
[NotNull] |
|||
public IStringLocalizer GetLocalizer<T>(); |
|||
|
|||
[NotNull] |
|||
public IStringLocalizer GetLocalizer(Type resourceType); |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.Toolbars; |
|||
|
|||
public interface IToolbarContributor |
|||
{ |
|||
Task ConfigureToolbarAsync(IToolbarConfigurationContext context); |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.Toolbars; |
|||
|
|||
public interface IToolbarManager |
|||
{ |
|||
Task<Toolbar> GetAsync(string name); |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.Toolbars; |
|||
|
|||
public static class StandardToolbars |
|||
{ |
|||
public const string Main = "Main"; |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
using System.Collections.Generic; |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.Toolbars; |
|||
|
|||
public class Toolbar |
|||
{ |
|||
public string Name { get; } |
|||
|
|||
public List<ToolbarItem> Items { get; } |
|||
|
|||
public Toolbar([NotNull] string name) |
|||
{ |
|||
Name = Check.NotNull(name, nameof(name)); |
|||
Items = new List<ToolbarItem>(); |
|||
} |
|||
} |
|||
@ -0,0 +1,51 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using JetBrains.Annotations; |
|||
using Microsoft.AspNetCore.Authorization; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Localization; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.Toolbars; |
|||
|
|||
public class ToolbarConfigurationContext : IToolbarConfigurationContext |
|||
{ |
|||
public IServiceProvider ServiceProvider { get; } |
|||
|
|||
private readonly IAbpLazyServiceProvider _lazyServiceProvider; |
|||
|
|||
public IAuthorizationService AuthorizationService => _lazyServiceProvider.LazyGetRequiredService<IAuthorizationService>(); |
|||
|
|||
public IStringLocalizerFactory StringLocalizerFactory => _lazyServiceProvider.LazyGetRequiredService<IStringLocalizerFactory>(); |
|||
|
|||
public Toolbar Toolbar { get; } |
|||
|
|||
public ToolbarConfigurationContext(Toolbar toolbar, IServiceProvider serviceProvider) |
|||
{ |
|||
Toolbar = toolbar; |
|||
ServiceProvider = serviceProvider; |
|||
_lazyServiceProvider = ServiceProvider.GetRequiredService<IAbpLazyServiceProvider>(); |
|||
} |
|||
|
|||
public Task<bool> IsGrantedAsync(string policyName) |
|||
{ |
|||
return AuthorizationService.IsGrantedAsync(policyName); |
|||
} |
|||
|
|||
public IStringLocalizer? GetDefaultLocalizer() |
|||
{ |
|||
return StringLocalizerFactory.CreateDefaultOrNull(); |
|||
} |
|||
|
|||
[NotNull] |
|||
public IStringLocalizer GetLocalizer<T>() |
|||
{ |
|||
return StringLocalizerFactory.Create<T>(); |
|||
} |
|||
|
|||
[NotNull] |
|||
public IStringLocalizer GetLocalizer(Type resourceType) |
|||
{ |
|||
return StringLocalizerFactory.Create(resourceType); |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
using System; |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.Toolbars; |
|||
|
|||
public class ToolbarItem |
|||
{ |
|||
public Type ComponentType { |
|||
get => _componentType; |
|||
set => _componentType = Check.NotNull(value, nameof(value)); |
|||
} |
|||
private Type _componentType = default!; |
|||
|
|||
public int Order { get; set; } |
|||
|
|||
public ToolbarItem([NotNull] Type componentType, int order = 0) |
|||
{ |
|||
Order = order; |
|||
ComponentType = Check.NotNull(componentType, nameof(componentType)); |
|||
} |
|||
} |
|||
@ -0,0 +1,38 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Options; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.Toolbars; |
|||
|
|||
public class ToolbarManager : IToolbarManager, ITransientDependency |
|||
{ |
|||
protected AbpToolbarOptions Options { get; } |
|||
protected IServiceProvider ServiceProvider { get; } |
|||
|
|||
public ToolbarManager( |
|||
IOptions<AbpToolbarOptions> options, |
|||
IServiceProvider serviceProvider) |
|||
{ |
|||
ServiceProvider = serviceProvider; |
|||
Options = options.Value; |
|||
} |
|||
|
|||
public async Task<Toolbar> GetAsync(string name) |
|||
{ |
|||
var toolbar = new Toolbar(name); |
|||
|
|||
using (var scope = ServiceProvider.CreateScope()) |
|||
{ |
|||
var context = new ToolbarConfigurationContext(toolbar, scope.ServiceProvider); |
|||
|
|||
foreach (var contributor in Options.Contributors) |
|||
{ |
|||
await contributor.ConfigureToolbarAsync(context); |
|||
} |
|||
} |
|||
|
|||
return toolbar; |
|||
} |
|||
} |
|||
@ -1,6 +0,0 @@ |
|||
@using MudBlazor |
|||
@using Volo.Abp.AspNetCore.Components.Web.Theming |
|||
@using Volo.Abp.AspNetCore.Components.Web.Theming.Layout |
|||
@using Volo.Abp.AspNetCore.Components.Web.Theming.PageToolbars |
|||
@using Volo.Abp.BlazoriseUI |
|||
|
|||
@ -0,0 +1,17 @@ |
|||
namespace Volo.Abp.MudBlazorUI; |
|||
|
|||
public class BreadcrumbItem |
|||
{ |
|||
public string Text { get; set; } |
|||
|
|||
public object? Icon { get; set; } |
|||
|
|||
public string? Url { get; set; } |
|||
|
|||
public BreadcrumbItem(string text, string? url = null, object? icon = null) |
|||
{ |
|||
Text = text; |
|||
Url = url; |
|||
Icon = icon; |
|||
} |
|||
} |
|||
Loading…
Reference in new issue