217 lines
7.8 KiB
Rust
217 lines
7.8 KiB
Rust
use web_sys::MouseEvent;
|
|
use yew::{function_component, Html, html, Callback, Properties, Component};
|
|
|
|
use crate::component::{overlay::Overlay, fa_icon::{FAIcon, FontawesomeIcon, FontawesomeSize}};
|
|
|
|
#[derive(PartialEq, Clone)]
|
|
pub struct ImageDescription {
|
|
link: String,
|
|
description: Option<&'static str>
|
|
}
|
|
|
|
impl ImageDescription {
|
|
pub fn new(link: impl Into<String>, description: &'static str) -> Self {
|
|
Self {
|
|
link: link.into(),
|
|
description: Some(description)
|
|
}
|
|
}
|
|
|
|
pub fn new_blank(link: impl Into<String>) -> Self {
|
|
Self {
|
|
link: link.into(),
|
|
description: None
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Properties, PartialEq)]
|
|
pub struct DefaultImageViewerProps {
|
|
pub images: Vec<ImageDescription>,
|
|
pub open: bool,
|
|
#[prop_or_default]
|
|
pub infinite: bool,
|
|
pub onclose: Callback<()>,
|
|
}
|
|
|
|
/*
|
|
#[function_component]
|
|
pub fn DefaultImageViewer(props: &DefaultImageViewerProps) -> Html {
|
|
let selected_image = use_state_eq(|| 0);
|
|
let select_next = selected_image.setter();
|
|
let select_prev = selected_image.setter();
|
|
|
|
let has_next = props.infinite || *selected_image < props.images.len() - 1;
|
|
let has_prev = props.infinite || *selected_image > 0;
|
|
let next = (*selected_image + props.images.len() + 1) % props.images.len();
|
|
let prev = (*selected_image + props.images.len() - 1) % props.images.len();
|
|
let onclose = props.onclose.clone();
|
|
|
|
html! {
|
|
<ImageViewer
|
|
image={props.images[*selected_image].clone()}
|
|
open={props.open && !props.images.is_empty()}
|
|
onclose={Callback::from(move |_| onclose.emit(()))}
|
|
has_next={has_next}
|
|
has_prev={has_prev}
|
|
onnext={Callback::from(move |_| if has_next {
|
|
select_next.set(next);
|
|
})}
|
|
onprev={Callback::from(move |_| if has_prev {
|
|
select_prev.set(prev);
|
|
})}
|
|
/>
|
|
}
|
|
}
|
|
*/
|
|
|
|
pub enum DefaultImageViewerMessage {
|
|
Next,
|
|
Prev
|
|
}
|
|
|
|
pub struct DefaultImageViewer {
|
|
current: usize,
|
|
}
|
|
|
|
impl Component for DefaultImageViewer {
|
|
type Message = DefaultImageViewerMessage;
|
|
|
|
type Properties = DefaultImageViewerProps;
|
|
|
|
fn create(_ctx: &yew::Context<Self>) -> Self {
|
|
Self { current: 0 }
|
|
}
|
|
|
|
fn view(&self, ctx: &yew::Context<Self>) -> Html {
|
|
let DefaultImageViewerProps {
|
|
images,
|
|
open,
|
|
infinite,
|
|
onclose,
|
|
} = ctx.props();
|
|
|
|
let has_next = *infinite || self.current < images.len() - 1;
|
|
let has_prev = *infinite || self.current > 0;
|
|
let onclose = onclose.clone();
|
|
|
|
html! {
|
|
<ImageViewer
|
|
image={images[self.current].clone()}
|
|
open={*open && !images.is_empty()}
|
|
onclose={Callback::from(move |_| onclose.emit(()))}
|
|
has_next={has_next}
|
|
has_prev={has_prev}
|
|
onnext={if has_next { ctx.link().callback(|_| DefaultImageViewerMessage::Next)} else { Default::default() }}
|
|
onprev={if has_prev { ctx.link().callback(|_| DefaultImageViewerMessage::Prev)} else { Default::default() }}
|
|
/>
|
|
}
|
|
}
|
|
|
|
fn update(&mut self, ctx: &yew::Context<Self>, msg: Self::Message) -> bool {
|
|
let images = &ctx.props().images;
|
|
|
|
self.current = match msg {
|
|
DefaultImageViewerMessage::Next => {
|
|
if self.current == images.len() - 1 { 0 } else { self.current + 1 }
|
|
},
|
|
DefaultImageViewerMessage::Prev => {
|
|
if self.current == 0 { images.len() - 1 } else { self.current - 1 }
|
|
}
|
|
};
|
|
|
|
images.len() > 1
|
|
}
|
|
|
|
fn changed(&mut self, ctx: &yew::Context<Self>, old_props: &Self::Properties) -> bool {
|
|
ctx.props() != old_props
|
|
}
|
|
}
|
|
|
|
#[derive(Properties, PartialEq)]
|
|
pub struct ImageViewerProps {
|
|
pub image: Option<ImageDescription>,
|
|
pub open: bool,
|
|
#[prop_or_default]
|
|
pub has_prev: bool,
|
|
#[prop_or_default]
|
|
pub has_next: bool,
|
|
pub onclose: Callback<()>,
|
|
pub onnext: Callback<MouseEvent>,
|
|
pub onprev: Callback<MouseEvent>,
|
|
}
|
|
|
|
#[function_component]
|
|
pub fn ImageViewer(props: &ImageViewerProps) -> Html {
|
|
let onclose = props.onclose.clone();
|
|
html! {
|
|
<>
|
|
<Overlay
|
|
class="overlay-gallery"
|
|
open={props.open && props.image.is_some()}
|
|
onclick={Callback::from(move |_| onclose.emit(()))}>
|
|
<>
|
|
<div></div>
|
|
{
|
|
if let Some(image_desc) = props.image.as_ref() {
|
|
let onclose = props.onclose.clone();
|
|
Some(html! {
|
|
<>
|
|
<div>
|
|
<img
|
|
onclick={Callback::from(move |_| onclose.emit(()))}
|
|
src={
|
|
if image_desc.link.starts_with("https?://") {
|
|
image_desc.link.to_string()
|
|
} else {
|
|
format!("/img/{}", image_desc.link)
|
|
}
|
|
}
|
|
/>
|
|
{
|
|
if let Some(description) = &image_desc.description {
|
|
Some(html! {
|
|
<div>{description}</div>
|
|
})
|
|
} else { None }
|
|
}
|
|
</div>
|
|
|
|
{
|
|
// Controls: Next
|
|
if props.has_next {
|
|
Some(html! {
|
|
<div class="overlay-gallery-next">
|
|
<FAIcon
|
|
icon={FontawesomeIcon::ChevronRight}
|
|
size={FontawesomeSize::XL}
|
|
onclick={props.onnext.clone()}
|
|
/>
|
|
</div>
|
|
})
|
|
} else { None }
|
|
}
|
|
|
|
{
|
|
// Controls: Prev
|
|
if props.has_prev {
|
|
Some(html! {
|
|
<div class="overlay-gallery-prev">
|
|
<FAIcon
|
|
icon={FontawesomeIcon::ChevronLeft}
|
|
size={FontawesomeSize::XL}
|
|
onclick={props.onprev.clone()}
|
|
/>
|
|
</div>
|
|
})
|
|
} else { None }
|
|
}
|
|
</>
|
|
})
|
|
} else { None }
|
|
}
|
|
</>
|
|
</Overlay>
|
|
</>
|
|
}
|
|
} |