kael_ui 0.2.0

Professional shadcn-inspired UI component library for Kael. 100+ accessible components for building beautiful, performant desktop applications.
use crate::animations::easings;
use kael::*;
use std::time::Duration;

#[derive(IntoElement)]
pub struct PulseIndicator {
    base: Stateful<Div>,
    color: Hsla,
    dot_size: Pixels,
    speed: Duration,
}

impl PulseIndicator {
    pub fn new(id: impl Into<ElementId>) -> Self {
        Self {
            base: div().id(id.into()),
            color: hsla(142.0 / 360.0, 0.71, 0.45, 1.0),
            dot_size: px(8.0),
            speed: Duration::from_secs(2),
        }
    }

    pub fn color(mut self, color: Hsla) -> Self {
        self.color = color;
        self
    }

    pub fn size(mut self, size: Pixels) -> Self {
        self.dot_size = size;
        self
    }

    pub fn speed(mut self, speed: Duration) -> Self {
        self.speed = speed;
        self
    }
}

impl RenderOnce for PulseIndicator {
    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
        let color = self.color;
        let dot = self.dot_size;
        let ring_max = dot * 2.5;
        let speed = self.speed;

        self.base
            .flex()
            .items_center()
            .justify_center()
            .size(ring_max)
            .child(
                div()
                    .absolute()
                    .rounded_full()
                    .bg(color.opacity(0.6))
                    .size(dot)
                    .with_animation(
                        "pulse-ring",
                        Animation::new(speed)
                            .repeat_forever()
                            .with_easing(easings::ease_out_cubic),
                        move |this, delta| {
                            let current_size = dot + (ring_max - dot) * delta;
                            let current_opacity = 0.6 * (1.0 - delta);
                            this.size(current_size).opacity(current_opacity)
                        },
                    ),
            )
            .child(div().absolute().rounded_full().bg(color).size(dot))
    }
}

impl Styled for PulseIndicator {
    fn style(&mut self) -> &mut StyleRefinement {
        self.base.style()
    }
}

impl InteractiveElement for PulseIndicator {
    fn interactivity(&mut self) -> &mut Interactivity {
        self.base.interactivity()
    }
}

impl StatefulInteractiveElement for PulseIndicator {}

impl ParentElement for PulseIndicator {
    fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
        self.base.extend(elements)
    }
}