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]
|
||||
# core
|
||||
default = ["core"]
|
||||
default = ["core", "ssr"]
|
||||
core = ["macro", "hooks", "html"]
|
||||
macro = ["dioxus-core-macro"]
|
||||
hooks = ["dioxus-hooks"]
|
||||
|
@ -48,6 +48,7 @@ async-std = { version = "1.9.0", features = ["attributes"] }
|
|||
im-rc = "15.0.0"
|
||||
rand = { version = "0.8.4", features = ["small_rng"] }
|
||||
fxhash = "0.2.1"
|
||||
gloo-timers = "0.2.1"
|
||||
|
||||
[workspace]
|
||||
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.
|
||||
|
||||
```rust
|
||||
static Example: FC<()> = |cx| {
|
||||
pub static Example: FC<()> = |cx| {
|
||||
|
||||
let text = "example";
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ fn Example(cx: Context, name: String) -> VNode {
|
|||
// or
|
||||
|
||||
#[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.
|
||||
|
|
|
@ -25,7 +25,7 @@ fn main() {
|
|||
#[derive(Debug)]
|
||||
struct CustomContext([&'static str; 3]);
|
||||
|
||||
static Example: FC<()> = |cx| {
|
||||
pub static Example: FC<()> = |cx| {
|
||||
cx.use_create_context(|| CustomContext(["Jack", "Jill", "Bob"]));
|
||||
|
||||
cx.render(rsx! {
|
||||
|
|
|
@ -13,7 +13,7 @@ fn main() {
|
|||
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"}));
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
|
|
|
@ -14,7 +14,7 @@ fn main() {
|
|||
}
|
||||
|
||||
// this is a component
|
||||
static Example: FC<()> = |cx| {
|
||||
pub static Example: FC<()> = |cx| {
|
||||
let (event, set_event) = use_state_classic(cx, || None);
|
||||
|
||||
let handler = move |evt| {
|
||||
|
@ -50,7 +50,7 @@ struct ExampleProps {
|
|||
name: String,
|
||||
}
|
||||
|
||||
static Example2: FC<ExampleProps> = |cx| {
|
||||
pub static Example2: FC<ExampleProps> = |cx| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
h1 {"hello {cx.name}"}
|
||||
|
|
|
@ -37,7 +37,7 @@ static App: FC<()> = |cx| {
|
|||
})
|
||||
};
|
||||
|
||||
static Example: FC<()> = |cx| {
|
||||
pub static Example: FC<()> = |cx| {
|
||||
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: "container py-5 max-w-md mx-auto"
|
||||
|
|
|
@ -9,7 +9,7 @@ fn main() {
|
|||
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, || "...?");
|
||||
|
||||
log::debug!("Running component....");
|
||||
|
|
|
@ -9,7 +9,7 @@ fn main() {
|
|||
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, || "...?");
|
||||
cx.render(rsx! {
|
||||
section { class: "py-12 px-4 text-center"
|
||||
|
|
|
@ -11,7 +11,7 @@ fn main() {
|
|||
wasm_bindgen_futures::spawn_local(WebsysRenderer::start(Example));
|
||||
}
|
||||
|
||||
static Example: FC<()> = |cx| {
|
||||
pub static Example: FC<()> = |cx| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
span {
|
||||
|
|
|
@ -27,7 +27,7 @@ struct ExampleProps {
|
|||
initial_name: &'static str,
|
||||
}
|
||||
|
||||
static Example: FC<ExampleProps> = |cx| {
|
||||
pub static Example: FC<ExampleProps> = |cx| {
|
||||
let name = use_state(cx, move || cx.initial_name);
|
||||
|
||||
cx.render(rsx! {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use dioxus::prelude::*;
|
||||
fn main() {}
|
||||
|
||||
static Example: FC<()> = |cx| {
|
||||
pub static Example: FC<()> = |cx| {
|
||||
let (g, set_g) = use_state(cx, || 0).classic();
|
||||
let v = (0..10).map(move |f| {
|
||||
rsx!(li {
|
||||
|
|
|
@ -17,7 +17,7 @@ fn main() {
|
|||
.expect("Webview finished");
|
||||
}
|
||||
|
||||
// static Example: FC<()> = |cx| {
|
||||
// pub static Example: FC<()> = |cx| {
|
||||
// cx.render(html! {
|
||||
// <div>
|
||||
// <svg class="octicon octicon-star v-align-text-bottom"
|
||||
|
@ -36,7 +36,7 @@ fn main() {
|
|||
// </div>
|
||||
// })
|
||||
// };
|
||||
static Example: FC<()> = |cx| {
|
||||
pub static Example: FC<()> = |cx| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
class: "flex items-center justify-center flex-col"
|
||||
|
|
|
@ -20,12 +20,8 @@ use dioxus::prelude::*;
|
|||
|
||||
const STYLE: &str = include_str!("./assets/calculator.css");
|
||||
fn main() {
|
||||
dioxus::desktop::launch(App, |cfg| {
|
||||
cfg.with_title("Calculator Demo")
|
||||
.with_resizable(true)
|
||||
.with_skip_taskbar(true)
|
||||
})
|
||||
.expect("failed to launch dioxus app");
|
||||
dioxus::desktop::launch(App, |cfg| cfg.with_title("Calculator Demo"))
|
||||
.expect("failed to launch dioxus app");
|
||||
}
|
||||
|
||||
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.
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {}
|
||||
|
||||
/// 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
|
||||
/// libraries.
|
||||
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::*;
|
||||
fn main() {}
|
||||
//! Example: The basics of Dioxus
|
||||
//! ----------------------------
|
||||
//!
|
||||
//! 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! {
|
||||
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.
|
||||
|
||||
use dioxus::prelude::*;
|
||||
fn main() {}
|
||||
|
||||
static App: FC<()> = |cx| {
|
||||
pub static Example: FC<()> = |cx| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
Banner {
|
||||
|
@ -32,7 +31,7 @@ static App: FC<()> = |cx| {
|
|||
})
|
||||
};
|
||||
|
||||
static Banner: FC<()> = |cx| {
|
||||
pub static Banner: FC<()> = |cx| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
h1 { "This is a great banner!" }
|
||||
|
|
|
@ -11,14 +11,12 @@
|
|||
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {}
|
||||
|
||||
// Convert a boolean conditional into a hide/show
|
||||
#[derive(PartialEq, Props)]
|
||||
struct MyProps {
|
||||
pub struct MyProps {
|
||||
should_show: bool,
|
||||
}
|
||||
static Example: FC<MyProps> = |cx| {
|
||||
pub static Example0: FC<MyProps> = |cx| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
{cx.should_show.then(|| rsx!{
|
||||
|
@ -38,10 +36,10 @@ static Example: FC<MyProps> = |cx| {
|
|||
// In short:
|
||||
// `rsx!(in cx, ...)` is shorthand for `cx.render(rsx!(...))`
|
||||
#[derive(PartialEq, Props)]
|
||||
struct MyProps1 {
|
||||
pub struct MyProps1 {
|
||||
should_show: bool,
|
||||
}
|
||||
static Example1: FC<MyProps1> = |cx| {
|
||||
pub static Example1: FC<MyProps1> = |cx| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
// With matching
|
||||
|
@ -70,16 +68,16 @@ static Example1: FC<MyProps1> = |cx| {
|
|||
/// Matching can be expanded
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum Color {
|
||||
pub enum Color {
|
||||
Green,
|
||||
Yellow,
|
||||
Red,
|
||||
}
|
||||
#[derive(PartialEq, Props)]
|
||||
struct MyProps2 {
|
||||
pub struct MyProps2 {
|
||||
color: Color,
|
||||
}
|
||||
static Example2: FC<MyProps2> = |cx| {
|
||||
pub static Example2: FC<MyProps2> = |cx| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
{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::*;
|
||||
fn main() {}
|
||||
|
||||
static Example: FC<()> = |cx| {
|
||||
pub static Example: FC<()> = |cx| {
|
||||
cx.render(rsx! {
|
||||
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::*;
|
||||
fn main() {}
|
||||
//! Example: Web Components & Custom Elements
|
||||
//! -----------------------------------------
|
||||
//!
|
||||
//! 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! {
|
||||
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!
|
||||
|
||||
fn main() {}
|
||||
|
||||
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
|
||||
|
||||
use dioxus::prelude::*;
|
||||
fn main() {}
|
||||
|
||||
// Returning multiple elements with rsx! or html!
|
||||
static Example1: FC<()> = |cx| {
|
||||
static App1: FC<()> = |cx| {
|
||||
cx.render(rsx! {
|
||||
h1 { }
|
||||
h2 { }
|
||||
h3 { }
|
||||
// {}
|
||||
// "hello world"
|
||||
})
|
||||
};
|
||||
|
||||
// Using the Fragment component
|
||||
static Example2: FC<()> = |cx| {
|
||||
static App2: FC<()> = |cx| {
|
||||
cx.render(rsx! {
|
||||
Fragment {
|
||||
div {}
|
||||
|
@ -34,7 +31,7 @@ static Example2: FC<()> = |cx| {
|
|||
};
|
||||
|
||||
// Using the `fragment` method on the NodeFactory
|
||||
static Example3: FC<()> = |cx| {
|
||||
static App3: FC<()> = |cx| {
|
||||
cx.render(LazyNodes::new(move |fac| {
|
||||
fac.fragment_from_iter([
|
||||
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.
|
||||
|
||||
use dioxus::prelude::*;
|
||||
fn main() {}
|
||||
|
||||
const STYLE: &str = r#"
|
||||
body {background-color: powderblue;}
|
||||
|
@ -20,7 +19,7 @@ h1 {color: blue;}
|
|||
p {color: red;}
|
||||
"#;
|
||||
|
||||
const Example: FC<()> = |cx| {
|
||||
pub static Example: FC<()> = |cx| {
|
||||
cx.render(rsx! {
|
||||
head { style { "{STYLE}" } }
|
||||
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::*;
|
||||
fn main() {}
|
||||
|
||||
static Example: FC<()> = |cx| {
|
||||
pub static Example: FC<()> = |cx| {
|
||||
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.
|
||||
//!
|
||||
//! 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::*;
|
||||
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| {
|
||||
rsx! {
|
||||
li {
|
||||
onclick: move |_| g.set(f)
|
||||
li { onclick: move |_| example_data.set(f)
|
||||
"ID: {f}"
|
||||
ul {
|
||||
{(0..10).map(|k| rsx!{
|
||||
|
@ -31,7 +31,7 @@ static Example: FC<()> = |cx| {
|
|||
});
|
||||
|
||||
cx.render(rsx! {
|
||||
h3 {"Selected: {g}"}
|
||||
h3 {"Selected: {example_data}"}
|
||||
ul {
|
||||
{v}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,13 @@
|
|||
|
||||
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.
|
||||
static ButtonList: FC<()> = |cx| {
|
||||
|
|
|
@ -1,7 +1,170 @@
|
|||
// mod iterator;
|
||||
// mod memo;
|
||||
#![allow(
|
||||
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 listener;
|
||||
mod memo;
|
||||
mod noderefs;
|
||||
mod signals;
|
||||
mod spreadpattern;
|
||||
mod statemanagement;
|
||||
mod suspense;
|
||||
mod task;
|
||||
mod testing;
|
||||
mod tostring;
|
||||
|
||||
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.
|
||||
|
||||
use dioxus::prelude::*;
|
||||
fn main() {}
|
||||
|
||||
// By default, components with no props are always memoized.
|
||||
// A props of () is considered empty.
|
||||
static Example: FC<()> = |cx| {
|
||||
pub static Example: FC<()> = |cx| {
|
||||
cx.render(rsx! {
|
||||
div { "100% memoized!" }
|
||||
})
|
||||
|
@ -32,11 +31,11 @@ static Example: FC<()> = |cx| {
|
|||
// However, the parent *must* create a new string on every render.
|
||||
// Notice how these props implement PartialEq - this is required for 'static props
|
||||
#[derive(PartialEq, Props)]
|
||||
struct MyProps1 {
|
||||
pub struct MyProps1 {
|
||||
name: String,
|
||||
}
|
||||
|
||||
static Example1: FC<MyProps1> = |cx| {
|
||||
pub static Example1: FC<MyProps1> = |cx| {
|
||||
cx.render(rsx! {
|
||||
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.
|
||||
// These strings cannot be modified, but may be cheaply shared in many places without issue.
|
||||
#[derive(PartialEq, Props)]
|
||||
struct MyProps2 {
|
||||
pub struct MyProps2 {
|
||||
name: std::rc::Rc<str>,
|
||||
}
|
||||
|
||||
static Example2: FC<MyProps2> = |cx| {
|
||||
pub static Example2: FC<MyProps2> = |cx| {
|
||||
cx.render(rsx! {
|
||||
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!.
|
||||
#[derive(PartialEq, Props)]
|
||||
struct MyProps3<'a> {
|
||||
pub struct MyProps3<'a> {
|
||||
name: &'a str,
|
||||
}
|
||||
// We need to manually specify a lifetime that ensures props and scope (the component's state) share the same lifetime.
|
||||
// Using the `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
|
||||
fn Example3<'a>(cx: Context<'a, MyProps3<'a>>) -> VNode {
|
||||
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!.
|
||||
// However, they cannot be compared, so we don't need the PartialEq flag.
|
||||
#[derive(Props)]
|
||||
struct MyProps4<'a> {
|
||||
pub struct MyProps4<'a> {
|
||||
onhandle: &'a dyn Fn(),
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use dioxus::prelude::*;
|
||||
fn main() {}
|
||||
|
||||
static Example: FC<()> = |cx| {
|
||||
pub static Example: FC<()> = |cx| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use dioxus::prelude::*;
|
||||
fn main() {}
|
||||
|
||||
static Example: FC<()> = |cx| {
|
||||
pub static Example: FC<()> = |cx| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
|
||||
|
|
|
@ -8,27 +8,26 @@
|
|||
//! values, using the manual props as the base and then modifying fields specified with non-spread attributes.
|
||||
|
||||
use dioxus::prelude::*;
|
||||
fn main() {}
|
||||
|
||||
static App: FC<()> = |cx| {
|
||||
pub static Example: FC<()> = |cx| {
|
||||
let props = MyProps {
|
||||
count: 0,
|
||||
live: true,
|
||||
name: "Dioxus",
|
||||
};
|
||||
cx.render(rsx! {
|
||||
Example { ..props, count: 10, div {"child"} }
|
||||
Example1 { ..props, count: 10, div {"child"} }
|
||||
})
|
||||
};
|
||||
|
||||
#[derive(PartialEq, Props)]
|
||||
struct MyProps {
|
||||
pub struct MyProps {
|
||||
count: u32,
|
||||
live: bool,
|
||||
name: &'static str,
|
||||
}
|
||||
|
||||
static Example: FC<MyProps> = |cx| {
|
||||
pub static Example1: FC<MyProps> = |cx| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
h1 { "Hello, {cx.name}"}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use dioxus::prelude::*;
|
||||
fn main() {}
|
||||
|
||||
static Example: FC<()> = |cx| {
|
||||
pub static Example: FC<()> = |cx| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
|
||||
|
|
|
@ -1,27 +1,27 @@
|
|||
//! Example: Suspense
|
||||
//! -----------------
|
||||
//! This example shows how the "use_fetch" hook is built on top of Dioxus' "suspense" API. Suspense enables components
|
||||
//! to wait on futures to complete before rendering the result into VNodes. These VNodes are immediately available in a
|
||||
//! "suspended" fashion and will automatically propogate to the UI when the future completes.
|
||||
//! This example demonstrates how the use_suspense hook can be used to load and render asynchronous data. Suspense enables
|
||||
//! components to wait on futures to complete before rendering the result into VNodes. These VNodes are immediately
|
||||
//! 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
|
||||
//! and all work will stop. In this example, we store the future in a hook so we can always resume it.
|
||||
//! Currently, suspense futures are non-restartable. In the future, we'll provide more granular control of how to start,
|
||||
//! stop, and reset these futures.
|
||||
|
||||
use dioxus::prelude::*;
|
||||
fn main() {}
|
||||
#[derive(serde::Deserialize)]
|
||||
struct DogApi {
|
||||
message: String,
|
||||
}
|
||||
const ENDPOINT: &str = "https://dog.ceo/api/breeds/image/random";
|
||||
|
||||
pub static App: FC<()> = |cx| {
|
||||
let doggo = use_future_effect(cx, move || async move {
|
||||
match surf::get(ENDPOINT).recv_json::<DogApi>().await {
|
||||
pub static Example: FC<()> = |cx| {
|
||||
let doggo = cx.use_suspense(
|
||||
|| surf::get(ENDPOINT).recv_json::<DogApi>(),
|
||||
|cx, res| match res {
|
||||
Ok(res) => rsx!(in cx, img { src: "{res.message}" }),
|
||||
Err(_) => rsx!(in cx, div { "No doggos for you :(" }),
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
cx.render(rsx!(
|
||||
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::*;
|
||||
fn main() {}
|
||||
|
||||
static Example: FC<()> = |cx| {
|
||||
pub static Example: FC<()> = |cx| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
|
||||
|
|
|
@ -1,10 +1,25 @@
|
|||
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! {
|
||||
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 dioxus::prelude::*;
|
||||
|
||||
static Example: FC<()> = |cx| {
|
||||
pub static Example: FC<()> = |cx| {
|
||||
let formatting = "formatting!";
|
||||
let formatting_tuple = ("a", "b");
|
||||
let lazy_fmt = format_args!("lazily formatted text");
|
||||
|
|
|
@ -12,7 +12,7 @@ fn main() {
|
|||
|
||||
const STYLE: &str = "body {overflow:hidden;}";
|
||||
|
||||
pub const App: FC<()> = |cx| {
|
||||
pub static App: FC<()> = |cx| {
|
||||
cx.render(rsx!(
|
||||
div { class: "overflow-hidden"
|
||||
style { "{STYLE}" }
|
||||
|
@ -28,7 +28,7 @@ pub const App: FC<()> = |cx| {
|
|||
))
|
||||
};
|
||||
|
||||
pub const Header: FC<()> = |cx| {
|
||||
pub static Header: FC<()> = |cx| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
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! {
|
||||
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! {
|
||||
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!(
|
||||
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!(
|
||||
svg {
|
||||
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:
|
||||
|
||||
```rust
|
||||
static Example: FC<()> = |cx| {
|
||||
pub static Example: FC<()> = |cx| {
|
||||
let observer = use_resize_observer();
|
||||
|
||||
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 |
|
||||
| ---------------------------- | ------------- | ---------------- |
|
||||
| Fetch | 👀 | Reqwest/surf |
|
||||
| Local storage (cache) | 👀 | Gloo |
|
||||
| Persistent storage (IndexDB) | 👀 | 👀 |
|
||||
| WebSocket | 👀 | Gloo |
|
||||
| 3D Renderer / WebGL | 👀 | Gloo |
|
||||
| Web Worker | 👀 | 👀 |
|
||||
| Router | 👀 | 👀 |
|
||||
| Notifications | 👀 | 👀 |
|
||||
| WebRTC Client | 👀 | 👀 |
|
||||
| Service Workers | 👀 | 👀 |
|
||||
| Resize Observer | 👀 | 👀 |
|
||||
| Canvas | 👀 | 👀 |
|
||||
| Clipboard | 👀 | 👀 |
|
||||
| Fullscreen | 👀 | 👀 |
|
||||
| History API | 👀 | 👀 |
|
||||
| Fetch | 👀 | Reqwest/surf |
|
||||
| Local storage (cache) | 👀 | Gloo |
|
||||
| Persistent storage (IndexDB) | 👀 | 👀 |
|
||||
| WebSocket | 👀 | Gloo |
|
||||
| 3D Renderer / WebGL | 👀 | Gloo |
|
||||
| Web Worker | 👀 | 👀 |
|
||||
| Router | 👀 | 👀 |
|
||||
| Notifications | 👀 | 👀 |
|
||||
| WebRTC Client | 👀 | 👀 |
|
||||
| Service Workers | 👀 | 👀 |
|
||||
| Resize Observer | 👀 | 👀 |
|
||||
| Canvas | 👀 | 👀 |
|
||||
| Clipboard | 👀 | 👀 |
|
||||
| Fullscreen | 👀 | 👀 |
|
||||
| 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:
|
||||
|
||||
```rust
|
||||
static Example: FC<()> = |cx| {
|
||||
pub static Example: FC<()> = |cx| {
|
||||
html! { <div> "blah" </div> }
|
||||
};
|
||||
|
||||
// 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.
|
||||
html_macro_to_vnodetree(move |allocator| {
|
||||
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>() {
|
||||
let name_str = name.to_string();
|
||||
|
||||
match is_valid_tag(&name_str) {
|
||||
true => input
|
||||
let first_char = name_str.chars().next().unwrap();
|
||||
if first_char.is_ascii_uppercase() {
|
||||
input
|
||||
.parse::<Component>()
|
||||
.map(|c| AmbiguousElement::Component(c))
|
||||
} else {
|
||||
input
|
||||
.parse::<Element>()
|
||||
.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",
|
||||
))
|
||||
}
|
||||
}
|
||||
.map(|c| AmbiguousElement::Element(c))
|
||||
}
|
||||
} else {
|
||||
if input.peek(LitStr) {
|
||||
|
|
|
@ -96,7 +96,7 @@ pub fn parse_component_body(
|
|||
}
|
||||
content.parse::<Token![..]>()?;
|
||||
*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 {
|
||||
return Err(Error::new(
|
||||
content.span(),
|
||||
|
@ -184,7 +184,7 @@ impl ToTokens for Component {
|
|||
};
|
||||
|
||||
tokens.append_all(quote! {
|
||||
__cx.virtual_child(
|
||||
__cx.component(
|
||||
#name,
|
||||
#builder,
|
||||
#key_token,
|
||||
|
|
|
@ -43,11 +43,6 @@ impl Parse for Element {
|
|||
fn parse(stream: ParseStream) -> Result<Self> {
|
||||
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
|
||||
let content: ParseBuffer;
|
||||
syn::braced!(content in stream);
|
||||
|
|
|
@ -4,7 +4,7 @@ fn main() {}
|
|||
// use dioxus_core as dioxus;
|
||||
// use dioxus_core::prelude::*;
|
||||
|
||||
// static Example: FC<()> = |cx| {
|
||||
// pub static Example: FC<()> = |cx| {
|
||||
// let list = (0..10).map(|f| LazyNodes::new(move |f| todo!()));
|
||||
|
||||
// cx.render(LazyNodes::new(move |cx| {
|
||||
|
|
|
@ -5,12 +5,8 @@ struct SomeContext {
|
|||
items: Vec<String>,
|
||||
}
|
||||
|
||||
struct Props {
|
||||
name: String,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
static Example: FC<Props> = |cpt| {
|
||||
static Example: FC<()> = |cpt| {
|
||||
todo!()
|
||||
|
||||
// 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
|
||||
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() {
|
||||
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> {
|
||||
_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,
|
||||
component: FC<P>,
|
||||
props: P,
|
||||
|
|
|
@ -123,7 +123,14 @@ impl<'a, T: 'static> UseState<'a, T> {
|
|||
let slot = self.inner.wip.clone();
|
||||
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> {
|
||||
pub fn get_mut(self) -> RefMut<'a, T> {
|
||||
// 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
|
||||
impl<'a, T: 'static + Display> std::fmt::Display for UseState<'a, T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
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)
|
||||
/// element.
|
||||
///
|
||||
/// Part of the HTML namespace. Only works in HTML-compatible renderers
|
||||
///
|
||||
/// ## Definition and Usage
|
||||
/// - 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.
|
||||
|
@ -1575,6 +1577,7 @@ builder_constructors! {
|
|||
/// [`<select>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select)
|
||||
/// element.
|
||||
select {
|
||||
value: String,
|
||||
autocomplete: String,
|
||||
autofocus: Bool,
|
||||
disabled: Bool,
|
||||
|
|
|
@ -9,7 +9,8 @@ use wry::application::window::Fullscreen;
|
|||
use wry::application::{
|
||||
dpi::LogicalSize,
|
||||
event::StartCause,
|
||||
platform::ios::{ScreenEdge, WindowBuilderExtIOS, WindowExtIOS},
|
||||
// platform::ios::{ScreenEdge, WindowBuilderExtIOS, WindowExtIOS},
|
||||
// platform::ios::{ScreenEdge, WindowBuilderExtIOS, WindowExtIOS},
|
||||
};
|
||||
use wry::webview::WebViewBuilder;
|
||||
use wry::{
|
||||
|
@ -23,7 +24,7 @@ fn init_logging() {
|
|||
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<()> {
|
||||
launch_with_props(root, (), builder)
|
||||
|
|
|
@ -38,7 +38,7 @@ struct ExampleProps {
|
|||
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());
|
||||
|
||||
cx.render(rsx! {
|
||||
|
|
|
@ -19,7 +19,7 @@ fn main() {
|
|||
.unwrap();
|
||||
}
|
||||
|
||||
pub const App: FC<()> = |cx| {
|
||||
pub static App: FC<()> = |cx| {
|
||||
cx.render(rsx!(
|
||||
div { class: "overflow-hidden"
|
||||
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! {
|
||||
div {
|
||||
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! {
|
||||
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! {
|
||||
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!(
|
||||
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!(
|
||||
svg {
|
||||
fill: "none"
|
||||
|
|
|
@ -25,7 +25,7 @@ fn main() {
|
|||
wasm_bindgen_futures::spawn_local(WebsysRenderer::start(App));
|
||||
}
|
||||
|
||||
pub const App: FC<()> = |cx| {
|
||||
pub static App: FC<()> = |cx| {
|
||||
cx.render(rsx!(
|
||||
div { class: "overflow-hidden"
|
||||
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! {
|
||||
div {
|
||||
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! {
|
||||
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! {
|
||||
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!(
|
||||
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!(
|
||||
svg {
|
||||
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.
|
||||
//!
|
||||
//! ```
|
||||
//! static Example: FC<()> = |cx| {
|
||||
//! pub static Example: FC<()> = |cx| {
|
||||
//! let (val, set_val) = use_state(cx, || 0);
|
||||
//! cx.render(rsx!(
|
||||
//! button { onclick: move |_| set_val(val + 1) }
|
||||
|
@ -156,7 +156,7 @@
|
|||
//! diouxs::web::launch(Example);
|
||||
//! }
|
||||
//!
|
||||
//! static Example: FC<()> = |cx| {
|
||||
//! pub static Example: FC<()> = |cx| {
|
||||
//! cx.render(rsx! {
|
||||
//! div { "Hello World!" }
|
||||
//! })
|
||||
|
@ -188,6 +188,8 @@ pub use dioxus_hooks as hooks;
|
|||
#[cfg(feature = "desktop")]
|
||||
pub use dioxus_desktop as desktop;
|
||||
|
||||
pub mod debug {}
|
||||
|
||||
pub mod prelude {
|
||||
//! A glob import that includes helper types like FC, rsx!, html!, and required traits
|
||||
pub use dioxus_core::prelude::*;
|
||||
|
|
Loading…
Reference in New Issue