khalas 0.2.0

UI Library based on Seed
use crate::{css::{self, values as val, Background, Border, Size, Style}, model::Model, render::Render, theme::Theme};
use seed::prelude::*;
use palette::Hsla;


#[derive(Debug, Copy, Clone)]
pub enum Msg {
    Clicked(f64, f64),
    Render(Option<RenderTimestampDelta>),
    Done,
}

#[derive(Debug, Clone)]
pub struct Ripple {
    animating: bool,
    orgin: (f64, f64),
    size: f64,
    start: f64,
}

impl Ripple {
    pub fn new(x: f64, y: f64) -> Self {
        Self {
            animating: false,
            orgin: (x, y),
            size: 0.0,
            start: 0.,
        }
    }

    fn orgin(&mut self, point: impl Into<(f64, f64)>) -> &mut Self {
        self.orgin = point.into();
        self
    }

    fn scale(&mut self, scale: impl Into<f64>) -> &mut Self {
        self.size += scale.into();
        self
    }
}

impl<GMsg> Model<Msg, GMsg> for Ripple {
    fn update(&mut self, msg: Msg, orders: &mut impl Orders<Msg, GMsg>) {
        match msg {
            Msg::Clicked(x, y) => {
                *self = Ripple::new(x, y);
                self.animating = true;
                orders.after_next_render(Msg::Render);
            }
            Msg::Render(delta) => {
                let delta: f64 = delta.unwrap_or_default().into();
                if self.start == 0. {
                    self.start = delta;
                }
                let progress = delta - self.start;
                if self.size < 1.0 {
                    self.scale(progress / 1000.);
                    orders.after_next_render(Msg::Render);
                } else {
                    orders.send_msg(Msg::Done);
                }
            }
            Msg::Done => {
                self.start = 0.;
                self.animating = false;
            }
        }
    }
}

impl Render<Msg> for Ripple {
    type View = Node<Msg>;


    fn render(&self, theme: &impl Theme) -> Self::View {
        let background = Background::default()
            .color(Hsla::new(0., 0., 1., 0.8));

        let border = Border::default()
            .radius(1.);

        let size = Size::default()
            .resize(self.size as f32, self.size as f32);

        let style = Style::default()
            .add(St::Position, "relative")
            .add(St::Left, self.orgin.0)
            .add(St::Top, self.orgin.1)
            .merge(&background)
            .merge(&border)
            .merge(&size);

        if self.animating {
            div! [style]
        } else {
            empty![]
        }
    }
}