diff --git a/examples/examples/calculator.rs b/examples/_examples/calculator_.rs similarity index 100% rename from examples/examples/calculator.rs rename to examples/_examples/calculator_.rs diff --git a/examples/examples/demo.rs b/examples/_examples/demo.rs similarity index 100% rename from examples/examples/demo.rs rename to examples/_examples/demo.rs diff --git a/examples/examples/hifive.rs b/examples/_examples/hifive.rs similarity index 100% rename from examples/examples/hifive.rs rename to examples/_examples/hifive.rs diff --git a/examples/weather_app.rs b/examples/weather_app.rs new file mode 100644 index 000000000..833cc0529 --- /dev/null +++ b/examples/weather_app.rs @@ -0,0 +1,58 @@ +//! Example: Weather App +//! -------------------- +//! +//! +//! + +use dioxus::prelude::*; + +fn main() { + // dioxus::desktop::launch(App, |c| c); +} + +static App: FC<()> = |cx, props| { + // + let body = use_suspense( + cx, + || async { + // + }, + |cx, props| { + // + rsx!(cx, WeatherDisplay {}) + }, + ); + + rsx!(cx, div { + {body} + }) +}; + +#[derive(PartialEq, Props)] +struct WeatherProps {} + +static WeatherDisplay: FC = |cx, props| { + // + cx.render(rsx!( + div { class: "flex items-center justify-center flex-col" + div { class: "flex items-center justify-center" + div { class: "flex flex-col bg-white rounded p-4 w-full max-w-xs" + div{ class: "font-bold text-xl" + "Jon's awesome site!!" + } + div{ class: "text-sm text-gray-500" + "He worked so hard on it :)" + } + div { class: "flex flex-row items-center justify-center mt-6" + div { class: "font-medium text-6xl" + "1337" + } + } + div { class: "flex flex-row justify-between mt-6" + "Legit made my own React" + } + } + } + } + )) +}; diff --git a/packages/desktop/src/lib.rs b/packages/desktop/src/lib.rs index 436103a70..50528288a 100644 --- a/packages/desktop/src/lib.rs +++ b/packages/desktop/src/lib.rs @@ -27,9 +27,9 @@ static HTML_CONTENT: &'static str = include_str!("./index.html"); pub fn launch( root: FC<()>, - builder: impl for<'a, 'b> FnOnce(&'b mut DesktopConfig<'a>) -> &'b mut DesktopConfig<'a>, + config_builder: impl for<'a, 'b> FnOnce(&'b mut DesktopConfig<'a>) -> &'b mut DesktopConfig<'a>, ) -> anyhow::Result<()> { - launch_with_props(root, (), builder) + launch_with_props(root, (), config_builder) } pub fn launch_with_props( root: FC

