docs: big updates to the reference
This commit is contained in:
parent
caf772cf24
commit
583fdfa561
|
@ -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 = [
|
||||||
|
|
|
@ -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";
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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! {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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}"}
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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....");
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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! {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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| {
|
||||||
|
|
|
@ -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 {}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
use dioxus::prelude::*;
|
|
||||||
fn main() {}
|
|
||||||
|
|
||||||
static Example: FC<()> = |cx| {
|
|
||||||
cx.render(rsx! {
|
|
||||||
div {
|
|
||||||
|
|
||||||
}
|
|
||||||
})
|
|
||||||
};
|
|
|
@ -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()}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
|
@ -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!" }
|
||||||
|
|
|
@ -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 }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
|
@ -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"}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
|
@ -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};
|
||||||
|
}
|
||||||
|
|
|
@ -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 {} });
|
||||||
|
|
|
@ -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 {}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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!"
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
|
@ -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}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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| {
|
||||||
|
|
|
@ -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")),
|
||||||
|
];
|
||||||
|
|
|
@ -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(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
||||||
|
|
|
@ -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}"}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
||||||
|
|
|
@ -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!()
|
|
||||||
}
|
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
|
@ -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 {
|
||||||
|
|
||||||
|
|
|
@ -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"}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
|
@ -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::*;
|
|
||||||
}
|
|
|
@ -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");
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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}"
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}
|
|
@ -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 | 👀 | 👀 |
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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| {
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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) {}
|
||||||
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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! {
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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::*;
|
||||||
|
|
Loading…
Reference in New Issue