werewolves/werewolves/src/components/dialog.rs

140 lines
4.1 KiB
Rust

// Copyright (C) 2025-2026 Emilis Bliūdžius
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use web_sys::Element;
use yew::prelude::*;
use crate::components::Button;
#[derive(Debug, Clone, PartialEq, Properties)]
pub struct DialogProps {
#[prop_or_default]
pub children: Html,
pub options: Box<[String]>,
#[prop_or_default]
pub cancel_callback: Option<Callback<()>>,
pub callback: Callback<String>,
}
#[function_component]
pub fn Dialog(
DialogProps {
children,
options,
cancel_callback,
callback,
}: &DialogProps,
) -> Html {
let options = options
.iter()
.map(|opt| {
let callback = callback.clone();
let option = opt.clone();
let cb = Callback::from(move |_| {
callback.emit(option.clone());
});
html! {
<Button on_click={cb}>{opt.clone()}</Button>
}
})
.collect::<Html>();
let backdrop_click = cancel_callback.clone().map(|cancel_callback| {
Callback::from(move |ev: MouseEvent| {
if let Some(div) = ev.target_dyn_into::<Element>()
&& div.class_name() == "dialog"
{
ev.stop_propagation();
cancel_callback.emit(());
}
})
});
html! {
<div class="click-backdrop" onclick={backdrop_click}>
<div class="dialog">
<div class="dialog-box">
<div class="message">
{children.clone()}
</div>
<div class="options">
{options}
</div>
</div>
</div>
</div>
}
}
#[derive(Debug, Clone, PartialEq, Properties)]
pub struct WithConfirmationProps {
pub state: UseStateHandle<bool>,
#[prop_or_default]
pub children: Html,
pub confirm_callback: Callback<()>,
pub message: Html,
}
#[function_component]
pub fn WithConfirmation(
WithConfirmationProps {
state,
children,
confirm_callback,
message,
}: &WithConfirmationProps,
) -> Html {
let about_dialog_state = state.clone();
let confirmation_dialog = about_dialog_state.then(|| {
let cancel_signout = {
let dialog = about_dialog_state.clone();
Callback::from(move |_| {
dialog.set(false);
})
};
let confirm_callback = confirm_callback.clone();
let callback = {
let dialog = about_dialog_state.clone();
Callback::from(move |opt: String| {
if opt == "ok" {
confirm_callback.emit(());
} else {
dialog.set(false);
}
})
};
let options: Box<[String]> = Box::new([String::from("ok"), String::from("take me back")]);
html! {
<Dialog
options={options}
cancel_callback={Some(cancel_signout)}
callback={callback}
>
{message.clone()}
</Dialog>
}
});
let confirmation_click = {
let dialog_set = about_dialog_state.setter();
move |_| {
dialog_set.set(true);
}
};
html! {
<>
<span class="has-confirm" onclick={confirmation_click}>{children.clone()}</span>
{confirmation_dialog}
</>
}
}