use crate::Signal;
use std::{fmt::Display, str::FromStr};
#[diagnostic::on_unimplemented(
message = "`bind` on this control needs a `Signal<T>` where `T: FromStr + Display + PartialEq`, or a form field",
label = "`{Self}` cannot be edited as text"
)]
pub trait TextValue: Clone + 'static {
fn text(&self) -> String;
fn shows(&self, text: &str) -> bool;
fn edit(&self, text: String);
fn touch(&self) {}
}
impl<T: FromStr + Display + PartialEq + 'static> TextValue for Signal<T> {
fn text(&self) -> String {
self.with(ToString::to_string)
}
fn shows(&self, text: &str) -> bool {
text.parse::<T>()
.is_ok_and(|parsed| self.with(|value| *value == parsed))
}
fn edit(&self, text: String) {
if let Ok(value) = text.parse::<T>() {
self.set(value);
}
}
}
#[diagnostic::on_unimplemented(
message = "`bind` on a checkbox needs a `Signal<bool>` or a `Signal<Vec<T>>`",
label = "`{Self}` cannot follow a checkbox"
)]
pub trait Checkbox: Clone + 'static {
fn checked(&self, choice: &str) -> bool;
fn check(&self, choice: &str, checked: bool);
}
impl Checkbox for Signal<bool> {
fn checked(&self, _: &str) -> bool {
self.get()
}
fn check(&self, _: &str, checked: bool) {
self.set(checked);
}
}
impl<T: FromStr + PartialEq + 'static> Checkbox for Signal<Vec<T>> {
fn checked(&self, choice: &str) -> bool {
selected(self, choice)
}
fn check(&self, choice: &str, checked: bool) {
let Ok(choice) = choice.parse::<T>() else {
return;
};
if self.with_untracked(|values| values.contains(&choice)) != checked {
self.update(|values| {
if checked {
values.push(choice);
} else {
values.retain(|value| *value != choice);
}
});
}
}
}
pub fn selected<T: FromStr + PartialEq>(values: &Signal<Vec<T>>, choice: &str) -> bool {
choice
.parse::<T>()
.is_ok_and(|choice| values.with(|values| values.contains(&choice)))
}