Add signal helpers from main that are needed for <Transition/>

This commit is contained in:
Greg Johnston 2022-12-09 22:52:14 -05:00
parent 46e77d72ea
commit 7959f5b324
3 changed files with 196 additions and 0 deletions

View file

@ -77,6 +77,7 @@ mod selector;
mod serialization;
mod signal;
mod signal_wrappers;
mod signal_wrappers_write;
mod spawn;
mod suspense;
@ -91,6 +92,7 @@ pub use selector::*;
pub use serialization::*;
pub use signal::*;
pub use signal_wrappers::*;
pub use signal_wrappers_write::*;
pub use spawn::*;
pub use suspense::*;

View file

@ -616,6 +616,28 @@ where
}
}
/// Returns a write-only handle to the signal.
///
/// Useful if you're trying to give write access to another component, or split an
/// `RwSignal` into a [ReadSignal] and a [WriteSignal].
/// ```
/// # use leptos_reactive::*;
/// # create_scope(create_runtime(), |cx| {
/// let count = create_rw_signal(cx, 0);
/// let set_count = count.write_only();
/// assert_eq!(count(), 0);
/// set_count(1);
/// assert_eq!(count(), 1);
/// # }).dispose();
/// ```
pub fn write_only(&self) -> WriteSignal<T> {
WriteSignal {
runtime: self.runtime,
id: self.id,
ty: PhantomData,
}
}
/// Generates a [Stream] that emits the new value of the signal whenever it changes.
pub fn to_stream(&self) -> impl Stream<Item = T>
where

View file

@ -0,0 +1,172 @@
use std::rc::Rc;
use crate::{RwSignal, Scope, WriteSignal};
/// A wrapper for any kind of settable reactive signal: a [WriteSignal](crate::WriteSignal),
/// [RwSignal](crate::RwSignal), or closure that receives a value and sets a signal depending
/// on it.
///
/// This allows you to create APIs that take any kind of `SignalSetter<T>` as an argument,
/// rather than adding a generic `F: Fn(T)`. Values can be set with the same
/// function call or `set()`, API as other signals.
///
/// ```rust
/// # use leptos_reactive::*;
/// # create_scope(create_runtime(), |cx| {
/// let (count, set_count) = create_signal(cx, 2);
/// let set_double_input = SignalSetter::map(cx, move |n| set_count(n * 2));
///
/// // this function takes any kind of signal setter
/// fn set_to_4(setter: &SignalSetter<i32>) {
/// // ✅ calling the signal sets the value
/// // it is a shorthand for arg.set()
/// setter(4);
/// }
///
/// set_to_4(&set_count.into());
/// assert_eq!(count(), 4);
/// set_to_4(&set_double_input);
/// assert_eq!(count(), 8);
/// # });
/// ```
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SignalSetter<T>(SignalSetterTypes<T>)
where
T: 'static;
impl<T> SignalSetter<T>
where
T: 'static,
{
/// Wraps a signal-setting closure, i.e., any computation that sets one or more
/// reactive signals.
/// ```rust
/// # use leptos_reactive::*;
/// # create_scope(create_runtime(), |cx| {
/// let (count, set_count) = create_signal(cx, 2);
/// let set_double_count = SignalSetter::map(cx, move |n| set_count(n * 2));
///
/// // this function takes any kind of signal setter
/// fn set_to_4(setter: &SignalSetter<i32>) {
/// // ✅ calling the signal sets the value
/// // it is a shorthand for arg.set()
/// setter(4)
/// }
///
/// set_to_4(&set_count.into());
/// assert_eq!(count(), 4);
/// set_to_4(&set_double_count);
/// assert_eq!(count(), 8);
/// # });
/// ```
pub fn map(cx: Scope, mapped_setter: impl Fn(T) + 'static) -> Self {
Self(SignalSetterTypes::Mapped(cx, Rc::new(mapped_setter)))
}
/// Calls the setter function with the given value.
///
/// ```rust
/// # use leptos_reactive::*;
/// # create_scope(create_runtime(), |cx| {
/// let (count, set_count) = create_signal(cx, 2);
/// let set_double_count = SignalSetter::map(cx, move |n| set_count(n * 2));
///
/// // this function takes any kind of signal setter
/// fn set_to_4(setter: &SignalSetter<i32>) {
/// // ✅ calling the signal sets the value
/// // it is a shorthand for arg.set()
/// setter(4);
/// }
///
/// set_to_4(&set_count.into());
/// assert_eq!(count(), 4);
/// set_to_4(&set_double_count);
/// assert_eq!(count(), 8);
/// # });
pub fn set(&self, value: T) {
match &self.0 {
SignalSetterTypes::Write(s) => s.set(value),
SignalSetterTypes::Mapped(_, s) => s(value),
}
}
}
impl<T> From<WriteSignal<T>> for SignalSetter<T> {
fn from(value: WriteSignal<T>) -> Self {
Self(SignalSetterTypes::Write(value))
}
}
impl<T> From<RwSignal<T>> for SignalSetter<T> {
fn from(value: RwSignal<T>) -> Self {
Self(SignalSetterTypes::Write(value.write_only()))
}
}
#[derive(Clone)]
enum SignalSetterTypes<T>
where
T: 'static,
{
Write(WriteSignal<T>),
Mapped(Scope, Rc<dyn Fn(T)>),
}
impl<T> std::fmt::Debug for SignalSetterTypes<T>
where
T: std::fmt::Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Write(arg0) => f.debug_tuple("WriteSignal").field(arg0).finish(),
Self::Mapped(_, _) => f.debug_tuple("Mapped").finish(),
}
}
}
impl<T> PartialEq for SignalSetterTypes<T>
where
T: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Write(l0), Self::Write(r0)) => l0 == r0,
(Self::Mapped(_, l0), Self::Mapped(_, r0)) => std::ptr::eq(l0, r0),
_ => false,
}
}
}
impl<T> Eq for SignalSetterTypes<T> where T: PartialEq {}
#[cfg(not(feature = "stable"))]
impl<T> FnOnce<(T,)> for SignalSetter<T>
where
T: 'static,
{
type Output = ();
extern "rust-call" fn call_once(self, args: (T,)) -> Self::Output {
self.set(args.0)
}
}
#[cfg(not(feature = "stable"))]
impl<T> FnMut<(T,)> for SignalSetter<T>
where
T: 'static,
{
extern "rust-call" fn call_mut(&mut self, args: (T,)) -> Self::Output {
self.set(args.0)
}
}
#[cfg(not(feature = "stable"))]
impl<T> Fn<(T,)> for SignalSetter<T>
where
T: 'static,
{
extern "rust-call" fn call(&self, args: (T,)) -> Self::Output {
self.set(args.0)
}
}