mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-21 19:53:04 +00:00
Update more examples, add css for more examples
This commit is contained in:
parent
60b78668ac
commit
cfc119cce2
19 changed files with 228 additions and 101 deletions
43
examples/assets/radio.css
Normal file
43
examples/assets/radio.css
Normal file
|
@ -0,0 +1,43 @@
|
|||
body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
button {
|
||||
margin: 10px;
|
||||
padding: 10px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
background-color: #f0f0f0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#pause {
|
||||
background-color: #ff0000;
|
||||
}
|
||||
|
||||
#play {
|
||||
background-color: #00ff00;
|
||||
}
|
||||
|
||||
|
||||
.bounce {
|
||||
animation: boomBox 0.5s infinite;
|
||||
}
|
||||
|
||||
@keyframes boomBox {
|
||||
0% {
|
||||
transform: scale(1.0);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: scale(2);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(1.0);
|
||||
}
|
||||
}
|
13
examples/assets/router.css
Normal file
13
examples/assets/router.css
Normal file
|
@ -0,0 +1,13 @@
|
|||
#navbar {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#blog-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: start;
|
||||
gap: 1rem;
|
||||
}
|
|
@ -1,4 +1,9 @@
|
|||
#![allow(clippy::await_holding_refcell_ref)]
|
||||
//! Read the size of elements using the MountedData struct.
|
||||
//!
|
||||
//! Whenever an Element is finally mounted to the Dom, its data is avaiable to be read.
|
||||
//! These fields can typically only be read asynchronously, since various renderers need to release the main thread to
|
||||
//! perform layout and painting.
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
use dioxus::{html::geometry::euclid::Rect, prelude::*};
|
||||
|
@ -33,6 +38,7 @@ fn app() -> Element {
|
|||
let read_dims = move |_| async move {
|
||||
let read = div_element.read();
|
||||
let client_rect = read.as_ref().map(|el| el.get_client_rect());
|
||||
|
||||
if let Some(client_rect) = client_rect {
|
||||
if let Ok(rect) = client_rect.await {
|
||||
dimensions.set(rect);
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
//! The example from the readme!
|
||||
//!
|
||||
//! This example demonstrates how to create a simple counter app with dioxus. The `Signal` type wraps inner values,
|
||||
//! making them `Copy`, allowing them to be freely used in closures and and async functions. `Signal` also provides
|
||||
//! helper methods like AddAssign, SubAssign, toggle, etc, to make it easy to update the value without running
|
||||
//! into lock issues.
|
||||
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
|
|
|
@ -15,12 +15,16 @@ fn app() -> Element {
|
|||
let mut state = use_signal(|| PlayerState { is_playing: false });
|
||||
|
||||
rsx!(
|
||||
div {
|
||||
h1 {"Select an option"}
|
||||
h3 { "The radio is... ", {state.read().is_playing()}, "!" }
|
||||
button { onclick: move |_| state.write().reduce(PlayerAction::Pause), "Pause" }
|
||||
button { onclick: move |_| state.write().reduce(PlayerAction::Play), "Play" }
|
||||
style { {include_str!("./assets/radio.css")} }
|
||||
h1 {"Select an option"}
|
||||
|
||||
// Add some cute animations if the radio is playing!
|
||||
div { class: if state.read().is_playing { "bounce" },
|
||||
"The radio is... ", {state.read().is_playing()}, "!"
|
||||
}
|
||||
|
||||
button { id: "play", onclick: move |_| state.write().reduce(PlayerAction::Pause), "Pause" }
|
||||
button { id: "pause", onclick: move |_| state.write().reduce(PlayerAction::Play), "Play" }
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,32 +1,59 @@
|
|||
//! An advanced usage of the router with nested routes and redirects.
|
||||
//!
|
||||
//! Dioxus implements an enum-based router, which allows you to define your routes in a type-safe way.
|
||||
//! However, since we need to bake quite a bit of logic into the enum, we have to add some extra syntax.
|
||||
//!
|
||||
//! Note that you don't need to use advanced features like nest, redirect, etc, since these can all be implemented
|
||||
//! manually, but they are provided as a convenience.
|
||||
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
launch_desktop(|| {
|
||||
rsx! {
|
||||
style { {include_str!("./assets/router.css")} }
|
||||
Router::<Route> {}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Turn off rustfmt since we're doing layouts and routes in the same enum
|
||||
#[derive(Routable, Clone, Debug, PartialEq)]
|
||||
#[rustfmt::skip]
|
||||
enum Route {
|
||||
// Wrap Home in a Navbar Layout
|
||||
#[layout(NavBar)]
|
||||
// The default route is always "/" unless otherwise specified
|
||||
#[route("/")]
|
||||
Home {},
|
||||
|
||||
// Wrap the next routes in a layout and a nest
|
||||
#[nest("/blog")]
|
||||
#[layout(Blog)]
|
||||
#[route("/")]
|
||||
BlogList {},
|
||||
#[route("/:name")]
|
||||
BlogPost { name: String },
|
||||
#[end_layout]
|
||||
#[layout(Blog)]
|
||||
// At "/blog", we want to show a list of blog posts
|
||||
#[route("/")]
|
||||
BlogList {},
|
||||
|
||||
// At "/blog/:name", we want to show a specific blog post, using the name slug
|
||||
#[route("/:name")]
|
||||
BlogPost { name: String },
|
||||
|
||||
// We need to end the blog layout and nest
|
||||
// Note we don't need either - we could've just done `/blog/` and `/blog/:name` without nesting,
|
||||
// but it's a bit cleaner this way
|
||||
#[end_layout]
|
||||
#[end_nest]
|
||||
|
||||
// And the regular page layout
|
||||
#[end_layout]
|
||||
|
||||
// Add some redirects for the `/myblog` route
|
||||
#[nest("/myblog")]
|
||||
#[redirect("/", || Route::BlogList {})]
|
||||
#[redirect("/:name", |name: String| Route::BlogPost { name })]
|
||||
#[end_nest]
|
||||
|
||||
// Finally, we need to handle the 404 page
|
||||
#[route("/:..route")]
|
||||
PageNotFound {
|
||||
route: Vec<String>,
|
||||
|
@ -36,15 +63,9 @@ enum Route {
|
|||
#[component]
|
||||
fn NavBar() -> Element {
|
||||
rsx! {
|
||||
nav {
|
||||
ul {
|
||||
li {
|
||||
Link { to: Route::Home {}, "Home" }
|
||||
}
|
||||
li {
|
||||
Link { to: Route::BlogList {}, "Blog" }
|
||||
}
|
||||
}
|
||||
nav { id: "navbar",
|
||||
Link { to: Route::Home {}, "Home" }
|
||||
Link { to: Route::BlogList {}, "Blog" }
|
||||
}
|
||||
Outlet::<Route> {}
|
||||
}
|
||||
|
@ -67,30 +88,31 @@ fn Blog() -> Element {
|
|||
fn BlogList() -> Element {
|
||||
rsx! {
|
||||
h2 { "Choose a post" }
|
||||
ul {
|
||||
li {
|
||||
Link {
|
||||
to: Route::BlogPost {
|
||||
name: "Blog post 1".into(),
|
||||
},
|
||||
"Read the first blog post"
|
||||
}
|
||||
div { id: "blog-list",
|
||||
Link { to: Route::BlogPost { name: "Blog post 1".into() },
|
||||
"Read the first blog post"
|
||||
}
|
||||
li {
|
||||
Link {
|
||||
to: Route::BlogPost {
|
||||
name: "Blog post 2".into(),
|
||||
},
|
||||
"Read the second blog post"
|
||||
}
|
||||
Link { to: Route::BlogPost { name: "Blog post 2".into() },
|
||||
"Read the second blog post"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We can use the `name` slug to show a specific blog post
|
||||
// In theory we could read from the filesystem or a database here
|
||||
#[component]
|
||||
fn BlogPost(name: String) -> Element {
|
||||
rsx! { h2 { "Blog Post: {name}" } }
|
||||
let contents = match name.as_str() {
|
||||
"Blog post 1" => "This is the first blog post. It's not very interesting.",
|
||||
"Blog post 2" => "This is the second blog post. It's not very interesting either.",
|
||||
_ => "This blog post doesn't exist.",
|
||||
};
|
||||
|
||||
rsx! {
|
||||
h2 { "{name}" }
|
||||
p { "{contents}" }
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
//! Scroll elements using their MountedData
|
||||
//!
|
||||
//! Dioxus exposes a few helpful APIs around elements (mimicking the DOM APIs) to allow you to interact with elements
|
||||
//! across the renderers. This includes scrolling, reading dimensions, and more.
|
||||
//!
|
||||
//! In this example we demonstrate how to scroll to the top of the page using the `scroll_to` method on the `MountedData`
|
||||
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
//! Add global shortcuts to your app while a component is active
|
||||
//!
|
||||
//! This demo shows how to add a global shortcut to your app that toggles a signal. You could use this to implement
|
||||
//! a raycast-type app, or to add a global shortcut to your app that toggles a component on and off.
|
||||
//!
|
||||
//! These are *global* shortcuts, so they will work even if your app is not in focus.
|
||||
|
||||
use dioxus::desktop::use_global_shortcut;
|
||||
use dioxus::prelude::*;
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//! Dioxus supports shorthand syntax for creating elements and components.
|
||||
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
//! A simple example demonstrating how to use signals to modify state from several different places.
|
||||
//!
|
||||
//! This simlpe example implements a counter that can be incremented, decremented, and paused. It also demonstrates
|
||||
//! that background tasks in use_futures can modify the value as well.
|
||||
//!
|
||||
//! Most signals implement Into<ReadOnlySignal<T>>, making ReadOnlySignal a good default type when building new
|
||||
//! library components that don't need to modify their values.
|
||||
|
||||
use dioxus::prelude::*;
|
||||
use std::time::Duration;
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
//! A few ways of mapping elements into rsx! syntax
|
||||
//!
|
||||
//! Rsx allows anything that's an iterator where the output type implements Into<Element>, so you can use any of the following:
|
||||
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
|
|
|
@ -1,20 +1,32 @@
|
|||
#![allow(non_snake_case)]
|
||||
//! A simple example of a router with a few routes and a nav bar.
|
||||
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
// Launch the router, using our `Route` component as the generic type
|
||||
// This will automatically boot the app to "/" unless otherwise specified
|
||||
launch(|| rsx! { Router::<Route> {} });
|
||||
}
|
||||
|
||||
/// By default, the Routable derive will use the name of the variant as the route
|
||||
/// You can also specify a specific component by adding the Component name to the `#[route]` attribute
|
||||
#[rustfmt::skip]
|
||||
#[derive(Routable, Clone, PartialEq)]
|
||||
enum Route {
|
||||
// Wrap the app in a Nav layout
|
||||
#[layout(Nav)]
|
||||
#[route("/")]
|
||||
Homepage {},
|
||||
#[route("/")]
|
||||
Homepage {},
|
||||
|
||||
#[route("/blog/:id")]
|
||||
Blog { id: String },
|
||||
#[route("/blog/:id")]
|
||||
Blog { id: String },
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn Homepage() -> Element {
|
||||
rsx! { h1 { "Welcome home" } }
|
||||
rsx! {
|
||||
h1 { "Welcome home" }
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
|
@ -25,6 +37,9 @@ fn Blog(id: String) -> Element {
|
|||
}
|
||||
}
|
||||
|
||||
/// A simple nav bar that links to the homepage and blog pages
|
||||
///
|
||||
/// The `Route` enum gives up typesafe routes, allowing us to rename routes and serialize them automatically
|
||||
#[component]
|
||||
fn Nav() -> Element {
|
||||
rsx! {
|
||||
|
@ -52,7 +67,3 @@ fn Nav() -> Element {
|
|||
div { Outlet::<Route> {} }
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
launch_desktop(|| rsx! { Router::<Route> {} });
|
||||
}
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
//! This example demonstrates how to use the spread operator to pass attributes to child components.
|
||||
//!
|
||||
//! This lets components like the `Link` allow the user to extend the attributes of the underlying `a` tag.
|
||||
//! These attributes are bundled into a `Vec<Attribute>` which can be spread into the child component using the `..` operator.
|
||||
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
//! Example: SSR
|
||||
//!
|
||||
//! This example shows how we can render the Dioxus Virtualdom using SSR.
|
||||
//! Dioxus' SSR is quite comprehensive and can generate a number of utility markers for things like hydration.
|
||||
//!
|
||||
//! You can also render without any markers to get a clean HTML output.
|
||||
|
||||
use dioxus::prelude::*;
|
||||
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
launch_desktop(app);
|
||||
}
|
||||
|
||||
fn app() -> Element {
|
||||
let mut state = use_signal(|| 0);
|
||||
let mut depth = use_signal(|| 1_usize);
|
||||
|
||||
if depth() == 5 {
|
||||
return rsx! {
|
||||
div { "Max depth reached" }
|
||||
button { onclick: move |_| depth -= 1, "Remove depth" }
|
||||
};
|
||||
}
|
||||
|
||||
let items = use_memo(move || (0..depth()).map(|f| f as _).collect::<Vec<isize>>());
|
||||
|
||||
rsx! {
|
||||
button { onclick: move |_| state += 1, "Increment" }
|
||||
button { onclick: move |_| depth += 1, "Add depth" }
|
||||
button {
|
||||
onclick: move |_| async move {
|
||||
depth += 1;
|
||||
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
|
||||
dbg!(items.read());
|
||||
// if depth() is 5, this will be the old since the memo hasn't been re-computed
|
||||
// use_memos are only re-computed when the signals they capture change
|
||||
// *and* they are used in the current render
|
||||
// If the use_memo isn't used, it can't be re-computed!
|
||||
},
|
||||
"Add depth with sleep"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
//! Handle async streams using use_future and awaiting the next value.
|
||||
|
||||
use dioxus::prelude::*;
|
||||
use futures_util::{future, stream, Stream, StreamExt};
|
||||
use std::time::Duration;
|
||||
|
@ -10,8 +12,11 @@ fn app() -> Element {
|
|||
let mut count = use_signal(|| 10);
|
||||
|
||||
use_future(move || async move {
|
||||
// Create the stream.
|
||||
// This could be a network request, a file read, or any other async operation.
|
||||
let mut stream = some_stream();
|
||||
|
||||
// Await the next value from the stream.
|
||||
while let Some(second) = stream.next().await {
|
||||
count.set(second);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#![allow(non_snake_case)]
|
||||
|
||||
//! Suspense in Dioxus
|
||||
//!
|
||||
//! Currently, `rsx!` does not accept futures as values. To achieve the functionality
|
||||
|
@ -49,6 +47,7 @@ fn app() -> Element {
|
|||
/// This component will re-render when the future has finished
|
||||
/// Suspense is achieved my moving the future into only the component that
|
||||
/// actually renders the data.
|
||||
#[component]
|
||||
fn Doggo() -> Element {
|
||||
let mut fut = use_resource(move || async move {
|
||||
#[derive(serde::Deserialize)]
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
// Thanks to @japsu and their project https://github.com/japsu/jatsi for the example!
|
||||
//! Thanks to @japsu and their project https://github.com/japsu/jatsi for the example!
|
||||
//!
|
||||
//! This example shows how to create a simple dice rolling app using SVG and Dioxus.
|
||||
//! The `svg` element and its children have a custom namespace, and are attached using different methods than regular
|
||||
//! HTML elements. Any element can specify a custom namespace by using the `namespace` meta attribute.
|
||||
//!
|
||||
//! If you `go-to-definition` on the `svg` element, you'll see its custom namespace.
|
||||
|
||||
use dioxus::prelude::*;
|
||||
use rand::{thread_rng, Rng};
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
//! Using `wry`'s http module, we can stream a video file from the local file system.
|
||||
//!
|
||||
//! You could load in any file type, but this example uses a video file.
|
||||
|
||||
use dioxus::desktop::wry::http;
|
||||
use dioxus::desktop::wry::http::Response;
|
||||
use dioxus::desktop::{use_asset_handler, AssetRequest};
|
||||
|
@ -9,25 +13,14 @@ use tokio::io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt};
|
|||
const VIDEO_PATH: &str = "./examples/assets/test_video.mp4";
|
||||
|
||||
fn main() {
|
||||
let video_file = PathBuf::from(VIDEO_PATH);
|
||||
if !video_file.exists() {
|
||||
tokio::runtime::Runtime::new()
|
||||
.unwrap()
|
||||
.block_on(async move {
|
||||
println!("Downloading video file...");
|
||||
let video_url =
|
||||
"http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4";
|
||||
let mut response = reqwest::get(video_url).await.unwrap();
|
||||
let mut file = tokio::fs::File::create(&video_file).await.unwrap();
|
||||
while let Some(chunk) = response.chunk().await.unwrap() {
|
||||
file.write_all(&chunk).await.unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
// For the sake of this example, we will download the video file if it doesn't exist
|
||||
ensure_video_is_loaded();
|
||||
|
||||
launch_desktop(app);
|
||||
}
|
||||
|
||||
fn app() -> Element {
|
||||
// Any request to /videos will be handled by this handler
|
||||
use_asset_handler("videos", move |request, responder| {
|
||||
// Using dioxus::spawn works, but is slower than a dedicated thread
|
||||
tokio::task::spawn(async move {
|
||||
|
@ -184,3 +177,21 @@ async fn get_stream_response(
|
|||
|
||||
http_response.map_err(Into::into)
|
||||
}
|
||||
|
||||
fn ensure_video_is_loaded() {
|
||||
let video_file = PathBuf::from(VIDEO_PATH);
|
||||
if !video_file.exists() {
|
||||
tokio::runtime::Runtime::new()
|
||||
.unwrap()
|
||||
.block_on(async move {
|
||||
println!("Downloading video file...");
|
||||
let video_url =
|
||||
"http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4";
|
||||
let mut response = reqwest::get(video_url).await.unwrap();
|
||||
let mut file = tokio::fs::File::create(&video_file).await.unwrap();
|
||||
while let Some(chunk) = response.chunk().await.unwrap() {
|
||||
file.write_all(&chunk).await.unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue