polish: clean up the web module

This commit is contained in:
Jonathan Kelley 2021-09-24 20:11:30 -04:00
parent 1464d2e0a7
commit 823adc0834
15 changed files with 144 additions and 50 deletions

58
examples/weather_app.rs Normal file
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"
}
}
}
}
))
};

View file

@ -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<P: Properties + 'static>(
root: FC<P>,

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>,
initial_state_fn: impl FnOnce() -> T,
) -> UseState<'a, T> {

View file

@ -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)
}
}

View file

@ -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"

View file

@ -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",

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(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<String>) -> Self {
self.rootname = name.into();
self

View file

@ -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};

View file

@ -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;
}

View file

@ -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<F>(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<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
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<T: Properties + 'static>(root: FC<T>, 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<T: Properties + 'static>(root: FC<T>, 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();

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;
pub struct NodeSlab {
pub(crate) struct NodeSlab {
nodes: Vec<Option<Node>>,
}
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 }
}
}

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 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<u32>,
raf_receiver: async_channel::Receiver<()>,