docs: big updates to the reference

This commit is contained in:
Jonathan Kelley 2021-07-16 16:11:25 -04:00
parent caf772cf24
commit 583fdfa561
57 changed files with 728 additions and 249 deletions

View File

@ -19,7 +19,7 @@ dioxus-mobile = { path = "./packages/mobile", optional = true }
[features] [features]
# core # core
default = ["core"] default = ["core", "ssr"]
core = ["macro", "hooks", "html"] core = ["macro", "hooks", "html"]
macro = ["dioxus-core-macro"] macro = ["dioxus-core-macro"]
hooks = ["dioxus-hooks"] hooks = ["dioxus-hooks"]
@ -48,6 +48,7 @@ async-std = { version = "1.9.0", features = ["attributes"] }
im-rc = "15.0.0" im-rc = "15.0.0"
rand = { version = "0.8.4", features = ["small_rng"] } rand = { version = "0.8.4", features = ["small_rng"] }
fxhash = "0.2.1" fxhash = "0.2.1"
gloo-timers = "0.2.1"
[workspace] [workspace]
members = [ members = [

View File

@ -60,7 +60,7 @@ Commas are entirely optional, but might be useful to delineate between elements
The `render` function provides an **extremely efficient** allocator for VNodes and text, so try not to use the `format!` macro in your components. Rust's default `ToString` methods pass through the global allocator, but all text in components is allocated inside a manually-managed Bump arena. To push you in the right direction, all text-based attributes take `std::fmt::Arguments` directly, so you'll want to reach for `format_args!` when the built-in `f-string` interpolation just doesn't cut it. The `render` function provides an **extremely efficient** allocator for VNodes and text, so try not to use the `format!` macro in your components. Rust's default `ToString` methods pass through the global allocator, but all text in components is allocated inside a manually-managed Bump arena. To push you in the right direction, all text-based attributes take `std::fmt::Arguments` directly, so you'll want to reach for `format_args!` when the built-in `f-string` interpolation just doesn't cut it.
```rust ```rust
static Example: FC<()> = |cx| { pub static Example: FC<()> = |cx| {
let text = "example"; let text = "example";

View File

@ -25,7 +25,7 @@ fn Example(cx: Context, name: String) -> VNode {
// or // or
#[functional_component] #[functional_component]
static Example: FC = |cx, name: String| html! { <div> "Hello {name}!" </div> }; pub static Example: FC = |cx, name: String| html! { <div> "Hello {name}!" </div> };
``` ```
The final output of components must be a tree of VNodes. We provide an html macro for using JSX-style syntax to write these, though, you could use any macro, DSL, templating engine, or the constructors directly. The final output of components must be a tree of VNodes. We provide an html macro for using JSX-style syntax to write these, though, you could use any macro, DSL, templating engine, or the constructors directly.

View File

@ -25,7 +25,7 @@ fn main() {
#[derive(Debug)] #[derive(Debug)]
struct CustomContext([&'static str; 3]); struct CustomContext([&'static str; 3]);
static Example: FC<()> = |cx| { pub static Example: FC<()> = |cx| {
cx.use_create_context(|| CustomContext(["Jack", "Jill", "Bob"])); cx.use_create_context(|| CustomContext(["Jack", "Jill", "Bob"]));
cx.render(rsx! { cx.render(rsx! {

View File

@ -13,7 +13,7 @@ fn main() {
wasm_bindgen_futures::spawn_local(WebsysRenderer::start(Example)); wasm_bindgen_futures::spawn_local(WebsysRenderer::start(Example));
} }
static Example: FC<()> = |cx| { pub static Example: FC<()> = |cx| {
let nodes = (0..500).map(|f| rsx! (li {"div"})); let nodes = (0..500).map(|f| rsx! (li {"div"}));
cx.render(rsx! { cx.render(rsx! {
div { div {

View File

@ -14,7 +14,7 @@ fn main() {
} }
// this is a component // this is a component
static Example: FC<()> = |cx| { pub static Example: FC<()> = |cx| {
let (event, set_event) = use_state_classic(cx, || None); let (event, set_event) = use_state_classic(cx, || None);
let handler = move |evt| { let handler = move |evt| {
@ -50,7 +50,7 @@ struct ExampleProps {
name: String, name: String,
} }
static Example2: FC<ExampleProps> = |cx| { pub static Example2: FC<ExampleProps> = |cx| {
cx.render(rsx! { cx.render(rsx! {
div { div {
h1 {"hello {cx.name}"} h1 {"hello {cx.name}"}

View File

@ -37,7 +37,7 @@ static App: FC<()> = |cx| {
}) })
}; };
static Example: FC<()> = |cx| { pub static Example: FC<()> = |cx| {
cx.render(rsx! { cx.render(rsx! {
div { class: "max-w-lg max-w-xs bg-blue-800 shadow-2xl rounded-lg mx-auto text-center py-12 mt-4 rounded-xl" div { class: "max-w-lg max-w-xs bg-blue-800 shadow-2xl rounded-lg mx-auto text-center py-12 mt-4 rounded-xl"
div { class: "container py-5 max-w-md mx-auto" div { class: "container py-5 max-w-md mx-auto"

View File

@ -9,7 +9,7 @@ fn main() {
wasm_bindgen_futures::spawn_local(WebsysRenderer::start(Example)) wasm_bindgen_futures::spawn_local(WebsysRenderer::start(Example))
} }
static Example: FC<()> = |cx| { pub static Example: FC<()> = |cx| {
let (name, set_name) = use_state_classic(cx, || "...?"); let (name, set_name) = use_state_classic(cx, || "...?");
log::debug!("Running component...."); log::debug!("Running component....");

View File

@ -9,7 +9,7 @@ fn main() {
wasm_bindgen_futures::spawn_local(WebsysRenderer::start(Example)) wasm_bindgen_futures::spawn_local(WebsysRenderer::start(Example))
} }
static Example: FC<()> = |cx| { pub static Example: FC<()> = |cx| {
let (name, set_name) = use_state_classic(cx, || "...?"); let (name, set_name) = use_state_classic(cx, || "...?");
cx.render(rsx! { cx.render(rsx! {
section { class: "py-12 px-4 text-center" section { class: "py-12 px-4 text-center"

View File

@ -11,7 +11,7 @@ fn main() {
wasm_bindgen_futures::spawn_local(WebsysRenderer::start(Example)); wasm_bindgen_futures::spawn_local(WebsysRenderer::start(Example));
} }
static Example: FC<()> = |cx| { pub static Example: FC<()> = |cx| {
cx.render(rsx! { cx.render(rsx! {
div { div {
span { span {

View File

@ -27,7 +27,7 @@ struct ExampleProps {
initial_name: &'static str, initial_name: &'static str,
} }
static Example: FC<ExampleProps> = |cx| { pub static Example: FC<ExampleProps> = |cx| {
let name = use_state(cx, move || cx.initial_name); let name = use_state(cx, move || cx.initial_name);
cx.render(rsx! { cx.render(rsx! {

View File

@ -1,7 +1,7 @@
use dioxus::prelude::*; use dioxus::prelude::*;
fn main() {} fn main() {}
static Example: FC<()> = |cx| { pub static Example: FC<()> = |cx| {
let (g, set_g) = use_state(cx, || 0).classic(); let (g, set_g) = use_state(cx, || 0).classic();
let v = (0..10).map(move |f| { let v = (0..10).map(move |f| {
rsx!(li { rsx!(li {

View File

@ -17,7 +17,7 @@ fn main() {
.expect("Webview finished"); .expect("Webview finished");
} }
// static Example: FC<()> = |cx| { // pub static Example: FC<()> = |cx| {
// cx.render(html! { // cx.render(html! {
// <div> // <div>
// <svg class="octicon octicon-star v-align-text-bottom" // <svg class="octicon octicon-star v-align-text-bottom"
@ -36,7 +36,7 @@ fn main() {
// </div> // </div>
// }) // })
// }; // };
static Example: FC<()> = |cx| { pub static Example: FC<()> = |cx| {
cx.render(rsx! { cx.render(rsx! {
div { div {
class: "flex items-center justify-center flex-col" class: "flex items-center justify-center flex-col"

View File

@ -20,12 +20,8 @@ use dioxus::prelude::*;
const STYLE: &str = include_str!("./assets/calculator.css"); const STYLE: &str = include_str!("./assets/calculator.css");
fn main() { fn main() {
dioxus::desktop::launch(App, |cfg| { dioxus::desktop::launch(App, |cfg| cfg.with_title("Calculator Demo"))
cfg.with_title("Calculator Demo") .expect("failed to launch dioxus app");
.with_resizable(true)
.with_skip_taskbar(true)
})
.expect("failed to launch dioxus app");
} }
static App: FC<()> = |cx| { static App: FC<()> = |cx| {

View File

@ -16,8 +16,6 @@
//! Feel free to file a PR or Issue if you run into another antipattern that you think users of Dioxus should know about. //! Feel free to file a PR or Issue if you run into another antipattern that you think users of Dioxus should know about.
use dioxus::prelude::*; use dioxus::prelude::*;
fn main() {}
/// Antipattern: Iterators without keys /// Antipattern: Iterators without keys
/// ----------------------------------- /// -----------------------------------
/// ///
@ -174,3 +172,10 @@ static _example: FC<()> = |cx| todo!();
/// This will only include the `core` dioxus crate which is relatively slim and fast to compile and avoids target-specific /// This will only include the `core` dioxus crate which is relatively slim and fast to compile and avoids target-specific
/// libraries. /// libraries.
static __example: FC<()> = |cx| todo!(); static __example: FC<()> = |cx| todo!();
pub static Example: FC<()> = |cx| {
cx.render(rsx! {
AntipatternNoKeys { data: std::collections::HashMap::new() }
AntipatternNestedFragments {}
})
};

View File

@ -1,10 +0,0 @@
use dioxus::prelude::*;
fn main() {}
static Example: FC<()> = |cx| {
cx.render(rsx! {
div {
}
})
};

View File

@ -1,10 +1,37 @@
use dioxus::prelude::*; //! Example: The basics of Dioxus
fn main() {} //! ----------------------------
//!
//! This small example covers some of the basics of Dioxus including
//! - Components
//! - Props
//! - Children
//! - the rsx! macro
static Example: FC<()> = |cx| { use dioxus::prelude::*;
pub static Example: FC<()> = |cx| {
cx.render(rsx! { cx.render(rsx! {
div { div {
Greeting {
name: "Dioxus"
div { "Dioxus is a fun, fast, and portable UI framework for Rust" }
}
}
})
};
#[derive(PartialEq, Props)]
struct GreetingProps {
name: &'static str,
}
static Greeting: FC<GreetingProps> = |cx| {
cx.render(rsx! {
div {
h1 { "Hello, {cx.name}!" }
p { "Welcome to the Diouxs framework" }
br {}
{cx.children()}
} }
}) })
}; };

View File

@ -17,9 +17,8 @@
//! In the future, this might become a runtime error, so consider it an error today. //! In the future, this might become a runtime error, so consider it an error today.
use dioxus::prelude::*; use dioxus::prelude::*;
fn main() {}
static App: FC<()> = |cx| { pub static Example: FC<()> = |cx| {
cx.render(rsx! { cx.render(rsx! {
div { div {
Banner { Banner {
@ -32,7 +31,7 @@ static App: FC<()> = |cx| {
}) })
}; };
static Banner: FC<()> = |cx| { pub static Banner: FC<()> = |cx| {
cx.render(rsx! { cx.render(rsx! {
div { div {
h1 { "This is a great banner!" } h1 { "This is a great banner!" }

View File

@ -11,14 +11,12 @@
use dioxus::prelude::*; use dioxus::prelude::*;
fn main() {}
// Convert a boolean conditional into a hide/show // Convert a boolean conditional into a hide/show
#[derive(PartialEq, Props)] #[derive(PartialEq, Props)]
struct MyProps { pub struct MyProps {
should_show: bool, should_show: bool,
} }
static Example: FC<MyProps> = |cx| { pub static Example0: FC<MyProps> = |cx| {
cx.render(rsx! { cx.render(rsx! {
div { div {
{cx.should_show.then(|| rsx!{ {cx.should_show.then(|| rsx!{
@ -38,10 +36,10 @@ static Example: FC<MyProps> = |cx| {
// In short: // In short:
// `rsx!(in cx, ...)` is shorthand for `cx.render(rsx!(...))` // `rsx!(in cx, ...)` is shorthand for `cx.render(rsx!(...))`
#[derive(PartialEq, Props)] #[derive(PartialEq, Props)]
struct MyProps1 { pub struct MyProps1 {
should_show: bool, should_show: bool,
} }
static Example1: FC<MyProps1> = |cx| { pub static Example1: FC<MyProps1> = |cx| {
cx.render(rsx! { cx.render(rsx! {
div { div {
// With matching // With matching
@ -70,16 +68,16 @@ static Example1: FC<MyProps1> = |cx| {
/// Matching can be expanded /// Matching can be expanded
#[derive(PartialEq)] #[derive(PartialEq)]
enum Color { pub enum Color {
Green, Green,
Yellow, Yellow,
Red, Red,
} }
#[derive(PartialEq, Props)] #[derive(PartialEq, Props)]
struct MyProps2 { pub struct MyProps2 {
color: Color, color: Color,
} }
static Example2: FC<MyProps2> = |cx| { pub static Example2: FC<MyProps2> = |cx| {
cx.render(rsx! { cx.render(rsx! {
div { div {
{match cx.color { {match cx.color {
@ -90,3 +88,29 @@ static Example2: FC<MyProps2> = |cx| {
} }
}) })
}; };
pub static Example: FC<()> = |cx| {
let should_show = use_state(cx, || false);
let mut color_index = use_state(cx, || 0);
let color = match *color_index % 2 {
2 => Color::Green,
1 => Color::Yellow,
_ => Color::Red,
};
cx.render(rsx! {
div {
button {
onclick: move |_| should_show.set(!*should_show)
"click to toggle showing the examples"
}
button {
onclick: move |_| color_index += 1
"click to for the enxt color"
}
Example0 { should_show: *should_show }
Example1 { should_show: *should_show }
Example2 { color: color }
}
})
};

View File

@ -1,10 +1,37 @@
use dioxus::prelude::*; use dioxus::prelude::*;
fn main() {} fn main() {}
static Example: FC<()> = |cx| { pub static Example: FC<()> = |cx| {
cx.render(rsx! { cx.render(rsx! {
div { div {
} }
}) })
}; };
// A controlled component:
static ControlledSelect: FC<()> = |cx| {
let value = use_state(cx, || String::from("Grapefruit"));
cx.render(rsx! {
select { value: "{value}", onchange: move |evt| value.set(evt.value()),
option { value: "Grapefruit", "Grapefruit"}
option { value: "Lime", "Lime"}
option { value: "Coconut", "Coconut"}
option { value: "Mango", "Mango"}
}
})
};
// TODO - how do uncontrolled things work?
static UncontrolledSelect: FC<()> = |cx| {
let value = use_state(cx, || String::new());
cx.render(rsx! {
select {
option { value: "Grapefruit", "Grapefruit"}
option { value: "Lime", "Lime"}
option { value: "Coconut", "Coconut"}
option { value: "Mango", "Mango"}
}
})
};

View File

@ -1,10 +1,44 @@
use dioxus::prelude::*; //! Example: Web Components & Custom Elements
fn main() {} //! -----------------------------------------
//!
//! Web components are a flavor of html elements that can be user-defined.
//! See: https://www.webcomponents.org for more information.
//!
//! Users who use webcomponents typically don't use Dioxus. However, if you would like to use webcomponents in Dioxus,
//! you can easily create a new custom element with compile-time correct wrappers around the webcomponent.
//!
//! We don't support building new webcomponents with Dioxus, however. :(
static Example: FC<()> = |cx| { use dioxus::prelude::*;
pub static Example: FC<()> = |cx| {
cx.render(rsx! { cx.render(rsx! {
div { div {
custom_element {
custom_attr: "custom data on custom elements"
}
} }
}) })
}; };
mod dioxus_elements {
use std::fmt::Arguments;
use dioxus::prelude::DioxusElement;
#[allow(non_camel_case_types)]
pub struct custom_element;
impl DioxusElement for custom_element {
const TAG_NAME: &'static str = "custom_element";
const NAME_SPACE: Option<&'static str> = None;
}
impl custom_element {
pub fn custom_attr<'a>(&self, f: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
f.attr("custom_asttr", val, None, false)
}
}
// Re-export the normal html namespace
pub use dioxus::prelude::dioxus_elements::*;
use dioxus_core::{nodes::Attribute, NodeFactory};
}

View File

@ -3,8 +3,6 @@
//! //!
//! This is a simple pattern that allows you to return no elements! //! This is a simple pattern that allows you to return no elements!
fn main() {}
use dioxus::prelude::*; use dioxus::prelude::*;
static Example: FC<()> = |cx| cx.render(rsx! { Fragment {} }); pub static Example: FC<()> = |cx| cx.render(rsx! { Fragment {} });

View File

@ -9,21 +9,18 @@
//! - By using the fragment() method on the node factory //! - By using the fragment() method on the node factory
use dioxus::prelude::*; use dioxus::prelude::*;
fn main() {}
// Returning multiple elements with rsx! or html! // Returning multiple elements with rsx! or html!
static Example1: FC<()> = |cx| { static App1: FC<()> = |cx| {
cx.render(rsx! { cx.render(rsx! {
h1 { } h1 { }
h2 { } h2 { }
h3 { } h3 { }
// {}
// "hello world"
}) })
}; };
// Using the Fragment component // Using the Fragment component
static Example2: FC<()> = |cx| { static App2: FC<()> = |cx| {
cx.render(rsx! { cx.render(rsx! {
Fragment { Fragment {
div {} div {}
@ -34,7 +31,7 @@ static Example2: FC<()> = |cx| {
}; };
// Using the `fragment` method on the NodeFactory // Using the `fragment` method on the NodeFactory
static Example3: FC<()> = |cx| { static App3: FC<()> = |cx| {
cx.render(LazyNodes::new(move |fac| { cx.render(LazyNodes::new(move |fac| {
fac.fragment_from_iter([ fac.fragment_from_iter([
fac.text(format_args!("A")), fac.text(format_args!("A")),
@ -44,3 +41,11 @@ static Example3: FC<()> = |cx| {
]) ])
})) }))
}; };
pub static Example: FC<()> = |cx| {
cx.render(rsx! {
App1 {}
App2 {}
App3 {}
})
};

View File

@ -12,7 +12,6 @@
//! A coming update with the assets system will make it possible to include global css from child components. //! A coming update with the assets system will make it possible to include global css from child components.
use dioxus::prelude::*; use dioxus::prelude::*;
fn main() {}
const STYLE: &str = r#" const STYLE: &str = r#"
body {background-color: powderblue;} body {background-color: powderblue;}
@ -20,7 +19,7 @@ h1 {color: blue;}
p {color: red;} p {color: red;}
"#; "#;
const Example: FC<()> = |cx| { pub static Example: FC<()> = |cx| {
cx.render(rsx! { cx.render(rsx! {
head { style { "{STYLE}" } } head { style { "{STYLE}" } }
body { body {

View File

@ -1,10 +1,38 @@
//! Example: Inline Styles
//! ----------------------
//!
//! This example shows how to use inline styles in Dioxus components.
//!
//! Inline styles function very similar to regular attributes, just grouped together in "style".
//!
//! Inline styles in Dioxus are more performant than React since we're able to cache attributes and compare by pointers.
//! However, it's still not as performant as cascaded styles. Use with care.
use dioxus::prelude::*; use dioxus::prelude::*;
fn main() {}
static Example: FC<()> = |cx| { pub static Example: FC<()> = |cx| {
cx.render(rsx! { cx.render(rsx! {
div { head {
style: { background_color: "powderblue" }
}
body {
h1 { style: { color: "blue" }
"This is a heading"
}
p { style: { color: "red" }
"This is a paragraph"
}
}
})
};
// .... technically the rsx! macro is slightly broken at the moment and alows 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: FC<()> = |cx| {
cx.render(rsx! {
div { color: "red"
"hello world!"
} }
}) })
}; };

View File

@ -7,17 +7,17 @@
//! resolves to VNodes. It's more efficient and easier to write than having to `collect` everywhere. //! resolves to VNodes. It's more efficient and easier to write than having to `collect` everywhere.
//! //!
//! This also makes it easy to write "pull"-style iterators that don't have a known size. //! This also makes it easy to write "pull"-style iterators that don't have a known size.
//!
fn main() {} //! However, when the size of an iterator needs to be know for display purposes, collecting is fine.
use dioxus::prelude::*; use dioxus::prelude::*;
static Example: FC<()> = |cx| {
let g = use_state(cx, || 0); pub static Example: FC<()> = |cx| {
let example_data = use_state(cx, || 0);
let v = (0..10).map(|f| { let v = (0..10).map(|f| {
rsx! { rsx! {
li { li { onclick: move |_| example_data.set(f)
onclick: move |_| g.set(f)
"ID: {f}" "ID: {f}"
ul { ul {
{(0..10).map(|k| rsx!{ {(0..10).map(|k| rsx!{
@ -31,7 +31,7 @@ static Example: FC<()> = |cx| {
}); });
cx.render(rsx! { cx.render(rsx! {
h3 {"Selected: {g}"} h3 {"Selected: {example_data}"}
ul { ul {
{v} {v}
} }

View File

@ -7,7 +7,13 @@
use dioxus::prelude::*; use dioxus::prelude::*;
fn main() {} pub static Example: FC<()> = |cx| {
cx.render(rsx! {
ButtonList {}
NonUpdatingEvents {}
DisablePropogation {}
})
};
/// We can use `set_name` in multiple closures; the closures automatically *copy* the reference to set_name. /// We can use `set_name` in multiple closures; the closures automatically *copy* the reference to set_name.
static ButtonList: FC<()> = |cx| { static ButtonList: FC<()> = |cx| {

View File

@ -1,7 +1,170 @@
// mod iterator; #![allow(
// mod memo; unused,
dead_code,
non_upper_case_globals,
non_camel_case_types,
non_snake_case
)]
mod antipatterns;
mod basics;
mod children;
mod conditional_rendering;
mod controlled_inputs;
mod custom_elements;
mod empty;
mod fragments;
mod global_css;
mod inline_styles;
mod iterators; mod iterators;
mod listener; mod listener;
mod memo; mod memo;
mod noderefs;
mod signals;
mod spreadpattern;
mod statemanagement;
mod suspense;
mod task;
mod testing;
mod tostring;
fn main() {} fn main() {}
use std::rc::Rc;
use dioxus::prelude::*;
static App: FC<()> = |cx| {
let (selection, set_selection) = use_state(cx, || None as Option<usize>).classic();
let body = match selection {
Some(id) => rsx!(in cx, ReferenceItem { selected: *id }),
None => rsx!(in cx, div { "Select an concept to explore" }),
};
cx.render(rsx! {
div {
ScrollSelector { onselect: move |id| set_selection(id) }
{body}
}
})
};
// this is its own component to stay memoized
#[derive(Props)]
struct ScrollSelectorProps<'a> {
onselect: &'a dyn Fn(Option<usize>),
}
fn ScrollSelector<'a>(cx: Context<'a, ScrollSelectorProps>) -> VNode<'a> {
let selection_list = (&REFERENCES).iter().enumerate().map(|(id, _)| {
rsx! {
li {
h3 {}
}
}
});
cx.render(rsx! {
div {
h1 {""}
ul {
{selection_list}
button {
onclick: move |_| (cx.onselect)(Some(10))
}
}
}
})
}
#[derive(PartialEq, Props)]
struct ReferenceItemProps {
selected: usize,
}
static ReferenceItem: FC<ReferenceItemProps> = |cx| {
let (caller, name, code) = REFERENCES[cx.selected];
// Create the component using the factory API directly
let caller_node = LazyNodes::new(move |f| f.component(caller, (), None, &[]));
cx.render(rsx! {
div {
// Source of the left, rendered on the right
div {
code { "{code}" }
}
div {
{caller_node}
}
}
})
};
static REFERENCES: &[(FC<()>, &str, &str)] = &[
(basics::Example, "Basics", include_str!("./basics.rs")),
(children::Example, "Children", include_str!("./children.rs")),
(
conditional_rendering::Example,
"Conditional Rendering",
include_str!("./conditional_rendering.rs"),
),
// TODO
(
controlled_inputs::Example,
"Controlled Inputs",
include_str!("./controlled_inputs.rs"),
),
(empty::Example, "empty", include_str!("./empty.rs")),
(
custom_elements::Example,
"Custom Elements & Web Components",
include_str!("./custom_elements.rs"),
),
(
fragments::Example,
"Fragments",
include_str!("./fragments.rs"),
),
(
iterators::Example,
"Iterators",
include_str!("./iterators.rs"),
),
(
global_css::Example,
"Global CSS",
include_str!("./global_css.rs"),
),
(
inline_styles::Example,
"Inline Styles",
include_str!("./inline_styles.rs"),
),
(listener::Example, "Listener", include_str!("./listener.rs")),
(memo::Example, "Memo", include_str!("./memo.rs")),
(
spreadpattern::Example,
"Spread Pattern",
include_str!("./spreadpattern.rs"),
),
(suspense::Example, "Suspense", include_str!("./suspense.rs")),
(task::Example, "Task", include_str!("./task.rs")),
(tostring::Example, "Tostring", include_str!("./tostring.rs")),
(
antipatterns::Example,
"Anti-patterns",
include_str!("./antipatterns.rs"),
),
/*
TODO!
*/
(signals::Example, "Signals", include_str!("./signals.rs")),
(noderefs::Example, "NodeRefs", include_str!("./noderefs.rs")),
(
statemanagement::Example,
"State Management",
include_str!("./statemanagement.rs"),
),
(testing::Example, "Testing", include_str!("./testing.rs")),
];

View File

@ -18,11 +18,10 @@
//! memoized collections like im-rc which are designed for this use case. //! memoized collections like im-rc which are designed for this use case.
use dioxus::prelude::*; use dioxus::prelude::*;
fn main() {}
// By default, components with no props are always memoized. // By default, components with no props are always memoized.
// A props of () is considered empty. // A props of () is considered empty.
static Example: FC<()> = |cx| { pub static Example: FC<()> = |cx| {
cx.render(rsx! { cx.render(rsx! {
div { "100% memoized!" } div { "100% memoized!" }
}) })
@ -32,11 +31,11 @@ static Example: FC<()> = |cx| {
// However, the parent *must* create a new string on every render. // However, the parent *must* create a new string on every render.
// Notice how these props implement PartialEq - this is required for 'static props // Notice how these props implement PartialEq - this is required for 'static props
#[derive(PartialEq, Props)] #[derive(PartialEq, Props)]
struct MyProps1 { pub struct MyProps1 {
name: String, name: String,
} }
static Example1: FC<MyProps1> = |cx| { pub static Example1: FC<MyProps1> = |cx| {
cx.render(rsx! { cx.render(rsx! {
div { "100% memoized! {cx.name}" } div { "100% memoized! {cx.name}" }
}) })
@ -46,11 +45,11 @@ static Example1: FC<MyProps1> = |cx| {
// In contrast with the `String` example, these props use `Rc<str>` which operates similar to strings in JavaScript. // In contrast with the `String` example, these props use `Rc<str>` which operates similar to strings in JavaScript.
// These strings cannot be modified, but may be cheaply shared in many places without issue. // These strings cannot be modified, but may be cheaply shared in many places without issue.
#[derive(PartialEq, Props)] #[derive(PartialEq, Props)]
struct MyProps2 { pub struct MyProps2 {
name: std::rc::Rc<str>, name: std::rc::Rc<str>,
} }
static Example2: FC<MyProps2> = |cx| { pub static Example2: FC<MyProps2> = |cx| {
cx.render(rsx! { cx.render(rsx! {
div { "100% memoized! {cx.name}" } div { "100% memoized! {cx.name}" }
}) })
@ -58,11 +57,11 @@ static Example2: FC<MyProps2> = |cx| {
// These props *do* borrow any content, and therefore cannot be safely memoized!. // These props *do* borrow any content, and therefore cannot be safely memoized!.
#[derive(PartialEq, Props)] #[derive(PartialEq, Props)]
struct MyProps3<'a> { pub struct MyProps3<'a> {
name: &'a str, name: &'a str,
} }
// We need to manually specify a lifetime that ensures props and scope (the component's state) share the same lifetime. // We need to manually specify a lifetime that ensures props and scope (the component's state) share the same lifetime.
// Using the `static Example: FC<()>` pattern _will_ specify a lifetime, but that lifetime will be static which might // Using the `pub static Example: FC<()>` pattern _will_ specify a lifetime, but that lifetime will be static which might
// not exactly be what you want // not exactly be what you want
fn Example3<'a>(cx: Context<'a, MyProps3<'a>>) -> VNode { fn Example3<'a>(cx: Context<'a, MyProps3<'a>>) -> VNode {
cx.render(rsx! { cx.render(rsx! {
@ -73,7 +72,7 @@ fn Example3<'a>(cx: Context<'a, MyProps3<'a>>) -> VNode {
// These props *do* borrow any content, and therefore cannot be safely memoized!. // These props *do* borrow any content, and therefore cannot be safely memoized!.
// However, they cannot be compared, so we don't need the PartialEq flag. // However, they cannot be compared, so we don't need the PartialEq flag.
#[derive(Props)] #[derive(Props)]
struct MyProps4<'a> { pub struct MyProps4<'a> {
onhandle: &'a dyn Fn(), onhandle: &'a dyn Fn(),
} }

View File

@ -1,7 +1,7 @@
use dioxus::prelude::*; use dioxus::prelude::*;
fn main() {} fn main() {}
static Example: FC<()> = |cx| { pub static Example: FC<()> = |cx| {
cx.render(rsx! { cx.render(rsx! {
div { div {

View File

@ -1,7 +1,7 @@
use dioxus::prelude::*; use dioxus::prelude::*;
fn main() {} fn main() {}
static Example: FC<()> = |cx| { pub static Example: FC<()> = |cx| {
cx.render(rsx! { cx.render(rsx! {
div { div {

View File

@ -8,27 +8,26 @@
//! values, using the manual props as the base and then modifying fields specified with non-spread attributes. //! values, using the manual props as the base and then modifying fields specified with non-spread attributes.
use dioxus::prelude::*; use dioxus::prelude::*;
fn main() {}
static App: FC<()> = |cx| { pub static Example: FC<()> = |cx| {
let props = MyProps { let props = MyProps {
count: 0, count: 0,
live: true, live: true,
name: "Dioxus", name: "Dioxus",
}; };
cx.render(rsx! { cx.render(rsx! {
Example { ..props, count: 10, div {"child"} } Example1 { ..props, count: 10, div {"child"} }
}) })
}; };
#[derive(PartialEq, Props)] #[derive(PartialEq, Props)]
struct MyProps { pub struct MyProps {
count: u32, count: u32,
live: bool, live: bool,
name: &'static str, name: &'static str,
} }
static Example: FC<MyProps> = |cx| { pub static Example1: FC<MyProps> = |cx| {
cx.render(rsx! { cx.render(rsx! {
div { div {
h1 { "Hello, {cx.name}"} h1 { "Hello, {cx.name}"}

View File

@ -1,7 +1,7 @@
use dioxus::prelude::*; use dioxus::prelude::*;
fn main() {} fn main() {}
static Example: FC<()> = |cx| { pub static Example: FC<()> = |cx| {
cx.render(rsx! { cx.render(rsx! {
div { div {

View File

@ -1,27 +1,27 @@
//! Example: Suspense //! Example: Suspense
//! ----------------- //! -----------------
//! This example shows how the "use_fetch" hook is built on top of Dioxus' "suspense" API. Suspense enables components //! This example demonstrates how the use_suspense hook can be used to load and render asynchronous data. Suspense enables
//! to wait on futures to complete before rendering the result into VNodes. These VNodes are immediately available in a //! components to wait on futures to complete before rendering the result into VNodes. These VNodes are immediately
//! "suspended" fashion and will automatically propogate to the UI when the future completes. //! available in a suspended" fashion and will automatically propogate to the UI when the future completes.
//! //!
//! Note that if your component updates or receives new props while it is awating the result, the future will be dropped //! Currently, suspense futures are non-restartable. In the future, we'll provide more granular control of how to start,
//! and all work will stop. In this example, we store the future in a hook so we can always resume it. //! stop, and reset these futures.
use dioxus::prelude::*; use dioxus::prelude::*;
fn main() {}
#[derive(serde::Deserialize)] #[derive(serde::Deserialize)]
struct DogApi { struct DogApi {
message: String, message: String,
} }
const ENDPOINT: &str = "https://dog.ceo/api/breeds/image/random"; const ENDPOINT: &str = "https://dog.ceo/api/breeds/image/random";
pub static App: FC<()> = |cx| { pub static Example: FC<()> = |cx| {
let doggo = use_future_effect(cx, move || async move { let doggo = cx.use_suspense(
match surf::get(ENDPOINT).recv_json::<DogApi>().await { || surf::get(ENDPOINT).recv_json::<DogApi>(),
|cx, res| match res {
Ok(res) => rsx!(in cx, img { src: "{res.message}" }), Ok(res) => rsx!(in cx, img { src: "{res.message}" }),
Err(_) => rsx!(in cx, div { "No doggos for you :(" }), Err(_) => rsx!(in cx, div { "No doggos for you :(" }),
} },
}); );
cx.render(rsx!( cx.render(rsx!(
div { div {
@ -30,34 +30,3 @@ pub static App: FC<()> = |cx| {
} }
)) ))
}; };
// use dioxus_core::virtual_dom::SuspendedContext;
use futures::Future;
use futures::FutureExt;
use std::pin::Pin;
fn use_fetch<'a, T, P, F>(cx: Context<P>, url: &str, g: F) -> VNode<'a>
where
T: serde::de::DeserializeOwned + 'static,
// F: FnOnce(SuspendedContext, surf::Result<T>) -> VNode<'a> + 'a,
F: FnOnce((), surf::Result<T>) -> VNode<'a> + 'a,
{
todo!()
// // essentially we're doing a "use_effect" but with no dependent props or post-render shenanigans
// let fetch_promise = cx.use_hook(
// move || surf::get(url).recv_json::<T>().boxed_local(),
// // just pass the future through
// |p| p,
// |_| (),
// );
// cx.suspend(fetch_promise, g)
}
/// Spawns the future only when the inputs change
fn use_future_effect<'a, 'b, F, P, F2>(cx: Context<P>, g: F2) -> VNode<'a>
where
F: Future<Output = VNode<'b>>,
F2: FnOnce() -> F + 'a,
{
todo!()
}

View File

@ -0,0 +1,61 @@
//! Example: Tasks
//! --------------
//!
//! Built around the same system that powers suspense, Dioxus also allows users to write arbitrary tasks that can modify
//! state asynchronously. `use_task` takes a future and returns a task handle and an option that holds the tasks's return
//! value. When the task completes, the component will re-render with the result freshly available.
//!
//! Tasks don't necessarily need to complete, however. It would be completely reasonable to wire up a websocket receiver
//! in a task and have it work infinitely while the app is running. If the socket throws an error, the task could complete
//! and the UI could present a helpful error message.
//!
//! Tasks also return the `TaskHandle` type which lets other component logic start, stop, and restart the task at any time.
//! Tasks are very much like an async-flavoroued coroutine, making them incredibly powerful.
//!
//! Tasks must be valid for the 'static lifetime, so any state management neeeds to be cloned into the closure. `use_state`
//! has a method called `for_async` which generates an AsyncUseState wrapper. This has a very similar API to the regualr
//! `use_state` but is `static.
//!
//! Remeber `use_task` is a hook! Don't call it out of order or in loops. You might aaccidentally swap the task handles
//! and break things in subtle ways.
//!
//! Whenever a component is scheduled for deletion, the task is dropped. Make sure that whatever primitives used in the
//! task have a valid drop implementation and won't leave resources tied up.
use dioxus::prelude::*;
pub static Example: FC<()> = |cx| {
let count = use_state(cx, || 0);
let mut direction = use_state(cx, || 1);
// Tasks are 'static, so we need to copy relevant items in
let (async_count, dir) = (count.for_async(), *direction);
let (task, result) = cx.use_task(move || async move {
// Count infinitely!
loop {
gloo_timers::future::TimeoutFuture::new(250).await;
*async_count.get_mut() += dir;
}
});
cx.render(rsx! {
div {
h1 {"count is {count}"}
button {
"Stop counting"
onclick: move |_| task.stop()
}
button {
"Start counting"
onclick: move |_| task.start()
}
button {
"Switch counting direcion"
onclick: move |_| {
direction *= -1;
task.restart();
}
}
}
})
};

View File

@ -1,7 +1,6 @@
use dioxus::prelude::*; use dioxus::prelude::*;
fn main() {}
static Example: FC<()> = |cx| { pub static Example: FC<()> = |cx| {
cx.render(rsx! { cx.render(rsx! {
div { div {

View File

@ -1,10 +1,25 @@
use dioxus::prelude::*; use dioxus::prelude::*;
fn main() {} use dioxus::ssr;
pub static Example: FC<()> = |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_in_place().unwrap();
ssr::render_root(&dom)
});
static Example: FC<()> = |cx| {
cx.render(rsx! { cx.render(rsx! {
div { div { "{as_string}" }
})
};
static SomeApp: FC<()> = |cx| {
cx.render(rsx! {
div { style: {background_color: "blue"}
h1 {"Some amazing app or component"}
p {"Things are great"}
} }
}) })
}; };

View File

@ -1,26 +0,0 @@
//! Example: Web Components
//! -----------------------
//!
//! Web components are a flavor of html elements that can be user-defined.
//! See: https://www.webcomponents.org for more information.
//!
//! Users who use webcomponents typically don't use Dioxus. However, if you would like to use webcomponents in Dioxus,
//! you can easily create a new custom element with compile-time correct wrappers around the webcomponent.
//!
//! We don't support building new webcomponents with Dioxus, however.
//!
fn main() {}
mod dioxus_elements {
use dioxus::prelude::DioxusElement;
struct custom_element;
impl DioxusElement for custom_element {
const TAG_NAME: &'static str = "custom_element";
const NAME_SPACE: Option<&'static str> = None;
}
// Re-export the normal html namespace
pub use dioxus::prelude::dioxus_elements::*;
}

View File

@ -49,7 +49,7 @@ const NONE_ELEMENT: Option<()> = None;
use baller::Baller; use baller::Baller;
use dioxus::prelude::*; use dioxus::prelude::*;
static Example: FC<()> = |cx| { pub static Example: FC<()> = |cx| {
let formatting = "formatting!"; let formatting = "formatting!";
let formatting_tuple = ("a", "b"); let formatting_tuple = ("a", "b");
let lazy_fmt = format_args!("lazily formatted text"); let lazy_fmt = format_args!("lazily formatted text");

View File

@ -12,7 +12,7 @@ fn main() {
const STYLE: &str = "body {overflow:hidden;}"; const STYLE: &str = "body {overflow:hidden;}";
pub const App: FC<()> = |cx| { pub static App: FC<()> = |cx| {
cx.render(rsx!( cx.render(rsx!(
div { class: "overflow-hidden" div { class: "overflow-hidden"
style { "{STYLE}" } style { "{STYLE}" }
@ -28,7 +28,7 @@ pub const App: FC<()> = |cx| {
)) ))
}; };
pub const Header: FC<()> = |cx| { pub static Header: FC<()> = |cx| {
cx.render(rsx! { cx.render(rsx! {
div { div {
header { class: "text-gray-400 bg-gray-900 body-font" header { class: "text-gray-400 bg-gray-900 body-font"
@ -54,7 +54,7 @@ pub const Header: FC<()> = |cx| {
}) })
}; };
pub const Hero: FC<()> = |cx| { pub static Hero: FC<()> = |cx| {
// //
cx.render(rsx! { cx.render(rsx! {
section{ class: "text-gray-400 bg-gray-900 body-font" section{ class: "text-gray-400 bg-gray-900 body-font"
@ -92,7 +92,7 @@ pub const Hero: FC<()> = |cx| {
} }
}) })
}; };
pub const Entry: FC<()> = |cx| { pub static Entry: FC<()> = |cx| {
// //
cx.render(rsx! { cx.render(rsx! {
section{ class: "text-gray-400 bg-gray-900 body-font" section{ class: "text-gray-400 bg-gray-900 body-font"
@ -105,7 +105,7 @@ pub const Entry: FC<()> = |cx| {
}) })
}; };
pub const StacksIcon: FC<()> = |cx| { pub static StacksIcon: FC<()> = |cx| {
cx.render(rsx!( cx.render(rsx!(
svg { svg {
// xmlns: "http://www.w3.org/2000/svg" // xmlns: "http://www.w3.org/2000/svg"
@ -120,7 +120,7 @@ pub const StacksIcon: FC<()> = |cx| {
} }
)) ))
}; };
pub const RightArrowIcon: FC<()> = |cx| { pub static RightArrowIcon: FC<()> = |cx| {
cx.render(rsx!( cx.render(rsx!(
svg { svg {
fill: "none" fill: "none"

114
examples/todomvc.rs Normal file
View File

@ -0,0 +1,114 @@
use dioxus::prelude::*;
use im_rc::HashMap;
use std::rc::Rc;
fn main() {
#[cfg(feature = "desktop")]
// #[cfg(not(target_arch = "wasm32"))]
dioxus::desktop::launch(App, |c| c);
#[cfg(feature = "desktop")]
dioxus::web::launch(App, |c| c);
}
#[derive(PartialEq)]
pub enum FilterState {
All,
Active,
Completed,
}
#[derive(Debug, PartialEq)]
pub struct TodoItem {
pub id: u32,
pub checked: bool,
pub contents: String,
}
const STYLE: &str = include_str!("./_examples/todomvc/style.css");
const App: FC<()> = |cx| {
let draft = use_state(cx, || "".to_string());
let todos = use_state(cx, || HashMap::<u32, Rc<TodoItem>>::new());
let filter = use_state(cx, || FilterState::All);
let todolist = todos
.iter()
.filter(|(id, item)| match *filter {
FilterState::All => true,
FilterState::Active => !item.checked,
FilterState::Completed => item.checked,
})
.map(|(id, todo)| {
rsx!(TodoEntry {
key: "{id}",
todo: todo.clone()
})
})
.collect::<Vec<_>>();
let items_left = todolist.len();
let item_text = match items_left {
1 => "item",
_ => "items",
};
cx.render(rsx! {
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())
}
}
{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 { "jkelleyrtp", href: "http://github.com/jkelleyrtp/" }}
p { "Part of ", a { "TodoMVC", href: "http://todomvc.com" }}
}
}
})
};
#[derive(PartialEq, Props)]
pub struct TodoEntryProps {
todo: std::rc::Rc<TodoItem>,
}
pub fn TodoEntry(cx: Context<TodoEntryProps>) -> VNode {
let TodoEntryProps { todo } = *cx;
let is_editing = use_state(cx, || false);
let contents = "";
cx.render(rsx! (
li {
"{todo.id}"
input {
class: "toggle"
r#type: "checkbox"
"{todo.checked}"
}
{is_editing.then(|| rsx!{
input {
value: "{contents}"
}
})}
}
))
}

View File

@ -12,7 +12,7 @@ https://github.com/rustwasm/gloo
For example, resize observer would function like this: For example, resize observer would function like this:
```rust ```rust
static Example: FC<()> = |cx| { pub static Example: FC<()> = |cx| {
let observer = use_resize_observer(); let observer = use_resize_observer();
cx.render(rsx!( cx.render(rsx!(
@ -29,18 +29,18 @@ For other services, we shell out to gloo. If the gloo service doesn't exist, the
| Service | Hook examples | Current Projects | | Service | Hook examples | Current Projects |
| ---------------------------- | ------------- | ---------------- | | ---------------------------- | ------------- | ---------------- |
| Fetch | 👀 | Reqwest/surf | | Fetch | 👀 | Reqwest/surf |
| Local storage (cache) | 👀 | Gloo | | Local storage (cache) | 👀 | Gloo |
| Persistent storage (IndexDB) | 👀 | 👀 | | Persistent storage (IndexDB) | 👀 | 👀 |
| WebSocket | 👀 | Gloo | | WebSocket | 👀 | Gloo |
| 3D Renderer / WebGL | 👀 | Gloo | | 3D Renderer / WebGL | 👀 | Gloo |
| Web Worker | 👀 | 👀 | | Web Worker | 👀 | 👀 |
| Router | 👀 | 👀 | | Router | 👀 | 👀 |
| Notifications | 👀 | 👀 | | Notifications | 👀 | 👀 |
| WebRTC Client | 👀 | 👀 | | WebRTC Client | 👀 | 👀 |
| Service Workers | 👀 | 👀 | | Service Workers | 👀 | 👀 |
| Resize Observer | 👀 | 👀 | | Resize Observer | 👀 | 👀 |
| Canvas | 👀 | 👀 | | Canvas | 👀 | 👀 |
| Clipboard | 👀 | 👀 | | Clipboard | 👀 | 👀 |
| Fullscreen | 👀 | 👀 | | Fullscreen | 👀 | 👀 |
| History API | 👀 | 👀 | | History API | 👀 | 👀 |

View File

@ -153,13 +153,13 @@ Notice that LiveComponent receivers (the client-side interpretation of a LiveCom
The `VNodeTree` type is a very special type that allows VNodes to be created using a pluggable allocator. The html! macro creates something that looks like: The `VNodeTree` type is a very special type that allows VNodes to be created using a pluggable allocator. The html! macro creates something that looks like:
```rust ```rust
static Example: FC<()> = |cx| { pub static Example: FC<()> = |cx| {
html! { <div> "blah" </div> } html! { <div> "blah" </div> }
}; };
// expands to... // expands to...
static Example: FC<()> = |cx| { pub static Example: FC<()> = |cx| {
// This function converts a Fn(allocator) -> VNode closure to a VNode struct that will later be evaluated. // This function converts a Fn(allocator) -> VNode closure to a VNode struct that will later be evaluated.
html_macro_to_vnodetree(move |allocator| { html_macro_to_vnodetree(move |allocator| {
let mut node0 = allocator.alloc(VElement::div); let mut node0 = allocator.alloc(VElement::div);

View File

@ -39,32 +39,18 @@ impl Parse for AmbiguousElement {
} }
} }
// if input.peek(Ident) {
// let name_str = input.fork().parse::<Ident>().unwrap().to_string();
// } else {
// }
if let Ok(name) = input.fork().parse::<Ident>() { if let Ok(name) = input.fork().parse::<Ident>() {
let name_str = name.to_string(); let name_str = name.to_string();
match is_valid_tag(&name_str) { let first_char = name_str.chars().next().unwrap();
true => input if first_char.is_ascii_uppercase() {
input
.parse::<Component>()
.map(|c| AmbiguousElement::Component(c))
} else {
input
.parse::<Element>() .parse::<Element>()
.map(|c| AmbiguousElement::Element(c)), .map(|c| AmbiguousElement::Element(c))
false => {
let first_char = name_str.chars().next().unwrap();
if first_char.is_ascii_uppercase() {
input
.parse::<Component>()
.map(|c| AmbiguousElement::Component(c))
} else {
let name = input.parse::<Ident>().unwrap();
Err(Error::new(
name.span(),
"Components must be uppercased, perhaps you mispelled a html tag",
))
}
}
} }
} else { } else {
if input.peek(LitStr) { if input.peek(LitStr) {

View File

@ -96,7 +96,7 @@ pub fn parse_component_body(
} }
content.parse::<Token![..]>()?; content.parse::<Token![..]>()?;
*manual_props = Some(content.parse::<Expr>()?); *manual_props = Some(content.parse::<Expr>()?);
} else if content.peek(Ident) && content.peek2(Token![:]) { } else if content.peek(Ident) && content.peek2(Token![:]) && !content.peek3(Token![:]) {
if !cfg.allow_fields { if !cfg.allow_fields {
return Err(Error::new( return Err(Error::new(
content.span(), content.span(),
@ -184,7 +184,7 @@ impl ToTokens for Component {
}; };
tokens.append_all(quote! { tokens.append_all(quote! {
__cx.virtual_child( __cx.component(
#name, #name,
#builder, #builder,
#key_token, #key_token,

View File

@ -43,11 +43,6 @@ impl Parse for Element {
fn parse(stream: ParseStream) -> Result<Self> { fn parse(stream: ParseStream) -> Result<Self> {
let name = Ident::parse(stream)?; let name = Ident::parse(stream)?;
// TODO: remove this in favor of the compile-time validation system
if !crate::util::is_valid_tag(&name.to_string()) {
return Err(Error::new(name.span(), "Not a valid Html tag"));
}
// parse the guts // parse the guts
let content: ParseBuffer; let content: ParseBuffer;
syn::braced!(content in stream); syn::braced!(content in stream);

View File

@ -4,7 +4,7 @@ fn main() {}
// use dioxus_core as dioxus; // use dioxus_core as dioxus;
// use dioxus_core::prelude::*; // use dioxus_core::prelude::*;
// static Example: FC<()> = |cx| { // pub static Example: FC<()> = |cx| {
// let list = (0..10).map(|f| LazyNodes::new(move |f| todo!())); // let list = (0..10).map(|f| LazyNodes::new(move |f| todo!()));
// cx.render(LazyNodes::new(move |cx| { // cx.render(LazyNodes::new(move |cx| {

View File

@ -5,12 +5,8 @@ struct SomeContext {
items: Vec<String>, items: Vec<String>,
} }
struct Props {
name: String,
}
#[allow(unused)] #[allow(unused)]
static Example: FC<Props> = |cpt| { static Example: FC<()> = |cpt| {
todo!() todo!()
// let value = cx.use_context(|c: &SomeContext| c.items.last().unwrap()); // let value = cx.use_context(|c: &SomeContext| c.items.last().unwrap());

View File

@ -321,7 +321,10 @@ Any function prefixed with "use" should not be called conditionally.
/// ///
/// ///
/// ///
pub fn use_task<Out, Fut, Init>(&self, task_initializer: Init) -> (&TaskHandle, &Option<Out>) pub fn use_task<Out, Fut, Init>(
self,
task_initializer: Init,
) -> (TaskHandle<'src>, &'src Option<Out>)
where where
Out: 'static, Out: 'static,
Fut: Future<Output = Out> + 'static, Fut: Future<Output = Out> + 'static,
@ -363,7 +366,7 @@ Any function prefixed with "use" should not be called conditionally.
if let Some(val) = hook.task_dump.as_ref().borrow_mut().take() { if let Some(val) = hook.task_dump.as_ref().borrow_mut().take() {
hook.value = Some(val); hook.value = Some(val);
} }
(&TaskHandle { _p: PhantomData }, &hook.value) (TaskHandle { _p: PhantomData }, &hook.value)
}, },
|_| {}, |_| {},
) )
@ -463,6 +466,14 @@ impl<'src, P> Context<'src, P> {
} }
} }
#[derive(Clone, Copy)]
pub struct TaskHandle<'src> { pub struct TaskHandle<'src> {
_p: PhantomData<&'src ()>, _p: PhantomData<&'src ()>,
} }
impl<'src> TaskHandle<'src> {
pub fn toggle(&self) {}
pub fn start(&self) {}
pub fn stop(&self) {}
pub fn restart(&self) {}
}

View File

@ -266,7 +266,7 @@ impl<'a> NodeFactory<'a> {
} }
} }
pub fn virtual_child<P>( pub fn component<P>(
&self, &self,
component: FC<P>, component: FC<P>,
props: P, props: P,

View File

@ -123,7 +123,14 @@ impl<'a, T: 'static> UseState<'a, T> {
let slot = self.inner.wip.clone(); let slot = self.inner.wip.clone();
Rc::new(move |new| *slot.borrow_mut() = Some(new)) Rc::new(move |new| *slot.borrow_mut() = Some(new))
} }
pub fn for_async(&self) -> AsyncUseState<T> {
AsyncUseState {
wip: self.inner.wip.clone(),
}
}
} }
impl<'a, T: 'static + ToOwned<Owned = T>> UseState<'a, T> { impl<'a, T: 'static + ToOwned<Owned = T>> UseState<'a, T> {
pub fn get_mut(self) -> RefMut<'a, T> { pub fn get_mut(self) -> RefMut<'a, T> {
// make sure we get processed // make sure we get processed
@ -175,9 +182,55 @@ impl<'a, T: Copy + Sub<T, Output = T>> SubAssign<T> for UseState<'a, T> {
} }
} }
/// MUL
impl<'a, T: Copy + Mul<T, Output = T>> Mul<T> for UseState<'a, T> {
type Output = T;
fn mul(self, rhs: T) -> Self::Output {
self.inner.current_val.mul(rhs)
}
}
impl<'a, T: Copy + Mul<T, Output = T>> MulAssign<T> for UseState<'a, T> {
fn mul_assign(&mut self, rhs: T) {
self.set(self.inner.current_val.mul(rhs));
}
}
/// DIV
impl<'a, T: Copy + Div<T, Output = T>> Div<T> for UseState<'a, T> {
type Output = T;
fn div(self, rhs: T) -> Self::Output {
self.inner.current_val.div(rhs)
}
}
impl<'a, T: Copy + Div<T, Output = T>> DivAssign<T> for UseState<'a, T> {
fn div_assign(&mut self, rhs: T) {
self.set(self.inner.current_val.div(rhs));
}
}
// enable displaty for the handle // enable displaty for the handle
impl<'a, T: 'static + Display> std::fmt::Display for UseState<'a, T> { impl<'a, T: 'static + Display> std::fmt::Display for UseState<'a, T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.inner.current_val) write!(f, "{}", self.inner.current_val)
} }
} }
/// A less ergonmic but still capable form of use_state that's valid for `static lifetime
pub struct AsyncUseState<T: 'static> {
wip: Rc<RefCell<Option<T>>>,
}
impl<T: ToOwned> AsyncUseState<T> {
pub fn get_mut<'a>(&'a self) -> RefMut<'a, T> {
// make sure we get processed
// self.needs_update();
// Bring out the new value, cloning if it we need to
// "get_mut" is locked behind "ToOwned" to make it explicit that cloning occurs to use this
RefMut::map(self.wip.borrow_mut(), |slot| {
//
slot.as_mut().unwrap()
})
}
}

View File

@ -924,6 +924,8 @@ builder_constructors! {
/// [`<div>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div) /// [`<div>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div)
/// element. /// element.
/// ///
/// Part of the HTML namespace. Only works in HTML-compatible renderers
///
/// ## Definition and Usage /// ## Definition and Usage
/// - The <div> tag defines a division or a section in an HTML document. /// - The <div> tag defines a division or a section in an HTML document.
/// - The <div> tag is used as a container for HTML elements - which is then styled with CSS or manipulated with JavaScript. /// - The <div> tag is used as a container for HTML elements - which is then styled with CSS or manipulated with JavaScript.
@ -1575,6 +1577,7 @@ builder_constructors! {
/// [`<select>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select) /// [`<select>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select)
/// element. /// element.
select { select {
value: String,
autocomplete: String, autocomplete: String,
autofocus: Bool, autofocus: Bool,
disabled: Bool, disabled: Bool,

View File

@ -9,7 +9,8 @@ use wry::application::window::Fullscreen;
use wry::application::{ use wry::application::{
dpi::LogicalSize, dpi::LogicalSize,
event::StartCause, event::StartCause,
platform::ios::{ScreenEdge, WindowBuilderExtIOS, WindowExtIOS}, // platform::ios::{ScreenEdge, WindowBuilderExtIOS, WindowExtIOS},
// platform::ios::{ScreenEdge, WindowBuilderExtIOS, WindowExtIOS},
}; };
use wry::webview::WebViewBuilder; use wry::webview::WebViewBuilder;
use wry::{ use wry::{
@ -23,7 +24,7 @@ fn init_logging() {
simple_logger::SimpleLogger::new().init().unwrap(); simple_logger::SimpleLogger::new().init().unwrap();
} }
static HTML_CONTENT: &'static str = include_str!("./../../webview/src/index.html"); static HTML_CONTENT: &'static str = include_str!("../../desktop/src/index.html");
pub fn launch(root: FC<()>, builder: fn(WindowBuilder) -> WindowBuilder) -> anyhow::Result<()> { pub fn launch(root: FC<()>, builder: fn(WindowBuilder) -> WindowBuilder) -> anyhow::Result<()> {
launch_with_props(root, (), builder) launch_with_props(root, (), builder)

View File

@ -38,7 +38,7 @@ struct ExampleProps {
initial_name: String, initial_name: String,
} }
static Example: FC<ExampleProps> = |cx| { pub static Example: FC<ExampleProps> = |cx| {
let dispaly_name = use_state(cx, move || cx.initial_name.clone()); let dispaly_name = use_state(cx, move || cx.initial_name.clone());
cx.render(rsx! { cx.render(rsx! {

View File

@ -19,7 +19,7 @@ fn main() {
.unwrap(); .unwrap();
} }
pub const App: FC<()> = |cx| { pub static App: FC<()> = |cx| {
cx.render(rsx!( cx.render(rsx!(
div { class: "overflow-hidden" div { class: "overflow-hidden"
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" }
@ -34,7 +34,7 @@ pub const App: FC<()> = |cx| {
)) ))
}; };
pub const Header: FC<()> = |cx| { pub static Header: FC<()> = |cx| {
cx.render(rsx! { cx.render(rsx! {
div { div {
header { class: "text-gray-400 bg-gray-900 body-font" header { class: "text-gray-400 bg-gray-900 body-font"
@ -60,7 +60,7 @@ pub const Header: FC<()> = |cx| {
}) })
}; };
pub const Hero: FC<()> = |cx| { pub static Hero: FC<()> = |cx| {
// //
cx.render(rsx! { cx.render(rsx! {
section{ class: "text-gray-400 bg-gray-900 body-font" section{ class: "text-gray-400 bg-gray-900 body-font"
@ -98,7 +98,7 @@ pub const Hero: FC<()> = |cx| {
} }
}) })
}; };
pub const Entry: FC<()> = |cx| { pub static Entry: FC<()> = |cx| {
// //
cx.render(rsx! { cx.render(rsx! {
section{ class: "text-gray-400 bg-gray-900 body-font" section{ class: "text-gray-400 bg-gray-900 body-font"
@ -111,7 +111,7 @@ pub const Entry: FC<()> = |cx| {
}) })
}; };
pub const StacksIcon: FC<()> = |cx| { pub static StacksIcon: FC<()> = |cx| {
cx.render(rsx!( cx.render(rsx!(
svg { svg {
// xmlns: "http://www.w3.org/2000/svg" // xmlns: "http://www.w3.org/2000/svg"
@ -126,7 +126,7 @@ pub const StacksIcon: FC<()> = |cx| {
} }
)) ))
}; };
pub const RightArrowIcon: FC<()> = |cx| { pub static RightArrowIcon: FC<()> = |cx| {
cx.render(rsx!( cx.render(rsx!(
svg { svg {
fill: "none" fill: "none"

View File

@ -25,7 +25,7 @@ fn main() {
wasm_bindgen_futures::spawn_local(WebsysRenderer::start(App)); wasm_bindgen_futures::spawn_local(WebsysRenderer::start(App));
} }
pub const App: FC<()> = |cx| { pub static App: FC<()> = |cx| {
cx.render(rsx!( cx.render(rsx!(
div { class: "overflow-hidden" div { class: "overflow-hidden"
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" }
@ -40,7 +40,7 @@ pub const App: FC<()> = |cx| {
)) ))
}; };
pub const Header: FC<()> = |cx| { pub static Header: FC<()> = |cx| {
cx.render(rsx! { cx.render(rsx! {
div { div {
header { class: "text-gray-400 bg-gray-900 body-font" header { class: "text-gray-400 bg-gray-900 body-font"
@ -66,7 +66,7 @@ pub const Header: FC<()> = |cx| {
}) })
}; };
pub const Hero: FC<()> = |cx| { pub static Hero: FC<()> = |cx| {
// //
cx.render(rsx! { cx.render(rsx! {
section{ class: "text-gray-400 bg-gray-900 body-font" section{ class: "text-gray-400 bg-gray-900 body-font"
@ -104,7 +104,7 @@ pub const Hero: FC<()> = |cx| {
} }
}) })
}; };
pub const Entry: FC<()> = |cx| { pub static Entry: FC<()> = |cx| {
// //
cx.render(rsx! { cx.render(rsx! {
section{ class: "text-gray-400 bg-gray-900 body-font" section{ class: "text-gray-400 bg-gray-900 body-font"
@ -117,7 +117,7 @@ pub const Entry: FC<()> = |cx| {
}) })
}; };
pub const StacksIcon: FC<()> = |cx| { pub static StacksIcon: FC<()> = |cx| {
cx.render(rsx!( cx.render(rsx!(
svg { svg {
// xmlns: "http://www.w3.org/2000/svg" // xmlns: "http://www.w3.org/2000/svg"
@ -132,7 +132,7 @@ pub const StacksIcon: FC<()> = |cx| {
} }
)) ))
}; };
pub const RightArrowIcon: FC<()> = |cx| { pub static RightArrowIcon: FC<()> = |cx| {
cx.render(rsx!( cx.render(rsx!(
svg { svg {
fill: "none" fill: "none"

View File

@ -93,7 +93,7 @@
//! Dioxus uses hooks for state management. Hooks are a form of state persisted between calls of the function component. //! Dioxus uses hooks for state management. Hooks are a form of state persisted between calls of the function component.
//! //!
//! ``` //! ```
//! static Example: FC<()> = |cx| { //! pub static Example: FC<()> = |cx| {
//! let (val, set_val) = use_state(cx, || 0); //! let (val, set_val) = use_state(cx, || 0);
//! cx.render(rsx!( //! cx.render(rsx!(
//! button { onclick: move |_| set_val(val + 1) } //! button { onclick: move |_| set_val(val + 1) }
@ -156,7 +156,7 @@
//! diouxs::web::launch(Example); //! diouxs::web::launch(Example);
//! } //! }
//! //!
//! static Example: FC<()> = |cx| { //! pub static Example: FC<()> = |cx| {
//! cx.render(rsx! { //! cx.render(rsx! {
//! div { "Hello World!" } //! div { "Hello World!" }
//! }) //! })
@ -188,6 +188,8 @@ pub use dioxus_hooks as hooks;
#[cfg(feature = "desktop")] #[cfg(feature = "desktop")]
pub use dioxus_desktop as desktop; pub use dioxus_desktop as desktop;
pub mod debug {}
pub mod prelude { pub mod prelude {
//! A glob import that includes helper types like FC, rsx!, html!, and required traits //! A glob import that includes helper types like FC, rsx!, html!, and required traits
pub use dioxus_core::prelude::*; pub use dioxus_core::prelude::*;