Compare commits
4 Commits
Author | SHA1 | Date |
---|---|---|
Greg Johnston | 9ae3f6f7f0 | |
Greg Johnston | 73f67d9a55 | |
Greg Johnston | 8643126d09 | |
Greg Johnston | 99d28ed045 |
|
@ -166,11 +166,11 @@ impl Mountable for EachRepr {
|
||||||
pub(crate) struct EachItem {
|
pub(crate) struct EachItem {
|
||||||
cx: Scope,
|
cx: Scope,
|
||||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||||
document_fragment: web_sys::DocumentFragment,
|
document_fragment: Option<web_sys::DocumentFragment>,
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
opening: Comment,
|
opening: Comment,
|
||||||
pub(crate) child: View,
|
pub(crate) child: View,
|
||||||
closing: Comment,
|
closing: Option<Comment>,
|
||||||
#[cfg(not(all(target_arch = "wasm32", feature = "web")))]
|
#[cfg(not(all(target_arch = "wasm32", feature = "web")))]
|
||||||
pub(crate) id: HydrationKey,
|
pub(crate) id: HydrationKey,
|
||||||
}
|
}
|
||||||
|
@ -192,16 +192,22 @@ impl fmt::Debug for EachItem {
|
||||||
impl EachItem {
|
impl EachItem {
|
||||||
fn new(cx: Scope, child: View) -> Self {
|
fn new(cx: Scope, child: View) -> Self {
|
||||||
let id = HydrationCtx::id();
|
let id = HydrationCtx::id();
|
||||||
|
let needs_closing = !matches!(child, View::Element(_));
|
||||||
|
|
||||||
let markers = (
|
let markers = (
|
||||||
Comment::new(Cow::Borrowed("</EachItem>"), &id, true),
|
if needs_closing {
|
||||||
|
Some(Comment::new(Cow::Borrowed("</EachItem>"), &id, true))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
Comment::new(Cow::Borrowed("<EachItem>"), &id, false),
|
Comment::new(Cow::Borrowed("<EachItem>"), &id, false),
|
||||||
);
|
);
|
||||||
|
|
||||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||||
let document_fragment = {
|
let document_fragment = if needs_closing {
|
||||||
let fragment = crate::document().create_document_fragment();
|
let fragment = crate::document().create_document_fragment();
|
||||||
|
let closing = markers.0.as_ref().unwrap();
|
||||||
|
|
||||||
// Insert the comments into the document fragment
|
// Insert the comments into the document fragment
|
||||||
// so they can serve as our references when inserting
|
// so they can serve as our references when inserting
|
||||||
|
@ -209,14 +215,16 @@ impl EachItem {
|
||||||
if !HydrationCtx::is_hydrating() {
|
if !HydrationCtx::is_hydrating() {
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
fragment
|
fragment
|
||||||
.append_with_node_2(&markers.1.node, &markers.0.node)
|
.append_with_node_2(&markers.1.node, &closing.node)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
fragment.append_with_node_1(&markers.0.node).unwrap();
|
fragment.append_with_node_1(&closing.node).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
mount_child(MountKind::Before(&markers.0.node), &child);
|
mount_child(MountKind::Before(&closing.node), &child);
|
||||||
|
|
||||||
fragment
|
Some(fragment)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
@ -243,7 +251,11 @@ impl Drop for EachItem {
|
||||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||||
impl Mountable for EachItem {
|
impl Mountable for EachItem {
|
||||||
fn get_mountable_node(&self) -> web_sys::Node {
|
fn get_mountable_node(&self) -> web_sys::Node {
|
||||||
self.document_fragment.clone().unchecked_into()
|
if let Some(fragment) = &self.document_fragment {
|
||||||
|
fragment.clone().unchecked_into()
|
||||||
|
} else {
|
||||||
|
self.child.get_mountable_node()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_opening_node(&self) -> web_sys::Node {
|
fn get_opening_node(&self) -> web_sys::Node {
|
||||||
|
@ -255,7 +267,11 @@ impl Mountable for EachItem {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_closing_node(&self) -> web_sys::Node {
|
fn get_closing_node(&self) -> web_sys::Node {
|
||||||
self.closing.node.clone()
|
if let Some(closing) = &self.closing {
|
||||||
|
closing.node.clone().unchecked_into()
|
||||||
|
} else {
|
||||||
|
self.child.get_mountable_node().clone()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,20 +280,25 @@ impl EachItem {
|
||||||
/// order to be reinserted somewhere else.
|
/// order to be reinserted somewhere else.
|
||||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||||
fn prepare_for_move(&self) {
|
fn prepare_for_move(&self) {
|
||||||
|
if let Some(fragment) = &self.document_fragment {
|
||||||
let start = self.get_opening_node();
|
let start = self.get_opening_node();
|
||||||
let end = &self.closing.node;
|
let end = &self.get_closing_node();
|
||||||
|
|
||||||
let mut sibling = start;
|
let mut sibling = start;
|
||||||
|
|
||||||
while sibling != *end {
|
while sibling != *end {
|
||||||
let next_sibling = sibling.next_sibling().unwrap();
|
let next_sibling = sibling.next_sibling().unwrap();
|
||||||
|
|
||||||
self.document_fragment.append_child(&sibling).unwrap();
|
fragment.append_child(&sibling).unwrap();
|
||||||
|
|
||||||
sibling = next_sibling;
|
sibling = next_sibling;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.document_fragment.append_with_node_1(end).unwrap();
|
fragment.append_with_node_1(end).unwrap();
|
||||||
|
} else {
|
||||||
|
let node = self.child.get_mountable_node();
|
||||||
|
node.unchecked_into::<web_sys::Element>().remove();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -349,7 +370,7 @@ where
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
cfg_if::cfg_if! {
|
||||||
if #[cfg(all(target_arch = "wasm32", feature = "web"))] {
|
if #[cfg(all(target_arch = "wasm32", feature = "web"))] {
|
||||||
create_effect(cx, move |prev_hash_run| {
|
create_effect(cx, move |prev_hash_run: Option<HashRun<FxIndexSet<K>>>| {
|
||||||
let mut children_borrow = children.borrow_mut();
|
let mut children_borrow = children.borrow_mut();
|
||||||
|
|
||||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||||
|
@ -359,14 +380,22 @@ where
|
||||||
closing.clone()
|
closing.clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
let items = items_fn();
|
let items_iter = items_fn().into_iter();
|
||||||
|
|
||||||
let items = items.into_iter().collect::<SmallVec<[_; 128]>>();
|
let (capacity, _) = items_iter.size_hint();
|
||||||
|
let mut hashed_items = FxIndexSet::with_capacity_and_hasher(
|
||||||
let hashed_items =
|
capacity,
|
||||||
items.iter().map(&key_fn).collect::<FxIndexSet<_>>();
|
BuildHasherDefault::<FxHasher>::default()
|
||||||
|
);
|
||||||
|
|
||||||
if let Some(HashRun(prev_hash_run)) = prev_hash_run {
|
if let Some(HashRun(prev_hash_run)) = prev_hash_run {
|
||||||
|
if !prev_hash_run.is_empty() {
|
||||||
|
let mut items = Vec::with_capacity(capacity);
|
||||||
|
for item in items_iter {
|
||||||
|
hashed_items.insert(key_fn(&item));
|
||||||
|
items.push(Some(item));
|
||||||
|
}
|
||||||
|
|
||||||
let cmds = diff(&prev_hash_run, &hashed_items);
|
let cmds = diff(&prev_hash_run, &hashed_items);
|
||||||
|
|
||||||
apply_cmds(
|
apply_cmds(
|
||||||
|
@ -377,21 +406,34 @@ where
|
||||||
&closing,
|
&closing,
|
||||||
cmds,
|
cmds,
|
||||||
&mut children_borrow,
|
&mut children_borrow,
|
||||||
items.into_iter().map(|t| Some(t)).collect(),
|
items,
|
||||||
&each_fn
|
&each_fn
|
||||||
);
|
);
|
||||||
} else {
|
return HashRun(hashed_items);
|
||||||
*children_borrow = Vec::with_capacity(items.len());
|
}
|
||||||
|
}
|
||||||
for item in items {
|
|
||||||
let (each_item, _) = cx.run_child_scope(|cx| EachItem::new(cx, each_fn(cx, item).into_view(cx)));
|
|
||||||
|
|
||||||
|
// if previous run is empty
|
||||||
|
*children_borrow = Vec::with_capacity(capacity);
|
||||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||||
mount_child(MountKind::Before(&closing), &each_item);
|
let fragment = crate::document().create_document_fragment();
|
||||||
|
|
||||||
|
for item in items_iter {
|
||||||
|
hashed_items.insert(key_fn(&item));
|
||||||
|
let (each_item, _) = cx.run_child_scope(|cx| EachItem::new(cx, each_fn(cx, item).into_view(cx)));
|
||||||
|
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||||
|
{
|
||||||
|
_ = fragment.append_child(&each_item.get_mountable_node());
|
||||||
|
}
|
||||||
|
|
||||||
children_borrow.push(Some(each_item));
|
children_borrow.push(Some(each_item));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||||
|
closing
|
||||||
|
.unchecked_ref::<web_sys::Element>()
|
||||||
|
.before_with_node_1(&fragment)
|
||||||
|
.expect("before to not err");
|
||||||
|
|
||||||
HashRun(hashed_items)
|
HashRun(hashed_items)
|
||||||
});
|
});
|
||||||
|
@ -421,6 +463,18 @@ fn diff<K: Eq + Hash>(from: &FxIndexSet<K>, to: &FxIndexSet<K>) -> Diff {
|
||||||
clear: true,
|
clear: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
} else if from.is_empty() {
|
||||||
|
return Diff {
|
||||||
|
added: to
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(at, _)| DiffOpAdd {
|
||||||
|
at,
|
||||||
|
mode: DiffOpAddMode::Append,
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get removed items
|
// Get removed items
|
||||||
|
@ -445,7 +499,7 @@ fn diff<K: Eq + Hash>(from: &FxIndexSet<K>, to: &FxIndexSet<K>) -> Diff {
|
||||||
|
|
||||||
// Get moved items
|
// Get moved items
|
||||||
let mut normalized_idx = 0;
|
let mut normalized_idx = 0;
|
||||||
let mut move_cmds = SmallVec::<[_; 8]>::with_capacity(to.len());
|
let mut move_cmds = Vec::new();
|
||||||
let mut added_idx = added.next().map(|k| to.get_full(k).unwrap().0);
|
let mut added_idx = added.next().map(|k| to.get_full(k).unwrap().0);
|
||||||
let mut removed_idx = removed.next().map(|k| from.get_full(k).unwrap().0);
|
let mut removed_idx = removed.next().map(|k| from.get_full(k).unwrap().0);
|
||||||
|
|
||||||
|
@ -539,9 +593,9 @@ fn apply_opts<K: Eq + Hash>(
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
struct Diff {
|
struct Diff {
|
||||||
removed: SmallVec<[DiffOpRemove; 8]>,
|
removed: Vec<DiffOpRemove>,
|
||||||
moved: SmallVec<[DiffOpMove; 8]>,
|
moved: Vec<DiffOpMove>,
|
||||||
added: SmallVec<[DiffOpAdd; 8]>,
|
added: Vec<DiffOpAdd>,
|
||||||
clear: bool,
|
clear: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -588,7 +642,7 @@ fn apply_cmds<T, EF, N>(
|
||||||
closing: &web_sys::Node,
|
closing: &web_sys::Node,
|
||||||
mut cmds: Diff,
|
mut cmds: Diff,
|
||||||
children: &mut Vec<Option<EachItem>>,
|
children: &mut Vec<Option<EachItem>>,
|
||||||
mut items: SmallVec<[Option<T>; 128]>,
|
mut items: Vec<Option<T>>,
|
||||||
each_fn: &EF,
|
each_fn: &EF,
|
||||||
) where
|
) where
|
||||||
EF: Fn(Scope, T) -> N,
|
EF: Fn(Scope, T) -> N,
|
||||||
|
|
|
@ -23,7 +23,12 @@ pub fn add_event_helper<E: crate::ev::EventDescriptor + 'static>(
|
||||||
let event_name = event.name();
|
let event_name = event.name();
|
||||||
|
|
||||||
if event.bubbles() {
|
if event.bubbles() {
|
||||||
add_event_listener(target, event_name, event_handler);
|
add_event_listener(
|
||||||
|
target,
|
||||||
|
event.event_delegation_key(),
|
||||||
|
event_name,
|
||||||
|
event_handler,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
add_event_listener_undelegated(target, &event_name, event_handler);
|
add_event_listener_undelegated(target, &event_name, event_handler);
|
||||||
}
|
}
|
||||||
|
@ -34,6 +39,7 @@ pub fn add_event_helper<E: crate::ev::EventDescriptor + 'static>(
|
||||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||||
pub fn add_event_listener<E>(
|
pub fn add_event_listener<E>(
|
||||||
target: &web_sys::Element,
|
target: &web_sys::Element,
|
||||||
|
key: Cow<'static, str>,
|
||||||
event_name: Cow<'static, str>,
|
event_name: Cow<'static, str>,
|
||||||
#[cfg(debug_assertions)] mut cb: impl FnMut(E) + 'static,
|
#[cfg(debug_assertions)] mut cb: impl FnMut(E) + 'static,
|
||||||
#[cfg(not(debug_assertions))] cb: impl FnMut(E) + 'static,
|
#[cfg(not(debug_assertions))] cb: impl FnMut(E) + 'static,
|
||||||
|
@ -51,9 +57,9 @@ pub fn add_event_listener<E>(
|
||||||
}
|
}
|
||||||
|
|
||||||
let cb = Closure::wrap(Box::new(cb) as Box<dyn FnMut(E)>).into_js_value();
|
let cb = Closure::wrap(Box::new(cb) as Box<dyn FnMut(E)>).into_js_value();
|
||||||
let key = event_delegation_key(&event_name);
|
let key = intern(&key);
|
||||||
_ = js_sys::Reflect::set(target, &JsValue::from_str(&key), &cb);
|
_ = js_sys::Reflect::set(target, &JsValue::from_str(&key), &cb);
|
||||||
add_delegated_event_listener(event_name);
|
add_delegated_event_listener(&key, event_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
@ -61,7 +67,8 @@ pub fn add_event_listener<E>(
|
||||||
pub(crate) fn add_event_listener_undelegated<E>(
|
pub(crate) fn add_event_listener_undelegated<E>(
|
||||||
target: &web_sys::Element,
|
target: &web_sys::Element,
|
||||||
event_name: &str,
|
event_name: &str,
|
||||||
mut cb: impl FnMut(E) + 'static,
|
#[cfg(debug_assertions)] mut cb: impl FnMut(E) + 'static,
|
||||||
|
#[cfg(not(debug_assertions))] cb: impl FnMut(E) + 'static,
|
||||||
) where
|
) where
|
||||||
E: FromWasmAbi + 'static,
|
E: FromWasmAbi + 'static,
|
||||||
{
|
{
|
||||||
|
@ -82,12 +89,15 @@ pub(crate) fn add_event_listener_undelegated<E>(
|
||||||
|
|
||||||
// cf eventHandler in ryansolid/dom-expressions
|
// cf eventHandler in ryansolid/dom-expressions
|
||||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||||
pub(crate) fn add_delegated_event_listener(event_name: Cow<'static, str>) {
|
pub(crate) fn add_delegated_event_listener(
|
||||||
|
key: &str,
|
||||||
|
event_name: Cow<'static, str>,
|
||||||
|
) {
|
||||||
GLOBAL_EVENTS.with(|global_events| {
|
GLOBAL_EVENTS.with(|global_events| {
|
||||||
let mut events = global_events.borrow_mut();
|
let mut events = global_events.borrow_mut();
|
||||||
if !events.contains(&event_name) {
|
if !events.contains(&event_name) {
|
||||||
// create global handler
|
// create global handler
|
||||||
let key = JsValue::from_str(&event_delegation_key(&event_name));
|
let key = JsValue::from_str(&key);
|
||||||
let handler = move |ev: web_sys::Event| {
|
let handler = move |ev: web_sys::Event| {
|
||||||
let target = ev.target();
|
let target = ev.target();
|
||||||
let node = ev.composed_path().get(0);
|
let node = ev.composed_path().get(0);
|
||||||
|
@ -163,11 +173,3 @@ pub(crate) fn add_delegated_event_listener(event_name: Cow<'static, str>) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
|
||||||
pub(crate) fn event_delegation_key(event_name: &str) -> String {
|
|
||||||
let event_name = intern(event_name);
|
|
||||||
let mut n = String::from("$$$");
|
|
||||||
n.push_str(event_name);
|
|
||||||
n
|
|
||||||
}
|
|
||||||
|
|
|
@ -11,6 +11,9 @@ pub trait EventDescriptor: Clone {
|
||||||
/// The name of the event, such as `click` or `mouseover`.
|
/// The name of the event, such as `click` or `mouseover`.
|
||||||
fn name(&self) -> Cow<'static, str>;
|
fn name(&self) -> Cow<'static, str>;
|
||||||
|
|
||||||
|
/// The key used for event delegation.
|
||||||
|
fn event_delegation_key(&self) -> Cow<'static, str>;
|
||||||
|
|
||||||
/// Indicates if this event bubbles. For example, `click` bubbles,
|
/// Indicates if this event bubbles. For example, `click` bubbles,
|
||||||
/// but `focus` does not.
|
/// but `focus` does not.
|
||||||
///
|
///
|
||||||
|
@ -34,6 +37,10 @@ impl<Ev: EventDescriptor> EventDescriptor for undelegated<Ev> {
|
||||||
self.0.name()
|
self.0.name()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn event_delegation_key(&self) -> Cow<'static, str> {
|
||||||
|
self.0.event_delegation_key()
|
||||||
|
}
|
||||||
|
|
||||||
fn bubbles(&self) -> bool {
|
fn bubbles(&self) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
@ -61,6 +68,10 @@ impl<E: FromWasmAbi> EventDescriptor for Custom<E> {
|
||||||
self.name.clone()
|
self.name.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn event_delegation_key(&self) -> Cow<'static, str> {
|
||||||
|
format!("$$${}", self.name).into()
|
||||||
|
}
|
||||||
|
|
||||||
fn bubbles(&self) -> bool {
|
fn bubbles(&self) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
@ -97,6 +108,10 @@ macro_rules! generate_event_types {
|
||||||
stringify!($event).into()
|
stringify!($event).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn event_delegation_key(&self) -> Cow<'static, str> {
|
||||||
|
concat!("$$$", stringify!($event)).into()
|
||||||
|
}
|
||||||
|
|
||||||
$(
|
$(
|
||||||
generate_event_types!($does_not_bubble);
|
generate_event_types!($does_not_bubble);
|
||||||
)?
|
)?
|
||||||
|
|
|
@ -623,9 +623,12 @@ impl<El: ElementDescriptor + 'static> HtmlElement<El> {
|
||||||
}
|
}
|
||||||
let event_name = event.name();
|
let event_name = event.name();
|
||||||
|
|
||||||
|
let key = event.event_delegation_key();
|
||||||
|
|
||||||
if event.bubbles() {
|
if event.bubbles() {
|
||||||
add_event_listener(
|
add_event_listener(
|
||||||
self.element.as_ref(),
|
self.element.as_ref(),
|
||||||
|
key,
|
||||||
event_name,
|
event_name,
|
||||||
event_handler,
|
event_handler,
|
||||||
);
|
);
|
||||||
|
@ -905,6 +908,11 @@ fn create_leptos_element(
|
||||||
id: crate::HydrationKey,
|
id: crate::HydrationKey,
|
||||||
clone_element: fn() -> web_sys::HtmlElement,
|
clone_element: fn() -> web_sys::HtmlElement,
|
||||||
) -> web_sys::HtmlElement {
|
) -> web_sys::HtmlElement {
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
{
|
||||||
|
_ = tag;
|
||||||
|
}
|
||||||
|
|
||||||
if HydrationCtx::is_hydrating() {
|
if HydrationCtx::is_hydrating() {
|
||||||
if let Some(el) = crate::document().get_element_by_id(&format!("_{id}"))
|
if let Some(el) = crate::document().get_element_by_id(&format!("_{id}"))
|
||||||
{
|
{
|
||||||
|
|
|
@ -584,7 +584,7 @@ impl View {
|
||||||
match &self {
|
match &self {
|
||||||
Self::Element(el) => {
|
Self::Element(el) => {
|
||||||
if event.bubbles() {
|
if event.bubbles() {
|
||||||
add_event_listener(&el.element, event.name(), event_handler);
|
add_event_listener(&el.element, event.event_delegation_key(), event.name(), event_handler);
|
||||||
} else {
|
} else {
|
||||||
add_event_listener_undelegated(
|
add_event_listener_undelegated(
|
||||||
&el.element,
|
&el.element,
|
||||||
|
|
Loading…
Reference in New Issue