diff --git a/src/BootstrapBlazor.Server/BootstrapBlazor.Server.csproj b/src/BootstrapBlazor.Server/BootstrapBlazor.Server.csproj index ce9d5f0920e..e2390bb028e 100644 --- a/src/BootstrapBlazor.Server/BootstrapBlazor.Server.csproj +++ b/src/BootstrapBlazor.Server/BootstrapBlazor.Server.csproj @@ -71,6 +71,7 @@ + diff --git a/src/BootstrapBlazor.Server/Components/Samples/CherryMarkdowns.razor b/src/BootstrapBlazor.Server/Components/Samples/CherryMarkdowns.razor index 9c7a9eaaf80..efe9fb25705 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/CherryMarkdowns.razor +++ b/src/BootstrapBlazor.Server/Components/Samples/CherryMarkdowns.razor @@ -3,6 +3,9 @@

@Localizer["Header"]

@Localizer["Tip"]

+ + +

@((MarkupString)Localizer["MarkdownsNote"].Value)

builder.Services.Configure<HubOptions>(option => option.MaximumReceiveMessageSize = null);
diff --git a/src/BootstrapBlazor.Server/Components/Samples/Vditors.razor b/src/BootstrapBlazor.Server/Components/Samples/Vditors.razor new file mode 100644 index 00000000000..f975d959e4c --- /dev/null +++ b/src/BootstrapBlazor.Server/Components/Samples/Vditors.razor @@ -0,0 +1,54 @@ +@page "/vditor" +@inject IOptionsMonitor WebsiteOption + +

@Localizer["VditorTitle"]

+ +

@Localizer["VditorSubTitle"]

+ + + +

@((MarkupString)Localizer["MarkdownsNote"].Value)

+ +
builder.Services.Configure<HubOptions>(option => option.MaximumReceiveMessageSize = null);
+ + +
+
+ + + + @foreach (var item in Enum.GetValues(typeof(VditorMode)).Cast()) + { + + } + + +
+
+ + + + + + + + +
+
+ +
+

+ +

+

+ +

