Browse Source

Merge remote-tracking branch 'origin/main' into jk/release-pre-07

Jonathan Kelley 5 days ago
parent
commit
fa21dd7368

+ 15 - 0
Cargo.lock

@@ -5637,6 +5637,7 @@ dependencies = [
  "dioxus-document",
  "dioxus-history",
  "dioxus-html",
+ "dioxus-native-dom",
  "futures-util",
  "keyboard-types",
  "rustc-hash 2.1.1",
@@ -5645,6 +5646,20 @@ dependencies = [
  "winit",
 ]
 
+[[package]]
+name = "dioxus-native-dom"
+version = "0.7.0-alpha.2"
+dependencies = [
+ "blitz-dom",
+ "blitz-traits",
+ "dioxus-core",
+ "dioxus-html",
+ "futures-util",
+ "keyboard-types",
+ "rustc-hash 2.1.1",
+ "tracing",
+]
+
 [[package]]
 name = "dioxus-playwright-default-features-disabled-test"
 version = "0.1.0"

+ 2 - 0
Cargo.toml

@@ -69,6 +69,7 @@ members = [
     "packages/logger",
     "packages/config-macros",
     "packages/native",
+    "packages/native-dom",
     "packages/asset-resolver",
     "packages/depinfo",
     "packages/server",
@@ -175,6 +176,7 @@ dioxus_server_macro = { path = "packages/server-macro", version = "0.7.0-alpha.2
 dioxus-dx-wire-format = { path = "packages/dx-wire-format", version = "0.7.0-alpha.2" }
 dioxus-logger = { path = "packages/logger", version = "0.7.0-alpha.2" }
 dioxus-native = { path = "packages/native", version = "0.7.0-alpha.2" }
+dioxus-native-dom = { path = "packages/native-dom", version = "0.7.0-alpha.2" }
 dioxus-asset-resolver = { path = "packages/asset-resolver", version = "0.7.0-alpha.2" }
 dioxus-config-macros = { path = "packages/config-macros", version = "0.7.0-alpha.2" }
 const-serialize = { path = "packages/const-serialize", version = "0.7.0-alpha.2" }

+ 3 - 5
packages/fullstack-hooks/src/history.rs

@@ -2,10 +2,8 @@
 
 use std::cell::OnceCell;
 
-use dioxus_core::{
-    prelude::{generation, queue_effect},
-    schedule_update,
-};
+use dioxus_core::{prelude::queue_effect, schedule_update};
+use dioxus_fullstack_protocol::is_hydrating;
 use dioxus_history::History;
 
 // If we are currently in a scope and this is the first run then queue a rerender
@@ -14,7 +12,7 @@ fn match_hydration<O>(
     during_hydration: impl FnOnce() -> O,
     after_hydration: impl FnOnce() -> O,
 ) -> O {
-    if generation() == 0 {
+    if is_hydrating() {
         let update = schedule_update();
         queue_effect(move || update());
         during_hydration()

+ 13 - 0
packages/fullstack-protocol/src/lib.rs

@@ -144,6 +144,19 @@ impl<T> SerializeContextEntry<T> {
     }
 }
 
+/// Check if the client is currently rendering a component for hydration. Always returns true on the server.
+pub fn is_hydrating() -> bool {
+    #[cfg(feature = "web")]
+    {
+        // On the client, we can check if the context is set
+        CONTEXT.with(|context| context.borrow().is_some())
+    }
+    #[cfg(not(feature = "web"))]
+    {
+        true
+    }
+}
+
 /// Get or insert the current serialize context. On the client, the hydration context this returns
 /// will always return `TakeDataError::DataNotAvailable` if hydration of the current chunk is finished.
 pub fn serialize_context() -> HydrationContext {

+ 40 - 0
packages/native-dom/Cargo.toml

@@ -0,0 +1,40 @@
+[package]
+name = "dioxus-native-dom"
+version = { workspace = true }
+authors = ["Jonathan Kelley", "Nico Burns"]
+edition = "2021"
+description = "Core headless native renderer for Dioxus based on blitz"
+license = "MIT OR Apache-2.0"
+repository = "https://github.com/DioxusLabs/dioxus/"
+homepage = "https://dioxuslabs.com/learn/0.6/getting_started"
+keywords = ["dom", "ui", "gui", "react"]
+
+[features]
+default = ["accessibility", "tracing", "svg", "system-fonts"]
+svg = ["blitz-dom/svg"]
+accessibility = ["blitz-dom/accessibility"]
+tracing = ["dep:tracing", "blitz-dom/tracing"]
+system-fonts = ["blitz-dom/system_fonts"]
+autofocus = []
+
+[dependencies]
+# Blitz dependencies
+blitz-dom = { version = "=0.1.0-alpha.5", default-features = false }
+blitz-traits = { version = "=0.1.0-alpha.5" }
+
+# DioxusLabs dependencies
+dioxus-core = { workspace = true }
+dioxus-html = { workspace = true }
+
+# Windowing & Input
+keyboard-types = { workspace = true }
+
+
+# Other dependencies
+tracing = { workspace = true, optional = true }
+rustc-hash = { workspace = true }
+futures-util = { workspace = true }
+
+[package.metadata.docs.rs]
+all-features = true
+rustdoc-args = ["--cfg", "docsrs"]

+ 11 - 12
packages/native/src/dioxus_document.rs → packages/native-dom/src/dioxus_document.rs

@@ -1,9 +1,10 @@
 //! Integration between Dioxus and Blitz
-use blitz_dom::Attribute;
-use futures_util::{pin_mut, FutureExt};
-use std::ops::{Deref, DerefMut};
-use std::{any::Any, collections::HashMap, rc::Rc, sync::Arc};
+use crate::events::{BlitzKeyboardData, NativeClickData, NativeConverter, NativeFormData};
+use crate::mutation_writer::{DioxusState, MutationWriter};
+use crate::qual_name;
+use crate::NodeId;
 
+use blitz_dom::Attribute;
 use blitz_dom::{
     net::Resource, BaseDocument, Document, EventDriver, EventHandler, Node, DEFAULT_CSS,
 };
@@ -15,11 +16,9 @@ use blitz_traits::{
 
 use dioxus_core::{ElementId, Event, VirtualDom};
 use dioxus_html::{set_event_converter, PlatformEventData};
-
-use crate::events::{BlitzKeyboardData, NativeClickData, NativeConverter, NativeFormData};
-use crate::mutation_writer::{DioxusState, MutationWriter};
-use crate::qual_name;
-use crate::NodeId;
+use futures_util::{pin_mut, FutureExt};
+use std::ops::{Deref, DerefMut};
+use std::{any::Any, collections::HashMap, rc::Rc, sync::Arc};
 
 fn wrap_event_data<T: Any>(value: T) -> Rc<dyn Any> {
     Rc::new(PlatformEventData::new(Box::new(value)))
@@ -36,9 +35,9 @@ fn get_dioxus_id(node: &Node) -> Option<ElementId> {
 }
 
 pub struct DioxusDocument {
-    pub(crate) vdom: VirtualDom,
-    pub(crate) vdom_state: DioxusState,
-    pub(crate) inner: BaseDocument,
+    pub inner: BaseDocument,
+    pub vdom: VirtualDom,
+    pub vdom_state: DioxusState,
 
     #[allow(unused)]
     pub(crate) html_element_id: NodeId,

+ 4 - 4
packages/native/src/events.rs → packages/native-dom/src/events.rs

@@ -6,11 +6,11 @@ use dioxus_html::{
         InteractionElementOffset, InteractionLocation, ModifiersInteraction, PointerInteraction,
     },
     AnimationData, ClipboardData, CompositionData, DragData, FocusData, FormData, FormValue,
-    HasFileData, HasFormData, HasMouseData, HtmlEventConverter, ImageData, KeyboardData, MediaData,
-    MountedData, MouseData, PlatformEventData, PointerData, ResizeData, ScrollData, SelectionData,
-    ToggleData, TouchData, TransitionData, VisibleData, WheelData,
+    HasFileData, HasFocusData, HasFormData, HasKeyboardData, HasMouseData, HtmlEventConverter,
+    ImageData, KeyboardData, MediaData, MountedData, MouseData, PlatformEventData, PointerData,
+    ResizeData, ScrollData, SelectionData, ToggleData, TouchData, TransitionData, VisibleData,
+    WheelData,
 };
-use dioxus_html::{HasFocusData, HasKeyboardData};
 use keyboard_types::{Code, Key, Location, Modifiers};
 use std::any::Any;
 use std::collections::HashMap;

+ 51 - 0
packages/native-dom/src/lib.rs

@@ -0,0 +1,51 @@
+#![cfg_attr(docsrs, feature(doc_cfg))]
+
+//! Core headless native renderer for Dioxus.
+//!
+//! ## Feature flags
+//!  - `default`: Enables the features listed below.
+//!  - `accessibility`: Enables [`accesskit`](https://docs.rs/accesskit/latest/accesskit/) accessibility support.
+//!  - `hot-reload`: Enables hot-reloading of Dioxus RSX.
+//!  - `menu`: Enables the [`muda`](https://docs.rs/muda/latest/muda/) menubar.
+//!  - `tracing`: Enables tracing support.
+
+mod dioxus_document;
+mod events;
+mod mutation_writer;
+pub use dioxus_document::DioxusDocument;
+
+use blitz_dom::{ns, LocalName, Namespace, QualName};
+type NodeId = usize;
+
+pub(crate) fn qual_name(local_name: &str, namespace: Option<&str>) -> QualName {
+    QualName {
+        prefix: None,
+        ns: namespace.map(Namespace::from).unwrap_or(ns!(html)),
+        local: LocalName::from(local_name),
+    }
+}
+
+// Syntax sugar to make tracing calls less noisy in function below
+macro_rules! trace {
+    ($pattern:literal) => {{
+        #[cfg(feature = "tracing")]
+        tracing::info!($pattern);
+    }};
+    ($pattern:literal, $item1:expr) => {{
+        #[cfg(feature = "tracing")]
+        tracing::info!($pattern, $item1);
+    }};
+    ($pattern:literal, $item1:expr, $item2:expr) => {{
+        #[cfg(feature = "tracing")]
+        tracing::info!($pattern, $item1, $item2);
+    }};
+    ($pattern:literal, $item1:expr, $item2:expr, $item3:expr) => {{
+        #[cfg(feature = "tracing")]
+        tracing::info!($pattern, $item1, $item2);
+    }};
+    ($pattern:literal, $item1:expr, $item2:expr, $item3:expr, $item4:expr) => {{
+        #[cfg(feature = "tracing")]
+        tracing::info!($pattern, $item1, $item2, $item3, $item4);
+    }};
+}
+pub(crate) use trace;

+ 0 - 0
packages/native/src/mutation_writer.rs → packages/native-dom/src/mutation_writer.rs


+ 1 - 0
packages/native/Cargo.toml

@@ -32,6 +32,7 @@ blitz-shell = { version = "=0.1.0-alpha.5", default-features = false }
 # DioxusLabs dependencies
 dioxus-core = { workspace = true }
 dioxus-html = { workspace = true }
+dioxus-native-dom = { workspace = true }
 dioxus-asset-resolver = { workspace = true }
 dioxus-cli-config = { workspace = true, optional = true }
 dioxus-devtools = { workspace = true, optional = true }

+ 2 - 7
packages/native/src/dioxus_application.rs

@@ -8,10 +8,7 @@ use winit::event_loop::{ActiveEventLoop, EventLoopProxy};
 use winit::window::WindowId;
 
 use crate::DioxusNativeWindowRenderer;
-use crate::{
-    contexts::DioxusNativeDocument, mutation_writer::MutationWriter, BlitzShellEvent,
-    DioxusDocument, WindowConfig,
-};
+use crate::{contexts::DioxusNativeDocument, BlitzShellEvent, DioxusDocument, WindowConfig};
 
 /// Dioxus-native specific event type
 pub enum DioxusNativeEvent {
@@ -138,9 +135,7 @@ impl ApplicationHandler<BlitzShellEvent> for DioxusNativeApplication {
                 .in_runtime(move || ScopeId::ROOT.provide_context(renderer));
 
             // Queue rebuild
-            let mut writer = MutationWriter::new(&mut doc.inner, &mut doc.vdom_state);
-            doc.vdom.rebuild(&mut writer);
-            drop(writer);
+            doc.initial_build();
 
             // And then request redraw
             window.request_redraw();

+ 3 - 40
packages/native/src/lib.rs

@@ -12,18 +12,16 @@
 mod assets;
 mod contexts;
 mod dioxus_application;
-mod dioxus_document;
 mod dioxus_renderer;
-mod events;
-mod mutation_writer;
+
+#[doc(inline)]
+pub use dioxus_native_dom::*;
 
 pub use anyrender_vello::{
     wgpu_context::DeviceHandle, CustomPaintCtx, CustomPaintSource, TextureHandle,
 };
 use assets::DioxusNativeNetProvider;
-use blitz_dom::{ns, LocalName, Namespace, QualName};
 pub use dioxus_application::{DioxusNativeApplication, DioxusNativeEvent};
-pub use dioxus_document::DioxusDocument;
 pub use dioxus_renderer::{use_wgpu, DioxusNativeWindowRenderer, Features, Limits};
 
 use blitz_shell::{create_default_event_loop, BlitzShellEvent, Config, WindowConfig};
@@ -31,8 +29,6 @@ use dioxus_core::{ComponentFunction, Element, VirtualDom};
 use std::any::Any;
 use winit::window::WindowAttributes;
 
-type NodeId = usize;
-
 /// Launch an interactive HTML/CSS renderer driven by the Dioxus virtualdom
 pub fn launch(app: fn() -> Element) {
     launch_cfg(app, vec![], vec![])
@@ -142,36 +138,3 @@ pub fn launch_cfg_with_props<P: Clone + 'static, M: 'static>(
     // Run event loop
     event_loop.run_app(&mut application).unwrap();
 }
-
-pub(crate) fn qual_name(local_name: &str, namespace: Option<&str>) -> QualName {
-    QualName {
-        prefix: None,
-        ns: namespace.map(Namespace::from).unwrap_or(ns!(html)),
-        local: LocalName::from(local_name),
-    }
-}
-
-// Syntax sugar to make tracing calls less noisy in function below
-macro_rules! trace {
-    ($pattern:literal) => {{
-        #[cfg(feature = "tracing")]
-        tracing::info!($pattern);
-    }};
-    ($pattern:literal, $item1:expr) => {{
-        #[cfg(feature = "tracing")]
-        tracing::info!($pattern, $item1);
-    }};
-    ($pattern:literal, $item1:expr, $item2:expr) => {{
-        #[cfg(feature = "tracing")]
-        tracing::info!($pattern, $item1, $item2);
-    }};
-    ($pattern:literal, $item1:expr, $item2:expr, $item3:expr) => {{
-        #[cfg(feature = "tracing")]
-        tracing::info!($pattern, $item1, $item2);
-    }};
-    ($pattern:literal, $item1:expr, $item2:expr, $item3:expr, $item4:expr) => {{
-        #[cfg(feature = "tracing")]
-        tracing::info!($pattern, $item1, $item2, $item3, $item4);
-    }};
-}
-pub(crate) use trace;

+ 30 - 0
packages/playwright-tests/fullstack-routing.spec.js

@@ -51,3 +51,33 @@ test("unknown route", async ({ request }) => {
 
   expect(response.status()).toBe(404);
 });
+
+// Clicking the link on the home page should navigate to the blog route
+test("click blog link", async ({ page }) => {
+  await page.goto("http://localhost:8888");
+
+  // Click the link to the blog route
+  await page.locator('a').click();
+
+  // Wait for navigation to complete
+  await page.waitForURL("http://localhost:8888/blog/1/");
+
+  // Check that the blog page is displayed
+  const text = await page.textContent("body");
+  expect(text).toContain("id: 1");
+});
+
+// Clicking the link on the blog page should navigate back to the home route
+test("click home link from blog", async ({ page }) => {
+  await page.goto("http://localhost:8888/blog/1");
+
+  // Click the link to the home route
+  await page.locator('a').click();
+
+  // Wait for navigation to complete
+  await page.waitForURL("http://localhost:8888");
+
+  // Check that the home page is displayed
+  const text = await page.textContent("body");
+  expect(text).toContain("Home");
+});

+ 7 - 0
packages/playwright-tests/fullstack-routing/src/main.rs

@@ -36,6 +36,9 @@ enum Route {
 
 #[component]
 fn Blog(id: i32) -> Element {
+    let route: Route = use_route();
+    assert_eq!(route, Route::Blog { id });
+
     rsx! {
         Link { to: Route::Home {}, "Go home" }
         "id: {id}"
@@ -51,8 +54,12 @@ fn ThrowsError() -> Element {
 
 #[component]
 fn Home() -> Element {
+    let route: Route = use_route();
+    assert_eq!(route, Route::Home);
+
     rsx! {
         "Home"
+        Link { to: Route::Blog { id: 1 }, "Go to blog 1" }
     }
 }