Skip to content

Commit 337dc10

Browse files
ntachevaTsvetomir-Hrdimodi
authored
chore(TabStrip): add kb for adding and removing tabs (#2998)
* chore(TabStrip): add kb for adding and removing tabs * chore: polish the example and update article content * polish KB article --------- Co-authored-by: Tsvetomir Hristov <[email protected]> Co-authored-by: Dimo Dimov <[email protected]>
1 parent e9bec3e commit 337dc10

File tree

2 files changed

+182
-9
lines changed

2 files changed

+182
-9
lines changed

components/tabstrip/tabs-collection.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@ To deactivate all tabs, set the ActiveTabId parameter to `string.Empty`.
7070
}
7171
````
7272

73+
## Add and Remove Tabs
74+
75+
If you are iterating through a collection to render the tabs and you need to allow the users to add and remove tabs, you may use the `ActiveTabId` parameter to set the active tab after adding and removing tabs. See details and example in this article: [Add and Remove Tabs](slug:tabstrip-kb-add-remove-tabs).
76+
77+
7378
## See Also
7479

7580
* [TabStrip Events](slug:tabstrip-events)
Lines changed: 177 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
---
2-
title: Dynamic Tabs
3-
description: How to create Dynamic Tabs in TabStip.
2+
title: Add and Remove TabStrip Tabs
3+
description: Learn how to dynamically add and remove tabs
44
type: how-to
5-
page_title: Dynamic Tabs
6-
slug: tabstrip-kb-dynamic-tabs
7-
position:
8-
tags:
5+
page_title: How to Add and Remove TabStrip Tabs
6+
slug: tabstrip-kb-add-remove-tabs
7+
tags: telerik,blazor,tabstrip,add tabs,remove tabs
8+
ticketid:
99
res_type: kb
1010
---
1111

@@ -20,12 +20,180 @@ res_type: kb
2020
</tbody>
2121
</table>
2222

23-
2423
## Description
2524

26-
How to create Dynamic Tabs in TabStip? How to add and remove tabs dynamically? How to get information about the currently active tab? How to set the content of the tabs dynamically?
25+
I have a collection of items representing separate tabs. I am iterating through that collection to render a tab for each item as shown in the [Tabs Collection article](slug:tabstrip-tabs-collection). I want to allow the user to add and remove tabs. How to achieve that?
26+
27+
This KB article also answers the following questions:
2728

29+
* How to implement add and remove tab functionality with the Telerik TabStrip component.
30+
* How to remove a tab using an "X" button in the tab header.
31+
* How to add a new tab with a "+" button, similar to browser tab controls.
32+
* How to position the add ("+") button next to the last tab header.
2833

2934
## Solution
3035

