Skip to content

Commit

Permalink
feat: New AnimatedPosition component (#1013)
Browse files Browse the repository at this point in the history
* feat: New `AnimatedPosition` component

* clean up

* .

* fix: Handle reoderdering of keyed children

* fmt

* comment out some compositor tests

* some improvements

* missing default

* fix lint

* fixes and test

* remove print

* rename fixed to global, and add torin tests

* position docs
  • Loading branch information
marc2332 authored Dec 21, 2024
1 parent 70903e8 commit f12fbaa
Show file tree
Hide file tree
Showing 14 changed files with 775 additions and 242 deletions.
163 changes: 163 additions & 0 deletions crates/components/src/animated_position.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
use std::time::Duration;

use dioxus::prelude::*;
use freya_elements::elements as dioxus_elements;
use freya_hooks::{
use_animation_with_dependencies,
use_node_signal_with_prev,
AnimDirection,
AnimNum,
Ease,
Function,
};

#[component]
pub fn AnimatedPosition(
children: Element,
width: String,
height: String,
#[props(default = Function::default())] function: Function,
#[props(default = Duration::from_millis(250))] duration: Duration,
#[props(default = Ease::default())] ease: Ease,
) -> Element {
let mut render_element = use_signal(|| false);
let (reference, size, old_size) = use_node_signal_with_prev();

let animations = use_animation_with_dependencies(
&(function, duration, ease),
move |ctx, (function, duration, ease)| {
let old_size = old_size().unwrap_or_default();
let size = size().unwrap_or_default();
(
ctx.with(
AnimNum::new(size.area.origin.x, old_size.area.origin.x)
.duration(duration)
.ease(ease)
.function(function),
),
ctx.with(
AnimNum::new(size.area.origin.y, old_size.area.origin.y)
.duration(duration)
.ease(ease)
.function(function),
),
)
},
);

use_effect(move || {
if animations.is_running() {
render_element.set(true);
}
});

use_effect(move || {
let has_size = size.read().is_some();
let has_old_size = old_size.read().is_some();
if has_size && has_old_size {
animations.run(AnimDirection::Reverse);
} else if has_size {
render_element.set(true);
}
});

let (offset_x, offset_y) = animations.get();
let offset_x = offset_x.read().as_f32();
let offset_y = offset_y.read().as_f32();

rsx!(
rect {
reference,
width: "{width}",
height: "{height}",
rect {
width: "0",
height: "0",
offset_x: "{offset_x}",
offset_y: "{offset_y}",
position: "global",
if render_element() {
rect {
width: "{size.read().as_ref().unwrap().area.width()}",
height: "{size.read().as_ref().unwrap().area.height()}",
{children}
}
}
}
}
)
}

#[cfg(test)]
mod test {
use std::time::Duration;

use freya::prelude::*;
use freya_testing::prelude::*;

#[tokio::test]
pub async fn animated_position() {
fn animated_position_app() -> Element {
let mut padding = use_signal(|| (100., 100.));

rsx!(
rect {
padding: "{padding().0} {padding().1}",
onclick: move |_| {
padding.write().0 += 10.;
padding.write().1 += 10.;
},
AnimatedPosition {
width: "50",
height: "50",
function: Function::Linear
}
}
)
}

let mut utils = launch_test(animated_position_app);

// Disable event loop ticker
utils.config().event_loop_ticker = false;

let root = utils.root();
utils.wait_for_update().await;
utils.wait_for_update().await;

let get_positions = || {
root.get(0)
.get(0)
.get(0)
.get(0)
.layout()
.unwrap()
.area
.origin
};

assert_eq!(get_positions().x, 100.);
assert_eq!(get_positions().y, 100.);

utils.click_cursor((5.0, 5.0)).await;
utils.wait_for_update().await;
utils.wait_for_update().await;
tokio::time::sleep(Duration::from_millis(125)).await;
utils.wait_for_update().await;
utils.wait_for_update().await;

assert!(get_positions().x < 106.);
assert!(get_positions().x > 105.);

assert!(get_positions().y < 106.);
assert!(get_positions().y > 105.);

utils.config().event_loop_ticker = true;

utils.wait_for_update().await;
tokio::time::sleep(Duration::from_millis(125)).await;
utils.wait_for_update().await;

assert_eq!(get_positions().x, 110.);
}
}
11 changes: 10 additions & 1 deletion crates/components/src/drag_drop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,14 +115,21 @@ pub struct DropZoneProps<T: 'static + PartialEq + Clone> {
children: Element,
/// Handler for the `ondrop` event.
ondrop: EventHandler<T>,
/// Width of the [DropZone].
#[props(default = "auto".to_string())]
width: String,
/// Height of the [DropZone].
#[props(default = "auto".to_string())]
height: String,
}

/// Elements from [`DragZone`]s can be dropped here.
#[allow(non_snake_case)]
pub fn DropZone<T: 'static + Clone + PartialEq>(props: DropZoneProps<T>) -> Element {
let mut drags = use_context::<Signal<Option<T>>>();

let onmouseup = move |_: MouseEvent| {
let onmouseup = move |e: MouseEvent| {
e.stop_propagation();
if let Some(current_drags) = &*drags.read() {
props.ondrop.call(current_drags.clone());
}
Expand All @@ -134,6 +141,8 @@ pub fn DropZone<T: 'static + Clone + PartialEq>(props: DropZoneProps<T>) -> Elem
rsx!(
rect {
onmouseup,
width: props.width,
height: props.height,
{props.children}
}
)
Expand Down
2 changes: 2 additions & 0 deletions crates/components/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
mod accordion;
mod activable_route;
mod animated_position;
mod animated_router;
mod body;
mod button;
Expand Down Expand Up @@ -42,6 +43,7 @@ mod window_drag_area;

pub use accordion::*;
pub use activable_route::*;
pub use animated_position::*;
pub use animated_router::*;
pub use body::*;
pub use button::*;
Expand Down
Loading

0 comments on commit f12fbaa

Please sign in to comment.