Merge branch 'master' into jk/router2dotoh
This commit is contained in:
commit
59f8b49fb6
26 files changed
+324
-322
No files matched your search
@@ -14,6 +14,7 @@ jobs:
|
|||||||
toolchain: stable
|
toolchain: stable
|
||||||
override: true
|
override: true
|
||||||
- uses: Swatinem/rust-cache@v1
|
- uses: Swatinem/rust-cache@v1
|
||||||
|
- run: sudo apt install libwebkit2gtk-4.0-dev libappindicator3-dev libgtk-3-dev libappindicator3-dev
|
||||||
- uses: actions-rs/cargo@v1
|
- uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
command: check
|
command: check
|
||||||
@@ -29,9 +30,11 @@ jobs:
|
|||||||
toolchain: stable
|
toolchain: stable
|
||||||
override: true
|
override: true
|
||||||
- uses: Swatinem/rust-cache@v1
|
- uses: Swatinem/rust-cache@v1
|
||||||
|
- run: sudo apt install libwebkit2gtk-4.0-dev libappindicator3-dev libgtk-3-dev libappindicator3-dev
|
||||||
- uses: actions-rs/cargo@v1
|
- uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
command: test
|
command: test
|
||||||
|
args: --features "desktop, ssr, router"
|
||||||
|
|
||||||
fmt:
|
fmt:
|
||||||
name: Rustfmt
|
name: Rustfmt
|
||||||
@@ -66,3 +69,5 @@ jobs:
|
|||||||
# with:
|
# with:
|
||||||
# command: clippy
|
# command: clippy
|
||||||
# args: -- -D warnings
|
# args: -- -D warnings
|
||||||
|
#
|
||||||
|
# sudo apt install libwebkit2gtk-4.0-dev libappindicator3-dev libgtk-3-dev libappindicator3-dev
|
||||||
@@ -19,52 +19,50 @@ jobs:
|
|||||||
max-parallel: 2
|
max-parallel: 2
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
target: [
|
target:
|
||||||
i686-pc-windows-gnu,
|
[
|
||||||
i686-pc-windows-msvc,
|
i686-pc-windows-gnu,
|
||||||
x86_64-pc-windows-gnu,
|
i686-pc-windows-msvc,
|
||||||
x86_64-pc-windows-msvc,
|
x86_64-pc-windows-gnu,
|
||||||
]
|
x86_64-pc-windows-msvc,
|
||||||
|
]
|
||||||
cfg_release_channel: [nightly, stable]
|
cfg_release_channel: [nightly, stable]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
# The Windows runners have autocrlf enabled by default
|
# The Windows runners have autocrlf enabled by default
|
||||||
# which causes failures for some of rustfmt's line-ending sensitive tests
|
# which causes failures for some of rustfmt's line-ending sensitive tests
|
||||||
- name: disable git eol translation
|
- name: disable git eol translation
|
||||||
run: git config --global core.autocrlf false
|
run: git config --global core.autocrlf false
|
||||||
- name: checkout
|
- name: checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
# Run build
|
# Run build
|
||||||
- name: Install Rustup using win.rustup.rs
|
- name: Install Rustup using win.rustup.rs
|
||||||
run: |
|
run: |
|
||||||
# Disable the download progress bar which can cause perf issues
|
# Disable the download progress bar which can cause perf issues
|
||||||
$ProgressPreference = "SilentlyContinue"
|
$ProgressPreference = "SilentlyContinue"
|
||||||
Invoke-WebRequest https://win.rustup.rs/ -OutFile rustup-init.exe
|
Invoke-WebRequest https://win.rustup.rs/ -OutFile rustup-init.exe
|
||||||
.\rustup-init.exe -y --default-host=x86_64-pc-windows-msvc --default-toolchain=none
|
.\rustup-init.exe -y --default-host=x86_64-pc-windows-msvc --default-toolchain=none
|
||||||
del rustup-init.exe
|
del rustup-init.exe
|
||||||
rustup target add ${{ matrix.target }}
|
rustup target add ${{ matrix.target }}
|
||||||
shell: powershell
|
shell: powershell
|
||||||
|
|
||||||
- name: Add mingw32 to path for i686-gnu
|
- name: Add mingw32 to path for i686-gnu
|
||||||
run: |
|
run: |
|
||||||
echo "C:\msys64\mingw32\bin" >> $GITHUB_PATH
|
echo "C:\msys64\mingw32\bin" >> $GITHUB_PATH
|
||||||
if: matrix.target == 'i686-pc-windows-gnu' && matrix.channel == 'nightly'
|
if: matrix.target == 'i686-pc-windows-gnu' && matrix.channel == 'nightly'
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Add mingw64 to path for x86_64-gnu
|
- name: Add mingw64 to path for x86_64-gnu
|
||||||
run: echo "C:\msys64\mingw64\bin" >> $GITHUB_PATH
|
run: echo "C:\msys64\mingw64\bin" >> $GITHUB_PATH
|
||||||
if: matrix.target == 'x86_64-pc-windows-gnu' && matrix.channel == 'nightly'
|
if: matrix.target == 'x86_64-pc-windows-gnu' && matrix.channel == 'nightly'
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: build
|
- name: test
|
||||||
run: |
|
run: |
|
||||||
rustc -Vv
|
rustc -Vv
|
||||||
cargo -V
|
cargo -V
|
||||||
set RUST_BACKTRACE=1
|
set RUST_BACKTRACE=1
|
||||||
cargo build
|
cargo build --features "desktop, ssr, router"
|
||||||
shell: cmd
|
cargo test --features "desktop, ssr, router"
|
||||||
|
shell: cmd
|
||||||
- name: test
|
|
||||||
run: cargo test
|
|
||||||
shell: cmd
|
|
||||||
@@ -38,9 +38,11 @@ $ cargo add dioxus --features web
|
|||||||
Add an `index.html` for Trunk to use. Make sure your "mount point" element has an ID of "main" (this can be configured later):
|
Add an `index.html` for Trunk to use. Make sure your "mount point" element has an ID of "main" (this can be configured later):
|
||||||
|
|
||||||
```html
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta content="text/html;charset=utf-8" http-equiv="Content-Type" name="viewport" content="width=device-width, initial-scale=1.0" charset="UTF-8">
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="main"> </div>
|
<div id="main"> </div>
|
||||||
|
|||||||
@@ -89,8 +89,8 @@ fn app(cx: Scope) -> Element {
|
|||||||
|
|
||||||
#[derive(Props)]
|
#[derive(Props)]
|
||||||
struct ActionButtonProps<'a> {
|
struct ActionButtonProps<'a> {
|
||||||
name: &'static str,
|
name: &'a str,
|
||||||
id: &'static str,
|
id: &'a str,
|
||||||
onclick: &'a dyn Fn(),
|
onclick: &'a dyn Fn(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -133,7 +133,7 @@ fn app(cx: Scope) -> Element {
|
|||||||
r#type: "{field}",
|
r#type: "{field}",
|
||||||
value: "{value}",
|
value: "{value}",
|
||||||
// checked: "false",
|
// checked: "false",
|
||||||
oninput: move |evt: Arc<FormEvent>| {
|
oninput: move |evt: FormEvent| {
|
||||||
println!("{:?}", evt);
|
println!("{:?}", evt);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,5 +35,5 @@ fn main() {
|
|||||||
|
|
||||||
let app: Component = |cx| cx.render(rsx!(div { "some app" }));
|
let app: Component = |cx| cx.render(rsx!(div { "some app" }));
|
||||||
|
|
||||||
dioxus_desktop::launch_core(app, (), |c| c.with_edits(edits));
|
dioxus_desktop::launch_with_props(app, (), |c| c.with_edits(edits));
|
||||||
}
|
}
|
||||||
@@ -116,8 +116,8 @@ fn app(cx: Scope) -> Element {
|
|||||||
|
|
||||||
#[derive(Props)]
|
#[derive(Props)]
|
||||||
struct CalculatorKeyProps<'a> {
|
struct CalculatorKeyProps<'a> {
|
||||||
name: &'static str,
|
name: &'a str,
|
||||||
onclick: &'a dyn Fn(Arc<MouseEvent>),
|
onclick: &'a dyn Fn(MouseEvent),
|
||||||
children: Element<'a>,
|
children: Element<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,7 +211,7 @@ impl Calculator {
|
|||||||
self.cur_val = self.display_value.parse::<f64>().unwrap();
|
self.cur_val = self.display_value.parse::<f64>().unwrap();
|
||||||
self.waiting_for_operand = true;
|
self.waiting_for_operand = true;
|
||||||
}
|
}
|
||||||
fn handle_keydown(&mut self, evt: Arc<KeyboardEvent>) {
|
fn handle_keydown(&mut self, evt: KeyboardEvent) {
|
||||||
match evt.key_code {
|
match evt.key_code {
|
||||||
KeyCode::Backspace => self.backspace(),
|
KeyCode::Backspace => self.backspace(),
|
||||||
KeyCode::Num0 => self.input_digit(0),
|
KeyCode::Num0 => self.input_digit(0),
|
||||||
|
|||||||
+40
-51
@@ -1,64 +1,53 @@
|
|||||||
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use dioxus_router::{use_router, Link};
|
use dioxus::router::{Link, Route, Router};
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Clone)]
|
fn main() {
|
||||||
pub enum Route {
|
dioxus::desktop::launch(app);
|
||||||
// #[at("/")]
|
|
||||||
Home,
|
|
||||||
|
|
||||||
// #[at("/:id")]
|
|
||||||
AllUsers { page: u32 },
|
|
||||||
|
|
||||||
// #[at("/:id")]
|
|
||||||
User { id: u32 },
|
|
||||||
|
|
||||||
// #[at("/:id")]
|
|
||||||
BlogList { page: u32 },
|
|
||||||
|
|
||||||
// #[at("/:id")]
|
|
||||||
BlogPost { post_id: u32 },
|
|
||||||
|
|
||||||
// #[at("/404")]
|
|
||||||
// #[not_found]
|
|
||||||
NotFound,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn app(cx: Scope) -> Element {
|
fn app(cx: Scope) -> Element {
|
||||||
let route = use_router(&cx, Route::parse);
|
|
||||||
|
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
nav {
|
Router {
|
||||||
Link { to: Route::Home, "Go home!" }
|
ul {
|
||||||
Link { to: Route::AllUsers { page: 0 }, "List all users" }
|
Link { to: "/", li { "Go home!" } }
|
||||||
Link { to: Route::BlogList { page: 0 }, "Blog posts" }
|
Link { to: "users", li { "List all users" } }
|
||||||
|
Link { to: "blog", li { "Blog posts" } }
|
||||||
|
}
|
||||||
|
Route { to: "/", "Home" }
|
||||||
|
Route { to: "users",
|
||||||
|
Route { to: "/", "User list" }
|
||||||
|
Route { to: ":name", BlogPost {} }
|
||||||
|
}
|
||||||
|
Route { to: "blog"
|
||||||
|
Route { to: "/", "Blog list" }
|
||||||
|
Route { to: ":post", BlogPost {} }
|
||||||
|
}
|
||||||
|
Route { to: "", "Err 404 Route Not Found" }
|
||||||
}
|
}
|
||||||
match route {
|
|
||||||
Route::Home => rsx!("Home"),
|
|
||||||
Route::AllUsers { page } => rsx!("All users - page {page}"),
|
|
||||||
Route::User { id } => rsx!("User - id: {id}"),
|
|
||||||
Route::BlogList { page } => rsx!("Blog posts - page {page}"),
|
|
||||||
Route::BlogPost { post_id } => rsx!("Blog post - post {post_id}"),
|
|
||||||
Route::NotFound => rsx!("Not found"),
|
|
||||||
}
|
|
||||||
footer {}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Route {
|
fn BlogPost(cx: Scope) -> Element {
|
||||||
// Generate the appropriate route from the "tail" end of the URL
|
let post = dioxus::router::use_route(&cx).last_segment()?;
|
||||||
fn parse(url: &str) -> Self {
|
|
||||||
use Route::*;
|
cx.render(rsx! {
|
||||||
match url {
|
div {
|
||||||
"/" => Home,
|
h1 { "Reading blog post: {post}" }
|
||||||
"/users" => AllUsers { page: 1 },
|
p { "example blog post" }
|
||||||
"/users/:page" => AllUsers { page: 1 },
|
|
||||||
"/users/:page/:id" => User { id: 1 },
|
|
||||||
"/blog" => BlogList { page: 1 },
|
|
||||||
"/blog/:page" => BlogList { page: 1 },
|
|
||||||
"/blog/:page/:id" => BlogPost { post_id: 1 },
|
|
||||||
_ => NotFound,
|
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn User(cx: Scope) -> Element {
|
||||||
|
let post = dioxus::router::use_route(&cx).last_segment()?;
|
||||||
|
let bold = dioxus::router::use_route(&cx).param::<bool>("bold");
|
||||||
|
|
||||||
|
cx.render(rsx! {
|
||||||
|
div {
|
||||||
|
h1 { "Reading blog post: {post}" }
|
||||||
|
p { "example blog post" }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
+1
-10
@@ -9,16 +9,7 @@
|
|||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
dioxus::desktop::launch_cfg(app, |c| {
|
dioxus::desktop::launch(app);
|
||||||
// we can configure our primary window through the Tauri Config
|
|
||||||
use dioxus::desktop::tao::platform::macos::*;
|
|
||||||
c.with_window(|w| {
|
|
||||||
w.with_fullsize_content_view(true)
|
|
||||||
.with_titlebar_buttons_hidden(false)
|
|
||||||
.with_titlebar_transparent(true)
|
|
||||||
.with_movable_by_window_background(true)
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn app(cx: Scope) -> Element {
|
pub fn app(cx: Scope) -> Element {
|
||||||
|
|||||||
@@ -187,8 +187,12 @@ impl Parse for ComponentField {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if input.peek(LitStr) && input.peek2(Token![,]) {
|
if input.peek(LitStr) && input.peek2(Token![,]) {
|
||||||
let content = ContentField::Formatted(input.parse()?);
|
let t: LitStr = input.fork().parse()?;
|
||||||
return Ok(Self { name, content });
|
|
||||||
|
if is_literal_foramtted(&t) {
|
||||||
|
let content = ContentField::Formatted(input.parse()?);
|
||||||
|
return Ok(Self { name, content });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if input.peek(LitStr) && input.peek2(LitStr) {
|
if input.peek(LitStr) && input.peek2(LitStr) {
|
||||||
@@ -209,3 +213,19 @@ impl ToTokens for ComponentField {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_literal_foramtted(lit: &LitStr) -> bool {
|
||||||
|
let s = lit.value();
|
||||||
|
let mut chars = s.chars();
|
||||||
|
|
||||||
|
while let Some(next) = chars.next() {
|
||||||
|
if next == '{' {
|
||||||
|
let nen = chars.next();
|
||||||
|
if nen == Some('{') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
//! This is all kinda WIP, but the bones are there.
|
//! This is all kinda WIP, but the bones are there.
|
||||||
|
|
||||||
use crate::{ElementId, ScopeId};
|
use crate::{ElementId, ScopeId};
|
||||||
use std::{any::Any, cell::Cell, rc::Rc, sync::Arc};
|
use std::{any::Any, cell::Cell, fmt::Debug, rc::Rc, sync::Arc};
|
||||||
|
|
||||||
pub(crate) struct BubbleState {
|
pub(crate) struct BubbleState {
|
||||||
pub canceled: Cell<bool>,
|
pub canceled: Cell<bool>,
|
||||||
@@ -143,6 +143,12 @@ pub struct UiEvent<T> {
|
|||||||
bubble_state: Rc<BubbleState>,
|
bubble_state: Rc<BubbleState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Debug> std::fmt::Debug for UiEvent<T> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("UiEvent").field("data", &self.data).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> std::ops::Deref for UiEvent<T> {
|
impl<T> std::ops::Deref for UiEvent<T> {
|
||||||
type Target = T;
|
type Target = T;
|
||||||
|
|
||||||
|
|||||||
@@ -690,8 +690,6 @@ impl ScopeState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Pushes the future onto the poll queue to be polled after the component renders.
|
/// Pushes the future onto the poll queue to be polled after the component renders.
|
||||||
///
|
|
||||||
/// The future is forcibly dropped if the component is not ready by the next render
|
|
||||||
pub fn push_future(&self, fut: impl Future<Output = ()> + 'static) -> TaskId {
|
pub fn push_future(&self, fut: impl Future<Output = ()> + 'static) -> TaskId {
|
||||||
// wake up the scheduler if it is sleeping
|
// wake up the scheduler if it is sleeping
|
||||||
self.tasks
|
self.tasks
|
||||||
|
|||||||
@@ -66,7 +66,6 @@ use tao::{
|
|||||||
event::{Event, StartCause, WindowEvent},
|
event::{Event, StartCause, WindowEvent},
|
||||||
event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget},
|
event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget},
|
||||||
keyboard::{KeyCode, ModifiersState},
|
keyboard::{KeyCode, ModifiersState},
|
||||||
menu::{MenuBar, MenuItem},
|
|
||||||
window::{Window, WindowId},
|
window::{Window, WindowId},
|
||||||
};
|
};
|
||||||
pub use wry;
|
pub use wry;
|
||||||
@@ -293,7 +292,7 @@ impl DesktopController {
|
|||||||
match req.method.as_str() {
|
match req.method.as_str() {
|
||||||
"user_event" => {
|
"user_event" => {
|
||||||
let event = events::trigger_from_serialized(req.params.unwrap());
|
let event = events::trigger_from_serialized(req.params.unwrap());
|
||||||
log::debug!("User event: {:?}", event);
|
log::trace!("User event: {:?}", event);
|
||||||
sender.unbounded_send(SchedulerMsg::Event(event)).unwrap();
|
sender.unbounded_send(SchedulerMsg::Event(event)).unwrap();
|
||||||
}
|
}
|
||||||
"initialize" => {
|
"initialize" => {
|
||||||
|
|||||||
@@ -59,7 +59,11 @@ impl<'a, T: 'static> UseState<'a, T> {
|
|||||||
|
|
||||||
pub fn setter(&self) -> Rc<dyn Fn(T)> {
|
pub fn setter(&self) -> Rc<dyn Fn(T)> {
|
||||||
let slot = self.0.wip.clone();
|
let slot = self.0.wip.clone();
|
||||||
Rc::new(move |new| *slot.borrow_mut() = Some(new))
|
let callback = self.0.update_callback.clone();
|
||||||
|
Rc::new(move |new| {
|
||||||
|
callback();
|
||||||
|
*slot.borrow_mut() = Some(new)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wtih(&self, f: impl FnOnce(&mut T)) {
|
pub fn wtih(&self, f: impl FnOnce(&mut T)) {
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ pub struct LinkProps<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn Link<'a>(cx: Scope<'a, LinkProps<'a>>) -> Element {
|
pub fn Link<'a>(cx: Scope<'a, LinkProps<'a>>) -> Element {
|
||||||
let service = cx.consume_context::<RouterService>().unwrap();
|
let service = cx.consume_context::<RouterService>()?;
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
a {
|
a {
|
||||||
href: "{cx.props.to}",
|
href: "{cx.props.to}",
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ pub fn Route<'a>(cx: Scope<'a, RouteProps<'a>>) -> Element {
|
|||||||
Some(RouteInner {})
|
Some(RouteInner {})
|
||||||
});
|
});
|
||||||
|
|
||||||
log::debug!("Checking route {}", cx.props.to);
|
log::trace!("Checking route {}", cx.props.to);
|
||||||
|
|
||||||
if router_root.should_render(cx.scope_id()) {
|
if router_root.should_render(cx.scope_id()) {
|
||||||
cx.render(rsx!(&cx.props.children))
|
cx.render(rsx!(&cx.props.children))
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ impl<'a> UseRoute<'a> {
|
|||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn last_segment(&self) -> Option<&str> {
|
pub fn last_segment(&self) -> Option<&'a str> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,6 +25,6 @@ impl<'a> UseRoute<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn use_route(cx: &ScopeState) -> UseRoute {
|
pub fn use_route<'a>(cx: &'a ScopeState) -> UseRoute<'a> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
@@ -47,7 +47,7 @@ impl RouterService {
|
|||||||
_root_found.set(false);
|
_root_found.set(false);
|
||||||
// checking if the route is valid is cheap, so we do it
|
// checking if the route is valid is cheap, so we do it
|
||||||
for (slot, _) in _slots.borrow_mut().iter().rev() {
|
for (slot, _) in _slots.borrow_mut().iter().rev() {
|
||||||
log::debug!("regenerating slot {:?}", slot);
|
log::trace!("regenerating slot {:?}", slot);
|
||||||
regen(*slot);
|
regen(*slot);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ dioxus-core = { path = "../core", version = "^0.1.6", features = ["serialize"] }
|
|||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
dioxus-hooks = { path = "../hooks" }
|
dioxus-hooks = { path = "../hooks" }
|
||||||
dioxus-html = { path = "../html" }
|
dioxus-html = { path = "../html" }
|
||||||
|
dioxus-core-macro = { path = "../core-macro" }
|
||||||
thiserror = "1.0.23"
|
thiserror = "1.0.23"
|
||||||
log = "0.4.13"
|
log = "0.4.13"
|
||||||
fern = { version = "0.6.0", features = ["colored"] }
|
fern = { version = "0.6.0", features = ["colored"] }
|
||||||
@@ -27,4 +28,3 @@ argh = "0.1.4"
|
|||||||
serde = "1.0.120"
|
serde = "1.0.120"
|
||||||
serde_json = "1.0.61"
|
serde_json = "1.0.61"
|
||||||
fs_extra = "1.2.0"
|
fs_extra = "1.2.0"
|
||||||
dioxus-core-macro = { path = "../core-macro" }
|
|
||||||
@@ -17,7 +17,7 @@ This crate is a part of the broader Dioxus ecosystem. For more resources about D
|
|||||||
|
|
||||||
Dioxus SSR provides utilities to render Dioxus components to valid HTML. Once rendered, the HTML can be rehydrated client side or served from your web-server of choice.
|
Dioxus SSR provides utilities to render Dioxus components to valid HTML. Once rendered, the HTML can be rehydrated client side or served from your web-server of choice.
|
||||||
|
|
||||||
```rust
|
```rust, ignore
|
||||||
let app: Component = |cx| cx.render(rsx!(div {"hello world!"}));
|
let app: Component = |cx| cx.render(rsx!(div {"hello world!"}));
|
||||||
|
|
||||||
let mut vdom = VirtualDom::new(app);
|
let mut vdom = VirtualDom::new(app);
|
||||||
@@ -32,7 +32,7 @@ assert_eq!(text, "<div>hello world!</div>")
|
|||||||
|
|
||||||
The simplest example is to simply render some `rsx!` nodes to html. This can be done with the [`render_lazy`] api.
|
The simplest example is to simply render some `rsx!` nodes to html. This can be done with the [`render_lazy`] api.
|
||||||
|
|
||||||
```rust
|
```rust, ignore
|
||||||
let content = dioxus::ssr::render(rsx!{
|
let content = dioxus::ssr::render(rsx!{
|
||||||
div {
|
div {
|
||||||
(0..5).map(|i| rsx!(
|
(0..5).map(|i| rsx!(
|
||||||
@@ -44,7 +44,7 @@ let content = dioxus::ssr::render(rsx!{
|
|||||||
|
|
||||||
## Rendering a VirtualDom
|
## Rendering a VirtualDom
|
||||||
|
|
||||||
```rust
|
```rust, ignore
|
||||||
let mut dom = VirtualDom::new(app);
|
let mut dom = VirtualDom::new(app);
|
||||||
let _ = dom.rebuild();
|
let _ = dom.rebuild();
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ let content = dioxus::ssr::render_vdom(&dom);
|
|||||||
## Configuring output
|
## Configuring output
|
||||||
It's possible to configure the output of the generated HTML.
|
It's possible to configure the output of the generated HTML.
|
||||||
|
|
||||||
```rust
|
```rust, ignore
|
||||||
let content = dioxus::ssr::render_vdom(&dom, |config| config.pretty(true).prerender(true));
|
let content = dioxus::ssr::render_vdom(&dom, |config| config.pretty(true).prerender(true));
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ let content = dioxus::ssr::render_vdom(&dom, |config| config.pretty(true).preren
|
|||||||
|
|
||||||
We provide the basic `SsrFormatter` object that implements `Display`, so you can integrate SSR into an existing string, or write directly to a file.
|
We provide the basic `SsrFormatter` object that implements `Display`, so you can integrate SSR into an existing string, or write directly to a file.
|
||||||
|
|
||||||
```rust
|
```rust, ignore
|
||||||
use std::fmt::{Error, Write};
|
use std::fmt::{Error, Write};
|
||||||
|
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
@@ -92,7 +92,7 @@ With pre-rendering enabled, this crate will generate element nodes with Element
|
|||||||
|
|
||||||
To enable pre-rendering, simply configure the `SsrConfig` with pre-rendering enabled.
|
To enable pre-rendering, simply configure the `SsrConfig` with pre-rendering enabled.
|
||||||
|
|
||||||
```rust
|
```rust, ignore
|
||||||
let dom = VirtualDom::new(App);
|
let dom = VirtualDom::new(App);
|
||||||
|
|
||||||
let text = dioxus::ssr::render_vdom(App, |cfg| cfg.pre_render(true));
|
let text = dioxus::ssr::render_vdom(App, |cfg| cfg.pre_render(true));
|
||||||
@@ -102,7 +102,7 @@ let text = dioxus::ssr::render_vdom(App, |cfg| cfg.pre_render(true));
|
|||||||
|
|
||||||
Dioxus SSR can also be to render on the server. Obviously, you can just render the VirtualDOM to a string and send that down.
|
Dioxus SSR can also be to render on the server. Obviously, you can just render the VirtualDOM to a string and send that down.
|
||||||
|
|
||||||
```rust
|
```rust, ignore
|
||||||
let text = dioxus::ssr::render_vdom(&vdom);
|
let text = dioxus::ssr::render_vdom(&vdom);
|
||||||
assert_eq!(text, "<div>hello world!</div>")
|
assert_eq!(text, "<div>hello world!</div>")
|
||||||
```
|
```
|
||||||
@@ -113,7 +113,7 @@ The rest of the space - IE doing this more efficiently, caching the virtualdom,
|
|||||||
|
|
||||||
Dioxus SSR needs an arena to allocate from - whether it be the VirtualDom or a dedicated Bump allocator. To render `rsx!` directly to a string, you'll want to create an `SsrRenderer` and call `render_lazy`.
|
Dioxus SSR needs an arena to allocate from - whether it be the VirtualDom or a dedicated Bump allocator. To render `rsx!` directly to a string, you'll want to create an `SsrRenderer` and call `render_lazy`.
|
||||||
|
|
||||||
```rust
|
```rust, ignore
|
||||||
let text = dioxus::ssr::SsrRenderer::new().render_lazy(rsx!{
|
let text = dioxus::ssr::SsrRenderer::new().render_lazy(rsx!{
|
||||||
div { "hello world" }
|
div { "hello world" }
|
||||||
});
|
});
|
||||||
@@ -122,7 +122,7 @@ assert_eq!(text, "<div>hello world!</div>")
|
|||||||
|
|
||||||
This can be automated with the `render_lazy!` macro:
|
This can be automated with the `render_lazy!` macro:
|
||||||
|
|
||||||
```rust
|
```rust, ignore
|
||||||
let text = render_lazy!(rsx!( div { "hello world" } ));
|
let text = render_lazy!(rsx!( div { "hello world" } ));
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
asd
|
<div title="About W3Schools"><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 0</p></div><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 1</p></div><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 2</p></div><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 3</p></div><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 4</p></div><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 5</p></div><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 6</p></div><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 7</p></div><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 8</p></div><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 9</p></div><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 10</p></div><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 11</p></div><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 12</p></div><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 13</p></div><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 14</p></div><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 15</p></div><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 16</p></div><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 17</p></div><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 18</p></div><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 19</p></div></div>
|
||||||
+12
-168
@@ -2,13 +2,15 @@
|
|||||||
|
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
|
|
||||||
use dioxus_core::exports::bumpalo;
|
|
||||||
use dioxus_core::exports::bumpalo::Bump;
|
|
||||||
use dioxus_core::IntoVNode;
|
use dioxus_core::IntoVNode;
|
||||||
use dioxus_core::*;
|
use dioxus_core::*;
|
||||||
|
|
||||||
|
fn app(_cx: Scope) -> Element {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
pub struct SsrRenderer {
|
pub struct SsrRenderer {
|
||||||
inner: bumpalo::Bump,
|
vdom: VirtualDom,
|
||||||
cfg: SsrConfig,
|
cfg: SsrConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -16,21 +18,14 @@ impl SsrRenderer {
|
|||||||
pub fn new(cfg: impl FnOnce(SsrConfig) -> SsrConfig) -> Self {
|
pub fn new(cfg: impl FnOnce(SsrConfig) -> SsrConfig) -> Self {
|
||||||
Self {
|
Self {
|
||||||
cfg: cfg(SsrConfig::default()),
|
cfg: cfg(SsrConfig::default()),
|
||||||
inner: bumpalo::Bump::new(),
|
vdom: VirtualDom::new(app),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_lazy<'a>(&'a mut self, f: LazyNodes<'a, '_>) -> String {
|
pub fn render_lazy<'a>(&'a mut self, f: LazyNodes<'a, '_>) -> String {
|
||||||
let bump = &mut self.inner as *mut _;
|
let scope = self.vdom.base_scope();
|
||||||
let s = self.render_inner(f);
|
let factory = NodeFactory::new(&scope);
|
||||||
|
|
||||||
// reuse the bump's memory
|
|
||||||
unsafe { (&mut *bump as &mut bumpalo::Bump).reset() };
|
|
||||||
s
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_inner<'a>(&'a self, f: LazyNodes<'a, '_>) -> String {
|
|
||||||
let factory = NodeFactory::new(&self.inner);
|
|
||||||
let root = f.into_vnode(factory);
|
let root = f.into_vnode(factory);
|
||||||
format!(
|
format!(
|
||||||
"{:}",
|
"{:}",
|
||||||
@@ -44,8 +39,8 @@ impl SsrRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_lazy<'a>(f: LazyNodes<'a, '_>) -> String {
|
pub fn render_lazy<'a>(f: LazyNodes<'a, '_>) -> String {
|
||||||
let bump = bumpalo::Bump::new();
|
let vdom = VirtualDom::new(app);
|
||||||
let borrowed = ≎
|
let scope: *const ScopeState = vdom.base_scope();
|
||||||
|
|
||||||
// Safety
|
// Safety
|
||||||
//
|
//
|
||||||
@@ -57,9 +52,9 @@ pub fn render_lazy<'a>(f: LazyNodes<'a, '_>) -> String {
|
|||||||
//
|
//
|
||||||
// Therefore, we cast our local bump alloactor into right lifetime. This is okay because our usage of the bump arena
|
// Therefore, we cast our local bump alloactor into right lifetime. This is okay because our usage of the bump arena
|
||||||
// is *definitely* shorter than the <'a> lifetime, and we return *owned* data - not borrowed data.
|
// is *definitely* shorter than the <'a> lifetime, and we return *owned* data - not borrowed data.
|
||||||
|
let scope = unsafe { &*scope };
|
||||||
|
|
||||||
let _b = unsafe { std::mem::transmute::<&Bump, &'a Bump>(borrowed) };
|
let root = f.into_vnode(NodeFactory::new(&scope));
|
||||||
let root = f.into_vnode(NodeFactory::new(_b));
|
|
||||||
|
|
||||||
format!(
|
format!(
|
||||||
"{:}",
|
"{:}",
|
||||||
@@ -282,154 +277,3 @@ impl SsrConfig {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
use dioxus_core::prelude::*;
|
|
||||||
use dioxus_core_macro::*;
|
|
||||||
use dioxus_html as dioxus_elements;
|
|
||||||
|
|
||||||
static SIMPLE_APP: Component = |cx| {
|
|
||||||
cx.render(rsx!(div {
|
|
||||||
"hello world!"
|
|
||||||
}))
|
|
||||||
};
|
|
||||||
|
|
||||||
static SLIGHTLY_MORE_COMPLEX: Component = |cx| {
|
|
||||||
cx.render(rsx! {
|
|
||||||
div { title: "About W3Schools",
|
|
||||||
(0..20).map(|f| rsx!{
|
|
||||||
div {
|
|
||||||
title: "About W3Schools",
|
|
||||||
style: "color:blue;text-align:center",
|
|
||||||
class: "About W3Schools",
|
|
||||||
p {
|
|
||||||
title: "About W3Schools",
|
|
||||||
"Hello world!: {f}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
static NESTED_APP: Component = |cx| {
|
|
||||||
cx.render(rsx!(
|
|
||||||
div {
|
|
||||||
SIMPLE_APP {}
|
|
||||||
}
|
|
||||||
))
|
|
||||||
};
|
|
||||||
static FRAGMENT_APP: Component = |cx| {
|
|
||||||
cx.render(rsx!(
|
|
||||||
div { "f1" }
|
|
||||||
div { "f2" }
|
|
||||||
div { "f3" }
|
|
||||||
div { "f4" }
|
|
||||||
))
|
|
||||||
};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn to_string_works() {
|
|
||||||
let mut dom = VirtualDom::new(SIMPLE_APP);
|
|
||||||
dom.rebuild();
|
|
||||||
dbg!(render_vdom(&dom));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hydration() {
|
|
||||||
let mut dom = VirtualDom::new(NESTED_APP);
|
|
||||||
dom.rebuild();
|
|
||||||
dbg!(render_vdom_cfg(&dom, |c| c.pre_render(true)));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn nested() {
|
|
||||||
let mut dom = VirtualDom::new(NESTED_APP);
|
|
||||||
dom.rebuild();
|
|
||||||
dbg!(render_vdom(&dom));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn fragment_app() {
|
|
||||||
let mut dom = VirtualDom::new(FRAGMENT_APP);
|
|
||||||
dom.rebuild();
|
|
||||||
dbg!(render_vdom(&dom));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn write_to_file() {
|
|
||||||
use std::fs::File;
|
|
||||||
use std::io::Write;
|
|
||||||
|
|
||||||
let mut file = File::create("index.html").unwrap();
|
|
||||||
|
|
||||||
let mut dom = VirtualDom::new(SLIGHTLY_MORE_COMPLEX);
|
|
||||||
dom.rebuild();
|
|
||||||
|
|
||||||
file.write_fmt(format_args!(
|
|
||||||
"{}",
|
|
||||||
TextRenderer::from_vdom(&dom, SsrConfig::default())
|
|
||||||
))
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn styles() {
|
|
||||||
static STLYE_APP: Component = |cx| {
|
|
||||||
cx.render(rsx! {
|
|
||||||
div { color: "blue", font_size: "46px" }
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut dom = VirtualDom::new(STLYE_APP);
|
|
||||||
dom.rebuild();
|
|
||||||
dbg!(render_vdom(&dom));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn lazy() {
|
|
||||||
let p1 = SsrRenderer::new(|c| c).render_lazy(rsx! {
|
|
||||||
div { "ello" }
|
|
||||||
});
|
|
||||||
|
|
||||||
let p2 = render_lazy(rsx! {
|
|
||||||
div {
|
|
||||||
"ello"
|
|
||||||
}
|
|
||||||
});
|
|
||||||
assert_eq!(p1, p2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn big_lazy() {
|
|
||||||
let s = render_lazy(rsx! {
|
|
||||||
div {
|
|
||||||
div {
|
|
||||||
div {
|
|
||||||
h1 { "ello world" }
|
|
||||||
h1 { "ello world" }
|
|
||||||
h1 { "ello world" }
|
|
||||||
h1 { "ello world" }
|
|
||||||
h1 { "ello world" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
dbg!(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn inner_html() {
|
|
||||||
let s = render_lazy(rsx! {
|
|
||||||
div {
|
|
||||||
dangerous_inner_html: "<div> ack </div>"
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
dbg!(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
use dioxus_core::prelude::*;
|
||||||
|
use dioxus_core_macro::*;
|
||||||
|
use dioxus_html as dioxus_elements;
|
||||||
|
use dioxus_ssr::{render_lazy, render_vdom, render_vdom_cfg, SsrConfig, SsrRenderer, TextRenderer};
|
||||||
|
|
||||||
|
static SIMPLE_APP: Component = |cx| {
|
||||||
|
cx.render(rsx!(div {
|
||||||
|
"hello world!"
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
|
||||||
|
static SLIGHTLY_MORE_COMPLEX: Component = |cx| {
|
||||||
|
cx.render(rsx! {
|
||||||
|
div { title: "About W3Schools",
|
||||||
|
(0..20).map(|f| rsx!{
|
||||||
|
div {
|
||||||
|
title: "About W3Schools",
|
||||||
|
style: "color:blue;text-align:center",
|
||||||
|
class: "About W3Schools",
|
||||||
|
p {
|
||||||
|
title: "About W3Schools",
|
||||||
|
"Hello world!: {f}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
static NESTED_APP: Component = |cx| {
|
||||||
|
cx.render(rsx!(
|
||||||
|
div {
|
||||||
|
SIMPLE_APP {}
|
||||||
|
}
|
||||||
|
))
|
||||||
|
};
|
||||||
|
static FRAGMENT_APP: Component = |cx| {
|
||||||
|
cx.render(rsx!(
|
||||||
|
div { "f1" }
|
||||||
|
div { "f2" }
|
||||||
|
div { "f3" }
|
||||||
|
div { "f4" }
|
||||||
|
))
|
||||||
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn to_string_works() {
|
||||||
|
let mut dom = VirtualDom::new(SIMPLE_APP);
|
||||||
|
dom.rebuild();
|
||||||
|
dbg!(render_vdom(&dom));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hydration() {
|
||||||
|
let mut dom = VirtualDom::new(NESTED_APP);
|
||||||
|
dom.rebuild();
|
||||||
|
dbg!(render_vdom_cfg(&dom, |c| c.pre_render(true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn nested() {
|
||||||
|
let mut dom = VirtualDom::new(NESTED_APP);
|
||||||
|
dom.rebuild();
|
||||||
|
dbg!(render_vdom(&dom));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fragment_app() {
|
||||||
|
let mut dom = VirtualDom::new(FRAGMENT_APP);
|
||||||
|
dom.rebuild();
|
||||||
|
dbg!(render_vdom(&dom));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn write_to_file() {
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
let mut file = File::create("index.html").unwrap();
|
||||||
|
|
||||||
|
let mut dom = VirtualDom::new(SLIGHTLY_MORE_COMPLEX);
|
||||||
|
dom.rebuild();
|
||||||
|
|
||||||
|
file.write_fmt(format_args!(
|
||||||
|
"{}",
|
||||||
|
TextRenderer::from_vdom(&dom, SsrConfig::default())
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn styles() {
|
||||||
|
static STLYE_APP: Component = |cx| {
|
||||||
|
cx.render(rsx! {
|
||||||
|
div { color: "blue", font_size: "46px" }
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut dom = VirtualDom::new(STLYE_APP);
|
||||||
|
dom.rebuild();
|
||||||
|
dbg!(render_vdom(&dom));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lazy() {
|
||||||
|
let p1 = SsrRenderer::new(|c| c).render_lazy(rsx! {
|
||||||
|
div { "ello" }
|
||||||
|
});
|
||||||
|
|
||||||
|
let p2 = render_lazy(rsx! {
|
||||||
|
div {
|
||||||
|
"ello"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
assert_eq!(p1, p2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn big_lazy() {
|
||||||
|
let s = render_lazy(rsx! {
|
||||||
|
div {
|
||||||
|
div {
|
||||||
|
div {
|
||||||
|
h1 { "ello world" }
|
||||||
|
h1 { "ello world" }
|
||||||
|
h1 { "ello world" }
|
||||||
|
h1 { "ello world" }
|
||||||
|
h1 { "ello world" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
dbg!(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn inner_html() {
|
||||||
|
let s = render_lazy(rsx! {
|
||||||
|
div {
|
||||||
|
dangerous_inner_html: "<div> ack </div>"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
dbg!(s);
|
||||||
|
}
|
||||||
@@ -281,7 +281,7 @@ impl WebsysDom {
|
|||||||
if name == synthetic_event.name
|
if name == synthetic_event.name
|
||||||
|| name.trim_start_matches("on") == synthetic_event.name
|
|| name.trim_start_matches("on") == synthetic_event.name
|
||||||
{
|
{
|
||||||
log::debug!("Preventing default");
|
log::trace!("Preventing default");
|
||||||
event.prevent_default();
|
event.prevent_default();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ pub async fn run_with_props<T: 'static + Send>(root: Component<T>, root_props: T
|
|||||||
|
|
||||||
let mut websys_dom = dom::WebsysDom::new(root_el, cfg, sender_callback);
|
let mut websys_dom = dom::WebsysDom::new(root_el, cfg, sender_callback);
|
||||||
|
|
||||||
log::debug!("rebuilding app");
|
log::trace!("rebuilding app");
|
||||||
let mut mutations = dom.rebuild();
|
let mut mutations = dom.rebuild();
|
||||||
|
|
||||||
// hydrating is simply running the dom for a single render. If the page is already written, then the corresponding
|
// hydrating is simply running the dom for a single render. If the page is already written, then the corresponding
|
||||||
@@ -169,12 +169,12 @@ pub async fn run_with_props<T: 'static + Send>(root: Component<T>, root_props: T
|
|||||||
let work_loop = ric_raf::RafLoop::new();
|
let work_loop = ric_raf::RafLoop::new();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
log::debug!("waiting for work");
|
log::trace!("waiting for work");
|
||||||
// if virtualdom has nothing, wait for it to have something before requesting idle time
|
// if virtualdom has nothing, wait for it to have something before requesting idle time
|
||||||
// if there is work then this future resolves immediately.
|
// if there is work then this future resolves immediately.
|
||||||
dom.wait_for_work().await;
|
dom.wait_for_work().await;
|
||||||
|
|
||||||
log::debug!("working..");
|
log::trace!("working..");
|
||||||
|
|
||||||
// wait for the mainthread to schedule us in
|
// wait for the mainthread to schedule us in
|
||||||
let mut deadline = work_loop.wait_for_idle_time().await;
|
let mut deadline = work_loop.wait_for_idle_time().await;
|
||||||
|
|||||||
+15
-15
@@ -39,7 +39,7 @@
|
|||||||
//!
|
//!
|
||||||
//! To launch an app, we use the `launch` method for the specific renderer we want to use. In the launch function, we pass the app's `Component`.
|
//! To launch an app, we use the `launch` method for the specific renderer we want to use. In the launch function, we pass the app's `Component`.
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust, ignore
|
||||||
//! use dioxus::prelude::*;
|
//! use dioxus::prelude::*;
|
||||||
//!
|
//!
|
||||||
//! fn main() {
|
//! fn main() {
|
||||||
@@ -62,7 +62,7 @@
|
|||||||
//! consistency, we force all attributes and listeners to be listed *before*
|
//! consistency, we force all attributes and listeners to be listed *before*
|
||||||
//! children.
|
//! children.
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust, ignore
|
||||||
//! let value = "123";
|
//! let value = "123";
|
||||||
//!
|
//!
|
||||||
//! rsx!(
|
//! rsx!(
|
||||||
@@ -78,7 +78,7 @@
|
|||||||
//! of the body as child elements and rust expressions. Any rust expression that
|
//! of the body as child elements and rust expressions. Any rust expression that
|
||||||
//! implements `IntoIterator<Item = impl IntoVNode>` will be parsed as a child.
|
//! implements `IntoIterator<Item = impl IntoVNode>` will be parsed as a child.
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust, ignore
|
||||||
//! rsx!(
|
//! rsx!(
|
||||||
//! div {
|
//! div {
|
||||||
//! (0..10).map(|_| rsx!(span { "hello world" }))
|
//! (0..10).map(|_| rsx!(span { "hello world" }))
|
||||||
@@ -94,7 +94,7 @@
|
|||||||
//! `cx` as the first argument of rsx. This is sometimes useful when we need to
|
//! `cx` as the first argument of rsx. This is sometimes useful when we need to
|
||||||
//! render nodes in match statements.
|
//! render nodes in match statements.
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust, ignore
|
||||||
//! fn example(cx: Scope) -> Element {
|
//! fn example(cx: Scope) -> Element {
|
||||||
//!
|
//!
|
||||||
//! // both of these are equivalent
|
//! // both of these are equivalent
|
||||||
@@ -107,7 +107,7 @@
|
|||||||
//! Putting everything together, we can write a simple component that a list of
|
//! Putting everything together, we can write a simple component that a list of
|
||||||
//! elements:
|
//! elements:
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust, ignore
|
||||||
//! fn app(cx: Scope) -> Element {
|
//! fn app(cx: Scope) -> Element {
|
||||||
//! let name = "dave";
|
//! let name = "dave";
|
||||||
//! cx.render(rsx!(
|
//! cx.render(rsx!(
|
||||||
@@ -135,7 +135,7 @@
|
|||||||
//!
|
//!
|
||||||
//! In Dioxus, all properties are memoized by default!
|
//! In Dioxus, all properties are memoized by default!
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust, ignore
|
||||||
//! fn App(cx: Scope) -> Element {
|
//! fn App(cx: Scope) -> Element {
|
||||||
//! cx.render(rsx!(
|
//! cx.render(rsx!(
|
||||||
//! Header {
|
//! Header {
|
||||||
@@ -149,7 +149,7 @@
|
|||||||
//! Our `Header` component takes in a `title` and a `color` property, which we
|
//! Our `Header` component takes in a `title` and a `color` property, which we
|
||||||
//! delcare on an explicit `HeaderProps` struct.
|
//! delcare on an explicit `HeaderProps` struct.
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```rust, ignore
|
||||||
//! // The `Props` derive macro lets us add additional functionality to how props are interpreted.
|
//! // The `Props` derive macro lets us add additional functionality to how props are interpreted.
|
||||||
//! #[derive(Props, PartialEq)]
|
//! #[derive(Props, PartialEq)]
|
||||||
//! struct HeaderProps {
|
//! struct HeaderProps {
|
||||||
@@ -170,7 +170,7 @@
|
|||||||
//! Components may use the `inline_props` macro to completely inline the props
|
//! Components may use the `inline_props` macro to completely inline the props
|
||||||
//! definition into the function arguments.
|
//! definition into the function arguments.
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust, ignore
|
||||||
//! #[inline_props]
|
//! #[inline_props]
|
||||||
//! fn Header(cx: Scope, title: String, color: String) -> Element {
|
//! fn Header(cx: Scope, title: String, color: String) -> Element {
|
||||||
//! cx.render(rsx!(
|
//! cx.render(rsx!(
|
||||||
@@ -186,7 +186,7 @@
|
|||||||
//! attach some lifetimes to the props struct.
|
//! attach some lifetimes to the props struct.
|
||||||
//! > Note: we don't need to derive `PartialEq` for borrowed props since they cannot be memoized.
|
//! > Note: we don't need to derive `PartialEq` for borrowed props since they cannot be memoized.
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust, ignore
|
||||||
//! #[derive(Props)]
|
//! #[derive(Props)]
|
||||||
//! struct HeaderProps<'a> {
|
//! struct HeaderProps<'a> {
|
||||||
//! title: &'a str,
|
//! title: &'a str,
|
||||||
@@ -206,7 +206,7 @@
|
|||||||
//! Components that beging with an uppercase letter may be called through
|
//! Components that beging with an uppercase letter may be called through
|
||||||
//! traditional curly-brace syntax like so:
|
//! traditional curly-brace syntax like so:
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust, ignore
|
||||||
//! rsx!(
|
//! rsx!(
|
||||||
//! Header { title: "My App" }
|
//! Header { title: "My App" }
|
||||||
//! )
|
//! )
|
||||||
@@ -215,7 +215,7 @@
|
|||||||
//! Alternatively, if your components begin with a lowercase letter, you can use
|
//! Alternatively, if your components begin with a lowercase letter, you can use
|
||||||
//! the function call syntax:
|
//! the function call syntax:
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust, ignore
|
||||||
//! rsx!(
|
//! rsx!(
|
||||||
//! header( title: "My App" )
|
//! header( title: "My App" )
|
||||||
//! )
|
//! )
|
||||||
@@ -230,7 +230,7 @@
|
|||||||
//! By convention, all hooks are functions that should start with `use_`. We can
|
//! By convention, all hooks are functions that should start with `use_`. We can
|
||||||
//! use hooks to define state and modify it from within listeners.
|
//! use hooks to define state and modify it from within listeners.
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust, ignore
|
||||||
//! fn app(cx: Scope) -> Element {
|
//! fn app(cx: Scope) -> Element {
|
||||||
//! let name = use_state(&cx, || "world");
|
//! let name = use_state(&cx, || "world");
|
||||||
//!
|
//!
|
||||||
@@ -250,7 +250,7 @@
|
|||||||
//!
|
//!
|
||||||
//! Most hooks you'll write are simply composition of other hooks:
|
//! Most hooks you'll write are simply composition of other hooks:
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust, ignore
|
||||||
//! fn use_username(cx: &ScopeState, id: Uuid) -> bool {
|
//! fn use_username(cx: &ScopeState, id: Uuid) -> bool {
|
||||||
//! let users = use_context::<Users>(cx);
|
//! let users = use_context::<Users>(cx);
|
||||||
//! users.get(&id).map(|user| user.logged_in).ok_or(false)
|
//! users.get(&id).map(|user| user.logged_in).ok_or(false)
|
||||||
@@ -259,7 +259,7 @@
|
|||||||
//!
|
//!
|
||||||
//! To create entirely new foundational hooks, we can use the `use_hook` method on ScopeState.
|
//! To create entirely new foundational hooks, we can use the `use_hook` method on ScopeState.
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust, ignore
|
||||||
//! fn use_mut_string(cx: &ScopeState) -> &mut String {
|
//! fn use_mut_string(cx: &ScopeState) -> &mut String {
|
||||||
//! cx.use_hook(|_| "Hello".to_string())
|
//! cx.use_hook(|_| "Hello".to_string())
|
||||||
//! }
|
//! }
|
||||||
@@ -271,7 +271,7 @@
|
|||||||
//!
|
//!
|
||||||
//! Using components, templates, and hooks, we can build a simple app.
|
//! Using components, templates, and hooks, we can build a simple app.
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust, ignore
|
||||||
//! use dioxus::prelude::*;
|
//! use dioxus::prelude::*;
|
||||||
//!
|
//!
|
||||||
//! fn main() {
|
//! fn main() {
|
||||||
|
|||||||
Reference in new issue
Block a user