Skip to content

Commit c926f58

Browse files
authored
feat(Vditor): add Vditor component (#6228)
* doc: 更新示例说明文档 * doc: 增加示例文档 * test: 增加单元测试 * doc: 增加菜单 * doc: 增加源码映射 * doc: 增加方法示例 * refactor: 更新 Pre 样式 * doc: 更新 Vditor 组件示例 * chore: 增加 Vditor 依赖
1 parent a3f4227 commit c926f58

File tree

9 files changed

+296
-3
lines changed

9 files changed

+296
-3
lines changed

src/BootstrapBlazor.Server/BootstrapBlazor.Server.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
<PackageReference Include="BootstrapBlazor.Topology" Version="9.0.0" />
7272
<PackageReference Include="BootstrapBlazor.UniverIcon" Version="9.0.1" />
7373
<PackageReference Include="BootstrapBlazor.UniverSheet" Version="9.0.5" />
74+
<PackageReference Include="BootstrapBlazor.Vditor" Version="9.0.0" />
7475
<PackageReference Include="BootstrapBlazor.VideoPlayer" Version="9.0.3" />
7576
<PackageReference Include="BootstrapBlazor.WinBox" Version="9.0.7" />
7677
<PackageReference Include="Longbow.Logging" Version="9.0.0" />

src/BootstrapBlazor.Server/Components/Samples/CherryMarkdowns.razor

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33

44
<h3>@Localizer["Header"]</h3>
55
<h4>@Localizer["Tip"]</h4>
6+
7+
<PackageTips Name="BootstrapBlazor.CherryMarkdown" />
8+
69
<p>@((MarkupString)Localizer["MarkdownsNote"].Value)</p>
710

811
<Pre class="no-highlight">builder.Services.Configure&lt;HubOptions&gt;(option => option.MaximumReceiveMessageSize = null);</Pre>
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
@page "/vditor"
2+
@inject IOptionsMonitor<WebsiteOptions> WebsiteOption
3+
4+
<h3>@Localizer["VditorTitle"]</h3>
5+
6+
<h4>@Localizer["VditorSubTitle"]</h4>
7+
8+
<PackageTips Name="BootstrapBlazor.Vditor" />
9+
10+
<p>@((MarkupString)Localizer["MarkdownsNote"].Value)</p>
11+
12+
<Pre class="no-highlight">builder.Services.Configure&lt;HubOptions&gt;(option => option.MaximumReceiveMessageSize = null);</Pre>
13+
14+
<DemoBlock Title="@Localizer["BaseUsageTitle"]" Introduction="@Localizer["BaseUsageIntro"]" Name="Normal">
15+
<section ignore class="row g-3">
16+
<div class="col-12">
17+
<BootstrapInputGroup>
18+
<BootstrapInputGroupLabel DisplayText="Mode"></BootstrapInputGroupLabel>
19+
<Segmented Value="_mode" OnValueChanged="OnModeChanged">
20+
@foreach (var item in Enum.GetValues(typeof(VditorMode)).Cast<VditorMode>())
21+
{
22+
<SegmentedItem Value="@item" Text="@item.ToString()" />
23+
}
24+
</Segmented>
25+
</BootstrapInputGroup>
26+
</div>
27+
<div class="col-12">
28+
<Button Text="GetValue" OnClick="OnTriggerGetValueAsync" IsDisabled="_isDisabled"></Button>
29+
<Button Text="InsertValue" OnClick="OnTriggerInsertValueAsync" IsDisabled="_isDisabled"></Button>
30+
<Button Text="GetHtml" OnClick="OnTriggerGetHtmlAsync" IsDisabled="_isDisabled"></Button>
31+
<Button Text="GetSelection" OnClick="OnTriggerGetSelectionAsync" IsDisabled="_isDisabled"></Button>
32+
<Button Text="Enable" OnClick="OnTriggerEnableAsync" IsDisabled="!_isDisabled"></Button>
33+
<Button Text="Disable" OnClick="OnTriggerDisableAsync" IsDisabled="_isDisabled"></Button>
34+
<Button Text="Focus" OnClick="OnTriggerFocusAsync" IsDisabled="_isDisabled"></Button>
35+
<Button Text="Blur" OnClick="OnTriggerBlurAsync" IsDisabled="_isDisabled"></Button>
36+
</div>
37+
</section>
38+
<Vditor Value="@_vditorValueString" Options="_vditorOptions" @ref="_vditor"
39+
OnRenderedAsync="OnRenderAsync"
40+
OnFocusAsync="OnFocusAsync" OnBlurAsync="OnBlurAsync"
41+
OnEscapeAsync="OnEscapeAsync" OnCtrlEnterAsync="OnCtrlEnterAsync"
42+
OnSelectAsync="OnSelectAsync" OnInputAsync="OnInputAsync"></Vditor>
43+
<section ignore class="mt-3">
44+
<p>
45+
<textarea class="form-control" rows="6" disabled="disabled">@_vditorValueString</textarea>
46+
</p>
47+
<p>
48+
<textarea class="form-control" rows="6" disabled="disabled"> @_htmlString</textarea>
49+
</p>
50+
<ConsoleLogger @ref="_logger"></ConsoleLogger>
51+
</section>
52+
</DemoBlock>
53+
54+
<AttributeTable Items="GetAttributes()" />
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the Apache 2.0 License
3+
// See the LICENSE file in the project root for more information.
4+
// Maintainer: Argo Zhang([email protected]) Website: https://www.blazor.zone
5+
6+
namespace BootstrapBlazor.Server.Components.Samples;
7+
8+
/// <summary>
9+
/// Vditors 组件示例代码
10+
/// </summary>
11+
public partial class Vditors
12+
{
13+
[Inject]
14+
[NotNull]
15+
private IStringLocalizer<Vditors>? Localizer { get; set; }
16+
17+
private VditorOptions _vditorOptions = new()
18+
{
19+
Height = "500px"
20+
};
21+
22+
private Vditor _vditor = default!;
23+
private string? _htmlString;
24+
private string _vditorValueString = "## 所见即所得(WYSIWYG)\n所见即所得模式对不熟悉 Markdown 的用户较为友好,熟悉 Markdown 的话也可以无缝使用。";
25+
private VditorMode _mode = VditorMode.WYSIWYG;
26+
private ConsoleLogger _logger = default!;
27+
28+
private async Task OnModeChanged(VditorMode mode)
29+
{
30+
_mode = mode;
31+
_vditorOptions.Mode = mode;
32+
if (mode == VditorMode.WYSIWYG)
33+
{
34+
_vditorValueString = "## 所见即所得(WYSIWYG)\n所见即所得模式对不熟悉 Markdown 的用户较为友好,熟悉 Markdown 的话也可以无缝使用。";
35+
}
36+
else if (mode == VditorMode.IR)
37+
{
38+
_vditorValueString = "## 即时渲染(IR)\n即时渲染模式对熟悉 Typora 的用户应该不会感到陌生,理论上这是最优雅的 Markdown 编辑方式。";
39+
}
40+
else if (mode == VditorMode.SV)
41+
{
42+
_vditorValueString = "## 分屏预览(SV)\n传统的分屏预览模式适合大屏下的 Markdown 编辑。";
43+
}
44+
45+
_htmlString = await _vditor.GetHtmlAsync();
46+
StateHasChanged();
47+
}
48+
49+
private Task OnRenderAsync()
50+
{
51+
_logger.Log($"Trigger OnRenderAsync");
52+
return Task.CompletedTask;
53+
}
54+
55+
private Task OnFocusAsync(string value)
56+
{
57+
_logger.Log($"Trigger OnFocusAsync");
58+
return Task.CompletedTask;
59+
}
60+
61+
private async Task OnBlurAsync(string value)
62+
{
63+
_vditorValueString = value;
64+
_logger.Log($"Trigger OnBlurAsync");
65+
66+
_htmlString = await _vditor.GetHtmlAsync();
67+
StateHasChanged();
68+
}
69+
70+
private Task OnEscapeAsync(string value)
71+
{
72+
_logger.Log($"Trigger OnEscapeAsync");
73+
return Task.CompletedTask;
74+
}
75+
76+
private Task OnSelectAsync(string value)
77+
{
78+
_logger.Log($"Trigger OnSelectAsync");
79+
return Task.CompletedTask;
80+
}
81+
82+
private async Task OnInputAsync(string value)
83+
{
84+
_vditorValueString = value;
85+
_htmlString = await _vditor.GetHtmlAsync();
86+
87+
_logger.Log($"Trigger OnInputAsync");
88+
StateHasChanged();
89+
}
90+
91+
private async Task OnCtrlEnterAsync(string value)
92+
{
93+
_vditorValueString = value;
94+
_htmlString = await _vditor.GetHtmlAsync();
95+
96+
_logger.Log($"Trigger OnCtrlEnterAsync");
97+
StateHasChanged();
98+
}
99+
100+
private async Task OnTriggerGetValueAsync()
101+
{
102+
_vditorValueString = await _vditor.GetValueAsync() ?? "";
103+
}
104+
105+
private async Task OnTriggerInsertValueAsync()
106+
{
107+
await _vditor.InsertValueAsync("光标处插入当前值");
108+
}
109+
110+
private async Task OnTriggerGetHtmlAsync()
111+
{
112+
_htmlString = await _vditor.GetHtmlAsync();
113+
}
114+
115+
private async Task OnTriggerGetSelectionAsync()
116+
{
117+
var selection = await _vditor.GetSelectionAsync() ?? "";
118+
_logger.Log($"Trigger OnTriggerGetSelectionAsync: {selection}");
119+
}
120+
121+
private bool _isDisabled = false;
122+
private async Task OnTriggerEnableAsync()
123+
{
124+
await _vditor.EnableAsync();
125+
_isDisabled = false;
126+
}
127+
128+
private async Task OnTriggerDisableAsync()
129+
{
130+
await _vditor.DisableAsync();
131+
_isDisabled = true;
132+
}
133+
134+
private async Task OnTriggerFocusAsync()
135+
{
136+
await _vditor.FocusAsync();
137+
}
138+
139+
private async Task OnTriggerBlurAsync()
140+
{
141+
await _vditor.BlurAsync();
142+
}
143+
144+
private static AttributeItem[] GetAttributes() =>
145+
[
146+
new()
147+
{
148+
Name = "EditorSettings",
149+
Description = "编辑器设置",
150+
Type = "EditorSettings",
151+
ValueList = " — ",
152+
DefaultValue = " — "
153+
},
154+
new()
155+
{
156+
Name = "ToolbarSettings",
157+
Description = "工具栏设置",
158+
Type = "ToolbarSettings",
159+
ValueList = " — ",
160+
DefaultValue = " — "
161+
},
162+
new()
163+
{
164+
Name = "Value",
165+
Description = "组件值",
166+
Type = "string",
167+
ValueList = " — ",
168+
DefaultValue = " — "
169+
},
170+
new()
171+
{
172+
Name = "Html",
173+
Description = "组件 Html 代码",
174+
Type = "string",
175+
ValueList = " — ",
176+
DefaultValue = " — "
177+
},
178+
new()
179+
{
180+
Name = "OnFileUpload",
181+
Description = "文件上传回调方法",
182+
Type = "Func<CherryMarkdownUploadFile, Task<string>>",
183+
ValueList = " — ",
184+
DefaultValue = " — "
185+
},
186+
new()
187+
{
188+
Name = "IsViewer",
189+
Description = "组件是否为浏览器模式",
190+
Type = "bool",
191+
ValueList = "true/false",
192+
DefaultValue = "false"
193+
}
194+
];
195+
}

src/BootstrapBlazor.Server/Extensions/MenusLocalizerExtensions.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,12 @@ void AddForm(DemoMenuItem item)
516516
{
517517
Text = Localizer["ValidateForm"],
518518
Url = "validate-form"
519+
},
520+
new()
521+
{
522+
IsNew = true,
523+
Text = Localizer["Vditor"],
524+
Url = "vditor"
519525
}
520526
};
521527
AddBadge(item);

