Merge pull request #290 from DioxusLabs/jk/use-state-unify
Make use_state a single value again
This commit is contained in:
16 files changed
+205
-136
No files matched your search
+24
-32
@@ -18,20 +18,16 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn app(cx: Scope) -> Element {
|
fn app(cx: Scope) -> Element {
|
||||||
let (display_value, set_display_value) = use_state(&cx, || String::from("0"));
|
let val = use_state(&cx, || String::from("0"));
|
||||||
|
|
||||||
let input_digit = move |num: u8| {
|
let input_digit = move |num: u8| {
|
||||||
if display_value == "0" {
|
if val.get() == "0" {
|
||||||
set_display_value(String::new());
|
val.set(String::new());
|
||||||
}
|
}
|
||||||
set_display_value
|
val.make_mut().push_str(num.to_string().as_str());
|
||||||
.make_mut()
|
|
||||||
.push_str(num.to_string().as_str());
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let input_operator = move |key: &str| {
|
let input_operator = move |key: &str| val.make_mut().push_str(key);
|
||||||
set_display_value.make_mut().push_str(key);
|
|
||||||
};
|
|
||||||
|
|
||||||
cx.render(rsx!(
|
cx.render(rsx!(
|
||||||
style { [include_str!("./assets/calculator.css")] }
|
style { [include_str!("./assets/calculator.css")] }
|
||||||
@@ -54,34 +50,34 @@ fn app(cx: Scope) -> Element {
|
|||||||
KeyCode::Num8 => input_digit(8),
|
KeyCode::Num8 => input_digit(8),
|
||||||
KeyCode::Num9 => input_digit(9),
|
KeyCode::Num9 => input_digit(9),
|
||||||
KeyCode::Backspace => {
|
KeyCode::Backspace => {
|
||||||
if !display_value.len() != 0 {
|
if !val.len() != 0 {
|
||||||
set_display_value.make_mut().pop();
|
val.make_mut().pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
div { class: "calculator-display", [display_value.to_string()] }
|
div { class: "calculator-display", [val.to_string()] }
|
||||||
div { class: "calculator-keypad",
|
div { class: "calculator-keypad",
|
||||||
div { class: "input-keys",
|
div { class: "input-keys",
|
||||||
div { class: "function-keys",
|
div { class: "function-keys",
|
||||||
button {
|
button {
|
||||||
class: "calculator-key key-clear",
|
class: "calculator-key key-clear",
|
||||||
onclick: move |_| {
|
onclick: move |_| {
|
||||||
set_display_value(String::new());
|
val.set(String::new());
|
||||||
if !display_value.is_empty(){
|
if !val.is_empty(){
|
||||||
set_display_value("0".into());
|
val.set("0".into());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[if display_value.is_empty() { "C" } else { "AC" }]
|
[if val.is_empty() { "C" } else { "AC" }]
|
||||||
}
|
}
|
||||||
button {
|
button {
|
||||||
class: "calculator-key key-sign",
|
class: "calculator-key key-sign",
|
||||||
onclick: move |_| {
|
onclick: move |_| {
|
||||||
let temp = calc_val(display_value.clone());
|
let temp = calc_val(val.as_str());
|
||||||
if temp > 0.0 {
|
if temp > 0.0 {
|
||||||
set_display_value(format!("-{}", temp));
|
val.set(format!("-{}", temp));
|
||||||
} else {
|
} else {
|
||||||
set_display_value(format!("{}", temp.abs()));
|
val.set(format!("{}", temp.abs()));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"±"
|
"±"
|
||||||
@@ -89,8 +85,8 @@ fn app(cx: Scope) -> Element {
|
|||||||
button {
|
button {
|
||||||
class: "calculator-key key-percent",
|
class: "calculator-key key-percent",
|
||||||
onclick: move |_| {
|
onclick: move |_| {
|
||||||
set_display_value(
|
val.set(
|
||||||
format!("{}", calc_val(display_value.clone()) / 100.0)
|
format!("{}", calc_val(val.as_str()) / 100.0)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
"%"
|
"%"
|
||||||
@@ -100,7 +96,7 @@ fn app(cx: Scope) -> Element {
|
|||||||
button { class: "calculator-key key-0", onclick: move |_| input_digit(0),
|
button { class: "calculator-key key-0", onclick: move |_| input_digit(0),
|
||||||
"0"
|
"0"
|
||||||
}
|
}
|
||||||
button { class: "calculator-key key-dot", onclick: move |_| set_display_value.make_mut().push('.'),
|
button { class: "calculator-key key-dot", onclick: move |_| val.make_mut().push('.'),
|
||||||
"●"
|
"●"
|
||||||
}
|
}
|
||||||
(1..10).map(|k| rsx!{
|
(1..10).map(|k| rsx!{
|
||||||
@@ -114,25 +110,21 @@ fn app(cx: Scope) -> Element {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
div { class: "operator-keys",
|
div { class: "operator-keys",
|
||||||
button { class: "calculator-key key-divide",
|
button { class: "calculator-key key-divide", onclick: move |_| input_operator("/"),
|
||||||
onclick: move |_| input_operator("/"),
|
|
||||||
"÷"
|
"÷"
|
||||||
}
|
}
|
||||||
button { class: "calculator-key key-multiply",
|
button { class: "calculator-key key-multiply", onclick: move |_| input_operator("*"),
|
||||||
onclick: move |_| input_operator("*"),
|
|
||||||
"×"
|
"×"
|
||||||
}
|
}
|
||||||
button { class: "calculator-key key-subtract",
|
button { class: "calculator-key key-subtract", onclick: move |_| input_operator("-"),
|
||||||
onclick: move |_| input_operator("-"),
|
|
||||||
"−"
|
"−"
|
||||||
}
|
}
|
||||||
button { class: "calculator-key key-add",
|
button { class: "calculator-key key-add", onclick: move |_| input_operator("+"),
|
||||||
onclick: move |_| input_operator("+"),
|
|
||||||
"+"
|
"+"
|
||||||
}
|
}
|
||||||
button { class: "calculator-key key-equals",
|
button { class: "calculator-key key-equals",
|
||||||
onclick: move |_| {
|
onclick: move |_| {
|
||||||
set_display_value(format!("{}", calc_val(display_value.clone())));
|
val.set(format!("{}", calc_val(val.as_str())));
|
||||||
},
|
},
|
||||||
"="
|
"="
|
||||||
}
|
}
|
||||||
@@ -145,7 +137,7 @@ fn app(cx: Scope) -> Element {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calc_val(val: String) -> f64 {
|
fn calc_val(val: &str) -> f64 {
|
||||||
let mut temp = String::new();
|
let mut temp = String::new();
|
||||||
let mut operation = "+".to_string();
|
let mut operation = "+".to_string();
|
||||||
|
|
||||||
|
|||||||
+18
-18
@@ -21,10 +21,10 @@ pub struct Client {
|
|||||||
|
|
||||||
fn app(cx: Scope) -> Element {
|
fn app(cx: Scope) -> Element {
|
||||||
let clients = use_ref(&cx, || vec![] as Vec<Client>);
|
let clients = use_ref(&cx, || vec![] as Vec<Client>);
|
||||||
let (scene, set_scene) = use_state(&cx, || Scene::ClientsList);
|
let scene = use_state(&cx, || Scene::ClientsList);
|
||||||
let (firstname, set_firstname) = use_state(&cx, String::new);
|
let firstname = use_state(&cx, String::new);
|
||||||
let (lastname, set_lastname) = use_state(&cx, String::new);
|
let lastname = use_state(&cx, String::new);
|
||||||
let (description, set_description) = use_state(&cx, String::new);
|
let description = use_state(&cx, String::new);
|
||||||
|
|
||||||
cx.render(rsx!(
|
cx.render(rsx!(
|
||||||
body {
|
body {
|
||||||
@@ -38,7 +38,7 @@ fn app(cx: Scope) -> Element {
|
|||||||
|
|
||||||
h1 {"Dioxus CRM Example"}
|
h1 {"Dioxus CRM Example"}
|
||||||
|
|
||||||
match scene {
|
match scene.get() {
|
||||||
Scene::ClientsList => rsx!(
|
Scene::ClientsList => rsx!(
|
||||||
div { class: "crm",
|
div { class: "crm",
|
||||||
h2 { margin_bottom: "10px", "List of clients" }
|
h2 { margin_bottom: "10px", "List of clients" }
|
||||||
@@ -51,8 +51,8 @@ fn app(cx: Scope) -> Element {
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
button { class: "pure-button pure-button-primary", onclick: move |_| set_scene(Scene::NewClientForm), "Add New" }
|
button { class: "pure-button pure-button-primary", onclick: move |_| scene.set(Scene::NewClientForm), "Add New" }
|
||||||
button { class: "pure-button", onclick: move |_| set_scene(Scene::Settings), "Settings" }
|
button { class: "pure-button", onclick: move |_| scene.set(Scene::Settings), "Settings" }
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
Scene::NewClientForm => rsx!(
|
Scene::NewClientForm => rsx!(
|
||||||
@@ -63,36 +63,36 @@ fn app(cx: Scope) -> Element {
|
|||||||
class: "new-client firstname",
|
class: "new-client firstname",
|
||||||
placeholder: "First name",
|
placeholder: "First name",
|
||||||
value: "{firstname}",
|
value: "{firstname}",
|
||||||
oninput: move |e| set_firstname(e.value.clone())
|
oninput: move |e| firstname.set(e.value.clone())
|
||||||
}
|
}
|
||||||
input {
|
input {
|
||||||
class: "new-client lastname",
|
class: "new-client lastname",
|
||||||
placeholder: "Last name",
|
placeholder: "Last name",
|
||||||
value: "{lastname}",
|
value: "{lastname}",
|
||||||
oninput: move |e| set_lastname(e.value.clone())
|
oninput: move |e| lastname.set(e.value.clone())
|
||||||
}
|
}
|
||||||
textarea {
|
textarea {
|
||||||
class: "new-client description",
|
class: "new-client description",
|
||||||
placeholder: "Description",
|
placeholder: "Description",
|
||||||
value: "{description}",
|
value: "{description}",
|
||||||
oninput: move |e| set_description(e.value.clone())
|
oninput: move |e| description.set(e.value.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
button {
|
button {
|
||||||
class: "pure-button pure-button-primary",
|
class: "pure-button pure-button-primary",
|
||||||
onclick: move |_| {
|
onclick: move |_| {
|
||||||
clients.write().push(Client {
|
clients.write().push(Client {
|
||||||
description: (*description).clone(),
|
description: (**description).clone(),
|
||||||
first_name: (*firstname).clone(),
|
first_name: (**firstname).clone(),
|
||||||
last_name: (*lastname).clone(),
|
last_name: (**lastname).clone(),
|
||||||
});
|
});
|
||||||
set_description(String::new());
|
description.set(String::new());
|
||||||
set_firstname(String::new());
|
firstname.set(String::new());
|
||||||
set_lastname(String::new());
|
lastname.set(String::new());
|
||||||
},
|
},
|
||||||
"Add New"
|
"Add New"
|
||||||
}
|
}
|
||||||
button { class: "pure-button", onclick: move |_| set_scene(Scene::ClientsList),
|
button { class: "pure-button", onclick: move |_| scene.set(Scene::ClientsList),
|
||||||
"Go Back"
|
"Go Back"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -108,7 +108,7 @@ fn app(cx: Scope) -> Element {
|
|||||||
}
|
}
|
||||||
button {
|
button {
|
||||||
class: "pure-button pure-button-primary",
|
class: "pure-button pure-button-primary",
|
||||||
onclick: move |_| set_scene(Scene::ClientsList),
|
onclick: move |_| scene.set(Scene::ClientsList),
|
||||||
"Go Back"
|
"Go Back"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,13 +5,15 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn app(cx: Scope) -> Element {
|
fn app(cx: Scope) -> Element {
|
||||||
let (disabled, set_disabled) = use_state(&cx, || false);
|
let disabled = use_state(&cx, || false);
|
||||||
|
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div {
|
div {
|
||||||
button {
|
button {
|
||||||
onclick: move |_| set_disabled(!disabled),
|
onclick: move |_| disabled.set(!disabled),
|
||||||
"click to " [if *disabled {"enable"} else {"disable"} ] " the lower button"
|
"click to "
|
||||||
|
[if disabled == true {"enable"} else {"disable"}]
|
||||||
|
" the lower button"
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
|
|||||||
+5
-5
@@ -16,7 +16,7 @@ struct ListBreeds {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn app(cx: Scope) -> Element {
|
fn app(cx: Scope) -> Element {
|
||||||
let (breed, set_breed) = use_state(&cx, || None);
|
let breed = use_state(&cx, || None);
|
||||||
|
|
||||||
let breeds = use_future(&cx, (), |_| async move {
|
let breeds = use_future(&cx, (), |_| async move {
|
||||||
reqwest::get("https://dog.ceo/api/breeds/list/all")
|
reqwest::get("https://dog.ceo/api/breeds/list/all")
|
||||||
@@ -32,17 +32,17 @@ fn app(cx: Scope) -> Element {
|
|||||||
h1 { "Select a dog breed!" }
|
h1 { "Select a dog breed!" }
|
||||||
div { display: "flex",
|
div { display: "flex",
|
||||||
ul { flex: "50%",
|
ul { flex: "50%",
|
||||||
breeds.message.keys().map(|breed| rsx!(
|
breeds.message.keys().map(|cur_breed| rsx!(
|
||||||
li {
|
li {
|
||||||
button {
|
button {
|
||||||
onclick: move |_| set_breed(Some(breed.clone())),
|
onclick: move |_| breed.set(Some(cur_breed.clone())),
|
||||||
"{breed}"
|
"{cur_breed}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
div { flex: "50%",
|
div { flex: "50%",
|
||||||
match breed {
|
match breed.get() {
|
||||||
Some(breed) => rsx!( Breed { breed: breed.clone() } ),
|
Some(breed) => rsx!( Breed { breed: breed.clone() } ),
|
||||||
None => rsx!("No Breed selected"),
|
None => rsx!("No Breed selected"),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ impl Label {
|
|||||||
|
|
||||||
fn app(cx: Scope) -> Element {
|
fn app(cx: Scope) -> Element {
|
||||||
let items = use_ref(&cx, Vec::new);
|
let items = use_ref(&cx, Vec::new);
|
||||||
let (selected, set_selected) = use_state(&cx, || None);
|
let selected = use_state(&cx, || None);
|
||||||
|
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div { class: "container",
|
div { class: "container",
|
||||||
@@ -71,7 +71,7 @@ fn app(cx: Scope) -> Element {
|
|||||||
rsx!(tr { class: "{is_in_danger}",
|
rsx!(tr { class: "{is_in_danger}",
|
||||||
td { class:"col-md-1" }
|
td { class:"col-md-1" }
|
||||||
td { class:"col-md-1", "{item.key}" }
|
td { class:"col-md-1", "{item.key}" }
|
||||||
td { class:"col-md-1", onclick: move |_| set_selected(Some(id)),
|
td { class:"col-md-1", onclick: move |_| selected.set(Some(id)),
|
||||||
a { class: "lbl", item.labels }
|
a { class: "lbl", item.labels }
|
||||||
}
|
}
|
||||||
td { class: "col-md-1",
|
td { class: "col-md-1",
|
||||||
|
|||||||
@@ -20,13 +20,13 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn app(cx: Scope) -> Element {
|
fn app(cx: Scope) -> Element {
|
||||||
let (val, set_val) = use_state(&cx, || 0);
|
let val = use_state(&cx, || 0);
|
||||||
|
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div {
|
div {
|
||||||
h1 { "hello world. Count: {val}" }
|
h1 { "hello world. Count: {val}" }
|
||||||
button {
|
button {
|
||||||
onclick: move |_| set_val(val + 1),
|
onclick: move |_| *val.make_mut() += 1,
|
||||||
"click to increment"
|
"click to increment"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,16 +15,16 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn app(cx: Scope) -> Element {
|
fn app(cx: Scope) -> Element {
|
||||||
let (state, set_state) = use_state(&cx, PlayerState::new);
|
let state = use_state(&cx, PlayerState::new);
|
||||||
|
|
||||||
cx.render(rsx!(
|
cx.render(rsx!(
|
||||||
div {
|
div {
|
||||||
h1 {"Select an option"}
|
h1 {"Select an option"}
|
||||||
h3 { "The radio is... " [state.is_playing()] "!" }
|
h3 { "The radio is... " [state.is_playing()] "!" }
|
||||||
button { onclick: move |_| set_state.make_mut().reduce(PlayerAction::Pause),
|
button { onclick: move |_| state.make_mut().reduce(PlayerAction::Pause),
|
||||||
"Pause"
|
"Pause"
|
||||||
}
|
}
|
||||||
button { onclick: move |_| set_state.make_mut().reduce(PlayerAction::Play),
|
button { onclick: move |_| state.make_mut().reduce(PlayerAction::Play),
|
||||||
"Play"
|
"Play"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -9,13 +9,13 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn app(cx: Scope) -> Element {
|
fn app(cx: Scope) -> Element {
|
||||||
let (count, set_count) = use_state(&cx, || 0);
|
let count = use_state(&cx, || 0);
|
||||||
|
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div {
|
div {
|
||||||
h1 { "High-Five counter: {count}" }
|
h1 { "High-Five counter: {count}" }
|
||||||
button { onclick: move |_| set_count(count + 1), "Up high!" }
|
button { onclick: move |_| *count.make_mut() += 1, "Up high!" }
|
||||||
button { onclick: move |_| set_count(count - 1), "Down low!" }
|
button { onclick: move |_| *count.make_mut() -= 1, "Down low!" }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -12,7 +12,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn example(cx: Scope) -> Element {
|
fn example(cx: Scope) -> Element {
|
||||||
let (items, _set_items) = use_state(&cx, || {
|
let items = use_state(&cx, || {
|
||||||
vec![Thing {
|
vec![Thing {
|
||||||
a: "asd".to_string(),
|
a: "asd".to_string(),
|
||||||
b: 10,
|
b: 10,
|
||||||
|
|||||||
+3
-3
@@ -7,7 +7,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn app(cx: Scope) -> Element {
|
fn app(cx: Scope) -> Element {
|
||||||
let (val, set_val) = use_state(&cx, || 5);
|
let val = use_state(&cx, || 5);
|
||||||
|
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div {
|
div {
|
||||||
@@ -21,12 +21,12 @@ fn app(cx: Scope) -> Element {
|
|||||||
height: "80%",
|
height: "80%",
|
||||||
width: "80%",
|
width: "80%",
|
||||||
Die {
|
Die {
|
||||||
value: *val,
|
value: **val,
|
||||||
keep: true,
|
keep: true,
|
||||||
onclick: move |_| {
|
onclick: move |_| {
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
set_val(rng.gen_range(1..6));
|
val.set(rng.gen_range(1..6));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -10,14 +10,14 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn app(cx: Scope) -> Element {
|
fn app(cx: Scope) -> Element {
|
||||||
let (count, set_count) = use_state(&cx, || 0);
|
let count = use_state(&cx, || 0);
|
||||||
|
|
||||||
use_future(&cx, (), move |_| {
|
use_future(&cx, (), move |_| {
|
||||||
let set_count = set_count.clone();
|
let count = count.clone();
|
||||||
async move {
|
async move {
|
||||||
loop {
|
loop {
|
||||||
tokio::time::sleep(Duration::from_millis(1000)).await;
|
tokio::time::sleep(Duration::from_millis(1000)).await;
|
||||||
set_count.modify(|f| f + 1);
|
count.modify(|f| f + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -26,7 +26,7 @@ fn app(cx: Scope) -> Element {
|
|||||||
div {
|
div {
|
||||||
h1 { "Current count: {count}" }
|
h1 { "Current count: {count}" }
|
||||||
button {
|
button {
|
||||||
onclick: move |_| set_count(0),
|
onclick: move |_| count.set(0),
|
||||||
"Reset the count"
|
"Reset the count"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn app(cx: Scope) -> Element {
|
fn app(cx: Scope) -> Element {
|
||||||
let (model, set_model) = use_state(&cx, || String::from("asd"));
|
let model = use_state(&cx, || String::from("asd"));
|
||||||
|
|
||||||
println!("{}", model);
|
println!("{}", model);
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ fn app(cx: Scope) -> Element {
|
|||||||
rows: "10",
|
rows: "10",
|
||||||
cols: "80",
|
cols: "80",
|
||||||
value: "{model}",
|
value: "{model}",
|
||||||
oninput: move |e| set_model(e.value.clone()),
|
oninput: move |e| model.set(e.value.clone()),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
+26
-26
@@ -19,15 +19,15 @@ pub struct TodoItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn app(cx: Scope<()>) -> Element {
|
pub fn app(cx: Scope<()>) -> Element {
|
||||||
let (todos, set_todos) = use_state(&cx, im_rc::HashMap::<u32, TodoItem>::default);
|
let todos = use_state(&cx, im_rc::HashMap::<u32, TodoItem>::default);
|
||||||
let (filter, set_filter) = use_state(&cx, || FilterState::All);
|
let filter = use_state(&cx, || FilterState::All);
|
||||||
let (draft, set_draft) = use_state(&cx, || "".to_string());
|
let draft = use_state(&cx, || "".to_string());
|
||||||
let (todo_id, set_todo_id) = use_state(&cx, || 0);
|
let todo_id = use_state(&cx, || 0);
|
||||||
|
|
||||||
// Filter the todos based on the filter state
|
// Filter the todos based on the filter state
|
||||||
let mut filtered_todos = todos
|
let mut filtered_todos = todos
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(_, item)| match filter {
|
.filter(|(_, item)| match **filter {
|
||||||
FilterState::All => true,
|
FilterState::All => true,
|
||||||
FilterState::Active => !item.checked,
|
FilterState::Active => !item.checked,
|
||||||
FilterState::Completed => item.checked,
|
FilterState::Completed => item.checked,
|
||||||
@@ -54,25 +54,25 @@ pub fn app(cx: Scope<()>) -> Element {
|
|||||||
placeholder: "What needs to be done?",
|
placeholder: "What needs to be done?",
|
||||||
value: "{draft}",
|
value: "{draft}",
|
||||||
autofocus: "true",
|
autofocus: "true",
|
||||||
oninput: move |evt| set_draft(evt.value.clone()),
|
oninput: move |evt| draft.set(evt.value.clone()),
|
||||||
onkeydown: move |evt| {
|
onkeydown: move |evt| {
|
||||||
if evt.key == "Enter" && !draft.is_empty() {
|
if evt.key == "Enter" && !draft.is_empty() {
|
||||||
set_todos.make_mut().insert(
|
todos.make_mut().insert(
|
||||||
*todo_id,
|
**todo_id,
|
||||||
TodoItem {
|
TodoItem {
|
||||||
id: *todo_id,
|
id: **todo_id,
|
||||||
checked: false,
|
checked: false,
|
||||||
contents: draft.clone(),
|
contents: draft.to_string(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
set_todo_id(todo_id + 1);
|
*todo_id.make_mut() += 1;
|
||||||
set_draft("".to_string());
|
draft.set("".to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ul { class: "todo-list",
|
ul { class: "todo-list",
|
||||||
filtered_todos.iter().map(|id| rsx!(todo_entry( key: "{id}", id: *id, set_todos: set_todos )))
|
filtered_todos.iter().map(|id| rsx!(todo_entry( key: "{id}", id: *id, todos: todos )))
|
||||||
}
|
}
|
||||||
(!todos.is_empty()).then(|| rsx!(
|
(!todos.is_empty()).then(|| rsx!(
|
||||||
footer { class: "footer",
|
footer { class: "footer",
|
||||||
@@ -81,14 +81,14 @@ pub fn app(cx: Scope<()>) -> Element {
|
|||||||
span {"{item_text} left"}
|
span {"{item_text} left"}
|
||||||
}
|
}
|
||||||
ul { class: "filters",
|
ul { class: "filters",
|
||||||
li { class: "All", a { onclick: move |_| set_filter(FilterState::All), "All" }}
|
li { class: "All", a { onclick: move |_| filter.set(FilterState::All), "All" }}
|
||||||
li { class: "Active", a { onclick: move |_| set_filter(FilterState::Active), "Active" }}
|
li { class: "Active", a { onclick: move |_| filter.set(FilterState::Active), "Active" }}
|
||||||
li { class: "Completed", a { onclick: move |_| set_filter(FilterState::Completed), "Completed" }}
|
li { class: "Completed", a { onclick: move |_| filter.set(FilterState::Completed), "Completed" }}
|
||||||
}
|
}
|
||||||
(show_clear_completed).then(|| rsx!(
|
(show_clear_completed).then(|| rsx!(
|
||||||
button {
|
button {
|
||||||
class: "clear-completed",
|
class: "clear-completed",
|
||||||
onclick: move |_| set_todos.make_mut().retain(|_, todo| !todo.checked),
|
onclick: move |_| todos.make_mut().retain(|_, todo| !todo.checked),
|
||||||
"Clear completed"
|
"Clear completed"
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
@@ -106,29 +106,29 @@ pub fn app(cx: Scope<()>) -> Element {
|
|||||||
|
|
||||||
#[derive(Props)]
|
#[derive(Props)]
|
||||||
pub struct TodoEntryProps<'a> {
|
pub struct TodoEntryProps<'a> {
|
||||||
set_todos: &'a UseState<im_rc::HashMap<u32, TodoItem>>,
|
todos: &'a UseState<im_rc::HashMap<u32, TodoItem>>,
|
||||||
id: u32,
|
id: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn todo_entry<'a>(cx: Scope<'a, TodoEntryProps<'a>>) -> Element {
|
pub fn todo_entry<'a>(cx: Scope<'a, TodoEntryProps<'a>>) -> Element {
|
||||||
let (is_editing, set_is_editing) = use_state(&cx, || false);
|
let is_editing = use_state(&cx, || false);
|
||||||
|
|
||||||
let todos = cx.props.set_todos.get();
|
let todos = cx.props.todos.get();
|
||||||
let todo = &todos[&cx.props.id];
|
let todo = &todos[&cx.props.id];
|
||||||
let completed = if todo.checked { "completed" } else { "" };
|
let completed = if todo.checked { "completed" } else { "" };
|
||||||
let editing = if *is_editing { "editing" } else { "" };
|
let editing = if **is_editing { "editing" } else { "" };
|
||||||
|
|
||||||
rsx!(cx, li {
|
rsx!(cx, li {
|
||||||
class: "{completed} {editing}",
|
class: "{completed} {editing}",
|
||||||
div { class: "view",
|
div { class: "view",
|
||||||
input { class: "toggle", r#type: "checkbox", id: "cbg-{todo.id}", checked: "{todo.checked}",
|
input { class: "toggle", r#type: "checkbox", id: "cbg-{todo.id}", checked: "{todo.checked}",
|
||||||
onchange: move |evt| {
|
onchange: move |evt| {
|
||||||
cx.props.set_todos.make_mut()[&cx.props.id].checked = evt.value.parse().unwrap();
|
cx.props.todos.make_mut()[&cx.props.id].checked = evt.value.parse().unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
label {
|
label {
|
||||||
r#for: "cbg-{todo.id}",
|
r#for: "cbg-{todo.id}",
|
||||||
onclick: move |_| set_is_editing(true),
|
onclick: move |_| is_editing.set(true),
|
||||||
"{todo.contents}"
|
"{todo.contents}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,12 +136,12 @@ pub fn todo_entry<'a>(cx: Scope<'a, TodoEntryProps<'a>>) -> Element {
|
|||||||
input {
|
input {
|
||||||
class: "edit",
|
class: "edit",
|
||||||
value: "{todo.contents}",
|
value: "{todo.contents}",
|
||||||
oninput: move |evt| cx.props.set_todos.make_mut()[&cx.props.id].contents = evt.value.clone(),
|
oninput: move |evt| cx.props.todos.make_mut()[&cx.props.id].contents = evt.value.clone(),
|
||||||
autofocus: "true",
|
autofocus: "true",
|
||||||
onfocusout: move |_| set_is_editing(false),
|
onfocusout: move |_| is_editing.set(false),
|
||||||
onkeydown: move |evt| {
|
onkeydown: move |evt| {
|
||||||
match evt.key.as_str() {
|
match evt.key.as_str() {
|
||||||
"Enter" | "Escape" | "Tab" => set_is_editing(false),
|
"Enter" | "Escape" | "Tab" => is_editing.set(false),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ fn app(cx: Scope) -> Element {
|
|||||||
// window.set_fullscreen(true);
|
// window.set_fullscreen(true);
|
||||||
// window.set_resizable(false);
|
// window.set_resizable(false);
|
||||||
|
|
||||||
let (fullscreen, set_fullscreen) = use_state(&cx, || false);
|
let fullscreen = use_state(&cx, || false);
|
||||||
let (always_on_top, set_always_on_top) = use_state(&cx, || false);
|
let always_on_top = use_state(&cx, || false);
|
||||||
let (decorations, set_decorations) = use_state(&cx, || false);
|
let decorations = use_state(&cx, || false);
|
||||||
|
|
||||||
cx.render(rsx!(
|
cx.render(rsx!(
|
||||||
link { href:"https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css", rel:"stylesheet" }
|
link { href:"https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css", rel:"stylesheet" }
|
||||||
@@ -39,10 +39,9 @@ fn app(cx: Scope) -> Element {
|
|||||||
onmousedown: |evt| evt.cancel_bubble(),
|
onmousedown: |evt| evt.cancel_bubble(),
|
||||||
onclick: move |_| {
|
onclick: move |_| {
|
||||||
|
|
||||||
window.set_fullscreen(!fullscreen);
|
window.set_fullscreen(!**fullscreen);
|
||||||
window.set_resizable(*fullscreen);
|
window.set_resizable(**fullscreen);
|
||||||
|
fullscreen.modify(|f| !*f);
|
||||||
set_fullscreen(!fullscreen);
|
|
||||||
},
|
},
|
||||||
"Fullscreen"
|
"Fullscreen"
|
||||||
}
|
}
|
||||||
@@ -65,7 +64,7 @@ fn app(cx: Scope) -> Element {
|
|||||||
onmousedown: |evt| evt.cancel_bubble(),
|
onmousedown: |evt| evt.cancel_bubble(),
|
||||||
onclick: move |_| {
|
onclick: move |_| {
|
||||||
window.set_always_on_top(!always_on_top);
|
window.set_always_on_top(!always_on_top);
|
||||||
set_always_on_top(!always_on_top);
|
always_on_top.set(!always_on_top);
|
||||||
},
|
},
|
||||||
"Always On Top"
|
"Always On Top"
|
||||||
}
|
}
|
||||||
@@ -76,7 +75,7 @@ fn app(cx: Scope) -> Element {
|
|||||||
onmousedown: |evt| evt.cancel_bubble(),
|
onmousedown: |evt| evt.cancel_bubble(),
|
||||||
onclick: move |_| {
|
onclick: move |_| {
|
||||||
window.set_decorations(!decorations);
|
window.set_decorations(!decorations);
|
||||||
set_decorations(!decorations);
|
decorations.set(!decorations);
|
||||||
},
|
},
|
||||||
"Set Decorations"
|
"Set Decorations"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn app(cx: Scope) -> Element {
|
fn app(cx: Scope) -> Element {
|
||||||
let (contents, set_contents) = use_state(&cx, || {
|
let contents = use_state(&cx, || {
|
||||||
String::from("<script>alert(\"hello world\")</script>")
|
String::from("<script>alert(\"hello world\")</script>")
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ fn app(cx: Scope) -> Element {
|
|||||||
input {
|
input {
|
||||||
value: "{contents}",
|
value: "{contents}",
|
||||||
r#type: "text",
|
r#type: "text",
|
||||||
oninput: move |e| set_contents(e.value.clone()),
|
oninput: move |e| contents.set(e.value.clone()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use dioxus_core::prelude::*;
|
|||||||
use std::{
|
use std::{
|
||||||
cell::{RefCell, RefMut},
|
cell::{RefCell, RefMut},
|
||||||
fmt::{Debug, Display},
|
fmt::{Debug, Display},
|
||||||
|
ops::Not,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
@@ -18,13 +19,13 @@ use std::{
|
|||||||
///
|
///
|
||||||
/// ```ignore
|
/// ```ignore
|
||||||
/// const Example: Component = |cx| {
|
/// const Example: Component = |cx| {
|
||||||
/// let (count, set_count) = use_state(&cx, || 0);
|
/// let count = use_state(&cx, || 0);
|
||||||
///
|
///
|
||||||
/// cx.render(rsx! {
|
/// cx.render(rsx! {
|
||||||
/// div {
|
/// div {
|
||||||
/// h1 { "Count: {count}" }
|
/// h1 { "Count: {count}" }
|
||||||
/// button { onclick: move |_| set_count(a - 1), "Increment" }
|
/// button { onclick: move |_| *count.modify() += 1, "Increment" }
|
||||||
/// button { onclick: move |_| set_count(a + 1), "Decrement" }
|
/// button { onclick: move |_| *count.modify() -= 1, "Decrement" }
|
||||||
/// }
|
/// }
|
||||||
/// ))
|
/// ))
|
||||||
/// }
|
/// }
|
||||||
@@ -32,7 +33,7 @@ use std::{
|
|||||||
pub fn use_state<'a, T: 'static>(
|
pub fn use_state<'a, T: 'static>(
|
||||||
cx: &'a ScopeState,
|
cx: &'a ScopeState,
|
||||||
initial_state_fn: impl FnOnce() -> T,
|
initial_state_fn: impl FnOnce() -> T,
|
||||||
) -> (&'a T, &'a UseState<T>) {
|
) -> &'a UseState<T> {
|
||||||
let hook = cx.use_hook(move |_| {
|
let hook = cx.use_hook(move |_| {
|
||||||
let current_val = Rc::new(initial_state_fn());
|
let current_val = Rc::new(initial_state_fn());
|
||||||
let update_callback = cx.schedule_update();
|
let update_callback = cx.schedule_update();
|
||||||
@@ -65,7 +66,7 @@ pub fn use_state<'a, T: 'static>(
|
|||||||
|
|
||||||
hook.current_val = hook.slot.borrow().clone();
|
hook.current_val = hook.slot.borrow().clone();
|
||||||
|
|
||||||
(hook.current_val.as_ref(), hook)
|
hook
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct UseState<T: 'static> {
|
pub struct UseState<T: 'static> {
|
||||||
@@ -76,6 +77,11 @@ pub struct UseState<T: 'static> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T: 'static> UseState<T> {
|
impl<T: 'static> UseState<T> {
|
||||||
|
/// Set the state to a new value.
|
||||||
|
pub fn set(&self, new: T) {
|
||||||
|
(self.setter)(new);
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the current value of the state by cloning its container Rc.
|
/// Get the current value of the state by cloning its container Rc.
|
||||||
///
|
///
|
||||||
/// This is useful when you are dealing with state in async contexts but need
|
/// This is useful when you are dealing with state in async contexts but need
|
||||||
@@ -188,7 +194,12 @@ impl<T: 'static> UseState<T> {
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get(&self) -> &Rc<T> {
|
pub fn get(&self) -> &T {
|
||||||
|
&self.current_val
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn get_rc(&self) -> &Rc<T> {
|
||||||
&self.current_val
|
&self.current_val
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,7 +271,7 @@ impl<T: Clone> UseState<T> {
|
|||||||
/// ```
|
/// ```
|
||||||
/// let (val, set_val) = use_state(&cx, || 0);
|
/// let (val, set_val) = use_state(&cx, || 0);
|
||||||
///
|
///
|
||||||
/// set_val.with_mut(|v| *v = 1);
|
/// *set_val.make_mut() += 1;
|
||||||
/// ```
|
/// ```
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn make_mut(&self) -> RefMut<T> {
|
pub fn make_mut(&self) -> RefMut<T> {
|
||||||
@@ -274,6 +285,12 @@ impl<T: Clone> UseState<T> {
|
|||||||
|
|
||||||
RefMut::map(slot, |rc| Rc::get_mut(rc).expect("the hard count to be 0"))
|
RefMut::map(slot, |rc| Rc::get_mut(rc).expect("the hard count to be 0"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert this handle to a tuple of the value and the handle itself.
|
||||||
|
#[must_use]
|
||||||
|
pub fn split(&self) -> (&T, &Self) {
|
||||||
|
(&self.current_val, self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: 'static> Clone for UseState<T> {
|
impl<T: 'static> Clone for UseState<T> {
|
||||||
@@ -293,10 +310,22 @@ impl<'a, T: 'static + Display> std::fmt::Display for UseState<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> PartialEq<UseState<T>> for UseState<T> {
|
impl<T: PartialEq> PartialEq<T> for UseState<T> {
|
||||||
|
fn eq(&self, other: &T) -> bool {
|
||||||
|
self.current_val.as_ref() == other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: this but for more interesting conrete types
|
||||||
|
impl PartialEq<bool> for &UseState<bool> {
|
||||||
|
fn eq(&self, other: &bool) -> bool {
|
||||||
|
self.current_val.as_ref() == other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: PartialEq> PartialEq<UseState<T>> for UseState<T> {
|
||||||
fn eq(&self, other: &UseState<T>) -> bool {
|
fn eq(&self, other: &UseState<T>) -> bool {
|
||||||
// some level of memoization for UseState
|
Rc::ptr_eq(&self.current_val, &other.current_val)
|
||||||
Rc::ptr_eq(&self.slot, &other.slot)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,10 +336,57 @@ impl<T: Debug> Debug for UseState<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T> std::ops::Deref for UseState<T> {
|
impl<'a, T> std::ops::Deref for UseState<T> {
|
||||||
type Target = Rc<dyn Fn(T)>;
|
type Target = T;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.setter
|
self.current_val.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Not + Copy> std::ops::Not for &UseState<T> {
|
||||||
|
type Output = <T as std::ops::Not>::Output;
|
||||||
|
|
||||||
|
fn not(self) -> Self::Output {
|
||||||
|
self.current_val.not()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Not + Copy> std::ops::Not for UseState<T> {
|
||||||
|
type Output = <T as std::ops::Not>::Output;
|
||||||
|
|
||||||
|
fn not(self) -> Self::Output {
|
||||||
|
self.current_val.not()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: std::ops::Add + Copy> std::ops::Add<T> for &UseState<T> {
|
||||||
|
type Output = <T as std::ops::Add>::Output;
|
||||||
|
|
||||||
|
fn add(self, other: T) -> Self::Output {
|
||||||
|
*self.current_val.as_ref() + other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: std::ops::Sub + Copy> std::ops::Sub<T> for &UseState<T> {
|
||||||
|
type Output = <T as std::ops::Sub>::Output;
|
||||||
|
|
||||||
|
fn sub(self, other: T) -> Self::Output {
|
||||||
|
*self.current_val.as_ref() - other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: std::ops::Div + Copy> std::ops::Div<T> for &UseState<T> {
|
||||||
|
type Output = <T as std::ops::Div>::Output;
|
||||||
|
|
||||||
|
fn div(self, other: T) -> Self::Output {
|
||||||
|
*self.current_val.as_ref() / other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: std::ops::Mul + Copy> std::ops::Mul<T> for &UseState<T> {
|
||||||
|
type Output = <T as std::ops::Mul>::Output;
|
||||||
|
|
||||||
|
fn mul(self, other: T) -> Self::Output {
|
||||||
|
*self.current_val.as_ref() * other
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -318,16 +394,16 @@ impl<'a, T> std::ops::Deref for UseState<T> {
|
|||||||
fn api_makes_sense() {
|
fn api_makes_sense() {
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
fn app(cx: Scope) -> Element {
|
fn app(cx: Scope) -> Element {
|
||||||
let (val, set_val) = use_state(&cx, || 0);
|
let val = use_state(&cx, || 0);
|
||||||
|
|
||||||
set_val(0);
|
val.set(0);
|
||||||
set_val.modify(|v| v + 1);
|
val.modify(|v| v + 1);
|
||||||
let real_current = set_val.current();
|
let real_current = val.current();
|
||||||
|
|
||||||
match val {
|
match val.get() {
|
||||||
10 => {
|
10 => {
|
||||||
set_val(20);
|
val.set(20);
|
||||||
set_val.modify(|v| v + 1);
|
val.modify(|v| v + 1);
|
||||||
}
|
}
|
||||||
20 => {}
|
20 => {}
|
||||||
_ => {
|
_ => {
|
||||||
@@ -336,9 +412,9 @@ fn api_makes_sense() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cx.spawn({
|
cx.spawn({
|
||||||
dioxus_core::to_owned![set_val];
|
dioxus_core::to_owned![val];
|
||||||
async move {
|
async move {
|
||||||
set_val.modify(|f| f + 1);
|
val.modify(|f| f + 1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user