+ +
+
+ + diff --git a/src/BootstrapBlazor.Server/Components/Samples/Vditors.razor.cs b/src/BootstrapBlazor.Server/Components/Samples/Vditors.razor.cs new file mode 100644 index 00000000000..70f0c4d0545 --- /dev/null +++ b/src/BootstrapBlazor.Server/Components/Samples/Vditors.razor.cs @@ -0,0 +1,195 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the Apache 2.0 License +// See the LICENSE file in the project root for more information. +// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone + +namespace BootstrapBlazor.Server.Components.Samples; + +/// +/// Vditors 组件示例代码 +/// +public partial class Vditors +{ + [Inject] + [NotNull] + private IStringLocalizer? Localizer { get; set; } + + private VditorOptions _vditorOptions = new() + { + Height = "500px" + }; + + private Vditor _vditor = default!; + private string? _htmlString; + private string _vditorValueString = "## 所见即所得(WYSIWYG)\n所见即所得模式对不熟悉 Markdown 的用户较为友好,熟悉 Markdown 的话也可以无缝使用。"; + private VditorMode _mode = VditorMode.WYSIWYG; + private ConsoleLogger _logger = default!; + + private async Task OnModeChanged(VditorMode mode) + { + _mode = mode; + _vditorOptions.Mode = mode; + if (mode == VditorMode.WYSIWYG) + { + _vditorValueString = "## 所见即所得(WYSIWYG)\n所见即所得模式对不熟悉 Markdown 的用户较为友好,熟悉 Markdown 的话也可以无缝使用。"; + } + else if (mode == VditorMode.IR) + { + _vditorValueString = "## 即时渲染(IR)\n即时渲染模式对熟悉 Typora 的用户应该不会感到陌生,理论上这是最优雅的 Markdown 编辑方式。"; + } + else if (mode == VditorMode.SV) + { + _vditorValueString = "## 分屏预览(SV)\n传统的分屏预览模式适合大屏下的 Markdown 编辑。"; + } + + _htmlString = await _vditor.GetHtmlAsync(); + StateHasChanged(); + } + + private Task OnRenderAsync() + { + _logger.Log($"Trigger OnRenderAsync"); + return Task.CompletedTask; + } + + private Task OnFocusAsync(string value) + { + _logger.Log($"Trigger OnFocusAsync"); + return Task.CompletedTask; + } + + private async Task OnBlurAsync(string value) + { + _vditorValueString = value; + _logger.Log($"Trigger OnBlurAsync"); + + _htmlString = await _vditor.GetHtmlAsync(); + StateHasChanged(); + } + + private Task OnEscapeAsync(string value) + { + _logger.Log($"Trigger OnEscapeAsync"); + return Task.CompletedTask; + } + + private Task OnSelectAsync(string value) + { + _logger.Log($"Trigger OnSelectAsync"); + return Task.CompletedTask; + } + + private async Task OnInputAsync(string value) + { + _vditorValueString = value; + _htmlString = await _vditor.GetHtmlAsync(); + + _logger.Log($"Trigger OnInputAsync"); + StateHasChanged(); + } + + private async Task OnCtrlEnterAsync(string value) + { + _vditorValueString = value; + _htmlString = await _vditor.GetHtmlAsync(); + + _logger.Log($"Trigger OnCtrlEnterAsync"); + StateHasChanged(); + } + + private async Task OnTriggerGetValueAsync() + { + _vditorValueString = await _vditor.GetValueAsync() ?? ""; + } + + private async Task OnTriggerInsertValueAsync() + { + await _vditor.InsertValueAsync("光标处插入当前值"); + } + + private async Task OnTriggerGetHtmlAsync() + { + _htmlString = await _vditor.GetHtmlAsync(); + } + + private async Task OnTriggerGetSelectionAsync() + { + var selection = await _vditor.GetSelectionAsync() ?? ""; + _logger.Log($"Trigger OnTriggerGetSelectionAsync: {selection}"); + } + + private bool _isDisabled = false; + private async Task OnTriggerEnableAsync() + { + await _vditor.EnableAsync(); + _isDisabled = false; + } + + private async Task OnTriggerDisableAsync() + { + await _vditor.DisableAsync(); + _isDisabled = true; + } + + private async Task OnTriggerFocusAsync() + { + await _vditor.FocusAsync(); + } + + private async Task OnTriggerBlurAsync() + { + await _vditor.BlurAsync(); + } + + private static AttributeItem[] GetAttributes() => + [ + new() + { + Name = "EditorSettings", + Description = "编辑器设置", + Type = "EditorSettings", + ValueList = " — ", + DefaultValue = " — " + }, + new() + { + Name = "ToolbarSettings", + Description = "工具栏设置", + Type = "ToolbarSettings", + ValueList = " — ", + DefaultValue = " — " + }, + new() + { + Name = "Value", + Description = "组件值", + Type = "string", + ValueList = " — ", + DefaultValue = " — " + }, + new() + { + Name = "Html", + Description = "组件 Html 代码", + Type = "string", + ValueList = " — ", + DefaultValue = " — " + }, + new() + { + Name = "OnFileUpload", + Description = "文件上传回调方法", + Type = "Func>", + ValueList = " — ", + DefaultValue = " — " + }, + new() + { + Name = "IsViewer", + Description = "组件是否为浏览器模式", + Type = "bool", + ValueList = "true/false", + DefaultValue = "false" + } + ]; +} diff --git a/src/BootstrapBlazor.Server/Extensions/MenusLocalizerExtensions.cs b/src/BootstrapBlazor.Server/Extensions/MenusLocalizerExtensions.cs index 54f284ce7d1..f4d62ae880c 100644 --- a/src/BootstrapBlazor.Server/Extensions/MenusLocalizerExtensions.cs +++ b/src/BootstrapBlazor.Server/Extensions/MenusLocalizerExtensions.cs @@ -516,6 +516,12 @@ void AddForm(DemoMenuItem item) { Text = Localizer["ValidateForm"], Url = "validate-form" + }, + new() + { + IsNew = true, + Text = Localizer["Vditor"], + Url = "vditor" } }; AddBadge(item); diff --git a/src/BootstrapBlazor.Server/Locales/en-US.json b/src/BootstrapBlazor.Server/Locales/en-US.json index 5e7e872e03f..84c5e7d3094 100644 --- a/src/BootstrapBlazor.Server/Locales/en-US.json +++ b/src/BootstrapBlazor.Server/Locales/en-US.json @@ -4948,7 +4948,8 @@ "ButtonUpload": "ButtonUpload", "AvatarUpload": "AvatarUpload", "CardUpload": "CardUpload", - "DropUpload": "DropUpload" + "DropUpload": "DropUpload", + "Vditor": "Vditor Markdown" }, "BootstrapBlazor.Server.Components.Samples.Table.TablesHeader": { "TablesHeaderTitle": "Header grouping function", @@ -7190,5 +7191,11 @@ "AudioDevicePauseText": "Pause", "AudioDeviceResumeText": "Resume", "AudioDeviceDownloadText": "Download" + }, + "BootstrapBlazor.Server.Components.Samples.Vditors": { + "VditorTitle": "Vditor Markdown", + "VditorSubTitle": "Vditor is a browser-based Markdown editor that supports WYSIWYG, instant rendering (similar to Typora), and split-screen preview mode.", + "BaseUsageTitle": "Basic usage", + "BaseUsageIntro": "Set the content displayed by the component by setting the Value value, and set the component configuration information by setting the Options parameter" } } diff --git a/src/BootstrapBlazor.Server/Locales/zh-CN.json b/src/BootstrapBlazor.Server/Locales/zh-CN.json index bfe6a152588..ce049b4a6c7 100644 --- a/src/BootstrapBlazor.Server/Locales/zh-CN.json +++ b/src/BootstrapBlazor.Server/Locales/zh-CN.json @@ -4948,7 +4948,8 @@ "ButtonUpload": "按钮上传组件 ButtonUpload", "AvatarUpload": "头像上传组件 AvatarUpload", "CardUpload": "卡片上传组件 CardUpload", - "DropUpload": "拖动上传组件 DropUpload" + "DropUpload": "拖动上传组件 DropUpload", + "Vditor": "富文本框 Vditor Markdown" }, "BootstrapBlazor.Server.Components.Samples.Table.TablesHeader": { "TablesHeaderTitle": "表头分组功能", @@ -7190,5 +7191,11 @@ "AudioDevicePauseText": "暂停", "AudioDeviceResumeText": "恢复", "AudioDeviceDownloadText": "下载" + }, + "BootstrapBlazor.Server.Components.Samples.Vditors": { + "VditorTitle": "Vditor Markdown 富文本编辑框", + "VditorSubTitle": "Vditor 是一款浏览器端的 Markdown 编辑器,支持所见即所得、即时渲染(类似 Typora)和分屏预览模式", + "BaseUsageTitle": "基本用法", + "BaseUsageIntro": "通过设置 Value 值设置组件显示的内容,通过 Options 参数设置组件配置信息" } } diff --git a/src/BootstrapBlazor.Server/docs.json b/src/BootstrapBlazor.Server/docs.json index 183ef5c10ae..118f42ad254 100644 --- a/src/BootstrapBlazor.Server/docs.json +++ b/src/BootstrapBlazor.Server/docs.json @@ -239,7 +239,8 @@ "video-device": "VideoDevices", "audio-device": "AudioDevices", "fullscreen-button": "FullScreenButtons", - "meet": "Meets" + "meet": "Meets", + "vditor": "Vditors" }, "video": { "table": "BV1ap4y1x7Qn?p=1", diff --git a/test/UnitTest/Performance/RecordTest.cs b/test/UnitTest/Performance/RecordTest.cs index 609b091aaf1..831aa175c8a 100644 --- a/test/UnitTest/Performance/RecordTest.cs +++ b/test/UnitTest/Performance/RecordTest.cs @@ -23,6 +23,25 @@ public void With_Ok() Assert.NotEqual(foo3, foo2); } + [Fact] + public void Height_Ok() + { + var dummy1 = new Dummy { Height = 100 }; + var dummy2 = new Dummy { Height = 100 }; + + Assert.Equal(dummy1, dummy2); + + dummy2.Width = 100; + Assert.NotEqual(dummy1, dummy2); + } + + struct Dummy + { + public int Height { get; set; } + + public int Width { get; set; } + } + record Foo { public Foo(string name) { Name = name; }