Skip to content
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,22 @@
@inherits BitComponentBase
@typeparam TItem

@if (ChildContent is not null || Options is not null)
{
<CascadingValue Value="this" IsFixed="true">
<div style="display:none" hidden>@(Options ?? ChildContent)</div>
</CascadingValue>
}

<div @ref="RootElement" @attributes="HtmlAttributes"
id="@_Id"
style="@StyleBuilder.Value"
class="@ClassBuilder.Value"
dir="@Dir?.ToString().ToLower()">
@foreach (var item in _items)
@if (ChildContent is not null || Options is not null)
{
var key = GetItemKey(item);
var isEnabled = IsEnabled && GetIsEnabled(item);
var headerTemplate = GetItemHeaderTemplate(item);

<BitAccordion @key="@key"
Background="Background"
Border="Border"
Class="@GetClass(item)"
Style="@GetStyle(item)"
Classes="_itemClasses"
Styles="_itemStyles"
Dir="Dir"
IsEnabled="isEnabled"
NoBorder="NoBorder"
Title="@GetTitle(item)"
Description="@GetDescription(item)"
ExpanderIcon="GetItemExpanderIcon(item)"
ExpanderIconName="@GetItemExpanderIconName(item)"
HeaderTemplate="headerTemplate"
IsExpanded="IsItemExpanded(item)"
OnClick="() => HandleOnItemClick(item)">
@GetItemBody(item)
</BitAccordion>
<CascadingValue Value="this" IsFixed="true">
@(Options ?? ChildContent)
</CascadingValue>
}
else
{
@foreach (var item in _items)
{
<_BitAccordionListItem @key="@GetItemKey(item)" AccordionList="this" Item="item" />
}
}
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ public partial class BitAccordionList<TItem> : BitComponentBase where TItem : cl
private string? _internalExpandedKey;
private List<string> _internalExpandedKeys = [];
private readonly HashSet<string> _expandedKeys = [];
private BitAccordionClassStyles? _itemClasses;
private BitAccordionClassStyles? _itemStyles;
internal BitAccordionClassStyles? _itemClasses;
internal BitAccordionClassStyles? _itemStyles;



Expand Down Expand Up @@ -157,6 +157,7 @@ public async Task ExpandAll()
}

await UpdateBoundKeys();
RefreshOptions();
StateHasChanged();
}

Expand All @@ -177,6 +178,7 @@ public async Task CollapseAll()
}

await UpdateBoundKeys();
RefreshOptions();
StateHasChanged();
}

Expand Down Expand Up @@ -325,6 +327,10 @@ protected override async Task OnParametersSetAsync()
}
}

// Options render their items themselves and Blazor skips re-rendering them when only the
// accordion list's own parameters (Styles, ExpandedKey(s), ...) change, so push a re-render to each one.
RefreshOptions();

await base.OnParametersSetAsync();
}

Expand Down Expand Up @@ -470,7 +476,7 @@ private void SyncFromExpandedKeys(IEnumerable<string>? keys)
_internalExpandedKeys = GetOrderedExpandedKeys();
}

private async Task HandleOnItemClick(TItem item)
internal async Task HandleOnItemClick(TItem item)
{
if (IsEnabled is false || GetIsEnabled(item) is false) return;

Expand Down Expand Up @@ -522,6 +528,20 @@ private async Task ToggleItem(TItem item, string key, bool expand)
await OnToggle.InvokeAsync(item);

await UpdateBoundKeys();

// A toggle can affect other items too (single-expand mode collapses the previously expanded
// item), and the click handler runs on the clicked item's renderer, so both the registered
// options and the accordion list itself need an explicit re-render.
RefreshOptions();
StateHasChanged();
}

private void RefreshOptions()
{
foreach (var item in _items)
{
(item as BitAccordionListOption)?.InternalStateHasChanged();
}
}

private async Task UpdateBoundKeys()
Expand Down Expand Up @@ -571,13 +591,13 @@ private void BuildItemClassStyles()
};
}

private bool IsItemExpanded(TItem item)
internal bool IsItemExpanded(TItem item)
{
var key = GetItemKey(item);
return key.HasValue() && _expandedKeys.Contains(key!);
}

private RenderFragment<bool>? GetItemHeaderTemplate(TItem item)
internal RenderFragment<bool>? GetItemHeaderTemplate(TItem item)
{
var itemTemplate = GetHeaderTemplate(item);
if (itemTemplate is not null) return _ => itemTemplate(item);
Expand All @@ -590,7 +610,7 @@ private bool IsItemExpanded(TItem item)
return null;
}

private RenderFragment? GetItemBody(TItem item)
internal RenderFragment? GetItemBody(TItem item)
{
// The option's plain inline content (ChildContent) is rendered as-is.
if (item is BitAccordionListOption listOption && listOption.ChildContent is not null)
Expand All @@ -609,19 +629,19 @@ private bool IsItemExpanded(TItem item)
return null;
}

private BitIconInfo? GetItemExpanderIcon(TItem item)
internal BitIconInfo? GetItemExpanderIcon(TItem item)
{
return GetExpanderIcon(item) ?? ExpanderIcon;
}

private string? GetItemExpanderIconName(TItem item)
internal string? GetItemExpanderIconName(TItem item)
{
return GetExpanderIconName(item) ?? ExpanderIconName;
}



private string? GetItemKey(TItem? item)
internal string? GetItemKey(TItem? item)
{
if (item is null) return null;

Expand Down Expand Up @@ -664,7 +684,7 @@ private void SetItemKey(TItem item, string value)
item.SetValueToProperty(NameSelectors.Key.Name, value);
}

