mirror of
https://github.com/adoyle0/thaw.git
synced 2025-01-23 06:19:22 -05:00
feat(lepots-v0.7): CSSTransition and ClassList
This commit is contained in:
parent
918d924342
commit
8ccef81e44
6 changed files with 118 additions and 128 deletions
|
@ -56,7 +56,6 @@ pub fn ComponentsPage() -> impl IntoView {
|
||||||
<SiteHeader/>
|
<SiteHeader/>
|
||||||
<Layout has_sider=true position=LayoutPosition::Absolute attr:style="top: 64px;">
|
<Layout has_sider=true position=LayoutPosition::Absolute attr:style="top: 64px;">
|
||||||
<div class="demo-components__sider">
|
<div class="demo-components__sider">
|
||||||
{move || select_name.get()}
|
|
||||||
<NavDrawer selected_value=select_name>
|
<NavDrawer selected_value=select_name>
|
||||||
{
|
{
|
||||||
gen_menu_data().into_iter().map(|data| {
|
gen_menu_data().into_iter().map(|data| {
|
||||||
|
|
|
@ -59,7 +59,7 @@ pub fn AccordionItem(
|
||||||
<div
|
<div
|
||||||
class="thaw-accordion-panel"
|
class="thaw-accordion-panel"
|
||||||
node_ref=panel_ref
|
node_ref=panel_ref
|
||||||
// style=("display: none", display.with(|d| d.is_none()))
|
style=move || display.get().unwrap_or_default()
|
||||||
>
|
>
|
||||||
{children()}
|
{children()}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
use leptos::prelude::*;
|
use leptos::{either::Either, prelude::*};
|
||||||
use thaw_components::OptionComp;
|
|
||||||
use thaw_utils::{class_list, mount_style};
|
use thaw_utils::{class_list, mount_style};
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn Table(
|
pub fn Table(
|
||||||
#[prop(optional, into)] style: MaybeProp<String>,
|
|
||||||
#[prop(optional, into)] class: MaybeProp<String>,
|
#[prop(optional, into)] class: MaybeProp<String>,
|
||||||
children: Children,
|
children: Children,
|
||||||
) -> impl IntoView {
|
) -> impl IntoView {
|
||||||
|
@ -31,9 +29,13 @@ pub fn TableHeaderCell(#[prop(optional)] children: Option<Children>) -> impl Int
|
||||||
view! {
|
view! {
|
||||||
<th class="thaw-table-header-cell">
|
<th class="thaw-table-header-cell">
|
||||||
<button class="thaw-table-header-cell__button" role="presentation">
|
<button class="thaw-table-header-cell__button" role="presentation">
|
||||||
<OptionComp value=children let:children>
|
{
|
||||||
{children()}
|
if let Some(children) = children {
|
||||||
</OptionComp>
|
Either::Left(children())
|
||||||
|
} else {
|
||||||
|
Either::Right(())
|
||||||
|
}
|
||||||
|
}
|
||||||
</button>
|
</button>
|
||||||
</th>
|
</th>
|
||||||
}
|
}
|
||||||
|
@ -61,9 +63,13 @@ pub fn TableRow(children: Children) -> impl IntoView {
|
||||||
pub fn TableCell(#[prop(optional)] children: Option<Children>) -> impl IntoView {
|
pub fn TableCell(#[prop(optional)] children: Option<Children>) -> impl IntoView {
|
||||||
view! {
|
view! {
|
||||||
<td class="thaw-table-cell">
|
<td class="thaw-table-cell">
|
||||||
<OptionComp value=children let:children>
|
{
|
||||||
{children()}
|
if let Some(children) = children {
|
||||||
</OptionComp>
|
Either::Left(children())
|
||||||
|
} else {
|
||||||
|
Either::Right(())
|
||||||
|
}
|
||||||
|
}
|
||||||
</td>
|
</td>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,10 @@ where
|
||||||
IV: IntoView + 'static,
|
IV: IntoView + 'static,
|
||||||
{
|
{
|
||||||
let display = RwSignal::new((!show.get_untracked()).then_some("display: none;"));
|
let display = RwSignal::new((!show.get_untracked()).then_some("display: none;"));
|
||||||
|
let next_frame = NextFrame::new();
|
||||||
|
let end_handle = StoredValue::new(None::<EventListenerHandle>);
|
||||||
|
let end_count = StoredValue::new(None::<usize>);
|
||||||
|
let finish = StoredValue::new(None::<Box<dyn FnOnce() + Send + Sync>>);
|
||||||
|
|
||||||
Effect::new(move |_| {
|
Effect::new(move |_| {
|
||||||
let target_ref = node_ref.get();
|
let target_ref = node_ref.get();
|
||||||
|
@ -35,27 +39,23 @@ where
|
||||||
};
|
};
|
||||||
|
|
||||||
let class_list = el.class_list();
|
let class_list = el.class_list();
|
||||||
let next_frame = NextFrame::use_();
|
|
||||||
let end_handle = StoredValue::new(None::<EventListenerHandle>);
|
|
||||||
let end_count = StoredValue::new(None::<usize>);
|
|
||||||
let finish = StoredValue::new(None::<Callback<()>>);
|
|
||||||
|
|
||||||
let on_end = Callback::new({
|
let on_end = {
|
||||||
let el = send_wrapper::SendWrapper::new(el.clone());
|
let el = send_wrapper::SendWrapper::new(el.clone());
|
||||||
move |remove: Callback<()>| {
|
move |remove: Box<dyn FnOnce() + Send + Sync>| {
|
||||||
let Some(CSSTransitionInfo {
|
let Some(CSSTransitionInfo {
|
||||||
types,
|
types,
|
||||||
prop_count,
|
prop_count,
|
||||||
timeout,
|
timeout,
|
||||||
}) = get_transition_info(&el)
|
}) = get_transition_info(&el)
|
||||||
else {
|
else {
|
||||||
remove.call(());
|
remove();
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
finish.set_value(Some(Callback::new(move |_| {
|
finish.set_value(Some(Box::new(move || {
|
||||||
end_count.set_value(None);
|
end_count.set_value(None);
|
||||||
remove.call(());
|
remove();
|
||||||
end_handle.update_value(|h| {
|
end_handle.update_value(|h| {
|
||||||
h.take().map(|h| {
|
h.take().map(|h| {
|
||||||
h.remove();
|
h.remove();
|
||||||
|
@ -65,9 +65,9 @@ where
|
||||||
|
|
||||||
set_timeout(
|
set_timeout(
|
||||||
move || {
|
move || {
|
||||||
finish.try_update_value(|v| {
|
if let Some(Some(f)) = finish.try_update_value(|f| f.take()) {
|
||||||
v.take().map(|f| f.call(()));
|
f();
|
||||||
});
|
}
|
||||||
},
|
},
|
||||||
Duration::from_millis(timeout + 1),
|
Duration::from_millis(timeout + 1),
|
||||||
);
|
);
|
||||||
|
@ -86,9 +86,9 @@ where
|
||||||
};
|
};
|
||||||
*v >= prop_count
|
*v >= prop_count
|
||||||
}) {
|
}) {
|
||||||
finish.update_value(|v| {
|
if let Some(Some(f)) = finish.try_update_value(|f| f.take()) {
|
||||||
v.take().map(|f| f.call(()));
|
f();
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let handle = match types {
|
let handle = match types {
|
||||||
|
@ -105,22 +105,31 @@ where
|
||||||
};
|
};
|
||||||
end_handle.set_value(Some(handle));
|
end_handle.set_value(Some(handle));
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
let on_finish = move || {
|
|
||||||
finish.update_value(|v| {
|
|
||||||
v.take().map(|f| f.call(()));
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let on_enter_fn = {
|
let on_finish = move || {
|
||||||
let class_list = class_list.clone();
|
if let Some(Some(f)) = finish.try_update_value(|f| f.take()) {
|
||||||
let class_list = send_wrapper::SendWrapper::new(class_list);
|
f();
|
||||||
let on_before_enter = on_before_enter.clone();
|
}
|
||||||
let on_after_enter = on_after_enter.clone();
|
};
|
||||||
let on_enter = on_enter.clone();
|
|
||||||
let on_end = on_end.clone();
|
let name = name.clone();
|
||||||
Callback::new(move |name: String| {
|
let effect = RenderEffect::new(move |prev: Option<bool>| {
|
||||||
|
let show = show.get();
|
||||||
|
let prev = if let Some(prev) = prev {
|
||||||
|
prev
|
||||||
|
} else if show && appear {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
return show;
|
||||||
|
};
|
||||||
|
|
||||||
|
let name = name.get_untracked();
|
||||||
|
|
||||||
|
if show && !prev {
|
||||||
|
on_finish();
|
||||||
|
{
|
||||||
|
// on_enter
|
||||||
if let Some(on_before_enter) = on_before_enter.as_ref() {
|
if let Some(on_before_enter) = on_before_enter.as_ref() {
|
||||||
on_before_enter.call(());
|
on_before_enter.call(());
|
||||||
}
|
}
|
||||||
|
@ -132,36 +141,29 @@ where
|
||||||
display.set(None);
|
display.set(None);
|
||||||
|
|
||||||
let class_list = class_list.clone();
|
let class_list = class_list.clone();
|
||||||
let on_after_enter = on_after_enter.clone();
|
|
||||||
let on_end = on_end.clone();
|
let on_end = on_end.clone();
|
||||||
let on_enter = on_enter.clone();
|
|
||||||
next_frame.run(move || {
|
next_frame.run(move || {
|
||||||
let _ = class_list.remove_1(&enter_from);
|
let _ = class_list.remove_1(&enter_from);
|
||||||
let _ = class_list.add_1(&enter_to);
|
let _ = class_list.add_1(&enter_to);
|
||||||
|
|
||||||
let remove = Callback::new(move |_| {
|
let class_list = send_wrapper::SendWrapper::new(class_list);
|
||||||
|
let remove = Box::new(move || {
|
||||||
let _ = class_list.remove_2(&enter_active, &enter_to);
|
let _ = class_list.remove_2(&enter_active, &enter_to);
|
||||||
if let Some(on_after_enter) = on_after_enter.as_ref() {
|
if let Some(on_after_enter) = on_after_enter.as_ref() {
|
||||||
on_after_enter.call(());
|
on_after_enter.call(());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
on_end.call(remove);
|
on_end(remove);
|
||||||
|
|
||||||
if let Some(on_enter) = on_enter {
|
if let Some(on_enter) = on_enter.as_ref() {
|
||||||
on_enter.call(());
|
on_enter.call(());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
})
|
}
|
||||||
};
|
} else if !show && prev {
|
||||||
|
on_finish();
|
||||||
let on_leave_fn = {
|
{
|
||||||
let class_list = class_list.clone();
|
// on_leave
|
||||||
let class_list = send_wrapper::SendWrapper::new(class_list);
|
|
||||||
let on_before_leave = on_before_leave.clone();
|
|
||||||
let on_after_leave = on_after_leave.clone();
|
|
||||||
let on_leave = on_leave.clone();
|
|
||||||
let on_end = on_end.clone();
|
|
||||||
Callback::new(move |name: String| {
|
|
||||||
if let Some(on_before_leave) = on_before_leave.as_ref() {
|
if let Some(on_before_leave) = on_before_leave.as_ref() {
|
||||||
on_before_leave.call(());
|
on_before_leave.call(());
|
||||||
}
|
}
|
||||||
|
@ -179,46 +181,27 @@ where
|
||||||
let _ = class_list.remove_1(&leave_from);
|
let _ = class_list.remove_1(&leave_from);
|
||||||
let _ = class_list.add_1(&leave_to);
|
let _ = class_list.add_1(&leave_to);
|
||||||
|
|
||||||
let remove = Callback::new(move |_| {
|
let class_list = send_wrapper::SendWrapper::new(class_list);
|
||||||
|
let remove = Box::new(move || {
|
||||||
let _ = class_list.remove_2(&leave_active, &leave_to);
|
let _ = class_list.remove_2(&leave_active, &leave_to);
|
||||||
display.set(Some("display: none;"));
|
display.set(Some("display: none;"));
|
||||||
if let Some(on_after_leave) = on_after_leave.as_ref() {
|
if let Some(on_after_leave) = on_after_leave.as_ref() {
|
||||||
on_after_leave.call(());
|
on_after_leave.call(());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
on_end.call(remove);
|
on_end(remove);
|
||||||
if let Some(on_leave) = on_leave {
|
if let Some(on_leave) = on_leave {
|
||||||
on_leave.call(());
|
on_leave.call(());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
})
|
}
|
||||||
};
|
|
||||||
|
|
||||||
let name = name.clone();
|
|
||||||
let _ = RenderEffect::new(move |prev: Option<bool>| {
|
|
||||||
let show = show.get();
|
|
||||||
let prev = if let Some(prev) = prev {
|
|
||||||
prev
|
|
||||||
} else if show && appear {
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
return show;
|
|
||||||
};
|
|
||||||
|
|
||||||
let name = name.get_untracked();
|
|
||||||
|
|
||||||
if show && !prev {
|
|
||||||
on_finish();
|
|
||||||
on_enter_fn.call(name);
|
|
||||||
} else if !show && prev {
|
|
||||||
on_finish();
|
|
||||||
on_leave_fn.call(name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
show
|
show
|
||||||
});
|
});
|
||||||
|
|
||||||
on_cleanup(move || {
|
on_cleanup(move || {
|
||||||
|
drop(effect);
|
||||||
end_handle.update_value(|handle| {
|
end_handle.update_value(|handle| {
|
||||||
if let Some(handle) = handle.take() {
|
if let Some(handle) = handle.take() {
|
||||||
handle.remove();
|
handle.remove();
|
||||||
|
|
|
@ -1,12 +1,8 @@
|
||||||
#[cfg(not(feature = "ssr"))]
|
#[cfg(not(feature = "ssr"))]
|
||||||
use leptos::prelude::RenderEffect;
|
use leptos::prelude::RenderEffect;
|
||||||
use leptos::{
|
use leptos::{
|
||||||
logging::log,
|
|
||||||
prelude::{MaybeProp, Memo, Oco, RwSignal},
|
prelude::{MaybeProp, Memo, Oco, RwSignal},
|
||||||
reactive_graph::{
|
reactive_graph::traits::{Get, Update, With, WithUntracked},
|
||||||
graph::{AnySubscriber, ToAnySubscriber},
|
|
||||||
traits::{Get, Update, With, WithUntracked},
|
|
||||||
},
|
|
||||||
tachys::renderer::DomRenderer,
|
tachys::renderer::DomRenderer,
|
||||||
};
|
};
|
||||||
use std::{collections::HashSet, sync::Arc};
|
use std::{collections::HashSet, sync::Arc};
|
||||||
|
@ -14,7 +10,8 @@ use std::{collections::HashSet, sync::Arc};
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
pub struct ClassList {
|
pub struct ClassList {
|
||||||
value: RwSignal<HashSet<Oco<'static, str>>>,
|
value: RwSignal<HashSet<Oco<'static, str>>>,
|
||||||
effects: Vec<AnySubscriber>,
|
effects_oco: Vec<Arc<RenderEffect<Oco<'static, str>>>>,
|
||||||
|
effects_option_oco: Vec<Arc<RenderEffect<Option<Oco<'static, str>>>>>,
|
||||||
effects_bool: Vec<Arc<RenderEffect<bool>>>,
|
effects_bool: Vec<Arc<RenderEffect<bool>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +55,7 @@ impl ClassList {
|
||||||
}
|
}
|
||||||
name
|
name
|
||||||
});
|
});
|
||||||
self.effects.push(effect.to_any_subscriber());
|
self.effects_oco.push(effect.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Class::FnOptionString(f) => {
|
Class::FnOptionString(f) => {
|
||||||
|
@ -99,7 +96,7 @@ impl ClassList {
|
||||||
}
|
}
|
||||||
name
|
name
|
||||||
});
|
});
|
||||||
self.effects.push(effect.to_any_subscriber());
|
self.effects_option_oco.push(effect.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Class::Fn(name, f) => {
|
Class::Fn(name, f) => {
|
||||||
|
@ -117,7 +114,6 @@ impl ClassList {
|
||||||
let effect = RenderEffect::new(move |old| {
|
let effect = RenderEffect::new(move |old| {
|
||||||
let name = name.clone();
|
let name = name.clone();
|
||||||
let new = f();
|
let new = f();
|
||||||
log!("ClassList: {name} {new} {old:#?}");
|
|
||||||
if old.is_none() {
|
if old.is_none() {
|
||||||
if new {
|
if new {
|
||||||
self.value.update(|set| {
|
self.value.update(|set| {
|
||||||
|
@ -209,11 +205,17 @@ where
|
||||||
fn build(self, el: &R::Element) -> Self::State {
|
fn build(self, el: &R::Element) -> Self::State {
|
||||||
let el = el.to_owned();
|
let el = el.to_owned();
|
||||||
RenderEffect::new(move |prev| {
|
RenderEffect::new(move |prev| {
|
||||||
if let Some(_) = prev {
|
|
||||||
unreachable!()
|
|
||||||
} else {
|
|
||||||
let mut class = String::new();
|
let mut class = String::new();
|
||||||
self.write_class_string(&mut class);
|
self.write_class_string(&mut class);
|
||||||
|
if let Some(state) = prev {
|
||||||
|
let (el, prev_class) = state;
|
||||||
|
if class != prev_class {
|
||||||
|
R::set_attribute(&el, "class", &class);
|
||||||
|
(el, class)
|
||||||
|
} else {
|
||||||
|
(el, prev_class)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if !class.is_empty() {
|
if !class.is_empty() {
|
||||||
R::set_attribute(&el, "class", &class);
|
R::set_attribute(&el, "class", &class);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ impl Default for NextFrame {
|
||||||
impl Copy for NextFrame {}
|
impl Copy for NextFrame {}
|
||||||
|
|
||||||
impl NextFrame {
|
impl NextFrame {
|
||||||
pub fn use_() -> Self {
|
pub fn new() -> Self {
|
||||||
let next_frame = NextFrame::default();
|
let next_frame = NextFrame::default();
|
||||||
|
|
||||||
on_cleanup(move || {
|
on_cleanup(move || {
|
||||||
|
|
Loading…
Add table
Reference in a new issue