mirror of
https://github.com/adoyle0/thaw.git
synced 2025-01-22 22:09:22 -05:00
feat: adds NavCategory
This commit is contained in:
parent
a11538ee5b
commit
5256c9275c
5 changed files with 185 additions and 17 deletions
|
@ -1,5 +1,7 @@
|
|||
mod nav_category;
|
||||
mod nav_drawer;
|
||||
mod nav_item;
|
||||
|
||||
pub use nav_category::*;
|
||||
pub use nav_drawer::*;
|
||||
pub use nav_item::*;
|
||||
|
|
|
@ -38,7 +38,8 @@
|
|||
z-index: 2;
|
||||
}
|
||||
|
||||
.thaw-nav-item {
|
||||
.thaw-nav-item,
|
||||
.thaw-nav-category-item {
|
||||
display: flex;
|
||||
text-transform: none;
|
||||
position: relative;
|
||||
|
@ -58,11 +59,13 @@
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
.thaw-nav-item:hover {
|
||||
.thaw-nav-item:hover,
|
||||
.thaw-nav-category-item:hover {
|
||||
background-color: var(--colorNeutralBackground4Hover);
|
||||
}
|
||||
|
||||
.thaw-nav-item:active {
|
||||
.thaw-nav-item:active,
|
||||
.thaw-nav-category-item:active {
|
||||
background-color: var(--colorNeutralBackground4Pressed);
|
||||
}
|
||||
|
||||
|
@ -75,3 +78,32 @@
|
|||
border-radius: var(--borderRadiusCircular);
|
||||
margin-inline-start: -18px;
|
||||
}
|
||||
|
||||
.thaw-nav-sub-item-group-enter-from,
|
||||
.thaw-nav-sub-item-group-enter-to {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.thaw-nav-sub-item-group-leave-to,
|
||||
.thaw-nav-sub-item-group-enter-from {
|
||||
opacity: 0;
|
||||
max-height: 0;
|
||||
}
|
||||
|
||||
.thaw-nav-sub-item-group-leave-active {
|
||||
overflow: hidden;
|
||||
transition: max-height 0.15s cubic-bezier(0.4, 0, 0.2, 1) 0s,
|
||||
opacity 0.15s cubic-bezier(0, 0, 0.2, 1) 0s,
|
||||
padding-top 0.15s cubic-bezier(0.4, 0, 0.2, 1) 0s;
|
||||
}
|
||||
|
||||
.thaw-nav-sub-item-group-enter-active {
|
||||
overflow: hidden;
|
||||
transition: max-height 0.15s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
opacity 0.15s cubic-bezier(0.4, 0, 1, 1),
|
||||
padding-top 0.15s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.thaw-nav-sub-item {
|
||||
padding-inline-start: 46px;
|
||||
}
|
||||
|
|
99
thaw/src/nav/nav_category.rs
Normal file
99
thaw/src/nav/nav_category.rs
Normal file
|
@ -0,0 +1,99 @@
|
|||
use super::NavDrawerInjection;
|
||||
use crate::Icon;
|
||||
use leptos::{either::Either, html, prelude::*};
|
||||
use thaw_components::CSSTransition;
|
||||
use thaw_utils::{class_list, StoredMaybeSignal};
|
||||
|
||||
#[component]
|
||||
pub fn NavCategory(
|
||||
#[prop(into)] value: MaybeSignal<String>,
|
||||
children: Children,
|
||||
nav_category_item: NavCategoryItem,
|
||||
) -> impl IntoView {
|
||||
let nav_drawer = NavDrawerInjection::expect_context();
|
||||
let value: StoredMaybeSignal<_> = value.into();
|
||||
let group_ref = NodeRef::<html::Div>::new();
|
||||
let on_click = move |_| {
|
||||
let value = value.get_untracked();
|
||||
if nav_drawer
|
||||
.selected_category_value
|
||||
.with_untracked(|selected_category_value| selected_category_value != Some(&value))
|
||||
{
|
||||
nav_drawer.selected_category_value.set(Some(value));
|
||||
}
|
||||
};
|
||||
|
||||
let is_show_group = Memo::new(move |_| {
|
||||
nav_drawer
|
||||
.selected_category_value
|
||||
.with_untracked(|selected_category_value| {
|
||||
value.with_untracked(|value| selected_category_value == Some(value))
|
||||
})
|
||||
});
|
||||
|
||||
let NavCategoryItem {
|
||||
class: item_class,
|
||||
icon: item_icon,
|
||||
children: item_children,
|
||||
} = nav_category_item;
|
||||
|
||||
view! {
|
||||
<button
|
||||
class=class_list!["thaw-nav-category-item", item_class]
|
||||
on:click=on_click
|
||||
aria-expanded=move || is_show_group.get()
|
||||
>
|
||||
{
|
||||
move || {
|
||||
if let Some(icon) = item_icon.get() {
|
||||
Either::Left(view! {
|
||||
<Icon icon=icon style="font-size: 20px"/>
|
||||
})
|
||||
} else {
|
||||
Either::Right(())
|
||||
}
|
||||
}
|
||||
}
|
||||
{item_children()}
|
||||
<span aria-hidden="true" class="thaw-nav-category-item__expand-icon">
|
||||
<svg
|
||||
fill="currentColor"
|
||||
aria-hidden="true"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
style=move || if is_show_group.get() {
|
||||
"transform: rotate(90deg)"
|
||||
} else {
|
||||
"transform: rotate(0deg)"
|
||||
}
|
||||
>
|
||||
<path d="M7.65 4.15c.2-.2.5-.2.7 0l5.49 5.46c.21.22.21.57 0 .78l-5.49 5.46a.5.5 0 0 1-.7-.7L12.8 10 7.65 4.85a.5.5 0 0 1 0-.7Z" fill="currentColor"></path>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
<CSSTransition
|
||||
node_ref=group_ref
|
||||
show=is_show_group
|
||||
name="thaw-nav-sub-item-group"
|
||||
let:display
|
||||
>
|
||||
<div
|
||||
class="thaw-nav-sub-item-group"
|
||||
node_ref=group_ref
|
||||
style=move || display.get().unwrap_or_default()
|
||||
>
|
||||
{children()}
|
||||
</div>
|
||||
</CSSTransition>
|
||||
}
|
||||
}
|
||||
|
||||
#[slot]
|
||||
pub struct NavCategoryItem {
|
||||
#[prop(optional, into)]
|
||||
class: MaybeProp<String>,
|
||||
#[prop(optional, into)]
|
||||
icon: MaybeProp<icondata_core::Icon>,
|
||||
children: Children,
|
||||
}
|
|
@ -1,12 +1,13 @@
|
|||
use crate::Scrollbar;
|
||||
use leptos::{context::Provider, prelude::*};
|
||||
use thaw_components::OptionComp;
|
||||
use thaw_utils::{class_list, mount_style, Model};
|
||||
use thaw_utils::{class_list, mount_style, OptionModel};
|
||||
|
||||
#[component]
|
||||
pub fn NavDrawer(
|
||||
#[prop(optional, into)] class: MaybeProp<String>,
|
||||
#[prop(optional, into)] selected_value: Model<String>,
|
||||
#[prop(optional, into)] selected_value: OptionModel<String>,
|
||||
#[prop(optional, into)] selected_category_value: OptionModel<String>,
|
||||
children: Children,
|
||||
#[prop(optional)] nav_drawer_header: Option<NavDrawerHeader>,
|
||||
#[prop(optional)] nav_drawer_footer: Option<NavDrawerFooter>,
|
||||
|
@ -14,7 +15,7 @@ pub fn NavDrawer(
|
|||
mount_style("nav-drawer", include_str!("./nav-drawer.css"));
|
||||
|
||||
view! {
|
||||
<Provider value=NavDrawerInjection(selected_value)>
|
||||
<Provider value=NavDrawerInjection{ selected_value, selected_category_value }>
|
||||
<div class=class_list!["thaw-nav-drawer", class]>
|
||||
<OptionComp value=nav_drawer_header let:header>
|
||||
<header class="thaw-nav-drawer__header">{(header.children)()}</header>
|
||||
|
@ -43,8 +44,13 @@ pub struct NavDrawerFooter {
|
|||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct NavDrawerInjection(pub Model<String>);
|
||||
|
||||
pub(crate) fn use_nav_drawer() -> NavDrawerInjection {
|
||||
expect_context()
|
||||
pub(crate) struct NavDrawerInjection {
|
||||
pub selected_value: OptionModel<String>,
|
||||
pub selected_category_value: OptionModel<String>,
|
||||
}
|
||||
|
||||
impl NavDrawerInjection {
|
||||
pub fn expect_context() -> Self {
|
||||
expect_context()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::{use_nav_drawer, Icon};
|
||||
use super::NavDrawerInjection;
|
||||
use crate::Icon;
|
||||
use leptos::{either::Either, prelude::*};
|
||||
use thaw_utils::{class_list, StoredMaybeSignal};
|
||||
|
||||
|
@ -10,12 +11,15 @@ pub fn NavItem(
|
|||
#[prop(optional, into)] href: Option<MaybeSignal<String>>,
|
||||
children: Children,
|
||||
) -> impl IntoView {
|
||||
let nav_drawer = use_nav_drawer();
|
||||
let nav_drawer = NavDrawerInjection::expect_context();
|
||||
let value: StoredMaybeSignal<_> = value.into();
|
||||
let on_click = move |_| {
|
||||
let value = value.get();
|
||||
if nav_drawer.0.with(|key| key != &value) {
|
||||
nav_drawer.0.set(value);
|
||||
let value = value.get_untracked();
|
||||
if nav_drawer
|
||||
.selected_value
|
||||
.with_untracked(|selected_value| selected_value != Some(&value))
|
||||
{
|
||||
nav_drawer.selected_value.set(Some(value));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -36,10 +40,16 @@ pub fn NavItem(
|
|||
}
|
||||
};
|
||||
|
||||
let selected = Memo::new(move |_| {
|
||||
nav_drawer.selected_value.with_untracked(|selected_value| {
|
||||
value.with_untracked(|value| selected_value == Some(value))
|
||||
})
|
||||
});
|
||||
|
||||
if let Some(href) = href {
|
||||
Either::Left(view! {
|
||||
<a
|
||||
class=class_list!["thaw-nav-item", ("thaw-nav-item--selected", move || nav_drawer.0.get() == value.get()), class]
|
||||
class=class_list!["thaw-nav-item", ("thaw-nav-item--selected", move || selected.get()), class]
|
||||
href=move || href.get()
|
||||
on:click=on_click
|
||||
>
|
||||
|
@ -49,7 +59,7 @@ pub fn NavItem(
|
|||
} else {
|
||||
Either::Right(view! {
|
||||
<button
|
||||
class=class_list!["thaw-nav-item", ("thaw-nav-item--selected", move || nav_drawer.0.get() == value.get()), class]
|
||||
class=class_list!["thaw-nav-item", ("thaw-nav-item--selected", move || selected.get()), class]
|
||||
on:click=on_click
|
||||
>
|
||||
{children()}
|
||||
|
@ -57,3 +67,22 @@ pub fn NavItem(
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn NavSubItem(
|
||||
#[prop(optional, into)] class: MaybeProp<String>,
|
||||
#[prop(optional, into)] icon: MaybeProp<icondata_core::Icon>,
|
||||
#[prop(into)] value: MaybeSignal<String>,
|
||||
#[prop(optional, into)] href: Option<MaybeSignal<String>>,
|
||||
children: Children,
|
||||
) -> impl IntoView {
|
||||
let class = MaybeProp::derive(move || {
|
||||
format!("thaw-nav-sub-item {}", class.get().unwrap_or_default()).into()
|
||||
});
|
||||
|
||||
if let Some(href) = href {
|
||||
Either::Left(view! { <NavItem class href icon value children/> })
|
||||
} else {
|
||||
Either::Right(view! { <NavItem class icon value children/> })
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue