restructure text editors and static assets

This commit is contained in:
sbwalker
2025-09-05 12:32:43 -04:00
parent 9f923ae968
commit 27041f464f
21 changed files with 36 additions and 2383 deletions

View File

@ -0,0 +1,194 @@
@using Microsoft.Extensions.Configuration
@using Oqtane.Interfaces
@using System.Text.RegularExpressions
@using Radzen
@using Radzen.Blazor
@namespace Oqtane.Modules.Controls
@inherits ModuleControlBase
@implements ITextEditor
@implements IDisposable
@inject Radzen.ThemeService ThemeService
@inject IRadzenEditorSettingService EditorSettingService
@inject DialogService DialogService
@inject NavigationManager NavigationManager
@inject IStringLocalizer<Oqtane.Modules.Controls.RadzenTextEditor> Localizer
<RadzenTheme Theme="@RadzenEditorDefinitions.DefaultTheme" />
<RadzenComponents />
<RadzenHtmlEditor @ref="_editor" Visible="_visible" Placeholder="@Placeholder" style="@($"height: {Height}px;")"
@bind-Value="_value" Execute="OnExecute" class="rz-text-editor">
<ChildContent>
@_toolbar
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
{
<RadzenHtmlEditorCustomTool CommandName="Settings" Icon="settings" Title="@Localizer["Settings"]" />
}
</ChildContent>
</RadzenHtmlEditor>
@code {
private Oqtane.Modules.Controls.RadzenTextEditorInterop _interop;
private RadzenHtmlEditor _editor;
private string _value;
private bool _visible = false;
private string _theme;
private string _background;
private IList<string> _toolbarItems;
private RenderFragment _toolbar;
[Parameter]
public string Placeholder { get; set; }
[Parameter]
public bool ReadOnly { get; set; }
[Parameter]
public int Height { get; set; } = 450;
public string Name => "Radzen HTML Editor";
public override List<Resource> Resources { get; set; } = new List<Resource>()
{
new Resource { ResourceType = ResourceType.Script, Url = "_content/Radzen.Blazor/Radzen.Blazor.js", Location = ResourceLocation.Body },
new Resource { ResourceType = ResourceType.Script, Url = "js/texteditors/radzen/radzen-interop.js", Location = ResourceLocation.Body }
};
protected override void OnInitialized()
{
_interop = new Oqtane.Modules.Controls.RadzenTextEditorInterop(JSRuntime);
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);
if (firstRender)
{
var interop = new Interop(JSRuntime);
await interop.IncludeLink("", "stylesheet", $"{PageState?.Alias.BaseUrl}/css/texteditors/radzen/radzentexteditor.css", "text/css", "", "", "");
await LoadSettings();
_visible = true;
StateHasChanged();
await _interop.Initialize(_editor.Element);
if (!string.IsNullOrEmpty(_theme))
{
ThemeService.SetTheme(_theme);
}
if (!string.IsNullOrEmpty(_background))
{
var backgroundColor = RadzenEditorDefinitions.TransparentBackgroundColor;
switch (_background)
{
case "Light":
backgroundColor = RadzenEditorDefinitions.LightBackgroundColor;
break;
case "Dark":
backgroundColor = RadzenEditorDefinitions.DarkBackgroundColor;
break;
}
await _interop.SetBackgroundColor(_editor.Element, backgroundColor);
}
}
}
public void Initialize(string content)
{
_value = !string.IsNullOrEmpty(content) ? content : string.Empty;
DialogService.OnOpen += OnDialogOpened;
}
public void Dispose()
{
if (DialogService != null)
{
DialogService.OnOpen -= OnDialogOpened;
}
}
public async Task<string> GetContent()
{
await Task.CompletedTask;
return _value;
}
private async Task LoadSettings()
{
var scope = await EditorSettingService.GetSettingScopeAsync(ModuleState.ModuleId);
var editorSetting = scope == 1
? await EditorSettingService.LoadSettingsFromModuleAsync(ModuleState.ModuleId)
: await EditorSettingService.LoadSettingsFromSiteAsync(PageState.Site.SiteId);
_theme = editorSetting.Theme;
_background = editorSetting.Background;
_toolbarItems = editorSetting.ToolbarItems.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).ToList();
_toolbar = SetupToolbarItems();
}
private RenderFragment SetupToolbarItems()
{
return builder =>
{
var sequence = 0;
foreach (var item in _toolbarItems)
{
if (RadzenEditorDefinitions.ToolbarItems.ContainsKey(item))
{
sequence = RadzenEditorDefinitions.ToolbarItems[item](builder, sequence);
}
}
};
}
private async Task OnExecute(HtmlEditorExecuteEventArgs args)
{
if (args.CommandName == "InsertImage")
{
await InsertImage(args.Editor);
}
else if (args.CommandName == "Settings")
{
await UpdateSettings(args.Editor);
}
}
private async Task InsertImage(RadzenHtmlEditor editor)
{
await editor.SaveSelectionAsync();
var result = await DialogService.OpenAsync<FileManagerDialog>(Localizer["DialogTitle.SelectImage"], new Dictionary<string, object>
{
{ "Filters", PageState.Site.ImageFiles }
});
await editor.RestoreSelectionAsync();
if (result != null)
{
await editor.ExecuteCommandAsync(HtmlEditorCommands.InsertHtml, result);
}
}
private async Task UpdateSettings(RadzenHtmlEditor editor)
{
await editor.SaveSelectionAsync();
var result = await DialogService.OpenAsync<SettingsDialog>(Localizer["Settings"], null, new DialogOptions { Width = "650px" });
if (result == true)
{
NavigationManager.NavigateTo(NavigationManager.Uri);
}
await editor.RestoreSelectionAsync();
}
private async void OnDialogOpened(string title, Type componentType, Dictionary<string, object> parameters, DialogOptions options)
{
await _interop.UpdateDialogLayout(_editor.Element);
}
}

View File

@ -0,0 +1,85 @@
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.Extensions.Localization;
using System;
using System.Collections.Generic;
namespace Oqtane.Modules.Controls
{
public sealed class RadzenEditorDefinitions
{
public static IStringLocalizer<Oqtane.Modules.Controls.RadzenTextEditor> Localizer { get; internal set; }
public const string TransparentBackgroundColor = "rgba(0, 0, 0, 0)";
public const string LightBackgroundColor = "rgba(255, 255, 255, 1)";
public const string DarkBackgroundColor = "rgba(0, 0, 0, 1)";
public const string DefaultTheme = "default";
public const string DefaultBackground = "Default";
public static readonly IDictionary<string, Func<RenderTreeBuilder, int, int>> ToolbarItems = new Dictionary<string, Func<RenderTreeBuilder, int, int>>()
{
{ "AlignCenter", (builder, sequence) => CreateFragment(builder, sequence, "AlignCenter", "RadzenHtmlEditorAlignCenter") },
{ "AlignLeft", (builder, sequence) => CreateFragment(builder, sequence, "AlignLeft", "RadzenHtmlEditorAlignLeft") },
{ "AlignRight", (builder, sequence) => CreateFragment(builder, sequence, "AlignRight", "RadzenHtmlEditorAlignRight") },
{ "Background", (builder, sequence) => CreateFragment(builder, sequence, "Background", "RadzenHtmlEditorBackground") },
{ "Color", (builder, sequence) => CreateFragment(builder, sequence, "Color", "RadzenHtmlEditorColor") },
{ "FontName", (builder, sequence) => CreateFragment(builder, sequence, "FontName", "RadzenHtmlEditorFontName") },
{ "FontSize", (builder, sequence) => CreateFragment(builder, sequence, "FontSize", "RadzenHtmlEditorFontSize") },
{ "FormatBlock", (builder, sequence) => CreateFragment(builder, sequence, "FormatBlock", "RadzenHtmlEditorFormatBlock") },
{ "Indent", (builder, sequence) => CreateFragment(builder, sequence, "Indent", "RadzenHtmlEditorIndent") },
{ "InsertImage", (builder, sequence) => CreateFragment(builder, sequence, "InsertImage", "RadzenHtmlEditorCustomTool", "InsertImage", "image") },
{ "Italic", (builder, sequence) => CreateFragment(builder, sequence, "Italic", "RadzenHtmlEditorItalic") },
{ "Justify", (builder, sequence) => CreateFragment(builder, sequence, "Justify", "RadzenHtmlEditorJustify") },
{ "Link", (builder, sequence) => CreateFragment(builder, sequence, "Link", "RadzenHtmlEditorLink") },
{ "OrderedList", (builder, sequence) => CreateFragment(builder, sequence, "OrderedList", "RadzenHtmlEditorOrderedList") },
{ "Outdent", (builder, sequence) => CreateFragment(builder, sequence, "Outdent", "RadzenHtmlEditorOutdent") },
{ "Redo", (builder, sequence) => CreateFragment(builder, sequence, "Redo", "RadzenHtmlEditorRedo") },
{ "RemoveFormat", (builder, sequence) => CreateFragment(builder, sequence, "RemoveFormat", "RadzenHtmlEditorRemoveFormat") },
{ "Separator", (builder, sequence) => CreateFragment(builder, sequence, "Separator", "RadzenHtmlEditorSeparator") },
{ "Source", (builder, sequence) => CreateFragment(builder, sequence, "Source", "RadzenHtmlEditorSource") },
{ "StrikeThrough", (builder, sequence) => CreateFragment(builder, sequence, "StrikeThrough", "RadzenHtmlEditorStrikeThrough") },
{ "Subscript", (builder, sequence) => CreateFragment(builder, sequence, "Subscript", "RadzenHtmlEditorSubscript") },
{ "Superscript", (builder, sequence) => CreateFragment(builder, sequence, "Superscript", "RadzenHtmlEditorSuperscript") },
{ "Underline", (builder, sequence) => CreateFragment(builder, sequence, "Underline", "RadzenHtmlEditorUnderline") },
{ "Undo", (builder, sequence) => CreateFragment(builder, sequence, "Undo", "RadzenHtmlEditorUndo") },
{ "Unlink", (builder, sequence) => CreateFragment(builder, sequence, "Unlink", "RadzenHtmlEditorUnlink") },
{ "UnorderedList", (builder, sequence) => CreateFragment(builder, sequence, "UnorderedList", "RadzenHtmlEditorUnorderedList") },
};
public static readonly string DefaultToolbarItems = "Undo,Redo,Separator,FontName,FontSize,FormatBlock,Bold,Italic,Underline,StrikeThrough,Separator,AlignLeft,AlignCenter,AlignRight,Justify,Separator,Indent,Outdent,UnorderedList,OrderedList,Separator,Color,Background,RemoveFormat,Separator,Subscript,Superscript,Separator,Link,Unlink,InsertImage,Separator,Source";
private static int CreateFragment(RenderTreeBuilder builder, int sequence, string name, string typeName, string commaneName = "", string icon = "")
{
var fullTypeName = $"Radzen.Blazor.{typeName}, Radzen.Blazor";
var type = Type.GetType(fullTypeName);
if (type != null)
{
var title = Localizer[$"{name}.Title"];
var placeholder = Localizer[$"{name}.Placeholder"];
builder.OpenComponent(sequence++, type);
if (!string.IsNullOrEmpty(title) && title != $"{name}.Title" && type.GetProperty("Title") != null)
{
builder.AddAttribute(sequence++, "Title", title);
}
if (!string.IsNullOrEmpty(placeholder) && placeholder != $"{name}.Placeholder" && type.GetProperty("Placeholder") != null)
{
builder.AddAttribute(sequence++, "Placeholder", placeholder);
}
if (!string.IsNullOrEmpty(commaneName) && type.GetProperty("CommandName") != null)
{
builder.AddAttribute(sequence++, "CommandName", commaneName);
}
if (!string.IsNullOrEmpty(icon) && type.GetProperty("Icon") != null)
{
builder.AddAttribute(sequence++, "Icon", icon);
}
builder.CloseComponent();
}
return sequence;
}
}
}

