mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-26 22:20:19 +00:00
Fix cargo test and a number of little cleanup bugs
This commit is contained in:
parent
eff1dd6c90
commit
0bd9692e45
49 changed files with 307 additions and 309 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -2787,8 +2787,6 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"axum 0.6.20",
|
"axum 0.6.20",
|
||||||
"dioxus",
|
"dioxus",
|
||||||
"dioxus-fullstack",
|
|
||||||
"dioxus-web",
|
|
||||||
"execute",
|
"execute",
|
||||||
"serde",
|
"serde",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
|
|
@ -51,7 +51,7 @@ fn ClientList() -> Element {
|
||||||
h2 { "List of Clients" }
|
h2 { "List of Clients" }
|
||||||
Link { to: Route::ClientAdd, class: "pure-button pure-button-primary", "Add Client" }
|
Link { to: Route::ClientAdd, class: "pure-button pure-button-primary", "Add Client" }
|
||||||
Link { to: Route::Settings, class: "pure-button", "Settings" }
|
Link { to: Route::Settings, class: "pure-button", "Settings" }
|
||||||
for client in CLIENTS.iter() {
|
for client in CLIENTS.read().iter() {
|
||||||
div { class: "client", style: "margin-bottom: 50px",
|
div { class: "client", style: "margin-bottom: 50px",
|
||||||
p { "Name: {client.first_name} {client.last_name}" }
|
p { "Name: {client.first_name} {client.last_name}" }
|
||||||
p { "Description: {client.description}" }
|
p { "Description: {client.description}" }
|
||||||
|
|
|
@ -6,7 +6,7 @@ use dioxus::prelude::*;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// We can render VirtualDoms
|
// We can render VirtualDoms
|
||||||
let mut vdom = VirtualDom::prebuilt(app);
|
let vdom = VirtualDom::prebuilt(app);
|
||||||
println!("{}", dioxus_ssr::render(&vdom));
|
println!("{}", dioxus_ssr::render(&vdom));
|
||||||
|
|
||||||
// Or we can render rsx! calls themselves
|
// Or we can render rsx! calls themselves
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
fn it_works() {
|
fn it_works() {
|
||||||
rsx!({()}))
|
rsx!({()})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,5 +37,5 @@ pub fn Explainer<'a>(
|
||||||
{left},
|
{left},
|
||||||
{right}
|
{right}
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,5 +9,5 @@ fn SaveClipboard() -> Element {
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
div { "hello world", "hello world", "hello world" }
|
div { "hello world", "hello world", "hello world" }
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,5 +11,5 @@ pub static Icon3: Component<()> = |cx| {
|
||||||
path { d: "M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2" }
|
path { d: "M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2" }
|
||||||
circle { cx: "12", cy: "7", r: "4" }
|
circle { cx: "12", cy: "7", r: "4" }
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,5 +3,5 @@ fn it_works() {
|
||||||
div {
|
div {
|
||||||
span { "Description: ", {package.description.as_deref().unwrap_or("❌❌❌❌ missing")} }
|
span { "Description: ", {package.description.as_deref().unwrap_or("❌❌❌❌ missing")} }
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
fn app() -> Element {
|
fn app() -> Element {
|
||||||
rsx! { div { "hello world" } })
|
rsx! { div { "hello world" } }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
fn app() -> Element {
|
fn app() -> Element {
|
||||||
rsx! {
|
rsx! {
|
||||||
div {"hello world" }
|
div {"hello world" }
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
fn app() -> Element {
|
fn app() -> Element {
|
||||||
rsx! { div { "hello world" } })
|
rsx! { div { "hello world" } }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
fn app() -> Element {
|
fn app() -> Element {
|
||||||
rsx! {
|
rsx! {
|
||||||
div {"hello world" }
|
div {"hello world" }
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,5 +4,5 @@ fn ItWroks() {
|
||||||
{left},
|
{left},
|
||||||
{right}
|
{right}
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
fn ItWroks() {
|
fn ItWroks() {
|
||||||
rsx! {
|
rsx! {
|
||||||
div { class: "flex flex-wrap items-center dark:text-white py-16 border-t font-light", {left}, {right} }
|
div { class: "flex flex-wrap items-center dark:text-white py-16 border-t font-light", {left}, {right} }
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,5 +4,5 @@ fn ItWroks() {
|
||||||
{left},
|
{left},
|
||||||
{right}
|
{right}
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
fn ItWroks() {
|
fn ItWroks() {
|
||||||
rsx! {
|
rsx! {
|
||||||
div { class: "flex flex-wrap items-center dark:text-white py-16 border-t font-light", {left}, {right} }
|
div { class: "flex flex-wrap items-center dark:text-white py-16 border-t font-light", {left}, {right} }
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -361,10 +361,10 @@ mod tests {
|
||||||
LineColumn { line: 3, column: 24 },
|
LineColumn { line: 3, column: 24 },
|
||||||
),
|
),
|
||||||
Span::new_from_str(
|
Span::new_from_str(
|
||||||
r#"use_state"#,
|
r#"use_signal"#,
|
||||||
LineColumn { line: 3, column: 24 },
|
LineColumn { line: 3, column: 24 },
|
||||||
),
|
),
|
||||||
"use_state".to_string()
|
"use_signal".to_string()
|
||||||
),
|
),
|
||||||
ConditionalInfo::If(IfInfo::new(
|
ConditionalInfo::If(IfInfo::new(
|
||||||
Span::new_from_str(
|
Span::new_from_str(
|
||||||
|
@ -401,8 +401,8 @@ mod tests {
|
||||||
vec![Issue::HookInsideConditional(
|
vec![Issue::HookInsideConditional(
|
||||||
HookInfo::new(
|
HookInfo::new(
|
||||||
Span::new_from_str(r#"use_signal(|| "hands")"#, LineColumn { line: 4, column: 28 }),
|
Span::new_from_str(r#"use_signal(|| "hands")"#, LineColumn { line: 4, column: 28 }),
|
||||||
Span::new_from_str(r#"use_state"#, LineColumn { line: 4, column: 28 }),
|
Span::new_from_str(r#"use_signal"#, LineColumn { line: 4, column: 28 }),
|
||||||
"use_state".to_string()
|
"use_signal".to_string()
|
||||||
),
|
),
|
||||||
ConditionalInfo::Match(MatchInfo::new(
|
ConditionalInfo::Match(MatchInfo::new(
|
||||||
Span::new_from_str(
|
Span::new_from_str(
|
||||||
|
@ -437,10 +437,10 @@ mod tests {
|
||||||
LineColumn { line: 3, column: 26 },
|
LineColumn { line: 3, column: 26 },
|
||||||
),
|
),
|
||||||
Span::new_from_str(
|
Span::new_from_str(
|
||||||
"use_state",
|
"use_signal",
|
||||||
LineColumn { line: 3, column: 26 },
|
LineColumn { line: 3, column: 26 },
|
||||||
),
|
),
|
||||||
"use_state".to_string()
|
"use_signal".to_string()
|
||||||
),
|
),
|
||||||
AnyLoopInfo::For(ForInfo::new(
|
AnyLoopInfo::For(ForInfo::new(
|
||||||
Span::new_from_str(
|
Span::new_from_str(
|
||||||
|
@ -478,10 +478,10 @@ mod tests {
|
||||||
LineColumn { line: 3, column: 24 },
|
LineColumn { line: 3, column: 24 },
|
||||||
),
|
),
|
||||||
Span::new_from_str(
|
Span::new_from_str(
|
||||||
"use_state",
|
"use_signal",
|
||||||
LineColumn { line: 3, column: 24 },
|
LineColumn { line: 3, column: 24 },
|
||||||
),
|
),
|
||||||
"use_state".to_string()
|
"use_signal".to_string()
|
||||||
),
|
),
|
||||||
AnyLoopInfo::While(WhileInfo::new(
|
AnyLoopInfo::While(WhileInfo::new(
|
||||||
Span::new_from_str(
|
Span::new_from_str(
|
||||||
|
@ -519,10 +519,10 @@ mod tests {
|
||||||
LineColumn { line: 3, column: 24 },
|
LineColumn { line: 3, column: 24 },
|
||||||
),
|
),
|
||||||
Span::new_from_str(
|
Span::new_from_str(
|
||||||
"use_state",
|
"use_signal",
|
||||||
LineColumn { line: 3, column: 24 },
|
LineColumn { line: 3, column: 24 },
|
||||||
),
|
),
|
||||||
"use_state".to_string()
|
"use_signal".to_string()
|
||||||
),
|
),
|
||||||
AnyLoopInfo::Loop(LoopInfo::new(Span::new_from_str(
|
AnyLoopInfo::Loop(LoopInfo::new(Span::new_from_str(
|
||||||
"loop {\n let something = use_signal(|| \"hands\");\n println!(\"clap your {something}\")\n }",
|
"loop {\n let something = use_signal(|| \"hands\");\n println!(\"clap your {something}\")\n }",
|
||||||
|
@ -573,13 +573,13 @@ mod tests {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Span::new_from_str(
|
Span::new_from_str(
|
||||||
"use_state",
|
"use_signal",
|
||||||
LineColumn {
|
LineColumn {
|
||||||
line: 3,
|
line: 3,
|
||||||
column: 16
|
column: 16
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
"use_state".to_string()
|
"use_signal".to_string()
|
||||||
),
|
),
|
||||||
ClosureInfo::new(Span::new_from_str(
|
ClosureInfo::new(Span::new_from_str(
|
||||||
"|| {\n let b = use_signal(|| 0);\n b.get()\n }",
|
"|| {\n let b = use_signal(|| 0);\n b.get()\n }",
|
||||||
|
@ -613,13 +613,13 @@ mod tests {
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
Span::new_from_str(
|
Span::new_from_str(
|
||||||
"use_state",
|
"use_signal",
|
||||||
LineColumn {
|
LineColumn {
|
||||||
line: 2,
|
line: 2,
|
||||||
column: 13
|
column: 13
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
"use_state".to_string()
|
"use_signal".to_string()
|
||||||
))]
|
))]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -240,11 +240,11 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
let expected = indoc! {r#"
|
let expected = indoc! {r#"
|
||||||
error: hook called conditionally: `use_state` (inside `if`)
|
error: hook called conditionally: `use_signal` (inside `if`)
|
||||||
--> src/main.rs:3:25
|
--> src/main.rs:3:25
|
||||||
|
|
|
|
||||||
3 | let something = use_signal(|| "hands");
|
3 | let something = use_signal(|| "hands");
|
||||||
| ^^^^^^^^^
|
| ^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= note: `if you_are_happy && you_know_it { … }` is the conditional
|
= note: `if you_are_happy && you_know_it { … }` is the conditional
|
||||||
"#};
|
"#};
|
||||||
|
@ -271,11 +271,11 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
let expected = indoc! {r#"
|
let expected = indoc! {r#"
|
||||||
error: hook called conditionally: `use_state` (inside `match`)
|
error: hook called conditionally: `use_signal` (inside `match`)
|
||||||
--> src/main.rs:4:29
|
--> src/main.rs:4:29
|
||||||
|
|
|
|
||||||
4 | let something = use_signal(|| "hands");
|
4 | let something = use_signal(|| "hands");
|
||||||
| ^^^^^^^^^
|
| ^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= note: `match you_are_happy && you_know_it { … }` is the conditional
|
= note: `match you_are_happy && you_know_it { … }` is the conditional
|
||||||
"#};
|
"#};
|
||||||
|
@ -299,11 +299,11 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
let expected = indoc! {r#"
|
let expected = indoc! {r#"
|
||||||
error: hook called in a loop: `use_state` (inside `for` loop)
|
error: hook called in a loop: `use_signal` (inside `for` loop)
|
||||||
--> src/main.rs:3:25
|
--> src/main.rs:3:25
|
||||||
|
|
|
|
||||||
3 | let something = use_signal(|| "hands");
|
3 | let something = use_signal(|| "hands");
|
||||||
| ^^^^^^^^^
|
| ^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= note: `for i in 0..10 { … }` is the loop
|
= note: `for i in 0..10 { … }` is the loop
|
||||||
"#};
|
"#};
|
||||||
|
@ -327,11 +327,11 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
let expected = indoc! {r#"
|
let expected = indoc! {r#"
|
||||||
error: hook called in a loop: `use_state` (inside `while` loop)
|
error: hook called in a loop: `use_signal` (inside `while` loop)
|
||||||
--> src/main.rs:3:25
|
--> src/main.rs:3:25
|
||||||
|
|
|
|
||||||
3 | let something = use_signal(|| "hands");
|
3 | let something = use_signal(|| "hands");
|
||||||
| ^^^^^^^^^
|
| ^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= note: `while check_thing() { … }` is the loop
|
= note: `while check_thing() { … }` is the loop
|
||||||
"#};
|
"#};
|
||||||
|
@ -355,11 +355,11 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
let expected = indoc! {r#"
|
let expected = indoc! {r#"
|
||||||
error: hook called in a loop: `use_state` (inside `loop`)
|
error: hook called in a loop: `use_signal` (inside `loop`)
|
||||||
--> src/main.rs:3:25
|
--> src/main.rs:3:25
|
||||||
|
|
|
|
||||||
3 | let something = use_signal(|| "hands");
|
3 | let something = use_signal(|| "hands");
|
||||||
| ^^^^^^^^^
|
| ^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= note: `loop { … }` is the loop
|
= note: `loop { … }` is the loop
|
||||||
"#};
|
"#};
|
||||||
|
@ -383,11 +383,11 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
let expected = indoc! {r#"
|
let expected = indoc! {r#"
|
||||||
error: hook called in a closure: `use_state`
|
error: hook called in a closure: `use_signal`
|
||||||
--> src/main.rs:3:25
|
--> src/main.rs:3:25
|
||||||
|
|
|
|
||||||
3 | let something = use_signal(|| "hands");
|
3 | let something = use_signal(|| "hands");
|
||||||
| ^^^^^^^^^
|
| ^^^^^^^^^^
|
||||||
"#};
|
"#};
|
||||||
|
|
||||||
assert_eq!(expected, issue_report.to_string());
|
assert_eq!(expected, issue_report.to_string());
|
||||||
|
@ -411,11 +411,11 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
let expected = indoc! {r#"
|
let expected = indoc! {r#"
|
||||||
error: hook called conditionally: `use_state` (inside `if`)
|
error: hook called conditionally: `use_signal` (inside `if`)
|
||||||
--> src/main.rs:3:25
|
--> src/main.rs:3:25
|
||||||
|
|
|
|
||||||
3 | let something = use_signal(|| {
|
3 | let something = use_signal(|| {
|
||||||
| ^^^^^^^^^
|
| ^^^^^^^^^^
|
||||||
4 | "hands"
|
4 | "hands"
|
||||||
5 | });
|
5 | });
|
||||||
|
|
|
|
||||||
|
|
|
@ -366,11 +366,11 @@ async fn start_server(
|
||||||
router: Router,
|
router: Router,
|
||||||
start_browser: bool,
|
start_browser: bool,
|
||||||
rustls: Option<RustlsConfig>,
|
rustls: Option<RustlsConfig>,
|
||||||
config: &CrateConfig,
|
_config: &CrateConfig,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
// If plugins, call on_serve_start event
|
// If plugins, call on_serve_start event
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
PluginManager::on_serve_start(config)?;
|
PluginManager::on_serve_start(_config)?;
|
||||||
|
|
||||||
// Parse address
|
// Parse address
|
||||||
let addr = format!("0.0.0.0:{}", port).parse().unwrap();
|
let addr = format!("0.0.0.0:{}", port).parse().unwrap();
|
||||||
|
|
|
@ -216,10 +216,18 @@ pub fn use_drop<D: FnOnce() + 'static>(destroy: D) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A hook that allows you to insert a "before render" function.
|
||||||
|
///
|
||||||
|
/// This function will always be called before dioxus tries to render your component. This should be used for safely handling
|
||||||
|
/// early returns
|
||||||
pub fn use_before_render(f: impl FnMut() + 'static) {
|
pub fn use_before_render(f: impl FnMut() + 'static) {
|
||||||
use_hook(|| before_render(f));
|
use_hook(|| before_render(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Push this function to be run after the next render
|
||||||
|
///
|
||||||
|
/// This function will always be called before dioxus tries to render your component. This should be used for safely handling
|
||||||
|
/// early returns
|
||||||
pub fn use_after_render(f: impl FnMut() + 'static) {
|
pub fn use_after_render(f: impl FnMut() + 'static) {
|
||||||
use_hook(|| after_render(f));
|
use_hook(|| after_render(f));
|
||||||
}
|
}
|
||||||
|
@ -274,6 +282,7 @@ pub async fn flush_sync() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Use a hook with a cleanup function
|
||||||
pub fn use_hook_with_cleanup<T: Clone + 'static>(
|
pub fn use_hook_with_cleanup<T: Clone + 'static>(
|
||||||
hook: impl FnOnce() -> T,
|
hook: impl FnOnce() -> T,
|
||||||
cleanup: impl FnOnce(T) + 'static,
|
cleanup: impl FnOnce(T) + 'static,
|
||||||
|
|
|
@ -45,10 +45,12 @@ impl Task {
|
||||||
Runtime::with(|rt| !rt.tasks.borrow()[self.0].active.get()).unwrap_or_default()
|
Runtime::with(|rt| !rt.tasks.borrow()[self.0].active.get()).unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Wake the task.
|
||||||
pub fn wake(&self) {
|
pub fn wake(&self) {
|
||||||
Runtime::with(|rt| _ = rt.sender.unbounded_send(SchedulerMsg::TaskNotified(*self)));
|
Runtime::with(|rt| _ = rt.sender.unbounded_send(SchedulerMsg::TaskNotified(*self)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the task as active or paused.
|
||||||
pub fn set_active(&self, active: bool) {
|
pub fn set_active(&self, active: bool) {
|
||||||
Runtime::with(|rt| rt.tasks.borrow()[self.0].active.set(active));
|
Runtime::with(|rt| rt.tasks.borrow()[self.0].active.set(active));
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,119 +85,119 @@ fn element_swap() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn attribute_diff() {
|
fn attribute_diff() {
|
||||||
fn app(cx: Scope) -> Element {
|
// fn app() -> Element {
|
||||||
let gen = cx.generation();
|
// let gen = cx.generation();
|
||||||
|
|
||||||
// attributes have to be sorted by name
|
// // attributes have to be sorted by name
|
||||||
let attrs = match gen % 5 {
|
// let attrs = match gen % 5 {
|
||||||
0 => cx.bump().alloc([Attribute::new(
|
// 0 => cx.bump().alloc([Attribute::new(
|
||||||
"a",
|
// "a",
|
||||||
AttributeValue::Text("hello"),
|
// AttributeValue::Text("hello".into()),
|
||||||
None,
|
// None,
|
||||||
false,
|
// false,
|
||||||
)]) as &[Attribute],
|
// )]) as &[Attribute],
|
||||||
1 => cx.bump().alloc([
|
// 1 => cx.bump().alloc([
|
||||||
Attribute::new("a", AttributeValue::Text("hello"), None, false),
|
// Attribute::new("a", AttributeValue::Text("hello".into()), None, false),
|
||||||
Attribute::new("b", AttributeValue::Text("hello"), None, false),
|
// Attribute::new("b", AttributeValue::Text("hello".into()), None, false),
|
||||||
Attribute::new("c", AttributeValue::Text("hello"), None, false),
|
// Attribute::new("c", AttributeValue::Text("hello".into()), None, false),
|
||||||
]) as &[Attribute],
|
// ]) as &[Attribute],
|
||||||
2 => cx.bump().alloc([
|
// 2 => cx.bump().alloc([
|
||||||
Attribute::new("c", AttributeValue::Text("hello"), None, false),
|
// Attribute::new("c", AttributeValue::Text("hello".into()), None, false),
|
||||||
Attribute::new("d", AttributeValue::Text("hello"), None, false),
|
// Attribute::new("d", AttributeValue::Text("hello".into()), None, false),
|
||||||
Attribute::new("e", AttributeValue::Text("hello"), None, false),
|
// Attribute::new("e", AttributeValue::Text("hello".into()), None, false),
|
||||||
]) as &[Attribute],
|
// ]) as &[Attribute],
|
||||||
3 => cx.bump().alloc([Attribute::new(
|
// 3 => cx.bump().alloc([Attribute::new(
|
||||||
"d",
|
// "d",
|
||||||
AttributeValue::Text("world"),
|
// AttributeValue::Text("world".into()),
|
||||||
None,
|
// None,
|
||||||
false,
|
// false,
|
||||||
)]) as &[Attribute],
|
// )]) as &[Attribute],
|
||||||
_ => unreachable!(),
|
// _ => unreachable!(),
|
||||||
};
|
// };
|
||||||
|
|
||||||
cx.render(rsx!(
|
// cx.render(rsx!(
|
||||||
div {
|
// div {
|
||||||
..*attrs,
|
// ..*attrs,
|
||||||
"hello"
|
// "hello"
|
||||||
}
|
// }
|
||||||
))
|
// ))
|
||||||
}
|
// }
|
||||||
|
|
||||||
let mut vdom = VirtualDom::new(app);
|
// let mut vdom = VirtualDom::new(app);
|
||||||
_ = vdom.rebuild();
|
// _ = vdom.rebuild();
|
||||||
|
|
||||||
vdom.mark_dirty(ScopeId::ROOT);
|
// vdom.mark_dirty(ScopeId::ROOT);
|
||||||
assert_eq!(
|
// assert_eq!(
|
||||||
vdom.render_immediate().santize().edits,
|
// vdom.render_immediate().santize().edits,
|
||||||
[
|
// [
|
||||||
SetAttribute {
|
// SetAttribute {
|
||||||
name: "b",
|
// name: "b",
|
||||||
value: (&AttributeValue::Text("hello",)).into(),
|
// value: (&AttributeValue::Text("hello",)).into(),
|
||||||
id: ElementId(1,),
|
// id: ElementId(1,),
|
||||||
ns: None,
|
// ns: None,
|
||||||
},
|
// },
|
||||||
SetAttribute {
|
// SetAttribute {
|
||||||
name: "c",
|
// name: "c",
|
||||||
value: (&AttributeValue::Text("hello",)).into(),
|
// value: (&AttributeValue::Text("hello",)).into(),
|
||||||
id: ElementId(1,),
|
// id: ElementId(1,),
|
||||||
ns: None,
|
// ns: None,
|
||||||
},
|
// },
|
||||||
]
|
// ]
|
||||||
);
|
// );
|
||||||
|
|
||||||
vdom.mark_dirty(ScopeId::ROOT);
|
// vdom.mark_dirty(ScopeId::ROOT);
|
||||||
assert_eq!(
|
// assert_eq!(
|
||||||
vdom.render_immediate().santize().edits,
|
// vdom.render_immediate().santize().edits,
|
||||||
[
|
// [
|
||||||
SetAttribute {
|
// SetAttribute {
|
||||||
name: "a",
|
// name: "a",
|
||||||
value: (&AttributeValue::None).into(),
|
// value: (&AttributeValue::None).into(),
|
||||||
id: ElementId(1,),
|
// id: ElementId(1,),
|
||||||
ns: None,
|
// ns: None,
|
||||||
},
|
// },
|
||||||
SetAttribute {
|
// SetAttribute {
|
||||||
name: "b",
|
// name: "b",
|
||||||
value: (&AttributeValue::None).into(),
|
// value: (&AttributeValue::None).into(),
|
||||||
id: ElementId(1,),
|
// id: ElementId(1,),
|
||||||
ns: None,
|
// ns: None,
|
||||||
},
|
// },
|
||||||
SetAttribute {
|
// SetAttribute {
|
||||||
name: "d",
|
// name: "d",
|
||||||
value: (&AttributeValue::Text("hello",)).into(),
|
// value: (&AttributeValue::Text("hello",)).into(),
|
||||||
id: ElementId(1,),
|
// id: ElementId(1,),
|
||||||
ns: None,
|
// ns: None,
|
||||||
},
|
// },
|
||||||
SetAttribute {
|
// SetAttribute {
|
||||||
name: "e",
|
// name: "e",
|
||||||
value: (&AttributeValue::Text("hello",)).into(),
|
// value: (&AttributeValue::Text("hello",)).into(),
|
||||||
id: ElementId(1,),
|
// id: ElementId(1,),
|
||||||
ns: None,
|
// ns: None,
|
||||||
},
|
// },
|
||||||
]
|
// ]
|
||||||
);
|
// );
|
||||||
|
|
||||||
vdom.mark_dirty(ScopeId::ROOT);
|
// vdom.mark_dirty(ScopeId::ROOT);
|
||||||
assert_eq!(
|
// assert_eq!(
|
||||||
vdom.render_immediate().santize().edits,
|
// vdom.render_immediate().santize().edits,
|
||||||
[
|
// [
|
||||||
SetAttribute {
|
// SetAttribute {
|
||||||
name: "c",
|
// name: "c",
|
||||||
value: (&AttributeValue::None).into(),
|
// value: (&AttributeValue::None).into(),
|
||||||
id: ElementId(1,),
|
// id: ElementId(1,),
|
||||||
ns: None,
|
// ns: None,
|
||||||
},
|
// },
|
||||||
SetAttribute {
|
// SetAttribute {
|
||||||
name: "d",
|
// name: "d",
|
||||||
value: (&AttributeValue::Text("world",)).into(),
|
// value: (&AttributeValue::Text("world",)).into(),
|
||||||
id: ElementId(1,),
|
// id: ElementId(1,),
|
||||||
ns: None,
|
// ns: None,
|
||||||
},
|
// },
|
||||||
SetAttribute {
|
// SetAttribute {
|
||||||
name: "e",
|
// name: "e",
|
||||||
value: (&AttributeValue::None).into(),
|
// value: (&AttributeValue::None).into(),
|
||||||
id: ElementId(1,),
|
// id: ElementId(1,),
|
||||||
ns: None,
|
// ns: None,
|
||||||
},
|
// },
|
||||||
]
|
// ]
|
||||||
);
|
// );
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
#![cfg(not(miri))]
|
#![cfg(not(miri))]
|
||||||
|
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use dioxus_core::{
|
use dioxus_core::{AttributeValue, DynamicNode, NoOpMutations, VComponent, VNode, *};
|
||||||
prelude::EventHandler, AttributeValue, DynamicNode, NoOpMutations, VComponent, VNode, *,
|
|
||||||
};
|
|
||||||
use std::{cfg, collections::HashSet, default::Default};
|
use std::{cfg, collections::HashSet, default::Default};
|
||||||
|
|
||||||
fn random_ns() -> Option<&'static str> {
|
fn random_ns() -> Option<&'static str> {
|
||||||
|
|
|
@ -90,7 +90,7 @@ fn memo_works_properly() {
|
||||||
na: String,
|
na: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Child(cx: ChildProps) -> Element {
|
fn Child(_props: ChildProps) -> Element {
|
||||||
rsx!( div { "goodbye world" } )
|
rsx!( div { "goodbye world" } )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,7 @@ async fn yield_now_works() {
|
||||||
// these two tasks should yield to eachother
|
// these two tasks should yield to eachother
|
||||||
use_hook(|| {
|
use_hook(|| {
|
||||||
spawn(async move {
|
spawn(async move {
|
||||||
for x in 0..10 {
|
for _ in 0..10 {
|
||||||
tokio::task::yield_now().await;
|
tokio::task::yield_now().await;
|
||||||
SEQUENCE.with(|s| s.borrow_mut().push(1));
|
SEQUENCE.with(|s| s.borrow_mut().push(1));
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ async fn yield_now_works() {
|
||||||
|
|
||||||
use_hook(|| {
|
use_hook(|| {
|
||||||
spawn(async move {
|
spawn(async move {
|
||||||
for x in 0..10 {
|
for _ in 0..10 {
|
||||||
tokio::task::yield_now().await;
|
tokio::task::yield_now().await;
|
||||||
SEQUENCE.with(|s| s.borrow_mut().push(2));
|
SEQUENCE.with(|s| s.borrow_mut().push(2));
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,7 +79,7 @@ default-features = false
|
||||||
features = ["tokio_runtime", "hot-reload"]
|
features = ["tokio_runtime", "hot-reload"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
dioxus = { workspace = true }
|
dioxus = { workspace = true, features = ["desktop"] }
|
||||||
exitcode = "1.1.2"
|
exitcode = "1.1.2"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
|
|
@ -3,6 +3,10 @@ use dioxus::prelude::*;
|
||||||
use dioxus_core::prelude::consume_context;
|
use dioxus_core::prelude::consume_context;
|
||||||
use dioxus_desktop::DesktopContext;
|
use dioxus_desktop::DesktopContext;
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
check_app_exits(app);
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn check_app_exits(app: fn() -> Element) {
|
pub(crate) fn check_app_exits(app: fn() -> Element) {
|
||||||
use dioxus_desktop::tao::window::WindowBuilder;
|
use dioxus_desktop::tao::window::WindowBuilder;
|
||||||
use dioxus_desktop::Config;
|
use dioxus_desktop::Config;
|
||||||
|
@ -10,7 +14,7 @@ pub(crate) fn check_app_exits(app: fn() -> Element) {
|
||||||
let should_panic = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(true));
|
let should_panic = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(true));
|
||||||
let should_panic_clone = should_panic.clone();
|
let should_panic_clone = should_panic.clone();
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
std::thread::sleep(std::time::Duration::from_secs(100));
|
std::thread::sleep(std::time::Duration::from_secs(5));
|
||||||
if should_panic_clone.load(std::sync::atomic::Ordering::SeqCst) {
|
if should_panic_clone.load(std::sync::atomic::Ordering::SeqCst) {
|
||||||
std::process::exit(exitcode::SOFTWARE);
|
std::process::exit(exitcode::SOFTWARE);
|
||||||
}
|
}
|
||||||
|
@ -24,14 +28,10 @@ pub(crate) fn check_app_exits(app: fn() -> Element) {
|
||||||
should_panic.store(false, std::sync::atomic::Ordering::SeqCst);
|
should_panic.store(false, std::sync::atomic::Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() {
|
|
||||||
check_app_exits(app);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mock_event(id: &'static str, value: &'static str) {
|
fn mock_event(id: &'static str, value: &'static str) {
|
||||||
use_effect(move || {
|
use_hook(move || {
|
||||||
spawn(async move {
|
spawn(async move {
|
||||||
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
|
tokio::time::sleep(std::time::Duration::from_millis(2000)).await;
|
||||||
|
|
||||||
let js = format!(
|
let js = format!(
|
||||||
r#"
|
r#"
|
||||||
|
@ -45,7 +45,7 @@ fn mock_event(id: &'static str, value: &'static str) {
|
||||||
value, id
|
value, id
|
||||||
);
|
);
|
||||||
|
|
||||||
dioxus::eval(js);
|
eval(&js).unwrap().await.unwrap();
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -205,7 +205,7 @@ fn app() -> Element {
|
||||||
r#"new FocusEvent("focusout",{bubbles: true})"#,
|
r#"new FocusEvent("focusout",{bubbles: true})"#,
|
||||||
);
|
);
|
||||||
|
|
||||||
if received_events() == 13 {
|
if received_events() == 12 {
|
||||||
println!("all events recieved");
|
println!("all events recieved");
|
||||||
desktop_context.close();
|
desktop_context.close();
|
||||||
}
|
}
|
||||||
|
@ -216,12 +216,11 @@ fn app() -> Element {
|
||||||
width: "100px",
|
width: "100px",
|
||||||
height: "100px",
|
height: "100px",
|
||||||
onmounted: move |evt| async move {
|
onmounted: move |evt| async move {
|
||||||
todo!();
|
let rect = evt.get_client_rect().await.unwrap();
|
||||||
// let rect = evt.get_client_rect().await.unwrap();
|
println!("rect: {:?}", rect);
|
||||||
// println!("rect: {:?}", rect);
|
assert_eq!(rect.width(), 100.0);
|
||||||
// assert_eq!(rect.width(), 100.0);
|
assert_eq!(rect.height(), 100.0);
|
||||||
// assert_eq!(rect.height(), 100.0);
|
received_events.with_mut(|x| *x += 1);
|
||||||
// received_events.with_mut(|x| *x + 1)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
button {
|
button {
|
||||||
|
@ -234,7 +233,7 @@ fn app() -> Element {
|
||||||
event.data.trigger_button(),
|
event.data.trigger_button(),
|
||||||
Some(dioxus_html::input_data::MouseButton::Primary),
|
Some(dioxus_html::input_data::MouseButton::Primary),
|
||||||
);
|
);
|
||||||
received_events.with_mut(|x| *x + 1);
|
received_events.with_mut(|x| *x += 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
div {
|
div {
|
||||||
|
@ -248,7 +247,7 @@ fn app() -> Element {
|
||||||
.held_buttons()
|
.held_buttons()
|
||||||
.contains(dioxus_html::input_data::MouseButton::Secondary),
|
.contains(dioxus_html::input_data::MouseButton::Secondary),
|
||||||
);
|
);
|
||||||
received_events.with_mut(|x| *x + 1);
|
received_events.with_mut(|x| *x += 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
div {
|
div {
|
||||||
|
@ -266,7 +265,7 @@ fn app() -> Element {
|
||||||
event.data.trigger_button(),
|
event.data.trigger_button(),
|
||||||
Some(dioxus_html::input_data::MouseButton::Secondary),
|
Some(dioxus_html::input_data::MouseButton::Secondary),
|
||||||
);
|
);
|
||||||
received_events.with_mut(|x| *x + 1);
|
received_events.with_mut(|x| *x += 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
div {
|
div {
|
||||||
|
@ -287,7 +286,7 @@ fn app() -> Element {
|
||||||
event.data.trigger_button(),
|
event.data.trigger_button(),
|
||||||
Some(dioxus_html::input_data::MouseButton::Secondary),
|
Some(dioxus_html::input_data::MouseButton::Secondary),
|
||||||
);
|
);
|
||||||
received_events.with_mut(|x| *x + 1);
|
received_events.with_mut(|x| *x += 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
div {
|
div {
|
||||||
|
@ -305,7 +304,7 @@ fn app() -> Element {
|
||||||
event.data.trigger_button(),
|
event.data.trigger_button(),
|
||||||
Some(dioxus_html::input_data::MouseButton::Secondary),
|
Some(dioxus_html::input_data::MouseButton::Secondary),
|
||||||
);
|
);
|
||||||
received_events.with_mut(|x| *x + 1);
|
received_events.with_mut(|x| *x += 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
div {
|
div {
|
||||||
|
@ -318,7 +317,7 @@ fn app() -> Element {
|
||||||
event.data.trigger_button(),
|
event.data.trigger_button(),
|
||||||
Some(dioxus_html::input_data::MouseButton::Primary),
|
Some(dioxus_html::input_data::MouseButton::Primary),
|
||||||
);
|
);
|
||||||
received_events.with_mut(|x| *x + 1);
|
received_events.with_mut(|x| *x += 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
div {
|
div {
|
||||||
|
@ -331,7 +330,7 @@ fn app() -> Element {
|
||||||
let dioxus_html::geometry::WheelDelta::Pixels(delta) = event.data.delta() else {
|
let dioxus_html::geometry::WheelDelta::Pixels(delta) = event.data.delta() else {
|
||||||
panic!("Expected delta to be in pixels") };
|
panic!("Expected delta to be in pixels") };
|
||||||
assert_eq!(delta, Vector3D::new(1.0, 2.0, 3.0));
|
assert_eq!(delta, Vector3D::new(1.0, 2.0, 3.0));
|
||||||
received_events.with_mut(|x| *x + 1);
|
received_events.with_mut(|x| *x += 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
input {
|
input {
|
||||||
|
@ -344,7 +343,7 @@ fn app() -> Element {
|
||||||
assert_eq!(event.data.location(), Location::Standard);
|
assert_eq!(event.data.location(), Location::Standard);
|
||||||
assert!(event.data.is_auto_repeating());
|
assert!(event.data.is_auto_repeating());
|
||||||
assert!(event.data.is_composing());
|
assert!(event.data.is_composing());
|
||||||
received_events.with_mut(|x| *x + 1)
|
received_events.with_mut(|x| *x += 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
input {
|
input {
|
||||||
|
@ -357,7 +356,7 @@ fn app() -> Element {
|
||||||
assert_eq!(event.data.location(), Location::Standard);
|
assert_eq!(event.data.location(), Location::Standard);
|
||||||
assert!(!event.data.is_auto_repeating());
|
assert!(!event.data.is_auto_repeating());
|
||||||
assert!(!event.data.is_composing());
|
assert!(!event.data.is_composing());
|
||||||
received_events.with_mut(|x| *x + 1)
|
received_events.with_mut(|x| *x += 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
input {
|
input {
|
||||||
|
@ -370,21 +369,21 @@ fn app() -> Element {
|
||||||
assert_eq!(event.data.location(), Location::Standard);
|
assert_eq!(event.data.location(), Location::Standard);
|
||||||
assert!(!event.data.is_auto_repeating());
|
assert!(!event.data.is_auto_repeating());
|
||||||
assert!(!event.data.is_composing());
|
assert!(!event.data.is_composing());
|
||||||
received_events.with_mut(|x| *x + 1)
|
received_events.with_mut(|x| *x += 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
input {
|
input {
|
||||||
id: "focus_in_div",
|
id: "focus_in_div",
|
||||||
onfocusin: move |event| {
|
onfocusin: move |event| {
|
||||||
println!("{:?}", event.data);
|
println!("{:?}", event.data);
|
||||||
received_events.with_mut(|x| *x + 1);
|
received_events.with_mut(|x| *x += 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
input {
|
input {
|
||||||
id: "focus_out_div",
|
id: "focus_out_div",
|
||||||
onfocusout: move |event| {
|
onfocusout: move |event| {
|
||||||
println!("{:?}", event.data);
|
println!("{:?}", event.data);
|
||||||
received_events.with_mut(|x| *x + 1);
|
received_events.with_mut(|x| *x += 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ pub(crate) fn check_app_exits(app: fn() -> Element) {
|
||||||
let should_panic = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(true));
|
let should_panic = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(true));
|
||||||
let should_panic_clone = should_panic.clone();
|
let should_panic_clone = should_panic.clone();
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
std::thread::sleep(std::time::Duration::from_secs(100));
|
std::thread::sleep(std::time::Duration::from_secs(5));
|
||||||
if should_panic_clone.load(std::sync::atomic::Ordering::SeqCst) {
|
if should_panic_clone.load(std::sync::atomic::Ordering::SeqCst) {
|
||||||
std::process::exit(exitcode::SOFTWARE);
|
std::process::exit(exitcode::SOFTWARE);
|
||||||
}
|
}
|
||||||
|
@ -31,19 +31,21 @@ fn use_inner_html(id: &'static str) -> Option<String> {
|
||||||
|
|
||||||
use_effect(move || {
|
use_effect(move || {
|
||||||
spawn(async move {
|
spawn(async move {
|
||||||
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
|
tokio::time::sleep(std::time::Duration::from_millis(2000)).await;
|
||||||
|
|
||||||
let res = dioxus::eval(format!(
|
let res = eval(&format!(
|
||||||
r#"let element = document.getElementById('{}');
|
r#"let element = document.getElementById('{}');
|
||||||
return element.innerHTML"#,
|
return element.innerHTML"#,
|
||||||
id
|
id
|
||||||
))
|
))
|
||||||
.await;
|
.unwrap()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
if let Ok(html) = res {
|
if let Some(html) = res.as_str() {
|
||||||
// serde_json::Value::String(html)
|
// serde_json::Value::String(html)
|
||||||
println!("html: {}", html);
|
println!("html: {}", html);
|
||||||
value.set(Some(html));
|
value.set(Some(html.to_string()));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -51,7 +53,7 @@ fn use_inner_html(id: &'static str) -> Option<String> {
|
||||||
value.read().clone()
|
value.read().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
const EXPECTED_HTML: &str = r#"<div id="5" style="width: 100px; height: 100px; color: rgb(0, 0, 0);"><input type="checkbox"><h1>text</h1><div><p>hello world</p></div></div>"#;
|
const EXPECTED_HTML: &str = r#"<div style="width: 100px; height: 100px; color: rgb(0, 0, 0);" id="5"><input type="checkbox"><h1>text</h1><div><p>hello world</p></div></div>"#;
|
||||||
|
|
||||||
fn check_html_renders() -> Element {
|
fn check_html_renders() -> Element {
|
||||||
let inner_html = use_inner_html("main_div");
|
let inner_html = use_inner_html("main_div");
|
||||||
|
@ -62,10 +64,6 @@ fn check_html_renders() -> Element {
|
||||||
println!("{}", raw_html);
|
println!("{}", raw_html);
|
||||||
let fragment = &raw_html;
|
let fragment = &raw_html;
|
||||||
let expected = EXPECTED_HTML;
|
let expected = EXPECTED_HTML;
|
||||||
// let fragment = scraper::Html::parse_fragment(&raw_html);
|
|
||||||
// println!("fragment: {}", fragment.html());
|
|
||||||
// let expected = scraper::Html::parse_fragment(EXPECTED_HTML);
|
|
||||||
// println!("expected: {}", expected.html());
|
|
||||||
assert_eq!(raw_html, EXPECTED_HTML);
|
assert_eq!(raw_html, EXPECTED_HTML);
|
||||||
if fragment == expected {
|
if fragment == expected {
|
||||||
println!("html matches");
|
println!("html matches");
|
||||||
|
|
|
@ -85,13 +85,3 @@ pub use dioxus_tui as tui;
|
||||||
|
|
||||||
#[cfg(feature = "ssr")]
|
#[cfg(feature = "ssr")]
|
||||||
pub use dioxus_ssr as ssr;
|
pub use dioxus_ssr as ssr;
|
||||||
|
|
||||||
/// Try to evaluate javascript in the target window
|
|
||||||
///
|
|
||||||
/// For the browser, this is the window object
|
|
||||||
/// For desktop/mobile, this is the webview object
|
|
||||||
///
|
|
||||||
/// For native, it will try and use the platform's JS engine if available
|
|
||||||
pub async fn eval(src: String) -> std::result::Result<String, Box<dyn std::error::Error>> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
|
@ -72,8 +72,8 @@ pub use use_coroutine::*;
|
||||||
mod use_future;
|
mod use_future;
|
||||||
pub use use_future::*;
|
pub use use_future::*;
|
||||||
|
|
||||||
mod use_sorted;
|
// mod use_sorted;
|
||||||
pub use use_sorted::*;
|
// pub use use_sorted::*;
|
||||||
|
|
||||||
mod use_resource;
|
mod use_resource;
|
||||||
pub use use_resource::*;
|
pub use use_resource::*;
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
use dioxus_core::prelude::*;
|
use crate::dependency::Dependency;
|
||||||
use dioxus_signals::{CopyValue, ReadOnlySignal, Readable, Signal, SignalData};
|
|
||||||
use dioxus_signals::{Storage, Writable};
|
|
||||||
// use generational_box::Storage;
|
|
||||||
|
|
||||||
use crate::use_signal;
|
use crate::use_signal;
|
||||||
use crate::{dependency::Dependency, use_hook_did_run};
|
use dioxus_core::prelude::*;
|
||||||
// use dioxus_signals::{signal::SignalData, ReadOnlySignal, Signal};
|
use dioxus_signals::{ReadOnlySignal, Readable, Signal, SignalData};
|
||||||
|
use dioxus_signals::{Storage, Writable};
|
||||||
|
|
||||||
/// Creates a new unsync Selector. The selector will be run immediately and whenever any signal it reads changes.
|
/// Creates a new unsync Selector. The selector will be run immediately and whenever any signal it reads changes.
|
||||||
///
|
///
|
||||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
||||||
LiveViewError,
|
LiveViewError,
|
||||||
};
|
};
|
||||||
use dioxus_core::prelude::*;
|
use dioxus_core::prelude::*;
|
||||||
use dioxus_html::{select, EventData, HtmlEvent, PlatformEventData};
|
use dioxus_html::{EventData, HtmlEvent, PlatformEventData};
|
||||||
use dioxus_interpreter_js::MutationState;
|
use dioxus_interpreter_js::MutationState;
|
||||||
use futures_util::{pin_mut, SinkExt, StreamExt};
|
use futures_util::{pin_mut, SinkExt, StreamExt};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
|
@ -41,7 +41,7 @@ fullstack = ["dioxus-fullstack"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
axum = { version = "0.6.1", features = ["ws"] }
|
axum = { version = "0.6.1", features = ["ws"] }
|
||||||
dioxus = { workspace = true }
|
dioxus = { workspace = true, features = ["router" ]}
|
||||||
# dioxus-liveview = { workspace = true, features = ["axum"] }
|
# dioxus-liveview = { workspace = true, features = ["axum"] }
|
||||||
dioxus-ssr = { path = "../ssr" }
|
dioxus-ssr = { path = "../ssr" }
|
||||||
criterion = { version = "0.5", features = ["async_tokio", "html_reports"] }
|
criterion = { version = "0.5", features = ["async_tokio", "html_reports"] }
|
||||||
|
|
|
@ -106,7 +106,7 @@ fn Route2(user_id: usize) -> Element {
|
||||||
fn Route3(dynamic: String) -> Element {
|
fn Route3(dynamic: String) -> Element {
|
||||||
let mut current_route_str = use_signal(String::new);
|
let mut current_route_str = use_signal(String::new);
|
||||||
|
|
||||||
let current_route = use_route()?;
|
let current_route = use_route();
|
||||||
let parsed = Route::from_str(¤t_route_str.read());
|
let parsed = Route::from_str(¤t_route_str.read());
|
||||||
|
|
||||||
let site_map = Route::SITE_MAP
|
let site_map = Route::SITE_MAP
|
||||||
|
|
|
@ -81,6 +81,9 @@ impl From<&Url> for IntoRoutable {
|
||||||
/// The properties for a [`Link`].
|
/// The properties for a [`Link`].
|
||||||
#[derive(Props, Clone, PartialEq)]
|
#[derive(Props, Clone, PartialEq)]
|
||||||
pub struct LinkProps {
|
pub struct LinkProps {
|
||||||
|
/// The class attribute for the `a` tag.
|
||||||
|
pub class: Option<String>,
|
||||||
|
|
||||||
/// A class to apply to the generate HTML anchor tag if the `target` route is active.
|
/// A class to apply to the generate HTML anchor tag if the `target` route is active.
|
||||||
pub active_class: Option<String>,
|
pub active_class: Option<String>,
|
||||||
|
|
||||||
|
@ -204,6 +207,7 @@ pub fn Link(props: LinkProps) -> Element {
|
||||||
onclick_only,
|
onclick_only,
|
||||||
rel,
|
rel,
|
||||||
to,
|
to,
|
||||||
|
class,
|
||||||
..
|
..
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
|
@ -226,9 +230,19 @@ pub fn Link(props: LinkProps) -> Element {
|
||||||
IntoRoutable::Route(route) => router.any_route_to_string(&**route),
|
IntoRoutable::Route(route) => router.any_route_to_string(&**route),
|
||||||
};
|
};
|
||||||
let parsed_route: NavigationTarget<Rc<dyn Any>> = router.resolve_into_routable(to.clone());
|
let parsed_route: NavigationTarget<Rc<dyn Any>> = router.resolve_into_routable(to.clone());
|
||||||
let class = active_class
|
|
||||||
.and_then(|active_class| (href == current_url).then(|| format!(" {active_class}")))
|
let mut class_ = String::new();
|
||||||
.unwrap_or_default();
|
if let Some(c) = class {
|
||||||
|
class_.push_str(&c);
|
||||||
|
}
|
||||||
|
if let Some(c) = active_class {
|
||||||
|
if href == current_url {
|
||||||
|
if !class_.is_empty() {
|
||||||
|
class_.push(' ');
|
||||||
|
}
|
||||||
|
class_.push_str(&c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let tag_target = new_tab.then_some("_blank").unwrap_or_default();
|
let tag_target = new_tab.then_some("_blank").unwrap_or_default();
|
||||||
|
|
||||||
|
@ -254,10 +268,10 @@ pub fn Link(props: LinkProps) -> Element {
|
||||||
rsx! {
|
rsx! {
|
||||||
a {
|
a {
|
||||||
onclick: action,
|
onclick: action,
|
||||||
href: "{href}",
|
href,
|
||||||
prevent_default: "{prevent_default}",
|
prevent_default,
|
||||||
class: "{class}",
|
class: class_,
|
||||||
rel: "{rel}",
|
rel,
|
||||||
target: "{tag_target}",
|
target: "{tag_target}",
|
||||||
..attributes,
|
..attributes,
|
||||||
{children}
|
{children}
|
||||||
|
|
|
@ -157,7 +157,7 @@ impl RouterContext {
|
||||||
/// Will fail silently if there is no previous location to go to.
|
/// Will fail silently if there is no previous location to go to.
|
||||||
pub fn go_back(&self) {
|
pub fn go_back(&self) {
|
||||||
{
|
{
|
||||||
self.inner.write().history.go_back();
|
self.inner.clone().write().history.go_back();
|
||||||
}
|
}
|
||||||
|
|
||||||
self.change_route();
|
self.change_route();
|
||||||
|
@ -168,7 +168,7 @@ impl RouterContext {
|
||||||
/// Will fail silently if there is no next location to go to.
|
/// Will fail silently if there is no next location to go to.
|
||||||
pub fn go_forward(&self) {
|
pub fn go_forward(&self) {
|
||||||
{
|
{
|
||||||
self.inner.write().history.go_forward();
|
self.inner.clone().write().history.go_forward();
|
||||||
}
|
}
|
||||||
|
|
||||||
self.change_route();
|
self.change_route();
|
||||||
|
@ -179,7 +179,7 @@ impl RouterContext {
|
||||||
target: NavigationTarget<Rc<dyn Any>>,
|
target: NavigationTarget<Rc<dyn Any>>,
|
||||||
) -> Option<ExternalNavigationFailure> {
|
) -> Option<ExternalNavigationFailure> {
|
||||||
{
|
{
|
||||||
let mut write = self.inner.write();
|
let mut write = self.inner.clone().write();
|
||||||
match target {
|
match target {
|
||||||
NavigationTarget::Internal(p) => write.history.push(p),
|
NavigationTarget::Internal(p) => write.history.push(p),
|
||||||
NavigationTarget::External(e) => return write.external(e),
|
NavigationTarget::External(e) => return write.external(e),
|
||||||
|
@ -195,7 +195,7 @@ impl RouterContext {
|
||||||
pub fn push(&self, target: impl Into<IntoRoutable>) -> Option<ExternalNavigationFailure> {
|
pub fn push(&self, target: impl Into<IntoRoutable>) -> Option<ExternalNavigationFailure> {
|
||||||
let target = self.resolve_into_routable(target.into());
|
let target = self.resolve_into_routable(target.into());
|
||||||
{
|
{
|
||||||
let mut write = self.inner.write();
|
let mut write = self.inner.clone().write();
|
||||||
match target {
|
match target {
|
||||||
NavigationTarget::Internal(p) => write.history.push(p),
|
NavigationTarget::Internal(p) => write.history.push(p),
|
||||||
NavigationTarget::External(e) => return write.external(e),
|
NavigationTarget::External(e) => return write.external(e),
|
||||||
|
@ -212,7 +212,7 @@ impl RouterContext {
|
||||||
let target = self.resolve_into_routable(target.into());
|
let target = self.resolve_into_routable(target.into());
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut state = self.inner.write();
|
let mut state = self.inner.clone().write();
|
||||||
match target {
|
match target {
|
||||||
NavigationTarget::Internal(p) => state.history.replace(p),
|
NavigationTarget::Internal(p) => state.history.replace(p),
|
||||||
NavigationTarget::External(e) => return state.external(e),
|
NavigationTarget::External(e) => return state.external(e),
|
||||||
|
@ -276,14 +276,14 @@ impl RouterContext {
|
||||||
|
|
||||||
/// Clear any unresolved errors
|
/// Clear any unresolved errors
|
||||||
pub fn clear_error(&self) {
|
pub fn clear_error(&self) {
|
||||||
let mut write_inner = self.inner.write();
|
let mut write_inner = self.inner.clone().write();
|
||||||
write_inner.unresolved_error = None;
|
write_inner.unresolved_error = None;
|
||||||
|
|
||||||
write_inner.update_subscribers();
|
write_inner.update_subscribers();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn render_error(&self) -> Element {
|
pub(crate) fn render_error(&self) -> Element {
|
||||||
let inner_read = self.inner.write();
|
let inner_read = self.inner.clone().write();
|
||||||
inner_read
|
inner_read
|
||||||
.unresolved_error
|
.unresolved_error
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -297,7 +297,7 @@ impl RouterContext {
|
||||||
let callback = callback.clone();
|
let callback = callback.clone();
|
||||||
drop(self_read);
|
drop(self_read);
|
||||||
if let Some(new) = callback(myself) {
|
if let Some(new) = callback(myself) {
|
||||||
let mut self_write = self.inner.write();
|
let mut self_write = self.inner.clone().write();
|
||||||
match new {
|
match new {
|
||||||
NavigationTarget::Internal(p) => self_write.history.replace(p),
|
NavigationTarget::Internal(p) => self_write.history.replace(p),
|
||||||
NavigationTarget::External(e) => return self_write.external(e),
|
NavigationTarget::External(e) => return self_write.external(e),
|
||||||
|
|
|
@ -33,7 +33,7 @@ use crate::utils::use_router_internal::use_router_internal;
|
||||||
///
|
///
|
||||||
/// #[component]
|
/// #[component]
|
||||||
/// fn Index() -> Element {
|
/// fn Index() -> Element {
|
||||||
/// let path: Route = use_route(&cx).unwrap();
|
/// let path: Route = use_route();
|
||||||
/// rsx! {
|
/// rsx! {
|
||||||
/// h2 { "Current Path" }
|
/// h2 { "Current Path" }
|
||||||
/// p { "{path}" }
|
/// p { "{path}" }
|
||||||
|
@ -45,14 +45,12 @@ use crate::utils::use_router_internal::use_router_internal;
|
||||||
/// # assert_eq!(dioxus_ssr::render(&vdom), "<h1>App</h1><h2>Current Path</h2><p>/</p>")
|
/// # assert_eq!(dioxus_ssr::render(&vdom), "<h1>App</h1><h2>Current Path</h2><p>/</p>")
|
||||||
/// ```
|
/// ```
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn use_route<R: Routable + Clone>() -> Option<R> {
|
pub fn use_route<R: Routable + Clone>() -> R {
|
||||||
match use_router_internal() {
|
match use_router_internal() {
|
||||||
Some(r) => Some(r.current()),
|
Some(r) => r.current(),
|
||||||
None => {
|
None => {
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
panic!("`use_route` must have access to a parent router");
|
panic!("`use_route` must have access to a parent router");
|
||||||
#[allow(unreachable_code)]
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,11 +73,10 @@ fn href_internal() {
|
||||||
}
|
}
|
||||||
|
|
||||||
let expected = format!(
|
let expected = format!(
|
||||||
"<h1>App</h1><a {href} {default} {class} {id} {rel} {target}>Link</a>",
|
"<h1>App</h1><a {href} {default} {class} {rel} {target}>Link</a>",
|
||||||
href = r#"href="/test""#,
|
href = r#"href="/test""#,
|
||||||
default = r#"dioxus-prevent-default="onclick""#,
|
default = r#"dioxus-prevent-default="onclick""#,
|
||||||
class = r#"class="""#,
|
class = r#"class="""#,
|
||||||
id = r#"id="""#,
|
|
||||||
rel = r#"rel="""#,
|
rel = r#"rel="""#,
|
||||||
target = r#"target="""#
|
target = r#"target="""#
|
||||||
);
|
);
|
||||||
|
@ -111,11 +110,10 @@ fn href_external() {
|
||||||
}
|
}
|
||||||
|
|
||||||
let expected = format!(
|
let expected = format!(
|
||||||
"<h1>App</h1><a {href} {default} {class} {id} {rel} {target}>Link</a>",
|
"<h1>App</h1><a {href} {default} {class} {rel} {target}>Link</a>",
|
||||||
href = r#"href="https://dioxuslabs.com/""#,
|
href = r#"href="https://dioxuslabs.com/""#,
|
||||||
default = r#"dioxus-prevent-default="""#,
|
default = r#"dioxus-prevent-default="""#,
|
||||||
class = r#"class="""#,
|
class = r#"class="""#,
|
||||||
id = r#"id="""#,
|
|
||||||
rel = r#"rel="noopener noreferrer""#,
|
rel = r#"rel="noopener noreferrer""#,
|
||||||
target = r#"target="""#
|
target = r#"target="""#
|
||||||
);
|
);
|
||||||
|
@ -150,11 +148,10 @@ fn with_class() {
|
||||||
}
|
}
|
||||||
|
|
||||||
let expected = format!(
|
let expected = format!(
|
||||||
"<h1>App</h1><a {href} {default} {class} {id} {rel} {target}>Link</a>",
|
"<h1>App</h1><a {href} {default} {class} {rel} {target}>Link</a>",
|
||||||
href = r#"href="/test""#,
|
href = r#"href="/test""#,
|
||||||
default = r#"dioxus-prevent-default="onclick""#,
|
default = r#"dioxus-prevent-default="onclick""#,
|
||||||
class = r#"class="test_class""#,
|
class = r#"class="test_class""#,
|
||||||
id = r#"id="""#,
|
|
||||||
rel = r#"rel="""#,
|
rel = r#"rel="""#,
|
||||||
target = r#"target="""#
|
target = r#"target="""#
|
||||||
);
|
);
|
||||||
|
@ -183,11 +180,10 @@ fn with_active_class_active() {
|
||||||
}
|
}
|
||||||
|
|
||||||
let expected = format!(
|
let expected = format!(
|
||||||
"<h1>App</h1><a {href} {default} {class} {id} {rel} {target}>Link</a>",
|
"<h1>App</h1><a {href} {default} {class} {rel} {target}>Link</a>",
|
||||||
href = r#"href="/""#,
|
href = r#"href="/""#,
|
||||||
default = r#"dioxus-prevent-default="onclick""#,
|
default = r#"dioxus-prevent-default="onclick""#,
|
||||||
class = r#"class="test_class active_class""#,
|
class = r#"class="test_class active_class""#,
|
||||||
id = r#"id="""#,
|
|
||||||
rel = r#"rel="""#,
|
rel = r#"rel="""#,
|
||||||
target = r#"target="""#
|
target = r#"target="""#
|
||||||
);
|
);
|
||||||
|
@ -223,11 +219,10 @@ fn with_active_class_inactive() {
|
||||||
}
|
}
|
||||||
|
|
||||||
let expected = format!(
|
let expected = format!(
|
||||||
"<h1>App</h1><a {href} {default} {class} {id} {rel} {target}>Link</a>",
|
"<h1>App</h1><a {href} {default} {class} {rel} {target}>Link</a>",
|
||||||
href = r#"href="/test""#,
|
href = r#"href="/test""#,
|
||||||
default = r#"dioxus-prevent-default="onclick""#,
|
default = r#"dioxus-prevent-default="onclick""#,
|
||||||
class = r#"class="test_class""#,
|
class = r#"class="test_class""#,
|
||||||
id = r#"id="""#,
|
|
||||||
rel = r#"rel="""#,
|
rel = r#"rel="""#,
|
||||||
target = r#"target="""#
|
target = r#"target="""#
|
||||||
);
|
);
|
||||||
|
@ -262,7 +257,7 @@ fn with_id() {
|
||||||
}
|
}
|
||||||
|
|
||||||
let expected = format!(
|
let expected = format!(
|
||||||
"<h1>App</h1><a {href} {default} {class} {id} {rel} {target}>Link</a>",
|
"<h1>App</h1><a {href} {default} {class} {rel} {target} {id}>Link</a>",
|
||||||
href = r#"href="/test""#,
|
href = r#"href="/test""#,
|
||||||
default = r#"dioxus-prevent-default="onclick""#,
|
default = r#"dioxus-prevent-default="onclick""#,
|
||||||
class = r#"class="""#,
|
class = r#"class="""#,
|
||||||
|
@ -301,11 +296,10 @@ fn with_new_tab() {
|
||||||
}
|
}
|
||||||
|
|
||||||
let expected = format!(
|
let expected = format!(
|
||||||
"<h1>App</h1><a {href} {default} {class} {id} {rel} {target}>Link</a>",
|
"<h1>App</h1><a {href} {default} {class} {rel} {target}>Link</a>",
|
||||||
href = r#"href="/test""#,
|
href = r#"href="/test""#,
|
||||||
default = r#"dioxus-prevent-default="""#,
|
default = r#"dioxus-prevent-default="""#,
|
||||||
class = r#"class="""#,
|
class = r#"class="""#,
|
||||||
id = r#"id="""#,
|
|
||||||
rel = r#"rel="""#,
|
rel = r#"rel="""#,
|
||||||
target = r#"target="_blank""#
|
target = r#"target="_blank""#
|
||||||
);
|
);
|
||||||
|
@ -333,11 +327,10 @@ fn with_new_tab_external() {
|
||||||
}
|
}
|
||||||
|
|
||||||
let expected = format!(
|
let expected = format!(
|
||||||
"<h1>App</h1><a {href} {default} {class} {id} {rel} {target}>Link</a>",
|
"<h1>App</h1><a {href} {default} {class} {rel} {target}>Link</a>",
|
||||||
href = r#"href="https://dioxuslabs.com/""#,
|
href = r#"href="https://dioxuslabs.com/""#,
|
||||||
default = r#"dioxus-prevent-default="""#,
|
default = r#"dioxus-prevent-default="""#,
|
||||||
class = r#"class="""#,
|
class = r#"class="""#,
|
||||||
id = r#"id="""#,
|
|
||||||
rel = r#"rel="noopener noreferrer""#,
|
rel = r#"rel="noopener noreferrer""#,
|
||||||
target = r#"target="_blank""#
|
target = r#"target="_blank""#
|
||||||
);
|
);
|
||||||
|
@ -372,11 +365,10 @@ fn with_rel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
let expected = format!(
|
let expected = format!(
|
||||||
"<h1>App</h1><a {href} {default} {class} {id} {rel} {target}>Link</a>",
|
"<h1>App</h1><a {href} {default} {class} {rel} {target}>Link</a>",
|
||||||
href = r#"href="/test""#,
|
href = r#"href="/test""#,
|
||||||
default = r#"dioxus-prevent-default="onclick""#,
|
default = r#"dioxus-prevent-default="onclick""#,
|
||||||
class = r#"class="""#,
|
class = r#"class="""#,
|
||||||
id = r#"id="""#,
|
|
||||||
rel = r#"rel="test_rel""#,
|
rel = r#"rel="test_rel""#,
|
||||||
target = r#"target="""#
|
target = r#"target="""#
|
||||||
);
|
);
|
||||||
|
|
|
@ -20,7 +20,7 @@ pub struct Element {
|
||||||
pub name: ElementName,
|
pub name: ElementName,
|
||||||
pub key: Option<IfmtInput>,
|
pub key: Option<IfmtInput>,
|
||||||
pub attributes: Vec<AttributeType>,
|
pub attributes: Vec<AttributeType>,
|
||||||
pub(crate) merged_attributes: Vec<AttributeType>,
|
pub merged_attributes: Vec<AttributeType>,
|
||||||
pub children: Vec<BodyNode>,
|
pub children: Vec<BodyNode>,
|
||||||
pub brace: syn::token::Brace,
|
pub brace: syn::token::Brace,
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,23 +19,23 @@ impl<R: Eq + Hash> Comparer<R> {
|
||||||
///
|
///
|
||||||
/// Generally, you shouldn't need to use this hook. Instead you can use [`crate::use_memo`]. If you have many values that you need to compare to a single value, this hook will change updates from O(n) to O(1) where n is the number of values you are comparing to.
|
/// Generally, you shouldn't need to use this hook. Instead you can use [`crate::use_memo`]. If you have many values that you need to compare to a single value, this hook will change updates from O(n) to O(1) where n is the number of values you are comparing to.
|
||||||
pub fn new(mut f: impl FnMut() -> R + 'static) -> Comparer<R> {
|
pub fn new(mut f: impl FnMut() -> R + 'static) -> Comparer<R> {
|
||||||
let subscribers: CopyValue<FxHashMap<R, Signal<bool>>> =
|
let mut subscribers: CopyValue<FxHashMap<R, Signal<bool>>> =
|
||||||
CopyValue::new(FxHashMap::default());
|
CopyValue::new(FxHashMap::default());
|
||||||
let previous = CopyValue::new(None);
|
let mut previous = CopyValue::new(None);
|
||||||
|
|
||||||
Effect::new(move || {
|
Effect::new(move || {
|
||||||
let subscribers = subscribers.read();
|
let mut subscribers = subscribers.read();
|
||||||
let mut previous = previous.write();
|
let mut previous = previous.write();
|
||||||
|
|
||||||
if let Some(previous) = previous.take() {
|
if let Some(previous) = previous.take() {
|
||||||
if let Some(value) = subscribers.get(&previous) {
|
if let Some(mut value) = subscribers.get(&previous).cloned() {
|
||||||
*value.write() = false;
|
value.set(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let current = f();
|
let current = f();
|
||||||
|
|
||||||
if let Some(value) = subscribers.get(¤t) {
|
if let Some(mut value) = subscribers.get(¤t).cloned() {
|
||||||
*value.write() = true;
|
*value.write() = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,21 +53,21 @@ impl<R: Eq + Hash, S: Storage<SignalData<bool>>> Comparer<R, S> {
|
||||||
pub fn new_maybe_sync(mut f: impl FnMut() -> R + 'static) -> Comparer<R> {
|
pub fn new_maybe_sync(mut f: impl FnMut() -> R + 'static) -> Comparer<R> {
|
||||||
let subscribers: CopyValue<FxHashMap<R, Signal<bool>>> =
|
let subscribers: CopyValue<FxHashMap<R, Signal<bool>>> =
|
||||||
CopyValue::new(FxHashMap::default());
|
CopyValue::new(FxHashMap::default());
|
||||||
let previous = CopyValue::new(None);
|
let mut previous = CopyValue::new(None);
|
||||||
|
|
||||||
Effect::new(move || {
|
Effect::new(move || {
|
||||||
let subscribers = subscribers.read();
|
let subscribers = subscribers.read();
|
||||||
let mut previous = previous.write();
|
let mut previous = previous.write();
|
||||||
|
|
||||||
if let Some(previous) = previous.take() {
|
if let Some(previous) = previous.take() {
|
||||||
if let Some(value) = subscribers.get(&previous) {
|
if let Some(mut value) = subscribers.get(&previous).cloned() {
|
||||||
*value.write() = false;
|
*value.write() = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let current = f();
|
let current = f();
|
||||||
|
|
||||||
if let Some(value) = subscribers.get(¤t) {
|
if let Some(mut value) = subscribers.get(¤t).cloned() {
|
||||||
*value.write() = true;
|
*value.write() = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,8 +78,8 @@ impl<R: Eq + Hash, S: Storage<SignalData<bool>>> Comparer<R, S> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a signal which is true when the value is equal to the value passed to this function.
|
/// Returns a signal which is true when the value is equal to the value passed to this function.
|
||||||
pub fn equal(&self, value: R) -> ReadOnlySignal<bool, S> {
|
pub fn equal(&mut self, value: R) -> ReadOnlySignal<bool, S> {
|
||||||
let subscribers = self.subscribers.read();
|
let mut subscribers = self.subscribers.write();
|
||||||
|
|
||||||
match subscribers.get(&value) {
|
match subscribers.get(&value) {
|
||||||
Some(&signal) => signal.into(),
|
Some(&signal) => signal.into(),
|
||||||
|
|
|
@ -3,7 +3,7 @@ use crate::write::Writable;
|
||||||
use crate::Write;
|
use crate::Write;
|
||||||
use dioxus_core::prelude::{IntoAttributeValue, ScopeId};
|
use dioxus_core::prelude::{IntoAttributeValue, ScopeId};
|
||||||
use generational_box::{AnyStorage, GenerationalRef, UnsyncStorage};
|
use generational_box::{AnyStorage, GenerationalRef, UnsyncStorage};
|
||||||
use std::{cell::Ref, mem::MaybeUninit, ops::Deref};
|
use std::{cell::Ref, io::prelude::Read, mem::MaybeUninit, ops::Deref};
|
||||||
|
|
||||||
use super::get_global_context;
|
use super::get_global_context;
|
||||||
use crate::{MappedSignal, Signal};
|
use crate::{MappedSignal, Signal};
|
||||||
|
@ -43,6 +43,10 @@ impl<T: 'static> GlobalSignal<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn write(&self) -> Write<T, UnsyncStorage> {
|
||||||
|
self.signal().write()
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the scope the signal was created in.
|
/// Get the scope the signal was created in.
|
||||||
pub fn origin_scope(&self) -> ScopeId {
|
pub fn origin_scope(&self) -> ScopeId {
|
||||||
ScopeId::ROOT
|
ScopeId::ROOT
|
||||||
|
@ -143,8 +147,11 @@ impl<T: Clone + 'static> Deref for GlobalSignal<T> {
|
||||||
|
|
||||||
// First we create a closure that captures something with the Same in memory layout as Self (MaybeUninit<Self>).
|
// First we create a closure that captures something with the Same in memory layout as Self (MaybeUninit<Self>).
|
||||||
let uninit_callable = MaybeUninit::<Self>::uninit();
|
let uninit_callable = MaybeUninit::<Self>::uninit();
|
||||||
|
|
||||||
// Then move that value into the closure. We assume that the closure now has a in memory layout of Self.
|
// Then move that value into the closure. We assume that the closure now has a in memory layout of Self.
|
||||||
let uninit_closure = move || Self::read(unsafe { &*uninit_callable.as_ptr() }).clone();
|
let uninit_closure = move || {
|
||||||
|
<GlobalSignal<T> as Readable<T>>::read(unsafe { &*uninit_callable.as_ptr() }).clone()
|
||||||
|
};
|
||||||
|
|
||||||
// Check that the size of the closure is the same as the size of Self in case the compiler changed the layout of the closure.
|
// Check that the size of the closure is the same as the size of Self in case the compiler changed the layout of the closure.
|
||||||
let size_of_closure = std::mem::size_of_val(&uninit_closure);
|
let size_of_closure = std::mem::size_of_val(&uninit_closure);
|
||||||
|
|
|
@ -22,7 +22,7 @@ fn into_signal_compiles() {
|
||||||
|
|
||||||
fn don_t_run() {
|
fn don_t_run() {
|
||||||
takes_signal_string("hello world");
|
takes_signal_string("hello world");
|
||||||
takes_signal_string(ReadOnlySignal::new(String::from("hello world")));
|
takes_signal_string(Signal::new(String::from("hello world")));
|
||||||
takes_option_signal_string("hello world");
|
takes_option_signal_string("hello world");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,9 @@ fn current_owner<S: Storage<T>, T>() -> Owner<S> {
|
||||||
let id = TypeId::of::<S>();
|
let id = TypeId::of::<S>();
|
||||||
let override_owner = if id == TypeId::of::<SyncStorage>() {
|
let override_owner = if id == TypeId::of::<SyncStorage>() {
|
||||||
SYNC_OWNER.with(|cell| {
|
SYNC_OWNER.with(|cell| {
|
||||||
cell.borrow().clone().map(|owner| {
|
let owner = cell.borrow();
|
||||||
|
|
||||||
|
owner.clone().map(|owner| {
|
||||||
*(Box::new(owner) as Box<dyn Any>)
|
*(Box::new(owner) as Box<dyn Any>)
|
||||||
.downcast::<Owner<S>>()
|
.downcast::<Owner<S>>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -268,7 +270,7 @@ impl<T: 'static, S: Storage<T>> Writable<T> for CopyValue<T, S> {
|
||||||
self.value.try_write()
|
self.value.try_write()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write(&self) -> Self::Mut<T> {
|
fn write(&mut self) -> Self::Mut<T> {
|
||||||
self.value.write()
|
self.value.write()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ pub trait Writable<T: 'static>: Readable<T> {
|
||||||
|
|
||||||
/// Get a mutable reference to the value. If the value has been dropped, this will panic.
|
/// Get a mutable reference to the value. If the value has been dropped, this will panic.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn write(&self) -> Self::Mut<T> {
|
fn write(&mut self) -> Self::Mut<T> {
|
||||||
self.try_write().unwrap()
|
self.try_write().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ pub trait Writable<T: 'static>: Readable<T> {
|
||||||
|
|
||||||
/// Run a function with a mutable reference to the value. If the value has been dropped, this will panic.
|
/// Run a function with a mutable reference to the value. If the value has been dropped, this will panic.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn with_mut<O>(&self, f: impl FnOnce(&mut T) -> O) -> O {
|
fn with_mut<O>(&mut self, f: impl FnOnce(&mut T) -> O) -> O {
|
||||||
f(&mut *self.write())
|
f(&mut *self.write())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ pub trait Writable<T: 'static>: Readable<T> {
|
||||||
|
|
||||||
/// Index into the inner value and return a reference to the result.
|
/// Index into the inner value and return a reference to the result.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn index_mut<I>(&self, index: I) -> Self::Mut<T::Output>
|
fn index_mut<I>(&mut self, index: I) -> Self::Mut<T::Output>
|
||||||
where
|
where
|
||||||
T: std::ops::IndexMut<I>,
|
T: std::ops::IndexMut<I>,
|
||||||
{
|
{
|
||||||
|
@ -58,7 +58,7 @@ pub trait Writable<T: 'static>: Readable<T> {
|
||||||
|
|
||||||
/// Takes the value out of the Signal, leaving a Default in its place.
|
/// Takes the value out of the Signal, leaving a Default in its place.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn take(&self) -> T
|
fn take(&mut self) -> T
|
||||||
where
|
where
|
||||||
T: Default,
|
T: Default,
|
||||||
{
|
{
|
||||||
|
@ -67,7 +67,7 @@ pub trait Writable<T: 'static>: Readable<T> {
|
||||||
|
|
||||||
/// Replace the value in the Signal, returning the old value.
|
/// Replace the value in the Signal, returning the old value.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn replace(&self, value: T) -> T {
|
fn replace(&mut self, value: T) -> T {
|
||||||
self.with_mut(|v| std::mem::replace(v, value))
|
self.with_mut(|v| std::mem::replace(v, value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,12 +75,12 @@ pub trait Writable<T: 'static>: Readable<T> {
|
||||||
/// An extension trait for Writable<Option<T>> that provides some convenience methods.
|
/// An extension trait for Writable<Option<T>> that provides some convenience methods.
|
||||||
pub trait WritableOptionExt<T: 'static>: Writable<Option<T>> {
|
pub trait WritableOptionExt<T: 'static>: Writable<Option<T>> {
|
||||||
/// Gets the value out of the Option, or inserts the given value if the Option is empty.
|
/// Gets the value out of the Option, or inserts the given value if the Option is empty.
|
||||||
fn get_or_insert(&self, default: T) -> Self::Mut<T> {
|
fn get_or_insert(&mut self, default: T) -> Self::Mut<T> {
|
||||||
self.get_or_insert_with(|| default)
|
self.get_or_insert_with(|| default)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the value out of the Option, or inserts the value returned by the given function if the Option is empty.
|
/// Gets the value out of the Option, or inserts the value returned by the given function if the Option is empty.
|
||||||
fn get_or_insert_with(&self, default: impl FnOnce() -> T) -> Self::Mut<T> {
|
fn get_or_insert_with(&mut self, default: impl FnOnce() -> T) -> Self::Mut<T> {
|
||||||
let borrow = self.read();
|
let borrow = self.read();
|
||||||
if borrow.is_none() {
|
if borrow.is_none() {
|
||||||
drop(borrow);
|
drop(borrow);
|
||||||
|
@ -93,7 +93,7 @@ pub trait WritableOptionExt<T: 'static>: Writable<Option<T>> {
|
||||||
|
|
||||||
/// Attempts to write the inner value of the Option.
|
/// Attempts to write the inner value of the Option.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn as_mut(&self) -> Option<Self::Mut<T>> {
|
fn as_mut(&mut self) -> Option<Self::Mut<T>> {
|
||||||
Self::try_map_mut(self.write(), |v: &mut Option<T>| v.as_mut())
|
Self::try_map_mut(self.write(), |v: &mut Option<T>| v.as_mut())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -169,7 +169,7 @@ pub trait WritableVecExt<T: 'static>: Writable<Vec<T>> {
|
||||||
|
|
||||||
/// Try to mutably get an element from the vector.
|
/// Try to mutably get an element from the vector.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn get_mut(&self, index: usize) -> Option<Self::Mut<T>> {
|
fn get_mut(&mut self, index: usize) -> Option<Self::Mut<T>> {
|
||||||
Self::try_map_mut(self.write(), |v: &mut Vec<T>| v.get_mut(index))
|
Self::try_map_mut(self.write(), |v: &mut Vec<T>| v.get_mut(index))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -175,7 +175,7 @@ fn components_hydrate() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn hello_world_hydrates() {
|
fn hello_world_hydrates() {
|
||||||
use dioxus_signals::use_signal;
|
use dioxus::hooks::use_signal;
|
||||||
|
|
||||||
fn app() -> Element {
|
fn app() -> Element {
|
||||||
let mut count = use_signal(|| 0);
|
let mut count = use_signal(|| 0);
|
||||||
|
|
|
@ -10,19 +10,12 @@
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub(crate) hydrate: bool,
|
pub(crate) hydrate: bool,
|
||||||
pub(crate) root: ConfigRoot,
|
pub(crate) root: ConfigRoot,
|
||||||
pub(crate) cached_strings: Vec<String>,
|
|
||||||
pub(crate) default_panic_hook: bool,
|
pub(crate) default_panic_hook: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Config {
|
pub(crate) enum ConfigRoot {
|
||||||
fn default() -> Self {
|
RootName(String),
|
||||||
Self {
|
RootElement(web_sys::Element),
|
||||||
hydrate: false,
|
|
||||||
root: ConfigRoot::RootName("main".to_string()),
|
|
||||||
cached_strings: Vec::new(),
|
|
||||||
default_panic_hook: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
|
@ -72,7 +65,12 @@ impl Config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) enum ConfigRoot {
|
impl Default for Config {
|
||||||
RootName(String),
|
fn default() -> Self {
|
||||||
RootElement(web_sys::Element),
|
Self {
|
||||||
|
hydrate: false,
|
||||||
|
root: ConfigRoot::RootName("main".to_string()),
|
||||||
|
default_panic_hook: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,8 +28,7 @@ pub fn launch_virtual_dom(vdom: VirtualDom, platform_config: Config) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
/// Launch the web application with the given root component and config
|
||||||
pub fn launch_cfg(root: fn() -> Element, platform_config: Config) {
|
pub fn launch_cfg(root: fn() -> Element, platform_config: Config) {
|
||||||
let mut vdom = VirtualDom::new(root);
|
launch_virtual_dom(VirtualDom::new(root), platform_config);
|
||||||
launch_virtual_dom(vdom, platform_config);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,9 +7,7 @@ publish = false
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
dioxus-web = { path = "../../packages/web", features=["hydrate"], optional = true }
|
dioxus = { workspace = true }
|
||||||
dioxus = { path = "../../packages/dioxus" }
|
|
||||||
dioxus-fullstack = { path = "../../packages/fullstack" }
|
|
||||||
axum = { version = "0.6.12", optional = true }
|
axum = { version = "0.6.12", optional = true }
|
||||||
tokio = { version = "1.27.0", features = ["full"], optional = true }
|
tokio = { version = "1.27.0", features = ["full"], optional = true }
|
||||||
serde = "1.0.159"
|
serde = "1.0.159"
|
||||||
|
@ -17,5 +15,5 @@ execute = "0.2.12"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
ssr = ["axum", "tokio", "dioxus-fullstack/axum"]
|
ssr = ["axum", "tokio", "dioxus/axum"]
|
||||||
web = ["dioxus-web"]
|
web = ["dioxus/web"]
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
|
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use dioxus_fullstack::prelude::*;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
Loading…
Reference in a new issue