private string? GetClass(TItem? item)
internal string? GetClass(TItem? item)
{
if (item is null) return null;

Expand All @@ -688,7 +708,7 @@ private void SetItemKey(TItem item, string value)
return item.GetValueFromProperty<string?>(NameSelectors.Class.Name);
}

private string? GetStyle(TItem? item)
internal string? GetStyle(TItem? item)
{
if (item is null) return null;

Expand All @@ -712,7 +732,7 @@ private void SetItemKey(TItem item, string value)
return item.GetValueFromProperty<string?>(NameSelectors.Style.Name);
}

private string? GetTitle(TItem? item)
internal string? GetTitle(TItem? item)
{
if (item is null) return null;

Expand All @@ -736,7 +756,7 @@ private void SetItemKey(TItem item, string value)
return item.GetValueFromProperty<string?>(NameSelectors.Title.Name);
}

private string? GetDescription(TItem? item)
internal string? GetDescription(TItem? item)
{
if (item is null) return null;

Expand All @@ -760,7 +780,7 @@ private void SetItemKey(TItem item, string value)
return item.GetValueFromProperty<string?>(NameSelectors.Description.Name);
}

private bool GetIsEnabled(TItem? item)
internal bool GetIsEnabled(TItem? item)
{
if (item is null) return false;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,13 @@ public partial class BitAccordionListOption : ComponentBase, IAsyncDisposable
[Parameter] public string? Title { get; set; }


internal void InternalStateHasChanged()
{
StateHasChanged();
}



protected override async Task OnInitializedAsync()
{
if (Parent is not null)
Expand All @@ -89,6 +96,18 @@ protected override async Task OnInitializedAsync()
await base.OnInitializedAsync();
}

// Renders the option's item in place, so the rendered order of the items always follows the
// markup order of the options, even when an option is added or removed conditionally later on.
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
if (Parent is null) return;

builder.OpenComponent<_BitAccordionListItem<BitAccordionListOption>>(0);
builder.AddComponentParameter(1, nameof(_BitAccordionListItem<BitAccordionListOption>.AccordionList), Parent);
builder.AddComponentParameter(2, nameof(_BitAccordionListItem<BitAccordionListOption>.Item), this);
builder.CloseComponent();
}

public async ValueTask DisposeAsync()
{
await DisposeAsync(true);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
@namespace Bit.BlazorUI
@typeparam TItem where TItem : class

@{
var isEnabled = AccordionList.IsEnabled && AccordionList.GetIsEnabled(Item);
var headerTemplate = AccordionList.GetItemHeaderTemplate(Item);
}

<BitAccordion Background="AccordionList.Background"
Border="AccordionList.Border"
Class="@AccordionList.GetClass(Item)"
Style="@AccordionList.GetStyle(Item)"
Classes="AccordionList._itemClasses"
Styles="AccordionList._itemStyles"
Dir="AccordionList.Dir"
IsEnabled="isEnabled"
NoBorder="AccordionList.NoBorder"
Title="@AccordionList.GetTitle(Item)"
Description="@AccordionList.GetDescription(Item)"
ExpanderIcon="AccordionList.GetItemExpanderIcon(Item)"
ExpanderIconName="@AccordionList.GetItemExpanderIconName(Item)"
HeaderTemplate="headerTemplate"
IsExpanded="AccordionList.IsItemExpanded(Item)"
OnClick="() => AccordionList.HandleOnItemClick(Item)">
@AccordionList.GetItemBody(Item)
</BitAccordion>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Bit.BlazorUI;

public partial class _BitAccordionListItem<TItem> : ComponentBase where TItem : class
{
[Parameter] public TItem Item { get; set; } = default!;

[Parameter] public BitAccordionList<TItem> AccordionList { get; set; } = default!;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,23 @@
@inherits BitComponentBase
@typeparam TItem

<CascadingValue Value="this" IsFixed="true">
<div style="display:none" hidden>@(Options ?? ChildContent)</div>
</CascadingValue>

<div @ref="RootElement" @attributes="HtmlAttributes"
id="@_Id"
role="group"
style="@StyleBuilder.Value"
class="@ClassBuilder.Value"
dir="@Dir?.ToString().ToLower()">
@for (int i = 0; i < _items.Count; i++)
@if (Options is not null || ChildContent is not null)
{
var item = _items[i];
var isEnabled = IsEnabled && GetIsEnabled(item);
var template = GetTemplate(item);
<button @onclick="() => HandleOnItemClick(item)"
tabindex="@(isEnabled ? 0 : -1)"
disabled="@(isEnabled is false)"
aria-disabled="@(isEnabled is false)"
title="@GetItemTitle(item)"
style="@GetItemStyle(item)"
class="@GetItemClass(item)">
@if (template is not null)
{
@template(item)
}
else if (ItemTemplate is not null)
{
@ItemTemplate(item)
}
else
{
var icon = GetItemIcon(item);
@if (icon is not null)
{
<i style="@Styles?.Icon" class="@icon.GetCssClasses() @Classes?.Icon" />
}

var text = GetItemText(item);
if (text.HasValue())
{
<span style="@Styles?.Text" class="bit-btg-btx @Classes?.Text">@text</span>
}
}
</button>
<CascadingValue Value="this" IsFixed="true">
@(Options ?? ChildContent)
</CascadingValue>
}
else
{
@foreach (var item in _items)
{
<_BitButtonGroupItem @key="item" ButtonGroup="this" Item="item" />
Comment thread
msynk marked this conversation as resolved.
Outdated
}
}
</div>
Loading
Loading