View File

@ -0,0 +1,60 @@
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using System.Threading.Tasks;
namespace Oqtane.Modules.Controls
{
public class RadzenTextEditorInterop
{
private readonly IJSRuntime _jsRuntime;
public RadzenTextEditorInterop(IJSRuntime jsRuntime)
{
_jsRuntime = jsRuntime;
}
public Task Initialize(ElementReference editor)
{
try
{
_jsRuntime.InvokeVoidAsync("Oqtane.RadzenTextEditor.initialize", editor);
}
catch
{
}
return Task.CompletedTask;
}
public Task SetBackgroundColor(ElementReference editor, string color)
{
try
{
_jsRuntime.InvokeVoidAsync(
"Oqtane.RadzenTextEditor.setBackgroundColor",
editor, color);
}
catch
{
}
return Task.CompletedTask;
}
public Task UpdateDialogLayout(ElementReference editor)
{
try
{
_jsRuntime.InvokeVoidAsync("Oqtane.RadzenTextEditor.updateDialogLayout", editor);
}
catch
{
}
return Task.CompletedTask;
}
}
}

View File

@ -0,0 +1,11 @@
namespace Oqtane.Modules.Controls
{
public class RadzenEditorSetting
{
public string Theme { get; set; }
public string Background { get; set; }
public string ToolbarItems { get; set; }
}
}