ratflow 0.1.0

A minimalistic framework for building TUI applications using a reactive architecture.
Documentation
use crate::render::Events;
use crossterm::event::{Event, KeyEvent, MouseEvent};
use reactive_graph::computed::Memo;
use reactive_graph::effect::{Effect, EffectFunction};
use reactive_graph::owner::{LocalStorage, expect_context};
use reactive_graph::signal::Trigger;
use reactive_graph::traits::{IsDisposed, Notify, Track};
use std::time::Duration;
use tokio::time::sleep;

#[inline]
#[track_caller]
pub fn create_effect<T, M>(fun: impl EffectFunction<T, M> + 'static) -> Effect<LocalStorage>
where
    T: 'static,
{
    Effect::new(fun)
}

#[inline]
#[track_caller]
pub fn on_event(mut fun: impl FnMut(&Event) + 'static) -> Effect<LocalStorage> {
    let events = expect_context::<Events>();
    Effect::watch(
        move || events.last(),
        move |event, _, _| {
            fun(event);
        },
        false,
    )
}

#[inline]
#[track_caller]
pub fn on_key(mut fun: impl FnMut(KeyEvent) + 'static) -> Effect<LocalStorage> {
    on_event(move |event| {
        if let Some(key_event) = event.as_key_event() {
            fun(key_event);
        }
    })
}

#[inline]
#[track_caller]
pub fn on_mouse(mut fun: impl FnMut(MouseEvent) + 'static) -> Effect<LocalStorage> {
    on_event(move |event| {
        if let Some(mouse_event) = event.as_mouse_event() {
            fun(mouse_event);
        }
    })
}

#[inline]
#[track_caller]
pub fn on_resize(mut fun: impl FnMut(u16, u16) + 'static) -> Effect<LocalStorage> {
    on_event(move |event| {
        if let Some((x, y)) = event.as_resize_event() {
            fun(x, y);
        }
    })
}

#[inline]
#[track_caller]
pub fn on_key_press(mut fun: impl FnMut(KeyEvent) + 'static) -> Effect<LocalStorage> {
    on_event(move |event| {
        if let Some(key_event) = event.as_key_press_event() {
            fun(key_event);
        }
    })
}

#[inline]
#[track_caller]
pub fn create_memo<T: Send + Sync + 'static + PartialEq>(
    fun: impl Fn() -> T + Send + Sync + 'static,
) -> Memo<T> {
    Memo::new(move |_| fun())
}

#[inline]
#[track_caller]
pub fn create_timeout(mut fun: impl FnMut() + 'static, duration: Duration) -> Effect<LocalStorage> {
    let trigger = Trigger::new();
    any_spawner::Executor::spawn(async move {
        sleep(duration).await;
        trigger.notify();
    });
    Effect::watch(
        move || trigger.track(),
        move |_, _, _| {
            fun();
        },
        false,
    )
}

#[inline]
#[track_caller]
pub fn create_interval(
    mut fun: impl FnMut() + 'static,
    duration: Duration,
) -> Effect<LocalStorage> {
    let trigger = Trigger::new();
    any_spawner::Executor::spawn(async move {
        loop {
            if trigger.is_disposed() {
                return;
            } else {
                trigger.notify();
            }
            sleep(duration).await;
        }
    });
    Effect::watch(
        move || trigger.track(),
        move |_, _, _| {
            fun();
        },
        false,
    )
}