109 lines
4.0 KiB
C#
109 lines
4.0 KiB
C#
using System.Text.RegularExpressions;
|
|
using Microsoft.AspNetCore.Components;
|
|
using Model;
|
|
|
|
namespace Shared.Services;
|
|
|
|
public class CardRenderingService
|
|
{
|
|
private readonly Dictionary<string, CardData> _cardLookup;
|
|
private readonly Dictionary<string, DocData> _docLookup;
|
|
private readonly List<string> _allTerms;
|
|
private readonly Regex _termRegex;
|
|
|
|
public CardRenderingService()
|
|
{
|
|
var allCards = CardDatabase.Cards
|
|
.Where(c => !string.IsNullOrWhiteSpace(c.Name))
|
|
.ToList();
|
|
|
|
_cardLookup = allCards
|
|
.GroupBy(c => c.Name, StringComparer.OrdinalIgnoreCase)
|
|
.ToDictionary(g => g.Key, g => g.First(), StringComparer.OrdinalIgnoreCase);
|
|
|
|
var allDocs = DocDatabase.Docs
|
|
.Where(d => !string.IsNullOrWhiteSpace(d.Title))
|
|
.ToList();
|
|
|
|
_docLookup = allDocs
|
|
.GroupBy(d => d.Title, StringComparer.OrdinalIgnoreCase)
|
|
.ToDictionary(g => g.Key, g => g.First(), StringComparer.OrdinalIgnoreCase);
|
|
|
|
_allTerms = _cardLookup.Keys
|
|
.Concat(_docLookup.Keys)
|
|
.Distinct(StringComparer.OrdinalIgnoreCase)
|
|
.OrderByDescending(n => n.Length)
|
|
.ToList();
|
|
|
|
var pattern = string.Join("|", _allTerms.Select(n => $@"\b{Regex.Escape(n)}\b"));
|
|
_termRegex = new Regex(pattern, RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
|
}
|
|
|
|
public RenderFragment RenderDescription(string description, Action<CardData?> onCardClick, Action<DocData?>? onDocClick = null, CardPreviewService? previewService = null)
|
|
{
|
|
return builder =>
|
|
{
|
|
var sequence = 0;
|
|
var paragraphs = description.Split('\n', StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
foreach (var paragraph in paragraphs)
|
|
{
|
|
builder.OpenElement(sequence++, "p");
|
|
|
|
var matches = _termRegex.Matches(paragraph);
|
|
var lastIndex = 0;
|
|
|
|
foreach (Match match in matches)
|
|
{
|
|
if (match.Index > lastIndex)
|
|
builder.AddContent(sequence++, paragraph.Substring(lastIndex, match.Index - lastIndex));
|
|
|
|
var term = match.Value;
|
|
var card = LookupCard(term);
|
|
var doc = LookupDoc(term);
|
|
|
|
builder.OpenElement(sequence++, "button");
|
|
builder.AddAttribute(sequence++, "class", "inline-card-btn");
|
|
|
|
if (card != null)
|
|
{
|
|
builder.AddAttribute(sequence++, "onclick",
|
|
EventCallback.Factory.Create(this, () => onCardClick(card)));
|
|
|
|
if (previewService != null)
|
|
{
|
|
builder.AddAttribute(sequence++, "onmouseenter",
|
|
EventCallback.Factory.Create<Microsoft.AspNetCore.Components.Web.MouseEventArgs>(this, e => previewService.Show(card, e.ClientX, e.ClientY)));
|
|
builder.AddAttribute(sequence++, "onmouseleave",
|
|
EventCallback.Factory.Create(this, () => previewService.Hide()));
|
|
}
|
|
}
|
|
else if (doc != null && onDocClick != null)
|
|
{
|
|
builder.AddAttribute(sequence++, "onclick",
|
|
EventCallback.Factory.Create(this, () => onDocClick(doc)));
|
|
}
|
|
|
|
builder.AddContent(sequence++, term);
|
|
builder.CloseElement();
|
|
|
|
lastIndex = match.Index + match.Length;
|
|
}
|
|
|
|
if (lastIndex < paragraph.Length) builder.AddContent(sequence++, paragraph.Substring(lastIndex));
|
|
|
|
builder.CloseElement();
|
|
}
|
|
};
|
|
}
|
|
|
|
private CardData? LookupCard(string name)
|
|
{
|
|
return _cardLookup.GetValueOrDefault(name);
|
|
}
|
|
|
|
private DocData? LookupDoc(string name)
|
|
{
|
|
return _docLookup.GetValueOrDefault(name);
|
|
}
|
|
} |