Skip to content

Commit 9639d98

Browse files
committedMar 19, 2025·
Allow auto linking of markdown headers.
1 parent 3bfc16b commit 9639d98

File tree

3 files changed

+64
-24
lines changed

3 files changed

+64
-24
lines changed
 

‎Radzen.Blazor/Markdown/BlazorMarkdownRenderer.cs

+25-8
Original file line numberDiff line numberDiff line change
@@ -6,35 +6,52 @@
66

77
namespace Radzen.Blazor.Markdown;
88

9-
class BlazorMarkdownRenderer(RenderTreeBuilder builder, Action<RenderTreeBuilder, int> outlet) : NodeVisitorBase
9+
class BlazorMarkdownRendererOptions
10+
{
11+
public bool AutoLinkHeadings { get; set; }
12+
}
13+
14+
class BlazorMarkdownRenderer(BlazorMarkdownRendererOptions options, RenderTreeBuilder builder, Action<RenderTreeBuilder, int> outlet) : NodeVisitorBase
1015
{
1116
public const string Outlet = "<!--rz-outlet-{0}-->";
1217

1318
public override void VisitHeading(Heading heading)
1419
{
1520
builder.OpenComponent<RadzenText>(0);
1621
builder.AddAttribute(1, nameof(RadzenText.ChildContent), RenderChildren(heading.Children));
22+
1723
switch (heading.Level)
1824
{
1925
case 1:
2026
builder.AddAttribute(2, nameof(RadzenText.TextStyle), TextStyle.H1);
2127
break;
2228
case 2:
23-
builder.AddAttribute(2, nameof(RadzenText.TextStyle), TextStyle.H2);
29+
builder.AddAttribute(3, nameof(RadzenText.TextStyle), TextStyle.H2);
2430
break;
2531
case 3:
26-
builder.AddAttribute(2, nameof(RadzenText.TextStyle), TextStyle.H3);
32+
builder.AddAttribute(4, nameof(RadzenText.TextStyle), TextStyle.H3);
2733
break;
2834
case 4:
29-
builder.AddAttribute(2, nameof(RadzenText.TextStyle), TextStyle.H4);
35+
builder.AddAttribute(5, nameof(RadzenText.TextStyle), TextStyle.H4);
3036
break;
3137
case 5:
32-
builder.AddAttribute(2, nameof(RadzenText.TextStyle), TextStyle.H5);
38+
builder.AddAttribute(6, nameof(RadzenText.TextStyle), TextStyle.H5);
3339
break;
3440
case 6:
35-
builder.AddAttribute(2, nameof(RadzenText.TextStyle), TextStyle.H6);
41+
builder.AddAttribute(7, nameof(RadzenText.TextStyle), TextStyle.H6);
3642
break;
3743
}
44+
45+
if (options.AutoLinkHeadings)
46+
{
47+
var anchor = Regex.Replace(heading.Value, @"[^\w\s-]", string.Empty).Replace(' ', '-').ToLowerInvariant().Trim();
48+
builder.AddAttribute(8, nameof(RadzenText.Anchor), anchor);
49+
}
50+
else
51+
{
52+
builder.AddAttribute(9, nameof(RadzenText.Anchor), (string)null);
53+
}
54+
3855
builder.CloseComponent();
3956
}
4057

@@ -67,7 +84,7 @@ private static void RenderCellAlignment(RenderTreeBuilder builder, TableCellAlig
6784
builder.AddAttribute(2, nameof(RadzenTableCell.Style), "text-align: center");
6885
break;
6986
case TableCellAlignment.Right:
70-
builder.AddAttribute(2, nameof(RadzenTableCell.Style), "text-align: right");
87+
builder.AddAttribute(3, nameof(RadzenTableCell.Style), "text-align: right");
7188
break;
7289
}
7390
}
@@ -120,7 +137,7 @@ private RenderFragment RenderChildren(IEnumerable<INode> children)
120137
{
121138
return innerBuilder =>
122139
{
123-
var inner = new BlazorMarkdownRenderer(innerBuilder, outlet);
140+
var inner = new BlazorMarkdownRenderer(options, innerBuilder, outlet);
124141
inner.VisitChildren(children);
125142
};
126143
}

‎Radzen.Blazor/RadzenMarkdown.razor.cs

+28-14
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ public partial class RadzenMarkdown : RadzenComponent
3737
[Parameter]
3838
public string? Text { get; set; }
3939

40+
/// <summary>
41+
/// Whether to create anchor links for headings.
42+
/// </summary>
43+
[Parameter]
44+
public bool AutoLinkHeadings { get; set; } = true;
45+
4046
/// <inheritdoc />
4147
protected override string GetComponentCssClass()
4248
{
@@ -58,21 +64,31 @@ private void RenderChildContent(RenderTreeBuilder builder)
5864
else
5965
{
6066
var document = MarkdownParser.Parse(Text);
61-
62-
var visitor = new BlazorMarkdownRenderer(builder, Empty);
6367

64-
document.Accept(visitor);
68+
Render(document, builder, Empty);
6569
}
6670
}
6771

