wip: move examples around
This commit is contained in:
1 parent
d298b626d3
commit
1e6e5e611b
86 files changed
+160
-216
No files matched your search
@@ -114,12 +114,13 @@ const APP: FC<()> = |(cx, _)| {
|
||||
struct CalculatorKeyProps<'a> {
|
||||
name: &'static str,
|
||||
onclick: &'a dyn Fn(MouseEvent),
|
||||
children: ScopeChildren<'a>,
|
||||
}
|
||||
|
||||
fn CalculatorKey<'a>((cx, props): Scope<'a, CalculatorKeyProps>) -> Element<'a> {
|
||||
fn CalculatorKey<'a>((cx, props): Scope<'a, CalculatorKeyProps<'a>>) -> Element {
|
||||
rsx!(cx, button {
|
||||
class: "calculator-key {props.name}"
|
||||
onclick: {props.onclick}
|
||||
{cx.children()}
|
||||
{&props.children}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_core_macro::format_args_f;
|
||||
use dioxus_core_macro::rsx;
|
||||
use dioxus_html as dioxus_elements;
|
||||
|
||||
fn main() {
|
||||
let mut dom = VirtualDom::new(EXAMPLE);
|
||||
dom.rebuild();
|
||||
println!("{}", dom);
|
||||
}
|
||||
|
||||
pub static EXAMPLE: FC<()> = |(cx, _)| {
|
||||
let list = (0..10).map(|_f| {
|
||||
rsx! {
|
||||
"{_f}"
|
||||
}
|
||||
});
|
||||
// let list = (0..10).map(|_f| Some(Box::new(move |_f| todo!())));
|
||||
|
||||
cx.render(Some(LazyNodes::new(move |cx| {
|
||||
cx.raw_element(
|
||||
"div",
|
||||
None,
|
||||
[],
|
||||
[],
|
||||
[
|
||||
cx.text(format_args!("hello")),
|
||||
cx.text(format_args!("hello")),
|
||||
cx.fragment_from_iter(list),
|
||||
],
|
||||
None,
|
||||
)
|
||||
})))
|
||||
};
|
||||
@@ -0,0 +1,15 @@
|
||||
use dioxus_core::prelude::*;
|
||||
|
||||
fn main() {
|
||||
let mut dom = VirtualDom::new(App);
|
||||
dom.rebuild();
|
||||
}
|
||||
|
||||
const App: FC<()> = |(cx, props)| {
|
||||
let id = cx.scope_id();
|
||||
// cx.submit_task(Box::pin(async move { id }));
|
||||
|
||||
// let (handle, contents) = use_task(cx, || async { "hello world".to_string() });
|
||||
|
||||
todo!()
|
||||
};
|
||||
@@ -0,0 +1,78 @@
|
||||
//! Demonstrate that borrowed data is possible as a property type
|
||||
//! Borrowing (rather than cloning) is very important for speed and ergonomics.
|
||||
//!
|
||||
//! It's slightly more advanced than just cloning, but well worth the investment.
|
||||
//!
|
||||
//! If you use the FC macro, we handle the lifetimes automatically, making it easy to write efficient & performant components.
|
||||
fn main() {}
|
||||
|
||||
use dioxus_core::prelude::*;
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
struct AppProps {
|
||||
items: Vec<Rc<ListItem>>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
struct ListItem {
|
||||
name: String,
|
||||
age: u32,
|
||||
}
|
||||
|
||||
fn app<'a>(cx: Context<'a>, props: &AppProps) -> Element<'a> {
|
||||
// let (val, set_val) = use_state_classic(cx, || 0);
|
||||
|
||||
todo!()
|
||||
// cx.render(LazyNodes::new(move |_nodecx| {
|
||||
// todo!()
|
||||
// // builder::ElementBuilder::new(_nodecx, "div")
|
||||
// // .iter_child({
|
||||
// // cx.items.iter().map(|child| {
|
||||
// // builder::virtual_child(
|
||||
// // _nodecx,
|
||||
// // ChildItem,
|
||||
// // ChildProps {
|
||||
// // item: child.clone(),
|
||||
// // item_handler: set_val.clone(),
|
||||
// // },
|
||||
// // None,
|
||||
// // &[],
|
||||
// // )
|
||||
// // })
|
||||
// // })
|
||||
// // .iter_child([builder::ElementBuilder::new(_nodecx, "div")
|
||||
// // .iter_child([builder::text3(_nodecx.bump(), format_args!("{}", val))])
|
||||
// // .finish()])
|
||||
// // .finish()
|
||||
// }))
|
||||
}
|
||||
|
||||
// props should derive a partialeq implementation automatically, but implement ptr compare for & fields
|
||||
struct ChildProps {
|
||||
// Pass down complex structs
|
||||
item: Rc<ListItem>,
|
||||
|
||||
// Even pass down handlers!
|
||||
item_handler: Rc<dyn Fn(i32)>,
|
||||
}
|
||||
|
||||
fn ChildItem<'a>(cx: Context<'a>, props: &'a ChildProps) -> Element<'a> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
impl PartialEq for ChildProps {
|
||||
fn eq(&self, _other: &Self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
impl Properties for ChildProps {
|
||||
type Builder = ();
|
||||
const IS_STATIC: bool = true;
|
||||
fn builder() -> Self::Builder {
|
||||
()
|
||||
}
|
||||
unsafe fn memoize(&self, other: &Self) -> bool {
|
||||
self == other
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
use dioxus_core::prelude::*;
|
||||
|
||||
fn main() {}
|
||||
struct SomeContext {
|
||||
items: Vec<String>,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
static Example: FC<()> = |(cx, props)| {
|
||||
todo!()
|
||||
|
||||
// let value = cx.use_context(|c: &SomeContext| c.items.last().unwrap());
|
||||
|
||||
// cx.render(LazyNodes::new(move |bump| {
|
||||
// builder::ElementBuilder::new(bump, "button")
|
||||
// .on("click", move |_| {
|
||||
// println!("Value is {}", cx.name);
|
||||
// println!("Value is {}", value.as_str());
|
||||
// println!("Value is {}", *value);
|
||||
// })
|
||||
// .on("click", move |_| {
|
||||
// println!("Value is {}", cx.name);
|
||||
// })
|
||||
// .finish()
|
||||
// }))
|
||||
// cx.render(html! {
|
||||
// <div>
|
||||
// <button onclick={move |_| println!("Value is {}", value)} />
|
||||
// <button onclick={move |_| println!("Value is {}", value)} />
|
||||
// <button onclick={move |_| println!("Value is {}", value)} />
|
||||
// <div>
|
||||
// <p> "Value is: {val}" </p>
|
||||
// </div>
|
||||
// </div>
|
||||
// })
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
fn main( ) {}
|
||||
@@ -0,0 +1,46 @@
|
||||
use dioxus_core::prelude::*;
|
||||
|
||||
fn main() {}
|
||||
|
||||
fn app<'a>(cx: Context<'a>, props: &()) -> Element<'a> {
|
||||
// let vak = use_suspense(
|
||||
// cx,
|
||||
// || async {},
|
||||
// |c, _res| c.render(LazyNodes::new(move |f| f.text(format_args!("")))),
|
||||
// );
|
||||
|
||||
// let d1 = cx.render(to_lazy_nodes(move |f| {
|
||||
// f.raw_element(
|
||||
// "div",
|
||||
// None,
|
||||
// [],
|
||||
// [],
|
||||
// [
|
||||
// // f.fragment_from_iter(vak),
|
||||
// f.text(format_args!("")),
|
||||
// f.text(format_args!("")),
|
||||
// f.text(format_args!("")),
|
||||
// f.text(format_args!("")),
|
||||
// ],
|
||||
// None,
|
||||
// )
|
||||
// }));
|
||||
|
||||
todo!()
|
||||
// cx.render(LazyNodes::new(move |f| {
|
||||
// f.raw_element(
|
||||
// "div",
|
||||
// None,
|
||||
// [],
|
||||
// [],
|
||||
// [
|
||||
// f.text(format_args!("")),
|
||||
// f.text(format_args!("")),
|
||||
// f.text(format_args!("")),
|
||||
// f.text(format_args!("")),
|
||||
// d1.unwrap(),
|
||||
// ],
|
||||
// None,
|
||||
// )
|
||||
// }))
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
use bumpalo::Bump;
|
||||
|
||||
fn main() {}
|
||||
|
||||
fn build(factory: Factory) {
|
||||
factory.text();
|
||||
div::new(factory)
|
||||
.r#class()
|
||||
.r#tag()
|
||||
.r#type()
|
||||
.add_children()
|
||||
.iter_children()
|
||||
.finish();
|
||||
}
|
||||
|
||||
/// # The `div` element
|
||||
///
|
||||
///
|
||||
/// The <div> HTML element is the generic container for flow content. It has no effect on the content or layout until
|
||||
/// styled in some way using CSS (e.g. styling is directly applied to it, or some kind of layout model like Flexbox is
|
||||
/// applied to its parent element).
|
||||
///
|
||||
/// As a "pure" container, the <div> element does not inherently represent anything. Instead, it's used to group content
|
||||
/// so it can be easily styled using the class or id attributes, marking a section of a document as being written in a
|
||||
/// different language (using the lang attribute), and so on.
|
||||
///
|
||||
/// ## Usage
|
||||
/// ```
|
||||
/// rsx!{
|
||||
/// div { class: "tall", id: "unique id"
|
||||
/// h1 {}
|
||||
/// p {}
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ## Specifications
|
||||
/// - Content categories: Flow content, palpable content.
|
||||
/// - Permitted content: Flow content.
|
||||
/// - Permitted parents: Any element that accepts flow content.
|
||||
#[allow(non_camel_case_types)]
|
||||
struct div {}
|
||||
|
||||
struct h1 {}
|
||||
|
||||
struct h2 {}
|
||||
|
||||
trait BasicElement: Sized {
|
||||
const TagName: &'static str;
|
||||
fn get_bump(&self) -> &Bump;
|
||||
fn new(factory: Factory) -> Self;
|
||||
fn add_children(self) -> Self {
|
||||
self
|
||||
}
|
||||
fn iter_children(self) -> Self {
|
||||
self
|
||||
}
|
||||
fn finish(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl BasicElement for div {
|
||||
const TagName: &'static str = "div";
|
||||
|
||||
fn get_bump(&self) -> &Bump {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn new(_factory: Factory) -> Self {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl div {
|
||||
fn class(self) -> Self {
|
||||
self
|
||||
}
|
||||
fn tag(self) -> Self {
|
||||
self
|
||||
}
|
||||
fn r#type(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct Factory<'a> {
|
||||
bump: &'a bumpalo::Bump,
|
||||
}
|
||||
|
||||
impl<'a> Factory<'a> {
|
||||
fn text(&self) -> &str {
|
||||
todo!()
|
||||
}
|
||||
fn new_el(&'a self, tag: &'static str) -> ElementBuilder<'a> {
|
||||
ElementBuilder {
|
||||
bump: self.bump,
|
||||
tag,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ElementBuilder<'a> {
|
||||
tag: &'static str,
|
||||
bump: &'a bumpalo::Bump,
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use dioxus::component::Scope;
|
||||
use dioxus::events::on::MouseEvent;
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
use rand::prelude::*;
|
||||
use std::fmt::Display;
|
||||
|
||||
fn main() {
|
||||
let mut dom = VirtualDom::new(App);
|
||||
let g = dom.rebuild();
|
||||
assert!(g.edits.len() > 1);
|
||||
}
|
||||
|
||||
fn App((cx, props): Scope<()>) -> Element {
|
||||
let mut rng = SmallRng::from_entropy();
|
||||
let rows = (0..10_000_usize).map(|f| {
|
||||
let label = Label::new(&mut rng);
|
||||
rsx! {
|
||||
Row {
|
||||
row_id: f,
|
||||
label: label
|
||||
}
|
||||
}
|
||||
});
|
||||
cx.render(rsx! {
|
||||
table {
|
||||
tbody {
|
||||
{rows}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Props)]
|
||||
struct RowProps {
|
||||
row_id: usize,
|
||||
label: Label,
|
||||
}
|
||||
|
||||
fn Row<'a>((cx, props): Scope<'a, RowProps>) -> Element<'a> {
|
||||
let handler = move |evt: MouseEvent| {
|
||||
let g = evt.button;
|
||||
};
|
||||
cx.render(rsx! {
|
||||
tr {
|
||||
// td { class:"col-md-1", "{props.row_id}" }
|
||||
// td { class:"col-md-1", onclick: move |_| { /* run onselect */ }
|
||||
// a { class: "lbl", "{props.label}" }
|
||||
// }
|
||||
// td { class: "col-md-1"
|
||||
// a { class: "remove", onclick: {handler}
|
||||
// span { class: "glyphicon glyphicon-remove remove" aria_hidden: "true" }
|
||||
// }
|
||||
// }
|
||||
// td { class: "col-md-6" }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
struct Label([&'static str; 3]);
|
||||
|
||||
impl Label {
|
||||
fn new(rng: &mut SmallRng) -> Self {
|
||||
Label([
|
||||
ADJECTIVES.choose(rng).unwrap(),
|
||||
COLOURS.choose(rng).unwrap(),
|
||||
NOUNS.choose(rng).unwrap(),
|
||||
])
|
||||
}
|
||||
}
|
||||
impl Display for Label {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{} {} {}", self.0[0], self.0[1], self.0[2])
|
||||
}
|
||||
}
|
||||
|
||||
static ADJECTIVES: &[&str] = &[
|
||||
"pretty",
|
||||
"large",
|
||||
"big",
|
||||
"small",
|
||||
"tall",
|
||||
"short",
|
||||
"long",
|
||||
"handsome",
|
||||
"plain",
|
||||
"quaint",
|
||||
"clean",
|
||||
"elegant",
|
||||
"easy",
|
||||
"angry",
|
||||
"crazy",
|
||||
"helpful",
|
||||
"mushy",
|
||||
"odd",
|
||||
"unsightly",
|
||||
"adorable",
|
||||
"important",
|
||||
"inexpensive",
|
||||
"cheap",
|
||||
"expensive",
|
||||
"fancy",
|
||||
];
|
||||
|
||||
static COLOURS: &[&str] = &[
|
||||
"red", "yellow", "blue", "green", "pink", "brown", "purple", "brown", "white", "black",
|
||||
"orange",
|
||||
];
|
||||
|
||||
static NOUNS: &[&str] = &[
|
||||
"table", "chair", "house", "bbq", "desk", "car", "pony", "cookie", "sandwich", "burger",
|
||||
"pizza", "mouse", "keyboard",
|
||||
];
|
||||
@@ -0,0 +1,148 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use dioxus::component::Scope;
|
||||
use dioxus::events::on::MouseEvent;
|
||||
use dioxus::nodes::IntoVNode;
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
|
||||
fn main() {}
|
||||
|
||||
fn html_usage() {
|
||||
let mo = move |_| {};
|
||||
let r = rsx! {
|
||||
div {
|
||||
onclick: move |_| {}
|
||||
onmouseover: {mo}
|
||||
"type": "bar",
|
||||
"hello world"
|
||||
}
|
||||
};
|
||||
|
||||
let items = ["bob", "bill", "jack"];
|
||||
|
||||
let f = items
|
||||
.iter()
|
||||
.filter(|f| f.starts_with('b'))
|
||||
.map(|f| rsx!("hello {f}"));
|
||||
|
||||
// let p = rsx!(div { {f} });
|
||||
}
|
||||
|
||||
static App2: FC<()> = |(cx, _)| cx.render(rsx!("hello world!"));
|
||||
|
||||
static App: FC<()> = |(cx, props)| {
|
||||
let name = cx.use_state(|| 0);
|
||||
|
||||
cx.render(rsx!(div {
|
||||
h1 {}
|
||||
h2 {}
|
||||
}))
|
||||
};
|
||||
|
||||
pub trait UseState<'a, T: 'static> {
|
||||
fn use_state(self, f: impl FnOnce() -> T) -> &'a T;
|
||||
}
|
||||
impl<'a, T: 'static> UseState<'a, T> for Context<'a> {
|
||||
fn use_state(self, f: impl FnOnce() -> T) -> &'a T {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
fn App3((cx, props): Scope<()>) -> Element {
|
||||
let p = rsx! {
|
||||
Child {
|
||||
bame: 10,
|
||||
}
|
||||
};
|
||||
todo!()
|
||||
// cx.render(rsx!(Child {
|
||||
// bame: 102,
|
||||
// ..ChildProps { bame: 10 }
|
||||
// }))
|
||||
}
|
||||
|
||||
#[derive(Props, PartialEq, Debug)]
|
||||
struct ChildProps {
|
||||
bame: i32, // children: Children<'a>,
|
||||
}
|
||||
|
||||
fn Child<'a>((cx, props): Scope<'a, ChildProps>) -> Element<'a> {
|
||||
cx.render(rsx!(div {
|
||||
// {props.children}
|
||||
}))
|
||||
}
|
||||
|
||||
// Some(LazyNodes::new(|f| {
|
||||
// //
|
||||
// // let r = f.fragment_from_iter(&props.children);
|
||||
// r
|
||||
// // todo!()
|
||||
// }))
|
||||
// todo!()
|
||||
// rsx!({ Some(p) })
|
||||
// todo!()
|
||||
|
||||
pub struct Children<'a> {
|
||||
children: VNode<'static>,
|
||||
_p: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
impl<'a> Children<'a> {
|
||||
pub fn new(children: VNode<'a>) -> Self {
|
||||
Self {
|
||||
children: unsafe { std::mem::transmute(children) },
|
||||
_p: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Bapp: FC<()> = |(cx, props)| {
|
||||
let name = cx.use_state(|| 0);
|
||||
|
||||
cx.render(rsx!(
|
||||
div {
|
||||
div {
|
||||
|
||||
}
|
||||
div {
|
||||
|
||||
}
|
||||
}
|
||||
))
|
||||
};
|
||||
|
||||
static Match: FC<()> = |(cx, props)| {
|
||||
//
|
||||
let b: Box<dyn Fn(NodeFactory) -> VNode> = Box::new(|f| todo!());
|
||||
|
||||
let b = match "ag" {
|
||||
"a" => {
|
||||
let __b: Box<dyn FnOnce(NodeFactory) -> VNode> = Box::new(|f: NodeFactory| todo!());
|
||||
__b
|
||||
}
|
||||
_ => {
|
||||
let __b: Box<dyn FnOnce(NodeFactory) -> VNode> = Box::new(|f: NodeFactory| todo!());
|
||||
__b
|
||||
}
|
||||
};
|
||||
|
||||
// let b: Box<dyn Fn(NodeFactory) -> VNode> = match "alph" {
|
||||
// "beta" => Box::new(|f: NodeFactory| {
|
||||
// //
|
||||
// todo!()
|
||||
// }),
|
||||
// _ => Box::new(|f: NodeFactory| {
|
||||
// //
|
||||
// todo!()
|
||||
// }),
|
||||
// };
|
||||
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -0,0 +1,259 @@
|
||||
use std::borrow::BorrowMut;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use dioxus::component::Scope;
|
||||
use dioxus::events::on::MouseEvent;
|
||||
use dioxus::nodes::{IntoVNode, VComponent, VFragment, VText};
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
|
||||
fn main() {}
|
||||
|
||||
fn t() {
|
||||
let g = rsx! {
|
||||
div {
|
||||
div {
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let g = {
|
||||
let ___p: Box<dyn FnOnce(NodeFactory) -> VNode> = Box::new(|__cx: NodeFactory| {
|
||||
use dioxus_elements::{GlobalAttributes, SvgAttributes};
|
||||
__cx.element(dioxus_elements::div, [], [], [], None)
|
||||
});
|
||||
// let __z = ___p as ;
|
||||
// __z
|
||||
};
|
||||
}
|
||||
|
||||
// #[derive(PartialEq, Props)]
|
||||
// struct OurProps {
|
||||
// foo: String,
|
||||
// }
|
||||
|
||||
// fn App<'a>((cx, props): Scope<'a, OurProps>) -> Element<'a> {
|
||||
// let a = rsx! {
|
||||
// div {
|
||||
// "asd"
|
||||
// "{props.foo}"
|
||||
// }
|
||||
// };
|
||||
|
||||
// let p = (0..10).map(|f| {
|
||||
// rsx! {
|
||||
// div {
|
||||
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
|
||||
// let g = match "text" {
|
||||
// "a" => {
|
||||
// rsx!("asd")
|
||||
// }
|
||||
// _ => {
|
||||
// rsx!("asd")
|
||||
// }
|
||||
// };
|
||||
|
||||
// let items = ["bob", "bill", "jack"];
|
||||
|
||||
// let f = items
|
||||
// .iter()
|
||||
// .filter(|f| f.starts_with('b'))
|
||||
// .map(|f| rsx!("hello {f}"));
|
||||
|
||||
// // use dioxus_hooks;
|
||||
// // let g = use_state(|| "hello".to_string());
|
||||
|
||||
// let s: &'a mut String = cx.use_hook(|_| String::new(), |f| f, |_| {});
|
||||
|
||||
// /*
|
||||
// the final closure is allowed to borrow anything provided it
|
||||
// */
|
||||
// // cx.render({
|
||||
// // let p: Option<Box<dyn FnOnce(_) -> _>> = Some(Box::new(move |__cx: NodeFactory| {
|
||||
// // use dioxus_elements::{GlobalAttributes, SvgAttributes};
|
||||
|
||||
// // let props = Child2Props { foo: s };
|
||||
// // let ch: VNode = __cx.component(Child2, props, None, []);
|
||||
// // __cx.element(
|
||||
// // dioxus_elements::div,
|
||||
// // [],
|
||||
// // [],
|
||||
// // [ch],
|
||||
// // // [__cx.component(Child2, fc_to_builder(Child2).foo(s).build(), None, [])],
|
||||
// // None,
|
||||
// // )
|
||||
// // }));
|
||||
// // p
|
||||
// // // let ___p: Box<dyn FnOnce(NodeFactory) -> VNode> = Box::new(move |__cx| {
|
||||
// // // use dioxus_elements::{GlobalAttributes, SvgAttributes};
|
||||
|
||||
// // // let props = Child2Props { foo: s };
|
||||
// // // let ch: VNode = __cx.component(Child2, props, None, []);
|
||||
// // // __cx.element(
|
||||
// // // dioxus_elements::div,
|
||||
// // // [],
|
||||
// // // [],
|
||||
// // // [ch],
|
||||
// // // // [__cx.component(Child2, fc_to_builder(Child2).foo(s).build(), None, [])],
|
||||
// // // None,
|
||||
// // // )
|
||||
// // // });
|
||||
// // // Some(___p)
|
||||
// // })
|
||||
|
||||
// let a = annotate_lazy(move |f| {
|
||||
// //
|
||||
// todo!()
|
||||
// });
|
||||
// let b = annotate_lazy(move |f| {
|
||||
// //
|
||||
// f.text(format_args!("{}", props.foo))
|
||||
// });
|
||||
|
||||
// let c = annotate_lazy(move |f| {
|
||||
// //
|
||||
// f.component(
|
||||
// Child,
|
||||
// OurProps {
|
||||
// //
|
||||
// foo: "hello".to_string(),
|
||||
// },
|
||||
// None,
|
||||
// [],
|
||||
// )
|
||||
// });
|
||||
|
||||
// let st: &'a String = cx.use_hook(|_| "hello".to_string(), |f| f, |_| {});
|
||||
|
||||
// let d = annotate_lazy(move |f| {
|
||||
// //
|
||||
// f.component(
|
||||
// Child2,
|
||||
// Child2Props {
|
||||
// //
|
||||
// foo: st,
|
||||
// },
|
||||
// None,
|
||||
// [],
|
||||
// )
|
||||
// });
|
||||
|
||||
// let e = match "asd" {
|
||||
// b => {
|
||||
// //
|
||||
|
||||
// annotate_lazy(move |f| {
|
||||
// //
|
||||
// f.text(format_args!("{}", props.foo))
|
||||
// })
|
||||
// }
|
||||
// a => {
|
||||
// //
|
||||
|
||||
// annotate_lazy(move |f| {
|
||||
// //
|
||||
// f.text(format_args!("{}", props.foo))
|
||||
// })
|
||||
// }
|
||||
// };
|
||||
|
||||
// cx.render(annotate_lazy(move |f| {
|
||||
// //
|
||||
|
||||
// f.raw_element(
|
||||
// "div",
|
||||
// None,
|
||||
// [],
|
||||
// [],
|
||||
// [
|
||||
// //
|
||||
// f.fragment_from_iter(a),
|
||||
// f.fragment_from_iter(b),
|
||||
// f.fragment_from_iter(c),
|
||||
// f.fragment_from_iter(e),
|
||||
// ],
|
||||
// None,
|
||||
// )
|
||||
// // todo!()
|
||||
// }))
|
||||
// // cx.render(rsx! {
|
||||
// // div {
|
||||
// // div {
|
||||
// // {a}
|
||||
// // // {p}
|
||||
// // // {g}
|
||||
// // // {f}
|
||||
// // }
|
||||
// // // div {
|
||||
// // // "asd"
|
||||
// // // div {
|
||||
// // // "asd"
|
||||
// // // }
|
||||
// // // }
|
||||
// // // Child {
|
||||
// // // foo: "asd".to_string(),
|
||||
// // // }
|
||||
// // Child2 {
|
||||
// // foo: s,
|
||||
// // }
|
||||
// // }
|
||||
// // })
|
||||
// }
|
||||
|
||||
// fn Child((cx, props): Scope<OurProps>) -> Element {
|
||||
// cx.render(rsx! {
|
||||
// div {
|
||||
// div {}
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
|
||||
#[derive(Props)]
|
||||
struct Child2Props<'a> {
|
||||
foo: &'a String,
|
||||
}
|
||||
|
||||
fn Child2<'a>((cx, props): Scope<'a, Child2Props>) -> Element<'a> {
|
||||
let node = cx
|
||||
.render(rsx! {
|
||||
div {
|
||||
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
ChildrenComp {
|
||||
div {}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Props)]
|
||||
struct ChildrenTest<'a> {
|
||||
children: ScopeChildren<'a>,
|
||||
}
|
||||
|
||||
fn ChildrenComp<'a>((cx, props): Scope<'a, ChildrenTest<'a>>) -> Element<'a> {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
div {
|
||||
{&props.children}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn ChildrenMemo((cx, props): Scope<()>) -> Element {
|
||||
todo!()
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
fn main() {}
|
||||
@@ -0,0 +1,17 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use dioxus_core::{lazynodes::LazyNodes, prelude::*};
|
||||
|
||||
// #[async_std::main]
|
||||
fn main() {
|
||||
static App: FC<()> =
|
||||
|(cx, props)| cx.render(Some(LazyNodes::new(move |f| f.text(format_args!("hello")))));
|
||||
|
||||
let mut dom = VirtualDom::new(App);
|
||||
|
||||
dom.rebuild();
|
||||
|
||||
// let deadline = async_std::task::sleep(Duration::from_millis(50));
|
||||
|
||||
// let _fut = dom.run_with_deadline(|| deadline.);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
# Reference Guide
|
||||
|
||||
This folder holds a bunch of useful reference code. However, none of the code is meant to be executed (nothing special will happen).
|
||||
|
||||
|
||||
| Reference: | What it does |
|
||||
| --------------------------------------------------- | ----------------------------------- |
|
||||
| [Basics](./basics.rs) | How to handle futures with suspense |
|
||||
| [Empty](./empty.rs) | How to handle futures with suspense |
|
||||
| [Children](./children.rs) | How to handle futures with suspense |
|
||||
| [Async](./async.rs) | How to handle futures with suspense |
|
||||
| [Suspense](./suspense.rs) | How to handle futures with suspense |
|
||||
| [Conditional Rendering](./conditional_rendering.rs) | How to handle futures with suspense |
|
||||
| [Controlled Inputs](./controlled_inputs.rs) | How to handle futures with suspense |
|
||||
| [Fragments](./fragments.rs) | How to handle futures with suspense |
|
||||
| [Inline Styles](./inline_styles.rs) | How to handle futures with suspense |
|
||||
| [Spread Pattern for Props](./spreadpattern.rs) | How to handle futures with suspense |
|
||||
| [Iterators](./iterators.rs) | How to handle futures with suspense |
|
||||
| [Listener](./listener.rs) | How to handle futures with suspense |
|
||||
| [Memo](./memo.rs) | How to handle futures with suspense |
|
||||
| [NodeRef](./noderefs.rs) | How to handle futures with suspense |
|
||||
| [Signals](./signals.rs) | How to handle futures with suspense |
|
||||
| [ToString](./tostring.rs) | How to handle futures with suspense |
|
||||
| [Global CSS](./global_css.rs) | How to handle futures with suspense |
|
||||
| [State Management](./statemanagement.rs) | How to handle futures with suspense |
|
||||
| [Testing](./testing.rs) | How to handle futures with suspense |
|
||||
| [Custom Elements](./custom_elements.rs) | How to handle futures with suspense |
|
||||
| [Web Components](./webcomponents.rs) | How to handle futures with suspense |
|
||||
| [Anti-patterns](./antipatterns.rs) | What not to do! |
|
||||
@@ -0,0 +1,181 @@
|
||||
//! Example: Antipatterns
|
||||
//! ---------------------
|
||||
//!
|
||||
//! This example shows what *not* to do and provides a reason why a given pattern is considered an "AntiPattern". Most
|
||||
//! anti-patterns are considered wrong due performance reasons or violate the "rules" of Dioxus. These rules are
|
||||
//! borrowed from other successful UI frameworks, and Dioxus is more focused on providing a familiar, ergonomic interface
|
||||
//! rather than building new harder-to-misuse patterns.
|
||||
//!
|
||||
//! In this list we showcase:
|
||||
//! - Not adding keys for iterators
|
||||
//! - Heavily nested fragments
|
||||
//! - Understanding ordering of set_state
|
||||
//! - Naming conventions
|
||||
//! - Rules of hooks
|
||||
//!
|
||||
//! 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::*;
|
||||
|
||||
/// Antipattern: Iterators without keys
|
||||
/// -----------------------------------
|
||||
///
|
||||
/// This is considered an anti-pattern for performance reasons. Dioxus will diff your current and old layout and must
|
||||
/// take a slower path if it can't correlate old elements with new elements. Lists are particularly susceptible to the
|
||||
/// "slow" path, so you're strongly encouraged to provide a unique, stable ID between renders. Additionally, providing
|
||||
/// the *wrong* keys is even worse - props might be assigned to the wrong components! Keys should be:
|
||||
/// - Unique
|
||||
/// - Stable
|
||||
/// - Predictable
|
||||
///
|
||||
/// Dioxus will log an error in the console if it detects that your iterator does not properly generate keys
|
||||
#[derive(PartialEq, Props)]
|
||||
struct NoKeysProps {
|
||||
data: std::collections::HashMap<u32, String>,
|
||||
}
|
||||
static AntipatternNoKeys: FC<NoKeysProps> = |(cx, props)| {
|
||||
// WRONG: Make sure to add keys!
|
||||
rsx!(cx, ul {
|
||||
{props.data.iter().map(|(k, v)| rsx!(li { "List item: {v}" }))}
|
||||
});
|
||||
// RIGHT: Like this:
|
||||
rsx!(cx, ul {
|
||||
{props.data.iter().map(|(k, v)| rsx!(li { key: "{k}", "List item: {v}" }))}
|
||||
})
|
||||
};
|
||||
|
||||
/// Antipattern: Deeply nested fragments
|
||||
/// ------------------------------------
|
||||
///
|
||||
/// This particular antipattern is not necessarily an antipattern in other frameworks but does has a performance impact
|
||||
/// in Dioxus apps. Fragments don't mount a physical element to the DOM immediately, so Dioxus must recurse into its
|
||||
/// children to find a physical DOM node. This process is called "normalization". Other frameworks perform an agressive
|
||||
/// mutative normalization while Dioxus keeps your VNodes immutable. This means that deeply nested fragments make Dioxus
|
||||
/// perform unnecessary work. Prefer one or two levels of fragments / nested components until presenting a true DOM element.
|
||||
///
|
||||
/// Only Component and Fragment nodes are susceptible to this issue. Dioxus mitigates this with components by providing
|
||||
/// an API for registering shared state without the ContextProvider pattern.
|
||||
static AntipatternNestedFragments: FC<()> = |(cx, props)| {
|
||||
// Try to avoid heavily nesting fragments
|
||||
rsx!(cx,
|
||||
Fragment {
|
||||
Fragment {
|
||||
Fragment {
|
||||
Fragment {
|
||||
Fragment {
|
||||
div { "Finally have a real node!" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
};
|
||||
|
||||
/// Antipattern: Using state after it's been updated
|
||||
/// -----------------------------------------------
|
||||
///
|
||||
/// This is an antipattern in other frameworks, but less so in Dioxus. However, it's important to highlight that use_state
|
||||
/// does *not* work the same way as it does in React. Rust provides explicit guards against mutating shared data - a huge
|
||||
/// problem in JavaScript land. With Rust and Dioxus, it's nearly impossible to misuse `use_state` - you simply can't
|
||||
/// accidentally modify the state you've received!
|
||||
///
|
||||
/// However, calling set_state will *not* update the current version of state in the component. This should be easy to
|
||||
/// recognize from the function signature, but Dioxus will not update the "live" version of state. Calling `set_state`
|
||||
/// merely places a new value in the queue and schedules the component for a future update.
|
||||
static AntipatternRelyingOnSetState: FC<()> = |(cx, props)| {
|
||||
let (state, set_state) = use_state(cx, || "Hello world").classic();
|
||||
set_state("New state");
|
||||
// This will return false! `state` will *still* be "Hello world"
|
||||
assert!(state == &"New state");
|
||||
todo!()
|
||||
};
|
||||
|
||||
/// Antipattern: Capitalization
|
||||
/// ---------------------------
|
||||
///
|
||||
/// This antipattern is enforced to retain parity with other frameworks and provide useful IDE feedback, but is less
|
||||
/// critical than other potential misuses. In short:
|
||||
/// - Only raw elements may start with a lowercase character
|
||||
/// - All components must start with an uppercase character
|
||||
///
|
||||
/// i.e.: the following component will be rejected when attempted to be used in the rsx! macro
|
||||
static antipattern_component: FC<()> = |(cx, props)| todo!();
|
||||
|
||||
/// Antipattern: Misusing hooks
|
||||
/// ---------------------------
|
||||
///
|
||||
/// This pattern is an unfortunate one where Dioxus replicates the same behavior as other frameworks. Dioxus supports
|
||||
/// "hooks" - i.e. "memory cells" that allow a value to be stored between renders. This allows other hooks to tap into
|
||||
/// a component's "memory" without explicitly adding all of its data to a struct definition. In Dioxus, hooks are allocated
|
||||
/// with a bump arena and then immediately sealed.
|
||||
///
|
||||
/// This means that hooks may not be misused:
|
||||
/// - Called out of order
|
||||
/// - Called in a conditional
|
||||
/// - Called in loops or callbacks
|
||||
///
|
||||
/// For the most part, Rust helps with rule #3 but does not save you from misusing rule #1 or #2. Dioxus will panic
|
||||
/// if hooks do not downcast to the same data between renders. This is validated by TypeId and, eventually, a custom key.
|
||||
#[derive(PartialEq, Props)]
|
||||
struct MisuedHooksProps {
|
||||
should_render_state: bool,
|
||||
}
|
||||
static AntipatternMisusedHooks: FC<MisuedHooksProps> = |(cx, props)| {
|
||||
if props.should_render_state {
|
||||
// do not place a hook in the conditional!
|
||||
// prefer to move it out of the conditional
|
||||
let (state, set_state) = use_state(cx, || "hello world").classic();
|
||||
rsx!(cx, div { "{state}" })
|
||||
} else {
|
||||
rsx!(cx, div { "Not rendering state" })
|
||||
}
|
||||
};
|
||||
|
||||
/// Antipattern: Downcasting refs and panicking
|
||||
/// ------------------------------------------
|
||||
///
|
||||
/// Occasionally it's useful to get the ref of an element to handle it directly. Elements support downcasting to
|
||||
/// Dioxus' virtual element types as well as their true native counterparts. Downcasting to Dioxus' virtual elements
|
||||
/// will never panic, but downcasting to native elements will fail if on an unsupported platform. We recommend avoiding
|
||||
/// publishing hooks and components that deeply rely on controlling elements using their native `ref`, preferring to
|
||||
/// use their Dioxus Virtual Element counterpart instead.
|
||||
/// This particular code *will panic* due to the unwrap. Try to avoid these types of patterns.
|
||||
/// ---------------------------------
|
||||
/// TODO: Get this to compile properly
|
||||
/// let div_ref = use_node_ref(&cx);
|
||||
///
|
||||
/// cx.render(rsx!{
|
||||
/// div { ref: div_ref, class: "custom class",
|
||||
/// button { "click me to see my parent's class"
|
||||
/// onclick: move |_| if let Some(div_ref) = div_ref {
|
||||
/// panic!("Div class is {}", div_ref.to_native::<web_sys::Element>().unwrap().class())
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// })
|
||||
static _example: FC<()> = |(cx, props)| todo!();
|
||||
|
||||
/// Antipattern: publishing components and hooks with all features enabled
|
||||
/// ----------------------------------------------------------------------
|
||||
///
|
||||
/// The `dioxus` crate combines a bunch of useful utilities together (like the rsx! and html! macros, hooks, and more).
|
||||
/// However, when publishing your custom hook or component, we highly advise using only the `core` feature on the dioxus
|
||||
/// crate. This makes your crate compile faster, makes it more stable, and avoids bringing in incompatible libraries that
|
||||
/// might make it not compile on unsupported platforms.
|
||||
///
|
||||
/// We don't have a code snippet for this, but just prefer to use this line:
|
||||
/// dioxus = { version = "*", features = ["core"]}
|
||||
/// instead of this one:
|
||||
/// dioxus = { version = "*", features = ["web", "desktop", "full"]}
|
||||
/// in your Cargo.toml
|
||||
///
|
||||
/// 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, props)| todo!();
|
||||
|
||||
pub static Example: FC<()> = |(cx, props)| {
|
||||
cx.render(rsx! {
|
||||
AntipatternNoKeys { data: std::collections::HashMap::new() }
|
||||
AntipatternNestedFragments {}
|
||||
})
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
//! Example: The basics of Dioxus
|
||||
//! ----------------------------
|
||||
//!
|
||||
//! This small example covers some of the basics of Dioxus including
|
||||
//! - Components
|
||||
//! - Props
|
||||
//! - Children
|
||||
//! - the rsx! macro
|
||||
|
||||
use dioxus::prelude::*;
|
||||
|
||||
pub static Example: FC<()> = |(cx, props)| {
|
||||
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, props)| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
h1 { "Hello, {props.name}!" }
|
||||
p { "Welcome to the Dioxus framework" }
|
||||
br {}
|
||||
{cx.children()}
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
//! Example: Children
|
||||
//! -----------------
|
||||
//!
|
||||
//! Dioxus supports passing children in from the parent. These children are allocated in the parent and just passed
|
||||
//! into the child. Components that pass in children may not be safely memoized, though in practice it's rare for a
|
||||
//! change in a parent to not result in a different set of children.
|
||||
//!
|
||||
//! In Dioxus, children can *only be a list*. Unlike React, you cannot pass in functions or arbitrary data. This is
|
||||
//! partially a limitation of having static types, but is rather intentional to encourage the use of attributes where
|
||||
//! arbitrary child data might normally be used. Check out the `function driven children` example for how to adopt your
|
||||
//! React pattern to Dioxus' semantics.
|
||||
//!
|
||||
//! Dioxus will let you use the `children` method more than once - and it's semantically *okay* - but you'll likely
|
||||
//! ruin your page if you try to clone elements in this way. Under the hood, Dioxus shares a "mounted ID" for each node,
|
||||
//! and mounting the same VNode in two places will overwrite the first mounted ID. This will likely lead to dead elements.
|
||||
//!
|
||||
//! In the future, this might become a runtime error, so consider it an error today.
|
||||
|
||||
use dioxus::prelude::*;
|
||||
|
||||
pub static Example: FC<()> = |(cx, props)| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
Banner {
|
||||
p { "Some Content1" }
|
||||
}
|
||||
Banner {
|
||||
p { "Some Content2" }
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
pub static Banner: FC<()> = |(cx, props)| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
h1 { "This is a great banner!" }
|
||||
div { class: "content"
|
||||
{cx.children()}
|
||||
}
|
||||
footer { "Wow, what a great footer" }
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -0,0 +1,116 @@
|
||||
//! Example: Conditional Rendering
|
||||
//! ------------------------------
|
||||
//!
|
||||
//! This example shows how to hide or show elements using conditional rendering.
|
||||
//!
|
||||
//! Oftentimes you might want to display a different UI given some sort of condition. This is called "conditional rendering".
|
||||
//! In Dioxus, you can perform conditional rendering with optionals or matching.
|
||||
//!
|
||||
//! The rsx! and html! macros accepts anything that is `IntoIter<Item = impl IntoVnode>`. Options and Results both implement
|
||||
//! IntoIter for impl VNode, so you can use option/result for conditional rendering.
|
||||
|
||||
use dioxus::prelude::*;
|
||||
|
||||
// Convert a boolean conditional into a hide/show
|
||||
#[derive(PartialEq, Props)]
|
||||
pub struct MyProps {
|
||||
should_show: bool,
|
||||
}
|
||||
pub static Example0: FC<MyProps> = |(cx, props)| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
{props.should_show.then(|| rsx!{
|
||||
h1 { "showing the title!" }
|
||||
})}
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
// Convert a boolean conditional into an either/or
|
||||
// Because rsx! is lazy (produces a closure), we cannot use it in two branch arms. To use it in matching/branching, we
|
||||
// must render it.
|
||||
//
|
||||
// Dioxus will let you render any `LazyNodes` into a `VNode` with `cx.render`. `rsx!` also supports the `in cx` syntax
|
||||
// which will do essentially the same thing as `cx.render`.
|
||||
//
|
||||
// In short:
|
||||
// `rsx!(cx, ...)` is shorthand for `cx.render(rsx!(...))`
|
||||
#[derive(PartialEq, Props)]
|
||||
pub struct MyProps1 {
|
||||
should_show: bool,
|
||||
}
|
||||
pub static Example1: FC<MyProps1> = |(cx, props)| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
// With matching
|
||||
{match props.should_show {
|
||||
true => cx.render(rsx!(div {"it is true!"})),
|
||||
false => rsx!(cx, div {"it is false!"}),
|
||||
}}
|
||||
|
||||
// or with just regular conditions
|
||||
{if props.should_show {
|
||||
rsx!(cx, div {"it is true!"})
|
||||
} else {
|
||||
rsx!(cx, div {"it is false!"})
|
||||
}}
|
||||
|
||||
// or with optional chaining
|
||||
{
|
||||
props.should_show
|
||||
.then(|| rsx!(cx, div {"it is true!"}))
|
||||
.unwrap_or_else(|| rsx!(cx, div {"it is false!"}))
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
/// Matching can be expanded
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub enum Color {
|
||||
Green,
|
||||
Yellow,
|
||||
Red,
|
||||
}
|
||||
#[derive(PartialEq, Props)]
|
||||
pub struct MyProps2 {
|
||||
color: Color,
|
||||
}
|
||||
pub static Example2: FC<MyProps2> = |(cx, props)| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
{match props.color {
|
||||
Color::Green => rsx!(cx, div {"it is Green!"}),
|
||||
Color::Yellow => rsx!(cx, div {"it is Yellow!"}),
|
||||
Color::Red => rsx!(cx, div {"it is Red!"}),
|
||||
}}
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
pub static Example: FC<()> = |(cx, props)| {
|
||||
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 }
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
use dioxus::prelude::*;
|
||||
fn main() {}
|
||||
|
||||
pub static Example: FC<()> = |(cx, props)| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
// A controlled component:
|
||||
static ControlledSelect: FC<()> = |(cx, props)| {
|
||||
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, props)| {
|
||||
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"}
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
//! 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. :(
|
||||
|
||||
use dioxus::prelude::*;
|
||||
|
||||
pub static Example: FC<()> = |(cx, props)| {
|
||||
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};
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
//! Example: Null/None Children
|
||||
//! ---------------------------
|
||||
//!
|
||||
//! This is a simple pattern that allows you to return no elements!
|
||||
|
||||
use dioxus::prelude::*;
|
||||
|
||||
pub static Example: FC<()> = |(cx, props)| cx.render(rsx! { Fragment {} });
|
||||
@@ -0,0 +1,85 @@
|
||||
//! Example: Error Handling
|
||||
//! ------------------------
|
||||
//!
|
||||
//! Error handling in Dioxus comes in a few flavors. Because Dioxus is a Rust project, Options and Results are obviously
|
||||
//! the go-to way of wrapping possibly-errored data. However, if a component fails when "unwrapping," everything will crash,
|
||||
//! the page will deadlock, and your users will be sad.
|
||||
//!
|
||||
//! So, obviously, you need to handle your errors.
|
||||
//!
|
||||
//! Fortunately, it's easy to avoid panics, even during quick prototyping.
|
||||
//!
|
||||
//! Here are a few strategies:
|
||||
//! - Leverage the ability to return "None" and propagate None directly
|
||||
//! - Instead of propagating "None" manually, use the "?" syntax sugar
|
||||
//! - Convert Results into Options with .ok()
|
||||
//! - Manually display a separate screen by matching on Options/Results
|
||||
//!
|
||||
//! There *are* plans to add helpful screens for when apps completely panic in Wasm. However, you should really try to
|
||||
//! avoid panicking.
|
||||
use dioxus::prelude::*;
|
||||
fn main() {}
|
||||
|
||||
/// This is one way to go about error handling (just toss things away with unwrap).
|
||||
/// However, if you get it wrong, the whole app will crash.
|
||||
/// This is pretty flimsy.
|
||||
static App: FC<()> = |(cx, props)| {
|
||||
let data = get_data().unwrap();
|
||||
cx.render(rsx!( div { "{data}" } ))
|
||||
};
|
||||
|
||||
/// This is a pretty verbose way of error handling
|
||||
/// However, it's still pretty good since we don't panic, just fail to render anything
|
||||
static App1: FC<()> = |(cx, props)| {
|
||||
let data = match get_data() {
|
||||
Some(data) => data,
|
||||
None => return None,
|
||||
};
|
||||
cx.render(rsx!( div { "{data}" } ))
|
||||
};
|
||||
|
||||
/// This is an even better form of error handling.
|
||||
/// However, it _does_ make the component go blank, which might not be desirable.
|
||||
///
|
||||
/// This type of error handling is good when you have "selectors" that produce Some/None based on some state that's
|
||||
/// already controlled higher in the tree. i.e. displaying a "Username" in a component that should only be shown if
|
||||
/// a user is logged in.
|
||||
///
|
||||
/// Dioxus will throw an error in the console if the None-path is ever taken.
|
||||
static App2: FC<()> = |(cx, props)| {
|
||||
let data = get_data()?;
|
||||
cx.render(rsx!( div { "{data}" } ))
|
||||
};
|
||||
|
||||
/// This is top-tier error handling since it displays a failure state.
|
||||
///
|
||||
/// However, the error is lacking in context.
|
||||
static App3: FC<()> = |(cx, props)| match get_data() {
|
||||
Some(data) => cx.render(rsx!( div { "{data}" } )),
|
||||
None => cx.render(rsx!( div { "Failed to load data :(" } )),
|
||||
};
|
||||
|
||||
/// For errors that return results, it's possible to short-circuit the match-based error handling with `.ok()` which converts
|
||||
/// a Result<T, V> into an Option<T> and lets you abort rendering by early-returning `None`
|
||||
static App4: FC<()> = |(cx, props)| {
|
||||
let data = get_data_err().ok()?;
|
||||
cx.render(rsx!( div { "{data}" } ))
|
||||
};
|
||||
|
||||
/// This is great error handling since it displays a failure state... with context!
|
||||
///
|
||||
/// Hopefully you'll never need to display a screen like this. It's rather bad taste
|
||||
static App5: FC<()> = |(cx, props)| match get_data_err() {
|
||||
Ok(data) => cx.render(rsx!( div { "{data}" } )),
|
||||
Err(c) => cx.render(rsx!( div { "Failed to load data: {c}" } )),
|
||||
};
|
||||
|
||||
// this fetching function produces "nothing"
|
||||
fn get_data() -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
// this fetching function produces "nothing"
|
||||
fn get_data_err() -> Result<String, &'static str> {
|
||||
Result::Err("Failed!")
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
//! Example: Fragments
|
||||
//! ------------------
|
||||
//!
|
||||
//! Dioxus can return multiple elements without a container through the use of the VNode called a "Fragment". Fragments do not
|
||||
//! have a mounted root and are inserted inline with their siblings. There are three ways of creating fragments as outlined
|
||||
//! in the examples below:
|
||||
//! - By returning multiple elements in Rsx!
|
||||
//! - By using the `Fragment` component
|
||||
//! - By using the fragment() method on the node factory
|
||||
|
||||
use dioxus::prelude::*;
|
||||
|
||||
// Returning multiple elements with rsx! or html!
|
||||
static App1: FC<()> = |(cx, props)| {
|
||||
cx.render(rsx! {
|
||||
h1 { }
|
||||
h2 { }
|
||||
h3 { }
|
||||
})
|
||||
};
|
||||
|
||||
// Using the Fragment component
|
||||
static App2: FC<()> = |(cx, props)| {
|
||||
cx.render(rsx! {
|
||||
Fragment {
|
||||
div {}
|
||||
div {}
|
||||
"asd"
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
// Using the `fragment` method on the NodeFactory
|
||||
static App3: FC<()> = |(cx, props)| {
|
||||
cx.render(LazyNodes::new(move |fac| {
|
||||
fac.fragment_from_iter([
|
||||
fac.text(format_args!("A")),
|
||||
fac.text(format_args!("B")),
|
||||
fac.text(format_args!("A")),
|
||||
fac.text(format_args!("B")),
|
||||
])
|
||||
}))
|
||||
};
|
||||
|
||||
pub static Example: FC<()> = |(cx, props)| {
|
||||
cx.render(rsx! {
|
||||
App1 {}
|
||||
App2 {}
|
||||
App3 {}
|
||||
})
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
//! Examples: CSS
|
||||
//! -------------
|
||||
//!
|
||||
//! Originally taken from:
|
||||
//! - https://www.w3schools.com/html/tryit.asp?filename=tryhtml_css_internal
|
||||
//!
|
||||
//! Include global styles in your app!
|
||||
//!
|
||||
//! You can simply drop in a "style" tag and set the inner contents to your stylesheet.
|
||||
//! It's slightly more manual than React, but is less magical.
|
||||
//!
|
||||
//! A coming update with the assets system will make it possible to include global css from child components.
|
||||
|
||||
use dioxus::prelude::*;
|
||||
|
||||
const STYLE: &str = r#"
|
||||
body {background-color: powderblue;}
|
||||
h1 {color: blue;}
|
||||
p {color: red;}
|
||||
"#;
|
||||
|
||||
pub static Example: FC<()> = |(cx, props)| {
|
||||
cx.render(rsx! {
|
||||
head { style { "{STYLE}" } }
|
||||
body {
|
||||
h1 {"This is a heading"}
|
||||
p {"This is a paragraph"}
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
//! Example: Inline Styles
|
||||
//! ----------------------
|
||||
//!
|
||||
//! This example shows how to use inline styles in Dioxus components.
|
||||
//!
|
||||
//! Inline styles function very similarly 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::*;
|
||||
|
||||
pub static Example: FC<()> = |(cx, props)| {
|
||||
cx.render(rsx! {
|
||||
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 allows styles not wrapped in style {}
|
||||
// I haven't noticed any name collisions yet, and am tentatively leaving this behavior in..
|
||||
// Don't rely on it.
|
||||
static Example2: FC<()> = |(cx, props)| {
|
||||
cx.render(rsx! {
|
||||
div { color: "red"
|
||||
"hello world!"
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
//! Example: Iterators
|
||||
//! ------------------
|
||||
//!
|
||||
//! This example demonstrates how to use iterators with Dioxus.
|
||||
//! Iterators must be used through the curly braces item in element bodies.
|
||||
//! While you might be inclined to `.collect::<>` into Html, Dioxus prefers you provide an iterator that
|
||||
//! 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.
|
||||
//!
|
||||
//! However, when the size of an iterator needs to be known for display purposes, collecting is fine.
|
||||
|
||||
use dioxus::prelude::*;
|
||||
|
||||
pub static Example: FC<()> = |(cx, props)| {
|
||||
let example_data = use_state(cx, || 0);
|
||||
|
||||
let v = (0..10).map(|f| {
|
||||
rsx! {
|
||||
li { onclick: move |_| example_data.set(f)
|
||||
"ID: {f}"
|
||||
ul {
|
||||
{(0..10).map(|k| rsx!{
|
||||
li {
|
||||
"Sub iterator: {f}.{k}"
|
||||
}
|
||||
})}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
cx.render(rsx! {
|
||||
h3 {"Selected: {example_data}"}
|
||||
ul {
|
||||
{v}
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -0,0 +1,56 @@
|
||||
//! Example: Listeners
|
||||
//! ------------------
|
||||
//!
|
||||
//! This example demonstrates the various ways listeners may be used in Dioxus.
|
||||
//! Listeners may be at most `FnMut` - but not `FnOnce`.
|
||||
//! Listeners may borrow the lifetime of cx (children and hooks), but not of local (stack) data.
|
||||
|
||||
use dioxus::prelude::*;
|
||||
|
||||
pub static Example: FC<()> = |(cx, props)| {
|
||||
cx.render(rsx! {
|
||||
ButtonList {}
|
||||
NonUpdatingEvents {}
|
||||
DisablePropagation {}
|
||||
})
|
||||
};
|
||||
|
||||
/// We can use `set_name` in multiple closures; the closures automatically *copy* the reference to set_name.
|
||||
static ButtonList: FC<()> = |(cx, props)| {
|
||||
let name = use_state(cx, || "...?");
|
||||
|
||||
let names = ["jack", "jill", "john", "jane"]
|
||||
.iter()
|
||||
.map(move |n| rsx!(button { onclick: move |_| name.set(n), "{n}" }));
|
||||
|
||||
cx.render(rsx!(
|
||||
div {
|
||||
h1 { "Hello, {name}" }
|
||||
{names}
|
||||
}
|
||||
))
|
||||
};
|
||||
|
||||
/// This shows how listeners may be without a visible change in the display.
|
||||
/// Check the console.
|
||||
static NonUpdatingEvents: FC<()> = |(cx, props)| {
|
||||
rsx!(cx, div {
|
||||
button {
|
||||
onclick: move |_| log::info!("Did not cause any updates!")
|
||||
"Click me to log!"
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
static DisablePropagation: FC<()> = |(cx, props)| {
|
||||
rsx!(cx,
|
||||
div {
|
||||
onclick: move |_| log::info!("event propagated to the div!")
|
||||
button {
|
||||
onclick: move |evt| {
|
||||
log::info!("Button will allow propagation");
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
};
|
||||
@@ -0,0 +1,84 @@
|
||||
//! Example: Memoization
|
||||
//! --------------------
|
||||
//!
|
||||
//! This example showcases how memoization works in Dioxus.
|
||||
//!
|
||||
//! Memoization is the process in which Dioxus skips diffing child components if their props don't change.
|
||||
//! In React, components are never memoized unless wrapped in `memo` or configured with `shouldComponentUpdate`.
|
||||
//!
|
||||
//! Due to the safety guarantees of Rust, we can automatically memoize components in some circumstances. Whenever a
|
||||
//! component's properties are valid for the `'static` lifetime, Dioxus will automatically compare the props before
|
||||
//! diffing the component. If the props don't change (according to PartialEq), the component will not be re-rendered.
|
||||
//!
|
||||
//! However, if the props use some generics or borrow from their parent, then Dioxus can't safely supress updates,
|
||||
//! and is forced to render the child. If you think that this behavior is wrong for your usecase, you can implement
|
||||
//! the memo method yourself, but beware, doing so is UNSAFE and may cause issues if you do it wrong.
|
||||
//!
|
||||
//! If you want to gain that little bit extra performance, consider using global state management, signals, or
|
||||
//! memoized collections like im-rc which are designed for this use case.
|
||||
|
||||
use dioxus::prelude::*;
|
||||
|
||||
// By default, components with no props are always memoized.
|
||||
// A props of () is considered empty.
|
||||
pub static Example: FC<()> = |(cx, props)| {
|
||||
cx.render(rsx! {
|
||||
div { "100% memoized!" }
|
||||
})
|
||||
};
|
||||
|
||||
// These props do not borrow any content, and therefore can be safely memoized.
|
||||
// However, the parent *must* create a new string on every render.
|
||||
// Notice how these props implement PartialEq - this is required for 'static props
|
||||
#[derive(PartialEq, Props)]
|
||||
pub struct MyProps1 {
|
||||
name: String,
|
||||
}
|
||||
|
||||
pub static Example1: FC<MyProps1> = |(cx, props)| {
|
||||
cx.render(rsx! {
|
||||
div { "100% memoized! {props.name}" }
|
||||
})
|
||||
};
|
||||
|
||||
// These props do not borrow any content, and therefore can be safely memoized.
|
||||
// In contrast with the `String` example, these props use `Rc<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)]
|
||||
pub struct MyProps2 {
|
||||
name: std::rc::Rc<str>,
|
||||
}
|
||||
|
||||
pub static Example2: FC<MyProps2> = |(cx, props)| {
|
||||
cx.render(rsx! {
|
||||
div { "100% memoized! {props.name}" }
|
||||
})
|
||||
};
|
||||
|
||||
// These props *do* borrow any content, and therefore cannot be safely memoized!.
|
||||
#[derive(PartialEq, Props)]
|
||||
pub struct MyProps3<'a> {
|
||||
name: &'a str,
|
||||
}
|
||||
// We need to manually specify a lifetime that ensures props and scope (the component's state) share the same lifetime.
|
||||
// Using the `pub static Example: 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>, props: &'a MyProps3) -> DomTree<'a> {
|
||||
cx.render(rsx! {
|
||||
div { "Not memoized! {props.name}" }
|
||||
})
|
||||
}
|
||||
|
||||
// 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)]
|
||||
pub struct MyProps4<'a> {
|
||||
onhandle: &'a dyn Fn(),
|
||||
}
|
||||
|
||||
// We need to manually specify a lifetime that ensures props and scope (the component's state) share the same lifetime.
|
||||
fn Example4<'a>(cx: Context<'a>, props: &'a MyProps4) -> DomTree<'a> {
|
||||
cx.render(rsx! {
|
||||
div { "Not memoized!", onclick: move |_| (props.onhandle)() }
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
use dioxus::prelude::*;
|
||||
fn main() {}
|
||||
|
||||
pub static Example: FC<()> = |(cx, props)| {
|
||||
let p = 10;
|
||||
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
use dioxus::prelude::*;
|
||||
fn main() {}
|
||||
|
||||
pub static Example: FC<()> = |(cx, props)| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
//! Example: Spread pattern for Components
|
||||
//! --------------------------------------
|
||||
//!
|
||||
//! Dioxus supports the "spread" pattern for manually building a component's properties. This is useful when props
|
||||
//! are passed down from a parent, or it's more ergonomic to construct props from outside the rsx! macro.
|
||||
//!
|
||||
//! To use the spread pattern, simply pass ".." followed by a Rust expression. This pattern also supports overriding
|
||||
//! values, using the manual props as the base and then modifying fields specified with non-spread attributes.
|
||||
|
||||
use dioxus::prelude::*;
|
||||
|
||||
pub static Example: FC<()> = |(cx, props)| {
|
||||
let props = MyProps {
|
||||
count: 0,
|
||||
live: true,
|
||||
name: "Dioxus",
|
||||
};
|
||||
cx.render(rsx! {
|
||||
Example1 { ..props, count: 10, div {"child"} }
|
||||
})
|
||||
};
|
||||
|
||||
#[derive(PartialEq, Props)]
|
||||
pub struct MyProps {
|
||||
count: u32,
|
||||
live: bool,
|
||||
name: &'static str,
|
||||
}
|
||||
|
||||
pub static Example1: FC<MyProps> = |cx, MyProps { count, live, name }| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
h1 { "Hello, {name}"}
|
||||
h3 {"Are we alive? {live}"}
|
||||
p {"Count is {count}"}
|
||||
{ cx.children() }
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
use dioxus::prelude::*;
|
||||
fn main() {}
|
||||
|
||||
pub static Example: FC<()> = |(cx, props)| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
//! Example: Suspense
|
||||
//! -----------------
|
||||
//! 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 propagate to the UI when the future completes.
|
||||
//!
|
||||
//! 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::*;
|
||||
#[derive(serde::Deserialize)]
|
||||
struct DogApi {
|
||||
message: String,
|
||||
}
|
||||
const ENDPOINT: &str = "https://dog.ceo/api/breeds/image/random";
|
||||
|
||||
pub static Example: FC<()> = |(cx, props)| {
|
||||
let doggo = use_suspense(
|
||||
cx,
|
||||
|| surf::get(ENDPOINT).recv_json::<DogApi>(),
|
||||
|cx, res| match res {
|
||||
Ok(res) => rsx!(
|
||||
cx,
|
||||
img {
|
||||
src: "{res.message}"
|
||||
}
|
||||
),
|
||||
Err(_) => rsx!(cx, div { "No doggos for you :(" }),
|
||||
},
|
||||
);
|
||||
|
||||
cx.render(rsx!(
|
||||
div {
|
||||
h1 {"Waiting for a doggo..."}
|
||||
{doggo}
|
||||
}
|
||||
))
|
||||
};
|
||||
@@ -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, props)| {
|
||||
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) = use_task(cx, move || async move {
|
||||
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.resume()
|
||||
}
|
||||
button {
|
||||
"Switch counting direcion"
|
||||
onclick: move |_| {
|
||||
direction *= -1;
|
||||
task.restart();
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
pub static Example: FC<()> = |(cx, props)| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
use dioxus::prelude::*;
|
||||
use dioxus::ssr;
|
||||
|
||||
pub static Example: FC<()> = |(cx, props)| {
|
||||
let as_string = use_state(cx, || {
|
||||
// Currently, SSR is only supported for whole VirtualDOMs
|
||||
// This is an easy/low hanging fruit to improve upon
|
||||
let mut dom = VirtualDom::new(SomeApp);
|
||||
dom.rebuild();
|
||||
ssr::render_vdom(&dom, |c| c)
|
||||
});
|
||||
|
||||
cx.render(rsx! {
|
||||
div { "{as_string}" }
|
||||
})
|
||||
};
|
||||
|
||||
static SomeApp: FC<()> = |(cx, props)| {
|
||||
cx.render(rsx! {
|
||||
div { style: {background_color: "blue"}
|
||||
h1 {"Some amazing app or component"}
|
||||
p {"Things are great"}
|
||||
}
|
||||
})
|
||||
};
|
||||
+11
-9
@@ -46,7 +46,8 @@ static App: FC<()> = |(cx, props)| {
|
||||
});
|
||||
|
||||
cx.render(rsx! {
|
||||
div { style: { width: "400px", height: "400px", position: "relative", background: "yellow" }
|
||||
div {
|
||||
width: "400px", height: "400px", position: "relative", background: "yellow"
|
||||
button { "reset", onclick: move |_| {} }
|
||||
Horsey { pos: *p1, "horsey 1" }
|
||||
Horsey { pos: *p2, "horsey 2" }
|
||||
@@ -54,18 +55,19 @@ static App: FC<()> = |(cx, props)| {
|
||||
})
|
||||
};
|
||||
|
||||
#[derive(PartialEq, Props)]
|
||||
struct HorseyProps {
|
||||
#[derive(Props)]
|
||||
struct HorseyProps<'a> {
|
||||
pos: i32,
|
||||
children: ScopeChildren<'a>,
|
||||
}
|
||||
|
||||
static Horsey: FC<HorseyProps> = |(cx, props)| {
|
||||
fn Horsey<'a>((cx, props): Scope<'a, HorseyProps<'a>>) -> Element {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
button { "pause" }
|
||||
div {
|
||||
{cx.children()}
|
||||
button { "pause" }
|
||||
div {
|
||||
{&props.children}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
}
|
||||
+5
-5
@@ -20,12 +20,12 @@ pub struct Client {
|
||||
}
|
||||
|
||||
static App: FC<()> = |(cx, _)| {
|
||||
let clients = use_ref(cx, || vec![] as Vec<Client>);
|
||||
let scene = use_state(cx, || Scene::ClientsList);
|
||||
let mut clients = use_ref(cx, || vec![] as Vec<Client>);
|
||||
let mut scene = use_state(cx, || Scene::ClientsList);
|
||||
|
||||
let firstname = use_state(cx, || String::new());
|
||||
let lastname = use_state(cx, || String::new());
|
||||
let description = use_state(cx, || String::new());
|
||||
let mut firstname = use_state(cx, || String::new());
|
||||
let mut lastname = use_state(cx, || String::new());
|
||||
let mut description = use_state(cx, || String::new());
|
||||
|
||||
let scene = match *scene {
|
||||
Scene::ClientsList => {
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,109 @@
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_hooks::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
|
||||
fn main() {
|
||||
dioxus_desktop::launch(App, |c| c)
|
||||
}
|
||||
|
||||
enum Scene {
|
||||
ClientsList,
|
||||
NewClientForm,
|
||||
Settings,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Client {
|
||||
pub first_name: String,
|
||||
pub last_name: String,
|
||||
pub description: String,
|
||||
}
|
||||
|
||||
static App: FC<()> = |(cx, _)| {
|
||||
let mut scene = use_state(cx, || Scene::ClientsList);
|
||||
let clients = use_ref(cx, || vec![] as Vec<Client>);
|
||||
|
||||
let mut firstname = use_state(cx, String::new);
|
||||
let mut lastname = use_state(cx, String::new);
|
||||
let mut description = use_state(cx, String::new);
|
||||
|
||||
let scene = match scene.get() {
|
||||
Scene::ClientsList => {
|
||||
rsx!(cx, div { class: "crm"
|
||||
h2 { "List of clients" margin_bottom: "10px" }
|
||||
div { class: "clients" margin_left: "10px"
|
||||
{clients.read().iter().map(|client| rsx!(
|
||||
div { class: "client" style: "margin-bottom: 50px"
|
||||
p { "First Name: {client.first_name}" }
|
||||
p { "Last Name: {client.last_name}" }
|
||||
p {"Description: {client.description}"}
|
||||
})
|
||||
)}
|
||||
}
|
||||
button { class: "pure-button pure-button-primary" onclick: move |_| scene.set(Scene::NewClientForm), "Add New" }
|
||||
button { class: "pure-button" onclick: move |_| scene.set(Scene::Settings), "Settings" }
|
||||
})
|
||||
}
|
||||
Scene::NewClientForm => {
|
||||
let add_new = move |_| {
|
||||
clients.write().push(Client {
|
||||
description: (*description).clone(),
|
||||
first_name: (*firstname).clone(),
|
||||
last_name: (*lastname).clone(),
|
||||
});
|
||||
description.set(String::new());
|
||||
firstname.set(String::new());
|
||||
lastname.set(String::new());
|
||||
};
|
||||
rsx!(cx, div { class: "crm"
|
||||
h2 {"Add new client" margin_bottom: "10px" }
|
||||
form { class: "pure-form"
|
||||
input { class: "new-client firstname" placeholder: "First name" value: "{firstname}"
|
||||
oninput: move |evt| firstname.set(evt.value)
|
||||
}
|
||||
input { class: "new-client lastname" placeholder: "Last name" value: "{lastname}"
|
||||
oninput: move |evt| lastname.set(evt.value)
|
||||
}
|
||||
textarea { class: "new-client description" placeholder: "Description" value: "{description}"
|
||||
oninput: move |evt| description.set(evt.value)
|
||||
}
|
||||
}
|
||||
button { class: "pure-button pure-button-primary", onclick: {add_new}, "Add New" }
|
||||
button { class: "pure-button", onclick: move |_| scene.set(Scene::ClientsList), "Go Back" }
|
||||
})
|
||||
}
|
||||
Scene::Settings => {
|
||||
rsx!(cx, div {
|
||||
h2 { "Settings" margin_bottom: "10px" }
|
||||
button {
|
||||
background: "rgb(202, 60, 60)"
|
||||
class: "pure-button pure-button-primary"
|
||||
onclick: move |_| {
|
||||
clients.write().clear();
|
||||
scene.set(Scene::ClientsList);
|
||||
},
|
||||
"Remove all clients"
|
||||
}
|
||||
button {
|
||||
class: "pure-button pure-button-primary"
|
||||
onclick: move |_| scene.set(Scene::ClientsList),
|
||||
"Go Back"
|
||||
}
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
rsx!(cx, body {
|
||||
link {
|
||||
rel: "stylesheet"
|
||||
href: "https://unpkg.com/purecss@2.0.6/build/pure-min.css"
|
||||
integrity: "sha384-Uu6IeWbM+gzNVXJcM9XV3SohHtmWE+3VGi496jvgX1jyvDTXfdK+rfZc8C1Aehk5"
|
||||
crossorigin: "anonymous"
|
||||
}
|
||||
margin_left: "35%"
|
||||
h1 {"Dioxus CRM Example"}
|
||||
{scene}
|
||||
})
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_core_macro::*;
|
||||
|
||||
use dioxus_html as dioxus_elements;
|
||||
|
||||
fn main() {
|
||||
dioxus_desktop::launch(App, |c| c);
|
||||
}
|
||||
|
||||
static App: FC<()> = |(cx, props)| {
|
||||
cx.render(rsx!(
|
||||
div {
|
||||
"hello world!"
|
||||
}
|
||||
{(0..10).map(|f| rsx!( div {"abc {f}"}))}
|
||||
))
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_hooks::*;
|
||||
|
||||
use dioxus_html as dioxus_elements;
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1 @@
|
||||
fn main() {}
|
||||
@@ -0,0 +1,379 @@
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
button {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: none;
|
||||
font-size: 100%;
|
||||
vertical-align: baseline;
|
||||
font-family: inherit;
|
||||
font-weight: inherit;
|
||||
color: inherit;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
body {
|
||||
font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
line-height: 1.4em;
|
||||
background: #f5f5f5;
|
||||
color: #4d4d4d;
|
||||
min-width: 230px;
|
||||
max-width: 550px;
|
||||
margin: 0 auto;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
:focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.todoapp {
|
||||
background: #fff;
|
||||
margin: 130px 0 40px 0;
|
||||
position: relative;
|
||||
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2),
|
||||
0 25px 50px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.todoapp input::-webkit-input-placeholder {
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
color: #e6e6e6;
|
||||
}
|
||||
|
||||
.todoapp input::-moz-placeholder {
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
color: #e6e6e6;
|
||||
}
|
||||
|
||||
.todoapp input::input-placeholder {
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
color: #e6e6e6;
|
||||
}
|
||||
|
||||
.todoapp h1 {
|
||||
position: absolute;
|
||||
top: -155px;
|
||||
width: 100%;
|
||||
font-size: 100px;
|
||||
font-weight: 100;
|
||||
text-align: center;
|
||||
color: rgba(175, 47, 47, 0.15);
|
||||
-webkit-text-rendering: optimizeLegibility;
|
||||
-moz-text-rendering: optimizeLegibility;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
|
||||
.new-todo,
|
||||
.edit {
|
||||
position: relative;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
font-size: 24px;
|
||||
font-family: inherit;
|
||||
font-weight: inherit;
|
||||
line-height: 1.4em;
|
||||
border: 0;
|
||||
color: inherit;
|
||||
padding: 6px;
|
||||
border: 1px solid #999;
|
||||
box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
|
||||
box-sizing: border-box;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.new-todo {
|
||||
padding: 16px 16px 16px 60px;
|
||||
border: none;
|
||||
background: rgba(0, 0, 0, 0.003);
|
||||
box-shadow: inset 0 -2px 1px rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
|
||||
.main {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
border-top: 1px solid #e6e6e6;
|
||||
}
|
||||
|
||||
.toggle-all {
|
||||
text-align: center;
|
||||
border: none;
|
||||
/* Mobile Safari */
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.toggle-all+label {
|
||||
width: 60px;
|
||||
height: 34px;
|
||||
font-size: 0;
|
||||
position: absolute;
|
||||
top: -52px;
|
||||
left: -13px;
|
||||
-webkit-transform: rotate(90deg);
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.toggle-all+label:before {
|
||||
content: '❯';
|
||||
font-size: 22px;
|
||||
color: #e6e6e6;
|
||||
padding: 10px 27px 10px 27px;
|
||||
}
|
||||
|
||||
.toggle-all:checked+label:before {
|
||||
color: #737373;
|
||||
}
|
||||
|
||||
.todo-list {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.todo-list li {
|
||||
position: relative;
|
||||
font-size: 24px;
|
||||
border-bottom: 1px solid #ededed;
|
||||
}
|
||||
|
||||
.todo-list li:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.todo-list li.editing {
|
||||
border-bottom: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.todo-list li.editing .edit {
|
||||
display: block;
|
||||
width: 506px;
|
||||
padding: 12px 16px;
|
||||
margin: 0 0 0 43px;
|
||||
}
|
||||
|
||||
.todo-list li.editing .view {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.todo-list li .toggle {
|
||||
text-align: center;
|
||||
width: 40px;
|
||||
/* auto, since non-WebKit browsers doesn't support input styling */
|
||||
height: auto;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
margin: auto 0;
|
||||
border: none;
|
||||
/* Mobile Safari */
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
.todo-list li .toggle {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.todo-list li .toggle+label {
|
||||
/*
|
||||
Firefox requires `#` to be escaped - https://bugzilla.mozilla.org/show_bug.cgi?id=922433
|
||||
IE and Edge requires *everything* to be escaped to render, so we do that instead of just the `#` - https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7157459/
|
||||
*/
|
||||
background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23ededed%22%20stroke-width%3D%223%22/%3E%3C/svg%3E');
|
||||
background-repeat: no-repeat;
|
||||
background-position: center left;
|
||||
}
|
||||
|
||||
.todo-list li .toggle:checked+label {
|
||||
background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23bddad5%22%20stroke-width%3D%223%22/%3E%3Cpath%20fill%3D%22%235dc2af%22%20d%3D%22M72%2025L42%2071%2027%2056l-4%204%2020%2020%2034-52z%22/%3E%3C/svg%3E');
|
||||
}
|
||||
|
||||
.todo-list li label {
|
||||
word-break: break-all;
|
||||
padding: 15px 15px 15px 60px;
|
||||
display: block;
|
||||
line-height: 1.2;
|
||||
transition: color 0.4s;
|
||||
}
|
||||
|
||||
.todo-list li.completed label {
|
||||
color: #d9d9d9;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.todo-list li .destroy {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 10px;
|
||||
bottom: 0;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin: auto 0;
|
||||
font-size: 30px;
|
||||
color: #cc9a9a;
|
||||
margin-bottom: 11px;
|
||||
transition: color 0.2s ease-out;
|
||||
}
|
||||
|
||||
.todo-list li .destroy:hover {
|
||||
color: #af5b5e;
|
||||
}
|
||||
|
||||
.todo-list li .destroy:after {
|
||||
content: '×';
|
||||
}
|
||||
|
||||
.todo-list li:hover .destroy {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.todo-list li .edit {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.todo-list li.editing:last-child {
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
color: #777;
|
||||
padding: 10px 15px;
|
||||
height: 20px;
|
||||
text-align: center;
|
||||
border-top: 1px solid #e6e6e6;
|
||||
}
|
||||
|
||||
.footer:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
height: 50px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2),
|
||||
0 8px 0 -3px #f6f6f6,
|
||||
0 9px 1px -3px rgba(0, 0, 0, 0.2),
|
||||
0 16px 0 -6px #f6f6f6,
|
||||
0 17px 2px -6px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.todo-count {
|
||||
float: left;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.todo-count strong {
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.filters {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.filters li {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.filters li a {
|
||||
color: inherit;
|
||||
margin: 3px;
|
||||
padding: 3px 7px;
|
||||
text-decoration: none;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.filters li a:hover {
|
||||
border-color: rgba(175, 47, 47, 0.1);
|
||||
}
|
||||
|
||||
.filters li a.selected {
|
||||
border-color: rgba(175, 47, 47, 0.2);
|
||||
}
|
||||
|
||||
.clear-completed,
|
||||
html .clear-completed:active {
|
||||
float: right;
|
||||
position: relative;
|
||||
line-height: 20px;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.clear-completed:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.info {
|
||||
margin: 65px auto 0;
|
||||
color: #bfbfbf;
|
||||
font-size: 10px;
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.info p {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.info a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.info a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/*
|
||||
Hack to remove background from Mobile Safari.
|
||||
Can't use it globally since it destroys checkboxes in Firefox
|
||||
*/
|
||||
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||
|
||||
.toggle-all,
|
||||
.todo-list li .toggle {
|
||||
background: none;
|
||||
}
|
||||
|
||||
.todo-list li .toggle {
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 430px) {
|
||||
.footer {
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.filters {
|
||||
bottom: 10px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
#![allow(non_upper_case_globals, non_snake_case)]
|
||||
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_hooks::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
use simple_logger::SimpleLogger;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
fn main() {
|
||||
if cfg!(debug_assertions) {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
}
|
||||
|
||||
dioxus_desktop::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,
|
||||
}
|
||||
pub type Todos = HashMap<u32, TodoItem>;
|
||||
|
||||
pub static App: FC<()> = |(cx, _)| {
|
||||
// Share our TodoList to the todos themselves
|
||||
use_provide_state(cx, Todos::new);
|
||||
|
||||
// Save state for the draft, filter
|
||||
let draft = use_state(cx, || "".to_string());
|
||||
let filter = use_state(cx, || FilterState::All);
|
||||
let mut todo_id = use_state(cx, || 0);
|
||||
|
||||
// Consume the todos
|
||||
let todos = use_shared_state::<Todos>(cx)?;
|
||||
|
||||
// Filter the todos based on the filter state
|
||||
let mut filtered_todos = todos
|
||||
.read()
|
||||
.iter()
|
||||
.filter(|(_, item)| match *filter {
|
||||
FilterState::All => true,
|
||||
FilterState::Active => !item.checked,
|
||||
FilterState::Completed => item.checked,
|
||||
})
|
||||
.map(|f| *f.0)
|
||||
.collect::<Vec<_>>();
|
||||
filtered_todos.sort_unstable();
|
||||
|
||||
// Define the actions to manage the todolist
|
||||
let mut submit_todo = move || {
|
||||
if !draft.is_empty() {
|
||||
todos.write().insert(
|
||||
*todo_id,
|
||||
TodoItem {
|
||||
id: *todo_id,
|
||||
checked: false,
|
||||
contents: draft.get().clone(),
|
||||
},
|
||||
);
|
||||
todo_id += 1;
|
||||
draft.set("".to_string());
|
||||
}
|
||||
};
|
||||
let clear_completed = move || {
|
||||
todos.write().retain(|_, todo| todo.checked == false);
|
||||
};
|
||||
|
||||
// Some assists in actually rendering the content
|
||||
let show_clear_completed = todos.read().values().any(|todo| todo.checked);
|
||||
let items_left = filtered_todos.len();
|
||||
let item_text = match items_left {
|
||||
1 => "item",
|
||||
_ => "items",
|
||||
};
|
||||
|
||||
cx.render(rsx!{
|
||||
section { class: "todoapp"
|
||||
style { {[include_str!("./todomvc.css")]} }
|
||||
div {
|
||||
header { class: "header"
|
||||
h1 {"todos"}
|
||||
input {
|
||||
class: "new-todo"
|
||||
placeholder: "What needs to be done?"
|
||||
value: "{draft}"
|
||||
autofocus: "true"
|
||||
oninput: move |evt| draft.set(evt.value)
|
||||
onkeydown: move |evt| {
|
||||
if evt.key == "Enter" {
|
||||
submit_todo();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ul { class: "todo-list"
|
||||
{filtered_todos.iter().map(|id| rsx!(TodoEntry { key: "{id}", id: *id }))}
|
||||
}
|
||||
{(!todos.read().is_empty()).then(|| rsx!(
|
||||
footer { class: "footer"
|
||||
span { class: "todo-count" strong {"{items_left} "} span {"{item_text} left"} }
|
||||
ul { class: "filters"
|
||||
li { class: "All", a { onclick: move |_| filter.set(FilterState::All), "All" }}
|
||||
li { class: "Active", a { onclick: move |_| filter.set(FilterState::Active), "Active" }}
|
||||
li { class: "Completed", a { onclick: move |_| filter.set(FilterState::Completed), "Completed" }}
|
||||
}
|
||||
{(show_clear_completed).then(|| rsx!(
|
||||
button { class: "clear-completed", onclick: move |_| clear_completed(),
|
||||
"Clear completed"
|
||||
}
|
||||
))}
|
||||
}
|
||||
))}
|
||||
}
|
||||
}
|
||||
footer { class: "info"
|
||||
p {"Double-click to edit a todo"}
|
||||
p { "Created by ", a { "jkelleyrtp", href: "http://github.com/jkelleyrtp/" }}
|
||||
p { "Part of ", a { "TodoMVC", href: "http://todomvc.com" }}
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
#[derive(PartialEq, Props)]
|
||||
pub struct TodoEntryProps {
|
||||
id: u32,
|
||||
}
|
||||
|
||||
pub fn TodoEntry((cx, props): Scope<TodoEntryProps>) -> Element {
|
||||
let todos = use_shared_state::<Todos>(cx)?;
|
||||
|
||||
let _todos = todos.read();
|
||||
let todo = _todos.get(&props.id)?;
|
||||
|
||||
let is_editing = use_state(cx, || false);
|
||||
let completed = if todo.checked { "completed" } else { "" };
|
||||
|
||||
cx.render(rsx!{
|
||||
li { class: "{completed}"
|
||||
div { class: "view"
|
||||
input { class: "toggle" r#type: "checkbox" id: "cbg-{todo.id}" checked: "{todo.checked}"
|
||||
onchange: move |evt| {
|
||||
if let Some(todo) = todos.write().get_mut(&props.id) {
|
||||
todo.checked = evt.value.parse().unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
label { r#for: "cbg-{todo.id}" pointer_events: "none"
|
||||
"{todo.contents}"
|
||||
}
|
||||
|
||||
{is_editing.then(|| rsx!{
|
||||
input { value: "{todo.contents}"
|
||||
oninput: move |evt| {
|
||||
if let Some(todo) = todos.write().get_mut(&props.id) {
|
||||
todo.contents = evt.value
|
||||
}
|
||||
},
|
||||
}
|
||||
})}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -10,11 +10,11 @@ fn main() {
|
||||
type RowList = im_rc::HashMap<usize, Rc<str>, FxBuildHasher>;
|
||||
|
||||
static App: FC<()> = |(cx, _props)| {
|
||||
let items = use_state(cx, || RowList::default());
|
||||
let mut items = use_state(cx, || RowList::default());
|
||||
|
||||
let create_rendered_rows = move |from, num| move |_| items.set(create_row_list(from, num));
|
||||
|
||||
let append_1_000_rows =
|
||||
let mut append_1_000_rows =
|
||||
move |_| items.set(create_row_list(items.len(), 1000).union((*items).clone()));
|
||||
|
||||
let update_every_10th_row = move |_| {
|
||||
|
||||
@@ -74,14 +74,15 @@ static App: FC<()> = |(cx, props)| {
|
||||
struct CalculatorKeyProps<'a> {
|
||||
name: &'static str,
|
||||
onclick: &'a dyn Fn(MouseEvent),
|
||||
children: ScopeChildren<'a>,
|
||||
}
|
||||
|
||||
fn CalculatorKey<'a>((cx, props): Scope<'a, CalculatorKeyProps>) -> Element<'a> {
|
||||
fn CalculatorKey<'a>((cx, props): Scope<'a, CalculatorKeyProps<'a>>) -> Element<'a> {
|
||||
cx.render(rsx! {
|
||||
button {
|
||||
class: "calculator-key {props.name}"
|
||||
onclick: {props.onclick}
|
||||
{cx.children()}
|
||||
{&props.children}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
use dioxus::prelude::*;
|
||||
use dioxus::router::Router;
|
||||
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub enum Route {
|
||||
// #[at("/")]
|
||||
Home,
|
||||
|
||||
// #[at("/:id")]
|
||||
AllUsers { page: u32 },
|
||||
|
||||
// #[at("/:id")]
|
||||
User { id: u32 },
|
||||
|
||||
// #[at("/:id")]
|
||||
BlogList { page: u32 },
|
||||
|
||||
// #[at("/:id")]
|
||||
BlogPost { post_id: u32 },
|
||||
|
||||
// #[at("/404")]
|
||||
// #[not_found]
|
||||
NotFound,
|
||||
}
|
||||
|
||||
static App: FC<()> = |(cx, props)| {
|
||||
let route = use_router(cx, Route::parse)?;
|
||||
|
||||
cx.render(rsx! {
|
||||
nav {
|
||||
Link { to: Route::Home, "Go home!" }
|
||||
Link { to: Route::AllUsers { page: 0 }, "List all users" }
|
||||
Link { to: Route::BlogList { page: 0 }, "Blog posts" }
|
||||
}
|
||||
{match route {
|
||||
Route::Home => rsx!("Home"),
|
||||
Route::AllUsers { page } => rsx!("All users - page {page}"),
|
||||
Route::User { id } => rsx!("User - id: {id}"),
|
||||
Route::BlogList { page } => rsx!("Blog posts - page {page}"),
|
||||
Route::BlogPost { post_id } => rsx!("Blog post - post {post_id}"),
|
||||
Route::NotFound => rsx!("Not found"),
|
||||
}}
|
||||
footer {}
|
||||
})
|
||||
};
|
||||
|
||||
impl Route {
|
||||
// Generate the appropriate route from the "tail" end of the URL
|
||||
fn parse(url: &str) -> Self {
|
||||
use Route::*;
|
||||
match url {
|
||||
"/" => Home,
|
||||
"/users" => AllUsers { page: 1 },
|
||||
"/users/:page" => AllUsers { page: 1 },
|
||||
"/users/:page/:id" => User { id: 1 },
|
||||
"/blog" => BlogList { page: 1 },
|
||||
"/blog/:page" => BlogList { page: 1 },
|
||||
"/blog/:page/:id" => BlogPost { post_id: 1 },
|
||||
_ => NotFound,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Routable for Route {
|
||||
fn from_path(path: &str, params: &std::collections::HashMap<&str, &str>) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn to_path(&self) -> String {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn routes() -> Vec<&'static str> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn not_found_route() -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn recognize(pathname: &str) -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -162,13 +162,13 @@ pub static Example: FC<()> = |(cx, props)| {
|
||||
|
||||
// Can pass in props directly as an expression
|
||||
{{
|
||||
let props = TallerProps {a: "hello"};
|
||||
let props = TallerProps {a: "hello", children: Default::default()};
|
||||
rsx!(Taller { ..props })
|
||||
}}
|
||||
|
||||
// Spreading can also be overridden manually
|
||||
Taller {
|
||||
..TallerProps { a: "ballin!" }
|
||||
..TallerProps { a: "ballin!", children: Default::default() }
|
||||
a: "not ballin!"
|
||||
}
|
||||
|
||||
@@ -180,7 +180,7 @@ pub static Example: FC<()> = |(cx, props)| {
|
||||
|
||||
mod baller {
|
||||
use super::*;
|
||||
#[derive(PartialEq, Props)]
|
||||
#[derive(Props, PartialEq)]
|
||||
pub struct BallerProps {}
|
||||
|
||||
/// This component totally balls
|
||||
@@ -189,13 +189,16 @@ mod baller {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Props)]
|
||||
pub struct TallerProps {
|
||||
#[derive(Props)]
|
||||
pub struct TallerProps<'a> {
|
||||
a: &'static str,
|
||||
|
||||
#[builder(default)]
|
||||
children: ScopeChildren<'a>,
|
||||
}
|
||||
|
||||
/// This component is taller than most :)
|
||||
pub fn Taller(_: Scope<TallerProps>) -> Element {
|
||||
pub fn Taller<'a>(_: Scope<'a, TallerProps<'a>>) -> Element {
|
||||
let b = true;
|
||||
todo!()
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
use dioxus::virtual_dom::VirtualDom;
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
fn main() {
|
||||
let mut dom = VirtualDom::new(App);
|
||||
dom.rebuild();
|
||||
println!(
|
||||
"{}",
|
||||
dioxus_ssr::render_vdom(&dom, |c| c.newline(true).indent(true))
|
||||
)
|
||||
}
|
||||
|
||||
pub static App: FC<()> = |(cx, props)| {
|
||||
cx.render(rsx!(
|
||||
div {
|
||||
class: "overflow-hidden"
|
||||
ul {
|
||||
{(0..10).map(|i| rsx!{ li { class: "flex flex-col", "entry: {i}"}})}
|
||||
}
|
||||
"hello world!"
|
||||
}
|
||||
))
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
//! Example: realworld usage of hydration
|
||||
//!
|
||||
//!
|
||||
use dioxus::virtual_dom::VirtualDom;
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_hooks::use_state;
|
||||
use dioxus_html as dioxus_elements;
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,298 @@
|
||||
<!-- a js-only interpreter for the dioxus patch stream :) -->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
|
||||
<meta charset="UTF-8" />
|
||||
<link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div>hello dioxus</div>
|
||||
</body>
|
||||
<script>
|
||||
class Interpreter {
|
||||
constructor(root) {
|
||||
this.stack = [root];
|
||||
}
|
||||
|
||||
top() {
|
||||
return this.stack[this.stack.length - 1];
|
||||
}
|
||||
|
||||
pop() {
|
||||
return this.stack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
class OPTABLE {
|
||||
// 0
|
||||
SetText(edit, interp) {
|
||||
interp.top(interp.stack).textContent = edit.text;
|
||||
}
|
||||
// 1
|
||||
RemoveSelfAndNextSiblings(edit) {
|
||||
const node = interp.pop();
|
||||
let sibling = node.nextSibling;
|
||||
while (sibling) {
|
||||
const temp = sibling.nextSibling;
|
||||
sibling.remove();
|
||||
sibling = temp;
|
||||
}
|
||||
node.remove();
|
||||
}
|
||||
|
||||
// 2
|
||||
ReplaceWith(edit, interp) {
|
||||
const newNode = interp.pop();
|
||||
const oldNode = interp.pop();
|
||||
oldNode.replaceWith(newNode);
|
||||
interp.stack.push(newNode);
|
||||
}
|
||||
|
||||
// 3
|
||||
SetAttribute(edit, interp) {
|
||||
const name = edit.name;
|
||||
const value = edit.value;
|
||||
const node = interp.top(interp.stack);
|
||||
node.setAttribute(name, value);
|
||||
|
||||
// Some attributes are "volatile" and don't work through `setAttribute`.
|
||||
if ((name === "value", interp)) {
|
||||
node.value = value;
|
||||
}
|
||||
if ((name === "checked", interp)) {
|
||||
node.checked = true;
|
||||
}
|
||||
if ((name === "selected", interp)) {
|
||||
node.selected = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 4
|
||||
RemoveAttribute(edit, interp) {
|
||||
const name = edit.name;
|
||||
const node = interp.top(interp.stack);
|
||||
node.removeAttribute(name);
|
||||
|
||||
// Some attributes are "volatile" and don't work through `removeAttribute`.
|
||||
if ((name === "value", interp)) {
|
||||
node.value = null;
|
||||
}
|
||||
if ((name === "checked", interp)) {
|
||||
node.checked = false;
|
||||
}
|
||||
if ((name === "selected", interp)) {
|
||||
node.selected = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 5
|
||||
PushReverseChild(edit, interp) {
|
||||
const n = edit.n;
|
||||
const parent = interp.top(interp.stack);
|
||||
const children = parent.childNodes;
|
||||
const child = children[children.length - n - 1];
|
||||
interp.stack.push(child);
|
||||
}
|
||||
|
||||
// 6
|
||||
PopPushChild(edit, interp) {
|
||||
const n = edit.n;
|
||||
interp.pop();
|
||||
const parent = interp.top(interp.stack);
|
||||
const children = parent.childNodes;
|
||||
const child = children[n];
|
||||
interp.stack.push(child);
|
||||
}
|
||||
|
||||
// 7
|
||||
Pop(edit, interp) {
|
||||
interp.pop();
|
||||
}
|
||||
|
||||
// 8
|
||||
AppendChild(edit, interp) {
|
||||
console.log(interp.stack);
|
||||
const child = interp.pop();
|
||||
console.log(interp.stack);
|
||||
interp.top().appendChild(child);
|
||||
}
|
||||
|
||||
// 9
|
||||
CreateTextNode(edit, interp) {
|
||||
// interp.stack.push(document.createTextNode("asd"));
|
||||
console.log(interp.stack);
|
||||
|
||||
interp.stack.push(document.createTextNode(edit.text));
|
||||
console.log(interp.stack);
|
||||
}
|
||||
|
||||
// 10
|
||||
CreateElement(edit, interp) {
|
||||
const tagName = edit.tag_name;
|
||||
interp.stack.push(document.createElement(tagName));
|
||||
}
|
||||
|
||||
// 11
|
||||
NewEventListener(edit, interp) {
|
||||
// todo!
|
||||
const eventId = mem32[i++];
|
||||
const eventType = interp.getCachedString(eventId);
|
||||
const a = mem32[i++];
|
||||
const b = mem32[i++];
|
||||
const el = interp.top(interp.stack);
|
||||
el.addEventListener(eventType, interp.eventHandler);
|
||||
el[`dodrio-a-${eventType}`] = a;
|
||||
el[`dodrio-b-${eventType}`] = b;
|
||||
}
|
||||
|
||||
// 12
|
||||
UpdateEventListener(edit, interp) {
|
||||
// todo!
|
||||
const eventId = mem32[i++];
|
||||
const eventType = interp.getCachedString(eventId);
|
||||
const el = interp.top(interp.stack);
|
||||
el[`dodrio-a-${eventType}`] = mem32[i++];
|
||||
el[`dodrio-b-${eventType}`] = mem32[i++];
|
||||
}
|
||||
|
||||
// 13
|
||||
RemoveEventListener(edit, interp) {
|
||||
// todo!
|
||||
const eventId = mem32[i++];
|
||||
const eventType = interp.getCachedString(eventId);
|
||||
const el = interp.top(interp.stack);
|
||||
el.removeEventListener(eventType, interp.eventHandler);
|
||||
}
|
||||
|
||||
// 14
|
||||
AddCachedString(edit, interp) {
|
||||
// todo!
|
||||
const pointer = mem32[i++];
|
||||
const length = mem32[i++];
|
||||
const id = mem32[i++];
|
||||
const str = string(mem8, pointer, length);
|
||||
interp.addCachedString(str, id);
|
||||
}
|
||||
|
||||
// 15
|
||||
DropCachedString(edit, interp) {
|
||||
// todo!
|
||||
const id = mem32[i++];
|
||||
interp.dropCachedString(id);
|
||||
}
|
||||
|
||||
// 16
|
||||
CreateElementNS(edit, interp) {
|
||||
// const tagNameId = mem32[i++];
|
||||
// const tagName = interp.getCachedString(tagNameId);
|
||||
// const nsId = mem32[i++];
|
||||
// const ns = interp.getCachedString(nsId);
|
||||
interp.stack.push(document.createElementNS(edit.ns, edit.tag_name));
|
||||
}
|
||||
|
||||
// 17
|
||||
SaveChildrenToTemporaries(edit, interp) {
|
||||
// let temp = mem32[i++];
|
||||
// const start = mem32[i++];
|
||||
// const end = mem32[i++];
|
||||
let temp = edit.temp;
|
||||
const start = edit.start;
|
||||
const end = edit.end;
|
||||
const parent = interp.top(interp.stack);
|
||||
const children = parent.childNodes;
|
||||
for (let i = start; i < end; i++, interp) {
|
||||
interp.temporaries[temp++] = children[i];
|
||||
}
|
||||
}
|
||||
|
||||
// 18
|
||||
PushChild(edit, interp) {
|
||||
const parent = interp.top(interp.stack);
|
||||
// const n = mem32[i++];
|
||||
const n = edit.n;
|
||||
const child = parent.childNodes[n];
|
||||
interp.stack.push(child);
|
||||
}
|
||||
|
||||
// 19
|
||||
PushTemporary(edit, interp) {
|
||||
// const temp = mem32[i++];
|
||||
const temp = edit.temp;
|
||||
interp.stack.push(interp.temporaries[temp]);
|
||||
}
|
||||
|
||||
// 20
|
||||
InsertBefore(edit, interp) {
|
||||
const before = interp.pop();
|
||||
const after = interp.pop();
|
||||
after.parentNode.insertBefore(before, after);
|
||||
interp.stack.push(before);
|
||||
}
|
||||
|
||||
// 21
|
||||
PopPushReverseChild(edit, interp) {
|
||||
// const n = mem32[i++];
|
||||
const n = edit.n;
|
||||
interp.pop();
|
||||
const parent = interp.top(interp.stack);
|
||||
const children = parent.childNodes;
|
||||
const child = children[children.length - n - 1];
|
||||
interp.stack.push(child);
|
||||
}
|
||||
|
||||
// 22
|
||||
RemoveChild(edit, interp) {
|
||||
// const n = mem32[i++];
|
||||
const n = edit.n;
|
||||
const parent = interp.top(interp.stack);
|
||||
const child = parent.childNodes[n];
|
||||
child.remove();
|
||||
}
|
||||
|
||||
// 23
|
||||
SetClass(edit, interp) {
|
||||
// const classId = mem32[i++];
|
||||
const className = edit.class_name;
|
||||
interp.top(interp.stack).className = className;
|
||||
}
|
||||
|
||||
// 24
|
||||
SaveTemplate(edit, interp) {
|
||||
const id = mem32[i++];
|
||||
const template = interp.top(interp.stack);
|
||||
interp.saveTemplate(id, template.cloneNode(true));
|
||||
}
|
||||
|
||||
// 25
|
||||
PushTemplate(edit, interp) {
|
||||
const id = mem32[i++];
|
||||
const template = interp.getTemplate(id);
|
||||
interp.stack.push(template.cloneNode(true));
|
||||
}
|
||||
|
||||
NewListener(edit, interp) {}
|
||||
}
|
||||
|
||||
const op_table = new OPTABLE();
|
||||
const interpreter = new Interpreter(window.document.body);
|
||||
|
||||
function EditListReceived(rawEditList) {
|
||||
let editList = JSON.parse(rawEditList);
|
||||
editList.forEach(function (edit, index) {
|
||||
op_table[edit.type](edit, interpreter);
|
||||
});
|
||||
}
|
||||
|
||||
let socket = new WebSocket("ws://127.0.0.1:8080/session/itsworkinggg");
|
||||
socket.onmessage = function(event) {
|
||||
console.log(event.data);
|
||||
EditListReceived(event.data);
|
||||
}
|
||||
// external.invoke("initiate");
|
||||
|
||||
</script>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,61 @@
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
use dioxus::virtual_dom::VirtualDom;
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_hooks::use_state;
|
||||
use dioxus_html as dioxus_elements;
|
||||
|
||||
fn main() {}
|
||||
// use tide::{Request, Response};
|
||||
|
||||
// #[async_std::main]
|
||||
// async fn main() -> Result<(), std::io::Error> {
|
||||
// let mut app = tide::new();
|
||||
|
||||
// app.at("/:name").get(|req: Request<()>| async move {
|
||||
// let initial_name: String = req
|
||||
// .param("name")
|
||||
// .map(|f| f.parse().unwrap_or("...?".to_string()))
|
||||
// .unwrap_or("...?".to_string());
|
||||
|
||||
// let mut dom = VirtualDom::new_with_props(Example, ExampleProps { initial_name });
|
||||
|
||||
// dom.rebuild();
|
||||
|
||||
// Ok(Response::builder(200)
|
||||
// .body(format!("{}", dioxus_ssr::render_vdom(&dom, |c| c)))
|
||||
// .content_type(tide::http::mime::HTML)
|
||||
// .build())
|
||||
// });
|
||||
|
||||
// println!("Server available at [http://127.0.0.1:8080/bill]");
|
||||
// app.listen("127.0.0.1:8080").await?;
|
||||
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
// #[derive(PartialEq, Props)]
|
||||
// struct ExampleProps {
|
||||
// initial_name: String,
|
||||
// }
|
||||
|
||||
// static Example: FC<ExampleProps> = |(cx, props)| {
|
||||
// let dispaly_name = use_state(cx, move || props.initial_name.clone());
|
||||
|
||||
// cx.render(rsx! {
|
||||
// div { class: "py-12 px-4 text-center w-full max-w-2xl mx-auto",
|
||||
// span { class: "text-sm font-semibold"
|
||||
// "Dioxus Example: Jack and Jill"
|
||||
// }
|
||||
// h2 { class: "text-5xl mt-2 mb-6 leading-tight font-semibold font-heading"
|
||||
// "Hello, {dispaly_name}"
|
||||
// }
|
||||
// ul {
|
||||
// {(0..10).map(|f| rsx!( li {"Element {f}"} ))}
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// };
|
||||
@@ -0,0 +1,147 @@
|
||||
use dioxus::virtual_dom::VirtualDom;
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_hooks::use_state;
|
||||
use dioxus_html as dioxus_elements;
|
||||
use dioxus_html::{GlobalAttributes, SvgAttributes};
|
||||
use dioxus_ssr::TextRenderer;
|
||||
|
||||
fn main() {
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
|
||||
let mut file = File::create("example.html").unwrap();
|
||||
|
||||
let mut dom = VirtualDom::new(App);
|
||||
|
||||
dom.rebuild();
|
||||
|
||||
file.write_fmt(format_args!(
|
||||
"{}",
|
||||
TextRenderer::from_vdom(&dom, Default::default())
|
||||
))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub static App: FC<()> = |(cx, props)| {
|
||||
cx.render(rsx!(
|
||||
div { class: "overflow-hidden"
|
||||
link { href:"https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel:"stylesheet" }
|
||||
Header {}
|
||||
Entry {}
|
||||
Hero {}
|
||||
Hero {}
|
||||
Hero {}
|
||||
Hero {}
|
||||
Hero {}
|
||||
}
|
||||
))
|
||||
};
|
||||
|
||||
pub static Header: FC<()> = |(cx, props)| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
header { class: "text-gray-400 bg-gray-900 body-font"
|
||||
div { class: "container mx-auto flex flex-wrap p-5 flex-col md:flex-row items-center"
|
||||
a { class: "flex title-font font-medium items-center text-white mb-4 md:mb-0"
|
||||
StacksIcon {}
|
||||
span { class: "ml-3 text-xl" "Hello Dioxus!"}
|
||||
}
|
||||
nav { class: "md:ml-auto flex flex-wrap items-center text-base justify-center"
|
||||
a { class: "mr-5 hover:text-white" "First Link"}
|
||||
a { class: "mr-5 hover:text-white" "Second Link"}
|
||||
a { class: "mr-5 hover:text-white" "Third Link"}
|
||||
a { class: "mr-5 hover:text-white" "Fourth Link"}
|
||||
}
|
||||
button {
|
||||
class: "inline-flex items-center bg-gray-800 border-0 py-1 px-3 focus:outline-none hover:bg-gray-700 rounded text-base mt-4 md:mt-0"
|
||||
"Button"
|
||||
RightArrowIcon {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
pub static Hero: FC<()> = |(cx, props)| {
|
||||
//
|
||||
cx.render(rsx! {
|
||||
section{ class: "text-gray-400 bg-gray-900 body-font"
|
||||
div { class: "container mx-auto flex px-5 py-24 md:flex-row flex-col items-center"
|
||||
div { class: "lg:flex-grow md:w-1/2 lg:pr-24 md:pr-16 flex flex-col md:items-start md:text-left mb-16 md:mb-0 items-center text-center"
|
||||
h1 { class: "title-font sm:text-4xl text-3xl mb-4 font-medium text-white"
|
||||
br { class: "hidden lg:inline-block" }
|
||||
"Dioxus Sneak Peek"
|
||||
}
|
||||
p {
|
||||
class: "mb-8 leading-relaxed"
|
||||
|
||||
"Dioxus is a new UI framework that makes it easy and simple to write cross-platform apps using web
|
||||
technologies! It is functional, fast, and portable. Dioxus can run on the web, on the desktop, and
|
||||
on mobile and embedded platforms."
|
||||
|
||||
}
|
||||
div { class: "flex justify-center"
|
||||
button {
|
||||
class: "inline-flex text-white bg-indigo-500 border-0 py-2 px-6 focus:outline-none hover:bg-indigo-600 rounded text-lg"
|
||||
"Learn more"
|
||||
}
|
||||
button {
|
||||
class: "ml-4 inline-flex text-gray-400 bg-gray-800 border-0 py-2 px-6 focus:outline-none hover:bg-gray-700 hover:text-white rounded text-lg"
|
||||
"Build an app"
|
||||
}
|
||||
}
|
||||
}
|
||||
div { class: "lg:max-w-lg lg:w-full md:w-1/2 w-5/6"
|
||||
img { class: "object-cover object-center rounded" alt: "hero" src: "https://i.imgur.com/oK6BLtw.png"
|
||||
referrerpolicy:"no-referrer"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
pub static Entry: FC<()> = |(cx, props)| {
|
||||
//
|
||||
cx.render(rsx! {
|
||||
section{ class: "text-gray-400 bg-gray-900 body-font"
|
||||
div { class: "container mx-auto flex px-5 py-24 md:flex-row flex-col items-center"
|
||||
textarea {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
pub static StacksIcon: FC<()> = |(cx, props)| {
|
||||
cx.render(rsx!(
|
||||
svg {
|
||||
xmlns: "http://www.w3.org/2000/svg"
|
||||
fill: "none"
|
||||
stroke: "currentColor"
|
||||
stroke_linecap: "round"
|
||||
stroke_linejoin: "round"
|
||||
stroke_width: "2"
|
||||
class: "w-10 h-10 text-white p-2 bg-indigo-500 rounded-full"
|
||||
view_box: "0 0 24 24"
|
||||
path { d: "M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"}
|
||||
}
|
||||
))
|
||||
};
|
||||
pub static RightArrowIcon: FC<()> = |(cx, props)| {
|
||||
cx.render(rsx!(
|
||||
svg {
|
||||
fill: "none"
|
||||
stroke: "currentColor"
|
||||
stroke_linecap: "round"
|
||||
stroke_linejoin: "round"
|
||||
stroke_width: "2"
|
||||
class: "w-4 h-4 ml-1"
|
||||
view_box: "0 0 24 24"
|
||||
path { d: "M5 12h14M12 5l7 7-7 7"}
|
||||
}
|
||||
))
|
||||
};
|
||||
@@ -117,7 +117,7 @@ pub static StacksIcon: FC<()> = |(cx, props)| {
|
||||
stroke_linejoin: "round"
|
||||
stroke_width: "2"
|
||||
class: "w-10 h-10 text-white p-2 bg-indigo-500 rounded-full"
|
||||
viewBox: "0 0 24 24"
|
||||
view_box: "0 0 24 24"
|
||||
path { d: "M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"}
|
||||
}
|
||||
))
|
||||
@@ -131,7 +131,7 @@ pub static RightArrowIcon: FC<()> = |(cx, props)| {
|
||||
stroke_linejoin: "round"
|
||||
stroke_width: "2"
|
||||
class: "w-4 h-4 ml-1"
|
||||
viewBox: "0 0 24 24"
|
||||
view_box: "0 0 24 24"
|
||||
path { d: "M5 12h14M12 5l7 7-7 7"}
|
||||
}
|
||||
))
|
||||
|
||||
+5
-5
@@ -23,9 +23,9 @@ pub struct TodoItem {
|
||||
|
||||
const STYLE: &str = include_str!("./assets/todomvc.css");
|
||||
const App: FC<()> = |(cx, props)| {
|
||||
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 mut draft = use_state(cx, || "".to_string());
|
||||
let mut todos = use_state(cx, || HashMap::<u32, Rc<TodoItem>>::new());
|
||||
let mut filter = use_state(cx, || FilterState::All);
|
||||
|
||||
let todolist = todos
|
||||
.iter()
|
||||
@@ -86,8 +86,8 @@ pub struct TodoEntryProps {
|
||||
}
|
||||
|
||||
pub fn TodoEntry((cx, props): Scope<TodoEntryProps>) -> Element {
|
||||
let is_editing = use_state(cx, || false);
|
||||
let contents = use_state(cx, || String::from(""));
|
||||
let mut is_editing = use_state(cx, || false);
|
||||
let mut contents = use_state(cx, || String::from(""));
|
||||
let todo = &props.todo;
|
||||
|
||||
rsx!(cx, li {
|
||||
|
||||
@@ -26,7 +26,7 @@ static App: FC<()> = |(cx, props)| {
|
||||
},
|
||||
|cx, props| {
|
||||
//
|
||||
rsx!(cx, WeatherDisplay {})
|
||||
rsx!(WeatherDisplay {})
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
//! Basic example that renders a simple VNode to the browser.
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_hooks::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
|
||||
fn main() {
|
||||
console_error_panic_hook::set_once();
|
||||
wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
|
||||
dioxus_web::launch(APP, |c| c)
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
struct DogApi {
|
||||
message: String,
|
||||
}
|
||||
|
||||
static APP: FC<()> = |(cx, _props)| {
|
||||
let state = use_state(cx, || 0);
|
||||
|
||||
const ENDPOINT: &str = "https://dog.ceo/api/breeds/image/random/";
|
||||
let doggo = use_suspense(
|
||||
cx,
|
||||
|| async { reqwest::get(ENDPOINT).await.unwrap().json::<DogApi>().await },
|
||||
|cx, res| match res {
|
||||
Ok(res) => rsx!(
|
||||
cx,
|
||||
img {
|
||||
src: "{res.message}"
|
||||
}
|
||||
),
|
||||
Err(_err) => rsx!(cx, div { "No doggos for you :(" }),
|
||||
},
|
||||
);
|
||||
|
||||
rsx!(cx, div { align_items: "center"
|
||||
section { class: "py-12 px-4 text-center"
|
||||
div { class: "w-full max-w-2xl mx-auto"
|
||||
span { class: "text-sm font-semibold"
|
||||
"count: {state}"
|
||||
}
|
||||
div {
|
||||
button { onclick: move |_| state.set(state + 1), "incr" }
|
||||
button { onclick: move |_| state.set(state - 1), "decr" }
|
||||
}
|
||||
div {
|
||||
{doggo}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -0,0 +1,89 @@
|
||||
//! Basic example that renders a simple VNode to the browser.
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_hooks::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
|
||||
fn main() {
|
||||
// Setup logging
|
||||
wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
// Run the app
|
||||
dioxus_web::launch(APP, |c| c)
|
||||
}
|
||||
|
||||
static APP: FC<()> = |(cx, _)| {
|
||||
let mut count = use_state(cx, || 3);
|
||||
let content = use_state(cx, || String::from("h1"));
|
||||
let text_content = use_state(cx, || String::from("Hello, world!"));
|
||||
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
h1 { "content val is {content}" }
|
||||
|
||||
input {
|
||||
r#type: "text",
|
||||
value: "{text_content}"
|
||||
oninput: move |e| text_content.set(e.value.clone())
|
||||
}
|
||||
|
||||
{(0..10).map(|_| {
|
||||
rsx!(
|
||||
button {
|
||||
onclick: move |_| count += 1,
|
||||
"Click to add."
|
||||
"Current count: {count}"
|
||||
}
|
||||
br {}
|
||||
)
|
||||
})}
|
||||
|
||||
select {
|
||||
name: "cars"
|
||||
id: "cars"
|
||||
value: "{content}"
|
||||
oninput: move |ev| {
|
||||
content.set(ev.value.clone());
|
||||
match ev.value.as_str() {
|
||||
"h1" => count.set(0),
|
||||
"h2" => count.set(5),
|
||||
"h3" => count.set(10),
|
||||
_ => {}
|
||||
}
|
||||
},
|
||||
|
||||
option { value: "h1", "h1" }
|
||||
option { value: "h2", "h2" }
|
||||
option { value: "h3", "h3" }
|
||||
}
|
||||
|
||||
{render_list(cx, *count)}
|
||||
|
||||
{render_bullets(cx)}
|
||||
|
||||
CHILD {}
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
fn render_bullets(cx: Context) -> Element {
|
||||
rsx!(cx, div {
|
||||
"bite me"
|
||||
})
|
||||
}
|
||||
|
||||
fn render_list(cx: Context, count: usize) -> Element {
|
||||
let items = (0..count).map(|f| {
|
||||
rsx! {
|
||||
li { "a - {f}" }
|
||||
li { "b - {f}" }
|
||||
li { "c - {f}" }
|
||||
}
|
||||
});
|
||||
|
||||
rsx!(cx, ul { {items} })
|
||||
}
|
||||
|
||||
static CHILD: FC<()> = |(cx, _)| rsx!(cx, div {"hello child"});
|
||||
@@ -0,0 +1,55 @@
|
||||
//! Basic example that renders a simple VNode to the browser.
|
||||
|
||||
use dioxus::events::on::MouseEvent;
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_hooks::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
|
||||
use std::future::Future;
|
||||
|
||||
use std::{pin::Pin, time::Duration};
|
||||
|
||||
use dioxus::prelude::*;
|
||||
|
||||
use dioxus_web::*;
|
||||
|
||||
fn main() {
|
||||
// Setup logging
|
||||
wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
// Run the app
|
||||
dioxus_web::launch(App, |c| c)
|
||||
}
|
||||
|
||||
static App: FC<()> = |(cx, props)| {
|
||||
let mut state = use_state(cx, || 0);
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
style: {
|
||||
align_items: "center"
|
||||
}
|
||||
section { class: "py-12 px-4 text-center"
|
||||
div { class: "w-full max-w-2xl mx-auto"
|
||||
span { class: "text-sm font-semibold"
|
||||
"static subtree"
|
||||
}
|
||||
}
|
||||
}
|
||||
section { class: "py-12 px-4 text-center"
|
||||
div { class: "w-full max-w-2xl mx-auto"
|
||||
span { class: "text-sm font-semibold"
|
||||
"dynamic subtree {state}"
|
||||
}
|
||||
div {
|
||||
button { onclick: move |e| state+=1, "incr" }
|
||||
br {}
|
||||
button { onclick: move |e| state-=1, "decr" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -0,0 +1,69 @@
|
||||
//! Example: Webview Renderer
|
||||
//! -------------------------
|
||||
//!
|
||||
//! This example shows how to use the dioxus_desktop crate to build a basic desktop application.
|
||||
//!
|
||||
//! Under the hood, the dioxus_desktop crate bridges a native Dioxus VirtualDom with a custom prebuit application running
|
||||
//! in the webview runtime. Custom handlers are provided for the webview instance to consume patches and emit user events
|
||||
//! into the native VDom instance.
|
||||
//!
|
||||
//! Currently, NodeRefs won't work properly, but all other event functionality will.
|
||||
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_hooks::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
|
||||
// #[cfg]
|
||||
fn main() {
|
||||
wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
|
||||
dioxus_web::launch(App, |c| c);
|
||||
// env_logger::init();
|
||||
// dioxus::web::launch(App, |c| c);
|
||||
}
|
||||
|
||||
static App: FC<()> = |(cx, props)| {
|
||||
dbg!("rednering parent");
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
But {
|
||||
h1 {"he"}
|
||||
}
|
||||
// But {
|
||||
// h1 {"llo"}
|
||||
// }
|
||||
// But {
|
||||
// h1 {"world"}
|
||||
// }
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
static But: FC<()> = |(cx, props)| {
|
||||
let mut count = use_state(cx, || 0);
|
||||
|
||||
// let d = Dropper { name: "asd" };
|
||||
// let handler = move |_| {
|
||||
// dbg!(d.name);
|
||||
// };
|
||||
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
h1 { "Hifive counter: {count}" }
|
||||
{cx.children()}
|
||||
button { onclick: move |_| count += 1, "Up high!" }
|
||||
button { onclick: move |_| count -= 1, "Down low!" }
|
||||
// button { onclick: {handler}, "Down low!" }
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
// struct Dropper {
|
||||
// name: &'static str,
|
||||
// }
|
||||
// impl Drop for Dropper {
|
||||
// fn drop(&mut self) {
|
||||
// dbg!("dropped");
|
||||
// }
|
||||
// }
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
Tiny CRM: A port of the Yew CRM example to Dioxus.
|
||||
*/
|
||||
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_hooks::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
fn main() {
|
||||
// Setup logging
|
||||
wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
dioxus_web::launch(App, |c| c)
|
||||
}
|
||||
|
||||
enum Scene {
|
||||
ClientsList,
|
||||
NewClientForm,
|
||||
Settings,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Client {
|
||||
pub first_name: String,
|
||||
pub last_name: String,
|
||||
pub description: String,
|
||||
}
|
||||
|
||||
static App: FC<()> = |(cx, _)| {
|
||||
let scene = use_state(cx, || Scene::ClientsList);
|
||||
let clients = use_ref(cx, || vec![] as Vec<Client>);
|
||||
|
||||
let firstname = use_state(cx, || String::new());
|
||||
let lastname = use_state(cx, || String::new());
|
||||
let description = use_state(cx, || String::new());
|
||||
|
||||
let scene = match *scene {
|
||||
Scene::ClientsList => {
|
||||
rsx!(cx, div { class: "crm"
|
||||
h2 { "List of clients" margin_bottom: "10px" }
|
||||
div { class: "clients" margin_left: "10px"
|
||||
{clients.read().iter().map(|client| rsx!(
|
||||
div { class: "client" style: "margin-bottom: 50px"
|
||||
p { "First Name: {client.first_name}" }
|
||||
p { "Last Name: {client.last_name}" }
|
||||
p {"Description: {client.description}"}
|
||||
})
|
||||
)}
|
||||
}
|
||||
button { class: "pure-button pure-button-primary" onclick: move |_| scene.set(Scene::NewClientForm), "Add New" }
|
||||
button { class: "pure-button" onclick: move |_| scene.set(Scene::Settings), "Settings" }
|
||||
})
|
||||
}
|
||||
Scene::NewClientForm => {
|
||||
let add_new = move |_| {
|
||||
clients.write().push(Client {
|
||||
description: (*description).clone(),
|
||||
first_name: (*firstname).clone(),
|
||||
last_name: (*lastname).clone(),
|
||||
});
|
||||
description.set(String::new());
|
||||
firstname.set(String::new());
|
||||
lastname.set(String::new());
|
||||
};
|
||||
rsx!(cx, div { class: "crm"
|
||||
h2 {"Add new client" margin_bottom: "10px" }
|
||||
form { class: "pure-form"
|
||||
input { class: "new-client firstname" placeholder: "First name" value: "{firstname}"
|
||||
oninput: move |evt| firstname.set(evt.value.clone())
|
||||
}
|
||||
input { class: "new-client lastname" placeholder: "Last name" value: "{lastname}"
|
||||
oninput: move |evt| lastname.set(evt.value.clone())
|
||||
}
|
||||
textarea { class: "new-client description" placeholder: "Description" value: "{description}"
|
||||
oninput: move |evt| description.set(evt.value.clone())
|
||||
}
|
||||
}
|
||||
button { class: "pure-button pure-button-primary", onclick: {add_new}, "Add New" }
|
||||
button { class: "pure-button", onclick: move |_| scene.set(Scene::ClientsList), "Go Back" }
|
||||
})
|
||||
}
|
||||
Scene::Settings => {
|
||||
rsx!(cx, div {
|
||||
h2 { "Settings" margin_bottom: "10px" }
|
||||
button {
|
||||
background: "rgb(202, 60, 60)"
|
||||
class: "pure-button pure-button-primary"
|
||||
onclick: move |_| {
|
||||
clients.write().clear();
|
||||
scene.set(Scene::ClientsList);
|
||||
},
|
||||
"Remove all clients"
|
||||
}
|
||||
button {
|
||||
class: "pure-button pure-button-primary"
|
||||
onclick: move |_| scene.set(Scene::ClientsList),
|
||||
"Go Back"
|
||||
}
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
rsx!(cx, body {
|
||||
link {
|
||||
rel: "stylesheet"
|
||||
href: "https://unpkg.com/purecss@2.0.6/build/pure-min.css"
|
||||
integrity: "sha384-Uu6IeWbM+gzNVXJcM9XV3SohHtmWE+3VGi496jvgX1jyvDTXfdK+rfZc8C1Aehk5"
|
||||
crossorigin: "anonymous"
|
||||
}
|
||||
margin_left: "35%"
|
||||
h1 {"Dioxus CRM Example"}
|
||||
{scene}
|
||||
})
|
||||
};
|
||||
@@ -0,0 +1,147 @@
|
||||
//! Basic example that renders a simple VNode to the browser.
|
||||
|
||||
use dioxus::events::on::MouseEvent;
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_hooks::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
use std::future::Future;
|
||||
|
||||
use std::{pin::Pin, time::Duration};
|
||||
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_html::SvgAttributes;
|
||||
|
||||
use dioxus_web::*;
|
||||
|
||||
fn main() {
|
||||
// Setup logging
|
||||
wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
// Run the app
|
||||
dioxus_web::launch(App, |c| c)
|
||||
}
|
||||
|
||||
pub static App: FC<()> = |(cx, props)| {
|
||||
cx.render(rsx!(
|
||||
div { class: "overflow-hidden"
|
||||
link { href:"https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel:"stylesheet" }
|
||||
Header {}
|
||||
// Entry {}
|
||||
Hero {}
|
||||
Hero {}
|
||||
Hero {}
|
||||
Hero {}
|
||||
Hero {}
|
||||
}
|
||||
))
|
||||
};
|
||||
|
||||
pub static Header: FC<()> = |(cx, props)| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
header { class: "text-gray-400 bg-gray-900 body-font"
|
||||
div { class: "container mx-auto flex flex-wrap p-5 flex-col md:flex-row items-center"
|
||||
a { class: "flex title-font font-medium items-center text-white mb-4 md:mb-0"
|
||||
StacksIcon {}
|
||||
span { class: "ml-3 text-xl" "Hello Dioxus!"}
|
||||
}
|
||||
nav { class: "md:ml-auto flex flex-wrap items-center text-base justify-center"
|
||||
a { class: "mr-5 hover:text-white" "First Link"}
|
||||
a { class: "mr-5 hover:text-white" "Second Link"}
|
||||
a { class: "mr-5 hover:text-white" "Third Link"}
|
||||
a { class: "mr-5 hover:text-white" "Fourth Link"}
|
||||
}
|
||||
button {
|
||||
class: "inline-flex items-center bg-gray-800 border-0 py-1 px-3 focus:outline-none hover:bg-gray-700 rounded text-base mt-4 md:mt-0"
|
||||
"Button"
|
||||
RightArrowIcon {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
pub static Hero: FC<()> = |(cx, props)| {
|
||||
//
|
||||
cx.render(rsx! {
|
||||
section{ class: "text-gray-400 bg-gray-900 body-font"
|
||||
div { class: "container mx-auto flex px-5 py-24 md:flex-row flex-col items-center"
|
||||
div { class: "lg:flex-grow md:w-1/2 lg:pr-24 md:pr-16 flex flex-col md:items-start md:text-left mb-16 md:mb-0 items-center text-center"
|
||||
h1 { class: "title-font sm:text-4xl text-3xl mb-4 font-medium text-white"
|
||||
br { class: "hidden lg:inline-block" }
|
||||
"Dioxus Sneak Peek"
|
||||
}
|
||||
p {
|
||||
class: "mb-8 leading-relaxed"
|
||||
|
||||
"Dioxus is a new UI framework that makes it easy and simple to write cross-platform apps using web
|
||||
technologies! It is functional, fast, and portable. Dioxus can run on the web, on the desktop, and
|
||||
on mobile and embedded platforms."
|
||||
|
||||
}
|
||||
div { class: "flex justify-center"
|
||||
button {
|
||||
class: "inline-flex text-white bg-indigo-500 border-0 py-2 px-6 focus:outline-none hover:bg-indigo-600 rounded text-lg"
|
||||
"Learn more"
|
||||
}
|
||||
button {
|
||||
class: "ml-4 inline-flex text-gray-400 bg-gray-800 border-0 py-2 px-6 focus:outline-none hover:bg-gray-700 hover:text-white rounded text-lg"
|
||||
"Build an app"
|
||||
}
|
||||
}
|
||||
}
|
||||
div { class: "lg:max-w-lg lg:w-full md:w-1/2 w-5/6"
|
||||
img { class: "object-cover object-center rounded" alt: "hero" src: "https://i.imgur.com/oK6BLtw.png"
|
||||
referrerpolicy:"no-referrer"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
pub static Entry: FC<()> = |(cx, props)| {
|
||||
//
|
||||
cx.render(rsx! {
|
||||
section{ class: "text-gray-400 bg-gray-900 body-font"
|
||||
div { class: "container mx-auto flex px-5 py-24 md:flex-row flex-col items-center"
|
||||
textarea {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
pub static StacksIcon: FC<()> = |(cx, props)| {
|
||||
cx.render(rsx!(
|
||||
svg {
|
||||
// xmlns: "http://www.w3.org/2000/svg"
|
||||
fill: "none"
|
||||
stroke: "currentColor"
|
||||
stroke_linecap: "round"
|
||||
stroke_linejoin: "round"
|
||||
stroke_width: "2"
|
||||
// class: "w-10 h-10 text-white p-2 bg-indigo-500 rounded-full"
|
||||
viewBox: "0 0 24 24"
|
||||
path { d: "M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"}
|
||||
}
|
||||
))
|
||||
};
|
||||
pub static RightArrowIcon: FC<()> = |(cx, props)| {
|
||||
cx.render(rsx!(
|
||||
svg {
|
||||
fill: "none"
|
||||
stroke: "currentColor"
|
||||
stroke_linecap: "round"
|
||||
stroke_linejoin: "round"
|
||||
stroke_width: "2"
|
||||
// class: "w-4 h-4 ml-1"
|
||||
viewBox: "0 0 24 24"
|
||||
path { d: "M5 12h14M12 5l7 7-7 7"}
|
||||
}
|
||||
))
|
||||
};
|
||||
@@ -22,7 +22,6 @@ static App: FC<()> = |(cx, props)| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
h1 { "Hifive counter: {count}" }
|
||||
{cx.children()}
|
||||
button { onclick: move |_| count += 1, "Up high!" }
|
||||
button { onclick: move |_| count -= 1, "Down low!" }
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user