diff --git a/crates/ascend/src/app.rs b/crates/ascend/src/app.rs index 87b1abf..a072b09 100644 --- a/crates/ascend/src/app.rs +++ b/crates/ascend/src/app.rs @@ -40,11 +40,6 @@ pub fn App() -> impl leptos::IntoView { <Router> - // <nav class="shadow-md mb-2 bg-white border-gray-200 px-4 lg:px-6 py-2.5"> - // <div class="flex flex-wrap justify-start items-center gap-4 max-w-screen-xl"> - // <HeaderItem text="Home" link="/"/> - // </div> - // </nav> <main> <Routes fallback=|| "Not found"> <Route path=path!("/wall") view=Wall /> diff --git a/crates/ascend/src/components/header.rs b/crates/ascend/src/components/header.rs index ecba423..cd1ada1 100644 --- a/crates/ascend/src/components/header.rs +++ b/crates/ascend/src/components/header.rs @@ -1,6 +1,54 @@ use leptos::prelude::*; -#[component] -pub fn Header() -> impl leptos::IntoView { - leptos::view! { <div class="border-b py-2 mb-4">"Ascend"</div> } +pub struct HeaderItems { + pub left: Vec<HeaderItem>, + pub middle: Vec<HeaderItem>, + pub right: Vec<HeaderItem>, +} + +pub struct HeaderItem { + pub text: String, + pub link: Option<String>, +} + +#[component] +pub fn Header(items: HeaderItems) -> impl IntoView { + let HeaderItems { left, middle, right } = items; + + view! { + <div class="text-xl font-semibold p-4 flex gap-4"> + // Left side of header + <Items items=left /> + + // Expanding space in the middle + <div class="flex-auto mx-auto"> + <Items items=middle /> + </div> + + // Right side of header + <Items items=right /> + </div> + } +} + +#[component] +fn Items(items: Vec<HeaderItem>) -> impl IntoView { + items.into_iter().map(|item| view! { <Item item /> }).collect_view() +} + +#[component] +fn Item(item: HeaderItem) -> impl IntoView { + let text = item.text; + let link = item.link; + + if let Some(link) = link { + view! { + <a href=link> + <span class="whitespace-nowrap">{text}</span> + </a> + } + .into_any() + } else { + view! { <span class="whitespace-nowrap">{text}</span> }.into_any() + } } diff --git a/crates/ascend/src/pages/edit_wall.rs b/crates/ascend/src/pages/edit_wall.rs index 87711f4..6b69dac 100644 --- a/crates/ascend/src/pages/edit_wall.rs +++ b/crates/ascend/src/pages/edit_wall.rs @@ -1,5 +1,7 @@ use crate::codec::ron::RonCodec; use crate::components::header::Header; +use crate::components::header::HeaderItem; +use crate::components::header::HeaderItems; use crate::models; use crate::models::HoldPosition; use crate::models::Wall; @@ -21,10 +23,54 @@ pub fn EditWall() -> impl leptos::IntoView { load_initial_data().await.unwrap() }; + let header_items = HeaderItems { + left: vec![HeaderItem { + text: "← ".to_string(), + link: Some("/wall".to_string()), + }], + middle: vec![HeaderItem { + text: "Edit wall".to_string(), + link: None, + }], + right: vec![], + }; + leptos::view! { <div class="min-w-screen min-h-screen bg-slate-900"> - <div class="container mx-auto"> - <Header /> + <div class="flex"> + // Left gradient chunk + <div class="flex-grow"> + <div class="h-2/5" style="background: #eaac53" /> + <div class="h-3/5" style="background: linear-gradient(to bottom left, #eaac53 49.5%, rgb(15 23 42) 50.5%)" /> + </div> + + <div class="flex-none container mx-auto text-black" style="background: #eaac53"> + <Header items=header_items /> + </div> + + // Right gradient chunk + <div class="flex-grow" style="background: #eaac53" /> + </div> + <div class="flex"> + // Left gradient chunk + <div class="flex-grow" /> + + <div class="flex-none container mx-auto"> + // Background color gradient + <div + class="h-6" + style="background: linear-gradient(to bottom left, #eaac53 49.5%, rgb(15 23 42) 50.5%)" + /> + </div> + + // Right gradient chunk + <div class="flex-grow"> + <div class="h-4/5" style="background: #eaac53" /> + <div class="h-1/5" style="background: linear-gradient(to bottom right, #eaac53 49.5%, rgb(15 23 42) 50.5%)" /> + </div> + </div> + + <div class="container mx-auto mt-2"> <Await future=load let:data> <Ready data=data.deref().to_owned() /> </Await> @@ -44,7 +90,10 @@ fn Ready(data: InitialData) -> impl leptos::IntoView { let grid_classes = format!("grid grid-rows-{} grid-cols-{} gap-2", data.wall.rows, data.wall.cols); - view! { <div class=move || { grid_classes.clone() }>{holds}</div> } + view! { + <div> + <p class="my-4 font-semibold">"Click hold to replace image"</p> + <div class=move || { grid_classes.clone() }>{holds}</div> </div>} } #[component] @@ -98,15 +147,13 @@ fn Hold(hold: models::Hold) -> impl leptos::IntoView { let img = move || { hold.read().image.as_ref().map(|img| { let src = format!("/files/holds/{}", img.filename); - view! { - <img class="object-cover w-full h-full" src=src /> - } + view! { <img class="object-cover w-full h-full" src=src /> } }) }; view! { <button on:click=open_camera> - <div class="bg-indigo-100 aspect-square rounded">{ img }</div> + <div class="bg-indigo-100 aspect-square rounded">{img}</div> </button> <input diff --git a/crates/ascend/src/pages/wall.rs b/crates/ascend/src/pages/wall.rs index 494daf4..0b3903c 100644 --- a/crates/ascend/src/pages/wall.rs +++ b/crates/ascend/src/pages/wall.rs @@ -1,5 +1,7 @@ use crate::codec::ron::RonCodec; use crate::components::header::Header; +use crate::components::header::HeaderItem; +use crate::components::header::HeaderItems; use crate::models; use crate::models::HoldRole; use leptos::prelude::*; @@ -15,10 +17,22 @@ pub fn Wall() -> impl leptos::IntoView { load_initial_data().await.unwrap() }; + let header_items = HeaderItems { + left: vec![], + middle: vec![HeaderItem { + text: "Ascend".to_string(), + link: None, + }], + right: vec![HeaderItem { + text: "Edit wall".to_string(), + link: Some("/wall/edit".to_string()), + }], + }; + leptos::view! { <div class="min-w-screen min-h-screen bg-slate-900"> <div class="container mx-auto"> - <Header /> + <Header items=header_items /> <Await future=load let:data> <Ready data=data.deref().to_owned() /> </Await> @@ -43,7 +57,7 @@ fn Ready(data: InitialData) -> impl leptos::IntoView { let role = move || current_problem.get().and_then(|problem| problem.holds.get(&hold_position).copied()); let role = Signal::derive(role); - let cell = view! { <Hold role hold=hold.clone()/> }; + let cell = view! { <Hold role hold=hold.clone() /> }; cells.push(cell); } @@ -63,7 +77,8 @@ fn Hold(hold: models::Hold, #[prop(into)] role: Signal<Option<HoldRole>>) -> imp Some(HoldRole::Normal) => Some("outline outline-offset-2 outline-blue-500"), Some(HoldRole::Zone) => Some("outline outline-offset-2 outline-amber-500"), Some(HoldRole::End) => Some("outline outline-offset-2 outline-red-500"), - None => Some("brightness-50"), + // None => Some("brightness-50"), + None => None, }; let mut s = "bg-indigo-100 aspect-square rounded".to_string(); if let Some(c) = role_classes { @@ -75,12 +90,10 @@ fn Hold(hold: models::Hold, #[prop(into)] role: Signal<Option<HoldRole>>) -> imp let img = hold.image.map(|img| { let src = format!("/files/holds/{}", img.filename); - view! { - <img class="hover:object-scale-down object-cover w-full h-full" src=src /> - } + view! { <img class="hover:object-scale-down object-cover w-full h-full" src=src /> } }); - view! { <div class=class>{ img }</div> } + view! { <div class=class>{img}</div> } } #[derive(Serialize, Deserialize, Clone)]