, diff --git a/packages/hooks/src/usestate.rs b/packages/hooks/src/usestate.rs index 10015a23c..763c44666 100644 --- a/packages/hooks/src/usestate.rs +++ b/packages/hooks/src/usestate.rs @@ -49,7 +49,7 @@ use std::{ /// } /// } /// ``` -pub fn use_state<'a, 'c, T: 'static>( +pub fn use_state<'a, T: 'static>( cx: Context<'a>, initial_state_fn: impl FnOnce() -> T, ) -> UseState<'a, T> { diff --git a/packages/html/src/lib.rs b/packages/html/src/lib.rs index 714969007..ecaec505b 100644 --- a/packages/html/src/lib.rs +++ b/packages/html/src/lib.rs @@ -1711,7 +1711,7 @@ impl select { } impl option { - fn selected<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> { + pub fn selected<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> { cx.attr("selected", val, None, true) } } diff --git a/packages/web/examples/crm2.rs b/packages/web/examples/crm2.rs index 47cd1f435..1c5dbcbaf 100644 --- a/packages/web/examples/crm2.rs +++ b/packages/web/examples/crm2.rs @@ -68,13 +68,13 @@ static App: FC<()> = |cx, _| { h2 {"Add new client" margin_bottom: "10px" } form { class: "pure-form" input { class: "new-client firstname" placeholder: "First name" value: "{firstname}" - oninput: move |e| firstname.set(e.value()) + oninput: move |evt| firstname.set(evt.value()) } input { class: "new-client lastname" placeholder: "Last name" value: "{lastname}" - oninput: move |e| lastname.set(e.value()) + oninput: move |evt| lastname.set(evt.value()) } textarea { class: "new-client description" placeholder: "Description" value: "{description}" - oninput: move |e| description.set(e.value()) + oninput: move |evt| description.set(evt.value()) } } button { class: "pure-button pure-button-primary", onclick: {add_new}, "Add New" } @@ -83,7 +83,7 @@ static App: FC<()> = |cx, _| { } Scene::Settings => { rsx!(cx, div { - h2 {"Settings" margin_bottom: "10px" } + h2 { "Settings" margin_bottom: "10px" } button { background: "rgb(202, 60, 60)" class: "pure-button pure-button-primary" diff --git a/packages/web/src/cache.rs b/packages/web/src/cache.rs index b38ed6afb..1cdba7958 100644 --- a/packages/web/src/cache.rs +++ b/packages/web/src/cache.rs @@ -2,8 +2,10 @@ /// This saves the decoding cost, making the interaction of Rust<->JS more performant. /// We intern all the HTML tags and attributes, making most operations much faster. /// -/// Interning takes about 1ms at the start of the app, but saves a *ton* of time later on. -pub fn intern_cached_strings() { +/// Interning takes < 1ms at the start of the app, but saves a *ton* of time later on. +/// +/// Eventually we might want to procedurally generate these strings for common words, phrases, and values. +pub(crate) fn intern_cached_strings() { let cached_words = [ // All the HTML Tags "a", diff --git a/packages/web/src/cfg.rs b/packages/web/src/cfg.rs index 376e466c9..287e81850 100644 --- a/packages/web/src/cfg.rs +++ b/packages/web/src/cfg.rs @@ -1,7 +1,18 @@ +/// Configuration for the WebSys renderer for the Dioxus VirtualDOM. +/// +/// This struct helps configure the specifics of hydration and render destination for WebSys. +/// +/// # Example +/// ```rust, ignore +/// fn main() { +/// dioxus::web::launch(App, |cfg| cfg.hydrate(true).root_name("myroot")) +/// } +/// ``` pub struct WebConfig { pub(crate) hydrate: bool, pub(crate) rootname: String, } + impl Default for WebConfig { fn default() -> Self { Self { @@ -10,6 +21,7 @@ impl Default for WebConfig { } } } + impl WebConfig { /// Enable SSR hydration /// @@ -17,12 +29,14 @@ impl WebConfig { /// work and suspended nodes. /// /// Dioxus will load up all the elements with the `dio_el` data attribute into memory when the page is loaded. - /// pub fn hydrate(mut self, f: bool) -> Self { self.hydrate = f; self } + /// Set the name of the element that Dioxus will use as the root. + /// + /// This is akint to calling React.render() on the element with the specified name. pub fn rootname(mut self, name: impl Into) -> Self { self.rootname = name.into(); self diff --git a/packages/web/src/dom.rs b/packages/web/src/dom.rs index 0fa6ec39f..4dfaecd5d 100644 --- a/packages/web/src/dom.rs +++ b/packages/web/src/dom.rs @@ -1,16 +1,15 @@ -use std::{collections::HashMap, fmt::Debug, rc::Rc, sync::Arc}; - use dioxus_core::{ - events::{on::GenericEventInner, SyntheticEvent, UserEvent}, + events::{SyntheticEvent, UserEvent}, mutations::NodeRefMutation, scheduler::SchedulerMsg, DomEdit, ElementId, ScopeId, }; use fxhash::FxHashMap; -use wasm_bindgen::{closure::Closure, JsCast, JsValue}; +use std::{fmt::Debug, rc::Rc}; +use wasm_bindgen::{closure::Closure, JsCast}; use web_sys::{ - window, Attr, CssStyleDeclaration, Document, Element, Event, HtmlElement, HtmlInputElement, - HtmlOptionElement, HtmlTextAreaElement, Node, NodeList, UiEvent, + Attr, CssStyleDeclaration, Document, Element, Event, HtmlElement, HtmlInputElement, + HtmlOptionElement, HtmlTextAreaElement, Node, NodeList, }; use crate::{nodeslab::NodeSlab, WebConfig}; diff --git a/packages/web/src/events.rs b/packages/web/src/events.rs index 7257fad2a..f64ffc8b7 100644 --- a/packages/web/src/events.rs +++ b/packages/web/src/events.rs @@ -1,12 +1,10 @@ //! Ported events into Dioxus Synthetic Event system -//! -//! event porting is pretty boring, sorry. use dioxus_core::events::on::*; use wasm_bindgen::JsCast; use web_sys::{Event, UiEvent}; -/// All events implement the generic event type - they're all UI events +/// All events implement the generic event type - they're all `UI events` trait WebsysGenericEvent { fn as_ui_event(&self) -> &UiEvent; } diff --git a/packages/web/src/lib.rs b/packages/web/src/lib.rs index 8927de4dd..27b3afb78 100644 --- a/packages/web/src/lib.rs +++ b/packages/web/src/lib.rs @@ -70,19 +70,28 @@ mod events; mod nodeslab; mod ric_raf; -/// Launches the VirtualDOM from the specified component function. +/// Launch the VirtualDOM given a root component and a configuration. /// -/// This method will block the thread with `spawn_local` +/// This function expects the root component to not have root props. To launch the root component with root props, use +/// `launch_with_props` instead. +/// +/// This method will block the thread with `spawn_local` from wasm_bindgen_futures. +/// +/// If you need to run the VirtualDOM in its own thread, use `run_with_props` instead and await the future. /// /// # Example /// +/// ```rust +/// fn main() { +/// dioxus_web::launch(App, |c| c); +/// } /// -/// -pub fn launch(root: FC<()>, config: F) -where - F: FnOnce(WebConfig) -> WebConfig, -{ - launch_with_props(root, (), config) +/// static App: FC<()> = |cx, props| { +/// rsx!(cx, div {"hello world"}) +/// } +/// ``` +pub fn launch(root_component: FC<()>, configuration: impl FnOnce(WebConfig) -> WebConfig) { + launch_with_props(root_component, (), configuration) } /// Launches the VirtualDOM from the specified component function and props. @@ -91,28 +100,41 @@ where /// /// # Example /// +/// ```rust +/// fn main() { +/// dioxus_web::launch_with_props(App, RootProps { name: String::from("joe") }, |c| c); +/// } /// -pub fn launch_with_props(root: FC, root_props: T, config: F) +/// #[derive(ParitalEq, Props)] +/// struct RootProps { +/// name: String +/// } +/// +/// static App: FC = |cx, props| { +/// rsx!(cx, div {"hello {props.name}"}) +/// } +/// ``` +pub fn launch_with_props(root_component: FC, root_properties: T, configuration_builder: F) where T: Properties + 'static, F: FnOnce(WebConfig) -> WebConfig, { - let config = config(WebConfig::default()); - wasm_bindgen_futures::spawn_local(run_with_props(root, root_props, config)); + let config = configuration_builder(WebConfig::default()); + wasm_bindgen_futures::spawn_local(run_with_props(root_component, root_properties, config)); } -/// This method is the primary entrypoint for Websys Dioxus apps. Will panic if an error occurs while rendering. -/// See DioxusErrors for more information on how these errors could occour. + +/// Runs the app as a future that can be scheduled around the main thread. +/// +/// Polls futures internal to the VirtualDOM, hence the async nature of this function. /// /// # Example /// /// ```ignore /// fn main() { -/// wasm_bindgen_futures::spawn_local(WebsysRenderer::start(Example)); +/// let app_fut = dioxus_web::run_with_props(App, RootProps { name: String::from("joe") }, |c| c); +/// wasm_bindgen_futures::spawn_local(app_fut); /// } /// ``` -/// -/// Run the app to completion, panicing if any error occurs while rendering. -/// Pairs well with the wasm_bindgen async handler pub async fn run_with_props(root: FC, root_props: T, cfg: WebConfig) { let mut dom = VirtualDom::new_with_props(root, root_props); @@ -135,8 +157,6 @@ pub async fn run_with_props(root: FC, root_props: T, if !should_hydrate { log::info!("Applying rebuild edits..., {:?}", mutations); websys_dom.process_edits(&mut mutations.edits); - } else { - // websys dom processed the config and hydrated the dom already } let work_loop = ric_raf::RafLoop::new(); diff --git a/packages/web/src/nodeslab.rs b/packages/web/src/nodeslab.rs index e374a3dc9..b986bd34e 100644 --- a/packages/web/src/nodeslab.rs +++ b/packages/web/src/nodeslab.rs @@ -1,18 +1,15 @@ -use std::ops::{Index, IndexMut}; +//! This module provides a mirror of the VirtualDOM Element Slab using a Vector. +use std::ops::{Index, IndexMut}; use web_sys::Node; -pub struct NodeSlab { +pub(crate) struct NodeSlab { nodes: Vec>, } impl NodeSlab { pub fn new(capacity: usize) -> NodeSlab { - let mut nodes = Vec::with_capacity(capacity); - for x in 0..5 { - nodes.push(None); - } - + let nodes = Vec::with_capacity(capacity); NodeSlab { nodes } } } diff --git a/packages/web/src/ric_raf.rs b/packages/web/src/ric_raf.rs index 671e15f8e..29c97bcba 100644 --- a/packages/web/src/ric_raf.rs +++ b/packages/web/src/ric_raf.rs @@ -1,12 +1,18 @@ -//! RequestAnimationFrame and RequestIdleCallback port and polyfill. +//! This module provides some utilities around scheduling tasks on the main thread of the browser. +//! +//! The ultimate goal here is to not block the main thread during animation frames, so our animations don't result in "jank". +//! +//! Hence, this module provides Dioxus "Jank Free Rendering" on the web. +//! +//! Because RIC doesn't work on Safari, we polyfill using the "ricpolyfill.js" file and use some basic detection to see +//! if RIC is available. use gloo_timers::future::TimeoutFuture; use js_sys::Function; -use wasm_bindgen::JsCast; -use wasm_bindgen::{prelude::Closure, JsValue}; +use wasm_bindgen::{prelude::Closure, JsCast, JsValue}; use web_sys::{window, Window}; -pub struct RafLoop { +pub(crate) struct RafLoop { window: Window, ric_receiver: async_channel::Receiver, raf_receiver: async_channel::Receiver<()>,