mirror of
https://github.com/adoyle0/thaw.git
synced 2025-03-13 05:59:49 -04:00
feat: binder component add width
This commit is contained in:
parent
c57910948f
commit
012ab684ba
7 changed files with 91 additions and 106 deletions
|
@ -1,7 +1,7 @@
|
||||||
mod theme;
|
mod theme;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
components::{Binder, Follower, FollowerPlacement},
|
components::{Binder, Follower, FollowerPlacement, FollowerWidth},
|
||||||
mount_style, use_theme,
|
mount_style, use_theme,
|
||||||
utils::StoredMaybeSignal,
|
utils::StoredMaybeSignal,
|
||||||
Input, Theme,
|
Input, Theme,
|
||||||
|
@ -61,7 +61,7 @@ pub fn AutoComplete(
|
||||||
allow_value
|
allow_value
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Follower slot show=is_show_menu placement=FollowerPlacement::BottomStart>
|
<Follower slot show=is_show_menu placement=FollowerPlacement::BottomStart width=FollowerWidth::Target>
|
||||||
<div
|
<div
|
||||||
class="thaw-auto-complete__menu"
|
class="thaw-auto-complete__menu"
|
||||||
style=move || menu_css_vars.get()
|
style=move || menu_css_vars.get()
|
||||||
|
|
|
@ -18,10 +18,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.thaw-color-picker-popover {
|
.thaw-color-picker-popover {
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
width: 240px;
|
width: 240px;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
background-color: var(--thaw-background-color);
|
background-color: var(--thaw-background-color);
|
||||||
|
@ -29,7 +25,6 @@
|
||||||
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),
|
||||||
0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 9px 28px 8px rgba(0, 0, 0, 0.05);
|
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 {
|
.thaw-color-picker-popover__panel {
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
mod color;
|
mod color;
|
||||||
mod theme;
|
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::*;
|
pub use color::*;
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
use leptos::{leptos_dom::helpers::WindowListenerHandle, wasm_bindgen::__rt::IntoJsResult};
|
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 trigger_ref = create_node_ref::<html::Div>();
|
||||||
let popover_ref = create_node_ref::<html::Div>();
|
let popover_ref = create_node_ref::<html::Div>();
|
||||||
let show_popover = move |_| {
|
let show_popover = move |_| {
|
||||||
let rect = trigger_ref.get().unwrap().get_bounding_client_rect();
|
|
||||||
is_show_popover.set(true);
|
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 timer = window_event_listener(ev::click, move |ev| {
|
||||||
let el = ev.target();
|
let el = ev.target();
|
||||||
|
@ -96,28 +86,24 @@ pub fn ColorPicker(#[prop(optional, into)] value: RwSignal<RGBA>) -> impl IntoVi
|
||||||
on_cleanup(move || timer.remove());
|
on_cleanup(move || timer.remove());
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<div class="thaw-color-picker-trigger" on:click=show_popover ref=trigger_ref>
|
<Binder target_ref=trigger_ref>
|
||||||
<div class="thaw-color-picker-trigger__content" style=move || style.get()>
|
<div class="thaw-color-picker-trigger" on:click=show_popover ref=trigger_ref>
|
||||||
{move || label.get()}
|
<div class="thaw-color-picker-trigger__content" style=move || style.get()>
|
||||||
|
{move || label.get()}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<Follower slot show=is_show_popover placement=FollowerPlacement::BottomStart>
|
||||||
<Teleport>
|
<div
|
||||||
<div
|
class="thaw-color-picker-popover"
|
||||||
class="thaw-color-picker-popover"
|
ref=popover_ref
|
||||||
ref=popover_ref
|
style=move || popover_css_vars.get()
|
||||||
style=move || {
|
>
|
||||||
if is_show_popover.get() {
|
|
||||||
popover_css_vars.get()
|
|
||||||
} else {
|
|
||||||
"display: none".to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
|
|
||||||
<ColorPanel hue=hue.read_only() sv/>
|
<ColorPanel hue=hue.read_only() sv/>
|
||||||
<HueSlider hue/>
|
<HueSlider hue/>
|
||||||
</div>
|
</div>
|
||||||
</Teleport>
|
</Follower>
|
||||||
|
</Binder>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,19 +32,17 @@ pub fn get_follower_placement_style(
|
||||||
let follower_height = follower_rect.height();
|
let follower_height = follower_rect.height();
|
||||||
let target_y = target_rect.y();
|
let target_y = target_rect.y();
|
||||||
let target_height = target_rect.height();
|
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 {
|
let Some(inner_height) = window_inner_height() else {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
if top + follower_height > inner_height {
|
if top + follower_height > inner_height && target_y - follower_height >= 0.0 {
|
||||||
if target_y - follower_height >= 0.0 {
|
target_y - follower_height
|
||||||
top = target_y - follower_height
|
} else {
|
||||||
}
|
top
|
||||||
}
|
}
|
||||||
|
|
||||||
top
|
|
||||||
};
|
};
|
||||||
|
|
||||||
style.push_str(&format!(
|
style.push_str(&format!(
|
||||||
|
|
|
@ -17,10 +17,20 @@ use leptos::{
|
||||||
pub struct Follower {
|
pub struct Follower {
|
||||||
#[prop(into)]
|
#[prop(into)]
|
||||||
show: MaybeSignal<bool>,
|
show: MaybeSignal<bool>,
|
||||||
|
#[prop(optional)]
|
||||||
|
width: Option<FollowerWidth>,
|
||||||
placement: FollowerPlacement,
|
placement: FollowerPlacement,
|
||||||
children: Children,
|
children: Children,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum FollowerWidth {
|
||||||
|
Target,
|
||||||
|
Px(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Copy for FollowerWidth {}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn Binder<El: ElementDescriptor + Clone + 'static>(
|
pub fn Binder<El: ElementDescriptor + Clone + 'static>(
|
||||||
#[prop(into)] target_ref: NodeRef<El>,
|
#[prop(into)] target_ref: NodeRef<El>,
|
||||||
|
@ -30,6 +40,7 @@ pub fn Binder<El: ElementDescriptor + Clone + 'static>(
|
||||||
mount_style("binder", include_str!("./binder.css"));
|
mount_style("binder", include_str!("./binder.css"));
|
||||||
let Follower {
|
let Follower {
|
||||||
show: follower_show,
|
show: follower_show,
|
||||||
|
width: follower_width,
|
||||||
placement: follower_placement,
|
placement: follower_placement,
|
||||||
children: follower_children,
|
children: follower_children,
|
||||||
} = follower;
|
} = follower;
|
||||||
|
@ -73,7 +84,7 @@ pub fn Binder<El: ElementDescriptor + Clone + 'static>(
|
||||||
|
|
||||||
let remove_scroll_listener = move |_| {
|
let remove_scroll_listener = move |_| {
|
||||||
scrollable_element_handle_vec.update_value(|vec| {
|
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);
|
scroll_listener.set_value(None);
|
||||||
};
|
};
|
||||||
|
@ -108,6 +119,7 @@ pub fn Binder<El: ElementDescriptor + Clone + 'static>(
|
||||||
<FollowerContainer
|
<FollowerContainer
|
||||||
show=follower_show
|
show=follower_show
|
||||||
target_ref
|
target_ref
|
||||||
|
width=follower_width
|
||||||
placement=follower_placement
|
placement=follower_placement
|
||||||
add_scroll_listener
|
add_scroll_listener
|
||||||
remove_scroll_listener
|
remove_scroll_listener
|
||||||
|
@ -123,6 +135,7 @@ pub fn Binder<El: ElementDescriptor + Clone + 'static>(
|
||||||
fn FollowerContainer<El: ElementDescriptor + Clone + 'static>(
|
fn FollowerContainer<El: ElementDescriptor + Clone + 'static>(
|
||||||
show: MaybeSignal<bool>,
|
show: MaybeSignal<bool>,
|
||||||
target_ref: NodeRef<El>,
|
target_ref: NodeRef<El>,
|
||||||
|
width: Option<FollowerWidth>,
|
||||||
placement: FollowerPlacement,
|
placement: FollowerPlacement,
|
||||||
#[prop(into)] add_scroll_listener: Callback<Callback<()>>,
|
#[prop(into)] add_scroll_listener: Callback<Callback<()>>,
|
||||||
#[prop(into)] remove_scroll_listener: 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 target_rect = target_ref.get_bounding_client_rect();
|
||||||
let content_rect = content_ref.get_bounding_client_rect();
|
let content_rect = content_ref.get_bounding_client_rect();
|
||||||
let mut style = String::new();
|
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) =
|
if let Some(placement_style) =
|
||||||
get_follower_placement_style(placement, target_rect, content_rect)
|
get_follower_placement_style(placement, target_rect, content_rect)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
mod theme;
|
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::wasm_bindgen::__rt::IntoJsResult;
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
@ -66,20 +71,7 @@ where
|
||||||
let trigger_ref = create_node_ref::<html::Div>();
|
let trigger_ref = create_node_ref::<html::Div>();
|
||||||
let menu_ref = create_node_ref::<html::Div>();
|
let menu_ref = create_node_ref::<html::Div>();
|
||||||
let show_menu = move |_| {
|
let show_menu = move |_| {
|
||||||
let rect = trigger_ref.get().unwrap().get_bounding_client_rect();
|
|
||||||
is_show_menu.set(true);
|
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 timer = window_event_listener(ev::click, move |ev| {
|
||||||
let el = ev.target();
|
let el = ev.target();
|
||||||
|
@ -111,47 +103,46 @@ where
|
||||||
None => String::new(),
|
None => String::new(),
|
||||||
});
|
});
|
||||||
view! {
|
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()}
|
{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>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
</div>
|
</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>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,16 +21,12 @@
|
||||||
background-color: var(--thaw-background-color);
|
background-color: var(--thaw-background-color);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
position: absolute;
|
width: 100%;
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
max-height: 200px;
|
max-height: 200px;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
box-shadow: 0 3px 6px -4px rgba(0, 0, 0, 0.12),
|
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);
|
0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 9px 28px 8px rgba(0, 0, 0, 0.05);
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
z-index: 1000;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.thaw-select-menu__item {
|
.thaw-select-menu__item {
|
||||||
|
|
Loading…
Add table
Reference in a new issue