chore: cleanup workspace

This commit is contained in:
Jonathan Kelley 2021-07-15 18:40:12 -04:00
parent 3bedcb93ca
commit 8f0bb5dc5b
54 changed files with 691 additions and 1822 deletions

View file

@ -12,34 +12,28 @@ dioxus-core = { path = "./packages/core" }
dioxus-core-macro = { path = "./packages/core-macro", optional = true }
dioxus-html = { path = "./packages/html", optional = true }
dioxus-web = { path = "./packages/web", optional = true }
dioxus-webview = { path = "./packages/webview", optional = true }
dioxus-desktop = { path = "./packages/desktop", optional = true }
dioxus-hooks = { path = "./packages/hooks", optional = true }
dioxus-ssr = { path = "./packages/ssr", optional = true }
dioxus-mobile = { path = "./packages/mobile", optional = true }
[features]
default = [
"core",
"macro",
"hooks",
"ssr",
"html",
"web",
"desktop",
# "atoms",
# "router",
]
atoms = []
# core
default = ["core"]
core = ["macro", "hooks", "html"]
macro = ["dioxus-core-macro"]
ssr = ["dioxus-ssr"]
hooks = ["dioxus-hooks"]
router = []
html = ["dioxus-html"]
web = ["dioxus-web"]
desktop = ["dioxus-webview"]
# Core just includes dioxus-core (the only required package)
core = []
# utilities
atoms = []
router = []
# targets
ssr = ["dioxus-ssr"]
web = ["dioxus-web"]
desktop = ["dioxus-desktop"]
mobile = ["dioxus-mobile"]
[dev-dependencies]
@ -64,7 +58,6 @@ members = [
"packages/hooks",
"packages/web",
"packages/ssr",
"packages/webview",
# "packages/atoms",
# "packages/docsite",
"packages/desktop",
# "packages/mobile",
]

View file

