!3640 doc(#I672IC): update DemoBlock component support get code snippet from Demo component

* feat: 基础组件 DemoBlock/Pre 支持获取代码片段新方式
* feat: 实现通过 Demo 参数获取源码逻辑
* feat: 增加 Converter 负责讲配置名字转化为 Type
This commit is contained in:
Argo 2022-12-22 05:30:41 +00:00
parent e64f940f90
commit a5f6173a0d
7 changed files with 142 additions and 16 deletions

View File

@ -8,13 +8,13 @@
<div class="card">
<div class="card-body">
@ChildContent
@RenderChildContent
</div>
@if (ShowCode)
{
<div class="card-footer">
<div class="card-footer-code collapse" id="@Id">
<Pre @key="@RazorFileName" CodeFile="@RazorFileName" BlockTitle="@BlockTitle"></Pre>
<Pre @key="@RazorFileName" CodeFile="@RazorFileName" BlockTitle="@BlockTitle" Demo="@Demo"></Pre>
</div>
<a class="card-footer-control collapsed" href="#@Id" data-bs-toggle="collapse" role="button" aria-label="show code">
<i class="fa-solid fa-caret-up"></i>

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/
using BootstrapBlazor.Shared.Services;
using Microsoft.JSInterop;
namespace BootstrapBlazor.Shared.Components;
@ -36,6 +37,12 @@ public sealed partial class DemoBlock
[Parameter]
public RenderFragment? ChildContent { get; set; }
/// <summary>
/// 获得/设置 示例代码片段 默认 null 未设置
/// </summary>
[Parameter]
public string? Demo { get; set; }
/// <summary>
/// 获得/设置 是否显示代码块 默认 true 显示
/// </summary>
@ -52,6 +59,10 @@ public sealed partial class DemoBlock
[NotNull]
private IStringLocalizer<DemoBlock>? Localizer { get; set; }
[Inject]
[NotNull]
private DemoComponentConverter? ComponentConverter { get; set; }
/// <summary>
/// 获得/设置 友好链接锚点名称
/// </summary>
@ -70,4 +81,15 @@ public sealed partial class DemoBlock
Title ??= Localizer[nameof(Title)];
TooltipText ??= Localizer[nameof(TooltipText)];
}
private RenderFragment RenderChildContent => builder =>
{
builder.AddContent(0, ChildContent);
if (ComponentConverter.TryParse(Demo, out var t))
{
builder.OpenComponent(1, t);
builder.CloseComponent();
}
};
}

View File

@ -46,6 +46,12 @@ public partial class Pre
[Parameter]
public string? BlockTitle { get; set; }
/// <summary>
/// 获得/设置 示例代码片段 默认 null 未设置
/// </summary>
[Parameter]
public string? Demo { get; set; }
[Inject]
[NotNull]
private IStringLocalizer<Pre>? Localizer { get; set; }
@ -103,9 +109,10 @@ public partial class Pre
private async Task GetCodeAsync()
{
// 优先查找 Demo 值
if (!string.IsNullOrEmpty(CodeFile))
{
var code = await Example.GetCodeAsync(CodeFile, BlockTitle);
var code = await Example.GetCodeAsync(CodeFile, BlockTitle, Demo);
if (!string.IsNullOrEmpty(code))
{
ChildContent = builder =>

View File

@ -31,6 +31,22 @@ internal static class CacheManagerExtensions
});
}
/// <summary>
/// 获得 指定代码文件当前文化设置的本地化资源集合
/// </summary>
/// <param name="cache"></param>
/// <param name="codeFile"></param>
/// <param name="options"></param>
/// <returns></returns>
public static IEnumerable<LocalizedString> GetDemoLocalizedStrings(this ICacheManager cache, string codeFile, JsonLocalizationOptions options)
{
var key = $"Snippet-{CultureInfo.CurrentUICulture.Name}-{nameof(GetLocalizedStrings)}-{codeFile}";
return cache.GetOrCreate(key, entry =>
{
return Utility.GetJsonStringByTypeName(options, typeof(CodeSnippetService).Assembly, $"BootstrapBlazor.Shared.{codeFile}");
});
}
/// <summary>
/// 获得 指定代码文件内当前代码块当前文化的代码片段
/// </summary>
@ -50,4 +66,10 @@ internal static class CacheManagerExtensions
var key = $"Snippet-{CultureInfo.CurrentUICulture.Name}-{nameof(GetContentFromFileAsync)}-{codeFile}";
return cache.GetOrCreateAsync(key, entry => factory(entry));
}
public static Task<string> GetContentFromDemoAsync(this ICacheManager cache, string demo, Func<ICacheEntry, Task<string>> factory)
{
var key = $"{nameof(GetContentFromDemoAsync)}-Demos-{demo}";
return cache.GetOrCreateAsync(key, entry => factory(entry));
}
}

View File

