kael_ui 0.2.0

Professional shadcn-inspired UI component library for Kael. 100+ accessible components for building beautiful, performant desktop applications.
use kael::{prelude::FluentBuilder as _, *};

pub struct SpotlightState {
    mouse_pos: Option<Point<Pixels>>,
}

impl Default for SpotlightState {
    fn default() -> Self {
        Self::new()
    }
}

impl SpotlightState {
    pub fn new() -> Self {
        Self { mouse_pos: None }
    }
}

#[derive(IntoElement)]
pub struct Spotlight {
    id: ElementId,
    state: Entity<SpotlightState>,
    color: Option<Hsla>,
    spot_size: Pixels,
    intensity: f32,
    children: Vec<AnyElement>,
    style: StyleRefinement,
}

impl Spotlight {
    pub fn new(id: impl Into<ElementId>, state: Entity<SpotlightState>) -> Self {
        Self {
            id: id.into(),
            state,
            color: None,
            spot_size: px(300.0),
            intensity: 0.15,
            children: Vec::new(),
            style: StyleRefinement::default(),
        }
    }

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

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

    pub fn intensity(mut self, intensity: f32) -> Self {
        self.intensity = intensity.clamp(0.0, 1.0);
        self
    }
}

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

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

impl RenderOnce for Spotlight {
    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
        let spot_color = self.color.unwrap_or(hsla(0.6, 0.8, 0.7, 1.0));
        let spot_size = self.spot_size;
        let intensity = self.intensity;
        let mouse_pos = self.state.read(cx).mouse_pos;
        let half_size = px(f32::from(spot_size) / 2.0);
        let user_style = self.style;

        let state_for_move = self.state.clone();

        let mut container = div()
            .id(self.id)
            .relative()
            .overflow_hidden()
            .on_mouse_move(move |event: &MouseMoveEvent, _, cx| {
                state_for_move.update(cx, |s, cx| {
                    s.mouse_pos = Some(event.position);
                    cx.notify();
                });
            })
            .map(|mut el| {
                el.style().refine(&user_style);
                el
            });

        for child in self.children {
            container = container.child(child);
        }

        if let Some(pos) = mouse_pos {
            let glow_color = Hsla {
                a: intensity,
                ..spot_color
            };

            let glow = div()
                .absolute()
                .left(pos.x - half_size)
                .top(pos.y - half_size)
                .w(spot_size)
                .h(spot_size)
                .rounded(spot_size)
                .bg(glow_color);

            container = container.child(glow);
        }

        container
    }
}