feat: binder component add width

This commit is contained in:
luoxiao 2023-11-12 02:34:40 +08:00
parent c57910948f
commit 012ab684ba
7 changed files with 91 additions and 106 deletions

View file

@ -1,7 +1,7 @@
mod theme;
use crate::{
components::{Binder, Follower, FollowerPlacement},
components::{Binder, Follower, FollowerPlacement, FollowerWidth},
mount_style, use_theme,
utils::StoredMaybeSignal,
Input, Theme,
@ -61,7 +61,7 @@ pub fn AutoComplete(
allow_value
/>
</div>
<Follower slot show=is_show_menu placement=FollowerPlacement::BottomStart>
<Follower slot show=is_show_menu placement=FollowerPlacement::BottomStart width=FollowerWidth::Target>
<div
class="thaw-auto-complete__menu"
style=move || menu_css_vars.get()

View file

@ -18,10 +18,6 @@
}
.thaw-color-picker-popover {
position: absolute;
top: 0;
left: 0;
right: 0;
width: 240px;
padding: 12px;
background-color: var(--thaw-background-color);
@ -29,7 +25,6 @@
box-sizing: border-box;
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;
}
.thaw-color-picker-popover__panel {

View file

@ -1,7 +1,8 @@
mod color;
mod theme;
use crate::{mount_style, teleport::Teleport, use_theme, Theme};
use crate::components::{Binder, Follower, FollowerPlacement};
use crate::{mount_style, use_theme, Theme};
pub use color::*;
use leptos::*;
use leptos::{leptos_dom::helpers::WindowListenerHandle, wasm_bindgen::__rt::IntoJsResult};
@ -62,18 +63,7 @@ pub fn ColorPicker(#[prop(optional, into)] value: RwSignal<RGBA>) -> impl IntoVi
let trigger_ref = create_node_ref::<html::Div>();
let popover_ref = create_node_ref::<html::Div>();
let show_popover = move |_| {
let rect = trigger_ref.get().unwrap().get_bounding_client_rect();
is_show_popover.set(true);
if let Some(popover_ref) = popover_ref.get() {
_ = popover_ref.style(
"transform",
format!(
"translateX({}px) translateY({}px)",
rect.x(),
rect.y() + rect.height()
),
);
}
};
let timer = window_event_listener(ev::click, move |ev| {
let el = ev.target();
@ -96,28 +86,24 @@ pub fn ColorPicker(#[prop(optional, into)] value: RwSignal<RGBA>) -> impl IntoVi
on_cleanup(move || timer.remove());
view! {
<div class="thaw-color-picker-trigger" on:click=show_popover ref=trigger_ref>
<div class="thaw-color-picker-trigger__content" style=move || style.get()>
{move || label.get()}
<Binder target_ref=trigger_ref>
<div class="thaw-color-picker-trigger" on:click=show_popover ref=trigger_ref>
<div class="thaw-color-picker-trigger__content" style=move || style.get()>
{move || label.get()}
</div>
</div>
</div>
<Teleport>
<div
class="thaw-color-picker-popover"
ref=popover_ref
style=move || {
if is_show_popover.get() {
popover_css_vars.get()
} else {
"display: none".to_string()
}
}
>
<Follower slot show=is_show_popover placement=FollowerPlacement::BottomStart>
<div
class="thaw-color-picker-popover"
ref=popover_ref
style=move || popover_css_vars.get()
>
<ColorPanel hue=hue.read_only() sv/>
<HueSlider hue/>
</div>
</Teleport>
<ColorPanel hue=hue.read_only() sv/>
<HueSlider hue/>
</div>
</Follower>
</Binder>
}
}

View file

@ -32,19 +32,17 @@ pub fn get_follower_placement_style(
let follower_height = follower_rect.height();
let target_y = target_rect.y();
let target_height = target_rect.height();
let mut top = target_y + target_height;
let top = target_y + target_height;
let Some(inner_height) = window_inner_height() else {
return None;
};
if top + follower_height > inner_height {
if target_y - follower_height >= 0.0 {
top = target_y - follower_height
}
if top + follower_height > inner_height && target_y - follower_height >= 0.0 {
target_y - follower_height
} else {
top
}
top
};
style.push_str(&format!(

View file

@ -17,10 +17,20 @@ use leptos::{
pub struct Follower {
#[prop(into)]
show: MaybeSignal<bool>,
#[prop(optional)]
width: Option<FollowerWidth>,
placement: FollowerPlacement,
children: Children,
}
#[derive(Clone)]
pub enum FollowerWidth {
Target,
Px(u32),
}
impl Copy for FollowerWidth {}
#[component]
pub fn Binder<El: ElementDescriptor + Clone + 'static>(
#[prop(into)] target_ref: NodeRef<El>,
@ -30,6 +40,7 @@ pub fn Binder<El: ElementDescriptor + Clone + 'static>(
mount_style("binder", include_str!("./binder.css"));
let Follower {
show: follower_show,
width: follower_width,
placement: follower_placement,
children: follower_children,
} = follower;
@ -73,7 +84,7 @@ pub fn Binder<El: ElementDescriptor + Clone + 'static>(
let remove_scroll_listener = move |_| {
scrollable_element_handle_vec.update_value(|vec| {
vec.drain(..).into_iter().for_each(|handle| handle.remove());
vec.drain(..).for_each(|handle| handle.remove());
});
scroll_listener.set_value(None);
};
@ -108,6 +119,7 @@ pub fn Binder<El: ElementDescriptor + Clone + 'static>(
<FollowerContainer
show=follower_show
target_ref
width=follower_width
placement=follower_placement
add_scroll_listener
remove_scroll_listener
@ -123,6 +135,7 @@ pub fn Binder<El: ElementDescriptor + Clone + 'static>(
fn FollowerContainer<El: ElementDescriptor + Clone + 'static>(
show: MaybeSignal<bool>,
target_ref: NodeRef<El>,
width: Option<FollowerWidth>,
placement: FollowerPlacement,
#[prop(into)] add_scroll_listener: Callback<Callback<()>>,
#[prop(into)] remove_scroll_listener: Callback<()>,
@ -142,7 +155,13 @@ fn FollowerContainer<El: ElementDescriptor + Clone + 'static>(
let target_rect = target_ref.get_bounding_client_rect();
let content_rect = content_ref.get_bounding_client_rect();
let mut style = String::new();
style.push_str(&format!("width: {}px;", target_rect.width()));
if let Some(width) = width {
let width = match width {
FollowerWidth::Target => format!("width: {}px;", target_rect.width()),
FollowerWidth::Px(width) => format!("width: {width}px;"),
};
style.push_str(&width);
}
if let Some(placement_style) =
get_follower_placement_style(placement, target_rect, content_rect)
{

View file

@ -1,6 +1,11 @@
mod theme;
use crate::{teleport::Teleport, theme::use_theme, utils::mount_style::mount_style, Theme};
use crate::{
components::{Binder, Follower, FollowerPlacement, FollowerWidth},
theme::use_theme,
utils::mount_style::mount_style,
Theme,
};
use leptos::wasm_bindgen::__rt::IntoJsResult;
use leptos::*;
use std::hash::Hash;
@ -66,20 +71,7 @@ where
let trigger_ref = create_node_ref::<html::Div>();
let menu_ref = create_node_ref::<html::Div>();
let show_menu = move |_| {
let rect = trigger_ref.get().unwrap().get_bounding_client_rect();
is_show_menu.set(true);
if let Some(menu_ref) = menu_ref.get() {
_ = menu_ref
.style("width", format!("{}px", rect.width()))
.style(
"transform",
format!(
"translateX({}px) translateY({}px) translateX(-50%)",
rect.x() + rect.width() / 2.0,
rect.y() + rect.height()
),
);
}
};
let timer = window_event_listener(ev::click, move |ev| {
let el = ev.target();
@ -111,47 +103,46 @@ where
None => String::new(),
});
view! {
<div class="thaw-select" ref=trigger_ref on:click=show_menu style=move || css_vars.get()>
<Binder target_ref=trigger_ref>
<div class="thaw-select" ref=trigger_ref on:click=show_menu style=move || css_vars.get()>
{move || select_option_label.get()}
</div>
<Teleport>
<div
class="thaw-select-menu"
style=move || {
if is_show_menu.get() { menu_css_vars.get() } else { "display: none;".into() }
}
ref=menu_ref
>
<For
each=move || options.get()
key=move |item| item.value.clone()
children=move |item| {
let item = store_value(item);
let onclick = move |_| {
let SelectOption { value: item_value, label: _ } = item.get_value();
value.set(Some(item_value));
is_show_menu.set(false);
};
view! {
<div
class="thaw-select-menu__item"
class=(
"thaw-select-menu__item-selected",
move || value.get() == Some(item.get_value().value),
)
on:click=onclick
>
{item.get_value().label}
</div>
}
}
/>
{move || select_option_label.get()}
</div>
</Teleport>
<Follower slot show=is_show_menu placement=FollowerPlacement::BottomStart width=FollowerWidth::Target>
<div
class="thaw-select-menu"
style=move || menu_css_vars.get()
ref=menu_ref
>
<For
each=move || options.get()
key=move |item| item.value.clone()
children=move |item| {
let item = store_value(item);
let onclick = move |_| {
let SelectOption { value: item_value, label: _ } = item.get_value();
value.set(Some(item_value));
is_show_menu.set(false);
};
view! {
<div
class="thaw-select-menu__item"
class=(
"thaw-select-menu__item-selected",
move || value.get() == Some(item.get_value().value),
)
on:click=onclick
>
{item.get_value().label}
</div>
}
}
/>
</div>
</Follower>
</Binder>
}
}

View file

@ -21,16 +21,12 @@
background-color: var(--thaw-background-color);
box-sizing: border-box;
padding: 5px;
position: absolute;
top: 0;
left: 0;
right: 0;
width: 100%;
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);
overflow: auto;
z-index: 1000;
}
.thaw-select-menu__item {