tab: Refactor creation to use builder pattern (#1553)

## Breaking change

```diff
- Tab::new("Account")
+ Tab::new().label("Account")
```

We can currently create a tab item without a label, such as only an
icon.
This commit is contained in:
Floyd Wang 2025-11-11 10:35:00 +08:00 committed by GitHub
parent 9228dbfb13
commit 74f23bfca8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 76 additions and 74 deletions

View file

@ -144,14 +144,14 @@ impl Render for TabsStory {
.icon(IconName::ArrowRight), .icon(IconName::ArrowRight),
), ),
) )
.child(Tab::new("Account")) .child(Tab::new().label("Account"))
.child(Tab::new("Profile").disabled(true)) .child(Tab::new().label("Profile").disabled(true))
.child(Tab::new("Documents")) .child(Tab::new().label("Documents"))
.child(Tab::new("Mail")) .child(Tab::new().label("Mail"))
.child(Tab::new("Appearance")) .child(Tab::new().label("Appearance"))
.child(Tab::new("Settings")) .child(Tab::new().label("Settings"))
.child(Tab::new("About")) .child(Tab::new().label("About"))
.child(Tab::new("License")) .child(Tab::new().label("License"))
.suffix( .suffix(
h_flex() h_flex()
.mx_1() .mx_1()
@ -197,14 +197,14 @@ impl Render for TabsStory {
.on_click(cx.listener(|this, ix: &usize, window, cx| { .on_click(cx.listener(|this, ix: &usize, window, cx| {
this.set_active_tab(*ix, window, cx); this.set_active_tab(*ix, window, cx);
})) }))
.child(Tab::new("Account")) .child(Tab::new().label("Account"))
.child(Tab::new("Profile").disabled(true)) .child(Tab::new().label("Profile").disabled(true))
.child(Tab::new("Documents & Files")) .child(Tab::new().label("Documents & Files"))
.child(Tab::new("Mail")) .child(Tab::new().label("Mail"))
.child(Tab::new("Appearance")) .child(Tab::new().label("Appearance"))
.child(Tab::new("Settings")) .child(Tab::new().label("Settings"))
.child(Tab::new("About")) .child(Tab::new().label("About"))
.child(Tab::new("License")), .child(Tab::new().label("License")),
), ),
) )
.child( .child(
@ -218,14 +218,14 @@ impl Render for TabsStory {
.on_click(cx.listener(|this, ix: &usize, window, cx| { .on_click(cx.listener(|this, ix: &usize, window, cx| {
this.set_active_tab(*ix, window, cx); this.set_active_tab(*ix, window, cx);
})) }))
.child(Tab::new("Account")) .child(Tab::new().label("Account"))
.child(Tab::new("Profile").disabled(true)) .child(Tab::new().label("Profile").disabled(true))
.child(Tab::new("Documents & Files")) .child(Tab::new().label("Documents & Files"))
.child(Tab::new("Mail")) .child(Tab::new().label("Mail"))
.child(Tab::new("Appearance")) .child(Tab::new().label("Appearance"))
.child(Tab::new("Settings")) .child(Tab::new().label("Settings"))
.child(Tab::new("About")) .child(Tab::new().label("About"))
.child(Tab::new("License")), .child(Tab::new().label("License")),
), ),
) )
.child( .child(

View file

@ -398,21 +398,19 @@ pub struct Tab {
impl From<&'static str> for Tab { impl From<&'static str> for Tab {
fn from(label: &'static str) -> Self { fn from(label: &'static str) -> Self {
let label = SharedString::from(label); Self::new().label(label)
Self::new(label)
} }
} }
impl From<String> for Tab { impl From<String> for Tab {
fn from(label: String) -> Self { fn from(label: String) -> Self {
let label = SharedString::from(label); Self::new().label(label)
Self::new(label)
} }
} }
impl From<SharedString> for Tab { impl From<SharedString> for Tab {
fn from(label: SharedString) -> Self { fn from(label: SharedString) -> Self {
Self::new(label) Self::new().label(label)
} }
} }
@ -449,10 +447,14 @@ impl Default for Tab {
impl Tab { impl Tab {
/// Create a new tab with a label. /// Create a new tab with a label.
pub fn new(label: impl Into<SharedString>) -> Self { pub fn new() -> Self {
let mut this = Self::default(); Self::default()
this.label = Some(label.into()); }
this
/// Set label for the tab.
pub fn label(mut self, label: impl Into<SharedString>) -> Self {
self.label = Some(label.into());
self
} }
/// Set icon for the tab. /// Set icon for the tab.

View file

@ -23,9 +23,9 @@ TabBar::new("tabs")
.on_click(|selected_index, _, _| { .on_click(|selected_index, _, _| {
println!("Tab {} selected", selected_index); println!("Tab {} selected", selected_index);
}) })
.child(Tab::new("Account")) .child(Tab::new().label("Account"))
.child(Tab::new("Profile")) .child(Tab::new().label("Profile"))
.child(Tab::new("Settings")) .child(Tab::new().label("Settings"))
``` ```
### Tab Variants ### Tab Variants
@ -35,9 +35,9 @@ TabBar::new("tabs")
```rust ```rust
TabBar::new("default-tabs") TabBar::new("default-tabs")
.selected_index(0) .selected_index(0)
.child(Tab::new("Account")) .child(Tab::new().label("Account"))
.child(Tab::new("Profile")) .child(Tab::new().label("Profile"))
.child(Tab::new("Documents")) .child(Tab::new().label("Documents"))
``` ```
#### Underline Tabs #### Underline Tabs
@ -46,9 +46,9 @@ TabBar::new("default-tabs")
TabBar::new("underline-tabs") TabBar::new("underline-tabs")
.underline() .underline()
.selected_index(0) .selected_index(0)
.child("Account") .child(Tab::new().label("Account"))
.child("Profile") .child(Tab::new().label("Profile"))
.child("Documents") .child(Tab::new().label("Documents"))
``` ```
#### Pill Tabs #### Pill Tabs
@ -57,9 +57,9 @@ TabBar::new("underline-tabs")
TabBar::new("pill-tabs") TabBar::new("pill-tabs")
.pill() .pill()
.selected_index(0) .selected_index(0)
.child(Tab::new("Account")) .child(Tab::new().label("Account"))
.child(Tab::new("Profile")) .child(Tab::new().label("Profile"))
.child(Tab::new("Documents")) .child(Tab::new().label("Documents"))
``` ```
#### Outline Tabs #### Outline Tabs
@ -68,9 +68,9 @@ TabBar::new("pill-tabs")
TabBar::new("outline-tabs") TabBar::new("outline-tabs")
.outline() .outline()
.selected_index(0) .selected_index(0)
.child(Tab::new("Account")) .child(Tab::new().label("Account"))
.child(Tab::new("Profile")) .child(Tab::new().label("Profile"))
.child(Tab::new("Documents")) .child(Tab::new().label("Documents"))
``` ```
#### Segmented Tabs #### Segmented Tabs
@ -92,19 +92,19 @@ TabBar::new("segmented-tabs")
```rust ```rust
// Extra Small // Extra Small
TabBar::new("tabs").xsmall() TabBar::new("tabs").xsmall()
.child(Tab::new("Small")) .child(Tab::new().label("Small"))
// Small // Small
TabBar::new("tabs").small() TabBar::new("tabs").small()
.child(Tab::new("Small")) .child(Tab::new().label("Small"))
// Medium (default) // Medium (default)
TabBar::new("tabs") TabBar::new("tabs")
.child(Tab::new("Medium")) .child(Tab::new().label("Medium"))
// Large // Large
TabBar::new("tabs").large() TabBar::new("tabs").large()
.child(Tab::new("Large")) .child(Tab::new().label("Large"))
``` ```
### Tabs with Icons ### Tabs with Icons
@ -137,18 +137,18 @@ TabBar::new("tabs-with-controls")
.child(Button::new("inbox").ghost().xsmall().icon(IconName::Inbox)) .child(Button::new("inbox").ghost().xsmall().icon(IconName::Inbox))
.child(Button::new("more").ghost().xsmall().icon(IconName::Ellipsis)) .child(Button::new("more").ghost().xsmall().icon(IconName::Ellipsis))
) )
.child(Tab::new("Account")) .child(Tab::new().label("Account"))
.child(Tab::new("Profile")) .child(Tab::new().label("Profile"))
.child(Tab::new("Settings")) .child(Tab::new().label("Settings"))
``` ```
### Disabled Tabs ### Disabled Tabs
```rust ```rust
TabBar::new("tabs-with-disabled") TabBar::new("tabs-with-disabled")
.child(Tab::new("Account")) .child(Tab::new().label("Account"))
.child(Tab::new("Profile").disabled(true)) .child(Tab::new().label("Profile").disabled(true))
.child(Tab::new("Settings")) .child(Tab::new().label("Settings"))
``` ```
### Dynamic Tabs ### Dynamic Tabs
@ -170,7 +170,7 @@ impl Render for TabsView {
.children( .children(
self.tabs self.tabs
.iter() .iter()
.map(|tab_name| Tab::new(tab_name.clone())) .map(|tab_name| Tab::new().label(tab_name.clone()))
) )
} }
} }
@ -187,11 +187,11 @@ If enable, the will have a dropdown button at the end of the tab bar to show all
TabBar::new("tabs-with-menu") TabBar::new("tabs-with-menu")
.menu(true) .menu(true)
.selected_index(0) .selected_index(0)
.child(Tab::new("Account")) .child(Tab::new().label("Account"))
.child(Tab::new("Profile")) .child(Tab::new().label("Profile"))
.child(Tab::new("Documents")) .child(Tab::new().label("Documents"))
.child(Tab::new("Mail")) .child(Tab::new().label("Mail"))
.child(Tab::new("Settings")) .child(Tab::new().label("Settings"))
``` ```
### Scrollable Tabs ### Scrollable Tabs
@ -207,11 +207,11 @@ impl Render for ScrollableTabsView {
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement { fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
TabBar::new("scrollable-tabs") TabBar::new("scrollable-tabs")
.track_scroll(&self.scroll_handle) .track_scroll(&self.scroll_handle)
.child(Tab::new("Very Long Tab Name 1")) .child(Tab::new().label("Very Long Tab Name 1"))
.child(Tab::new("Very Long Tab Name 2")) .child(Tab::new().label("Very Long Tab Name 2"))
.child(Tab::new("Very Long Tab Name 3")) .child(Tab::new().label("Very Long Tab Name 3"))
.child(Tab::new("Very Long Tab Name 4")) .child(Tab::new().label("Very Long Tab Name 4"))
.child(Tab::new("Very Long Tab Name 5")) .child(Tab::new().label("Very Long Tab Name 5"))
} }
} }
``` ```
@ -221,7 +221,7 @@ impl Render for ScrollableTabsView {
```rust ```rust
TabBar::new("custom-tabs") TabBar::new("custom-tabs")
.child( .child(
Tab::new("Custom Tab") Tab::new().label("Custom Tab")
.id("custom-id") .id("custom-id")
.prefix(IconName::Star) .prefix(IconName::Star)
.suffix(IconName::X) .suffix(IconName::X)
@ -352,9 +352,9 @@ impl Render for TabsWithContent {
view.active_tab = *index; view.active_tab = *index;
cx.notify(); cx.notify();
})) }))
.child(Tab::new("Account")) .child(Tab::new().label("Account"))
.child(Tab::new("Profile")) .child(Tab::new().label("Profile"))
.child(Tab::new("Settings")) .child(Tab::new().label("Settings"))
) )
.child( .child(
div() div()
@ -401,7 +401,7 @@ impl Render for CloseableTabsView {
.iter() .iter()
.enumerate() .enumerate()
.map(|(index, tab_name)| { .map(|(index, tab_name)| {
Tab::new(tab_name.clone()) Tab::new().label(tab_name.clone())
.suffix( .suffix(
Button::new(format!("close-{}", index)) Button::new(format!("close-{}", index))
.icon(IconName::X) .icon(IconName::X)