feat: more API updates
This commit is contained in:
1 parent
b3ac2ee3f7
commit
a4f280d163
16 files changed
+226
-188
No files matched your search
@@ -49,7 +49,7 @@
|
|||||||
Dioxus is a portable, performant, and ergonomic framework for building cross-platform user experiences in Rust.
|
Dioxus is a portable, performant, and ergonomic framework for building cross-platform user experiences in Rust.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn app(cx: Scope<()>) -> Element {
|
fn app(cx: Scope) -> Element {
|
||||||
let mut count = use_state(&cx, || 0);
|
let mut count = use_state(&cx, || 0);
|
||||||
|
|
||||||
cx.render(rsx!(
|
cx.render(rsx!(
|
||||||
@@ -85,18 +85,6 @@ If you know React, then you already know Dioxus.
|
|||||||
<tr>
|
<tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
Available cargo features:
|
|
||||||
- default: core (macro, hooks, html)
|
|
||||||
- macro: support for `Props` and `rsx` macros
|
|
||||||
- hooks: foundational hooks like `use_state`, `use_ref`, etc.
|
|
||||||
- html: the entire namespace of `html` elements, their listeners, and attributes
|
|
||||||
- router: a cross-platform (web and desktop) solution for global app routing
|
|
||||||
- liveview: a threadpool to spawn new VirtualDoms and their handles in
|
|
||||||
- ssr: render the virtualdom to a string
|
|
||||||
- web: render the your app on the web
|
|
||||||
- desktop: render your app locally rendered with webview
|
|
||||||
- mobile: render your app on your device rendered with webview
|
|
||||||
|
|
||||||
## Examples:
|
## Examples:
|
||||||
|
|
||||||
| File Navigator (Desktop) | Bluetooth scanner (Desktop) | TodoMVC (All platforms) | Tailwind (Liveview) |
|
| File Navigator (Desktop) | Bluetooth scanner (Desktop) | TodoMVC (All platforms) | Tailwind (Liveview) |
|
||||||
|
|||||||
@@ -4,19 +4,19 @@ fn main() {}
|
|||||||
|
|
||||||
type Element<'a> = ();
|
type Element<'a> = ();
|
||||||
|
|
||||||
pub struct Scope<'a, T> {
|
pub struct Scope<'a, T = ()> {
|
||||||
props: &'a T,
|
props: &'a T,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline_props]
|
// #[inline_props]
|
||||||
pub fn component(
|
pub fn component<'a>(
|
||||||
cx: Scope,
|
cx: Scope<'a>,
|
||||||
chkk: String,
|
chkk: String,
|
||||||
chkk2: String,
|
chkk2: String,
|
||||||
r: u32,
|
r: u32,
|
||||||
cat: &'a str,
|
cat: &'a str,
|
||||||
drd: String,
|
drd: String,
|
||||||
e: String,
|
e: String,
|
||||||
) -> Element {
|
) -> Element<'a> {
|
||||||
let r = chkk.len();
|
let r = chkk.len();
|
||||||
}
|
}
|
||||||
@@ -92,13 +92,19 @@ impl ToTokens for InlinePropsBody {
|
|||||||
quote! { #[derive(Props)] }
|
quote! { #[derive(Props)] }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let lifetime = if generics.params.is_empty() {
|
||||||
|
quote! {}
|
||||||
|
} else {
|
||||||
|
quote! { 'a, }
|
||||||
|
};
|
||||||
|
|
||||||
out_tokens.append_all(quote! {
|
out_tokens.append_all(quote! {
|
||||||
#modifiers
|
#modifiers
|
||||||
#vis struct #struct_name #generics {
|
#vis struct #struct_name #generics {
|
||||||
#(#fields),*
|
#(#fields),*
|
||||||
}
|
}
|
||||||
|
|
||||||
#vis fn #ident #generics (#cx_token: Scope<'a, #struct_name #generics>) #output {
|
#vis fn #ident #generics (#cx_token: Scope<#lifetime #struct_name #generics>) #output {
|
||||||
let #struct_name { #(#field_names),* } = &cx.props;
|
let #struct_name { #(#field_names),* } = &cx.props;
|
||||||
#block
|
#block
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -226,7 +226,7 @@ pub fn routable_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
|
|||||||
/// # Example
|
/// # Example
|
||||||
/// ```
|
/// ```
|
||||||
/// #[inline_props]
|
/// #[inline_props]
|
||||||
/// fn app(cx: Scope<{ bob: String }>) -> Element {
|
/// fn app(cx: Scope, bob: String) -> Element {
|
||||||
/// cx.render(rsx!("hello, {bob}"))
|
/// cx.render(rsx!("hello, {bob}"))
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -609,7 +609,7 @@ impl ScopeState {
|
|||||||
///
|
///
|
||||||
/// This is a "fundamental" operation and should only be called during initialization of a hook.
|
/// This is a "fundamental" operation and should only be called during initialization of a hook.
|
||||||
///
|
///
|
||||||
/// For a hook that provides the same functionality, use `use_provide_state` and `use_consume_state` instead.
|
/// For a hook that provides the same functionality, use `use_provide_context` and `use_consume_context` instead.
|
||||||
///
|
///
|
||||||
/// When the component is dropped, so is the context. Be aware of this behavior when consuming
|
/// When the component is dropped, so is the context. Be aware of this behavior when consuming
|
||||||
/// the context via Rc/Weak.
|
/// the context via Rc/Weak.
|
||||||
@@ -620,7 +620,7 @@ impl ScopeState {
|
|||||||
/// struct SharedState(&'static str);
|
/// struct SharedState(&'static str);
|
||||||
///
|
///
|
||||||
/// static App: Component<()> = |cx, props|{
|
/// static App: Component<()> = |cx, props|{
|
||||||
/// cx.use_hook(|_| cx.provide_state(SharedState("world")), |_| {}, |_| {});
|
/// cx.use_hook(|_| cx.provide_context(SharedState("world")), |_| {}, |_| {});
|
||||||
/// rsx!(cx, Child {})
|
/// rsx!(cx, Child {})
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
@@ -629,7 +629,7 @@ impl ScopeState {
|
|||||||
/// rsx!(cx, div { "hello {state.0}" })
|
/// rsx!(cx, div { "hello {state.0}" })
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn provide_state<T: 'static>(&self, value: T) {
|
pub fn provide_context<T: 'static>(&self, value: T) {
|
||||||
self.shared_contexts
|
self.shared_contexts
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.insert(TypeId::of::<T>(), Rc::new(value))
|
.insert(TypeId::of::<T>(), Rc::new(value))
|
||||||
@@ -638,7 +638,7 @@ impl ScopeState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Try to retrieve a SharedState with type T from the any parent Scope.
|
/// Try to retrieve a SharedState with type T from the any parent Scope.
|
||||||
pub fn consume_state<T: 'static>(&self) -> Option<Rc<T>> {
|
pub fn consume_context<T: 'static>(&self) -> Option<Rc<T>> {
|
||||||
if let Some(shared) = self.shared_contexts.borrow().get(&TypeId::of::<T>()) {
|
if let Some(shared) = self.shared_contexts.borrow().get(&TypeId::of::<T>()) {
|
||||||
Some(shared.clone().downcast::<T>().unwrap())
|
Some(shared.clone().downcast::<T>().unwrap())
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -14,12 +14,12 @@ fn shared_state_test() {
|
|||||||
struct MySharedState(&'static str);
|
struct MySharedState(&'static str);
|
||||||
|
|
||||||
static App: Component<()> = |cx| {
|
static App: Component<()> = |cx| {
|
||||||
cx.provide_state(MySharedState("world!"));
|
cx.provide_context(MySharedState("world!"));
|
||||||
cx.render(rsx!(Child {}))
|
cx.render(rsx!(Child {}))
|
||||||
};
|
};
|
||||||
|
|
||||||
static Child: Component<()> = |cx| {
|
static Child: Component<()> = |cx| {
|
||||||
let shared = cx.consume_state::<MySharedState>()?;
|
let shared = cx.consume_context::<MySharedState>()?;
|
||||||
cx.render(rsx!("Hello, {shared.0}"))
|
cx.render(rsx!("Hello, {shared.0}"))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,29 +1,30 @@
|
|||||||
//! Example: README.md showcase
|
|
||||||
//!
|
|
||||||
//! The example from the README.md.
|
|
||||||
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use dioxus_core as dioxus;
|
use dioxus_core as dioxus;
|
||||||
use dioxus_core_macro::*;
|
use dioxus_core_macro::*;
|
||||||
use dioxus_hooks::*;
|
use dioxus_hooks::*;
|
||||||
use dioxus_html as dioxus_elements;
|
use dioxus_html as dioxus_elements;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// simple_logger::init().unwrap();
|
|
||||||
dioxus_desktop::launch(app);
|
dioxus_desktop::launch(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn app(cx: Scope<()>) -> Element {
|
fn app(cx: Scope<()>) -> Element {
|
||||||
let mut count = use_state(&cx, || 0);
|
let count = use_state(&cx, || 0);
|
||||||
log::debug!("count is {:?}", count);
|
|
||||||
|
|
||||||
cx.push_future(|| async move {
|
// push the futureo on initialization
|
||||||
tokio::time::sleep(Duration::from_millis(1000)).await;
|
cx.use_hook(
|
||||||
println!("count is now {:?}", count);
|
|_| {
|
||||||
count += 1;
|
cx.push_future({
|
||||||
});
|
let count = count.for_async();
|
||||||
|
async move {
|
||||||
|
tokio::time::sleep(Duration::from_millis(1000)).await;
|
||||||
|
*count.get_mut() += 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|_| {},
|
||||||
|
);
|
||||||
|
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div {
|
div {
|
||||||
|
|||||||
@@ -59,11 +59,11 @@ impl<T> ProvidedStateInner<T> {
|
|||||||
///
|
///
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
pub fn use_shared_state<'a, T: 'static>(cx: &'a ScopeState) -> Option<UseSharedState<'a, T>> {
|
pub fn use_context<'a, T: 'static>(cx: &'a ScopeState) -> Option<UseSharedState<'a, T>> {
|
||||||
cx.use_hook(
|
cx.use_hook(
|
||||||
|_| {
|
|_| {
|
||||||
let scope_id = cx.scope_id();
|
let scope_id = cx.scope_id();
|
||||||
let root = cx.consume_state::<ProvidedState<T>>();
|
let root = cx.consume_context::<ProvidedState<T>>();
|
||||||
|
|
||||||
if let Some(root) = root.as_ref() {
|
if let Some(root) = root.as_ref() {
|
||||||
root.borrow_mut().consumers.insert(scope_id);
|
root.borrow_mut().consumers.insert(scope_id);
|
||||||
@@ -175,7 +175,7 @@ where
|
|||||||
///
|
///
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
pub fn use_provide_state<'a, T: 'static>(cx: &'a ScopeState, f: impl FnOnce() -> T) {
|
pub fn use_context_provider<'a, T: 'static>(cx: &'a ScopeState, f: impl FnOnce() -> T) {
|
||||||
cx.use_hook(
|
cx.use_hook(
|
||||||
|_| {
|
|_| {
|
||||||
let state: ProvidedState<T> = RefCell::new(ProvidedStateInner {
|
let state: ProvidedState<T> = RefCell::new(ProvidedStateInner {
|
||||||
@@ -183,7 +183,7 @@ pub fn use_provide_state<'a, T: 'static>(cx: &'a ScopeState, f: impl FnOnce() ->
|
|||||||
notify_any: cx.schedule_update_any(),
|
notify_any: cx.schedule_update_any(),
|
||||||
consumers: HashSet::new(),
|
consumers: HashSet::new(),
|
||||||
});
|
});
|
||||||
cx.provide_state(state)
|
cx.provide_context(state)
|
||||||
},
|
},
|
||||||
|_inner| {},
|
|_inner| {},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -2,6 +2,7 @@ use dioxus_core::prelude::*;
|
|||||||
use dioxus_core_macro::*;
|
use dioxus_core_macro::*;
|
||||||
use dioxus_html as dioxus_elements;
|
use dioxus_html as dioxus_elements;
|
||||||
use dioxus_router::*;
|
use dioxus_router::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
console_error_panic_hook::set_once();
|
console_error_panic_hook::set_once();
|
||||||
@@ -10,18 +11,19 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static APP: Component<()> = |cx| {
|
static APP: Component<()> = |cx| {
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
enum Route {
|
enum Route {
|
||||||
Home,
|
Home,
|
||||||
About,
|
About,
|
||||||
NotFound,
|
NotFound,
|
||||||
}
|
}
|
||||||
|
impl Default for Route {
|
||||||
|
fn default() -> Self {
|
||||||
|
Route::Home
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let route = use_router(&cx, |s| match s {
|
let route = use_router(&cx, |c| {});
|
||||||
"/" => Route::Home,
|
|
||||||
"/about" => Route::About,
|
|
||||||
_ => Route::NotFound,
|
|
||||||
});
|
|
||||||
|
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div {
|
div {
|
||||||
@@ -31,8 +33,8 @@ static APP: Component<()> = |cx| {
|
|||||||
Route::NotFound => rsx!(h1 { "NotFound" }),
|
Route::NotFound => rsx!(h1 { "NotFound" }),
|
||||||
}}
|
}}
|
||||||
nav {
|
nav {
|
||||||
Link { to: Route::Home, href: |_| "/".to_string() }
|
Link { to: Route::Home, href: "/" }
|
||||||
Link { to: Route::About, href: |_| "/about".to_string() }
|
Link { to: Route::About, href: "/about" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
+51
-67
@@ -1,72 +1,56 @@
|
|||||||
|
//! Dioxus-Router
|
||||||
|
//!
|
||||||
|
//! A simple match-based router and router service for most routing needs.
|
||||||
|
//!
|
||||||
|
//! Dioxus-Router is not a *declarative* router. Instead it uses a simple parse-match
|
||||||
|
//! pattern which can be derived via a macro.
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! fn app(cx: Scope) -> Element {
|
||||||
|
//! let route = use_router(&cx, |svc, path| {
|
||||||
|
//! match path {
|
||||||
|
//! "/about" => Route::About,
|
||||||
|
//! _ => Route::Home,
|
||||||
|
//! }
|
||||||
|
//! });
|
||||||
|
//!
|
||||||
|
//! match route {
|
||||||
|
//! Route::Home => rsx!(cx, h1 { "Home" }),
|
||||||
|
//! Route::About => rsx!(cx, h1 { "About" }),
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
|
||||||
|
mod link;
|
||||||
|
mod platform;
|
||||||
|
mod service;
|
||||||
|
mod userouter;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
use std::{cell::RefCell, rc::Rc};
|
pub use link::*;
|
||||||
|
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||||
|
pub use service::*;
|
||||||
|
pub use userouter::*;
|
||||||
|
|
||||||
use dioxus::Attribute;
|
pub trait Routable:
|
||||||
use dioxus_core as dioxus;
|
'static + Send + Clone + PartialEq + Serialize + DeserializeOwned + Default
|
||||||
|
{
|
||||||
use dioxus_core::prelude::*;
|
|
||||||
use dioxus_core_macro::{format_args_f, rsx, Props};
|
|
||||||
use dioxus_html as dioxus_elements;
|
|
||||||
// use wasm_bindgen::{JsCast, JsValue};
|
|
||||||
|
|
||||||
use crate::utils::strip_slash_suffix;
|
|
||||||
|
|
||||||
/// Initialize the app's router service and provide access to `Link` components
|
|
||||||
pub fn use_router<R: 'static>(cx: &ScopeState, f: impl Fn(&str) -> R) -> &R {
|
|
||||||
let r = f("/");
|
|
||||||
cx.use_hook(
|
|
||||||
|_| {
|
|
||||||
//
|
|
||||||
r
|
|
||||||
},
|
|
||||||
|f| f,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
impl<T> Routable for T where
|
||||||
pub trait Routable: 'static + Send + Clone + PartialEq {}
|
T: 'static + Send + Clone + PartialEq + Serialize + DeserializeOwned + Default
|
||||||
impl<T> Routable for T where T: 'static + Send + Clone + PartialEq {}
|
{
|
||||||
|
|
||||||
#[derive(Props)]
|
|
||||||
pub struct LinkProps<'a, R: Routable> {
|
|
||||||
to: R,
|
|
||||||
|
|
||||||
/// The url that gets pushed to the history stack
|
|
||||||
///
|
|
||||||
/// You can either put it your own inline method or just autoderive the route using `derive(Routable)`
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
///
|
|
||||||
/// Link { to: Route::Home, href: |_| "home".to_string() }
|
|
||||||
///
|
|
||||||
/// // or
|
|
||||||
///
|
|
||||||
/// Link { to: Route::Home, href: Route::as_url }
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
#[props(default, setter(strip_option))]
|
|
||||||
href: Option<&'a str>,
|
|
||||||
|
|
||||||
#[props(default, setter(strip_option))]
|
|
||||||
class: Option<&'a str>,
|
|
||||||
|
|
||||||
children: Element<'a>,
|
|
||||||
|
|
||||||
#[props(default)]
|
|
||||||
attributes: Option<&'a [Attribute<'a>]>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn Link<'a, R: Routable>(cx: Scope<'a, LinkProps<'a, R>>) -> Element {
|
|
||||||
// let service = todo!();
|
|
||||||
// let service: todo!() = use_router_service::<R>(&cx)?;
|
|
||||||
let class = cx.props.class.unwrap_or("");
|
|
||||||
cx.render(rsx! {
|
|
||||||
a {
|
|
||||||
href: "#",
|
|
||||||
class: "{class}",
|
|
||||||
{&cx.props.children}
|
|
||||||
// onclick: move |_| service.push_route(cx.props.to.clone()),
|
|
||||||
// href: format_args!("{}", (cx.props.href)(&cx.props.to)),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
use crate::{Routable, RouterService};
|
||||||
|
use dioxus::Attribute;
|
||||||
|
use dioxus_core as dioxus;
|
||||||
|
use dioxus_core::prelude::*;
|
||||||
|
use dioxus_core_macro::{format_args_f, rsx, Props};
|
||||||
|
use dioxus_html as dioxus_elements;
|
||||||
|
|
||||||
|
#[derive(Props)]
|
||||||
|
pub struct LinkProps<'a, R: Routable> {
|
||||||
|
to: R,
|
||||||
|
|
||||||
|
/// The url that gets pushed to the history stack
|
||||||
|
///
|
||||||
|
/// You can either put it your own inline method or just autoderive the route using `derive(Routable)`
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
///
|
||||||
|
/// Link { to: Route::Home, href: |_| "home".to_string() }
|
||||||
|
///
|
||||||
|
/// // or
|
||||||
|
///
|
||||||
|
/// Link { to: Route::Home, href: Route::as_url }
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
#[props(default, setter(strip_option))]
|
||||||
|
href: Option<&'a str>,
|
||||||
|
|
||||||
|
#[props(default, setter(strip_option))]
|
||||||
|
class: Option<&'a str>,
|
||||||
|
|
||||||
|
children: Element<'a>,
|
||||||
|
|
||||||
|
#[props(default)]
|
||||||
|
attributes: Option<&'a [Attribute<'a>]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn Link<'a, R: Routable>(cx: Scope<'a, LinkProps<'a, R>>) -> Element {
|
||||||
|
let service = cx.consume_context::<RouterService<R>>()?;
|
||||||
|
cx.render(rsx! {
|
||||||
|
a {
|
||||||
|
href: "#",
|
||||||
|
class: format_args!("{}", cx.props.class.unwrap_or("")),
|
||||||
|
{&cx.props.children}
|
||||||
|
onclick: move |_| service.push_route(cx.props.to.clone()),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1 +1,7 @@
|
|||||||
|
pub fn parse_current_route() -> String {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_base_route() -> String {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
use crate::Routable;
|
||||||
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
|
pub struct RouterService<R: Routable> {
|
||||||
|
pub(crate) regen_route: Rc<dyn Fn()>,
|
||||||
|
pub(crate) pending_routes: RefCell<Vec<R>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Routable> RouterService<R> {
|
||||||
|
pub fn current_path(&self) -> &str {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
pub fn push_route(&self, route: R) {
|
||||||
|
self.pending_routes.borrow_mut().push(route);
|
||||||
|
(self.regen_route)();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RouterCfg {
|
||||||
|
initial_route: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RouterCfg {
|
||||||
|
pub fn new(initial_route: String) -> Self {
|
||||||
|
Self { initial_route }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
|
use crate::{Routable, RouterCfg, RouterService};
|
||||||
|
use dioxus_core::ScopeState;
|
||||||
|
|
||||||
|
/// Initialize the app's router service and provide access to `Link` components
|
||||||
|
pub fn use_router<'a, R: Routable>(cx: &'a ScopeState, cfg: impl FnOnce(&mut RouterCfg)) -> &'a R {
|
||||||
|
cx.use_hook(
|
||||||
|
|_| {
|
||||||
|
let svc: RouterService<R> = RouterService {
|
||||||
|
regen_route: cx.schedule_update(),
|
||||||
|
pending_routes: RefCell::new(Vec::new()),
|
||||||
|
};
|
||||||
|
let first_path = R::default();
|
||||||
|
cx.provide_context(svc);
|
||||||
|
UseRouterInner {
|
||||||
|
svc: cx.consume_context::<RouterService<R>>().unwrap(),
|
||||||
|
history: vec![first_path],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|f| {
|
||||||
|
let mut pending_routes = f.svc.pending_routes.borrow_mut();
|
||||||
|
|
||||||
|
for route in pending_routes.drain(..) {
|
||||||
|
f.history.push(route);
|
||||||
|
}
|
||||||
|
|
||||||
|
f.history.last().unwrap()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct UseRouterInner<R: Routable> {
|
||||||
|
svc: Rc<RouterService<R>>,
|
||||||
|
history: Vec<R>,
|
||||||
|
}
|
||||||
+8
-69
@@ -246,7 +246,6 @@ impl WebsysDom {
|
|||||||
.unwrap(),
|
.unwrap(),
|
||||||
};
|
};
|
||||||
|
|
||||||
use smallstr;
|
|
||||||
use smallstr::SmallString;
|
use smallstr::SmallString;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
@@ -260,7 +259,7 @@ impl WebsysDom {
|
|||||||
self.nodes[(id as usize)] = Some(el);
|
self.nodes[(id as usize)] = Some(el);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_event_listener(&mut self, event: &'static str, scope: ScopeId, real_id: u64) {
|
fn new_event_listener(&mut self, event: &'static str, _scope: ScopeId, _real_id: u64) {
|
||||||
let event = wasm_bindgen::intern(event);
|
let event = wasm_bindgen::intern(event);
|
||||||
|
|
||||||
// attach the correct attributes to the element
|
// attach the correct attributes to the element
|
||||||
@@ -271,24 +270,9 @@ impl WebsysDom {
|
|||||||
let el = self.stack.top();
|
let el = self.stack.top();
|
||||||
|
|
||||||
let el = el.dyn_ref::<Element>().unwrap();
|
let el = el.dyn_ref::<Element>().unwrap();
|
||||||
// let el = el.dyn_ref::<Element>().unwrap();
|
|
||||||
// .expect(&format!("not an element: {:?}", el));
|
|
||||||
|
|
||||||
// let scope_id = scope.data().as_ffi();
|
|
||||||
// let scope_id = scope.0 as u64;
|
|
||||||
// "dioxus-event-click",
|
|
||||||
// "1.10"
|
|
||||||
// &format!("", scope_id, real_id),
|
|
||||||
// &format!("dioxus-event-{}", event),
|
|
||||||
// &format!("{}.{}", scope_id, real_id),
|
|
||||||
// &format!("dioxus-event-{}", event),
|
|
||||||
// &format!("{}.{}", scope_id, real_id),
|
|
||||||
|
|
||||||
el.set_attribute("dioxus-event", event).unwrap();
|
el.set_attribute("dioxus-event", event).unwrap();
|
||||||
|
|
||||||
// el.set_attribute(&format!("dioxus-event"), &format!("{}", event))
|
|
||||||
// .unwrap();
|
|
||||||
|
|
||||||
// Register the callback to decode
|
// Register the callback to decode
|
||||||
|
|
||||||
if let Some(entry) = self.listeners.get_mut(event) {
|
if let Some(entry) = self.listeners.get_mut(event) {
|
||||||
@@ -487,20 +471,17 @@ impl Stack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct DioxusWebsysEvent(web_sys::Event);
|
pub struct DioxusWebsysEvent(web_sys::Event);
|
||||||
|
|
||||||
|
// safety: currently the web is not multithreaded and our VirtualDom exists on the same thread
|
||||||
unsafe impl Send for DioxusWebsysEvent {}
|
unsafe impl Send for DioxusWebsysEvent {}
|
||||||
unsafe impl Sync for DioxusWebsysEvent {}
|
unsafe impl Sync for DioxusWebsysEvent {}
|
||||||
|
|
||||||
// trait MyTrait {}
|
|
||||||
// impl MyTrait for web_sys::Event {}
|
|
||||||
|
|
||||||
// todo: some of these events are being casted to the wrong event type.
|
// todo: some of these events are being casted to the wrong event type.
|
||||||
// We need tests that simulate clicks/etc and make sure every event type works.
|
// We need tests that simulate clicks/etc and make sure every event type works.
|
||||||
fn virtual_event_from_websys_event(event: web_sys::Event) -> Arc<dyn Any + Send + Sync> {
|
fn virtual_event_from_websys_event(event: web_sys::Event) -> Arc<dyn Any + Send + Sync> {
|
||||||
use dioxus_html::on::*;
|
use dioxus_html::on::*;
|
||||||
use dioxus_html::KeyCode;
|
use dioxus_html::KeyCode;
|
||||||
// event.prevent_default();
|
|
||||||
|
|
||||||
// use dioxus_core::events::on::*;
|
|
||||||
match event.type_().as_str() {
|
match event.type_().as_str() {
|
||||||
"copy" | "cut" | "paste" => Arc::new(ClipboardEvent {}),
|
"copy" | "cut" | "paste" => Arc::new(ClipboardEvent {}),
|
||||||
"compositionend" | "compositionstart" | "compositionupdate" => {
|
"compositionend" | "compositionstart" | "compositionupdate" => {
|
||||||
@@ -525,11 +506,7 @@ fn virtual_event_from_websys_event(event: web_sys::Event) -> Arc<dyn Any + Send
|
|||||||
which: evt.which() as usize,
|
which: evt.which() as usize,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
"focus" | "blur" => {
|
"focus" | "blur" => Arc::new(FocusEvent {}),
|
||||||
//
|
|
||||||
Arc::new(FocusEvent {})
|
|
||||||
}
|
|
||||||
// "change" => SyntheticEvent::GenericEvent(DioxusEvent::new((), DioxusWebsysEvent(event))),
|
|
||||||
|
|
||||||
// todo: these handlers might get really slow if the input box gets large and allocation pressure is heavy
|
// todo: these handlers might get really slow if the input box gets large and allocation pressure is heavy
|
||||||
// don't have a good solution with the serialized event problem
|
// don't have a good solution with the serialized event problem
|
||||||
@@ -623,7 +600,6 @@ fn virtual_event_from_websys_event(event: web_sys::Event) -> Arc<dyn Any + Send
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
"select" => Arc::new(SelectionEvent {}),
|
"select" => Arc::new(SelectionEvent {}),
|
||||||
|
|
||||||
"touchcancel" | "touchend" | "touchmove" | "touchstart" => {
|
"touchcancel" | "touchend" | "touchmove" | "touchstart" => {
|
||||||
let evt: &web_sys::TouchEvent = event.dyn_ref().unwrap();
|
let evt: &web_sys::TouchEvent = event.dyn_ref().unwrap();
|
||||||
Arc::new(TouchEvent {
|
Arc::new(TouchEvent {
|
||||||
@@ -633,9 +609,7 @@ fn virtual_event_from_websys_event(event: web_sys::Event) -> Arc<dyn Any + Send
|
|||||||
shift_key: evt.shift_key(),
|
shift_key: evt.shift_key(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
"scroll" => Arc::new(()),
|
"scroll" => Arc::new(()),
|
||||||
|
|
||||||
"wheel" => {
|
"wheel" => {
|
||||||
let evt: &web_sys::WheelEvent = event.dyn_ref().unwrap();
|
let evt: &web_sys::WheelEvent = event.dyn_ref().unwrap();
|
||||||
Arc::new(WheelEvent {
|
Arc::new(WheelEvent {
|
||||||
@@ -645,7 +619,6 @@ fn virtual_event_from_websys_event(event: web_sys::Event) -> Arc<dyn Any + Send
|
|||||||
delta_mode: evt.delta_mode(),
|
delta_mode: evt.delta_mode(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
"animationstart" | "animationend" | "animationiteration" => {
|
"animationstart" | "animationend" | "animationiteration" => {
|
||||||
let evt: &web_sys::AnimationEvent = event.dyn_ref().unwrap();
|
let evt: &web_sys::AnimationEvent = event.dyn_ref().unwrap();
|
||||||
Arc::new(AnimationEvent {
|
Arc::new(AnimationEvent {
|
||||||
@@ -654,7 +627,6 @@ fn virtual_event_from_websys_event(event: web_sys::Event) -> Arc<dyn Any + Send
|
|||||||
pseudo_element: evt.pseudo_element(),
|
pseudo_element: evt.pseudo_element(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
"transitionend" => {
|
"transitionend" => {
|
||||||
let evt: &web_sys::TransitionEvent = event.dyn_ref().unwrap();
|
let evt: &web_sys::TransitionEvent = event.dyn_ref().unwrap();
|
||||||
Arc::new(TransitionEvent {
|
Arc::new(TransitionEvent {
|
||||||
@@ -663,20 +635,11 @@ fn virtual_event_from_websys_event(event: web_sys::Event) -> Arc<dyn Any + Send
|
|||||||
pseudo_element: evt.pseudo_element(),
|
pseudo_element: evt.pseudo_element(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
"abort" | "canplay" | "canplaythrough" | "durationchange" | "emptied" | "encrypted"
|
"abort" | "canplay" | "canplaythrough" | "durationchange" | "emptied" | "encrypted"
|
||||||
| "ended" | "error" | "loadeddata" | "loadedmetadata" | "loadstart" | "pause" | "play"
|
| "ended" | "error" | "loadeddata" | "loadedmetadata" | "loadstart" | "pause" | "play"
|
||||||
| "playing" | "progress" | "ratechange" | "seeked" | "seeking" | "stalled" | "suspend"
|
| "playing" | "progress" | "ratechange" | "seeked" | "seeking" | "stalled" | "suspend"
|
||||||
| "timeupdate" | "volumechange" | "waiting" => {
|
| "timeupdate" | "volumechange" | "waiting" => Arc::new(MediaEvent {}),
|
||||||
//
|
"toggle" => Arc::new(ToggleEvent {}),
|
||||||
Arc::new(MediaEvent {})
|
|
||||||
}
|
|
||||||
|
|
||||||
"toggle" => {
|
|
||||||
//
|
|
||||||
Arc::new(ToggleEvent {})
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => Arc::new(()),
|
_ => Arc::new(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -684,6 +647,8 @@ fn virtual_event_from_websys_event(event: web_sys::Event) -> Arc<dyn Any + Send
|
|||||||
/// This function decodes a websys event and produces an EventTrigger
|
/// This function decodes a websys event and produces an EventTrigger
|
||||||
/// With the websys implementation, we attach a unique key to the nodes
|
/// With the websys implementation, we attach a unique key to the nodes
|
||||||
fn decode_trigger(event: &web_sys::Event) -> anyhow::Result<UserEvent> {
|
fn decode_trigger(event: &web_sys::Event) -> anyhow::Result<UserEvent> {
|
||||||
|
use anyhow::Context;
|
||||||
|
|
||||||
let target = event
|
let target = event
|
||||||
.target()
|
.target()
|
||||||
.expect("missing target")
|
.expect("missing target")
|
||||||
@@ -692,42 +657,16 @@ fn decode_trigger(event: &web_sys::Event) -> anyhow::Result<UserEvent> {
|
|||||||
|
|
||||||
let typ = event.type_();
|
let typ = event.type_();
|
||||||
|
|
||||||
use anyhow::Context;
|
|
||||||
|
|
||||||
let element_id = target
|
let element_id = target
|
||||||
.get_attribute("dioxus-id")
|
.get_attribute("dioxus-id")
|
||||||
.context("Could not find element id on event target")?
|
.context("Could not find element id on event target")?
|
||||||
.parse()?;
|
.parse()?;
|
||||||
|
|
||||||
// The error handling here is not very descriptive and needs to be replaced with a zero-cost error system
|
|
||||||
let val: String = target
|
|
||||||
.get_attribute("dioxus-event")
|
|
||||||
.context(format!("wrong format - received {:#?}", typ))?;
|
|
||||||
// .get_attribute(&format!("dioxus-event-{}", typ))
|
|
||||||
// .context(format!("wrong format - received {:#?}", typ))?;
|
|
||||||
|
|
||||||
let mut fields = val.splitn(3, ".");
|
|
||||||
|
|
||||||
// let gi_id = fields
|
|
||||||
// .next()
|
|
||||||
// .and_then(|f| f.parse::<u64>().ok())
|
|
||||||
// .context("failed to parse gi id")?;
|
|
||||||
|
|
||||||
// let real_id = fields
|
|
||||||
// .next()
|
|
||||||
// .and_then(|raw_id| raw_id.parse::<u64>().ok())
|
|
||||||
// .context("failed to parse real id")?;
|
|
||||||
|
|
||||||
// let triggered_scope = gi_id;
|
|
||||||
|
|
||||||
Ok(UserEvent {
|
Ok(UserEvent {
|
||||||
name: event_name_from_typ(&typ),
|
name: event_name_from_typ(&typ),
|
||||||
data: virtual_event_from_websys_event(event.clone()),
|
data: virtual_event_from_websys_event(event.clone()),
|
||||||
element: Some(ElementId(element_id)),
|
element: Some(ElementId(element_id)),
|
||||||
scope_id: None,
|
scope_id: None,
|
||||||
// scope_id: Some(ScopeId(triggered_scope as usize)),
|
|
||||||
// element: Some(ElementId(real_id as usize)),
|
|
||||||
// scope_id: Some(ScopeId(triggered_scope as usize)),
|
|
||||||
priority: dioxus_core::EventPriority::Medium,
|
priority: dioxus_core::EventPriority::Medium,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user