Perfected Navbar

This commit is contained in:
Franklin 2023-04-04 11:14:16 -04:00
parent 508a165208
commit 2df40ac348
11 changed files with 304 additions and 87 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -8,6 +8,6 @@ body {
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
align-items: stretch; align-items: stretch;
background-color: rgb(0, 0, 0, 0.02); background-color: rgb(243, 243, 243);
} }

View File

@ -1,6 +1,6 @@
/* Mobile View */ /* Mobile View */
.navbar-background { .navbar-background {
border-top: 3px solid #04B2D9; /*border-top: 3px solid #04B2D9;*/
position: fixed; position: fixed;
z-index: 1; z-index: 1;
width: 100%; width: 100%;
@ -9,8 +9,9 @@
justify-content: end; justify-content: end;
align-items: center; align-items: center;
align-items: stretch; align-items: stretch;
background-color: #252631; background-color: #02114A;
min-height: 74px; min-height: 74px;
border-radius: 0px 0px 16px 16px;
} }
.navbar-container { .navbar-container {
@ -26,14 +27,52 @@
font-size: 20pt; font-size: 20pt;
color: white; color: white;
display: flex; display: flex;
flex-direction: column;
justify-content: center; justify-content: center;
align-items: center; align-items: flex-end;
padding: 0px;
gap: 4px;
margin-top: 5px; margin-top: 5px;
max-height: 35px; max-height: 35px;
min-width: 30px; min-width: 30px;
padding: 15px; padding: 15px;
} }
.navbar-hamburger-bar-1 {
transition-duration: 0.4s;
width: 16px;
height: 3px;
background: #FFFFFF;
border-radius: 8888px;
}
.navbar-hamburger-bars {
transition-duration: 0.4s;
width: 27px;
height: 3px;
background: #FFFFFF;
border-radius: 8888px;
}
.navbar-hamburger-x-1 {
transition-duration: 0.2s;
position: absolute;
width: 27px;
height: 3px;
background: #FFFFFF;
border-radius: 8888px;
transform: rotate(-43.4deg);
}
.navbar-hamburger-x-2 {
transition-duration: 0.2s;
position: absolute;
width: 27px;
height: 3px;
background: #FFFFFF;
border-radius: 8888px;
transform: rotate(43.4deg);
}
.navbar-hamburger:hover { .navbar-hamburger:hover {
background-color: #ffffff14; background-color: #ffffff14;
font-size: 22pt; font-size: 22pt;
@ -52,21 +91,32 @@
} }
.navbar-open { .navbar-open {
margin-top: 20px;
margin-right: -50px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: flex-start; justify-content: flex-start;
margin-top: 10px; align-items: end;
} }
.navbar-item { .navbar-item {
color: white; color: white;
font-size: 12pt; font-size: 12pt;
padding-top: 10px; padding-top: 10px;
padding-left: 5px; padding-left: 10px;
padding-right: 10px;
padding-bottom: 10px; padding-bottom: 10px;
font-family: Source Sans Pro; font-family: Space Grotesk;
}
.navbar-item-selected {
color: white;
font-size: 12pt;
padding-top: 10px;
padding-left: 10px;
padding-right: 10px;
padding-bottom: 10px;
font-family: Space Grotesk;
} }
.navbar-item:hover { .navbar-item:hover {
background-color: #ffffff14; background-color: #ffffff14;
font-size: 13pt; font-size: 13pt;
@ -89,25 +139,29 @@
cursor: pointer; cursor: pointer;
} }
.navbar-title {
color: white;
font-size: 16pt;
font-weight: 100;
font-family: 'Gill Sans';
}
.navbar-image { .navbar-image {
max-width: 50px; width: 60px;
max-height: 50px; height: 50px;
object-fit: cover;
margin-top: -10px;
} }
/* Desktop view */ /* Desktop view */
@media only screen and (min-width: 850px) { @media only screen and (min-width: 750px) {
.navbar-background {
background-color: rgb(243, 243, 243);
border-radius: 0px;
border-bottom: solid 0.5px #d8d8d8;
min-height: 60px;
}
.navbar-container { .navbar-container {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: flex-start; justify-content: space-between;
align-items: stretch; align-items: center;
padding: 0px;
} }
.navbar { .navbar {
@ -120,9 +174,10 @@
.navbar-closed { .navbar-closed {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: flex-end; justify-content: center;
flex-grow: 1; flex-grow: 1;
margin-top: 0px; margin-top: 0px;
gap: 5%;
} }
.navbar-open { .navbar-open {
@ -134,31 +189,52 @@
} }
.navbar-item { .navbar-item {
color: white; color: #000000;
font-size: 12pt; font-size: 12pt;
padding-left: 20px; padding-left: 20px;
padding-right: 20px; padding-right: 20px;
padding-top: 10px; padding-top: 10px;
padding-bottom: 10px; padding-bottom: 10px;
font-family: Source Sans Pro; font-family: Source Sans Pro;
font-weight: 200;
}
.navbar-item-selected {
color: #02114A;
font-size: 12pt;
padding-left: 20px;
padding-right: 20px;
padding-top: 10px;
padding-bottom: 10px;
font-family: Source Sans Pro;
font-weight: bold;
} }
.navbar-item:hover { .navbar-item:hover {
background-color: #ffffff14; background-color: rgb(0, 0, 0, 0);
font-size: 13pt; font-size: 12pt;
font-weight: bold; font-weight: normal;
border-radius: 3px; border-radius: 0px;
transition-duration: 0.3s; transition-duration: 0.0s;
cursor: pointer; cursor: pointer;
} }
.navbar-item-contact-us {
font-size: 14px;
font-family: Space Grotesk;
.navbar-title {
color: white; color: white;
font-size: 23pt; background-color: #02114A;
font-family: Sacramento; display: flex;
} flex-direction: column;
justify-content: center;
.navbar-hamburger { padding: 0px;
display: none; margin-top: 10px;
margin-right: 30px;
max-height: 35px;
min-width: 30px;
padding: 2px 10px;
border-radius: 5px;
cursor: pointer;
} }
} }

View File

@ -4,7 +4,8 @@
background-color: white; background-color: white;
height: 495px; height: 495px;
width: 390px; width: 390px;
min-width: 80%; max-width: 400px;
max-height: 505px;
border-radius: 6px; border-radius: 6px;
display: flex; display: flex;
@ -17,12 +18,6 @@
gap: 6px; gap: 6px;
} }
@media only screen and (max-width: 400px) {
.project-listing-card {
width: 90%;
}
}
.project-listing-card:hover { .project-listing-card:hover {
cursor: pointer; cursor: pointer;
/*border: 3px solid #04B2D9;*/ /*border: 3px solid #04B2D9;*/

View File

@ -13,7 +13,6 @@
flex-direction: column; flex-direction: column;
gap: 25px; gap: 25px;
justify-content: stretch; justify-content: stretch;
flex-wrap: wrap;
align-items: stretch; align-items: stretch;
width: 100%; width: 100%;
} }
@ -49,6 +48,39 @@
font-size: 16px; font-size: 16px;
} }
.project-search-filter-select {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
width: 100%;
font-size: 13px;
font-weight: 400;
font-family: Space Grotesk;
text-align: center;
border: 1px solid #02114A;
cursor: pointer;
}
.project-search-filter-select-item {
padding: 15px 10px;
width: 100%;
height: 100%;
border-right: 1px solid #02114A;
}
.project-search-filter-select-item-selected {
color: white;
padding: 15px 10px;
width: 100%;
height: 100%;
border-right: 1px solid #02114A;
background-color: #02114A;
}
.project-search-divider { .project-search-divider {
width: 100%; width: 100%;
@ -86,3 +118,38 @@
margin-bottom: 50px; margin-bottom: 50px;
gap: 30px; gap: 30px;
} }
@media only screen and (min-width: 750px) {
.project-search-filters-container {
flex-direction: row;
flex-flow: row wrap;
justify-content: center;
align-items: center;
}
.project-search-filter-container {
max-width: 450px;
min-width: 400px;
}
.project-search-button {
min-width: 400px;
margin-top: 30px;
}
.project-search-filter-select {
width: 100%;
font-size: 15px;
font-weight: 400;
font-family: Space Grotesk;
text-align: center;
border: 1px solid #02114A;
cursor: pointer;
}
.project-search-results-container {
flex-direction: row;
flex-flow: row wrap;
justify-content: center;
align-items: center;
}
}

View File

@ -17,7 +17,7 @@
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet"> <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Sacramento" rel="stylesheet"> <link href="https://fonts.googleapis.com/css?family=Space+Grotesk" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Inter" rel="stylesheet"> <link href="https://fonts.googleapis.com/css?family=Inter" rel="stylesheet">
<script src="https://kit.fontawesome.com/fcdfdfe1ad.js" crossorigin="anonymous"></script> <script src="https://kit.fontawesome.com/fcdfdfe1ad.js" crossorigin="anonymous"></script>

View File

@ -1,18 +1,19 @@
use jl_types::domain::project_state::ProjectState; use stdweb::web::{IEventTarget, event::ResizeEvent};
use yew::prelude::*; use yew::prelude::*;
use yew_router::prelude::use_navigator; use yew_router::prelude::{use_navigator, use_route};
use crate::{routes::main_router::{Route}, constants::{NAVBAR_TITLE, NAVBAR_COL_LANDING, NAVBAR_COL_PROYECTOS_ACABADOS, NAVBAR_COL_CONTACTO, NAVBAR_COL_PROYECTOS_EN_CONSTRUCCION}}; use crate::{routes::main_router::{Route}, constants::{NAVBAR_COL_LANDING, NAVBAR_COL_CONTACTO, NAVBAR_COL_PROYECTOS}};
//use yew_router::prelude::use_navigator; //use yew_router::prelude::use_navigator;
#[function_component(NavigationBar)] #[function_component(NavigationBar)]
pub fn navigation_bar() -> Html { pub fn navigation_bar() -> Html {
let current_route: Option<Route> = use_route();
let navigator = use_navigator().unwrap(); let navigator = use_navigator().unwrap();
let cloned_navigator_1 = navigator.clone(); let cloned_navigator_1 = navigator.clone();
let cloned_navigator_2 = navigator.clone(); let cloned_navigator_2 = navigator.clone();
let cloned_navigator_3 = navigator.clone(); let cloned_navigator_3 = navigator.clone();
let cloned_navigator_4 = navigator.clone();
let cloned_navigator_5 = navigator.clone(); let cloned_navigator_5 = navigator.clone();
let navbar_toggle = use_state(|| true); let navbar_toggle = use_state(|| true);
@ -20,39 +21,92 @@ pub fn navigation_bar() -> Html {
let navbar_toggle = navbar_toggle.clone(); let navbar_toggle = navbar_toggle.clone();
Callback::from(move |_| navbar_toggle.set(!*navbar_toggle)) Callback::from(move |_| navbar_toggle.set(!*navbar_toggle))
}; };
let window_device_handle = use_state_eq(|| if stdweb::web::window().inner_width() > 750 {
WindowDevice::Desktop
} else {
WindowDevice::Mobile
});
let window_device_handle_cloned = window_device_handle.clone();
stdweb::web::window().add_event_listener(move |_: ResizeEvent| {
let current_width = stdweb::web::window().inner_width();
if current_width >= 750 {
window_device_handle_cloned.set(WindowDevice::Desktop)
} else {
window_device_handle_cloned.set(WindowDevice::Mobile)
}
});
html! { html! {
<div class={"navbar-background"}> <div class={"navbar-background"}>
<div class={"navbar-container"}> <div class={"navbar-container"}>
<div class={"navbar-brand-container"} onclick={move |_| cloned_navigator_5.push(&Route::LandingPage)}> <div class={"navbar-brand-container"} onclick={move |_| cloned_navigator_5.push(&Route::LandingPage)}>
<img class={"navbar-image"} src="images/proyectosenconstruccion_logo_transparent_bg.png" alt=""/> <img class={"navbar-image"} src={ if (*window_device_handle) == WindowDevice::Mobile {"images/logo-white.png"} else {"images/logo-color.png"}} alt=""/>
<div class={"navbar-title"}>
{NAVBAR_TITLE}
</div>
</div> </div>
<div class={if *navbar_toggle {"navbar-closed"} else {"navbar-open"}}> <div class={if *navbar_toggle {"navbar-closed"} else {"navbar-open"}}>
<div onclick={move |_| cloned_navigator_1.push(&Route::LandingPage)} class={"navbar-item"}> <div onclick={move |_| cloned_navigator_1.push(&Route::LandingPage)} class={
if current_route.is_some() && matches!(current_route.clone().unwrap(), Route::LandingPage) {"navbar-item-selected"} else {"navbar-item"}
}>
{NAVBAR_COL_LANDING} {NAVBAR_COL_LANDING}
</div> </div>
<div onclick={move |_| cloned_navigator_2.push(&Route::Search { project_state: ProjectState::InConstruction })} class={"navbar-item"}> <div onclick={move |_| cloned_navigator_2.push(&Route::Search)} class={
{NAVBAR_COL_PROYECTOS_EN_CONSTRUCCION} if current_route.is_some() && matches!(current_route.unwrap(), Route::Search) {"navbar-item-selected"} else {"navbar-item"}
}>
{NAVBAR_COL_PROYECTOS}
</div> </div>
{
<div onclick={move |_| cloned_navigator_3.push(&Route::Search { project_state: ProjectState::Finished })} class={"navbar-item"}> if (*window_device_handle) == WindowDevice::Mobile {
{NAVBAR_COL_PROYECTOS_ACABADOS} html! {
</div> <div onclick={move |_| cloned_navigator_3.push(&Route::Contact)} class={"navbar-item"}>
<div onclick={move |_| cloned_navigator_4.push(&Route::Contact)} class={"navbar-item"}>
{NAVBAR_COL_CONTACTO} {NAVBAR_COL_CONTACTO}
</div> </div>
}
} else { html! {} }
}
</div> </div>
</div> </div>
{
if (*window_device_handle) == WindowDevice::Mobile {
html! {
<div class={"navbar-hamburger"} onclick={on_click_hamburger}> <div class={"navbar-hamburger"} onclick={on_click_hamburger}>
<i class="fa-solid fa-bars" ></i> {
if *navbar_toggle {
html! {
<>
<div class={"navbar-hamburger-bar-1"}></div>
<div class={"navbar-hamburger-bars"}></div>
<div class={"navbar-hamburger-bars"}></div>
</>
}
} else {
html! {
<>
<div class={"navbar-hamburger-x-1"}></div>
<div class={"navbar-hamburger-x-2"}></div>
</>
}
}
}
</div> </div>
}
} else {
html! {
<div class={"navbar-item-contact-us"}>
{"Contáctanos"}
</div>
}
}
}
</div> </div>
} }
} }
#[derive(PartialEq, Eq, PartialOrd, Ord)]
enum WindowDevice {
Desktop,
Mobile
}

View File

@ -1,7 +1,5 @@
pub const NAVBAR_TITLE: &str = "Proyectos en Construcción";
pub const NAVBAR_COL_LANDING: &str = "Inicio"; pub const NAVBAR_COL_LANDING: &str = "Inicio";
pub const NAVBAR_COL_PROYECTOS_ACABADOS: &str = "Listos para entrega"; pub const NAVBAR_COL_PROYECTOS: &str = "Proyectos";
pub const NAVBAR_COL_PROYECTOS_EN_CONSTRUCCION: &str = "En construcción";
pub const NAVBAR_COL_CONTACTO: &str = "Contacto"; pub const NAVBAR_COL_CONTACTO: &str = "Contacto";
pub const PRIMARY_COLOR: &str = "#41BDD9"; pub const PRIMARY_COLOR: &str = "#41BDD9";

View File

@ -8,18 +8,16 @@ use crate::{components::{nav_bar::NavigationBar, project_card::ProjectCard, foot
#[function_component(SearchPage)] #[function_component(SearchPage)]
pub fn search_page(props: &SearchPageProperties) -> Html { pub fn search_page() -> Html {
// let force_update_trigger = use_force_update(); // let force_update_trigger = use_force_update();
let cities_handle = use_state(|| Vec::from([OptionWrapper::new(None)])); let cities_handle = use_state(|| Vec::from([OptionWrapper::new(None)]));
let districts_handle = use_state(|| Vec::from([OptionWrapper::new(None)])); let districts_handle = use_state(|| Vec::from([OptionWrapper::new(None)]));
let search_results_handle: UseStateHandle<Vec<ProjectCardDto>> = use_state(|| Vec::new()); let search_results_handle: UseStateHandle<Vec<ProjectCardDto>> = use_state(|| Vec::new());
let page_counter: UseStateHandle<i64> = use_state(|| 1); let page_counter: UseStateHandle<i64> = use_state(|| 1);
let finished_loading = use_state(|| false); let finished_loading = use_state(|| false);
let project_state_filter_handle = use_state_eq(|| ProjectState::InConstruction);
let mut filters = Vec::new(); let filters = Vec::new();
if props.project_state.eq(&ProjectState::Finished) {
filters.push(Filter::Finished);
}
// All code to execute on first render and never again // All code to execute on first render and never again
use_state(|| { use_state(|| {
@ -130,7 +128,6 @@ pub fn search_page(props: &SearchPageProperties) -> Html {
class_css: Some("project-search-filter-item".into()) class_css: Some("project-search-filter-item".into())
}); });
//TODO: District dropdown should only show districts in city, otherwise show nothing or disabled
let project_district_drop_down = comp_with::<DropDown<OptionWrapper<String>>>(DropDownProps { let project_district_drop_down = comp_with::<DropDown<OptionWrapper<String>>>(DropDownProps {
initial: OptionWrapper::new(None), initial: OptionWrapper::new(None),
options: (*districts_handle).clone(), options: (*districts_handle).clone(),
@ -151,12 +148,11 @@ pub fn search_page(props: &SearchPageProperties) -> Html {
let project_condition_filter = project_condition_filter.clone(); let project_condition_filter = project_condition_filter.clone();
let project_city_filter = project_city_filter.clone(); let project_city_filter = project_city_filter.clone();
let project_district_filter = project_district_filter.clone(); let project_district_filter = project_district_filter.clone();
let project_state_filter = project_state_filter_handle.clone();
let props = props.clone();
Callback::from(move |_| { Callback::from(move |_| {
let mut filters = Vec::new(); let mut filters = Vec::new();
if props.project_state.eq(&ProjectState::Finished) { if (*project_state_filter).eq(&ProjectState::Finished) {
filters.push(Filter::Finished); filters.push(Filter::Finished);
} }
match &(*project_type_filter).option { match &(*project_type_filter).option {
@ -188,12 +184,49 @@ pub fn search_page(props: &SearchPageProperties) -> Html {
info!("done"); info!("done");
}); });
})}; })};
let inconstruction_state_select_onclick = {
let project_state_filter = project_state_filter_handle.clone();
Callback::from(move |_| {
project_state_filter.set(ProjectState::InConstruction)
})
};
let finished_state_select_onclick = {
let project_state_filter = project_state_filter_handle.clone();
Callback::from(move |_| {
project_state_filter.set(ProjectState::Finished)
})
};
html!{ html!{
<> <>
<NavigationBar/> <NavigationBar/>
<div class={"page-container"}> <div class={"page-container"}>
<div class={"project-search-container"}> <div class={"project-search-container"}>
<div class={"project-search-filters-container"}> // Filters <div class={"project-search-filters-container"}> // Filters
<div class={"project-search-filter-container"}>
<div class={"project-search-filter-label"}>
{"Estado del Proyecto"}
</div>
<div class={"project-search-filter-select"}>
<div class={if (*project_state_filter_handle) == ProjectState::InConstruction {
"project-search-filter-select-item-selected"
} else {
"project-search-filter-select-item"
}} onclick={inconstruction_state_select_onclick}>
{"En construcción"}
</div>
<div class={if (*project_state_filter_handle) == ProjectState::Finished {
"project-search-filter-select-item-selected"
} else {
"project-search-filter-select-item"
}} onclick={finished_state_select_onclick}>
{"Terminado"}
</div>
</div>
</div>
<div class={"project-search-filter-container"}> <div class={"project-search-filter-container"}>
<div class={"project-search-filter-label"}> <div class={"project-search-filter-label"}>
{"Tipo de Proyecto"} {"Tipo de Proyecto"}
@ -264,8 +297,3 @@ pub fn search_page(props: &SearchPageProperties) -> Html {
</> </>
} }
} }
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Properties)]
pub struct SearchPageProperties {
pub project_state: ProjectState,
}

View File

@ -1,4 +1,3 @@
use jl_types::domain::project_state::ProjectState;
use yew_router::prelude::*; use yew_router::prelude::*;
use yew::prelude::*; use yew::prelude::*;
@ -10,8 +9,8 @@ use crate::{pages::{landing::LandingPage, search::{SearchPage}, details::Details
pub enum Route { pub enum Route {
#[at("/")] #[at("/")]
LandingPage, LandingPage,
#[at("/search/:project_state")] #[at("/search")]
Search { project_state: ProjectState }, Search,
#[at("/details/:project_id")] #[at("/details/:project_id")]
Details { project_id: Uuid }, Details { project_id: Uuid },
#[at("/contact")] #[at("/contact")]
@ -25,7 +24,7 @@ pub enum Route {
pub fn switch(routes: Route) -> Html { pub fn switch(routes: Route) -> Html {
match routes { match routes {
Route::LandingPage => html! { <LandingPage/> }, Route::LandingPage => html! { <LandingPage/> },
Route::Search { project_state } => html! { <SearchPage project_state={project_state}/> }, Route::Search => html! { <SearchPage/> },
Route::Details { project_id } => html! { <DetailsPage project_id={project_id}/> }, Route::Details { project_id } => html! { <DetailsPage project_id={project_id}/> },
Route::NotFound => html! { <NotFoundPage/> }, Route::NotFound => html! { <NotFoundPage/> },
Route::Contact => html! { <ContactPage/> } Route::Contact => html! { <ContactPage/> }