@@ -66,6 +71,15 @@ If you know React, then you already know Dioxus.
- Multi-channel asynchronous scheduler for first-class async support.
- And more! Read the [full release post here](https://dioxuslabs.com/).
+
+### Examples
+
+All examples in this repo are desktop apps. To run an example, simply clone this repo and use cargo with the `desktop` feature enabled. For SSR examples, you might need to enable SSR instead.
+
+```
+cargo run --features desktop --example EXAMPLE
+```
+
## Get Started with...
@@ -80,8 +94,7 @@ If you know React, then you already know Dioxus.
-
-## Examples Projects:
+## Example Projects:
| File Navigator (Desktop) | WiFi scanner (Desktop) | TodoMVC (All platforms) | E-commerce w/ Tailwind (Liveview) |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -90,9 +103,6 @@ If you know React, then you already know Dioxus.
See the awesome-dioxus page for a curated list of content in the Dioxus Ecosystem.
-## Running examples locally
-
-All local examples are built for the desktop renderer. This means you can simply clone this repo and call `cargo run --example EXAMPLE_NAME`. To run non-desktop examples, checkout the example projects shown above.
## Why Dioxus and why Rust?
diff --git a/docs/guide/src/elements/conditional_rendering.md b/docs/guide/src/elements/conditional_rendering.md
index b23df6000..c23e62e47 100644
--- a/docs/guide/src/elements/conditional_rendering.md
+++ b/docs/guide/src/elements/conditional_rendering.md
@@ -81,11 +81,6 @@ fn App(cx: Scope)-> Element {
}
```
-This syntax even enables us to write a one-line component:
-```rust
-static App: Component = |cx| rsx!(cx, "hello world!");
-```
-
Alternatively, for match statements, we can just return the builder itself and pass it into a final, single call to `cx.render`:
```rust
diff --git a/docs/guide/src/hello_world.md b/docs/guide/src/hello_world.md
index fbe6153f5..e9eb92081 100644
--- a/docs/guide/src/hello_world.md
+++ b/docs/guide/src/hello_world.md
@@ -132,12 +132,6 @@ fn App(cx: Scope) -> Element {
}
```
-Writing `fn App(cx: Scope) -> Element {` might become tedious. Rust will also let you write functions as static closures, but these types of Components cannot have props that borrow data.
-
-```rust
-static App: Component = |cx| cx.render(rsx!(div { "Hello, world!" }));
-```
-
### What is this `Scope` object?
Coming from React, the `Scope` object might be confusing. In React, you'll want to store data between renders with hooks. However, hooks rely on global variables which make them difficult to integrate in multi-tenant systems like server-rendering.
diff --git a/examples/README.md b/examples/README.md
index 2e4959e7f..e108c56cd 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -115,7 +115,7 @@ fn App(cx: Scope) -> Element {
Tiny components:
```rust
-static App: Component = |cx, _| rsx!(cx, div {"hello world!"});
+static App: Component = |cx| rsx!(cx, div {"hello world!"});
```
Borrowed prop contents:
diff --git a/examples/async.rs b/examples/async.rs
index 4d292779e..dcd3b2757 100644
--- a/examples/async.rs
+++ b/examples/async.rs
@@ -17,27 +17,32 @@ fn app(cx: Scope) -> Element {
let (async_count, dir) = (count.for_async(), *direction);
- let task = use_coroutine(&cx, move || async move {
- loop {
- TimeoutFuture::new(250).await;
- *async_count.modify() += dir;
+ let task = use_coroutine(&cx, move || {
+ //
+ async move {
+ loop {
+ TimeoutFuture::new(250).await;
+ // *async_count.modify() += dir;
+ }
}
});
- rsx!(cx, div {
- h1 {"count is {count}"}
- button { onclick: move |_| task.stop(),
- "Stop counting"
- }
- button { onclick: move |_| task.resume(),
- "Start counting"
- }
- button {
- onclick: move |_| {
- *direction.modify() *= -1;
- task.restart();
- },
- "Switch counting direcion"
+ cx.render(rsx! {
+ div {
+ h1 {"count is {count}"}
+ button { onclick: move |_| task.stop(),
+ "Stop counting"
+ }
+ button { onclick: move |_| task.resume(),
+ "Start counting"
+ }
+ button {
+ onclick: move |_| {
+ *direction.modify() *= -1;
+ task.restart();
+ },
+ "Switch counting direcion"
+ }
}
})
}
diff --git a/examples/borrowed.rs b/examples/borrowed.rs
index 82920029b..63191bc97 100644
--- a/examples/borrowed.rs
+++ b/examples/borrowed.rs
@@ -17,10 +17,10 @@ and is proven to be safe with MIRI.
use dioxus::prelude::*;
fn main() {
- dioxus::desktop::launch(App);
+ dioxus::desktop::launch(app);
}
-fn App(cx: Scope) -> Element {
+fn app(cx: Scope) -> Element {
let text = cx.use_hook(|_| vec![String::from("abc=def")]);
let first = text.get_mut(0).unwrap();
diff --git a/examples/calculator.rs b/examples/calculator.rs
index c09f1b2aa..e43323a21 100644
--- a/examples/calculator.rs
+++ b/examples/calculator.rs
@@ -12,7 +12,7 @@ use dioxus::prelude::*;
use separator::Separatable;
fn main() {
- // dioxus::desktop::launch(app);
+ dioxus::desktop::launch(app);
}
fn app(cx: Scope) -> Element {
diff --git a/examples/core/alternative.rs b/examples/core/alternative.rs
deleted file mode 100644
index 219c603ee..000000000
--- a/examples/core/alternative.rs
+++ /dev/null
@@ -1,33 +0,0 @@
-use dioxus_core::prelude::*;
-use dioxus_core_macro::format_args_f;
-use dioxus_core_macro::rsx;
-use dioxus_html as dioxus_elements;
-
-fn main() {
- let mut dom = VirtualDom::new(EXAMPLE);
- dom.rebuild();
- println!("{}", dom);
-}
-
-pub static EXAMPLE: Component = |cx| {
- let list = (0..10).map(|_f| {
- rsx! {
- "{_f}"
- }
- });
-
- cx.render(Some(LazyNodes::new(move |cx| {
- cx.raw_element(
- "div",
- None,
- [],
- [],
- [
- cx.text(format_args!("hello")),
- cx.text(format_args!("hello")),
- cx.fragment_from_iter(list),
- ],
- None,
- )
- })))
-};
diff --git a/examples/core/html.rs b/examples/core/html.rs
deleted file mode 100644
index 96ce94986..000000000
--- a/examples/core/html.rs
+++ /dev/null
@@ -1,108 +0,0 @@
-use bumpalo::Bump;
-
-fn main() {}
-
-fn build(factory: Factory) {
- factory.text();
- div::new(factory)
- .r#class()
- .r#tag()
- .r#type()
- .add_children()
- .iter_children()
- .finish();
-}
-
-/// # The `div` element
-///
-///
-/// The
HTML element is the generic container for flow content. It has no effect on the content or layout until
-/// styled in some way using CSS (e.g. styling is directly applied to it, or some kind of layout model like Flexbox is
-/// applied to its parent element).
-///
-/// As a "pure" container, the
element does not inherently represent anything. Instead, it's used to group content
-/// so it can be easily styled using the class or id attributes, marking a section of a document as being written in a
-/// different language (using the lang attribute), and so on.
-///
-/// ## Usage
-/// ```
-/// rsx!{
-/// div { class: "tall", id: "unique id"
-/// h1 {}
-/// p {}
-/// }
-/// }
-///
-/// ```
-///
-/// ## Specifications
-/// - Content categories: Flow content, palpable content.
-/// - Permitted content: Flow content.
-/// - Permitted parents: Any element that accepts flow content.
-#[allow(non_camel_case_types)]
-struct div {}
-
-struct h1 {}
-
-struct h2 {}
-
-trait BasicElement: Sized {
- const TagName: &'static str;
- fn get_bump(&self) -> &Bump;
- fn new(factory: Factory) -> Self;
- fn add_children(self) -> Self {
- self
- }
- fn iter_children(self) -> Self {
- self
- }
- fn finish(self) -> Self {
- self
- }
-}
-
-impl BasicElement for div {
- const TagName: &'static str = "div";
-
- fn get_bump(&self) -> &Bump {
- todo!()
- }
-
- fn new(_factory: Factory) -> Self {
- todo!()
- }
-}
-
-impl div {
- fn class(self) -> Self {
- self
- }
- fn tag(self) -> Self {
- self
- }
- fn r#type(self) -> Self {
- self
- }
-}
-
-#[derive(Clone, Copy)]
-struct Factory<'a> {
- bump: &'a bumpalo::Bump,
-}
-
-impl<'a> Factory<'a> {
- fn text(&self) -> &str {
- todo!()
- }
- fn new_el(&'a self, tag: &'static str) -> ElementBuilder<'a> {
- ElementBuilder {
- bump: self.bump,
- tag,
- }
- }
-}
-
-struct ElementBuilder<'a> {
- tag: &'static str,
- bump: &'a bumpalo::Bump,
-}
diff --git a/examples/core/jsframework.rs b/examples/core/jsframework.rs
deleted file mode 100644
index f9fa9c431..000000000
--- a/examples/core/jsframework.rs
+++ /dev/null
@@ -1,118 +0,0 @@
-#![allow(non_snake_case)]
-
-use dioxus::component::Scope;
-use dioxus::events::on::MouseEvent;
-use dioxus_core as dioxus;
-use dioxus_core::prelude::*;
-use dioxus_core_macro::*;
-use dioxus_html as dioxus_elements;
-use rand::prelude::*;
-use std::fmt::Display;
-
-fn main() {
- let mut dom = VirtualDom::new(App);
- let g = dom.rebuild();
- assert!(g.edits.len() > 1);
-}
-
-fn App((cx, props): Scope) -> Element {
- let mut rng = SmallRng::from_entropy();
- let rows = (0..10_000_usize).map(|f| {
- let label = Label::new(&mut rng);
- rsx! {
- Row {
- row_id: f,
- label: label
- }
- }
- });
- cx.render(rsx! {
- table {
- tbody {
- {rows}
- }
- }
- })
-}
-
-#[derive(PartialEq, Props)]
-struct RowProps {
- row_id: usize,
- label: Label,
-}
-
-fn Row<'a>((cx, props): Scope<'a, RowProps>) -> Element<'a> {
- let handler = move |evt: MouseEvent| {
- let g = evt.button;
- };
- cx.render(rsx! {
- tr {
- // td { class:"col-md-1", "{cx.props.row_id}" }
- // td { class:"col-md-1", onclick: move |_| { /* run onselect */ }
- // a { class: "lbl", "{cx.props.label}" }
- // }
- // td { class: "col-md-1"
- // a { class: "remove", onclick: {handler}
- // span { class: "glyphicon glyphicon-remove remove" aria_hidden: "true" }
- // }
- // }
- // td { class: "col-md-6" }
- }
- })
-}
-
-#[derive(PartialEq)]
-struct Label([&'static str; 3]);
-
-impl Label {
- fn new(rng: &mut SmallRng) -> Self {
- Label([
- ADJECTIVES.choose(rng).unwrap(),
- COLOURS.choose(rng).unwrap(),
- NOUNS.choose(rng).unwrap(),
- ])
- }
-}
-impl Display for Label {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(f, "{} {} {}", self.0[0], self.0[1], self.0[2])
- }
-}
-
-static ADJECTIVES: &[&str] = &[
- "pretty",
- "large",
- "big",
- "small",
- "tall",
- "short",
- "long",
- "handsome",
- "plain",
- "quaint",
- "clean",
- "elegant",
- "easy",
- "angry",
- "crazy",
- "helpful",
- "mushy",
- "odd",
- "unsightly",
- "adorable",
- "important",
- "inexpensive",
- "cheap",
- "expensive",
- "fancy",
-];
-
-static COLOURS: &[&str] = &[
- "red", "yellow", "blue", "green", "pink", "brown", "purple", "brown", "white", "black",
- "orange",
-];
-
-static NOUNS: &[&str] = &[
- "table", "chair", "house", "bbq", "desk", "car", "pony", "cookie", "sandwich", "burger",
- "pizza", "mouse", "keyboard",
-];
diff --git a/examples/core_reference/antipatterns.rs b/examples/core_reference/antipatterns.rs
index c031428bd..5814e7096 100644
--- a/examples/core_reference/antipatterns.rs
+++ b/examples/core_reference/antipatterns.rs
@@ -32,16 +32,20 @@ use dioxus::prelude::*;
struct NoKeysProps {
data: std::collections::HashMap,
}
-static AntipatternNoKeys: Component = |cx| {
+fn AntipatternNoKeys(cx: Scope) -> Element {
// WRONG: Make sure to add keys!
- rsx!(cx, ul {
- {cx.props.data.iter().map(|(k, v)| rsx!(li { "List item: {v}" }))}
+ cx.render(rsx! {
+ ul {
+ cx.props.data.iter().map(|(k, v)| rsx!(li { "List item: {v}" }))
+ }
});
// RIGHT: Like this:
- rsx!(cx, ul {
- {cx.props.data.iter().map(|(k, v)| rsx!(li { key: "{k}", "List item: {v}" }))}
+ cx.render(rsx! {
+ ul {
+ cx.props.data.iter().map(|(k, v)| rsx!(li { key: "{k}", "List item: {v}" }))
+ }
})
-};
+}
/// Antipattern: Deeply nested fragments
/// ------------------------------------
@@ -54,9 +58,9 @@ static AntipatternNoKeys: Component = |cx| {
///
/// Only Component and Fragment nodes are susceptible to this issue. Dioxus mitigates this with components by providing
/// an API for registering shared state without the ContextProvider pattern.
-static AntipatternNestedFragments: Component = |cx| {
+fn AntipatternNestedFragments(cx: Scope<()>) -> Element {
// Try to avoid heavily nesting fragments
- rsx!(cx,
+ cx.render(rsx!(
Fragment {
Fragment {
Fragment {
@@ -68,8 +72,8 @@ static AntipatternNestedFragments: Component = |cx| {
}
}
}
- )
-};
+ ))
+}
/// Antipattern: Using state after it's been updated
/// -----------------------------------------------
@@ -82,13 +86,13 @@ static AntipatternNestedFragments: Component = |cx| {
/// However, calling set_state will *not* update the current version of state in the component. This should be easy to
/// recognize from the function signature, but Dioxus will not update the "live" version of state. Calling `set_state`
/// merely places a new value in the queue and schedules the component for a future update.
-static AntipatternRelyingOnSetState: Component = |cx| {
+fn AntipatternRelyingOnSetState(cx: Scope) -> Element {
let (state, set_state) = use_state(&cx, || "Hello world").classic();
set_state("New state");
// This will return false! `state` will *still* be "Hello world"
assert!(state == &"New state");
todo!()
-};
+}
/// Antipattern: Capitalization
/// ---------------------------
@@ -120,16 +124,16 @@ static antipattern_component: Component = |cx| todo!();
struct MisuedHooksProps {
should_render_state: bool,
}
-static AntipatternMisusedHooks: Component = |cx| {
+fn AntipatternMisusedHooks(cx: Scope) -> Element {
if props.should_render_state {
// do not place a hook in the conditional!
// prefer to move it out of the conditional
let (state, set_state) = use_state(&cx, || "hello world").classic();
- rsx!(cx, div { "{state}" })
+ cx.render(rsx!(div { "{state}" }))
} else {
- rsx!(cx, div { "Not rendering state" })
+ cx.render(rsx!(div { "Not rendering state" }))
}
-};
+}
/// Antipattern: Downcasting refs and panicking
/// ------------------------------------------
@@ -173,9 +177,9 @@ static _example: Component = |cx| todo!();
/// libraries.
static __example: Component = |cx| todo!();
-pub static Example: Component = |cx| {
+fn Example(cx: Scope) -> Element {
cx.render(rsx! {
AntipatternNoKeys { data: std::collections::HashMap::new() }
AntipatternNestedFragments {}
})
-};
+}
diff --git a/examples/core_reference/basics.rs b/examples/core_reference/basics.rs
index 3ba64e01a..4c076a3a2 100644
--- a/examples/core_reference/basics.rs
+++ b/examples/core_reference/basics.rs
@@ -9,7 +9,7 @@
use dioxus::prelude::*;
-pub static Example: Component = |cx| {
+pub fn Example(cx: Scope) -> Element {
cx.render(rsx! {
div {
Greeting {
@@ -18,20 +18,21 @@ pub static Example: Component = |cx| {
}
}
})
-};
-
-#[derive(PartialEq, Props)]
-struct GreetingProps {
- name: &'static str,
}
-static Greeting: Component = |cx| {
+#[derive(PartialEq, Props)]
+struct GreetingProps<'a> {
+ name: &'static str,
+ children: Element<'a>,
+}
+
+pub fn Greeting<'a>(cx: Scope<'a, GreetingProps<'a>>) -> Element {
cx.render(rsx! {
div {
h1 { "Hello, {cx.props.name}!" }
p { "Welcome to the Dioxus framework" }
br {}
- {cx.children()}
+ &cx.props.children
}
})
-};
+}
diff --git a/examples/core_reference/children.rs b/examples/core_reference/children.rs
index 8190f53d4..1b922b322 100644
--- a/examples/core_reference/children.rs
+++ b/examples/core_reference/children.rs
@@ -18,7 +18,7 @@
use dioxus::prelude::*;
-pub static Example: Component = |cx| {
+fn Example(cx: Scope) -> Element {
cx.render(rsx! {
div {
Banner {
@@ -29,16 +29,19 @@ pub static Example: Component = |cx| {
}
}
})
-};
+}
-pub static Banner: Component = |cx| {
+#[derive(Props)]
+struct BannerProps {
+ children: Element<'a>,
+}
+
+fn Banner(cx: Scope) -> Element {
cx.render(rsx! {
div {
h1 { "This is a great banner!" }
- div { class: "content"
- {cx.children()}
- }
+ div { class: "content", &cx.props.children }
footer { "Wow, what a great footer" }
}
})
-};
+}
diff --git a/examples/core_reference/conditional_rendering.rs b/examples/core_reference/conditional_rendering.rs
index 9d15a735a..8b7f4baaa 100644
--- a/examples/core_reference/conditional_rendering.rs
+++ b/examples/core_reference/conditional_rendering.rs
@@ -16,15 +16,15 @@ use dioxus::prelude::*;
pub struct MyProps {
should_show: bool,
}
-pub static Example0: Component = |cx| {
+pub fn Example0(cx: Scope) -> Element {
cx.render(rsx! {
div {
- {cx.props.should_show.then(|| rsx!{
+ cx.props.should_show.then(|| rsx!{
h1 { "showing the title!" }
- })}
+ })
}
})
-};
+}
// Convert a boolean conditional into an either/or
// Because rsx! is lazy (produces a closure), we cannot use it in two branch arms. To use it in matching/branching, we
@@ -39,31 +39,29 @@ pub static Example0: Component = |cx| {
pub struct MyProps1 {
should_show: bool,
}
-pub static Example1: Component = |cx| {
+pub fn Example1(cx: Scope) -> Element {
cx.render(rsx! {
div {
// With matching
- {match props.should_show {
+ match props.should_show {
true => cx.render(rsx!(div {"it is true!"})),
false => rsx!(cx, div {"it is false!"}),
- }}
+ },
// or with just regular conditions
- {if props.should_show {
+ if props.should_show {
rsx!(cx, div {"it is true!"})
} else {
rsx!(cx, div {"it is false!"})
- }}
+ },
// or with optional chaining
- {
- props.should_show
+ props.should_show
.then(|| rsx!(cx, div {"it is true!"}))
.unwrap_or_else(|| rsx!(cx, div {"it is false!"}))
- }
}
})
-};
+}
/// Matching can be expanded
@@ -77,19 +75,19 @@ pub enum Color {
pub struct MyProps2 {
color: Color,
}
-pub static Example2: Component = |cx| {
+pub fn Example2(cx: Scope) -> Element {
cx.render(rsx! {
div {
- {match props.color {
+ match props.color {
Color::Green => rsx!(cx, div {"it is Green!"}),
Color::Yellow => rsx!(cx, div {"it is Yellow!"}),
Color::Red => rsx!(cx, div {"it is Red!"}),
- }}
+ }
}
})
-};
+}
-pub static Example: Component = |cx| {
+pub fn Example(cx: Scope<()>) -> Element {
let should_show = use_state(&cx, || false);
let mut color_index = use_state(&cx, || 0);
let color = match *color_index % 2 {
@@ -101,11 +99,11 @@ pub static Example: Component = |cx| {
cx.render(rsx! {
div {
button {
- onclick: move |_| should_show.set(!*should_show)
+ onclick: move |_| should_show.set(!*should_show),
"click to toggle showing the examples"
}
button {
- onclick: move |_| color_index += 1
+ onclick: move |_| color_index += 1,
"click to for the enxt color"
}
Example0 { should_show: *should_show }
@@ -113,4 +111,4 @@ pub static Example: Component = |cx| {
Example2 { color: color }
}
})
-};
+}
diff --git a/examples/core_reference/controlled_inputs.rs b/examples/core_reference/controlled_inputs.rs
index eccf46064..8f3292e22 100644
--- a/examples/core_reference/controlled_inputs.rs
+++ b/examples/core_reference/controlled_inputs.rs
@@ -1,16 +1,16 @@
use dioxus::prelude::*;
fn main() {}
-pub static Example: Component = |cx| {
+pub fn Example(cx: Scope) -> Element {
cx.render(rsx! {
div {
}
})
-};
+}
// A controlled component:
-static ControlledSelect: Component = |cx| {
+pub fn ControlledSelect(cx: Scope) -> Element {
let value = use_state(&cx, || String::from("Grapefruit"));
cx.render(rsx! {
select { value: "{value}", onchange: move |evt| value.set(evt.value()),
@@ -20,10 +20,10 @@ static ControlledSelect: Component = |cx| {
option { value: "Mango", "Mango"}
}
})
-};
+}
// TODO - how do uncontrolled things work?
-static UncontrolledSelect: Component = |cx| {
+pub fn UncontrolledSelect(cx: Scope) -> Element {
let value = use_state(&cx, || String::new());
cx.render(rsx! {
@@ -34,4 +34,4 @@ static UncontrolledSelect: Component = |cx| {
option { value: "Mango", "Mango"}
}
})
-};
+}
diff --git a/examples/core_reference/custom_elements.rs b/examples/core_reference/custom_elements.rs
index 0830ed01a..6c7367d20 100644
--- a/examples/core_reference/custom_elements.rs
+++ b/examples/core_reference/custom_elements.rs
@@ -11,7 +11,7 @@
use dioxus::prelude::*;
-pub static Example: Component = |cx| {
+pub fn Example(cx: Scope) -> Element {
cx.render(rsx! {
div {
custom_element {
@@ -19,7 +19,7 @@ pub static Example: Component = |cx| {
}
}
})
-};
+}
mod dioxus_elements {
use std::fmt::Arguments;
diff --git a/examples/core_reference/empty.rs b/examples/core_reference/empty.rs
index 78adce251..e773901e9 100644
--- a/examples/core_reference/empty.rs
+++ b/examples/core_reference/empty.rs
@@ -5,4 +5,6 @@
use dioxus::prelude::*;
-pub static Example: Component = |cx| cx.render(rsx! { Fragment {} });
+pub fn Example(cx: Scope) -> Element {
+ None
+}
diff --git a/examples/core_reference/errorhandling.rs b/examples/core_reference/errorhandling.rs
index 3a2875bf6..72b7d5468 100644
--- a/examples/core_reference/errorhandling.rs
+++ b/examples/core_reference/errorhandling.rs
@@ -23,20 +23,20 @@ fn main() {}
/// This is one way to go about error handling (just toss things away with unwrap).
/// However, if you get it wrong, the whole app will crash.
/// This is pretty flimsy.
-static App: Component = |cx| {
+pub fn App(cx: Scope) -> Element {
let data = get_data().unwrap();
cx.render(rsx!( div { "{data}" } ))
-};
+}
/// This is a pretty verbose way of error handling
/// However, it's still pretty good since we don't panic, just fail to render anything
-static App1: Component = |cx| {
+pub fn App1(cx: Scope) -> Element {
let data = match get_data() {
Some(data) => data,
None => return None,
};
cx.render(rsx!( div { "{data}" } ))
-};
+}
/// This is an even better form of error handling.
/// However, it _does_ make the component go blank, which might not be desirable.
@@ -46,33 +46,37 @@ static App1: Component = |cx| {
/// a user is logged in.
///
/// Dioxus will throw an error in the console if the None-path is ever taken.
-static App2: Component = |cx| {
+pub fn App2(cx: Scope) -> Element {
let data = get_data()?;
cx.render(rsx!( div { "{data}" } ))
-};
+}
/// This is top-tier error handling since it displays a failure state.
///
/// However, the error is lacking in context.
-static App3: Component = |cx| match get_data() {
- Some(data) => cx.render(rsx!( div { "{data}" } )),
- None => cx.render(rsx!( div { "Failed to load data :(" } )),
-};
+pub fn App3(cx: Scope) -> Element {
+ match get_data() {
+ Some(data) => cx.render(rsx!( div { "{data}" } )),
+ None => cx.render(rsx!( div { "Failed to load data :(" } )),
+ }
+}
/// For errors that return results, it's possible to short-circuit the match-based error handling with `.ok()` which converts
/// a Result into an Option and lets you abort rendering by early-returning `None`
-static App4: Component = |cx| {
+pub fn App4(cx: Scope) -> Element {
let data = get_data_err().ok()?;
cx.render(rsx!( div { "{data}" } ))
-};
+}
/// This is great error handling since it displays a failure state... with context!
///
/// Hopefully you'll never need to display a screen like this. It's rather bad taste
-static App5: Component = |cx| match get_data_err() {
- Ok(data) => cx.render(rsx!( div { "{data}" } )),
- Err(c) => cx.render(rsx!( div { "Failed to load data: {c}" } )),
-};
+pub fn App5(cx: Scope) -> Element {
+ match get_data_err() {
+ Ok(data) => cx.render(rsx!( div { "{data}" } )),
+ Err(c) => cx.render(rsx!( div { "Failed to load data: {c}" } )),
+ }
+}
// this fetching function produces "nothing"
fn get_data() -> Option {
diff --git a/examples/core_reference/fragments.rs b/examples/core_reference/fragments.rs
index 2a5b87373..85523de31 100644
--- a/examples/core_reference/fragments.rs
+++ b/examples/core_reference/fragments.rs
@@ -10,17 +10,25 @@
use dioxus::prelude::*;
+pub fn Example(cx: Scope) -> Element {
+ cx.render(rsx! {
+ App1 {}
+ App2 {}
+ App3 {}
+ })
+}
+
// Returning multiple elements with rsx! or html!
-static App1: Component = |cx| {
+pub fn App1(cx: Scope) -> Element {
cx.render(rsx! {
h1 { }
h2 { }
h3 { }
})
-};
+}
// Using the Fragment component
-static App2: Component = |cx| {
+pub fn App2(cx: Scope) -> Element {
cx.render(rsx! {
Fragment {
div {}
@@ -28,10 +36,10 @@ static App2: Component = |cx| {
"asd"
}
})
-};
+}
// Using the `fragment` method on the NodeFactory
-static App3: Component = |cx| {
+pub fn App3(cx: Scope) -> Element {
cx.render(LazyNodes::new(move |fac| {
fac.fragment_from_iter([
fac.text(format_args!("A")),
@@ -40,12 +48,4 @@ static App3: Component = |cx| {
fac.text(format_args!("B")),
])
}))
-};
-
-pub static Example: Component = |cx| {
- cx.render(rsx! {
- App1 {}
- App2 {}
- App3 {}
- })
-};
+}
diff --git a/examples/core_reference/global_css.rs b/examples/core_reference/global_css.rs
index a7b201d69..cd007b42f 100644
--- a/examples/core_reference/global_css.rs
+++ b/examples/core_reference/global_css.rs
@@ -19,7 +19,7 @@ h1 {color: blue;}
p {color: red;}
"#;
-pub static Example: Component = |cx| {
+pub fn Example(cx: Scope) -> Element {
cx.render(rsx! {
head { style { "{STYLE}" } }
body {
@@ -27,4 +27,4 @@ pub static Example: Component = |cx| {
p {"This is a paragraph"}
}
})
-};
+}
diff --git a/examples/core_reference/inline_styles.rs b/examples/core_reference/inline_styles.rs
index d3459bcfd..35816e760 100644
--- a/examples/core_reference/inline_styles.rs
+++ b/examples/core_reference/inline_styles.rs
@@ -10,7 +10,7 @@
use dioxus::prelude::*;
-pub static Example: Component = |cx| {
+pub fn Example(cx: Scope) -> Element {
cx.render(rsx! {
head {
style: { background_color: "powderblue" }
@@ -24,15 +24,15 @@ pub static Example: Component = |cx| {
}
}
})
-};
+}
// .... technically the rsx! macro is slightly broken at the moment and allows styles not wrapped in style {}
// I haven't noticed any name collisions yet, and am tentatively leaving this behavior in..
// Don't rely on it.
-static Example2: Component = |cx| {
+pub fn Example2(cx: Scope) -> Element {
cx.render(rsx! {
div { color: "red"
"hello world!"
}
})
-};
+}
diff --git a/examples/core_reference/iterators.rs b/examples/core_reference/iterators.rs
index ad376a1bd..12922344d 100644
--- a/examples/core_reference/iterators.rs
+++ b/examples/core_reference/iterators.rs
@@ -15,25 +15,20 @@ use dioxus::prelude::*;
pub static Example: Component = |cx| {
let example_data = use_state(&cx, || 0);
- let v = (0..10).map(|f| {
- rsx! {
- li { onclick: move |_| example_data.set(f)
- "ID: {f}"
- ul {
- (0..10).map(|k| rsx!{
- li {
- "Sub iterator: {f}.{k}"
- }
- })
- }
- }
- }
- });
-
cx.render(rsx! {
h3 {"Selected: {example_data}"}
ul {
- {v}
+ (0..10).map(|f| rsx! {
+ li {
+ onclick: move |_| example_data.set(f),
+ "ID: {f}"
+ ul {
+ (0..10).map(|k| rsx!{
+ li { "Sub iterator: {f}.{k}" }
+ })
+ }
+ }
+ })
}
})
};
diff --git a/examples/core_reference/listener.rs b/examples/core_reference/listener.rs
index b76e16046..ba87eece3 100644
--- a/examples/core_reference/listener.rs
+++ b/examples/core_reference/listener.rs
@@ -7,50 +7,49 @@
use dioxus::prelude::*;
-pub static Example: Component = |cx| {
+pub fn Example(cx: Scope) -> Element {
cx.render(rsx! {
ButtonList {}
NonUpdatingEvents {}
DisablePropagation {}
})
-};
+}
/// We can use `set_name` in multiple closures; the closures automatically *copy* the reference to set_name.
-static ButtonList: Component = |cx| {
+pub fn ButtonList(cx: Scope) -> Element {
let name = use_state(&cx, || "...?");
- let names = ["jack", "jill", "john", "jane"]
- .iter()
- .map(move |n| rsx!(button { onclick: move |_| name.set(n), "{n}" }));
-
cx.render(rsx!(
div {
h1 { "Hello, {name}" }
- {names}
+
+ ["jack", "jill", "john", "jane"]
+ .iter()
+ .map(move |n| rsx!(button { onclick: move |_| name.set(n), "{n}" }))
}
))
-};
+}
/// This shows how listeners may be without a visible change in the display.
/// Check the console.
-static NonUpdatingEvents: Component = |cx| {
- rsx!(cx, div {
- button {
- onclick: move |_| log::info!("Did not cause any updates!")
- "Click me to log!"
+pub fn NonUpdatingEvents(cx: Scope) -> Element {
+ cx.render(rsx! {
+ div {
+ button {
+ onclick: move |_| log::info!("Did not cause any updates!"),
+ "Click me to log!"
+ }
}
})
-};
+}
-static DisablePropagation: Component = |cx| {
- rsx!(cx,
+pub fn DisablePropagation(cx: Scope) -> Element {
+ cx.render(rsx! {
div {
onclick: move |_| log::info!("event propagated to the div!")
button {
- onclick: move |evt| {
- log::info!("Button will allow propagation");
- }
+ onclick: move |evt| log::info!("Button will allow propagation"),
}
}
- )
-};
+ })
+}
diff --git a/examples/core_reference/memo.rs b/examples/core_reference/memo.rs
index 061646a52..0c2e0a156 100644
--- a/examples/core_reference/memo.rs
+++ b/examples/core_reference/memo.rs
@@ -21,11 +21,11 @@ use dioxus::prelude::*;
// By default, components with no props are always memoized.
// A props of () is considered empty.
-pub static Example: Component = |cx| {
+pub fn Example(cx: Scope) -> Element {
cx.render(rsx! {
div { "100% memoized!" }
})
-};
+}
// These props do not borrow any content, and therefore can be safely memoized.
// However, the parent *must* create a new string on every render.
@@ -35,11 +35,11 @@ pub struct MyProps1 {
name: String,
}
-pub static Example1: Component = |cx| {
+pub fn Example1(cx: Scope) -> Element {
cx.render(rsx! {
div { "100% memoized! {cx.props.name}" }
})
-};
+}
// These props do not borrow any content, and therefore can be safely memoized.
// In contrast with the `String` example, these props use `Rc` which operates similar to strings in JavaScript.
@@ -49,11 +49,11 @@ pub struct MyProps2 {
name: std::rc::Rc,
}
-pub static Example2: Component = |cx| {
+pub fn Example2(cx: Scope) -> Element {
cx.render(rsx! {
div { "100% memoized! {cx.props.name}" }
})
-};
+}
// These props *do* borrow any content, and therefore cannot be safely memoized!.
#[derive(PartialEq, Props)]
@@ -61,7 +61,7 @@ pub struct MyProps3<'a> {
name: &'a str,
}
// We need to manually specify a lifetime that ensures props and scope (the component's state) share the same lifetime.
-// Using the `pub static Example: Component` pattern _will_ specify a lifetime, but that lifetime will be static which might
+// Using the `pub fn Example(cx: Scope): Component` pattern _will_ specify a lifetime, but that lifetime will be static which might
// not exactly be what you want
fn Example3(cx: Scope<'a, MyProps3<'a>>) -> DomTree {
cx.render(rsx! {
diff --git a/examples/core_reference/noderefs.rs b/examples/core_reference/noderefs.rs
deleted file mode 100644
index a3d7053d4..000000000
--- a/examples/core_reference/noderefs.rs
+++ /dev/null
@@ -1,12 +0,0 @@
-use dioxus::prelude::*;
-fn main() {}
-
-pub static Example: Component = |cx| {
- let p = 10;
-
- cx.render(rsx! {
- div {
-
- }
- })
-};
diff --git a/examples/core_reference/signals.rs b/examples/core_reference/signals.rs
deleted file mode 100644
index d15e3f70a..000000000
--- a/examples/core_reference/signals.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-use dioxus::prelude::*;
-fn main() {}
-
-pub static Example: Component = |cx| {
- cx.render(rsx! {
- div {
-
- }
- })
-};
diff --git a/examples/core_reference/spreadpattern.rs b/examples/core_reference/spreadpattern.rs
index c24d008f2..34a467f90 100644
--- a/examples/core_reference/spreadpattern.rs
+++ b/examples/core_reference/spreadpattern.rs
@@ -9,7 +9,7 @@
use dioxus::prelude::*;
-pub static Example: Component = |cx| {
+pub fn Example(cx: Scope) -> Element {
let props = MyProps {
count: 0,
live: true,
@@ -18,22 +18,24 @@ pub static Example: Component = |cx| {
cx.render(rsx! {
Example1 { ..props, count: 10, div {"child"} }
})
-};
+}
-#[derive(PartialEq, Props)]
-pub struct MyProps {
+#[derive(Props)]
+pub struct MyProps<'a> {
count: u32,
live: bool,
name: &'static str,
+ children: Element<'a>,
}
-pub static Example1: Component = |cx, MyProps { count, live, name }| {
+pub fn Example1(cx: Scope) -> Element {
+ let MyProps { count, live, name } = cx.props;
cx.render(rsx! {
div {
h1 { "Hello, {name}"}
h3 {"Are we alive? {live}"}
p {"Count is {count}"}
- { cx.children() }
+ &cx.props.children
}
})
-};
+}
diff --git a/examples/core_reference/statemanagement.rs b/examples/core_reference/statemanagement.rs
deleted file mode 100644
index d15e3f70a..000000000
--- a/examples/core_reference/statemanagement.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-use dioxus::prelude::*;
-fn main() {}
-
-pub static Example: Component = |cx| {
- cx.render(rsx! {
- div {
-
- }
- })
-};
diff --git a/examples/core_reference/suspense.rs b/examples/core_reference/suspense.rs
index e624595fa..ea1f92b96 100644
--- a/examples/core_reference/suspense.rs
+++ b/examples/core_reference/suspense.rs
@@ -14,7 +14,7 @@ struct DogApi {
}
const ENDPOINT: &str = "https://dog.ceo/api/breeds/image/random";
-pub static Example: Component = |cx| {
+pub fn Example(cx: Scope) -> Element {
let doggo = use_suspense(
cx,
|| surf::get(ENDPOINT).recv_json::(),
@@ -35,4 +35,4 @@ pub static Example: Component = |cx| {
{doggo}
}
))
-};
+}
diff --git a/examples/core_reference/task.rs b/examples/core_reference/task.rs
index d213917c4..25f634517 100644
--- a/examples/core_reference/task.rs
+++ b/examples/core_reference/task.rs
@@ -42,19 +42,19 @@ pub static Example: Component = |cx| {
div {
h1 {"count is {count}"}
button {
+ onclick: move |_| task.stop(),
"Stop counting"
- onclick: move |_| task.stop()
}
button {
+ onclick: move |_| task.resume(),
"Start counting"
- onclick: move |_| task.resume()
}
button {
- "Switch counting direcion"
onclick: move |_| {
direction *= -1;
task.restart();
}
+ "Switch counting direcion"
}
}
})
diff --git a/examples/core_reference/testing.rs b/examples/core_reference/testing.rs
deleted file mode 100644
index aaef5fb7f..000000000
--- a/examples/core_reference/testing.rs
+++ /dev/null
@@ -1,9 +0,0 @@
-use dioxus::prelude::*;
-
-pub static Example: Component = |cx| {
- cx.render(rsx! {
- div {
-
- }
- })
-};
diff --git a/examples/core_reference/tostring.rs b/examples/core_reference/tostring.rs
deleted file mode 100644
index 9c8563935..000000000
--- a/examples/core_reference/tostring.rs
+++ /dev/null
@@ -1,25 +0,0 @@
-use dioxus::prelude::*;
-use dioxus::ssr;
-
-pub static Example: Component = |cx| {
- let as_string = use_state(&cx, || {
- // Currently, SSR is only supported for whole VirtualDOMs
- // This is an easy/low hanging fruit to improve upon
- let mut dom = VirtualDom::new(SomeApp);
- dom.rebuild();
- ssr::render_vdom(&dom)
- });
-
- cx.render(rsx! {
- div { "{as_string}" }
- })
-};
-
-static SomeApp: Component = |cx| {
- cx.render(rsx! {
- div { style: {background_color: "blue"}
- h1 {"Some amazing app or component"}
- p {"Things are great"}
- }
- })
-};
diff --git a/examples/coroutine.rs b/examples/coroutine.rs
index ee957bd34..8ef6c2bfb 100644
--- a/examples/coroutine.rs
+++ b/examples/coroutine.rs
@@ -22,12 +22,12 @@
//! the coroutine was initiated. `use_state` always returns the same setter, so you don't need to worry about
fn main() {
- dioxus::desktop::launch(App);
+ dioxus::desktop::launch(app);
}
use dioxus::prelude::*;
-static App: Component = |cx| {
+fn app(cx: Scope) -> Element {
let p1 = use_state(&cx, || 0);
let p2 = use_state(&cx, || 0);
@@ -53,7 +53,7 @@ static App: Component = |cx| {
Horsey { pos: *p2, "horsey 2" }
}
})
-};
+}
#[derive(Props)]
struct HorseyProps<'a> {
diff --git a/examples/crm.rs b/examples/crm.rs
index 11acb4346..b72fe541d 100644
--- a/examples/crm.rs
+++ b/examples/crm.rs
@@ -20,12 +20,11 @@ pub struct Client {
}
fn app(cx: Scope) -> Element {
- let clients = use_ref(&cx, || vec![] as Vec);
-
let scene = use_state(&cx, || Scene::ClientsList);
let firstname = use_state(&cx, String::new);
let lastname = use_state(&cx, String::new);
let description = use_state(&cx, String::new);
+ let clients = use_ref(&cx, || vec![] as Vec);
cx.render(rsx!(
body {
diff --git a/examples/desktop/core.rs b/examples/desktop/core.rs
deleted file mode 100644
index de3e04b5d..000000000
--- a/examples/desktop/core.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-use dioxus_core::prelude::*;
-use dioxus_core_macro::*;
-use dioxus_html as dioxus_elements;
-
-fn main() {}
diff --git a/examples/desktop/crm.rs b/examples/desktop/crm.rs
deleted file mode 100644
index 5c3d50228..000000000
--- a/examples/desktop/crm.rs
+++ /dev/null
@@ -1,109 +0,0 @@
-use dioxus_core as dioxus;
-use dioxus_core::prelude::*;
-use dioxus_core_macro::*;
-use dioxus_hooks::*;
-use dioxus_html as dioxus_elements;
-
-fn main() {
- dioxus_desktop::launch(App)
-}
-
-enum Scene {
- ClientsList,
- NewClientForm,
- Settings,
-}
-
-#[derive(Clone, Debug, Default)]
-pub struct Client {
- pub first_name: String,
- pub last_name: String,
- pub description: String,
-}
-
-static App: Component = |cx| {
- let mut scene = use_state(&cx, || Scene::ClientsList);
- let clients = use_ref(&cx, || vec![] as Vec);
-
- let mut firstname = use_state(&cx, String::new);
- let mut lastname = use_state(&cx, String::new);
- let mut description = use_state(&cx, String::new);
-
- let scene = match scene.get() {
- Scene::ClientsList => {
- rsx!(cx, div { class: "crm"
- h2 { "List of clients" margin_bottom: "10px" }
- div { class: "clients" margin_left: "10px"
- {clients.read().iter().map(|client| rsx!(
- div { class: "client" style: "margin-bottom: 50px"
- p { "First Name: {client.first_name}" }
- p { "Last Name: {client.last_name}" }
- p {"Description: {client.description}"}
- })
- )}
- }
- button { class: "pure-button pure-button-primary" onclick: move |_| scene.set(Scene::NewClientForm), "Add New" }
- button { class: "pure-button" onclick: move |_| scene.set(Scene::Settings), "Settings" }
- })
- }
- Scene::NewClientForm => {
- let add_new = move |_| {
- clients.write().push(Client {
- description: (*description).clone(),
- first_name: (*firstname).clone(),
- last_name: (*lastname).clone(),
- });
- description.set(String::new());
- firstname.set(String::new());
- lastname.set(String::new());
- };
- rsx!(cx, div { class: "crm"
- h2 {"Add new client" margin_bottom: "10px" }
- form { class: "pure-form"
- input { class: "new-client firstname" placeholder: "First name" value: "{firstname}"
- oninput: move |evt| firstname.set(evt.value)
- }
- input { class: "new-client lastname" placeholder: "Last name" value: "{lastname}"
- oninput: move |evt| lastname.set(evt.value)
- }
- textarea { class: "new-client description" placeholder: "Description" value: "{description}"
- oninput: move |evt| description.set(evt.value)
- }
- }
- button { class: "pure-button pure-button-primary", onclick: {add_new}, "Add New" }
- button { class: "pure-button", onclick: move |_| scene.set(Scene::ClientsList), "Go Back" }
- })
- }
- Scene::Settings => {
- rsx!(cx, div {
- h2 { "Settings" margin_bottom: "10px" }
- button {
- background: "rgb(202, 60, 60)"
- class: "pure-button pure-button-primary"
- onclick: move |_| {
- clients.write().clear();
- scene.set(Scene::ClientsList);
- },
- "Remove all clients"
- }
- button {
- class: "pure-button pure-button-primary"
- onclick: move |_| scene.set(Scene::ClientsList),
- "Go Back"
- }
- })
- }
- };
-
- rsx!(cx, body {
- link {
- rel: "stylesheet"
- href: "https://unpkg.com/purecss@2.0.6/build/pure-min.css"
- integrity: "sha384-Uu6IeWbM+gzNVXJcM9XV3SohHtmWE+3VGi496jvgX1jyvDTXfdK+rfZc8C1Aehk5"
- crossorigin: "anonymous"
- }
- margin_left: "35%"
- h1 {"Dioxus CRM Example"}
- {scene}
- })
-};
diff --git a/examples/desktop/demo.rs b/examples/desktop/demo.rs
deleted file mode 100644
index d8dc51697..000000000
--- a/examples/desktop/demo.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-use dioxus_core as dioxus;
-use dioxus_core::prelude::*;
-use dioxus_core_macro::*;
-
-use dioxus_html as dioxus_elements;
-
-fn main() {
- dioxus_desktop::launch(App);
-}
-
-static App: Component = |cx| {
- cx.render(rsx!(
- div {
- "hello world!"
- }
- (0..10).map(|f| rsx!( div {"abc {f}"}))
- ))
-};
diff --git a/examples/desktop/todomvc.css b/examples/desktop/todomvc.css
deleted file mode 100644
index ab9dbd7a9..000000000
--- a/examples/desktop/todomvc.css
+++ /dev/null
@@ -1,379 +0,0 @@
-html,
-body {
- margin: 0;
- padding: 0;
-}
-
-button {
- margin: 0;
- padding: 0;
- border: 0;
- background: none;
- font-size: 100%;
- vertical-align: baseline;
- font-family: inherit;
- font-weight: inherit;
- color: inherit;
- -webkit-appearance: none;
- appearance: none;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
-}
-
-body {
- font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
- line-height: 1.4em;
- background: #f5f5f5;
- color: #4d4d4d;
- min-width: 230px;
- max-width: 550px;
- margin: 0 auto;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
- font-weight: 300;
-}
-
-:focus {
- outline: 0;
-}
-
-.hidden {
- display: none;
-}
-
-.todoapp {
- background: #fff;
- margin: 130px 0 40px 0;
- position: relative;
- box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2),
- 0 25px 50px 0 rgba(0, 0, 0, 0.1);
-}
-
-.todoapp input::-webkit-input-placeholder {
- font-style: italic;
- font-weight: 300;
- color: #e6e6e6;
-}
-
-.todoapp input::-moz-placeholder {
- font-style: italic;
- font-weight: 300;
- color: #e6e6e6;
-}
-
-.todoapp input::input-placeholder {
- font-style: italic;
- font-weight: 300;
- color: #e6e6e6;
-}
-
-.todoapp h1 {
- position: absolute;
- top: -155px;
- width: 100%;
- font-size: 100px;
- font-weight: 100;
- text-align: center;
- color: rgba(175, 47, 47, 0.15);
- -webkit-text-rendering: optimizeLegibility;
- -moz-text-rendering: optimizeLegibility;
- text-rendering: optimizeLegibility;
-}
-
-.new-todo,
-.edit {
- position: relative;
- margin: 0;
- width: 100%;
- font-size: 24px;
- font-family: inherit;
- font-weight: inherit;
- line-height: 1.4em;
- border: 0;
- color: inherit;
- padding: 6px;
- border: 1px solid #999;
- box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
- box-sizing: border-box;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
-}
-
-.new-todo {
- padding: 16px 16px 16px 60px;
- border: none;
- background: rgba(0, 0, 0, 0.003);
- box-shadow: inset 0 -2px 1px rgba(0, 0, 0, 0.03);
-}
-
-.main {
- position: relative;
- z-index: 2;
- border-top: 1px solid #e6e6e6;
-}
-
-.toggle-all {
- text-align: center;
- border: none;
- /* Mobile Safari */
- opacity: 0;
- position: absolute;
-}
-
-.toggle-all+label {
- width: 60px;
- height: 34px;
- font-size: 0;
- position: absolute;
- top: -52px;
- left: -13px;
- -webkit-transform: rotate(90deg);
- transform: rotate(90deg);
-}
-
-.toggle-all+label:before {
- content: '❯';
- font-size: 22px;
- color: #e6e6e6;
- padding: 10px 27px 10px 27px;
-}
-
-.toggle-all:checked+label:before {
- color: #737373;
-}
-
-.todo-list {
- margin: 0;
- padding: 0;
- list-style: none;
-}
-
-.todo-list li {
- position: relative;
- font-size: 24px;
- border-bottom: 1px solid #ededed;
-}
-
-.todo-list li:last-child {
- border-bottom: none;
-}
-
-.todo-list li.editing {
- border-bottom: none;
- padding: 0;
-}
-
-.todo-list li.editing .edit {
- display: block;
- width: 506px;
- padding: 12px 16px;
- margin: 0 0 0 43px;
-}
-
-.todo-list li.editing .view {
- display: none;
-}
-
-.todo-list li .toggle {
- text-align: center;
- width: 40px;
- /* auto, since non-WebKit browsers doesn't support input styling */
- height: auto;
- position: absolute;
- top: 0;
- bottom: 0;
- margin: auto 0;
- border: none;
- /* Mobile Safari */
- -webkit-appearance: none;
- appearance: none;
-}
-
-.todo-list li .toggle {
- opacity: 0;
-}
-
-.todo-list li .toggle+label {
- /*
- Firefox requires `#` to be escaped - https://bugzilla.mozilla.org/show_bug.cgi?id=922433
- IE and Edge requires *everything* to be escaped to render, so we do that instead of just the `#` - https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7157459/
- */
- background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23ededed%22%20stroke-width%3D%223%22/%3E%3C/svg%3E');
- background-repeat: no-repeat;
- background-position: center left;
-}
-
-.todo-list li .toggle:checked+label {
- background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23bddad5%22%20stroke-width%3D%223%22/%3E%3Cpath%20fill%3D%22%235dc2af%22%20d%3D%22M72%2025L42%2071%2027%2056l-4%204%2020%2020%2034-52z%22/%3E%3C/svg%3E');
-}
-
-.todo-list li label {
- word-break: break-all;
- padding: 15px 15px 15px 60px;
- display: block;
- line-height: 1.2;
- transition: color 0.4s;
-}
-
-.todo-list li.completed label {
- color: #d9d9d9;
- text-decoration: line-through;
-}
-
-.todo-list li .destroy {
- display: none;
- position: absolute;
- top: 0;
- right: 10px;
- bottom: 0;
- width: 40px;
- height: 40px;
- margin: auto 0;
- font-size: 30px;
- color: #cc9a9a;
- margin-bottom: 11px;
- transition: color 0.2s ease-out;
-}
-
-.todo-list li .destroy:hover {
- color: #af5b5e;
-}
-
-.todo-list li .destroy:after {
- content: '×';
-}
-
-.todo-list li:hover .destroy {
- display: block;
-}
-
-.todo-list li .edit {
- display: none;
-}
-
-.todo-list li.editing:last-child {
- margin-bottom: -1px;
-}
-
-.footer {
- color: #777;
- padding: 10px 15px;
- height: 20px;
- text-align: center;
- border-top: 1px solid #e6e6e6;
-}
-
-.footer:before {
- content: '';
- position: absolute;
- right: 0;
- bottom: 0;
- left: 0;
- height: 50px;
- overflow: hidden;
- box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2),
- 0 8px 0 -3px #f6f6f6,
- 0 9px 1px -3px rgba(0, 0, 0, 0.2),
- 0 16px 0 -6px #f6f6f6,
- 0 17px 2px -6px rgba(0, 0, 0, 0.2);
-}
-
-.todo-count {
- float: left;
- text-align: left;
-}
-
-.todo-count strong {
- font-weight: 300;
-}
-
-.filters {
- margin: 0;
- padding: 0;
- list-style: none;
- position: absolute;
- right: 0;
- left: 0;
-}
-
-.filters li {
- display: inline;
-}
-
-.filters li a {
- color: inherit;
- margin: 3px;
- padding: 3px 7px;
- text-decoration: none;
- border: 1px solid transparent;
- border-radius: 3px;
-}
-
-.filters li a:hover {
- border-color: rgba(175, 47, 47, 0.1);
-}
-
-.filters li a.selected {
- border-color: rgba(175, 47, 47, 0.2);
-}
-
-.clear-completed,
-html .clear-completed:active {
- float: right;
- position: relative;
- line-height: 20px;
- text-decoration: none;
- cursor: pointer;
-}
-
-.clear-completed:hover {
- text-decoration: underline;
-}
-
-.info {
- margin: 65px auto 0;
- color: #bfbfbf;
- font-size: 10px;
- text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
- text-align: center;
-}
-
-.info p {
- line-height: 1;
-}
-
-.info a {
- color: inherit;
- text-decoration: none;
- font-weight: 400;
-}
-
-.info a:hover {
- text-decoration: underline;
-}
-
-/*
- Hack to remove background from Mobile Safari.
- Can't use it globally since it destroys checkboxes in Firefox
-*/
-@media screen and (-webkit-min-device-pixel-ratio:0) {
-
- .toggle-all,
- .todo-list li .toggle {
- background: none;
- }
-
- .todo-list li .toggle {
- height: 40px;
- }
-}
-
-@media (max-width: 430px) {
- .footer {
- height: 50px;
- }
-
- .filters {
- bottom: 10px;
- }
-}
diff --git a/examples/desktop/todomvc.rs b/examples/desktop/todomvc.rs
deleted file mode 100644
index 64bfb2876..000000000
--- a/examples/desktop/todomvc.rs
+++ /dev/null
@@ -1,170 +0,0 @@
-#![allow(non_upper_case_globals, non_snake_case)]
-
-use dioxus_core as dioxus;
-use dioxus_core::prelude::*;
-use dioxus_core_macro::*;
-use dioxus_hooks::*;
-use dioxus_html as dioxus_elements;
-
-use std::collections::HashMap;
-
-fn main() {
- dioxus_desktop::launch(App)
-}
-
-#[derive(PartialEq)]
-pub enum FilterState {
- All,
- Active,
- Completed,
-}
-
-#[derive(Debug, PartialEq)]
-pub struct TodoItem {
- pub id: u32,
- pub checked: bool,
- pub contents: String,
-}
-pub type Todos = HashMap;
-
-pub static App: Component = |cx| {
- // Share our TodoList to the todos themselves
- use_provide_state(cx, Todos::new);
-
- // Save state for the draft, filter
- let draft = use_state(&cx, || "".to_string());
- let filter = use_state(&cx, || FilterState::All);
- let mut todo_id = use_state(&cx, || 0);
-
- // Consume the todos
- let todos = use_shared_state::(cx)?;
-
- // Filter the todos based on the filter state
- let mut filtered_todos = todos
- .read()
- .iter()
- .filter(|(_, item)| match *filter {
- FilterState::All => true,
- FilterState::Active => !item.checked,
- FilterState::Completed => item.checked,
- })
- .map(|f| *f.0)
- .collect::>();
- filtered_todos.sort_unstable();
-
- // Define the actions to manage the todolist
- let mut submit_todo = move || {
- if !draft.is_empty() {
- todos.write().insert(
- *todo_id,
- TodoItem {
- id: *todo_id,
- checked: false,
- contents: draft.get().clone(),
- },
- );
- todo_id += 1;
- draft.set("".to_string());
- }
- };
- let clear_completed = move || {
- todos.write().retain(|_, todo| todo.checked == false);
- };
-
- // Some assists in actually rendering the content
- let show_clear_completed = todos.read().values().any(|todo| todo.checked);
- let items_left = filtered_todos.len();
- let item_text = match items_left {
- 1 => "item",
- _ => "items",
- };
-
- cx.render(rsx!{
- section { class: "todoapp"
- style { {[include_str!("./todomvc.css")]} }
- div {
- header { class: "header"
- h1 {"todos"}
- input {
- class: "new-todo"
- placeholder: "What needs to be done?"
- value: "{draft}"
- autofocus: "true"
- oninput: move |evt| draft.set(evt.value)
- onkeydown: move |evt| {
- if evt.key == "Enter" {
- submit_todo();
- }
- }
- }
- }
- ul { class: "todo-list",
- filtered_todos.iter().map(|id| rsx!(TodoEntry { key: "{id}", id: *id }))
- }
- (!todos.read().is_empty()).then(|| rsx!(
- footer { class: "footer",
- span { class: "todo-count" strong {"{items_left} "} span {"{item_text} left"} }
- ul { class: "filters"
- li { class: "All", a { onclick: move |_| filter.set(FilterState::All), "All" }}
- li { class: "Active", a { onclick: move |_| filter.set(FilterState::Active), "Active" }}
- li { class: "Completed", a { onclick: move |_| filter.set(FilterState::Completed), "Completed" }}
- }
- show_clear_completed.then(|| rsx!(
- button { class: "clear-completed", onclick: move |_| clear_completed(),
- "Clear completed"
- }
- ))
- }
- ))
- }
- }
- footer { class: "info"
- p {"Double-click to edit a todo"}
- p { "Created by ", a { "jkelleyrtp", href: "http://github.com/jkelleyrtp/" }}
- p { "Part of ", a { "TodoMVC", href: "http://todomvc.com" }}
- }
- })
-};
-
-#[derive(PartialEq, Props)]
-pub struct TodoEntryProps {
- id: u32,
-}
-
-pub fn TodoEntry((cx, props): Scope) -> Element {
- let todos = use_shared_state::(cx)?;
-
- let _todos = todos.read();
- let todo = _todos.get(&cx.props.id)?;
-
- let is_editing = use_state(&cx, || false);
- let completed = if todo.checked { "completed" } else { "" };
-
- cx.render(rsx!{
- li { class: "{completed}"
- div { class: "view"
- input { class: "toggle" r#type: "checkbox" id: "cbg-{todo.id}" checked: "{todo.checked}"
- onchange: move |evt| {
- if let Some(todo) = todos.write().get_mut(&cx.props.id) {
- todo.checked = evt.value.parse().unwrap()
- }
- }
- }
-
- label { r#for: "cbg-{todo.id}" pointer_events: "none"
- "{todo.contents}"
- }
-
- {is_editing.then(|| rsx!{
- input { value: "{todo.contents}"
- oninput: move |evt| {
- if let Some(todo) = todos.write().get_mut(&cx.props.id) {
- todo.contents = evt.value
- }
- },
- }
- })}
- }
- }
- })
-}
diff --git a/examples/framework_benchmark.rs b/examples/framework_benchmark.rs
index 73a82ea2c..f43ac4205 100644
--- a/examples/framework_benchmark.rs
+++ b/examples/framework_benchmark.rs
@@ -2,7 +2,7 @@ use dioxus::prelude::*;
use rand::prelude::*;
fn main() {
- dioxus::desktop::launch(App);
+ dioxus::desktop::launch(app);
}
#[derive(Clone, PartialEq)]
@@ -29,7 +29,7 @@ impl Label {
}
}
-static App: Component = |cx| {
+fn app(cx: Scope) -> Element {
let items = use_ref(&cx, || vec![]);
let selected = use_state(&cx, || None);
@@ -64,7 +64,7 @@ static App: Component = |cx| {
}
table {
tbody {
- {items.read().iter().enumerate().map(|(id, item)| {
+ items.read().iter().enumerate().map(|(id, item)| {
let is_in_danger = if (*selected).map(|s| s == id).unwrap_or(false) {"danger"} else {""};
rsx!(tr { class: "{is_in_danger}",
td { class:"col-md-1" }
@@ -79,13 +79,13 @@ static App: Component = |cx| {
}
td { class: "col-md-6" }
})
- })}
+ })
}
}
span { class: "preloadicon glyphicon glyphicon-remove", aria_hidden: "true" }
}
})
-};
+}
#[derive(Props)]
struct ActionButtonProps<'a> {
@@ -95,9 +95,17 @@ struct ActionButtonProps<'a> {
}
fn ActionButton<'a>(cx: Scope<'a, ActionButtonProps<'a>>) -> Element {
- rsx!(cx, div { class: "col-sm-6 smallpad",
- button { class:"btn btn-primary btn-block", r#type: "button", id: "{cx.props.id}", onclick: move |_| (cx.props.onclick)(),
- "{cx.props.name}"
+ cx.render(rsx! {
+ div {
+ class: "col-sm-6 smallpad",
+ button {
+ class:"btn btn-primary btn-block",
+ r#type: "button",
+ id: "{cx.props.id}",
+ onclick: move |_| (cx.props.onclick)(),
+
+ "{cx.props.name}"
+ }
}
})
}
diff --git a/examples/hydration.rs b/examples/hydration.rs
index e0a23b46b..6ec777352 100644
--- a/examples/hydration.rs
+++ b/examples/hydration.rs
@@ -13,12 +13,13 @@ use dioxus::prelude::*;
use dioxus::ssr;
fn main() {
- let vdom = VirtualDom::new(App);
+ let vdom = VirtualDom::new(app);
let content = ssr::render_vdom_cfg(&vdom, |f| f.pre_render(true));
- dioxus::desktop::launch_cfg(App, |c| c.with_prerendered(content));
+
+ dioxus::desktop::launch_cfg(app, |c| c.with_prerendered(content));
}
-static App: Component = |cx| {
+fn app(cx: Scope) -> Element {
let val = use_state(&cx, || 0);
cx.render(rsx! {
@@ -30,4 +31,4 @@ static App: Component = |cx| {
}
}
})
-};
+}
diff --git a/examples/inputs.rs b/examples/inputs.rs
index 18edc9e8f..ed11b48d9 100644
--- a/examples/inputs.rs
+++ b/examples/inputs.rs
@@ -7,7 +7,7 @@ use std::sync::Arc;
use dioxus::{events::FormEvent, prelude::*};
fn main() {
- dioxus::desktop::launch(App);
+ dioxus::desktop::launch(app);
}
const FIELDS: &[(&str, &str)] = &[
@@ -37,7 +37,7 @@ const FIELDS: &[(&str, &str)] = &[
("week", ""), // degrades to text most of the time
];
-static App: Component = |cx| {
+fn app(cx: Scope) -> Element {
cx.render(rsx! {
div { margin_left: "30px",
@@ -147,4 +147,4 @@ static App: Component = |cx| {
}
})
-};
+}
diff --git a/examples/manual_edits.rs b/examples/manual_edits.rs
index 5b0d0de4e..65690424e 100644
--- a/examples/manual_edits.rs
+++ b/examples/manual_edits.rs
@@ -33,7 +33,7 @@ fn main() {
AppendChildren { many: 1 },
];
- let app: Component = |cx| rsx!(cx, div { "some app" });
+ let app: Component = |cx| cx.render(rsx!(div { "some app" }));
dioxus_desktop::run(app, (), |c| c.with_edits(edits));
}
diff --git a/examples/pattern_model.rs b/examples/pattern_model.rs
index d9d2eb47e..0d5f07b81 100644
--- a/examples/pattern_model.rs
+++ b/examples/pattern_model.rs
@@ -21,7 +21,6 @@ use dioxus::desktop::wry::application::dpi::LogicalSize;
use dioxus::events::*;
use dioxus::prelude::*;
-const STYLE: &str = include_str!("./assets/calculator.css");
fn main() {
env_logger::init();
dioxus::desktop::launch_cfg(app, |cfg| {
@@ -40,32 +39,36 @@ fn app(cx: Scope) -> Element {
let clear_text = if clear_display { "C" } else { "AC" };
let formatted = state.read().formatted_display();
- rsx!(cx, div { id: "wrapper",
- div { class: "app", style { "{STYLE}" }
- div { class: "calculator", onkeypress: move |evt| state.write().handle_keydown(evt),
- div { class: "calculator-display", "{formatted}"}
- div { class: "calculator-keypad",
- div { class: "input-keys",
- div { class: "function-keys",
- CalculatorKey { name: "key-clear", onclick: move |_| state.write().clear_display(), "{clear_text}" }
- CalculatorKey { name: "key-sign", onclick: move |_| state.write().toggle_sign(), "±"}
- CalculatorKey { name: "key-percent", onclick: move |_| state.write().toggle_percent(), "%"}
+ cx.render(rsx!{
+ div { id: "wrapper",
+ div {
+ class: "app",
+ style { [include_str!("./assets/calculator.css")] }
+ div { class: "calculator", onkeypress: move |evt| state.write().handle_keydown(evt),
+ div { class: "calculator-display", "{formatted}"}
+ div { class: "calculator-keypad",
+ div { class: "input-keys",
+ div { class: "function-keys",
+ CalculatorKey { name: "key-clear", onclick: move |_| state.write().clear_display(), "{clear_text}" }
+ CalculatorKey { name: "key-sign", onclick: move |_| state.write().toggle_sign(), "±"}
+ CalculatorKey { name: "key-percent", onclick: move |_| state.write().toggle_percent(), "%"}
+ }
+ div { class: "digit-keys",
+ CalculatorKey { name: "key-0", onclick: move |_| state.write().input_digit(0), "0" }
+ CalculatorKey { name: "key-dot", onclick: move |_| state.write().input_dot(), "●" }
+ (1..10).map(move |k| rsx!{
+ CalculatorKey { key: "{k}", name: "key-{k}", onclick: move |_| state.write().input_digit(k), "{k}" }
+ })
+ }
}
- div { class: "digit-keys",
- CalculatorKey { name: "key-0", onclick: move |_| state.write().input_digit(0), "0" }
- CalculatorKey { name: "key-dot", onclick: move |_| state.write().input_dot(), "●" }
- (1..10).map(move |k| rsx!{
- CalculatorKey { key: "{k}", name: "key-{k}", onclick: move |_| state.write().input_digit(k), "{k}" }
- })
+ div { class: "operator-keys",
+ CalculatorKey { name:"key-divide", onclick: move |_| state.write().set_operator(Operator::Div), "÷" }
+ CalculatorKey { name:"key-multiply", onclick: move |_| state.write().set_operator(Operator::Mul), "×" }
+ CalculatorKey { name:"key-subtract", onclick: move |_| state.write().set_operator(Operator::Sub), "−" }
+ CalculatorKey { name:"key-add", onclick: move |_| state.write().set_operator(Operator::Add), "+" }
+ CalculatorKey { name:"key-equals", onclick: move |_| state.write().perform_operation(), "=" }
}
}
- div { class: "operator-keys",
- CalculatorKey { name:"key-divide", onclick: move |_| state.write().set_operator(Operator::Div), "÷" }
- CalculatorKey { name:"key-multiply", onclick: move |_| state.write().set_operator(Operator::Mul), "×" }
- CalculatorKey { name:"key-subtract", onclick: move |_| state.write().set_operator(Operator::Sub), "−" }
- CalculatorKey { name:"key-add", onclick: move |_| state.write().set_operator(Operator::Add), "+" }
- CalculatorKey { name:"key-equals", onclick: move |_| state.write().perform_operation(), "=" }
- }
}
}
}
diff --git a/examples/pattern_reducer.rs b/examples/pattern_reducer.rs
index 26bf3acff..d646b5105 100644
--- a/examples/pattern_reducer.rs
+++ b/examples/pattern_reducer.rs
@@ -8,10 +8,10 @@
use dioxus::prelude::*;
fn main() {
env_logger::init();
- dioxus::desktop::launch(App);
+ dioxus::desktop::launch(app);
}
-pub static App: Component = |cx| {
+fn app(cx: Scope) -> Element {
let state = use_state(&cx, PlayerState::new);
cx.render(rsx!(
@@ -26,7 +26,7 @@ pub static App: Component = |cx| {
}
}
))
-};
+}
enum PlayerAction {
Pause,
diff --git a/examples/readme.rs b/examples/readme.rs
index ae17c1cd9..a9ea60822 100644
--- a/examples/readme.rs
+++ b/examples/readme.rs
@@ -5,10 +5,10 @@
use dioxus::prelude::*;
fn main() {
- dioxus::desktop::launch(App);
+ dioxus::desktop::launch(app);
}
-static App: Component = |cx| {
+fn app(cx: Scope) -> Element {
let mut count = use_state(&cx, || 0);
cx.render(rsx! {
@@ -18,4 +18,4 @@ static App: Component = |cx| {
button { onclick: move |_| count -= 1, "Down low!" }
}
})
-};
+}
diff --git a/examples/router.rs b/examples/router.rs
index c28853185..4f4b7b2c1 100644
--- a/examples/router.rs
+++ b/examples/router.rs
@@ -23,7 +23,7 @@ pub enum Route {
NotFound,
}
-static App: Component = |cx| {
+fn app(cx: Scope) -> Element {
let route = use_router(&cx, Route::parse);
cx.render(rsx! {
@@ -42,7 +42,7 @@ static App: Component = |cx| {
}
footer {}
})
-};
+}
impl Route {
// Generate the appropriate route from the "tail" end of the URL
diff --git a/examples/rsx_compile_fail.rs b/examples/rsx_compile_fail.rs
index cdc69c21e..20760ef5b 100644
--- a/examples/rsx_compile_fail.rs
+++ b/examples/rsx_compile_fail.rs
@@ -1,3 +1,6 @@
+//! This example just flexes the ability to use arbitrary expressions within RSX.
+//! It also proves that lifetimes work properly, especially when used with use_ref
+
use dioxus::prelude::*;
fn main() {
diff --git a/examples/rsx_usage.rs b/examples/rsx_usage.rs
index 04afb7407..490aea844 100644
--- a/examples/rsx_usage.rs
+++ b/examples/rsx_usage.rs
@@ -39,7 +39,7 @@
//! - Allow top-level fragments
//!
fn main() {
- dioxus::desktop::launch(EXAMPLE);
+ dioxus::desktop::launch(app);
}
/// When trying to return "nothing" to Dioxus, you'll need to specify the type parameter or Rust will be sad.
@@ -49,7 +49,7 @@ const NONE_ELEMENT: Option<()> = None;
use baller::Baller;
use dioxus::prelude::*;
-pub static EXAMPLE: Component = |cx| {
+fn app(cx: Scope) -> Element {
let formatting = "formatting!";
let formatting_tuple = ("a", "b");
let lazy_fmt = format_args!("lazily formatted text");
@@ -192,10 +192,12 @@ pub static EXAMPLE: Component = |cx| {
[helper(&cx, "hello world!")]
}
})
-};
+}
fn helper<'a>(cx: &'a ScopeState, text: &str) -> Element<'a> {
- rsx!(cx, p { "{text}" })
+ cx.render(rsx! {
+ p { "{text}" }
+ })
}
mod baller {
@@ -227,5 +229,7 @@ pub fn Taller<'a>(cx: Scope<'a, TallerProps<'a>>) -> Element {
#[inline_props]
fn with_inline<'a>(cx: Scope<'a>, text: &'a str) -> Element {
- rsx!(cx, p { "{text}" })
+ cx.render(rsx! {
+ p { "{text}" }
+ })
}
diff --git a/examples/ssr.rs b/examples/ssr.rs
index 21a73b67b..b07d0a2c1 100644
--- a/examples/ssr.rs
+++ b/examples/ssr.rs
@@ -2,16 +2,16 @@ use dioxus::prelude::*;
use dioxus::ssr;
fn main() {
- let mut vdom = VirtualDom::new(APP);
+ let mut vdom = VirtualDom::new(app);
let _ = vdom.rebuild();
println!("{}", ssr::render_vdom(&vdom));
}
-static APP: Component = |cx| {
+fn app(cx: Scope) -> Element {
cx.render(rsx!(
div {
h1 { "Title" }
p { "Body" }
}
))
-};
+}
diff --git a/examples/ssr/basic.rs b/examples/ssr/basic.rs
index 916a9fa0e..2217b5a4a 100644
--- a/examples/ssr/basic.rs
+++ b/examples/ssr/basic.rs
@@ -3,8 +3,9 @@ use dioxus_core as dioxus;
use dioxus_core::prelude::*;
use dioxus_core_macro::*;
use dioxus_html as dioxus_elements;
+
fn main() {
- let mut dom = VirtualDom::new(App);
+ let mut dom = VirtualDom::new(app);
dom.rebuild();
println!(
"{}",
@@ -12,14 +13,14 @@ fn main() {
)
}
-pub static App: Component = |cx| {
+fn app(cx: Scope) -> Element {
cx.render(rsx!(
div {
class: "overflow-hidden"
ul {
- {(0..10).map(|i| rsx!{ li { class: "flex flex-col", "entry: {i}"}})}
+ (0..10).map(|i| rsx!{ li { class: "flex flex-col", "entry: {i}"}})
}
"hello world!"
}
))
-};
+}
diff --git a/examples/ssr/hydration.rs b/examples/ssr/hydration.rs
deleted file mode 100644
index 92eee69fa..000000000
--- a/examples/ssr/hydration.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-//! Example: realworld usage of hydration
-//!
-//!
-use dioxus::virtual_dom::VirtualDom;
-use dioxus_core as dioxus;
-use dioxus_core::prelude::*;
-use dioxus_hooks::use_state;
-use dioxus_html as dioxus_elements;
-
-fn main() {}
diff --git a/examples/ssr/template.html b/examples/ssr/template.html
deleted file mode 100644
index 0bacc8be5..000000000
--- a/examples/ssr/template.html
+++ /dev/null
@@ -1,298 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
hello dioxus
-
-
-
-
diff --git a/examples/ssr/tide.rs b/examples/ssr/tide.rs
deleted file mode 100644
index d7255b91d..000000000
--- a/examples/ssr/tide.rs
+++ /dev/null
@@ -1,61 +0,0 @@
-//!
-//!
-//!
-use dioxus::virtual_dom::VirtualDom;
-use dioxus_core as dioxus;
-use dioxus_core::prelude::*;
-use dioxus_core_macro::*;
-use dioxus_hooks::use_state;
-use dioxus_html as dioxus_elements;
-
-fn main() {}
-// use tide::{Request, Response};
-
-// #[async_std::main]
-// async fn main() -> Result<(), std::io::Error> {
-// let mut app = tide::new();
-
-// app.at("/:name").get(|req: Request<()>| async move {
-// let initial_name: String = req
-// .param("name")
-// .map(|f| f.parse().unwrap_or("...?".to_string()))
-// .unwrap_or("...?".to_string());
-
-// let mut dom = VirtualDom::new_with_props(Example, ExampleProps { initial_name });
-
-// dom.rebuild();
-
-// Ok(Response::builder(200)
-// .body(format!("{}", dioxus_ssr::render_vdom(&dom)))
-// .content_type(tide::http::mime::HTML)
-// .build())
-// });
-
-// println!("Server available at [http://127.0.0.1:8080/bill]");
-// app.listen("127.0.0.1:8080").await?;
-
-// Ok(())
-// }
-
-// #[derive(PartialEq, Props)]
-// struct ExampleProps {
-// initial_name: String,
-// }
-
-// static Example: Component = |cx| {
-// let dispaly_name = use_state(&cx, move || props.initial_name.clone());
-
-// cx.render(rsx! {
-// div { class: "py-12 px-4 text-center w-full max-w-2xl mx-auto",
-// span { class: "text-sm font-semibold"
-// "Dioxus Example: Jack and Jill"
-// }
-// h2 { class: "text-5xl mt-2 mb-6 leading-tight font-semibold font-heading"
-// "Hello, {dispaly_name}"
-// }
-// ul {
-// {(0..10).map(|f| rsx!( li {"Element {f}"} ))}
-// }
-// }
-// })
-// };
diff --git a/examples/ssr/tofile.rs b/examples/ssr/tofile.rs
index 9c64f14d5..da6e0eeb0 100644
--- a/examples/ssr/tofile.rs
+++ b/examples/ssr/tofile.rs
@@ -24,7 +24,7 @@ fn main() {
.unwrap();
}
-pub static App: Component = |cx| {
+pub fn App(cx: Scope) -> Element {
cx.render(rsx!(
div { class: "overflow-hidden"
link { href:"https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel:"stylesheet" }
@@ -37,9 +37,9 @@ pub static App: Component = |cx| {
Hero {}
}
))
-};
+}
-pub static Header: Component = |cx| {
+pub fn Header(cx: Scope) -> Element {
cx.render(rsx! {
div {
header { class: "text-gray-400 bg-gray-900 body-font"
@@ -63,9 +63,9 @@ pub static Header: Component = |cx| {
}
}
})
-};
+}
-pub static Hero: Component = |cx| {
+pub fn Hero(cx: Scope) -> Element {
//
cx.render(rsx! {
section{ class: "text-gray-400 bg-gray-900 body-font"
@@ -102,8 +102,9 @@ pub static Hero: Component = |cx| {
}
}
})
-};
-pub static Entry: Component = |cx| {
+}
+
+pub fn Entry(cx: Scope) -> Element {
//
cx.render(rsx! {
section{ class: "text-gray-400 bg-gray-900 body-font"
@@ -114,9 +115,9 @@ pub static Entry: Component = |cx| {
}
}
})
-};
+}
-pub static StacksIcon: Component = |cx| {
+pub fn StacksIcon(cx: Scope) -> Element {
cx.render(rsx!(
svg {
xmlns: "http://www.w3.org/2000/svg"
@@ -130,8 +131,9 @@ pub static StacksIcon: Component = |cx| {
path { d: "M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"}
}
))
-};
-pub static RightArrowIcon: Component = |cx| {
+}
+
+pub fn RightArrowIcon(cx: Scope) -> Element {
cx.render(rsx!(
svg {
fill: "none"
@@ -144,4 +146,4 @@ pub static RightArrowIcon: Component = |cx| {
path { d: "M5 12h14M12 5l7 7-7 7"}
}
))
-};
+}
diff --git a/examples/tailwind.rs b/examples/tailwind.rs
index 9bd87c15e..2d5244f17 100644
--- a/examples/tailwind.rs
+++ b/examples/tailwind.rs
@@ -14,7 +14,7 @@ fn main() {
const STYLE: &str = "body {overflow:hidden;}";
-pub static App: Component = |cx| {
+pub fn App(cx: Scope) -> Element {
cx.render(rsx!(
div { class: "overflow-hidden",
style { "{STYLE}" }
@@ -28,9 +28,9 @@ pub static App: Component = |cx| {
Hero {}
}
))
-};
+}
-pub static Header: Component = |cx| {
+pub fn Header(cx: Scope) -> Element {
cx.render(rsx! {
div {
header { class: "text-gray-400 bg-gray-900 body-font",
@@ -54,9 +54,9 @@ pub static Header: Component = |cx| {
}
}
})
-};
+}
-pub static Hero: Component = |cx| {
+pub fn Hero(cx: Scope) -> Element {
//
cx.render(rsx! {
section{ class: "text-gray-400 bg-gray-900 body-font",
@@ -93,8 +93,8 @@ pub static Hero: Component = |cx| {
}
}
})
-};
-pub static Entry: Component = |cx| {
+}
+pub fn Entry(cx: Scope) -> Element {
//
cx.render(rsx! {
section{ class: "text-gray-400 bg-gray-900 body-font",
@@ -105,9 +105,9 @@ pub static Entry: Component = |cx| {
}
}
})
-};
+}
-pub static StacksIcon: Component = |cx| {
+pub fn StacksIcon(cx: Scope) -> Element {
cx.render(rsx!(
svg {
// xmlns: "http://www.w3.org/2000/svg"
@@ -121,8 +121,8 @@ pub static StacksIcon: Component = |cx| {
path { d: "M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"}
}
))
-};
-pub static RightArrowIcon: Component = |cx| {
+}
+pub fn RightArrowIcon(cx: Scope) -> Element {
cx.render(rsx!(
svg {
fill: "none",
@@ -135,4 +135,4 @@ pub static RightArrowIcon: Component = |cx| {
path { d: "M5 12h14M12 5l7 7-7 7"}
}
))
-};
+}
diff --git a/examples/tasks.rs b/examples/tasks.rs
index 0838c1fa2..0a63019d7 100644
--- a/examples/tasks.rs
+++ b/examples/tasks.rs
@@ -12,13 +12,14 @@ fn main() {
fn app(cx: Scope) -> Element {
let count = use_state(&cx, || 0);
- use_future(&cx, || {
- for_async![count];
+ use_future(&cx, move || {
+ let count = UseState::for_async(&count);
+ // for_async![count];
async move {
- loop {
- tokio::time::sleep(Duration::from_millis(1000)).await;
- count += 1;
- }
+ // loop {
+ // tokio::time::sleep(Duration::from_millis(1000)).await;
+ // count += 1;
+ // }
}
});
diff --git a/examples/todomvc.rs b/examples/todomvc.rs
index 8a62e0ddd..907779894 100644
--- a/examples/todomvc.rs
+++ b/examples/todomvc.rs
@@ -4,7 +4,7 @@ use im_rc::HashMap;
use std::rc::Rc;
fn main() {
- dioxus::desktop::launch(App);
+ dioxus::desktop::launch(app);
}
#[derive(PartialEq)]
@@ -21,8 +21,7 @@ pub struct TodoItem {
pub contents: String,
}
-const STYLE: &str = include_str!("./assets/todomvc.css");
-const App: Component = |cx| {
+fn app(cx: Scope) -> Element {
let draft = use_state(&cx, || "".to_string());
let todos = use_state(&cx, || HashMap::>::new());
let filter = use_state(&cx, || FilterState::All);
@@ -48,40 +47,43 @@ const App: Component = |cx| {
_ => "items",
};
- rsx!(cx, div { id: "app",
- style {"{STYLE}"}
- div {
- header { class: "header",
- h1 {"todos"}
- input {
- class: "new-todo",
- placeholder: "What needs to be done?",
- value: "{draft}",
- oninput: move |evt| draft.set(evt.value.clone()),
+ cx.render(rsx!(
+ div { id: "app",
+ style { [include_str!("./assets/todomvc.css")] }
+ div {
+ header {
+ class: "header",
+ h1 { "todos" }
+ input {
+ class: "new-todo",
+ placeholder: "What needs to be done?",
+ value: "{draft}",
+ oninput: move |evt| draft.set(evt.value.clone()),
+ }
}
+ todolist,
+ (!todos.is_empty()).then(|| rsx!(
+ footer {
+ span {
+ strong {"{items_left}"}
+ span {"{item_text} left"}
+ }
+ ul { class: "filters",
+ li { class: "All", a { href: "", onclick: move |_| filter.set(FilterState::All), "All" }}
+ li { class: "Active", a { href: "active", onclick: move |_| filter.set(FilterState::Active), "Active" }}
+ li { class: "Completed", a { href: "completed", onclick: move |_| filter.set(FilterState::Completed), "Completed" }}
+ }
+ }
+ ))
+ }
+ footer { class: "info",
+ p {"Double-click to edit a todo"}
+ p { "Created by ", a { href: "http://github.com/jkelleyrtp/", "jkelleyrtp" }}
+ p { "Part of ", a { href: "http://todomvc.com", "TodoMVC" }}
}
- todolist,
- (!todos.is_empty()).then(|| rsx!(
- footer {
- span {
- strong {"{items_left}"}
- span {"{item_text} left"}
- }
- ul { class: "filters",
- li { class: "All", a { href: "", onclick: move |_| filter.set(FilterState::All), "All" }}
- li { class: "Active", a { href: "active", onclick: move |_| filter.set(FilterState::Active), "Active" }}
- li { class: "Completed", a { href: "completed", onclick: move |_| filter.set(FilterState::Completed), "Completed" }}
- }
- }
- ))
}
- footer { class: "info",
- p {"Double-click to edit a todo"}
- p { "Created by ", a { href: "http://github.com/jkelleyrtp/", "jkelleyrtp" }}
- p { "Part of ", a { href: "http://todomvc.com", "TodoMVC" }}
- }
- })
-};
+ ))
+}
#[derive(PartialEq, Props)]
pub struct TodoEntryProps {
@@ -93,18 +95,20 @@ pub fn TodoEntry(cx: Scope) -> Element {
let contents = use_state(&cx, || String::from(""));
let todo = &cx.props.todo;
- rsx!(cx, li {
- "{todo.id}"
- input {
- class: "toggle",
- r#type: "checkbox",
- "{todo.checked}"
- }
- {is_editing.then(|| rsx!{
+ cx.render(rsx! {
+ li {
+ "{todo.id}"
input {
- value: "{contents}",
- oninput: move |evt| contents.set(evt.value.clone())
+ class: "toggle",
+ r#type: "checkbox",
+ "{todo.checked}"
}
- })}
+ is_editing.then(|| rsx!{
+ input {
+ value: "{contents}",
+ oninput: move |evt| contents.set(evt.value.clone())
+ }
+ })
+ }
})
}
diff --git a/examples/weather_app.rs b/examples/weather_app.rs
index 66e90dbb7..e174fd397 100644
--- a/examples/weather_app.rs
+++ b/examples/weather_app.rs
@@ -7,22 +7,23 @@
use dioxus::prelude::*;
fn main() {
- dioxus::desktop::launch(App);
+ dioxus::desktop::launch(app);
}
const ENDPOINT: &str = "https://api.openweathermap.org/data/2.5/weather";
-static App: Component = |cx| {
+fn app(cx: Scope) -> Element {
//
let body = use_suspense(
- cx,
+ &cx,
|| async {
- let content = reqwest::get(ENDPOINT)
- .await
- .unwrap()
- .json::()
- .await
- .unwrap();
+ todo!()
+ // let content = reqwest::get(ENDPOINT)
+ // .await
+ // .unwrap()
+ // .json::()
+ // .await
+ // .unwrap();
},
|props| {
//
@@ -35,13 +36,12 @@ static App: Component = |cx| {
{body}
}
})
-};
+}
#[derive(PartialEq, Props)]
struct WeatherProps {}
-static WeatherDisplay: Component = |cx| {
- //
+fn WeatherDisplay(cx: Scope) -> Element {
cx.render(rsx!(
div { class: "flex items-center justify-center flex-col",
div { class: "flex items-center justify-center",
@@ -64,4 +64,4 @@ static WeatherDisplay: Component = |cx| {
}
}
))
-};
+}
diff --git a/examples/web_tick.rs b/examples/web_tick.rs
index 74cc1897f..ae33d769a 100644
--- a/examples/web_tick.rs
+++ b/examples/web_tick.rs
@@ -13,10 +13,10 @@
use dioxus::prelude::*;
fn main() {
- dioxus::desktop::launch(App);
+ dioxus::desktop::launch(app);
}
-static App: Component = |cx| {
+fn app(cx: Scope) -> Element {
let mut rng = SmallRng::from_entropy();
cx.render(rsx! {
@@ -29,7 +29,7 @@ static App: Component = |cx| {
}
}
})
-};
+}
#[derive(PartialEq, Props)]
struct RowProps {
diff --git a/examples/xss_safety.rs b/examples/xss_safety.rs
index c694d646f..c08f31616 100644
--- a/examples/xss_safety.rs
+++ b/examples/xss_safety.rs
@@ -1,3 +1,7 @@
+//! XSS Safety
+//!
+//! This example proves that Dioxus is safe from XSS attacks.
+
use dioxus::prelude::*;
fn main() {
@@ -9,19 +13,11 @@ fn app(cx: Scope) -> Element {
cx.render(rsx! {
div {
- "hello world!"
-
- h1 { "{contents}" }
-
- h3 { [contents.as_str()] }
-
+ h3 { "{contents}" }
input {
value: "{contents}",
- oninput: move |e| {
- contents.set(e.value.clone());
- eprintln!("asd");
- },
- "type": "text",
+ r#type: "text",
+ oninput: move |e| contents.set(e.value.clone()),
}
}
})
diff --git a/packages/core-macro/src/inlineprops.rs b/packages/core-macro/src/inlineprops.rs
index acbf778d3..ac71d5601 100644
--- a/packages/core-macro/src/inlineprops.rs
+++ b/packages/core-macro/src/inlineprops.rs
@@ -99,8 +99,8 @@ impl ToTokens for InlinePropsBody {
};
out_tokens.append_all(quote! {
- #[allow(non_camel_case)]
#modifiers
+ #[allow(non_camel_case_types)]
#vis struct #struct_name #generics {
#(#fields),*
}
diff --git a/packages/hooks/src/usestate.rs b/packages/hooks/src/usestate.rs
deleted file mode 100644
index 03f15a58b..000000000
--- a/packages/hooks/src/usestate.rs
+++ /dev/null
@@ -1,277 +0,0 @@
-use dioxus_core::prelude::*;
-use std::{
- cell::{Cell, Ref, RefCell, RefMut},
- fmt::{Debug, Display},
- rc::Rc,
-};
-
-/// Store state between component renders!
-///
-/// ## Dioxus equivalent of useState, designed for Rust
-///
-/// The Dioxus version of `useState` for state management inside components. It allows you to ergonomically store and
-/// modify state between component renders. When the state is updated, the component will re-render.
-///
-/// Dioxus' use_state basically wraps a RefCell with helper methods and integrates it with the VirtualDOM update system.
-///
-/// [`use_state`] exposes a few helper methods to modify the underlying state:
-/// - `.set(new)` allows you to override the "work in progress" value with a new value
-/// - `.get_mut()` allows you to modify the WIP value
-/// - `.get_wip()` allows you to access the WIP value
-/// - `.deref()` provides the previous value (often done implicitly, though a manual dereference with `*` might be required)
-///
-/// Additionally, a ton of std::ops traits are implemented for the `UseState` wrapper, meaning any mutative type operations
-/// will automatically be called on the WIP value.
-///
-/// ## Combinators
-///
-/// On top of the methods to set/get state, `use_state` also supports fancy combinators to extend its functionality:
-/// - `.classic()` and `.split()` convert the hook into the classic React-style hook
-/// ```rust
-/// let (state, set_state) = use_state(&cx, || 10).split()
-/// ```
-///
-///
-/// Usage:
-///
-/// ```ignore
-/// const Example: Component = |cx| {
-/// let counter = use_state(&cx, || 0);
-///
-/// cx.render(rsx! {
-/// div {
-/// h1 { "Counter: {counter}" }
-/// button { onclick: move |_| counter += 1, "Increment" }
-/// button { onclick: move |_| counter -= 1, "Decrement" }
-/// }
-/// ))
-/// }
-/// ```
-pub fn use_state<'a, T: 'static>(
- cx: &'a ScopeState,
- initial_state_fn: impl FnOnce() -> T,
-) -> UseState<'a, T> {
- let hook = cx.use_hook(move |_| {
- let first_val = initial_state_fn();
- UseStateInner {
- current_val: Rc::new(first_val),
- update_callback: cx.schedule_update(),
- wip: Rc::new(RefCell::new(None)),
- update_scheuled: Cell::new(false),
- }
- });
-
- hook.update_scheuled.set(false);
- let mut new_val = hook.wip.borrow_mut();
- if new_val.is_some() {
- // if there's only one reference (weak or otherwise), we can just swap the values
- if let Some(val) = Rc::get_mut(&mut hook.current_val) {
- *val = new_val.take().unwrap();
- } else {
- hook.current_val = Rc::new(new_val.take().unwrap());
- }
- }
-
- UseState { inner: &*hook }
-}
-struct UseStateInner {
- current_val: Rc,
- update_scheuled: Cell,
- update_callback: Rc,
- wip: Rc>>,
-}
-
-pub struct UseState<'a, T: 'static> {
- inner: &'a UseStateInner,
-}
-
-impl Copy for UseState<'_, T> {}
-impl<'a, T> Clone for UseState<'a, T>
-where
- T: 'static,
-{
- fn clone(&self) -> Self {
- UseState { inner: self.inner }
- }
-}
-
-impl Debug for UseState<'_, T> {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(f, "{:?}", self.inner.current_val)
- }
-}
-impl<'a, T: 'static> UseState<'a, T> {
- /// Tell the Dioxus Scheduler that we need to be processed
- pub fn needs_update(&self) {
- if !self.inner.update_scheuled.get() {
- self.inner.update_scheuled.set(true);
- (self.inner.update_callback)();
- }
- }
-
- pub fn set(&self, new_val: T) {
- *self.inner.wip.borrow_mut() = Some(new_val);
- self.needs_update();
- }
-
- pub fn get(&self) -> &'a T {
- &self.inner.current_val
- }
-
- pub fn get_rc(&self) -> &'a Rc {
- &self.inner.current_val
- }
-
- /// Get the current status of the work-in-progress data
- pub fn get_wip(&self) -> Ref