src/BootstrapBlazor.Server/Locales/en-US.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4948,7 +4948,8 @@
49484948
"ButtonUpload": "ButtonUpload",
49494949
"AvatarUpload": "AvatarUpload",
49504950
"CardUpload": "CardUpload",
4951-
"DropUpload": "DropUpload"
4951+
"DropUpload": "DropUpload",
4952+
"Vditor": "Vditor Markdown"
49524953
},
49534954
"BootstrapBlazor.Server.Components.Samples.Table.TablesHeader": {
49544955
"TablesHeaderTitle": "Header grouping function",
@@ -7190,5 +7191,11 @@
71907191
"AudioDevicePauseText": "Pause",
71917192
"AudioDeviceResumeText": "Resume",
71927193
"AudioDeviceDownloadText": "Download"
7194+
},
7195+
"BootstrapBlazor.Server.Components.Samples.Vditors": {
7196+
"VditorTitle": "Vditor Markdown",
7197+
"VditorSubTitle": "Vditor is a browser-based Markdown editor that supports WYSIWYG, instant rendering (similar to Typora), and split-screen preview mode.",
7198+
"BaseUsageTitle": "Basic usage",
7199+
"BaseUsageIntro": "Set the content displayed by the component by setting the <code>Value</code> value, and set the component configuration information by setting the <code>Options</code> parameter"
71937200
}
71947201
}

