mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-26 22:20:19 +00:00
Merge pull request #1941 from jereanon/fix/25-examples-repo
Address discrepancies between examples repo and examples
This commit is contained in:
commit
a454f2c7ac
4 changed files with 417 additions and 28 deletions
|
@ -172,3 +172,11 @@ required-features = ["http"]
|
|||
[[example]]
|
||||
name = "suspense"
|
||||
required-features = ["http"]
|
||||
|
||||
[[example]]
|
||||
name = "weather_app"
|
||||
required-features = ["http"]
|
||||
|
||||
[[example]]
|
||||
name = "image_generator_openai"
|
||||
required-features = ["http"]
|
|
@ -30,16 +30,10 @@ cargo run --example hello_world
|
|||
|
||||
### Props
|
||||
|
||||
[borrowed](./borrowed.rs) - Borrowed props
|
||||
|
||||
[inlineprops](./inlineprops.rs) - Demo of `inline_props` macro
|
||||
|
||||
[optional_props](./optional_props.rs) - Optional props
|
||||
|
||||
### CSS
|
||||
|
||||
[all_css](./all_css.rs) - You can specify any CSS attribute
|
||||
|
||||
[tailwind](./tailwind/) - You can use a library for styling
|
||||
|
||||
## Input Handling
|
||||
|
@ -58,12 +52,6 @@ cargo run --example hello_world
|
|||
|
||||
### State Management
|
||||
|
||||
[fermi](./fermi.rs) - Fermi library for state management
|
||||
|
||||
[pattern_reducer](./pattern_reducer.rs) - The reducer pattern with `use_state`
|
||||
|
||||
[rsx_compile_fail](./rsx_compile_fail.rs)
|
||||
|
||||
### Async
|
||||
|
||||
[login_form](./login_form.rs) - Login endpoint example
|
||||
|
@ -74,22 +62,8 @@ cargo run --example hello_world
|
|||
|
||||
### SVG
|
||||
|
||||
[svg_basic](./svg_basic.rs)
|
||||
|
||||
[svg](./svg.rs)
|
||||
|
||||
### Performance
|
||||
|
||||
[framework_benchmark](./framework_benchmark.rs) - Renders a huge list
|
||||
|
||||
> Note: The benchmark should be run in release mode:
|
||||
>
|
||||
>```shell
|
||||
> cargo run --example framework_benchmark --release
|
||||
>```
|
||||
|
||||
[heavy_compute](./heavy_compute.rs) - How to deal with expensive operations
|
||||
|
||||
## Server-side rendering
|
||||
|
||||
[ssr](./ssr.rs) - Rendering RSX server-side
|
||||
|
@ -120,8 +94,6 @@ cargo run --example hello_world
|
|||
|
||||
[calculator](./calculator.rs) - Simple calculator
|
||||
|
||||
[pattern_model](./pattern_model.rs) - Simple calculator, but using a custom struct as the model
|
||||
|
||||
[crm](./crm.rs) - Toy multi-page customer management app
|
||||
|
||||
[dog_app](./dog_app.rs) - Accesses dog API
|
||||
|
|
150
examples/image_generator_openai.rs
Normal file
150
examples/image_generator_openai.rs
Normal file
|
@ -0,0 +1,150 @@
|
|||
use dioxus::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{json, Error};
|
||||
|
||||
fn main() {
|
||||
launch(app)
|
||||
}
|
||||
|
||||
fn app() -> Element {
|
||||
let mut api = use_signal(|| "".to_string());
|
||||
let mut prompt = use_signal(|| "".to_string());
|
||||
let mut n_image = use_signal(|| 1.to_string());
|
||||
let mut image = use_signal(|| ImageResponse {
|
||||
created: 0,
|
||||
data: Vec::new(),
|
||||
});
|
||||
let mut loading = use_signal(|| "".to_string());
|
||||
|
||||
let mut generate_images = use_resource(move || async move {
|
||||
let api_key = api.peek().clone();
|
||||
let prompt = prompt.peek().clone();
|
||||
let number_of_images = n_image.peek().clone();
|
||||
|
||||
if (api_key.is_empty() || prompt.is_empty() || number_of_images.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
loading.set("is-loading".to_string());
|
||||
let images = request(api_key, prompt, number_of_images).await;
|
||||
match images {
|
||||
Ok(imgz) => {
|
||||
image.set(imgz);
|
||||
}
|
||||
Err(e) => {
|
||||
println!("Error: {:?}", e);
|
||||
}
|
||||
}
|
||||
loading.set("".to_string());
|
||||
});
|
||||
|
||||
rsx! {
|
||||
head {
|
||||
link {
|
||||
rel: "stylesheet",
|
||||
href: "https://unpkg.com/bulma@0.9.0/css/bulma.min.css",
|
||||
}
|
||||
}
|
||||
div { class: "container",
|
||||
div { class: "columns",
|
||||
div { class: "column",
|
||||
input { class: "input is-primary mt-4",
|
||||
value:"{api}",
|
||||
r#type: "text",
|
||||
placeholder: "API",
|
||||
oninput: move |evt| {
|
||||
api.set(evt.value().clone());
|
||||
},
|
||||
}
|
||||
|
||||
input { class: "input is-primary mt-4",
|
||||
placeholder: "MAX 1000 Dgts",
|
||||
r#type: "text",
|
||||
value:"{prompt}",
|
||||
oninput: move |evt| {
|
||||
prompt.set(evt.value().clone());
|
||||
},
|
||||
}
|
||||
|
||||
input { class: "input is-primary mt-4",
|
||||
r#type: "number",
|
||||
min:"1",
|
||||
max:"10",
|
||||
value:"{n_image}",
|
||||
oninput: move |evt| {
|
||||
n_image.set(evt.value().clone());
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button { class: "button is-primary {loading}",
|
||||
onclick: move |_| {
|
||||
generate_images.restart();
|
||||
},
|
||||
"Generate image"
|
||||
}
|
||||
br {
|
||||
}
|
||||
}
|
||||
{image.read().data.iter().map(|image| {
|
||||
rsx!(
|
||||
section { class: "is-flex",
|
||||
div { class: "container is-fluid",
|
||||
div { class: "container has-text-centered",
|
||||
div { class: "is-justify-content-center",
|
||||
div { class: "level",
|
||||
div { class: "level-item",
|
||||
figure { class: "image",
|
||||
img {
|
||||
alt: "",
|
||||
src: "{image.url}",
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
} }
|
||||
}
|
||||
async fn request(api: String, prompt: String, n_image: String) -> Result<ImageResponse, Error> {
|
||||
let client = reqwest::Client::new();
|
||||
let body = json!({
|
||||
"prompt": prompt,
|
||||
"n":n_image.parse::<i32>().unwrap_or(1),
|
||||
"size":"1024x1024",
|
||||
});
|
||||
|
||||
let mut authorization = "Bearer ".to_string();
|
||||
authorization.push_str(&api);
|
||||
|
||||
let res = client
|
||||
.post("https://api.openai.com/v1/images/generations")
|
||||
.body(body.to_string())
|
||||
.header("Content-Type", "application/json")
|
||||
.header("Authorization", authorization)
|
||||
.send()
|
||||
.await
|
||||
.unwrap()
|
||||
.text()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let deserialized: ImageResponse = serde_json::from_str(&res)?;
|
||||
Ok(deserialized)
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Props, Clone)]
|
||||
struct UrlImage {
|
||||
url: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Props, Clone)]
|
||||
struct ImageResponse {
|
||||
created: i32,
|
||||
data: Vec<UrlImage>,
|
||||
}
|
259
examples/weather_app.rs
Normal file
259
examples/weather_app.rs
Normal file
|
@ -0,0 +1,259 @@
|
|||
#![allow(non_snake_case)]
|
||||
|
||||
use dioxus::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
fn main() {
|
||||
launch(app);
|
||||
}
|
||||
|
||||
fn app() -> Element {
|
||||
let country = use_signal(|| WeatherLocation {
|
||||
name: "Berlin".to_string(),
|
||||
country: "Germany".to_string(),
|
||||
latitude: 52.5244,
|
||||
longitude: 13.4105,
|
||||
id: 2950159,
|
||||
});
|
||||
|
||||
let current_weather =
|
||||
use_resource(move || async move { get_weather(&country.read().clone()).await });
|
||||
|
||||
rsx! {
|
||||
link {
|
||||
rel: "stylesheet",
|
||||
href: "https://unpkg.com/tailwindcss@^2.0/dist/tailwind.min.css"
|
||||
}
|
||||
div { class: "mx-auto p-4 bg-gray-100 h-screen flex justify-center",
|
||||
div { class: "flex items-center justify-center flex-row",
|
||||
div { class: "flex items-start justify-center flex-row",
|
||||
SearchBox { country: country }
|
||||
div { class: "flex flex-wrap w-full px-2",
|
||||
div { class: "bg-gray-900 text-white relative min-w-0 break-words rounded-lg overflow-hidden shadow-sm mb-4 w-full bg-white dark:bg-gray-600",
|
||||
div { class: "px-6 py-6 relative",
|
||||
if let Some(Ok(weather)) = current_weather.read().as_ref() {
|
||||
CountryData {
|
||||
country: country.read().clone(),
|
||||
weather: weather.clone(),
|
||||
}
|
||||
Forecast {
|
||||
weather: weather.clone(),
|
||||
}
|
||||
|
||||
} else {
|
||||
p {
|
||||
"Loading.."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[component]
|
||||
fn CountryData(weather: WeatherResponse, country: WeatherLocation) -> Element {
|
||||
let today = "Today";
|
||||
let max_temp = weather.daily.temperature_2m_max.first().unwrap();
|
||||
let min_temp = weather.daily.temperature_2m_min.first().unwrap();
|
||||
|
||||
rsx! {
|
||||
div { class: "flex mb-4 justify-between items-center",
|
||||
div {
|
||||
h5 { class: "mb-0 font-medium text-xl", "{country.name} 🏞️" }
|
||||
h6 { class: "mb-0", "{today}" }
|
||||
}
|
||||
div {
|
||||
div { class: "flex items-center",
|
||||
span { "Temp min" }
|
||||
span { class: "px-2 inline-block", "👉 {min_temp}°" }
|
||||
}
|
||||
div { class: "flex items-center",
|
||||
span { "Temp max" }
|
||||
span { class: "px-2 inline-block ", "👉 {max_temp}º" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[component]
|
||||
fn Forecast(weather: WeatherResponse) -> Element {
|
||||
let today = (weather.daily.temperature_2m_max.first().unwrap()
|
||||
+ weather.daily.temperature_2m_max.first().unwrap())
|
||||
/ 2.0;
|
||||
let tomorrow = (weather.daily.temperature_2m_max.get(1).unwrap()
|
||||
+ weather.daily.temperature_2m_max.get(1).unwrap())
|
||||
/ 2.0;
|
||||
let past_tomorrow = (weather.daily.temperature_2m_max.get(2).unwrap()
|
||||
+ weather.daily.temperature_2m_max.get(2).unwrap())
|
||||
/ 2.0;
|
||||
rsx! {
|
||||
div { class: "px-6 pt-4 relative",
|
||||
div { class: "w-full h-px bg-gray-100 mb-4" }
|
||||
div { p { class: "text-center w-full mb-4", "👇 Forecast 📆" } }
|
||||
div { class: "text-center justify-between items-center flex",
|
||||
div { class: "text-center mb-0 flex items-center justify-center flex-col mx-4 w-16",
|
||||
span { class: "block my-1", "Today" }
|
||||
span { class: "block my-1", "{today}°" }
|
||||
}
|
||||
div { class: "text-center mb-0 flex items-center justify-center flex-col mx-8 w-16",
|
||||
span { class: "block my-1", "Tomorrow" }
|
||||
span { class: "block my-1", "{tomorrow}°" }
|
||||
}
|
||||
div { class: "text-center mb-0 flex items-center justify-center flex-col mx-2 w-30",
|
||||
span { class: "block my-1", "Past Tomorrow" }
|
||||
span { class: "block my-1", "{past_tomorrow}°" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn SearchBox(mut country: Signal<WeatherLocation>) -> Element {
|
||||
let mut input = use_signal(|| "".to_string());
|
||||
|
||||
let locations = use_resource(move || async move {
|
||||
let current_location = input.read().clone();
|
||||
get_locations(¤t_location).await
|
||||
});
|
||||
|
||||
rsx! {
|
||||
div {
|
||||
div { class: "inline-flex flex-col justify-center relative text-gray-500",
|
||||
div { class: "relative",
|
||||
input {
|
||||
class: "p-2 pl-8 rounded-lg border border-gray-200 bg-gray-200 focus:bg-white focus:outline-none focus:ring-2 focus:ring-yellow-600 focus:border-transparent",
|
||||
placeholder: "Country name",
|
||||
"type": "text",
|
||||
autofocus: true,
|
||||
oninput: move |e| input.set(e.value())
|
||||
}
|
||||
svg {
|
||||
class: "w-4 h-4 absolute left-2.5 top-3.5",
|
||||
"viewBox": "0 0 24 24",
|
||||
fill: "none",
|
||||
stroke: "currentColor",
|
||||
xmlns: "http://www.w3.org/2000/svg",
|
||||
path {
|
||||
d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z",
|
||||
"stroke-linejoin": "round",
|
||||
"stroke-linecap": "round",
|
||||
"stroke-width": "2"
|
||||
}
|
||||
}
|
||||
}
|
||||
ul { class: "bg-white border border-gray-100 w-full mt-2 max-h-72 overflow-auto",
|
||||
{
|
||||
if let Some(Ok(locs)) = locations.read().as_ref() {
|
||||
rsx! {
|
||||
{
|
||||
locs.iter().cloned().map(move |wl| {
|
||||
rsx! {
|
||||
li { class: "pl-8 pr-2 py-1 border-b-2 border-gray-100 relative cursor-pointer hover:bg-yellow-50 hover:text-gray-900",
|
||||
onclick: move |_| country.set(wl.clone()),
|
||||
MapIcon {}
|
||||
b {
|
||||
"{wl.name}"
|
||||
}
|
||||
" · {wl.country}"
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
rsx! { "loading locations..." }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn MapIcon() -> Element {
|
||||
rsx! {
|
||||
svg {
|
||||
class: "stroke-current absolute w-4 h-4 left-2 top-2",
|
||||
stroke: "currentColor",
|
||||
xmlns: "http://www.w3.org/2000/svg",
|
||||
"viewBox": "0 0 24 24",
|
||||
fill: "none",
|
||||
path {
|
||||
"stroke-linejoin": "round",
|
||||
"stroke-width": "2",
|
||||
"stroke-linecap": "round",
|
||||
d: "M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"
|
||||
}
|
||||
path {
|
||||
"stroke-linecap": "round",
|
||||
"stroke-linejoin": "round",
|
||||
d: "M15 11a3 3 0 11-6 0 3 3 0 016 0z",
|
||||
"stroke-width": "2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Clone)]
|
||||
struct WeatherLocation {
|
||||
id: usize,
|
||||
name: String,
|
||||
latitude: f32,
|
||||
longitude: f32,
|
||||
country: String,
|
||||
}
|
||||
|
||||
type WeatherLocations = Vec<WeatherLocation>;
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
struct SearchResponse {
|
||||
results: WeatherLocations,
|
||||
}
|
||||
|
||||
async fn get_locations(input: &str) -> reqwest::Result<WeatherLocations> {
|
||||
let res = reqwest::get(&format!(
|
||||
"https://geocoding-api.open-meteo.com/v1/search?name={input}"
|
||||
))
|
||||
.await?
|
||||
.json::<SearchResponse>()
|
||||
.await?;
|
||||
|
||||
Ok(res.results)
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Clone)]
|
||||
struct WeatherResponse {
|
||||
daily: DailyWeather,
|
||||
hourly: HourlyWeather,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Clone)]
|
||||
struct HourlyWeather {
|
||||
time: Vec<String>,
|
||||
temperature_2m: Vec<f32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Clone)]
|
||||
struct DailyWeather {
|
||||
temperature_2m_min: Vec<f32>,
|
||||
temperature_2m_max: Vec<f32>,
|
||||
}
|
||||
|
||||
async fn get_weather(location: &WeatherLocation) -> reqwest::Result<WeatherResponse> {
|
||||
let res = reqwest::get(&format!("https://api.open-meteo.com/v1/forecast?latitude={}&longitude={}&hourly=temperature_2m&daily=temperature_2m_max,temperature_2m_min,apparent_temperature_max,apparent_temperature_min&timezone=GMT", location.latitude, location.longitude))
|
||||
.await
|
||||
?
|
||||
.json::<WeatherResponse>()
|
||||
.await
|
||||
?;
|
||||
|
||||
Ok(res)
|
||||
}
|
Loading…
Reference in a new issue