@ -1,23 +0,0 @@
[package]
name = "atoms"
version = "0.0.0"
authors = ["Jonathan Kelley <jkelleyrtp@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.40"
dioxus-core = { path="../core" }
thiserror = "1.0.24"
log = "0.4.14"
im-rc = "15.0.0"
futures = "0.3.15"
[dev-dependencies]
uuid = { version="0.8.2", features=["v4", "wasm-bindgen"] }
dioxus-web = { path="../web" }
wasm-bindgen-futures = "*"
wasm-logger = "0.2.0"
console_error_panic_hook = "0.1.6"

View file

@ -1,164 +0,0 @@
<div align="center">
<h1>⚛ Dirac</h1>
<p>
<strong>Global state for Dioxus</strong>
</p>
</div>
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) = dirac::use_read_write(&cx, &COUNT);
rsx!(in cx, button { onclick: move |_| set_count(count + 1), "+" } )
}
const Decr: FC<()> = |cx| {
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 = dirac::use_read(&cx, &COUNT);
cx.render(rsx!{
"Count is {count}"
Incr {}
Decr {}
})
}
```
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.
## Support
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
## Atoms
A simple atom of state is defined globally as a const:
```rust
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 Dioxus App.
This is then later used in components like so:
```rust
fn App(cx: Context<()>) -> VNode {
// The Atom root must be initialized at the top of the application before any use_Atom hooks
dirac::intialize_root(&cx, |_| {});
let color = dirac::use_read(&cx, &LightColor);
cx.render(rsx!( h1 {"Color of light: {color}"} ))
}
```
Atoms are considered "Writable" objects since any consumer may also set the Atom's value with their own:
```rust
fn App(cx: Context<()>) -> VNode {
let color = dirac::use_read(&cx, &LightColor);
let set_color = dirac::use_write(&cx, &LightColor);
cx.render(rsx!(
h1 { "Color: {color}" }
button { onclick: move |_| set_color("red"), "red" }
button { onclick: move |_| set_color("yellow"), "yellow" }
button { onclick: move |_| set_color("green"), "green" }
))
}
```
"Reading" a value with use_read subscribes that component to any changes to that value while "Writing" a value does not. It's a good idea to use `write-only` whenever it makes sense to prevent unnecessary evaluations. Both `read` and `write` may be combined together to provide a `use_state` style hook.
```rust
fn App(cx: Context<()>) -> VNode {
let (color, set_color) = dirac::use_read_write(&cx, &Light);
cx.render(rsx!(
h1 { "Color: {color}" }
button { onclick: move |_| set_color("red"), "red" }
))
}
```
## Selectors
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 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.
returning static data:
```rust
const Light: Atom<&'static str> = |_| "Green";
const NumChars: Selector<usize> = |api| api.get(&Light).len();
```
Selectors will update their selection and notify subscribed components whenever their dependencies also update. The `get` calls in a selector will subscribe that selector to whatever `Readable` is being `get`-ted. Unlike hooks, you may use `get` in conditionals; an internal algorithm decides when a selector needs to be updated based on what it `get`-ted last time it was ran.
Selectors may also returned borrowed data:
```rust
const Light: Atom<&'static str> = |_| "Green";
const ThreeChars: SelectorBorrowed<str> = |api| api.get(&Light).chars().take(3).unwrap();
```
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()
}
```
Returning borrowed data is generally frowned upon, but may be useful when used wisely.
- 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. Dirac tries to optimize this behavior the best it can to balance component evaluations with memory overhead.
## Families
You might notice that collections will not be performant with just sets/gets. We don't want to clone our entire HashMap every time we want to insert or remove an entry! That's where `Families` come in. Families are memoized collections (HashMap and OrdMap) that wrap the immutable datastructures library `im-rc`. Families are defined by a function that takes the FamilyApi and returns a function that provides a default value given a key. In this example, we insert a value into the collection when initialized, and then return a function that takes a key and returns a default value.
```rust
const CloudRatings: AtomFamily<&str, i32> = |api| {
api.insert("Oracle", -1);
|key| match key {
"AWS" => 1,
"Azure" => 2,
"GCP" => 3,
_ => 0
}
}
```
Whenever you `select` on a `Family`, the ID of the entry is tracked. Other subscribers will only be updated if they too select the same family with the same key and that value is updated. Otherwise, these subscribers will never re-render on an "insert", "remove", or "update" of the collection. You could easily implement this yourself with Atoms, immutable datastructures, and selectors, but our implementation is more efficient and first-class.
```rust
fn App(cx: Context<()>) -> VNode {
let (rating, set_rating) = dirac::use_read_write(cx, CloudRatings.select("AWS"));
cx.render(rsx!(
h1 { "AWS rating: {rating}" }
button { onclick: move |_| set_rating((rating + 1) % 5), "incr" }
))
}
```

View file

@ -1,45 +0,0 @@
//! RecoilAPI Pattern
//! ----------------
//! This example demonstrates how the use_recoil_callback hook can be used to build view controllers.
//! These view controllers are cheap to make and can be easily shared across the app to provide global
//! state logic to many components.
//!
//! This pattern is meant to replace the typical use_dispatch pattern used in Redux apps.
use dioxus_core::prelude::*;
use recoil::*;
const TITLE: Atom<String> = |_| format!("Heading");
const SUBTITLE: Atom<String> = |_| format!("Subheading");
struct TitleController(RecoilApi);
impl TitleController {
fn new(api: RecoilApi) -> Self {
Self(api)
}
fn uppercase(&self) {
self.0.modify(&TITLE, |f| *f = f.to_uppercase());
self.0.modify(&SUBTITLE, |f| *f = f.to_uppercase());
}
fn lowercase(&self) {
self.0.modify(&TITLE, |f| *f = f.to_lowercase());
self.0.modify(&SUBTITLE, |f| *f = f.to_lowercase());
}
}
fn main() {
wasm_bindgen_futures::spawn_local(dioxus_web::WebsysRenderer::start(|cx| {
let title = use_read(&cx, &TITLE);
let subtitle = use_read(&cx, &SUBTITLE);
let controller = TitleController::new(use_recoil_api(&cx));
rsx! { in cx,
div {
"{title}"
"{subtitle}"
button { onclick: move |_| controller.uppercase(), "Uppercase" }
button { onclick: move |_| controller.lowercase(), "Lowercase" }
}
}
}))
}

View file

@ -1,36 +0,0 @@
//! Example: RecoilCallback
//! ------------------------
//! This example shows how use_recoil_callback can be used to abstract over sets/gets.
//! This hook provides a way to capture the RecoilApi object. In this case, we capture
//! it in a closure an abstract the set/get functionality behind the update_title function.
//!
//! It should be straightforward to build a complex app with recoil_callback.
use dioxus_core::prelude::*;
use recoil::*;
const TITLE: Atom<&str> = |_| "red";
fn update_title(api: &RecoilApi) {
match *api.get(&TITLE) {
"green" => api.set(&TITLE, "yellow"),
"yellow" => api.set(&TITLE, "red"),
"red" => api.set(&TITLE, "green"),
_ => {}
}
}
static App: FC<()> = |cx| {
let title = use_read(&cx, &TITLE);
let next_light = use_recoil_api(&cx, |api| move |_| update_title(&api));
rsx! { in cx,
div {
"{title}"
button { onclick: {next_light}, "Next light" }
}
}
};
fn main() {
wasm_bindgen_futures::spawn_local(dioxus_web::WebsysRenderer::start(App))
}

View file

@ -1,50 +0,0 @@
//! Example: ECS Architecture for very list selectors
//! --------------------------------------------------
//! Sometimes, you need *peak* performance. Cloning and Rc might simply be too much overhead for your app.
//! If you're building CPU intense apps like graphics editors, simulations, or advanced visualizations,
//! slicing up your state beyond atoms might be desirable.
//!
//! Instead of storing groups of entities in a collection of structs, the ECS Architecture instead stores
//! an array for each field in of a struct. This tends to improve performance for batch operations on
//! individual fields at the cost of complexity. Fortunately, this ECS model is built right into Recoil,
//! making it easier than ever to enable sharded datastructures in your app.
//!
//! Instead of defining a struct for our primary datastructure, we'll instead use a type tuple, and then later
//! index that tuple to get the value we care about. Unfortunately, we lose name information wrt to each
//! type in the type tuple. This can be solved with an associated module, the derive EcsMacro, or just
//! by good documentation.
//!
//! This approach is best suited for applications where individual entries in families are very large
//! and updates to neighbors are costly in terms of Clone or field comparisons for memoization.
use dioxus::prelude::*;
use dioxus_core as dioxus;
use recoil::*;
type TodoModel = (
bool, // checked
String, // name
String, // contents
);
const TODOS: EcsModel<u32, TodoModel> = |builder| {};
// const SELECT_TITLE: SelectorBorrowed<u32, &str> = |s, k| TODOS.field(0).select(k);
// const SELECT_SUBTITLE: SelectorBorrowed<u32, &str> = |s, k| TODOS.field(1).select(k);
static App: FC<()> = |cx| {
use_init_recoil_root(cx, |_| {});
// let title = use_recoil_value(cx, &C_SELECTOR);
let title = "";
rsx! { in cx,
div {
"{title}"
// button { onclick: {next_light}, "Next light" }
}
}
};
fn main() {
wasm_bindgen_futures::spawn_local(dioxus_web::WebsysRenderer::start(App))
}

View file

@ -1,54 +0,0 @@
use std::{collections::HashMap, rc::Rc};
use dioxus_core::prelude::*;
use recoil::*;
use uuid::Uuid;
const TODOS: AtomHashMap<Uuid, Rc<Todo>> = |map| {};
#[derive(PartialEq)]
struct Todo {
checked: bool,
title: String,
content: String,
}
static App: FC<()> = |cx| {
use_init_recoil_root(cx, move |cfg| {});
let todos = use_read_family(cx, &TODOS);
// rsx! { in cx,
// div {
// h1 {"Basic Todolist with AtomFamilies in Recoil.rs"}
// ul { { todos.keys().map(|id| rsx! { Child { id: *id } }) } }
// }
// }
cx.render(html! {
<a href="#" class="">
<img class="inline-block h-10 w-10 rounded-full object-cover ring-2 ring-white" src="/images/person/4.jpg" alt="Jade"/>
</a>
})
};
#[derive(Props, PartialEq)]
struct ChildProps {
id: Uuid,
}
static Child: FC<ChildProps> = |cx| {
let (todo, set_todo) = use_read_write(cx, &TODOS.select(&cx.id));
rsx! { in cx,
li {
h1 {"{todo.title}"}
input { type: "checkbox", name: "scales", checked: "{todo.checked}" }
label { "{todo.content}", for: "scales" }
p {"{todo.content}"}
}
}
};
fn main() {
wasm_bindgen_futures::spawn_local(dioxus_web::WebsysRenderer::start(App))
}

View file

@ -1,47 +0,0 @@
use dioxus_core::prelude::*;
use im_rc::HashMap as ImMap;
use recoil::*;
use uuid::Uuid;
const TODOS: Atom<ImMap<Uuid, Todo>> = |_| ImMap::new();
#[derive(PartialEq)]
struct Todo {
checked: bool,
title: String,
contents: String,
}
static App: FC<()> = |cx| {
use_init_recoil_root(cx, |_| {});
let todos = use_read(&cx, &TODOS);
rsx! { in cx,
div {
"Basic Todolist with AtomFamilies in Recoil.rs"
}
}
};
#[derive(Props, PartialEq)]
struct ChildProps {
id: Uuid,
}
static Child: FC<ChildProps> = |cx| {
let todo = use_read(cx, &TODOS).get(&cx.id).unwrap();
// let (todo, set_todo) = use_read_write(cx, &TODOS);
rsx! { in cx,
div {
h1 {"{todo.title}"}
input { type: "checkbox", name: "scales", checked: "{todo.checked}" }
label { "{todo.contents}", for: "scales" }
p {"{todo.contents}"}
}
}
};
fn main() {
wasm_bindgen_futures::spawn_local(dioxus_web::WebsysRenderer::start(App))
}

View file

@ -1,29 +0,0 @@
use dioxus_core::prelude::*;
use recoil::*;
const COUNT: Atom<i32> = |_| 0;
static App: FC<()> = |cx| {
use_init_recoil_root(cx, |_| {});
let (count, set_count) = use_read_write(&cx, &COUNT);
rsx! { in cx,
div {
"Count: {count}"
br {}
button { onclick: move |_| set_count(count + 1), "<Incr" }
">___<"
button { onclick: move |_| set_count(count - 1), "Decr>" }
}
}
};
fn main() {
// Setup logging
wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
console_error_panic_hook::set_once();
log::debug!("asd");
wasm_bindgen_futures::spawn_local(dioxus_web::WebsysRenderer::start(App));
}

View file

@ -1,35 +0,0 @@
use std::collections::HashMap;
use dioxus_core::prelude::*;
use recoil::*;
const A_ITEMS: AtomHashMap<i32, i32> = |_| HashMap::new();
const B_ITEMS: AtomHashMap<i32, i32> = |_| HashMap::new();
const C_SELECTOR: SelectorFamily<i32, i32> = |api, key| {
let a = api.get(&A_ITEMS.select(&key));
let b = api.get(&B_ITEMS.select(&key));
a + b
};
const D_SELECTOR: SelectorFamilyBorrowed<i32, i32> = |api, key| -> &i32 {
let a = api.get(&A_ITEMS.select(&key));
a
};
static App: FC<()> = |cx| {
use_init_recoil_root(cx, |_| {});
let title = use_read(cx, &C_SELECTOR);
rsx! { in cx,
div {
"{title}"
// button { onclick: {next_light}, "Next light" }
}
}
};
fn main() {
wasm_bindgen_futures::spawn_local(dioxus_web::WebsysRenderer::start(App))
}

View file

@ -1,46 +0,0 @@
use dioxus_core::prelude::*;
use recoil::*;
const A: Atom<i32> = |_| 0;
const B: Atom<i32> = |_| 0;
const C: Selector<i32> = |api| api.get(&A) + api.get(&B);
static App: FC<()> = |cx| {
use_init_recoil_root(cx, |_| {});
rsx! { in cx,
div {
Banner {}
BtnA {}
BtnB {}
}
}
};
static Banner: FC<()> = |cx| {
let count = use_read(&cx, &C);
cx.render(rsx! { h1 { "Count: {count}" } })
};
static BtnA: FC<()> = |cx| {
let (a, set) = use_read_write(&cx, &A);
rsx! { in cx,
div { "a"
button { "+", onclick: move |_| set(a + 1) }
button { "-", onclick: move |_| set(a - 1) }
}
}
};
static BtnB: FC<()> = |cx| {
let (b, set) = use_read_write(&cx, &B);
rsx! { in cx,
div { "b"
button { "+", onclick: move |_| set(b + 1) }
button { "-", onclick: move |_| set(b - 1) }
}
}
};
fn main() {
wasm_bindgen_futures::spawn_local(dioxus_web::WebsysRenderer::start(App))
}

View file

@ -1,10 +0,0 @@
fn main() {}
use dioxus_core::prelude::*;
static App: FC<()> = |cx| {
//
use_initialize_app()
};
fn use_initialize_app(cx: &impl Scoped) {}

View file

@ -1,50 +0,0 @@
// const Posts: AtomFamily = |family| {
// family.on_get(|key| {
// //
// })
// };
fn main() {
wasm_bindgen_futures::spawn_local(dioxus_web::WebsysRenderer::start(App))
}
use std::future::Future;
use dioxus_core::prelude::*;
static App: FC<()> = |cx| {
//
let title = use_async_atom();
let title_card = suspend(&cx, title, move |val| {
//
rsx!(in cx, div {
h3 { "{val}" }
})
});
// let fut = (use_async_atom(), use_async_atom());
// let title_card2 = cx.suspend(fut, move |(text, text2)| {
// cx.render(rsx!( h3 { "{text}" } ))
// });
cx.render(rsx! {
div {
{title_card}
// {title_card2}
}
})
};
async fn use_async_atom() -> String {
todo!()
}
fn suspend<'a, O>(
c: &impl Scoped<'a>,
f: impl Future<Output = O>,
g: impl FnOnce(O) -> VNode<'a> + 'a,
) -> VNode<'a> {
todo!()
}

View file

@ -1 +0,0 @@
println!("asd")

View file

@ -1,112 +0,0 @@
# Architecture
## ECS
It's often ideal to represent list-y state as an SoA (struct of arrays) instead of an AoS (array of structs). In 99% of apps, normal clone-y performance is fine. If you need more performance on top of cloning on update, IM.rs will provide fast immutable data structures.
But, if you need **extreme performance** consider the ECS (SoA) model. With ECS model, we can modify fields of an entry without invalidating selectors on neighboring fields.
This approach is for that 0.1% of apps that need peak performance. Our philosophy is that these tools should be available when needed, but you shouldn't need to reach for them often. An example use case might be a graphics editor or simulation engine where thousands of entities with many fields are rendered to the screen in realtime.
Recoil will even help you:
```rust
type TodoModel = (
String, // title
String // subtitle
);
const TODOS: RecoilEcs<u32, TodoModel> = |builder| {
builder.push("SomeTitle".to_string(), "SomeSubtitle".to_string());
};
const SELECT_TITLE: SelectorBorrowed<u32, &str> = |s, k| TODOS.field(0).select(k);
const SELECT_SUBTITLE: SelectorBorrowed<u32, &str> = |s, k| TODOS.field(1).select(k);
```
Or with a custom derive macro to take care of some boilerplate and maintain readability. This macro simply generates the type tuple from the model fields and then some associated constants for indexing them.
```rust
#[derive(EcsModel)]
struct TodoModel {
title: String,
subtitle: String
}
// derives these impl (you don't need to write this yourself, but can if you want):
mod TodoModel {
type Layout = (String, String);
const title: u8 = 1;
const subtitle: u8 = 2;
}
const TODOS: RecoilEcs<u32, TodoModel::Layout> = |builder| {};
const SELECT_TITLE: SelectorBorrowed<u32, &str> = |s, k| TODOS.field(TodoModel::title).select(k);
const SELECT_SUBTITLE: SelectorBorrowed<u32, &str> = |s, k| TODOS.field(TodoModel::subtitle).select(k);
```
## Optimization
## Selectors and references.
Because new values are inserted without touching the original, we can keep old values around. As such, it makes sense to allow borrowed data since we can continue to reference old data if the data itself hasn't changed.
However, this does lead to a greater memory overhead, so occasionally we'll want to sacrifice an a component render in order to evict old values. This will be done automatically for us due to the nature of Rc.
Also, we want selectors to have internal dependencies.
## Async Atoms
- make it so "waiting" data can be provided
- someone updates via a future IE `atom.set(|| async {})`
- RecoilJS handles this with a "loadable" that has different states (ready, waiting, err just like Rust's poll method on future)
- I think the old value is provided until the new value is ready(?)
- Integrates with suspense to directly await the contents
## Async atoms
```rust
let (atom, set_atom, modify_atom) = (ATOM.use_read(cx), ATOM.use_write(cx), ATOM.use_modify(cx));
const Title: AsyncAtom<String> = |builder| {
builder.on_set(|api, new_val| async {
})
builder.on_get(|api| async {
})
}
```
## Async selectors
```rust
struct ContentCard {
title: String,
content: String,
}
const ROOT_URL: Atom<&str> = |_| "localhost:8080";
// Since we don't plan on changing any content during the lifetime of the app, a selector works fine
const ContentCards: SelectorFamily<Uuid, ContentCard> = |api, key| api.on_get_async(async {
// Whenever the root_url changes, this atom will be re-evaluated
let root_url = api.get(&ROOT_URL);
let data: ContentCard = fetch(format!("{}/{}", root_url, key).await?.json().await?;
data
})
static ContentCard: FC<()> = |cx| {
let body = async match use_recoil_value()(cx.id).await {
Ok(content) => rsx!{in cx, p {"{content}"} }
Err(e) => rsx!{in cx, p {"Failed to load"}}
};
rsx!{
div {
h1{ "Non-async data here" }}
Suspense {
content: {body}
fallback: { rsx!{ div {"Loading..."} } }
}
}
}
};
```

View file

@ -1,58 +0,0 @@
# 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
}
```

