use std::collections::HashSet;
use serde::{Deserialize, Serialize};
use yew::prelude::*;
use yew_agent::{use_bridge, HandlerId, Public, UseBridgeHandle, Worker, WorkerLink};
pub enum ModalMsg {
Open,
Close,
CloseFromAgent(ModalCloseMsg),
}
#[derive(Clone, Debug, Properties, PartialEq)]
pub struct ModalProps {
pub id: String,
#[prop_or_default]
pub children: Children,
#[prop_or_default]
pub trigger: Html,
#[prop_or_default]
pub classes: Classes,
}
#[function_component(Modal)]
pub fn modal(props: &ModalProps) -> Html {
let is_active = use_state(|| false);
let mut class = Classes::from("modal");
class.push(props.classes.clone());
let (opencb, closecb) = if *is_active {
class.push("is-active");
let is_active = is_active.clone();
(Callback::noop(), Callback::from(move |_| is_active.set(false)))
} else {
let is_active = is_active.clone();
(Callback::from(move |_| is_active.set(true)), Callback::noop())
};
{
let id = props.id.clone();
let _bridge: UseBridgeHandle<ModalCloser> = use_bridge(move |response: ModalCloseMsg| {
if response.0 == id {
is_active.set(false);
} else {
}
});
}
html! {
<>
<div onclick={opencb}>
{props.trigger.clone()}
</div>
<div id={props.id.clone()} {class}>
<div class="modal-background" onclick={closecb.clone()}></div>
<div class="modal-content">
{props.children.clone()}
</div>
<button class="modal-close is-large" aria-label="close" onclick={closecb}></button>
</div>
</>
}
}
#[derive(Clone, Debug, Properties, PartialEq)]
pub struct ModalCardProps {
pub id: String,
pub title: String,
#[prop_or_default]
pub body: Html,
#[prop_or_default]
pub footer: Html,
#[prop_or_default]
pub trigger: Html,
#[prop_or_default]
pub classes: Classes,
}
#[function_component(ModalCard)]
pub fn modal_card(props: &ModalCardProps) -> Html {
let is_active = use_state(|| false);
let mut class = Classes::from("modal");
class.push(props.classes.clone());
let (opencb, closecb) = if *is_active {
class.push("is-active");
let is_active = is_active.clone();
(Callback::noop(), Callback::from(move |_| is_active.set(false)))
} else {
let is_active = is_active.clone();
(Callback::from(move |_| is_active.set(true)), Callback::noop())
};
{
let id = props.id.clone();
let _bridge: UseBridgeHandle<ModalCloser> = use_bridge(move |response: ModalCloseMsg| {
if response.0 == id {
is_active.set(false);
} else {
}
});
}
html! {
<>
<div onclick={opencb}>
{props.trigger.clone()}
</div>
<div id={props.id.clone()} {class}>
<div class="modal-background" onclick={closecb.clone()}></div>
<div class="modal-card">
<header class="modal-card-head">
<p class="modal-card-title">{props.title.clone()}</p>
<button class="delete" aria-label="close" onclick={closecb.clone()}></button>
</header>
<section class="modal-card-body">
{props.body.clone()}
</section>
<footer class="modal-card-foot">
{props.footer.clone()}
</footer>
</div>
<button class="modal-close is-large" aria-label="close" onclick={closecb}></button>
</div>
</>
}
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct ModalCloseMsg(pub String);
pub struct ModalCloser {
link: WorkerLink<Self>,
subscribers: HashSet<HandlerId>,
}
impl Worker for ModalCloser {
type Input = ModalCloseMsg;
type Message = ();
type Output = ModalCloseMsg;
type Reach = Public<ModalCloser>;
fn create(link: WorkerLink<Self>) -> Self {
Self { link, subscribers: HashSet::new() }
}
fn update(&mut self, _: Self::Message) {}
fn handle_input(&mut self, msg: Self::Input, _: HandlerId) {
for cmp in self.subscribers.iter() {
self.link.respond(*cmp, msg.clone());
}
}
fn connected(&mut self, id: HandlerId) {
self.subscribers.insert(id);
}
fn disconnected(&mut self, id: HandlerId) {
self.subscribers.remove(&id);
}
}