Files
ChronoCCG/Chrono/Shared/Services/CardRenderingService.cs

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);
}
}