Update more examples, add css for more examples

This commit is contained in:
Jonathan Kelley 2024-02-14 13:48:58 -08:00
parent 60b78668ac
commit cfc119cce2
No known key found for this signature in database
GPG key ID: 1FBB50F7EB0A08BE
19 changed files with 228 additions and 101 deletions

43
examples/assets/radio.css Normal file
View 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);
}
}

View 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;
}

View file

@ -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);

View file

@ -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() {

View file

@ -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" }
)
}

View file

@ -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]

View file

@ -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() {

View file

@ -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::*;

View file

@ -1,3 +1,5 @@
//! Dioxus supports shorthand syntax for creating elements and components.
use dioxus::prelude::*;
fn main() {

View file

@ -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;

View file

@ -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() {

View file

@ -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> {} });
}

View file

@ -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() {

View file

@ -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::*;

View file

@ -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"
}
}
}

View file

@ -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);
}

View file

@ -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)]

View file

@ -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};

View file

@ -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();
}
});
}
}