Skip to content

Commit a2da6b4

Browse files
authored
feat: custom tab label (#91)
1 parent a78ef1a commit a2da6b4

File tree

3 files changed

+126
-29
lines changed

3 files changed

+126
-29
lines changed

demo_markdown/docs/tabs/mod.md

+34-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,33 @@ view! {
1515
}
1616
```
1717

18+
### Custom tab label
19+
20+
```rust demo
21+
use leptos_meta::Style;
22+
let value = create_rw_signal(String::from("apple"));
23+
24+
view! {
25+
<Style id="demo-tab-label">
26+
".p-0 { padding: 0 }"
27+
</Style>
28+
<Tabs value>
29+
<Tab key="apple">
30+
<TabLabel slot class="p-0">
31+
"🍎 Apple"
32+
</TabLabel>
33+
"apple"
34+
</Tab>
35+
<Tab key="pear">
36+
<TabLabel slot>
37+
"🍐 Pear"
38+
</TabLabel>
39+
"pear"
40+
</Tab>
41+
</Tabs>
42+
}
43+
```
44+
1845
### Tabs Props
1946

2047
| Name | Type | Default | Description |
@@ -29,5 +56,11 @@ view! {
2956
| -------- | --------------------- | -------------------- | -------------------------------------- |
3057
| class | `MaybeSignal<String>` | `Default::default()` | Addtional classes for the tab element. |
3158
| key | `String` | | The indentifier of the tab. |
32-
| label | `String` | | The label of the tab. |
59+
| label | `String` | `Default::default()` | The label of the tab. |
3360
| children | `Children` | | Tabs content. |
61+
62+
### Tab Slots
63+
64+
| Name | Default | Description |
65+
| -------- | ------- | -------------- |
66+
| TabLabel | `None` | label content. |

thaw/src/tabs/mod.rs

+49-20
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,13 @@ fn TabsInner(
6262

6363
view! {
6464
<div class=class_list!["thaw-tabs", move || class.get()] style=move || css_vars.get()>
65-
<div class="thaw-tabs__label-list" ref=label_list_ref>
65+
<div class="thaw-tabs__label-list" ref=label_list_ref role="tablist">
6666
<For
6767
each=move || tab_options_vec.get()
6868
key=move |v| v.key.clone()
6969
children=move |option| {
7070
let label_ref = create_node_ref::<html::Span>();
71-
let TabOption { key, label } = option;
71+
let TabOption { key, label, label_view } = option;
7272
create_effect({
7373
let key = key.clone();
7474
move |_| {
@@ -93,26 +93,55 @@ fn TabsInner(
9393
}
9494
}
9595
});
96-
view! {
97-
<span
98-
class="thaw-tabs__label"
99-
class=(
100-
"thaw-tabs__label--active",
101-
{
96+
let is_active = create_memo({
97+
let key = key.clone();
98+
move |_| key == value.get()
99+
});
100+
if let Some(label_view) = label_view {
101+
let TabLabelView { class, children } = label_view;
102+
view! {
103+
<span
104+
class=class_list![
105+
"thaw-tabs__label", ("thaw-tabs__label--active", move ||
106+
is_active.get()), move || class.get()
107+
]
108+
109+
on:click={
102110
let key = key.clone();
103-
move || key == value.get()
104-
},
105-
)
106-
107-
on:click={
108-
let key = key.clone();
109-
move |_| value.set(key.clone())
110-
}
111+
move |_| value.set(key.clone())
112+
}
113+
114+
ref=label_ref
115+
role="tab"
116+
aria-selected=move || {
117+
if is_active.get() { "true" } else { "false" }
118+
}
119+
>
120+
121+
{children}
122+
</span>
123+
}
124+
} else {
125+
view! {
126+
<span
127+
class="thaw-tabs__label"
128+
class=("thaw-tabs__label--active", move || is_active.get())
111129

112-
ref=label_ref
113-
>
114-
{label}
115-
</span>
130+
on:click={
131+
let key = key.clone();
132+
move |_| value.set(key.clone())
133+
}
134+
135+
ref=label_ref
136+
role="tab"
137+
aria-selected=move || {
138+
if is_active.get() { "true" } else { "false" }
139+
}
140+
>
141+
142+
{if label.is_empty() { key } else { label }}
143+
</span>
144+
}
116145
}
117146
}
118147
/>

thaw/src/tabs/tab.rs

+43-8
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,37 @@ use leptos::*;
66
pub(crate) struct TabOption {
77
pub key: String,
88
pub label: String,
9+
pub label_view: Option<TabLabelView>,
10+
}
11+
12+
#[derive(Clone)]
13+
pub(crate) struct TabLabelView {
14+
pub class: MaybeSignal<String>,
15+
pub children: Fragment,
16+
}
17+
18+
impl From<TabLabel> for TabLabelView {
19+
fn from(tab_label: TabLabel) -> Self {
20+
let TabLabel { class, children } = tab_label;
21+
Self {
22+
class,
23+
children: children(),
24+
}
25+
}
26+
}
27+
28+
#[slot]
29+
pub struct TabLabel {
30+
#[prop(optional, into)]
31+
class: MaybeSignal<String>,
32+
children: Children,
933
}
1034

1135
#[component]
1236
pub fn Tab(
1337
#[prop(into)] key: String,
14-
#[prop(into)] label: String,
38+
#[prop(optional, into)] label: String,
39+
#[prop(optional)] tab_label: Option<TabLabel>,
1540
#[prop(optional, into)] class: MaybeSignal<String>,
1641
children: Children,
1742
) -> impl IntoView {
@@ -20,19 +45,29 @@ pub fn Tab(
2045
tabs.push_tab_options(TabOption {
2146
key: key.clone(),
2247
label,
48+
label_view: tab_label.map(|label| label.into()),
2349
});
2450

25-
on_cleanup({
51+
let is_active = create_memo({
2652
let key = key.clone();
2753
let tabs = tabs.clone();
28-
move || {
29-
tabs.remove_tab_options(&key);
30-
}
54+
move |_| key == tabs.get_key()
55+
});
56+
57+
on_cleanup(move || {
58+
tabs.remove_tab_options(&key);
3159
});
3260

3361
view! {
34-
<div class=class_list![
35-
"thaw-tab", ("thaw-tab--hidden", move || key != tabs.get_key()), move || class.get()
36-
]>{children()}</div>
62+
<div
63+
class=class_list![
64+
"thaw-tab", ("thaw-tab--hidden", move || ! is_active.get()), move || class.get()
65+
]
66+
67+
role="tabpanel"
68+
aria-hidden=move || if is_active.get() { "false" } else { "true" }
69+
>
70+
{children()}
71+
</div>
3772
}
3873
}

0 commit comments

Comments
 (0)