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