From b9ff95fa12c46365fe73b64a4926a506d5da2342 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 16 Jun 2021 23:37:55 -0400 Subject: [PATCH] wip: start moving events to rc --- Cargo.toml | 63 ++++++------ examples/Cargo.toml | 82 ---------------- examples/README.md | 4 +- examples/blah.rs | 9 -- examples/common.rs | 50 ---------- examples/doc_generator.rs | 56 ----------- examples/external_updates.rs | 5 +- examples/fc_macro.rs | 75 --------------- examples/hello_web.rs | 6 -- examples/router.rs | 4 +- examples/tide_ssr.rs | 104 -------------------- packages/core/examples/rsx_usage.rs | 78 --------------- packages/core/src/events.rs | 143 +++++++++------------------- packages/core/src/virtual_dom.rs | 3 +- packages/inputs/Cargo.toml | 8 ++ packages/inputs/README.md | 28 ++++++ packages/inputs/src/lib.rs | 7 ++ packages/web/Cargo.toml | 4 + packages/web/examples/input.rs | 72 ++++++++++++++ packages/web/src/interpreter.rs | 37 ++++++- src/lib.rs | 16 +++- 21 files changed, 249 insertions(+), 605 deletions(-) delete mode 100644 examples/Cargo.toml delete mode 100644 examples/blah.rs delete mode 100644 examples/common.rs delete mode 100644 examples/doc_generator.rs delete mode 100644 examples/fc_macro.rs delete mode 100644 examples/hello_web.rs delete mode 100644 examples/tide_ssr.rs create mode 100644 packages/inputs/Cargo.toml create mode 100644 packages/inputs/README.md create mode 100644 packages/inputs/src/lib.rs create mode 100644 packages/web/examples/input.rs diff --git a/Cargo.toml b/Cargo.toml index 6c87ee13c..c6db9998c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,41 +23,38 @@ router = [] web = [] desktop = [] -[workspace] -# members = ["packages/core-macro"] -members = [ - "packages/core-macro", - "packages/core", - "packages/web", - "packages/ssr", - "packages/docsite", - "packages/atoms", - "packages/router", -] -# "packages/webview", - -# "packages/cli", -# "packages/webview", -# "packages/hooks", -# "packages/ios", [profile.dev] split-debuginfo = "unpacked" -# "packages/liveview", -# "packages/3d", -# Built-in -# "packages/webview/client", -# "packages/router", -# "packages/webview", -# "packages/livehost", -# "packages/vscode-ext", -# "packages/recoil", -# "packages/redux", -# "packages/macro", -# TODO @Jon, share the validation code -# "packages/web", -# "packages/cli", -# "examples", -# "packages/html-macro", +[dev-dependencies] +# For the tide ssr examples +async-std = { version="1.9.0", features=["attributes"] } +tide = { version="0.16.0" } + +# For the livewview example +tide-websockets = "0.4.0" +serde_millis = "0.1" +serde_json = "1" +serde = { version="1", features=['derive'] } + +# For the doc generator +pulldown-cmark = { version="0.8.0", default-features=false } + +dioxus-webview = { path="./packages/webview", version="0.0.0" } +dioxus-hooks = { path="./packages/hooks", version="0.0.0" } +rand = "0.8.4" + + +[workspace] +members = [ + "packages/core-macro", + "packages/core", + "packages/web", + # "packages/ssr", + # "packages/docsite", + # "packages/atoms", + # "packages/router", + # "packages/inputs", +] diff --git a/examples/Cargo.toml b/examples/Cargo.toml deleted file mode 100644 index a3b59e4da..000000000 --- a/examples/Cargo.toml +++ /dev/null @@ -1,82 +0,0 @@ -[package] -name = "dioxus-examples" -version = "0.0.0" -authors = ["Jonathan Kelley "] -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -fern = { version = "0.6.0", features = ["colored"] } -log = "0.4.1" -dioxus = { path = "../packages/dioxus" } -dioxus-ssr = { path = "../packages/ssr" } -rand = "0.8.2" -anyhow = "*" - - -[dev-dependencies] -# For the tide ssr examples -async-std = { version = "1.9.0", features = ["attributes"] } -tide = { version = "0.15.0" } - -# For the livewview example -tide-websockets = "0.1.0" -serde_millis = "0.1" -serde_json = "1" -serde = { version = "1", features = ['derive'] } - -# For the doc generator -pulldown-cmark = { version = "0.8.0", default-features = false } - -dioxus-webview = { path = "../packages/webview", version = "0.0.0" } -dioxus-hooks = { path = "../packages/hooks", version = "0.0.0" } - - -# Shared functionality is done as a lib -[lib] -path = "common.rs" - -# ================================ -# Examples are manually keyed in -# ================================ - -[[example]] -path = "example_app.rs" -name = "example_app" - -[[example]] -path = "website.rs" -name = "website" - -# [[example]] -# path = "hello_web.rs" -# name = "hello_web" - -# [[example]] -# path = "tide_ssr.rs" -# name = "tide_ssr" - -# [[example]] -# path = "doc_generator.rs" -# name = "doc_generator" - -# [[example]] -# path = "router.rs" -# name = "router" - -# [[example]] -# path = "fc_macro.rs" -# name = "fc_macro" - -# [[example]] -# path = "webview.rs" -# name = "webview" - -# [[example]] -# path = "blah.rs" -# name = "blah" - -# [[example]] -# path = "live.rs" -# name = "live" diff --git a/examples/README.md b/examples/README.md index 8a22b3c05..2f1fc982c 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,3 +1,5 @@ # Examples -Most of these examples are run through webview so you don't need the dioxus cli installed to preview the functionality. Anything labeled `_web` will need to be compiled with Dioxus CLI. +Most of these examples are run through webview so you don't need the dioxus cli installed to preview the functionality. Anything labeled `_web` will need to be built with the Dioxus CLI to preview features that only a native bundle can handle. + +List of examples: diff --git a/examples/blah.rs b/examples/blah.rs deleted file mode 100644 index 54a7ed29a..000000000 --- a/examples/blah.rs +++ /dev/null @@ -1,9 +0,0 @@ -use dioxus::prelude::*; - -fn main() { - let g = html! { -
- -
- }; -} diff --git a/examples/common.rs b/examples/common.rs deleted file mode 100644 index d5097da24..000000000 --- a/examples/common.rs +++ /dev/null @@ -1,50 +0,0 @@ -//! Common utilities for integration examples - -pub mod logger { - - use fern::colors::{Color, ColoredLevelConfig}; - use log::debug; - - pub fn set_up_logging(bin_name: &'static str) { - // configure colors for the whole line - let colors_line = ColoredLevelConfig::new() - .error(Color::Red) - .warn(Color::Yellow) - // we actually don't need to specify the color for debug and info, they are white by default - .info(Color::White) - .debug(Color::White) - // depending on the terminals color scheme, this is the same as the background color - .trace(Color::BrightBlack); - - // configure colors for the name of the level. - // since almost all of them are the same as the color for the whole line, we - // just clone `colors_line` and overwrite our changes - let colors_level = colors_line.clone().info(Color::Green); - // here we set up our fern Dispatch - fern::Dispatch::new() - .format(move |out, message, record| { - out.finish(format_args!( - "{color_line}[{level}{color_line}] {message}\x1B[0m", - color_line = format_args!( - "\x1B[{}m", - colors_line.get_color(&record.level()).to_fg_str() - ), - level = colors_level.color(record.level()), - message = message, - )); - }) - // set the default log level. to filter out verbose log messages from dependencies, set - // this to Warn and overwrite the log level for your crate. - .level(log::LevelFilter::Warn) - // change log levels for individual modules. Note: This looks for the record's target - // field which defaults to the module path but can be overwritten with the `target` - // parameter: - // `info!(target="special_target", "This log message is about special_target");` - .level_for(bin_name, log::LevelFilter::Info) - // .level_for("pretty_colored", log::LevelFilter::Trace) - // output to stdout - .chain(std::io::stdout()) - .apply() - .unwrap(); - } -} diff --git a/examples/doc_generator.rs b/examples/doc_generator.rs deleted file mode 100644 index 2d8c599a9..000000000 --- a/examples/doc_generator.rs +++ /dev/null @@ -1,56 +0,0 @@ -//! The docs generator takes in the `docs` folder and creates a neat, statically-renderer webpage. -//! These docs are used to generate the public-facing doc content, showcasing Dioxus' abiltity to -//! be used in custom static rendering pipelines. -//! -//! We use pulldown_cmark as the markdown parser, but instead of outputting html directly, we output -//! VNodes to be used in conjuction with our custom templates. - -use dioxus::core::prelude::*; -use pulldown_cmark::{Options, Parser}; - -fn main() { - let gen_dir = "../docs/"; - - let site: FC<()> = |_| { - html! { - - - - - - - - - } - }; -} - -static Homepage: FC<()> = |_| { - html! {
} -}; - -static DocPage: FC<()> = |_| { - html! {
} -}; - -// struct StaticSiteCfg { -// gen_dir: &'static str, -// homepage_template: fn() -> VNode, -// page_template: fn(page: &'static str) -> VNode, -// } - -// impl StaticSiteCfg { -// fn render(self) -> anyhow::Result { -// let StaticSiteCfg { .. } = self; - -// // Set up options and parser. Strikethroughs are not part of the CommonMark standard -// // and we therefore must enable it explicitly. -// let mut options = Options::empty(); -// options.insert(Options::ENABLE_STRIKETHROUGH); -// let parser = Parser::new_ext(markdown_input, options); - -// // - -// Ok(html! {
}) -// } -// } diff --git a/examples/external_updates.rs b/examples/external_updates.rs index 262b29224..c12fbec80 100644 --- a/examples/external_updates.rs +++ b/examples/external_updates.rs @@ -1,5 +1,6 @@ //! Example: External Updates //! ------------------------- +//! //! Cause updates to the VirtualDOM state from outside the component lifecycle. //! The root props could be changed or the use_receiver hook could be used. //! @@ -30,8 +31,8 @@ fn App(ctx: Context, props: &RootProps) -> VNode { ctx.render(rsx! { div { - a { href="/dogs/"} - a { href="/cats/"} + a { href: "/dogs/"} + a { href: "/cats/"} {content} } }) diff --git a/examples/fc_macro.rs b/examples/fc_macro.rs deleted file mode 100644 index 31acac19f..000000000 --- a/examples/fc_macro.rs +++ /dev/null @@ -1,75 +0,0 @@ -use dioxus::prelude::*; -use dioxus_ssr::TextRenderer; - -// todo @Jon, support components in the html! macro -// let renderer = TextRenderer::new(|_| html! {}); -fn main() { - let renderer = TextRenderer::<()>::new(|_| html! {
"Hello world"
}); - let output = renderer.render(); -} - -/// An example component that demonstrates how to use the functional_component macro -/// This macro makes writing functional components elegant, similar to how Rocket parses URIs. -/// -/// You don't actually *need* this macro to be productive, but it makes life easier, and components cleaner. -/// This approach also integrates well with tools like Rust-Analyzer. -/// -/// Notice that Context is normally generic over props, but RA doesn't care when in proc-macro mode. -/// Also notice that ctx.props still works like you would expect, so migrating to the macro is easy. -#[fc] -fn example(ctx: &Context, name: String) -> VNode { - html! {
"Hello, {name}!"
} -} - -/* -TODO - -/// The macro can also be applied to statics in order to make components less verbose -/// The FC type automatically adds the inference, and property fields are automatically added as function arguments -#[fc] -static Example: FC = |ctx, name: String| { - html! {
"Hello, {name}!"
} -}; -*/ - -// This trait is not exposed to users directly, though they could manually implement this for struct-style components -trait Comp { - type Props: Properties; - fn render(&self, ctx: &mut Context) -> VNode; - fn builder(&self) -> Self::Props; -} -trait Properties { - fn new() -> Self; -} - -impl Comp for FC { - type Props = T; - - fn render(&self, ctx: &mut Context) -> VNode { - let g = self(ctx); - g - } - - fn builder(&self) -> T { - T::new() - } -} - -#[allow(unused, non_upper_case_globals)] -static MyComp: FC<()> = |ctx| { - html! { -
-

