diff --git a/Cargo.toml b/Cargo.toml index 848d18a24c8..d56df56bce1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,9 +21,12 @@ travis-ci = { repository = "yewstack/yew" } [dependencies] anymap = "0.12" bincode = { version = "~1.2.1", optional = true } +console_error_panic_hook = { version = "0.1", optional = true } failure = "0.1" +gloo = { version = "0.2", optional = true } http = "0.2" indexmap = "1.0.2" +js-sys = { version = "0.3", optional = true } log = "0.4" proc-macro-hack = "0.5" proc-macro-nested = "0.1" @@ -33,10 +36,61 @@ serde_cbor = { version = "0.10.2", optional = true } serde_json = "1.0" serde_yaml = { version = "0.8.3", optional = true } slab = "0.4" -stdweb = "0.4.20" +stdweb = { version = "0.4.20", optional = true } toml = { version = "0.5", optional = true } yew-macro = { version = "0.10.1", path = "crates/macro" } +[dependencies.web-sys] +version = "0.3" +optional = true +features = [ + "AbortController", + "AbortSignal", + "BinaryType", + "Blob", + "console", + "DedicatedWorkerGlobalScope", + "Document", + "DomTokenList", + "DragEvent", + "Element", + "Event", + "EventTarget", + "File", + "FileList", + "FileReader", + "FocusEvent", + "Headers", + "HtmlElement", + "HtmlInputElement", + "HtmlSelectElement", + "HtmlTextAreaElement", + "KeyboardEvent", + "Location", + "MessageEvent", + "MouseEvent", + "Node", + "ObserverCallback", + "PointerEvent", + "ReferrerPolicy", + "Request", + "RequestCache", + "RequestCredentials", + "RequestInit", + "RequestMode", + "RequestRedirect", + "Response", + "Storage", + "Text", + "TouchEvent", + "UiEvent", + "WebSocket", + "WheelEvent", + "Window", + "Worker", + "WorkerOptions", +] + [target.'cfg(all(target_arch = "wasm32", not(target_os="wasi"), not(cargo_web)))'.dependencies] wasm-bindgen = "0.2.56" wasm-bindgen-futures = "0.4.4" @@ -54,6 +108,8 @@ rustversion = "1.0" [features] default = ["services", "agent"] +std_web = ["stdweb"] +web_sys = ["console_error_panic_hook", "gloo", "js-sys", "web-sys"] doc_test = [] web_test = [] wasm_test = [] diff --git a/build.rs b/build.rs index a0445febb0e..437ea43a5d1 100644 --- a/build.rs +++ b/build.rs @@ -1,6 +1,12 @@ use std::env; pub fn main() { + if cfg!(all(feature = "web_sys", feature = "std_web")) { + panic!("don't use `web_sys` and `std_web` simultaneously") + } else if cfg!(not(any(feature = "web_sys", feature = "std_web"))) { + panic!("please select either `web_sys` or `std_web`") + } + let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default(); let cargo_web = env::var("COMPILING_UNDER_CARGO_WEB").unwrap_or_default(); if target_arch == "wasm32" && cargo_web != "1" { diff --git a/src/app.rs b/src/app.rs index 3e6a9c2835e..295ae11b0ef 100644 --- a/src/app.rs +++ b/src/app.rs @@ -2,7 +2,10 @@ //! a component in an isolated scope. use crate::html::{Component, NodeRef, Scope}; +#[cfg(feature = "std_web")] use stdweb::web::{document, Element, INode, IParentNode}; +#[cfg(feature = "web_sys")] +use web_sys::Element; /// An application instance. #[derive(Debug)] @@ -42,8 +45,13 @@ where /// Alias to `mount("body", ...)`. pub fn mount_to_body(self) -> Scope { + #[cfg(feature = "std_web")] + let document = document(); + #[cfg(feature = "web_sys")] + let document = web_sys::window().unwrap().document().unwrap(); + // Bootstrap the component for `Window` environment only (not for `Worker`) - let element = document() + let element = document .query_selector("body") .expect("can't get body node for rendering") .expect("can't unwrap body node"); @@ -55,11 +63,16 @@ where /// need to manipulate the body element. For example, adding/removing app-wide /// CSS classes of the body element. pub fn mount_as_body(self) -> Scope { - let html_element = document() + #[cfg(feature = "std_web")] + let document = document(); + #[cfg(feature = "web_sys")] + let document = web_sys::window().unwrap().document().unwrap(); + + let html_element = document .query_selector("html") .expect("can't get html node for rendering") .expect("can't unwrap html node"); - let body_element = document() + let body_element = document .query_selector("body") .expect("can't get body node for rendering") .expect("can't unwrap body node"); @@ -97,8 +110,13 @@ where /// Alias to `mount_with_props("body", ...)`. pub fn mount_to_body_with_props(self, props: COMP::Properties) -> Scope { + #[cfg(feature = "std_web")] + let document = document(); + #[cfg(feature = "web_sys")] + let document = web_sys::window().unwrap().document().unwrap(); + // Bootstrap the component for `Window` environment only (not for `Worker`) - let element = document() + let element = document .query_selector("body") .expect("can't get body node for rendering") .expect("can't unwrap body node"); @@ -110,11 +128,16 @@ where /// when you need to manipulate the body element. For example, adding/removing app-wide /// CSS classes of the body element. pub fn mount_as_body_with_props(self, props: COMP::Properties) -> Scope { - let html_element = document() + #[cfg(feature = "std_web")] + let document = document(); + #[cfg(feature = "web_sys")] + let document = web_sys::window().unwrap().document().unwrap(); + + let html_element = document .query_selector("html") .expect("can't get html node for rendering") .expect("can't unwrap html node"); - let body_element = document() + let body_element = document .query_selector("body") .expect("can't get body node for rendering") .expect("can't unwrap body node"); diff --git a/src/components/select.rs b/src/components/select.rs index a28f5d61892..cb919f2782c 100644 --- a/src/components/select.rs +++ b/src/components/select.rs @@ -121,7 +121,11 @@ where fn onchange(&self) -> Callback { self.link.callback(|event| match event { ChangeData::Select(elem) => { - let value = elem.selected_index().map(|x| x as usize); + let value = elem.selected_index(); + #[cfg(feature = "std_web")] + let value = value.map(|x| x as usize); + #[cfg(feature = "web_sys")] + let value = Some(value as usize); Msg::Selected(value) } _ => { diff --git a/src/lib.rs b/src/lib.rs index bc74e9b5b51..08e247834f8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -96,6 +96,7 @@ pub mod services; pub mod events { pub use crate::html::{ChangeData, InputData}; + #[cfg(feature = "std_web")] pub use stdweb::web::event::{ BlurEvent, ClickEvent, ContextMenuEvent, DoubleClickEvent, DragDropEvent, DragEndEvent, DragEnterEvent, DragEvent, DragExitEvent, DragLeaveEvent, DragOverEvent, DragStartEvent, @@ -106,15 +107,24 @@ pub mod events { PointerLeaveEvent, PointerMoveEvent, PointerOutEvent, PointerOverEvent, PointerUpEvent, ScrollEvent, SubmitEvent, TouchCancel, TouchEnd, TouchEnter, TouchMove, TouchStart, }; + #[cfg(feature = "web_sys")] + pub use web_sys::{ + DragEvent, Event, FocusEvent, KeyboardEvent, MouseEvent, PointerEvent, TouchEvent, UiEvent, + WheelEvent, + }; } /// Initializes yew framework. It should be called first. pub fn initialize() { + #[cfg(feature = "std_web")] stdweb::initialize(); + #[cfg(feature = "web_sys")] + std::panic::set_hook(Box::new(console_error_panic_hook::hook)); } /// Starts event loop. pub fn run_loop() { + #[cfg(feature = "std_web")] stdweb::event_loop(); } diff --git a/src/utils.rs b/src/utils.rs index c56d43227ba..62f24e15bf1 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,12 +1,31 @@ //! This module contains useful utils to get information about the current document. use failure::{err_msg, Error}; +#[cfg(feature = "std_web")] use stdweb::web::document; /// Returns `host` for the current document. Useful to connect to a server that server the app. pub fn host() -> Result { - document() + #[cfg(feature = "std_web")] + { + document() + .location() + .ok_or_else(|| err_msg("can't get location")) + .and_then(|l| l.host().map_err(Error::from)) + } + #[cfg(feature = "web_sys")] + web_sys::window() + .unwrap() + .document() + .unwrap() .location() .ok_or_else(|| err_msg("can't get location")) - .and_then(|l| l.host().map_err(Error::from)) + .and_then(|l| { + l.host().map_err(|e| { + err_msg( + e.as_string() + .unwrap_or_else(|| String::from("error not recoverable")), + ) + }) + }) }