123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451 |
- //! Dioxus WebSys
- //! --------------
- //! This crate implements a renderer of the Dioxus Virtual DOM for the web browser using Websys.
- use dioxus::prelude::{Context, Properties, VNode};
- use futures_util::{pin_mut, Stream, StreamExt};
- use fxhash::FxHashMap;
- use web_sys::{window, Document, Element, Event, Node};
- // use futures::{channel::mpsc, SinkExt, StreamExt};
- use dioxus::virtual_dom::VirtualDom;
- pub use dioxus_core as dioxus;
- use dioxus_core::{events::EventTrigger, prelude::FC};
- pub use dioxus_core::prelude;
- // pub mod interpreter;
- pub mod new;
- /// The `WebsysRenderer` provides a way of rendering a Dioxus Virtual DOM to the browser's DOM.
- /// Under the hood, we leverage WebSys and interact directly with the DOM
- ///
- pub struct WebsysRenderer {
- internal_dom: VirtualDom,
- }
- impl WebsysRenderer {
- /// 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.
- ///
- /// ```ignore
- /// fn main() {
- /// wasm_bindgen_futures::spawn_local(WebsysRenderer::start(Example));
- /// }
- /// ```
- ///
- /// Run the app to completion, panicing if any error occurs while rendering.
- /// Pairs well with the wasm_bindgen async handler
- pub async fn start(root: FC<()>) {
- Self::new(root).run().await.expect("Virtual DOM failed :(");
- }
- /// Create a new instance of the Dioxus Virtual Dom with no properties for the root component.
- ///
- /// This means that the root component must either consumes its own context, or statics are used to generate the page.
- /// The root component can access things like routing in its context.
- pub fn new(root: FC<()>) -> Self {
- Self::new_with_props(root, ())
- }
- /// Create a new text-renderer instance from a functional component root.
- /// Automatically progresses the creation of the VNode tree to completion.
- ///
- /// A VDom is automatically created. If you want more granular control of the VDom, use `from_vdom`
- pub fn new_with_props<T: Properties + 'static>(root: FC<T>, root_props: T) -> Self {
- Self::from_vdom(VirtualDom::new_with_props(root, root_props))
- }
- /// Create a new text renderer from an existing Virtual DOM.
- pub fn from_vdom(dom: VirtualDom) -> Self {
- Self { internal_dom: dom }
- }
- pub async fn run(&mut self) -> dioxus_core::error::Result<()> {
- use wasm_bindgen::JsCast;
- let root = prepare_websys_dom();
- let root_node = root.clone().dyn_into::<Node>().unwrap();
- let mut websys_dom = crate::new::WebsysDom::new(root.clone());
- websys_dom.stack.push(root_node.clone());
- websys_dom.stack.push(root_node);
- let mut edits = Vec::new();
- self.internal_dom.rebuild(&mut websys_dom, &mut edits)?;
- websys_dom.process_edits(&mut edits);
- log::info!("Going into event loop");
- loop {
- let trigger = {
- let real_queue = websys_dom.wait_for_event();
- if self.internal_dom.tasks.is_empty() {
- log::info!("tasks is empty, waiting for dom event to trigger soemthing");
- real_queue.await
- } else {
- log::info!("tasks is not empty, waiting for either tasks or event system");
- let task_queue = (&mut self.internal_dom.tasks).next();
- pin_mut!(real_queue);
- pin_mut!(task_queue);
- match futures_util::future::select(real_queue, task_queue).await {
- futures_util::future::Either::Left((trigger, _)) => trigger,
- futures_util::future::Either::Right((trigger, _)) => trigger,
- }
- }
- };
- if let Some(real_trigger) = trigger {
- log::info!("event received");
- self.internal_dom.queue_event(real_trigger)?;
- let mut edits = Vec::new();
- self.internal_dom
- .progress_with_event(&mut websys_dom, &mut edits)
- .await?;
- websys_dom.process_edits(&mut edits);
- }
- }
- // should actually never return from this, should be an error, rustc just cant see it
- Ok(())
- }
- }
- fn prepare_websys_dom() -> Element {
- // Initialize the container on the dom
- // Hook up the body as the root component to render tinto
- let window = web_sys::window().expect("should have access to the Window");
- let document = window
- .document()
- .expect("should have access to the Document");
- // let body = document.body().unwrap();
- let el = document.get_element_by_id("dioxusroot").unwrap();
- // Build a dummy div
- // let container: &Element = body.as_ref();
- // container.set_inner_html("");
- // container
- // .append_child(
- // document
- // .create_element("div")
- // .expect("should create element OK")
- // .as_ref(),
- // )
- // .expect("should append child OK");
- el
- // container.clone()
- }
- // Progress the mount of the root component
- // Iterate through the nodes, attaching the closure and sender to the listener
- // {
- // let mut remote_sender = sender.clone();
- // let listener = move || {
- // let event = EventTrigger::new();
- // wasm_bindgen_futures::spawn_local(async move {
- // remote_sender
- // .send(event)
- // .await
- // .expect("Updating receiver failed");
- // })
- // };
- // }
- /// Wasm-bindgen has a performance option to intern commonly used phrases
- /// 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_cache() {
- let cached_words = [
- // All the HTML Tags
- "a",
- "abbr",
- "address",
- "area",
- "article",
- "aside",
- "audio",
- "b",
- "base",
- "bdi",
- "bdo",
- "big",
- "blockquote",
- "body",
- "br",
- "button",
- "canvas",
- "caption",
- "cite",
- "code",
- "col",
- "colgroup",
- "command",
- "data",
- "datalist",
- "dd",
- "del",
- "details",
- "dfn",
- "dialog",
- "div",
- "dl",
- "dt",
- "em",
- "embed",
- "fieldset",
- "figcaption",
- "figure",
- "footer",
- "form",
- "h1",
- "h2",
- "h3",
- "h4",
- "h5",
- "h6",
- "head",
- "header",
- "hr",
- "html",
- "i",
- "iframe",
- "img",
- "input",
- "ins",
- "kbd",
- "keygen",
- "label",
- "legend",
- "li",
- "link",
- "main",
- "map",
- "mark",
- "menu",
- "menuitem",
- "meta",
- "meter",
- "nav",
- "noscript",
- "object",
- "ol",
- "optgroup",
- "option",
- "output",
- "p",
- "param",
- "picture",
- "pre",
- "progress",
- "q",
- "rp",
- "rt",
- "ruby",
- "s",
- "samp",
- "script",
- "section",
- "select",
- "small",
- "source",
- "span",
- "strong",
- "style",
- "sub",
- "summary",
- "sup",
- "table",
- "tbody",
- "td",
- "textarea",
- "tfoot",
- "th",
- "thead",
- "time",
- "title",
- "tr",
- "track",
- "u",
- "ul",
- "var",
- "video",
- "wbr",
- // All the event handlers
- "Attribute",
- "accept",
- "accept-charset",
- "accesskey",
- "action",
- "alt",
- "async",
- "autocomplete",
- "autofocus",
- "autoplay",
- "charset",
- "checked",
- "cite",
- "class",
- "cols",
- "colspan",
- "content",
- "contenteditable",
- "controls",
- "coords",
- "data",
- "data-*",
- "datetime",
- "default",
- "defer",
- "dir",
- "dirname",
- "disabled",
- "download",
- "draggable",
- "enctype",
- "for",
- "form",
- "formaction",
- "headers",
- "height",
- "hidden",
- "high",
- "href",
- "hreflang",
- "http-equiv",
- "id",
- "ismap",
- "kind",
- "label",
- "lang",
- "list",
- "loop",
- "low",
- "max",
- "maxlength",
- "media",
- "method",
- "min",
- "multiple",
- "muted",
- "name",
- "novalidate",
- "onabort",
- "onafterprint",
- "onbeforeprint",
- "onbeforeunload",
- "onblur",
- "oncanplay",
- "oncanplaythrough",
- "onchange",
- "onclick",
- "oncontextmenu",
- "oncopy",
- "oncuechange",
- "oncut",
- "ondblclick",
- "ondrag",
- "ondragend",
- "ondragenter",
- "ondragleave",
- "ondragover",
- "ondragstart",
- "ondrop",
- "ondurationchange",
- "onemptied",
- "onended",
- "onerror",
- "onfocus",
- "onhashchange",
- "oninput",
- "oninvalid",
- "onkeydown",
- "onkeypress",
- "onkeyup",
- "onload",
- "onloadeddata",
- "onloadedmetadata",
- "onloadstart",
- "onmousedown",
- "onmousemove",
- "onmouseout",
- "onmouseover",
- "onmouseup",
- "onmousewheel",
- "onoffline",
- "ononline",
- "<body>",
- "onpageshow",
- "onpaste",
- "onpause",
- "onplay",
- "onplaying",
- "<body>",
- "onprogress",
- "onratechange",
- "onreset",
- "onresize",
- "onscroll",
- "onsearch",
- "onseeked",
- "onseeking",
- "onselect",
- "onstalled",
- "<body>",
- "onsubmit",
- "onsuspend",
- "ontimeupdate",
- "ontoggle",
- "onunload",
- "onvolumechange",
- "onwaiting",
- "onwheel",
- "open",
- "optimum",
- "pattern",
- "placeholder",
- "poster",
- "preload",
- "readonly",
- "rel",
- "required",
- "reversed",
- "rows",
- "rowspan",
- "sandbox",
- "scope",
- "selected",
- "shape",
- "size",
- "sizes",
- "span",
- "spellcheck",
- "src",
- "srcdoc",
- "srclang",
- "srcset",
- "start",
- "step",
- "style",
- "tabindex",
- "target",
- "title",
- "translate",
- "type",
- "usemap",
- "value",
- "width",
- "wrap",
- ];
- for s in cached_words {
- wasm_bindgen::intern(s);
- }
- }
|