"hello world"

-
- } -}; - -fn my_comp(ctx: &Context<()>) -> VNode { - todo!() -} - -fn test() { - let mut ctx = Context { props: &() }; - let f = MyComp.render(&mut ctx); - let props = MyComp.builder(); -} diff --git a/examples/hello_web.rs b/examples/hello_web.rs deleted file mode 100644 index 1f4c05399..000000000 --- a/examples/hello_web.rs +++ /dev/null @@ -1,6 +0,0 @@ -//! Example: Hello-web -//! ----------------- -//! -//! This example showcases Dioxus-websys to build interactive single-page-applications, much like React. - -fn main() {} diff --git a/examples/router.rs b/examples/router.rs index d0d37dbd9..983a18ff4 100644 --- a/examples/router.rs +++ b/examples/router.rs @@ -34,8 +34,8 @@ fn App(ctx: Context<()>) -> VNode { ctx.render(rsx! { div { - a { href="/dogs/"} - a { href="/cats/"} + a { href: "/dogs/"} + a { href: "/cats/"} {router.render()} } }) diff --git a/examples/tide_ssr.rs b/examples/tide_ssr.rs deleted file mode 100644 index 409b86b37..000000000 --- a/examples/tide_ssr.rs +++ /dev/null @@ -1,104 +0,0 @@ -//! Example: Tide Server-Side-Rendering -//! ----------------------------------- -//! -//! This demo shows how to use the to_string utility on VNodes to convert them into valid HTML. -//! You can use the html! macro to craft webpages generated by the server on-the-fly. -//! -//! Server-side-renderered webpages are a great use of Rust's async story, where servers can handle -//! thousands of simultaneous clients on minimal hardware. - -use dioxus::prelude::*; -use dioxus_ssr::TextRenderer; -use rand::Rng; -use tide::{Request, Response}; - -#[async_std::main] -async fn main() -> Result<(), std::io::Error> { - dioxus_examples::logger::set_up_logging("tide_ssr"); - - // Build the API - let mut app = tide::new(); - app.at("/fib/:n").get(fibsum); - - // Launch the server - let addr = "127.0.0.1:8080"; - log::info!("App is ready at {}", addr); - log::info!("Navigate to a fibonacci number: http://{}/fib/21", addr); - app.listen(addr).await?; - - Ok(()) -} - -fn fib(n: usize) -> usize { - if n == 0 || n == 1 { - n - } else { - fib(n - 1) + fib(n - 2) - } -} - -/// Calculate the fibonacci number for a given request input -async fn fibsum(req: Request<()>) -> tide::Result { - let n: usize = req.param("n")?.parse().unwrap_or(0); - - // Start a stopwatch - // Compute the nth number in the fibonacci sequence - // Stop the stopwatch - let start = std::time::Instant::now(); - let fib_n = fib(n); - let duration = start.elapsed().as_nanos(); - - // Generate another random number to try - let other_fib_to_try = rand::thread_rng().gen_range(1..42); - - let text = TextRenderer::<()>::to_text(html! { - - - // Header - - - - - - - // Body - -
-
-
- // Title -
- {format!("Fibonacci Calculator: n = {}",n)} -
- - // Subtext / description -
- {format!("Calculated in {} nanoseconds",duration)} -
- -
- // Main number -
- {format!("{}",fib_n)} -
-
- - // Try another - -
-
-
- - - - }); - - Ok(Response::builder(203) - .body(text) - .content_type(tide::http::mime::HTML) - .build()) -} diff --git a/packages/core/examples/rsx_usage.rs b/packages/core/examples/rsx_usage.rs index d739e1bf6..c3b5b8d64 100644 --- a/packages/core/examples/rsx_usage.rs +++ b/packages/core/examples/rsx_usage.rs @@ -45,81 +45,3 @@ impl Render for Button { }) } } - -// #[fc] -// fn Button(ctx: Context, onhover: Option<&dyn Fn()>) -> VNode {} - -// h1 { -// tag: "type", abc: 123, class: "big small wide short", -// "title1" -// "title1" -// "title1" -// "title" -// } - -// h1 ("title") { -// tag: "type", -// abc: 123, -// class: "big small wide short", -// } - -// // - -// button { "Jill!", -// class: "inline-block py-4 px-8 mr-6 leading-none text-white bg-indigo-600 hover:bg-indigo-900 font-semibold rounded shadow" -// onclick: move |_| set_name("jill"), -// onclick: move |_| set_name("jill"), -// } - -// button { -// class: "inline-block py-4 px-8 mr-6 leading-none text-white bg-indigo-600 hover:bg-indigo-900 font-semibold rounded shadow" -// onclick: move |_| set_name("jill"), -// onclick: move |_| set_name("jill"), -// // this is valid -// "Jill!", -// // this is also valid -// {"Jill!"} -// } - -// h1 { "Text", class: "inline-block py-4 px-8 mr-6 leading-none" } - -// //

-// // "Text" -// //

- -// h1 { -// div { -// h1 {} -// h2 {} -// Brick {} - -// p {} -// p { -// tag: "type", -// abc: 123, -// enabled: true, -// class: "big small wide short", - -// a { "abcder" }, -// h2 { "whatsup", class: "abc-123" }, -// CustomComponent { a: 123, b: 456, key: "1" }, -// } - -// div { class: "big small wide short", -// div {}, -// div {}, -// div {}, -// div {}, -// } -// } -// } - -// h2 {} -// h3 {} -// "abcd123" diff --git a/packages/core/src/events.rs b/packages/core/src/events.rs index 03ea198c3..35a53b43f 100644 --- a/packages/core/src/events.rs +++ b/packages/core/src/events.rs @@ -1,9 +1,8 @@ -//! Virtual Events -//! This module provides a wrapping of platform-specific events with a list of events easier to work with. -//! 3rd party renderers are responsible for forming this virtual events from events. -//! The goal here is to provide a consistent event interface across all renderer types. +//! This module provides a set of common events for all Dioxus apps to target, regardless of host platform. +//! ------------------------------------------------------------------------------------------------------- //! -//! also... websys integerates poorly with rust analyzer, so we handle that for you automatically. +//! 3rd party renderers are responsible for converting their native events into these virtual event types. Events might +//! be heavy or need to interact through FFI, so the events themselves are designed to be lazy. use crate::innerlude::ScopeIdx; @@ -46,15 +45,13 @@ pub enum VirtualEvent { MouseEvent(on::MouseEvent), PointerEvent(on::PointerEvent), - // todo - // ImageEvent(event_data::ImageEvent), OtherEvent, } pub mod on { #![allow(unused)] - use std::ops::Deref; + use std::{ops::Deref, rc::Rc}; use crate::{ builder::ElementBuilder, @@ -64,15 +61,10 @@ pub mod on { use super::VirtualEvent; - macro_rules! event_builder { - ( - $eventdata:ident; + macro_rules! event_directory { + ( $( $eventdata:ident: [ $( $name:ident )* ]; )* ) => { $( - $(#[$attr:meta])* - $name:ident - )* ) => { $( - $(#[$attr])* pub fn $name<'a>( c: &'_ NodeCtx<'a>, callback: impl Fn($eventdata) + 'a, @@ -84,41 +76,61 @@ pub mod on { scope: c.scope_ref.arena_idx, callback: bump.alloc(move |evt: VirtualEvent| match evt { VirtualEvent::$eventdata(event) => callback(event), - _ => { - unreachable!("Downcasted VirtualEvent to wrong event type - this is a bug!") - } + _ => unreachable!("Downcasted VirtualEvent to wrong event type - this is an internal bug!") }), } } )* - }; - } + )* + }; + } + + event_directory! { + ClipboardEvent: [copy cut paste]; + CompositionEvent: [compositionend compositionstart compositionupdate]; + KeyboardEvent: [keydown keypress keyup]; + FocusEvent: [focus blur]; + FormEvent: [change input invalid reset submit]; + GenericEvent: []; + MouseEvent: [ + click contextmenu doubleclick drag dragend dragenter dragexit + dragleave dragover dragstart drop mousedown mouseenter mouseleave + mousemove mouseout mouseover mouseup + ]; + PointerEvent: [ + pointerdown pointermove pointerup pointercancel gotpointercapture + lostpointercapture pointerenter pointerleave pointerover pointerout + ]; + SelectionEvent: [select]; + TouchEvent: [touchcancel touchend touchmove touchstart]; + UIEvent: [scroll]; + WheelEvent: [wheel]; + MediaEvent: [ + abort canplay canplaythrough durationchange emptied encrypted + ended error loadeddata loadedmetadata loadstart pause play + playing progress ratechange seeked seeking stalled suspend + timeupdate volumechange waiting + ]; + AnimationEvent: [animationstart animationend animationiteration]; + TransitionEvent: [transitionend]; + ToggleEvent: [toggle]; + } pub struct GetModifierKey(pub Box bool>); impl std::fmt::Debug for GetModifierKey { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - // just skip for now - Ok(()) + Ok(()) // just skip for now } } // DOMDataTransfer clipboardData #[derive(Debug)] pub struct ClipboardEvent {} - event_builder! { - ClipboardEvent; - copy cut paste - } - // string data #[derive(Debug)] pub struct CompositionEvent { data: String, } - event_builder! { - CompositionEvent; - compositionend compositionstart compositionupdate - } #[derive(Debug)] pub struct KeyboardEvent { @@ -134,12 +146,11 @@ pub mod on { which: usize, get_modifier_state: GetModifierKey, } - pub struct KeyboardEvent2(pub Box); + pub struct KeyboardEvent2(pub Rc); impl std::ops::Deref for KeyboardEvent2 { - type Target = Box; - + type Target = dyn KeyboardEventT; fn deref(&self) -> &Self::Target { - &self.0 + self.0.as_ref() } } @@ -157,32 +168,16 @@ pub mod on { fn get_modifier_state(&self) -> GetModifierKey; } - event_builder! { - KeyboardEvent; - keydown keypress keyup - } - #[derive(Debug)] pub struct FocusEvent {/* DOMEventTarget relatedTarget */} - event_builder! { - FocusEvent; - focus blur - } #[derive(Debug)] pub struct FormEvent { pub value: String, } - event_builder! { - FormEvent; - change input invalid reset submit - } #[derive(Debug)] pub struct GenericEvent {/* Error Load */} - event_builder! { - GenericEvent; - } #[derive(Debug)] pub struct MouseEvent(pub Box); @@ -210,12 +205,6 @@ pub mod on { self.0.as_ref() } } - event_builder! { - MouseEvent; - click contextmenu doubleclick drag dragend dragenter dragexit - dragleave dragover dragstart drop mousedown mouseenter mouseleave - mousemove mouseout mouseover mouseup - } #[derive(Debug)] pub struct PointerEvent(Box); @@ -255,18 +244,9 @@ pub mod on { pointer_type: String, is_primary: bool, } - event_builder! { - PointerEvent; - pointerdown pointermove pointerup pointercancel gotpointercapture - lostpointercapture pointerenter pointerleave pointerover pointerout - } #[derive(Debug)] pub struct SelectionEvent {} - event_builder! { - SelectionEvent; - select - } #[derive(Debug)] pub struct TouchEvent { @@ -282,20 +262,12 @@ pub mod on { // touches: DOMTouchList, // getModifierState(key): boolean } - event_builder! { - TouchEvent; - touchcancel touchend touchmove touchstart - } #[derive(Debug)] pub struct UIEvent { // DOMAbstractView view detail: i32, } - event_builder! { - UIEvent; - scroll - } #[derive(Debug)] pub struct WheelEvent { @@ -304,20 +276,9 @@ pub mod on { delta_y: i32, delta_z: i32, } - event_builder! { - WheelEvent; - wheel - } #[derive(Debug)] pub struct MediaEvent {} - event_builder! { - MediaEvent; - abort canplay canplaythrough durationchange emptied encrypted - ended error loadeddata loadedmetadata loadstart pause play - playing progress ratechange seeked seeking stalled suspend - timeupdate volumechange waiting - } // todo! // imageevent clashes with media event @@ -336,10 +297,6 @@ pub mod on { pseudo_element: String, elapsed_time: f32, } - event_builder! { - AnimationEvent; - animationstart animationend animationiteration - } #[derive(Debug)] pub struct TransitionEvent { @@ -347,15 +304,7 @@ pub mod on { pseudo_element: String, elapsed_time: f32, } - event_builder! { - TransitionEvent; - transitionend - } #[derive(Debug)] pub struct ToggleEvent {} - event_builder! { - ToggleEvent; - toggle - } } diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index 2a35a341a..2c04a99d1 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -274,7 +274,7 @@ impl VirtualDom { // Now, there are events in the queue let mut updates = self.event_queue.0.as_ref().borrow_mut(); - // Order the nodes by their height, we want the biggest nodes on the top + // Order the nodes by their height, we want the nodes with the smallest depth on top // This prevents us from running the same component multiple times updates.sort_unstable(); @@ -300,6 +300,7 @@ impl VirtualDom { // let cur_component = inner.get_mut(update.idx).unwrap(); cur_component.run_scope()?; + // diff_machine.change_list.load_known_root(1); diff_machine.diff_node(cur_component.old_frame(), cur_component.next_frame()); diff --git a/packages/inputs/Cargo.toml b/packages/inputs/Cargo.toml new file mode 100644 index 000000000..e7cf1c5e9 --- /dev/null +++ b/packages/inputs/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "dioxus-inputs" +version = "0.0.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] \ No newline at end of file diff --git a/packages/inputs/README.md b/packages/inputs/README.md new file mode 100644 index 000000000..ae99b4e5b --- /dev/null +++ b/packages/inputs/README.md @@ -0,0 +1,28 @@ +# Cross-platform controlled inputs + +Dioxus does not include controlled inputs in Dioxus-Core. This would require core integration with HTML that is inherently not cross platform. Instead, this crate exists to provide a bunch of cross-platform input types that abstract over renderer quirks. + +Included is: + +- Button +- Checkbox +- Color +- Date +- Datetime-local +- Email +- File +- Hidden +- Image +- Month +- Number +- Password +- Radio +- Range +- Reset +- Search +- Submit +- Tel +- Text +- Time +- Url +- Week diff --git a/packages/inputs/src/lib.rs b/packages/inputs/src/lib.rs new file mode 100644 index 000000000..31e1bb209 --- /dev/null +++ b/packages/inputs/src/lib.rs @@ -0,0 +1,7 @@ +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +} diff --git a/packages/web/Cargo.toml b/packages/web/Cargo.toml index 1e8b60900..49154404d 100644 --- a/packages/web/Cargo.toml +++ b/packages/web/Cargo.toml @@ -36,9 +36,12 @@ version = "0.3.50" features = [ "Comment", "Document", + # "DataTransfer", "Element", "HtmlElement", "HtmlInputElement", + "HtmlSelectElement", + "HtmlTextAreaElement", "EventTarget", "HtmlCollection", "Node", @@ -60,6 +63,7 @@ features = [ "DocumentType", "CharacterData", "HtmlOptionElement", + ] [profile.release] diff --git a/packages/web/examples/input.rs b/packages/web/examples/input.rs new file mode 100644 index 000000000..857c052ec --- /dev/null +++ b/packages/web/examples/input.rs @@ -0,0 +1,72 @@ +use dioxus_core as dioxus; +use dioxus_core::prelude::*; +use dioxus_web::WebsysRenderer; + +fn main() { + // wasm_logger::init(wasm_logger::Config::new(log::Level::Debug)); + console_error_panic_hook::set_once(); + + log::info!("hello world"); + wasm_bindgen_futures::spawn_local(WebsysRenderer::start(App)); +} + +static App: FC<()> = |ctx| { + let (val, set_val) = use_state(&ctx, || "asd".to_string()); + + ctx.render(rsx! { + div { class: "max-w-lg max-w-xs bg-blue-800 shadow-2xl rounded-lg mx-auto text-center py-12 mt-4 rounded-xl" + div { class: "container py-5 max-w-md mx-auto" + h1 { class: "text-gray-200 text-center font-extrabold -mt-3 text-3xl", + "Text Input Example" + } + div { class: "mb-4" + input { + placeholder: "Username" + class: "shadow appearance-none rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" + id: "username" + type: "text" + value: "{val}" + oninput: move |evet| { + log::debug!("Value is {:#?}", evet); + set_val(evet.value); + } + } + p { "Val is: {val}" } + } + } + } + }) +}; + +static Example: FC<()> = |ctx| { + ctx.render(rsx! { + div { class: "max-w-lg max-w-xs bg-blue-800 shadow-2xl rounded-lg mx-auto text-center py-12 mt-4 rounded-xl" + div { class: "container py-5 max-w-md mx-auto" + h1 { class: "text-gray-200 text-center font-extrabold -mt-3 text-3xl", + "Text Input Example" + } + UserInput {} + } + } + }) +}; + +static UserInput: FC<()> = |ctx| { + let (val, set_val) = use_state(&ctx, || "asd".to_string()); + + rsx!{ in ctx, + div { class: "mb-4" + input { class: "shadow appearance-none rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" + placeholder: "Username" + id: "username" + type: "text" + oninput: move |evet| { + log::debug!("Value is {:#?}", evet); + set_val(evet.value); + } + } + p { "Val is: {val}" } + } + } + +}; diff --git a/packages/web/src/interpreter.rs b/packages/web/src/interpreter.rs index 9836f4a4f..15ef15e84 100644 --- a/packages/web/src/interpreter.rs +++ b/packages/web/src/interpreter.rs @@ -7,7 +7,9 @@ use dioxus_core::{ }; use fxhash::FxHashMap; use wasm_bindgen::{closure::Closure, JsCast}; -use web_sys::{window, Document, Element, Event, HtmlInputElement, HtmlOptionElement, Node}; +use web_sys::{ + window, Document, Element, Event, HtmlElement, HtmlInputElement, HtmlOptionElement, Node, +}; #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] pub struct CacheId(u32); @@ -564,11 +566,38 @@ fn virtual_event_from_websys_event(event: &web_sys::Event) -> VirtualEvent { todo!() } - "change" | "input" | "invalid" | "reset" | "submit" => { + "change" => { + let evt: web_sys::Event = event.clone().dyn_into().expect("wrong error typ"); + todo!() + // VirtualEvent::FormEvent(FormEvent {value:}) + } + + "input" | "invalid" | "reset" | "submit" => { // is a special react events let evt: web_sys::InputEvent = event.clone().dyn_into().expect("wrong event type"); - let value: Option = (&evt).data(); - let value = value.unwrap_or_default(); + let this: web_sys::EventTarget = evt.target().unwrap(); + + let value = (&this) + .dyn_ref() + .map(|input: &web_sys::HtmlInputElement| input.value()) + .or_else(|| { + (&this) + .dyn_ref() + .map(|input: &web_sys::HtmlTextAreaElement| input.value()) + }) + .or_else(|| { + (&this) + .dyn_ref::() + .unwrap() + .text_content() + }) + .expect("only an InputElement or TextAreaElement or an element with contenteditable=true can have an oninput event listener"); + + // let p2 = evt.data_transfer(); + + // let value: Option = (&evt).data(); + // let value = val; + // let value = value.unwrap_or_default(); // let value = (&evt).data().expect("No data to unwrap"); // todo - this needs to be a "controlled" event diff --git a/src/lib.rs b/src/lib.rs index 400b2ce0e..79bdedf7b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -177,6 +177,11 @@ //! }; //! ``` +pub mod prelude { + //! A glob import that includes helper types like FC, rsx!, html!, and required traits + pub use dioxus_core::prelude::*; + pub use dioxus_core_macro::fc; +} // Just a heads-up, the core functionality of dioxus rests in Dioxus-Core. This crate just wraps a bunch of utilities // together and exports their namespaces to something predicatble. #[cfg(feature = "core")] @@ -186,12 +191,12 @@ pub mod core { // Re-export core completely pub use dioxus_core::*; } -pub mod prelude { - //! A glob import that includes helper types like FC, rsx!, html!, and required traits - pub use dioxus_core::prelude::*; - pub use dioxus_core_macro::fc; -} +// Input elements work differently on different platforms. +// This module helps abstract over Selects, TextInputs, TextAreas, Radios, etc for a cross-platform input experience +pub mod inputs { + //! Cross-platform abstractions over user inputs +} #[cfg(feature = "web")] pub mod web { //! A web-sys based renderer for building fast and interactive web applications @@ -214,6 +219,7 @@ pub mod testing { } #[cfg(feature = "atoms")] pub mod atoms {} + #[cfg(feature = "desktop")] pub mod desktop { //! A webview based renderer for building desktop applications with Dioxus