Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions examples/01-app-demos/calculator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@ fn app() -> Element {
if val() == "0" {
val.set(String::new());
}
val.write().push_str(num.as_str());
val.push_str(num.as_str());
};

let mut input_operator = move |key: &str| val.write().push_str(key);
let mut input_operator = move |key: &str| val.push_str(key);

let handle_key_down_event = move |evt: KeyboardEvent| match evt.key() {
Key::Backspace => {
if !val().is_empty() {
val.write().pop();
val.pop();
}
}
Key::Character(character) => match character.as_str() {
Expand Down Expand Up @@ -104,7 +104,7 @@ fn app() -> Element {
}
button {
class: "calculator-key key-dot",
onclick: move |_| val.write().push('.'),
onclick: move |_| val.push('.'),
"●"
}
for k in 1..10 {
Expand Down
6 changes: 3 additions & 3 deletions examples/01-app-demos/counters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ fn app() -> Element {
Stylesheet { href: STYLE }

div { id: "controls",
button { onclick: move |_| counters.write().push(0), "Add counter" }
button { onclick: move |_| { counters.write().pop(); }, "Remove counter" }
button { onclick: move |_| counters.push(0), "Add counter" }
button { onclick: move |_| { counters.pop(); }, "Remove counter" }
}

h3 { "Total: {sum}" }
Expand All @@ -47,7 +47,7 @@ fn app() -> Element {
}
}
button { onclick: move |_| counters.write()[i] += 1, "+1" }
button { onclick: move |_| { counters.write().remove(i); }, "x" }
button { onclick: move |_| { counters.remove(i); }, "x" }
}
}
}
Expand Down
12 changes: 6 additions & 6 deletions examples/01-app-demos/todomvc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,13 +115,13 @@ fn TodoHeader(mut todos: WriteSignal<HashMap<u32, TodoItem>>) -> Element {
let mut todo_id = use_signal(|| 0);

let onkeydown = move |evt: KeyboardEvent| {
if evt.key() == Key::Enter && !draft.read().is_empty() {
if evt.key() == Key::Enter && !draft.is_empty() {
let id = todo_id();
let todo = TodoItem {
checked: false,
contents: draft.to_string(),
};
todos.write().insert(id, todo);
todos.insert(id, todo);
todo_id += 1;
draft.set("".to_string());
}
Expand Down Expand Up @@ -169,7 +169,7 @@ fn TodoEntry(mut todos: WriteSignal<HashMap<u32, TodoItem>>, id: u32) -> Element
r#type: "checkbox",
id: "cbg-{id}",
checked: "{checked}",
oninput: move |evt| todos.write().get_mut(&id).unwrap().checked = evt.checked()
oninput: move |evt| todos.get_mut(&id).unwrap().checked = evt.checked()
}
label {
r#for: "cbg-{id}",
Expand All @@ -181,7 +181,7 @@ fn TodoEntry(mut todos: WriteSignal<HashMap<u32, TodoItem>>, id: u32) -> Element
class: "destroy",
onclick: move |evt| {
evt.prevent_default();
todos.write().remove(&id);
todos.remove(&id);
},
}
}
Expand All @@ -191,7 +191,7 @@ fn TodoEntry(mut todos: WriteSignal<HashMap<u32, TodoItem>>, id: u32) -> Element
input {
class: "edit",
value: "{contents}",
oninput: move |evt| todos.write().get_mut(&id).unwrap().contents = evt.value(),
oninput: move |evt| todos.get_mut(&id).unwrap().contents = evt.value(),
autofocus: "true",
onfocusout: move |_| is_editing.set(false),
onkeydown: move |evt| {
Expand Down Expand Up @@ -250,7 +250,7 @@ fn ListFooter(
if show_clear_completed() {
button {
class: "clear-completed",
onclick: move |_| todos.write().retain(|_, todo| !todo.checked),
onclick: move |_| todos.retain(|_, todo| !todo.checked),
"Clear completed"
}
}
Expand Down
2 changes: 1 addition & 1 deletion examples/01-app-demos/todomvc_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ fn TodoHeader(mut todos: Store<TodoState>) -> Element {
let mut todo_id = use_signal(|| 0);

let onkeydown = move |evt: KeyboardEvent| {
if evt.key() == Key::Enter && !draft.read().is_empty() {
if evt.key() == Key::Enter && !draft.is_empty() {
let id = todo_id();
let todo = TodoItem::new(draft.take());
todos.todos().insert(id, todo);
Expand Down
2 changes: 1 addition & 1 deletion examples/01-app-demos/websocket_chat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ fn app() -> Element {
use_future(move || async move {
while let Ok(msg) = socket.recv().await {
match msg {
ServerEvent::ReceiveMessage(message) => message_list.write().push(message),
ServerEvent::ReceiveMessage(message) => message_list.push(message),
ServerEvent::Connected { messages } => message_list.set(messages),
}
}
Expand Down
2 changes: 1 addition & 1 deletion examples/07-fullstack/server_sent_events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ fn app() -> Element {

// And then poll it for new events, adding them to our signal
while let Some(Ok(event)) = stream.recv().await {
events.write().push(event);
events.push(event);
}

anyhow::Ok(())
Expand Down
10 changes: 5 additions & 5 deletions examples/07-fullstack/streaming.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,23 @@ fn app() -> Element {
let mut json_responses = use_signal(Vec::new);

let mut start_text_stream = use_action(move || async move {
text_responses.write().clear();
text_responses.clear();
let mut stream = text_stream(Some(100)).await?;

while let Some(Ok(text)) = stream.next().await {
text_responses.write().push_str(&text);
text_responses.write().push('\n');
text_responses.push_str(&text);
text_responses.push('\n');
}

dioxus::Ok(())
});

let mut start_json_stream = use_action(move || async move {
json_responses.write().clear();
json_responses.clear();
let mut stream = json_stream().await?;

while let Some(Ok(dog)) = stream.next().await {
json_responses.write().push(dog);
json_responses.push(dog);
}

dioxus::Ok(())
Expand Down
2 changes: 1 addition & 1 deletion examples/08-apis/control_focus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ fn app() -> Element {
input {
r#type: "number",
value: "{i}",
onmounted: move |cx| elements.write().push(cx.data()),
onmounted: move |cx| elements.push(cx.data()),
oninput: move |_| running.set(false),
}
}
Expand Down
6 changes: 3 additions & 3 deletions examples/08-apis/file_upload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ fn app() -> Element {
for file in files {
let filename = file.name();
if let Ok(contents) = file.read_string().await {
files_uploaded.write().push(UploadedFile {
files_uploaded.push(UploadedFile {
name: filename,
contents,
});
} else {
files_uploaded.write().push(UploadedFile {
files_uploaded.push(UploadedFile {
name: filename,
contents: "Failed to read file".into(),
});
Expand All @@ -45,7 +45,7 @@ fn app() -> Element {

h1 { "File Upload Example" }
p { "Drop a .txt, .rs, or .js file here to read it" }
button { onclick: move |_| files_uploaded.write().clear(), "Clear files" }
button { onclick: move |_| files_uploaded.clear(), "Clear files" }

div {
label { r#for: "directory-upload", "Enable directory upload" }
Expand Down
2 changes: 1 addition & 1 deletion examples/08-apis/window_popup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ fn app() -> Element {
let handle = use_coroutine(move |mut rx: UnboundedReceiver<String>| async move {
use futures_util::StreamExt;
while let Some(message) = rx.next().await {
emails_sent.write().push(message);
emails_sent.push(message);
}
});

Expand Down
29 changes: 29 additions & 0 deletions packages/signals/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,32 @@ pub mod warnings;

mod boxed;
pub use boxed::*;

/// A macro to define extension methods for signal types that call the method with either `with` or `with_mut` depending on the mutability of self.
macro_rules! ext_methods {
(
$(
$(#[$meta:meta])*
fn $name:ident $(<$($gen:tt),*>)? (&$($self:ident)+ $(, $arg_name:ident: $arg_type:ty )* ) $(-> $ret:ty)? = $expr:expr;
)*
) => {
$(
$(#[$meta])*
#[track_caller]
fn $name$(<$($gen),*>)? (& $($self)+ $(, $arg_name: $arg_type )* ) $(-> $ret)?
{
ext_methods!(@with $($self)+, $($arg_name),*; $expr)
}
)*
};

(@with mut $self:ident, $($arg_name:ident),*; $expr:expr) => {
$self.with_mut(|_self| ($expr)(_self, $($arg_name),*))
};

(@with $self:ident, $($arg_name:ident),*; $expr:expr) => {
$self.with(|_self| ($expr)(_self, $($arg_name),*))
};
}

pub(crate) use ext_methods;
105 changes: 102 additions & 3 deletions packages/signals/src/read.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use std::{mem::MaybeUninit, ops::Index};
use std::collections::{HashMap, HashSet};
use std::{
mem::MaybeUninit,
ops::{Deref, Index},
};

use crate::{ext_methods, MappedSignal, ReadSignal};
use dioxus_core::Subscribers;
use generational_box::{AnyStorage, UnsyncStorage};

use crate::{MappedSignal, ReadSignal};

/// A reference to a value that can be read from.
#[allow(type_alias_bounds)]
pub type ReadableRef<'a, T: Readable, O = <T as Readable>::Target> =
Expand Down Expand Up @@ -423,3 +426,99 @@ pub trait ReadableResultExt<T, E>: Readable<Target = Result<T, E>> {
}

impl<T, E, R> ReadableResultExt<T, E> for R where R: Readable<Target = Result<T, E>> {}

/// An extension trait for [`Readable<String>`] that provides some convenience methods.
pub trait ReadableStringExt: Readable<Target = String> {
ext_methods! {
/// Check the capacity of the string.
fn capacity(&self) -> usize = String::capacity;
}
}

impl<W> ReadableStringExt for W where W: Readable<Target = String> {}

/// An extension trait for [`Readable<String>`] and [`Readable<str>`] that provides some convenience methods.
pub trait ReadableStrExt: Readable<Target: Deref<Target = str> + 'static> {
ext_methods! {
/// Check if the string is empty.
fn is_empty(&self) -> bool = |s: &Self::Target| s.deref().is_empty();

/// Get the length of the string.
fn len(&self) -> usize = |s: &Self::Target| s.deref().len();

/// Check if the string contains the given pattern.
fn contains(&self, pat: &str) -> bool = |s: &Self::Target, pat| s.deref().contains(pat);
}
}

impl<W> ReadableStrExt for W where W: Readable<Target: Deref<Target = str> + 'static> {}

/// An extension trait for [`Readable<HashMap<K, V, H>>`] that provides some convenience methods.
pub trait ReadableHashMapExt<K: 'static, V: 'static, H: 'static>:
Readable<Target = HashMap<K, V, H>>
{
ext_methods! {
/// Check if the hashmap is empty.
fn is_empty(&self) -> bool = HashMap::is_empty;

/// Get the length of the hashmap.
fn len(&self) -> usize = HashMap::len;

/// Get the capacity of the hashmap.
fn capacity(&self) -> usize = HashMap::capacity;
}

/// Get the value for the given key.
#[track_caller]
fn get(&self, key: &K) -> Option<ReadableRef<'_, Self, V>>
where
K: std::hash::Hash + Eq,
H: std::hash::BuildHasher,
{
<Self::Storage as AnyStorage>::try_map(self.read(), |v| v.get(key))
}

/// Check if the hashmap contains the given key.
#[track_caller]
fn contains_key(&self, key: &K) -> bool
where
K: std::hash::Hash + Eq,
H: std::hash::BuildHasher,
{
self.with(|v| v.contains_key(key))
}
}

impl<K: 'static, V: 'static, H: 'static, R> ReadableHashMapExt<K, V, H> for R where
R: Readable<Target = HashMap<K, V, H>>
{
}

/// An extension trait for [`Readable<HashSet<V, H>>`] that provides some convenience methods.
pub trait ReadableHashSetExt<V: 'static, H: 'static>: Readable<Target = HashSet<V, H>> {
ext_methods! {
/// Check if the hashset is empty.
fn is_empty(&self) -> bool = HashSet::is_empty;

/// Get the length of the hashset.
fn len(&self) -> usize = HashSet::len;

/// Get the capacity of the hashset.
fn capacity(&self) -> usize = HashSet::capacity;
}

/// Check if the hashset contains the given value.
#[track_caller]
fn contains(&self, value: &V) -> bool
where
V: std::hash::Hash + Eq,
H: std::hash::BuildHasher,
{
self.with(|v| v.contains(value))
}
}

impl<V: 'static, H: 'static, R> ReadableHashSetExt<V, H> for R where
R: Readable<Target = HashSet<V, H>>
{
}
Loading