Skip to content

Commit 30bc5ed

Browse files
Added support fof #8
1 parent 9ba96c2 commit 30bc5ed

File tree

7 files changed

+293
-0
lines changed

7 files changed

+293
-0
lines changed

src/AspNetCore.Utilities.Bootstrap5TagHelpers.Sample/Views/Home/Index.cshtml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,46 @@
239239
</tbody>
240240
</table>
241241

242+
<h2>Accordion</h2>
243+
<p>For rendering Accordions</p>
244+
245+
<table class="table table-striped table-hover">
246+
<thead>
247+
<tr>
248+
<th>Example</th>
249+
<th>Code</th>
250+
</tr>
251+
</thead>
252+
<tbody>
253+
<tr>
254+
<td>
255+
<accordion id="testAccordion" always-open="true">
256+
<accordion-item id="itemOne" expanded="true">
257+
<accordion-header>
258+
Accordion Item #1
259+
</accordion-header>
260+
<accordion-body>
261+
<strong>This is the first item's accordion body.</strong> It is shown by default, until the collapse plugin adds the appropriate classes that we use to style each element. These classes control the overall appearance, as well as the showing and hiding via CSS transitions. You can modify any of this with custom CSS or overriding our default variables. It's also worth noting that just about any HTML can go within the <code>.accordion-body</code>, though the transition does limit overflow.
262+
</accordion-body>
263+
</accordion-item>
264+
<accordion-item>
265+
<accordion-header>
266+
Accordion Item #2
267+
</accordion-header>
268+
<accordion-body>
269+
<strong>This is the second item's accordion body.</strong> It is shown by default, until the collapse plugin adds the appropriate classes that we use to style each element. These classes control the overall appearance, as well as the showing and hiding via CSS transitions. You can modify any of this with custom CSS or overriding our default variables. It's also worth noting that just about any HTML can go within the <code>.accordion-body</code>, though the transition does limit overflow.
270+
</accordion-body>
271+
</accordion-item>
272+
</accordion>
273+
</td>
274+
<td>
275+
<code>
276+
</code>
277+
</td>
278+
</tr>
279+
</tbody>
280+
</table>
281+
242282
<h2>Cards</h2>
243283

