Feat/model (#92)

* feat: add Model

* feat: RwSignal is migrated to Model
This commit is contained in:
luoxiaozero 2024-01-29 10:57:55 +08:00
parent 1cba68520a
commit b2f68906df
22 changed files with 248 additions and 46 deletions

View file

@ -3,7 +3,7 @@ mod theme;
use crate::{ use crate::{
components::{Binder, Follower, FollowerPlacement, FollowerWidth}, components::{Binder, Follower, FollowerPlacement, FollowerWidth},
use_theme, use_theme,
utils::{class_list::class_list, mount_style, StoredMaybeSignal}, utils::{class_list::class_list, mount_style, Model, StoredMaybeSignal},
ComponentRef, Input, InputPrefix, InputRef, InputSuffix, Theme, ComponentRef, Input, InputPrefix, InputRef, InputSuffix, Theme,
}; };
use leptos::*; use leptos::*;
@ -27,7 +27,7 @@ pub struct AutoCompleteSuffix {
#[component] #[component]
pub fn AutoComplete( pub fn AutoComplete(
#[prop(optional, into)] value: RwSignal<String>, #[prop(optional, into)] value: Model<String>,
#[prop(optional, into)] placeholder: MaybeSignal<String>, #[prop(optional, into)] placeholder: MaybeSignal<String>,
#[prop(optional, into)] options: MaybeSignal<Vec<AutoCompleteOption>>, #[prop(optional, into)] options: MaybeSignal<Vec<AutoCompleteOption>>,
#[prop(optional, into)] clear_after_select: MaybeSignal<bool>, #[prop(optional, into)] clear_after_select: MaybeSignal<bool>,

View file

@ -3,7 +3,7 @@ mod theme;
use crate::{ use crate::{
chrono::{Datelike, Days, Local, NaiveDate}, chrono::{Datelike, Days, Local, NaiveDate},
use_theme, use_theme,
utils::{class_list::class_list, mount_style}, utils::{class_list::class_list, mount_style, Model},
Button, ButtonGroup, ButtonVariant, Theme, Button, ButtonGroup, ButtonVariant, Theme,
}; };
use chrono::{Month, Months}; use chrono::{Month, Months};
@ -14,7 +14,7 @@ pub use theme::CalendarTheme;
#[component] #[component]
pub fn Calendar( pub fn Calendar(
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: MaybeSignal<String>,
#[prop(optional, into)] value: RwSignal<Option<NaiveDate>>, #[prop(optional, into)] value: Model<Option<NaiveDate>>,
) -> impl IntoView { ) -> impl IntoView {
mount_style("calendar", include_str!("./calendar.css")); mount_style("calendar", include_str!("./calendar.css"));
let theme = use_theme(Theme::light); let theme = use_theme(Theme::light);
@ -170,7 +170,7 @@ pub fn Calendar(
#[component] #[component]
fn CalendarItem( fn CalendarItem(
value: RwSignal<Option<NaiveDate>>, value: Model<Option<NaiveDate>>,
index: usize, index: usize,
date: CalendarItemDate, date: CalendarItemDate,
) -> impl IntoView { ) -> impl IntoView {

View file

@ -1,16 +1,17 @@
use crate::utils::Model;
use leptos::*; use leptos::*;
use std::collections::HashSet; use std::collections::HashSet;
#[component] #[component]
pub fn CheckboxGroup( pub fn CheckboxGroup(
#[prop(optional, into)] value: RwSignal<HashSet<String>>, #[prop(optional, into)] value: Model<HashSet<String>>,
children: Children, children: Children,
) -> impl IntoView { ) -> impl IntoView {
view! { <Provider value=CheckboxGroupInjection(value) children/> } view! { <Provider value=CheckboxGroupInjection(value) children/> }
} }
#[derive(Clone)] #[derive(Clone)]
pub(crate) struct CheckboxGroupInjection(pub RwSignal<HashSet<String>>); pub(crate) struct CheckboxGroupInjection(pub Model<HashSet<String>>);
pub(crate) fn use_checkbox_group() -> CheckboxGroupInjection { pub(crate) fn use_checkbox_group() -> CheckboxGroupInjection {
expect_context() expect_context()

View file

@ -5,7 +5,7 @@ use crate::{
components::*, components::*,
icon::*, icon::*,
theme::use_theme, theme::use_theme,
utils::{class_list::class_list, mount_style}, utils::{class_list::class_list, mount_style, Model},
Theme, Theme,
}; };
pub use checkbox_group::CheckboxGroup; pub use checkbox_group::CheckboxGroup;
@ -14,7 +14,7 @@ use leptos::*;
#[component] #[component]
pub fn Checkbox( pub fn Checkbox(
#[prop(optional, into)] value: RwSignal<bool>, #[prop(optional, into)] value: Model<bool>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: MaybeSignal<String>,
children: Children, children: Children,
) -> impl IntoView { ) -> impl IntoView {
@ -44,7 +44,7 @@ pub fn Checkbox(
> >
<input class="thaw-checkbox__input" type="checkbox"/> <input class="thaw-checkbox__input" type="checkbox"/>
<div class="thaw-checkbox__dot"> <div class="thaw-checkbox__dot">
<If cond=value> <If cond=value.signal()>
<Then slot> <Then slot>
<Icon icon=icondata::AiCheckOutlined style="color: white"/> <Icon icon=icondata::AiCheckOutlined style="color: white"/>
</Then> </Then>

View file

@ -6,7 +6,7 @@ pub use theme::CollapseTheme;
use crate::{ use crate::{
use_theme, use_theme,
utils::{class_list::class_list, mount_style}, utils::{class_list::class_list, mount_style, Model},
Theme, Theme,
}; };
use leptos::*; use leptos::*;
@ -15,7 +15,7 @@ use std::collections::HashSet;
#[component] #[component]
pub fn Collapse( pub fn Collapse(
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: MaybeSignal<String>,
#[prop(optional, into)] value: RwSignal<HashSet<String>>, #[prop(optional, into)] value: Model<HashSet<String>>,
#[prop(optional)] accordion: bool, #[prop(optional)] accordion: bool,
children: Children, children: Children,
) -> impl IntoView { ) -> impl IntoView {
@ -42,7 +42,7 @@ pub fn Collapse(
#[derive(Clone)] #[derive(Clone)]
pub(crate) struct CollapseInjection { pub(crate) struct CollapseInjection {
pub value: RwSignal<HashSet<String>>, pub value: Model<HashSet<String>>,
pub accordion: bool, pub accordion: bool,
} }

View file

@ -4,7 +4,7 @@ mod theme;
use crate::{ use crate::{
components::{Binder, Follower, FollowerPlacement}, components::{Binder, Follower, FollowerPlacement},
use_theme, use_theme,
utils::{class_list::class_list, mount_style}, utils::{class_list::class_list, mount_style, Model},
Theme, Theme,
}; };
pub use color::*; pub use color::*;
@ -14,7 +14,7 @@ pub use theme::ColorPickerTheme;
#[component] #[component]
pub fn ColorPicker( pub fn ColorPicker(
#[prop(optional, into)] value: RwSignal<RGBA>, #[prop(optional, into)] value: Model<RGBA>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: MaybeSignal<String>,
) -> impl IntoView { ) -> impl IntoView {
mount_style("color-picker", include_str!("./color-picker.css")); mount_style("color-picker", include_str!("./color-picker.css"));

View file

@ -4,7 +4,7 @@ mod theme;
use crate::{ use crate::{
chrono::NaiveDate, chrono::NaiveDate,
components::{Binder, Follower, FollowerPlacement}, components::{Binder, Follower, FollowerPlacement},
utils::{mount_style, now_date, ComponentRef}, utils::{mount_style, now_date, ComponentRef, Model},
Icon, Input, InputSuffix, SignalWatch, Icon, Input, InputSuffix, SignalWatch,
}; };
use leptos::*; use leptos::*;
@ -13,7 +13,7 @@ pub use theme::DatePickerTheme;
#[component] #[component]
pub fn DatePicker( pub fn DatePicker(
#[prop(optional, into)] value: RwSignal<Option<NaiveDate>>, #[prop(optional, into)] value: Model<Option<NaiveDate>>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: MaybeSignal<String>,
#[prop(attrs)] attrs: Vec<(&'static str, Attribute)>, #[prop(attrs)] attrs: Vec<(&'static str, Attribute)>,
) -> impl IntoView { ) -> impl IntoView {

View file

@ -1,13 +1,13 @@
use crate::{ use crate::{
components::Teleport, components::Teleport,
utils::{class_list::class_list, mount_style}, utils::{class_list::class_list, mount_style, Model},
Card, Card,
}; };
use leptos::*; use leptos::*;
#[component] #[component]
pub fn Drawer( pub fn Drawer(
#[prop(into)] show: RwSignal<bool>, #[prop(into)] show: Model<bool>,
#[prop(optional, into)] title: MaybeSignal<String>, #[prop(optional, into)] title: MaybeSignal<String>,
#[prop(optional, into)] placement: MaybeSignal<DrawerPlacement>, #[prop(optional, into)] placement: MaybeSignal<DrawerPlacement>,
#[prop(default = MaybeSignal::Static("520px".to_string()), into)] width: MaybeSignal<String>, #[prop(default = MaybeSignal::Static("520px".to_string()), into)] width: MaybeSignal<String>,

View file

@ -6,7 +6,7 @@ pub use theme::InputTheme;
use crate::{ use crate::{
theme::{use_theme, Theme}, theme::{use_theme, Theme},
utils::{class_list::class_list, mount_style, ComponentRef}, utils::{class_list::class_list, mount_style, ComponentRef, Model},
}; };
use leptos::*; use leptos::*;
@ -42,7 +42,7 @@ pub struct InputSuffix {
#[component] #[component]
pub fn Input( pub fn Input(
#[prop(optional, into)] value: RwSignal<String>, #[prop(optional, into)] value: Model<String>,
#[prop(optional, into)] allow_value: Option<Callback<String, bool>>, #[prop(optional, into)] allow_value: Option<Callback<String, bool>>,
#[prop(optional, into)] variant: MaybeSignal<InputVariant>, #[prop(optional, into)] variant: MaybeSignal<InputVariant>,
#[prop(optional, into)] placeholder: MaybeSignal<String>, #[prop(optional, into)] placeholder: MaybeSignal<String>,

View file

@ -1,12 +1,12 @@
use crate::{ use crate::{
theme::{use_theme, Theme}, theme::{use_theme, Theme},
utils::{class_list::class_list, mount_style, ComponentRef}, utils::{class_list::class_list, mount_style, ComponentRef, Model},
}; };
use leptos::*; use leptos::*;
#[component] #[component]
pub fn TextArea( pub fn TextArea(
#[prop(optional, into)] value: RwSignal<String>, #[prop(optional, into)] value: Model<String>,
#[prop(optional, into)] allow_value: Option<Callback<String, bool>>, #[prop(optional, into)] allow_value: Option<Callback<String, bool>>,
#[prop(optional, into)] placeholder: MaybeSignal<String>, #[prop(optional, into)] placeholder: MaybeSignal<String>,
#[prop(optional, into)] on_focus: Option<Callback<ev::FocusEvent>>, #[prop(optional, into)] on_focus: Option<Callback<ev::FocusEvent>>,

View file

@ -1,4 +1,4 @@
use crate::utils::StoredMaybeSignal; use crate::utils::{Model, StoredMaybeSignal};
use crate::{Button, ButtonVariant, ComponentRef, Icon, Input, InputRef, InputSuffix}; use crate::{Button, ButtonVariant, ComponentRef, Icon, Input, InputRef, InputSuffix};
use leptos::*; use leptos::*;
use std::ops::{Add, Sub}; use std::ops::{Add, Sub};
@ -6,7 +6,7 @@ use std::str::FromStr;
#[component] #[component]
pub fn InputNumber<T>( pub fn InputNumber<T>(
#[prop(optional, into)] value: RwSignal<T>, #[prop(optional, into)] value: Model<T>,
#[prop(optional, into)] placeholder: MaybeSignal<String>, #[prop(optional, into)] placeholder: MaybeSignal<String>,
#[prop(into)] step: MaybeSignal<T>, #[prop(into)] step: MaybeSignal<T>,
#[prop(optional, into)] disabled: MaybeSignal<bool>, #[prop(optional, into)] disabled: MaybeSignal<bool>,

View file

@ -2,7 +2,7 @@ mod menu_group;
mod menu_item; mod menu_item;
mod theme; mod theme;
use crate::utils::class_list::class_list; use crate::utils::{class_list::class_list, Model};
use leptos::*; use leptos::*;
pub use menu_group::MenuGroup; pub use menu_group::MenuGroup;
pub use menu_item::*; pub use menu_item::*;
@ -10,7 +10,7 @@ pub use theme::MenuTheme;
#[component] #[component]
pub fn Menu( pub fn Menu(
#[prop(optional, into)] value: RwSignal<String>, #[prop(optional, into)] value: Model<String>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: MaybeSignal<String>,
children: Children, children: Children,
) -> impl IntoView { ) -> impl IntoView {
@ -22,7 +22,7 @@ pub fn Menu(
} }
#[derive(Clone)] #[derive(Clone)]
pub(crate) struct MenuInjection(pub RwSignal<String>); pub(crate) struct MenuInjection(pub Model<String>);
pub(crate) fn use_menu() -> MenuInjection { pub(crate) fn use_menu() -> MenuInjection {
expect_context() expect_context()

View file

@ -1,14 +1,14 @@
mod tabbar_item; mod tabbar_item;
mod theme; mod theme;
use crate::{use_theme, utils::mount_style, Theme}; use crate::{use_theme, utils::{mount_style, Model}, Theme};
use leptos::*; use leptos::*;
pub use tabbar_item::*; pub use tabbar_item::*;
pub use theme::TabbarTheme; pub use theme::TabbarTheme;
#[component] #[component]
pub fn Tabbar( pub fn Tabbar(
#[prop(optional, into)] value: RwSignal<String>, #[prop(optional, into)] value: Model<String>,
children: Children, children: Children,
) -> impl IntoView { ) -> impl IntoView {
mount_style("tabbar", include_str!("./tabbar.css")); mount_style("tabbar", include_str!("./tabbar.css"));
@ -32,7 +32,7 @@ pub fn Tabbar(
} }
#[derive(Clone)] #[derive(Clone)]
pub(crate) struct TabbarInjection(pub RwSignal<String>); pub(crate) struct TabbarInjection(pub Model<String>);
pub(crate) fn use_tabbar() -> TabbarInjection { pub(crate) fn use_tabbar() -> TabbarInjection {
expect_context() expect_context()

View file

@ -1,4 +1,5 @@
use crate::icon::*; use crate::icon::*;
use crate::utils::Model;
use crate::{ use crate::{
components::{OptionComp, Teleport}, components::{OptionComp, Teleport},
utils::{mount_style, StoredMaybeSignal}, utils::{mount_style, StoredMaybeSignal},
@ -13,7 +14,7 @@ pub struct ModalFooter {
#[component] #[component]
pub fn Modal( pub fn Modal(
#[prop(into)] show: RwSignal<bool>, #[prop(into)] show: Model<bool>,
#[prop(optional, into)] title: MaybeSignal<String>, #[prop(optional, into)] title: MaybeSignal<String>,
children: Children, children: Children,
#[prop(optional)] modal_footer: Option<ModalFooter>, #[prop(optional)] modal_footer: Option<ModalFooter>,

View file

@ -1,13 +1,13 @@
use crate::{ use crate::{
theme::use_theme, theme::use_theme,
utils::{class_list::class_list, mount_style}, utils::{class_list::class_list, mount_style, Model},
Theme, Theme,
}; };
use leptos::*; use leptos::*;
#[component] #[component]
pub fn Radio( pub fn Radio(
#[prop(optional, into)] value: RwSignal<bool>, #[prop(optional, into)] value: Model<bool>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: MaybeSignal<String>,
children: Children, children: Children,
) -> impl IntoView { ) -> impl IntoView {

View file

@ -3,7 +3,7 @@ mod theme;
use crate::{ use crate::{
components::{Binder, Follower, FollowerPlacement, FollowerWidth}, components::{Binder, Follower, FollowerPlacement, FollowerWidth},
theme::use_theme, theme::use_theme,
utils::{class_list::class_list, mount_style}, utils::{class_list::class_list, mount_style, Model},
Theme, Theme,
}; };
use leptos::*; use leptos::*;
@ -18,7 +18,7 @@ pub struct SelectOption<T> {
#[component] #[component]
pub fn Select<T>( pub fn Select<T>(
#[prop(optional, into)] value: RwSignal<Option<T>>, #[prop(optional, into)] value: Model<Option<T>>,
#[prop(optional, into)] options: MaybeSignal<Vec<SelectOption<T>>>, #[prop(optional, into)] options: MaybeSignal<Vec<SelectOption<T>>>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: MaybeSignal<String>,
) -> impl IntoView ) -> impl IntoView

View file

@ -4,7 +4,7 @@ mod theme;
use crate::{ use crate::{
components::OptionComp, components::OptionComp,
theme::use_theme, theme::use_theme,
utils::{class_list::class_list, mount_style}, utils::{class_list::class_list, mount_style, Model},
Theme, Theme,
}; };
use leptos::*; use leptos::*;
@ -15,7 +15,7 @@ pub use theme::SliderTheme;
#[component] #[component]
pub fn Slider( pub fn Slider(
#[prop(optional, into)] value: RwSignal<f64>, #[prop(optional, into)] value: Model<f64>,
#[prop(default = MaybeSignal::Static(100f64), into)] max: MaybeSignal<f64>, #[prop(default = MaybeSignal::Static(100f64), into)] max: MaybeSignal<f64>,
#[prop(optional, into)] step: MaybeSignal<f64>, #[prop(optional, into)] step: MaybeSignal<f64>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: MaybeSignal<String>,

View file

@ -2,7 +2,7 @@ mod theme;
use crate::{ use crate::{
theme::use_theme, theme::use_theme,
utils::{class_list::class_list, mount_style}, utils::{class_list::class_list, mount_style, Model},
Theme, Theme,
}; };
use leptos::*; use leptos::*;
@ -10,7 +10,7 @@ pub use theme::SwitchTheme;
#[component] #[component]
pub fn Switch( pub fn Switch(
#[prop(optional, into)] value: RwSignal<bool>, #[prop(optional, into)] value: Model<bool>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: MaybeSignal<String>,
) -> impl IntoView { ) -> impl IntoView {
mount_style("switch", include_str!("./switch.css")); mount_style("switch", include_str!("./switch.css"));

View file

@ -2,7 +2,7 @@ mod tab;
use crate::{ use crate::{
theme::use_theme, theme::use_theme,
utils::{class_list::class_list, mount_style}, utils::{class_list::class_list, mount_style, Model},
Theme, Theme,
}; };
use leptos::*; use leptos::*;
@ -11,7 +11,7 @@ pub use tab::*;
#[component] #[component]
pub fn Tabs( pub fn Tabs(
#[prop(optional, into)] value: RwSignal<String>, #[prop(optional, into)] value: Model<String>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: MaybeSignal<String>,
children: Children, children: Children,
) -> impl IntoView { ) -> impl IntoView {
@ -30,7 +30,7 @@ pub fn Tabs(
#[component] #[component]
fn TabsInner( fn TabsInner(
value: RwSignal<String>, value: Model<String>,
tab_options_vec: RwSignal<Vec<TabOption>>, tab_options_vec: RwSignal<Vec<TabOption>>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: MaybeSignal<String>,
children: Children, children: Children,
@ -161,7 +161,7 @@ pub(crate) struct TabsLabelLine {
#[derive(Clone)] #[derive(Clone)]
pub(crate) struct TabsInjection { pub(crate) struct TabsInjection {
active_key: RwSignal<String>, active_key: Model<String>,
tab_options_vec: RwSignal<Vec<TabOption>>, tab_options_vec: RwSignal<Vec<TabOption>>,
} }

View file

@ -4,7 +4,7 @@ use crate::{
chrono::{Local, NaiveTime, Timelike}, chrono::{Local, NaiveTime, Timelike},
components::{Binder, Follower, FollowerPlacement}, components::{Binder, Follower, FollowerPlacement},
use_theme, use_theme,
utils::{mount_style, ComponentRef}, utils::{mount_style, ComponentRef, Model},
Button, ButtonSize, ButtonVariant, Icon, Input, InputSuffix, SignalWatch, Theme, Button, ButtonSize, ButtonVariant, Icon, Input, InputSuffix, SignalWatch, Theme,
}; };
use leptos::*; use leptos::*;
@ -12,7 +12,7 @@ pub use theme::TimePickerTheme;
#[component] #[component]
pub fn TimePicker( pub fn TimePicker(
#[prop(optional, into)] value: RwSignal<Option<NaiveTime>>, #[prop(optional, into)] value: Model<Option<NaiveTime>>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: MaybeSignal<String>,
#[prop(attrs)] attrs: Vec<(&'static str, Attribute)>, #[prop(attrs)] attrs: Vec<(&'static str, Attribute)>,
) -> impl IntoView { ) -> impl IntoView {

View file

@ -2,6 +2,7 @@
pub(crate) mod class_list; pub(crate) mod class_list;
mod component_ref; mod component_ref;
mod event_listener; mod event_listener;
mod model;
mod mount_style; mod mount_style;
mod signal; mod signal;
mod stored_maybe_signal; mod stored_maybe_signal;
@ -10,6 +11,7 @@ mod time;
// pub use callback::AsyncCallback; // pub use callback::AsyncCallback;
pub use component_ref::{create_component_ref, ComponentRef}; pub use component_ref::{create_component_ref, ComponentRef};
pub(crate) use event_listener::*; pub(crate) use event_listener::*;
pub(crate) use model::Model;
pub(crate) use mount_style::mount_style; pub(crate) use mount_style::mount_style;
pub use signal::SignalWatch; pub use signal::SignalWatch;
pub(crate) use stored_maybe_signal::*; pub(crate) use stored_maybe_signal::*;

198
thaw/src/utils/model.rs Normal file
View file

@ -0,0 +1,198 @@
use leptos::{
Memo, ReadSignal, RwSignal, Signal, SignalGet, SignalGetUntracked, SignalSet, SignalUpdate,
SignalWith, SignalWithUntracked, WriteSignal,
};
pub struct Model<T>
where
T: 'static,
{
read: Signal<T>,
write: WriteSignal<T>,
on_write: Option<WriteSignal<T>>,
}
impl<T: Default> Default for Model<T> {
fn default() -> Self {
RwSignal::new(Default::default()).into()
}
}
impl<T> Clone for Model<T> {
fn clone(&self) -> Self {
Self {
read: self.read.clone(),
write: self.write.clone(),
on_write: self.on_write.clone(),
}
}
}
impl<T> Copy for Model<T> {}
impl<T> Model<T> {
fn new(value: T) -> Self {
let rw_signal = RwSignal::new(value);
rw_signal.into()
}
pub fn signal(&self) -> Signal<T> {
self.read.clone()
}
}
impl<T: Clone> SignalGet for Model<T> {
type Value = T;
fn get(&self) -> Self::Value {
self.read.get()
}
fn try_get(&self) -> Option<Self::Value> {
self.read.try_get()
}
}
impl<T: Clone> SignalGetUntracked for Model<T> {
type Value = T;
fn get_untracked(&self) -> Self::Value {
self.read.get_untracked()
}
fn try_get_untracked(&self) -> Option<Self::Value> {
self.read.try_get_untracked()
}
}
impl<T: Clone> SignalSet for Model<T> {
type Value = T;
fn set(&self, new_value: Self::Value) {
if let Some(on_write) = self.on_write.as_ref() {
on_write.set(new_value.clone());
}
self.write.set(new_value);
}
fn try_set(&self, new_value: Self::Value) -> Option<Self::Value> {
if let Some(on_write) = self.on_write.as_ref() {
on_write.try_set(new_value.clone());
}
self.write.try_set(new_value)
}
}
impl<T> SignalWith for Model<T> {
type Value = T;
fn with<O>(&self, f: impl FnOnce(&Self::Value) -> O) -> O {
self.read.with(f)
}
fn try_with<O>(&self, f: impl FnOnce(&Self::Value) -> O) -> Option<O> {
self.read.try_with(f)
}
}
impl<T> SignalWithUntracked for Model<T> {
type Value = T;
fn with_untracked<O>(&self, f: impl FnOnce(&Self::Value) -> O) -> O {
self.read.with_untracked(f)
}
fn try_with_untracked<O>(&self, f: impl FnOnce(&Self::Value) -> O) -> Option<O> {
self.read.try_with_untracked(f)
}
}
impl<T> SignalUpdate for Model<T> {
type Value = T;
fn update(&self, f: impl FnOnce(&mut Self::Value)) {
self.write.update(f);
}
fn try_update<O>(&self, f: impl FnOnce(&mut Self::Value) -> O) -> Option<O> {
self.write.try_update(f)
}
}
impl<T> From<T> for Model<T> {
fn from(value: T) -> Self {
Self::new(value)
}
}
impl<T> From<RwSignal<T>> for Model<T> {
fn from(rw_signal: RwSignal<T>) -> Self {
let (read, write) = rw_signal.split();
Self {
read: read.into(),
write,
on_write: None,
}
}
}
impl<T> From<(ReadSignal<T>, WriteSignal<T>)> for Model<T> {
fn from((read, write): (ReadSignal<T>, WriteSignal<T>)) -> Self {
Self {
read: read.into(),
write,
on_write: None,
}
}
}
impl<T> From<(Memo<T>, WriteSignal<T>)> for Model<T> {
fn from((read, write): (Memo<T>, WriteSignal<T>)) -> Self {
Self {
read: read.into(),
write,
on_write: None,
}
}
}
impl<T: Default> From<(Option<T>, WriteSignal<T>)> for Model<T> {
fn from((read, write): (Option<T>, WriteSignal<T>)) -> Self {
let mut modal = Self::new(read.unwrap_or_default());
modal.on_write = Some(write.into());
modal
}
}
#[cfg(test)]
mod test {
use super::Model;
use leptos::*;
#[test]
fn from() {
let runtime = create_runtime();
// T
let modal: Model<i32> = 0.into();
assert_eq!(modal.get_untracked(), 0);
modal.set(1);
assert_eq!(modal.get_untracked(), 1);
// RwSignal
let rw_signal = RwSignal::new(0);
let modal: Model<i32> = rw_signal.into();
assert_eq!(modal.get_untracked(), 0);
modal.set(1);
assert_eq!(modal.get_untracked(), 1);
// Read Write
let (read, write) = create_signal(0);
let modal: Model<i32> = (read, write).into();
assert_eq!(modal.get_untracked(), 0);
modal.set(1);
assert_eq!(modal.get_untracked(), 1);
runtime.dispose();
}
}