mirror of
https://github.com/adoyle0/thaw.git
synced 2025-02-08 19:03:09 -05:00
feat: custom tab label (#91)
This commit is contained in:
parent
a78ef1a8ac
commit
a2da6b4a59
3 changed files with 125 additions and 28 deletions
|
@ -15,6 +15,33 @@ view! {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Custom tab label
|
||||||
|
|
||||||
|
```rust demo
|
||||||
|
use leptos_meta::Style;
|
||||||
|
let value = create_rw_signal(String::from("apple"));
|
||||||
|
|
||||||
|
view! {
|
||||||
|
<Style id="demo-tab-label">
|
||||||
|
".p-0 { padding: 0 }"
|
||||||
|
</Style>
|
||||||
|
<Tabs value>
|
||||||
|
<Tab key="apple">
|
||||||
|
<TabLabel slot class="p-0">
|
||||||
|
"🍎 Apple"
|
||||||
|
</TabLabel>
|
||||||
|
"apple"
|
||||||
|
</Tab>
|
||||||
|
<Tab key="pear">
|
||||||
|
<TabLabel slot>
|
||||||
|
"🍐 Pear"
|
||||||
|
</TabLabel>
|
||||||
|
"pear"
|
||||||
|
</Tab>
|
||||||
|
</Tabs>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Tabs Props
|
### Tabs Props
|
||||||
|
|
||||||
| Name | Type | Default | Description |
|
| Name | Type | Default | Description |
|
||||||
|
@ -29,5 +56,11 @@ view! {
|
||||||
| -------- | --------------------- | -------------------- | -------------------------------------- |
|
| -------- | --------------------- | -------------------- | -------------------------------------- |
|
||||||
| class | `MaybeSignal<String>` | `Default::default()` | Addtional classes for the tab element. |
|
| class | `MaybeSignal<String>` | `Default::default()` | Addtional classes for the tab element. |
|
||||||
| key | `String` | | The indentifier of the tab. |
|
| key | `String` | | The indentifier of the tab. |
|
||||||
| label | `String` | | The label of the tab. |
|
| label | `String` | `Default::default()` | The label of the tab. |
|
||||||
| children | `Children` | | Tabs content. |
|
| children | `Children` | | Tabs content. |
|
||||||
|
|
||||||
|
### Tab Slots
|
||||||
|
|
||||||
|
| Name | Default | Description |
|
||||||
|
| -------- | ------- | -------------- |
|
||||||
|
| TabLabel | `None` | label content. |
|
||||||
|
|
|
@ -62,13 +62,13 @@ fn TabsInner(
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<div class=class_list!["thaw-tabs", move || class.get()] style=move || css_vars.get()>
|
<div class=class_list!["thaw-tabs", move || class.get()] style=move || css_vars.get()>
|
||||||
<div class="thaw-tabs__label-list" ref=label_list_ref>
|
<div class="thaw-tabs__label-list" ref=label_list_ref role="tablist">
|
||||||
<For
|
<For
|
||||||
each=move || tab_options_vec.get()
|
each=move || tab_options_vec.get()
|
||||||
key=move |v| v.key.clone()
|
key=move |v| v.key.clone()
|
||||||
children=move |option| {
|
children=move |option| {
|
||||||
let label_ref = create_node_ref::<html::Span>();
|
let label_ref = create_node_ref::<html::Span>();
|
||||||
let TabOption { key, label } = option;
|
let TabOption { key, label, label_view } = option;
|
||||||
create_effect({
|
create_effect({
|
||||||
let key = key.clone();
|
let key = key.clone();
|
||||||
move |_| {
|
move |_| {
|
||||||
|
@ -93,26 +93,55 @@ fn TabsInner(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
view! {
|
let is_active = create_memo({
|
||||||
<span
|
let key = key.clone();
|
||||||
class="thaw-tabs__label"
|
move |_| key == value.get()
|
||||||
class=(
|
});
|
||||||
"thaw-tabs__label--active",
|
if let Some(label_view) = label_view {
|
||||||
{
|
let TabLabelView { class, children } = label_view;
|
||||||
|
view! {
|
||||||
|
<span
|
||||||
|
class=class_list![
|
||||||
|
"thaw-tabs__label", ("thaw-tabs__label--active", move ||
|
||||||
|
is_active.get()), move || class.get()
|
||||||
|
]
|
||||||
|
|
||||||
|
on:click={
|
||||||
let key = key.clone();
|
let key = key.clone();
|
||||||
move || key == value.get()
|
move |_| value.set(key.clone())
|
||||||
},
|
}
|
||||||
)
|
|
||||||
|
|
||||||
on:click={
|
ref=label_ref
|
||||||
let key = key.clone();
|
role="tab"
|
||||||
move |_| value.set(key.clone())
|
aria-selected=move || {
|
||||||
}
|
if is_active.get() { "true" } else { "false" }
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
ref=label_ref
|
{children}
|
||||||
>
|
</span>
|
||||||
{label}
|
}
|
||||||
</span>
|
} else {
|
||||||
|
view! {
|
||||||
|
<span
|
||||||
|
class="thaw-tabs__label"
|
||||||
|
class=("thaw-tabs__label--active", move || is_active.get())
|
||||||
|
|
||||||
|
on:click={
|
||||||
|
let key = key.clone();
|
||||||
|
move |_| value.set(key.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
ref=label_ref
|
||||||
|
role="tab"
|
||||||
|
aria-selected=move || {
|
||||||
|
if is_active.get() { "true" } else { "false" }
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
{if label.is_empty() { key } else { label }}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -6,12 +6,37 @@ use leptos::*;
|
||||||
pub(crate) struct TabOption {
|
pub(crate) struct TabOption {
|
||||||
pub key: String,
|
pub key: String,
|
||||||
pub label: String,
|
pub label: String,
|
||||||
|
pub label_view: Option<TabLabelView>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub(crate) struct TabLabelView {
|
||||||
|
pub class: MaybeSignal<String>,
|
||||||
|
pub children: Fragment,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<TabLabel> for TabLabelView {
|
||||||
|
fn from(tab_label: TabLabel) -> Self {
|
||||||
|
let TabLabel { class, children } = tab_label;
|
||||||
|
Self {
|
||||||
|
class,
|
||||||
|
children: children(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[slot]
|
||||||
|
pub struct TabLabel {
|
||||||
|
#[prop(optional, into)]
|
||||||
|
class: MaybeSignal<String>,
|
||||||
|
children: Children,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn Tab(
|
pub fn Tab(
|
||||||
#[prop(into)] key: String,
|
#[prop(into)] key: String,
|
||||||
#[prop(into)] label: String,
|
#[prop(optional, into)] label: String,
|
||||||
|
#[prop(optional)] tab_label: Option<TabLabel>,
|
||||||
#[prop(optional, into)] class: MaybeSignal<String>,
|
#[prop(optional, into)] class: MaybeSignal<String>,
|
||||||
children: Children,
|
children: Children,
|
||||||
) -> impl IntoView {
|
) -> impl IntoView {
|
||||||
|
@ -20,19 +45,29 @@ pub fn Tab(
|
||||||
tabs.push_tab_options(TabOption {
|
tabs.push_tab_options(TabOption {
|
||||||
key: key.clone(),
|
key: key.clone(),
|
||||||
label,
|
label,
|
||||||
|
label_view: tab_label.map(|label| label.into()),
|
||||||
});
|
});
|
||||||
|
|
||||||
on_cleanup({
|
let is_active = create_memo({
|
||||||
let key = key.clone();
|
let key = key.clone();
|
||||||
let tabs = tabs.clone();
|
let tabs = tabs.clone();
|
||||||
move || {
|
move |_| key == tabs.get_key()
|
||||||
tabs.remove_tab_options(&key);
|
});
|
||||||
}
|
|
||||||
|
on_cleanup(move || {
|
||||||
|
tabs.remove_tab_options(&key);
|
||||||
});
|
});
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<div class=class_list![
|
<div
|
||||||
"thaw-tab", ("thaw-tab--hidden", move || key != tabs.get_key()), move || class.get()
|
class=class_list![
|
||||||
]>{children()}</div>
|
"thaw-tab", ("thaw-tab--hidden", move || ! is_active.get()), move || class.get()
|
||||||
|
]
|
||||||
|
|
||||||
|
role="tabpanel"
|
||||||
|
aria-hidden=move || if is_active.get() { "false" } else { "true" }
|
||||||
|
>
|
||||||
|
{children()}
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue