Finished media_slideshow
This commit is contained in:
parent
474cbf3817
commit
66e932a6a0
43
css/components/loading.css
Normal file
43
css/components/loading.css
Normal file
@ -0,0 +1,43 @@
|
||||
.loading-container{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 50vh;
|
||||
}.lds-facebook {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
.lds-facebook div {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
left: 8px;
|
||||
width: 16px;
|
||||
background: #5D6A73;
|
||||
animation: lds-facebook 1.2s cubic-bezier(0, 0.5, 0.5, 1) infinite;
|
||||
}
|
||||
.lds-facebook div:nth-child(1) {
|
||||
left: 8px;
|
||||
animation-delay: -0.24s;
|
||||
}
|
||||
.lds-facebook div:nth-child(2) {
|
||||
left: 32px;
|
||||
animation-delay: -0.12s;
|
||||
}
|
||||
.lds-facebook div:nth-child(3) {
|
||||
left: 56px;
|
||||
animation-delay: 0;
|
||||
}
|
||||
@keyframes lds-facebook {
|
||||
0% {
|
||||
top: 8px;
|
||||
height: 64px;
|
||||
}
|
||||
50%, 100% {
|
||||
top: 24px;
|
||||
height: 32px;
|
||||
}
|
||||
}
|
||||
|
83
css/components/media_slideshow.css
Normal file
83
css/components/media_slideshow.css
Normal file
@ -0,0 +1,83 @@
|
||||
|
||||
.media-slideshow-frame {
|
||||
height: 360px;
|
||||
background-color: gray;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.media-slideshow-photo {
|
||||
background-color: gray;
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.media-slideshow-video {
|
||||
background-color: gray;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: none;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.media-slideshow-arrow-left {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
width: auto;
|
||||
margin-top: -22px;
|
||||
padding: 16px;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
transition: 0.6s ease;
|
||||
border-radius: 0 3px 3px 0;
|
||||
user-select: none;
|
||||
transition: 0.6s ease;
|
||||
}
|
||||
.media-slideshow-arrow-right {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
width: auto;
|
||||
margin-top: -22px;
|
||||
padding: 16px;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
transition: 0.6s ease;
|
||||
border-radius: 0 3px 3px 0;
|
||||
user-select: none;
|
||||
right: 0;
|
||||
border-radius: 3px 0 0 3px;
|
||||
transition: 0.6s ease;
|
||||
}
|
||||
|
||||
.media-slideshow-arrow-left:hover, .media-slideshow-arrow-right:hover {
|
||||
background-color: rgba(0,0,0,0.8);
|
||||
}
|
||||
|
||||
/* Number text (1/3 etc) */
|
||||
.media-slideshow-number-text {
|
||||
color: #f2f2f2;
|
||||
font-size: 12px;
|
||||
padding: 8px 12px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
background-color: rgba(0,0,0,0.8);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
|
||||
/* Fading animation */
|
||||
.fade {
|
||||
animation-name: fade;
|
||||
animation-duration: 1.5s;
|
||||
}
|
||||
|
||||
@keyframes fade {
|
||||
from {opacity: .4}
|
||||
to {opacity: 1}
|
||||
}
|
@ -18,15 +18,10 @@ Divide the Details page into 3 main sections:
|
||||
justify-content: stretch;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.details-head-image-frame {
|
||||
display: flex;
|
||||
height: 300px;
|
||||
width: 100%;
|
||||
background-color: gray;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.details-head-title {
|
||||
font-size: 25px;
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
.navbar-background {
|
||||
border-top: 3px solid #04B2D9;
|
||||
position: fixed;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@ -42,7 +43,7 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.navbar-closed{
|
||||
.navbar-closed {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
|
@ -10,7 +10,8 @@
|
||||
<link data-trunk type="text/css" href="css/search.css" rel="css" />
|
||||
<link data-trunk type="text/css" href="css/project_card.css" rel="css" />
|
||||
<link data-trunk type="text/css" href="css/details.css" rel="css" />
|
||||
|
||||
<link data-trunk type="text/css" href="css/components/loading.css" rel="css" />
|
||||
<link data-trunk type="text/css" href="css/components/media_slideshow.css" rel="css" />
|
||||
|
||||
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css?family=Sacramento" rel="stylesheet">
|
||||
|
72
src/components/media_slideshow.rs
Normal file
72
src/components/media_slideshow.rs
Normal file
@ -0,0 +1,72 @@
|
||||
use jl_types::domain::media::Media;
|
||||
use yew::prelude::*;
|
||||
|
||||
|
||||
#[function_component(MediaSlideshow)]
|
||||
pub fn media_slideshow(props: &MediaSlideshowProps) -> Html {
|
||||
let current_media_index = use_state(|| 0);
|
||||
let total_media_count = props.media_list.len();
|
||||
let previous_click_cb = {
|
||||
let current_media_index = current_media_index.clone();
|
||||
let total_media_count = total_media_count.clone();
|
||||
Callback::from(move |_| {
|
||||
if *current_media_index > 0 {
|
||||
current_media_index.set(*current_media_index -1);
|
||||
} else {
|
||||
current_media_index.set(total_media_count - 1);
|
||||
}
|
||||
})
|
||||
};
|
||||
let next_click_cb = {
|
||||
let current_media_index = current_media_index.clone();
|
||||
let total_media_count = total_media_count.clone();
|
||||
Callback::from(move |_| {
|
||||
if *current_media_index >= total_media_count - 1 {
|
||||
current_media_index.set(0);
|
||||
} else {
|
||||
current_media_index.set(*current_media_index + 1);
|
||||
}
|
||||
})
|
||||
};
|
||||
html! {
|
||||
<div class={"media-slideshow-frame"}>
|
||||
{
|
||||
props.media_list.iter().enumerate().map(|(index, media)| {
|
||||
match media {
|
||||
Media::Photo(url) => html! {
|
||||
<img class={"media-slideshow-photo fade"} style={if *current_media_index == index {
|
||||
"display: block;"
|
||||
} else {
|
||||
"display: none;"
|
||||
}} src={url.clone()} />
|
||||
},
|
||||
Media::Video(url) => html! {
|
||||
<iframe class={"media-slideshow-video fade"} style={if *current_media_index == index {
|
||||
"display: block;"
|
||||
} else {
|
||||
"display: none;"
|
||||
}}
|
||||
src={format!("{url}?autoplay=1&mute=1")}>
|
||||
</iframe>
|
||||
}
|
||||
}
|
||||
}).collect::<Html>()
|
||||
}
|
||||
|
||||
<div class={"media-slideshow-arrow-left"} onclick={previous_click_cb}>
|
||||
<i class="fa-solid fa-chevron-left"></i>
|
||||
</div>
|
||||
|
||||
<div class={"media-slideshow-arrow-right"} onclick={next_click_cb}>
|
||||
<i class="fa-solid fa-chevron-right"></i>
|
||||
</div>
|
||||
|
||||
<div class={"media-slideshow-number-text"}>{format!("{}/{}", *current_media_index + 1, total_media_count)}</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Properties, PartialEq, PartialOrd)]
|
||||
pub struct MediaSlideshowProps {
|
||||
pub media_list: Vec<Media>,
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
pub mod nav_bar;
|
||||
pub mod search_filter;
|
||||
pub mod project_card;
|
||||
pub mod media_slideshow;
|
@ -1,25 +1,80 @@
|
||||
use jl_types::dto::listing::Listing;
|
||||
use log::{error, info};
|
||||
use uuid::Uuid;
|
||||
use yew::prelude::*;
|
||||
|
||||
use crate::components::nav_bar::NavigationBar;
|
||||
use crate::{components::{nav_bar::NavigationBar, media_slideshow::MediaSlideshow}, api::backend::get_project_listing};
|
||||
|
||||
#[function_component(DetailsPage)]
|
||||
pub fn details_page(_props: &DetailsPageProps) -> Html {
|
||||
pub fn details_page(props: &DetailsPageProps) -> Html {
|
||||
//TODO: query backend
|
||||
let finished_loading = use_state(|| false);
|
||||
let listing_handle: UseStateHandle<Listing> = use_state(|| Listing::default());
|
||||
use_state(|| {
|
||||
let finished_loading = finished_loading.clone();
|
||||
let listing_handle = listing_handle.clone();
|
||||
let project_id = props.project_id.clone();
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
match get_project_listing(&project_id).await {
|
||||
Ok(listing) => {
|
||||
listing_handle.set(listing);
|
||||
finished_loading.set(true);
|
||||
info!("Finshed loading listing");
|
||||
},
|
||||
Err(error) => {
|
||||
error!("Error fetching listing. Error: {:?}", error);
|
||||
},
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
let listing = (*listing_handle).clone();
|
||||
|
||||
let project_title = format!("{} en {}, {}", &listing.project.project_type, &listing.location.district, &listing.location.city);
|
||||
let project_description = &listing.project.description;
|
||||
let project_media_list = listing.project.media.media_list;
|
||||
/*let project_price = format!("Desde ${} USD", match listing.project.starts_from {
|
||||
Some(price) => {
|
||||
let price_separated = price.separate_with_commas();
|
||||
if price_separated.contains(".") {
|
||||
price_separated
|
||||
} else {
|
||||
format!("{price_separated}.00")
|
||||
}
|
||||
},
|
||||
None => "0.00".into()
|
||||
});*/
|
||||
|
||||
let project_condition = &listing.project.project_condition.to_string();
|
||||
|
||||
let project_est_finish_date = &listing.project.finish_date.format("%m/%Y");
|
||||
|
||||
html!{
|
||||
<>
|
||||
<NavigationBar/>
|
||||
<div class={"page-container"}>
|
||||
<div class={"details-container"}>
|
||||
<div class={"details-head"}>
|
||||
<div class={"details-head-image-frame"}>
|
||||
{
|
||||
if *finished_loading {
|
||||
html!{
|
||||
<div class={"details-container"}>
|
||||
<div class={"details-head"}>
|
||||
<MediaSlideshow media_list={project_media_list}/>
|
||||
<div class={"details-head-title"}>{project_title}</div>
|
||||
<div class={"details-head-price"}>{"RD$123,130.00"}</div>
|
||||
<div>{project_description}</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
} else {
|
||||
html!{
|
||||
<div class={"loading-container"}>
|
||||
<div class="lds-facebook"><div></div><div></div><div></div></div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</div>
|
||||
<div class={"details-head-title"}>{"Suites by refa piantini"}</div>
|
||||
<div class={"details-head-price"}>{"RD$123,130.00"}</div>
|
||||
<div>{"Andres Julio Aybar #39"}</div>
|
||||
<div>{"Descripción"}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ pub fn search_page(props: &SearchPageProperties) -> Html {
|
||||
let districts_handle = use_state(|| Vec::from([OptionWrapper::new(None)]));
|
||||
let search_results_handle: UseStateHandle<Vec<ProjectCardDto>> = use_state(|| Vec::new());
|
||||
let page_counter: UseStateHandle<i64> = use_state(|| 1);
|
||||
let finished_loading = use_state(|| false);
|
||||
|
||||
let mut filters = Vec::new();
|
||||
if props.project_state.eq(&ProjectState::Finished) {
|
||||
@ -25,6 +26,7 @@ pub fn search_page(props: &SearchPageProperties) -> Html {
|
||||
let cities_handle = cities_handle.clone();
|
||||
let search_results_handle = search_results_handle.clone();
|
||||
let page_counter = page_counter.clone();
|
||||
let finished_loading = finished_loading.clone();
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
match get_all_cities().await {
|
||||
Ok(cities) => {
|
||||
@ -36,7 +38,8 @@ pub fn search_page(props: &SearchPageProperties) -> Html {
|
||||
};
|
||||
match get_all_projects_with_filters_paged(&(*page_counter), filters).await {
|
||||
Ok(projects) => {
|
||||
search_results_handle.set(projects)
|
||||
search_results_handle.set(projects);
|
||||
finished_loading.set(true);
|
||||
},
|
||||
Err(error) => info!("Error in loading projects: {error}"),
|
||||
};
|
||||
@ -233,9 +236,23 @@ pub fn search_page(props: &SearchPageProperties) -> Html {
|
||||
<div class={"project-search-divider"}/>
|
||||
</div>
|
||||
|
||||
<div class={"project-search-results-container"}> // Search Results Content
|
||||
{(*search_results_handle).clone().into_iter().map(|project| html!{<ProjectCard {project}/>}).collect::<Html>()}
|
||||
</div>
|
||||
{
|
||||
if *finished_loading {
|
||||
html!{
|
||||
<div class={"project-search-results-container"}> // Search Results Content
|
||||
{(*search_results_handle).clone().into_iter().map(|project| html!{<ProjectCard {project}/>}).collect::<Html>()}
|
||||
</div>
|
||||
}
|
||||
} else {
|
||||
html!{
|
||||
<div class={"project-search-results-container"}> // Search Results Content
|
||||
<div class="lds-facebook"><div></div><div></div><div></div></div>
|
||||
</div>
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user