View file

@ -1,573 +0,0 @@
use std::{
cell::{Ref, RefCell},
collections::HashMap,
hash::Hash,
marker::PhantomData,
rc::Rc,
};
pub use atomfamily::*;
pub use atoms::*;
pub use ecs::*;
use error::*;
pub use hooks::*;
pub use hooks::*;
pub use root::*;
pub use selector::*;
pub use selectorfamily::*;
pub use traits::*;
pub use utils::*;
mod tracingimmap;
mod traits {
use dioxus_core::prelude::Context;
use super::*;
pub trait MapKey: PartialEq + Hash + 'static {}
impl<T: PartialEq + Hash + 'static> MapKey for T {}
pub trait AtomValue: PartialEq + 'static {}
impl<T: PartialEq + 'static> AtomValue for T {}
// Atoms, selectors, and their family variants are readable
pub trait Readable<T: AtomValue>: Sized + Copy {
fn use_read<'a, P: 'static>(self, cx: Context<'a, P>) -> &'a T {
hooks::use_read(cx, self)
}
// This returns a future of the value
// If the atom is currently pending, that future will resolve to pending
// If the atom is currently ready, the future will immediately resolve
// if the atom switches from ready to pending, the component will re-run, returning a pending future
fn use_read_async<'a, P>(self, cx: Context<'a, P>) -> &'a T {
todo!()
}
fn initialize(self, api: &RecoilRoot) -> T;
// We use the Raw Ptr to the atom
// TODO: Make sure atoms with the same definitions don't get merged together. I don't think they do, but double check
fn static_id(self) -> u32;
}
// Only atoms and atom families are writable
// Selectors and selector families are not
pub trait Writable<T: AtomValue>: Readable<T> + Sized {
fn use_read_write<'a, P>(self, cx: Context<'a, P>) -> (&'a T, &'a Rc<dyn Fn(T)>) {
todo!()
}
fn use_write<'a, P>(self, cx: Context<'a, P>) -> &'a Rc<dyn Fn(T)> {
todo!()
}
}
}
mod atoms {
use super::*;
// Currently doesn't do anything, but will eventually add effects, id, serialize/deserialize keys, etc
// Doesn't currently allow async values, but the goal is to eventually enable them
pub struct AtomBuilder {}
pub type Atom<T> = fn(&mut AtomBuilder) -> T;
// impl<T: AtomValue> Readable<T> for Atom<T> {}
impl<T: AtomValue> Readable<T> for &'static Atom<T> {
fn static_id(self) -> u32 {
self as *const _ as u32
}
fn initialize(self, api: &RecoilRoot) -> T {
let mut builder = AtomBuilder {};
let p = self(&mut builder);
p
}
}
impl<T: AtomValue> Writable<T> for &'static Atom<T> {}
mod compilests {
use super::*;
use dioxus_core::prelude::Context;
fn _test(cx: Context<()>) {
const EXAMPLE_ATOM: Atom<i32> = |_| 10;
// ensure that atoms are both read and write
let _ = use_read(cx, &EXAMPLE_ATOM);
let _ = use_read_write(cx, &EXAMPLE_ATOM);
let _ = use_write(cx, &EXAMPLE_ATOM);
}
}
}
mod atomfamily {
use super::*;
pub trait FamilyCollection<K, V> {}
impl<K, V> FamilyCollection<K, V> for HashMap<K, V> {}
use im_rc::HashMap as ImHashMap;
/// AtomHashMaps provide an efficient way of maintaing collections of atoms.
///
/// Under the hood, AtomHashMaps uses [IM](https://www.rust-lang.org)'s immutable HashMap implementation to lazily
/// clone data as it is modified.
///
///
///
///
///
///
pub type AtomHashMap<K, V> = fn(&mut ImHashMap<K, V>);
pub trait AtomFamilySelector<K: MapKey, V: AtomValue + Clone> {
fn select(&'static self, k: &K) -> AtomMapSelection<K, V> {
todo!()
}
}
impl<K: MapKey, V: AtomValue + Clone> AtomFamilySelector<K, V> for AtomHashMap<K, V> {
fn select(&'static self, k: &K) -> AtomMapSelection<K, V> {
todo!()
}
}
pub struct AtomMapSelection<'a, K: MapKey, V: AtomValue> {
root: &'static AtomHashMap<K, V>,
key: &'a K,
}
impl<'a, K: MapKey, V: AtomValue> Readable<V> for &AtomMapSelection<'a, K, V> {
fn static_id(self) -> u32 {
todo!()
}
fn initialize(self, api: &RecoilRoot) -> V {
todo!()
// let mut builder = AtomBuilder {};
// let p = self(&mut builder);
// p
}
}
impl<'a, K: MapKey, T: AtomValue> Writable<T> for &AtomMapSelection<'a, K, T> {}
mod compiletests {
use dioxus_core::prelude::Context;
use super::*;
const Titles: AtomHashMap<u32, &str> = |map| {};
fn test(cx: Context<()>) {
let title = Titles.select(&10).use_read(cx);
let t2 = use_read(cx, &Titles.select(&10));
}
}
}
mod selector {
use super::*;
pub struct SelectorBuilder {}
impl SelectorBuilder {
pub fn get<T: AtomValue>(&self, t: impl Readable<T>) -> &T {
todo!()
}
}
pub type Selector<T> = fn(&mut SelectorBuilder) -> T;
impl<T: AtomValue> Readable<T> for &'static Selector<T> {
fn static_id(self) -> u32 {
todo!()
}
fn initialize(self, api: &RecoilRoot) -> T {
todo!()
}
}
pub struct SelectorFamilyBuilder {}
impl SelectorFamilyBuilder {
pub fn get<T: AtomValue>(&self, t: impl Readable<T>) -> &T {
todo!()
}
}
}
mod selectorfamily {
use super::*;
// pub trait SelectionSelector<K, V> {
// fn select(&self, k: &K) -> CollectionSelection<K, V> {
// todo!()
// }
// }
// impl<K, V, F> SelectionSelector<K, V> for AtomFamily<K, V, F> {}
/// Create a new value as a result of a combination of previous values
/// If you need to return borrowed data, check out [`SelectorFamilyBorrowed`]
pub type SelectorFamily<Key, Value> = fn(&mut SelectorFamilyBuilder, Key) -> Value;
impl<K, V: AtomValue> Readable<V> for &'static SelectorFamily<K, V> {
fn static_id(self) -> u32 {
todo!()
}
fn initialize(self, api: &RecoilRoot) -> V {
todo!()
}
}
/// Borrowed selector families are surprisingly - discouraged.
/// This is because it's not possible safely memoize these values without keeping old versions around.
///
/// However, it does come in handy to borrow the contents of an item without re-rendering child components.
pub type SelectorFamilyBorrowed<Key, Value> =
for<'a> fn(&'a mut SelectorFamilyBuilder, Key) -> &'a Value;
// impl<'a, K, V: 'a> SelectionSelector<K, V> for fn(&'a mut SelectorFamilyBuilder, K) -> V {}
}
mod root {
use std::{
any::{Any, TypeId},
collections::{HashSet, VecDeque},
iter::FromIterator,
sync::atomic::{AtomicU32, AtomicUsize},
};
use super::*;
// use generational_arena::Index as ConsumerId;
type AtomId = u32;
type ConsumerId = u32;
pub type RecoilContext = RefCell<RecoilRoot>;
// Sometimes memoization means we don't need to re-render components that holds "correct values"
// IE we consider re-render more expensive than keeping the old value around.
// We *could* unsafely overwrite this slot, but that's just **asking** for UB (holding a &mut while & is held in components)
//
// Instead, we choose to let the hook itself hold onto the Rc<T> by not forcing a render when T is the same.
// Whenever the component needs to be re-rendered for other reasons, the "get" method will automatically update the Rc<T> to the most recent one.
pub struct RecoilRoot {
nodes: RefCell<HashMap<AtomId, Slot>>,
consumer_map: HashMap<ConsumerId, AtomId>,
}
struct Slot {
type_id: TypeId,
source: AtomId,
value: Rc<dyn Any>,
consumers: HashMap<ConsumerId, Rc<dyn Fn()>>,
dependents: HashSet<AtomId>,
}
static NEXT_ID: AtomicU32 = AtomicU32::new(0);
fn next_consumer_id() -> u32 {
NEXT_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
}
impl RecoilRoot {
pub(crate) fn new() -> Self {
Self {
nodes: Default::default(),
consumer_map: Default::default(),
}
}
pub fn subscribe<T: AtomValue>(
&mut self,
readable: impl Readable<T>,
receiver_fn: Rc<dyn Fn()>,
) -> ConsumerId {
let consumer_id = next_consumer_id();
let atom_id = readable.static_id();
log::debug!("Subscribing consumer to atom {} {}", consumer_id, atom_id);
let mut nodes = self.nodes.borrow_mut();
let slot = nodes.get_mut(&atom_id).unwrap();
slot.consumers.insert(consumer_id, receiver_fn);
self.consumer_map.insert(consumer_id, atom_id);
consumer_id
}
pub fn unsubscribe(&mut self, consumer_id: ConsumerId) {
let atom_id = self.consumer_map.get(&consumer_id).unwrap();
let mut nodes = self.nodes.borrow_mut();
let slot = nodes.get_mut(&atom_id).unwrap();
slot.consumers.remove(&consumer_id);
}
/// Directly get the *slot*
/// All Atoms are held in slots (an Rc)
///
///
pub fn try_get_raw<T: AtomValue>(&self, readable: impl Readable<T>) -> Result<Rc<T>> {
let atom_id = readable.static_id();
let mut nodes = self.nodes.borrow_mut();
if !nodes.contains_key(&atom_id) {
let value = Slot {
type_id: TypeId::of::<T>(),
source: atom_id,
value: Rc::new(readable.initialize(self)),
consumers: Default::default(),
dependents: Default::default(),
};
nodes.insert(atom_id, value);
}
let out = nodes
.get(&atom_id)
.unwrap()
.value
.clone()
.downcast::<T>()
.unwrap();
Ok(out)
}
pub fn try_set<T: AtomValue>(
&mut self,
writable: impl Writable<T>,
new_val: T,
) -> crate::error::Result<()> {
let atom_id = writable.static_id();
self.set_by_id(atom_id, new_val);
Ok(())
}
// A slightly dangerous method to manually overwrite any slot given an AtomId
pub(crate) fn set_by_id<T: AtomValue>(&mut self, atom_id: AtomId, new_val: T) {
let mut nodes = self.nodes.borrow_mut();
let consumers = match nodes.get_mut(&atom_id) {
Some(slot) => {
slot.value = Rc::new(new_val);
&slot.consumers
}
None => {
let value = Slot {
type_id: TypeId::of::<T>(),
source: atom_id,
value: Rc::new(new_val),
// value: Rc::new(writable.initialize(self)),
consumers: Default::default(),
dependents: Default::default(),
};
nodes.insert(atom_id, value);
&nodes.get(&atom_id).unwrap().consumers
}
};
for (id, consumer_fn) in consumers {
log::debug!("triggering selector {}", id);
consumer_fn();
}
}
}
}
mod hooks {
use super::*;
use dioxus_core::prelude::Context;
pub fn use_init_recoil_root<P>(cx: Context<P>, cfg: impl Fn(())) {
cx.use_create_context(move || RefCell::new(RecoilRoot::new()))
}
/// Gain access to the recoil API directly - set, get, modify, everything
/// This is the foundational hook in which read/write/modify are built on
///
/// This does not subscribe the component to *any* updates
///
/// You can use this method to create controllers that perform much more complex actions than set/get
/// However, be aware that "getting" values through this hook will not subscribe the component to any updates.
pub fn use_recoil_api<'a, P>(cx: Context<'a, P>) -> &'a Rc<RecoilContext> {
cx.use_context::<RecoilContext>()
}
pub fn use_write<'a, P, T: AtomValue>(
cx: Context<'a, P>,
// todo: this shouldn't need to be static
writable: impl Writable<T>,
) -> &'a Rc<dyn Fn(T)> {
let api = use_recoil_api(cx);
cx.use_hook(
move |_| {
let api = api.clone();
let raw_id = writable.static_id();
Rc::new(move |new_val| {
//
log::debug!("setting new value ");
let mut api = api.as_ref().borrow_mut();
// api.try_set(writable, new_val).expect("failed to set");
api.set_by_id(raw_id, new_val);
}) as Rc<dyn Fn(T)>
},
move |hook| &*hook,
|hook| {},
)
}
/// Read the atom and get the Rc directly to the Atom's slot
/// This is useful if you need the memoized Atom value. However, Rc<T> is not as easy to
/// work with as
pub fn use_read_raw<'a, P, T: AtomValue>(
cx: Context<'a, P>,
readable: impl Readable<T>,
) -> &'a Rc<T> {
struct ReadHook<T> {
value: Rc<T>,
consumer_id: u32,
}
let api = use_recoil_api(cx);
cx.use_hook(
move |_| {
let mut api = api.as_ref().borrow_mut();
let update = cx.schedule_update();
let val = api.try_get_raw(readable).unwrap();
let id = api.subscribe(readable, update);
ReadHook {
value: val,
consumer_id: id,
}
},
move |hook| {
let api = api.as_ref().borrow();
let val = api.try_get_raw(readable).unwrap();
hook.value = val;
&hook.value
},
move |hook| {
let mut api = api.as_ref().borrow_mut();
api.unsubscribe(hook.consumer_id);
},
)
}
///
pub fn use_read<'a, P, T: AtomValue>(cx: Context<'a, P>, readable: impl Readable<T>) -> &'a T {
use_read_raw(cx, readable).as_ref()
}
/// # Use an atom in both read and write
///
/// This method is only available for atoms and family selections (not selectors).
///
/// This is equivalent to calling both `use_read` and `use_write`, but saves you the hassle and repitition
///
/// ## Example
///
/// ```
/// const Title: Atom<&str> = |_| "hello";
/// let (title, set_title) = use_read_write(cx, &Title);
///
/// // equivalent to:
/// let (title, set_title) = (use_read(cx, &Title), use_write(cx, &Title));
/// ```
pub fn use_read_write<'a, P, T: AtomValue + 'static>(
cx: Context<'a, P>,
writable: impl Writable<T>,
) -> (&'a T, &'a Rc<dyn Fn(T)>) {
(use_read(cx, writable), use_write(cx, writable))
}
/// # Modify an atom without using `use_read`.
///
/// Occasionally, a component might want to write to an atom without subscribing to its changes. `use_write` does not
/// provide this functionality, so `use_modify` exists to gain access to the current atom value while setting it.
///
/// ## Notes
///
/// Do note that this hook can only be used with Atoms where T: Clone since we actually clone the current atom to make
/// it mutable.
///
/// Also note that you need to stack-borrow the closure since the modify closure expects an &dyn Fn. If we made it
/// a static type, it wouldn't be possible to use the `modify` closure more than once (two closures always are different)
///
/// ## Example
///
/// ```ignore
/// let modify_atom = use_modify(cx, Atom);
///
/// modify_atom(&|a| *a += 1)
/// ```
pub fn use_modify<'a, T: AtomValue + 'static + Clone, P>(
cx: Context<'a, P>,
writable: impl Writable<T>,
) -> impl Fn(&dyn Fn()) {
|_| {}
}
/// Use a family collection directly
/// !! Any changes to the family will cause this subscriber to update
/// Try not to put this at the very top-level of your app.
pub fn use_read_family<'a, K, V, P>(
cx: Context<'a, P>,
t: &AtomHashMap<K, V>,
) -> &'a im_rc::HashMap<K, V> {
todo!()
}
}
mod ecs {
use super::*;
pub struct Blah<K, V> {
_p: PhantomData<(K, V)>,
}
pub type EcsModel<K, Ty> = fn(Blah<K, Ty>);
}
mod utils {
use super::*;
use dioxus_core::prelude::*;
pub struct RecoilApi {}
/// This tiny util wraps your main component with the initializer for the recoil root.
/// This is useful for small programs and the examples in this crate
pub fn RecoilApp<T: 'static>(
root: impl for<'a> Fn(Context<'a, T>) -> VNode,
) -> impl for<'a> Fn(Context<'a, T>) -> VNode {
move |cx| {
use_init_recoil_root(cx, |_| {});
root(cx)
}
}
}
mod compiletests {}
pub mod error {
use thiserror::Error as ThisError;
pub type Result<T, E = Error> = std::result::Result<T, E>;
#[derive(ThisError, Debug)]
pub enum Error {
#[error("Fatal Internal Error: {0}")]
FatalInternal(&'static str),
// #[error("Context is missing")]
// MissingSharedContext,
// #[error("No event to progress")]
// NoEvent,
// #[error("Wrong Properties Type")]
// WrongProps,
// #[error("Base scope has not been mounted yet")]
// NotMounted,
// #[error("I/O Error: {0}")]
// BorrowMut(#[from] std::),
// #[error("eee Error: {0}")]
// IO(#[from] core::result::),
#[error("I/O Error: {0}")]
IO(#[from] std::io::Error),
#[error(transparent)]
Other(#[from] anyhow::Error), // source and Display delegate to anyhow::Error
}
}

View file

@ -1,29 +0,0 @@
//! Tracing immap
//! Traces modifications since last generation
//! To reconstruct the history, you will need *all* the generations between the start and end points
use im_rc::HashMap as ImMap;
pub struct TracedHashMap<K, V> {
inner: ImMap<K, V>,
generation: u32,
mods_since_last_gen: Vec<K>,
}
impl<K: Clone, V: Clone> TracedHashMap<K, V> {
fn next_generation(&self) -> Self {
Self {
generation: self.generation + 1,
inner: self.inner.clone(),
mods_since_last_gen: vec![],
}
}
}
#[test]
fn compare_dos() {
let map1 = im_rc::hashmap! {3 => 2, 2 => 3};
let map2 = im_rc::hashmap! {2 => 3, 3 => 2};
assert_eq!(map1, map2);
}

View file

@ -1,5 +1,5 @@
[package]
name = "dioxus-webview"
name = "dioxus-desktop"
version = "0.0.0"
authors = ["Jonathan Kelley <jkelleyrtp@gmail.com>"]
edition = "2018"

View file

@ -2,7 +2,7 @@ use dioxus_core as dioxus;
use dioxus_core::prelude::*;
fn main() {
dioxus_webview::launch(App, |f| f.with_focus().with_maximized(true)).expect("Failed");
dioxus_webview::launch(App, |f| f.with_maximized(true)).expect("Failed");
}
static App: FC<()> = |cx| {

View file

@ -132,9 +132,6 @@
<body>
<div id="_dioxusroot">
<div>
</div>
</div>
</body>

View file

@ -4,6 +4,7 @@ use std::sync::mpsc::channel;
use std::sync::{Arc, RwLock};
use dioxus_core::*;
pub use wry;
use wry::application::event::{Event, WindowEvent};
use wry::application::event_loop::{ControlFlow, EventLoop};
@ -72,6 +73,8 @@ impl<T: Properties + 'static> WebviewRenderer<T> {
let registry = Arc::new(RwLock::new(Some(WebviewRegistry::new())));
let webview = WebViewBuilder::new(window)?
// .with_visible(false)
// .with_transparent(true)
.with_url(&format!("data:text/html,{}", HTML_CONTENT))?
.with_rpc_handler(move |_window: &Window, mut req: RpcRequest| {
match req.method.as_str() {
@ -126,19 +129,33 @@ impl<T: Properties + 'static> WebviewRenderer<T> {
.build()?;
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
*control_flow = ControlFlow::Poll;
match event {
Event::WindowEvent { event, .. } => {
//
match event {
Event::WindowEvent {
event, window_id, ..
} => match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(_) | WindowEvent::Moved(_) => {
let _ = webview.resize();
}
_ => {}
},
Event::MainEventsCleared => {
webview.resize();
// window.request_redraw();
}
}
_ => {
// let _ = webview.resize();
}
_ => {} // Event::WindowEvent { event, .. } => {
// //
// match event {
// WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
// _ => {}
// }
// }
// _ => {
// // let _ = webview.resize();
// }
}
});
}

View file

@ -1,3 +0,0 @@
{
"rust-analyzer.inlayHints.enable": false
}

View file

@ -1,13 +0,0 @@
[package]
name = "dioxus-docs-site"
version = "0.0.0"
authors = ["Jonathan Kelley <jkelleyrtp@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
dioxus-ssr = { path="../ssr" }
pulldown-cmark = "0.8.0"
atoms = { path="../atoms" }
codeblocks = { path="../../../ecosystem-dioxus/syntec-dioxus/" }

View file

@ -1,10 +0,0 @@
# Dioxus docs site
Generate the liveview site powering diouxslabs.com.
We use the dioxus SSR crate and the use_router hook to generate all the routes

View file

@ -1,17 +0,0 @@
<svg
x="0px"
y="0px"
viewBox="0 0 100 100"
width="15"
height="15"
class="css-83uoqv"
>
<path
fill="currentColor"
d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0, 0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"
></path>
<polygon
fill="currentColor"
points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"
></polygon>
</svg>

Before

Width:  |  Height:  |  Size: 427 B

View file

@ -1,9 +0,0 @@
use dioxus_ssr::prelude::*;
pub static Blog: FC<()> = |cx| {
rsx! { in cx,
div {
}
}
};

View file

@ -1,9 +0,0 @@
use dioxus_ssr::prelude::*;
pub static Community: FC<()> = |cx| {
rsx! { in cx,
div {
}
}
};

View file

@ -1,9 +0,0 @@
use dioxus_ssr::prelude::*;
pub static Docs: FC<()> = |cx| {
rsx! { in cx,
div {
}
}
};

View file

@ -1,42 +0,0 @@
use dioxus_ssr::prelude::*;
const HeroContent: [(&'static str, &'static str); 3] = [
("Declarative",
"React makes it painless to create interactive UIs. Design simple views for each state in your application, and React will efficiently update and render just the right components when your data changes.\nDeclarative views make your code more predictable and easier to debug."),
("Component-Based", "Build encapsulated components that manage their own state, then compose them to make complex UIs.\nSince component logic is written in JavaScript instead of templates, you can easily pass rich data through your app and keep state out of the DOM."),
("Learn Once, Write Anywhere", "We dont make assumptions about the rest of your technology stack, so you can develop new features in React without rewriting existing code.\nReact can also render on the server using Node and power mobile apps using React Native."),
];
const SnippetHighlights: &'static str = include_str!("./../snippets.md");
pub static Home: FC<()> = |cx| {
let hero = HeroContent.iter().map(|(title, body)| {
rsx! {
div {
h3 { "{title}" }
div { {body.split("\n").map(|paragraph| rsx!( p{"{paragraph}"} ))} }
}
}
});
let snippets: Vec<VNode> = crate::utils::markdown_to_snippet(cx, SnippetHighlights);
rsx! { in cx,
div {
header {
// Hero
section {
div { {hero} }
}
hr {}
// Highlighted Snippets
section {
{snippets}
}
}
div {}
section {}
}
}
};

View file

@ -1,9 +0,0 @@
use dioxus_ssr::prelude::*;
pub static Tutorial: FC<()> = |cx| {
rsx! { in cx,
div {
}
}
};

View file

@ -1,152 +0,0 @@
#![allow(non_upper_case_globals)]
use dioxus_ssr::{prelude::*, TextRenderer};
pub mod utils;
mod components {
mod community;
pub use community::*;
mod docs;
pub use docs::*;
mod blog;
pub use blog::*;
mod homepage;
pub use homepage::*;
mod tutorial;
pub use tutorial::*;
}
use components::*;
fn main() {
let renderer = TextRenderer::new(App);
/*
renderer.render_at("community")
*/
}
static App: FC<()> = |cx| {
let (url, set_url) = use_state(cx, || "");
let body = match *url {
"community" => rsx!(in cx, Community {}),
"tutorial" => rsx!(in cx, Tutorial {}),
"blog" => rsx!(in cx, Blog {}),
"docs" => rsx!(in cx, Docs {}),
_ => rsx!(in cx, Home {}),
};
cx.render(rsx! {
div {
NavBar {}
{body}
Footer {}
}
})
};
static NavBar: FC<()> = |cx| {
cx.render(rsx! {
header {
a {
href: "/"
img { /*logo*/ }
span {}
}
nav {
a { href: "/community/support", "Community" }
a { href: "/docs/getting-started", "Docs" }
a { href: "/tutorial/tutorial", "Tutorial" }
a { href: "/blog/", "Blog" }
}
form {}
div {}
}
})
};
static SECTIONS: &[(&str, &[(&str, &str)])] = &[
(
"Docs",
&[
("Installation", "docs/installation"),
("Main Concepts", "docs/main"),
("Advanced Guides", "docs/advanced"),
("Hooks", "docs/hooks"),
("Testing", "docs/testing"),
("Contributing", "docs/contributing"),
("FAQ", "docs/faq"),
],
),
(
"Channels",
&[("Github", "https://github.com/jkelleyrtp/dioxus")],
),
(
"Community",
&[
("Code of Conduct", "docs/installation"),
("Community Resources", "docs/main"),
],
),
(
"More",
&[
("Tutorial", "docs/installation"),
("Blog", "docs/main"),
("Privacy", "docs/advanced"),
("Terms", "docs/hooks"),
],
),
];
fn Footer(cx: Context<()>) -> VNode {
let sections = SECTIONS.iter().map(|(section, raw_links)| {
let links = raw_links.iter().map(|(link_name, href)| {
rsx! (
a { href: "{href}",
"{link_name}",
{href.starts_with("http").then(|| rsx!( ExternalLinkIcon {} ))}
}
)
});
rsx! {
div {
div { "{section}" }
{links}
}
}
});
cx.render(rsx! {
footer {
div {
div {
div {
{sections}
}
section {
a {
img {}
}
p {}
}
}
}
}
})
}
const ExternalLinkIcon: FC<()> = |cx| {
cx.render(html! {
<svg x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15">
<path
fill="currentColor"
d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0, 0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"
></path>
<polygon
fill="currentColor"
points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"
></polygon>
</svg>
})
};

View file

@ -1,56 +0,0 @@
# A Simple Component
```rust
#[derive(PartialEq, Properties)]
struct Props { name: &'static str }
static HelloMessage: FC<Props> = |cx| {
cx.render(rsx!{
div { "Hello {cx.props.name}" }
})
}
```
# Two syntaxes: html! and rsx!
Choose from a close-to-html syntax or the standard rsx! syntax
```rust
static HelloMessage: FC<()> = |cx| {
cx.render(html!{
<div> Hello World! </div>
})
}
```
# A Stateful Component
Store state with hooks!
```rust
enum LightState {
Green
Yellow,
Red,
}
static HelloMessage: FC<()> = |cx| {
let (color, set_color) = use_state(cx, || LightState::Green);
let title = match color {
Green => "Green means go",
Yellow => "Yellow means slow down",
Red => "Red means stop",
};
cx.render(rsx!{
h1 { "{title}" }
button { "tick"
onclick: move |_| set_color(match color {
Green => Yellow,
Yellow => Red,
Red => Green,
})
}
})
}
```

View file

@ -1,31 +0,0 @@
use dioxus_ssr::prelude::{Context, VNode};
// Parse a snippet into
pub fn markdown_to_snippet<'a, P>(cx: Context<'a, P>, text: &str) -> Vec<VNode<'a>> {
let snips = Vec::new();
use pulldown_cmark::{Options, Parser};
let mut options = Options::empty();
let mut parser = Parser::new_ext(text, options);
while let Some(evt) = parser.next() {
match evt {
pulldown_cmark::Event::Start(tag) => {
// take until the end
let r = parser.next();
}
// push a p{} tag with the contents
pulldown_cmark::Event::Text(text) => todo!(),
// Code delinates an end
pulldown_cmark::Event::Code(code) => todo!(),
// not supported
pulldown_cmark::Event::Html(ht) => {}
_ => {}
}
}
//
snips
}

View file

@ -1207,6 +1207,7 @@ builder_constructors! {
srcset: String, // FIXME this is much more complicated
usemap: String, // FIXME should be a fragment starting with '#'
width: usize,
referrerpolicy: String,
// sizes: SpacedList<String>, // FIXME it's not really just a string
};

View file

@ -0,0 +1,2 @@
[build]
target = "aarch64-apple-ios"

5
packages/mobile/.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,5 @@
{
"rust-analyzer.inlayHints.enable": true,
"rust-analyzer.checkOnSave.allTargets": true,
"rust-analyzer.cargo.target": "aarch64-apple-ios"
}

View file

@ -1,5 +1,5 @@
[package]
name = "dioxus-ios"
name = "dioxus-mobile"
version = "0.0.0"
authors = ["Jonathan Kelley <jkelleyrtp@gmail.com>"]
edition = "2018"
@ -7,6 +7,19 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
cacao = { git = "https://github.com/ryanmcgrath/cacao" }
anyhow = "1.0.42"
# cacao = { git = "https://github.com/ryanmcgrath/cacao" }
dioxus-core = { path = "../core", version = "0.1.2" }
log = "0.4.14"
serde = "1.0.126"
serde_json = "1.0.64"
wry = "0.11.0"
[target.'cfg(target_os = "android")'.dependencies]
android_logger = "0.9.0"
log = "0.4.11"
ndk-glue = "0.2.1"
[target.'cfg(not(target_os = "android"))'.dependencies]
simple_logger = "1.11.0"

View file

@ -0,0 +1,43 @@
//! webview dom
use dioxus_core::{DomEdit, RealDom, RealDomNode, ScopeId};
use DomEdit::*;
pub struct WebviewRegistry {}
impl WebviewRegistry {
pub fn new() -> Self {
Self {}
}
}
pub struct WebviewDom<'bump> {
pub edits: Vec<DomEdit<'bump>>,
pub node_counter: u64,
pub registry: WebviewRegistry,
}
impl WebviewDom<'_> {
pub fn new(registry: WebviewRegistry) -> Self {
Self {
edits: Vec::new(),
node_counter: 0,
registry,
}
}
// Finish using the dom (for its edit list) and give back the node and event registry
pub fn consume(self) -> WebviewRegistry {
self.registry
}
}
impl<'bump> RealDom<'bump> for WebviewDom<'bump> {
fn raw_node_as_any(&self) -> &mut dyn std::any::Any {
todo!()
// self.edits.push(PushRoot { root });
}
fn request_available_node(&mut self) -> RealDomNode {
self.node_counter += 1;
RealDomNode::from_u64(self.node_counter)
}
}

