mirror of
https://github.com/adoyle0/thaw.git
synced 2025-01-23 06:19:22 -05:00
feat: theme
This commit is contained in:
parent
c974f80ea5
commit
cf18b488db
20 changed files with 295 additions and 133 deletions
|
@ -5,7 +5,22 @@ use melt_ui::*;
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn App() -> impl IntoView {
|
pub fn App() -> impl IntoView {
|
||||||
let theme = create_rw_signal(Theme::light());
|
fn use_query_value(key: &str) -> Option<String> {
|
||||||
|
let href = window().location().href().ok()?;
|
||||||
|
let url = Url::try_from(href.as_str()).ok()?;
|
||||||
|
url.search_params.get(key).cloned()
|
||||||
|
}
|
||||||
|
let theme = use_query_value("theme").map_or_else(Theme::light, |name| {
|
||||||
|
if name == "light" {
|
||||||
|
Theme::light()
|
||||||
|
} else if name == "dark" {
|
||||||
|
Theme::dark()
|
||||||
|
} else {
|
||||||
|
Theme::light()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let theme = create_rw_signal(theme);
|
||||||
|
|
||||||
provide_context(theme);
|
provide_context(theme);
|
||||||
view! {
|
view! {
|
||||||
<Provider theme>
|
<Provider theme>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
use leptos_router::{use_navigate, use_query_map, Url};
|
use leptos_router::{use_navigate, use_query_map};
|
||||||
use melt_ui::*;
|
use melt_ui::*;
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
|
@ -8,19 +8,6 @@ pub fn Home() -> impl IntoView {
|
||||||
// mobile page
|
// mobile page
|
||||||
if let Some(path) = query_map.get("path") {
|
if let Some(path) = query_map.get("path") {
|
||||||
let navigate = use_navigate();
|
let navigate = use_navigate();
|
||||||
if let Some((_, search)) = path.split_once("?") {
|
|
||||||
if let Some((key, value)) = search.split_once("=") {
|
|
||||||
if key == "theme" {
|
|
||||||
let theme = use_rw_theme();
|
|
||||||
let theme_name = theme.with_untracked(|theme| theme.name.clone());
|
|
||||||
if value == "light" && theme_name != "light" {
|
|
||||||
theme.set(Theme::light())
|
|
||||||
} else if value == "dark" && theme_name != "dark" {
|
|
||||||
theme.set(Theme::dark())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
navigate(path, Default::default());
|
navigate(path, Default::default());
|
||||||
}
|
}
|
||||||
view! {
|
view! {
|
||||||
|
|
|
@ -4,7 +4,7 @@ use melt_ui::{use_theme, Theme};
|
||||||
#[component]
|
#[component]
|
||||||
pub fn MobilePage(path: &'static str) -> impl IntoView {
|
pub fn MobilePage(path: &'static str) -> impl IntoView {
|
||||||
let theme = use_theme(Theme::light);
|
let theme = use_theme(Theme::light);
|
||||||
let src = create_memo(move |_| theme.with(|theme| format!("{path}?theme={}", theme.name)));
|
let src = create_memo(move |_| theme.with(|theme| format!("{path}&theme={}", theme.name)));
|
||||||
let style = create_memo(move |_| {
|
let style = create_memo(move |_| {
|
||||||
theme.with(|theme| {
|
theme.with(|theme| {
|
||||||
let mut style = String::from("margin-top: 5vh; width: 350px; height: 680px; border-radius: 16px; box-shadow: 0 6px 16px -9px rgba(0, 0, 0, .08), 0 9px 28px 0 rgba(0, 0, 0, .05), 0 12px 48px 16px rgba(0, 0, 0, .03);");
|
let mut style = String::from("margin-top: 5vh; width: 350px; height: 680px; border-radius: 16px; box-shadow: 0 6px 16px -9px rgba(0, 0, 0, .08), 0 9px 28px 0 rgba(0, 0, 0, .05), 0 12px 48px 16px rgba(0, 0, 0, .03);");
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
max-height: 200px;
|
max-height: 200px;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
background-color: #fff;
|
background-color: var(--melt-background-color);
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
box-shadow: 0 3px 6px -4px rgba(0, 0, 0, 0.12),
|
box-shadow: 0 3px 6px -4px rgba(0, 0, 0, 0.12),
|
||||||
|
@ -16,5 +16,5 @@
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.melt-auto-complete__menu-item:hover {
|
.melt-auto-complete__menu-item:hover {
|
||||||
background-color: #f6f6f7;
|
background-color: var(--melt-background-color-hover);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
use crate::{mount_style, teleport::Teleport, utils::maybe_rw_signal::MaybeRwSignal, Input};
|
mod theme;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
mount_style, teleport::Teleport, use_theme, utils::maybe_rw_signal::MaybeRwSignal, Input, Theme,
|
||||||
|
};
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
|
pub use theme::AutoCompleteTheme;
|
||||||
|
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub struct AutoCompleteOption {
|
pub struct AutoCompleteOption {
|
||||||
|
@ -14,6 +19,22 @@ pub fn AutoComplete(
|
||||||
#[prop(optional, into)] options: MaybeSignal<Vec<AutoCompleteOption>>,
|
#[prop(optional, into)] options: MaybeSignal<Vec<AutoCompleteOption>>,
|
||||||
) -> impl IntoView {
|
) -> impl IntoView {
|
||||||
mount_style("auto-complete", include_str!("./auto-complete.css"));
|
mount_style("auto-complete", include_str!("./auto-complete.css"));
|
||||||
|
let theme = use_theme(Theme::light);
|
||||||
|
let menu_css_vars = create_memo(move |_| {
|
||||||
|
let mut css_vars = String::new();
|
||||||
|
theme.with(|theme| {
|
||||||
|
css_vars.push_str(&format!(
|
||||||
|
"--melt-background-color: {};",
|
||||||
|
theme.select.menu_background_color
|
||||||
|
));
|
||||||
|
css_vars.push_str(&format!(
|
||||||
|
"--melt-background-color-hover: {};",
|
||||||
|
theme.select.menu_background_color_hover
|
||||||
|
));
|
||||||
|
});
|
||||||
|
css_vars
|
||||||
|
});
|
||||||
|
|
||||||
let is_show_menu = create_rw_signal(false);
|
let is_show_menu = create_rw_signal(false);
|
||||||
let auto_complete_ref = create_node_ref::<html::Div>();
|
let auto_complete_ref = create_node_ref::<html::Div>();
|
||||||
let auto_complete_menu_ref = create_node_ref::<html::Div>();
|
let auto_complete_menu_ref = create_node_ref::<html::Div>();
|
||||||
|
@ -58,7 +79,7 @@ pub fn AutoComplete(
|
||||||
<div
|
<div
|
||||||
class="melt-auto-complete__menu"
|
class="melt-auto-complete__menu"
|
||||||
style=move || {
|
style=move || {
|
||||||
if is_show_menu.get() { None } else { Some("display: none;") }
|
if is_show_menu.get() { menu_css_vars.get() } else { "display: none;".to_string() }
|
||||||
}
|
}
|
||||||
|
|
||||||
ref=auto_complete_menu_ref
|
ref=auto_complete_menu_ref
|
||||||
|
|
23
src/auto_complete/theme.rs
Normal file
23
src/auto_complete/theme.rs
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
use crate::theme::ThemeMethod;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct AutoCompleteTheme {
|
||||||
|
pub menu_background_color: String,
|
||||||
|
pub menu_background_color_hover: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ThemeMethod for AutoCompleteTheme {
|
||||||
|
fn light() -> Self {
|
||||||
|
Self {
|
||||||
|
menu_background_color: "#fff".into(),
|
||||||
|
menu_background_color_hover: "#f3f5f6".into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dark() -> Self {
|
||||||
|
Self {
|
||||||
|
menu_background_color: "#48484e".into(),
|
||||||
|
menu_background_color_hover: "#ffffff17".into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,7 +24,7 @@
|
||||||
right: 0;
|
right: 0;
|
||||||
width: 240px;
|
width: 240px;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
background-color: #fff;
|
background-color: var(--melt-background-color);
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
box-shadow: 0 3px 6px -4px rgba(0, 0, 0, 0.12),
|
box-shadow: 0 3px 6px -4px rgba(0, 0, 0, 0.12),
|
||||||
|
@ -65,7 +65,7 @@
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
border: 2px solid white;
|
border: 2px solid white;
|
||||||
box-shadow: 0 0 2px 0 rgba(0, 0, 0, .45);
|
box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.45);
|
||||||
}
|
}
|
||||||
|
|
||||||
.melt-color-picker-slider {
|
.melt-color-picker-slider {
|
||||||
|
|
|
@ -1,14 +1,28 @@
|
||||||
mod color;
|
mod color;
|
||||||
|
mod theme;
|
||||||
|
|
||||||
use crate::{mount_style, teleport::Teleport, utils::maybe_rw_signal::MaybeRwSignal};
|
use crate::{
|
||||||
|
mount_style, teleport::Teleport, use_theme, utils::maybe_rw_signal::MaybeRwSignal, Theme,
|
||||||
|
};
|
||||||
pub use color::*;
|
pub use color::*;
|
||||||
use leptos::leptos_dom::helpers::WindowListenerHandle;
|
use leptos::leptos_dom::helpers::WindowListenerHandle;
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
|
pub use theme::ColorPickerTheme;
|
||||||
use wasm_bindgen::__rt::IntoJsResult;
|
use wasm_bindgen::__rt::IntoJsResult;
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn ColorPicker(#[prop(optional, into)] value: MaybeRwSignal<RGBA>) -> impl IntoView {
|
pub fn ColorPicker(#[prop(optional, into)] value: MaybeRwSignal<RGBA>) -> impl IntoView {
|
||||||
mount_style("color-picker", include_str!("./color-picker.css"));
|
mount_style("color-picker", include_str!("./color-picker.css"));
|
||||||
|
let theme = use_theme(Theme::light);
|
||||||
|
let popover_css_vars = create_memo(move |_| {
|
||||||
|
theme.with(|theme| {
|
||||||
|
format!(
|
||||||
|
"--melt-background-color: {};",
|
||||||
|
theme.color_picker.popover_background_color
|
||||||
|
)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
let hue = create_rw_signal(0);
|
let hue = create_rw_signal(0);
|
||||||
let sv = create_rw_signal((0.0, 0.0));
|
let sv = create_rw_signal((0.0, 0.0));
|
||||||
let label = create_rw_signal(String::new());
|
let label = create_rw_signal(String::new());
|
||||||
|
@ -95,7 +109,7 @@ pub fn ColorPicker(#[prop(optional, into)] value: MaybeRwSignal<RGBA>) -> impl I
|
||||||
class="melt-color-picker-popover"
|
class="melt-color-picker-popover"
|
||||||
ref=popover_ref
|
ref=popover_ref
|
||||||
style=move || {
|
style=move || {
|
||||||
if !is_show_popover.get() { Some("display: none") } else { None }
|
if is_show_popover.get() { popover_css_vars.get() } else { "display: none".to_string() }
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|
||||||
|
|
20
src/color_picker/theme.rs
Normal file
20
src/color_picker/theme.rs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
use crate::theme::ThemeMethod;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ColorPickerTheme {
|
||||||
|
pub popover_background_color: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ThemeMethod for ColorPickerTheme {
|
||||||
|
fn light() -> Self {
|
||||||
|
Self {
|
||||||
|
popover_background_color: "#fff".into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dark() -> Self {
|
||||||
|
Self {
|
||||||
|
popover_background_color: "#48484e".into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,14 @@
|
||||||
|
mod theme;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
components::*,
|
components::*,
|
||||||
icon::*,
|
icon::*,
|
||||||
|
use_theme,
|
||||||
utils::{mount_style::mount_style, StoredMaybeSignal},
|
utils::{mount_style::mount_style, StoredMaybeSignal},
|
||||||
|
Theme,
|
||||||
};
|
};
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
|
pub use theme::NavBarTheme;
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn NavBar(
|
pub fn NavBar(
|
||||||
|
@ -15,6 +20,15 @@ pub fn NavBar(
|
||||||
#[prop(optional, into)] on_click_right: Option<Callback<ev::MouseEvent>>,
|
#[prop(optional, into)] on_click_right: Option<Callback<ev::MouseEvent>>,
|
||||||
) -> impl IntoView {
|
) -> impl IntoView {
|
||||||
mount_style("nav-bar", include_str!("./nav-bar.css"));
|
mount_style("nav-bar", include_str!("./nav-bar.css"));
|
||||||
|
let theme = use_theme(Theme::light);
|
||||||
|
let css_vars = create_memo(move |_| {
|
||||||
|
theme.with(|theme| {
|
||||||
|
format!(
|
||||||
|
"--melt-background-color: {};",
|
||||||
|
theme.nav_bar.background_color
|
||||||
|
)
|
||||||
|
})
|
||||||
|
});
|
||||||
let title: StoredMaybeSignal<_> = title.into();
|
let title: StoredMaybeSignal<_> = title.into();
|
||||||
let left_text: StoredMaybeSignal<_> = left_text.into();
|
let left_text: StoredMaybeSignal<_> = left_text.into();
|
||||||
let right_text: StoredMaybeSignal<_> = right_text.into();
|
let right_text: StoredMaybeSignal<_> = right_text.into();
|
||||||
|
@ -32,7 +46,7 @@ pub fn NavBar(
|
||||||
};
|
};
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<div class="melt-nav-bar">
|
<div class="melt-nav-bar" style=move || css_vars.get()>
|
||||||
<If cond=MaybeSignal::derive(move || left_arrow.get() || !left_text.get().is_empty())>
|
<If cond=MaybeSignal::derive(move || left_arrow.get() || !left_text.get().is_empty())>
|
||||||
<Then slot>
|
<Then slot>
|
||||||
<div class="melt-nav-bar__left" on:click=on_click_left>
|
<div class="melt-nav-bar__left" on:click=on_click_left>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
right: 0;
|
right: 0;
|
||||||
height: 46px;
|
height: 46px;
|
||||||
line-height: 46px;
|
line-height: 46px;
|
||||||
background-color: #fff;
|
background-color: var(--melt-background-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.melt-nav-bar__center {
|
.melt-nav-bar__center {
|
||||||
|
|
20
src/mobile/nav_bar/theme.rs
Normal file
20
src/mobile/nav_bar/theme.rs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
use crate::theme::ThemeMethod;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct NavBarTheme {
|
||||||
|
pub background_color: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ThemeMethod for NavBarTheme {
|
||||||
|
fn light() -> Self {
|
||||||
|
Self {
|
||||||
|
background_color: "#fff".into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dark() -> Self {
|
||||||
|
Self {
|
||||||
|
background_color: "#323233".into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,14 @@
|
||||||
mod tabbar_item;
|
mod tabbar_item;
|
||||||
|
mod theme;
|
||||||
|
|
||||||
use crate::utils::{maybe_rw_signal::MaybeRwSignal, mount_style::mount_style};
|
use crate::{
|
||||||
|
use_theme,
|
||||||
|
utils::{maybe_rw_signal::MaybeRwSignal, mount_style::mount_style},
|
||||||
|
Theme,
|
||||||
|
};
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
|
|
||||||
pub use tabbar_item::*;
|
pub use tabbar_item::*;
|
||||||
|
pub use theme::TabbarTheme;
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn Tabbar(
|
pub fn Tabbar(
|
||||||
|
@ -11,6 +16,15 @@ pub fn Tabbar(
|
||||||
children: Children,
|
children: Children,
|
||||||
) -> impl IntoView {
|
) -> impl IntoView {
|
||||||
mount_style("tabbar", include_str!("./tabbar.css"));
|
mount_style("tabbar", include_str!("./tabbar.css"));
|
||||||
|
let theme = use_theme(Theme::light);
|
||||||
|
let css_vars = create_memo(move |_| {
|
||||||
|
theme.with(|theme| {
|
||||||
|
format!(
|
||||||
|
"--melt-background-color: {};",
|
||||||
|
theme.tabbar.background_color
|
||||||
|
)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
let tabbar_injection_key = create_rw_signal(TabbarInjectionKey::new(value.get()));
|
let tabbar_injection_key = create_rw_signal(TabbarInjectionKey::new(value.get()));
|
||||||
create_effect(move |_| {
|
create_effect(move |_| {
|
||||||
|
@ -29,7 +43,7 @@ pub fn Tabbar(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
provide_context(tabbar_injection_key);
|
provide_context(tabbar_injection_key);
|
||||||
view! { <div class="melt-tabbar">{children()}</div> }
|
view! { <div class="melt-tabbar" style=move || css_vars.get()>{children()}</div> }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
.melt-tabbar {
|
.melt-tabbar {
|
||||||
background-color: #fff;
|
background-color: var(--melt-background-color);
|
||||||
position: fixed;
|
position: fixed;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
@ -7,4 +7,4 @@
|
||||||
height: 50px;
|
height: 50px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-around;
|
justify-content: space-around;
|
||||||
}
|
}
|
||||||
|
|
20
src/mobile/tabbar/theme.rs
Normal file
20
src/mobile/tabbar/theme.rs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
use crate::theme::ThemeMethod;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct TabbarTheme {
|
||||||
|
pub background_color: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ThemeMethod for TabbarTheme {
|
||||||
|
fn light() -> Self {
|
||||||
|
Self {
|
||||||
|
background_color: "#fff".into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dark() -> Self {
|
||||||
|
Self {
|
||||||
|
background_color: "#323233".into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -47,16 +47,16 @@ where
|
||||||
css_vars
|
css_vars
|
||||||
});
|
});
|
||||||
|
|
||||||
let popover_css_vars = create_memo(move |_| {
|
let menu_css_vars = create_memo(move |_| {
|
||||||
let mut css_vars = String::new();
|
let mut css_vars = String::new();
|
||||||
theme.with(|theme| {
|
theme.with(|theme| {
|
||||||
css_vars.push_str(&format!(
|
css_vars.push_str(&format!(
|
||||||
"--melt-background-color: {};",
|
"--melt-background-color: {};",
|
||||||
theme.select.popover_background_color
|
theme.select.menu_background_color
|
||||||
));
|
));
|
||||||
css_vars.push_str(&format!(
|
css_vars.push_str(&format!(
|
||||||
"--melt-background-color-hover: {};",
|
"--melt-background-color-hover: {};",
|
||||||
theme.select.popover_background_color_hover
|
theme.select.menu_background_color_hover
|
||||||
));
|
));
|
||||||
css_vars.push_str(&format!("--melt-font-color: {};", theme.select.font_color));
|
css_vars.push_str(&format!("--melt-font-color: {};", theme.select.font_color));
|
||||||
css_vars.push_str(&format!(
|
css_vars.push_str(&format!(
|
||||||
|
@ -67,14 +67,14 @@ where
|
||||||
css_vars
|
css_vars
|
||||||
});
|
});
|
||||||
|
|
||||||
let is_show_popover = create_rw_signal(false);
|
let is_show_menu = create_rw_signal(false);
|
||||||
let trigger_ref = create_node_ref::<html::Div>();
|
let trigger_ref = create_node_ref::<html::Div>();
|
||||||
let popover_ref = create_node_ref::<html::Div>();
|
let menu_ref = create_node_ref::<html::Div>();
|
||||||
let show_popover = move |_| {
|
let show_menu = move |_| {
|
||||||
let rect = trigger_ref.get().unwrap().get_bounding_client_rect();
|
let rect = trigger_ref.get().unwrap().get_bounding_client_rect();
|
||||||
is_show_popover.set(true);
|
is_show_menu.set(true);
|
||||||
if let Some(popover_ref) = popover_ref.get() {
|
if let Some(menu_ref) = menu_ref.get() {
|
||||||
popover_ref
|
menu_ref
|
||||||
.style("width", format!("{}px", rect.width()))
|
.style("width", format!("{}px", rect.width()))
|
||||||
.style(
|
.style(
|
||||||
"transform",
|
"transform",
|
||||||
|
@ -95,14 +95,14 @@ where
|
||||||
if current_el == *body {
|
if current_el == *body {
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
if current_el == ***popover_ref.get().unwrap()
|
if current_el == ***menu_ref.get().unwrap()
|
||||||
|| current_el == ***trigger_ref.get().unwrap()
|
|| current_el == ***trigger_ref.get().unwrap()
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
el = current_el.parent_element();
|
el = current_el.parent_element();
|
||||||
}
|
}
|
||||||
is_show_popover.set(false);
|
is_show_menu.set(false);
|
||||||
});
|
});
|
||||||
on_cleanup(move || timer.remove());
|
on_cleanup(move || timer.remove());
|
||||||
|
|
||||||
|
@ -116,7 +116,7 @@ where
|
||||||
None => String::new(),
|
None => String::new(),
|
||||||
});
|
});
|
||||||
view! {
|
view! {
|
||||||
<div class="melt-select" ref=trigger_ref on:click=show_popover style=move || css_vars.get()>
|
<div class="melt-select" ref=trigger_ref on:click=show_menu style=move || css_vars.get()>
|
||||||
|
|
||||||
{move || select_option_label.get()}
|
{move || select_option_label.get()}
|
||||||
|
|
||||||
|
@ -125,14 +125,14 @@ where
|
||||||
<div
|
<div
|
||||||
class="melt-select-menu"
|
class="melt-select-menu"
|
||||||
style=move || {
|
style=move || {
|
||||||
if is_show_popover.get() {
|
if is_show_menu.get() {
|
||||||
popover_css_vars.get()
|
menu_css_vars.get()
|
||||||
} else {
|
} else {
|
||||||
"display: none;".into()
|
"display: none;".into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ref=popover_ref
|
ref=menu_ref
|
||||||
>
|
>
|
||||||
<For
|
<For
|
||||||
each=move || options.get()
|
each=move || options.get()
|
||||||
|
@ -142,7 +142,7 @@ where
|
||||||
let onclick = move |_| {
|
let onclick = move |_| {
|
||||||
let SelectOption { value: item_value, label: _ } = item.get_value();
|
let SelectOption { value: item_value, label: _ } = item.get_value();
|
||||||
value.set(Some(item_value));
|
value.set(Some(item_value));
|
||||||
is_show_popover.set(false);
|
is_show_menu.set(false);
|
||||||
};
|
};
|
||||||
view! {
|
view! {
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -5,8 +5,8 @@ pub struct SelectTheme {
|
||||||
pub font_color: String,
|
pub font_color: String,
|
||||||
pub border_color: String,
|
pub border_color: String,
|
||||||
pub background_color: String,
|
pub background_color: String,
|
||||||
pub popover_background_color: String,
|
pub menu_background_color: String,
|
||||||
pub popover_background_color_hover: String,
|
pub menu_background_color_hover: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ThemeMethod for SelectTheme {
|
impl ThemeMethod for SelectTheme {
|
||||||
|
@ -15,8 +15,8 @@ impl ThemeMethod for SelectTheme {
|
||||||
font_color: "#333639".into(),
|
font_color: "#333639".into(),
|
||||||
border_color: "#e0e0e6".into(),
|
border_color: "#e0e0e6".into(),
|
||||||
background_color: "#fff".into(),
|
background_color: "#fff".into(),
|
||||||
popover_background_color: "#fff".into(),
|
menu_background_color: "#fff".into(),
|
||||||
popover_background_color_hover: "#f3f5f6".into(),
|
menu_background_color_hover: "#f3f5f6".into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,8 +25,8 @@ impl ThemeMethod for SelectTheme {
|
||||||
font_color: "#ffffffd1".into(),
|
font_color: "#ffffffd1".into(),
|
||||||
border_color: "#0000".into(),
|
border_color: "#0000".into(),
|
||||||
background_color: "#ffffff1a".into(),
|
background_color: "#ffffff1a".into(),
|
||||||
popover_background_color: "#48484e".into(),
|
menu_background_color: "#48484e".into(),
|
||||||
popover_background_color_hover: "#ffffff17".into(),
|
menu_background_color_hover: "#ffffff17".into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,10 @@ mod common;
|
||||||
|
|
||||||
use self::common::CommonTheme;
|
use self::common::CommonTheme;
|
||||||
use crate::{
|
use crate::{
|
||||||
AlertTheme, AvatarTheme, ButtonTheme, InputTheme, MenuTheme, MessageTheme, SelectTheme,
|
mobile::{NavBarTheme, TabbarTheme},
|
||||||
SkeletionTheme, SliderTheme, SwitchTheme, TableTheme, TagTheme, UploadTheme,
|
AlertTheme, AutoCompleteTheme, AvatarTheme, ButtonTheme, ColorPickerTheme, InputTheme,
|
||||||
|
MenuTheme, MessageTheme, SelectTheme, SkeletionTheme, SliderTheme, SwitchTheme, TableTheme,
|
||||||
|
TagTheme, UploadTheme,
|
||||||
};
|
};
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
|
|
||||||
|
@ -29,6 +31,10 @@ pub struct Theme {
|
||||||
pub slider: SliderTheme,
|
pub slider: SliderTheme,
|
||||||
pub switch: SwitchTheme,
|
pub switch: SwitchTheme,
|
||||||
pub upload: UploadTheme,
|
pub upload: UploadTheme,
|
||||||
|
pub nav_bar: NavBarTheme,
|
||||||
|
pub tabbar: TabbarTheme,
|
||||||
|
pub auto_complete: AutoCompleteTheme,
|
||||||
|
pub color_picker: ColorPickerTheme,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Theme {
|
impl Theme {
|
||||||
|
@ -49,6 +55,10 @@ impl Theme {
|
||||||
slider: SliderTheme::light(),
|
slider: SliderTheme::light(),
|
||||||
switch: SwitchTheme::light(),
|
switch: SwitchTheme::light(),
|
||||||
upload: UploadTheme::light(),
|
upload: UploadTheme::light(),
|
||||||
|
nav_bar: NavBarTheme::light(),
|
||||||
|
tabbar: TabbarTheme::light(),
|
||||||
|
auto_complete: AutoCompleteTheme::light(),
|
||||||
|
color_picker: ColorPickerTheme::light(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn dark() -> Self {
|
pub fn dark() -> Self {
|
||||||
|
@ -68,6 +78,10 @@ impl Theme {
|
||||||
slider: SliderTheme::dark(),
|
slider: SliderTheme::dark(),
|
||||||
switch: SwitchTheme::dark(),
|
switch: SwitchTheme::dark(),
|
||||||
upload: UploadTheme::dark(),
|
upload: UploadTheme::dark(),
|
||||||
|
nav_bar: NavBarTheme::dark(),
|
||||||
|
tabbar: TabbarTheme::dark(),
|
||||||
|
auto_complete: AutoCompleteTheme::dark(),
|
||||||
|
color_picker: ColorPickerTheme::dark(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,89 +1,89 @@
|
||||||
use leptos::StoredValue;
|
// use leptos::StoredValue;
|
||||||
use std::{fmt, future::Future, pin::Pin, rc::Rc};
|
// use std::{fmt, future::Future, pin::Pin, rc::Rc};
|
||||||
|
|
||||||
pub struct AsyncCallback<In: 'static, Out: 'static = ()>(
|
// pub struct AsyncCallback<In: 'static, Out: 'static = ()>(
|
||||||
#[allow(clippy::complexity)] StoredValue<Rc<dyn Fn(In) -> Pin<Box<dyn Future<Output = Out>>>>>,
|
// #[allow(clippy::complexity)] StoredValue<Rc<dyn Fn(In) -> Pin<Box<dyn Future<Output = Out>>>>>,
|
||||||
);
|
// );
|
||||||
|
|
||||||
impl<In> fmt::Debug for AsyncCallback<In> {
|
// impl<In> fmt::Debug for AsyncCallback<In> {
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
fmt.write_str("AsyncCallback")
|
// fmt.write_str("AsyncCallback")
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
impl<In, Out> Clone for AsyncCallback<In, Out> {
|
// impl<In, Out> Clone for AsyncCallback<In, Out> {
|
||||||
fn clone(&self) -> Self {
|
// fn clone(&self) -> Self {
|
||||||
*self
|
// *self
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
impl<In, Out> Copy for AsyncCallback<In, Out> {}
|
// impl<In, Out> Copy for AsyncCallback<In, Out> {}
|
||||||
|
|
||||||
impl<In, Out> AsyncCallback<In, Out> {
|
// impl<In, Out> AsyncCallback<In, Out> {
|
||||||
pub fn new<F, Fu>(f: F) -> Self
|
// pub fn new<F, Fu>(f: F) -> Self
|
||||||
where
|
// where
|
||||||
F: Fn(In) -> Fu + 'static,
|
// F: Fn(In) -> Fu + 'static,
|
||||||
Fu: Future<Output = Out> + 'static,
|
// Fu: Future<Output = Out> + 'static,
|
||||||
{
|
// {
|
||||||
let f = Rc::new(move |input: In| {
|
// let f = Rc::new(move |input: In| {
|
||||||
let fut = f(input);
|
// let fut = f(input);
|
||||||
Box::pin(fut) as Pin<Box<dyn Future<Output = Out>>>
|
// Box::pin(fut) as Pin<Box<dyn Future<Output = Out>>>
|
||||||
});
|
// });
|
||||||
Self(StoredValue::new(f))
|
// Self(StoredValue::new(f))
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub async fn call(&self, input: In) -> Out {
|
// pub async fn call(&self, input: In) -> Out {
|
||||||
let f = self.0.get_value();
|
// let f = self.0.get_value();
|
||||||
f(input).await
|
// f(input).await
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
impl<F, In, Fu, Out> From<F> for AsyncCallback<In, Out>
|
// impl<F, In, Fu, Out> From<F> for AsyncCallback<In, Out>
|
||||||
where
|
// where
|
||||||
F: Fn(In) -> Fu + 'static,
|
// F: Fn(In) -> Fu + 'static,
|
||||||
Fu: Future<Output = Out> + 'static,
|
// Fu: Future<Output = Out> + 'static,
|
||||||
{
|
// {
|
||||||
fn from(f: F) -> AsyncCallback<In, Out> {
|
// fn from(f: F) -> AsyncCallback<In, Out> {
|
||||||
AsyncCallback::new(f)
|
// AsyncCallback::new(f)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[cfg(test)]
|
// #[cfg(test)]
|
||||||
mod tests {
|
// mod tests {
|
||||||
use crate::utils::AsyncCallback;
|
// use crate::utils::AsyncCallback;
|
||||||
use leptos::create_runtime;
|
// use leptos::create_runtime;
|
||||||
|
|
||||||
struct NoClone {}
|
// struct NoClone {}
|
||||||
|
|
||||||
#[test]
|
// #[test]
|
||||||
fn clone_async_callback() {
|
// fn clone_async_callback() {
|
||||||
let rt = create_runtime();
|
// let rt = create_runtime();
|
||||||
let callback = AsyncCallback::new(move |_no_clone: NoClone| async { NoClone {} });
|
// let callback = AsyncCallback::new(move |_no_clone: NoClone| async { NoClone {} });
|
||||||
let _cloned = callback.clone();
|
// let _cloned = callback.clone();
|
||||||
rt.dispose();
|
// rt.dispose();
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[test]
|
// #[test]
|
||||||
fn async_callback_from() {
|
// fn async_callback_from() {
|
||||||
let rt = create_runtime();
|
// let rt = create_runtime();
|
||||||
let _callback: AsyncCallback<(), String> = (|()| async { "test".to_string() }).into();
|
// let _callback: AsyncCallback<(), String> = (|()| async { "test".to_string() }).into();
|
||||||
rt.dispose();
|
// rt.dispose();
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[test]
|
// #[test]
|
||||||
fn async_callback_from_html() {
|
// fn async_callback_from_html() {
|
||||||
let rt = create_runtime();
|
// let rt = create_runtime();
|
||||||
use leptos::{
|
// use leptos::{
|
||||||
html::{HtmlElement, H1},
|
// html::{HtmlElement, H1},
|
||||||
*,
|
// *,
|
||||||
};
|
// };
|
||||||
|
|
||||||
let _callback: AsyncCallback<String, HtmlElement<H1>> = (|x: String| async move {
|
// let _callback: AsyncCallback<String, HtmlElement<H1>> = (|x: String| async move {
|
||||||
view! {
|
// view! {
|
||||||
<h1>{x}</h1>
|
// <h1>{x}</h1>
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
.into();
|
// .into();
|
||||||
rt.dispose();
|
// rt.dispose();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
mod callback;
|
// mod callback;
|
||||||
pub mod maybe_rw_signal;
|
pub mod maybe_rw_signal;
|
||||||
mod maybe_signal_store;
|
mod maybe_signal_store;
|
||||||
pub mod mount_style;
|
pub mod mount_style;
|
||||||
pub mod signal;
|
pub mod signal;
|
||||||
|
|
||||||
pub use callback::AsyncCallback;
|
// pub use callback::AsyncCallback;
|
||||||
pub use maybe_signal_store::*;
|
pub use maybe_signal_store::*;
|
||||||
|
|
Loading…
Add table
Reference in a new issue