Wave/Wave/Components/AdvancedMarkdownEditor.razor

159 lines
6.3 KiB
Plaintext

@implements IDisposable
@rendermode InteractiveServer
@using Wave.Utilities
@inject IStringLocalizer<AdvancedMarkdownEditor> Localizer
<section class="grid grid-cols-1 lg:grid-cols-2 gap-x-8 gap-y-4">
<div class="flex">
<div class="join join-vertical min-h-96 h-full w-full">
<Toolbar>
<ToolbarSection>
<ToolbarButton onclick="window.insertBeforeSelection('# ', true);"
title="@Localizer["Tools_H1_Tooltip"]">
<strong>@Localizer["Tools_H1_Label"]</strong>
</ToolbarButton>
<ToolbarButton onclick="window.insertBeforeSelection('## ', true);"
title="@Localizer["Tools_H2_Tooltip"]">
<strong>@Localizer["Tools_H2_Label"]</strong>
</ToolbarButton>
<ToolbarButton onclick="window.insertBeforeSelection('### ', true);"
title="@Localizer["Tools_H3_Tooltip"]">
<strong>@Localizer["Tools_H3_Label"]</strong>
</ToolbarButton>
<ToolbarButton onclick="window.insertBeforeSelection('#### ', true);"
title="@Localizer["Tools_H4_Tooltip"]">
@Localizer["Tools_H4_Label"]
</ToolbarButton>
</ToolbarSection>
<ToolbarSection>
<ToolbarButton onclick="window.insertBeforeAndAfterSelection('**');"
title="@Localizer["Tools_Bold_Tooltip"]">
<strong>B</strong>
</ToolbarButton>
<ToolbarButton onclick="window.insertBeforeAndAfterSelection('*')"
title="@Localizer["Tools_Italic_Tooltip"]">
<em>I</em>
</ToolbarButton>
<ToolbarButton onclick="window.insertBeforeAndAfterSelection('++')"
title="@Localizer["Tools_Underline_Tooltip"]">
<u>U</u>
</ToolbarButton>
<ToolbarButton onclick="window.insertBeforeAndAfterSelection('~~')"
title="@Localizer["Tools_StrikeThrough_Tooltip"]">
<del>@Localizer["Tools_StrikeThrough_Label"]</del>
</ToolbarButton>
<ToolbarButton onclick="window.insertBeforeAndAfterSelection('==')"
title="@Localizer["Tools_Mark_Tooltip"]">
<mark>@Localizer["Tools_Mark_Label"]</mark>
</ToolbarButton>
<ToolbarButton onclick="window.insertBeforeSelection('> ', true)"
title="@Localizer["Tools_Cite_Tooltip"]">
| <em>@Localizer["Tools_Cite_Label"]</em>
</ToolbarButton>
</ToolbarSection>
<ToolbarSection>
<ToolbarButton onclick="window.insertBeforeSelection('1. ', true)">
1.
</ToolbarButton>
<ToolbarButton onclick="window.insertBeforeSelection('a. ', true)">
a.
</ToolbarButton>
<ToolbarButton onclick="window.insertBeforeSelection('A. ', true)">
A.
</ToolbarButton>
<ToolbarButton onclick="window.insertBeforeSelection('i. ', true)">
i.
</ToolbarButton>
<ToolbarButton onclick="window.insertBeforeSelection('I. ', true)">
I.
</ToolbarButton>
</ToolbarSection>
<ToolbarSection>
<ToolbarButton onclick="window.insertBeforeAndAfterSelection('`')"
title="@Localizer["Tools_CodeLine_Tooltip"]">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-4 h-4">
<path fill-rule="evenodd" d="M14.447 3.026a.75.75 0 0 1 .527.921l-4.5 16.5a.75.75 0 0 1-1.448-.394l4.5-16.5a.75.75 0 0 1 .921-.527ZM16.72 6.22a.75.75 0 0 1 1.06 0l5.25 5.25a.75.75 0 0 1 0 1.06l-5.25 5.25a.75.75 0 1 1-1.06-1.06L21.44 12l-4.72-4.72a.75.75 0 0 1 0-1.06Zm-9.44 0a.75.75 0 0 1 0 1.06L2.56 12l4.72 4.72a.75.75 0 0 1-1.06 1.06L.97 12.53a.75.75 0 0 1 0-1.06l5.25-5.25a.75.75 0 0 1 1.06 0Z" clip-rule="evenodd" />
</svg>
</ToolbarButton>
<ToolbarButton onclick="window.insertBeforeAndAfterSelection('\n```\n')"
title="@Localizer["Tools_CodeBlock_Tooltip"]">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-4 h-4">
<path fill-rule="evenodd" d="M3 6a3 3 0 0 1 3-3h12a3 3 0 0 1 3 3v12a3 3 0 0 1-3 3H6a3 3 0 0 1-3-3V6Zm14.25 6a.75.75 0 0 1-.22.53l-2.25 2.25a.75.75 0 1 1-1.06-1.06L15.44 12l-1.72-1.72a.75.75 0 1 1 1.06-1.06l2.25 2.25c.141.14.22.331.22.53Zm-10.28-.53a.75.75 0 0 0 0 1.06l2.25 2.25a.75.75 0 1 0 1.06-1.06L8.56 12l1.72-1.72a.75.75 0 1 0-1.06-1.06l-2.25 2.25Z" clip-rule="evenodd" />
</svg>
</ToolbarButton>
</ToolbarSection>
</Toolbar>
@ChildContent
</div>
</div>
<div class="bg-base-200 p-2">
@if (!string.IsNullOrWhiteSpace(Title)) {
<h2 class="text-2xl lg:text-4xl font-bold mb-6 hyphens-auto">@Title</h2>
}
@if (!string.IsNullOrWhiteSpace(Markdown)) {
<div class="prose prose-neutral max-w-none hyphens-auto text-justify">
@HtmlPreview
</div>
} else {
<div class="flex flex-col gap-4">
<div class="skeleton h-4 w-full"></div>
<div class="skeleton h-4 w-full"></div>
<div class="skeleton h-32 w-full"></div>
<div class="skeleton h-4 w-full"></div>
<div class="skeleton h-4 w-full"></div>
<div class="skeleton h-4 w-full"></div>
</div>
}
</div>
</section>
<SectionContent SectionName="scripts">
</SectionContent>
@code {
[Parameter]
public required Func<string?> MarkdownCallback { get; set; }
[Parameter]
public string? Title { get; set; }
[Parameter]
public required RenderFragment ChildContent { get; set; }
private string? Markdown { get; set; }
private MarkupString HtmlPreview { get; set; } = new();
private CancellationTokenSource? Token { get; set; }
private Timer? Timer { get; set; }
protected override void OnInitialized() {
Timer = new Timer(_ => {
UpdateHtml();
}, null, TimeSpan.FromMilliseconds(250), TimeSpan.FromSeconds(1));
}
private void UpdateHtml() {
try {
string? markdown = MarkdownCallback.Invoke();
if (string.IsNullOrWhiteSpace(markdown)) return;
Token?.Cancel();
Token = new CancellationTokenSource();
string html = MarkdownUtilities.Parse(markdown);
Markdown = markdown;
HtmlPreview = new MarkupString(html);
InvokeAsync(StateHasChanged);
} catch (Exception ex) {
Console.Error.WriteLine(ex);
} finally {
Token = null;
}
}
public void Dispose() {
Timer?.Dispose();
Timer = null;
}
}