mirror of
https://github.com/adoyle0/thaw.git
synced 2025-01-22 14:09:21 -05:00
fix: Binder component sync position
This commit is contained in:
parent
3e59f506dd
commit
b04feef0d1
14 changed files with 87 additions and 74 deletions
|
@ -2,7 +2,7 @@ use crate::{ConfigInjection, Icon};
|
|||
use leptos::{either::Either, ev, html, prelude::*};
|
||||
use thaw_components::{CSSTransition, Teleport};
|
||||
use thaw_utils::{
|
||||
add_event_listener, class_list, get_scroll_parent, mount_style, BoxCallback,
|
||||
add_event_listener, class_list, get_scroll_parent_element, mount_style, BoxCallback,
|
||||
EventListenerHandle,
|
||||
};
|
||||
|
||||
|
@ -43,7 +43,7 @@ pub fn BackTop(
|
|||
};
|
||||
|
||||
request_animation_frame(move || {
|
||||
let scroll_el = get_scroll_parent(&placeholder_el)
|
||||
let scroll_el = get_scroll_parent_element(&placeholder_el)
|
||||
.unwrap_or_else(|| document().document_element().unwrap());
|
||||
|
||||
{
|
||||
|
|
|
@ -55,7 +55,7 @@ pub fn Combobox(
|
|||
let Some(clear_icon_el) = clear_icon_ref.get() else {
|
||||
return;
|
||||
};
|
||||
let handler = add_event_listener(clear_icon_el.into(), ev::click, move |e| {
|
||||
let handler = add_event_listener(clear_icon_el, ev::click, move |e| {
|
||||
if disabled.get_untracked() {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -80,7 +80,7 @@ pub fn Menu(
|
|||
let Some(target_el) = target_ref.get() else {
|
||||
return;
|
||||
};
|
||||
let handler = add_event_listener(target_el.into(), ev::click, move |event| {
|
||||
let handler = add_event_listener(target_el, ev::click, move |event| {
|
||||
if trigger_type != MenuTriggerType::Click {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -99,7 +99,7 @@ pub fn Popover(
|
|||
let Some(target_el) = target_ref.get() else {
|
||||
return;
|
||||
};
|
||||
let handler = add_event_listener(target_el.into(), ev::click, move |event| {
|
||||
let handler = add_event_listener(target_el, ev::click, move |event| {
|
||||
if trigger_type != PopoverTriggerType::Click {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ pub fn Upload(
|
|||
let Some(trigger_el) = trigger_ref.get() else {
|
||||
return;
|
||||
};
|
||||
let handle = add_event_listener(trigger_el.into(), ev::click, move |_| {
|
||||
let handle = add_event_listener(trigger_el, ev::click, move |_| {
|
||||
if let Some(input_ref) = input_ref.get_untracked() {
|
||||
input_ref.click();
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ use leptos::{
|
|||
leptos_dom::helpers::WindowListenerHandle,
|
||||
prelude::*,
|
||||
};
|
||||
use thaw_utils::{add_event_listener, get_scroll_parent, mount_style, EventListenerHandle};
|
||||
use thaw_utils::{add_event_listener, get_scroll_parent_node, mount_style, EventListenerHandle};
|
||||
|
||||
#[slot]
|
||||
pub struct Follower {
|
||||
|
@ -141,12 +141,12 @@ where
|
|||
};
|
||||
|
||||
let mut handle_vec = vec![];
|
||||
let mut cursor = get_scroll_parent(&el);
|
||||
let mut cursor = get_scroll_parent_node(&el);
|
||||
loop {
|
||||
if let Some(el) = cursor.take() {
|
||||
cursor = get_scroll_parent(&el);
|
||||
if let Some(node) = cursor.take() {
|
||||
cursor = get_scroll_parent_node(&node);
|
||||
|
||||
let handle = add_event_listener(el, ev::scroll, move |_| {
|
||||
let handle = add_event_listener(node, ev::scroll, move |_| {
|
||||
sync_position();
|
||||
});
|
||||
handle_vec.push(handle);
|
||||
|
@ -213,9 +213,7 @@ where
|
|||
node_ref=content_ref
|
||||
style=move || content_style.get()
|
||||
>
|
||||
<Provider value=follower_injection>
|
||||
{follower_children()}
|
||||
</Provider>
|
||||
<Provider value=follower_injection>{follower_children()}</Provider>
|
||||
</div>
|
||||
</div>
|
||||
</Teleport>
|
||||
|
|
|
@ -92,13 +92,13 @@ where
|
|||
}
|
||||
};
|
||||
let handle = match types {
|
||||
AnimationTypes::Transition => add_event_listener(
|
||||
el.deref().clone().into(),
|
||||
ev::transitionend,
|
||||
move |_| event_listener(),
|
||||
),
|
||||
AnimationTypes::Transition => {
|
||||
add_event_listener(el.deref().clone(), ev::transitionend, move |_| {
|
||||
event_listener()
|
||||
})
|
||||
}
|
||||
AnimationTypes::Animation => {
|
||||
add_event_listener(el.deref().clone().into(), ev::animationend, move |_| {
|
||||
add_event_listener(el.deref().clone(), ev::animationend, move |_| {
|
||||
event_listener()
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,39 +1,49 @@
|
|||
use leptos::prelude::*;
|
||||
use web_sys::Element;
|
||||
use wasm_bindgen::JsCast;
|
||||
use web_sys::{Element, Node};
|
||||
|
||||
pub fn get_scroll_parent(element: &Element) -> Option<Element> {
|
||||
let Some(parent_element) = get_parent_element(element) else {
|
||||
return None;
|
||||
};
|
||||
pub fn get_scroll_parent_node(node: &Node) -> Option<Node> {
|
||||
let parent_node = node.parent_node()?;
|
||||
|
||||
if parent_element.node_type() == 9 {
|
||||
return Some(parent_element);
|
||||
}
|
||||
|
||||
if parent_element.node_type() == 1 {
|
||||
if let Some((overflow, overflow_x, overflow_y)) = get_overflow(&parent_element) {
|
||||
let node_type = parent_node.node_type();
|
||||
if node_type == Node::ELEMENT_NODE {
|
||||
let el = parent_node.clone().dyn_into::<Element>().unwrap();
|
||||
if let Some((overflow, overflow_x, overflow_y)) = get_overflow(&el) {
|
||||
let overflow = format!("{overflow}{overflow_x}{overflow_y}");
|
||||
if overflow.contains("auto") {
|
||||
return Some(parent_element);
|
||||
return Some(parent_node);
|
||||
}
|
||||
if overflow.contains("scroll") {
|
||||
return Some(parent_element);
|
||||
return Some(parent_node);
|
||||
}
|
||||
if overflow.contains("overlay") {
|
||||
return Some(parent_element);
|
||||
return Some(parent_node);
|
||||
}
|
||||
}
|
||||
} else if node_type == Node::DOCUMENT_NODE {
|
||||
return Some(document().into());
|
||||
}
|
||||
|
||||
get_scroll_parent_node(&parent_node)
|
||||
}
|
||||
|
||||
pub fn get_scroll_parent_element(element: &Element) -> Option<Element> {
|
||||
let parent_element = element.parent_element()?;
|
||||
|
||||
if let Some((overflow, overflow_x, overflow_y)) = get_overflow(&parent_element) {
|
||||
let overflow = format!("{overflow}{overflow_x}{overflow_y}");
|
||||
if overflow.contains("auto") {
|
||||
return Some(parent_element);
|
||||
}
|
||||
if overflow.contains("scroll") {
|
||||
return Some(parent_element);
|
||||
}
|
||||
if overflow.contains("overlay") {
|
||||
return Some(parent_element);
|
||||
}
|
||||
}
|
||||
|
||||
get_scroll_parent(&parent_element)
|
||||
}
|
||||
|
||||
fn get_parent_element(element: &Element) -> Option<Element> {
|
||||
if element.node_type() == 9 {
|
||||
None
|
||||
} else {
|
||||
element.parent_element()
|
||||
}
|
||||
get_scroll_parent_element(&parent_element)
|
||||
}
|
||||
|
||||
fn get_overflow(parent_element: &Element) -> Option<(String, String, String)> {
|
||||
|
|
|
@ -2,6 +2,6 @@ mod get_scroll_parent;
|
|||
mod mount_style;
|
||||
mod scroll_into_view;
|
||||
|
||||
pub use get_scroll_parent::get_scroll_parent;
|
||||
pub use get_scroll_parent::{get_scroll_parent_element, get_scroll_parent_node};
|
||||
pub use mount_style::{mount_dynamic_style, mount_style};
|
||||
pub use scroll_into_view::scroll_into_view;
|
||||
|
|
|
@ -3,8 +3,8 @@ use web_sys::HtmlElement;
|
|||
|
||||
pub fn scroll_into_view(el: &HtmlElement) {
|
||||
cfg_if! { if #[cfg(all(target_arch = "wasm32", any(feature = "csr", feature = "hydrate")))] {
|
||||
use super::get_scroll_parent;
|
||||
if let Some(parent) = get_scroll_parent(el) {
|
||||
use super::get_scroll_parent_element;
|
||||
if let Some(parent) = get_scroll_parent_element(el) {
|
||||
let parent_rect = parent.get_bounding_client_rect();
|
||||
let el_rect = el.get_bounding_client_rect();
|
||||
if el_rect.y() < parent_rect.y() {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use ::wasm_bindgen::{prelude::Closure, JsCast};
|
||||
use leptos::ev;
|
||||
use web_sys::{Element, EventTarget};
|
||||
use web_sys::EventTarget;
|
||||
|
||||
pub fn add_event_listener<E>(
|
||||
target: Element,
|
||||
target: impl Into<EventTarget>,
|
||||
event: E,
|
||||
cb: impl Fn(E::EventType) + 'static,
|
||||
) -> EventListenerHandle
|
||||
|
@ -31,12 +31,12 @@ impl EventListenerHandle {
|
|||
}
|
||||
|
||||
fn add_event_listener_untyped(
|
||||
target: Element,
|
||||
target: impl Into<EventTarget>,
|
||||
event_name: &str,
|
||||
cb: impl Fn(web_sys::Event) + 'static,
|
||||
) -> EventListenerHandle {
|
||||
fn wel(
|
||||
target: Element,
|
||||
target: EventTarget,
|
||||
cb: Box<dyn FnMut(web_sys::Event)>,
|
||||
event_name: &str,
|
||||
) -> EventListenerHandle {
|
||||
|
@ -54,11 +54,11 @@ fn add_event_listener_untyped(
|
|||
})
|
||||
}
|
||||
|
||||
wel(target, Box::new(cb), event_name)
|
||||
wel(target.into(), Box::new(cb), event_name)
|
||||
}
|
||||
|
||||
pub fn add_event_listener_with_bool<E: ev::EventDescriptor + 'static>(
|
||||
target: impl IntoEventTarget,
|
||||
target: impl Into<EventTarget>,
|
||||
event: E,
|
||||
cb: impl Fn(E::EventType) + 'static,
|
||||
use_capture: bool,
|
||||
|
@ -67,7 +67,7 @@ where
|
|||
E::EventType: JsCast,
|
||||
{
|
||||
add_event_listener_untyped_with_bool(
|
||||
target.into_event_target(),
|
||||
target,
|
||||
&event.name(),
|
||||
move |e| cb(e.unchecked_into::<E::EventType>()),
|
||||
use_capture,
|
||||
|
@ -75,7 +75,7 @@ where
|
|||
}
|
||||
|
||||
fn add_event_listener_untyped_with_bool(
|
||||
target: EventTarget,
|
||||
target: impl Into<EventTarget>,
|
||||
event_name: &str,
|
||||
cb: impl Fn(web_sys::Event) + 'static,
|
||||
use_capture: bool,
|
||||
|
@ -107,24 +107,30 @@ fn add_event_listener_untyped_with_bool(
|
|||
})
|
||||
}
|
||||
|
||||
wel(target, Box::new(cb), event_name, use_capture)
|
||||
wel(target.into(), Box::new(cb), event_name, use_capture)
|
||||
}
|
||||
|
||||
pub trait IntoEventTarget {
|
||||
fn into_event_target(self) -> EventTarget;
|
||||
}
|
||||
// pub trait IntoEventTarget {
|
||||
// fn into_event_target(self) -> EventTarget;
|
||||
// }
|
||||
|
||||
impl IntoEventTarget for EventTarget {
|
||||
fn into_event_target(self) -> EventTarget {
|
||||
self
|
||||
}
|
||||
}
|
||||
// impl IntoEventTarget for EventTarget {
|
||||
// fn into_event_target(self) -> EventTarget {
|
||||
// self
|
||||
// }
|
||||
// }
|
||||
|
||||
impl IntoEventTarget for web_sys::Document {
|
||||
fn into_event_target(self) -> EventTarget {
|
||||
self.into()
|
||||
}
|
||||
}
|
||||
// impl IntoEventTarget for web_sys::Document {
|
||||
// fn into_event_target(self) -> EventTarget {
|
||||
// self.into()
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl IntoEventTarget for Element {
|
||||
// fn into_event_target(self) -> EventTarget {
|
||||
// self.into()
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl IntoEventTarget for HtmlElement<AnyElement> {
|
||||
// fn into_event_target(self) -> EventTarget {
|
||||
|
|
|
@ -4,6 +4,6 @@ mod signal_watch;
|
|||
mod stored_maybe_signal;
|
||||
|
||||
pub use component_ref::ComponentRef;
|
||||
pub use model::{Model, OptionModel, VecModel, VecModelWithValue, OptionModelWithValue};
|
||||
pub use model::{Model, OptionModel, OptionModelWithValue, VecModel, VecModelWithValue};
|
||||
pub use signal_watch::SignalWatch;
|
||||
pub use stored_maybe_signal::StoredMaybeSignal;
|
||||
|
|
|
@ -58,7 +58,9 @@ impl<T: Send + Sync> OptionModel<T> {
|
|||
pub fn with_untracked<O>(&self, fun: impl FnOnce(OptionModelWithValue<T>) -> O) -> O {
|
||||
match self {
|
||||
Self::T(read, _, _) => read.with_untracked(|value| fun(OptionModelWithValue::T(value))),
|
||||
Self::Option(read, _, _) => read.with_untracked(|value| fun(OptionModelWithValue::Option(value))),
|
||||
Self::Option(read, _, _) => {
|
||||
read.with_untracked(|value| fun(OptionModelWithValue::Option(value)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,10 +67,7 @@ impl<T: Send + Sync> VecModel<T> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn with<O>(
|
||||
&self,
|
||||
fun: impl FnOnce(VecModelWithValue<T>) -> O,
|
||||
) -> O {
|
||||
pub fn with<O>(&self, fun: impl FnOnce(VecModelWithValue<T>) -> O) -> O {
|
||||
match self {
|
||||
Self::T(read, _, _) => read.with(|value| fun(VecModelWithValue::T(value))),
|
||||
Self::Option(read, _, _) => read.with(|value| fun(VecModelWithValue::Option(value))),
|
||||
|
|
Loading…
Add table
Reference in a new issue