View file

@ -1,70 +1,230 @@
use dioxus::virtual_dom::VirtualDom;
pub use dioxus_core as dioxus;
use dioxus_core::{events::EventTrigger, prelude::FC};
use dioxus_core::*;
use std::{
collections::HashMap,
sync::{Arc, RwLock},
};
use wry::application::event::{Event, WindowEvent};
use wry::application::event_loop::{ControlFlow, EventLoop};
use wry::application::window::Fullscreen;
use wry::application::{
dpi::LogicalSize,
event::StartCause,
platform::ios::{ScreenEdge, WindowBuilderExtIOS, WindowExtIOS},
};
use wry::webview::WebViewBuilder;
use wry::{
application::window::{Window, WindowBuilder},
webview::{RpcRequest, RpcResponse},
};
mod dom;
use dom::*;
pub struct IosRenderer {
internal_dom: VirtualDom,
fn init_logging() {
simple_logger::SimpleLogger::new().init().unwrap();
}
impl IosRenderer {
pub async fn run(&mut self) -> dioxus_core::error::Result<()> {
let (sender, mut receiver) = async_channel::unbounded::<EventTrigger>();
static HTML_CONTENT: &'static str = include_str!("./../../webview/src/index.html");
// let body_element = prepare_websys_dom();
pub fn launch(root: FC<()>, builder: fn(WindowBuilder) -> WindowBuilder) -> anyhow::Result<()> {
launch_with_props(root, (), builder)
}
pub fn launch_with_props<P: Properties + 'static>(
root: FC<P>,
props: P,
builder: fn(WindowBuilder) -> WindowBuilder,
) -> anyhow::Result<()> {
WebviewRenderer::run(root, props, builder)
}
let mut patch_machine = interpreter::PatchMachine::new(body_element.clone(), move |ev| {
log::debug!("Event trigger! {:#?}", ev);
let mut c = sender.clone();
// wasm_bindgen_futures::spawn_local(async move {
// c.send(ev).await.unwrap();
// });
/// The `WebviewRenderer` provides a way of rendering a Dioxus Virtual DOM through a bridge to a Webview instance.
/// Components used in WebviewRenderer instances can directly use system libraries, access the filesystem, and multithread with ease.
pub struct WebviewRenderer<T> {
/// The root component used to render the Webview
root: FC<T>,
}
enum RpcEvent<'a> {
Initialize {
//
edits: Vec<DomEdit<'a>>,
},
}
impl<T: Properties + 'static> WebviewRenderer<T> {
pub fn run(
root: FC<T>,
props: T,
user_builder: fn(WindowBuilder) -> WindowBuilder,
) -> anyhow::Result<()> {
Self::run_with_edits(root, props, user_builder, None)
}
pub fn run_with_edits(
root: FC<T>,
props: T,
user_builder: fn(WindowBuilder) -> WindowBuilder,
redits: Option<Vec<DomEdit<'static>>>,
) -> anyhow::Result<()> {
// pub fn run_with_edits(
// root: FC<T>,
// props: T,
// user_builder: fn(WindowBuilder) -> WindowBuilder,
// redits: Option<Vec<DomEdit<'static>>>,
// ) -> anyhow::Result<()> {
let mut weviews = HashMap::new();
let vir = VirtualDom::new_with_props(root, props);
let vdom = Arc::new(RwLock::new(vir));
let event_loop = EventLoop::new();
event_loop.run(move |event, event_loop, control_flow| {
*control_flow = ControlFlow::Wait;
match event {
Event::NewEvents(StartCause::Init) => {
println!("Init");
let window = user_builder(WindowBuilder::new())
.build(&event_loop)
.unwrap();
let registry = Arc::new(RwLock::new(Some(WebviewRegistry::new())));
let window = WindowBuilder::new().build(&event_loop).unwrap();
let window_id = window.id();
let vdom = vdom.clone();
let weview = WebViewBuilder::new(window)
.unwrap()
// .with_visible(false)
// .with_transparent(true)
.with_url(&format!("data:text/html,{}", HTML_CONTENT))
.unwrap()
.with_rpc_handler(move |_window: &Window, mut req: RpcRequest| {
match req.method.as_str() {
"initiate" => {
// let edits = if let Some(edits) = &redits {
// serde_json::to_value(edits).unwrap()
// } else
let edits = {
let mut lock = vdom.write().unwrap();
let mut reg_lock = registry.write().unwrap();
// Create the thin wrapper around the registry to collect the edits into
let mut real =
dom::WebviewDom::new(reg_lock.take().unwrap());
// Serialize the edit stream
let edits = {
let mut edits = Vec::new();
lock.rebuild(&mut real, &mut edits).unwrap();
serde_json::to_value(edits).unwrap()
};
// Give back the registry into its slot
*reg_lock = Some(real.consume());
edits
};
// Return the edits into the webview runtime
Some(RpcResponse::new_result(req.id.take(), Some(edits)))
}
"user_event" => {
let mut lock = vdom.write().unwrap();
let mut reg_lock = registry.write().unwrap();
// Create the thin wrapper around the registry to collect the edits into
let mut real = dom::WebviewDom::new(reg_lock.take().unwrap());
// Serialize the edit stream
let edits = {
let mut edits = Vec::new();
lock.rebuild(&mut real, &mut edits).unwrap();
serde_json::to_value(edits).unwrap()
};
// Give back the registry into its slot
*reg_lock = Some(real.consume());
// Return the edits into the webview runtime
Some(RpcResponse::new_result(req.id.take(), Some(edits)))
}
_ => todo!("this message failed"),
}
})
.build()
.unwrap();
// let weview = WebViewBuilder::new(window)
// .unwrap()
// .with_url("https://tauri.studio")
// .unwrap()
// .build()
// .unwrap();
weviews.insert(window_id, weview);
}
Event::Resumed => {
println!("applicationDidBecomeActive");
}
Event::Suspended => {
println!("applicationWillResignActive");
}
Event::LoopDestroyed => {
println!("applicationWillTerminate");
}
Event::WindowEvent {
window_id,
event: WindowEvent::Touch(touch),
..
} => {
println!("touch on {:?} {:?}", window_id, touch);
}
_ => {}
}
});
let root_node = body_element.first_child().unwrap();
patch_machine.stack.push(root_node.clone());
}
}
// todo: initialize the event registry properly on the root
fn main() {
init_logging();
let event_loop = EventLoop::new();
let edits = self.internal_dom.rebuild()?;
log::debug!("Received edits: {:#?}", edits);
edits.iter().for_each(|edit| {
log::debug!("patching with {:?}", edit);
patch_machine.handle_edit(edit);
let mut weviews = HashMap::new();
event_loop.run(move |event, event_loop, control_flow| {
*control_flow = ControlFlow::Wait;
match event {
Event::NewEvents(StartCause::Init) => {
println!("Init");
let window = WindowBuilder::new().build(&event_loop).unwrap();
let window_id = window.id();
let weview = WebViewBuilder::new(window)
.unwrap()
.with_url("https://tauri.studio")
.unwrap()
.build()
.unwrap();
weviews.insert(window_id, weview);
}
Event::Resumed => {
println!("applicationDidBecomeActive");
}
Event::Suspended => {
println!("applicationWillResignActive");
}
Event::LoopDestroyed => {
println!("applicationWillTerminate");
}
Event::WindowEvent {
window_id,
event: WindowEvent::Touch(touch),
..
} => {
println!("touch on {:?} {:?}", window_id, touch);
}
_ => {}
}
});
patch_machine.reset();
let root_node = body_element.first_child().unwrap();
patch_machine.stack.push(root_node.clone());
// log::debug!("patch stack size {:?}", patch_machine.stack);
// Event loop waits for the receiver to finish up
// TODO! Connect the sender to the virtual dom's suspense system
// Suspense is basically an external event that can force renders to specific nodes
while let Ok(event) = receiver.recv().await {
log::debug!("Stack before entrance {:#?}", patch_machine.stack.top());
// log::debug!("patch stack size before {:#?}", patch_machine.stack);
// patch_machine.reset();
// patch_machine.stack.push(root_node.clone());
let edits = self.internal_dom.progress_with_event(event)?;
log::debug!("Received edits: {:#?}", edits);
for edit in &edits {
log::debug!("edit stream {:?}", edit);
// log::debug!("Stream stack {:#?}", patch_machine.stack.top());
patch_machine.handle_edit(edit);
}
// log::debug!("patch stack size after {:#?}", patch_machine.stack);
patch_machine.reset();
// our root node reference gets invalidated
// not sure why
// for now, just select the first child again.
// eventually, we'll just make our own root element instead of using body
// or just use body directly IDEK
let root_node = body_element.first_child().unwrap();
patch_machine.stack.push(root_node.clone());
}
Ok(()) // should actually never return from this, should be an error, rustc just cant see it
}
}

View file

@ -0,0 +1,70 @@
use dioxus::virtual_dom::VirtualDom;
pub use dioxus_core as dioxus;
use dioxus_core::{events::EventTrigger, prelude::FC};
pub struct IosRenderer {
internal_dom: VirtualDom,
}
impl IosRenderer {
pub async fn run(&mut self) -> dioxus_core::error::Result<()> {
let (sender, mut receiver) = async_channel::unbounded::<EventTrigger>();
// let body_element = prepare_websys_dom();
let mut patch_machine = interpreter::PatchMachine::new(body_element.clone(), move |ev| {
log::debug!("Event trigger! {:#?}", ev);
let mut c = sender.clone();
// wasm_bindgen_futures::spawn_local(async move {
// c.send(ev).await.unwrap();
// });
});
let root_node = body_element.first_child().unwrap();
patch_machine.stack.push(root_node.clone());
// todo: initialize the event registry properly on the root
let edits = self.internal_dom.rebuild()?;
log::debug!("Received edits: {:#?}", edits);
edits.iter().for_each(|edit| {
log::debug!("patching with {:?}", edit);
patch_machine.handle_edit(edit);
});
patch_machine.reset();
let root_node = body_element.first_child().unwrap();
patch_machine.stack.push(root_node.clone());
// log::debug!("patch stack size {:?}", patch_machine.stack);
// Event loop waits for the receiver to finish up
// TODO! Connect the sender to the virtual dom's suspense system
// Suspense is basically an external event that can force renders to specific nodes
while let Ok(event) = receiver.recv().await {
log::debug!("Stack before entrance {:#?}", patch_machine.stack.top());
// log::debug!("patch stack size before {:#?}", patch_machine.stack);
// patch_machine.reset();
// patch_machine.stack.push(root_node.clone());
let edits = self.internal_dom.progress_with_event(event)?;
log::debug!("Received edits: {:#?}", edits);
for edit in &edits {
log::debug!("edit stream {:?}", edit);
// log::debug!("Stream stack {:#?}", patch_machine.stack.top());
patch_machine.handle_edit(edit);
}
// log::debug!("patch stack size after {:#?}", patch_machine.stack);
patch_machine.reset();
// our root node reference gets invalidated
// not sure why
// for now, just select the first child again.
// eventually, we'll just make our own root element instead of using body
// or just use body directly IDEK
let root_node = body_element.first_child().unwrap();
patch_machine.stack.push(root_node.clone());
}
Ok(()) // should actually never return from this, should be an error, rustc just cant see it
}
}

View file

@ -0,0 +1,142 @@
use dioxus::virtual_dom::VirtualDom;
use dioxus_core as dioxus;
use dioxus_core::prelude::*;
use dioxus_hooks::use_state;
use dioxus_html as dioxus_elements;
use dioxus_html::{GlobalAttributes, SvgAttributes};
use dioxus_ssr::TextRenderer;
fn main() {
use std::fs::File;
use std::io::Write;
let mut file = File::create("example.html").unwrap();
let mut dom = VirtualDom::new(App);
dom.rebuild_in_place().expect("failed to run virtualdom");
file.write_fmt(format_args!("{}", TextRenderer::new(&dom)))
.unwrap();
}
pub const App: FC<()> = |cx| {
cx.render(rsx!(
div { class: "overflow-hidden"
link { href:"https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel:"stylesheet" }
Header {}
// Entry {}
Hero {}
Hero {}
Hero {}
Hero {}
Hero {}
}
))
};
pub const Header: FC<()> = |cx| {
cx.render(rsx! {
div {
header { class: "text-gray-400 bg-gray-900 body-font"
div { class: "container mx-auto flex flex-wrap p-5 flex-col md:flex-row items-center"
a { class: "flex title-font font-medium items-center text-white mb-4 md:mb-0"
StacksIcon {}
span { class: "ml-3 text-xl" "Hello Dioxus!"}
}
nav { class: "md:ml-auto flex flex-wrap items-center text-base justify-center"
a { class: "mr-5 hover:text-white" "First Link"}
a { class: "mr-5 hover:text-white" "Second Link"}
a { class: "mr-5 hover:text-white" "Third Link"}
a { class: "mr-5 hover:text-white" "Fourth Link"}
}
button {
class: "inline-flex items-center bg-gray-800 border-0 py-1 px-3 focus:outline-none hover:bg-gray-700 rounded text-base mt-4 md:mt-0"
"Button"
RightArrowIcon {}
}
}
}
}
})
};
pub const Hero: FC<()> = |cx| {
//
cx.render(rsx! {
section{ class: "text-gray-400 bg-gray-900 body-font"
div { class: "container mx-auto flex px-5 py-24 md:flex-row flex-col items-center"
div { class: "lg:flex-grow md:w-1/2 lg:pr-24 md:pr-16 flex flex-col md:items-start md:text-left mb-16 md:mb-0 items-center text-center"
h1 { class: "title-font sm:text-4xl text-3xl mb-4 font-medium text-white"
br { class: "hidden lg:inline-block" }
"Dioxus Sneak Peek"
}
p {
class: "mb-8 leading-relaxed"
"Dioxus is a new UI framework that makes it easy and simple to write cross-platform apps using web
technologies! It is functional, fast, and portable. Dioxus can run on the web, on the desktop, and
on mobile and embedded platforms."
}
div { class: "flex justify-center"
button {
class: "inline-flex text-white bg-indigo-500 border-0 py-2 px-6 focus:outline-none hover:bg-indigo-600 rounded text-lg"
"Learn more"
}
button {
class: "ml-4 inline-flex text-gray-400 bg-gray-800 border-0 py-2 px-6 focus:outline-none hover:bg-gray-700 hover:text-white rounded text-lg"
"Build an app"
}
}
}
div { class: "lg:max-w-lg lg:w-full md:w-1/2 w-5/6"
img { class: "object-cover object-center rounded" alt: "hero" src: "https://i.imgur.com/oK6BLtw.png"
referrerpolicy:"no-referrer"
}
}
}
}
})
};
pub const Entry: FC<()> = |cx| {
//
cx.render(rsx! {
section{ class: "text-gray-400 bg-gray-900 body-font"
div { class: "container mx-auto flex px-5 py-24 md:flex-row flex-col items-center"
textarea {
}
}
}
})
};
pub const StacksIcon: FC<()> = |cx| {
cx.render(rsx!(
svg {
// xmlns: "http://www.w3.org/2000/svg"
fill: "none"
stroke: "currentColor"
stroke_linecap: "round"
stroke_linejoin: "round"
stroke_width: "2"
// class: "w-10 h-10 text-white p-2 bg-indigo-500 rounded-full"
viewBox: "0 0 24 24"
path { d: "M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"}
}
))
};
pub const RightArrowIcon: FC<()> = |cx| {
cx.render(rsx!(
svg {
fill: "none"
stroke: "currentColor"
stroke_linecap: "round"
stroke_linejoin: "round"
stroke_width: "2"
// class: "w-4 h-4 ml-1"
viewBox: "0 0 24 24"
path { d: "M5 12h14M12 5l7 7-7 7"}
}
))
};

View file

@ -61,7 +61,7 @@ pub struct TextRenderer<'a> {
}
impl<'a> TextRenderer<'a> {
fn new(vdom: &'a VirtualDom) -> Self {
pub fn new(vdom: &'a VirtualDom) -> Self {
Self {
vdom,
cfg: SsrConfig::default(),

View file

@ -22,7 +22,7 @@ console_error_panic_hook = "0.1.6"
generational-arena = "0.2.8"
wasm-bindgen-test = "0.3.21"
once_cell = "1.7.2"
atoms = { path = "../atoms" }
# atoms = { path = "../atoms" }
async-channel = "1.6.1"
anyhow = "1.0.41"
slotmap = "1.0.3"

View file

@ -0,0 +1,148 @@
//! Basic example that renders a simple VNode to the browser.
use dioxus::events::on::MouseEvent;
use dioxus_core as dioxus;
use dioxus_core::prelude::*;
use dioxus_hooks::*;
use dioxus_html as dioxus_elements;
// use wasm_timer;
use std::future::Future;
use std::{pin::Pin, time::Duration};
use dioxus::prelude::*;
use dioxus_html::SvgAttributes;
use dioxus_web::*;
fn main() {
// Setup logging
wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
console_error_panic_hook::set_once();
// Run the app
wasm_bindgen_futures::spawn_local(WebsysRenderer::start(App));
}
pub const App: FC<()> = |cx| {
cx.render(rsx!(
div { class: "overflow-hidden"
link { href:"https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel:"stylesheet" }
Header {}
// Entry {}
Hero {}
Hero {}
Hero {}
Hero {}
Hero {}
}
))
};
pub const Header: FC<()> = |cx| {
cx.render(rsx! {
div {
header { class: "text-gray-400 bg-gray-900 body-font"
div { class: "container mx-auto flex flex-wrap p-5 flex-col md:flex-row items-center"
a { class: "flex title-font font-medium items-center text-white mb-4 md:mb-0"
StacksIcon {}
span { class: "ml-3 text-xl" "Hello Dioxus!"}
}
nav { class: "md:ml-auto flex flex-wrap items-center text-base justify-center"
a { class: "mr-5 hover:text-white" "First Link"}
a { class: "mr-5 hover:text-white" "Second Link"}
a { class: "mr-5 hover:text-white" "Third Link"}
a { class: "mr-5 hover:text-white" "Fourth Link"}
}
button {
class: "inline-flex items-center bg-gray-800 border-0 py-1 px-3 focus:outline-none hover:bg-gray-700 rounded text-base mt-4 md:mt-0"
"Button"
RightArrowIcon {}
}
}
}
}
})
};
pub const Hero: FC<()> = |cx| {
//
cx.render(rsx! {
section{ class: "text-gray-400 bg-gray-900 body-font"
div { class: "container mx-auto flex px-5 py-24 md:flex-row flex-col items-center"
div { class: "lg:flex-grow md:w-1/2 lg:pr-24 md:pr-16 flex flex-col md:items-start md:text-left mb-16 md:mb-0 items-center text-center"
h1 { class: "title-font sm:text-4xl text-3xl mb-4 font-medium text-white"
br { class: "hidden lg:inline-block" }
"Dioxus Sneak Peek"
}
p {
class: "mb-8 leading-relaxed"
"Dioxus is a new UI framework that makes it easy and simple to write cross-platform apps using web
technologies! It is functional, fast, and portable. Dioxus can run on the web, on the desktop, and
on mobile and embedded platforms."
}
div { class: "flex justify-center"
button {
class: "inline-flex text-white bg-indigo-500 border-0 py-2 px-6 focus:outline-none hover:bg-indigo-600 rounded text-lg"
"Learn more"
}
button {
class: "ml-4 inline-flex text-gray-400 bg-gray-800 border-0 py-2 px-6 focus:outline-none hover:bg-gray-700 hover:text-white rounded text-lg"
"Build an app"
}
}
}
div { class: "lg:max-w-lg lg:w-full md:w-1/2 w-5/6"
img { class: "object-cover object-center rounded" alt: "hero" src: "https://i.imgur.com/oK6BLtw.png"
referrerpolicy:"no-referrer"
}
}
}
}
})
};
pub const Entry: FC<()> = |cx| {
//
cx.render(rsx! {
section{ class: "text-gray-400 bg-gray-900 body-font"
div { class: "container mx-auto flex px-5 py-24 md:flex-row flex-col items-center"
textarea {
}
}
}
})
};
pub const StacksIcon: FC<()> = |cx| {
cx.render(rsx!(
svg {
// xmlns: "http://www.w3.org/2000/svg"
fill: "none"
stroke: "currentColor"
stroke_linecap: "round"
stroke_linejoin: "round"
stroke_width: "2"
// class: "w-10 h-10 text-white p-2 bg-indigo-500 rounded-full"
viewBox: "0 0 24 24"
path { d: "M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"}
}
))
};
pub const RightArrowIcon: FC<()> = |cx| {
cx.render(rsx!(
svg {
fill: "none"
stroke: "currentColor"
stroke_linecap: "round"
stroke_linejoin: "round"
stroke_width: "2"
// class: "w-4 h-4 ml-1"
viewBox: "0 0 24 24"
path { d: "M5 12h14M12 5l7 7-7 7"}
}
))
};

View file

@ -1,3 +0,0 @@
{
"rust-analyzer.inlayHints.enable": false
}

View file

@ -176,6 +176,9 @@ pub use dioxus_core::events;
#[cfg(feature = "web")]
pub use dioxus_web as web;
#[cfg(feature = "mobile")]
pub use dioxus_mobile as mobile;
#[cfg(feature = "ssr")]
pub use dioxus_ssr as ssr;