Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dynamic tabs support #538

Merged
merged 1 commit into from
Jan 31, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 3 additions & 0 deletions BlazorBootstrap.Demo.RCL/Pages/Tabs/TabsDocumentation.razor
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@
<SectionHeading Size="HeadingSize.H2" Text="Tab call back event: OnTabClicked" PageUrl="@pageUrl" HashTagName="tab-callback-event-on-tab-clicked" />
<Demo Type="typeof(Tabs_Demo_12_Tab_Callback_Event_OnTabClicked)" />

<SectionHeading Size="HeadingSize.H2" Text="Dynamic tabs" PageUrl="@pageUrl" HashTagName="dynamic-tabs" />
<Demo Type="typeof(Tabs_Demo_13_Dynamic_Tabs)" />

@code {
private string pageUrl = "/tabs";
private string title = "Blazor Tabs Component";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
<Tabs>
@foreach (var customer in customers)
{
<Tab IsActive="selectedCustomer == customer" OnTabClicked="async () => await OnTabClickedAsync(customer)" Title="@customer.CustomerName">
<Tab Title="@customer.CustomerName"
IsActive="selectedCustomer.CustomerId == customer.CustomerId"
OnTabClicked="() => OnTabClicked(customer)">
<Content>
<div class="mt-3">
This is the placeholder content for the <b>@customer.CustomerName</b> tab.
Expand All @@ -17,24 +19,11 @@
}

@code {
private List<Customer> customers = new()
{
new Customer(1, "Marvin Klein"),
new Customer(2, "Vikram Reddy"),
new Customer(3, "Bandita PA"),
new Customer(4, "Daina JJ")
};
private List<Customer> customers = new() { new(1, "Marvin Klein"), new(2, "Vikram Reddy"), new(3, "Bandita PA"), new(4, "Daina JJ") };

private Customer? selectedCustomer;
private Customer selectedCustomer = default!;

protected override void OnInitialized()
{
selectedCustomer = customers.First();
}
protected override void OnInitialized() => selectedCustomer = customers.First();

private Task OnTabClickedAsync(Customer customer)
{
selectedCustomer = customer;
return Task.CompletedTask;
}
private void OnTabClicked(Customer customer) => selectedCustomer = customer;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<Tabs @ref="tabs">
@foreach (var customer in customers)
{
<Tab Title="@customer.CustomerName"
OnTabClicked="() => OnTabClicked(customer)">
<Content>
<div class="mt-3">
This is the placeholder content for the <b>@customer.CustomerName</b> tab.
</div>
</Content>
</Tab>
}
</Tabs>

@if (selectedCustomer is not null)
{
<div class="mt-3">Selected customer: <b>@selectedCustomer.CustomerName</b></div>
}

<Button Color="ButtonColor.Success" Class="mt-3" @onclick="AddCustomer">Add customer</Button>

@code {
Tabs tabs = default!;

private List<Customer> customers = new() { new(1, "Marvin Klein"), new(2, "Vikram Reddy"), new(3, "Bandita PA"), new(4, "Daina JJ") };

private Customer selectedCustomer = default!;

protected override void OnInitialized() => selectedCustomer = customers.Last();

private void AddCustomer()
{
var count = customers.Count;
var customer = new Customer(count + 1, $"Customer {count + 1}");
customers.Add(customer);
//selectedCustomer = customer; NOTE: this line is not required
tabs.InitializeRecentTab(showTab: true);
}

private void OnTabClicked(Customer customer) => selectedCustomer = customer;
}
51 changes: 37 additions & 14 deletions blazorbootstrap/Components/Tabs/Tabs.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,9 @@ protected override async Task OnInitializedAsync()
{
objRef ??= DotNetObjectReference.Create(this);

Attributes ??= new();
if(IsVertical)
Attributes ??= new Dictionary<string, object>();

if (IsVertical)
Attributes.Add("aria-orientation", "vertical");

await base.OnInitializedAsync();
Expand Down Expand Up @@ -97,6 +98,30 @@ public async Task bsShowTab(string activeTabId, string previousActiveTabId)
await OnShowing.InvokeAsync(args);
}

/// <summary>
/// Initializes the most recently added tab, optionally displaying it.
/// </summary>
/// <param name="showTab">Specifies whether to display the tab after initialization.</param>
public void InitializeRecentTab(bool showTab)
{
if (!tabs?.Any() ?? false) return;

ExecuteAfterRender(
async () =>
{
var tab = tabs!.LastOrDefault();

if (tab is { Disabled: false })
{
await JS.InvokeVoidAsync("window.blazorBootstrap.tabs.initializeNewTab", tab.ElementId, objRef);

if (showTab)
await ShowTabAsync(tab);
}
}
);
}

/// <summary>
/// Selects the first tab and show its associated pane.
/// </summary>
Expand All @@ -106,7 +131,7 @@ public async Task ShowFirstTabAsync()

var tab = tabs!.FirstOrDefault(x => !x.Disabled);

if (tab != null)
if (tab is { Disabled: false })
await ShowTabAsync(tab);
}

Expand All @@ -119,7 +144,7 @@ public async Task ShowLastTabAsync()

var tab = tabs!.LastOrDefault(x => !x.Disabled);

if (tab != null)
if (tab is { Disabled: false })
await ShowTabAsync(tab);
}