@ -25,6 +25,10 @@ public static class ServicesExtensions
services.AddSingleton(typeof(IDataService<>), typeof(TableDemoDataService<>));
services.AddSingleton(typeof(ILookupService), typeof(DemoLookupService));
services.AddSingleton<MockDataTableDynamicService>();
// 增加代码示例服务
services.AddSingleton<DemoComponentConverter>();
services.AddSingleton<MenuService>();
services.AddScoped<FanControllerDataService>();

View File

@ -51,12 +51,12 @@ class CodeSnippetService
/// 获得示例源码方法
/// </summary>
/// <returns></returns>
public async Task<string> GetCodeAsync(string codeFile, string? blockTitle)
public async Task<string> GetCodeAsync(string codeFile, string? blockTitle, string? demo)
{
var content = "";
try
{
var payload = await GetContentFromFile(codeFile);
var payload = await GetContentFromDemo(demo) ?? await GetContentFromFile(codeFile);
if (blockTitle != null)
{
@ -136,6 +136,35 @@ class CodeSnippetService
}
}
private async Task<string?> GetContentFromDemo(string? demo) => string.IsNullOrEmpty(demo)
? null
: await CacheManager.GetContentFromDemoAsync(demo, async entry =>
{
var payload = "";
if (IsDevelopment)
{
payload = await ReadDemoTextAsync(demo);
}
else
{
if (OperatingSystem.IsBrowser())
{
Client.BaseAddress = new Uri($"{ServerUrl}/api/");
payload = await Client.GetStringAsync($"Code?fileName={demo}");
}
else
{
payload = await Client.GetStringAsync(demo);
}
}
// 将资源文件信息替换
CacheManager.GetDemoLocalizedStrings(demo, LocalizerOptions).ToList().ForEach(l => payload = ReplacePayload(payload, l));
payload = ReplaceSymbols(payload);
return payload;
});
private Task<string> GetContentFromFile(string codeFile) => CacheManager.GetContentFromFileAsync(codeFile, async entry =>
{
var payload = "";
@ -159,21 +188,22 @@ class CodeSnippetService
if (Path.GetExtension(codeFile) == ".razor")
{
// 将资源文件信息替换
CacheManager.GetLocalizedStrings(codeFile, LocalizerOptions).ToList().ForEach(ReplacePayload);
payload = payload.Replace("@@", "@")
.Replace("&lt;", "<")
.Replace("&gt;", ">");
CacheManager.GetLocalizedStrings(codeFile, LocalizerOptions).ToList().ForEach(l => payload = ReplacePayload(payload, l));
payload = ReplaceSymbols(payload);
}
return payload;
void ReplacePayload(LocalizedString l)
{
payload = payload.Replace($"@(((MarkupString)Localizer[\"{l.Name}\"].Value).ToString())", l.Value)
.Replace($"@((MarkupString)Localizer[\"{l.Name}\"].Value)", l.Value)
.Replace($"@Localizer[\"{l.Name}\"]", l.Value);
}
});
private static string ReplaceSymbols(string payload) => payload
.Replace("@@", "@")
.Replace("&lt;", "<")
.Replace("&gt;", ">");
private static string ReplacePayload(string payload, LocalizedString l) => payload
.Replace($"@(((MarkupString)Localizer[\"{l.Name}\"].Value).ToString())", l.Value)
.Replace($"@((MarkupString)Localizer[\"{l.Name}\"].Value)", l.Value)
.Replace($"@Localizer[\"{l.Name}\"]", l.Value);
private async Task<string> ReadFileTextAsync(string codeFile)
{
var payload = "";
@ -186,4 +216,18 @@ class CodeSnippetService
}
return payload;
}
private async Task<string> ReadDemoTextAsync(string codeFile)
{
var payload = "";
var paths = new string[] { "..", "BootstrapBlazor.Shared", "Demos" };
var folder = Path.Combine(ContentRootPath, string.Join(Path.DirectorySeparatorChar, paths));
var file = Path.Combine(folder, codeFile.Replace('.', Path.DirectorySeparatorChar));
file = $"{file}.razor";
if (File.Exists(file))
{
payload = await File.ReadAllTextAsync(file);
}
return payload;
}
}

View File

@ -0,0 +1,27 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/
namespace BootstrapBlazor.Shared.Services;
internal class DemoComponentConverter
{
private static Dictionary<string, Type>? Cache { get; set; }
public bool TryParse(string? name, [NotNullWhen(true)] out Type? type)
{
type = null;
name = name?.Split('.').LastOrDefault();
if (!string.IsNullOrEmpty(name))
{
Cache ??= BuildDemoTable();
if (!string.IsNullOrEmpty(name) && Cache.ContainsKey(name))
{
type = Cache[name];
}
}
return type != null;
}
private Dictionary<string, Type> BuildDemoTable() => GetType().Assembly.GetExportedTypes().Where(i => i.Namespace?.Contains(".Demos.") ?? false).ToDictionary(i => i.Name);
}