diff --git a/index.html b/index.html index abe66e0..c92de83 100644 --- a/index.html +++ b/index.html @@ -11,6 +11,8 @@ + + diff --git a/main.scss b/main.scss index e7607e1..68fc74a 100644 --- a/main.scss +++ b/main.scss @@ -40,6 +40,21 @@ html * { 0.02) !important; } +.card { + backdrop-filter: blur(4px) !important; + --bulma-card-background-color: #1E1E2E80 !important; + --bulma-shadow-h: 232 !important; + --bulma-shadow-s: 97% !important; + --bulma-shadow-l: 85% !important; + --bulma-shadow: 0 0.5em 1em -0.125em hsla(var(--bulma-shadow-h), + var(--bulma-shadow-s), + var(--bulma-shadow-l), + 0.3), 0 0px 0 1px hsla(var(--bulma-shadow-h), + var(--bulma-shadow-s), + var(--bulma-shadow-l), + 0.02) !important; +} + .input, .select select, .textarea { diff --git a/public/blog/testimage.jpg b/public/blog/testimage.jpg new file mode 100644 index 0000000..df9e801 Binary files /dev/null and b/public/blog/testimage.jpg differ diff --git a/src/main.rs b/src/main.rs index 9a4a52f..18742e9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,13 +21,13 @@ use pages::not_found::PageNotFound; pub enum Route { #[at("/")] About, - + #[at("/blog/entries/:id")] - Entry { id: u64 }, + Entry { id: u8 }, #[at("/blog/entries")] Entries, #[at("/blog/authors/:id")] - Author { id: u64 }, + Author { id: u8 }, #[at("/blog/authors")] Authors, @@ -131,7 +131,7 @@ fn switch(routes: Route) -> Html { Route::About => { html! { } } Route::Entries => { html! { } } - Route::Entry { id } => { html! { } } + Route::Entry { id } => { html! { } } Route::Authors => { html! { } } Route::Author { id } => { html! { } } diff --git a/src/pages/about.rs b/src/pages/about.rs index e87a39a..efc0223 100644 --- a/src/pages/about.rs +++ b/src/pages/about.rs @@ -12,18 +12,7 @@ impl Component for About { fn view(&self, _ctx: &Context) -> Html { html! { <> - - { self.view_info_card() } - - > - } - } -} -impl About { - fn view_info_card(&self) -> Html { - html! { - <> - + {r" Lucielle R. Hoerner"} diff --git a/src/pages/blog/author.rs b/src/pages/blog/author.rs index fdf0c72..642b5b6 100644 --- a/src/pages/blog/author.rs +++ b/src/pages/blog/author.rs @@ -4,7 +4,7 @@ use crate::pages::blog::content::BlogEntry; #[derive(Clone, Debug, Eq, PartialEq, Properties)] pub struct Props { - pub seed: u64, + pub seed: u8, } pub struct Author { @@ -25,8 +25,26 @@ impl Component for Author { fn view(&self, _ctx: &Context) -> Html { let Self { author } = self; - html! { + + + + {author.name.clone()} + + { for author.keywords.iter().map(|tag| html! { { tag } }) } + + + + {r#"about me"#} + + { author.about.clone() } + + + } + } +} + + /* @@ -53,7 +71,7 @@ impl Component for Author { { "About me" } - { "This author has chosen not to reveal anything about themselves" } + { author.about.clone() } @@ -61,6 +79,4 @@ impl Component for Author { - } - } -} \ No newline at end of file + */ \ No newline at end of file diff --git a/src/pages/blog/authorcard.rs b/src/pages/blog/authorcard.rs index 23615d9..b2f7beb 100644 --- a/src/pages/blog/authorcard.rs +++ b/src/pages/blog/authorcard.rs @@ -1,26 +1,26 @@ use yew::prelude::*; -use yew_router::prelude::*; +use yew_router::components::Link; + +use crate::pages::blog::content::BlogEntry; +use crate::pages::blog::content::Author; -use crate::content::Author; -use crate::generator::Generated; use crate::Route; #[derive(Clone, Debug, PartialEq, Eq, Properties)] -pub struct Props { - pub seed: u64, +pub struct PropsAuthorCard { + pub seed: u8, } pub struct AuthorCard { author: Author, } + impl Component for AuthorCard { type Message = (); - type Properties = Props; + type Properties = PropsAuthorCard; fn create(ctx: &Context) -> Self { - Self { - author: Author::from_seed(ctx.props().seed), - } + Self { author: Author::from_seed(ctx.props().seed), } } fn changed(&mut self, ctx: &Context, _old_props: &Self::Properties) -> bool { @@ -31,12 +31,13 @@ impl Component for AuthorCard { fn view(&self, _ctx: &Context) -> Html { let Self { author } = self; html! { + <> - + @@ -54,6 +55,7 @@ impl Component for AuthorCard { > + > } } -} \ No newline at end of file +} diff --git a/src/pages/blog/authors.rs b/src/pages/blog/authors.rs index debcf71..6df8d0d 100644 --- a/src/pages/blog/authors.rs +++ b/src/pages/blog/authors.rs @@ -1,37 +1,31 @@ use yew::prelude::*; -use crate::pages::blog::content::AuthorCard; +use crate::pages::blog::authorcard::AuthorCard; -pub enum Msg { +/*pub enum Msg { NextAuthors, -} +}*/ pub struct Authors { - seeds: Vec, + seeds: Vec, } impl Component for Authors { - type Message = Msg; + type Message = (); //Msg; type Properties = (); fn create(_ctx: &Context) -> Self { - Self { seeds: vec![1], } + Self { seeds: vec![0], } } - fn update(&mut self, _ctx: &Context, msg: Self::Message) -> bool { - match msg { Msg::NextAuthors => { self.seeds = vec![1]; true } } - } + /*fn update(&mut self, _ctx: &Context, msg: Self::Message) -> bool { + match msg { Msg::NextAuthors => { self.seeds = vec![0]; true } } + }*/ - fn view(&self, ctx: &Context) -> Html { + fn view(&self, _: &Context) -> Html { html! { - - { for self.seeds.iter().map(|&seed| { html! { - - - - - - } }) } - + { for self.seeds.iter().map(|&seed| { html! { + + } }) } } } } diff --git a/src/pages/blog/content.rs b/src/pages/blog/content.rs index a4ac299..33f58a5 100644 --- a/src/pages/blog/content.rs +++ b/src/pages/blog/content.rs @@ -1,34 +1,39 @@ -use std::rc::Rc; -use std::ops::Range; - -use serde::{Deserialize, Serialize}; -use yew::prelude::*; -use yew_router::prelude::*; -use yew_router::components::Link; - -use crate::Route; - -const ELLIPSIS: &str = "\u{02026}"; - #[derive(Clone, Debug, Eq, PartialEq)] pub struct Author { - pub seed: u64, + pub seed: u8, pub name: String, pub keywords: Vec, pub image_url: String, + pub about: String } impl BlogEntry for Author { fn from_entry(entry: &mut Entry) -> Self { return match entry.seed { - 1 => Self {seed: entry.seed, name: "iouring".to_string(), keywords: vec!["meow".to_string(), "mrrp".to_string(), "mew".to_string()], image_url: "".to_string() }, - _ => Self {seed: entry.seed, name: "none".to_string(), keywords: vec!["meow".to_string(), "mrrp".to_string(), "mew".to_string()], image_url: "".to_string()} - }; + 0 => Self { + seed: entry.seed, + name: "iouring".to_string(), + image_url: "profile.avif".to_string(), + about: "sup".to_string(), + keywords: vec![ + "meow".to_string(), + "mrrp".to_string(), + "mew".to_string() + ] + }, + _ => Self { + seed: u8::MAX, + name: "not found".to_string(), + image_url: "".to_string(), + about: "".to_string(), + keywords: vec![] + } + } } } #[derive(Clone, Debug, Eq, PartialEq)] pub struct PostMeta { - pub seed: u64, + pub seed: u8, pub title: String, pub author: Author, pub keywords: Vec, @@ -36,12 +41,21 @@ pub struct PostMeta { } impl BlogEntry for PostMeta { fn from_entry(entry: &mut Entry) -> Self { - return Self { - seed: entry.seed, - title: "awawa title".to_string(), - author: Author::from_entry(entry), - keywords: vec!["meow".to_string(), "mrrp".to_string(), "mew".to_string()], - image_url: "".to_string() + return match entry.seed { + 0 => Self { + seed: entry.seed, + title: "awawa title".to_string(), + author: Author::from_entry(entry), + keywords: vec!["meow".to_string(), "mrrp".to_string(), "mew".to_string()], + image_url: "testimage.jpg".to_string() + }, + _ => Self { + seed: entry.seed, + title: "not found".to_string(), + author: Author::from_entry(entry), + keywords: vec![], + image_url: "".to_string() + } } } } @@ -53,7 +67,10 @@ pub struct Post { } impl BlogEntry for Post { fn from_entry(entry: &mut Entry) -> Self { - return Self { meta: PostMeta::from_entry(entry), content: vec![PostPart::from_entry(entry)] } + return Self { + meta: PostMeta::from_entry(entry), + content: vec![PostPart::from_entry(entry)] + } } } @@ -64,7 +81,10 @@ pub enum PostPart { } impl BlogEntry for PostPart { fn from_entry(entry: &mut Entry) -> Self { - return Self::Quote(Quote::from_entry(entry)); + // TODO: ... | add proper logic + + return Self::Section(Section::from_entry(entry)) + // return Self::Quote(Quote::from_entry(entry)) } } @@ -76,7 +96,22 @@ pub struct Section { } impl BlogEntry for Section { fn from_entry(entry: &mut Entry) -> Self { - return Self { title: "awawa title".to_string(), paragraphs: vec!["meow".to_string(), "mrrp".to_string(), "mew".to_string()], image_url: "".to_string() } + return match entry.seed { + 0 => Self { + title: "awawa title".to_string(), + image_url: "".to_string(), + paragraphs: vec![ + "meow 1".to_string(), + "mrrp 2".to_string(), + "mew 3".to_string() + ] + }, + _ => Self { + title: "not found".to_string(), + image_url: "".to_string(), + paragraphs: vec![] + }, + } } } @@ -92,332 +127,18 @@ impl BlogEntry for Quote { } pub struct Entry { - pub seed: u64 + pub seed: u8 } impl Entry { - pub fn from_seed(seed: u64) -> Self { + pub fn from_seed(seed: u8) -> Self { Self { seed } } } pub trait BlogEntry: Sized { fn from_entry(entry: &mut Entry) -> Self; - fn from_seed(seed: u64) -> Self { + fn from_seed(seed: u8) -> Self { Self::from_entry(&mut Entry::from_seed(seed)) } } - -#[derive(Clone, Debug, PartialEq, Eq, Properties)] -pub struct PropsAuthorCard { - pub seed: u64, -} - -pub struct AuthorCard { - author: Author, -} -impl Component for AuthorCard { - type Message = (); - type Properties = PropsAuthorCard; - - fn create(ctx: &Context) -> Self { - Self { - author: Author::from_seed(ctx.props().seed), - } - } - - fn changed(&mut self, ctx: &Context, _old_props: &Self::Properties) -> bool { - self.author = Author::from_seed(ctx.props().seed); - true - } - - fn view(&self, _ctx: &Context) -> Html { - let Self { author } = self; - html! { - - - - - - - - - - { &author.name } - - { "I like " } - { author.keywords.join(", ") } - - - - - - - } - } -} - -#[derive(Clone, Debug, PartialEq, Eq, Properties)] -pub struct PropsPostCard { - pub seed: u64, -} - -#[derive(PartialEq, Eq, Debug)] -pub struct PostMetaState { - inner: PostMeta, -} - -impl Reducible for PostMetaState { - type Action = u64; - - fn reduce(self: Rc, action: u64) -> Rc { - Self { inner: PostMeta::from_seed(action.into()), }.into() - } -} - -#[function_component] -pub fn PostCard(props: &PropsPostCard) -> Html { - let seed = props.seed; - - let post = use_reducer_eq(|| PostMetaState { - inner: PostMeta::from_seed(seed.into()), - }); - - { - let post_dispatcher = post.dispatcher(); - use_effect_with(seed, move |seed| { - post_dispatcher.dispatch(*seed); - - || {} - }); - } - - let post = &post.inner; - - html! { - - - - - - - - classes={classes!("title", "is-block")} to={Route::Entry { id: post.seed }}> - { &post.title } - > - classes={classes!("subtitle", "is-block")} to={Route::Author { id: post.author.seed }}> - { &post.author.name } - > - - - } -} - -#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)] -pub struct PageQuery { - pub page: u64, -} - -#[derive(Clone, Debug, PartialEq, Eq, Properties)] -pub struct PropsNavigation { - pub page: u64, - pub total_pages: u64, - pub route_to_page: Route, -} - -#[function_component] -pub fn RelNavButtons(props: &PropsNavigation) -> Html { - let PropsNavigation { - page, - total_pages, - route_to_page: to, - } = props.clone(); - - html! { - <> - - classes={classes!("pagination-previous")} - disabled={page==1} - query={Some(PageQuery{page: page - 1})} - to={to.clone()} - > - { "Previous" } - > - - classes={classes!("pagination-next")} - disabled={page==total_pages} - query={Some(PageQuery{page: page + 1})} - {to} - > - { "Next page" } - > - > - } -} - -#[derive(Properties, Clone, Debug, PartialEq, Eq)] -pub struct RenderLinksProps { - range: Range, - len: usize, - max_links: usize, - props: PropsNavigation, -} - -#[function_component] -pub fn RenderLinks(props: &RenderLinksProps) -> Html { - let RenderLinksProps { - range, - len, - max_links, - props, - } = props.clone(); - - let mut range = range; - - if len > max_links { - let last_link = - html! {}; - // remove 1 for the ellipsis and 1 for the last link - let links = range - .take(max_links - 2) - .map(|page| html! {}); - html! { - <> - { for links } - { ELLIPSIS } - { last_link } - > - } - } else { - html! { - for page in range { - - } - } - } -} - -#[derive(Properties, Clone, Debug, PartialEq, Eq)] -pub struct RenderLinkProps { - to_page: u64, - props: PropsNavigation, -} - -#[function_component] -pub fn RenderLink(props: &RenderLinkProps) -> Html { - let RenderLinkProps { to_page, props } = props.clone(); - - let PropsNavigation { - page, - route_to_page, - .. - } = props; - - let is_current_class = if to_page == page { "is-current" } else { "" }; - - html! { - - - classes={classes!("pagination-link", is_current_class)} - to={route_to_page} - query={Some(PageQuery{page: to_page})} - > - { to_page } - > - - } -} - -#[function_component] -pub fn Links(props: &PropsNavigation) -> Html { - const LINKS_PER_SIDE: usize = 3; - - let PropsNavigation { - page, total_pages, .. - } = *props; - - let pages_prev = page.checked_sub(1).unwrap_or_default() as usize; - let pages_next = (total_pages - page) as usize; - - let links_left = LINKS_PER_SIDE.min(pages_prev) - // if there are less than `LINKS_PER_SIDE` to the right, we add some more on the left. - + LINKS_PER_SIDE.checked_sub(pages_next).unwrap_or_default(); - let links_right = 2 * LINKS_PER_SIDE - links_left; - - html! { - <> - - - - > - } -} - -#[function_component] -pub fn Pagination(props: &PropsNavigation) -> Html { - html! { - - - - - - - } -} - -#[function_component] -pub fn Nav() -> Html { - let navbar_active = use_state_eq(|| false); - - let toggle_navbar = { - let navbar_active = navbar_active.clone(); - - Callback::from(move |_| { - navbar_active.set(!*navbar_active); - }) - }; - - let active_class = if !*navbar_active { "is-active" } else { "" }; - - html! { - - - { "Yew Blog" } - - - - - - - - - - classes={classes!("navbar-item")} to={Route::Entries}> // TODO: ... - { "Home" } - > - classes={classes!("navbar-item")} to={Route::Entries}> - { "Posts" } - > - - - - { "More" } - - - classes={classes!("navbar-item")} to={Route::Authors}> - { "Meet the authors" } - > - - - - - - } -} diff --git a/src/pages/blog/entries.rs b/src/pages/blog/entries.rs index 0745823..0f63200 100644 --- a/src/pages/blog/entries.rs +++ b/src/pages/blog/entries.rs @@ -1,26 +1,28 @@ use yew::prelude::*; use yew_router::prelude::*; -use crate::pages::blog::content::PostCard; -use crate::pages::blog::content::{PageQuery, Pagination}; +use crate::pages::blog::content::Author; +use crate::pages::blog::content::BlogEntry; +use crate::pages::blog::entrycard::EntryCard; +use crate::pages::blog::navigation::{PageQuery, Pagination}; use crate::Route; -const ITEMS_PER_PAGE: u64 = 10; -const TOTAL_PAGES: u64 = u64::MAX / ITEMS_PER_PAGE; +const ITEMS_PER_PAGE: u8 = 10; +const TOTAL_PAGES: u8 = 1; pub enum Msg { PageUpdated, } pub struct Entries { - page: u64, + page: u8, _listener: LocationHandle, } -fn current_page(ctx: &Context) -> u64 { +fn current_page(ctx: &Context) -> u8 { let location = ctx.link().location().unwrap(); - location.query::().map(|it| it.page as u64).unwrap_or(1) + location.query::().map(|it| it.page as u8).unwrap_or(1) } impl Component for Entries { @@ -52,8 +54,8 @@ impl Component for Entries { html! { - { "Posts" } - { "All of our quality writing in one place" } + { "entries" } + { "here are some things i yapped about" } { self.view_posts(ctx) } ) -> Html { let start_seed = (self.page - 1) * ITEMS_PER_PAGE; - let mut cards = (0..ITEMS_PER_PAGE).map(|seed_offset| { - html! { - - - - } - }); + let cards: Vec<_> = (0..ITEMS_PER_PAGE) + .filter(|&seed_offset| Author::from_seed(seed_offset).seed != u8::MAX) + .map(|seed_offset| { + html! { + + + + } + }).collect(); html! { - - - { for cards.by_ref().take(ITEMS_PER_PAGE as usize / 2) } - - { for cards } diff --git a/src/pages/blog/entry.rs b/src/pages/blog/entry.rs index 005e969..12f159d 100644 --- a/src/pages/blog/entry.rs +++ b/src/pages/blog/entry.rs @@ -9,7 +9,7 @@ use crate::pages::blog::content::BlogEntry; #[derive(Clone, Debug, Eq, PartialEq, Properties)] pub struct Props { - pub seed: u32, + pub seed: u8, } #[derive(PartialEq, Eq, Debug)] @@ -18,8 +18,8 @@ pub struct PostState { } impl Reducible for PostState { - type Action = u32; - fn reduce(self: Rc, action: u32) -> Rc { + type Action = u8; + fn reduce(self: Rc, action: u8) -> Rc { Self { inner: content::Post::from_seed(action.into()), }.into() } } diff --git a/src/pages/blog/entrycard.rs b/src/pages/blog/entrycard.rs new file mode 100644 index 0000000..2774360 --- /dev/null +++ b/src/pages/blog/entrycard.rs @@ -0,0 +1,68 @@ +use std::rc::Rc; + +use yew::prelude::*; +use yew_router::components::Link; + +use crate::pages::blog::content::BlogEntry; +use crate::pages::blog::content::PostMeta; + +use crate::Route; + +#[derive(Clone, Debug, PartialEq, Eq, Properties)] +pub struct PropsEntryCard { + pub seed: u8, +} + +#[derive(PartialEq, Eq, Debug)] +pub struct PostMetaState { + inner: PostMeta, +} + +impl Reducible for PostMetaState { + type Action = u8; + + fn reduce(self: Rc, action: u8) -> Rc { + Self { inner: PostMeta::from_seed(action.into()), }.into() + } +} + +#[function_component] +pub fn EntryCard(props: &PropsEntryCard) -> Html { + let seed = props.seed; + + let post = use_reducer_eq(|| PostMetaState { + inner: PostMeta::from_seed(seed.into()), + }); + + { + let post_dispatcher = post.dispatcher(); + use_effect_with(seed, move |seed| { + post_dispatcher.dispatch(*seed); + + || {} + }); + } + + let post = &post.inner; + + html! { + + + + classes={classes!("title", "is-block")} to={Route::Entry { id: post.seed }}> + { &post.title } + > + classes={classes!("subtitle", "is-block")} to={Route::Author { id: post.author.seed }}> + { &post.author.name } + > + + + } +} diff --git a/src/pages/blog/mod.rs b/src/pages/blog/mod.rs index 12ceea5..78af590 100644 --- a/src/pages/blog/mod.rs +++ b/src/pages/blog/mod.rs @@ -1,5 +1,8 @@ pub mod content; pub mod entries; pub mod entry; +pub mod entrycard; pub mod authors; -pub mod author; \ No newline at end of file +pub mod author; +pub mod authorcard; +pub mod navigation; diff --git a/src/pages/blog/navigation.rs b/src/pages/blog/navigation.rs new file mode 100644 index 0000000..c459e35 --- /dev/null +++ b/src/pages/blog/navigation.rs @@ -0,0 +1,213 @@ +use std::ops::Range; + +use serde::{Deserialize, Serialize}; +use yew::prelude::*; +use yew_router::components::Link; + +use crate::Route; + +const ELLIPSIS: &str = "\u{02026}"; + +#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)] +pub struct PageQuery { + pub page: u8, +} + +#[derive(Clone, Debug, PartialEq, Eq, Properties)] +pub struct PropsNavigation { + pub page: u8, + pub total_pages: u8, + pub route_to_page: Route, +} + +#[function_component] +pub fn RelNavButtons(props: &PropsNavigation) -> Html { + let PropsNavigation { + page, + total_pages, + route_to_page: to, + } = props.clone(); + + html! { + <> + + classes={classes!("pagination-previous")} + disabled={page==1} + query={Some(PageQuery{page: page - 1})} + to={to.clone()} + > + { "Previous" } + > + + classes={classes!("pagination-next")} + disabled={page==total_pages} + query={Some(PageQuery{page: page + 1})} + {to} + > + { "Next page" } + > + > + } +} + +#[derive(Properties, Clone, Debug, PartialEq, Eq)] +pub struct RenderLinksProps { + range: Range, + len: usize, + max_links: usize, + props: PropsNavigation, +} + +#[function_component] +pub fn RenderLinks(props: &RenderLinksProps) -> Html { + let RenderLinksProps { + range, + len, + max_links, + props, + } = props.clone(); + + let mut range = range; + + if len > max_links { + let last_link = + html! {}; + let links = range + .take(max_links - 2) + .map(|page| html! {}); + html! { + <> + { for links } + { ELLIPSIS } + { last_link } + > + } + } else { + html! { + for page in range { + + } + } + } +} + +#[derive(Properties, Clone, Debug, PartialEq, Eq)] +pub struct RenderLinkProps { + to_page: u8, + props: PropsNavigation, +} + +#[function_component] +pub fn RenderLink(props: &RenderLinkProps) -> Html { + let RenderLinkProps { to_page, props } = props.clone(); + + let PropsNavigation { + page, + route_to_page, + .. + } = props; + + let is_current_class = if to_page == page { "is-current" } else { "" }; + + html! { + + + classes={classes!("pagination-link", is_current_class)} + to={route_to_page} + query={Some(PageQuery{page: to_page})} + > + { to_page } + > + + } +} + +#[function_component] +pub fn Links(props: &PropsNavigation) -> Html { + const LINKS_PER_SIDE: usize = 3; + + let PropsNavigation { + page, total_pages, .. + } = *props; + + let pages_prev = page.checked_sub(1).unwrap_or_default() as usize; + let pages_next = (total_pages - page) as usize; + + let links_left = LINKS_PER_SIDE.min(pages_prev) + // if there are less than `LINKS_PER_SIDE` to the right, we add some more on the left. + + LINKS_PER_SIDE.checked_sub(pages_next).unwrap_or_default(); + let links_right = 2 * LINKS_PER_SIDE - links_left; + + html! { + <> + + + + > + } +} + +#[function_component] +pub fn Pagination(props: &PropsNavigation) -> Html { + html! { + + + + + + + } +} + +#[function_component] +pub fn Nav() -> Html { + let navbar_active = use_state_eq(|| false); + + let toggle_navbar = { + let navbar_active = navbar_active.clone(); + + Callback::from(move |_| { + navbar_active.set(!*navbar_active); + }) + }; + + let active_class = if !*navbar_active { "is-active" } else { "" }; + + html! { + + + { "Yew Blog" } + + + + + + + + + + classes={classes!("navbar-item")} to={Route::Entries}> // TODO: ... + { "Home" } + > + classes={classes!("navbar-item")} to={Route::Entries}> + { "Posts" } + > + + + + { "More" } + + + classes={classes!("navbar-item")} to={Route::Authors}> + { "Meet the authors" } + > + + + + + + } +}
{r" Lucielle R. Hoerner"}
{author.name.clone()}
+ { for author.keywords.iter().map(|tag| html! {
{ tag }
{r#"about me"#}
{ "About me" }
{ &author.name }
- { "I like " } - { author.keywords.join(", ") } -