244284
<p>For rendering bootstrap cards</p>
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using ICG.AspNetCore.Utilities.Bootstrap5TagHelpers.Contexts;
2+
using Microsoft.AspNetCore.Mvc.Rendering;
3+
using Microsoft.AspNetCore.Mvc.TagHelpers;
4+
using Microsoft.AspNetCore.Razor.TagHelpers;
5+
using System.Text.Encodings.Web;
6+
using System.Threading.Tasks;
7+
8+
namespace ICG.AspNetCore.Utilities.Bootstrap5TagHelpers.Accordion;
9+
10+
/// <summary>
11+
/// Tag helper for individual items
12+
/// </summary>
13+
[HtmlTargetElement("accordion-body", ParentTag = "accordion-item")]
14+
public class AccordionBodyTagHelper : TagHelper
15+
{
16+
/// <summary>
17+
/// Processes the tag helper
18+
/// </summary>
19+
/// <param name="context"></param>
20+
/// <param name="output"></param>
21+
/// <returns></returns>
22+
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
23+
{
24+
//Get our context
25+
var accordionContext = (AccordionContext)context.Items[typeof(AccordionContext)];
26+
var itemContext = (AccordionItemContext)context.Items[typeof(AccordionItemContext)];
27+
28+
//Set as a Div
29+
output.TagName = "div";
30+
31+
//Add an id attribute
32+
output.Attributes.Add("id", itemContext.ItemId);
33+
34+
//Setup default items that are always there
35+
output.AddClass("accordion-collapse", HtmlEncoder.Default);
36+
output.AddClass("collapse", HtmlEncoder.Default);
37+
38+
//If not always open add the parent id
39+
if(!accordionContext.AlwaysOpen)
40+
output.Attributes.Add("data-bs-parent", $"#{accordionContext.Id}");
41+
42+
if (itemContext.Expanded)
43+
output.AddClass("show", HtmlEncoder.Default);
44+
45+
//Create a wrapping div
46+
var wrappingDiv = new TagBuilder("div");
47+
wrappingDiv.AddCssClass("accordion-body");
48+
49+
//Get the child content
50+
var content = (await output.GetChildContentAsync()).GetContent();
51+
wrappingDiv.InnerHtml.AppendHtml(content);
52+
53+
//Add to the output
54+
output.Content.AppendHtml(wrappingDiv);
55+
}
56+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using ICG.AspNetCore.Utilities.Bootstrap5TagHelpers.Contexts;
2+
using Microsoft.AspNetCore.Mvc.Rendering;
3+
using Microsoft.AspNetCore.Mvc.TagHelpers;
4+
using Microsoft.AspNetCore.Razor.TagHelpers;
5+
using System.Text.Encodings.Web;
6+
using System.Threading.Tasks;
7+
8+
namespace ICG.AspNetCore.Utilities.Bootstrap5TagHelpers.Accordion;
9+
10+
/// <summary>
11+
/// Tag helper for individual items
12+
/// </summary>
13+
[HtmlTargetElement("accordion-header", ParentTag = "accordion-item")]
14+
public class AccordionHeaderTagHelper : TagHelper
15+
{
16+
/// <summary>
17+
/// Processes the tag helper
18+
/// </summary>
19+
/// <param name="context"></param>
20+
/// <param name="output"></param>
21+
/// <returns></returns>
22+
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
23+
{
24+
//Get our context
25+
var accordionContext = (AccordionContext)context.Items[typeof(AccordionContext)];
26+
var itemContext = (AccordionItemContext)context.Items[typeof(AccordionItemContext)];
27+
28+
//Set as a Div
29+
output.TagName = "h2";
30+
31+
//Add default class
32+
output.AddClass("accordion-header", HtmlEncoder.Default);
33+
34+
//Build wrapping button
35+
var wrappingButton = new TagBuilder("button");
36+
wrappingButton.AddCssClass("accordion-button");
37+
wrappingButton.Attributes.Add("type", "button");
38+
wrappingButton.Attributes.Add("data-bs-toggle", "collapse");
39+
wrappingButton.Attributes.Add("data-bs-target", $"#{itemContext.ItemId}");
40+
wrappingButton.Attributes.Add("aria-expanded", itemContext.Expanded.ToString().ToLower());
41+
wrappingButton.Attributes.Add("aria-controls", itemContext.ItemId);
42+
43+
//Special behaviors for if it is not open
44+
if (!itemContext.Expanded)
45+
{
46+
wrappingButton.AddCssClass("collapsed");
47+
}
48+
49+
//Get the child content
50+
var content = (await output.GetChildContentAsync()).GetContent();
51+
wrappingButton.InnerHtml.AppendHtml(content);
52+
53+
//Add to the output
54+
output.Content.AppendHtml(wrappingButton);
55+
}
56+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
using ICG.AspNetCore.Utilities.Bootstrap5TagHelpers.Contexts;
2+
using Microsoft.AspNetCore.Mvc.TagHelpers;
3+
using Microsoft.AspNetCore.Razor.TagHelpers;
4+
using System;
5+
using System.Text.Encodings.Web;
6+
using System.Threading.Tasks;
7+
8+
namespace ICG.AspNetCore.Utilities.Bootstrap5TagHelpers.Accordion;
9+
10+
/// <summary>
11+
/// Tag helper for individual items
12+
/// </summary>
13+
[HtmlTargetElement("accordion-item", ParentTag = "accordion")]
14+
public class AccordionItemTagHelper : TagHelper
15+
{
16+
/// <summary>
17+
/// Should this be rendered as expanded
18+
/// </summary>
19+
public bool Expanded { get; set; }
20+
21+
/// <summary>
22+
/// Processes the tag helper
23+
/// </summary>
24+
/// <param name="context"></param>
25+
/// <param name="output"></param>
26+
/// <returns></returns>
27+
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
28+
{
29+
var id = output.Attributes["id"]?.Value?.ToString();
30+
31+
if (id != null)
32+
{
33+
//Remove the id from the wrapping div
34+
output.Attributes.Remove(output.Attributes["id"]);
35+
}
36+
else
37+
{
38+
id = Guid.NewGuid().ToString();
39+
}
40+
41+
//Set as a Div
42+
output.TagName = "div";
43+
44+
45+
//Add wrapping class
46+
output.AddClass("accordion-item", HtmlEncoder.Default);
47+
48+
49+
// setup content
50+
var itemContext = new AccordionItemContext() { ItemId = id, Expanded = Expanded };
51+
context.Items[typeof(AccordionItemContext)] = itemContext;
52+
53+
var content = (await output.GetChildContentAsync()).GetContent();
54+
55+
output.Content.AppendHtml(content);
56+
}
57+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using ICG.AspNetCore.Utilities.Bootstrap5TagHelpers.Contexts;
2+
using Microsoft.AspNetCore.Mvc.TagHelpers;
3+
using Microsoft.AspNetCore.Razor.TagHelpers;
4+
using System.Text.Encodings.Web;
5+
using System.Threading.Tasks;
6+
7+
namespace ICG.AspNetCore.Utilities.Bootstrap5TagHelpers.Accordion;
8+
9+
/// <summary>
10+
/// A tag helper for rendering a bootstrap card to a view
11+
/// </summary>
12+
[RestrictChildren("accordion-item")]
13+
public class AccordionTagHelper : TagHelper
14+
{
15+
/// <summary>
16+
/// Should this render as a Flush accordion
17+
/// </summary>
18+
public bool IsFlush { get; set; } = false;
19+
20+
/// <summary>
21+
/// If set to true child items can be opened/closed at any time
22+
/// </summary>
23+
public bool AlwaysOpen { get; set; } = false;
24+
25+
/// <summary>
26+
/// Processes the tag helper
27+
/// </summary>
28+
/// <param name="context"></param>
29+
/// <param name="output"></param>
30+
/// <returns></returns>
31+
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
32+
{
33+
var id = output.Attributes["id"]?.Value?.ToString();
34+
35+
output.TagName = "div";
36+
37+
output.AddClass("accordion", HtmlEncoder.Default);
38+
39+
if (IsFlush)
40+
output.AddClass("accordion-flush", HtmlEncoder.Default);
41+
42+
// setup content
43+
var accordionContext = new AccordionContext() {Id = id, AlwaysOpen = AlwaysOpen};
44+
context.Items[typeof(AccordionContext)] = accordionContext;
45+
46+
var content = (await output.GetChildContentAsync()).GetContent();
47+
48+
output.Content.AppendHtml(content);
49+
}
50+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
namespace ICG.AspNetCore.Utilities.Bootstrap5TagHelpers.Contexts;
2+
3+
/// <summary>
4+
/// Context helper for accordion tag helper
5+
/// </summary>
6+
public class AccordionContext
7+
{
8+
/// <summary>
9+
/// The ID of the parent accordion item
10+
/// </summary>
11+
public string Id { get; set; }
12+
13+
/// <summary>
14+
/// Should it render as always open
15+
/// </summary>
16+
public bool AlwaysOpen { get; set; }
17+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
namespace ICG.AspNetCore.Utilities.Bootstrap5TagHelpers.Contexts;
2+
3+
/// <summary>
4+
/// Context for an accordion item
5+
/// </summary>
6+
public class AccordionItemContext
7+
{
8+
/// <summary>
9+
/// What is the unique item id for this item
10+
/// </summary>
11+
public string ItemId { get; set; }
12+
13+
/// <summary>
14+
/// Should this render as expanded
15+
/// </summary>
16+
public bool Expanded { get; set; }
17+
}

0 commit comments

Comments
 (0)