docs: move around examples

This commit is contained in:
Jonathan Kelley committed 2021-06-17 18:00:32 -04:00
1 parent b9ff95fa12
commit 70cd46dbb2
45 files changed
+3742 -2291

No files matched your search

+1
View File
@@ -49,3 +49,4 @@ lodash
crates.io
utilties
js
Dyon
+13 -12
View File
@@ -10,7 +10,7 @@ description = "Core functionality for Dioxus - a concurrent renderer-agnostic Vi
[dependencies]
dioxus-core = { path="./packages/core", 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]
@@ -30,20 +30,20 @@ split-debuginfo = "unpacked"
[dev-dependencies]
# For the tide ssr examples
async-std = { version="1.9.0", features=["attributes"] }
tide = { version="0.16.0" }
# async-std = { version="1.9.0", features=["attributes"] }
# tide = { version="0.16.0" }
# For the livewview example
tide-websockets = "0.4.0"
serde_millis = "0.1"
serde_json = "1"
serde = { version="1", features=['derive'] }
# tide-websockets = "0.4.0"
# serde_millis = "0.1"
# serde_json = "1"
# serde = { version="1", features=['derive'] }
# 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-hooks = { path="./packages/hooks", version="0.0.0" }
# dioxus-webview = { path="./packages/webview", version="0.0.0" }
# dioxus-hooks = { path="./packages/hooks", version="0.0.0" }
rand = "0.8.4"
@@ -51,10 +51,11 @@ rand = "0.8.4"
members = [
"packages/core-macro",
"packages/core",
"packages/web",
"packages/html-namespace",
# "packages/web",
# "packages/atoms",
# "packages/ssr",
# "packages/docsite",
# "packages/atoms",
# "packages/router",
# "packages/inputs",
]
+5 -4
View File
@@ -8,10 +8,10 @@
Dioxus is a portable, performant, and ergonomic framework for building cross-platform user experiences in Rust.
```rust
fn Example(ctx: Context<()>) -> VNode {
let (selection, set_selection) = use_state(&ctx, || "..?");
fn Example(cx: Context<()>) -> VNode {
let (selection, set_selection) = use_state(&cx, || "..?");
ctx.render(rsx! {
cx.render(rsx! {
h1 { "Hello, {selection}" }
button { "?", onclick: move |_| set_selection("world!")}
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.
@@ -29,6 +29,7 @@ If you know React, then you already know Dioxus.
- Minimal boilerplate
- Familiar design and semantics
- Simple build, test, and deploy
- Compile-time correct templating
- Support for html! and rsx! templating
- SSR, WASM, desktop, and mobile support
- 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 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
much of the traditional reducer boilerplate.
*/
#![allow(unused)]
use std::future::Future;
use dioxus::hooks::use_reducer;
use dioxus_ssr::prelude::*;
fn main() {}
use dioxus::prelude::*;
use std::future::Future;
enum Actions {
Pause,
@@ -88,8 +88,6 @@ enum KindaState {
static EnumReducer: FC<()> = |ctx| {
let (state, reduce) = use_reducer(&ctx, || KindaState::Ready, |cur, new| *cur = new);
let contents = helper(&ctx);
let status = match state {
KindaState::Ready => "Ready",
KindaState::Complete => "Complete",
@@ -99,7 +97,6 @@ static EnumReducer: FC<()> = |ctx| {
ctx.render(rsx! {
div {
h1 {"{status}"}
{contents}
button {
"Set 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
/// These tests can run locally.
/// They use the "compare" method of the debug renderer to do partial tree compares for succint
-94
View File
@@ -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
// }
}
-39
View File
@@ -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.
-65
View File
@@ -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 {}
-42
View File
@@ -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()}
}
})
}
+60
View File
@@ -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!()
}
}
-45
View File
@@ -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
View File
@@ -9,8 +9,8 @@
use dioxus::prelude::*;
async fn main() {
dioxus_webview::launch(|ctx| {
fn main() {
dioxus::webview::launch(|ctx| {
let (count, set_count) = use_state(&ctx, || 0);
ctx.render(rsx! {
@@ -23,5 +23,5 @@ async fn main() {
}
}
})
}).await;
});
}
+2 -2
View File
@@ -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)
- [ ] Hooks
- [ ] Re-exported through the `dioxus` crate (not essential to core virtualdom)
- [ ] fragments
- [ ] pass-thru components
- [x] fragments
- [x] pass-thru components
## Less-essential todos
+5 -5
View File
@@ -13,20 +13,20 @@ Sorted by priority
| Context | ✅ | ✅ | share state through the tree |
| Hook | ✅ | ✅ | memory cells in components |
| SSR | ✅ | ✅ | render directly to string |
| Runs natively | ✅ | 👀 | runs as a sharable binary |
| Component Children | ✅ | ✅ | ctx.children() as a list of nodes |
| Runs natively | ✅ | 👀 | runs as a portable binary w/ extra tooling |
| Component Children | ✅ | ✅ | cx.children() as a list of nodes |
| Null components | ✅ | ✅ | allow returning no components |
| No-div components | ✅ | ✅ | components that render components |
| Fragments | ✅ | ✅ | rsx! can return multiple elements without a root |
| 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 |
| 1st class global state | 🛠 | ✅ | redux/recoil/mobx on top of context |
| ----- Phase 2 ----- | ----- | ----- | ----- |
| 1st class router | 👀 | ✅ | Hook built on top of history |
| Assets | 👀 | ✅ | include css/svg/img url statically |
| Integrated classnames | 🛠 | 👀 | built-in `classnames` |
| Suspense | 👀 | 👀 | schedule future render from future |
| Suspense | 👀 | 👀 | schedule future render from future/promise |
| Transition | 👀 | 👀 | High-level control over suspense |
| Animation | 👀 | ✅ | Spring-style animations |
| Mobile | 👀 | ✅ | Render with cacao |
@@ -34,7 +34,7 @@ Sorted by priority
| 3D Renderer | 👀 | ✅ | react-three-fiber |
| ----- Phase 3 ----- | ----- | ----- | ----- |
| 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 |
| LiveView | 👀 | 👀 | Example for SSR + WASM apps |
+36 -39
View File
@@ -1,42 +1,42 @@
<div align="center">
<h1>⚛ Atoms</h1>
<h1>⚛ Dirac</h1>
<p>
<strong>Global state for Dioxus</strong>
</p>
</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
const COUNT: Atom<u32> = |_| 0;
const Incr: FC<()> = |cx| {
let (count, set_count) = Atoms::use_read_write(&cx, &COUNT);
cx.render(rsx!( button { onclick: move |_| set_count(count + 1), "+" } ))
let (count, set_count) = dirac::use_read_write(&cx, &COUNT);
rsx!(in cx, button { onclick: move |_| set_count(count + 1), "+" } )
}
const Decr: FC<()> = |cx| {
let (count, set_count) = Atoms::use_read_write(&cx, &COUNT);
cx.render(rsx!( button { onclick: move |_| set_count(count - 1), "-" } ))
let (count, set_count) = dirac::use_read_write(&cx, &COUNT);
rsx!(in cx, button { onclick: move |_| set_count(count - 1), "-" } )
}
const App: FC<()> = |cx| {
let count = Atoms::use_read(&cx, &COUNT);
let count = dirac::use_read(&cx, &COUNT);
cx.render(rsx!{
div {
"Count is {count}"
Incr {}
Decr {}
}
"Count is {count}"
Incr {}
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
@@ -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:
```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:
```rust
fn App(ctx: Context<()>) -> VNode {
// 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}"} ))
}
@@ -67,15 +67,13 @@ Atoms are considered "Writable" objects since any consumer may also set the Atom
```rust
fn App(ctx: Context<()>) -> VNode {
let color = Atoms::use_read(&ctx, Light);
let set_color = Atoms::use_write(&ctx, Light);
let color = dirac::use_read(&ctx, &LightColor);
let set_color = dirac::use_write(&ctx, &LightColor);
ctx.render(rsx!(
div {
h1 { "Color: {color}" }
button { onclick: move |_| set_color("red"), "red" }
button { onclick: move |_| set_color("yellow"), "yellow" }
button { onclick: move |_| set_color("green"), "green" }
}
h1 { "Color: {color}" }
button { onclick: move |_| set_color("red"), "red" }
button { onclick: move |_| set_color("yellow"), "yellow" }
button { onclick: move |_| set_color("green"), "green" }
))
}
```
@@ -84,12 +82,10 @@ fn App(ctx: Context<()>) -> VNode {
```rust
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!(
div {
h1 { "Color: {color}" }
button { onclick: move |_| set_color("red"), "red" }
}
h1 { "Color: {color}" }
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 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.
@@ -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:
```rust
// An atom
fn Light(api: &mut AtomBuilder) -> &'static str {
"Green"
}
// A selector
fn ThreeChars(api: &mut SelectorApi) -> &'static str {
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.
- 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
@@ -156,12 +155,10 @@ Whenever you `select` on a `Family`, the ID of the entry is tracked. Other subsc
```rust
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!(
div {
h1 { "AWS rating: {rating}" }
button { onclick: move |_| set_rating((rating + 1) % 5), "incr" }
}
h1 { "AWS rating: {rating}" }
button { onclick: move |_| set_rating((rating + 1) % 5), "incr" }
))
}
```
+10
View File
@@ -0,0 +1,10 @@
fn main() {}
use dioxus_core::prelude::*;
static App: FC<()> = |ctx| {
//
use_initialize_app()
};
fn use_initialize_app(cx: &impl Scoped) {}
+58
View File
@@ -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
}
```
+7 -10
View File
@@ -5,13 +5,8 @@ use dioxus_core::prelude::*;
static Example: FC<()> = |ctx| {
ctx.render(dioxus_core::prelude::LazyNodes::new(move |ctx| {
let bump = ctx.bump();
dioxus::builder::ElementBuilder::new(ctx, "h1")
.children([{
use bumpalo::core_alloc::fmt::Write;
let mut s = bumpalo::collections::String::new_in(bump);
write!(s, "hello");
dioxus::builder::text2(s)
}])
dioxus_core::builder::ElementBuilder::new(ctx, "h1")
.children([dioxus_core::builder::text3(bump, format_args!("hello"))])
.finish()
}))
};
@@ -22,9 +17,11 @@ struct Props {
static Example2: FC<Props> = |ctx| {
ctx.render(dioxus_core::prelude::LazyNodes::new(move |__ctx| {
let bump = __ctx.bump();
dioxus::builder::ElementBuilder::new(__ctx, "h1")
.children([{ dioxus::builder::text3(bump, format_args!("{}", ctx.text)) }])
// .children([{ dioxus::builder::text3(bump, format_args!("hello")) }])
dioxus_core::builder::ElementBuilder::new(__ctx, "h1")
.children([dioxus_core::builder::text3(
bump,
format_args!("{}", ctx.props.text),
)])
.finish()
}))
};
+35 -50
View File
@@ -4,14 +4,12 @@
//! 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.
fn main() {}
use std::{borrow::Borrow, ops::Deref, rc::Rc};
use dioxus_core::prelude::*;
use std::rc::Rc;
struct Props {
struct AppProps {
items: Vec<Rc<ListItem>>,
}
@@ -21,67 +19,54 @@ struct ListItem {
age: u32,
}
fn app(ctx: Context<Props>) -> VNode {
fn app(ctx: Context<AppProps>) -> VNode {
let (val, set_val) = use_state(&ctx, || 0);
ctx.render(dioxus::prelude::LazyNodes::new(move |c| {
let mut root = builder::ElementBuilder::new(c, "div");
for child in &ctx.items {
// notice that the child directly borrows from our vec
// this makes lists very fast (simply views reusing lifetimes)
// <ChildItem item=child hanldler=setter />
root = root.child(builder::virtual_child(
c,
ChildItem,
// create the props with nothing but the fc<T>
fc_to_builder(ChildItem)
.item(child.clone())
.item_handler(Callback(set_val.clone()))
.build(),
None,
&[],
));
}
root.finish()
ctx.render(LazyNodes::new(move |_nodectx| {
builder::ElementBuilder::new(_nodectx, "div")
.iter_child({
ctx.items.iter().map(|child| {
builder::virtual_child(
_nodectx,
ChildItem,
ChildProps {
item: child.clone(),
item_handler: set_val.clone(),
},
None,
&[],
)
})
})
.iter_child([builder::ElementBuilder::new(_nodectx, "div")
.iter_child([builder::text3(_nodectx.bump(), format_args!("{}", val))])
.finish()])
.finish()
}))
}
// props should derive a partialeq implementation automatically, but implement ptr compare for & fields
#[derive(Props, PartialEq)]
struct ChildProps {
// Pass down complex structs
item: Rc<ListItem>,
// Even pass down handlers!
item_handler: Callback<i32>,
item_handler: Rc<dyn Fn(i32)>,
}
fn ChildItem<'a>(ctx: Context<ChildProps>) -> VNode {
ctx.render(rsx! {
div {
// onclick: move |evt| (ctx.item_handler)(10)
h1 { "abcd123 {ctx.item.name}" }
h2 { "abcd123" }
div {
"abcd123"
h2 { }
p { }
}
}
})
fn ChildItem<'a>(cx: Context<'a, ChildProps>) -> VNode {
cx.render(LazyNodes::new(move |__cx| todo!()))
}
#[derive(Clone)]
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> {
impl PartialEq for ChildProps {
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 {
()
}
}
-29
View File
@@ -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}
}
}
};
-72
View File
@@ -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() {}
-59
View File
@@ -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!()
}
-44
View File
@@ -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>
})
}
-16
View File
@@ -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);
}
-47
View File
@@ -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") }
}
})
}
}
-18
View File
@@ -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 {}
-41
View File
@@ -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()
}
-8
View File
@@ -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 { } } ");
}
-16
View File
@@ -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"
}
}
};
}
-28
View File
@@ -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
View File
@@ -4,6 +4,8 @@
//! 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.
use std::rc::Rc;
use crate::innerlude::ScopeIdx;
#[derive(Debug)]
@@ -26,24 +28,21 @@ impl EventTrigger {
#[derive(Debug)]
pub enum VirtualEvent {
// Real events
ClipboardEvent(on::ClipboardEvent),
CompositionEvent(on::CompositionEvent),
KeyboardEvent(on::KeyboardEvent),
FocusEvent(on::FocusEvent),
FormEvent(on::FormEvent),
GenericEvent(on::GenericEvent),
SelectionEvent(on::SelectionEvent),
TouchEvent(on::TouchEvent),
UIEvent(on::UIEvent),
WheelEvent(on::WheelEvent),
MediaEvent(on::MediaEvent),
AnimationEvent(on::AnimationEvent),
TransitionEvent(on::TransitionEvent),
ToggleEvent(on::ToggleEvent),
// TODO these events are particularly heavy, so we box them
MouseEvent(on::MouseEvent),
PointerEvent(on::PointerEvent),
ClipboardEvent(Rc<dyn on::ClipboardEvent>),
CompositionEvent(Rc<dyn on::CompositionEvent>),
KeyboardEvent(Rc<dyn on::KeyboardEvent>),
FocusEvent(Rc<dyn on::FocusEvent>),
FormEvent(Rc<dyn on::FormEvent>),
SelectionEvent(Rc<dyn on::SelectionEvent>),
TouchEvent(Rc<dyn on::TouchEvent>),
UIEvent(Rc<dyn on::UIEvent>),
WheelEvent(Rc<dyn on::WheelEvent>),
MediaEvent(Rc<dyn on::MediaEvent>),
AnimationEvent(Rc<dyn on::AnimationEvent>),
TransitionEvent(Rc<dyn on::TransitionEvent>),
ToggleEvent(Rc<dyn on::ToggleEvent>),
MouseEvent(Rc<dyn on::MouseEvent>),
PointerEvent(Rc<dyn on::PointerEvent>),
// ImageEvent(event_data::ImageEvent),
OtherEvent,
@@ -51,7 +50,7 @@ pub enum VirtualEvent {
pub mod on {
#![allow(unused)]
use std::{ops::Deref, rc::Rc};
use std::{fmt::Debug, ops::Deref, rc::Rc};
use crate::{
builder::ElementBuilder,
@@ -64,10 +63,13 @@ pub mod on {
macro_rules! event_directory {
( $( $eventdata:ident: [ $( $name:ident )* ]; )* ) => {
$(
$(
pub fn $name<'a>(
c: &'_ NodeCtx<'a>,
callback: impl Fn($eventdata) + 'a,
callback: impl Fn(Rc<dyn $eventdata>) + 'a,
) -> Listener<'a> {
let bump = &c.bump();
Listener {
@@ -91,7 +93,6 @@ pub mod on {
KeyboardEvent: [keydown keypress keyup];
FocusEvent: [focus blur];
FormEvent: [change input invalid reset submit];
GenericEvent: [];
MouseEvent: [
click contextmenu doubleclick drag dragend dragenter dragexit
dragleave dragover dragstart drop mousedown mouseenter mouseleave
@@ -116,45 +117,46 @@ pub mod on {
ToggleEvent: [toggle];
}
pub struct GetModifierKey(pub Box<dyn Fn(usize) -> bool>);
impl std::fmt::Debug for GetModifierKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Ok(()) // just skip for now
}
trait GenericEvent {
/// Returns whether or not a specific event is a bubbling event
fn bubbles(&self) -> bool;
/// 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
#[derive(Debug)]
pub struct ClipboardEvent {}
#[derive(Debug)]
pub struct CompositionEvent {
data: String,
pub trait ClipboardEvent: Debug {
// DOMDataTransfer clipboardData
}
#[derive(Debug)]
pub struct KeyboardEvent {
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 CompositionEvent: Debug {
fn data(&self) -> String;
}
pub trait KeyboardEventT {
pub trait KeyboardEvent: Debug {
fn char_code(&self) -> usize;
fn ctrl_key(&self) -> bool;
fn key(&self) -> String;
@@ -165,146 +167,125 @@ pub mod on {
fn repeat(&self) -> bool;
fn shift_key(&self) -> bool;
fn which(&self) -> usize;
fn get_modifier_state(&self) -> GetModifierKey;
fn get_modifier_state(&self, key_code: usize) -> bool;
}
#[derive(Debug)]
pub struct FocusEvent {/* DOMEventTarget relatedTarget */}
#[derive(Debug)]
pub struct FormEvent {
pub value: String,
pub trait FocusEvent: Debug {
/* DOMEventTarget relatedTarget */
}
#[derive(Debug)]
pub struct GenericEvent {/* Error Load */}
#[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()
}
pub trait FormEvent: Debug {
fn value(&self) -> String;
}
#[derive(Debug)]
pub struct PointerEvent(Box<RawPointerEvent>);
impl Deref for PointerEvent {
type Target = RawPointerEvent;
fn deref(&self) -> &Self::Target {
self.0.as_ref()
}
pub trait MouseEvent: Debug {
fn alt_key(&self) -> bool;
fn button(&self) -> i32;
fn buttons(&self) -> i32;
fn client_x(&self) -> i32;
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 struct RawPointerEvent {
pub trait PointerEvent: Debug {
// Mouse only
alt_key: bool,
button: usize,
buttons: usize,
client_x: i32,
client_y: i32,
ctrl_key: bool,
meta_key: bool,
page_x: i32,
page_y: i32,
screen_x: i32,
screen_y: i32,
shift_key: bool,
get_modifier_state: GetModifierKey,
// Pointer-specific
pointer_id: usize,
width: usize,
height: usize,
pressure: usize,
tangential_pressure: usize,
tilt_x: i32,
tilt_y: i32,
twist: i32,
pointer_type: String,
is_primary: bool,
fn alt_key(&self) -> bool;
fn button(&self) -> usize;
fn buttons(&self) -> usize;
fn client_x(&self) -> i32;
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;
fn pointer_id(&self) -> usize;
fn width(&self) -> usize;
fn height(&self) -> usize;
fn pressure(&self) -> usize;
fn tangential_pressure(&self) -> usize;
fn tilt_x(&self) -> i32;
fn tilt_y(&self) -> i32;
fn twist(&self) -> i32;
fn pointer_type(&self) -> String;
fn is_primary(&self) -> bool;
}
#[derive(Debug)]
pub struct SelectionEvent {}
pub trait SelectionEvent: Debug {}
#[derive(Debug)]
pub struct TouchEvent {
alt_key: bool,
ctrl_key: bool,
meta_key: bool,
shift_key: bool,
get_modifier_state: GetModifierKey,
//
pub trait TouchEvent: Debug {
fn alt_key(&self) -> bool;
fn ctrl_key(&self) -> bool;
fn meta_key(&self) -> bool;
fn shift_key(&self) -> bool;
fn get_modifier_state(&self, key_code: usize) -> bool;
// changedTouches: DOMTouchList,
// todo
// targetTouches: DOMTouchList,
// touches: DOMTouchList,
// getModifierState(key): boolean
}
#[derive(Debug)]
pub struct UIEvent {
pub trait UIEvent: Debug {
// DOMAbstractView view
detail: i32,
fn detail(&self) -> i32;
}
#[derive(Debug)]
pub struct WheelEvent {
delta_mode: i32,
delta_x: i32,
delta_y: i32,
delta_z: i32,
pub trait WheelEvent: Debug {
fn delta_mode(&self) -> i32;
fn delta_x(&self) -> i32;
fn delta_y(&self) -> i32;
fn delta_z(&self) -> i32;
}
#[derive(Debug)]
pub struct MediaEvent {}
pub trait MediaEvent: Debug {}
// todo!
// imageevent clashes with media event
// 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,
pub trait ImageEvent: Debug {
// load error
}
#[derive(Debug)]
pub struct TransitionEvent {
property_name: String,
pseudo_element: String,
elapsed_time: f32,
pub trait AnimationEvent: Debug {
fn animation_name(&self) -> String;
fn pseudo_element(&self) -> String;
fn elapsed_time(&self) -> f32;
}
#[derive(Debug)]
pub struct ToggleEvent {}
pub trait TransitionEvent: Debug {
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();
}
}
};
}
}
-68
View File
@@ -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())
}
-469
View File
@@ -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"
);
}
}
-320
View File
@@ -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());
}
}
-203
View File
@@ -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");
}
}
-98
View File
@@ -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");
}
}
-142
View File
@@ -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");
}
}
+11
View File
@@ -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"
+22
View File
@@ -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,
// })
}
}
+627
View File
@@ -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;
}
}
+21
View File
@@ -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}
}
}
};
}
+22 -19
View File
@@ -602,31 +602,34 @@ fn virtual_event_from_websys_event(event: &web_sys::Event) -> VirtualEvent {
// todo - this needs to be a "controlled" event
// 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"
| "dragleave" | "dragover" | "dragstart" | "drop" | "mousedown" | "mouseenter"
| "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup" => {
let evt: web_sys::MouseEvent = event.clone().dyn_into().unwrap();
VirtualEvent::MouseEvent(MouseEvent(Box::new(RawMouseEvent {
alt_key: evt.alt_key(),
button: evt.button() as i32,
buttons: evt.buttons() as i32,
client_x: evt.client_x(),
client_y: evt.client_y(),
ctrl_key: evt.ctrl_key(),
meta_key: evt.meta_key(),
page_x: evt.page_x(),
page_y: evt.page_y(),
screen_x: evt.screen_x(),
screen_y: evt.screen_y(),
shift_key: evt.shift_key(),
get_modifier_state: GetModifierKey(Box::new(|f| {
// evt.get_modifier_state(f)
todo!("This is not yet implemented properly, sorry :(");
})),
})))
// MouseEvent(Box::new(RawMouseEvent {
// alt_key: evt.alt_key(),
// button: evt.button() as i32,
// buttons: evt.buttons() as i32,
// client_x: evt.client_x(),
// client_y: evt.client_y(),
// ctrl_key: evt.ctrl_key(),
// meta_key: evt.meta_key(),
// page_x: evt.page_x(),
// page_y: evt.page_y(),
// screen_x: evt.screen_x(),
// screen_y: evt.screen_y(),
// shift_key: evt.shift_key(),
// get_modifier_state: GetModifierKey(Box::new(|f| {
// // evt.get_modifier_state(f)
// todo!("This is not yet implemented properly, sorry :(");
// })),
// }))
todo!()
// VirtualEvent::MouseEvent()
}
"pointerdown" | "pointermove" | "pointerup" | "pointercancel" | "gotpointercapture"
+10 -2
View File
@@ -182,6 +182,12 @@ pub mod prelude {
pub use dioxus_core::prelude::*;
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
// together and exports their namespaces to something predicatble.
#[cfg(feature = "core")]
@@ -220,7 +226,9 @@ pub mod testing {
#[cfg(feature = "atoms")]
pub mod atoms {}
#[cfg(feature = "desktop")]
pub mod desktop {
// #[cfg(feature = "desktop")]
pub mod webview {
//! A webview based renderer for building desktop applications with Dioxus
use dioxus_core::prelude::FC;
pub fn launch<P>(f: FC<P>) {}
}