trading-charts 0.1.2

Rust binding of Lightweight Charts for Leptos
use super::data::options::ChartOptions;
use crate::bindings::{JsError, TradingChartBinding};
use leptos::{
    tachys::{
        html::{
            attribute::global::{StyleAttribute, ClassAttribute},
            node_ref::NodeRefAttribute,
        },
        view::any_view::IntoAny,
        reactive_graph::node_ref::NodeRef,
    },
    reactive::{
        effect::Effect,
        traits::{Get, With, WithUntracked},
        signal::ReadSignal,
    },
    context::provide_context,
    children::Children,
    html::Div,
    IntoView,
    component,
    view,
};

fn make_chart(options: Option<ReadSignal<ChartOptions>>) -> Result<TradingChartBinding, JsError> {
    Ok(match options {
        None => TradingChartBinding::new(None)?,
        Some(options) => {
            let chart = options.with_untracked(|options| TradingChartBinding::new(Some(options)))?;
            let _ = Effect::new({
                let chart = chart.clone();

                move || {
                    options.with(|options| {
                        if let Err(err) = chart.apply_chart_options(options) {
                            err.log();
                        }
                    })
                }
            });

            chart
        }
    })
}

#[component]
pub fn Chart<'a, 'b>(
    #[prop(optional)] options: Option<ReadSignal<ChartOptions>>,

    #[prop(optional)] style: Option<&'a str>,

    #[prop(optional)] class: Option<&'b str>,

    #[prop(optional)] children: Option<Children>,
) -> impl IntoView {
    let chart = match make_chart(options) {
        Ok(chart) => chart,
        Err(err) => {
            err.log();
            return view!().into_any();
        }
    };

    let node_ref = NodeRef::<Div>::new();
    let _ = Effect::new({
        let chart = chart.clone();

        move || {
            if let Some(node) = node_ref.get() {
                if let Err(err) = chart.bind_chart(node) {
                    err.log();
                }
            }
        }
    });

    provide_context(chart);

    let style = style.map_or_else(String::new, |s| s.to_string());
    let class = class.map_or_else(String::new, |s| s.to_string());
    let children = match children {
        Some(children) => children().into_any(),
        None => (view! {<></>}).into_any(),
    };

    let res = view! {
        <>
            <div style=style class=class node_ref={node_ref}/>
            {children}
        </>
    };

    res.into_any()
}