mirror of
https://github.com/adoyle0/thaw.git
synced 2025-01-23 06:19:22 -05:00
feat: adds VecModel
This commit is contained in:
parent
a653960040
commit
f11efb4ef8
5 changed files with 343 additions and 33 deletions
|
@ -1,7 +1,7 @@
|
||||||
# Combobox
|
# Combobox
|
||||||
|
|
||||||
```rust demo
|
```rust demo
|
||||||
let selected_options = RwSignal::new(vec![]);
|
let selected_options = RwSignal::new(None::<String>);
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<Combobox selected_options>
|
<Combobox selected_options>
|
||||||
|
@ -17,7 +17,7 @@ view! {
|
||||||
let selected_options = RwSignal::new(vec![]);
|
let selected_options = RwSignal::new(vec![]);
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<Combobox selected_options multiselect=true clearable=true>
|
<Combobox selected_options clearable=true>
|
||||||
<ComboboxOption value="cat" text="Car" />
|
<ComboboxOption value="cat" text="Car" />
|
||||||
<ComboboxOption value="dog" text="Dog" />
|
<ComboboxOption value="dog" text="Dog" />
|
||||||
</Combobox>
|
</Combobox>
|
||||||
|
@ -30,7 +30,7 @@ view! {
|
||||||
let selected_options = RwSignal::new(vec![]);
|
let selected_options = RwSignal::new(vec![]);
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<Combobox selected_options multiselect=true>
|
<Combobox selected_options>
|
||||||
<ComboboxOption value="cat" text="Car" />
|
<ComboboxOption value="cat" text="Car" />
|
||||||
<ComboboxOption value="dog" text="Dog" />
|
<ComboboxOption value="dog" text="Dog" />
|
||||||
</Combobox>
|
</Combobox>
|
||||||
|
|
|
@ -3,13 +3,12 @@ use crate::_aria::use_active_descendant;
|
||||||
use leptos::{context::Provider, ev, html, prelude::*};
|
use leptos::{context::Provider, ev, html, prelude::*};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use thaw_components::{Binder, Follower, FollowerPlacement, FollowerWidth};
|
use thaw_components::{Binder, Follower, FollowerPlacement, FollowerWidth};
|
||||||
use thaw_utils::{add_event_listener, mount_style, Model};
|
use thaw_utils::{add_event_listener, mount_style, Model, VecModel};
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn Combobox(
|
pub fn Combobox(
|
||||||
#[prop(optional, into)] value: Model<String>,
|
#[prop(optional, into)] value: Model<String>,
|
||||||
#[prop(optional, into)] selected_options: Model<Vec<String>>,
|
#[prop(optional, into)] selected_options: VecModel<String>,
|
||||||
#[prop(optional)] multiselect: bool,
|
|
||||||
#[prop(optional)] clearable: bool,
|
#[prop(optional)] clearable: bool,
|
||||||
children: Children,
|
children: Children,
|
||||||
) -> impl IntoView {
|
) -> impl IntoView {
|
||||||
|
@ -23,7 +22,12 @@ pub fn Combobox(
|
||||||
let clear_icon_ref = NodeRef::<html::Span>::new();
|
let clear_icon_ref = NodeRef::<html::Span>::new();
|
||||||
let is_show_clear_icon = Memo::new(move |_| {
|
let is_show_clear_icon = Memo::new(move |_| {
|
||||||
if clearable {
|
if clearable {
|
||||||
selected_options.with(|options| !options.is_empty())
|
selected_options.with(|options| match options {
|
||||||
|
(None, None, Some(v)) => !v.is_empty(),
|
||||||
|
(None, Some(v), None) => v.is_some(),
|
||||||
|
(Some(v), None, None) => !v.is_empty(),
|
||||||
|
_ => unreachable!(),
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
@ -75,21 +79,24 @@ pub fn Combobox(
|
||||||
|
|
||||||
let on_input = move |ev| {
|
let on_input = move |ev| {
|
||||||
let input_value = event_target_value(&ev);
|
let input_value = event_target_value(&ev);
|
||||||
if !multiselect {
|
if selected_options.with_untracked(|options| match options {
|
||||||
if selected_options.with_untracked(|options| {
|
(None, None, Some(_)) => false,
|
||||||
if let Some(option) = options.first() {
|
(None, Some(v), None) => {
|
||||||
if option != &input_value {
|
if let Some(v) = v.as_ref() {
|
||||||
return true;
|
v != &input_value
|
||||||
}
|
} else {
|
||||||
|
false
|
||||||
}
|
}
|
||||||
false
|
|
||||||
}) {
|
|
||||||
selected_options.set(vec![]);
|
|
||||||
}
|
}
|
||||||
|
(Some(v), None, None) => v != &input_value,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}) {
|
||||||
|
selected_options.set(vec![]);
|
||||||
}
|
}
|
||||||
value.set(input_value);
|
value.set(input_value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let multiselect = selected_options.is_vec();
|
||||||
let combobox_injection = ComboboxInjection {
|
let combobox_injection = ComboboxInjection {
|
||||||
value,
|
value,
|
||||||
multiselect,
|
multiselect,
|
||||||
|
@ -103,13 +110,20 @@ pub fn Combobox(
|
||||||
let on_blur = {
|
let on_blur = {
|
||||||
let active_descendant_controller = active_descendant_controller.clone();
|
let active_descendant_controller = active_descendant_controller.clone();
|
||||||
move |_| {
|
move |_| {
|
||||||
if multiselect {
|
selected_options.with_untracked(|options| match options {
|
||||||
value.set(String::new());
|
(None, None, Some(_)) => value.set(String::new()),
|
||||||
} else {
|
(None, Some(v), None) => {
|
||||||
if selected_options.with_untracked(|options| options.is_empty()) {
|
if v.is_none() {
|
||||||
value.set(String::new());
|
value.set(String::new())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
(Some(v), None, None) => {
|
||||||
|
if v.is_empty() {
|
||||||
|
value.set(String::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
});
|
||||||
active_descendant_controller.blur();
|
active_descendant_controller.blur();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -208,7 +222,7 @@ pub fn Combobox(
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub(crate) struct ComboboxInjection {
|
pub(crate) struct ComboboxInjection {
|
||||||
value: Model<String>,
|
value: Model<String>,
|
||||||
selected_options: Model<Vec<String>>,
|
selected_options: VecModel<String>,
|
||||||
options: StoredValue<HashMap<String, (String, String)>>,
|
options: StoredValue<HashMap<String, (String, String)>>,
|
||||||
is_show_listbox: RwSignal<bool>,
|
is_show_listbox: RwSignal<bool>,
|
||||||
pub multiselect: bool,
|
pub multiselect: bool,
|
||||||
|
@ -229,23 +243,40 @@ impl ComboboxInjection {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_selected(&self, value: &String) -> bool {
|
pub fn is_selected(&self, value: &String) -> bool {
|
||||||
self.selected_options
|
self.selected_options.with(|options| match options {
|
||||||
.with(|options| options.contains(value))
|
(None, None, Some(v)) => v.contains(value),
|
||||||
|
(None, Some(v), None) => {
|
||||||
|
if let Some(v) = v.as_ref() {
|
||||||
|
v == value
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Some(v), None, None) => v == value,
|
||||||
|
_ => unreachable!(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select_option(&self, value: &String, text: &String) {
|
pub fn select_option(&self, value: &String, text: &String) {
|
||||||
self.selected_options.update(|options| {
|
self.selected_options.update(|options| match options {
|
||||||
if self.multiselect {
|
(None, None, Some(v)) => {
|
||||||
if let Some(index) = options.iter().position(|v| v == value) {
|
if let Some(index) = v.iter().position(|v| v == value) {
|
||||||
options.remove(index);
|
v.remove(index);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
options.push(value.clone());
|
v.push(value.clone());
|
||||||
} else {
|
}
|
||||||
*options = vec![value.clone()];
|
(None, Some(v), None) => {
|
||||||
|
*v = Some(value.clone());
|
||||||
self.value.set(text.clone());
|
self.value.set(text.clone());
|
||||||
self.is_show_listbox.set(false);
|
self.is_show_listbox.set(false);
|
||||||
}
|
}
|
||||||
|
(Some(v), None, None) => {
|
||||||
|
*v = value.clone();
|
||||||
|
self.value.set(text.clone());
|
||||||
|
self.is_show_listbox.set(false);
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ mod signal_watch;
|
||||||
mod stored_maybe_signal;
|
mod stored_maybe_signal;
|
||||||
|
|
||||||
pub use component_ref::ComponentRef;
|
pub use component_ref::ComponentRef;
|
||||||
pub use model::{Model, OptionModel};
|
pub use model::{Model, OptionModel, VecModel};
|
||||||
pub use optional_maybe_signal::OptionalMaybeSignal;
|
pub use optional_maybe_signal::OptionalMaybeSignal;
|
||||||
pub use signal_watch::SignalWatch;
|
pub use signal_watch::SignalWatch;
|
||||||
pub use stored_maybe_signal::StoredMaybeSignal;
|
pub use stored_maybe_signal::StoredMaybeSignal;
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
mod option_model;
|
mod option_model;
|
||||||
|
mod vec_model;
|
||||||
|
|
||||||
pub use option_model::OptionModel;
|
pub use option_model::OptionModel;
|
||||||
|
pub use vec_model::VecModel;
|
||||||
|
|
||||||
use leptos::reactive_graph::{
|
use leptos::reactive_graph::{
|
||||||
computed::Memo,
|
computed::Memo,
|
||||||
|
|
277
thaw_utils/src/signals/model/vec_model.rs
Normal file
277
thaw_utils/src/signals/model/vec_model.rs
Normal file
|
@ -0,0 +1,277 @@
|
||||||
|
use leptos::{
|
||||||
|
prelude::Update,
|
||||||
|
reactive_graph::{
|
||||||
|
computed::Memo,
|
||||||
|
signal::{ReadSignal, RwSignal, WriteSignal},
|
||||||
|
traits::{GetUntracked, Set, With, WithUntracked},
|
||||||
|
wrappers::read::Signal,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub enum VecModel<T>
|
||||||
|
where
|
||||||
|
T: 'static,
|
||||||
|
{
|
||||||
|
T(Signal<T>, WriteSignal<T>, Option<WriteSignal<T>>),
|
||||||
|
Option(
|
||||||
|
Signal<Option<T>>,
|
||||||
|
WriteSignal<Option<T>>,
|
||||||
|
Option<WriteSignal<Option<T>>>,
|
||||||
|
),
|
||||||
|
Vec(
|
||||||
|
Signal<Vec<T>>,
|
||||||
|
WriteSignal<Vec<T>>,
|
||||||
|
Option<WriteSignal<Vec<T>>>,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Default + Send + Sync> Default for VecModel<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new(Default::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Clone for VecModel<T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Copy for VecModel<T> {}
|
||||||
|
|
||||||
|
impl<T: Send + Sync> VecModel<T> {
|
||||||
|
fn new(value: T) -> Self {
|
||||||
|
Self::new_option(Some(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_option(value: Option<T>) -> Self {
|
||||||
|
let rw_signal = RwSignal::new(value);
|
||||||
|
rw_signal.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_vec(value: Vec<T>) -> Self {
|
||||||
|
let rw_signal = RwSignal::new(value);
|
||||||
|
rw_signal.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_vec(&self) -> bool {
|
||||||
|
if let VecModel::Vec(_, _, _) = self {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with<O>(
|
||||||
|
&self,
|
||||||
|
fun: impl FnOnce((Option<&T>, Option<&Option<T>>, Option<&Vec<T>>)) -> O,
|
||||||
|
) -> O {
|
||||||
|
match self {
|
||||||
|
Self::T(read, _, _) => read.with(|value| fun((Some(value), None, None))),
|
||||||
|
Self::Option(read, _, _) => read.with(|value| fun((None, Some(value), None))),
|
||||||
|
Self::Vec(read, _, _) => read.with(|value| fun((None, None, Some(value)))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_untracked<O>(
|
||||||
|
&self,
|
||||||
|
fun: impl FnOnce((Option<&T>, Option<&Option<T>>, Option<&Vec<T>>)) -> O,
|
||||||
|
) -> O {
|
||||||
|
match self {
|
||||||
|
Self::T(read, _, _) => read.with_untracked(|value| fun((Some(value), None, None))),
|
||||||
|
Self::Option(read, _, _) => read.with_untracked(|value| fun((None, Some(value), None))),
|
||||||
|
Self::Vec(read, _, _) => read.with_untracked(|value| fun((None, None, Some(value)))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Send + Sync + Clone + Default> VecModel<T> {
|
||||||
|
pub fn set(&self, mut value: Vec<T>) {
|
||||||
|
match self {
|
||||||
|
Self::T(read, write, on_write) => {
|
||||||
|
let value = if value.is_empty() {
|
||||||
|
Default::default()
|
||||||
|
} else {
|
||||||
|
value.remove(0)
|
||||||
|
};
|
||||||
|
|
||||||
|
write.set(value);
|
||||||
|
if let Some(on_write) = on_write.as_ref() {
|
||||||
|
on_write.set(read.get_untracked());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::Option(read, write, on_write) => {
|
||||||
|
let value = if value.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(value.remove(0))
|
||||||
|
};
|
||||||
|
write.set(value);
|
||||||
|
if let Some(on_write) = on_write.as_ref() {
|
||||||
|
on_write.set(read.get_untracked());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::Vec(read, write, on_write) => {
|
||||||
|
write.set(value);
|
||||||
|
if let Some(on_write) = on_write.as_ref() {
|
||||||
|
on_write.set(read.get_untracked());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(
|
||||||
|
&self,
|
||||||
|
fun: impl FnOnce((Option<&mut T>, Option<&mut Option<T>>, Option<&mut Vec<T>>)),
|
||||||
|
) {
|
||||||
|
match self {
|
||||||
|
Self::T(read, write, on_write) => {
|
||||||
|
write.update(move |write| {
|
||||||
|
fun((Some(write), None, None));
|
||||||
|
});
|
||||||
|
if let Some(on_write) = on_write.as_ref() {
|
||||||
|
on_write.set(read.get_untracked());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::Option(read, write, on_write) => {
|
||||||
|
write.update(move |write| {
|
||||||
|
fun((None, Some(write), None));
|
||||||
|
});
|
||||||
|
if let Some(on_write) = on_write.as_ref() {
|
||||||
|
on_write.set(read.get_untracked());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::Vec(read, write, on_write) => {
|
||||||
|
write.update(move |write| {
|
||||||
|
fun((None, None, Some(write)));
|
||||||
|
});
|
||||||
|
if let Some(on_write) = on_write.as_ref() {
|
||||||
|
on_write.set(read.get_untracked());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Send + Sync> From<T> for VecModel<T> {
|
||||||
|
fn from(value: T) -> Self {
|
||||||
|
Self::new(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Send + Sync> From<Option<T>> for VecModel<T> {
|
||||||
|
fn from(value: Option<T>) -> Self {
|
||||||
|
Self::new_option(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Send + Sync> From<Vec<T>> for VecModel<T> {
|
||||||
|
fn from(value: Vec<T>) -> Self {
|
||||||
|
Self::new_vec(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Send + Sync> From<RwSignal<T>> for VecModel<T> {
|
||||||
|
fn from(rw_signal: RwSignal<T>) -> Self {
|
||||||
|
let (read, write) = rw_signal.split();
|
||||||
|
Self::T(read.into(), write, None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Send + Sync> From<RwSignal<Option<T>>> for VecModel<T> {
|
||||||
|
fn from(rw_signal: RwSignal<Option<T>>) -> Self {
|
||||||
|
let (read, write) = rw_signal.split();
|
||||||
|
Self::Option(read.into(), write, None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Send + Sync> From<RwSignal<Vec<T>>> for VecModel<T> {
|
||||||
|
fn from(rw_signal: RwSignal<Vec<T>>) -> Self {
|
||||||
|
let (read, write) = rw_signal.split();
|
||||||
|
Self::Vec(read.into(), write, None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<(Signal<T>, WriteSignal<T>)> for VecModel<T> {
|
||||||
|
fn from((read, write): (Signal<T>, WriteSignal<T>)) -> Self {
|
||||||
|
Self::T(read, write, None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<(Signal<Option<T>>, WriteSignal<Option<T>>)> for VecModel<T> {
|
||||||
|
fn from((read, write): (Signal<Option<T>>, WriteSignal<Option<T>>)) -> Self {
|
||||||
|
Self::Option(read, write, None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<(Signal<Vec<T>>, WriteSignal<Vec<T>>)> for VecModel<T> {
|
||||||
|
fn from((read, write): (Signal<Vec<T>>, WriteSignal<Vec<T>>)) -> Self {
|
||||||
|
Self::Vec(read, write, None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Send + Sync> From<(ReadSignal<T>, WriteSignal<T>)> for VecModel<T> {
|
||||||
|
fn from((read, write): (ReadSignal<T>, WriteSignal<T>)) -> Self {
|
||||||
|
Self::T(read.into(), write, None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Send + Sync> From<(ReadSignal<Option<T>>, WriteSignal<Option<T>>)> for VecModel<T> {
|
||||||
|
fn from((read, write): (ReadSignal<Option<T>>, WriteSignal<Option<T>>)) -> Self {
|
||||||
|
Self::Option(read.into(), write, None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Send + Sync> From<(ReadSignal<Vec<T>>, WriteSignal<Vec<T>>)> for VecModel<T> {
|
||||||
|
fn from((read, write): (ReadSignal<Vec<T>>, WriteSignal<Vec<T>>)) -> Self {
|
||||||
|
Self::Vec(read.into(), write, None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Send + Sync> From<(Memo<T>, WriteSignal<T>)> for VecModel<T> {
|
||||||
|
fn from((read, write): (Memo<T>, WriteSignal<T>)) -> Self {
|
||||||
|
Self::T(read.into(), write, None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Send + Sync> From<(Memo<Option<T>>, WriteSignal<Option<T>>)> for VecModel<T> {
|
||||||
|
fn from((read, write): (Memo<Option<T>>, WriteSignal<Option<T>>)) -> Self {
|
||||||
|
Self::Option(read.into(), write, None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Send + Sync> From<(Memo<Vec<T>>, WriteSignal<Vec<T>>)> for VecModel<T> {
|
||||||
|
fn from((read, write): (Memo<Vec<T>>, WriteSignal<Vec<T>>)) -> Self {
|
||||||
|
Self::Vec(read.into(), write, None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Default + Send + Sync> From<(Option<T>, WriteSignal<T>)> for VecModel<T> {
|
||||||
|
fn from((read, write): (Option<T>, WriteSignal<T>)) -> Self {
|
||||||
|
let mut model = Self::new(read.unwrap_or_default());
|
||||||
|
if let VecModel::T(_, _, on_write) = &mut model {
|
||||||
|
*on_write = Some(write);
|
||||||
|
}
|
||||||
|
model
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Default + Send + Sync> From<(Option<Option<T>>, WriteSignal<Option<T>>)> for VecModel<T> {
|
||||||
|
fn from((read, write): (Option<Option<T>>, WriteSignal<Option<T>>)) -> Self {
|
||||||
|
let mut model = Self::new_option(read.unwrap_or_default());
|
||||||
|
if let VecModel::Option(_, _, on_write) = &mut model {
|
||||||
|
*on_write = Some(write);
|
||||||
|
}
|
||||||
|
model
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Default + Send + Sync> From<(Option<Vec<T>>, WriteSignal<Vec<T>>)> for VecModel<T> {
|
||||||
|
fn from((read, write): (Option<Vec<T>>, WriteSignal<Vec<T>>)) -> Self {
|
||||||
|
let mut model = Self::new_vec(read.unwrap_or_default());
|
||||||
|
if let VecModel::Vec(_, _, on_write) = &mut model {
|
||||||
|
*on_write = Some(write);
|
||||||
|
}
|
||||||
|
model
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue