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;
|
justify-content: stretch;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
gap: 10px;
|
||||||
|
|
||||||
.details-head-image-frame {
|
|
||||||
display: flex;
|
|
||||||
height: 300px;
|
|
||||||
width: 100%;
|
|
||||||
background-color: gray;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.details-head-title {
|
.details-head-title {
|
||||||
font-size: 25px;
|
font-size: 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
.navbar-background {
|
.navbar-background {
|
||||||
border-top: 3px solid #04B2D9;
|
border-top: 3px solid #04B2D9;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
z-index: 1;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
@ -42,7 +43,7 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-closed{
|
.navbar-closed {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: flex-start;
|
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/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/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/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=Source+Sans+Pro" rel="stylesheet">
|
||||||
<link href="https://fonts.googleapis.com/css?family=Sacramento" 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 nav_bar;
|
||||||
pub mod search_filter;
|
pub mod search_filter;
|
||||||
pub mod project_card;
|
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 uuid::Uuid;
|
||||||
use yew::prelude::*;
|
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)]
|
#[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!{
|
html!{
|
||||||
<>
|
<>
|
||||||
<NavigationBar/>
|
<NavigationBar/>
|
||||||
<div class={"page-container"}>
|
<div class={"page-container"}>
|
||||||
<div class={"details-container"}>
|
{
|
||||||
<div class={"details-head"}>
|
if *finished_loading {
|
||||||
<div class={"details-head-image-frame"}>
|
html!{
|
||||||
|
<div class={"details-container"}>
|
||||||
</div>
|
<div class={"details-head"}>
|
||||||
<div class={"details-head-title"}>{"Suites by refa piantini"}</div>
|
<MediaSlideshow media_list={project_media_list}/>
|
||||||
<div class={"details-head-price"}>{"RD$123,130.00"}</div>
|
<div class={"details-head-title"}>{project_title}</div>
|
||||||
<div>{"Andres Julio Aybar #39"}</div>
|
<div class={"details-head-price"}>{"RD$123,130.00"}</div>
|
||||||
<div>{"Descripción"}</div>
|
<div>{project_description}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
html!{
|
||||||
|
<div class={"loading-container"}>
|
||||||
|
<div class="lds-facebook"><div></div><div></div><div></div></div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,8 @@ pub fn search_page(props: &SearchPageProperties) -> Html {
|
|||||||
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 mut filters = Vec::new();
|
let mut filters = Vec::new();
|
||||||
if props.project_state.eq(&ProjectState::Finished) {
|
if props.project_state.eq(&ProjectState::Finished) {
|
||||||
filters.push(Filter::Finished);
|
filters.push(Filter::Finished);
|
||||||
@ -25,6 +26,7 @@ pub fn search_page(props: &SearchPageProperties) -> Html {
|
|||||||
let cities_handle = cities_handle.clone();
|
let cities_handle = cities_handle.clone();
|
||||||
let search_results_handle = search_results_handle.clone();
|
let search_results_handle = search_results_handle.clone();
|
||||||
let page_counter = page_counter.clone();
|
let page_counter = page_counter.clone();
|
||||||
|
let finished_loading = finished_loading.clone();
|
||||||
wasm_bindgen_futures::spawn_local(async move {
|
wasm_bindgen_futures::spawn_local(async move {
|
||||||
match get_all_cities().await {
|
match get_all_cities().await {
|
||||||
Ok(cities) => {
|
Ok(cities) => {
|
||||||
@ -36,7 +38,8 @@ pub fn search_page(props: &SearchPageProperties) -> Html {
|
|||||||
};
|
};
|
||||||
match get_all_projects_with_filters_paged(&(*page_counter), filters).await {
|
match get_all_projects_with_filters_paged(&(*page_counter), filters).await {
|
||||||
Ok(projects) => {
|
Ok(projects) => {
|
||||||
search_results_handle.set(projects)
|
search_results_handle.set(projects);
|
||||||
|
finished_loading.set(true);
|
||||||
},
|
},
|
||||||
Err(error) => info!("Error in loading projects: {error}"),
|
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 class={"project-search-divider"}/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class={"project-search-results-container"}> // Search Results Content
|
{
|
||||||
{(*search_results_handle).clone().into_iter().map(|project| html!{<ProjectCard {project}/>}).collect::<Html>()}
|
if *finished_loading {
|
||||||
</div>
|
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>
|
</div>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user