polish: clean up the web module

This commit is contained in:
Jonathan Kelley committed 2021-09-24 20:11:30 -04:00
1 parent 1464d2e0a7
commit 823adc0834
15 files changed
+144 -50

No files matched your search

File renamed without changes.
File renamed without changes.
File renamed without changes.
+58
View File
@@ -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<WeatherProps> = |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"
}
}
}
}
))
};
+2 -2
View File
@@ -27,9 +27,9 @@ static HTML_CONTENT: &'static str = include_str!("./index.html");
pub fn launch( pub fn launch(
root: FC<()>, 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<()> { ) -> anyhow::Result<()> {
launch_with_props(root, (), builder) launch_with_props(root, (), config_builder)
} }
pub fn launch_with_props<P: Properties + 'static>( pub fn launch_with_props<P: Properties + 'static>(
root: FC<P>, root: FC<P>,
+1 -1
View File
@@ -49,7 +49,7 @@ use std::{
/// } /// }
/// } /// }
/// ``` /// ```
pub fn use_state<'a, 'c, T: 'static>( pub fn use_state<'a, T: 'static>(
cx: Context<'a>, cx: Context<'a>,
initial_state_fn: impl FnOnce() -> T, initial_state_fn: impl FnOnce() -> T,
) -> UseState<'a, T> { ) -> UseState<'a, T> {
+1 -1
View File
@@ -1711,7 +1711,7 @@ impl select {
} }
impl option { 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) cx.attr("selected", val, None, true)
} }
} }
+4 -4
View File
@@ -68,13 +68,13 @@ static App: FC<()> = |cx, _| {
h2 {"Add new client" margin_bottom: "10px" } h2 {"Add new client" margin_bottom: "10px" }
form { class: "pure-form" form { class: "pure-form"
input { class: "new-client firstname" placeholder: "First name" value: "{firstname}" 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}" 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}" 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" } button { class: "pure-button pure-button-primary", onclick: {add_new}, "Add New" }
@@ -83,7 +83,7 @@ static App: FC<()> = |cx, _| {
} }
Scene::Settings => { Scene::Settings => {
rsx!(cx, div { rsx!(cx, div {
h2 {"Settings" margin_bottom: "10px" } h2 { "Settings" margin_bottom: "10px" }
button { button {
background: "rgb(202, 60, 60)" background: "rgb(202, 60, 60)"
class: "pure-button pure-button-primary" class: "pure-button pure-button-primary"
+4 -2
View File
@@ -2,8 +2,10 @@
/// This saves the decoding cost, making the interaction of Rust<->JS more performant. /// 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. /// 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. /// Interning takes < 1ms at the start of the app, but saves a *ton* of time later on.
pub fn intern_cached_strings() { ///
/// Eventually we might want to procedurally generate these strings for common words, phrases, and values.
pub(crate) fn intern_cached_strings() {
let cached_words = [ let cached_words = [
// All the HTML Tags // All the HTML Tags
"a", "a",
+15 -1
View File
@@ -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 struct WebConfig {
pub(crate) hydrate: bool, pub(crate) hydrate: bool,
pub(crate) rootname: String, pub(crate) rootname: String,
} }
impl Default for WebConfig { impl Default for WebConfig {
fn default() -> Self { fn default() -> Self {
Self { Self {
@@ -10,6 +21,7 @@ impl Default for WebConfig {
} }
} }
} }
impl WebConfig { impl WebConfig {
/// Enable SSR hydration /// Enable SSR hydration
/// ///
@@ -17,12 +29,14 @@ impl WebConfig {
/// work and suspended nodes. /// work and suspended nodes.
/// ///
/// Dioxus will load up all the elements with the `dio_el` data attribute into memory when the page is loaded. /// 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 { pub fn hydrate(mut self, f: bool) -> Self {
self.hydrate = f; self.hydrate = f;
self 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<String>) -> Self { pub fn rootname(mut self, name: impl Into<String>) -> Self {
self.rootname = name.into(); self.rootname = name.into();
self self
+5 -6
View File
@@ -1,16 +1,15 @@
use std::{collections::HashMap, fmt::Debug, rc::Rc, sync::Arc};
use dioxus_core::{ use dioxus_core::{
events::{on::GenericEventInner, SyntheticEvent, UserEvent}, events::{SyntheticEvent, UserEvent},
mutations::NodeRefMutation, mutations::NodeRefMutation,
scheduler::SchedulerMsg, scheduler::SchedulerMsg,
DomEdit, ElementId, ScopeId, DomEdit, ElementId, ScopeId,
}; };
use fxhash::FxHashMap; 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::{ use web_sys::{
window, Attr, CssStyleDeclaration, Document, Element, Event, HtmlElement, HtmlInputElement, Attr, CssStyleDeclaration, Document, Element, Event, HtmlElement, HtmlInputElement,
HtmlOptionElement, HtmlTextAreaElement, Node, NodeList, UiEvent, HtmlOptionElement, HtmlTextAreaElement, Node, NodeList,
}; };
use crate::{nodeslab::NodeSlab, WebConfig}; use crate::{nodeslab::NodeSlab, WebConfig};
+1 -3
View File
@@ -1,12 +1,10 @@
//! Ported events into Dioxus Synthetic Event system //! Ported events into Dioxus Synthetic Event system
//!
//! event porting is pretty boring, sorry.
use dioxus_core::events::on::*; use dioxus_core::events::on::*;
use wasm_bindgen::JsCast; use wasm_bindgen::JsCast;
use web_sys::{Event, UiEvent}; 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 { trait WebsysGenericEvent {
fn as_ui_event(&self) -> &UiEvent; fn as_ui_event(&self) -> &UiEvent;
} }
+39 -19
View File
@@ -70,19 +70,28 @@ mod events;
mod nodeslab; mod nodeslab;
mod ric_raf; 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 /// # Example
/// ///
/// ```rust
/// fn main() {
/// dioxus_web::launch(App, |c| c);
/// }
/// ///
/// /// static App: FC<()> = |cx, props| {
pub fn launch<F>(root: FC<()>, config: F) /// rsx!(cx, div {"hello world"})
where /// }
F: FnOnce(WebConfig) -> WebConfig, /// ```
{ pub fn launch(root_component: FC<()>, configuration: impl FnOnce(WebConfig) -> WebConfig) {
launch_with_props(root, (), config) launch_with_props(root_component, (), configuration)
} }
/// Launches the VirtualDOM from the specified component function and props. /// Launches the VirtualDOM from the specified component function and props.
@@ -91,28 +100,41 @@ where
/// ///
/// # Example /// # Example
/// ///
/// ```rust
/// fn main() {
/// dioxus_web::launch_with_props(App, RootProps { name: String::from("joe") }, |c| c);
/// }
/// ///
pub fn launch_with_props<T, F>(root: FC<T>, root_props: T, config: F) /// #[derive(ParitalEq, Props)]
/// struct RootProps {
/// name: String
/// }
///
/// static App: FC<RootProps> = |cx, props| {
/// rsx!(cx, div {"hello {props.name}"})
/// }
/// ```
pub fn launch_with_props<T, F>(root_component: FC<T>, root_properties: T, configuration_builder: F)
where where
T: Properties + 'static, T: Properties + 'static,
F: FnOnce(WebConfig) -> WebConfig, F: FnOnce(WebConfig) -> WebConfig,
{ {
let config = config(WebConfig::default()); let config = configuration_builder(WebConfig::default());
wasm_bindgen_futures::spawn_local(run_with_props(root, root_props, config)); 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 /// # Example
/// ///
/// ```ignore /// ```ignore
/// fn main() { /// 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<T: Properties + 'static>(root: FC<T>, root_props: T, cfg: WebConfig) { pub async fn run_with_props<T: Properties + 'static>(root: FC<T>, root_props: T, cfg: WebConfig) {
let mut dom = VirtualDom::new_with_props(root, root_props); let mut dom = VirtualDom::new_with_props(root, root_props);
@@ -135,8 +157,6 @@ pub async fn run_with_props<T: Properties + 'static>(root: FC<T>, root_props: T,
if !should_hydrate { if !should_hydrate {
log::info!("Applying rebuild edits..., {:?}", mutations); log::info!("Applying rebuild edits..., {:?}", mutations);
websys_dom.process_edits(&mut mutations.edits); 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(); let work_loop = ric_raf::RafLoop::new();
+4 -7
View File
@@ -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; use web_sys::Node;
pub struct NodeSlab { pub(crate) struct NodeSlab {
nodes: Vec<Option<Node>>, nodes: Vec<Option<Node>>,
} }
impl NodeSlab { impl NodeSlab {
pub fn new(capacity: usize) -> NodeSlab { pub fn new(capacity: usize) -> NodeSlab {
let mut nodes = Vec::with_capacity(capacity); let nodes = Vec::with_capacity(capacity);
for x in 0..5 {
nodes.push(None);
}
NodeSlab { nodes } NodeSlab { nodes }
} }
} }
+10 -4
View File
@@ -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 gloo_timers::future::TimeoutFuture;
use js_sys::Function; use js_sys::Function;
use wasm_bindgen::JsCast; use wasm_bindgen::{prelude::Closure, JsCast, JsValue};
use wasm_bindgen::{prelude::Closure, JsValue};
use web_sys::{window, Window}; use web_sys::{window, Window};
pub struct RafLoop { pub(crate) struct RafLoop {
window: Window, window: Window,
ric_receiver: async_channel::Receiver<u32>, ric_receiver: async_channel::Receiver<u32>,
raf_receiver: async_channel::Receiver<()>, raf_receiver: async_channel::Receiver<()>,