Skip to content

Commit

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

* slight improvements

* clean up and docs

* update doc example

* add test

* update test
  • Loading branch information
marc2332 authored Dec 21, 2024
1 parent f12fbaa commit e6fd369
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 0 deletions.
2 changes: 2 additions & 0 deletions crates/components/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ mod menu;
mod native_container;
mod native_router;
mod network_image;
mod overflowed_content;
mod popup;
mod progress_bar;
mod radio;
Expand Down Expand Up @@ -63,6 +64,7 @@ pub use menu::*;
pub use native_container::*;
pub use native_router::*;
pub use network_image::*;
pub use overflowed_content::*;
pub use popup::*;
pub use progress_bar::*;
pub use radio::*;
Expand Down
137 changes: 137 additions & 0 deletions crates/components/src/overflowed_content.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
use std::time::Duration;

use dioxus::prelude::*;
use freya_elements::elements as dioxus_elements;
use freya_hooks::{
use_animation,
use_node_signal,
AnimDirection,
AnimNum,
Ease,
Function,
OnFinish,
};

/// Animate the content of a container when the content overflows.
///
/// This is primarily targeted to text that can't be fully shown in small layouts.
///
/// # Example
///
/// ```no_run
/// # use freya::prelude::*;
/// fn app() -> Element {
/// rsx!(
/// Button {
/// OverflowedContent {
/// width: "100",
/// rect {
/// direction: "horizontal",
/// cross_align: "center",
/// label {
/// "Freya is a cross-platform GUI library for Rust"
/// }
/// }
/// }
/// }
/// )
/// }
/// ```
#[component]
pub fn OverflowedContent(
children: Element,
#[props(default = "100%".to_string())] width: String,
#[props(default = "auto".to_string())] height: String,
#[props(default = Duration::from_secs(4))] duration: Duration,
) -> Element {
let (label_reference, label_size) = use_node_signal();
let (rect_reference, rect_size) = use_node_signal();

let rect_width = rect_size.read().area.width();
let label_width = label_size.read().area.width();
let does_overflow = label_width > rect_width;

let animations = use_animation(move |ctx| {
ctx.on_finish(OnFinish::Restart);

ctx.with(
AnimNum::new(0., 100.)
.duration(duration)
.ease(Ease::InOut)
.function(Function::Linear),
)
});

use_effect(use_reactive!(|does_overflow| {
if does_overflow {
animations.run(AnimDirection::Forward);
}
}));

let progress = animations.get();
let progress = progress.read().as_f32();
let offset_x = if does_overflow {
((label_width + rect_width) * progress / 100.) - rect_width
} else {
0.
};

rsx!(
rect {
width,
height,
offset_x: "{-offset_x}",
overflow: "clip",
reference: rect_reference,
rect {
reference: label_reference,
max_lines: "1",
{children}
}
}
)
}

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

use freya::prelude::*;
use freya_testing::prelude::*;
use tokio::time::sleep;

#[tokio::test]
pub async fn overflowed_content() {
fn app() -> Element {
rsx!(
OverflowedContent {
duration: Duration::from_millis(50),
width: "50",
label {
"123456789123456789"
}
}
)
}

let mut utils = launch_test(app);

let root = utils.root();
let label = root.get(0).get(0).get(0);

utils.wait_for_update().await;
utils.wait_for_update().await;
assert_eq!(label.layout().unwrap().area.min_x(), 50.);

sleep(Duration::from_millis(50)).await;
utils.wait_for_update().await;
utils.wait_for_update().await;
assert!(label.layout().unwrap().area.min_x() < 0.);

sleep(Duration::from_millis(50)).await;
utils.wait_for_update().await;
utils.wait_for_update().await;
utils.wait_for_update().await;
assert_eq!(label.layout().unwrap().area.min_x(), 50.);
}
}
3 changes: 3 additions & 0 deletions crates/elements/src/definitions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,9 @@ builder_constructors! {
#[doc = include_str!("_docs/attributes/opacity.md")]
opacity: String,

// Reference
reference: Reference,

// Accessibility
a11y_id: String,
a11y_auto_focus: String,
Expand Down
30 changes: 30 additions & 0 deletions examples/animated_overflow_content.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#![cfg_attr(
all(not(debug_assertions), target_os = "windows"),
windows_subsystem = "windows"
)]

use freya::prelude::*;

fn main() {
launch_with_props(app, "Animation", (400.0, 350.0));
}

import_svg!(Ferris, "./ferris.svg", "70%", "50%");

fn app() -> Element {
rsx!(
Button {
OverflowedContent {
width: "100",
rect {
direction: "horizontal",
cross_align: "center",
Ferris { }
label {
"Freya is a cross-platform GUI library for Rust"
}
}
}
}
)
}

0 comments on commit e6fd369

Please sign in to comment.