Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ members = [
"examples/dyn_create_destroy_apps",
"examples/file_upload",
"examples/function_memory_game",
"examples/function_router",
"examples/function_todomvc",
"examples/futures",
"examples/game_of_life",
Expand Down
23 changes: 23 additions & 0 deletions examples/function_router/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[package]
name = "function_router"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"

[dependencies]
lipsum = "0.8"
log = "0.4"
rand = { version = "0.8", features = ["small_rng"] }
yew = { path = "../../packages/yew" }
yew-router = { path = "../../packages/yew-router" }
serde = { version = "1.0", features = ["derive"] }
lazy_static = "1.4.0"
gloo-timers = "0.2"

[target.'cfg(target_arch = "wasm32")'.dependencies]
getrandom = { version = "0.2", features = ["js"] }
instant = { version = "0.1", features = ["wasm-bindgen"] }
wasm-logger = "0.2"

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
instant = { version = "0.1" }
49 changes: 49 additions & 0 deletions examples/function_router/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Function Router Example

This is identical to the router example, but written in function
components.

[![Demo](https://img.shields.io/website?label=demo&url=https%3A%2F%2Fexamples.yew.rs%2Ffunction_router)](https://examples.yew.rs/function_router)

A blog all about yew.
The best way to figure out what this example is about is to just open it up.
It's mobile friendly too!

## Running

While not strictly necessary, this example should be built in release mode:

```bash
trunk serve --release
```

Content generation can take up quite a bit of time in debug builds.

## Concepts

This example involves many different parts, here are just the Yew specific things:

- Uses [`yew-router`] to render and switch between multiple pages.

The example automatically adapts to the `--public-url` value passed to Trunk.
This allows it to be hosted on any path, not just at the root.
For example, our demo is hosted at [/router](https://examples.yew.rs/router).

This is achieved by adding `<base data-trunk-public-url />` to the [index.html](index.html) file.
Trunk rewrites this tag to contain the value passed to `--public-url` which can then be retrieved at runtime.
Take a look at [`Route`](src/main.rs) for the implementation.

## Improvements

- Use a special image component which shows a progress bar until the image is loaded.
- Scroll back to the top after switching route
- Run content generation in a dedicated web worker
- Use longer Markov chains to achieve more coherent results
- Make images deterministic (the same seed should produce the same images)
- Show posts by the author on their page
(this is currently impossible because we need to find post seeds which in turn generate the author's seed)
- Show other posts at the end of a post ("continue reading")
- Home (`/`) should include links to the post list and the author introduction
- Detect sub-path from `--public-url` value passed to Trunk. See: thedodd/trunk#51

[`yew-router`]: https://docs.rs/yew-router/latest/yew_router/
34 changes: 34 additions & 0 deletions examples/function_router/data/keywords.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
allergenics
archaeology
austria
berries
birds
color
conservation
cosmology
culture
europe
evergreens
fleshy
france
guides
horticulture
ireland
landscaping
medicine
music
poison
religion
rome
rust
scotland
seeds
spain
taxonomy
toxics
tradition
trees
wasm
wood
woodworking
yew
20 changes: 20 additions & 0 deletions examples/function_router/data/syllables.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
ald
ber
fe
ger
jo
jus
kas
lix
lu
mon
mour
nas
ridge
ry
si
star
tey
tim
tin
yew
317 changes: 317 additions & 0 deletions examples/function_router/data/yew.txt

Large diffs are not rendered by default.

17 changes: 17 additions & 0 deletions examples/function_router/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />

<title>Yew • Function Router</title>
<base data-trunk-public-url />
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/[email protected]/css/bulma.min.css"
/>
<link data-trunk rel="sass" href="index.scss" />
</head>

<body></body>
</html>
27 changes: 27 additions & 0 deletions examples/function_router/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
.hero {
&.has-background {
position: relative;
overflow: hidden;
}

&-background {
position: absolute;
object-fit: cover;
object-position: bottom;
width: 100%;
height: 100%;

&.is-transparent {
opacity: 0.3;
}
}
}

.burger {
background-color: transparent;
border: none;
}

.navbar-brand {
align-items: center;
}
118 changes: 118 additions & 0 deletions examples/function_router/src/app.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
use yew::prelude::*;
use yew_router::prelude::*;

use crate::components::nav::Nav;
use crate::pages::{
author::Author, author_list::AuthorList, home::Home, page_not_found::PageNotFound, post::Post,
post_list::PostList,
};

#[derive(Routable, PartialEq, Clone, Debug)]
pub enum Route {
#[at("/posts/:id")]
Post { id: u32 },
#[at("/posts")]
Posts,
#[at("/authors/:id")]
Author { id: u32 },
#[at("/authors")]
Authors,
#[at("/")]
Home,
#[not_found]
#[at("/404")]
NotFound,
}

#[function_component]
pub fn App() -> Html {
html! {
<BrowserRouter>
<Nav />

<main>
<Switch<Route> render={Switch::render(switch)} />
</main>
<footer class="footer">
<div class="content has-text-centered">
{ "Powered by " }
<a href="https://yew.rs">{ "Yew" }</a>
{ " using " }
<a href="https://bulma.io">{ "Bulma" }</a>
{ " and images from " }
<a href="https://unsplash.com">{ "Unsplash" }</a>
</div>
</footer>
</BrowserRouter>
}
}

#[cfg(not(target_arch = "wasm32"))]
mod arch_native {
use super::*;

use yew::virtual_dom::AttrValue;
use yew_router::history::{AnyHistory, History, MemoryHistory};

use std::collections::HashMap;

#[derive(Properties, PartialEq, Debug)]
pub struct ServerAppProps {
pub url: AttrValue,
pub queries: HashMap<String, String>,
}

#[function_component]
pub fn ServerApp(props: &ServerAppProps) -> Html {
let history = AnyHistory::from(MemoryHistory::new());
history
.push_with_query(&*props.url, &props.queries)
.unwrap();

html! {
<Router history={history}>
<Nav />

<main>
<Switch<Route> render={Switch::render(switch)} />
</main>
<footer class="footer">
<div class="content has-text-centered">
{ "Powered by " }
<a href="https://yew.rs">{ "Yew" }</a>
{ " using " }
<a href="https://bulma.io">{ "Bulma" }</a>
{ " and images from " }
<a href="https://unsplash.com">{ "Unsplash" }</a>
</div>
</footer>
</Router>
}
}
}

#[cfg(not(target_arch = "wasm32"))]
pub use arch_native::*;

fn switch(routes: &Route) -> Html {
match routes.clone() {
Route::Post { id } => {
html! { <Post seed={id} /> }
}
Route::Posts => {
html! { <PostList /> }
}
Route::Author { id } => {
html! { <Author seed={id} /> }
}
Route::Authors => {
html! { <AuthorList /> }
}
Route::Home => {
html! { <Home /> }
}
Route::NotFound => {
html! { <PageNotFound /> }
}
}
}
75 changes: 75 additions & 0 deletions examples/function_router/src/components/author_card.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use std::rc::Rc;

use crate::{content::Author, generator::Generated, Route};
use yew::prelude::*;
use yew_router::prelude::*;

#[derive(Clone, Debug, PartialEq, Properties)]
pub struct Props {
pub seed: u32,
}

#[derive(PartialEq, Debug)]
pub struct AuthorState {
pub inner: Author,
}

impl Reducible for AuthorState {
type Action = u32;

fn reduce(self: Rc<Self>, action: u32) -> Rc<Self> {
Self {
inner: Author::generate_from_seed(action),
}
.into()
}
}

#[function_component]
pub fn AuthorCard(props: &Props) -> Html {
let seed = props.seed;

let author = use_reducer_eq(|| AuthorState {
inner: Author::generate_from_seed(seed),
});

{
let author_dispatcher = author.dispatcher();
use_effect_with_deps(
move |seed| {
author_dispatcher.dispatch(*seed);

|| {}
},
seed,
);
}

let author = &author.inner;

html! {
<div class="card">
<div class="card-content">
<div class="media">
<div class="media-left">
<figure class="image is-128x128">
<img alt="Author's profile picture" src={author.image_url.clone()} />
</figure>
</div>
<div class="media-content">
<p class="title is-3">{ &author.name }</p>
<p>
{ "I like " }
<b>{ author.keywords.join(", ") }</b>
</p>
</div>
</div>
</div>
<footer class="card-footer">
<Link<Route> classes={classes!("card-footer-item")} to={Route::Author { id: author.seed }}>
{ "Profile" }
</Link<Route>>
</footer>
</div>
}
}
5 changes: 5 additions & 0 deletions examples/function_router/src/components/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pub mod author_card;
pub mod nav;
pub mod pagination;
pub mod post_card;
pub mod progress_delay;
Loading