src/BootstrapBlazor.Server/Locales/zh-CN.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4948,7 +4948,8 @@
49484948
"ButtonUpload": "按钮上传组件 ButtonUpload",
49494949
"AvatarUpload": "头像上传组件 AvatarUpload",
49504950
"CardUpload": "卡片上传组件 CardUpload",
4951-
"DropUpload": "拖动上传组件 DropUpload"
4951+
"DropUpload": "拖动上传组件 DropUpload",
4952+
"Vditor": "富文本框 Vditor Markdown"
49524953
},
49534954
"BootstrapBlazor.Server.Components.Samples.Table.TablesHeader": {
49544955
"TablesHeaderTitle": "表头分组功能",
@@ -7190,5 +7191,11 @@
71907191
"AudioDevicePauseText": "暂停",
71917192
"AudioDeviceResumeText": "恢复",
71927193
"AudioDeviceDownloadText": "下载"
7194+
},
7195+
"BootstrapBlazor.Server.Components.Samples.Vditors": {
7196+
"VditorTitle": "Vditor Markdown 富文本编辑框",
7197+
"VditorSubTitle": "Vditor 是一款浏览器端的 Markdown 编辑器,支持所见即所得、即时渲染(类似 Typora)和分屏预览模式",
7198+
"BaseUsageTitle": "基本用法",
7199+
"BaseUsageIntro": "通过设置 <code>Value</code> 值设置组件显示的内容,通过 <code>Options</code> 参数设置组件配置信息"
71937200
}
71947201
}

src/BootstrapBlazor.Server/docs.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,8 @@
239239
"video-device": "VideoDevices",
240240
"audio-device": "AudioDevices",
241241
"fullscreen-button": "FullScreenButtons",
242-
"meet": "Meets"
242+
"meet": "Meets",
243+
"vditor": "Vditors"
243244
},
244245
"video": {
245246
"table": "BV1ap4y1x7Qn?p=1",

test/UnitTest/Performance/RecordTest.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,25 @@ public void With_Ok()
2323
Assert.NotEqual(foo3, foo2);
2424
}
2525

26+
[Fact]
27+
public void Height_Ok()
28+
{
29+
var dummy1 = new Dummy { Height = 100 };
30+
var dummy2 = new Dummy { Height = 100 };
31+
32+
Assert.Equal(dummy1, dummy2);
33+
34+
dummy2.Width = 100;
35+
Assert.NotEqual(dummy1, dummy2);
36+
}
37+
38+
struct Dummy
39+
{
40+
public int Height { get; set; }
41+
42+
public int Width { get; set; }
43+
}
44+
2645
record Foo
2746
{
2847
public Foo(string name) { Name = name; }

0 commit comments

Comments
 (0)