docs: move around examples
This commit is contained in:
1 parent
b9ff95fa12
commit
70cd46dbb2
45 files changed
+3742
-2291
No files matched your search
Vendored
+1
@@ -49,3 +49,4 @@ lodash
|
|||||||
crates.io
|
crates.io
|
||||||
utilties
|
utilties
|
||||||
js
|
js
|
||||||
|
Dyon
|
||||||
+13
-12
@@ -10,7 +10,7 @@ description = "Core functionality for Dioxus - a concurrent renderer-agnostic Vi
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
dioxus-core = { path="./packages/core", version="0.1.0" }
|
dioxus-core = { path="./packages/core", version="0.1.0" }
|
||||||
dioxus-core-macro = { path="./packages/core-macro", version="0.1.0" }
|
dioxus-core-macro = { path="./packages/core-macro", version="0.1.0" }
|
||||||
dioxus-hooks = { path="./packages/hooks", version="0.0.0" }
|
# dioxus-hooks = { path="./packages/hooks", version="0.0.0" }
|
||||||
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
@@ -30,20 +30,20 @@ split-debuginfo = "unpacked"
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
# For the tide ssr examples
|
# For the tide ssr examples
|
||||||
async-std = { version="1.9.0", features=["attributes"] }
|
# async-std = { version="1.9.0", features=["attributes"] }
|
||||||
tide = { version="0.16.0" }
|
# tide = { version="0.16.0" }
|
||||||
|
|
||||||
# For the livewview example
|
# For the livewview example
|
||||||
tide-websockets = "0.4.0"
|
# tide-websockets = "0.4.0"
|
||||||
serde_millis = "0.1"
|
# serde_millis = "0.1"
|
||||||
serde_json = "1"
|
# serde_json = "1"
|
||||||
serde = { version="1", features=['derive'] }
|
# serde = { version="1", features=['derive'] }
|
||||||
|
|
||||||
# For the doc generator
|
# For the doc generator
|
||||||
pulldown-cmark = { version="0.8.0", default-features=false }
|
# pulldown-cmark = { version="0.8.0", default-features=false }
|
||||||
|
|
||||||
dioxus-webview = { path="./packages/webview", version="0.0.0" }
|
# dioxus-webview = { path="./packages/webview", version="0.0.0" }
|
||||||
dioxus-hooks = { path="./packages/hooks", version="0.0.0" }
|
# dioxus-hooks = { path="./packages/hooks", version="0.0.0" }
|
||||||
rand = "0.8.4"
|
rand = "0.8.4"
|
||||||
|
|
||||||
|
|
||||||
@@ -51,10 +51,11 @@ rand = "0.8.4"
|
|||||||
members = [
|
members = [
|
||||||
"packages/core-macro",
|
"packages/core-macro",
|
||||||
"packages/core",
|
"packages/core",
|
||||||
"packages/web",
|
"packages/html-namespace",
|
||||||
|
# "packages/web",
|
||||||
|
# "packages/atoms",
|
||||||
# "packages/ssr",
|
# "packages/ssr",
|
||||||
# "packages/docsite",
|
# "packages/docsite",
|
||||||
# "packages/atoms",
|
|
||||||
# "packages/router",
|
# "packages/router",
|
||||||
# "packages/inputs",
|
# "packages/inputs",
|
||||||
]
|
]
|
||||||
@@ -8,10 +8,10 @@
|
|||||||
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 Example(ctx: Context<()>) -> VNode {
|
fn Example(cx: Context<()>) -> VNode {
|
||||||
let (selection, set_selection) = use_state(&ctx, || "..?");
|
let (selection, set_selection) = use_state(&cx, || "..?");
|
||||||
|
|
||||||
ctx.render(rsx! {
|
cx.render(rsx! {
|
||||||
h1 { "Hello, {selection}" }
|
h1 { "Hello, {selection}" }
|
||||||
button { "?", onclick: move |_| set_selection("world!")}
|
button { "?", onclick: move |_| set_selection("world!")}
|
||||||
button { "?", onclick: move |_| set_selection("Dioxus 🎉")}
|
button { "?", onclick: move |_| set_selection("Dioxus 🎉")}
|
||||||
@@ -19,7 +19,7 @@ fn Example(ctx: Context<()>) -> VNode {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
Dioxus can be used to deliver webapps, desktop apps, static pages, liveview apps, Android apps, iOS Apps, and more. At its core, Dioxus is entirely renderer agnostic and has great documentation for creating new renderers for any platform.
|
Dioxus can be used to deliver webapps, desktop apps, static pages, liveview apps, eventually mobile apps (WIP), and more. At its core, Dioxus is entirely renderer agnostic and has great documentation for creating new renderers for any platform.
|
||||||
|
|
||||||
If you know React, then you already know Dioxus.
|
If you know React, then you already know Dioxus.
|
||||||
|
|
||||||
@@ -29,6 +29,7 @@ If you know React, then you already know Dioxus.
|
|||||||
- Minimal boilerplate
|
- Minimal boilerplate
|
||||||
- Familiar design and semantics
|
- Familiar design and semantics
|
||||||
- Simple build, test, and deploy
|
- Simple build, test, and deploy
|
||||||
|
- Compile-time correct templating
|
||||||
- Support for html! and rsx! templating
|
- Support for html! and rsx! templating
|
||||||
- SSR, WASM, desktop, and mobile support
|
- SSR, WASM, desktop, and mobile support
|
||||||
- Powerful and simple integrated state management
|
- Powerful and simple integrated state management
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
|
#![allow(unused)]
|
||||||
/*
|
/*
|
||||||
This example shows how to encapsulate sate in dioxus components with the reducer pattern.
|
This example shows how to encapsulate sate in dioxus components with the reducer pattern.
|
||||||
This pattern is very useful when a single component can handle many types of input that can
|
This pattern is very useful when a single component can handle many types of input that can
|
||||||
be represented by an enum. This particular pattern is very powerful in rust where ADTs can simplify
|
be represented by an enum. This particular pattern is very powerful in rust where ADTs can simplify
|
||||||
much of the traditional reducer boilerplate.
|
much of the traditional reducer boilerplate.
|
||||||
*/
|
*/
|
||||||
#![allow(unused)]
|
|
||||||
use std::future::Future;
|
|
||||||
|
|
||||||
use dioxus::hooks::use_reducer;
|
fn main() {}
|
||||||
use dioxus_ssr::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
use std::future::Future;
|
||||||
|
|
||||||
enum Actions {
|
enum Actions {
|
||||||
Pause,
|
Pause,
|
||||||
@@ -88,8 +88,6 @@ enum KindaState {
|
|||||||
static EnumReducer: FC<()> = |ctx| {
|
static EnumReducer: FC<()> = |ctx| {
|
||||||
let (state, reduce) = use_reducer(&ctx, || KindaState::Ready, |cur, new| *cur = new);
|
let (state, reduce) = use_reducer(&ctx, || KindaState::Ready, |cur, new| *cur = new);
|
||||||
|
|
||||||
let contents = helper(&ctx);
|
|
||||||
|
|
||||||
let status = match state {
|
let status = match state {
|
||||||
KindaState::Ready => "Ready",
|
KindaState::Ready => "Ready",
|
||||||
KindaState::Complete => "Complete",
|
KindaState::Complete => "Complete",
|
||||||
@@ -99,7 +97,6 @@ static EnumReducer: FC<()> = |ctx| {
|
|||||||
ctx.render(rsx! {
|
ctx.render(rsx! {
|
||||||
div {
|
div {
|
||||||
h1 {"{status}"}
|
h1 {"{status}"}
|
||||||
{contents}
|
|
||||||
button {
|
button {
|
||||||
"Set Ready"
|
"Set Ready"
|
||||||
onclick: move |_| reduce(KindaState::Ready)
|
onclick: move |_| reduce(KindaState::Ready)
|
||||||
@@ -126,12 +123,6 @@ static EnumReducer: FC<()> = |ctx| {
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
fn helper(ctx: &Context) -> VNode {
|
|
||||||
ctx.render(rsx! {
|
|
||||||
div {}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Demonstrate how the DebugRenderer can be used to unit test components without needing a browser
|
/// Demonstrate how the DebugRenderer can be used to unit test components without needing a browser
|
||||||
/// These tests can run locally.
|
/// These tests can run locally.
|
||||||
/// They use the "compare" method of the debug renderer to do partial tree compares for succint
|
/// They use the "compare" method of the debug renderer to do partial tree compares for succint
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
//! Example App
|
|
||||||
//! --------------
|
|
||||||
//!
|
|
||||||
//! This example shows how to use the cross-platform abilities of dioxus to generate everything a dioxus app might need.
|
|
||||||
//! All of your apps will look like this.
|
|
||||||
//!
|
|
||||||
//! cargo run --features dioxus/static
|
|
||||||
|
|
||||||
use std::u32;
|
|
||||||
|
|
||||||
use dioxus::prelude::Context;
|
|
||||||
|
|
||||||
// #[allow(unused_lifetimes)]
|
|
||||||
#[derive(Debug, PartialEq, Hash)]
|
|
||||||
struct Pool<'a> {
|
|
||||||
a: u32,
|
|
||||||
b: &'a str,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct UserData {}
|
|
||||||
type Query<In, Out> = fn(&Pool, In) -> Result<Out, ()>;
|
|
||||||
// type Query<In, Out> = fn(&Pool, In) -> Out;
|
|
||||||
|
|
||||||
static GET_USER: Query<String, Vec<UserData>> = |pool, name| {
|
|
||||||
//
|
|
||||||
let b = Ok(())?;
|
|
||||||
let b = Ok(())?;
|
|
||||||
let b = Ok(())?;
|
|
||||||
let b = Ok(())?;
|
|
||||||
let b = Ok(())?;
|
|
||||||
let b = Ok(())?;
|
|
||||||
todo!()
|
|
||||||
};
|
|
||||||
|
|
||||||
static SET_USER: Query<String, Vec<UserData>> = |pool, name| {
|
|
||||||
//
|
|
||||||
todo!()
|
|
||||||
};
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
// // returns a future
|
|
||||||
// let user_data = use_db(&ctx, GET_USER, || "Bill");
|
|
||||||
|
|
||||||
// use_try_suspense(&ctx, async move {
|
|
||||||
// match user_data.await? {
|
|
||||||
// Ok() => {}
|
|
||||||
// Err(err) => {}
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fn use_try_suspense(ctx: &Context<()>) {
|
|
||||||
// let c: Result<(), ()> = {
|
|
||||||
// // let b = Ok(());
|
|
||||||
// // b?
|
|
||||||
// };
|
|
||||||
}
|
|
||||||
|
|
||||||
mod launches {
|
|
||||||
#[cfg(feature = "wasm")]
|
|
||||||
fn launch() {
|
|
||||||
// launch the wasm_rednerer
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "static")]
|
|
||||||
fn launch() {
|
|
||||||
// render the tree to text
|
|
||||||
}
|
|
||||||
|
|
||||||
// #[cfg(features = "server")]
|
|
||||||
// fn launch() {
|
|
||||||
// // launch the app
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[cfg(features = "liveview")]
|
|
||||||
// fn launch() {
|
|
||||||
// // launch the app
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[cfg(features = "desktop")]
|
|
||||||
// fn launch() {
|
|
||||||
// // launch the app
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[cfg(features = "android")]
|
|
||||||
// fn launch() {
|
|
||||||
// // launch a simulator in dev mode
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[cfg(features = "ios")]
|
|
||||||
// fn launch() {
|
|
||||||
// // launch a simulator in dev mode
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
//! Example: External Updates
|
|
||||||
//! -------------------------
|
|
||||||
//!
|
|
||||||
//! Cause updates to the VirtualDOM state from outside the component lifecycle.
|
|
||||||
//! The root props could be changed or the use_receiver hook could be used.
|
|
||||||
//!
|
|
||||||
//!
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let (recv, sender) = channel();
|
|
||||||
|
|
||||||
async_std::task::spawn({
|
|
||||||
for location in ["a", "b", "c", "d"] {
|
|
||||||
sender.send(location);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let app = diouxs_webview::launch_with_props(App, RootProps { recv }).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct RootProps {
|
|
||||||
navigator: Receiver<&'static str>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn App(ctx: Context, props: &RootProps) -> VNode {
|
|
||||||
let router = use_router(&ctx, |router| {});
|
|
||||||
|
|
||||||
let navigator = use_history(&ctx);
|
|
||||||
|
|
||||||
use_receiver(&ctx, || ctx.recv.clone(), |to| navigator.navigate(to));
|
|
||||||
|
|
||||||
ctx.render(rsx! {
|
|
||||||
div {
|
|
||||||
a { href: "/dogs/"}
|
|
||||||
a { href: "/cats/"}
|
|
||||||
{content}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
File renamed without changes.
@@ -1,65 +0,0 @@
|
|||||||
use dioxus::prelude::*;
|
|
||||||
use tide::{with_state, Request};
|
|
||||||
use tide_websockets::{WebSocket, WebSocketConnection};
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let mut g = Context { props: &() };
|
|
||||||
LiveComponent(&mut g);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
async fn server() -> tide::Result<()> {
|
|
||||||
// Build the API
|
|
||||||
let mut app = tide::with_state(());
|
|
||||||
app.at("/app").get(WebSocket::new(live_handler));
|
|
||||||
|
|
||||||
// Launch the server
|
|
||||||
app.listen("127.0.0.1:8080").await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn live_handler(req: Request<()>, stream: WebSocketConnection) -> tide::Result<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
static LiveComponent: FC<()> = |ctx| {
|
|
||||||
use_live_component(
|
|
||||||
ctx,
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
|| {
|
|
||||||
// Always wait on the context's live component API
|
|
||||||
// suspend the component until this promise arrives, or fall back
|
|
||||||
let g = &LiveComponent;
|
|
||||||
html! {
|
|
||||||
<div>
|
|
||||||
{"Hello world!"}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
},
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
|| {
|
|
||||||
// actually use the code originally specified in the component
|
|
||||||
// this gives use the function pointer. We don't necessarily get to hash the same pointer between two binaries
|
|
||||||
// Some key will need to be made, likely based on the function parameter
|
|
||||||
let g = &LiveComponent;
|
|
||||||
html! {
|
|
||||||
<div>
|
|
||||||
{"Hello world!"}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
/// This hooks connects with the LiveContext at the top of the app.
|
|
||||||
fn use_live_component<T>(ctx: &mut Context<T>, b: fn() -> VNode) -> VNode {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// LiveContext is a special, limited form of the context api that disallows the "Context API"
|
|
||||||
/// Its purpose is to shield the original Context where calls to use_context will fail. Instead of panicing and confusing
|
|
||||||
/// users, we simply disallow usage of "use_context" and "childen". In effect, only serialiable props can be allowed.
|
|
||||||
///
|
|
||||||
/// In the future, we might try to lift these restrictions (esp children since they are virtual) but are limited via the web connection
|
|
||||||
struct LiveContext {}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
//! Dioxus Router
|
|
||||||
//! -------------
|
|
||||||
//!
|
|
||||||
//! This exmaple showcases the Dioxus Router hook. This hook makes it possible to using the browser's navigation API to
|
|
||||||
//! display different content based on the Page's URL. The hook provides a configuration object that calls various
|
|
||||||
//! specified callbacks whenever the page URL changes. Using this hook should feel like building a "URL directory", similar
|
|
||||||
//! to how Tide handles paths.
|
|
||||||
|
|
||||||
use dioxus::prelude::*;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
diouxs_webview::launch(App).run().await
|
|
||||||
}
|
|
||||||
|
|
||||||
fn App(ctx: Context<()>) -> VNode {
|
|
||||||
let router = use_router(&ctx, |router| {
|
|
||||||
//
|
|
||||||
router.get("/dogs/:dogId/").render(|ctx, request| {
|
|
||||||
rsx! {
|
|
||||||
div {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get("/cats/:catId/").render(|ctx, request| {
|
|
||||||
rsx! {
|
|
||||||
div {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
ctx.render(rsx! {
|
|
||||||
div {
|
|
||||||
a { href: "/dogs/"}
|
|
||||||
a { href: "/cats/"}
|
|
||||||
{router.render()}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
fn main() {}
|
||||||
|
|
||||||
|
mod components {
|
||||||
|
use baller::Baller;
|
||||||
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
|
fn example() {
|
||||||
|
let g = rsx! {
|
||||||
|
div {
|
||||||
|
crate::components::baller::Baller {}
|
||||||
|
baller::Baller {
|
||||||
|
}
|
||||||
|
Taller {
|
||||||
|
a: "asd"
|
||||||
|
}
|
||||||
|
baller::Baller {}
|
||||||
|
baller::Baller {}
|
||||||
|
Baller {
|
||||||
|
// todo: manual props
|
||||||
|
// {...BallerProps {}}
|
||||||
|
}
|
||||||
|
div {
|
||||||
|
a: "asd",
|
||||||
|
a: "asd",
|
||||||
|
a: "asd",
|
||||||
|
a: "asd",
|
||||||
|
div {
|
||||||
|
"asdas",
|
||||||
|
"asdas",
|
||||||
|
"asdas",
|
||||||
|
"asdas",
|
||||||
|
div {},
|
||||||
|
div {
|
||||||
|
class: "asdasd"
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
mod baller {
|
||||||
|
use super::*;
|
||||||
|
pub struct BallerProps {}
|
||||||
|
|
||||||
|
pub fn Baller(ctx: Context<()>) -> VNode {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Props)]
|
||||||
|
pub struct TallerProps {
|
||||||
|
a: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn Taller(ctx: Context<TallerProps>) -> VNode {
|
||||||
|
let b = true;
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
//! DioxusLabs Webiste
|
|
||||||
//! ------------------
|
|
||||||
//!
|
|
||||||
//! This is the example powering the DioxusLabs website :)
|
|
||||||
//! It's always good to dogfood your code, right?
|
|
||||||
|
|
||||||
use dioxus::prelude::*;
|
|
||||||
|
|
||||||
fn main() {}
|
|
||||||
|
|
||||||
mod state {
|
|
||||||
pub struct AppState {
|
|
||||||
cur_page: Route,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum Route {
|
|
||||||
Homepage,
|
|
||||||
Docs,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static APP: FC<()> = |ctx| {
|
|
||||||
ctx.render(html! {
|
|
||||||
<div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Draw the navbar on top of the screen
|
|
||||||
static Navbar: FC<state::Route> = |ctx| {
|
|
||||||
ctx.render(html! {
|
|
||||||
<div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
static Homepage: FC<()> = |ctx| {
|
|
||||||
ctx.render(html! {
|
|
||||||
<div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
})
|
|
||||||
};
|
|
||||||
+3
-3
@@ -9,8 +9,8 @@
|
|||||||
|
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
async fn main() {
|
fn main() {
|
||||||
dioxus_webview::launch(|ctx| {
|
dioxus::webview::launch(|ctx| {
|
||||||
let (count, set_count) = use_state(&ctx, || 0);
|
let (count, set_count) = use_state(&ctx, || 0);
|
||||||
|
|
||||||
ctx.render(rsx! {
|
ctx.render(rsx! {
|
||||||
@@ -23,5 +23,5 @@ async fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}).await;
|
});
|
||||||
}
|
}
|
||||||
+2
-2
@@ -113,8 +113,8 @@ Welcome to the first iteration of the Dioxus Virtual DOM! This release brings su
|
|||||||
- [ ] Re-exported through the `dioxus` crate (not core)
|
- [ ] Re-exported through the `dioxus` crate (not core)
|
||||||
- [ ] Hooks
|
- [ ] Hooks
|
||||||
- [ ] Re-exported through the `dioxus` crate (not essential to core virtualdom)
|
- [ ] Re-exported through the `dioxus` crate (not essential to core virtualdom)
|
||||||
- [ ] fragments
|
- [x] fragments
|
||||||
- [ ] pass-thru components
|
- [x] pass-thru components
|
||||||
|
|
||||||
## Less-essential todos
|
## Less-essential todos
|
||||||
|
|
||||||
|
|||||||
+5
-5
@@ -13,20 +13,20 @@ Sorted by priority
|
|||||||
| Context | ✅ | ✅ | share state through the tree |
|
| Context | ✅ | ✅ | share state through the tree |
|
||||||
| Hook | ✅ | ✅ | memory cells in components |
|
| Hook | ✅ | ✅ | memory cells in components |
|
||||||
| SSR | ✅ | ✅ | render directly to string |
|
| SSR | ✅ | ✅ | render directly to string |
|
||||||
| Runs natively | ✅ | 👀 | runs as a sharable binary |
|
| Runs natively | ✅ | 👀 | runs as a portable binary w/ extra tooling |
|
||||||
| Component Children | ✅ | ✅ | ctx.children() as a list of nodes |
|
| Component Children | ✅ | ✅ | cx.children() as a list of nodes |
|
||||||
| Null components | ✅ | ✅ | allow returning no components |
|
| Null components | ✅ | ✅ | allow returning no components |
|
||||||
| No-div components | ✅ | ✅ | components that render components |
|
| No-div components | ✅ | ✅ | components that render components |
|
||||||
| Fragments | ✅ | ✅ | rsx! can return multiple elements without a root |
|
| Fragments | ✅ | ✅ | rsx! can return multiple elements without a root |
|
||||||
| NodeRef | 👀 | ✅ | gain direct access to nodes |
|
| NodeRef | 👀 | ✅ | gain direct access to nodes |
|
||||||
| Controlled Inputs | 👀 | ✅ | stateful wrappers around inputs |
|
| Controlled Inputs | ✅ | ✅ | stateful wrappers around inputs |
|
||||||
| CSS/Inline Styles | 🛠 | ✅ | syntax for inline/conditional styles |
|
| CSS/Inline Styles | 🛠 | ✅ | syntax for inline/conditional styles |
|
||||||
| 1st class global state | 🛠 | ✅ | redux/recoil/mobx on top of context |
|
| 1st class global state | 🛠 | ✅ | redux/recoil/mobx on top of context |
|
||||||
| ----- Phase 2 ----- | ----- | ----- | ----- |
|
| ----- Phase 2 ----- | ----- | ----- | ----- |
|
||||||
| 1st class router | 👀 | ✅ | Hook built on top of history |
|
| 1st class router | 👀 | ✅ | Hook built on top of history |
|
||||||
| Assets | 👀 | ✅ | include css/svg/img url statically |
|
| Assets | 👀 | ✅ | include css/svg/img url statically |
|
||||||
| Integrated classnames | 🛠 | 👀 | built-in `classnames` |
|
| Integrated classnames | 🛠 | 👀 | built-in `classnames` |
|
||||||
| Suspense | 👀 | 👀 | schedule future render from future |
|
| Suspense | 👀 | 👀 | schedule future render from future/promise |
|
||||||
| Transition | 👀 | 👀 | High-level control over suspense |
|
| Transition | 👀 | 👀 | High-level control over suspense |
|
||||||
| Animation | 👀 | ✅ | Spring-style animations |
|
| Animation | 👀 | ✅ | Spring-style animations |
|
||||||
| Mobile | 👀 | ✅ | Render with cacao |
|
| Mobile | 👀 | ✅ | Render with cacao |
|
||||||
@@ -34,7 +34,7 @@ Sorted by priority
|
|||||||
| 3D Renderer | 👀 | ✅ | react-three-fiber |
|
| 3D Renderer | 👀 | ✅ | react-three-fiber |
|
||||||
| ----- Phase 3 ----- | ----- | ----- | ----- |
|
| ----- Phase 3 ----- | ----- | ----- | ----- |
|
||||||
| Portal | 👀 | ✅ | cast elements through tree |
|
| Portal | 👀 | ✅ | cast elements through tree |
|
||||||
| Error/Panic boundary | 👀 | ✅ | catch panics and display BSOD |
|
| Error/Panic boundary | 👀 | ✅ | catch panics and display custom BSOD |
|
||||||
| Code-splitting | 👀 | ✅ | Make bundle smaller/lazy |
|
| Code-splitting | 👀 | ✅ | Make bundle smaller/lazy |
|
||||||
| LiveView | 👀 | 👀 | Example for SSR + WASM apps |
|
| LiveView | 👀 | 👀 | Example for SSR + WASM apps |
|
||||||
|
|
||||||
|
|||||||
+36
-39
@@ -1,42 +1,42 @@
|
|||||||
<div align="center">
|
<div align="center">
|
||||||
<h1>⚛ Atoms</h1>
|
<h1>⚛ Dirac</h1>
|
||||||
<p>
|
<p>
|
||||||
<strong>Global state for Dioxus</strong>
|
<strong>Global state for Dioxus</strong>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Atoms is a global state management toolkit for Dioxus, built on the concept of composable atoms of state:
|
Dirac is a global state management toolkit for Dioxus, built on the concept of composable atoms of state:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
const COUNT: Atom<u32> = |_| 0;
|
const COUNT: Atom<u32> = |_| 0;
|
||||||
|
|
||||||
const Incr: FC<()> = |cx| {
|
const Incr: FC<()> = |cx| {
|
||||||
let (count, set_count) = Atoms::use_read_write(&cx, &COUNT);
|
let (count, set_count) = dirac::use_read_write(&cx, &COUNT);
|
||||||
cx.render(rsx!( button { onclick: move |_| set_count(count + 1), "+" } ))
|
rsx!(in cx, button { onclick: move |_| set_count(count + 1), "+" } )
|
||||||
}
|
}
|
||||||
|
|
||||||
const Decr: FC<()> = |cx| {
|
const Decr: FC<()> = |cx| {
|
||||||
let (count, set_count) = Atoms::use_read_write(&cx, &COUNT);
|
let (count, set_count) = dirac::use_read_write(&cx, &COUNT);
|
||||||
cx.render(rsx!( button { onclick: move |_| set_count(count - 1), "-" } ))
|
rsx!(in cx, button { onclick: move |_| set_count(count - 1), "-" } )
|
||||||
}
|
}
|
||||||
|
|
||||||
const App: FC<()> = |cx| {
|
const App: FC<()> = |cx| {
|
||||||
let count = Atoms::use_read(&cx, &COUNT);
|
let count = dirac::use_read(&cx, &COUNT);
|
||||||
cx.render(rsx!{
|
cx.render(rsx!{
|
||||||
div {
|
"Count is {count}"
|
||||||
"Count is {count}"
|
Incr {}
|
||||||
Incr {}
|
Decr {}
|
||||||
Decr {}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Atoms.rs provides a global state management API for Dioxus apps built on the concept of "atomic state." Instead of grouping state together into a single bundle ALA Redux, Atom provides individual building blocks of state called Atoms. These atoms can be set/get anywhere in the app and combined to craft complex state. Atom should be easier to learn and more efficient than Redux.
|
Dirac provides a global state management API for Dioxus apps built on the concept of "atomic state." Instead of grouping state together into a single bundle like Redux, Dyon provides individual building blocks of state called atoms. These atoms can be set/get anywhere in the app and combined to craft complex state. Dyon should be easier to learn, more efficient than Redux, and allow you to build complex Dioxus apps with relative ease.
|
||||||
|
|
||||||
Atoms.rs is officially supported by the Dioxus team. By doing so, we are "planting our flag in the sand" for atomic state management instead of bundled (Redux-style) state management. Atomic state management fits well with the internals of Dioxus and idiomatic Rust, meaning Atoms.rs state management will be faster, more efficient, and less sensitive to data races than Redux-style apps.
|
## Support
|
||||||
|
|
||||||
Internally, Dioxus uses batching to speed up linear-style operations. Atoms.rs integrates with this batching optimization, making app-wide state changes extremely fast. This way, Atoms.rs can be pushed significantly harder than Redux without the need to enable/disable debug flags to prevent performance slowdowns.
|
Dyon is officially supported by the Dioxus team. By doing so, we are "planting our flag in the sand" for atomic state management instead of bundled (Redux-style) state management. Atomic state management fits well with the internals of Dioxus and idiomatic Rust, meaning Dirac state management will be faster, more efficient, and less sensitive to data races than Redux-style apps.
|
||||||
|
|
||||||
|
Internally, Dioxus uses batching to speed up linear-style operations. Dirac integrates with this batching optimization, making app-wide state changes extremely fast. This way, Dirac can be pushed significantly harder than Redux without the need to enable/disable debug flags to prevent performance slowdowns.
|
||||||
|
|
||||||
# Guide
|
# Guide
|
||||||
|
|
||||||
@@ -45,19 +45,19 @@ Internally, Dioxus uses batching to speed up linear-style operations. Atoms.rs i
|
|||||||
A simple atom of state is defined globally as a const:
|
A simple atom of state is defined globally as a const:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
const Light: Atom<&str> = |_| "Green";
|
const LightColor: Atom<&str> = |_| "Green";
|
||||||
```
|
```
|
||||||
|
|
||||||
This atom of state is initialized with a value of `"Green"`. The atom that is returned does not actually contain any values. Instead, the atom's key - which is automatically generated in this instance - is used in the context of a Atom App.
|
This atom of state is initialized with a value of `"Green"`. The atom that is returned does not actually contain any values. Instead, the atom's key - which is automatically generated in this instance - is used in the context of a Dioxus App.
|
||||||
|
|
||||||
This is then later used in components like so:
|
This is then later used in components like so:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn App(ctx: Context<()>) -> VNode {
|
fn App(ctx: Context<()>) -> VNode {
|
||||||
// The Atom root must be initialized at the top of the application before any use_Atom hooks
|
// The Atom root must be initialized at the top of the application before any use_Atom hooks
|
||||||
Atoms::init_root(&ctx, |_| {});
|
dirac::intialize_root(&ctx, |_| {});
|
||||||
|
|
||||||
let color = Atoms::use_read(&ctx, Light);
|
let color = dirac::use_read(&ctx, &LightColor);
|
||||||
|
|
||||||
ctx.render(rsx!( h1 {"Color of light: {color}"} ))
|
ctx.render(rsx!( h1 {"Color of light: {color}"} ))
|
||||||
}
|
}
|
||||||
@@ -67,15 +67,13 @@ Atoms are considered "Writable" objects since any consumer may also set the Atom
|
|||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn App(ctx: Context<()>) -> VNode {
|
fn App(ctx: Context<()>) -> VNode {
|
||||||
let color = Atoms::use_read(&ctx, Light);
|
let color = dirac::use_read(&ctx, &LightColor);
|
||||||
let set_color = Atoms::use_write(&ctx, Light);
|
let set_color = dirac::use_write(&ctx, &LightColor);
|
||||||
ctx.render(rsx!(
|
ctx.render(rsx!(
|
||||||
div {
|
h1 { "Color: {color}" }
|
||||||
h1 { "Color: {color}" }
|
button { onclick: move |_| set_color("red"), "red" }
|
||||||
button { onclick: move |_| set_color("red"), "red" }
|
button { onclick: move |_| set_color("yellow"), "yellow" }
|
||||||
button { onclick: move |_| set_color("yellow"), "yellow" }
|
button { onclick: move |_| set_color("green"), "green" }
|
||||||
button { onclick: move |_| set_color("green"), "green" }
|
|
||||||
}
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -84,12 +82,10 @@ fn App(ctx: Context<()>) -> VNode {
|
|||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn App(ctx: Context<()>) -> VNode {
|
fn App(ctx: Context<()>) -> VNode {
|
||||||
let (color, set_color) = Atoms::use_read_write(ctx, Light);
|
let (color, set_color) = dirac::use_read_write(&ctx, &Light);
|
||||||
ctx.render(rsx!(
|
ctx.render(rsx!(
|
||||||
div {
|
h1 { "Color: {color}" }
|
||||||
h1 { "Color: {color}" }
|
button { onclick: move |_| set_color("red"), "red" }
|
||||||
button { onclick: move |_| set_color("red"), "red" }
|
|
||||||
}
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -98,7 +94,7 @@ fn App(ctx: Context<()>) -> VNode {
|
|||||||
|
|
||||||
Selectors are a concept popular in the JS world as a way of narrowing down a selection of state outside the VDOM lifecycle. Selectors have two functions: 1 summarize/narrow down some complex state and 2) memoize calculations.
|
Selectors are a concept popular in the JS world as a way of narrowing down a selection of state outside the VDOM lifecycle. Selectors have two functions: 1 summarize/narrow down some complex state and 2) memoize calculations.
|
||||||
|
|
||||||
Selectors are only `readable` - they cannot be set. This differs from AtomJS where selectors _are_ `writable`. Selectors, as you might've guessed, "select" some data from atoms and other selectors.
|
Selectors are only `readable` - they cannot be set. This differs from RecoilJS where selectors _are_ `writable`. Selectors, as you might've guessed, "select" some data from atoms and other selectors.
|
||||||
|
|
||||||
Selectors provide a `SelectorApi` which essentially exposes a read-only `AtomApi`. They have the `get` method which allows any readable valued to be obtained for the purpose of generating a new value. A `Selector` may only return `'static` data, however `SelectorBorrowed` may return borrowed data.
|
Selectors provide a `SelectorApi` which essentially exposes a read-only `AtomApi`. They have the `get` method which allows any readable valued to be obtained for the purpose of generating a new value. A `Selector` may only return `'static` data, however `SelectorBorrowed` may return borrowed data.
|
||||||
|
|
||||||
@@ -121,9 +117,12 @@ const ThreeChars: SelectorBorrowed<str> = |api| api.get(&Light).chars().take(3).
|
|||||||
Unfortunately, Rust tries to coerce `T` (the type in the Selector generics) to the `'static` lifetime because we defined it as a static. `SelectorBorrowed` defines a type for a function that returns `&T` and provides the right lifetime for `T`. If you don't like having to use a dedicated `Borrowed` type, regular functions work too:
|
Unfortunately, Rust tries to coerce `T` (the type in the Selector generics) to the `'static` lifetime because we defined it as a static. `SelectorBorrowed` defines a type for a function that returns `&T` and provides the right lifetime for `T`. If you don't like having to use a dedicated `Borrowed` type, regular functions work too:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
// An atom
|
||||||
fn Light(api: &mut AtomBuilder) -> &'static str {
|
fn Light(api: &mut AtomBuilder) -> &'static str {
|
||||||
"Green"
|
"Green"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A selector
|
||||||
fn ThreeChars(api: &mut SelectorApi) -> &'static str {
|
fn ThreeChars(api: &mut SelectorApi) -> &'static str {
|
||||||
api.get(&Light).chars().take(3).unwrap()
|
api.get(&Light).chars().take(3).unwrap()
|
||||||
}
|
}
|
||||||
@@ -134,7 +133,7 @@ Returning borrowed data is generally frowned upon, but may be useful when used w
|
|||||||
- If a selected value equals its previous selection (via PartialEq), the old value must be kept around to avoid evaluating subscribed components.
|
- If a selected value equals its previous selection (via PartialEq), the old value must be kept around to avoid evaluating subscribed components.
|
||||||
- It's unlikely that a change in a dependency's data will not change the selector's output.
|
- It's unlikely that a change in a dependency's data will not change the selector's output.
|
||||||
|
|
||||||
In general, borrowed selectors introduce a slight memory overhead as they need to retain previous state to safely memoize downstream subscribers. The amount of state kept around scales with the amount of `gets` in a selector - though as the number of dependencies increase, the less likely the selector actually stays memoized. Atom tries to optimize this behavior the best it can to balance component evaluations with memory overhead.
|
In general, borrowed selectors introduce a slight memory overhead as they need to retain previous state to safely memoize downstream subscribers. The amount of state kept around scales with the amount of `gets` in a selector - though as the number of dependencies increase, the less likely the selector actually stays memoized. Dirac tries to optimize this behavior the best it can to balance component evaluations with memory overhead.
|
||||||
|
|
||||||
## Families
|
## Families
|
||||||
|
|
||||||
@@ -156,12 +155,10 @@ Whenever you `select` on a `Family`, the ID of the entry is tracked. Other subsc
|
|||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn App(ctx: Context<()>) -> VNode {
|
fn App(ctx: Context<()>) -> VNode {
|
||||||
let (rating, set_rating) = Atoms::use_read_write(ctx, CloudRatings.select("AWS"));
|
let (rating, set_rating) = dirac::use_read_write(ctx, CloudRatings.select("AWS"));
|
||||||
ctx.render(rsx!(
|
ctx.render(rsx!(
|
||||||
div {
|
h1 { "AWS rating: {rating}" }
|
||||||
h1 { "AWS rating: {rating}" }
|
button { onclick: move |_| set_rating((rating + 1) % 5), "incr" }
|
||||||
button { onclick: move |_| set_rating((rating + 1) % 5), "incr" }
|
|
||||||
}
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
fn main() {}
|
||||||
|
|
||||||
|
use dioxus_core::prelude::*;
|
||||||
|
|
||||||
|
static App: FC<()> = |ctx| {
|
||||||
|
//
|
||||||
|
use_initialize_app()
|
||||||
|
};
|
||||||
|
|
||||||
|
fn use_initialize_app(cx: &impl Scoped) {}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
# Asynchronous state management
|
||||||
|
|
||||||
|
Async is pervasive in web frontends. The web heavily relies on external resources like http, requests, storage, etc.
|
||||||
|
|
||||||
|
## How does Redux do it?
|
||||||
|
|
||||||
|
Thunks
|
||||||
|
|
||||||
|
- call a synchronous dispatch function
|
||||||
|
- queues an asynchronous "thunk"
|
||||||
|
- thunks may not modify state, may only call reducers
|
||||||
|
|
||||||
|
## How does Recoil do it?
|
||||||
|
|
||||||
|
useRecoilCallback
|
||||||
|
|
||||||
|
- call an async function
|
||||||
|
- get/set any atom with the Recoil API
|
||||||
|
- synchronously set values
|
||||||
|
|
||||||
|
async atoms
|
||||||
|
|
||||||
|
- atom's "get" returns a promise that resolves to the atom's value
|
||||||
|
- if a request fails
|
||||||
|
|
||||||
|
## How should we do it?
|
||||||
|
|
||||||
|
```rust
|
||||||
|
const USER_ID: Atom<String> = |atom| String::from("ThisIsDummyData");
|
||||||
|
const TITLE: Selector<Result<String>> = |atom| {
|
||||||
|
atom.async_get(|api| async {
|
||||||
|
let user_id = api.get(ID);
|
||||||
|
let resp: Result<UserData> = surf::get(format!("https://myapi.com/users/{}", user_id))
|
||||||
|
.recv_json()
|
||||||
|
.await;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deref)]
|
||||||
|
struct UserManager(RecoilApi);
|
||||||
|
impl UserManager {
|
||||||
|
fn load_user_data() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn Example(cx: Context<()>) -> Result<VNode> {
|
||||||
|
let title = use_read(cx, TITLE)?;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn use_initialize_app() {
|
||||||
|
// initialize theme systems
|
||||||
|
// initialize websocket connection
|
||||||
|
// create task queue
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -5,13 +5,8 @@ use dioxus_core::prelude::*;
|
|||||||
static Example: FC<()> = |ctx| {
|
static Example: FC<()> = |ctx| {
|
||||||
ctx.render(dioxus_core::prelude::LazyNodes::new(move |ctx| {
|
ctx.render(dioxus_core::prelude::LazyNodes::new(move |ctx| {
|
||||||
let bump = ctx.bump();
|
let bump = ctx.bump();
|
||||||
dioxus::builder::ElementBuilder::new(ctx, "h1")
|
dioxus_core::builder::ElementBuilder::new(ctx, "h1")
|
||||||
.children([{
|
.children([dioxus_core::builder::text3(bump, format_args!("hello"))])
|
||||||
use bumpalo::core_alloc::fmt::Write;
|
|
||||||
let mut s = bumpalo::collections::String::new_in(bump);
|
|
||||||
write!(s, "hello");
|
|
||||||
dioxus::builder::text2(s)
|
|
||||||
}])
|
|
||||||
.finish()
|
.finish()
|
||||||
}))
|
}))
|
||||||
};
|
};
|
||||||
@@ -22,9 +17,11 @@ struct Props {
|
|||||||
static Example2: FC<Props> = |ctx| {
|
static Example2: FC<Props> = |ctx| {
|
||||||
ctx.render(dioxus_core::prelude::LazyNodes::new(move |__ctx| {
|
ctx.render(dioxus_core::prelude::LazyNodes::new(move |__ctx| {
|
||||||
let bump = __ctx.bump();
|
let bump = __ctx.bump();
|
||||||
dioxus::builder::ElementBuilder::new(__ctx, "h1")
|
dioxus_core::builder::ElementBuilder::new(__ctx, "h1")
|
||||||
.children([{ dioxus::builder::text3(bump, format_args!("{}", ctx.text)) }])
|
.children([dioxus_core::builder::text3(
|
||||||
// .children([{ dioxus::builder::text3(bump, format_args!("hello")) }])
|
bump,
|
||||||
|
format_args!("{}", ctx.props.text),
|
||||||
|
)])
|
||||||
.finish()
|
.finish()
|
||||||
}))
|
}))
|
||||||
};
|
};
|
||||||
@@ -4,14 +4,12 @@
|
|||||||
//! It's slightly more advanced than just cloning, but well worth the investment.
|
//! It's slightly more advanced than just cloning, but well worth the investment.
|
||||||
//!
|
//!
|
||||||
//! If you use the FC macro, we handle the lifetimes automatically, making it easy to write efficient & performant components.
|
//! If you use the FC macro, we handle the lifetimes automatically, making it easy to write efficient & performant components.
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
||||||
use std::{borrow::Borrow, ops::Deref, rc::Rc};
|
|
||||||
|
|
||||||
use dioxus_core::prelude::*;
|
use dioxus_core::prelude::*;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
struct Props {
|
struct AppProps {
|
||||||
items: Vec<Rc<ListItem>>,
|
items: Vec<Rc<ListItem>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,67 +19,54 @@ struct ListItem {
|
|||||||
age: u32,
|
age: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn app(ctx: Context<Props>) -> VNode {
|
fn app(ctx: Context<AppProps>) -> VNode {
|
||||||
let (val, set_val) = use_state(&ctx, || 0);
|
let (val, set_val) = use_state(&ctx, || 0);
|
||||||
|
|
||||||
ctx.render(dioxus::prelude::LazyNodes::new(move |c| {
|
ctx.render(LazyNodes::new(move |_nodectx| {
|
||||||
let mut root = builder::ElementBuilder::new(c, "div");
|
builder::ElementBuilder::new(_nodectx, "div")
|
||||||
for child in &ctx.items {
|
.iter_child({
|
||||||
// notice that the child directly borrows from our vec
|
ctx.items.iter().map(|child| {
|
||||||
// this makes lists very fast (simply views reusing lifetimes)
|
builder::virtual_child(
|
||||||
// <ChildItem item=child hanldler=setter />
|
_nodectx,
|
||||||
root = root.child(builder::virtual_child(
|
ChildItem,
|
||||||
c,
|
ChildProps {
|
||||||
ChildItem,
|
item: child.clone(),
|
||||||
// create the props with nothing but the fc<T>
|
item_handler: set_val.clone(),
|
||||||
fc_to_builder(ChildItem)
|
},
|
||||||
.item(child.clone())
|
None,
|
||||||
.item_handler(Callback(set_val.clone()))
|
&[],
|
||||||
.build(),
|
)
|
||||||
None,
|
})
|
||||||
&[],
|
})
|
||||||
));
|
.iter_child([builder::ElementBuilder::new(_nodectx, "div")
|
||||||
}
|
.iter_child([builder::text3(_nodectx.bump(), format_args!("{}", val))])
|
||||||
root.finish()
|
.finish()])
|
||||||
|
.finish()
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
// props should derive a partialeq implementation automatically, but implement ptr compare for & fields
|
// props should derive a partialeq implementation automatically, but implement ptr compare for & fields
|
||||||
#[derive(Props, PartialEq)]
|
|
||||||
struct ChildProps {
|
struct ChildProps {
|
||||||
// Pass down complex structs
|
// Pass down complex structs
|
||||||
item: Rc<ListItem>,
|
item: Rc<ListItem>,
|
||||||
|
|
||||||
// Even pass down handlers!
|
// Even pass down handlers!
|
||||||
item_handler: Callback<i32>,
|
item_handler: Rc<dyn Fn(i32)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ChildItem<'a>(ctx: Context<ChildProps>) -> VNode {
|
fn ChildItem<'a>(cx: Context<'a, ChildProps>) -> VNode {
|
||||||
ctx.render(rsx! {
|
cx.render(LazyNodes::new(move |__cx| todo!()))
|
||||||
div {
|
|
||||||
// onclick: move |evt| (ctx.item_handler)(10)
|
|
||||||
h1 { "abcd123 {ctx.item.name}" }
|
|
||||||
h2 { "abcd123" }
|
|
||||||
div {
|
|
||||||
"abcd123"
|
|
||||||
h2 { }
|
|
||||||
p { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
impl PartialEq for ChildProps {
|
||||||
struct Callback<I, O = ()>(Rc<dyn Fn(I) -> O>);
|
|
||||||
impl<I, O> Deref for Callback<I, O> {
|
|
||||||
type Target = Rc<dyn Fn(I) -> O>;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<I, O> PartialEq for Callback<I, O> {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
Rc::ptr_eq(&self.0, &other.0)
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unsafe impl Properties for ChildProps {
|
||||||
|
type Builder = ();
|
||||||
|
const CAN_BE_MEMOIZED: bool = false;
|
||||||
|
fn builder() -> Self::Builder {
|
||||||
|
()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
use std::{ops::Deref, rc::Rc};
|
|
||||||
|
|
||||||
use dioxus::virtual_dom::Scope;
|
|
||||||
use dioxus_core::prelude::*;
|
|
||||||
|
|
||||||
type RcStr = Rc<str>;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let r: RcStr = "asdasd".into();
|
|
||||||
let r: RcStr = String::from("asdasd").into();
|
|
||||||
|
|
||||||
let g = rsx! {
|
|
||||||
div {
|
|
||||||
Example {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static Example: FC<()> = |ctx| {
|
|
||||||
let nodes = ctx.children();
|
|
||||||
|
|
||||||
//
|
|
||||||
rsx! { in ctx,
|
|
||||||
div {
|
|
||||||
h1 {"these are children nodes: "}
|
|
||||||
{nodes}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
use dioxus_core::component::fc_to_builder;
|
|
||||||
use dioxus_core::prelude::*;
|
|
||||||
|
|
||||||
static BLAH: FC<()> = |ctx| {
|
|
||||||
let g = "asd".to_string();
|
|
||||||
ctx.render(rsx! {
|
|
||||||
div {
|
|
||||||
SomeComponent {
|
|
||||||
some_field: g
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(PartialEq, Props)]
|
|
||||||
pub struct ExampleProps {
|
|
||||||
some_field: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
static SomeComponent: FC<ExampleProps> = |ctx| {
|
|
||||||
let blah = rsx! {
|
|
||||||
div {}
|
|
||||||
};
|
|
||||||
|
|
||||||
let data = match 1 {
|
|
||||||
1 => ctx.render(rsx! (
|
|
||||||
div {
|
|
||||||
h1 {}
|
|
||||||
h3 {}
|
|
||||||
}
|
|
||||||
)),
|
|
||||||
1 => ctx.render(rsx!( div { "abc" } )),
|
|
||||||
2 => ctx.render(rsx!( div { "abc" } )),
|
|
||||||
3 => ctx.render(rsx!( div { "abc" } )),
|
|
||||||
_ => todo!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let data = match 1 {
|
|
||||||
1 => ctx.render(rsx! (
|
|
||||||
div {
|
|
||||||
h1 {}
|
|
||||||
h3 {}
|
|
||||||
}
|
|
||||||
)),
|
|
||||||
1 => ctx.render(rsx!(
|
|
||||||
div { "abc" }
|
|
||||||
)),
|
|
||||||
2 => ctx.render(rsx!(
|
|
||||||
div { "abc" }
|
|
||||||
)),
|
|
||||||
3 => ctx.render(rsx!(
|
|
||||||
div { "abc" }
|
|
||||||
)),
|
|
||||||
_ => todo!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let i = (0..10).map(|v| {
|
|
||||||
rsx! {
|
|
||||||
div {
|
|
||||||
"{v}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ctx.render(rsx! {
|
|
||||||
div {
|
|
||||||
""
|
|
||||||
}
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
fn main() {}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
use baller::Baller;
|
|
||||||
use dioxus_core::prelude::*;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let g = rsx! {
|
|
||||||
div {
|
|
||||||
crate::baller::Baller {}
|
|
||||||
baller::Baller {
|
|
||||||
}
|
|
||||||
Taller {
|
|
||||||
a: "asd"
|
|
||||||
}
|
|
||||||
baller::Baller {}
|
|
||||||
baller::Baller {}
|
|
||||||
Baller {
|
|
||||||
// todo: manual props
|
|
||||||
// {...BallerProps {}}
|
|
||||||
}
|
|
||||||
div {
|
|
||||||
a: "asd",
|
|
||||||
a: "asd",
|
|
||||||
a: "asd",
|
|
||||||
a: "asd",
|
|
||||||
div {
|
|
||||||
"asdas",
|
|
||||||
"asdas",
|
|
||||||
"asdas",
|
|
||||||
"asdas",
|
|
||||||
div {},
|
|
||||||
div {
|
|
||||||
// classes: {[ ("baller", true), ("maller", false) ]}
|
|
||||||
// class: "asdasd"
|
|
||||||
// class: "{customname}",
|
|
||||||
// class: {[("baller", true), ("hover", false)]}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
mod baller {
|
|
||||||
use super::*;
|
|
||||||
pub struct BallerProps {}
|
|
||||||
|
|
||||||
pub fn Baller(ctx: Context<()>) -> VNode {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Props)]
|
|
||||||
pub struct TallerProps {
|
|
||||||
a: &'static str,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn Taller(ctx: Context<TallerProps>) -> VNode {
|
|
||||||
let b = true;
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
#![allow(unused)]
|
|
||||||
//! Example of components in
|
|
||||||
|
|
||||||
use std::{borrow::Borrow, marker::PhantomData};
|
|
||||||
|
|
||||||
use dioxus_core::prelude::*;
|
|
||||||
|
|
||||||
fn main() {}
|
|
||||||
|
|
||||||
static Header: FC<()> = |ctx| {
|
|
||||||
let inner = use_ref(&ctx, || 0);
|
|
||||||
|
|
||||||
let handler1 = move || println!("Value is {}", inner.borrow());
|
|
||||||
|
|
||||||
ctx.render(dioxus::prelude::LazyNodes::new(|nodectx| {
|
|
||||||
builder::ElementBuilder::new(nodectx, "div")
|
|
||||||
.child(VNode::Component(nodectx.bump().alloc(VComponent::new(
|
|
||||||
nodectx,
|
|
||||||
Bottom,
|
|
||||||
(),
|
|
||||||
None,
|
|
||||||
&[],
|
|
||||||
))))
|
|
||||||
.finish()
|
|
||||||
}))
|
|
||||||
};
|
|
||||||
|
|
||||||
static Bottom: FC<()> = |ctx| {
|
|
||||||
ctx.render(html! {
|
|
||||||
<div>
|
|
||||||
<h1> "bruh 1" </h1>
|
|
||||||
<h1> "bruh 2" </h1>
|
|
||||||
</div>
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
fn Top(ctx: Context<()>) -> VNode {
|
|
||||||
ctx.render(html! {
|
|
||||||
<div>
|
|
||||||
<h1> "bruh 1" </h1>
|
|
||||||
<h1> "bruh 2" </h1>
|
|
||||||
</div>
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
use dioxus_core::prelude::*;
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Props)]
|
|
||||||
struct SomeProps {
|
|
||||||
a: i32,
|
|
||||||
|
|
||||||
// automatically do the default (none) and automatically Into<T>
|
|
||||||
#[builder(default, setter(strip_option))]
|
|
||||||
b: Option<i32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let g: SomeProps = SomeProps::builder().a(10).b(10).build();
|
|
||||||
|
|
||||||
let _r = g.b.unwrap_or_else(|| 10);
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
use dioxus_core::prelude::*;
|
|
||||||
|
|
||||||
fn main() {}
|
|
||||||
|
|
||||||
trait SProps {}
|
|
||||||
|
|
||||||
trait Comp {
|
|
||||||
type Props;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Comp for FC<T> {
|
|
||||||
type Props = T;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test() {
|
|
||||||
// let g: FC<ButtonProps> = CustomButton;
|
|
||||||
}
|
|
||||||
|
|
||||||
trait Render: Sized {
|
|
||||||
fn render(ctx: Context<Self>) -> VNode;
|
|
||||||
}
|
|
||||||
// include as much as you might accept
|
|
||||||
struct Button {
|
|
||||||
onhover: Option<Box<dyn Fn()>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Render for Button {
|
|
||||||
fn render(ctx: Context<Self>) -> VNode {
|
|
||||||
let _onfocus = move |_evt: ()| log::debug!("Focused");
|
|
||||||
|
|
||||||
// todo!()
|
|
||||||
ctx.render(rsx! {
|
|
||||||
button {
|
|
||||||
// ..ctx.attrs,
|
|
||||||
class: "abc123",
|
|
||||||
// style: { a: 2, b: 3, c: 4 },
|
|
||||||
onclick: move |_evt| {
|
|
||||||
// log::info("hello world");
|
|
||||||
},
|
|
||||||
// Custom1 { a: 123 }
|
|
||||||
// Custom2 { a: 456, "abc", h1 {"1"}, h2 {"2"} }
|
|
||||||
// Custom3 { a: "sometext goes here" }
|
|
||||||
// Custom4 { onclick: |evt| log::info("click") }
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
//! The book of patterns
|
|
||||||
//! -----
|
|
||||||
//!
|
|
||||||
//! This "book" shows a bunch of useful patterns, ergonomic highlights, and quirks for common usecases when building components.
|
|
||||||
//!
|
|
||||||
//! Here's what's in the book:
|
|
||||||
//! - [ ] Use the builder pattern directly instead of the macro
|
|
||||||
//! - [ ] Using statics instead of functions for components without type hassle.
|
|
||||||
//! - [ ] Use the FC macro on statics and fns for inling props
|
|
||||||
//! - [ ] Using borrowed data from parent to child
|
|
||||||
//!
|
|
||||||
//! Running this example will generate a beautiful webpage. This webpage is used on the diouxs website.
|
|
||||||
|
|
||||||
fn main() {}
|
|
||||||
|
|
||||||
mod static_usage {}
|
|
||||||
mod fc_macro {}
|
|
||||||
mod borrowed_data {}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
use dioxus_core::debug_renderer::DebugRenderer;
|
|
||||||
use dioxus_core::{component::Properties, prelude::*};
|
|
||||||
|
|
||||||
fn main() -> Result<(), ()> {
|
|
||||||
let p1 = SomeProps { name: "bob".into() };
|
|
||||||
|
|
||||||
let _vdom = DebugRenderer::new_with_props(Example, p1);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Props)]
|
|
||||||
struct SomeProps {
|
|
||||||
name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
static Example: FC<SomeProps> = |ctx| {
|
|
||||||
ctx.render(html! {
|
|
||||||
<div>
|
|
||||||
<h1> "hello world!" </h1>
|
|
||||||
</div>
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
#[derive(PartialEq, Clone)]
|
|
||||||
struct MyStruct {
|
|
||||||
a: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_before_to_owned() {
|
|
||||||
let new_str = MyStruct {
|
|
||||||
a: "asd".to_string(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let out = town(&new_str);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn town<T: ToOwned + PartialEq>(t: &T) -> T::Owned {
|
|
||||||
t.to_owned()
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
use dioxus_core::prelude::*;
|
|
||||||
use dioxus_core_macro::rsx_template;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let g = html!("<div> </div>");
|
|
||||||
let g = html!("<div> </div>");
|
|
||||||
// let g = rsx!("div { div { } } ");
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
use dioxus_core::prelude::*;
|
|
||||||
use dioxus_core_macro::format_args_f;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let num = 123;
|
|
||||||
let b = Bump::new();
|
|
||||||
|
|
||||||
let g = rsx! {
|
|
||||||
div {
|
|
||||||
"abc {num}"
|
|
||||||
div {
|
|
||||||
"asd"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
use dioxus::virtual_dom::VirtualDom;
|
|
||||||
use dioxus_core::prelude::*;
|
|
||||||
fn main() {
|
|
||||||
let mut dom = VirtualDom::new(App);
|
|
||||||
let edits = dom.rebuild().unwrap();
|
|
||||||
dbg!(edits);
|
|
||||||
}
|
|
||||||
|
|
||||||
static App: FC<()> = |ctx| {
|
|
||||||
//
|
|
||||||
ctx.render(rsx! {
|
|
||||||
div {
|
|
||||||
"abc"
|
|
||||||
"123"
|
|
||||||
}
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
static Fragment: FC<()> = |ctx| {
|
|
||||||
//
|
|
||||||
|
|
||||||
let children = ctx.children();
|
|
||||||
ctx.render(LazyNodes::new(move |c: &NodeCtx| {
|
|
||||||
//
|
|
||||||
let frag = c.bump().alloc(VFragment::new(None, children));
|
|
||||||
VNode::Fragment(frag)
|
|
||||||
}))
|
|
||||||
};
|
|
||||||
+150
-169
@@ -4,6 +4,8 @@
|
|||||||
//! 3rd party renderers are responsible for converting their native events into these virtual event types. Events might
|
//! 3rd party renderers are responsible for converting their native events into these virtual event types. Events might
|
||||||
//! be heavy or need to interact through FFI, so the events themselves are designed to be lazy.
|
//! be heavy or need to interact through FFI, so the events themselves are designed to be lazy.
|
||||||
|
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::innerlude::ScopeIdx;
|
use crate::innerlude::ScopeIdx;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -26,24 +28,21 @@ impl EventTrigger {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum VirtualEvent {
|
pub enum VirtualEvent {
|
||||||
// Real events
|
// Real events
|
||||||
ClipboardEvent(on::ClipboardEvent),
|
ClipboardEvent(Rc<dyn on::ClipboardEvent>),
|
||||||
CompositionEvent(on::CompositionEvent),
|
CompositionEvent(Rc<dyn on::CompositionEvent>),
|
||||||
KeyboardEvent(on::KeyboardEvent),
|
KeyboardEvent(Rc<dyn on::KeyboardEvent>),
|
||||||
FocusEvent(on::FocusEvent),
|
FocusEvent(Rc<dyn on::FocusEvent>),
|
||||||
FormEvent(on::FormEvent),
|
FormEvent(Rc<dyn on::FormEvent>),
|
||||||
GenericEvent(on::GenericEvent),
|
SelectionEvent(Rc<dyn on::SelectionEvent>),
|
||||||
SelectionEvent(on::SelectionEvent),
|
TouchEvent(Rc<dyn on::TouchEvent>),
|
||||||
TouchEvent(on::TouchEvent),
|
UIEvent(Rc<dyn on::UIEvent>),
|
||||||
UIEvent(on::UIEvent),
|
WheelEvent(Rc<dyn on::WheelEvent>),
|
||||||
WheelEvent(on::WheelEvent),
|
MediaEvent(Rc<dyn on::MediaEvent>),
|
||||||
MediaEvent(on::MediaEvent),
|
AnimationEvent(Rc<dyn on::AnimationEvent>),
|
||||||
AnimationEvent(on::AnimationEvent),
|
TransitionEvent(Rc<dyn on::TransitionEvent>),
|
||||||
TransitionEvent(on::TransitionEvent),
|
ToggleEvent(Rc<dyn on::ToggleEvent>),
|
||||||
ToggleEvent(on::ToggleEvent),
|
MouseEvent(Rc<dyn on::MouseEvent>),
|
||||||
|
PointerEvent(Rc<dyn on::PointerEvent>),
|
||||||
// TODO these events are particularly heavy, so we box them
|
|
||||||
MouseEvent(on::MouseEvent),
|
|
||||||
PointerEvent(on::PointerEvent),
|
|
||||||
|
|
||||||
// ImageEvent(event_data::ImageEvent),
|
// ImageEvent(event_data::ImageEvent),
|
||||||
OtherEvent,
|
OtherEvent,
|
||||||
@@ -51,7 +50,7 @@ pub enum VirtualEvent {
|
|||||||
|
|
||||||
pub mod on {
|
pub mod on {
|
||||||
#![allow(unused)]
|
#![allow(unused)]
|
||||||
use std::{ops::Deref, rc::Rc};
|
use std::{fmt::Debug, ops::Deref, rc::Rc};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
builder::ElementBuilder,
|
builder::ElementBuilder,
|
||||||
@@ -64,10 +63,13 @@ pub mod on {
|
|||||||
macro_rules! event_directory {
|
macro_rules! event_directory {
|
||||||
( $( $eventdata:ident: [ $( $name:ident )* ]; )* ) => {
|
( $( $eventdata:ident: [ $( $name:ident )* ]; )* ) => {
|
||||||
$(
|
$(
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$(
|
$(
|
||||||
pub fn $name<'a>(
|
pub fn $name<'a>(
|
||||||
c: &'_ NodeCtx<'a>,
|
c: &'_ NodeCtx<'a>,
|
||||||
callback: impl Fn($eventdata) + 'a,
|
callback: impl Fn(Rc<dyn $eventdata>) + 'a,
|
||||||
) -> Listener<'a> {
|
) -> Listener<'a> {
|
||||||
let bump = &c.bump();
|
let bump = &c.bump();
|
||||||
Listener {
|
Listener {
|
||||||
@@ -91,7 +93,6 @@ pub mod on {
|
|||||||
KeyboardEvent: [keydown keypress keyup];
|
KeyboardEvent: [keydown keypress keyup];
|
||||||
FocusEvent: [focus blur];
|
FocusEvent: [focus blur];
|
||||||
FormEvent: [change input invalid reset submit];
|
FormEvent: [change input invalid reset submit];
|
||||||
GenericEvent: [];
|
|
||||||
MouseEvent: [
|
MouseEvent: [
|
||||||
click contextmenu doubleclick drag dragend dragenter dragexit
|
click contextmenu doubleclick drag dragend dragenter dragexit
|
||||||
dragleave dragover dragstart drop mousedown mouseenter mouseleave
|
dragleave dragover dragstart drop mousedown mouseenter mouseleave
|
||||||
@@ -116,45 +117,46 @@ pub mod on {
|
|||||||
ToggleEvent: [toggle];
|
ToggleEvent: [toggle];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GetModifierKey(pub Box<dyn Fn(usize) -> bool>);
|
trait GenericEvent {
|
||||||
impl std::fmt::Debug for GetModifierKey {
|
/// Returns whether or not a specific event is a bubbling event
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn bubbles(&self) -> bool;
|
||||||
Ok(()) // just skip for now
|
/// Sets or returns whether the event should propagate up the hierarchy or not
|
||||||
}
|
fn cancelBubble(&self) -> ();
|
||||||
|
/// Returns whether or not an event can have its default action prevented
|
||||||
|
fn cancelable(&self) -> bool;
|
||||||
|
/// Returns whether the event is composed or not
|
||||||
|
fn composed(&self) -> bool;
|
||||||
|
/// Returns the event's path
|
||||||
|
fn composedPath(&self) -> ();
|
||||||
|
/// Returns the element whose event listeners triggered the event
|
||||||
|
fn currentTarget(&self) -> ();
|
||||||
|
/// Returns whether or not the preventDefault method was called for the event
|
||||||
|
fn defaultPrevented(&self) -> ();
|
||||||
|
/// Returns which phase of the event flow is currently being evaluated
|
||||||
|
fn eventPhase(&self) -> ();
|
||||||
|
/// Returns whether or not an event is trusted
|
||||||
|
fn isTrusted(&self) -> ();
|
||||||
|
/// Cancels the event if it is cancelable, meaning that the default action that belongs to the event will
|
||||||
|
fn preventDefault(&self) -> ();
|
||||||
|
/// Prevents other listeners of the same event from being called
|
||||||
|
fn stopImmediatePropagation(&self) -> ();
|
||||||
|
/// Prevents further propagation of an event during event flow
|
||||||
|
fn stopPropagation(&self) -> ();
|
||||||
|
/// Returns the element that triggered the event
|
||||||
|
fn target(&self) -> ();
|
||||||
|
/// Returns the time (in milliseconds relative to the epoch) at which the event was created
|
||||||
|
fn timeStamp(&self) -> usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
// DOMDataTransfer clipboardData
|
pub trait ClipboardEvent: Debug {
|
||||||
#[derive(Debug)]
|
// DOMDataTransfer clipboardData
|
||||||
pub struct ClipboardEvent {}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct CompositionEvent {
|
|
||||||
data: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub trait CompositionEvent: Debug {
|
||||||
pub struct KeyboardEvent {
|
fn data(&self) -> String;
|
||||||
char_code: usize,
|
|
||||||
ctrl_key: bool,
|
|
||||||
key: String,
|
|
||||||
key_code: usize,
|
|
||||||
locale: String,
|
|
||||||
location: usize,
|
|
||||||
meta_key: bool,
|
|
||||||
repeat: bool,
|
|
||||||
shift_key: bool,
|
|
||||||
which: usize,
|
|
||||||
get_modifier_state: GetModifierKey,
|
|
||||||
}
|
|
||||||
pub struct KeyboardEvent2(pub Rc<dyn KeyboardEventT>);
|
|
||||||
impl std::ops::Deref for KeyboardEvent2 {
|
|
||||||
type Target = dyn KeyboardEventT;
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
self.0.as_ref()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait KeyboardEventT {
|
pub trait KeyboardEvent: Debug {
|
||||||
fn char_code(&self) -> usize;
|
fn char_code(&self) -> usize;
|
||||||
fn ctrl_key(&self) -> bool;
|
fn ctrl_key(&self) -> bool;
|
||||||
fn key(&self) -> String;
|
fn key(&self) -> String;
|
||||||
@@ -165,146 +167,125 @@ pub mod on {
|
|||||||
fn repeat(&self) -> bool;
|
fn repeat(&self) -> bool;
|
||||||
fn shift_key(&self) -> bool;
|
fn shift_key(&self) -> bool;
|
||||||
fn which(&self) -> usize;
|
fn which(&self) -> usize;
|
||||||
fn get_modifier_state(&self) -> GetModifierKey;
|
fn get_modifier_state(&self, key_code: usize) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub trait FocusEvent: Debug {
|
||||||
pub struct FocusEvent {/* DOMEventTarget relatedTarget */}
|
/* DOMEventTarget relatedTarget */
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct FormEvent {
|
|
||||||
pub value: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub trait FormEvent: Debug {
|
||||||
pub struct GenericEvent {/* Error Load */}
|
fn value(&self) -> String;
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct MouseEvent(pub Box<RawMouseEvent>);
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct RawMouseEvent {
|
|
||||||
pub alt_key: bool,
|
|
||||||
pub button: i32,
|
|
||||||
pub buttons: i32,
|
|
||||||
pub client_x: i32,
|
|
||||||
pub client_y: i32,
|
|
||||||
pub ctrl_key: bool,
|
|
||||||
pub meta_key: bool,
|
|
||||||
pub page_x: i32,
|
|
||||||
pub page_y: i32,
|
|
||||||
pub screen_x: i32,
|
|
||||||
pub screen_y: i32,
|
|
||||||
pub shift_key: bool,
|
|
||||||
pub get_modifier_state: GetModifierKey,
|
|
||||||
// relatedTarget: DOMEventTarget,
|
|
||||||
}
|
|
||||||
impl Deref for MouseEvent {
|
|
||||||
type Target = RawMouseEvent;
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
self.0.as_ref()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub trait MouseEvent: Debug {
|
||||||
pub struct PointerEvent(Box<RawPointerEvent>);
|
fn alt_key(&self) -> bool;
|
||||||
impl Deref for PointerEvent {
|
fn button(&self) -> i32;
|
||||||
type Target = RawPointerEvent;
|
fn buttons(&self) -> i32;
|
||||||
fn deref(&self) -> &Self::Target {
|
fn client_x(&self) -> i32;
|
||||||
self.0.as_ref()
|
fn client_y(&self) -> i32;
|
||||||
}
|
fn ctrl_key(&self) -> bool;
|
||||||
|
fn meta_key(&self) -> bool;
|
||||||
|
fn page_x(&self) -> i32;
|
||||||
|
fn page_y(&self) -> i32;
|
||||||
|
fn screen_x(&self) -> i32;
|
||||||
|
fn screen_y(&self) -> i32;
|
||||||
|
fn shift_key(&self) -> bool;
|
||||||
|
fn get_modifier_state(&self, key_code: usize) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub trait PointerEvent: Debug {
|
||||||
pub struct RawPointerEvent {
|
|
||||||
// Mouse only
|
// Mouse only
|
||||||
alt_key: bool,
|
fn alt_key(&self) -> bool;
|
||||||
button: usize,
|
fn button(&self) -> usize;
|
||||||
buttons: usize,
|
fn buttons(&self) -> usize;
|
||||||
client_x: i32,
|
fn client_x(&self) -> i32;
|
||||||
client_y: i32,
|
fn client_y(&self) -> i32;
|
||||||
ctrl_key: bool,
|
fn ctrl_key(&self) -> bool;
|
||||||
meta_key: bool,
|
fn meta_key(&self) -> bool;
|
||||||
page_x: i32,
|
fn page_x(&self) -> i32;
|
||||||
page_y: i32,
|
fn page_y(&self) -> i32;
|
||||||
screen_x: i32,
|
fn screen_x(&self) -> i32;
|
||||||
screen_y: i32,
|
fn screen_y(&self) -> i32;
|
||||||
shift_key: bool,
|
fn shift_key(&self) -> bool;
|
||||||
get_modifier_state: GetModifierKey,
|
fn get_modifier_state(&self, key_code: usize) -> bool;
|
||||||
|
fn pointer_id(&self) -> usize;
|
||||||
// Pointer-specific
|
fn width(&self) -> usize;
|
||||||
pointer_id: usize,
|
fn height(&self) -> usize;
|
||||||
width: usize,
|
fn pressure(&self) -> usize;
|
||||||
height: usize,
|
fn tangential_pressure(&self) -> usize;
|
||||||
pressure: usize,
|
fn tilt_x(&self) -> i32;
|
||||||
tangential_pressure: usize,
|
fn tilt_y(&self) -> i32;
|
||||||
tilt_x: i32,
|
fn twist(&self) -> i32;
|
||||||
tilt_y: i32,
|
fn pointer_type(&self) -> String;
|
||||||
twist: i32,
|
fn is_primary(&self) -> bool;
|
||||||
pointer_type: String,
|
|
||||||
is_primary: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub trait SelectionEvent: Debug {}
|
||||||
pub struct SelectionEvent {}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub trait TouchEvent: Debug {
|
||||||
pub struct TouchEvent {
|
fn alt_key(&self) -> bool;
|
||||||
alt_key: bool,
|
fn ctrl_key(&self) -> bool;
|
||||||
ctrl_key: bool,
|
fn meta_key(&self) -> bool;
|
||||||
meta_key: bool,
|
fn shift_key(&self) -> bool;
|
||||||
shift_key: bool,
|
fn get_modifier_state(&self, key_code: usize) -> bool;
|
||||||
get_modifier_state: GetModifierKey,
|
|
||||||
//
|
|
||||||
// changedTouches: DOMTouchList,
|
// changedTouches: DOMTouchList,
|
||||||
// todo
|
|
||||||
// targetTouches: DOMTouchList,
|
// targetTouches: DOMTouchList,
|
||||||
// touches: DOMTouchList,
|
// touches: DOMTouchList,
|
||||||
// getModifierState(key): boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub trait UIEvent: Debug {
|
||||||
pub struct UIEvent {
|
|
||||||
// DOMAbstractView view
|
// DOMAbstractView view
|
||||||
detail: i32,
|
fn detail(&self) -> i32;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub trait WheelEvent: Debug {
|
||||||
pub struct WheelEvent {
|
fn delta_mode(&self) -> i32;
|
||||||
delta_mode: i32,
|
fn delta_x(&self) -> i32;
|
||||||
delta_x: i32,
|
fn delta_y(&self) -> i32;
|
||||||
delta_y: i32,
|
fn delta_z(&self) -> i32;
|
||||||
delta_z: i32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub trait MediaEvent: Debug {}
|
||||||
pub struct MediaEvent {}
|
|
||||||
|
|
||||||
// todo!
|
pub trait ImageEvent: Debug {
|
||||||
// imageevent clashes with media event
|
// load error
|
||||||
// might need to derive this e manually
|
|
||||||
//
|
|
||||||
// #[derive(Debug)]
|
|
||||||
// pub struct ImageEvent {}
|
|
||||||
// event_builder! {
|
|
||||||
// ImageEvent;
|
|
||||||
// load error
|
|
||||||
// }
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct AnimationEvent {
|
|
||||||
animation_name: String,
|
|
||||||
pseudo_element: String,
|
|
||||||
elapsed_time: f32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub trait AnimationEvent: Debug {
|
||||||
pub struct TransitionEvent {
|
fn animation_name(&self) -> String;
|
||||||
property_name: String,
|
fn pseudo_element(&self) -> String;
|
||||||
pseudo_element: String,
|
fn elapsed_time(&self) -> f32;
|
||||||
elapsed_time: f32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub trait TransitionEvent: Debug {
|
||||||
pub struct ToggleEvent {}
|
fn property_name(&self) -> String;
|
||||||
|
fn pseudo_element(&self) -> String;
|
||||||
|
fn elapsed_time(&self) -> f32;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ToggleEvent: Debug {}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod tests {
|
||||||
|
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate as dioxus;
|
||||||
|
use crate::events::on::MouseEvent;
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
fn autocomplete() {
|
||||||
|
// let v = move |evt| {
|
||||||
|
// let r = evt.alt_key();
|
||||||
|
// };
|
||||||
|
|
||||||
|
let g = rsx! {
|
||||||
|
button {
|
||||||
|
onclick: move |evt| {
|
||||||
|
let r = evt.alt_key();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
mod use_context;
|
|
||||||
mod use_effect;
|
|
||||||
mod use_reducer;
|
|
||||||
mod use_ref;
|
|
||||||
mod use_state;
|
|
||||||
|
|
||||||
pub use use_context::*;
|
|
||||||
pub use use_effect::*;
|
|
||||||
pub use use_reducer::*;
|
|
||||||
pub use use_ref::*;
|
|
||||||
pub use use_state::*;
|
|
||||||
|
|
||||||
use crate::{HookUpdater, CURRENT_HOOK};
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::ops::DerefMut;
|
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
pub fn use_hook<InternalHook: 'static, Output, Tear: FnOnce(&mut InternalHook) -> () + 'static>(
|
|
||||||
initializer: impl FnOnce() -> InternalHook,
|
|
||||||
runner: impl FnOnce(&mut InternalHook, HookUpdater) -> Output,
|
|
||||||
tear_down: Tear,
|
|
||||||
) -> Output {
|
|
||||||
// Extract current hook
|
|
||||||
let updater = CURRENT_HOOK.with(|hook_state_holder| {
|
|
||||||
let mut hook_state_holder = hook_state_holder
|
|
||||||
.try_borrow_mut()
|
|
||||||
.expect("Nested hooks not supported");
|
|
||||||
|
|
||||||
let mut hook_state = hook_state_holder
|
|
||||||
.as_mut()
|
|
||||||
.expect("No current hook. Hooks can only be called inside function components");
|
|
||||||
|
|
||||||
// Determine which hook position we're at and increment for the next hook
|
|
||||||
let hook_pos = hook_state.counter;
|
|
||||||
hook_state.counter += 1;
|
|
||||||
|
|
||||||
// Initialize hook if this is the first call
|
|
||||||
if hook_pos >= hook_state.hooks.len() {
|
|
||||||
let initial_state = Rc::new(RefCell::new(initializer()));
|
|
||||||
hook_state.hooks.push(initial_state.clone());
|
|
||||||
hook_state.destroy_listeners.push(Box::new(move || {
|
|
||||||
let mut is = initial_state.borrow_mut();
|
|
||||||
let ihook = is.deref_mut();
|
|
||||||
tear_down(ihook);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
let hook = hook_state
|
|
||||||
.hooks
|
|
||||||
.get(hook_pos)
|
|
||||||
.expect("Not the same number of hooks. Hooks must not be called conditionally")
|
|
||||||
.clone();
|
|
||||||
|
|
||||||
HookUpdater {
|
|
||||||
hook,
|
|
||||||
process_message: hook_state.process_message.clone(),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Execute the actual hook closure we were given. Let it mutate the hook state and let
|
|
||||||
// it create a callback that takes the mutable hook state.
|
|
||||||
let mut hook = updater.hook.borrow_mut();
|
|
||||||
let hook: &mut InternalHook = hook
|
|
||||||
.downcast_mut()
|
|
||||||
.expect("Incompatible hook type. Hooks must always be called in the same order");
|
|
||||||
|
|
||||||
runner(hook, updater.clone())
|
|
||||||
}
|
|
||||||
@@ -1,469 +0,0 @@
|
|||||||
// Naming this file use_context could be confusing. Not least to the IDE.
|
|
||||||
use crate::{get_current_scope, use_hook};
|
|
||||||
use std::any::TypeId;
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::rc::{Rc, Weak};
|
|
||||||
use std::{iter, mem};
|
|
||||||
use yew::html;
|
|
||||||
use yew::html::{AnyScope, Scope};
|
|
||||||
use yew::{Children, Component, ComponentLink, Html, Properties};
|
|
||||||
|
|
||||||
type ConsumerCallback<T> = Box<dyn Fn(Rc<T>)>;
|
|
||||||
type UseContextOutput<T> = Option<Rc<T>>;
|
|
||||||
|
|
||||||
struct UseContext<T2: Clone + PartialEq + 'static> {
|
|
||||||
provider_scope: Option<Scope<ContextProvider<T2>>>,
|
|
||||||
current_context: Option<Rc<T2>>,
|
|
||||||
callback: Option<Rc<ConsumerCallback<T2>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn use_context<T: Clone + PartialEq + 'static>() -> UseContextOutput<T> {
|
|
||||||
let scope = get_current_scope()
|
|
||||||
.expect("No current Scope. `use_context` can only be called inside function components");
|
|
||||||
|
|
||||||
use_hook(
|
|
||||||
// Initializer
|
|
||||||
move || {
|
|
||||||
let provider_scope = find_context_provider_scope::<T>(&scope);
|
|
||||||
let current_context =
|
|
||||||
with_provider_component(&provider_scope, |comp| Rc::clone(&comp.context));
|
|
||||||
|
|
||||||
UseContext {
|
|
||||||
provider_scope,
|
|
||||||
current_context,
|
|
||||||
callback: None,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// Runner
|
|
||||||
|hook, updater| {
|
|
||||||
// setup a listener for the context provider to update us
|
|
||||||
let listener = move |ctx: Rc<T>| {
|
|
||||||
updater.callback(move |state: &mut UseContext<T>| {
|
|
||||||
state.current_context = Some(ctx);
|
|
||||||
true
|
|
||||||
});
|
|
||||||
};
|
|
||||||
hook.callback = Some(Rc::new(Box::new(listener)));
|
|
||||||
|
|
||||||
// Subscribe to the context provider with our callback
|
|
||||||
let weak_cb = Rc::downgrade(hook.callback.as_ref().unwrap());
|
|
||||||
with_provider_component(&hook.provider_scope, |comp| {
|
|
||||||
comp.subscribe_consumer(weak_cb)
|
|
||||||
});
|
|
||||||
|
|
||||||
// Return the current state
|
|
||||||
hook.current_context.clone()
|
|
||||||
},
|
|
||||||
// Cleanup
|
|
||||||
|hook| {
|
|
||||||
if let Some(cb) = hook.callback.take() {
|
|
||||||
drop(cb);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Properties)]
|
|
||||||
pub struct ContextProviderProps<T: Clone + PartialEq> {
|
|
||||||
pub context: T,
|
|
||||||
pub children: Children,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ContextProvider<T: Clone + PartialEq + 'static> {
|
|
||||||
context: Rc<T>,
|
|
||||||
children: Children,
|
|
||||||
consumers: RefCell<Vec<Weak<ConsumerCallback<T>>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Clone + PartialEq> ContextProvider<T> {
|
|
||||||
/// Add the callback to the subscriber list to be called whenever the context changes.
|
|
||||||
/// The consumer is unsubscribed as soon as the callback is dropped.
|
|
||||||
fn subscribe_consumer(&self, mut callback: Weak<ConsumerCallback<T>>) {
|
|
||||||
// consumers re-subscribe on every render. Try to keep the subscriber list small by reusing dead slots.
|
|
||||||
let mut consumers = self.consumers.borrow_mut();
|
|
||||||
for cb in consumers.iter_mut() {
|
|
||||||
if cb.strong_count() == 0 {
|
|
||||||
mem::swap(cb, &mut callback);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// no slot to reuse, this is a new consumer
|
|
||||||
consumers.push(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Notify all subscribed consumers and remove dropped consumers from the list.
|
|
||||||
fn notify_consumers(&mut self) {
|
|
||||||
let context = &self.context;
|
|
||||||
self.consumers.borrow_mut().retain(|cb| {
|
|
||||||
if let Some(cb) = cb.upgrade() {
|
|
||||||
cb(Rc::clone(context));
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Clone + PartialEq + 'static> Component for ContextProvider<T> {
|
|
||||||
type Message = ();
|
|
||||||
type Properties = ContextProviderProps<T>;
|
|
||||||
|
|
||||||
fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self {
|
|
||||||
Self {
|
|
||||||
children: ctx.children,
|
|
||||||
context: Rc::new(ctx.context),
|
|
||||||
consumers: RefCell::new(Vec::new()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update(&mut self, _msg: Self::Message) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn change(&mut self, props: Self::Properties) -> bool {
|
|
||||||
let should_render = if self.children == ctx.children {
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
self.children = ctx.children;
|
|
||||||
true
|
|
||||||
};
|
|
||||||
|
|
||||||
let new_context = Rc::new(ctx.context);
|
|
||||||
if self.context != new_context {
|
|
||||||
self.context = new_context;
|
|
||||||
self.notify_consumers();
|
|
||||||
}
|
|
||||||
|
|
||||||
should_render
|
|
||||||
}
|
|
||||||
|
|
||||||
fn view(&self) -> Html {
|
|
||||||
html! { <>{ self.children.clone() }</> }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_context_provider_scope<T: Clone + PartialEq + 'static>(
|
|
||||||
scope: &AnyScope,
|
|
||||||
) -> Option<Scope<ContextProvider<T>>> {
|
|
||||||
let expected_type_id = TypeId::of::<ContextProvider<T>>();
|
|
||||||
iter::successors(Some(scope), |scope| scope.get_parent())
|
|
||||||
.filter(|scope| scope.get_type_id() == &expected_type_id)
|
|
||||||
.cloned()
|
|
||||||
.map(AnyScope::downcast::<ContextProvider<T>>)
|
|
||||||
.next()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_provider_component<T, F, R>(
|
|
||||||
provider_scope: &Option<Scope<ContextProvider<T>>>,
|
|
||||||
f: F,
|
|
||||||
) -> Option<R>
|
|
||||||
where
|
|
||||||
T: Clone + PartialEq,
|
|
||||||
F: FnOnce(&ContextProvider<T>) -> R,
|
|
||||||
{
|
|
||||||
provider_scope
|
|
||||||
.as_ref()
|
|
||||||
.and_then(|scope| scope.get_component().map(|comp| f(&*comp)))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use crate::hooks::{use_effect, use_ref, use_state};
|
|
||||||
use crate::util::*;
|
|
||||||
use crate::{FunctionComponent, FunctionProvider};
|
|
||||||
use wasm_bindgen_test::*;
|
|
||||||
use yew::prelude::*;
|
|
||||||
|
|
||||||
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
|
|
||||||
|
|
||||||
#[wasm_bindgen_test]
|
|
||||||
fn use_context_scoping_works() {
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
struct ExampleContext(String);
|
|
||||||
struct UseContextFunctionOuter {}
|
|
||||||
struct UseContextFunctionInner {}
|
|
||||||
struct ExpectNoContextFunction {}
|
|
||||||
type UseContextComponent = FunctionComponent<UseContextFunctionOuter>;
|
|
||||||
type UseContextComponentInner = FunctionComponent<UseContextFunctionInner>;
|
|
||||||
type ExpectNoContextComponent = FunctionComponent<ExpectNoContextFunction>;
|
|
||||||
impl FunctionProvider for ExpectNoContextFunction {
|
|
||||||
type TProps = ();
|
|
||||||
|
|
||||||
fn run(_props: &Self::TProps) -> Html {
|
|
||||||
if use_context::<ExampleContext>().is_some() {
|
|
||||||
yew::services::ConsoleService::log(&format!(
|
|
||||||
"Context should be None here, but was {:?}!",
|
|
||||||
use_context::<ExampleContext>().unwrap()
|
|
||||||
));
|
|
||||||
};
|
|
||||||
return html! {
|
|
||||||
<div></div>
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl FunctionProvider for UseContextFunctionOuter {
|
|
||||||
type TProps = ();
|
|
||||||
|
|
||||||
fn run(_props: &Self::TProps) -> Html {
|
|
||||||
type ExampleContextProvider = ContextProvider<ExampleContext>;
|
|
||||||
return html! {
|
|
||||||
<div>
|
|
||||||
<ExampleContextProvider context=ExampleContext("wrong1".into())>
|
|
||||||
<div>{"ignored"}</div>
|
|
||||||
</ExampleContextProvider>
|
|
||||||
<ExampleContextProvider context=ExampleContext("wrong2".into())>
|
|
||||||
<ExampleContextProvider context=ExampleContext("correct".into())>
|
|
||||||
<ExampleContextProvider context=ExampleContext("wrong1".into())>
|
|
||||||
<div>{"ignored"}</div>
|
|
||||||
</ExampleContextProvider>
|
|
||||||
<UseContextComponentInner />
|
|
||||||
</ExampleContextProvider>
|
|
||||||
</ExampleContextProvider>
|
|
||||||
<ExampleContextProvider context=ExampleContext("wrong3".into())>
|
|
||||||
<div>{"ignored"}</div>
|
|
||||||
</ExampleContextProvider>
|
|
||||||
<ExpectNoContextComponent />
|
|
||||||
</div>
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl FunctionProvider for UseContextFunctionInner {
|
|
||||||
type TProps = ();
|
|
||||||
|
|
||||||
fn run(_props: &Self::TProps) -> Html {
|
|
||||||
let context = use_context::<ExampleContext>();
|
|
||||||
return html! {
|
|
||||||
<div id="result">{ &context.unwrap().0 }</div>
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let app: App<UseContextComponent> = yew::App::new();
|
|
||||||
app.mount(yew::utils::document().get_element_by_id("output").unwrap());
|
|
||||||
let result: String = obtain_result_by_id("result");
|
|
||||||
assert_eq!("correct", result);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen_test]
|
|
||||||
fn use_context_works_with_multiple_types() {
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
struct ContextA(u32);
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
struct ContextB(u32);
|
|
||||||
|
|
||||||
struct Test1Function;
|
|
||||||
impl FunctionProvider for Test1Function {
|
|
||||||
type TProps = ();
|
|
||||||
|
|
||||||
fn run(_props: &Self::TProps) -> Html {
|
|
||||||
assert_eq!(use_context::<ContextA>(), Some(Rc::new(ContextA(2))));
|
|
||||||
assert_eq!(use_context::<ContextB>(), Some(Rc::new(ContextB(1))));
|
|
||||||
|
|
||||||
return html! {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
type Test1 = FunctionComponent<Test1Function>;
|
|
||||||
|
|
||||||
struct Test2Function;
|
|
||||||
impl FunctionProvider for Test2Function {
|
|
||||||
type TProps = ();
|
|
||||||
|
|
||||||
fn run(_props: &Self::TProps) -> Html {
|
|
||||||
assert_eq!(use_context::<ContextA>(), Some(Rc::new(ContextA(0))));
|
|
||||||
assert_eq!(use_context::<ContextB>(), Some(Rc::new(ContextB(1))));
|
|
||||||
|
|
||||||
return html! {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
type Test2 = FunctionComponent<Test2Function>;
|
|
||||||
|
|
||||||
struct Test3Function;
|
|
||||||
impl FunctionProvider for Test3Function {
|
|
||||||
type TProps = ();
|
|
||||||
|
|
||||||
fn run(_props: &Self::TProps) -> Html {
|
|
||||||
assert_eq!(use_context::<ContextA>(), Some(Rc::new(ContextA(0))));
|
|
||||||
assert_eq!(use_context::<ContextB>(), None);
|
|
||||||
|
|
||||||
return html! {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
type Test3 = FunctionComponent<Test3Function>;
|
|
||||||
|
|
||||||
struct Test4Function;
|
|
||||||
impl FunctionProvider for Test4Function {
|
|
||||||
type TProps = ();
|
|
||||||
|
|
||||||
fn run(_props: &Self::TProps) -> Html {
|
|
||||||
assert_eq!(use_context::<ContextA>(), None);
|
|
||||||
assert_eq!(use_context::<ContextB>(), None);
|
|
||||||
|
|
||||||
return html! {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
type Test4 = FunctionComponent<Test4Function>;
|
|
||||||
|
|
||||||
struct TestFunction;
|
|
||||||
impl FunctionProvider for TestFunction {
|
|
||||||
type TProps = ();
|
|
||||||
|
|
||||||
fn run(_props: &Self::TProps) -> Html {
|
|
||||||
type ContextAProvider = ContextProvider<ContextA>;
|
|
||||||
type ContextBProvider = ContextProvider<ContextB>;
|
|
||||||
|
|
||||||
return html! {
|
|
||||||
<div>
|
|
||||||
<ContextAProvider context=ContextA(0)>
|
|
||||||
<ContextBProvider context=ContextB(1)>
|
|
||||||
<ContextAProvider context=ContextA(2)>
|
|
||||||
<Test1/>
|
|
||||||
</ContextAProvider>
|
|
||||||
<Test2/>
|
|
||||||
</ContextBProvider>
|
|
||||||
<Test3/>
|
|
||||||
</ContextAProvider>
|
|
||||||
<Test4 />
|
|
||||||
</div>
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
type TestComponent = FunctionComponent<TestFunction>;
|
|
||||||
|
|
||||||
let app: App<TestComponent> = yew::App::new();
|
|
||||||
app.mount(yew::utils::document().get_element_by_id("output").unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen_test]
|
|
||||||
fn use_context_update_works() {
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
struct MyContext(String);
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Properties)]
|
|
||||||
struct RenderCounterProps {
|
|
||||||
id: String,
|
|
||||||
children: Children,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct RenderCounterFunction;
|
|
||||||
impl FunctionProvider for RenderCounterFunction {
|
|
||||||
type TProps = RenderCounterProps;
|
|
||||||
|
|
||||||
fn run(props: &Self::TProps) -> Html {
|
|
||||||
let counter = use_ref(|| 0);
|
|
||||||
*counter.borrow_mut() += 1;
|
|
||||||
log::info!("Render counter {:?}", counter);
|
|
||||||
return html! {
|
|
||||||
<>
|
|
||||||
<div id=ctx.id.clone()>
|
|
||||||
{ format!("total: {}", counter.borrow()) }
|
|
||||||
</div>
|
|
||||||
{ ctx.children.clone() }
|
|
||||||
</>
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
type RenderCounter = FunctionComponent<RenderCounterFunction>;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Properties)]
|
|
||||||
struct ContextOutletProps {
|
|
||||||
id: String,
|
|
||||||
#[prop_or_default]
|
|
||||||
magic: usize,
|
|
||||||
}
|
|
||||||
struct ContextOutletFunction;
|
|
||||||
impl FunctionProvider for ContextOutletFunction {
|
|
||||||
type TProps = ContextOutletProps;
|
|
||||||
|
|
||||||
fn run(props: &Self::TProps) -> Html {
|
|
||||||
let counter = use_ref(|| 0);
|
|
||||||
*counter.borrow_mut() += 1;
|
|
||||||
|
|
||||||
let ctx = use_context::<Rc<MyContext>>().expect("context not passed down");
|
|
||||||
log::info!("============");
|
|
||||||
log::info!("ctx is {:#?}", ctx);
|
|
||||||
log::info!("magic is {:#?}", ctx.magic);
|
|
||||||
log::info!("outlet counter is {:#?}", ctx);
|
|
||||||
log::info!("============");
|
|
||||||
|
|
||||||
return html! {
|
|
||||||
<>
|
|
||||||
<div>{ format!("magic: {}\n", ctx.magic) }</div>
|
|
||||||
<div id=ctx.id.clone()>
|
|
||||||
{ format!("current: {}, total: {}", ctx.0, counter.borrow()) }
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
type ContextOutlet = FunctionComponent<ContextOutletFunction>;
|
|
||||||
|
|
||||||
struct TestFunction;
|
|
||||||
impl FunctionProvider for TestFunction {
|
|
||||||
type TProps = ();
|
|
||||||
|
|
||||||
fn run(_props: &Self::TProps) -> Html {
|
|
||||||
type MyContextProvider = ContextProvider<Rc<MyContext>>;
|
|
||||||
|
|
||||||
let (ctx, set_ctx) = use_state(|| MyContext("hello".into()));
|
|
||||||
let rendered = use_ref(|| 0);
|
|
||||||
|
|
||||||
// this is used to force an update specific to test-2
|
|
||||||
let (magic_rc, set_magic) = use_state(|| 0);
|
|
||||||
let magic: usize = *magic_rc;
|
|
||||||
|
|
||||||
use_effect(move || {
|
|
||||||
let count = *rendered.borrow();
|
|
||||||
match count {
|
|
||||||
0 => {
|
|
||||||
set_ctx(MyContext("world".into()));
|
|
||||||
*rendered.borrow_mut() += 1;
|
|
||||||
}
|
|
||||||
1 => {
|
|
||||||
// force test-2 to re-render.
|
|
||||||
set_magic(1);
|
|
||||||
*rendered.borrow_mut() += 1;
|
|
||||||
}
|
|
||||||
2 => {
|
|
||||||
set_ctx(MyContext("hello world!".into()));
|
|
||||||
*rendered.borrow_mut() += 1;
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
};
|
|
||||||
|| {}
|
|
||||||
});
|
|
||||||
|
|
||||||
return html! {
|
|
||||||
<MyContextProvider context=ctx>
|
|
||||||
<RenderCounter id="test-0">
|
|
||||||
<ContextOutlet id="test-1"/>
|
|
||||||
<ContextOutlet id="test-2" magic=magic/>
|
|
||||||
</RenderCounter>
|
|
||||||
</MyContextProvider>
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
type TestComponent = FunctionComponent<TestFunction>;
|
|
||||||
|
|
||||||
wasm_logger::init(wasm_logger::Config::default());
|
|
||||||
let app: App<TestComponent> = yew::App::new();
|
|
||||||
app.mount(yew::utils::document().get_element_by_id("output").unwrap());
|
|
||||||
|
|
||||||
// 1 initial render + 3 update steps
|
|
||||||
assert_eq!(obtain_result_by_id("test-0"), "total: 4");
|
|
||||||
|
|
||||||
// 1 initial + 2 context update
|
|
||||||
assert_eq!(
|
|
||||||
obtain_result_by_id("test-1"),
|
|
||||||
"current: hello world!, total: 3"
|
|
||||||
);
|
|
||||||
|
|
||||||
// 1 initial + 1 context update + 1 magic update + 1 context update
|
|
||||||
assert_eq!(
|
|
||||||
obtain_result_by_id("test-2"),
|
|
||||||
"current: hello world!, total: 4"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,320 +0,0 @@
|
|||||||
use crate::use_hook;
|
|
||||||
use std::{borrow::Borrow, rc::Rc};
|
|
||||||
|
|
||||||
struct UseEffect<Destructor> {
|
|
||||||
destructor: Option<Box<Destructor>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This hook is used for hooking into the component's lifecycle.
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
/// ```rust
|
|
||||||
/// # use yew_functional::{function_component, use_effect, use_state};
|
|
||||||
/// # use yew::prelude::*;
|
|
||||||
/// # use std::rc::Rc;
|
|
||||||
/// #
|
|
||||||
/// #[function_component(UseEffect)]
|
|
||||||
/// fn effect() -> Html {
|
|
||||||
/// let (counter, set_counter) = use_state(|| 0);
|
|
||||||
///
|
|
||||||
/// let counter_one = counter.clone();
|
|
||||||
/// use_effect(move || {
|
|
||||||
/// // Make a call to DOM API after component is rendered
|
|
||||||
/// yew::utils::document().set_title(&format!("You clicked {} times", counter_one));
|
|
||||||
///
|
|
||||||
/// // Perform the cleanup
|
|
||||||
/// || yew::utils::document().set_title(&format!("You clicked 0 times"))
|
|
||||||
/// });
|
|
||||||
///
|
|
||||||
/// let onclick = {
|
|
||||||
/// let counter = Rc::clone(&counter);
|
|
||||||
/// Callback::from(move |_| set_counter(*counter + 1))
|
|
||||||
/// };
|
|
||||||
///
|
|
||||||
/// html! {
|
|
||||||
/// <button onclick=onclick>{ format!("Increment to {}", counter) }</button>
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
pub fn use_effect<Destructor>(callback: impl FnOnce() -> Destructor + 'static)
|
|
||||||
where
|
|
||||||
Destructor: FnOnce() + 'static,
|
|
||||||
{
|
|
||||||
let callback = Box::new(callback);
|
|
||||||
use_hook(
|
|
||||||
move || {
|
|
||||||
let effect: UseEffect<Destructor> = UseEffect { destructor: None };
|
|
||||||
effect
|
|
||||||
},
|
|
||||||
|_, updater| {
|
|
||||||
// Run on every render
|
|
||||||
updater.post_render(move |state: &mut UseEffect<Destructor>| {
|
|
||||||
if let Some(de) = state.destructor.take() {
|
|
||||||
de();
|
|
||||||
}
|
|
||||||
let new_destructor = callback();
|
|
||||||
state.destructor.replace(Box::new(new_destructor));
|
|
||||||
false
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|hook| {
|
|
||||||
if let Some(destructor) = hook.destructor.take() {
|
|
||||||
destructor()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
struct UseEffectDeps<Destructor, Dependents> {
|
|
||||||
destructor: Option<Box<Destructor>>,
|
|
||||||
deps: Rc<Dependents>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This hook is similar to [`use_effect`] but it accepts dependencies.
|
|
||||||
///
|
|
||||||
/// Whenever the dependencies are changed, the effect callback is called again.
|
|
||||||
/// To detect changes, dependencies must implement `PartialEq`.
|
|
||||||
/// Note that the destructor also runs when dependencies change.
|
|
||||||
pub fn use_effect_with_deps<Callback, Destructor, Dependents>(callback: Callback, deps: Dependents)
|
|
||||||
where
|
|
||||||
Callback: FnOnce(&Dependents) -> Destructor + 'static,
|
|
||||||
Destructor: FnOnce() + 'static,
|
|
||||||
Dependents: PartialEq + 'static,
|
|
||||||
{
|
|
||||||
let deps = Rc::new(deps);
|
|
||||||
let deps_c = deps.clone();
|
|
||||||
|
|
||||||
use_hook(
|
|
||||||
move || {
|
|
||||||
let destructor: Option<Box<Destructor>> = None;
|
|
||||||
UseEffectDeps {
|
|
||||||
destructor,
|
|
||||||
deps: deps_c,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
move |_, updater| {
|
|
||||||
updater.post_render(move |state: &mut UseEffectDeps<Destructor, Dependents>| {
|
|
||||||
if state.deps != deps {
|
|
||||||
if let Some(de) = state.destructor.take() {
|
|
||||||
de();
|
|
||||||
}
|
|
||||||
let new_destructor = callback(deps.borrow());
|
|
||||||
state.deps = deps;
|
|
||||||
state.destructor.replace(Box::new(new_destructor));
|
|
||||||
} else if state.destructor.is_none() {
|
|
||||||
state
|
|
||||||
.destructor
|
|
||||||
.replace(Box::new(callback(state.deps.borrow())));
|
|
||||||
}
|
|
||||||
false
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|hook| {
|
|
||||||
if let Some(destructor) = hook.destructor.take() {
|
|
||||||
destructor()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use crate::hooks::{use_effect_with_deps, use_ref, use_state};
|
|
||||||
use crate::util::*;
|
|
||||||
use crate::{FunctionComponent, FunctionProvider};
|
|
||||||
use std::ops::Deref;
|
|
||||||
use std::ops::DerefMut;
|
|
||||||
use std::rc::Rc;
|
|
||||||
use wasm_bindgen_test::*;
|
|
||||||
use yew::prelude::*;
|
|
||||||
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
|
|
||||||
|
|
||||||
#[wasm_bindgen_test]
|
|
||||||
fn use_effect_works_many_times() {
|
|
||||||
struct UseEffectFunction {}
|
|
||||||
impl FunctionProvider for UseEffectFunction {
|
|
||||||
type TProps = ();
|
|
||||||
|
|
||||||
fn run(_: &Self::TProps) -> Html {
|
|
||||||
let (counter, set_counter) = use_state(|| 0);
|
|
||||||
let counter_clone = counter.clone();
|
|
||||||
|
|
||||||
use_effect_with_deps(
|
|
||||||
move |_| {
|
|
||||||
if *counter_clone < 4 {
|
|
||||||
set_counter(*counter_clone + 1);
|
|
||||||
}
|
|
||||||
|| {}
|
|
||||||
},
|
|
||||||
*counter,
|
|
||||||
);
|
|
||||||
|
|
||||||
return html! {
|
|
||||||
<div>
|
|
||||||
{"The test result is"}
|
|
||||||
<div id="result">{counter}</div>
|
|
||||||
{"\n"}
|
|
||||||
</div>
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type UseEffectComponent = FunctionComponent<UseEffectFunction>;
|
|
||||||
let app: App<UseEffectComponent> = yew::App::new();
|
|
||||||
app.mount(yew::utils::document().get_element_by_id("output").unwrap());
|
|
||||||
let result = obtain_result();
|
|
||||||
assert_eq!(result.as_str(), "4");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen_test]
|
|
||||||
fn use_effect_works_once() {
|
|
||||||
struct UseEffectFunction {}
|
|
||||||
impl FunctionProvider for UseEffectFunction {
|
|
||||||
type TProps = ();
|
|
||||||
|
|
||||||
fn run(_: &Self::TProps) -> Html {
|
|
||||||
let (counter, set_counter) = use_state(|| 0);
|
|
||||||
let counter_clone = counter.clone();
|
|
||||||
|
|
||||||
use_effect_with_deps(
|
|
||||||
move |_| {
|
|
||||||
set_counter(*counter_clone + 1);
|
|
||||||
|| panic!("Destructor should not have been called")
|
|
||||||
},
|
|
||||||
(),
|
|
||||||
);
|
|
||||||
|
|
||||||
return html! {
|
|
||||||
<div>
|
|
||||||
{"The test result is"}
|
|
||||||
<div id="result">{counter}</div>
|
|
||||||
{"\n"}
|
|
||||||
</div>
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
type UseEffectComponent = FunctionComponent<UseEffectFunction>;
|
|
||||||
let app: App<UseEffectComponent> = yew::App::new();
|
|
||||||
app.mount(yew::utils::document().get_element_by_id("output").unwrap());
|
|
||||||
let result = obtain_result();
|
|
||||||
assert_eq!(result.as_str(), "1");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen_test]
|
|
||||||
fn use_effect_refires_on_dependency_change() {
|
|
||||||
struct UseEffectFunction {}
|
|
||||||
impl FunctionProvider for UseEffectFunction {
|
|
||||||
type TProps = ();
|
|
||||||
|
|
||||||
fn run(_: &Self::TProps) -> Html {
|
|
||||||
let number_ref = use_ref(|| 0);
|
|
||||||
let number_ref_c = number_ref.clone();
|
|
||||||
let number_ref2 = use_ref(|| 0);
|
|
||||||
let number_ref2_c = number_ref2.clone();
|
|
||||||
let arg = *number_ref.borrow_mut().deref_mut();
|
|
||||||
let (_, set_counter) = use_state(|| 0);
|
|
||||||
use_effect_with_deps(
|
|
||||||
move |dep| {
|
|
||||||
let mut ref_mut = number_ref_c.borrow_mut();
|
|
||||||
let inner_ref_mut = ref_mut.deref_mut();
|
|
||||||
if *inner_ref_mut < 1 {
|
|
||||||
*inner_ref_mut += 1;
|
|
||||||
assert_eq!(dep, &0);
|
|
||||||
} else {
|
|
||||||
assert_eq!(dep, &1);
|
|
||||||
}
|
|
||||||
set_counter(10); // we just need to make sure it does not panic
|
|
||||||
move || {
|
|
||||||
set_counter(11);
|
|
||||||
*number_ref2_c.borrow_mut().deref_mut() += 1;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
arg,
|
|
||||||
);
|
|
||||||
return html! {
|
|
||||||
<div>
|
|
||||||
{"The test result is"}
|
|
||||||
<div id="result">{*number_ref.borrow_mut().deref_mut()}{*number_ref2.borrow_mut().deref_mut()}</div>
|
|
||||||
{"\n"}
|
|
||||||
</div>
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
type UseEffectComponent = FunctionComponent<UseEffectFunction>;
|
|
||||||
let app: App<UseEffectComponent> = yew::App::new();
|
|
||||||
app.mount(yew::utils::document().get_element_by_id("output").unwrap());
|
|
||||||
let result: String = obtain_result();
|
|
||||||
|
|
||||||
assert_eq!(result.as_str(), "11");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen_test]
|
|
||||||
fn use_effect_destroys_on_component_drop() {
|
|
||||||
struct UseEffectFunction {}
|
|
||||||
struct UseEffectWrapper {}
|
|
||||||
#[derive(Properties, Clone)]
|
|
||||||
struct WrapperProps {
|
|
||||||
destroy_called: Rc<dyn Fn()>,
|
|
||||||
}
|
|
||||||
impl PartialEq for WrapperProps {
|
|
||||||
fn eq(&self, _other: &Self) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[derive(Properties, Clone)]
|
|
||||||
struct FunctionProps {
|
|
||||||
effect_called: Rc<dyn Fn()>,
|
|
||||||
destroy_called: Rc<dyn Fn()>,
|
|
||||||
}
|
|
||||||
impl PartialEq for FunctionProps {
|
|
||||||
fn eq(&self, _other: &Self) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
type UseEffectComponent = FunctionComponent<UseEffectFunction>;
|
|
||||||
type UseEffectWrapperComponent = FunctionComponent<UseEffectWrapper>;
|
|
||||||
impl FunctionProvider for UseEffectFunction {
|
|
||||||
type TProps = FunctionProps;
|
|
||||||
|
|
||||||
fn run(props: &Self::TProps) -> Html {
|
|
||||||
let effect_called = ctx.effect_called.clone();
|
|
||||||
let destroy_called = ctx.destroy_called.clone();
|
|
||||||
use_effect_with_deps(
|
|
||||||
move |_| {
|
|
||||||
effect_called();
|
|
||||||
move || destroy_called()
|
|
||||||
},
|
|
||||||
(),
|
|
||||||
);
|
|
||||||
return html! {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl FunctionProvider for UseEffectWrapper {
|
|
||||||
type TProps = WrapperProps;
|
|
||||||
|
|
||||||
fn run(props: &Self::TProps) -> Html {
|
|
||||||
let (show, set_show) = use_state(|| true);
|
|
||||||
if *show {
|
|
||||||
let effect_called: Rc<dyn Fn()> = Rc::new(move || set_show(false));
|
|
||||||
return html! {
|
|
||||||
<UseEffectComponent destroy_called=ctx.destroy_called.clone() effect_called=effect_called />
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return html! {
|
|
||||||
<div>{"EMPTY"}</div>
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let app: App<UseEffectWrapperComponent> = yew::App::new();
|
|
||||||
let destroy_counter = Rc::new(std::cell::RefCell::new(0));
|
|
||||||
let destroy_counter_c = destroy_counter.clone();
|
|
||||||
app.mount_with_props(
|
|
||||||
yew::utils::document().get_element_by_id("output").unwrap(),
|
|
||||||
WrapperProps {
|
|
||||||
destroy_called: Rc::new(move || *destroy_counter_c.borrow_mut().deref_mut() += 1),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
assert_eq!(1, *destroy_counter.borrow().deref());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,203 +0,0 @@
|
|||||||
use crate::use_hook;
|
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
struct UseReducer<State> {
|
|
||||||
current_state: Rc<State>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This hook is an alternative to [`use_state`]. It is used to handle component's state and is used
|
|
||||||
/// when complex actions needs to be performed on said state.
|
|
||||||
///
|
|
||||||
/// For lazy initialization, consider using [`use_reducer_with_init`] instead.
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
/// ```rust
|
|
||||||
/// # use yew_functional::{function_component, use_reducer};
|
|
||||||
/// # use yew::prelude::*;
|
|
||||||
/// # use std::rc::Rc;
|
|
||||||
/// # use std::ops::DerefMut;
|
|
||||||
/// #
|
|
||||||
/// #[function_component(UseReducer)]
|
|
||||||
/// fn reducer() -> Html {
|
|
||||||
/// /// reducer's Action
|
|
||||||
/// enum Action {
|
|
||||||
/// Double,
|
|
||||||
/// Square,
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// /// reducer's State
|
|
||||||
/// struct CounterState {
|
|
||||||
/// counter: i32,
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// let (
|
|
||||||
/// counter, // the state
|
|
||||||
/// // function to update the state
|
|
||||||
/// // as the same suggests, it dispatches the values to the reducer function
|
|
||||||
/// dispatch
|
|
||||||
/// ) = use_reducer(
|
|
||||||
/// // the reducer function
|
|
||||||
/// |prev: Rc<CounterState>, action: Action| CounterState {
|
|
||||||
/// counter: match action {
|
|
||||||
/// Action::Double => prev.counter * 2,
|
|
||||||
/// Action::Square => prev.counter * prev.counter,
|
|
||||||
/// }
|
|
||||||
/// },
|
|
||||||
/// // initial state
|
|
||||||
/// CounterState { counter: 1 },
|
|
||||||
/// );
|
|
||||||
///
|
|
||||||
/// let double_onclick = {
|
|
||||||
/// let dispatch = Rc::clone(&dispatch);
|
|
||||||
/// Callback::from(move |_| dispatch(Action::Double))
|
|
||||||
/// };
|
|
||||||
/// let square_onclick = Callback::from(move |_| dispatch(Action::Square));
|
|
||||||
///
|
|
||||||
/// html! {
|
|
||||||
/// <>
|
|
||||||
/// <div id="result">{ counter.counter }</div>
|
|
||||||
///
|
|
||||||
/// <button onclick=double_onclick>{ "Double" }</button>
|
|
||||||
/// <button onclick=square_onclick>{ "Square" }</button>
|
|
||||||
/// </>
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
pub fn use_reducer<Action: 'static, Reducer, State: 'static>(
|
|
||||||
reducer: Reducer,
|
|
||||||
initial_state: State,
|
|
||||||
) -> (Rc<State>, Rc<dyn Fn(Action)>)
|
|
||||||
where
|
|
||||||
Reducer: Fn(Rc<State>, Action) -> State + 'static,
|
|
||||||
{
|
|
||||||
use_reducer_with_init(reducer, initial_state, |a| a)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// [`use_reducer`] but with init argument.
|
|
||||||
///
|
|
||||||
/// This is useful for lazy initialization where it is beneficial not to perform expensive
|
|
||||||
/// computation up-front
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
/// ```rust
|
|
||||||
/// # use yew_functional::{function_component, use_reducer_with_init};
|
|
||||||
/// # use yew::prelude::*;
|
|
||||||
/// # use std::rc::Rc;
|
|
||||||
/// #
|
|
||||||
/// #[function_component(UseReducerWithInit)]
|
|
||||||
/// fn reducer_with_init() -> Html {
|
|
||||||
/// struct CounterState {
|
|
||||||
/// counter: i32,
|
|
||||||
/// }
|
|
||||||
/// let (counter, dispatch) = use_reducer_with_init(
|
|
||||||
/// |prev: Rc<CounterState>, action: i32| CounterState {
|
|
||||||
/// counter: prev.counter + action,
|
|
||||||
/// },
|
|
||||||
/// 0,
|
|
||||||
/// |initial: i32| CounterState {
|
|
||||||
/// counter: initial + 10,
|
|
||||||
/// },
|
|
||||||
/// );
|
|
||||||
///
|
|
||||||
/// html! {
|
|
||||||
/// <>
|
|
||||||
/// <div id="result">{counter.counter}</div>
|
|
||||||
///
|
|
||||||
/// <button onclick=Callback::from(move |_| dispatch(10))>{"Increment by 10"}</button>
|
|
||||||
/// </>
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
pub fn use_reducer_with_init<
|
|
||||||
Reducer,
|
|
||||||
Action: 'static,
|
|
||||||
State: 'static,
|
|
||||||
InitialState: 'static,
|
|
||||||
InitFn: 'static,
|
|
||||||
>(
|
|
||||||
reducer: Reducer,
|
|
||||||
initial_state: InitialState,
|
|
||||||
init: InitFn,
|
|
||||||
) -> (Rc<State>, Rc<dyn Fn(Action)>)
|
|
||||||
where
|
|
||||||
Reducer: Fn(Rc<State>, Action) -> State + 'static,
|
|
||||||
InitFn: Fn(InitialState) -> State,
|
|
||||||
{
|
|
||||||
let init = Box::new(init);
|
|
||||||
let reducer = Rc::new(reducer);
|
|
||||||
use_hook(
|
|
||||||
move || UseReducer {
|
|
||||||
current_state: Rc::new(init(initial_state)),
|
|
||||||
},
|
|
||||||
|s, updater| {
|
|
||||||
let setter: Rc<dyn Fn(Action)> = Rc::new(move |action: Action| {
|
|
||||||
let reducer = reducer.clone();
|
|
||||||
// We call the callback, consumer the updater
|
|
||||||
// Required to put the type annotations on Self so the method knows how to downcast
|
|
||||||
updater.callback(move |state: &mut UseReducer<State>| {
|
|
||||||
let new_state = reducer(state.current_state.clone(), action);
|
|
||||||
state.current_state = Rc::new(new_state);
|
|
||||||
true
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
let current = s.current_state.clone();
|
|
||||||
(current, setter)
|
|
||||||
},
|
|
||||||
|_| {},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
use crate::hooks::use_effect_with_deps;
|
|
||||||
use crate::util::*;
|
|
||||||
use crate::{FunctionComponent, FunctionProvider};
|
|
||||||
use wasm_bindgen_test::*;
|
|
||||||
use yew::prelude::*;
|
|
||||||
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
|
|
||||||
|
|
||||||
#[wasm_bindgen_test]
|
|
||||||
fn use_reducer_works() {
|
|
||||||
struct UseReducerFunction {}
|
|
||||||
impl FunctionProvider for UseReducerFunction {
|
|
||||||
type TProps = ();
|
|
||||||
fn run(_: &Self::TProps) -> Html {
|
|
||||||
struct CounterState {
|
|
||||||
counter: i32,
|
|
||||||
}
|
|
||||||
let (counter, dispatch) = use_reducer_with_init(
|
|
||||||
|prev: std::rc::Rc<CounterState>, action: i32| CounterState {
|
|
||||||
counter: prev.counter + action,
|
|
||||||
},
|
|
||||||
0,
|
|
||||||
|initial: i32| CounterState {
|
|
||||||
counter: initial + 10,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
use_effect_with_deps(
|
|
||||||
move |_| {
|
|
||||||
dispatch(1);
|
|
||||||
|| {}
|
|
||||||
},
|
|
||||||
(),
|
|
||||||
);
|
|
||||||
return html! {
|
|
||||||
<div>
|
|
||||||
{"The test result is"}
|
|
||||||
<div id="result">{counter.counter}</div>
|
|
||||||
{"\n"}
|
|
||||||
</div>
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
type UseReducerComponent = FunctionComponent<UseReducerFunction>;
|
|
||||||
let app: App<UseReducerComponent> = yew::App::new();
|
|
||||||
app.mount(yew::utils::document().get_element_by_id("output").unwrap());
|
|
||||||
let result = obtain_result();
|
|
||||||
|
|
||||||
assert_eq!(result.as_str(), "11");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
use crate::use_hook;
|
|
||||||
use std::{cell::RefCell, rc::Rc};
|
|
||||||
|
|
||||||
/// This hook is used for obtaining a mutable reference to a stateful value.
|
|
||||||
/// Its state persists across renders.
|
|
||||||
///
|
|
||||||
/// It is important to note that you do not get notified of state changes.
|
|
||||||
/// If you need the component to be re-rendered on state change, consider using [`use_state`].
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
/// ```rust
|
|
||||||
/// # use yew_functional::{function_component, use_state, use_ref};
|
|
||||||
/// # use yew::prelude::*;
|
|
||||||
/// # use std::rc::Rc;
|
|
||||||
/// # use std::cell::RefCell;
|
|
||||||
/// # use std::ops::{Deref, DerefMut};
|
|
||||||
/// #
|
|
||||||
/// #[function_component(UseRef)]
|
|
||||||
/// fn ref_hook() -> Html {
|
|
||||||
/// let (message, set_message) = use_state(|| "".to_string());
|
|
||||||
/// let message_count = use_ref(|| 0);
|
|
||||||
///
|
|
||||||
/// let onclick = Callback::from(move |e| {
|
|
||||||
/// let window = yew::utils::window();
|
|
||||||
///
|
|
||||||
/// if *message_count.borrow_mut() > 3 {
|
|
||||||
/// window.alert_with_message("Message limit reached");
|
|
||||||
/// } else {
|
|
||||||
/// *message_count.borrow_mut() += 1;
|
|
||||||
/// window.alert_with_message("Message sent");
|
|
||||||
/// }
|
|
||||||
/// });
|
|
||||||
///
|
|
||||||
/// let onchange = Callback::from(move |e| {
|
|
||||||
/// if let ChangeData::Value(value) = e {
|
|
||||||
/// set_message(value)
|
|
||||||
/// }
|
|
||||||
/// });
|
|
||||||
///
|
|
||||||
/// html! {
|
|
||||||
/// <div>
|
|
||||||
/// <input onchange=onchange value=message />
|
|
||||||
/// <button onclick=onclick>{ "Send" }</button>
|
|
||||||
/// </div>
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
pub fn use_ref<T: 'static>(initial_value: impl FnOnce() -> T + 'static) -> Rc<RefCell<T>> {
|
|
||||||
use_hook(
|
|
||||||
|| Rc::new(RefCell::new(initial_value())),
|
|
||||||
|state| state.clone(),
|
|
||||||
|_| {},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use crate::{
|
|
||||||
hooks::use_state,
|
|
||||||
util::*,
|
|
||||||
{FunctionComponent, FunctionProvider},
|
|
||||||
};
|
|
||||||
use std::ops::DerefMut;
|
|
||||||
use wasm_bindgen_test::*;
|
|
||||||
use yew::prelude::*;
|
|
||||||
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
|
|
||||||
|
|
||||||
#[wasm_bindgen_test]
|
|
||||||
fn use_ref_works() {
|
|
||||||
struct UseRefFunction {}
|
|
||||||
impl FunctionProvider for UseRefFunction {
|
|
||||||
type TProps = ();
|
|
||||||
|
|
||||||
fn run(_: &Self::TProps) -> Html {
|
|
||||||
let ref_example = use_ref(|| 0);
|
|
||||||
*ref_example.borrow_mut().deref_mut() += 1;
|
|
||||||
let (counter, set_counter) = use_state(|| 0);
|
|
||||||
if *counter < 5 {
|
|
||||||
set_counter(*counter + 1)
|
|
||||||
}
|
|
||||||
return html! {
|
|
||||||
<div>
|
|
||||||
{"The test output is: "}
|
|
||||||
<div id="result">{*ref_example.borrow_mut().deref_mut() > 4}</div>
|
|
||||||
{"\n"}
|
|
||||||
</div>
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
type UseRefComponent = FunctionComponent<UseRefFunction>;
|
|
||||||
let app: App<UseRefComponent> = yew::App::new();
|
|
||||||
app.mount(yew::utils::document().get_element_by_id("output").unwrap());
|
|
||||||
|
|
||||||
let result = obtain_result();
|
|
||||||
assert_eq!(result.as_str(), "true");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,142 +0,0 @@
|
|||||||
use crate::use_hook;
|
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
struct UseState<T2> {
|
|
||||||
current: Rc<T2>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This hook is used to mange state in a function component.
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
/// ```rust
|
|
||||||
/// # use yew_functional::{function_component, use_state, use_ref};
|
|
||||||
/// # use yew::prelude::*;
|
|
||||||
/// # use std::rc::Rc;
|
|
||||||
/// #
|
|
||||||
/// #[function_component(UseState)]
|
|
||||||
/// fn state() -> Html {
|
|
||||||
/// let (
|
|
||||||
/// counter, // the returned state
|
|
||||||
/// set_counter // setter to update the state
|
|
||||||
/// ) = use_state(|| 0);
|
|
||||||
/// let onclick = {
|
|
||||||
/// let counter = Rc::clone(&counter);
|
|
||||||
/// Callback::from(move |_| set_counter(*counter + 1))
|
|
||||||
/// };
|
|
||||||
///
|
|
||||||
/// html! {
|
|
||||||
/// <div>
|
|
||||||
/// <button onclick=onclick>{ "Increment value" }</button>
|
|
||||||
/// <p>
|
|
||||||
/// <b>{ "Current value: " }</b>
|
|
||||||
/// { counter }
|
|
||||||
/// </p>
|
|
||||||
/// </div>
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
pub fn use_state<T: 'static, F: FnOnce() -> T + 'static>(
|
|
||||||
initial_state_fn: F,
|
|
||||||
) -> (Rc<T>, Rc<dyn Fn(T)>) {
|
|
||||||
use_hook(
|
|
||||||
// Initializer
|
|
||||||
move || UseState {
|
|
||||||
current: Rc::new(initial_state_fn()),
|
|
||||||
},
|
|
||||||
// Runner
|
|
||||||
move |hook, updater| {
|
|
||||||
let setter: Rc<(dyn Fn(T))> = Rc::new(move |new_val: T| {
|
|
||||||
updater.callback(move |st: &mut UseState<T>| {
|
|
||||||
st.current = Rc::new(new_val);
|
|
||||||
true
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
let current = hook.current.clone();
|
|
||||||
(current, setter)
|
|
||||||
},
|
|
||||||
// Teardown
|
|
||||||
|_| {},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use crate::hooks::use_effect_with_deps;
|
|
||||||
use crate::util::*;
|
|
||||||
use crate::{FunctionComponent, FunctionProvider};
|
|
||||||
use wasm_bindgen_test::*;
|
|
||||||
use yew::prelude::*;
|
|
||||||
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
|
|
||||||
|
|
||||||
#[wasm_bindgen_test]
|
|
||||||
fn use_state_works() {
|
|
||||||
struct UseStateFunction {}
|
|
||||||
impl FunctionProvider for UseStateFunction {
|
|
||||||
type TProps = ();
|
|
||||||
|
|
||||||
fn run(_: &Self::TProps) -> Html {
|
|
||||||
let (counter, set_counter) = use_state(|| 0);
|
|
||||||
if *counter < 5 {
|
|
||||||
set_counter(*counter + 1)
|
|
||||||
}
|
|
||||||
return html! {
|
|
||||||
<div>
|
|
||||||
{"Test Output: "}
|
|
||||||
<div id="result">{*counter}</div>
|
|
||||||
{"\n"}
|
|
||||||
</div>
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
type UseComponent = FunctionComponent<UseStateFunction>;
|
|
||||||
let app: App<UseComponent> = yew::App::new();
|
|
||||||
app.mount(yew::utils::document().get_element_by_id("output").unwrap());
|
|
||||||
let result = obtain_result();
|
|
||||||
assert_eq!(result.as_str(), "5");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen_test]
|
|
||||||
fn multiple_use_state_setters() {
|
|
||||||
struct UseStateFunction {}
|
|
||||||
impl FunctionProvider for UseStateFunction {
|
|
||||||
type TProps = ();
|
|
||||||
|
|
||||||
fn run(_: &Self::TProps) -> Html {
|
|
||||||
let (counter, set_counter_in_use_effect) = use_state(|| 0);
|
|
||||||
let counter = *counter;
|
|
||||||
// clone without manually wrapping with Rc
|
|
||||||
let set_counter_in_another_scope = set_counter_in_use_effect.clone();
|
|
||||||
use_effect_with_deps(
|
|
||||||
move |_| {
|
|
||||||
// 1st location
|
|
||||||
set_counter_in_use_effect(counter + 1);
|
|
||||||
|| {}
|
|
||||||
},
|
|
||||||
(),
|
|
||||||
);
|
|
||||||
let another_scope = move || {
|
|
||||||
if counter < 11 {
|
|
||||||
// 2nd location
|
|
||||||
set_counter_in_another_scope(counter + 10)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
another_scope();
|
|
||||||
return html! {
|
|
||||||
<div>
|
|
||||||
{"Test Output: "}
|
|
||||||
// expected output
|
|
||||||
<div id="result">{counter}</div>
|
|
||||||
{"\n"}
|
|
||||||
</div>
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
type UseComponent = FunctionComponent<UseStateFunction>;
|
|
||||||
let app: App<UseComponent> = yew::App::new();
|
|
||||||
app.mount(yew::utils::document().get_element_by_id("output").unwrap());
|
|
||||||
let result = obtain_result();
|
|
||||||
assert_eq!(result.as_str(), "11");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
[package]
|
||||||
|
name = "dioxus-html-namespace"
|
||||||
|
version = "0.0.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
scraper = "0.12.0"
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Html (and SVG) Namespace for Dioxus
|
||||||
|
|
||||||
|
The Dioxus `rsx!` and `html!` macros can accept any compile-time correct namespace on top of NodeBuilder. This crate provides the HTML (and SVG) namespaces which get imported in the Dioxus prelude.
|
||||||
|
|
||||||
|
However, this abstraction enables you to add any namespace of elements, provided they're in scope when rsx! is called. For an example, a UI that is designed for Augmented Reality might use different primitives than HTML:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use ar_namespace::*;
|
||||||
|
|
||||||
|
rsx! {
|
||||||
|
magic_div {
|
||||||
|
magic_header {}
|
||||||
|
magic_paragraph {
|
||||||
|
on_magic_click: move |event| {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This is currently a not-very-explored part of Dioxus. However, the namespacing system does make it possible to provide syntax highlighting, documentation, "go to definition" and compile-time correctness, so it's worth having it abstracted.
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,42 @@
|
|||||||
|
use scraper::{Html, Selector};
|
||||||
|
|
||||||
|
struct AttrClass {
|
||||||
|
attr_name: String,
|
||||||
|
attr_doc_link: String,
|
||||||
|
target_elements: Vec<String>,
|
||||||
|
docs: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
const contents: &str = "";
|
||||||
|
// let contents = include_str!("./attrlist.html");
|
||||||
|
fn main() {
|
||||||
|
let mut items: Vec<AttrClass> = Vec::new();
|
||||||
|
|
||||||
|
let fragment = Html::parse_fragment(contents);
|
||||||
|
|
||||||
|
let ul_selector = Selector::parse("tbody").unwrap();
|
||||||
|
let li_selector = Selector::parse("tr").unwrap();
|
||||||
|
|
||||||
|
let ul = fragment.select(&ul_selector).next().unwrap();
|
||||||
|
for element in ul.select(&li_selector) {
|
||||||
|
let mut childs = element.children().into_iter();
|
||||||
|
|
||||||
|
let attr_field_node = childs.next().unwrap();
|
||||||
|
let elements_node = childs.next().unwrap();
|
||||||
|
let description_node = childs.next().unwrap();
|
||||||
|
|
||||||
|
// let attr_name = { todo!() };
|
||||||
|
// let attr_doc_link = { todo!() };
|
||||||
|
// let target_elements = { todo!() };
|
||||||
|
|
||||||
|
// let docs = description_node.text();
|
||||||
|
|
||||||
|
// todo!()
|
||||||
|
// items.push(AttrClass {
|
||||||
|
// attr_name,
|
||||||
|
// attr_doc_link,
|
||||||
|
// target_elements,
|
||||||
|
// docs,
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,627 @@
|
|||||||
|
trait DioxusElement {
|
||||||
|
const TAG_NAME: &'static str;
|
||||||
|
const NAME_SPACE: Option<&'static str>;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct myinput {}
|
||||||
|
|
||||||
|
struct NodeBuilder {}
|
||||||
|
impl NodeBuilder {
|
||||||
|
// fn add_attr()
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! builder_constructors {
|
||||||
|
( $(
|
||||||
|
$(#[$attr:meta])*
|
||||||
|
$name:ident;
|
||||||
|
// {
|
||||||
|
// attrs: [
|
||||||
|
// $(
|
||||||
|
// $(#[$attr2:meta])*
|
||||||
|
// $attr3:ident
|
||||||
|
// ,
|
||||||
|
// )*
|
||||||
|
// ];
|
||||||
|
// }
|
||||||
|
)* ) => {
|
||||||
|
$(
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
$(#[$attr])*
|
||||||
|
pub struct $name<'a> {
|
||||||
|
// the we wrap the builder, shielding what can be added
|
||||||
|
builder: &'a NodeBuilder
|
||||||
|
}
|
||||||
|
impl<'a> DioxusElement for $name<'a> {
|
||||||
|
const TAG_NAME: &'static str = stringify!($name);
|
||||||
|
const NAME_SPACE: Option<&'static str> = None;
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
};
|
||||||
|
|
||||||
|
( $(
|
||||||
|
$(#[$attr:meta])*
|
||||||
|
$name:ident <> $namespace:tt;
|
||||||
|
)* ) => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Organized in the same order as
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element
|
||||||
|
//
|
||||||
|
// Does not include obsolete elements.
|
||||||
|
builder_constructors! {
|
||||||
|
// Document metadata
|
||||||
|
|
||||||
|
/// Build a
|
||||||
|
/// [`<base>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base)
|
||||||
|
/// element.
|
||||||
|
///
|
||||||
|
/// A base element is your mom!
|
||||||
|
base;
|
||||||
|
|
||||||
|
/// Build a
|
||||||
|
/// [`<head>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/head)
|
||||||
|
/// element.
|
||||||
|
head;
|
||||||
|
/// Build a
|
||||||
|
/// [`<link>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link)
|
||||||
|
/// element.
|
||||||
|
link;
|
||||||
|
/// Build a
|
||||||
|
/// [`<meta>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta)
|
||||||
|
/// element.
|
||||||
|
meta;
|
||||||
|
/// Build a
|
||||||
|
/// [`<style>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style)
|
||||||
|
/// element.
|
||||||
|
style;
|
||||||
|
/// Build a
|
||||||
|
/// [`<title>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/title)
|
||||||
|
/// element.
|
||||||
|
title;
|
||||||
|
|
||||||
|
// Sectioning root
|
||||||
|
|
||||||
|
/// Build a
|
||||||
|
/// [`<body>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/body)
|
||||||
|
/// element.
|
||||||
|
body;
|
||||||
|
|
||||||
|
// Content sectioning
|
||||||
|
|
||||||
|
/// Build a
|
||||||
|
/// [`<address>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/address)
|
||||||
|
/// element.
|
||||||
|
address;
|
||||||
|
/// Build a
|
||||||
|
/// [`<article>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/article)
|
||||||
|
/// element.
|
||||||
|
article;
|
||||||
|
/// Build a
|
||||||
|
/// [`<aside>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/aside)
|
||||||
|
/// element.
|
||||||
|
aside;
|
||||||
|
/// Build a
|
||||||
|
/// [`<footer>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/footer)
|
||||||
|
/// element.
|
||||||
|
footer;
|
||||||
|
/// Build a
|
||||||
|
/// [`<header>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/header)
|
||||||
|
/// element.
|
||||||
|
header;
|
||||||
|
|
||||||
|
/// Build a
|
||||||
|
/// [`<h1>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h1)
|
||||||
|
/// element.
|
||||||
|
h1;
|
||||||
|
|
||||||
|
/// Build a
|
||||||
|
/// [`<h2>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h2)
|
||||||
|
/// element.
|
||||||
|
h2;
|
||||||
|
/// Build a
|
||||||
|
/// [`<h3>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h3)
|
||||||
|
/// element.
|
||||||
|
h3;
|
||||||
|
/// Build a
|
||||||
|
/// [`<h4>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h4)
|
||||||
|
/// element.
|
||||||
|
h4;
|
||||||
|
/// Build a
|
||||||
|
/// [`<h5>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h5)
|
||||||
|
/// element.
|
||||||
|
h5;
|
||||||
|
/// Build a
|
||||||
|
/// [`<h6>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h6)
|
||||||
|
/// element.
|
||||||
|
h6;
|
||||||
|
/// Build a
|
||||||
|
/// [`<hgroup>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/hgroup)
|
||||||
|
/// element.
|
||||||
|
hgroup;
|
||||||
|
/// Build a
|
||||||
|
/// [`<main>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/main)
|
||||||
|
/// element.
|
||||||
|
main;
|
||||||
|
/// Build a
|
||||||
|
/// [`<nav>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/nav)
|
||||||
|
/// element.
|
||||||
|
nav;
|
||||||
|
/// Build a
|
||||||
|
/// [`<section>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/section)
|
||||||
|
/// element.
|
||||||
|
section;
|
||||||
|
|
||||||
|
// Text content
|
||||||
|
|
||||||
|
/// Build a
|
||||||
|
/// [`<blockquote>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/blockquote)
|
||||||
|
/// element.
|
||||||
|
blockquote;
|
||||||
|
/// Build a
|
||||||
|
/// [`<dd>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dd)
|
||||||
|
/// element.
|
||||||
|
dd;
|
||||||
|
/// Build a
|
||||||
|
/// [`<div>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div)
|
||||||
|
/// element.
|
||||||
|
div;
|
||||||
|
/// Build a
|
||||||
|
/// [`<dl>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dl)
|
||||||
|
/// element.
|
||||||
|
dl;
|
||||||
|
/// Build a
|
||||||
|
/// [`<dt>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dt)
|
||||||
|
/// element.
|
||||||
|
dt;
|
||||||
|
/// Build a
|
||||||
|
/// [`<figcaption>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figcaption)
|
||||||
|
/// element.
|
||||||
|
figcaption;
|
||||||
|
/// Build a
|
||||||
|
/// [`<figure>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figure)
|
||||||
|
/// element.
|
||||||
|
figure;
|
||||||
|
/// Build a
|
||||||
|
/// [`<hr>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/hr)
|
||||||
|
/// element.
|
||||||
|
hr;
|
||||||
|
/// Build a
|
||||||
|
/// [`<li>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/li)
|
||||||
|
/// element.
|
||||||
|
li;
|
||||||
|
/// Build a
|
||||||
|
/// [`<ol>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ol)
|
||||||
|
/// element.
|
||||||
|
ol;
|
||||||
|
/// Build a
|
||||||
|
/// [`<p>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/p)
|
||||||
|
/// element.
|
||||||
|
p;
|
||||||
|
/// Build a
|
||||||
|
/// [`<pre>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/pre)
|
||||||
|
/// element.
|
||||||
|
pre;
|
||||||
|
/// Build a
|
||||||
|
/// [`<ul>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ul)
|
||||||
|
/// element.
|
||||||
|
ul;
|
||||||
|
|
||||||
|
// Inline text semantics
|
||||||
|
|
||||||
|
/// Build a
|
||||||
|
/// [`<a>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a)
|
||||||
|
/// element.
|
||||||
|
a;
|
||||||
|
/// Build a
|
||||||
|
/// [`<abbr>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/abbr)
|
||||||
|
/// element.
|
||||||
|
abbr;
|
||||||
|
/// Build a
|
||||||
|
/// [`<b>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/b)
|
||||||
|
/// element.
|
||||||
|
b;
|
||||||
|
/// Build a
|
||||||
|
/// [`<bdi>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/bdi)
|
||||||
|
/// element.
|
||||||
|
bdi;
|
||||||
|
/// Build a
|
||||||
|
/// [`<bdo>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/bdo)
|
||||||
|
/// element.
|
||||||
|
bdo;
|
||||||
|
/// Build a
|
||||||
|
/// [`<br>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/br)
|
||||||
|
/// element.
|
||||||
|
br;
|
||||||
|
/// Build a
|
||||||
|
/// [`<cite>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/cite)
|
||||||
|
/// element.
|
||||||
|
cite;
|
||||||
|
/// Build a
|
||||||
|
/// [`<code>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/code)
|
||||||
|
/// element.
|
||||||
|
code;
|
||||||
|
/// Build a
|
||||||
|
/// [`<data>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/data)
|
||||||
|
/// element.
|
||||||
|
data;
|
||||||
|
/// Build a
|
||||||
|
/// [`<dfn>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dfn)
|
||||||
|
/// element.
|
||||||
|
dfn;
|
||||||
|
/// Build a
|
||||||
|
/// [`<em>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/em)
|
||||||
|
/// element.
|
||||||
|
em;
|
||||||
|
/// Build a
|
||||||
|
/// [`<i>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/i)
|
||||||
|
/// element.
|
||||||
|
i;
|
||||||
|
/// Build a
|
||||||
|
/// [`<kbd>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/kbd)
|
||||||
|
/// element.
|
||||||
|
kbd;
|
||||||
|
/// Build a
|
||||||
|
/// [`<mark>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/mark)
|
||||||
|
/// element.
|
||||||
|
mark;
|
||||||
|
/// Build a
|
||||||
|
/// [`<q>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/q)
|
||||||
|
/// element.
|
||||||
|
q;
|
||||||
|
/// Build a
|
||||||
|
/// [`<rb>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/rb)
|
||||||
|
/// element.
|
||||||
|
rb;
|
||||||
|
/// Build a
|
||||||
|
/// [`<rp>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/rp)
|
||||||
|
/// element.
|
||||||
|
rp;
|
||||||
|
/// Build a
|
||||||
|
/// [`<rt>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/rt)
|
||||||
|
/// element.
|
||||||
|
rt;
|
||||||
|
/// Build a
|
||||||
|
/// [`<rtc>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/rtc)
|
||||||
|
/// element.
|
||||||
|
rtc;
|
||||||
|
/// Build a
|
||||||
|
/// [`<ruby>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ruby)
|
||||||
|
/// element.
|
||||||
|
ruby;
|
||||||
|
/// Build a
|
||||||
|
/// [`<s>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/s)
|
||||||
|
/// element.
|
||||||
|
s;
|
||||||
|
/// Build a
|
||||||
|
/// [`<samp>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/samp)
|
||||||
|
/// element.
|
||||||
|
samp;
|
||||||
|
/// Build a
|
||||||
|
/// [`<small>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/small)
|
||||||
|
/// element.
|
||||||
|
small;
|
||||||
|
/// Build a
|
||||||
|
/// [`<span>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/span)
|
||||||
|
/// element.
|
||||||
|
span;
|
||||||
|
/// Build a
|
||||||
|
/// [`<strong>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/strong)
|
||||||
|
/// element.
|
||||||
|
strong;
|
||||||
|
/// Build a
|
||||||
|
/// [`<sub>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/sub)
|
||||||
|
/// element.
|
||||||
|
sub;
|
||||||
|
/// Build a
|
||||||
|
/// [`<sup>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/sup)
|
||||||
|
/// element.
|
||||||
|
sup;
|
||||||
|
/// Build a
|
||||||
|
/// [`<time>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/time)
|
||||||
|
/// element.
|
||||||
|
time;
|
||||||
|
/// Build a
|
||||||
|
/// [`<u>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/u)
|
||||||
|
/// element.
|
||||||
|
u;
|
||||||
|
/// Build a
|
||||||
|
/// [`<var>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/var)
|
||||||
|
/// element.
|
||||||
|
var;
|
||||||
|
/// Build a
|
||||||
|
/// [`<wbr>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/wbr)
|
||||||
|
/// element.
|
||||||
|
wbr;
|
||||||
|
|
||||||
|
// Image and multimedia
|
||||||
|
|
||||||
|
/// Build a
|
||||||
|
/// [`<area>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/area)
|
||||||
|
/// element.
|
||||||
|
area;
|
||||||
|
/// Build a
|
||||||
|
/// [`<audio>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/audio)
|
||||||
|
/// element.
|
||||||
|
audio;
|
||||||
|
/// Build a
|
||||||
|
/// [`<img>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img)
|
||||||
|
/// element.
|
||||||
|
img;
|
||||||
|
/// Build a
|
||||||
|
/// [`<map>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/map)
|
||||||
|
/// element.
|
||||||
|
map;
|
||||||
|
/// Build a
|
||||||
|
/// [`<track>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/track)
|
||||||
|
/// element.
|
||||||
|
track;
|
||||||
|
/// Build a
|
||||||
|
/// [`<video>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video)
|
||||||
|
/// element.
|
||||||
|
video;
|
||||||
|
|
||||||
|
// Embedded content
|
||||||
|
|
||||||
|
/// Build a
|
||||||
|
/// [`<embed>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/embed)
|
||||||
|
/// element.
|
||||||
|
embed;
|
||||||
|
/// Build a
|
||||||
|
/// [`<iframe>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe)
|
||||||
|
/// element.
|
||||||
|
iframe;
|
||||||
|
/// Build a
|
||||||
|
/// [`<object>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/object)
|
||||||
|
/// element.
|
||||||
|
object;
|
||||||
|
/// Build a
|
||||||
|
/// [`<param>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/param)
|
||||||
|
/// element.
|
||||||
|
param;
|
||||||
|
/// Build a
|
||||||
|
/// [`<picture>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture)
|
||||||
|
/// element.
|
||||||
|
picture;
|
||||||
|
/// Build a
|
||||||
|
/// [`<source>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/source)
|
||||||
|
/// element.
|
||||||
|
source;
|
||||||
|
|
||||||
|
// Scripting
|
||||||
|
|
||||||
|
/// Build a
|
||||||
|
/// [`<canvas>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas)
|
||||||
|
/// element.
|
||||||
|
canvas;
|
||||||
|
/// Build a
|
||||||
|
/// [`<noscript>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/noscript)
|
||||||
|
/// element.
|
||||||
|
noscript;
|
||||||
|
/// Build a
|
||||||
|
/// [`<script>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script)
|
||||||
|
/// element.
|
||||||
|
script;
|
||||||
|
|
||||||
|
// Demarcating edits
|
||||||
|
|
||||||
|
/// Build a
|
||||||
|
/// [`<del>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/del)
|
||||||
|
/// element.
|
||||||
|
del;
|
||||||
|
/// Build a
|
||||||
|
/// [`<ins>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ins)
|
||||||
|
/// element.
|
||||||
|
ins;
|
||||||
|
|
||||||
|
// Table content
|
||||||
|
|
||||||
|
/// Build a
|
||||||
|
/// [`<caption>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/caption)
|
||||||
|
/// element.
|
||||||
|
caption;
|
||||||
|
/// Build a
|
||||||
|
/// [`<col>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/col)
|
||||||
|
/// element.
|
||||||
|
col;
|
||||||
|
/// Build a
|
||||||
|
/// [`<colgroup>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/colgroup)
|
||||||
|
/// element.
|
||||||
|
colgroup;
|
||||||
|
/// Build a
|
||||||
|
/// [`<table>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/table)
|
||||||
|
/// element.
|
||||||
|
table;
|
||||||
|
/// Build a
|
||||||
|
/// [`<tbody>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tbody)
|
||||||
|
/// element.
|
||||||
|
tbody;
|
||||||
|
/// Build a
|
||||||
|
/// [`<td>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/td)
|
||||||
|
/// element.
|
||||||
|
td;
|
||||||
|
/// Build a
|
||||||
|
/// [`<tfoot>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tfoot)
|
||||||
|
/// element.
|
||||||
|
tfoot;
|
||||||
|
/// Build a
|
||||||
|
/// [`<th>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/th)
|
||||||
|
/// element.
|
||||||
|
th;
|
||||||
|
/// Build a
|
||||||
|
/// [`<thead>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/thead)
|
||||||
|
/// element.
|
||||||
|
thead;
|
||||||
|
/// Build a
|
||||||
|
/// [`<tr>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tr)
|
||||||
|
/// element.
|
||||||
|
tr;
|
||||||
|
|
||||||
|
// Forms
|
||||||
|
|
||||||
|
/// Build a
|
||||||
|
/// [`<button>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button)
|
||||||
|
/// element.
|
||||||
|
button;
|
||||||
|
/// Build a
|
||||||
|
/// [`<datalist>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/datalist)
|
||||||
|
/// element.
|
||||||
|
datalist;
|
||||||
|
/// Build a
|
||||||
|
/// [`<fieldset>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/fieldset)
|
||||||
|
/// element.
|
||||||
|
fieldset;
|
||||||
|
/// Build a
|
||||||
|
/// [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form)
|
||||||
|
/// element.
|
||||||
|
form;
|
||||||
|
/// Build a
|
||||||
|
/// [`<input>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input)
|
||||||
|
/// element.
|
||||||
|
input;
|
||||||
|
/// Build a
|
||||||
|
/// [`<label>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label)
|
||||||
|
/// element.
|
||||||
|
label;
|
||||||
|
/// Build a
|
||||||
|
/// [`<legend>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/legend)
|
||||||
|
/// element.
|
||||||
|
legend;
|
||||||
|
/// Build a
|
||||||
|
/// [`<meter>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meter)
|
||||||
|
/// element.
|
||||||
|
meter;
|
||||||
|
/// Build a
|
||||||
|
/// [`<optgroup>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/optgroup)
|
||||||
|
/// element.
|
||||||
|
optgroup;
|
||||||
|
/// Build a
|
||||||
|
/// [`<option>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option)
|
||||||
|
/// element.
|
||||||
|
option;
|
||||||
|
/// Build a
|
||||||
|
/// [`<output>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/output)
|
||||||
|
/// element.
|
||||||
|
output;
|
||||||
|
/// Build a
|
||||||
|
/// [`<progress>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/progress)
|
||||||
|
/// element.
|
||||||
|
progress;
|
||||||
|
/// Build a
|
||||||
|
/// [`<select>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select)
|
||||||
|
/// element.
|
||||||
|
select;
|
||||||
|
/// Build a
|
||||||
|
/// [`<textarea>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea)
|
||||||
|
/// element.
|
||||||
|
textarea;
|
||||||
|
|
||||||
|
// Interactive elements
|
||||||
|
|
||||||
|
/// Build a
|
||||||
|
/// [`<details>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details)
|
||||||
|
/// element.
|
||||||
|
details;
|
||||||
|
/// Build a
|
||||||
|
/// [`<dialog>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog)
|
||||||
|
/// element.
|
||||||
|
dialog;
|
||||||
|
/// Build a
|
||||||
|
/// [`<menu>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/menu)
|
||||||
|
/// element.
|
||||||
|
menu;
|
||||||
|
/// Build a
|
||||||
|
/// [`<menuitem>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/menuitem)
|
||||||
|
/// element.
|
||||||
|
menuitem;
|
||||||
|
/// Build a
|
||||||
|
/// [`<summary>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/summary)
|
||||||
|
/// element.
|
||||||
|
summary;
|
||||||
|
|
||||||
|
// Web components
|
||||||
|
|
||||||
|
/// Build a
|
||||||
|
/// [`<slot>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/slot)
|
||||||
|
/// element.
|
||||||
|
slot;
|
||||||
|
/// Build a
|
||||||
|
/// [`<template>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template)
|
||||||
|
/// element.
|
||||||
|
template;
|
||||||
|
}
|
||||||
|
|
||||||
|
builder_constructors! {
|
||||||
|
// SVG components
|
||||||
|
|
||||||
|
/// Build a
|
||||||
|
/// [`<svg>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/svg)
|
||||||
|
/// element.
|
||||||
|
svg <> "http://www.w3.org/2000/svg" ;
|
||||||
|
/// Build a
|
||||||
|
/// [`<path>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/path)
|
||||||
|
/// element.
|
||||||
|
path <> "http://www.w3.org/2000/svg";
|
||||||
|
/// Build a
|
||||||
|
/// [`<circle>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/circle)
|
||||||
|
/// element.
|
||||||
|
circle <> "http://www.w3.org/2000/svg";
|
||||||
|
/// Build a
|
||||||
|
/// [`<ellipse>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/ellipse)
|
||||||
|
/// element.
|
||||||
|
ellipse <> "http://www.w3.org/2000/svg";
|
||||||
|
/// Build a
|
||||||
|
/// [`<line>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/line)
|
||||||
|
/// element.
|
||||||
|
line <> "http://www.w3.org/2000/svg";
|
||||||
|
/// Build a
|
||||||
|
/// [`<polygon>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/polygon)
|
||||||
|
/// element.
|
||||||
|
polygon <> "http://www.w3.org/2000/svg";
|
||||||
|
/// Build a
|
||||||
|
/// [`<polyline>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/polyline)
|
||||||
|
/// element.
|
||||||
|
polyline <> "http://www.w3.org/2000/svg";
|
||||||
|
/// Build a
|
||||||
|
/// [`<rect>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/rect)
|
||||||
|
/// element.
|
||||||
|
rect <> "http://www.w3.org/2000/svg";
|
||||||
|
/// Build a
|
||||||
|
/// [`<image>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/image)
|
||||||
|
/// element.
|
||||||
|
image <> "http://www.w3.org/2000/svg";
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_attrs {
|
||||||
|
( $(
|
||||||
|
$(#[$attr:meta])*
|
||||||
|
$name:ident: [$($f:ident)*];
|
||||||
|
)* ) => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_attrs! {
|
||||||
|
/// Indicates whether the user can interact with the element
|
||||||
|
disabled: [button command fieldset input keygen optgroup option select textarea];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
fn r#type() {}
|
||||||
|
|
||||||
|
struct MyThing {}
|
||||||
|
impl MyThing {
|
||||||
|
/// eat my socks
|
||||||
|
fn r#type(&self) -> &Self {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
fn nothing(&self) -> &Self {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn test() {
|
||||||
|
let t = MyThing {};
|
||||||
|
t.r#type().r#nothing();
|
||||||
|
|
||||||
|
// let g = div::TAG_NAME;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
use dioxus_core as dioxus;
|
||||||
|
use dioxus_core::events::on::*;
|
||||||
|
use dioxus_core::prelude::*;
|
||||||
|
|
||||||
|
fn main() {}
|
||||||
|
|
||||||
|
fn autocomplete() {
|
||||||
|
let handler = move |evt| {
|
||||||
|
let r = evt.alt_key();
|
||||||
|
if evt.alt_key() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
let g = rsx! {
|
||||||
|
button {
|
||||||
|
button {
|
||||||
|
onclick: {handler}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -602,31 +602,34 @@ fn virtual_event_from_websys_event(event: &web_sys::Event) -> VirtualEvent {
|
|||||||
|
|
||||||
// todo - this needs to be a "controlled" event
|
// todo - this needs to be a "controlled" event
|
||||||
// these events won't carry the right data with them
|
// these events won't carry the right data with them
|
||||||
VirtualEvent::FormEvent(FormEvent { value })
|
todo!()
|
||||||
|
// VirtualEvent::FormEvent(FormEvent { value })
|
||||||
}
|
}
|
||||||
|
|
||||||
"click" | "contextmenu" | "doubleclick" | "drag" | "dragend" | "dragenter" | "dragexit"
|
"click" | "contextmenu" | "doubleclick" | "drag" | "dragend" | "dragenter" | "dragexit"
|
||||||
| "dragleave" | "dragover" | "dragstart" | "drop" | "mousedown" | "mouseenter"
|
| "dragleave" | "dragover" | "dragstart" | "drop" | "mousedown" | "mouseenter"
|
||||||
| "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup" => {
|
| "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup" => {
|
||||||
let evt: web_sys::MouseEvent = event.clone().dyn_into().unwrap();
|
let evt: web_sys::MouseEvent = event.clone().dyn_into().unwrap();
|
||||||
VirtualEvent::MouseEvent(MouseEvent(Box::new(RawMouseEvent {
|
// MouseEvent(Box::new(RawMouseEvent {
|
||||||
alt_key: evt.alt_key(),
|
// alt_key: evt.alt_key(),
|
||||||
button: evt.button() as i32,
|
// button: evt.button() as i32,
|
||||||
buttons: evt.buttons() as i32,
|
// buttons: evt.buttons() as i32,
|
||||||
client_x: evt.client_x(),
|
// client_x: evt.client_x(),
|
||||||
client_y: evt.client_y(),
|
// client_y: evt.client_y(),
|
||||||
ctrl_key: evt.ctrl_key(),
|
// ctrl_key: evt.ctrl_key(),
|
||||||
meta_key: evt.meta_key(),
|
// meta_key: evt.meta_key(),
|
||||||
page_x: evt.page_x(),
|
// page_x: evt.page_x(),
|
||||||
page_y: evt.page_y(),
|
// page_y: evt.page_y(),
|
||||||
screen_x: evt.screen_x(),
|
// screen_x: evt.screen_x(),
|
||||||
screen_y: evt.screen_y(),
|
// screen_y: evt.screen_y(),
|
||||||
shift_key: evt.shift_key(),
|
// shift_key: evt.shift_key(),
|
||||||
get_modifier_state: GetModifierKey(Box::new(|f| {
|
// get_modifier_state: GetModifierKey(Box::new(|f| {
|
||||||
// evt.get_modifier_state(f)
|
// // evt.get_modifier_state(f)
|
||||||
todo!("This is not yet implemented properly, sorry :(");
|
// todo!("This is not yet implemented properly, sorry :(");
|
||||||
})),
|
// })),
|
||||||
})))
|
// }))
|
||||||
|
todo!()
|
||||||
|
// VirtualEvent::MouseEvent()
|
||||||
}
|
}
|
||||||
|
|
||||||
"pointerdown" | "pointermove" | "pointerup" | "pointercancel" | "gotpointercapture"
|
"pointerdown" | "pointermove" | "pointerup" | "pointercancel" | "gotpointercapture"
|
||||||
|
|||||||
+10
-2
@@ -182,6 +182,12 @@ pub mod prelude {
|
|||||||
pub use dioxus_core::prelude::*;
|
pub use dioxus_core::prelude::*;
|
||||||
pub use dioxus_core_macro::fc;
|
pub use dioxus_core_macro::fc;
|
||||||
}
|
}
|
||||||
|
pub mod builder {
|
||||||
|
// pub use dioxus_core::builder::*;
|
||||||
|
}
|
||||||
|
pub mod events {
|
||||||
|
// pub use dioxus_core::events::*;
|
||||||
|
}
|
||||||
// Just a heads-up, the core functionality of dioxus rests in Dioxus-Core. This crate just wraps a bunch of utilities
|
// Just a heads-up, the core functionality of dioxus rests in Dioxus-Core. This crate just wraps a bunch of utilities
|
||||||
// together and exports their namespaces to something predicatble.
|
// together and exports their namespaces to something predicatble.
|
||||||
#[cfg(feature = "core")]
|
#[cfg(feature = "core")]
|
||||||
@@ -220,7 +226,9 @@ pub mod testing {
|
|||||||
#[cfg(feature = "atoms")]
|
#[cfg(feature = "atoms")]
|
||||||
pub mod atoms {}
|
pub mod atoms {}
|
||||||
|
|
||||||
#[cfg(feature = "desktop")]
|
// #[cfg(feature = "desktop")]
|
||||||
pub mod desktop {
|
pub mod webview {
|
||||||
//! A webview based renderer for building desktop applications with Dioxus
|
//! A webview based renderer for building desktop applications with Dioxus
|
||||||
|
use dioxus_core::prelude::FC;
|
||||||
|
pub fn launch<P>(f: FC<P>) {}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user