72+
private void Render(Document document, RenderTreeBuilder builder, Action<RenderTreeBuilder, int> outlet)
73+
{
74+
var options = new BlazorMarkdownRendererOptions
75+
{
76+
AutoLinkHeadings = AutoLinkHeadings
77+
};
78+
79+
var visitor = new BlazorMarkdownRenderer(options, builder, outlet);
80+
81+
document.Accept(visitor);
82+
}
83+
6884
private static void Empty(RenderTreeBuilder builder, int marker)
6985
{
7086
}
7187

72-
private static void ProcessFramesWithMarkers(RenderTreeBuilder builder, ArrayRange<RenderTreeFrame> frames)
88+
private void ProcessFramesWithMarkers(RenderTreeBuilder builder, ArrayRange<RenderTreeFrame> frames)
7389
{
74-
var markdownBuilder = new StringBuilder();
75-
var componentFrames = new Dictionary<int, (int startIndex, int endIndex)>();
90+
var markdown = new StringBuilder();
91+
var outletFrames = new Dictionary<int, (int startIndex, int endIndex)>();
7692
var markerId = 0;
7793
var index = 0;
7894

@@ -83,18 +99,18 @@ private static void ProcessFramesWithMarkers(RenderTreeBuilder builder, ArrayRan
8399
if (frame.FrameType == RenderTreeFrameType.Text || frame.FrameType == RenderTreeFrameType.Markup)
84100
{
85101
var content = frame.FrameType == RenderTreeFrameType.Text ? frame.TextContent : frame.MarkupContent;
86-
markdownBuilder.Append(content);
102+
markdown.Append(content);
87103
index++;
88104
}
89105
else if (frame.FrameType == RenderTreeFrameType.Component || frame.FrameType == RenderTreeFrameType.Element)
90106
{
91107
// Insert a marker for this component
92108
var marker = string.Format(BlazorMarkdownRenderer.Outlet, markerId);
93-
markdownBuilder.Append(marker);
109+
markdown.Append(marker);
94110

95111
// Store the component information for later
96112
var subtreeLength = GetSubtreeLength(frame);
97-
componentFrames.Add(markerId, (index, index + subtreeLength));
113+
outletFrames.Add(markerId, (index, index + subtreeLength));
98114

99115
// Increment marker ID and skip past this component
100116
markerId++;
@@ -107,19 +123,17 @@ private static void ProcessFramesWithMarkers(RenderTreeBuilder builder, ArrayRan
107123
}
108124
}
109125

110-
var document = MarkdownParser.Parse(markdownBuilder.ToString());
126+
var document = MarkdownParser.Parse(markdown.ToString());
111127

112128
void RenderOutlet(RenderTreeBuilder outletBuilder, int markerId)
113129
{
114-
if (componentFrames.TryGetValue(markerId, out var componentFrame))
130+
if (outletFrames.TryGetValue(markerId, out var componentFrame))
115131
{
116132
CopyFrames(outletBuilder, frames, componentFrame.startIndex, componentFrame.endIndex);
117133
}
118134
}
119135

120-
var visitor = new BlazorMarkdownRenderer(builder, RenderOutlet);
121-
122-
document.Accept(visitor);
136+
Render(document, builder, RenderOutlet);
123137
}
124138

125139
private static void CopyFrames(RenderTreeBuilder builder, ArrayRange<RenderTreeFrame> frames, int startIndex, int endIndex)

‎RadzenBlazorDemos/Pages/MarkdownConfig.razor

+11-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
<RadzenMarkdown class="rz-p-0 rz-p-md-12">
1+
<RadzenCard Variant="Variant.Outlined">
2+
<RadzenStack Orientation="Orientation.Horizontal" AlignItems="AlignItems.Center" Gap="0.5rem" Wrap="FlexWrap.Wrap">
3+
<RadzenCheckBox @bind-Value="@autoLinkHeadings" Name="autoLinkHeadings"></RadzenCheckBox>
4+
<RadzenLabel Text="Auto link headings" Component="autoLinkHeadings" />
5+
</RadzenStack>
6+
</RadzenCard>
7+
<RadzenMarkdown class="rz-p-0 rz-p-md-12" AutoLinkHeadings=@autoLinkHeadings>
28
### RadzenMarkdown
39

410
**RadzenMarkdown** allows you to render Markdown content in your Blazor applications.
@@ -17,4 +23,7 @@ Use markdown content right in your Blazor components - no need to create separat
1723
This is a **bold** text.
1824
</RadzenMarkdown>")
1925
```
20-
</RadzenMarkdown>
26+
</RadzenMarkdown>
27+
@code {
28+
bool autoLinkHeadings = true;
29+
}

0 commit comments

Comments
 (0)
Please sign in to comment.