feat: use if component

This commit is contained in:
luoxiao 2023-06-10 12:40:52 +08:00
parent 4e0b338a65
commit 933a9c7316
13 changed files with 105 additions and 111 deletions

View file

@ -43,6 +43,9 @@ pub fn App(cx: Scope) -> impl IntoView {
<Route path="/button" view=move |cx| view! {cx,
<ButtonPage />
} />
<Route path="/checkbox" view=move |cx| view! {cx,
<CheckboxPage />
} />
</Route>
</Routes>
<Routes base="/mobile".to_string()>

View file

@ -1,20 +0,0 @@
use leptos::*;
use melt_ui::*;
#[component]
pub fn DemoCheckout(cx: Scope) -> impl IntoView {
let (checked, set_checked) = create_signal(cx, false);
view! {cx,
<div>
<Checkbox>
"A"
</Checkbox>
<Checkbox checked=true>
"B"
</Checkbox>
<Checkbox checked=checked on_checked=set_checked>
"Click"
</Checkbox>
</div>
}
}

View file

@ -1,13 +0,0 @@
use leptos::*;
use melt_ui::*;
#[component]
pub fn DemoSlider(cx: Scope) -> impl IntoView {
let (value, set_value) = create_signal(cx, 0.0);
let on_value = SignalSetter::map(cx, move |value| {
set_value.set(value);
});
view! { cx,
<Slider value=value on_value=on_value/>
}
}

View file

@ -1,6 +1,4 @@
mod app;
mod demo_checkbox;
mod demo_slider;
mod pages;
use app::*;

View file

@ -0,0 +1,14 @@
use leptos::*;
use melt_ui::*;
#[component]
pub fn CheckboxPage(cx: Scope) -> impl IntoView {
let checked = create_rw_signal(cx, false);
view! {cx,
<div>
<Checkbox checked>
"Click"
</Checkbox>
</div>
}
}

View file

@ -41,6 +41,7 @@ pub fn ComponentsPage(cx: Scope) -> impl IntoView {
<MenuItem key="modal" label="madal" />
<MenuItem key="nav-bar" label="nav-bar" />
<MenuItem key="button" label="button" />
<MenuItem key="checkbox" label="checkbox" />
</Menu>
</aside>
<main>

View file

@ -1,5 +1,3 @@
use crate::demo_checkbox::*;
use crate::demo_slider::*;
use leptos::*;
use leptos_router::use_navigate;
use melt_ui::*;
@ -38,9 +36,5 @@ pub fn Home(cx: Scope) -> impl IntoView {
<Progress percentage=count/>
</Space>
<hr />
<DemoCheckout />
<hr />
<DemoSlider />
}
}

View file

@ -1,4 +1,5 @@
mod button;
mod checkbox;
mod components;
mod home;
mod image;
@ -11,6 +12,7 @@ mod slider;
mod tabbar;
pub use button::*;
pub use checkbox::*;
pub use components::*;
pub use home::*;
pub use image::*;

View file

@ -1,51 +1,75 @@
use std::rc::Rc;
use crate::{components::*, utils::mount_style::mount_style};
use leptos::*;
use stylers::style_sheet_str;
#[slot]
pub struct CardHeader {
children: ChildrenFn,
}
#[slot]
pub struct CardHeaderExtra {
children: ChildrenFn,
}
#[slot]
pub struct CardFooter {
#[prop(default = leptos::MaybeSignal::Static(true), into)]
if_: MaybeSignal<bool>,
children: ChildrenFn,
}
#[component]
pub fn Card(
cx: Scope,
#[prop(optional)] title: MaybeSignal<String>,
#[prop(default = None)] header: Option<Children>,
#[prop(default = None)] header_extra: Option<Children>,
#[prop(optional, into)] title: MaybeSignal<&'static str>,
#[prop(optional)] card_header: Option<CardHeader>,
#[prop(optional)] card_header_extra: Option<CardHeaderExtra>,
children: Children,
#[prop(default = None)] footer: Option<Children>,
#[prop(optional)] card_footer: Option<CardFooter>,
) -> impl IntoView {
let class_name = mount_style("card", || style_sheet_str!("./src/card/card.css"));
let is_header = card_header.is_some();
let header = card_header.map_or(None, |v| Some(Rc::new(v)));
let header_extra = card_header_extra.map_or(None, |v| Some(Rc::new(v)));
// let footer = card_footer.map_or(None, |v| Some(Rc::new(v)));
view! {
cx, class=class_name,
<div class="melt-card">
{
if header.is_some() || title.get().is_empty() {
view! {
cx, class=class_name,
<If cond=MaybeSignal::derive(cx, move || is_header || title.get().is_empty()) >
<Then slot>
<div class="melt-card__header">
<div class="melt-card__header-content">
<OptionComp value=header bind:header>
<OptionComp value=header.clone() bind:header>
<Fallback slot>
{ title.get() }
</Fallback>
{ header(cx) }
{ (header.children)(cx) }
</OptionComp>
</div>
<OptionComp value=header_extra bind:header_extra>
<OptionComp value=header_extra.clone() bind:header_extra>
<div class="melt-card__header-extra">
{ header_extra(cx) }
{ (header_extra.children)(cx) }
</div>
</OptionComp>
</div>
}.into()
} else {
None
}
}
</Then>
</If>
<div class="melt-card__content">
{ children(cx) }
</div>
<OptionComp value=footer bind:footer>
<div class="melt-card__footer">
{ footer(cx) }
</div>
<OptionComp value=card_footer bind:footer>
<If cond=footer.if_ >
<Then slot>
<div class="melt-card__footer">
{ (footer.children)(cx) }
</div>
</Then>
</If>
</OptionComp>
</div>
}

View file

@ -1,13 +1,12 @@
use crate::{theme::use_theme, utils::mount_style::mount_style, Theme};
use crate::{theme::use_theme, utils::mount_style::mount_style, Theme, components::*};
use leptos::*;
use stylers::style_sheet_str;
use leptos_icons::*;
use stylers::style_sheet_str;
#[component]
pub fn Checkbox(
cx: Scope,
#[prop(optional, into)] checked: MaybeSignal<bool>,
#[prop(optional, into)] on_checked: Option<SignalSetter<bool>>,
#[prop(into)] checked: RwSignal<bool>,
children: Children,
) -> impl IntoView {
let theme = use_theme(cx, Theme::light);
@ -22,29 +21,20 @@ pub fn Checkbox(
css_vars.push_str(&format!("--background-color-checked: {bg_color};"));
css_vars
});
let on_click = move |_| {
if let Some(on_checked) = on_checked {
on_checked.set(!checked.get());
}
};
view! {cx, class=class_name,
<div class:melt-checkbox=true class=("melt-checkbox--checked", move || checked.get()) style=move || css_vars.get() on:click=on_click>
<div class:melt-checkbox=true class=("melt-checkbox--checked", move || checked.get()) style=move || css_vars.get()
on:click=move |_| checked.set(!checked.get_untracked())>
<input class="melt-checkbox__input" type="checkbox" />
<div class="melt-checkbox__dot">
{
move || {
if checked.get() {
view! {cx,
<Icon icon=AiIcon::AiCheckOutlined style="color: white"/>
}.into()
} else {
None
}
}
}
<If cond=checked>
<Then slot>
<Icon icon=AiIcon::AiCheckOutlined style="color: white"/>
</Then>
</If>
</div>
<div class="melt-checkbox__label">
{children(cx)}
{ children(cx) }
</div>
</div>
}

View file

@ -15,7 +15,7 @@ pub struct ElseIf {
#[component]
pub fn If(
cx: Scope,
cond: MaybeSignal<bool>,
#[prop(into)] cond: MaybeSignal<bool>,
then: Then,
#[prop(default=vec![])] else_if: Vec<ElseIf>,
#[prop(optional)] fallback: Option<Fallback>,

View file

@ -2,8 +2,8 @@ mod if_comp;
mod option_comp;
pub use if_comp::*;
use leptos::*;
pub use option_comp::*;
use leptos::*;
#[slot]
pub struct Fallback {

View file

@ -1,48 +1,49 @@
use crate::card::*;
use crate::components::OptionComp;
use crate::teleport::*;
use crate::utils::mount_style::mount_style;
use leptos::*;
use stylers::style_sheet_str;
use leptos_icons::*;
#[slot]
pub struct ModalFooter {
children: ChildrenFn,
}
#[component]
pub fn Modal(
cx: Scope,
#[prop(optional, into)] title: Option<MaybeSignal<String>>,
children: Children,
#[prop(optional)] footer: Option<Children>,
#[prop(into)] show: RwSignal<bool>,
#[prop(optional, into)] title: MaybeSignal<&'static str>,
children: Children,
#[prop(optional)] modal_footer: Option<ModalFooter>,
) -> impl IntoView {
let class_name = mount_style("modal", || style_sheet_str!("./src/modal/modal.css"));
let header = move |cx| {
view! {
cx, class=class_name,
<>
<span class="melt-model-title">
{title}
</span>
</>
}
};
let header_extra = move |cx| {
view! {
cx,
<>
<span style="cursor: pointer;" on:click=move |_| show.set(false)>
<Icon icon=AiIcon::AiCloseOutlined/>
</span>
</>
}
};
view! {
cx, class=class_name,
<Teleport>
<div class="melt-modal-container" style=move || if show.get() { "" } else { "display: none" }>
<div class="melt-modal-mask"></div>
<div class="melt-modal-body">
<Card header=Some(Box::new(header)) header_extra=Some(Box::new(header_extra)) footer=footer>
{children(cx)}
<Card>
<CardHeader slot>
<span class="melt-model-title">
{ title.get() }
</span>
</CardHeader>
<CardHeaderExtra slot>
<span style="cursor: pointer;" on:click=move |_| show.set(false)>
<Icon icon=AiIcon::AiCloseOutlined/>
</span>
</CardHeaderExtra>
{ children(cx) }
<CardFooter slot if_=modal_footer.is_some()>
<OptionComp value=modal_footer.as_ref() bind:footer>
{ (footer.children)(cx) }
</OptionComp>
</CardFooter>
</Card>
</div>
</div>