vertigo 0.13.1

Reactive Real-DOM library with SSR for Rust
Documentation
use std::rc::Rc;

use crate::DropResource;

use super::{
    Computed, Context, GraphId, ToComputed, dependencies::get_dependencies, value_inner::ValueInner,
};

/// A reactive value. Basic building block of app state.
///
/// Can be read or written.
///
/// ```rust
/// use vertigo::{Value, transaction};
///
/// let value = Value::new(5);
///
/// transaction(|context| {
///     assert_eq!(value.get(context), 5);
/// });
///
/// value.set(10);
///
/// transaction(|context| {
///     assert_eq!(value.get(context), 10);
/// });
/// ```
///
#[derive(Clone)]
pub struct Value<T: Clone + PartialEq + 'static> {
    inner: Rc<ValueInner<T>>,
}

impl<T: Clone + PartialEq + Default + 'static> Default for Value<T> {
    fn default() -> Self {
        Self::new(T::default())
    }
}

impl<T: Clone + PartialEq> PartialEq for Value<T> {
    fn eq(&self, other: &Self) -> bool {
        self.inner.id.eq(&other.inner.id)
    }
}

impl<T: Clone + PartialEq + 'static> ToComputed<T> for Value<T> {
    fn to_computed(&self) -> Computed<T> {
        self.to_computed()
    }
}

impl<T: Clone + PartialEq + 'static> ToComputed<T> for &Value<T> {
    fn to_computed(&self) -> Computed<T> {
        (*self).to_computed()
    }
}

impl<T: Clone + PartialEq + 'static> Value<T> {
    pub fn new(value: T) -> Self {
        Value {
            inner: Rc::new(ValueInner::new(value)),
        }
    }

    /// Create a value that is connected to a generator, where `value` parameter is a starting value,
    /// and `create` function takes care of updating it.
    ///
    /// See [Router implementation](../src/vertigo/router.rs.html#97) for example example.
    pub fn with_connect<F>(value: T, create: F) -> Computed<T>
    where
        F: Fn(&Value<T>) -> DropResource + 'static,
    {
        let value = Value::new(value);
        let value_clone = value.clone();
        value
            .to_computed()
            .when_connect(move || create(&value_clone))
    }

    /// Get the value.
    ///
    /// Use this in callbacks. You can get [Context] object using [transaction](crate::transaction) function.
    /// During rendering you should directly embed the `Value` in [dom!](crate::dom) or use [.render_value()](Value::render_value) method.
    ///
    /// Returned `T` is cloned - it's not reactive.
    pub fn get(&self, context: &Context) -> T {
        context.add_parent(self.inner.id, self.inner.clone());
        self.inner.get()
    }

    /// Reactively convert `Value` into [Computed] with provided transformation function applied.
    pub fn map<K: Clone + 'static, F: 'static + Fn(T) -> K>(&self, fun: F) -> Computed<K> {
        Computed::from({
            let myself = self.clone();
            move |context| fun(myself.get(context))
        })
    }

    pub fn id(&self) -> GraphId {
        self.inner.id
    }

    /// Reactively convert the `Value`` into [Computed] without any mapping.
    pub fn to_computed(&self) -> Computed<T> {
        let myself = self.clone();

        Computed::from(move |context| myself.get(context))
    }

    pub fn change(&self, change_fn: impl FnOnce(&mut T)) {
        get_dependencies().transaction(|ctx| {
            let mut value = self.get(ctx);
            change_fn(&mut value);
            self.set(value);
        });
    }

    pub fn set(&self, value: T) {
        get_dependencies().transaction(|_| {
            let need_refresh = self.inner.set(value);
            if need_refresh {
                get_dependencies().report_set(self.inner.id);
            }
        });
    }

    pub fn add_event(&self, callback: impl Fn(T) + 'static) -> DropResource {
        self.inner.add_event(callback)
    }
}