feat: update select component

This commit is contained in:
luoxiao 2023-06-25 22:21:30 +08:00
parent 3c2440db49
commit 405de6a2d9
8 changed files with 119 additions and 7 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

View file

@ -52,6 +52,9 @@ pub fn App(cx: Scope) -> impl IntoView {
<Route path="/tabs" view=move |cx| view! {cx, <Route path="/tabs" view=move |cx| view! {cx,
<TabsPage /> <TabsPage />
} /> } />
<Route path="/select" view=move |cx| view! {cx,
<SelectPage />
} />
</Route> </Route>
</Routes> </Routes>
<Routes base="/melt-ui/mobile".to_string()> <Routes base="/melt-ui/mobile".to_string()>

View file

@ -47,6 +47,7 @@ pub fn ComponentsPage(cx: Scope) -> impl IntoView {
<MenuItem key="checkbox" label="checkbox" /> <MenuItem key="checkbox" label="checkbox" />
<MenuItem key="toast" label="toast" /> <MenuItem key="toast" label="toast" />
<MenuItem key="tabs" label="tabs" /> <MenuItem key="tabs" label="tabs" />
<MenuItem key="select" label="select" />
</Menu> </Menu>
</LayoutSider> </LayoutSider>
<Layout style="padding: 8px 12px 28px"> <Layout style="padding: 8px 12px 28px">

View file

@ -8,6 +8,7 @@ mod menu;
mod mobile; mod mobile;
mod modal; mod modal;
mod nav_bar; mod nav_bar;
mod select;
mod slider; mod slider;
mod tabbar; mod tabbar;
mod tabs; mod tabs;
@ -23,6 +24,7 @@ pub use menu::*;
pub use mobile::*; pub use mobile::*;
pub use modal::*; pub use modal::*;
pub use nav_bar::*; pub use nav_bar::*;
pub use select::*;
pub use slider::*; pub use slider::*;
pub use tabbar::*; pub use tabbar::*;
pub use tabs::*; pub use tabs::*;

View file

@ -0,0 +1,15 @@
use leptos::*;
use melt_ui::*;
#[component]
pub fn SelectPage(cx: Scope) -> impl IntoView {
let selected_value = create_rw_signal(cx, Some(String::from("apple")));
let options = vec![SelectOption {
label: String::from("apple"),
value: String::from("apple"),
}];
view! { cx,
<Select value=selected_value options/>
}
}

View file

@ -30,6 +30,7 @@ pub use layout::*;
pub use menu::*; pub use menu::*;
pub use modal::*; pub use modal::*;
pub use progress::*; pub use progress::*;
pub use select::*;
pub use slider::*; pub use slider::*;
pub use space::*; pub use space::*;
pub use table::*; pub use table::*;

View file

@ -1,6 +1,13 @@
use crate::teleport::Teleport; use crate::{
teleport::Teleport,
theme::use_theme,
utils::{dom::window_event_listener, mount_style::mount_style},
Theme,
};
use leptos::*; use leptos::*;
use std::hash::Hash; use std::hash::Hash;
use stylers::style_sheet_str;
use wasm_bindgen::__rt::IntoJsResult;
#[derive(Clone, PartialEq, Eq, Hash)] #[derive(Clone, PartialEq, Eq, Hash)]
pub struct SelectOption<T> { pub struct SelectOption<T> {
@ -17,6 +24,19 @@ pub fn Select<T>(
where where
T: Eq + Hash + Clone + 'static, T: Eq + Hash + Clone + 'static,
{ {
let class_name = mount_style("select", || style_sheet_str!("./src/select/select.css"));
let theme = use_theme(cx, Theme::light);
let css_vars = create_memo(cx, move |_| {
let mut css_vars = String::new();
let theme = theme.get();
let bg_color = theme.common.color_primary;
css_vars.push_str(&format!("--font-color: {bg_color};"));
css_vars.push_str(&format!("--border-color-hover: {bg_color};"));
css_vars
});
let is_show_popover = create_rw_signal(cx, false); let is_show_popover = create_rw_signal(cx, false);
let trigger_ref = create_node_ref::<html::Div>(cx); let trigger_ref = create_node_ref::<html::Div>(cx);
let popover_ref = create_node_ref::<html::Div>(cx); let popover_ref = create_node_ref::<html::Div>(cx);
@ -36,6 +56,25 @@ where
); );
} }
}; };
let timer = window_event_listener(ev::click, move |ev| {
let el = ev.target();
let mut el: Option<web_sys::Element> =
el.into_js_result().map_or(None, |el| Some(el.into()));
let body = document().body().unwrap();
while let Some(current_el) = el {
if current_el == *body {
break;
};
if current_el == ***popover_ref.get().unwrap()
|| current_el == ***trigger_ref.get().unwrap()
{
return;
}
el = current_el.parent_element();
}
is_show_popover.set(false);
});
on_cleanup(cx, timer);
let temp_options = options.clone(); let temp_options = options.clone();
let select_option_label = create_memo(cx, move |_| match value.get() { let select_option_label = create_memo(cx, move |_| match value.get() {
@ -46,21 +85,27 @@ where
.map_or(String::new(), |v| v.label.clone()), .map_or(String::new(), |v| v.label.clone()),
None => String::new(), None => String::new(),
}); });
view! { cx, view! { cx, class=class_name,
<div class="melt-select" ref=trigger_ref on:click=show_popover> <div class="melt-select" ref=trigger_ref on:click=show_popover style=move || css_vars.get()>
{ {
move || select_option_label.get() move || select_option_label.get()
} }
</div> </div>
<Teleport> <Teleport>
<div class="melt-select-menu" style=move || if is_show_popover.get() { "display: none" } else { "" } ref=popover_ref> <div class="melt-select-menu" style=move || if is_show_popover.get() { css_vars.get() } else { format!("display: none; {}", css_vars.get()) } ref=popover_ref>
<For <For
each=move || options.get() each=move || options.get()
key=move |item| item.value.clone() key=move |item| item.value.clone()
view=move |cx, item| { view=move |cx, item| {
view! { cx, let item = store_value(cx, item);
<div class="melt-select-menu__item"> let onclick = move |_| {
{ item.label } let SelectOption { value: item_value, label: _ } = item.get_value();
value.set(Some(item_value));
is_show_popover.set(false);
};
view! { cx, class=class_name,
<div class="melt-select-menu__item" class=("melt-select-menu__item-selected", move || value.get() == Some(item.get_value().value) ) on:click=onclick>
{ item.get_value().label }
</div> </div>
} }
} }

45
src/select/select.css Normal file
View file

@ -0,0 +1,45 @@
.melt-select {
position: relative;
padding: 0 10px;
height: 30px;
line-height: 30px;
background-color: #fafbfc;
color: #24292e;
border: 1px solid #e0e0e6;
border-radius: 3px;
transition: all 0.3s;
cursor: pointer;
box-sizing: border-box;
}
.melt-select:hover {
border-color: var(--border-color-hover);
}
.melt-select-menu {
box-sizing: border-box;
padding: 5px;
position: absolute;
top: 0;
left: 0;
right: 0;
max-height: 200px;
border-radius: 3px;
box-shadow: 0 3px 6px -4px rgba(0, 0, 0, 0.12),
0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 9px 28px 8px rgba(0, 0, 0, 0.05);
z-index: 1000;
}
.melt-select-menu__item {
padding: 6px 5px;
border-radius: 2px;
cursor: pointer;
}
.melt-select-menu__item:hover {
background-color: #f6f6f7;
}
.melt-select-menu__item-selected {
color: var(--font-color);
}