31-
An example is available in the following project: [https://github.com/telerik/blazor-ui/tree/master/tabstrip/DynamicTabs](https://github.com/telerik/blazor-ui/tree/master/tabstrip/DynamicTabs).
36+
1. [Render the TabStrip tabs in a loop](slug:tabstrip-tabs-collection).
37+
1. Use a [`HeaderTemplate`](slug:tabstrip-header-template) for the tabs to add Remove buttons. You can display the buttons conditionally based on the tab count.
38+
1. Declare a button for adding new tabs.
39+
1. Use custom styling and JavaScript to position the Add button next to the last tab header.
40+
41+
>caption Adding and removing TabStrip tabs at runtime
42+
43+
````RAZOR
44+
@inject IJSRuntime JS
45+
46+
<div class="dynamic-tabstrip-wrapper k-relative">
47+
<TelerikButton OnClick="@AddTab"
48+
Class="add-tab-button !k-absolute k-z-10 k-ratio-1"
49+
FillMode="@ThemeConstants.Button.FillMode.Flat"
50+
Icon="@SvgIcon.Plus"
51+
ThemeColor="@ThemeConstants.Button.ThemeColor.Primary" />
52+
53+
<TelerikTabStrip @bind-ActiveTabId="@ActiveTabId" PersistTabContent="true">
54+
@foreach (Tab tab in Tabs)
55+
{
56+
<TabStripTab @key="tab.Id" Id="@tab.Id">
57+
<HeaderTemplate>
58+
<div class="k-flex-layout k-gap-2">
59+
<span>@tab.Title</span>
60+
@if (Tabs.Count > 1)
61+
{
62+
<TelerikButton OnClick="@(() => RemoveTab(tab))"
63+
Class="remove-tab-button"
64+
FillMode="@ThemeConstants.Button.FillMode.Flat"
65+
Icon="@SvgIcon.X"
66+
Size="@ThemeConstants.Button.Size.Small"
67+
ThemeColor="@ThemeConstants.Button.ThemeColor.Primary" />
68+
}
69+
</div>
70+
</HeaderTemplate>
71+
<Content>
72+
Content for <strong>@tab.Title</strong>
73+
</Content>
74+
</TabStripTab>
75+
}
76+
</TelerikTabStrip>
77+
</div>
78+
79+
<style>
80+
.dynamic-tabstrip-wrapper {
81+
--add-tab-button-size: 32.8px;
82+
}
83+
84+
.dynamic-tabstrip-wrapper .k-tabstrip-items {
85+
max-width: calc(100% - var(--add-tab-button-size));
86+
}
87+
88+
.dynamic-tabstrip-wrapper .add-tab-button {
89+
width: var(--add-tab-button-size);
90+
padding-block: 6px;
91+
z-index: 10000;
92+
}
93+
94+
.remove-tab-button {
95+
padding: 0 !important;
96+
}
97+
</style>
98+
99+
@* Move JavaScript code to a JS file *@
100+
<script suppress-error="BL9992">
101+
window.positionAddTabButton = () => {
102+
const tabStripItems = Array.from(document.querySelectorAll(".dynamic-tabstrip-wrapper .k-tabstrip-item"));
103+
const tabStripWrapperWidth = document.querySelector(".dynamic-tabstrip-wrapper").scrollWidth;
104+
105+
let totalWidth = !tabStripItems ? 0 : tabStripItems.reduce(
106+
(accumulator, currentItem) => accumulator + parseFloat(currentItem.getBoundingClientRect().width),
107+
0,
108+
);
109+
110+
const addTabButton = document.querySelector(".add-tab-button");
111+
const addTabButtonWidth = addTabButton.getBoundingClientRect().width;
112+
113+
// assure button is never positioned outside the boundaries of the wrapper
114+
if (totalWidth + addTabButtonWidth > tabStripWrapperWidth) {
115+
totalWidth = tabStripWrapperWidth - addTabButtonWidth;
116+
}
117+
118+
addTabButton.style.left = `${totalWidth}px`;
119+
};
120+
</script>
121+
122+
123+
@code {
124+
private List<Tab> Tabs = new List<Tab>()
125+
{
126+
new Tab { Title = "Tab 1" },
127+
new Tab { Title = "Tab 2" },
128+
new Tab { Title = "Tab 3" }
129+
};
130+
131+
private string ActiveTabId { get; set; } = string.Empty;
132+
133+
private bool ShouldPositionAddButton { get; set; }
134+
135+
private int LastTabNumber { get; set; } = 3;
136+
137+
private void AddTab()
138+
{
139+
Tab tabToAdd = new Tab { Id = Guid.NewGuid().ToString(), Title = $"New Tab {++LastTabNumber}" };
140+
141+
Tabs.Add(tabToAdd);
142+
143+
//In this example, we are always activating the newly added tab. Adjust the logic to activate a different tab if needed.
144+
ActiveTabId = tabToAdd.Id;
145+
146+
ShouldPositionAddButton = true;
147+
}
148+
149+
private void RemoveTab(Tab tab)
150+
{
151+
if (Tabs.Count <= 1)
152+
{
153+
return;
154+
}
155+
156+
// Activate the tab after or before the removed one if it's active
157+
if (ActiveTabId == tab.Id)
158+
{
159+
int removedTabIndex = Tabs.FindIndex(x => x.Id == tab.Id);
160+
if (removedTabIndex == Tabs.Count - 1)
161+
{
162+
ActiveTabId = Tabs.ElementAt(removedTabIndex - 1).Id;
163+
}
164+
else
165+
{
166+
ActiveTabId = Tabs.ElementAt(removedTabIndex + 1).Id;
167+
}
168+
}
169+
170+
Tabs.Remove(tab);
171+
172+
ShouldPositionAddButton = true;
173+
}
174+
175+
protected override async Task OnAfterRenderAsync(bool firstRender)
176+
{
177+
if (firstRender || ShouldPositionAddButton)
178+
{
179+
ShouldPositionAddButton = false;
180+
await JS.InvokeVoidAsync("positionAddTabButton");
181+
}
182+
183+
await base.OnAfterRenderAsync(firstRender);
184+
}
185+
186+
public class Tab
187+
{
188+
public string Id { get; set; } = Guid.NewGuid().ToString();
189+
190+
public string Title { get; set; } = string.Empty;
191+
}
192+
193+
}
194+
````
195+
196+
## See Also
197+
198+
* [Dynamic Tab Collection](slug:tabstrip-tabs-collection)
199+
* [TabStrip Tab `HeaderTemplate`](slug:tabstrip-header-template)

0 commit comments

Comments
 (0)