Expand All @@ -135,14 +160,14 @@ public async Task ShowTabByIndexAsync(int tabIndex)

var tab = tabs[tabIndex];

if (tab != null && !tab.Disabled)
if (tab is { Disabled: false })
await ShowTabAsync(tab);
}

/// <summary>
/// Selects the tab by name and show its associated pane.
/// </summary>
/// <param name="tabName"></param>
/// <param name="tabName">The name of the tab to select.</param>
public async Task ShowTabByNameAsync(string tabName)
{
if (!tabs?.Any() ?? false) return;
Expand All @@ -155,14 +180,12 @@ public async Task ShowTabByNameAsync(string tabName)

internal void AddTab(Tab tab)
{
if (tab != null)
{
tabs?.Add(tab);
tabs!.Add(tab);

if (tab is { IsActive: true, Disabled: false }) activeTab = tab;
if (tab is { IsActive: true, Disabled: false })
activeTab = tab;

StateHasChanged(); // This is mandatory
}
StateHasChanged(); // This is mandatory
}

/// <summary>
Expand All @@ -172,9 +195,9 @@ internal async Task SetDefaultActiveTabAsync()
{
if (!tabs?.Any() ?? false) return;

activeTab ??= tabs?.FirstOrDefault(x => !x.Disabled)!;
activeTab ??= tabs!.FirstOrDefault(x => !x.Disabled)!;

if (activeTab != null)
if (activeTab is not null)
await ShowTabAsync(activeTab);
}

Expand Down
26 changes: 26 additions & 0 deletions blazorbootstrap/wwwroot/blazor.bootstrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -630,6 +630,32 @@ window.blazorBootstrap = {
});
});
},
initializeNewTab: (tabId, dotNetHelper) => {
let tabEl = document.getElementById(tabId);
if (tabEl == null)
return;

tabEl?.addEventListener('show.bs.tab', (event) => {
// event.target --> active tab
// event.relatedTarget --> previous active tab (if available)
dotNetHelper.invokeMethodAsync('bsShowTab', event.target?.id, event.relatedTarget?.id);
});
tabEl?.addEventListener('shown.bs.tab', (event) => {
// event.target --> active tab
// event.relatedTarget --> previous active tab
dotNetHelper.invokeMethodAsync('bsShownTab', event.target?.id, event.relatedTarget?.id);
});
tabEl?.addEventListener('hide.bs.tab', (event) => {
// event.target --> current active tab
// event.relatedTarget --> new soon-to-be-active tab
dotNetHelper.invokeMethodAsync('bsHideTab', event.relatedTarget?.id, event.target?.id);
});
tabEl?.addEventListener('hidden.bs.tab', (event) => {
// event.target --> previous active tab
// event.relatedTarget --> new active tab
dotNetHelper.invokeMethodAsync('bsHiddenTab', event.relatedTarget?.id, event.target?.id);
});
},
show: (elementId) => {
let navTabsEl = document.getElementById(elementId);
if (navTabsEl != null)
Expand Down
Loading