Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add MarkdownViewer extra component (#10111) #10211

Merged
Merged
3 changes: 3 additions & 0 deletions src/BlazorUI/Bit.BlazorUI.Extras/Bit.BlazorUI.Extras.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
<PackageReference Include="Newtonsoft.Json" Version="13.0.3">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Jint" Version="4.2.0">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Condition="'$(TargetFramework)' == 'net8.0'" Include="Microsoft.AspNetCore.Components.Web" Version="8.0.0" />
<PackageReference Condition="'$(TargetFramework)' == 'net9.0'" Include="Microsoft.AspNetCore.Components.Web" Version="9.0.0" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender)
scripts.AddRange(DateAdapterScripts);
}

await _js.BitChartJsInitChartJs(scripts);
await _js.BitExtrasInitScripts(scripts);

if (Config is not null)
{
Expand Down
33 changes: 0 additions & 33 deletions src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ namespace BitBlazorUI {
}

export class BitChart {
private static _initPromise?: Promise<unknown>;
private static _bitCharts = new Map<string, Chart>();

public static getChartJs(canvasId: string) {
Expand All @@ -31,38 +30,6 @@ namespace BitBlazorUI {
return BitChart._bitCharts.get(canvasId)!;
}

public static async initChartJs(scripts: string[]) {
if (BitChart._initPromise) {
await BitChart._initPromise;
}

const allScripts = Array.from(document.scripts).map(s => s.src);
const notAppenedScripts = scripts.filter(s => !allScripts.find(as => as.endsWith(s)));

if (notAppenedScripts.length == 0) return Promise.resolve();

const promise = new Promise(async (resolve: any, reject: any) => {
try {
for (let url of notAppenedScripts) await addScript(url);
resolve();
} catch (e: any) {
reject(e);
}
});
BitChart._initPromise = promise;
return promise;

async function addScript(url: string) {
return new Promise((res, rej) => {
const script = document.createElement('script');
script.src = url;
script.onload = res;
script.onerror = rej;
document.body.appendChild(script);
})
}
}

public static removeChart(canvasId: string) {
if (!BitChart._bitCharts.has(canvasId)) return;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,6 @@ internal static class BitChartJsInterop
Converters = { new IsoDateTimeConverter() }
};

public static ValueTask BitChartJsInitChartJs(this IJSRuntime jsRuntime, IEnumerable<string> scripts)
{
return jsRuntime.InvokeVoid("BitBlazorUI.BitChart.initChartJs", scripts);
}

public static ValueTask BitChartJsRemoveChart(this IJSRuntime jsRuntime, string? canvasId)
{
return jsRuntime.InvokeVoid("BitBlazorUI.BitChart.removeChart", canvasId);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@namespace Bit.BlazorUI
@inherits BitComponentBase

<div @ref="RootElement"
@attributes="HtmlAttributes"
id="@_Id"
style="@StyleBuilder.Value"
class="@ClassBuilder.Value"
dir="@Dir?.ToString().ToLower()">
@((MarkupString)(_html ?? ""))
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
using Jint;

namespace Bit.BlazorUI;

/// <summary>
/// BitMarkdownViewer is a Blazor wrapper around the famous markedjs library.
/// <see href="https://github.com/markedjs/marked"/>
/// </summary>
public partial class BitMarkdownViewer : BitComponentBase
{
private static string _ScriptPath = "_content/Bit.BlazorUI.Extras/marked/marked-15.0.7.js";



private string? _html;
private CancellationTokenSource _cts = new();



[Inject] private IJSRuntime _js { get; set; } = default!;



/// <summary>
/// The Markdown string value to render as an html element.
/// </summary>
[Parameter, CallOnSet(nameof(OnMarkdownSet))]
public string? Markdown { get; set; }



protected override string RootElementClass => "bit-mdv";

protected override async Task OnInitializedAsync()
{
if (_js.IsRuntimeInvalid()) // prerendering
{
try
{
await RunJint();
}
catch (Exception ex)
{
System.Console.Error.WriteLine(ex.ToString());
}
}
else if (OperatingSystem.IsBrowser())
{
if (await _js.BitMarkdownViewerCheckScript(_ScriptPath))
{
await ParseAndRender();
}
}

await base.OnInitializedAsync();
}

protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);

if (firstRender)
{
if (await _js.BitMarkdownViewerCheckScript(_ScriptPath) is false)
{
await _js.BitExtrasInitScripts([_ScriptPath]);
}

if (_html.HasNoValue())
{
await ParseAndRender();
}
}
}


private async Task RunJint()
{
if (Markdown.HasNoValue()) return;

await Task.Run(async () =>
{
var scriptPath = Path.Combine(Environment.CurrentDirectory, "..", "..", "Bit.BlazorUI.Extras", "wwwroot", "marked", "marked-15.0.7.js");
var script = await File.ReadAllTextAsync(scriptPath);

var engine = new Engine(options =>
{
options.CancellationToken(_cts.Token);
}).Execute(script);

var fn = engine.Evaluate("marked.parse").AsFunctionInstance();

_html = fn.Call(Markdown).AsString();

await InvokeAsync(StateHasChanged);
}, _cts.Token);
}

private async Task OnMarkdownSet()
{
if (IsRendered is false) return;

await ParseAndRender();
}

private async Task ParseAndRender()
{
if (Markdown.HasNoValue()) return;

_html = await _js.BitMarkdownViewerParse(Markdown!);

StateHasChanged();
}



protected override async ValueTask DisposeAsync(bool disposing)
{
if (IsDisposed || disposing is false) return;

_cts.Cancel();
_cts.Dispose();

await base.DisposeAsync(disposing);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@import '../../../Bit.BlazorUI/Styles/functions.scss';

.bit-mdv {
all: revert;

* {
all: revert;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace BitBlazorUI {
export class MarkdownViewer {
private static _initPromise?: Promise<unknown>;

public static checkScript(script: string) {
const allScripts = Array.from(document.scripts).map(s => s.src);
return !!allScripts.find(as => as.includes(script));
}

public static parse(md: string) {
const html = marked.parse(md, { async: false });
return html;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace Bit.BlazorUI;

[System.Diagnostics.CodeAnalysis.SuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
internal static class BitMarkdownViewerJsRuntimeExtensions
{
public static ValueTask<bool> BitMarkdownViewerCheckScript(this IJSRuntime jsRuntime, string script)
{
return jsRuntime.FastInvoke<bool>("BitBlazorUI.MarkdownViewer.checkScript", script);
}

public static ValueTask<string> BitMarkdownViewerParse(this IJSRuntime jsRuntime, string markdown)
{
return jsRuntime.FastInvoke<string>("BitBlazorUI.MarkdownViewer.parse", markdown);
}
}
Loading
Loading